mirror of
https://github.com/jmarshall23/Quake_4_Alpha.git
synced 2026-08-12 04:01:12 +02:00
Fixed broken UI transitions.
Fixed collision manager code that wasn't properly reverse engineered. Fixed session code. Fixed numerous crashes.
This commit is contained in:
@@ -16,6 +16,7 @@ set(CMAKE_CXX_EXTENSIONS ON)
|
||||
|
||||
option(Q4_BUILD_IDLIB "Build the Quake 4 SDK idlib seed" ON)
|
||||
option(Q4_BUILD_ENGINE_SEED "Compile the PDB-proven Doom engine seed" ON)
|
||||
option(Q4_BUILD_GAME_DLL "Build the Quake 4 single-player game DLL" ON)
|
||||
option(Q4_BUILD_RECON_TOOLS "Build reconstruction inventory tools" ON)
|
||||
|
||||
if(MSVC)
|
||||
@@ -34,6 +35,10 @@ if(Q4_BUILD_ENGINE_SEED)
|
||||
add_subdirectory(engine)
|
||||
endif()
|
||||
|
||||
if(Q4_BUILD_GAME_DLL)
|
||||
add_subdirectory(game)
|
||||
endif()
|
||||
|
||||
if(Q4_BUILD_RECON_TOOLS)
|
||||
add_subdirectory(tools/reconstruction)
|
||||
endif()
|
||||
|
||||
@@ -510,6 +510,8 @@ private:
|
||||
void RemapEdges( cm_node_t *node, int *edgeRemap );
|
||||
void RemapPolygonReferences_r( cm_node_t *node, cm_polygon_t *polygon, cm_polygon_t *newPolygon );
|
||||
void RemapBrushReferences_r( cm_node_t *node, cm_brush_t *brush, cm_brush_t *newBrush );
|
||||
void R_FilterPolygonIntoTree( idCollisionModelLocal *model, cm_node_t *node, cm_polygonRef_t *pref, cm_polygon_t *polygon );
|
||||
void R_FilterBrushIntoTree( idCollisionModelLocal *model, cm_node_t *node, cm_brushRef_t *pref, cm_brush_t *brush );
|
||||
void RemovePolygonReferences_r( cm_node_t *node, cm_polygon_t *polygon );
|
||||
void RemoveBrushReferences_r( cm_node_t *node, cm_brush_t *brush );
|
||||
void FreeTree_r( idCollisionModelLocal *model, cm_node_t *headNode, cm_node_t *node );
|
||||
@@ -540,6 +542,13 @@ private:
|
||||
bool TestTrmInPolygon( cm_traceWork_t *traceWork, cm_polygon_t *polygon );
|
||||
bool RotateTrmThroughPolygon( cm_traceWork_t *traceWork, cm_polygon_t *polygon );
|
||||
bool TranslateTrmThroughPolygon( cm_traceWork_t *traceWork, cm_polygon_t *polygon );
|
||||
int TranslateEdgeThroughEdge( idVec3 &cross, idPluecker &l1, idPluecker &l2, float *fraction );
|
||||
void TranslateTrmEdgeThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmEdge_t *trmEdge );
|
||||
void TranslateTrmVertexThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmVertex_t *v, int bitNum );
|
||||
void TranslatePointThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmVertex_t *v );
|
||||
void TranslateVertexThroughTrmPolygon( cm_traceWork_t *tw, cm_trmPolygon_t *trmpoly,
|
||||
cm_polygon_t *poly, cm_vertex_t *v, idVec3 &endp, idPluecker &pl );
|
||||
void SetupTranslationHeartPlanes( cm_traceWork_t *tw );
|
||||
void CM_GetCollisionPointTexCoords( idVec2 &texCoord, cm_traceWork_t *traceWork, cm_polygon_t *polygon );
|
||||
void CM_GetMaterialType( cm_traceWork_t *traceWork, cm_polygon_t *polygon );
|
||||
bool IsRenderModelName( const char *name );
|
||||
|
||||
@@ -37,14 +37,17 @@ along with Quake 4 Reconstructed Source Code. If not, see <http://www.gnu.org/l
|
||||
|
||||
#if !defined( Q4_CM_LEGACY_SEED )
|
||||
|
||||
#define CM_FILE_EXT "cm"
|
||||
#define CM_FILEID "CM"
|
||||
#define CM_FILEVERSION "3"
|
||||
|
||||
void CM_GetNodeBounds( idBounds *bounds, cm_node_t *node );
|
||||
int CM_GetNodeContents( cm_node_t *node );
|
||||
int CM_GetModelMemory( idCollisionModelLocal *model );
|
||||
|
||||
void idCollisionModelManagerLocal::WriteNodes( idFile *file, cm_node_t *node ) {}
|
||||
cm_node_t *idCollisionModelManagerLocal::ParseNodes( Lexer *lexer, idCollisionModelLocal *model, cm_node_t *parent ) { return NULL; }
|
||||
void idCollisionModelManagerLocal::WritePolygons( idFile *file, cm_node_t *node ) {}
|
||||
void idCollisionModelManagerLocal::WriteBrushes( idFile *file, cm_node_t *node ) {}
|
||||
void idCollisionModelManagerLocal::ParseVertices( Lexer *lexer, idCollisionModelLocal *model ) {}
|
||||
void idCollisionModelManagerLocal::ParseEdges( Lexer *lexer, idCollisionModelLocal *model ) {}
|
||||
void idCollisionModelManagerLocal::ParsePolygons( Lexer *lexer, idCollisionModelLocal *model ) {}
|
||||
void idCollisionModelManagerLocal::ParseBrushes( Lexer *lexer, idCollisionModelLocal *model ) {}
|
||||
void idCollisionModelManagerLocal::WriteCollisionModel( idFile *file, idCollisionModelLocal *model ) {}
|
||||
|
||||
void idCollisionModelManagerLocal::WriteCollisionModelsToFile( const char *filename, unsigned int mapFileCRC ) {
|
||||
@@ -57,12 +60,243 @@ bool idCollisionModelManagerLocal::WriteCollisionModelForMapEntity( const idMapE
|
||||
return false;
|
||||
}
|
||||
|
||||
cm_node_t *idCollisionModelManagerLocal::ParseNodes( Lexer *lexer,
|
||||
idCollisionModelLocal *model, cm_node_t *parent ) {
|
||||
++model->numNodes;
|
||||
cm_node_t *node = AllocNode( model,
|
||||
model->numNodes < NODE_BLOCK_SIZE_SMALL ? NODE_BLOCK_SIZE_SMALL : NODE_BLOCK_SIZE_LARGE );
|
||||
node->brushes = NULL;
|
||||
node->polygons = NULL;
|
||||
node->parent = parent;
|
||||
lexer->ExpectTokenString( "(" );
|
||||
node->planeType = lexer->ParseInt();
|
||||
node->planeDist = lexer->ParseFloat();
|
||||
lexer->ExpectTokenString( ")" );
|
||||
if ( node->planeType != -1 ) {
|
||||
node->children[0] = ParseNodes( lexer, model, node );
|
||||
node->children[1] = ParseNodes( lexer, model, node );
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
void idCollisionModelManagerLocal::ParseVertices( Lexer *lexer, idCollisionModelLocal *model ) {
|
||||
lexer->ExpectTokenString( "{" );
|
||||
model->maxVertices = model->numVertices = lexer->ParseInt();
|
||||
model->vertices = static_cast<cm_vertex_t *>( Mem_Alloc( model->maxVertices * sizeof( cm_vertex_t ), MA_CM ) );
|
||||
memset( model->vertices, 0, model->maxVertices * sizeof( cm_vertex_t ) );
|
||||
for ( int i = 0; i < model->numVertices; ++i ) {
|
||||
lexer->Parse1DMatrix( 3, model->vertices[i].p.ToFloatPtr() );
|
||||
}
|
||||
lexer->ExpectTokenString( "}" );
|
||||
}
|
||||
|
||||
void idCollisionModelManagerLocal::ParseEdges( Lexer *lexer, idCollisionModelLocal *model ) {
|
||||
lexer->ExpectTokenString( "{" );
|
||||
model->maxEdges = model->numEdges = lexer->ParseInt();
|
||||
model->edges = static_cast<cm_edge_t *>( Mem_Alloc( model->maxEdges * sizeof( cm_edge_t ), MA_CM ) );
|
||||
memset( model->edges, 0, model->maxEdges * sizeof( cm_edge_t ) );
|
||||
for ( int i = 0; i < model->numEdges; ++i ) {
|
||||
lexer->ExpectTokenString( "(" );
|
||||
model->edges[i].vertexNum[0] = lexer->ParseInt();
|
||||
model->edges[i].vertexNum[1] = lexer->ParseInt();
|
||||
lexer->ExpectTokenString( ")" );
|
||||
model->edges[i].internal = lexer->ParseInt();
|
||||
model->edges[i].numUsers = lexer->ParseInt();
|
||||
model->edges[i].normal.Zero();
|
||||
model->numInternalEdges += model->edges[i].internal;
|
||||
}
|
||||
lexer->ExpectTokenString( "}" );
|
||||
}
|
||||
|
||||
void idCollisionModelManagerLocal::ParsePolygons( Lexer *lexer, idCollisionModelLocal *model ) {
|
||||
idToken token;
|
||||
idVec3 normal;
|
||||
|
||||
model->maxPolygons = lexer->ParseInt();
|
||||
model->numPolygons = 0;
|
||||
model->polygons = static_cast<cm_polygon_t *>( Mem_Alloc16(
|
||||
model->maxPolygons * sizeof( cm_polygon_t ), MA_CM ) );
|
||||
memset( model->polygons, 0, model->maxPolygons * sizeof( cm_polygon_t ) );
|
||||
model->maxPolygonEdges = lexer->ParseInt();
|
||||
model->numPolygonEdges = 0;
|
||||
model->polygonEdges = static_cast<int *>( Mem_Alloc16(
|
||||
model->maxPolygonEdges * sizeof( int ), MA_CM ) );
|
||||
|
||||
lexer->ExpectTokenString( "{" );
|
||||
while ( !lexer->CheckTokenString( "}" ) ) {
|
||||
const int numEdges = lexer->ParseInt();
|
||||
cm_polygon_t *polygon = AllocPolygon( model, numEdges );
|
||||
memset( polygon, 0, sizeof( *polygon ) );
|
||||
polygon->numEdges = numEdges;
|
||||
polygon->edges = model->polygonEdges + model->numPolygonEdges - numEdges;
|
||||
|
||||
lexer->ExpectTokenString( "(" );
|
||||
for ( int i = 0; i < numEdges; ++i ) {
|
||||
polygon->edges[i] = lexer->ParseInt();
|
||||
}
|
||||
lexer->ExpectTokenString( ")" );
|
||||
lexer->Parse1DMatrix( 3, normal.ToFloatPtr() );
|
||||
polygon->plane.SetNormal( normal );
|
||||
polygon->plane.SetDist( lexer->ParseFloat() );
|
||||
lexer->Parse1DMatrix( 3, polygon->bounds[0].ToFloatPtr() );
|
||||
lexer->Parse1DMatrix( 3, polygon->bounds[1].ToFloatPtr() );
|
||||
lexer->ExpectTokenType( TT_STRING, 0, &token );
|
||||
polygon->material = declManager->FindMaterial( token.c_str() );
|
||||
polygon->contents = polygon->material->GetContentFlags();
|
||||
polygon->texBounds[0].Zero();
|
||||
polygon->texBounds[1].Zero();
|
||||
polygon->texBounds[2].Zero();
|
||||
polygon->primitiveNum = 0;
|
||||
|
||||
if ( lexer->ReadToken( &token ) ) {
|
||||
lexer->UnreadToken( &token );
|
||||
if ( token == "(" ) {
|
||||
lexer->Parse1DMatrix( 2, polygon->texBounds[0].ToFloatPtr() );
|
||||
lexer->Parse1DMatrix( 2, polygon->texBounds[1].ToFloatPtr() );
|
||||
lexer->Parse1DMatrix( 2, polygon->texBounds[2].ToFloatPtr() );
|
||||
polygon->primitiveNum = lexer->ParseInt();
|
||||
}
|
||||
}
|
||||
polygon->checkcount = 0;
|
||||
R_FilterPolygonIntoTree( model, model->node, NULL, polygon );
|
||||
}
|
||||
}
|
||||
|
||||
void idCollisionModelManagerLocal::ParseBrushes( Lexer *lexer, idCollisionModelLocal *model ) {
|
||||
idToken token;
|
||||
idVec3 normal;
|
||||
|
||||
model->maxBrushes = lexer->ParseInt();
|
||||
model->numBrushes = 0;
|
||||
model->brushes = static_cast<cm_brush_t *>( Mem_Alloc16(
|
||||
model->maxBrushes * sizeof( cm_brush_t ), MA_CM ) );
|
||||
memset( model->brushes, 0, model->maxBrushes * sizeof( cm_brush_t ) );
|
||||
model->maxBrushPlanes = lexer->ParseInt();
|
||||
model->numBrushPlanes = 0;
|
||||
model->brushPlanes = static_cast<idPlane *>( Mem_Alloc16(
|
||||
model->maxBrushPlanes * sizeof( idPlane ), MA_CM ) );
|
||||
|
||||
lexer->ExpectTokenString( "{" );
|
||||
while ( !lexer->CheckTokenString( "}" ) ) {
|
||||
const int numPlanes = lexer->ParseInt();
|
||||
cm_brush_t *brush = AllocBrush( model, numPlanes );
|
||||
memset( brush, 0, sizeof( *brush ) );
|
||||
brush->numPlanes = numPlanes;
|
||||
brush->planes = model->brushPlanes + model->numBrushPlanes - numPlanes;
|
||||
|
||||
lexer->ExpectTokenString( "{" );
|
||||
for ( int i = 0; i < numPlanes; ++i ) {
|
||||
lexer->Parse1DMatrix( 3, normal.ToFloatPtr() );
|
||||
brush->planes[i].SetNormal( normal );
|
||||
brush->planes[i].SetDist( lexer->ParseFloat() );
|
||||
}
|
||||
lexer->ExpectTokenString( "}" );
|
||||
lexer->Parse1DMatrix( 3, brush->bounds[0].ToFloatPtr() );
|
||||
lexer->Parse1DMatrix( 3, brush->bounds[1].ToFloatPtr() );
|
||||
lexer->ReadToken( &token );
|
||||
if ( token.type == TT_NUMBER ) {
|
||||
brush->contents = token.GetIntValue();
|
||||
brush->primitiveNum = 0;
|
||||
} else {
|
||||
brush->contents = ContentsFromString( token.c_str() );
|
||||
brush->primitiveNum = lexer->ParseInt();
|
||||
}
|
||||
brush->material = NULL;
|
||||
brush->checkcount = 0;
|
||||
R_FilterBrushIntoTree( model, model->node, NULL, brush );
|
||||
}
|
||||
}
|
||||
|
||||
bool idCollisionModelManagerLocal::ParseCollisionModel( Lexer *lexer, const char *filename, unsigned int mapFileCRC ) {
|
||||
return false;
|
||||
idToken token;
|
||||
lexer->ExpectTokenType( TT_STRING, 0, &token );
|
||||
|
||||
idStr fullName = token.c_str();
|
||||
if ( filename != NULL && idStr::IcmpnPath( filename, "maps/", 5 ) == 0 ) {
|
||||
idStr mapBase = filename;
|
||||
mapBase.StripFileExtension();
|
||||
fullName = mapBase + "/" + token.c_str();
|
||||
}
|
||||
|
||||
idCollisionModelLocal *model = FindModel( fullName.c_str() );
|
||||
if ( model != NULL ) {
|
||||
FreeModelMemory( model );
|
||||
} else {
|
||||
model = AllocModel();
|
||||
models.Append( model );
|
||||
}
|
||||
model->name = fullName;
|
||||
model->fileTime = mapFileCRC;
|
||||
model->refCount = 0;
|
||||
lexer->ExpectTokenType( TT_NUMBER, 0, &token );
|
||||
model->numPrimitives = token.GetIntValue();
|
||||
|
||||
lexer->ExpectTokenString( "{" );
|
||||
while ( !lexer->CheckTokenString( "}" ) ) {
|
||||
lexer->ReadToken( &token );
|
||||
if ( token == "vertices" ) {
|
||||
ParseVertices( lexer, model );
|
||||
} else if ( token == "edges" ) {
|
||||
ParseEdges( lexer, model );
|
||||
} else if ( token == "nodes" ) {
|
||||
lexer->ExpectTokenString( "{" );
|
||||
model->node = ParseNodes( lexer, model, NULL );
|
||||
lexer->ExpectTokenString( "}" );
|
||||
} else if ( token == "polygons" ) {
|
||||
ParsePolygons( lexer, model );
|
||||
} else if ( token == "brushes" ) {
|
||||
ParseBrushes( lexer, model );
|
||||
} else {
|
||||
lexer->Error( "ParseCollisionModel: bad token \"%s\"", token.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
++checkCount;
|
||||
CalculateEdgeNormals( model, model->node );
|
||||
CM_GetNodeBounds( &model->bounds, model->node );
|
||||
model->contents = CM_GetNodeContents( model->node );
|
||||
model->usedMemory = CM_GetModelMemory( model );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idCollisionModelManagerLocal::LoadCollisionModelFile( const char *filename, unsigned int mapFileCRC ) {
|
||||
idStr fileName = filename;
|
||||
fileName.SetFileExtension( CM_FILE_EXT );
|
||||
idAutoPtr<Lexer> lexer( LexerFactory::MakeLexer( fileName.c_str(),
|
||||
LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE, false ) );
|
||||
if ( !lexer->IsLoaded() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
idToken token;
|
||||
if ( !lexer->ExpectTokenString( CM_FILEID ) ) {
|
||||
common->Warning( "%s is not a CM file", fileName.c_str() );
|
||||
return false;
|
||||
}
|
||||
if ( !lexer->ReadToken( &token ) || token != CM_FILEVERSION ) {
|
||||
common->Warning( "%s has version %s instead of %s", fileName.c_str(), token.c_str(), CM_FILEVERSION );
|
||||
return false;
|
||||
}
|
||||
if ( !lexer->ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) ) {
|
||||
common->Warning( "%s has no map file CRC", fileName.c_str() );
|
||||
return false;
|
||||
}
|
||||
const unsigned int crc = token.GetUnsignedLongValue();
|
||||
if ( mapFileCRC != 0 && crc != mapFileCRC ) {
|
||||
common->Printf( "%s is out of date\n", fileName.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
while ( lexer->ReadToken( &token ) ) {
|
||||
if ( token != "collisionModel" ) {
|
||||
lexer->Error( "idCollisionModelManagerLocal::LoadCollisionModelFile: bad token \"%s\"", token.c_str() );
|
||||
return false;
|
||||
}
|
||||
if ( !ParseCollisionModel( lexer, filename, mapFileCRC ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -303,6 +303,89 @@ void idCollisionModelManagerLocal::RemapBrushReferences_r( cm_node_t *node, cm_b
|
||||
}
|
||||
}
|
||||
|
||||
static bool CM_R_InsideAllChildren( cm_node_t *node, const idBounds &bounds ) {
|
||||
if ( node->planeType == -1 ) {
|
||||
return true;
|
||||
}
|
||||
const int axis = node->planeType;
|
||||
return bounds[0][axis] < node->planeDist &&
|
||||
bounds[1][axis] > node->planeDist &&
|
||||
CM_R_InsideAllChildren( node->children[0], bounds ) &&
|
||||
CM_R_InsideAllChildren( node->children[1], bounds );
|
||||
}
|
||||
|
||||
void idCollisionModelManagerLocal::R_FilterPolygonIntoTree( idCollisionModelLocal *model,
|
||||
cm_node_t *node, cm_polygonRef_t *pref, cm_polygon_t *polygon ) {
|
||||
while ( node->planeType != -1 ) {
|
||||
const int axis = node->planeType;
|
||||
if ( polygon->bounds[0][axis] < node->planeDist &&
|
||||
polygon->bounds[1][axis] > node->planeDist &&
|
||||
CM_R_InsideAllChildren( node->children[0], polygon->bounds ) &&
|
||||
CM_R_InsideAllChildren( node->children[1], polygon->bounds ) ) {
|
||||
break;
|
||||
}
|
||||
if ( polygon->bounds[0][axis] >= node->planeDist ) {
|
||||
node = node->children[0];
|
||||
continue;
|
||||
}
|
||||
if ( polygon->bounds[1][axis] <= node->planeDist ) {
|
||||
node = node->children[1];
|
||||
continue;
|
||||
}
|
||||
R_FilterPolygonIntoTree( model, node->children[1], NULL, polygon );
|
||||
node = node->children[0];
|
||||
}
|
||||
|
||||
if ( pref != NULL ) {
|
||||
pref->next = node->polygons;
|
||||
node->polygons = pref;
|
||||
return;
|
||||
}
|
||||
|
||||
cm_polygonRef_t *ref = AllocPolygonReference( model,
|
||||
model->numPolygonRefs < REFERENCE_BLOCK_SIZE_SMALL ? REFERENCE_BLOCK_SIZE_SMALL : REFERENCE_BLOCK_SIZE_LARGE );
|
||||
ref->p = polygon;
|
||||
ref->next = node->polygons;
|
||||
node->polygons = ref;
|
||||
++model->numPolygonRefs;
|
||||
}
|
||||
|
||||
void idCollisionModelManagerLocal::R_FilterBrushIntoTree( idCollisionModelLocal *model,
|
||||
cm_node_t *node, cm_brushRef_t *pref, cm_brush_t *brush ) {
|
||||
while ( node->planeType != -1 ) {
|
||||
const int axis = node->planeType;
|
||||
if ( brush->bounds[0][axis] < node->planeDist &&
|
||||
brush->bounds[1][axis] > node->planeDist &&
|
||||
CM_R_InsideAllChildren( node->children[0], brush->bounds ) &&
|
||||
CM_R_InsideAllChildren( node->children[1], brush->bounds ) ) {
|
||||
break;
|
||||
}
|
||||
if ( brush->bounds[0][axis] >= node->planeDist ) {
|
||||
node = node->children[0];
|
||||
continue;
|
||||
}
|
||||
if ( brush->bounds[1][axis] <= node->planeDist ) {
|
||||
node = node->children[1];
|
||||
continue;
|
||||
}
|
||||
R_FilterBrushIntoTree( model, node->children[1], NULL, brush );
|
||||
node = node->children[0];
|
||||
}
|
||||
|
||||
if ( pref != NULL ) {
|
||||
pref->next = node->brushes;
|
||||
node->brushes = pref;
|
||||
return;
|
||||
}
|
||||
|
||||
cm_brushRef_t *ref = AllocBrushReference( model,
|
||||
model->numBrushRefs < REFERENCE_BLOCK_SIZE_SMALL ? REFERENCE_BLOCK_SIZE_SMALL : REFERENCE_BLOCK_SIZE_LARGE );
|
||||
ref->b = brush;
|
||||
ref->next = node->brushes;
|
||||
node->brushes = ref;
|
||||
++model->numBrushRefs;
|
||||
}
|
||||
|
||||
void CM_R_GetNodeBounds( idBounds *bounds, cm_node_t *node ) {
|
||||
while ( true ) {
|
||||
for ( cm_polygonRef_t *ref = node->polygons; ref != NULL; ref = ref->next ) {
|
||||
|
||||
@@ -40,32 +40,6 @@ CollisionModel_translate.obj source owner identified by quake4.pdb.
|
||||
|
||||
#include "CollisionModel_local.h"
|
||||
|
||||
void idCollisionModelManagerLocal::SetupTrm( cm_traceWork_t *traceWork, const idTraceModel *trm ) {
|
||||
traceWork->numVerts = trm->numVerts;
|
||||
for ( int i = 0; i < trm->numVerts; i++ ) {
|
||||
traceWork->vertices[i].p = trm->verts[i];
|
||||
traceWork->vertices[i].used = false;
|
||||
}
|
||||
|
||||
traceWork->numEdges = trm->numEdges;
|
||||
for ( int i = 1; i <= trm->numEdges; i++ ) {
|
||||
traceWork->edges[i].vertexNum[0] = trm->edges[i].v[0];
|
||||
traceWork->edges[i].vertexNum[1] = trm->edges[i].v[1];
|
||||
traceWork->edges[i].used = false;
|
||||
}
|
||||
|
||||
traceWork->numPolys = trm->numPolys;
|
||||
for ( int i = 0; i < trm->numPolys; i++ ) {
|
||||
traceWork->polys[i].numEdges = trm->polys[i].numEdges;
|
||||
for ( int j = 0; j < trm->polys[i].numEdges; j++ ) {
|
||||
traceWork->polys[i].edges[j] = trm->polys[i].edges[j];
|
||||
}
|
||||
traceWork->polys[i].plane.SetNormal( trm->polys[i].normal );
|
||||
traceWork->polys[i].used = false;
|
||||
}
|
||||
traceWork->isConvex = trm->isConvex;
|
||||
}
|
||||
|
||||
void idCollisionModelManagerLocal::CM_GetCollisionPointTexCoords( idVec2 &texCoord,
|
||||
cm_traceWork_t *traceWork, cm_polygon_t *polygon ) {
|
||||
texCoord.Set( 0.5f, 0.5f );
|
||||
@@ -103,51 +77,11 @@ void idCollisionModelManagerLocal::CM_GetMaterialType( cm_traceWork_t *traceWork
|
||||
}
|
||||
}
|
||||
|
||||
bool idCollisionModelManagerLocal::TranslateTrmThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *polygon ) {
|
||||
if ( polygon == NULL || polygon->checkcount == checkCount ) {
|
||||
return false;
|
||||
}
|
||||
polygon->checkcount = checkCount;
|
||||
if ( !( polygon->contents & tw->contents ) ||
|
||||
!tw->traceBounds.IntersectsBounds( polygon->bounds ) ||
|
||||
polygon->plane.Normal() * tw->dir >= 0.0f ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the trace-model support point closest to the polygon plane.
|
||||
idVec3 support;
|
||||
for ( int axis = 0; axis < 3; ++axis ) {
|
||||
support[axis] = polygon->plane[axis] >= 0.0f ? tw->trmBounds[0][axis] : tw->trmBounds[1][axis];
|
||||
}
|
||||
const float supportDistance = support * polygon->plane.Normal();
|
||||
const float startDistance = polygon->plane.Distance( tw->start ) + supportDistance;
|
||||
const float endDistance = polygon->plane.Distance( tw->end ) + supportDistance;
|
||||
if ( startDistance <= CM_CLIP_EPSILON || endDistance >= startDistance ) {
|
||||
return false;
|
||||
}
|
||||
float fraction = ( startDistance - CM_CLIP_EPSILON ) / ( startDistance - endDistance );
|
||||
fraction = idMath::ClampFloat( 0.0f, tw->trace.fraction, fraction );
|
||||
const idVec3 contactPoint = tw->start + fraction * tw->dir + support;
|
||||
const idBounds expanded = polygon->bounds.Expand( CM_BOX_EPSILON );
|
||||
if ( !expanded.ContainsPoint( contactPoint ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tw->trace.fraction = fraction;
|
||||
tw->trace.c.normal = polygon->plane.Normal();
|
||||
tw->trace.c.dist = polygon->plane.Dist();
|
||||
tw->trace.c.contents = polygon->contents;
|
||||
tw->trace.c.material = polygon->material;
|
||||
tw->trace.c.type = tw->pointTrace ? CONTACT_TRMVERTEX : CONTACT_EDGE;
|
||||
tw->trace.c.point = contactPoint;
|
||||
tw->trace.c.modelFeature = polygon->primitiveNum;
|
||||
tw->trace.c.trmFeature = 0;
|
||||
CM_GetMaterialType( tw, polygon );
|
||||
if ( tw->getContacts && tw->contacts != NULL && tw->numContacts < tw->maxContacts ) {
|
||||
tw->contacts[tw->numContacts++] = tw->trace.c;
|
||||
}
|
||||
return fraction == 0.0f;
|
||||
}
|
||||
#define Q4_CM_OBJECT_TRANSLATION
|
||||
#define Q4_CM_TRANSLATE_HELPERS_ONLY
|
||||
#include "collisionmodel_translate_legacy.inc"
|
||||
#undef Q4_CM_TRANSLATE_HELPERS_ONLY
|
||||
#undef Q4_CM_OBJECT_TRANSLATION
|
||||
|
||||
void idCollisionModelManagerLocal::Translation( trace_t *results, const idVec3 &start,
|
||||
const idVec3 &end, const idTraceModel *trm, const idMat3 &trmAxis,
|
||||
@@ -157,6 +91,7 @@ void idCollisionModelManagerLocal::Translation( trace_t *results, const idVec3 &
|
||||
results->fraction = 1.0f;
|
||||
results->endpos = end;
|
||||
results->endAxis = trmAxis;
|
||||
|
||||
if ( collisionModel == NULL ) {
|
||||
return;
|
||||
}
|
||||
@@ -165,10 +100,17 @@ void idCollisionModelManagerLocal::Translation( trace_t *results, const idVec3 &
|
||||
collisionModel, modelOrigin, modelAxis );
|
||||
return;
|
||||
}
|
||||
const bool pointTrace = trm == NULL || trm->bounds.GetVolume() <= 0.0f;
|
||||
|
||||
const bool pointTrace = trm == NULL ||
|
||||
( trm->bounds[1].x - trm->bounds[0].x <= 0.0f &&
|
||||
trm->bounds[1].y - trm->bounds[0].y <= 0.0f &&
|
||||
trm->bounds[1].z - trm->bounds[0].z <= 0.0f );
|
||||
if ( !pointTrace && ( end - start ).LengthSqr() > Square( CM_MAX_TRACE_DIST ) ) {
|
||||
results->fraction = 0.0f;
|
||||
results->endpos = start;
|
||||
results->endAxis = trmAxis;
|
||||
results->c.normal.Zero();
|
||||
results->c.material = NULL;
|
||||
results->c.point = start;
|
||||
common->Printf( "idCollisionModelManagerLocal::Translation: huge translation\n" );
|
||||
return;
|
||||
@@ -178,18 +120,20 @@ void idCollisionModelManagerLocal::Translation( trace_t *results, const idVec3 &
|
||||
ALIGN16( cm_traceWork_t tw );
|
||||
memset( &tw, 0, sizeof( tw ) );
|
||||
tw.trace.fraction = 1.0f;
|
||||
tw.trace.c.contents = 0;
|
||||
tw.trace.c.type = CONTACT_NONE;
|
||||
tw.contents = contentMask;
|
||||
tw.model = static_cast<idCollisionModelLocal *>( collisionModel );
|
||||
tw.rotation = false;
|
||||
tw.positionTest = false;
|
||||
tw.pointTrace = pointTrace;
|
||||
tw.isConvex = trm == NULL || trm->isConvex;
|
||||
tw.quickExit = false;
|
||||
tw.getContacts = getContacts;
|
||||
tw.contacts = contacts;
|
||||
tw.maxContacts = maxContacts;
|
||||
tw.numContacts = 0;
|
||||
tw.start = start - modelOrigin;
|
||||
tw.end = end - modelOrigin;
|
||||
tw.dir = tw.end - tw.start;
|
||||
|
||||
idMat3 inverseModelAxis = mat3_identity;
|
||||
const bool modelRotated = modelAxis.IsRotated();
|
||||
@@ -197,33 +141,143 @@ void idCollisionModelManagerLocal::Translation( trace_t *results, const idVec3 &
|
||||
inverseModelAxis = modelAxis.Transpose();
|
||||
tw.start *= inverseModelAxis;
|
||||
tw.end *= inverseModelAxis;
|
||||
tw.dir *= inverseModelAxis;
|
||||
}
|
||||
tw.dir = tw.end - tw.start;
|
||||
|
||||
if ( pointTrace ) {
|
||||
tw.pointTrace = true;
|
||||
tw.isConvex = true;
|
||||
tw.trmBounds.Zero();
|
||||
} else {
|
||||
idMat3 localTraceAxis = trmAxis;
|
||||
if ( modelRotated ) {
|
||||
localTraceAxis *= inverseModelAxis;
|
||||
}
|
||||
tw.trmBounds.FromTransformedBounds( trm->bounds, vec3_origin, localTraceAxis );
|
||||
}
|
||||
for ( int i = 0; i < 3; ++i ) {
|
||||
if ( tw.start[i] < tw.end[i] ) {
|
||||
tw.traceBounds[0][i] = tw.start[i] + tw.trmBounds[0][i] - CM_BOX_EPSILON;
|
||||
tw.traceBounds[1][i] = tw.end[i] + tw.trmBounds[1][i] + CM_BOX_EPSILON;
|
||||
} else {
|
||||
tw.traceBounds[0][i] = tw.end[i] + tw.trmBounds[0][i] - CM_BOX_EPSILON;
|
||||
tw.traceBounds[1][i] = tw.start[i] + tw.trmBounds[1][i] + CM_BOX_EPSILON;
|
||||
tw.traceBounds[0][i] = Min( tw.start[i], tw.end[i] ) - CM_BOX_EPSILON;
|
||||
tw.traceBounds[1][i] = Max( tw.start[i], tw.end[i] ) + CM_BOX_EPSILON;
|
||||
tw.trmExtents[i] = CM_BOX_EPSILON;
|
||||
}
|
||||
SetupTranslationHeartPlanes( &tw );
|
||||
tw.maxDistFromHeartPlane1 = CM_BOX_EPSILON;
|
||||
tw.maxDistFromHeartPlane2 = CM_BOX_EPSILON;
|
||||
tw.numVerts = 1;
|
||||
tw.vertices[0].p = tw.start;
|
||||
tw.vertices[0].endp = tw.end;
|
||||
tw.vertices[0].pl.FromRay( tw.start, tw.dir );
|
||||
tw.vertices[0].used = true;
|
||||
tw.numEdges = 0;
|
||||
tw.numPolys = 0;
|
||||
TraceThroughModel( &tw );
|
||||
} else {
|
||||
tw.pointTrace = false;
|
||||
SetupTrm( &tw, trm );
|
||||
|
||||
idMat3 trmTransform = trmAxis;
|
||||
if ( modelRotated ) {
|
||||
trmTransform *= inverseModelAxis;
|
||||
}
|
||||
|
||||
// The trace origin follows the transformed trace-model center, while
|
||||
// vertices remain positioned relative to the caller's physics origin.
|
||||
const idVec3 traceOrigin = tw.start;
|
||||
const idVec3 traceEnd = tw.end;
|
||||
const idVec3 transformedOffset = trm->offset * trmTransform;
|
||||
tw.start = traceOrigin + transformedOffset;
|
||||
tw.end = traceEnd + transformedOffset;
|
||||
|
||||
for ( int i = 0; i < tw.numPolys; ++i ) {
|
||||
tw.polys[i].plane.SetNormal( trm->polys[i].normal * trmTransform );
|
||||
const float facing = tw.polys[i].plane.Normal() * tw.dir;
|
||||
if ( facing > 0.0f || ( !trm->isConvex && facing == 0.0f ) ) {
|
||||
tw.polys[i].used = true;
|
||||
for ( int j = 0; j < tw.polys[i].numEdges; ++j ) {
|
||||
cm_trmEdge_t &edge = tw.edges[abs( tw.polys[i].edges[j] )];
|
||||
edge.used = true;
|
||||
tw.vertices[edge.vertexNum[0]].used = true;
|
||||
tw.vertices[edge.vertexNum[1]].used = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tw.trmBounds.Clear();
|
||||
for ( int i = 0; i < tw.numVerts; ++i ) {
|
||||
cm_trmVertex_t &vertex = tw.vertices[i];
|
||||
if ( !vertex.used ) {
|
||||
continue;
|
||||
}
|
||||
vertex.p = trm->verts[i] * trmTransform + traceOrigin;
|
||||
vertex.endp = vertex.p + tw.dir;
|
||||
vertex.pl.FromRay( vertex.p, tw.dir );
|
||||
tw.trmBounds.AddPoint( vertex.p - tw.start );
|
||||
}
|
||||
|
||||
for ( int i = 1; i <= tw.numEdges; ++i ) {
|
||||
cm_trmEdge_t &edge = tw.edges[i];
|
||||
if ( !edge.used ) {
|
||||
continue;
|
||||
}
|
||||
const idVec3 &edgeStart = tw.vertices[edge.vertexNum[0]].p;
|
||||
const idVec3 &edgeEnd = tw.vertices[edge.vertexNum[1]].p;
|
||||
edge.pl.FromLine( edgeStart, edgeEnd );
|
||||
const idVec3 edgeDir = edgeStart - edgeEnd;
|
||||
edge.cross[0] = edgeDir[0] * tw.dir[1] - edgeDir[1] * tw.dir[0];
|
||||
edge.cross[1] = edgeDir[0] * tw.dir[2] - edgeDir[2] * tw.dir[0];
|
||||
edge.cross[2] = edgeDir[1] * tw.dir[2] - edgeDir[2] * tw.dir[1];
|
||||
edge.bitNum = static_cast<short>( i );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < tw.numPolys; ++i ) {
|
||||
cm_trmPolygon_t &polygon = tw.polys[i];
|
||||
if ( polygon.used ) {
|
||||
const cm_trmEdge_t &edge = tw.edges[abs( polygon.edges[0] )];
|
||||
polygon.plane.FitThroughPoint( tw.vertices[edge.vertexNum[0]].p );
|
||||
}
|
||||
}
|
||||
|
||||
for ( int i = 0; i < 3; ++i ) {
|
||||
tw.traceBounds[0][i] = Min( tw.start[i], tw.end[i] ) + tw.trmBounds[0][i] - CM_BOX_EPSILON;
|
||||
tw.traceBounds[1][i] = Max( tw.start[i], tw.end[i] ) + tw.trmBounds[1][i] + CM_BOX_EPSILON;
|
||||
tw.trmExtents[i] = Max( idMath::Fabs( tw.trmBounds[0][i] ),
|
||||
idMath::Fabs( tw.trmBounds[1][i] ) ) + CM_BOX_EPSILON;
|
||||
}
|
||||
|
||||
SetupTranslationHeartPlanes( &tw );
|
||||
tw.maxDistFromHeartPlane1 = 0.0f;
|
||||
tw.maxDistFromHeartPlane2 = 0.0f;
|
||||
for ( int i = 0; i < tw.numVerts; ++i ) {
|
||||
if ( !tw.vertices[i].used ) {
|
||||
continue;
|
||||
}
|
||||
tw.maxDistFromHeartPlane1 = Max( tw.maxDistFromHeartPlane1,
|
||||
idMath::Fabs( tw.heartPlane1.Distance( tw.vertices[i].p ) ) );
|
||||
tw.maxDistFromHeartPlane2 = Max( tw.maxDistFromHeartPlane2,
|
||||
idMath::Fabs( tw.heartPlane2.Distance( tw.vertices[i].p ) ) );
|
||||
}
|
||||
tw.maxDistFromHeartPlane1 += CM_BOX_EPSILON;
|
||||
tw.maxDistFromHeartPlane2 += CM_BOX_EPSILON;
|
||||
TraceThroughModel( &tw );
|
||||
}
|
||||
|
||||
if ( tw.getContacts ) {
|
||||
if ( modelRotated ) {
|
||||
for ( int i = 0; i < tw.numContacts; ++i ) {
|
||||
tw.contacts[i].normal *= modelAxis;
|
||||
tw.contacts[i].point *= modelAxis;
|
||||
}
|
||||
}
|
||||
if ( modelOrigin != vec3_origin ) {
|
||||
for ( int i = 0; i < tw.numContacts; ++i ) {
|
||||
tw.contacts[i].point += modelOrigin;
|
||||
tw.contacts[i].dist += modelOrigin * tw.contacts[i].normal;
|
||||
}
|
||||
}
|
||||
numContacts = tw.numContacts;
|
||||
return;
|
||||
}
|
||||
|
||||
*results = tw.trace;
|
||||
results->endpos = start + results->fraction * ( end - start );
|
||||
results->endAxis = trmAxis;
|
||||
results->endAxis = pointTrace ? mat3_identity : trmAxis;
|
||||
if ( results->fraction < 1.0f ) {
|
||||
if ( results->fraction > 0.0f && results->endpos.Compare( start ) ) {
|
||||
results->fraction = 0.0f;
|
||||
}
|
||||
if ( modelRotated ) {
|
||||
results->c.normal *= modelAxis;
|
||||
results->c.point *= modelAxis;
|
||||
@@ -231,7 +285,6 @@ void idCollisionModelManagerLocal::Translation( trace_t *results, const idVec3 &
|
||||
results->c.point += modelOrigin;
|
||||
results->c.dist += modelOrigin * results->c.normal;
|
||||
}
|
||||
numContacts = tw.numContacts;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,10 +30,22 @@ along with Quake 4 Reconstructed Source Code. If not, see <http://www.gnu.org/l
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
#if !defined( Q4_CM_OBJECT_TRANSLATION )
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "CollisionModel_local.h"
|
||||
#endif
|
||||
|
||||
#if defined( Q4_CM_OBJECT_TRANSLATION )
|
||||
#define CM_MANAGER_CHECKCOUNT checkCount
|
||||
#define CM_TRACE_BOUNDS( tw ) ( (tw)->traceBounds )
|
||||
#define CM_TRM_BOUNDS( tw ) ( (tw)->trmBounds )
|
||||
#else
|
||||
#define CM_MANAGER_CHECKCOUNT idCollisionModelManagerLocal::checkCount
|
||||
#define CM_TRACE_BOUNDS( tw ) ( (tw)->bounds )
|
||||
#define CM_TRM_BOUNDS( tw ) ( (tw)->size )
|
||||
#endif
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
@@ -239,7 +251,7 @@ void idCollisionModelManagerLocal::TranslateTrmEdgeThroughPolygon( cm_traceWork_
|
||||
edgeNum = poly->edges[i];
|
||||
edge = tw->model->edges + abs(edgeNum);
|
||||
// if this edge is already checked
|
||||
if ( edge->checkcount == idCollisionModelManagerLocal::checkCount ) {
|
||||
if ( edge->checkcount == CM_MANAGER_CHECKCOUNT ) {
|
||||
continue;
|
||||
}
|
||||
// can never collide with internal edges
|
||||
@@ -293,12 +305,14 @@ void idCollisionModelManagerLocal::TranslateTrmEdgeThroughPolygon( cm_traceWork_
|
||||
// create plane with normal vector orthogonal to both the polygon edge and the trm edge
|
||||
start = tw->model->vertices[edge->vertexNum[0]].p;
|
||||
end = tw->model->vertices[edge->vertexNum[1]].p;
|
||||
tw->trace.c.normal = ( end - start ).Cross( trmEdge->end - trmEdge->start );
|
||||
const idVec3 &trmStart = tw->vertices[trmEdge->vertexNum[0]].p;
|
||||
const idVec3 &trmEnd = tw->vertices[trmEdge->vertexNum[1]].p;
|
||||
tw->trace.c.normal = ( end - start ).Cross( trmEnd - trmStart );
|
||||
// FIXME: do this normalize when we know the first collision
|
||||
tw->trace.c.normal.Normalize();
|
||||
tw->trace.c.dist = tw->trace.c.normal * start;
|
||||
// make sure the collision plane faces the trace model
|
||||
if ( tw->trace.c.normal * trmEdge->start - tw->trace.c.dist < 0.0f ) {
|
||||
if ( tw->trace.c.normal * trmStart - tw->trace.c.dist < 0.0f ) {
|
||||
tw->trace.c.normal = -tw->trace.c.normal;
|
||||
tw->trace.c.dist = -tw->trace.c.dist;
|
||||
}
|
||||
@@ -311,7 +325,7 @@ void idCollisionModelManagerLocal::TranslateTrmEdgeThroughPolygon( cm_traceWork_
|
||||
normal[0] = trmEdge->cross[2];
|
||||
normal[1] = -trmEdge->cross[1];
|
||||
normal[2] = trmEdge->cross[0];
|
||||
dist = normal * trmEdge->start;
|
||||
dist = normal * trmStart;
|
||||
d1 = normal * start - dist;
|
||||
d2 = normal * end - dist;
|
||||
f1 = d1 / ( d1 - d2 );
|
||||
@@ -445,9 +459,9 @@ void idCollisionModelManagerLocal::TranslatePointThroughPolygon( cm_traceWork_t
|
||||
edgeNum = poly->edges[i];
|
||||
edge = tw->model->edges + abs(edgeNum);
|
||||
// if we didn't yet calculate the sidedness for this edge
|
||||
if ( edge->checkcount != idCollisionModelManagerLocal::checkCount ) {
|
||||
if ( edge->checkcount != CM_MANAGER_CHECKCOUNT ) {
|
||||
float fl;
|
||||
edge->checkcount = idCollisionModelManagerLocal::checkCount;
|
||||
edge->checkcount = CM_MANAGER_CHECKCOUNT;
|
||||
pl.FromLine(tw->model->vertices[edge->vertexNum[0]].p, tw->model->vertices[edge->vertexNum[1]].p);
|
||||
fl = v->pl.PermutedInnerProduct( pl );
|
||||
edge->side = FLOATSIGNBITSET(fl);
|
||||
@@ -541,10 +555,10 @@ bool idCollisionModelManagerLocal::TranslateTrmThroughPolygon( cm_traceWork_t *t
|
||||
cm_edge_t *e;
|
||||
|
||||
// if already checked this polygon
|
||||
if ( p->checkcount == idCollisionModelManagerLocal::checkCount ) {
|
||||
if ( p->checkcount == CM_MANAGER_CHECKCOUNT ) {
|
||||
return false;
|
||||
}
|
||||
p->checkcount = idCollisionModelManagerLocal::checkCount;
|
||||
p->checkcount = CM_MANAGER_CHECKCOUNT;
|
||||
|
||||
// if this polygon does not have the right contents behind it
|
||||
if ( !(p->contents & tw->contents) ) {
|
||||
@@ -552,7 +566,7 @@ bool idCollisionModelManagerLocal::TranslateTrmThroughPolygon( cm_traceWork_t *t
|
||||
}
|
||||
|
||||
// if the the trace bounds do not intersect the polygon bounds
|
||||
if ( !tw->bounds.IntersectsBounds( p->bounds ) ) {
|
||||
if ( !CM_TRACE_BOUNDS( tw ).IntersectsBounds( p->bounds ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -581,7 +595,7 @@ bool idCollisionModelManagerLocal::TranslateTrmThroughPolygon( cm_traceWork_t *t
|
||||
else {
|
||||
|
||||
// trace bounds should cross polygon plane
|
||||
switch ( tw->bounds.PlaneSide( p->plane ) ) {
|
||||
switch ( CM_TRACE_BOUNDS( tw ).PlaneSide( p->plane ) ) {
|
||||
case PLANESIDE_CROSS:
|
||||
break;
|
||||
case PLANESIDE_FRONT:
|
||||
@@ -598,7 +612,7 @@ bool idCollisionModelManagerLocal::TranslateTrmThroughPolygon( cm_traceWork_t *t
|
||||
edgeNum = p->edges[i];
|
||||
e = tw->model->edges + abs(edgeNum);
|
||||
// reset sidedness cache if this is the first time we encounter this edge during this trace
|
||||
if ( e->checkcount != idCollisionModelManagerLocal::checkCount ) {
|
||||
if ( e->checkcount != CM_MANAGER_CHECKCOUNT ) {
|
||||
e->sideSet = 0;
|
||||
}
|
||||
// pluecker coordinate for edge
|
||||
@@ -607,7 +621,7 @@ bool idCollisionModelManagerLocal::TranslateTrmThroughPolygon( cm_traceWork_t *t
|
||||
|
||||
v = &tw->model->vertices[e->vertexNum[INTSIGNBITSET(edgeNum)]];
|
||||
// reset sidedness cache if this is the first time we encounter this vertex during this trace
|
||||
if ( v->checkcount != idCollisionModelManagerLocal::checkCount ) {
|
||||
if ( v->checkcount != CM_MANAGER_CHECKCOUNT ) {
|
||||
v->sideSet = 0;
|
||||
}
|
||||
// pluecker coordinate for vertex movement vector
|
||||
@@ -637,11 +651,11 @@ bool idCollisionModelManagerLocal::TranslateTrmThroughPolygon( cm_traceWork_t *t
|
||||
edgeNum = p->edges[i];
|
||||
e = tw->model->edges + abs(edgeNum);
|
||||
|
||||
if ( e->checkcount == idCollisionModelManagerLocal::checkCount ) {
|
||||
if ( e->checkcount == CM_MANAGER_CHECKCOUNT ) {
|
||||
continue;
|
||||
}
|
||||
// set edge check count
|
||||
e->checkcount = idCollisionModelManagerLocal::checkCount;
|
||||
e->checkcount = CM_MANAGER_CHECKCOUNT;
|
||||
// can never collide with internal edges
|
||||
if ( e->internal ) {
|
||||
continue;
|
||||
@@ -651,14 +665,14 @@ bool idCollisionModelManagerLocal::TranslateTrmThroughPolygon( cm_traceWork_t *t
|
||||
|
||||
v = tw->model->vertices + e->vertexNum[k ^ INTSIGNBITSET(edgeNum)];
|
||||
// if this vertex is already checked
|
||||
if ( v->checkcount == idCollisionModelManagerLocal::checkCount ) {
|
||||
if ( v->checkcount == CM_MANAGER_CHECKCOUNT ) {
|
||||
continue;
|
||||
}
|
||||
// set vertex check count
|
||||
v->checkcount = idCollisionModelManagerLocal::checkCount;
|
||||
v->checkcount = CM_MANAGER_CHECKCOUNT;
|
||||
|
||||
// if the vertex is outside the trace bounds
|
||||
if ( !tw->bounds.ContainsPoint( v->p ) ) {
|
||||
if ( !CM_TRACE_BOUNDS( tw ).ContainsPoint( v->p ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -684,12 +698,12 @@ bool idCollisionModelManagerLocal::TranslateTrmThroughPolygon( cm_traceWork_t *t
|
||||
// decrease bounds
|
||||
for ( i = 0; i < 3; i++ ) {
|
||||
if ( tw->start[i] < endp[i] ) {
|
||||
tw->bounds[0][i] = tw->start[i] + tw->size[0][i] - CM_BOX_EPSILON;
|
||||
tw->bounds[1][i] = endp[i] + tw->size[1][i] + CM_BOX_EPSILON;
|
||||
CM_TRACE_BOUNDS( tw )[0][i] = tw->start[i] + CM_TRM_BOUNDS( tw )[0][i] - CM_BOX_EPSILON;
|
||||
CM_TRACE_BOUNDS( tw )[1][i] = endp[i] + CM_TRM_BOUNDS( tw )[1][i] + CM_BOX_EPSILON;
|
||||
}
|
||||
else {
|
||||
tw->bounds[0][i] = endp[i] + tw->size[0][i] - CM_BOX_EPSILON;
|
||||
tw->bounds[1][i] = tw->start[i] + tw->size[1][i] + CM_BOX_EPSILON;
|
||||
CM_TRACE_BOUNDS( tw )[0][i] = endp[i] + CM_TRM_BOUNDS( tw )[0][i] - CM_BOX_EPSILON;
|
||||
CM_TRACE_BOUNDS( tw )[1][i] = tw->start[i] + CM_TRM_BOUNDS( tw )[1][i] + CM_BOX_EPSILON;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -755,6 +769,8 @@ void idCollisionModelManagerLocal::SetupTranslationHeartPlanes( cm_traceWork_t *
|
||||
idCollisionModelManagerLocal::Translation
|
||||
================
|
||||
*/
|
||||
#if !defined( Q4_CM_TRANSLATE_HELPERS_ONLY )
|
||||
|
||||
#ifdef _DEBUG
|
||||
static int entered = 0;
|
||||
#endif
|
||||
@@ -1114,3 +1130,9 @@ void idCollisionModelManagerLocal::Translation( trace_t *results, const idVec3 &
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // !Q4_CM_TRANSLATE_HELPERS_ONLY
|
||||
|
||||
#undef CM_MANAGER_CHECKCOUNT
|
||||
#undef CM_TRACE_BOUNDS
|
||||
#undef CM_TRM_BOUNDS
|
||||
|
||||
@@ -42,6 +42,12 @@ set(Q4_CM_TRANSLATE_SOURCE ${PROJECT_SOURCE_DIR}/cm/collisionmodel_translate.cpp
|
||||
list(REMOVE_ITEM Q4_CM_SOURCES ${Q4_CM_MODEL_SOURCE})
|
||||
|
||||
file(GLOB Q4_FRAMEWORK_TOP_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/framework/*.cpp)
|
||||
set(Q4_FRAMEWORK_PLAYER_MODEL_SOURCE ${PROJECT_SOURCE_DIR}/framework/DeclPlayerModel.cpp)
|
||||
set(Q4_FRAMEWORK_ENGINE_TOP_SOURCES ${Q4_FRAMEWORK_TOP_SOURCES})
|
||||
# DeclPlayerModel is owned by gamex86.dll in the retail PDB. Keep it out of
|
||||
# q4xp.exe so the reconstructed compiland and its allocator live in the same
|
||||
# module that registers DECL_PLAYER_MODEL.
|
||||
list(REMOVE_ITEM Q4_FRAMEWORK_ENGINE_TOP_SOURCES ${Q4_FRAMEWORK_PLAYER_MODEL_SOURCE})
|
||||
set(Q4_FRAMEWORK_SESSION_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/framework/common.cpp
|
||||
${PROJECT_SOURCE_DIR}/framework/session.cpp
|
||||
@@ -58,8 +64,11 @@ set(Q4_FRAMEWORK_CONSOLE_SOURCES
|
||||
file(GLOB Q4_FRAMEWORK_ASYNC_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/framework/async/*.cpp)
|
||||
set(Q4_FRAMEWORK_MSGCHANNEL_SOURCE ${PROJECT_SOURCE_DIR}/framework/async/msgchannel.cpp)
|
||||
list(REMOVE_ITEM Q4_FRAMEWORK_ASYNC_SOURCES ${Q4_FRAMEWORK_MSGCHANNEL_SOURCE})
|
||||
set(Q4_FRAMEWORK_CORE_SOURCES ${Q4_FRAMEWORK_TOP_SOURCES})
|
||||
list(REMOVE_ITEM Q4_FRAMEWORK_CORE_SOURCES ${Q4_FRAMEWORK_SESSION_SOURCES} ${Q4_FRAMEWORK_CONSOLE_SOURCES})
|
||||
set(Q4_FRAMEWORK_CORE_SOURCES ${Q4_FRAMEWORK_ENGINE_TOP_SOURCES})
|
||||
list(REMOVE_ITEM Q4_FRAMEWORK_CORE_SOURCES
|
||||
${Q4_FRAMEWORK_SESSION_SOURCES}
|
||||
${Q4_FRAMEWORK_CONSOLE_SOURCES}
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE Q4_UI_SOURCES CONFIGURE_DEPENDS
|
||||
${PROJECT_SOURCE_DIR}/ui/*.cpp
|
||||
@@ -357,7 +366,7 @@ add_executable(q4xp WIN32
|
||||
${Q4_AAS_SOURCES}
|
||||
${Q4_CM_MODEL_SOURCE}
|
||||
${Q4_CM_SOURCES}
|
||||
${Q4_FRAMEWORK_TOP_SOURCES}
|
||||
${Q4_FRAMEWORK_ENGINE_TOP_SOURCES}
|
||||
${Q4_FRAMEWORK_ASYNC_SOURCES}
|
||||
${Q4_FRAMEWORK_MSGCHANNEL_SOURCE}
|
||||
${Q4_UI_SOURCES}
|
||||
@@ -388,6 +397,7 @@ target_compile_definitions(q4xp PRIVATE
|
||||
WIN32
|
||||
_WINDOWS
|
||||
_LOAD_DLL
|
||||
__DOOM_DLL__
|
||||
_USE_32BIT_TIME_T
|
||||
Q4_NO_PUNKBUSTER
|
||||
Q4_RECON_SEED
|
||||
@@ -429,6 +439,7 @@ if(MSVC)
|
||||
/DEBUG:FULL
|
||||
/PDB:E:/projects/Quake4Alpha/q4xp.pdb
|
||||
/MAP:E:/projects/Quake4Alpha/q4xp.map
|
||||
/STACK:16777216,4096
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -190,11 +190,7 @@ public:
|
||||
static idCVar gui_configServerRate;
|
||||
|
||||
int timeHitch;
|
||||
idRenderWorld * rw;
|
||||
int sw;
|
||||
idDemoFile * readDemo;
|
||||
idDemoFile * writeDemo;
|
||||
int renderdemoVersion;
|
||||
|
||||
bool menuActive;
|
||||
int menuSoundWorld; // SOUNDWORLD_MENU in Quake 4's id-based sound API
|
||||
@@ -310,14 +306,14 @@ public:
|
||||
void RunGameTic();
|
||||
|
||||
void FinishCmdLoad();
|
||||
void LoadLoadingGui(const char *mapName);
|
||||
void LoadLoadingGui( const char *mapName, const char *entityFilter );
|
||||
|
||||
void DemoShot( const char *name );
|
||||
|
||||
void TestGUI( const char *name );
|
||||
|
||||
int GetBytesNeededForMapLoad( const char *mapName );
|
||||
void SetBytesNeededForMapLoad( const char *mapName, int bytesNeeded );
|
||||
int GetBytesNeededForMapLoad( const char *mapName, const char *entityFilter );
|
||||
void SetBytesNeededForMapLoad( const char *mapName, const char *entityFilter, int bytesNeeded );
|
||||
|
||||
void ExecuteMapChange( bool noFadeWipe = false );
|
||||
void UnloadMap();
|
||||
|
||||
@@ -89,32 +89,6 @@ class idNetworkSystem {
|
||||
public:
|
||||
virtual ~idNetworkSystem( void ) {}
|
||||
|
||||
#ifdef Q4_RECON_ENGINE_PRIVATE
|
||||
// Interface and ordering recovered from quake4.exe's 24-slot vtable.
|
||||
virtual void ServerSendReliableMessage( int clientNum, const idBitMsg &msg );
|
||||
virtual void ServerSendReliableMessageExcluding( int clientNum, const idBitMsg &msg );
|
||||
virtual int ServerGetClientPing( int clientNum );
|
||||
virtual int ServerGetClientPrediction( int clientNum );
|
||||
virtual int ServerGetClientTimeSinceLastPacket( int clientNum );
|
||||
virtual int ServerGetClientTimeSinceLastInput( int clientNum );
|
||||
virtual int ServerGetClientOutgoingRate( int clientNum );
|
||||
virtual int ServerGetClientIncomingRate( int clientNum );
|
||||
virtual float ServerGetClientIncomingPacketLoss( int clientNum );
|
||||
virtual void ClientSendReliableMessage( const idBitMsg &msg );
|
||||
virtual int ClientGetPrediction( void );
|
||||
virtual int ClientGetTimeSinceLastPacket( void );
|
||||
virtual int ClientGetOutgoingRate( void );
|
||||
virtual int ClientGetIncomingRate( void );
|
||||
virtual float ClientGetIncomingPacketLoss( void );
|
||||
virtual const char * GetServerAddress( void );
|
||||
virtual const char * GetClientAddress( int clientNum );
|
||||
virtual void AddFriend( int clientNum );
|
||||
virtual void RemoveFriend( int clientNum );
|
||||
virtual void SetLoadingText( const char *loadingText );
|
||||
virtual void AddLoadingIcon( const char *icon );
|
||||
virtual const char * GetClientGUID( int clientNum );
|
||||
virtual void GetTrafficStats( int &bytesSent, int &packetsSent, int &bytesReceived, int &packetsReceived ) const;
|
||||
#else
|
||||
virtual void Shutdown( void );
|
||||
|
||||
virtual void ServerSendReliableMessage( int clientNum, const idBitMsg &msg, bool inhibitRepeater = false );
|
||||
@@ -176,7 +150,6 @@ public:
|
||||
private:
|
||||
scannedServer_t scannedServer;
|
||||
scannedClient_t scannedClient;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern idNetworkSystem * networkSystem;
|
||||
|
||||
@@ -33,6 +33,30 @@ idNetworkSystem * networkSystem = &networkSystemLocal;
|
||||
// The retail implementation stores this state in Raven's server-scan object.
|
||||
// Keep the ABI-neutral state here until that UI scanner is reconstructed.
|
||||
static bool networkFriendClients[MAX_ASYNC_CLIENTS];
|
||||
static idList<sortInfo_t> networkSortFunctions;
|
||||
static idList<sortInfo_t> networkActiveSortFunctions;
|
||||
|
||||
static int FindSortFunction( const idList<sortInfo_t> &list, const sortInfo_t &sortInfo ) {
|
||||
for ( int i = 0; i < list.Num(); i++ ) {
|
||||
if ( list[i].column == sortInfo.column &&
|
||||
list[i].compareFn == sortInfo.compareFn &&
|
||||
list[i].filterFn == sortInfo.filterFn ) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idNetworkSystem::Shutdown
|
||||
==================
|
||||
*/
|
||||
void idNetworkSystem::Shutdown( void ) {
|
||||
networkSortFunctions.Clear();
|
||||
networkActiveSortFunctions.Clear();
|
||||
memset( networkFriendClients, 0, sizeof( networkFriendClients ) );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@@ -40,18 +64,42 @@ static bool networkFriendClients[MAX_ASYNC_CLIENTS];
|
||||
idNetworkSystem::ServerSendReliableMessage
|
||||
==================
|
||||
*/
|
||||
void idNetworkSystem::ServerSendReliableMessage( int clientNum, const idBitMsg &msg ) {
|
||||
void idNetworkSystem::ServerSendReliableMessage( int clientNum, const idBitMsg &msg, bool inhibitRepeater ) {
|
||||
(void)inhibitRepeater;
|
||||
if ( idAsyncNetwork::server.IsActive() ) {
|
||||
idAsyncNetwork::server.SendReliableGameMessage( clientNum, msg );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idNetworkSystem::RepeaterSendReliableMessage
|
||||
idNetworkSystem::RepeaterSendReliableMessageExcluding
|
||||
==================
|
||||
*/
|
||||
void idNetworkSystem::RepeaterSendReliableMessage( int clientNum, const idBitMsg &msg, bool inhibitHeader, int including ) {
|
||||
(void)inhibitHeader;
|
||||
(void)including;
|
||||
if ( idAsyncNetwork::server.IsActive() ) {
|
||||
idAsyncNetwork::server.SendReliableGameMessage( clientNum, msg );
|
||||
}
|
||||
}
|
||||
|
||||
void idNetworkSystem::RepeaterSendReliableMessageExcluding( int excluding, const idBitMsg &msg, bool inhibitHeader, int clientNum ) {
|
||||
(void)inhibitHeader;
|
||||
(void)clientNum;
|
||||
if ( idAsyncNetwork::server.IsActive() ) {
|
||||
idAsyncNetwork::server.SendReliableGameMessageExcluding( excluding, msg );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idNetworkSystem::ServerSendReliableMessageExcluding
|
||||
==================
|
||||
*/
|
||||
void idNetworkSystem::ServerSendReliableMessageExcluding( int clientNum, const idBitMsg &msg ) {
|
||||
void idNetworkSystem::ServerSendReliableMessageExcluding( int clientNum, const idBitMsg &msg, bool inhibitRepeater ) {
|
||||
(void)inhibitRepeater;
|
||||
if ( idAsyncNetwork::server.IsActive() ) {
|
||||
idAsyncNetwork::server.SendReliableGameMessageExcluding( clientNum, msg );
|
||||
}
|
||||
@@ -69,18 +117,6 @@ int idNetworkSystem::ServerGetClientPing( int clientNum ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idNetworkSystem::ServerGetClientPrediction
|
||||
==================
|
||||
*/
|
||||
int idNetworkSystem::ServerGetClientPrediction( int clientNum ) {
|
||||
if ( idAsyncNetwork::server.IsActive() ) {
|
||||
return idAsyncNetwork::server.GetClientPrediction( clientNum );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idNetworkSystem::ServerGetClientTimeSinceLastPacket
|
||||
@@ -141,6 +177,32 @@ float idNetworkSystem::ServerGetClientIncomingPacketLoss( int clientNum ) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idNetworkSystem::ServerGetClientNum
|
||||
idNetworkSystem::ServerGetServerTime
|
||||
idNetworkSystem::ServerConnectBot
|
||||
idNetworkSystem::RepeaterGetClientNum
|
||||
==================
|
||||
*/
|
||||
int idNetworkSystem::ServerGetClientNum( int clientId ) {
|
||||
// The reconstructed asynchronous server does not expose its private clientId
|
||||
// table. Local callers pass an already resolved client number.
|
||||
return ( clientId >= 0 && clientId < MAX_ASYNC_CLIENTS ) ? clientId : -1;
|
||||
}
|
||||
|
||||
int idNetworkSystem::ServerGetServerTime( void ) {
|
||||
return common->GetFrameTime();
|
||||
}
|
||||
|
||||
int idNetworkSystem::ServerConnectBot( void ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int idNetworkSystem::RepeaterGetClientNum( int clientId ) {
|
||||
return ServerGetClientNum( clientId );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idNetworkSystem::ClientSendReliableMessage
|
||||
@@ -301,3 +363,100 @@ void idNetworkSystem::GetTrafficStats( int &bytesSent, int &packetsSent, int &by
|
||||
idAsyncNetwork::client.GetTrafficStats( bytesSent, packetsSent, bytesReceived, packetsReceived );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idNetworkSystem server browser API
|
||||
==================
|
||||
*/
|
||||
int idNetworkSystem::GetNumScannedServers( void ) {
|
||||
return idAsyncNetwork::client.serverList.Num();
|
||||
}
|
||||
|
||||
const scannedServer_t *idNetworkSystem::GetScannedServerInfo( int serverNum ) {
|
||||
if ( serverNum < 0 || serverNum >= idAsyncNetwork::client.serverList.Num() ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const networkServer_t &source = idAsyncNetwork::client.serverList[serverNum];
|
||||
scannedServer.adr = source.adr;
|
||||
scannedServer.serverInfo = source.serverInfo;
|
||||
scannedServer.ping = source.ping;
|
||||
scannedServer.clients = source.clients;
|
||||
scannedServer.OSMask = source.OSMask;
|
||||
scannedServer.favorite = false;
|
||||
scannedServer.dedicated = source.serverInfo.GetBool( "si_dedicated" );
|
||||
scannedServer.performanceFiltered = false;
|
||||
return &scannedServer;
|
||||
}
|
||||
|
||||
const scannedClient_t *idNetworkSystem::GetScannedServerClientInfo( int serverNum, int clientNum ) {
|
||||
if ( serverNum < 0 || serverNum >= idAsyncNetwork::client.serverList.Num() ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const networkServer_t &source = idAsyncNetwork::client.serverList[serverNum];
|
||||
if ( clientNum < 0 || clientNum >= source.clients || clientNum >= MAX_ASYNC_CLIENTS ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scannedClient.nickname = source.nickname[clientNum];
|
||||
scannedClient.clan.Clear();
|
||||
scannedClient.ping = source.pings[clientNum];
|
||||
scannedClient.rate = source.rate[clientNum];
|
||||
return &scannedClient;
|
||||
}
|
||||
|
||||
void idNetworkSystem::AddSortFunction( const sortInfo_t &sortInfo ) {
|
||||
if ( FindSortFunction( networkSortFunctions, sortInfo ) < 0 ) {
|
||||
networkSortFunctions.Append( sortInfo );
|
||||
}
|
||||
}
|
||||
|
||||
bool idNetworkSystem::RemoveSortFunction( const sortInfo_t &sortInfo ) {
|
||||
const int activeIndex = FindSortFunction( networkActiveSortFunctions, sortInfo );
|
||||
if ( activeIndex >= 0 ) {
|
||||
networkActiveSortFunctions.RemoveIndex( activeIndex );
|
||||
}
|
||||
const int index = FindSortFunction( networkSortFunctions, sortInfo );
|
||||
if ( index < 0 ) {
|
||||
return false;
|
||||
}
|
||||
networkSortFunctions.RemoveIndex( index );
|
||||
return true;
|
||||
}
|
||||
|
||||
void idNetworkSystem::UseSortFunction( const sortInfo_t &sortInfo, bool use ) {
|
||||
const int index = FindSortFunction( networkActiveSortFunctions, sortInfo );
|
||||
if ( use ) {
|
||||
AddSortFunction( sortInfo );
|
||||
if ( index < 0 ) {
|
||||
networkActiveSortFunctions.Append( sortInfo );
|
||||
}
|
||||
} else if ( index >= 0 ) {
|
||||
networkActiveSortFunctions.RemoveIndex( index );
|
||||
}
|
||||
}
|
||||
|
||||
bool idNetworkSystem::SortFunctionIsActive( const sortInfo_t &sortInfo ) {
|
||||
return FindSortFunction( networkActiveSortFunctions, sortInfo ) >= 0;
|
||||
}
|
||||
|
||||
bool idNetworkSystem::HTTPEnable( bool enable ) {
|
||||
(void)enable;
|
||||
return false;
|
||||
}
|
||||
|
||||
void idNetworkSystem::ClientSetServerInfo( const idDict &serverSI ) {
|
||||
if ( game != NULL ) {
|
||||
game->SetServerInfo( serverSI );
|
||||
}
|
||||
}
|
||||
|
||||
void idNetworkSystem::RepeaterSetInfo( const idDict &info ) {
|
||||
(void)info;
|
||||
}
|
||||
|
||||
const char *idNetworkSystem::GetViewerGUID( int clientNum ) {
|
||||
return GetClientGUID( clientNum );
|
||||
}
|
||||
|
||||
@@ -2815,6 +2815,7 @@ void idCommonLocal::LoadGameDLL( void ) {
|
||||
gameImport.declManager = ::declManager;
|
||||
gameImport.AASFileManager = ::AASFileManager;
|
||||
gameImport.collisionModelManager = ::collisionModelManager;
|
||||
gameImport.bse = ::bse;
|
||||
|
||||
gameExport = *GetGameAPI( &gameImport );
|
||||
|
||||
|
||||
+12
-10
@@ -439,7 +439,7 @@ void idConsoleLocal::Clear() {
|
||||
int i;
|
||||
|
||||
for ( i = 0 ; i < CON_TEXTSIZE ; i++ ) {
|
||||
text[i] = (idStr::ColorIndex(C_COLOR_CYAN)<<8) | ' ';
|
||||
text[i] = (idStr::ColorIndex(C_COLOR_CONSOLE)<<8) | ' ';
|
||||
}
|
||||
|
||||
Bottom(); // go to end
|
||||
@@ -830,7 +830,7 @@ void idConsoleLocal::Linefeed() {
|
||||
}
|
||||
current++;
|
||||
for ( i = 0; i < LINE_WIDTH; i++ ) {
|
||||
text[(current%TOTAL_LINES)*LINE_WIDTH+i] = (idStr::ColorIndex(C_COLOR_CYAN)<<8) | ' ';
|
||||
text[(current%TOTAL_LINES)*LINE_WIDTH+i] = (idStr::ColorIndex(C_COLOR_CONSOLE)<<8) | ' ';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -855,7 +855,7 @@ void idConsoleLocal::Print( const char *txt ) {
|
||||
}
|
||||
#endif
|
||||
|
||||
color = idStr::ColorIndex( C_COLOR_CYAN );
|
||||
color = idStr::ColorIndex( C_COLOR_CONSOLE );
|
||||
|
||||
while ( (c = *(const unsigned char*)txt) != 0 ) {
|
||||
int escapeType;
|
||||
@@ -866,7 +866,7 @@ void idConsoleLocal::Print( const char *txt ) {
|
||||
continue;
|
||||
}
|
||||
if ( *( txt + 1 ) == C_COLOR_DEFAULT ) {
|
||||
color = idStr::ColorIndex( C_COLOR_CYAN );
|
||||
color = idStr::ColorIndex( C_COLOR_CONSOLE );
|
||||
} else {
|
||||
color = idStr::ColorIndex( *( txt + 1 ) );
|
||||
}
|
||||
@@ -963,7 +963,7 @@ void idConsoleLocal::DrawInput() {
|
||||
}
|
||||
}
|
||||
|
||||
renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) );
|
||||
renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CONSOLE ) );
|
||||
|
||||
renderSystem->DrawSmallChar( 1 * SMALLCHAR_WIDTH, y, ']', localConsole.charSetShader );
|
||||
|
||||
@@ -1021,7 +1021,7 @@ void idConsoleLocal::DrawNotify() {
|
||||
v += SMALLCHAR_HEIGHT;
|
||||
}
|
||||
|
||||
renderSystem->SetColor( colorCyan );
|
||||
renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CONSOLE ) );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1057,13 +1057,15 @@ void idConsoleLocal::DrawSolidConsole( float frac ) {
|
||||
renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, y, 0, 1.0f - displayFrac, 1, 1, consoleShader );
|
||||
}
|
||||
|
||||
renderSystem->SetColor( colorCyan );
|
||||
renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CONSOLE ) );
|
||||
renderSystem->DrawStretchPic( 0, y, SCREEN_WIDTH, 2, 0, 0, 0, 0, whiteShader );
|
||||
renderSystem->SetColor( colorWhite );
|
||||
|
||||
// draw the version number
|
||||
|
||||
renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) );
|
||||
idVec4 versionColor = colorWhite;
|
||||
versionColor.w = 0.5f;
|
||||
renderSystem->SetColor( versionColor );
|
||||
|
||||
idStr version = va( "%s %s %s V%s Build %u", "GSS_TEXT_NAME", "Quake4", "Release", "0.13.0.7", 1834u );
|
||||
i = version.Length();
|
||||
@@ -1084,7 +1086,7 @@ void idConsoleLocal::DrawSolidConsole( float frac ) {
|
||||
// draw from the bottom up
|
||||
if ( display != current ) {
|
||||
// draw arrows to show the buffer is backscrolled
|
||||
renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) );
|
||||
renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CONSOLE ) );
|
||||
for ( x = 0; x < LINE_WIDTH; x += 4 ) {
|
||||
renderSystem->DrawSmallChar( (x+1)*SMALLCHAR_WIDTH, idMath::FtoiFast( y ), '^', localConsole.charSetShader );
|
||||
}
|
||||
@@ -1128,7 +1130,7 @@ void idConsoleLocal::DrawSolidConsole( float frac ) {
|
||||
// draw the input prompt, user text, and cursor if desired
|
||||
DrawInput();
|
||||
|
||||
renderSystem->SetColor( colorCyan );
|
||||
renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CONSOLE ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
+21
-18
@@ -63,37 +63,40 @@ along with Quake 4 Reconstructed Source Code. If not, see <http://www.gnu.org/l
|
||||
|
||||
typedef enum {
|
||||
DECL_TABLE = 0,
|
||||
DECL_MATERIAL,
|
||||
DECL_SKIN,
|
||||
DECL_SOUND,
|
||||
DECL_ENTITYDEF,
|
||||
DECL_MODELDEF,
|
||||
DECL_MATERIAL = 1,
|
||||
DECL_SKIN = 2,
|
||||
DECL_SOUND = 3,
|
||||
DECL_ENTITYDEF = 4,
|
||||
DECL_MODELDEF = 5,
|
||||
// RAVEN BEGIN
|
||||
// jscott: added new decls
|
||||
DECL_MATERIALTYPE,
|
||||
DECL_LIPSYNC,
|
||||
DECL_PLAYBACK,
|
||||
DECL_EFFECT,
|
||||
DECL_MATERIALTYPE = 6,
|
||||
DECL_LIPSYNC = 7,
|
||||
DECL_PLAYBACK = 8,
|
||||
DECL_EFFECT = 9,
|
||||
// rjohnson: camera is now contained in a def for frame commands
|
||||
DECL_CAMERADEF,
|
||||
DECL_CAMERADEF = 10,
|
||||
// jscott: don't use these
|
||||
// DECL_FX,
|
||||
// DECL_PARTICLE,
|
||||
// RAVEN END
|
||||
DECL_AF,
|
||||
DECL_PDA,
|
||||
DECL_VIDEO,
|
||||
DECL_AUDIO,
|
||||
DECL_EMAIL,
|
||||
DECL_MODELEXPORT,
|
||||
DECL_MAPDEF,
|
||||
DECL_AF = 11,
|
||||
DECL_PDA = 12,
|
||||
DECL_VIDEO = 13,
|
||||
DECL_AUDIO = 14,
|
||||
DECL_EMAIL = 15,
|
||||
DECL_MODELEXPORT = 16,
|
||||
DECL_MAPDEF = 17,
|
||||
|
||||
// new decl types can be added here
|
||||
DECL_PLAYER_MODEL,
|
||||
DECL_PLAYER_MODEL = 18,
|
||||
|
||||
DECL_MAX_TYPES = 32
|
||||
} declType_t;
|
||||
|
||||
static_assert( DECL_EFFECT == 9 && DECL_PLAYER_MODEL == 18 && DECL_MAX_TYPES == 32,
|
||||
"Quake 4 declType_t ABI drift" );
|
||||
|
||||
typedef enum {
|
||||
DS_UNPARSED,
|
||||
DS_DEFAULTED, // set if a parse failed due to an error, or the lack of any source
|
||||
|
||||
@@ -25,6 +25,8 @@ along with Quake 4 Reconstructed Source Code. If not, see <http://www.gnu.org/l
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "../bse/BSE.h"
|
||||
|
||||
/*
|
||||
|
||||
GUIs and script remain separately parsed
|
||||
@@ -894,15 +896,20 @@ void idDeclManagerLocal::Init( void ) {
|
||||
RegisterDeclType( "materialType", DECL_MATERIALTYPE, idDeclAllocator<rvDeclMatType> );
|
||||
RegisterDeclType( "lipSync", DECL_LIPSYNC, idDeclAllocator<rvDeclLipSync> );
|
||||
RegisterDeclType( "playback", DECL_PLAYBACK, idDeclAllocator<rvDeclPlayback> );
|
||||
RegisterDeclType( "effect", DECL_EFFECT, idDeclAllocator<rvDeclEffect> );
|
||||
RegisterDeclType( "articulatedFigure", DECL_AF, idDeclAllocator<idDeclAF> );
|
||||
RegisterDeclType( "pda", DECL_PDA, idDeclAllocator<idDeclPDA> );
|
||||
RegisterDeclType( "email", DECL_EMAIL, idDeclAllocator<idDeclEmail> );
|
||||
RegisterDeclType( "video", DECL_VIDEO, idDeclAllocator<idDeclVideo> );
|
||||
RegisterDeclType( "audio", DECL_AUDIO, idDeclAllocator<idDeclAudio> );
|
||||
|
||||
RegisterDeclFolder( "materials", ".mtr", DECL_MATERIAL );
|
||||
RegisterDeclFolder( "skins", ".skin", DECL_SKIN );
|
||||
RegisterDeclFolder( "sound", ".sndshd", DECL_SOUND );
|
||||
RegisterDeclFolderWrapper( "materials", ".mtr", DECL_MATERIAL );
|
||||
RegisterDeclFolderWrapper( "skins", ".skin", DECL_SKIN );
|
||||
RegisterDeclFolderWrapper( "sound", ".sndshd", DECL_SOUND, false, true );
|
||||
RegisterDeclFolderWrapper( "materials/types", ".mtt", DECL_MATERIALTYPE );
|
||||
RegisterDeclFolderWrapper( "lipsync", ".lipsync", DECL_LIPSYNC );
|
||||
RegisterDeclFolderWrapper( "playbacks", ".playback", DECL_PLAYBACK, true );
|
||||
RegisterDeclFolderWrapper( "effects", ".fx", DECL_EFFECT, true );
|
||||
|
||||
// add console commands
|
||||
cmdSystem->AddCommand( "listDecls", ListDecls_f, CMD_FL_SYSTEM, "lists all decls" );
|
||||
|
||||
+56
-22
@@ -1440,26 +1440,49 @@ void idSessionLocal::UnloadMap() {
|
||||
idSessionLocal::LoadLoadingGui
|
||||
===============
|
||||
*/
|
||||
void idSessionLocal::LoadLoadingGui( const char *mapName ) {
|
||||
// load / program a gui to stay up on the screen while loading
|
||||
idStr stripped = mapName;
|
||||
stripped.StripFileExtension();
|
||||
stripped.StripPath();
|
||||
|
||||
char guiMap[ MAX_STRING_CHARS ];
|
||||
strncpy( guiMap, va( "guis/map/%s.gui", stripped.c_str() ), MAX_STRING_CHARS );
|
||||
// give the gamecode a chance to override
|
||||
const char *gameLoadingGui = game->GetLoadingGui( mapName );
|
||||
if ( gameLoadingGui != NULL && gameLoadingGui[0] != '\0' ) {
|
||||
idStr::Copynz( guiMap, gameLoadingGui, sizeof( guiMap ) );
|
||||
void idSessionLocal::LoadLoadingGui( const char *mapName, const char *entityFilter ) {
|
||||
idStr mapDeclName = mapName;
|
||||
if ( entityFilter && entityFilter[0] && !idAsyncNetwork::IsActive() ) {
|
||||
mapDeclName += "_";
|
||||
mapDeclName += entityFilter;
|
||||
}
|
||||
|
||||
if ( uiManager->CheckGui( guiMap ) ) {
|
||||
guiLoading = uiManager->FindGui( guiMap, true, false, true );
|
||||
const idDecl *decl = declManager->FindType( DECL_MAPDEF, mapDeclName.c_str(), false );
|
||||
const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>( decl );
|
||||
|
||||
const char *levelName = mapDeclName.c_str();
|
||||
const char *objectives = "";
|
||||
const char *loadImage = "gfx/guis/loadscreens/generic";
|
||||
|
||||
if ( mapDef ) {
|
||||
levelName = mapDef->dict.GetString( "name", mapDeclName.c_str() );
|
||||
objectives = mapDef->dict.GetString( "objectives", "" );
|
||||
loadImage = mapDef->dict.GetString( "loadimage", "gfx/guis/loadscreens/generic" );
|
||||
|
||||
const char *loadGui = mapDef->dict.GetString( "loadgui", "" );
|
||||
if ( loadGui[0] ) {
|
||||
guiLoading = uiManager->FindGui( loadGui, true, false, true );
|
||||
} else if ( objectives[0] ) {
|
||||
guiLoading = uiManager->FindGui( "guis/loading/splevel.gui", true, false, true );
|
||||
} else if ( idAsyncNetwork::IsActive() ) {
|
||||
guiLoading = uiManager->FindGui( "guis/loading/mplevel.gui", true, false, true );
|
||||
} else {
|
||||
guiLoading = uiManager->FindGui( "guis/map/loading.gui", true, false, true );
|
||||
guiLoading = uiManager->FindGui( "guis/loading/generic.gui", true, false, true );
|
||||
}
|
||||
} else if ( idAsyncNetwork::IsActive() ) {
|
||||
guiLoading = uiManager->FindGui( "guis/loading/mplevel.gui", true, false, true );
|
||||
} else {
|
||||
guiLoading = uiManager->FindGui( "guis/loading/generic.gui", true, false, true );
|
||||
}
|
||||
|
||||
if ( guiLoading ) {
|
||||
guiLoading->SetStateFloat( "map_loading", 0.0f );
|
||||
guiLoading->SetStateString( "loading_bkgnd", loadImage );
|
||||
guiLoading->SetStateString( "loading_levelname", common->GetLocalizedString( levelName ) );
|
||||
guiLoading->SetStateString( "loading_objectives", common->GetLocalizedString( objectives ) );
|
||||
declManager->FindMaterial( loadImage )->SetSort( SS_GUI );
|
||||
guiLoading->StateChanged( com_frameTime, false );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1467,8 +1490,13 @@ void idSessionLocal::LoadLoadingGui( const char *mapName ) {
|
||||
idSessionLocal::GetBytesNeededForMapLoad
|
||||
===============
|
||||
*/
|
||||
int idSessionLocal::GetBytesNeededForMapLoad( const char *mapName ) {
|
||||
const idDecl *mapDecl = declManager->FindType( DECL_MAPDEF, mapName, false );
|
||||
int idSessionLocal::GetBytesNeededForMapLoad( const char *mapName, const char *entityFilter ) {
|
||||
idStr mapDeclName = mapName;
|
||||
if ( entityFilter && entityFilter[0] ) {
|
||||
mapDeclName += "_";
|
||||
mapDeclName += entityFilter;
|
||||
}
|
||||
const idDecl *mapDecl = declManager->FindType( DECL_MAPDEF, mapDeclName.c_str(), false );
|
||||
const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>( mapDecl );
|
||||
if ( mapDef ) {
|
||||
return mapDef->dict.GetInt( va("size%d", Max( 0, com_machineSpec.GetInteger() ) ) );
|
||||
@@ -1486,8 +1514,13 @@ int idSessionLocal::GetBytesNeededForMapLoad( const char *mapName ) {
|
||||
idSessionLocal::SetBytesNeededForMapLoad
|
||||
===============
|
||||
*/
|
||||
void idSessionLocal::SetBytesNeededForMapLoad( const char *mapName, int bytesNeeded ) {
|
||||
idDecl *mapDecl = const_cast<idDecl *>(declManager->FindType( DECL_MAPDEF, mapName, false ));
|
||||
void idSessionLocal::SetBytesNeededForMapLoad( const char *mapName, const char *entityFilter, int bytesNeeded ) {
|
||||
idStr mapDeclName = mapName;
|
||||
if ( entityFilter && entityFilter[0] ) {
|
||||
mapDeclName += "_";
|
||||
mapDeclName += entityFilter;
|
||||
}
|
||||
idDecl *mapDecl = const_cast<idDecl *>(declManager->FindType( DECL_MAPDEF, mapDeclName.c_str(), false ));
|
||||
idDeclEntityDef *mapDef = static_cast<idDeclEntityDef *>( mapDecl );
|
||||
|
||||
if ( com_updateLoadSize.GetBool() && mapDef ) {
|
||||
@@ -1554,6 +1587,7 @@ void idSessionLocal::ExecuteMapChange( bool noFadeWipe ) {
|
||||
|
||||
// extract the map name from serverinfo
|
||||
idStr mapString = mapSpawnData.serverInfo.GetString( "si_map" );
|
||||
idStr filterString = mapSpawnData.serverInfo.GetString( "si_entityFilter" );
|
||||
|
||||
idStr fullMapName = "maps/";
|
||||
fullMapName += mapString;
|
||||
@@ -1581,7 +1615,7 @@ void idSessionLocal::ExecuteMapChange( bool noFadeWipe ) {
|
||||
uiManager->Reload( true );
|
||||
|
||||
// set the loading gui that we will wipe to
|
||||
LoadLoadingGui( mapString );
|
||||
LoadLoadingGui( mapString.c_str(), filterString.c_str() );
|
||||
|
||||
// cause prints to force screen updates as a pacifier,
|
||||
// and draw the loading gui instead of game draws
|
||||
@@ -1591,7 +1625,7 @@ void idSessionLocal::ExecuteMapChange( bool noFadeWipe ) {
|
||||
// work for new maps etc. after the first load. we can also drop the sizes into the default.cfg
|
||||
fileSystem->ResetReadCount();
|
||||
if ( !reloadingSameMap ) {
|
||||
bytesNeededForMapLoad = GetBytesNeededForMapLoad( mapString.c_str() );
|
||||
bytesNeededForMapLoad = GetBytesNeededForMapLoad( mapString.c_str(), filterString.c_str() );
|
||||
} else {
|
||||
bytesNeededForMapLoad = 30 * 1024 * 1024;
|
||||
}
|
||||
@@ -1662,7 +1696,7 @@ void idSessionLocal::ExecuteMapChange( bool noFadeWipe ) {
|
||||
renderSystem->EndLevelLoad();
|
||||
soundSystem->EndLevelLoad( mapString.c_str() );
|
||||
declManager->EndLevelLoad();
|
||||
SetBytesNeededForMapLoad( mapString.c_str(), fileSystem->GetReadCount() );
|
||||
SetBytesNeededForMapLoad( mapString.c_str(), filterString.c_str(), fileSystem->GetReadCount() );
|
||||
}
|
||||
uiManager->EndLevelLoad();
|
||||
|
||||
|
||||
+27
-1
@@ -504,6 +504,7 @@ void idGameLocal::Init( void ) {
|
||||
// RAVEN BEGIN
|
||||
// rjohnson: camera is now contained in a def for frame commands
|
||||
declManager->RegisterDeclType( "camera", DECL_CAMERADEF, idDeclAllocator<idDeclCameraDef> );
|
||||
declManager->RegisterDeclType( "playerModel", DECL_PLAYER_MODEL, idDeclAllocator<rvDeclPlayerModel> );
|
||||
// RAVEN END
|
||||
// register game specific decl folders
|
||||
// RAVEN BEGIN
|
||||
@@ -1351,6 +1352,7 @@ void idGameLocal::LoadMap( const char *mapName, int randseed ) {
|
||||
// RAVEN END
|
||||
}
|
||||
mapFileName = mapFile->GetName();
|
||||
Printf( "Q4 game trace: map resolved as %s\n", mapFileName.c_str() );
|
||||
|
||||
assert(!idStr::Cmp(mapFileName, mapFile->GetName()));
|
||||
|
||||
@@ -1370,8 +1372,13 @@ void idGameLocal::LoadMap( const char *mapName, int randseed ) {
|
||||
// RAVEN END
|
||||
|
||||
// load the collision map
|
||||
networkSystem->SetLoadingText( common->GetLocalizedString( "#str_107668" ) );
|
||||
Printf( "Q4 game trace: resolving collision loading text\n" );
|
||||
const char *collisionLoadingText = common->GetLocalizedString( "#str_107668" );
|
||||
Printf( "Q4 game trace: setting collision loading text\n" );
|
||||
networkSystem->SetLoadingText( collisionLoadingText );
|
||||
Printf( "Q4 game trace: loading collision map\n" );
|
||||
collisionModelManager->LoadMap( mapFile, false );
|
||||
Printf( "Q4 game trace: collision map loaded\n" );
|
||||
|
||||
numClients = 0;
|
||||
|
||||
@@ -1445,10 +1452,12 @@ void idGameLocal::LoadMap( const char *mapName, int randseed ) {
|
||||
gravityInfo.Clear();
|
||||
scriptObjectProxies.Clear();
|
||||
// RAVEN END
|
||||
Printf( "Q4 game trace: base map state cleared\n" );
|
||||
|
||||
if ( !editEntities ) {
|
||||
editEntities = new idEditEntities;
|
||||
}
|
||||
Printf( "Q4 game trace: edit entities ready\n" );
|
||||
|
||||
if ( gameLocal.isMultiplayer ) {
|
||||
gravity.Set( 0, 0, -g_mp_gravity.GetFloat() );
|
||||
@@ -1464,6 +1473,7 @@ void idGameLocal::LoadMap( const char *mapName, int randseed ) {
|
||||
aiManager.UnMarkAllReachBlocked();
|
||||
aiManager.Clear();
|
||||
// RAVEN END
|
||||
Printf( "Q4 game trace: AI manager cleared\n" );
|
||||
|
||||
skipCinematic = false;
|
||||
inCinematic = false;
|
||||
@@ -1473,11 +1483,15 @@ void idGameLocal::LoadMap( const char *mapName, int randseed ) {
|
||||
|
||||
// RAVEN BEGIN
|
||||
// ddynerman: main world instance
|
||||
Printf( "Q4 game trace: adding world instance\n" );
|
||||
PACIFIER_UPDATE;
|
||||
AddInstance( 0, true );
|
||||
Printf( "Q4 game trace: world instance added\n" );
|
||||
assert( instances.Num() == 1 && instances[ 0 ]->GetInstanceID() == 0 );
|
||||
// RAVEN END
|
||||
Printf( "Q4 game trace: initializing PVS\n" );
|
||||
pvs.Init();
|
||||
Printf( "Q4 game trace: PVS initialized\n" );
|
||||
// RAVEN BEGIN
|
||||
// mwhitlock: Xenon texture streaming
|
||||
#if defined(_XENON)
|
||||
@@ -1498,10 +1512,14 @@ void idGameLocal::LoadMap( const char *mapName, int randseed ) {
|
||||
|
||||
// RAVEN BEGIN
|
||||
// cdr: Obstacle Avoidance
|
||||
Printf( "Q4 game trace: initializing AI movement\n" );
|
||||
AI_MoveInitialize();
|
||||
Printf( "Q4 game trace: AI movement initialized\n" );
|
||||
// RAVEN END
|
||||
|
||||
Printf( "Q4 game trace: precaching extras\n" );
|
||||
FindEntityDef( "preCacheExtras", false );
|
||||
Printf( "Q4 game trace: extras precached\n" );
|
||||
|
||||
if ( !sameMap ) {
|
||||
mapFile->RemovePrimitiveData();
|
||||
@@ -1511,6 +1529,7 @@ void idGameLocal::LoadMap( const char *mapName, int randseed ) {
|
||||
// ddynerman: ambient light list
|
||||
ambientLights.Clear();
|
||||
// RAVEN END
|
||||
Printf( "Q4 game trace: LoadMap complete\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -8214,6 +8233,7 @@ idGameLocal::AddClipWorld
|
||||
===================
|
||||
*/
|
||||
int idGameLocal::AddClipWorld( int id ) {
|
||||
Printf( "Q4 game trace: AddClipWorld %d begin (num=%d)\n", id, clip.Num() );
|
||||
if( id >= clip.Num() ) {
|
||||
// if we want an index higher in the list, fill the intermediate indices with empties
|
||||
for( int i = clip.Num(); i <= id; i++ ) {
|
||||
@@ -8227,12 +8247,15 @@ int idGameLocal::AddClipWorld( int id ) {
|
||||
RV_PUSH_SYS_HEAP_ID(RV_HEAP_ID_LEVEL);
|
||||
// RAVEN END
|
||||
clip[ id ] = new idClip();
|
||||
Printf( "Q4 game trace: AddClipWorld %d allocated\n", id );
|
||||
// RAVEN BEGIN
|
||||
// mwhitlock: Dynamic memory consolidation
|
||||
RV_POP_HEAP();
|
||||
// RAVEN END
|
||||
clip[ id ]->Init();
|
||||
Printf( "Q4 game trace: AddClipWorld %d initialized\n", id );
|
||||
}
|
||||
Printf( "Q4 game trace: AddClipWorld %d complete\n", id );
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -8271,6 +8294,7 @@ idGameLocal::AddInstance
|
||||
===================
|
||||
*/
|
||||
int idGameLocal::AddInstance( int id, bool deferPopulate ) {
|
||||
Printf( "Q4 game trace: AddInstance %d begin (num=%d)\n", id, instances.Num() );
|
||||
if ( id == -1 ) {
|
||||
id = instances.Num();
|
||||
}
|
||||
@@ -8287,6 +8311,7 @@ int idGameLocal::AddInstance( int id, bool deferPopulate ) {
|
||||
// mwhitlock: Dynamic memory consolidation
|
||||
RV_PUSH_SYS_HEAP_ID(RV_HEAP_ID_LEVEL);
|
||||
instances[ id ] = new rvInstance( id, deferPopulate );
|
||||
Printf( "Q4 game trace: AddInstance %d allocated\n", id );
|
||||
RV_POP_HEAP();
|
||||
// RAVEN END
|
||||
|
||||
@@ -8302,6 +8327,7 @@ int idGameLocal::AddInstance( int id, bool deferPopulate ) {
|
||||
|
||||
// keep the min spawn index correctly set
|
||||
ServerSetMinSpawnIndex();
|
||||
Printf( "Q4 game trace: AddInstance %d complete\n", id );
|
||||
|
||||
return instances[ id ]->GetInstanceID();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,11 +34,7 @@ instancing of objects.
|
||||
|
||||
#include "../Game_local.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "TypeInfo.h"
|
||||
#else
|
||||
#include "NoGameTypeInfo.h"
|
||||
#endif
|
||||
|
||||
/***********************************************************************
|
||||
|
||||
|
||||
@@ -27,11 +27,7 @@ along with Quake 4 Reconstructed Source Code. If not, see <http://www.gnu.org/l
|
||||
|
||||
#include "../Game_local.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "TypeInfo.h"
|
||||
#else
|
||||
#include "NoGameTypeInfo.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
Save game related helper classes.
|
||||
|
||||
@@ -48,11 +48,7 @@ along with Quake 4 Reconstructed Source Code. If not, see <http://www.gnu.org/l
|
||||
#endif
|
||||
// RAVEN END
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "TypeInfo.h"
|
||||
#else
|
||||
#include "NoGameTypeInfo.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
==================
|
||||
@@ -194,7 +190,7 @@ void Cmd_ListSpawnArgs_f( const idCmdArgs &args ) {
|
||||
|
||||
for ( i = 0; i < ent->spawnArgs.GetNumKeyVals(); i++ ) {
|
||||
const idKeyValue *kv = ent->spawnArgs.GetKeyVal( i );
|
||||
gameLocal.Printf( "\"%s\" "S_COLOR_WHITE"\"%s\"\n", kv->GetKey().c_str(), kv->GetValue().c_str() );
|
||||
gameLocal.Printf( "\"%s\" " S_COLOR_WHITE "\"%s\"\n", kv->GetKey().c_str(), kv->GetValue().c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1239,7 +1239,6 @@ void idClip::GetClipSectorsStaticContents( void ) {
|
||||
for( y = 0; y < CLIPSECTOR_WIDTH; y++ ) {
|
||||
org.x = ( x / nodeScale.x ) + nodeOffset.x;
|
||||
org.y = ( y / nodeScale.y ) + nodeOffset.y;
|
||||
|
||||
int contents = collisionModelManager->Contents( org, trm, mat3_identity, -1, world, vec3_origin, mat3_default );
|
||||
clipSectors[ x + ( y << CLIPSECTOR_DEPTH ) ].contents = contents;
|
||||
}
|
||||
|
||||
@@ -76,3 +76,42 @@ set_target_properties(q4_idlib PROPERTIES
|
||||
OUTPUT_NAME idlib
|
||||
FOLDER "Engine"
|
||||
)
|
||||
|
||||
# The Quake 4 SDK builds idlib a second time with Q4SDK for gamex86.dll.
|
||||
# Several public classes (notably the SIMD hierarchy) are macro-sensitive, so
|
||||
# linking the engine-private idlib into the game DLL gives the DLL a different
|
||||
# vtable layout than the one seen by its game translation units.
|
||||
set(Q4_GAME_IDLIB_SOURCES
|
||||
${Q4_IDLIB_SOURCES}
|
||||
geometry/Winding2D.cpp
|
||||
math/Lcp.cpp
|
||||
math/Ode.cpp
|
||||
)
|
||||
|
||||
add_library(q4_game_idlib STATIC ${Q4_GAME_IDLIB_SOURCES})
|
||||
|
||||
target_include_directories(q4_game_idlib
|
||||
PUBLIC
|
||||
${PROJECT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PRIVATE
|
||||
"C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include"
|
||||
)
|
||||
|
||||
target_compile_definitions(q4_game_idlib
|
||||
PRIVATE
|
||||
WIN32
|
||||
_WINDOWS
|
||||
_USE_32BIT_TIME_T
|
||||
Q4SDK
|
||||
Q4_NO_PUNKBUSTER
|
||||
$<$<CONFIG:Debug>:_DEBUG>
|
||||
$<$<CONFIG:Release>:NDEBUG;_FINAL>
|
||||
)
|
||||
|
||||
target_precompile_headers(q4_game_idlib PRIVATE precompiled.h)
|
||||
|
||||
set_target_properties(q4_game_idlib PROPERTIES
|
||||
OUTPUT_NAME game_idlib
|
||||
FOLDER "Game"
|
||||
)
|
||||
|
||||
+1
-1
@@ -581,7 +581,7 @@ void AssertFailed( const char *file, int line, const char *expression ) {
|
||||
#ifdef _WIN32
|
||||
// RAVEN BEGIN
|
||||
// jnewquist: Visual Studio platform independent breakpoint
|
||||
__debugbreak();
|
||||
// __debugbreak();
|
||||
// RAVEN END
|
||||
#elif defined( __linux__ )
|
||||
__asm__ __volatile__ ("int $0x03");
|
||||
|
||||
@@ -520,6 +520,7 @@ void idGameLocal::Init( void ) {
|
||||
// RAVEN BEGIN
|
||||
// rjohnson: camera is now contained in a def for frame commands
|
||||
declManager->RegisterDeclType( "camera", DECL_CAMERADEF, idDeclAllocator<idDeclCameraDef> );
|
||||
declManager->RegisterDeclType( "playerModel", DECL_PLAYER_MODEL, idDeclAllocator<rvDeclPlayerModel> );
|
||||
// RAVEN END
|
||||
// register game specific decl folders
|
||||
// RAVEN BEGIN
|
||||
|
||||
@@ -476,6 +476,9 @@ byte *R_Dropsample( const byte *in, int inwidth, int inheight,
|
||||
int outwidth, int outheight );
|
||||
byte *R_ResampleTexture( const byte *in, int inwidth, int inheight,
|
||||
int outwidth, int outheight );
|
||||
|
||||
extern int emptyCubeSize;
|
||||
void makeEmptyCubeMap( idImage *image );
|
||||
byte *R_MipMapWithAlphaSpecularity( const byte *in, int width, int height );
|
||||
byte *R_MipMap( const byte *in, int width, int height, bool preserveBorder );
|
||||
byte *R_MipMap3D( const byte *in, int width, int height, int depth, bool preserveBorder );
|
||||
|
||||
@@ -162,6 +162,9 @@ public:
|
||||
|
||||
idStr mapName; // ie: maps/tim_dm2.proc, written to demoFile
|
||||
ID_TIME_T mapTimeStamp; // for fast reloads of the same level
|
||||
idStr m_filename; // proc/MD5RProc source selected for this world
|
||||
unsigned int m_CRC; // map file CRC stored in the proc header
|
||||
unsigned int procFileVersion; // PROC/MD5RProc format version
|
||||
|
||||
areaNode_t * areaNodes;
|
||||
int numAreaNodes;
|
||||
@@ -232,6 +235,7 @@ public:
|
||||
void AddAreaEntityRefs( int areaNum, const struct portalStack_s *ps );
|
||||
bool CullLightByPortals( const idRenderLightLocal *light, const struct portalStack_s *ps );
|
||||
void AddAreaLightRefs( int areaNum, const struct portalStack_s *ps );
|
||||
void AddAreaEffectRefs( int areaNum, const struct portalStack_s *ps );
|
||||
void AddAreaRefs( int areaNum, const struct portalStack_s *ps );
|
||||
void BuildConnectedAreas_r( int areaNum );
|
||||
void BuildConnectedAreas( void );
|
||||
@@ -274,7 +278,13 @@ public:
|
||||
void AddLightRefToArea( idRenderLightLocal *light, portalArea_t *area );
|
||||
void AddEffectRefToArea( rvRenderEffectLocal *effect, portalArea_t *area );
|
||||
void FreeEffectDefDerivedData( rvRenderEffectLocal *effect );
|
||||
void PushEffectDef( rvRenderEffectLocal *effect );
|
||||
void PushEffectDef( int effectHandle );
|
||||
void PushPolytopeIntoTree_r( idRenderEntityLocal *def, idRenderLightLocal *light,
|
||||
rvRenderEffectLocal *effect, const idBox &box,
|
||||
const idVec3 *points, int numPoints, int nodeNum );
|
||||
void PushPolytopeIntoTree( idRenderEntityLocal *def, idRenderLightLocal *light,
|
||||
rvRenderEffectLocal *effect, const idBox &box,
|
||||
const idVec3 *points, int numPoints );
|
||||
|
||||
void RecurseProcBSP_r( modelTrace_t *results, int parentNodeNum, int nodeNum, float p1f, float p2f, const idVec3 &p1, const idVec3 &p2 ) const;
|
||||
|
||||
|
||||
@@ -192,9 +192,11 @@ void RB_ARB2_DrawInteraction( const drawInteraction_t *din ) {
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, din->localViewOrigin.ToFloatPtr() );
|
||||
}
|
||||
|
||||
static const float zeroOne[4] = { 0, 0, 0, 1 };
|
||||
static const float oneZero[4] = { 1, 1, 1, 0 };
|
||||
static const float negOneOne[4] = { -1, -1, -1, 1 };
|
||||
// Quake 4 packs the vertex-color multiplier and addend into x/y.
|
||||
// interaction.vfp evaluates: color * env[16].x + env[16].y.
|
||||
static const float zeroOne[4] = { 0, 1, 0, 0 };
|
||||
static const float oneZero[4] = { 1, 0, 0, 0 };
|
||||
static const float negOneOne[4] = { -1, 1, 0, 0 };
|
||||
|
||||
switch ( din->vertexColor ) {
|
||||
case SVC_IGNORE:
|
||||
|
||||
@@ -677,6 +677,33 @@ static void makeNormalizeVectorCubeMap( idImage *image ) {
|
||||
Mem_Free(pixels[0]);
|
||||
}
|
||||
|
||||
int emptyCubeSize = 128;
|
||||
|
||||
void makeEmptyCubeMap( idImage *image ) {
|
||||
byte *pixels[6];
|
||||
const int size = emptyCubeSize;
|
||||
pixels[0] = static_cast<byte *>( Mem_Alloc( size * size * 4 * 6 ) );
|
||||
|
||||
for ( int face = 0; face < 6; ++face ) {
|
||||
pixels[face] = pixels[0] + face * size * size * 4;
|
||||
for ( int y = 0; y < size; ++y ) {
|
||||
for ( int x = 0; x < size; ++x ) {
|
||||
float vector[3];
|
||||
getCubeVector( face, size, x, y, vector );
|
||||
byte *pixel = pixels[face] + 4 * ( y * size + x );
|
||||
pixel[0] = static_cast<byte>( vector[0] * 127.0f + 128.0f );
|
||||
pixel[1] = static_cast<byte>( vector[1] * 127.0f + 128.0f );
|
||||
pixel[2] = static_cast<byte>( vector[2] * 127.0f + 128.0f );
|
||||
pixel[3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image->GenerateCubeImage( (const byte **)pixels, size,
|
||||
TF_LINEAR, false, TD_HIGH_QUALITY );
|
||||
Mem_Free( pixels[0] );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -56,6 +56,9 @@ Manager
|
||||
ID_TIME_T ProgramImagesTimestamp;
|
||||
bool gWriteProgramFlag;
|
||||
|
||||
idCVar r_skipDownsize( "r_skipDownsize", "0", CVAR_RENDERER | CVAR_BOOL,
|
||||
"skip downsize command in materials", idCmdSystem::ArgCompletion_Boolean );
|
||||
|
||||
/*
|
||||
|
||||
Anywhere that an image name is used (diffusemaps, bumpmaps, specularmaps, lights, etc),
|
||||
@@ -214,7 +217,6 @@ static void R_AddNormalMaps( byte *data1, int width1, int height1, byte *data2,
|
||||
for ( j = 0 ; j < width1 ; j++ ) {
|
||||
byte *d1, *d2;
|
||||
idVec3 n;
|
||||
float len;
|
||||
|
||||
d1 = data1 + ( i * width1 + j ) * 4;
|
||||
d2 = data2 + ( i * width1 + j ) * 4;
|
||||
@@ -225,8 +227,10 @@ static void R_AddNormalMaps( byte *data1, int width1, int height1, byte *data2,
|
||||
|
||||
// There are some normal maps that blend to 0,0,0 at the edges
|
||||
// this screws up compression, so we try to correct that here by instead fading it to 0,0,1
|
||||
len = n.LengthFast();
|
||||
if ( len < 1.0f ) {
|
||||
// Quake 4 tests the exact squared length against 0.96. The Doom 3
|
||||
// LengthFast()/1.0 test can accept an encoded normal whose X/Y sum is
|
||||
// already greater than one, producing a negative square-root input.
|
||||
if ( n.LengthSqr() < 0.96f ) {
|
||||
n[2] = idMath::Sqrt(1.0 - (n[0]*n[0]) - (n[1]*n[1]));
|
||||
}
|
||||
|
||||
@@ -234,9 +238,9 @@ static void R_AddNormalMaps( byte *data1, int width1, int height1, byte *data2,
|
||||
n[1] += ( d2[1] - 128 ) / 127.0;
|
||||
n.Normalize();
|
||||
|
||||
d1[0] = (byte)(n[0] * 127 + 128);
|
||||
d1[1] = (byte)(n[1] * 127 + 128);
|
||||
d1[2] = (byte)(n[2] * 127 + 128);
|
||||
d1[0] = (byte)idMath::ClampInt( 0, 255, (int)( n[0] * 127.0f + 128.0f ) );
|
||||
d1[1] = (byte)idMath::ClampInt( 0, 255, (int)( n[1] * 127.0f + 128.0f ) );
|
||||
d1[2] = (byte)idMath::ClampInt( 0, 255, (int)( n[2] * 127.0f + 128.0f ) );
|
||||
d1[3] = 255;
|
||||
}
|
||||
}
|
||||
@@ -585,6 +589,36 @@ static bool R_ParseImageProgram_r( idLexer &src, byte **pic, int *width, int *he
|
||||
return true;
|
||||
}
|
||||
|
||||
// Raven image programs can explicitly reduce an image before it is uploaded.
|
||||
// This must be parsed recursively: treating "downsize" as an image name leaves
|
||||
// the opening parenthesis in the material stream and defaults the whole material.
|
||||
if ( !token.Icmp( "downsize" ) ) {
|
||||
MatchAndAppendToken( src, "(" );
|
||||
gWriteProgramFlag = true;
|
||||
|
||||
if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, "," );
|
||||
src.ReadToken( &token );
|
||||
AppendToken( token );
|
||||
const int shift = token.GetIntValue();
|
||||
|
||||
if ( pic && !r_skipDownsize.GetBool() ) {
|
||||
const int newWidth = *width >> shift;
|
||||
const int newHeight = *height >> shift;
|
||||
byte *newPic = R_ResampleTexture( *pic, *width, *height, newWidth, newHeight );
|
||||
R_StaticFree( *pic );
|
||||
*pic = newPic;
|
||||
*width = newWidth;
|
||||
*height = newHeight;
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, ")" );
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we are just parsing instead of loading or checking,
|
||||
// don't do the R_LoadImage
|
||||
if ( !timestamps && !pic ) {
|
||||
|
||||
+309
-2
@@ -26,6 +26,23 @@ along with Quake 4 Reconstructed Source Code. If not, see <http://www.gnu.org/l
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
#include "Shaders.h"
|
||||
|
||||
// Negative expression indexes identify renderer-provided GLSL constants to
|
||||
// rvNewShaderStage::ParseShaderParm. Keep this order synchronized with the
|
||||
// binding table in Shaders.cpp and with the retail Quake 4 material parser.
|
||||
static const char *materialShaderConstantNames[] = {
|
||||
"lightOrigin", "viewOrigin", "lightProject_s", "lightProject_t",
|
||||
"lightProject_q", "lightFalloff_s", "bumpMatrix_s", "bumpMatrix_t",
|
||||
"diffuseMatrix_s", "diffuseMatrix_t", "specularMatrix_s", "specularMatrix_t",
|
||||
"colorModulate", "colorAdd", "diffuseColor", "specularColor",
|
||||
"colorMatrix0", "colorMatrix1", "colorMatrix2", "projectionMatrix0",
|
||||
"projectionMatrix1", "projectionMatrix2", "projectionMatrix3", "modelMatrix0",
|
||||
"modelMatrix1", "modelMatrix2", "globalEyePos", "mvpMatrix0",
|
||||
"mvpMatrix1", "mvpMatrix2", "mvpMatrix3", "gaussianSampleOffsets",
|
||||
"gaussianSampleWeights", "gaussianSampleOffsetsHorizontal",
|
||||
"gaussianSampleOffsetsVertical", "gaussianSampleWeights2"
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -73,7 +90,15 @@ idMaterial::CommonInit
|
||||
void idMaterial::CommonInit() {
|
||||
desc = "<none>";
|
||||
renderBump = "";
|
||||
portalDistanceNear = 262144.0f;
|
||||
portalDistanceFar = 262144.0f;
|
||||
contentFlags = CONTENTS_SOLID;
|
||||
allowOverlays = true;
|
||||
materialType = NULL;
|
||||
materialTypeArray = NULL;
|
||||
MTAWidth = 0;
|
||||
MTAHeight = 0;
|
||||
portalImage = NULL;
|
||||
surfaceFlags = SURFTYPE_NONE;
|
||||
materialFlags = 0;
|
||||
sort = SS_BAD;
|
||||
@@ -97,7 +122,6 @@ void idMaterial::CommonInit() {
|
||||
ambientLight = false;
|
||||
noFog = false;
|
||||
hasSubview = false;
|
||||
allowOverlays = true;
|
||||
unsmoothedTangents = false;
|
||||
gui = NULL;
|
||||
memset( deformRegisters, 0, sizeof( deformRegisters ) );
|
||||
@@ -124,6 +148,7 @@ idMaterial::idMaterial() {
|
||||
// we put this here instead of in CommonInit, because
|
||||
// we don't want it cleared when a material is purged
|
||||
surfaceArea = 0;
|
||||
globalUseCount = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -153,6 +178,10 @@ void idMaterial::FreeData() {
|
||||
Mem_Free( stages[i].newStage );
|
||||
stages[i].newStage = NULL;
|
||||
}
|
||||
if ( stages[i].newShaderStage != NULL ) {
|
||||
delete stages[i].newShaderStage;
|
||||
stages[i].newShaderStage = NULL;
|
||||
}
|
||||
}
|
||||
R_StaticFree( stages );
|
||||
stages = NULL;
|
||||
@@ -169,6 +198,11 @@ void idMaterial::FreeData() {
|
||||
R_StaticFree( ops );
|
||||
ops = NULL;
|
||||
}
|
||||
if ( materialTypeArray != NULL ) {
|
||||
Mem_Free( materialTypeArray );
|
||||
materialTypeArray = NULL;
|
||||
materialTypeArrayName.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -230,6 +264,14 @@ static infoParm_t infoParms[] = {
|
||||
{"aassolid", 0, 0, CONTENTS_AAS_SOLID }, // solid for AAS
|
||||
{"aasobstacle", 0, 0, CONTENTS_AAS_OBSTACLE },// used to compile an obstacle into AAS that can be enabled/disabled
|
||||
{"flashlight_trigger", 0, 0, CONTENTS_FLASHLIGHT_TRIGGER }, // used for triggers that are activated by the flashlight
|
||||
{"sightClip", 0, 0, CONTENTS_SIGHTCLIP },
|
||||
{"largeShotClip", 0, 0, CONTENTS_LARGESHOTCLIP },
|
||||
{"shotClip", 1, 0, CONTENTS_PROJECTILE },
|
||||
{"vehicleclip", 0, 0, CONTENTS_VEHICLECLIP },
|
||||
{"flyclip", 0, 0, CONTENTS_FLYCLIP },
|
||||
{"notacticalfeatures", 0, 0, CONTENTS_NOTACTICALFEATURES },
|
||||
{"bounce", 0, SURF_BOUNCE, 0 },
|
||||
{"itemclip", 0, 0, CONTENTS_ITEMCLIP },
|
||||
{"nonsolid", 1, 0, 0 }, // clears the solid flag
|
||||
{"nullNormal", 0, SURF_NULLNORMAL,0 }, // renderbump will draw as 0x80 0x80 0x80
|
||||
|
||||
@@ -242,6 +284,7 @@ static infoParm_t infoParms[] = {
|
||||
// because they represent discrete objects like gui shaders
|
||||
// mirrors, or autosprites
|
||||
{"noFragment", 0, SURF_NOFRAGMENT, 0 },
|
||||
{"noTFix", 1, SURF_NO_T_FIX, 0 },
|
||||
|
||||
{"slick", 0, SURF_SLICK, 0 },
|
||||
{"collision", 0, SURF_COLLISION, 0 },
|
||||
@@ -589,14 +632,45 @@ int idMaterial::ParseTerm( idLexer &src ) {
|
||||
pd->registersAreConstant = false;
|
||||
return EXP_REG_GLOBAL7;
|
||||
}
|
||||
if ( !token.Icmp( "IsMultiplayer" ) ) {
|
||||
return GetExpressionConstant( session->IsMultiplayer() ? 1.0f : 0.0f );
|
||||
}
|
||||
if ( !token.Icmp( "fragmentPrograms" ) ) {
|
||||
return GetExpressionConstant( (float) glConfig.ARBFragmentProgramAvailable );
|
||||
}
|
||||
if ( !token.Icmp( "POTCorrectionX" ) ) {
|
||||
return GetExpressionConstant( (float)glConfig.vidWidth / (float)MakePowerOfTwo( glConfig.vidWidth ) );
|
||||
}
|
||||
if ( !token.Icmp( "POTCorrectionY" ) ) {
|
||||
return GetExpressionConstant( (float)glConfig.vidHeight / (float)MakePowerOfTwo( glConfig.vidHeight ) );
|
||||
}
|
||||
if ( !token.Icmp( "VideoWidth" ) ) {
|
||||
return GetExpressionConstant( (float)glConfig.vidWidth );
|
||||
}
|
||||
if ( !token.Icmp( "VideoHeight" ) ) {
|
||||
return GetExpressionConstant( (float)glConfig.vidHeight );
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "sound" ) ) {
|
||||
pd->registersAreConstant = false;
|
||||
return EmitOp( 0, 0, OP_TYPE_SOUND );
|
||||
}
|
||||
if ( !token.Icmp( "glslPrograms" ) ) {
|
||||
pd->registersAreConstant = false;
|
||||
return EmitOp( 0, 0, OP_TYPE_GLSL_ENABLED );
|
||||
}
|
||||
if ( !token.Icmp( "DecalLife" ) ) {
|
||||
pd->registersAreConstant = false;
|
||||
return EXP_REG_DECAL_LIFE;
|
||||
}
|
||||
if ( !token.Icmp( "DecalSpawn" ) ) {
|
||||
pd->registersAreConstant = false;
|
||||
return EXP_REG_DECAL_SPAWN;
|
||||
}
|
||||
if ( !token.Icmp( "VertexRandomizer" ) ) {
|
||||
pd->registersAreConstant = false;
|
||||
return EXP_REG_VERTEX_RANDOMIZER;
|
||||
}
|
||||
|
||||
// parse negative numbers
|
||||
if ( token == "-" ) {
|
||||
@@ -613,6 +687,12 @@ int idMaterial::ParseTerm( idLexer &src ) {
|
||||
return GetExpressionConstant( (float) token.GetFloatValue() );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < (int)( sizeof( materialShaderConstantNames ) / sizeof( materialShaderConstantNames[0] ) ); ++i ) {
|
||||
if ( !token.Icmp( materialShaderConstantNames[i] ) ) {
|
||||
return -1 - i;
|
||||
}
|
||||
}
|
||||
|
||||
// see if it is a table name
|
||||
const idDeclTable *table = static_cast<const idDeclTable *>( declManager->FindType( DECL_TABLE, token.c_str(), false ) );
|
||||
if ( !table ) {
|
||||
@@ -726,6 +806,10 @@ idMaterial::ClearStage
|
||||
*/
|
||||
void idMaterial::ClearStage( shaderStage_t *ss ) {
|
||||
ss->drawStateBits = 0;
|
||||
ss->mStageRegisterStart = numRegisters;
|
||||
ss->mNumStageRegisters = 0;
|
||||
ss->mStageOpsStart = numOps;
|
||||
ss->mNumStageOps = 0;
|
||||
ss->conditionRegister = GetExpressionConstant( 1 );
|
||||
ss->color.registers[0] =
|
||||
ss->color.registers[1] =
|
||||
@@ -757,6 +841,10 @@ int idMaterial::NameToSrcBlendMode( const idStr &name ) {
|
||||
return GLS_SRCBLEND_ONE_MINUS_DST_ALPHA;
|
||||
} else if ( !name.Icmp( "GL_SRC_ALPHA_SATURATE" ) ) {
|
||||
return GLS_SRCBLEND_ALPHA_SATURATE;
|
||||
} else if ( !name.Icmp( "GL_SRC_COLOR" ) ) {
|
||||
return GLS_SRCBLEND_SRC_COLOR;
|
||||
} else if ( !name.Icmp( "GL_ONE_MINUS_SRC_COLOR" ) ) {
|
||||
return GLS_SRCBLEND_ONE_MINUS_SRC_COLOR;
|
||||
}
|
||||
|
||||
common->Warning( "unknown blend mode '%s' in material '%s'", name.c_str(), GetName() );
|
||||
@@ -787,6 +875,10 @@ int idMaterial::NameToDstBlendMode( const idStr &name ) {
|
||||
return GLS_DSTBLEND_SRC_COLOR;
|
||||
} else if ( !name.Icmp( "GL_ONE_MINUS_SRC_COLOR" ) ) {
|
||||
return GLS_DSTBLEND_ONE_MINUS_SRC_COLOR;
|
||||
} else if ( !name.Icmp( "GL_DST_COLOR" ) ) {
|
||||
return GLS_DSTBLEND_DST_COLOR;
|
||||
} else if ( !name.Icmp( "GL_ONE_MINUS_DST_COLOR" ) ) {
|
||||
return GLS_DSTBLEND_ONE_MINUS_DST_COLOR;
|
||||
}
|
||||
|
||||
common->Warning( "unknown blend mode '%s' in material '%s'", name.c_str(), GetName() );
|
||||
@@ -903,6 +995,56 @@ void idMaterial::ParseVertexParm( idLexer &src, newShaderStage_t *newStage ) {
|
||||
newStage->vertexParms[parm][3] = ParseExpression( src );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idMaterial::ParseFragmentParm
|
||||
|
||||
Quake 4 exposes eight fragment-program parameter vectors in addition to the
|
||||
vertex parameters inherited from Doom 3.
|
||||
================
|
||||
*/
|
||||
void idMaterial::ParseFragmentParm( idLexer &src, newShaderStage_t *newStage ) {
|
||||
idToken token;
|
||||
|
||||
src.ReadTokenOnLine( &token );
|
||||
const int parm = token.GetIntValue();
|
||||
if ( !token.IsNumeric() || parm < 0 || parm >= MAX_FRAGMENT_PARMS ) {
|
||||
common->Warning( "bad fragmentParm number\n" );
|
||||
SetMaterialFlag( MF_DEFAULTED );
|
||||
return;
|
||||
}
|
||||
if ( parm >= newStage->numFragmentParms ) {
|
||||
newStage->numFragmentParms = parm + 1;
|
||||
}
|
||||
|
||||
newStage->fragmentParms[parm][0] = ParseExpression( src );
|
||||
|
||||
src.ReadTokenOnLine( &token );
|
||||
if ( !token[0] || token.Icmp( "," ) ) {
|
||||
newStage->fragmentParms[parm][1] =
|
||||
newStage->fragmentParms[parm][2] =
|
||||
newStage->fragmentParms[parm][3] = newStage->fragmentParms[parm][0];
|
||||
return;
|
||||
}
|
||||
|
||||
newStage->fragmentParms[parm][1] = ParseExpression( src );
|
||||
src.ReadTokenOnLine( &token );
|
||||
if ( !token[0] || token.Icmp( "," ) ) {
|
||||
newStage->fragmentParms[parm][2] = GetExpressionConstant( 0 );
|
||||
newStage->fragmentParms[parm][3] = GetExpressionConstant( 1 );
|
||||
return;
|
||||
}
|
||||
|
||||
newStage->fragmentParms[parm][2] = ParseExpression( src );
|
||||
src.ReadTokenOnLine( &token );
|
||||
if ( !token[0] || token.Icmp( "," ) ) {
|
||||
newStage->fragmentParms[parm][3] = GetExpressionConstant( 1 );
|
||||
return;
|
||||
}
|
||||
|
||||
newStage->fragmentParms[parm][3] = ParseExpression( src );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
@@ -1078,6 +1220,8 @@ bool idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
|
||||
int a, b;
|
||||
int matrix[2][3];
|
||||
newShaderStage_t newStage;
|
||||
rvNewShaderStage *newShaderStage = NULL;
|
||||
bool skipWarning = false;
|
||||
|
||||
if ( numStages >= MAX_SHADER_STAGES ) {
|
||||
SetMaterialFlag( MF_DEFAULTED );
|
||||
@@ -1146,11 +1290,51 @@ bool idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "reflectionRenderMap" ) ) {
|
||||
ts->dynamic = DI_REFLECTION_RENDER;
|
||||
ts->width = src.ParseInt();
|
||||
ts->height = src.ParseInt();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "refractionRenderMap" ) ) {
|
||||
ts->dynamic = DI_REFRACTION_RENDER;
|
||||
ts->width = src.ParseInt();
|
||||
ts->height = src.ParseInt();
|
||||
ts->texgen = TG_SCREEN;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "cubeRenderMap" ) ) {
|
||||
ts->dynamic = DI_CUBE_RENDER;
|
||||
ts->width = ts->height = src.ParseInt();
|
||||
ts->texgen = TG_REFLECT_CUBE;
|
||||
emptyCubeSize = ts->width;
|
||||
ts->image = globalImages->ImageFromFunction( "_emptyCubeMap", makeEmptyCubeMap );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "screen" ) ) {
|
||||
ts->texgen = TG_SCREEN;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "screen2" ) ) {
|
||||
ts->texgen = TG_SCREEN2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "glassWarp" ) ) {
|
||||
ts->texgen = TG_GLASSWARP;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The retail loader only uses this as a build-time image flag. It is
|
||||
// still a valid runtime stage option and must not default the material.
|
||||
if ( !token.Icmp( "nomips" ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "videomap" ) ) {
|
||||
// note that videomaps will always be in clamp mode, so texture
|
||||
// coordinates had better be in the 0 to 1 range
|
||||
@@ -1223,6 +1407,10 @@ bool idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
|
||||
trp = TR_CLAMP_TO_ZERO_ALPHA;
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "mirroredrepeat" ) ) {
|
||||
trp = TR_MIRRORED_REPEAT;
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "uncompressed" ) || !token.Icmp( "highquality" ) ) {
|
||||
if ( !globalImages->image_ignoreHighQuality.GetInteger() ) {
|
||||
td = TD_HIGH_QUALITY;
|
||||
@@ -1272,6 +1460,8 @@ bool idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
|
||||
texGenRegisters[0] = ParseExpression( src );
|
||||
texGenRegisters[1] = ParseExpression( src );
|
||||
texGenRegisters[2] = ParseExpression( src );
|
||||
} else if ( !token.Icmp( "potCorrection" ) ) {
|
||||
ts->texgen = TG_POT_CORRECTION;
|
||||
} else {
|
||||
common->Warning( "bad texGen '%s' in material %s", token.c_str(), GetName() );
|
||||
SetMaterialFlag( MF_DEFAULTED );
|
||||
@@ -1406,6 +1596,26 @@ bool idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
|
||||
ss->hasAlphaTest = true;
|
||||
ss->alphaTestRegister = ParseExpression( src );
|
||||
coverage = MC_PERFORATED;
|
||||
if ( !ss->hasAlphaFunc ) {
|
||||
ss->alphaTestMode = GL_GREATER;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "alphaFunc" ) ) {
|
||||
ss->hasAlphaFunc = true;
|
||||
ss->hasAlphaTest = true;
|
||||
ss->alphaTestMode = GL_GREATER;
|
||||
if ( src.ReadToken( &token ) ) {
|
||||
if ( !token.Icmp( "less" ) ) {
|
||||
ss->alphaTestMode = GL_LESS;
|
||||
} else if ( !token.Icmp( "equal" ) ) {
|
||||
ss->alphaTestMode = GL_EQUAL;
|
||||
} else if ( !token.Icmp( "greater" ) ) {
|
||||
ss->alphaTestMode = GL_GREATER;
|
||||
} else {
|
||||
common->Warning( "unknown alpha func '%s' in material '%s'", token.c_str(), GetName() );
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1464,7 +1674,47 @@ bool idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
|
||||
if ( src.ReadTokenOnLine( &token ) ) {
|
||||
newStage.vertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, token.c_str() );
|
||||
newStage.fragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, token.c_str() );
|
||||
if ( !newStage.fragmentProgram ) {
|
||||
newStage.vertexProgram = 0;
|
||||
skipWarning = true;
|
||||
}
|
||||
#if defined( _MD5R_SUPPORT ) || defined( Q4SDK_MD5R )
|
||||
idStr md5rProgram = "md5r";
|
||||
md5rProgram += token.c_str();
|
||||
newStage.md5rVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, md5rProgram.c_str() );
|
||||
#endif
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "glslProgram" ) ) {
|
||||
if ( newShaderStage != NULL ) {
|
||||
common->Warning( "ParseStage - glslProgram: Shader program already set!" );
|
||||
SetMaterialFlag( MF_DEFAULTED );
|
||||
delete newShaderStage;
|
||||
return false;
|
||||
}
|
||||
newShaderStage = new rvGLSLShaderStage();
|
||||
if ( !newShaderStage->ParseProgram( src, this ) ) {
|
||||
skipWarning = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "shaderParm" ) ) {
|
||||
if ( newShaderStage == NULL ) {
|
||||
common->Warning( "ParseStage: shaderParm set before shader type declared." );
|
||||
SetMaterialFlag( MF_DEFAULTED );
|
||||
return false;
|
||||
}
|
||||
newShaderStage->ParseShaderParm( src, this );
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "shaderTexture" ) ) {
|
||||
if ( newShaderStage == NULL ) {
|
||||
common->Warning( "ParseStage: shaderTexture set before shader type declared." );
|
||||
SetMaterialFlag( MF_DEFAULTED );
|
||||
return false;
|
||||
}
|
||||
newShaderStage->ParseTextureParm( src, this, trpDefault );
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "fragmentProgram" ) ) {
|
||||
@@ -1476,6 +1726,11 @@ bool idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
|
||||
if ( !token.Icmp( "vertexProgram" ) ) {
|
||||
if ( src.ReadTokenOnLine( &token ) ) {
|
||||
newStage.vertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, token.c_str() );
|
||||
#if defined( _MD5R_SUPPORT ) || defined( Q4SDK_MD5R )
|
||||
idStr md5rProgram = "md5r";
|
||||
md5rProgram += token.c_str();
|
||||
newStage.md5rVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, md5rProgram.c_str() );
|
||||
#endif
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -1498,6 +1753,10 @@ bool idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
|
||||
ParseVertexParm( src, &newStage );
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "fragmentParm" ) ) {
|
||||
ParseFragmentParm( src, &newStage );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "fragmentMap" ) ) {
|
||||
ParseFragmentMap( src, &newStage );
|
||||
@@ -1507,15 +1766,19 @@ bool idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
|
||||
|
||||
common->Warning( "unknown token '%s' in material '%s'", token.c_str(), GetName() );
|
||||
SetMaterialFlag( MF_DEFAULTED );
|
||||
delete newShaderStage;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// if we are using newStage, allocate a copy of it
|
||||
ss->mNumStageRegisters = numRegisters - ss->mStageRegisterStart;
|
||||
ss->mNumStageOps = numOps - ss->mStageOpsStart;
|
||||
if ( newStage.fragmentProgram || newStage.vertexProgram ) {
|
||||
ss->newStage = (newShaderStage_t *)Mem_Alloc( sizeof( newStage ) );
|
||||
*(ss->newStage) = newStage;
|
||||
}
|
||||
ss->newShaderStage = newShaderStage;
|
||||
|
||||
// successfully parsed a stage
|
||||
numStages++;
|
||||
@@ -1543,7 +1806,7 @@ bool idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
|
||||
if ( !ts->image ) {
|
||||
ts->image = globalImages->defaultImage;
|
||||
}
|
||||
} else if ( !ts->cinematic && !ts->dynamic && !ss->newStage ) {
|
||||
} else if ( !skipWarning && !ts->cinematic && !ts->dynamic && !ss->newStage && !ss->newShaderStage ) {
|
||||
common->Warning( "material '%s' had stage with no image", GetName() );
|
||||
ts->image = globalImages->defaultImage;
|
||||
}
|
||||
@@ -1783,6 +2046,39 @@ bool idMaterial::ParseMaterial( idLexer &src ) {
|
||||
desc = token.c_str();
|
||||
continue;
|
||||
}
|
||||
// default material type used by collision traces and impact effects
|
||||
else if ( !token.Icmp( "materialType" ) ) {
|
||||
src.ReadTokenOnLine( &token );
|
||||
materialType = static_cast<const rvDeclMatType *>(
|
||||
declManager->FindType( DECL_MATERIALTYPE, token.c_str(), true ) );
|
||||
continue;
|
||||
}
|
||||
// per-texel material type lookup image
|
||||
else if ( !token.Icmp( "materialImage" ) ) {
|
||||
src.ReadTokenOnLine( &token );
|
||||
materialTypeArray = MT_GetMaterialTypeArray( token.c_str(), MTAWidth, MTAHeight );
|
||||
materialTypeArrayName = token.c_str();
|
||||
continue;
|
||||
}
|
||||
else if ( !token.Icmp( "sky" ) ) {
|
||||
SetMaterialFlag( MF_SKY );
|
||||
continue;
|
||||
}
|
||||
else if ( !token.Icmp( "portalDistanceNear" ) ) {
|
||||
portalDistanceNear = src.ParseFloat();
|
||||
continue;
|
||||
}
|
||||
else if ( !token.Icmp( "portalDistanceFar" ) ) {
|
||||
portalDistanceFar = src.ParseFloat();
|
||||
continue;
|
||||
}
|
||||
else if ( !token.Icmp( "portalImage" ) ) {
|
||||
src.ReadTokenOnLine( &token );
|
||||
portalImage = globalImages->ImageFromFile( token.c_str(), TF_DEFAULT, true,
|
||||
TR_CLAMP, TD_DEFAULT, CF_2D );
|
||||
src.SkipRestOfLine();
|
||||
continue;
|
||||
}
|
||||
// check for the surface / content bit flags
|
||||
else if ( CheckSurfaceParm( &token ) ) {
|
||||
continue;
|
||||
@@ -1805,6 +2101,10 @@ bool idMaterial::ParseMaterial( idLexer &src ) {
|
||||
SetMaterialFlag( MF_NOSHADOWS );
|
||||
continue;
|
||||
}
|
||||
else if ( !token.Icmp( "needCurrentRender" ) ) {
|
||||
SetMaterialFlag( MF_NEED_CURRENT_RENDER );
|
||||
continue;
|
||||
}
|
||||
else if ( !token.Icmp( "suppressInSubview" ) ) {
|
||||
suppressInSubview = true;
|
||||
continue;
|
||||
@@ -2235,6 +2535,10 @@ bool idMaterial::Parse( const char *text, const int textLength, bool noCaching )
|
||||
}
|
||||
}
|
||||
|
||||
if ( ( portalDistanceNear < 262144.0f || portalDistanceFar < 262144.0f ) && !portalImage ) {
|
||||
portalImage = globalImages->blackImage;
|
||||
}
|
||||
|
||||
// add a tiny offset to the sort orders, so that different materials
|
||||
// that have the same sort value will at least sort consistantly, instead
|
||||
// of flickering back and forth
|
||||
@@ -2456,6 +2760,9 @@ void idMaterial::EvaluateRegisters( float *registers, const float shaderParms[MA
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_TYPE_GLSL_ENABLED:
|
||||
registers[op->c] = glConfig.GLSLProgramAvailable ? 1.0f : 0.0f;
|
||||
break;
|
||||
case OP_TYPE_GT:
|
||||
registers[op->c] = registers[ op->a ] > registers[op->b];
|
||||
break;
|
||||
|
||||
@@ -1193,7 +1193,7 @@ bool idRenderModelStatic::ConvertLWOToModelSurfaces( const struct st_lwObject *l
|
||||
}
|
||||
|
||||
if ( numTVertexes ) {
|
||||
tvList = (idVec2 *)Mem_Alloc( numTVertexes * sizeof( tvList[0] ) );
|
||||
tvList = (idVec2 *)R_StaticAlloc( numTVertexes * sizeof( tvList[0] ) );
|
||||
int offset = 0;
|
||||
for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
|
||||
if ( vm->type == LWID_('T','X','U','V') ) {
|
||||
@@ -1208,7 +1208,7 @@ bool idRenderModelStatic::ConvertLWOToModelSurfaces( const struct st_lwObject *l
|
||||
} else {
|
||||
common->Warning( "ConvertLWOToModelSurfaces: model \'%s\' has bad or missing uv data", name.c_str() );
|
||||
numTVertexes = 1;
|
||||
tvList = (idVec2 *)Mem_ClearedAlloc( numTVertexes * sizeof( tvList[0] ) );
|
||||
tvList = (idVec2 *)R_ClearedStaticAlloc( numTVertexes * sizeof( tvList[0] ) );
|
||||
}
|
||||
|
||||
// It seems like the tools our artists are using often generate
|
||||
|
||||
+103
-13
@@ -123,6 +123,9 @@ idRenderWorldLocal::idRenderWorldLocal
|
||||
idRenderWorldLocal::idRenderWorldLocal() {
|
||||
mapName.Clear();
|
||||
mapTimeStamp = FILE_NOT_FOUND_TIMESTAMP;
|
||||
m_filename.Clear();
|
||||
m_CRC = 0;
|
||||
procFileVersion = 0;
|
||||
|
||||
generateAllInteractionsCalled = false;
|
||||
|
||||
@@ -541,7 +544,9 @@ void idRenderWorldLocal::FreeEffectDefDerivedData( rvRenderEffectLocal *def ) {
|
||||
}
|
||||
def->effectRefs = NULL;
|
||||
def->viewEffect = NULL;
|
||||
delete def->dynamicModel;
|
||||
def->dynamicModel = NULL;
|
||||
def->dynamicModelFrameCount = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -569,24 +574,109 @@ void idRenderWorldLocal::AddEffectRefToArea( rvRenderEffectLocal *def, portalAre
|
||||
|
||||
/*
|
||||
==================
|
||||
PushEffectDef
|
||||
PushPolytopeIntoTree_r
|
||||
|
||||
Place the effect in every portal area touched by its transformed current
|
||||
bounds. Retail performs the same placement through the proc BSP.
|
||||
Places an oriented polytope in each portal area it touches. Quake 4 uses
|
||||
this path for effects because their accumulated bounds may be much larger
|
||||
than the axis-aligned bounds accepted by BoundsInAreas.
|
||||
==================
|
||||
*/
|
||||
void idRenderWorldLocal::PushEffectDef( rvRenderEffectLocal *def ) {
|
||||
if ( def == NULL || def->referenceBounds.IsCleared() || areaNodes == NULL ) {
|
||||
void idRenderWorldLocal::PushPolytopeIntoTree_r( idRenderEntityLocal *def,
|
||||
idRenderLightLocal *light, rvRenderEffectLocal *effect,
|
||||
const idBox &box, const idVec3 *points, int numPoints, int nodeNum ) {
|
||||
if ( nodeNum < 0 ) {
|
||||
portalArea_t *area = &portalAreas[-1 - nodeNum];
|
||||
if ( area->viewCount == tr.viewCount ) {
|
||||
return;
|
||||
}
|
||||
area->viewCount = tr.viewCount;
|
||||
if ( def != NULL ) {
|
||||
AddEntityRefToArea( def, area );
|
||||
}
|
||||
if ( light != NULL ) {
|
||||
AddLightRefToArea( light, area );
|
||||
}
|
||||
if ( effect != NULL ) {
|
||||
AddEffectRefToArea( effect, area );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
idBounds worldBounds;
|
||||
worldBounds.FromTransformedBounds( def->referenceBounds, def->parms.origin, def->parms.axis );
|
||||
int areas[128];
|
||||
const int numAreas = BoundsInAreas( worldBounds, areas, sizeof( areas ) / sizeof( areas[0] ) );
|
||||
for ( int i = 0; i < numAreas; ++i ) {
|
||||
AddEffectRefToArea( def, &portalAreas[areas[i]] );
|
||||
areaNode_t *node = &areaNodes[nodeNum];
|
||||
if ( r_useNodeCommonChildren.GetBool() &&
|
||||
node->commonChildrenArea != CHILDREN_HAVE_MULTIPLE_AREAS &&
|
||||
portalAreas[node->commonChildrenArea].viewCount == tr.viewCount ) {
|
||||
return;
|
||||
}
|
||||
|
||||
int side = box.PlaneSide( node->plane, 0.0f );
|
||||
bool front = side != PLANESIDE_BACK;
|
||||
bool back = side != PLANESIDE_FRONT;
|
||||
|
||||
// The oriented box is deliberately conservative. When actual points
|
||||
// are supplied, retail tightens a crossing result with exact tests.
|
||||
if ( side == PLANESIDE_CROSS && points != NULL && numPoints > 0 ) {
|
||||
front = false;
|
||||
back = false;
|
||||
for ( int i = 0; i < numPoints; ++i ) {
|
||||
const float distance = node->plane.Distance( points[i] );
|
||||
if ( distance > 0.0f ) {
|
||||
front = true;
|
||||
} else if ( distance < 0.0f ) {
|
||||
back = true;
|
||||
}
|
||||
if ( front && back ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( front && node->children[0] != 0 ) {
|
||||
PushPolytopeIntoTree_r( def, light, effect, box, points, numPoints, node->children[0] );
|
||||
}
|
||||
if ( back && node->children[1] != 0 ) {
|
||||
PushPolytopeIntoTree_r( def, light, effect, box, points, numPoints, node->children[1] );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PushPolytopeIntoTree
|
||||
==================
|
||||
*/
|
||||
void idRenderWorldLocal::PushPolytopeIntoTree( idRenderEntityLocal *def,
|
||||
idRenderLightLocal *light, rvRenderEffectLocal *effect,
|
||||
const idBox &box, const idVec3 *points, int numPoints ) {
|
||||
if ( areaNodes != NULL ) {
|
||||
PushPolytopeIntoTree_r( def, light, effect, box, points, numPoints, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PushEffectDef
|
||||
==================
|
||||
*/
|
||||
void idRenderWorldLocal::PushEffectDef( int effectHandle ) {
|
||||
if ( effectHandle < 0 || effectHandle >= effectDefs.Num() ) {
|
||||
common->Printf( "idRenderWorld::PushEffectDef: invalid handle %i >= %i\n",
|
||||
effectHandle, effectDefs.Num() );
|
||||
return;
|
||||
}
|
||||
|
||||
rvRenderEffectLocal *def = effectDefs[effectHandle];
|
||||
if ( def == NULL ) {
|
||||
common->Printf( "idRenderWorld::PushEffectDef: handle %i [0, %i] is NULL\n",
|
||||
effectHandle, effectDefs.Num() );
|
||||
return;
|
||||
}
|
||||
|
||||
R_AxisToModelMatrix( def->parms.axis, def->parms.origin, def->modelMatrix );
|
||||
def->lastModifiedFrameNum = tr.frameCount;
|
||||
++tr.viewCount;
|
||||
|
||||
const idBox box( def->referenceBounds, def->parms.origin, def->parms.axis );
|
||||
PushPolytopeIntoTree( NULL, NULL, def, box, NULL, 0 );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -637,7 +727,7 @@ bool idRenderWorldLocal::UpdateEffectDef( qhandle_t effectHandle, const renderEf
|
||||
return true;
|
||||
}
|
||||
if ( push ) {
|
||||
PushEffectDef( def );
|
||||
PushEffectDef( effectHandle );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -681,7 +771,7 @@ void idRenderWorldLocal::PushMarkedDefs() {
|
||||
for ( int i = 0; i < markedEffectDefs.Num(); ++i ) {
|
||||
const int handle = markedEffectDefs[i];
|
||||
if ( handle >= 0 && handle < effectDefs.Num() && effectDefs[handle] != NULL ) {
|
||||
PushEffectDef( effectDefs[handle] );
|
||||
PushEffectDef( handle );
|
||||
}
|
||||
}
|
||||
ClearMarkedDefs();
|
||||
|
||||
@@ -130,10 +130,17 @@ idRenderModel *idRenderWorldLocal::ParseModel( idLexer *src ) {
|
||||
model->InitEmpty( token );
|
||||
|
||||
int numSurfaces = src->ParseInt();
|
||||
common->Printf( "Q4 proc trace: model %s surfaces=%d version=%u\n", token.c_str(), numSurfaces, procFileVersion );
|
||||
if ( numSurfaces < 0 ) {
|
||||
src->Error( "R_ParseModel: bad numSurfaces" );
|
||||
}
|
||||
|
||||
// PROC v2+ stores a per-area portal-sky flag after the surface count.
|
||||
// Only static world models (_areaN) carry this field.
|
||||
if ( procFileVersion > 1 && model->IsStaticWorldModel() ) {
|
||||
model->SetHasSky( src->ParseBool() );
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < numSurfaces ; i++ ) {
|
||||
src->ExpectTokenString( "{" );
|
||||
|
||||
@@ -151,9 +158,12 @@ idRenderModel *idRenderWorldLocal::ParseModel( idLexer *src ) {
|
||||
|
||||
R_AllocStaticTriSurfVerts( tri, tri->numVerts );
|
||||
for ( j = 0 ; j < tri->numVerts ; j++ ) {
|
||||
float vec[8];
|
||||
float vec[12];
|
||||
|
||||
src->Parse1DMatrix( 8, vec );
|
||||
const int valuesRead = src->Parse1DMatrixOpenEnded( 12, vec );
|
||||
if ( valuesRead != 8 && valuesRead != 12 ) {
|
||||
src->Error( "R_ParseModel: bad vertex read" );
|
||||
}
|
||||
|
||||
tri->verts[j].xyz[0] = vec[0];
|
||||
tri->verts[j].xyz[1] = vec[1];
|
||||
@@ -163,6 +173,14 @@ idRenderModel *idRenderWorldLocal::ParseModel( idLexer *src ) {
|
||||
tri->verts[j].normal[0] = vec[5];
|
||||
tri->verts[j].normal[1] = vec[6];
|
||||
tri->verts[j].normal[2] = vec[7];
|
||||
if ( valuesRead == 12 ) {
|
||||
tri->verts[j].color[0] = static_cast<byte>( vec[8] );
|
||||
tri->verts[j].color[1] = static_cast<byte>( vec[9] );
|
||||
tri->verts[j].color[2] = static_cast<byte>( vec[10] );
|
||||
tri->verts[j].color[3] = static_cast<byte>( vec[11] );
|
||||
} else {
|
||||
*reinterpret_cast<unsigned int *>( tri->verts[j].color ) = 0xFF000000u;
|
||||
}
|
||||
}
|
||||
|
||||
R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
|
||||
@@ -178,6 +196,7 @@ idRenderModel *idRenderWorldLocal::ParseModel( idLexer *src ) {
|
||||
src->ExpectTokenString( "}" );
|
||||
|
||||
model->FinishSurfaces();
|
||||
common->Printf( "Q4 proc trace: finished model %s\n", model->Name() );
|
||||
|
||||
return model;
|
||||
}
|
||||
@@ -201,6 +220,7 @@ idRenderModel *idRenderWorldLocal::ParseShadowModel( idLexer *src ) {
|
||||
|
||||
model = renderModelManager->AllocModel();
|
||||
model->InitEmpty( token );
|
||||
common->Printf( "Q4 proc trace: shadow model %s\n", token.c_str() );
|
||||
|
||||
surf.shader = tr.defaultMaterial;
|
||||
|
||||
@@ -239,6 +259,7 @@ idRenderModel *idRenderWorldLocal::ParseShadowModel( idLexer *src ) {
|
||||
|
||||
// we do NOT do a model->FinishSurfaceces, because we don't need sil edges, planes, tangents, etc.
|
||||
// model->FinishSurfaces();
|
||||
common->Printf( "Q4 proc trace: finished shadow model %s\n", model->Name() );
|
||||
|
||||
return model;
|
||||
}
|
||||
@@ -537,6 +558,7 @@ bool idRenderWorldLocal::InitFromMap( const char *name ) {
|
||||
|
||||
mapName = name;
|
||||
mapTimeStamp = currentTimeStamp;
|
||||
m_filename = filename;
|
||||
|
||||
// if we are writing a demo, archive the load command
|
||||
if ( session->writeDemo ) {
|
||||
@@ -549,6 +571,21 @@ bool idRenderWorldLocal::InitFromMap( const char *name ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Quake 4 PROC files carry a quoted format version followed by the source
|
||||
// map CRC before any of the Doom 3-style top-level blocks.
|
||||
if ( !src->ReadToken( &token ) ) {
|
||||
common->Warning( "%s is missing version", filename.c_str() );
|
||||
delete src;
|
||||
return false;
|
||||
}
|
||||
procFileVersion = static_cast<unsigned int>( atol( token.c_str() ) );
|
||||
if ( !src->ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) ) {
|
||||
common->Warning( "%s has no map file CRC", filename.c_str() );
|
||||
delete src;
|
||||
return false;
|
||||
}
|
||||
m_CRC = static_cast<unsigned int>( token.GetIntValue() );
|
||||
|
||||
// parse the file
|
||||
while ( 1 ) {
|
||||
if ( !src->ReadToken( &token ) ) {
|
||||
@@ -662,17 +699,6 @@ void idRenderWorldLocal::AddWorldModelEntities() {
|
||||
common->Error( "idRenderWorldLocal::InitFromMap: bad area model lookup" );
|
||||
}
|
||||
|
||||
idRenderModel *hModel = def->parms.hModel;
|
||||
|
||||
for ( int j = 0; j < hModel->NumSurfaces(); j++ ) {
|
||||
const modelSurface_t *surf = hModel->Surface( j );
|
||||
|
||||
if ( surf->shader->GetName() == idStr( "textures/smf/portal_sky" ) ) {
|
||||
def->needsPortalSky = true;
|
||||
portalAreas[i].hasSkybox = true;
|
||||
}
|
||||
}
|
||||
|
||||
def->referenceBounds = def->parms.hModel->Bounds();
|
||||
|
||||
def->parms.axis[0][0] = 1;
|
||||
@@ -688,6 +714,8 @@ void idRenderWorldLocal::AddWorldModelEntities() {
|
||||
def->parms.shaderParms[2] =
|
||||
def->parms.shaderParms[3] = 1;
|
||||
|
||||
portalAreas[i].hasSkybox = def->parms.hModel->GetHasSky();
|
||||
|
||||
AddEntityRefToArea( def, &portalAreas[i] );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,6 +754,46 @@ void idRenderWorldLocal::AddAreaLightRefs( int areaNum, const portalStack_t *ps
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
AddAreaEffectRefs
|
||||
|
||||
This is the point where BSE effect definitions visible through a portal chain
|
||||
are added to the current view.
|
||||
===================
|
||||
*/
|
||||
void idRenderWorldLocal::AddAreaEffectRefs( int areaNum, const portalStack_t *ps ) {
|
||||
portalArea_t *area = &portalAreas[areaNum];
|
||||
|
||||
for ( areaReference_t *ref = area->effectRefs.areaNext;
|
||||
ref != &area->effectRefs; ref = ref->areaNext ) {
|
||||
rvRenderEffectLocal *effect = ref->effect;
|
||||
if ( effect == NULL ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !r_skipSuppress.GetBool() ) {
|
||||
if ( effect->parms.suppressSurfaceInViewID != 0 &&
|
||||
effect->parms.suppressSurfaceInViewID == tr.viewDef->renderView.viewID ) {
|
||||
continue;
|
||||
}
|
||||
if ( effect->parms.allowSurfaceInViewID != 0 &&
|
||||
effect->parms.allowSurfaceInViewID != tr.viewDef->renderView.viewID ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( r_useEntityCulling.GetBool() &&
|
||||
R_CullLocalBox( effect->referenceBounds, effect->modelMatrix,
|
||||
ps->numPortalPlanes, ps->portalPlanes ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
viewEffect_t *viewEffect = R_SetEffectDefViewEntity( effect );
|
||||
viewEffect->scissorRect.Union( ps->rect );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
AddAreaRefs
|
||||
@@ -770,6 +810,7 @@ void idRenderWorldLocal::AddAreaRefs( int areaNum, const portalStack_t *ps ) {
|
||||
// add the models and lights, using more precise culling to the planes
|
||||
AddAreaEntityRefs( areaNum, ps );
|
||||
AddAreaLightRefs( areaNum, ps );
|
||||
AddAreaEffectRefs( areaNum, ps );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -837,6 +878,7 @@ void idRenderWorldLocal::FindViewLightsAndEntities( void ) {
|
||||
// clear the visible lightDef and entityDef lists
|
||||
tr.viewDef->viewLights = NULL;
|
||||
tr.viewDef->viewEntitys = NULL;
|
||||
tr.viewDef->viewEffects = NULL;
|
||||
|
||||
// find the area to start the portal flooding in
|
||||
if ( !r_usePortals.GetBool() ) {
|
||||
|
||||
@@ -396,6 +396,12 @@ void GL_State( int stateBits ) {
|
||||
case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA:
|
||||
dstFactor = GL_ONE_MINUS_DST_ALPHA;
|
||||
break;
|
||||
case GLS_DSTBLEND_DST_COLOR:
|
||||
dstFactor = GL_DST_COLOR;
|
||||
break;
|
||||
case GLS_DSTBLEND_ONE_MINUS_DST_COLOR:
|
||||
dstFactor = GL_ONE_MINUS_DST_COLOR;
|
||||
break;
|
||||
default:
|
||||
dstFactor = GL_ONE; // to get warning to shut up
|
||||
common->Error( "GL_State: invalid dst blend state bits\n" );
|
||||
|
||||
@@ -26,6 +26,7 @@ along with Quake 4 Reconstructed Source Code. If not, see <http://www.gnu.org/l
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
#include "../bse/BSE.h"
|
||||
|
||||
static const float CHECK_BOUNDS_EPSILON = 1.0f;
|
||||
|
||||
@@ -408,6 +409,70 @@ viewEntity_t *R_SetEntityDefViewEntity( idRenderEntityLocal *def ) {
|
||||
return vModel;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
R_SetEffectDefViewEntity
|
||||
|
||||
Create the frame-local view record for a BSE effect. The scissor is expanded
|
||||
later by each visible portal chain that references the effect.
|
||||
=============
|
||||
*/
|
||||
viewEffect_t *R_SetEffectDefViewEntity( rvRenderEffectLocal *def ) {
|
||||
if ( def->viewCount == tr.viewCount ) {
|
||||
return def->viewEffect;
|
||||
}
|
||||
def->viewCount = tr.viewCount;
|
||||
|
||||
viewEffect_t *viewEffect = static_cast<viewEffect_t *>( R_ClearedFrameAlloc( sizeof( *viewEffect ) ) );
|
||||
viewEffect->effectDef = def;
|
||||
viewEffect->scissorRect.Clear();
|
||||
viewEffect->modelDepthHack = def->parms.modelDepthHack;
|
||||
viewEffect->weaponDepthHackInViewID = def->parms.weaponDepthHackInViewID;
|
||||
|
||||
R_AxisToModelMatrix( def->parms.axis, def->parms.origin, viewEffect->modelMatrix );
|
||||
if ( tr.viewDef != NULL ) {
|
||||
myGlMultMatrix( viewEffect->modelMatrix, tr.viewDef->worldSpace.modelViewMatrix,
|
||||
viewEffect->modelViewMatrix );
|
||||
viewEffect->distanceToCamera = ( def->parms.origin - tr.viewDef->renderView.vieworg ).LengthSqr();
|
||||
viewEffect->next = tr.viewDef->viewEffects;
|
||||
tr.viewDef->viewEffects = viewEffect;
|
||||
}
|
||||
|
||||
def->viewEffect = viewEffect;
|
||||
return viewEffect;
|
||||
}
|
||||
|
||||
static int R_CompareViewEffects( const void *left, const void *right ) {
|
||||
const viewEffect_t *a = *static_cast<viewEffect_t * const *>( left );
|
||||
const viewEffect_t *b = *static_cast<viewEffect_t * const *>( right );
|
||||
if ( a->distanceToCamera < b->distanceToCamera ) {
|
||||
return 1;
|
||||
}
|
||||
if ( a->distanceToCamera > b->distanceToCamera ) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int R_SortViewEffects( viewEffect_t ***array ) {
|
||||
int count = 0;
|
||||
for ( viewEffect_t *effect = tr.viewDef->viewEffects; effect != NULL; effect = effect->next ) {
|
||||
++count;
|
||||
}
|
||||
if ( count == 0 ) {
|
||||
*array = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*array = static_cast<viewEffect_t **>( R_FrameAlloc( count * sizeof( (*array)[0] ) ) );
|
||||
int index = 0;
|
||||
for ( viewEffect_t *effect = tr.viewDef->viewEffects; effect != NULL; effect = effect->next ) {
|
||||
(*array)[index++] = effect;
|
||||
}
|
||||
qsort( *array, count, sizeof( (*array)[0] ), R_CompareViewEffects );
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
R_TestPointInViewLight
|
||||
@@ -1174,6 +1239,46 @@ idRenderModel *R_EntityDefDynamicModel( idRenderEntityLocal *def ) {
|
||||
return def->dynamicModel;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
R_EffectDefDynamicModel
|
||||
|
||||
Build the transient render model emitted by a serviced BSE effect. Retail
|
||||
rebuilds continuous effect geometry once per render frame.
|
||||
===================
|
||||
*/
|
||||
static idRenderModel *R_EffectDefDynamicModel( rvRenderEffectLocal *def ) {
|
||||
if ( tr.viewDef == NULL || def == NULL || def->effect == NULL || bse == NULL ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *effectName = def->parms.declEffect ? def->parms.declEffect->GetName() : "";
|
||||
if ( bse->Filtered( effectName, EC_IGNORE ) ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( def->dynamicModelFrameCount != tr.frameCount ) {
|
||||
delete def->dynamicModel;
|
||||
def->dynamicModel = def->effect->Render( &def->parms, tr.viewDef );
|
||||
def->dynamicModelFrameCount = tr.frameCount;
|
||||
}
|
||||
|
||||
if ( def->dynamicModel != NULL ) {
|
||||
const float depthHack = def->dynamicModel->DepthHack();
|
||||
if ( depthHack != 0.0f ) {
|
||||
idPlane eye;
|
||||
idPlane clip;
|
||||
idVec3 ndc;
|
||||
R_TransformModelToClip( def->parms.origin, tr.viewDef->worldSpace.modelViewMatrix,
|
||||
tr.viewDef->projectionMatrix, eye, clip );
|
||||
R_TransformClipToDevice( clip, tr.viewDef, ndc );
|
||||
def->parms.modelDepthHack = depthHack * ( 1.0f - ndc.z );
|
||||
}
|
||||
}
|
||||
|
||||
return def->dynamicModel;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_AddDrawSurf
|
||||
@@ -1296,6 +1401,57 @@ void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const
|
||||
// adds for this view
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_AddDrawSurf
|
||||
|
||||
BSE overload. Effect geometry uses renderEffect shader parameters and the
|
||||
viewEffect matrix prefix while sharing the normal draw-surface back end.
|
||||
=================
|
||||
*/
|
||||
void R_AddDrawSurf( const srfTriangles_t *tri, const viewEffect_t *space, const renderEffect_t *renderEffect,
|
||||
const idMaterial *shader, const idScreenRect &scissor, unsigned int flags ) {
|
||||
drawSurf_t *drawSurf = static_cast<drawSurf_t *>( R_ClearedFrameAlloc( sizeof( *drawSurf ) ) );
|
||||
drawSurf->geo = tri;
|
||||
drawSurf->space = reinterpret_cast<const viewEntity_t *>( space );
|
||||
drawSurf->material = shader;
|
||||
drawSurf->scissorRect = scissor;
|
||||
drawSurf->sort = shader->GetSort() + tr.sortOffset;
|
||||
drawSurf->mFlags = flags;
|
||||
|
||||
tr.sortOffset += 0.000001f;
|
||||
|
||||
if ( tr.viewDef->numDrawSurfs == tr.viewDef->maxDrawSurfs ) {
|
||||
drawSurf_t **old = tr.viewDef->drawSurfs;
|
||||
int copyBytes;
|
||||
if ( tr.viewDef->maxDrawSurfs == 0 ) {
|
||||
tr.viewDef->maxDrawSurfs = INITIAL_DRAWSURFS;
|
||||
copyBytes = 0;
|
||||
} else {
|
||||
copyBytes = tr.viewDef->maxDrawSurfs * sizeof( tr.viewDef->drawSurfs[0] );
|
||||
tr.viewDef->maxDrawSurfs *= 2;
|
||||
}
|
||||
tr.viewDef->drawSurfs = static_cast<drawSurf_t **>(
|
||||
R_FrameAlloc( tr.viewDef->maxDrawSurfs * sizeof( tr.viewDef->drawSurfs[0] ) ) );
|
||||
if ( copyBytes > 0 ) {
|
||||
memcpy( tr.viewDef->drawSurfs, old, copyBytes );
|
||||
}
|
||||
}
|
||||
tr.viewDef->drawSurfs[tr.viewDef->numDrawSurfs++] = drawSurf;
|
||||
|
||||
const float *constantRegisters = shader->ConstantRegisters();
|
||||
if ( constantRegisters != NULL ) {
|
||||
drawSurf->shaderRegisters = constantRegisters;
|
||||
} else {
|
||||
float *registers = static_cast<float *>(
|
||||
R_FrameAlloc( shader->GetNumRegisters() * sizeof( registers[0] ) ) );
|
||||
drawSurf->shaderRegisters = registers;
|
||||
shader->EvaluateRegisters( registers, renderEffect->shaderParms, tr.viewDef, 0 );
|
||||
}
|
||||
|
||||
R_DeformDrawSurf( drawSurf );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_AddAmbientDrawsurfs
|
||||
@@ -1405,6 +1561,57 @@ static void R_AddAmbientDrawsurfs( viewEntity_t *vEntity ) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_AddAmbientEffectDrawsurfs
|
||||
|
||||
Submit every visible surface generated by a BSE effect model.
|
||||
===============
|
||||
*/
|
||||
static void R_AddAmbientEffectDrawsurfs( viewEffect_t *viewEffect ) {
|
||||
rvRenderEffectLocal *def = viewEffect->effectDef;
|
||||
idRenderModel *model = def->dynamicModel;
|
||||
if ( model == NULL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int total = model->NumSurfaces();
|
||||
for ( int i = 0; i < total; ++i ) {
|
||||
const modelSurface_t *surface = model->Surface( i );
|
||||
if ( surface == NULL || surface->geometry == NULL || surface->geometry->numIndexes == 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
srfTriangles_t *triangles = surface->geometry;
|
||||
const idMaterial *shader = surface->shader;
|
||||
if ( shader == NULL || !shader->IsDrawn() ) {
|
||||
continue;
|
||||
}
|
||||
if ( R_CullLocalBox( triangles->bounds, viewEffect->modelMatrix, 5, tr.viewDef->frustum ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
def->visibleCount = tr.viewCount;
|
||||
if ( triangles->primBatchMesh == NULL ) {
|
||||
if ( !R_CreateAmbientCache( triangles, false ) ) {
|
||||
return;
|
||||
}
|
||||
vertexCache.Touch( triangles->ambientCache );
|
||||
|
||||
if ( r_useIndexBuffers.GetBool() && triangles->indexCache == NULL ) {
|
||||
vertexCache.Alloc( triangles->indexes,
|
||||
triangles->numIndexes * sizeof( triangles->indexes[0] ), &triangles->indexCache, true );
|
||||
}
|
||||
if ( triangles->indexCache != NULL ) {
|
||||
vertexCache.Touch( triangles->indexCache );
|
||||
}
|
||||
}
|
||||
|
||||
R_AddDrawSurf( triangles, viewEffect, &def->parms, shader, viewEffect->scissorRect );
|
||||
triangles->ambientViewCount = tr.viewCount;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
R_CalcEntityScissorRectangle
|
||||
@@ -1483,6 +1690,30 @@ void R_AddModelSurfaces( void ) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
R_AddEffectSurfaces
|
||||
|
||||
Effects are rendered back-to-front, matching the retail view-effect sort.
|
||||
===================
|
||||
*/
|
||||
void R_AddEffectSurfaces( void ) {
|
||||
viewEffect_t **effects = NULL;
|
||||
const int count = R_SortViewEffects( &effects );
|
||||
for ( int i = 0; i < count; ++i ) {
|
||||
viewEffect_t *viewEffect = effects[i];
|
||||
if ( viewEffect->scissorRect.IsEmpty() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
idRenderModel *model = R_EffectDefDynamicModel( viewEffect->effectDef );
|
||||
if ( model == NULL || model->NumSurfaces() <= 0 ) {
|
||||
continue;
|
||||
}
|
||||
R_AddAmbientEffectDrawsurfs( viewEffect );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
R_RemoveUnecessaryViewLights
|
||||
|
||||
@@ -405,6 +405,21 @@ typedef struct viewEntity_s {
|
||||
} viewEntity_t;
|
||||
|
||||
|
||||
// A BSE effect uses the same matrix prefix as a viewEntity so its generated
|
||||
// surfaces can travel through the existing draw-surface back end. The retail
|
||||
// PDB records this structure as 164 bytes in the 32-bit build.
|
||||
typedef struct viewEffect_s {
|
||||
struct viewEffect_s * next;
|
||||
rvRenderEffectLocal * effectDef;
|
||||
idScreenRect scissorRect;
|
||||
int weaponDepthHackInViewID;
|
||||
float modelDepthHack;
|
||||
float modelMatrix[16];
|
||||
float modelViewMatrix[16];
|
||||
float distanceToCamera;
|
||||
} viewEffect_t;
|
||||
|
||||
|
||||
const int MAX_CLIP_PLANES = 1; // we may expand this to six for some subview issues
|
||||
|
||||
// viewDefs are allocated on the frame temporary stack memory
|
||||
@@ -456,6 +471,7 @@ typedef struct viewDef_s {
|
||||
|
||||
struct viewLight_s *viewLights; // chain of all viewLights effecting view
|
||||
struct viewEntity_s *viewEntitys; // chain of all viewEntities effecting view, including off screen ones casting shadows
|
||||
struct viewEffect_s *viewEffects; // chain of visible Quake 4 BSE effects
|
||||
// we use viewEntities as a check to see if a given view consists solely
|
||||
// of 2D rendering, which we can optimize in certain ways. A 2D view will
|
||||
// not have any viewEntities
|
||||
@@ -1138,6 +1154,8 @@ const int GLS_DSTBLEND_SRC_ALPHA = 0x00000050;
|
||||
const int GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA = 0x00000060;
|
||||
const int GLS_DSTBLEND_DST_ALPHA = 0x00000070;
|
||||
const int GLS_DSTBLEND_ONE_MINUS_DST_ALPHA = 0x00000080;
|
||||
const int GLS_DSTBLEND_DST_COLOR = 0x00000090;
|
||||
const int GLS_DSTBLEND_ONE_MINUS_DST_COLOR = 0x000000a0;
|
||||
const int GLS_DSTBLEND_BITS = 0x000000f0;
|
||||
|
||||
|
||||
@@ -1293,10 +1311,13 @@ bool R_IssueEntityDefCallback( idRenderEntityLocal *def );
|
||||
idRenderModel *R_EntityDefDynamicModel( idRenderEntityLocal *def );
|
||||
|
||||
viewEntity_t *R_SetEntityDefViewEntity( idRenderEntityLocal *def );
|
||||
viewEffect_t *R_SetEffectDefViewEntity( rvRenderEffectLocal *def );
|
||||
viewLight_t *R_SetLightDefViewLight( idRenderLightLocal *def );
|
||||
|
||||
void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const renderEntity_t *renderEntity,
|
||||
const idMaterial *shader, const idScreenRect &scissor, unsigned int flags = 0 );
|
||||
void R_AddDrawSurf( const srfTriangles_t *tri, const viewEffect_t *space, const renderEffect_t *renderEffect,
|
||||
const idMaterial *shader, const idScreenRect &scissor, unsigned int flags = 0 );
|
||||
|
||||
void R_LinkLightSurf( const drawSurf_t **link, const srfTriangles_t *tri, const viewEntity_t *space,
|
||||
const idRenderLightLocal *light, const idMaterial *shader, const idScreenRect &scissor, bool viewInsideShadow );
|
||||
@@ -1323,6 +1344,7 @@ void R_SetLightProject( idPlane lightProject[4], const idVec3 origin, const idVe
|
||||
|
||||
void R_AddLightSurfaces( void );
|
||||
void R_AddModelSurfaces( void );
|
||||
void R_AddEffectSurfaces( void );
|
||||
void R_RemoveUnecessaryViewLights( void );
|
||||
|
||||
void R_FreeDerivedData( void );
|
||||
|
||||
@@ -305,7 +305,9 @@ void *R_StaticAlloc( int bytes ) {
|
||||
|
||||
tr.staticAllocCount += bytes;
|
||||
|
||||
buf = Mem_Alloc( bytes );
|
||||
// Quake 4's renderer static allocations are 16-byte aligned. A number of
|
||||
// the retail SSE/SSE2 paths use aligned loads directly from these blocks.
|
||||
buf = Mem_Alloc16( bytes, MA_RENDER );
|
||||
|
||||
// don't exit on failure on zero length allocations since the old code didn't
|
||||
if ( !buf && ( bytes != 0 ) ) {
|
||||
@@ -334,7 +336,7 @@ R_StaticFree
|
||||
*/
|
||||
void R_StaticFree( void *data ) {
|
||||
tr.pc.c_free++;
|
||||
Mem_Free( data );
|
||||
Mem_Free16( data );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1111,6 +1113,9 @@ void R_RenderView( viewDef_t *parms ) {
|
||||
// lists
|
||||
R_AddModelSurfaces();
|
||||
|
||||
// instantiate and add the transient geometry generated by visible BSE effects
|
||||
R_AddEffectSurfaces();
|
||||
|
||||
// any viewLight that didn't have visible surfaces can have it's shadows removed
|
||||
R_RemoveUnecessaryViewLights();
|
||||
|
||||
|
||||
+80
-27
@@ -49,7 +49,7 @@ void idSoundShader::Init() {
|
||||
errorDuringParse = false;
|
||||
noShakes = false;
|
||||
frequentlyUsed = false;
|
||||
leadinVolume = 0.0f;
|
||||
leadinVolume = 1.0f;
|
||||
memset( leadins, 0, sizeof( leadins ) );
|
||||
numLeadins = 0;
|
||||
memset( entries, 0, sizeof( entries ) );
|
||||
@@ -70,7 +70,7 @@ void idSoundShader::FreeData() {
|
||||
shakes.Clear();
|
||||
}
|
||||
|
||||
const char *idSoundShader::DefaultDefinition() const { return "{\n\t_default.wav\n}"; }
|
||||
const char *idSoundShader::DefaultDefinition() const { return "{\n\tsound/_default.wav\n}"; }
|
||||
|
||||
bool idSoundShader::SetDefaultText() {
|
||||
idStr wavName = GetName();
|
||||
@@ -106,29 +106,79 @@ bool idSoundShader::Parse( const char *text, int textLength, bool noCaching ) {
|
||||
|
||||
bool idSoundShader::ParseShader( idLexer &src ) {
|
||||
idToken token;
|
||||
int maxSamples = idSoundSystemLocal::s_maxSoundsPerShader.GetInteger();
|
||||
if ( maxSamples <= 0 || maxSamples > SOUND_MAX_LIST_WAVS ) maxSamples = SOUND_MAX_LIST_WAVS;
|
||||
|
||||
while ( src.ReadToken( &token ) ) {
|
||||
if ( token == "}" ) return true;
|
||||
memset( &parms, 0, sizeof( parms ) );
|
||||
parms.minDistance = 40.0f;
|
||||
parms.maxDistance = 400.0f;
|
||||
parms.volume = 1.0f;
|
||||
parms.frequencyShift = 1.0f;
|
||||
minFrequencyShift = 1.0f;
|
||||
maxFrequencyShift = 1.0f;
|
||||
altSound = NULL;
|
||||
memset( leadins, 0, sizeof( leadins ) );
|
||||
memset( entries, 0, sizeof( entries ) );
|
||||
numLeadins = 0;
|
||||
numEntries = 0;
|
||||
|
||||
int maxSamples = idSoundSystemLocal::s_maxSoundsPerShader.GetInteger();
|
||||
if ( com_makingBuild.GetBool() || maxSamples <= 0 || maxSamples > SOUND_MAX_LIST_WAVS ) {
|
||||
maxSamples = SOUND_MAX_LIST_WAVS;
|
||||
}
|
||||
|
||||
while ( src.ExpectAnyToken( &token ) ) {
|
||||
if ( token == "}" ) {
|
||||
if ( !soundSystem->GetInsideLevelLoad() ) {
|
||||
soundSystem->ValidateSoundShader( this );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if ( !token.Icmp( "minSamples" ) ) {
|
||||
maxSamples = idMath::ClampInt( src.ParseInt(), SOUND_MAX_LIST_WAVS, maxSamples );
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "frequencyshift" ) ) {
|
||||
minFrequencyShift = src.ParseFloat();
|
||||
if ( !src.ExpectTokenString( "," ) ) {
|
||||
src.FreeSource();
|
||||
return false;
|
||||
}
|
||||
maxFrequencyShift = src.ParseFloat();
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "description" ) ) { if ( src.ReadTokenOnLine( &token ) ) desc = token; continue; }
|
||||
if ( !token.Icmp( "mindistance" ) ) { parms.minDistance = src.ParseFloat(); continue; }
|
||||
if ( !token.Icmp( "maxdistance" ) ) { parms.maxDistance = src.ParseFloat(); continue; }
|
||||
if ( !token.Icmp( "volume" ) ) { parms.volume = src.ParseFloat(); continue; }
|
||||
if ( !token.Icmp( "attenuatedVolume" ) ) { parms.attenuatedVolume = src.ParseFloat(); continue; }
|
||||
if ( !token.Icmp( "volumeDb" ) ) {
|
||||
float volumeDb = src.ParseFloat();
|
||||
if ( volumeDb > 10.0f ) {
|
||||
common->Warning( "Clamping volume of '%s' to +10dB (3 times louder)", GetName() );
|
||||
volumeDb = 10.0f;
|
||||
}
|
||||
parms.volume = idMath::dBToScale( volumeDb );
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "leadinVolume" ) ) { leadinVolume = src.ParseFloat(); continue; }
|
||||
if ( !token.Icmp( "soundClass" ) ) { parms.soundClass = idMath::ClampInt( 0, SOUND_MAX_CLASSES - 1, src.ParseInt() ); continue; }
|
||||
if ( !token.Icmp( "frequencyShift" ) ) { parms.frequencyShift = src.ParseFloat(); continue; }
|
||||
if ( !token.Icmp( "minFrequencyShift" ) ) { minFrequencyShift = src.ParseFloat(); continue; }
|
||||
if ( !token.Icmp( "maxFrequencyShift" ) ) { maxFrequencyShift = src.ParseFloat(); continue; }
|
||||
if ( !token.Icmp( "wetLevel" ) ) { parms.wetLevel = src.ParseFloat(); continue; }
|
||||
if ( !token.Icmp( "dryLevel" ) ) { parms.dryLevel = src.ParseFloat(); continue; }
|
||||
if ( !token.Icmp( "soundClass" ) ) {
|
||||
parms.soundClass = src.ParseInt();
|
||||
if ( parms.soundClass < 0 || parms.soundClass >= SOUND_MAX_CLASSES ) {
|
||||
src.Warning( "SoundClass out of range" );
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "shakes" ) ) {
|
||||
if ( src.ReadToken( &token ) ) {
|
||||
if ( src.ExpectAnyToken( &token ) ) {
|
||||
if ( token.type == TT_NUMBER ) parms.shakes = token.GetFloatValue(); else { parms.shakes = 1.0f; src.UnreadToken( &token ); }
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "shakeData" ) ) {
|
||||
if ( !src.ExpectAnyToken( &token ) ) return false;
|
||||
const int shakeIndex = atoi( token.c_str() );
|
||||
if ( !src.ExpectAnyToken( &token ) ) return false;
|
||||
SetShakeData( shakeIndex, token.c_str() );
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "altSound" ) ) { if ( src.ReadToken( &token ) ) altSound = declManager->FindSound( token ); continue; }
|
||||
if ( !token.Icmp( "frequentlyUsed" ) ) { frequentlyUsed = true; continue; }
|
||||
if ( !token.Icmp( "no_shakes" ) ) { noShakes = true; continue; }
|
||||
@@ -142,24 +192,27 @@ bool idSoundShader::ParseShader( idLexer &src ) {
|
||||
if ( !token.Icmp( "global" ) ) { parms.soundShaderFlags |= SSF_GLOBAL; continue; }
|
||||
if ( !token.Icmp( "unclamped" ) ) { parms.soundShaderFlags |= SSF_UNCLAMPED; continue; }
|
||||
if ( !token.Icmp( "omnidirectional" ) ) { parms.soundShaderFlags |= SSF_OMNIDIRECTIONAL; continue; }
|
||||
if ( !token.Icmp( "doppler" ) ) { parms.soundShaderFlags |= SSF_USEDOPPLER; continue; }
|
||||
if ( !token.Icmp( "no_randomstart" ) ) { parms.soundShaderFlags |= SSF_NO_RANDOMSTART; continue; }
|
||||
if ( !token.Icmp( "vo" ) ) { parms.soundShaderFlags |= SSF_IS_VO; continue; }
|
||||
if ( !token.Icmp( "center" ) ) { parms.soundShaderFlags |= SSF_CENTER; continue; }
|
||||
if ( !token.Icmp( "ordered" ) || !token.Icmp( "plain" ) || !token.Icmp( "onDemand" ) ) continue;
|
||||
if ( !token.Icmp( "reverb" ) ) { src.ReadTokenOnLine( &token ); continue; }
|
||||
if ( !token.Icmp( "useDoppler" ) ) { parms.soundShaderFlags |= SSF_USEDOPPLER; continue; }
|
||||
if ( !token.Icmp( "noRandomStart" ) ) { parms.soundShaderFlags |= SSF_NO_RANDOMSTART; continue; }
|
||||
if ( !token.Icmp( "voForPlayer" ) ) { parms.soundShaderFlags |= SSF_VO_FOR_PLAYER; continue; }
|
||||
if ( !token.Icmp( "onDemand" ) ) continue;
|
||||
|
||||
if ( !token.Icmp( "leadin" ) ) {
|
||||
if ( !src.ReadToken( &token ) ) return false;
|
||||
if ( numLeadins < maxSamples ) leadins[numLeadins++] = soundSystemLocal.FindSample( token );
|
||||
if ( soundSystem->HasCache() && numLeadins < maxSamples ) {
|
||||
leadins[numLeadins++] = soundSystem->FindSample( token );
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ( token.Find( ".wav", false ) >= 0 || token.Find( ".ogg", false ) >= 0 ) {
|
||||
token.BackSlashesToSlashes();
|
||||
if ( numEntries < maxSamples ) entries[numEntries++] = soundSystemLocal.FindSample( token );
|
||||
continue;
|
||||
|
||||
// In Quake 4 every otherwise-unrecognized token is a sample name. The
|
||||
// retail assets intentionally omit .wav/.ogg on many VO and shake rows.
|
||||
if ( soundSystem->HasCache() && numEntries < maxSamples ) {
|
||||
rvCommonSample *sample = soundSystem->FindSample( token );
|
||||
if ( sample != NULL ) {
|
||||
entries[numEntries++] = sample;
|
||||
}
|
||||
}
|
||||
src.Warning( "unknown token '%s' in sound shader '%s'", token.c_str(), GetName() );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ public:
|
||||
// Returns NULL if gui by that name does not exist.
|
||||
virtual idUserInterface * FindGui( const char *qpath, bool autoLoad = false, bool needUnique = false, bool forceUnique = false ) = 0;
|
||||
|
||||
#ifdef Q4_RECON_ENGINE_PRIVATE
|
||||
#if defined( Q4_RECON_ENGINE_PRIVATE ) || defined( Q4_RECON_RETAIL_UI_MANAGER_ABI )
|
||||
// Retail engine slot; the three index helpers below are later SDK additions.
|
||||
virtual idUserInterface * FindDemoGui( const char *qpath ) = 0;
|
||||
#else
|
||||
|
||||
+118
-103
@@ -222,10 +222,6 @@ void Script_ResetTime(idWindow *window, idList<idGSWinVar> *src) {
|
||||
drawWin_t *win = NULL;
|
||||
if (parm && src->Num() > 1) {
|
||||
win = window->GetGui()->GetDesktop()->FindChildByName(*parm);
|
||||
if ( !idStr::Icmp( parm->c_str(), "anim_in" ) || !idStr::Icmp( parm->c_str(), "anim_newIn" ) ) {
|
||||
common->DPrintf( "Q4 menu trace: resetTime requested '%s', resolved '%s'\n",
|
||||
parm->c_str(), ( win && win->win ) ? win->win->GetName() : "<not found>" );
|
||||
}
|
||||
parm = dynamic_cast<idWinStr*>((*src)[1].var);
|
||||
}
|
||||
if (win && win->win) {
|
||||
@@ -252,66 +248,48 @@ Script_Transition
|
||||
=========================
|
||||
*/
|
||||
void Script_Transition(idWindow *window, idList<idGSWinVar> *src) {
|
||||
// transitions always affect rect or vec4 vars
|
||||
if (src->Num() >= 4) {
|
||||
idWinRectangle *rect = NULL;
|
||||
idWinVec4 *vec4 = dynamic_cast<idWinVec4*>((*src)[0].var);
|
||||
//
|
||||
// added float variable
|
||||
idWinFloat* val = NULL;
|
||||
idWinFloatMember *member = NULL;
|
||||
//
|
||||
if (vec4 == NULL) {
|
||||
rect = dynamic_cast<idWinRectangle*>((*src)[0].var);
|
||||
//
|
||||
// added float variable
|
||||
if ( NULL == rect ) {
|
||||
val = dynamic_cast<idWinFloat*>((*src)[0].var);
|
||||
if ( NULL == val ) {
|
||||
member = dynamic_cast<idWinFloatMember*>((*src)[0].var);
|
||||
}
|
||||
}
|
||||
//
|
||||
}
|
||||
idWinVec4 *from = dynamic_cast<idWinVec4*>((*src)[1].var);
|
||||
idWinVec4 *to = dynamic_cast<idWinVec4*>((*src)[2].var);
|
||||
idWinStr *timeStr = dynamic_cast<idWinStr*>((*src)[3].var);
|
||||
//
|
||||
// added float variable
|
||||
if (!((vec4 || rect || val || member) && from && to && timeStr)) {
|
||||
//
|
||||
common->Warning("Bad transition in gui %s in window %s\n", window->GetGui()->GetSourceFile(), window->GetName());
|
||||
if ( src->Num() < 4 ) {
|
||||
return;
|
||||
}
|
||||
int time = atoi(*timeStr);
|
||||
float ac = 0.0f;
|
||||
float dc = 0.0f;
|
||||
if (src->Num() > 4) {
|
||||
idWinStr *acv = dynamic_cast<idWinStr*>((*src)[4].var);
|
||||
idWinStr *dcv = dynamic_cast<idWinStr*>((*src)[5].var);
|
||||
assert(acv && dcv);
|
||||
ac = atof(*acv);
|
||||
dc = atof(*dcv);
|
||||
|
||||
idWinVar *destination = (*src)[0].var;
|
||||
idWinInt *timeVar = dynamic_cast<idWinInt *>( (*src)[3].var );
|
||||
if ( !destination || !timeVar ) {
|
||||
common->Warning( "Bad transition in gui %s in window %s\n", window->GetGui()->GetSourceFile(), window->GetName() );
|
||||
return;
|
||||
}
|
||||
|
||||
if (vec4) {
|
||||
vec4->SetEval(false);
|
||||
window->AddTransition(vec4, *from, *to, time, ac, dc);
|
||||
//
|
||||
// added float variable
|
||||
} else if ( val ) {
|
||||
val->SetEval ( false );
|
||||
window->AddTransition(val, *from, *to, time, ac, dc);
|
||||
} else if ( member ) {
|
||||
member->SetEval( false );
|
||||
window->AddTransition( member, *from, *to, time, ac, dc );
|
||||
//
|
||||
idVec4 from;
|
||||
idVec4 to;
|
||||
idWinVec4 *fromVec = dynamic_cast<idWinVec4 *>( (*src)[1].var );
|
||||
idWinVec4 *toVec = dynamic_cast<idWinVec4 *>( (*src)[2].var );
|
||||
if ( fromVec ) {
|
||||
from = static_cast<const idVec4 &>( *fromVec );
|
||||
} else {
|
||||
rect->SetEval(false);
|
||||
window->AddTransition(rect, *from, *to, time, ac, dc);
|
||||
const float value = (*src)[1].var->x();
|
||||
from.Set( value, value, value, value );
|
||||
}
|
||||
if ( toVec ) {
|
||||
to = static_cast<const idVec4 &>( *toVec );
|
||||
} else {
|
||||
const float value = (*src)[2].var->x();
|
||||
to.Set( value, value, value, value );
|
||||
}
|
||||
|
||||
float accel = 0.0f;
|
||||
float decel = 0.0f;
|
||||
if ( src->Num() > 5 ) {
|
||||
idWinFloat *accelVar = dynamic_cast<idWinFloat *>( (*src)[4].var );
|
||||
idWinFloat *decelVar = dynamic_cast<idWinFloat *>( (*src)[5].var );
|
||||
if ( accelVar && decelVar ) {
|
||||
accel = *accelVar;
|
||||
decel = *decelVar;
|
||||
}
|
||||
}
|
||||
|
||||
destination->SetEval( false );
|
||||
window->AddTransition( destination, from, to, *timeVar, accel, decel );
|
||||
window->StartTransition();
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@@ -592,72 +570,109 @@ void idGuiScript::FixupParms(idWindow *win) {
|
||||
} else if (handler == &Script_Transition) {
|
||||
if (parms.Num() < 4) {
|
||||
common->Warning("Window %s in gui %s has a bad transition definition", win->GetName(), win->GetGui()->GetSourceFile());
|
||||
handler = NULL;
|
||||
return;
|
||||
}
|
||||
idWinStr *str = dynamic_cast<idWinStr*>(parms[0].var);
|
||||
assert(str);
|
||||
|
||||
//
|
||||
drawWin_t *destowner;
|
||||
idWinVar *dest = win->GetWinVarByName(*str, true, &destowner );
|
||||
//
|
||||
|
||||
if (dest) {
|
||||
delete parms[0].var;
|
||||
parms[0].var = dest;
|
||||
parms[0].own = false;
|
||||
} else {
|
||||
drawWin_t *destOwner = NULL;
|
||||
idWinVar *dest = win->GetWinVarByName(*str, true, &destOwner );
|
||||
idWinVec4 *destVec = dynamic_cast<idWinVec4 *>( dest );
|
||||
idWinFloat *destFloat = dynamic_cast<idWinFloat *>( dest );
|
||||
idWinRectangle *destRect = dynamic_cast<idWinRectangle *>( dest );
|
||||
idWinFloatMember *destMember = dynamic_cast<idWinFloatMember *>( dest );
|
||||
if ( !destVec && !destFloat && !destRect && !destMember ) {
|
||||
common->Warning("Window %s in gui %s: a transition does not have a valid destination var %s", win->GetName(), win->GetGui()->GetSourceFile(),str->c_str());
|
||||
handler = NULL;
|
||||
return;
|
||||
}
|
||||
delete parms[0].var;
|
||||
parms[0].var = dest;
|
||||
parms[0].own = false;
|
||||
|
||||
//
|
||||
// support variables as parameters
|
||||
int c;
|
||||
for ( c = 1; c < 3; c ++ ) {
|
||||
// Quake 4 retains scalar transition parameters as floats and vector /
|
||||
// rectangle parameters as vec4s. Script_Transition then expands a
|
||||
// scalar into all four interpolation lanes.
|
||||
for ( int c = 1; c < 3; c++ ) {
|
||||
str = dynamic_cast<idWinStr*>(parms[c].var);
|
||||
assert( str );
|
||||
|
||||
idWinVec4 *v4 = new idWinVec4;
|
||||
parms[c].var = v4;
|
||||
if ( str->c_str()[0] == '$' ) {
|
||||
drawWin_t *sourceOwner = NULL;
|
||||
idWinVar *source = win->GetWinVarByName( str->c_str() + 1, true, &sourceOwner );
|
||||
idWinVec4 *sourceVec = dynamic_cast<idWinVec4 *>( source );
|
||||
idWinFloat *sourceFloat = dynamic_cast<idWinFloat *>( source );
|
||||
idWinRectangle *sourceRect = dynamic_cast<idWinRectangle *>( source );
|
||||
idWinFloatMember *sourceMember = dynamic_cast<idWinFloatMember *>( source );
|
||||
|
||||
const bool compatible =
|
||||
( destVec && sourceVec ) ||
|
||||
( destFloat && sourceFloat ) ||
|
||||
( destRect && ( sourceVec || sourceRect ) ) ||
|
||||
( destMember && ( sourceFloat || sourceMember ) );
|
||||
if ( !compatible ) {
|
||||
common->Warning( "Window %s in gui %s: transition has an invalid parameter %d (%s)",
|
||||
win->GetName(), win->GetGui()->GetSourceFile(), c, str->c_str() );
|
||||
handler = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( destRect && sourceRect ) {
|
||||
idWinVec4 *value = new idWinVec4;
|
||||
*value = idVec4( 0.0f, 0.0f, 0.0f, 0.0f );
|
||||
idWindow *sourceParent = sourceOwner ?
|
||||
( sourceOwner->simp ? sourceOwner->simp->GetParent() : sourceOwner->win->GetParent() ) : NULL;
|
||||
idWindow *destinationParent = destOwner ?
|
||||
( destOwner->simp ? destOwner->simp->GetParent() : destOwner->win->GetParent() ) : NULL;
|
||||
if ( sourceParent && destinationParent ) {
|
||||
idRectangle rect = *sourceRect;
|
||||
sourceParent->ClientToScreen( &rect );
|
||||
destinationParent->ScreenToClient( &rect );
|
||||
*value = rect.ToVec4();
|
||||
} else {
|
||||
value->Set( sourceRect->c_str() );
|
||||
}
|
||||
parms[c].var = value;
|
||||
parms[c].own = true;
|
||||
|
||||
drawWin_t* owner;
|
||||
|
||||
if ( (*str[0]) == '$' ) {
|
||||
dest = win->GetWinVarByName ( (const char*)(*str) + 1, true, &owner );
|
||||
} else {
|
||||
dest = NULL;
|
||||
}
|
||||
|
||||
if ( dest ) {
|
||||
idWindow* ownerparent;
|
||||
idWindow* destparent;
|
||||
if ( owner ) {
|
||||
ownerparent = owner->simp?owner->simp->GetParent():owner->win->GetParent();
|
||||
destparent = destowner->simp?destowner->simp->GetParent():destowner->win->GetParent();
|
||||
|
||||
// If its the rectangle they are referencing then adjust it
|
||||
if ( ownerparent && destparent &&
|
||||
(dest == (owner->simp?owner->simp->GetWinVarByName ( "rect" ):owner->win->GetWinVarByName ( "rect" ) ) ) )
|
||||
{
|
||||
idRectangle rect;
|
||||
rect = *(dynamic_cast<idWinRectangle*>(dest));
|
||||
ownerparent->ClientToScreen ( &rect );
|
||||
destparent->ScreenToClient ( &rect );
|
||||
*v4 = rect.ToVec4 ( );
|
||||
} else {
|
||||
v4->Set ( dest->c_str ( ) );
|
||||
parms[c].var = source;
|
||||
parms[c].own = false;
|
||||
}
|
||||
} else {
|
||||
v4->Set ( dest->c_str ( ) );
|
||||
}
|
||||
idWinVar *value;
|
||||
if ( destVec || destRect ) {
|
||||
idWinVec4 *vecValue = new idWinVec4;
|
||||
*vecValue = idVec4( 0.0f, 0.0f, 0.0f, 0.0f );
|
||||
value = vecValue;
|
||||
} else {
|
||||
v4->Set(*str);
|
||||
value = new idWinFloat;
|
||||
}
|
||||
value->Set( str->c_str() );
|
||||
parms[c].var = value;
|
||||
parms[c].own = true;
|
||||
}
|
||||
|
||||
delete str;
|
||||
}
|
||||
//
|
||||
|
||||
str = dynamic_cast<idWinStr *>( parms[3].var );
|
||||
assert( str );
|
||||
idWinInt *time = new idWinInt;
|
||||
time->Set( str->c_str() );
|
||||
delete str;
|
||||
parms[3].var = time;
|
||||
parms[3].own = true;
|
||||
|
||||
for ( int i = 4; i < parms.Num() && i < 6; i++ ) {
|
||||
str = dynamic_cast<idWinStr *>( parms[i].var );
|
||||
assert( str );
|
||||
idWinFloat *value = new idWinFloat;
|
||||
value->Set( str->c_str() );
|
||||
delete str;
|
||||
parms[i].var = value;
|
||||
parms[i].own = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
int c = parms.Num();
|
||||
|
||||
+8
-27
@@ -612,6 +612,10 @@ idWindow::RunTimeEvents
|
||||
*/
|
||||
bool idWindow::RunTimeEvents(int time) {
|
||||
|
||||
if ( lastTimeRun > time ) {
|
||||
lastTimeRun = time;
|
||||
}
|
||||
|
||||
if ( time - lastTimeRun < common->GetUserCmdMSec() ) {
|
||||
//common->Printf("Skipping gui time events at %i\n", time);
|
||||
return false;
|
||||
@@ -659,10 +663,6 @@ void idWindow::RunNamedEvent ( const char* eventName )
|
||||
continue;
|
||||
}
|
||||
UpdateWinVars();
|
||||
const bool traceIngameCheck = !idStr::Icmp( eventName, "ingameCheck" );
|
||||
if ( traceIngameCheck ) {
|
||||
common->DPrintf( "Q4 menu trace: ingameCheck begin gui::inGame='%s'\n", gui->State().GetString( "inGame" ) );
|
||||
}
|
||||
|
||||
// Make sure we got all the current values for stuff
|
||||
if (expressionRegisters.Num() && ops.Num()) {
|
||||
@@ -670,11 +670,6 @@ void idWindow::RunNamedEvent ( const char* eventName )
|
||||
}
|
||||
|
||||
RunScriptList( namedEvents[i]->mEvent );
|
||||
if ( traceIngameCheck ) {
|
||||
drawWin_t *newGame = gui->GetDesktop()->FindChildByName( "main_b_newgame" );
|
||||
idWinVar *shown = newGame ? ( newGame->win ? newGame->win->GetWinVarByName( "visible" ) : newGame->simp->GetWinVarByName( "visible" ) ) : NULL;
|
||||
common->DPrintf( "Q4 menu trace: ingameCheck end main_b_newgame visible=%s\n", shown ? shown->c_str() : "<not found>" );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1080,10 +1075,6 @@ idWindow::Time
|
||||
*/
|
||||
void idWindow::Time() {
|
||||
|
||||
if ( !idStr::Icmp( name, "anim_newIn" ) && !noTime ) {
|
||||
common->DPrintf( "Q4 menu trace: anim_newIn unexpectedly active at guiTime=%d epoch=%d\n", gui->GetTime(), timeLine );
|
||||
}
|
||||
|
||||
if ( noTime ) {
|
||||
return;
|
||||
}
|
||||
@@ -1098,10 +1089,6 @@ void idWindow::Time() {
|
||||
if ( c > 0 ) {
|
||||
for (int i = 0; i < c; i++) {
|
||||
if ( timeLineEvents[i]->pending && gui->GetTime() - timeLine >= timeLineEvents[i]->time ) {
|
||||
if ( !idStr::Icmp( name, "anim_in" ) || !idStr::Icmp( name, "video_bethsoft" ) ) {
|
||||
common->DPrintf( "Q4 menu trace: timeline '%s' firing %d at guiTime=%d epoch=%d\n",
|
||||
name.c_str(), timeLineEvents[i]->time, gui->GetTime(), timeLine );
|
||||
}
|
||||
timeLineEvents[i]->pending = false;
|
||||
RunScriptList( timeLineEvents[i]->event );
|
||||
}
|
||||
@@ -2646,9 +2633,6 @@ bool idWindow::Parse( idParser *src, bool rebuild) {
|
||||
|
||||
SetupFromState();
|
||||
PostParse();
|
||||
if ( !idStr::Icmp( name, "anim_newIn" ) ) {
|
||||
common->DPrintf( "Q4 menu trace: parsed anim_newIn notime=%d events=%d\n", noTime ? 1 : 0, timeLineEvents.Num() );
|
||||
}
|
||||
|
||||
// hook into the main window parsing for the gui editor
|
||||
// If we are in the gui editor then add the internal var to the
|
||||
@@ -2772,10 +2756,6 @@ idWindow::ResetTime
|
||||
================
|
||||
*/
|
||||
void idWindow::ResetTime(int t) {
|
||||
if ( !idStr::Icmp( name, "anim_in" ) || !idStr::Icmp( name, "anim_newIn" ) || !idStr::Icmp( name, "video_bethsoft" ) ) {
|
||||
common->DPrintf( "Q4 menu trace: ResetTime '%s' t=%d guiTime=%d\n", name.c_str(), t, gui->GetTime() );
|
||||
}
|
||||
|
||||
timeLine = gui->GetTime() - t;
|
||||
|
||||
int i, c = timeLineEvents.Num();
|
||||
@@ -3239,9 +3219,10 @@ void idWindow::EvaluateRegisters(float *registers) {
|
||||
break;
|
||||
}
|
||||
if ( op->b >= 0 && registers[op->b] >= 0 && registers[op->b] < 4 ) {
|
||||
// grabs vector components
|
||||
idWinVec4 *var = (idWinVec4 *)( op->a );
|
||||
registers[op->c] = ((idVec4&)var)[registers[op->b]];
|
||||
// The operand is an idWinVar pointer. Casting the local pointer
|
||||
// itself to idVec4 reads unrelated stack data and makes GUI color
|
||||
// expressions depend on the current stack layout.
|
||||
registers[op->c] = ((idWinVar *)(op->a))->GetMember( (int)registers[op->b] );
|
||||
} else {
|
||||
registers[op->c] = ((idWinVar*)(op->a))->x();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user