nuclear@0: /* nuclear@0: Open Asset Import Library (assimp) nuclear@0: ---------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the nuclear@0: following conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: nuclear@0: ---------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file BlenderModifier.cpp nuclear@0: * @brief Implementation of some blender modifiers (i.e subdivision, mirror). nuclear@0: */ nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER nuclear@0: #include "BlenderModifier.h" nuclear@0: #include "SceneCombiner.h" nuclear@0: #include "Subdivision.h" nuclear@0: nuclear@0: #include nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: using namespace Assimp::Blender; nuclear@0: nuclear@0: template BlenderModifier* god() { nuclear@0: return new T(); nuclear@0: } nuclear@0: nuclear@0: // add all available modifiers here nuclear@0: typedef BlenderModifier* (*fpCreateModifier)(); nuclear@0: static const fpCreateModifier creators[] = { nuclear@0: &god, nuclear@0: &god, nuclear@0: nuclear@0: NULL // sentinel nuclear@0: }; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // just testing out some new macros to simplify logging nuclear@0: #define ASSIMP_LOG_WARN_F(string,...)\ nuclear@0: DefaultLogger::get()->warn((Formatter::format(string),__VA_ARGS__)) nuclear@0: nuclear@0: #define ASSIMP_LOG_ERROR_F(string,...)\ nuclear@0: DefaultLogger::get()->error((Formatter::format(string),__VA_ARGS__)) nuclear@0: nuclear@0: #define ASSIMP_LOG_DEBUG_F(string,...)\ nuclear@0: DefaultLogger::get()->debug((Formatter::format(string),__VA_ARGS__)) nuclear@0: nuclear@0: #define ASSIMP_LOG_INFO_F(string,...)\ nuclear@0: DefaultLogger::get()->info((Formatter::format(string),__VA_ARGS__)) nuclear@0: nuclear@0: nuclear@0: #define ASSIMP_LOG_WARN(string)\ nuclear@0: DefaultLogger::get()->warn(string) nuclear@0: nuclear@0: #define ASSIMP_LOG_ERROR(string)\ nuclear@0: DefaultLogger::get()->error(string) nuclear@0: nuclear@0: #define ASSIMP_LOG_DEBUG(string)\ nuclear@0: DefaultLogger::get()->debug(string) nuclear@0: nuclear@0: #define ASSIMP_LOG_INFO(string)\ nuclear@0: DefaultLogger::get()->info(string) nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: struct SharedModifierData : ElemBase nuclear@0: { nuclear@0: ModifierData modifier; nuclear@0: }; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderModifierShowcase::ApplyModifiers(aiNode& out, ConversionData& conv_data, const Scene& in, const Object& orig_object ) nuclear@0: { nuclear@0: size_t cnt = 0u, ful = 0u; nuclear@0: nuclear@0: // NOTE: this cast is potentially unsafe by design, so we need to perform type checks before nuclear@0: // we're allowed to dereference the pointers without risking to crash. We might still be nuclear@0: // invoking UB btw - we're assuming that the ModifierData member of the respective modifier nuclear@0: // structures is at offset sizeof(vftable) with no padding. nuclear@0: const SharedModifierData* cur = boost::static_pointer_cast ( orig_object.modifiers.first.get() ); nuclear@0: for (; cur; cur = boost::static_pointer_cast ( cur->modifier.next.get() ), ++ful) { nuclear@0: ai_assert(cur->dna_type); nuclear@0: nuclear@0: const Structure* s = conv_data.db.dna.Get( cur->dna_type ); nuclear@0: if (!s) { nuclear@0: ASSIMP_LOG_WARN_F("BlendModifier: could not resolve DNA name: ",cur->dna_type); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: // this is a common trait of all XXXMirrorData structures in BlenderDNA nuclear@0: const Field* f = s->Get("modifier"); nuclear@0: if (!f || f->offset != 0) { nuclear@0: ASSIMP_LOG_WARN("BlendModifier: expected a `modifier` member at offset 0"); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: s = conv_data.db.dna.Get( f->type ); nuclear@0: if (!s || s->name != "ModifierData") { nuclear@0: ASSIMP_LOG_WARN("BlendModifier: expected a ModifierData structure as first member"); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: // now, we can be sure that we should be fine to dereference *cur* as nuclear@0: // ModifierData (with the above note). nuclear@0: const ModifierData& dat = cur->modifier; nuclear@0: nuclear@0: const fpCreateModifier* curgod = creators; nuclear@0: std::vector< BlenderModifier* >::iterator curmod = cached_modifiers->begin(), endmod = cached_modifiers->end(); nuclear@0: nuclear@0: for (;*curgod;++curgod,++curmod) { // allocate modifiers on the fly nuclear@0: if (curmod == endmod) { nuclear@0: cached_modifiers->push_back((*curgod)()); nuclear@0: nuclear@0: endmod = cached_modifiers->end(); nuclear@0: curmod = endmod-1; nuclear@0: } nuclear@0: nuclear@0: BlenderModifier* const modifier = *curmod; nuclear@0: if(modifier->IsActive(dat)) { nuclear@0: modifier->DoIt(out,conv_data,*boost::static_pointer_cast(cur),in,orig_object); nuclear@0: cnt++; nuclear@0: nuclear@0: curgod = NULL; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: if (curgod) { nuclear@0: ASSIMP_LOG_WARN_F("Couldn't find a handler for modifier: ",dat.name); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Even though we managed to resolve some or all of the modifiers on this nuclear@0: // object, we still can't say whether our modifier implementations were nuclear@0: // able to fully do their job. nuclear@0: if (ful) { nuclear@0: ASSIMP_LOG_DEBUG_F("BlendModifier: found handlers for ",cnt," of ",ful," modifiers on `",orig_object.id.name, nuclear@0: "`, check log messages above for errors"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: bool BlenderModifier_Mirror :: IsActive (const ModifierData& modin) nuclear@0: { nuclear@0: return modin.type == ModifierData::eModifierType_Mirror; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderModifier_Mirror :: DoIt(aiNode& out, ConversionData& conv_data, const ElemBase& orig_modifier, nuclear@0: const Scene& /*in*/, nuclear@0: const Object& orig_object ) nuclear@0: { nuclear@0: // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers() nuclear@0: const MirrorModifierData& mir = static_cast(orig_modifier); nuclear@0: ai_assert(mir.modifier.type == ModifierData::eModifierType_Mirror); nuclear@0: nuclear@0: conv_data.meshes->reserve(conv_data.meshes->size() + out.mNumMeshes); nuclear@0: nuclear@0: // XXX not entirely correct, mirroring on two axes results in 4 distinct objects in blender ... nuclear@0: nuclear@0: // take all input meshes and clone them nuclear@0: for (unsigned int i = 0; i < out.mNumMeshes; ++i) { nuclear@0: aiMesh* mesh; nuclear@0: SceneCombiner::Copy(&mesh,conv_data.meshes[out.mMeshes[i]]); nuclear@0: nuclear@0: const float xs = mir.flag & MirrorModifierData::Flags_AXIS_X ? -1.f : 1.f; nuclear@0: const float ys = mir.flag & MirrorModifierData::Flags_AXIS_Y ? -1.f : 1.f; nuclear@0: const float zs = mir.flag & MirrorModifierData::Flags_AXIS_Z ? -1.f : 1.f; nuclear@0: nuclear@0: if (mir.mirror_ob) { nuclear@0: const aiVector3D center( mir.mirror_ob->obmat[3][0],mir.mirror_ob->obmat[3][1],mir.mirror_ob->obmat[3][2] ); nuclear@0: for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { nuclear@0: aiVector3D& v = mesh->mVertices[i]; nuclear@0: nuclear@0: v.x = center.x + xs*(center.x - v.x); nuclear@0: v.y = center.y + ys*(center.y - v.y); nuclear@0: v.z = center.z + zs*(center.z - v.z); nuclear@0: } nuclear@0: } nuclear@0: else { nuclear@0: for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { nuclear@0: aiVector3D& v = mesh->mVertices[i]; nuclear@0: v.x *= xs;v.y *= ys;v.z *= zs; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (mesh->mNormals) { nuclear@0: for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { nuclear@0: aiVector3D& v = mesh->mNormals[i]; nuclear@0: v.x *= xs;v.y *= ys;v.z *= zs; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (mesh->mTangents) { nuclear@0: for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { nuclear@0: aiVector3D& v = mesh->mTangents[i]; nuclear@0: v.x *= xs;v.y *= ys;v.z *= zs; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (mesh->mBitangents) { nuclear@0: for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { nuclear@0: aiVector3D& v = mesh->mBitangents[i]; nuclear@0: v.x *= xs;v.y *= ys;v.z *= zs; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: const float us = mir.flag & MirrorModifierData::Flags_MIRROR_U ? -1.f : 1.f; nuclear@0: const float vs = mir.flag & MirrorModifierData::Flags_MIRROR_V ? -1.f : 1.f; nuclear@0: nuclear@0: for (unsigned int n = 0; mesh->HasTextureCoords(n); ++n) { nuclear@0: for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { nuclear@0: aiVector3D& v = mesh->mTextureCoords[n][i]; nuclear@0: v.x *= us;v.y *= vs; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: conv_data.meshes->push_back(mesh); nuclear@0: } nuclear@0: unsigned int* nind = new unsigned int[out.mNumMeshes*2]; nuclear@0: nuclear@0: std::copy(out.mMeshes,out.mMeshes+out.mNumMeshes,nind); nuclear@0: std::transform(out.mMeshes,out.mMeshes+out.mNumMeshes,nind+out.mNumMeshes, nuclear@0: std::bind1st(std::plus< unsigned int >(),out.mNumMeshes)); nuclear@0: nuclear@0: delete[] out.mMeshes; nuclear@0: out.mMeshes = nind; nuclear@0: out.mNumMeshes *= 2; nuclear@0: nuclear@0: ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Mirror` modifier to `", nuclear@0: orig_object.id.name,"`"); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: bool BlenderModifier_Subdivision :: IsActive (const ModifierData& modin) nuclear@0: { nuclear@0: return modin.type == ModifierData::eModifierType_Subsurf; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderModifier_Subdivision :: DoIt(aiNode& out, ConversionData& conv_data, const ElemBase& orig_modifier, nuclear@0: const Scene& /*in*/, nuclear@0: const Object& orig_object ) nuclear@0: { nuclear@0: // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers() nuclear@0: const SubsurfModifierData& mir = static_cast(orig_modifier); nuclear@0: ai_assert(mir.modifier.type == ModifierData::eModifierType_Subsurf); nuclear@0: nuclear@0: Subdivider::Algorithm algo; nuclear@0: switch (mir.subdivType) nuclear@0: { nuclear@0: case SubsurfModifierData::TYPE_CatmullClarke: nuclear@0: algo = Subdivider::CATMULL_CLARKE; nuclear@0: break; nuclear@0: nuclear@0: case SubsurfModifierData::TYPE_Simple: nuclear@0: ASSIMP_LOG_WARN("BlendModifier: The `SIMPLE` subdivision algorithm is not currently implemented, using Catmull-Clarke"); nuclear@0: algo = Subdivider::CATMULL_CLARKE; nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: ASSIMP_LOG_WARN_F("BlendModifier: Unrecognized subdivision algorithm: ",mir.subdivType); nuclear@0: return; nuclear@0: }; nuclear@0: nuclear@0: boost::scoped_ptr subd(Subdivider::Create(algo)); nuclear@0: ai_assert(subd); nuclear@0: nuclear@0: aiMesh** const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes]; nuclear@0: boost::scoped_array tempmeshes(new aiMesh*[out.mNumMeshes]()); nuclear@0: nuclear@0: subd->Subdivide(meshes,out.mNumMeshes,tempmeshes.get(),std::max( mir.renderLevels, mir.levels ),true); nuclear@0: std::copy(tempmeshes.get(),tempmeshes.get()+out.mNumMeshes,meshes); nuclear@0: nuclear@0: ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Subdivision` modifier to `", nuclear@0: orig_object.id.name,"`"); nuclear@0: } nuclear@0: nuclear@0: #endif