1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following 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
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF 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
51namespace Assimp {
52
53// -------------------------------------------------------------------------------
54void 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// -------------------------------------------------------------------------------
78void 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// -------------------------------------------------------------------------------
92void 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// -------------------------------------------------------------------------------
99void 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// -------------------------------------------------------------------------------
123void 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// -------------------------------------------------------------------------------
131void FindMeshCenter (aiMesh* mesh, aiVector3D& out)
132{
133 aiVector3D min,max;
134 FindMeshCenter(mesh,out,min,max);
135}
136
137// -------------------------------------------------------------------------------
138void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,
139 const aiMatrix4x4& m)
140{
141 aiVector3D min,max;
142 FindMeshCenterTransformed(mesh,out,min,max,m);
143}
144
145// -------------------------------------------------------------------------------
146ai_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// -------------------------------------------------------------------------------
157ai_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// -------------------------------------------------------------------------------
179unsigned 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// -------------------------------------------------------------------------------
214VertexWeightTable* 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// -------------------------------------------------------------------------------
234const 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// -------------------------------------------------------------------------------
273const 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// -------------------------------------------------------------------------------
299aiMesh* 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