1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use super::either::either;
6use super::{is_false, is_zero3};
7use crate::Id;
8
9#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
10#[serde(deny_unknown_fields)]
11pub struct Mesh {
12 #[serde(default, skip_serializing_if = "Option::is_none")]
13 pub id: Option<Id>,
14 pub file: String,
15 #[serde(default, skip_serializing_if = "Option::is_none")]
16 pub node: Option<String>,
17 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
18 pub nodes: BTreeMap<String, Node>,
19 #[serde(default, skip_serializing_if = "Option::is_none")]
20 pub authoring: Option<toml::Value>,
21}
22
23#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
24#[serde(deny_unknown_fields)]
25pub struct Node {
26 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub material: Option<String>,
28 #[serde(default, skip_serializing_if = "is_false")]
29 pub hidden: bool,
30 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub shadow: Option<Shadow>,
32 #[serde(default, skip_serializing_if = "Option::is_none")]
33 pub two_sided: Option<bool>,
34}
35
36#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "snake_case")]
38pub enum Shadow {
39 #[default]
40 Cast,
41 Only,
42 None,
43}
44
45#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
46#[serde(untagged)]
47pub enum Scale {
48 Uniform(f32),
49 Axes([f32; 3]),
50}
51
52either!(Scale, "a number or three numbers", number => Uniform, seq => Axes([f32; 3]),);
53
54impl Scale {
55 pub fn axes(self) -> [f32; 3] {
56 match self {
57 Self::Uniform(value) => [value; 3],
58 Self::Axes(axes) => axes,
59 }
60 }
61}
62
63#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
64#[serde(deny_unknown_fields)]
65pub struct Object {
66 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub id: Option<Id>,
68 pub name: String,
69 #[serde(default, skip_serializing_if = "Option::is_none")]
70 pub mesh: Option<String>,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
72 pub prefab: Option<String>,
73 #[serde(default, skip_serializing_if = "is_zero3")]
74 pub at: [f32; 3],
75 #[serde(default, skip_serializing_if = "is_zero3")]
76 pub rotate: [f32; 3],
77 #[serde(default, skip_serializing_if = "Option::is_none")]
78 pub scale: Option<Scale>,
79 #[serde(default, skip_serializing_if = "Option::is_none")]
80 pub parent: Option<String>,
81 #[serde(default, skip_serializing_if = "Option::is_none")]
82 pub material: Option<String>,
83 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
84 pub materials: BTreeMap<String, String>,
85 #[serde(default, skip_serializing_if = "Option::is_none")]
86 pub shadow: Option<Shadow>,
87 #[serde(default, skip_serializing_if = "is_false")]
88 pub two_sided: bool,
89 #[serde(default, skip_serializing_if = "is_false")]
90 pub hidden: bool,
91 #[serde(default, skip_serializing_if = "Vec::is_empty")]
92 pub clip: Vec<[f32; 4]>,
93 #[serde(default, skip_serializing_if = "Option::is_none")]
94 pub content: Option<String>,
95 #[serde(default, skip_serializing_if = "Option::is_none")]
96 pub pick: Option<u32>,
97 #[serde(default, skip_serializing_if = "Option::is_none")]
98 pub alpha_cutoff: Option<f32>,
99 #[serde(default, skip_serializing_if = "is_false")]
100 pub face_camera: bool,
101 #[serde(default, skip_serializing_if = "Option::is_none")]
102 pub dynamic: Option<bool>,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
104 pub body: Option<Body>,
105 #[serde(default, skip_serializing_if = "Option::is_none")]
106 pub character: Option<Character>,
107 #[serde(default, skip_serializing_if = "Option::is_none")]
108 pub trigger: Option<Trigger>,
109 #[serde(default, skip_serializing_if = "Option::is_none")]
110 pub animation: Option<Animation>,
111 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
112 pub set: BTreeMap<String, toml::Value>,
113 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub authoring: Option<toml::Value>,
115}
116
117impl Object {
118 pub const ALPHA_CUTOFF: f32 = 0.5;
119
120 pub fn is_placement(&self) -> bool {
121 self.prefab.is_some()
122 }
123
124 pub fn scale_axes(&self) -> [f32; 3] {
125 self.scale.map_or([1.0; 3], Scale::axes)
126 }
127}
128
129#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
130#[serde(deny_unknown_fields)]
131pub struct Light {
132 #[serde(default, skip_serializing_if = "Option::is_none")]
133 pub id: Option<Id>,
134 pub name: String,
135 pub position: [f32; 3],
136 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub color: Option<[f32; 3]>,
138 pub intensity: f32,
139 #[serde(default, skip_serializing_if = "Option::is_none")]
140 pub radius: Option<f32>,
141 #[serde(default, skip_serializing_if = "Option::is_none")]
142 pub range: Option<f32>,
143 #[serde(default, skip_serializing_if = "Option::is_none")]
144 pub shadow: Option<bool>,
145 #[serde(default, skip_serializing_if = "Option::is_none")]
146 pub authoring: Option<toml::Value>,
147}
148
149impl Light {
150 pub const COLOR: [f32; 3] = [1.0; 3];
151 pub const RADIUS: f32 = 0.0;
152 pub const RANGE: f32 = 10.0;
153 pub const SHADOW: bool = true;
154}
155
156#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
157#[serde(deny_unknown_fields)]
158pub struct Emitter {
159 #[serde(default, skip_serializing_if = "Option::is_none")]
160 pub id: Option<Id>,
161 pub name: String,
162 pub position: [f32; 3],
163 pub radius: f32,
164 #[serde(default, skip_serializing_if = "Option::is_none")]
165 pub color: Option<[f32; 3]>,
166 pub intensity: f32,
167 #[serde(default, skip_serializing_if = "Option::is_none")]
168 pub authoring: Option<toml::Value>,
169}
170
171#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
172#[serde(rename_all = "snake_case")]
173pub enum MoverKind {
174 Turn,
175 Slide,
176}
177
178#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
179#[serde(rename_all = "snake_case")]
180pub enum Motion {
181 #[default]
182 Swing,
183 Loop,
184 Once,
185}
186
187#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
188#[serde(deny_unknown_fields)]
189pub struct Mover {
190 #[serde(default, skip_serializing_if = "Option::is_none")]
191 pub id: Option<Id>,
192 pub name: String,
193 pub objects: Vec<String>,
194 pub kind: MoverKind,
195 #[serde(default, skip_serializing_if = "Option::is_none")]
196 pub pivot: Option<[f32; 3]>,
197 pub axis: [f32; 3],
198 pub travel: [f32; 2],
199 #[serde(default, skip_serializing_if = "Option::is_none")]
200 pub period: Option<f32>,
201 #[serde(default, skip_serializing_if = "Option::is_none")]
202 pub motion: Option<Motion>,
203 #[serde(default, skip_serializing_if = "Option::is_none")]
204 pub offset: Option<f32>,
205 #[serde(default, skip_serializing_if = "Vec::is_empty")]
206 pub clip: Vec<[f32; 4]>,
207 #[serde(default, skip_serializing_if = "Option::is_none")]
208 pub authoring: Option<toml::Value>,
209}
210
211impl Mover {
212 pub const PERIOD: f32 = 4.0;
213}
214
215#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
216#[serde(deny_unknown_fields)]
217pub struct Content {
218 #[serde(default, skip_serializing_if = "Option::is_none")]
219 pub id: Option<Id>,
220 pub image: String,
221 #[serde(default, skip_serializing_if = "Option::is_none")]
222 pub authoring: Option<toml::Value>,
223}
224
225#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
226#[serde(deny_unknown_fields)]
227pub struct Text {
228 #[serde(default, skip_serializing_if = "Option::is_none")]
229 pub id: Option<Id>,
230 pub text: String,
231 pub font: String,
232 pub size: f32,
233 #[serde(default, skip_serializing_if = "Option::is_none")]
234 pub color: Option<[f32; 4]>,
235 #[serde(default, skip_serializing_if = "is_zero3")]
236 pub at: [f32; 3],
237 #[serde(default, skip_serializing_if = "is_zero3")]
238 pub rotate: [f32; 3],
239 #[serde(default, skip_serializing_if = "is_false")]
240 pub lit: bool,
241 #[serde(default, skip_serializing_if = "is_false")]
242 pub dynamic: bool,
243 #[serde(default, skip_serializing_if = "Option::is_none")]
244 pub authoring: Option<toml::Value>,
245}
246
247impl Text {
248 pub const COLOR: [f32; 4] = [1.0; 4];
249}
250
251#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
252#[serde(rename_all = "snake_case")]
253pub enum Cue {
254 #[default]
255 Start,
256 Hit,
257 Game,
258}
259
260#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
261#[serde(deny_unknown_fields)]
262pub struct Sound {
263 #[serde(default, skip_serializing_if = "Option::is_none")]
264 pub id: Option<Id>,
265 pub file: String,
266 #[serde(default, skip_serializing_if = "Option::is_none")]
267 pub volume: Option<f32>,
268 #[serde(default, skip_serializing_if = "Option::is_none")]
269 pub pan: Option<f32>,
270 #[serde(default, rename = "loop", skip_serializing_if = "is_false")]
271 pub looping: bool,
272 #[serde(default, skip_serializing_if = "Option::is_none")]
273 pub play: Option<Cue>,
274 #[serde(default, skip_serializing_if = "Option::is_none")]
275 pub object: Option<String>,
276 #[serde(default, skip_serializing_if = "Option::is_none")]
277 pub authoring: Option<toml::Value>,
278}
279
280#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
281#[serde(rename_all = "snake_case")]
282pub enum SunModel {
283 Daylight,
284 Authored,
285}
286
287#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
288#[serde(deny_unknown_fields)]
289pub struct Sun {
290 pub model: SunModel,
291 #[serde(default, skip_serializing_if = "Option::is_none")]
292 pub hour: Option<f64>,
293 #[serde(default, skip_serializing_if = "Option::is_none")]
294 pub day: Option<f64>,
295 #[serde(default, skip_serializing_if = "Option::is_none")]
296 pub latitude: Option<f64>,
297 #[serde(default, skip_serializing_if = "Option::is_none")]
298 pub heading: Option<f64>,
299 #[serde(default, skip_serializing_if = "Option::is_none")]
300 pub reference_hour: Option<f64>,
301 #[serde(default, skip_serializing_if = "Option::is_none")]
302 pub toward: Option<[f32; 3]>,
303 #[serde(default, skip_serializing_if = "Option::is_none")]
304 pub color: Option<[f32; 3]>,
305 #[serde(default, skip_serializing_if = "Option::is_none")]
306 pub irradiance: Option<f32>,
307 #[serde(default, skip_serializing_if = "Option::is_none")]
308 pub radius: Option<f32>,
309}
310
311impl Sun {
312 pub const REFERENCE_HOUR: f64 = 12.0;
313 pub const DAY: f64 = 172.0;
314 pub const LATITUDE: f64 = 45.0;
315 pub const HEADING: f64 = 180.0;
316}
317
318#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
319#[serde(deny_unknown_fields)]
320pub struct Haze {
321 pub lo: [f32; 3],
322 pub hi: [f32; 3],
323 #[serde(default, skip_serializing_if = "Option::is_none")]
324 pub amount: Option<f32>,
325 #[serde(default, skip_serializing_if = "Option::is_none")]
326 pub fog: Option<f32>,
327 #[serde(default, skip_serializing_if = "Option::is_none")]
328 pub smoke: Option<f32>,
329 #[serde(default, skip_serializing_if = "Option::is_none")]
330 pub mist: Option<f32>,
331 #[serde(default, skip_serializing_if = "Option::is_none")]
332 pub floor: Option<f32>,
333 #[serde(default, skip_serializing_if = "Option::is_none")]
334 pub phase: Option<f32>,
335 #[serde(default, skip_serializing_if = "Option::is_none")]
336 pub back: Option<f32>,
337 #[serde(default, skip_serializing_if = "Option::is_none")]
338 pub reach: Option<f32>,
339 #[serde(default, skip_serializing_if = "Option::is_none")]
340 pub gold: Option<[f32; 3]>,
341 #[serde(default, skip_serializing_if = "Option::is_none")]
342 pub ambient: Option<[f32; 3]>,
343 #[serde(default, skip_serializing_if = "Option::is_none")]
344 pub unmapped: Option<f32>,
345 #[serde(default, skip_serializing_if = "Option::is_none")]
346 pub seed: Option<u32>,
347}
348
349#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
350#[serde(rename_all = "snake_case")]
351pub enum Projection {
352 #[default]
353 Perspective,
354 Orthographic,
355}
356
357#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
358#[serde(deny_unknown_fields)]
359pub struct Camera {
360 #[serde(default, skip_serializing_if = "Option::is_none")]
361 pub projection: Option<Projection>,
362 #[serde(default, skip_serializing_if = "Option::is_none")]
363 pub at: Option<[f32; 3]>,
364 #[serde(default, skip_serializing_if = "Option::is_none")]
365 pub look_at: Option<[f32; 3]>,
366 #[serde(default, skip_serializing_if = "Option::is_none")]
367 pub up: Option<[f32; 3]>,
368 #[serde(default, skip_serializing_if = "Option::is_none")]
369 pub fov: Option<f32>,
370 #[serde(default, skip_serializing_if = "Option::is_none")]
371 pub focal: Option<f32>,
372 #[serde(default, skip_serializing_if = "Option::is_none")]
373 pub sensor: Option<f32>,
374 #[serde(default, skip_serializing_if = "Option::is_none")]
375 pub height: Option<f32>,
376 #[serde(default, skip_serializing_if = "Option::is_none")]
377 pub near: Option<f32>,
378 #[serde(default, skip_serializing_if = "Option::is_none")]
379 pub far: Option<f32>,
380 #[serde(default, skip_serializing_if = "Option::is_none")]
381 pub shift: Option<[f32; 2]>,
382 #[serde(default, skip_serializing_if = "Option::is_none")]
383 pub fstop: Option<f32>,
384 #[serde(default, skip_serializing_if = "Option::is_none")]
385 pub focus: Option<f32>,
386 #[serde(default, skip_serializing_if = "Option::is_none")]
387 pub preset: Option<Preset>,
388}
389
390impl Camera {
391 pub const AT: [f32; 3] = [0.0, 0.0, 5.0];
392 pub const LOOK_AT: [f32; 3] = [0.0; 3];
393 pub const UP: [f32; 3] = [0.0, 1.0, 0.0];
394 pub const FOV: f32 = 40.0;
395 pub const SENSOR: f32 = 24.0;
396 pub const NEAR: f32 = 0.05;
397 pub const FAR: f32 = 100.0;
398}
399
400#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
401#[serde(deny_unknown_fields)]
402pub struct Preset {
403 #[serde(default, skip_serializing_if = "Option::is_none")]
404 pub gain: Option<f32>,
405 #[serde(default, skip_serializing_if = "Option::is_none")]
406 pub orbit: Option<Orbit>,
407 #[serde(default, skip_serializing_if = "Option::is_none")]
408 pub look: Option<Look>,
409 #[serde(default, skip_serializing_if = "Option::is_none")]
410 pub zoom: Option<Zoom>,
411 #[serde(default, skip_serializing_if = "Option::is_none")]
412 pub wander: Option<Wander>,
413 #[serde(default, skip_serializing_if = "Option::is_none")]
414 pub nudge: Option<Nudge>,
415 #[serde(default, skip_serializing_if = "Option::is_none")]
416 pub hush: Option<Hush>,
417 #[serde(default, skip_serializing_if = "Option::is_none")]
418 pub depth: Option<Depth>,
419}
420
421#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
422#[serde(deny_unknown_fields)]
423pub struct Orbit {
424 #[serde(default, skip_serializing_if = "Option::is_none")]
425 pub stiffness: Option<f32>,
426 #[serde(default, skip_serializing_if = "Option::is_none")]
427 pub yaw: Option<f32>,
428 #[serde(default, skip_serializing_if = "Option::is_none")]
429 pub pitch: Option<f32>,
430 #[serde(default, skip_serializing_if = "Option::is_none")]
431 pub hold: Option<f32>,
432}
433
434#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
435#[serde(deny_unknown_fields)]
436pub struct Look {
437 #[serde(default, skip_serializing_if = "Option::is_none")]
438 pub stiffness: Option<f32>,
439 #[serde(default, skip_serializing_if = "Option::is_none")]
440 pub lean: Option<f32>,
441 #[serde(default, skip_serializing_if = "Option::is_none")]
442 pub yaw: Option<f32>,
443 #[serde(default, skip_serializing_if = "Option::is_none")]
444 pub pitch: Option<f32>,
445 #[serde(default, skip_serializing_if = "Option::is_none")]
446 pub sign: Option<f32>,
447}
448
449#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
450#[serde(deny_unknown_fields)]
451pub struct Zoom {
452 #[serde(default, skip_serializing_if = "Option::is_none")]
453 pub stiffness: Option<f32>,
454 #[serde(default, skip_serializing_if = "Option::is_none")]
455 pub hold: Option<f32>,
456 #[serde(default, skip_serializing_if = "Option::is_none")]
457 pub max: Option<f32>,
458 #[serde(default, skip_serializing_if = "Option::is_none")]
459 pub rate: Option<f32>,
460}
461
462#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
463#[serde(deny_unknown_fields)]
464pub struct Wander {
465 #[serde(default, skip_serializing_if = "Option::is_none")]
466 pub idle: Option<f32>,
467 #[serde(default, skip_serializing_if = "Option::is_none")]
468 pub every: Option<f32>,
469 #[serde(default, skip_serializing_if = "Option::is_none")]
470 pub hold: Option<f32>,
471}
472
473#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
474#[serde(deny_unknown_fields)]
475pub struct Nudge {
476 #[serde(default, skip_serializing_if = "Option::is_none")]
477 pub stiffness: Option<f32>,
478 #[serde(default, skip_serializing_if = "Option::is_none")]
479 pub pitch: Option<f32>,
480 #[serde(default, skip_serializing_if = "Option::is_none")]
481 pub hold: Option<f32>,
482 #[serde(default, skip_serializing_if = "Option::is_none")]
483 pub floor: Option<f32>,
484}
485
486#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
487#[serde(deny_unknown_fields)]
488pub struct Hush {
489 #[serde(default, skip_serializing_if = "Option::is_none")]
490 pub floor: Option<f32>,
491 #[serde(default, skip_serializing_if = "Option::is_none")]
492 pub stiffness: Option<f32>,
493 #[serde(default, skip_serializing_if = "Option::is_none")]
494 pub blocks_cursor: Option<bool>,
495}
496
497#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
498#[serde(deny_unknown_fields)]
499pub struct Depth {
500 #[serde(default, skip_serializing_if = "Option::is_none")]
501 pub focus: Option<f32>,
502 #[serde(default, skip_serializing_if = "Option::is_none")]
503 pub blur: Option<f32>,
504 #[serde(default, skip_serializing_if = "Option::is_none")]
505 pub floor: Option<f32>,
506}
507
508#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
509#[serde(deny_unknown_fields)]
510pub struct Trace {
511 #[serde(default, skip_serializing_if = "is_false")]
512 pub transmissive_shadows: bool,
513 #[serde(default, skip_serializing_if = "Option::is_none")]
514 pub clamp_indirect: Option<f32>,
515 #[serde(default, skip_serializing_if = "Option::is_none")]
516 pub filter_glossy: Option<f32>,
517}
518
519impl Trace {
520 pub const CLAMP_INDIRECT: f32 = 0.0;
521 pub const FILTER_GLOSSY: f32 = 0.0;
522}
523
524#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
525#[serde(rename_all = "snake_case")]
526pub enum Casters {
527 #[default]
528 Meshes,
529 Proxies,
530 None,
531}
532
533#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
534#[serde(deny_unknown_fields)]
535pub struct Plates {
536 pub dir: String,
537 #[serde(default, skip_serializing_if = "Option::is_none")]
538 pub proxies: Option<String>,
539 #[serde(default, skip_serializing_if = "Option::is_none")]
540 pub casters: Option<Casters>,
541}
542
543#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
544#[serde(deny_unknown_fields)]
545pub struct Physics {
546 #[serde(default, skip_serializing_if = "Option::is_none")]
547 pub gravity: Option<[f32; 3]>,
548 #[serde(default, skip_serializing_if = "Option::is_none")]
549 pub rate: Option<f32>,
550 #[serde(default, skip_serializing_if = "Option::is_none")]
551 pub substeps: Option<u32>,
552 #[serde(default, skip_serializing_if = "Option::is_none")]
553 pub iterations: Option<u32>,
554}
555
556impl Physics {
557 pub const GRAVITY: [f32; 3] = [0.0, -9.81, 0.0];
558 pub const RATE: f32 = 60.0;
559 pub const SUBSTEPS: u32 = 1;
560 pub const ITERATIONS: u32 = 4;
561}
562
563#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
564#[serde(rename_all = "snake_case")]
565pub enum BodyKind {
566 #[default]
567 Dynamic,
568 Kinematic,
569 Fixed,
570}
571
572#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
573#[serde(rename_all = "snake_case")]
574pub enum BodyShape {
575 #[default]
576 Box,
577 Sphere,
578 Capsule,
579 Cylinder,
580}
581
582#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
583#[serde(deny_unknown_fields)]
584pub struct Body {
585 #[serde(default, skip_serializing_if = "Option::is_none")]
586 pub kind: Option<BodyKind>,
587 #[serde(default, skip_serializing_if = "Option::is_none")]
588 pub shape: Option<BodyShape>,
589 #[serde(default, skip_serializing_if = "Option::is_none")]
590 pub half: Option<[f32; 3]>,
591 #[serde(default, skip_serializing_if = "Option::is_none")]
592 pub radius: Option<f32>,
593 #[serde(default, skip_serializing_if = "Option::is_none")]
594 pub half_height: Option<f32>,
595 #[serde(default, skip_serializing_if = "Option::is_none")]
596 pub offset: Option<[f32; 3]>,
597 #[serde(default, skip_serializing_if = "Option::is_none")]
598 pub density: Option<f32>,
599 #[serde(default, skip_serializing_if = "Option::is_none")]
600 pub friction: Option<f32>,
601 #[serde(default, skip_serializing_if = "Option::is_none")]
602 pub restitution: Option<f32>,
603 #[serde(default, skip_serializing_if = "Option::is_none")]
604 pub velocity: Option<[f32; 3]>,
605 #[serde(default, skip_serializing_if = "Option::is_none")]
606 pub spin: Option<[f32; 3]>,
607 #[serde(default, skip_serializing_if = "Option::is_none")]
608 pub gravity_scale: Option<f32>,
609 #[serde(default, skip_serializing_if = "Option::is_none")]
610 pub damping: Option<[f32; 2]>,
611 #[serde(default, skip_serializing_if = "is_false")]
612 pub ccd: bool,
613 #[serde(default, skip_serializing_if = "Option::is_none")]
614 pub layer: Option<String>,
615}
616
617impl Body {
618 pub const DENSITY: f32 = 1.0;
619 pub const FRICTION: f32 = 0.5;
620 pub const RESTITUTION: f32 = 0.0;
621 pub const GRAVITY_SCALE: f32 = 1.0;
622}
623
624#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
625pub enum CharacterKind {
626 #[default]
627 #[serde(rename = "3d")]
628 ThreeD,
629 #[serde(rename = "2d")]
630 TwoD,
631}
632
633#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
634#[serde(rename_all = "snake_case")]
635pub enum Plane {
636 #[default]
637 Xy,
638 Yz,
639}
640
641#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
642#[serde(deny_unknown_fields)]
643pub struct Character {
644 #[serde(default, skip_serializing_if = "Option::is_none")]
645 pub kind: Option<CharacterKind>,
646 #[serde(default, skip_serializing_if = "Option::is_none")]
647 pub radius: Option<f32>,
648 #[serde(default, skip_serializing_if = "Option::is_none")]
649 pub height: Option<f32>,
650 #[serde(default, skip_serializing_if = "Option::is_none")]
651 pub max_climb: Option<f32>,
652 #[serde(default, skip_serializing_if = "Option::is_none")]
653 pub max_step: Option<f32>,
654 #[serde(default, skip_serializing_if = "Option::is_none")]
655 pub snap: Option<f32>,
656 #[serde(default, skip_serializing_if = "Option::is_none")]
657 pub coyote: Option<u32>,
658 #[serde(default, skip_serializing_if = "Option::is_none")]
659 pub jump_buffer: Option<u32>,
660 #[serde(default, skip_serializing_if = "Option::is_none")]
661 pub jump_speed: Option<f32>,
662 #[serde(default, skip_serializing_if = "Option::is_none")]
663 pub layer: Option<String>,
664 #[serde(default, skip_serializing_if = "Option::is_none")]
665 pub plane: Option<Plane>,
666 #[serde(default, skip_serializing_if = "Option::is_none")]
667 pub variable_jump: Option<f32>,
668 #[serde(default, skip_serializing_if = "Option::is_none")]
669 pub wall_slide: Option<f32>,
670 #[serde(default, skip_serializing_if = "Option::is_none")]
671 pub wall_jump: Option<[f32; 2]>,
672}
673
674impl Character {
675 pub const MAX_CLIMB: f32 = 45.0;
676 pub const MAX_STEP: f32 = 0.3;
677 pub const SNAP: f32 = 0.2;
678 pub const COYOTE: u32 = 6;
679 pub const JUMP_BUFFER: u32 = 6;
680 pub const JUMP_SPEED: f32 = 5.0;
681 pub const VARIABLE_JUMP: f32 = 0.0;
682 pub const TICKS: u32 = 1000;
683}
684
685#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
686#[serde(deny_unknown_fields)]
687pub struct Trigger {
688 #[serde(default, skip_serializing_if = "Option::is_none")]
689 pub shape: Option<BodyShape>,
690 #[serde(default, skip_serializing_if = "Option::is_none")]
691 pub half: Option<[f32; 3]>,
692 #[serde(default, skip_serializing_if = "Option::is_none")]
693 pub radius: Option<f32>,
694 #[serde(default, skip_serializing_if = "Option::is_none")]
695 pub half_height: Option<f32>,
696 #[serde(default, skip_serializing_if = "Option::is_none")]
697 pub offset: Option<[f32; 3]>,
698 #[serde(default, skip_serializing_if = "Option::is_none")]
699 pub layer: Option<String>,
700 #[serde(default, skip_serializing_if = "Option::is_none")]
701 pub mask: Option<Vec<String>>,
702}
703
704#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
705#[serde(rename_all = "kebab-case")]
706pub enum RigKind {
707 Follow,
708 FirstPerson,
709 ThirdPerson,
710}
711
712impl RigKind {
713 pub fn name(self) -> &'static str {
714 match self {
715 Self::Follow => "follow",
716 Self::FirstPerson => "first-person",
717 Self::ThirdPerson => "third-person",
718 }
719 }
720}
721
722#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
723#[serde(deny_unknown_fields)]
724pub struct Bounds {
725 pub min: [f32; 3],
726 pub max: [f32; 3],
727}
728
729#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
730#[serde(deny_unknown_fields)]
731pub struct Rig {
732 #[serde(default, skip_serializing_if = "Option::is_none")]
733 pub id: Option<Id>,
734 pub name: String,
735 pub kind: RigKind,
736 #[serde(default, skip_serializing_if = "Option::is_none")]
737 pub target: Option<String>,
738 #[serde(default, skip_serializing_if = "Option::is_none")]
739 pub offset: Option<[f32; 3]>,
740 #[serde(default, skip_serializing_if = "Option::is_none")]
741 pub dead_zone: Option<[f32; 2]>,
742 #[serde(default, skip_serializing_if = "Option::is_none")]
743 pub look_ahead: Option<f32>,
744 #[serde(default, skip_serializing_if = "Option::is_none")]
745 pub damping: Option<[f32; 3]>,
746 #[serde(default, skip_serializing_if = "Option::is_none")]
747 pub bounds: Option<Bounds>,
748 #[serde(default, skip_serializing_if = "Option::is_none")]
749 pub orthographic: Option<f32>,
750 #[serde(default, skip_serializing_if = "Option::is_none")]
751 pub snap: Option<f32>,
752 #[serde(default, skip_serializing_if = "Option::is_none")]
753 pub sensitivity: Option<f32>,
754 #[serde(default, skip_serializing_if = "Option::is_none")]
755 pub invert: Option<bool>,
756 #[serde(default, skip_serializing_if = "Option::is_none")]
757 pub smoothing: Option<f32>,
758 #[serde(default, skip_serializing_if = "Option::is_none")]
759 pub pitch: Option<[f32; 2]>,
760 #[serde(default, skip_serializing_if = "Option::is_none")]
761 pub head_bob: Option<f32>,
762 #[serde(default, skip_serializing_if = "Option::is_none")]
763 pub distance: Option<f32>,
764 #[serde(default, skip_serializing_if = "Option::is_none")]
765 pub collide: Option<bool>,
766 #[serde(default, skip_serializing_if = "Option::is_none")]
767 pub authoring: Option<toml::Value>,
768}
769
770impl Rig {
771 pub const DEAD_ZONE: [f32; 2] = [0.0; 2];
772 pub const LOOK_AHEAD: f32 = 0.0;
773 pub const DAMPING: [f32; 3] = [0.0; 3];
774 pub const SENSITIVITY: f32 = 1.0;
775 pub const INVERT: bool = false;
776 pub const SMOOTHING: f32 = 0.0;
777 pub const PITCH: [f32; 2] = [-85.0, 85.0];
778 pub const HEAD_BOB: f32 = 0.0;
779 pub const DISTANCE: f32 = 4.0;
780 pub const COLLIDE: bool = true;
781}
782
783#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
784#[serde(rename_all = "kebab-case")]
785pub enum Repeat {
786 #[default]
787 Loop,
788 Once,
789 PingPong,
790}
791
792#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
793#[serde(deny_unknown_fields)]
794pub struct BlendClip {
795 pub clip: String,
796 #[serde(default, skip_serializing_if = "Option::is_none")]
797 pub weight: Option<f32>,
798}
799
800impl BlendClip {
801 pub const WEIGHT: f32 = 1.0;
802}
803
804#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
805#[serde(deny_unknown_fields)]
806pub struct SpriteClip {
807 pub from: u32,
808 pub to: u32,
809 #[serde(default, skip_serializing_if = "Option::is_none")]
810 pub fps: Option<f32>,
811 #[serde(default, rename = "loop", skip_serializing_if = "Option::is_none")]
812 pub looping: Option<Repeat>,
813}
814
815impl SpriteClip {
816 pub fn count(&self) -> u32 {
817 self.to.saturating_sub(self.from).saturating_add(1)
818 }
819}
820
821#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
822#[serde(deny_unknown_fields)]
823pub struct ClipEvent {
824 pub name: String,
825 #[serde(default, skip_serializing_if = "Option::is_none")]
826 pub clip: Option<String>,
827 #[serde(default, skip_serializing_if = "Option::is_none")]
828 pub frame: Option<u32>,
829 #[serde(default, skip_serializing_if = "Option::is_none")]
830 pub time: Option<f32>,
831}
832
833#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
834#[serde(deny_unknown_fields)]
835pub struct Animation {
836 #[serde(default, skip_serializing_if = "Option::is_none")]
837 pub clip: Option<String>,
838 #[serde(default, skip_serializing_if = "Option::is_none")]
839 pub speed: Option<f32>,
840 #[serde(default, rename = "loop", skip_serializing_if = "Option::is_none")]
841 pub looping: Option<Repeat>,
842 #[serde(default, skip_serializing_if = "Option::is_none")]
843 pub start: Option<f32>,
844 #[serde(default, skip_serializing_if = "Option::is_none")]
845 pub blend: Option<Vec<BlendClip>>,
846 #[serde(default, skip_serializing_if = "Vec::is_empty")]
847 pub events: Vec<ClipEvent>,
848 #[serde(default, skip_serializing_if = "Option::is_none")]
849 pub atlas: Option<String>,
850 #[serde(default, skip_serializing_if = "Option::is_none")]
851 pub grid: Option<[u32; 2]>,
852 #[serde(default, skip_serializing_if = "Option::is_none")]
853 pub frames: Option<Vec<[u32; 4]>>,
854 #[serde(default, skip_serializing_if = "Option::is_none")]
855 pub fps: Option<f32>,
856 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
857 pub clips: BTreeMap<String, SpriteClip>,
858}
859
860impl Animation {
861 pub const SPEED: f32 = 1.0;
862 pub const START: f32 = 0.0;
863 pub const FPS: f32 = 12.0;
864
865 pub fn is_sprite(&self) -> bool {
866 self.atlas.is_some()
867 }
868
869 pub fn frame_count(&self) -> Option<u32> {
870 if let Some([columns, rows]) = self.grid {
871 return Some(columns.saturating_mul(rows));
872 }
873 self.frames
874 .as_ref()
875 .map(|frames| u32::try_from(frames.len()).unwrap_or(u32::MAX))
876 }
877}
878
879#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
880#[serde(rename_all = "snake_case")]
881pub enum TilePlane {
882 #[default]
883 Xy,
884 Xz,
885 Yz,
886}
887
888impl TilePlane {
889 pub fn axes(self) -> ([f32; 3], [f32; 3]) {
890 match self {
891 Self::Xy => ([1.0, 0.0, 0.0], [0.0, 1.0, 0.0]),
892 Self::Xz => ([1.0, 0.0, 0.0], [0.0, 0.0, -1.0]),
893 Self::Yz => ([0.0, 0.0, -1.0], [0.0, 1.0, 0.0]),
894 }
895 }
896}
897
898#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
899#[serde(untagged)]
900pub enum CellSize {
901 Square(f32),
902 Sides([f32; 2]),
903}
904
905either!(CellSize, "a number or two numbers", number => Square, seq => Sides([f32; 2]),);
906
907impl CellSize {
908 pub fn sides(self) -> [f32; 2] {
909 match self {
910 Self::Square(side) => [side; 2],
911 Self::Sides(sides) => sides,
912 }
913 }
914}
915
916#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
917#[serde(deny_unknown_fields)]
918pub struct TileCell {
919 pub at: [i32; 2],
920 pub tile: String,
921}
922
923#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
924#[serde(deny_unknown_fields)]
925pub struct Tiles {
926 #[serde(default, skip_serializing_if = "Option::is_none")]
927 pub id: Option<Id>,
928 pub name: String,
929 #[serde(default, skip_serializing_if = "Option::is_none")]
930 pub cell: Option<CellSize>,
931 #[serde(default, skip_serializing_if = "is_zero3")]
932 pub origin: [f32; 3],
933 #[serde(default, skip_serializing_if = "Option::is_none")]
934 pub plane: Option<TilePlane>,
935 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
936 pub palette: BTreeMap<String, String>,
937 #[serde(default, skip_serializing_if = "Option::is_none")]
938 pub rows: Option<Vec<String>>,
939 #[serde(default, skip_serializing_if = "Option::is_none")]
940 pub cells: Option<Vec<TileCell>>,
941 #[serde(default, skip_serializing_if = "Option::is_none")]
942 pub authoring: Option<toml::Value>,
943}
944
945impl Tiles {
946 pub const CELL: [f32; 2] = [1.0; 2];
947 pub const EMPTY: char = '.';
948
949 pub fn cell_sides(&self) -> [f32; 2] {
950 self.cell.map_or(Self::CELL, CellSize::sides)
951 }
952
953 pub fn cells(&self) -> Vec<([i32; 2], &str)> {
954 let mut out = Vec::new();
955 if let Some(rows) = &self.rows {
956 for (place, row) in rows.iter().enumerate() {
957 let j = (rows.len() - 1 - place) as i32;
958 for (i, (at, character)) in row.char_indices().enumerate() {
959 if character != Self::EMPTY {
960 out.push(([i as i32, j], &row[at..at + character.len_utf8()]));
961 }
962 }
963 }
964 }
965 for cell in self.cells.iter().flatten() {
966 out.push((cell.at, cell.tile.as_str()));
967 }
968 out
969 }
970
971 pub fn position(&self, at: [i32; 2]) -> [f32; 3] {
972 let (across, up) = self.plane.unwrap_or_default().axes();
973 let [width, height] = self.cell_sides();
974 let (i, j) = (at[0] as f32 * width, at[1] as f32 * height);
975 [
976 self.origin[0] + i * across[0] + j * up[0],
977 self.origin[1] + i * across[1] + j * up[1],
978 self.origin[2] + i * across[2] + j * up[2],
979 ]
980 }
981}
982
983#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
984#[serde(rename_all = "snake_case")]
985pub enum ProxyKind {
986 Box,
987 Hull,
988}
989
990#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
991#[serde(deny_unknown_fields)]
992pub struct Proxy {
993 #[serde(default, skip_serializing_if = "Option::is_none")]
994 pub id: Option<Id>,
995 pub object: String,
996 pub kind: ProxyKind,
997 #[serde(default, skip_serializing_if = "Option::is_none")]
998 pub at: Option<[f32; 3]>,
999 #[serde(default, skip_serializing_if = "Option::is_none")]
1000 pub rotate: Option<[f32; 3]>,
1001 #[serde(default, skip_serializing_if = "Option::is_none")]
1002 pub size: Option<[f32; 3]>,
1003 #[serde(default, skip_serializing_if = "Option::is_none")]
1004 pub points: Option<Vec<[f32; 3]>>,
1005 #[serde(default, skip_serializing_if = "Option::is_none")]
1006 pub triangles: Option<Vec<[u32; 3]>>,
1007 #[serde(default, skip_serializing_if = "Option::is_none")]
1008 pub authoring: Option<toml::Value>,
1009}