Coverage Report

Created: 2026-01-07 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/PostProcessing/ConvertToLHProcess.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  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
106
bool MakeLeftHandedProcess::IsActive(unsigned int pFlags) const {
83
106
    return 0 != (pFlags & aiProcess_MakeLeftHanded);
84
106
}
85
86
// ------------------------------------------------------------------------------------------------
87
// Executes the post processing step on the given imported data.
88
28
void MakeLeftHandedProcess::Execute(aiScene *pScene) {
89
    // Check for an existent root node to proceed
90
28
    ai_assert(pScene->mRootNode != nullptr);
91
28
    ASSIMP_LOG_DEBUG("MakeLeftHandedProcess begin");
92
93
    // recursively convert all the nodes
94
28
    ProcessNode(pScene->mRootNode, aiMatrix4x4());
95
96
    // process the meshes accordingly
97
28
    for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
98
0
        ProcessMesh(pScene->mMeshes[a]);
99
0
    }
100
101
    // process the materials accordingly
102
28
    for (unsigned int a = 0; a < pScene->mNumMaterials; ++a) {
103
0
        ProcessMaterial(pScene->mMaterials[a]);
104
0
    }
105
106
    // transform all animation channels as well
107
39
    for (unsigned int a = 0; a < pScene->mNumAnimations; a++) {
108
11
        aiAnimation *anim = pScene->mAnimations[a];
109
48
        for (unsigned int b = 0; b < anim->mNumChannels; b++) {
110
37
            aiNodeAnim *nodeAnim = anim->mChannels[b];
111
37
            ProcessAnimation(nodeAnim);
112
37
        }
113
11
    }
114
115
    // process the cameras accordingly
116
87
    for( unsigned int a = 0; a < pScene->mNumCameras; ++a)
117
59
    {
118
59
        ProcessCamera(pScene->mCameras[a]);
119
59
    }
120
28
    ASSIMP_LOG_DEBUG("MakeLeftHandedProcess finished");
