/* =============== idInteraction::FreeSurfaces Frees the surfaces, but leaves the interaction linked in, so it will be regenerated automatically =============== */ void idInteraction::FreeSurfaces( void ) { if ( this->surfaces ) { for ( int i = 0 ; i < this->numSurfaces ; i++ ) { surfaceInteraction_t *sint = &this->surfaces[i]; if ( sint->lightTris ) { if ( sint->lightTris != LIGHT_TRIS_DEFERRED ) { R_FreeStaticTriSurf( sint->lightTris ); } sint->lightTris = NULL; } if ( sint->shadowTris ) { // if it doesn't have an entityDef, it is part of a prelight // model, not a generated interaction if ( this->entityDef ) { R_FreeStaticTriSurf( sint->shadowTris ); sint->shadowTris = NULL; } } R_FreeInteractionCullInfo( sint->cullInfo ); } R_StaticFree( this->surfaces ); this->surfaces = NULL; } this->numSurfaces = -1; }
/* ==================== idMD5Mesh::UpdateSurface ==================== */ void idMD5Mesh::UpdateSurface( const struct renderEntity_s *ent, const idJointMat *entJoints, const idJointMat *entJointsInverted, modelSurface_t *surf ) { tr.pc.c_deformedSurfaces++; tr.pc.c_deformedVerts += deformInfo->numOutputVerts; tr.pc.c_deformedIndexes += deformInfo->numIndexes; surf->shader = shader; if ( surf->geometry != NULL ) { // if the number of verts and indexes are the same we can re-use the triangle surface if ( surf->geometry->numVerts == deformInfo->numOutputVerts && surf->geometry->numIndexes == deformInfo->numIndexes ) { R_FreeStaticTriSurfVertexCaches( surf->geometry ); } else { R_FreeStaticTriSurf( surf->geometry ); surf->geometry = R_AllocStaticTriSurf(); } } else { surf->geometry = R_AllocStaticTriSurf(); } srfTriangles_t * tri = surf->geometry; // note that some of the data is referenced, and should not be freed tri->referencedIndexes = true; tri->numIndexes = deformInfo->numIndexes; tri->indexes = deformInfo->indexes; tri->silIndexes = deformInfo->silIndexes; tri->numMirroredVerts = deformInfo->numMirroredVerts; tri->mirroredVerts = deformInfo->mirroredVerts; tri->numDupVerts = deformInfo->numDupVerts; tri->dupVerts = deformInfo->dupVerts; tri->numSilEdges = deformInfo->numSilEdges; tri->silEdges = deformInfo->silEdges; tri->indexCache = deformInfo->staticIndexCache; tri->numVerts = deformInfo->numOutputVerts; if ( r_useGPUSkinning.GetBool() ) { if ( tri->verts != NULL && tri->verts != deformInfo->verts ) { R_FreeStaticTriSurfVerts( tri ); } tri->verts = deformInfo->verts; tri->ambientCache = deformInfo->staticAmbientCache; tri->shadowCache = deformInfo->staticShadowCache; tri->referencedVerts = true; } else { if ( tri->verts == NULL || tri->verts == deformInfo->verts ) { tri->verts = NULL; R_AllocStaticTriSurfVerts( tri, deformInfo->numOutputVerts ); assert( tri->verts != NULL ); // quiet analyze warning memcpy( tri->verts, deformInfo->verts, deformInfo->numOutputVerts * sizeof( deformInfo->verts[0] ) ); // copy over the texture coordinates } TransformVertsAndTangents( tri->verts, deformInfo->numOutputVerts, deformInfo->verts, entJointsInverted ); tri->referencedVerts = false; } tri->tangentsCalculated = true; CalculateBounds( entJoints, tri->bounds ); }
/* ==================== WriteOutputFile ==================== */ void WriteOutputFile( void ) { int i; uEntity_t *entity; idStr qpath; // write the file common->Printf( "----- WriteOutputFile -----\n" ); sprintf( qpath, "%s." PROC_FILE_EXT, dmapGlobals.mapFileBase ); common->Printf( "writing %s\n", qpath.c_str() ); // _D3XP used fs_cdpath procFile = fileSystem->OpenFileWrite( qpath, "fs_devpath" ); if ( !procFile ) { common->Error( "Error opening %s", qpath.c_str() ); } procFile->WriteFloatString( "%s\n\n", PROC_FILE_ID ); // write the entity models and information, writing entities first for ( i=dmapGlobals.num_entities - 1 ; i >= 0 ; i-- ) { entity = &dmapGlobals.uEntities[i]; if ( !entity->primitives ) { continue; } WriteOutputEntity( i ); } // write the shadow volumes for ( i = 0 ; i < dmapGlobals.mapLights.Num() ; i++ ) { mapLight_t *light = dmapGlobals.mapLights[i]; if ( !light->shadowTris ) { continue; } procFile->WriteFloatString( "shadowModel { /* name = */ \"_prelight_%s\"\n\n", light->name ); WriteShadowTriangles( light->shadowTris ); procFile->WriteFloatString( "}\n\n" ); R_FreeStaticTriSurf( light->shadowTris ); light->shadowTris = NULL; } fileSystem->CloseFile( procFile ); }
/* ==================== idRenderModelOverlay::AddOverlaySurfacesToModel ==================== */ void idRenderModelOverlay::AddOverlaySurfacesToModel( idRenderModel *baseModel ) { int i, j, k, numVerts, numIndexes, surfaceNum; const modelSurface_t *baseSurf; idRenderModelStatic *staticModel; overlaySurface_t *surf; srfTriangles_t *newTri; modelSurface_t *newSurf; if ( baseModel == NULL || baseModel->IsDefaultModel() ) { return; } // md5 models won't have any surfaces when r_showSkel is set if ( !baseModel->NumSurfaces() ) { return; } if ( baseModel->IsDynamicModel() != DM_STATIC ) { common->Error( "idRenderModelOverlay::AddOverlaySurfacesToModel: baseModel is not a static model" ); } assert( dynamic_cast<idRenderModelStatic *>(baseModel) != NULL ); staticModel = static_cast<idRenderModelStatic *>(baseModel); staticModel->overlaysAdded = 0; if ( !materials.Num() ) { staticModel->DeleteSurfacesWithNegativeId(); return; } for ( k = 0; k < materials.Num(); k++ ) { numVerts = numIndexes = 0; for ( i = 0; i < materials[k]->surfaces.Num(); i++ ) { numVerts += materials[k]->surfaces[i]->numVerts; numIndexes += materials[k]->surfaces[i]->numIndexes; } if ( staticModel->FindSurfaceWithId( -1 - k, surfaceNum ) ) { newSurf = &staticModel->surfaces[surfaceNum]; } else { newSurf = &staticModel->surfaces.Alloc(); newSurf->geometry = NULL; newSurf->shader = materials[k]->material; newSurf->id = -1 - k; } if ( newSurf->geometry == NULL || newSurf->geometry->numVerts < numVerts || newSurf->geometry->numIndexes < numIndexes ) { R_FreeStaticTriSurf( newSurf->geometry ); newSurf->geometry = R_AllocStaticTriSurf(); R_AllocStaticTriSurfVerts( newSurf->geometry, numVerts ); R_AllocStaticTriSurfIndexes( newSurf->geometry, numIndexes ); SIMDProcessor->Memset( newSurf->geometry->verts, 0, numVerts * sizeof( newTri->verts[0] ) ); } else { R_FreeStaticTriSurfVertexCaches( newSurf->geometry ); } newTri = newSurf->geometry; numVerts = numIndexes = 0; for ( i = 0; i < materials[k]->surfaces.Num(); i++ ) { surf = materials[k]->surfaces[i]; // get the model surface for this overlay surface if ( surf->surfaceNum < staticModel->NumSurfaces() ) { baseSurf = staticModel->Surface( surf->surfaceNum ); } else { baseSurf = NULL; } // if the surface ids no longer match if ( !baseSurf || baseSurf->id != surf->surfaceId ) { // find the surface with the correct id if ( staticModel->FindSurfaceWithId( surf->surfaceId, surf->surfaceNum ) ) { baseSurf = staticModel->Surface( surf->surfaceNum ); } else { // the surface with this id no longer exists FreeSurface( surf ); materials[k]->surfaces.RemoveIndex( i ); i--; continue; } } // copy indexes; for ( j = 0; j < surf->numIndexes; j++ ) { newTri->indexes[numIndexes + j] = numVerts + surf->indexes[j]; } numIndexes += surf->numIndexes; // copy vertices for ( j = 0; j < surf->numVerts; j++ ) { overlayVertex_t *overlayVert = &surf->verts[j]; newTri->verts[numVerts].st[0] = overlayVert->st[0]; newTri->verts[numVerts].st[1] = overlayVert->st[1]; if ( overlayVert->vertexNum >= baseSurf->geometry->numVerts ) { // This can happen when playing a demofile and a model has been changed since it was recorded, so just issue a warning and go on. common->Warning( "idRenderModelOverlay::AddOverlaySurfacesToModel: overlay vertex out of range. Model has probably changed since generating the overlay." ); FreeSurface( surf ); materials[k]->surfaces.RemoveIndex( i ); staticModel->DeleteSurfaceWithId( newSurf->id ); return; } newTri->verts[numVerts].xyz = baseSurf->geometry->verts[overlayVert->vertexNum].xyz; numVerts++; } } newTri->numVerts = numVerts; newTri->numIndexes = numIndexes; R_BoundTriSurf( newTri ); staticModel->overlaysAdded++; // so we don't create an overlay on an overlay surface } }
/* ==================== WriteOutputSurfaces ==================== */ static void WriteOutputSurfaces( int entityNum, int areaNum ) { mapTri_t *ambient, *copy; int surfaceNum; int numSurfaces; idMapEntity *entity; uArea_t *area; optimizeGroup_t *group, *groupStep; int i; // , j; // int col; srfTriangles_t *uTri; // mapTri_t *tri; typedef struct interactionTris_s { struct interactionTris_s *next; mapTri_t *triList; mapLight_t *light; } interactionTris_t; interactionTris_t *interactions, *checkInter; //, *nextInter; area = &dmapGlobals.uEntities[entityNum].areas[areaNum]; entity = dmapGlobals.uEntities[entityNum].mapEntity; numSurfaces = CountUniqueShaders( area->groups ); if ( entityNum == 0 ) { procFile->WriteFloatString( "model { /* name = */ \"_area%i\" /* numSurfaces = */ %i\n\n", areaNum, numSurfaces ); } else { const char *name; entity->epairs.GetString( "name", "", &name ); if ( !name[0] ) { common->Error( "Entity %i has surfaces, but no name key", entityNum ); } procFile->WriteFloatString( "model { /* name = */ \"%s\" /* numSurfaces = */ %i\n\n", name, numSurfaces ); } surfaceNum = 0; for ( group = area->groups ; group ; group = group->nextGroup ) { if ( group->surfaceEmited ) { continue; } // combine all groups compatible with this one // usually several optimizeGroup_t can be combined into a single // surface, even though they couldn't be merged together to save // vertexes because they had different planes, texture coordinates, or lights. // Different mergeGroups will stay in separate surfaces. ambient = NULL; // each light that illuminates any of the groups in the surface will // get its own list of indexes out of the original surface interactions = NULL; for ( groupStep = group ; groupStep ; groupStep = groupStep->nextGroup ) { if ( groupStep->surfaceEmited ) { continue; } if ( !GroupsAreSurfaceCompatible( group, groupStep ) ) { continue; } // copy it out to the ambient list copy = CopyTriList( groupStep->triList ); ambient = MergeTriLists( ambient, copy ); groupStep->surfaceEmited = true; // duplicate it into an interaction for each groupLight for ( i = 0 ; i < groupStep->numGroupLights ; i++ ) { for ( checkInter = interactions ; checkInter ; checkInter = checkInter->next ) { if ( checkInter->light == groupStep->groupLights[i] ) { break; } } if ( !checkInter ) { // create a new interaction checkInter = (interactionTris_t *)Mem_ClearedAlloc( sizeof( *checkInter ) ); checkInter->light = groupStep->groupLights[i]; checkInter->next = interactions; interactions = checkInter; } copy = CopyTriList( groupStep->triList ); checkInter->triList = MergeTriLists( checkInter->triList, copy ); } } if ( !ambient ) { continue; } if ( surfaceNum >= numSurfaces ) { common->Error( "WriteOutputSurfaces: surfaceNum >= numSurfaces" ); } procFile->WriteFloatString( "/* surface %i */ { ", surfaceNum ); surfaceNum++; procFile->WriteFloatString( "\"%s\" ", ambient->material->GetName() ); uTri = ShareMapTriVerts( ambient ); FreeTriList( ambient ); CleanupUTriangles( uTri ); WriteUTriangles( uTri ); R_FreeStaticTriSurf( uTri ); procFile->WriteFloatString( "}\n\n" ); } procFile->WriteFloatString( "}\n\n" ); }
/* ==================== idMD5Mesh::UpdateSurface ==================== */ void idMD5Mesh::UpdateSurface( const struct renderEntity_s *ent, const idJointMat *entJoints, modelSurface_t *surf ) { int i, base; srfTriangles_t *tri; tr.pc.c_deformedSurfaces++; tr.pc.c_deformedVerts += deformInfo->numOutputVerts; tr.pc.c_deformedIndexes += deformInfo->numIndexes; surf->shader = shader; if ( surf->geometry ) { // if the number of verts and indexes are the same we can re-use the triangle surface // the number of indexes must be the same to assure the correct amount of memory is allocated for the facePlanes if ( surf->geometry->numVerts == deformInfo->numOutputVerts && surf->geometry->numIndexes == deformInfo->numIndexes ) { R_FreeStaticTriSurfVertexCaches( surf->geometry ); } else { R_FreeStaticTriSurf( surf->geometry ); surf->geometry = R_AllocStaticTriSurf(); } } else { surf->geometry = R_AllocStaticTriSurf(); } tri = surf->geometry; // note that some of the data is references, and should not be freed tri->deformedSurface = true; tri->tangentsCalculated = false; tri->facePlanesCalculated = false; tri->numIndexes = deformInfo->numIndexes; tri->indexes = deformInfo->indexes; tri->silIndexes = deformInfo->silIndexes; tri->numMirroredVerts = deformInfo->numMirroredVerts; tri->mirroredVerts = deformInfo->mirroredVerts; tri->numDupVerts = deformInfo->numDupVerts; tri->dupVerts = deformInfo->dupVerts; tri->numSilEdges = deformInfo->numSilEdges; tri->silEdges = deformInfo->silEdges; tri->dominantTris = deformInfo->dominantTris; tri->numVerts = deformInfo->numOutputVerts; if ( tri->verts == NULL ) { R_AllocStaticTriSurfVerts( tri, tri->numVerts ); for ( i = 0; i < deformInfo->numSourceVerts; i++ ) { tri->verts[i].Clear(); tri->verts[i].st = texCoords[i]; } } if ( ent->shaderParms[ SHADERPARM_MD5_SKINSCALE ] != 0.0f ) { TransformScaledVerts( tri->verts, entJoints, ent->shaderParms[ SHADERPARM_MD5_SKINSCALE ] ); } else { TransformVerts( tri->verts, entJoints ); } // replicate the mirror seam vertexes base = deformInfo->numOutputVerts - deformInfo->numMirroredVerts; for ( i = 0; i < deformInfo->numMirroredVerts; i++ ) { tri->verts[base + i] = tri->verts[deformInfo->mirroredVerts[i]]; } R_BoundTriSurf( tri ); // If a surface is going to be have a lighting interaction generated, it will also have to call // R_DeriveTangents() to get normals, tangents, and face planes. If it only // needs shadows generated, it will only have to generate face planes. If it only // has ambient drawing, or is culled, no additional work will be necessary if ( !r_useDeferredTangents.GetBool() ) { // set face planes, vertex normals, tangents R_DeriveTangents( tri ); } }
/* ====================== CreateStaticInteraction Called by idRenderWorldLocal::GenerateAllInteractions ====================== */ void idInteraction::CreateStaticInteraction() { // note that it is a static interaction staticInteraction = true; const idRenderModel *model = entityDef->parms.hModel; if ( model == NULL || model->NumSurfaces() <= 0 || model->IsDynamicModel() != DM_STATIC ) { MakeEmpty(); return; } const idBounds bounds = model->Bounds( &entityDef->parms ); // if it doesn't contact the light frustum, none of the surfaces will if ( R_CullModelBoundsToLight( lightDef, bounds, entityDef->modelRenderMatrix ) ) { MakeEmpty(); return; } // // create slots for each of the model's surfaces // numSurfaces = model->NumSurfaces(); surfaces = (surfaceInteraction_t *)R_ClearedStaticAlloc( sizeof( *surfaces ) * numSurfaces ); bool interactionGenerated = false; // check each surface in the model for ( int c = 0 ; c < model->NumSurfaces() ; c++ ) { const modelSurface_t * surf = model->Surface( c ); const srfTriangles_t * tri = surf->geometry; if ( tri == NULL ) { continue; } // determine the shader for this surface, possibly by skinning // Note that this will be wrong if customSkin/customShader are // changed after map load time without invalidating the interaction! const idMaterial * const shader = R_RemapShaderBySkin( surf->shader, entityDef->parms.customSkin, entityDef->parms.customShader ); if ( shader == NULL ) { continue; } // try to cull each surface if ( R_CullModelBoundsToLight( lightDef, tri->bounds, entityDef->modelRenderMatrix ) ) { continue; } surfaceInteraction_t *sint = &surfaces[c]; // generate a set of indexes for the lit surfaces, culling away triangles that are // not at least partially inside the light if ( shader->ReceivesLighting() ) { srfTriangles_t * lightTris = R_CreateInteractionLightTris( entityDef, tri, lightDef, shader ); if ( lightTris != NULL ) { // make a static index cache sint->numLightTrisIndexes = lightTris->numIndexes; sint->lightTrisIndexCache = vertexCache.AllocStaticIndex( lightTris->indexes, ALIGN( lightTris->numIndexes * sizeof( lightTris->indexes[0] ), INDEX_CACHE_ALIGN ) ); interactionGenerated = true; R_FreeStaticTriSurf( lightTris ); } } // if the interaction has shadows and this surface casts a shadow if ( HasShadows() && shader->SurfaceCastsShadow() && tri->silEdges != NULL ) { // if the light has an optimized shadow volume, don't create shadows for any models that are part of the base areas if ( lightDef->parms.prelightModel == NULL || !model->IsStaticWorldModel() || r_skipPrelightShadows.GetBool() ) { srfTriangles_t * shadowTris = R_CreateInteractionShadowVolume( entityDef, tri, lightDef ); if ( shadowTris != NULL ) { // make a static index cache sint->shadowIndexCache = vertexCache.AllocStaticIndex( shadowTris->indexes, ALIGN( shadowTris->numIndexes * sizeof( shadowTris->indexes[0] ), INDEX_CACHE_ALIGN ) ); sint->numShadowIndexes = shadowTris->numIndexes; #if defined( KEEP_INTERACTION_CPU_DATA ) sint->shadowIndexes = shadowTris->indexes; shadowTris->indexes = NULL; #endif if ( shader->Coverage() != MC_OPAQUE ) { // if any surface is a shadow-casting perforated or translucent surface, or the // base surface is suppressed in the view (world weapon shadows) we can't use // the external shadow optimizations because we can see through some of the faces sint->numShadowIndexesNoCaps = shadowTris->numIndexes; } else { sint->numShadowIndexesNoCaps = shadowTris->numShadowIndexesNoCaps; } R_FreeStaticTriSurf( shadowTris ); } interactionGenerated = true; } } } // if none of the surfaces generated anything, don't even bother checking? if ( !interactionGenerated ) { MakeEmpty(); } }
/* ==================== R_CreateInteractionLightTris This is only used for the static interaction case, dynamic interactions just draw everything and let the GPU deal with it. The resulting surface will be a subset of the original triangles, it will never clip triangles, but it may cull on a per-triangle basis. ==================== */ static srfTriangles_t *R_CreateInteractionLightTris( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, const idMaterial *shader ) { SCOPED_PROFILE_EVENT( "R_CreateInteractionLightTris" ); int i; int numIndexes; triIndex_t *indexes; srfTriangles_t *newTri; int c_backfaced; int c_distance; idBounds bounds; bool includeBackFaces; int faceNum; c_backfaced = 0; c_distance = 0; numIndexes = 0; indexes = NULL; // it is debatable if non-shadowing lights should light back faces. we aren't at the moment if ( r_lightAllBackFaces.GetBool() || light->lightShader->LightEffectsBackSides() || shader->ReceivesLightingOnBackSides() || ent->parms.noSelfShadow || ent->parms.noShadow ) { includeBackFaces = true; } else { includeBackFaces = false; } // allocate a new surface for the lit triangles newTri = R_AllocStaticTriSurf(); // save a reference to the original surface newTri->ambientSurface = const_cast<srfTriangles_t *>(tri); // the light surface references the verts of the ambient surface newTri->numVerts = tri->numVerts; R_ReferenceStaticTriSurfVerts( newTri, tri ); // calculate cull information srfCullInfo_t cullInfo = {}; if ( !includeBackFaces ) { R_CalcInteractionFacing( ent, tri, light, cullInfo ); } R_CalcInteractionCullBits( ent, tri, light, cullInfo ); // if the surface is completely inside the light frustum if ( cullInfo.cullBits == LIGHT_CULL_ALL_FRONT ) { // if we aren't self shadowing, let back facing triangles get // through so the smooth shaded bump maps light all the way around if ( includeBackFaces ) { // the whole surface is lit so the light surface just references the indexes of the ambient surface newTri->indexes = tri->indexes; newTri->indexCache = tri->indexCache; // R_ReferenceStaticTriSurfIndexes( newTri, tri ); numIndexes = tri->numIndexes; bounds = tri->bounds; } else { // the light tris indexes are going to be a subset of the original indexes so we generally // allocate too much memory here but we decrease the memory block when the number of indexes is known R_AllocStaticTriSurfIndexes( newTri, tri->numIndexes ); // back face cull the individual triangles indexes = newTri->indexes; const byte *facing = cullInfo.facing; for ( faceNum = i = 0; i < tri->numIndexes; i += 3, faceNum++ ) { if ( !facing[ faceNum ] ) { c_backfaced++; continue; } indexes[numIndexes+0] = tri->indexes[i+0]; indexes[numIndexes+1] = tri->indexes[i+1]; indexes[numIndexes+2] = tri->indexes[i+2]; numIndexes += 3; } // get bounds for the surface SIMDProcessor->MinMax( bounds[0], bounds[1], tri->verts, indexes, numIndexes ); // decrease the size of the memory block to the size of the number of used indexes newTri->numIndexes = numIndexes; R_ResizeStaticTriSurfIndexes( newTri, numIndexes ); } } else { // the light tris indexes are going to be a subset of the original indexes so we generally // allocate too much memory here but we decrease the memory block when the number of indexes is known R_AllocStaticTriSurfIndexes( newTri, tri->numIndexes ); // cull individual triangles indexes = newTri->indexes; const byte *facing = cullInfo.facing; const byte *cullBits = cullInfo.cullBits; for ( faceNum = i = 0; i < tri->numIndexes; i += 3, faceNum++ ) { int i1, i2, i3; // if we aren't self shadowing, let back facing triangles get // through so the smooth shaded bump maps light all the way around if ( !includeBackFaces ) { // back face cull if ( !facing[ faceNum ] ) { c_backfaced++; continue; } } i1 = tri->indexes[i+0]; i2 = tri->indexes[i+1]; i3 = tri->indexes[i+2]; // fast cull outside the frustum // if all three points are off one plane side, it definately isn't visible if ( cullBits[i1] & cullBits[i2] & cullBits[i3] ) { c_distance++; continue; } // add to the list indexes[numIndexes+0] = i1; indexes[numIndexes+1] = i2; indexes[numIndexes+2] = i3; numIndexes += 3; } // get bounds for the surface SIMDProcessor->MinMax( bounds[0], bounds[1], tri->verts, indexes, numIndexes ); // decrease the size of the memory block to the size of the number of used indexes newTri->numIndexes = numIndexes; R_ResizeStaticTriSurfIndexes( newTri, numIndexes ); } // free the cull information when it's no longer needed R_FreeInteractionCullInfo( cullInfo ); if ( !numIndexes ) { R_FreeStaticTriSurf( newTri ); return NULL; } newTri->numIndexes = numIndexes; newTri->bounds = bounds; return newTri; }