use animsmith_core::{InputIdentity, SourceFormatV1};
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ProfileSelection {
family: String,
profile_revision: u32,
engine_version: String,
importer: String,
}
impl ProfileSelection {
pub fn new(
family: impl Into<String>,
profile_revision: u32,
engine_version: impl Into<String>,
importer: impl Into<String>,
) -> Self {
Self {
family: family.into(),
profile_revision,
engine_version: engine_version.into(),
importer: importer.into(),
}
}
pub fn family(&self) -> &str {
&self.family
}
pub const fn profile_revision(&self) -> u32 {
self.profile_revision
}
pub fn engine_version(&self) -> &str {
&self.engine_version
}
pub fn importer(&self) -> &str {
&self.importer
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum FactId {
AcceptedInputs,
AnimationAddressability,
TargetCoordinateBasis,
TargetLinearUnit,
UnitConversionControl,
AxisConversionControl,
ExactAxisConversion,
ResultingHierarchyScale,
WholeEndFrameRequired,
AnimationChannelHandling,
ExtensionHandling,
ConstructHandling,
AnimationTargetAddressability,
RootMotionAddressability,
}
impl FactId {
pub const fn as_str(self) -> &'static str {
match self {
Self::AcceptedInputs => "accepted_inputs",
Self::AnimationAddressability => "animation_addressability",
Self::TargetCoordinateBasis => "target_coordinate_basis",
Self::TargetLinearUnit => "target_linear_unit",
Self::UnitConversionControl => "unit_conversion_control",
Self::AxisConversionControl => "axis_conversion_control",
Self::ExactAxisConversion => "exact_axis_conversion",
Self::ResultingHierarchyScale => "resulting_hierarchy_scale",
Self::WholeEndFrameRequired => "whole_end_frame_required",
Self::AnimationChannelHandling => "animation_channel_handling",
Self::ExtensionHandling => "extension_handling",
Self::ConstructHandling => "construct_handling",
Self::AnimationTargetAddressability => "animation_target_addressability",
Self::RootMotionAddressability => "root_motion_addressability",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Handedness {
Left,
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum UpAxis {
X,
Y,
Z,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct CoordinateBasis {
pub handedness: Handedness,
pub up_axis: UpAxis,
pub forward_axis: ForwardAxis,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ForwardAxis {
PositiveX,
NegativeX,
PositiveY,
NegativeY,
PositiveZ,
NegativeZ,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum LinearUnit {
Metre,
Centimetre,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SettingId {
ConvertUnits,
BakeAxisConversion,
RootMotionSource,
RootRotation,
RootPositionY,
RootPositionXz,
}
impl SettingId {
pub const fn as_str(self) -> &'static str {
match self {
Self::ConvertUnits => "convert_units",
Self::BakeAxisConversion => "bake_axis_conversion",
Self::RootMotionSource => "root_motion_source",
Self::RootRotation => "root_rotation",
Self::RootPositionY => "root_position_y",
Self::RootPositionXz => "root_position_xz",
}
}
pub(crate) fn from_str(value: &str) -> Option<Self> {
match value {
"convert_units" => Some(Self::ConvertUnits),
"bake_axis_conversion" => Some(Self::BakeAxisConversion),
"root_motion_source" => Some(Self::RootMotionSource),
"root_rotation" => Some(Self::RootRotation),
"root_position_y" => Some(Self::RootPositionY),
"root_position_xz" => Some(Self::RootPositionXz),
_ => None,
}
}
}
impl std::fmt::Display for SettingId {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ConversionControl {
ProfileSetting(SettingId),
ImporterOption,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ImportHandling {
Preserved,
Converted,
Discarded,
Unsupported,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum TargetAddressability {
NamePathDerivedId,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AnimationAddressability {
GltfAssetLabel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum RootMotionAddressability {
ExactSourceTransformPath,
HumanoidAvatarBody,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum FactValue {
AcceptedFormats(Vec<SourceFormatV1>),
AnimationAddressability(AnimationAddressability),
CoordinateBasis(CoordinateBasis),
LinearUnit(LinearUnit),
ConversionControl(ConversionControl),
Boolean(bool),
ImportHandling(ImportHandling),
TargetAddressability(TargetAddressability),
RootMotionAddressability(RootMotionAddressability),
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum FactState {
Known(FactValue),
Unknown,
NotApplicable,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ProfileFact {
id: FactId,
state: FactState,
}
impl ProfileFact {
pub(crate) fn new(id: FactId, state: FactState) -> Self {
Self { id, state }
}
pub const fn id(&self) -> FactId {
self.id
}
pub const fn state(&self) -> &FactState {
&self.state
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SettingScope {
Document,
Clip,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SettingDomain {
Boolean,
BakeOrExtract,
SourceTransformPath,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SettingApplicability {
Applicable,
NotApplicable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DefaultStatus {
RequiredWithoutDefault,
NotApplicable,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct SettingDescriptor {
id: SettingId,
scope: SettingScope,
domain: SettingDomain,
applicability: SettingApplicability,
default_status: DefaultStatus,
}
impl SettingDescriptor {
pub(crate) const fn new(
id: SettingId,
scope: SettingScope,
domain: SettingDomain,
applicability: SettingApplicability,
default_status: DefaultStatus,
) -> Self {
Self {
id,
scope,
domain,
applicability,
default_status,
}
}
pub const fn id(&self) -> SettingId {
self.id
}
pub const fn scope(&self) -> SettingScope {
self.scope
}
pub const fn domain(&self) -> SettingDomain {
self.domain
}
pub const fn applicability(&self) -> SettingApplicability {
self.applicability
}
pub const fn default_status(&self) -> DefaultStatus {
self.default_status
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum BakeOrExtract {
Bake,
Extract,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum SettingValue {
Boolean(bool),
BakeOrExtract(BakeOrExtract),
SourceTransformPath(String),
}
pub type SettingMap = BTreeMap<String, SettingValue>;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct EngineDeclaration {
pub selection: Option<ProfileSelection>,
pub document_settings: Option<SettingMap>,
pub clip_settings: BTreeMap<String, SettingMap>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PrimarySource {
id: &'static str,
target_version: &'static str,
url: &'static str,
verified_on: &'static str,
supported_facts: Vec<FactId>,
supported_settings: Vec<SettingId>,
}
impl PrimarySource {
pub(crate) fn new(
id: &'static str,
target_version: &'static str,
url: &'static str,
verified_on: &'static str,
mut supported_facts: Vec<FactId>,
mut supported_settings: Vec<SettingId>,
) -> Self {
supported_facts.sort_by_key(|id| id.as_str());
supported_settings.sort_by_key(|id| id.as_str());
Self {
id,
target_version,
url,
verified_on,
supported_facts,
supported_settings,
}
}
pub const fn id(&self) -> &'static str {
self.id
}
pub const fn target_version(&self) -> &'static str {
self.target_version
}
pub const fn url(&self) -> &'static str {
self.url
}
pub const fn verified_on(&self) -> &'static str {
self.verified_on
}
pub fn supported_facts(&self) -> &[FactId] {
&self.supported_facts
}
pub fn supported_settings(&self) -> &[SettingId] {
&self.supported_settings
}
#[cfg(test)]
pub(crate) fn reverse_supported_ids_for_test(&mut self) {
self.supported_facts.reverse();
self.supported_settings.reverse();
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EngineProfile {
pub(crate) selection: ProfileSelection,
pub(crate) fact_bundle_urn: &'static str,
pub(crate) facts: Vec<ProfileFact>,
pub(crate) settings: Vec<SettingDescriptor>,
pub(crate) sources: Vec<PrimarySource>,
pub(crate) facts_identity: InputIdentity,
}
impl EngineProfile {
pub const fn selection(&self) -> &ProfileSelection {
&self.selection
}
pub const fn fact_bundle_urn(&self) -> &'static str {
self.fact_bundle_urn
}
pub fn accepted_inputs(&self) -> &[SourceFormatV1] {
match self.fact(FactId::AcceptedInputs).map(ProfileFact::state) {
Some(FactState::Known(FactValue::AcceptedFormats(formats))) => formats,
_ => &[],
}
}
pub fn facts(&self) -> &[ProfileFact] {
&self.facts
}
pub fn setting_descriptors(&self) -> &[SettingDescriptor] {
&self.settings
}
pub fn sources(&self) -> &[PrimarySource] {
&self.sources
}
pub const fn facts_identity(&self) -> &InputIdentity {
&self.facts_identity
}
pub fn fact(&self, id: FactId) -> Option<&ProfileFact> {
self.facts.iter().find(|fact| fact.id == id)
}
pub fn setting_descriptor(&self, id: SettingId) -> Option<&SettingDescriptor> {
self.settings.iter().find(|descriptor| descriptor.id == id)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SettingIdV2 {
AnimationType,
AvatarSetup,
ImportAnimation,
RootMotionSource,
RootRotation,
RootPositionY,
RootPositionXz,
RotateSceneEntity,
RotateMeshes,
LoadMeshes,
ExtensionHandlerEnvironment,
BevyAnimationFeature,
LoadAnimations,
AnimationFps,
AnimationTrimming,
SampleRate,
}
impl SettingIdV2 {
pub const fn as_str(self) -> &'static str {
match self {
Self::AnimationType => "animation_type",
Self::AvatarSetup => "avatar_setup",
Self::ImportAnimation => "import_animation",
Self::RootMotionSource => "root_motion_source",
Self::RootRotation => "root_rotation",
Self::RootPositionY => "root_position_y",
Self::RootPositionXz => "root_position_xz",
Self::RotateSceneEntity => "rotate_scene_entity",
Self::RotateMeshes => "rotate_meshes",
Self::LoadMeshes => "load_meshes",
Self::ExtensionHandlerEnvironment => "extension_handler_environment",
Self::BevyAnimationFeature => "bevy_animation_feature",
Self::LoadAnimations => "load_animations",
Self::AnimationFps => "animation_fps",
Self::AnimationTrimming => "animation_trimming",
Self::SampleRate => "sample_rate",
}
}
pub(crate) fn from_str_v2(value: &str) -> Option<Self> {
match value {
"animation_type" => Some(Self::AnimationType),
"avatar_setup" => Some(Self::AvatarSetup),
"import_animation" => Some(Self::ImportAnimation),
"root_motion_source" => Some(Self::RootMotionSource),
"root_rotation" => Some(Self::RootRotation),
"root_position_y" => Some(Self::RootPositionY),
"root_position_xz" => Some(Self::RootPositionXz),
"rotate_scene_entity" => Some(Self::RotateSceneEntity),
"rotate_meshes" => Some(Self::RotateMeshes),
"load_meshes" => Some(Self::LoadMeshes),
"extension_handler_environment" => Some(Self::ExtensionHandlerEnvironment),
"bevy_animation_feature" => Some(Self::BevyAnimationFeature),
"load_animations" => Some(Self::LoadAnimations),
"animation_fps" => Some(Self::AnimationFps),
"animation_trimming" => Some(Self::AnimationTrimming),
"sample_rate" => Some(Self::SampleRate),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum UnrealSampleRateV2 {
Default30,
SourceDetermined,
CustomHz(u32),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum UnityAnimationTypeV2 {
Generic,
Humanoid,
Legacy,
}
impl UnityAnimationTypeV2 {
pub const fn as_str(self) -> &'static str {
match self {
Self::Generic => "generic",
Self::Humanoid => "humanoid",
Self::Legacy => "legacy",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum UnityAvatarSetupV2 {
CreateFromThisModel,
CopyFromOtherAvatar,
}
impl UnityAvatarSetupV2 {
pub const fn as_str(self) -> &'static str {
match self {
Self::CreateFromThisModel => "create_from_this_model",
Self::CopyFromOtherAvatar => "copy_from_other_avatar",
}
}
}
impl std::fmt::Display for SettingIdV2 {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum BevyLoadMeshesStateV2 {
Empty,
Nonempty,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum BevyGltfHandlerEnvironmentV2 {
BareEmpty,
BevyPbrStock019,
}
impl BevyGltfHandlerEnvironmentV2 {
pub const fn as_str(self) -> &'static str {
match self {
Self::BareEmpty => "bare_empty",
Self::BevyPbrStock019 => "bevy_pbr_stock_0_19",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SettingDomainV2 {
Boolean,
BakeOrExtract,
SourceTransformPath,
AnimationType,
AvatarSetup,
LoadMeshesState,
HandlerEnvironment,
PositiveInteger,
SampleRate,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum SettingValueV2 {
Boolean(bool),
BakeOrExtract(BakeOrExtract),
SourceTransformPath(String),
AnimationType(UnityAnimationTypeV2),
AvatarSetup(UnityAvatarSetupV2),
LoadMeshesState(BevyLoadMeshesStateV2),
HandlerEnvironment(BevyGltfHandlerEnvironmentV2),
PositiveInteger(u32),
SampleRate(UnrealSampleRateV2),
}
impl SettingValueV2 {
pub(crate) const fn matches_domain(&self, domain: SettingDomainV2) -> bool {
matches!(
(self, domain),
(Self::Boolean(_), SettingDomainV2::Boolean)
| (Self::BakeOrExtract(_), SettingDomainV2::BakeOrExtract)
| (
Self::SourceTransformPath(_),
SettingDomainV2::SourceTransformPath
)
| (Self::AnimationType(_), SettingDomainV2::AnimationType)
| (Self::AvatarSetup(_), SettingDomainV2::AvatarSetup)
| (Self::LoadMeshesState(_), SettingDomainV2::LoadMeshesState)
| (
Self::HandlerEnvironment(_),
SettingDomainV2::HandlerEnvironment
)
| (
Self::PositiveInteger(1..=120),
SettingDomainV2::PositiveInteger
)
| (
Self::SampleRate(
UnrealSampleRateV2::Default30 | UnrealSampleRateV2::SourceDetermined
),
SettingDomainV2::SampleRate
)
| (
Self::SampleRate(UnrealSampleRateV2::CustomHz(1..=48_000)),
SettingDomainV2::SampleRate
)
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum SettingDefaultV2 {
RequiredExplicit,
Verified(SettingValueV2),
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct SettingDescriptorV2 {
pub(crate) id: SettingIdV2,
pub(crate) scope: SettingScope,
pub(crate) domain: SettingDomainV2,
pub(crate) default: SettingDefaultV2,
}
impl SettingDescriptorV2 {
pub(crate) const fn new(
id: SettingIdV2,
scope: SettingScope,
domain: SettingDomainV2,
default: SettingDefaultV2,
) -> Self {
Self {
id,
scope,
domain,
default,
}
}
pub const fn id(&self) -> SettingIdV2 {
self.id
}
pub const fn scope(&self) -> SettingScope {
self.scope
}
pub const fn domain(&self) -> SettingDomainV2 {
self.domain
}
pub const fn default(&self) -> &SettingDefaultV2 {
&self.default
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PrimarySourceV2 {
pub(crate) id: &'static str,
pub(crate) target_version: &'static str,
pub(crate) url: &'static str,
pub(crate) verified_on: &'static str,
pub(crate) supported_facts: Vec<animsmith_core::engine_contract::EngineFactIdV2>,
pub(crate) supported_settings: Vec<SettingIdV2>,
}
impl PrimarySourceV2 {
pub const fn id(&self) -> &'static str {
self.id
}
pub const fn target_version(&self) -> &'static str {
self.target_version
}
pub const fn url(&self) -> &'static str {
self.url
}
pub const fn verified_on(&self) -> &'static str {
self.verified_on
}
pub fn supports_accepted_inputs(&self) -> bool {
self.supported_facts
.contains(&animsmith_core::engine_contract::EngineFactIdV2::AcceptedInputs)
}
pub fn supported_facts(&self) -> &[animsmith_core::engine_contract::EngineFactIdV2] {
&self.supported_facts
}
pub fn supported_settings(&self) -> &[SettingIdV2] {
&self.supported_settings
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EngineProfileV2 {
pub(crate) selection: ProfileSelection,
pub(crate) profile_urn: &'static str,
pub(crate) accepted_inputs: Vec<SourceFormatV1>,
pub(crate) facts: Vec<animsmith_core::engine_contract::EngineProfileFactV2>,
pub(crate) settings: Vec<SettingDescriptorV2>,
pub(crate) sources: Vec<PrimarySourceV2>,
}
impl EngineProfileV2 {
pub const fn selection(&self) -> &ProfileSelection {
&self.selection
}
pub const fn profile_urn(&self) -> &'static str {
self.profile_urn
}
pub fn accepted_inputs(&self) -> &[SourceFormatV1] {
&self.accepted_inputs
}
pub fn facts(&self) -> &[animsmith_core::engine_contract::EngineProfileFactV2] {
&self.facts
}
pub fn setting_descriptors(&self) -> &[SettingDescriptorV2] {
&self.settings
}
pub fn sources(&self) -> &[PrimarySourceV2] {
&self.sources
}
pub fn setting_descriptor(&self, id: SettingIdV2) -> Option<&SettingDescriptorV2> {
self.settings.iter().find(|descriptor| descriptor.id == id)
}
}
pub type SettingMapV2 = BTreeMap<String, SettingValueV2>;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct EngineDeclarationV2 {
pub selection: Option<ProfileSelection>,
pub document_settings: Option<SettingMapV2>,
pub clip_settings: BTreeMap<String, SettingMapV2>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ResolvedSettingOriginV2 {
ExplicitConfig,
ProfileDefault,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ResolvedSettingV2 {
pub(crate) value: SettingValueV2,
pub(crate) origin: ResolvedSettingOriginV2,
}
impl ResolvedSettingV2 {
pub const fn value(&self) -> &SettingValueV2 {
&self.value
}
pub const fn origin(&self) -> ResolvedSettingOriginV2 {
self.origin
}
}