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