395 lines
14 KiB
Rust
395 lines
14 KiB
Rust
//! Cubism Model 加载与管理
|
||
//!
|
||
//! 封装 moc3 读取 → revive → model 初始化 → 参数更新 → drawable 数据提取。
|
||
|
||
use super::core_ffi::{
|
||
alloc_aligned, AlignedBuffer, CubismCore, CsmVector2, CSM_ALIGNOF_MOC, CSM_ALIGNOF_MODEL,
|
||
};
|
||
use anyhow::{anyhow, Context, Result};
|
||
use std::path::Path;
|
||
|
||
/// 一个 drawable 的渲染所需数据快照
|
||
#[derive(Clone)]
|
||
pub struct DrawableSnapshot {
|
||
/// 在 Core drawable 数组中的原始索引(用于查 mask 引用)
|
||
pub index: usize,
|
||
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],
|
||
/// 本 drawable 依赖的 mask drawable 索引(空表示无遮罩)
|
||
pub mask_indices: Vec<i32>,
|
||
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>,
|
||
/// Part 数量
|
||
pub part_count: usize,
|
||
/// Part ID 列表(与 Core 内部顺序一致)
|
||
pub part_ids: Vec<String>,
|
||
/// Part 索引映射(id → index)
|
||
pub part_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);
|
||
}
|
||
|
||
// 读取 part 信息(Pose 会按 PartOpacity 切换互斥部件,如 Haru 的两组手臂)
|
||
let part_count = unsafe { (core.csm_get_part_count)(model) } as usize;
|
||
let part_ids_ptr = unsafe { (core.csm_get_part_ids)(model) };
|
||
let mut part_ids = Vec::with_capacity(part_count);
|
||
let mut part_index = std::collections::HashMap::new();
|
||
for i in 0..part_count {
|
||
let id = unsafe {
|
||
super::core_ffi::cstr_ptr_to_string(*part_ids_ptr.add(i))
|
||
.unwrap_or_default()
|
||
};
|
||
part_index.insert(id.clone(), i);
|
||
part_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] 模型加载成功: 参数={} part={} drawable={} canvas={:?} origin=({:.1},{:.1})x{:.0}ppu",
|
||
param_count, part_count, drawable_count, canvas.size, canvas.origin[0], canvas.origin[1], canvas.pixels_per_unit
|
||
);
|
||
|
||
Ok(Self {
|
||
_core: core,
|
||
_moc_buf: moc_buf,
|
||
moc,
|
||
_model_buf: model_buf,
|
||
model,
|
||
param_count,
|
||
param_ids,
|
||
param_index,
|
||
part_count,
|
||
part_ids,
|
||
part_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) {
|
||
self.set_param_by_index(core, idx, value);
|
||
true
|
||
} else {
|
||
false
|
||
}
|
||
}
|
||
|
||
/// 按参数索引设置值
|
||
pub fn set_param_by_index(&self, core: &CubismCore, index: usize, value: f32) {
|
||
if index < self.param_count {
|
||
let slice = self.param_values_mut(core);
|
||
slice[index] = value;
|
||
}
|
||
}
|
||
|
||
/// 按参数 ID 获取值
|
||
pub fn get_param(&self, core: &CubismCore, id: &str) -> Option<f32> {
|
||
let idx = *self.param_index.get(id)?;
|
||
self.get_param_by_index(core, idx)
|
||
}
|
||
|
||
/// 按参数索引获取值
|
||
pub fn get_param_by_index(&self, core: &CubismCore, index: usize) -> Option<f32> {
|
||
if index >= self.param_count {
|
||
return None;
|
||
}
|
||
let slice = self.param_values_mut(core);
|
||
Some(slice[index])
|
||
}
|
||
|
||
/// 将所有参数重置为默认值
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// 获取 PartOpacity 缓冲区(可读写)
|
||
pub fn part_opacities_mut(&self, core: &CubismCore) -> &mut [f32] {
|
||
unsafe {
|
||
let p = (core.csm_get_part_opacities)(self.model);
|
||
std::slice::from_raw_parts_mut(p, self.part_count)
|
||
}
|
||
}
|
||
|
||
/// 按 Part ID 获取索引
|
||
pub fn part_index(&self, id: &str) -> Option<usize> {
|
||
self.part_index.get(id).copied()
|
||
}
|
||
|
||
/// 按 Part 索引设置 opacity
|
||
pub fn set_part_opacity_by_index(&self, core: &CubismCore, index: usize, opacity: f32) {
|
||
if index < self.part_count {
|
||
let slice = self.part_opacities_mut(core);
|
||
slice[index] = opacity.clamp(0.0, 1.0);
|
||
}
|
||
}
|
||
|
||
/// 按 Part 索引获取 opacity
|
||
pub fn get_part_opacity_by_index(&self, core: &CubismCore, index: usize) -> Option<f32> {
|
||
if index >= self.part_count {
|
||
return None;
|
||
}
|
||
let slice = self.part_opacities_mut(core);
|
||
Some(slice[index])
|
||
}
|
||
|
||
/// 更新 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);
|
||
let masks = (core.csm_get_drawable_masks)(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);
|
||
|
||
// 取出本 drawable 的 mask 索引列表
|
||
let mk = *mask_counts.add(i) as usize;
|
||
let mut mask_indices: Vec<i32> = Vec::with_capacity(mk);
|
||
if mk > 0 {
|
||
let mptr = *masks.add(i);
|
||
for k in 0..mk {
|
||
mask_indices.push(*mptr.add(k));
|
||
}
|
||
}
|
||
|
||
DrawableSnapshot {
|
||
index: i,
|
||
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_indices,
|
||
mask_counts: mk as i32,
|
||
}
|
||
})
|
||
.collect()
|
||
}
|
||
}
|
||
}
|