summaryrefslogtreecommitdiff
path: root/world/src/vrm.rs
diff options
context:
space:
mode:
Diffstat (limited to 'world/src/vrm.rs')
-rw-r--r--world/src/vrm.rs107
1 files changed, 96 insertions, 11 deletions
diff --git a/world/src/vrm.rs b/world/src/vrm.rs
index a9ea6f4..1b89642 100644
--- a/world/src/vrm.rs
+++ b/world/src/vrm.rs
@@ -1,6 +1,65 @@
-use std::collections::BTreeMap;
+/*
+ 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 <https://www.gnu.org/licenses/>.
+*/
+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<usize>,
+}
+
+pub fn extract_vrm_data(gltf: &Gltf) -> Result<VrmInfo> {
+ 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 {
@@ -10,29 +69,55 @@ pub struct Vrm {
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VrmHumanoid {
- pub human_bones: Vec<VrmHumanBone>,
+ human_bones: Vec<VrmHumanBone>,
+}
+
+#[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<VrmcFirstPerson>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
-pub struct VrmHumanBone {
- pub bone: String,
- pub node: usize,
+struct VrmcFirstPerson {
+ mesh_annotations: Vec<VrmcFirstPersonMeshAnnotation>,
}
#[derive(Debug, Deserialize)]
-pub struct Vrmc {
- pub humanoid: VrmcHumanoid,
+#[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")]
-pub struct VrmcHumanoid {
- pub human_bones: BTreeMap<String, VrmcHumanBone>,
+struct VrmcHumanoid {
+ human_bones: BTreeMap<String, VrmcHumanBone>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
-pub struct VrmcHumanBone {
- pub node: usize,
+struct VrmcHumanBone {
+ node: usize,
}