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