/src/assimp/code/PostProcessing/GenFaceNormalsProcess.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2025, assimp team |
7 | | |
8 | | All rights reserved. |
9 | | |
10 | | Redistribution and use of this software in source and binary forms, |
11 | | with or without modification, are permitted provided that the following |
12 | | conditions are met: |
13 | | |
14 | | * Redistributions of source code must retain the above |
15 | | copyright notice, this list of conditions and the |
16 | | following disclaimer. |
17 | | |
18 | | * Redistributions in binary form must reproduce the above |
19 | | copyright notice, this list of conditions and the |
20 | | following disclaimer in the documentation and/or other |
21 | | materials provided with the distribution. |
22 | | |
23 | | * Neither the name of the assimp team, nor the names of its |
24 | | contributors may be used to endorse or promote products |
25 | | derived from this software without specific prior |
26 | | written permission of the assimp team. |
27 | | |
28 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
29 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
30 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
31 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
32 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
33 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
34 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
35 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
36 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
37 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
38 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
39 | | --------------------------------------------------------------------------- |
40 | | */ |
41 | | |
42 | | /** |
43 | | * @file Implementation of the post-processing step to generate face |
44 | | * normals for all imported faces. |
45 | | */ |
46 | | |
47 | | #include "GenFaceNormalsProcess.h" |
48 | | #include <assimp/Exceptional.h> |
49 | | #include <assimp/postprocess.h> |
50 | | #include <assimp/qnan.h> |
51 | | #include <assimp/scene.h> |
52 | | #include <assimp/DefaultLogger.hpp> |
53 | | |
54 | | using namespace Assimp; |
55 | | |
56 | | // ------------------------------------------------------------------------------------------------ |
57 | | // Returns whether the processing step is in the given flag field. |
58 | 96 | bool GenFaceNormalsProcess::IsActive(unsigned int pFlags) const { |
59 | 96 | force_ = (pFlags & aiProcess_ForceGenNormals) != 0; |
60 | 96 | flippedWindingOrder_ = (pFlags & aiProcess_FlipWindingOrder) != 0; |
61 | 96 | leftHanded_ = (pFlags & aiProcess_MakeLeftHanded) != 0; |
62 | 96 | return (pFlags & aiProcess_GenNormals) != 0; |
63 | 96 | } |
64 | | |
65 | | // ------------------------------------------------------------------------------------------------ |
66 | | // Executes the post-processing step on the given imported data. |
67 | 0 | void GenFaceNormalsProcess::Execute(aiScene *pScene) { |
68 | 0 | ASSIMP_LOG_DEBUG("GenFaceNormalsProcess begin"); |
69 | |
|
70 | 0 | if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { |
71 | 0 | throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); |
72 | 0 | } |
73 | | |
74 | 0 | bool bHas = false; |
75 | 0 | for (unsigned int a = 0; a < pScene->mNumMeshes; a++) { |
76 | 0 | if (this->GenMeshFaceNormals(pScene->mMeshes[a])) { |
77 | 0 | bHas = true; |
78 | 0 | } |
79 | 0 | } |
80 | 0 | if (bHas) { |
81 | 0 | ASSIMP_LOG_INFO("GenFaceNormalsProcess finished. " |
82 | 0 | "Face normals have been calculated"); |
83 | 0 | } else { |
84 | 0 | ASSIMP_LOG_DEBUG("GenFaceNormalsProcess finished. " |
85 | 0 | "Normals are already there"); |
86 | 0 | } |
87 | 0 | } |
88 | | |
89 | | // ------------------------------------------------------------------------------------------------ |
90 | | // Executes the post-processing step on the given imported data. |
91 | 0 | bool GenFaceNormalsProcess::GenMeshFaceNormals(aiMesh *pMesh) { |
92 | 0 | if (nullptr != pMesh->mNormals) { |
93 | 0 | if (force_) { |
94 | 0 | delete[] pMesh->mNormals; |
95 | 0 | } else { |
96 | 0 | return false; |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | | // If the mesh consists of lines and/or points but not of |
101 | | // triangles or higher-order polygons the normal vectors |
102 | | // are undefined. |
103 | 0 | if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) { |
104 | 0 | ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes"); |
105 | 0 | return false; |
106 | 0 | } |
107 | | |
108 | | // allocate an array to hold the output normals |
109 | 0 | std::vector<aiVector3D> normals; |
110 | 0 | normals.resize(pMesh->mNumVertices); |
111 | | |
112 | | // mask to indicate if a vertex was already referenced and needs to be duplicated |
113 | 0 | std::vector<bool> alreadyReferenced; |
114 | 0 | alreadyReferenced.resize(pMesh->mNumVertices, false); |
115 | 0 | std::vector<aiVector3D> duplicatedVertices; |
116 | |
|
117 | 0 | auto storeNormalSplitVertex = [&](unsigned int index, const aiVector3D& normal) { |
118 | 0 | if (!alreadyReferenced[index]) { |
119 | 0 | normals[index] = normal; |
120 | 0 | alreadyReferenced[index] = true; |
121 | 0 | } else { |
122 | 0 | duplicatedVertices.push_back(pMesh->mVertices[index]); |
123 | 0 | normals.push_back(normal); |
124 | 0 | index = pMesh->mNumVertices + static_cast<unsigned int>(duplicatedVertices.size() - 1); |
125 | 0 | } |
126 | 0 | return index; |
127 | 0 | }; |
128 | |
|
129 | 0 | const aiVector3D undefinedNormal = aiVector3D(get_qnan()); |
130 | | |
131 | | // iterate through all faces and compute per-face normals but store them per-vertex. |
132 | 0 | for (unsigned int a = 0; a < pMesh->mNumFaces; a++) { |
133 | 0 | const aiFace &face = pMesh->mFaces[a]; |
134 | 0 | if (face.mNumIndices < 3) { |
135 | | // either a point or a line -> no well-defined normal vector |
136 | 0 | for (unsigned int i = 0; i < face.mNumIndices; ++i) { |
137 | 0 | face.mIndices[i] = storeNormalSplitVertex(face.mIndices[i], undefinedNormal); |
138 | 0 | } |
139 | 0 | continue; |
140 | 0 | } |
141 | | |
142 | 0 | const aiVector3D *pV1 = &pMesh->mVertices[face.mIndices[0]]; |
143 | 0 | const aiVector3D *pV2 = &pMesh->mVertices[face.mIndices[1]]; |
144 | 0 | const aiVector3D *pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices - 1]]; |
145 | | // Boolean XOR - if either but not both of these flags are set, then the winding order has |
146 | | // changed and the cross-product to calculate the normal needs to be reversed |
147 | 0 | if (flippedWindingOrder_ != leftHanded_) |
148 | 0 | std::swap(pV2, pV3); |
149 | 0 | const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe(); |
150 | |
|
151 | 0 | for (unsigned int i = 0; i < face.mNumIndices; ++i) { |
152 | 0 | face.mIndices[i] = storeNormalSplitVertex(face.mIndices[i], vNor); |
153 | 0 | } |
154 | 0 | } |
155 | | |
156 | | // store normals (and additional vertices) back into the mesh |
157 | 0 | if (!duplicatedVertices.empty()) { |
158 | 0 | const aiVector3D * oldVertices = pMesh->mVertices; |
159 | 0 | auto oldNumVertices = pMesh->mNumVertices; |
160 | 0 | pMesh->mNumVertices += static_cast<unsigned int>(duplicatedVertices.size()); |
161 | 0 | pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; |
162 | 0 | memcpy(pMesh->mVertices, oldVertices, oldNumVertices * sizeof(aiVector3D)); |
163 | 0 | memcpy(pMesh->mVertices + oldNumVertices, duplicatedVertices.data(), duplicatedVertices.size() * sizeof(aiVector3D)); |
164 | 0 | delete[] oldVertices; |
165 | 0 | } |
166 | 0 | pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; |
167 | 0 | memcpy(pMesh->mNormals, normals.data(), normals.size() * sizeof(aiVector3D)); |
168 | |
|
169 | 0 | return true; |
170 | 0 | } |