Physic of ball for review

Started by
0 comments, last by tapped 10 years, 3 months ago

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);
}
Advertisement

First of all your code is not cache friendly at all. You branch your code to much, for example why check if the force vector is empty?
Another thing, you have not made a common solution for physic update. The same update function should work for all types of shapes not only spheres.

The update function should only be a physic integration. As you may recall: a = F / m, v += a * t, s += v * t + 0.5 * a * t * t for linear physic, and angularAcc = torq * I-1, rotation += angularAcc * t, orientation += rotation * t for angular physic.

I see you are doing this, however you should separate your integration code from the contact resolving, so that you can have common solution for all bodies. So what you need to do is to clean up your code, and separate it, and have in mind that your solution should not be specific for each shape, but common.

Also to paste code from visual studio, turn on "space for tabs" in the editor settings, and then convert your code file before you copy it.

This topic is closed to new replies.

Advertisement