jedi-academy/codemp/game/g_exphysics.c
2013-04-23 15:21:39 +10:00

232 lines
6.9 KiB
C

// Copyright (C) 2000-2002 Raven Software, Inc.
//
/*****************************************************************************
* name: g_exphysics.c
*
* desc: Custom physics system (Expensive Physics)
*
* $Author: osman $
* $Revision: 1.4 $
*
*****************************************************************************/
#pragma warning(disable : 4701) //local variable may be used without having been initialized
#include "g_local.h"
#define MAX_GRAVITY_PULL 512
//Run physics on the object (purely origin-related) using custom epVelocity entity
//state value. Origin smoothing on the client is expected to compensate for choppy
//movement.
void G_RunExPhys(gentity_t *ent, float gravity, float mass, float bounce, qboolean autoKill, int *g2Bolts, int numG2Bolts)
{
trace_t tr;
vec3_t projectedOrigin;
vec3_t vNorm;
vec3_t ground;
float velScaling = 0.1f;
float vTotal = 0.0f;
assert(mass <= 1.0f && mass >= 0.01f);
if (gravity)
{ //factor it in before we do anything.
VectorCopy(ent->r.currentOrigin, ground);
ground[2] -= 0.1f;
trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ground, ent->s.number, ent->clipmask);
if (tr.fraction == 1.0f)
{
ent->s.groundEntityNum = ENTITYNUM_NONE;
}
else
{
ent->s.groundEntityNum = tr.entityNum;
}
if (ent->s.groundEntityNum == ENTITYNUM_NONE)
{
ent->epGravFactor += gravity;
if (ent->epGravFactor > MAX_GRAVITY_PULL)
{ //cap it off if needed
ent->epGravFactor = MAX_GRAVITY_PULL;
}
ent->epVelocity[2] -= ent->epGravFactor;
}
else
{ //if we're sitting on something then reset the gravity factor.
ent->epGravFactor = 0;
}
}
if (!ent->epVelocity[0] && !ent->epVelocity[1] && !ent->epVelocity[2])
{ //nothing to do if we have no velocity even after gravity.
if (ent->touch)
{ //call touch if we're in something
trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ent->s.number, ent->clipmask);
if (tr.startsolid || tr.allsolid)
{
ent->touch(ent, &g_entities[tr.entityNum], &tr);
}
}
return;
}
//get the projected origin based on velocity.
VectorMA(ent->r.currentOrigin, velScaling, ent->epVelocity, projectedOrigin);
VectorScale(ent->epVelocity, 1.0f-mass, ent->epVelocity); //scale it down based on mass
VectorCopy(ent->epVelocity, vNorm);
vTotal = VectorNormalize(vNorm);
if (vTotal < 1 && ent->s.groundEntityNum != ENTITYNUM_NONE)
{ //we've pretty much stopped moving anyway, just clear it out then.
VectorClear(ent->epVelocity);
ent->epGravFactor = 0;
trap_LinkEntity(ent);
return;
}
if (ent->ghoul2 && g2Bolts)
{ //Have we been passed a bolt index array to clip against points on the skeleton?
vec3_t tMins, tMaxs;
vec3_t trajDif;
vec3_t gbmAngles;
vec3_t boneOrg;
vec3_t projectedBoneOrg;
vec3_t collisionRootPos;
mdxaBone_t matrix;
trace_t bestCollision;
qboolean hasFirstCollision = qfalse;
int i = 0;
//Maybe we could use a trap call and get the default radius for the bone specified,
//but this will do at least for now.
VectorSet(tMins, -3, -3, -3);
VectorSet(tMaxs, 3, 3, 3);
gbmAngles[PITCH] = gbmAngles[ROLL] = 0;
gbmAngles[YAW] = ent->s.apos.trBase[YAW];
//Get the difference relative to the entity origin and projected origin, to add to each bolt position.
VectorSubtract(ent->r.currentOrigin, projectedOrigin, trajDif);
while (i < numG2Bolts)
{
//Get the position of the actual bolt for this frame
trap_G2API_GetBoltMatrix(ent->ghoul2, 0, g2Bolts[i], &matrix, gbmAngles, ent->r.currentOrigin, level.time, NULL, ent->modelScale);
BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boneOrg);
//Now add the projected positional difference into the result
VectorAdd(boneOrg, trajDif, projectedBoneOrg);
trap_Trace(&tr, boneOrg, tMins, tMaxs, projectedBoneOrg, ent->s.number, ent->clipmask);
if (tr.fraction != 1.0 || tr.startsolid || tr.allsolid)
{ //we've hit something
//Store the "deepest" collision we have
if (!hasFirstCollision)
{ //don't have one yet so just use this one
bestCollision = tr;
VectorCopy(boneOrg, collisionRootPos);
hasFirstCollision = qtrue;
}
else
{
if (tr.allsolid && !bestCollision.allsolid)
{ //If the whole trace is solid then this one is deeper
bestCollision = tr;
VectorCopy(boneOrg, collisionRootPos);
}
else if (tr.startsolid && !bestCollision.startsolid && !bestCollision.allsolid)
{ //Next deepest is if it's startsolid
bestCollision = tr;
VectorCopy(boneOrg, collisionRootPos);
}
else if (!bestCollision.startsolid && !bestCollision.allsolid &&
tr.fraction < bestCollision.fraction)
{ //and finally, if neither is startsolid/allsolid, but the new one has a smaller fraction, then it's closer to an impact point so we will use it
bestCollision = tr;
VectorCopy(boneOrg, collisionRootPos);
}
}
}
i++;
}
if (hasFirstCollision)
{ //at least one bolt collided
//We'll get the offset between the collided bolt and endpos, then trace there
//from the origin so that our desired position becomes that point.
VectorSubtract(collisionRootPos, bestCollision.endpos, trajDif);
VectorAdd(ent->r.currentOrigin, trajDif, projectedOrigin);
}
}
//If we didn't collide with any bolts projectedOrigin will still be the original desired
//projected position so all is well. If we did then projectedOrigin will be modified
//to provide us with a relative position which does not place the bolt in a solid.
trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, projectedOrigin, ent->s.number, ent->clipmask);
if (tr.startsolid || tr.allsolid)
{ //can't go anywhere from here
#ifdef _DEBUG
Com_Printf("ExPhys object in solid (%i)\n", ent->s.number);
#endif
if (autoKill)
{
ent->think = G_FreeEntity;
ent->nextthink = level.time;
}
return;
}
//Go ahead and set it to the trace endpoint regardless of what it hit
G_SetOrigin(ent, tr.endpos);
trap_LinkEntity(ent);
if (tr.fraction == 1.0f)
{ //Nothing was in the way.
return;
}
if (bounce)
{
vTotal *= bounce; //scale it by bounce
VectorScale(tr.plane.normal, vTotal, vNorm); //scale the trace plane normal by the bounce factor
if (vNorm[2] > 0)
{
ent->epGravFactor -= vNorm[2]*(1.0f-mass); //The lighter it is the more gravity will be reduced by bouncing vertically.
if (ent->epGravFactor < 0)
{
ent->epGravFactor = 0;
}
}
//call touch first so we can check velocity upon impact if we want
if (tr.entityNum != ENTITYNUM_NONE && ent->touch)
{ //then call the touch function
ent->touch(ent, &g_entities[tr.entityNum], &tr);
}
VectorAdd(ent->epVelocity, vNorm, ent->epVelocity); //add it into the existing velocity.
}
else
{ //if no bounce, kill when it hits something.
ent->epVelocity[0] = 0;
ent->epVelocity[1] = 0;
if (!gravity)
{
ent->epVelocity[2] = 0;
}
}
}