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:
Justin Marshall
2026-08-08 05:03:09 -07:00
parent fec8fb2314
commit ff55f3d9b3
42 changed files with 1951 additions and 3074 deletions
+5
View File
@@ -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()
+9
View File
@@ -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 );
+241 -7
View File
@@ -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 ) {
return false;
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
+83
View File
@@ -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 ) {
+144 -91
View File
@@ -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();
for ( int i = 0; i < 3; ++i ) {
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 {
idMat3 localTraceAxis = trmAxis;
tw.pointTrace = false;
SetupTrm( &tw, trm );
idMat3 trmTransform = trmAxis;
if ( modelRotated ) {
localTraceAxis *= inverseModelAxis;
trmTransform *= 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;
// 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.trmExtents[i] = Max( idMath::Fabs( tw.trmBounds[0][i] ),
idMath::Fabs( tw.trmBounds[1][i] ) ) + CM_BOX_EPSILON;
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 );
}
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
+43 -21
View File
@@ -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
+14 -3
View File
@@ -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()
+3 -7
View File
@@ -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();
-27
View File
@@ -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;
+173 -14
View File
@@ -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 );
}
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+10 -3
View File
@@ -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" );
+57 -23
View File
@@ -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/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/map/loading.gui", true, false, true );
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 );
}
guiLoading->SetStateFloat( "map_loading", 0.0f );
}
/*
@@ -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
View File
@@ -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
-4
View File
@@ -34,11 +34,7 @@ instancing of objects.
#include "../Game_local.h"
#ifdef _WIN32
#include "TypeInfo.h"
#else
#include "NoGameTypeInfo.h"
#endif
/***********************************************************************
-4
View File
@@ -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.
+1 -5
View File
@@ -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() );
}
}
-1
View File
@@ -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;
}
+39
View File
@@ -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
View File
@@ -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");
+1
View File
@@ -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
+4 -1
View File
@@ -475,7 +475,10 @@ FIXME: make an "imageBlock" type to hold byte*,width,height?
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 );
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 );
+11 -1
View File
@@ -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;
+5 -3
View File
@@ -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:
+27
View File
@@ -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] );
}
+40 -6
View File
@@ -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 ) {
+310 -3
View File
@@ -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,8 +1596,28 @@ 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;
}
// shorthand for 2D modulated
if ( !token.Icmp( "colored" ) ) {
@@ -1464,9 +1674,49 @@ 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" ) ) {
if ( src.ReadTokenOnLine( &token ) ) {
newStage.fragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, token.c_str() );
@@ -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;
+2 -2
View File
@@ -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
View File
@@ -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();
+41 -13
View File
@@ -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] );
}
}
+42
View File
@@ -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() ) {
+6
View File
@@ -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" );
+231
View File
@@ -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
+22
View File
@@ -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 );
+7 -2
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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
+126 -111
View File
@@ -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());
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);
}
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 );
//
} else {
rect->SetEval(false);
window->AddTransition(rect, *from, *to, time, ac, dc);
}
window->StartTransition();
if ( src->Num() < 4 ) {
return;
}
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;
}
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 {
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 {
@@ -591,73 +569,110 @@ 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());
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;
parms[c].own = true;
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 );
drawWin_t* owner;
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 ( (*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 ( );
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 {
v4->Set ( dest->c_str ( ) );
value->Set( sourceRect->c_str() );
}
parms[c].var = value;
parms[c].own = true;
} else {
v4->Set ( dest->c_str ( ) );
parms[c].var = source;
parms[c].own = false;
}
} else {
v4->Set(*str);
}
idWinVar *value;
if ( destVec || destRect ) {
idWinVec4 *vecValue = new idWinVec4;
*vecValue = idVec4( 0.0f, 0.0f, 0.0f, 0.0f );
value = vecValue;
} else {
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
View File
@@ -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;
}
@@ -1079,10 +1074,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();
}