583 lines
13 KiB
C++
583 lines
13 KiB
C++
// Copyright (C) 2007 Id Software, Inc.
|
|
//
|
|
|
|
#include "../precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "../Game_local.h"
|
|
|
|
static idVec3 vec3_boxEpsilon( CM_BOX_EPSILON, CM_BOX_EPSILON, CM_BOX_EPSILON );
|
|
|
|
#include "../Player.h"
|
|
|
|
const float CLIP_CHECK_FACTOR = CM_CLIP_EPSILON * 0.5f;
|
|
|
|
/*
|
|
===============================================================
|
|
|
|
idClipModel
|
|
|
|
===============================================================
|
|
|
|
*/
|
|
|
|
CLASS_DECLARATION( idClass, idClipModel )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idClipModel::Draw
|
|
================
|
|
*/
|
|
void idClipModel::Draw( const idVec3& origin, const idMat3& axis, float radius, int lifetime ) const {
|
|
idPlayer* localPlayer = gameLocal.GetLocalPlayer();
|
|
if ( !localPlayer ) {
|
|
return;
|
|
}
|
|
|
|
renderView_t* view = localPlayer->GetRenderView();
|
|
if ( !view ) {
|
|
return;
|
|
}
|
|
|
|
if ( collisionModel != NULL ) {
|
|
collisionModelManager->DrawModel( collisionModel, origin, axis, view->vieworg, view->viewaxis, radius, lifetime );
|
|
}
|
|
|
|
for ( int i = 0; i < traceModels.Num(); i++ ) {
|
|
idCollisionModel* cm = gameLocal.traceModelCache.GetCollisionModel( traceModels[ i ] );
|
|
collisionModelManager->DrawModel( cm, origin, axis, view->vieworg, view->viewaxis, radius, lifetime );
|
|
|
|
/* idBounds bounds( idVec3( -1, -1, -1 ), idVec3( 1, 1, 1 ) );
|
|
|
|
const traceModelWater_t* waterPoints = gameLocal.traceModelCache.GetWaterPoints( traceModels[ i ] );
|
|
int j;
|
|
for ( j = 0; j < MAX_TRACEMODEL_WATER_POINTS; j++ ) {
|
|
gameRenderWorld->DebugBox( colorRed, idBox( bounds, origin + ( waterPoints[ j ].xyz * axis ), mat3_identity ) );
|
|
}*/
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::Draw
|
|
================
|
|
*/
|
|
void idClipModel::Draw( float radius ) const {
|
|
Draw( GetOrigin(), GetAxis(), radius );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::FreeModel
|
|
================
|
|
*/
|
|
void idClipModel::FreeModel( void ) {
|
|
int i;
|
|
|
|
for ( i = 0; i < traceModels.Num(); i++ ) {
|
|
gameLocal.traceModelCache.FreeTraceModel( traceModels[i] );
|
|
}
|
|
traceModels.SetNum( 0, false );
|
|
|
|
if ( collisionModel != NULL ) {
|
|
collisionModelManager->FreeModel( collisionModel );
|
|
collisionModel = NULL;
|
|
}
|
|
|
|
renderEntity = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::LoadCollisionModel
|
|
================
|
|
*/
|
|
bool idClipModel::LoadCollisionModel( const char *name ) {
|
|
assert( !IsLinked() );
|
|
if ( IsLinked() ) {
|
|
gameLocal.Error( "LoadCollisionModel called while linked" );
|
|
}
|
|
|
|
FreeModel();
|
|
|
|
if ( name != NULL && *name != '\0' ) {
|
|
collisionModel = collisionModelManager->LoadModel( gameLocal.GetMapName(), name );
|
|
}
|
|
if ( collisionModel != NULL ) {
|
|
bounds = collisionModel->GetBounds();
|
|
absBounds = bounds;
|
|
contents = collisionModel->GetContents();
|
|
backupContents = contents;
|
|
return true;
|
|
} else {
|
|
bounds.Zero();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::LoadCollisionModel
|
|
================
|
|
*/
|
|
void idClipModel::LoadCollisionModel( idCollisionModel *model ) {
|
|
assert( !IsLinked() );
|
|
if ( IsLinked() ) {
|
|
gameLocal.Error( "LoadCollisionModel called while linked" );
|
|
}
|
|
|
|
FreeModel();
|
|
|
|
collisionModel = model;
|
|
bounds = collisionModel->GetBounds();
|
|
absBounds = bounds;
|
|
contents = collisionModel->GetContents();
|
|
backupContents = contents;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::LoadTraceModel
|
|
================
|
|
*/
|
|
void idClipModel::LoadTraceModel( const idTraceModel &trm, bool includeBrushes ) {
|
|
assert( !IsLinked() );
|
|
if ( IsLinked() ) {
|
|
gameLocal.Error( "LoadTraceModel called while linked" );
|
|
}
|
|
|
|
FreeModel();
|
|
|
|
traceModels.Append( gameLocal.traceModelCache.AllocTraceModel( trm, includeBrushes ) );
|
|
bounds = trm.bounds;
|
|
absBounds = bounds;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::LoadRenderModel
|
|
================
|
|
*/
|
|
void idClipModel::LoadRenderModel( qhandle_t renderEntity ) {
|
|
assert( !IsLinked() );
|
|
if ( IsLinked() ) {
|
|
gameLocal.Error( "LoadRenderModel called while linked" );
|
|
}
|
|
|
|
FreeModel();
|
|
|
|
if ( renderEntity != -1 ) {
|
|
this->renderEntity = renderEntity;
|
|
this->bounds = gameRenderWorld->GetRenderEntity( renderEntity )->hModel->Bounds();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::Init
|
|
================
|
|
*/
|
|
void idClipModel::Init( void ) {
|
|
entity = NULL;
|
|
entityNumber = ENTITYNUM_NONE;
|
|
id = 0;
|
|
origin.Zero();
|
|
axis.Identity();
|
|
bounds.Zero();
|
|
absBounds.Zero();
|
|
material = NULL;
|
|
contents = CONTENTS_BODY;
|
|
backupContents = contents;
|
|
collisionModel = NULL;
|
|
renderEntity = 0;
|
|
traceModels.Clear();
|
|
clipLinks = NULL;
|
|
lastLinkCoords[0] = 0;
|
|
lastLinkCoords[1] = 0;
|
|
lastLinkCoords[2] = 0;
|
|
lastLinkCoords[3] = 0;
|
|
nextDeleted = NULL;
|
|
deleteThreadCount = 0;
|
|
lastMailBox = 0xffffffff;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::idClipModel
|
|
================
|
|
*/
|
|
idClipModel::idClipModel( void ) {
|
|
Init();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::idClipModel
|
|
================
|
|
*/
|
|
idClipModel::idClipModel( const char *name ) {
|
|
Init();
|
|
LoadCollisionModel( name );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::idClipModel
|
|
================
|
|
*/
|
|
idClipModel::idClipModel( idCollisionModel *model ) {
|
|
Init();
|
|
LoadCollisionModel( model );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::idClipModel
|
|
================
|
|
*/
|
|
idClipModel::idClipModel( const idTraceModel &trm, bool includeBrushes ) {
|
|
Init();
|
|
LoadTraceModel( trm, includeBrushes );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::idClipModel
|
|
================
|
|
*/
|
|
idClipModel::idClipModel( qhandle_t renderEntity ) {
|
|
Init();
|
|
contents = CONTENTS_RENDERMODEL;
|
|
backupContents = contents;
|
|
LoadRenderModel( renderEntity );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::idClipModel
|
|
================
|
|
*/
|
|
idClipModel::idClipModel( const idClipModel *model ) {
|
|
int i;
|
|
|
|
SetEntity( model->entity );
|
|
id = model->id;
|
|
origin = model->origin;
|
|
axis = model->axis;
|
|
bounds = model->bounds;
|
|
absBounds = model->absBounds;
|
|
material = model->material;
|
|
contents = model->contents;
|
|
backupContents = contents;
|
|
if ( model->collisionModel != NULL ) {
|
|
collisionModel = collisionModelManager->LoadModel( gameLocal.GetMapName(), model->collisionModel->GetName() );
|
|
} else {
|
|
collisionModel = NULL;
|
|
}
|
|
traceModels.SetNum( model->traceModels.Num() );
|
|
for ( i = 0; i < model->traceModels.Num(); i++ ) {
|
|
traceModels[i] = gameLocal.traceModelCache.CopyTraceModel( model->traceModels[i] );
|
|
}
|
|
renderEntity = model->renderEntity;
|
|
clipLinks = NULL;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::~idClipModel
|
|
================
|
|
*/
|
|
idClipModel::~idClipModel( void ) {
|
|
// make sure the clip model is no longer linked
|
|
FreeModel();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::SetEntity
|
|
================
|
|
*/
|
|
void idClipModel::SetEntity( idEntity *newEntity ) {
|
|
entity = newEntity;
|
|
entityNumber = ( newEntity != NULL ) ? newEntity->entityNumber : ENTITYNUM_WORLD;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::GetEntityName
|
|
================
|
|
*/
|
|
const char *idClipModel::GetEntityName( void ) const {
|
|
if ( collisionModelManager->GetThreadId() == MAIN_THREAD_ID && GetEntity() != NULL ) {
|
|
return GetEntity()->GetName();
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::SetPosition
|
|
================
|
|
*/
|
|
void idClipModel::SetPosition( const idVec3 &newOrigin, const idMat3 &newAxis, idClip &clp ) {
|
|
origin = newOrigin;
|
|
axis = newAxis;
|
|
|
|
if ( CheckCoords( clp ) ) {
|
|
Unlink( clp ); // unlink from old position
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idClipModel::GetMassProperties
|
|
================
|
|
*/
|
|
void idClipModel::GetMassProperties( const float density, float &mass, idVec3 ¢erOfMass, idMat3 &inertiaTensor ) const {
|
|
if ( traceModels.Num() == 0 ) {
|
|
|
|
gameLocal.Error( "idClipModel::GetMassProperties: clip model %d on '%s' is not a trace model", id, entity->GetName() );
|
|
|
|
} else if ( traceModels.Num() == 1 ) {
|
|
|
|
gameLocal.traceModelCache.GetMassProperties( traceModels[0], density, mass, centerOfMass, inertiaTensor );
|
|
|
|
} else {
|
|
|
|
int i;
|
|
float trmMass;
|
|
idVec3 trmCenterOfMass;
|
|
idMat3 trmInertiaTensor;
|
|
|
|
mass = 0.0f;
|
|
centerOfMass.Zero();
|
|
inertiaTensor.Zero();
|
|
|
|
for ( i = 0; i < traceModels.Num(); i++ ) {
|
|
gameLocal.traceModelCache.GetMassProperties( traceModels[i], density, trmMass, trmCenterOfMass, trmInertiaTensor );
|
|
mass += trmMass;
|
|
centerOfMass += trmMass * trmCenterOfMass;
|
|
}
|
|
centerOfMass /= mass;
|
|
|
|
for ( i = 0; i < traceModels.Num(); i++ ) {
|
|
gameLocal.traceModelCache.GetMassProperties( traceModels[i], density, trmMass, trmCenterOfMass, trmInertiaTensor );
|
|
trmInertiaTensor.InertiaTranslateSelf( trmMass, trmCenterOfMass, centerOfMass - trmCenterOfMass );
|
|
inertiaTensor += trmInertiaTensor;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idClipModel::Unlink
|
|
===============
|
|
*/
|
|
void idClipModel::Unlink( idClip &clp ) {
|
|
clipLink_t *link;
|
|
|
|
clp.Lock();
|
|
|
|
for ( link = clipLinks; link; link = clipLinks ) {
|
|
clipLinks = link->nextLink;
|
|
if ( link->prevInSector ) {
|
|
link->prevInSector->nextInSector = link->nextInSector;
|
|
} else {
|
|
link->sector->clipLinks = link->nextInSector;
|
|
}
|
|
if ( link->nextInSector ) {
|
|
link->nextInSector->prevInSector = link->prevInSector;
|
|
}
|
|
|
|
clp.GetClipLinkAllocator().Free( link );
|
|
}
|
|
|
|
clp.Unlock();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idClipModel::CheckCoords
|
|
===============
|
|
*/
|
|
bool idClipModel::CheckCoords( idClip& clp ) {
|
|
// set the abs box
|
|
if ( axis.IsRotated() ) {
|
|
// expand for rotation
|
|
absBounds.FromTransformedBounds( bounds, origin, axis );
|
|
} else {
|
|
// normal
|
|
absBounds[0] = bounds[0] + origin;
|
|
absBounds[1] = bounds[1] + origin;
|
|
}
|
|
|
|
// because movement is clipped an epsilon away from an actual edge,
|
|
// we must fully check even when bounding boxes don't quite touch
|
|
absBounds[0] -= vec3_boxEpsilon;
|
|
absBounds[1] += vec3_boxEpsilon;
|
|
|
|
int coords[ 4 ];
|
|
clp.CoordsForBounds( coords, absBounds );
|
|
|
|
if ( IsLinked() ) {
|
|
if ( ( lastLinkCoords[ 0 ] == coords[ 0 ] ) &&
|
|
( lastLinkCoords[ 1 ] == coords[ 1 ] ) &&
|
|
( lastLinkCoords[ 2 ] == coords[ 2 ] ) &&
|
|
( lastLinkCoords[ 3 ] == coords[ 3 ] ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
lastLinkCoords[ 0 ] = coords[ 0 ];
|
|
lastLinkCoords[ 1 ] = coords[ 1 ];
|
|
lastLinkCoords[ 2 ] = coords[ 2 ];
|
|
lastLinkCoords[ 3 ] = coords[ 3 ];
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idClipModel::Link
|
|
===============
|
|
*/
|
|
void idClipModel::Link( idClip &clp ) {
|
|
if ( !entity ) {
|
|
assert( false );
|
|
return;
|
|
}
|
|
|
|
if ( !clp.GetClipSectors() || entity->fl.forceDisableClip ) {
|
|
return;
|
|
}
|
|
|
|
if ( bounds.IsCleared() ) {
|
|
return;
|
|
}
|
|
|
|
if ( !CheckCoords( clp ) ) {
|
|
return;
|
|
}
|
|
|
|
Unlink( clp ); // unlink from old position
|
|
|
|
clp.Lock();
|
|
|
|
int x, y;
|
|
for( x = lastLinkCoords[ 0 ]; x < lastLinkCoords[ 2 ]; x++ ) {
|
|
for( y = lastLinkCoords[ 1 ]; y < lastLinkCoords[ 3 ]; y++ ) {
|
|
clipSector_t* sector = &clp.GetClipSectors()[ x + ( y << CLIPSECTOR_DEPTH ) ];
|
|
|
|
clipLink_t* link = clp.GetClipLinkAllocator().Alloc();
|
|
link->clipModel = this;
|
|
link->sector = sector;
|
|
link->nextInSector = sector->clipLinks;
|
|
link->prevInSector = NULL;
|
|
if ( sector->clipLinks ) {
|
|
sector->clipLinks->prevInSector = link;
|
|
}
|
|
sector->clipLinks = link;
|
|
link->nextLink = clipLinks;
|
|
clipLinks = link;
|
|
}
|
|
}
|
|
|
|
clp.Unlock();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idClipModel::Link
|
|
===============
|
|
*/
|
|
void idClipModel::Link( idClip &clp, idEntity *ent, int newId, const idVec3 &newOrigin, const idMat3 &newAxis, int _renderModelHandle ) {
|
|
SetEntity( ent );
|
|
id = newId;
|
|
|
|
origin = newOrigin;
|
|
axis = newAxis;
|
|
|
|
if ( _renderModelHandle != -1 ) {
|
|
renderEntity = _renderModelHandle;
|
|
const renderEntity_t* renderEnt = gameRenderWorld->GetRenderEntity( renderEntity );
|
|
if ( renderEnt ) {
|
|
bounds = renderEnt->bounds;
|
|
}
|
|
}
|
|
|
|
Link( clp );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idClipModel::GetTraceModel
|
|
===============
|
|
*/
|
|
const idTraceModel *idClipModel::GetTraceModel( int index ) const {
|
|
if ( index < traceModels.Num() ) {
|
|
return gameLocal.traceModelCache.GetTraceModel( traceModels[index] );
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idClipModel::GetWaterPoints
|
|
===============
|
|
*/
|
|
const traceModelWater_t* idClipModel::GetWaterPoints( int index ) const {
|
|
if ( index < traceModels.Num() ) {
|
|
return gameLocal.traceModelCache.GetWaterPoints( traceModels[ index ] );
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idClipModel::GetTraceModelVolume
|
|
===============
|
|
*/
|
|
float idClipModel::GetTraceModelVolume( int index ) const {
|
|
if ( index < traceModels.Num() ) {
|
|
return gameLocal.traceModelCache.GetVolume( traceModels[ index ] );
|
|
} else {
|
|
return 0.f;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idClipModel::IsEqual
|
|
===============
|
|
*/
|
|
bool idClipModel::IsEqual( const idTraceModel &trm ) const {
|
|
return ( traceModels.Num() != 0 && *gameLocal.traceModelCache.GetTraceModel( traceModels[0] ) == trm );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idClipModel::GetCollisionModel
|
|
===============
|
|
*/
|
|
idCollisionModel *idClipModel::GetCollisionModel( int index ) const {
|
|
if ( collisionModel != NULL ) {
|
|
return collisionModel;
|
|
} else {
|
|
if ( index >= 0 && index < traceModels.Num() ) {
|
|
return gameLocal.traceModelCache.GetCollisionModel( traceModels[index] );
|
|
}
|
|
}
|
|
if ( collisionModelManager->GetThreadId() == MAIN_THREAD_ID ) {
|
|
if ( entity != NULL ) {
|
|
gameLocal.Warning( "idClipModel::GetCollisionModel: clip model %d on '%s' (%i) is not a collision or trace model", id, entity->GetName(), entity->entityNumber );
|
|
} else {
|
|
assert( false );
|
|
gameLocal.Warning( "idClipModel::GetCollisionModel: clip model %d on NULL entity is not a collision or trace model", id );
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|