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