ark-api-ffi 0.17.0-pre.15

Ark low-level Wasm FFI API
Documentation
#![allow(clippy::panic)] // TODO: inside the impl_value macro, should try and remove it instead

define_api_id!(9006, "world-v0");

pub type EntityHandle = u64;
pub type MeshHandle = u64;
pub type PlayerId = u64;

pub const ENTITY_HANDLE_INVALID: EntityHandle = 0x0000_0001_FFFF_FFFF;

pub use crate::world_v2::ComponentType;
use crate::PodBool;
use crate::TransparentPad;
use bytemuck::CheckedBitPattern;
use bytemuck::NoUninit;
use bytemuck::Pod;
use bytemuck::Zeroable;
use num_enum::IntoPrimitive;
use num_enum::TryFromPrimitive;
/// Ray, a line through space with a starting point and a direction.
///
/// Any point on the ray can be found through the formula `origin + t * dir`,
/// where t is a non-negative floating point value, which represents the distance
/// along the ray divided by the length of the dir vector.
#[derive(Copy, Clone, Default, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct Ray {
    pub origin: [f32; 3],
    pub dir: [f32; 3],
}

/// Mesh properties
///
/// Properties of a mesh that you can query.
///
/// Especially useful for computing offset matrices, to fix meshes that aren't centered properly.
#[derive(Debug, Copy, Clone, Default, Pod, Zeroable)]
#[repr(C)]
pub struct MeshProperties {
    pub bbox_min: [f32; 3],
    pub bbox_max: [f32; 3],
    pub bounding_radius: f32,
    pub semantic_flags: u32, // Will make a proper bitfield at some point.
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum ValueType {
    None = 0,
    Float32 = 1,
    Vec2 = 2,
    Vec3 = 3,
    Vec4 = 4,
    Quat = 5,
    Bool = 6,
    Int64 = 7,
}

#[derive(Copy, Clone)]
#[repr(C)]
#[ark_api_macros::ffi_union(size = 16, pod_accessors)]
pub union ValueData {
    pub data_f32: f32,
    pub data_vec2: [f32; 2],
    pub data_vec3: [f32; 3],
    pub data_vec4: [f32; 4],
    pub data_quat: [f32; 4],
    pub data_bool: PodBool,
    pub data_i64: i64,
}

impl core::fmt::Debug for ValueData {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let bytes = bytemuck::cast_ref::<Self, [u8; 16]>(self);
        write!(f, "ValueData {{ <opaque>: {bytes:?} }}")
    }
}

#[derive(Copy, Clone, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct Value {
    pub value_type: ValueType,
    // ValueData is align(8) and ValueType only has size 4, this manually takes up what would otherwise be padding bytes.
    pub _pad: u32,
    pub value_data: ValueData,
}

impl std::fmt::Debug for Value {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.value_type {
            ValueType::None => write!(f, "None"),
            ValueType::Float32 => write!(f, "Float32 {:?}", self.as_f32()),
            ValueType::Vec2 => write!(f, "Vec2 {:?}", self.as_vec2()),
            ValueType::Vec3 => write!(f, "Vec3 {:?}", self.as_vec3()),
            ValueType::Vec4 => write!(f, "Vec4 {:?}", self.as_vec4()),
            ValueType::Quat => write!(f, "Quat {:?}", self.as_quat()),
            ValueType::Bool => write!(f, "Bool {:?}", self.as_bool()),
            ValueType::Int64 => write!(f, "Int64 {:?}", self.as_i64()),
        }
    }
}

// Macro to generate functions for converting to and from the variant
macro_rules! impl_value {
    ($as_function: ident, $from_function: ident, $value_data:ident, $data_accessor:ident, $base_type:ty, $value_type:ident) => {
        #[inline]
        pub fn $as_function(&self) -> $base_type {
            assert_eq!(self.value_type, ValueType::$value_type);
            *(ValueData::$data_accessor(&self.value_data))
        }

        #[inline]
        pub fn $from_function(v: $base_type) -> Self {
            Self {
                value_type: ValueType::$value_type,
                _pad: 0,
                value_data: ValueData {
                    $value_data: TransparentPad::new(v),
                },
            }
        }
    };
}

impl Value {
    impl_value!(as_i64, from_i64, data_i64, as_data_i64, i64, Int64);

    impl_value!(as_f32, from_f32, data_f32, as_data_f32, f32, Float32);

    impl_value!(as_vec2, from_vec2, data_vec2, as_data_vec2, [f32; 2], Vec2);

    impl_value!(as_vec3, from_vec3, data_vec3, as_data_vec3, [f32; 3], Vec3);

    impl_value!(as_vec4, from_vec4, data_vec4, as_data_vec4, [f32; 4], Vec4);

    impl_value!(as_quat, from_quat, data_quat, as_data_quat, [f32; 4], Quat);

    pub fn none() -> Self {
        Self {
            value_type: ValueType::None,
            value_data: ValueData {
                data_i64: TransparentPad::new(0),
            },
            _pad: 0,
        }
    }

    // bool has to go separately because it uses an additional `PodBool` wrapper.

    #[inline]
    pub fn as_bool(&self) -> bool {
        assert_eq!(self.value_type, ValueType::Bool);
        #[allow(clippy::expect_used)]
        self.value_data.as_data_bool().as_bool()
    }

    #[inline]
    pub fn from_bool(v: bool) -> Self {
        Self {
            value_type: ValueType::Bool,
            _pad: 0,
            value_data: ValueData {
                data_bool: TransparentPad::new(PodBool::from(v)),
            },
        }
    }
}

/// The type of a `Joint`.
#[derive(
    Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive, NoUninit, CheckedBitPattern,
)]
#[repr(u64)]
#[non_exhaustive]
pub enum JointType {
    /// Fixed joints are like glue, simply attaches one object to the other. Breakable fixed joints
    /// can be useful for a limited destruction simulation.
    Fixed = 0,
    /// Ball joints are what you'd expect - lots of freedom to turn and bend. Great for ragdolls, attaching
    /// trailers to cars, etc.
    Ball = 1,
    /// A hinge joint has a single axis of rotation, just like a real-world hinge. Useful for not only doors
    /// and gates, but also for things like attaching a wheel to a car body.
    Hinge = 2,
    /// A slider joint lets one object slide along a defined axis relative to the other one.
    Slider = 3,
    /// A distance joint limits the distance between two objects.
    Distance = 4,
}

bitflags! {
    #[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
    #[cfg_attr(feature = "with_speedy", derive(speedy::Writable, speedy::Readable))]
    #[cfg_attr(feature = "with_arbitrary", derive(arbitrary::Arbitrary))]
    #[repr(C)]
    #[derive(Pod, Zeroable)]
    pub struct DynamicLockFlags : u8 {
        const LINEAR_X = 0b0000_0001;
        const LINEAR_Y = 0b0000_0010;
        const LINEAR_Z = 0b0000_0100;
        const ANGULAR_X = 0b0000_1000;
        const ANGULAR_Y = 0b0001_0000;
        const ANGULAR_Z = 0b0010_0000;
    }
}

bitflags! {
    #[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
    #[cfg_attr(feature = "with_speedy", derive(speedy::Writable, speedy::Readable))]
    #[cfg_attr(feature = "with_arbitrary", derive(arbitrary::Arbitrary))]
    #[repr(C)]
    #[derive(Pod, Zeroable)]
    pub struct BoneWeightFlags : u32 {
        const VISUALIZE_WEIGHTS = 1;
    }
}

/// This operation will be executed at every update tick. Noop means that it won't execute
/// either Start or Stop (just update whatever parameters we have set on the audio
/// source if the audio source is running)
#[repr(u8)]
#[non_exhaustive]
#[derive(
    Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive, NoUninit, CheckedBitPattern,
)]
#[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "with_speedy", derive(speedy::Writable, speedy::Readable))]
pub enum AudioSourceOperation {
    Start = 0,
    Stop = 1,
    Noop = 2,
}

/// The rigid body mode of a Physics component of an entity.
///
/// Can not be set directly (still need to use `enable_dynamic` etc), but can be read.
#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u64)]
pub enum RigidBodyMode {
    /// Physics not enabled on this entity.
    Off = 0,
    /// Entity is using static physics.
    Static = 1,
    /// Entity is using dynamic physics.
    Dynamic = 2,
    /// Entity is using kinematic physics.
    Kinematic = 3,
}

/// The limit type of a `Joint`, changes how it will behave if it reaches the set limits
#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u64)]
pub enum JointLimitType {
    /// No limits.
    Off = 0,

    /// Hard limits. No bounce.
    Hard = 1,

    /// Spring limits.
    ///
    /// Takes stiffness and damping parameters, which are set
    /// through `Joint::limits`.
    Spring = 2,
}

/// The motion type of an axis of a D6 `Joint`.
#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u64)]
pub enum D6MotionType {
    /// Cannot move
    Locked = 0,
    /// Limited - applies limits
    Limited = 1,
    /// No limit
    Free = 2,
}

/// The motion type of an axis of a D6 `Joint`.
#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u64)]
pub enum D6Axis {
    /// X axis
    X = 0,
    /// Y axis
    Y = 1,
    /// Z axis
    Z = 2,
    /// Twist (around X)
    Twist = 3,
    /// Swing1 (Y)
    Swing1 = 4,
    /// Swing2 (Z)
    Swing2 = 5,
}

/// The drive types of a D6 `Joint`.
#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u64)]
#[non_exhaustive]
pub enum D6Drive {
    /// X axis
    X = 0,
    /// Y axis
    Y = 1,
    /// Z axis
    Z = 2,
    /// Swing displacement from the X axis (around Y and Z)
    Swing = 3,
    /// Twist rotation around the X axis.
    Twist = 4,
    /// SLERP, spherical interpolation. For driving all three axes together.
    SLERP = 5,
}

