/src/assimp/code/AssetLib/Blender/BlenderModifier.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Open Asset Import Library (assimp) |
3 | | ---------------------------------------------------------------------- |
4 | | |
5 | | Copyright (c) 2006-2025, assimp team |
6 | | |
7 | | All rights reserved. |
8 | | |
9 | | Redistribution and use of this software in source and binary forms, |
10 | | with or without modification, are permitted provided that the |
11 | | following conditions are met: |
12 | | |
13 | | * Redistributions of source code must retain the above |
14 | | copyright notice, this list of conditions and the |
15 | | following disclaimer. |
16 | | |
17 | | * Redistributions in binary form must reproduce the above |
18 | | copyright notice, this list of conditions and the |
19 | | following disclaimer in the documentation and/or other |
20 | | materials provided with the distribution. |
21 | | |
22 | | * Neither the name of the assimp team, nor the names of its |
23 | | contributors may be used to endorse or promote products |
24 | | derived from this software without specific prior |
25 | | written permission of the assimp team. |
26 | | |
27 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
28 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
29 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
30 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
31 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
32 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
33 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
34 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
35 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
36 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
37 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
38 | | |
39 | | ---------------------------------------------------------------------- |
40 | | */ |
41 | | |
42 | | /** @file BlenderModifier.cpp |
43 | | * @brief Implementation of some blender modifiers (i.e subdivision, mirror). |
44 | | */ |
45 | | |
46 | | #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER |
47 | | |
48 | | #include "BlenderModifier.h" |
49 | | #include <assimp/SceneCombiner.h> |
50 | | #include <assimp/Subdivision.h> |
51 | | #include <assimp/scene.h> |
52 | | #include <memory> |
53 | | |
54 | | #include <functional> |
55 | | |
56 | | using namespace Assimp; |
57 | | using namespace Assimp::Blender; |
58 | | |
59 | | template <typename T> |
60 | 0 | BlenderModifier *god() { |
61 | 0 | return new T(); |
62 | 0 | } Unexecuted instantiation: Assimp::Blender::BlenderModifier* god<Assimp::Blender::BlenderModifier_Mirror>() Unexecuted instantiation: Assimp::Blender::BlenderModifier* god<Assimp::Blender::BlenderModifier_Subdivision>() |
63 | | |
64 | | // add all available modifiers here |
65 | | typedef BlenderModifier *(*fpCreateModifier)(); |
66 | | static const fpCreateModifier creators[] = { |
67 | | &god<BlenderModifier_Mirror>, |
68 | | &god<BlenderModifier_Subdivision>, |
69 | | |
70 | | nullptr // sentinel |
71 | | }; |
72 | | |
73 | | |
74 | | // ------------------------------------------------------------------------------------------------ |
75 | 0 | void BlenderModifierShowcase::ApplyModifiers(aiNode &out, ConversionData &conv_data, const Scene &in, const Object &orig_object) { |
76 | 0 | size_t cnt = 0u, ful = 0u; |
77 | | |
78 | | // NOTE: this cast is potentially unsafe by design, so we need to perform type checks before |
79 | | // we're allowed to dereference the pointers without risking to crash. We might still be |
80 | | // invoking UB btw - we're assuming that the ModifierData member of the respective modifier |
81 | | // structures is at offset sizeof(vftable) with no padding. |
82 | 0 | const SharedModifierData *cur = static_cast<const SharedModifierData *>(orig_object.modifiers.first.get()); |
83 | 0 | for (; cur; cur = static_cast<const SharedModifierData *>(cur->modifier.next.get()), ++ful) { |
84 | 0 | ai_assert(cur->dna_type); |
85 | |
|
86 | 0 | const Structure *s = conv_data.db.dna.Get(cur->dna_type); |
87 | 0 | if (!s) { |
88 | 0 | ASSIMP_LOG_WARN("BlendModifier: could not resolve DNA name: ", cur->dna_type); |
89 | 0 | continue; |
90 | 0 | } |
91 | | |
92 | | // this is a common trait of all XXXMirrorData structures in BlenderDNA |
93 | 0 | const Field *f = s->Get("modifier"); |
94 | 0 | if (!f || f->offset != 0) { |
95 | 0 | ASSIMP_LOG_WARN("BlendModifier: expected a `modifier` member at offset 0"); |
96 | 0 | continue; |
97 | 0 | } |
98 | | |
99 | 0 | s = conv_data.db.dna.Get(f->type); |
100 | 0 | if (!s || s->name != "ModifierData") { |
101 | 0 | ASSIMP_LOG_WARN("BlendModifier: expected a ModifierData structure as first member"); |
102 | 0 | continue; |
103 | 0 | } |
104 | | |
105 | | // now, we can be sure that we should be fine to dereference *cur* as |
106 | | // ModifierData (with the above note). |
107 | 0 | const ModifierData &dat = cur->modifier; |
108 | |
|
109 | 0 | const fpCreateModifier *curgod = creators; |
110 | 0 | std::vector<BlenderModifier *>::iterator curmod = cached_modifiers->begin(), endmod = cached_modifiers->end(); |
111 | |
|
112 | 0 | for (; *curgod; ++curgod, ++curmod) { // allocate modifiers on the fly |
113 | 0 | if (curmod == endmod) { |
114 | 0 | cached_modifiers->push_back((*curgod)()); |
115 | |
|
116 | 0 | endmod = cached_modifiers->end(); |
117 | 0 | curmod = endmod - 1; |
118 | 0 | } |
119 | |
|
120 | 0 | BlenderModifier *const modifier = *curmod; |
121 | 0 | if (modifier->IsActive(dat)) { |
122 | 0 | modifier->DoIt(out, conv_data, *static_cast<const ElemBase *>(cur), in, orig_object); |
123 | 0 | cnt++; |
124 | |
|
125 | 0 | curgod = nullptr; |
126 | 0 | break; |
127 | 0 | } |
128 | 0 | } |
129 | 0 | if (curgod) { |
130 | 0 | ASSIMP_LOG_WARN("Couldn't find a handler for modifier: ", dat.name); |
131 | 0 | } |
132 | 0 | } |
133 | | |
134 | | // Even though we managed to resolve some or all of the modifiers on this |
135 | | // object, we still can't say whether our modifier implementations were |
136 | | // able to fully do their job. |
137 | 0 | if (ful) { |
138 | 0 | ASSIMP_LOG_DEBUG("BlendModifier: found handlers for ", cnt, " of ", ful, " modifiers on `", orig_object.id.name, |
139 | 0 | "`, check log messages above for errors"); |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | | // ------------------------------------------------------------------------------------------------ |
144 | 0 | bool BlenderModifier_Mirror ::IsActive(const ModifierData &modin) { |
145 | 0 | return modin.type == ModifierData::eModifierType_Mirror; |
146 | 0 | } |
147 | | |
148 | | // ------------------------------------------------------------------------------------------------ |
149 | | void BlenderModifier_Mirror ::DoIt(aiNode &out, ConversionData &conv_data, const ElemBase &orig_modifier, |
150 | | const Scene & /*in*/, |
151 | 0 | const Object &orig_object) { |
152 | | // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers() |
153 | 0 | const MirrorModifierData &mir = static_cast<const MirrorModifierData &>(orig_modifier); |
154 | 0 | ai_assert(mir.modifier.type == ModifierData::eModifierType_Mirror); |
155 | 0 | std::shared_ptr<Object> mirror_ob = mir.mirror_ob.lock(); |
156 | |
|
157 | 0 | conv_data.meshes->reserve(conv_data.meshes->size() + out.mNumMeshes); |
158 | | |
159 | | // XXX not entirely correct, mirroring on two axes results in 4 distinct objects in blender ... |
160 | | |
161 | | // take all input meshes and clone them |
162 | 0 | for (unsigned int i = 0; i < out.mNumMeshes; ++i) { |
163 | 0 | aiMesh *mesh; |
164 | 0 | SceneCombiner::Copy(&mesh, conv_data.meshes[out.mMeshes[i]]); |
165 | |
|
166 | 0 | const float xs = mir.flag & MirrorModifierData::Flags_AXIS_X ? -1.f : 1.f; |
167 | 0 | const float ys = mir.flag & MirrorModifierData::Flags_AXIS_Y ? -1.f : 1.f; |
168 | 0 | const float zs = mir.flag & MirrorModifierData::Flags_AXIS_Z ? -1.f : 1.f; |
169 | |
|
170 | 0 | if (mirror_ob) { |
171 | 0 | const aiVector3D center(mirror_ob->obmat[3][0], mirror_ob->obmat[3][1], mirror_ob->obmat[3][2]); |
172 | 0 | for (unsigned int j = 0; j < mesh->mNumVertices; ++j) { |
173 | 0 | aiVector3D &v = mesh->mVertices[j]; |
174 | |
|
175 | 0 | v.x = center.x + xs * (center.x - v.x); |
176 | 0 | v.y = center.y + ys * (center.y - v.y); |
177 | 0 | v.z = center.z + zs * (center.z - v.z); |
178 | 0 | } |
179 | 0 | } else { |
180 | 0 | for (unsigned int j = 0; j < mesh->mNumVertices; ++j) { |
181 | 0 | aiVector3D &v = mesh->mVertices[j]; |
182 | 0 | v.x *= xs; |
183 | 0 | v.y *= ys; |
184 | 0 | v.z *= zs; |
185 | 0 | } |
186 | 0 | } |
187 | |
|
188 | 0 | if (mesh->mNormals) { |
189 | 0 | for (unsigned int j = 0; j < mesh->mNumVertices; ++j) { |
190 | 0 | aiVector3D &v = mesh->mNormals[j]; |
191 | 0 | v.x *= xs; |
192 | 0 | v.y *= ys; |
193 | 0 | v.z *= zs; |
194 | 0 | } |
195 | 0 | } |
196 | |
|
197 | 0 | if (mesh->mTangents) { |
198 | 0 | for (unsigned int j = 0; j < mesh->mNumVertices; ++j) { |
199 | 0 | aiVector3D &v = mesh->mTangents[j]; |
200 | 0 | v.x *= xs; |
201 | 0 | v.y *= ys; |
202 | 0 | v.z *= zs; |
203 | 0 | } |
204 | 0 | } |
205 | |
|
206 | 0 | if (mesh->mBitangents) { |
207 | 0 | for (unsigned int j = 0; j < mesh->mNumVertices; ++j) { |
208 | 0 | aiVector3D &v = mesh->mBitangents[j]; |
209 | 0 | v.x *= xs; |
210 | 0 | v.y *= ys; |
211 | 0 | v.z *= zs; |
212 | 0 | } |
213 | 0 | } |
214 | |
|
215 | 0 | const float us = mir.flag & MirrorModifierData::Flags_MIRROR_U ? -1.f : 1.f; |
216 | 0 | const float vs = mir.flag & MirrorModifierData::Flags_MIRROR_V ? -1.f : 1.f; |
217 | |
|
218 | 0 | for (unsigned int n = 0; mesh->HasTextureCoords(n); ++n) { |
219 | 0 | for (unsigned int j = 0; j < mesh->mNumVertices; ++j) { |
220 | 0 | aiVector3D &v = mesh->mTextureCoords[n][j]; |
221 | 0 | v.x *= us; |
222 | 0 | v.y *= vs; |
223 | 0 | } |
224 | 0 | } |
225 | | |
226 | | // Only reverse the winding order if an odd number of axes were mirrored. |
227 | 0 | if (xs * ys * zs < 0) { |
228 | 0 | for (unsigned int j = 0; j < mesh->mNumFaces; ++j) { |
229 | 0 | aiFace &face = mesh->mFaces[j]; |
230 | 0 | for (unsigned int fi = 0; fi < face.mNumIndices / 2; ++fi) |
231 | 0 | std::swap(face.mIndices[fi], face.mIndices[face.mNumIndices - 1 - fi]); |
232 | 0 | } |
233 | 0 | } |
234 | |
|
235 | 0 | conv_data.meshes->push_back(mesh); |
236 | 0 | } |
237 | 0 | unsigned int *nind = new unsigned int[out.mNumMeshes * 2]; |
238 | |
|
239 | 0 | std::copy(out.mMeshes, out.mMeshes + out.mNumMeshes, nind); |
240 | 0 | std::transform(out.mMeshes, out.mMeshes + out.mNumMeshes, nind + out.mNumMeshes, |
241 | 0 | [&out](unsigned int n) { return out.mNumMeshes + n; }); |
242 | |
|
243 | 0 | delete[] out.mMeshes; |
244 | 0 | out.mMeshes = nind; |
245 | 0 | out.mNumMeshes *= 2; |
246 | |
|
247 | 0 | ASSIMP_LOG_INFO("BlendModifier: Applied the `Mirror` modifier to `", |
248 | 0 | orig_object.id.name, "`"); |
249 | 0 | } |
250 | | |
251 | | // ------------------------------------------------------------------------------------------------ |
252 | 0 | bool BlenderModifier_Subdivision ::IsActive(const ModifierData &modin) { |
253 | 0 | return modin.type == ModifierData::eModifierType_Subsurf; |
254 | 0 | } |
255 | | |
256 | | // ------------------------------------------------------------------------------------------------ |
257 | | void BlenderModifier_Subdivision ::DoIt(aiNode &out, ConversionData &conv_data, const ElemBase &orig_modifier, |
258 | | const Scene & /*in*/, |
259 | 0 | const Object &orig_object) { |
260 | | // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers() |
261 | 0 | const SubsurfModifierData &mir = static_cast<const SubsurfModifierData &>(orig_modifier); |
262 | 0 | ai_assert(mir.modifier.type == ModifierData::eModifierType_Subsurf); |
263 | |
|
264 | 0 | Subdivider::Algorithm algo; |
265 | 0 | switch (mir.subdivType) { |
266 | 0 | case SubsurfModifierData::TYPE_CatmullClarke: |
267 | 0 | algo = Subdivider::CATMULL_CLARKE; |
268 | 0 | break; |
269 | | |
270 | 0 | case SubsurfModifierData::TYPE_Simple: |
271 | 0 | ASSIMP_LOG_WARN("BlendModifier: The `SIMPLE` subdivision algorithm is not currently implemented, using Catmull-Clarke"); |
272 | 0 | algo = Subdivider::CATMULL_CLARKE; |
273 | 0 | break; |
274 | | |
275 | 0 | default: |
276 | 0 | ASSIMP_LOG_WARN("BlendModifier: Unrecognized subdivision algorithm: ", mir.subdivType); |
277 | 0 | return; |
278 | 0 | }; |
279 | |
|
280 | 0 | std::unique_ptr<Subdivider> subd(Subdivider::Create(algo)); |
281 | 0 | ai_assert(subd); |
282 | 0 | if (conv_data.meshes->empty()) { |
283 | 0 | return; |
284 | 0 | } |
285 | 0 | const size_t meshIndex = conv_data.meshes->size() - out.mNumMeshes; |
286 | 0 | if (meshIndex >= conv_data.meshes->size()) { |
287 | 0 | ASSIMP_LOG_ERROR("Invalid index detected."); |
288 | 0 | return; |
289 | 0 | } |
290 | 0 | aiMesh **const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes]; |
291 | 0 | std::unique_ptr<aiMesh *[]> tempmeshes(new aiMesh *[out.mNumMeshes]()); |
292 | |
|
293 | 0 | subd->Subdivide(meshes, out.mNumMeshes, tempmeshes.get(), std::max(mir.renderLevels, mir.levels), true); |
294 | 0 | std::copy(tempmeshes.get(), tempmeshes.get() + out.mNumMeshes, meshes); |
295 | |
|
296 | 0 | ASSIMP_LOG_INFO("BlendModifier: Applied the `Subdivision` modifier to `", |
297 | 0 | orig_object.id.name, "`"); |
298 | 0 | } |
299 | | |
300 | | #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER |