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 invert nuclear@0: * all normals in meshes with infacing normals. nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: // internal headers nuclear@0: #include "FixNormalsStep.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: FixInfacingNormalsProcess::FixInfacingNormalsProcess() nuclear@0: { nuclear@0: // nothing to do here nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: FixInfacingNormalsProcess::~FixInfacingNormalsProcess() 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 FixInfacingNormalsProcess::IsActive( unsigned int pFlags) const nuclear@0: { nuclear@0: return (pFlags & aiProcess_FixInfacingNormals) != 0; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Executes the post processing step on the given imported data. nuclear@0: void FixInfacingNormalsProcess::Execute( aiScene* pScene) nuclear@0: { nuclear@0: DefaultLogger::get()->debug("FixInfacingNormalsProcess begin"); nuclear@0: nuclear@0: bool bHas = false; nuclear@0: for( unsigned int a = 0; a < pScene->mNumMeshes; a++) nuclear@0: if(ProcessMesh( pScene->mMeshes[a],a))bHas = true; nuclear@0: nuclear@0: if (bHas) nuclear@0: DefaultLogger::get()->debug("FixInfacingNormalsProcess finished. Found issues."); nuclear@0: else DefaultLogger::get()->debug("FixInfacingNormalsProcess finished. No changes to the scene."); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Apply the step to the mesh nuclear@0: bool FixInfacingNormalsProcess::ProcessMesh( aiMesh* pcMesh, unsigned int index) nuclear@0: { nuclear@0: ai_assert(NULL != pcMesh); nuclear@0: nuclear@0: // Nothing to do if there are no model normals nuclear@0: if (!pcMesh->HasNormals())return false; nuclear@0: nuclear@0: // Compute the bounding box of both the model vertices + normals and nuclear@0: // the umodified model vertices. Then check whether the first BB nuclear@0: // is smaller than the second. In this case we can assume that the nuclear@0: // normals need to be flipped, although there are a few special cases .. nuclear@0: // convex, concave, planar models ... nuclear@0: nuclear@0: aiVector3D vMin0 (1e10f,1e10f,1e10f); nuclear@0: aiVector3D vMin1 (1e10f,1e10f,1e10f); nuclear@0: aiVector3D vMax0 (-1e10f,-1e10f,-1e10f); nuclear@0: aiVector3D vMax1 (-1e10f,-1e10f,-1e10f); nuclear@0: nuclear@0: for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) nuclear@0: { nuclear@0: vMin1.x = std::min(vMin1.x,pcMesh->mVertices[i].x); nuclear@0: vMin1.y = std::min(vMin1.y,pcMesh->mVertices[i].y); nuclear@0: vMin1.z = std::min(vMin1.z,pcMesh->mVertices[i].z); nuclear@0: nuclear@0: vMax1.x = std::max(vMax1.x,pcMesh->mVertices[i].x); nuclear@0: vMax1.y = std::max(vMax1.y,pcMesh->mVertices[i].y); nuclear@0: vMax1.z = std::max(vMax1.z,pcMesh->mVertices[i].z); nuclear@0: nuclear@0: const aiVector3D vWithNormal = pcMesh->mVertices[i] + pcMesh->mNormals[i]; nuclear@0: nuclear@0: vMin0.x = std::min(vMin0.x,vWithNormal.x); nuclear@0: vMin0.y = std::min(vMin0.y,vWithNormal.y); nuclear@0: vMin0.z = std::min(vMin0.z,vWithNormal.z); nuclear@0: nuclear@0: vMax0.x = std::max(vMax0.x,vWithNormal.x); nuclear@0: vMax0.y = std::max(vMax0.y,vWithNormal.y); nuclear@0: vMax0.z = std::max(vMax0.z,vWithNormal.z); nuclear@0: } nuclear@0: nuclear@0: const float fDelta0_x = (vMax0.x - vMin0.x); nuclear@0: const float fDelta0_y = (vMax0.y - vMin0.y); nuclear@0: const float fDelta0_z = (vMax0.z - vMin0.z); nuclear@0: nuclear@0: const float fDelta1_x = (vMax1.x - vMin1.x); nuclear@0: const float fDelta1_y = (vMax1.y - vMin1.y); nuclear@0: const float fDelta1_z = (vMax1.z - vMin1.z); nuclear@0: nuclear@0: // Check whether the boxes are overlapping nuclear@0: if ((fDelta0_x > 0.0f) != (fDelta1_x > 0.0f))return false; nuclear@0: if ((fDelta0_y > 0.0f) != (fDelta1_y > 0.0f))return false; nuclear@0: if ((fDelta0_z > 0.0f) != (fDelta1_z > 0.0f))return false; nuclear@0: nuclear@0: // Check whether this is a planar surface nuclear@0: const float fDelta1_yz = fDelta1_y * fDelta1_z; nuclear@0: nuclear@0: if (fDelta1_x < 0.05f * sqrtf( fDelta1_yz ))return false; nuclear@0: if (fDelta1_y < 0.05f * sqrtf( fDelta1_z * fDelta1_x ))return false; nuclear@0: if (fDelta1_z < 0.05f * sqrtf( fDelta1_y * fDelta1_x ))return false; nuclear@0: nuclear@0: // now compare the volumes of the bounding boxes nuclear@0: if (::fabsf(fDelta0_x * fDelta1_yz) < nuclear@0: ::fabsf(fDelta1_x * fDelta1_y * fDelta1_z)) nuclear@0: { nuclear@0: if (!DefaultLogger::isNullLogger()) nuclear@0: { nuclear@0: char buffer[128]; // should be sufficiently large nuclear@0: ::sprintf(buffer,"Mesh %i: Normals are facing inwards (or the mesh is planar)",index); nuclear@0: DefaultLogger::get()->info(buffer); nuclear@0: } nuclear@0: nuclear@0: // Invert normals nuclear@0: for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) nuclear@0: pcMesh->mNormals[i] *= -1.0f; nuclear@0: nuclear@0: // ... and flip faces nuclear@0: for (unsigned int i = 0; i < pcMesh->mNumFaces;++i) nuclear@0: { nuclear@0: aiFace& face = pcMesh->mFaces[i]; nuclear@0: for( unsigned int b = 0; b < face.mNumIndices / 2; b++) nuclear@0: std::swap( face.mIndices[b], face.mIndices[ face.mNumIndices - 1 - b]); nuclear@0: } nuclear@0: return true; nuclear@0: } nuclear@0: return false; nuclear@0: }