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
205impl std::fmt::Display for SettingId {
206 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 formatter.write_str(self.as_str())
208 }
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
213pub enum ConversionControl {
214 ProfileSetting(SettingId),
216 ImporterOption,
218}
219
220#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
222pub enum ImportHandling {
223 Preserved,
225 Converted,
227 Discarded,
229 Unsupported,
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
235pub enum TargetAddressability {
236 NamePathDerivedId,
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
242pub enum AnimationAddressability {
243 GltfAssetLabel,
247}
248
249#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
251pub enum RootMotionAddressability {
252 ExactSourceTransformPath,
254 HumanoidAvatarBody,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
260pub enum FactValue {
261 AcceptedFormats(Vec<SourceFormatV1>),
263 AnimationAddressability(AnimationAddressability),
265 CoordinateBasis(CoordinateBasis),
267 LinearUnit(LinearUnit),
269 ConversionControl(ConversionControl),
271 Boolean(bool),
273 ImportHandling(ImportHandling),
275 TargetAddressability(TargetAddressability),
277 RootMotionAddressability(RootMotionAddressability),
279}
280
281#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
283pub enum FactState {
284 Known(FactValue),
286 Unknown,
288 NotApplicable,
290}
291
292#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
294pub struct ProfileFact {
295 id: FactId,
296 state: FactState,
297}
298
299impl ProfileFact {
300 pub(crate) fn new(id: FactId, state: FactState) -> Self {
301 Self { id, state }
302 }
303
304 pub const fn id(&self) -> FactId {
306 self.id
307 }
308
309 pub const fn state(&self) -> &FactState {
311 &self.state
312 }
313}
314
315#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
317pub enum SettingScope {
318 Document,
320 Clip,
322}
323
324#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
326pub enum SettingDomain {
327 Boolean,
329 BakeOrExtract,
331 SourceTransformPath,
333}
334
335#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
337pub enum SettingApplicability {
338 Applicable,
340 NotApplicable,
342}
343
344#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
346pub enum DefaultStatus {
347 RequiredWithoutDefault,
349 NotApplicable,
351}
352
353#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
355pub struct SettingDescriptor {
356 id: SettingId,
357 scope: SettingScope,
358 domain: SettingDomain,
359 applicability: SettingApplicability,
360 default_status: DefaultStatus,
361}
362
363impl SettingDescriptor {
364 pub(crate) const fn new(
365 id: SettingId,
366 scope: SettingScope,
367 domain: SettingDomain,
368 applicability: SettingApplicability,
369 default_status: DefaultStatus,
370 ) -> Self {
371 Self {
372 id,
373 scope,
374 domain,
375 applicability,
376 default_status,
377 }
378 }
379
380 pub const fn id(&self) -> SettingId {
382 self.id
383 }
384
385 pub const fn scope(&self) -> SettingScope {
387 self.scope
388 }
389
390 pub const fn domain(&self) -> SettingDomain {
392 self.domain
393 }
394
395 pub const fn applicability(&self) -> SettingApplicability {
397 self.applicability
398 }
399
400 pub const fn default_status(&self) -> DefaultStatus {
402 self.default_status
403 }
404}
405
406#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
408pub enum BakeOrExtract {
409 Bake,
411 Extract,
413}
414
415#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
417pub enum SettingValue {
418 Boolean(bool),
420 BakeOrExtract(BakeOrExtract),
422 SourceTransformPath(String),
424}
425
426pub type SettingMap = BTreeMap<String, SettingValue>;
428
429#[derive(Debug, Clone, Default, PartialEq, Eq)]
431pub struct EngineDeclaration {
432 pub selection: Option<ProfileSelection>,
434 pub document_settings: Option<SettingMap>,
436 pub clip_settings: BTreeMap<String, SettingMap>,
438}
439
440#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
442pub struct PrimarySource {
443 id: &'static str,
444 target_version: &'static str,
445 url: &'static str,
446 verified_on: &'static str,
447 supported_facts: Vec<FactId>,
448 supported_settings: Vec<SettingId>,
449}
450
451impl PrimarySource {
452 pub(crate) fn new(
453 id: &'static str,
454 target_version: &'static str,
455 url: &'static str,
456 verified_on: &'static str,
457 mut supported_facts: Vec<FactId>,
458 mut supported_settings: Vec<SettingId>,
459 ) -> Self {
460 supported_facts.sort_by_key(|id| id.as_str());
461 supported_settings.sort_by_key(|id| id.as_str());
462 Self {
463 id,
464 target_version,
465 url,
466 verified_on,
467 supported_facts,
468 supported_settings,
469 }
470 }
471
472 pub const fn id(&self) -> &'static str {
474 self.id
475 }
476
477 pub const fn target_version(&self) -> &'static str {
479 self.target_version
480 }
481
482 pub const fn url(&self) -> &'static str {
484 self.url
485 }
486
487 pub const fn verified_on(&self) -> &'static str {
489 self.verified_on
490 }
491
492 pub fn supported_facts(&self) -> &[FactId] {
494 &self.supported_facts
495 }
496
497 pub fn supported_settings(&self) -> &[SettingId] {
499 &self.supported_settings
500 }
501
502 #[cfg(test)]
503 pub(crate) fn reverse_supported_ids_for_test(&mut self) {
504 self.supported_facts.reverse();
505 self.supported_settings.reverse();
506 }
507}
508
509#[derive(Debug, Clone, PartialEq, Eq)]
511pub struct EngineProfile {
512 pub(crate) selection: ProfileSelection,
513 pub(crate) fact_bundle_urn: &'static str,
514 pub(crate) facts: Vec<ProfileFact>,
515 pub(crate) settings: Vec<SettingDescriptor>,
516 pub(crate) sources: Vec<PrimarySource>,
517 pub(crate) facts_identity: InputIdentity,
518}
519
520impl EngineProfile {
521 pub const fn selection(&self) -> &ProfileSelection {
523 &self.selection
524 }
525
526 pub const fn fact_bundle_urn(&self) -> &'static str {
528 self.fact_bundle_urn
529 }
530
531 pub fn accepted_inputs(&self) -> &[SourceFormatV1] {
533 match self.fact(FactId::AcceptedInputs).map(ProfileFact::state) {
534 Some(FactState::Known(FactValue::AcceptedFormats(formats))) => formats,
535 _ => &[],
536 }
537 }
538
539 pub fn facts(&self) -> &[ProfileFact] {
541 &self.facts
542 }
543
544 pub fn setting_descriptors(&self) -> &[SettingDescriptor] {
546 &self.settings
547 }
548
549 pub fn sources(&self) -> &[PrimarySource] {
551 &self.sources
552 }
553
554 pub const fn facts_identity(&self) -> &InputIdentity {
556 &self.facts_identity
557 }
558
559 pub fn fact(&self, id: FactId) -> Option<&ProfileFact> {
561 self.facts.iter().find(|fact| fact.id == id)
562 }
563
564 pub fn setting_descriptor(&self, id: SettingId) -> Option<&SettingDescriptor> {
566 self.settings.iter().find(|descriptor| descriptor.id == id)
567 }
568}