Coverage Report

Created: 2026-09-14 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/PostProcessing/LimitBoneWeightsProcess.cpp
Line
Count
Source
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2026, assimp team
6
7
All rights reserved.
8
9
Redistribution and use of this software in source and binary forms,
10
with or without modification, are permitted provided that the
11
following conditions are met:
12
13
* Redistributions of source code must retain the above
14
  copyright notice, this list of conditions and the
15
  following disclaimer.
16
17
* Redistributions in binary form must reproduce the above
18
  copyright notice, this list of conditions and the
19
  following disclaimer in the documentation and/or other
20
  materials provided with the distribution.
21
22
* Neither the name of the assimp team, nor the names of its
23
  contributors may be used to endorse or promote products
24
  derived from this software without specific prior
25
  written permission of the assimp team.
26
27
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
---------------------------------------------------------------------- */
39
#include "LimitBoneWeightsProcess.h"
40
#include <assimp/SmallVector.h>
41
#include <assimp/StringUtils.h>
42
#include <assimp/postprocess.h>
43
#include <assimp/DefaultLogger.hpp>
44
#include <assimp/scene.h>
45
#include <stdio.h>
46
47
namespace Assimp {
48
49
// Make sure this value is set.
50
#ifndef AI_LMW_MAX_WEIGHTS
51
#   define AI_LMW_MAX_WEIGHTS 16
52
#endif
53
54
// ------------------------------------------------------------------------------------------------
55
// Constructor to be privately used by Importer
56
LimitBoneWeightsProcess::LimitBoneWeightsProcess() :
57
151k
        mMaxWeights(AI_LMW_MAX_WEIGHTS), mRemoveEmptyBones(true) {
58
    // empty
59
151k
}
60
61
// ------------------------------------------------------------------------------------------------
62
// Returns whether the processing step is present in the given flag field.
63
119k
bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const {
64
119k
    return (pFlags & aiProcess_LimitBoneWeights) != 0;
65
119k
}
66
67
// ------------------------------------------------------------------------------------------------
68
// Executes the post processing step on the given imported data.
69
30.5k
void LimitBoneWeightsProcess::Execute( aiScene* pScene) {
70
30.5k
    ai_assert(pScene != nullptr);
71
72
30.5k
    ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess begin");
73
74
544k
    for (unsigned int m = 0; m < pScene->mNumMeshes; ++m) {
75
513k
        ProcessMesh(pScene->mMeshes[m]);
76
513k
    }
77
78
30.5k
    ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess end");
79
30.5k
}
80
81
// ------------------------------------------------------------------------------------------------
82
// Executes the post processing step on the given imported data.
83
30.5k
void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) {
84
30.5k
    this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS);
85
30.5k
    this->mRemoveEmptyBones = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, 1) != 0;
86
30.5k
}
87
88
// ------------------------------------------------------------------------------------------------
89
425
static unsigned int removeEmptyBones(aiMesh *pMesh) {
90
425
    ai_assert(pMesh != nullptr);
91
92
425
    unsigned int writeBone = 0;
93
3.62k
    for (unsigned int readBone = 0; readBone< pMesh->mNumBones; ++readBone) {
94
3.20k
        aiBone* bone = pMesh->mBones[readBone];
95
3.20k
        if (bone->mNumWeights > 0) {
96
1.94k
            pMesh->mBones[writeBone++] = bone;
97
1.94k
        } else {
98
1.26k
            delete bone;
99
1.26k
        }
100
3.20k
    }
101
102
425
    return writeBone;
103
425
}
104
105
// ------------------------------------------------------------------------------------------------
106
// Unites identical vertices in the given mesh
107
513k
void LimitBoneWeightsProcess::ProcessMesh(aiMesh* pMesh) {
108
513k
    if (!pMesh->HasBones())
109
509k
        return;
110
111
    // collect all bone weights per vertex
112
4.01k
    typedef SmallVector<Weight,8> VertexWeightArray;
113
4.01k
    typedef std::vector<VertexWeightArray> WeightsPerVertex;
114
4.01k
    WeightsPerVertex vertexWeights(pMesh->mNumVertices);
115
4.01k
    size_t maxVertexWeights = 0;
116
117
109k
    for (unsigned int b = 0; b < pMesh->mNumBones; ++b) {
118
105k
        const aiBone* bone = pMesh->mBones[b];
119
1.69M
        for (unsigned int w = 0; w < bone->mNumWeights; ++w) {
120
1.59M
            const aiVertexWeight& vw = bone->mWeights[w];
121
122
1.59M
            if (vertexWeights.size() <= vw.mVertexId)
123
976k
                continue;
124
125
615k
            vertexWeights[vw.mVertexId].push_back(Weight(b, vw.mWeight));
126
615k
            maxVertexWeights = std::max(maxVertexWeights, vertexWeights[vw.mVertexId].size());
127
615k
        }
128
105k
    }
129
130
4.01k
    if (maxVertexWeights <= mMaxWeights)
131
3.58k
        return;
132
133
425
    unsigned int removed = 0, old_bones = pMesh->mNumBones;
134
135
    // now cut the weight count if it exceeds the maximum
136
93.7k
    for (WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit) {
137
93.3k
        if (vit->size() <= mMaxWeights)
138
75.0k
            continue;
139
140
        // more than the defined maximum -> first sort by weight in descending order. That's
141
        // why we defined the < operator in such a weird way.
142
18.3k
        std::sort(vit->begin(), vit->end());
143
144
        // now kill everything beyond the maximum count
145
18.3k
        unsigned int m = static_cast<unsigned int>(vit->size());
146
18.3k
        vit->resize(mMaxWeights);
147
18.3k
        removed += static_cast<unsigned int>(m - vit->size());
148
149
        // and renormalize the weights
150
18.3k
        float sum = 0.0f;
151
91.7k
        for(const Weight* it = vit->begin(); it != vit->end(); ++it) {
152
73.3k
            sum += it->mWeight;
153
73.3k
        }
154
18.3k
        if (0.0f != sum) {
155
18.2k
            const float invSum = 1.0f / sum;
156
91.0k
            for(Weight* it = vit->begin(); it != vit->end(); ++it) {
157
72.8k
                it->mWeight *= invSum;
158
72.8k
            }
159
18.2k
        }
160
18.3k
    }
161
162
    // clear weight count for all bone
163
3.62k
    for (unsigned int a = 0; a < pMesh->mNumBones; ++a) {
164
3.20k
        pMesh->mBones[a]->mNumWeights = 0;
165
3.20k
    }
166
167
    // rebuild the vertex weight array for all bones
168
93.7k
    for (unsigned int a = 0; a < vertexWeights.size(); ++a) {
169
93.3k
        const VertexWeightArray& vw = vertexWeights[a];
170
374k
        for (const Weight* it = vw.begin(); it != vw.end(); ++it) {
171
281k
            aiBone* bone = pMesh->mBones[it->mBone];
172
281k
            bone->mWeights[bone->mNumWeights++] = aiVertexWeight(a, it->mWeight);
173
281k
        }
174
93.3k
    }
175
176
    // remove empty bones
177
425
    if (mRemoveEmptyBones) {
178
425
        pMesh->mNumBones = removeEmptyBones(pMesh);
179
425
    }
180
181
425
    if (!DefaultLogger::isNullLogger()) {
182
        ASSIMP_LOG_INFO("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones);
183
0
    }
184
425
}
185
186
} // namespace Assimp