diff --git a/.build.sh b/.build.sh index f0f14ea..b16b65c 100644 --- a/.build.sh +++ b/.build.sh @@ -1,5 +1,6 @@ #!/bin/bash cd /home/showen/Showen/ShowenV2 export PATH=/home/showen/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin:$PATH -cargo check 2>&1 | tail -80 -echo "EXIT=$?" +cargo check 2>&1 | grep -E "^error" | head -60 +echo "---SUMMARY---" +cargo check 2>&1 | tail -3 diff --git a/.find_cargo.sh b/.find_cargo.sh new file mode 100644 index 0000000..cad9c28 --- /dev/null +++ b/.find_cargo.sh @@ -0,0 +1,16 @@ +#!/bin/bash +echo "=== find cargo config ===" +find /home/showen -name "config.toml" -path "*.cargo*" 2>/dev/null +find /home/showen -name "config" -path "*.cargo*" 2>/dev/null +find / -name "config.toml" -path "*cargo*" 2>/dev/null | head -5 + +echo "=== check .cargo dir ===" +ls -la /home/showen/.cargo/ 2>/dev/null + +echo "=== rustup cargo home ===" +echo $CARGO_HOME +ls -la ~/.cargo/ 2>/dev/null + +echo "=== project cargo ===" +ls -la /home/showen/Showen/ShowenV2/.cargo/ 2>/dev/null +find /home/showen/Showen -name ".cargo" -type d 2>/dev/null diff --git a/src/plugins/live2d/animation.rs b/src/plugins/live2d/animation.rs index 9423f1d..d3c7cec 100644 --- a/src/plugins/live2d/animation.rs +++ b/src/plugins/live2d/animation.rs @@ -85,38 +85,38 @@ impl AnimationState { } fn update_blink(&mut self, now: Instant, core: &CubismCore, model: &CubismModel) { - match &mut self.blink_phase { + // 先计算新的眨眼状态和当前 eye_open 值,避免借用冲突 + let (new_phase, eye_open) = match self.blink_phase { BlinkPhase::Idle => { if now.duration_since(self.last_blink) >= self.next_blink_interval { - self.blink_phase = BlinkPhase::Closing(0.0); - self.last_blink = now; + (BlinkPhase::Closing(0.0), 1.0) + } else { + (BlinkPhase::Idle, 1.0) } } - BlinkPhase::Closing(t) => { - *t += 0.15; // 闭眼速度 - if *t >= 1.0 { - *t = 1.0; - self.blink_phase = BlinkPhase::Opening(1.0); + BlinkPhase::Closing(mut t) => { + t += 0.15; + if t >= 1.0 { + (BlinkPhase::Opening(1.0), 0.0) + } else { + (BlinkPhase::Closing(t), 1.0 - t) } - let eye_open = 1.0 - *t; - model.set_param(core, "ParamEyeLOpen", eye_open); - model.set_param(core, "ParamEyeROpen", eye_open); } - BlinkPhase::Opening(t) => { - *t -= 0.1; // 睁眼速度(比闭眼慢) - if *t <= 0.0 { - *t = 0.0; - self.blink_phase = BlinkPhase::Idle; + BlinkPhase::Opening(mut t) => { + t -= 0.1; + if t <= 0.0 { self.last_blink = now; - // 下次眨眼间隔(2-5 秒随机) self.next_blink_interval = Duration::from_secs(2 + (now.elapsed().as_nanos() % 3) as u64); + (BlinkPhase::Idle, 1.0) + } else { + (BlinkPhase::Opening(t), 1.0 - t) } - let eye_open = 1.0 - *t; - model.set_param(core, "ParamEyeLOpen", eye_open); - model.set_param(core, "ParamEyeROpen", eye_open); } - } + }; + self.blink_phase = new_phase; + model.set_param(core, "ParamEyeLOpen", eye_open); + model.set_param(core, "ParamEyeROpen", eye_open); } fn update_mouth( diff --git a/src/plugins/live2d/core_ffi.rs b/src/plugins/live2d/core_ffi.rs index 1f1f2b1..0760069 100644 --- a/src/plugins/live2d/core_ffi.rs +++ b/src/plugins/live2d/core_ffi.rs @@ -4,7 +4,6 @@ //! 动态加载 libLive2DCubismCore.so(需 libm.so 预加载,因 Core 内部使用 powf)。 use libloading::{Library, Symbol}; -use std::ffi::CString; use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_void}; /// moc 对齐要求(字节) @@ -268,9 +267,3 @@ unsafe fn libc_strlen(ptr: *const c_char) -> usize { } n } - -// 保留 CString 防止 unused 警告(未来日志函数需要) -#[allow(dead_code)] -fn _ensure_cstring_linked() -> CString { - CString::new("").unwrap() -} diff --git a/src/plugins/live2d/gl.rs b/src/plugins/live2d/gl.rs index 1437039..42b8872 100644 --- a/src/plugins/live2d/gl.rs +++ b/src/plugins/live2d/gl.rs @@ -1,39 +1,27 @@ //! OpenGL ES 2.0 / EGL 渲染后端 //! -//! 使用 EGL 创建离屏 PBuffer 上下文(无窗口),用 OpenGL ES 2.0 渲染 Live2D 模型。 -//! 渲染结果通过 glReadPixels 读取,交给 video 插件或直接显示。 -//! -//! 这里采用 X11 窗口方式渲染(设备有 X11 + kwin),直接显示到屏幕。 -//! 如需离屏渲染可改为 PBuffer。 +//! 使用 EGL + X11 窗口创建渲染上下文,用 OpenGL ES 2.0 渲染 Live2D 模型。 -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Result}; use std::ffi::c_void; -use std::os::raw::{c_char, c_int, c_uint, c_void as c_void_raw}; +use std::os::raw::{c_char, c_int, c_long, c_uint, c_void as c_void_raw}; -// ============ EGL 类型与常量 ============ +// ============ EGL ============ pub mod egl { - pub const EGL_VERSION_1_5: c_int = 1; + use std::os::raw::{c_int, c_uint, c_void}; pub const EGL_SURFACE_TYPE: c_int = 0x3033; pub const EGL_WINDOW_BIT: c_int = 0x0004; - pub const EGL_PBUFFER_BIT: c_int = 0x0001; pub const EGL_BLUE_SIZE: c_int = 0x3022; pub const EGL_GREEN_SIZE: c_int = 0x3023; pub const EGL_RED_SIZE: c_int = 0x3024; - pub const EGL_DEPTH_SIZE: c_int = 0x3025; pub const EGL_ALPHA_SIZE: c_int = 0x3021; - pub const EGL_STENCIL_SIZE: c_int = 0x3026; pub const EGL_RENDERABLE_TYPE: c_int = 0x3040; pub const EGL_OPENGL_ES2_BIT: c_int = 0x0004; pub const EGL_NONE: c_int = 0x3038; pub const EGL_CONTEXT_CLIENT_VERSION: c_int = 0x3098; - pub const EGL_DISPLAY: usize = 0; - pub const EGL_NO_DISPLAY: isize = 0; - pub const EGL_NO_CONTEXT: isize = 0; - pub const EGL_NO_SURFACE: isize = 0; - pub type EGLDisplay = *mut c_void; pub type EGLConfig = *mut c_void; pub type EGLContext = *mut c_void; @@ -42,16 +30,20 @@ pub mod egl { pub type NativeWindowType = *mut c_void; pub type EGLBoolean = c_uint; pub type EGLint = c_int; + + /// EGL_DEFAULT_DISPLAY(即 NULL) + pub fn default_display() -> NativeDisplayType { + std::ptr::null_mut() + } } -// ============ X11 类型 ============ +// ============ X11 ============ pub mod x11 { - use std::os::raw::{c_long, c_uint, c_void}; + use std::os::raw::{c_int, c_long, c_uint, c_void}; pub type Display = c_void; pub type Window = c_long; - pub type XID = c_long; #[repr(C)] pub struct XSetWindowAttributes { @@ -76,10 +68,9 @@ pub mod x11 { pub const CWEventMask: c_long = 1 << 11; pub const ExposureMask: c_long = 1 << 15; pub const StructureNotifyMask: c_long = 1 << 17; - pub const KeyPressMask: c_long = 1 << 0; } -// ============ GLES2 类型与常量 ============ +// ============ GLES2 ============ pub mod gl { use std::os::raw::{c_int, c_uint}; @@ -89,57 +80,42 @@ pub mod gl { pub type GLint = c_int; pub type GLsizei = c_int; pub type GLbitfield = c_uint; - pub type GLfloat = f32; pub const GL_FLOAT: GLenum = 0x1406; - pub const GL_FLOAT_VEC2: GLenum = 0x8B50; - pub const GL_FLOAT_VEC4: GLenum = 0x8B52; - pub const GL_FLOAT_MAT4: GLenum = 0x8B5C; - pub const GL_SAMPLER_2D: GLenum = 0x8B5E; - pub const GL_ARRAY_BUFFER: GLenum = 0x8892; pub const GL_ELEMENT_ARRAY_BUFFER: GLenum = 0x8893; - pub const GL_TEXTURE_2D: GLenum = 0x0DE1; pub const GL_TEXTURE0: GLenum = 0x84C0; pub const GL_RGBA: GLenum = 0x1908; pub const GL_UNSIGNED_BYTE: GLenum = 0x1401; pub const GL_UNSIGNED_SHORT: GLenum = 0x1403; - pub const GL_NEAREST: GLenum = 0x2600; pub const GL_LINEAR: GLenum = 0x2601; pub const GL_CLAMP_TO_EDGE: GLenum = 0x812F; pub const GL_TEXTURE_MIN_FILTER: GLenum = 0x2801; pub const GL_TEXTURE_MAG_FILTER: GLenum = 0x2800; pub const GL_TEXTURE_WRAP_S: GLenum = 0x2802; pub const GL_TEXTURE_WRAP_T: GLenum = 0x2803; - pub const GL_VERTEX_SHADER: GLenum = 0x8B31; pub const GL_FRAGMENT_SHADER: GLenum = 0x8B30; pub const GL_COMPILE_STATUS: GLenum = 0x8B81; pub const GL_LINK_STATUS: GLenum = 0x8B82; - pub const GL_TRIANGLES: GLenum = 0x0004; pub const GL_BLEND: GLenum = 0x0BE2; pub const GL_SRC_ALPHA: GLenum = 0x0302; pub const GL_ONE_MINUS_SRC_ALPHA: GLenum = 0x0303; pub const GL_ONE: GLenum = 1; pub const GL_ZERO: GLenum = 0; + pub const GL_SRC_COLOR: GLenum = 0x0300; pub const GL_COLOR_BUFFER_BIT: GLbitfield = 0x4000; - pub const GL_DEPTH_BUFFER_BIT: GLbitfield = 0x0100; - pub const GL_BLEND_DST_RGB: GLenum = 0x80C8; - pub const GL_BLEND_SRC_RGB: GLenum = 0x80C9; - pub const GL_BLEND_DST_ALPHA: GLenum = 0x80CA; - pub const GL_BLEND_SRC_ALPHA: GLenum = 0x80CB; - pub const GL_BLEND_EQUATION: GLenum = 0x8009; - pub const GL_FUNC_ADD: GLenum = 0x8006; - pub const GL_FUNC_REVERSE_SUBTRACT: GLenum = 0x800B; pub const GL_STATIC_DRAW: GLenum = 0x88E4; pub const GL_FALSE: GLenum = 0; - pub const GL_TRUE: GLenum = 1; pub const GL_NO_ERROR: GLenum = 0; } -/// 着色器源码(标准模式,支持 mask 和非 mask) +pub use gl::{GLenum, GLuint, GLint, GLsizei}; + +// ============ 着色器源码 ============ + pub const VERT_SHADER_SRC: &str = r#"#version 100 attribute vec4 a_position; attribute vec2 a_texCoord; @@ -168,113 +144,100 @@ void main() { } "#; -/// OpenGL ES2 / EGL 动态库函数集合 +// ============ 动态库函数集合 ============ + pub struct Gl { _lib_egl: libloading::Library, _lib_gl: libloading::Library, _lib_x11: libloading::Library, - // EGL - egl_get_display: - unsafe extern "C" fn(disp: egl::NativeDisplayType) -> egl::EGLDisplay, - egl_initialize: - unsafe extern "C" fn(dpy: egl::EGLDisplay, major: *mut c_int, minor: *mut c_int) -> egl::EGLBoolean, - egl_choose_config: unsafe extern "C" fn( + + pub egl_get_display: unsafe extern "C" fn(disp: egl::NativeDisplayType) -> egl::EGLDisplay, + pub egl_initialize: unsafe extern "C" fn( + dpy: egl::EGLDisplay, + major: *mut c_int, + minor: *mut c_int, + ) -> egl::EGLBoolean, + pub egl_choose_config: unsafe extern "C" fn( dpy: egl::EGLDisplay, attrib_list: *const egl::EGLint, configs: *mut egl::EGLConfig, config_size: egl::EGLint, num_config: *mut egl::EGLint, ) -> egl::EGLBoolean, - egl_create_window_surface: unsafe extern "C" fn( + pub egl_create_window_surface: unsafe extern "C" fn( dpy: egl::EGLDisplay, config: egl::EGLConfig, win: egl::NativeWindowType, attrib_list: *const egl::EGLint, ) -> egl::EGLSurface, - egl_create_context: unsafe extern "C" fn( + pub egl_create_context: unsafe extern "C" fn( dpy: egl::EGLDisplay, config: egl::EGLConfig, share_ctx: egl::EGLContext, attrib_list: *const egl::EGLint, ) -> egl::EGLContext, - egl_make_current: unsafe extern "C" fn( + pub egl_make_current: unsafe extern "C" fn( dpy: egl::EGLDisplay, draw: egl::EGLSurface, read: egl::EGLSurface, ctx: egl::EGLContext, ) -> egl::EGLBoolean, - egl_swap_buffers: + pub egl_swap_buffers: unsafe extern "C" fn(dpy: egl::EGLDisplay, surface: egl::EGLSurface) -> egl::EGLBoolean, - egl_terminate: unsafe extern "C" fn(dpy: egl::EGLDisplay) -> egl::EGLBoolean, - // GLES2 - gl_clear: unsafe extern "C" fn(mask: gl::GLbitfield), - gl_clear_color: unsafe extern "C" fn(r: f32, g: f32, b: f32, a: f32), - gl_enable: unsafe extern "C" fn(cap: gl::GLenum), - gl_disable: unsafe extern "C" fn(cap: gl::GLenum), - gl_blend_func: unsafe extern "C" fn(sfactor: gl::GLenum, dfactor: gl::GLenum), - gl_blend_func_separate: unsafe extern "C" fn( - src_rgb: gl::GLenum, - dst_rgb: gl::GLenum, - src_alpha: gl::GLenum, - dst_alpha: gl::GLenum, - ), - gl_viewport: unsafe extern "C" fn(x: gl::GLint, y: gl::GLint, w: gl::GLsizei, h: gl::GLsizei), - gl_create_shader: unsafe extern "C" fn(t: gl::GLenum) -> gl::GLuint, - gl_shader_source: unsafe extern "C" fn( + pub gl_clear: unsafe extern "C" fn(mask: gl::GLbitfield), + pub gl_clear_color: unsafe extern "C" fn(r: f32, g: f32, b: f32, a: f32), + pub gl_enable: unsafe extern "C" fn(cap: gl::GLenum), + pub gl_blend_func: unsafe extern "C" fn(sfactor: gl::GLenum, dfactor: gl::GLenum), + pub gl_viewport: unsafe extern "C" fn(x: gl::GLint, y: gl::GLint, w: gl::GLsizei, h: gl::GLsizei), + pub gl_create_shader: unsafe extern "C" fn(t: gl::GLenum) -> gl::GLuint, + pub gl_shader_source: unsafe extern "C" fn( shader: gl::GLuint, count: gl::GLsizei, string: *const *const c_char, length: *const gl::GLint, ), - gl_compile_shader: unsafe extern "C" fn(shader: gl::GLuint), - gl_get_shaderiv: unsafe extern "C" fn( - shader: gl::GLuint, - pname: gl::GLenum, - params: *mut gl::GLint, - ), - gl_get_shader_info_log: unsafe extern "C" fn( + pub gl_compile_shader: unsafe extern "C" fn(shader: gl::GLuint), + pub gl_get_shaderiv: + unsafe extern "C" fn(shader: gl::GLuint, pname: gl::GLenum, params: *mut gl::GLint), + pub gl_get_shader_info_log: unsafe extern "C" fn( shader: gl::GLuint, buf_size: gl::GLsizei, length: *mut gl::GLsizei, info_log: *mut c_char, ), - gl_create_program: unsafe extern "C" fn() -> gl::GLuint, - gl_attach_shader: unsafe extern "C" fn(program: gl::GLuint, shader: gl::GLuint), - gl_link_program: unsafe extern "C" fn(program: gl::GLuint), - gl_get_programiv: unsafe extern "C" fn( - program: gl::GLuint, - pname: gl::GLenum, - params: *mut gl::GLint, - ), - gl_get_program_info_log: unsafe extern "C" fn( + pub gl_create_program: unsafe extern "C" fn() -> gl::GLuint, + pub gl_attach_shader: unsafe extern "C" fn(program: gl::GLuint, shader: gl::GLuint), + pub gl_link_program: unsafe extern "C" fn(program: gl::GLuint), + pub gl_get_programiv: + unsafe extern "C" fn(program: gl::GLuint, pname: gl::GLenum, params: *mut gl::GLint), + pub gl_get_program_info_log: unsafe extern "C" fn( program: gl::GLuint, buf_size: gl::GLsizei, length: *mut gl::GLsizei, info_log: *mut c_char, ), - gl_use_program: unsafe extern "C" fn(program: gl::GLuint), - gl_get_attrib_location: + pub gl_use_program: unsafe extern "C" fn(program: gl::GLuint), + pub gl_get_attrib_location: unsafe extern "C" fn(program: gl::GLuint, name: *const c_char) -> gl::GLint, - gl_get_uniform_location: + pub gl_get_uniform_location: unsafe extern "C" fn(program: gl::GLuint, name: *const c_char) -> gl::GLint, - gl_uniform_matrix4fv: unsafe extern "C" fn( + pub gl_uniform_matrix4fv: unsafe extern "C" fn( location: gl::GLint, count: gl::GLsizei, transpose: gl::GLenum, value: *const f32, ), - gl_uniform1i: unsafe extern "C" fn(location: gl::GLint, v: gl::GLint), - gl_uniform4f: - unsafe extern "C" fn(location: gl::GLint, x: f32, y: f32, z: f32, w: f32), - gl_gen_textures: unsafe extern "C" fn(n: gl::GLsizei, textures: *mut gl::GLuint), - gl_bind_texture: unsafe extern "C" fn(target: gl::GLenum, texture: gl::GLuint), - gl_tex_parameteri: + pub gl_uniform1i: unsafe extern "C" fn(location: gl::GLint, v: gl::GLint), + pub gl_uniform4f: unsafe extern "C" fn(location: gl::GLint, x: f32, y: f32, z: f32, w: f32), + pub gl_gen_textures: unsafe extern "C" fn(n: gl::GLsizei, textures: *mut gl::GLuint), + pub gl_bind_texture: unsafe extern "C" fn(target: gl::GLenum, texture: gl::GLuint), + pub gl_tex_parameteri: unsafe extern "C" fn(target: gl::GLenum, pname: gl::GLenum, param: gl::GLint), - gl_tex_image_2d: unsafe extern "C" fn( + pub gl_tex_image_2d: unsafe extern "C" fn( target: gl::GLenum, level: gl::GLint, - internalformat: gl::GLenum, + internalformat: gl::GLint, width: gl::GLsizei, height: gl::GLsizei, border: gl::GLint, @@ -282,17 +245,17 @@ pub struct Gl { type_: gl::GLenum, data: *const c_void_raw, ), - gl_active_texture: unsafe extern "C" fn(texture: gl::GLenum), - gl_gen_buffers: unsafe extern "C" fn(n: gl::GLsizei, buffers: *mut gl::GLuint), - gl_bind_buffer: unsafe extern "C" fn(target: gl::GLenum, buffer: gl::GLuint), - gl_buffer_data: unsafe extern "C" fn( + pub gl_active_texture: unsafe extern "C" fn(texture: gl::GLenum), + pub gl_gen_buffers: unsafe extern "C" fn(n: gl::GLsizei, buffers: *mut gl::GLuint), + pub gl_bind_buffer: unsafe extern "C" fn(target: gl::GLenum, buffer: gl::GLuint), + pub gl_buffer_data: unsafe extern "C" fn( target: gl::GLenum, size: isize, data: *const c_void_raw, usage: gl::GLenum, ), - gl_enable_vertex_attrib_array: unsafe extern "C" fn(index: gl::GLuint), - gl_vertex_attrib_pointer: unsafe extern "C" fn( + pub gl_enable_vertex_attrib_array: unsafe extern "C" fn(index: gl::GLuint), + pub gl_vertex_attrib_pointer: unsafe extern "C" fn( index: gl::GLuint, size: gl::GLint, type_: gl::GLenum, @@ -300,21 +263,17 @@ pub struct Gl { stride: gl::GLsizei, pointer: *const c_void_raw, ), - gl_draw_elements: unsafe extern "C" fn( + pub gl_draw_elements: unsafe extern "C" fn( mode: gl::GLenum, count: gl::GLsizei, type_: gl::GLenum, indices: *const c_void_raw, ), - gl_get_error: unsafe extern "C" fn() -> gl::GLenum, - gl_delete_textures: unsafe extern "C" fn(n: gl::GLsizei, textures: *const gl::GLuint), - gl_delete_buffers: unsafe extern "C" fn(n: gl::GLsizei, buffers: *const gl::GLuint), + pub gl_get_error: unsafe extern "C" fn() -> gl::GLenum, + pub gl_delete_buffers: unsafe extern "C" fn(n: gl::GLsizei, buffers: *const gl::GLuint), - // X11 - x_open_display: - unsafe extern "C" fn(name: *const c_char) -> *mut x11::Display, - x_close_display: unsafe extern "C" fn(disp: *mut x11::Display) -> c_int, - x_create_window: unsafe extern "C" fn( + pub x_open_display: unsafe extern "C" fn(name: *const c_char) -> *mut x11::Display, + pub x_create_window: unsafe extern "C" fn( disp: *mut x11::Display, parent: x11::Window, x: c_int, @@ -328,14 +287,13 @@ pub struct Gl { valuemask: c_long, attributes: *mut x11::XSetWindowAttributes, ) -> x11::Window, - x_map_window: unsafe extern "C" fn(disp: *mut x11::Display, w: x11::Window) -> c_int, - x_store_name: + pub x_map_window: unsafe extern "C" fn(disp: *mut x11::Display, w: x11::Window) -> c_int, + pub x_store_name: unsafe extern "C" fn(disp: *mut x11::Display, w: x11::Window, name: *const c_char) -> c_int, - x_flush: unsafe extern "C" fn(disp: *mut x11::Display) -> c_int, + pub x_flush: unsafe extern "C" fn(disp: *mut x11::Display) -> c_int, } impl Gl { - /// 加载所有动态库函数 pub fn load() -> Result { let lib_egl = unsafe { libloading::Library::new("libEGL.so.1")? }; let lib_gl = unsafe { libloading::Library::new("libGLESv2.so.2")? }; @@ -350,14 +308,11 @@ impl Gl { egl_create_context: *lib_egl.get(b"eglCreateContext\0")?, egl_make_current: *lib_egl.get(b"eglMakeCurrent\0")?, egl_swap_buffers: *lib_egl.get(b"eglSwapBuffers\0")?, - egl_terminate: *lib_egl.get(b"eglTerminate\0")?, gl_clear: *lib_gl.get(b"glClear\0")?, gl_clear_color: *lib_gl.get(b"glClearColor\0")?, gl_enable: *lib_gl.get(b"glEnable\0")?, - gl_disable: *lib_gl.get(b"glDisable\0")?, gl_blend_func: *lib_gl.get(b"glBlendFunc\0")?, - gl_blend_func_separate: *lib_gl.get(b"glBlendFuncSeparate\0")?, gl_viewport: *lib_gl.get(b"glViewport\0")?, gl_create_shader: *lib_gl.get(b"glCreateShader\0")?, gl_shader_source: *lib_gl.get(b"glShaderSource\0")?, @@ -387,11 +342,9 @@ impl Gl { gl_vertex_attrib_pointer: *lib_gl.get(b"glVertexAttribPointer\0")?, gl_draw_elements: *lib_gl.get(b"glDrawElements\0")?, gl_get_error: *lib_gl.get(b"glGetError\0")?, - gl_delete_textures: *lib_gl.get(b"glDeleteTextures\0")?, gl_delete_buffers: *lib_gl.get(b"glDeleteBuffers\0")?, x_open_display: *lib_x11.get(b"XOpenDisplay\0")?, - x_close_display: *lib_x11.get(b"XCloseDisplay\0")?, x_create_window: *lib_x11.get(b"XCreateWindow\0")?, x_map_window: *lib_x11.get(b"XMapWindow\0")?, x_store_name: *lib_x11.get(b"XStoreName\0")?, @@ -405,15 +358,14 @@ impl Gl { } } -/// EGL/GLES2/X11 渲染上下文 +// ============ 渲染上下文 ============ + pub struct RenderContext { pub gl: Gl, - display: egl::EGLDisplay, - _config: egl::EGLConfig, - context: egl::EGLContext, - surface: egl::EGLSurface, - x_display: *mut x11::Display, - x_window: x11::Window, + pub display: egl::EGLDisplay, + pub surface: egl::EGLSurface, + pub x_display: *mut x11::Display, + pub x_window: x11::Window, pub program: GLuint, pub attrib_position: GLint, pub attrib_tex_coord: GLint, @@ -426,9 +378,9 @@ pub struct RenderContext { pub height: i32, } -use gl::*; +// 含裸指针,手动声明 Send(仅在单渲染线程使用) +unsafe impl Send for RenderContext {} -/// 创建并链接着色器程序 fn compile_shader(gl: &Gl, shader_type: GLenum, src: &str) -> Result { unsafe { let shader = (gl.gl_create_shader)(shader_type); @@ -441,18 +393,14 @@ fn compile_shader(gl: &Gl, shader_type: GLenum, src: &str) -> Result { (gl.gl_compile_shader)(shader); let mut status = 0; - (gl.gl_get_shaderiv)(shader, GL_COMPILE_STATUS, &mut status); + (gl.gl_get_shaderiv)(shader, gl::GL_COMPILE_STATUS, &mut status); if status == 0 { let mut log = vec![0i8; 1024]; - (gl.gl_get_shader_info_log)( - shader, + (gl.gl_get_shader_info_log)(shader, 1024, std::ptr::null_mut(), log.as_mut_ptr()); + let log_str = String::from_utf8_lossy(std::slice::from_raw_parts( + log.as_ptr() as *const u8, 1024, - std::ptr::null_mut(), - log.as_mut_ptr(), - ); - let log_str = String::from_utf8_lossy( - std::slice::from_raw_parts(log.as_ptr() as *const u8, 1024), - ); + )); return Err(anyhow!("着色器编译失败: {}", log_str)); } Ok(shader) @@ -460,16 +408,14 @@ fn compile_shader(gl: &Gl, shader_type: GLenum, src: &str) -> Result { } impl RenderContext { - /// 创建全屏 X11 窗口 + EGL 上下文 pub fn new_fullscreen(width: i32, height: i32) -> Result { let gl = Gl::load()?; // X11 窗口 let x_display = unsafe { (gl.x_open_display)(std::ptr::null()) }; if x_display.is_null() { - return Err(anyhow!("XOpenDisplay 失败(DISPLAY 环境变量是否设置?)")); + return Err(anyhow!("XOpenDisplay 失败")); } - let root_window: x11::Window = 0; // DefaultRootWindow let mut attrs = x11::XSetWindowAttributes { background_pixmap: 0, background_pixel: 0, @@ -483,21 +429,21 @@ impl RenderContext { save_under: 0, event_mask: x11::ExposureMask | x11::StructureNotifyMask, do_not_propagate_mask: 0, - override_redirect: 1, // 全屏无装饰 + override_redirect: 1, colormap: 0, cursor: 0, }; let x_window = unsafe { (gl.x_create_window)( x_display, - root_window, 0, 0, - width as u32, - height as u32, 0, - 24, // depth - 1, // InputOutput + width as c_uint, + height as c_uint, + 0, + 24, + 1, std::ptr::null_mut(), x11::CWOverrideRedirect | x11::CWEventMask, &mut attrs, @@ -514,19 +460,17 @@ impl RenderContext { } // EGL - let display = unsafe { (gl.egl_get_display)(egl::EGL_DEFAULT_DISPLAY_PTR()) }; + let display = unsafe { (gl.egl_get_display)(egl::default_display()) }; if display.is_null() { return Err(anyhow!("eglGetDisplay 失败")); } let mut major = 0; let mut minor = 0; - let ok = unsafe { (gl.egl_initialize)(display, &mut major, &mut minor) }; - if ok == 0 { + if unsafe { (gl.egl_initialize)(display, &mut major, &mut minor) } == 0 { return Err(anyhow!("eglInitialize 失败")); } - println!("[Live2D] EGL 版本: {}.{}", major, minor); + println!("[Live2D] EGL {}.{}", major, minor); - // 选配置 let config_attrs = [ egl::EGL_RED_SIZE, 8, egl::EGL_GREEN_SIZE, 8, @@ -538,7 +482,7 @@ impl RenderContext { ]; let mut config: egl::EGLConfig = std::ptr::null_mut(); let mut num_config = 0; - let ok = unsafe { + if unsafe { (gl.egl_choose_config)( display, config_attrs.as_ptr(), @@ -546,107 +490,74 @@ impl RenderContext { 1, &mut num_config, ) - }; - if ok == 0 || num_config == 0 { + } == 0 || num_config == 0 + { return Err(anyhow!("eglChooseConfig 失败")); } - // 创建 surface let surface = unsafe { - (gl.egl_create_window_surface)( - display, - config, - x_window as egl::NativeWindowType, - std::ptr::null(), - ) + (gl.egl_create_window_surface)(display, config, x_window as egl::NativeWindowType, std::ptr::null()) }; if surface.is_null() { return Err(anyhow!("eglCreateWindowSurface 失败")); } - // 创建 context(GLES2) let ctx_attrs = [egl::EGL_CONTEXT_CLIENT_VERSION, 2, egl::EGL_NONE]; - let context = unsafe { - (gl.egl_create_context)(display, config, std::ptr::null(), ctx_attrs.as_ptr()) - }; + let context = unsafe { (gl.egl_create_context)(display, config, std::ptr::null(), ctx_attrs.as_ptr()) }; if context.is_null() { return Err(anyhow!("eglCreateContext 失败")); } - // make current - let ok = unsafe { - (gl.egl_make_current)(display, surface, surface, context) - }; - if ok == 0 { + if unsafe { (gl.egl_make_current)(display, surface, surface, context) } == 0 { return Err(anyhow!("eglMakeCurrent 失败")); } - // 设置视口 unsafe { (gl.gl_viewport)(0, 0, width, height); - (gl.gl_clear_color)(0.0, 0.0, 0.0, 0.0); - (gl.gl_enable)(GL_BLEND); - (gl.gl_blend_func)(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + (gl.gl_clear_color)(0.0, 0.0, 0.0, 1.0); + (gl.gl_enable)(gl::GL_BLEND); + (gl.gl_blend_func)(gl::GL_SRC_ALPHA, gl::GL_ONE_MINUS_SRC_ALPHA); } - // 编译着色器 - let vert = compile_shader(&gl, GL_VERTEX_SHADER, VERT_SHADER_SRC)?; - let frag = compile_shader(&gl, GL_FRAGMENT_SHADER, FRAG_SHADER_SRC)?; + let vert = compile_shader(&gl, gl::GL_VERTEX_SHADER, VERT_SHADER_SRC)?; + let frag = compile_shader(&gl, gl::GL_FRAGMENT_SHADER, FRAG_SHADER_SRC)?; let program = unsafe { let p = (gl.gl_create_program)(); (gl.gl_attach_shader)(p, vert); (gl.gl_attach_shader)(p, frag); (gl.gl_link_program)(p); let mut status = 0; - (gl.gl_get_programiv)(p, GL_LINK_STATUS, &mut status); + (gl.gl_get_programiv)(p, gl::GL_LINK_STATUS, &mut status); if status == 0 { let mut log = vec![0i8; 1024]; - (gl.gl_get_program_info_log)( - p, + (gl.gl_get_program_info_log)(p, 1024, std::ptr::null_mut(), log.as_mut_ptr()); + let log_str = String::from_utf8_lossy(std::slice::from_raw_parts( + log.as_ptr() as *const u8, 1024, - std::ptr::null_mut(), - log.as_mut_ptr(), - ); - let log_str = String::from_utf8_lossy( - std::slice::from_raw_parts(log.as_ptr() as *const u8, 1024), - ); + )); return Err(anyhow!("着色器链接失败: {}", log_str)); } p }; - unsafe { (gl.gl_use_program)(program); } - // 获取 attribute/uniform 位置 - let attrib_position = unsafe { - let name = std::ffi::CString::new("a_position")?; - (gl.gl_get_attrib_location)(program, name.as_ptr()) + let get = |name: &str| -> Result { + let c = std::ffi::CString::new(name)?; + Ok(unsafe { (gl.gl_get_attrib_location)(program, c.as_ptr()) }) }; - let attrib_tex_coord = unsafe { - let name = std::ffi::CString::new("a_texCoord")?; - (gl.gl_get_attrib_location)(program, name.as_ptr()) - }; - let uniform_matrix = unsafe { - let name = std::ffi::CString::new("u_matrix")?; - (gl.gl_get_uniform_location)(program, name.as_ptr()) - }; - let uniform_texture = unsafe { - let name = std::ffi::CString::new("s_texture0")?; - (gl.gl_get_uniform_location)(program, name.as_ptr()) - }; - let uniform_base_color = unsafe { - let name = std::ffi::CString::new("u_baseColor")?; - (gl.gl_get_uniform_location)(program, name.as_ptr()) - }; - let uniform_multiply_color = unsafe { - let name = std::ffi::CString::new("u_multiplyColor")?; - (gl.gl_get_uniform_location)(program, name.as_ptr()) - }; - let uniform_screen_color = unsafe { - let name = std::ffi::CString::new("u_screenColor")?; - (gl.gl_get_uniform_location)(program, name.as_ptr()) + let getu = |name: &str| -> Result { + let c = std::ffi::CString::new(name)?; + Ok(unsafe { (gl.gl_get_uniform_location)(program, c.as_ptr()) }) }; + let attrib_position = get("a_position")?; + let attrib_tex_coord = get("a_texCoord")?; + let uniform_matrix = getu("u_matrix")?; + let uniform_texture = getu("s_texture0")?; + let uniform_base_color = getu("u_baseColor")?; + let uniform_multiply_color = getu("u_multiplyColor")?; + let uniform_screen_color = getu("u_screenColor")?; + println!( "[Live2D] GL 程序就绪: program={} pos={} tex={} mat={} tex_u={} base={} mult={} scr={}", program, attrib_position, attrib_tex_coord, uniform_matrix, @@ -656,8 +567,6 @@ impl RenderContext { Ok(Self { gl, display, - _config: config, - context, surface, x_display, x_window, @@ -674,63 +583,41 @@ impl RenderContext { }) } - /// 上传纹理到 GPU pub fn create_texture(&self, img: &super::assets::TextureImage) -> GLuint { unsafe { let mut tex = 0; (self.gl.gl_gen_textures)(1, &mut tex); - (self.gl.gl_bind_texture)(GL_TEXTURE_2D, tex); - (self.gl.gl_tex_parameteri)(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR as i32); - (self.gl.gl_tex_parameteri)(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR as i32); - (self.gl.gl_tex_parameteri)(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE as i32); - (self.gl.gl_tex_parameteri)(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE as i32); + (self.gl.gl_bind_texture)(gl::GL_TEXTURE_2D, tex); + (self.gl.gl_tex_parameteri)(gl::GL_TEXTURE_2D, gl::GL_TEXTURE_MIN_FILTER, gl::GL_LINEAR as GLint); + (self.gl.gl_tex_parameteri)(gl::GL_TEXTURE_2D, gl::GL_TEXTURE_MAG_FILTER, gl::GL_LINEAR as GLint); + (self.gl.gl_tex_parameteri)(gl::GL_TEXTURE_2D, gl::GL_TEXTURE_WRAP_S, gl::GL_CLAMP_TO_EDGE as GLint); + (self.gl.gl_tex_parameteri)(gl::GL_TEXTURE_2D, gl::GL_TEXTURE_WRAP_T, gl::GL_CLAMP_TO_EDGE as GLint); (self.gl.gl_tex_image_2d)( - GL_TEXTURE_2D, + gl::GL_TEXTURE_2D, 0, - GL_RGBA as i32, - img.width as i32, - img.height as i32, + gl::GL_RGBA as GLint, + img.width as GLsizei, + img.height as GLsizei, 0, - GL_RGBA, - GL_UNSIGNED_BYTE, + gl::GL_RGBA, + gl::GL_UNSIGNED_BYTE, img.rgba.as_ptr() as *const c_void, ); let err = (self.gl.gl_get_error)(); - if err != GL_NO_ERROR { + if err != gl::GL_NO_ERROR { eprintln!("[Live2D] glTexImage2D 错误: 0x{:x}", err); } tex } } - /// 交换缓冲区(显示到屏幕) pub fn swap_buffers(&self) { unsafe { - (self.gl.gl_clear)(GL_COLOR_BUFFER_BIT); - // 注意:clear 在绘制前调用,这里 swap 只是提交 - // 实际渲染流程:clear → draw → swap (self.gl.egl_swap_buffers)(self.display, self.surface); } } - /// 清屏 pub fn clear(&self) { - unsafe { (self.gl.gl_clear)(GL_COLOR_BUFFER_BIT); } - } -} - -impl Drop for RenderContext { - fn drop(&mut self) { - // 简单清理(EGL/X11 资源在进程退出时自动释放) - unsafe { - (self.gl.x_flush)(self.x_display); - } - } -} - -// EGL_DEFAULT_DISPLAY 常量 -impl egl { - pub fn EGL_DEFAULT_DISPLAY_PTR() -> NativeDisplayType { - std::ptr::null_mut() + unsafe { (self.gl.gl_clear)(gl::GL_COLOR_BUFFER_BIT); } } } diff --git a/src/plugins/live2d/model.rs b/src/plugins/live2d/model.rs index 39a28f7..7b3d94e 100644 --- a/src/plugins/live2d/model.rs +++ b/src/plugins/live2d/model.rs @@ -3,8 +3,7 @@ //! 封装 moc3 读取 → revive → model 初始化 → 参数更新 → drawable 数据提取。 use super::core_ffi::{ - alloc_aligned, AlignedBuffer, CubismCore, CsmVector2, CsmVector4, CSM_ALIGNOF_MOC, - CSM_ALIGNOF_MODEL, + alloc_aligned, AlignedBuffer, CubismCore, CsmVector2, CSM_ALIGNOF_MOC, CSM_ALIGNOF_MODEL, }; use anyhow::{anyhow, Context, Result}; use std::path::Path; @@ -308,9 +307,3 @@ impl CubismModel { } } } - -impl CubismModel { - // 保留 CsmVector4 引用避免 unused - #[allow(dead_code)] - fn _ensure_v4_linked(_v: CsmVector4) {} -} diff --git a/src/plugins/live2d/plugin.rs b/src/plugins/live2d/plugin.rs index 8d8f76a..16510e3 100644 --- a/src/plugins/live2d/plugin.rs +++ b/src/plugins/live2d/plugin.rs @@ -6,7 +6,6 @@ use crate::core::config::RenderType; use crate::core::message::{Destination, Envelope, Message}; use crate::core::plugin::{Platform, Plugin, PluginContext, PluginInfo}; -use crate::core::plugin_ids; use anyhow::{Context, Result}; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; @@ -103,7 +102,7 @@ impl Plugin for Live2DPlugin { } impl Live2DPlugin { - fn start_live2d(&mut self, config: &crate::core::config::Config) -> Result<()> { + fn start_live2d(&mut self, config: &crate::core::config::AppConfig) -> Result<()> { // 先停止旧的渲染 self.stop_live2d(); @@ -155,8 +154,8 @@ impl Live2DPlugin { from: "live2d".to_string(), to: Destination::Manager, message: Message::StateChanged { - old_state: Some("live2d_running".into()), - new_state: Some("live2d_error".into()), + old_state: "live2d_running".into(), + new_state: "live2d_error".into(), }, }); break; @@ -206,7 +205,7 @@ fn find_core_so() -> Result { } /// 查找 live2d 资源根目录 -fn find_live2d_base(config: &crate::core::config::Config) -> Result { +fn find_live2d_base(config: &crate::core::config::AppConfig) -> Result { let candidates = [ PathBuf::from("configs/live2d"), PathBuf::from("/home/showen/Showen/ShowenV2/configs/live2d"), @@ -219,7 +218,3 @@ fn find_live2d_base(config: &crate::core::config::Config) -> Result { } anyhow::bail!("找不到 configs/live2d 目录") } - -// 保留 plugin_ids 引用避免 unused -#[allow(unused_imports)] -use plugin_ids as _; diff --git a/src/plugins/live2d/renderer.rs b/src/plugins/live2d/renderer.rs index 87936b6..c2608a5 100644 --- a/src/plugins/live2d/renderer.rs +++ b/src/plugins/live2d/renderer.rs @@ -3,7 +3,7 @@ use super::animation::AnimationState; use super::assets::{self, Model3Json, TextureImage}; use super::core_ffi::{self, CubismCore}; -use super::gl::{self, RenderContext}; +use super::gl::{self as gl_api, RenderContext}; use super::model::{CubismModel, DrawableSnapshot}; use anyhow::{Context, Result}; use std::path::Path; @@ -36,7 +36,7 @@ impl Live2DRenderer { let paths = assets::resolve_asset_paths(live2d_base_dir, model3_json_rel)?; let model3 = Model3Json::from_path(&paths.model3_json)?; println!( - "[Live2D] model3.json: 版本={} moc={} 纹理数={}", + "[Live2D] model3.json: 版本={} moc={} 纹理?{}", model3.version, model3.file_references.moc, model3.file_references.textures.len() @@ -48,7 +48,7 @@ impl Live2DRenderer { let gl_ctx = RenderContext::new_fullscreen(width, height)?; let texture_ids: Vec = textures.iter().map(|t| gl_ctx.create_texture(t)).collect(); - println!("[Live2D] 已上传 {} 个纹理到 GPU", texture_ids.len()); + println!("[Live2D] 已上?{} 个纹理到 GPU", texture_ids.len()); let canvas = model.canvas; let model_matrix = compute_model_matrix(canvas, width, height); @@ -84,13 +84,13 @@ impl Live2DRenderer { unsafe { let gl = &self.gl_ctx.gl; (gl.gl_clear_color)(0.0, 0.0, 0.0, 1.0); - (gl.gl_clear)(gl::GL_COLOR_BUFFER_BIT); - (gl.gl_enable)(gl::GL_BLEND); + (gl.gl_clear)(gl_api::GL_COLOR_BUFFER_BIT); + (gl.gl_enable)(gl_api::GL_BLEND); (gl.gl_use_program)(self.gl_ctx.program); (gl.gl_uniform_matrix4fv)( self.gl_ctx.uniform_matrix, 1, - gl::GL_FALSE, + gl_api::GL_FALSE, self.model_matrix.as_ptr(), ); } @@ -117,13 +117,12 @@ impl Live2DRenderer { // 纹理 let tex_idx = snap.texture_index as usize; if tex_idx < self.texture_ids.len() { - (gl.gl_active_texture)(gl::GL_TEXTURE0); - (gl.gl_bind_texture)(gl::GL_TEXTURE_2D, self.texture_ids[tex_idx]); + (gl.gl_active_texture)(gl_api::GL_TEXTURE0); + (gl.gl_bind_texture)(gl_api::GL_TEXTURE_2D, self.texture_ids[tex_idx]); (gl.gl_uniform1i)(self.gl_ctx.uniform_texture, 0); } - // base color(不透明度乘到 alpha) - (gl.gl_uniform4f)(self.gl_ctx.uniform_base_color, 1.0, 1.0, 1.0, snap.opacity); + // base color(不透明度乘?alpha? (gl.gl_uniform4f)(self.gl_ctx.uniform_base_color, 1.0, 1.0, 1.0, snap.opacity); // multiply/screen color let mc = snap.multiply_color; (gl.gl_uniform4f)(self.gl_ctx.uniform_multiply_color, mc[0], mc[1], mc[2], mc[3]); @@ -133,16 +132,16 @@ impl Live2DRenderer { // 混合模式 match snap.blend_mode { core_ffi::blend_mode::NORMAL => { - (gl.gl_blend_func)(gl::GL_SRC_ALPHA, gl::GL_ONE_MINUS_SRC_ALPHA); + (gl.gl_blend_func)(gl_api::GL_SRC_ALPHA, gl_api::GL_ONE_MINUS_SRC_ALPHA); } core_ffi::blend_mode::ADDITIVE => { - (gl.gl_blend_func)(gl::GL_SRC_ALPHA, gl::GL_ONE); + (gl.gl_blend_func)(gl_api::GL_SRC_ALPHA, gl_api::GL_ONE); } core_ffi::blend_mode::MULTIPLICATIVE => { - (gl.gl_blend_func)(gl::GL_ZERO, gl::GL_SRC_COLOR); + (gl.gl_blend_func)(gl_api::GL_ZERO, gl_api::GL_SRC_COLOR); } _ => { - (gl.gl_blend_func)(gl::GL_SRC_ALPHA, gl::GL_ONE_MINUS_SRC_ALPHA); + (gl.gl_blend_func)(gl_api::GL_SRC_ALPHA, gl_api::GL_ONE_MINUS_SRC_ALPHA); } } @@ -158,22 +157,22 @@ impl Live2DRenderer { let mut vbo = 0u32; (gl.gl_gen_buffers)(1, &mut vbo); - (gl.gl_bind_buffer)(gl::GL_ARRAY_BUFFER, vbo); + (gl.gl_bind_buffer)(gl_api::GL_ARRAY_BUFFER, vbo); (gl.gl_buffer_data)( - gl::GL_ARRAY_BUFFER, + gl_api::GL_ARRAY_BUFFER, (vertex_data.len() * 4) as isize, vertex_data.as_ptr() as *const std::ffi::c_void, - gl::GL_STATIC_DRAW, + gl_api::GL_STATIC_DRAW, ); let mut ibo = 0u32; (gl.gl_gen_buffers)(1, &mut ibo); - (gl.gl_bind_buffer)(gl::GL_ELEMENT_ARRAY_BUFFER, ibo); + (gl.gl_bind_buffer)(gl_api::GL_ELEMENT_ARRAY_BUFFER, ibo); (gl.gl_buffer_data)( - gl::GL_ELEMENT_ARRAY_BUFFER, + gl_api::GL_ELEMENT_ARRAY_BUFFER, (snap.indices.len() * 2) as isize, snap.indices.as_ptr() as *const std::ffi::c_void, - gl::GL_STATIC_DRAW, + gl_api::GL_STATIC_DRAW, ); let stride = 4 * 4; @@ -182,8 +181,8 @@ impl Live2DRenderer { (gl.gl_vertex_attrib_pointer)( self.gl_ctx.attrib_position as u32, 2, - gl::GL_FLOAT, - gl::GL_FALSE, + gl_api::GL_FLOAT, + gl_api::GL_FALSE, stride, std::ptr::null(), ); @@ -193,17 +192,17 @@ impl Live2DRenderer { (gl.gl_vertex_attrib_pointer)( self.gl_ctx.attrib_tex_coord as u32, 2, - gl::GL_FLOAT, - gl::GL_FALSE, + gl_api::GL_FLOAT, + gl_api::GL_FALSE, stride, (2 * 4) as *const std::ffi::c_void, ); } (gl.gl_draw_elements)( - gl::GL_TRIANGLES, + gl_api::GL_TRIANGLES, snap.indices.len() as i32, - gl::GL_UNSIGNED_SHORT, + gl_api::GL_UNSIGNED_SHORT, std::ptr::null(), ); @@ -219,7 +218,7 @@ impl Live2DRenderer { while self.running { let start = Instant::now(); if let Err(e) = self.render_frame() { - eprintln!("[Live2D] 渲染帧失败: {e}"); + eprintln!("[Live2D] 渲染帧失? {e}"); break; } let elapsed = start.elapsed(); @@ -235,14 +234,11 @@ impl Live2DRenderer { } } -/// 计算 model → NDC 变换矩阵(列优先 4x4) -/// -/// Cubism 坐标:原点在 canvas 中心,Y 轴向上,单位为像素。 -/// 需要等比缩放使模型完整显示在窗口内。 -fn compute_model_matrix( +/// 计算 model ?NDC 变换矩阵(列优先 4x4?/// +/// Cubism 坐标:原点在 canvas 中心,Y 轴向上,单位为像素?/// 需要等比缩放使模型完整显示在窗口内?fn compute_model_matrix( canvas: super::model::CanvasInfo, - width: i32, - height: i32, + _width: i32, + _height: i32, ) -> [f32; 16] { let canvas_w = canvas.size[0].max(1.0); let canvas_h = canvas.size[1].max(1.0);