2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
29 #include "../../../idlib/precompiled.h"
35 #define TEXTURE_OFFSET_EQUAL_EPSILON 0.005
36 #define TEXTURE_VECTOR_EQUAL_EPSILON 0.001
42 The triList is appended to the apropriate optimzeGroup_t,
43 creating a new one if needed.
44 The entire list is assumed to come from the same planar primitive
47 static void AddTriListToArea( uEntity_t *e, mapTri_t *triList, int planeNum, int areaNum, textureVectors_t *texVec ) {
49 optimizeGroup_t *group;
56 area = &e->areas[areaNum];
57 for ( group = area->groups ; group ; group = group->nextGroup ) {
58 if ( group->material == triList->material
59 && group->planeNum == planeNum
60 && group->mergeGroup == triList->mergeGroup ) {
61 // check the texture vectors
62 for ( i = 0 ; i < 2 ; i++ ) {
63 for ( j = 0 ; j < 3 ; j++ ) {
64 if ( idMath::Fabs( texVec->v[i][j] - group->texVec.v[i][j] ) > TEXTURE_VECTOR_EQUAL_EPSILON ) {
71 if ( idMath::Fabs( texVec->v[i][3] - group->texVec.v[i][3] ) > TEXTURE_OFFSET_EQUAL_EPSILON ) {
78 // different texture offsets
79 i = 1; // just for debugger breakpoint
85 group = (optimizeGroup_t *)Mem_Alloc( sizeof( *group ) );
86 memset( group, 0, sizeof( *group ) );
87 group->planeNum = planeNum;
88 group->mergeGroup = triList->mergeGroup;
89 group->material = triList->material;
90 group->nextGroup = area->groups;
91 group->texVec = *texVec;
95 group->triList = MergeTriLists( group->triList, triList );
103 static void TexVecForTri( textureVectors_t *texVec, mapTri_t *tri ) {
107 idDrawVert *a, *b, *c;
113 d0[0] = b->xyz[0] - a->xyz[0];
114 d0[1] = b->xyz[1] - a->xyz[1];
115 d0[2] = b->xyz[2] - a->xyz[2];
116 d0[3] = b->st[0] - a->st[0];
117 d0[4] = b->st[1] - a->st[1];
119 d1[0] = c->xyz[0] - a->xyz[0];
120 d1[1] = c->xyz[1] - a->xyz[1];
121 d1[2] = c->xyz[2] - a->xyz[2];
122 d1[3] = c->st[0] - a->st[0];
123 d1[4] = c->st[1] - a->st[1];
125 area = d0[3] * d1[4] - d0[4] * d1[3];
128 temp[0] = (d0[0] * d1[4] - d0[4] * d1[0]) * inva;
129 temp[1] = (d0[1] * d1[4] - d0[4] * d1[1]) * inva;
130 temp[2] = (d0[2] * d1[4] - d0[4] * d1[2]) * inva;
132 texVec->v[0].ToVec3() = temp;
133 texVec->v[0][3] = tri->v[0].xyz * texVec->v[0].ToVec3() - tri->v[0].st[0];
135 temp[0] = (d0[3] * d1[0] - d0[0] * d1[3]) * inva;
136 temp[1] = (d0[3] * d1[1] - d0[1] * d1[3]) * inva;
137 temp[2] = (d0[3] * d1[2] - d0[2] * d1[3]) * inva;
139 texVec->v[1].ToVec3() = temp;
140 texVec->v[1][3] = tri->v[0].xyz * texVec->v[0].ToVec3() - tri->v[0].st[1];
149 //#define SNAP_FLOAT_TO_INT 8
150 #define SNAP_FLOAT_TO_INT 256
151 #define SNAP_INT_TO_FLOAT (1.0/SNAP_FLOAT_TO_INT)
153 mapTri_t *TriListForSide( const side_t *s, const idWinding *w ) {
156 mapTri_t *tri, *triList;
158 const idMaterial *si;
162 // skip any generated faces
167 // don't create faces for non-visible sides
168 if ( !si->SurfaceCastsShadow() && !si->IsDrawn() ) {
173 // triangle fan using only the outer verts
174 // this gives the minimum triangle count,
175 // but may have some very distended triangles
177 for ( i = 2 ; i < w->GetNumPoints() ; i++ ) {
183 for ( j = 0 ; j < 3 ; j++ ) {
185 vec = &((*w)[0]).ToVec3();
186 } else if ( j == 1 ) {
187 vec = &((*w)[i-1]).ToVec3();
189 vec = &((*w)[i]).ToVec3();
194 // round the xyz to a given precision
195 for ( k = 0 ; k < 3 ; k++ ) {
196 dv->xyz[k] = SNAP_INT_TO_FLOAT * floor( vec[k] * SNAP_FLOAT_TO_INT + 0.5 );
199 VectorCopy( *vec, dv->xyz );
202 // calculate texture s/t from brush primitive texture matrix
203 dv->st[0] = DotProduct( dv->xyz, s->texVec.v[0] ) + s->texVec.v[0][3];
204 dv->st[1] = DotProduct( dv->xyz, s->texVec.v[1] ) + s->texVec.v[1][3];
207 dv->normal = dmapGlobals.mapPlanes[s->planenum].Normal();
208 if ( dv->normal.Length() < 0.9 || dv->normal.Length() > 1.1 ) {
209 common->Error( "Bad normal in TriListForSide" );
214 // triangle fan from central point, more verts and tris, but less distended
215 // I use this when debugging some tjunction problems
217 for ( i = 0 ; i < w->GetNumPoints() ; i++ ) {
225 for ( j = 0 ; j < 3 ; j++ ) {
228 midPoint = w->GetCenter();
229 } else if ( j == 1 ) {
230 vec = &((*w)[i]).ToVec3();
232 vec = &((*w)[(i+1)%w->GetNumPoints()]).ToVec3();
237 VectorCopy( *vec, dv->xyz );
239 // calculate texture s/t from brush primitive texture matrix
240 dv->st[0] = DotProduct( dv->xyz, s->texVec.v[0] ) + s->texVec.v[0][3];
241 dv->st[1] = DotProduct( dv->xyz, s->texVec.v[1] ) + s->texVec.v[1][3];
244 dv->normal = dmapGlobals.mapPlanes[s->planenum].Normal();
245 if ( dv->normal.Length() < 0.9f || dv->normal.Length() > 1.1f ) {
246 common->Error( "Bad normal in TriListForSide" );
252 // set merge groups if needed, to prevent multiple sides from being
253 // merged into a single surface in the case of gui shaders, mirrors, and autosprites
254 if ( s->material->IsDiscrete() ) {
255 for ( tri = triList ; tri ; tri = tri->next ) {
256 tri->mergeGroup = (void *)s;
263 //=================================================================================
269 Adds non-opaque leaf fragments to the convex hull
272 static void ClipSideByTree_r( idWinding *w, side_t *side, node_t *node ) {
273 idWinding *front, *back;
279 if ( node->planenum != PLANENUM_LEAF ) {
280 if ( side->planenum == node->planenum ) {
281 ClipSideByTree_r( w, side, node->children[0] );
284 if ( side->planenum == ( node->planenum ^ 1) ) {
285 ClipSideByTree_r( w, side, node->children[1] );
289 w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back );
292 ClipSideByTree_r( front, side, node->children[0] );
293 ClipSideByTree_r( back, side, node->children[1] );
298 // if opaque leaf, don't add
299 if ( !node->opaque ) {
300 if ( !side->visibleHull ) {
301 side->visibleHull = w->Copy();
304 side->visibleHull->AddToConvexHull( w, dmapGlobals.mapPlanes[ side->planenum ].Normal() );
314 =====================
317 Creates side->visibleHull for all visible sides
319 The visible hull for a side will consist of the convex hull of
320 all points in non-opaque clusters, which allows overlaps
321 to be trimmed off automatically.
322 =====================
324 void ClipSidesByTree( uEntity_t *e ) {
331 common->Printf( "----- ClipSidesByTree -----\n");
333 for ( prim = e->primitives ; prim ; prim = prim->next ) {
336 // FIXME: other primitives!
339 for ( i = 0 ; i < b->numsides ; i++ ) {
341 if ( !side->winding) {
344 w = side->winding->Copy();
345 side->visibleHull = NULL;
346 ClipSideByTree_r( w, side, e->tree->headnode );
347 // for debugging, we can choose to use the entire original side
348 // but we skip this if the side was completely clipped away
349 if ( side->visibleHull && dmapGlobals.noClipSides ) {
350 delete side->visibleHull;
351 side->visibleHull = side->winding->Copy();
359 //=================================================================================
365 This is used for adding curve triangles
366 The winding will be freed before it returns
369 void ClipTriIntoTree_r( idWinding *w, mapTri_t *originalTri, uEntity_t *e, node_t *node ) {
370 idWinding *front, *back;
376 if ( node->planenum != PLANENUM_LEAF ) {
377 w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back );
380 ClipTriIntoTree_r( front, originalTri, e, node->children[0] );
381 ClipTriIntoTree_r( back, originalTri, e, node->children[1] );
386 // if opaque leaf, don't add
387 if ( !node->opaque && node->area >= 0 ) {
391 textureVectors_t texVec;
393 list = WindingToTriList( w, originalTri );
395 PlaneForTri( originalTri, plane );
396 planeNum = FindFloatPlane( plane );
398 TexVecForTri( &texVec, originalTri );
400 AddTriListToArea( e, list, planeNum, node->area, &texVec );
409 //=============================================================
413 CheckWindingInAreas_r
415 Returns the area number that the winding is in, or
416 -2 if it crosses multiple areas.
420 static int CheckWindingInAreas_r( const idWinding *w, node_t *node ) {
421 idWinding *front, *back;
427 if ( node->planenum != PLANENUM_LEAF ) {
430 if ( side->planenum == node->planenum ) {
431 return CheckWindingInAreas_r( w, node->children[0] );
433 if ( side->planenum == ( node->planenum ^ 1) ) {
434 return CheckWindingInAreas_r( w, node->children[1] );
437 w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back );
439 a1 = CheckWindingInAreas_r( front, node->children[0] );
441 a2 = CheckWindingInAreas_r( back, node->children[1] );
444 if ( a1 == -2 || a2 == -2 ) {
445 return -2; // different
448 return a2; // one solid
451 return a1; // one solid
455 return -2; // cross areas
467 PutWindingIntoAreas_r
469 Clips a winding down into the bsp tree, then converts
470 the fragments to triangles and adds them to the area lists
473 static void PutWindingIntoAreas_r( uEntity_t *e, const idWinding *w, side_t *side, node_t *node ) {
474 idWinding *front, *back;
481 if ( node->planenum != PLANENUM_LEAF ) {
482 if ( side->planenum == node->planenum ) {
483 PutWindingIntoAreas_r( e, w, side, node->children[0] );
486 if ( side->planenum == ( node->planenum ^ 1) ) {
487 PutWindingIntoAreas_r( e, w, side, node->children[1] );
491 // see if we need to split it
492 // adding the "noFragment" flag to big surfaces like sky boxes
493 // will avoid potentially dicing them up into tons of triangles
494 // that take forever to optimize back together
495 if ( !dmapGlobals.fullCarve || side->material->NoFragment() ) {
496 area = CheckWindingInAreas_r( w, node );
500 // put in single area
501 tri = TriListForSide( side, w );
502 AddTriListToArea( e, tri, side->planenum, area, &side->texVec );
507 w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back );
509 PutWindingIntoAreas_r( e, front, side, node->children[0] );
514 PutWindingIntoAreas_r( e, back, side, node->children[1] );
522 // if opaque leaf, don't add
523 if ( node->area >= 0 && !node->opaque ) {
526 tri = TriListForSide( side, w );
527 AddTriListToArea( e, tri, side->planenum, node->area, &side->texVec );
535 Used for curves and inlined models
538 void AddMapTriToAreas( mapTri_t *tri, uEntity_t *e ) {
542 // skip degenerate triangles from pinched curves
543 if ( MapTriArea( tri ) <= 0 ) {
547 if ( dmapGlobals.fullCarve ) {
548 // always fragment into areas
549 w = WindingForTri( tri );
550 ClipTriIntoTree_r( w, tri, e, e->tree->headnode );
554 w = WindingForTri( tri );
555 area = CheckWindingInAreas_r( w, e->tree->headnode );
564 textureVectors_t texVec;
566 // put in single area
567 newTri = CopyMapTri( tri );
570 PlaneForTri( tri, plane );
571 planeNum = FindFloatPlane( plane );
573 TexVecForTri( &texVec, newTri );
575 AddTriListToArea( e, newTri, planeNum, area, &texVec );
577 // fragment into areas
578 w = WindingForTri( tri );
579 ClipTriIntoTree_r( w, tri, e, e->tree->headnode );
584 =====================
587 =====================
589 void PutPrimitivesInAreas( uEntity_t *e ) {
596 common->Printf( "----- PutPrimitivesInAreas -----\n");
598 // allocate space for surface chains for each area
599 e->areas = (uArea_t *)Mem_Alloc( e->numAreas * sizeof( e->areas[0] ) );
600 memset( e->areas, 0, e->numAreas * sizeof( e->areas[0] ) );
602 // for each primitive, clip it to the non-solid leafs
603 // and divide it into different areas
604 for ( prim = e->primitives ; prim ; prim = prim->next ) {
608 // add curve triangles
609 for ( tri = prim->tris ; tri ; tri = tri->next ) {
610 AddMapTriToAreas( tri, e );
615 // clip in brush sides
616 for ( i = 0 ; i < b->numsides ; i++ ) {
618 if ( !side->visibleHull ) {
621 PutWindingIntoAreas_r( e, side->visibleHull, side, e->tree->headnode );
626 // optionally inline some of the func_static models
627 if ( dmapGlobals.entityNum == 0 ) {
628 bool inlineAll = dmapGlobals.uEntities[0].mapEntity->epairs.GetBool( "inlineAllStatics" );
630 for ( int eNum = 1 ; eNum < dmapGlobals.num_entities ; eNum++ ) {
631 uEntity_t *entity = &dmapGlobals.uEntities[eNum];
632 const char *className = entity->mapEntity->epairs.GetString( "classname" );
633 if ( idStr::Icmp( className, "func_static" ) ) {
636 if ( !entity->mapEntity->epairs.GetBool( "inline" ) && !inlineAll ) {
639 const char *modelName = entity->mapEntity->epairs.GetString( "model" );
643 idRenderModel *model = renderModelManager->FindModel( modelName );
645 common->Printf( "inlining %s.\n", entity->mapEntity->epairs.GetString( "name" ) );
648 // get the rotation matrix in either full form, or single angle form
649 if ( !entity->mapEntity->epairs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) {
650 float angle = entity->mapEntity->epairs.GetFloat( "angle" );
651 if ( angle != 0.0f ) {
652 axis = idAngles( 0.0f, angle, 0.0f ).ToMat3();
658 idVec3 origin = entity->mapEntity->epairs.GetVector( "origin" );
660 for ( i = 0 ; i < model->NumSurfaces() ; i++ ) {
661 const modelSurface_t *surface = model->Surface( i );
662 const srfTriangles_t *tri = surface->geometry;
665 memset( &mapTri, 0, sizeof( mapTri ) );
666 mapTri.material = surface->shader;
667 // don't let discretes (autosprites, etc) merge together
668 if ( mapTri.material->IsDiscrete() ) {
669 mapTri.mergeGroup = (void *)surface;
671 for ( int j = 0 ; j < tri->numIndexes ; j += 3 ) {
672 for ( int k = 0 ; k < 3 ; k++ ) {
673 idVec3 v = tri->verts[tri->indexes[j+k]].xyz;
675 mapTri.v[k].xyz = v * axis + origin;
677 mapTri.v[k].normal = tri->verts[tri->indexes[j+k]].normal * axis;
678 mapTri.v[k].st = tri->verts[tri->indexes[j+k]].st;
680 AddMapTriToAreas( &mapTri, e );
687 //============================================================================
693 Carves a triangle by the frustom planes of a light, producing
694 a (possibly empty) list of triangles on the inside and outside.
696 The original triangle is not modified.
698 If no clipping is required, the result will be a copy of the original.
700 If clipping was required, the outside fragments will be planar clips, which
701 will benefit from re-optimization.
704 static void ClipTriByLight( const mapLight_t *light, const mapTri_t *tri,
705 mapTri_t **in, mapTri_t **out ) {
706 idWinding *inside, *oldInside;
707 idWinding *outside[6];
714 // clip this winding to the light
715 inside = WindingForTri( tri );
717 for ( i = 0 ; i < 6 ; i++ ) {
720 oldInside->Split( light->def.frustum[i], 0, &outside[i], &inside );
732 // the entire winding is outside this light
734 // free the clipped fragments
735 for ( i = 0 ; i < 6 ; i++ ) {
741 *out = CopyMapTri( tri );
748 // the entire winding is inside this light
750 // free the inside copy
753 *in = CopyMapTri( tri );
759 // the winding is split
760 *in = WindingToTriList( inside, tri );
763 // combine all the outside fragments
764 for ( i = 0 ; i < 6 ; i++ ) {
768 list = WindingToTriList( outside[i], tri );
770 *out = MergeTriLists( *out, list );
780 static void BoundOptimizeGroup( optimizeGroup_t *group ) {
781 group->bounds.Clear();
782 for ( mapTri_t *tri = group->triList ; tri ; tri = tri->next ) {
783 group->bounds.AddPoint( tri->v[0].xyz );
784 group->bounds.AddPoint( tri->v[1].xyz );
785 group->bounds.AddPoint( tri->v[2].xyz );
793 Build the beam tree and shadow volume surface for a light
796 static void BuildLightShadows( uEntity_t *e, mapLight_t *light ) {
798 optimizeGroup_t *group;
801 optimizeGroup_t *shadowerGroups;
803 bool hasPerforatedSurface = false;
806 // build a group list of all the triangles that will contribute to
807 // the optimized shadow volume, leaving the original triangles alone
811 // shadowers will contain all the triangles that will contribute to the
813 shadowerGroups = NULL;
814 lightOrigin = light->def.globalLightOrigin;
816 // if the light is no-shadows, don't add any surfaces
817 // to the beam tree at all
818 if ( !light->def.parms.noShadows
819 && light->def.lightShader->LightCastsShadows() ) {
820 for ( i = 0 ; i < e->numAreas ; i++ ) {
821 for ( group = e->areas[i].groups ; group ; group = group->nextGroup ) {
822 // if the surface doesn't cast shadows, skip it
823 if ( !group->material->SurfaceCastsShadow() ) {
827 // if the group doesn't face away from the light, it
828 // won't contribute to the shadow volume
829 if ( dmapGlobals.mapPlanes[ group->planeNum ].Distance( lightOrigin ) > 0 ) {
833 // if the group bounds doesn't intersect the light bounds,
835 if ( !group->bounds.IntersectsBounds( light->def.frustumTris->bounds ) ) {
839 // build up a list of the triangle fragments inside the
842 for ( tri = group->triList ; tri ; tri = tri->next ) {
845 // clip it to the light frustum
846 ClipTriByLight( light, tri, &in, &out );
848 shadowers = MergeTriLists( shadowers, in );
851 // if we didn't get any out of this group, we don't
852 // need to create a new group in the shadower list
857 // find a group in shadowerGroups to add these to
858 // we will ignore everything but planenum, and we
859 // can merge across areas
860 optimizeGroup_t *check;
862 for ( check = shadowerGroups ; check ; check = check->nextGroup ) {
863 if ( check->planeNum == group->planeNum ) {
868 check = (optimizeGroup_t *)Mem_Alloc( sizeof( *check ) );
870 check->triList = NULL;
871 check->nextGroup = shadowerGroups;
872 shadowerGroups = check;
875 // if any surface is a shadow-casting perforated or translucent surface, we
876 // can't use the face removal optimizations because we can see through
878 if ( group->material->Coverage() != MC_OPAQUE ) {
879 hasPerforatedSurface = true;
882 check->triList = MergeTriLists( check->triList, shadowers );
887 // take the shadower group list and create a beam tree and shadow volume
888 light->shadowTris = CreateLightShadow( shadowerGroups, light );
890 if ( light->shadowTris && hasPerforatedSurface ) {
891 // can't ever remove front faces, because we can see through some of them
892 light->shadowTris->numShadowIndexesNoCaps = light->shadowTris->numShadowIndexesNoFrontCaps =
893 light->shadowTris->numIndexes;
896 // we don't need the original shadower triangles for anything else
897 FreeOptimizeGroupList( shadowerGroups );
905 Divide each group into an inside group and an outside group, based
906 on which fragments are illuminated by the light's beam tree
909 static void CarveGroupsByLight( uEntity_t *e, mapLight_t *light ) {
911 optimizeGroup_t *group, *newGroup, *carvedGroups, *nextGroup;
912 mapTri_t *tri, *inside, *outside;
915 for ( i = 0 ; i < e->numAreas ; i++ ) {
919 // we will be either freeing or reassigning the groups as we go
920 for ( group = area->groups ; group ; group = nextGroup ) {
921 nextGroup = group->nextGroup;
922 // if the surface doesn't get lit, don't carve it up
923 if ( ( light->def.lightShader->IsFogLight() && !group->material->ReceivesFog() )
924 || ( !light->def.lightShader->IsFogLight() && !group->material->ReceivesLighting() )
925 || !group->bounds.IntersectsBounds( light->def.frustumTris->bounds ) ) {
927 group->nextGroup = carvedGroups;
928 carvedGroups = group;
932 if ( group->numGroupLights == MAX_GROUP_LIGHTS ) {
933 common->Error( "MAX_GROUP_LIGHTS around %f %f %f",
934 group->triList->v[0].xyz[0], group->triList->v[0].xyz[1], group->triList->v[0].xyz[2] );
937 // if the group doesn't face the light,
938 // it won't get carved at all
939 if ( !light->def.lightShader->LightEffectsBackSides() &&
940 !group->material->ReceivesLightingOnBackSides() &&
941 dmapGlobals.mapPlanes[ group->planeNum ].Distance( light->def.parms.origin ) <= 0 ) {
943 group->nextGroup = carvedGroups;
944 carvedGroups = group;
948 // split into lists for hit-by-light, and not-hit-by-light
952 for ( tri = group->triList ; tri ; tri = tri->next ) {
955 ClipTriByLight( light, tri, &in, &out );
956 inside = MergeTriLists( inside, in );
957 outside = MergeTriLists( outside, out );
961 newGroup = (optimizeGroup_t *)Mem_Alloc( sizeof( *newGroup ) );
963 newGroup->groupLights[newGroup->numGroupLights] = light;
964 newGroup->numGroupLights++;
965 newGroup->triList = inside;
966 newGroup->nextGroup = carvedGroups;
967 carvedGroups = newGroup;
971 newGroup = (optimizeGroup_t *)Mem_Alloc( sizeof( *newGroup ) );
973 newGroup->triList = outside;
974 newGroup->nextGroup = carvedGroups;
975 carvedGroups = newGroup;
979 group->nextGroup = NULL;
980 FreeOptimizeGroupList( group );
983 // replace this area's group list with the new one
984 area->groups = carvedGroups;
989 =====================
992 Break optimize groups up into additional groups at light boundaries, so
993 optimization won't cross light bounds
994 =====================
996 void Prelight( uEntity_t *e ) {
1001 // don't prelight anything but the world entity
1002 if ( dmapGlobals.entityNum != 0 ) {
1006 if ( dmapGlobals.shadowOptLevel > 0 ) {
1007 common->Printf( "----- BuildLightShadows -----\n" );
1008 start = Sys_Milliseconds();
1010 // calc bounds for all the groups to speed things up
1011 for ( i = 0 ; i < e->numAreas ; i++ ) {
1012 uArea_t *area = &e->areas[i];
1014 for ( optimizeGroup_t *group = area->groups ; group ; group = group->nextGroup ) {
1015 BoundOptimizeGroup( group );
1019 for ( i = 0 ; i < dmapGlobals.mapLights.Num() ; i++ ) {
1020 light = dmapGlobals.mapLights[i];
1021 BuildLightShadows( e, light );
1024 end = Sys_Milliseconds();
1025 common->Printf( "%5.1f seconds for BuildLightShadows\n", ( end - start ) / 1000.0 );
1029 if ( !dmapGlobals.noLightCarve ) {
1030 common->Printf( "----- CarveGroupsByLight -----\n" );
1031 start = Sys_Milliseconds();
1032 // now subdivide the optimize groups into additional groups for
1033 // each light that illuminates them
1034 for ( i = 0 ; i < dmapGlobals.mapLights.Num() ; i++ ) {
1035 light = dmapGlobals.mapLights[i];
1036 CarveGroupsByLight( e, light );
1039 end = Sys_Milliseconds();
1040 common->Printf( "%5.1f seconds for CarveGroupsByLight\n", ( end - start ) / 1000.0 );