vrshoot

view libs/assimp/CalcTangentsProcess.cpp @ 0:b2f14e535253

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