121
28
}
122
123
// ------------------------------------------------------------------------------------------------
124
// Recursively converts a node, all of its children and all of its meshes
125
7.51k
void MakeLeftHandedProcess::ProcessNode(aiNode *pNode, const aiMatrix4x4 &pParentGlobalRotation) {
126
    // mirror all base vectors at the local Z axis
127
7.51k
    pNode->mTransformation.c1 = -pNode->mTransformation.c1;
128
7.51k
    pNode->mTransformation.c2 = -pNode->mTransformation.c2;
129
7.51k
    pNode->mTransformation.c3 = -pNode->mTransformation.c3;
130
7.51k
    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
7.51k
    pNode->mTransformation.a3 = -pNode->mTransformation.a3;
135
7.51k
    pNode->mTransformation.b3 = -pNode->mTransformation.b3;
136
7.51k
    pNode->mTransformation.c3 = -pNode->mTransformation.c3;
137
7.51k
    pNode->mTransformation.d3 = -pNode->mTransformation.d3; // useless, but anyways...
138
139
    // continue for all children
140
14.9k
    for (size_t a = 0; a < pNode->mNumChildren; ++a) {
141
7.48k
        ProcessNode(pNode->mChildren[a], pParentGlobalRotation * pNode->mTransformation);
142
7.48k
    }
143
7.51k
}
144
145
// ------------------------------------------------------------------------------------------------
146
// Converts a single mesh to left handed coordinates.
147
0
void MakeLeftHandedProcess::ProcessMesh(aiMesh *pMesh) {
148
0
    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
0
    for (size_t a = 0; a < pMesh->mNumVertices; ++a) {
154
0
        pMesh->mVertices[a].z *= -1.0f;
155
0
        if (pMesh->HasNormals()) {
156
0
            pMesh->mNormals[a].z *= -1.0f;
157
0
        }
158
0
        if (pMesh->HasTangentsAndBitangents()) {
159
0
            pMesh->mTangents[a].z *= -1.0f;
160
0
            pMesh->mBitangents[a].z *= -1.0f;
161
0
        }
162
0
    }
163
164
    // mirror anim meshes positions, normals and stuff along the Z axis
165
0
    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
0
    for (size_t a = 0; a < pMesh->mNumBones; ++a) {
180
0
        aiBone *bone = pMesh->mBones[a];
181
0
        bone->mOffsetMatrix.a3 = -bone->mOffsetMatrix.a3;
182
0
        bone->mOffsetMatrix.b3 = -bone->mOffsetMatrix.b3;
183
0
        bone->mOffsetMatrix.d3 = -bone->mOffsetMatrix.d3;
184
0
        bone->mOffsetMatrix.c1 = -bone->mOffsetMatrix.c1;
185
0
        bone->mOffsetMatrix.c2 = -bone->mOffsetMatrix.c2;
186
0
        bone->mOffsetMatrix.c4 = -bone->mOffsetMatrix.c4;
187
0
    }
188
189
    // mirror bitangents as well as they're derived from the texture coords
190
0
    if (pMesh->HasTangentsAndBitangents()) {
191
0
        for (unsigned int a = 0; a < pMesh->mNumVertices; a++)
192
0
            pMesh->mBitangents[a] *= -1.0f;
193
0
    }
194
0
}
195
196
// ------------------------------------------------------------------------------------------------
197
// Converts a single material to left handed coordinates.
198
0
void MakeLeftHandedProcess::ProcessMaterial(aiMaterial *_mat) {
199
0
    if (nullptr == _mat) {
200
0
        ASSIMP_LOG_ERROR("Nullptr to aiMaterial found.");
201
0
        return;
202
0
    }
203
204
0
    aiMaterial *mat = (aiMaterial *)_mat;
205
0
    for (unsigned int a = 0; a < mat->mNumProperties; ++a) {
206
0
        aiMaterialProperty *prop = mat->mProperties[a];
207
208
        // Mapping axis for UV mappings?
209
0
        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
0
    }
215
0
}
216
217
// ------------------------------------------------------------------------------------------------
218
// Converts the given animation to LH coordinates.
219
37
void MakeLeftHandedProcess::ProcessAnimation(aiNodeAnim *pAnim) {
220
    // position keys
221
120
    for (unsigned int a = 0; a < pAnim->mNumPositionKeys; a++)
222
83
        pAnim->mPositionKeys[a].mValue.z *= -1.0f;
223
224
    // rotation keys
225
82
    for (unsigned int a = 0; a < pAnim->mNumRotationKeys; a++) {
226
45
        pAnim->mRotationKeys[a].mValue.x *= -1.0f;
227
45
        pAnim->mRotationKeys[a].mValue.y *= -1.0f;
228
45
    }
229
37
}
230
231
// ------------------------------------------------------------------------------------------------
232
// Converts a single camera to left handed coordinates.
233
void MakeLeftHandedProcess::ProcessCamera( aiCamera* pCam)
234
59
{
235
59
    pCam->mLookAt = ai_real(2.0f) * pCam->mPosition - pCam->mLookAt;
236
59
}
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
383
FlipUVsProcess::FlipUVsProcess() = default;
245
246
// ------------------------------------------------------------------------------------------------
247
// Destructor, private as well
248
383
FlipUVsProcess::~FlipUVsProcess() = default;
249
250
// ------------------------------------------------------------------------------------------------
251
// Returns whether the processing step is present in the given flag field.
252
106
bool FlipUVsProcess::IsActive(unsigned int pFlags) const {
253
106
    return 0 != (pFlags & aiProcess_FlipUVs);
254
106
}
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
106
bool FlipWindingOrderProcess::IsActive(unsigned int pFlags) const {
307
106
    return 0 != (pFlags & aiProcess_FlipWindingOrder);
308
106
}
309
310
// ------------------------------------------------------------------------------------------------
311
// Executes the post processing step on the given imported data.
312
28
void FlipWindingOrderProcess::Execute(aiScene *pScene) {
313
28
    ASSIMP_LOG_DEBUG("FlipWindingOrderProcess begin");
314
28
    for (unsigned int i = 0; i < pScene->mNumMeshes; ++i)
315
0
        ProcessMesh(pScene->mMeshes[i]);
316
28
    ASSIMP_LOG_DEBUG("FlipWindingOrderProcess finished");
317
28
}
318
319
// ------------------------------------------------------------------------------------------------
320
// Converts a single mesh
321
0
void FlipWindingOrderProcess::ProcessMesh(aiMesh *pMesh) {
322
    // invert the order of all faces in this mesh
323
0
    for (unsigned int a = 0; a < pMesh->mNumFaces; a++) {
324
0
        aiFace &face = pMesh->mFaces[a];
325
0
        for (unsigned int b = 0; b < face.mNumIndices / 2; b++) {
326
0
            std::swap(face.mIndices[b], face.mIndices[face.mNumIndices - 1 - b]);
327
0
        }
328
0
    }
329
330
    // invert the order of all components in this mesh anim meshes
331
0
    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
0
}
366
367
#endif // !! ASSIMP_BUILD_NO_FLIPWINDING_PROCESS