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