/src/assimp/code/PostProcessing/JoinVerticesProcess.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 Implementation of the post processing step to join identical vertices |
43 | | * for all imported meshes |
44 | | */ |
45 | | |
46 | | #ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS |
47 | | |
48 | | #include "JoinVerticesProcess.h" |
49 | | #include "ProcessHelper.h" |
50 | | #include <assimp/Vertex.h> |
51 | | #include <assimp/TinyFormatter.h> |
52 | | |
53 | | #include <stdio.h> |
54 | | #include <unordered_set> |
55 | | #include <unordered_map> |
56 | | #include <memory> |
57 | | #include <map> |
58 | | |
59 | | using namespace Assimp; |
60 | | |
61 | | // ------------------------------------------------------------------------------------------------ |
62 | | // Returns whether the processing step is present in the given flag field. |
63 | 8.34k | bool JoinVerticesProcess::IsActive( unsigned int pFlags) const { |
64 | 8.34k | return (pFlags & aiProcess_JoinIdenticalVertices) != 0; |
65 | 8.34k | } |
66 | | // ------------------------------------------------------------------------------------------------ |
67 | | // Executes the post processing step on the given imported data. |
68 | 8.23k | void JoinVerticesProcess::Execute( aiScene* pScene) { |
69 | 8.23k | ASSIMP_LOG_DEBUG("JoinVerticesProcess begin"); |
70 | | |
71 | | // get the total number of vertices BEFORE the step is executed |
72 | 8.23k | int iNumOldVertices = 0; |
73 | 8.23k | if (!DefaultLogger::isNullLogger()) { |
74 | 0 | for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { |
75 | 0 | iNumOldVertices += pScene->mMeshes[a]->mNumVertices; |
76 | 0 | } |
77 | 0 | } |
78 | | |
79 | | // execute the step |
80 | 8.23k | int iNumVertices = 0; |
81 | 72.5k | for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { |
82 | 64.2k | iNumVertices += ProcessMesh( pScene->mMeshes[a],a); |
83 | 64.2k | } |
84 | | |
85 | 8.23k | pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; |
86 | | |
87 | | // if logging is active, print detailed statistics |
88 | 8.23k | if (!DefaultLogger::isNullLogger()) { |
89 | 0 | if (iNumOldVertices == iNumVertices) { |
90 | 0 | ASSIMP_LOG_DEBUG("JoinVerticesProcess finished "); |
91 | 0 | return; |
92 | 0 | } |
93 | | |
94 | | // Show statistics |
95 | 0 | ASSIMP_LOG_INFO("JoinVerticesProcess finished | Verts in: ", iNumOldVertices, |
96 | 0 | " out: ", iNumVertices, " | ~", |
97 | 0 | ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f ); |
98 | 0 | } |
99 | 8.23k | } |
100 | | |
101 | | namespace { |
102 | | |
103 | | struct CompareVerticesAlmostEqual { |
104 | 8.38M | bool operator () (const Vertex & a, const Vertex & b) const { |
105 | 8.38M | static const float epsilon = 1e-5f; |
106 | 8.38M | static const float squareEpsilon = epsilon * epsilon; |
107 | | |
108 | 8.38M | if ((a.position - b.position).SquareLength() > squareEpsilon) { |
109 | 108 | return false; |
110 | 108 | } |
111 | | |
112 | | // We just test the other attributes even if they're not present in the mesh. |
113 | | // In this case they're initialized to 0 so the comparison succeeds. |
114 | | // By this method the non-present attributes are effectively ignored in the comparison. |
115 | | |
116 | 8.38M | if ((a.normal - b.normal).SquareLength() > squareEpsilon) { |
117 | 2.06M | return false; |
118 | 2.06M | } |
119 | | |
120 | 6.31M | if ((a.tangent - b.tangent).SquareLength() > squareEpsilon) { |
121 | 1.19M | return false; |
122 | 1.19M | } |
123 | | |
124 | 5.11M | if ((a.bitangent - b.bitangent).SquareLength() > squareEpsilon) { |
125 | 28.6k | return false; |
126 | 28.6k | } |
127 | | |
128 | 45.2M | for (uint32_t i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i ++) { |
129 | 40.2M | if ((a.texcoords[i] - b.texcoords[i]).SquareLength() > squareEpsilon) { |
130 | 71.8k | return false; |
131 | 71.8k | } |
132 | 40.2M | } |
133 | | |
134 | 40.9M | for (uint32_t i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; i ++) { |
135 | 36.4M | if (GetColorDifference(a.colors[i], b.colors[i]) > squareEpsilon) { |
136 | 522k | return false; |
137 | 522k | } |
138 | 36.4M | } |
139 | | |
140 | | // If reached this point, they are ~equal |
141 | 4.49M | return true; |
142 | 5.01M | } |
143 | | }; |
144 | | |
145 | | struct HashVertex { |
146 | 28.7M | inline void hash_combine(std::size_t& seed, const ai_real& v) const { |
147 | 28.7M | std::hash<ai_real> hasher; |
148 | 28.7M | seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); |
149 | 28.7M | } |
150 | | |
151 | 9.57M | size_t operator () (const Vertex & v) const { |
152 | 9.57M | size_t hash = 0; |
153 | | |
154 | 9.57M | hash_combine(hash, v.position.x); |
155 | 9.57M | hash_combine(hash, v.position.y); |
156 | 9.57M | hash_combine(hash, v.position.z); |
157 | | |
158 | 9.57M | return hash; |
159 | 9.57M | } |
160 | | }; |
161 | | |
162 | | template<class XMesh> |
163 | 64.2k | void updateXMeshVertices(XMesh *pMesh, std::vector<int> &uniqueVertices) { |
164 | | // replace vertex data with the unique data sets |
165 | 64.2k | pMesh->mNumVertices = (unsigned int)uniqueVertices.size(); |
166 | | |
167 | | // ---------------------------------------------------------------------------- |
168 | | // NOTE - we're *not* calling Vertex::SortBack() because it would check for |
169 | | // presence of every single vertex component once PER VERTEX. And our CPU |
170 | | // dislikes branches, even if they're easily predictable. |
171 | | // ---------------------------------------------------------------------------- |
172 | | |
173 | | // Position, if present (check made for aiAnimMesh) |
174 | 64.2k | if (pMesh->mVertices) { |
175 | 64.2k | std::unique_ptr<aiVector3D[]> oldVertices(pMesh->mVertices); |
176 | 64.2k | pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; |
177 | 2.63M | for (unsigned int a = 0; a < pMesh->mNumVertices; a++) |
178 | 2.56M | pMesh->mVertices[a] = oldVertices[uniqueVertices[a]]; |
179 | 64.2k | } |
180 | | |
181 | | // Normals, if present |
182 | 64.2k | if (pMesh->mNormals) { |
183 | 57.1k | std::unique_ptr<aiVector3D[]> oldNormals(pMesh->mNormals); |
184 | 57.1k | pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; |
185 | 2.59M | for (unsigned int a = 0; a < pMesh->mNumVertices; a++) |
186 | 2.53M | pMesh->mNormals[a] = oldNormals[uniqueVertices[a]]; |
187 | 57.1k | } |
188 | | // Tangents, if present |
189 | 64.2k | if (pMesh->mTangents) { |
190 | 9.44k | std::unique_ptr<aiVector3D[]> oldTangents(pMesh->mTangents); |
191 | 9.44k | pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; |
192 | 753k | for (unsigned int a = 0; a < pMesh->mNumVertices; a++) |
193 | 743k | pMesh->mTangents[a] = oldTangents[uniqueVertices[a]]; |
194 | 9.44k | } |
195 | | // Bitangents as well |
196 | 64.2k | if (pMesh->mBitangents) { |
197 | 9.44k | std::unique_ptr<aiVector3D[]> oldBitangents(pMesh->mBitangents); |
198 | 9.44k | pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; |
199 | 753k | for (unsigned int a = 0; a < pMesh->mNumVertices; a++) |
200 | 743k | pMesh->mBitangents[a] = oldBitangents[uniqueVertices[a]]; |
201 | 9.44k | } |
202 | | // Vertex colors |
203 | 84.3k | for (unsigned int a = 0; pMesh->HasVertexColors(a); a++) { |
204 | 20.1k | std::unique_ptr<aiColor4D[]> oldColors(pMesh->mColors[a]); |
205 | 20.1k | pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices]; |
206 | 631k | for (unsigned int b = 0; b < pMesh->mNumVertices; b++) |
207 | 611k | pMesh->mColors[a][b] = oldColors[uniqueVertices[b]]; |
208 | 20.1k | } |
209 | | // Texture coords |
210 | 75.2k | for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++) { |
211 | 10.9k | std::unique_ptr<aiVector3D[]> oldTextureCoords(pMesh->mTextureCoords[a]); |
212 | 10.9k | pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices]; |
213 | 763k | for (unsigned int b = 0; b < pMesh->mNumVertices; b++) |
214 | 752k | pMesh->mTextureCoords[a][b] = oldTextureCoords[uniqueVertices[b]]; |
215 | 10.9k | } |
216 | 64.2k | } JoinVerticesProcess.cpp:void (anonymous namespace)::updateXMeshVertices<aiMesh>(aiMesh*, std::__1::vector<int, std::__1::allocator<int> >&) Line | Count | Source | 163 | 64.2k | void updateXMeshVertices(XMesh *pMesh, std::vector<int> &uniqueVertices) { | 164 | | // replace vertex data with the unique data sets | 165 | 64.2k | pMesh->mNumVertices = (unsigned int)uniqueVertices.size(); | 166 | | | 167 | | // ---------------------------------------------------------------------------- | 168 | | // NOTE - we're *not* calling Vertex::SortBack() because it would check for | 169 | | // presence of every single vertex component once PER VERTEX. And our CPU | 170 | | // dislikes branches, even if they're easily predictable. | 171 | | // ---------------------------------------------------------------------------- | 172 | | | 173 | | // Position, if present (check made for aiAnimMesh) | 174 | 64.2k | if (pMesh->mVertices) { | 175 | 64.2k | std::unique_ptr<aiVector3D[]> oldVertices(pMesh->mVertices); | 176 | 64.2k | pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; | 177 | 2.63M | for (unsigned int a = 0; a < pMesh->mNumVertices; a++) | 178 | 2.56M | pMesh->mVertices[a] = oldVertices[uniqueVertices[a]]; | 179 | 64.2k | } | 180 | | | 181 | | // Normals, if present | 182 | 64.2k | if (pMesh->mNormals) { | 183 | 57.1k | std::unique_ptr<aiVector3D[]> oldNormals(pMesh->mNormals); | 184 | 57.1k | pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; | 185 | 2.59M | for (unsigned int a = 0; a < pMesh->mNumVertices; a++) | 186 | 2.53M | pMesh->mNormals[a] = oldNormals[uniqueVertices[a]]; | 187 | 57.1k | } | 188 | | // Tangents, if present | 189 | 64.2k | if (pMesh->mTangents) { | 190 | 9.44k | std::unique_ptr<aiVector3D[]> oldTangents(pMesh->mTangents); | 191 | 9.44k | pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; | 192 | 753k | for (unsigned int a = 0; a < pMesh->mNumVertices; a++) | 193 | 743k | pMesh->mTangents[a] = oldTangents[uniqueVertices[a]]; | 194 | 9.44k | } | 195 | | // Bitangents as well | 196 | 64.2k | if (pMesh->mBitangents) { | 197 | 9.44k | std::unique_ptr<aiVector3D[]> oldBitangents(pMesh->mBitangents); | 198 | 9.44k | pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; | 199 | 753k | for (unsigned int a = 0; a < pMesh->mNumVertices; a++) | 200 | 743k | pMesh->mBitangents[a] = oldBitangents[uniqueVertices[a]]; | 201 | 9.44k | } | 202 | | // Vertex colors | 203 | 84.3k | for (unsigned int a = 0; pMesh->HasVertexColors(a); a++) { | 204 | 20.1k | std::unique_ptr<aiColor4D[]> oldColors(pMesh->mColors[a]); | 205 | 20.1k | pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices]; | 206 | 631k | for (unsigned int b = 0; b < pMesh->mNumVertices; b++) | 207 | 611k | pMesh->mColors[a][b] = oldColors[uniqueVertices[b]]; | 208 | 20.1k | } | 209 | | // Texture coords | 210 | 75.2k | for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++) { | 211 | 10.9k | std::unique_ptr<aiVector3D[]> oldTextureCoords(pMesh->mTextureCoords[a]); | 212 | 10.9k | pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices]; | 213 | 763k | for (unsigned int b = 0; b < pMesh->mNumVertices; b++) | 214 | 752k | pMesh->mTextureCoords[a][b] = oldTextureCoords[uniqueVertices[b]]; | 215 | 10.9k | } | 216 | 64.2k | } |
Unexecuted instantiation: JoinVerticesProcess.cpp:void (anonymous namespace)::updateXMeshVertices<aiAnimMesh>(aiAnimMesh*, std::__1::vector<int, std::__1::allocator<int> >&) |
217 | | |
218 | | } // namespace |
219 | | |
220 | | // ------------------------------------------------------------------------------------------------ |
221 | | |
222 | | static constexpr size_t JOINED_VERTICES_MARK = 0x80000000u; |
223 | | |
224 | | // now start the JoinVerticesProcess |
225 | 64.2k | int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) { |
226 | 64.2k | static_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 8, "AI_MAX_NUMBER_OF_COLOR_SETS == 8"); |
227 | 64.2k | static_assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 8, "AI_MAX_NUMBER_OF_TEXTURECOORDS == 8"); |
228 | | |
229 | | // Return early if we don't have any positions |
230 | 64.2k | if (!pMesh->HasPositions() || !pMesh->HasFaces()) { |
231 | 0 | return 0; |
232 | 0 | } |
233 | | |
234 | | // We should care only about used vertices, not all of them |
235 | | // (this can happen due to original file vertices buffer being used by |
236 | | // multiple meshes) |
237 | 64.2k | std::vector<bool> usedVertexIndicesMask; |
238 | 64.2k | usedVertexIndicesMask.resize(pMesh->mNumVertices, false); |
239 | 2.90M | for (unsigned int a = 0; a < pMesh->mNumFaces; a++) { |
240 | 2.84M | aiFace& face = pMesh->mFaces[a]; |
241 | 11.2M | for (unsigned int b = 0; b < face.mNumIndices; b++) { |
242 | 8.40M | usedVertexIndicesMask[face.mIndices[b]] = true; |
243 | 8.40M | } |
244 | 2.84M | } |
245 | | |
246 | | // We'll never have more vertices afterwards. |
247 | 64.2k | std::vector<int> uniqueVertices; |
248 | | |
249 | | // For each vertex the index of the vertex it was replaced by. |
250 | | // Since the maximal number of vertices is 2^31-1, the most significand bit can be used to mark |
251 | | // whether a new vertex was created for the index (true) or if it was replaced by an existing |
252 | | // unique vertex (false). This saves an additional std::vector<bool> and greatly enhances |
253 | | // branching performance. |
254 | 64.2k | static_assert(AI_MAX_VERTICES == 0x7fffffff, "AI_MAX_VERTICES == 0x7fffffff"); |
255 | 64.2k | std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff); |
256 | | |
257 | | // Run an optimized code path if we don't have multiple UVs or vertex colors. |
258 | | // This should yield false in more than 99% of all imports ... |
259 | 64.2k | const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0; |
260 | | |
261 | | // We'll never have more vertices afterwards. |
262 | 64.2k | std::vector<std::vector<int>> uniqueAnimatedVertices; |
263 | 64.2k | if (hasAnimMeshes) { |
264 | 0 | uniqueAnimatedVertices.resize(pMesh->mNumAnimMeshes); |
265 | 0 | for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { |
266 | 0 | uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices); |
267 | 0 | } |
268 | 0 | } |
269 | | // a map that maps a vertex to its new index |
270 | 64.2k | std::unordered_map<Vertex, int, HashVertex, CompareVerticesAlmostEqual> vertex2Index = {}; |
271 | | // we can not end up with more vertices than we started with |
272 | | // Now check each vertex if it brings something new to the table |
273 | 64.2k | int newIndex = 0; |
274 | 7.55M | for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { |
275 | | // if the vertex is unused Do nothing |
276 | 7.49M | if (!usedVertexIndicesMask[a]) { |
277 | 430k | continue; |
278 | 430k | } |
279 | | // collect the vertex data |
280 | 7.06M | Vertex v(pMesh,a); |
281 | | // is the vertex already in the map? |
282 | 7.06M | auto it = vertex2Index.find(v); |
283 | | // if the vertex is not in the map then it is a new vertex add it. |
284 | 7.06M | if (it == vertex2Index.end()) { |
285 | | // this is a new vertex give it a new index |
286 | 2.56M | vertex2Index.emplace(v, newIndex); |
287 | | // keep track of its index and increment 1 |
288 | 2.56M | replaceIndex[a] = newIndex++; |
289 | | // add the vertex to the unique vertices |
290 | 2.56M | uniqueVertices.push_back(a); |
291 | 2.56M | if (hasAnimMeshes) { |
292 | 0 | for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { |
293 | 0 | uniqueAnimatedVertices[animMeshIndex].emplace_back(a); |
294 | 0 | } |
295 | 0 | } |
296 | 4.49M | } else{ |
297 | | // if the vertex is already there just find the replace index that is appropriate to it |
298 | | // mark it with JOINED_VERTICES_MARK |
299 | 4.49M | replaceIndex[a] = it->second | JOINED_VERTICES_MARK; |
300 | 4.49M | } |
301 | 7.06M | } |
302 | | |
303 | 64.2k | if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) { |
304 | 0 | ASSIMP_LOG_VERBOSE_DEBUG( |
305 | 0 | "Mesh ",meshIndex, |
306 | 0 | " (", |
307 | 0 | (pMesh->mName.length ? pMesh->mName.data : "unnamed"), |
308 | 0 | ") | Verts in: ",pMesh->mNumVertices, |
309 | 0 | " out: ", |
310 | 0 | uniqueVertices.size(), |
311 | 0 | " | ~", |
312 | 0 | ((pMesh->mNumVertices - uniqueVertices.size()) / (float)pMesh->mNumVertices) * 100.f, |
313 | 0 | "%" |
314 | 0 | ); |
315 | 0 | } |
316 | | |
317 | 64.2k | updateXMeshVertices(pMesh, uniqueVertices); |
318 | 64.2k | if (hasAnimMeshes) { |
319 | 0 | for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { |
320 | 0 | updateXMeshVertices(pMesh->mAnimMeshes[animMeshIndex], uniqueAnimatedVertices[animMeshIndex]); |
321 | 0 | } |
322 | 0 | } |
323 | | |
324 | | // adjust the indices in all faces |
325 | 2.90M | for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { |
326 | 2.84M | aiFace& face = pMesh->mFaces[a]; |
327 | 11.2M | for( unsigned int b = 0; b < face.mNumIndices; b++) { |
328 | 8.40M | face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~JOINED_VERTICES_MARK; |
329 | 8.40M | } |
330 | 2.84M | } |
331 | | |
332 | | // adjust bone vertex weights. |
333 | 71.5k | for( int a = 0; a < (int)pMesh->mNumBones; a++) { |
334 | 7.29k | aiBone* bone = pMesh->mBones[a]; |
335 | 7.29k | std::vector<aiVertexWeight> newWeights; |
336 | 7.29k | newWeights.reserve( bone->mNumWeights); |
337 | | |
338 | 7.29k | if (nullptr != bone->mWeights) { |
339 | 916k | for ( unsigned int b = 0; b < bone->mNumWeights; b++ ) { |
340 | 909k | const aiVertexWeight& ow = bone->mWeights[ b ]; |
341 | | // if the vertex is a unique one, translate it |
342 | | // filter out joined vertices by JOINED_VERTICES_MARK. |
343 | 909k | if ( !( replaceIndex[ ow.mVertexId ] & JOINED_VERTICES_MARK ) ) { |
344 | 422k | aiVertexWeight nw; |
345 | 422k | nw.mVertexId = replaceIndex[ ow.mVertexId ]; |
346 | 422k | nw.mWeight = ow.mWeight; |
347 | 422k | newWeights.push_back( nw ); |
348 | 422k | } |
349 | 909k | } |
350 | 7.29k | } else { |
351 | 0 | ASSIMP_LOG_ERROR( "X-Export: aiBone shall contain weights, but pointer to them is nullptr." ); |
352 | 0 | } |
353 | | |
354 | 7.29k | if (newWeights.size() > 0) { |
355 | | // kill the old and replace them with the translated weights |
356 | 6.26k | delete [] bone->mWeights; |
357 | 6.26k | bone->mNumWeights = (unsigned int)newWeights.size(); |
358 | | |
359 | 6.26k | bone->mWeights = new aiVertexWeight[bone->mNumWeights]; |
360 | 6.26k | memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight)); |
361 | 6.26k | } |
362 | 7.29k | } |
363 | 64.2k | return pMesh->mNumVertices; |
364 | 64.2k | } |
365 | | |
366 | | #endif // !! ASSIMP_BUILD_NO_JOINVERTICES_PROCESS |