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:
2026-07-07 16:04:49 +08:00
parent 9637bdd323
commit 2aac62b2bb
14 changed files with 1865 additions and 0 deletions

316
src/plugins/live2d/model.rs Normal file
View 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) {}
}