Files
ShowenV2/src/plugins/live2d/model.rs

395 lines
14 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 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()
}
}
}