mirror of
https://github.com/Q3Rally-Team/q3rally.git
synced 2024-11-26 22:01:50 +00:00
2392 lines
74 KiB
C
2392 lines
74 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
Copyright (C) 2002-2021 Q3Rally Team (Per Thormann - q3rally@gmail.com)
|
|
|
|
This file is part of q3rally source code.
|
|
|
|
q3rally source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
q3rally source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with q3rally; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "../qcommon/q_shared.h"
|
|
#ifdef GAME
|
|
#include "g_local.h"
|
|
#else
|
|
#include "bg_public.h"
|
|
//#include "../cgame/cg_local.h"
|
|
#endif
|
|
#include "bg_local.h"
|
|
|
|
float CP_CURRENT_GRAVITY;
|
|
|
|
// not actually used now, use cvars instead
|
|
float CP_SPRING_STRENGTH = 110 * (CP_FRAME_MASS / 350.0f) * CP_GRAVITY; // 110
|
|
float CP_SHOCK_STRENGTH = 13 * CP_GRAVITY; // 12
|
|
float CP_SWAYBAR_STRENGTH = 21 * CP_GRAVITY; // 20 * grav
|
|
|
|
float CP_M_2_QU = CP_FT_2_QU / CP_FT_2_M; // 35.66
|
|
float CP_WR_STRENGTH = 2400.0f * CP_WHEEL_MASS;
|
|
float CP_WR_DAMP_STRENGTH = 140.0f * CP_WHEEL_MASS;
|
|
|
|
|
|
static int numTraces;
|
|
|
|
/*
|
|
================================================================================
|
|
PM_DebugDynamics
|
|
================================================================================
|
|
*/
|
|
void PM_DebugDynamics( carBody_t *body, carPoint_t *points ){
|
|
Com_Printf("\n");
|
|
Com_Printf("PM_DebugDynamics: point ORIGIN - %.3f, %.3f, %.3f\n", points[pm->pDebug-1].r[0], points[pm->pDebug-1].r[1], points[pm->pDebug-1].r[2]);
|
|
Com_Printf("PM_DebugDynamics: point VELOCITY - %.3f, %.3f, %.3f\n", points[pm->pDebug-1].v[0], points[pm->pDebug-1].v[1], points[pm->pDebug-1].v[2]);
|
|
|
|
Com_Printf("PM_DebugDynamics: point ONGROUND - %i\n", points[pm->pDebug-1].onGround);
|
|
Com_Printf("PM_DebugDynamics: point NORMAL 1- %.3f, %.3f, %.3f\n", points[pm->pDebug-1].normals[0][0], points[pm->pDebug-1].normals[0][1], points[pm->pDebug-1].normals[0][2]);
|
|
Com_Printf("PM_DebugDynamics: point NORMAL 2- %.3f, %.3f, %.3f\n", points[pm->pDebug-1].normals[1][0], points[pm->pDebug-1].normals[1][1], points[pm->pDebug-1].normals[1][2]);
|
|
Com_Printf("PM_DebugDynamics: point NORMAL 3- %.3f, %.3f, %.3f\n", points[pm->pDebug-1].normals[2][0], points[pm->pDebug-1].normals[2][1], points[pm->pDebug-1].normals[2][2]);
|
|
// Com_Printf("PM_DebugDynamics: point MASS - %.3f\n", points[pm->pDebug-1].mass);
|
|
|
|
Com_Printf("PM_DebugDynamics: body ORIGIN - %.3f, %.3f, %.3f\n", body->r[0], body->r[1], body->r[2]);
|
|
Com_Printf("PM_DebugDynamics: body VELOCITY - %.3f, %.3f, %.3f\n", body->v[0], body->v[1], body->v[2]);
|
|
Com_Printf("PM_DebugDynamics: body ANG VELOCITY - %.3f, %.3f, %.3f\n", body->w[0], body->w[1], body->w[2]);
|
|
|
|
// Com_Printf("PM_DebugDynamics: body COM - %.3f, %.3f, %.3f\n", body->CoM[0], body->CoM[1], body->CoM[2]);
|
|
// Com_Printf("PM_DebugDynamics: body MASS - %.3f\n", body->mass);
|
|
|
|
Com_Printf("Direction vectors ----------------------------------------\n");
|
|
|
|
Com_Printf("PM_DebugDynamics: body FORWARD - %.3f, %.3f, %.3f\n", body->forward[0], body->forward[1], body->forward[2]);
|
|
Com_Printf("PM_DebugDynamics: body RIGHT - %.3f, %.3f, %.3f\n", body->right[0], body->right[1], body->right[2]);
|
|
Com_Printf("PM_DebugDynamics: body UP - %.3f, %.3f, %.3f\n", body->up[0], body->up[1], body->up[2]);
|
|
Com_Printf("\n");
|
|
}
|
|
|
|
|
|
/*
|
|
================================================================================
|
|
PM_DebugForces
|
|
================================================================================
|
|
*/
|
|
void PM_DebugForces( carBody_t *body, carPoint_t *points ){
|
|
Com_Printf("\n");
|
|
Com_Printf("PM_DebugForces: point GRAVITY - %.3f, %.3f, %.3f\n", points[pm->pDebug-1].forces[GRAVITY][0], points[pm->pDebug-1].forces[GRAVITY][1], points[pm->pDebug-1].forces[GRAVITY][2]);
|
|
Com_Printf("PM_DebugForces: point NORMAL - %.3f, %.3f, %.3f\n", points[pm->pDebug-1].forces[NORMAL][0], points[pm->pDebug-1].forces[NORMAL][1], points[pm->pDebug-1].forces[NORMAL][2]);
|
|
Com_Printf("PM_DebugForces: point SHOCK - %.3f, %.3f, %.3f\n", points[pm->pDebug-1].forces[SHOCK][0], points[pm->pDebug-1].forces[SHOCK][1], points[pm->pDebug-1].forces[SHOCK][2]);
|
|
Com_Printf("PM_DebugForces: point SPRING - %.3f, %.3f, %.3f\n", points[pm->pDebug-1].forces[SPRING][0], points[pm->pDebug-1].forces[SPRING][1], points[pm->pDebug-1].forces[SPRING][2]);
|
|
Com_Printf("PM_DebugForces: point ROAD - %.3f, %.3f, %.3f\n", points[pm->pDebug-1].forces[ROAD][0], points[pm->pDebug-1].forces[ROAD][1], points[pm->pDebug-1].forces[ROAD][2]);
|
|
Com_Printf("PM_DebugForces: point INTERNAL - %.3f, %.3f, %.3f\n", points[pm->pDebug-1].forces[INTERNAL][0], points[pm->pDebug-1].forces[INTERNAL][1], points[pm->pDebug-1].forces[INTERNAL][2]);
|
|
Com_Printf("PM_DebugForces: point AIR_FRICTION - %.3f, %.3f, %.3f\n", points[pm->pDebug-1].forces[AIR_FRICTION][0], points[pm->pDebug-1].forces[AIR_FRICTION][1], points[pm->pDebug-1].forces[AIR_FRICTION][2]);
|
|
|
|
Com_Printf("PM_DebugForces: point NETFORCE - %.3f, %.3f, %.3f\n", points[pm->pDebug-1].netForce[0], points[pm->pDebug-1].netForce[1], points[pm->pDebug-1].netForce[2]);
|
|
|
|
Com_Printf("PM_DebugForces: body netForce - %.3f, %.3f, %.3f\n", body->netForce[0], body->netForce[1], body->netForce[2]);
|
|
Com_Printf("PM_DebugForces: body netMoment - %.3f, %.3f, %.3f\n", body->netMoment[0], body->netMoment[1], body->netMoment[2]);
|
|
Com_Printf("\n");
|
|
}
|
|
|
|
|
|
/*
|
|
================================================================================
|
|
PM_CopyTargetToSource
|
|
================================================================================
|
|
*/
|
|
static void PM_CopyTargetToSource( carBody_t *tBody, carBody_t *sBody, carPoint_t *tPoints, carPoint_t *sPoints){
|
|
memcpy(sBody, tBody, sizeof(carBody_t));
|
|
memcpy(sPoints, tPoints, NUM_CAR_POINTS * sizeof(carPoint_t));
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
PM_SetCoM
|
|
===================
|
|
*/
|
|
void PM_SetCoM( carBody_t *body, carPoint_t *points ){
|
|
vec3_t temp, origin;
|
|
//vec3_t delta;
|
|
float totalMass, length;
|
|
int i;
|
|
|
|
VectorClear(temp);
|
|
totalMass = CP_CAR_MASS;
|
|
|
|
for (i = FIRST_FRAME_POINT; i < LAST_FRAME_POINT; i++){
|
|
VectorMA(temp, points[i].mass, points[i].r, temp);
|
|
// totalMass += points[i].mass;
|
|
}
|
|
|
|
for (i = 0; i < FIRST_FRAME_POINT; i++){
|
|
// VectorSubtract( points[i+4].r, points[i].r, delta );
|
|
// length = DotProduct( body->up, delta );
|
|
length = body->curSpringLengths[i];
|
|
|
|
if ( length < CP_SPRING_MINLEN )
|
|
{
|
|
length = CP_SPRING_MINLEN;
|
|
VectorMA( points[i+4].r, length, body->up, origin );
|
|
VectorMA( temp, points[i].mass, origin, temp );
|
|
}
|
|
else if ( length > CP_SPRING_MAXLEN )
|
|
{
|
|
length = CP_SPRING_MAXLEN;
|
|
VectorMA( points[i+4].r, length, body->up, origin );
|
|
VectorMA( temp, points[i].mass, origin, temp );
|
|
}
|
|
else
|
|
VectorMA( temp, points[i].mass, points[i].r, temp );
|
|
|
|
// totalMass += points[i].mass;
|
|
}
|
|
|
|
VectorScale( temp, 1.0f / totalMass, body->CoM );
|
|
}
|
|
|
|
|
|
/*
|
|
===========================================================================================
|
|
POINT FUNCTIONS
|
|
===========================================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
================================================================================
|
|
PM_ClearCarForces
|
|
================================================================================
|
|
*/
|
|
static void PM_ClearCarForces( carBody_t *body, carPoint_t *points){
|
|
int i, j;
|
|
|
|
VectorClear( body->netForce );
|
|
VectorClear( body->netMoment);
|
|
|
|
for ( i = 0; i < NUM_CAR_POINTS; i++ ){
|
|
VectorClear( points[i].netForce );
|
|
points[i].netMoment = 0;
|
|
for ( j = 0; j < NUM_CAR_FORCES; j++ ){
|
|
VectorClear( points[i].forces[j] );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
PM_CalculateNetForce
|
|
===================
|
|
*/
|
|
void PM_CalculateNetForce( carPoint_t *point, int pointIndex ){
|
|
int i;
|
|
float dot;
|
|
// vec3_t vecForce, dV;
|
|
|
|
VectorClear( point->netForce );
|
|
|
|
for (i = 0; i < NUM_CAR_FORCES; i++){
|
|
if (i == NORMAL) continue;
|
|
/*
|
|
if (VectorLength(point->forces[i]) >= 1<<23){
|
|
Com_Printf("PM_CalculateNetForce: Car point force %d is greater than %d\n", i, 1<<23);
|
|
Com_Printf("PM_CalculateNetForce: Car point force is %f\n", VectorLength(point->forces[i]));
|
|
|
|
VectorNormalize(point->forces[i]);
|
|
VectorScale(point->forces[i], 1<<23, point->forces[i]);
|
|
}
|
|
*/
|
|
if ( VectorNAN( point->forces[i] ) )
|
|
{
|
|
Com_Printf( "Force %i on point %i was NAN\n", i, pointIndex );
|
|
continue;
|
|
}
|
|
|
|
VectorAdd( point->netForce, point->forces[i], point->netForce );
|
|
}
|
|
|
|
VectorClear( point->forces[NORMAL] );
|
|
// VectorClear( vecForce );
|
|
|
|
// calculate normal force
|
|
if ( point->onGround )
|
|
{
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
if ( VectorLengthSquared( point->normals[i] ) < 10e-2 ) continue;
|
|
|
|
// calculate new velocity of wheel after collision
|
|
dot = DotProduct( point->normals[i], point->v );
|
|
if ( dot < -10.0f )
|
|
{
|
|
// VectorScale(point->normals[i], -(1 + point->elasticity) * dot, dV);
|
|
// VectorAdd(point->v, dV, point->v);
|
|
VectorMA( point->v, -(1.0f + point->elasticity) * dot, point->normals[i], point->v );
|
|
}
|
|
else if ( dot < 1.0f )
|
|
{
|
|
// going slow enough so just stop it so it doesnt vibrate
|
|
// VectorScale(point->normals[i], -dot, dV);
|
|
// VectorAdd(point->v, dV, point->v);
|
|
VectorMA( point->v, -dot, point->normals[i], point->v );
|
|
}
|
|
|
|
// add normal force to balance the other forces
|
|
dot = OVERCLIP * DotProduct( point->normals[i], point->netForce );
|
|
if (dot < 0)
|
|
{
|
|
// VectorScale(point->normals[i], -dot, vecForce);
|
|
// VectorAdd(point->forces[NORMAL], vecForce, point->forces[NORMAL]);
|
|
VectorMA( point->forces[NORMAL], -dot, point->normals[i], point->forces[NORMAL] );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (VectorLength(point->forces[NORMAL]) >= 1<<23) {
|
|
Com_Printf("PM_CalculateNetForce: Car point normal force is greater than %d\n", 1<<23);
|
|
|
|
VectorNormalize(point->forces[NORMAL]);
|
|
VectorScale(point->forces[NORMAL], 1<<23, point->forces[NORMAL]);
|
|
}
|
|
*/
|
|
VectorAdd( point->netForce, point->forces[NORMAL], point->netForce );
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
PM_AccelerateAndMove
|
|
===================
|
|
*/
|
|
static void PM_AccelerateAndMove( car_t *car, carPoint_t *source, carPoint_t *target, float time )
|
|
{
|
|
float alpha;
|
|
vec3_t vec, avgAccel;
|
|
// vec3_t avgVel;
|
|
|
|
// kill car if forces too high
|
|
if ( VectorNAN( source->netForce ) )
|
|
{
|
|
Com_Printf( "Blowing up car because of car point force\n" );
|
|
pm->damage.damage = 32000;
|
|
pm->damage.dflags = DAMAGE_NO_PROTECTION;
|
|
pm->damage.otherEnt = -1;
|
|
pm->damage.mod = MOD_HIGH_FORCES;
|
|
return;
|
|
}
|
|
|
|
// using const acceleration
|
|
|
|
// NOTE: this method is really not right but it works so oh well.
|
|
|
|
// target has last netForce still in it
|
|
VectorScale( source->netForce, 0.4f / source->mass, avgAccel );
|
|
VectorMA( source->v, time, avgAccel, target->v );
|
|
|
|
VectorAdd( source->v, target->v, vec );
|
|
VectorMA( source->r, time / 2, vec, target->r );
|
|
|
|
// UPDATE: use a moment of inertia different than .5*mr^2?
|
|
alpha = (source->netMoment + target->netMoment) / (0.75f * source->mass * WHEEL_RADIUS * WHEEL_RADIUS);
|
|
target->w = source->w + (time / 6.0f) * alpha;
|
|
|
|
if (source->w < 0 && target->w > 0)
|
|
{
|
|
target->w = 0;
|
|
target->netMoment = 0;
|
|
}
|
|
if (source->w > 0 && target->w < 0)
|
|
{
|
|
target->w = 0;
|
|
target->netMoment = 0;
|
|
}
|
|
|
|
// wheels are trying to turn in the wrong direction..
|
|
// FIXME: use force in opposite diretion instead of just
|
|
// stopping the wheels from turning.
|
|
// Play transmission grinding sound if the force is too big
|
|
|
|
// PHYSICS TEMP
|
|
if ( car->gear > 0 && target->w > -1.5f )
|
|
{
|
|
// Com_Printf("You're screwing up your transmission %f\n", target->w);
|
|
target->w = 0;
|
|
}
|
|
else if ( car->gear < 0 && target->w < 1.5f )
|
|
{
|
|
// Com_Printf("You're screwing up your transmission %f\n", target->w);
|
|
target->w = 0;
|
|
}
|
|
|
|
target->slipping = source->slipping;
|
|
|
|
// copy netForce and netMoment to target so we can use it during the AccelerateandMove next frame
|
|
VectorCopy( source->netForce, target->netForce );
|
|
target->netMoment = source->netMoment;
|
|
}
|
|
|
|
|
|
/*
|
|
===========================================================================================
|
|
INITIALIZATION FUNCTIONS
|
|
===========================================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
===================
|
|
PM_UpdateFrameVelocities
|
|
===================
|
|
*/
|
|
static void PM_UpdateFrameVelocities( carBody_t *body, carPoint_t *points )
|
|
{
|
|
vec3_t arm;
|
|
vec3_t cross;
|
|
int i;
|
|
|
|
for ( i = FIRST_FRAME_POINT; i < NUM_CAR_POINTS; i++ )
|
|
{
|
|
VectorSubtract( points[i].r, body->CoM, arm );
|
|
CrossProduct( body->w, arm, cross );
|
|
VectorAdd( body->v, cross, points[i].v );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
PM_InitializeFrame
|
|
===================
|
|
*/
|
|
static void PM_InitializeFrame( carBody_t *body, carPoint_t *point, float f, float r, float u )
|
|
{
|
|
vec3_t arm;
|
|
vec3_t cross;
|
|
|
|
VectorScale( body->forward, f * WHEEL_FORWARD, arm );
|
|
VectorMA( arm, r * WHEEL_RIGHT, body->right, arm );
|
|
VectorMA( arm, -u * WHEEL_UP, body->up, arm );
|
|
VectorAdd( body->r, arm, point->r );
|
|
|
|
CrossProduct( body->w, arm, cross );
|
|
VectorAdd( body->v, cross, point->v );
|
|
|
|
VectorClear( point->normals[0] );
|
|
VectorClear( point->normals[1] );
|
|
VectorClear( point->normals[2] );
|
|
|
|
point->onGround = qfalse;
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
PM_InitializeWheel
|
|
===================
|
|
*/
|
|
static void PM_InitializeWheel( carBody_t *body, carPoint_t *points, int i, int f, int r )
|
|
{
|
|
vec3_t arm;
|
|
vec3_t cross;
|
|
|
|
VectorScale( body->forward, f * WHEEL_FORWARD, arm );
|
|
VectorMA( arm, r * WHEEL_RIGHT, body->right, arm );
|
|
VectorMA( arm, WHEEL_UP, body->up, arm );
|
|
VectorAdd( body->r, arm, points[i].r );
|
|
|
|
CrossProduct( body->w, arm, cross );
|
|
VectorAdd( body->v, cross, points[i].v );
|
|
|
|
VectorClear( points[i].normals[0] );
|
|
VectorClear( points[i].normals[1] );
|
|
VectorClear( points[i].normals[2] );
|
|
|
|
// UPDATE: added for client prediction, should just leave normals instead?
|
|
points[i].onGround = qfalse;
|
|
|
|
// recalculate spring length
|
|
VectorSubtract( points[i+4].r, points[i].r, arm );
|
|
body->curSpringLengths[i] = DotProduct( arm, body->up );
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
PM_InitializeVehicle
|
|
===================
|
|
*/
|
|
void PM_InitializeVehicle( car_t *car, vec3_t origin, vec3_t angles, vec3_t velocity ){
|
|
float m[3][3];
|
|
int i;
|
|
float halfWidth, halfLength, halfHeight;
|
|
float forwardScale, rightScale, upScale;
|
|
|
|
// UPDATE: use memset?
|
|
|
|
VectorCopy(origin, car->sBody.r);
|
|
AnglesToOrientation(angles, car->sBody.t);
|
|
// AnglesToQuaternion(angles, car->sBody.q);
|
|
OrientationToVectors(car->sBody.t, car->sBody.forward, car->sBody.right, car->sBody.up);
|
|
VectorCopy(velocity, car->sBody.v);
|
|
VectorClear(car->sBody.w);
|
|
VectorClear(car->sBody.L);
|
|
|
|
// set locations and velocities of frame points
|
|
PM_InitializeFrame(&car->sBody, &car->sPoints[FL_FRAME], 1.0f, -1.0f, 0.0f);
|
|
PM_InitializeFrame(&car->sBody, &car->sPoints[FR_FRAME], 1.0f, 1.0f, 0.0f);
|
|
PM_InitializeFrame(&car->sBody, &car->sPoints[RL_FRAME], -1.0f, -1.0f, 0.0f);
|
|
PM_InitializeFrame(&car->sBody, &car->sPoints[RR_FRAME], -1.0f, 1.0f, 0.0f);
|
|
|
|
// body collision points
|
|
forwardScale = ((CAR_LENGTH/2.0f) - BODY_RADIUS) / WHEEL_FORWARD;
|
|
rightScale = ((CAR_WIDTH/2.0f) - BODY_RADIUS) / WHEEL_RIGHT;
|
|
upScale = (3.0f) / -WHEEL_UP;
|
|
PM_InitializeFrame(&car->sBody, &car->sPoints[FL_BODY], forwardScale, -rightScale, upScale);
|
|
PM_InitializeFrame(&car->sBody, &car->sPoints[FR_BODY], forwardScale, rightScale, upScale);
|
|
PM_InitializeFrame(&car->sBody, &car->sPoints[ML_BODY], 0.0f, -rightScale, upScale);
|
|
PM_InitializeFrame(&car->sBody, &car->sPoints[MR_BODY], 0.0f, rightScale, upScale);
|
|
PM_InitializeFrame(&car->sBody, &car->sPoints[RL_BODY], -forwardScale, -rightScale, upScale);
|
|
PM_InitializeFrame(&car->sBody, &car->sPoints[RR_BODY], -forwardScale, rightScale, upScale);
|
|
|
|
PM_InitializeFrame(&car->sBody, &car->sPoints[ML_ROOF], 0.0f, -rightScale, 0.6f);
|
|
PM_InitializeFrame(&car->sBody, &car->sPoints[MR_ROOF], 0.0f, rightScale, 0.6f);
|
|
|
|
// set locations and velocities of wheel points
|
|
PM_InitializeWheel(&car->sBody, car->sPoints, FL_WHEEL, 1.0f, -1.0f);
|
|
PM_InitializeWheel(&car->sBody, car->sPoints, FR_WHEEL, 1.0f, 1.0f);
|
|
PM_InitializeWheel(&car->sBody, car->sPoints, RL_WHEEL, -1.0f, -1.0f);
|
|
PM_InitializeWheel(&car->sBody, car->sPoints, RR_WHEEL, -1.0f, 1.0f);
|
|
|
|
car->sBody.mass = 4 * CP_FRAME_MASS;
|
|
car->tBody.mass = 4 * CP_FRAME_MASS;
|
|
|
|
for (i = 0; i < FIRST_FRAME_POINT; i++){
|
|
car->sPoints[i].mass = CP_WHEEL_MASS;
|
|
car->sPoints[i].elasticity = CP_WHEEL_ELASTICITY;
|
|
car->sPoints[i].scof = CP_SCOF;
|
|
car->sPoints[i].kcof = CP_KCOF;
|
|
car->sPoints[i].slipping = qfalse;
|
|
car->sPoints[i].onGround = qfalse;
|
|
car->sPoints[i].onGroundTime = 0;
|
|
car->sPoints[i].radius = WHEEL_RADIUS;
|
|
car->tPoints[i].mass = CP_WHEEL_MASS;
|
|
car->tPoints[i].elasticity = CP_WHEEL_ELASTICITY;
|
|
car->tPoints[i].scof = CP_SCOF;
|
|
car->tPoints[i].kcof = CP_KCOF;
|
|
car->tPoints[i].slipping = qfalse;
|
|
car->tPoints[i].onGround = qfalse;
|
|
car->tPoints[i].onGroundTime = 0;
|
|
car->tPoints[i].radius = WHEEL_RADIUS;
|
|
}
|
|
|
|
for (; i < NUM_CAR_POINTS; i++){
|
|
if (i < LAST_FRAME_POINT){
|
|
if( i < 6 )
|
|
{
|
|
car->sPoints[i].mass = 0.5f * car->sBody.mass * pm->car_frontweight_dist;
|
|
car->tPoints[i].mass = 0.5f * car->sBody.mass * pm->car_frontweight_dist;
|
|
}
|
|
else
|
|
{
|
|
car->sPoints[i].mass = 0.5f * car->sBody.mass * (1.0f - pm->car_frontweight_dist);
|
|
car->tPoints[i].mass = 0.5f * car->sBody.mass * (1.0f - pm->car_frontweight_dist);
|
|
}
|
|
}
|
|
else {
|
|
car->sPoints[i].mass = 0.0f;
|
|
car->tPoints[i].mass = 0.0f;
|
|
}
|
|
car->sPoints[i].scof = CP_SCOF;
|
|
car->sPoints[i].kcof = CP_KCOF;
|
|
car->sPoints[i].elasticity = pm->car_body_elasticity;
|
|
car->sPoints[i].slipping = qfalse;
|
|
car->sPoints[i].onGround = qfalse;
|
|
car->sPoints[i].onGroundTime = 0;
|
|
car->sPoints[i].radius = BODY_RADIUS;
|
|
car->tPoints[i].scof = CP_SCOF;
|
|
car->tPoints[i].kcof = CP_KCOF;
|
|
car->tPoints[i].elasticity = pm->car_body_elasticity;
|
|
car->tPoints[i].slipping = qfalse;
|
|
car->tPoints[i].onGround = qfalse;
|
|
car->tPoints[i].onGroundTime = 0;
|
|
car->tPoints[i].radius = BODY_RADIUS;
|
|
}
|
|
|
|
car->sBody.elasticity = car->sPoints[4].elasticity;
|
|
car->tBody.elasticity = car->sPoints[4].elasticity;
|
|
|
|
PM_SetCoM(&car->sBody, car->sPoints);
|
|
|
|
halfWidth = CAR_WIDTH / 2.0f;
|
|
halfLength = CAR_LENGTH / 2.0f;
|
|
halfHeight = CAR_HEIGHT / 2.0f;
|
|
|
|
car->inverseBodyInertiaTensor[0][0] = pm->car_IT_xScale * 3.0f / (car->sBody.mass * (halfLength * halfLength + halfHeight * halfHeight));
|
|
car->inverseBodyInertiaTensor[1][1] = pm->car_IT_yScale * 3.0f / (car->sBody.mass * (halfWidth * halfWidth + halfHeight * halfHeight));
|
|
car->inverseBodyInertiaTensor[2][2] = pm->car_IT_zScale * 3.0f / (car->sBody.mass * (halfWidth * halfWidth + halfLength * halfLength));
|
|
|
|
car->springStrength = CP_SPRING_STRENGTH;
|
|
car->springMaxLength = CP_SPRING_MAXLEN;
|
|
car->springMinLength = CP_SPRING_MINLEN;
|
|
// car->shockStrength = CP_SHOCK_STRENGTH;
|
|
|
|
car->gear = 1;
|
|
car->rpm = CP_RPM_MIN;
|
|
|
|
// car->aCOF = CP_AIR_COF;
|
|
// car->dfCOF = CP_FRAC_TO_DF;
|
|
// car->ewCOF = CP_ENGINE_TIRE_COF;
|
|
|
|
MatrixMultiply(car->sBody.t, car->inverseBodyInertiaTensor, car->sBody.inverseWorldInertiaTensor);
|
|
MatrixTranspose(car->sBody.t, m);
|
|
MatrixMultiply(car->sBody.inverseWorldInertiaTensor, m, car->sBody.inverseWorldInertiaTensor);
|
|
|
|
PM_ClearCarForces(&car->sBody, car->sPoints);
|
|
PM_ClearCarForces(&car->tBody, car->tPoints);
|
|
|
|
PM_CopyTargetToSource(&car->sBody, &car->tBody, car->sPoints, car->tPoints);
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
PM_SetFluidDensity
|
|
===================
|
|
*/
|
|
static void PM_SetFluidDensity(carPoint_t *points, int i){
|
|
vec3_t dest;
|
|
int cont, level, type;
|
|
|
|
// check waterlevel
|
|
level = 0;
|
|
type = 0;
|
|
|
|
VectorCopy( points[i].r, dest );
|
|
dest[2] = points[i].r[2] - (points[i].radius - 0.5f);
|
|
cont = pm->pointcontents( dest, pm->ps->clientNum );
|
|
if ( cont & MASK_WATER ){ // bottom of point is in the water
|
|
level++;
|
|
type = cont;
|
|
}
|
|
|
|
dest[2] = points[i].r[2];
|
|
cont = pm->pointcontents (dest, pm->ps->clientNum );
|
|
if ( cont & MASK_WATER ){ // middle of point is in the water
|
|
level++;
|
|
type = cont;
|
|
}
|
|
|
|
dest[2] = points[i].r[2] + (points[i].radius - 0.5f);
|
|
cont = pm->pointcontents (dest, pm->ps->clientNum );
|
|
if ( cont & MASK_WATER ) { // top of point is in the water
|
|
level++;
|
|
type = cont;
|
|
}
|
|
|
|
if ( level )
|
|
{
|
|
if (type & CONTENTS_WATER){
|
|
points[i].fluidDensity = (level * CP_WATER_DENSITY + (3.0f - level) * CP_AIR_DENSITY) / 3.0f;
|
|
}
|
|
else if (type & CONTENTS_LAVA){
|
|
points[i].fluidDensity = (level * CP_LAVA_DENSITY + (3.0f - level) * CP_AIR_DENSITY) / 3.0f;
|
|
}
|
|
else if (type & CONTENTS_SLIME){
|
|
points[i].fluidDensity = (level * CP_SLIME_DENSITY + (3.0f - level) * CP_AIR_DENSITY) / 3.0f;
|
|
}
|
|
else{
|
|
points[i].fluidDensity = CP_AIR_DENSITY;
|
|
}
|
|
|
|
// Com_Printf("PM_SetFluidDensity: level %i, density %.3f\n", level, points[i].fluidDensity);
|
|
}
|
|
else
|
|
{
|
|
points[i].fluidDensity = CP_AIR_DENSITY;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
PM_CheckSurfaceFlags
|
|
===================
|
|
*/
|
|
static void PM_CheckSurfaceFlags( trace_t *trace, carPoint_t *point ){
|
|
|
|
// TODO: include rolling friction... harder to roll tires in sand or mud
|
|
|
|
if( pm->frictionFunc( point, &point->scof, &point->kcof ) )
|
|
{
|
|
point->kcof *= pm->car_friction_scale;
|
|
point->scof *= pm->car_friction_scale;
|
|
|
|
return;
|
|
}
|
|
else if (trace->surfaceFlags & SURF_SLICK){
|
|
point->kcof = CP_ICE_KCOF;
|
|
point->scof = CP_ICE_SCOF;
|
|
}
|
|
else if (trace->surfaceFlags & SURF_ICE) {
|
|
point->kcof = CP_ICE_KCOF;
|
|
point->scof = CP_ICE_SCOF;
|
|
}
|
|
else if (trace->surfaceFlags & SURF_GRASS){
|
|
point->kcof = CP_GRASS_KCOF;
|
|
point->scof = CP_GRASS_SCOF;
|
|
}
|
|
else if (trace->surfaceFlags & SURF_DUST){
|
|
point->kcof = CP_DIRT_KCOF;
|
|
point->scof = CP_DIRT_SCOF;
|
|
}
|
|
else if (trace->surfaceFlags & SURF_SAND){
|
|
point->kcof = CP_SAND_KCOF;
|
|
point->scof = CP_SAND_SCOF;
|
|
}
|
|
else if (trace->surfaceFlags & SURF_SNOW){
|
|
point->kcof = CP_SNOW_KCOF;
|
|
point->scof = CP_SNOW_SCOF;
|
|
}
|
|
else if (trace->surfaceFlags & SURF_GRAVEL) {
|
|
point->kcof = CP_GRAVEL_KCOF;
|
|
point->scof = CP_GRAVEL_SCOF;
|
|
}
|
|
else if (trace->surfaceFlags & SURF_DIRT) {
|
|
point->kcof = CP_DIRT_KCOF;
|
|
point->scof = CP_DIRT_SCOF;
|
|
}
|
|
else {
|
|
point->kcof = CP_KCOF;
|
|
point->scof = CP_SCOF;
|
|
}
|
|
|
|
if (trace->surfaceFlags & SURF_WET){
|
|
point->kcof *= CP_WET_SCALE;
|
|
point->scof *= CP_WET_SCALE;
|
|
}
|
|
|
|
point->kcof *= pm->car_friction_scale;
|
|
point->scof *= pm->car_friction_scale;
|
|
}
|
|
|
|
|
|
/*
|
|
================================================================================
|
|
PM_ApplyForce
|
|
|
|
This function is largely based on the physics articles and
|
|
source from Chris Hecker: http://www.d6.com/users/checker/dynamics.htm
|
|
================================================================================
|
|
*/
|
|
void PM_ApplyForce( carBody_t *body, vec3_t force, vec3_t at ){
|
|
vec3_t arm, moment;
|
|
|
|
VectorSubtract( at, body->CoM, arm );
|
|
|
|
VectorAdd( body->netForce, force, body->netForce );
|
|
|
|
CrossProduct( arm, force, moment);
|
|
VectorAdd( body->netMoment, moment, body->netMoment );
|
|
}
|
|
|
|
|
|
/*
|
|
================================================================================
|
|
PM_ApplyCollision
|
|
|
|
Rigid body <-> world collision function.
|
|
|
|
|
|
This function is largely based on the physics articles and
|
|
source from Chris Hecker: http://www.d6.com/users/checker/dynamics.htm
|
|
|
|
Given: rigid body, impact origin, impact normal, and elasticity
|
|
Find: the new linear and angular velocities as a result of the impact.
|
|
================================================================================
|
|
*/
|
|
static float PM_ApplyCollision( carBody_t *body, carPoint_t *points, vec3_t at, vec3_t normal, float elasticity ){
|
|
vec3_t arm;
|
|
vec3_t vP1;
|
|
vec3_t impulse, impulseMoment;
|
|
vec3_t delta, cross, cross2;
|
|
float impulseNum, oppositeImpulseNum, impulseDen, dot;
|
|
int i;
|
|
|
|
VectorSubtract(at, body->CoM, arm);
|
|
// VectorSubtract(at, body->r, arm);
|
|
|
|
if (pm->pDebug){
|
|
Com_Printf("PM_ApplyCollision: arm %0.3f, %0.3f, %0.3f\n", arm[0], arm[1], arm[2]);
|
|
Com_Printf("PM_ApplyCollision: normal %0.3f, %0.3f, %0.3f\n", normal[0], normal[1], normal[2]);
|
|
}
|
|
|
|
CrossProduct(body->w, arm, cross);
|
|
VectorAdd(body->v, cross, vP1);
|
|
|
|
// added from collision
|
|
VectorClear(impulse);
|
|
dot = DotProduct(normal, vP1);
|
|
if ( dot < 0 ){
|
|
impulseNum = -(1.0f + elasticity) * dot;
|
|
oppositeImpulseNum = -(1.0f - elasticity) * dot;
|
|
|
|
CrossProduct(arm, normal, cross);
|
|
VectorRotate(cross, body->inverseWorldInertiaTensor, cross2);
|
|
CrossProduct(cross2, arm, cross);
|
|
// Com_Printf( "oneOverMass %f, cross.normal %f\n", 1.0f / body->mass, DotProduct(cross, normal) );
|
|
impulseDen = 1.0f / body->mass + DotProduct(cross, normal);
|
|
|
|
VectorScale(normal, impulseNum / impulseDen, impulse);
|
|
}
|
|
else {
|
|
// not hitting surface
|
|
// Com_Printf( "PM_ApplyCollision: not hitting surface, %f\n", dot );
|
|
return 0.0f;
|
|
}
|
|
|
|
// apply impulse to primary quantities
|
|
CrossProduct(arm, impulse, impulseMoment);
|
|
VectorScale( impulse, 1.0 / body->mass, impulse );
|
|
VectorAdd(body->v, impulse, body->v);
|
|
VectorAdd(body->L, impulseMoment, body->L);
|
|
|
|
// compute affected auxiliary quantities
|
|
VectorRotate(body->L, body->inverseWorldInertiaTensor, body->w);
|
|
|
|
// temp to help wheel movement when hitting walls
|
|
// FIXME: is this still needed?
|
|
VectorMA(impulse, -DotProduct(impulse, body->up), body->up, delta);
|
|
for (i = 0; i < FIRST_FRAME_POINT; i++){
|
|
VectorAdd(points[i].v, delta, points[i].v);
|
|
}
|
|
|
|
PM_UpdateFrameVelocities(body, points);
|
|
|
|
if ( fabs(oppositeImpulseNum / impulseDen) > 5000.0f )
|
|
return fabs(oppositeImpulseNum / impulseDen);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
#if 0
|
|
/*
|
|
================================================================================
|
|
PM_ApplyBodyBodyCollision
|
|
|
|
This function is a hacked up version of PM_ApplyCollision to try to simulate
|
|
the collision between two cars.
|
|
|
|
Given: 2 rigid bodies, impact origin, impact normal, and elasticity
|
|
Find: the new linear and angular velocities of the two objects as a result of the impact.
|
|
================================================================================
|
|
*/
|
|
static float PM_ApplyBodyBodyCollision( carBody_t *body1, carPoint_t *points1, carBody_t *body2, carPoint_t *points2, vec3_t at, vec3_t normal, float elasticity ){
|
|
//vec3_t arm1, arm2;
|
|
vec3_t vP1, vP2;
|
|
//vec3_t impulse, impulseMoment;
|
|
//vec3_t cross, cross2;
|
|
vec3_t diff1, diff2, delta;
|
|
//float impulseNum, oppositeImpulseNum, impulseDen;
|
|
//float totalMass;
|
|
int i;
|
|
|
|
/*
|
|
totalMass = body1->mass + body2->mass;
|
|
|
|
VectorSubtract(at, body1->CoM, arm1);
|
|
VectorSubtract(at, body2->CoM, arm2);
|
|
|
|
if (pm->pDebug){
|
|
Com_Printf("PM_ApplyBodyBodyCollision: arm1 %0.3f, %0.3f, %0.3f\n", arm1[0], arm1[1], arm1[2]);
|
|
Com_Printf("PM_ApplyBodyBodyCollision: arm2 %0.3f, %0.3f, %0.3f\n", arm2[0], arm2[1], arm2[2]);
|
|
Com_Printf("PM_ApplyBodyBodyCollision: normal %0.3f, %0.3f, %0.3f\n", normal[0], normal[1], normal[2]);
|
|
}
|
|
|
|
CrossProduct(body1->w, arm1, cross);
|
|
VectorAdd(body1->v, cross, vP1);
|
|
|
|
CrossProduct(body2->w, arm2, cross);
|
|
VectorAdd(body2->v, cross, vP2);
|
|
*/
|
|
|
|
// hacked up physics
|
|
VectorCopy( body1->v, vP1 );
|
|
VectorCopy( body1->L, vP2 );
|
|
PM_ApplyCollision( body1, points1, at, normal, elasticity / 2.0f );
|
|
// VectorMA( body1->v, body2->mass / totalMass, vP2, body1->v );
|
|
|
|
// Com_Printf( "PM_ApplyBodyBodyCollision: v before %0.3f, %0.3f, %0.3f\n", body2->v[0], body2->v[1], body2->v[2] );
|
|
|
|
VectorSubtract( vP1, body1->v, diff1 );
|
|
VectorSubtract( vP2, body1->L, diff2 );
|
|
|
|
VectorAdd( body2->v, diff1, body2->v );
|
|
VectorAdd( body2->L, diff2, body2->L );
|
|
|
|
// compute affected auxiliary quantities
|
|
VectorRotate( body2->L, body2->inverseWorldInertiaTensor, body2->w );
|
|
|
|
// temp to help wheel movement when hitting walls
|
|
// FIXME: is this still needed?
|
|
VectorMA( diff1, -DotProduct(diff1, body2->up), body2->up, delta );
|
|
for ( i = 0; i < FIRST_FRAME_POINT; i++ ){
|
|
VectorAdd( points2[i].v, delta, points2[i].v );
|
|
}
|
|
|
|
PM_UpdateFrameVelocities( body2, points2 );
|
|
|
|
// VectorInverse( normal );
|
|
// PM_ApplyCollision( body2, points2, at, normal, elasticity );
|
|
// VectorMA( body2->v, body1->mass / totalMass, vP1, body2->v );
|
|
|
|
// Com_Printf( "PM_ApplyBodyBodyCollision: v after %0.3f, %0.3f, %0.3f\n", body2->v[0], body2->v[1], body2->v[2] );
|
|
|
|
return 0;
|
|
|
|
/*
|
|
VectorSubtract(vP1, vP2, vP1);
|
|
|
|
// added from collision
|
|
VectorClear(impulse);
|
|
// if (DotProduct(normal, vP1) < 0){
|
|
// massFraction = (2.0f * body1->mass) / (body1->mass + body2->mass);
|
|
// impulseNum = massFraction * DotProduct(normal, vP1);
|
|
// massFraction = ((1.0f + elasticity) * body1->mass) / (body1->mass + body2->mass);
|
|
impulseNum = -(1 + elasticity) * DotProduct(normal, vP1);
|
|
oppositeImpulseNum = -(1.0f - elasticity) * DotProduct(normal, vP1);
|
|
|
|
// impulseDen = 1.0f / body1->mass;
|
|
impulseDen = 1.0f / body1->mass + 1.0f / body2->mass;
|
|
CrossProduct(arm1, normal, cross);
|
|
VectorRotate(cross, body1->inverseWorldInertiaTensor, cross2);
|
|
CrossProduct(cross2, arm1, cross);
|
|
//impulseDen = 1.0f / body1->mass + DotProduct(cross, normal);
|
|
impulseDen += DotProduct(cross, normal);
|
|
|
|
CrossProduct(arm2, normal, cross);
|
|
VectorRotate(cross, body2->inverseWorldInertiaTensor, cross2);
|
|
CrossProduct(cross2, arm2, cross);
|
|
impulseDen += DotProduct(cross, normal);
|
|
|
|
VectorScale(normal, impulseNum / impulseDen, impulse);
|
|
|
|
// }
|
|
// else {
|
|
// // not hitting surface
|
|
// return;
|
|
// }
|
|
|
|
// apply impulse to primary quantities
|
|
VectorMA(body1->v, 1.0 / body1->mass, impulse, body1->v);
|
|
CrossProduct(arm1, impulse, impulseMoment);
|
|
VectorAdd(body1->L, impulseMoment, body1->L);
|
|
|
|
// compute affected auxiliary quantities
|
|
VectorRotate(body1->L, body1->inverseWorldInertiaTensor, body1->w);
|
|
|
|
// apply impulse to primary quantities of second object
|
|
VectorInverse(impulse);
|
|
VectorMA(body2->v, 1.0 / body2->mass, impulse, body2->v);
|
|
CrossProduct(arm2, impulse, impulseMoment);
|
|
VectorAdd(body2->L, impulseMoment, body2->L);
|
|
|
|
// compute affected auxiliary quantities of second object
|
|
VectorRotate(body2->L, body2->inverseWorldInertiaTensor, body2->w);
|
|
|
|
// temp to help wheel movement when hitting walls
|
|
// VectorMA(impulse, -DotProduct(impulse, body->up), body->up, delta);
|
|
// for (i = 0; i < FIRST_FRAME_POINT; i++){
|
|
// VectorMA(points[i].v, 1.0 / body->mass, delta, points[i].v);
|
|
// }
|
|
|
|
PM_UpdateFrameVelocities(body1, points1);
|
|
|
|
if (fabs(oppositeImpulseNum / impulseDen) > 5000.0f)
|
|
return fabs(oppositeImpulseNum / impulseDen);
|
|
else
|
|
return 0;
|
|
*/
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
================================================================================
|
|
PM_ApplyPointBodyCollision
|
|
|
|
This function is a hacked up version of PM_ApplyCollision to try to simulate
|
|
the collision between a tire and the body of a car (like when the suspension bottoms out).
|
|
|
|
Given: rigid body, impact origin, impact normal, and elasticity
|
|
Find: the new linear and angular velocities as a result of the impact.
|
|
================================================================================
|
|
*/
|
|
static void PM_ApplyPointBodyCollision( carBody_t *body, carPoint_t *points, carPoint_t *pt, vec3_t at, vec3_t normal, float elasticity ){
|
|
vec3_t arm;
|
|
vec3_t vP1;
|
|
vec3_t impulse, impulseMoment;
|
|
vec3_t cross, cross2;
|
|
float impulseNum, impulseDen;
|
|
float massFraction;
|
|
|
|
VectorSubtract(at, body->CoM, arm);
|
|
// VectorSubtract(at, body->r, arm);
|
|
|
|
if (pm->pDebug){
|
|
Com_Printf("PM_ApplyPointBodyCollision: arm %0.3f, %0.3f, %0.3f\n", arm[0], arm[1], arm[2]);
|
|
Com_Printf("PM_ApplyPointBodyCollision: normal %0.3f, %0.3f, %0.3f\n", normal[0], normal[1], normal[2]);
|
|
}
|
|
|
|
CrossProduct(body->w, arm, cross);
|
|
VectorAdd(body->v, cross, vP1);
|
|
|
|
// added from collision
|
|
VectorClear(impulse);
|
|
// if (DotProduct(normal, vP1) < 0){
|
|
massFraction = (2.0f * pt->mass) / (pt->mass + body->mass);
|
|
impulseNum = massFraction * (DotProduct(normal, pt->v) - DotProduct(normal, vP1));
|
|
|
|
CrossProduct(arm, normal, cross);
|
|
VectorRotate(cross, body->inverseWorldInertiaTensor, cross2);
|
|
CrossProduct(cross2, arm, cross);
|
|
impulseDen = 1.0 / body->mass + DotProduct(cross, normal);
|
|
|
|
VectorScale(normal, impulseNum / impulseDen, impulse);
|
|
// }
|
|
|
|
// apply impulse to primary quantities of rigid body
|
|
VectorMA(body->v, 1.0 / body->mass, impulse, body->v);
|
|
CrossProduct(arm, impulse, impulseMoment);
|
|
VectorAdd(body->L, impulseMoment, body->L);
|
|
|
|
// compute affected auxiliary quantities
|
|
VectorRotate(body->L, body->inverseWorldInertiaTensor, body->w);
|
|
|
|
VectorMA(pt->v, -1.0 / pt->mass, impulse, pt->v);
|
|
/*
|
|
// temp to help wheel movement when hitting walls
|
|
VectorMA(impulse, -DotProduct(impulse, body->up), body->up, delta);
|
|
for (i = 0; i < FIRST_FRAME_POINT; i++){
|
|
VectorMA(points[i].v, 1.0 / body->mass, delta, points[i].v);
|
|
}
|
|
*/
|
|
PM_UpdateFrameVelocities(body, points);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CGAME
|
|
void CG_Sparks( const vec3_t origin, const vec3_t normal, const vec3_t direction, const float speed );
|
|
#endif
|
|
|
|
/*
|
|
================================================================================
|
|
PM_CarBodyFrictionForces
|
|
|
|
Friction forces between the car body and the world
|
|
|
|
================================================================================
|
|
*/
|
|
static void PM_CarBodyFrictionForces( car_t *car, carBody_t *body, carPoint_t *points, int i ){
|
|
vec3_t vel, force;
|
|
float normalForce;
|
|
float speed;
|
|
|
|
normalForce = DotProduct( body->netForce, points[i].normals[0] );
|
|
|
|
if ( normalForce >= 0.0f ) return;
|
|
|
|
VectorMA( body->netForce, -normalForce, points[i].normals[0], force );
|
|
VectorMA( points[i].v, -DotProduct( points[i].v, points[i].normals[0] ), points[i].normals[0], vel );
|
|
speed = VectorNormalize( vel );
|
|
|
|
if ( VectorLength(force) > fabs( 0.7f * normalForce ) || speed > 2.0f ){
|
|
// VectorNormalize(vel);
|
|
VectorScale( vel, 0.4f * normalForce, force );
|
|
}
|
|
else {
|
|
VectorInverse( force );
|
|
}
|
|
|
|
PM_ApplyForce( body, force, points[i].r );
|
|
|
|
#ifdef CGAME
|
|
if( speed > 20.0f )
|
|
{
|
|
vec3_t origin;
|
|
float r;
|
|
|
|
r = random() * 5000 / speed;
|
|
if( r < 80 )
|
|
{
|
|
VectorMA( points[i].r, -BODY_RADIUS / 1.5f, points[i].normals[0], origin );
|
|
// VectorInverse( vel );
|
|
VectorMA( vel, 0.1f, points[i].normals[0], vel );
|
|
VectorNormalize( vel );
|
|
CG_Sparks( origin, points[i].normals[0], vel, speed * 0.5f );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
================================================================================
|
|
PM_Generate_SwayBar_Forces
|
|
|
|
Simulates ARBs in the car
|
|
|
|
================================================================================
|
|
*/
|
|
static void PM_Generate_SwayBar_Forces(car_t *car, carBody_t *body, carPoint_t *points, int leftWheel, int rightWheel ){
|
|
// vec3_t lSpring, rSpring;
|
|
// vec3_t lengthDiff;
|
|
float dot, force;
|
|
// carPoint_t *lWheel;
|
|
// carPoint_t *rWheel;
|
|
|
|
// lWheel = &points[leftWheel];
|
|
// rWheel = &points[rightWheel];
|
|
|
|
if ( fabs( DotProduct( body->v, body->forward ) ) < 50.0f)
|
|
return;
|
|
|
|
// VectorSubtract(points[leftWheel+4].v, lWheel->v, lSpring);
|
|
// VectorSubtract(points[rightWheel+4].v, rWheel->v, rSpring);
|
|
|
|
// VectorSubtract(lSpring, rSpring, lengthDiff);
|
|
|
|
dot = body->curSpringLengths[leftWheel] - body->curSpringLengths[rightWheel];
|
|
|
|
// if dotproduct(lengthDiff, up) < 0, rSpring > lSpring
|
|
// else rSpring < lSpring
|
|
// dot = DotProduct(body->up, lengthDiff);
|
|
force = dot * pm->car_swaybar * CP_GRAVITY;
|
|
|
|
if (force < 0 && !points[leftWheel].onGround)
|
|
return;
|
|
else if (force > 0 && !points[rightWheel].onGround)
|
|
return;
|
|
|
|
if (force < 0 && pm->ps->viewangles[ROLL] > 0){
|
|
// Com_Printf("Sway bars shouldnt be forcing\n");
|
|
return;
|
|
}
|
|
else if (force > 0 && pm->ps->viewangles[ROLL] < 0){
|
|
// Com_Printf("Sway bars shouldnt be forcing\n");
|
|
return;
|
|
}
|
|
|
|
// Com_Printf("PM_Generate_SwayBar_Forces: force %f, roll %f\n", force, pm->ps->viewangles[ROLL]);
|
|
|
|
// VectorScale(body->up, force, lWheel->forces[SWAY_BAR]);
|
|
VectorScale(body->up, -force, points[leftWheel+4].forces[SWAY_BAR]);
|
|
|
|
// VectorScale(body->up, -force, rWheel->forces[SWAY_BAR]);
|
|
VectorScale(body->up, force, points[rightWheel+4].forces[SWAY_BAR]);
|
|
}
|
|
|
|
|
|
/*
|
|
================================================================================
|
|
PM_Generate_FrameWheel_Forces
|
|
|
|
Handles spring and damper physics for the suspension
|
|
|
|
================================================================================
|
|
*/
|
|
static int PM_Generate_FrameWheel_Forces(car_t *car, carPoint_t *points, int frameIndex, int wheelIndex ){
|
|
vec3_t delta, diff;
|
|
float compression, springVel, length;
|
|
int hitType;
|
|
carPoint_t *frame, *wheel;
|
|
|
|
hitType = HTYPE_NO_HIT;
|
|
|
|
frame = &points[frameIndex];
|
|
wheel = &points[wheelIndex];
|
|
|
|
//
|
|
// Calculate forces on the frame
|
|
//
|
|
|
|
// spring force based on length of spring (origin difference of the two points)
|
|
length = car->sBody.curSpringLengths[wheelIndex];
|
|
|
|
// shock force based on velocity difference of the two points
|
|
VectorSubtract(frame->v, wheel->v, delta);
|
|
springVel = DotProduct(car->sBody.up, delta);
|
|
|
|
if (springVel > 0){
|
|
VectorScale(car->sBody.up, -pm->car_shock_up * CP_GRAVITY * springVel, frame->forces[SHOCK]);
|
|
}
|
|
else {
|
|
VectorScale(car->sBody.up, -pm->car_shock_down * CP_GRAVITY * springVel, frame->forces[SHOCK]);
|
|
}
|
|
|
|
// compression = springVel > 0 ? 0 : springVel;
|
|
// VectorScale(car->sBody.up, -car->shockStrength * compression, frame->forces[SHOCK]);
|
|
|
|
if (length < car->springMinLength){
|
|
if (springVel < 0){
|
|
VectorScale(car->sBody.up, -springVel, diff);
|
|
VectorSubtract(wheel->v, diff, wheel->v);
|
|
|
|
if (wheel->onGround){
|
|
// suspension bottomed out and tires are on the ground so we have
|
|
// to treat this as a collision of the frame with the ground
|
|
|
|
hitType = HTYPE_BOTTOMED_OUT;
|
|
}
|
|
}
|
|
// length = car->springMinLength;
|
|
}
|
|
else if (length > car->springMaxLength){
|
|
if (springVel > 0){
|
|
// PM_ApplyPointBodyCollision(&car->sBody, &car->sPoints, car->sBody.up, 1.0);
|
|
/*
|
|
VectorScale(car->sBody.up, DotProduct(car->sBody.up, wheelVelocity), diff);
|
|
VectorSubtract(wheelVelocity, diff, delta);
|
|
|
|
VectorScale(car->sBody.up, DotProduct(car->sBody.up, frameVelocity), diff);
|
|
VectorAdd(delta, diff, wheel->v);
|
|
*/
|
|
// move wheel so its not too far out
|
|
VectorMA( wheel->r, length - car->springMaxLength, car->sBody.up, wheel->r );
|
|
car->sBody.curSpringLengths[wheelIndex] = car->springMaxLength;
|
|
|
|
}
|
|
hitType = HTYPE_MAXED_OUT;
|
|
// length = car->springMaxLength;
|
|
}
|
|
|
|
// linear
|
|
// compression = car->springMaxLength - length;
|
|
|
|
/*
|
|
if (length > car->springMaxLength)
|
|
compression = car->springMaxLength - length;
|
|
else
|
|
compression = (car->springMaxLength*2.0f - length) / 5.0f;
|
|
*/
|
|
|
|
// x^2
|
|
// compression = car->springMaxLength - length;
|
|
// compression *= compression;
|
|
|
|
// exp(x/max dist)
|
|
|
|
compression = car->springMaxLength - length;
|
|
|
|
if( compression > 0 )
|
|
compression = (car->springMaxLength - car->springMinLength) * (exp( compression / (car->springMaxLength - car->springMinLength)) - 1 ) / 1.718281828f;
|
|
|
|
if (compression < 0)
|
|
VectorScale(car->sBody.up, 3.0f * CP_SPRING_STRENGTH * compression, frame->forces[SPRING]);
|
|
else
|
|
VectorScale(car->sBody.up, car->springStrength * compression, frame->forces[SPRING]);
|
|
|
|
/*
|
|
{
|
|
float shock = VectorLength( frame->forces[SHOCK] );
|
|
if( shock > CP_MAX_SHOCK_FORCE )
|
|
{
|
|
#ifdef GAME
|
|
Com_Printf( "%i Shock Force: %f\n", (wheel - &car->sPoints[0]), shock );
|
|
#endif
|
|
VectorScale( frame->forces[SHOCK], CP_MAX_SHOCK_FORCE / shock, frame->forces[SHOCK] );
|
|
}
|
|
}
|
|
*/
|
|
|
|
//
|
|
// Calculate forces on the tire
|
|
//
|
|
VectorScale(frame->forces[SPRING], -1, wheel->forces[SPRING]);
|
|
VectorScale(frame->forces[SHOCK], -1, wheel->forces[SHOCK]);
|
|
|
|
return hitType;
|
|
}
|
|
|
|
|
|
/*
|
|
================================================================================
|
|
PM_CalculateForces
|
|
|
|
Main force calculating function for the entire car and the wheels
|
|
|
|
================================================================================
|
|
*/
|
|
static void PM_CalculateForces( car_t *car, carBody_t *body, carPoint_t *points, float sec ){
|
|
vec3_t delta, diff;
|
|
//vec3_t arm;
|
|
vec3_t force, hitOrigin, normal;
|
|
float length, k, b, springVel, dot;
|
|
int count;
|
|
int i, hitType, n;
|
|
//float impulseDamage;
|
|
|
|
VectorClear(force);
|
|
|
|
// add gravity forces
|
|
for (i = 0; i < NUM_CAR_POINTS; i++){
|
|
VectorSet(points[i].forces[GRAVITY], 0, 0, -CP_CURRENT_GRAVITY * points[i].mass);
|
|
}
|
|
|
|
VectorClear(hitOrigin);
|
|
count = 0;
|
|
|
|
PM_Generate_SwayBar_Forces(car, body, points, 0, 1);
|
|
PM_Generate_SwayBar_Forces(car, body, points, 2, 3);
|
|
|
|
// add spring forces
|
|
for (i = 0; i < FIRST_FRAME_POINT; i++)
|
|
{
|
|
// VectorSubtract(points[i+4].r, points[i].r, delta);
|
|
if ( body->curSpringLengths[i] > 20 )
|
|
{
|
|
// Com_Printf("Initialize wheel %i, frame %f,%f,%f, wheel %f,%f,%f\n", i, points[i+4].r[0], points[i+4].r[1], points[i+4].r[2], points[i].r[0], points[i].r[1], points[i].r[2]);
|
|
if (i == 0)
|
|
PM_InitializeWheel(body, points, i, 1.0f, -1.0f);
|
|
else if (i == 1)
|
|
PM_InitializeWheel(body, points, i, 1.0f, 1.0f);
|
|
else if (i == 2)
|
|
PM_InitializeWheel(body, points, i, -1.0f, -1.0f);
|
|
else if (i == 3)
|
|
PM_InitializeWheel(body, points, i, -1.0f, 1.0f);
|
|
}
|
|
|
|
hitType = PM_Generate_FrameWheel_Forces(car, points, i+4, i);
|
|
|
|
if ( VectorNAN( points[i].netForce ) )
|
|
Com_Printf("Blowing up car because of car point force on wheel %d\n", i );
|
|
|
|
if (hitType == HTYPE_BOTTOMED_OUT)
|
|
{
|
|
// add a normal to the frame point
|
|
for ( n = 0; n < 3; n++ )
|
|
{
|
|
if ( VectorLength( points[i+4].normals[n] ) ) continue;
|
|
VectorCopy( body->up, points[i+4].normals[n] );
|
|
break;
|
|
}
|
|
VectorAdd( hitOrigin, points[i+4].r, hitOrigin );
|
|
count++;
|
|
}
|
|
else if (hitType == HTYPE_MAXED_OUT)
|
|
{
|
|
// add a normal to the frame point
|
|
for ( n = 0; n < 3; n++ )
|
|
{
|
|
if ( VectorLength( points[i+4].normals[n] ) ) continue;
|
|
VectorScale( body->up, -1.0f, points[i+4].normals[n] );
|
|
break;
|
|
}
|
|
|
|
VectorAdd( points[i].forces[SPRING], points[i].forces[SHOCK], force );
|
|
|
|
dot = DotProduct( force, body->up );
|
|
if ( dot < 0.0f )
|
|
VectorMA( points[i].forces[SPRING], -dot, body->up, points[i].forces[SPRING] );
|
|
}
|
|
}
|
|
|
|
if ( count != 0 )
|
|
{
|
|
if (pm->pDebug)
|
|
Com_Printf("PM_CalculateForces: Frame-Wheel Collision with %i wheels\n", count);
|
|
|
|
VectorScale(hitOrigin, 1.0 / (float)count, hitOrigin);
|
|
/*impulseDamage = */PM_ApplyCollision(body, points, hitOrigin, car->sBody.up, body->elasticity);
|
|
|
|
// add normal force
|
|
// FIXME - change this so i can do it only once right before acceleration
|
|
// FIXME: replace with proper normal.netForce
|
|
/*
|
|
VectorSet(delta, 0, 0, -CP_CURRENT_GRAVITY * CP_CAR_MASS);
|
|
if (DotProduct(body->up, delta) < 0.0f){
|
|
VectorScale(body->up, -OVERCLIP * DotProduct(body->up, delta), body->normalForce);
|
|
PM_ApplyForce(body, body->normalForce, hitOrigin);
|
|
}
|
|
*/
|
|
|
|
// damage stuff
|
|
// FIXME: taking too much damage during normal driving
|
|
/*
|
|
impulseDamage /= (2000.0f / sec) / count;
|
|
if (impulseDamage > 0.00001f){
|
|
VectorCopy(hitOrigin, pm->damage.origin);
|
|
VectorCopy(car->sBody.up, pm->damage.dir);
|
|
pm->damage.damage += impulseDamage;
|
|
pm->damage.mod = MOD_BO_SHOCKS;
|
|
}
|
|
*/
|
|
VectorCopy( hitOrigin, pm->damage.origin );
|
|
VectorCopy( car->sBody.up, pm->damage.dir );
|
|
pm->damage.dflags = DAMAGE_NO_KNOCKBACK;
|
|
pm->damage.mod = MOD_BO_SHOCKS;
|
|
}
|
|
|
|
// UPDATE - moved lower down
|
|
// PM_Generate_SwayBar_Forces(body, points, 0, 1);
|
|
// PM_Generate_SwayBar_Forces(body, points, 2, 3);
|
|
|
|
// calculate frame hits
|
|
VectorClear(hitOrigin);
|
|
VectorClear(normal);
|
|
count = 0;
|
|
|
|
for (i = FIRST_FRAME_POINT; i < NUM_CAR_POINTS; i++){
|
|
if ( !points[i].onGround ) continue;
|
|
if ( DotProduct(points[i].v, points[i].normals[0] ) > 0.0f) continue;
|
|
|
|
VectorAdd( normal, points[i].normals[0], normal );
|
|
VectorAdd( hitOrigin, points[i].r, hitOrigin );
|
|
count++;
|
|
}
|
|
|
|
if ( count != 0 )
|
|
{
|
|
VectorScale(hitOrigin, 1.0 / (float)count, hitOrigin);
|
|
VectorNormalize(normal);
|
|
|
|
if (pm->pDebug)
|
|
Com_Printf("PM_CalculateForces: Frame hit a surface at %i spots\n", count);
|
|
|
|
/*impulseDamage = */PM_ApplyCollision( body, points, hitOrigin, normal, 0.0f );
|
|
|
|
// add normal force
|
|
// FIXME - change this so i can do it only once right before acceleration
|
|
// FIXME: replace with proper normal.netForce
|
|
/*
|
|
VectorSet(delta, 0, 0, -CP_CURRENT_GRAVITY * CP_CAR_MASS);
|
|
if (DotProduct(normal, delta) < 0.0f){
|
|
VectorScale(body->up, -OVERCLIP * DotProduct(body->up, delta), body->normalForce);
|
|
PM_ApplyForce(body, body->normalForce, hitOrigin);
|
|
}
|
|
*/
|
|
|
|
// damage stuff - FIXME: dont take damage for sitting up against walls
|
|
// divide by number of spots hit maybe?
|
|
/*
|
|
impulseDamage /= (50000.0f / sec) / count;
|
|
if (impulseDamage > 0.00001f){
|
|
VectorCopy(hitOrigin, pm->damage.origin);
|
|
VectorCopy(normal, pm->damage.dir);
|
|
pm->damage.damage += impulseDamage;
|
|
pm->damage.mod = MOD_WORLD_COLLISION;
|
|
}
|
|
*/
|
|
VectorCopy(hitOrigin, pm->damage.origin);
|
|
VectorCopy(normal, pm->damage.dir);
|
|
pm->damage.dflags = DAMAGE_NO_KNOCKBACK;
|
|
pm->damage.mod = MOD_WORLD_COLLISION;
|
|
}
|
|
|
|
// steering, acceleration, braking, etc
|
|
PM_AddRoadForces(car, body, points, sec);
|
|
|
|
for (i = 0; i < FIRST_FRAME_POINT; i++)
|
|
{
|
|
// new
|
|
// VectorMA(car->sPoints[i].r, -WHEEL_RADIUS, car->sPoints[i].normals[0], arm);
|
|
|
|
// remove component of force in body up direction
|
|
VectorCopy(points[i].forces[ROAD], force);
|
|
VectorMA(force, -DotProduct(force, car->sBody.up), car->sBody.up, force);
|
|
// apply force to axle, not tire-ground contact
|
|
PM_ApplyForce(body, force, car->sPoints[i].r);
|
|
// PM_ApplyForce(body, force, arm);
|
|
|
|
// end new
|
|
VectorScale(points[i].forces[ROAD], points[i].mass / points[i+4].mass, points[i].forces[ROAD]);
|
|
// }
|
|
|
|
// internal forces to return the wheel to perpendicular
|
|
// for (i = 0; i < FIRST_FRAME_POINT; i++)
|
|
// {
|
|
VectorSubtract( points[i+4].r, points[i].r, delta );
|
|
VectorMA( delta, -DotProduct(delta, car->sBody.up), car->sBody.up, diff );
|
|
length = VectorNormalize(diff);
|
|
|
|
if ( length > 2.0f ){
|
|
// k = 10000; // good at 90fps
|
|
k = pm->car_wheel * CP_WHEEL_MASS;
|
|
|
|
// b = 2 * sqrt(k * points[i].mass);
|
|
b = pm->car_wheel_damp * CP_WHEEL_MASS;
|
|
|
|
VectorScale(diff, -k * length, force);
|
|
|
|
VectorSubtract(points[i+4].v, points[i].v, delta);
|
|
VectorMA(delta, -DotProduct(delta, car->sBody.up), car->sBody.up, diff);
|
|
springVel = VectorNormalize(diff);
|
|
VectorMA(force, -b * springVel, diff, force);
|
|
|
|
// VectorCopy(force, points[i+4].forces[INTERNAL]);
|
|
VectorScale(force, -1, points[i].forces[INTERNAL]);
|
|
}
|
|
}
|
|
|
|
// calculate net forces
|
|
for (i = 0; i < NUM_CAR_POINTS; i++){
|
|
PM_CalculateNetForce(&points[i], i);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================================================================================
|
|
PM_AccelerateAndMoveBody
|
|
|
|
This function is largely based on the physics articles and
|
|
source from Chris Hecker: http://www.d6.com/users/checker/dynamics.htm
|
|
|
|
================================================================================
|
|
*/
|
|
static void PM_AccelerateAndMoveBody( car_t *car, carBody_t *sBody, carBody_t *tBody, float time ){
|
|
float m[3][3];
|
|
float m2[3][3];
|
|
vec3_t vec;
|
|
|
|
vec3_t force;
|
|
int i, n;
|
|
float dot, impulseDamage;
|
|
float time_2 = time / 2.0f;
|
|
|
|
// FIXME: handle collisions here as well?
|
|
// FIXME: just have one normal force for the entire body instead of for each point?
|
|
for (i = FIRST_FRAME_POINT; i < NUM_CAR_POINTS; i++){
|
|
if (!car->sPoints[i].onGround) continue;
|
|
for (n = 0; n < 3; n++){
|
|
if (!VectorLength(car->sPoints[i].normals[n])) continue;
|
|
dot = DotProduct(car->sPoints[i].normals[n], sBody->netForce);
|
|
if (dot >= 0.0f) continue;
|
|
|
|
VectorScale(car->sPoints[i].normals[n], -OVERCLIP * dot, force);
|
|
PM_ApplyForce(sBody, force, car->sPoints[i].r);
|
|
}
|
|
}
|
|
|
|
// damage stuff
|
|
VectorSubtract(tBody->v, sBody->v, vec);
|
|
impulseDamage = VectorLength(vec);
|
|
if (impulseDamage > 5.0f){
|
|
// Com_Printf("impulseDamage %f\n", impulseDamage);
|
|
pm->damage.damage += impulseDamage / 25.0f;
|
|
}
|
|
|
|
|
|
// linear
|
|
// NOTE: this method is really not right but it works so oh well.
|
|
// tBody contains netForce from last frame
|
|
VectorAdd(sBody->netForce, tBody->netForce, vec);
|
|
VectorMA(sBody->v, time_2 / sBody->mass, vec, tBody->v);
|
|
|
|
VectorAdd(sBody->v, tBody->v, vec);
|
|
VectorMA(sBody->r, time_2, vec, tBody->r);
|
|
|
|
// angular
|
|
VectorAdd(sBody->netMoment, tBody->netMoment, vec);
|
|
|
|
VectorMA(sBody->L, time_2, vec, tBody->L);
|
|
VectorRotate(tBody->L, sBody->inverseWorldInertiaTensor, tBody->w);
|
|
|
|
VectorAdd(sBody->w, tBody->w, vec);
|
|
|
|
m[0][0] = 0; m[0][1] = time_2 * -vec[2]; m[0][2] = time_2 * vec[1];
|
|
m[1][0] = time_2 * vec[2]; m[1][1] = 0; m[1][2] = time_2 * -vec[0];
|
|
m[2][0] = time_2 * -vec[1]; m[2][1] = time_2 * vec[0]; m[2][2] = 0;
|
|
|
|
MatrixMultiply(m, sBody->t, m2);
|
|
MatrixAdd(sBody->t, m2, tBody->t);
|
|
OrthonormalizeOrientation(tBody->t);
|
|
|
|
// Optimization note: check if inverseWorldInertiaTensor is only really
|
|
// needed for collisions. If it is then might be best just to calculate
|
|
// it there since collisions dont usually happen every frame.
|
|
// Note: only used in collisions and converting L to w. If I can get w
|
|
// some other way then I can move it.
|
|
|
|
// MatrixMultiply(tBody->t, car->inverseBodyInertiaTensor, m);
|
|
// This should be the same as MatrixMultiply(tBody->t, car->inverseBodyInertiaTensor, m);
|
|
// without so many multiplies and adds.
|
|
m[0][0] = car->inverseBodyInertiaTensor[0][0] * tBody->t[0][0];
|
|
m[1][0] = car->inverseBodyInertiaTensor[0][0] * tBody->t[1][0];
|
|
m[2][0] = car->inverseBodyInertiaTensor[0][0] * tBody->t[2][0];
|
|
|
|
m[0][1] = car->inverseBodyInertiaTensor[1][1] * tBody->t[0][1];
|
|
m[1][1] = car->inverseBodyInertiaTensor[1][1] * tBody->t[1][1];
|
|
m[2][1] = car->inverseBodyInertiaTensor[1][1] * tBody->t[2][1];
|
|
|
|
m[0][2] = car->inverseBodyInertiaTensor[2][2] * tBody->t[0][2];
|
|
m[1][2] = car->inverseBodyInertiaTensor[2][2] * tBody->t[1][2];
|
|
m[2][2] = car->inverseBodyInertiaTensor[2][2] * tBody->t[2][2];
|
|
|
|
MatrixTranspose(tBody->t, m2);
|
|
MatrixMultiply(m, m2, tBody->inverseWorldInertiaTensor);
|
|
|
|
// generate direction vectors
|
|
OrientationToVectors(tBody->t, tBody->forward, tBody->right, tBody->up);
|
|
|
|
// copy netForce and netMoment to target so we can use it next frame
|
|
VectorCopy(sBody->netForce, tBody->netForce);
|
|
VectorCopy(sBody->netMoment, tBody->netMoment);
|
|
}
|
|
|
|
|
|
/*
|
|
================================================================================
|
|
PM_CalculateSecondaryQuantities
|
|
|
|
Used in cgame to calculate w and forward, right, up from the new known
|
|
primary values from the server
|
|
|
|
================================================================================
|
|
*/
|
|
void PM_CalculateSecondaryQuantities( car_t *car, carBody_t *body, carPoint_t *points ){
|
|
float m[3][3];
|
|
float m2[3][3];
|
|
float forwardScale, rightScale, upScale;
|
|
vec3_t diff;
|
|
int i;
|
|
|
|
// MatrixMultiply(body->t, car->inverseBodyInertiaTensor, m);
|
|
m[0][0] = car->inverseBodyInertiaTensor[0][0] * body->t[0][0];
|
|
m[1][0] = car->inverseBodyInertiaTensor[0][0] * body->t[1][0];
|
|
m[2][0] = car->inverseBodyInertiaTensor[0][0] * body->t[2][0];
|
|
|
|
m[0][1] = car->inverseBodyInertiaTensor[1][1] * body->t[0][1];
|
|
m[1][1] = car->inverseBodyInertiaTensor[1][1] * body->t[1][1];
|
|
m[2][1] = car->inverseBodyInertiaTensor[1][1] * body->t[2][1];
|
|
|
|
m[0][2] = car->inverseBodyInertiaTensor[2][2] * body->t[0][2];
|
|
m[1][2] = car->inverseBodyInertiaTensor[2][2] * body->t[1][2];
|
|
m[2][2] = car->inverseBodyInertiaTensor[2][2] * body->t[2][2];
|
|
|
|
MatrixTranspose(body->t, m2);
|
|
MatrixMultiply(m, m2, body->inverseWorldInertiaTensor);
|
|
|
|
VectorRotate(body->L, body->inverseWorldInertiaTensor, body->w);
|
|
|
|
OrientationToVectors(body->t, body->forward, body->right, body->up);
|
|
|
|
// set locations and velocities of frame points
|
|
PM_InitializeFrame(body, &points[FL_FRAME], 1.0f, -1.0f, 0.0f);
|
|
PM_InitializeFrame(body, &points[FR_FRAME], 1.0f, 1.0f, 0.0f);
|
|
PM_InitializeFrame(body, &points[RL_FRAME], -1.0f, -1.0f, 0.0f);
|
|
PM_InitializeFrame(body, &points[RR_FRAME], -1.0f, 1.0f, 0.0f);
|
|
|
|
// body collision points
|
|
forwardScale = ((CAR_LENGTH/2.0f) - BODY_RADIUS) / WHEEL_FORWARD;
|
|
rightScale = ((CAR_WIDTH/2.0f) - BODY_RADIUS) / WHEEL_RIGHT;
|
|
upScale = (0) / -WHEEL_UP;
|
|
PM_InitializeFrame(body, &points[FL_BODY], forwardScale, -rightScale, upScale);
|
|
PM_InitializeFrame(body, &points[FR_BODY], forwardScale, rightScale, upScale);
|
|
PM_InitializeFrame(body, &points[ML_BODY], 0.0f, -rightScale, upScale);
|
|
PM_InitializeFrame(body, &points[MR_BODY], 0.0f, rightScale, upScale);
|
|
PM_InitializeFrame(body, &points[RL_BODY], -forwardScale, -rightScale, upScale);
|
|
PM_InitializeFrame(body, &points[RR_BODY], -forwardScale, rightScale, upScale);
|
|
|
|
PM_InitializeFrame(body, &points[ML_ROOF], 0.0f, -rightScale, 0.6f);
|
|
PM_InitializeFrame(body, &points[MR_ROOF], 0.0f, rightScale, 0.6f);
|
|
|
|
// calculate spring lengths
|
|
for( i = 0; i < FIRST_FRAME_POINT; i++ )
|
|
{
|
|
VectorSubtract( points[i+4].r, points[i].r, diff );
|
|
body->curSpringLengths[i] = DotProduct( diff, body->up );
|
|
}
|
|
|
|
PM_SetCoM(body, points);
|
|
|
|
PM_ClearCarForces(body, points);
|
|
PM_CopyTargetToSource(&car->sBody, &car->tBody, car->sPoints, car->tPoints);
|
|
/*
|
|
VectorCopy( car->sBody.v, car->oldBodies[2].v );
|
|
VectorCopy( car->sBody.w, car->oldBodies[2].w );
|
|
VectorCopy( car->sBody.netForce, car->oldBodies[2].netForce );
|
|
VectorCopy( car->sBody.netMoment, car->oldBodies[2].netMoment );
|
|
|
|
VectorCopy( car->sBody.v, car->oldBodies[1].v );
|
|
VectorCopy( car->sBody.w, car->oldBodies[1].w );
|
|
VectorCopy( car->sBody.netForce, car->oldBodies[1].netForce );
|
|
VectorCopy( car->sBody.netMoment, car->oldBodies[1].netMoment );
|
|
|
|
VectorCopy( car->sBody.v, car->oldBodies[0].v );
|
|
VectorCopy( car->sBody.w, car->oldBodies[0].w );
|
|
VectorCopy( car->sBody.netForce, car->oldBodies[0].netForce );
|
|
VectorCopy( car->sBody.netMoment, car->oldBodies[0].netMoment );
|
|
|
|
for( i = 0; i < LAST_RW_POINT; i++ )
|
|
{
|
|
VectorCopy( car->sPoints[i].netForce, car->oldPoints[2][i].netForce );
|
|
VectorCopy( car->sPoints[i].v, car->oldPoints[2][i].v );
|
|
car->oldPoints[2][i].netMoment = car->sPoints[i].netMoment;
|
|
|
|
VectorCopy( car->sPoints[i].netForce, car->oldPoints[1][i].netForce );
|
|
VectorCopy( car->sPoints[i].v, car->oldPoints[1][i].v );
|
|
car->oldPoints[1][i].netMoment = car->sPoints[i].netMoment;
|
|
|
|
VectorCopy( car->sPoints[i].netForce, car->oldPoints[0][i].netForce );
|
|
VectorCopy( car->sPoints[i].v, car->oldPoints[0][i].v );
|
|
car->oldPoints[0][i].netMoment = car->sPoints[i].netMoment;
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
/*
|
|
================================================================================
|
|
PM_CalculateTargetBody
|
|
|
|
Integrates the current state to find the next state.
|
|
|
|
================================================================================
|
|
*/
|
|
static void PM_CalculateTargetBody(car_t *car, carBody_t *sBody, carBody_t *tBody, carPoint_t *sPoints, carPoint_t *tPoints, float time){
|
|
int i;
|
|
float forwardScale, rightScale, upScale;
|
|
vec3_t diff;
|
|
|
|
// move body
|
|
PM_AccelerateAndMoveBody( car, sBody, tBody, time );
|
|
|
|
// move wheels
|
|
for (i = 0; i < FIRST_FRAME_POINT; i++){
|
|
PM_AccelerateAndMove( car, &sPoints[i], &tPoints[i], time );
|
|
}
|
|
|
|
// set locations and velocities of frame points
|
|
PM_InitializeFrame(tBody, &tPoints[FL_FRAME], 1.0f, -1.0f, 0.0f);
|
|
PM_InitializeFrame(tBody, &tPoints[FR_FRAME], 1.0f, 1.0f, 0.0f);
|
|
PM_InitializeFrame(tBody, &tPoints[RL_FRAME], -1.0f, -1.0f, 0.0f);
|
|
PM_InitializeFrame(tBody, &tPoints[RR_FRAME], -1.0f, 1.0f, 0.0f);
|
|
|
|
// body collision points
|
|
forwardScale = ((CAR_LENGTH/2.0f) - BODY_RADIUS) / WHEEL_FORWARD;
|
|
rightScale = ((CAR_WIDTH/2.0f) - BODY_RADIUS) / WHEEL_RIGHT;
|
|
upScale = (0) / -WHEEL_UP;
|
|
PM_InitializeFrame(tBody, &tPoints[FL_BODY], forwardScale, -rightScale, upScale);
|
|
PM_InitializeFrame(tBody, &tPoints[FR_BODY], forwardScale, rightScale, upScale);
|
|
PM_InitializeFrame(tBody, &tPoints[ML_BODY], 0.0f, -rightScale, upScale);
|
|
PM_InitializeFrame(tBody, &tPoints[MR_BODY], 0.0f, rightScale, upScale);
|
|
PM_InitializeFrame(tBody, &tPoints[RL_BODY], -forwardScale, -rightScale, upScale);
|
|
PM_InitializeFrame(tBody, &tPoints[RR_BODY], -forwardScale, rightScale, upScale);
|
|
|
|
PM_InitializeFrame(tBody, &tPoints[ML_ROOF], 0.0f, -rightScale, 0.6f);
|
|
PM_InitializeFrame(tBody, &tPoints[MR_ROOF], 0.0f, rightScale, 0.6f);
|
|
|
|
// calculate spring lengths
|
|
for( i = 0; i < FIRST_FRAME_POINT; i++ )
|
|
{
|
|
VectorSubtract( tPoints[i+4].r, tPoints[i].r, diff );
|
|
tBody->curSpringLengths[i] = DotProduct( diff, tBody->up );
|
|
}
|
|
|
|
// set the center of mass
|
|
PM_SetCoM(tBody, tPoints);
|
|
}
|
|
|
|
|
|
/*
|
|
qboolean PM_Hit_Car( trace_t trace ){
|
|
// hit another car
|
|
vec3_t f, r, u, delta;
|
|
float dotF, dotR, dotU;
|
|
qboolean lengthHitOK, widthHitOK, heightHitOK;
|
|
|
|
lengthHitOK = qfalse;
|
|
widthHitOK = qfalse;
|
|
heightHitOK = qfalse;
|
|
|
|
VectorSubtract(trace.endpos, pm->cars[trace.entityNum]->sBody.r, delta);
|
|
OrientationToVectors(pm->cars[trace.entityNum]->sBody.t, f, r, u);
|
|
|
|
// need to add radius of trace box to this check?
|
|
dotF = DotProduct(delta, f);
|
|
dotR = DotProduct(delta, r);
|
|
dotU = DotProduct(delta, u);
|
|
|
|
if (dotU < CAR_HEIGHT/2 + WHEEL_RADIUS && dotU > -CAR_HEIGHT/2 - WHEEL_RADIUS){
|
|
heightHitOK = qtrue;
|
|
}
|
|
if (dotF < CAR_LENGTH/2 + WHEEL_RADIUS && dotF > -CAR_LENGTH/2 - WHEEL_RADIUS){
|
|
lengthHitOK = qtrue;
|
|
}
|
|
if (dotR < CAR_WIDTH/2 + WHEEL_RADIUS && dotR > -CAR_WIDTH/2 - WHEEL_RADIUS){
|
|
widthHitOK = qtrue;
|
|
}
|
|
|
|
// if (heightHitOK){
|
|
#ifdef GAME
|
|
// G_LogPrintf( "%d hit %d\n", pm->ps->clientNum, trace.entityNum);
|
|
// G_LogPrintf( "trace.endpos : %f, %f, %f\n", trace.endpos[0], trace.endpos[1], trace.endpos[2]);
|
|
// G_LogPrintf( "pm->cars[trace.entityNum]->sBody.r : %f, %f, %f\n", pm->cars[trace.entityNum]->sBody.r[0], pm->cars[trace.entityNum]->sBody.r[1], pm->cars[trace.entityNum]->sBody.r[2]);
|
|
// G_LogPrintf( "delta : %f, %f, %f\n", delta[0], delta[1], delta[2]);
|
|
|
|
// G_LogPrintf( "height dist: %f, max %f\n", dotU, CAR_HEIGHT/2 + WHEEL_RADIUS);
|
|
// G_LogPrintf( "length dist: %f, max %f\n", dotF, CAR_LENGTH/2 + WHEEL_RADIUS);
|
|
// G_LogPrintf( "width dist: %f, max %f\n", dotR, CAR_WIDTH/2 + WHEEL_RADIUS);
|
|
// G_LogPrintf( "f : %f, %f, %f\n", f[0], f[1], f[2]);
|
|
// G_LogPrintf( "r : %f, %f, %f\n", r[0], r[1], r[2]);
|
|
// G_LogPrintf( "u : %f, %f, %f\n", u[0], u[1], u[2]);
|
|
#endif
|
|
// }
|
|
|
|
if (widthHitOK){
|
|
#ifdef GAME
|
|
G_LogPrintf("width is ok\n");
|
|
#endif
|
|
return qtrue;
|
|
}
|
|
|
|
if (lengthHitOK && heightHitOK && widthHitOK){
|
|
#ifdef GAME
|
|
G_LogPrintf("all dimensions are ok\n");
|
|
#endif
|
|
return qtrue;
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
*/
|
|
|
|
|
|
/*
|
|
===================
|
|
PM_Trace_Points
|
|
|
|
Traces out the points from current state to next state to check for collisions.
|
|
Partially based on the PM_SlideMove function.
|
|
|
|
===================
|
|
*/
|
|
#define MAX_CLIP_PLANES 3
|
|
static void PM_Trace_Points( car_t *car, carPoint_t *sPoints, carPoint_t *tPoints, float time ){
|
|
vec3_t maxs, mins;
|
|
vec3_t start, dest, dir;
|
|
//vec3_t normal;
|
|
trace_t trace;
|
|
int i, j;
|
|
//float minTrace = 1.0f;
|
|
//int hitFirst, hitEnt;
|
|
carPoint_t *sPoint, *tPoint;
|
|
|
|
//int count = 0;
|
|
vec3_t hitOrigin;
|
|
|
|
// new stuff
|
|
int bumpcount, numbumps;
|
|
float d;
|
|
int numplanes;
|
|
vec3_t vel;
|
|
vec3_t clipVelocity;
|
|
int k;
|
|
//int l;
|
|
float time_left;
|
|
float into;
|
|
|
|
numbumps = 4;
|
|
// end
|
|
|
|
VectorClear(hitOrigin);
|
|
|
|
// should actually do all points
|
|
for( i = 0 ; i < NUM_CAR_POINTS ; i++ ) {
|
|
// skip collision detection of suspension points
|
|
if( i >= FIRST_FRAME_POINT && i < LAST_FRAME_POINT ) continue;
|
|
|
|
sPoint = &sPoints[i];
|
|
tPoint = &tPoints[i];
|
|
|
|
VectorSet( mins, -sPoint->radius, -sPoint->radius, -sPoint->radius );
|
|
VectorSet( maxs, sPoint->radius, sPoint->radius, sPoint->radius );
|
|
|
|
if( i >= LAST_FRAME_POINT ) {
|
|
mins[2] /= 1.5f;
|
|
maxs[2] /= 1.5f;
|
|
}
|
|
|
|
/*
|
|
VectorSubtract( car->sBody.r, sPoints[i].r, dir);
|
|
if (VectorLength(dir))
|
|
VectorScale(dir, 20.0f / VectorLength(dir), dir);
|
|
|
|
VectorCopy( sPoints[i].r, start );
|
|
VectorAdd( start, dir, start );
|
|
|
|
VectorCopy( tPoints[i].r, dest );
|
|
|
|
// trace the frame to target position
|
|
pm->trace( &trace, start, mins, maxs, dest, pm->ps->clientNum, pm->tracemask );
|
|
|
|
#ifdef GAME
|
|
if (trace.contents & CONTENTS_BODY){
|
|
// G_LogPrintf("car was hit\n");
|
|
if (trace.startsolid)
|
|
Com_Printf("start solid and hit CONTENTS_BODY\n");
|
|
if (trace.allsolid)
|
|
Com_Printf("all solid and hit CONTENTS_BODY\n");
|
|
|
|
// need to make it at least a tiny ways
|
|
if (trace.fraction != 0.0f){
|
|
if (trace.fraction < minTrace){
|
|
minTrace = trace.fraction;
|
|
VectorClear(hitOrigin);
|
|
count = 0;
|
|
}
|
|
if (trace.fraction == minTrace){
|
|
VectorAdd(hitOrigin, trace.endpos, hitOrigin);
|
|
count++;
|
|
if (g_entities[trace.entityNum].flags & FL_EXTRA_BBOX)
|
|
hitEnt = g_entities[trace.entityNum].r.ownerNum;
|
|
else
|
|
hitEnt = trace.entityNum;
|
|
|
|
// PM_CheckSurfaceFlags( &trace, &tPoints[i] );
|
|
}
|
|
|
|
// PM_SetFluidDensity(tPoints, i);
|
|
// continue;
|
|
}
|
|
|
|
if (trace.fraction == 0)
|
|
Com_Printf("fraction == 0 and hit CONTENTS_BODY\n");
|
|
|
|
// trace the frame to target position but skip other cars
|
|
pm->trace( &trace, start, mins, maxs, dest, pm->ps->clientNum, pm->tracemask & ~CONTENTS_BODY );
|
|
}
|
|
|
|
VectorClear(tPoints[i].normals[1]);
|
|
#endif
|
|
|
|
if (trace.contents & CONTENTS_BODY){
|
|
Com_Printf("hit body again?\n");
|
|
}
|
|
|
|
// if the frame is flush on the ground
|
|
if( trace.fraction == 0.0F ) {
|
|
tPoints[i].onGround = qtrue;
|
|
if( VectorLength(trace.plane.normal) > 0.0F ) {
|
|
VectorCopy( trace.plane.normal, tPoints[i].normals[0] );
|
|
}
|
|
|
|
PM_CheckSurfaceFlags( &trace, &tPoints[i] );
|
|
} else if (trace.fraction < 1.0F){
|
|
tPoints[i].onGround = qtrue;
|
|
VectorCopy( trace.plane.normal, tPoints[i].normals[0] );
|
|
|
|
PM_CheckSurfaceFlags( &trace, &tPoints[i] );
|
|
} else {
|
|
tPoints[i].onGround = qfalse;
|
|
continue;
|
|
}
|
|
*/
|
|
|
|
// move the point in towards the center of the car
|
|
// this is to try to stop the wheel bboxes from sitting directly on the surface
|
|
// and then not getting a valid trace because it is startsoild
|
|
|
|
VectorSubtract( car->sBody.r, sPoint->r, dir);
|
|
if ( VectorLengthSquared(dir) )
|
|
VectorScale( dir, 20.0f / VectorLength(dir), dir );
|
|
|
|
VectorAdd( sPoint->r, dir, start );
|
|
VectorCopy( tPoint->r, dest );
|
|
|
|
/*
|
|
numTraces++;
|
|
pm->trace( &trace, sPoints[i].r, mins, maxs, sPoints[i].r, pm->ps->clientNum, pm->tracemask );
|
|
|
|
if( trace.startsolid )
|
|
{
|
|
// if we are starting solid then offset the starting point otherwise
|
|
// we dont need to.
|
|
|
|
Com_Printf( "the start is solid!\n" );
|
|
|
|
// try adding on a non zero velocity and the previous normal vector
|
|
// if( VectorLengthSquared( sPoints[i].v ) == 0.0f )
|
|
// VectorMA( sPoints[i].lastNonZeroVelocity, -1.0f, sPoints[i].normals[0], dir );
|
|
// else
|
|
VectorMA( sPoints[i].v, -20.0f, sPoints[i].normals[0], dir );
|
|
VectorNormalize( dir );
|
|
VectorSubtract( sPoints[i].r, dir, start );
|
|
}
|
|
else
|
|
VectorCopy( sPoints[i].r, start );
|
|
VectorCopy( tPoints[i].r, dest );
|
|
*/
|
|
|
|
// OPTIMIZE: just use sPoints[i].v if not startsolid?
|
|
// VectorSubtract( dest, start, vel );
|
|
VectorSubtract( tPoint->r, start, vel );
|
|
VectorScale( vel, 1 / time, vel );
|
|
|
|
numplanes = 0;
|
|
time_left = time;
|
|
|
|
for ( bumpcount=0 ; bumpcount < numbumps ; bumpcount++ ) {
|
|
VectorMA( start, time_left, vel, dest );
|
|
|
|
// see if we can make it there
|
|
numTraces++;
|
|
pm->trace ( &trace, start, mins, maxs, dest, pm->ps->clientNum, pm->tracemask);
|
|
|
|
if ( trace.startsolid && !bumpcount ) {
|
|
VectorCopy( sPoint->r, start );
|
|
VectorSubtract( dest, start, vel );
|
|
VectorScale( vel, 1 / time, vel );
|
|
|
|
// Com_Printf("Start point %d is in a solid\n", i);
|
|
continue;
|
|
}
|
|
|
|
if (trace.allsolid) {
|
|
// entity is completely trapped in another solid
|
|
// Com_Printf("trace %d is all solid\n", i);
|
|
break;
|
|
}
|
|
|
|
if (trace.fraction > 0) {
|
|
// actually covered some distance
|
|
// VectorCopy ( trace.endpos, start );
|
|
VectorScale ( trace.endpos, 0.999f, start );
|
|
}
|
|
|
|
if ( trace.fraction == 1 ) {
|
|
if ( !bumpcount ){
|
|
tPoint->onGround = qfalse;
|
|
VectorClear(tPoint->normals[0]);
|
|
VectorClear(tPoint->normals[1]);
|
|
VectorClear(tPoint->normals[2]);
|
|
}
|
|
|
|
break; // moved the entire distance
|
|
}
|
|
|
|
time_left -= time_left * trace.fraction;
|
|
|
|
if ( numplanes >= MAX_CLIP_PLANES ) {
|
|
// this shouldn't really happen
|
|
// Com_Printf("numplanes >= MAX_CLIP_PLANES\n");
|
|
break;
|
|
}
|
|
|
|
//
|
|
// if this is the same plane we hit before, nudge velocity
|
|
// out along it, which fixes some epsilon issues with
|
|
// non-axial planes
|
|
//
|
|
|
|
for ( j = 0 ; j < numplanes; j++ ) {
|
|
if ( DotProduct( trace.plane.normal, tPoint->normals[j] ) > 0.99 ) {
|
|
VectorAdd( trace.plane.normal, vel, vel );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( j < numplanes ) {
|
|
continue;
|
|
}
|
|
|
|
#ifdef GAME
|
|
if (trace.contents & CONTENTS_BODY){
|
|
if (g_entities[trace.entityNum].flags & FL_EXTRA_BBOX){
|
|
hitFirst = g_entities[trace.entityNum].r.ownerNum;
|
|
// Com_Printf("Hit extra bbox\n");
|
|
}
|
|
else
|
|
hitFirst = trace.entityNum;
|
|
|
|
if (g_entities[hitFirst].client){
|
|
|
|
// G_LogPrintf("car was hit\n");
|
|
// if (trace.startsolid)
|
|
// Com_Printf("start solid and hit CONTENTS_BODY\n");
|
|
// if (trace.allsolid)
|
|
// Com_Printf("all solid and hit CONTENTS_BODY\n");
|
|
|
|
// need to make it at least a tiny ways
|
|
if (trace.fraction != 0.0f)
|
|
{
|
|
if (trace.fraction < minTrace)
|
|
{
|
|
minTrace = trace.fraction;
|
|
VectorClear(hitOrigin);
|
|
count = 0;
|
|
}
|
|
|
|
if (trace.fraction == minTrace)
|
|
{
|
|
VectorAdd(hitOrigin, trace.endpos, hitOrigin);
|
|
count++;
|
|
hitEnt = hitFirst;
|
|
|
|
tPoints[i].onGround = qtrue;
|
|
|
|
// PM_CheckSurfaceFlags( &trace, &tPoints[i] );
|
|
}
|
|
|
|
// PM_SetFluidDensity(tPoints, i);
|
|
// continue;
|
|
}
|
|
else
|
|
{
|
|
// inside another car
|
|
// Com_Printf("fraction == 0 and hit CONTENTS_BODY\n");
|
|
}
|
|
|
|
// trace the frame to target position but skip other cars
|
|
// pm->trace( &trace, start, mins, maxs, dest, pm->ps->clientNum, pm->tracemask & ~CONTENTS_BODY );
|
|
pm->tracemask &= ~CONTENTS_BODY;
|
|
continue;
|
|
}
|
|
else {
|
|
// Com_Printf( "hit non player CONTENTS_BODY\n" );
|
|
PM_AddTouchEnt( trace.entityNum, trace.endpos );
|
|
|
|
pm->tracemask &= ~CONTENTS_BODY;
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
VectorCopy ( trace.plane.normal, tPoint->normals[numplanes] );
|
|
tPoint->onGround = qtrue;
|
|
PM_CheckSurfaceFlags( &trace, tPoint );
|
|
numplanes++;
|
|
|
|
//
|
|
// modify velocity so it parallels all of the clip planes
|
|
//
|
|
|
|
// find a plane that it enters
|
|
for ( j = 0 ; j < numplanes ; j++ ) {
|
|
into = DotProduct( vel, tPoint->normals[j] );
|
|
if ( into > 0.01f ) {
|
|
continue; // move doesn't interact with the plane
|
|
}
|
|
|
|
// see how hard we are hitting things
|
|
// if ( -into > pml.impactSpeed ) {
|
|
// pml.impactSpeed = -into;
|
|
// }
|
|
|
|
// slide along the plane
|
|
VectorMA( vel, -into * OVERCLIP, tPoint->normals[j], clipVelocity );
|
|
// PM_ClipVelocity ( vel, tPoints[i].normals[j], clipVelocity, OVERCLIP );
|
|
|
|
// see if there is a second plane that the new move enters
|
|
for ( k = 0 ; k < numplanes ; k++ ) {
|
|
if ( k == j ) {
|
|
continue;
|
|
}
|
|
|
|
into = DotProduct( clipVelocity, tPoint->normals[k] );
|
|
if ( into > 0.01f ) {
|
|
continue; // move doesn't interact with the plane
|
|
}
|
|
|
|
// try clipping the move to the plane
|
|
VectorMA( clipVelocity, -into * OVERCLIP, tPoint->normals[j], clipVelocity );
|
|
// PM_ClipVelocity( clipVelocity, tPoints[i].normals[k], clipVelocity, OVERCLIP );
|
|
|
|
// see if it goes back into the first clip plane
|
|
if ( DotProduct( clipVelocity, tPoint->normals[j] ) >= 0 ) {
|
|
continue;
|
|
}
|
|
|
|
// slide the original velocity along the crease
|
|
CrossProduct ( tPoint->normals[j], tPoint->normals[k], dir );
|
|
VectorNormalize( dir );
|
|
d = DotProduct( dir, vel );
|
|
VectorScale( dir, d, clipVelocity );
|
|
|
|
// dont do this because i never see that triple plane interaction message
|
|
// so it probably only rarely happens, if ever
|
|
/*
|
|
// see if there is a third plane the the new move enters
|
|
for ( l = 0 ; l < numplanes ; l++ ) {
|
|
if ( l == j || l == k ) {
|
|
continue;
|
|
}
|
|
if ( DotProduct( clipVelocity, tPoints[i].normals[l] ) >= 0.1 ) {
|
|
continue; // move doesn't interact with the plane
|
|
}
|
|
|
|
// stop dead at a tripple plane interaction
|
|
Com_Printf( "triple plane interaction\n" );
|
|
return;
|
|
}
|
|
*/
|
|
}
|
|
|
|
// if we have fixed all interactions, try another move
|
|
VectorCopy( clipVelocity, vel );
|
|
// VectorCopy( endClipVelocity, endVelocity );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// NEW- copy final position to the point
|
|
// VectorCopy( start, tPoints[i].r );
|
|
|
|
PM_SetFluidDensity( tPoints, i );
|
|
}
|
|
|
|
|
|
#ifdef GAME
|
|
if ( count && pm->cars[hitEnt] )
|
|
{
|
|
float impulseDamage;
|
|
|
|
// G_LogPrintf( "minTrace %f\n", minTrace );
|
|
// G_LogPrintf("count = %d\n", count);
|
|
// G_LogPrintf("pml.physicsSplit = %d\n", pml.physicsSplit);
|
|
|
|
// calculate car position at collision
|
|
// G_LogPrintf( "car was hit with %f traced\n", minTrace );
|
|
VectorScale(hitOrigin, 1.0f / count, hitOrigin);
|
|
|
|
// G_LogPrintf("hitOrigin = %f, %f, %f\n", hitOrigin[0], hitOrigin[1], hitOrigin[2]);
|
|
|
|
PM_CalculateTargetBody(car, &car->sBody, &car->tBody, car->sPoints, car->tPoints, time * minTrace);
|
|
|
|
// VectorSubtract(car->tBody.r, pm->cars[hitEnt]->sBody.r, normal);
|
|
VectorSubtract(hitOrigin, pm->cars[hitEnt]->sBody.r, normal);
|
|
VectorNormalize(normal);
|
|
|
|
impulseDamage = PM_ApplyBodyBodyCollision(&car->tBody, car->tPoints, &pm->cars[hitEnt]->sBody, pm->cars[hitEnt]->sPoints, hitOrigin, normal, 0.25f);
|
|
|
|
// set normal on this car
|
|
for (i = 0; i < 3; i++){
|
|
if (!VectorLength(tPoints[hitEnt].normals[i])){
|
|
VectorScale(normal, -1.0f, tPoints[hitEnt].normals[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// damage stuff
|
|
|
|
VectorCopy(hitOrigin, pm->damage.origin);
|
|
VectorCopy(normal, pm->damage.dir);
|
|
pm->damage.dflags = DAMAGE_NO_KNOCKBACK;
|
|
pm->damage.mod = MOD_CAR_COLLISION;
|
|
pm->damage.otherEnt = trace.entityNum;
|
|
|
|
PM_CopyTargetToSource(&car->tBody, &car->sBody, tPoints, sPoints);
|
|
|
|
// run physics again but remove CONTENTS_BODY first so it cant collide with cars
|
|
pml.physicsSplit++;
|
|
pm->tracemask &= ~CONTENTS_BODY;
|
|
PM_DriveMove(car, time * (1.0f - minTrace), qfalse);
|
|
}
|
|
#endif
|
|
|
|
// break the frame up into parts
|
|
// fix this so it doesnt cause infinite loops
|
|
/*
|
|
if (minTrace < 1.0f){
|
|
// Check for touching multiple surfaces with different normals
|
|
// (wheel on ground and against wall)
|
|
|
|
VectorCopy( sPoints[hitFirst].r, start );
|
|
VectorCopy( tPoints[hitFirst].r, dest );
|
|
VectorSubtract(dest, start, dir);
|
|
length = VectorNormalize(dir);
|
|
VectorMA(start, (length * minTrace) - 0.05f, dir, start);
|
|
|
|
VectorClear(normal);
|
|
|
|
for (i = 0; i < 3; i++){
|
|
VectorCopy(start, dest);
|
|
dest[i] += dir[i] > 0.0f ? 0.10f : -0.10f;
|
|
|
|
pm->trace( &trace, start, mins, maxs, dest, pm->ps->clientNum, pm->tracemask );
|
|
if (trace.fraction < 1.0F){
|
|
uniqueNormal = qtrue;
|
|
|
|
for(j = 0; j < 3; j++){
|
|
if (i == j) continue;
|
|
if(VectorCompare(trace.plane.normal, tPoints[hitFirst].normals[j])){
|
|
uniqueNormal = qfalse;
|
|
break;
|
|
}
|
|
}
|
|
if (uniqueNormal && VectorLength(trace.plane.normal)){
|
|
VectorCopy(trace.plane.normal, tPoints[hitFirst].normals[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pml.physicsSplit > 5){
|
|
// Com_Printf("Breaking out of physics loop\n");
|
|
}
|
|
else {
|
|
pml.physicsSplit++;
|
|
|
|
PM_CalculateTargetBody(car, &car->sBody, &car->tBody, car->sPoints, car->tPoints, time * minTrace);
|
|
PM_CopyTargetToSource(&car->tBody, &car->sBody, car->tPoints, car->sPoints);
|
|
PM_DriveMove(car, time * (1.0f - minTrace));
|
|
}
|
|
// }
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
//int trap_Milliseconds( void );
|
|
|
|
/*
|
|
================================================================================
|
|
PM_DriveMove
|
|
|
|
The mother functioner of Q3Rally car movement. Calculates the forces and torques
|
|
on the car, integrates to get the next state and traces to handle collisions with
|
|
the world and other objects.
|
|
|
|
================================================================================
|
|
*/
|
|
void PM_DriveMove( car_t *car, float time, qboolean includeBodies )
|
|
{
|
|
int i;
|
|
//int t, t1, t2, t3, t4, t5;
|
|
|
|
if( car->initializeOnNextMove )
|
|
{
|
|
PM_InitializeVehicle( car, pm->ps->origin, pm->ps->viewangles, pm->ps->velocity );
|
|
car->initializeOnNextMove = qfalse;
|
|
}
|
|
|
|
//t = trap_Milliseconds();
|
|
|
|
numTraces = 0;
|
|
|
|
if (pml.physicsSplit > 2){
|
|
Com_Printf("forcing end of frame\n");
|
|
return;
|
|
}
|
|
|
|
if (time == 0)
|
|
return;
|
|
|
|
// if ( VectorNAN(car->sBody.r) || VectorNAN(car->sBody.v) || VectorNAN(car->sBody.L) )
|
|
if ( VectorNAN(car->sBody.r) || VectorNAN(car->sBody.v) ||
|
|
car->sBody.L[0] > 1<<25 ||
|
|
car->sBody.L[1] > 1<<25 ||
|
|
car->sBody.L[2] > 1<<25 )
|
|
{
|
|
// Com_Printf( "Blowing up car because of car body\n" );
|
|
pm->damage.damage = 32000;
|
|
pm->damage.dflags = DAMAGE_NO_PROTECTION;
|
|
pm->damage.otherEnt = -1;
|
|
pm->damage.mod = MOD_HIGH_FORCES;
|
|
return;
|
|
}
|
|
|
|
CP_CURRENT_GRAVITY = (pm->ps->gravity / 800.0f) * CP_GRAVITY;
|
|
|
|
// set spring strengths for "jump" and "crouch"
|
|
// FIXME: change this so it works in cgame and game
|
|
#if GAME
|
|
car->springStrength = pm->car_spring * (CP_FRAME_MASS / 350.0f) * CP_GRAVITY;
|
|
#else
|
|
car->springStrength = CP_SPRING_STRENGTH;
|
|
#endif
|
|
// car->shockStrength = CP_SHOCK_STRENGTH;
|
|
|
|
if (pm->cmd.upmove > 0){
|
|
car->springStrength *= 5.0f;
|
|
}
|
|
else if (pm->cmd.upmove < 0){
|
|
car->springStrength /= 5.0f;
|
|
}
|
|
|
|
pm->damage.damage = 0;
|
|
pm->damage.otherEnt = -1;
|
|
|
|
//t1 = trap_Milliseconds();
|
|
|
|
// calculate target positions etc
|
|
// -------------------------------------------------------------------------
|
|
|
|
// calculate forces
|
|
PM_CalculateForces(car, &car->sBody, car->sPoints, time);
|
|
|
|
//t2 = trap_Milliseconds();
|
|
|
|
// apply frame forces to the body
|
|
// for (i = FIRST_FRAME_POINT; i < LAST_FRAME_POINT; i++){
|
|
for (i = FIRST_FRAME_POINT; i < NUM_CAR_POINTS; i++){
|
|
PM_ApplyForce(&car->sBody, car->sPoints[i].netForce, car->sPoints[i].r);
|
|
// }
|
|
|
|
// for (i = FIRST_FRAME_POINT; i < NUM_CAR_POINTS; i++){
|
|
if (!car->sPoints[i].onGround) continue;
|
|
|
|
PM_CarBodyFrictionForces( car, &car->sBody, car->sPoints, i );
|
|
|
|
if (VectorNAN(car->sPoints[i].netForce))
|
|
Com_Printf("Blowing up car because of car point force on frame\n");
|
|
}
|
|
|
|
//t3 = trap_Milliseconds();
|
|
|
|
// print out the forces
|
|
/*
|
|
if (pm->pDebug > 0 && pm->pDebug <= 8){
|
|
if (pm->client)
|
|
Com_Printf("client sBody -----------------------------------------\n");
|
|
else
|
|
Com_Printf("server sBody -----------------------------------------\n");
|
|
PM_DebugForces(&car->sBody, car->sPoints);
|
|
PM_DebugDynamics(&car->sBody, car->sPoints);
|
|
}
|
|
*/
|
|
|
|
PM_CalculateTargetBody(car, &car->sBody, &car->tBody, car->sPoints, car->tPoints, time);
|
|
|
|
//t4 = trap_Milliseconds();
|
|
|
|
PM_Trace_Points(car, car->sPoints, car->tPoints, time);
|
|
|
|
//t5 = trap_Milliseconds();
|
|
|
|
// print out the forces
|
|
/*
|
|
if (pm->pDebug > 0 && pm->pDebug <= 8){
|
|
if (pm->client)
|
|
Com_Printf("client tBody -----------------------------------------\n");
|
|
else
|
|
Com_Printf("server tBody -----------------------------------------\n");
|
|
// PM_DebugForces(&car->tBody, car->tPoints);
|
|
PM_DebugDynamics(&car->tBody, car->tPoints);
|
|
}
|
|
*/
|
|
|
|
PM_CopyTargetToSource(&car->tBody, &car->sBody, car->tPoints, car->sPoints);
|
|
|
|
// clear forces at the end of the frame instead of at the beginning so we
|
|
// can apply forces from nearby explosions between frames
|
|
PM_ClearCarForces(&car->sBody, car->sPoints);
|
|
|
|
// #ifdef CGAME
|
|
// Com_Printf( "numTraces %i\n", numTraces );
|
|
// #endif
|
|
// Com_Printf( "t1 %d, t2 %d, t3 %d, t4 %d, t5 %d\n", t1 - t, t2 - t, t3 - t, t4 - t, t5 - t );
|
|
}
|
|
|