Coverage Report

Created: 2026-01-25 07:15

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-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