Files
ShowenV2/src/plugins/live2d/core_ffi.rs
pulsareonbot 2aac62b2bb feat(live2d): implement OpenGL ES 2.0 rendering backend with EGL support
- 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.
2026-07-07 16:04:49 +08:00

277 lines
11 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 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.soCore 依赖 powf 但 NEEDED 未声明)。
pub fn load(so_path: &str) -> anyhow::Result<Self> {
// 预加载 libmRTLD_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()
}