nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file Implementation of the post processing step to generate face nuclear@0: * normals for all imported faces. nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: // internal headers nuclear@0: #include "GenVertexNormalsProcess.h" nuclear@0: #include "ProcessHelper.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: GenVertexNormalsProcess::GenVertexNormalsProcess() nuclear@0: { nuclear@0: this->configMaxAngle = AI_DEG_TO_RAD(175.f); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: GenVertexNormalsProcess::~GenVertexNormalsProcess() nuclear@0: { nuclear@0: // nothing to do here nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the processing step is present in the given flag field. nuclear@0: bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const nuclear@0: { nuclear@0: return (pFlags & aiProcess_GenSmoothNormals) != 0; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Executes the post processing step on the given imported data. nuclear@0: void GenVertexNormalsProcess::SetupProperties(const Importer* pImp) nuclear@0: { nuclear@0: // Get the current value of the AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE property nuclear@0: configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,175.f); nuclear@0: configMaxAngle = AI_DEG_TO_RAD(std::max(std::min(configMaxAngle,175.0f),0.0f)); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Executes the post processing step on the given imported data. nuclear@0: void GenVertexNormalsProcess::Execute( aiScene* pScene) nuclear@0: { nuclear@0: DefaultLogger::get()->debug("GenVertexNormalsProcess begin"); nuclear@0: nuclear@0: if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) nuclear@0: throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); nuclear@0: nuclear@0: bool bHas = false; nuclear@0: for( unsigned int a = 0; a < pScene->mNumMeshes; a++) nuclear@0: { nuclear@0: if(GenMeshVertexNormals( pScene->mMeshes[a],a)) nuclear@0: bHas = true; nuclear@0: } nuclear@0: nuclear@0: if (bHas) { nuclear@0: DefaultLogger::get()->info("GenVertexNormalsProcess finished. " nuclear@0: "Vertex normals have been calculated"); nuclear@0: } nuclear@0: else DefaultLogger::get()->debug("GenVertexNormalsProcess finished. " nuclear@0: "Normals are already there"); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Executes the post processing step on the given imported data. nuclear@0: bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int meshIndex) nuclear@0: { nuclear@0: if (NULL != pMesh->mNormals) nuclear@0: return false; nuclear@0: nuclear@0: // If the mesh consists of lines and/or points but not of nuclear@0: // triangles or higher-order polygons the normal vectors nuclear@0: // are undefined. nuclear@0: if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) nuclear@0: { nuclear@0: DefaultLogger::get()->info("Normal vectors are undefined for line and point meshes"); nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // Allocate the array to hold the output normals nuclear@0: const float qnan = std::numeric_limits::quiet_NaN(); nuclear@0: pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; nuclear@0: nuclear@0: // Compute per-face normals but store them per-vertex nuclear@0: for( unsigned int a = 0; a < pMesh->mNumFaces; a++) nuclear@0: { nuclear@0: const aiFace& face = pMesh->mFaces[a]; nuclear@0: if (face.mNumIndices < 3) nuclear@0: { nuclear@0: // either a point or a line -> no normal vector nuclear@0: for (unsigned int i = 0;i < face.mNumIndices;++i) { nuclear@0: pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan); nuclear@0: } nuclear@0: nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]]; nuclear@0: const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]]; nuclear@0: const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]]; nuclear@0: const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).Normalize(); nuclear@0: nuclear@0: for (unsigned int i = 0;i < face.mNumIndices;++i) { nuclear@0: pMesh->mNormals[face.mIndices[i]] = vNor; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Set up a SpatialSort to quickly find all vertices close to a given position nuclear@0: // check whether we can reuse the SpatialSort of a previous step. nuclear@0: SpatialSort* vertexFinder = NULL; nuclear@0: SpatialSort _vertexFinder; nuclear@0: float posEpsilon = 1e-5f; nuclear@0: if (shared) { nuclear@0: std::vector >* avf; nuclear@0: shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); nuclear@0: if (avf) nuclear@0: { nuclear@0: std::pair& blubb = avf->operator [] (meshIndex); nuclear@0: vertexFinder = &blubb.first; nuclear@0: posEpsilon = blubb.second; nuclear@0: } nuclear@0: } nuclear@0: if (!vertexFinder) { nuclear@0: _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); nuclear@0: vertexFinder = &_vertexFinder; nuclear@0: posEpsilon = ComputePositionEpsilon(pMesh); nuclear@0: } nuclear@0: std::vector verticesFound; nuclear@0: aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices]; nuclear@0: nuclear@0: if (configMaxAngle >= AI_DEG_TO_RAD( 175.f )) { nuclear@0: // There is no angle limit. Thus all vertices with positions close nuclear@0: // to each other will receive the same vertex normal. This allows us nuclear@0: // to optimize the whole algorithm a little bit ... nuclear@0: std::vector abHad(pMesh->mNumVertices,false); nuclear@0: for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { nuclear@0: if (abHad[i]) { nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: // Get all vertices that share this one ... nuclear@0: vertexFinder->FindPositions( pMesh->mVertices[i], posEpsilon, verticesFound); nuclear@0: nuclear@0: aiVector3D pcNor; nuclear@0: for (unsigned int a = 0; a < verticesFound.size(); ++a) { nuclear@0: const aiVector3D& v = pMesh->mNormals[verticesFound[a]]; nuclear@0: if (is_not_qnan(v.x))pcNor += v; nuclear@0: } nuclear@0: pcNor.Normalize(); nuclear@0: nuclear@0: // Write the smoothed normal back to all affected normals nuclear@0: for (unsigned int a = 0; a < verticesFound.size(); ++a) nuclear@0: { nuclear@0: register unsigned int vidx = verticesFound[a]; nuclear@0: pcNew[vidx] = pcNor; nuclear@0: abHad[vidx] = true; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: // Slower code path if a smooth angle is set. There are many ways to achieve nuclear@0: // the effect, this one is the most straightforward one. nuclear@0: else { nuclear@0: const float fLimit = ::cos(configMaxAngle); nuclear@0: for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { nuclear@0: // Get all vertices that share this one ... nuclear@0: vertexFinder->FindPositions( pMesh->mVertices[i] , posEpsilon, verticesFound); nuclear@0: nuclear@0: aiVector3D pcNor; nuclear@0: for (unsigned int a = 0; a < verticesFound.size(); ++a) { nuclear@0: const aiVector3D& v = pMesh->mNormals[verticesFound[a]]; nuclear@0: nuclear@0: // check whether the angle between the two normals is not too large nuclear@0: // HACK: if v.x is qnan the dot product will become qnan, too nuclear@0: // therefore the comparison against fLimit should be false nuclear@0: // in every case. nuclear@0: if (v * pMesh->mNormals[i] < fLimit) nuclear@0: continue; nuclear@0: nuclear@0: pcNor += v; nuclear@0: } nuclear@0: pcNew[i] = pcNor.Normalize(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: delete[] pMesh->mNormals; nuclear@0: pMesh->mNormals = pcNew; nuclear@0: nuclear@0: return true; nuclear@0: }