Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / tools / 3d / build_shadow_skin / main.cpp
blob4992ca19d72f19f55b2b334c172994c135cb4e7b
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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;
28 using namespace NL3D;
29 using namespace std;
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);
45 return &block;
48 // ***************************************************************************
50 template<class T>
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);
55 uint i, j;
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);
66 // find the lod
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);
81 CIndexBufferRead iba;
82 pb->lock (iba);
83 if (iba.getFormat() == CIndexBuffer::Indices32)
85 const uint32 *triPtr= (const uint32 *) iba.getPtr();
86 for(j=0;j<pb->getNumIndexes();j++)
88 uint idx= *triPtr;
89 // Flag the vertex with its own index => used.
90 vertexUsed[idx]= idx;
91 triPtr++;
94 else
96 const uint16 *triPtr= (const uint16 *) iba.getPtr();
97 for(j=0;j<pb->getNumIndexes();j++)
99 uint idx= *triPtr;
100 // Flag the vertex with its own index => used.
101 vertexUsed[idx]= idx;
102 triPtr++;
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;
125 uint numMerged= 0;
126 // Skip Geomorphs.
127 for(i=(uint)geomorphs.size();i<vertexUsed.size();i++)
129 // If this vertex is used.
130 if(vertexUsed[i]!=-1)
132 // Build the vertex
133 CShadowVertex shadowVert;
134 shadowVert.Vertex= *(CVector*)vba.getVertexCoordPointer(i);
135 // Select the best Matrix.
136 CMesh::CSkinWeight sw= skinWeights[i];
137 float maxW= 0;
138 uint matId= 0;
139 for(j=0;j<NL3D_MESH_SKINNING_MAX_MATRIX;j++)
141 // if no more matrix influenced, stop
142 if(sw.Weights[j]==0)
143 break;
144 if(sw.Weights[j]>maxW)
146 matId= sw.MatrixId[j];
147 maxW= sw.Weights[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())
156 // Append
157 uint index= (uint)shadowVertices.size();
158 vertexToVSkin[i]= index;
159 shadowVertices.push_back(shadowVert);
160 shadowVertexMap.insert(make_pair(shadowVert, index));
162 else
164 // Ok, map.
165 vertexToVSkin[i]= it->second;
166 numMerged++;
171 // Some Info on what have been merged.
172 if(shadowVertices.size())
174 // TestYoyo.
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;
189 pb->lock (iba);
190 if (iba.getFormat() == CIndexBuffer::Indices32)
192 const uint32 *triPtr= (const uint32 *) iba.getPtr();
193 for(j=0;j<pb->getNumIndexes();j++)
195 uint idx= *triPtr;
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);
202 triPtr++;
205 else
207 const uint16 *triPtr= (const uint16 *) iba.getPtr();
208 for(j=0;j<pb->getNumIndexes();j++)
210 uint idx= *triPtr;
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);
217 triPtr++;
222 // Re-Optim VertexCache Hard usage
223 if(shadowTriangles.size())
225 CIndexBuffer pb;
226 // fill
227 pb.setNumIndexes((uint32)shadowTriangles.size());
229 CIndexBufferReadWrite iba;
230 pb.lock (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]);
238 // optimize
239 CStripifier stripifier;
240 stripifier.optimizeTriangles(pb, pb);
241 // get.
243 CIndexBufferReadWrite iba;
244 pb.lock (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;
251 triPtr++;
254 else
256 const uint16 *triPtr= (const uint16 *) iba.getPtr();
257 for(i=0;i<shadowTriangles.size();i++)
259 shadowTriangles[i]= *triPtr;
260 triPtr++;
266 // set
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();
300 if (argc <3 )
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");
308 exit(-1);
311 // Parse args.
312 string shapeNameIn= argv[1];
313 string shapeNameOut= argv[2];
314 sint percentageFace= 100;
315 sint maxFace= INT_MAX;
316 if(argc>=4)
317 NLMISC::fromString(argv[3], percentageFace);
318 if(argc>=5)
319 NLMISC::fromString(argv[4], maxFace);
321 // **** Load the Mesh In.
322 IShape *pResult = NULL;
323 CMeshMRM *pMesh;
324 CShapeStream shapeStreamIn;
325 CIFile shapeFileIn;
326 if(!shapeFileIn.open(shapeNameIn))
328 nlwarning("ERROR: cannot open input file: %s", shapeNameIn.c_str());
329 exit(-1);
331 shapeStreamIn.serial(shapeFileIn);
332 pMesh= dynamic_cast<CMeshMRM*>(shapeStreamIn.getShapePointer());
333 shapeFileIn.close();
334 if(!pMesh)
336 CMeshMRMSkinned *pMeshSkinned= dynamic_cast<CMeshMRMSkinned*>(shapeStreamIn.getShapePointer());
337 if(!pMeshSkinned)
339 nlwarning("ERROR: Not a MRM Mesh nor a MRM Mesh skinned: %s", shapeNameIn.c_str());
340 exit(-1);
343 CMeshMRMSkinnedGeom *pMeshGeom= (CMeshMRMSkinnedGeom*)&pMeshSkinned->getMeshGeom();
345 // **** Add Shadow mesh.
346 float faceRatio;
347 clamp(percentageFace, 0, 100);
348 faceRatio= (float)percentageFace/100;
349 maxFace= max(0, maxFace);
350 addShadowMeshIntro(pMeshGeom, faceRatio, maxFace);
351 pResult = pMeshSkinned;
353 else
355 CMeshMRMGeom *pMeshGeom= (CMeshMRMGeom*)&pMesh->getMeshGeom();
356 if( !pMeshGeom->isSkinned() )
358 nlwarning("ERROR: Not a Skinned MRM Mesh: %s", shapeNameIn.c_str());
359 exit(-1);
362 // **** Add Shadow mesh.
363 float faceRatio;
364 clamp(percentageFace, 0, 100);
365 faceRatio= (float)percentageFace/100;
366 maxFace= max(0, maxFace);
367 addShadowMeshIntro(pMeshGeom, faceRatio, maxFace);
368 pResult = pMesh;
371 // **** Save It
372 CShapeStream shapeStreamOut;
373 COFile shapeFileOut;
374 if(!shapeFileOut.open(shapeNameOut) )
376 nlwarning("ERROR: cannot open output file: %s", shapeNameOut.c_str());
377 exit(-1);
380 nlassert (pResult);
381 shapeStreamOut.setShapePointer(pResult);
382 shapeStreamOut.serial(shapeFileOut);
383 shapeFileOut.close();
385 return 0;