vrshoot

diff 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 diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/libs/assimp/CalcTangentsProcess.cpp	Sat Feb 01 19:58:19 2014 +0200
     1.3 @@ -0,0 +1,292 @@
     1.4 +/*
     1.5 +---------------------------------------------------------------------------
     1.6 +Open Asset Import Library (assimp)
     1.7 +---------------------------------------------------------------------------
     1.8 +
     1.9 +Copyright (c) 2006-2012, assimp team
    1.10 +
    1.11 +All rights reserved.
    1.12 +
    1.13 +Redistribution and use of this software in source and binary forms, 
    1.14 +with or without modification, are permitted provided that the following 
    1.15 +conditions are met:
    1.16 +
    1.17 +* Redistributions of source code must retain the above
    1.18 +  copyright notice, this list of conditions and the
    1.19 +  following disclaimer.
    1.20 +
    1.21 +* Redistributions in binary form must reproduce the above
    1.22 +  copyright notice, this list of conditions and the
    1.23 +  following disclaimer in the documentation and/or other
    1.24 +  materials provided with the distribution.
    1.25 +
    1.26 +* Neither the name of the assimp team, nor the names of its
    1.27 +  contributors may be used to endorse or promote products
    1.28 +  derived from this software without specific prior
    1.29 +  written permission of the assimp team.
    1.30 +
    1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
    1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.42 +---------------------------------------------------------------------------
    1.43 +*/
    1.44 +
    1.45 +/** @file Implementation of the post processing step to calculate 
    1.46 + *  tangents and bitangents for all imported meshes
    1.47 + */
    1.48 +
    1.49 +#include "AssimpPCH.h"
    1.50 +
    1.51 +// internal headers
    1.52 +#include "CalcTangentsProcess.h"
    1.53 +#include "ProcessHelper.h"
    1.54 +#include "TinyFormatter.h"
    1.55 +
    1.56 +using namespace Assimp;
    1.57 +
    1.58 +// ------------------------------------------------------------------------------------------------
    1.59 +// Constructor to be privately used by Importer
    1.60 +CalcTangentsProcess::CalcTangentsProcess()
    1.61 +{
    1.62 +	this->configMaxAngle = AI_DEG_TO_RAD(45.f);
    1.63 +}
    1.64 +
    1.65 +// ------------------------------------------------------------------------------------------------
    1.66 +// Destructor, private as well
    1.67 +CalcTangentsProcess::~CalcTangentsProcess()
    1.68 +{
    1.69 +	// nothing to do here
    1.70 +}
    1.71 +
    1.72 +// ------------------------------------------------------------------------------------------------
    1.73 +// Returns whether the processing step is present in the given flag field.
    1.74 +bool CalcTangentsProcess::IsActive( unsigned int pFlags) const
    1.75 +{
    1.76 +	return (pFlags & aiProcess_CalcTangentSpace) != 0;
    1.77 +}
    1.78 +
    1.79 +// ------------------------------------------------------------------------------------------------
    1.80 +// Executes the post processing step on the given imported data.
    1.81 +void CalcTangentsProcess::SetupProperties(const Importer* pImp)
    1.82 +{
    1.83 +	// get the current value of the property
    1.84 +	configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f);
    1.85 +	configMaxAngle = std::max(std::min(configMaxAngle,45.0f),0.0f);
    1.86 +	configMaxAngle = AI_DEG_TO_RAD(configMaxAngle);
    1.87 +
    1.88 +	configSourceUV = pImp->GetPropertyInteger(AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX,0);
    1.89 +}
    1.90 +
    1.91 +// ------------------------------------------------------------------------------------------------
    1.92 +// Executes the post processing step on the given imported data.
    1.93 +void CalcTangentsProcess::Execute( aiScene* pScene)
    1.94 +{
    1.95 +	DefaultLogger::get()->debug("CalcTangentsProcess begin");
    1.96 +
    1.97 +	bool bHas = false;
    1.98 +	for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
    1.99 +		if(ProcessMesh( pScene->mMeshes[a],a))bHas = true;
   1.100 +
   1.101 +	if (bHas)DefaultLogger::get()->info("CalcTangentsProcess finished. Tangents have been calculated");
   1.102 +	else DefaultLogger::get()->debug("CalcTangentsProcess finished");
   1.103 +}
   1.104 +
   1.105 +// ------------------------------------------------------------------------------------------------
   1.106 +// Calculates tangents and bitangents for the given mesh
   1.107 +bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
   1.108 +{
   1.109 +	// we assume that the mesh is still in the verbose vertex format where each face has its own set
   1.110 +	// of vertices and no vertices are shared between faces. Sadly I don't know any quick test to 
   1.111 +	// assert() it here.
   1.112 +    //assert( must be verbose, dammit);
   1.113 +
   1.114 +	if (pMesh->mTangents) // thisimplies that mBitangents is also there
   1.115 +		return false;
   1.116 +
   1.117 +	// If the mesh consists of lines and/or points but not of
   1.118 +	// triangles or higher-order polygons the normal vectors
   1.119 +	// are undefined.
   1.120 +	if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON)))
   1.121 +	{
   1.122 +		DefaultLogger::get()->info("Tangents are undefined for line and point meshes");
   1.123 +		return false;
   1.124 +	}
   1.125 +
   1.126 +	// what we can check, though, is if the mesh has normals and texture coordinates. That's a requirement
   1.127 +	if( pMesh->mNormals == NULL)
   1.128 +	{
   1.129 +		DefaultLogger::get()->error("Failed to compute tangents; need normals");
   1.130 +		return false;
   1.131 +	}
   1.132 +	if( configSourceUV >= AI_MAX_NUMBER_OF_TEXTURECOORDS || !pMesh->mTextureCoords[configSourceUV] )
   1.133 +	{
   1.134 +		DefaultLogger::get()->error((Formatter::format("Failed to compute tangents; need UV data in channel"),configSourceUV));
   1.135 +		return false;
   1.136 +	}
   1.137 +	 
   1.138 +	const float angleEpsilon = 0.9999f;
   1.139 +
   1.140 +	std::vector<bool> vertexDone( pMesh->mNumVertices, false);
   1.141 +	const float qnan = get_qnan();
   1.142 +
   1.143 +	// create space for the tangents and bitangents
   1.144 +	pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
   1.145 +	pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
   1.146 +
   1.147 +	const aiVector3D* meshPos = pMesh->mVertices;
   1.148 +	const aiVector3D* meshNorm = pMesh->mNormals;
   1.149 +	const aiVector3D* meshTex = pMesh->mTextureCoords[configSourceUV];
   1.150 +	aiVector3D* meshTang = pMesh->mTangents;
   1.151 +	aiVector3D* meshBitang = pMesh->mBitangents;
   1.152 +	
   1.153 +	// calculate the tangent and bitangent for every face
   1.154 +	for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
   1.155 +	{
   1.156 +		const aiFace& face = pMesh->mFaces[a];
   1.157 +		if (face.mNumIndices < 3)
   1.158 +		{
   1.159 +			// There are less than three indices, thus the tangent vector
   1.160 +			// is not defined. We are finished with these vertices now,
   1.161 +			// their tangent vectors are set to qnan.
   1.162 +			for (unsigned int i = 0; i < face.mNumIndices;++i)
   1.163 +			{
   1.164 +				register unsigned int idx = face.mIndices[i];
   1.165 +				vertexDone  [idx] = true;
   1.166 +				meshTang    [idx] = aiVector3D(qnan);
   1.167 +				meshBitang  [idx] = aiVector3D(qnan);
   1.168 +			}
   1.169 +
   1.170 +			continue;
   1.171 +		}
   1.172 +
   1.173 +		// triangle or polygon... we always use only the first three indices. A polygon
   1.174 +		// is supposed to be planar anyways....
   1.175 +		// FIXME: (thom) create correct calculation for multi-vertex polygons maybe?
   1.176 +		const unsigned int p0 = face.mIndices[0], p1 = face.mIndices[1], p2 = face.mIndices[2];
   1.177 +
   1.178 +		// position differences p1->p2 and p1->p3
   1.179 +		aiVector3D v = meshPos[p1] - meshPos[p0], w = meshPos[p2] - meshPos[p0];
   1.180 +
   1.181 +		// texture offset p1->p2 and p1->p3
   1.182 +		float sx = meshTex[p1].x - meshTex[p0].x, sy = meshTex[p1].y - meshTex[p0].y;
   1.183 +        float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y;
   1.184 +		float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f;
   1.185 +
   1.186 +		// tangent points in the direction where to positive X axis of the texture coords would point in model space
   1.187 +		// bitangents points along the positive Y axis of the texture coords, respectively
   1.188 +		aiVector3D tangent, bitangent;
   1.189 +		tangent.x = (w.x * sy - v.x * ty) * dirCorrection;
   1.190 +        tangent.y = (w.y * sy - v.y * ty) * dirCorrection;
   1.191 +        tangent.z = (w.z * sy - v.z * ty) * dirCorrection;
   1.192 +        bitangent.x = (w.x * sx - v.x * tx) * dirCorrection;
   1.193 +        bitangent.y = (w.y * sx - v.y * tx) * dirCorrection;
   1.194 +        bitangent.z = (w.z * sx - v.z * tx) * dirCorrection;
   1.195 +
   1.196 +		// store for every vertex of that face
   1.197 +		for( unsigned int b = 0; b < face.mNumIndices; b++)
   1.198 +		{
   1.199 +			unsigned int p = face.mIndices[b];
   1.200 +
   1.201 +			// project tangent and bitangent into the plane formed by the vertex' normal
   1.202 +			aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
   1.203 +			aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]);
   1.204 +			localTangent.Normalize(); localBitangent.Normalize();
   1.205 +
   1.206 +			// and write it into the mesh.
   1.207 +			meshTang[p] = localTangent;
   1.208 +			meshBitang[p] = localBitangent;
   1.209 +		}
   1.210 +    }
   1.211 +
   1.212 +
   1.213 +	// create a helper to quickly find locally close vertices among the vertex array
   1.214 +	// FIX: check whether we can reuse the SpatialSort of a previous step
   1.215 +	SpatialSort* vertexFinder = NULL;
   1.216 +	SpatialSort  _vertexFinder;
   1.217 +	float posEpsilon;
   1.218 +	if (shared)
   1.219 +	{
   1.220 +		std::vector<std::pair<SpatialSort,float> >* avf;
   1.221 +		shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
   1.222 +		if (avf)
   1.223 +		{
   1.224 +			std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
   1.225 +			vertexFinder = &blubb.first;
   1.226 +			posEpsilon = blubb.second;;
   1.227 +		}
   1.228 +	}
   1.229 +	if (!vertexFinder)
   1.230 +	{
   1.231 +		_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
   1.232 +		vertexFinder = &_vertexFinder;
   1.233 +		posEpsilon = ComputePositionEpsilon(pMesh);
   1.234 +	}
   1.235 +	std::vector<unsigned int> verticesFound;
   1.236 +
   1.237 +	const float fLimit = cosf(configMaxAngle); 
   1.238 +	std::vector<unsigned int> closeVertices;
   1.239 +
   1.240 +	// in the second pass we now smooth out all tangents and bitangents at the same local position 
   1.241 +	// if they are not too far off.
   1.242 +	for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
   1.243 +	{
   1.244 +		if( vertexDone[a])
   1.245 +			continue;
   1.246 +
   1.247 +		const aiVector3D& origPos = pMesh->mVertices[a];
   1.248 +		const aiVector3D& origNorm = pMesh->mNormals[a];
   1.249 +		const aiVector3D& origTang = pMesh->mTangents[a];
   1.250 +		const aiVector3D& origBitang = pMesh->mBitangents[a];
   1.251 +		closeVertices.clear();
   1.252 +
   1.253 +		// find all vertices close to that position
   1.254 +		vertexFinder->FindPositions( origPos, posEpsilon, verticesFound);
   1.255 +
   1.256 +		closeVertices.reserve (verticesFound.size()+5);
   1.257 +		closeVertices.push_back( a);
   1.258 +
   1.259 +		// look among them for other vertices sharing the same normal and a close-enough tangent/bitangent
   1.260 +		for( unsigned int b = 0; b < verticesFound.size(); b++)
   1.261 +		{
   1.262 +			unsigned int idx = verticesFound[b];
   1.263 +			if( vertexDone[idx])
   1.264 +				continue;
   1.265 +			if( meshNorm[idx] * origNorm < angleEpsilon)
   1.266 +				continue;
   1.267 +			if(  meshTang[idx] * origTang < fLimit)
   1.268 +				continue;
   1.269 +			if( meshBitang[idx] * origBitang < fLimit)
   1.270 +				continue;
   1.271 +
   1.272 +			// it's similar enough -> add it to the smoothing group
   1.273 +			closeVertices.push_back( idx);
   1.274 +			vertexDone[idx] = true;
   1.275 +		}
   1.276 +
   1.277 +		// smooth the tangents and bitangents of all vertices that were found to be close enough
   1.278 +		aiVector3D smoothTangent( 0, 0, 0), smoothBitangent( 0, 0, 0);
   1.279 +		for( unsigned int b = 0; b < closeVertices.size(); ++b)
   1.280 +		{
   1.281 +			smoothTangent += meshTang[ closeVertices[b] ];
   1.282 +			smoothBitangent += meshBitang[ closeVertices[b] ];
   1.283 +		}
   1.284 +		smoothTangent.Normalize();
   1.285 +		smoothBitangent.Normalize();
   1.286 +
   1.287 +		// and write it back into all affected tangents
   1.288 +		for( unsigned int b = 0; b < closeVertices.size(); ++b)
   1.289 +		{
   1.290 +			meshTang[ closeVertices[b] ] = smoothTangent;
   1.291 +			meshBitang[ closeVertices[b] ] = smoothBitangent;
   1.292 +		}
   1.293 +	}
   1.294 +	return true;
   1.295 +}