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 IRRLoader.cpp
44 * @brief Implementation of the Irr importer class
45 */
46
47
48
49#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
50
51#include "IRRLoader.h"
52#include "ParsingUtils.h"
53#include "fast_atof.h"
54#include "GenericProperty.h"
55
56#include <assimp/SceneCombiner.h>
57#include "StandardShapes.h"
58#include "Importer.h"
59
60// We need MathFunctions.h to compute the lcm/gcd of a number
61#include "MathFunctions.h"
62#include <memory>
63#include <assimp/DefaultLogger.hpp>
64#include <assimp/mesh.h>
65#include <assimp/material.h>
66#include <assimp/scene.h>
67#include <assimp/IOSystem.hpp>
68#include <assimp/postprocess.h>
69#include <assimp/importerdesc.h>
70
71using namespace Assimp;
72using namespace irr;
73using namespace irr::io;
74
75static const aiImporterDesc desc = {
76 "Irrlicht Scene Reader",
77 "",
78 "",
79 "http://irrlicht.sourceforge.net/",
80 aiImporterFlags_SupportTextFlavour,
81 0,
82 0,
83 0,
84 0,
85 "irr xml"
86};
87
88// ------------------------------------------------------------------------------------------------
89// Constructor to be privately used by Importer
90IRRImporter::IRRImporter()
91 : fps(),
92 configSpeedFlag()
93{}
94
95// ------------------------------------------------------------------------------------------------
96// Destructor, private as well
97IRRImporter::~IRRImporter()
98{}
99
100// ------------------------------------------------------------------------------------------------
101// Returns whether the class can handle the format of the given file.
102bool IRRImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
103{
104 /* NOTE: A simple check for the file extension is not enough
105 * here. Irrmesh and irr are easy, but xml is too generic
106 * and could be collada, too. So we need to open the file and
107 * search for typical tokens.
108 */
109 const std::string extension = GetExtension(pFile);
110
111 if (extension == "irr")return true;
112 else if (extension == "xml" || checkSig)
113 {
114 /* If CanRead() is called in order to check whether we
115 * support a specific file extension in general pIOHandler
116 * might be NULL and it's our duty to return true here.
117 */
118 if (!pIOHandler)return true;
119 const char* tokens[] = {"irr_scene"};
120 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
121 }
122 return false;
123}
124
125// ------------------------------------------------------------------------------------------------
126const aiImporterDesc* IRRImporter::GetInfo () const
127{
128 return &desc;
129}
130
131// ------------------------------------------------------------------------------------------------
132void IRRImporter::SetupProperties(const Importer* pImp)
133{
134 // read the output frame rate of all node animation channels
135 fps = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IRR_ANIM_FPS,100);
136 if (fps < 10.) {
137 DefaultLogger::get()->error("IRR: Invalid FPS configuration");
138 fps = 100;
139 }
140
141 // AI_CONFIG_FAVOUR_SPEED
142 configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
143}
144
145// ------------------------------------------------------------------------------------------------
146// Build a mesh tha consists of a single squad (a side of a skybox)
147aiMesh* IRRImporter::BuildSingleQuadMesh(const SkyboxVertex& v1,
148 const SkyboxVertex& v2,
149 const SkyboxVertex& v3,
150 const SkyboxVertex& v4)
151{
152 // allocate and prepare the mesh
153 aiMesh* out = new aiMesh();
154
155 out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
156 out->mNumFaces = 1;
157
158 // build the face
159 out->mFaces = new aiFace[1];
160 aiFace& face = out->mFaces[0];
161
162 face.mNumIndices = 4;
163 face.mIndices = new unsigned int[4];
164 for (unsigned int i = 0; i < 4;++i)
165 face.mIndices[i] = i;
166
167 out->mNumVertices = 4;
168
169 // copy vertex positions
170 aiVector3D* vec = out->mVertices = new aiVector3D[4];
171 *vec++ = v1.position;
172 *vec++ = v2.position;
173 *vec++ = v3.position;
174 *vec = v4.position;
175
176 // copy vertex normals
177 vec = out->mNormals = new aiVector3D[4];
178 *vec++ = v1.normal;
179 *vec++ = v2.normal;
180 *vec++ = v3.normal;
181 *vec = v4.normal;
182
183 // copy texture coordinates
184 vec = out->mTextureCoords[0] = new aiVector3D[4];
185 *vec++ = v1.uv;
186 *vec++ = v2.uv;
187 *vec++ = v3.uv;
188 *vec = v4.uv;
189 return out;
190}
191
192// ------------------------------------------------------------------------------------------------
193void IRRImporter::BuildSkybox(std::vector<aiMesh*>& meshes, std::vector<aiMaterial*> materials)
194{
195 // Update the material of the skybox - replace the name and disable shading for skyboxes.
196 for (unsigned int i = 0; i < 6;++i) {
197 aiMaterial* out = ( aiMaterial* ) (*(materials.end()-(6-i)));
198
199 aiString s;
200 s.length = ::ai_snprintf( s.data, MAXLEN, "SkyboxSide_%u",i );
201 out->AddProperty(&s,AI_MATKEY_NAME);
202
203 int shading = aiShadingMode_NoShading;
204 out->AddProperty(&shading,1,AI_MATKEY_SHADING_MODEL);
205 }
206
207 // Skyboxes are much more difficult. They are represented
208 // by six single planes with different textures, so we'll
209 // need to build six meshes.
210
211 const ai_real l = 10.0; // the size used by Irrlicht
212
213 // FRONT SIDE
214 meshes.push_back( BuildSingleQuadMesh(
215 SkyboxVertex(-l,-l,-l, 0, 0, 1, 1.0,1.0),
216 SkyboxVertex( l,-l,-l, 0, 0, 1, 0.0,1.0),
217 SkyboxVertex( l, l,-l, 0, 0, 1, 0.0,0.0),
218 SkyboxVertex(-l, l,-l, 0, 0, 1, 1.0,0.0)) );
219 meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size()-6u);
220
221 // LEFT SIDE
222 meshes.push_back( BuildSingleQuadMesh(
223 SkyboxVertex( l,-l,-l, -1, 0, 0, 1.0,1.0),
224 SkyboxVertex( l,-l, l, -1, 0, 0, 0.0,1.0),
225 SkyboxVertex( l, l, l, -1, 0, 0, 0.0,0.0),
226 SkyboxVertex( l, l,-l, -1, 0, 0, 1.0,0.0)) );
227 meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size()-5u);
228
229 // BACK SIDE
230 meshes.push_back( BuildSingleQuadMesh(
231 SkyboxVertex( l,-l, l, 0, 0, -1, 1.0,1.0),
232 SkyboxVertex(-l,-l, l, 0, 0, -1, 0.0,1.0),
233 SkyboxVertex(-l, l, l, 0, 0, -1, 0.0,0.0),
234 SkyboxVertex( l, l, l, 0, 0, -1, 1.0,0.0)) );
235 meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size()-4u);
236
237 // RIGHT SIDE
238 meshes.push_back( BuildSingleQuadMesh(
239 SkyboxVertex(-l,-l, l, 1, 0, 0, 1.0,1.0),
240 SkyboxVertex(-l,-l,-l, 1, 0, 0, 0.0,1.0),
241 SkyboxVertex(-l, l,-l, 1, 0, 0, 0.0,0.0),
242 SkyboxVertex(-l, l, l, 1, 0, 0, 1.0,0.0)) );
243 meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size()-3u);
244
245 // TOP SIDE
246 meshes.push_back( BuildSingleQuadMesh(
247 SkyboxVertex( l, l,-l, 0, -1, 0, 1.0,1.0),
248 SkyboxVertex( l, l, l, 0, -1, 0, 0.0,1.0),
249 SkyboxVertex(-l, l, l, 0, -1, 0, 0.0,0.0),
250 SkyboxVertex(-l, l,-l, 0, -1, 0, 1.0,0.0)) );
251 meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size()-2u);
252
253 // BOTTOM SIDE
254 meshes.push_back( BuildSingleQuadMesh(
255 SkyboxVertex( l,-l, l, 0, 1, 0, 0.0,0.0),
256 SkyboxVertex( l,-l,-l, 0, 1, 0, 1.0,0.0),
257 SkyboxVertex(-l,-l,-l, 0, 1, 0, 1.0,1.0),
258 SkyboxVertex(-l,-l, l, 0, 1, 0, 0.0,1.0)) );
259 meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size()-1u);
260}
261
262// ------------------------------------------------------------------------------------------------
263void IRRImporter::CopyMaterial(std::vector<aiMaterial*>& materials,
264 std::vector< std::pair<aiMaterial*, unsigned int> >& inmaterials,
265 unsigned int& defMatIdx,
266 aiMesh* mesh)
267{
268 if (inmaterials.empty()) {
269 // Do we have a default material? If not we need to create one
270 if (UINT_MAX == defMatIdx)
271 {
272 defMatIdx = (unsigned int)materials.size();
273 //TODO: add this materials to someone?
274 /*aiMaterial* mat = new aiMaterial();
275
276 aiString s;
277 s.Set(AI_DEFAULT_MATERIAL_NAME);
278 mat->AddProperty(&s,AI_MATKEY_NAME);
279
280 aiColor3D c(0.6f,0.6f,0.6f);
281 mat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE);*/
282 }
283 mesh->mMaterialIndex = defMatIdx;
284 return;
285 }
286 else if (inmaterials.size() > 1) {
287 DefaultLogger::get()->info("IRR: Skipping additional materials");
288 }
289
290 mesh->mMaterialIndex = (unsigned int)materials.size();
291 materials.push_back(inmaterials[0].first);
292}
293
294
295// ------------------------------------------------------------------------------------------------
296inline int ClampSpline(int idx, int size)
297{
298 return ( idx<0 ? size+idx : ( idx>=size ? idx-size : idx ) );
299}
300
301// ------------------------------------------------------------------------------------------------
302inline void FindSuitableMultiple(int& angle)
303{
304 if (angle < 3)angle = 3;
305 else if (angle < 10) angle = 10;
306 else if (angle < 20) angle = 20;
307 else if (angle < 30) angle = 30;
308 else
309 {
310 }
311}
312
313// ------------------------------------------------------------------------------------------------
314void IRRImporter::ComputeAnimations(Node* root, aiNode* real, std::vector<aiNodeAnim*>& anims)
315{
316 ai_assert(NULL != root && NULL != real);
317
318 // XXX totally WIP - doesn't produce proper results, need to evaluate
319 // whether there's any use for Irrlicht's proprietary scene format
320 // outside Irrlicht ...
321
322 if (root->animators.empty()) {
323 return;
324 }
325 unsigned int total = 0;
326 for (std::list<Animator>::iterator it = root->animators.begin();it != root->animators.end(); ++it) {
327 if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) {
328 DefaultLogger::get()->warn("IRR: Skipping unknown or unsupported animator");
329 continue;
330 }
331 ++total;
332 }
333 if (!total)return;
334 else if (1 == total) {
335 DefaultLogger::get()->warn("IRR: Adding dummy nodes to simulate multiple animators");
336 }
337
338 // NOTE: 1 tick == i millisecond
339
340 unsigned int cur = 0;
341 for (std::list<Animator>::iterator it = root->animators.begin();
342 it != root->animators.end(); ++it)
343 {
344 if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER)continue;
345
346 Animator& in = *it ;
347 aiNodeAnim* anim = new aiNodeAnim();
348
349 if (cur != total-1) {
350 // Build a new name - a prefix instead of a suffix because it is
351 // easier to check against
352 anim->mNodeName.length = ::ai_snprintf(anim->mNodeName.data, MAXLEN,
353 "$INST_DUMMY_%i_%s",total-1,
354 (root->name.length() ? root->name.c_str() : ""));
355
356 // we'll also need to insert a dummy in the node hierarchy.
357 aiNode* dummy = new aiNode();
358
359 for (unsigned int i = 0; i < real->mParent->mNumChildren;++i)
360 if (real->mParent->mChildren[i] == real)
361 real->mParent->mChildren[i] = dummy;
362
363 dummy->mParent = real->mParent;
364 dummy->mName = anim->mNodeName;
365
366 dummy->mNumChildren = 1;
367 dummy->mChildren = new aiNode*[dummy->mNumChildren];
368 dummy->mChildren[0] = real;
369
370 // the transformation matrix of the dummy node is the identity
371
372 real->mParent = dummy;
373 }
374 else anim->mNodeName.Set(root->name);
375 ++cur;
376
377 switch (in.type) {
378 case Animator::ROTATION:
379 {
380 // -----------------------------------------------------
381 // find out how long a full rotation will take
382 // This is the least common multiple of 360.f and all
383 // three euler angles. Although we'll surely find a
384 // possible multiple (haha) it could be somewhat large
385 // for our purposes. So we need to modify the angles
386 // here in order to get good results.
387 // -----------------------------------------------------
388 int angles[3];
389 angles[0] = (int)(in.direction.x*100);
390 angles[1] = (int)(in.direction.y*100);
391 angles[2] = (int)(in.direction.z*100);
392
393 angles[0] %= 360;
394 angles[1] %= 360;
395 angles[2] %= 360;
396
397 if ( (angles[0]*angles[1]) != 0 && (angles[1]*angles[2]) != 0 )
398 {
399 FindSuitableMultiple(angles[0]);
400 FindSuitableMultiple(angles[1]);
401 FindSuitableMultiple(angles[2]);
402 }
403
404 int lcm = 360;
405
406 if (angles[0])
407 lcm = Math::lcm(lcm,angles[0]);
408
409 if (angles[1])
410 lcm = Math::lcm(lcm,angles[1]);
411
412 if (angles[2])
413 lcm = Math::lcm(lcm,angles[2]);
414
415 if (360 == lcm)
416 break;
417
418#if 0
419 // This can be a division through zero, but we don't care
420 float f1 = (float)lcm / angles[0];
421 float f2 = (float)lcm / angles[1];
422 float f3 = (float)lcm / angles[2];
423#endif
424
425 // find out how many time units we'll need for the finest
426 // track (in seconds) - this defines the number of output
427 // keys (fps * seconds)
428 float max = 0.f;
429 if (angles[0])
430 max = (float)lcm / angles[0];
431 if (angles[1])
432 max = std::max(max, (float)lcm / angles[1]);
433 if (angles[2])
434 max = std::max(max, (float)lcm / angles[2]);
435
436 anim->mNumRotationKeys = (unsigned int)(max*fps);
437 anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
438
439 // begin with a zero angle
440 aiVector3D angle;
441 for (unsigned int i = 0; i < anim->mNumRotationKeys;++i)
442 {
443 // build the quaternion for the given euler angles
444 aiQuatKey& q = anim->mRotationKeys[i];
445
446 q.mValue = aiQuaternion(angle.x, angle.y, angle.z);
447 q.mTime = (double)i;
448
449 // increase the angle
450 angle += in.direction;
451 }
452
453 // This animation is repeated and repeated ...
454 anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
455 }
456 break;
457
458 case Animator::FLY_CIRCLE:
459 {
460 // -----------------------------------------------------
461 // Find out how much time we'll need to perform a
462 // full circle.
463 // -----------------------------------------------------
464 const double seconds = (1. / in.speed) / 1000.;
465 const double tdelta = 1000. / fps;
466
467 anim->mNumPositionKeys = (unsigned int) (fps * seconds);
468 anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
469
470 // from Irrlicht, what else should we do than copying it?
471 aiVector3D vecU,vecV;
472 if (in.direction.y) {
473 vecV = aiVector3D(50,0,0) ^ in.direction;
474 }
475 else vecV = aiVector3D(0,50,00) ^ in.direction;
476 vecV.Normalize();
477 vecU = (vecV ^ in.direction).Normalize();
478
479 // build the output keys
480 for (unsigned int i = 0; i < anim->mNumPositionKeys;++i) {
481 aiVectorKey& key = anim->mPositionKeys[i];
482 key.mTime = i * tdelta;
483
484 const ai_real t = (ai_real) ( in.speed * key.mTime );
485 key.mValue = in.circleCenter + in.circleRadius * ((vecU * std::cos(t)) + (vecV * std::sin(t)));
486 }
487
488 // This animation is repeated and repeated ...
489 anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
490 }
491 break;
492
493 case Animator::FLY_STRAIGHT:
494 {
495 anim->mPostState = anim->mPreState = (in.loop ? aiAnimBehaviour_REPEAT : aiAnimBehaviour_CONSTANT);
496 const double seconds = in.timeForWay / 1000.;
497 const double tdelta = 1000. / fps;
498
499 anim->mNumPositionKeys = (unsigned int) (fps * seconds);
500 anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
501
502 aiVector3D diff = in.direction - in.circleCenter;
503 const ai_real lengthOfWay = diff.Length();
504 diff.Normalize();
505
506 const double timeFactor = lengthOfWay / in.timeForWay;
507
508 // build the output keys
509 for (unsigned int i = 0; i < anim->mNumPositionKeys;++i) {
510 aiVectorKey& key = anim->mPositionKeys[i];
511 key.mTime = i * tdelta;
512 key.mValue = in.circleCenter + diff * ai_real(timeFactor * key.mTime);
513 }
514 }
515 break;
516
517 case Animator::FOLLOW_SPLINE:
518 {
519 // repeat outside the defined time range
520 anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
521 const int size = (int)in.splineKeys.size();
522 if (!size) {
523 // We have no point in the spline. That's bad. Really bad.
524 DefaultLogger::get()->warn("IRR: Spline animators with no points defined");
525
526 delete anim;anim = NULL;
527 break;
528 }
529 else if (size == 1) {
530 // We have just one point in the spline so we don't need the full calculation
531 anim->mNumPositionKeys = 1;
532 anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
533
534 anim->mPositionKeys[0].mValue = in.splineKeys[0].mValue;
535 anim->mPositionKeys[0].mTime = 0.f;
536 break;
537 }
538
539 unsigned int ticksPerFull = 15;
540 anim->mNumPositionKeys = (unsigned int) ( ticksPerFull * fps );
541 anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
542
543 for (unsigned int i = 0; i < anim->mNumPositionKeys;++i)
544 {
545 aiVectorKey& key = anim->mPositionKeys[i];
546
547 const ai_real dt = (i * in.speed * ai_real( 0.001 ) );
548 const ai_real u = dt - std::floor(dt);
549 const int idx = (int)std::floor(dt) % size;
550
551 // get the 4 current points to evaluate the spline
552 const aiVector3D& p0 = in.splineKeys[ ClampSpline( idx - 1, size ) ].mValue;
553 const aiVector3D& p1 = in.splineKeys[ ClampSpline( idx + 0, size ) ].mValue;
554 const aiVector3D& p2 = in.splineKeys[ ClampSpline( idx + 1, size ) ].mValue;
555 const aiVector3D& p3 = in.splineKeys[ ClampSpline( idx + 2, size ) ].mValue;
556
557 // compute polynomials
558 const ai_real u2 = u*u;
559 const ai_real u3 = u2*2;
560
561 const ai_real h1 = ai_real( 2.0 ) * u3 - ai_real( 3.0 ) * u2 + ai_real( 1.0 );
562 const ai_real h2 = ai_real( -2.0 ) * u3 + ai_real( 3.0 ) * u3;
563 const ai_real h3 = u3 - ai_real( 2.0 ) * u3;
564 const ai_real h4 = u3 - u2;
565
566 // compute the spline tangents
567 const aiVector3D t1 = ( p2 - p0 ) * in.tightness;
568 aiVector3D t2 = ( p3 - p1 ) * in.tightness;
569
570 // and use them to get the interpolated point
571 t2 = (h1 * p1 + p2 * h2 + t1 * h3 + h4 * t2);
572
573 // build a simple translation matrix from it
574 key.mValue = t2;
575 key.mTime = (double) i;
576 }
577 }
578 break;
579 default:
580 // UNKNOWN , OTHER
581 break;
582 };
583 if (anim) {
584 anims.push_back(anim);
585 ++total;
586 }
587 }
588}
589
590// ------------------------------------------------------------------------------------------------
591// This function is maybe more generic than we'd need it here
592void SetupMapping (aiMaterial* mat, aiTextureMapping mode, const aiVector3D& axis = aiVector3D(0.f,0.f,-1.f))
593{
594 // Check whether there are texture properties defined - setup
595 // the desired texture mapping mode for all of them and ignore
596 // all UV settings we might encounter. WE HAVE NO UVS!
597
598 std::vector<aiMaterialProperty*> p;
599 p.reserve(mat->mNumProperties+1);
600
601 for (unsigned int i = 0; i < mat->mNumProperties;++i)
602 {
603 aiMaterialProperty* prop = mat->mProperties[i];
604 if (!::strcmp( prop->mKey.data, "$tex.file")) {
605 // Setup the mapping key
606 aiMaterialProperty* m = new aiMaterialProperty();
607 m->mKey.Set("$tex.mapping");
608 m->mIndex = prop->mIndex;
609 m->mSemantic = prop->mSemantic;
610 m->mType = aiPTI_Integer;
611
612 m->mDataLength = 4;
613 m->mData = new char[4];
614 *((int*)m->mData) = mode;
615
616 p.push_back(prop);
617 p.push_back(m);
618
619 // Setup the mapping axis
620 if (mode == aiTextureMapping_CYLINDER || mode == aiTextureMapping_PLANE || mode == aiTextureMapping_SPHERE) {
621 m = new aiMaterialProperty();
622 m->mKey.Set("$tex.mapaxis");
623 m->mIndex = prop->mIndex;
624 m->mSemantic = prop->mSemantic;
625 m->mType = aiPTI_Float;
626
627 m->mDataLength = 12;
628 m->mData = new char[12];
629 *((aiVector3D*)m->mData) = axis;
630 p.push_back(m);
631 }
632 }
633 else if (! ::strcmp( prop->mKey.data, "$tex.uvwsrc")) {
634 delete mat->mProperties[i];
635 }
636 else p.push_back(prop);
637 }
638
639 if (p.empty())return;
640
641 // rebuild the output array
642 if (p.size() > mat->mNumAllocated) {
643 delete[] mat->mProperties;
644 mat->mProperties = new aiMaterialProperty*[p.size()*2];
645
646 mat->mNumAllocated = static_cast<unsigned int>(p.size()*2);
647 }
648 mat->mNumProperties = (unsigned int)p.size();
649 ::memcpy(mat->mProperties,&p[0],sizeof(void*)*mat->mNumProperties);
650}
651
652// ------------------------------------------------------------------------------------------------
653void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
654 BatchLoader& batch,
655 std::vector<aiMesh*>& meshes,
656 std::vector<aiNodeAnim*>& anims,
657 std::vector<AttachmentInfo>& attach,
658 std::vector<aiMaterial*>& materials,
659 unsigned int& defMatIdx)
660{
661 unsigned int oldMeshSize = (unsigned int)meshes.size();
662 //unsigned int meshTrafoAssign = 0;
663
664 // Now determine the type of the node
665 switch (root->type)
666 {
667 case Node::ANIMMESH:
668 case Node::MESH:
669 {
670 if (!root->meshPath.length())
671 break;
672
673 // Get the loaded mesh from the scene and add it to
674 // the list of all scenes to be attached to the
675 // graph we're currently building
676 aiScene* scene = batch.GetImport(root->id);
677 if (!scene) {
678 DefaultLogger::get()->error("IRR: Unable to load external file: " + root->meshPath);
679 break;
680 }
681 attach.push_back(AttachmentInfo(scene,rootOut));
682
683 // Now combine the material we've loaded for this mesh
684 // with the real materials we got from the file. As we
685 // don't execute any pp-steps on the file, the numbers
686 // should be equal. If they are not, we can impossibly
687 // do this ...
688 if (root->materials.size() != (unsigned int)scene->mNumMaterials) {
689 DefaultLogger::get()->warn("IRR: Failed to match imported materials "
690 "with the materials found in the IRR scene file");
691
692 break;
693 }
694 for (unsigned int i = 0; i < scene->mNumMaterials;++i) {
695 // Delete the old material, we don't need it anymore
696 delete scene->mMaterials[i];
697
698 std::pair<aiMaterial*, unsigned int>& src = root->materials[i];
699 scene->mMaterials[i] = src.first;
700 }
701
702 // NOTE: Each mesh should have exactly one material assigned,
703 // but we do it in a separate loop if this behaviour changes
704 // in future.
705 for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
706 // Process material flags
707 aiMesh* mesh = scene->mMeshes[i];
708
709
710 // If "trans_vertex_alpha" mode is enabled, search all vertex colors
711 // and check whether they have a common alpha value. This is quite
712 // often the case so we can simply extract it to a shared oacity
713 // value.
714 std::pair<aiMaterial*, unsigned int>& src = root->materials[mesh->mMaterialIndex];
715 aiMaterial* mat = (aiMaterial*)src.first;
716
717 if (mesh->HasVertexColors(0) && src.second & AI_IRRMESH_MAT_trans_vertex_alpha)
718 {
719 bool bdo = true;
720 for (unsigned int a = 1; a < mesh->mNumVertices;++a) {
721
722 if (mesh->mColors[0][a].a != mesh->mColors[0][a-1].a) {
723 bdo = false;
724 break;
725 }
726 }
727 if (bdo) {
728 DefaultLogger::get()->info("IRR: Replacing mesh vertex alpha with common opacity");
729
730 for (unsigned int a = 0; a < mesh->mNumVertices;++a)
731 mesh->mColors[0][a].a = 1.f;
732
733 mat->AddProperty(& mesh->mColors[0][0].a, 1, AI_MATKEY_OPACITY);
734 }
735 }
736
737 // If we have a second texture coordinate set and a second texture
738 // (either lightmap, normalmap, 2layered material) we need to
739 // setup the correct UV index for it. The texture can either
740 // be diffuse (lightmap & 2layer) or a normal map (normal & parallax)
741 if (mesh->HasTextureCoords(1)) {
742
743 int idx = 1;
744 if (src.second & (AI_IRRMESH_MAT_solid_2layer | AI_IRRMESH_MAT_lightmap)) {
745 mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_DIFFUSE(0));
746 }
747 else if (src.second & AI_IRRMESH_MAT_normalmap_solid) {
748 mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_NORMALS(0));
749 }
750 }
751 }
752 }
753 break;
754
755 case Node::LIGHT:
756 case Node::CAMERA:
757
758 // We're already finished with lights and cameras
759 break;
760
761
762 case Node::SPHERE:
763 {
764 // Generate the sphere model. Our input parameter to
765 // the sphere generation algorithm is the number of
766 // subdivisions of each triangle - but here we have
767 // the number of poylgons on a specific axis. Just
768 // use some hardcoded limits to approximate this ...
769 unsigned int mul = root->spherePolyCountX*root->spherePolyCountY;
770 if (mul < 100)mul = 2;
771 else if (mul < 300)mul = 3;
772 else mul = 4;
773
774 meshes.push_back(StandardShapes::MakeMesh(mul,
775 &StandardShapes::MakeSphere));
776
777 // Adjust scaling
778 root->scaling *= root->sphereRadius/2;
779
780 // Copy one output material
781 CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
782
783 // Now adjust this output material - if there is a first texture
784 // set, setup spherical UV mapping around the Y axis.
785 SetupMapping ( (aiMaterial*) materials.back(), aiTextureMapping_SPHERE);
786 }
787 break;
788
789 case Node::CUBE:
790 {
791 // Generate an unit cube first
792 meshes.push_back(StandardShapes::MakeMesh(
793 &StandardShapes::MakeHexahedron));
794
795 // Adjust scaling
796 root->scaling *= root->sphereRadius;
797
798 // Copy one output material
799 CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
800
801 // Now adjust this output material - if there is a first texture
802 // set, setup cubic UV mapping
803 SetupMapping ( (aiMaterial*) materials.back(), aiTextureMapping_BOX );
804 }
805 break;
806
807
808 case Node::SKYBOX:
809 {
810 // A skybox is defined by six materials
811 if (root->materials.size() < 6) {
812 DefaultLogger::get()->error("IRR: There should be six materials for a skybox");
813 break;
814 }
815
816 // copy those materials and generate 6 meshes for our new skybox
817 materials.reserve(materials.size() + 6);
818 for (unsigned int i = 0; i < 6;++i)
819 materials.insert(materials.end(),root->materials[i].first);
820
821 BuildSkybox(meshes,materials);
822
823 // *************************************************************
824 // Skyboxes will require a different code path for rendering,
825 // so there must be a way for the user to add special support
826 // for IRR skyboxes. We add a 'IRR.SkyBox_' prefix to the node.
827 // *************************************************************
828 root->name = "IRR.SkyBox_" + root->name;
829 DefaultLogger::get()->info("IRR: Loading skybox, this will "
830 "require special handling to be displayed correctly");
831 }
832 break;
833
834 case Node::TERRAIN:
835 {
836 // to support terrains, we'd need to have a texture decoder
837 DefaultLogger::get()->error("IRR: Unsupported node - TERRAIN");
838 }
839 break;
840 default:
841 // DUMMY
842 break;
843 };
844
845 // Check whether we added a mesh (or more than one ...). In this case
846 // we'll also need to attach it to the node
847 if (oldMeshSize != (unsigned int) meshes.size()) {
848
849 rootOut->mNumMeshes = (unsigned int)meshes.size() - oldMeshSize;
850 rootOut->mMeshes = new unsigned int[rootOut->mNumMeshes];
851
852 for (unsigned int a = 0; a < rootOut->mNumMeshes;++a) {
853 rootOut->mMeshes[a] = oldMeshSize+a;
854 }
855 }
856
857 // Setup the name of this node
858 rootOut->mName.Set(root->name);
859
860 // Now compute the final local transformation matrix of the
861 // node from the given translation, rotation and scaling values.
862 // (the rotation is given in Euler angles, XYZ order)
863 //std::swap((float&)root->rotation.z,(float&)root->rotation.y);
864 rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation) );
865
866 // apply scaling
867 aiMatrix4x4& mat = rootOut->mTransformation;
868 mat.a1 *= root->scaling.x;
869 mat.b1 *= root->scaling.x;
870 mat.c1 *= root->scaling.x;
871 mat.a2 *= root->scaling.y;
872 mat.b2 *= root->scaling.y;
873 mat.c2 *= root->scaling.y;
874 mat.a3 *= root->scaling.z;
875 mat.b3 *= root->scaling.z;
876 mat.c3 *= root->scaling.z;
877
878 // apply translation
879 mat.a4 += root->position.x;
880 mat.b4 += root->position.y;
881 mat.c4 += root->position.z;
882
883 // now compute animations for the node
884 ComputeAnimations(root,rootOut, anims);
885
886 // Add all children recursively. First allocate enough storage
887 // for them, then call us again
888 rootOut->mNumChildren = (unsigned int)root->children.size();
889 if (rootOut->mNumChildren) {
890
891 rootOut->mChildren = new aiNode*[rootOut->mNumChildren];
892 for (unsigned int i = 0; i < rootOut->mNumChildren;++i) {
893
894 aiNode* node = rootOut->mChildren[i] = new aiNode();
895 node->mParent = rootOut;
896 GenerateGraph(root->children[i],node,scene,batch,meshes,
897 anims,attach,materials,defMatIdx);
898 }
899 }
900}
901
902// ------------------------------------------------------------------------------------------------
903// Imports the given file into the given scene structure.
904void IRRImporter::InternReadFile( const std::string& pFile,
905 aiScene* pScene, IOSystem* pIOHandler)
906{
907 std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
908
909 // Check whether we can read from the file
910 if( file.get() == NULL)
911 throw DeadlyImportError( "Failed to open IRR file " + pFile + "");
912
913 // Construct the irrXML parser
914 CIrrXML_IOStreamReader st(file.get());
915 reader = createIrrXMLReader((IFileReadCallBack*) &st);
916
917 // The root node of the scene
918 Node* root = new Node(Node::DUMMY);
919 root->parent = NULL;
920 root->name = "<IRRSceneRoot>";
921
922 // Current node parent
923 Node* curParent = root;
924
925 // Scenegraph node we're currently working on
926 Node* curNode = NULL;
927
928 // List of output cameras
929 std::vector<aiCamera*> cameras;
930
931 // List of output lights
932 std::vector<aiLight*> lights;
933
934 // Batch loader used to load external models
935 BatchLoader batch(pIOHandler);
936// batch.SetBasePath(pFile);
937
938 cameras.reserve(5);
939 lights.reserve(5);
940
941 bool inMaterials = false, inAnimator = false;
942 unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0, guessedMatCnt = 0;
943
944 // Parse the XML file
945 while (reader->read()) {
946 switch (reader->getNodeType()) {
947 case EXN_ELEMENT:
948
949 if (!ASSIMP_stricmp(reader->getNodeName(),"node")) {
950 // ***********************************************************************
951 /* What we're going to do with the node depends
952 * on its type:
953 *
954 * "mesh" - Load a mesh from an external file
955 * "cube" - Generate a cube
956 * "skybox" - Generate a skybox
957 * "light" - A light source
958 * "sphere" - Generate a sphere mesh
959 * "animatedMesh" - Load an animated mesh from an external file
960 * and join its animation channels with ours.
961 * "empty" - A dummy node
962 * "camera" - A camera
963 * "terrain" - a terrain node (data comes from a heightmap)
964 * "billboard", ""
965 *
966 * Each of these nodes can be animated and all can have multiple
967 * materials assigned (except lights, cameras and dummies, of course).
968 */
969 // ***********************************************************************
970 const char* sz = reader->getAttributeValueSafe("type");
971 Node* nd;
972 if (!ASSIMP_stricmp(sz,"mesh") || !ASSIMP_stricmp(sz,"octTree")) {
973 // OctTree's and meshes are treated equally
974 nd = new Node(Node::MESH);
975 }
976 else if (!ASSIMP_stricmp(sz,"cube")) {
977 nd = new Node(Node::CUBE);
978 ++guessedMeshCnt;
979 // meshes.push_back(StandardShapes::MakeMesh(&StandardShapes::MakeHexahedron));
980 }
981 else if (!ASSIMP_stricmp(sz,"skybox")) {
982 nd = new Node(Node::SKYBOX);
983 guessedMeshCnt += 6;
984 }
985 else if (!ASSIMP_stricmp(sz,"camera")) {
986 nd = new Node(Node::CAMERA);
987
988 // Setup a temporary name for the camera
989 aiCamera* cam = new aiCamera();
990 cam->mName.Set( nd->name );
991 cameras.push_back(cam);
992 }
993 else if (!ASSIMP_stricmp(sz,"light")) {
994 nd = new Node(Node::LIGHT);
995
996 // Setup a temporary name for the light
997 aiLight* cam = new aiLight();
998 cam->mName.Set( nd->name );
999 lights.push_back(cam);
1000 }
1001 else if (!ASSIMP_stricmp(sz,"sphere")) {
1002 nd = new Node(Node::SPHERE);
1003 ++guessedMeshCnt;
1004 }
1005 else if (!ASSIMP_stricmp(sz,"animatedMesh")) {
1006 nd = new Node(Node::ANIMMESH);
1007 }
1008 else if (!ASSIMP_stricmp(sz,"empty")) {
1009 nd = new Node(Node::DUMMY);
1010 }
1011 else if (!ASSIMP_stricmp(sz,"terrain")) {
1012 nd = new Node(Node::TERRAIN);
1013 }
1014 else if (!ASSIMP_stricmp(sz,"billBoard")) {
1015 // We don't support billboards, so ignore them
1016 DefaultLogger::get()->error("IRR: Billboards are not supported by Assimp");
1017 nd = new Node(Node::DUMMY);
1018 }
1019 else {
1020 DefaultLogger::get()->warn("IRR: Found unknown node: " + std::string(sz));
1021
1022 /* We skip the contents of nodes we don't know.
1023 * We parse the transformation and all animators
1024 * and skip the rest.
1025 */
1026 nd = new Node(Node::DUMMY);
1027 }
1028
1029 /* Attach the newly created node to the scenegraph
1030 */
1031 curNode = nd;
1032 nd->parent = curParent;
1033 curParent->children.push_back(nd);
1034 }
1035 else if (!ASSIMP_stricmp(reader->getNodeName(),"materials")) {
1036 inMaterials = true;
1037 }
1038 else if (!ASSIMP_stricmp(reader->getNodeName(),"animators")) {
1039 inAnimator = true;
1040 }
1041 else if (!ASSIMP_stricmp(reader->getNodeName(),"attributes")) {
1042 /* We should have a valid node here
1043 * FIX: no ... the scene root node is also contained in an attributes block
1044 */
1045 if (!curNode) {
1046#if 0
1047 DefaultLogger::get()->error("IRR: Encountered <attributes> element, but "
1048 "there is no node active");
1049#endif
1050 continue;
1051 }
1052
1053 Animator* curAnim = NULL;
1054
1055 // Materials can occur for nearly any type of node
1056 if (inMaterials && curNode->type != Node::DUMMY) {
1057 /* This is a material description - parse it!
1058 */
1059 curNode->materials.push_back(std::pair< aiMaterial*, unsigned int > () );
1060 std::pair< aiMaterial*, unsigned int >& p = curNode->materials.back();
1061
1062 p.first = ParseMaterial(p.second);
1063
1064 ++guessedMatCnt;
1065 continue;
1066 }
1067 else if (inAnimator) {
1068 /* This is an animation path - add a new animator
1069 * to the list.
1070 */
1071 curNode->animators.push_back(Animator());
1072 curAnim = & curNode->animators.back();
1073
1074 ++guessedAnimCnt;
1075 }
1076
1077 /* Parse all elements in the attributes block
1078 * and process them.
1079 */
1080 while (reader->read()) {
1081 if (reader->getNodeType() == EXN_ELEMENT) {
1082 if (!ASSIMP_stricmp(reader->getNodeName(),"vector3d")) {
1083 VectorProperty prop;
1084 ReadVectorProperty(prop);
1085
1086 if (inAnimator) {
1087 if (curAnim->type == Animator::ROTATION && prop.name == "Rotation") {
1088 // We store the rotation euler angles in 'direction'
1089 curAnim->direction = prop.value;
1090 }
1091 else if (curAnim->type == Animator::FOLLOW_SPLINE) {
1092 // Check whether the vector follows the PointN naming scheme,
1093 // here N is the ONE-based index of the point
1094 if (prop.name.length() >= 6 && prop.name.substr(0,5) == "Point") {
1095 // Add a new key to the list
1096 curAnim->splineKeys.push_back(aiVectorKey());
1097 aiVectorKey& key = curAnim->splineKeys.back();
1098
1099 // and parse its properties
1100 key.mValue = prop.value;
1101 key.mTime = strtoul10(&prop.name[5]);
1102 }
1103 }
1104 else if (curAnim->type == Animator::FLY_CIRCLE) {
1105 if (prop.name == "Center") {
1106 curAnim->circleCenter = prop.value;
1107 }
1108 else if (prop.name == "Direction") {
1109 curAnim->direction = prop.value;
1110
1111 // From Irrlicht's source - a workaround for backward compatibility with Irrlicht 1.1
1112 if (curAnim->direction == aiVector3D()) {
1113 curAnim->direction = aiVector3D(0.f,1.f,0.f);
1114 }
1115 else curAnim->direction.Normalize();
1116 }
1117 }
1118 else if (curAnim->type == Animator::FLY_STRAIGHT) {
1119 if (prop.name == "Start") {
1120 // We reuse the field here
1121 curAnim->circleCenter = prop.value;
1122 }
1123 else if (prop.name == "End") {
1124 // We reuse the field here
1125 curAnim->direction = prop.value;
1126 }
1127 }
1128 }
1129 else {
1130 if (prop.name == "Position") {
1131 curNode->position = prop.value;
1132 }
1133 else if (prop.name == "Rotation") {
1134 curNode->rotation = prop.value;
1135 }
1136 else if (prop.name == "Scale") {
1137 curNode->scaling = prop.value;
1138 }
1139 else if (Node::CAMERA == curNode->type)
1140 {
1141 aiCamera* cam = cameras.back();
1142 if (prop.name == "Target") {
1143 cam->mLookAt = prop.value;
1144 }
1145 else if (prop.name == "UpVector") {
1146 cam->mUp = prop.value;
1147 }
1148 }
1149 }
1150 }
1151 else if (!ASSIMP_stricmp(reader->getNodeName(),"bool")) {
1152 BoolProperty prop;
1153 ReadBoolProperty(prop);
1154
1155 if (inAnimator && curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") {
1156 curAnim->loop = prop.value;
1157 }
1158 }
1159 else if (!ASSIMP_stricmp(reader->getNodeName(),"float")) {
1160 FloatProperty prop;
1161 ReadFloatProperty(prop);
1162
1163 if (inAnimator) {
1164 // The speed property exists for several animators
1165 if (prop.name == "Speed") {
1166 curAnim->speed = prop.value;
1167 }
1168 else if (curAnim->type == Animator::FLY_CIRCLE && prop.name == "Radius") {
1169 curAnim->circleRadius = prop.value;
1170 }
1171 else if (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness") {
1172 curAnim->tightness = prop.value;
1173 }
1174 }
1175 else {
1176 if (prop.name == "FramesPerSecond" && Node::ANIMMESH == curNode->type) {
1177 curNode->framesPerSecond = prop.value;
1178 }
1179 else if (Node::CAMERA == curNode->type) {
1180 /* This is the vertical, not the horizontal FOV.
1181 * We need to compute the right FOV from the
1182 * screen aspect which we don't know yet.
1183 */
1184 if (prop.name == "Fovy") {
1185 cameras.back()->mHorizontalFOV = prop.value;
1186 }
1187 else if (prop.name == "Aspect") {
1188 cameras.back()->mAspect = prop.value;
1189 }
1190 else if (prop.name == "ZNear") {
1191 cameras.back()->mClipPlaneNear = prop.value;
1192 }
1193 else if (prop.name == "ZFar") {
1194 cameras.back()->mClipPlaneFar = prop.value;
1195 }
1196 }
1197 else if (Node::LIGHT == curNode->type) {
1198 /* Additional light information
1199 */
1200 if (prop.name == "Attenuation") {
1201 lights.back()->mAttenuationLinear = prop.value;
1202 }
1203 else if (prop.name == "OuterCone") {
1204 lights.back()->mAngleOuterCone = AI_DEG_TO_RAD( prop.value );
1205 }
1206 else if (prop.name == "InnerCone") {
1207 lights.back()->mAngleInnerCone = AI_DEG_TO_RAD( prop.value );
1208 }
1209 }
1210 // radius of the sphere to be generated -
1211 // or alternatively, size of the cube
1212 else if ((Node::SPHERE == curNode->type && prop.name == "Radius")
1213 || (Node::CUBE == curNode->type && prop.name == "Size" )) {
1214
1215 curNode->sphereRadius = prop.value;
1216 }
1217 }
1218 }
1219 else if (!ASSIMP_stricmp(reader->getNodeName(),"int")) {
1220 IntProperty prop;
1221 ReadIntProperty(prop);
1222
1223 if (inAnimator) {
1224 if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
1225 curAnim->timeForWay = prop.value;
1226 }
1227 }
1228 else {
1229 // sphere polgon numbers in each direction
1230 if (Node::SPHERE == curNode->type) {
1231
1232 if (prop.name == "PolyCountX") {
1233 curNode->spherePolyCountX = prop.value;
1234 }
1235 else if (prop.name == "PolyCountY") {
1236 curNode->spherePolyCountY = prop.value;
1237 }
1238 }
1239 }
1240 }
1241 else if (!ASSIMP_stricmp(reader->getNodeName(),"string") ||!ASSIMP_stricmp(reader->getNodeName(),"enum")) {
1242 StringProperty prop;
1243 ReadStringProperty(prop);
1244 if (prop.value.length()) {
1245 if (prop.name == "Name") {
1246 curNode->name = prop.value;
1247
1248 /* If we're either a camera or a light source
1249 * we need to update the name in the aiLight/
1250 * aiCamera structure, too.
1251 */
1252 if (Node::CAMERA == curNode->type) {
1253 cameras.back()->mName.Set(prop.value);
1254 }
1255 else if (Node::LIGHT == curNode->type) {
1256 lights.back()->mName.Set(prop.value);
1257 }
1258 }
1259 else if (Node::LIGHT == curNode->type && "LightType" == prop.name)
1260 {
1261 if (prop.value == "Spot")
1262 lights.back()->mType = aiLightSource_SPOT;
1263 else if (prop.value == "Point")
1264 lights.back()->mType = aiLightSource_POINT;
1265 else if (prop.value == "Directional")
1266 lights.back()->mType = aiLightSource_DIRECTIONAL;
1267 else
1268 {
1269 // We won't pass the validation with aiLightSourceType_UNDEFINED,
1270 // so we remove the light and replace it with a silly dummy node
1271 delete lights.back();
1272 lights.pop_back();
1273 curNode->type = Node::DUMMY;
1274
1275 DefaultLogger::get()->error("Ignoring light of unknown type: " + prop.value);
1276 }
1277 }
1278 else if ((prop.name == "Mesh" && Node::MESH == curNode->type) ||
1279 Node::ANIMMESH == curNode->type)
1280 {
1281 /* This is the file name of the mesh - either
1282 * animated or not. We need to make sure we setup
1283 * the correct postprocessing settings here.
1284 */
1285 unsigned int pp = 0;
1286 BatchLoader::PropertyMap map;
1287
1288 /* If the mesh is a static one remove all animations from the impor data
1289 */
1290 if (Node::ANIMMESH != curNode->type) {
1291 pp |= aiProcess_RemoveComponent;
1292 SetGenericProperty<int>(map.ints,AI_CONFIG_PP_RVC_FLAGS,
1293 aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
1294 }
1295
1296 /* TODO: maybe implement the protection against recursive
1297 * loading calls directly in BatchLoader? The current
1298 * implementation is not absolutely safe. A LWS and an IRR
1299 * file referencing each other *could* cause the system to
1300 * recurse forever.
1301 */
1302
1303 const std::string extension = GetExtension(prop.value);
1304 if ("irr" == extension) {
1305 DefaultLogger::get()->error("IRR: Can't load another IRR file recursively");
1306 }
1307 else
1308 {
1309 curNode->id = batch.AddLoadRequest(prop.value,pp,&map);
1310 curNode->meshPath = prop.value;
1311 }
1312 }
1313 else if (inAnimator && prop.name == "Type")
1314 {
1315 // type of the animator
1316 if (prop.value == "rotation") {
1317 curAnim->type = Animator::ROTATION;
1318 }
1319 else if (prop.value == "flyCircle") {
1320 curAnim->type = Animator::FLY_CIRCLE;
1321 }
1322 else if (prop.value == "flyStraight") {
1323 curAnim->type = Animator::FLY_CIRCLE;
1324 }
1325 else if (prop.value == "followSpline") {
1326 curAnim->type = Animator::FOLLOW_SPLINE;
1327 }
1328 else {
1329 DefaultLogger::get()->warn("IRR: Ignoring unknown animator: "
1330 + prop.value);
1331
1332 curAnim->type = Animator::UNKNOWN;
1333 }
1334 }
1335 }
1336 }
1337 }
1338 else if (reader->getNodeType() == EXN_ELEMENT_END && !ASSIMP_stricmp(reader->getNodeName(),"attributes")) {
1339 break;
1340 }
1341 }
1342 }
1343 break;
1344
1345 case EXN_ELEMENT_END:
1346
1347 // If we reached the end of a node, we need to continue processing its parent
1348 if (!ASSIMP_stricmp(reader->getNodeName(),"node")) {
1349 if (!curNode) {
1350 // currently is no node set. We need to go
1351 // back in the node hierarchy
1352 if (!curParent) {
1353 curParent = root;
1354 DefaultLogger::get()->error("IRR: Too many closing <node> elements");
1355 }
1356 else curParent = curParent->parent;
1357 }
1358 else curNode = NULL;
1359 }
1360 // clear all flags
1361 else if (!ASSIMP_stricmp(reader->getNodeName(),"materials")) {
1362 inMaterials = false;
1363 }
1364 else if (!ASSIMP_stricmp(reader->getNodeName(),"animators")) {
1365 inAnimator = false;
1366 }
1367 break;
1368
1369 default:
1370 // GCC complains that not all enumeration values are handled
1371 break;
1372 }
1373 }
1374
1375 /* Now iterate through all cameras and compute their final (horizontal) FOV
1376 */
1377 for (aiCamera *cam : cameras) {
1378
1379 // screen aspect could be missing
1380 if (cam->mAspect) {
1381 cam->mHorizontalFOV *= cam->mAspect;
1382 }
1383 else DefaultLogger::get()->warn("IRR: Camera aspect is not given, can't compute horizontal FOV");
1384 }
1385
1386 batch.LoadAll();
1387
1388 /* Allocate a tempoary scene data structure
1389 */
1390 aiScene* tempScene = new aiScene();
1391 tempScene->mRootNode = new aiNode();
1392 tempScene->mRootNode->mName.Set("<IRRRoot>");
1393
1394 /* Copy the cameras to the output array
1395 */
1396 if (!cameras.empty()) {
1397 tempScene->mNumCameras = (unsigned int)cameras.size();
1398 tempScene->mCameras = new aiCamera*[tempScene->mNumCameras];
1399 ::memcpy(tempScene->mCameras,&cameras[0],sizeof(void*)*tempScene->mNumCameras);
1400 }
1401
1402 /* Copy the light sources to the output array
1403 */
1404 if (!lights.empty()) {
1405 tempScene->mNumLights = (unsigned int)lights.size();
1406 tempScene->mLights = new aiLight*[tempScene->mNumLights];
1407 ::memcpy(tempScene->mLights,&lights[0],sizeof(void*)*tempScene->mNumLights);
1408 }
1409
1410 // temporary data
1411 std::vector< aiNodeAnim*> anims;
1412 std::vector< aiMaterial*> materials;
1413 std::vector< AttachmentInfo > attach;
1414 std::vector<aiMesh*> meshes;
1415
1416 // try to guess how much storage we'll need
1417 anims.reserve (guessedAnimCnt + (guessedAnimCnt >> 2));
1418 meshes.reserve (guessedMeshCnt + (guessedMeshCnt >> 2));
1419 materials.reserve (guessedMatCnt + (guessedMatCnt >> 2));
1420
1421 /* Now process our scenegraph recursively: generate final
1422 * meshes and generate animation channels for all nodes.
1423 */
1424 unsigned int defMatIdx = UINT_MAX;
1425 GenerateGraph(root,tempScene->mRootNode, tempScene,
1426 batch, meshes, anims, attach, materials, defMatIdx);
1427
1428 if (!anims.empty())
1429 {
1430 tempScene->mNumAnimations = 1;
1431 tempScene->mAnimations = new aiAnimation*[tempScene->mNumAnimations];
1432 aiAnimation* an = tempScene->mAnimations[0] = new aiAnimation();
1433
1434 // ***********************************************************
1435 // This is only the global animation channel of the scene.
1436 // If there are animated models, they will have separate
1437 // animation channels in the scene. To display IRR scenes
1438 // correctly, users will need to combine the global anim
1439 // channel with all the local animations they want to play
1440 // ***********************************************************
1441 an->mName.Set("Irr_GlobalAnimChannel");
1442
1443 // copy all node animation channels to the global channel
1444 an->mNumChannels = (unsigned int)anims.size();
1445 an->mChannels = new aiNodeAnim*[an->mNumChannels];
1446 ::memcpy(an->mChannels, & anims [0], sizeof(void*)*an->mNumChannels);
1447 }
1448 if (!meshes.empty()) {
1449 // copy all meshes to the temporary scene
1450 tempScene->mNumMeshes = (unsigned int)meshes.size();
1451 tempScene->mMeshes = new aiMesh*[tempScene->mNumMeshes];
1452 ::memcpy(tempScene->mMeshes,&meshes[0],tempScene->mNumMeshes*
1453 sizeof(void*));
1454 }
1455
1456 /* Copy all materials to the output array
1457 */
1458 if (!materials.empty()) {
1459 tempScene->mNumMaterials = (unsigned int)materials.size();
1460 tempScene->mMaterials = new aiMaterial*[tempScene->mNumMaterials];
1461 ::memcpy(tempScene->mMaterials,&materials[0],sizeof(void*)*
1462 tempScene->mNumMaterials);
1463 }
1464
1465 /* Now merge all sub scenes and attach them to the correct
1466 * attachment points in the scenegraph.
1467 */
1468 SceneCombiner::MergeScenes(&pScene,tempScene,attach,
1469 AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
1470 AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) : 0));
1471
1472
1473 /* If we have no meshes | no materials now set the INCOMPLETE
1474 * scene flag. This is necessary if we failed to load all
1475 * models from external files
1476 */
1477 if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
1478 DefaultLogger::get()->warn("IRR: No meshes loaded, setting AI_SCENE_FLAGS_INCOMPLETE");
1479 pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
1480 }
1481
1482 /* Finished ... everything destructs automatically and all
1483 * temporary scenes have already been deleted by MergeScenes()
1484 */
1485 return;
1486}
1487
1488#endif // !! ASSIMP_BUILD_NO_IRR_IMPORTER
1489