Example #1
0
// Clips f to a list of potential cutting brushes
// If f clips into new faces, returns the list of new faces in pOutputList
static void ClipFaceToBrushList( face_t *f, const CUtlVector<bspbrush_t *> &cutBrushes, face_t **pOutputList )
{
	*pOutputList = NULL;

	if ( f->split[0] )
		return;

	face_t *pClipList = CopyFace( f );
	pClipList->next = NULL;
	bool clipped = false;
	for ( int i = 0; i < cutBrushes.Count(); i++ )
	{
		bspbrush_t *cut = cutBrushes[i];
		for ( face_t *pCutFace = pClipList; pCutFace; pCutFace = pCutFace->next )
		{
			face_t *pClip = NULL;
			// already split, no need to clip
			if ( pCutFace->split[0] )
				continue;

			if ( ClipFaceToBrush( pCutFace, cut, &pClip ) )
			{
				clipped = true;
				// mark face bad, the brush clipped it away
				pCutFace->split[0] = pCutFace;
			}
			else if ( pClip )
			{
				clipped = true;
				// mark this face as split
				pCutFace->split[0] = pCutFace;

				// insert face fragments at head of list (UNDONE: reverses order, do we care?)
				while ( pClip )
				{
					face_t *next = pClip->next;
					pClip->next = pClipList;
					pClipList = pClip;
					pClip = next;
				}
			}
		}
	}
	if ( clipped )
	{
		*pOutputList = pClipList;
	}
	else
	{
		// didn't do any clipping, go ahead and free the copy of the face here.
		FreeFaceList( pClipList );
	}
}
Example #2
0
//-----------------------------------------------------------------------------
// Purpose: Build faces for the detail brushes and merge them into the BSP
// Input  : *worldtree - 
//			brush_start - 
//			brush_end - 
//-----------------------------------------------------------------------------
face_t *MergeDetailTree( tree_t *worldtree, int brush_start, int brush_end )
{
	int			start;
	bspbrush_t	*detailbrushes = NULL;
	tree_t		*detailtree = NULL;
	face_t		*pFaces = NULL;
	face_t		*pLeafFaceList = NULL;

	// Grab the list of detail brushes
	detailbrushes = MakeBspBrushList (brush_start, brush_end, map_mins, map_maxs, ONLY_DETAIL );
	if (detailbrushes)
	{
		start = Plat_FloatTime();
		Msg("Chop Details...");
		// if there are detail brushes, chop them against each other
		if (!nocsg)
			detailbrushes = ChopBrushes (detailbrushes);

		Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
		// Now mark the visible sides so we can eliminate all detail brush sides
		// that are covered by other detail brush sides
		// NOTE: This still leaves detail brush sides that are covered by the world. (these are removed in the merge operation)
		Msg("Find Visible Detail Sides...");
		pFaces = ComputeVisibleBrushSides( detailbrushes );
		TryMergeFaceList( &pFaces );
		SubdivideFaceList( &pFaces );
		Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );

		start = Plat_FloatTime();
		Msg("Merging details...");
		// Merge the detail solids and faces into the world tree
		// Merge all of the faces into the world tree
		pLeafFaceList = FilterFacesIntoTree( worldtree, pFaces );
		FilterBrushesIntoTree( worldtree, detailbrushes );

		FreeFaceList( pFaces );
		FreeBrushList(detailbrushes);

		Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
	}

	return pLeafFaceList;
}
Example #3
0
// Compute a list of faces that are visible on the detail brush sides
face_t *ComputeVisibleBrushSides( bspbrush_t *list )
{
	face_t *pTotalFaces = NULL;
	CUtlVector<bspbrush_t *> cutBrushes;

	// Go through the whole brush list
	for ( bspbrush_t *pbrush = list; pbrush; pbrush = pbrush->next )
	{
		face_t *pFaces = NULL;
		mapbrush_t *mb = pbrush->original;

		if ( !(mb->contents & ALL_VISIBLE_CONTENTS) )
			continue;

		// Make a face for each brush side, then clip it by the other
		// details to see if any fragments are visible
		for ( int i = 0; i < pbrush->numsides; i++ )
		{
			winding_t *winding = pbrush->sides[i].winding;
			if ( !winding )
				continue;
			
			if (! (pbrush->sides[i].contents & ALL_VISIBLE_CONTENTS) )
				continue;

			side_t *side = FindOriginalSide( mb, pbrush->sides + i );
			face_t *f = MakeBrushFace( side, winding );

			// link to head of face list
			f->next = pFaces;
			pFaces = f;
		}

		// Make a list of brushes that can cut the face list for this brush
		cutBrushes.RemoveAll();
		if ( GetListOfCutBrushes( cutBrushes, pbrush, list ) )
		{
			// now cut each face to find visible fragments
			for ( face_t *f = pFaces; f; f = f->next )
			{
				// this will be a new list of faces that this face cuts into
				face_t *pClip = NULL;
				ClipFaceToBrushList( f, cutBrushes, &pClip );
				if ( pClip )
				{
					int outCount = CountFaceList(pClip);
					// it cut into more faces (or it was completely cut away)
					if ( outCount <= 1 )
					{
						// was removed or cut down, mark as split
						f->split[0] = f;
						// insert face fragments at head of list (UNDONE: reverses order, do we care?)
						while ( pClip )
						{
							face_t *next = pClip->next;
							pClip->next = pFaces;
							pFaces = pClip;
							pClip = next;
						}
					}
					else
					{
						// it cut into more than one visible fragment
						// Don't fragment details
						// UNDONE: Build 2d convex hull of this list and swap face winding 
						// with that polygon?  That would fix the remaining issues.
						FreeFaceList( pClip );
						pClip = NULL;
					}
				}
			}
		}
	
		// move visible fragments to global face list
		while ( pFaces )
		{
			face_t *next = pFaces->next;
			if ( pFaces->split[0] )
			{
				FreeFace( pFaces );
			}
			else
			{
				pFaces->next = pTotalFaces;
				pTotalFaces = pFaces;
			}
			pFaces = next;
		}
	}

	return pTotalFaces;
}
Example #4
0
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pFace - input face to test
//			*pbrush - brush to clip face against
//			**pOutputList - list of faces clipped from pFace
// Output : Returns true if the brush completely clips the face
//-----------------------------------------------------------------------------
// NOTE: This assumes the brushes have already been chopped so that no solid space
// is enclosed by more than one brush!!
bool ClipFaceToBrush( face_t *pFace, bspbrush_t *pbrush, face_t **pOutputList )
{
	int planenum = pFace->planenum & (~1);
	int foundSide = -1;

	CUtlVector<int> sortedSides;

	int i;
	for ( i = 0; i < pbrush->numsides && foundSide < 0; i++ )
	{
		int bplane = pbrush->sides[i].planenum & (~1);
		if ( bplane == planenum )
			foundSide = i;
	}

	Vector offset = -0.5f * (pbrush->maxs + pbrush->mins);
	face_t *currentface = CopyFace( pFace );

	if ( foundSide >= 0 )
	{
		sortedSides.RemoveAll();
		for ( i = 0; i < pbrush->numsides; i++ )
		{
			// don't clip to bevels
			if ( pbrush->sides[i].bevel )
				continue;

			if ( g_MainMap->mapplanes[pbrush->sides[i].planenum].type <= PLANE_Z )
			{
				sortedSides.AddToHead( i );
			}
			else
			{
				sortedSides.AddToTail( i );
			}
		}

		for ( i = 0; i < sortedSides.Size(); i++ )
		{
			int index = sortedSides[i];
			if ( index == foundSide )
				continue;
			
			plane_t *plane = &g_MainMap->mapplanes[pbrush->sides[index].planenum];
			winding_t *frontwinding, *backwinding;
			ClipWindingEpsilon_Offset(currentface->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, offset);
			
			// only clip if some part of this face is on the back side of all brush sides
			if ( !backwinding || WindingIsTiny(backwinding))
			{
				FreeFaceList( *pOutputList );
				*pOutputList = NULL;
				break;
			}
			if ( frontwinding && !WindingIsTiny(frontwinding) )
			{
				// add this fragment to the return list
				// make a face for the fragment
				face_t *f = NewFaceFromFace( pFace );
				f->w = frontwinding;
				
				// link the fragment in
				f->next = *pOutputList;
				*pOutputList = f;
			}

			// update the current winding to be the part behind each plane
			FreeWinding( currentface->w );
			currentface->w = backwinding;
		}

		// free the bit that is left in solid or not clipped (if we broke out early)
		FreeFace( currentface );

		// if we made it all the way through and didn't produce any fragments then the whole face was clipped away
		if ( !*pOutputList && i == sortedSides.Size() )
		{
			return true;
		}
	}
	return false;
}
Example #5
0
// Returns false if union of brushes is obviously zero
static void     AddPlaneToUnion(brushhull_t* hull, const int planenum)
{
    bool            need_new_face = false;

    bface_t*        new_face_list;

    bface_t*        face;
    bface_t*        next;

    plane_t*        split;
    Winding*        front;
    Winding*        back;

    new_face_list = NULL;

    next = NULL;

    hlassert(hull);

    if (!hull->faces)
    {
        return;
    }
    hlassert(hull->faces->w);

    for (face = hull->faces; face; face = next)
    {
        hlassert(face->w);
        next = face->next;

        // Duplicate plane, ignore
        if (face->planenum == planenum)
        {
            AddFaceToList(&new_face_list, CopyFace(face));
            continue;
        }

        split = &g_mapplanes[planenum];
        face->w->Clip(split->normal, split->dist, &front, &back);

        if (front)
        {
            delete front;
            need_new_face = true;

            if (back)
            {                                              // Intersected the face
                delete face->w;
                face->w = back;
                AddFaceToList(&new_face_list, CopyFace(face));
            }
        }
        else
        {
            // Completely missed it, back is identical to face->w so it is destroyed
            if (back)
            {
                delete back;
                AddFaceToList(&new_face_list, CopyFace(face));
            }
        }
        hlassert(face->w);
    }

    FreeFaceList(hull->faces);
    hull->faces = new_face_list;

    if (need_new_face && (NumberOfHullFaces(hull) > 2))
    {
        Winding*        new_winding = NewWindingFromPlane(hull, planenum);

        if (new_winding)
        {
            bface_t*        new_face = (bface_t*)Alloc(sizeof(bface_t));

            new_face->planenum = planenum;
            new_face->w = new_winding;

            new_face->next = hull->faces;
            hull->faces = new_face;
        }
    }
}
Example #6
0
void            CalculateBrushUnions(const int brushnum)
{
    int             bn, hull;
    brush_t*        b1;
    brush_t*        b2;
    brushhull_t*    bh1;
    brushhull_t*    bh2;
    entity_t*       e;

    b1 = &g_mapbrushes[brushnum];
    e = &g_entities[b1->entitynum];

    for (hull = 0; hull < 1 /* NUM_HULLS */ ; hull++)
    {
        bh1 = &b1->hulls[hull];
        if (!bh1->faces)                                   // Skip it if it is not in this hull
        {
            continue;
        }

        for (bn = brushnum + 1; bn < e->numbrushes; bn++)
        {                                                  // Only compare if b2 > b1, tests are communitive
            b2 = &g_mapbrushes[e->firstbrush + bn];
            bh2 = &b2->hulls[hull];

            if (!bh2->faces)                               // Skip it if it is not in this hull
            {
                continue;
            }
            if (b1->contents != b2->contents)
            {
                continue;                                  // different contents, ignore
            }

            Developer(DEVELOPER_LEVEL_SPAM, "Processing hull %d brush %d and brush %d\n", hull, brushnum, bn);

            {
                brushhull_t     union_hull;
                bface_t*        face;

                union_hull.bounds = bh1->bounds;

                union_hull.faces = CopyFaceList(bh1->faces);

                for (face = bh2->faces; face; face = face->next)
                {
                    AddPlaneToUnion(&union_hull, face->planenum);
                }

                // union was clipped away (no intersection)
                if (!union_hull.faces)
                {
                    continue;
                }

                if (g_developer >= DEVELOPER_LEVEL_MESSAGE)
                {
                    Log("\nUnion windings\n");
                    DumpHullWindings(&union_hull);

                    Log("\nBrush %d windings\n", brushnum);
                    DumpHullWindings(bh1);

                    Log("\nBrush %d windings\n", bn);
                    DumpHullWindings(bh2);
                }


                {
                    vec_t           volume_brush_1;
                    vec_t           volume_brush_2;
                    vec_t           volume_brush_union;
                    vec_t           volume_ratio_1;
                    vec_t           volume_ratio_2;

                    if (isInvalidHull(&union_hull))
                    {
                        FreeFaceList(union_hull.faces);
                        continue;
                    }

                    volume_brush_union = CalculateSolidVolume(&union_hull);
                    volume_brush_1 = CalculateSolidVolume(bh1);
                    volume_brush_2 = CalculateSolidVolume(bh2);

                    volume_ratio_1 = volume_brush_union / volume_brush_1;
                    volume_ratio_2 = volume_brush_union / volume_brush_2;

                    if ((volume_ratio_1 > g_BrushUnionThreshold) || (g_developer >= DEVELOPER_LEVEL_MESSAGE))
                    {
                        volume_ratio_1 *= 100.0;
                        Warning("Entity %d : Brush %d intersects with brush %d by %2.3f percent", 
#ifdef HLCSG_COUNT_NEW
							b1->originalentitynum, b1->originalbrushnum, b2->originalbrushnum, 
#else
							b1->entitynum, brushnum, bn, 
#endif
							volume_ratio_1);
                    }
                    if ((volume_ratio_2 > g_BrushUnionThreshold) || (g_developer >= DEVELOPER_LEVEL_MESSAGE))
                    {
                        volume_ratio_2 *= 100.0;
                        Warning("Entity %d : Brush %d intersects with brush %d by %2.3f percent", 
#ifdef HLCSG_COUNT_NEW
							b1->originalentitynum, b2->originalbrushnum, b1->originalbrushnum, 
#else
							b1->entitynum, bn, brushnum, 
#endif
							volume_ratio_2);
                    }
                }

                FreeFaceList(union_hull.faces);
            }
        }
    }
}