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)
}
}