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.
This commit is contained in:
316
src/plugins/live2d/model.rs
Normal file
316
src/plugins/live2d/model.rs
Normal file
@@ -0,0 +1,316 @@
|
||||
//! Cubism Model 加载与管理
|
||||
//!
|
||||
//! 封装 moc3 读取 → revive → model 初始化 → 参数更新 → drawable 数据提取。
|
||||
|
||||
use super::core_ffi::{
|
||||
alloc_aligned, AlignedBuffer, CubismCore, CsmVector2, CsmVector4, CSM_ALIGNOF_MOC,
|
||||
CSM_ALIGNOF_MODEL,
|
||||
};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use std::path::Path;
|
||||
|
||||
/// 一个 drawable 的渲染所需数据快照
|
||||
#[derive(Clone)]
|
||||
pub struct DrawableSnapshot {
|
||||
pub texture_index: i32,
|
||||
pub draw_order: i32,
|
||||
pub render_order: i32,
|
||||
pub opacity: f32,
|
||||
pub blend_mode: i32,
|
||||
pub constant_flags: u8,
|
||||
pub dynamic_flags: u8,
|
||||
pub vertex_positions: Vec<[f32; 2]>,
|
||||
pub vertex_uvs: Vec<[f32; 2]>,
|
||||
pub indices: Vec<u16>,
|
||||
pub multiply_color: [f32; 4],
|
||||
pub screen_color: [f32; 4],
|
||||
pub mask_counts: i32,
|
||||
}
|
||||
|
||||
/// Canvas 信息
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct CanvasInfo {
|
||||
pub size: [f32; 2],
|
||||
pub origin: [f32; 2],
|
||||
pub pixels_per_unit: f32,
|
||||
}
|
||||
|
||||
/// 参数信息
|
||||
pub struct ParameterInfo {
|
||||
pub id: String,
|
||||
pub min_value: f32,
|
||||
pub max_value: f32,
|
||||
pub default_value: f32,
|
||||
}
|
||||
|
||||
/// 活跃的 Cubism Model
|
||||
pub struct CubismModel {
|
||||
/// Core 句柄(保持引用防止 .so 卸载)
|
||||
_core: std::sync::Arc<CubismCore>,
|
||||
/// moc3 原始数据(对齐 64)
|
||||
_moc_buf: AlignedBuffer,
|
||||
/// moc 指针
|
||||
moc: *mut std::ffi::c_void,
|
||||
/// model 内存(对齐 16)
|
||||
_model_buf: AlignedBuffer,
|
||||
/// model 指针
|
||||
model: *mut std::ffi::c_void,
|
||||
/// 参数数量
|
||||
pub param_count: usize,
|
||||
/// 参数 ID 列表(与 Core 内部顺序一致)
|
||||
pub param_ids: Vec<String>,
|
||||
/// 参数索引映射(id → index)
|
||||
pub param_index: std::collections::HashMap<String, usize>,
|
||||
/// drawable 数量
|
||||
pub drawable_count: usize,
|
||||
/// canvas 信息
|
||||
pub canvas: CanvasInfo,
|
||||
}
|
||||
|
||||
// model 指针可跨线程发送(渲染线程独占使用)
|
||||
unsafe impl Send for CubismModel {}
|
||||
|
||||
impl CubismModel {
|
||||
/// 从 moc3 文件加载 model
|
||||
pub fn load(core: std::sync::Arc<CubismCore>, moc3_path: &Path) -> Result<Self> {
|
||||
let moc_data = std::fs::read(moc3_path)
|
||||
.with_context(|| format!("读取 moc3 失败: {}", moc3_path.display()))?;
|
||||
|
||||
// 分配 64 对齐内存
|
||||
let mut moc_buf = alloc_aligned(moc_data.len(), CSM_ALIGNOF_MOC)?;
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping(moc_data.as_ptr(), moc_buf.as_ptr(), moc_data.len());
|
||||
}
|
||||
|
||||
// 检查一致性
|
||||
let valid = unsafe {
|
||||
(core.csm_has_moc_consistency)(moc_buf.as_mut_void(), moc_data.len() as u32)
|
||||
};
|
||||
if valid != 1 {
|
||||
return Err(anyhow!("moc3 一致性检查失败(文件损坏或版本不支持)"));
|
||||
}
|
||||
|
||||
// Revive moc
|
||||
let moc = unsafe {
|
||||
(core.csm_revive_moc_in_place)(moc_buf.as_mut_void(), moc_data.len() as u32)
|
||||
};
|
||||
if moc.is_null() {
|
||||
return Err(anyhow!("csmReviveMocInPlace 返回空指针"));
|
||||
}
|
||||
|
||||
// 获取 model 所需大小
|
||||
let model_size = unsafe { (core.csm_get_sizeof_model)(moc) };
|
||||
if model_size == 0 {
|
||||
return Err(anyhow!("csmGetSizeofModel 返回 0"));
|
||||
}
|
||||
|
||||
// 分配 16 对齐内存
|
||||
let mut model_buf = alloc_aligned(model_size as usize, CSM_ALIGNOF_MODEL)?;
|
||||
let model = unsafe {
|
||||
(core.csm_initialize_model_in_place)(
|
||||
moc,
|
||||
model_buf.as_mut_void(),
|
||||
model_size,
|
||||
)
|
||||
};
|
||||
if model.is_null() {
|
||||
return Err(anyhow!("csmInitializeModelInPlace 返回空指针"));
|
||||
}
|
||||
|
||||
// 读取参数信息
|
||||
let param_count = unsafe { (core.csm_get_parameter_count)(model) } as usize;
|
||||
let param_ids_ptr = unsafe { (core.csm_get_parameter_ids)(model) };
|
||||
let mut param_ids = Vec::with_capacity(param_count);
|
||||
let mut param_index = std::collections::HashMap::new();
|
||||
for i in 0..param_count {
|
||||
let id = unsafe {
|
||||
super::core_ffi::cstr_ptr_to_string(*param_ids_ptr.add(i))
|
||||
.unwrap_or_default()
|
||||
};
|
||||
param_index.insert(id.clone(), i);
|
||||
param_ids.push(id);
|
||||
}
|
||||
|
||||
// 读取 drawable 数量
|
||||
let drawable_count = unsafe { (core.csm_get_drawable_count)(model) } as usize;
|
||||
|
||||
// 读取 canvas 信息
|
||||
let mut size = CsmVector2::default();
|
||||
let mut origin = CsmVector2::default();
|
||||
let mut ppu = 0.0f32;
|
||||
unsafe {
|
||||
(core.csm_read_canvas_info)(model, &mut size, &mut origin, &mut ppu);
|
||||
}
|
||||
|
||||
let canvas = CanvasInfo {
|
||||
size: [size.x, size.y],
|
||||
origin: [origin.x, origin.y],
|
||||
pixels_per_unit: ppu,
|
||||
};
|
||||
|
||||
println!(
|
||||
"[Live2D] 模型加载成功: 参数={} drawable={} canvas={:?}x{:.0}ppu",
|
||||
param_count, drawable_count, canvas.size, canvas.pixels_per_unit
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
_core: core,
|
||||
_moc_buf: moc_buf,
|
||||
moc,
|
||||
_model_buf: model_buf,
|
||||
model,
|
||||
param_count,
|
||||
param_ids,
|
||||
param_index,
|
||||
drawable_count,
|
||||
canvas,
|
||||
})
|
||||
}
|
||||
|
||||
/// 获取参数最小值
|
||||
pub fn param_min_values(&self, core: &CubismCore) -> Vec<f32> {
|
||||
unsafe {
|
||||
let p = (core.csm_get_parameter_minimum_values)(self.model);
|
||||
std::slice::from_raw_parts(p, self.param_count).to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取参数最大值
|
||||
pub fn param_max_values(&self, core: &CubismCore) -> Vec<f32> {
|
||||
unsafe {
|
||||
let p = (core.csm_get_parameter_maximum_values)(self.model);
|
||||
std::slice::from_raw_parts(p, self.param_count).to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取参数默认值
|
||||
pub fn param_default_values(&self, core: &CubismCore) -> Vec<f32> {
|
||||
unsafe {
|
||||
let p = (core.csm_get_parameter_default_values)(self.model);
|
||||
std::slice::from_raw_parts(p, self.param_count).to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取参数值缓冲区(可读写)
|
||||
pub fn param_values_mut(&self, core: &CubismCore) -> &mut [f32] {
|
||||
unsafe {
|
||||
let p = (core.csm_get_parameter_values)(self.model);
|
||||
std::slice::from_raw_parts_mut(p, self.param_count)
|
||||
}
|
||||
}
|
||||
|
||||
/// 按参数 ID 设置值
|
||||
pub fn set_param(&self, core: &CubismCore, id: &str, value: f32) -> bool {
|
||||
if let Some(&idx) = self.param_index.get(id) {
|
||||
let slice = self.param_values_mut(core);
|
||||
slice[idx] = value;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// 按参数 ID 获取值
|
||||
pub fn get_param(&self, core: &CubismCore, id: &str) -> Option<f32> {
|
||||
let idx = *self.param_index.get(id)?;
|
||||
let slice = self.param_values_mut(core);
|
||||
Some(slice[idx])
|
||||
}
|
||||
|
||||
/// 将所有参数重置为默认值
|
||||
pub fn reset_to_default(&self, core: &CubismCore) {
|
||||
let defaults = self.param_default_values(core);
|
||||
let slice = self.param_values_mut(core);
|
||||
for (i, v) in defaults.into_iter().enumerate() {
|
||||
slice[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新 model(应用参数变化,重新计算顶点/不透明度等)
|
||||
pub fn update(&self, core: &CubismCore) {
|
||||
unsafe { (core.csm_update_model)(self.model) };
|
||||
}
|
||||
|
||||
/// 重置动态标志
|
||||
pub fn reset_dynamic_flags(&self, core: &CubismCore) {
|
||||
unsafe { (core.csm_reset_drawable_dynamic_flags)(self.model) };
|
||||
}
|
||||
|
||||
/// 获取所有 drawable 的渲染数据快照(按 render order 排序)
|
||||
pub fn drawable_snapshots(&self, core: &CubismCore) -> Vec<DrawableSnapshot> {
|
||||
let count = self.drawable_count;
|
||||
unsafe {
|
||||
let render_orders = (core.csm_get_render_orders)(self.model);
|
||||
let texture_indices = (core.csm_get_drawable_texture_indices)(self.model);
|
||||
let draw_orders = (core.csm_get_drawable_draw_orders)(self.model);
|
||||
let opacities = (core.csm_get_drawable_opacities)(self.model);
|
||||
let blend_modes = (core.csm_get_drawable_blend_modes)(self.model);
|
||||
let const_flags = (core.csm_get_drawable_constant_flags)(self.model);
|
||||
let dyn_flags = (core.csm_get_drawable_dynamic_flags)(self.model);
|
||||
let mask_counts = (core.csm_get_drawable_mask_counts)(self.model);
|
||||
let vertex_counts = (core.csm_get_drawable_vertex_counts)(self.model);
|
||||
let vertex_positions = (core.csm_get_drawable_vertex_positions)(self.model);
|
||||
let vertex_uvs = (core.csm_get_drawable_vertex_uvs)(self.model);
|
||||
let index_counts = (core.csm_get_drawable_index_counts)(self.model);
|
||||
let indices = (core.csm_get_drawable_indices)(self.model);
|
||||
let multiply_colors = (core.csm_get_drawable_multiply_colors)(self.model);
|
||||
let screen_colors = (core.csm_get_drawable_screen_colors)(self.model);
|
||||
|
||||
// 先收集每个 drawable 的原始索引
|
||||
let mut items: Vec<(usize, i32)> = (0..count)
|
||||
.map(|i| (i, *render_orders.add(i)))
|
||||
.collect();
|
||||
// 按 render_order 升序排列
|
||||
items.sort_by_key(|&(_, ro)| ro);
|
||||
|
||||
items
|
||||
.into_iter()
|
||||
.map(|(i, ro)| {
|
||||
let vc = *vertex_counts.add(i) as usize;
|
||||
let ic = *index_counts.add(i) as usize;
|
||||
let vp = *vertex_positions.add(i);
|
||||
let vu = *vertex_uvs.add(i);
|
||||
let idx = *indices.add(i);
|
||||
|
||||
let mut verts = Vec::with_capacity(vc);
|
||||
let mut uvs = Vec::with_capacity(vc);
|
||||
for v in 0..vc {
|
||||
let p = *vp.add(v);
|
||||
let u = *vu.add(v);
|
||||
verts.push([p.x, p.y]);
|
||||
uvs.push([u.x, u.y]);
|
||||
}
|
||||
let mut idx_vec = Vec::with_capacity(ic);
|
||||
for k in 0..ic {
|
||||
idx_vec.push(*idx.add(k));
|
||||
}
|
||||
|
||||
let mc = *multiply_colors.add(i);
|
||||
let sc = *screen_colors.add(i);
|
||||
|
||||
DrawableSnapshot {
|
||||
texture_index: *texture_indices.add(i),
|
||||
draw_order: *draw_orders.add(i),
|
||||
render_order: ro,
|
||||
opacity: *opacities.add(i),
|
||||
blend_mode: *blend_modes.add(i),
|
||||
constant_flags: *const_flags.add(i),
|
||||
dynamic_flags: *dyn_flags.add(i),
|
||||
vertex_positions: verts,
|
||||
vertex_uvs: uvs,
|
||||
indices: idx_vec,
|
||||
multiply_color: [mc.x, mc.y, mc.z, mc.w],
|
||||
screen_color: [sc.x, sc.y, sc.z, sc.w],
|
||||
mask_counts: *mask_counts.add(i),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CubismModel {
|
||||
// 保留 CsmVector4 引用避免 unused
|
||||
#[allow(dead_code)]
|
||||
fn _ensure_v4_linked(_v: CsmVector4) {}
|
||||
}
|
||||
Reference in New Issue
Block a user