Coverage Report

Created: 2024-08-02 07:04

/src/assimp/code/PostProcessing/ProcessHelper.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2024, assimp team
6
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
12
following 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
43
/// @file ProcessHelper.cpp
44
/** Implement shared utility functions for postprocessing steps */
45
46
#include "ProcessHelper.h"
47
48
#include <limits>
49
50
namespace Assimp {
51
52
// -------------------------------------------------------------------------------
53
0
void ConvertListToStrings(const std::string &in, std::list<std::string> &out) {
54
0
    const char *s = in.c_str();
55
0
    const char *end = in.c_str() + in.size();
56
0
    while (*s) {
57
0
        SkipSpacesAndLineEnd(&s, end);
58
0
        if (*s == '\'') {
59
0
            const char *base = ++s;
60
0
            while (*s != '\'') {
61
0
                ++s;
62
0
                if (*s == '\0') {
63
0
                    ASSIMP_LOG_ERROR("ConvertListToString: String list is ill-formatted");
64
0
                    return;
65
0
                }
66
0
            }
67
0
            out.emplace_back(base, (size_t)(s - base));
68
0
            ++s;
69
0
        } else {
70
0
            out.push_back(GetNextToken(s, end));
71
0
        }
72
0
    }
73
0
}
74
75
// -------------------------------------------------------------------------------
76
void FindAABBTransformed(const aiMesh *mesh, aiVector3D &min, aiVector3D &max,
77
0
        const aiMatrix4x4 &m) {
78
0
    min = aiVector3D(ai_real(10e10), ai_real(10e10), ai_real(10e10));
79
0
    max = aiVector3D(ai_real(-10e10), ai_real(-10e10), ai_real(-10e10));
80
0
    for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
81
0
        const aiVector3D v = m * mesh->mVertices[i];
82
0
        min = std::min(v, min);
83
0
        max = std::max(v, max);
84
0
    }
85
0
}
86
87
// -------------------------------------------------------------------------------
88
0
void FindMeshCenter(aiMesh *mesh, aiVector3D &out, aiVector3D &min, aiVector3D &max) {
89
0
    ArrayBounds(mesh->mVertices, mesh->mNumVertices, min, max);
90
0
    out = min + (max - min) * (ai_real)0.5;
91
0
}
92
93
// -------------------------------------------------------------------------------
94
0
void FindSceneCenter(aiScene *scene, aiVector3D &out, aiVector3D &min, aiVector3D &max) {
95
0
    if (nullptr == scene) {
96
0
        return;
97
0
    }
98
99
0
    if (0 == scene->mNumMeshes) {
100
0
        return;
101
0
    }
102
0
    FindMeshCenter(scene->mMeshes[0], out, min, max);
103
0
    for (unsigned int i = 1; i < scene->mNumMeshes; ++i) {
104
0
        aiVector3D tout, tmin, tmax;
105
0
        FindMeshCenter(scene->mMeshes[i], tout, tmin, tmax);
106
0
        if (min[0] > tmin[0]) min[0] = tmin[0];
107
0
        if (min[1] > tmin[1]) min[1] = tmin[1];
108
0
        if (min[2] > tmin[2]) min[2] = tmin[2];
109
0
        if (max[0] < tmax[0]) max[0] = tmax[0];
110
0
        if (max[1] < tmax[1]) max[1] = tmax[1];
111
0
        if (max[2] < tmax[2]) max[2] = tmax[2];
112
0
    }
113
0
    out = min + (max - min) * (ai_real)0.5;
114
0
}
115
116
// -------------------------------------------------------------------------------
117
void FindMeshCenterTransformed(aiMesh *mesh, aiVector3D &out, aiVector3D &min,
118
0
        aiVector3D &max, const aiMatrix4x4 &m) {
119
0
    FindAABBTransformed(mesh, min, max, m);
120
0
    out = min + (max - min) * (ai_real)0.5;
121
0
}
122
123
// -------------------------------------------------------------------------------
124
0
void FindMeshCenter(aiMesh *mesh, aiVector3D &out) {
125
0
    aiVector3D min, max;
126
0
    FindMeshCenter(mesh, out, min, max);
127
0
}
128
129
// -------------------------------------------------------------------------------
130
void FindMeshCenterTransformed(aiMesh *mesh, aiVector3D &out,
131
0
        const aiMatrix4x4 &m) {
132
0
    aiVector3D min, max;
133
0
    FindMeshCenterTransformed(mesh, out, min, max, m);
134
0
}
135
136
// -------------------------------------------------------------------------------
137
0
ai_real ComputePositionEpsilon(const aiMesh *pMesh) {
138
0
    const ai_real epsilon = ai_real(1e-4);
139
140
    // calculate the position bounds so we have a reliable epsilon to check position differences against
141
0
    aiVector3D minVec, maxVec;
142
0
    ArrayBounds(pMesh->mVertices, pMesh->mNumVertices, minVec, maxVec);
143
0
    return (maxVec - minVec).Length() * epsilon;
144
0
}
145
146
// -------------------------------------------------------------------------------
147
0
ai_real ComputePositionEpsilon(const aiMesh *const *pMeshes, size_t num) {
148
0
    ai_assert(nullptr != pMeshes);
149
150
0
    const ai_real epsilon = ai_real(1e-4);
151
152
    // calculate the position bounds so we have a reliable epsilon to check position differences against
153
0
    aiVector3D minVec, maxVec, mi, ma;
154
0
    MinMaxChooser<aiVector3D>()(minVec, maxVec);
155
156
0
    for (size_t a = 0; a < num; ++a) {
157
0
        const aiMesh *pMesh = pMeshes[a];
158
0
        ArrayBounds(pMesh->mVertices, pMesh->mNumVertices, mi, ma);
159
160
0
        minVec = std::min(minVec, mi);
161
0
        maxVec = std::max(maxVec, ma);
162
0
    }
163
0
    return (maxVec - minVec).Length() * epsilon;
164
0
}
165
166
// -------------------------------------------------------------------------------
167
0
unsigned int GetMeshVFormatUnique(const aiMesh *pcMesh) {
168
0
    ai_assert(nullptr != pcMesh);
169
170
    // FIX: the hash may never be 0. Otherwise a comparison against
171
    // nullptr could be successful
172
0
    unsigned int iRet = 1;
173
174
    // normals
175
0
    if (pcMesh->HasNormals()) iRet |= 0x2;
176
    // tangents and bitangents
177
0
    if (pcMesh->HasTangentsAndBitangents()) iRet |= 0x4;
178
179
180
0
    static_assert(8 >= AI_MAX_NUMBER_OF_COLOR_SETS, "static_assert(8 >= AI_MAX_NUMBER_OF_COLOR_SETS)");
181
0
    static_assert(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS, "static_assert(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS)");
182
183
    // texture coordinates
184
0
    unsigned int p = 0;
185
0
    while (pcMesh->HasTextureCoords(p)) {
186
0
        iRet |= (0x100 << p);
187
0
        if (3 == pcMesh->mNumUVComponents[p])
188
0
            iRet |= (0x10000 << p);
189
190
0
        ++p;
191
0
    }
192
    // vertex colors
193
0
    p = 0;
194
0
    while (pcMesh->HasVertexColors(p))
195
0
        iRet |= (0x1000000 << p++);
196
0
    return iRet;
197
0
}
198
199
// -------------------------------------------------------------------------------
200
0
VertexWeightTable *ComputeVertexBoneWeightTable(const aiMesh *pMesh) {
201
0
    if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) {
202
0
        return nullptr;
203
0
    }
204
205
0
    VertexWeightTable *avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices];
206
0
    for (unsigned int i = 0; i < pMesh->mNumBones; ++i) {
207
208
0
        aiBone *bone = pMesh->mBones[i];
209
0
        for (unsigned int a = 0; a < bone->mNumWeights; ++a) {
210
0
            const aiVertexWeight &weight = bone->mWeights[a];
211
0
            avPerVertexWeights[weight.mVertexId].emplace_back(i, weight.mWeight);
212
0
        }
213
0
    }
214
0
    return avPerVertexWeights;
215
0
}
216
217
// -------------------------------------------------------------------------------
218
0
const char *MappingTypeToString(aiTextureMapping in) {
219
0
    switch (in) {
220
0
    case aiTextureMapping_UV:
221
0
        return "UV";
222
0
    case aiTextureMapping_BOX:
223
0
        return "Box";
224
0
    case aiTextureMapping_SPHERE:
225
0
        return "Sphere";
226
0
    case aiTextureMapping_CYLINDER:
227
0
        return "Cylinder";
228
0
    case aiTextureMapping_PLANE:
229
0
        return "Plane";
230
0
    case aiTextureMapping_OTHER:
231
0
        return "Other";
232
0
    default:
233
0
        break;
234
0
    }
235
236
0
    ai_assert(false);
237
0
    return "BUG";
238
0
}
239
240
// -------------------------------------------------------------------------------
241
0
aiMesh *MakeSubmesh(const aiMesh *pMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags) {
242
0
    aiMesh *oMesh = new aiMesh();
243
0
    std::vector<unsigned int> vMap(pMesh->mNumVertices, UINT_MAX);
244
245
0
    size_t numSubVerts = 0;
246
0
    size_t numSubFaces = subMeshFaces.size();
247
248
0
    for (unsigned int i = 0; i < numSubFaces; i++) {
249
0
        const aiFace &f = pMesh->mFaces[subMeshFaces[i]];
250
251
0
        for (unsigned int j = 0; j < f.mNumIndices; j++) {
252
0
            if (vMap[f.mIndices[j]] == UINT_MAX) {
253
0
                vMap[f.mIndices[j]] = static_cast<unsigned int>(numSubVerts++);
254
0
            }
255
0
        }
256
0
    }
257
258
0
    oMesh->mName = pMesh->mName;
259
260
0
    oMesh->mMaterialIndex = pMesh->mMaterialIndex;
261
0
    oMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
262
263
    // create all the arrays for this mesh if the old mesh contained them
264
265
0
    oMesh->mNumFaces = static_cast<unsigned int>(subMeshFaces.size());
266
0
    oMesh->mNumVertices = static_cast<unsigned int>(numSubVerts);
267
0
    oMesh->mVertices = new aiVector3D[numSubVerts];
268
0
    if (pMesh->HasNormals()) {
269
0
        oMesh->mNormals = new aiVector3D[numSubVerts];
270
0
    }
271
272
0
    if (pMesh->HasTangentsAndBitangents()) {
273
0
        oMesh->mTangents = new aiVector3D[numSubVerts];
274
0
        oMesh->mBitangents = new aiVector3D[numSubVerts];
275
0
    }
276
277
0
    for (size_t a = 0; pMesh->HasTextureCoords(static_cast<unsigned int>(a)); ++a) {
278
0
        oMesh->mTextureCoords[a] = new aiVector3D[numSubVerts];
279
0
        oMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
280
0
    }
281
282
0
    for (size_t a = 0; pMesh->HasVertexColors(static_cast<unsigned int>(a)); ++a) {
283
0
        oMesh->mColors[a] = new aiColor4D[numSubVerts];
284
0
    }
285
286
    // and copy over the data, generating faces with linear indices along the way
287
0
    oMesh->mFaces = new aiFace[numSubFaces];
288
289
0
    for (unsigned int a = 0; a < numSubFaces; ++a) {
290
291
0
        const aiFace &srcFace = pMesh->mFaces[subMeshFaces[a]];
292
0
        aiFace &dstFace = oMesh->mFaces[a];
293
0
        dstFace.mNumIndices = srcFace.mNumIndices;
294
0
        dstFace.mIndices = new unsigned int[dstFace.mNumIndices];
295
296
        // accumulate linearly all the vertices of the source face
297
0
        for (size_t b = 0; b < dstFace.mNumIndices; ++b) {
298
0
            dstFace.mIndices[b] = vMap[srcFace.mIndices[b]];
299
0
        }
300
0
    }
301
302
0
    for (unsigned int srcIndex = 0; srcIndex < pMesh->mNumVertices; ++srcIndex) {
303
0
        unsigned int nvi = vMap[srcIndex];
304
0
        if (nvi == UINT_MAX) {
305
0
            continue;
306
0
        }
307
308
0
        oMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
309
0
        if (pMesh->HasNormals()) {
310
0
            oMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
311
0
        }
312
313
0
        if (pMesh->HasTangentsAndBitangents()) {
314
0
            oMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
315
0
            oMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
316
0
        }
317
0
        for (size_t c = 0, cc = pMesh->GetNumUVChannels(); c < cc; ++c) {
318
0
            oMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
319
0
        }
320
0
        for (size_t c = 0, cc = pMesh->GetNumColorChannels(); c < cc; ++c) {
321
0
            oMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
322
0
        }
323
0
    }
324
325
0
    if (~subFlags & AI_SUBMESH_FLAGS_SANS_BONES) {
326
0
        std::vector<unsigned int> subBones(pMesh->mNumBones, 0);
327
328
0
        for (unsigned int a = 0; a < pMesh->mNumBones; ++a) {
329
0
            const aiBone *bone = pMesh->mBones[a];
330
331
0
            for (unsigned int b = 0; b < bone->mNumWeights; b++) {
332
0
                unsigned int v = vMap[bone->mWeights[b].mVertexId];
333
334
0
                if (v != UINT_MAX) {
335
0
                    subBones[a]++;
336
0
                }
337
0
            }
338
0
        }
339
340
0
        for (unsigned int a = 0; a < pMesh->mNumBones; ++a) {
341
0
            if (subBones[a] > 0) {
342
0
                oMesh->mNumBones++;
343
0
            }
344
0
        }
345
346
0
        if (oMesh->mNumBones) {
347
0
            oMesh->mBones = new aiBone *[oMesh->mNumBones]();
348
0
            unsigned int nbParanoia = oMesh->mNumBones;
349
350
0
            oMesh->mNumBones = 0; //rewind
351
352
0
            for (unsigned int a = 0; a < pMesh->mNumBones; ++a) {
353
0
                if (subBones[a] == 0) {
354
0
                    continue;
355
0
                }
356
0
                aiBone *newBone = new aiBone;
357
0
                oMesh->mBones[oMesh->mNumBones++] = newBone;
358
359
0
                const aiBone *bone = pMesh->mBones[a];
360
361
0
                newBone->mName = bone->mName;
362
0
                newBone->mOffsetMatrix = bone->mOffsetMatrix;
363
0
                newBone->mWeights = new aiVertexWeight[subBones[a]];
364
365
0
                for (unsigned int b = 0; b < bone->mNumWeights; b++) {
366
0
                    const unsigned int v = vMap[bone->mWeights[b].mVertexId];
367
368
0
                    if (v != UINT_MAX) {
369
0
                        aiVertexWeight w(v, bone->mWeights[b].mWeight);
370
0
                        newBone->mWeights[newBone->mNumWeights++] = w;
371
0
                    }
372
0
                }
373
0
            }
374
375
0
            ai_assert(nbParanoia == oMesh->mNumBones);
376
0
            (void)nbParanoia; // remove compiler warning on release build
377
0
        }
378
0
    }
379
380
0
    return oMesh;
381
0
}
382
383
} // namespace Assimp