1pub const VRM: &str = "VRM";
5
6#[cfg(feature = "rustc_hash")]
7use rustc_hash::FxHashMap as HashMap;
8use serde::{Deserialize, Serialize};
9#[cfg(not(feature = "rustc_hash"))]
10use std::collections::HashMap;
11
12use crate::serde_utils::{
13 deserialize_option_index, deserialize_option_map_and_skip_nullable,
14 deserialize_option_map_index,
15};
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub struct VRM0Schema {
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub blend_shape_master: Option<VRMBlendShape>,
23
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub exporter_version: Option<String>,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub first_person: Option<VRMFirstPerson>,
30
31 #[serde(skip_serializing_if = "Option::is_none")]
32 pub humanoid: Option<VRMHumanoid>,
33
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub material_properties: Option<Vec<VRMMaterial>>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
38 pub meta: Option<VRMMeta>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub secondary_animation: Option<VRMSecondaryAnimation>,
42
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub spec_version: Option<String>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
50#[serde(rename_all = "camelCase")]
51pub struct VRMBlendShape {
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub blend_shape_groups: Option<Vec<VRMBlendShapeGroup>>,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
57#[serde(rename_all = "camelCase")]
58pub struct VRMBlendShapeGroup {
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub binds: Option<Vec<VRMBlendShapeBind>>,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub is_binary: Option<bool>,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub material_values: Option<Vec<VRMBlendShapeMaterialBind>>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub name: Option<String>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub preset_name: Option<PresetName>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct VRMBlendShapeBind {
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub index: Option<i64>,
84
85 #[serde(
86 default,
87 skip_serializing_if = "Option::is_none",
88 deserialize_with = "deserialize_option_index::<_, gltf::json::Mesh>"
89 )]
90 #[cfg(feature = "gltf_index")]
91 pub mesh: Option<gltf::json::Index<gltf::json::Mesh>>,
92 #[cfg(not(feature = "gltf_index"))]
93 pub mesh: Option<i64>,
94
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub weight: Option<f64>,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
101#[serde(rename_all = "camelCase")]
102pub struct VRMBlendShapeMaterialBind {
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub material_name: Option<String>,
105
106 #[serde(skip_serializing_if = "Option::is_none")]
107 pub property_name: Option<String>,
108
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub target_value: Option<Vec<f64>>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
114#[serde(rename_all = "camelCase")]
115pub struct VRMFirstPerson {
116 #[serde(
119 default,
120 skip_serializing_if = "Option::is_none",
121 deserialize_with = "deserialize_option_index::<_, gltf::json::Node>"
122 )]
123 #[cfg(feature = "gltf_index")]
124 pub first_person_bone: Option<gltf::json::Index<gltf::json::Node>>,
125 #[cfg(not(feature = "gltf_index"))]
126 pub first_person_bone: Option<i64>,
127
128 #[serde(skip_serializing_if = "Option::is_none")]
131 pub first_person_bone_offset: Option<FirstPersonBoneOffset>,
132
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub look_at_horizontal_inner: Option<VRMFirstPersonDegreeMap>,
135
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub look_at_horizontal_outer: Option<VRMFirstPersonDegreeMap>,
138
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub look_at_type_name: Option<LookAtTypeName>,
142
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub look_at_vertical_down: Option<VRMFirstPersonDegreeMap>,
145
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub look_at_vertical_up: Option<VRMFirstPersonDegreeMap>,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub mesh_annotations: Option<Vec<VRMFirstPersonMeshAnnotation>>,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct OptionalVector3 {
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub x: Option<f64>,
161
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub y: Option<f64>,
164
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub z: Option<f64>,
167}
168
169pub type FirstPersonBoneOffset = OptionalVector3;
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175#[serde(rename_all = "camelCase")]
176pub struct VRMFirstPersonDegreeMap {
177 #[serde(skip_serializing_if = "Option::is_none")]
179 pub curve: Option<Vec<f64>>,
180
181 #[serde(skip_serializing_if = "Option::is_none")]
183 pub x_range: Option<f64>,
184
185 #[serde(skip_serializing_if = "Option::is_none")]
187 pub y_range: Option<f64>,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
191#[serde(rename_all = "camelCase")]
192pub struct VRMFirstPersonMeshAnnotation {
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub first_person_flag: Option<String>,
195
196 #[serde(
197 default,
198 skip_serializing_if = "Option::is_none",
199 deserialize_with = "deserialize_option_index::<_, gltf::json::Mesh>"
200 )]
201 #[cfg(feature = "gltf_index")]
202 pub mesh: Option<gltf::json::Index<gltf::json::Mesh>>,
203 #[cfg(not(feature = "gltf_index"))]
204 pub mesh: Option<i64>,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
208#[serde(rename_all = "camelCase")]
209pub struct VRMHumanoid {
210 #[serde(skip_serializing_if = "Option::is_none")]
212 pub arm_stretch: Option<f64>,
213
214 #[serde(skip_serializing_if = "Option::is_none")]
216 pub feet_spacing: Option<f64>,
217
218 #[serde(skip_serializing_if = "Option::is_none")]
220 pub has_translation_do_f: Option<bool>,
221
222 #[serde(skip_serializing_if = "Option::is_none")]
223 pub human_bones: Option<Vec<VRMHumanoidBone>>,
224
225 #[serde(skip_serializing_if = "Option::is_none")]
227 pub leg_stretch: Option<f64>,
228
229 #[serde(skip_serializing_if = "Option::is_none")]
231 pub lower_arm_twist: Option<f64>,
232
233 #[serde(skip_serializing_if = "Option::is_none")]
235 pub lower_leg_twist: Option<f64>,
236
237 #[serde(skip_serializing_if = "Option::is_none")]
239 pub upper_arm_twist: Option<f64>,
240
241 #[serde(skip_serializing_if = "Option::is_none")]
243 pub upper_leg_twist: Option<f64>,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
247#[serde(rename_all = "camelCase")]
248pub struct VRMHumanoidBone {
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub axis_length: Option<f64>,
252
253 #[serde(skip_serializing_if = "Option::is_none")]
255 pub bone: Option<Bone>,
256
257 #[serde(skip_serializing_if = "Option::is_none")]
259 pub center: Option<Center>,
260
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub max: Option<Max>,
264
265 #[serde(skip_serializing_if = "Option::is_none")]
267 pub min: Option<Min>,
268
269 #[serde(
271 default,
272 skip_serializing_if = "Option::is_none",
273 deserialize_with = "deserialize_option_index::<_, gltf::json::Node>"
274 )]
275 #[cfg(feature = "gltf_index")]
276 pub node: Option<gltf::json::Index<gltf::json::Node>>,
277 #[cfg(not(feature = "gltf_index"))]
278 pub node: Option<i64>,
279
280 #[serde(skip_serializing_if = "Option::is_none")]
282 pub use_default_values: Option<bool>,
283}
284
285pub type Center = OptionalVector3;
287
288pub type Max = OptionalVector3;
290
291pub type Min = OptionalVector3;
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
295#[serde(rename_all = "camelCase")]
296pub struct VRMMaterial {
297 #[serde(
298 default,
299 skip_serializing_if = "Option::is_none",
300 deserialize_with = "deserialize_option_map_and_skip_nullable::<_, String, f64>"
301 )]
302 pub float_properties: Option<HashMap<String, f64>>,
303
304 #[serde(skip_serializing_if = "Option::is_none")]
305 pub keyword_map: Option<HashMap<String, bool>>,
306
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub name: Option<String>,
309
310 #[serde(skip_serializing_if = "Option::is_none")]
311 pub render_queue: Option<i64>,
312
313 #[serde(skip_serializing_if = "Option::is_none")]
318 pub shader: Option<String>,
319
320 #[serde(skip_serializing_if = "Option::is_none")]
321 pub tag_map: Option<HashMap<String, String>>,
322
323 #[serde(
324 default,
325 skip_serializing_if = "Option::is_none",
326 deserialize_with = "deserialize_option_map_index::<_, gltf::json::Texture>"
327 )]
328 #[cfg(feature = "gltf_index")]
329 pub texture_properties: Option<HashMap<String, gltf::json::Index<gltf::json::Texture>>>,
330 #[cfg(not(feature = "gltf_index"))]
331 pub texture_properties: Option<HashMap<String, usize>>,
332
333 #[serde(skip_serializing_if = "Option::is_none")]
334 pub vector_properties: Option<HashMap<String, Vec<f64>>>,
335}
336
337#[derive(Debug, Clone, Serialize, Deserialize)]
338#[serde(rename_all = "camelCase")]
339pub struct VRMMeta {
340 #[serde(skip_serializing_if = "Option::is_none")]
342 pub allowed_user_name: Option<AllowedUserName>,
343
344 #[serde(skip_serializing_if = "Option::is_none")]
346 pub author: Option<String>,
347
348 #[serde(skip_serializing_if = "Option::is_none")]
350 pub commercial_ussage_name: Option<UssageName>,
351
352 #[serde(skip_serializing_if = "Option::is_none")]
354 pub contact_information: Option<String>,
355
356 #[serde(skip_serializing_if = "Option::is_none")]
358 pub license_name: Option<LicenseName>,
359
360 #[serde(skip_serializing_if = "Option::is_none")]
362 pub other_license_url: Option<String>,
363
364 #[serde(skip_serializing_if = "Option::is_none")]
367 pub other_permission_url: Option<String>,
368
369 #[serde(skip_serializing_if = "Option::is_none")]
371 pub reference: Option<String>,
372
373 #[serde(skip_serializing_if = "Option::is_none")]
375 pub sexual_ussage_name: Option<UssageName>,
376
377 #[serde(
379 default,
380 skip_serializing_if = "Option::is_none",
381 deserialize_with = "deserialize_option_index::<_, gltf::json::Texture>"
382 )]
383 #[cfg(feature = "gltf_index")]
384 pub texture: Option<gltf::json::Index<gltf::json::Texture>>,
385 #[cfg(not(feature = "gltf_index"))]
386 pub texture: Option<i64>,
387
388 #[serde(skip_serializing_if = "Option::is_none")]
390 pub title: Option<String>,
391
392 #[serde(skip_serializing_if = "Option::is_none")]
394 pub version: Option<String>,
395
396 #[serde(skip_serializing_if = "Option::is_none")]
398 pub violent_ussage_name: Option<UssageName>,
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize)]
403#[serde(rename_all = "camelCase")]
404pub struct VRMSecondaryAnimation {
405 #[serde(skip_serializing_if = "Option::is_none")]
406 pub bone_groups: Option<Vec<VRMSecondaryAnimationSpring>>,
407
408 #[serde(skip_serializing_if = "Option::is_none")]
409 pub collider_groups: Option<Vec<VRMSecondaryAnimationColliderGroup>>,
410}
411
412#[derive(Debug, Clone, Serialize, Deserialize)]
413#[serde(rename_all = "camelCase")]
414pub struct VRMSecondaryAnimationSpring {
415 #[serde(skip_serializing_if = "Option::is_none")]
417 #[cfg(feature = "gltf_index")]
418 pub bones: Option<Vec<gltf::json::Index<gltf::json::Node>>>,
419 #[cfg(not(feature = "gltf_index"))]
420 pub bones: Option<Vec<i64>>,
421
422 #[serde(
426 default,
427 skip_serializing_if = "Option::is_none",
428 deserialize_with = "deserialize_option_index::<_, gltf::json::Node>"
429 )]
430 #[cfg(feature = "gltf_index")]
431 pub center: Option<gltf::json::Index<gltf::json::Node>>,
432 #[cfg(not(feature = "gltf_index"))]
433 pub center: Option<i64>,
434
435 #[serde(skip_serializing_if = "Option::is_none")]
437 pub collider_groups: Option<Vec<i64>>,
438
439 #[serde(skip_serializing_if = "Option::is_none")]
441 pub comment: Option<String>,
442
443 #[serde(skip_serializing_if = "Option::is_none")]
445 pub drag_force: Option<f64>,
446
447 #[serde(skip_serializing_if = "Option::is_none")]
450 pub gravity_dir: Option<GravityDir>,
451
452 #[serde(skip_serializing_if = "Option::is_none")]
454 pub gravity_power: Option<f64>,
455
456 #[serde(skip_serializing_if = "Option::is_none")]
458 pub hit_radius: Option<f64>,
459
460 #[serde(skip_serializing_if = "Option::is_none")]
462 pub stiffiness: Option<f64>,
463}
464
465pub type GravityDir = OptionalVector3;
468
469#[derive(Debug, Clone, Serialize, Deserialize)]
470pub struct VRMSecondaryAnimationColliderGroup {
471 #[serde(skip_serializing_if = "Option::is_none")]
472 pub colliders: Option<Vec<Collider>>,
473
474 #[serde(
476 default,
477 skip_serializing_if = "Option::is_none",
478 deserialize_with = "deserialize_option_index::<_, gltf::json::Node>"
479 )]
480 pub node: Option<gltf::json::Index<gltf::json::Node>>,
481 #[cfg(not(feature = "gltf_index"))]
482 pub node: Option<i64>,
483}
484
485#[derive(Debug, Clone, Serialize, Deserialize)]
486pub struct Collider {
487 #[serde(skip_serializing_if = "Option::is_none")]
489 pub offset: Option<Offset>,
490
491 #[serde(skip_serializing_if = "Option::is_none")]
493 pub radius: Option<f64>,
494}
495
496pub type Offset = OptionalVector3;
498
499#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
501#[serde(rename_all = "snake_case")]
502pub enum PresetName {
503 A,
504 Angry,
505 Blink,
506 BlinkL,
507 BlinkR,
508 E,
509 Fun,
510 I,
511 Joy,
512 Lookdown,
513 Lookleft,
514 Lookright,
515 Lookup,
516 Neutral,
517 O,
518 Sorrow,
519 U,
520 Unknown,
521}
522
523#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
525pub enum LookAtTypeName {
526 BlendShape,
527 Bone,
528}
529
530#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
532#[serde(rename_all = "camelCase")]
533pub enum Bone {
534 Chest,
535 Head,
536 Hips,
537 Jaw,
538 LeftEye,
539 LeftFoot,
540 LeftHand,
541 LeftIndexDistal,
542 LeftIndexIntermediate,
543 LeftIndexProximal,
544 LeftLittleDistal,
545 LeftLittleIntermediate,
546 LeftLittleProximal,
547 LeftLowerArm,
548 LeftLowerLeg,
549 LeftMiddleDistal,
550 LeftMiddleIntermediate,
551 LeftMiddleProximal,
552 LeftRingDistal,
553 LeftRingIntermediate,
554 LeftRingProximal,
555 LeftShoulder,
556 LeftThumbDistal,
557 LeftThumbIntermediate,
558 LeftThumbProximal,
559 LeftToes,
560 LeftUpperArm,
561 LeftUpperLeg,
562 Neck,
563 RightEye,
564 RightFoot,
565 RightHand,
566 RightIndexDistal,
567 RightIndexIntermediate,
568 RightIndexProximal,
569 RightLittleDistal,
570 RightLittleIntermediate,
571 RightLittleProximal,
572 RightLowerArm,
573 RightLowerLeg,
574 RightMiddleDistal,
575 RightMiddleIntermediate,
576 RightMiddleProximal,
577 RightRingDistal,
578 RightRingIntermediate,
579 RightRingProximal,
580 RightShoulder,
581 RightThumbDistal,
582 RightThumbIntermediate,
583 RightThumbProximal,
584 RightToes,
585 RightUpperArm,
586 RightUpperLeg,
587 Spine,
588 UpperChest,
589}
590
591#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
593pub enum AllowedUserName {
594 Everyone,
595 ExplicitlyLicensedPerson,
596 OnlyAuthor,
597}
598
599#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
603pub enum UssageName {
604 Allow,
605 Disallow,
606}
607
608#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
610pub enum LicenseName {
611 #[serde(rename = "CC0")]
612 Cc0,
613
614 #[serde(rename = "CC_BY")]
615 CcBy,
616
617 #[serde(rename = "CC_BY_NC")]
618 CcByNc,
619
620 #[serde(rename = "CC_BY_NC_ND")]
621 CcByNcNd,
622
623 #[serde(rename = "CC_BY_NC_SA")]
624 CcByNcSa,
625
626 #[serde(rename = "CC_BY_ND")]
627 CcByNd,
628
629 #[serde(rename = "CC_BY_SA")]
630 CcBySa,
631
632 #[serde(rename = "Redistribution_Prohibited")]
633 RedistributionProhibited,
634
635 Other,
636}