// 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 _T("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)
{
TCHAR path[256];
DWORD len=256 * sizeof(TCHAR);
DWORD type;
if (RegQueryValueEx(hKey, _T("Bank Path"), 0, &type, (LPBYTE)path, &len)==ERROR_SUCCESS)
return tStrToUtf8(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, _T("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, _T("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 information
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;