예제 #1
0
/*
============
idAASBuild::LedgeSubdivFlood_r
============
*/
void idAASBuild::LedgeSubdivFlood_r( idBrushBSPNode *node, const idLedge *ledge ) {
	int s1, i;
	idBrushBSPPortal *p1;
	idWinding *w;
	idList<idBrushBSPNode *> nodeList;
	if( node->GetFlags() & NODE_VISITED ) {
		return;
	}
	// if this is not already a ledge area
	if( !( node->GetFlags() & AREA_LEDGE ) ) {
		for( p1 = node->GetPortals(); p1; p1 = p1->Next( s1 ) ) {
			s1 = ( p1->GetNode( 1 ) == node );
			if( !( p1->GetFlags() & FACE_FLOOR ) ) {
				continue;
			}
			// split the area if some part of the floor portal is inside the expanded ledge
			w = ledge->ChopWinding( p1->GetWinding() );
			if( !w ) {
				continue;
			}
			delete w;
			for( i = 0; i < ledge->numSplitPlanes; i++ ) {
				if( node->PlaneSide( ledge->planes[i], 0.1f ) != SIDE_CROSS ) {
					continue;
				}
				if( !node->Split( ledge->planes[i], -1 ) ) {
					continue;
				}
				numLedgeSubdivisions++;
				DisplayRealTimeString( "\r%6d", numLedgeSubdivisions );
				node->GetChild( 0 )->SetFlag( NODE_VISITED );
				LedgeSubdivFlood_r( node->GetChild( 1 ), ledge );
				return;
			}
			node->SetFlag( AREA_LEDGE );
			break;
		}
	}
	node->SetFlag( NODE_VISITED );
	// get all nodes we might need to flood into
	for( p1 = node->GetPortals(); p1; p1 = p1->Next( s1 ) ) {
		s1 = ( p1->GetNode( 1 ) == node );
		if( p1->GetNode( !s1 )->GetContents() & AREACONTENTS_SOLID ) {
			continue;
		}
		// flood through this portal if the portal is partly inside the expanded ledge
		w = ledge->ChopWinding( p1->GetWinding() );
		if( !w ) {
			continue;
		}
		delete w;
		// add to list, cannot flood directly cause portals might be split on the way
		nodeList.Append( p1->GetNode( !s1 ) );
	}
	// flood into other nodes
	for( i = 0; i < nodeList.Num(); i++ ) {
		LedgeSubdivLeafNodes_r( nodeList[i], ledge );
	}
}
/*
================
idAASBuild::GetAreaForLeafNode
================
*/
bool idAASBuild::GetAreaForLeafNode( idBrushBSPNode* node, int* areaNum )
{
	int s, faceNum;
	idBrushBSPPortal* p;
	aasArea_t area;
	
	if( node->GetAreaNum() )
	{
		*areaNum = -node->GetAreaNum();
		return true;
	}
	
	area.flags = node->GetFlags();
	area.cluster = area.clusterAreaNum = 0;
	area.contents = node->GetContents();
	area.firstFace = file->faceIndex.Num();
	area.numFaces = 0;
	area.reach = NULL;
	area.rev_reach = NULL;
	
	for( p = node->GetPortals(); p; p = p->Next( s ) )
	{
		s = ( p->GetNode( 1 ) == node );
		
		if( !GetFaceForPortal( p, s, &faceNum ) )
		{
			continue;
		}
		
		file->faceIndex.Append( faceNum );
		area.numFaces++;
		
		if( faceNum > 0 )
		{
			file->faces[abs( faceNum )].areas[0] = file->areas.Num();
		}
		else
		{
			file->faces[abs( faceNum )].areas[1] = file->areas.Num();
		}
	}
	
	if( !area.numFaces )
	{
		*areaNum = 0;
		return false;
	}
	
	*areaNum = -file->areas.Num();
	node->SetAreaNum( file->areas.Num() );
	file->areas.Append( area );
	
	DisplayRealTimeString( "\r%6d", file->areas.Num() );
	
	return true;
}
예제 #3
0
/*
============
idAASBuild::MergeWithAdjacentLeafNodes
============
*/
bool idAASBuild::MergeWithAdjacentLeafNodes( idBrushBSP &bsp, idBrushBSPNode *node ) {
	int s, numMerges = 0, otherNodeFlags;
	idBrushBSPPortal *p;

	do {
		for ( p = node->GetPortals(); p; p = p->Next(s) ) {
			s = (p->GetNode(1) == node);

			// both leaf nodes must have the same contents
			if ( node->GetContents() != p->GetNode(!s)->GetContents() ) {
				continue;
			}

			// cannot merge leaf nodes if one is near a ledge and the other is not
			if ( (node->GetFlags() & AREA_LEDGE) != (p->GetNode(!s)->GetFlags() & AREA_LEDGE) ) {
				continue;
			}

			// cannot merge leaf nodes if one has a floor portal and the other a gap portal
			if ( node->GetFlags() & AREA_FLOOR ) {
				if ( p->GetNode(!s)->GetFlags() & AREA_GAP ) {
					if ( !AllGapsLeadToOtherNode( p->GetNode(!s), node ) ) {
						continue;
					}
				}
			}
			else if ( node->GetFlags() & AREA_GAP ) {
				if ( p->GetNode(!s)->GetFlags() & AREA_FLOOR ) {
					if ( !AllGapsLeadToOtherNode( node, p->GetNode(!s) ) ) {
						continue;
					}
				}
			}

			otherNodeFlags = p->GetNode(!s)->GetFlags();

			// try to merge the leaf nodes
			if ( bsp.TryMergeLeafNodes( p, s ) ) {
				node->SetFlag( otherNodeFlags );
				if ( node->GetFlags() & AREA_FLOOR ) {
					node->RemoveFlag( AREA_GAP );
				}
				numMerges++;
				DisplayRealTimeString( "\r%6d", ++numMergedLeafNodes );
				break;
			}
		}
	} while( p );

	if ( numMerges ) {
		return true;
	}
	return false;
}
예제 #4
0
/*
============
idBrushList::Merge
============
*/
void idBrushList::Merge( bool ( *MergeAllowed )( idBrush* b1, idBrush* b2 ) )
{
	idPlaneSet planeList;
	idBrush* b1, *b2, *nextb2;
	int numMerges;
	
	common->Printf( "[Brush Merge]\n" );
	common->Printf( "%6d original brushes\n", Num() );
	
	CreatePlaneList( planeList );
	
	numMerges = 0;
	for( b1 = Head(); b1; b1 = b1->next )
	{
	
		for( b2 = Head(); b2; b2 = nextb2 )
		{
			nextb2 = b2->Next();
			
			if( b2 == b1 )
			{
				continue;
			}
			
			if( MergeAllowed && !MergeAllowed( b1, b2 ) )
			{
				continue;
			}
			
			if( b1->TryMerge( b2, planeList ) )
			{
				Delete( b2 );
				DisplayRealTimeString( "\r%6d", ++numMerges );
				nextb2 = Head();
			}
		}
	}
	
	common->Printf( "\r%6d brushes merged\n", numMerges );
}
예제 #5
0
/*
============
idBrushList::Chop
============
*/
void idBrushList::Chop( bool ( *ChopAllowed )( idBrush* b1, idBrush* b2 ) )
{
	idBrush*	b1, *b2, *next;
	idBrushList sub1, sub2, keep;
	int i, j, c1, c2;
	idPlaneSet planeList;
	
#ifdef OUTPUT_CHOP_STATS
	common->Printf( "[Brush CSG]\n" );
	common->Printf( "%6d original brushes\n", this->Num() );
#endif
	
	CreatePlaneList( planeList );
	
	for( b1 = this->Head(); b1; b1 = this->Head() )
	{
	
		for( b2 = b1->next; b2; b2 = next )
		{
		
			next = b2->next;
			
			for( i = 0; i < 3; i++ )
			{
				if( b1->bounds[0][i] >= b2->bounds[1][i] )
				{
					break;
				}
				if( b1->bounds[1][i] <= b2->bounds[0][i] )
				{
					break;
				}
			}
			if( i < 3 )
			{
				continue;
			}
			
			for( i = 0; i < b1->GetNumSides(); i++ )
			{
				for( j = 0; j < b2->GetNumSides(); j++ )
				{
					if( b1->GetSide( i )->GetPlaneNum() == ( b2->GetSide( j )->GetPlaneNum() ^ 1 ) )
					{
						// opposite planes, so not touching
						break;
					}
				}
				if( j < b2->GetNumSides() )
				{
					break;
				}
			}
			if( i < b1->GetNumSides() )
			{
				continue;
			}
			
			sub1.Clear();
			sub2.Clear();
			
			c1 = 999999;
			c2 = 999999;
			
			// if b2 may chop up b1
			if( !ChopAllowed || ChopAllowed( b2,  b1 ) )
			{
				if( !b1->Subtract( b2, sub1 ) )
				{
					// didn't really intersect
					continue;
				}
				if( sub1.IsEmpty() )
				{
					// b1 is swallowed by b2
					this->Delete( b1 );
					break;
				}
				c1 = sub1.Num();
			}
			
			// if b1 may chop up b2
			if( !ChopAllowed || ChopAllowed( b1,  b2 ) )
			{
				if( !b2->Subtract( b1, sub2 ) )
				{
					// didn't really intersect
					continue;
				}
				if( sub2.IsEmpty() )
				{
					// b2 is swallowed by b1
					sub1.Free();
					this->Delete( b2 );
					continue;
				}
				c2 = sub2.Num();
			}
			
			if( sub1.IsEmpty() && sub2.IsEmpty() )
			{
				continue;
			}
			
			// don't allow too much fragmentation
			if( c1 > 2 && c2 > 2 )
			{
				sub1.Free();
				sub2.Free();
				continue;
			}
			
			if( c1 < c2 )
			{
				sub2.Free();
				this->AddToTail( sub1 );
				this->Delete( b1 );
				break;
			}
			else
			{
				sub1.Free();
				this->AddToTail( sub2 );
				this->Delete( b2 );
				continue;
			}
		}
		
		if( !b2 )
		{
			// b1 is no longer intersecting anything, so keep it
			this->Remove( b1 );
			keep.AddToTail( b1 );
#ifdef OUTPUT_CHOP_STATS
			DisplayRealTimeString( "\r%6d", keep.numBrushes );
#endif
		}
	}
	
	*this = keep;
	
#ifdef OUTPUT_CHOP_STATS
	common->Printf( "\r%6d output brushes\n", Num() );
#endif
}
예제 #6
0
void idAASBuild::GravSubdivLeafNode( idBrushBSPNode *node ) {
	int s1, s2, i, j, k, side1;
	int numSplits, numSplitters;
	idBrushBSPPortal *p1, *p2;
	idWinding *w1, *w2;
	idVec3 normal;
	idPlane plane;
	idPlaneSet planeList;
	float d, min, max;
	int *splitterOrder;
	int *bestNumSplits;
	int floor, gap, numFloorChecked;

	// if this leaf node is already classified it cannot have a combination of floor and gap portals
	if ( node->GetFlags() & (AREA_FLOOR|AREA_GAP) ) {
		return;
	}

	floor = gap = 0;

	// check if the area has a floor
	for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
		s1 = (p1->GetNode(1) == node);

		if ( p1->GetFlags() & FACE_FLOOR ) {
			floor++;
		}
	}

	// find seperating planes between gap and floor portals
	for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
		s1 = (p1->GetNode(1) == node);

		// if the portal is a gap seen from this side
		if ( PortalIsGap( p1, s1 ) ) {
			gap++;
			// if the area doesn't have a floor
			if ( !floor ) {
				break;
			}
		}
		else {
			continue;
		}

		numFloorChecked = 0;

		w1 = p1->GetWinding();

		// test all edges of the gap
		for ( i = 0; i < w1->GetNumPoints(); i++ ) {

			// create a plane through the edge of the gap parallel to the direction of gravity
			normal = (*w1)[(i+1)%w1->GetNumPoints()].ToVec3() - (*w1)[i].ToVec3();
			normal = normal.Cross( aasSettings->invGravityDir );
			if ( normal.Normalize() < 0.2f ) {
				continue;
			}
			plane.SetNormal( normal );
			plane.FitThroughPoint( (*w1)[i].ToVec3() );

			// get the side of the plane the gap is on
			side1 = w1->PlaneSide( plane, GRAVSUBDIV_EPSILON );
			if ( side1 == SIDE_ON ) {
				break;
			}

			// test if the plane through the edge of the gap seperates the gap from a floor portal
			for ( p2 = node->GetPortals(); p2; p2 = p2->Next(s2) ) {
				s2 = (p2->GetNode(1) == node);

				if ( !( p2->GetFlags() & FACE_FLOOR ) ) {
					continue;
				}

				if ( p2->GetFlags() & FACE_CHECKED ) {
					continue;
				}

				w2 = p2->GetWinding();

				min = 2.0f * GRAVSUBDIV_EPSILON;
				max = GRAVSUBDIV_EPSILON;
				if ( side1 == SIDE_FRONT ) {
					for ( j = 0; j < w2->GetNumPoints(); j++ ) {
						d = plane.Distance( (*w2)[j].ToVec3() );
						if ( d >= GRAVSUBDIV_EPSILON ) {
							break;	// point at the same side of the plane as the gap
						}
						d = idMath::Fabs( d );
						if ( d < min ) {
							min = d;
						}
						if ( d > max ) {
							max = d;
						}
					}
				}
				else {
					for ( j = 0; j < w2->GetNumPoints(); j++ ) {
						d = plane.Distance( (*w2)[j].ToVec3() );
						if ( d <= -GRAVSUBDIV_EPSILON ) {
							break;	// point at the same side of the plane as the gap
						}
						d = idMath::Fabs( d );
						if ( d < min ) {
							min = d;
						}
						if ( d > max ) {
							max = d;
						}
					}
				}

				// a point of the floor portal was found to be at the same side of the plane as the gap
				if ( j < w2->GetNumPoints() ) {
					continue;
				}

				// if the floor portal touches the plane
				if ( min < GRAVSUBDIV_EPSILON && max > GRAVSUBDIV_EPSILON ) {
					planeList.FindPlane( plane, 0.00001f, 0.1f );
				}

				p2->SetFlag( FACE_CHECKED );
				numFloorChecked++;

			}
			if ( numFloorChecked == floor ) {
				break;
			}
		}

		for ( p2 = node->GetPortals(); p2; p2 = p2->Next(s2) ) {
			s2 = (p2->GetNode(1) == node);
			p2->RemoveFlag( FACE_CHECKED );
		}
	}

	// if the leaf node does not have both floor and gap portals
	if ( !( gap && floor) ) {
		if ( floor ) {
			node->SetFlag( AREA_FLOOR );
		}
		else if ( gap ) {
			node->SetFlag( AREA_GAP );
		}
		return;
	}

	// if no valid seperators found
	if ( planeList.Num() == 0 ) {
		// NOTE: this should never happend, if it does the leaf node has degenerate portals
		return;
	}

	splitterOrder = (int *) _alloca( planeList.Num() * sizeof( int ) );
	bestNumSplits = (int *) _alloca( planeList.Num() * sizeof( int ) );
	numSplitters = 0;

	// test all possible seperators and sort them from best to worst
	for ( i = 0; i < planeList.Num(); i += 2 ) {
		numSplits = 0;

		for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
			s1 = (p1->GetNode(1) == node);
			if ( p1->GetWinding()->PlaneSide( planeList[i], 0.1f ) == SIDE_CROSS ) {
				numSplits++;
			}
		}

		for ( j = 0; j < numSplitters; j++ ) {
			if ( numSplits < bestNumSplits[j] ) {
				for ( k = numSplitters; k > j; k-- ) {
					bestNumSplits[k] = bestNumSplits[k-1];
					splitterOrder[k] = splitterOrder[k-1];
				}
				bestNumSplits[j] = numSplits;
				splitterOrder[j] = i;
				numSplitters++;
				break;
			}
		}
		if ( j >= numSplitters ) {
			bestNumSplits[j] = numSplits;
			splitterOrder[j] = i;
			numSplitters++;
		}
	}

	// try all seperators in order from best to worst
	for ( i = 0; i < numSplitters; i++ ) {
		if ( node->Split( planeList[splitterOrder[i]], -1 ) ) {
			// we found a seperator that works
			break;
		}
	}
	if ( i >= numSplitters) {
		return;
	}

	DisplayRealTimeString( "\r%6d", ++numGravitationalSubdivisions );

	// test children for further splits
	GravSubdivLeafNode( node->GetChild(0) );
	GravSubdivLeafNode( node->GetChild(1) );
}