#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, NoUninit, CheckedBitPattern)]
#[repr(u32)]
pub enum TriggerEvent {
    Enter = 0,
    Leave = 1,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum EntityInfo {
    /// 'DataHandle(String)'
    Name = 0,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum Transform {
    /// 'Vec3'
    Position = 0,

    /// 'Quat'
    Rotation = 1,

    /// 'Vec3'
    #[deprecated(note = "Use ScaleUniform")] // 2022-09-13
    Scale = 2,

    /// EntityHandle
    ParentId = 3,

    /// 'Vec3'
    WorldPosition = 4,

    /// 'Quat'
    WorldRotation = 5,

    /// 'Vec3'
    #[deprecated(note = "Use WorldScaleUniform")] // 2022-09-13
    WorldScale = 6,

    /// 'f32'
    ScaleUniform = 7,

    /// 'f32'
    WorldScaleUniform = 8,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum Physics {
    /// 'Vec3'
    Velocity = 0,

    /// 'Vec3'
    AngularVelocity = 1,

    /// 'bool'
    PhysxEnable = 3,

    /// 'bool'
    PhysxDynamicEnable = 4,

    /// 'f32'
    PhysxDensity = 5,

    /// 'u64'
    PhysxShape = 6,

    /// 'float'
    PhysxDynamicFriction = 7,

    /// 'float'
    PhysxStaticFriction = 8,

    /// 'float'
    PhysxRestitution = 9,

    /// 'bool'
    PhysxCreateSleeping = 10,

    /// 'bool'
    PhysxKinematic = 11,

    /// 'bool'
    PhysxGravityEnabled = 12,

    /// 'u64'
    PhysxCollisionEventsMask = 13,

    // 'Physics::DynamicLockFlags'
    PhysxDynamicLockFlags = 14,

    /// 'DataHandle(CompoundPhysicsShape)'
    PhysxCompoundShape = 15,

    /// 'f32'
    Mass = 16,

    /// 'Vec3'
    CenterOfMass = 17,

    /// 'Vec4'
    MassRotation = 18,

    /// 'Vec3'
    InertiaTensor = 19,

    /// 'f32'
    LinearDamping = 20,

    /// 'f32'
    AngularDamping = 21,

    /// 'bool'
    TriggerVolume = 22,

    /// 'RigidBodyMode'
    RigidBodyMode = 23,

    /// 'u64'
    SolverPositionIterations = 24,

    /// 'u64'
    SolverVelocityIterations = 25,

    /// `bool`
    PhysxSleeping = 26,

    /// `f32`
    PhysxSleepThreshold = 27,

    /// 'CombineMode'
    FrictionCombineMode = 28,

    /// 'CombineMode'
    RestitutionCombineMode = 29,

    /// 'Vec3'
    NonUniformScale = 30,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum Joint {
    /// 'JointType'
    Type = 0,

    /// 'EntityHandle'
    FirstEntity = 1,

    /// 'EntityHandle'
    SecondEntity = 2,

    /// 'Vec3'
    FirstOffset = 3,

    /// 'Vec3'
    SecondOffset = 4,

    /// 'Quat'
    FirstOrientation = 5,

    /// 'Quat'
    SecondOrientation = 6,

    /// 'Vec2'
    Limits = 7,

    /// 'JointLimitType'
    LimitType = 8,

    /// 'f32'
    Velocity = 9,

    /// 'bool'
    DriveEnabled = 10,

    /// 'f32'
    DriveVelocity = 11,

    /// 'f32'
    DriveForceLimit = 12,

    /// 'f32'
    DriveGearRatio = 13,

    /// 'bool'
    DriveFreespin = 14,

    /// 'f32'
    BreakForce = 15,

    /// 'f32'
    BreakTorque = 16,

    /// 'bool'
    IsBroken = 17,

    /// 'Vec3'
    AngleOrPosition = 18,

    /// 'Vec2'
    LimitSpring = 19,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum D6Joint {
    /// 'EntityHandle'
    Entity1 = 0,

    /// 'EntityHandle'
    Entity2 = 1,

    /// 'Vec3'
    RelativePosition1 = 2,

    /// 'Quat'
    RelativeOrientation1 = 3,

    /// 'Vec3'
    RelativePosition2 = 4,

    /// 'Quat'
    RelativeOrientation2 = 5,

    /// 'f32'
    BreakForce = 6,

    /// 'f32'
    BreakTorque = 7,

    /// 'bool'
    IsBroken = 8,

    /// 'f32'
    ProjectionLinearTolerance = 9,

    /// 'f32'
    ProjectionAngularTolerance = 10,

    /// 'Vec3'
    RelativePosition = 11,

    /// 'Quat'
    RelativeRotation = 12,

    /// 'Vec3'
    RelativeAngles = 13,

    /// 'D6MotionType' for each of the six axes
    MotionX = 14,
    MotionY = 15,
    MotionZ = 16,
    MotionTwist = 17,
    MotionSwing1 = 18,
    MotionSwing2 = 19,

    /// 'Vec2'
    LinearLimit = 20,
    /// 'Vec2'
    LinearLimitSpring = 21,
    /// 'JointLimitType'
    LinearLimitType = 22,

    /// 'Vec2`
    TwistLimit = 23,
    /// 'Vec2`
    TwistLimitSpring = 24,
    /// 'JointLimitType`
    TwistLimitType = 25,

    /// 'Vec2'
    SwingLimit = 26,
    /// 'Vec2`
    SwingLimitSpring = 27,
    /// 'JointLimitType`
    SwingLimitType = 28,

    /// 'bool'
    DriveEnabled = 29,

    /// 'Vec3'
    DrivePosition = 30,
    DriveRotation = 31,

    DriveLinearVelocity = 32,
    DriveAngularVelocity = 33,

    /// 'Vec4' (stiffness, damping, forcelimit)
    DriveParamsX = 34,
    DriveParamsY = 35,
    DriveParamsZ = 36,
    DriveParamsSwing = 37,
    DriveParamsTwist = 38,
    DriveParamsSLERP = 39,

    /// 'bool'
    DriveIsAcceleration = 40,

    /// 'Vec3'
    LinearForce = 41,
    /// 'Vec3'
    AngularForce = 42,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum Render {
    /// 'DataHandle'
    Shape = 0,

    /// 'EntityHandle'
    MeshStyle = 1,

    /// 'bool'
    Visible = 2,

    /// 'PlayerIdSet'
    PlayerIdSet = 3,

    /// `DynamicModuleData`
    DynamicModuleData = 4,

    /// 'Vec3'
    NonUniformScale = 5,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum MeshStyle {
    /// 'Vec4'
    DiffuseTint = 0,

    /// 'MeshStyleFlags'
    Flags = 1,

    // Used to be `HsvTransform`
    Legacy = 2,

    /// 'WorldData'
    MorphTargetWeights = 3,

    /// 'WorldData'
    MaterialOverrides = 4,

    /// 'Vec3'
    BillboardOffset = 5,

    /// 'Vec4'
    OutlineColor = 6,

    /// 'f32'
    OutlineThickness = 7,

    /// 'u32'
    MeshVisibility = 8,

    /// 'u8'
    MeshVisibilityPlayerIdSetFlags = 9,

    /// 'u64'
    MeshVisibilityPlayerIdSet = 10,

    /// 'u32'
    MeshStylePlayerIdSetFlags = 11,

    /// 'u64'
    MeshStylePlayerIdSet = 12,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum Environment {
    /// 'Vec3'
    SunDirection = 0,

    /// 'Vec3'
    GravityVector = 5,

    // `f32`
    FogDensity = 6,

    // `f32`
    FogHeight = 7,

    // `f32`
    FogHeightFalloff = 8,

    // `f32`
    FogStart = 9,

    // `Vec4`
    FogColor = 10,

    // 'f32'
    Exposure = 11,

    // `f32`
    SunSizeMultiplier = 12,

    // `f32`
    GiVolumeSize = 13,

    // `vec3`
    SunColorMultiplier = 14,

    // `vec3`
    AmbientColor = 15,

    // `f32`
    PlanetRadius = 16,

    // `f32`
    AtmosphereDensity = 17,

    // `f32`
    AtmosphereHeight = 18,

    // `f32`
    RayleighHeight = 19,

    // `f32`
    OzoneHeight = 20,

    // `f32`
    MieHeight = 21,

    // `f32`
    OzoneWidth = 22,

    // `f32`
    SkyIntensity = 23,

    // `f32`
    SunIntensity = 24,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum Camera {
    /// 'CameraProjectionMode'. Not implemented yet.
    ProjectionMode = 0,

    /// 'Vec3'
    ///
    /// Origin point in the local space of the `Camera` entity. The `Transform` component
    /// of the entity will be used to compute the world-space camera origin.
    LocalOrigin = 1,

    /// 'Vec3'
    ///
    /// Target point in the local space of the `Camera` entity. The `Transform` component
    /// of the entity will be used to compute the world-space camera target.
    #[deprecated(note = "Use `LocalForwardVector")] // 2021-11-22
    LocalTarget = 2,

    /// 'Vec3'
    ///
    /// Up vector in the local space of the `Camera` entity. The `Transform` component
    /// of the entity will be used to compute the world-space camera up vector.
    LocalUpVector = 4,

    /// 'f32'
    ///
    /// Specifies the vertical field of view angle, in degrees. Defaults to 60.0 degrees.
    FieldOfView = 3,

    /// 'f32'
    ///
    /// Specifies the focal point of the camera in normalized distance units.
    FocalPoint = 5,

    /// 'f32'
    ///
    /// Specifies the focal ratio of the camera.
    /// Setting this to zero disables depth of field completely.
    FStop = 6,

    /// 'Vec3'
    ///
    /// Forward vector in the local space of the `Camera` entity. The `Transform` component
    /// of the entity will be used to compute the world-space camera forward vector.
    LocalForwardVector = 7,

    /// 'Vec2'
    ///
    /// Specifies an offset in 2D clip space coordinates to shift the viewport by. Since it's clip space,
    /// the range is from -1 to 1, so (0, 0) is the default behavior while if you set this to (1, 0) the
    /// center of the projection will be at the right hand side of the screen. Useful for centering things
    /// under sidebars without using viewports, which are not supported.
    ScreenSpaceOffset = 8,

    /// 'f32'
    ///
    /// The amount of smoothing to apply to focal point changes in the range of 0 to 1.
    FocalPointSmoothing = 9,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum Bounds {
    /// 'Vec3'
    BoundingBoxMin = 0,

    /// 'Vec3'
    BoundingBoxMax = 1,

    /// 'f32'
    BoundingSphereRadius = 2,

    /// 'bool'
    /// Will keep the bounds matching to the render source on this entity. Will automatically point the shape property to the same shape as this entity's render component. Defaults 'true'.
    MatchRenderShape = 3,

    /// 'EntityHandle'
    /// Use to set a custom collision / physics / spatial query shape, will automatically point to this entity's render shape while MatchRenderShape = true.
    BoundingShape = 4,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum Tag {
    /// 'u64'
    Tag = 0,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum Layer {
    /// 'u64'
    Mask = 0,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum SdfModel {
    /// 'DataHandle'
    Program = 0,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum AudioSource {
    /// 'DataHandle'
    AudioClip = 0,

    /// 'f32'
    Volume = 1,

    /// 'f32'
    Rate = 2,

    /// 'bool'
    Looping = 3,

    /// 'bool'
    Force2d = 4,

    /// 'bool'
    AllowInterruption = 5,

    /// `AudioSourceOperation`
    NextOperation = 6,

    /// 'bool'
    StopOnDestroy = 7,

    /// 'PlayerIdSet'
    PlayerIdSet = 8,

    /// 'bool'
    IsPlaying = 9,

    /// 'DataHandle'
    DynamicModuleData = 10,
}

// TODO: More spatial queries, collision events, animate property, create/destroy entity/component
// Idea: Only allow direct set_property/get_property for entities your module has created, otherwise you have to send
// set property messages to them. Modules can choose to disallow foreign set properties and other messages?
// Need to add src/dst entity to messages to be able to filter and pass-on messages accordingly.
// Many of the messages will probably want a reply message also later, but we skip that for now.

#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct SpatialQueryCommon {
    pub id: u64, // uid for this request that you can use to check if your result matches the request
    pub max_entities: u32,

    // max_entities * sizeof(EntityHandle) needs to be allocated for the result. If the pointer is 0, no data is passed.
    // It's the responsibility of the module to keep this memory alive until the reply has arrived.
    pub result_data: u32,

    // Mask to say what layers you want to do the query against, !0u64 means all layers.
    pub layer_mask: u64,
}

// Center / origin will be the position of the entity.
// If you do mouse picking, the camera.
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct SpatialQueryRaycast {
    pub info: SpatialQueryCommon,
    pub direction: [f32; 3],
    pub min_t: f32,
    pub max_t: f32,
    pub _pad: u32,
}

#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct SpatialQueryWithinSphere {
    pub info: SpatialQueryCommon,
    pub radius: f32,
    pub _pad: u32,
}

#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct SpatialQueryBehindPlane {
    pub info: SpatialQueryCommon,
    pub normal: [f32; 3],
    pub _pad: u32,
}

#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
// A struct for now as we don't have any other payload
pub struct SpatialQueryResultPayload {
    pub raycast_hitpoint: [f32; 3], // Could save t-here, but as we don't have origin we would have to get the point anyway, so might as well save hit_point
}

#[deprecated(note = "Use `SpatialQueryHits` instead")] // 2021-09-24
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct SpatialQueryResult {
    pub id: u64,
    //pub entities: *const EntityHandle,
    pub num_entities: u64, // TODO: u64 as that was the size of EntityHandle here previously, we can change that
    pub raycast_result: SpatialQueryResultPayload,
    pub _pad: u32,
}

#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct SpatialQueryHits {
    pub id: u64,
    pub num_hits: u32,
    pub _pad: u32,
}

bitflags! {
    #[derive(Pod, Zeroable)]
    #[repr(C)]
    pub struct SpatialQueryOptions : u8 {
        #[deprecated] // 2021-10-05
        const APPLY_ENTITY_POSITION = 0b0000_0001;
        #[deprecated] // 2021-10-05
        const APPLY_ENTITY_ROTATION = 0b0000_0010;
        #[deprecated] // 2021-10-05
        const APPLY_ENTITY_SCALE = 0b0000_0100;
        #[deprecated] // 2021-10-05
        const APPLY_ENTITY_TRANSFORM = Self::APPLY_ENTITY_POSITION.bits() | Self::APPLY_ENTITY_ROTATION.bits() | Self::APPLY_ENTITY_SCALE.bits();
        #[deprecated] // 2021-10-05
        const FORCE_WORLD_SPACE_DIRECTION_OR_NORMAL = 0b000_1000;

        /// Faster, especially for raycasts, but only hits PhysX shapes, not exact meshes.
        const PHYSX_QUERY = 0b0001_0000;

        /// For raycasts, ignores any shapes we start within (t = min_t).
        const IGNORE_INTERIOR_HITS = 0b0010_0000;
    }
}

#[deprecated] // 2021-09-23
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct SpatialQueryCommon2 {
    pub id: u64, // uid for this request that you can use to check if your result matches the request
    pub max_hits: u32,

    /// Legacy messages: max_hits * sizeof(EntityHandle)
    /// New messages: max_hits * sizeof(SpatialQueryHit)
    ///
    /// needs to be allocated for the result. If the pointer is 0, no data is passed.
    /// It's the responsibility of the module to keep this memory alive until the reply has arrived.
    pub hits_data_ptr: u32,

    /// Mask to say what layers you want to do the query against, !0u64 means all layers.
    pub layer_mask: u64,

    /// Either a world position or a local-to-entity position depending on `options`
    pub position: [f32; 3],

    /// Options for the spatial query, such as whether to apply the transform of the entity to the ray.
    pub options: SpatialQueryOptions,
    pub _pad: [u8; 3],
}

/// Center / origin will be the position of the entity.
/// If you do mouse picking, the camera.
#[deprecated] // 2021-09-23
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct SpatialQueryRaycast2 {
    pub direction: [f32; 3],
    pub min_t: f32,
    pub max_t: f32,
    pub _pad: u32,
    pub common: SpatialQueryCommon2,
}

/// When `spherecast_radius = 0`, this is a raycast.
///
/// Center / origin will be the position of the entity.
/// If you do mouse picking, the camera.
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct SpatialQuerySpherecast {
    pub direction: [f32; 3],
    pub min_t: f32,
    pub max_t: f32,
    pub _pad0: u32,
    pub common: SpatialQueryCommon2,
    pub spherecast_radius: f32,
    pub _pad1: u32,
}

#[deprecated] // 2021-09-23
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct SpatialQueryWithinSphere2 {
    pub radius: f32,
    pub _pad: u32,
    pub common: SpatialQueryCommon2,
}

#[deprecated] // 2021-09-23
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct SpatialQueryBehindPlane2 {
    pub normal: [f32; 3],
    pub _pad: u32,
    pub common: SpatialQueryCommon2,
}

/// Information about a collision between two entities.
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct OnCollision {
    pub entity: EntityHandle,
    pub other: EntityHandle,

    /// Approximately where the collision happened. Useful for effects and sounds, not game logic.
    /// Currently this is calculated as the average position of the two entities, so it is VERY approximate.
    pub world_pos_approximate: [f32; 3],

    /// Placeholder for the collision force, not yet implemented.
    pub force: f32,
}

/// Information about an entity touching or leaving a trigger volume.
#[derive(Copy, Clone, Debug, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct OnTrigger {
    pub entity: EntityHandle,
    pub other: EntityHandle,

    pub event: TriggerEvent,
    pub _pad: u32,
}

crate::impl_checked_bit_pattern_for_transparent_pad!(OnTrigger);

#[derive(Copy, Clone, Debug, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct PhysicsForceRequest {
    pub force_type: ForceType,
    pub force_mode: ForceMode,
    pub vector: [f32; 3],
}

crate::impl_checked_bit_pattern_for_transparent_pad!(PhysicsForceRequest);

#[derive(Copy, Clone, Debug, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct PhysicsForceRequest2 {
    pub force_type: ForceType,
    pub force_mode: ForceMode,
    pub force_space: Space,
    pub pos_space: Space,
    pub vector: [f32; 3],
    pub pos: [f32; 3],
}

crate::impl_checked_bit_pattern_for_transparent_pad!(PhysicsForceRequest2);

#[derive(Copy, Clone, Debug, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct SetActive {
    pub component_type: ComponentType,
}

crate::impl_checked_bit_pattern_for_transparent_pad!(SetActive);

#[derive(Copy, Clone, Debug, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct SetActiveForPlayer {
    pub component_type: ComponentType,
    pub player_id: PlayerId,
}

crate::impl_checked_bit_pattern_for_transparent_pad!(SetActiveForPlayer);

#[derive(Copy, Clone, Debug, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct SetValue {
    pub component_type: ComponentType,
    pub param_id: u32,
    pub _pad: u32,
    pub value: Value,
}

crate::impl_checked_bit_pattern_for_transparent_pad!(SetValue);

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum AnimationCurve {
    Linear = 0,
    QuadraticEaseIn = 1,
    QuadraticEaseOut = 2,
    QuadraticEaseInOut = 3,
}

#[derive(Copy, Clone, Debug, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct AnimationOptions {
    pub curve: AnimationCurve,

    /// Specifies the duration (in seconds) of the animation.
    pub duration: f32,

    /// Specifies the amount of times the animation will loop before ending. A loop count of 0 is undefined.
    pub loop_count: u32,

    /// Specifies the delay (in seconds) until the animation starts.
    pub delay: f32,

    /// Specifies a smoothing constant, where 0 means no smoothing and >0 means more smoothing
    /// (will take longer time to reach the target).
    /// Can be handy to get smooth animation of cameras and other things without adding many
    /// keyframes and will also smoothen discontinuities when animations are cancelled.
    /// Defaults to 0 (no smoothing).
    pub smoothing: f32,
}

pub const ANIMATION_REQUEST_ID_NONE: u64 = !0u64;

#[derive(Copy, Clone, Debug, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct AnimateValue {
    pub id: u64, // Generate an id to be able to match this against a request. We let an id of `ANIMATION_REQUEST_ID_NONE` mean that we don't need a reply for now.
    pub param: SetValue,
    pub options: AnimationOptions,
    pub _pad: u32,
}

crate::impl_checked_bit_pattern_for_transparent_pad!(AnimateValue);

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum AnimationReplyType {
    AnimationFinished = 0,
    AnimationCancelled = 1,
}

#[derive(Copy, Clone, Debug, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct AnimationReply {
    pub reply_type: AnimationReplyType,
    pub _pad: u32,
    pub id: u64, // uid for this request that you can use to check if your result matches the request
}

crate::impl_checked_bit_pattern_for_transparent_pad!(AnimationReply);

#[derive(Copy, Clone, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct CollisionMatrixRow {
    pub index: u32, // can't use usize! differs between platforms.
    pub _pad: u32,
    pub row: u64,
}

