summaryrefslogtreecommitdiff
path: root/import/src/vrm.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-03-12 17:54:43 +0100
committermetamuffin <metamuffin@disroot.org>2025-03-12 17:54:43 +0100
commitdc8304afefa71037bea99722bee29f7645753836 (patch)
tree4fd2b70d6faec18573bd590442b443b7d7a53c1a /import/src/vrm.rs
parent3ed621256f1e02032250477fa574eab38bd34976 (diff)
downloadweareserver-dc8304afefa71037bea99722bee29f7645753836.tar
weareserver-dc8304afefa71037bea99722bee29f7645753836.tar.bz2
weareserver-dc8304afefa71037bea99722bee29f7645753836.tar.zst
rename crates and binaries
Diffstat (limited to 'import/src/vrm.rs')
-rw-r--r--import/src/vrm.rs135
1 files changed, 135 insertions, 0 deletions
diff --git a/import/src/vrm.rs b/import/src/vrm.rs
new file mode 100644
index 0000000..f014e66
--- /dev/null
+++ b/import/src/vrm.rs
@@ -0,0 +1,135 @@
+/*
+ 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};
+use weareshared::Vec3A;
+
+#[derive(Debug, Default)]
+pub struct VrmInfo {
+ pub bone_node_names: Vec<(usize, String)>,
+ pub hide_first_person: BTreeSet<usize>,
+ pub camera_mount: Option<usize>,
+ pub camera_mount_offset: Option<Vec3A>,
+}
+
+pub fn extract_vrm_data(gltf: &Gltf) -> Result<VrmInfo> {
+ let mut o = VrmInfo::default();
+ let mut fp = None;
+ if let Some(vrm) = gltf.extension_value("VRM") {
+ // serde_json::to_writer(std::fs::File::create("/tmp/vrm").unwrap(), vrm).unwrap();
+ let vrm: Vrm = serde_json::from_value(vrm.clone())?;
+ for bone in vrm.humanoid.human_bones {
+ o.bone_node_names.push((bone.node, bone.bone))
+ }
+ fp = vrm.first_person;
+ }
+ if let Some(vrm) = gltf.extension_value("VRMC_vrm") {
+ // serde_json::to_writer(std::fs::File::create("/tmp/vrmc").unwrap(), vrm).unwrap();
+ let vrm: Vrmc = serde_json::from_value(vrm.clone())?;
+ for (name, bone) in vrm.humanoid.human_bones {
+ o.bone_node_names.push((bone.node, name))
+ }
+ fp = vrm.first_person;
+ }
+ if let Some(fp) = fp {
+ o.camera_mount = fp.first_person_bone;
+ o.camera_mount_offset = fp.first_person_bone_offset.map(convert_vrm_vec);
+ for ann in fp.mesh_annotations {
+ if let FirstPersonFlag::ThirdPersonOnly = ann.first_person_flag {
+ o.hide_first_person.insert(ann.node);
+ }
+ }
+ }
+ Ok(o)
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Vrm {
+ humanoid: VrmHumanoid,
+ first_person: Option<VrmcFirstPerson>,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct VrmHumanoid {
+ 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")]
+struct VrmcFirstPerson {
+ first_person_bone: Option<usize>,
+ first_person_bone_offset: Option<VrmVec3>,
+ mesh_annotations: Vec<VrmcFirstPersonMeshAnnotation>,
+}
+
+#[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<String, VrmcHumanBone>,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct VrmcHumanBone {
+ node: usize,
+}
+
+#[derive(Debug, Deserialize)]
+struct VrmVec3 {
+ x: f32,
+ y: f32,
+ z: f32,
+}
+fn convert_vrm_vec(VrmVec3 { x, y, z }: VrmVec3) -> Vec3A {
+ Vec3A::new(x, y, z)
+}