www.pudn.com > zfxcengine-0.1.0.zip > cePolygon.cpp


// $Id: cePolygon.cpp,v 1.10 2005/08/24 02:32:36 andreaskohn Exp $ 
#include  
#include "Math/ceMath.h" 
 
namespace ZFXCE 
{ 
	//////////////////////////////////////////////////////////////////////////////// 
	cePolygon::cePolygon() 
	{	 
		m_pPoints = NULL; 
		m_puiIndices = NULL; 
		m_uiNumOfPoints = 0; 
		m_uiNumOfIndices = 0; 
		memset(&m_AABB, 0, sizeof(ceAABB) ); 
		memset(&m_Plane, 0, sizeof(cePlane)); 
	} 
	//////////////////////////////////////////////////////////////////////////////// 
	cePolygon::~cePolygon() 
	{ 
		delete[] m_pPoints; 
		m_pPoints = 0; 
 
		delete[] m_puiIndices; 
		m_puiIndices = 0; 
 
		m_uiNumOfIndices = m_uiNumOfPoints = 0; 
 
		memset(&m_AABB, 0, sizeof(ceAABB) ); 
		memset(&m_Plane, 0, sizeof(cePlane)); 
	} 
	//////////////////////////////////////////////////////////////////////////////// 
	void cePolygon::Set(const ceVec3f* pPoints, UINT uiNumOfPoints,  
				const WORD* pIndices, UINT uiNumOfIndices) 
	{ 
		// Wenn noch alte Daten da, dann erstmal löschen 
		if (m_pPoints) 
			delete[] m_pPoints; 
		 
		m_pPoints = 0; 
 
		if (m_puiIndices) 
			delete [] m_puiIndices; 
		 
		m_puiIndices = 0; 
 
		m_pPoints = new ceVec3f[uiNumOfPoints]; 
		memset(&m_pPoints[0], 0, sizeof(ceVec3f) * uiNumOfPoints); 
 
		m_puiIndices = new WORD[uiNumOfIndices]; 
		memset(&m_puiIndices, 0, sizeof(WORD) * uiNumOfIndices); 
 
		m_uiNumOfIndices = uiNumOfIndices; 
		m_uiNumOfPoints = uiNumOfPoints; 
 
		// Daten nun komplett kopieren 
		memcpy(&m_pPoints[0], &pPoints[0], sizeof(ceVec3f) * uiNumOfPoints); 
		memcpy(&m_puiIndices[0], &pIndices[0], sizeof(WORD) * uiNumOfIndices); 
 
		// Die Ebene dieses Polygons berechnen, dazu brauchen wir erstmal einen Punkt... 
		ceVec3f vEdge0 = m_pPoints[ m_puiIndices[1] ] - m_pPoints[ m_puiIndices[0] ]; 
 
		bool bFoundPlane = false; 
		ceVec3f vEdge1; 
		vEdge0.Normalize(); 
		for(UINT i=0;bFoundPlane = FALSE; i++) 
		{ 
			if ( i+1 >= m_uiNumOfIndices) 
				break; 
 
			vEdge1 = m_pPoints[ m_puiIndices[i] ] - m_pPoints[ m_puiIndices[0] ]; 
			vEdge1.Normalize(); 
			if (vEdge0.AngleWith(vEdge1) < 0.000001f) { 
				bFoundPlane = true; 
			} 
		} 
 
		m_Plane.m_vNormal.Cross(vEdge0, vEdge1); 
		m_Plane.m_vNormal.Normalize(); 
		m_Plane.m_fDistance = -(m_Plane.m_vNormal * m_pPoints[0]); 
		m_Plane.m_vPointOnPlane = m_pPoints[0]; 
 
		CalcBoundingBox(); 
	} 
	//////////////////////////////////////////////////////////////////////////////// 
	void cePolygon::CalcBoundingBox() 
	{ 
		PUSH_FUNCTION; 
 
		ceVec3f vMax, vMin; 
 
		// Set a randomize point as start of search 
		vMax = vMin = m_pPoints[0]; 
 
		// Iterate sequenciell over all nodes to find limitations 
		for (size_t i=0; i < m_uiNumOfPoints; ++i) { 
			if (m_pPoints[i].x > vMax.x) 
				vMax.x = m_pPoints[i].x; 
			else if (m_pPoints[i].x < vMin.x) 
				vMin.x = m_pPoints[i].x; 
			if (m_pPoints[i].y > vMax.y) 
				vMax.y = m_pPoints[i].y; 
			else if (m_pPoints[i].y < vMin.y) 
				vMin.y = m_pPoints[i].y; 
			if (m_pPoints[i].z > vMax.z) 
				vMax.z = m_pPoints[i].z; 
			else if (m_pPoints[i].z < vMin.z) 
				vMin.z = m_pPoints[i].z; 
		} 
 
		// Set found limitations 
		m_AABB.m_vMax = vMax; 
		m_AABB.m_vMin = vMin; 
		m_AABB.m_vCenter = (vMax + vMin) * 0.5f; 
	} 
	//////////////////////////////////////////////////////////////////////////////// 
	void cePolygon::Clip(const cePlane& Plane, cePolygon* pFront,  
				cePolygon* pBack) const 
	{ 
		PUSH_FUNCTION; 
 
		if (NULL == pFront && NULL == pBack) 
			return; 
 
		UINT uiNumInFront=0, uiNumInBack=0; 
 
		ceVec3f *pInFront = NULL; 
		ceVec3f *pInBack  = NULL; 
		const size_t size = m_uiNumOfPoints*3; 
		pInFront = new ceVec3f[size]; 
		pInBack = new ceVec3f[size]; 
		ce_assert(NULL != pInFront && NULL != pInBack); 
		 
		// Classify first point of pointlist  
		switch (Plane.Classify(m_pPoints[0])){ 
		case ceFront: 
			pInFront[uiNumInFront++] = m_pPoints[0]; 
			break; 
		 
		case ceBack: 
			pInBack[uiNumInBack++] = m_pPoints[0]; 
			break; 
		 
		case cePlanar: 
			pInFront[uiNumInFront++] = m_pPoints[0]; 
			pInBack[uiNumInBack++] = m_pPoints[0]; 
			break; 
		default: 
			return; 
		} 
 
		// Der erste Index ist 1, da wir ja oben schon das erste Element setzen 
		for (UINT uiLoop=1; uiLoop < (m_uiNumOfPoints+1); uiLoop++) 
		{ 
			UINT uiCurrent = uiLoop; 
			if(uiLoop == m_uiNumOfPoints) 
				uiCurrent = 0; 
 
			// 2 benachbarte Punkte 
			const ceVec3f vA = m_pPoints[uiLoop-1]; 
			const ceVec3f vB = m_pPoints[uiCurrent]; 
 
			const INT uiClassA = Plane.Classify(vA); 
			//const UINT uiClassB = Plane.Classify(vB); 
 
			// Wenn planar, dann in beide Liste hinzufügen 
			if (uiClassA == cePlanar) 
			{ 
				pInFront[uiNumInFront++] = m_pPoints[uiCurrent]; 
				pInBack[uiNumInBack++] = m_pPoints[uiCurrent]; 
			} 
			else // Schneidet dieser Punkt die Ebene? 
			{ 
				ceRay Ray; 
				Ray.m_vOrigin = vA; 
				Ray.m_vDir = vB - vA; 
 
				FLOAT fLength = Ray.m_vDir.GetLength(); 
 
				if (0.0f != fLength) 
					Ray.m_vDir /= fLength; 
 
				ceVec3f vHit; 
				if( Ray.Intersects(Plane, FALSE, fLength, 0, &vHit) && uiClassA != cePlanar) 
				{ 
					pInBack[uiNumInBack++] = vHit; 
					uiNumInFront++; 
					pInFront[uiNumInFront] = vHit; 
 
					if(uiClassA == ceFront) 
					{ 
						if (0 != uiCurrent) 
							pInFront[uiNumInFront++] = m_pPoints[uiCurrent]; 
					} 
					else if (uiClassA == ceBack) 
					{ 
						if (0 != uiCurrent) { 
							uiNumInBack++; 
							pInBack[uiNumInBack] = m_pPoints[uiCurrent]; 
						}	 
					} 
				} 
				else 
				{ 
					if (0 == uiCurrent) 
						continue; 
 
					if (uiClassA == ceFront) 
						pInFront[uiNumInFront++] = m_pPoints[uiCurrent]; 
					else if (uiClassA == ceBack) 
						pInBack[uiNumInBack++] = m_pPoints[uiCurrent]; 
					else 
						return; 
				} 
			} 
		} 
 
		// Wir haben jetzt die beiden Polygone, fehlen noch die Indices 
		WORD usIndex0, usIndex1, usIndex2; 
	 
		WORD* pusFront = 0; 
		WORD* pusBack = 0; 
 
		if( uiNumInFront > 2) 
		{ 
			pusFront = new WORD[ (uiNumInFront-2) * 3]; 
 
			usIndex0 = 0; 
			usIndex1 = 1; 
			usIndex2 = 2; 
			for(UINT uiLoop=0; uiLoop < (uiNumInFront-2); uiLoop++) 
			{ 
				if (0 != uiLoop) 
				{ 
					usIndex1 = usIndex2; 
					usIndex2++; 
				} 
 
				pusFront[ uiLoop * 3] = usIndex0; 
				pusFront[ uiLoop * 3 + 1] = usIndex1; 
				pusFront[ uiLoop * 3 + 2] = usIndex2; 
			} 
		} 
		if (uiNumInBack > 2) 
		{ 
			pusBack = new WORD[ (uiNumInBack-2) * 3]; 
 
			usIndex0 = 0; 
			usIndex1 = 1; 
			usIndex2 = 2; 
			for (UINT uiLoop=0; uiLoop < (uiNumInBack-2); uiLoop++) 
			{ 
				if (0 != uiLoop) 
				{ 
					usIndex1 = usIndex2; 
					usIndex2++; 
				} 
 
				pusBack[ uiLoop * 3] = usIndex0; 
				pusBack[ uiLoop * 3 + 1] = usIndex1; 
				pusBack[ uiLoop * 3 + 2] = usIndex2; 
			} 
		} 
 
		if (0 != pFront && 0 != pusFront) 
		{ 
			pFront->Set(pInFront, uiNumInFront, pusFront, (uiNumInFront-2) * 3); 
 
			// Orientierung richten 
			if (pFront->GetPlane().m_vNormal * m_Plane.m_vNormal < 0.0f) 
				pFront->SwapFaces(); 
		} 
 
		if (0 != pBack && 0 != pusBack) 
		{ 
			pBack->Set(pInBack, uiNumInBack, pusBack, (uiNumInBack-2) * 3); 
 
			// Orientierung richten 
			if (pBack->GetPlane().m_vNormal * m_Plane.m_vNormal < 0.0f) 
				pBack->SwapFaces(); 
		} 
 
		// Release allocated memory 
		delete[] pInBack; 
		pInBack = 0; 
		delete[] pInFront; 
		pInFront = 0; 
		delete[] pusBack; 
		pusBack = 0; 
		delete[] pusFront; 
		pusFront = 0; 
	}	 
	//////////////////////////////////////////////////////////////////////////////// 
	void cePolygon::Clip(const ceAABB& AABB) 
	{ 
		cePolygon BackPolygon, ClippedPolygon; 
		cePlane Planes[6]; 
		BOOL bClipped=FALSE; 
 
		// Die Ebenen der Box berechnen, die Normalen zeigen nach außen 
		AABB.GetPlanes(Planes); 
 
		ClippedPolygon.CopyFrom( *this); 
 
		// Nun das Polygon gegen die Ebenen der Box clippen 
		for(UINT i=0; i < 6; i++) 
		{ 
			if(Planes[i].Classify(ClippedPolygon) == ceClipped) 
			{ 
				ClippedPolygon.Clip(Planes[i], 0, &BackPolygon); 
				ClippedPolygon.CopyFrom( BackPolygon); 
				bClipped = TRUE; 
			} 
		} 
		// Wenn das Polygon geändert wurde, dann kopieren wir es in dieses Objekt 
		// XXX: Das ist gefaehrlich, wenn das Verhalten nicht dokumentiert wird. Die andere Clip() 
		//      Methode tut das nicht. 
		if (bClipped) 
			this->CopyFrom(ClippedPolygon); 
	} 
	//////////////////////////////////////////////////////////////////////////////// 
	INT cePolygon::Cull(const ceAABB& AABB) const 
	{ 
		cePlane Planes[6]; 
		AABB.GetPlanes(Planes); 
 
		// Schneidet das Polygon die Box überhaupt 
		if(m_AABB.Intersects(AABB) == FALSE) 
			return ceCulled; 
 
		// Das Polygon KÖNNTE die Box nun schneiden, muss aber nicht 
		BOOL bFirst = TRUE; 
		UINT uiInside = 0; 
		for(UINT i=0; i < 6; i++) 
		{ 
			if(bFirst) 
			{ 
				for(UINT p=0; p < m_uiNumOfPoints; p++) 
				{ 
					if( AABB.Intersects( m_pPoints[i] ) ) 
						uiInside++; 
				} 
				bFirst = FALSE; 
 
				if(uiInside == m_uiNumOfPoints) 
					return ceVisible; 
			} 
 
			UINT uiCurrent = 0; 
			for (UINT uiLoop=1; uiLoop < (m_uiNumOfPoints+1); uiLoop++) 
			{ 
				if(uiLoop == m_uiNumOfPoints) 
					uiCurrent = 0; 
				else 
					uiCurrent = uiLoop; 
 
				ceRay Ray; 
				Ray.m_vOrigin = m_pPoints[uiLoop-1]; 
				Ray.m_vDir = m_pPoints[uiCurrent] - m_pPoints[uiLoop-1]; 
			 
				const FLOAT fLength = Ray.m_vDir.GetLength(); 
 
				if (fLength != 0.0f) 
					Ray.m_vDir /= fLength; 
 
				if(Ray.Intersects(Planes[i], FALSE, fLength, 0, 0) ) 
					return ceClipped; 
			} 
		} 
 
		return ceCulled; 
	} 
	//////////////////////////////////////////////////////////////////////////////// 
	void cePolygon::CopyFrom(const cePolygon& Polygon) 
	{ 
		Set(Polygon.m_pPoints, Polygon.m_uiNumOfPoints, Polygon.m_puiIndices, Polygon.m_uiNumOfIndices); 
	} 
	//////////////////////////////////////////////////////////////////////////////// 
	void cePolygon::SwapFaces() 
	{ 
		WORD* pusIndices = new WORD[m_uiNumOfIndices]; 
 
		for (UINT i=0; i < m_uiNumOfIndices; i++) 
			pusIndices[m_uiNumOfIndices-i-1] = m_puiIndices[i]; 
 
		m_Plane.m_vNormal *= -1.0f; 
		m_Plane.m_fDistance *= -1.0f; 
 
		delete[] m_puiIndices; 
		m_puiIndices = pusIndices; 
	} 
	//////////////////////////////////////////////////////////////////////////////// 
	bool cePolygon::Intersects(const ceRay& Ray, BOOL bCull, FLOAT* pF) const 
	{ 
		ceRay* pRay = (ceRay*) &Ray; 
 
		for (UINT i=0; i < m_uiNumOfIndices; i+=3) { 
			if (pRay->Intersects(m_pPoints[ m_puiIndices[i] ], 
								m_pPoints[ m_puiIndices[i+1] ], 
								m_pPoints[ m_puiIndices[i+2] ], 
								FALSE, pF) ) { 
				return true; 
			} 
			if (!bCull) { 
				if( pRay->Intersects(m_pPoints[ m_puiIndices[i+2] ], 
									m_pPoints[ m_puiIndices[i+1] ], 
									m_pPoints[ m_puiIndices[i] ], 
									FALSE, pF) ) { 
					return true; 
				} 
			} 
		} 
	 
		return false; 
	} 
	//////////////////////////////////////////////////////////////////////////////// 
	bool cePolygon::Intersects(const ceRay& Ray, BOOL bCull, FLOAT fL,  
				FLOAT* pF) const 
	{ 
		ceRay* pRay = (ceRay*) &Ray; 
 
		for (UINT i=0; i < m_uiNumOfIndices; i+=3) { 
			if( pRay->Intersects(m_pPoints[ m_puiIndices[i] ], 
								m_pPoints[ m_puiIndices[i+1] ], 
								m_pPoints[ m_puiIndices[i+2] ], 
								FALSE, fL, pF) ) { 
				return true; 
			} 
			if (!bCull) { 
				if( pRay->Intersects(m_pPoints[ m_puiIndices[i+2] ], 
									m_pPoints[ m_puiIndices[i+1] ], 
									m_pPoints[ m_puiIndices[i] ], 
									FALSE, fL, pF) ) { 
					return true; 
				} 
			} 
		} 
 
		return false; 
	} 
 
} // Namespace ZFXCE