/src/assimp/code/AssetLib/Assjson/mesh_splitter.cpp
Line | Count | Source |
1 | | /* |
2 | | Assimp2Json |
3 | | Copyright (c) 2011, Alexander C. Gessler |
4 | | |
5 | | Licensed under a 3-clause BSD license. See the LICENSE file for more information. |
6 | | |
7 | | */ |
8 | | |
9 | | #include "mesh_splitter.h" |
10 | | |
11 | | #include <assimp/scene.h> |
12 | | |
13 | | // ---------------------------------------------------------------------------- |
14 | | // Note: this is largely based on assimp's SplitLargeMeshes_Vertex process. |
15 | | // it is refactored and the coding style is slightly improved, though. |
16 | | // ---------------------------------------------------------------------------- |
17 | | |
18 | | // ------------------------------------------------------------------------------------------------ |
19 | | // Executes the post processing step on the given imported data. |
20 | 0 | void MeshSplitter::Execute( aiScene* pScene) { |
21 | 0 | std::vector<std::pair<aiMesh*, unsigned int> > source_mesh_map; |
22 | |
|
23 | 0 | for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { |
24 | 0 | SplitMesh(a, pScene->mMeshes[a],source_mesh_map); |
25 | 0 | } |
26 | |
|
27 | 0 | const unsigned int size = static_cast<unsigned int>(source_mesh_map.size()); |
28 | 0 | if (size != pScene->mNumMeshes) { |
29 | | // it seems something has been split. rebuild the mesh list |
30 | 0 | delete[] pScene->mMeshes; |
31 | 0 | pScene->mNumMeshes = size; |
32 | 0 | pScene->mMeshes = new aiMesh*[size](); |
33 | |
|
34 | 0 | for (unsigned int i = 0; i < size;++i) { |
35 | 0 | pScene->mMeshes[i] = source_mesh_map[i].first; |
36 | 0 | } |
37 | | |
38 | | // now we need to update all nodes |
39 | 0 | UpdateNode(pScene->mRootNode,source_mesh_map); |
40 | 0 | } |
41 | 0 | } |
42 | | |
43 | | |
44 | | // ------------------------------------------------------------------------------------------------ |
45 | 0 | void MeshSplitter::UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map) { |
46 | | // TODO: should better use std::(multi)set for source_mesh_map. |
47 | | |
48 | | // for every index in out list build a new entry |
49 | 0 | std::vector<unsigned int> aiEntries; |
50 | 0 | aiEntries.reserve(pcNode->mNumMeshes + 1); |
51 | 0 | for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) { |
52 | 0 | for (unsigned int a = 0, end = static_cast<unsigned int>(source_mesh_map.size()); a < end;++a) { |
53 | 0 | if (source_mesh_map[a].second == pcNode->mMeshes[i]) { |
54 | 0 | aiEntries.push_back(a); |
55 | 0 | } |
56 | 0 | } |
57 | 0 | } |
58 | | |
59 | | // now build the new list |
60 | 0 | delete pcNode->mMeshes; |
61 | 0 | pcNode->mNumMeshes = static_cast<unsigned int>(aiEntries.size()); |
62 | 0 | pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; |
63 | |
|
64 | 0 | for (unsigned int b = 0; b < pcNode->mNumMeshes;++b) { |
65 | 0 | pcNode->mMeshes[b] = aiEntries[b]; |
66 | 0 | } |
67 | | |
68 | | // recursively update children |
69 | 0 | for (unsigned int i = 0, end = pcNode->mNumChildren; i < end;++i) { |
70 | 0 | UpdateNode ( pcNode->mChildren[i], source_mesh_map ); |
71 | 0 | } |
72 | 0 | } |
73 | | |
74 | | static const unsigned int WAS_NOT_COPIED = 0xffffffff; |
75 | | |
76 | | using PerVertexWeight = std::pair <unsigned int,float>; |
77 | | using VertexWeightTable = std::vector <PerVertexWeight>; |
78 | | |
79 | | // ------------------------------------------------------------------------------------------------ |
80 | 0 | VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) { |
81 | 0 | if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) { |
82 | 0 | return nullptr; |
83 | 0 | } |
84 | | |
85 | 0 | VertexWeightTable* const avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices]; |
86 | 0 | for (unsigned int i = 0; i < pMesh->mNumBones;++i) { |
87 | |
|
88 | 0 | aiBone* bone = pMesh->mBones[i]; |
89 | 0 | for (unsigned int a = 0; a < bone->mNumWeights;++a) { |
90 | 0 | const aiVertexWeight& weight = bone->mWeights[a]; |
91 | 0 | avPerVertexWeights[weight.mVertexId].emplace_back(i,weight.mWeight); |
92 | 0 | } |
93 | 0 | } |
94 | 0 | return avPerVertexWeights; |
95 | 0 | } |
96 | | |
97 | | // ------------------------------------------------------------------------------------------------ |
98 | 0 | void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map) { |
99 | | // TODO: should better use std::(multi)set for source_mesh_map. |
100 | |
|
101 | 0 | if (in_mesh->mNumVertices <= LIMIT) { |
102 | 0 | source_mesh_map.emplace_back(in_mesh,a); |
103 | 0 | return; |
104 | 0 | } |
105 | | |
106 | | // build a per-vertex weight list if necessary |
107 | 0 | VertexWeightTable* avPerVertexWeights = ComputeVertexBoneWeightTable(in_mesh); |
108 | | |
109 | | // we need to split this mesh into sub meshes. Estimate submesh size |
110 | 0 | const unsigned int sub_meshes = (in_mesh->mNumVertices / LIMIT) + 1; |
111 | | |
112 | | // create a std::vector<unsigned int> to remember which vertices have already |
113 | | // been copied and to which position (i.e. output index) |
114 | 0 | std::vector<unsigned int> was_copied_to; |
115 | 0 | was_copied_to.resize(in_mesh->mNumVertices,WAS_NOT_COPIED); |
116 | | |
117 | | // Try to find a good estimate for the number of output faces |
118 | | // per mesh. Add 12.5% as buffer |
119 | 0 | unsigned int size_estimated = in_mesh->mNumFaces / sub_meshes; |
120 | 0 | size_estimated += size_estimated / 8; |
121 | | |
122 | | // now generate all submeshes |
123 | 0 | unsigned int base = 0; |
124 | 0 | while (true) { |
125 | 0 | const unsigned int out_vertex_index = LIMIT; |
126 | |
|
127 | 0 | aiMesh* out_mesh = new aiMesh(); |
128 | 0 | out_mesh->mNumVertices = 0; |
129 | 0 | out_mesh->mMaterialIndex = in_mesh->mMaterialIndex; |
130 | | |
131 | | // the name carries the adjacency information between the meshes |
132 | 0 | out_mesh->mName = in_mesh->mName; |
133 | |
|
134 | 0 | typedef std::vector<aiVertexWeight> BoneWeightList; |
135 | 0 | if (in_mesh->HasBones()) { |
136 | 0 | out_mesh->mBones = new aiBone*[in_mesh->mNumBones](); |
137 | 0 | } |
138 | | |
139 | | // clear the temporary helper array |
140 | 0 | if (base) { |
141 | 0 | std::fill(was_copied_to.begin(), was_copied_to.end(), WAS_NOT_COPIED); |
142 | 0 | } |
143 | |
|
144 | 0 | std::vector<aiFace> vFaces; |
145 | | |
146 | | // reserve enough storage for most cases |
147 | 0 | if (in_mesh->HasPositions()) { |
148 | 0 | out_mesh->mVertices = new aiVector3D[out_vertex_index]; |
149 | 0 | } |
150 | |
|
151 | 0 | if (in_mesh->HasNormals()) { |
152 | 0 | out_mesh->mNormals = new aiVector3D[out_vertex_index]; |
153 | 0 | } |
154 | |
|
155 | 0 | if (in_mesh->HasTangentsAndBitangents()) { |
156 | 0 | out_mesh->mTangents = new aiVector3D[out_vertex_index]; |
157 | 0 | out_mesh->mBitangents = new aiVector3D[out_vertex_index]; |
158 | 0 | } |
159 | |
|
160 | 0 | for (unsigned int c = 0; in_mesh->HasVertexColors(c);++c) { |
161 | 0 | out_mesh->mColors[c] = new aiColor4D[out_vertex_index]; |
162 | 0 | } |
163 | |
|
164 | 0 | for (unsigned int c = 0; in_mesh->HasTextureCoords(c);++c) { |
165 | 0 | out_mesh->mNumUVComponents[c] = in_mesh->mNumUVComponents[c]; |
166 | 0 | out_mesh->mTextureCoords[c] = new aiVector3D[out_vertex_index]; |
167 | 0 | } |
168 | 0 | vFaces.reserve(size_estimated); |
169 | | |
170 | | // (we will also need to copy the array of indices) |
171 | 0 | while (base < in_mesh->mNumFaces) { |
172 | 0 | const unsigned int iNumIndices = in_mesh->mFaces[base].mNumIndices; |
173 | | |
174 | | // doesn't catch degenerates but is quite fast |
175 | 0 | unsigned int iNeed = 0; |
176 | 0 | for (unsigned int v = 0; v < iNumIndices;++v) { |
177 | 0 | unsigned int index = in_mesh->mFaces[base].mIndices[v]; |
178 | | |
179 | | // check whether we do already have this vertex |
180 | 0 | if (WAS_NOT_COPIED == was_copied_to[index]) { |
181 | 0 | iNeed++; |
182 | 0 | } |
183 | 0 | } |
184 | 0 | if (out_mesh->mNumVertices + iNeed > out_vertex_index) { |
185 | | // don't use this face |
186 | 0 | break; |
187 | 0 | } |
188 | | |
189 | 0 | vFaces.emplace_back(); |
190 | 0 | aiFace& rFace = vFaces.back(); |
191 | | |
192 | | // setup face type and number of indices |
193 | 0 | rFace.mNumIndices = iNumIndices; |
194 | 0 | rFace.mIndices = new unsigned int[iNumIndices]; |
195 | | |
196 | | // need to update the output primitive types |
197 | 0 | switch (rFace.mNumIndices) |
198 | 0 | { |
199 | 0 | case 1: |
200 | 0 | out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; |
201 | 0 | break; |
202 | 0 | case 2: |
203 | 0 | out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; |
204 | 0 | break; |
205 | 0 | case 3: |
206 | 0 | out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; |
207 | 0 | break; |
208 | 0 | default: |
209 | 0 | out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; |
210 | 0 | } |
211 | | |
212 | | // and copy the contents of the old array, offset them by current base |
213 | 0 | for (unsigned int v = 0; v < iNumIndices;++v) { |
214 | 0 | const unsigned int index = in_mesh->mFaces[base].mIndices[v]; |
215 | | |
216 | | // check whether we do already have this vertex |
217 | 0 | if (WAS_NOT_COPIED != was_copied_to[index]) { |
218 | 0 | rFace.mIndices[v] = was_copied_to[index]; |
219 | 0 | continue; |
220 | 0 | } |
221 | | |
222 | | // copy positions |
223 | 0 | out_mesh->mVertices[out_mesh->mNumVertices] = (in_mesh->mVertices[index]); |
224 | | |
225 | | // copy normals |
226 | 0 | if (in_mesh->HasNormals()) { |
227 | 0 | out_mesh->mNormals[out_mesh->mNumVertices] = (in_mesh->mNormals[index]); |
228 | 0 | } |
229 | | |
230 | | // copy tangents/bi-tangents |
231 | 0 | if (in_mesh->HasTangentsAndBitangents()) { |
232 | 0 | out_mesh->mTangents[out_mesh->mNumVertices] = (in_mesh->mTangents[index]); |
233 | 0 | out_mesh->mBitangents[out_mesh->mNumVertices] = (in_mesh->mBitangents[index]); |
234 | 0 | } |
235 | | |
236 | | // texture coordinates |
237 | 0 | for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { |
238 | 0 | if (in_mesh->HasTextureCoords( c)) { |
239 | 0 | out_mesh->mTextureCoords[c][out_mesh->mNumVertices] = in_mesh->mTextureCoords[c][index]; |
240 | 0 | } |
241 | 0 | } |
242 | | // vertex colors |
243 | 0 | for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { |
244 | 0 | if (in_mesh->HasVertexColors( c)) { |
245 | 0 | out_mesh->mColors[c][out_mesh->mNumVertices] = in_mesh->mColors[c][index]; |
246 | 0 | } |
247 | 0 | } |
248 | | // check whether we have bone weights assigned to this vertex |
249 | 0 | rFace.mIndices[v] = out_mesh->mNumVertices; |
250 | 0 | if (avPerVertexWeights) { |
251 | 0 | VertexWeightTable& table = avPerVertexWeights[ out_mesh->mNumVertices ]; |
252 | 0 | for (VertexWeightTable::const_iterator iter = table.begin(), end = table.end(); iter != end;++iter) { |
253 | | // allocate the bone weight array if necessary and store it in the mBones field (HACK!) |
254 | 0 | BoneWeightList* weight_list = reinterpret_cast<BoneWeightList*>(out_mesh->mBones[(*iter).first]); |
255 | 0 | if (!weight_list) { |
256 | 0 | weight_list = new BoneWeightList(); |
257 | 0 | out_mesh->mBones[(*iter).first] = reinterpret_cast<aiBone*>(weight_list); |
258 | 0 | } |
259 | 0 | weight_list->push_back(aiVertexWeight(out_mesh->mNumVertices,(*iter).second)); |
260 | 0 | } |
261 | 0 | } |
262 | |
|
263 | 0 | was_copied_to[index] = out_mesh->mNumVertices; |
264 | 0 | out_mesh->mNumVertices++; |
265 | 0 | } |
266 | 0 | base++; |
267 | 0 | if(out_mesh->mNumVertices == out_vertex_index) { |
268 | | // break here. The face is only added if it was complete |
269 | 0 | break; |
270 | 0 | } |
271 | 0 | } |
272 | | |
273 | | // check which bones we'll need to create for this submesh |
274 | 0 | if (in_mesh->HasBones()) { |
275 | 0 | aiBone** ppCurrent = out_mesh->mBones; |
276 | 0 | for (unsigned int k = 0; k < in_mesh->mNumBones;++k) { |
277 | | // check whether the bone exists |
278 | 0 | BoneWeightList* const weight_list = reinterpret_cast<BoneWeightList*>(out_mesh->mBones[k]); |
279 | |
|
280 | 0 | if (weight_list) { |
281 | 0 | const aiBone* const bone_in = in_mesh->mBones[k]; |
282 | 0 | aiBone* const bone_out = new aiBone(); |
283 | 0 | *ppCurrent++ = bone_out; |
284 | 0 | bone_out->mName = aiString(bone_in->mName); |
285 | 0 | bone_out->mOffsetMatrix =bone_in->mOffsetMatrix; |
286 | 0 | bone_out->mNumWeights = (unsigned int)weight_list->size(); |
287 | 0 | bone_out->mWeights = new aiVertexWeight[bone_out->mNumWeights]; |
288 | | |
289 | | // copy the vertex weights |
290 | 0 | ::memcpy(bone_out->mWeights, &(*weight_list)[0],bone_out->mNumWeights * sizeof(aiVertexWeight)); |
291 | |
|
292 | 0 | delete weight_list; |
293 | 0 | out_mesh->mNumBones++; |
294 | 0 | } |
295 | 0 | } |
296 | 0 | } |
297 | | |
298 | | // copy the face list to the mesh |
299 | 0 | out_mesh->mFaces = new aiFace[vFaces.size()]; |
300 | 0 | out_mesh->mNumFaces = (unsigned int)vFaces.size(); |
301 | |
|
302 | 0 | for (unsigned int p = 0; p < out_mesh->mNumFaces;++p) { |
303 | 0 | out_mesh->mFaces[p] = vFaces[p]; |
304 | 0 | } |
305 | | |
306 | | // add the newly created mesh to the list |
307 | 0 | source_mesh_map.emplace_back(out_mesh,a); |
308 | |
|
309 | 0 | if (base == in_mesh->mNumFaces) { |
310 | 0 | break; |
311 | 0 | } |
312 | 0 | } |
313 | | |
314 | | // delete the per-vertex weight list again |
315 | 0 | delete[] avPerVertexWeights; |
316 | | |
317 | | // now delete the old mesh data |
318 | 0 | delete in_mesh; |
319 | 0 | } |