vrshoot

annotate 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
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 }