/src/assimp/code/PostProcessing/ConvertToLHProcess.cpp
Line | Count | Source |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2026, 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 MakeLeftHandedProcess.cpp |
43 | | * @brief Implementation of the post processing step to convert all |
44 | | * imported data to a left-handed coordinate system. |
45 | | * |
46 | | * Face order & UV flip are also implemented here, for the sake of a |
47 | | * better location. |
48 | | */ |
49 | | |
50 | | #include "ConvertToLHProcess.h" |
51 | | #include <assimp/postprocess.h> |
52 | | #include <assimp/scene.h> |
53 | | #include <assimp/DefaultLogger.hpp> |
54 | | |
55 | | using namespace Assimp; |
56 | | |
57 | | #ifndef ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS |
58 | | |
59 | | namespace { |
60 | | |
61 | | template <typename aiMeshType> |
62 | 0 | void flipUVs(aiMeshType *pMesh) { |
63 | 0 | if (pMesh == nullptr) { |
64 | 0 | return; |
65 | 0 | } |
66 | | // mirror texture y coordinate |
67 | 0 | for (unsigned int tcIdx = 0; tcIdx < AI_MAX_NUMBER_OF_TEXTURECOORDS; tcIdx++) { |
68 | 0 | if (!pMesh->HasTextureCoords(tcIdx)) { |
69 | 0 | break; |
70 | 0 | } |
71 | | |
72 | 0 | for (unsigned int vIdx = 0; vIdx < pMesh->mNumVertices; vIdx++) { |
73 | 0 | pMesh->mTextureCoords[tcIdx][vIdx].y = 1.0f - pMesh->mTextureCoords[tcIdx][vIdx].y; |
74 | 0 | } |
75 | 0 | } |
76 | 0 | } Unexecuted instantiation: ConvertToLHProcess.cpp:void (anonymous namespace)::flipUVs<aiMesh>(aiMesh*) Unexecuted instantiation: ConvertToLHProcess.cpp:void (anonymous namespace)::flipUVs<aiAnimMesh>(aiAnimMesh*) |
77 | | |
78 | | } // namespace |
79 | | |
80 | | // ------------------------------------------------------------------------------------------------ |
81 | | // Returns whether the processing step is present in the given flag field. |
82 | 8.63k | bool MakeLeftHandedProcess::IsActive(unsigned int pFlags) const { |
83 | 8.63k | return 0 != (pFlags & aiProcess_MakeLeftHanded); |
84 | 8.63k | } |
85 | | |
86 | | // ------------------------------------------------------------------------------------------------ |
87 | | // Executes the post processing step on the given imported data. |
88 | 31 | void MakeLeftHandedProcess::Execute(aiScene *pScene) { |
89 | | // Check for an existent root node to proceed |
90 | 31 | ai_assert(pScene->mRootNode != nullptr); |
91 | 31 | ASSIMP_LOG_DEBUG("MakeLeftHandedProcess begin"); |
92 | | |
93 | | // recursively convert all the nodes |
94 | 31 | ProcessNode(pScene->mRootNode, aiMatrix4x4()); |
95 | | |
96 | | // process the meshes accordingly |
97 | 48 | for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { |
98 | 17 | ProcessMesh(pScene->mMeshes[a]); |
99 | 17 | } |
100 | | |
101 | | // process the materials accordingly |
102 | 42 | for (unsigned int a = 0; a < pScene->mNumMaterials; ++a) { |
103 | 11 | ProcessMaterial(pScene->mMaterials[a]); |
104 | 11 | } |
105 | | |
106 | | // transform all animation channels as well |
107 | 41 | for (unsigned int a = 0; a < pScene->mNumAnimations; a++) { |
108 | 10 | aiAnimation *anim = pScene->mAnimations[a]; |
109 | 213 | for (unsigned int b = 0; b < anim->mNumChannels; b++) { |
110 | 203 | aiNodeAnim *nodeAnim = anim->mChannels[b]; |
111 | 203 | ProcessAnimation(nodeAnim); |
112 | 203 | } |
113 | 10 | } |
114 | | |
115 | | // process the cameras accordingly |
116 | 71 | for( unsigned int a = 0; a < pScene->mNumCameras; ++a) |
117 | 40 | { |
118 | 40 | ProcessCamera(pScene->mCameras[a]); |
119 | 40 | } |
120 | 31 | ASSIMP_LOG_DEBUG("MakeLeftHandedProcess finished"); |
121 | 31 | } |
122 | | |
123 | | // ------------------------------------------------------------------------------------------------ |
124 | | // Recursively converts a node, all of its children and all of its meshes |
125 | 3.52k | void MakeLeftHandedProcess::ProcessNode(aiNode *pNode, const aiMatrix4x4 &pParentGlobalRotation) { |
126 | | // mirror all base vectors at the local Z axis |
127 | 3.52k | pNode->mTransformation.c1 = -pNode->mTransformation.c1; |
128 | 3.52k | pNode->mTransformation.c2 = -pNode->mTransformation.c2; |
129 | 3.52k | pNode->mTransformation.c3 = -pNode->mTransformation.c3; |
130 | 3.52k | pNode->mTransformation.c4 = -pNode->mTransformation.c4; |
131 | | |
132 | | // now invert the Z axis again to keep the matrix determinant positive. |
133 | | // The local meshes will be inverted accordingly so that the result should look just fine again. |
134 | 3.52k | pNode->mTransformation.a3 = -pNode->mTransformation.a3; |
135 | 3.52k | pNode->mTransformation.b3 = -pNode->mTransformation.b3; |
136 | 3.52k | pNode->mTransformation.c3 = -pNode->mTransformation.c3; |
137 | 3.52k | pNode->mTransformation.d3 = -pNode->mTransformation.d3; // useless, but anyways... |
138 | | |
139 | | // continue for all children |
140 | 7.02k | for (size_t a = 0; a < pNode->mNumChildren; ++a) { |
141 | 3.49k | ProcessNode(pNode->mChildren[a], pParentGlobalRotation * pNode->mTransformation); |
142 | 3.49k | } |
143 | 3.52k | } |
144 | | |
145 | | // ------------------------------------------------------------------------------------------------ |
146 | | // Converts a single mesh to left handed coordinates. |
147 | 17 | void MakeLeftHandedProcess::ProcessMesh(aiMesh *pMesh) { |
148 | 17 | if (nullptr == pMesh) { |
149 | 0 | ASSIMP_LOG_ERROR("Nullptr to mesh found."); |
150 | 0 | return; |
151 | 0 | } |
152 | | // mirror positions, normals and stuff along the Z axis |
153 | 58.6k | for (size_t a = 0; a < pMesh->mNumVertices; ++a) { |
154 | 58.5k | pMesh->mVertices[a].z *= -1.0f; |
155 | 58.5k | if (pMesh->HasNormals()) { |
156 | 58.5k | pMesh->mNormals[a].z *= -1.0f; |
157 | 58.5k | } |
158 | 58.5k | if (pMesh->HasTangentsAndBitangents()) { |
159 | 0 | pMesh->mTangents[a].z *= -1.0f; |
160 | 0 | pMesh->mBitangents[a].z *= -1.0f; |
161 | 0 | } |
162 | 58.5k | } |
163 | | |
164 | | // mirror anim meshes positions, normals and stuff along the Z axis |
165 | 17 | for (size_t m = 0; m < pMesh->mNumAnimMeshes; ++m) { |
166 | 0 | for (size_t a = 0; a < pMesh->mAnimMeshes[m]->mNumVertices; ++a) { |
167 | 0 | pMesh->mAnimMeshes[m]->mVertices[a].z *= -1.0f; |
168 | 0 | if (pMesh->mAnimMeshes[m]->HasNormals()) { |
169 | 0 | pMesh->mAnimMeshes[m]->mNormals[a].z *= -1.0f; |
170 | 0 | } |
171 | 0 | if (pMesh->mAnimMeshes[m]->HasTangentsAndBitangents()) { |
172 | 0 | pMesh->mAnimMeshes[m]->mTangents[a].z *= -1.0f; |
173 | 0 | pMesh->mAnimMeshes[m]->mBitangents[a].z *= -1.0f; |
174 | 0 | } |
175 | 0 | } |
176 | 0 | } |
177 | | |
178 | | // mirror offset matrices of all bones |
179 | 112 | for (size_t a = 0; a < pMesh->mNumBones; ++a) { |
180 | 95 | aiBone *bone = pMesh->mBones[a]; |
181 | 95 | bone->mOffsetMatrix.a3 = -bone->mOffsetMatrix.a3; |
182 | 95 | bone->mOffsetMatrix.b3 = -bone->mOffsetMatrix.b3; |
183 | 95 | bone->mOffsetMatrix.d3 = -bone->mOffsetMatrix.d3; |
184 | 95 | bone->mOffsetMatrix.c1 = -bone->mOffsetMatrix.c1; |
185 | 95 | bone->mOffsetMatrix.c2 = -bone->mOffsetMatrix.c2; |
186 | 95 | bone->mOffsetMatrix.c4 = -bone->mOffsetMatrix.c4; |
187 | 95 | } |
188 | | |
189 | | // mirror bitangents as well as they're derived from the texture coords |
190 | 17 | if (pMesh->HasTangentsAndBitangents()) { |
191 | 0 | for (unsigned int a = 0; a < pMesh->mNumVertices; a++) |
192 | 0 | pMesh->mBitangents[a] *= -1.0f; |
193 | 0 | } |
194 | 17 | } |
195 | | |
196 | | // ------------------------------------------------------------------------------------------------ |
197 | | // Converts a single material to left handed coordinates. |
198 | 11 | void MakeLeftHandedProcess::ProcessMaterial(aiMaterial *_mat) { |
199 | 11 | if (nullptr == _mat) { |
200 | 0 | ASSIMP_LOG_ERROR("Nullptr to aiMaterial found."); |
201 | 0 | return; |
202 | 0 | } |
203 | | |
204 | 11 | aiMaterial *mat = (aiMaterial *)_mat; |
205 | 123 | for (unsigned int a = 0; a < mat->mNumProperties; ++a) { |
206 | 112 | aiMaterialProperty *prop = mat->mProperties[a]; |
207 | | |
208 | | // Mapping axis for UV mappings? |
209 | 112 | if (!::strcmp(prop->mKey.data, "$tex.mapaxis")) { |
210 | 0 | ai_assert(prop->mDataLength >= sizeof(aiVector3D)); // something is wrong with the validation if we end up here |
211 | 0 | aiVector3D *pff = (aiVector3D *)prop->mData; |
212 | 0 | pff->z *= -1.f; |
213 | 0 | } |
214 | 112 | } |
215 | 11 | } |
216 | | |
217 | | // ------------------------------------------------------------------------------------------------ |
218 | | // Converts the given animation to LH coordinates. |
219 | 203 | void MakeLeftHandedProcess::ProcessAnimation(aiNodeAnim *pAnim) { |
220 | | // position keys |
221 | 619 | for (unsigned int a = 0; a < pAnim->mNumPositionKeys; a++) |
222 | 416 | pAnim->mPositionKeys[a].mValue.z *= -1.0f; |
223 | | |
224 | | // rotation keys |
225 | 6.22k | for (unsigned int a = 0; a < pAnim->mNumRotationKeys; a++) { |
226 | 6.02k | pAnim->mRotationKeys[a].mValue.x *= -1.0f; |
227 | 6.02k | pAnim->mRotationKeys[a].mValue.y *= -1.0f; |
228 | 6.02k | } |
229 | 203 | } |
230 | | |
231 | | // ------------------------------------------------------------------------------------------------ |
232 | | // Converts a single camera to left handed coordinates. |
233 | | void MakeLeftHandedProcess::ProcessCamera( aiCamera* pCam) |
234 | 40 | { |
235 | 40 | pCam->mLookAt = ai_real(2.0f) * pCam->mPosition - pCam->mLookAt; |
236 | 40 | } |
237 | | |
238 | | #endif // !! ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS |
239 | | #ifndef ASSIMP_BUILD_NO_FLIPUVS_PROCESS |
240 | | // # FlipUVsProcess |
241 | | |
242 | | // ------------------------------------------------------------------------------------------------ |
243 | | // Constructor to be privately used by Importer |
244 | 26.3k | FlipUVsProcess::FlipUVsProcess() = default; |
245 | | |
246 | | // ------------------------------------------------------------------------------------------------ |
247 | | // Destructor, private as well |
248 | 26.3k | FlipUVsProcess::~FlipUVsProcess() = default; |
249 | | |
250 | | // ------------------------------------------------------------------------------------------------ |
251 | | // Returns whether the processing step is present in the given flag field. |
252 | 8.63k | bool FlipUVsProcess::IsActive(unsigned int pFlags) const { |
253 | 8.63k | return 0 != (pFlags & aiProcess_FlipUVs); |
254 | 8.63k | } |
255 | | |
256 | | // ------------------------------------------------------------------------------------------------ |
257 | | // Executes the post processing step on the given imported data. |
258 | 0 | void FlipUVsProcess::Execute(aiScene *pScene) { |
259 | 0 | ASSIMP_LOG_DEBUG("FlipUVsProcess begin"); |
260 | 0 | for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) |
261 | 0 | ProcessMesh(pScene->mMeshes[i]); |
262 | |
|
263 | 0 | for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) |
264 | 0 | ProcessMaterial(pScene->mMaterials[i]); |
265 | 0 | ASSIMP_LOG_DEBUG("FlipUVsProcess finished"); |
266 | 0 | } |
267 | | |
268 | | // ------------------------------------------------------------------------------------------------ |
269 | | // Converts a single material |
270 | 0 | void FlipUVsProcess::ProcessMaterial(aiMaterial *_mat) { |
271 | 0 | aiMaterial *mat = (aiMaterial *)_mat; |
272 | 0 | for (unsigned int a = 0; a < mat->mNumProperties; ++a) { |
273 | 0 | aiMaterialProperty *prop = mat->mProperties[a]; |
274 | 0 | if (!prop) { |
275 | 0 | ASSIMP_LOG_VERBOSE_DEBUG("Property is null"); |
276 | 0 | continue; |
277 | 0 | } |
278 | | |
279 | | // UV transformation key? |
280 | 0 | if (!::strcmp(prop->mKey.data, "$tex.uvtrafo")) { |
281 | 0 | ai_assert(prop->mDataLength >= sizeof(aiUVTransform)); // something is wrong with the validation if we end up here |
282 | 0 | aiUVTransform *uv = (aiUVTransform *)prop->mData; |
283 | | |
284 | | // just flip it, that's everything |
285 | 0 | uv->mTranslation.y *= -1.f; |
286 | 0 | uv->mRotation *= -1.f; |
287 | 0 | } |
288 | 0 | } |
289 | 0 | } |
290 | | |
291 | | // ------------------------------------------------------------------------------------------------ |
292 | | // Converts a single mesh |
293 | 0 | void FlipUVsProcess::ProcessMesh(aiMesh *pMesh) { |
294 | 0 | flipUVs(pMesh); |
295 | 0 | for (unsigned int idx = 0; idx < pMesh->mNumAnimMeshes; idx++) { |
296 | 0 | flipUVs(pMesh->mAnimMeshes[idx]); |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | | #endif // !ASSIMP_BUILD_NO_FLIPUVS_PROCESS |
301 | | #ifndef ASSIMP_BUILD_NO_FLIPWINDING_PROCESS |
302 | | // # FlipWindingOrderProcess |
303 | | |
304 | | // ------------------------------------------------------------------------------------------------ |
305 | | // Returns whether the processing step is present in the given flag field. |
306 | 8.63k | bool FlipWindingOrderProcess::IsActive(unsigned int pFlags) const { |
307 | 8.63k | return 0 != (pFlags & aiProcess_FlipWindingOrder); |
308 | 8.63k | } |
309 | | |
310 | | // ------------------------------------------------------------------------------------------------ |
311 | | // Executes the post processing step on the given imported data. |
312 | 31 | void FlipWindingOrderProcess::Execute(aiScene *pScene) { |
313 | 31 | ASSIMP_LOG_DEBUG("FlipWindingOrderProcess begin"); |
314 | 48 | for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) |
315 | 17 | ProcessMesh(pScene->mMeshes[i]); |
316 | 31 | ASSIMP_LOG_DEBUG("FlipWindingOrderProcess finished"); |
317 | 31 | } |
318 | | |
319 | | // ------------------------------------------------------------------------------------------------ |
320 | | // Converts a single mesh |
321 | 17 | void FlipWindingOrderProcess::ProcessMesh(aiMesh *pMesh) { |
322 | | // invert the order of all faces in this mesh |
323 | 18.8k | for (unsigned int a = 0; a < pMesh->mNumFaces; a++) { |
324 | 18.7k | aiFace &face = pMesh->mFaces[a]; |
325 | 39.7k | for (unsigned int b = 0; b < face.mNumIndices / 2; b++) { |
326 | 20.9k | std::swap(face.mIndices[b], face.mIndices[face.mNumIndices - 1 - b]); |
327 | 20.9k | } |
328 | 18.7k | } |
329 | | |
330 | | // invert the order of all components in this mesh anim meshes |
331 | 17 | for (unsigned int m = 0; m < pMesh->mNumAnimMeshes; m++) { |
332 | 0 | aiAnimMesh *animMesh = pMesh->mAnimMeshes[m]; |
333 | 0 | unsigned int numVertices = animMesh->mNumVertices; |
334 | 0 | if (animMesh->HasPositions()) { |
335 | 0 | for (unsigned int a = 0; a < numVertices; a++) { |
336 | 0 | std::swap(animMesh->mVertices[a], animMesh->mVertices[numVertices - 1 - a]); |
337 | 0 | } |
338 | 0 | } |
339 | 0 | if (animMesh->HasNormals()) { |
340 | 0 | for (unsigned int a = 0; a < numVertices; a++) { |
341 | 0 | std::swap(animMesh->mNormals[a], animMesh->mNormals[numVertices - 1 - a]); |
342 | 0 | } |
343 | 0 | } |
344 | 0 | for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i++) { |
345 | 0 | if (animMesh->HasTextureCoords(i)) { |
346 | 0 | for (unsigned int a = 0; a < numVertices; a++) { |
347 | 0 | std::swap(animMesh->mTextureCoords[i][a], animMesh->mTextureCoords[i][numVertices - 1 - a]); |
348 | 0 | } |
349 | 0 | } |
350 | 0 | } |
351 | 0 | if (animMesh->HasTangentsAndBitangents()) { |
352 | 0 | for (unsigned int a = 0; a < numVertices; a++) { |
353 | 0 | std::swap(animMesh->mTangents[a], animMesh->mTangents[numVertices - 1 - a]); |
354 | 0 | std::swap(animMesh->mBitangents[a], animMesh->mBitangents[numVertices - 1 - a]); |
355 | 0 | } |
356 | 0 | } |
357 | 0 | for (unsigned int v = 0; v < AI_MAX_NUMBER_OF_COLOR_SETS; v++) { |
358 | 0 | if (animMesh->HasVertexColors(v)) { |
359 | 0 | for (unsigned int a = 0; a < numVertices; a++) { |
360 | 0 | std::swap(animMesh->mColors[v][a], animMesh->mColors[v][numVertices - 1 - a]); |
361 | 0 | } |
362 | 0 | } |
363 | 0 | } |
364 | 0 | } |
365 | 17 | } |
366 | | |
367 | | #endif // !! ASSIMP_BUILD_NO_FLIPWINDING_PROCESS |