Skip to main content

vrm_spec/
vrmc_spring_bone_1_0.rs

1//! Data structures for the [`VRMC_springBone`](https://github.com/vrm-c/vrm-specification/tree/master/specification/VRMC_springBone-1.0) 1.0 glTF Extension.
2
3#[cfg(feature = "rustc_hash")]
4use rustc_hash::FxHashMap as HashMap;
5use serde::{Deserialize, Serialize};
6#[cfg(not(feature = "rustc_hash"))]
7use std::collections::HashMap;
8
9/// VRMC_springBone extension name
10pub const VRMC_SPRING_BONE: &str = "VRMC_springBone";
11
12/// SpringBone makes objects such as costumes and hair swaying
13#[derive(Debug, Clone, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct VrmcSpringBoneSchema {
16    /// An array of colliderGroups.
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub collider_groups: Option<Vec<ColliderGroup>>,
19
20    /// An array of colliders.
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub colliders: Option<Vec<Collider>>,
23
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub extensions: Option<HashMap<String, HashMap<String, Option<serde_json::Value>>>>,
26
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub extras: Option<serde_json::Value>,
29
30    /// Specification version of VRMC_springBone
31    pub spec_version: String,
32
33    /// An array of springs.
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub springs: Option<Vec<Spring>>,
36}
37
38/// collider group definition for SpringBone
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct ColliderGroup {
41    /// An array of colliders.
42    pub colliders: Vec<usize>,
43
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub extensions: Option<HashMap<String, HashMap<String, Option<serde_json::Value>>>>,
46
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub extras: Option<serde_json::Value>,
49
50    /// Name of the ColliderGroup
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub name: Option<String>,
53}
54
55/// collider definition for SpringBone
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct Collider {
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub extensions: Option<HashMap<String, HashMap<String, Option<serde_json::Value>>>>,
60
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub extras: Option<serde_json::Value>,
63
64    /// The node index.
65    #[cfg(feature = "gltf_index")]
66    pub node: gltf::json::Index<gltf::json::Node>,
67    #[cfg(not(feature = "gltf_index"))]
68    pub node: usize,
69
70    pub shape: ColliderShape,
71}
72
73/// Shape of collider. Have one of sphere and capsule
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct ColliderShape {
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub capsule: Option<ColliderShapeCapsule>,
78
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub extensions: Option<HashMap<String, HashMap<String, Option<serde_json::Value>>>>,
81
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub extras: Option<serde_json::Value>,
84
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub sphere: Option<ColliderShapeSphere>,
87}
88
89#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
90pub struct ColliderShapeCapsule {
91    /// The capsule head. vector3
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub offset: Option<[f64; 3]>,
94
95    /// The capsule radius
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub radius: Option<f64>,
98
99    /// The capsule tail. vector3
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub tail: Option<[f64; 3]>,
102}
103
104#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
105pub struct ColliderShapeSphere {
106    /// The sphere center. vector3
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub offset: Option<[f64; 3]>,
109
110    /// The sphere radius
111    #[serde(skip_serializing_if = "Option::is_none")]
112    pub radius: Option<f64>,
113}
114
115/// A bone group of VRMCSpringBone.
116#[derive(Debug, Clone, Serialize, Deserialize)]
117#[serde(rename_all = "camelCase")]
118pub struct Spring {
119    /// An index of node which is used as a root of center space.
120    #[cfg(feature = "gltf_index")]
121    pub center: Option<gltf::json::Index<gltf::json::Node>>,
122    #[cfg(not(feature = "gltf_index"))]
123    pub center: Option<usize>,
124
125    /// Indices of ColliderGroups that detect collision with this spring.
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub collider_groups: Option<Vec<i64>>,
128
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub extensions: Option<HashMap<String, HashMap<String, Option<serde_json::Value>>>>,
131
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub extras: Option<serde_json::Value>,
134
135    /// Joints of the spring. Except for the first element, a previous joint of the array must be
136    /// an ancestor of the joint.
137    pub joints: Vec<SpringBoneJoint>,
138
139    /// Name of the Spring
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub name: Option<String>,
142}
143
144/// A bone joint of VRMCSpringBone.
145#[derive(Debug, Clone, Serialize, Deserialize)]
146#[serde(rename_all = "camelCase")]
147pub struct SpringBoneJoint {
148    /// Air resistance. Deceleration force.
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub drag_force: Option<f64>,
151
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub extensions: Option<HashMap<String, HashMap<String, Option<serde_json::Value>>>>,
154
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub extras: Option<serde_json::Value>,
157
158    /// The direction of gravity. A gravity other than downward direction also works.
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub gravity_dir: Option<[f64; 3]>,
161
162    /// Gravitational acceleration.
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub gravity_power: Option<f64>,
165
166    /// The radius of spring sphere.
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub hit_radius: Option<f64>,
169
170    /// The node index.
171    #[cfg(feature = "gltf_index")]
172    pub node: gltf::json::Index<gltf::json::Node>,
173    #[cfg(not(feature = "gltf_index"))]
174    pub node: usize,
175
176    /// The force to return to the initial pose.
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub stiffness: Option<f64>,
179}