Exemple #1
0
/*
 * ProcessWorldModel
 */
static void ProcessWorldModel(void){
	entity_t *e;
	tree_t *tree;
	boolean_t leaked;
	boolean_t optimize;

	e = &entities[entity_num];

	brush_start = e->first_brush;
	brush_end = brush_start + e->num_brushes;
	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++){
		Com_Verbose("--------------------------------------------\n");

		RunThreadsOn((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.

		Com_Verbose("--------------------------------------------\n");

		tree = AllocTree();
		tree->head_node = 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->head_node);
		else {
			leaked = true;
			LeakFile(tree);

			if(leaktest){
				Com_Error(ERR_FATAL, "--- MAP LEAKED, ABORTING LEAKTEST ---\n");
			}
			Com_Verbose("**** leaked ****\n");
		}

		MarkVisibleSides(tree, brush_start, brush_end);
		if(noopt || leaked)
			break;
		if(!optimize){
			FreeTree(tree);
		}
	}

	FloodAreas(tree);
	MakeFaces(tree->head_node);
	FixTjuncs(tree->head_node);

	if(!noprune)
		PruneNodes(tree->head_node);

	WriteBSP(tree->head_node);

	if(!leaked)
		WritePortalFile(tree);

	FreeTree(tree);
}
Exemple #2
0
void ProcessWorldModel( void )
{
    int            i, s;
    entity_t    *e;
    tree_t        *tree;
    face_t        *faces;
    qboolean    ignoreLeaks, leaked;
    xmlNodePtr    polyline, leaknode;
    char        level[ 2 ], shader[ 1024 ];
    const char    *value;
    
    
    /* sets integer blockSize from worldspawn "_blocksize" key if it exists */
    value = ValueForKey( &entities[ 0 ], "_blocksize" );
    if( value[ 0 ] == '\0' )
        value = ValueForKey( &entities[ 0 ], "blocksize" );
    if( value[ 0 ] == '\0' )
        value = ValueForKey( &entities[ 0 ], "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 ] );
    
    /* sof2: ignore leaks? */
    value = ValueForKey( &entities[ 0 ], "_ignoreleaks" );    /* ydnar */
    if( value[ 0 ] == '\0' )
        value = ValueForKey( &entities[ 0 ], "ignoreleaks" );
    if( value[ 0 ] == '1' )
        ignoreLeaks = qtrue;
    else
        ignoreLeaks = qfalse;
    
    /* begin worldspawn model */
    BeginModel();
    e = &entities[ 0 ];
    e->firstDrawSurf = 0;
    
    /* ydnar: gs mods */
    ClearMetaTriangles();

    /* 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 ) || ignoreLeaks )
    {
        /* 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 );
        
        /* build a visible face tree */
        faces = MakeVisibleBSPFaceList( entities[ 0 ].brushes );
        FreeTree( tree );
        tree = FaceBSP( faces );
        MakeTreePortals( tree );
        FilterStructuralBrushesIntoTree( e, tree );
        leaked = qfalse;
        
        /* ydnar: flood again for skybox */
        if( skyboxPresent )
            FloodEntities( tree );
    }
    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 );
    
    /* flood from entities */
    FloodAreas( tree );
    
    /* create drawsurfs for triangle models */
    AddTriangleModels( e );
    
    /* create drawsurfs for surface models */
    AddEntitySurfaceModels( e );
    
    /* generate bsp brushes from map brushes */
    EmitBrushes( e->brushes, &e->firstBrush, &e->numBrushes );
    
    /* add references to the detail brushes */
    FilterDetailBrushesIntoTree( e, tree );
    
    /* drawsurfs that cross fog boundaries will need to be split along the fog boundary */
    if( !nofog )
        FogDrawSurfaces( e );
    
    /* subdivide each drawsurf as required by shader tesselation */
    if( !nosubdivide )
        SubdivideFaceSurfaces( e, tree );
    
    /* add in any vertexes required to fix t-junctions */
    if( !notjunc )
        FixTJunctions( e );
    
    /* ydnar: classify the surfaces */
    ClassifyEntitySurfaces( e );
    
    /* ydnar: project decals */
    MakeEntityDecals( e );
    
    /* ydnar: meta surfaces */
    MakeEntityMetaTriangles( e );
    SmoothMetaTriangles();
    FixMetaTJunctions();
    MergeMetaTriangles();
    
    /* ydnar: debug portals */
    if( debugPortals )
        MakeDebugPortalSurfs( tree );
    
    /* ydnar: fog hull */
    value = ValueForKey( &entities[ 0 ], "_foghull" );
    if( value[ 0 ] != '\0' )
    {
        sprintf( shader, "textures/%s", value );
        MakeFogHullSurfs( e, tree, shader );
    }
    
    /* ydnar: bug 645: do flares for lights */
    for( i = 0; i < numEntities && emitFlares; i++ )
    {
        entity_t    *light, *target;
        const char    *value, *flareShader;
        vec3_t        origin, targetOrigin, normal, color;
        int            lightStyle;
        
        
        /* get light */
        light = &entities[ i ];
        value = ValueForKey( light, "classname" );
        if( !strcmp( value, "light" ) )
        {
            /* get flare shader */
            flareShader = ValueForKey( light, "_flareshader" );
            value = ValueForKey( light, "_flare" );
            if( flareShader[ 0 ] != '\0' || value[ 0 ] != '\0' )
            {
                /* get specifics */
                GetVectorForKey( light, "origin", origin );
                GetVectorForKey( light, "_color", color );
                lightStyle = IntForKey( light, "_style" );
                if( lightStyle == 0 )
                    lightStyle = IntForKey( light, "style" );
                
                /* handle directional spotlights */
                value = ValueForKey( light, "target" );
                if( value[ 0 ] != '\0' )
                {
                    /* get target light */
                    target = FindTargetEntity( value );
                    if( target != NULL )
                    {
                        GetVectorForKey( target, "origin", targetOrigin );
                        VectorSubtract( targetOrigin, origin, normal );
                        VectorNormalize( normal, normal );
                    }
                }
                else
                    //%    VectorClear( normal );
                    VectorSet( normal, 0, 0, -1 );
                
                /* create the flare surface (note shader defaults automatically) */
                DrawSurfaceForFlare( mapEntityNum, origin, normal, color, (char*) flareShader, lightStyle );
            }
        }
    }
    
    /* add references to the final drawsurfs in the apropriate clusters */
    FilterDrawsurfsIntoTree( e, tree );
    
    /* match drawsurfaces back to original brushsides (sof2) */
    FixBrushSides( e );
    
    /* finish */
    EndModel( e, tree->headnode );
    FreeTree( tree );
}
Exemple #3
0
/*
============
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);
}
Exemple #4
0
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 );
}
Exemple #5
0
/*
============
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);
}
Exemple #6
0
Fichier : bsp.c Projet : otty/cake3
/*
============
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);
}