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