- Add `gl.rs` for OpenGL ES 2.0 and EGL context management. - Introduce `mod.rs` to organize Live2D plugin modules. - Create `model.rs` for loading and managing Cubism models, including parameter handling and drawable data extraction. - Implement shader compilation and rendering context setup for Live2D models.
277 lines
11 KiB
Rust
277 lines
11 KiB
Rust
//! Cubism Core FFI 绑定
|
||
//!
|
||
//! 直接对应 `Live2DCubismCore.h` 的 C API。
|
||
//! 动态加载 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 对齐要求(字节)
|
||
pub const CSM_ALIGNOF_MOC: usize = 64;
|
||
/// model 对齐要求(字节)
|
||
pub const CSM_ALIGNOF_MODEL: usize = 16;
|
||
|
||
/// 可绘制常量标志位
|
||
pub mod constant_flags {
|
||
pub const BLEND_ADDITIVE: u8 = 1 << 0;
|
||
pub const BLEND_MULTIPLICATIVE: u8 = 1 << 1;
|
||
pub const IS_DOUBLE_SIDED: u8 = 1 << 2;
|
||
pub const IS_INVERTED_MASK: u8 = 1 << 3;
|
||
}
|
||
|
||
/// 可绘制动态标志位
|
||
pub mod dynamic_flags {
|
||
pub const IS_VISIBLE: u8 = 1 << 0;
|
||
pub const VISIBILITY_DID_CHANGE: u8 = 1 << 1;
|
||
pub const OPACITY_DID_CHANGE: u8 = 1 << 2;
|
||
pub const DRAW_ORDER_DID_CHANGE: u8 = 1 << 3;
|
||
pub const RENDER_ORDER_DID_CHANGE: u8 = 1 << 4;
|
||
pub const VERTEX_POSITIONS_DID_CHANGE: u8 = 1 << 5;
|
||
pub const BLEND_COLOR_DID_CHANGE: u8 = 1 << 6;
|
||
}
|
||
|
||
/// 混合模式
|
||
pub mod blend_mode {
|
||
pub const NORMAL: c_int = 0;
|
||
pub const ADDITIVE: c_int = 1;
|
||
pub const MULTIPLICATIVE: c_int = 2;
|
||
}
|
||
|
||
#[repr(C)]
|
||
#[derive(Clone, Copy, Debug, Default)]
|
||
pub struct CsmVector2 {
|
||
pub x: f32,
|
||
pub y: f32,
|
||
}
|
||
|
||
#[repr(C)]
|
||
#[derive(Clone, Copy, Debug, Default)]
|
||
pub struct CsmVector4 {
|
||
pub x: f32,
|
||
pub y: f32,
|
||
pub z: f32,
|
||
pub w: f32,
|
||
}
|
||
|
||
/// Cubism Core 动态加载封装
|
||
pub struct CubismCore {
|
||
_libm: Library,
|
||
_lib: Library,
|
||
pub csm_get_version: unsafe extern "C" fn() -> c_uint,
|
||
pub csm_get_latest_moc_version: unsafe extern "C" fn() -> c_uint,
|
||
pub csm_get_moc_version:
|
||
unsafe extern "C" fn(address: *const c_void, size: c_uint) -> c_uint,
|
||
pub csm_has_moc_consistency:
|
||
unsafe extern "C" fn(address: *mut c_void, size: c_uint) -> c_int,
|
||
pub csm_revive_moc_in_place:
|
||
unsafe extern "C" fn(address: *mut c_void, size: c_uint) -> *mut c_void,
|
||
pub csm_get_sizeof_model: unsafe extern "C" fn(moc: *const c_void) -> c_uint,
|
||
pub csm_initialize_model_in_place: unsafe extern "C" fn(
|
||
moc: *const c_void,
|
||
address: *mut c_void,
|
||
size: c_uint,
|
||
) -> *mut c_void,
|
||
pub csm_update_model: unsafe extern "C" fn(model: *mut c_void),
|
||
pub csm_get_render_orders: unsafe extern "C" fn(model: *const c_void) -> *const c_int,
|
||
pub csm_read_canvas_info: unsafe extern "C" fn(
|
||
model: *const c_void,
|
||
out_size: *mut CsmVector2,
|
||
out_origin: *mut CsmVector2,
|
||
out_ppu: *mut f32,
|
||
),
|
||
pub csm_get_parameter_count: unsafe extern "C" fn(model: *const c_void) -> c_int,
|
||
pub csm_get_parameter_ids:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const *const c_char,
|
||
pub csm_get_parameter_minimum_values:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const f32,
|
||
pub csm_get_parameter_maximum_values:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const f32,
|
||
pub csm_get_parameter_default_values:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const f32,
|
||
pub csm_get_parameter_values: unsafe extern "C" fn(model: *mut c_void) -> *mut f32,
|
||
pub csm_get_part_count: unsafe extern "C" fn(model: *const c_void) -> c_int,
|
||
pub csm_get_part_opacities: unsafe extern "C" fn(model: *mut c_void) -> *mut f32,
|
||
pub csm_get_drawable_count: unsafe extern "C" fn(model: *const c_void) -> c_int,
|
||
pub csm_get_drawable_ids:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const *const c_char,
|
||
pub csm_get_drawable_constant_flags:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const c_uchar,
|
||
pub csm_get_drawable_dynamic_flags:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const c_uchar,
|
||
pub csm_get_drawable_blend_modes:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const c_int,
|
||
pub csm_get_drawable_texture_indices:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const c_int,
|
||
pub csm_get_drawable_draw_orders:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const c_int,
|
||
pub csm_get_drawable_opacities:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const f32,
|
||
pub csm_get_drawable_mask_counts:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const c_int,
|
||
pub csm_get_drawable_masks:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const *const c_int,
|
||
pub csm_get_drawable_vertex_counts:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const c_int,
|
||
pub csm_get_drawable_vertex_positions:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const *const CsmVector2,
|
||
pub csm_get_drawable_vertex_uvs:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const *const CsmVector2,
|
||
pub csm_get_drawable_index_counts:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const c_int,
|
||
pub csm_get_drawable_indices:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const *const u16,
|
||
pub csm_get_drawable_multiply_colors:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const CsmVector4,
|
||
pub csm_get_drawable_screen_colors:
|
||
unsafe extern "C" fn(model: *const c_void) -> *const CsmVector4,
|
||
pub csm_reset_drawable_dynamic_flags: unsafe extern "C" fn(model: *mut c_void),
|
||
}
|
||
|
||
impl CubismCore {
|
||
/// 加载 Cubism Core 动态库
|
||
///
|
||
/// `so_path` 为 libLive2DCubismCore.so 的路径。
|
||
/// 内部会先 dlopen libm.so(Core 依赖 powf 但 NEEDED 未声明)。
|
||
pub fn load(so_path: &str) -> anyhow::Result<Self> {
|
||
// 预加载 libm,RTLD_GLOBAL 让后续 .so 能解析到 powf
|
||
let libm = unsafe {
|
||
Library::new("libm.so.6").or_else(|_| Library::new("libm.so.6.1"))?
|
||
};
|
||
|
||
let lib = unsafe { Library::new(so_path)? };
|
||
|
||
unsafe {
|
||
Ok(Self {
|
||
csm_get_version: *lib.get(b"csmGetVersion\0")?,
|
||
csm_get_latest_moc_version: *lib.get(b"csmGetLatestMocVersion\0")?,
|
||
csm_get_moc_version: *lib.get(b"csmGetMocVersion\0")?,
|
||
csm_has_moc_consistency: *lib.get(b"csmHasMocConsistency\0")?,
|
||
csm_revive_moc_in_place: *lib.get(b"csmReviveMocInPlace\0")?,
|
||
csm_get_sizeof_model: *lib.get(b"csmGetSizeofModel\0")?,
|
||
csm_initialize_model_in_place: *lib.get(b"csmInitializeModelInPlace\0")?,
|
||
csm_update_model: *lib.get(b"csmUpdateModel\0")?,
|
||
csm_get_render_orders: *lib.get(b"csmGetRenderOrders\0")?,
|
||
csm_read_canvas_info: *lib.get(b"csmReadCanvasInfo\0")?,
|
||
csm_get_parameter_count: *lib.get(b"csmGetParameterCount\0")?,
|
||
csm_get_parameter_ids: *lib.get(b"csmGetParameterIds\0")?,
|
||
csm_get_parameter_minimum_values: *lib
|
||
.get(b"csmGetParameterMinimumValues\0")?,
|
||
csm_get_parameter_maximum_values: *lib
|
||
.get(b"csmGetParameterMaximumValues\0")?,
|
||
csm_get_parameter_default_values: *lib
|
||
.get(b"csmGetParameterDefaultValues\0")?,
|
||
csm_get_parameter_values: *lib.get(b"csmGetParameterValues\0")?,
|
||
csm_get_part_count: *lib.get(b"csmGetPartCount\0")?,
|
||
csm_get_part_opacities: *lib.get(b"csmGetPartOpacities\0")?,
|
||
csm_get_drawable_count: *lib.get(b"csmGetDrawableCount\0")?,
|
||
csm_get_drawable_ids: *lib.get(b"csmGetDrawableIds\0")?,
|
||
csm_get_drawable_constant_flags: *lib
|
||
.get(b"csmGetDrawableConstantFlags\0")?,
|
||
csm_get_drawable_dynamic_flags: *lib
|
||
.get(b"csmGetDrawableDynamicFlags\0")?,
|
||
csm_get_drawable_blend_modes: *lib.get(b"csmGetDrawableBlendModes\0")?,
|
||
csm_get_drawable_texture_indices: *lib
|
||
.get(b"csmGetDrawableTextureIndices\0")?,
|
||
csm_get_drawable_draw_orders: *lib.get(b"csmGetDrawableDrawOrders\0")?,
|
||
csm_get_drawable_opacities: *lib.get(b"csmGetDrawableOpacities\0")?,
|
||
csm_get_drawable_mask_counts: *lib.get(b"csmGetDrawableMaskCounts\0")?,
|
||
csm_get_drawable_masks: *lib.get(b"csmGetDrawableMasks\0")?,
|
||
csm_get_drawable_vertex_counts: *lib
|
||
.get(b"csmGetDrawableVertexCounts\0")?,
|
||
csm_get_drawable_vertex_positions: *lib
|
||
.get(b"csmGetDrawableVertexPositions\0")?,
|
||
csm_get_drawable_vertex_uvs: *lib.get(b"csmGetDrawableVertexUvs\0")?,
|
||
csm_get_drawable_index_counts: *lib.get(b"csmGetDrawableIndexCounts\0")?,
|
||
csm_get_drawable_indices: *lib.get(b"csmGetDrawableIndices\0")?,
|
||
csm_get_drawable_multiply_colors: *lib
|
||
.get(b"csmGetDrawableMultiplyColors\0")?,
|
||
csm_get_drawable_screen_colors: *lib
|
||
.get(b"csmGetDrawableScreenColors\0")?,
|
||
csm_reset_drawable_dynamic_flags: *lib
|
||
.get(b"csmResetDrawableDynamicFlags\0")?,
|
||
_libm: libm,
|
||
_lib: lib,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// 查询 Core 版本(返回原始 u32,格式 0xMMmmpppp)
|
||
pub fn version(&self) -> u32 {
|
||
unsafe { (self.csm_get_version)() }
|
||
}
|
||
|
||
/// 格式化版本号为 "major.minor.patch"
|
||
pub fn version_string(&self) -> String {
|
||
let v = self.version();
|
||
format!(
|
||
"{}.{}.{}",
|
||
(v >> 24) & 0xFF,
|
||
(v >> 16) & 0xFF,
|
||
v & 0xFFFF
|
||
)
|
||
}
|
||
}
|
||
|
||
/// 分配对齐内存(返回对齐指针和底层分配,底层分配需保持存活)
|
||
pub fn alloc_aligned(size: usize, align: usize) -> anyhow::Result<AlignedBuffer> {
|
||
let layout = std::alloc::Layout::from_size_align(size, align)?;
|
||
let ptr = unsafe { std::alloc::alloc(layout) };
|
||
if ptr.is_null() {
|
||
anyhow::bail!("aligned alloc failed for size={size} align={align}");
|
||
}
|
||
Ok(AlignedBuffer { ptr, layout })
|
||
}
|
||
|
||
pub struct AlignedBuffer {
|
||
ptr: *mut u8,
|
||
layout: std::alloc::Layout,
|
||
}
|
||
|
||
impl AlignedBuffer {
|
||
pub fn as_ptr(&self) -> *mut u8 {
|
||
self.ptr
|
||
}
|
||
pub fn as_mut_void(&mut self) -> *mut c_void {
|
||
self.ptr as *mut c_void
|
||
}
|
||
pub fn as_void(&self) -> *const c_void {
|
||
self.ptr as *const c_void
|
||
}
|
||
}
|
||
|
||
impl Drop for AlignedBuffer {
|
||
fn drop(&mut self) {
|
||
unsafe { std::alloc::dealloc(self.ptr, self.layout) }
|
||
}
|
||
}
|
||
|
||
unsafe impl Send for AlignedBuffer {}
|
||
unsafe impl Sync for AlignedBuffer {}
|
||
|
||
/// 将 C 字符串指针转换为 Rust String(空指针返回 None)
|
||
pub unsafe fn cstr_ptr_to_string(ptr: *const c_char) -> Option<String> {
|
||
if ptr.is_null() {
|
||
return None;
|
||
}
|
||
let raw = std::slice::from_raw_parts(ptr as *const u8, libc_strlen(ptr));
|
||
Some(String::from_utf8_lossy(raw).into_owned())
|
||
}
|
||
|
||
/// 简易 strlen(避免依赖 libc crate)
|
||
unsafe fn libc_strlen(ptr: *const c_char) -> usize {
|
||
let mut p = ptr as *const u8;
|
||
let mut n = 0;
|
||
while *p != 0 {
|
||
p = p.add(1);
|
||
n += 1;
|
||
}
|
||
n
|
||
}
|
||
|
||
// 保留 CString 防止 unused 警告(未来日志函数需要)
|
||
#[allow(dead_code)]
|
||
fn _ensure_cstring_linked() -> CString {
|
||
CString::new("").unwrap()
|
||
}
|