1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2021 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <nel/misc/file.h>
21 #include <nel/3d/mesh.h>
22 #include <nel/3d/mesh_mrm.h>
23 #include <nel/3d/mesh_mrm_skinned.h>
24 #include <nel/3d/scene.h>
25 #include <nel/3d/register_3d.h>
26 #include <nel/misc/app_context.h>
27 #include <nel/misc/o_xml.h>
28 #include <nel/misc/i_xml.h>
33 using namespace NLMISC
;
44 bool operator == (const CVertex
&v1
, const CVertex
&v2
)
46 return (v1
.vertex
== v2
.vertex
) && (v1
.normal
== v2
.normal
) && (v1
.uv
== v2
.uv
);
49 bool operator < (const CVertex
&v1
, const CVertex
&v2
)
52 if (v1.vertex == v2.vertex)
54 if (v1.normal == v2.normal)
56 return (v1.uv < v2.uv);
58 return (v1.normal < v1.normal);
61 return (v1
.vertex
< v2
.vertex
);
64 const CIndexBuffer
*getRdrPassPrimitiveBlock(const CMeshGeom
*mesh
, uint lodId
, uint renderPass
)
66 return &(mesh
->getRdrPassPrimitiveBlock(lodId
, renderPass
));
69 const CIndexBuffer
*getRdrPassPrimitiveBlock(const CMeshMRMGeom
*mesh
, uint lodId
, uint renderPass
)
71 return &(mesh
->getRdrPassPrimitiveBlock(lodId
, renderPass
));
74 // ***************************************************************************
76 const CIndexBuffer
*getRdrPassPrimitiveBlock(const CMeshMRMSkinnedGeom
*mesh
, uint lodId
, uint renderPass
)
78 static CIndexBuffer block
;
79 mesh
->getRdrPassPrimitiveBlock(lodId
, renderPass
, block
);
83 // ***************************************************************************
85 bool ProcessMeshMRMSkinned(const std::string
&filename
, IShape
*shapeMesh
);
86 bool ProcessMeshMRM(const std::string
&filename
, IShape
*shapeMesh
);
87 bool ProcessMesh(const std::string
&filename
, IShape
*shapeMesh
);
89 int main(int argc
, char* argv
[])
93 cout
<< "Syntax : shape2obj <NeL .shape file>" << endl
;
98 if (!NLMISC::INelContext::isContextInitialised()) new NLMISC::CApplicationContext();
101 CScene::registerBasics();
103 IShape
*shapeMesh
= NULL
;
108 CShapeStream streamShape
;
110 string filename
= argv
[1];
112 if (!ifile
.open(filename
)) return 1;
117 streamShape
.serial(ifile
);
120 shapeMesh
= streamShape
.getShapePointer();
122 catch (const Exception
& e
)
124 cout
<< "Error : " << e
.what() << endl
;
129 if (ProcessMeshMRMSkinned(filename
, shapeMesh
)) return 0;
130 if (ProcessMeshMRM(filename
, shapeMesh
)) return 0;
131 if (ProcessMesh(filename
, shapeMesh
)) return 0;
136 bool ProcessMeshMRMSkinned(const std::string
&filename
, IShape
*shapeMesh
)
138 CMeshMRMSkinned
*mesh
= dynamic_cast<CMeshMRMSkinned
*>(shapeMesh
);
140 if (!mesh
) return false;
144 CMeshMRMSkinnedGeom
* meshIn
= (CMeshMRMSkinnedGeom
*)&mesh
->getMeshGeom();
146 std::vector
<CMesh::CSkinWeight
> skinWeights
;
147 meshIn
->getSkinWeights(skinWeights
);
148 CVertexBuffer vertexBuffer
;
149 meshIn
->getVertexBuffer(vertexBuffer
);
151 CVertexBufferRead vba
;
152 vertexBuffer
.lock (vba
);
155 // **** Select the Lod.
156 uint numLods
= meshIn
->getNbLod();
158 // get the max tris displayed
159 float numMeshFacesMin
= (float)meshIn
->getLevelDetail().MinFaceUsed
;
160 float numMeshFacesMax
= (float)meshIn
->getLevelDetail().MaxFaceUsed
;
162 sint lodId
= numLods
-1;
164 // **** First, for the best lod indicate what vertex is used or not. Also index geomorphs to know what real vertex is used
165 vector
<sint
> vertexUsed
;
166 // -1 means "not used"
167 vertexUsed
.resize(skinWeights
.size(), -1);
168 // Parse all triangles.
169 for(i
=0;i
<meshIn
->getNbRdrPass(lodId
); ++i
)
171 const CIndexBuffer
*pb
= getRdrPassPrimitiveBlock(meshIn
, lodId
, i
);
172 CIndexBufferRead iba
;
174 if (iba
.getFormat() == CIndexBuffer::Indices32
)
176 const uint32
*triPtr
= (const uint32
*) iba
.getPtr();
177 for(j
=0;j
<pb
->getNumIndexes(); ++j
)
180 // Flag the vertex with its own index => used.
181 vertexUsed
[idx
]= idx
;
187 const uint16
*triPtr
= (const uint16
*) iba
.getPtr();
188 for(j
=0;j
<pb
->getNumIndexes(); ++j
)
191 // Flag the vertex with its own index => used.
192 vertexUsed
[idx
]= idx
;
197 // Special for Geomorphs: must take The End target vertex.
198 const std::vector
<CMRMWedgeGeom
> &geomorphs
= meshIn
->getGeomorphs(lodId
);
199 for(i
=0;i
<geomorphs
.size(); ++i
)
201 uint trueIdx
= geomorphs
[i
].End
;
202 // map to the Geomorph Target.
203 vertexUsed
[i
]= trueIdx
;
204 // mark also the real vertex used as used.
205 vertexUsed
[trueIdx
]= trueIdx
;
209 // **** For all vertices used (not geomorphs), compute vertex Skins.
210 vector
<CVertex
> shadowVertices
;
211 vector
<sint
> vertexToVSkin
;
212 vertexToVSkin
.resize(vertexUsed
.size());
213 shadowVertices
.reserve(vertexUsed
.size());
214 // use a map to remove duplicates (because of UV/normal discontinuities before!!)
215 map
<CVertex
, uint
> shadowVertexMap
;
218 for(i
=geomorphs
.size();i
<vertexUsed
.size(); ++i
)
220 // If this vertex is used.
221 if(vertexUsed
[i
]!=-1)
226 shadowVert
.vertex
= *(CVector
*)vba
.getVertexCoordPointer(i
);
227 shadowVert
.normal
= *(CVector
*)vba
.getNormalCoordPointer(i
);
228 shadowVert
.uv
= *(CUV
*)vba
.getTexCoordPointer(i
);
230 // Select the best Matrix.
231 CMesh::CSkinWeight sw= skinWeights[i];
234 for(j=0;j<NL3D_MESH_SKINNING_MAX_MATRIX;j++)
236 // if no more matrix influenced, stop
239 if(sw.Weights[j]>maxW)
241 matId= sw.MatrixId[j];
245 // shadowVert.MatrixId= matId;
247 // If dont find the shadowVertex in the map.
248 map
<CVertex
, uint
>::iterator it
= shadowVertexMap
.find(shadowVert
);
249 if(it
==shadowVertexMap
.end())
252 uint index
= shadowVertices
.size();
253 vertexToVSkin
[i
]= index
;
254 shadowVertices
.push_back(shadowVert
);
255 shadowVertexMap
.insert(make_pair(shadowVert
, index
));
260 vertexToVSkin
[i
]= it
->second
;
267 ofstream
ofs(string(filename
+ ".obj").c_str());
269 for(size_t y
= 0; y
< shadowVertices
.size(); ++y
)
271 CVector v
= shadowVertices
[y
].vertex
;
272 CVector vn
= shadowVertices
[y
].normal
;
273 CUV vt
= shadowVertices
[y
].uv
;
275 ofs
<< "v " << v
.x
<< " " << v
.y
<< " " << v
.z
<< endl
;
276 ofs
<< "vn " << vn
.x
<< " " << vn
.y
<< " " << vn
.z
<< endl
;
277 ofs
<< "vt " << vt
.U
<< " " << (1.0f
- vt
.V
) << endl
;
280 // **** Get All Faces
281 // Final List Of Triangles that match the bone.
282 vector
<uint32
> shadowTriangles
;
283 shadowTriangles
.reserve(1000);
284 // Parse all input tri of the mesh.
285 for(i
=0; i
<meshIn
->getNbRdrPass(lodId
); ++i
)
287 ofs
<< "g pass" << i
<< endl
;
289 const CIndexBuffer
*pb
= getRdrPassPrimitiveBlock(meshIn
, lodId
, i
);
290 CIndexBufferRead iba
;
292 if (iba
.getFormat() == CIndexBuffer::Indices32
)
294 const uint32
*triPtr
= (const uint32
*) iba
.getPtr();
296 for(j
=0; j
<pb
->getNumIndexes(); ++j
)
299 // Get the real Vertex (ie not the geomporhed one).
300 idx
= vertexUsed
[idx
];
301 // Get the ShadowVertex associated
302 idx
= vertexToVSkin
[idx
];
304 shadowTriangles
.push_back(idx
);
310 const uint16
*triPtr
= (const uint16
*) iba
.getPtr();
311 for(j
=0; j
<pb
->getNumIndexes(); ++j
)
314 // Get the real Vertex (ie not the geomporhed one).
315 idx
= vertexUsed
[idx
];
316 // Get the ShadowVertex associated
317 idx
= vertexToVSkin
[idx
];
319 shadowTriangles
.push_back(idx
);
324 for(size_t pass
= 0; pass
<shadowTriangles
.size(); pass
+= 3)
326 ofs
<< "f " << shadowTriangles
[pass
]+1 << "/" << shadowTriangles
[pass
]+1 << "/" << shadowTriangles
[pass
]+1 << " ";
327 ofs
<< shadowTriangles
[pass
+1]+1 << "/" << shadowTriangles
[pass
+1]+1 << "/" << shadowTriangles
[pass
+1]+1 << " ";
328 ofs
<< shadowTriangles
[pass
+2]+1 << "/" << shadowTriangles
[pass
+2]+1 << "/" << shadowTriangles
[pass
+2]+1 << endl
;
331 shadowTriangles
.clear();
339 bool ProcessMeshMRM(const std::string
&filename
, IShape
*shapeMesh
)
341 CMeshMRM
*mesh
= dynamic_cast<CMeshMRM
*>(shapeMesh
);
343 if (!mesh
) return false;
347 CMeshMRMGeom
* meshIn
= (CMeshMRMGeom
*)&mesh
->getMeshGeom();
349 std::vector
<CMesh::CSkinWeight
> skinWeights
= meshIn
->getSkinWeights();
350 CVertexBuffer vertexBuffer
= meshIn
->getVertexBuffer();
352 CVertexBufferRead vba
;
353 vertexBuffer
.lock (vba
);
356 // **** Select the Lod.
357 uint numLods
= meshIn
->getNbLod();
359 // get the max tris displayed
360 float numMeshFacesMin
= (float)meshIn
->getLevelDetail().MinFaceUsed
;
361 float numMeshFacesMax
= (float)meshIn
->getLevelDetail().MaxFaceUsed
;
363 sint lodId
= numLods
-1;
365 // **** First, for the best lod indicate what vertex is used or not. Also index geomorphs to know what real vertex is used
366 vector
<sint
> vertexUsed
;
367 // -1 means "not used"
368 vertexUsed
.resize(skinWeights
.size(), -1);
369 // Parse all triangles.
370 for(i
=0;i
<meshIn
->getNbRdrPass(lodId
); ++i
)
372 const CIndexBuffer
*pb
= getRdrPassPrimitiveBlock(meshIn
, lodId
, i
);
373 CIndexBufferRead iba
;
375 if (iba
.getFormat() == CIndexBuffer::Indices32
)
377 const uint32
*triPtr
= (const uint32
*) iba
.getPtr();
378 for(j
=0;j
<pb
->getNumIndexes(); ++j
)
381 // Flag the vertex with its own index => used.
382 vertexUsed
[idx
]= idx
;
388 const uint16
*triPtr
= (const uint16
*) iba
.getPtr();
389 for(j
=0;j
<pb
->getNumIndexes(); ++j
)
392 // Flag the vertex with its own index => used.
393 vertexUsed
[idx
]= idx
;
398 // Special for Geomorphs: must take The End target vertex.
399 const std::vector
<CMRMWedgeGeom
> &geomorphs
= meshIn
->getGeomorphs(lodId
);
400 for(i
=0;i
<geomorphs
.size(); ++i
)
402 uint trueIdx
= geomorphs
[i
].End
;
403 // map to the Geomorph Target.
404 vertexUsed
[i
]= trueIdx
;
405 // mark also the real vertex used as used.
406 vertexUsed
[trueIdx
]= trueIdx
;
410 // **** For all vertices used (not geomorphs), compute vertex Skins.
411 vector
<CVertex
> shadowVertices
;
412 vector
<sint
> vertexToVSkin
;
413 vertexToVSkin
.resize(vertexUsed
.size());
414 shadowVertices
.reserve(vertexUsed
.size());
415 // use a map to remove duplicates (because of UV/normal discontinuities before!!)
416 map
<CVertex
, uint
> shadowVertexMap
;
419 for(i
=geomorphs
.size();i
<vertexUsed
.size(); ++i
)
421 // If this vertex is used.
422 if(vertexUsed
[i
]!=-1)
427 shadowVert
.vertex
= *(CVector
*)vba
.getVertexCoordPointer(i
);
428 shadowVert
.normal
= *(CVector
*)vba
.getNormalCoordPointer(i
);
429 shadowVert
.uv
= *(CUV
*)vba
.getTexCoordPointer(i
);
431 // Select the best Matrix.
432 CMesh::CSkinWeight sw= skinWeights[i];
435 for(j=0;j<NL3D_MESH_SKINNING_MAX_MATRIX;j++)
437 // if no more matrix influenced, stop
440 if(sw.Weights[j]>maxW)
442 matId= sw.MatrixId[j];
446 // shadowVert.MatrixId= matId;
448 // If dont find the shadowVertex in the map.
449 map
<CVertex
, uint
>::iterator it
= shadowVertexMap
.find(shadowVert
);
450 if(it
==shadowVertexMap
.end())
453 uint index
= shadowVertices
.size();
454 vertexToVSkin
[i
]= index
;
455 shadowVertices
.push_back(shadowVert
);
456 shadowVertexMap
.insert(make_pair(shadowVert
, index
));
461 vertexToVSkin
[i
]= it
->second
;
468 ofstream
ofs(string(filename
+ ".obj").c_str());
470 for(size_t y
= 0; y
< shadowVertices
.size(); ++y
)
472 CVector v
= shadowVertices
[y
].vertex
;
473 CVector vn
= shadowVertices
[y
].normal
;
474 CUV vt
= shadowVertices
[y
].uv
;
476 ofs
<< "v " << v
.x
<< " " << v
.y
<< " " << v
.z
<< endl
;
477 ofs
<< "vn " << vn
.x
<< " " << vn
.y
<< " " << vn
.z
<< endl
;
478 ofs
<< "vt " << vt
.U
<< " " << (1.0f
- vt
.V
) << endl
;
481 // **** Get All Faces
482 // Final List Of Triangles that match the bone.
483 vector
<uint32
> shadowTriangles
;
484 shadowTriangles
.reserve(1000);
485 // Parse all input tri of the mesh.
486 for(i
=0; i
<meshIn
->getNbRdrPass(lodId
); ++i
)
488 ofs
<< "g pass" << i
<< endl
;
490 const CIndexBuffer
*pb
= getRdrPassPrimitiveBlock(meshIn
, lodId
, i
);
491 CIndexBufferRead iba
;
493 if (iba
.getFormat() == CIndexBuffer::Indices32
)
495 const uint32
*triPtr
= (const uint32
*) iba
.getPtr();
497 for(j
=0; j
<pb
->getNumIndexes(); ++j
)
500 // Get the real Vertex (ie not the geomporhed one).
501 idx
= vertexUsed
[idx
];
502 // Get the ShadowVertex associated
503 idx
= vertexToVSkin
[idx
];
505 shadowTriangles
.push_back(idx
);
511 const uint16
*triPtr
= (const uint16
*) iba
.getPtr();
512 for(j
=0; j
<pb
->getNumIndexes(); ++j
)
515 // Get the real Vertex (ie not the geomporhed one).
516 idx
= vertexUsed
[idx
];
517 // Get the ShadowVertex associated
518 idx
= vertexToVSkin
[idx
];
520 shadowTriangles
.push_back(idx
);
525 for(size_t pass
= 0; pass
<shadowTriangles
.size(); pass
+= 3)
527 ofs
<< "f " << shadowTriangles
[pass
]+1 << "/" << shadowTriangles
[pass
]+1 << "/" << shadowTriangles
[pass
]+1 << " ";
528 ofs
<< shadowTriangles
[pass
+1]+1 << "/" << shadowTriangles
[pass
+1]+1 << "/" << shadowTriangles
[pass
+1]+1 << " ";
529 ofs
<< shadowTriangles
[pass
+2]+1 << "/" << shadowTriangles
[pass
+2]+1 << "/" << shadowTriangles
[pass
+2]+1 << endl
;
532 shadowTriangles
.clear();
541 bool ProcessMesh(const std::string
&filename
, IShape
*shapeMesh
)
543 CMesh
*mesh
= dynamic_cast<CMesh
*>(shapeMesh
);
545 if (!mesh
) return false;
549 const CMeshGeom
*meshIn
= &mesh
->getMeshGeom();
551 CVertexBuffer vertexBuffer
= meshIn
->getVertexBuffer();
553 CVertexBufferRead vba
;
554 vertexBuffer
.lock (vba
);
557 // **** First, for the best lod indicate what vertex is used or not. Also index geomorphs to know what real vertex is used
558 vector
<sint
> vertexUsed
;
559 // -1 means "not used"
560 vertexUsed
.resize(vertexBuffer
.capacity(), -1);
561 // Parse all triangles.
562 for(i
=0;i
<meshIn
->getNbRdrPass(0); ++i
)
564 const CIndexBuffer
*pb
= getRdrPassPrimitiveBlock(meshIn
, 0, i
);
565 CIndexBufferRead iba
;
567 if (iba
.getFormat() == CIndexBuffer::Indices32
)
569 const uint32
*triPtr
= (const uint32
*) iba
.getPtr();
570 for(j
=0;j
<pb
->getNumIndexes(); ++j
)
573 // Flag the vertex with its own index => used.
574 vertexUsed
[idx
]= idx
;
580 const uint16
*triPtr
= (const uint16
*) iba
.getPtr();
581 for(j
=0;j
<pb
->getNumIndexes(); ++j
)
584 // Flag the vertex with its own index => used.
585 vertexUsed
[idx
]= idx
;
592 // **** For all vertices used (not geomorphs), compute vertex Skins.
593 vector
<CVertex
> shadowVertices
;
594 vector
<sint
> vertexToVSkin
;
595 vertexToVSkin
.resize(vertexUsed
.size());
596 shadowVertices
.reserve(vertexUsed
.size());
598 for(i
=0;i
<vertexUsed
.size(); ++i
)
600 // If this vertex is used.
601 if(vertexUsed
[i
]!=-1)
606 shadowVert
.vertex
= *(CVector
*)vba
.getVertexCoordPointer(i
);
607 shadowVert
.normal
= *(CVector
*)vba
.getNormalCoordPointer(i
);
608 shadowVert
.uv
= *(CUV
*)vba
.getTexCoordPointer(i
);
611 uint index
= shadowVertices
.size();
612 vertexToVSkin
[i
]= index
;
613 shadowVertices
.push_back(shadowVert
);
617 ofstream
ofs(string(filename
+ ".obj").c_str());
619 map
<CVector
, sint
> weldedVerticesToId
;
620 vector
<CVector
> weldedVertices
;
621 vector
<sint
> shadowToWelded
;
622 weldedVertices
.reserve(shadowVertices
.size());
623 shadowToWelded
.resize(shadowVertices
.size());
625 for (i
= 0; i
< shadowVertices
.size(); ++i
)
627 CVector v
= shadowVertices
[i
].vertex
;
628 map
<CVector
, sint
>::iterator it
= weldedVerticesToId
.find(v
);
629 if (it
== weldedVerticesToId
.end())
631 weldedVerticesToId
[v
] = weldedVertices
.size();
632 shadowToWelded
[i
] = weldedVertices
.size();
633 weldedVertices
.push_back(v
);
637 shadowToWelded
[i
] = it
->second
;
641 for(size_t y
= 0; y
< weldedVertices
.size(); ++y
)
643 CVector v
= weldedVertices
[y
];
644 ofs
<< "v " << v
.x
<< " " << v
.y
<< " " << v
.z
<< endl
;
647 for(size_t y
= 0; y
< shadowVertices
.size(); ++y
)
649 CVector vn
= shadowVertices
[y
].normal
;
650 CUV vt
= shadowVertices
[y
].uv
;
651 ofs
<< "vn " << vn
.x
<< " " << vn
.y
<< " " << vn
.z
<< endl
;
652 ofs
<< "vt " << vt
.U
<< " " << (1.0f
- vt
.V
) << endl
;
655 // **** Get All Faces
656 // Final List Of Triangles that match the bone.
657 vector
<uint32
> shadowTriangles
;
658 shadowTriangles
.reserve(1000);
659 // Parse all input tri of the mesh.
660 for(i
=0; i
<meshIn
->getNbRdrPass(0); ++i
)
662 ofs
<< "g " << filename
<< endl
;
664 const CIndexBuffer
*pb
= getRdrPassPrimitiveBlock(meshIn
, 0, i
);
665 CIndexBufferRead iba
;
667 if (iba
.getFormat() == CIndexBuffer::Indices32
)
669 const uint32
*triPtr
= (const uint32
*) iba
.getPtr();
671 for(j
=0; j
<pb
->getNumIndexes(); ++j
)
674 // Get the real Vertex (ie not the geomporhed one).
675 idx
= vertexUsed
[idx
];
676 // Get the ShadowVertex associated
677 idx
= vertexToVSkin
[idx
];
679 shadowTriangles
.push_back(idx
);
685 const uint16
*triPtr
= (const uint16
*) iba
.getPtr();
686 for(j
=0; j
<pb
->getNumIndexes(); ++j
)
689 // Get the real Vertex (ie not the geomporhed one).
690 idx
= vertexUsed
[idx
];
691 // Get the ShadowVertex associated
692 idx
= vertexToVSkin
[idx
];
694 shadowTriangles
.push_back(idx
);
699 for(size_t pass
= 0; pass
<shadowTriangles
.size(); pass
+= 3)
701 ofs
<< "f " << shadowToWelded
[shadowTriangles
[pass
]]+1 << "/" << shadowTriangles
[pass
]+1 << "/" << shadowTriangles
[pass
]+1 << " ";
702 ofs
<< shadowToWelded
[shadowTriangles
[pass
+1]]+1 << "/" << shadowTriangles
[pass
+1]+1 << "/" << shadowTriangles
[pass
+1]+1 << " ";
703 ofs
<< shadowToWelded
[shadowTriangles
[pass
+2]]+1 << "/" << shadowTriangles
[pass
+2]+1 << "/" << shadowTriangles
[pass
+2]+1 << endl
;
706 shadowTriangles
.clear();