//--------------------------------------------------------------------------------------------------------------------------------------------------- // // Copyright (C)2007 DarkWynter Studios. All rights reserved. // //--------------------------------------------------------------------------------------------------------------------------------------------------- // {Contact : darkwynter.com for licensing information //--------------------------------------------------------------------------------------------------------------------------------------------------- #define HACK_MOVEMENT namespace DarkWynter.Engine.Physics { #region Using Statements using System; using Microsoft.Xna.Framework; using Globals; using GameObjects; using Utilities; // Logging... #endregion /// /// Mass object associated with all Game Objects /// public class Mass { /// /// Pointer from this mass back up to the game object associated with it /// public GameObject gameObjectPointer; // GPU UPDATE VARIABLES ====================================================================== /// /// The objects center location /// public Vector3 _currentPosition = new Vector3(); public Vector3 currentPosition { get { return _currentPosition; } set { _currentPosition = value; } } /// /// Needed for updating the lookup map /// public Vector3 _lastPosition = new Vector3(); public Vector3 lastPosition { get { return _lastPosition; } set { _lastPosition = value; } } // Orientation /// /// Stores current rotation information /// public Quaternion _currentRotation = new Quaternion(); public Quaternion currentRotation { get { return _currentRotation; } set { _currentRotation = value; } } /// /// Straight ahead - normalized /// public Vector3 _normalVector = new Vector3(); public Vector3 normalVector { get { return _normalVector; } set { _normalVector = value; } } /// /// Straight up - normalized /// public Vector3 _upVector = new Vector3(); public Vector3 upVector { get { return _upVector; } set { _upVector = value; } } /// /// Straight to the right - normalized /// public Vector3 _perpVector = new Vector3(); public Vector3 perpVector { get { return _perpVector; } set { _perpVector = value; } } // Motion /// /// The object's current acceleration /// public Vector3 _acceleration = new Vector3(); public Vector3 acceleration { get { return _acceleration; } set { _acceleration = value; } } /// /// Un-Normalized Vector in the direction of object movement /// public Vector3 _velocity = new Vector3(); public Vector3 velocity { get { return _velocity; } set { _velocity = value; } } /// /// Used to sum the forces on a GameObject during Update /// public Vector3 _totalForce = new Vector3(); public Vector3 totalForce { get { return _totalForce; } set { _totalForce = value; } } /// /// Model Scale Factor /// public Vector3 _scale = new Vector3(1.0f); public Vector3 scale { get { return _scale; } set { _scale = value; } } /// /// Object height above the terrain. Based on model properties /// public float _objectHeight; public float objectHeight { get { return _objectHeight; } set { _objectHeight = value; } } // Energy /// /// Initial Fire energy given to player /// public float _energy = 0.0f; public float energy { get { return _energy; } set { _energy = value; } } #region Attributes /// /// Bounding volume of this mass /// public BoundingVolume boundingVolume = new BoundingVolume(); /// /// Tells us what type of an object his is /// public Enums_Engine.ObjectType objectType = new Enums_Engine.ObjectType(); /// /// Temperature change check /// public bool isChanging = false; /// /// Motion check /// public bool isMoving = false; /// /// Have we completed all collisions on this mass /// public bool collisionChecked = false; /// /// Does the object need to be recycled /// public bool deadObject = false; // Properties /// /// Kilos /// public float mass = 0.0f; /// /// Used to calculate bonding factor to other game objects /// public float adhesion = 0.1f; /// /// Used to calculate bonding factor to like objects /// public float cohesion = 0.5f; /// /// Temperature (Celcius) at which a particle changes state /// public float stateChangeThreshold = 0; /// /// Coefficient Of Restitution for this object /// public float COR; /// /// Static Friction Coefficient of this object /// public float staticFrictionCoefficient; /// /// Dynamic Friction Coefficient of this object /// public float dynamicFrictionCoefficient; /// /// Amount of damage to be applied when this mass falls/hits the terrain /// public float fallingDamageMultiplier = 0.0f; /// /// Motion type of this object /// public Enums_Engine.MovementType movementType = Enums_Engine.MovementType.WALK; /// /// Change in energy value /// private float newEnergy = 0.0f; /// /// Orientation of the Sensor /// public Vector3 sensorDirection = new Vector3(); /// /// Used for falling damage calculations /// public float lastMaxHeight = 0; /// /// Number of times this object has collided with something /// public int numberOfCollision = 0; #endregion /// /// Constructor /// public Mass() { totalForce = Vector3.Zero; velocity = Vector3.Zero; } /// /// Reset mass to initial state /// public void ClearValues() { // Reset everything here // Called by constructor... objectType = Enums_Engine.ObjectType.NONE; objectHeight = 0; gameObjectPointer = null; // Orientation currentRotation = Quaternion.Identity; // Stores current rotation information normalVector = Vector3.Zero; // Straight ahead - normalized upVector = Vector3.Zero; // Straight up - normalized perpVector = Vector3.Zero; // Straight to the right?(left?) numberOfCollision = 0; // Position currentPosition = Vector3.Zero; // The objects center location lastPosition = Vector3.Zero; // Needed for updating the lookup map // Motion Variables acceleration = Vector3.Zero; velocity = Vector3.Zero; // Un-Normalized Vector in the direction of object movement totalForce = Vector3.Zero; // Used to sum the forces on a GameObject during Update // Reaction Properties mass = 1; // Kilos scale = new Vector3(1.0f); // Model Scale Factor adhesion = 0; // Used to calculate bonding factor to other game objects cohesion = 0; // Used to calculate bonding factor to like objects // Energy State energy = 0.0f; // Initial Fire energy given to player newEnergy = 0.0f; stateChangeThreshold = 0; // Temperature (Celcius) at which a particle changes state COR = 0.0f; // Coefficient Of Restitution for this object staticFrictionCoefficient = 0.0f; // Static Friction Coefficient of this object dynamicFrictionCoefficient = 0.0f; // Dynamic Friction Coefficient of this object isChanging = false; isMoving = false; collisionChecked = false; fallingDamageMultiplier = 0.0f; deadObject = false; } /// /// Update the mass's position /// public void Update() { // Signal collision that this object needs collision detection collisionChecked = false; // If mass is moving, update physics // .. else, set isMoving false to indicate no update needed if (totalForce != Vector3.Zero || velocity != Vector3.Zero) { isMoving = true; // Set last position to the old current position lastPosition = currentPosition; // Add force if below speed limit and motion type is Hover if (totalForce.Length() > Statics_Engine.PlayerSettings.MAX_HOVER_SPEED && movementType == Enums_Engine.MovementType.HOVER) { totalForce.Normalize(); totalForce *= Statics_Engine.PlayerSettings.MAX_HOVER_SPEED; } // Acceleration calc acceleration = totalForce / mass; if (float.IsNaN(acceleration.X) || float.IsNaN(acceleration.Y) || float.IsNaN(acceleration.Z)) { System.Diagnostics.Debug.WriteLine("acceleration = NaN"); Logging.addNan(); } // Force has been applied.. zero it out.. totalForce = Vector3.Zero; // Movement according to time S = u*t + 1/2*g*t^2 currentPosition = lastPosition + velocity * Statics_Engine.SystemSettings.dt + (0.5f * acceleration * Statics_Engine.SystemSettings.dt * Statics_Engine.SystemSettings.dt); if (float.IsNaN(currentPosition.X)) { System.Diagnostics.Debug.WriteLine("CurrentPosition = NaN"); Logging.addNan(); currentPosition = lastPosition; } // Calculate velocity if (movementType == Enums_Engine.MovementType.WALK && gameObjectPointer.heightDifference >= 0.0f) { velocity = Vector3.Zero; } else { velocity += acceleration * Statics_Engine.SystemSettings.dt; } // CLEAN: ?? set the height if (currentPosition.Y > lastMaxHeight) { lastMaxHeight = currentPosition.Y; } // Ensure that mass is within level boundaries EnforceLevelBoundary(); } else { isMoving = false; } // Check if new energy has been added to the object if (newEnergy != 0.0f) { // Call the energy update function UpdateEnergy(); } } /// /// Boundary checking /// public void EnforceLevelBoundary() { // X = 0 plane if (currentPosition.X < 0.0f) { // Clamp the position _currentPosition.X = 1.0f; _lastPosition.X = 0.0f; AddForce(new Vector3(-2.0f * mass * velocity.X * COR / Statics_Engine.SystemSettings.dt, 0.0f, 0.0f)); } // X = mapSize plane if (currentPosition.X > (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor) { // Clamp the position _currentPosition.X = (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor - 1; _lastPosition.X = (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor - 2; AddForce(new Vector3(-2.0f * mass * velocity.X * COR / Statics_Engine.SystemSettings.dt, 0.0f, 0.0f)); } //// Y = 0 plane //if (currentPosition.Y < 0.0f) //{ // // Clamp the position // currentPosition.Y = 1.0f; // lastPosition.Y = 0.0f; // AddForce(new Vector3(0.0f, -2.0f * mass * velocity.Y * COR / Statics.SystemSettings.dt, 0.0f)); //} // Y = mapSize plane if (currentPosition.Y > (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor) { // Clamp the position _currentPosition.Y = (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor - 1; _lastPosition.Y = (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor - 2; AddForce(new Vector3(0.0f, -2.0f * mass * velocity.Y * COR / Statics_Engine.SystemSettings.dt, 0.0f)); } // Z = 0 plane if (currentPosition.Z < 0.0f) { // Clamp the position _currentPosition.Z = 1.0f; _lastPosition.Z = 0.0f; AddForce(new Vector3(0.0f, 0.0f, -2.0f * mass * velocity.Z * COR / Statics_Engine.SystemSettings.dt)); } // Z = mapSize plane if (currentPosition.Z > (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor) { // Clamp the position _currentPosition.Z = (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor - 1; _lastPosition.Z = (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor - 2; AddForce(new Vector3(0.0f, 0.0f, -2.0f * mass * velocity.Z * COR / Statics_Engine.SystemSettings.dt)); } if (float.IsNaN(currentPosition.X)) { Logging.addNan(); _currentPosition.X = 1.0f; _lastPosition.X = 1.0f; } if (float.IsNaN(currentPosition.Y)) { Logging.addNan(); _currentPosition.Y = 1.0f; _lastPosition.Y = 0.0f; } if (float.IsNaN(currentPosition.Z)) { Logging.addNan(); _currentPosition.Z = 1.0f; _lastPosition.Z = 0.0f; } ///////////////////////////////////////// // this is a hack to fix the crashes due to lastPosition being invalid.. we really shouldn't have to do this // // X = 0 plane if (lastPosition.X < 0.0f) { // Clamp the position _lastPosition.X = 0.0f; } // X = mapSize plane if (lastPosition.X > (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor) { // Clamp the position _lastPosition.X = (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor - 2; } // Y = 0 plane if (lastPosition.Y < 0.0f) { // Clamp the position _lastPosition.Y = 0.0f; } // Y = mapSize plane if (lastPosition.Y > (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor) { // Clamp the position _lastPosition.Y = (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor - 2; } // Z = 0 plane if (lastPosition.Z < 0.0f) { // Clamp the position _lastPosition.Z = 0.0f; } // Z = mapSize plane if (lastPosition.Z > (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor) { // Clamp the position _lastPosition.Z = (Statics_Engine.TerrainSettings.vertexMapSize - 1) * Statics_Engine.TerrainSettings.terrainScaleFactor - 2; } } /// /// Set this mass's position vectors /// /// Center position /// Reference position public void SetPosition(Vector3 position, Vector3 reference) { normalVector = new Vector3(0, 0, 1); //reference - position; normalVector.Normalize(); upVector = new Vector3(0.0f, 1.0f, 0.0f); perpVector = new Vector3(-1.0f, 0.0f, 0.0f);//Vector3.Cross(normalVector, upVector); //new Vector3(-1.0f, 0.0f, 0.0f); upVector = Vector3.Cross(perpVector, normalVector); // Look in Z+ direction currentRotation = -Quaternion.Identity; Vector2 unitHoriz = new Vector2(0, 1); Vector2 unitVert = new Vector2(1, 0); Vector3 N = reference - position; N.Normalize(); Vector2 Nhoriz = new Vector2(N.X, N.Z); Vector2 Nvert = new Vector2(N.Z, N.Y); float angleHoriz = (float)Math.Acos((double)(Vector2.Dot(unitHoriz, Nhoriz))); float anglVert = 0.0f;// (float)Math.Acos((double)(Vector2.Dot(unitVert, Nvert))); //Determine the sign of the angle needed Vector3 cross = Vector3.Cross(Vector3.UnitZ, N); if (cross.Y <= 0) { angleHoriz = -Math.Abs(angleHoriz); } else { angleHoriz = Math.Abs(angleHoriz); } //passing negative because I am negating it again inside the function Rotate(anglVert, angleHoriz); // perpVector = Vector3.Cross(normalVector, upVector); //upVector = Vector3.Cross(perpVector, normalVector); currentPosition = new Vector3() + position; lastPosition = position - normalVector; } /// /// Add an external force to be applied to the mass /// /// Force to apply public void AddForce(Vector3 force) { totalForce += force; if (float.IsNaN(totalForce.X)) { Logging.addNan(); System.Diagnostics.Debug.WriteLine("AddForce.. totalForce = NaN"); totalForce = Vector3.Zero; } } /// /// Add force caused due to friction /// /// Total frictional coefficients /// Normal force /// Tangential direction of motion public void AddFriction(float totalFriction, float perpendicularForce, Vector3 tangentialDirection) { if (tangentialDirection.Length() != 0.0f) { tangentialDirection.Normalize(); } totalForce += (totalFriction * Math.Abs(perpendicularForce)) * tangentialDirection; if (float.IsNaN(totalForce.X)) { Logging.addNan(); //throw new Exception("NaN Error."); System.Diagnostics.Debug.WriteLine("AddFriction.. totalForce = NaN"); totalForce = Vector3.Zero; } } /// /// Function to add/remove energy from the object /// private void UpdateEnergy() { // Add newEnergy to energy and reset newEnergy energy += newEnergy; newEnergy = 0.0f; // Bounds checking if (energy > 1.0f) { energy = 1.0f; } else if (energy < -1.0f) { energy = -1.0f; } } /// /// Function to set the energy state of an object /// /// Energy state of object private void SetEnergy(float newEnergyValue) { energy = newEnergyValue; } /// /// Add/Remove energy to the mass /// /// Change in energy public void ChangeEnergy(float amount) { newEnergy += amount; } /// /// Move the player based on the controller inputs /// /// Force applied along the normal /// Force applied along the tangent public void MoveObject(float dn, float dx) { if (movementType == Enums_Engine.MovementType.WALK) { // ********************************************* HACK ************************************************// // Need the AI to have a diff rate of speed for the second level so we dont wait forever... // // ********************************************* HACK ************************************************// float walkSpeed = 0.0f; if (Statics_Engine.levelIndex == 1) { walkSpeed = 60.0f; } else { walkSpeed = Statics_Engine.PlayerSettings.WALK_SPEED; } float moveRatioN = dn * walkSpeed; float moveRatioX = dx * walkSpeed; //Statics.lookupMap.RemoveCurrent(this); sensorDirection = Vector3.Zero; if (dn != 0) { sensorDirection += new Vector3(normalVector.X, 0, normalVector.Z) * moveRatioN; } if (dx != 0) { sensorDirection += new Vector3(perpVector.X, 0, perpVector.Z) * moveRatioX; } lastPosition = currentPosition; currentPosition += sensorDirection; EnforceLevelBoundary(); } else { float moveRatioN = dn * Statics_Engine.PlayerSettings.HOVER_SPEED; float moveRatioX = dx * Statics_Engine.PlayerSettings.HOVER_SPEED; if (Math.Abs(totalForce.X) + Math.Abs(totalForce.Z) < 5000000.0f) { AddForce(new Vector3(normalVector.X, 0.0f, normalVector.Z) * moveRatioN); AddForce(new Vector3(perpVector.X, 0.0f, perpVector.Z) * moveRatioX); } } } /// /// Force applied when the player hits something /// /// Amount of rebound force public void KickBack(float amount) { if (movementType == Enums_Engine.MovementType.WALK && amount != 0.0f) { //Statics.lookupMap.RemoveCurrent(this); lastPosition = currentPosition; currentPosition += new Vector3(-sensorDirection.X, 0, -sensorDirection.Z) * amount; EnforceLevelBoundary(); } else if (movementType == Enums_Engine.MovementType.HOVER) { velocity += -velocity * amount; } } /// /// Rotate the coordinate system /// /// Vertical rotation /// Horizontal rotation public void Rotate(float dVert, float dHoriz) { Quaternion vertRotation = Quaternion.CreateFromAxisAngle(perpVector, dVert); Quaternion horizRotation = Quaternion.CreateFromAxisAngle(upVector, -dHoriz); Quaternion totalRotation; Vector3 newNormal = Vector3.Transform(normalVector, Matrix.CreateFromQuaternion(horizRotation * vertRotation)); // Limit the rotation double angle1 = Math.Acos((double)(Vector3.Dot(normalVector, new Vector3(0, 1, 0)))); double angle2 = Math.Acos((double)(Vector3.Dot(normalVector, newNormal))); double angle; if (dVert > 0) { angle = angle1 - angle2; } else { angle = angle1 + angle2; } if (angle < 1.3f || angle > 2.3f) { //ignore the vertical rotation totalRotation = horizRotation;// *(-vertRotation); } else { //do both rotations totalRotation = horizRotation * vertRotation; } currentRotation = Quaternion.Normalize(totalRotation * currentRotation); normalVector = Vector3.Transform(normalVector, Matrix.CreateFromQuaternion(totalRotation)); perpVector = Vector3.Transform(perpVector, Matrix.CreateFromQuaternion(totalRotation)); } } } /* Legacy Code //Strafe object on the V (sideways) plane public void Strafe(float dx) { if (movementType == MovementType.WALK && dx != 0.0f) { Statics.lookupMap.RemoveCurrent(this); float moveRatio = dx / Statics.PlayerSettings.PLAYER_SPEED_ON_THE_GROUND; lastPosition = currentPosition; sensorDirection = new Vector3(perpVector.X, 0, perpVector.Z) * moveRatio * Statics.PlayerSettings.PLAYER_WALK_SPEED; currentPosition += sensorDirection; EnforceBoundary(); } else { AddForce(perpVector * dx); } } //Move forwards or backwards along normal (does not restrict Y value) public void Move(float dn) { if (movementType == MovementType.WALK && dn != 0.0f) { Statics.lookupMap.RemoveCurrent(this); float moveRatio = dn / Statics.PlayerSettings.PLAYER_SPEED_ON_THE_GROUND; lastPosition = currentPosition; sensorDirection = new Vector3(normalVector.X, 0, normalVector.Z) * moveRatio * Statics.PlayerSettings.PLAYER_WALK_SPEED; currentPosition += sensorDirection; EnforceBoundary(); } else { AddForce(normalVector * dn); } } */