517 lines
13 KiB
C++
517 lines
13 KiB
C++
// Copyright (C) 2007 Id Software, Inc.
|
|
//
|
|
|
|
#include "../precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "TraceModelCache.h"
|
|
|
|
#define TRM_HEADER "TRM"
|
|
#define TRM_VERSION "1.2"
|
|
|
|
/*
|
|
===============
|
|
idTraceModelCache::ClearTraceModelCache
|
|
===============
|
|
*/
|
|
void idTraceModelCache::ClearTraceModelCache( void ) {
|
|
for ( int i = 0; i < cache.Num(); i++ ) {
|
|
collisionModelManager->FreeModel( cache[ i ]->collisionModel );
|
|
cache[ i ]->collisionModel = NULL;
|
|
}
|
|
cache.Clear();
|
|
fileCache.Clear();
|
|
allocator.Shutdown();
|
|
hash.Free();
|
|
nameHash.Free();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idTraceModelCache::TraceModelCacheSize
|
|
===============
|
|
*/
|
|
size_t idTraceModelCache::TraceModelCacheSize( void ) {
|
|
return cache.Size();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idTraceModelCache::FindTraceModel
|
|
===============
|
|
*/
|
|
int idTraceModelCache::FindTraceModel( const idTraceModel& trm, bool includeBrushes ) {
|
|
int hashKey = GetTraceModelHashKey( trm );
|
|
for ( int i = hash.GetFirst( hashKey ); i != idHashIndex::NULL_INDEX; i = hash.GetNext( i ) ) {
|
|
if ( cache[ i ]->trm != trm ) {
|
|
continue;
|
|
}
|
|
|
|
if ( cache[ i ]->includesBrushes != includeBrushes ) {
|
|
continue;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idTraceModelCache::PrecacheTraceModel
|
|
===============
|
|
*/
|
|
int idTraceModelCache::PrecacheTraceModel( const idTraceModel &trm, const char* fileName ) {
|
|
int index = AllocTraceModel( trm, false );
|
|
cache[ index ]->refCount--;
|
|
if ( fileName != NULL ) {
|
|
AllocFileEntry( fileName, index );
|
|
}
|
|
return index;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idTraceModelCache::AllocTraceModel
|
|
===============
|
|
*/
|
|
int idTraceModelCache::AllocTraceModel( const idTraceModel &trm, bool includeBrushes ) {
|
|
int i, hashKey, traceModelIndex;
|
|
|
|
hashKey = GetTraceModelHashKey( trm );
|
|
for ( i = hash.GetFirst( hashKey ); i != idHashIndex::NULL_INDEX; i = hash.GetNext( i ) ) {
|
|
if ( cache[ i ]->trm == trm ) {
|
|
cache[ i ]->refCount++;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
traceModelIndex = cache.Num();
|
|
hash.Add( hashKey, traceModelIndex );
|
|
|
|
trmCache_t& entry = *( cache.Alloc() = allocator.Alloc() );
|
|
entry.trm = trm;
|
|
entry.trm.ClearUnused();
|
|
entry.trm.GetMassProperties( 1.0f, entry.volume, entry.centerOfMass, entry.inertiaTensor );
|
|
entry.refCount = 1;
|
|
entry.collisionModel = collisionModelManager->ModelFromTrm( gameLocal.GetMapName(), va( "traceModel%d", traceModelIndex ), trm, includeBrushes );
|
|
entry.hasWater = false;
|
|
entry.includesBrushes = includeBrushes;
|
|
SetupWaterPoints( entry );
|
|
|
|
return traceModelIndex;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idTraceModelCache::FreeTraceModel
|
|
===============
|
|
*/
|
|
void idTraceModelCache::FreeTraceModel( const int traceModelIndex ) {
|
|
if ( traceModelIndex < 0 || traceModelIndex >= cache.Num() || cache[ traceModelIndex ]->refCount <= 0 ) {
|
|
gameLocal.Warning( "idClipModel::FreeTraceModel: tried to free uncached trace model" );
|
|
return;
|
|
}
|
|
cache[ traceModelIndex ]->refCount--;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idTraceModelCache::CopyTraceModel
|
|
===============
|
|
*/
|
|
int idTraceModelCache::CopyTraceModel( const int traceModelIndex ) {
|
|
if ( traceModelIndex < 0 || traceModelIndex >= cache.Num() || cache[ traceModelIndex ]->refCount <= 0 ) {
|
|
gameLocal.Warning( "idTraceModelCache::CopyTraceModel: tried to copy an uncached trace model" );
|
|
return -1;
|
|
}
|
|
cache[ traceModelIndex ]->refCount++;
|
|
return traceModelIndex;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idTraceModelCache::GetMassProperties
|
|
===============
|
|
*/
|
|
void idTraceModelCache::GetMassProperties( const int traceModelIndex, const float density, float &mass, idVec3 ¢erOfMass, idMat3 &inertiaTensor ) const {
|
|
if ( traceModelIndex < 0 || traceModelIndex >= cache.Num() || cache[ traceModelIndex ]->refCount <= 0 ) {
|
|
assert( 0 );
|
|
gameLocal.Warning( "idTraceModelCache::GetMassProperties: tried to use an uncached trace model" );
|
|
inertiaTensor.Identity();
|
|
centerOfMass.Zero();
|
|
mass = 1.0f;
|
|
return;
|
|
}
|
|
|
|
const trmCache_t* entry = cache[ traceModelIndex ];
|
|
|
|
mass = entry->volume * density;
|
|
centerOfMass = entry->centerOfMass;
|
|
inertiaTensor = density * entry->inertiaTensor;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idTraceModelCache::GetTraceModelHashKey
|
|
===============
|
|
*/
|
|
int idTraceModelCache::GetTraceModelHashKey( const idTraceModel &trm ) {
|
|
const idVec3 &v = trm.bounds[0];
|
|
return ( trm.type << 8 ) ^ ( trm.numVerts << 4 ) ^ ( trm.numEdges << 2 ) ^ ( trm.numPolys << 0 ) ^ idMath::FloatHash( v.ToFloatPtr(), v.GetDimension() );
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
NewPolyPoint
|
|
============
|
|
*/
|
|
idTraceModelCache::polyPoint_t idTraceModelCache::polyPointPool[ MAX_TRACEMODEL_WATER_POINTS_POOL ];
|
|
idTraceModelCache::polyPoint_t* idTraceModelCache::freePolyPoints[ MAX_TRACEMODEL_WATER_POINTS_POOL ];
|
|
int idTraceModelCache::numFreePolyPoints = 0;
|
|
bool idTraceModelCache::polyPointPoolValid = false;
|
|
|
|
idTraceModelCache::polyPoint_t* idTraceModelCache::NewPolyPoint( void ) {
|
|
/* polyPoint_t* point = new polyPoint_t;
|
|
numPolyPoints++;
|
|
if ( numPolyPoints > maxNumPolyPoints ) {
|
|
maxNumPolyPoints = numPolyPoints;
|
|
gameLocal.Printf( "***************** maxNumPolyPoints = %i\n", maxNumPolyPoints );
|
|
}*/
|
|
|
|
if ( !polyPointPoolValid ) {
|
|
for ( int i = 0; i < MAX_TRACEMODEL_WATER_POINTS_POOL; i++ ) {
|
|
freePolyPoints[ i ] = &polyPointPool[ i ];
|
|
}
|
|
polyPointPoolValid = true;
|
|
numFreePolyPoints = MAX_TRACEMODEL_WATER_POINTS_POOL;
|
|
}
|
|
|
|
if ( numFreePolyPoints == 0 ) {
|
|
return NULL;
|
|
}
|
|
|
|
numFreePolyPoints--;
|
|
polyPoint_t* point = freePolyPoints[ numFreePolyPoints ];
|
|
return point;
|
|
}
|
|
|
|
/*
|
|
============
|
|
DeletePolyPoint
|
|
============
|
|
*/
|
|
void idTraceModelCache::DeletePolyPoint( polyPoint_t* point ) {
|
|
/* delete point;
|
|
numPolyPoints--;*/
|
|
for ( int i = 0; i < numFreePolyPoints; i++ ) {
|
|
if ( freePolyPoints[ i ] == point ) {
|
|
// WTF - shouldn't happen!
|
|
int poo = 3;
|
|
}
|
|
}
|
|
|
|
freePolyPoints[ numFreePolyPoints++ ] = point;
|
|
point->node.Clear();
|
|
}
|
|
|
|
/*
|
|
============
|
|
DeletePointList
|
|
============
|
|
*/
|
|
void idTraceModelCache::DeletePointList( idLinkList< polyPoint_t >& points ) {
|
|
while ( polyPoint_t* next = points.Next() ) {
|
|
DeletePolyPoint( next );
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
FindClosestPoints
|
|
============
|
|
*/
|
|
void idTraceModelCache::FindClosestPoints( idLinkList< polyPoint_t >& points, polyPointPtr_t& closePoint1, polyPointPtr_t& closePoint2 ) {
|
|
closePoint1 = NULL;
|
|
closePoint2 = NULL;
|
|
float closeDist = idMath::INFINITY;
|
|
|
|
for ( polyPoint_t* next = points.Next(); next; next = next->node.Next() ) {
|
|
for ( polyPoint_t* next2 = next->node.Next(); next2; next2 = next2->node.Next() ) {
|
|
|
|
float dist = ( next->xyz - next2->xyz ).LengthSqr() * next->squareWeight * next2->squareWeight;
|
|
if ( dist < closeDist ) {
|
|
closeDist = dist;
|
|
closePoint1 = next;
|
|
closePoint2 = next2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idTraceModelCache::SetupWaterPoints
|
|
============
|
|
*/
|
|
void idTraceModelCache::SetupWaterPoints( trmCache_t& entry ) {
|
|
idTraceModel& trm = entry.trm;
|
|
if ( !trm.bounds.GetVolume() || !trm.isConvex ) {
|
|
return;
|
|
}
|
|
|
|
numFreePolyPoints = MAX_TRACEMODEL_WATER_POINTS_POOL;
|
|
idLinkList< polyPoint_t > points;
|
|
|
|
const int numPoints = MAX_TRACEMODEL_WATER_POINTS;
|
|
|
|
int count;
|
|
float startScale = 1.f;
|
|
while ( true ) {
|
|
count = 0;
|
|
float numPerSide = pow( numPoints * startScale, 1.f / 3.f );
|
|
idVec3 spacing = ( trm.bounds.GetMaxs() - trm.bounds.GetMins() ) * ( 1 / numPerSide );
|
|
if ( spacing.FixDenormals( 0.00001f ) ) {
|
|
// one of the values is too small!
|
|
return;
|
|
}
|
|
|
|
for ( float x = trm.bounds.GetMins().x; x < trm.bounds.GetMaxs().x; x+= spacing.x ) {
|
|
for ( float y = trm.bounds.GetMins().y; y < trm.bounds.GetMaxs().y; y+= spacing.y ) {
|
|
for ( float z = trm.bounds.GetMins().z; z < trm.bounds.GetMaxs().z; z+= spacing.z ) {
|
|
idVec3 xyz( x, y, z );
|
|
xyz += spacing * 0.5f;
|
|
|
|
if ( !trm.ContainsPoint( xyz ) ) {
|
|
continue;
|
|
}
|
|
polyPoint_t* point = NewPolyPoint();
|
|
if ( point == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
point->node.SetOwner( point );
|
|
point->weight = 1.f;
|
|
point->xyz = xyz;
|
|
|
|
point->node.AddToEnd( points );
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( count < numPoints ) {
|
|
DeletePointList( points );
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
startScale *= 2.f;
|
|
if ( startScale > 16.f ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
for ( polyPoint_t* next = points.Next(); next; next = next->node.Next() ) {
|
|
next->weight /= count;
|
|
next->squareWeight = Square( next->weight );
|
|
}
|
|
|
|
while ( count > numPoints ) {
|
|
polyPoint_t* point1;
|
|
polyPoint_t* point2;
|
|
|
|
FindClosestPoints( points, point1, point2 );
|
|
|
|
point1->xyz = ( point1->xyz + point2->xyz ) * 0.5f;
|
|
point1->weight += point2->weight;
|
|
point1->squareWeight = Square( point1->weight );
|
|
DeletePolyPoint( point2 );
|
|
count--;
|
|
}
|
|
|
|
int i = 0;
|
|
for ( polyPoint_t* next = points.Next(); next; next = next->node.Next() ) {
|
|
entry.waterPoints[ i ].weight = next->weight;
|
|
entry.waterPoints[ i ].xyz = next->xyz;
|
|
i++;
|
|
}
|
|
entry.hasWater = true;
|
|
|
|
DeletePointList( points );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idTraceModelCache::Write
|
|
============
|
|
*/
|
|
void idTraceModelCache::Write( int index, idFile* fp ) {
|
|
if ( index < 0 || index >= cache.Num() ) {
|
|
gameLocal.Warning( "idClipModel::Write: tried to write uncached trace model" );
|
|
return;
|
|
}
|
|
|
|
trmCache_t& entry = *cache[ index ];
|
|
|
|
fp->WriteString( TRM_HEADER );
|
|
fp->WriteString( TRM_VERSION );
|
|
|
|
entry.trm.Write( fp, &TrmNameForMaterial );
|
|
fp->WriteFloat( entry.volume );
|
|
fp->WriteVec3( entry.centerOfMass );
|
|
fp->WriteMat3( entry.inertiaTensor );
|
|
fp->WriteBool( entry.includesBrushes );
|
|
fp->WriteBool( entry.hasWater );
|
|
if ( entry.hasWater ) {
|
|
for ( int i = 0; i < MAX_TRACEMODEL_WATER_POINTS; i++ ) {
|
|
fp->WriteVec3( entry.waterPoints[ i ].xyz );
|
|
fp->WriteFloat( entry.waterPoints[ i ].weight );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idTraceModelCache::TrmMaterialForName
|
|
============
|
|
*/
|
|
const idMaterial* idTraceModelCache::TrmMaterialForName( const char* name ) {
|
|
if ( *name == '\0' ) {
|
|
return NULL;
|
|
}
|
|
return gameLocal.declMaterialType[ name ];
|
|
}
|
|
|
|
/*
|
|
============
|
|
idTraceModelCache::TrmNameForMaterial
|
|
============
|
|
*/
|
|
const char* idTraceModelCache::TrmNameForMaterial( const idMaterial* material ) {
|
|
if ( material == NULL ) {
|
|
return "";
|
|
}
|
|
return material->GetName();
|
|
}
|
|
|
|
/*
|
|
============
|
|
idTraceModelCache::Read
|
|
============
|
|
*/
|
|
void idTraceModelCache::Read( idTraceModel& trm, idFile* fp ) {
|
|
idStr header;
|
|
fp->ReadString( header );
|
|
|
|
if ( header != TRM_HEADER ) {
|
|
gameLocal.Warning( "idTraceModelCache::Read File is not a Trace Model '%s'", fp->GetName() );
|
|
trm.SetupBox( 8.f );
|
|
AllocFileEntry( fp->GetName(), AllocTraceModel( trm, false ) );
|
|
return;
|
|
}
|
|
|
|
idStr version;
|
|
fp->ReadString( version );
|
|
if ( version != TRM_VERSION ) {
|
|
gameLocal.Warning( "idTraceModelCache::Read File has Wrong Version '%s'", fp->GetName() );
|
|
trm.SetupBox( 8.f );
|
|
AllocFileEntry( fp->GetName(), AllocTraceModel( trm, false ) );
|
|
return;
|
|
}
|
|
|
|
trm.Read( fp, &TrmMaterialForName );
|
|
|
|
trmCache_t entry;
|
|
entry.trm = trm;
|
|
entry.trm.ClearUnused();
|
|
fp->ReadFloat( entry.volume );
|
|
fp->ReadVec3( entry.centerOfMass );
|
|
fp->ReadMat3( entry.inertiaTensor );
|
|
fp->ReadBool( entry.includesBrushes );
|
|
fp->ReadBool( entry.hasWater );
|
|
if ( entry.hasWater ) {
|
|
for ( int i = 0; i < MAX_TRACEMODEL_WATER_POINTS; i++ ) {
|
|
fp->ReadVec3( entry.waterPoints[ i ].xyz );
|
|
fp->ReadFloat( entry.waterPoints[ i ].weight );
|
|
}
|
|
}
|
|
|
|
int index = FindTraceModel( trm, entry.includesBrushes );
|
|
if ( index != -1 ) {
|
|
AllocFileEntry( fp->GetName(), index );
|
|
return;
|
|
}
|
|
|
|
if ( FindTraceModel( fp->GetName(), entry.includesBrushes ) != -1 ) {
|
|
gameLocal.Error( "idTraceModelCache::Tried to Cache an Already Cached File'%s'", fp->GetName() );
|
|
}
|
|
|
|
int traceModelIndex = cache.Num();
|
|
|
|
trmCache_t* cachedEntry = allocator.Alloc();
|
|
cache.Alloc() = cachedEntry;
|
|
*cachedEntry = entry;
|
|
cachedEntry->refCount = 0;
|
|
cachedEntry->collisionModel = collisionModelManager->ModelFromTrm( gameLocal.GetMapName(), va( "traceModel%d", traceModelIndex ), trm, entry.includesBrushes );
|
|
|
|
int hashKey = GetTraceModelHashKey( trm );
|
|
hash.Add( hashKey, traceModelIndex );
|
|
|
|
AllocFileEntry( fp->GetName(), traceModelIndex );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idTraceModelCache::FindTraceModel
|
|
============
|
|
*/
|
|
int idTraceModelCache::FindTraceModel( const char* fileName, bool includeBrushes ) {
|
|
int index = FindFileEntry( fileName, includeBrushes );
|
|
if ( index == -1 ) {
|
|
return -1;
|
|
}
|
|
|
|
return fileCache[ index ].entryIndex;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idTraceModelCache::AllocFileEntry
|
|
============
|
|
*/
|
|
void idTraceModelCache::AllocFileEntry( const char* fileName, int traceModelIndex ) {
|
|
int hashKey = idStr::Hash( fileName );
|
|
|
|
int cacheIndex = fileCache.Num();
|
|
trmFileCache_t& cache = fileCache.Alloc();
|
|
cache.fileName = fileName;
|
|
cache.entryIndex = traceModelIndex;
|
|
|
|
nameHash.Add( hashKey, cacheIndex );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idTraceModelCache::FindFileEntry
|
|
============
|
|
*/
|
|
int idTraceModelCache::FindFileEntry( const char* fileName, bool includeBrushes ) {
|
|
int hashKey = idStr::Hash( fileName );
|
|
|
|
for ( int i = nameHash.GetFirst( hashKey ); i != idHashIndex::NULL_INDEX; i = nameHash.GetNext( i ) ) {
|
|
if ( fileCache[ i ].fileName.Cmp( fileName ) != 0 ) {
|
|
continue;
|
|
}
|
|
|
|
if ( cache[ fileCache[ i ].entryIndex ]->includesBrushes != includeBrushes ) {
|
|
continue;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|