/* ================ FreeDMapFile ================ */ void FreeDMapFile( void ) { int i, j; FreeBrush( buildBrush ); buildBrush = NULL; // free the entities and brushes for( i = 0; i < dmapGlobals.num_entities; i++ ) { uEntity_t *ent; primitive_t *prim, *nextPrim; ent = &dmapGlobals.uEntities[i]; FreeTree( ent->tree ); // free primitives for( prim = ent->primitives; prim; prim = nextPrim ) { nextPrim = prim->next; if( prim->brush ) { FreeBrush( prim->brush ); } if( prim->tris ) { FreeTriList( prim->tris ); } Mem_Free( prim ); } // free area surfaces if( ent->areas ) { for( j = 0; j < ent->numAreas; j++ ) { uArea_t *area; area = &ent->areas[j]; FreeOptimizeGroupList( area->groups ); } Mem_Free( ent->areas ); } } Mem_Free( dmapGlobals.uEntities ); dmapGlobals.num_entities = 0; // free the map lights for( i = 0; i < dmapGlobals.mapLights.Num(); i++ ) { R_FreeLightDefDerivedData( &dmapGlobals.mapLights[i]->def ); } dmapGlobals.mapLights.DeleteContents( true ); }
/* ==================== CarveGroupsByLight Divide each group into an inside group and an outside group, based on which fragments are illuminated by the light's beam tree ==================== */ static void CarveGroupsByLight( uEntity_t *e, mapLight_t *light ) { int i; optimizeGroup_t *group, *newGroup, *carvedGroups, *nextGroup; mapTri_t *tri, *inside, *outside; uArea_t *area; for ( i = 0 ; i < e->numAreas ; i++ ) { area = &e->areas[i]; carvedGroups = NULL; // we will be either freeing or reassigning the groups as we go for ( group = area->groups ; group ; group = nextGroup ) { nextGroup = group->nextGroup; // if the surface doesn't get lit, don't carve it up if ( ( light->def.lightShader->IsFogLight() && !group->material->ReceivesFog() ) || ( !light->def.lightShader->IsFogLight() && !group->material->ReceivesLighting() ) || !group->bounds.IntersectsBounds( light->def.frustumTris->bounds ) ) { group->nextGroup = carvedGroups; carvedGroups = group; continue; } if ( group->numGroupLights == MAX_GROUP_LIGHTS ) { common->Error( "MAX_GROUP_LIGHTS around %f %f %f", group->triList->v[0].xyz[0], group->triList->v[0].xyz[1], group->triList->v[0].xyz[2] ); } // if the group doesn't face the light, // it won't get carved at all if ( !light->def.lightShader->LightEffectsBackSides() && !group->material->ReceivesLightingOnBackSides() && dmapGlobals.mapPlanes[ group->planeNum ].Distance( light->def.parms.origin ) <= 0 ) { group->nextGroup = carvedGroups; carvedGroups = group; continue; } // split into lists for hit-by-light, and not-hit-by-light inside = NULL; outside = NULL; for ( tri = group->triList ; tri ; tri = tri->next ) { mapTri_t *in, *out; ClipTriByLight( light, tri, &in, &out ); inside = MergeTriLists( inside, in ); outside = MergeTriLists( outside, out ); } if ( inside ) { newGroup = (optimizeGroup_t *)Mem_Alloc( sizeof( *newGroup ) ); *newGroup = *group; newGroup->groupLights[newGroup->numGroupLights] = light; newGroup->numGroupLights++; newGroup->triList = inside; newGroup->nextGroup = carvedGroups; carvedGroups = newGroup; } if ( outside ) { newGroup = (optimizeGroup_t *)Mem_Alloc( sizeof( *newGroup ) ); *newGroup = *group; newGroup->triList = outside; newGroup->nextGroup = carvedGroups; carvedGroups = newGroup; } // free the original group->nextGroup = NULL; FreeOptimizeGroupList( group ); } // replace this area's group list with the new one area->groups = carvedGroups; } }
/* ==================== BuildLightShadows Build the beam tree and shadow volume surface for a light ==================== */ static void BuildLightShadows( uEntity_t *e, mapLight_t *light ) { int i; optimizeGroup_t *group; mapTri_t *tri; mapTri_t *shadowers; optimizeGroup_t *shadowerGroups; idVec3 lightOrigin; bool hasPerforatedSurface = false; // // build a group list of all the triangles that will contribute to // the optimized shadow volume, leaving the original triangles alone // // shadowers will contain all the triangles that will contribute to the // shadow volume shadowerGroups = NULL; lightOrigin = light->def.globalLightOrigin; // if the light is no-shadows, don't add any surfaces // to the beam tree at all if ( !light->def.parms.noShadows && light->def.lightShader->LightCastsShadows() ) { for ( i = 0 ; i < e->numAreas ; i++ ) { for ( group = e->areas[i].groups ; group ; group = group->nextGroup ) { // if the surface doesn't cast shadows, skip it if ( !group->material->SurfaceCastsShadow() ) { continue; } // if the group doesn't face away from the light, it // won't contribute to the shadow volume if ( dmapGlobals.mapPlanes[ group->planeNum ].Distance( lightOrigin ) > 0 ) { continue; } // if the group bounds doesn't intersect the light bounds, // skip it if ( !group->bounds.IntersectsBounds( light->def.frustumTris->bounds ) ) { continue; } // build up a list of the triangle fragments inside the // light frustum shadowers = NULL; for ( tri = group->triList ; tri ; tri = tri->next ) { mapTri_t *in, *out; // clip it to the light frustum ClipTriByLight( light, tri, &in, &out ); FreeTriList( out ); shadowers = MergeTriLists( shadowers, in ); } // if we didn't get any out of this group, we don't // need to create a new group in the shadower list if ( !shadowers ) { continue; } // find a group in shadowerGroups to add these to // we will ignore everything but planenum, and we // can merge across areas optimizeGroup_t *check; for ( check = shadowerGroups ; check ; check = check->nextGroup ) { if ( check->planeNum == group->planeNum ) { break; } } if ( !check ) { check = (optimizeGroup_t *)Mem_Alloc( sizeof( *check ) ); *check = *group; check->triList = NULL; check->nextGroup = shadowerGroups; shadowerGroups = check; } // if any surface is a shadow-casting perforated or translucent surface, we // can't use the face removal optimizations because we can see through // some of the faces if ( group->material->Coverage() != MC_OPAQUE ) { hasPerforatedSurface = true; } check->triList = MergeTriLists( check->triList, shadowers ); } } } // take the shadower group list and create a beam tree and shadow volume light->shadowTris = CreateLightShadow( shadowerGroups, light ); if ( light->shadowTris && hasPerforatedSurface ) { // can't ever remove front faces, because we can see through some of them light->shadowTris->numShadowIndexesNoCaps = light->shadowTris->numShadowIndexesNoFrontCaps = light->shadowTris->numIndexes; } // we don't need the original shadower triangles for anything else FreeOptimizeGroupList( shadowerGroups ); }