feat(live2d): 添加 Pose 处理模块以管理互斥 Part 的显示状态,优化模型加载和参数更新

This commit is contained in:
2026-07-09 00:29:56 +08:00
parent 514fca6000
commit 9e31aee42d
5 changed files with 327 additions and 6 deletions

View File

@@ -64,6 +64,12 @@ pub struct CubismModel {
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 信息
@@ -134,6 +140,20 @@ impl CubismModel {
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;
@@ -152,8 +172,8 @@ impl CubismModel {
};
println!(
"[Live2D] 模型加载成功: 参数={} drawable={} canvas={:?} origin=({:.1},{:.1})x{:.0}ppu",
param_count, drawable_count, canvas.size, canvas.origin[0], canvas.origin[1], canvas.pixels_per_unit
"[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 {
@@ -165,6 +185,9 @@ impl CubismModel {
param_count,
param_ids,
param_index,
part_count,
part_ids,
part_index,
drawable_count,
canvas,
})
@@ -205,19 +228,34 @@ impl CubismModel {
/// 按参数 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;
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[idx])
Some(slice[index])
}
/// 将所有参数重置为默认值
@@ -229,6 +267,36 @@ impl CubismModel {
}
}
/// 获取 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) };