#include "StdAfx.h" #include "DecalModelRenderer.h" #include "Map/Box.h" #include "DecalModel.h" DecalModelRenderer::DecalModelRenderer() { } DecalModelRenderer::~DecalModelRenderer() { } void DecalModelRenderer::AddDecal(DecalModel* pkDecal) { m_DecalMap.SetAt(pkDecal, pkDecal); } void DecalModelRenderer::RemoveDecal(DecalModel* pkDecal) { m_DecalMap.RemoveAt(pkDecal); } void DecalModelRenderer::Clear() { m_DecalMap.RemoveAll(); } bool DecalModelRenderer::BuildDecal(NiAVObject* pkModel, DecalModel* pkDecal, int iModelZoneX, int iModelZoneY) { if (!pkDecal || !pkModel) return false; if (!NiIsKindOf(NiGeometry, pkModel)) return false; NiPoint3 kDecalWorldPos = pkDecal->GetPosition(); float fRadius = pkDecal->GetRadius(); float fDepth = pkDecal->GetDepth(); NiBound kDecalBound; kDecalBound.SetCenterAndRadius(kDecalWorldPos, fRadius); const NiBound& kModelBound = pkModel->GetWorldBound(); if (!NiBound::TestIntersect(0.0f, kDecalBound, NiPoint3::ZERO, kModelBound, NiPoint3::ZERO)) return false; NiGeometry* pkGeometry = (NiGeometry*)pkModel; if (!pkGeometry) return false; unsigned short usVertCount = pkGeometry->GetVertexCount(); NiPoint3* pkResult = NiNew NiPoint3[usVertCount]; if (!pkResult) return false; const NiTransform& kWorldTransform = pkModel->GetWorldTransform(); NiTriBasedGeom* pkTriBasedGeom = NiDynamicCast(NiTriBasedGeom, pkGeometry); NiPoint3* pkVertices = pkTriBasedGeom->GetVertices(); unsigned short usTriangleCount = pkTriBasedGeom->GetTriangleCount(); unsigned short usVertexCount = pkTriBasedGeom->GetVertexCount(); unsigned short usResultCount = ClipGeometry(usVertexCount, pkVertices, kWorldTransform, kDecalBound, pkResult); if (usResultCount <= 0) return false; pkDecal->BuildGeom(usResultCount, pkResult); delete[] pkResult; return true; } unsigned int DecalModelRenderer::ClipGeometry(unsigned int uiVertexCount, const NiPoint3* pkVert, const NiTransform& kWorldTransform, const NiBound& kBound, NiPoint3* pkResult) { // 0:l 1:r 2:f 3:b 4:t 5:b NiPoint3 kC = kBound.GetCenter(); float fRadius = kBound.GetRadius(); NiPoint3 left, right, front, back, top, bottom; left = kC+NiPoint3(fRadius, 0, 0); right = kC+NiPoint3(-fRadius, 0, 0); front = kC+NiPoint3(0, -fRadius, 0); back = kC+NiPoint3(0, fRadius, 0); top = kC+NiPoint3(0, 0, fRadius); bottom = kC+NiPoint3(0, 0, -fRadius); NiPlane kPlane[6] = { NiPlane(NiPoint3::UNIT_Y, front), NiPlane(-NiPoint3::UNIT_Y, back), NiPlane(-NiPoint3::UNIT_X, left), NiPlane(NiPoint3::UNIT_X, right), NiPlane(NiPoint3::UNIT_Z, bottom), NiPlane(-NiPoint3::UNIT_Z, top) }; NiPoint3 pkVertBuffer[1024]; unsigned int ui = 0; unsigned short usResultCount = 0; usResultCount = ClipPolygonAgainstPlane(kPlane[0], uiVertexCount, /*pucLocation,*/ pkVert, kWorldTransform, pkVertBuffer, true); if (usResultCount > 0) { usResultCount = ClipPolygonAgainstPlane(kPlane[1], usResultCount, /*pucLocation,*/ pkVertBuffer, kWorldTransform, pkResult, false); if (usResultCount > 0) { usResultCount = ClipPolygonAgainstPlane(kPlane[2], usResultCount, /*pucLocation,*/ pkResult, kWorldTransform, pkVertBuffer, false); if (usResultCount > 0) { usResultCount = ClipPolygonAgainstPlane(kPlane[3], usResultCount, /*pucLocation,*/ pkVertBuffer, kWorldTransform, pkResult, false); if (usResultCount > 0) { usResultCount = ClipPolygonAgainstPlane(kPlane[4], usResultCount, /*pucLocation,*/ pkResult, kWorldTransform, pkVertBuffer, false); if (usResultCount > 0) { usResultCount = ClipPolygonAgainstPlane(kPlane[5], usResultCount, /*pucLocation,*/ pkVertBuffer, kWorldTransform, pkResult, false); } } } } } return usResultCount; } unsigned int DecalModelRenderer::ClipPolygonAgainstPlane(const NiPlane& kPlane, unsigned int uiVertexCount, /*unsigned char* location,*/ const NiPoint3* pkVert, const NiTransform& kWorldTransform, NiPoint3* pkResult, bool bTransform) { m_pucLocation = NiAlloc(unsigned char, uiVertexCount * 2); unsigned int uiInteriorCount = 0; unsigned int uiExteriorCount = 0; const float fEpsilon = 0.001f; unsigned int ui = 0; for (ui; ui < uiVertexCount; ui++) { NiPoint3 kPos = bTransform?kWorldTransform*pkVert[ui]:pkVert[ui]; if (kPlane.GetNormal().Dot(kPos)-kPlane.GetConstant() > fEpsilon) { m_pucLocation[ui] = NiPlane::POSITIVE_SIDE; uiInteriorCount++; } else if (kPlane.GetNormal().Dot(kPos)-kPlane.GetConstant() < -fEpsilon) { m_pucLocation[ui] = NiPlane::NEGATIVE_SIDE; uiExteriorCount++; } else { m_pucLocation[ui] = NiPlane::NO_SIDE; } } if (uiExteriorCount== 0) return uiVertexCount; if (uiInteriorCount == 0) return 0; // kPlane is m_kNormal*(x,y,z) = m_fConstant // m_kNormal*(x,y,z) - m_fConstant = 0 unsigned int uiCount = 0; unsigned int uiPrevious = uiVertexCount - 1; for (unsigned ui = 0; ui < uiVertexCount; ui++) { unsigned int uiLoc = m_pucLocation[ui]; if ( uiLoc == NiPlane::NEGATIVE_SIDE) //Exterior { if (m_pucLocation[uiPrevious] == NiPlane::POSITIVE_SIDE) { const NiPoint3& kV1 = bTransform?kWorldTransform*pkVert[uiPrevious]:pkVert[uiPrevious]; const NiPoint3& kV2 = bTransform?kWorldTransform*pkVert[ui]:pkVert[ui]; const NiPoint3 kDelta = kV2 - kV1; NiPoint3 kN = kPlane.GetNormal(); float fD = kPlane.GetConstant(); float fT = (fD - kN.Dot(kV1))/kN.Dot(kDelta); pkResult[uiCount++] = kV1 + kDelta*fT; } } else { const NiPoint3& kV1 = bTransform?kWorldTransform*pkVert[ui]:pkVert[ui]; if (uiLoc ==NiPlane::POSITIVE_SIDE && m_pucLocation[uiPrevious] == NiPlane::NEGATIVE_SIDE) { const NiPoint3& kV2 = bTransform?kWorldTransform*pkVert[uiPrevious]:pkVert[uiPrevious]; const NiPoint3 kDelta = kV2 - kV1; NiPoint3 kN = kPlane.GetNormal(); float fD = kPlane.GetConstant(); float fT = (fD - kN.Dot(kV1))/kN.Dot(kDelta); pkResult[uiCount++] = kV1 + kDelta*fT; } pkResult[uiCount++] = kV1; } uiPrevious = ui; } NiFree(m_pucLocation); return uiCount; } void DecalModelRenderer::Render(int iZoneX, int iZoneY, NiAVObject* pkModel, unsigned int frameID) { NiTMapIterator pos = m_DecalMap.GetFirstPos(); while (pos) { DecalModel* pkDecal = NULL; m_DecalMap.GetNext(pos, pkDecal, pkDecal); if (!pkDecal) continue; if (!BuildDecal(pkModel, pkDecal, iZoneX, iZoneY)) continue; pkDecal->Render(); } }