1 | /* |
2 | Open Asset Import Library (assimp) |
3 | ---------------------------------------------------------------------- |
4 | |
5 | Copyright (c) 2006-2017, 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 "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> BlenderModifier* god() { |
60 | return new T(); |
61 | } |
62 | |
63 | // add all available modifiers here |
64 | typedef BlenderModifier* (*fpCreateModifier)(); |
65 | static const fpCreateModifier creators[] = { |
66 | &god<BlenderModifier_Mirror>, |
67 | &god<BlenderModifier_Subdivision>, |
68 | |
69 | NULL // sentinel |
70 | }; |
71 | |
72 | // ------------------------------------------------------------------------------------------------ |
73 | // just testing out some new macros to simplify logging |
74 | #define ASSIMP_LOG_WARN_F(string,...)\ |
75 | DefaultLogger::get()->warn((Formatter::format(string),__VA_ARGS__)) |
76 | |
77 | #define ASSIMP_LOG_ERROR_F(string,...)\ |
78 | DefaultLogger::get()->error((Formatter::format(string),__VA_ARGS__)) |
79 | |
80 | #define ASSIMP_LOG_DEBUG_F(string,...)\ |
81 | DefaultLogger::get()->debug((Formatter::format(string),__VA_ARGS__)) |
82 | |
83 | #define ASSIMP_LOG_INFO_F(string,...)\ |
84 | DefaultLogger::get()->info((Formatter::format(string),__VA_ARGS__)) |
85 | |
86 | |
87 | #define ASSIMP_LOG_WARN(string)\ |
88 | DefaultLogger::get()->warn(string) |
89 | |
90 | #define ASSIMP_LOG_ERROR(string)\ |
91 | DefaultLogger::get()->error(string) |
92 | |
93 | #define ASSIMP_LOG_DEBUG(string)\ |
94 | DefaultLogger::get()->debug(string) |
95 | |
96 | #define ASSIMP_LOG_INFO(string)\ |
97 | DefaultLogger::get()->info(string) |
98 | |
99 | |
100 | // ------------------------------------------------------------------------------------------------ |
101 | struct SharedModifierData : ElemBase |
102 | { |
103 | ModifierData modifier; |
104 | }; |
105 | |
106 | // ------------------------------------------------------------------------------------------------ |
107 | void BlenderModifierShowcase::ApplyModifiers(aiNode& out, ConversionData& conv_data, const Scene& in, const Object& orig_object ) |
108 | { |
109 | size_t cnt = 0u, ful = 0u; |
110 | |
111 | // NOTE: this cast is potentially unsafe by design, so we need to perform type checks before |
112 | // we're allowed to dereference the pointers without risking to crash. We might still be |
113 | // invoking UB btw - we're assuming that the ModifierData member of the respective modifier |
114 | // structures is at offset sizeof(vftable) with no padding. |
115 | const SharedModifierData* cur = static_cast<const SharedModifierData *> ( orig_object.modifiers.first.get() ); |
116 | for (; cur; cur = static_cast<const SharedModifierData *> ( cur->modifier.next.get() ), ++ful) { |
117 | ai_assert(cur->dna_type); |
118 | |
119 | const Structure* s = conv_data.db.dna.Get( cur->dna_type ); |
120 | if (!s) { |
121 | ASSIMP_LOG_WARN_F("BlendModifier: could not resolve DNA name: " ,cur->dna_type); |
122 | continue; |
123 | } |
124 | |
125 | // this is a common trait of all XXXMirrorData structures in BlenderDNA |
126 | const Field* f = s->Get("modifier" ); |
127 | if (!f || f->offset != 0) { |
128 | ASSIMP_LOG_WARN("BlendModifier: expected a `modifier` member at offset 0" ); |
129 | continue; |
130 | } |
131 | |
132 | s = conv_data.db.dna.Get( f->type ); |
133 | if (!s || s->name != "ModifierData" ) { |
134 | ASSIMP_LOG_WARN("BlendModifier: expected a ModifierData structure as first member" ); |
135 | continue; |
136 | } |
137 | |
138 | // now, we can be sure that we should be fine to dereference *cur* as |
139 | // ModifierData (with the above note). |
140 | const ModifierData& dat = cur->modifier; |
141 | |
142 | const fpCreateModifier* curgod = creators; |
143 | std::vector< BlenderModifier* >::iterator curmod = cached_modifiers->begin(), endmod = cached_modifiers->end(); |
144 | |
145 | for (;*curgod;++curgod,++curmod) { // allocate modifiers on the fly |
146 | if (curmod == endmod) { |
147 | cached_modifiers->push_back((*curgod)()); |
148 | |
149 | endmod = cached_modifiers->end(); |
150 | curmod = endmod-1; |
151 | } |
152 | |
153 | BlenderModifier* const modifier = *curmod; |
154 | if(modifier->IsActive(dat)) { |
155 | modifier->DoIt(out,conv_data,*static_cast<const ElemBase *>(cur),in,orig_object); |
156 | cnt++; |
157 | |
158 | curgod = NULL; |
159 | break; |
160 | } |
161 | } |
162 | if (curgod) { |
163 | ASSIMP_LOG_WARN_F("Couldn't find a handler for modifier: " ,dat.name); |
164 | } |
165 | } |
166 | |
167 | // Even though we managed to resolve some or all of the modifiers on this |
168 | // object, we still can't say whether our modifier implementations were |
169 | // able to fully do their job. |
170 | if (ful) { |
171 | ASSIMP_LOG_DEBUG_F("BlendModifier: found handlers for " ,cnt," of " ,ful," modifiers on `" ,orig_object.id.name, |
172 | "`, check log messages above for errors" ); |
173 | } |
174 | } |
175 | |
176 | |
177 | |
178 | // ------------------------------------------------------------------------------------------------ |
179 | bool BlenderModifier_Mirror :: IsActive (const ModifierData& modin) |
180 | { |
181 | return modin.type == ModifierData::eModifierType_Mirror; |
182 | } |
183 | |
184 | // ------------------------------------------------------------------------------------------------ |
185 | void BlenderModifier_Mirror :: DoIt(aiNode& out, ConversionData& conv_data, const ElemBase& orig_modifier, |
186 | const Scene& /*in*/, |
187 | const Object& orig_object ) |
188 | { |
189 | // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers() |
190 | const MirrorModifierData& mir = static_cast<const MirrorModifierData&>(orig_modifier); |
191 | ai_assert(mir.modifier.type == ModifierData::eModifierType_Mirror); |
192 | |
193 | conv_data.meshes->reserve(conv_data.meshes->size() + out.mNumMeshes); |
194 | |
195 | // XXX not entirely correct, mirroring on two axes results in 4 distinct objects in blender ... |
196 | |
197 | // take all input meshes and clone them |
198 | for (unsigned int i = 0; i < out.mNumMeshes; ++i) { |
199 | aiMesh* mesh; |
200 | SceneCombiner::Copy(&mesh,conv_data.meshes[out.mMeshes[i]]); |
201 | |
202 | const float xs = mir.flag & MirrorModifierData::Flags_AXIS_X ? -1.f : 1.f; |
203 | const float ys = mir.flag & MirrorModifierData::Flags_AXIS_Y ? -1.f : 1.f; |
204 | const float zs = mir.flag & MirrorModifierData::Flags_AXIS_Z ? -1.f : 1.f; |
205 | |
206 | if (mir.mirror_ob) { |
207 | const aiVector3D center( mir.mirror_ob->obmat[3][0],mir.mirror_ob->obmat[3][1],mir.mirror_ob->obmat[3][2] ); |
208 | for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { |
209 | aiVector3D& v = mesh->mVertices[i]; |
210 | |
211 | v.x = center.x + xs*(center.x - v.x); |
212 | v.y = center.y + ys*(center.y - v.y); |
213 | v.z = center.z + zs*(center.z - v.z); |
214 | } |
215 | } |
216 | else { |
217 | for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { |
218 | aiVector3D& v = mesh->mVertices[i]; |
219 | v.x *= xs;v.y *= ys;v.z *= zs; |
220 | } |
221 | } |
222 | |
223 | if (mesh->mNormals) { |
224 | for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { |
225 | aiVector3D& v = mesh->mNormals[i]; |
226 | v.x *= xs;v.y *= ys;v.z *= zs; |
227 | } |
228 | } |
229 | |
230 | if (mesh->mTangents) { |
231 | for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { |
232 | aiVector3D& v = mesh->mTangents[i]; |
233 | v.x *= xs;v.y *= ys;v.z *= zs; |
234 | } |
235 | } |
236 | |
237 | if (mesh->mBitangents) { |
238 | for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { |
239 | aiVector3D& v = mesh->mBitangents[i]; |
240 | v.x *= xs;v.y *= ys;v.z *= zs; |
241 | } |
242 | } |
243 | |
244 | const float us = mir.flag & MirrorModifierData::Flags_MIRROR_U ? -1.f : 1.f; |
245 | const float vs = mir.flag & MirrorModifierData::Flags_MIRROR_V ? -1.f : 1.f; |
246 | |
247 | for (unsigned int n = 0; mesh->HasTextureCoords(n); ++n) { |
248 | for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { |
249 | aiVector3D& v = mesh->mTextureCoords[n][i]; |
250 | v.x *= us;v.y *= vs; |
251 | } |
252 | } |
253 | |
254 | // Only reverse the winding order if an odd number of axes were mirrored. |
255 | if (xs * ys * zs < 0) { |
256 | for( unsigned int i = 0; i < mesh->mNumFaces; i++) { |
257 | aiFace& face = mesh->mFaces[i]; |
258 | for( unsigned int fi = 0; fi < face.mNumIndices / 2; ++fi) |
259 | std::swap( face.mIndices[fi], face.mIndices[face.mNumIndices - 1 - fi]); |
260 | } |
261 | } |
262 | |
263 | conv_data.meshes->push_back(mesh); |
264 | } |
265 | unsigned int* nind = new unsigned int[out.mNumMeshes*2]; |
266 | |
267 | std::copy(out.mMeshes,out.mMeshes+out.mNumMeshes,nind); |
268 | std::transform(out.mMeshes,out.mMeshes+out.mNumMeshes,nind+out.mNumMeshes, |
269 | [&out](unsigned int n) { return out.mNumMeshes + n; }); |
270 | |
271 | delete[] out.mMeshes; |
272 | out.mMeshes = nind; |
273 | out.mNumMeshes *= 2; |
274 | |
275 | ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Mirror` modifier to `" , |
276 | orig_object.id.name,"`" ); |
277 | } |
278 | |
279 | // ------------------------------------------------------------------------------------------------ |
280 | bool BlenderModifier_Subdivision :: IsActive (const ModifierData& modin) |
281 | { |
282 | return modin.type == ModifierData::eModifierType_Subsurf; |
283 | } |
284 | |
285 | // ------------------------------------------------------------------------------------------------ |
286 | void BlenderModifier_Subdivision :: DoIt(aiNode& out, ConversionData& conv_data, const ElemBase& orig_modifier, |
287 | const Scene& /*in*/, |
288 | const Object& orig_object ) |
289 | { |
290 | // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers() |
291 | const SubsurfModifierData& mir = static_cast<const SubsurfModifierData&>(orig_modifier); |
292 | ai_assert(mir.modifier.type == ModifierData::eModifierType_Subsurf); |
293 | |
294 | Subdivider::Algorithm algo; |
295 | switch (mir.subdivType) |
296 | { |
297 | case SubsurfModifierData::TYPE_CatmullClarke: |
298 | algo = Subdivider::CATMULL_CLARKE; |
299 | break; |
300 | |
301 | case SubsurfModifierData::TYPE_Simple: |
302 | ASSIMP_LOG_WARN("BlendModifier: The `SIMPLE` subdivision algorithm is not currently implemented, using Catmull-Clarke" ); |
303 | algo = Subdivider::CATMULL_CLARKE; |
304 | break; |
305 | |
306 | default: |
307 | ASSIMP_LOG_WARN_F("BlendModifier: Unrecognized subdivision algorithm: " ,mir.subdivType); |
308 | return; |
309 | }; |
310 | |
311 | std::unique_ptr<Subdivider> subd(Subdivider::Create(algo)); |
312 | ai_assert(subd); |
313 | if ( conv_data.meshes->empty() ) { |
314 | return; |
315 | } |
316 | aiMesh** const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes]; |
317 | std::unique_ptr<aiMesh*[]> tempmeshes(new aiMesh*[out.mNumMeshes]()); |
318 | |
319 | subd->Subdivide(meshes,out.mNumMeshes,tempmeshes.get(),std::max( mir.renderLevels, mir.levels ),true); |
320 | std::copy(tempmeshes.get(),tempmeshes.get()+out.mNumMeshes,meshes); |
321 | |
322 | ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Subdivision` modifier to `" , |
323 | orig_object.id.name,"`" ); |
324 | } |
325 | |
326 | #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER |
327 | |