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