| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- using System;
- using System.Collections.Generic;
- namespace BansheeEngine
- {
- /** @addtogroup Physics
- * @{
- */
- /// <summary>
- /// Rigidbody is a dynamic physics object that can be moved using forces (or directly). It will interact with other
- /// static and dynamic physics objects in the scene accordingly (it will push other non-kinematic rigidbodies,
- /// and collide with static objects).
- ///
- /// The shape and mass of a rigidbody is governed by its colliders. You must attach at least one collider for the
- /// rigidbody to be valid. Colliders that are on the same scene object as the rigidbody, or on child scene objects
- /// are automatically considered as part of the rigidbody.
- /// </summary>
- public sealed class Rigidbody : Component
- {
- internal NativeRigidbody native;
- private List<Collider> children = new List<Collider>();
- private Joint parentJoint;
- [SerializeField]
- internal SerializableData serializableData = new SerializableData();
- /// <summary>
- /// Triggered when some object starts interacting with one of the child colliders. Only triggered if proper
- /// collision report mode is turned on.
- /// </summary>
- public event Action<CollisionData> OnCollisionBegin;
- /// <summary>
- /// Triggered for every frame that an object remains interacting with one of the child colliders. Only triggered if
- /// proper collision report mode is turned on.
- /// </summary>
- public event Action<CollisionData> OnCollisionStay;
- /// <summary>
- /// Triggered when some object stops interacting with one of the child colliders. Only triggered if proper collision
- /// report mode is turned on.
- /// </summary>
- public event Action<CollisionData> OnCollisionEnd;
- /// <summary>
- /// Determines the mass of the object and all of its collider shapes. Only relevant if RigidbodyFlag.AutoMass or
- /// RigidbodyFlag.AutoTensors is turned off. Value of zero means the object is immovable (but can be rotated).
- /// </summary>
- public float Mass
- {
- get { return serializableData.mass; }
- set
- {
- serializableData.mass = value;
- if (native != null)
- native.Mass = value;
- }
- }
- /// <summary>
- /// Determines if the body is kinematic. Kinematic body will not move in response to external forces (for example
- /// gravity, or another object pushing it), essentially behaving like collider. Unlike a collider though, you can
- /// still move the object and have other dynamic objects respond correctly (that is, it will push other objects).
- /// </summary>
- public bool Kinematic
- {
- get { return serializableData.isKinematic; }
- set
- {
- if (serializableData.isKinematic == value)
- return;
- serializableData.isKinematic = value;
- if (native != null)
- {
- native.Kinematic = value;
- ClearColliders();
- UpdateColliders();
- }
- }
- }
- /// <summary>
- /// Determines if the body is sleeping. Objects that aren't moved/rotated for a while are put to sleep to reduce
- /// load on the physics system. You may also manually force objects to sleep or wake up.
- /// </summary>
- public bool Sleeping
- {
- get
- {
- if (native != null)
- return native.Sleeping;
- return true;
- }
- set
- {
- if (native != null)
- native.Sleeping = value;
- }
- }
- /// <summary>
- /// Threshold of force and torque under which the object will be considered to be put to sleep.
- /// </summary>
- public float SleepThreshold
- {
- get { return serializableData.sleepThreshold; }
- set
- {
- serializableData.sleepThreshold = value;
- if (native != null)
- native.SleepThreshold = value;
- }
- }
- /// <summary>
- /// Determines whether or not the rigidbody will have the global gravity force applied to it.
- /// </summary>
- public bool UseGravity
- {
- get { return serializableData.useGravity; }
- set
- {
- serializableData.useGravity = value;
- if (native != null)
- native.UseGravity = value;
- }
- }
- /// <summary>
- /// Determines current linear velocity of the body.
- /// </summary>
- public Vector3 Velocity
- {
- get
- {
- if (native != null)
- return native.Velocity;
- return Vector3.Zero;
- }
- set
- {
- if (native != null)
- native.Velocity = value;
- }
- }
- /// <summary>
- /// Determines current angular velocity of the body.
- /// </summary>
- public Vector3 AngularVelocity
- {
- get
- {
- if (native != null)
- return native.AngularVelocity;
- return Vector3.Zero;
- }
- set
- {
- if (native != null)
- native.AngularVelocity = value;
- }
- }
- /// <summary>
- /// Determines linear drag of the body. Higher drag values means the object resists linear movement more.
- /// </summary>
- public float Drag
- {
- get { return serializableData.linearDrag; }
- set
- {
- serializableData.linearDrag = value;
- if (native != null)
- native.Drag = value;
- }
- }
- /// <summary>
- /// Determines angular drag of the body. Higher drag values means the object resists angular movement more.
- /// </summary>
- public float AngularDrag
- {
- get { return serializableData.angularDrag; }
- set
- {
- serializableData.angularDrag = value;
- if (native != null)
- native.AngularDrag = value;
- }
- }
- /// <summary>
- /// Sets the inertia tensor in local mass space. Inertia tensor determines how difficult is to rotate the object.
- /// Values of zero in the inertia tensor mean the object will be unable to rotate around a specific axis. Changing
- /// this value is only relevant if RigidbodyFlag.AutoTensors is turned off.
- /// </summary>
- public Vector3 InertiaTensor
- {
- get { return serializableData.inertiaTensor; }
- set
- {
- serializableData.inertiaTensor = value;
- if (native != null)
- native.InertiaTensor = value;
- }
- }
- /// <summary>
- /// Determines position of the center of the mass. Changing this value is only relevant if RigidbodyFlag.AutoTensors
- /// is turned off.
- /// </summary>
- public Vector3 CenterOfMassPosition
- {
- get { return serializableData.centerMassPosition; }
- set
- {
- serializableData.centerMassPosition = value;
- if (native != null)
- native.CenterOfMassPosition = value;
- }
- }
- /// <summary>
- /// Determines rotation of the inertia tensor (rotation of the center of mass frame). Changing this value is only
- /// relevant if RigidbodyFlag.AutoTensors is turned off.
- /// </summary>
- public Quaternion CenterOfMassRotation
- {
- get { return serializableData.centerMassRotation; }
- set
- {
- serializableData.centerMassRotation = value;
- if (native != null)
- native.CenterOfMassRotation = value;
- }
- }
- /// <summary>
- /// Determines maximum angular velocity of the rigidbody. Velocity will be clamped to this value.
- /// </summary>
- public float MaxAngularVelocity
- {
- get { return serializableData.maxAngularVelocity; }
- set
- {
- serializableData.maxAngularVelocity = value;
- if (native != null)
- native.MaxAngularVelocity = value;
- }
- }
- /// <summary>
- /// Determines number of iterations to use when solving for position. Higher values can improve precision and
- /// numerical stability of the simulation.
- /// </summary>
- public int PositionSolverCount
- {
- get { return serializableData.positionSolverCount; }
- set
- {
- serializableData.positionSolverCount = value;
- if (native != null)
- serializableData.positionSolverCount = value;
- }
- }
- /// <summary>
- /// Determines number of iterations to use when solving for velocity. Higher values can improve precision and
- /// numerical stability of the simulation.
- /// </summary>
- public int VelocitySolverCount
- {
- get { return serializableData.velocitySolverCount; }
- set
- {
- serializableData.velocitySolverCount = value;
- if (native != null)
- serializableData.velocitySolverCount = value;
- }
- }
- /// <summary>
- /// Determines which (if any) collision events are reported.
- /// </summary>
- public CollisionReportMode CollisionReportMode
- {
- get { return serializableData.collisionReportMode; }
- set
- {
- if (serializableData.collisionReportMode == value)
- return;
- serializableData.collisionReportMode = value;
- foreach (var entry in children)
- entry.UpdateCollisionReportMode();
- }
- }
- /// <summary>
- /// Various flags that control the behaviour of the rigidbody.
- /// </summary>
- public RigidbodyFlag Flags
- {
- get { return serializableData.flags; }
- set
- {
- if (serializableData.flags == value)
- return;
- serializableData.flags = value;
- if (native != null)
- {
- native.Flags = value;
- native.UpdateMassDistribution();
- }
- }
- }
- /// <summary>
- /// Moves the rigidbody to a specific position. This method will ensure physically correct movement, meaning the
- /// body will collide with other objects along the way.
- /// </summary>
- /// <param name="position">New position for the body, in world space.</param>
- public void Move(Vector3 position)
- {
- if (native != null)
- native.Move(position);
- NotifyFlags = TransformChangedFlags.None;
- SceneObject.Position = position;
- NotifyFlags = TransformChangedFlags.Transform | TransformChangedFlags.Parent;
- }
- /// <summary>
- /// Rotates the rigidbody. This method will ensure physically correct rotation, meaning the body will collide with
- /// other objects along the way.
- /// </summary>
- /// <param name="rotation">New orientation of the body, in world space.</param>
- public void Rotate(Quaternion rotation)
- {
- if (native != null)
- native.Rotate(rotation);
- NotifyFlags = TransformChangedFlags.None;
- SceneObject.Rotation = rotation;
- NotifyFlags = TransformChangedFlags.Transform | TransformChangedFlags.Parent;
- }
- /// <summary>
- /// Applies a force to the center of the mass of the rigidbody. This will produce linear momentum.
- /// </summary>
- /// <param name="force">Force to apply.</param>
- /// <param name="mode">Determines what type of force was applied.</param>
- public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force)
- {
- if (native != null)
- native.AddForce(force, mode);
- }
- /// <summary>
- /// Applies a torque to the rigidbody. This will produce angular momentum.
- /// </summary>
- /// <param name="torque">Torque to apply.</param>
- /// <param name="mode">Determines what type of torque was applied.</param>
- public void AddTorque(Vector3 torque, ForceMode mode = ForceMode.Force)
- {
- if (native != null)
- native.AddTorque(torque, mode);
- }
- /// <summary>
- /// Applies a force to a specific point on the rigidbody. This will in most cases produce both linear and angular
- /// momentum.
- /// </summary>
- /// <param name="force">Force to apply.</param>
- /// <param name="position">World space point to apply the force at.</param>
- /// <param name="mode">Determines what type of force was applied.</param>
- public void AddForceAtPoint(Vector3 force, Vector3 position, PointForceMode mode = PointForceMode.Force)
- {
- if (native != null)
- native.AddForceAtPoint(force, position, mode);
- }
- /// <summary>
- /// Returns the total (linear + angular) velocity at a specific point.
- /// </summary>
- /// <param name="position">Point in world space.</param>
- /// <returns>Total velocity of the point.</returns>
- public Vector3 GetVelocityAtPoint(Vector3 position)
- {
- if (native != null)
- return native.GetVelocityAtPoint(position);
- return position;
- }
- /// <summary>
- /// Triggered when one of the child colliders begins touching another object.
- /// </summary>
- /// <param name="data">Data about the collision.</param>
- internal void DoOnCollisionBegin(CollisionData data)
- {
- if (OnCollisionBegin != null)
- OnCollisionBegin(data);
- }
- /// <summary>
- /// Triggered when one of the child colliders ends touching another object.
- /// </summary>
- /// <param name="data">Data about the collision.</param>
- internal void DoOnCollisionStay(CollisionData data)
- {
- if (OnCollisionStay != null)
- OnCollisionStay(data);
- }
- /// <summary>
- /// Triggered when one of the child colliders ends touching another object.
- /// </summary>
- /// <param name="data">Data about the collision.</param>
- internal void DoOnCollisionEnd(CollisionData data)
- {
- if (OnCollisionEnd != null)
- OnCollisionEnd(data);
- }
- /// <summary>
- /// Sets that joint that this rigidbody is attached to. Allows the rigidbody to notify the joint when it moves.
- /// </summary>
- /// <param name="joint">Joint the rigidbody is attached to, or null if none.</param>
- internal void SetJoint(Joint joint)
- {
- parentJoint = joint;
- }
- /// <summary>
- /// Recalculates rigidbody's mass, inertia tensors and center of mass depending on the currently set child
- /// colliders. This should be called whenever relevant child collider properties change(like mass or shape).
- ///
- /// If automatic tensor calculation is turned off then this will do nothing. If automatic mass calculation is turned
- /// off then this will use the mass set directly on the body using <see cref="Mass"/>.
- /// </summary>
- internal void UpdateMassDistribution()
- {
- if (native != null)
- native.UpdateMassDistribution();
- }
- /// <summary>
- /// Unregisters all child colliders from the Rigidbody.
- /// </summary>
- internal void ClearColliders()
- {
- foreach (var collider in children)
- collider.SetRigidbody(null, true);
- children.Clear();
- if (native != null)
- native.RemoveColliders();
- }
- /// <summary>
- /// Registers a new collider with the Rigidbody. This collider will then be used to calculate Rigidbody's geometry
- /// used for collisions, and optionally (depending on set flags) total mass, inertia tensors and center of mass.
- /// </summary>
- /// <param name="collider">Collider to register.</param>
- internal void AddCollider(Collider collider)
- {
- if (native == null)
- return;
- children.Add(collider);
- native.AddCollider(collider);
- }
- /// <summary>
- /// Unregisters the collider from the Rigidbody.
- /// </summary>
- /// <param name="collider">Collider to unregister.</param>
- internal void RemoveCollider(Collider collider)
- {
- if (native == null)
- return;
- if (children.Exists(x => x == collider))
- {
- native.RemoveCollider(collider);
- children.Remove(collider);
- }
- }
- private void OnInitialize()
- {
- NotifyFlags = TransformChangedFlags.Transform | TransformChangedFlags.Parent;
- }
- private void OnEnable()
- {
- RestoreNative();
- }
- private void OnDisable()
- {
- DestroyNative();
- }
- private void OnDestroy()
- {
- DestroyNative();
- }
- private void OnTransformChanged(TransformChangedFlags flags)
- {
- if (!SceneObject.Active)
- return;
- if ((flags & TransformChangedFlags.Parent) != 0)
- {
- ClearColliders();
- UpdateColliders();
- if ((serializableData.flags & RigidbodyFlag.AutoTensors) != 0)
- native.UpdateMassDistribution();
- #if DEBUG
- CheckForNestedRigibody();
- #endif
- }
- native.Position = SceneObject.Position;
- native.Rotation = SceneObject.Rotation;
- if (parentJoint != null)
- parentJoint.NotifyRigidbodyMoved(this);
- }
- /// <summary>
- /// Searches child scene objects for Collider components and attaches them to the rigidbody. Make sure to call
- /// <see cref="ClearColliders"/> if you need to clear old colliders first.
- /// </summary>
- private void UpdateColliders()
- {
- Stack<SceneObject> todo = new Stack<SceneObject>();
- todo.Push(SceneObject);
- while (todo.Count > 0)
- {
- SceneObject currentSO = todo.Pop();
- if (currentSO.GetComponent<Collider>() != null)
- {
- Collider[] colliders = currentSO.GetComponents<Collider>();
- foreach (var entry in colliders)
- {
- if (!entry.IsValidParent(this))
- continue;
- if (entry.native == null)
- continue;
- entry.SetRigidbody(this, true);
- entry.native.Rigidbody = native;
- children.Add(entry);
- native.AddCollider(entry);
- }
- }
- int childCount = currentSO.GetNumChildren();
- for (int i = 0; i < childCount; i++)
- {
- SceneObject child = currentSO.GetChild(i);
- if (child.GetComponent<Rigidbody>() != null)
- continue;
- todo.Push(child);
- }
- }
- }
- /// <summary>
- /// Checks if the rigidbody is nested under another rigidbody, and throws out a warning if so.
- /// </summary>
- private void CheckForNestedRigibody()
- {
- SceneObject currentSO = SceneObject.Parent;
- while (currentSO != null)
- {
- if (currentSO.GetComponent<Rigidbody>() != null)
- {
- Debug.LogWarning("Nested Rigidbodies detected. This will result in inconsistent transformations. " +
- "To parent one Rigidbody to another move its colliders to the new parent, but remove the " +
- "Rigidbody component.");
- return;
- }
- currentSO = currentSO.Parent;
- }
- }
- /// <summary>
- /// Destroys the internal rigidbody representation.
- /// </summary>
- private void DestroyNative()
- {
- ClearColliders();
- if (native != null)
- {
- native.Destroy();
- native = null;
- }
- }
- /// <summary>
- /// Restores internal rigidbody representation and assigns it the properties stored by the component.
- /// </summary>
- private void RestoreNative()
- {
- native = new NativeRigidbody(SceneObject);
- native.Component = this;
- UpdateColliders();
- #if DEBUG
- CheckForNestedRigibody();
- #endif
- native.Position = SceneObject.Position;
- native.Rotation = SceneObject.Rotation;
- // Note: Merge into one call to avoid many virtual function calls
- native.PositionSolverCount = serializableData.positionSolverCount;
- native.VelocitySolverCount = serializableData.velocitySolverCount;
- native.MaxAngularVelocity = serializableData.maxAngularVelocity;
- native.Drag = serializableData.linearDrag;;
- native.AngularDrag = serializableData.angularDrag;
- native.SleepThreshold = serializableData.sleepThreshold;
- native.UseGravity = serializableData.useGravity;
- native.Kinematic = serializableData.isKinematic;
- native.Flags = serializableData.flags;
- if ((serializableData.flags & RigidbodyFlag.AutoTensors) == 0)
- {
- native.CenterOfMassPosition = serializableData.centerMassPosition;
- native.CenterOfMassRotation = serializableData.centerMassRotation;
- native.InertiaTensor = serializableData.inertiaTensor;
- native.Mass = serializableData.mass;
- }
- else
- {
- if ((serializableData.flags & RigidbodyFlag.AutoMass) == 0)
- native.Mass = serializableData.mass;
- native.UpdateMassDistribution();
- }
- }
- /// <summary>
- /// Holds all data the rigidbody component needs to persist through serialization.
- /// </summary>
- [SerializeObject]
- internal class SerializableData
- {
- public int positionSolverCount = 4;
- public int velocitySolverCount = 1;
- public RigidbodyFlag flags = RigidbodyFlag.AutoTensors | RigidbodyFlag.AutoMass;
- public CollisionReportMode collisionReportMode = CollisionReportMode.None;
- public Vector3 centerMassPosition = Vector3.Zero;
- public Quaternion centerMassRotation = Quaternion.Identity;
- public Vector3 inertiaTensor = Vector3.Zero;
- public float mass = 0.0f;
- public float maxAngularVelocity = 1.0f;
- public float linearDrag = 0.0f;
- public float angularDrag = 0.0f;
- public float sleepThreshold = 0.0f;
- public bool useGravity = true;
- public bool isKinematic = false;
- }
- }
- /// <summary>
- /// Type of force or torque that can be applied to a rigidbody.
- /// </summary>
- public enum ForceMode
- {
- /// <summary>
- /// Value applied is a force.
- /// </summary>
- Force,
- /// <summary>
- /// Value applied is an impulse (a direct change in its linear or angular momentum).
- /// </summary>
- Impulse,
- /// <summary>
- /// Value applied is velocity.
- /// </summary>
- Velocity,
- /// <summary>
- /// Value applied is accelearation.
- /// </summary>
- Acceleration
- }
- /// <summary>
- /// Type of force that can be applied to a rigidbody at an arbitrary point.
- /// </summary>
- public enum PointForceMode
- {
- /// <summary>
- /// Value applied is a force.
- /// </summary>
- Force,
- /// <summary>
- /// Value applied is an impulse (a direct change in its linear or angular momentum).
- /// </summary>
- Impulse,
- }
- /// <summary>
- /// Flags that control options of a Rigidbody object.
- /// </summary>
- [Flags]
- public enum RigidbodyFlag
- {
- /// <summary>
- /// No options.
- /// </summary>
- None = 0x00,
- /// <summary>
- /// Automatically calculate center of mass transform and inertia tensors from child shapes (colliders)
- /// </summary>
- AutoTensors = 0x01,
- /// <summary>
- /// Calculate mass distribution from child shapes (colliders). Only relevant when auto-tensors is on.
- /// </summary>
- AutoMass = 0x02,
- /// <summary>
- /// Enables continous collision detection. This can prevent fast moving bodies from tunneling through each other.
- /// This must also be enabled globally in Physics otherwise the flag will be ignored.
- /// </summary>
- CCD = 0x04
- }
- /** @} */
- }
|