Files
ShowenV2/src/plugins/live2d/assets.rs
pulsareonbot 2aac62b2bb 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.
2026-07-07 16:04:49 +08:00

156 lines
4.4 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.
//! 资源管理model3.json 解析 + PNG 纹理加载
use anyhow::{anyhow, Context, Result};
use serde::Deserialize;
use std::path::{Path, PathBuf};
/// model3.json 中的 Groups 条目
#[derive(Deserialize, Clone, Debug)]
pub struct ModelGroup {
#[serde(rename = "Target")]
pub target: String,
#[serde(rename = "Name")]
pub name: String,
#[serde(rename = "Ids")]
pub ids: Vec<String>,
}
/// model3.json 文件引用
#[derive(Deserialize, Clone, Debug)]
pub struct FileReferences {
#[serde(rename = "Moc")]
pub moc: String,
#[serde(rename = "Textures", default)]
pub textures: Vec<String>,
#[serde(rename = "Physics", default)]
pub physics: Option<String>,
#[serde(rename = "Pose", default)]
pub pose: Option<String>,
#[serde(rename = "DisplayInfo", default)]
pub display_info: Option<String>,
}
/// model3.json 根结构
#[derive(Deserialize, Clone, Debug)]
pub struct Model3Json {
#[serde(rename = "Version")]
pub version: i32,
#[serde(rename = "FileReferences")]
pub file_references: FileReferences,
#[serde(rename = "Groups", default)]
pub groups: Vec<ModelGroup>,
}
impl Model3Json {
/// 从文件解析
pub fn from_path(path: &Path) -> Result<Self> {
let text = std::fs::read_to_string(path)
.with_context(|| format!("读取 model3.json 失败: {}", path.display()))?;
let json: Model3Json = serde_json::from_str(&text)
.with_context(|| format!("解析 model3.json 失败: {}", path.display()))?;
Ok(json)
}
/// 获取 LipSync 参数 ID 列表
pub fn lip_sync_ids(&self) -> Vec<String> {
self.groups
.iter()
.find(|g| g.target == "Parameter" && g.name == "LipSync")
.map(|g| g.ids.clone())
.unwrap_or_else(|| vec!["ParamMouthOpenY".to_string()])
}
/// 获取 EyeBlink 参数 ID 列表
pub fn eye_blink_ids(&self) -> Vec<String> {
self.groups
.iter()
.find(|g| g.target == "Parameter" && g.name == "EyeBlink")
.map(|g| g.ids.clone())
.unwrap_or_else(|| {
vec!["ParamEyeLOpen".to_string(), "ParamEyeROpen".to_string()]
})
}
}
/// 加载的纹理图像RGBA
pub struct TextureImage {
pub width: u32,
pub height: u32,
pub rgba: Vec<u8>,
}
/// 加载 PNG 纹理(使用 image crate
pub fn load_texture(path: &Path) -> Result<TextureImage> {
let img = image::open(path)
.with_context(|| format!("加载纹理失败: {}", path.display()))?
.to_rgba8();
let width = img.width();
let height = img.height();
let rgba = img.into_raw();
Ok(TextureImage {
width,
height,
rgba,
})
}
/// 加载 model3.json 引用的所有纹理(返回按 texture_00, texture_01 顺序的列表)
pub fn load_all_textures(
model3_dir: &Path,
file_refs: &FileReferences,
) -> Result<Vec<TextureImage>> {
let mut textures = Vec::new();
for tex_rel in &file_refs.textures {
let tex_path = model3_dir.join(tex_rel);
let img = load_texture(&tex_path)?;
println!(
"[Live2D] 纹理加载: {} ({}x{})",
tex_rel, img.width, img.height
);
textures.push(img);
}
Ok(textures)
}
/// 解析 model3.json 并返回完整路径集
pub struct ModelAssetPaths {
pub model3_json: PathBuf,
pub model3_dir: PathBuf,
pub moc3: PathBuf,
pub textures: Vec<PathBuf>,
}
/// 从 model3.json 相对路径(如 "haru/Haru.model3.json")解析资源路径
pub fn resolve_asset_paths(
live2d_base_dir: &Path,
model3_json_rel: &str,
) -> Result<ModelAssetPaths> {
let model3_json = live2d_base_dir.join(model3_json_rel);
if !model3_json.exists() {
return Err(anyhow!(
"model3.json 不存在: {}",
model3_json.display()
));
}
let model3_dir = model3_json
.parent()
.ok_or_else(|| anyhow!("无法获取 model3.json 的父目录"))?
.to_path_buf();
let model3 = Model3Json::from_path(&model3_json)?;
let moc3 = model3_dir.join(&model3.file_references.moc);
let textures = model3
.file_references
.textures
.iter()
.map(|t| model3_dir.join(t))
.collect();
Ok(ModelAssetPaths {
model3_json,
model3_dir,
moc3,
textures,
})
}