Пример #1
0
PORTAL* ClipPortal ( long Node, PORTAL *Portal )
{
	// this recursive function repeatedly clips the current portal to the 
	// tree until it ends up in a leaf at which point it is returned

	PORTAL* PortalList     = NULL, *FrontPortalList = NULL;
	PORTAL* BackPortalList = NULL, *Iterator        = NULL;
	PORTAL* FrontSplit     = NULL, *BackSplit       = NULL;

	switch ( ClassifyPoly ( &PlaneArray [ NodeArray [ Node ].Plane ], ( POLYGON* ) Portal ) )
	{
		case CP_ONPLANE:
		{
			// the portal has to be sent down both sides of the tree and tracked, send
			// it down front first but do no delete any bits that end up in solid space,
			// just ignore them
			if ( NodeArray [ Node ].IsLeaf != 0 )
			{
				 // this is a leaf node ( the front is a leaf )
				Portal->LeafOwnerArray [ Portal->NumberOfLeafs ] = NodeArray [ Node ].Front;
				Portal->NumberOfLeafs++;
				Portal->Next	= NULL;
				Portal->Prev	= NULL;
				FrontPortalList	= Portal;  
			}
			else
			{
				// send the portal down the front list and get returned a 
				// list of PortalFragments that survived the front tree
				FrontPortalList = ClipPortal ( NodeArray [ Node ].Front, Portal );
			}

			// then send each fragment down the back tree.
			if ( FrontPortalList         == NULL ) return NULL;;
			if ( NodeArray [ Node ].Back == -1   ) return FrontPortalList;

			// loop through each front list fragment and send it down the back tree
			while ( FrontPortalList != NULL )
			{
				PORTAL* tempnext = FrontPortalList->Next;
				BackPortalList	 = NULL;
				BackPortalList	 = ClipPortal ( NodeArray [ Node ].Back, FrontPortalList );

				// now set to the end of the returned back fragment list to get the last fragment
				if ( BackPortalList != NULL )
				{
					Iterator = BackPortalList;

					while ( Iterator->Next != NULL )
					{
						Iterator = Iterator->Next;
					}

					// attach the last fragment to the first fragment from a previous loop
					Iterator->Next  = PortalList;
					
					if ( PortalList != NULL )
					{
						PortalList->Prev = Iterator;
					}

					// portal list now points at the current complete list of fragment collected from each loop
					PortalList = BackPortalList;	 
				}

				FrontPortalList = tempnext;
			}

			return PortalList;
		}
		break;
	
		case CP_FRONT:
		{
			// either send it down the front tree or add it to the portal 
			// list because it has come out in empty space
			if ( NodeArray [ Node ].IsLeaf == 0 )
			{
				PortalList = ClipPortal ( NodeArray [ Node ].Front, Portal );
				
				return PortalList;
			}
			else
			{
				// the front node is an empty leaf so add the fragment to the portal list
				Portal->LeafOwnerArray [ Portal->NumberOfLeafs ] = NodeArray [ Node ].Front;
				Portal->NumberOfLeafs++;
				Portal->Next = NULL;
				Portal->Prev = NULL;
			
				return Portal;
			}
		}
		break;

		case CP_BACK:
		{
			// either send it down the back tree or delete it if no back tree exists
			// because it means it has come out in solid space
			if ( NodeArray [ Node ].Back != -1 )
			{
				PortalList = ClipPortal ( NodeArray [ Node ].Back, Portal );

				return PortalList;
			} 
			else
			{
				DeletePortal ( Portal );
				Portal = NULL;
				return NULL; 
			}
		}
		break;

		case CP_SPANNING:
		{
			// portal fragment is spanning the plane, so it must be split
			FrontSplit = new PORTAL;
			BackSplit  = new PORTAL;
			
			ZeroMemory ( FrontSplit, sizeof ( PORTAL ) );
			ZeroMemory ( BackSplit,  sizeof ( PORTAL ) );
	
			// split the portal fragment into two
   			SplitPortal ( Portal, &PlaneArray [ NodeArray [ Node ].Plane ], FrontSplit, BackSplit );

			// delete the original portal fragment
			DeletePortal ( Portal );
			Portal = NULL;
	
			// there is another front node
			if ( NodeArray [ Node ].IsLeaf == 0 )
			{
				FrontPortalList = ClipPortal ( NodeArray [ Node ].Front, FrontSplit );
			}
			else
			{
				// it's about to get pushed into a leaf
				FrontSplit->LeafOwnerArray [ FrontSplit->NumberOfLeafs ] = NodeArray [ Node ].Front;
				FrontSplit->NumberOfLeafs++;
				FrontSplit->Prev = NULL;
				FrontSplit->Next = NULL;
				FrontPortalList  = FrontSplit;
			}
	
			// the backsplit is in solid space
			if ( NodeArray [ Node ].Back != -1 )
			{
				BackPortalList = ClipPortal ( NodeArray [ Node ].Back, BackSplit );
			}
			else 
			{
				// we ended up in solid space
				DeletePortal ( BackSplit );
				BackSplit = NULL;
			}
    
			// find the end of the list and attach it to front back list
			if ( FrontPortalList != NULL )
			{
				// there is something in the front list
				Iterator = FrontPortalList;

				while ( Iterator->Next != NULL )
				{
					Iterator = Iterator->Next;
				}
				
				if ( BackPortalList != NULL )
				{
					Iterator->Next		 = BackPortalList;
					BackPortalList->Prev = Iterator;
				}

				return FrontPortalList;
			}
			else
			{
				// there is nothing in the front list
				if ( BackPortalList != NULL )
					return BackPortalList;

				return NULL;
			}
			
			// if we got here, we are fresh out of portal fragments
			// so simply return NULL
			return NULL;
		}
		break;

		default:
			return NULL;
		break;
	}

	return NULL;
}
Пример #2
0
Poly *Poly::ClipToList ( Poly *pPoly_, bool bClipOnPlane_ )
{
	switch ( ClassifyPoly ( pPoly_ ) )
	{
	case eCP::FRONT:
		{
			return pPoly_->CopyPoly ( );
		} break;

	case eCP::BACK:
		{
			if ( IsLast ( ) )
			{
				return NULL;
			}

			return m_pNext->ClipToList ( pPoly_, bClipOnPlane_ );
		} break;

	case eCP::ONPLANE:
		{
			double	Angle = plane.n.Dot ( pPoly_->plane.n ) - 1;

			if ( ( Angle < epsilon ) && ( Angle > -epsilon ) )
			{
				if ( !bClipOnPlane_ )
				{
					return pPoly_->CopyPoly ( );
				}
			}

			if ( IsLast ( ) )
			{
				return NULL;
			}

			return m_pNext->ClipToList ( pPoly_, bClipOnPlane_ );
		} break;

	case eCP::SPLIT:
		{
			Poly *pFront	= NULL;
			Poly *pBack		= NULL;

			SplitPoly ( pPoly_, &pFront, &pBack );

			if ( IsLast ( ) )
			{
				delete pBack;

				return pFront;
			}

			Poly *pBackFrags = m_pNext->ClipToList ( pBack, bClipOnPlane_ );

			if ( pBackFrags == NULL )
			{
				delete pBack;

				return pFront;
			}

			if ( *pBackFrags == *pBack )
			{
				delete pFront;
				delete pBack;
				delete pBackFrags;

				return pPoly_->CopyPoly ( );
			}

			delete pBack;

			pFront->AddPoly ( pBackFrags );

			return pFront;
		} break;
	}

	return NULL;
}
Пример #3
0
//determines which polygon would be the best to split. Returns the integer index of that
//polygon. Note that it assumes that the list contain at least one element. In addition,
//it will return the number of polygons that lie on the same plane to make allocation
//of nodes easier, along with how many will end up on the front and back
static uint32 FindBestSplitPlane(PrePolyArray& PolyList, 
								 uint32& nNumLieOn, uint32& nNumFront, uint32& nNumBack)
{
	ASSERT(PolyList.GetSize() > 0);

	uint32 nBestIndex = 0;
	uint32 nBestScore = 0xFFFFFFFF;

	//plane information
	PVector vNormal;
	PReal fPlaneDist;

	//counts
	uint32 nFront, nBack, nSplit, nOn;

	//the poly classification
	uint32 nType;

	//the number of polygons to test
	uint32 nNumPolies = PolyList.GetSize();

	//the amount of polygons to skip over. Currently just do 2 * sqrt(n)
	//samples, so 20 polygons will be sampled for 100 nodes, etx. This
	//speeds up BSP generation time significantly.
	uint32 nPolyInc = LTMAX(1, (uint32)(nNumPolies / sqrt(4.0 * nNumPolies)));

	for(uint32 nCurrPoly = 0; nCurrPoly < nNumPolies; nCurrPoly += nPolyInc)
	{
		fPlaneDist	= PolyList[nCurrPoly]->Dist();
		vNormal		= PolyList[nCurrPoly]->Normal();

		//reset the info
		nFront = nBack = nSplit = nOn = 0;

		//now need to count up the split information
		for(uint32 nTestPoly = 0; nTestPoly < PolyList.GetSize(); nTestPoly++)
		{
			//skip over the current poly
			if(nTestPoly == nCurrPoly)
			{
				nOn++;
				continue;
			}

			nType = ClassifyPoly(vNormal, fPlaneDist, PolyList[nTestPoly]);

			if(nType == PLANE_SPAN)
				nSplit++;
			else if(nType == PLANE_FRONT)
				nFront++;
			else if(nType == PLANE_BACK)
				nBack++;
			else
				nOn++;
		}

		//ok, figure out the score
		uint32 nScore = CalcSplitScore(nSplit, nFront, nBack);

		if(nScore < nBestScore)
		{
			nBestScore	= nScore;
			nBestIndex	= nCurrPoly;
			nNumLieOn	= nOn;
			nNumFront	= nFront + nSplit;
			nNumBack	= nBack + nSplit;
		}
	}

	return nBestIndex;
}
Пример #4
0
static bool RecursivelyBuildBSP(CLightBSPNode** ppNode, PrePolyArray& PolyList, CLightBSPPoly** ppBSPPolyList)
{
	//sanity check
	ASSERT(ppNode);
	ASSERT(PolyList.GetSize() > 0);

	//number of polies that lie on the plane
	uint32 nNumLieOn, nFront, nBack;

	//first off, find the best splitting plane
	uint32 nSplitPlane = FindBestSplitPlane(PolyList, nNumLieOn, nFront, nBack);

	//allocate the new node
	*ppNode = AllocateBSPNode(nNumLieOn);

	//check for memory failure
	if(*ppNode == NULL)
		return false;

	//setup the node
	(*ppNode)->m_vPlaneNorm = PolyList[nSplitPlane]->Normal();
	(*ppNode)->m_fPlaneDist = PolyList[nSplitPlane]->Dist();

	//create the front, back, and on arrays
	PrePolyArray	Front, Back, On;
	Front.SetSize(nFront);
	Back.SetSize(nBack);
	On.SetSize(nNumLieOn);

	//now fill up all the lists
	PVector vNormal = PolyList[nSplitPlane]->Normal();
	float	fDist	= PolyList[nSplitPlane]->Dist();

	//index to the coplanar poly offset
	uint32 nPolyIndex = 0;

	//indices for the lists
	uint32 nFrontIndex	= 0;
	uint32 nBackIndex	= 0;
	uint32 nOnIndex		= 0;

	uint32 nType;

	//now need to count up the split information
	for(uint32 nTestPoly = 0; nTestPoly < PolyList.GetSize(); nTestPoly++)
	{
		if(nTestPoly == nSplitPlane)
		{
			On[nOnIndex++] = PolyList[nTestPoly];
			(*ppNode)->m_pPolyList[nPolyIndex++] = ppBSPPolyList[PolyList[nTestPoly]->m_Index];
			continue;
		}

		nType = ClassifyPoly(vNormal, fDist, PolyList[nTestPoly]);

		if(nType & PLANE_FRONT)
			Front[nFrontIndex++] = PolyList[nTestPoly];
		if(nType & PLANE_BACK)
			Back[nBackIndex++] = PolyList[nTestPoly];
		if(nType == PLANE_ON)
		{
			On[nOnIndex++] = PolyList[nTestPoly];
			(*ppNode)->m_pPolyList[nPolyIndex++] = ppBSPPolyList[PolyList[nTestPoly]->m_Index];
		}
	}

	//sanity check
	ASSERT(nPolyIndex == (*ppNode)->m_nNumPolies);
	ASSERT(nFrontIndex == nFront);
	ASSERT(nBackIndex == nBack);
	ASSERT(nOnIndex == nNumLieOn);

	//build up the child lists
	bool bSuccess = true;

	if(Front.GetSize() > 0)
	{
		bSuccess = RecursivelyBuildBSP(&(*ppNode)->m_pFront, Front, ppBSPPolyList);
	}
	if(bSuccess && (Back.GetSize() > 0))
	{
		bSuccess = RecursivelyBuildBSP(&(*ppNode)->m_pBack, Back, ppBSPPolyList);
	}

	//see if we need to clean up
	if(!bSuccess)
	{
		FreeBSPNode(*ppNode);
		*ppNode = NULL;
		return false;
	}

	//we need to update the node's bounding sphere. This is done by running through all
	//the polygons on the plane and generating a bounding box, finding its center, and then
	//the maximum distance to the points
	PVector vMin((PReal)MAX_CREAL, (PReal)MAX_CREAL, (PReal)MAX_CREAL);
	PVector vMax(-vMin);

	uint32 nCurrPoly;
	for(nCurrPoly = 0; nCurrPoly < nNumLieOn; nCurrPoly++)
	{
		CPrePoly* pPoly = On[nCurrPoly];
		for(uint32 nCurrVert = 0; nCurrVert < pPoly->NumVerts(); nCurrVert++)
		{
			VEC_MIN(vMin, vMin, pPoly->Pt(nCurrVert));
			VEC_MAX(vMax, vMax, pPoly->Pt(nCurrVert));
		}
	}

	//found the center
	(*ppNode)->m_vBSphereCenter = ((vMin + vMax) / 2);

	//now update the radius
	(*ppNode)->m_fBSphereRadSqr = 0;

	PReal fDistSqr;

	for(nCurrPoly = 0; nCurrPoly < nNumLieOn; nCurrPoly++)
	{
		CPrePoly* pPoly = On[nCurrPoly];
		for(uint32 nCurrVert = 0; nCurrVert < pPoly->NumVerts(); nCurrVert++)
		{
			fDistSqr = (pPoly->Pt(nCurrVert) - (*ppNode)->m_vBSphereCenter).MagSqr();

			if(fDistSqr > (*ppNode)->m_fBSphereRadSqr)
			{
				(*ppNode)->m_fBSphereRadSqr = fDistSqr;
			}
		}
	}
	
	//add some padding to the radius to compensate for inaccuracy
	(*ppNode)->m_fBSphereRadSqr += NODE_RADIUS_PADDING;

	return bSuccess;	
}
Пример #5
0
static void BSPTreeCreateRecursive(BSPTreeNode *tree,
				   PolyListNode *pllist,
#if BSPTREE_STATS
				   int depth,
#endif
				   struct obstack *scratch)
{
  PolyListNode *plnode, *front, *back;
  EdgeIntersection edges[2];

