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 // -------------------------------------------------------------------------------
ConvertListToStrings(const std::string & in,std::list<std::string> & out)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 // -------------------------------------------------------------------------------
FindAABBTransformed(const aiMesh * mesh,aiVector3D & min,aiVector3D & max,const aiMatrix4x4 & m)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 // -------------------------------------------------------------------------------
FindMeshCenter(aiMesh * mesh,aiVector3D & out,aiVector3D & min,aiVector3D & max)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 // -------------------------------------------------------------------------------
FindSceneCenter(aiScene * scene,aiVector3D & out,aiVector3D & min,aiVector3D & max)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 // -------------------------------------------------------------------------------
FindMeshCenterTransformed(aiMesh * mesh,aiVector3D & out,aiVector3D & min,aiVector3D & max,const aiMatrix4x4 & m)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 // -------------------------------------------------------------------------------
FindMeshCenter(aiMesh * mesh,aiVector3D & out)131 void FindMeshCenter (aiMesh* mesh, aiVector3D& out)
132 {
133     aiVector3D min,max;
134     FindMeshCenter(mesh,out,min,max);
135 }
136 
137 // -------------------------------------------------------------------------------
FindMeshCenterTransformed(aiMesh * mesh,aiVector3D & out,const aiMatrix4x4 & m)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 // -------------------------------------------------------------------------------
ComputePositionEpsilon(const aiMesh * pMesh)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 // -------------------------------------------------------------------------------
ComputePositionEpsilon(const aiMesh * const * pMeshes,size_t num)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 // -------------------------------------------------------------------------------
GetMeshVFormatUnique(const aiMesh * pcMesh)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 // -------------------------------------------------------------------------------
ComputeVertexBoneWeightTable(const aiMesh * pMesh)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 // -------------------------------------------------------------------------------
TextureTypeToString(aiTextureType in)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 // -------------------------------------------------------------------------------
MappingTypeToString(aiTextureMapping in)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 // -------------------------------------------------------------------------------
MakeSubmesh(const aiMesh * pMesh,const std::vector<unsigned int> & subMeshFaces,unsigned int subFlags)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