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