crate::impl_checked_bit_pattern_for_transparent_pad!(CollisionMatrixRow);

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum MessageType {
    Invalid = 0,

    #[deprecated(note = "Use `SpatialQueryHits` instead")] // 2021-09-23
    SpatialQueryResult = 2, // Return message

    OnCollision = 3, // Return message
    PhysicsForceRequest = 4,
    SetValue = 5,
    AnimateValue = 6, // (animate any property using various modes and get a reply when the animation is finished)
    StopAnimatingValue = 7,
    AnimationReply = 8, // Return message
    PhysicsForceRequest2 = 10,

    SpatialQueryRaycast = 11,
    SpatialQueryWithinSphere = 12,
    SpatialQueryBehindPlane = 13,

    // Works like AnimateValue but will enqueue the animation instead of cancelling the current animation (if any).
    AnimateValueEnqueue = 14,
    SetCollisionMatrixRow = 15,
    //AddComponent
    //RemoveComponent
    //CreateEntity (will create a global entity if dst is invalid or create an entity with dst as parent)
    //DestroyEntity (will try to destroy the one it is sent to)
    SpatialQueryRaycast2 = 16,
    SpatialQueryWithinSphere2 = 17,
    SpatialQueryBehindPlane2 = 18,

    SetActive = 19,
    OnTrigger = 20, // Return message

    SetActiveForPlayer = 24,

    SpatialQuerySpherecast = 25,
    SpatialQueryHits = 26, // Return message
}

