mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-22 17:08:43 +00:00
3140 lines
93 KiB
C++
3140 lines
93 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
#include "std3d.h"
|
|
|
|
#include "nel/3d/mrm_builder.h"
|
|
#include "nel/3d/mrm_parameters.h"
|
|
using namespace NLMISC;
|
|
using namespace std;
|
|
|
|
|
|
namespace NL3D
|
|
{
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// Tools Methods.
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
static bool findElement(vector<sint> &array, sint elt)
|
|
{
|
|
return find(array.begin(), array.end(), elt) != array.end();
|
|
}
|
|
// ***************************************************************************
|
|
static bool deleteElement(vector<sint> &array, sint elt)
|
|
{
|
|
bool found=false;
|
|
vector<sint>::iterator it=array.begin();
|
|
|
|
while( (it=find(array.begin(), array.end(), elt)) != array.end() )
|
|
found=true, array.erase(it);
|
|
|
|
return found;
|
|
// Do not use remove<> since it desn't modify size ... (???)
|
|
// This doesn't seem to work.
|
|
//return remove(array.begin(), array.end(), elt)!=array.end();
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// Edge Cost methods.
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
bool CMRMBuilder::vertexHasOneWedge(sint numvertex)
|
|
{
|
|
CMRMVertex &vert= TmpVertices[numvertex];
|
|
for(sint attId=0;attId<NumAttributes;attId++)
|
|
{
|
|
sint numwedge=-1;
|
|
for(sint i=0;i<(sint)vert.SharedFaces.size();i++)
|
|
{
|
|
sint w= TmpFaces[vert.SharedFaces[i]].getAssociatedWedge(attId, numvertex);
|
|
if(numwedge>=0 && numwedge!=w) return false;
|
|
else numwedge=w;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
// ***************************************************************************
|
|
bool CMRMBuilder::vertexHasOneMaterial(sint numvertex)
|
|
{
|
|
sint matId=-1;
|
|
CMRMVertex &vert= TmpVertices[numvertex];
|
|
for(sint i=0;i<(sint)vert.SharedFaces.size();i++)
|
|
{
|
|
sint m= TmpFaces[vert.SharedFaces[i]].MaterialId;
|
|
if(matId>=0 && matId!=m) return false;
|
|
else matId=m;
|
|
}
|
|
return true;
|
|
}
|
|
// ***************************************************************************
|
|
bool CMRMBuilder::vertexContinue(sint numvertex)
|
|
{
|
|
return vertexHasOneWedge(numvertex) && vertexHasOneMaterial(numvertex);
|
|
}
|
|
// ***************************************************************************
|
|
bool CMRMBuilder::vertexClosed(sint numvertex)
|
|
{
|
|
CMRMVertex &vert= TmpVertices[numvertex];
|
|
map<CMRMEdge, sint> EdgeShare;
|
|
// Init to 0.
|
|
sint i;
|
|
for(i=0;i<(sint)vert.SharedFaces.size();i++)
|
|
{
|
|
CMRMFaceBuild &f=TmpFaces[vert.SharedFaces[i]];
|
|
EdgeShare[f.getEdge(0)]= 0;
|
|
EdgeShare[f.getEdge(1)]= 0;
|
|
EdgeShare[f.getEdge(2)]= 0;
|
|
}
|
|
// Inc count.
|
|
for(i=0;i<(sint)vert.SharedFaces.size();i++)
|
|
{
|
|
CMRMFaceBuild &f=TmpFaces[vert.SharedFaces[i]];
|
|
EdgeShare[f.getEdge(0)]++;
|
|
EdgeShare[f.getEdge(1)]++;
|
|
EdgeShare[f.getEdge(2)]++;
|
|
}
|
|
// Test open edges.
|
|
for(i=0;i<(sint)vert.SharedFaces.size();i++)
|
|
{
|
|
CMRMFaceBuild &f=TmpFaces[vert.SharedFaces[i]];
|
|
sint v0= f.Corner[0].Vertex;
|
|
sint v1= f.Corner[1].Vertex;
|
|
sint v2= f.Corner[2].Vertex;
|
|
if(EdgeShare[f.getEdge(0)]<2 && (v0==numvertex || v1==numvertex)) return false;
|
|
if(EdgeShare[f.getEdge(1)]<2 && (v1==numvertex || v2==numvertex)) return false;
|
|
if(EdgeShare[f.getEdge(2)]<2 && (v0==numvertex || v2==numvertex)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
// ***************************************************************************
|
|
float CMRMBuilder::getDeltaFaceNormals(sint numvertex)
|
|
{
|
|
// return a positive value of Somme(|DeltaNormals|) / NNormals.
|
|
CMRMVertex &vert= TmpVertices[numvertex];
|
|
float delta=0;
|
|
CVector refNormal;
|
|
sint nfaces=(sint)vert.SharedFaces.size();
|
|
for(sint i=0;i<nfaces;i++)
|
|
{
|
|
CVector normal;
|
|
CVector &v0= TmpVertices[TmpFaces[i].Corner[0].Vertex].Current;
|
|
CVector &v1= TmpVertices[TmpFaces[i].Corner[1].Vertex].Current;
|
|
CVector &v2= TmpVertices[TmpFaces[i].Corner[2].Vertex].Current;
|
|
normal= (v1-v0)^(v2-v0);
|
|
normal.normalize();
|
|
if(i==0)
|
|
refNormal=normal;
|
|
else
|
|
delta+=(1-refNormal*normal);
|
|
}
|
|
if(nfaces<2)
|
|
return 0;
|
|
else
|
|
return delta/(nfaces-1);
|
|
}
|
|
// ***************************************************************************
|
|
bool CMRMBuilder::edgeContinue(const CMRMEdge &edge)
|
|
{
|
|
sint v0= edge.v0;
|
|
sint v1= edge.v1;
|
|
CMRMVertex &Vertex1=TmpVertices[v0];
|
|
|
|
// build list sharing edge.
|
|
vector<sint> deletedFaces;
|
|
sint i;
|
|
for(i=0;i<(sint)Vertex1.SharedFaces.size();i++)
|
|
{
|
|
sint numFace= Vertex1.SharedFaces[i];
|
|
if(TmpFaces[numFace].hasVertex(v1))
|
|
deletedFaces.push_back(numFace);
|
|
}
|
|
|
|
sint matId=-1;
|
|
// test if faces have same material.
|
|
for(i=0;i<(sint)deletedFaces.size();i++)
|
|
{
|
|
sint m;
|
|
m= TmpFaces[deletedFaces[i]].MaterialId;
|
|
if(matId>=0 && matId!=m) return false;
|
|
else matId=m;
|
|
}
|
|
|
|
// test if faces have same wedge (for all att).
|
|
for(sint attId=0;attId<NumAttributes;attId++)
|
|
{
|
|
sint numwedge1=-1,numwedge2=-1;
|
|
for(i=0;i<(sint)deletedFaces.size();i++)
|
|
{
|
|
sint w;
|
|
w= TmpFaces[deletedFaces[i]].getAssociatedWedge(attId, v0);
|
|
if(numwedge1>=0 && numwedge1!=w) return false;
|
|
else numwedge1=w;
|
|
w= TmpFaces[deletedFaces[i]].getAssociatedWedge(attId, v1);
|
|
if(numwedge2>=0 && numwedge2!=w) return false;
|
|
else numwedge2=w;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
// ***************************************************************************
|
|
bool CMRMBuilder::edgeNearUniqueMatFace(const CMRMEdge &edge)
|
|
{
|
|
sint v0= edge.v0;
|
|
sint v1= edge.v1;
|
|
CMRMVertex &Vertex1=TmpVertices[v0];
|
|
|
|
// build list sharing edge.
|
|
vector<sint> deletedFaces;
|
|
sint i;
|
|
for(i=0;i<(sint)Vertex1.SharedFaces.size();i++)
|
|
{
|
|
sint numFace= Vertex1.SharedFaces[i];
|
|
if(TmpFaces[numFace].hasVertex(v1))
|
|
deletedFaces.push_back(numFace);
|
|
}
|
|
|
|
// test if faces are not isolated OneMaterial faces.
|
|
for(i=0;i<(sint)deletedFaces.size();i++)
|
|
{
|
|
CMRMFaceBuild &f=TmpFaces[deletedFaces[i]];
|
|
if( !edgeContinue(f.getEdge(0)) &&
|
|
!edgeContinue(f.getEdge(1)) &&
|
|
!edgeContinue(f.getEdge(2)))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
float CMRMBuilder::computeEdgeCost(const CMRMEdge &edge)
|
|
{
|
|
sint v1= edge.v0;
|
|
sint v2= edge.v1;
|
|
// more expensive is the edge, later it will collapse.
|
|
|
|
|
|
// **** standard cost
|
|
|
|
// compute size of the edge.
|
|
float cost=(TmpVertices[v1].Current-TmpVertices[v2].Current).norm();
|
|
|
|
// compute "curvature" of the edge.
|
|
float faceCost= (getDeltaFaceNormals(v1)+getDeltaFaceNormals(v2));
|
|
// totally plane faces (faceCost==0) must be collapsed with respect to size (and not random if cost==0).
|
|
// else we may have Plane Mesh (like flags) that will collapse in a very ugly way.
|
|
faceCost= max(faceCost, 0.01f);
|
|
|
|
// modulate size with curvature.
|
|
cost*= faceCost;
|
|
|
|
// Like H.Hope, add a weight on discontinuities..
|
|
if( !vertexContinue(v1) && !vertexContinue(v2) )
|
|
{
|
|
// Nb: don't do this on discontinuities edges, unless the unique material face will collapse (pffiou!!).
|
|
if( edgeContinue(edge) || edgeNearUniqueMatFace(edge) )
|
|
cost*=4;
|
|
}
|
|
|
|
// **** Interface Sewing cost
|
|
if(_HasMeshInterfaces)
|
|
{
|
|
// if the 2 vertices come from a Sewing Interface mesh (must be a real interface id)
|
|
sint meshSewingId= TmpVertices[v1].InterfaceLink.InterfaceId;
|
|
if( meshSewingId>=0 && TmpVertices[v2].InterfaceLink.InterfaceId>=0 )
|
|
{
|
|
// if the 2 vertices come from the same Sewing Interface mesh
|
|
if( meshSewingId == TmpVertices[v2].InterfaceLink.InterfaceId )
|
|
{
|
|
// Then the edge is one of the sewing interface mesh. must do special things for it
|
|
CMRMSewingMesh &sewingMesh= _SewingMeshes[meshSewingId];
|
|
uint dummy;
|
|
|
|
// get the sewing edge id
|
|
CMRMEdge sewingEdge;
|
|
sewingEdge.v0= TmpVertices[v1].InterfaceLink.InterfaceVertexId;
|
|
sewingEdge.v1= TmpVertices[v2].InterfaceLink.InterfaceVertexId;
|
|
// if the current sewing lod want to collapse this edge
|
|
sint collapseId= sewingMesh.mustCollapseEdge(_CurrentLodComputed, sewingEdge, dummy);
|
|
if(collapseId>=0)
|
|
{
|
|
// Then set a negative priority (ie will collapse as soon as possible). from -N to -1.
|
|
// NB: sort them according to collapseId
|
|
cost= (float)(-sewingMesh.getNumCollapseEdge(_CurrentLodComputed) + collapseId);
|
|
}
|
|
else
|
|
{
|
|
// This edge must not collapse at this Lod, set an infinite priority (hope will never collapse).
|
|
cost= FLT_MAX;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The edge is between 2 interfaces but not the same. If we collide it we'll have holes!
|
|
This problem arise if space beetween interfaces is small. eg: if we setup an interface beetween
|
|
hair and head, and another one beetween head and torso, then we'll have this problem in the
|
|
back of the neck.
|
|
The solution is to make a big big cost to hope we'll never collide them (else Holes...)!!
|
|
Don't use FLT_MAX to still have a correct order if we don't have choice...
|
|
*/
|
|
cost*= 10000;
|
|
}
|
|
}
|
|
}
|
|
|
|
return cost;
|
|
}
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// Collapse Methods.
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
bool CMRMBuilder::faceShareWedges(CMRMFaceBuild *face, sint attribId, sint numVertex1, sint numVertex2)
|
|
{
|
|
sint numWedge1= face->getAssociatedWedge(attribId, numVertex1);
|
|
sint numWedge2= face->getAssociatedWedge(attribId, numVertex2);
|
|
if(numWedge1<0) return false;
|
|
if(numWedge2<0) return false;
|
|
|
|
CMRMAttribute &w1= TmpAttributes[attribId][numWedge1];
|
|
CMRMAttribute &w2= TmpAttributes[attribId][numWedge2];
|
|
return w1.Shared && w2.Shared && w1.NbSharedFaces>0 && w2.NbSharedFaces>0;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMRMBuilder::insertFaceIntoEdgeList(CMRMFaceBuild &f)
|
|
{
|
|
float len;
|
|
if(f.ValidIt0)
|
|
{
|
|
len= computeEdgeCost(f.getEdge(0));
|
|
f. It0= EdgeCollapses.insert( TEdgeMap::value_type( len, CMRMEdgeFace(f.getEdge(0),&f) ) );
|
|
}
|
|
if(f.ValidIt1)
|
|
{
|
|
len= computeEdgeCost(f.getEdge(1));
|
|
f. It1= EdgeCollapses.insert( TEdgeMap::value_type( len, CMRMEdgeFace(f.getEdge(1),&f) ) );
|
|
}
|
|
if(f.ValidIt2)
|
|
{
|
|
len= computeEdgeCost(f.getEdge(2));
|
|
f. It2= EdgeCollapses.insert( TEdgeMap::value_type( len, CMRMEdgeFace(f.getEdge(2),&f) ) );
|
|
}
|
|
}
|
|
// ***************************************************************************
|
|
void CMRMBuilder::removeFaceFromEdgeList(CMRMFaceBuild &f)
|
|
{
|
|
if(f.ValidIt0)
|
|
EdgeCollapses.erase(f.It0);
|
|
if(f.ValidIt1)
|
|
EdgeCollapses.erase(f.It1);
|
|
if(f.ValidIt2)
|
|
EdgeCollapses.erase(f.It2);
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
struct CTmpVertexWeight
|
|
{
|
|
uint32 MatrixId;
|
|
float Weight;
|
|
// For find().
|
|
bool operator==(const CTmpVertexWeight &o) const
|
|
{
|
|
return MatrixId==o.MatrixId;
|
|
}
|
|
// For sort().
|
|
bool operator<(const CTmpVertexWeight &o) const
|
|
{
|
|
return Weight>o.Weight;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
// ***************************************************************************
|
|
CMesh::CSkinWeight CMRMBuilder::collapseSkinWeight(const CMesh::CSkinWeight &sw1, const CMesh::CSkinWeight &sw2, float interValue) const
|
|
{
|
|
// If fast interpolation.
|
|
if(interValue==0)
|
|
return sw1;
|
|
if(interValue==1)
|
|
return sw2;
|
|
|
|
// else, must blend a skinWeight: must identify matrix which exist in the 2 sws, and add new ones.
|
|
uint nbMats1=0;
|
|
uint nbMats2=0;
|
|
static vector<CTmpVertexWeight> sws;
|
|
sws.reserve(NL3D_MESH_SKINNING_MAX_MATRIX * 2);
|
|
sws.clear();
|
|
|
|
// For all weights of sw1.
|
|
uint i;
|
|
for(i=0; i<NL3D_MESH_SKINNING_MAX_MATRIX; i++)
|
|
{
|
|
CTmpVertexWeight vw;
|
|
vw.MatrixId= sw1.MatrixId[i];
|
|
vw.Weight= sw1.Weights[i]*(1-interValue);
|
|
// if this weight is not null.
|
|
if(vw.Weight>0)
|
|
{
|
|
// add it to the list.
|
|
sws.push_back(vw);
|
|
}
|
|
// For skinning reduction.
|
|
if(sw1.Weights[i]>0)
|
|
nbMats1++;
|
|
}
|
|
|
|
|
|
// For all weights of sw1.
|
|
for(i=0; i<NL3D_MESH_SKINNING_MAX_MATRIX; i++)
|
|
{
|
|
CTmpVertexWeight vw;
|
|
vw.MatrixId= sw2.MatrixId[i];
|
|
vw.Weight= sw2.Weights[i]*(interValue);
|
|
// if this weight is not null.
|
|
if(vw.Weight>0)
|
|
{
|
|
// add it or add influence to the matrix.
|
|
vector<CTmpVertexWeight>::iterator it= find(sws.begin(), sws.end(), vw);
|
|
if(it== sws.end())
|
|
sws.push_back(vw);
|
|
else
|
|
it->Weight+= vw.Weight;
|
|
}
|
|
// For skinning reduction.
|
|
if(sw2.Weights[i]>0)
|
|
nbMats2++;
|
|
}
|
|
|
|
|
|
// Then keep just the best.
|
|
// sort by Weight decreasing order.
|
|
sort(sws.begin(), sws.end());
|
|
|
|
// clamp the result to the wanted max matrix.
|
|
uint nbMatsOut;
|
|
switch(_SkinReduction)
|
|
{
|
|
case CMRMParameters::SkinReductionMin:
|
|
nbMatsOut= min(nbMats1, nbMats2);
|
|
break;
|
|
case CMRMParameters::SkinReductionMax:
|
|
nbMatsOut= max(nbMats1, nbMats2);
|
|
break;
|
|
case CMRMParameters::SkinReductionBest:
|
|
nbMatsOut= min( (uint)sws.size(), (uint)NL3D_MESH_SKINNING_MAX_MATRIX );
|
|
break;
|
|
default:
|
|
nlstop;
|
|
};
|
|
// For security.
|
|
nbMatsOut= min(nbMatsOut, (uint)sws.size());
|
|
nlassert(nbMatsOut<=NL3D_MESH_SKINNING_MAX_MATRIX);
|
|
|
|
|
|
// Then output the result to the skinWeight, normalizing.
|
|
float sumWeight=0;
|
|
for(i= 0; i<nbMatsOut; i++)
|
|
{
|
|
sumWeight+= sws[i].Weight;
|
|
}
|
|
|
|
CMesh::CSkinWeight ret;
|
|
// Fill only needed matrix (other are rested in CMesh::CSkinWeight ctor).
|
|
for(i= 0; i<nbMatsOut; i++)
|
|
{
|
|
ret.MatrixId[i]= sws[i].MatrixId;
|
|
ret.Weights[i]= sws[i].Weight / sumWeight;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
sint CMRMBuilder::collapseEdge(const CMRMEdge &edge)
|
|
{
|
|
sint i,j;
|
|
float InterValue;
|
|
sint edgeV1=edge.v0;
|
|
sint edgeV2=edge.v1;
|
|
|
|
|
|
// 0. collapse the vertices.
|
|
//==========================
|
|
|
|
// edge.Vertex1 kept, but morphed.
|
|
// edge.Vertex2 deleted, and must know on which vertex it collapse.
|
|
CMRMVertex &Vertex1=TmpVertices[edgeV1], &Vertex2=TmpVertices[edgeV2];
|
|
|
|
// Interpolation choice.
|
|
// Default is to interpolate vertex 0 to the middle of the edge.
|
|
InterValue=0.5;
|
|
//InterValue=1;
|
|
// **** If at least one vertex of the edge is on a mesh sewing interface, must change InterValue
|
|
if( _HasMeshInterfaces && (Vertex1.InterfaceLink.InterfaceId>=0 || Vertex2.InterfaceLink.InterfaceId>=0) )
|
|
{
|
|
// If this is an edge of a mesh sewing interface
|
|
if(Vertex1.InterfaceLink.InterfaceId==Vertex2.InterfaceLink.InterfaceId)
|
|
{
|
|
// Then the edge is one of the sewing interface mesh. must do special things for it
|
|
CMRMSewingMesh &sewingMesh= _SewingMeshes[Vertex1.InterfaceLink.InterfaceId];
|
|
|
|
// get the sewing edge id
|
|
CMRMEdge sewingEdge;
|
|
sewingEdge.v0= Vertex1.InterfaceLink.InterfaceVertexId;
|
|
sewingEdge.v1= Vertex2.InterfaceLink.InterfaceVertexId;
|
|
|
|
// Get the edge in the sewing mesh which is said to be collapsed
|
|
uint vertToCollapse;
|
|
sint collapseId= sewingMesh.mustCollapseEdge(_CurrentLodComputed, sewingEdge, vertToCollapse);
|
|
// if exist
|
|
if(collapseId>=0)
|
|
{
|
|
// if it is v0 which must collapse, then InterValue=1
|
|
if(vertToCollapse==(uint)sewingEdge.v0)
|
|
InterValue= 1;
|
|
else
|
|
InterValue= 0;
|
|
|
|
}
|
|
else
|
|
{
|
|
// This should not happens. But it is still possible if this edge don't want to collapse but if their
|
|
// is no more choice. Take a default value
|
|
InterValue= 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// must collapse to the vertex on the sewing interface (as if it was open)
|
|
if(Vertex1.InterfaceLink.InterfaceId>=0)
|
|
{
|
|
// NB: it is possible that both vertices are on a different sewing interface... still collapse (must have to)
|
|
InterValue= 0;
|
|
}
|
|
else
|
|
{
|
|
// Then Vertex2 is on a sewing interface, collapse to it
|
|
InterValue= 1;
|
|
}
|
|
}
|
|
}
|
|
// **** Else, on special cases, it is much more efficient to interpolate at start or at end of edge.
|
|
else
|
|
{
|
|
// If one vertex is "open", ie his shared faces do not represent a closed Fan, then interpolate to this one,
|
|
// so the mesh has the same silhouette.
|
|
bool vc1= vertexClosed(edgeV1);
|
|
bool vc2= vertexClosed(edgeV2);
|
|
if(!vc1 && vc2) InterValue=0;
|
|
else if(vc1 && !vc2) InterValue=1;
|
|
else
|
|
{
|
|
// Do the same test but with vertex continue: it is preferable to not move the boundaries
|
|
// of a material, or a mapping.
|
|
bool vc1= vertexContinue(edgeV1);
|
|
bool vc2= vertexContinue(edgeV2);
|
|
if(!vc1 && vc2) InterValue=0;
|
|
if(vc1 && !vc2) InterValue=1;
|
|
}
|
|
}
|
|
/*BENCH_TotalCollapses++;
|
|
if(InterValue==0.5)
|
|
BENCH_MiddleCollapses++;*/
|
|
|
|
// Collapse the Vertex.
|
|
//========================
|
|
Vertex1.Current= Vertex1.Current*(1-InterValue) + Vertex2.Current*InterValue;
|
|
for (i = 0; i < (sint)Vertex1.BSCurrent.size(); ++i)
|
|
Vertex1.BSCurrent[i] = Vertex1.BSCurrent[i]*(1-InterValue) + Vertex2.BSCurrent[i]*InterValue;
|
|
Vertex2.CollapsedTo= edgeV1;
|
|
if(_Skinned)
|
|
Vertex1.CurrentSW= collapseSkinWeight(Vertex1.CurrentSW, Vertex2.CurrentSW, InterValue);
|
|
if( _HasMeshInterfaces )
|
|
Vertex1.InterfaceLink= InterValue<0.5f? Vertex1.InterfaceLink : Vertex2.InterfaceLink;
|
|
|
|
// \todo yoyo: TODO_BUG: Don't know why, but vertices may point on deleted faces.
|
|
// Temp: we destroy here thoses face from SharedFaces...
|
|
for(i=0;i<(sint)Vertex1.SharedFaces.size();i++)
|
|
{
|
|
sint numFace= Vertex1.SharedFaces[i];
|
|
if(TmpFaces[numFace].Deleted)
|
|
deleteElement(Vertex1.SharedFaces, numFace), i--;
|
|
}
|
|
for(i=0;i<(sint)Vertex2.SharedFaces.size();i++)
|
|
{
|
|
sint numFace= Vertex2.SharedFaces[i];
|
|
if(TmpFaces[numFace].Deleted)
|
|
deleteElement(Vertex2.SharedFaces, numFace), i--;
|
|
}
|
|
|
|
|
|
// Build Neighbor faces.
|
|
vector<sint> neighboorFaces;
|
|
for(i=0;i<(sint)Vertex1.SharedFaces.size();i++)
|
|
{
|
|
sint numFace= Vertex1.SharedFaces[i];
|
|
if(!findElement(neighboorFaces, numFace))
|
|
neighboorFaces.push_back(numFace);
|
|
}
|
|
for(i=0;i<(sint)Vertex2.SharedFaces.size();i++)
|
|
{
|
|
sint numFace= Vertex2.SharedFaces[i];
|
|
if(!findElement(neighboorFaces, numFace))
|
|
neighboorFaces.push_back(numFace);
|
|
}
|
|
|
|
// Build faces which will be destroyed (may 1 or 2, maybe more for non conventionnal meshes).
|
|
vector<sint> deletedFaces;
|
|
for(i=0;i<(sint)Vertex1.SharedFaces.size();i++)
|
|
{
|
|
sint numFace= Vertex1.SharedFaces[i];
|
|
nlassert(!TmpFaces[numFace].Deleted);
|
|
if(TmpFaces[numFace].hasVertex(edgeV2))
|
|
deletedFaces.push_back(numFace);
|
|
}
|
|
|
|
|
|
// 1. Collapse the wedges.
|
|
//========================
|
|
|
|
// For ALL Attributes.
|
|
for(sint attId=0;attId<NumAttributes;attId++)
|
|
{
|
|
// a/ Stock the wedge interpolation in each destroyed face.
|
|
//------------------------------------------------------
|
|
for(i=0;i<(sint)deletedFaces.size();i++)
|
|
{
|
|
CMRMFaceBuild &face= TmpFaces[deletedFaces[i]];
|
|
|
|
CVectorH &w0= TmpAttributes[attId][ face.getAssociatedWedge(attId, edgeV1) ].Current;
|
|
CVectorH &w1= TmpAttributes[attId][ face.getAssociatedWedge(attId, edgeV2) ].Current;
|
|
|
|
CVectorH &itp= face.InterpolatedAttribute;
|
|
itp.x= w0.x*(1-InterValue) + w1.x*InterValue;
|
|
itp.y= w0.y*(1-InterValue) + w1.y*InterValue;
|
|
itp.z= w0.z*(1-InterValue) + w1.z*InterValue;
|
|
itp.w= w0.w*(1-InterValue) + w1.w*InterValue;
|
|
|
|
for (j = 0; j < (sint)face.BSInterpolated.size(); ++j)
|
|
{
|
|
CVectorH &w0 = TmpAttributes[attId][face.getAssociatedWedge(attId, edgeV1)].BSCurrent[j];
|
|
CVectorH &w1 = TmpAttributes[attId][face.getAssociatedWedge(attId, edgeV2)].BSCurrent[j];
|
|
CVectorH &itb = face.BSInterpolated[j];
|
|
itb.x = w0.x*(1-InterValue) + w1.x*InterValue;
|
|
itb.y = w0.y*(1-InterValue) + w1.y*InterValue;
|
|
itb.z = w0.z*(1-InterValue) + w1.z*InterValue;
|
|
itb.w = w0.w*(1-InterValue) + w1.w*InterValue;
|
|
}
|
|
}
|
|
|
|
|
|
// b/ Build wedge list to be modify.
|
|
//----------------------------------
|
|
vector<sint> wedges;
|
|
|
|
for(i=0;i<(sint)neighboorFaces.size();i++)
|
|
{
|
|
CMRMFaceBuild &face= TmpFaces[neighboorFaces[i]];
|
|
sint numWedge;
|
|
|
|
numWedge= face.getAssociatedWedge(attId, edgeV1);
|
|
if(numWedge>=0 && !findElement(wedges, numWedge))
|
|
wedges.push_back(numWedge);
|
|
|
|
numWedge= face.getAssociatedWedge(attId, edgeV2);
|
|
if(numWedge>=0 && !findElement(wedges, numWedge))
|
|
wedges.push_back(numWedge);
|
|
}
|
|
|
|
|
|
// c/ Count numFaces which point on those wedges. (- deleted faces).
|
|
//------------------------------------------------------------------
|
|
|
|
for(i=0;i<(sint)wedges.size();i++)
|
|
{
|
|
sint numWedge= wedges[i];
|
|
CMRMAttribute &wedge= TmpAttributes[attId][numWedge];
|
|
|
|
wedge.NbSharedFaces=0;
|
|
wedge.Shared=false;
|
|
|
|
// Count total ref count.
|
|
for(j=0;j<(sint)neighboorFaces.size();j++)
|
|
{
|
|
if(TmpFaces[neighboorFaces[j]].hasWedge(attId, numWedge))
|
|
wedge.NbSharedFaces++;
|
|
}
|
|
|
|
// Minus deleted faces.
|
|
for(j=0;j<(sint)deletedFaces.size();j++)
|
|
{
|
|
if(TmpFaces[deletedFaces[j]].hasWedge(attId, numWedge))
|
|
{
|
|
wedge.NbSharedFaces--;
|
|
wedge.Shared=true;
|
|
wedge.InterpolatedFace=deletedFaces[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// d/ Collapse wedge following 3 possibles cases.
|
|
//-----------------------------------------------
|
|
|
|
|
|
for(i=0;i<(sint)wedges.size();i++)
|
|
{
|
|
sint numWedge= wedges[i];
|
|
CMRMAttribute &wedge= TmpAttributes[attId][numWedge];
|
|
|
|
// if wedge not shared...
|
|
if(!wedge.Shared)
|
|
{
|
|
// We've got an "exterior wedge" which lost no corner => do not merge it nor delete it.
|
|
// Leave it as the same value (extrapolate it may not be a good solution).
|
|
}
|
|
else
|
|
{
|
|
// if wedge dissapears, notify.
|
|
if(wedge.NbSharedFaces==0)
|
|
{
|
|
wedge.CollapsedTo=-2;
|
|
// Do not change his value. (as specified in Hope article).
|
|
}
|
|
else
|
|
{
|
|
CMRMFaceBuild &face= TmpFaces[wedge.InterpolatedFace];
|
|
|
|
// Must interpolate it.
|
|
wedge.Current= face.InterpolatedAttribute;
|
|
wedge.BSCurrent = face.BSInterpolated;
|
|
|
|
// Must merge the wedge of the second vertex on first
|
|
// ONLY IF 2 interpolated wedges are shared and NbSharedFaces!=0.
|
|
if( numWedge==face.getAssociatedWedge(attId, edgeV2) &&
|
|
faceShareWedges(&face, attId, edgeV1, edgeV2) )
|
|
{
|
|
wedge.CollapsedTo= face.getAssociatedWedge(attId, edgeV1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// 3. collapse faces.
|
|
//===================
|
|
|
|
// delete face shared by edge.
|
|
for(i=0;i<(sint)deletedFaces.size();i++)
|
|
{
|
|
sint numFace= deletedFaces[i];
|
|
TmpFaces[numFace].Deleted=true;
|
|
|
|
// release edges from list.
|
|
removeFaceFromEdgeList(TmpFaces[numFace]);
|
|
// invalid all it!!
|
|
TmpFaces[numFace].invalidAllIts(EdgeCollapses);
|
|
|
|
|
|
// delete from vertex1 and 2 the deleted faces.
|
|
deleteElement( Vertex1.SharedFaces, numFace);
|
|
deleteElement( Vertex2.SharedFaces, numFace);
|
|
}
|
|
|
|
|
|
// must ref correctly the faces.
|
|
for(i=0;i<(sint)neighboorFaces.size();i++)
|
|
{
|
|
CMRMFaceBuild &face=TmpFaces[neighboorFaces[i]];
|
|
|
|
// good vertices
|
|
if(face.Corner[0].Vertex ==edgeV2) face.Corner[0].Vertex=edgeV1;
|
|
if(face.Corner[1].Vertex ==edgeV2) face.Corner[1].Vertex=edgeV1;
|
|
if(face.Corner[2].Vertex ==edgeV2) face.Corner[2].Vertex=edgeV1;
|
|
// nb: doesn't matter if deletedFaces are modified...
|
|
|
|
// good wedges
|
|
for(sint attId=0;attId<NumAttributes;attId++)
|
|
{
|
|
sint newWedge;
|
|
newWedge= TmpAttributes[attId][ face.Corner[0].Attributes[attId] ].CollapsedTo;
|
|
if(newWedge>=0) face.Corner[0].Attributes[attId]= newWedge;
|
|
newWedge= TmpAttributes[attId][ face.Corner[1].Attributes[attId] ].CollapsedTo;
|
|
if(newWedge>=0) face.Corner[1].Attributes[attId]= newWedge;
|
|
newWedge= TmpAttributes[attId][ face.Corner[2].Attributes[attId] ].CollapsedTo;
|
|
if(newWedge>=0) face.Corner[2].Attributes[attId]= newWedge;
|
|
}
|
|
|
|
// good edges.
|
|
/* Those ones are updated in collapseEdges(): they are removed from the edgeCollapseList,
|
|
then they are re-inserted with good Vertex indices.
|
|
*/
|
|
}
|
|
|
|
|
|
// The vertex1 has now the shared env of vertex2.
|
|
Vertex1.SharedFaces.insert(Vertex1.SharedFaces.end(), Vertex2.SharedFaces.begin(),
|
|
Vertex2.SharedFaces.end());
|
|
|
|
|
|
return (sint)deletedFaces.size();
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
sint CMRMBuilder::followVertex(sint i)
|
|
{
|
|
CMRMVertex &vert=TmpVertices[i];
|
|
if(vert.CollapsedTo>=0)
|
|
return followVertex(vert.CollapsedTo);
|
|
else
|
|
return i;
|
|
}
|
|
// ***************************************************************************
|
|
sint CMRMBuilder::followWedge(sint attribId, sint i)
|
|
{
|
|
CMRMAttribute &wedge= TmpAttributes[attribId][i];
|
|
if(wedge.CollapsedTo>=0)
|
|
return followWedge(attribId, wedge.CollapsedTo);
|
|
else
|
|
return i;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// Mesh Level method.
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
CMRMBuilder::CMRMBuilder()
|
|
{
|
|
NumAttributes= 0;
|
|
_Skinned= false;
|
|
_HasMeshInterfaces= false;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CMRMBuilder::init(const CMRMMesh &baseMesh)
|
|
{
|
|
sint i, attId;
|
|
|
|
|
|
// First clear ALL.
|
|
TmpVertices.clear();
|
|
for(attId=0;attId<NL3D_MRM_MAX_ATTRIB;attId++)
|
|
{
|
|
TmpAttributes[attId].clear();
|
|
}
|
|
TmpFaces.clear();
|
|
EdgeCollapses.clear();
|
|
|
|
|
|
// resize.
|
|
NumAttributes= baseMesh.NumAttributes;
|
|
TmpVertices.resize(baseMesh.Vertices.size());
|
|
for(attId=0;attId<NumAttributes;attId++)
|
|
{
|
|
TmpAttributes[attId].resize(baseMesh.Attributes[attId].size());
|
|
}
|
|
TmpFaces.resize(baseMesh.Faces.size());
|
|
|
|
|
|
// Then copy.
|
|
for(i=0;i<(sint)baseMesh.Vertices.size();i++)
|
|
{
|
|
TmpVertices[i].Current= TmpVertices[i].Original= baseMesh.Vertices[i];
|
|
TmpVertices[i].BSCurrent.resize(baseMesh.BlendShapes.size());
|
|
for(uint32 j = 0; j <baseMesh.BlendShapes.size() ;++j)
|
|
TmpVertices[i].BSCurrent[j]= baseMesh.BlendShapes[j].Vertices[i];
|
|
if(_Skinned)
|
|
TmpVertices[i].CurrentSW= TmpVertices[i].OriginalSW= baseMesh.SkinWeights[i];
|
|
if(_HasMeshInterfaces)
|
|
TmpVertices[i].InterfaceLink= baseMesh.InterfaceLinks[i];
|
|
}
|
|
for(attId=0;attId<NumAttributes;attId++)
|
|
{
|
|
for(i=0;i<(sint)baseMesh.Attributes[attId].size();i++)
|
|
{
|
|
TmpAttributes[attId][i].Current= TmpAttributes[attId][i].Original=
|
|
baseMesh.Attributes[attId][i];
|
|
TmpAttributes[attId][i].BSCurrent.resize(baseMesh.BlendShapes.size());
|
|
for(uint32 j = 0; j <baseMesh.BlendShapes.size() ;++j)
|
|
TmpAttributes[attId][i].BSCurrent[j]= baseMesh.BlendShapes[j].Attributes[attId][i];
|
|
}
|
|
}
|
|
for(i=0;i<(sint)baseMesh.Faces.size();i++)
|
|
{
|
|
TmpFaces[i]= baseMesh.Faces[i];
|
|
TmpFaces[i].BSInterpolated.resize(baseMesh.BlendShapes.size());
|
|
}
|
|
|
|
|
|
// Create vertices sharedFaces.
|
|
for(i=0;i<(sint)TmpFaces.size();i++)
|
|
{
|
|
CMRMFaceBuild &face= TmpFaces[i];
|
|
|
|
TmpVertices[face.Corner[0].Vertex].SharedFaces.push_back(i);
|
|
TmpVertices[face.Corner[1].Vertex].SharedFaces.push_back(i);
|
|
TmpVertices[face.Corner[2].Vertex].SharedFaces.push_back(i);
|
|
}
|
|
|
|
|
|
// Compute EdgeCost.
|
|
for(i=0;i<(sint)TmpFaces.size();i++)
|
|
{
|
|
CMRMFaceBuild &f= TmpFaces[i];
|
|
// At start, valid all edges.
|
|
f. ValidIt0= true;
|
|
f. ValidIt1= true;
|
|
f. ValidIt2= true;
|
|
insertFaceIntoEdgeList(f);
|
|
}
|
|
}
|
|
// ***************************************************************************
|
|
void CMRMBuilder::collapseEdges(sint nWantedFaces)
|
|
{
|
|
ItEdgeMap EdgeIt;
|
|
|
|
sint nCurrentFaces=(sint)TmpFaces.size();
|
|
sint bug0=0,bug2=0,bug3=0;
|
|
|
|
while(nCurrentFaces>nWantedFaces)
|
|
{
|
|
bug0++;
|
|
EdgeIt= EdgeCollapses.begin();
|
|
|
|
if(EdgeIt== EdgeCollapses.end())
|
|
break;
|
|
|
|
// 0. Look if edge already deleted
|
|
//================================
|
|
CMRMEdge edge=(*EdgeIt).second;
|
|
|
|
// Is it valid?? (ie his vertices exist yet??).
|
|
if(TmpVertices[ edge.v0 ].CollapsedTo>=0
|
|
|| TmpVertices[ edge.v1 ].CollapsedTo>=0)
|
|
{
|
|
// \todo yoyo: TODO_BUG: potential bug here...
|
|
CMRMFaceBuild &f= *(EdgeIt->second.Face);
|
|
nlassert(f.validEdgeIt(EdgeIt->second));
|
|
f.invalidEdgeIt(EdgeIt->second, EdgeCollapses);
|
|
EdgeCollapses.erase(EdgeIt);
|
|
bug2++;
|
|
continue;
|
|
}
|
|
// \todo yoyo: TODO_BUG: potential bug here...
|
|
// If a mesh is "open" it will crash if a "hole collapse"...
|
|
if(edge.v0==edge.v1)
|
|
{
|
|
CMRMFaceBuild &f= *(EdgeIt->second.Face);
|
|
nlassert(f.validEdgeIt(EdgeIt->second));
|
|
f.invalidEdgeIt(EdgeIt->second, EdgeCollapses);
|
|
EdgeCollapses.erase(EdgeIt);
|
|
bug3++;
|
|
continue;
|
|
}
|
|
|
|
|
|
// 1. else, OK, collapse it!!
|
|
//===========================
|
|
sint vertexCollapsed= edge.v0;
|
|
nCurrentFaces-= collapseEdge(edge);
|
|
|
|
|
|
// 2. Must reorder all his neighborhood.
|
|
//======================================
|
|
CMRMVertex &vert=TmpVertices[vertexCollapsed];
|
|
sint i;
|
|
// we delete from list modified edges, and we re-add them with their new value.
|
|
for(i=0;i<(sint)vert.SharedFaces.size();i++)
|
|
{
|
|
CMRMFaceBuild &f= TmpFaces[vert.SharedFaces[i]];
|
|
removeFaceFromEdgeList(f);
|
|
insertFaceIntoEdgeList(f);
|
|
}
|
|
|
|
}
|
|
}
|
|
// ***************************************************************************
|
|
void CMRMBuilder::saveCoarserMesh(CMRMMesh &coarserMesh)
|
|
{
|
|
sint i,attId,index;
|
|
// First clear ALL.
|
|
coarserMesh.Vertices.clear();
|
|
coarserMesh.SkinWeights.clear();
|
|
coarserMesh.InterfaceLinks.clear();
|
|
for(attId=0;attId<NL3D_MRM_MAX_ATTRIB;attId++)
|
|
{
|
|
coarserMesh.Attributes[attId].clear();
|
|
}
|
|
coarserMesh.Faces.clear();
|
|
coarserMesh.NumAttributes= NumAttributes;
|
|
|
|
// Vertices.
|
|
//==========
|
|
index=0;
|
|
for(i=0;i<(sint)TmpVertices.size();i++)
|
|
{
|
|
CMRMVertex &vert=TmpVertices[i];
|
|
if(vert.CollapsedTo==-1) // if exist yet.
|
|
{
|
|
vert.CoarserIndex=index;
|
|
coarserMesh.Vertices.push_back(vert.Current);
|
|
if(_Skinned)
|
|
coarserMesh.SkinWeights.push_back(vert.CurrentSW);
|
|
if(_HasMeshInterfaces)
|
|
coarserMesh.InterfaceLinks.push_back(vert.InterfaceLink);
|
|
|
|
index++;
|
|
}
|
|
else
|
|
vert.CoarserIndex=-1; // indicate that this vertex no more exist and is to be geomorphed to another.
|
|
}
|
|
|
|
|
|
// Attributes.
|
|
//============
|
|
for(attId=0;attId<NumAttributes;attId++)
|
|
{
|
|
index=0;
|
|
for(i=0;i<(sint)TmpAttributes[attId].size();i++)
|
|
{
|
|
CMRMAttribute &wedge= TmpAttributes[attId][i];
|
|
if(wedge.CollapsedTo==-1) // if exist yet.
|
|
{
|
|
wedge.CoarserIndex=index;
|
|
coarserMesh.Attributes[attId].push_back(wedge.Current);
|
|
index++;
|
|
}
|
|
else if(wedge.CollapsedTo==-2) // else if totaly destroyed.
|
|
{
|
|
// Insert this wedge in the coarser mesh.
|
|
// NB: the coarser mesh faces do not use it anymore, but FinerMesh use it
|
|
// for geomorph (LODMesh.CoarserFaces may point to it).
|
|
// NB: look at buildFinalMRM(), it works fine for all cases.
|
|
wedge.CoarserIndex=index;
|
|
coarserMesh.Attributes[attId].push_back(wedge.Current);
|
|
index++;
|
|
}
|
|
else
|
|
wedge.CoarserIndex=-1; // indicate that this wedge no more exist and is to be geomorphed to another.
|
|
}
|
|
}
|
|
|
|
// Faces.
|
|
//=======
|
|
for(i=0;i<(sint)TmpFaces.size();i++)
|
|
{
|
|
CMRMFaceBuild &face=TmpFaces[i];
|
|
if(!face.Deleted)
|
|
{
|
|
CMRMFace newFace;
|
|
// Material.
|
|
newFace.MaterialId= face.MaterialId;
|
|
for(sint j=0;j<3;j++)
|
|
{
|
|
// Vertex.
|
|
newFace.Corner[j].Vertex= TmpVertices[face.Corner[j].Vertex].CoarserIndex;
|
|
nlassert(newFace.Corner[j].Vertex>=0);
|
|
// Attributes.
|
|
for(attId=0;attId<NumAttributes;attId++)
|
|
{
|
|
sint oldidx= face.Corner[j].Attributes[attId];
|
|
newFace.Corner[j].Attributes[attId]= TmpAttributes[attId][oldidx].CoarserIndex;
|
|
nlassert(newFace.Corner[j].Attributes[attId]>=0);
|
|
}
|
|
|
|
}
|
|
|
|
coarserMesh.Faces.push_back(newFace);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMRMBuilder::makeLODMesh(CMRMMeshGeom &lodMesh)
|
|
{
|
|
sint i,j,attId,index,coidx;
|
|
|
|
// for all faces of this mesh, find target in the coarser mesh.
|
|
for(i=0;i<(sint)lodMesh.CoarserFaces.size();i++)
|
|
{
|
|
CMRMFace &face= lodMesh.CoarserFaces[i];
|
|
|
|
// For 3 corners.
|
|
for(j=0;j<3;j++)
|
|
{
|
|
// Vertex.
|
|
// The index is yet the index in the finer mesh.
|
|
index= face.Corner[j].Vertex;
|
|
// the index in the coarser mesh is vert.CoarserIndex.
|
|
coidx= TmpVertices[index].CoarserIndex;
|
|
// but if this vertex is collapsed, must find the good index (yet in the finer mesh)
|
|
if(coidx==-1)
|
|
{
|
|
// find to which we must collapse.
|
|
index= followVertex(index);
|
|
// and so we have the coarser index. this one must be valid.
|
|
coidx= TmpVertices[index].CoarserIndex;
|
|
nlassert(coidx>=0);
|
|
}
|
|
// update corner of CoarserFace.
|
|
face.Corner[j].Vertex= coidx;
|
|
|
|
|
|
// Do exactly same thing for all attributes.
|
|
for(attId=0;attId<NumAttributes;attId++)
|
|
{
|
|
index= face.Corner[j].Attributes[attId];
|
|
coidx= TmpAttributes[attId][index].CoarserIndex;
|
|
if(coidx==-1)
|
|
{
|
|
index= followWedge(attId, index);
|
|
coidx= TmpAttributes[attId][index].CoarserIndex;
|
|
nlassert(coidx>=0);
|
|
}
|
|
face.Corner[j].Attributes[attId]= coidx;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Transform source blend shapes to source blend shapes modified (just calculate new vertex/attr position)
|
|
/*void CMRMBuilder::computeBsVerticesAttributes(vector<CMRMMesh> &srcBsMeshs, vector<CMRMMesh> &bsMeshsMod)
|
|
{
|
|
sint i, j, k, attId;
|
|
|
|
bsMeshsMod.resize (srcBsMeshs.size());
|
|
for (k = 0; k < (sint)srcBsMeshs.size(); ++k)
|
|
{
|
|
CMRMMesh &rBsMesh = srcBsMeshs[k];
|
|
CMRMMesh &rBsMeshMod = bsMeshsMod[k];
|
|
|
|
// Calculate modified vertices with the linear equation back tracking help
|
|
rBsMeshMod.Vertices.resize (rBsMesh.Vertices.size());
|
|
for (i = 0; i < (sint)rBsMesh.Vertices.size(); ++i)
|
|
{
|
|
CLinearEquation &LinEq = TmpVertices[i].CurrentLinEq;
|
|
rBsMeshMod.Vertices[i] = CVector(0.0f, 0.0f, 0.0f);
|
|
for (j = 0; j < (sint)LinEq.Elts.size(); ++j)
|
|
{
|
|
rBsMeshMod.Vertices[i] += LinEq.Elts[j].factor * rBsMesh.Vertices[LinEq.Elts[j].index];
|
|
}
|
|
}
|
|
|
|
// All attributes
|
|
rBsMeshMod.NumAttributes = NumAttributes;
|
|
for (attId = 0; attId < NumAttributes; attId++)
|
|
{
|
|
rBsMeshMod.Attributes[attId].resize (rBsMesh.Attributes[attId].size());
|
|
for (i = 0; i < (sint)rBsMesh.Attributes[attId].size(); ++i)
|
|
{
|
|
CLinearEquation &LinEq = TmpAttributes[attId][i].CurrentLinEq;
|
|
rBsMeshMod.Attributes[attId][i] = CVectorH(0.0f, 0.0f, 0.0f, 0.0f);
|
|
for (j = 0; j < (sint)LinEq.Elts.size(); ++j)
|
|
{
|
|
rBsMeshMod.Attributes[attId][i].x += LinEq.Elts[j].factor * rBsMesh.Attributes[attId][LinEq.Elts[j].index].x;
|
|
rBsMeshMod.Attributes[attId][i].y += LinEq.Elts[j].factor * rBsMesh.Attributes[attId][LinEq.Elts[j].index].y;
|
|
rBsMeshMod.Attributes[attId][i].z += LinEq.Elts[j].factor * rBsMesh.Attributes[attId][LinEq.Elts[j].index].z;
|
|
rBsMeshMod.Attributes[attId][i].w += LinEq.Elts[j].factor * rBsMesh.Attributes[attId][LinEq.Elts[j].index].w;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}*/
|
|
|
|
// ***************************************************************************
|
|
// Transform source Blend Shape Meshes Modified into coarser blend shape mesh (compact vertices)
|
|
void CMRMBuilder::makeCoarserBS (vector<CMRMBlendShape> &csBsMeshs)
|
|
{
|
|
uint32 i, k;
|
|
sint32 nSizeVert, nSizeAttr, attId;
|
|
|
|
// Calculate size of vertices array
|
|
nSizeVert = 0;
|
|
for (i = 0; i < TmpVertices.size(); ++i)
|
|
if(TmpVertices[i].CoarserIndex > nSizeVert)
|
|
nSizeVert = TmpVertices[i].CoarserIndex;
|
|
++nSizeVert;
|
|
|
|
for (k = 0; k < csBsMeshs.size(); ++k)
|
|
{
|
|
CMRMBlendShape &rBsCoarserMesh = csBsMeshs[k];
|
|
|
|
rBsCoarserMesh.Vertices.resize (nSizeVert);
|
|
rBsCoarserMesh.NumAttributes = NumAttributes;
|
|
|
|
// Vertices
|
|
for(i = 0; i < TmpVertices.size(); ++i)
|
|
{
|
|
CMRMVertex &vert = TmpVertices[i];
|
|
if (vert.CoarserIndex != -1)
|
|
{
|
|
rBsCoarserMesh.Vertices[vert.CoarserIndex] = vert.BSCurrent[k];
|
|
}
|
|
}
|
|
|
|
for (attId = 0; attId < NumAttributes; attId++)
|
|
{
|
|
// Calculate size of attribute attId array
|
|
nSizeAttr = 0;
|
|
for(i = 0; i < TmpAttributes[attId].size(); i++)
|
|
if (TmpAttributes[attId][i].CoarserIndex > nSizeAttr)
|
|
nSizeAttr = TmpAttributes[attId][i].CoarserIndex;
|
|
++nSizeAttr;
|
|
|
|
rBsCoarserMesh.Attributes[attId].resize (nSizeAttr);
|
|
|
|
for (i = 0; i < TmpAttributes[attId].size(); i++)
|
|
{
|
|
CMRMAttribute &wedge = TmpAttributes[attId][i];
|
|
if (wedge.CoarserIndex != -1)
|
|
{
|
|
rBsCoarserMesh.Attributes[attId][wedge.CoarserIndex] = wedge.BSCurrent[k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CMRMBuilder::makeFromMesh(const CMRMMesh &baseMesh, CMRMMeshGeom &lodMesh, CMRMMesh &coarserMesh, sint nWantedFaces)
|
|
{
|
|
// Init Tmp values in MRM builder.
|
|
init(baseMesh);
|
|
|
|
// compute MRM too next tgt face.
|
|
collapseEdges(nWantedFaces);
|
|
|
|
// save the coarser mesh.
|
|
saveCoarserMesh(coarserMesh);
|
|
// Build coarser BlendShapes.
|
|
coarserMesh.BlendShapes.resize(baseMesh.BlendShapes.size());
|
|
makeCoarserBS(coarserMesh.BlendShapes);
|
|
|
|
// build the lodMesh (baseMesh, with vertex/Attributes collapse infos).
|
|
lodMesh= baseMesh;
|
|
makeLODMesh(lodMesh);
|
|
|
|
// end for this level.
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// Global MRM Level method.
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMRMBuilder::buildAllLods(const CMRMMesh &baseMesh, std::vector<CMRMMeshGeom> &lodMeshs,
|
|
uint nWantedLods, uint divisor)
|
|
{
|
|
sint nFaces= (sint)baseMesh.Faces.size();
|
|
sint nBaseFaces;
|
|
sint i;
|
|
CMRMMesh srcMesh = baseMesh;
|
|
|
|
// coarsest LOD will have those number of faces.
|
|
nBaseFaces=nFaces/divisor;
|
|
nBaseFaces=max(nBaseFaces,4);
|
|
|
|
// must have at least 2 LOD to be really intersting. But the rest of the process work too with only one Lod!!
|
|
nlassert(nWantedLods>=1);
|
|
lodMeshs.resize(nWantedLods);
|
|
|
|
// If only one lod asked, must init some Tmp Global values (like NumAttributes)
|
|
if(nWantedLods==1)
|
|
{
|
|
_CurrentLodComputed= 0;
|
|
init(baseMesh);
|
|
}
|
|
|
|
// must fill all LODs, from end to start. do not proces last lod since it will be the coarsest mesh.
|
|
for(i=nWantedLods-1;i>0;i--)
|
|
{
|
|
sint nbWantedFaces;
|
|
|
|
// for sewing computing
|
|
_CurrentLodComputed= i;
|
|
|
|
// Linear.
|
|
nbWantedFaces= nBaseFaces + (nFaces-nBaseFaces) * (i-1)/(nWantedLods-1);
|
|
nbWantedFaces=max(nbWantedFaces,4);
|
|
|
|
// Build this LOD.
|
|
CMRMMesh csMesh;
|
|
// The mesh
|
|
makeFromMesh(srcMesh, lodMeshs[i], csMesh, nbWantedFaces);
|
|
|
|
// next mesh to process is csMesh.
|
|
srcMesh = csMesh;
|
|
}
|
|
// the first lodMedsh gets the coarsest mesh.
|
|
lodMeshs[0]= srcMesh;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMRMBuilder::buildFinalMRM(std::vector<CMRMMeshGeom> &lodMeshs, CMRMMeshFinal &finalMRM)
|
|
{
|
|
sint i,j;
|
|
sint lodId, attId;
|
|
sint nLods= (sint)lodMeshs.size();
|
|
|
|
// Init.
|
|
// ===============
|
|
finalMRM.reset();
|
|
finalMRM.NumAttributes= NumAttributes;
|
|
finalMRM.Skinned= _Skinned;
|
|
CMRMMeshFinal::CWedge::NumAttributesToCompare= NumAttributes;
|
|
CMRMMeshFinal::CWedge::CompareSkinning= _Skinned;
|
|
finalMRM.Lods.resize(nLods);
|
|
|
|
|
|
// Build Wedges, and faces index.
|
|
// ===============
|
|
// for all lods.
|
|
for(lodId=0; lodId<nLods; lodId++)
|
|
{
|
|
CMRMMeshGeom &lodMesh= lodMeshs[lodId];
|
|
CMRMMeshGeom &lodMeshPrec= lodMeshs[lodId==0?0:lodId-1];
|
|
// for all face corner.
|
|
for(i=0; i<(sint)lodMesh.Faces.size();i++)
|
|
{
|
|
// The current face.
|
|
CMRMFace &face= lodMesh.Faces[i];
|
|
// the current face, but which points to the prec LOD vertices/attributes.
|
|
CMRMFace &faceCoarser= lodMesh.CoarserFaces[i];
|
|
// for 3 corners.
|
|
for(j=0;j<3;j++)
|
|
{
|
|
CMRMCorner &corner= face.Corner[j];
|
|
CMRMCorner &cornerCoarser= faceCoarser.Corner[j];
|
|
// start and end wedge (geomorph), maybe same.
|
|
CMRMMeshFinal::CWedge wedgeStart;
|
|
CMRMMeshFinal::CWedge wedgeEnd;
|
|
|
|
// fill wedgeStart with values from lodMesh.
|
|
wedgeStart.Vertex= lodMesh.Vertices[corner.Vertex];
|
|
if(_Skinned)
|
|
wedgeStart.VertexSkin= lodMesh.SkinWeights[corner.Vertex];
|
|
for(attId=0; attId<NumAttributes; attId++)
|
|
{
|
|
wedgeStart.Attributes[attId]= lodMesh.Attributes[attId][corner.Attributes[attId]];
|
|
}
|
|
|
|
// if geomorph possible (ie not lod 0).
|
|
if(lodId>0)
|
|
{
|
|
// fill wedgeEnd with values from coarser lodMesh.
|
|
wedgeEnd.Vertex= lodMeshPrec.Vertices[cornerCoarser.Vertex];
|
|
if(_Skinned)
|
|
wedgeEnd.VertexSkin= lodMeshPrec.SkinWeights[cornerCoarser.Vertex];
|
|
for(attId=0; attId<NumAttributes; attId++)
|
|
{
|
|
wedgeEnd.Attributes[attId]= lodMeshPrec.Attributes[attId][cornerCoarser.Attributes[attId]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no geomorph.
|
|
wedgeEnd= wedgeStart;
|
|
}
|
|
|
|
// find/insert wedge, and get Ids. NB: if start/end same, same indices.
|
|
sint wedgeStartId= finalMRM.findInsertWedge(wedgeStart);
|
|
sint wedgeEndId= finalMRM.findInsertWedge(wedgeEnd);
|
|
|
|
// store in TmpCorner.
|
|
corner.WedgeStartId= wedgeStartId;
|
|
corner.WedgeEndId= wedgeEndId;
|
|
}
|
|
}
|
|
|
|
// Here, the number of wedge indicate the max number of wedge this LOD needs.
|
|
finalMRM.Lods[lodId].NWedges= (sint)finalMRM.Wedges.size();
|
|
}
|
|
|
|
|
|
// Count NBWedges necessary for geomorph, and compute Dest geomorph wedges ids.
|
|
// ===============
|
|
// the number of geomorph required for one LOD.
|
|
sint sglmGeom;
|
|
// the number of geomorph required for all LOD (max of sglmGeom).
|
|
sint sglmGeomMax= 0;
|
|
|
|
// Do not process lod 0, since no geomorph.
|
|
for(lodId=1; lodId<nLods; lodId++)
|
|
{
|
|
CMRMMeshGeom &lodMesh= lodMeshs[lodId];
|
|
|
|
// reset the GeomMap, the one which indicate if we have already inserted a geomorph.
|
|
_GeomMap.clear();
|
|
sglmGeom= 0;
|
|
|
|
// for all face corner.
|
|
for(i=0; i<(sint)lodMesh.Faces.size();i++)
|
|
{
|
|
// The current face.
|
|
CMRMFace &face= lodMesh.Faces[i];
|
|
// for 3 corners.
|
|
for(j=0;j<3;j++)
|
|
{
|
|
CMRMCorner &corner= face.Corner[j];
|
|
|
|
// if not same wedge Ids, this is a geomorphed wedge.
|
|
if(corner.WedgeStartId != corner.WedgeEndId)
|
|
{
|
|
// search if it exist yet in the set.
|
|
CMRMWedgeGeom geom;
|
|
geom.Start= corner.WedgeStartId;
|
|
geom.End= corner.WedgeEndId;
|
|
sint geomDest= sglmGeom;
|
|
// if don't find this geom in the set, then it is a new one.
|
|
TGeomMap::const_iterator it= _GeomMap.find(geom);
|
|
if(it == _GeomMap.end())
|
|
{
|
|
_GeomMap.insert( make_pair(geom, geomDest) );
|
|
sglmGeom++;
|
|
}
|
|
else
|
|
geomDest= it->second;
|
|
|
|
// store this Geom Id in the corner.
|
|
corner.WedgeGeomId= geomDest;
|
|
}
|
|
}
|
|
}
|
|
|
|
// take the max.
|
|
sglmGeomMax= max(sglmGeomMax, sglmGeom);
|
|
}
|
|
|
|
|
|
// inform the finalMRM.
|
|
finalMRM.NGeomSpace= sglmGeomMax;
|
|
|
|
|
|
// decal all wedges/ face index.
|
|
// ===============
|
|
// insert an empty space for dest geomorph.
|
|
finalMRM.Wedges.insert(finalMRM.Wedges.begin(), sglmGeomMax, CMRMMeshFinal::CWedge());
|
|
|
|
// Parse all faces corner of All lods, and decal Start/End Wedge index.
|
|
for(lodId=0; lodId<nLods; lodId++)
|
|
{
|
|
CMRMMeshGeom &lodMesh= lodMeshs[lodId];
|
|
|
|
// for all face corner.
|
|
for(i=0; i<(sint)lodMesh.Faces.size();i++)
|
|
{
|
|
// The current face.
|
|
CMRMFace &face= lodMesh.Faces[i];
|
|
// for 3 corners.
|
|
for(j=0;j<3;j++)
|
|
{
|
|
CMRMCorner &corner= face.Corner[j];
|
|
|
|
// decal indices.
|
|
corner.WedgeStartId+= sglmGeomMax;
|
|
corner.WedgeEndId+= sglmGeomMax;
|
|
}
|
|
}
|
|
|
|
// increment too the number of wedge required for this Lod.
|
|
finalMRM.Lods[lodId].NWedges+= sglmGeomMax;
|
|
}
|
|
|
|
|
|
// fill faces.
|
|
// ===============
|
|
// Parse all faces corner of All lods, and build Faces/Geomorphs..
|
|
for(lodId=0; lodId<nLods; lodId++)
|
|
{
|
|
CMRMMeshGeom &lodMesh= lodMeshs[lodId];
|
|
CMRMMeshFinal::CLod &lodDest= finalMRM.Lods[lodId];
|
|
|
|
// alloc final faces of this LOD.
|
|
lodDest.Faces.resize(lodMesh.Faces.size());
|
|
|
|
// reset the GeomMap, the one which indicate if we have already inserted a geomorph.
|
|
_GeomMap.clear();
|
|
|
|
// for all face corner.
|
|
for(i=0; i<(sint)lodMesh.Faces.size();i++)
|
|
{
|
|
// The current face.
|
|
CMRMFace &face= lodMesh.Faces[i];
|
|
// The dest face.
|
|
CMRMMeshFinal::CFace &faceDest= lodDest.Faces[i];
|
|
// fill good material.
|
|
faceDest.MaterialId= face.MaterialId;
|
|
|
|
// for 3 corners.
|
|
for(j=0;j<3;j++)
|
|
{
|
|
CMRMCorner &corner= face.Corner[j];
|
|
|
|
// if not same wedge Ids, this is a geomorphed wedge.
|
|
if(corner.WedgeStartId != corner.WedgeEndId)
|
|
{
|
|
// geomorph, so point to geomorphed wedge.
|
|
faceDest.WedgeId[j]= corner.WedgeGeomId;
|
|
|
|
// Build the geomorph, add it to the list (if not yet inserted).
|
|
CMRMWedgeGeom geom;
|
|
geom.Start= corner.WedgeStartId;
|
|
geom.End= corner.WedgeEndId;
|
|
// if don't find this geom in the set, then it is a new one.
|
|
TGeomMap::const_iterator it= _GeomMap.find(geom);
|
|
if(it == _GeomMap.end())
|
|
{
|
|
// mark it as inserted.
|
|
_GeomMap.insert( make_pair(geom, corner.WedgeGeomId) );
|
|
// and we must insert this geom in the array.
|
|
nlassert( corner.WedgeGeomId==(sint)lodDest.Geomorphs.size() );
|
|
lodDest.Geomorphs.push_back(geom);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no geomorph, so just point to good wedge.
|
|
faceDest.WedgeId[j]= corner.WedgeStartId;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// process all wedges, and compute NSkinMatUsed, skipping geomorphs.
|
|
// ===============
|
|
// NB: this works because weights are sorted from biggest to lowest.
|
|
if(_Skinned)
|
|
{
|
|
for(i=finalMRM.NGeomSpace; i<(sint)finalMRM.Wedges.size();i++)
|
|
{
|
|
CMRMMeshFinal::CWedge &wedge= finalMRM.Wedges[i];
|
|
for(j=0; j<NL3D_MESH_SKINNING_MAX_MATRIX; j++)
|
|
{
|
|
if(wedge.VertexSkin.Weights[j]==0)
|
|
break;
|
|
}
|
|
nlassert(j>0);
|
|
wedge.NSkinMatUsed= j;
|
|
}
|
|
}
|
|
|
|
// Blend Shape Stuff
|
|
finalMRM.MRMBlendShapesFinals.resize (lodMeshs[0].BlendShapes.size());
|
|
for (lodId = 0; lodId < nLods; ++lodId)
|
|
{
|
|
CMRMMeshGeom &lodMesh= lodMeshs[lodId];
|
|
CMRMMeshGeom &lodMeshPrec= lodMeshs[lodId==0?0:lodId-1];
|
|
|
|
// for all face corner.
|
|
for (i = 0; i < (sint)lodMesh.Faces.size(); ++i)
|
|
{
|
|
// The current face.
|
|
CMRMFace &face = lodMesh.Faces[i];
|
|
// the current face, but which points to the prec LOD vertices/attributes.
|
|
CMRMFace &faceCoarser = lodMesh.CoarserFaces[i];
|
|
// for 3 corners.
|
|
for (j = 0; j < 3; ++j)
|
|
{
|
|
CMRMCorner &corner = face.Corner[j];
|
|
CMRMCorner &cornerCoarser = faceCoarser.Corner[j];
|
|
|
|
sint startDestIndex = corner.WedgeStartId;
|
|
|
|
for (sint k = 0; k < (sint)finalMRM.MRMBlendShapesFinals.size(); ++k)
|
|
{
|
|
CMRMMeshFinal::CMRMBlendShapeFinal &rBSFinal = finalMRM.MRMBlendShapesFinals[k];
|
|
|
|
rBSFinal.Wedges.resize (finalMRM.Wedges.size());
|
|
// Fill WedgeStart used by this corner.
|
|
rBSFinal.Wedges[startDestIndex].Vertex = lodMesh.BlendShapes[k].Vertices[corner.Vertex];
|
|
for (attId = 0; attId < NumAttributes; ++attId)
|
|
{
|
|
rBSFinal.Wedges[startDestIndex].Attributes[attId] = lodMesh.BlendShapes[k].Attributes[attId][corner.Attributes[attId]];
|
|
}
|
|
|
|
// If geomorph, must fill the end too
|
|
if(lodId>0 && corner.WedgeStartId != corner.WedgeEndId)
|
|
{
|
|
sint endDestIndex = corner.WedgeEndId;
|
|
|
|
rBSFinal.Wedges[endDestIndex].Vertex = lodMeshPrec.BlendShapes[k].Vertices[cornerCoarser.Vertex];
|
|
for (attId = 0; attId < NumAttributes; ++attId)
|
|
{
|
|
rBSFinal.Wedges[endDestIndex].Attributes[attId] = lodMeshPrec.BlendShapes[k].Attributes[attId][cornerCoarser.Attributes[attId]];
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// Interface to MeshBuild Part.
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
sint CMRMBuilder::findInsertAttributeInBaseMesh(CMRMMesh &baseMesh, sint attId, sint vertexId, const CVectorH &att)
|
|
{
|
|
// find this attribute in the map.
|
|
CAttributeKey key;
|
|
key.VertexId= vertexId;
|
|
key.Attribute= att;
|
|
TAttributeMap::iterator it= _AttributeMap[attId].find(key);
|
|
|
|
// if attribute not found in the map, then insert a new one.
|
|
if(it==_AttributeMap[attId].end())
|
|
{
|
|
sint idx= (sint)baseMesh.Attributes[attId].size();
|
|
// insert into the array.
|
|
baseMesh.Attributes[attId].push_back(att);
|
|
// insert into the map.
|
|
_AttributeMap[attId].insert(make_pair(key, idx));
|
|
return idx;
|
|
}
|
|
else
|
|
{
|
|
// return the one found.
|
|
return it->second;
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
sint CMRMBuilder::findInsertNormalInBaseMesh(CMRMMesh &baseMesh, sint attId, sint vertexId, const CVector &normal)
|
|
{
|
|
CVectorH att;
|
|
att= normal;
|
|
att.w= 0;
|
|
return findInsertAttributeInBaseMesh(baseMesh, attId, vertexId, att);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
sint CMRMBuilder::findInsertColorInBaseMesh(CMRMMesh &baseMesh, sint attId, sint vertexId, CRGBA col)
|
|
{
|
|
CVectorH att;
|
|
att.x= col.R;
|
|
att.y= col.G;
|
|
att.z= col.B;
|
|
att.w= col.A;
|
|
return findInsertAttributeInBaseMesh(baseMesh, attId, vertexId, att);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
sint CMRMBuilder::findInsertUvwInBaseMesh(CMRMMesh &baseMesh, sint attId, sint vertexId, const NLMISC::CUVW &uvw)
|
|
{
|
|
CVectorH att;
|
|
att.x= uvw.U;
|
|
att.y= uvw.V;
|
|
att.z= uvw.W;
|
|
att.w= 0;
|
|
return findInsertAttributeInBaseMesh(baseMesh, attId, vertexId, att);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
CRGBA CMRMBuilder::attToColor(const CVectorH &att) const
|
|
{
|
|
CRGBA ret;
|
|
float tmp;
|
|
tmp= att.x; clamp(tmp, 0, 255);
|
|
ret.R= (uint8)(uint)tmp;
|
|
tmp= att.y; clamp(tmp, 0, 255);
|
|
ret.G= (uint8)(uint)tmp;
|
|
tmp= att.z; clamp(tmp, 0, 255);
|
|
ret.B= (uint8)(uint)tmp;
|
|
tmp= att.w; clamp(tmp, 0, 255);
|
|
ret.A= (uint8)(uint)tmp;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
NLMISC::CUVW CMRMBuilder::attToUvw(const CVectorH &att) const
|
|
{
|
|
return CUVW(att.x, att.y, att.z);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
uint32 CMRMBuilder::buildMrmBaseMesh(const CMesh::CMeshBuild &mbuild, CMRMMesh &baseMesh)
|
|
{
|
|
sint i,j,k;
|
|
sint nFaces;
|
|
sint attId;
|
|
// build the supported VertexFormat.
|
|
uint32 retVbFlags= CVertexBuffer::PositionFlag;
|
|
|
|
|
|
// reset the baseMesh.
|
|
baseMesh= CMRMMesh();
|
|
// reset Tmp.
|
|
for(attId=0; attId<NL3D_MRM_MAX_ATTRIB;attId++)
|
|
_AttributeMap[attId].clear();
|
|
|
|
|
|
// Compute number of attributes used by the MeshBuild.
|
|
// ========================
|
|
// Compute too
|
|
if(mbuild.VertexFlags & CVertexBuffer::NormalFlag)
|
|
{
|
|
baseMesh.NumAttributes++;
|
|
retVbFlags|= CVertexBuffer::NormalFlag;
|
|
}
|
|
if(mbuild.VertexFlags & CVertexBuffer::PrimaryColorFlag)
|
|
{
|
|
baseMesh.NumAttributes++;
|
|
retVbFlags|= CVertexBuffer::PrimaryColorFlag;
|
|
}
|
|
if(mbuild.VertexFlags & CVertexBuffer::SecondaryColorFlag)
|
|
{
|
|
baseMesh.NumAttributes++;
|
|
retVbFlags|= CVertexBuffer::SecondaryColorFlag;
|
|
}
|
|
for(k=0; k<CVertexBuffer::MaxStage;k++)
|
|
{
|
|
uint flag=CVertexBuffer::TexCoord0Flag<<k;
|
|
if(mbuild.VertexFlags & flag)
|
|
{
|
|
baseMesh.NumAttributes++;
|
|
retVbFlags|=flag;
|
|
}
|
|
}
|
|
nlassert(baseMesh.NumAttributes<=NL3D_MRM_MAX_ATTRIB);
|
|
|
|
|
|
// Fill basics: Vertices and Faces materials / index to vertices.
|
|
// ========================
|
|
// Just copy vertices.
|
|
baseMesh.Vertices= mbuild.Vertices;
|
|
// Just copy SkinWeights.
|
|
if(_Skinned)
|
|
baseMesh.SkinWeights= mbuild.SkinWeights;
|
|
// Just copy InterfaceLinks
|
|
if(_HasMeshInterfaces)
|
|
baseMesh.InterfaceLinks= mbuild.InterfaceLinks;
|
|
// Resize faces.
|
|
nFaces= (sint)mbuild.Faces.size();
|
|
baseMesh.Faces.resize(nFaces);
|
|
for(i=0; i<nFaces; i++)
|
|
{
|
|
// copy material Id.
|
|
baseMesh.Faces[i].MaterialId= mbuild.Faces[i].MaterialId;
|
|
// Copy Vertex index.
|
|
for(j=0; j<3; j++)
|
|
{
|
|
baseMesh.Faces[i].Corner[j].Vertex= mbuild.Faces[i].Corner[j].Vertex;
|
|
}
|
|
}
|
|
|
|
// Resolve attributes discontinuities and Fill attributes of the baseMesh.
|
|
// ========================
|
|
// For all corners.
|
|
for(i=0; i<nFaces; i++)
|
|
{
|
|
for(j=0; j<3; j++)
|
|
{
|
|
const CMesh::CCorner &srcCorner= mbuild.Faces[i].Corner[j];
|
|
CMRMCorner &destCorner= baseMesh.Faces[i].Corner[j];
|
|
attId= 0;
|
|
|
|
// For all activated attributes in mbuild, find/insert the attribute in the baseMesh.
|
|
// NB: 2 attributes are said to be different if they have not the same value OR if they don't lie
|
|
// on the same vertex. This is very important for MRM computing.
|
|
if(mbuild.VertexFlags & CVertexBuffer::NormalFlag)
|
|
{
|
|
destCorner.Attributes[attId]= findInsertNormalInBaseMesh(baseMesh, attId, destCorner.Vertex, srcCorner.Normal);
|
|
attId++;
|
|
}
|
|
if(mbuild.VertexFlags & CVertexBuffer::PrimaryColorFlag)
|
|
{
|
|
destCorner.Attributes[attId]= findInsertColorInBaseMesh(baseMesh, attId, destCorner.Vertex, srcCorner.Color);
|
|
attId++;
|
|
}
|
|
if(mbuild.VertexFlags & CVertexBuffer::SecondaryColorFlag)
|
|
{
|
|
destCorner.Attributes[attId]= findInsertColorInBaseMesh(baseMesh, attId, destCorner.Vertex, srcCorner.Specular);
|
|
attId++;
|
|
}
|
|
for(k=0; k<CVertexBuffer::MaxStage;k++)
|
|
{
|
|
if(mbuild.VertexFlags & (CVertexBuffer::TexCoord0Flag<<k))
|
|
{
|
|
destCorner.Attributes[attId]= findInsertUvwInBaseMesh(baseMesh, attId, destCorner.Vertex, srcCorner.Uvws[k]);
|
|
attId++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// End. clear Tmp infos.
|
|
// ========================
|
|
// reset Tmp.
|
|
for(attId=0; attId<NL3D_MRM_MAX_ATTRIB;attId++)
|
|
_AttributeMap[attId].clear();
|
|
|
|
return retVbFlags;
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
CMesh::CSkinWeight CMRMBuilder::normalizeSkinWeight(const CMesh::CSkinWeight &sw) const
|
|
{
|
|
uint nbMats= 0;
|
|
static vector<CTmpVertexWeight> sws;
|
|
sws.reserve(NL3D_MESH_SKINNING_MAX_MATRIX);
|
|
sws.clear();
|
|
|
|
// For all weights of sw1.
|
|
uint i;
|
|
for(i=0; i<NL3D_MESH_SKINNING_MAX_MATRIX; i++)
|
|
{
|
|
CTmpVertexWeight vw;
|
|
vw.MatrixId= sw.MatrixId[i];
|
|
vw.Weight= sw.Weights[i];
|
|
// if this weight is not null.
|
|
if(vw.Weight>0)
|
|
{
|
|
// add it to the list.
|
|
sws.push_back(vw);
|
|
nbMats++;
|
|
}
|
|
}
|
|
|
|
// sort by Weight decreasing order.
|
|
sort(sws.begin(), sws.end());
|
|
|
|
|
|
// Then output the result to the skinWeight, normalizing.
|
|
float sumWeight=0;
|
|
for(i= 0; i<nbMats; i++)
|
|
{
|
|
sumWeight+= sws[i].Weight;
|
|
}
|
|
|
|
CMesh::CSkinWeight ret;
|
|
// Fill only needed matrix (other are rested in CMesh::CSkinWeight ctor).
|
|
for(i= 0; i<nbMats; i++)
|
|
{
|
|
ret.MatrixId[i]= sws[i].MatrixId;
|
|
ret.Weights[i]= sws[i].Weight / sumWeight;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMRMBuilder::normalizeBaseMeshSkin(CMRMMesh &baseMesh) const
|
|
{
|
|
nlassert(_Skinned);
|
|
|
|
for(uint i=0; i<baseMesh.SkinWeights.size(); i++)
|
|
{
|
|
baseMesh.SkinWeights[i]= normalizeSkinWeight(baseMesh.SkinWeights[i]);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMRMBuilder::buildMeshBuildMrm(const CMRMMeshFinal &finalMRM, CMeshMRMGeom::CMeshBuildMRM &mbuild, uint32 vbFlags, uint32 nbMats, const CMesh::CMeshBuild &mb)
|
|
{
|
|
sint i,j,k;
|
|
sint attId;
|
|
|
|
// reset the mbuild.
|
|
mbuild= CMeshMRMGeom::CMeshBuildMRM();
|
|
// Setup VB.
|
|
|
|
bool useFormatExt = false;
|
|
// Check whether there are texture coordinates with more than 2 compnents, which force us to use an extended vertex format
|
|
for (k = 0; k < CVertexBuffer::MaxStage; ++k)
|
|
{
|
|
if (
|
|
(vbFlags & (CVertexBuffer::TexCoord0Flag << k))
|
|
&& mb.NumCoords[k] != 2)
|
|
{
|
|
useFormatExt = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint numTexCoordUsed = 0;
|
|
|
|
|
|
for (k = 0; k < CVertexBuffer::MaxStage; ++k)
|
|
{
|
|
if (vbFlags & (CVertexBuffer::TexCoord0Flag << k))
|
|
{
|
|
numTexCoordUsed = k;
|
|
}
|
|
}
|
|
|
|
if (!useFormatExt)
|
|
{
|
|
// setup standard format
|
|
mbuild.VBuffer.setVertexFormat(vbFlags);
|
|
}
|
|
else // setup extended format
|
|
{
|
|
mbuild.VBuffer.clearValueEx();
|
|
if (vbFlags & CVertexBuffer::PositionFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::Position, CVertexBuffer::Float3);
|
|
if (vbFlags & CVertexBuffer::NormalFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::Normal, CVertexBuffer::Float3);
|
|
if (vbFlags & CVertexBuffer::PrimaryColorFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::PrimaryColor, CVertexBuffer::UChar4);
|
|
if (vbFlags & CVertexBuffer::SecondaryColorFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::SecondaryColor, CVertexBuffer::UChar4);
|
|
if (vbFlags & CVertexBuffer::WeightFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::Weight, CVertexBuffer::Float4);
|
|
if (vbFlags & CVertexBuffer::PaletteSkinFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::PaletteSkin, CVertexBuffer::UChar4);
|
|
if (vbFlags & CVertexBuffer::FogFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::Fog, CVertexBuffer::Float1);
|
|
|
|
for (k = 0; k < CVertexBuffer::MaxStage; ++k)
|
|
{
|
|
if (vbFlags & (CVertexBuffer::TexCoord0Flag << k))
|
|
{
|
|
switch(mb.NumCoords[k])
|
|
{
|
|
case 2:
|
|
mbuild.VBuffer.addValueEx((CVertexBuffer::TValue) (CVertexBuffer::TexCoord0 + k), CVertexBuffer::Float2);
|
|
break;
|
|
case 3:
|
|
mbuild.VBuffer.addValueEx((CVertexBuffer::TValue) (CVertexBuffer::TexCoord0 + k), CVertexBuffer::Float3);
|
|
break;
|
|
default:
|
|
nlassert(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
mbuild.VBuffer.initEx();
|
|
}
|
|
|
|
// Copy the UVRouting
|
|
for (i=0; i<CVertexBuffer::MaxStage; i++)
|
|
{
|
|
mbuild.VBuffer.setUVRouting (i, mb.UVRouting[i]);
|
|
}
|
|
|
|
// Setup the VertexBuffer.
|
|
// ========================
|
|
// resize the VB.
|
|
mbuild.VBuffer.setNumVertices((uint32)finalMRM.Wedges.size());
|
|
// Setup SkinWeights.
|
|
if(_Skinned)
|
|
mbuild.SkinWeights.resize(finalMRM.Wedges.size());
|
|
|
|
CVertexBufferReadWrite vba;
|
|
mbuild.VBuffer.lock (vba);
|
|
|
|
// fill the VB.
|
|
for(i=0; i<(sint)finalMRM.Wedges.size(); i++)
|
|
{
|
|
const CMRMMeshFinal::CWedge &wedge= finalMRM.Wedges[i];
|
|
|
|
// setup Vertex.
|
|
vba.setVertexCoord(i, wedge.Vertex);
|
|
|
|
// seutp attributes.
|
|
attId= 0;
|
|
|
|
// For all activated attributes in mbuild, retriev the attribute from the finalMRM.
|
|
if(vbFlags & CVertexBuffer::NormalFlag)
|
|
{
|
|
vba.setNormalCoord(i, wedge.Attributes[attId] );
|
|
attId++;
|
|
}
|
|
if(vbFlags & CVertexBuffer::PrimaryColorFlag)
|
|
{
|
|
vba.setColor(i, attToColor(wedge.Attributes[attId]) );
|
|
attId++;
|
|
}
|
|
if(vbFlags & CVertexBuffer::SecondaryColorFlag)
|
|
{
|
|
vba.setSpecular(i, attToColor(wedge.Attributes[attId]) );
|
|
attId++;
|
|
}
|
|
for(k=0; k<CVertexBuffer::MaxStage;k++)
|
|
{
|
|
if(vbFlags & (CVertexBuffer::TexCoord0Flag<<k))
|
|
{
|
|
switch(mb.NumCoords[k])
|
|
{
|
|
case 2:
|
|
vba.setTexCoord(i, k, (CUV) attToUvw(wedge.Attributes[attId]) );
|
|
break;
|
|
case 3:
|
|
{
|
|
CUVW uvw = attToUvw(wedge.Attributes[attId]);
|
|
vba.setValueFloat3Ex((CVertexBuffer::TValue) (CVertexBuffer::TexCoord0 + k), i, uvw.U, uvw.V, uvw.W);
|
|
}
|
|
break;
|
|
default:
|
|
nlassert(0);
|
|
break;
|
|
}
|
|
attId++;
|
|
}
|
|
}
|
|
|
|
// Setup SkinWeights.
|
|
if(_Skinned)
|
|
{
|
|
mbuild.SkinWeights[i]= wedge.VertexSkin;
|
|
}
|
|
}
|
|
|
|
|
|
// Build Lods.
|
|
// ========================
|
|
// resize
|
|
mbuild.Lods.resize(finalMRM.Lods.size());
|
|
// fill.
|
|
for(i=0; i<(sint)finalMRM.Lods.size(); i++)
|
|
{
|
|
const CMRMMeshFinal::CLod &srcLod= finalMRM.Lods[i];
|
|
CMeshMRMGeom::CLod &destLod= mbuild.Lods[i];
|
|
|
|
// Basic.
|
|
//---------
|
|
|
|
// Copy NWedges infos.
|
|
destLod.NWedges= srcLod.NWedges;
|
|
// Copy Geomorphs infos.
|
|
destLod.Geomorphs= srcLod.Geomorphs;
|
|
|
|
|
|
// Reorder faces by rdrpass.
|
|
//---------
|
|
|
|
// First count the number of faces used by this LOD for each material
|
|
vector<sint> matCount;
|
|
// resize, and reset to 0.
|
|
matCount.clear();
|
|
matCount.resize(nbMats, 0);
|
|
// For each face of this Lods, incr the mat face counter.
|
|
for(j= 0; j<(sint)srcLod.Faces.size(); j++)
|
|
{
|
|
sint matId= srcLod.Faces[j].MaterialId;
|
|
nlassert(matId>=0);
|
|
nlassert(matId<(sint)nbMats);
|
|
// increment the refcount of this material by this LOD.
|
|
matCount[matId]++;
|
|
}
|
|
|
|
// Then for each material not empty, create a rdrPass, and ref it for this material.
|
|
vector<sint> rdrPassIndex; // material to rdrPass map.
|
|
rdrPassIndex.resize(nbMats);
|
|
for(j=0; j<(sint)nbMats; j++)
|
|
{
|
|
if(matCount[j]==0)
|
|
rdrPassIndex[j]= -1;
|
|
else
|
|
{
|
|
// map material to rdrPass.
|
|
sint idRdrPass= (sint)destLod.RdrPass.size();
|
|
rdrPassIndex[j]= idRdrPass;
|
|
// create a rdrPass.
|
|
destLod.RdrPass.push_back(CMeshMRMGeom::CRdrPass());
|
|
// assign the good materialId to this rdrPass.
|
|
destLod.RdrPass[idRdrPass].MaterialId= j;
|
|
// reserve the array of faces of this rdrPass.
|
|
destLod.RdrPass[idRdrPass].PBlock.reserve(3*matCount[j]);
|
|
}
|
|
}
|
|
|
|
// Then for each face, add it to the good rdrPass of this Lod.
|
|
for(j= 0; j<(sint)srcLod.Faces.size(); j++)
|
|
{
|
|
sint matId= srcLod.Faces[j].MaterialId;
|
|
sint idRdrPass= rdrPassIndex[matId];
|
|
// add this face to the good rdrPass.
|
|
sint w0= srcLod.Faces[j].WedgeId[0];
|
|
sint w1= srcLod.Faces[j].WedgeId[1];
|
|
sint w2= srcLod.Faces[j].WedgeId[2];
|
|
CIndexBuffer &ib = destLod.RdrPass[idRdrPass].PBlock;
|
|
uint index = ib.getNumIndexes();
|
|
ib.setNumIndexes(index+3);
|
|
CIndexBufferReadWrite ibaWrite;
|
|
ib.lock (ibaWrite);
|
|
ibaWrite.setTri(index, w0, w1, w2);
|
|
}
|
|
|
|
|
|
// Build skin info for this Lod.
|
|
//---------
|
|
for(j=0; j<NL3D_MESH_SKINNING_MAX_MATRIX; j++)
|
|
{
|
|
destLod.InfluencedVertices[j].clear();
|
|
}
|
|
destLod.MatrixInfluences.clear();
|
|
if(_Skinned)
|
|
{
|
|
// This is the set which tell what wedge has already been inserted.
|
|
set<uint> wedgeInfSet;
|
|
|
|
// First, build the list of vertices influenced by this Lod.
|
|
for(j= 0; j<(sint)srcLod.Faces.size(); j++)
|
|
{
|
|
for(k=0; k<3; k++)
|
|
{
|
|
sint wedgeId= srcLod.Faces[j].WedgeId[k];
|
|
// If it is a geomorph
|
|
if(wedgeId<finalMRM.NGeomSpace)
|
|
{
|
|
// add the start and end to the list (if not here). NB: wedgeId is both the id
|
|
// of the dest wedge, and the id of the geomorph.
|
|
sint wedgeStartId= destLod.Geomorphs[wedgeId].Start;
|
|
sint wedgeEndId= destLod.Geomorphs[wedgeId].End;
|
|
uint nMatUsedStart= finalMRM.Wedges[wedgeStartId].NSkinMatUsed;
|
|
uint nMatUsedEnd= finalMRM.Wedges[wedgeEndId].NSkinMatUsed;
|
|
|
|
// if insertion in the set work, add to the good array.
|
|
if( wedgeInfSet.insert(wedgeStartId).second )
|
|
destLod.InfluencedVertices[nMatUsedStart-1].push_back(wedgeStartId);
|
|
if( wedgeInfSet.insert(wedgeEndId).second )
|
|
destLod.InfluencedVertices[nMatUsedEnd-1].push_back(wedgeEndId);
|
|
}
|
|
else
|
|
{
|
|
uint nMatUsed= finalMRM.Wedges[wedgeId].NSkinMatUsed;
|
|
|
|
// just add this wedge to the list (if not here).
|
|
// if insertion in the set work, add to the array.
|
|
if( wedgeInfSet.insert(wedgeId).second )
|
|
destLod.InfluencedVertices[nMatUsed-1].push_back(wedgeId);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Optimisation: for better cache, sort the destLod.InfluencedVertices in increasing order.
|
|
for(j=0; j<NL3D_MESH_SKINNING_MAX_MATRIX; j++)
|
|
{
|
|
sort(destLod.InfluencedVertices[j].begin(), destLod.InfluencedVertices[j].end());
|
|
}
|
|
|
|
|
|
// Then Build the MatrixInfluences array, for all thoses Influenced Vertices only.
|
|
// This is the map MatrixId -> MatrixInfId.
|
|
map<uint, uint> matrixInfMap;
|
|
|
|
// For all influenced vertices, flags matrix they use.
|
|
uint iSkinMat;
|
|
for(iSkinMat= 0; iSkinMat<NL3D_MESH_SKINNING_MAX_MATRIX; iSkinMat++)
|
|
{
|
|
for(j= 0; j<(sint)destLod.InfluencedVertices[iSkinMat].size(); j++)
|
|
{
|
|
uint wedgeId= destLod.InfluencedVertices[iSkinMat][j];
|
|
|
|
// take the original wedge.
|
|
const CMRMMeshFinal::CWedge &wedge= finalMRM.Wedges[wedgeId];
|
|
// For all matrix with not null influence...
|
|
for(k= 0; k<NL3D_MESH_SKINNING_MAX_MATRIX; k++)
|
|
{
|
|
float matWeight= wedge.VertexSkin.Weights[k];
|
|
|
|
// This check the validity of skin weights sort. If false, problem before in the algo.
|
|
if((uint)k<iSkinMat+1)
|
|
{
|
|
nlassert( matWeight>0 );
|
|
}
|
|
else
|
|
{
|
|
nlassert( matWeight==0 );
|
|
}
|
|
// if not null influence.
|
|
if(matWeight>0)
|
|
{
|
|
uint matId= wedge.VertexSkin.MatrixId[k];
|
|
|
|
// search/insert the matrixInfId.
|
|
map<uint, uint>::iterator it= matrixInfMap.find(matId);
|
|
if( it==matrixInfMap.end() )
|
|
{
|
|
uint matInfId= (uint)destLod.MatrixInfluences.size();
|
|
matrixInfMap.insert( make_pair(matId, matInfId) );
|
|
// create the new MatrixInfluence.
|
|
destLod.MatrixInfluences.push_back(matId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Indicate Skinning.
|
|
mbuild.Skinned= _Skinned;
|
|
|
|
|
|
|
|
bool useTgSpace = mb.MeshVertexProgram != NULL ? mb.MeshVertexProgram->needTangentSpace() : false;
|
|
|
|
// Construct Blend Shapes
|
|
//// mbuild <- finalMRM
|
|
mbuild.BlendShapes.resize (finalMRM.MRMBlendShapesFinals.size());
|
|
for (k = 0; k < (sint)mbuild.BlendShapes.size(); ++k)
|
|
{
|
|
CBlendShape &rBS = mbuild.BlendShapes[k];
|
|
sint32 nNbVertVB = (sint32)finalMRM.Wedges.size();
|
|
bool bIsDeltaPos = false;
|
|
rBS.deltaPos.resize (nNbVertVB, CVector(0.0f,0.0f,0.0f));
|
|
bool bIsDeltaNorm = false;
|
|
rBS.deltaNorm.resize (nNbVertVB, CVector(0.0f,0.0f,0.0f));
|
|
bool bIsDeltaUV = false;
|
|
rBS.deltaUV.resize (nNbVertVB, CUV(0.0f,0.0f));
|
|
bool bIsDeltaCol = false;
|
|
rBS.deltaCol.resize (nNbVertVB, CRGBAF(0.0f,0.0f,0.0f,0.0f));
|
|
bool bIsDeltaTgSpace = false;
|
|
if (useTgSpace)
|
|
{
|
|
rBS.deltaTgSpace.resize(nNbVertVB, CVector::Null);
|
|
}
|
|
|
|
rBS.VertRefs.resize (nNbVertVB, 0xffffffff);
|
|
|
|
for (i = 0; i < nNbVertVB; i++)
|
|
{
|
|
const CMRMMeshFinal::CWedge &rWedgeRef = finalMRM.Wedges[i];
|
|
const CMRMMeshFinal::CWedge &rWedgeTar = finalMRM.MRMBlendShapesFinals[k].Wedges[i];
|
|
|
|
CVector delta = rWedgeTar.Vertex - rWedgeRef.Vertex;
|
|
CVectorH attr;
|
|
|
|
if (delta.norm() > 0.001f)
|
|
{
|
|
rBS.deltaPos[i] = delta;
|
|
rBS.VertRefs[i] = i;
|
|
bIsDeltaPos = true;
|
|
}
|
|
|
|
attId = 0;
|
|
if (vbFlags & CVertexBuffer::NormalFlag)
|
|
{
|
|
attr = rWedgeRef.Attributes[attId];
|
|
CVector NormRef = CVector(attr.x, attr.y, attr.z);
|
|
attr = rWedgeTar.Attributes[attId];
|
|
CVector NormTar = CVector(attr.x, attr.y, attr.z);
|
|
delta = NormTar - NormRef;
|
|
if (delta.norm() > 0.001f)
|
|
{
|
|
rBS.deltaNorm[i] = delta;
|
|
rBS.VertRefs[i] = i;
|
|
bIsDeltaNorm = true;
|
|
}
|
|
attId++;
|
|
}
|
|
|
|
if (vbFlags & CVertexBuffer::PrimaryColorFlag)
|
|
{
|
|
attr = rWedgeRef.Attributes[attId];
|
|
CRGBAF RGBARef = CRGBAF(attr.x/255.0f, attr.y/255.0f, attr.z/255.0f, attr.w/255.0f);
|
|
attr = rWedgeTar.Attributes[attId];
|
|
CRGBAF RGBATar = CRGBAF(attr.x/255.0f, attr.y/255.0f, attr.z/255.0f, attr.w/255.0f);
|
|
CRGBAF deltaRGBA = RGBATar - RGBARef;
|
|
if ((deltaRGBA.R*deltaRGBA.R + deltaRGBA.G*deltaRGBA.G +
|
|
deltaRGBA.B*deltaRGBA.B + deltaRGBA.A*deltaRGBA.A) > 0.0001f)
|
|
{
|
|
rBS.deltaCol[i] = deltaRGBA;
|
|
rBS.VertRefs[i] = i;
|
|
bIsDeltaCol = true;
|
|
}
|
|
attId++;
|
|
}
|
|
|
|
if (vbFlags & CVertexBuffer::SecondaryColorFlag)
|
|
{ // Nothing to do !
|
|
attId++;
|
|
}
|
|
|
|
// Do that only for the UV0
|
|
if (vbFlags & CVertexBuffer::TexCoord0Flag)
|
|
{
|
|
attr = rWedgeRef.Attributes[attId];
|
|
CUV UVRef = CUV(attr.x, attr.y);
|
|
attr = rWedgeTar.Attributes[attId];
|
|
CUV UVTar = CUV(attr.x, attr.y);
|
|
CUV deltaUV = UVTar - UVRef;
|
|
if ((deltaUV.U*deltaUV.U + deltaUV.V*deltaUV.V) > 0.0001f)
|
|
{
|
|
rBS.deltaUV[i] = deltaUV;
|
|
rBS.VertRefs[i] = i;
|
|
bIsDeltaUV = true;
|
|
}
|
|
attId++;
|
|
}
|
|
|
|
if (useTgSpace)
|
|
{
|
|
attr = rWedgeRef.Attributes[attId];
|
|
CVector TgSpaceRef = CVector(attr.x, attr.y, attr.z);
|
|
attr = rWedgeTar.Attributes[attId];
|
|
CVector TgSpaceTar = CVector(attr.x, attr.y, attr.z);
|
|
delta = TgSpaceTar - TgSpaceRef;
|
|
if (delta.norm() > 0.001f)
|
|
{
|
|
rBS.deltaTgSpace[i] = delta;
|
|
rBS.VertRefs[i] = i;
|
|
bIsDeltaTgSpace = true;
|
|
}
|
|
attId++;
|
|
}
|
|
|
|
} // End of all vertices added in blend shape
|
|
|
|
// Delete unused items and calculate the number of vertex used (blended)
|
|
|
|
sint32 nNbVertUsed = nNbVertVB;
|
|
sint32 nDstPos = 0;
|
|
for (j = 0; j < nNbVertVB; ++j)
|
|
{
|
|
if (rBS.VertRefs[j] == 0xffffffff) // Is vertex UNused
|
|
{
|
|
--nNbVertUsed;
|
|
}
|
|
else // Vertex used
|
|
{
|
|
if (nDstPos != j)
|
|
{
|
|
rBS.VertRefs[nDstPos] = rBS.VertRefs[j];
|
|
rBS.deltaPos[nDstPos] = rBS.deltaPos[j];
|
|
rBS.deltaNorm[nDstPos] = rBS.deltaNorm[j];
|
|
rBS.deltaUV[nDstPos] = rBS.deltaUV[j];
|
|
rBS.deltaCol[nDstPos] = rBS.deltaCol[j];
|
|
if (useTgSpace)
|
|
{
|
|
rBS.deltaTgSpace[nDstPos] = rBS.deltaTgSpace[j];
|
|
}
|
|
}
|
|
++nDstPos;
|
|
}
|
|
}
|
|
|
|
if (bIsDeltaPos)
|
|
rBS.deltaPos.resize (nNbVertUsed);
|
|
else
|
|
rBS.deltaPos.resize (0);
|
|
|
|
if (bIsDeltaNorm)
|
|
rBS.deltaNorm.resize (nNbVertUsed);
|
|
else
|
|
rBS.deltaNorm.resize (0);
|
|
|
|
if (bIsDeltaUV)
|
|
rBS.deltaUV.resize (nNbVertUsed);
|
|
else
|
|
rBS.deltaUV.resize (0);
|
|
|
|
if (bIsDeltaCol)
|
|
rBS.deltaCol.resize (nNbVertUsed);
|
|
else
|
|
rBS.deltaCol.resize (0);
|
|
|
|
if (bIsDeltaTgSpace)
|
|
rBS.deltaTgSpace.resize (nNbVertUsed);
|
|
else
|
|
rBS.deltaTgSpace.resize (0);
|
|
|
|
|
|
rBS.VertRefs.resize (nNbVertUsed);
|
|
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CMRMBuilder::buildMeshBuildMrm(const CMRMMeshFinal &finalMRM, CMeshMRMSkinnedGeom::CMeshBuildMRM &mbuild, uint32 vbFlags, uint32 nbMats, const CMesh::CMeshBuild &mb)
|
|
{
|
|
sint i,j,k;
|
|
sint attId;
|
|
|
|
// reset the mbuild.
|
|
mbuild= CMeshMRMSkinnedGeom::CMeshBuildMRM();
|
|
// Setup VB.
|
|
|
|
bool useFormatExt = false;
|
|
// Check whether there are texture coordinates with more than 2 compnents, which force us to use an extended vertex format
|
|
for (k = 0; k < CVertexBuffer::MaxStage; ++k)
|
|
{
|
|
if (
|
|
(vbFlags & (CVertexBuffer::TexCoord0Flag << k))
|
|
&& mb.NumCoords[k] != 2)
|
|
{
|
|
useFormatExt = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint numTexCoordUsed = 0;
|
|
|
|
|
|
for (k = 0; k < CVertexBuffer::MaxStage; ++k)
|
|
{
|
|
if (vbFlags & (CVertexBuffer::TexCoord0Flag << k))
|
|
{
|
|
numTexCoordUsed = k;
|
|
}
|
|
}
|
|
|
|
if (!useFormatExt)
|
|
{
|
|
// setup standard format
|
|
mbuild.VBuffer.setVertexFormat(vbFlags);
|
|
}
|
|
else // setup extended format
|
|
{
|
|
mbuild.VBuffer.clearValueEx();
|
|
if (vbFlags & CVertexBuffer::PositionFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::Position, CVertexBuffer::Float3);
|
|
if (vbFlags & CVertexBuffer::NormalFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::Normal, CVertexBuffer::Float3);
|
|
if (vbFlags & CVertexBuffer::PrimaryColorFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::PrimaryColor, CVertexBuffer::UChar4);
|
|
if (vbFlags & CVertexBuffer::SecondaryColorFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::SecondaryColor, CVertexBuffer::UChar4);
|
|
if (vbFlags & CVertexBuffer::WeightFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::Weight, CVertexBuffer::Float4);
|
|
if (vbFlags & CVertexBuffer::PaletteSkinFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::PaletteSkin, CVertexBuffer::UChar4);
|
|
if (vbFlags & CVertexBuffer::FogFlag) mbuild.VBuffer.addValueEx(CVertexBuffer::Fog, CVertexBuffer::Float1);
|
|
|
|
for (k = 0; k < CVertexBuffer::MaxStage; ++k)
|
|
{
|
|
if (vbFlags & (CVertexBuffer::TexCoord0Flag << k))
|
|
{
|
|
switch(mb.NumCoords[k])
|
|
{
|
|
case 2:
|
|
mbuild.VBuffer.addValueEx((CVertexBuffer::TValue) (CVertexBuffer::TexCoord0 + k), CVertexBuffer::Float2);
|
|
break;
|
|
case 3:
|
|
mbuild.VBuffer.addValueEx((CVertexBuffer::TValue) (CVertexBuffer::TexCoord0 + k), CVertexBuffer::Float3);
|
|
break;
|
|
default:
|
|
nlassert(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
mbuild.VBuffer.initEx();
|
|
}
|
|
|
|
// Copy the UVRouting
|
|
for (i=0; i<CVertexBuffer::MaxStage; i++)
|
|
{
|
|
mbuild.VBuffer.setUVRouting (i, mb.UVRouting[i]);
|
|
}
|
|
|
|
// Setup the VertexBuffer.
|
|
// ========================
|
|
// resize the VB.
|
|
mbuild.VBuffer.setNumVertices((uint32)finalMRM.Wedges.size());
|
|
|
|
CVertexBufferReadWrite vba;
|
|
mbuild.VBuffer.lock (vba);
|
|
|
|
// Setup SkinWeights.
|
|
if(_Skinned)
|
|
mbuild.SkinWeights.resize(finalMRM.Wedges.size());
|
|
|
|
// fill the VB.
|
|
for(i=0; i<(sint)finalMRM.Wedges.size(); i++)
|
|
{
|
|
const CMRMMeshFinal::CWedge &wedge= finalMRM.Wedges[i];
|
|
|
|
// setup Vertex.
|
|
vba.setVertexCoord(i, wedge.Vertex);
|
|
|
|
// seutp attributes.
|
|
attId= 0;
|
|
|
|
// For all activated attributes in mbuild, retrieve the attribute from the finalMRM.
|
|
if(vbFlags & CVertexBuffer::NormalFlag)
|
|
{
|
|
vba.setNormalCoord(i, wedge.Attributes[attId] );
|
|
attId++;
|
|
}
|
|
if(vbFlags & CVertexBuffer::PrimaryColorFlag)
|
|
{
|
|
vba.setColor(i, attToColor(wedge.Attributes[attId]) );
|
|
attId++;
|
|
}
|
|
if(vbFlags & CVertexBuffer::SecondaryColorFlag)
|
|
{
|
|
vba.setSpecular(i, attToColor(wedge.Attributes[attId]) );
|
|
attId++;
|
|
}
|
|
for(k=0; k<CVertexBuffer::MaxStage;k++)
|
|
{
|
|
if(vbFlags & (CVertexBuffer::TexCoord0Flag<<k))
|
|
{
|
|
switch(mb.NumCoords[k])
|
|
{
|
|
case 2:
|
|
vba.setTexCoord(i, k, (CUV) attToUvw(wedge.Attributes[attId]) );
|
|
break;
|
|
case 3:
|
|
{
|
|
CUVW uvw = attToUvw(wedge.Attributes[attId]);
|
|
vba.setValueFloat3Ex((CVertexBuffer::TValue) (CVertexBuffer::TexCoord0 + k), i, uvw.U, uvw.V, uvw.W);
|
|
}
|
|
break;
|
|
default:
|
|
nlassert(0);
|
|
break;
|
|
}
|
|
attId++;
|
|
}
|
|
}
|
|
|
|
// Setup SkinWeights.
|
|
if(_Skinned)
|
|
{
|
|
mbuild.SkinWeights[i]= wedge.VertexSkin;
|
|
}
|
|
}
|
|
|
|
|
|
// Build Lods.
|
|
// ========================
|
|
// resize
|
|
mbuild.Lods.resize(finalMRM.Lods.size());
|
|
// fill.
|
|
for(i=0; i<(sint)finalMRM.Lods.size(); i++)
|
|
{
|
|
const CMRMMeshFinal::CLod &srcLod= finalMRM.Lods[i];
|
|
CMeshMRMSkinnedGeom::CLod &destLod= mbuild.Lods[i];
|
|
|
|
// Basic.
|
|
//---------
|
|
|
|
// Copy NWedges infos.
|
|
destLod.NWedges= srcLod.NWedges;
|
|
// Copy Geomorphs infos.
|
|
destLod.Geomorphs= srcLod.Geomorphs;
|
|
|
|
|
|
// Reorder faces by rdrpass.
|
|
//---------
|
|
|
|
// First count the number of faces used by this LOD for each material
|
|
vector<sint> matCount;
|
|
// resize, and reset to 0.
|
|
matCount.clear();
|
|
matCount.resize(nbMats, 0);
|
|
// For each face of this Lods, incr the mat face counter.
|
|
for(j= 0; j<(sint)srcLod.Faces.size(); j++)
|
|
{
|
|
sint matId= srcLod.Faces[j].MaterialId;
|
|
nlassert(matId>=0);
|
|
nlassert(matId<(sint)nbMats);
|
|
// increment the refcount of this material by this LOD.
|
|
matCount[matId]++;
|
|
}
|
|
|
|
// Then for each material not empty, create a rdrPass, and ref it for this material.
|
|
vector<sint> rdrPassIndex; // material to rdrPass map.
|
|
rdrPassIndex.resize(nbMats);
|
|
for(j=0; j<(sint)nbMats; j++)
|
|
{
|
|
if(matCount[j]==0)
|
|
rdrPassIndex[j]= -1;
|
|
else
|
|
{
|
|
// map material to rdrPass.
|
|
sint idRdrPass= (sint)destLod.RdrPass.size();
|
|
rdrPassIndex[j]= idRdrPass;
|
|
// create a rdrPass.
|
|
destLod.RdrPass.push_back(CMeshMRMSkinnedGeom::CRdrPass());
|
|
// assign the good materialId to this rdrPass.
|
|
destLod.RdrPass[idRdrPass].MaterialId= j;
|
|
// reserve the array of faces of this rdrPass.
|
|
destLod.RdrPass[idRdrPass].PBlock.reserve(3*matCount[j]);
|
|
}
|
|
}
|
|
|
|
// Then for each face, add it to the good rdrPass of this Lod.
|
|
for(j= 0; j<(sint)srcLod.Faces.size(); j++)
|
|
{
|
|
sint matId= srcLod.Faces[j].MaterialId;
|
|
sint idRdrPass= rdrPassIndex[matId];
|
|
// add this face to the good rdrPass.
|
|
sint w0= srcLod.Faces[j].WedgeId[0];
|
|
sint w1= srcLod.Faces[j].WedgeId[1];
|
|
sint w2= srcLod.Faces[j].WedgeId[2];
|
|
destLod.RdrPass[idRdrPass].PBlock.push_back (w0);
|
|
destLod.RdrPass[idRdrPass].PBlock.push_back (w1);
|
|
destLod.RdrPass[idRdrPass].PBlock.push_back (w2);
|
|
}
|
|
|
|
|
|
// Build skin info for this Lod.
|
|
//---------
|
|
for(j=0; j<NL3D_MESH_SKINNING_MAX_MATRIX; j++)
|
|
{
|
|
destLod.InfluencedVertices[j].clear();
|
|
}
|
|
destLod.MatrixInfluences.clear();
|
|
if(_Skinned)
|
|
{
|
|
// This is the set which tell what wedge has already been inserted.
|
|
set<uint> wedgeInfSet;
|
|
|
|
// First, build the list of vertices influenced by this Lod.
|
|
for(j= 0; j<(sint)srcLod.Faces.size(); j++)
|
|
{
|
|
for(k=0; k<3; k++)
|
|
{
|
|
sint wedgeId= srcLod.Faces[j].WedgeId[k];
|
|
// If it is a geomorph
|
|
if(wedgeId<finalMRM.NGeomSpace)
|
|
{
|
|
// add the start and end to the list (if not here). NB: wedgeId is both the id
|
|
// of the dest wedge, and the id of the geomorph.
|
|
sint wedgeStartId= destLod.Geomorphs[wedgeId].Start;
|
|
sint wedgeEndId= destLod.Geomorphs[wedgeId].End;
|
|
uint nMatUsedStart= finalMRM.Wedges[wedgeStartId].NSkinMatUsed;
|
|
uint nMatUsedEnd= finalMRM.Wedges[wedgeEndId].NSkinMatUsed;
|
|
|
|
// if insertion in the set work, add to the good array.
|
|
if( wedgeInfSet.insert(wedgeStartId).second )
|
|
destLod.InfluencedVertices[nMatUsedStart-1].push_back(wedgeStartId);
|
|
if( wedgeInfSet.insert(wedgeEndId).second )
|
|
destLod.InfluencedVertices[nMatUsedEnd-1].push_back(wedgeEndId);
|
|
}
|
|
else
|
|
{
|
|
uint nMatUsed= finalMRM.Wedges[wedgeId].NSkinMatUsed;
|
|
|
|
// just add this wedge to the list (if not here).
|
|
// if insertion in the set work, add to the array.
|
|
if( wedgeInfSet.insert(wedgeId).second )
|
|
destLod.InfluencedVertices[nMatUsed-1].push_back(wedgeId);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Optimisation: for better cache, sort the destLod.InfluencedVertices in increasing order.
|
|
for(j=0; j<NL3D_MESH_SKINNING_MAX_MATRIX; j++)
|
|
{
|
|
sort(destLod.InfluencedVertices[j].begin(), destLod.InfluencedVertices[j].end());
|
|
}
|
|
|
|
|
|
// Then Build the MatrixInfluences array, for all thoses Influenced Vertices only.
|
|
// This is the map MatrixId -> MatrixInfId.
|
|
map<uint, uint> matrixInfMap;
|
|
|
|
// For all influenced vertices, flags matrix they use.
|
|
uint iSkinMat;
|
|
for(iSkinMat= 0; iSkinMat<NL3D_MESH_SKINNING_MAX_MATRIX; iSkinMat++)
|
|
{
|
|
for(j= 0; j<(sint)destLod.InfluencedVertices[iSkinMat].size(); j++)
|
|
{
|
|
uint wedgeId= destLod.InfluencedVertices[iSkinMat][j];
|
|
|
|
// take the original wedge.
|
|
const CMRMMeshFinal::CWedge &wedge= finalMRM.Wedges[wedgeId];
|
|
// For all matrix with not null influence...
|
|
for(k= 0; k<NL3D_MESH_SKINNING_MAX_MATRIX; k++)
|
|
{
|
|
float matWeight= wedge.VertexSkin.Weights[k];
|
|
|
|
// This check the validity of skin weights sort. If false, problem before in the algo.
|
|
if((uint)k<iSkinMat+1)
|
|
{
|
|
nlassert( matWeight>0 );
|
|
}
|
|
else
|
|
{
|
|
nlassert( matWeight==0 );
|
|
}
|
|
// if not null influence.
|
|
if(matWeight>0)
|
|
{
|
|
uint matId= wedge.VertexSkin.MatrixId[k];
|
|
|
|
// search/insert the matrixInfId.
|
|
map<uint, uint>::iterator it= matrixInfMap.find(matId);
|
|
if( it==matrixInfMap.end() )
|
|
{
|
|
uint matInfId= (uint)destLod.MatrixInfluences.size();
|
|
matrixInfMap.insert( make_pair(matId, matInfId) );
|
|
// create the new MatrixInfluence.
|
|
destLod.MatrixInfluences.push_back(matId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Indicate Skinning.
|
|
mbuild.Skinned= _Skinned;
|
|
|
|
|
|
|
|
bool useTgSpace = mb.MeshVertexProgram != NULL ? mb.MeshVertexProgram->needTangentSpace() : false;
|
|
|
|
// Construct Blend Shapes
|
|
//// mbuild <- finalMRM
|
|
mbuild.BlendShapes.resize (finalMRM.MRMBlendShapesFinals.size());
|
|
for (k = 0; k < (sint)mbuild.BlendShapes.size(); ++k)
|
|
{
|
|
CBlendShape &rBS = mbuild.BlendShapes[k];
|
|
sint32 nNbVertVB = (sint32)finalMRM.Wedges.size();
|
|
bool bIsDeltaPos = false;
|
|
rBS.deltaPos.resize (nNbVertVB, CVector(0.0f,0.0f,0.0f));
|
|
bool bIsDeltaNorm = false;
|
|
rBS.deltaNorm.resize (nNbVertVB, CVector(0.0f,0.0f,0.0f));
|
|
bool bIsDeltaUV = false;
|
|
rBS.deltaUV.resize (nNbVertVB, CUV(0.0f,0.0f));
|
|
bool bIsDeltaCol = false;
|
|
rBS.deltaCol.resize (nNbVertVB, CRGBAF(0.0f,0.0f,0.0f,0.0f));
|
|
bool bIsDeltaTgSpace = false;
|
|
if (useTgSpace)
|
|
{
|
|
rBS.deltaTgSpace.resize(nNbVertVB, CVector::Null);
|
|
}
|
|
|
|
rBS.VertRefs.resize (nNbVertVB, 0xffffffff);
|
|
|
|
for (i = 0; i < nNbVertVB; i++)
|
|
{
|
|
const CMRMMeshFinal::CWedge &rWedgeRef = finalMRM.Wedges[i];
|
|
const CMRMMeshFinal::CWedge &rWedgeTar = finalMRM.MRMBlendShapesFinals[k].Wedges[i];
|
|
|
|
CVector delta = rWedgeTar.Vertex - rWedgeRef.Vertex;
|
|
CVectorH attr;
|
|
|
|
if (delta.norm() > 0.001f)
|
|
{
|
|
rBS.deltaPos[i] = delta;
|
|
rBS.VertRefs[i] = i;
|
|
bIsDeltaPos = true;
|
|
}
|
|
|
|
attId = 0;
|
|
if (vbFlags & CVertexBuffer::NormalFlag)
|
|
{
|
|
attr = rWedgeRef.Attributes[attId];
|
|
CVector NormRef = CVector(attr.x, attr.y, attr.z);
|
|
attr = rWedgeTar.Attributes[attId];
|
|
CVector NormTar = CVector(attr.x, attr.y, attr.z);
|
|
delta = NormTar - NormRef;
|
|
if (delta.norm() > 0.001f)
|
|
{
|
|
rBS.deltaNorm[i] = delta;
|
|
rBS.VertRefs[i] = i;
|
|
bIsDeltaNorm = true;
|
|
}
|
|
attId++;
|
|
}
|
|
|
|
if (vbFlags & CVertexBuffer::PrimaryColorFlag)
|
|
{
|
|
attr = rWedgeRef.Attributes[attId];
|
|
CRGBAF RGBARef = CRGBAF(attr.x/255.0f, attr.y/255.0f, attr.z/255.0f, attr.w/255.0f);
|
|
attr = rWedgeTar.Attributes[attId];
|
|
CRGBAF RGBATar = CRGBAF(attr.x/255.0f, attr.y/255.0f, attr.z/255.0f, attr.w/255.0f);
|
|
CRGBAF deltaRGBA = RGBATar - RGBARef;
|
|
if ((deltaRGBA.R*deltaRGBA.R + deltaRGBA.G*deltaRGBA.G +
|
|
deltaRGBA.B*deltaRGBA.B + deltaRGBA.A*deltaRGBA.A) > 0.0001f)
|
|
{
|
|
rBS.deltaCol[i] = deltaRGBA;
|
|
rBS.VertRefs[i] = i;
|
|
bIsDeltaCol = true;
|
|
}
|
|
attId++;
|
|
}
|
|
|
|
if (vbFlags & CVertexBuffer::SecondaryColorFlag)
|
|
{ // Nothing to do !
|
|
attId++;
|
|
}
|
|
|
|
// Do that only for the UV0
|
|
if (vbFlags & CVertexBuffer::TexCoord0Flag)
|
|
{
|
|
attr = rWedgeRef.Attributes[attId];
|
|
CUV UVRef = CUV(attr.x, attr.y);
|
|
attr = rWedgeTar.Attributes[attId];
|
|
CUV UVTar = CUV(attr.x, attr.y);
|
|
CUV deltaUV = UVTar - UVRef;
|
|
if ((deltaUV.U*deltaUV.U + deltaUV.V*deltaUV.V) > 0.0001f)
|
|
{
|
|
rBS.deltaUV[i] = deltaUV;
|
|
rBS.VertRefs[i] = i;
|
|
bIsDeltaUV = true;
|
|
}
|
|
attId++;
|
|
}
|
|
|
|
if (useTgSpace)
|
|
{
|
|
attr = rWedgeRef.Attributes[attId];
|
|
CVector TgSpaceRef = CVector(attr.x, attr.y, attr.z);
|
|
attr = rWedgeTar.Attributes[attId];
|
|
CVector TgSpaceTar = CVector(attr.x, attr.y, attr.z);
|
|
delta = TgSpaceTar - TgSpaceRef;
|
|
if (delta.norm() > 0.001f)
|
|
{
|
|
rBS.deltaTgSpace[i] = delta;
|
|
rBS.VertRefs[i] = i;
|
|
bIsDeltaTgSpace = true;
|
|
}
|
|
attId++;
|
|
}
|
|
|
|
} // End of all vertices added in blend shape
|
|
|
|
// Delete unused items and calculate the number of vertex used (blended)
|
|
|
|
sint32 nNbVertUsed = nNbVertVB;
|
|
sint32 nDstPos = 0;
|
|
for (j = 0; j < nNbVertVB; ++j)
|
|
{
|
|
if (rBS.VertRefs[j] == 0xffffffff) // Is vertex UNused
|
|
{
|
|
--nNbVertUsed;
|
|
}
|
|
else // Vertex used
|
|
{
|
|
if (nDstPos != j)
|
|
{
|
|
rBS.VertRefs[nDstPos] = rBS.VertRefs[j];
|
|
rBS.deltaPos[nDstPos] = rBS.deltaPos[j];
|
|
rBS.deltaNorm[nDstPos] = rBS.deltaNorm[j];
|
|
rBS.deltaUV[nDstPos] = rBS.deltaUV[j];
|
|
rBS.deltaCol[nDstPos] = rBS.deltaCol[j];
|
|
if (useTgSpace)
|
|
{
|
|
rBS.deltaTgSpace[nDstPos] = rBS.deltaTgSpace[j];
|
|
}
|
|
}
|
|
++nDstPos;
|
|
}
|
|
}
|
|
|
|
if (bIsDeltaPos)
|
|
rBS.deltaPos.resize (nNbVertUsed);
|
|
else
|
|
rBS.deltaPos.resize (0);
|
|
|
|
if (bIsDeltaNorm)
|
|
rBS.deltaNorm.resize (nNbVertUsed);
|
|
else
|
|
rBS.deltaNorm.resize (0);
|
|
|
|
if (bIsDeltaUV)
|
|
rBS.deltaUV.resize (nNbVertUsed);
|
|
else
|
|
rBS.deltaUV.resize (0);
|
|
|
|
if (bIsDeltaCol)
|
|
rBS.deltaCol.resize (nNbVertUsed);
|
|
else
|
|
rBS.deltaCol.resize (0);
|
|
|
|
if (bIsDeltaTgSpace)
|
|
rBS.deltaTgSpace.resize (nNbVertUsed);
|
|
else
|
|
rBS.deltaTgSpace.resize (0);
|
|
|
|
|
|
rBS.VertRefs.resize (nNbVertUsed);
|
|
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CMRMBuilder::buildBlendShapes (CMRMMesh& baseMesh,
|
|
std::vector<CMesh::CMeshBuild*> &bsList, uint32 VertexFlags)
|
|
{
|
|
uint32 i, j, k, m, destIndex;
|
|
uint32 attId;
|
|
CVectorH vh;
|
|
vector<CMRMBlendShape> &bsMeshes= baseMesh.BlendShapes;
|
|
|
|
bsMeshes.resize (bsList.size());
|
|
|
|
for (i = 0; i < bsList.size(); ++i)
|
|
{
|
|
// Construct a blend shape like a mrm mesh
|
|
nlassert (baseMesh.Vertices.size() == bsList[i]->Vertices.size());
|
|
bsMeshes[i].Vertices.resize (baseMesh.Vertices.size());
|
|
bsMeshes[i].Vertices = bsList[i]->Vertices;
|
|
|
|
bsMeshes[i].NumAttributes = baseMesh.NumAttributes;
|
|
for (j = 0; j < (uint32)bsMeshes[i].NumAttributes; ++j)
|
|
bsMeshes[i].Attributes[j].resize(baseMesh.Attributes[j].size());
|
|
|
|
// For all corners parse the faces (given by the baseMesh) and construct blend shape mrm meshes
|
|
for (j = 0; j < baseMesh.Faces.size(); ++j)
|
|
for (k = 0; k < 3; ++k)
|
|
{
|
|
const CMesh::CCorner &srcCorner = bsList[i]->Faces[j].Corner[k];
|
|
CMRMCorner &neutralCorner = baseMesh.Faces[j].Corner[k];
|
|
|
|
attId= 0;
|
|
|
|
if (VertexFlags & CVertexBuffer::NormalFlag)
|
|
{
|
|
destIndex = neutralCorner.Attributes[attId];
|
|
vh.x = srcCorner.Normal.x;
|
|
vh.y = srcCorner.Normal.y;
|
|
vh.z = srcCorner.Normal.z;
|
|
vh.w = 0.0f;
|
|
bsMeshes[i].Attributes[attId].operator[](destIndex) = vh;
|
|
attId++;
|
|
}
|
|
if (VertexFlags & CVertexBuffer::PrimaryColorFlag)
|
|
{
|
|
destIndex = neutralCorner.Attributes[attId];
|
|
vh.x = srcCorner.Color.R;
|
|
vh.y = srcCorner.Color.G;
|
|
vh.z = srcCorner.Color.B;
|
|
vh.w = srcCorner.Color.A;
|
|
bsMeshes[i].Attributes[attId].operator[](destIndex) = vh;
|
|
attId++;
|
|
}
|
|
if (VertexFlags & CVertexBuffer::SecondaryColorFlag)
|
|
{
|
|
destIndex = neutralCorner.Attributes[attId];
|
|
vh.x = srcCorner.Specular.R;
|
|
vh.y = srcCorner.Specular.G;
|
|
vh.z = srcCorner.Specular.B;
|
|
vh.w = srcCorner.Specular.A;
|
|
bsMeshes[i].Attributes[attId].operator[](destIndex) = vh;
|
|
attId++;
|
|
}
|
|
for (m = 0; m < CVertexBuffer::MaxStage; ++m)
|
|
{
|
|
if (VertexFlags & (CVertexBuffer::TexCoord0Flag<<m))
|
|
{
|
|
destIndex = neutralCorner.Attributes[attId];
|
|
vh.x = srcCorner.Uvws[m].U;
|
|
vh.y = srcCorner.Uvws[m].V;
|
|
vh.z = srcCorner.Uvws[m].W;
|
|
vh.w = 0.0f;
|
|
bsMeshes[i].Attributes[attId].operator[](destIndex) = vh;
|
|
attId++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMRMBuilder::compileMRM(const CMesh::CMeshBuild &mbuild, std::vector<CMesh::CMeshBuild*> &bsList,
|
|
const CMRMParameters ¶ms, CMeshMRMGeom::CMeshBuildMRM &mrmMesh,
|
|
uint numMaxMaterial)
|
|
{
|
|
// Temp data.
|
|
CMRMMesh baseMesh;
|
|
vector<CMRMMeshGeom> lodMeshs;
|
|
CMRMMeshFinal finalMRM;
|
|
vector<CMRMMeshFinal> finalBsMRM;
|
|
uint32 vbFlags;
|
|
|
|
|
|
nlassert(params.DistanceFinest>=0);
|
|
nlassert(params.DistanceMiddle > params.DistanceFinest);
|
|
nlassert(params.DistanceCoarsest > params.DistanceMiddle);
|
|
|
|
|
|
// Copy some parameters.
|
|
_SkinReduction= params.SkinReduction;
|
|
|
|
// Skinning??
|
|
_Skinned= ((mbuild.VertexFlags & CVertexBuffer::PaletteSkinFlag)==CVertexBuffer::PaletteSkinFlag);
|
|
// Skinning is OK only if SkinWeights are of same size as vertices.
|
|
_Skinned= _Skinned && ( mbuild.Vertices.size()==mbuild.SkinWeights.size() );
|
|
|
|
// MeshInterface setuped ?
|
|
_HasMeshInterfaces= buildMRMSewingMeshes(mbuild, params.NLods, params.Divisor);
|
|
|
|
// from mbuild, build an internal MRM mesh representation.
|
|
// vbFlags returned is the VBuffer format supported by CMRMBuilder.
|
|
// NB: skinning is removed because skinning is made in software in CMeshMRMGeom.
|
|
vbFlags= buildMrmBaseMesh(mbuild, baseMesh);
|
|
|
|
// Construct all blend shapes in the same way we have constructed the basemesh mrm
|
|
buildBlendShapes (baseMesh, bsList, vbFlags);
|
|
|
|
// If skinned, must ensure that skin weights have weights in ascending order.
|
|
if(_Skinned)
|
|
{
|
|
normalizeBaseMeshSkin(baseMesh);
|
|
}
|
|
|
|
// from this baseMesh, builds all LODs of the MRM, with geomorph info. NB: vertices/wedges are duplicated.
|
|
buildAllLods ( baseMesh, lodMeshs, params.NLods, params.Divisor );
|
|
|
|
// From this array of LOD, build a finalMRM, by regrouping identical vertices/wedges, and compute index geomorphs.
|
|
buildFinalMRM(lodMeshs, finalMRM);
|
|
|
|
// From this finalMRM, build output: a CMeshBuildMRM.
|
|
buildMeshBuildMrm(finalMRM, mrmMesh, vbFlags, numMaxMaterial, mbuild);
|
|
|
|
// Copy degradation control params.
|
|
mrmMesh.DistanceFinest= params.DistanceFinest;
|
|
mrmMesh.DistanceMiddle= params.DistanceMiddle;
|
|
mrmMesh.DistanceCoarsest= params.DistanceCoarsest;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMRMBuilder::compileMRM(const CMesh::CMeshBuild &mbuild, std::vector<CMesh::CMeshBuild*> &bsList,
|
|
const CMRMParameters ¶ms, CMeshMRMSkinnedGeom::CMeshBuildMRM &mrmMesh,
|
|
uint numMaxMaterial)
|
|
{
|
|
// Temp data.
|
|
CMRMMesh baseMesh;
|
|
vector<CMRMMeshGeom> lodMeshs;
|
|
CMRMMeshFinal finalMRM;
|
|
vector<CMRMMeshFinal> finalBsMRM;
|
|
uint32 vbFlags;
|
|
|
|
|
|
nlassert(params.DistanceFinest>=0);
|
|
nlassert(params.DistanceMiddle > params.DistanceFinest);
|
|
nlassert(params.DistanceCoarsest > params.DistanceMiddle);
|
|
|
|
|
|
// Copy some parameters.
|
|
_SkinReduction= params.SkinReduction;
|
|
|
|
// Skinning??
|
|
_Skinned= ((mbuild.VertexFlags & CVertexBuffer::PaletteSkinFlag)==CVertexBuffer::PaletteSkinFlag);
|
|
// Skinning is OK only if SkinWeights are of same size as vertices.
|
|
_Skinned= _Skinned && ( mbuild.Vertices.size()==mbuild.SkinWeights.size() );
|
|
|
|
// MeshInterface setuped ?
|
|
_HasMeshInterfaces= buildMRMSewingMeshes(mbuild, params.NLods, params.Divisor);
|
|
|
|
// from mbuild, build an internal MRM mesh representation.
|
|
// vbFlags returned is the VBuffer format supported by CMRMBuilder.
|
|
// NB: skinning is removed because skinning is made in software in CMeshMRMGeom.
|
|
vbFlags= buildMrmBaseMesh(mbuild, baseMesh);
|
|
|
|
// Construct all blend shapes in the same way we have constructed the basemesh mrm
|
|
buildBlendShapes (baseMesh, bsList, vbFlags);
|
|
|
|
// If skinned, must ensure that skin weights have weights in ascending order.
|
|
if(_Skinned)
|
|
{
|
|
normalizeBaseMeshSkin(baseMesh);
|
|
}
|
|
|
|
// from this baseMesh, builds all LODs of the MRM, with geomorph info. NB: vertices/wedges are duplicated.
|
|
buildAllLods ( baseMesh, lodMeshs, params.NLods, params.Divisor );
|
|
|
|
// From this array of LOD, build a finalMRM, by regrouping identical vertices/wedges, and compute index geomorphs.
|
|
buildFinalMRM(lodMeshs, finalMRM);
|
|
|
|
// From this finalMRM, build output: a CMeshBuildMRM.
|
|
buildMeshBuildMrm(finalMRM, mrmMesh, vbFlags, numMaxMaterial, mbuild);
|
|
|
|
// Copy degradation control params.
|
|
mrmMesh.DistanceFinest= params.DistanceFinest;
|
|
mrmMesh.DistanceMiddle= params.DistanceMiddle;
|
|
mrmMesh.DistanceCoarsest= params.DistanceCoarsest;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// MRM Interface system
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
// ***************************************************************************
|
|
bool CMRMBuilder::buildMRMSewingMeshes(const CMesh::CMeshBuild &mbuild, uint nWantedLods, uint divisor)
|
|
{
|
|
nlassert(nWantedLods>=1);
|
|
nlassert(divisor>=1);
|
|
if(mbuild.Interfaces.size()==0)
|
|
return false;
|
|
// must have same size
|
|
if(mbuild.InterfaceLinks.size()!=mbuild.Vertices.size())
|
|
return false;
|
|
|
|
// **** For each interface, MRM-ize it and store.
|
|
_SewingMeshes.resize(mbuild.Interfaces.size());
|
|
for(uint i=0;i<mbuild.Interfaces.size();i++)
|
|
{
|
|
_SewingMeshes[i].build(mbuild.Interfaces[i], nWantedLods, divisor);
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
} // NL3D
|