1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following 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
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF 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
56using namespace Assimp;
57using namespace Assimp::Blender;
58
59template <typename T> BlenderModifier* god() {
60 return new T();
61}
62
63// add all available modifiers here
64typedef BlenderModifier* (*fpCreateModifier)();
65static 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// ------------------------------------------------------------------------------------------------
101struct SharedModifierData : ElemBase
102{
103 ModifierData modifier;
104};
105
106// ------------------------------------------------------------------------------------------------
107void 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// ------------------------------------------------------------------------------------------------
179bool BlenderModifier_Mirror :: IsActive (const ModifierData& modin)
180{
181 return modin.type == ModifierData::eModifierType_Mirror;
182}
183
184// ------------------------------------------------------------------------------------------------
185void 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// ------------------------------------------------------------------------------------------------
280bool BlenderModifier_Subdivision :: IsActive (const ModifierData& modin)
281{
282 return modin.type == ModifierData::eModifierType_Subsurf;
283}
284
285// ------------------------------------------------------------------------------------------------
286void 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