1/*
2---------------------------------------------------------------------------
3Open Asset Import Library (assimp)
4---------------------------------------------------------------------------
5
6Copyright (c) 2006-2017, assimp team
7
8
9All rights reserved.
10
11Redistribution and use of this software in source and binary forms,
12with or without modification, are permitted provided that the following
13conditions are met:
14
15* Redistributions of source code must retain the above
16 copyright notice, this list of conditions and the
17 following disclaimer.
18
19* Redistributions in binary form must reproduce the above
20 copyright notice, this list of conditions and the
21 following disclaimer in the documentation and/or other
22 materials provided with the distribution.
23
24* Neither the name of the assimp team, nor the names of its
25 contributors may be used to endorse or promote products
26 derived from this software without specific prior
27 written permission of the assimp team.
28
29THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40---------------------------------------------------------------------------
41*/
42
43/** @file Implementation of the IrrMesh importer class */
44
45
46
47#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
48
49#include "IRRMeshLoader.h"
50#include "ParsingUtils.h"
51#include "fast_atof.h"
52#include <memory>
53#include <assimp/IOSystem.hpp>
54#include <assimp/mesh.h>
55#include <assimp/DefaultLogger.hpp>
56#include <assimp/material.h>
57#include <assimp/scene.h>
58#include <assimp/importerdesc.h>
59#include "Macros.h"
60
61using namespace Assimp;
62using namespace irr;
63using namespace irr::io;
64
65static const aiImporterDesc desc = {
66 "Irrlicht Mesh Reader",
67 "",
68 "",
69 "http://irrlicht.sourceforge.net/",
70 aiImporterFlags_SupportTextFlavour,
71 0,
72 0,
73 0,
74 0,
75 "xml irrmesh"
76};
77
78// ------------------------------------------------------------------------------------------------
79// Constructor to be privately used by Importer
80IRRMeshImporter::IRRMeshImporter()
81{}
82
83// ------------------------------------------------------------------------------------------------
84// Destructor, private as well
85IRRMeshImporter::~IRRMeshImporter()
86{}
87
88// ------------------------------------------------------------------------------------------------
89// Returns whether the class can handle the format of the given file.
90bool IRRMeshImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
91{
92 /* NOTE: A simple check for the file extension is not enough
93 * here. Irrmesh and irr are easy, but xml is too generic
94 * and could be collada, too. So we need to open the file and
95 * search for typical tokens.
96 */
97 const std::string extension = GetExtension(pFile);
98
99 if (extension == "irrmesh")return true;
100 else if (extension == "xml" || checkSig)
101 {
102 /* If CanRead() is called to check whether the loader
103 * supports a specific file extension in general we
104 * must return true here.
105 */
106 if (!pIOHandler)return true;
107 const char* tokens[] = {"irrmesh"};
108 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
109 }
110 return false;
111}
112
113// ------------------------------------------------------------------------------------------------
114// Get a list of all file extensions which are handled by this class
115const aiImporterDesc* IRRMeshImporter::GetInfo () const
116{
117 return &desc;
118}
119
120static void releaseMaterial( aiMaterial **mat ) {
121 if(*mat!= nullptr) {
122 delete *mat;
123 *mat = nullptr;
124 }
125}
126
127static void releaseMesh( aiMesh **mesh ) {
128 if (*mesh != nullptr){
129 delete *mesh;
130 *mesh = nullptr;
131 }
132}
133
134// ------------------------------------------------------------------------------------------------
135// Imports the given file into the given scene structure.
136void IRRMeshImporter::InternReadFile( const std::string& pFile,
137 aiScene* pScene, IOSystem* pIOHandler)
138{
139 std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
140
141 // Check whether we can read from the file
142 if( file.get() == NULL)
143 throw DeadlyImportError( "Failed to open IRRMESH file " + pFile + "");
144
145 // Construct the irrXML parser
146 CIrrXML_IOStreamReader st(file.get());
147 reader = createIrrXMLReader((IFileReadCallBack*) &st);
148
149 // final data
150 std::vector<aiMaterial*> materials;
151 std::vector<aiMesh*> meshes;
152 materials.reserve (5);
153 meshes.reserve(5);
154
155 // temporary data - current mesh buffer
156 aiMaterial* curMat = nullptr;
157 aiMesh* curMesh = nullptr;
158 unsigned int curMatFlags = 0;
159
160 std::vector<aiVector3D> curVertices,curNormals,curTangents,curBitangents;
161 std::vector<aiColor4D> curColors;
162 std::vector<aiVector3D> curUVs,curUV2s;
163
164 // some temporary variables
165 int textMeaning = 0;
166 int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents
167 bool useColors = false;
168
169 // Parse the XML file
170 while (reader->read()) {
171 switch (reader->getNodeType()) {
172 case EXN_ELEMENT:
173
174 if (!ASSIMP_stricmp(reader->getNodeName(),"buffer") && (curMat || curMesh)) {
175 // end of previous buffer. A material and a mesh should be there
176 if ( !curMat || !curMesh) {
177 DefaultLogger::get()->error("IRRMESH: A buffer must contain a mesh and a material");
178 releaseMaterial( &curMat );
179 releaseMesh( &curMesh );
180 } else {
181 materials.push_back(curMat);
182 meshes.push_back(curMesh);
183 }
184 curMat = nullptr;
185 curMesh = nullptr;
186
187 curVertices.clear();
188 curColors.clear();
189 curNormals.clear();
190 curUV2s.clear();
191 curUVs.clear();
192 curTangents.clear();
193 curBitangents.clear();
194 }
195
196
197 if (!ASSIMP_stricmp(reader->getNodeName(),"material")) {
198 if (curMat) {
199 DefaultLogger::get()->warn("IRRMESH: Only one material description per buffer, please");
200 releaseMaterial( &curMat );
201 }
202 curMat = ParseMaterial(curMatFlags);
203 }
204 /* no else here! */ if (!ASSIMP_stricmp(reader->getNodeName(),"vertices"))
205 {
206 int num = reader->getAttributeValueAsInt("vertexCount");
207
208 if (!num) {
209 // This is possible ... remove the mesh from the list and skip further reading
210 DefaultLogger::get()->warn("IRRMESH: Found mesh with zero vertices");
211
212 releaseMaterial( &curMat );
213 releaseMesh( &curMesh );
214 textMeaning = 0;
215 continue;
216 }
217
218 curVertices.reserve(num);
219 curNormals.reserve(num);
220 curColors.reserve(num);
221 curUVs.reserve(num);
222
223 // Determine the file format
224 const char* t = reader->getAttributeValueSafe("type");
225 if (!ASSIMP_stricmp("2tcoords", t)) {
226 curUV2s.reserve (num);
227 vertexFormat = 1;
228
229 if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) {
230 // *********************************************************
231 // We have a second texture! So use this UV channel
232 // for it. The 2nd texture can be either a normal
233 // texture (solid_2layer or lightmap_xxx) or a normal
234 // map (normal_..., parallax_...)
235 // *********************************************************
236 int idx = 1;
237 aiMaterial* mat = ( aiMaterial* ) curMat;
238
239 if (curMatFlags & AI_IRRMESH_MAT_lightmap){
240 mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_LIGHTMAP(0));
241 }
242 else if (curMatFlags & AI_IRRMESH_MAT_normalmap_solid){
243 mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_NORMALS(0));
244 }
245 else if (curMatFlags & AI_IRRMESH_MAT_solid_2layer) {
246 mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_DIFFUSE(1));
247 }
248 }
249 }
250 else if (!ASSIMP_stricmp("tangents", t)) {
251 curTangents.reserve (num);
252 curBitangents.reserve (num);
253 vertexFormat = 2;
254 }
255 else if (ASSIMP_stricmp("standard", t)) {
256 releaseMaterial( &curMat );
257 DefaultLogger::get()->warn("IRRMESH: Unknown vertex format");
258 }
259 else vertexFormat = 0;
260 textMeaning = 1;
261 }
262 else if (!ASSIMP_stricmp(reader->getNodeName(),"indices")) {
263 if (curVertices.empty() && curMat) {
264 releaseMaterial( &curMat );
265 throw DeadlyImportError("IRRMESH: indices must come after vertices");
266 }
267
268 textMeaning = 2;
269
270 // start a new mesh
271 curMesh = new aiMesh();
272
273 // allocate storage for all faces
274 curMesh->mNumVertices = reader->getAttributeValueAsInt("indexCount");
275 if (!curMesh->mNumVertices) {
276 // This is possible ... remove the mesh from the list and skip further reading
277 DefaultLogger::get()->warn("IRRMESH: Found mesh with zero indices");
278
279 // mesh - away
280 releaseMesh( &curMesh );
281
282 // material - away
283 releaseMaterial( &curMat );
284
285 textMeaning = 0;
286 continue;
287 }
288
289 if (curMesh->mNumVertices % 3) {
290 DefaultLogger::get()->warn("IRRMESH: Number if indices isn't divisible by 3");
291 }
292
293 curMesh->mNumFaces = curMesh->mNumVertices / 3;
294 curMesh->mFaces = new aiFace[curMesh->mNumFaces];
295
296 // setup some members
297 curMesh->mMaterialIndex = (unsigned int)materials.size();
298 curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
299
300 // allocate storage for all vertices
301 curMesh->mVertices = new aiVector3D[curMesh->mNumVertices];
302
303 if (curNormals.size() == curVertices.size()) {
304 curMesh->mNormals = new aiVector3D[curMesh->mNumVertices];
305 }
306 if (curTangents.size() == curVertices.size()) {
307 curMesh->mTangents = new aiVector3D[curMesh->mNumVertices];
308 }
309 if (curBitangents.size() == curVertices.size()) {
310 curMesh->mBitangents = new aiVector3D[curMesh->mNumVertices];
311 }
312 if (curColors.size() == curVertices.size() && useColors) {
313 curMesh->mColors[0] = new aiColor4D[curMesh->mNumVertices];
314 }
315 if (curUVs.size() == curVertices.size()) {
316 curMesh->mTextureCoords[0] = new aiVector3D[curMesh->mNumVertices];
317 }
318 if (curUV2s.size() == curVertices.size()) {
319 curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices];
320 }
321 }
322 break;
323
324 case EXN_TEXT:
325 {
326 const char* sz = reader->getNodeData();
327 if (textMeaning == 1) {
328 textMeaning = 0;
329
330 // read vertices
331 do {
332 SkipSpacesAndLineEnd(&sz);
333 aiVector3D temp;aiColor4D c;
334
335 // Read the vertex position
336 sz = fast_atoreal_move<float>(sz,(float&)temp.x);
337 SkipSpaces(&sz);
338
339 sz = fast_atoreal_move<float>(sz,(float&)temp.y);
340 SkipSpaces(&sz);
341
342 sz = fast_atoreal_move<float>(sz,(float&)temp.z);
343 SkipSpaces(&sz);
344 curVertices.push_back(temp);
345
346 // Read the vertex normals
347 sz = fast_atoreal_move<float>(sz,(float&)temp.x);
348 SkipSpaces(&sz);
349
350 sz = fast_atoreal_move<float>(sz,(float&)temp.y);
351 SkipSpaces(&sz);
352
353 sz = fast_atoreal_move<float>(sz,(float&)temp.z);
354 SkipSpaces(&sz);
355 curNormals.push_back(temp);
356
357 // read the vertex colors
358 uint32_t clr = strtoul16(sz,&sz);
359 ColorFromARGBPacked(clr,c);
360
361 if (!curColors.empty() && c != *(curColors.end()-1))
362 useColors = true;
363
364 curColors.push_back(c);
365 SkipSpaces(&sz);
366
367
368 // read the first UV coordinate set
369 sz = fast_atoreal_move<float>(sz,(float&)temp.x);
370 SkipSpaces(&sz);
371
372 sz = fast_atoreal_move<float>(sz,(float&)temp.y);
373 SkipSpaces(&sz);
374 temp.z = 0.f;
375 temp.y = 1.f - temp.y; // DX to OGL
376 curUVs.push_back(temp);
377
378 // read the (optional) second UV coordinate set
379 if (vertexFormat == 1) {
380 sz = fast_atoreal_move<float>(sz,(float&)temp.x);
381 SkipSpaces(&sz);
382
383 sz = fast_atoreal_move<float>(sz,(float&)temp.y);
384 temp.y = 1.f - temp.y; // DX to OGL
385 curUV2s.push_back(temp);
386 }
387 // read optional tangent and bitangent vectors
388 else if (vertexFormat == 2) {
389 // tangents
390 sz = fast_atoreal_move<float>(sz,(float&)temp.x);
391 SkipSpaces(&sz);
392
393 sz = fast_atoreal_move<float>(sz,(float&)temp.z);
394 SkipSpaces(&sz);
395
396 sz = fast_atoreal_move<float>(sz,(float&)temp.y);
397 SkipSpaces(&sz);
398 temp.y *= -1.0f;
399 curTangents.push_back(temp);
400
401 // bitangents
402 sz = fast_atoreal_move<float>(sz,(float&)temp.x);
403 SkipSpaces(&sz);
404
405 sz = fast_atoreal_move<float>(sz,(float&)temp.z);
406 SkipSpaces(&sz);
407
408 sz = fast_atoreal_move<float>(sz,(float&)temp.y);
409 SkipSpaces(&sz);
410 temp.y *= -1.0f;
411 curBitangents.push_back(temp);
412 }
413 }
414
415 /* IMPORTANT: We assume that each vertex is specified in one
416 line. So we can skip the rest of the line - unknown vertex
417 elements are ignored.
418 */
419
420 while (SkipLine(&sz));
421 }
422 else if (textMeaning == 2) {
423 textMeaning = 0;
424
425 // read indices
426 aiFace* curFace = curMesh->mFaces;
427 aiFace* const faceEnd = curMesh->mFaces + curMesh->mNumFaces;
428
429 aiVector3D* pcV = curMesh->mVertices;
430 aiVector3D* pcN = curMesh->mNormals;
431 aiVector3D* pcT = curMesh->mTangents;
432 aiVector3D* pcB = curMesh->mBitangents;
433 aiColor4D* pcC0 = curMesh->mColors[0];
434 aiVector3D* pcT0 = curMesh->mTextureCoords[0];
435 aiVector3D* pcT1 = curMesh->mTextureCoords[1];
436
437 unsigned int curIdx = 0;
438 unsigned int total = 0;
439 while(SkipSpacesAndLineEnd(&sz)) {
440 if (curFace >= faceEnd) {
441 DefaultLogger::get()->error("IRRMESH: Too many indices");
442 break;
443 }
444 if (!curIdx) {
445 curFace->mNumIndices = 3;
446 curFace->mIndices = new unsigned int[3];
447 }
448
449 unsigned int idx = strtoul10(sz,&sz);
450 if (idx >= curVertices.size()) {
451 DefaultLogger::get()->error("IRRMESH: Index out of range");
452 idx = 0;
453 }
454
455 curFace->mIndices[curIdx] = total++;
456
457 *pcV++ = curVertices[idx];
458 if (pcN)*pcN++ = curNormals[idx];
459 if (pcT)*pcT++ = curTangents[idx];
460 if (pcB)*pcB++ = curBitangents[idx];
461 if (pcC0)*pcC0++ = curColors[idx];
462 if (pcT0)*pcT0++ = curUVs[idx];
463 if (pcT1)*pcT1++ = curUV2s[idx];
464
465 if (++curIdx == 3) {
466 ++curFace;
467 curIdx = 0;
468 }
469 }
470
471 if (curFace != faceEnd)
472 DefaultLogger::get()->error("IRRMESH: Not enough indices");
473
474 // Finish processing the mesh - do some small material workarounds
475 if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) {
476 // Take the opacity value of the current material
477 // from the common vertex color alpha
478 aiMaterial* mat = (aiMaterial*)curMat;
479 mat->AddProperty(&curColors[0].a,1,AI_MATKEY_OPACITY);
480 }
481 }}
482 break;
483
484 default:
485 // GCC complains here ...
486 break;
487
488 };
489 }
490
491 // End of the last buffer. A material and a mesh should be there
492 if (curMat || curMesh) {
493 if ( !curMat || !curMesh) {
494 DefaultLogger::get()->error("IRRMESH: A buffer must contain a mesh and a material");
495 releaseMaterial( &curMat );
496 releaseMesh( &curMesh );
497 }
498 else {
499 materials.push_back(curMat);
500 meshes.push_back(curMesh);
501 }
502 }
503
504 if (materials.empty())
505 throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file");
506
507
508 // now generate the output scene
509 pScene->mNumMeshes = (unsigned int)meshes.size();
510 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
511 for (unsigned int i = 0; i < pScene->mNumMeshes;++i) {
512 pScene->mMeshes[i] = meshes[i];
513
514 // clean this value ...
515 pScene->mMeshes[i]->mNumUVComponents[3] = 0;
516 }
517
518 pScene->mNumMaterials = (unsigned int)materials.size();
519 pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
520 ::memcpy(pScene->mMaterials,&materials[0],sizeof(void*)*pScene->mNumMaterials);
521
522 pScene->mRootNode = new aiNode();
523 pScene->mRootNode->mName.Set("<IRRMesh>");
524 pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
525 pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
526
527 for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
528 pScene->mRootNode->mMeshes[i] = i;
529
530 // clean up and return
531 delete reader;
532 AI_DEBUG_INVALIDATE_PTR(reader);
533}
534
535#endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER
536