1/*
2---------------------------------------------------------------------------
3Open Asset Import Library (assimp)
4---------------------------------------------------------------------------
5
6Copyright (c) 2006-2017, assimp team
7
8
9All rights reserved.
10
11Redistribution and use of this software in source and binary forms,
12with or without modification, are permitted provided that the following
13conditions 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
29THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40---------------------------------------------------------------------------
41*/
42
43/** @file Implementation of the post processing step to generate face
44* normals for all imported faces.
45*/
46
47
48
49// internal headers
50#include "GenVertexNormalsProcess.h"
51#include "ProcessHelper.h"
52#include "Exceptional.h"
53#include "qnan.h"
54
55using namespace Assimp;
56
57// ------------------------------------------------------------------------------------------------
58// Constructor to be privately used by Importer
59GenVertexNormalsProcess::GenVertexNormalsProcess()
60: configMaxAngle( AI_DEG_TO_RAD( 175.f ) ) {
61 // empty
62}
63
64// ------------------------------------------------------------------------------------------------
65// Destructor, private as well
66GenVertexNormalsProcess::~GenVertexNormalsProcess() {
67 // nothing to do here
68}
69
70// ------------------------------------------------------------------------------------------------
71// Returns whether the processing step is present in the given flag field.
72bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const
73{
74 return (pFlags & aiProcess_GenSmoothNormals) != 0;
75}
76
77// ------------------------------------------------------------------------------------------------
78// Executes the post processing step on the given imported data.
79void GenVertexNormalsProcess::SetupProperties(const Importer* pImp)
80{
81 // Get the current value of the AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE property
82 configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,(ai_real)175.0);
83 configMaxAngle = AI_DEG_TO_RAD(std::max(std::min(configMaxAngle,(ai_real)175.0),(ai_real)0.0));
84}
85
86// ------------------------------------------------------------------------------------------------
87// Executes the post processing step on the given imported data.
88void GenVertexNormalsProcess::Execute( aiScene* pScene)
89{
90 DefaultLogger::get()->debug("GenVertexNormalsProcess begin");
91
92 if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT)
93 throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
94
95 bool bHas = false;
96 for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
97 {
98 if(GenMeshVertexNormals( pScene->mMeshes[a],a))
99 bHas = true;
100 }
101
102 if (bHas) {
103 DefaultLogger::get()->info("GenVertexNormalsProcess finished. "
104 "Vertex normals have been calculated");
105 }
106 else DefaultLogger::get()->debug("GenVertexNormalsProcess finished. "
107 "Normals are already there");
108}
109
110// ------------------------------------------------------------------------------------------------
111// Executes the post processing step on the given imported data.
112bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int meshIndex)
113{
114 if (NULL != pMesh->mNormals)
115 return false;
116
117 // If the mesh consists of lines and/or points but not of
118 // triangles or higher-order polygons the normal vectors
119 // are undefined.
120 if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON)))
121 {
122 DefaultLogger::get()->info("Normal vectors are undefined for line and point meshes");
123 return false;
124 }
125
126 // Allocate the array to hold the output normals
127 const float qnan = std::numeric_limits<ai_real>::quiet_NaN();
128 pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
129
130 // Compute per-face normals but store them per-vertex
131 for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
132 {
133 const aiFace& face = pMesh->mFaces[a];
134 if (face.mNumIndices < 3)
135 {
136 // either a point or a line -> no normal vector
137 for (unsigned int i = 0;i < face.mNumIndices;++i) {
138 pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan);
139 }
140
141 continue;
142 }
143
144 const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
145 const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
146 const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]];
147 const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1));
148
149 for (unsigned int i = 0;i < face.mNumIndices;++i) {
150 pMesh->mNormals[face.mIndices[i]] = vNor;
151 }
152 }
153
154 // Set up a SpatialSort to quickly find all vertices close to a given position
155 // check whether we can reuse the SpatialSort of a previous step.
156 SpatialSort* vertexFinder = NULL;
157 SpatialSort _vertexFinder;
158 ai_real posEpsilon = ai_real( 1e-5 );
159 if (shared) {
160 std::vector<std::pair<SpatialSort,ai_real> >* avf;
161 shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
162 if (avf)
163 {
164 std::pair<SpatialSort,ai_real>& blubb = avf->operator [] (meshIndex);
165 vertexFinder = &blubb.first;
166 posEpsilon = blubb.second;
167 }
168 }
169 if (!vertexFinder) {
170 _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
171 vertexFinder = &_vertexFinder;
172 posEpsilon = ComputePositionEpsilon(pMesh);
173 }
174 std::vector<unsigned int> verticesFound;
175 aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices];
176
177 if (configMaxAngle >= AI_DEG_TO_RAD( 175.f )) {
178 // There is no angle limit. Thus all vertices with positions close
179 // to each other will receive the same vertex normal. This allows us
180 // to optimize the whole algorithm a little bit ...
181 std::vector<bool> abHad(pMesh->mNumVertices,false);
182 for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
183 if (abHad[i]) {
184 continue;
185 }
186
187 // Get all vertices that share this one ...
188 vertexFinder->FindPositions( pMesh->mVertices[i], posEpsilon, verticesFound);
189
190 aiVector3D pcNor;
191 for (unsigned int a = 0; a < verticesFound.size(); ++a) {
192 const aiVector3D& v = pMesh->mNormals[verticesFound[a]];
193 if (is_not_qnan(v.x))pcNor += v;
194 }
195 pcNor.NormalizeSafe();
196
197 // Write the smoothed normal back to all affected normals
198 for (unsigned int a = 0; a < verticesFound.size(); ++a)
199 {
200 unsigned int vidx = verticesFound[a];
201 pcNew[vidx] = pcNor;
202 abHad[vidx] = true;
203 }
204 }
205 }
206 // Slower code path if a smooth angle is set. There are many ways to achieve
207 // the effect, this one is the most straightforward one.
208 else {
209 const ai_real fLimit = std::cos(configMaxAngle);
210 for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
211 // Get all vertices that share this one ...
212 vertexFinder->FindPositions( pMesh->mVertices[i] , posEpsilon, verticesFound);
213
214 aiVector3D vr = pMesh->mNormals[i];
215 ai_real vrlen = vr.Length();
216
217 aiVector3D pcNor;
218 for (unsigned int a = 0; a < verticesFound.size(); ++a) {
219 aiVector3D v = pMesh->mNormals[verticesFound[a]];
220
221 // check whether the angle between the two normals is not too large
222 // HACK: if v.x is qnan the dot product will become qnan, too
223 // therefore the comparison against fLimit should be false
224 // in every case.
225 if (v * vr >= fLimit * vrlen * v.Length())
226 pcNor += v;
227 }
228 pcNew[i] = pcNor.NormalizeSafe();
229 }
230 }
231
232 delete[] pMesh->mNormals;
233 pMesh->mNormals = pcNew;
234
235 return true;
236}
237