1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2017, assimp team
7 
8 
9 All rights reserved.
10 
11 Redistribution and use of this software in source and binary forms,
12 with or without modification, are permitted provided that the following
13 conditions are met:
14 
15 * Redistributions of source code must retain the above
16   copyright notice, this list of conditions and the
17   following disclaimer.
18 
19 * Redistributions in binary form must reproduce the above
20   copyright notice, this list of conditions and the
21   following disclaimer in the documentation and/or other
22   materials provided with the distribution.
23 
24 * Neither the name of the assimp team, nor the names of its
25   contributors may be used to endorse or promote products
26   derived from this software without specific prior
27   written permission of the assimp team.
28 
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 ---------------------------------------------------------------------------
41 */
42 
43 /** @file Implementation of the post processing step to calculate
44  *  tangents and bitangents for all imported meshes
45  */
46 
47 // internal headers
48 #include "CalcTangentsProcess.h"
49 #include "ProcessHelper.h"
50 #include "TinyFormatter.h"
51 #include "qnan.h"
52 
53 using namespace Assimp;
54 
55 // ------------------------------------------------------------------------------------------------
56 // Constructor to be privately used by Importer
CalcTangentsProcess()57 CalcTangentsProcess::CalcTangentsProcess()
58 : configMaxAngle( AI_DEG_TO_RAD(45.f) )
59 , configSourceUV( 0 ) {
60     // nothing to do here
61 }
62 
63 // ------------------------------------------------------------------------------------------------
64 // Destructor, private as well
~CalcTangentsProcess()65 CalcTangentsProcess::~CalcTangentsProcess()
66 {
67     // nothing to do here
68 }
69 
70 // ------------------------------------------------------------------------------------------------
71 // Returns whether the processing step is present in the given flag field.
IsActive(unsigned int pFlags) const72 bool CalcTangentsProcess::IsActive( unsigned int pFlags) const
73 {
74     return (pFlags & aiProcess_CalcTangentSpace) != 0;
75 }
76 
77 // ------------------------------------------------------------------------------------------------
78 // Executes the post processing step on the given imported data.
SetupProperties(const Importer * pImp)79 void CalcTangentsProcess::SetupProperties(const Importer* pImp)
80 {
81     ai_assert( NULL != pImp );
82 
83     // get the current value of the property
84     configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f);
85     configMaxAngle = std::max(std::min(configMaxAngle,45.0f),0.0f);
86     configMaxAngle = AI_DEG_TO_RAD(configMaxAngle);
87 
88     configSourceUV = pImp->GetPropertyInteger(AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX,0);
89 }
90 
91 // ------------------------------------------------------------------------------------------------
92 // Executes the post processing step on the given imported data.
Execute(aiScene * pScene)93 void CalcTangentsProcess::Execute( aiScene* pScene)
94 {
95     ai_assert( NULL != pScene );
96 
97     DefaultLogger::get()->debug("CalcTangentsProcess begin");
98 
99     bool bHas = false;
100     for ( unsigned int a = 0; a < pScene->mNumMeshes; a++ ) {
101         if(ProcessMesh( pScene->mMeshes[a],a))bHas = true;
102     }
103 
104     if ( bHas ) {
105         DefaultLogger::get()->info("CalcTangentsProcess finished. Tangents have been calculated");
106     } else {
107         DefaultLogger::get()->debug("CalcTangentsProcess finished");
108     }
109 }
110 
111 // ------------------------------------------------------------------------------------------------
112 // Calculates tangents and bi-tangents for the given mesh
ProcessMesh(aiMesh * pMesh,unsigned int meshIndex)113 bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
114 {
115     // we assume that the mesh is still in the verbose vertex format where each face has its own set
116     // of vertices and no vertices are shared between faces. Sadly I don't know any quick test to
117     // assert() it here.
118     // assert( must be verbose, dammit);
119 
120     if (pMesh->mTangents) // this implies that mBitangents is also there
121         return false;
122 
123     // If the mesh consists of lines and/or points but not of
124     // triangles or higher-order polygons the normal vectors
125     // are undefined.
126     if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON)))
127     {
128         DefaultLogger::get()->info("Tangents are undefined for line and point meshes");
129         return false;
130     }
131 
132     // what we can check, though, is if the mesh has normals and texture coordinates. That's a requirement
133     if( pMesh->mNormals == NULL)
134     {
135         DefaultLogger::get()->error("Failed to compute tangents; need normals");
136         return false;
137     }
138     if( configSourceUV >= AI_MAX_NUMBER_OF_TEXTURECOORDS || !pMesh->mTextureCoords[configSourceUV] )
139     {
140         DefaultLogger::get()->error((Formatter::format("Failed to compute tangents; need UV data in channel"),configSourceUV));
141         return false;
142     }
143 
144     const float angleEpsilon = 0.9999f;
145 
146     std::vector<bool> vertexDone( pMesh->mNumVertices, false);
147     const float qnan = get_qnan();
148 
149     // create space for the tangents and bitangents
150     pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
151     pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
152 
153     const aiVector3D* meshPos = pMesh->mVertices;
154     const aiVector3D* meshNorm = pMesh->mNormals;
155     const aiVector3D* meshTex = pMesh->mTextureCoords[configSourceUV];
156     aiVector3D* meshTang = pMesh->mTangents;
157     aiVector3D* meshBitang = pMesh->mBitangents;
158 
159     // calculate the tangent and bitangent for every face
160     for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
161     {
162         const aiFace& face = pMesh->mFaces[a];
163         if (face.mNumIndices < 3)
164         {
165             // There are less than three indices, thus the tangent vector
166             // is not defined. We are finished with these vertices now,
167             // their tangent vectors are set to qnan.
168             for (unsigned int i = 0; i < face.mNumIndices;++i)
169             {
170                 unsigned int idx = face.mIndices[i];
171                 vertexDone  [idx] = true;
172                 meshTang    [idx] = aiVector3D(qnan);
173                 meshBitang  [idx] = aiVector3D(qnan);
174             }
175 
176             continue;
177         }
178 
179         // triangle or polygon... we always use only the first three indices. A polygon
180         // is supposed to be planar anyways....
181         // FIXME: (thom) create correct calculation for multi-vertex polygons maybe?
182         const unsigned int p0 = face.mIndices[0], p1 = face.mIndices[1], p2 = face.mIndices[2];
183 
184         // position differences p1->p2 and p1->p3
185         aiVector3D v = meshPos[p1] - meshPos[p0], w = meshPos[p2] - meshPos[p0];
186 
187         // texture offset p1->p2 and p1->p3
188         float sx = meshTex[p1].x - meshTex[p0].x, sy = meshTex[p1].y - meshTex[p0].y;
189         float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y;
190         float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f;
191         // when t1, t2, t3 in same position in UV space, just use default UV direction.
192         if ( 0 == sx && 0 ==sy && 0 == tx && 0 == ty ) {
193             sx = 0.0; sy = 1.0;
194             tx = 1.0; ty = 0.0;
195         }
196 
197         // tangent points in the direction where to positive X axis of the texture coord's would point in model space
198         // bitangent's points along the positive Y axis of the texture coord's, respectively
199         aiVector3D tangent, bitangent;
200         tangent.x = (w.x * sy - v.x * ty) * dirCorrection;
201         tangent.y = (w.y * sy - v.y * ty) * dirCorrection;
202         tangent.z = (w.z * sy - v.z * ty) * dirCorrection;
203         bitangent.x = (w.x * sx - v.x * tx) * dirCorrection;
204         bitangent.y = (w.y * sx - v.y * tx) * dirCorrection;
205         bitangent.z = (w.z * sx - v.z * tx) * dirCorrection;
206 
207         // store for every vertex of that face
208         for( unsigned int b = 0; b < face.mNumIndices; ++b ) {
209             unsigned int p = face.mIndices[b];
210 
211             // project tangent and bitangent into the plane formed by the vertex' normal
212             aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
213             aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]);
214             localTangent.Normalize(); localBitangent.Normalize();
215 
216             // reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN.
217             bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z);
218             bool invalid_bitangent = is_special_float(localBitangent.x) || is_special_float(localBitangent.y) || is_special_float(localBitangent.z);
219             if (invalid_tangent != invalid_bitangent) {
220                 if (invalid_tangent) {
221                     localTangent = meshNorm[p] ^ localBitangent;
222                     localTangent.Normalize();
223                 } else {
224                     localBitangent = localTangent ^ meshNorm[p];
225                     localBitangent.Normalize();
226                 }
227             }
228 
229             // and write it into the mesh.
230             meshTang[ p ]   = localTangent;
231             meshBitang[ p ] = localBitangent;
232         }
233     }
234 
235 
236     // create a helper to quickly find locally close vertices among the vertex array
237     // FIX: check whether we can reuse the SpatialSort of a previous step
238     SpatialSort* vertexFinder = NULL;
239     SpatialSort  _vertexFinder;
240     float posEpsilon;
241     if (shared)
242     {
243         std::vector<std::pair<SpatialSort,float> >* avf;
244         shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
245         if (avf)
246         {
247             std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
248             vertexFinder = &blubb.first;
249             posEpsilon = blubb.second;;
250         }
251     }
252     if (!vertexFinder)
253     {
254         _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
255         vertexFinder = &_vertexFinder;
256         posEpsilon = ComputePositionEpsilon(pMesh);
257     }
258     std::vector<unsigned int> verticesFound;
259 
260     const float fLimit = std::cos(configMaxAngle);
261     std::vector<unsigned int> closeVertices;
262 
263     // in the second pass we now smooth out all tangents and bitangents at the same local position
264     // if they are not too far off.
265     for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
266     {
267         if( vertexDone[a])
268             continue;
269 
270         const aiVector3D& origPos = pMesh->mVertices[a];
271         const aiVector3D& origNorm = pMesh->mNormals[a];
272         const aiVector3D& origTang = pMesh->mTangents[a];
273         const aiVector3D& origBitang = pMesh->mBitangents[a];
274         closeVertices.resize( 0 );
275 
276         // find all vertices close to that position
277         vertexFinder->FindPositions( origPos, posEpsilon, verticesFound);
278 
279         closeVertices.reserve (verticesFound.size()+5);
280         closeVertices.push_back( a);
281 
282         // look among them for other vertices sharing the same normal and a close-enough tangent/bitangent
283         for( unsigned int b = 0; b < verticesFound.size(); b++)
284         {
285             unsigned int idx = verticesFound[b];
286             if( vertexDone[idx])
287                 continue;
288             if( meshNorm[idx] * origNorm < angleEpsilon)
289                 continue;
290             if(  meshTang[idx] * origTang < fLimit)
291                 continue;
292             if( meshBitang[idx] * origBitang < fLimit)
293                 continue;
294 
295             // it's similar enough -> add it to the smoothing group
296             closeVertices.push_back( idx);
297             vertexDone[idx] = true;
298         }
299 
300         // smooth the tangents and bitangents of all vertices that were found to be close enough
301         aiVector3D smoothTangent( 0, 0, 0), smoothBitangent( 0, 0, 0);
302         for( unsigned int b = 0; b < closeVertices.size(); ++b)
303         {
304             smoothTangent += meshTang[ closeVertices[b] ];
305             smoothBitangent += meshBitang[ closeVertices[b] ];
306         }
307         smoothTangent.Normalize();
308         smoothBitangent.Normalize();
309 
310         // and write it back into all affected tangents
311         for( unsigned int b = 0; b < closeVertices.size(); ++b)
312         {
313             meshTang[ closeVertices[b] ] = smoothTangent;
314             meshBitang[ closeVertices[b] ] = smoothBitangent;
315         }
316     }
317     return true;
318 }
319