/*
wearechat - generic multiplayer game with voip
Copyright (C) 2025 metamuffin
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, version 3 of the License only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
use anyhow::Result;
use gltf::Gltf;
use serde::Deserialize;
use std::{
collections::{BTreeMap, BTreeSet},
fs::File,
};
pub struct VrmInfo {
pub bone_node_names: Vec<(usize, String)>,
pub hide_first_person: BTreeSet,
}
pub fn extract_vrm_data(gltf: &Gltf) -> Result {
let mut bone_node_names = Vec::new();
let mut hide_first_person = BTreeSet::new();
if let Some(vrm) = gltf.extension_value("VRM") {
serde_json::to_writer(File::create("/tmp/vrm").unwrap(), vrm).unwrap();
let vrm: Vrm = serde_json::from_value(vrm.clone())?;
for bone in vrm.humanoid.human_bones {
bone_node_names.push((bone.node, bone.bone))
}
}
if let Some(vrm) = gltf.extension_value("VRMC_vrm") {
serde_json::to_writer(File::create("/tmp/vrmc").unwrap(), vrm).unwrap();
let vrm: Vrmc = serde_json::from_value(vrm.clone())?;
for (name, bone) in vrm.humanoid.human_bones {
bone_node_names.push((bone.node, name))
}
if let Some(fp) = vrm.first_person {
for ann in fp.mesh_annotations {
match ann.first_person_flag {
FirstPersonFlag::ThirdPersonOnly => {
hide_first_person.insert(ann.node);
}
_ => (),
}
}
}
}
eprintln!("hide {hide_first_person:?}");
Ok(VrmInfo {
bone_node_names,
hide_first_person,
})
}
#[derive(Debug, Deserialize)]
pub struct Vrm {
pub humanoid: VrmHumanoid,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VrmHumanoid {
human_bones: Vec,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct VrmHumanBone {
bone: String,
node: usize,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Vrmc {
humanoid: VrmcHumanoid,
first_person: Option,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct VrmcFirstPerson {
mesh_annotations: Vec,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct VrmcFirstPersonMeshAnnotation {
node: usize,
#[serde(alias = "type")] // spec says something but their sample uses "type" instead
first_person_flag: FirstPersonFlag,
}
#[derive(Debug, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
enum FirstPersonFlag {
ThirdPersonOnly,
FirstPersonOnly,
Both,
#[default]
Auto,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct VrmcHumanoid {
human_bones: BTreeMap,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct VrmcHumanBone {
node: usize,
}