1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "nel/misc/path.h"
18 #include "nel/3d/mesh_mrm_skinned.h"
19 #include "nel/3d/mesh_mrm.h"
20 #include "nel/3d/mesh.h"
21 #include "nel/misc/file.h"
22 #include "nel/misc/path.h"
23 #include "nel/3d/stripifier.h"
24 #include "nel/3d/register_3d.h"
27 using namespace NLMISC
;
32 // ***************************************************************************
34 const CIndexBuffer
*getRdrPassPrimitiveBlock(const CMeshMRMGeom
*mesh
, uint lodId
, uint renderPass
)
36 return &(mesh
->getRdrPassPrimitiveBlock(lodId
, renderPass
));
39 // ***************************************************************************
41 const CIndexBuffer
*getRdrPassPrimitiveBlock(const CMeshMRMSkinnedGeom
*mesh
, uint lodId
, uint renderPass
)
43 static CIndexBuffer block
;
44 mesh
->getRdrPassPrimitiveBlock(lodId
, renderPass
, block
);
48 // ***************************************************************************
51 void addShadowMesh(T
*meshIn
, float paramFaceRatio
, sint paramMaxFace
, const std::vector
<CMesh::CSkinWeight
> &skinWeights
, const CVertexBuffer
&vertexBuffer
)
53 CVertexBufferRead vba
;
54 vertexBuffer
.lock (vba
);
57 // **** Select the Lod.
58 uint numLods
= meshIn
->getNbLod();
60 // get the max tris displayed
61 float numMeshFacesMin
= (float)meshIn
->getLevelDetail().MinFaceUsed
;
62 float numMeshFacesMax
= (float)meshIn
->getLevelDetail().MaxFaceUsed
;
63 float maxFaceWanted
= numMeshFacesMax
* paramFaceRatio
;
64 // minimize with maxFace param
65 maxFaceWanted
= min(maxFaceWanted
, (float)paramMaxFace
);
67 float fLod
= ( (maxFaceWanted
-numMeshFacesMin
) / (numMeshFacesMax
-numMeshFacesMin
) )*(numLods
-1);
68 // round the lod wanted.
69 sint lodId
= (sint
)floor(0.5f
+ fLod
);
70 clamp(lodId
, 0, (sint
)numLods
-1);
73 // **** First, for the best lod indicate what vertex is used or not. Also index geomorphs to know what real vertex is used
74 vector
<sint
> vertexUsed
;
75 // -1 means "not used"
76 vertexUsed
.resize(skinWeights
.size(), -1);
77 // Parse all triangles.
78 for(i
=0;i
<meshIn
->getNbRdrPass(lodId
);i
++)
80 const CIndexBuffer
*pb
= getRdrPassPrimitiveBlock(meshIn
, lodId
, i
);
83 if (iba
.getFormat() == CIndexBuffer::Indices32
)
85 const uint32
*triPtr
= (const uint32
*) iba
.getPtr();
86 for(j
=0;j
<pb
->getNumIndexes();j
++)
89 // Flag the vertex with its own index => used.
96 const uint16
*triPtr
= (const uint16
*) iba
.getPtr();
97 for(j
=0;j
<pb
->getNumIndexes();j
++)
100 // Flag the vertex with its own index => used.
101 vertexUsed
[idx
]= idx
;
106 // Special for Geomorphs: must take The End target vertex.
107 const std::vector
<CMRMWedgeGeom
> &geomorphs
= meshIn
->getGeomorphs(lodId
);
108 for(i
=0;i
<geomorphs
.size();i
++)
110 uint trueIdx
= geomorphs
[i
].End
;
111 // map to the Geomorph Target.
112 vertexUsed
[i
]= trueIdx
;
113 // mark also the real vertex used as used.
114 vertexUsed
[trueIdx
]= trueIdx
;
118 // **** For all vertices used (not geomorphs), compute vertex Skins.
119 vector
<CShadowVertex
> shadowVertices
;
120 vector
<sint
> vertexToVSkin
;
121 vertexToVSkin
.resize(vertexUsed
.size());
122 shadowVertices
.reserve(vertexUsed
.size());
123 // use a map to remove duplicates (because of UV/normal discontinuities before!!)
124 map
<CShadowVertex
, uint
> shadowVertexMap
;
127 for(i
=(uint
)geomorphs
.size();i
<vertexUsed
.size();i
++)
129 // If this vertex is used.
130 if(vertexUsed
[i
]!=-1)
133 CShadowVertex shadowVert
;
134 shadowVert
.Vertex
= *(CVector
*)vba
.getVertexCoordPointer(i
);
135 // Select the best Matrix.
136 CMesh::CSkinWeight sw
= skinWeights
[i
];
139 for(j
=0;j
<NL3D_MESH_SKINNING_MAX_MATRIX
;j
++)
141 // if no more matrix influenced, stop
144 if(sw
.Weights
[j
]>maxW
)
146 matId
= sw
.MatrixId
[j
];
150 shadowVert
.MatrixId
= matId
;
152 // If dont find the shadowVertex in the map.
153 map
<CShadowVertex
, uint
>::iterator it
= shadowVertexMap
.find(shadowVert
);
154 if(it
==shadowVertexMap
.end())
157 uint index
= (uint
)shadowVertices
.size();
158 vertexToVSkin
[i
]= index
;
159 shadowVertices
.push_back(shadowVert
);
160 shadowVertexMap
.insert(make_pair(shadowVert
, index
));
165 vertexToVSkin
[i
]= it
->second
;
171 // Some Info on what have been merged.
172 if(shadowVertices
.size())
175 /*nlinfo("%d Vertices have been merged. => %d %%", numMerged,
176 100*numMerged / (numMerged+shadowVertices.size()));*/
180 // **** Get All Faces
181 // Final List Of Triangles that match the bone.
182 vector
<uint32
> shadowTriangles
;
183 shadowTriangles
.reserve(1000);
184 // Parse all input tri of the mesh.
185 for(i
=0;i
<meshIn
->getNbRdrPass(lodId
);i
++)
187 const CIndexBuffer
*pb
= getRdrPassPrimitiveBlock(meshIn
, lodId
, i
);
188 CIndexBufferRead iba
;
190 if (iba
.getFormat() == CIndexBuffer::Indices32
)
192 const uint32
*triPtr
= (const uint32
*) iba
.getPtr();
193 for(j
=0;j
<pb
->getNumIndexes();j
++)
196 // Get the real Vertex (ie not the geomporhed one).
197 idx
= vertexUsed
[idx
];
198 // Get the ShadowVertex associated
199 idx
= vertexToVSkin
[idx
];
201 shadowTriangles
.push_back(idx
);
207 const uint16
*triPtr
= (const uint16
*) iba
.getPtr();
208 for(j
=0;j
<pb
->getNumIndexes();j
++)
211 // Get the real Vertex (ie not the geomporhed one).
212 idx
= vertexUsed
[idx
];
213 // Get the ShadowVertex associated
214 idx
= vertexToVSkin
[idx
];
216 shadowTriangles
.push_back(idx
);
222 // Re-Optim VertexCache Hard usage
223 if(shadowTriangles
.size())
227 pb
.setNumIndexes((uint32
)shadowTriangles
.size());
229 CIndexBufferReadWrite iba
;
231 for(i
=0;i
<shadowTriangles
.size()/3;i
++)
233 iba
.setTri(i
*3, shadowTriangles
[i
*3 + 0],
234 shadowTriangles
[i
*3 + 1],
235 shadowTriangles
[i
*3 + 2]);
239 CStripifier stripifier
;
240 stripifier
.optimizeTriangles(pb
, pb
);
243 CIndexBufferReadWrite iba
;
245 if (iba
.getFormat() == CIndexBuffer::Indices32
)
247 const uint32
*triPtr
= (const uint32
*) iba
.getPtr();
248 for(i
=0;i
<shadowTriangles
.size();i
++)
250 shadowTriangles
[i
]= *triPtr
;
256 const uint16
*triPtr
= (const uint16
*) iba
.getPtr();
257 for(i
=0;i
<shadowTriangles
.size();i
++)
259 shadowTriangles
[i
]= *triPtr
;
267 meshIn
->setShadowMesh(shadowVertices
, shadowTriangles
);
270 // ***************************************************************************
272 void addShadowMeshIntro(CMeshMRMGeom
*meshIn
, float paramFaceRatio
, sint paramMaxFace
)
274 const std::vector
<CMesh::CSkinWeight
> &skinWeights
= meshIn
->getSkinWeights();
275 const CVertexBuffer
&vertexBuffer
= meshIn
->getVertexBuffer();
276 addShadowMesh (meshIn
, paramFaceRatio
, paramMaxFace
, skinWeights
, vertexBuffer
);
279 // ***************************************************************************
281 void addShadowMeshIntro(CMeshMRMSkinnedGeom
*meshIn
, float paramFaceRatio
, sint paramMaxFace
)
283 std::vector
<CMesh::CSkinWeight
> skinWeights
;
284 meshIn
->getSkinWeights(skinWeights
);
285 CVertexBuffer vertexBuffer
;
286 meshIn
->getVertexBuffer(vertexBuffer
);
287 addShadowMesh (meshIn
, paramFaceRatio
, paramMaxFace
, skinWeights
, vertexBuffer
);
290 // ***************************************************************************
292 int main(int argc
, char *argv
[])
294 // Filter addSearchPath
295 NLMISC::createDebug();
296 NLMISC::InfoLog
->addNegativeFilter ("adding the path");
298 NL3D::registerSerial3d();
302 string execName
= CFile::getFilename(argv
[0]);
303 printf("%s add a ShadowSkin to a Skinned MRM Mesh\n", execName
.c_str());
304 printf(" usage: %s shape_in shape_out [facePercentage] [maxFaces] \n", execName
.c_str());
305 printf(" NB: shape_in and shape_out can be same file\n");
306 printf(" NB: facePercentage is a number between 0 and 100. It computes the maxFaces for the shadow according to the input Mesh.\n");
307 printf(" NB: maxFaces can be given manually. The min of the 2 parameters is taken.\n");
312 string shapeNameIn
= argv
[1];
313 string shapeNameOut
= argv
[2];
314 sint percentageFace
= 100;
315 sint maxFace
= INT_MAX
;
317 NLMISC::fromString(argv
[3], percentageFace
);
319 NLMISC::fromString(argv
[4], maxFace
);
321 // **** Load the Mesh In.
322 IShape
*pResult
= NULL
;
324 CShapeStream shapeStreamIn
;
326 if(!shapeFileIn
.open(shapeNameIn
))
328 nlwarning("ERROR: cannot open input file: %s", shapeNameIn
.c_str());
331 shapeStreamIn
.serial(shapeFileIn
);
332 pMesh
= dynamic_cast<CMeshMRM
*>(shapeStreamIn
.getShapePointer());
336 CMeshMRMSkinned
*pMeshSkinned
= dynamic_cast<CMeshMRMSkinned
*>(shapeStreamIn
.getShapePointer());
339 nlwarning("ERROR: Not a MRM Mesh nor a MRM Mesh skinned: %s", shapeNameIn
.c_str());
343 CMeshMRMSkinnedGeom
*pMeshGeom
= (CMeshMRMSkinnedGeom
*)&pMeshSkinned
->getMeshGeom();
345 // **** Add Shadow mesh.
347 clamp(percentageFace
, 0, 100);
348 faceRatio
= (float)percentageFace
/100;
349 maxFace
= max(0, maxFace
);
350 addShadowMeshIntro(pMeshGeom
, faceRatio
, maxFace
);
351 pResult
= pMeshSkinned
;
355 CMeshMRMGeom
*pMeshGeom
= (CMeshMRMGeom
*)&pMesh
->getMeshGeom();
356 if( !pMeshGeom
->isSkinned() )
358 nlwarning("ERROR: Not a Skinned MRM Mesh: %s", shapeNameIn
.c_str());
362 // **** Add Shadow mesh.
364 clamp(percentageFace
, 0, 100);
365 faceRatio
= (float)percentageFace
/100;
366 maxFace
= max(0, maxFace
);
367 addShadowMeshIntro(pMeshGeom
, faceRatio
, maxFace
);
372 CShapeStream shapeStreamOut
;
374 if(!shapeFileOut
.open(shapeNameOut
) )
376 nlwarning("ERROR: cannot open output file: %s", shapeNameOut
.c_str());
381 shapeStreamOut
.setShapePointer(pResult
);
382 shapeStreamOut
.serial(shapeFileOut
);
383 shapeFileOut
.close();