1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions are met: |
14 | |
15 | * Redistributions of source code must retain the above |
16 | copyright notice, this list of conditions and the |
17 | following disclaimer. |
18 | |
19 | * Redistributions in binary form must reproduce the above |
20 | copyright notice, this list of conditions and the |
21 | following disclaimer in the documentation and/or other |
22 | materials provided with the distribution. |
23 | |
24 | * Neither the name of the assimp team, nor the names of its |
25 | contributors may be used to endorse or promote products |
26 | derived from this software without specific prior |
27 | written permission of the assimp team. |
28 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
40 | --------------------------------------------------------------------------- |
41 | */ |
42 | |
43 | /** @file Implementation of the post processing step to invert |
44 | * all normals in meshes with infacing normals. |
45 | */ |
46 | |
47 | // internal headers |
48 | #include "FixNormalsStep.h" |
49 | #include "StringUtils.h" |
50 | #include <assimp/DefaultLogger.hpp> |
51 | #include <assimp/postprocess.h> |
52 | #include <assimp/scene.h> |
53 | #include <stdio.h> |
54 | |
55 | |
56 | using namespace Assimp; |
57 | |
58 | |
59 | // ------------------------------------------------------------------------------------------------ |
60 | // Constructor to be privately used by Importer |
61 | FixInfacingNormalsProcess::FixInfacingNormalsProcess() |
62 | { |
63 | // nothing to do here |
64 | } |
65 | |
66 | // ------------------------------------------------------------------------------------------------ |
67 | // Destructor, private as well |
68 | FixInfacingNormalsProcess::~FixInfacingNormalsProcess() |
69 | { |
70 | // nothing to do here |
71 | } |
72 | |
73 | // ------------------------------------------------------------------------------------------------ |
74 | // Returns whether the processing step is present in the given flag field. |
75 | bool FixInfacingNormalsProcess::IsActive( unsigned int pFlags) const |
76 | { |
77 | return (pFlags & aiProcess_FixInfacingNormals) != 0; |
78 | } |
79 | |
80 | // ------------------------------------------------------------------------------------------------ |
81 | // Executes the post processing step on the given imported data. |
82 | void FixInfacingNormalsProcess::Execute( aiScene* pScene) |
83 | { |
84 | DefaultLogger::get()->debug("FixInfacingNormalsProcess begin" ); |
85 | |
86 | bool bHas = false; |
87 | for( unsigned int a = 0; a < pScene->mNumMeshes; a++) |
88 | if(ProcessMesh( pScene->mMeshes[a],a))bHas = true; |
89 | |
90 | if (bHas) |
91 | DefaultLogger::get()->debug("FixInfacingNormalsProcess finished. Found issues." ); |
92 | else DefaultLogger::get()->debug("FixInfacingNormalsProcess finished. No changes to the scene." ); |
93 | } |
94 | |
95 | // ------------------------------------------------------------------------------------------------ |
96 | // Apply the step to the mesh |
97 | bool FixInfacingNormalsProcess::ProcessMesh( aiMesh* pcMesh, unsigned int index) |
98 | { |
99 | ai_assert(NULL != pcMesh); |
100 | |
101 | // Nothing to do if there are no model normals |
102 | if (!pcMesh->HasNormals())return false; |
103 | |
104 | // Compute the bounding box of both the model vertices + normals and |
105 | // the umodified model vertices. Then check whether the first BB |
106 | // is smaller than the second. In this case we can assume that the |
107 | // normals need to be flipped, although there are a few special cases .. |
108 | // convex, concave, planar models ... |
109 | |
110 | aiVector3D vMin0 (1e10f,1e10f,1e10f); |
111 | aiVector3D vMin1 (1e10f,1e10f,1e10f); |
112 | aiVector3D vMax0 (-1e10f,-1e10f,-1e10f); |
113 | aiVector3D vMax1 (-1e10f,-1e10f,-1e10f); |
114 | |
115 | for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) |
116 | { |
117 | vMin1.x = std::min(vMin1.x,pcMesh->mVertices[i].x); |
118 | vMin1.y = std::min(vMin1.y,pcMesh->mVertices[i].y); |
119 | vMin1.z = std::min(vMin1.z,pcMesh->mVertices[i].z); |
120 | |
121 | vMax1.x = std::max(vMax1.x,pcMesh->mVertices[i].x); |
122 | vMax1.y = std::max(vMax1.y,pcMesh->mVertices[i].y); |
123 | vMax1.z = std::max(vMax1.z,pcMesh->mVertices[i].z); |
124 | |
125 | const aiVector3D vWithNormal = pcMesh->mVertices[i] + pcMesh->mNormals[i]; |
126 | |
127 | vMin0.x = std::min(vMin0.x,vWithNormal.x); |
128 | vMin0.y = std::min(vMin0.y,vWithNormal.y); |
129 | vMin0.z = std::min(vMin0.z,vWithNormal.z); |
130 | |
131 | vMax0.x = std::max(vMax0.x,vWithNormal.x); |
132 | vMax0.y = std::max(vMax0.y,vWithNormal.y); |
133 | vMax0.z = std::max(vMax0.z,vWithNormal.z); |
134 | } |
135 | |
136 | const float fDelta0_x = (vMax0.x - vMin0.x); |
137 | const float fDelta0_y = (vMax0.y - vMin0.y); |
138 | const float fDelta0_z = (vMax0.z - vMin0.z); |
139 | |
140 | const float fDelta1_x = (vMax1.x - vMin1.x); |
141 | const float fDelta1_y = (vMax1.y - vMin1.y); |
142 | const float fDelta1_z = (vMax1.z - vMin1.z); |
143 | |
144 | // Check whether the boxes are overlapping |
145 | if ((fDelta0_x > 0.0f) != (fDelta1_x > 0.0f))return false; |
146 | if ((fDelta0_y > 0.0f) != (fDelta1_y > 0.0f))return false; |
147 | if ((fDelta0_z > 0.0f) != (fDelta1_z > 0.0f))return false; |
148 | |
149 | // Check whether this is a planar surface |
150 | const float fDelta1_yz = fDelta1_y * fDelta1_z; |
151 | |
152 | if (fDelta1_x < 0.05f * std::sqrt( fDelta1_yz ))return false; |
153 | if (fDelta1_y < 0.05f * std::sqrt( fDelta1_z * fDelta1_x ))return false; |
154 | if (fDelta1_z < 0.05f * std::sqrt( fDelta1_y * fDelta1_x ))return false; |
155 | |
156 | // now compare the volumes of the bounding boxes |
157 | if (std::fabs(fDelta0_x * fDelta0_y * fDelta0_z) < |
158 | std::fabs(fDelta1_x * fDelta1_yz)) |
159 | { |
160 | if (!DefaultLogger::isNullLogger()) |
161 | { |
162 | char buffer[128]; // should be sufficiently large |
163 | ai_snprintf(buffer,128,"Mesh %u: Normals are facing inwards (or the mesh is planar)" ,index); |
164 | DefaultLogger::get()->info(buffer); |
165 | } |
166 | |
167 | // Invert normals |
168 | for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) |
169 | pcMesh->mNormals[i] *= -1.0f; |
170 | |
171 | // ... and flip faces |
172 | for (unsigned int i = 0; i < pcMesh->mNumFaces;++i) |
173 | { |
174 | aiFace& face = pcMesh->mFaces[i]; |
175 | for( unsigned int b = 0; b < face.mNumIndices / 2; b++) |
176 | std::swap( face.mIndices[b], face.mIndices[ face.mNumIndices - 1 - b]); |
177 | } |
178 | return true; |
179 | } |
180 | return false; |
181 | } |
182 | |