/* ============ ProcessSubModel ============ */ void ProcessSubModel( ) { entity_t *e; int start, end; tree_t *tree; bspbrush_t *list; Vector mins, maxs; e = &entities[entity_num]; start = e->firstbrush; end = start + e->numbrushes; mins[0] = mins[1] = mins[2] = MIN_COORD_INTEGER; maxs[0] = maxs[1] = maxs[2] = MAX_COORD_INTEGER; list = MakeBspBrushList (start, end, mins, maxs, FULL_DETAIL); if (!nocsg) list = ChopBrushes (list); tree = BrushBSP (list, mins, maxs); // This would wind up crashing the engine because we'd have a negative leaf index in dmodel_t::headnode. if ( tree->headnode->planenum == PLANENUM_LEAF ) { const char *pClassName = ValueForKey( e, "classname" ); const char *pTargetName = ValueForKey( e, "targetname" ); Error( "bmodel %d has no head node (class '%s', targetname '%s')", entity_num, pClassName, pTargetName ); } MakeTreePortals (tree); #if DEBUG_BRUSHMODEL if ( entity_num == DEBUG_BRUSHMODEL ) WriteGLView( tree, "tree_all" ); #endif MarkVisibleSides (tree, start, end, FULL_DETAIL); MakeFaces (tree->headnode); FixTjuncs( tree->headnode, NULL ); WriteBSP( tree->headnode, NULL ); #if DEBUG_BRUSHMODEL if ( entity_num == DEBUG_BRUSHMODEL ) { WriteGLView( tree, "tree_vis" ); WriteGLViewFaces( tree, "tree_faces" ); } #endif FreeTree (tree); }
/* ============ ProcessWorldModel ============ */ void ProcessWorldModel (void) { entity_t *e; tree_t *tree; qboolean leaked; qboolean optimize; e = &entities[entity_num]; brush_start = e->firstbrush; brush_end = brush_start + e->numbrushes; leaked = false; // // perform per-block operations // if (block_xh * 1024 > map_maxs[0]) block_xh = floor(map_maxs[0]/1024.0); if ( (block_xl+1) * 1024 < map_mins[0]) block_xl = floor(map_mins[0]/1024.0); if (block_yh * 1024 > map_maxs[1]) block_yh = floor(map_maxs[1]/1024.0); if ( (block_yl+1) * 1024 < map_mins[1]) block_yl = floor(map_mins[1]/1024.0); if (block_xl <-4) block_xl = -4; if (block_yl <-4) block_yl = -4; if (block_xh > 3) block_xh = 3; if (block_yh > 3) block_yh = 3; for (optimize = false ; optimize <= true ; optimize++) { qprintf ("--------------------------------------------\n"); RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1), !verbose, ProcessBlock_Thread); // // build the division tree // oversizing the blocks guarantees that all the boundaries // will also get nodes. // qprintf ("--------------------------------------------\n"); tree = AllocTree (); tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1); tree->mins[0] = (block_xl)*1024; tree->mins[1] = (block_yl)*1024; tree->mins[2] = map_mins[2] - 8; tree->maxs[0] = (block_xh+1)*1024; tree->maxs[1] = (block_yh+1)*1024; tree->maxs[2] = map_maxs[2] + 8; // // perform the global operations // MakeTreePortals (tree); if (FloodEntities (tree)) FillOutside (tree->headnode); else { printf ("**** leaked ****\n"); leaked = true; LeakFile (tree); if (leaktest) { printf ("--- MAP LEAKED ---\n"); exit (0); } } MarkVisibleSides (tree, brush_start, brush_end); if (noopt || leaked) break; if (!optimize) { FreeTree (tree); } } FloodAreas (tree); if (glview) WriteGLView (tree, source); MakeFaces (tree->headnode); FixTjuncs (tree->headnode); if (!noprune) PruneNodes (tree->headnode); WriteBSP (tree->headnode); if (!leaked) WritePortalFile (tree); FreeTree (tree); }
void ProcessWorldModel (void) { entity_t *e; tree_t *tree = NULL; qboolean leaked; int optimize; int start; e = &entities[entity_num]; brush_start = e->firstbrush; brush_end = brush_start + e->numbrushes; leaked = false; // // perform per-block operations // if (block_xh * BLOCKS_SIZE > g_MainMap->map_maxs[0]) { block_xh = floor(g_MainMap->map_maxs[0]/BLOCKS_SIZE); } if ( (block_xl+1) * BLOCKS_SIZE < g_MainMap->map_mins[0]) { block_xl = floor(g_MainMap->map_mins[0]/BLOCKS_SIZE); } if (block_yh * BLOCKS_SIZE > g_MainMap->map_maxs[1]) { block_yh = floor(g_MainMap->map_maxs[1]/BLOCKS_SIZE); } if ( (block_yl+1) * BLOCKS_SIZE < g_MainMap->map_mins[1]) { block_yl = floor(g_MainMap->map_mins[1]/BLOCKS_SIZE); } // HLTOOLS: updated to +/- MAX_COORD_INTEGER ( new world size limits / worldsize.h ) if (block_xl < BLOCKS_MIN) { block_xl = BLOCKS_MIN; } if (block_yl < BLOCKS_MIN) { block_yl = BLOCKS_MIN; } if (block_xh > BLOCKS_MAX) { block_xh = BLOCKS_MAX; } if (block_yh > BLOCKS_MAX) { block_yh = BLOCKS_MAX; } for (optimize = 0 ; optimize <= 1 ; optimize++) { qprintf ("--------------------------------------------\n"); RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1), !verbose, ProcessBlock_Thread); // // build the division tree // oversizing the blocks guarantees that all the boundaries // will also get nodes. // qprintf ("--------------------------------------------\n"); tree = AllocTree (); tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1); tree->mins[0] = (block_xl)*BLOCKS_SIZE; tree->mins[1] = (block_yl)*BLOCKS_SIZE; tree->mins[2] = g_MainMap->map_mins[2] - 8; tree->maxs[0] = (block_xh+1)*BLOCKS_SIZE; tree->maxs[1] = (block_yh+1)*BLOCKS_SIZE; tree->maxs[2] = g_MainMap->map_maxs[2] + 8; // // perform the global operations // // make the portals/faces by traversing down to each empty leaf MakeTreePortals (tree); if (FloodEntities (tree)) { // turns everthing outside into solid FillOutside (tree->headnode); } else { Warning( ("**** leaked ****\n") ); leaked = true; LeakFile (tree); if (leaktest) { Warning( ("--- MAP LEAKED ---\n") ); exit (0); } } // mark the brush sides that actually turned into faces MarkVisibleSides (tree, brush_start, brush_end, NO_DETAIL); if (noopt || leaked) break; if (!optimize) { // If we are optimizing, free the tree. Next time we will construct it again, but // we'll use the information in MarkVisibleSides() so we'll only split with planes that // actually contribute renderable geometry FreeTree (tree); } } FloodAreas (tree); RemoveAreaPortalBrushes_R( tree->headnode ); start = Plat_FloatTime(); Msg("Building Faces..."); // this turns portals with one solid side into faces // it also subdivides each face if necessary to fit max lightmap dimensions MakeFaces (tree->headnode); Msg("done (%d)\n", (int)(Plat_FloatTime() - start) ); if (glview) { WriteGLView (tree, source); } AssignOccluderAreas( tree ); Compute3DSkyboxAreas( tree->headnode, g_SkyAreas ); face_t *pLeafFaceList = NULL; if ( !nodetail ) { pLeafFaceList = MergeDetailTree( tree, brush_start, brush_end ); } start = Plat_FloatTime(); Msg("FixTjuncs...\n"); // This unifies the vertex list for all edges (splits collinear edges to remove t-junctions) // It also welds the list of vertices out of each winding/portal and rounds nearly integer verts to integer pLeafFaceList = FixTjuncs (tree->headnode, pLeafFaceList); // this merges all of the solid nodes that have separating planes if (!noprune) { Msg("PruneNodes...\n"); PruneNodes (tree->headnode); } // Msg( "SplitSubdividedFaces...\n" ); // SplitSubdividedFaces( tree->headnode ); Msg("WriteBSP...\n"); WriteBSP (tree->headnode, pLeafFaceList); Msg("done (%d)\n", (int)(Plat_FloatTime() - start) ); if (!leaked) { WritePortalFile (tree); } FreeTree( tree ); FreeLeafFaces( pLeafFaceList ); }
/* ============ ProcessWorldModel ============ */ void ProcessWorldModel( void ) { entity_t *e; tree_t *tree; bspface_t *faces; qboolean leaked; BeginModel(); e = &entities[0]; e->firstDrawSurf = 0;//numMapDrawSurfs; // check for patches with adjacent edges that need to LOD together PatchMapDrawSurfs( e ); // build an initial bsp tree using all of the sides // of all of the structural brushes faces = MakeStructuralBspFaceList ( entities[0].brushes ); tree = FaceBSP( faces ); MakeTreePortals (tree); FilterStructuralBrushesIntoTree( e, tree ); // see if the bsp is completely enclosed if ( FloodEntities (tree) ) { // rebuild a better bsp tree using only the // sides that are visible from the inside FillOutside (tree->headnode); // chop the sides to the convex hull of // their visible fragments, giving us the smallest // polygons ClipSidesIntoTree( e, tree ); faces = MakeVisibleBspFaceList( entities[0].brushes ); FreeTree (tree); tree = FaceBSP( faces ); MakeTreePortals( tree ); FilterStructuralBrushesIntoTree( e, tree ); leaked = qfalse; } else { _printf ("**********************\n"); _printf ("******* leaked *******\n"); _printf ("**********************\n"); LeakFile (tree); if ( leaktest ) { _printf ("--- MAP LEAKED, ABORTING LEAKTEST ---\n"); exit (0); } leaked = qtrue; // chop the sides to the convex hull of // their visible fragments, giving us the smallest // polygons ClipSidesIntoTree( e, tree ); } // save out information for visibility processing NumberClusters( tree ); if ( !leaked ) { WritePortalFile( tree ); } if ( glview ) { // dump the portals for debugging WriteGLView( tree, source ); } FloodAreas (tree); // add references to the detail brushes FilterDetailBrushesIntoTree( e, tree ); // create drawsurfs for triangle models AddTriangleModels( tree ); // drawsurfs that cross fog boundaries will need to // be split along the bound if ( !nofog ) { FogDrawSurfs(); // may fragment drawsurfs } // subdivide each drawsurf as required by shader tesselation if ( !nosubdivide ) { SubdivideDrawSurfs( e, tree ); } // merge together all common shaders on the same plane and remove // all colinear points, so extra tjunctions won't be generated if ( !nomerge ) { MergeSides( e, tree ); // !@# testing } // add in any vertexes required to fix tjunctions if ( !notjunc ) { FixTJunctions( e ); } // allocate lightmaps for faces and patches AllocateLightmaps( e ); // add references to the final drawsurfs in the apropriate clusters FilterDrawsurfsIntoTree( e, tree ); EndModel( tree->headnode ); FreeTree (tree); }
/* ============ ProcessWorldModel ============ */ void ProcessWorldModel(void) { int s; entity_t *e; tree_t *tree; bspFace_t *faces; qboolean leaked; xmlNodePtr polyline, leaknode; char level[2]; const char *value; e = &entities[0]; e->firstDrawSurf = 0; //numMapDrawSurfs; // sets integer blockSize from worldspawn "_blocksize" key if it exists value = ValueForKey(e, "_blocksize"); if(value[0] == '\0') value = ValueForKey(e, "blocksize"); if(value[0] == '\0') value = ValueForKey(e, "chopsize"); // sof2 if(value[0] != '\0') { // scan 3 numbers s = sscanf(value, "%d %d %d", &blockSize[0], &blockSize[1], &blockSize[2]); // handle legacy case if(s == 1) { blockSize[1] = blockSize[0]; blockSize[2] = blockSize[0]; } } Sys_Printf("block size = { %d %d %d }\n", blockSize[0], blockSize[1], blockSize[2]); BeginModel(e); // check for patches with adjacent edges that need to LOD together PatchMapDrawSurfs(e); // build an initial bsp tree using all of the sides // of all of the structural brushes faces = MakeStructuralBspFaceList(entities[0].brushes); tree = FaceBSP(faces); MakeTreePortals(tree); FilterStructuralBrushesIntoTree(e, tree); if(drawFlag) { // draw unoptimized portals in new window drawTree = tree; Draw_Scene(DrawTree); } // see if the bsp is completely enclosed if(FloodEntities(tree)) { // rebuild a better bsp tree using only the // sides that are visible from the inside FillOutside(tree->headnode); // chop the sides to the convex hull of // their visible fragments, giving us the smallest // polygons ClipSidesIntoTree(e, tree); faces = MakeVisibleBspFaceList(entities[0].brushes); FreeTree(tree); tree = FaceBSP(faces); MakeTreePortals(tree); FilterStructuralBrushesIntoTree(e, tree); leaked = qfalse; } else { Sys_FPrintf(SYS_NOXML, "**********************\n"); Sys_FPrintf(SYS_NOXML, "******* leaked *******\n"); Sys_FPrintf(SYS_NOXML, "**********************\n"); polyline = LeakFile(tree); leaknode = xmlNewNode(NULL, "message"); xmlNodeSetContent(leaknode, "MAP LEAKED\n"); xmlAddChild(leaknode, polyline); level[0] = (int)'0' + SYS_ERR; level[1] = 0; xmlSetProp(leaknode, "level", (char *)&level); xml_SendNode(leaknode); if(leaktest) { Sys_Printf("--- MAP LEAKED, ABORTING LEAKTEST ---\n"); exit(0); } leaked = qtrue; // chop the sides to the convex hull of // their visible fragments, giving us the smallest // polygons ClipSidesIntoTree(e, tree); } // save out information for visibility processing NumberClusters(tree); if(!leaked) { WritePortalFile(tree); } if(glview) { // dump the portals for debugging WriteGLView(tree, source); } FloodAreas(tree); if(drawFlag) { // draw optimized portals in new window drawTree = tree; Draw_Scene(DrawTree); } // add references to the detail brushes FilterDetailBrushesIntoTree(e, tree); // create drawsurfs for triangle models AddTriangleModels(); // drawsurfs that cross fog boundaries will need to // be split along the bound if(!nofog) { FogDrawSurfs(); // may fragment drawsurfs } // subdivide each drawsurf as required by shader tesselation if(!nosubdivide) { SubdivideDrawSurfs(e, tree); } // merge together all common shaders on the same plane and remove // all colinear points, so extra tjunctions won't be generated if(!nomerge) { MergeSides(e, tree); // !@# testing } // add in any vertexes required to fix tjunctions if(!notjunc) { FixTJunctions(e); } // allocate lightmaps for faces and patches AllocateLightmaps(e); // add references to the final drawsurfs in the apropriate clusters FilterDrawsurfsIntoTree(e, tree); EndModel(e, tree->headnode); FreeTree(tree); }
/* ============ ProcessModel ============ */ bool ProcessModel( uEntity_t* e, bool floodFill ) { bspface_t* faces; // build a bsp tree using all of the sides // of all of the structural brushes faces = MakeStructuralBspFaceList( e->primitives ); // RB: dump BSP for debugging if( dmapGlobals.glview ) { WriteGLView( faces, "facelist" ); } // RB end e->tree = FaceBSP( faces ); // create portals at every leaf intersection // to allow flood filling MakeTreePortals( e->tree ); // RB: calculate node numbers for split plane analysis NumberNodes_r( e->tree->headnode, 0 ); // classify the leafs as opaque or areaportal FilterBrushesIntoTree( e ); // RB: use mapTri_t by MapPolygonMesh primitives in case we don't use brushes FilterMeshesIntoTree( e ); // RB: dump BSP for debugging //if( dmapGlobals.glview ) //{ //WriteGLView( e->tree, "unclipped", dmapGlobals.entityNum ); //} // RB end // see if the bsp is completely enclosed if( floodFill && !dmapGlobals.noFlood ) { if( FloodEntities( e->tree ) ) { // set the outside leafs to opaque FillOutside( e ); } else { common->Printf( "**********************\n" ); common->Warning( "******* leaked *******" ); common->Printf( "**********************\n" ); LeakFile( e->tree ); // bail out here. If someone really wants to // process a map that leaks, they should use // -noFlood return false; } } // get minimum convex hulls for each visible side // this must be done before creating area portals, // because the visible hull is used as the portal ClipSidesByTree( e ); // determine areas before clipping tris into the // tree, so tris will never cross area boundaries FloodAreas( e ); // RB: dump BSP for debugging if( dmapGlobals.glview ) { WriteGLView( e->tree, "areas", dmapGlobals.entityNum ); } // RB end // we now have a BSP tree with solid and non-solid leafs marked with areas // all primitives will now be clipped into this, throwing away // fragments in the solid areas PutPrimitivesInAreas( e ); // now build shadow volumes for the lights and split // the optimize lists by the light beam trees // so there won't be unneeded overdraw in the static // case Prelight( e ); // optimizing is a superset of fixing tjunctions if( !dmapGlobals.noOptimize ) { OptimizeEntity( e ); } else if( !dmapGlobals.noTJunc ) { FixEntityTjunctions( e ); } // now fix t junctions across areas FixGlobalTjunctions( e ); return true; }
/* ============ Dmap ============ */ void Dmap( const idCmdArgs& args ) { int i; int start, end; char path[1024]; idStr passedName; bool leaked = false; bool noCM = false; bool noAAS = false; ResetDmapGlobals(); if( args.Argc() < 2 ) { DmapHelp(); return; } common->Printf( "---- dmap ----\n" ); dmapGlobals.fullCarve = true; dmapGlobals.shadowOptLevel = SO_MERGE_SURFACES; // create shadows by merging all surfaces, but no super optimization // dmapGlobals.shadowOptLevel = SO_CLIP_OCCLUDERS; // remove occluders that are completely covered // dmapGlobals.shadowOptLevel = SO_SIL_OPTIMIZE; // dmapGlobals.shadowOptLevel = SO_CULL_OCCLUDED; dmapGlobals.noLightCarve = true; for( i = 1 ; i < args.Argc() ; i++ ) { const char* s; s = args.Argv( i ); if( s[0] == '-' ) { s++; if( s[0] == '\0' ) { continue; } } if( !idStr::Icmp( s, "glview" ) ) { dmapGlobals.glview = true; } else if( !idStr::Icmp( s, "v" ) ) { common->Printf( "verbose = true\n" ); dmapGlobals.verbose = true; } else if( !idStr::Icmp( s, "draw" ) ) { common->Printf( "drawflag = true\n" ); dmapGlobals.drawflag = true; } else if( !idStr::Icmp( s, "noFlood" ) ) { common->Printf( "noFlood = true\n" ); dmapGlobals.noFlood = true; } else if( !idStr::Icmp( s, "noLightCarve" ) ) { common->Printf( "noLightCarve = true\n" ); dmapGlobals.noLightCarve = true; } else if( !idStr::Icmp( s, "lightCarve" ) ) { common->Printf( "noLightCarve = false\n" ); dmapGlobals.noLightCarve = false; } else if( !idStr::Icmp( s, "noOpt" ) ) { common->Printf( "noOptimize = true\n" ); dmapGlobals.noOptimize = true; } else if( !idStr::Icmp( s, "verboseentities" ) ) { common->Printf( "verboseentities = true\n" ); dmapGlobals.verboseentities = true; } else if( !idStr::Icmp( s, "noCurves" ) ) { common->Printf( "noCurves = true\n" ); dmapGlobals.noCurves = true; } else if( !idStr::Icmp( s, "noModels" ) ) { common->Printf( "noModels = true\n" ); dmapGlobals.noModelBrushes = true; } else if( !idStr::Icmp( s, "noClipSides" ) ) { common->Printf( "noClipSides = true\n" ); dmapGlobals.noClipSides = true; } else if( !idStr::Icmp( s, "noCarve" ) ) { common->Printf( "noCarve = true\n" ); dmapGlobals.fullCarve = false; } else if( !idStr::Icmp( s, "shadowOpt" ) ) { dmapGlobals.shadowOptLevel = ( shadowOptLevel_t )atoi( args.Argv( i + 1 ) ); common->Printf( "shadowOpt = %i\n", dmapGlobals.shadowOptLevel ); i += 1; } else if( !idStr::Icmp( s, "noTjunc" ) ) { // triangle optimization won't work properly without tjunction fixing common->Printf( "noTJunc = true\n" ); dmapGlobals.noTJunc = true; dmapGlobals.noOptimize = true; common->Printf( "forcing noOptimize = true\n" ); } else if( !idStr::Icmp( s, "noCM" ) ) { noCM = true; common->Printf( "noCM = true\n" ); } else if( !idStr::Icmp( s, "noAAS" ) ) { noAAS = true; common->Printf( "noAAS = true\n" ); } else { break; } } if( i >= args.Argc() ) { common->Error( "usage: dmap [options] mapfile" ); } passedName = args.Argv( i ); // may have an extension passedName.BackSlashesToSlashes(); if( passedName.Icmpn( "maps/", 4 ) != 0 ) { passedName = "maps/" + passedName; } idStr stripped = passedName; stripped.StripFileExtension(); idStr::Copynz( dmapGlobals.mapFileBase, stripped, sizeof( dmapGlobals.mapFileBase ) ); bool region = false; // if this isn't a regioned map, delete the last saved region map if( passedName.Right( 4 ) != ".reg" ) { sprintf( path, "%s.reg", dmapGlobals.mapFileBase ); fileSystem->RemoveFile( path ); } else { region = true; } passedName = stripped; // delete any old line leak files sprintf( path, "%s.lin", dmapGlobals.mapFileBase ); fileSystem->RemoveFile( path ); // delete any old generated binary proc files idStr generated = va( "generated/%s.bproc", dmapGlobals.mapFileBase ); fileSystem->RemoveFile( generated.c_str() ); // // start from scratch // start = Sys_Milliseconds(); if( !LoadDMapFile( passedName ) ) { return; } if( ProcessModels() ) { WriteOutputFile(); // RB: dump BSP after nodes being pruned and optimized if( dmapGlobals.glview ) { uEntity_t* world = &dmapGlobals.uEntities[0]; WriteGLView( world->tree, "pruned", 0, true ); } // RB end } else { leaked = true; } FreeDMapFile(); common->Printf( "%i total shadow triangles\n", dmapGlobals.totalShadowTriangles ); common->Printf( "%i total shadow verts\n", dmapGlobals.totalShadowVerts ); end = Sys_Milliseconds(); common->Printf( "-----------------------\n" ); common->Printf( "%5.0f seconds for dmap\n", ( end - start ) * 0.001f ); if( !leaked ) { if( !noCM ) { // make sure the collision model manager is not used by the game cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" ); // create the collision map start = Sys_Milliseconds(); collisionModelManager->LoadMap( dmapGlobals.dmapFile ); collisionModelManager->FreeMap(); end = Sys_Milliseconds(); common->Printf( "-------------------------------------\n" ); common->Printf( "%5.0f seconds to create collision map\n", ( end - start ) * 0.001f ); } if( !noAAS && !region ) { // create AAS files RunAAS_f( args ); } } // free the common .map representation delete dmapGlobals.dmapFile; // clear the map plane list dmapGlobals.mapPlanes.Clear(); }