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