1use animsmith_core::{InputIdentity, SourceFormatV1};
2use std::collections::BTreeMap;
3
4#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
6pub struct ProfileSelection {
7 family: String,
8 profile_revision: u32,
9 engine_version: String,
10 importer: String,
11}
12
13impl ProfileSelection {
14 pub fn new(
16 family: impl Into<String>,
17 profile_revision: u32,
18 engine_version: impl Into<String>,
19 importer: impl Into<String>,
20 ) -> Self {
21 Self {
22 family: family.into(),
23 profile_revision,
24 engine_version: engine_version.into(),
25 importer: importer.into(),
26 }
27 }
28
29 pub fn family(&self) -> &str {
31 &self.family
32 }
33
34 pub const fn profile_revision(&self) -> u32 {
36 self.profile_revision
37 }
38
39 pub fn engine_version(&self) -> &str {
41 &self.engine_version
42 }
43
44 pub fn importer(&self) -> &str {
46 &self.importer
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
52pub enum FactId {
53 AcceptedInputs,
55 AnimationAddressability,
57 TargetCoordinateBasis,
59 TargetLinearUnit,
61 UnitConversionControl,
63 AxisConversionControl,
65 ExactAxisConversion,
67 ResultingHierarchyScale,
69 WholeEndFrameRequired,
71 AnimationChannelHandling,
73 ExtensionHandling,
75 ConstructHandling,
77 AnimationTargetAddressability,
79 RootMotionAddressability,
81}
82
83impl FactId {
84 pub const fn as_str(self) -> &'static str {
86 match self {
87 Self::AcceptedInputs => "accepted_inputs",
88 Self::AnimationAddressability => "animation_addressability",
89 Self::TargetCoordinateBasis => "target_coordinate_basis",
90 Self::TargetLinearUnit => "target_linear_unit",
91 Self::UnitConversionControl => "unit_conversion_control",
92 Self::AxisConversionControl => "axis_conversion_control",
93 Self::ExactAxisConversion => "exact_axis_conversion",
94 Self::ResultingHierarchyScale => "resulting_hierarchy_scale",
95 Self::WholeEndFrameRequired => "whole_end_frame_required",
96 Self::AnimationChannelHandling => "animation_channel_handling",
97 Self::ExtensionHandling => "extension_handling",
98 Self::ConstructHandling => "construct_handling",
99 Self::AnimationTargetAddressability => "animation_target_addressability",
100 Self::RootMotionAddressability => "root_motion_addressability",
101 }
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
107pub enum Handedness {
108 Left,
110 Right,
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
116pub enum UpAxis {
117 X,
119 Y,
121 Z,
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
127pub struct CoordinateBasis {
128 pub handedness: Handedness,
130 pub up_axis: UpAxis,
132 pub forward_axis: ForwardAxis,
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
138pub enum ForwardAxis {
139 PositiveX,
141 NegativeX,
143 PositiveY,
145 NegativeY,
147 PositiveZ,
149 NegativeZ,
151}
152
153#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
155pub enum LinearUnit {
156 Metre,
158 Centimetre,
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
164pub enum SettingId {
165 ConvertUnits,
167 BakeAxisConversion,
169 RootMotionSource,
171 RootRotation,
173 RootPositionY,
175 RootPositionXz,
177}
178
179impl SettingId {
180 pub const fn as_str(self) -> &'static str {
182 match self {
183 Self::ConvertUnits => "convert_units",
184 Self::BakeAxisConversion => "bake_axis_conversion",
185 Self::RootMotionSource => "root_motion_source",
186 Self::RootRotation => "root_rotation",
187 Self::RootPositionY => "root_position_y",
188 Self::RootPositionXz => "root_position_xz",
189 }
190 }
191
192 pub(crate) fn from_str(value: &str) -> Option<Self> {
193 match value {
194 "convert_units" => Some(Self::ConvertUnits),
195 "bake_axis_conversion" => Some(Self::BakeAxisConversion),
196 "root_motion_source" => Some(Self::RootMotionSource),
197 "root_rotation" => Some(Self::RootRotation),
198 "root_position_y" => Some(Self::RootPositionY),
199 "root_position_xz" => Some(Self::RootPositionXz),
200 _ => None,
201 }
202 }
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
207pub enum ConversionControl {
208 ProfileSetting(SettingId),
210 ImporterOption,
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
216pub enum ImportHandling {
217 Preserved,
219 Converted,
221 Discarded,
223 Unsupported,
225}
226
227#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
229pub enum TargetAddressability {
230 NamePathDerivedId,
232}
233
234#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
236pub enum AnimationAddressability {
237 GltfAssetLabel,
241}
242
243#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
245pub enum RootMotionAddressability {
246 ExactSourceTransformPath,
248 HumanoidAvatarBody,
250}
251
252#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
254pub enum FactValue {
255 AcceptedFormats(Vec<SourceFormatV1>),
257 AnimationAddressability(AnimationAddressability),
259 CoordinateBasis(CoordinateBasis),
261 LinearUnit(LinearUnit),
263 ConversionControl(ConversionControl),
265 Boolean(bool),
267 ImportHandling(ImportHandling),
269 TargetAddressability(TargetAddressability),
271 RootMotionAddressability(RootMotionAddressability),
273}
274
275#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
277pub enum FactState {
278 Known(FactValue),
280 Unknown,
282 NotApplicable,
284}
285
286#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
288pub struct ProfileFact {
289 id: FactId,
290 state: FactState,
291}
292
293impl ProfileFact {
294 pub(crate) fn new(id: FactId, state: FactState) -> Self {
295 Self { id, state }
296 }
297
298 pub const fn id(&self) -> FactId {
300 self.id
301 }
302
303 pub const fn state(&self) -> &FactState {
305 &self.state
306 }
307}
308
309#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
311pub enum SettingScope {
312 Document,
314 Clip,
316}
317
318#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
320pub enum SettingDomain {
321 Boolean,
323 BakeOrExtract,
325 SourceTransformPath,
327}
328
329#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
331pub enum SettingApplicability {
332 Applicable,
334 NotApplicable,
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
340pub enum DefaultStatus {
341 RequiredWithoutDefault,
343 NotApplicable,
345}
346
347#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
349pub struct SettingDescriptor {
350 id: SettingId,
351 scope: SettingScope,
352 domain: SettingDomain,
353 applicability: SettingApplicability,
354 default_status: DefaultStatus,
355}
356
357impl SettingDescriptor {
358 pub(crate) const fn new(
359 id: SettingId,
360 scope: SettingScope,
361 domain: SettingDomain,
362 applicability: SettingApplicability,
363 default_status: DefaultStatus,
364 ) -> Self {
365 Self {
366 id,
367 scope,
368 domain,
369 applicability,
370 default_status,
371 }
372 }
373
374 pub const fn id(&self) -> SettingId {
376 self.id
377 }
378
379 pub const fn scope(&self) -> SettingScope {
381 self.scope
382 }
383
384 pub const fn domain(&self) -> SettingDomain {
386 self.domain
387 }
388
389 pub const fn applicability(&self) -> SettingApplicability {
391 self.applicability
392 }
393
394 pub const fn default_status(&self) -> DefaultStatus {
396 self.default_status
397 }
398}
399
400#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
402pub enum BakeOrExtract {
403 Bake,
405 Extract,
407}
408
409#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
411pub enum SettingValue {
412 Boolean(bool),
414 BakeOrExtract(BakeOrExtract),
416 SourceTransformPath(String),
418}
419
420pub type SettingMap = BTreeMap<String, SettingValue>;
422
423#[derive(Debug, Clone, Default, PartialEq, Eq)]
425pub struct EngineDeclaration {
426 pub selection: Option<ProfileSelection>,
428 pub document_settings: Option<SettingMap>,
430 pub clip_settings: BTreeMap<String, SettingMap>,
432}
433
434#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
436pub struct PrimarySource {
437 id: &'static str,
438 target_version: &'static str,
439 url: &'static str,
440 verified_on: &'static str,
441 supported_facts: Vec<FactId>,
442 supported_settings: Vec<SettingId>,
443}
444
445impl PrimarySource {
446 pub(crate) fn new(
447 id: &'static str,
448 target_version: &'static str,
449 url: &'static str,
450 verified_on: &'static str,
451 mut supported_facts: Vec<FactId>,
452 mut supported_settings: Vec<SettingId>,
453 ) -> Self {
454 supported_facts.sort_by_key(|id| id.as_str());
455 supported_settings.sort_by_key(|id| id.as_str());
456 Self {
457 id,
458 target_version,
459 url,
460 verified_on,
461 supported_facts,
462 supported_settings,
463 }
464 }
465
466 pub const fn id(&self) -> &'static str {
468 self.id
469 }
470
471 pub const fn target_version(&self) -> &'static str {
473 self.target_version
474 }
475
476 pub const fn url(&self) -> &'static str {
478 self.url
479 }
480
481 pub const fn verified_on(&self) -> &'static str {
483 self.verified_on
484 }
485
486 pub fn supported_facts(&self) -> &[FactId] {
488 &self.supported_facts
489 }
490
491 pub fn supported_settings(&self) -> &[SettingId] {
493 &self.supported_settings
494 }
495
496 #[cfg(test)]
497 pub(crate) fn reverse_supported_ids_for_test(&mut self) {
498 self.supported_facts.reverse();
499 self.supported_settings.reverse();
500 }
501}
502
503#[derive(Debug, Clone, PartialEq, Eq)]
505pub struct EngineProfile {
506 pub(crate) selection: ProfileSelection,
507 pub(crate) fact_bundle_urn: &'static str,
508 pub(crate) facts: Vec<ProfileFact>,
509 pub(crate) settings: Vec<SettingDescriptor>,
510 pub(crate) sources: Vec<PrimarySource>,
511 pub(crate) facts_identity: InputIdentity,
512}
513
514impl EngineProfile {
515 pub const fn selection(&self) -> &ProfileSelection {
517 &self.selection
518 }
519
520 pub const fn fact_bundle_urn(&self) -> &'static str {
522 self.fact_bundle_urn
523 }
524
525 pub fn accepted_inputs(&self) -> &[SourceFormatV1] {
527 match self.fact(FactId::AcceptedInputs).map(ProfileFact::state) {
528 Some(FactState::Known(FactValue::AcceptedFormats(formats))) => formats,
529 _ => &[],
530 }
531 }
532
533 pub fn facts(&self) -> &[ProfileFact] {
535 &self.facts
536 }
537
538 pub fn setting_descriptors(&self) -> &[SettingDescriptor] {
540 &self.settings
541 }
542
543 pub fn sources(&self) -> &[PrimarySource] {
545 &self.sources
546 }
547
548 pub const fn facts_identity(&self) -> &InputIdentity {
550 &self.facts_identity
551 }
552
553 pub fn fact(&self, id: FactId) -> Option<&ProfileFact> {
555 self.facts.iter().find(|fact| fact.id == id)
556 }
557
558 pub fn setting_descriptor(&self, id: SettingId) -> Option<&SettingDescriptor> {
560 self.settings.iter().find(|descriptor| descriptor.id == id)
561 }
562}