#include "StdAfx.h" #include "DynamicSimpleMesh.h" const unsigned short DynamicSimpleMeshData::ms_usInvalid = (unsigned short)~0; DynamicSimpleMeshData::DynamicSimpleMeshData(bool bWantNormals, bool bWantColors, unsigned short usNumTexturesets, int iMaxPQuantity, int iPGrowBy, int iMaxVQuantity, int iVGrowBy, int iMaxTQuantity, int iTGrowBy) { NIASSERT(iMaxPQuantity > 0 && iPGrowBy > 0); NIASSERT(iMaxVQuantity > 0 && iVGrowBy > 0); NIASSERT(iMaxTQuantity > 0 && iTGrowBy > 0); // polygons m_usMaxPQuantity = (unsigned short)(iMaxPQuantity > 0 ? iMaxPQuantity : 1); SetPGrowBy(iPGrowBy); m_usPQuantity = 0; m_akPolygon = NiAlloc(Polygon, m_usMaxPQuantity); m_ausPIndexer = NiAlloc(unsigned short,m_usMaxPQuantity); memset(m_ausPIndexer, 0xFF, m_usMaxPQuantity * sizeof(unsigned short)); // vertices m_usMaxVQuantity = (unsigned short)(iMaxVQuantity > 0 ? iMaxVQuantity : 1); SetVGrowBy(iVGrowBy); SetVertexCount(0); m_pkVertex = NiNew NiPoint3[m_usMaxVQuantity]; // normals if (bWantNormals) { m_pkNormal = NiNew NiPoint3[m_usMaxVQuantity]; for (unsigned short i = 0; i < m_usMaxVQuantity; i++) m_pkNormal[i] = -NiPoint3::UNIT_Z; } else { m_pkNormal = 0; } // colors (initial maximum storage assumes all rectangles) m_pkColor = (bWantColors ? NiNew NiColorA[m_usMaxVQuantity] : 0); // texture coordinates (initial maximum storage assumes all rectangles) if (usNumTexturesets > 0) { int iQuantity = (int)usNumTexturesets * (int)m_usMaxVQuantity; m_pkTexture = NiNew NiPoint2[iQuantity]; SetNumTexturesets(usNumTexturesets); } else { m_pkTexture = 0; } // triangles (initial maximum storage assumes all rectangles) m_usMaxIQuantity = (unsigned short)(iMaxTQuantity > 0 ? 3 * iMaxTQuantity : 3); SetTGrowBy(iTGrowBy); SetTriangleCount(0); m_uiTriListLength = 0; m_pusTriList = NiAlloc(unsigned short,m_usMaxIQuantity); m_bBoundNeedsUpdate = false; } DynamicSimpleMeshData::~DynamicSimpleMeshData() { RemoveAll(); NiFree(m_akPolygon); NiFree(m_ausPIndexer); } bool DynamicSimpleMeshData::IsValid(int iPolygon) const { return 0 <= iPolygon && iPolygon < (int)m_usMaxPQuantity && m_ausPIndexer[iPolygon] != ms_usInvalid; } int DynamicSimpleMeshData::Insert(unsigned short usNumVertices, unsigned short usNumTriangles/*=0*/, const unsigned short* ausTriList/*=0*/) { NIASSERT(usNumVertices >= 3); if (usNumVertices < 3) return -1; NIASSERT(usNumTriangles == 0 || ausTriList); if (usNumTriangles > 0 && !ausTriList) return -1; // If the input number of triangles is zero, the polygon is assumed to be // convex. A triangle fan is used to represent it. if (usNumTriangles == 0) usNumTriangles = usNumVertices - 2; // resize polygon array (if necessary) unsigned short usPolygon = m_usPQuantity++; unsigned short usNewMaxQuantity; int iNumBytes; unsigned int uiDestBytes; if (m_usPQuantity > m_usMaxPQuantity) { usNewMaxQuantity = m_usMaxPQuantity + m_usPGrowBy; Polygon* akNewPolygon = NiAlloc(Polygon,usNewMaxQuantity); iNumBytes = m_usMaxPQuantity * sizeof(Polygon); NiMemcpy(akNewPolygon, m_akPolygon, iNumBytes); NiFree( m_akPolygon); m_akPolygon = akNewPolygon; unsigned short* ausNewPIndexer = NiAlloc(unsigned short, usNewMaxQuantity); uiDestBytes = usNewMaxQuantity * sizeof(unsigned short); iNumBytes = m_usMaxPQuantity * sizeof(unsigned short); NiMemcpy(ausNewPIndexer, uiDestBytes, m_ausPIndexer, iNumBytes); iNumBytes = (usNewMaxQuantity - m_usMaxPQuantity) * sizeof(unsigned short); NIASSERT(iNumBytes > 0); memset(&ausNewPIndexer[m_usMaxPQuantity], 0xFF, iNumBytes); NiFree( m_ausPIndexer); m_ausPIndexer = ausNewPIndexer; m_usMaxPQuantity = usNewMaxQuantity; } // create the polygon Polygon& kPoly = m_akPolygon[usPolygon]; kPoly.m_usNumVertices = usNumVertices; kPoly.m_usVOffset = m_usVertices; kPoly.m_usNumTriangles = usNumTriangles; kPoly.m_usIOffset = 3 * m_usTriangles; // Find the first available polygon handle for the new index. unsigned short i; for (i = 0; i < m_usPQuantity; i++) { if (m_ausPIndexer[i] == ms_usInvalid) { m_ausPIndexer[i] = usPolygon; usPolygon = i; break; } } NIASSERT(i <= m_usMaxPQuantity); // resize vertex arrays (if necessary) SetVertexCount(m_usVertices + kPoly.m_usNumVertices); int iDelta = (int)m_usVertices - (int)m_usMaxVQuantity; unsigned short usChunks; float fRatio; if (iDelta > 0) { fRatio = (float)iDelta / (float)m_usVGrowBy; usChunks = 1 + (unsigned short)(fRatio + 0.5f); usNewMaxQuantity = m_usMaxVQuantity + usChunks * m_usVGrowBy; // resize vertices NiPoint3* akNewVertex = NiNew NiPoint3[usNewMaxQuantity]; uiDestBytes = usNewMaxQuantity * sizeof(NiPoint3); iNumBytes = m_usMaxVQuantity * sizeof(NiPoint3); NiMemcpy(akNewVertex, uiDestBytes, m_pkVertex, iNumBytes); NiDelete[] m_pkVertex; m_pkVertex = akNewVertex; // resize normals if (m_pkNormal) { NiPoint3* akNewNormal = NiNew NiPoint3[usNewMaxQuantity]; uiDestBytes = usNewMaxQuantity * sizeof(NiPoint3); NiMemcpy(akNewNormal, uiDestBytes, m_pkNormal, iNumBytes); NiDelete[] m_pkNormal; m_pkNormal = akNewNormal; unsigned short i; for (i = m_usMaxVQuantity; i < usNewMaxQuantity; i++) m_pkNormal[i] = -NiPoint3::UNIT_Z; } // resize colors if (m_pkColor) { NiColorA* akNewColor = NiNew NiColorA[usNewMaxQuantity]; iNumBytes = m_usMaxVQuantity * sizeof(NiColorA); uiDestBytes = usNewMaxQuantity * sizeof(NiColorA); NiMemcpy(akNewColor, uiDestBytes, m_pkColor, iNumBytes); NiDelete[] m_pkColor; m_pkColor = akNewColor; } // Resize texture coordinates. The structure of the code is more // complicated than that of vertices, normals, and colors. The // texture coordinate array stores multiple subarrays of coordinates. // The entire array must be resized, but each old subarray must be // copied into the corresponding new subarray. unsigned short usNumSets = GetTexturesets(); if (usNumSets > 0) { int iNewMaxQuantity = (int)usNumSets * (int)usNewMaxQuantity; NiPoint2* akNewTexture = NiNew NiPoint2[iNewMaxQuantity]; iNumBytes = m_usMaxVQuantity * sizeof(NiPoint2); for (unsigned short usSet = 0; usSet < usNumSets; usSet++) { int iOffset = (int)m_usVertices * (int)usSet; NiPoint2* akDstTexture = &akNewTexture[iOffset]; NiPoint2* akSrcTexture = &m_pkTexture[iOffset]; NiMemcpy(akDstTexture, akSrcTexture, iNumBytes); } NiDelete[] m_pkTexture; m_pkTexture = akNewTexture; } m_usMaxVQuantity = usNewMaxQuantity; } // resize index array (if necessary) SetTriangleCount(m_usTriangles + usNumTriangles); m_uiTriListLength += 3 * usNumTriangles; iDelta = 3 * (int)m_usTriangles - (int)m_usMaxIQuantity; if (iDelta > 0) { fRatio = (float)iDelta / (float)m_usIGrowBy; usChunks = 1 + (unsigned short)(fRatio + 0.5f); usNewMaxQuantity = m_usMaxIQuantity + usChunks * m_usIGrowBy; unsigned short* ausNewTriList = NiAlloc(unsigned short, usNewMaxQuantity); iNumBytes = m_usMaxIQuantity * sizeof(unsigned short); uiDestBytes = usNewMaxQuantity * sizeof(unsigned short); NiMemcpy(ausNewTriList, uiDestBytes, m_pusTriList, iNumBytes); NiFree( m_pusTriList); m_pusTriList = ausNewTriList; m_usMaxIQuantity = usNewMaxQuantity; } // Insert the polygon index array. It is important to use the values // kPoly.m_usVOffset and kPoly.m_usIOffset here instead of m_usVertices // and m_usTriangles, since the latter quantities were incremented when // resizing the vertex and index arrays. unsigned short* pusTriList = &m_pusTriList[kPoly.m_usIOffset]; if (ausTriList) { // simple polygon, indices are provided by the caller iNumBytes = usNumTriangles * 3 * sizeof(unsigned short); NiMemcpy(pusTriList, ausTriList, iNumBytes); // Copy over each index adding in the polygon offset unsigned int uiLoop; for (uiLoop = 0; uiLoop < (unsigned int)usNumTriangles * 3; uiLoop++) { pusTriList[uiLoop] = ausTriList[uiLoop] + kPoly.m_usVOffset; } } else { // convex polygon, use a triangle fan for (i = 0; i < usNumTriangles; i++) { *pusTriList++ = kPoly.m_usVOffset; *pusTriList++ = kPoly.m_usVOffset + i + 1; *pusTriList++ = kPoly.m_usVOffset + i + 2; } } MarkAsChanged(VERTEX_MASK); return (int)usPolygon; } bool DynamicSimpleMeshData::Remove(int iPolygon) { if (!IsValid(iPolygon)) return false; // Update the indexer array that maps handles to array indices. First, // invalidate the caller's handle. unsigned short usTarget = m_ausPIndexer[iPolygon]; m_ausPIndexer[iPolygon] = ms_usInvalid; // Second, decrement all indices that are larger than the target index. // This reflects the fact that the polygons will be shifted left by one // to maintain a compact polygon array. unsigned short i, usVisited = 1; for (i = 0; usVisited < m_usPQuantity && i < m_usMaxPQuantity; i++) { if (m_ausPIndexer[i] != ms_usInvalid) { usVisited++; if (m_ausPIndexer[i] > usTarget) m_ausPIndexer[i]--; } } if (m_usPQuantity > 1) { Polygon& kDstPoly = m_akPolygon[usTarget]; if (usTarget + 1 == m_usPQuantity) { // Removing last polygon, so no need to shift arrays. Just // decrement the vertex and triangle counts. SetVertexCount(m_usVertices - kDstPoly.m_usNumVertices); SetTriangleCount(m_usTriangles - kDstPoly.m_usNumTriangles); m_uiTriListLength -= 3 * kDstPoly.m_usNumTriangles; } else { Polygon& kSrcPoly = m_akPolygon[usTarget + 1]; // Shift the vertices to the left. int iVDst = kDstPoly.m_usVOffset; int iVSrc = kSrcPoly.m_usVOffset; NIASSERT(iVSrc - iVDst == kDstPoly.m_usNumVertices); int iVRem = (int)m_usVertices - iVSrc; int iNumBytes = iVRem * sizeof(NiPoint3); memmove(&m_pkVertex[iVDst], &m_pkVertex[iVSrc], iNumBytes); SetVertexCount(m_usVertices - kDstPoly.m_usNumVertices); // Shift the normals to the left. if (m_pkNormal) memmove(&m_pkNormal[iVDst], &m_pkNormal[iVSrc], iNumBytes); // Shift the colors to the left. if (m_pkColor) { iNumBytes = iVRem * sizeof(NiColorA); memmove(&m_pkColor[iVDst], &m_pkColor[iVSrc], iNumBytes); } // Shift the texture coordinates to the left. unsigned short usNumSets = GetTexturesets(); if (usNumSets) { iNumBytes = iVRem * sizeof(NiPoint2); for (unsigned short usSet = 0; usSet < usNumSets; usSet++) { NiPoint2* akTexture = GetTextureset(usSet); memmove(&akTexture[iVDst], &akTexture[iVSrc], iNumBytes); } } // Shift the triangle index array to the left. int iIDst = kDstPoly.m_usIOffset; int iISrc = kSrcPoly.m_usIOffset; NIASSERT(iISrc - iIDst == 3 * kDstPoly.m_usNumTriangles); int iIRem = 3 * (int)m_usTriangles - (int)iISrc; iNumBytes = iIRem * sizeof(unsigned short); memmove(&m_pusTriList[iIDst], &m_pusTriList[iISrc], iNumBytes); SetTriangleCount(m_usTriangles - kDstPoly.m_usNumTriangles); m_uiTriListLength -= 3 * kDstPoly.m_usNumTriangles; // Adjust the index values to account for the shift in the vertex // arrays. unsigned short* pusTriList = &m_pusTriList[iIDst]; for (int j = 0; j < iIRem; j++) { *pusTriList -= kDstPoly.m_usNumVertices; pusTriList++; } // Shift the polygon array left by one to maintain a compact // array. for (i = usTarget; i + 1 < m_usPQuantity; i++) { Polygon& kDst = m_akPolygon[i]; Polygon& kSrc = m_akPolygon[i + 1]; kDst.m_usVOffset = kSrc.m_usVOffset - kDst.m_usNumVertices; kDst.m_usIOffset = kSrc.m_usIOffset - 3 * kDst.m_usNumTriangles; kDst.m_usNumVertices = kSrc.m_usNumVertices; kDst.m_usNumTriangles = kSrc.m_usNumTriangles; } } } else // m_usPQuantity == 1 { SetVertexCount(0); SetTriangleCount(0); m_uiTriListLength = 0; } m_usPQuantity--; MarkAsChanged(DIRTY_MASK); m_bBoundNeedsUpdate = true; return true; } void DynamicSimpleMeshData::RemoveAll() { if (m_usPQuantity > 0) { memset(m_akPolygon, 0, m_usPQuantity * sizeof(Polygon)); m_usPQuantity = 0; SetVertexCount(0); SetTriangleCount(0); MarkAsChanged(DIRTY_MASK); m_bBoundNeedsUpdate = true; } } void DynamicSimpleMeshData::UpdateBound() { m_bBoundNeedsUpdate = false; if (m_usVertices > 0) m_kBound.ComputeFromData(m_usVertices, m_pkVertex); } bool DynamicSimpleMeshData::SetVertices(int iPolygon, const NiPoint3* akValue) { if (IsValid(iPolygon) && akValue) { Polygon& kPoly = GetPolygon(iPolygon); NiPoint3* akVertex = &m_pkVertex[kPoly.m_usVOffset]; for (int i = 0; i < (int)kPoly.m_usNumVertices; i++) { akVertex[i].x = akValue[i].x; akVertex[i].y = akValue[i].y; akVertex[i].z = akValue[i].z; } MarkAsChanged(VERTEX_MASK); m_bBoundNeedsUpdate = true; return true; } return false; } bool DynamicSimpleMeshData::GetVertices(int iPolygon, NiPoint3* akValue) const { if (IsValid(iPolygon) && akValue) { const Polygon& kPoly = GetPolygon(iPolygon); NiPoint3* akVertex = &m_pkVertex[kPoly.m_usVOffset]; for (int i = 0; i < (int)kPoly.m_usNumVertices; i++) { akValue[i].x = akVertex[i].x; akValue[i].y = akVertex[i].y; akValue[i].z = akVertex[i].z; } return true; } return false; } bool DynamicSimpleMeshData::SetVertex(int iPolygon, int iVert, const NiPoint3& kValue) { if (IsValid(iPolygon)) { Polygon& kPoly = GetPolygon(iPolygon); if (iVert < 0 || iVert >= kPoly.m_usNumVertices) { NIASSERT(0); return false; } NiPoint3* akVertex = &m_pkVertex[kPoly.m_usVOffset]; akVertex[iVert] = kValue; MarkAsChanged(VERTEX_MASK); m_bBoundNeedsUpdate = true; return true; } return false; } bool DynamicSimpleMeshData::GetVertex(int iPolygon, int iVert, NiPoint3& kValue) const { if (IsValid(iPolygon)) { const Polygon& kPoly = GetPolygon(iPolygon); if (iVert < 0 || iVert >= kPoly.m_usNumVertices) return false; NiPoint3* pkVertex = &m_pkVertex[kPoly.m_usVOffset + iVert]; kValue = *pkVertex; return true; } return false; } bool DynamicSimpleMeshData::SetColor(int iPolygon, int iVertex, const NiColorA& kValue) { if (m_pkColor && IsValid(iPolygon)) { Polygon& kPoly = GetPolygon(iPolygon); if (0 <= iVertex && iVertex < (int)kPoly.m_usNumVertices) { int i = iVertex + (int)kPoly.m_usVOffset; m_pkColor[i] = kValue; MarkAsChanged(COLOR_MASK); return true; } } return false; } bool DynamicSimpleMeshData::GetColor(int iPolygon, int iVertex, NiColorA& kValue) const { if (m_pkColor && IsValid(iPolygon)) { const Polygon& kPoly = GetPolygon(iPolygon); if (0 <= iVertex && iVertex < (int)kPoly.m_usNumVertices) { int i = iVertex + (int)kPoly.m_usVOffset; kValue = m_pkColor[i]; return true; } } return false; } bool DynamicSimpleMeshData::SetColors(int iPolygon, const NiColorA* akValue) { if (m_pkColor && IsValid(iPolygon) && akValue) { Polygon& kPoly = GetPolygon(iPolygon); if (kPoly.m_usNumVertices > 0) { NiColorA* akColor = &m_pkColor[kPoly.m_usVOffset]; int iNumBytes = kPoly.m_usNumVertices * sizeof(NiColorA); NiMemcpy(akColor, akValue, iNumBytes); MarkAsChanged(COLOR_MASK); return true; } } return false; } bool DynamicSimpleMeshData::SetColors(int iPolygon, const NiColorA& kCommonValue) { if (m_pkColor && IsValid(iPolygon)) { Polygon& kPoly = GetPolygon(iPolygon); if (kPoly.m_usNumVertices > 0) { NiColorA* akColor = &m_pkColor[kPoly.m_usVOffset]; for (unsigned short i = 0; i < kPoly.m_usNumVertices; i++) akColor[i] = kCommonValue; MarkAsChanged(COLOR_MASK); return true; } } return false; } bool DynamicSimpleMeshData::GetColors(int iPolygon, NiColorA* akValue) const { if (m_pkColor && IsValid(iPolygon) && akValue) { const Polygon& kPoly = GetPolygon(iPolygon); if (kPoly.m_usNumVertices > 0) { const NiColorA* akColor = &m_pkColor[kPoly.m_usVOffset]; int iNumBytes = kPoly.m_usNumVertices * sizeof(NiColorA); NiMemcpy(akValue, akColor, iNumBytes); return true; } } return false; } bool DynamicSimpleMeshData::SetTextures(int iPolygon, unsigned short usSet, const NiPoint2* akValue) { if (IsValid(iPolygon) && akValue) { Polygon& kPoly = GetPolygon(iPolygon); NiPoint2* akTex = &m_pkTexture[kPoly.m_usVOffset]; for (int i = 0; i < (int)kPoly.m_usVOffset; ++i) { akTex[i].x = akValue[i].x; akTex[i].y = akValue[i].y; } MarkAsChanged(TEXTURE_MASK); m_bBoundNeedsUpdate = true; return true; } return false; } bool DynamicSimpleMeshData::SetTextures(int iPolygon, unsigned short usSet, int iVert, const NiPoint2& kValue) { if (IsValid(iPolygon)) { Polygon& kPoly = GetPolygon(iPolygon); NiPoint2* akTex = &m_pkTexture[kPoly.m_usVOffset]; akTex[iVert].x = kValue.x; akTex[iVert].y = kValue.y; MarkAsChanged(TEXTURE_MASK); m_bBoundNeedsUpdate = true; return true; } return false; } int DynamicSimpleMeshData::GetNumVertices(int iPolygon) const { if (IsValid(iPolygon)) return (int)GetPolygon(iPolygon).m_usNumVertices; return 0; } int DynamicSimpleMeshData::GetNumTriangles(int iPolygon) const { if (IsValid(iPolygon)) return (int)GetPolygon(iPolygon).m_usNumTriangles; return 0; } DynamicSimpleMesh::DynamicSimpleMesh() { } DynamicSimpleMesh::DynamicSimpleMesh(DynamicSimpleMeshData* pkModelData) : NiTriShape(pkModelData) { } DynamicSimpleMesh::~DynamicSimpleMesh() { } void DynamicSimpleMesh::Draw(NiRenderer* pkRenderer) { RenderImmediate(pkRenderer); } void DynamicSimpleMesh::RenderImmediate(NiRenderer* pkRenderer) { NiTriShape::RenderImmediate(pkRenderer); } void DynamicSimpleMesh::UpdateWorldBound() { if (GetData()->IsBoundNeedsUpdate()) { GetData()->UpdateBound(); NiGeometry::UpdateWorldBound(); } } int DynamicSimpleMesh::Insert(unsigned short usNumVertices, unsigned short usNumTriangles/*=0*/, const unsigned short* ausTriList/*=0*/) { return GetData()->Insert(usNumVertices, usNumTriangles, ausTriList); } bool DynamicSimpleMesh::SetVertices(int iPolygon, const NiPoint3* akValue) { return GetData()->SetVertices(iPolygon, akValue); } bool DynamicSimpleMesh::GetVertices(int iPolygon, NiPoint3* akValue) const { return GetData()->GetVertices(iPolygon, akValue); } bool DynamicSimpleMesh::SetVertex(int iPolygon, int iVert, const NiPoint3& kValue) { return GetData()->SetVertex(iPolygon, iVert, kValue); } bool DynamicSimpleMesh::Remove(int iPolygon) { return GetData()->Remove(iPolygon); } void DynamicSimpleMesh::RemoveAll() { return GetData()->RemoveAll(); } bool DynamicSimpleMesh::GetVertex(int iPolygon, int iVert, NiPoint3& kValue) const { return GetData()->GetVertex(iPolygon, iVert, kValue); } bool DynamicSimpleMesh::GetColors(int iPolygon, NiColorA* akValue) const { return GetData()->GetColors(iPolygon, akValue); } bool DynamicSimpleMesh::SetColors(int iPolygon, const NiColorA& kCommonValue) { return GetData()->SetColors(iPolygon, kCommonValue); } bool DynamicSimpleMesh::SetColors(int iPolygon, const NiColorA* akValue) { return GetData()->SetColors(iPolygon, akValue); } bool DynamicSimpleMesh::GetColor(int iPolygon, int iVertex, NiColorA& kValue) const { return GetData()->GetColor(iPolygon, iVertex, kValue); } bool DynamicSimpleMesh::SetColor(int iPolygon, int iVertex, const NiColorA& kValue) { return GetData()->SetColor(iPolygon, iVertex, kValue); } bool DynamicSimpleMesh::SetTextures(int iPolygon, unsigned short usSet, int iVert, const NiPoint2& kValue) { return GetData()->SetTextures(iPolygon, usSet, iVert, kValue); } bool DynamicSimpleMesh::SetTextures(int iPolygon, unsigned short usSet, const NiPoint2* akValue) { return GetData()->SetTextures(iPolygon, usSet, akValue); } DynamicSimpleMeshData* DynamicSimpleMesh::GetData() { return NiSmartPointerCast(DynamicSimpleMeshData, m_spModelData); } const DynamicSimpleMeshData* DynamicSimpleMesh::GetData() const { return NiSmartPointerCast(DynamicSimpleMeshData, m_spModelData); }