1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2016, assimp team
7 
8 All rights reserved.
9 
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the following
12 conditions are met:
13 
14 * Redistributions of source code must retain the above
15   copyright notice, this list of conditions and the
16   following disclaimer.
17 
18 * Redistributions in binary form must reproduce the above
19   copyright notice, this list of conditions and the
20   following disclaimer in the documentation and/or other
21   materials provided with the distribution.
22 
23 * Neither the name of the assimp team, nor the names of its
24   contributors may be used to endorse or promote products
25   derived from this software without specific prior
26   written permission of the assimp team.
27 
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ---------------------------------------------------------------------------
40 */
41 
42 /** @file PretransformVertices.cpp
43  *  @brief Implementation of the "PretransformVertices" post processing step
44 */
45 
46 
47 #include "PretransformVertices.h"
48 #include "ProcessHelper.h"
49 #include "SceneCombiner.h"
50 #include "Exceptional.h"
51 
52 using namespace Assimp;
53 
54 // some array offsets
55 #define AI_PTVS_VERTEX 0x0
56 #define AI_PTVS_FACE 0x1
57 
58 // ------------------------------------------------------------------------------------------------
59 // Constructor to be privately used by Importer
PretransformVertices()60 PretransformVertices::PretransformVertices()
61 :   configKeepHierarchy (false), configNormalize(false), configTransform(false), configTransformation()
62 {
63 }
64 
65 // ------------------------------------------------------------------------------------------------
66 // Destructor, private as well
~PretransformVertices()67 PretransformVertices::~PretransformVertices()
68 {
69     // nothing to do here
70 }
71 
72 // ------------------------------------------------------------------------------------------------
73 // Returns whether the processing step is present in the given flag field.
IsActive(unsigned int pFlags) const74 bool PretransformVertices::IsActive( unsigned int pFlags) const
75 {
76     return  (pFlags & aiProcess_PreTransformVertices) != 0;
77 }
78 
79 // ------------------------------------------------------------------------------------------------
80 // Setup import configuration
SetupProperties(const Importer * pImp)81 void PretransformVertices::SetupProperties(const Importer* pImp)
82 {
83     // Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE,
84     // AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION
85     configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY,0));
86     configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE,0));
87     configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION,0));
88 
89     configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4());
90 }
91 
92 // ------------------------------------------------------------------------------------------------
93 // Count the number of nodes
CountNodes(aiNode * pcNode)94 unsigned int PretransformVertices::CountNodes( aiNode* pcNode )
95 {
96     unsigned int iRet = 1;
97     for (unsigned int i = 0;i < pcNode->mNumChildren;++i)
98     {
99         iRet += CountNodes(pcNode->mChildren[i]);
100     }
101     return iRet;
102 }
103 
104 // ------------------------------------------------------------------------------------------------
105 // Get a bitwise combination identifying the vertex format of a mesh
GetMeshVFormat(aiMesh * pcMesh)106 unsigned int PretransformVertices::GetMeshVFormat(aiMesh* pcMesh)
107 {
108     // the vertex format is stored in aiMesh::mBones for later retrieval.
109     // there isn't a good reason to compute it a few hundred times
110     // from scratch. The pointer is unused as animations are lost
111     // during PretransformVertices.
112     if (pcMesh->mBones)
113         return (unsigned int)(uint64_t)pcMesh->mBones;
114 
115 
116     const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
117 
118     // store the value for later use
119     pcMesh->mBones = (aiBone**)(uint64_t)iRet;
120     return iRet;
121 }
122 
123 // ------------------------------------------------------------------------------------------------
124 // Count the number of vertices in the whole scene and a given
125 // material index
CountVerticesAndFaces(aiScene * pcScene,aiNode * pcNode,unsigned int iMat,unsigned int iVFormat,unsigned int * piFaces,unsigned int * piVertices)126 void PretransformVertices::CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
127     unsigned int iVFormat, unsigned int* piFaces, unsigned int* piVertices)
128 {
129     for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
130     {
131         aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ];
132         if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh))
133         {
134             *piVertices += pcMesh->mNumVertices;
135             *piFaces += pcMesh->mNumFaces;
136         }
137     }
138     for (unsigned int i = 0;i < pcNode->mNumChildren;++i)
139     {
140         CountVerticesAndFaces(pcScene,pcNode->mChildren[i],iMat,
141             iVFormat,piFaces,piVertices);
142     }
143 }
144 
145 // ------------------------------------------------------------------------------------------------
146 // Collect vertex/face data
CollectData(aiScene * pcScene,aiNode * pcNode,unsigned int iMat,unsigned int iVFormat,aiMesh * pcMeshOut,unsigned int aiCurrent[2],unsigned int * num_refs)147 void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
148     unsigned int iVFormat, aiMesh* pcMeshOut,
149     unsigned int aiCurrent[2], unsigned int* num_refs)
150 {
151     // No need to multiply if there's no transformation
152     const bool identity = pcNode->mTransformation.IsIdentity();
153     for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
154     {
155         aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ];
156         if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh))
157         {
158             // Decrement mesh reference counter
159             unsigned int& num_ref = num_refs[pcNode->mMeshes[i]];
160             ai_assert(0 != num_ref);
161             --num_ref;
162 
163             if (identity)   {
164                 // copy positions without modifying them
165                 ::memcpy(pcMeshOut->mVertices + aiCurrent[AI_PTVS_VERTEX],
166                     pcMesh->mVertices,
167                     pcMesh->mNumVertices * sizeof(aiVector3D));
168 
169                 if (iVFormat & 0x2) {
170                     // copy normals without modifying them
171                     ::memcpy(pcMeshOut->mNormals + aiCurrent[AI_PTVS_VERTEX],
172                         pcMesh->mNormals,
173                         pcMesh->mNumVertices * sizeof(aiVector3D));
174                 }
175                 if (iVFormat & 0x4)
176                 {
177                     // copy tangents without modifying them
178                     ::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX],
179                         pcMesh->mTangents,
180                         pcMesh->mNumVertices * sizeof(aiVector3D));
181                     // copy bitangents without modifying them
182                     ::memcpy(pcMeshOut->mBitangents + aiCurrent[AI_PTVS_VERTEX],
183                         pcMesh->mBitangents,
184                         pcMesh->mNumVertices * sizeof(aiVector3D));
185                 }
186             }
187             else
188             {
189                 // copy positions, transform them to worldspace
190                 for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)  {
191                     pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n];
192                 }
193                 aiMatrix4x4 mWorldIT = pcNode->mTransformation;
194                 mWorldIT.Inverse().Transpose();
195 
196                 // TODO: implement Inverse() for aiMatrix3x3
197                 aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
198 
199                 if (iVFormat & 0x2)
200                 {
201                     // copy normals, transform them to worldspace
202                     for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)  {
203                         pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] =
204                             (m * pcMesh->mNormals[n]).Normalize();
205                     }
206                 }
207                 if (iVFormat & 0x4)
208                 {
209                     // copy tangents and bitangents, transform them to worldspace
210                     for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)  {
211                         pcMeshOut->mTangents  [aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mTangents[n]).Normalize();
212                         pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mBitangents[n]).Normalize();
213                     }
214                 }
215             }
216             unsigned int p = 0;
217             while (iVFormat & (0x100 << p))
218             {
219                 // copy texture coordinates
220                 memcpy(pcMeshOut->mTextureCoords[p] + aiCurrent[AI_PTVS_VERTEX],
221                     pcMesh->mTextureCoords[p],
222                     pcMesh->mNumVertices * sizeof(aiVector3D));
223                 ++p;
224             }
225             p = 0;
226             while (iVFormat & (0x1000000 << p))
227             {
228                 // copy vertex colors
229                 memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX],
230                     pcMesh->mColors[p],
231                     pcMesh->mNumVertices * sizeof(aiColor4D));
232                 ++p;
233             }
234             // now we need to copy all faces. since we will delete the source mesh afterwards,
235             // we don't need to reallocate the array of indices except if this mesh is
236             // referenced multiple times.
237             for (unsigned int planck = 0;planck < pcMesh->mNumFaces;++planck)
238             {
239                 aiFace& f_src = pcMesh->mFaces[planck];
240                 aiFace& f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck];
241 
242                 const unsigned int num_idx = f_src.mNumIndices;
243 
244                 f_dst.mNumIndices = num_idx;
245 
246                 unsigned int* pi;
247                 if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */
248                     pi = f_dst.mIndices = f_src.mIndices;
249 
250                     // offset all vertex indices
251                     for (unsigned int hahn = 0; hahn < num_idx;++hahn){
252                         pi[hahn] += aiCurrent[AI_PTVS_VERTEX];
253                     }
254                 }
255                 else {
256                     pi = f_dst.mIndices = new unsigned int[num_idx];
257 
258                     // copy and offset all vertex indices
259                     for (unsigned int hahn = 0; hahn < num_idx;++hahn){
260                         pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX];
261                     }
262                 }
263 
264                 // Update the mPrimitiveTypes member of the mesh
265                 switch (pcMesh->mFaces[planck].mNumIndices)
266                 {
267                 case 0x1:
268                     pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT;
269                     break;
270                 case 0x2:
271                     pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_LINE;
272                     break;
273                 case 0x3:
274                     pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
275                     break;
276                 default:
277                     pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
278                     break;
279                 };
280             }
281             aiCurrent[AI_PTVS_VERTEX] += pcMesh->mNumVertices;
282             aiCurrent[AI_PTVS_FACE]   += pcMesh->mNumFaces;
283         }
284     }
285 
286     // append all children of us
287     for (unsigned int i = 0;i < pcNode->mNumChildren;++i) {
288         CollectData(pcScene,pcNode->mChildren[i],iMat,
289             iVFormat,pcMeshOut,aiCurrent,num_refs);
290     }
291 }
292 
293 // ------------------------------------------------------------------------------------------------
294 // Get a list of all vertex formats that occur for a given material index
295 // The output list contains duplicate elements
GetVFormatList(aiScene * pcScene,unsigned int iMat,std::list<unsigned int> & aiOut)296 void PretransformVertices::GetVFormatList( aiScene* pcScene, unsigned int iMat,
297     std::list<unsigned int>& aiOut)
298 {
299     for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
300     {
301         aiMesh* pcMesh = pcScene->mMeshes[ i ];
302         if (iMat == pcMesh->mMaterialIndex) {
303             aiOut.push_back(GetMeshVFormat(pcMesh));
304         }
305     }
306 }
307 
308 // ------------------------------------------------------------------------------------------------
309 // Compute the absolute transformation matrices of each node
ComputeAbsoluteTransform(aiNode * pcNode)310 void PretransformVertices::ComputeAbsoluteTransform( aiNode* pcNode )
311 {
312     if (pcNode->mParent)    {
313         pcNode->mTransformation = pcNode->mParent->mTransformation*pcNode->mTransformation;
314     }
315 
316     for (unsigned int i = 0;i < pcNode->mNumChildren;++i)   {
317         ComputeAbsoluteTransform(pcNode->mChildren[i]);
318     }
319 }
320 
321 // ------------------------------------------------------------------------------------------------
322 // Apply the node transformation to a mesh
ApplyTransform(aiMesh * mesh,const aiMatrix4x4 & mat)323 void PretransformVertices::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)
324 {
325     // Check whether we need to transform the coordinates at all
326     if (!mat.IsIdentity()) {
327 
328         if (mesh->HasPositions()) {
329             for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
330                 mesh->mVertices[i] = mat * mesh->mVertices[i];
331             }
332         }
333         if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
334             aiMatrix4x4 mWorldIT = mat;
335             mWorldIT.Inverse().Transpose();
336 
337             // TODO: implement Inverse() for aiMatrix3x3
338             aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
339 
340             if (mesh->HasNormals()) {
341                 for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
342                     mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
343                 }
344             }
345             if (mesh->HasTangentsAndBitangents()) {
346                 for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
347                     mesh->mTangents[i]   = (m * mesh->mTangents[i]).Normalize();
348                     mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
349                 }
350             }
351         }
352     }
353 }
354 
355 // ------------------------------------------------------------------------------------------------
356 // Simple routine to build meshes in worldspace, no further optimization
BuildWCSMeshes(std::vector<aiMesh * > & out,aiMesh ** in,unsigned int numIn,aiNode * node)357 void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in,
358     unsigned int numIn, aiNode* node)
359 {
360     // NOTE:
361     //  aiMesh::mNumBones store original source mesh, or UINT_MAX if not a copy
362     //  aiMesh::mBones store reference to abs. transform we multiplied with
363 
364     // process meshes
365     for (unsigned int i = 0; i < node->mNumMeshes;++i) {
366         aiMesh* mesh = in[node->mMeshes[i]];
367 
368         // check whether we can operate on this mesh
369         if (!mesh->mBones || *reinterpret_cast<aiMatrix4x4*>(mesh->mBones) == node->mTransformation) {
370             // yes, we can.
371             mesh->mBones = reinterpret_cast<aiBone**> (&node->mTransformation);
372             mesh->mNumBones = UINT_MAX;
373         }
374         else {
375 
376             // try to find us in the list of newly created meshes
377             for (unsigned int n = 0; n < out.size(); ++n) {
378                 aiMesh* ctz = out[n];
379                 if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4*>(ctz->mBones) ==  node->mTransformation) {
380 
381                     // ok, use this one. Update node mesh index
382                     node->mMeshes[i] = numIn + n;
383                 }
384             }
385             if (node->mMeshes[i] < numIn) {
386                 // Worst case. Need to operate on a full copy of the mesh
387                 DefaultLogger::get()->info("PretransformVertices: Copying mesh due to mismatching transforms");
388                 aiMesh* ntz;
389 
390                 const unsigned int tmp = mesh->mNumBones; //
391                 mesh->mNumBones = 0;
392                 SceneCombiner::Copy(&ntz,mesh);
393                 mesh->mNumBones = tmp;
394 
395                 ntz->mNumBones = node->mMeshes[i];
396                 ntz->mBones = reinterpret_cast<aiBone**> (&node->mTransformation);
397 
398                 out.push_back(ntz);
399 
400                 node->mMeshes[i] = numIn + out.size() - 1;
401             }
402         }
403     }
404 
405     // call children
406     for (unsigned int i = 0; i < node->mNumChildren;++i)
407         BuildWCSMeshes(out,in,numIn,node->mChildren[i]);
408 }
409 
410 // ------------------------------------------------------------------------------------------------
411 // Reset transformation matrices to identity
MakeIdentityTransform(aiNode * nd)412 void PretransformVertices::MakeIdentityTransform(aiNode* nd)
413 {
414     nd->mTransformation = aiMatrix4x4();
415 
416     // call children
417     for (unsigned int i = 0; i < nd->mNumChildren;++i)
418         MakeIdentityTransform(nd->mChildren[i]);
419 }
420 
421 // ------------------------------------------------------------------------------------------------
422 // Build reference counters for all meshes
BuildMeshRefCountArray(aiNode * nd,unsigned int * refs)423 void PretransformVertices::BuildMeshRefCountArray(aiNode* nd, unsigned int * refs)
424 {
425     for (unsigned int i = 0; i< nd->mNumMeshes;++i)
426         refs[nd->mMeshes[i]]++;
427 
428     // call children
429     for (unsigned int i = 0; i < nd->mNumChildren;++i)
430         BuildMeshRefCountArray(nd->mChildren[i],refs);
431 }
432 
433 // ------------------------------------------------------------------------------------------------
434 // Executes the post processing step on the given imported data.
Execute(aiScene * pScene)435 void PretransformVertices::Execute( aiScene* pScene)
436 {
437     DefaultLogger::get()->debug("PretransformVerticesProcess begin");
438 
439     // Return immediately if we have no meshes
440     if (!pScene->mNumMeshes)
441         return;
442 
443     const unsigned int iOldMeshes = pScene->mNumMeshes;
444     const unsigned int iOldAnimationChannels = pScene->mNumAnimations;
445     const unsigned int iOldNodes = CountNodes(pScene->mRootNode);
446 
447     if(configTransform) {
448         pScene->mRootNode->mTransformation = configTransformation;
449     }
450 
451     // first compute absolute transformation matrices for all nodes
452     ComputeAbsoluteTransform(pScene->mRootNode);
453 
454     // Delete aiMesh::mBones for all meshes. The bones are
455     // removed during this step and we need the pointer as
456     // temporary storage
457     for (unsigned int i = 0; i < pScene->mNumMeshes;++i)    {
458         aiMesh* mesh = pScene->mMeshes[i];
459 
460         for (unsigned int a = 0; a < mesh->mNumBones;++a)
461             delete mesh->mBones[a];
462 
463         delete[] mesh->mBones;
464         mesh->mBones = NULL;
465     }
466 
467     // now build a list of output meshes
468     std::vector<aiMesh*> apcOutMeshes;
469 
470     // Keep scene hierarchy? It's an easy job in this case ...
471     // we go on and transform all meshes, if one is referenced by nodes
472     // with different absolute transformations a depth copy of the mesh
473     // is required.
474     if( configKeepHierarchy ) {
475 
476         // Hack: store the matrix we're transforming a mesh with in aiMesh::mBones
477         BuildWCSMeshes(apcOutMeshes,pScene->mMeshes,pScene->mNumMeshes, pScene->mRootNode);
478 
479         // ... if new meshes have been generated, append them to the end of the scene
480         if (apcOutMeshes.size() > 0) {
481             aiMesh** npp = new aiMesh*[pScene->mNumMeshes + apcOutMeshes.size()];
482 
483             memcpy(npp,pScene->mMeshes,sizeof(aiMesh*)*pScene->mNumMeshes);
484             memcpy(npp+pScene->mNumMeshes,&apcOutMeshes[0],sizeof(aiMesh*)*apcOutMeshes.size());
485 
486             pScene->mNumMeshes  += apcOutMeshes.size();
487             delete[] pScene->mMeshes; pScene->mMeshes = npp;
488         }
489 
490         // now iterate through all meshes and transform them to worldspace
491         for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
492             ApplyTransform(pScene->mMeshes[i],*reinterpret_cast<aiMatrix4x4*>( pScene->mMeshes[i]->mBones ));
493 
494             // prevent improper destruction
495             pScene->mMeshes[i]->mBones    = NULL;
496             pScene->mMeshes[i]->mNumBones = 0;
497         }
498     }
499     else {
500 
501         apcOutMeshes.reserve(pScene->mNumMaterials<<1u);
502         std::list<unsigned int> aiVFormats;
503 
504         std::vector<unsigned int> s(pScene->mNumMeshes,0);
505         BuildMeshRefCountArray(pScene->mRootNode,&s[0]);
506 
507         for (unsigned int i = 0; i < pScene->mNumMaterials;++i)     {
508             // get the list of all vertex formats for this material
509             aiVFormats.clear();
510             GetVFormatList(pScene,i,aiVFormats);
511             aiVFormats.sort();
512             aiVFormats.unique();
513             for (std::list<unsigned int>::const_iterator j =  aiVFormats.begin();j != aiVFormats.end();++j) {
514                 unsigned int iVertices = 0;
515                 unsigned int iFaces = 0;
516                 CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices);
517                 if (0 != iFaces && 0 != iVertices)
518                 {
519                     apcOutMeshes.push_back(new aiMesh());
520                     aiMesh* pcMesh = apcOutMeshes.back();
521                     pcMesh->mNumFaces = iFaces;
522                     pcMesh->mNumVertices = iVertices;
523                     pcMesh->mFaces = new aiFace[iFaces];
524                     pcMesh->mVertices = new aiVector3D[iVertices];
525                     pcMesh->mMaterialIndex = i;
526                     if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices];
527                     if ((*j) & 0x4)
528                     {
529                         pcMesh->mTangents    = new aiVector3D[iVertices];
530                         pcMesh->mBitangents  = new aiVector3D[iVertices];
531                     }
532                     iFaces = 0;
533                     while ((*j) & (0x100 << iFaces))
534                     {
535                         pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
536                         if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3;
537                         else pcMesh->mNumUVComponents[iFaces] = 2;
538                         iFaces++;
539                     }
540                     iFaces = 0;
541                     while ((*j) & (0x1000000 << iFaces))
542                         pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
543 
544                     // fill the mesh ...
545                     unsigned int aiTemp[2] = {0,0};
546                     CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp,&s[0]);
547                 }
548             }
549         }
550 
551         // If no meshes are referenced in the node graph it is possible that we get no output meshes.
552         if (apcOutMeshes.empty())   {
553             throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes");
554         }
555         else
556         {
557             // now delete all meshes in the scene and build a new mesh list
558             for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
559             {
560                 aiMesh* mesh = pScene->mMeshes[i];
561                 mesh->mNumBones = 0;
562                 mesh->mBones    = NULL;
563 
564                 // we're reusing the face index arrays. avoid destruction
565                 for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
566                     mesh->mFaces[a].mNumIndices = 0;
567                     mesh->mFaces[a].mIndices = NULL;
568                 }
569 
570                 delete mesh;
571 
572                 // Invalidate the contents of the old mesh array. We will most
573                 // likely have less output meshes now, so the last entries of
574                 // the mesh array are not overridden. We set them to NULL to
575                 // make sure the developer gets notified when his application
576                 // attempts to access these fields ...
577                 mesh = NULL;
578             }
579 
580             // It is impossible that we have more output meshes than
581             // input meshes, so we can easily reuse the old mesh array
582             pScene->mNumMeshes = (unsigned int)apcOutMeshes.size();
583             for (unsigned int i = 0; i < pScene->mNumMeshes;++i) {
584                 pScene->mMeshes[i] = apcOutMeshes[i];
585             }
586         }
587     }
588 
589     // remove all animations from the scene
590     for (unsigned int i = 0; i < pScene->mNumAnimations;++i)
591         delete pScene->mAnimations[i];
592     delete[] pScene->mAnimations;
593 
594     pScene->mAnimations    = NULL;
595     pScene->mNumAnimations = 0;
596 
597     // --- we need to keep all cameras and lights
598     for (unsigned int i = 0; i < pScene->mNumCameras;++i)
599     {
600         aiCamera* cam = pScene->mCameras[i];
601         const aiNode* nd = pScene->mRootNode->FindNode(cam->mName);
602         ai_assert(NULL != nd);
603 
604         // multiply all properties of the camera with the absolute
605         // transformation of the corresponding node
606         cam->mPosition = nd->mTransformation * cam->mPosition;
607         cam->mLookAt   = aiMatrix3x3( nd->mTransformation ) * cam->mLookAt;
608         cam->mUp       = aiMatrix3x3( nd->mTransformation ) * cam->mUp;
609     }
610 
611     for (unsigned int i = 0; i < pScene->mNumLights;++i)
612     {
613         aiLight* l = pScene->mLights[i];
614         const aiNode* nd = pScene->mRootNode->FindNode(l->mName);
615         ai_assert(NULL != nd);
616 
617         // multiply all properties of the camera with the absolute
618         // transformation of the corresponding node
619         l->mPosition   = nd->mTransformation * l->mPosition;
620         l->mDirection  = aiMatrix3x3( nd->mTransformation ) * l->mDirection;
621         l->mUp         = aiMatrix3x3( nd->mTransformation ) * l->mUp;
622     }
623 
624     if( !configKeepHierarchy ) {
625 
626         // now delete all nodes in the scene and build a new
627         // flat node graph with a root node and some level 1 children
628         delete pScene->mRootNode;
629         pScene->mRootNode = new aiNode();
630         pScene->mRootNode->mName.Set("<dummy_root>");
631 
632         if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras)
633         {
634             pScene->mRootNode->mNumMeshes = 1;
635             pScene->mRootNode->mMeshes = new unsigned int[1];
636             pScene->mRootNode->mMeshes[0] = 0;
637         }
638         else
639         {
640             pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras;
641             aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
642 
643             // generate mesh nodes
644             for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes)
645             {
646                 aiNode* pcNode = *nodes = new aiNode();
647                 pcNode->mParent = pScene->mRootNode;
648                 pcNode->mName.length = ::ai_snprintf(pcNode->mName.data,MAXLEN,"mesh_%u",i);
649 
650                 // setup mesh indices
651                 pcNode->mNumMeshes = 1;
652                 pcNode->mMeshes = new unsigned int[1];
653                 pcNode->mMeshes[0] = i;
654             }
655             // generate light nodes
656             for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes)
657             {
658                 aiNode* pcNode = *nodes = new aiNode();
659                 pcNode->mParent = pScene->mRootNode;
660                 pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u",i);
661                 pScene->mLights[i]->mName = pcNode->mName;
662             }
663             // generate camera nodes
664             for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes)
665             {
666                 aiNode* pcNode = *nodes = new aiNode();
667                 pcNode->mParent = pScene->mRootNode;
668                 pcNode->mName.length = ::ai_snprintf(pcNode->mName.data,MAXLEN,"cam_%u",i);
669                 pScene->mCameras[i]->mName = pcNode->mName;
670             }
671         }
672     }
673     else {
674         // ... and finally set the transformation matrix of all nodes to identity
675         MakeIdentityTransform(pScene->mRootNode);
676     }
677 
678     if (configNormalize) {
679         // compute the boundary of all meshes
680         aiVector3D min,max;
681         MinMaxChooser<aiVector3D> ()(min,max);
682 
683         for (unsigned int a = 0; a <  pScene->mNumMeshes; ++a) {
684             aiMesh* m = pScene->mMeshes[a];
685             for (unsigned int i = 0; i < m->mNumVertices;++i) {
686                 min = std::min(m->mVertices[i],min);
687                 max = std::max(m->mVertices[i],max);
688             }
689         }
690 
691         // find the dominant axis
692         aiVector3D d = max-min;
693         const float div = std::max(d.x,std::max(d.y,d.z))*0.5f;
694 
695         d = min+d*0.5f;
696         for (unsigned int a = 0; a <  pScene->mNumMeshes; ++a) {
697             aiMesh* m = pScene->mMeshes[a];
698             for (unsigned int i = 0; i < m->mNumVertices;++i) {
699                 m->mVertices[i] = (m->mVertices[i]-d)/div;
700             }
701         }
702     }
703 
704     // print statistics
705     if (!DefaultLogger::isNullLogger())
706     {
707         char buffer[4096];
708 
709         DefaultLogger::get()->debug("PretransformVerticesProcess finished");
710 
711         ::ai_snprintf(buffer,4096,"Removed %u nodes and %u animation channels (%u output nodes)",
712             iOldNodes,iOldAnimationChannels,CountNodes(pScene->mRootNode));
713         DefaultLogger::get()->info(buffer);
714 
715         ai_snprintf(buffer, 4096,"Kept %u lights and %u cameras",
716             pScene->mNumLights,pScene->mNumCameras);
717         DefaultLogger::get()->info(buffer);
718 
719         ai_snprintf(buffer, 4096,"Moved %u meshes to WCS (number of output meshes: %u)",
720             iOldMeshes,pScene->mNumMeshes);
721         DefaultLogger::get()->info(buffer);
722     }
723 }
724 
725