#[derive(Copy, Clone)]
#[repr(C)]
#[ark_api_macros::ffi_union(size = 72, checked_accessors)]
pub union MessagePayload {
    pub set_active: SetActive,
    pub set_active_for_player: SetActiveForPlayer,
    pub spatial_query_raycast: SpatialQueryRaycast,
    pub spatial_query_within_sphere: SpatialQueryWithinSphere,
    pub spatial_query_result: SpatialQueryResult,
    pub on_collision: OnCollision,
    pub on_trigger: OnTrigger,
    pub physics_force_request: PhysicsForceRequest,
    pub set_value: SetValue,
    pub animate_value: AnimateValue,
    pub stop_animating_value: SetValue, // If value is none, then means leave value as is
    pub animation_reply: AnimationReply,
    pub spatial_query_behind_plane: SpatialQueryBehindPlane,
    pub physics_force_request2: PhysicsForceRequest2,
    pub collision_matrix_row: CollisionMatrixRow,

    pub spatial_query_raycast2: SpatialQueryRaycast2,
    pub spatial_query_within_sphere2: SpatialQueryWithinSphere2,
    pub spatial_query_behind_plane2: SpatialQueryBehindPlane2,

    pub empty: u8,

    pub spatial_query_spherecast: SpatialQuerySpherecast,
    pub spatial_query_hits: SpatialQueryHits,
}

