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