|
3D Pong Collision & Response
Submitted by |
Here is an example / inspiration on how to handle a 3D pong / breakout game. It handles
AABBox / Sphere collision tests, and perform some simple collision physics, based on
an extended 'velocity reflection' model (does friction / restitution and handles difference in
object mass). Thanks to the physics, Objects don't get squashed on walls, and react pretty
well to multiple simultaneous collision. The code is unoptimised, but should be already quite
fast already. SInce the collision detection routines work on overlaps, you have to be careful
with the velocity of the objects, and their relative size (relative to their velocity). Swept
tests would solve the problem of both accuracy and stability on thin&fast moving objects, but
that's for another COTD....
Other goodies are,
Quake3 style camera control.
3rd person camera view.
split screen.
somewhat stand alone openGL framework.
general physics system and collision design tips.
simple vector class.
fully commented.
possible future improvements :
Mesh collision.
Swept volume collison tests and physics.
The code is free, and all that.
Have fun
|
Currently browsing [pong_collision.zip] (75,616 bytes) - [pong collision/source/main.cpp] - (17,154 bytes)
//=========================================================
//
// Simple Physics Demo
//
//=========================================================
#include <conio.h>
#include <math.h>
#include <time.h> // 3x3 matrix class
#include <stdio.h> // 3x3 matrix class
#include <stdlib.h> // 3x3 matrix class
#include <memory.h>
#include <gl/glut.h>
#ifdef WIN32
#include <windows.h>
#include <winuser.h>
#endif
//------------------------------------------------------------------
// Game interface
//------------------------------------------------------------------
#include "Game Code.h"
//--------------------------------------------------------------------------
// random float
//--------------------------------------------------------------------------
float frand(float range)
{
return range * (rand() / (float)RAND_MAX);
}
float sgn(float x)
{
return (x < 0.0f)? -1.0f : 1.0f;
}
//--------------------------------------------------------------------------
// openGL tool function
//--------------------------------------------------------------------------
void SetMaterial(int ColorIndex, float transparency)
{
float t = transparency;
float a = 0.5f, b = 1.0f;
static GLfloat Color[16][4] = { { 0, 0, 0, t }, { a, 0, 0, t }, { 0, a, 0, t }, { 0, 0, a, t },
{ a, a, 0, t }, { a, 0, a, t }, { 0, a, a, t }, { a, a, a, t },
{ a, a, a, 1 }, { b, 0, 0, 1 }, { 0, b, 0, 1 }, { 0, 0, b, 1 },
{ b, b, 0, 1 }, { b, 0, b, 1 }, { 0, b, b, 1 }, { b, b, b, 1 } };
GLfloat Black[4] = { 0, 0, 0, 1 };
GLfloat White[4] = { 1, 1, 1, 1 };
int AmbientIndex = ColorIndex % 8;
int DiffuseIndex = (AmbientIndex + 8) % 16;
glMaterialfv(GL_FRONT, GL_AMBIENT , Color[AmbientIndex]);
glMaterialfv(GL_FRONT, GL_DIFFUSE , Color[DiffuseIndex]);
glMaterialfv(GL_FRONT, GL_EMISSION , Black);
glMaterialfv(GL_FRONT, GL_SPECULAR , White);
glMaterialf (GL_FRONT, GL_SHININESS, 20.0f);
}
//------------------------------------------------------------------
// cameras
//------------------------------------------------------------------
int screen_width = 800; // The width of the screen in pixels
int screen_height = 600; // The height of the screen in pixels
int screen_bpp = 32; // bits per pixels
int screen_hz = 60; // dispaly refresh
Vector CamPos [2] = { Vector(0.0f, 0.0f, 0.0f), Vector(0.0f, 0.0f, 0.0f) };
Vector CamAngle [2] = { Vector(0.0f, 0.0f, 0.0f), Vector(0.5f, -0.5f, 0.0f) };
Vector CamMove [2] = { Vector(0.0f, 0.0f, 0.0f), Vector(0.0f, 0.0f, 0.0f) };
Vector CameraViewportPos [2] = { Vector(0.0f, 0.5f, 0.0f), Vector(0.0f, 0.0f, 0.0f) };
Vector CameraViewportSize [2] = { Vector(1.0f, 0.5f, 0.0f), Vector(1.0f, 0.5f, 0.0f) };
Vector CamLeft [2] = { Vector(1.0f, 0.0f, 0.0f), Vector(1.0f, 0.0f, 0.0f) };
Vector CamUp [2] = { Vector(0.0f, 1.0f, 0.0f), Vector(0.0f, 1.0f, 0.0f) };
Vector CamDir [2] = { Vector(0.0f, 0.0f, 1.0f), Vector(0.0f, 0.0f, 1.0f) };
//int CameraKeys [2][6] = { { 'z', 's', 'q', 'd', 'a', 'w' }, { 'z', 's', 'q', 'd', 'a', 'w' } };
int CameraKeys [2][6] = { { 'w', 's', 'a', 'd', 'q', 'z' }, { 'w', 's', 'a', 'd', 'q', 'z' } };
int CameraSphereID [2] = { 0, 1 };
float CameraFOV [2] = { 90.0f, 90.0f };
enum { eNumCameras = 2 };
//------------------------------------------------------------------
// input buffer
//------------------------------------------------------------------
int mouse_x =0;
int mouse_y =0;
int mouse_button=0;
int old_mouse_x =0;
int old_mouse_y =0;
unsigned char key[256];
//--------------------------------------------------------------------------
// the rate of updates (recommended 5fps -> 60 fps for updates).
//--------------------------------------------------------------------------
float dbg_updatefps = 30.0f;
int dbg_update_frame = 0;
int dbg_overlapped = false;
float dbg_world_size = 1000.0f; // size of world
int dbg_seed =0;
const float dbg_Camera_movement_damping = 0.8f;
const float dbg_InputForce = 40.0f;
void Init()
{
dbg_update_frame = 0;
dbg_overlapped = 0;
dbg_seed = clock();
srand(dbg_seed);
GameInit();
}
void UpdateCamera(int id)
{
if (CameraSphereID[id] == -1)
{
CamMove[id].x *= dbg_Camera_movement_damping * 0.8f;
CamMove[id].y *= dbg_Camera_movement_damping * 0.8f;
CamMove[id].z *= dbg_Camera_movement_damping * 0.8f;
}
else
{
CamMove[id].x = 0.0f;
CamMove[id].y = 0.0f;
CamMove[id].z = 0.0f;
}
//---------------------------------------------
// Handle controls
//---------------------------------------------
if (mouse_button == id + 1)
{
CamMove[id].z += ((key[CameraKeys[id][1]]) - (key[CameraKeys[id][0]])) * 1.0f;
CamMove[id].x += ((key[CameraKeys[id][3]]) - (key[CameraKeys[id][2]])) * 1.0f;
CamMove[id].y += ((key[CameraKeys[id][4]]) - (key[CameraKeys[id][5]])) * 1.0f;
}
//-----------------------------------------------------------------
// handle mouse
//-----------------------------------------------------------------
if (mouse_button == id + 1)
{
CamAngle[id].x += ((mouse_y - old_mouse_y) / 200.0f);
CamAngle[id].y += ((mouse_x - old_mouse_x) / 200.0f);
CamAngle[id].x = (CamAngle[id].x < -1.5f)? -1.5f : (CamAngle[id].x > 1.5f)? 1.5f : CamAngle[id].x;
}
//-----------------------------------------------------------------
// setup camera position
//-----------------------------------------------------------------
float cos_yaw = (float) cos(CamAngle[id].y);
float sin_yaw = (float)-sin(CamAngle[id].y);
float cos_pitch = (float)cos(CamAngle[id].x);
float sin_pitch = (float)sin(CamAngle[id].x);
CamLeft[id] = Vector( cos_yaw, 0.0f, -sin_yaw);
CamDir [id] = Vector( sin_yaw * cos_pitch, sin_pitch, cos_yaw * cos_pitch);
CamUp [id] = CamDir[id] ^ CamLeft[id];
//------------------------------------------------------------------------
// calculate the force from inputs
//------------------------------------------------------------------------
Vector Force(0, 0, 0);
Force += CamLeft[id] * CamMove[id].x;
Force += CamDir [id] * CamMove[id].z;
Force += Vector(0.0f, CamMove[id].y, 0.0f);
if (CameraSphereID[id] != -1)
{
GameAddImpulseToObject(CameraSphereID[id], Force * 100.0f);
bool bThirdPerson = true;
if (bThirdPerson)
CamPos[id] = GameGetObjectPos(CameraSphereID[id]) + CamDir [id] * 30.0f + CamUp[id] * 15.0f;
else
CamPos[id] = GameGetObjectPos(CameraSphereID[id]);
}
else
{
CamPos[id] += Force;
}
}
//--------------------------------------------------------------------------
// update all the spheres independently
//--------------------------------------------------------------------------
void Update(void)
{
//--------------------------------------------------------------------------
// dirty windows key management
//--------------------------------------------------------------------------
#ifdef WIN32
GetKeyboardState(key);
for(int i = 0; i < 256; i ++) key[i] = (unsigned char) ((key[i] & (1 << 7)) >> 7);
for(int i = 0; i <= 9; i ++) key[i+48] |= key[i+96]; // bind characters 'a' to 'A', ect...
for(int i = 'A'; i <= 'Z'; i ++) key[i+'a' - 'A'] = key[i]; // bind characters 'a' to 'A', ect...
#endif
//--------------------------------------------------------------------------
// move cameras, and their attached spheres (update cam first, so we can add forces to
// attached spheres).
//--------------------------------------------------------------------------
for(int i = 0; i < 2; i ++)
{
UpdateCamera(i);
}
old_mouse_y = mouse_y;
old_mouse_x = mouse_x;
//--------------------------------------------------------------------------
// Update spehre physics, with collisions
//--------------------------------------------------------------------------
GameUpdate();
//--------------------------------------------------------------------------
// clear key buffer
//--------------------------------------------------------------------------
for(int i = 0; i < 256; i ++)
key[i] = 0;
}
float fRadius = 1000.0f;
int glStarList = -1;
enum { eNumStars = 2000 };
void RenderSky()
{
const float two_pi = (float) atan(1) * 8.0f;
if (!glIsList(glStarList))
{
glStarList = glGenLists(1);
glNewList(glStarList, GL_COMPILE);
glBegin(GL_POINTS);
for (int i = 0; i < eNumStars; i ++)
{
float latitude = frand(two_pi);
float longitude = frand(two_pi);
Vector Rad(frand(fRadius * 0.2f) + fRadius, frand(fRadius * 0.2f) + fRadius, frand(fRadius * 0.2f) + fRadius);
Vector Pos( (float) cos(latitude) * (float) cos(longitude) * Rad.x,
(float) sin(latitude) * Rad.y,
(float) cos(latitude) * (float) sin(longitude) * Rad.z);
Vector Norm = Pos; Norm.Normalise();
glNormal3f (Norm.x, Norm.y, Norm.z);
glVertex3fv(&Pos.x);
}
glEnd();
glEndList();
}
SetMaterial(2, 1.0f);
glCallList(glStarList);
}
//---------------------------------------------
// setup the camera for rendering and collision
//---------------------------------------------
void RenderCamera(int id)
{
float x = -(CamPos[id] * CamLeft[id]);
float y = -(CamPos[id] * CamUp [id]);
float z = -(CamPos[id] * CamDir [id]);
//-----------------------------------------------------------------
// the inverse of the camera position matrix, to render objects
// in camera space
//-----------------------------------------------------------------
float mat[16] = { CamLeft[id].x, CamUp[id].x, CamDir[id].x, 0.0f,
CamLeft[id].y, CamUp[id].y, CamDir[id].y, 0.0f,
CamLeft[id].z, CamUp[id].z, CamDir[id].z, 0.0f,
x, y, z, 1.0f };
//-----------------------------------------------------------------
// Setup the infinite projection matrix, just for a laugh
//-----------------------------------------------------------------
float vpratio = 1.0f;
if (eNumCameras > 1)
vpratio = CameraViewportSize[id].x / CameraViewportSize[id].y;
float fov = CameraFOV[id];
float nearp = 0.1f;
float aspect = 4.0f / 3.0f * vpratio;
float pinf[4][4] = { { 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 } };
pinf[0][0] = (float) atan(fov) / aspect;
pinf[1][1] = (float) atan(fov);
pinf[3][2] = -2.0f * nearp;
pinf[2][2] = -1.0f;
pinf[2][3] = -1.0f;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glLoadMatrixf(&pinf[0][0]);
//-----------------------------------------------------------------
// Setup the model view matrix
//-----------------------------------------------------------------
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMultMatrixf(mat);
//-----------------------------------------------------------------
// Setup Viewport (which part of the screen to render the scene).
//-----------------------------------------------------------------
if (eNumCameras > 1)
{
glViewport( (int)(CameraViewportPos [id].x * screen_width),
(int)(CameraViewportPos [id].y * screen_height),
(int)(CameraViewportSize[id].x * screen_width),
(int)(CameraViewportSize[id].y * screen_height));
}
else
{
glViewport( 0, 0, screen_width, screen_height);
}
//-----------------------------------------------------------------
// render sky
//-----------------------------------------------------------------
RenderSky();
GameRender();
}
//--------------------------------------------------------------------------
// render simulation
//--------------------------------------------------------------------------
void Render(void)
{
//--------------------------------------------------------------------------
// render stuff
//--------------------------------------------------------------------------
glClearColor(0.01f, 0.2f, 0.08f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Update();
//--------------------------------------------------------------------------
// render cameras
//--------------------------------------------------------------------------
for(int i = 0; i < eNumCameras; i ++)
RenderCamera(i);
glutSwapBuffers();
}
//--------------------------------------------------------------------------
// Update & render simulation
//--------------------------------------------------------------------------
void Timer(int t)
{
Render();
glutTimerFunc(t, Timer, (int)(100.0f / dbg_updatefps));
}
void Keyboard(unsigned char k, int x, int y)
{
key[k] = 1; // compared to windows asynch keys, the repeated inputs are very slow
//---------------------------------------------
// Initialise the simulation
//---------------------------------------------
if (k == 'r' || k == 'R')
Init();
}
void Mouse(int Button, int State, int x, int y)
{
mouse_button = 0;
if (Button == GLUT_LEFT_BUTTON)
{
if (State == GLUT_DOWN)
{
mouse_button = 1;
}
}
if (Button == GLUT_RIGHT_BUTTON)
{
if (State == GLUT_DOWN)
{
mouse_button = 2;
}
}
old_mouse_y = mouse_y;
old_mouse_x = mouse_x;
mouse_y = y;
mouse_x = x;
}
void Motion(int x, int y)
{
old_mouse_y = mouse_y;
old_mouse_x = mouse_x;
mouse_y = y;
mouse_x = x;
}
void PassiveMotion(int x, int y)
{
mouse_y = y;
mouse_x = x;
old_mouse_y = mouse_y;
old_mouse_x = mouse_x;
}
//--------------------------------------------------------------------------
// window management
//--------------------------------------------------------------------------
void Reshape(int w, int h)
{
screen_width = w;
screen_height = h;
}
/*
bool EnterFullscreen()
{
char mode_str[64];
sprintf(mode_str, "%dx%d:%d@%d", screen_width, screen_height, screen_bpp, screen_hz);
glutGameModeString(mode_str);
if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE))
return false;
glutEnterGameMode();
screen_width = glutGameModeGet( GLUT_GAME_MODE_WIDTH );
screen_height = glutGameModeGet( GLUT_GAME_MODE_WIDTH );
screen_bpp = glutGameModeGet( GLUT_GAME_MODE_PIXEL_DEPTH );
screen_hz = glutGameModeGet( GLUT_GAME_MODE_REFRESH_RATE );
return true;
}
/**/
bool EnterWindow()
{
glutInitWindowSize (screen_width, screen_height);
glutInitWindowPosition (0, 0);
glutCreateWindow ("swept spheres vs. triangles demo");
glutReshapeFunc (Reshape);
return true;
}
//--------------------------------------------------------------------------
// main loop
//--------------------------------------------------------------------------
int main(int argc, char** argv)
{
printf ("3D Pong / breakout collision demo.\n");
printf ("---------------------------------.\n");
printf ("performs AABox/sphere tests, with added physics.\n");
printf ("collision response based on a simple extend reflexion model.\n");
printf ("press 'r' to reset the simulation.\n");
printf ("'a', 's', 'w', 'd', 'q', 'z' control the camera movement.\n");
printf ("hold mouse1/2 to move one of the camera.\n");
printf ("comments / rants, email at olivierrenault@hotmail.com.\n");
printf ("Enjoy :)\n");
printf ("---------------------------.\n");
printf ("- Oli.\n");
printf ("---------------------------.\n");
printf ("possible future extensions : \n");
printf ("--------------------------- \n");
printf (" - Triangle mesh collision. \n");
printf (" - Swept volume collision tests. \n");
dbg_seed = clock();
srand(dbg_seed);
//--------------------------------------------------------------------------
// OpenGL / GLUT init
//--------------------------------------------------------------------------
glutInit( &argc, argv );
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
// if (!EnterFullscreen())
EnterWindow();
glShadeModel (GL_SMOOTH);
glEnable (GL_NORMALIZE);
glEnable (GL_DEPTH_TEST);
glEnable (GL_CULL_FACE);
glDisable (GL_LIGHTING);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
float GlobalAmbient[] = { 0.3f, 0.3f, 0.3f, 1.0f };
float LightAmbient [] = { 0.2f, 0.2f, 0.2f, 1.0f };
float LightSpecular[] = { 0.7f, 0.7f, 0.7f, 1.0f };
float LightDiffuse [] = { 0.5f, 0.5f, 0.5f, 1.0f };
float LightPos [] = { 0.0f, 60.0f, 0.0f, 0.0f };
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, GlobalAmbient);
glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular);
glLightfv(GL_LIGHT0, GL_AMBIENT, LightSpecular);
glutDisplayFunc (Render);
glutTimerFunc (0, Timer, (int)(100.0f / dbg_updatefps));
glutKeyboardFunc (Keyboard);
glutMouseFunc (Mouse);
glutPassiveMotionFunc (PassiveMotion);
glutMotionFunc (Motion);
Init ();
glutMainLoop ();
return (0);
}
|
|
Currently browsing [pong_collision.zip] (75,616 bytes) - [pong collision/source/game code.cpp] - (23,741 bytes)
//=========================================================
//
// Simple Physics Demo
//
//=========================================================
#include <stdlib.h>
#include <stdio.h>
#include "Game Code.h"
//---------------------------------------------------------------------
// Base collision object class
//---------------------------------------------------------------------
struct CObject
{
public:
//---------------------------------------------------------------------
// Constructors
//---------------------------------------------------------------------
CObject()
{}
CObject(const Vector& xPos, const Vector& xVel, float fMass)
: m_xPos(xPos)
, m_xVel(xVel)
, m_fMass(fMass)
{
}
//---------------------------------------------------------------------
// Modifiers / Selectors
//---------------------------------------------------------------------
bool IsStatic(void) const { return (m_fMass < 0.0000001f); }
const Vector& GetPosition() const { return m_xPos; }
const Vector& GetVelocity() const { return m_xVel; }
float GetMass () const { return m_fMass; }
void SetPosition(const Vector& xPos) { m_xPos = xPos; }
void SetVelocity(const Vector& xVel) { m_xVel = xVel; }
void SetMass (float fMass) { m_fMass = fMass; }
//---------------------------------------------------------------------
// Member functions
//---------------------------------------------------------------------
void Update(float dt)
{
if (IsStatic()) // only massive objects can be moved
return;
m_xPos += m_xVel * dt;
}
//---------------------------------------------------------------------
// Virtual Interface
//---------------------------------------------------------------------
virtual void Render(void) const = 0;
virtual bool Intersect(const CObject& xObject, Vector& xP0, Vector &xP1) const = 0;
virtual bool Intersect(const class CSphere& xSphere, Vector& xP0, Vector &xP1) const = 0;
virtual bool Intersect(const class CAABBox& xBox, Vector& xP0, Vector &xP1) const = 0;
//-------------------------------------------------
// process the collision response on two objects
//-------------------------------------------------
bool ProcessCollision(CObject& xObj, const Vector& xThisPoint, const Vector& xObjPoint)
{
Vector N = xObjPoint - xThisPoint; // normal of plane of collision
//-------------------------------------------------
// calcualte the amount of collison response for both objects
//-------------------------------------------------
float fRatio1, fRatio2;
if (!CalculateMassRatio(xObj, fRatio1, fRatio2, true))
return false;
m_xPos += N * fRatio1; // move the Objects away from each other
xObj.m_xPos -= N * fRatio2;
Vector xVel = m_xVel - xObj.m_xVel; // Calcualte the relative velocity
float nv = N * xVel; // Calcualte the impact velocity
if (nv > 0.0f) // spheres moving away from each other, so don't reflect
return false;
float n2 = N * N; // the normal of collision length squared
if (n2 < 0.00001f) // to small, can't be of any use
return false;
CalculateMassRatio(xObj, fRatio1, fRatio2, false);
float fElasticity = 0.8f; // coefficient of elqsticity
float fFriction = 0.1f; // coefficient of friciton
//----------------------------------------------
// Collision response. Calcualte the two velocity components
//----------------------------------------------
Vector Vn = N * (nv / n2); // relative velocity alon the normal of collision
Vector Vt = xVel - Vn; // tangencial velocity (along the collision plane)
//----------------------------------------------
// apply response
// V = -Vn . (1.0f + CoR) + Vt . CoF
//----------------------------------------------
m_xVel -= ((1.0f + fElasticity) * fRatio1) * Vn + Vt * fFriction; // reflect the first sphere
xObj.m_xVel += ((1.0f + fElasticity) * fRatio2) * Vn + Vt * fFriction; // reflect the second sphere
return true;
}
private:
//-------------------------------------------------
// calcualte the amount of collison response for both objects
// based on the ratio of mass
// if one of the object is static (mass = 0.0f)
// then the amount of response will be maximum on the mobile object
// to avoid squqshing objects against static walls,
// you can set the amount of impulse equally apart when separating objects
//-------------------------------------------------
bool CalculateMassRatio(CObject& xObj, float &fRatio1, float& fRatio2, bool bNormalise=false)
{
float m = (GetMass() + xObj.GetMass());
if (m < 0.000001f)
return false;
else if (xObj.GetMass() < 0.0000001f)
{
fRatio1 = 1.0f;
fRatio2 = 0.0f;
}
else if (GetMass() < 0.0000001f)
{
fRatio1 = 0.0f;
fRatio2 = 1.0f;
}
else
{
if (bNormalise)
{
fRatio1 = 0.5f;
fRatio2 = 1.0f - fRatio1;
}
else
{
fRatio1 = xObj.GetMass() / m;
fRatio2 = 1.0f - fRatio1;
}
}
return true;
}
protected:
Vector m_xPos;
Vector m_xVel;
float m_fMass;
};
//---------------------------------------------------------------------------
// Object list management
//
// Maintain a list of objects, for collision and rendering
//---------------------------------------------------------------------------
class CObjectList
{
public:
//---------------------------------------------------------------------------
// cosntructors
//---------------------------------------------------------------------------
CObjectList()
: m_iNumObjects(0)
{}
//---------------------------------------------------------------------------
// update all the objects
//---------------------------------------------------------------------------
void Update(float dt)
{
//---------------------------------------------------------------------------
// update all the objects positions
//---------------------------------------------------------------------------
for(int i = 0; i < m_iNumObjects; i ++)
{
m_pxObjects[i]->Update(dt);
}
//---------------------------------------------------------------------------
// test all objects against each other and process all collisions
//---------------------------------------------------------------------------
for(int i = 0; i < m_iNumObjects; i ++)
{
for (int j = i+1; j < m_iNumObjects; j ++)
{
//---------------------------------------------------------------------------
// test collision between two objects
//---------------------------------------------------------------------------
if (m_pxObjects[i]->IsStatic() && m_pxObjects[j]->IsStatic())
continue;
//---------------------------------------------------------------------------
// process the collision
//---------------------------------------------------------------------------
Vector xP0;
Vector xP1;
if (m_pxObjects[i]->Intersect(*m_pxObjects[j], xP0, xP1))
{
m_pxObjects[i]->ProcessCollision(*m_pxObjects[j], xP0, xP1);
}
}
}
}
//---------------------------------------------------------------------------
// render all the objects
//---------------------------------------------------------------------------
void Render() const
{
for(int i = 0; i < m_iNumObjects; i ++)
{
m_pxObjects[i]->Render();
}
}
//---------------------------------------------------------------------------
// Add object reference to list
//---------------------------------------------------------------------------
void RegisterObject(CObject* pxNewObject)
{
for(int i = 0; i < m_iNumObjects; i ++)
{
if (m_pxObjects[i] == pxNewObject)
return;
}
if (m_iNumObjects >= eMaxObjects)
return;
m_pxObjects[m_iNumObjects] = pxNewObject;
m_iNumObjects++;
}
//---------------------------------------------------------------------------
// remove object reference to list
//---------------------------------------------------------------------------
void UnregisterObject(CObject* pxOldObject)
{
int i;
for(i = 0; i < m_iNumObjects; i ++)
{
if (m_pxObjects[i] == pxOldObject)
break;
}
if (i == m_iNumObjects)
return;
m_iNumObjects--;
m_pxObjects[i] = m_pxObjects[m_iNumObjects];
}
private:
enum { eMaxObjects = 128 };
CObject* m_pxObjects[eMaxObjects];
int m_iNumObjects;
};
//---------------------------------------------------------------------------
// Collision sphere structure
//---------------------------------------------------------------------------
struct CSphere: public CObject
{
public:
//---------------------------------------------------------------------------
// Constrcutors
//---------------------------------------------------------------------------
CSphere()
{}
CSphere(const Vector& xPos, float fRad, float fMass)
: CObject(xPos, Vector(0, 0, 0), fMass)
, m_fRad(fRad)
{}
//---------------------------------------------------------------------
// Modifiers / Selectors
//---------------------------------------------------------------------
float GetRadius() const { return m_fRad; }
//---------------------------------------------------------------------
// Virtual Interface
//---------------------------------------------------------------------
virtual bool Intersect(const CObject& xObject, Vector& xP0, Vector &xP1) const
{
return xObject.Intersect(*this, xP1, xP0);
}
virtual bool Intersect(const class CAABBox& xBox, Vector& xP0, Vector &xP1) const;
virtual bool Intersect(const CSphere &xSphere, Vector& xP0, Vector& xP1) const;
virtual void Render(void) const
{
if (IsStatic())
SetMaterial(4, 1.0f);
else
SetMaterial(5, 1.0f);
glPushMatrix();
glTranslatef(m_xPos.x, m_xPos.y, m_xPos.z);
glutSolidSphere(m_fRad, 8, 8);
glPopMatrix();
}
private:
float m_fRad;
};
//---------------------------------------------------------------------------
// Axis-aligned box collision object
//---------------------------------------------------------------------------
struct CAABBox: public CObject
{
public:
//---------------------------------------------------------------------------
// Constrcutors
//---------------------------------------------------------------------------
CAABBox()
{}
CAABBox(const Vector& xPos, const Vector& xExt, float fMass)
: CObject(xPos, Vector(0, 0, 0), fMass)
, m_xExt(xExt)
{}
//---------------------------------------------------------------------
// Modifiers / Selectors
//---------------------------------------------------------------------
const Vector& GetExt() const { return m_xExt; }
//---------------------------------------------------------------------
// Virtual Interface
//---------------------------------------------------------------------
virtual bool Intersect(const CObject& xObject, Vector& xP0, Vector &xP1) const
{
return xObject.Intersect(*this, xP1, xP0);
}
virtual bool Intersect(const class CSphere& xSphere, Vector& pBox, Vector &pSphere) const;
virtual bool Intersect(const class CAABBox& xBox, Vector& xP0, Vector &xP1) const;
virtual void Render(void) const
{
if (IsStatic())
SetMaterial(2, 1.0f);
else
SetMaterial(3, 1.0f);
if (IsStatic())
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
else
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glPushMatrix();
glTranslatef(m_xPos.x, m_xPos.y, m_xPos.z);
glScalef(m_xExt.x, m_xExt.y, m_xExt.z);
glutSolidCube(2.0f);
glPopMatrix();
}
private:
Vector m_xExt;
};
//---------------------------------------------------------------------------
// MAIN INTERSECTION ROUTINES
// --------------------------
// return if two objects intersect, and where on the surface of the objects
// Note : for AABBox vs. AABBox, the test only returns teh separation vector
// ----- This is sufficient, because that's all that is needed in the collison
// ----- response. The other objects return the actual points on the surface
// ----- for demonstration pruposes.
//---------------------------------------------------------------------------
bool CSphere::Intersect(const class CAABBox& xBox, Vector& xP0, Vector &xP1) const
{
return xBox.Intersect(*this, xP1, xP0);
}
//---------------------------------------------------------------------------
// Sphere vs. Sphere collision
//---------------------------------------------------------------------------
bool CSphere::Intersect(const CSphere &xSphere, Vector& pP0, Vector& pP1) const
{
Vector pDist = xSphere.GetPosition() - GetPosition(); // relative position of sphere centre to the box centre
float dist2 = pDist * pDist; // distance of the point on the box to sphere centre, squared
float r = GetRadius() + xSphere.GetRadius();
float r2 = r * r;
if (dist2 > r2) return false; // point outside sphere, no intersection
//------------------------------------------------------
// calcualte point on sphere surface closest to point on box.
//------------------------------------------------------
pDist /= sqrt(dist2); // normalise
pP0 = GetPosition() + pDist * GetRadius();
pP1 = xSphere.GetPosition() - pDist * xSphere.GetRadius();
return true;
}
//---------------------------------------------------------------------------
// tool to calcualte the amount of overlap between two spans along an axis
//---------------------------------------------------------------------------
bool AxisIntersect(float min0, float max0, float min1, float max1, float& d)
{
float d0 = max1 - min0;
float d1 = max0 - min1;
if (d0 < 0.0f || d1 < 0.0f)
return false;
if (d0 < d1)
d = d0;
else
d = -d1;
return true;
}
//---------------------------------------------------------------------------
// AABBox vs. AABBox intersection test.
// (as stated above, it only returns the separation vector, not the collison points).
//---------------------------------------------------------------------------
bool CAABBox::Intersect(const class CAABBox& xBox, Vector& xP0, Vector &xP1) const
{
//---------------------------------------------------------------------------
// calculate the box boundaries, as it is easier that way for the test
//---------------------------------------------------------------------------
Vector xMin0 = GetPosition() - GetExt();
Vector xMax0 = GetPosition() + GetExt();
Vector xMin1 = xBox.GetPosition() - xBox.GetExt();
Vector xMax1 = xBox.GetPosition() + xBox.GetExt();
//---------------------------------------------------------------------------
// test intersection along x axis
//---------------------------------------------------------------------------
Vector N(0, 0, 0);
if (!AxisIntersect(xMin0.x, xMax0.x, xMin1.x, xMax1.x, N.x))
return false;
//---------------------------------------------------------------------------
// test intersection along y axis
//---------------------------------------------------------------------------
if (!AxisIntersect(xMin0.y, xMax0.y, xMin1.y, xMax1.y, N.y))
return false;
//---------------------------------------------------------------------------
// test intersection along z axis
//---------------------------------------------------------------------------
if (!AxisIntersect(xMin0.z, xMax0.z, xMin1.z, xMax1.z, N.z))
return false;
//---------------------------------------------------------------------------
// select the axis with the minimum of separation as the collision axis
//---------------------------------------------------------------------------
float mindist = fabs(N.x);
if (fabs(N.y) < mindist)
{
mindist = fabs(N.y);
N.x = 0.0f;
}
else
{
N.y = 0.0f;
}
if (fabs(N.z) < mindist)
{
N.x = N.y = 0.0f;
}
else
{
N.z = 0.0f;
}
xP0 = Vector(0, 0, 0);
xP1 = N;
return true;
}
//---------------------------------------------------------------------------
// AABBox vs. Sphere
//---------------------------------------------------------------------------
bool CAABBox::Intersect(const class CSphere& xSphere, Vector& pBox, Vector &pSphere) const
{
Vector pDiff = xSphere.GetPosition() - GetPosition(); // relative position of sphere centre to the box centre
Vector pExt = GetExt(); // size of the box along X, Y and Z direction.
//------------------------------------------------------
// see if sphere coords are within the box coords
//------------------------------------------------------
float dx = pExt.x - fabs(pDiff.x); // distance of sphere centre to one of the X-Face of the box
float dy = pExt.y - fabs(pDiff.y); // distance of sphere centre to one of the Y-Face of the box
float dz = pExt.z - fabs(pDiff.z); // distance of sphere centre to one of the Z-Face of the box
bool outx = (dx < 0.0f); // sphere centre between the two X-Faces of the box
bool outy = (dy < 0.0f); // sphere centre between the two Y-Faces of the box
bool outz = (dz < 0.0f); // sphere centre between the two Z-Faces of the box
bool in = !(outx|outy|outz); // sphere centre inside all the faces of the box
//------------------------------------------------------
// sphere centre in the box. deep intersection
//------------------------------------------------------
if (in)
{
//------------------------------------------------------
// find closest plane on box to the sphere centre.
//------------------------------------------------------
float mindist;
if (1) // one of the X-Face closest to the spehre centre?
{ //
mindist = dx; //
pBox = Vector(dx * sgn(pDiff.x), 0.0f, 0.0f); // which X-Face of the box closest to sphere
pSphere = Vector(-xSphere.GetRadius() * sgn(pDiff.x), 0.0f, 0.0f); // point on the sphere furthest from the face
}
if (dy < mindist) // one of the Y-Face closest to the spehre centre?
{ //
mindist = dy; //
pBox = Vector(0.0f, dy * sgn(pDiff.y), 0.0f); // which Y-Face of the box closest to sphere
pSphere = Vector(0.0f, -xSphere.GetRadius() * sgn(pDiff.y), 0.0f); // point on the sphere furthest from the face
}
if (dz < mindist) // one of the Z-Face closest to the spehre centre?
{ //
mindist = dz; //
pBox = Vector(0.0f, 0.0f, dz * sgn(pDiff.z)); // which Z-Face of the box closest to sphere
pSphere = Vector(0.0f, 0.0f, -xSphere.GetRadius() * sgn(pDiff.z)); // point on the sphere furthest from the face
}
pBox += xSphere.GetPosition();
pSphere += xSphere.GetPosition();
return true;
}
//------------------------------------------------------
// sphere centre not in the box. This is the general case
//------------------------------------------------------
else
{
//------------------------------------------------------
// find the closest plane on the box to the sphere
// (could be a corner, an edge or a face of the box).
//------------------------------------------------------
pBox = pDiff;
if (outx) pBox.x = sgn(pDiff.x) * pExt.x;
if (outy) pBox.y = sgn(pDiff.y) * pExt.y;
if (outz) pBox.z = sgn(pDiff.z) * pExt.z;
pBox += GetPosition();
//------------------------------------------------------
// see if the point on the box surface is in the sphere,
// by checking the distance of the point from the sphere
// centre against the sphere radius.
//------------------------------------------------------
Vector pDist = pBox - xSphere.GetPosition(); // relative position of point in box to the sphere centre
float dist2 = pDist * pDist; // distance of the point on the box to sphere centre, squared
float r2 = xSphere.GetRadius() * xSphere.GetRadius(); // radius of sphere, squared
if (dist2 > r2) return false; // point outside sphere, no intersection
//------------------------------------------------------
// calcualte point on sphere surface closest to point on box.
//------------------------------------------------------
pDist /= sqrt(dist2); // normalise
pDist *= xSphere.GetRadius();
pSphere = pDist;
pSphere += xSphere.GetPosition();
return true;
}
}
//-----------------------------------------------------------------------------
// Game Interface
//-----------------------------------------------------------------------------
enum { iNumSpheres = 10, iNumBoxes = 20, iNumWalls = 6 };
CAABBox xBoxes [iNumBoxes]; // all boxes in the world
CSphere xSpheres[iNumSpheres]; // all spheres in the world
CObjectList xObjectList; // all objects in the world wrapped up in a list
//-----------------------------------------------------------------------------
// add impulse to an object
//-----------------------------------------------------------------------------
void GameAddImpulseToObject(int iObjectID, const Vector& xImpulse)
{
if (iObjectID < 0 || iObjectID >= iNumSpheres)
return;
xSpheres[iObjectID].SetVelocity(xImpulse);
}
//-----------------------------------------------------------------------------
// get an object position
//-----------------------------------------------------------------------------
Vector GameGetObjectPos(int iObjectID)
{
if (iObjectID < 0 || iObjectID >= iNumSpheres)
return Vector(0, 0, 0);
return xSpheres[iObjectID].GetPosition();
}
//-----------------------------------------------------------------------------
// Init the game state
//-----------------------------------------------------------------------------
void GameInit()
{
xBoxes[0] = CAABBox (Vector(-200, 0, 0), Vector(100, 100, 100), 0.0f);
xBoxes[1] = CAABBox (Vector( 200, 0, 0), Vector(100, 100, 100), 0.0f);
xBoxes[2] = CAABBox (Vector( 0, 200, 0), Vector(100, 100, 100), 0.0f);
xBoxes[3] = CAABBox (Vector( 0,-200, 0), Vector(100, 100, 100), 0.0f);
xBoxes[4] = CAABBox (Vector( 0, 0,-200), Vector(100, 100, 100), 0.0f);
xBoxes[5] = CAABBox (Vector( 0, 0, 200), Vector(100, 100, 100), 0.0f);
for (int i = iNumWalls; i < iNumBoxes; i ++)
{
xBoxes[i] = CAABBox(Vector::Random(200) - Vector(100, 100, 100), Vector::Random(20) + Vector(10, 10, 10), frand(1000.0f) + 1000.0f);
}
for (int i = 0; i < iNumSpheres; i ++)
xSpheres[i] = CSphere(Vector::Random(200) - Vector(100, 100, 100), frand(10) + 2, frand(100.0f) + 10.0f);
for(int i = 0; i < iNumBoxes; i ++)
xObjectList.RegisterObject(&xBoxes[i]);
for(int i = 0; i < iNumSpheres; i ++)
xObjectList.RegisterObject(&xSpheres[i]);
}
//-----------------------------------------------------------------------------
// update the game state
//-----------------------------------------------------------------------------
void GameUpdate()
{
xObjectList.Update(1.0f / 60.0f);
}
//-----------------------------------------------------------------------------
// render the game
//-----------------------------------------------------------------------------
void GameRender()
{
xObjectList.Render();
}
|
|
Currently browsing [pong_collision.zip] (75,616 bytes) - [pong collision/source/game code.h] - (3,874 bytes)
//=========================================================
//
// Simple Physics Demo
//
//=========================================================
#ifndef OLI_GAME_CODE_H
#define OLI_GAME_CODE_H
#include <math.h>
#include <gl/glut.h>
//--------------------------------------------------------------------------
// random float
//--------------------------------------------------------------------------
extern float frand(float range);
extern float sgn(float x);
extern void SetMaterial(int ColorIndex, float transparency);
//===========================================================================
// VECTORS
//===========================================================================
class Vector
{
public:
float x,y,z;
public:
inline Vector(void)
{}
inline Vector(float Ix,float Iy,float Iz): x(Ix), y(Iy), z(Iz) {}
inline Vector &operator /=(const float Scalar) { *this *= (1.0f / Scalar); return *this; }
inline Vector &operator *=(const float Scalar) { x *= Scalar; y *= Scalar; z *= Scalar; return *this; }
inline Vector &operator +=(const Vector &Other) { x += Other.x; y += Other.y; z += Other.z; return *this; }
inline Vector &operator -=(const Vector &Other) { x -= Other.x; y -= Other.y; z -= Other.z; return *this; }
inline Vector& operator ^=(const Vector &V) // Cross product
{
float Tempx = (y * V.z) - (z * V.y);
float Tempy = (z * V.x) - (x * V.z);
z = (x * V.y) - (y * V.x);
x = Tempx;
y = Tempy;
return *this;
}
inline Vector operator ^ (const Vector& V) const { Vector Temp(*this); return Temp ^= V; }
inline Vector operator * (float s) const { Vector Temp(*this); return Temp *= s; };
inline Vector operator / (float s) const { Vector Temp(*this); return Temp /= s; }
inline Vector operator + (const Vector &V) const { Vector Temp(*this); return Temp += V; }
inline Vector operator - (const Vector &V) const { Vector Temp(*this); return Temp -= V; }
friend Vector operator * (float k, const Vector& V) { return V * k; } // dot product
inline float operator * (const Vector &V) const { return (x * V.x) + (y * V.y) + (z * V.z); }
inline Vector operator -(void) const { return Vector(-x, -y, -z); }
inline float GetLength(void) const { return (float) sqrt((*this) * (*this)); }
float Normalise()
{
float Length = GetLength();
if (Length == 0.0f)
return 0.0f;
(*this) *= (1.0f / Length);
return Length;
}
static Vector Random(const Vector& Radius=Vector(1.0f, 1.0f, 1.0f))
{
return Vector(frand(Radius.x), frand(Radius.y), frand(Radius.z));
}
static Vector Random(float radius)
{
return Vector(frand(radius), frand(radius), frand(radius));
}
//-------------------------------------------------------------------------
// Compute normal of a triangle. return normal length
//-------------------------------------------------------------------------
float ComputeNormal(const Vector& V0, const Vector& V1, const Vector& V2)
{
Vector E = V1; E -= V0;
Vector F = V2; F -= V1;
(*this) = E ^ F;
return (*this).Normalise();
}
void Render(void) const
{
glPointSize(3.0f);
glBegin(GL_POINTS);
glVertex3fv(&x);
glEnd();
}
static void Render(const Vector& V0, const Vector& V1)
{
V0.Render();
V1.Render();
glBegin(GL_LINES);
glVertex3fv(&V0.x);
glVertex3fv(&V1.x);
glEnd();
}
};
//------------------------------------------------------------------
// Game interface
//------------------------------------------------------------------
extern void GameInit();
extern void GameUpdate();
extern void GameRender();
extern void GameAddImpulseToObject(int iObjectID, const Vector& xImpulse);
extern Vector GameGetObjectPos(int iObjectID);
#endif //OLI_GAME_CODE_H
|
|
Currently browsing [pong_collision.zip] (75,616 bytes) - [pong collision/pong_collision_private.h] - (588 bytes)
// THIS FILE WILL BE OVERWRITTEN BY DEV-C++!
// DO NOT EDIT!
#ifndef PONG_COLLISION_PRIVATE_H
#define PONG_COLLISION_PRIVATE_H
// VERSION DEFINITIONS
#define VER_STRING "0.1.1.1"
#define VER_MAJOR 0
#define VER_MINOR 1
#define VER_RELEASE 1
#define VER_BUILD 1
#define COMPANY_NAME ""
#define FILE_VERSION ""
#define FILE_DESCRIPTION "Developed using the Dev-C++ IDE"
#define INTERNAL_NAME ""
#define LEGAL_COPYRIGHT ""
#define LEGAL_TRADEMARKS ""
#define ORIGINAL_FILENAME ""
#define PRODUCT_NAME ""
#define PRODUCT_VERSION ""
#endif //PONG_COLLISION_PRIVATE_H
|
|
The zip file viewer built into the Developer Toolbox made use
of the zlib library, as well as the zlibdll source additions.
|