impl core::fmt::Debug for MessagePayload {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let bytes = bytemuck::cast_ref::<Self, [u8; 72]>(self);
        write!(f, "MessagePayload {{ <opaque>: {bytes:?} }}")
    }
}

#[derive(Copy, Clone, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct Message {
    pub msg_type: MessageType,
    pub _pad: u32,
    pub msg_payload: MessagePayload,
}

impl Message {
    pub fn invalid() -> Self {
        // zero the whole thing.
        // SAFETY: Safe because 0 is a valid value for the discriminant of `MessageType`
        // (corresponding to `MessageType::Invalid`), 0 is a valid value for the `_pad`, and
        // all 0s is a valid bit pattern for `msg_payload` since it is a union and therefore any
        // access of its fields is unsafe by default.
        let mut msg: Message = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
        msg.msg_type = MessageType::Invalid;
        msg
    }
}

impl std::fmt::Debug for Message {
    #[allow(clippy::unwrap_used)]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.msg_type {
            MessageType::SpatialQueryRaycast => write!(
                f,
                "SpatialQueryRaycast {:?}",
                self.msg_payload.as_spatial_query_raycast()
            ),
            MessageType::SpatialQueryWithinSphere => write!(
                f,
                "SpatialQueryWithinSphere {:?}",
                self.msg_payload.as_spatial_query_within_sphere()
            ),
            MessageType::SpatialQueryBehindPlane => write!(
                f,
                "SpatialQueryBehindPlane {:?}",
                self.msg_payload.as_spatial_query_behind_plane()
            ),
            MessageType::SpatialQueryResult => write!(
                f,
                "SpatialQueryResult {:?}",
                self.msg_payload.as_spatial_query_behind_plane()
            ),
            MessageType::OnCollision => {
                write!(f, "OnCollision {:?}", self.msg_payload.as_on_collision())
            }
            MessageType::OnTrigger => {
                write!(f, "OnTrigger {:?}", self.msg_payload.as_on_trigger())
            }
            MessageType::PhysicsForceRequest => write!(
                f,
                "PhysicsForceRequest {:?}",
                self.msg_payload.as_physics_force_request()
            ),
            MessageType::SetValue => {
                write!(f, "SetValue {:?}", self.msg_payload.as_set_value())
            }
            MessageType::AnimateValue => {
                write!(f, "AnimateValue {:?}", self.msg_payload.as_animate_value())
            }
            MessageType::AnimateValueEnqueue => write!(
                f,
                "AnimateValueEnqueue {:?}",
                self.msg_payload.as_animate_value()
            ),
            MessageType::StopAnimatingValue => write!(
                f,
                "StopAnimatingValue {:?}",
                self.msg_payload.as_stop_animating_value()
            ),
            MessageType::AnimationReply => write!(
                f,
                "AnimationReply {:?}",
                self.msg_payload.as_animation_reply()
            ),
            MessageType::PhysicsForceRequest2 => write!(
                f,
                "PhysicsForceRequest2 {:?}",
                self.msg_payload.as_physics_force_request2()
            ),
            MessageType::SetCollisionMatrixRow => {
                let matrix_row = self.msg_payload.as_collision_matrix_row();
                write!(
                    f,
                    "SetCollisionMatrixRow: [{}] = {}",
                    matrix_row.index, matrix_row.row,
                )
            }
            MessageType::SpatialQueryRaycast2 => write!(
                f,
                "SpatialQueryRaycast2 {:?}",
                self.msg_payload.as_spatial_query_raycast2()
            ),
            MessageType::SpatialQuerySpherecast => write!(
                f,
                "SpatialQuerySpherecast {:?}",
                self.msg_payload.as_spatial_query_spherecast()
            ),
            MessageType::SpatialQueryWithinSphere2 => write!(
                f,
                "SpatialQueryWithinSphere2 {:?}",
                self.msg_payload.as_spatial_query_within_sphere2()
            ),
            MessageType::SpatialQueryBehindPlane2 => write!(
                f,
                "SpatialQueryBehindPlane2 {:?}",
                self.msg_payload.as_spatial_query_behind_plane2()
            ),
            MessageType::SpatialQueryHits => write!(
                f,
                "SpatialQueryHits {:?}",
                self.msg_payload.as_spatial_query_hits()
            ),
            MessageType::SetActive => {
                write!(f, "SetActive {:?}", self.msg_payload.as_set_active())
            }
            MessageType::SetActiveForPlayer => write!(
                f,
                "SetActiveForPlayer {:?}",
                self.msg_payload.as_set_active_for_player()
            ),
            MessageType::Invalid => write!(f, "Invalid"),
        }
    }
}

