Could You review below code from physics point of view. It serves to set speed, rotation for the ball. I implemented it according to my best knowledge from lyceum and a bit from books (however mostly from lyceum, because some parts of book was not understood by me). Maybe this code would be also useful for someone else.
Here is some explanation of function which serves to set force:
void Physic::setForce(D3DXVECTOR3* force, D3DXVECTOR3* collisionPoint) - this should be used when there is some constant force which influence all the time ball
void Physic::setV(D3DXVECTOR3* V, D3DXVECTOR3* collisionPoint) - this should be used when for example somebody kick the ball (so force is not set all the time to the ball), then You set as argument V = mass of leg * velocity of leg / mass of the ball => this come from "momentum rule" known from school
Header
#ifndef _PHYSIC_
#define _PHYSIC_
#include "common.h"
#include "const.h"
#define RESISTANCE_X 0.1f
#define RESISTANCE_Z 0.1f
#define RESISTANCE_MAGNUM 0.1f
#define GROUND_ABSORBANCE 0.2f
#define GROUND_FRICTION 0.1f
#define WALL_ABSORBANCE 0.2f
//REST_THRESHOLD is the energy below which the ball is flagged as laying on ground.
//It is defined as Gravity * Height_above_ground + 0.5 * Velocity * Velocity
#define REST_THRESHOLD 0.005f
#define GROUND_LEVEL 0
#define MIN_BOUND_X -10.0f
#define MAX_BOUND_X 10.0f
#define MIN_BOUND_Z -10.0f
#define MAX_BOUND_Z 10.0f
class Physic
{
public:
typedef enum
{
SPHERE = 0,
CUBE = 1,
CUBOID = 2,
CYLINDER = 3,
OTHER = 4
} Type;
Physic(Type type, D3DXVECTOR3 pos, float mass, void* param);
~Physic();
void setForce(D3DXVECTOR3* force, D3DXVECTOR3* collisionPoint);
void setV(D3DXVECTOR3* V, D3DXVECTOR3* collisionPoint);
void resetForce();
void update(float deltaTime);
Type mType;
float mR; //distance from centre to point of net force (const for sphere)
D3DXVECTOR3 mPos; //Position
D3DXVECTOR3 mV; //Linear velocity
float mM; //Mass
D3DXVECTOR3 mNetF; //Sum of forces applied to object
D3DXVECTOR3 mA;
D3DXQUATERNION mQuatOrientation; //Orientation (rotation of the mesh)
D3DXMATRIX mMatOrientation; //Orientation (rotation of the mesh in matrix form)
D3DXVECTOR3 mE; //Angular acceleration
vector<D3DXVECTOR3> mForces;
vector<D3DXVECTOR3> mPointsF;
vector<D3DXVECTOR3> mVs;
vector<D3DXVECTOR3> mPointsV;
D3DXVECTOR3 mW; //Angular velocity
D3DXMATRIX mIinv; //Inverse moment of inertia tensor matrix
bool mGround; //Is ball placed on the ground
private:
void takeIntoAccountBounds();
float getDistFromOrigin();
};
#endif
.cpp file
#include "physic.h"
Physic::Physic(Physic::Type type, D3DXVECTOR3 pos, float mass, void* param):mM(mass),mType(type),mGround(false)
{
//init variables
mA = D3DXVECTOR3(0,0,0);
mV = D3DXVECTOR3(0,0,0);
mR = 0;
mNetF = D3DXVECTOR3(0,0,0);
mE = D3DXVECTOR3(0,0,0);
mW = D3DXVECTOR3(0,0,0);
//Set inertia tensor
D3DXMatrixIdentity(&mIinv);
if(mType == Physic::SPHERE)
{
mR = *(static_cast<float *>(param));
mIinv._11 = 1.00f / (mM * 0.40f * pow(mR, 2));
mIinv._22 = mIinv._11;
mIinv._33 = mIinv._11;
}
//Set mPos, orientation
mPos = pos;
d3d::setQuaternion(&mQuatOrientation, d3d::vecUP, 0); //second parameter in fact might be anything, angle = 0
D3DXMatrixRotationQuaternion(&mMatOrientation, &mQuatOrientation);
D3DXMatrixTranspose(&mMatOrientation, &mMatOrientation); //convert matrix to left-hand coordinate system
mMatOrientation._41 += mPos.x;
mMatOrientation._42 += mPos.y;
mMatOrientation._43 += mPos.z;
}
Physic::~Physic(){}
void Physic::setForce(D3DXVECTOR3* force, D3DXVECTOR3* collisionPoint)
{
mNetF += *force;
if(mType == SPHERE)
{
mForces.push_back(*force);
mPointsF.push_back(*collisionPoint);
}
}
void Physic::setV(D3DXVECTOR3* V, D3DXVECTOR3* collisionPoint)
{
mV += *V;
if(mType == SPHERE)
{
mVs.push_back(*V);
mPointsV.push_back(*collisionPoint);
}
}
void Physic::update(float t)
{
//calculate rotational motion
if(mType == SPHERE)
{
//for each collision point calculate Wo
if(!mVs.empty())
{
D3DXVECTOR3 L = D3DXVECTOR3(0,0,0);
u32 size = static_cast<u32>(mVs.size());
for(u32 i=0; i<size; ++i)
L += calc::CrossProduct(&(mPointsV.at(i) - mPos), &mVs.at(i)) * mM;
mVs.clear();
mPointsV.clear();
D3DXVECTOR3 Wo;
D3DXVec3TransformCoord(&Wo, &L, &mIinv); //angular momentum
mW += Wo;
}
//for each collision point calculate torque
if(!mForces.empty())
{
D3DXVECTOR3 torque = D3DXVECTOR3(0,0,0);
u32 size = static_cast<u32>(mForces.size());
for(u32 i=0; i<size; ++i)
torque += calc::CrossProduct(&(mPointsF.at(i) - mPos), &mForces.at(i));
mForces.clear();
mPointsF.clear();
D3DXVec3TransformCoord(&mE, &torque, &mIinv);
}
mW += mE * t; // E = M/I = W - Wo = M/I * t
//Update velocity about resitance
mW -= mW * RESISTANCE_MAGNUM * t;
D3DXVECTOR3 fi = mW * t;
// Apply angle to orientation
mQuatOrientation.w -= 0.5f * (mQuatOrientation.x * fi.x + mQuatOrientation.y * fi.y + mQuatOrientation.z * fi.z);
mQuatOrientation.x += 0.5f * (mQuatOrientation.w * fi.x - mQuatOrientation.z * fi.y + mQuatOrientation.y * fi.z);
mQuatOrientation.y += 0.5f * (mQuatOrientation.z * fi.x + mQuatOrientation.w * fi.y - mQuatOrientation.x * fi.z);
mQuatOrientation.z += 0.5f * (mQuatOrientation.x * fi.y - mQuatOrientation.y * fi.x + mQuatOrientation.w * fi.z);
D3DXQuaternionNormalize(&mQuatOrientation, &mQuatOrientation); //normalize the quaternion (creates a unit quaternion)
D3DXMatrixRotationQuaternion(&mMatOrientation, &mQuatOrientation);
D3DXMatrixTranspose(&mMatOrientation, &mMatOrientation); //convert matrix to left-hand coordinate system
}
//calculate progressive motion
mA += mNetF / mM;
mNetF = D3DXVECTOR3(0,0,0); //clear force
D3DXVECTOR3 Vo = mV;
mV += mA * t;
//Update velocity about resitance
mV.x -= mV.x * RESISTANCE_X * t;
mV.z -= mV.z * RESISTANCE_Z * t;
if(!mGround) //Apply gravity if the ball is not resting on the ground
mV.y += ph::g.y*t;
takeIntoAccountBounds();
mPos += ((Vo + mV) / 2.00f) * t;
//update mat orientation
mMatOrientation._41 = mPos.x;
mMatOrientation._42 = mPos.y;
mMatOrientation._43 = mPos.z;
}
void Physic::takeIntoAccountBounds()
{
if(mType != SPHERE)
mR = getDistFromOrigin();
//Check bounce on ground
if(!mGround)
{
if(mPos.y < GROUND_LEVEL + mR)
{
//Align the ball with the ground.
mPos.y = GROUND_LEVEL + mR;
//TO DO: Play sound
//Invert the Y velocity
mV.y = -mV.y * (1 - GROUND_ABSORBANCE);
//X and Z velocity are reduced because of friction (tarcie)
mV.x *= (1 - GROUND_FRICTION);
mV.z *= (1 - GROUND_FRICTION);
}
}
else //Ball is resting or rolling on ground
{
// X and Z velocity are reduced because of friction
mV.x *= (1 - GROUND_FRICTION);
mV.z *= (1 - GROUND_FRICTION);
}
//If the Y direction motion is below a certain threshold, flag the instance as laying on the ground
if(ph::g.y * (mPos.y + GROUND_LEVEL - mR) + 0.5f * mV.y * mV.y < REST_THRESHOLD)
{
//Align the ball with the ground
mPos.y = GROUND_LEVEL + mR;
mV.y = 0;
mGround = true;
}
// Check bounce on walls
if(mPos.z - mR < d3d::MIN_BOUND_Z)
{
//Align the ball with the wall
mPos.z = d3d::MIN_BOUND_Z + mR;
//Play sound
//Invert the Z velocity
mV.z = -mV.z * (1 - WALL_ABSORBANCE);
}
if(mPos.z + mR > d3d::MAX_BOUND_Z)
{
//Align the ball with the wall
mPos.z = d3d::MAX_BOUND_Z - mR;
//Play sound
//Invert the Z velocity
mV.z = -mV.z * (1 - WALL_ABSORBANCE);
}
if(mPos.x - mR < d3d::MIN_BOUND_X)
{
//Align the ball with the wall
mPos.x = d3d::MIN_BOUND_X + mR;
//Play sound
//Invert the Z velocity
mV.x = -mV.x * (1 - WALL_ABSORBANCE);
}
if(mPos.x + mR < d3d::MAX_BOUND_X)
{
//Align the ball with the wall
mPos.x = d3d::MAX_BOUND_X - mR;
//Play sound
//Invert the Z velocity
mV.x = -mV.x * (1 - WALL_ABSORBANCE);
}
}
float Physic::getDistFromOrigin()
{
switch(mType)
{
case SPHERE: return mR;
case CUBOID: return mR;
case CYLINDER: return mR;
}
return 0;
}
void Physic::resetForce()
{
mE = D3DXVECTOR3(0,0,0);
mA = D3DXVECTOR3(0,0,0);
}