  tree->front = tree->back = NULL;
  ListPop(pllist, plnode);
  tree->polylist = plnode;
  check_poly(plnode->poly);
  PolyPlane(plnode, &tree->plane);

  front = back = NULL;
  while (pllist) {    
    ListPop(pllist, plnode);
    check_poly(plnode->poly);
    switch (ClassifyPoly(&tree->plane, plnode->poly, edges)) {
    case BACKOF:
      check_poly(plnode->poly);
      ListPush(back, plnode);
      break;
    case COPLANAR:
      check_poly(plnode->poly);
      ListPush(tree->polylist, plnode);
      break;
    case INFRONTOF:
      check_poly(plnode->poly);
      ListPush(front, plnode);
      break;
    case BOTH_SIDES:
      check_poly(plnode->poly);
      SplitPolyNode(plnode, &front, &back, edges, scratch);
      break;
    }
  }
  if (front) {
    tree->front = obstack_alloc(scratch, sizeof(*tree->front));
    BSPTreeCreateRecursive(tree->front, front,
#if BSPTREE_STATS
			   depth+1,
#endif
			   scratch);
  }
  if (back) {
    tree->back = obstack_alloc(scratch, sizeof(*tree->back));
    BSPTreeCreateRecursive(tree->back, back,
#if BSPTREE_STATS
			   depth+1,
#endif
			   scratch);
  }
#if BSPTREE_STATS
  if (depth > tree_depth) {
    tree_depth = depth;
  }
#endif
}