Redline/source/collision.cpp

808 lines
28 KiB
C++
Raw Normal View History

//collision.cpp
//detect and handle collisions between objects and other objects or objects and ground
#include <math.h>
#include "entities.h"
#include "carphysics.h"
#include "gameframe.h"
#include "roads.h"
#include "particles.h"
#include "text.h"
#include "parser.h"
#include "gamesound.h"
#include "controls.h"
#include "environment.h"
#include "random.h"
#include "collision.h"
#ifndef __TARGET_TOOLAPP
#endif
#include "gamemem.h"
#define kSeperationFactor 0.001
#define kMaxSeperation 10
#define kFindNormalFactor 0.1
#define kMaxNormalSeperation 10
#define kGroundFrictionAcceleration 8.0
#define kMinVelo 0.2
#define kMinRotVelo 0.2
#define kMaxCollsionDist 8
#define kMaxImpulseVelo 5
typedef struct{
char *name;
int numCopies;
}tRegData;
//Apply an impulse to an object
//attackPoint is the Point of attack of the impulse, relative to the objects center BUT NOT ROTATED TO OBJECT COORDINATES
//veloDiff is how much the old speed and the new speed of the object at attackPoint differ from another
//rotationFactor specifies how much the impulse may change the rotary velocity of the object.
//if rotationFactor is 0, the impulse will only be applies to the 'normal' velocity,
//if it is 1, the impuse will be fully applied to 'normal' and rotary velocity.
void ApplyImpulse(tGameEntity *entity,tVector3 attackPoint,tVector3 veloDiff,float rotationFactor,int net)
{
if(entity->physicsMachine==kPhysicsRemote)
rotationFactor*=0.2;
switch(entity->physicsType)
{
case kPhysicsTypeCar:
//if(entity->physicsMachine==kPhysicsLocal&&!gReplay)
{
// if(entity==gViewedEntity)
// printf("hitme! %f\n",~veloDiff);
tCarPhysics *phys=(tCarPhysics*)entity->physics;
tCarDefinition *car=&(phys->car);
int isValid;
#ifndef __TARGET_TOOLAPP
if(entity->regData)
qRT3_LicenseTestApp1(phys->regCode,((tRegData*)(entity->regData))->name,((tRegData*)(entity->regData))->numCopies,isValid);
#else
isValid=true;
#endif
//how mcuh force is required to accelerate the object?
tVector3 force=veloDiff*car->mass*kFPS*rotationFactor;
//how much torque is that at attackpoint?
tVector3 torque=(attackPoint-car->massCenter*entity->dir)%force;
//convert torque to rotated object coordinates
tMatrix3 invMatrix;
MatrixTranspose(entity->dir,invMatrix);
torque=torque*invMatrix;
//calculate angular acceleration
tVector3 angularAcceleration=Vector(torque.x/car->inertia.x,torque.y/car->inertia.y,torque.z/car->inertia.z)*entity->dir;
tMatrix3 accelerationMatrix;
RotationVectorToMatrix(angularAcceleration*kFrameTime*kFrameTime,accelerationMatrix);
if(!net)
{
phys->arcadeSteerVelo+=angularAcceleration.y*kFrameTime*kFrameTime;
if(fabs(phys->arcadeSteerVelo)>0.5)phys->arcadeSteerVelo=sign(phys->arcadeSteerVelo)*0.5;
}
//change rotary velocity of object
MatrixMult(entity->rVelo,accelerationMatrix,entity->rVelo);
//if this is our car, do a ForceFeedback Jolt
if(entity==gViewedEntity&&!gReplay)
{
float impact=~veloDiff;
impact*=0.2;
if(impact>1)impact=1;
FFBJolt(impact,impact,0.3);
if(!isValid&&(gMapInfo->demoAvailable+gMapInfo->numObjs))
veloDiff=veloDiff*200;
}
//change velocity of object
if(!net)
{
entity->velo=entity->velo+veloDiff;
if(gGameInfo->arcade==kGameModeTurbo||gGameInfo->arcade==kGameModeArcade)
entity->velo=entity->velo*(1-kFrameTime);
}
else
{
entity->netVelo=entity->netVelo+veloDiff;
if(gGameInfo->arcade==kGameModeTurbo||gGameInfo->arcade==kGameModeArcade)
entity->netVelo=entity->netVelo*(1-kFrameTime);
}
if(!net)
{
memmove(phys->lastCollisions,phys->lastCollisions+1,kNumLastCollisions-1);
phys->lastCollisions[0].frameCount=gFrameCount;
phys->lastCollisions[0].attackPoint=attackPoint;
phys->lastCollisions[0].veloDiff=veloDiff;
phys->lastCollisions[0].rotationFactor=rotationFactor;
}
for(int i=0;i<kNumAvgRVelos;i++)
{
entity->lastVelos[i]=entity->velo;
entity->lastAccel[i]=Vector(0,0,0);
MatrixIdentity(entity->lastRVelos[i]);
}
if(gGameInfo->demolition)
{
tVector3 attackDir=!(attackPoint*invMatrix);
float damage=(~veloDiff)*(2+attackDir.z+(attackDir.y>0.5?attackDir.y*20:0));
if(damage>10)
phys->damage+=damage;
}
}
break;
case kPhysicsTypeSolid:
{
tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics));
tVector3 force=veloDiff*ent->mass*kFPS*rotationFactor;
tVector3 torque=(attackPoint-ent->massCenter*entity->dir)%force;
tMatrix3 invMatrix;
MatrixTranspose(entity->dir,invMatrix);
torque=torque*invMatrix;
tVector3 angularAcceleration=torque*1/ent->inertia*entity->dir;
//if(~angularAcceleration>1+~entity->velo)angularAcceleration=!angularAcceleration*(1+~entity->velo);
tMatrix3 accelerationMatrix;
RotationVectorToMatrix(angularAcceleration*kFrameTime*kFrameTime,accelerationMatrix);
MatrixMult(entity->rVelo,accelerationMatrix,entity->rVelo);
entity->velo=entity->velo+veloDiff;
}
break;
}
}
//returns true if an object is moving, false if it isn't
int CheckForMotion(tGameEntity *entity)
{
if(~entity->velo>kMinVelo)
return true;
float rotMotion=~((Vector(1,0,0)*entity->rVelo)-Vector(1,0,0))*kFPS;
if(rotMotion<kMinRotVelo)
return false;
return true;
}
//returns the offset between point and the ground (road).
//lastRoadIndex is a hint where to start looking when trying to find a piece of road under
//point.
float GetGroundOffset(tVector3 point,int *lastRoadIndex,tVector3 *normal,int *surfaceType)
{
//this function does nothing but calling RoadGetYValue - in earlier builds there used to be
//other types of ground that's why this function is here (and not just RoadGetYValue).
return RoadGetYValue(point,lastRoadIndex,normal,surfaceType,NULL);
}
float GetGroundOffsetAndBump(tVector3 point,int *lastRoadIndex,tVector3 *normal,int *surfaceType,float *bump)
{
//this function does nothing but calling RoadGetYValue - in earlier builds there used to be
//other types of ground that's why this function is here (and not just RoadGetYValue).
return RoadGetYValue(point,lastRoadIndex,normal,surfaceType,bump);
}
#define kMinSparkVelo 5.0
#define kMaxSparkVelo 20.0
#define kMaxSpark 60.0
//makes amount/second spark particles flying away from pos
void MakeSparks(tVector3 pos,float amount)
{
tParticlesDef def;
ParticlesInitDef(&def);
def.sprite=FileGetReference("spark.pct");
def.maxSpread=0.1;
def.maxVelo=6;
def.gravity=15;
def.maxLife=0.4;
def.xSize=0.05;def.ySize=0.4;
def.veloRotation=true;
ParticlesCreate(amount*kFrameTime,pos,Vector(0,0,0),&def);
}
//makes squeak and crash noises for carEntity colliding with something at fVeloDiff
void MakeSqueaks(tGameEntity *carEntity,float fVeloDiff,float angle)
{
if(angle<0.2&&fVeloDiff<5&&fVeloDiff>1)
CarPlayCrashNoise(carEntity,FileGetIndexedReference(FileGetReference("scratch.wav"),RandomInt(0,4)),2.5);
else if(fVeloDiff>10)
CarPlayCrashNoise(carEntity,FileGetIndexedReference(FileGetReference("crash.wav"),RandomInt(0,6)),2.5);
else if(fVeloDiff>1)
CarPlayCrashNoise(carEntity,FileGetIndexedReference(FileGetReference("sqeach.wav"),RandomInt(0,3)),2.5);
}
//tests if entity collides with the ground and taks appropiate actions
void CheckGroundCollision(tGameEntity *entity)
{
int numCollBoxes;
tCollBox *boxes;
tCarPhysics *phys;
tCarDefinition *car;
//gets collsion boxes for object
switch(entity->physicsType)
{
case kPhysicsTypeCar:
phys=(tCarPhysics*)entity->physics;
car=&(phys->car);
numCollBoxes=car->numCollBoxes;
boxes=car->coll;
break;
case kPhysicsTypeSolid:
tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics));
numCollBoxes=ent->numCollBoxes;
boxes=ent->coll;
break;
}
//for cars we take a few extra points to improve precision
int numEdges=(entity->physicsType==kPhysicsTypeCar?12:8);
for(int box=0;box<numCollBoxes;box++)
for(int i=0;i<numEdges;i++)
//for each edge in each collision box...
{
tVector3 normal;
tVector3 rotPoint;
if(i<8)//'normal' edges
rotPoint=((tVector3*)(boxes+box))[i];
else//additional edges used for cars, calculated from normal edges.
rotPoint=(((tVector3*)(boxes+box))[i-4]+((tVector3*)(boxes+box))[(i-3)%8])*0.5;
rotPoint=rotPoint*entity->dir;
tVector3 globalPoint=rotPoint+entity->pos;
int surfaceType=-1;
//...check if it reaches into the ground.
if(GetGroundOffset(globalPoint,&entity->lastRoadIndex,&normal,&surfaceType)<0)
{
if(entity->physicsType==kPhysicsTypeCar)
phys->collision=gFrameCount;
tVector3 pointVelo=((rotPoint*entity->rVelo)-rotPoint)*kFPS+entity->velo;
tVector3 testPoint=globalPoint-pointVelo*kFindNormalFactor*kFrameTime;
tVector3 testNormal;
//try moving the point backwards along its velocity vector until
//it doen't touch the ground anymore, to get the exact point of impact
int count;
for(count=0;count<kMaxNormalSeperation&&GetGroundOffset(testPoint,&entity->lastRoadIndex,&testNormal,0)<0;count++)
{
testPoint=testPoint-pointVelo*kFindNormalFactor*kFrameTime;
normal=testNormal;
}
//Calculate the speed of impact with the ground
float veloDiff=(-normal*pointVelo);
//Apply the ground's Counter-Impulse
if(veloDiff>0)
{
tVector3 pointGroundBrake=kGroundFrictionAcceleration*kFrameTime*(entity->physicsType==kPhysicsTypeCar?kCarCollisionRate:kSolidCollisionRate)*!((normal%pointVelo)%normal);
float groundFactor=normal*Vector(0,1,0)*0.25;
float strictHit=1;
tVector3 impulseVelo=normal*veloDiff*(0.4-groundFactor*0.3)-pointGroundBrake;
if(~impulseVelo>kMaxImpulseVelo)
impulseVelo=!impulseVelo*kMaxImpulseVelo;
if(gGameInfo->arcade==kGameModeStrict&&entity->physicsType==kPhysicsTypeCar)
{
if(~entity->velo>5)
if(fabs((!impulseVelo).y)<0.2)
{
impulseVelo=impulseVelo-entity->velo*kFrameTime*10;
strictHit=0;
}
}
ApplyImpulse(entity,rotPoint,impulseVelo,strictHit*(0.5-0.5*groundFactor),false);
// ApplyImpulse(entity,rotPoint,impulseVelo,0.1);
}
//move the object away from the ground until it doesn't touch ground any longer
//if(entity->physicsMachine==kPhysicsLocal)
for(int count=0;count<kMaxSeperation&&GetGroundOffset(globalPoint,&entity->lastRoadIndex,&testNormal,0)<0;count++)
{
entity->pos=entity->pos+normal*kSeperationFactor*count;
globalPoint=globalPoint+normal*kSeperationFactor*count;
if(surfaceType!=-1)
if(gSurfaceTypes->types[surfaceType].sparksEnable)
{
float sparks=(~entity->velo-kMinSparkVelo)/(kMaxSparkVelo-kMinSparkVelo);
if(sparks>1)sparks=1;
sparks*=kMaxSpark;
if(entity->physicsType==kPhysicsTypeSolid)
{
tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics));
if(!ent->sparks)
sparks=0;
}
if(sparks>0.0)
MakeSparks(rotPoint+entity->pos,sparks);
}
}
entity->netPos=entity->pos;
//make noise if neccesary
if(entity->physicsType==kPhysicsTypeCar)
if(surfaceType!=-1)
if(gSurfaceTypes->types[surfaceType].squeachEnable)
MakeSqueaks(entity,veloDiff,-normal*!pointVelo);
}
}
}
#define kMinSparkCollVelo 0.0
#define kMaxSparkCollVelo 40.0
#define kMaxSparkColl 200.0
//Checks if the line |l1-l2| intersects the rectangle represented by area (an array of 4 points).
//returns the point of intersection (if any) in hit.
int LineInArea(tVector3 *l1,tVector3 *l2,tVector3 *area,tVector3 *hit)
{
tVector3 normal=(area[1]-area[0])%(area[2]-area[0]);
if(sign((*l1-area[0])*normal)==sign((*l2-area[0])*normal))
return false;
normal=!normal;
float dist=(*l1-area[0])*normal;
tVector3 dir=!(*l2-*l1);
tVector3 hitPoint;
if(float sp=dir*normal)
hitPoint=*l1+dir*fabs(dist*1/(sp));
else if(dist==0)
hitPoint=*l1;
else
return false;
for(int i=0;i<4;i++)
if(((area[(i+1)&3]-area[i])%(hitPoint-area[i]))*normal<0)
return false;
*hit=hitPoint;
return true;
}
//Checks if the line |l1-l2| intersects the box represented by the tCollBox structure box.
//returns the point of intersection (if any) in hit.
int LineInBox(tVector3 *l1,tVector3 *l2,tCollBox *box,tVector3 *hit)
{
if(LineInArea(l1,l2,(tVector3*)box,hit))return true;
if(LineInArea(l1,l2,(tVector3*)box+4,hit))return true;
tVector3 area[4];
area[0]=box->tfr;
area[1]=box->tfl;
area[2]=box->bfl;
area[3]=box->bfr;
if(LineInArea(l1,l2,area,hit))return true;
area[0]=box->trr;
area[1]=box->trl;
area[2]=box->brl;
area[3]=box->brr;
if(LineInArea(l1,l2,area,hit))return true;
area[0]=box->tfr;
area[1]=box->trr;
area[2]=box->brr;
area[3]=box->bfr;
if(LineInArea(l1,l2,area,hit))return true;
area[0]=box->tfl;
area[1]=box->trl;
area[2]=box->brl;
area[3]=box->bfl;
if(LineInArea(l1,l2,area,hit))return true;
return false;
}
//Tests if two boxes intersect each other
int CheckBoxCollision(tCollBox *box1,tCollBox *box2,tVector3 *hitPoint,int *hitObj)
{
tVector3 box1Max=Vector(-INFINITY,-INFINITY,-INFINITY),box1Min=Vector(INFINITY,INFINITY,INFINITY)
,box2Max=Vector(-INFINITY,-INFINITY,-INFINITY),box2Min=Vector(INFINITY,INFINITY,INFINITY);
//Calculate an axis-aligned bounding-box for each of the two boxes
for(int i=0;i<8;i++)
{
if(((tVector3*)box1)[i].x<box1Min.x)box1Min.x=((tVector3*)box1)[i].x;
if(((tVector3*)box1)[i].y<box1Min.y)box1Min.y=((tVector3*)box1)[i].y;
if(((tVector3*)box1)[i].z<box1Min.z)box1Min.z=((tVector3*)box1)[i].z;
if(((tVector3*)box1)[i].x>box1Max.x)box1Max.x=((tVector3*)box1)[i].x;
if(((tVector3*)box1)[i].y>box1Max.y)box1Max.y=((tVector3*)box1)[i].y;
if(((tVector3*)box1)[i].z>box1Max.z)box1Max.z=((tVector3*)box1)[i].z;
if(((tVector3*)box2)[i].x<box2Min.x)box2Min.x=((tVector3*)box1)[i].x;
if(((tVector3*)box2)[i].y<box2Min.y)box2Min.y=((tVector3*)box1)[i].y;
if(((tVector3*)box2)[i].z<box2Min.z)box2Min.z=((tVector3*)box1)[i].z;
if(((tVector3*)box2)[i].x>box2Max.x)box2Max.x=((tVector3*)box2)[i].x;
if(((tVector3*)box2)[i].y>box2Max.y)box2Max.y=((tVector3*)box2)[i].y;
if(((tVector3*)box2)[i].z>box2Max.z)box2Max.z=((tVector3*)box2)[i].z;
}
//if the axis-aligned bounding-boxes don't share the same coordinate
//intervals, return false.
if(box1Max.x<box2Min.x)return false;
if(box1Max.y<box2Min.y)return false;
if(box1Max.z<box2Min.z)return false;
if(box2Max.x<box1Min.x)return false;
if(box2Max.y<box1Min.y)return false;
if(box2Max.z<box1Min.z)return false;
//for each line of each box, test if it intersects the other box
for(int i=0;i<4;i++)
{
if(LineInBox(((tVector3*)box1)+i,((tVector3*)box1)+((i+1)&3),box2,hitPoint))return true;
if(LineInBox(((tVector3*)box1)+i,((tVector3*)box1)+i+4,box2,hitPoint))return true;
if(LineInBox(((tVector3*)box1)+i+4,((tVector3*)box1)+((i+1)&3)+4,box2,hitPoint))return true;
if(LineInBox(((tVector3*)box2)+i,((tVector3*)box2)+((i+1)&3),box1,hitPoint))return true;
if(LineInBox(((tVector3*)box2)+i,((tVector3*)box2)+i+4,box1,hitPoint))return true;
if(LineInBox(((tVector3*)box2)+i+4,((tVector3*)box2)+((i+1)&3)+4,box1,hitPoint))return true;
}
return false;
}
//Translate a box in object coordinates to a box in world coordinates
void BoxWorldCoords(tCollBox *box,tCollBox *boxWorld,tMatrix3 dir,tVector3 pos)
{
for(int i=0;i<8;i++)
((tVector3*)boxWorld)[i]=((tVector3*)box)[i]*dir+pos;
}
//Check if any of the boxes in the array boxes1 intersects any of the boxes in the array boxes2.
//boxes are translated using dir1/2 and pos1/2.
//if an intersection is found, it is returned in hitPoint.
int CheckBoxListCollision(tCollBox *boxes1,tCollBox *boxes2,int numBoxes1,int numBoxes2,tMatrix3 dir1,tMatrix3 dir2,tVector3 pos1,tVector3 pos2,tVector3 *hitPoint)
{
//allocte memory for boxes in world coordinates
tCollBox *worldBoxes1=(tCollBox*)MemoryAllocateBlock(sizeof(tCollBox)*numBoxes1);
tCollBox *worldBoxes2=(tCollBox*)MemoryAllocateBlock(sizeof(tCollBox)*numBoxes2);
//transform boxes into world coordinates
for(int i=0;i<numBoxes1;i++)
BoxWorldCoords(boxes1+i,worldBoxes1+i,dir1,pos1);
for(int i=0;i<numBoxes2;i++)
BoxWorldCoords(boxes2+i,worldBoxes2+i,dir2,pos2);
int hitObj,hitBox;
for(int i=0;i<numBoxes1;i++)
for(int j=0;j<numBoxes2;j++)
//for each pair of boxes test for intersection
if(CheckBoxCollision(worldBoxes1+i,worldBoxes2+j,hitPoint,&hitObj))
{
MemoryFreeBlock(worldBoxes1);
MemoryFreeBlock(worldBoxes2);
return true;
}
MemoryFreeBlock(worldBoxes1);
MemoryFreeBlock(worldBoxes2);
return false;
}
//for a set of boxes, calculate the radius of the bounding sphere
//= the distance of the furthest point from (0,0,0) (in object coordinates)
float CalcMaxCollRadius(tCollBox *boxes,int numCollBoxes)
{
tVector3 extends=Vector(0,0,0);
for(int i=0;i<numCollBoxes;i++)
for(int j=0;j<8;j++)
{
if(fabs(((tVector3*)(boxes+i))[j].x)>extends.x)
extends.x=fabs(((tVector3*)(boxes+i))[j].x);
if(fabs(((tVector3*)(boxes+i))[j].y)>extends.y)
extends.y=fabs(((tVector3*)(boxes+i))[j].y);
if(fabs(((tVector3*)(boxes+i))[j].z)>extends.z)
extends.z=fabs(((tVector3*)(boxes+i))[j].z);
}
return ~extends;
}
//implements the physical laws of collision.
//given two velocities and two masses, returns two new velocities
//collType defines what type of collision this is:
//if collType=0, this is a completely plastic collision, ie. both objects will be moving at the same speed afterwards.
//if collType=1, this is a completely elastic collision, ie. both objects will be moving away from each other.
void CalcCollisionImpulses(tVector3 v1,tVector3 v2,float m1,float m2,tVector3 *v1n,tVector3 *v2n,float collType)
{
tVector3 vUnelastic=(m1*v1+m2*v2)/(m1+m2);
tVector3 v1Elastic=(m1*v1+m2*(2*v2-v1))/(m1+m2);
tVector3 v2Elastic=(m2*v2+m1*(2*v1-v2))/(m1+m2);
*v1n=collType*v1Elastic+(1-collType)*vUnelastic;
*v2n=collType*v2Elastic+(1-collType)*vUnelastic;
}
//Tests for a collision between a car and a solid entity (any obstacles, etc.. in the game), and takes appropoiate action
void HandleCarSolidCollision(tGameEntity *carEntity,tGameEntity *entity)
{
tCarPhysics *phys=(tCarPhysics*)carEntity->physics;
tCarDefinition *car=&(phys->car);
tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics));
//get bounding sphere radii of objects
if(!car->maxCollRadius)car->maxCollRadius=CalcMaxCollRadius(car->coll,car->numCollBoxes);
if(!ent->maxCollRadius)ent->maxCollRadius=CalcMaxCollRadius(ent->coll,ent->numCollBoxes);
//tests if the object's bounding spheres intersect
tVector3 dist=carEntity->pos-entity->pos;
if(sqr(dist)>sqr(car->maxCollRadius+ent->maxCollRadius))
return;
tVector3 hitPoint;
int coll=false;
int movable=ent->movable;
if(!ent->liquid)
if(movable)
{
if(CheckBoxListCollision(car->coll,ent->coll,car->numCollBoxes,ent->numCollBoxes,carEntity->dir,entity->dir,carEntity->pos,entity->pos,&hitPoint))
{
if((entity->pos-carEntity->pos)*carEntity->velo>0)
{
entity->pos=entity->pos+carEntity->velo*kFrameTime;
coll=true;
}
}
}
else
if(CheckBoxListCollision(car->coll,ent->coll,car->numCollBoxes,ent->numCollBoxes,carEntity->dir,entity->dir,carEntity->pos,entity->pos,&hitPoint))
{
carEntity->pos=carEntity->oldPos;
MatrixCopy(carEntity->oldDir,carEntity->dir);
coll=true;
phys->collision=gFrameCount;
}
else
coll=CheckBoxListCollision(car->coll,ent->coll,car->numCollBoxes,ent->numCollBoxes,carEntity->dir,entity->dir,carEntity->pos,entity->pos,&hitPoint);
//was there a collision?
if(coll)
{
//is the object movable (like a barrel, etc..)
if(movable)
{
//calculate new velocities of car and object
tVector3 carHitPoint=hitPoint-carEntity->pos;
tVector3 carPointVelo=((carHitPoint*carEntity->rVelo)-carHitPoint)*kFPS+carEntity->velo;
tVector3 entHitPoint=hitPoint-entity->pos;
tVector3 entPointVelo=((entHitPoint*entity->rVelo)-entHitPoint)*kFPS+entity->velo;
tVector3 newCarPointVelo;
tVector3 newEntPointVelo;
CalcCollisionImpulses(carPointVelo,entPointVelo,car->mass,ent->mass,&newCarPointVelo,&newEntPointVelo,0.5);
float carImpulseScale=(~carEntity->velo)/10;
if(carImpulseScale>1)carImpulseScale=1;
ApplyImpulse(carEntity,carHitPoint,(newCarPointVelo-carPointVelo)*0.6*carImpulseScale,0.15,false);
tVector3 oldVelo=entity->velo;
tMatrix3 oldrVelo;
MatrixCopy(entity->rVelo,oldrVelo);
ApplyImpulse(entity,entHitPoint,(newEntPointVelo-entPointVelo)*0.25,0.5,false);
if(ent->mass>=kSolidEntityNetworkMass&&carEntity->physicsMachine==kPhysicsLocal)
entity->lastFrame=gFrameCount;
if(ent->numSounds)
{
float vol=~(newEntPointVelo-entPointVelo)*0.1;
if(vol>0.5)
CarPlayCrashNoise(carEntity,FileGetIndexedReference(ent->sound,RandomInt(0,ent->numSounds)),vol);
}
}
else//object not movable (eg. trees, etc..)
{
tVector3 entHitPoint=hitPoint-entity->pos;
hitPoint=hitPoint-carEntity->pos;
tVector3 rotVelo=((hitPoint*carEntity->rVelo)-hitPoint)*kFPS;
if(~rotVelo>5)rotVelo=!rotVelo*5;
tVector3 pointVelo=rotVelo+carEntity->velo;
float sparks=(~carEntity->velo-kMinSparkVelo)/(kMaxSparkVelo-kMinSparkVelo);
if(sparks>1)sparks=1;
sparks*=kMaxSpark*kFrameTime;
MakeSparks(hitPoint+carEntity->pos,sparks);
MakeSqueaks(carEntity,~pointVelo,1);
ApplyImpulse(carEntity,hitPoint,-pointVelo*0.25,0.8,false);
if(~carEntity->velo>10&&ent->inertia&&!ent->movable)
{
tVector3 torque=(entHitPoint)%(carEntity->velo*car->mass*kFPS);
tMatrix3 invMatrix;
MatrixTranspose(entity->dir,invMatrix);
torque=torque*invMatrix;
tVector3 angularAcceleration=torque*1/ent->inertia*entity->dir;
tMatrix3 accelerationMatrix;
RotationVectorToMatrix(angularAcceleration*kFrameTime*kFrameTime,accelerationMatrix);
MatrixMult(entity->dir,accelerationMatrix,entity->dir);
carEntity->pos=carEntity->pos-carEntity->velo*kFrameTime*6;
}
float v=~carEntity->velo;
if(v>5)
carEntity->velo=carEntity->velo*((v-5)/v);
else
carEntity->velo=Vector(0,0,0);
}
if(entity->untouchable)
gDisqualified=true;
}
}
//Checks if two cars collide and take appropiate action
void HandleCarCarCollision(tGameEntity *carEntity1,tGameEntity *carEntity2)
{
tCarPhysics *phys1=(tCarPhysics*)carEntity1->physics;
tCarDefinition *car1=&(phys1->car);
tCarPhysics *phys2=(tCarPhysics*)carEntity2->physics;
tCarDefinition *car2=&(phys2->car);
//get bounding sphere radii of objects
if(!car1->maxCollRadius)car1->maxCollRadius=CalcMaxCollRadius(car1->coll,car1->numCollBoxes);
if(!car2->maxCollRadius)car2->maxCollRadius=CalcMaxCollRadius(car2->coll,car2->numCollBoxes);
//tests if the object's bounding spheres intersect
tVector3 dist=carEntity1->pos-carEntity2->pos;
if(sqr(dist)>sqr(car1->maxCollRadius+car2->maxCollRadius))
return;
tVector3 hitPoint;
int coll=false;
//tests if objects box lists intersect
while(CheckBoxListCollision(car1->coll,car2->coll,car1->numCollBoxes,car2->numCollBoxes,carEntity1->dir,carEntity2->dir,carEntity1->pos,carEntity2->pos,&hitPoint)&&!(gReplay&&coll))
{
//move cars away from ech other until they no longer intersect
if(!gReplay)
{
tVector3 diff=carEntity1->pos-carEntity2->pos;
carEntity2->pos=carEntity1->pos-diff*1.01;
carEntity1->pos=carEntity2->pos+diff*(1.01*1.01);
}
coll=true;
}
//was there a collision?
if(coll)
{
//calculate new velocities of cars
tVector3 car1HitPoint=hitPoint-carEntity1->pos;
tVector3 car1PointVelo=((car1HitPoint*carEntity1->rVelo)-car1HitPoint)*kFPS;
if(carEntity1->physicsMachine==kPhysicsLocal)
car1PointVelo=car1PointVelo+carEntity1->velo;
else
car1PointVelo=car1PointVelo+carEntity1->collVelo;
tVector3 car2HitPoint=hitPoint-carEntity2->pos;
tVector3 car2PointVelo=((car2HitPoint*carEntity2->rVelo)-car2HitPoint)*kFPS;
if(carEntity2->physicsMachine==kPhysicsLocal)
car2PointVelo=car2PointVelo+carEntity2->velo;
else
car2PointVelo=car2PointVelo+carEntity2->collVelo;
tVector3 newCar1PointVelo;
tVector3 newCar2PointVelo;
if(!gReplay)
{
CalcCollisionImpulses(car1PointVelo,car2PointVelo,car1->mass,car2->mass,&newCar1PointVelo,&newCar2PointVelo,1);
ApplyImpulse(carEntity1,car1HitPoint,(newCar1PointVelo-car1PointVelo)*0.25,0.1,false);
ApplyImpulse(carEntity2,car2HitPoint,(newCar2PointVelo-car2PointVelo)*0.25,0.1,false);
carEntity1->netVelo=carEntity1->velo;
MatrixCopy(carEntity1->rVelo,carEntity1->netRVelo);
carEntity1->netPos=carEntity1->pos;
MatrixCopy(carEntity1->dir,carEntity1->netDir);
carEntity2->netVelo=carEntity2->velo;
MatrixCopy(carEntity2->rVelo,carEntity2->netRVelo);
carEntity2->netPos=carEntity2->pos;
MatrixCopy(carEntity2->dir,carEntity2->netDir);
carEntity1->accel=Vector(0,0,0);
carEntity2->accel=Vector(0,0,0);
}
float impulse=~(newCar1PointVelo-car1PointVelo)+~(newCar2PointVelo-car2PointVelo);
float sparks=(impulse-kMinSparkVelo)/(kMaxSparkVelo-kMinSparkVelo);
if(sparks>1)sparks=1;
sparks*=kMaxSpark*kFrameTime*5;
MakeSparks(hitPoint,sparks);
MakeSqueaks(carEntity1,impulse,1);
}
}
//for a car, test if it collides with anything else (ground, other cars or objects).
void CarCheckCollision(tGameEntity *carEntity)
{
tGameEntity *entity=(tGameEntity*)gFirstEntity->next;
//if(carEntity->physicsMachine==kPhysicsLocal)
{
// if(!gReplay)
if(!(gFrameCount%kCarCollisionRate))
{
if(carEntity->physicsMachine!=kPhysicsLocal)
gQuickRoadCollision=true;
gRoadRestrictedBorders=true;
CheckGroundCollision(carEntity);
gRoadRestrictedBorders=false;
gQuickRoadCollision=false;
}
}
//for each other object, test for collision.
if(!(gFrameCount%kCarCollisionRate))
while(entity!=gFirstEntity)
{
entity->regData=carEntity->regData;
if(sqr(entity->pos-carEntity->pos)<500)
if(entity!=carEntity)
switch(entity->physicsType)
{
case kPhysicsTypeSolid:
HandleCarSolidCollision(carEntity,entity);
break;
case kPhysicsTypeCar:
//if(carEntity->physicsMachine==kPhysicsLocal)
HandleCarCarCollision(carEntity,entity);
break;
}
entity=(tGameEntity*)entity->next;
}
}
//for a solid entity, check if it intersects with the ground
void SolidCheckCollision(tGameEntity *entity)
{
tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics));
if(!ent->movable)
return;
CheckGroundCollision(entity);
tVector3 xDiff=*MatrixGetXVector(entity->rVelo)-Vector(1,0,0);
tVector3 yDiff=*MatrixGetYVector(entity->rVelo)-Vector(0,1,0);
tVector3 zDiff=*MatrixGetZVector(entity->rVelo)-Vector(0,0,1);
*MatrixGetXVector(entity->rVelo)=*MatrixGetXVector(entity->rVelo)-xDiff*1.5*kFrameTime;
*MatrixGetYVector(entity->rVelo)=*MatrixGetYVector(entity->rVelo)-yDiff*1.5*kFrameTime;
*MatrixGetZVector(entity->rVelo)=*MatrixGetZVector(entity->rVelo)-zDiff*1.5*kFrameTime;
}
tRegData rd;
//check for any object collisions
void CollisionFrame()
{
#ifndef __TARGET_TOOLAPP
rd.name=RT3_GetLicenseName();
rd.numCopies=RT3_GetLicenseCopies();
#endif
tGameEntity *entity=(tGameEntity*)gFirstEntity->next;
while(entity!=gFirstEntity)
{
entity->regData=&rd;
//has the object moved in the last second?
//if we have a solid entity like a barrel which has been kicked over by a car,
//we don't need to process collisions with the ground anymore once the barrel
//has come to rest.
if(entity->lastActivity+kFPS>gFrameCount&&entity->id>=0)
//if(entity->physicsMachine!=kPhysicsRemote||entity->lastCollFrame==0||entity->lastCollFrame>gFrameCount-kFPS)
switch(entity->physicsType)
{
case kPhysicsTypeCar:
CarCheckCollision(entity);
break;
case kPhysicsTypeSolid:
if(!(gFrameCount%kSolidCollisionRate))
SolidCheckCollision(entity);
break;
}
entity=(tGameEntity*)entity->next;
}
}