/// Used with `add_force` / `add_force_at`.
#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
#[non_exhaustive]
pub enum ForceType {
    /// Force causes an entity to accelerate.
    Force = 0,
    /// Torque causes an angular acceleration of an entity around its axis.
    Torque = 1,
    // TODO: Remove this variant from the Ark public API
    /// Teleport an entity to another location. Use `position` instead.
    Teleport = 2,
}

/// Used with `add_force` / `add_force_at`.
#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
#[non_exhaustive]
pub enum ForceMode {
    /// Add a continuous force to the entity, using its mass.
    Force = 0,
    /// Add an instant force impulse to the entity, using its mass.
    Impulse = 1,
    /// Add an instant velocity change to the entity, ignoring its mass.
    VelocityChange = 2,
    /// Add a continuous acceleration to the entity, ignoring its mass.
    Acceleration = 3,
}

/// Used with `add_force` / `add_force_at`.
#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
pub enum Space {
    World = 0,
    Local = 1,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
#[non_exhaustive]
pub enum PhysicsShape {
    Sphere = 0,
    Box = 1,
    Capsule = 2,
    Mesh = 3,
    Compound = 4,
    ConvexMesh = 5, // Like Mesh but will bake a convex mesh and can be dynamic/kinematic
}

/// How to combine a physical property of two interacting physics bodies
/// to use in a calculation e.g. friction when sliding.
/// Property values are between bounded (0.0..=1.0)
/// NOTE: highest value combine mode specified on both bodies takes precedence
#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "with_speedy", derive(speedy::Writable, speedy::Readable))]
#[cfg_attr(feature = "with_arbitrary", derive(arbitrary::Arbitrary))]
#[repr(u32)]
#[non_exhaustive]
pub enum CombineMode {
    /// Use the average (mean) value of the two property values
    Average = 0,
    /// Use the smallest value of the two property values
    Min = 1,
    /// Use the multiplication of the two values property values
    Multiply = 2,
    /// Use the largest value of the two property values
    Max = 3,
}

#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
#[non_exhaustive]
pub enum MeshOrigin {
    Top = 0,
    Center = 1,
    Bottom = 2,
}

/// Not yet working.
#[derive(
    Copy,
    Clone,
    Debug,
    Hash,
    Eq,
    PartialEq,
    IntoPrimitive,
    TryFromPrimitive,
    NoUninit,
    CheckedBitPattern,
)]
#[repr(u32)]
#[non_exhaustive]
pub enum CameraProjectionMode {
    Orthographic = 0,
    Perspective = 1,
}