// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdafx.h" #include "nel_patch_mesh.h" #include "nel/misc/time_nl.h" #include "vertex_neighborhood.h" #include "../nel_3dsmax_shared/nel_3dsmax_shared.h" #include "nel/3d/zone_symmetrisation.h" // For MAX_RELEASE #include using namespace NL3D; using namespace NLMISC; #ifndef max #define max(a,b) (((a) > (b)) ? (a) : (b)) #endif max //#ifndef min //#define min(a,b) (((a) < (b)) ? (a) : (b)) //#endif min #define PBLOCK_REF 0 #ifdef USE_CACHE #ifndef NDEBUG #define DEBUG_PIPELINE #endif // NDEBUG #endif // USE_CACHE float bindWhere[BIND_COUNT]= { 0.25f, 0.75f, 0.5f, 0.5f }; #define RK_APPDATA_TILEFILE 0 #define RK_APPDATA_LAND 1 #define REGKEY_TILEDIT "Software\\Nevrax\\Ryzom\\Tile_Edit" //#define CHECK_VALIDITY // check validity // Default constructor, allocate the array CPatchMeshData::CPatchMeshData () { // Get a global allocator CPatchAllocator& _Allocator=GetAllocator (); _UIPatch=_Allocator.AllocPatch.allocate(); _UIVertex=_Allocator.AllocVertex.allocate(); _MapHitToTileIndex=_Allocator.AllocInt.allocate(); } // Copy constructor, allocate the array CPatchMeshData::CPatchMeshData (const CPatchMeshData& src) { this->CPatchMeshData::CPatchMeshData (); this->operator= (src); } // Destructor CPatchMeshData::~CPatchMeshData () { // Get a global allocator CPatchAllocator& _Allocator=GetAllocator (); _Allocator.AllocPatch.free (_UIPatch); _Allocator.AllocVertex.free (_UIVertex); _Allocator.AllocInt.free (_MapHitToTileIndex); } // Copy CPatchMeshData& CPatchMeshData::operator= (const CPatchMeshData& src) { *_UIPatch=*src._UIPatch; *_UIVertex=*src._UIVertex; *_MapHitToTileIndex=*src._MapHitToTileIndex; return *this; } NL3D::CTileBank bank; std::string GetBankPathName () { HKEY hKey; if (RegOpenKeyEx(HKEY_CURRENT_USER, REGKEY_TILEDIT, 0, KEY_READ, &hKey)==ERROR_SUCCESS) { char path[256]; DWORD len=256; DWORD type; if (RegQueryValueEx(hKey, "Bank Path", 0, &type, (LPBYTE)path, &len)==ERROR_SUCCESS) return std::string (path); RegCloseKey (hKey); } return ""; } int GetBankTileSetSet () { HKEY hKey; if (RegOpenKeyEx(HKEY_CURRENT_USER, REGKEY_TILEDIT, 0, KEY_READ, &hKey)==ERROR_SUCCESS) { int tileSetSet; DWORD len=256; DWORD type; if (RegQueryValueEx(hKey, "Tileset Set", 0, &type, (LPBYTE)&tileSetSet, &len)==ERROR_SUCCESS) return tileSetSet; RegCloseKey (hKey); } return -1; } void SetBankPathName (const std::string& path) { HKEY hKey; if (RegCreateKey(HKEY_CURRENT_USER, REGKEY_TILEDIT, &hKey)==ERROR_SUCCESS) { RegSetValueEx(hKey, "Bank Path", 0, REG_SZ, (LPBYTE)path.c_str(), path.length()+1); RegCloseKey (hKey); } } void SetBankTileSetSet (int tileSetSet) { HKEY hKey; if (RegCreateKey(HKEY_CURRENT_USER, REGKEY_TILEDIT, &hKey)==ERROR_SUCCESS) { RegSetValueEx(hKey, "Tileset Set", 0, REG_DWORD, (LPBYTE)&tileSetSet, 4); RegCloseKey (hKey); } } // RPatchMesh -------------------------------------------------------------------------------------------------------------------------------------------- // Constructeur RPatchMesh::RPatchMesh (PatchMesh *pmesh) { // Invalidate ValidGeom=NEVER; ValidTopo=NEVER; ValidTexmap=NEVER; ValidSelect=NEVER; ValidDisplay=NEVER; ValidBindingInfo=FOREVER; ValidBindingPos=FOREVER; rTess.ModeTile=true; rTess.KeepMapping=false; rTess.TransitionType=1; SetSelLevel (EP_OBJECT); rTess.TileTesselLevel=0; build=-1; paintHack=false; if (pmesh) { // Patches SetNumPatches (pmesh->getNumPatches()); // Vertices SetNumVerts (pmesh->getNumVerts()); } // Getback the binding informations for (int v=0; vhooks.Count(); v++) { int hookPoint=pmesh->hooks[v].hookPoint; int hookEdge=pmesh->hooks[v].hookEdge; int hookPatch=pmesh->hooks[v].hookPatch; AddHook (hookPoint, hookEdge, *pmesh); } } RPatchMesh::RPatchMesh () { // Set level SetSelLevel (EP_OBJECT); build=-1; rTess.TransitionType=1; paintHack=false; // Invalidate ValidGeom=NEVER; ValidTopo=NEVER; ValidTexmap=NEVER; ValidSelect=NEVER; ValidDisplay=NEVER; ValidBindingInfo=FOREVER; ValidBindingPos=FOREVER; rTess.ModeTile=true; rTess.KeepMapping=false; rTess.TransitionType=1; SetSelLevel (EP_OBJECT); rTess.TileTesselLevel=0; build=-1; paintHack=false; }; RPatchMesh::~RPatchMesh () { }; // Check if a vertex is in a seg bool IsVertexInEdge (int nVert, int nSeg, const PatchMesh& patch) { if ((patch.edges[nSeg].v1==nVert)||(patch.edges[nSeg].v2==nVert)) return true; else return false; } // return other vertex in edge int GetOtherVertex (int nVert, int nSeg, const PatchMesh& patch) { if (patch.edges[nSeg].v1==nVert) return patch.edges[nSeg].v2; else { nlassert (patch.edges[nSeg].v2==nVert); return patch.edges[nSeg].v1; } } // Check if two vertex are joined by a edge int IsVerticesJoined (int nVert0, int nVert1, const CVertexNeighborhood& tab, const PatchMesh& patch) { // Count of neigbor for vertex 0 uint listSize=tab.getNeighborCount (nVert0); // List of neigbor const uint* pList=tab.getNeighborList (nVert0); // For each neigbor for (uint n=0; n1)&&bCreate) { if (bAssert) nlassert (0); } else if (patch.edges[nSeg].patches.Count() > 0) #endif // (MAX_RELEASE < 4000) { // config 1? int nSeg0=IsVerticesJoined (v0, nVert, tab, patch); int nSeg1=IsVerticesJoined (v1, nVert, tab, patch); if ((nSeg0!=-1)&&(nSeg1!=-1)) { // additionnal check. #if (MAX_RELEASE < 4000) if ((patch.edges[nSeg0].patch2==-1)&&(patch.edges[nSeg1].patch2==-1)) { if (!IsVertexInPatch (nVert, patch.edges[nSeg].patch1, patch)) config=0; } #else // (MAX_RELEASE < 4000) if ((patch.edges[nSeg0].patches.Count()<2)&&(patch.edges[nSeg1].patches.Count()<2)) { if (!IsVertexInPatch (nVert, patch.edges[nSeg].patches[0], patch)) config=0; } #endif // (MAX_RELEASE < 4000) } if (config==-1) { // Check config 2 // v0 **** v2 **** nVert **** v3 **** v1 // *********************************** int nEdge0; int nEdge1; int nEdge2; int nEdge3; if (IsVerticesJoined2 (v0, nVert, v2, nEdge0, nEdge1, tab, patch)&& IsVerticesJoined2 (v1, nVert, v3, nEdge2, nEdge3, tab, patch)) { // additionnal check if ((v2!=v3)&& (nEdge0!=nEdge1)&& (nEdge0!=nEdge2)&& (nEdge0!=nEdge3)&& (nEdge1!=nEdge2)&& (nEdge1!=nEdge3)&& (nEdge2!=nEdge3)&& #if (MAX_RELEASE < 4000) (patch.edges[nEdge0].patch2==-1)&& (patch.edges[nEdge1].patch2==-1)&& (patch.edges[nEdge2].patch2==-1)&& (patch.edges[nEdge3].patch2==-1)&& (!IsVertexInPatch (nVert, patch.edges[nSeg].patch1, patch))&& (!IsVertexInPatch (v2, patch.edges[nSeg].patch1, patch))&& (!IsVertexInPatch (v3, patch.edges[nSeg].patch1, patch))) #else // (MAX_RELEASE < 4000) (patch.edges[nEdge0].patches.Count()<2)&& (patch.edges[nEdge1].patches.Count()<2)&& (patch.edges[nEdge2].patches.Count()<2)&& (patch.edges[nEdge3].patches.Count()<2)&& (!IsVertexInPatch (nVert, patch.edges[nSeg].patches[0], patch))&& (!IsVertexInPatch (v2, patch.edges[nSeg].patches[0], patch))&& (!IsVertexInPatch (v3, patch.edges[nSeg].patches[0], patch))) #endif // (MAX_RELEASE < 4000) { config=1; } } } if (config==-1) { // No config if (bAssert) nlassert (0); } } } } return config; } // Patchmesh Check validity (debug stuff) bool RPatchMesh::Validity (const PatchMesh& patch, bool bAssert) { #ifdef CHECK_VALIDITY bool bRet=true; // Check patches count int nPatchCount=patch.numPatches; int nPatchUICount=getUIPatchSize(); if (nPatchCount!=nPatchUICount) { if (bAssert) nlassert (0); // Not the same patch count bRet=false; } // Check vertices count int nVertCount=patch.numVerts; int nVertUICount=getUIVertexSize(); if (nVertCount!=nVertUICount) { if (bAssert) nlassert (0); // Not the same vertices count bRet=false; } // Static table to avoid alloc prb CVertexNeighborhood& tab=vertexNeighborhoodGlobal; tab.build (patch); // Check binding info in vertices for (int nVertex=0; nVertex<(int)getUIVertexSize(); nVertex++) { // Binding on this vertex ? if (getUIVertex(nVertex).Binding.bBinded) { // Check # of the patch int nPatchNumber=getUIVertex(nVertex).Binding.nPatch; if ((nPatchNumber>=nPatchUICount)||(nPatchNumber<0)) { if (bAssert) nlassert (0); // # patch invalid in binding info bRet=false; } // Check # of the edge int nEdgeNumber=getUIVertex(nVertex).Binding.nEdge; if ((nEdgeNumber>=4)||(nEdgeNumber<0)) { if (bAssert) nlassert (0); // # edge invalid in binding info bRet=false; } // Check fWhere typeBind type = (typeBind)(getUIVertex(nVertex).Binding.nType); if ((type!=BIND_25)&& (type!=BIND_50)&& (type!=BIND_75)&& (type!=BIND_SINGLE)) { if (bAssert) nlassert (0); // fWhere value invalid in binding info bRet=false; } // Primary vertex int nPrim=getUIVertex(nVertex).Binding.nPrimVert; if ((nPrim<0)||(nPrim>=patch.numVerts)) { if (bAssert) nlassert(0); bRet=false; if (nPrim!=nVertex) { if (!getUIVertex(nPrim).Binding.bBinded) { if (bAssert) nlassert(0); bRet=false; } if (getUIVertex(nPrim).Binding.nEdge!=(uint)nEdgeNumber) { if (bAssert) nlassert(0); bRet=false; } if (getUIVertex(nPrim).Binding.nPatch!=(uint)nPatchNumber) { if (bAssert) nlassert(0); bRet=false; } if ((getUIVertex(nPrim).Binding.nPrimVert==BIND_50)||(getUIVertex(nPrim).Binding.nPrimVert==BIND_SINGLE)) { if (bAssert) nlassert(0); bRet=false; } } } // Check geom int v0, v1, v2, v3; switch (CheckBind (nPrim, patch.patches[nPatchNumber].edge[nEdgeNumber], v0, v1, v2, v3, tab, patch, bAssert, false)) { case 0: if (type!=BIND_SINGLE) { if (bAssert) nlassert(0); bRet=false; } break; case 1: if (nVertex!=nPrim) { if (v2==nVertex) { if (type!=BIND_25) { if (bAssert) nlassert(0); bRet=false; } } else if (v3==nVertex) { if (type!=BIND_75) { if (bAssert) nlassert(0); bRet=false; } } else { if (type!=BIND_50) { if (bAssert) nlassert(0); bRet=false; } } } break; } } } // Check mode tile int nSelTileSize=tileSel.GetSize(); int nTileSize=getUIPatchSize()*NUM_TILE_SEL; if (nSelTileSize!=nTileSize) { if (bAssert) nlassert(0); bRet=false; } // Check count of it index if (rTess.TileTesselLevel>=0) { int nFaceCount=mesh.numFaces; int nHitCount=_Data._MapHitToTileIndex->size(); if (nFaceCount!=nHitCount) { if (bAssert) nlassert (0); bRet=false; } } // Return the bad news... return bRet; #else // CHECK_VALIDITY return true; #endif } static Point3 InterpCenter(Point3 e1, Point3 i1, Point3 i2, Point3 e2, Point3 *v1 = NULL, Point3 *v2 = NULL, Point3 *v3 = NULL, Point3 *v4 = NULL) { Point3 e1i1 =(e1 + i1) / 2.0f; Point3 i1i2 =(i1 + i2) / 2.0f; Point3 i2e2 =(i2 + e2) / 2.0f; Point3 a =(e1i1 + i1i2) / 2.0f; Point3 b =(i1i2 + i2e2) / 2.0f; if (v1) *v1 = e1i1; if (v2) *v2 = a; if (v3) *v3 = b; if (v4) *v4 = i2e2; return (a + b) / 2.0f; } // Update binded vertices's position void RPatchMesh::UpdateBindingPos (PatchMesh& patch) { // Check validity #ifndef NDEBUG Validity (patch, true); #endif // NDEBUG // Vertex count int nVertexCount=patch.getNumVerts (); // Pointer uint vertSize=getUIVertexSize(); for (uint nV=0; nVBinding.bBinded) { static const float fU[4]={0.f, 1.f, 0.f, -1.f}; static const float fV[4]={1.f, 0.f, -1.f, 0.f}; static const float fUadd[4]={0.f, 0.f, 1.f, 1.f}; static const float fVadd[4]={0.f, 1.f, 1.f, 0.f}; // Calcule new position of the vertex int nEdge=patch.patches[pVertex->Binding.nPatch].edge[pVertex->Binding.nEdge]; PatchEdge &edge=patch.edges[nEdge]; // Save Point3 vOld=patch.verts[nV].p; switch (pVertex->Binding.nType) { case BIND_25: { Point3 v0, v1, v2, v3, v4; v2=InterpCenter(patch.verts[edge.v1].p, patch.vecs[edge.vec12].p, patch.vecs[edge.vec21].p, patch.verts[edge.v2].p, &v0, &v1, &v3, &v4); // Final interpolation patch.verts[nV].p=InterpCenter(patch.verts[edge.v1].p, v0, v1, v2, &patch.vecs[pVertex->Binding.nBefore2].p, &patch.vecs[pVertex->Binding.nBefore].p, &patch.vecs[pVertex->Binding.nAfter].p, &patch.vecs[pVertex->Binding.nAfter2].p); } break; case BIND_50: patch.verts[nV].p=InterpCenter(patch.verts[edge.v1].p, patch.vecs[edge.vec12].p, patch.vecs[edge.vec21].p, patch.verts[edge.v2].p, NULL, NULL, NULL, NULL); break; case BIND_75: { Point3 v0, v1, v2, v3, v4; v2=InterpCenter(patch.verts[edge.v1].p, patch.vecs[edge.vec12].p, patch.vecs[edge.vec21].p, patch.verts[edge.v2].p, &v0, &v1, &v3, &v4); // Final interpolation patch.verts[nV].p=InterpCenter(v2, v3, v4, patch.verts[edge.v2].p, &patch.vecs[pVertex->Binding.nBefore2].p, &patch.vecs[pVertex->Binding.nBefore].p, &patch.vecs[pVertex->Binding.nAfter].p, &patch.vecs[pVertex->Binding.nAfter2].p); } break; case BIND_SINGLE: patch.verts[nV].p=InterpCenter(patch.verts[edge.v1].p, patch.vecs[edge.vec12].p, patch.vecs[edge.vec21].p, patch.verts[edge.v2].p, &patch.vecs[pVertex->Binding.nBefore2].p, &patch.vecs[pVertex->Binding.nBefore].p, &patch.vecs[pVertex->Binding.nAfter].p, &patch.vecs[pVertex->Binding.nAfter2].p); break; default: nlassert (0); } patch.vecs[pVertex->Binding.nT].p += patch.verts[nV].p-vOld; } } patch.computeInteriors(); patch.InvalidateGeomCache(); } // Build internal binding info void RPatchMesh::UpdateBindingInfo (PatchMesh& patch) { // Make tab vert to edge list // Static table to avoid alloc prb CVertexNeighborhood& tab=vertexNeighborhoodGlobal; tab.build (patch); for (int n=0; n=0); nlassert (nVertex<(sint)getUIVertexSize()); nlassert (nPrimary>=0); nlassert (nPrimary<(sint)getUIVertexSize()); nlassert (nPatch>=0); nlassert (nPatch<(sint)getUIPatchSize()); nlassert (nEdge>=0); nlassert (nEdge<4); nlassert ((nType==BIND_25)||(nType==BIND_50)||(nType==BIND_75)||(nType==BIND_SINGLE)); // Go... getUIVertex (nVertex).Binding.bBinded=true; getUIVertex (nVertex).Binding.nPatch=nPatch; getUIVertex (nVertex).Binding.nEdge=nEdge; getUIVertex (nVertex).Binding.nType=nType; getUIVertex (nVertex).Binding.nPrimVert=nPrimary; getUIVertex (nVertex).Binding.nBefore=-1; getUIVertex (nVertex).Binding.nAfter=-1; getUIVertex (nVertex).Binding.nT=-1; getUIVertex (nVertex).Binding.nBefore2=-1; getUIVertex (nVertex).Binding.nAfter2=-1; } // Unbind a vertex void RPatchMesh::UnBindingVertex (int nVertex) { getUIVertex (nVertex).Binding.bBinded=false; getUIVertex (nVertex).Binding.nAfter=-1; getUIVertex (nVertex).Binding.nAfter2=-1; getUIVertex (nVertex).Binding.nBefore=-1; getUIVertex (nVertex).Binding.nBefore2=-1; getUIVertex (nVertex).Binding.nT=-1; } // Resize vertex buffer void RPatchMesh::SetNumVerts (int nVert) { int nOldSize=getUIVertexSize(); resizeUIVertex (nVert); for (int n=nOldSize; nInit (nTileU, nTileV, true); int v; for(v=0; vgetTileSize()); int nOldTile=u+(2*nTileCountU*v); nOldTile%=nOldTileCount; nlassert (nOldTile<(int)getUIPatch (nPatch).getTileSize()); pPatch->getTileDesc (nTile)=getUIPatch (nPatch).getTileDesc (nOldTile); } for(v=0; vgetColorSize()); int nOldVertex=u+((2*nTileCountU+1)*v); nOldVertex%=nOldVertexCount; nlassert (nOldVertex<(int)getUIPatch (nPatch).getColorSize()); pPatch->setColor (nVertex, getUIPatch (nPatch).getColor (nOldVertex)); } // Edge info pPatch->getEdge (0)=getUIPatch (nPatch).getEdge (0); pPatch->getEdge (3)=getUIPatch (nPatch).getEdge (3); // * 1 // Patch ptr pPatch++; // Init patch info without intialize tabl pPatch->Init (nTileV, nTileU, true); for(v=0; vgetTileSize()); int nOldTile=u+(2*nTileCountU*(nTileCountV+v)); nOldTile%=nOldTileCount; nlassert (nOldTile<(int)getUIPatch (nPatch).getTileSize()); /*pPatch->Tile[nTile]=getUIPatch (nPatch).Tile[nOldTile]; pPatch->Tile[nTile].Rotate=(getUIPatch (nPatch).Tile[nOldTile].Rotate+3)&3;*/ pPatch->getTileDesc (nTile)=getUIPatch (nPatch).getTileDesc (nOldTile); pPatch->getTileDesc (nTile).rotate (3); } for(v=0; vgetColorSize()); int nOldVertex=u+((2*nTileCountU+1)*(nTileCountV+v)); nOldVertex%=nOldVertexCount; nlassert (nOldVertex<(int)getUIPatch (nPatch).getColorSize()); pPatch->setColor (nVertex, getUIPatch (nPatch).getColor (nOldVertex)); } // Edge info pPatch->getEdge (3)=getUIPatch (nPatch).getEdge (0); pPatch->getEdge (0)=getUIPatch (nPatch).getEdge (1); // * 2 // Patch ptr pPatch++; // Init patch info without intialize tabl pPatch->Init (nTileU, nTileV, true); for(v=0; vgetTileSize()); int nOldTile=nTileCountU+u+(2*nTileCountU*(nTileCountV+v)); nOldTile%=nOldTileCount; nlassert (nOldTile<(int)getUIPatch (nPatch).getTileSize()); pPatch->getTileDesc (nTile)=getUIPatch (nPatch).getTileDesc (nOldTile); //pPatch->Tile[nTile].Rotate=(getUIPatch (nPatch).Tile[nOldTile].Rotate+2)&3; pPatch->getTileDesc (nTile).rotate (2); } for(v=0; vgetColorSize()); int nOldVertex=nTileCountU+u+((2*nTileCountU+1)*(nTileCountV+v)); nOldVertex%=nOldVertexCount; nlassert (nOldVertex<(int)getUIPatch (nPatch).getColorSize()); pPatch->setColor (nVertex, getUIPatch (nPatch).getColor (nOldVertex)); } // Edge info pPatch->getEdge (0)=getUIPatch (nPatch).getEdge (2); pPatch->getEdge (3)=getUIPatch (nPatch).getEdge (1); // * 3 // Patch ptr pPatch++; // Init patch info without intialize tabl pPatch->Init (nTileV, nTileU, true); for(v=0; vgetTileSize()); int nOldTile=nTileCountU+u+(2*nTileCountU*v); nOldTile%=nOldTileCount; nlassert (nOldTile<(int)getUIPatch (nPatch).getTileSize()); pPatch->getTileDesc (nTile)=getUIPatch (nPatch).getTileDesc (nOldTile); //pPatch->Tile[nTile].Rotate=(getUIPatch (nPatch).Tile[nOldTile].Rotate+1)&3; pPatch->getTileDesc (nTile).rotate (1); } for(v=0; vgetColorSize()); int nOldVertex=nTileCountU+u+((2*nTileCountU+1)*v); nOldVertex%=nOldVertexCount; nlassert (nOldVertex<(int)getUIPatch (nPatch).getColorSize()); pPatch->setColor (nVertex, getUIPatch (nPatch).getColor (nOldVertex)); } // Edge info pPatch->getEdge (0)=getUIPatch (nPatch).getEdge (3); pPatch->getEdge (3)=getUIPatch (nPatch).getEdge (2); // Update binding info for (int i=0; i<(int)getUIVertexSize(); i++) { if (getUIVertex (i).Binding.bBinded) { if ((int)getUIVertex (i).Binding.nPatch==nPatch) { UnBindingVertex (i); } } } UnbindRelatedPatch (nPatch, patch); InvalidateBindingInfo (); } // Subdivide edge 1 and 3 void RPatchMesh::SubdivideU (int nPatch, int nV0, int nV1, int nFirstPatch, PatchMesh& patch) { // Subdivide patch int nOldTileCount=(1<Init (nTileU, nTileV, true); // Copy info int nTileCountU=1<getTileSize()); int nOldTile=v+nTileCountV*nU+(nTileCountU-1-u)*nTileCountV*2; nOldTile%=nOldTileCount; nlassert (nOldTile<(int)getUIPatch (nPatch).getTileSize()); pPatch->getTileDesc (nTile)=getUIPatch (nPatch).getTileDesc (nOldTile); //pPatch->Tile[nTile].Rotate=(getUIPatch (nPatch).Tile[nOldTile].Rotate+3)&3; pPatch->getTileDesc (nTile).rotate (3); } for(v=0; vgetColorSize()); int nOldVertex=v+nTileCountV*nU+(nTileCountU-u)*(nTileCountV*2+1); nOldVertex%=nOldVertexCount; nlassert (nOldVertex<(int)getUIPatch (nPatch).getColorSize ()); pPatch->setColor (nVertex, getUIPatch (nPatch).getColor (nOldVertex)); } // Edge info if (nNewPatch==nFirstPatch) { pPatch->getEdge (3)=getUIPatch (nPatch).getEdge (0); pPatch->getEdge (0)=getUIPatch (nPatch).getEdge (1); pPatch->getEdge (2)=getUIPatch (nPatch).getEdge (3); } else { pPatch->getEdge (0)=getUIPatch (nPatch).getEdge (1); pPatch->getEdge (1)=getUIPatch (nPatch).getEdge (2); pPatch->getEdge (2)=getUIPatch (nPatch).getEdge (3); } } // Update binding info for (int i=0; i<(int)getUIVertexSize(); i++) { if ((getUIVertex (i).Binding.bBinded)&&((int)getUIVertex (i).Binding.nPatch==nPatch)) { if (getUIVertex (i).Binding.nEdge&1) { UnBindingVertex (i); } else { if (getUIVertex (i).Binding.nEdge==0) { getUIVertex (i).Binding.nPatch=nFirstPatch; getUIVertex (i).Binding.nEdge=3; } else { nlassert (getUIVertex (i).Binding.nEdge==2); getUIVertex (i).Binding.nPatch=nFirstPatch+1; getUIVertex (i).Binding.nEdge=1; } } } } UnbindRelatedPatch (nPatch, patch); InvalidateBindingInfo (); } // Subdivide edge 0 and 2 void RPatchMesh::SubdivideV (int nPatch, int nV0, int nV1, int nFirstPatch, PatchMesh& patch) { // Subdivide patch int nOldTileCount=(1<Init (nTileU, nTileV, true); // Copy info int nTileCountU=1<getTileSize()); int nOldTile=u+(nTileCountU*(nV*nTileCountV+v)); nOldTile%=nOldTileCount; nlassert (nOldTile<(int)getUIPatch (nPatch).getTileSize()); pPatch->getTileDesc (nTile)=getUIPatch (nPatch).getTileDesc (nOldTile); //pPatch->Tile[nTile].Rotate=getUIPatch (nPatch).Tile[nOldTile].Rotate; } for(v=0; vgetColorSize()); int nOldVertex=u+((nTileCountU+1)*(nV*nTileCountV+v)); nOldVertex%=nOldVertexCount; nlassert (nOldVertex<(int)getUIPatch (nPatch).getColorSize()); pPatch->setColor (nVertex, getUIPatch (nPatch).getColor (nOldVertex)); } // Edge info if (nNewPatch==nFirstPatch) { pPatch->getEdge (0)=getUIPatch (nPatch).getEdge (0); pPatch->getEdge (2)=getUIPatch (nPatch).getEdge (2); pPatch->getEdge (3)=getUIPatch (nPatch).getEdge (3); } else { pPatch->getEdge (0)=getUIPatch (nPatch).getEdge (0); pPatch->getEdge (1)=getUIPatch (nPatch).getEdge (1); pPatch->getEdge (2)=getUIPatch (nPatch).getEdge (2); } } // Update binding info for (int i=0; i<(int)getUIVertexSize (); i++) { if ((getUIVertex (i).Binding.bBinded)&&((int)getUIVertex (i).Binding.nPatch==nPatch)) { if ((getUIVertex (i).Binding.nEdge&1)==0) { UnBindingVertex (i); } else { if (getUIVertex (i).Binding.nEdge==1) { getUIVertex (i).Binding.nPatch=nFirstPatch+1; getUIVertex (i).Binding.nEdge=1; } else { nlassert (getUIVertex (i).Binding.nEdge==3); getUIVertex (i).Binding.nPatch=nFirstPatch; getUIVertex (i).Binding.nEdge=3; } } } } UnbindRelatedPatch (nPatch, patch); InvalidateBindingInfo (); } // return on which edge is int WhereIsTheEdge (int nPatch, int nEdge, const PatchMesh& patch) { for (int i=0; i<4; i++) { if ((patch.patches[nPatch].edge[i])==nEdge) return i; } nlassert (0); return -1; } // AddHook void RPatchMesh::AddHook (int nVert, int nSeg, PatchMesh& patch) { #if (MAX_RELEASE < 4000) // Une side of the edge must be cleared nlassert (patch.edges[nSeg].patch2==-1); int patch1 = patch.edges[nSeg].patch1; #else // (MAX_RELEASE < 4000) // Une side of the edge must be cleared nlassert (patch.edges[nSeg].patches.Count()<2); int patch1 = patch.edges[nSeg].patches.Count()>0?patch.edges[nSeg].patches[0]:-1; #endif // (MAX_RELEASE < 4000) int nEdge=WhereIsTheEdge(patch1, nSeg, patch); nlassert(patch.patches[patch1].edge[nEdge]==nSeg); BindingVertex(nVert, patch1, nEdge, nVert, BIND_SINGLE); InvalidateBindingInfo (); } // AddHook void RPatchMesh::AddHook (int nVert0, int nVert1, int nVert2, int nSeg, PatchMesh& patch) { #if (MAX_RELEASE < 4000) // Une side of the edge must be cleared nlassert (patch.edges[nSeg].patch2==-1); int patch1 = patch.edges[nSeg].patch1; #else // (MAX_RELEASE < 4000) // Une side of the edge must be cleared nlassert (patch.edges[nSeg].patches.Count()<2); int patch1 = patch.edges[nSeg].patches.Count()>0?patch.edges[nSeg].patches[0]:-1; #endif // (MAX_RELEASE < 4000) int nEdge = WhereIsTheEdge(patch1, nSeg, patch); BindingVertex(nVert0, patch1, nEdge, nVert1, BIND_25); BindingVertex(nVert1, patch1, nEdge, nVert1, BIND_50); BindingVertex(nVert2, patch1, nEdge, nVert1, BIND_75); InvalidateBindingInfo (); } // RemoveHook void RPatchMesh::RemoveHook (PatchMesh& patch) { for (int i=0; i binding safe void RPatchMesh::Attach(RPatchMesh *rattPatch, PatchMesh& patch) { // Add to the end. // Resize buffers int nOldVertCount=getUIVertexSize(); int nOldPolyCount=getUIPatchSize(); int nNewVertCount=nOldVertCount+rattPatch->getUIVertexSize(); int nNewPolyCount=nOldPolyCount+rattPatch->getUIPatchSize(); SetNumVerts (nNewVertCount); SetNumPatches (nNewPolyCount); // Add vert and patch, binding safe for (int nV=0; nVgetUIVertex (nV); // Bind patch if (getUIVertex (nOldVertCount+nV).Binding.bBinded) { getUIVertex (nOldVertCount+nV).Binding.nPatch+=nOldPolyCount; getUIVertex (nOldVertCount+nV).Binding.nPrimVert+=nOldVertCount; } } for (int nP=0; nPgetUIPatch (nP); InvalidateBindingInfo (); } int GetAdjacent (int nMe, int nedge, PatchMesh *pMesh) { int nEdge=pMesh->patches[nMe].edge[nedge]; #if (MAX_RELEASE < 4000) if (pMesh->edges[nEdge].patch1==nMe) return pMesh->edges[nEdge].patch2; else { nlassert (pMesh->edges[nEdge].patch2==nMe); return pMesh->edges[nEdge].patch1; } #else // (MAX_RELEASE < 4000) if ((pMesh->edges[nEdge].patches.Count()>0?pMesh->edges[nEdge].patches[0]:-1)==nMe) return (pMesh->edges[nEdge].patches.Count()>1?pMesh->edges[nEdge].patches[1]:-1); else { nlassert ((pMesh->edges[nEdge].patches.Count()>1?pMesh->edges[nEdge].patches[1]:-1)==nMe); return (pMesh->edges[nEdge].patches.Count()>0?pMesh->edges[nEdge].patches[0]:-1); } #endif // (MAX_RELEASE < 4000) } // return on which edge is int WhereInMyAdjacent (int nMe, int nAdjacent, PatchMesh *pMesh) { for (int i=0; i<4; i++) { if (GetAdjacent (nAdjacent, i, pMesh)==nMe) return i; } return -1; } // Extrude void RPatchMesh::CreateExtrusion (PatchMesh *patch) { // Resize buffer int nOldVertCount=getUIVertexSize(); int nOldPolyCount=getUIPatchSize(); int nNewVertCount=patch->getNumVerts (); int nNewPolyCount=patch->getNumPatches (); SetNumVerts (nNewVertCount); SetNumPatches (nNewPolyCount); patch->buildLinkages(); // update bind data for (int i=0; ipatchSel[i]) { /*int nNewFaces[4]; int nWhichEdge[4]; int nEdgeCount=(patch->patches[i].type==PATCH_QUAD)?4:3; // For all edges for (int nEdge=0; nEdgepatches[i].edge[nEdge]; // Find the new face FindPatch (patch, nE, nWhichEdge[nEdge], nNewFaces[nEdge], nOldPolyCount); } // Look for binded point... for (int nV=0; nV=nOldPolyCount)) nAdj=GetAdjacent (nP, 2, patch); if ((nAdj!=-1)&&(nAdj=nOldPolyCount)) nAdj=GetAdjacent (nP, 3, patch); if ((nAdj!=-1)&&(nAdjnumPatches; nn++) { int nv; for (nv=0; nv<4; nv++) { if (patch->patches[nn].edge[nv]==nEdge) { nPatch=nn; WhichEdge=nv; break; } } if (nv!=4) break; } // Not found ? if (nn==patch->numPatches) { nPatch=-1; WhichEdge=-1; } } // Resolve topologie changes -> Bind safe void RPatchMesh::ResolveTopoChanges(PatchMesh *patch, bool aux1) { // Nombre de patches if (patch->numPatches>(int)getUIPatchSize()) SetNumPatches (patch->numPatches); if (patch->numVerts>(int)getUIVertexSize()) SetNumVerts (patch->numVerts); // static array to avoid alloc prob static std::vector cUIPatch; static std::vector cUIVertex; static std::vector pnRemapPatch; static std::vector pnRemapVertex; // Reserve static array cUIPatch.reserve (300); cUIVertex.reserve (300); pnRemapPatch.reserve (300); pnRemapVertex.reserve (300); // Resize arrays pnRemapPatch.resize (0); pnRemapPatch.resize (getUIPatchSize(), -1); pnRemapVertex.resize (0); pnRemapVertex.resize (getUIVertexSize(), -1); // Fill the patch array uint size=getUIPatchSize(); cUIPatch.resize (size); uint n; for (n=0; nnumVerts; ++i) { int nTag; if (aux1) nTag=patch->verts[i].aux1; else nTag=patch->verts[i].aux2; if (nTag>=0) { pnRemapVertex[nTag]=i; } } // Build remap poly info for (i = 0; i < patch->numPatches; ++i) { int nTag; if (aux1) nTag=patch->patches[i].aux1; else nTag=patch->patches[i].aux2; if (nTag>=0) { pnRemapPatch[nTag]=i; } } // Unbind vertex from deleted vertex for (i=0; i<(int)pnRemapVertex.size(); i++) { if (pnRemapVertex[i]==-1) { UnbindRelatedVertex (i, *patch); } } // Unbind vertex from deleted poly for (i=0; i<(int)pnRemapPatch.size(); i++) { if (pnRemapPatch[i]==-1) { UnbindRelatedPatch (i, *patch); } } // Remap poly info for (i = 0; i < patch->numPatches; ++i) { int nTag; if (aux1) nTag=patch->patches[i].aux1; else nTag=patch->patches[i].aux2; if (nTag>=0) { if (nTag!=i) getUIPatch (i)=cUIPatch[nTag]; } } // Remap vertex info for (i = 0; i < patch->numVerts; ++i) { int nTag; if (aux1) nTag=patch->verts[i].aux1; else nTag=patch->verts[i].aux2; if ((nTag>=0)&&(nTag!=i)) { getUIVertex (i)=cUIVertex[nTag]; } if (getUIVertex (i).Binding.bBinded) { int nPatchTag; nPatchTag=pnRemapPatch[cUIVertex[nTag].Binding.nPatch]; int nVertexTag=pnRemapVertex[cUIVertex[nTag].Binding.nPrimVert]; if ((nPatchTag==-1)||(nVertexTag==-1)) UnBindingVertex (i); else { getUIVertex (i).Binding.nPrimVert=nVertexTag; getUIVertex (i).Binding.nPatch=nPatchTag; } } } // Resize buffers SetNumPatches (patch->numPatches); SetNumVerts (patch->numVerts); // Invalidate binding infos InvalidateBindingInfo (); } // Weld -> Bind safe void RPatchMesh::Weld (PatchMesh *patch) { ResolveTopoChanges(patch, false); } int GetEdgeNumberInPatch (int nPatch, int nEdge, PatchMesh *patch) { for (int i=0; i<4; i++) { if (patch->patches[nPatch].edge[i]==nEdge) return i; } return -1; } // Add a patch void RPatchMesh::AddPatch (int nEdge, int nFirstPatch, PatchMesh *patch) { // Add a patch SetNumPatches (getUIPatchSize()+1); // Find the tile resolution... int nV=RPO_DEFAULT_TESSEL; int nedge=GetEdgeNumberInPatch (nFirstPatch, nEdge, patch); nlassert (nedge!=-1); if (nedge&1) nV=getUIPatch (nFirstPatch).NbTilesU; else nV=getUIPatch (nFirstPatch).NbTilesV; getUIPatch (getUIPatchSize()-1).Init (nV, nV); } // Unbind vertex associed to the vertex void RPatchMesh::UnbindRelatedVertex (int nVertex, PatchMesh& patch) { uint nFace=getUIVertex (nVertex).Binding.nPatch; uint nEdge=getUIVertex (nVertex).Binding.nEdge; for (int j=0; j<(int)getUIVertexSize(); j++) { if ((getUIVertex (j).Binding.bBinded)&& (getUIVertex (j).Binding.nPatch==nFace)&& (getUIVertex (j).Binding.nEdge==nEdge)) { UnBindingVertex (j); } } } // Unbind vertex associed to the patch void RPatchMesh::UnbindRelatedPatch (int nPatch, PatchMesh& patch) { for (int i=0; i<4; i++) { int n=patch.patches[nPatch].v[i]; if (getUIVertex (n).Binding.bBinded) { UnbindRelatedVertex (n, patch); } } } // Delete patches and vertices void RPatchMesh::DeleteAndSweep (const BitArray &remapVerts, const BitArray &remapPatches, PatchMesh& patch) { int mapVSize=remapVerts.GetSize(); int vSize=getUIVertexSize(); int mapPSize=remapPatches.GetSize(); int pSize=getUIPatchSize(); nlassert (mapVSize==vSize); nlassert (mapPSize==pSize); int i,j; // Unbind vertex from deleted vertex for (i=0; i<(int)getUIVertexSize(); i++) { if (remapVerts[i]) { UnbindRelatedVertex (i, patch); } } // Unbind vertex from deleted poly for (i=0; i<(int)getUIPatchSize(); i++) { if (remapPatches[i]) { UnbindRelatedPatch (i, patch); } } // Make vertex remap table static std::vector newVIndex; newVIndex.reserve (1000); newVIndex.resize (vSize); for (i=0, j=0; i newPIndex; newPIndex.reserve (1000); newPIndex.resize (pSize); for (i=0, j=0; iRead(&nVersion, sizeof (nVersion), &nb); switch (nVersion) { case RPATCHMESH_SERIALIZE_VERSION_9: case RPATCHMESH_SERIALIZE_VERSION_8: case RPATCHMESH_SERIALIZE_VERSION_7: case RPATCHMESH_SERIALIZE_VERSION_6: case RPATCHMESH_SERIALIZE_VERSION_5: case RPATCHMESH_SERIALIZE_VERSION_4: case RPATCHMESH_SERIALIZE_VERSION_3: case RPATCHMESH_SERIALIZE_VERSION_2: case RPATCHMESH_SERIALIZE_VERSION_1: { // Patch info int nSize; iload->Read(&nSize, sizeof (nSize), &nb); SetNumPatches (nSize); int i; for (i=0; iRead(&getUIPatch (i).NbTilesU, sizeof (int), &nb); iload->Read(&getUIPatch (i).NbTilesV, sizeof (int), &nb); if (nVersionRead(&nSize2, sizeof (int), &nb); for (int j=0; jRead(&old, sizeof (int), &nb); iload->Read(&old, sizeof (int), &nb); getUIPatch (i).getTileDesc (j).setEmpty (); } } else // RPATCHMESH_SERIALIZE_VERSION_3 { // Tiles int nSize2; iload->Read(&nSize2, sizeof (int), &nb); for (int j=0; jRead(&getUIPatch (i).getTileDesc (j)._Num, sizeof (USHORT), &nb); // Version 5, number of cell for 256x256 tiles if (nVersion>=RPATCHMESH_SERIALIZE_VERSION_5) iload->Read(&getUIPatch (i).getTileDesc (j)._Flags, sizeof (USHORT), &nb); else getUIPatch (i).getTileDesc (j)._Flags=0; // Clear displace flags in version lower than 8 if (nVersionRead(&noise, sizeof (uint8), &nb); getUIPatch (i).getTileDesc (j).setDisplace (noise); } for (int k=0; k<3; k++) { bool invert; int tile; int rotate; iload->Read(&invert, sizeof (bool), &nb); iload->Read(&tile, sizeof (int), &nb); iload->Read(&rotate, sizeof (int), &nb); // Not used anymore getUIPatch (i).getTileDesc (j)._MatIDTab[k].Invert=invert; getUIPatch (i).getTileDesc (j)._MatIDTab[k].Tile=tile; getUIPatch (i).getTileDesc (j)._MatIDTab[k].Rotate=rotate; } } } // Colors int nSize2; iload->Read(&nSize2, sizeof (int), &nb); for (int j=0; jRead(&color, sizeof (uint), &nb); getUIPatch (i).setColor (j, color); // Before RPATCHMESH_SERIALIZE_VERSION_6, force color to white if (nVersion=RPATCHMESH_SERIALIZE_VERSION_7) { // Save the 4 edges for (int e=0; e<4; e++) { // Get an edge ref CEdgeInfo& edge=getUIPatch (i).getEdge (e); // Read it iload->Read(&edge.Flags, sizeof (uint32), &nb); } } } // Vertex info iload->Read(&nSize, sizeof (nSize), &nb); SetNumVerts (nSize); for (i=0; iRead(&bBinded, sizeof (bool), &nb); iload->Read(&nType, sizeof (typeBind), &nb); iload->Read(&nEdge, sizeof (uint), &nb); iload->Read(&nPatch, sizeof (uint), &nb); iload->Read(&nBefore, sizeof (uint), &nb); iload->Read(&nBefore2, sizeof (uint), &nb); iload->Read(&nAfter, sizeof (uint), &nb); iload->Read(&nAfter2, sizeof (uint), &nb); iload->Read(&nT, sizeof (uint), &nb); iload->Read(&nType, sizeof (typeBind), &nb); iload->Read(&nPrimVert, sizeof (uint), &nb); getUIVertex (i).Binding.bBinded=bBinded; getUIVertex (i).Binding.nType=nType; getUIVertex (i).Binding.nEdge=nEdge; getUIVertex (i).Binding.nPatch=nPatch; getUIVertex (i).Binding.nBefore=nBefore; getUIVertex (i).Binding.nBefore2=nBefore2; getUIVertex (i).Binding.nAfter=nAfter; getUIVertex (i).Binding.nAfter2=nAfter2; getUIVertex (i).Binding.nT=nT; getUIVertex (i).Binding.nType=nType; getUIVertex (i).Binding.nPrimVert=nPrimVert; } // Some info iload->Read(&rTess.TileTesselLevel, sizeof (rTess.TileTesselLevel), &nb); iload->Read(&rTess.ModeTile, sizeof (rTess.ModeTile), &nb); iload->Read(&rTess.KeepMapping, sizeof (rTess.KeepMapping), &nb); if (nVersion==RPATCHMESH_SERIALIZE_VERSION_4) iload->Read(&rTess.TransitionType, sizeof (rTess.TransitionType), &nb); iload->Read(&selLevel, sizeof (selLevel), &nb); } break; default: return IO_ERROR; } ValidGeom.SetEmpty(); ValidTopo.SetEmpty(); ValidTexmap.SetEmpty(); ValidSelect.SetEmpty(); ValidDisplay.SetEmpty(); ValidBindingInfo.SetEmpty(); ValidBindingPos.SetEmpty(); return IO_OK; } // Save IOResult RPatchMesh::Save(ISave *isave) { ULONG nb; // Version unsigned int nVersion=RPATCHMESH_SERIALIZE_VERSION; isave->Write(&nVersion, sizeof (nVersion), &nb); // Patch info int nSize=getUIPatchSize(); isave->Write(&nSize, sizeof (nSize), &nb); int i; for (int i=0; iWrite(&getUIPatch (i).NbTilesU, sizeof (int), &nb); isave->Write(&getUIPatch (i).NbTilesV, sizeof (int), &nb); // Tiles int nSize2=getUIPatch (i).getTileSize(); isave->Write(&nSize2, sizeof (int), &nb); int j; for (j=0; jWrite(&num, sizeof (USHORT), &nb); num=getUIPatch (i).getTileDesc (j)._Flags; isave->Write(&num, sizeof (USHORT), &nb); // Save noise uint8 noise=getUIPatch (i).getTileDesc (j).getDisplace (); isave->Write(&noise, sizeof (uint8), &nb); for (int k=0; k<3; k++) { bool invert = false; int tile; int rotate; // Not used anymore invert=(getUIPatch (i).getTileDesc (j)._MatIDTab[k].Invert!=0); tile=getUIPatch (i).getTileDesc (j)._MatIDTab[k].Tile; rotate=getUIPatch (i).getTileDesc (j)._MatIDTab[k].Rotate; isave->Write(&invert, sizeof (bool), &nb); isave->Write(&tile, sizeof (int), &nb); isave->Write(&rotate, sizeof (int), &nb); } } // Colors nSize2=getUIPatch (i).getColorSize(); isave->Write(&nSize2, sizeof (int), &nb); for (j=0; jWrite(&color, sizeof (int), &nb); } // Edges info (nVersion >= RPATCHMESH_SERIALIZE_VERSION_7) // Save the 4 edges for (int e=0; e<4; e++) { // Get an edge ref const CEdgeInfo& edge=getUIPatch (i).getEdge (e); // Write it isave->Write(&edge.Flags, sizeof (uint32), &nb); } } // Vertex info nSize=getUIVertexSize(); isave->Write(&nSize, sizeof (nSize), &nb); for (i=0; iWrite(&bBinded, sizeof (bool), &nb); isave->Write(&nType, sizeof (typeBind), &nb); isave->Write(&nEdge, sizeof (uint), &nb); isave->Write(&nPatch, sizeof (uint), &nb); isave->Write(&nBefore, sizeof (uint), &nb); isave->Write(&nBefore2, sizeof (uint), &nb); isave->Write(&nAfter, sizeof (uint), &nb); isave->Write(&nAfter2, sizeof (uint), &nb); isave->Write(&nT, sizeof (uint), &nb); isave->Write(&nType, sizeof (typeBind), &nb); isave->Write(&nPrimVert, sizeof (uint), &nb); } // Some info isave->Write(&rTess.TileTesselLevel, sizeof (rTess.TileTesselLevel), &nb); isave->Write(&rTess.ModeTile, sizeof (rTess.ModeTile), &nb); isave->Write(&rTess.KeepMapping, sizeof (rTess.KeepMapping), &nb); isave->Write(&rTess.TransitionType, sizeof (rTess.TransitionType), &nb); isave->Write(&selLevel, sizeof (selLevel), &nb); return IO_OK; }; // *** Tile Methods // Get the matrix of the selected tiles Matrix3 RPatchMesh::GetSelTileTm(PatchMesh& patch, TimeValue t, INode *node, bool& bHasSel) const { Interval valid; Box3 box; Matrix3 otm = node->GetObjectTM(t, &valid); Matrix3 tm = node->GetNodeTM(t, &valid); bHasSel = false; // For all patchs for (int nPatch=0; nPatch<(int)getUIPatchSize(); nPatch++) { float fV=0; int nBU=1<GetObjectTM(t, &valid); bHasSel=false; // For all patchs for (int nPatch=0; nPatch<(int)getUIPatchSize(); nPatch++) { float fV=0; int nBU=1<=0) { nlassert (mesh.numFaces==(int)getMapHitSize ()); for (int nf=0; nfsetRndLimits(gw->getRndLimits() & ~GW_BACKCULL); bRet=mesh.SubObjectHitTest (gw, ma, hr, nFlags, list); MeshSubHitRec *rec=list.First(); while (rec) { if (flags&SUBHIT_PATCH_SELONLY) { int otot=0; } if (flags&SUBHIT_PATCH_UNSELONLY) { int otot=0; } // Check the hit nlassert (rec->index>=0); nlassert (rec->index<(int)getMapHitSize ()); nlassert (rec->indexindex); // Add a patch hit hitList.AddHit (rec->dist, &patch, nRemapedIndex, PATCH_HIT_TILE); // Next hit rec=rec->Next(); } } return bRet; } // Return the patch adjacent at nPatch in the edge nEdge. If no patch adjacent, return nPatch int GetAdjacentPatch (int nPatch, int nEdge, PatchMesh *patch) { PatchEdge *pEdge=patch->edges+patch->patches[nPatch].edge[nEdge]; #if (MAX_RELEASE < 4000) nlassert ((pEdge->patch1==nPatch)||(pEdge->patch2==nPatch)); if (pEdge->patch1==nPatch) return (pEdge->patch2!=-1)?pEdge->patch2:nPatch; else return (pEdge->patch1!=-1)?pEdge->patch1:nPatch; #else // (MAX_RELEASE < 4000) nlassert ((pEdge->patches.Count()>0&&pEdge->patches[0]==nPatch)||(pEdge->patches.Count()>1&&pEdge->patches[1]==nPatch)); if (pEdge->patches[0]==nPatch) return (pEdge->patches.Count()>1&&pEdge->patches[1]!=-1)?pEdge->patches[1]:nPatch; else return (pEdge->patches.Count()>0&&pEdge->patches[0]!=-1)?pEdge->patches[0]:nPatch; #endif // (MAX_RELEASE < 4000) } // Build the mesh void RPatchMesh::BuildMesh(TimeValue t, PatchMesh& patch, Mesh *pMesh) { // Check validity Point3 point; int x,y; // Mesh pointer if (!pMesh) pMesh=&mesh; // Chech binding info are valid UpdateBinding (patch, t); nlassert (ValidBindingPos.InInterval (t)); nlassert (ValidBindingInfo.InInterval (t)); ChannelMask Build=0; if (!ValidTopo.InInterval (t)) { Build|=PART_TOPO; } if (!ValidGeom.InInterval (t)) { Build|=PART_GEOM; } if (!ValidTexmap.InInterval (t)) { Build|=PART_TEXMAP; } if (!ValidSelect.InInterval (t)) { Build|=PART_SELECT|PART_TOPO|PART_GEOM; } if (!ValidDisplay.InInterval (t)) { Build|=PART_DISPLAY; } if (!Build) return; TTicks ticks=CTime::getPerformanceTime (); #ifndef NDEBUG Validity (patch, true); #endif // NDEBUG // Build tracking static int nBuildNumber=0; build=nBuildNumber++; // Textured mode ? bool bTextured=false; if (rTess.TileTesselLevel>=0) // ok, there is at least two tri for a tile, so we can texture... bTextured=true; // Count channels int nChannelCount=0; BitArray pChannelBit (MAX_MESHMAPS); pChannelBit.ClearAll (); int nChannel; for (nChannel=0; nChannel0)||((!rTess.KeepMapping)&&(nChannel==1))) { pChannelBit.Set (nChannel); nChannelCount=nChannel+1; } } // Show interior ? bool bShowInter=(patch.GetShowInterior()!=0); if (Build&(PART_TOPO|PART_GEOM)) { int nbdivsU,nbdivsV; int nf=0; int finalVertexCount=0; for(int p=0 ; psetNumVerts(finalVertexCount,FALSE,TRUE); // Rebuild bind info //UpdateBindingInfo (patch); } if (Build|=PART_TOPO) { // Num of patch pMesh->setNumFaces(nf,FALSE,TRUE); if (bTextured) resizeMapHit (nf); // For each channel for (nChannel=0; nChannelsetMapSupport(nChannel, TRUE); if (rTess.KeepMapping||!bTextured||(nChannel!=1)) { // Number of vertex pMesh->setNumMapVerts (nChannel, finalVertexCount); } else { // Number of vertex pMesh->setNumMapVerts (nChannel, (nf/2)*4); } // Num of patch pMesh->setNumMapFaces(nChannel, nf); } } } } if (Build&(PART_GEOM|PART_GEOM|PART_SELECT|PART_TEXMAP)) { // First vertex of the two tri... int offset=0; // Next face index int f=0; // Next vertex index int nv=0; // Next mapping vertex index int mp=0; // First Edge int ne=0; // Clear the edges if (Build&PART_SELECT) pMesh->edgeSel.ClearAll (); // Tile level int nTileLevel=rTess.TileTesselLevel; // Count of vertex by tile int nVertexTileCount=(1<setVert(nv, point); nv++; } } } // Vertices selection if (Build&PART_SELECT) { pMesh->vertSel.Set ( offset, patch.vertSel[patch.patches[p].v[0]]); pMesh->vertSel.Set ( offset+nbdivsV*(nbdivsU+1), patch.vertSel[patch.patches[p].v[1]]); pMesh->vertSel.Set ( offset+nbdivsV*(nbdivsU+1)+nbdivsU, patch.vertSel[patch.patches[p].v[2]]); pMesh->vertSel.Set ( offset+nbdivsU, patch.vertSel[patch.patches[p].v[3]]); } } if (Build&(PART_TOPO|PART_SELECT|PART_TEXMAP)) { if (Build&PART_SELECT) { // Hide all verts int nLastVert=offset+(nbdivsV+1)*(nbdivsU+1); for (int tt=offset; ttvertHide.Set (tt); } // Show some of them pMesh->vertHide.Clear (offset); pMesh->vertHide.Clear (offset+nbdivsU); pMesh->vertHide.Clear (nLastVert-1-nbdivsU); pMesh->vertHide.Clear (nLastVert-1); // Edge selection int ne=f*3; if (GetSelLevel()==EP_PATCH) { int bPatchSel=patch.patchSel[p]; int nUV=nbdivsU*nbdivsV; if (bPatchSel||patch.patchSel[GetAdjacentPatch (p, 3, &patch)]) { // Top for (int ee=0; eeedgeSel.Set (ne+6*ee+2); } if (bPatchSel||patch.patchSel[GetAdjacentPatch (p, 1, &patch)]) { // Bottom for (int ee=nUV-nbdivsU; eeedgeSel.Set (ne+6*ee+3); } if (bPatchSel||patch.patchSel[GetAdjacentPatch (p, 0, &patch)]) { // Left for (int ee=0; eeedgeSel.Set (ne+6*nbdivsU*ee); } if (bPatchSel||patch.patchSel[GetAdjacentPatch (p, 2, &patch)]) { // Right for (int ee=0; eeedgeSel.Set (ne+6*nbdivsU*ee+4+6*(nbdivsU-1)); } } else if ((GetSelLevel()==EP_EDGE)||paint) { // Top if (patch.edgeSel[patch.patches[p].edge[3]]) { for (int ee=0; eeedgeSel.Set(ne+6*ee+2); } // Bottom if (patch.edgeSel[patch.patches[p].edge[1]]) { int nUV=nbdivsU*nbdivsV; for (int ee=nUV-nbdivsU; eeedgeSel.Set(ne+6*ee+3); } // Left if (patch.edgeSel[patch.patches[p].edge[0]]) { for (int ee=0; eeedgeSel.Set(ne+6*nbdivsU*ee); } // Right if (patch.edgeSel[patch.patches[p].edge[2]]) { for (int ee=0; eeedgeSel.Set(ne+6*nbdivsU*ee+4+6*(nbdivsU-1)); } } } // Topology has changed int mpcopy=mp; int offsetcopy=offset; for(y=0 ; yfaces[f].v[0]=offset+x; pMesh->faces[f].v[1]=offset+x+(nbdivsU+1); pMesh->faces[f].v[2]=offset+x+1; pMesh->faces[f].flags&=~(EDGE_ALL); if (bShowInter) pMesh->faces[f].flags|=EDGE_A|EDGE_C; if (x==0) pMesh->faces[f].flags|=EDGE_A; if (y==0) pMesh->faces[f].flags|=EDGE_C; pMesh->faces[f].smGroup=1; pMesh->faces[f+1].v[0]=offset+x+(nbdivsU+1); pMesh->faces[f+1].v[1]=offset+x+(nbdivsU+1)+1; pMesh->faces[f+1].v[2]=offset+x+1; pMesh->faces[f+1].flags&=~(EDGE_ALL); if (bShowInter) pMesh->faces[f+1].flags|=EDGE_A|EDGE_B; if (x==nbdivsU-1) pMesh->faces[f+1].flags|=EDGE_B; if (y==nbdivsV-1) pMesh->faces[f+1].flags|=EDGE_A; pMesh->faces[f+1].smGroup=1; // MatId /*pMesh->faces[f].setMatID((abs(f>>1)%3)); pMesh->faces[f+1].setMatID((abs(f>>1)%3));*/ // Tile number // Face display if (patch.patches[p].flags&PATCH_HIDDEN) { pMesh->faces[f].flags|=FACE_HIDDEN; pMesh->faces[f+1].flags|=FACE_HIDDEN; } else { pMesh->faces[f].flags&=~(FACE_HIDDEN); pMesh->faces[f+1].flags&=~(FACE_HIDDEN); } // Texture maps int nTileU=x>>nTileLevel; int nTileV=y>>nTileLevel; for (nChannel=0; nChannelmapFaces(nChannel); nlassert (pTvP); if (pTvP) { pTvP+=f; if (rTess.KeepMapping||!bTextured||(nChannel!=1)) { pTvP->t[0]=offset+x; pTvP->t[1]=offset+x+(nbdivsU+1); pTvP->t[2]=offset+x+1; pTvP++; pTvP->t[0]=offset+x+(nbdivsU+1); pTvP->t[1]=offset+x+(nbdivsU+1)+1; pTvP->t[2]=offset+x+1; } else { int nX2=x*2; pTvP->t[0]=mp+nX2; pTvP->t[1]=mp+nX2+nbdivsU*2; pTvP->t[2]=mp+nX2+1; pTvP++; pTvP->t[0]=mp+nX2+nbdivsU*2; pTvP->t[1]=mp+nX2+nbdivsU*2+1; pTvP->t[2]=mp+nX2+1; } } } } } if (Build&PART_TEXMAP) { int uTile=x>>nTileLevel; int vTile=y>>nTileLevel; uint nTileNumber=0; tileDesc& desc=getUIPatch (p).getTileDesc (uTile+vTile*nTileCountU); if (!desc.isEmpty ()) { nTileNumber=desc.getLayer(std::min (desc.getNumLayer()-1, rTess.TransitionType-1)).Tile+1; } pMesh->faces[f].setMatID(nTileNumber); pMesh->faces[f+1].setMatID(nTileNumber); } if (Build&PART_SELECT) { // Remap for hittest if (bTextured) { int nU=x>>rTess.TileTesselLevel; int nV=y>>rTess.TileTesselLevel; int tileNumber=GetTileNumber(p, nU, nV); setRemapEntry (f, tileNumber); setRemapEntry (f+1, tileNumber); if (GetSelLevel()==EP_TILE) { int bTileSel=tileSel[tileNumber]; if (bTileSel) { // Top if ((y&(nTess-1))==0) pMesh->edgeSel.Set(3*f+2); // Left if ((x&(nTess-1))==0) pMesh->edgeSel.Set(3*f); // Bottom if ((y&(nTess-1))==(nTess-1)) pMesh->edgeSel.Set(3*f+3); // Right if ((x&(nTess-1))==(nTess-1)) pMesh->edgeSel.Set(3*f+4); } // Face selection pMesh->faceSel.Set (f, bTileSel); pMesh->faceSel.Set (f+1, bTileSel); } } } f+=2; } offset+=nbdivsU+1; mp+=nbdivsU*4; } offset+=nbdivsU+1; // Generate texture vertices if ((Build&PART_TOPO)|(Build&PART_TEXMAP)) { // Copy mapping // For each channel for (nChannel=0; nChannelsetMapVert (nChannel, offsetcopy+u+v*nUCount, vPoint); fU+=fUd; } fV+=fVd; } } else { // For each tile for (int vTile=0; vTile>1)/(float)(nVertexTileCount>>1); for (int u=0; u>1)/(float)(nVertexTileCount>>1); int nIndex=mpcopy+vTile*nVertexTileCount*nVertexTileCount*nTileCountU+v*nVertexTileCount*nTileCountU+ uTile*nVertexTileCount+u; Point3 vRight=(vTt[(2+nRotate)&3][nCase]-vTt[(3+nRotate)&3][nCase])*fV+vTt[(3+nRotate)&3][nCase]; Point3 vLeft=(vTt[(1+nRotate)&3][nCase]-vTt[(0+nRotate)&3][nCase])*fV+vTt[(0+nRotate)&3][nCase]; Point3 vPoint=(vRight-vLeft)*fU+vLeft; pMesh->setMapVert (nChannel, nIndex, vPoint); } } } } } } } } } } } } if (Build&PART_TOPO) { pMesh->InvalidateTopologyCache(); //pMesh->BuildStripsAndEdges(); } if (Build&PART_GEOM) { pMesh->InvalidateGeomCache(); } ValidGeom=FOREVER; ValidTopo=FOREVER; ValidTexmap=FOREVER; ValidSelect=FOREVER; ValidDisplay=FOREVER; for (int nf=0; nfgetGW(); Matrix3 mat = inode->GetObjectTM(t); gw->setTransform(mat); // Becarful with sublevel selection mesh.dispFlags=0; switch (GetSelLevel()) { case EP_VERTEX: mesh.SetDispFlag(DISP_VERTTICKS|DISP_SELVERTS); mesh.selLevel = MESH_VERTEX; break; case EP_EDGE: case EP_PATCH: case EP_TILE: mesh.SetDispFlag(DISP_SELEDGES); mesh.selLevel = MESH_EDGE; break; default: mesh.SetDispFlag(0); mesh.selLevel = MESH_OBJECT; break; } if (paint) { mesh.SetDispFlag(DISP_SELEDGES); mesh.selLevel = MESH_EDGE; } // Go mesh.render( gw, inode->Mtls(), (flags&USE_DAMAGE_RECT) ? &vpt->GetDammageRect() : NULL, COMP_ALL | ((flags&DISP_SHOWSUBOBJECT)?COMP_OBJSELECTED:0), inode->NumMtls()); // Draw vector if ((GetSelLevel()==EP_VERTEX)&&(inode->Selected())) { UpdateBinding (patch, t); // Set new flags patch.SetDispFlag(DISP_VERTTICKS|DISP_SELVERTS); patch.selLevel=PATCH_VERTEX; DWORD dw=gw->getRndLimits(); BitArray bit; bit.SetSize (patch.numPatches); bit.ClearAll (); if ((dw&GW_PICK)==0) { // Hide all patch for (int nP=0; nPMtls(), (flags&USE_DAMAGE_RECT) ? &vpt->GetDammageRect() : NULL, COMP_ALL | ((flags&DISP_SHOWSUBOBJECT)?COMP_OBJSELECTED:0), inode->NumMtls()); if ((dw&GW_PICK)==0) { // Hide all patch for (int nP=0; nPSelected()) { gw->setColor(LINE_COLOR, 0, 0, 0); for (int i=0; imarker (&patch.verts[i].p, DOT_MRKR); } } } // A // - // B C // - // - // D // Draw the arrow if (GetSelLevel()==EP_PATCH) { for (int i=0; ipolyline (2, p, NULL, NULL, FALSE, NULL); p[1]=c; gw->polyline (2, p, NULL, NULL, FALSE, NULL); p[1]=d; gw->polyline (2, p, NULL, NULL, FALSE, NULL); } } } return 0; } // Return a tile desc tileDesc& RPatchMesh::getTileDesc (int nTile) { int patch=nTile/NUM_TILE_SEL; int tile=nTile%NUM_TILE_SEL; int tileY=tile/MAX_TILE_IN_PATCH; int tileX=tile%MAX_TILE_IN_PATCH; tile=tileY*(1<numPatches; p++) { // Selected ? if (patch->patchSel[p]) { // Tessel U and V int nOldU=1<<(getUIPatch (p).NbTilesU); int nOldV=1<<(getUIPatch (p).NbTilesV); // Reverse UV count int tmp=getUIPatch (p).NbTilesU; getUIPatch (p).NbTilesU=getUIPatch (p).NbTilesV; getUIPatch (p).NbTilesV=tmp; // Copy old array UI_PATCH old=getUIPatch (p); // Turn tile array int u, v; for (v=0; vnumVerts; v++) { // Binded ? if (getUIVertex (v).Binding.bBinded) { // On a patch turned ? int nPatch=getUIVertex (v).Binding.nPatch; if (patch->patchSel[nPatch]) { // Ok, turn the bind info.. getUIVertex (v).Binding.nEdge--; getUIVertex (v).Binding.nEdge&=3; } } } } // Turn selected patch void RPatchMesh::RotateTiles (PatchMesh *patch, int rot) { // For each patch for (int p=0; pnumPatches; p++) { // Tessel U and V int nU=1<<(getUIPatch (p).NbTilesU); int nV=1<<(getUIPatch (p).NbTilesV); // Turn tile array for (int v=0; vnumPatches; p++) { // Tessel U and V int nU=1<<(getUIPatch (p).NbTilesU); int nV=1<<(getUIPatch (p).NbTilesV); // Copy old array UI_PATCH old=getUIPatch (p); // Turn tile array for (int v=0; v(&fromOb), PART_GEOM|SELECT_CHANNEL|PART_SUBSEL_TYPE|PART_DISPLAY|PART_TOPO|TEXMAP_CHANNEL); } #endif // USE_CACHE CBankManager RPatchMesh::manager;