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 skip_serializing_if = "Option::is_none",
299 deserialize_with = "deserialize_option_map_and_skip_nullable::<_, String, f64>"
300 )]
301 pub float_properties: Option<HashMap<String, f64>>,
302
303 #[serde(skip_serializing_if = "Option::is_none")]
304 pub keyword_map: Option<HashMap<String, bool>>,
305
306 #[serde(skip_serializing_if = "Option::is_none")]
307 pub name: Option<String>,
308
309 #[serde(skip_serializing_if = "Option::is_none")]
310 pub render_queue: Option<i64>,
311
312 #[serde(skip_serializing_if = "Option::is_none")]
317 pub shader: Option<String>,
318
319 #[serde(skip_serializing_if = "Option::is_none")]
320 pub tag_map: Option<HashMap<String, String>>,
321
322 #[serde(
323 default,
324 skip_serializing_if = "Option::is_none",
325 deserialize_with = "deserialize_option_map_index::<_, gltf::json::Texture>"
326 )]
327 #[cfg(feature = "gltf_index")]
328 pub texture_properties: Option<HashMap<String, gltf::json::Index<gltf::json::Texture>>>,
329 #[cfg(not(feature = "gltf_index"))]
330 pub texture_properties: Option<HashMap<String, usize>>,
331
332 #[serde(skip_serializing_if = "Option::is_none")]
333 pub vector_properties: Option<HashMap<String, Vec<f64>>>,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize)]
337#[serde(rename_all = "camelCase")]
338pub struct VRMMeta {
339 #[serde(skip_serializing_if = "Option::is_none")]
341 pub allowed_user_name: Option<AllowedUserName>,
342
343 #[serde(skip_serializing_if = "Option::is_none")]
345 pub author: Option<String>,
346
347 #[serde(skip_serializing_if = "Option::is_none")]
349 pub commercial_ussage_name: Option<UssageName>,
350
351 #[serde(skip_serializing_if = "Option::is_none")]
353 pub contact_information: Option<String>,
354
355 #[serde(skip_serializing_if = "Option::is_none")]
357 pub license_name: Option<LicenseName>,
358
359 #[serde(skip_serializing_if = "Option::is_none")]
361 pub other_license_url: Option<String>,
362
363 #[serde(skip_serializing_if = "Option::is_none")]
366 pub other_permission_url: Option<String>,
367
368 #[serde(skip_serializing_if = "Option::is_none")]
370 pub reference: Option<String>,
371
372 #[serde(skip_serializing_if = "Option::is_none")]
374 pub sexual_ussage_name: Option<UssageName>,
375
376 #[serde(
378 default,
379 skip_serializing_if = "Option::is_none",
380 deserialize_with = "deserialize_option_index::<_, gltf::json::Texture>"
381 )]
382 #[cfg(feature = "gltf_index")]
383 pub texture: Option<gltf::json::Index<gltf::json::Texture>>,
384 #[cfg(not(feature = "gltf_index"))]
385 pub texture: Option<i64>,
386
387 #[serde(skip_serializing_if = "Option::is_none")]
389 pub title: Option<String>,
390
391 #[serde(skip_serializing_if = "Option::is_none")]
393 pub version: Option<String>,
394
395 #[serde(skip_serializing_if = "Option::is_none")]
397 pub violent_ussage_name: Option<UssageName>,
398}
399
400#[derive(Debug, Clone, Serialize, Deserialize)]
402#[serde(rename_all = "camelCase")]
403pub struct VRMSecondaryAnimation {
404 #[serde(skip_serializing_if = "Option::is_none")]
405 pub bone_groups: Option<Vec<VRMSecondaryAnimationSpring>>,
406
407 #[serde(skip_serializing_if = "Option::is_none")]
408 pub collider_groups: Option<Vec<VRMSecondaryAnimationColliderGroup>>,
409}
410
411#[derive(Debug, Clone, Serialize, Deserialize)]
412#[serde(rename_all = "camelCase")]
413pub struct VRMSecondaryAnimationSpring {
414 #[serde(skip_serializing_if = "Option::is_none")]
416 #[cfg(feature = "gltf_index")]
417 pub bones: Option<Vec<gltf::json::Index<gltf::json::Node>>>,
418 #[cfg(not(feature = "gltf_index"))]
419 pub bones: Option<Vec<i64>>,
420
421 #[serde(
425 default,
426 skip_serializing_if = "Option::is_none",
427 deserialize_with = "deserialize_option_index::<_, gltf::json::Node>"
428 )]
429 #[cfg(feature = "gltf_index")]
430 pub center: Option<gltf::json::Index<gltf::json::Node>>,
431 #[cfg(not(feature = "gltf_index"))]
432 pub center: Option<i64>,
433
434 #[serde(skip_serializing_if = "Option::is_none")]
436 pub collider_groups: Option<Vec<i64>>,
437
438 #[serde(skip_serializing_if = "Option::is_none")]
440 pub comment: Option<String>,
441
442 #[serde(skip_serializing_if = "Option::is_none")]
444 pub drag_force: Option<f64>,
445
446 #[serde(skip_serializing_if = "Option::is_none")]
449 pub gravity_dir: Option<GravityDir>,
450
451 #[serde(skip_serializing_if = "Option::is_none")]
453 pub gravity_power: Option<f64>,
454
455 #[serde(skip_serializing_if = "Option::is_none")]
457 pub hit_radius: Option<f64>,
458
459 #[serde(skip_serializing_if = "Option::is_none")]
461 pub stiffiness: Option<f64>,
462}
463
464pub type GravityDir = OptionalVector3;
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
469pub struct VRMSecondaryAnimationColliderGroup {
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub colliders: Option<Vec<Collider>>,
472
473 #[serde(
475 default,
476 skip_serializing_if = "Option::is_none",
477 deserialize_with = "deserialize_option_index::<_, gltf::json::Node>"
478 )]
479 pub node: Option<gltf::json::Index<gltf::json::Node>>,
480 #[cfg(not(feature = "gltf_index"))]
481 pub node: Option<i64>,
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize)]
485pub struct Collider {
486 #[serde(skip_serializing_if = "Option::is_none")]
488 pub offset: Option<Offset>,
489
490 #[serde(skip_serializing_if = "Option::is_none")]
492 pub radius: Option<f64>,
493}
494
495pub type Offset = OptionalVector3;
497
498#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
500#[serde(rename_all = "snake_case")]
501pub enum PresetName {
502 A,
503 Angry,
504 Blink,
505 BlinkL,
506 BlinkR,
507 E,
508 Fun,
509 I,
510 Joy,
511 Lookdown,
512 Lookleft,
513 Lookright,
514 Lookup,
515 Neutral,
516 O,
517 Sorrow,
518 U,
519 Unknown,
520}
521
522#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
524pub enum LookAtTypeName {
525 BlendShape,
526 Bone,
527}
528
529#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
531#[serde(rename_all = "camelCase")]
532pub enum Bone {
533 Chest,
534 Head,
535 Hips,
536 Jaw,
537 LeftEye,
538 LeftFoot,
539 LeftHand,
540 LeftIndexDistal,
541 LeftIndexIntermediate,
542 LeftIndexProximal,
543 LeftLittleDistal,
544 LeftLittleIntermediate,
545 LeftLittleProximal,
546 LeftLowerArm,
547 LeftLowerLeg,
548 LeftMiddleDistal,
549 LeftMiddleIntermediate,
550 LeftMiddleProximal,
551 LeftRingDistal,
552 LeftRingIntermediate,
553 LeftRingProximal,
554 LeftShoulder,
555 LeftThumbDistal,
556 LeftThumbIntermediate,
557 LeftThumbProximal,
558 LeftToes,
559 LeftUpperArm,
560 LeftUpperLeg,
561 Neck,
562 RightEye,
563 RightFoot,
564 RightHand,
565 RightIndexDistal,
566 RightIndexIntermediate,
567 RightIndexProximal,
568 RightLittleDistal,
569 RightLittleIntermediate,
570 RightLittleProximal,
571 RightLowerArm,
572 RightLowerLeg,
573 RightMiddleDistal,
574 RightMiddleIntermediate,
575 RightMiddleProximal,
576 RightRingDistal,
577 RightRingIntermediate,
578 RightRingProximal,
579 RightShoulder,
580 RightThumbDistal,
581 RightThumbIntermediate,
582 RightThumbProximal,
583 RightToes,
584 RightUpperArm,
585 RightUpperLeg,
586 Spine,
587 UpperChest,
588}
589
590#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
592pub enum AllowedUserName {
593 Everyone,
594 ExplicitlyLicensedPerson,
595 OnlyAuthor,
596}
597
598#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
602pub enum UssageName {
603 Allow,
604 Disallow,
605}
606
607#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
609pub enum LicenseName {
610 #[serde(rename = "CC0")]
611 Cc0,
612
613 #[serde(rename = "CC_BY")]
614 CcBy,
615
616 #[serde(rename = "CC_BY_NC")]
617 CcByNc,
618
619 #[serde(rename = "CC_BY_NC_ND")]
620 CcByNcNd,
621
622 #[serde(rename = "CC_BY_NC_SA")]
623 CcByNcSa,
624
625 #[serde(rename = "CC_BY_ND")]
626 CcByNd,
627
628 #[serde(rename = "CC_BY_SA")]
629 CcBySa,
630
631 #[serde(rename = "Redistribution_Prohibited")]
632 RedistributionProhibited,
633
634 Other,
635}