using MagicPhysX.Toolkit.Colliders; using MagicPhysX.Toolkit.Internal; using System.Numerics; using System.Runtime.CompilerServices; namespace MagicPhysX.Toolkit; // PxBase <- PxActor <- PxRigidActor <- PxRigidStatic // <- PxRidigBody <- PxArticulationLink // <- PxRigidDynamic public unsafe abstract class RigidActor { internal void* handler; protected Collider collider; public ref PxRigidActor RigidActorHandler => ref Unsafe.AsRef(handler); protected unsafe RigidActor(void* handler, Collider collider) { this.handler = handler; this.collider = collider; } public Collider GetComponent() => collider; public T GetComponent() where T : Collider { return (T)collider; } public Transform transform { get => RigidActorHandler.GetGlobalPose(); } } public unsafe class Rigidstatic : RigidActor { public ref PxRigidStatic RigidStatic => ref Unsafe.AsRef(handler); internal Rigidstatic(PxRigidStatic* handler, Collider collider) : base(handler, collider) { } } /// /// Control of an object's position through physics simulation. /// public unsafe class Rigidbody : RigidActor { PxScene* scene; public ref PxRigidDynamic RigidDynamic => ref Unsafe.AsRef(handler); public ref PxRigidBody RigidBody => ref Unsafe.AsRef(handler); public ref PxRigidActor RigidActor => ref Unsafe.AsRef(handler); public ref PxActor Actor => ref Unsafe.AsRef(handler); static readonly PxRigidDynamicLockFlags PxRigidDynamicLockFlagsLockAngular = PxRigidDynamicLockFlags.LockAngularX | PxRigidDynamicLockFlags.LockAngularY | PxRigidDynamicLockFlags.LockAngularZ; internal Rigidbody(PxRigidDynamic* handler, Collider collider, PxScene* scene) : base(handler, collider) { this.scene = scene; } /// /// The velocity vector of the rigidbody. It represents the rate of change of Rigidbody position. /// public Vector3 velocity { get => RigidDynamic.GetLinearVelocity(); set => RigidDynamic.SetLinearVelocityMut(value.AsPxPointer(), autowake: true); } /// /// The angular velocity vector of the rigidbody measured in radians per second. /// public Vector3 angularVelocity { get => RigidDynamic.GetAngularVelocity(); set => RigidDynamic.SetAngularVelocityMut(value.AsPxPointer(), autowake: true); } /// /// The drag of the object. /// public float drag { get => RigidBody.GetLinearDamping(); set => RigidBody.SetLinearDampingMut(value); } /// /// The angular drag of the object. /// public float angularDrag { get => RigidBody.GetAngularDamping(); set => RigidBody.SetAngularDampingMut(value); } /// /// The mass of the rigidbody. /// public float mass { get => RigidBody.GetMass(); set => RigidBody.SetMassMut(value); } /// /// Controls whether gravity affects this rigidbody. /// public bool useGravity { get => (Actor.GetActorFlags() & PxActorFlags.DisableGravity) == 0; set => Actor.SetActorFlagMut(PxActorFlag.DisableGravity, !value); } /// /// Maximum velocity of a rigidbody when moving out of penetrating state. /// public float maxDepenetrationVelocity { get => RigidBody.GetMaxDepenetrationVelocity(); set => RigidBody.SetMaxDepenetrationVelocityMut(value); } /// /// Controls whether physics affects the rigidbody. /// public bool isKinematic { get => (RigidBody.GetRigidBodyFlags() & PxRigidBodyFlags.Kinematic) == PxRigidBodyFlags.Kinematic; set => RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.Kinematic, value); } /// /// Controls whether physics will change the rotation of the object. /// public bool freezeRotation { get => (RigidDynamic.GetRigidDynamicLockFlags() & PxRigidDynamicLockFlagsLockAngular) == PxRigidDynamicLockFlagsLockAngular; set => RigidDynamic.SetRigidDynamicLockFlagsMut(value ? RigidDynamic.GetRigidDynamicLockFlags() | PxRigidDynamicLockFlagsLockAngular : RigidDynamic.GetRigidDynamicLockFlags() & ~PxRigidDynamicLockFlagsLockAngular); } /// /// Controls which degrees of freedom are allowed for the simulation of this Rigidbody. /// public RigidbodyConstraints constraints { get => RigidDynamic.GetRigidDynamicLockFlags().AsRigidbodyConstraints(); set => RigidDynamic.SetRigidDynamicLockFlagsMut(constraints.AsPxRigidDynamicLockFlags()); } /// /// The Rigidbody's collision detection mode. /// public CollisionDetectionMode collisionDetectionMode { get { var flags = RigidBody.GetRigidBodyFlags(); if ((flags & PxRigidBodyFlags.EnableSpeculativeCcd) == PxRigidBodyFlags.EnableSpeculativeCcd) { return CollisionDetectionMode.ContinuousSpeculative; } if ((flags & PxRigidBodyFlags.EnableCcd) == PxRigidBodyFlags.EnableCcd) { return CollisionDetectionMode.ContinuousDynamic; } return CollisionDetectionMode.Discrete; } set { switch (value) { case CollisionDetectionMode.ContinuousDynamic: RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.EnableCcd, true); RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.EnableSpeculativeCcd, false); break; case CollisionDetectionMode.ContinuousSpeculative: RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.EnableCcd, false); RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.EnableSpeculativeCcd, true); break; case CollisionDetectionMode.Continuous: // ??? break; case CollisionDetectionMode.Discrete: RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.EnableCcd, false); RigidBody.SetRigidBodyFlagMut(PxRigidBodyFlag.EnableSpeculativeCcd, false); break; } } } /// /// The center of mass relative to the transform's origin. /// public Vector3 centerOfMass { get => RigidBody.GetCMassLocalPose().p; set { var pose = RigidBody.GetCMassLocalPose(); pose.p = value; RigidBody.SetCMassLocalPoseMut(&pose); } } /// /// The center of mass of the rigidbody in world space (Read Only). /// public Vector3 worldCenterOfMass { get { var globalPose = RigidActor.GetGlobalPose(); return globalPose.Transform(centerOfMass.AsPxPointer()); } } /// /// The inertia tensor of this body, defined as a diagonal matrix in a reference frame positioned at this body's center of mass and rotated by Rigidbody.inertiaTensorRotation. /// public Vector3 inertiaTensor { get => RigidBody.GetMassSpaceInertiaTensor(); set => RigidBody.SetMassSpaceInertiaTensorMut(value.AsPxPointer()); } /// /// Should collision detection be enabled? (By default always enabled). /// public bool detectCollisions { get { var num = RigidActor.GetNbShapes(); if (num > 0) { PxShape*[] shapes = new PxShape*[num]; fixed (PxShape** p = shapes) { RigidActor.GetShapes(p, num, 0); ref PxShape shape = ref Unsafe.AsRef(shapes[0]); return (shape.GetFlags() & PxShapeFlags.SimulationShape) == PxShapeFlags.SimulationShape; } } return false; } set { var num = RigidActor.GetNbShapes(); if (num > 0) { PxShape*[] shapes = new PxShape*[num]; fixed (PxShape** p = shapes) { RigidActor.GetShapes(p, num, 0); for (var i = 0; i < num; i++) { ref PxShape shape = ref Unsafe.AsRef(shapes[i]); shape.SetFlagMut(PxShapeFlag.SimulationShape, value); } } } } } /// /// The position of the rigidbody. /// public Vector3 position { get => RigidActor.GetGlobalPose().p; set { var pose = RigidActor.GetGlobalPose(); pose.p = value; RigidActor.SetGlobalPoseMut(&pose, autowake: true); } } /// /// The rotation of the Rigidbody. /// public Quaternion rotation { get => RigidActor.GetGlobalPose().q; set { var pose = RigidActor.GetGlobalPose(); pose.q = value; RigidActor.SetGlobalPoseMut(&pose, autowake: true); } } /// /// The solverIterations determines how accurately Rigidbody joints and collision contacts are resolved. Overrides Physics.defaultSolverIterations. Must be positive. /// public int solverIterations { get { uint minPositionIters; uint* p = &minPositionIters; uint minVelocityIters; uint* _ = &minVelocityIters; RigidDynamic.GetSolverIterationCounts(p, _); return (int)*p; } set => RigidDynamic.SetSolverIterationCountsMut((uint)value, (uint)solverVelocityIterations); } /// /// The mass-normalized energy threshold, below which objects start going to sleep. /// public float sleepThreshold { get => RigidDynamic.GetSleepThreshold(); set => RigidDynamic.SetSleepThresholdMut(value); } /// /// The maximimum angular velocity of the rigidbody measured in radians per second. (Default 7) range { 0, infinity }. /// public float maxAngularVelocity { get => RigidBody.GetMaxAngularVelocity(); set => RigidBody.SetMaxAngularVelocityMut(value); } /// /// The solverVelocityIterations affects how how accurately Rigidbody joints and collision contacts are resolved. Overrides Physics.defaultSolverVelocityIterations. Must be positive. /// public int solverVelocityIterations { get { uint minPositionIters; uint* _ = &minPositionIters; uint minVelocityIters; uint* v = &minVelocityIters; RigidDynamic.GetSolverIterationCounts(_, v); return (int)*v; } set => RigidDynamic.SetSolverIterationCountsMut((uint)solverIterations, (uint)value); } /// /// Sets the mass based on the attached colliders assuming a constant density. /// /// public void SetDensity(float density) { RigidBody.ExtUpdateMassAndInertia(&density, 1, null, false); } /// /// Forces a rigidbody to sleep at least one frame. /// public void Sleep() { RigidDynamic.PutToSleepMut(); } /// /// Is the rigidbody sleeping? /// public bool IsSleeping() { return RigidDynamic.IsSleeping(); } /// /// Forces a rigidbody to wake up. /// public void WakeUp() { RigidDynamic.WakeUpMut(); } /// /// Reset the center of mass of the rigidbody. /// public void ResetCenterOfMass() { centerOfMass = Vector3.Zero; } /// /// Reset the inertia tensor value and rotation. /// public void ResetInertiaTensor() { inertiaTensor = Vector3.Zero; } /// /// Adds a force to the Rigidbody. /// /// Force vector in world coordinates. /// Type of force to apply. public void AddForce(Vector3 force, ForceMode mode) { RigidBody.AddForceMut(force.AsPxPointer(), (PxForceMode)mode, autowake: true); } /// /// Adds a force to the Rigidbody. /// /// Force vector in world coordinates. public void AddForce(Vector3 force) { RigidBody.AddForceMut(force.AsPxPointer(), PxForceMode.Force, autowake: true); } public void AddForce(float x, float y, float z, ForceMode mode) { AddForce(new Vector3(x, y, z), mode); } public void AddForce(float x, float y, float z) { AddForce(new Vector3(x, y, z)); } /// /// Adds a force to the rigidbody relative to its coordinate system. /// /// Force vector in local coordinates. /// Type of force to apply. public void AddRelativeForce(Vector3 force, ForceMode mode) { var globalPose = RigidActor.GetGlobalPose(); var globalForce = globalPose.Transform(force.AsPxPointer()); RigidBody.AddForceMut(&globalForce, (PxForceMode)mode, autowake: true); } /// /// Adds a force to the rigidbody relative to its coordinate system. /// /// Force vector in local coordinates. public void AddRelativeForce(Vector3 force) { AddRelativeForce(force, ForceMode.Force); } public void AddRelativeForce(float x, float y, float z, ForceMode mode) { AddRelativeForce(new Vector3(x, y, z), mode); } public void AddRelativeForce(float x, float y, float z) { AddRelativeForce(new Vector3(x, y, z)); } /// /// Adds a torque to the rigidbody. /// /// Torque vector in world coordinates. /// The type of torque to apply. public void AddTorque(Vector3 torque, ForceMode mode) { RigidBody.AddTorqueMut(torque.AsPxPointer(), (PxForceMode)mode, autowake: true); } /// /// Adds a torque to the rigidbody. /// /// Torque vector in world coordinates. public void AddTorque(Vector3 torque) { RigidBody.AddTorqueMut(torque.AsPxPointer(), PxForceMode.Force, autowake: true); } public void AddTorque(float x, float y, float z, ForceMode mode) { AddTorque(new Vector3(x, y, z), mode); } public void AddTorque(float x, float y, float z) { AddTorque(new Vector3(x, y, z)); } /// /// Adds a torque to the rigidbody relative to its coordinate system. /// /// Torque vector in local coordinates. /// Type of force to apply. public void AddRelativeTorque(Vector3 torque, ForceMode mode) { var globalPose = RigidActor.GetGlobalPose(); var globalToque = globalPose.Transform(torque.AsPxPointer()); RigidBody.AddTorqueMut(&globalToque, (PxForceMode)mode, autowake: true); } /// /// Adds a torque to the rigidbody relative to its coordinate system. /// /// Torque vector in local coordinates. public void AddRelativeTorque(Vector3 torque) { AddRelativeTorque(torque, ForceMode.Force); } public void AddRelativeTorque(float x, float y, float z, ForceMode mode) { AddRelativeTorque(new Vector3(x, y, z), mode); } public void AddRelativeTorque(float x, float y, float z) { AddRelativeTorque(new Vector3(x, y, z)); } /// /// Applies force at position. As a result this will apply a torque and force on the object. /// /// Force vector in world coordinates. /// Position in world coordinates. /// Type of force to apply. public void AddForceAtPosition(Vector3 force, Vector3 position, ForceMode mode) { RigidBody.ExtAddForceAtPos(force.AsPxPointer(), position.AsPxPointer(), (PxForceMode)mode, wakeup: true); } /// /// Applies force at position. As a result this will apply a torque and force on the object. /// /// Force vector in world coordinates. /// Position in world coordinates. public void AddForceAtPosition(Vector3 force, Vector3 position) { RigidBody.ExtAddForceAtPos(force.AsPxPointer(), position.AsPxPointer(), PxForceMode.Force, wakeup: true); } }