#include "StdAfx.h" #include "ClipGeometryProcess.h" #define decalEpsilon 0.25f NiPoint3 CClipGeometryProcess::m_kDecalCenter; NiPoint3 CClipGeometryProcess::m_kDecalNormal; NiPlane CClipGeometryProcess::m_kLeftPlane; NiPlane CClipGeometryProcess::m_kRightPlane; NiPlane CClipGeometryProcess::m_kBottomPlane; NiPlane CClipGeometryProcess::m_kTopPlane; NiPlane CClipGeometryProcess::m_kFrontPlane; NiPlane CClipGeometryProcess::m_kBackPlane; unsigned int CClipGeometryProcess::m_uiMaxVertexCount = 0; unsigned int CClipGeometryProcess::m_uiMaxTriangleCount = 0; int CClipGeometryProcess::m_iDecalVertexCount = 0; int CClipGeometryProcess::m_iDecalTriangleCount = 0; NiPoint3 CClipGeometryProcess::m_kVertexArray[MAX_DecalVertices]; NiPoint2 CClipGeometryProcess::m_kTexcoordArray[MAX_DecalVertices]; CClipGeometryProcess::Triangle CClipGeometryProcess::m_kTriangleArray[MAX_DecalVertices]; CClipGeometryProcess::CClipGeometryProcess() { } CClipGeometryProcess::~CClipGeometryProcess() { } bool CClipGeometryProcess::Clip( NiGeometry* pkGeometry, const NiPoint3& center, const NiPoint3& normal, const NiPoint3& tangent, float width, float length, float height ) { m_kDecalCenter = center; m_kDecalNormal = normal; const NiTransform& kModelWorldTransform = pkGeometry->GetWorldTransform(); NiPoint3 binormal; binormal = tangent.Cross(normal); // Calculate boundary planes float d = m_kDecalCenter.Dot(tangent); m_kLeftPlane = NiPlane(tangent, width * 0.5f - d); m_kRightPlane = NiPlane(-tangent, width * 0.5f + d); d = m_kDecalCenter.Dot(normal); m_kFrontPlane = NiPlane(normal, height - d); m_kBackPlane = NiPlane(-normal, 0.3f + d); d = m_kDecalCenter.Dot(binormal); m_kBottomPlane = NiPlane(binormal, length * 0.5f - d); m_kTopPlane = NiPlane(-binormal, length * 0.5f + d); // Begin with empty mesh m_iDecalVertexCount = 0; m_iDecalTriangleCount = 0; float fT = NiGetCurrentTimeInSec(); ClipGeometry(pkGeometry); if(m_iDecalVertexCount == 0) return false; // Assign texture mapping coordinates float one_over_w = 1.0f / width; float one_over_h = 1.0f / length; for(long a = 0; a < m_iDecalVertexCount; ++a) { NiPoint3 v = m_kVertexArray[a]; float s = v.Dot(tangent) * one_over_w + 0.5f; float t = v.Dot(binormal) * one_over_h + 0.5f; m_kVertexArray[a] = NiPoint3(v.x, v.y, v.z); m_kTexcoordArray[a] = NiPoint2(s, t); } return true; } void CClipGeometryProcess::ClipGeometry(NiGeometry *pkGeometry) { NiPoint3* kVerts = pkGeometry->GetVertices(); NiPoint3* kNorms = pkGeometry->GetNormals(); if (!kVerts || !kNorms) return; const NiTransform& kModelTransform = pkGeometry->GetWorldTransform(); const NiMatrix3& kRot = kModelTransform.m_Rotate; if(NiIsKindOf(NiTriShape, pkGeometry)) { NiTriShape* pkTriShape = NiDynamicCast(NiTriShape, pkGeometry); unsigned short* usTriList = pkTriShape->GetTriList(); unsigned int uiTriLength = pkTriShape->GetTriListLength(); NiPoint3 kTris[9]; NiPoint3 kNorm[9]; for(unsigned int ui = 0; ui < uiTriLength;) { kNorm[0] = kRot * kNorms[usTriList[ui]]; kTris[0] = kModelTransform * kVerts[usTriList[ui++]]; kNorm[1] = kRot * kNorms[usTriList[ui]]; kTris[1] = kModelTransform * kVerts[usTriList[ui++]]; kNorm[2] = kRot * kNorms[usTriList[ui]]; kTris[2] = kModelTransform * kVerts[usTriList[ui++]]; NiPoint3 cross; long count = ClipPolygon(3, kTris, kNorm, kTris, kNorm); if((count != 0) && (!AddPolygon(count, kTris, kNorm))) break; } } else if(NiIsKindOf(NiTriStrips, pkGeometry)) { NiTriStrips* pkTriStrips = NiDynamicCast(NiTriStrips, pkGeometry); unsigned int uiTriCount = pkTriStrips->GetTriangleCount(); unsigned short i0, i1, i2; NiPoint3 kTris[9]; NiPoint3 kNorm[9]; for(unsigned int ui = 0; ui < uiTriCount; ui++) { pkTriStrips->GetTriangleIndices(ui, i0, i1, i2); kTris[0] = kModelTransform * kVerts[i0]; kTris[1] = kModelTransform * kVerts[i1]; kTris[2] = kModelTransform * kVerts[i2]; kNorm[0] = kRot * kNorms[i0]; kNorm[1] = kRot * kNorms[i1]; kNorm[2] = kRot * kNorms[i2]; NiPoint3 cross; cross = (kTris[1] - kTris[0]).Cross(kTris[2] - kTris[0]); long count = ClipPolygon(3, kTris, kNorm, kTris, kNorm); if((count != 0) && (!AddPolygon(count, kTris, kNorm))) break; } } } long CClipGeometryProcess::ClipPolygon( long vertexCount, const NiPoint3* vertex, const NiPoint3* normal, NiPoint3* newVertex, NiPoint3* newNormal) { static NiPoint3 tempVertex[9]; static NiPoint3 tempNormal[9]; // Clip against all six planes long count = ClipPolygonAgainstPlane(m_kLeftPlane, vertexCount, vertex, normal, tempVertex, tempNormal); if(count != 0) { count = ClipPolygonAgainstPlane(m_kRightPlane, count, tempVertex, tempNormal, newVertex, newNormal); if(count != 0) { count = ClipPolygonAgainstPlane(m_kBottomPlane, count, newVertex, newNormal, tempVertex, tempNormal); if(count != 0) { count = ClipPolygonAgainstPlane(m_kTopPlane, count, tempVertex, tempNormal, newVertex, newNormal); if(count != 0) { count = ClipPolygonAgainstPlane(m_kBackPlane, count, newVertex, newNormal, tempVertex, tempNormal); if(count != 0) { count = ClipPolygonAgainstPlane(m_kFrontPlane, count, tempVertex, tempNormal, newVertex, newNormal); } } } } } return count; } float Dot(const NiPlane& p, const NiPoint3& q) { return (p.m_kNormal.x * q.x + p.m_kNormal.y * q.y + p.m_kNormal.z * q.z + p.m_fConstant); } long CClipGeometryProcess::ClipPolygonAgainstPlane( const NiPlane& plane, long vertexCount, const NiPoint3* vertex, const NiPoint3* normal, NiPoint3* newVertex, NiPoint3* newNormal) { bool negative[10]; // Classify vertices long negativeCount = 0; for(long a = 0; a < vertexCount; ++a) { bool neg = (Dot(plane, vertex[a]) < 0.0F); negative[a] = neg; negativeCount += neg; } // Discard this polygon ifit's completely culled if(negativeCount == vertexCount) return 0; long count = 0; for(long b = 0; b < vertexCount; ++b) { // c is the index of the previous vertex long c = (b != 0) ? b - 1 : vertexCount - 1; if(negative[b]) { if(!negative[c]) { // Current vertex is on negative side of plane, // but previous vertex is on positive side. const NiPoint3& v1 = vertex[c]; const NiPoint3& v2 = vertex[b]; float t = Dot(plane, v1) / (plane.m_kNormal.x * (v1.x - v2.x) + plane.m_kNormal.y * (v1.y - v2.y) + plane.m_kNormal.z * (v1.z - v2.z)); newVertex[count] = v1 * (1.0f - t) + v2 * t; const NiPoint3& n1 = normal[c]; const NiPoint3& n2 = normal[b]; newNormal[count] = n1 * (1.0f - t) + n2 * t; ++count; } } else { if(negative[c]) { // Current vertex is on positive side of plane, // but previous vertex is on negative side. const NiPoint3& v1 = vertex[b]; const NiPoint3& v2 = vertex[c]; float t = Dot(plane, v1) / (plane.m_kNormal.x * (v1.x - v2.x) + plane.m_kNormal.y * (v1.y - v2.y) + plane.m_kNormal.z * (v1.z - v2.z)); newVertex[count] = v1 * (1.0f - t) + v2 * t; const NiPoint3& n1 = normal[b]; const NiPoint3& n2 = normal[c]; newNormal[count] = n1 * (1.0f - t) + n2 * t; ++count; } // Include current vertex newVertex[count] = vertex[b]; newNormal[count] = normal[b]; ++count; } } // Return number of vertices in clipped polygon return count; } bool CClipGeometryProcess::AddPolygon( long vertexCount, const NiPoint3* vertex, const NiPoint3* normal) { long count = m_iDecalVertexCount; if(count + vertexCount >= MAX_DecalVertices) return false; // Add polygon as a triangle fan Triangle *triangle = m_kTriangleArray + m_iDecalTriangleCount; m_iDecalTriangleCount += vertexCount - 2; for(long a = 0; a < vertexCount; ++a) { triangle->index[0] = (unsigned short)count; triangle->index[1] = (unsigned short)(count + a + 1); triangle->index[2] = (unsigned short)(count + a + 2); ++triangle; } // Assign texture mapping coordinates //float one_over_w = 1.0f / width; //float one_over_h = 1.0f / length; //for(long a = 0; a < m_uiMaxVertexCount; ++a) //{ // NiPoint3 v = m_kVertexArray[a] - center; // float s = v.Dot(tangent) * one_over_w + 0.5f; // float t = v.Dot(binormal) * one_over_h + 0.5f; // m_kTexcoordArray[a] = NiPoint2(s, t); //} /* for(int index = 0; index < m_uiMaxVertexCount; index++) { m_pkVertex[index] = m_kVertexArray[index]; m_pkVertex[index].z += 0.05f; m_pkTexture[index] = m_kTexcoordArray[index]; } */ // Assign vertex colors float f = 1.0F / (1.0F - decalEpsilon); for(long b = 0; b < vertexCount; ++b) { m_kVertexArray[count] = vertex[b]; const NiPoint3& n = normal[b]; float alpha = (m_kDecalNormal.Dot(n) / n.Length() - decalEpsilon) * f; ++count; } m_iDecalVertexCount = count; return true; }