nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: 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 following nuclear@0: 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: /** @file ASELoader.cpp nuclear@0: * @brief Implementation of the ASE importer class nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #include "assimp/config.h" nuclear@0: #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER nuclear@0: nuclear@0: // internal headers nuclear@0: #include "ASELoader.h" nuclear@0: #include "StringComparison.h" nuclear@0: #include "SkeletonMeshBuilder.h" nuclear@0: #include "TargetAnimation.h" nuclear@0: nuclear@0: // utilities nuclear@0: #include "fast_atof.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: using namespace Assimp::ASE; nuclear@0: nuclear@0: static const aiImporterDesc desc = { nuclear@0: "ASE Importer", nuclear@0: "", nuclear@0: "", nuclear@0: "Similar to 3DS but text-encoded", nuclear@0: aiImporterFlags_SupportTextFlavour, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: "ase ask" nuclear@0: }; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: ASEImporter::ASEImporter() nuclear@0: : noSkeletonMesh() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: ASEImporter::~ASEImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the class can handle the format of the given file. nuclear@0: bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const nuclear@0: { nuclear@0: // check file extension nuclear@0: const std::string extension = GetExtension(pFile); nuclear@0: nuclear@0: if( extension == "ase" || extension == "ask") nuclear@0: return true; nuclear@0: nuclear@0: if ((!extension.length() || cs) && pIOHandler) { nuclear@0: const char* tokens[] = {"*3dsmax_asciiexport"}; nuclear@0: return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Loader meta information nuclear@0: const aiImporterDesc* ASEImporter::GetInfo () const nuclear@0: { nuclear@0: return &desc; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Setup configuration options nuclear@0: void ASEImporter::SetupProperties(const Importer* pImp) nuclear@0: { nuclear@0: configRecomputeNormals = (pImp->GetPropertyInteger( nuclear@0: AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS,1) ? true : false); nuclear@0: nuclear@0: noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Imports the given file into the given scene structure. nuclear@0: void ASEImporter::InternReadFile( const std::string& pFile, nuclear@0: aiScene* pScene, IOSystem* pIOHandler) nuclear@0: { nuclear@0: boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); nuclear@0: nuclear@0: // Check whether we can read from the file nuclear@0: if( file.get() == NULL) { nuclear@0: throw DeadlyImportError( "Failed to open ASE file " + pFile + "."); nuclear@0: } nuclear@0: nuclear@0: // Allocate storage and copy the contents of the file to a memory buffer nuclear@0: std::vector mBuffer2; nuclear@0: TextFileToBuffer(file.get(),mBuffer2); nuclear@0: nuclear@0: this->mBuffer = &mBuffer2[0]; nuclear@0: this->pcScene = pScene; nuclear@0: nuclear@0: // ------------------------------------------------------------------ nuclear@0: // Guess the file format by looking at the extension nuclear@0: // ASC is considered to be the older format 110, nuclear@0: // ASE is the actual version 200 (that is currently written by max) nuclear@0: // ------------------------------------------------------------------ nuclear@0: unsigned int defaultFormat; nuclear@0: std::string::size_type s = pFile.length()-1; nuclear@0: switch (pFile.c_str()[s]) { nuclear@0: nuclear@0: case 'C': nuclear@0: case 'c': nuclear@0: defaultFormat = AI_ASE_OLD_FILE_FORMAT; nuclear@0: break; nuclear@0: default: nuclear@0: defaultFormat = AI_ASE_NEW_FILE_FORMAT; nuclear@0: }; nuclear@0: nuclear@0: // Construct an ASE parser and parse the file nuclear@0: ASE::Parser parser(mBuffer,defaultFormat); nuclear@0: mParser = &parser; nuclear@0: mParser->Parse(); nuclear@0: nuclear@0: //------------------------------------------------------------------ nuclear@0: // Check whether we god at least one mesh. If we did - generate nuclear@0: // materials and copy meshes. nuclear@0: // ------------------------------------------------------------------ nuclear@0: if ( !mParser->m_vMeshes.empty()) { nuclear@0: nuclear@0: // If absolutely no material has been loaded from the file nuclear@0: // we need to generate a default material nuclear@0: GenerateDefaultMaterial(); nuclear@0: nuclear@0: // process all meshes nuclear@0: bool tookNormals = false; nuclear@0: std::vector avOutMeshes; nuclear@0: avOutMeshes.reserve(mParser->m_vMeshes.size()*2); nuclear@0: for (std::vector::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) { nuclear@0: if ((*i).bSkip) { nuclear@0: continue; nuclear@0: } nuclear@0: BuildUniqueRepresentation(*i); nuclear@0: nuclear@0: // Need to generate proper vertex normals if necessary nuclear@0: if(GenerateNormals(*i)) { nuclear@0: tookNormals = true; nuclear@0: } nuclear@0: nuclear@0: // Convert all meshes to aiMesh objects nuclear@0: ConvertMeshes(*i,avOutMeshes); nuclear@0: } nuclear@0: if (tookNormals) { nuclear@0: DefaultLogger::get()->debug("ASE: Taking normals from the file. Use " nuclear@0: "the AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS setting if you " nuclear@0: "experience problems"); nuclear@0: } nuclear@0: nuclear@0: // Now build the output mesh list. Remove dummies nuclear@0: pScene->mNumMeshes = (unsigned int)avOutMeshes.size(); nuclear@0: aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; nuclear@0: for (std::vector::const_iterator i = avOutMeshes.begin();i != avOutMeshes.end();++i) { nuclear@0: if (!(*i)->mNumFaces) { nuclear@0: continue; nuclear@0: } nuclear@0: *pp++ = *i; nuclear@0: } nuclear@0: pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes); nuclear@0: nuclear@0: // Build final material indices (remove submaterials and setup nuclear@0: // the final list) nuclear@0: BuildMaterialIndices(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------ nuclear@0: // Copy all scene graph nodes - lights, cameras, dummies and meshes nuclear@0: // into one huge list. nuclear@0: //------------------------------------------------------------------ nuclear@0: std::vector nodes; nuclear@0: nodes.reserve(mParser->m_vMeshes.size() +mParser->m_vLights.size() nuclear@0: + mParser->m_vCameras.size() + mParser->m_vDummies.size()); nuclear@0: nuclear@0: // Lights nuclear@0: for (std::vector::iterator it = mParser->m_vLights.begin(), nuclear@0: end = mParser->m_vLights.end();it != end; ++it)nodes.push_back(&(*it)); nuclear@0: // Cameras nuclear@0: for (std::vector::iterator it = mParser->m_vCameras.begin(), nuclear@0: end = mParser->m_vCameras.end();it != end; ++it)nodes.push_back(&(*it)); nuclear@0: // Meshes nuclear@0: for (std::vector::iterator it = mParser->m_vMeshes.begin(), nuclear@0: end = mParser->m_vMeshes.end();it != end; ++it)nodes.push_back(&(*it)); nuclear@0: // Dummies nuclear@0: for (std::vector::iterator it = mParser->m_vDummies.begin(), nuclear@0: end = mParser->m_vDummies.end();it != end; ++it)nodes.push_back(&(*it)); nuclear@0: nuclear@0: // build the final node graph nuclear@0: BuildNodes(nodes); nuclear@0: nuclear@0: // build output animations nuclear@0: BuildAnimations(nodes); nuclear@0: nuclear@0: // build output cameras nuclear@0: BuildCameras(); nuclear@0: nuclear@0: // build output lights nuclear@0: BuildLights(); nuclear@0: nuclear@0: // ------------------------------------------------------------------ nuclear@0: // If we have no meshes use the SkeletonMeshBuilder helper class nuclear@0: // to build a mesh for the animation skeleton nuclear@0: // FIXME: very strange results nuclear@0: // ------------------------------------------------------------------ nuclear@0: if (!pScene->mNumMeshes) { nuclear@0: pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; nuclear@0: if (!noSkeletonMesh) { nuclear@0: SkeletonMeshBuilder skeleton(pScene); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void ASEImporter::GenerateDefaultMaterial() nuclear@0: { nuclear@0: ai_assert(NULL != mParser); nuclear@0: nuclear@0: bool bHas = false; nuclear@0: for (std::vector::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) { nuclear@0: if ((*i).bSkip)continue; nuclear@0: if (ASE::Face::DEFAULT_MATINDEX == (*i).iMaterialIndex) { nuclear@0: (*i).iMaterialIndex = (unsigned int)mParser->m_vMaterials.size(); nuclear@0: bHas = true; nuclear@0: } nuclear@0: } nuclear@0: if (bHas || mParser->m_vMaterials.empty()) { nuclear@0: // add a simple material without submaterials to the parser's list nuclear@0: mParser->m_vMaterials.push_back ( ASE::Material() ); nuclear@0: ASE::Material& mat = mParser->m_vMaterials.back(); nuclear@0: nuclear@0: mat.mDiffuse = aiColor3D(0.6f,0.6f,0.6f); nuclear@0: mat.mSpecular = aiColor3D(1.0f,1.0f,1.0f); nuclear@0: mat.mAmbient = aiColor3D(0.05f,0.05f,0.05f); nuclear@0: mat.mShading = Discreet3DS::Gouraud; nuclear@0: mat.mName = AI_DEFAULT_MATERIAL_NAME; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void ASEImporter::BuildAnimations(const std::vector& nodes) nuclear@0: { nuclear@0: // check whether we have at least one mesh which has animations nuclear@0: std::vector::const_iterator i = nodes.begin(); nuclear@0: unsigned int iNum = 0; nuclear@0: for (;i != nodes.end();++i) { nuclear@0: nuclear@0: // TODO: Implement Bezier & TCB support nuclear@0: if ((*i)->mAnim.mPositionType != ASE::Animation::TRACK) { nuclear@0: DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. " nuclear@0: "This is not supported."); nuclear@0: } nuclear@0: if ((*i)->mAnim.mRotationType != ASE::Animation::TRACK) { nuclear@0: DefaultLogger::get()->warn("ASE: Rotation controller uses Bezier/TCB keys. " nuclear@0: "This is not supported."); nuclear@0: } nuclear@0: if ((*i)->mAnim.mScalingType != ASE::Animation::TRACK) { nuclear@0: DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. " nuclear@0: "This is not supported."); nuclear@0: } nuclear@0: nuclear@0: // We compare against 1 here - firstly one key is not nuclear@0: // really an animation and secondly MAX writes dummies nuclear@0: // that represent the node transformation. nuclear@0: if ((*i)->mAnim.akeyPositions.size()>1 || (*i)->mAnim.akeyRotations.size()>1 || (*i)->mAnim.akeyScaling.size()>1){ nuclear@0: ++iNum; nuclear@0: } nuclear@0: if ((*i)->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( (*i)->mTargetPosition.x )) { nuclear@0: ++iNum; nuclear@0: } nuclear@0: } nuclear@0: if (iNum) { nuclear@0: // Generate a new animation channel and setup everything for it nuclear@0: pcScene->mNumAnimations = 1; nuclear@0: pcScene->mAnimations = new aiAnimation*[1]; nuclear@0: aiAnimation* pcAnim = pcScene->mAnimations[0] = new aiAnimation(); nuclear@0: pcAnim->mNumChannels = iNum; nuclear@0: pcAnim->mChannels = new aiNodeAnim*[iNum]; nuclear@0: pcAnim->mTicksPerSecond = mParser->iFrameSpeed * mParser->iTicksPerFrame; nuclear@0: nuclear@0: iNum = 0; nuclear@0: nuclear@0: // Now iterate through all meshes and collect all data we can find nuclear@0: for (i = nodes.begin();i != nodes.end();++i) { nuclear@0: nuclear@0: ASE::BaseNode* me = *i; nuclear@0: if ( me->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( me->mTargetPosition.x )) { nuclear@0: // Generate an extra channel for the camera/light target. nuclear@0: // BuildNodes() does also generate an extra node, named nuclear@0: // .Target. nuclear@0: aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); nuclear@0: nd->mNodeName.Set(me->mName + ".Target"); nuclear@0: nuclear@0: // If there is no input position channel we will need nuclear@0: // to supply the default position from the node's nuclear@0: // local transformation matrix. nuclear@0: /*TargetAnimationHelper helper; nuclear@0: if (me->mAnim.akeyPositions.empty()) nuclear@0: { nuclear@0: aiMatrix4x4& mat = (*i)->mTransform; nuclear@0: helper.SetFixedMainAnimationChannel(aiVector3D( nuclear@0: mat.a4, mat.b4, mat.c4)); nuclear@0: } nuclear@0: else helper.SetMainAnimationChannel (&me->mAnim.akeyPositions); nuclear@0: helper.SetTargetAnimationChannel (&me->mTargetAnim.akeyPositions); nuclear@0: nuclear@0: helper.Process(&me->mTargetAnim.akeyPositions);*/ nuclear@0: nuclear@0: // Allocate the key array and fill it nuclear@0: nd->mNumPositionKeys = (unsigned int) me->mTargetAnim.akeyPositions.size(); nuclear@0: nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; nuclear@0: nuclear@0: ::memcpy(nd->mPositionKeys,&me->mTargetAnim.akeyPositions[0], nuclear@0: nd->mNumPositionKeys * sizeof(aiVectorKey)); nuclear@0: } nuclear@0: nuclear@0: if (me->mAnim.akeyPositions.size() > 1 || me->mAnim.akeyRotations.size() > 1 || me->mAnim.akeyScaling.size() > 1) { nuclear@0: // Begin a new node animation channel for this node nuclear@0: aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); nuclear@0: nd->mNodeName.Set(me->mName); nuclear@0: nuclear@0: // copy position keys nuclear@0: if (me->mAnim.akeyPositions.size() > 1 ) nuclear@0: { nuclear@0: // Allocate the key array and fill it nuclear@0: nd->mNumPositionKeys = (unsigned int) me->mAnim.akeyPositions.size(); nuclear@0: nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; nuclear@0: nuclear@0: ::memcpy(nd->mPositionKeys,&me->mAnim.akeyPositions[0], nuclear@0: nd->mNumPositionKeys * sizeof(aiVectorKey)); nuclear@0: } nuclear@0: // copy rotation keys nuclear@0: if (me->mAnim.akeyRotations.size() > 1 ) { nuclear@0: // Allocate the key array and fill it nuclear@0: nd->mNumRotationKeys = (unsigned int) me->mAnim.akeyRotations.size(); nuclear@0: nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys]; nuclear@0: nuclear@0: // -------------------------------------------------------------------- nuclear@0: // Rotation keys are offsets to the previous keys. nuclear@0: // We have the quaternion representations of all nuclear@0: // of them, so we just need to concatenate all nuclear@0: // (unit-length) quaternions to get the absolute nuclear@0: // rotations. nuclear@0: // Rotation keys are ABSOLUTE for older files nuclear@0: // -------------------------------------------------------------------- nuclear@0: nuclear@0: aiQuaternion cur; nuclear@0: for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) { nuclear@0: aiQuatKey q = me->mAnim.akeyRotations[a]; nuclear@0: nuclear@0: if (mParser->iFileFormat > 110) { nuclear@0: cur = (a ? cur*q.mValue : q.mValue); nuclear@0: q.mValue = cur.Normalize(); nuclear@0: } nuclear@0: nd->mRotationKeys[a] = q; nuclear@0: nuclear@0: // need this to get to Assimp quaternion conventions nuclear@0: nd->mRotationKeys[a].mValue.w *= -1.f; nuclear@0: } nuclear@0: } nuclear@0: // copy scaling keys nuclear@0: if (me->mAnim.akeyScaling.size() > 1 ) { nuclear@0: // Allocate the key array and fill it nuclear@0: nd->mNumScalingKeys = (unsigned int) me->mAnim.akeyScaling.size(); nuclear@0: nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys]; nuclear@0: nuclear@0: ::memcpy(nd->mScalingKeys,&me->mAnim.akeyScaling[0], nuclear@0: nd->mNumScalingKeys * sizeof(aiVectorKey)); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Build output cameras nuclear@0: void ASEImporter::BuildCameras() nuclear@0: { nuclear@0: if (!mParser->m_vCameras.empty()) { nuclear@0: pcScene->mNumCameras = (unsigned int)mParser->m_vCameras.size(); nuclear@0: pcScene->mCameras = new aiCamera*[pcScene->mNumCameras]; nuclear@0: nuclear@0: for (unsigned int i = 0; i < pcScene->mNumCameras;++i) { nuclear@0: aiCamera* out = pcScene->mCameras[i] = new aiCamera(); nuclear@0: ASE::Camera& in = mParser->m_vCameras[i]; nuclear@0: nuclear@0: // copy members nuclear@0: out->mClipPlaneFar = in.mFar; nuclear@0: out->mClipPlaneNear = (in.mNear ? in.mNear : 0.1f); nuclear@0: out->mHorizontalFOV = in.mFOV; nuclear@0: nuclear@0: out->mName.Set(in.mName); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Build output lights nuclear@0: void ASEImporter::BuildLights() nuclear@0: { nuclear@0: if (!mParser->m_vLights.empty()) { nuclear@0: pcScene->mNumLights = (unsigned int)mParser->m_vLights.size(); nuclear@0: pcScene->mLights = new aiLight*[pcScene->mNumLights]; nuclear@0: nuclear@0: for (unsigned int i = 0; i < pcScene->mNumLights;++i) { nuclear@0: aiLight* out = pcScene->mLights[i] = new aiLight(); nuclear@0: ASE::Light& in = mParser->m_vLights[i]; nuclear@0: nuclear@0: // The direction is encoded in the transformation matrix of the node. nuclear@0: // In 3DS MAX the light source points into negative Z direction if nuclear@0: // the node transformation is the identity. nuclear@0: out->mDirection = aiVector3D(0.f,0.f,-1.f); nuclear@0: nuclear@0: out->mName.Set(in.mName); nuclear@0: switch (in.mLightType) nuclear@0: { nuclear@0: case ASE::Light::TARGET: nuclear@0: out->mType = aiLightSource_SPOT; nuclear@0: out->mAngleInnerCone = AI_DEG_TO_RAD(in.mAngle); nuclear@0: out->mAngleOuterCone = (in.mFalloff ? AI_DEG_TO_RAD(in.mFalloff) : out->mAngleInnerCone); nuclear@0: break; nuclear@0: nuclear@0: case ASE::Light::DIRECTIONAL: nuclear@0: out->mType = aiLightSource_DIRECTIONAL; nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: //case ASE::Light::OMNI: nuclear@0: out->mType = aiLightSource_POINT; nuclear@0: break; nuclear@0: }; nuclear@0: out->mColorDiffuse = out->mColorSpecular = in.mColor * in.mIntensity; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void ASEImporter::AddNodes(const std::vector& nodes, nuclear@0: aiNode* pcParent,const char* szName) nuclear@0: { nuclear@0: aiMatrix4x4 m; nuclear@0: AddNodes(nodes,pcParent,szName,m); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Add meshes to a given node nuclear@0: void ASEImporter::AddMeshes(const ASE::BaseNode* snode,aiNode* node) nuclear@0: { nuclear@0: for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) { nuclear@0: // Get the name of the mesh (the mesh instance has been temporarily stored in the third vertex color) nuclear@0: const aiMesh* pcMesh = pcScene->mMeshes[i]; nuclear@0: const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2]; nuclear@0: nuclear@0: if (mesh == snode) { nuclear@0: ++node->mNumMeshes; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if(node->mNumMeshes) { nuclear@0: node->mMeshes = new unsigned int[node->mNumMeshes]; nuclear@0: for (unsigned int i = 0, p = 0; i < pcScene->mNumMeshes;++i) { nuclear@0: nuclear@0: const aiMesh* pcMesh = pcScene->mMeshes[i]; nuclear@0: const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2]; nuclear@0: if (mesh == snode) { nuclear@0: node->mMeshes[p++] = i; nuclear@0: nuclear@0: // Transform all vertices of the mesh back into their local space -> nuclear@0: // at the moment they are pretransformed nuclear@0: aiMatrix4x4 m = mesh->mTransform; nuclear@0: m.Inverse(); nuclear@0: nuclear@0: aiVector3D* pvCurPtr = pcMesh->mVertices; nuclear@0: const aiVector3D* pvEndPtr = pvCurPtr + pcMesh->mNumVertices; nuclear@0: while (pvCurPtr != pvEndPtr) { nuclear@0: *pvCurPtr = m * (*pvCurPtr); nuclear@0: pvCurPtr++; nuclear@0: } nuclear@0: nuclear@0: // Do the same for the normal vectors, if we have them. nuclear@0: // As always, inverse transpose. nuclear@0: if (pcMesh->mNormals) { nuclear@0: aiMatrix3x3 m3 = aiMatrix3x3( mesh->mTransform ); nuclear@0: m3.Transpose(); nuclear@0: nuclear@0: pvCurPtr = pcMesh->mNormals; nuclear@0: pvEndPtr = pvCurPtr + pcMesh->mNumVertices; nuclear@0: while (pvCurPtr != pvEndPtr) { nuclear@0: *pvCurPtr = m3 * (*pvCurPtr); nuclear@0: pvCurPtr++; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Add child nodes to a given parent node nuclear@0: void ASEImporter::AddNodes (const std::vector& nodes, nuclear@0: aiNode* pcParent, const char* szName, nuclear@0: const aiMatrix4x4& mat) nuclear@0: { nuclear@0: const size_t len = szName ? ::strlen(szName) : 0; nuclear@0: ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS); nuclear@0: nuclear@0: // Receives child nodes for the pcParent node nuclear@0: std::vector apcNodes; nuclear@0: nuclear@0: // Now iterate through all nodes in the scene and search for one nuclear@0: // which has *us* as parent. nuclear@0: for (std::vector::const_iterator it = nodes.begin(), end = nodes.end(); it != end; ++it) { nuclear@0: const BaseNode* snode = *it; nuclear@0: if (szName) { nuclear@0: if (len != snode->mParent.length() || ::strcmp(szName,snode->mParent.c_str())) nuclear@0: continue; nuclear@0: } nuclear@0: else if (snode->mParent.length()) nuclear@0: continue; nuclear@0: nuclear@0: (*it)->mProcessed = true; nuclear@0: nuclear@0: // Allocate a new node and add it to the output data structure nuclear@0: apcNodes.push_back(new aiNode()); nuclear@0: aiNode* node = apcNodes.back(); nuclear@0: nuclear@0: node->mName.Set((snode->mName.length() ? snode->mName.c_str() : "Unnamed_Node")); nuclear@0: node->mParent = pcParent; nuclear@0: nuclear@0: // Setup the transformation matrix of the node nuclear@0: aiMatrix4x4 mParentAdjust = mat; nuclear@0: mParentAdjust.Inverse(); nuclear@0: node->mTransformation = mParentAdjust*snode->mTransform; nuclear@0: nuclear@0: // Add sub nodes - prevent stack overflow due to recursive parenting nuclear@0: if (node->mName != node->mParent->mName) { nuclear@0: AddNodes(nodes,node,node->mName.data,snode->mTransform); nuclear@0: } nuclear@0: nuclear@0: // Further processing depends on the type of the node nuclear@0: if (snode->mType == ASE::BaseNode::Mesh) { nuclear@0: // If the type of this node is "Mesh" we need to search nuclear@0: // the list of output meshes in the data structure for nuclear@0: // all those that belonged to this node once. This is nuclear@0: // slightly inconvinient here and a better solution should nuclear@0: // be used when this code is refactored next. nuclear@0: AddMeshes(snode,node); nuclear@0: } nuclear@0: else if (is_not_qnan( snode->mTargetPosition.x )) { nuclear@0: // If this is a target camera or light we generate a small nuclear@0: // child node which marks the position of the camera nuclear@0: // target (the direction information is contained in *this* nuclear@0: // node's animation track but the exact target position nuclear@0: // would be lost otherwise) nuclear@0: if (!node->mNumChildren) { nuclear@0: node->mChildren = new aiNode*[1]; nuclear@0: } nuclear@0: nuclear@0: aiNode* nd = new aiNode(); nuclear@0: nuclear@0: nd->mName.Set ( snode->mName + ".Target" ); nuclear@0: nuclear@0: nd->mTransformation.a4 = snode->mTargetPosition.x - snode->mTransform.a4; nuclear@0: nd->mTransformation.b4 = snode->mTargetPosition.y - snode->mTransform.b4; nuclear@0: nd->mTransformation.c4 = snode->mTargetPosition.z - snode->mTransform.c4; nuclear@0: nuclear@0: nd->mParent = node; nuclear@0: nuclear@0: // The .Target node is always the first child node nuclear@0: for (unsigned int m = 0; m < node->mNumChildren;++m) nuclear@0: node->mChildren[m+1] = node->mChildren[m]; nuclear@0: nuclear@0: node->mChildren[0] = nd; nuclear@0: node->mNumChildren++; nuclear@0: nuclear@0: // What we did is so great, it is at least worth a debug message nuclear@0: DefaultLogger::get()->debug("ASE: Generating separate target node ("+snode->mName+")"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Allocate enough space for the child nodes nuclear@0: // We allocate one slot more in case this is a target camera/light nuclear@0: pcParent->mNumChildren = (unsigned int)apcNodes.size(); nuclear@0: if (pcParent->mNumChildren) { nuclear@0: pcParent->mChildren = new aiNode*[apcNodes.size()+1 /* PLUS ONE !!! */]; nuclear@0: nuclear@0: // now build all nodes for our nice new children nuclear@0: for (unsigned int p = 0; p < apcNodes.size();++p) nuclear@0: pcParent->mChildren[p] = apcNodes[p]; nuclear@0: } nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Build the output node graph nuclear@0: void ASEImporter::BuildNodes(std::vector& nodes) { nuclear@0: ai_assert(NULL != pcScene); nuclear@0: nuclear@0: // allocate the one and only root node nuclear@0: aiNode* root = pcScene->mRootNode = new aiNode(); nuclear@0: root->mName.Set(""); nuclear@0: nuclear@0: // Setup the coordinate system transformation nuclear@0: pcScene->mRootNode->mNumChildren = 1; nuclear@0: pcScene->mRootNode->mChildren = new aiNode*[1]; nuclear@0: aiNode* ch = pcScene->mRootNode->mChildren[0] = new aiNode(); nuclear@0: ch->mParent = root; nuclear@0: nuclear@0: // Change the transformation matrix of all nodes nuclear@0: for (std::vector::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) { nuclear@0: aiMatrix4x4& m = (*it)->mTransform; nuclear@0: m.Transpose(); // row-order vs column-order nuclear@0: } nuclear@0: nuclear@0: // add all nodes nuclear@0: AddNodes(nodes,ch,NULL); nuclear@0: nuclear@0: // now iterate through al nodes and find those that have not yet nuclear@0: // been added to the nodegraph (= their parent could not be recognized) nuclear@0: std::vector aiList; nuclear@0: for (std::vector::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) { nuclear@0: if ((*it)->mProcessed) { nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: // check whether our parent is known nuclear@0: bool bKnowParent = false; nuclear@0: nuclear@0: // search the list another time, starting *here* and try to find out whether nuclear@0: // there is a node that references *us* as a parent nuclear@0: for (std::vector::const_iterator it2 = nodes.begin();it2 != end; ++it2) { nuclear@0: if (it2 == it) { nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: if ((*it2)->mName == (*it)->mParent) { nuclear@0: bKnowParent = true; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: if (!bKnowParent) { nuclear@0: aiList.push_back(*it); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Are there ane orphaned nodes? nuclear@0: if (!aiList.empty()) { nuclear@0: std::vector apcNodes; nuclear@0: apcNodes.reserve(aiList.size() + pcScene->mRootNode->mNumChildren); nuclear@0: nuclear@0: for (unsigned int i = 0; i < pcScene->mRootNode->mNumChildren;++i) nuclear@0: apcNodes.push_back(pcScene->mRootNode->mChildren[i]); nuclear@0: nuclear@0: delete[] pcScene->mRootNode->mChildren; nuclear@0: for (std::vector::/*const_*/iterator i = aiList.begin();i != aiList.end();++i) { nuclear@0: const ASE::BaseNode* src = *i; nuclear@0: nuclear@0: // The parent is not known, so we can assume that we must add nuclear@0: // this node to the root node of the whole scene nuclear@0: aiNode* pcNode = new aiNode(); nuclear@0: pcNode->mParent = pcScene->mRootNode; nuclear@0: pcNode->mName.Set(src->mName); nuclear@0: AddMeshes(src,pcNode); nuclear@0: AddNodes(nodes,pcNode,pcNode->mName.data); nuclear@0: apcNodes.push_back(pcNode); nuclear@0: } nuclear@0: nuclear@0: // Regenerate our output array nuclear@0: pcScene->mRootNode->mChildren = new aiNode*[apcNodes.size()]; nuclear@0: for (unsigned int i = 0; i < apcNodes.size();++i) nuclear@0: pcScene->mRootNode->mChildren[i] = apcNodes[i]; nuclear@0: nuclear@0: pcScene->mRootNode->mNumChildren = (unsigned int)apcNodes.size(); nuclear@0: } nuclear@0: nuclear@0: // Reset the third color set to NULL - we used this field to store a temporary pointer nuclear@0: for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) nuclear@0: pcScene->mMeshes[i]->mColors[2] = NULL; nuclear@0: nuclear@0: // The root node should not have at least one child or the file is valid nuclear@0: if (!pcScene->mRootNode->mNumChildren) { nuclear@0: throw DeadlyImportError("ASE: No nodes loaded. The file is either empty or corrupt"); nuclear@0: } nuclear@0: nuclear@0: // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system nuclear@0: pcScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f, nuclear@0: 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Convert the imported data to the internal verbose representation nuclear@0: void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) { nuclear@0: // allocate output storage nuclear@0: std::vector mPositions; nuclear@0: std::vector amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; nuclear@0: std::vector mVertexColors; nuclear@0: std::vector mNormals; nuclear@0: std::vector mBoneVertices; nuclear@0: nuclear@0: unsigned int iSize = (unsigned int)mesh.mFaces.size() * 3; nuclear@0: mPositions.resize(iSize); nuclear@0: nuclear@0: // optional texture coordinates nuclear@0: for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) { nuclear@0: if (!mesh.amTexCoords[i].empty()) { nuclear@0: amTexCoords[i].resize(iSize); nuclear@0: } nuclear@0: } nuclear@0: // optional vertex colors nuclear@0: if (!mesh.mVertexColors.empty()) { nuclear@0: mVertexColors.resize(iSize); nuclear@0: } nuclear@0: nuclear@0: // optional vertex normals (vertex normals can simply be copied) nuclear@0: if (!mesh.mNormals.empty()) { nuclear@0: mNormals.resize(iSize); nuclear@0: } nuclear@0: // bone vertices. There is no need to change the bone list nuclear@0: if (!mesh.mBoneVertices.empty()) { nuclear@0: mBoneVertices.resize(iSize); nuclear@0: } nuclear@0: nuclear@0: // iterate through all faces in the mesh nuclear@0: unsigned int iCurrent = 0, fi = 0; nuclear@0: for (std::vector::iterator i = mesh.mFaces.begin();i != mesh.mFaces.end();++i,++fi) { nuclear@0: for (unsigned int n = 0; n < 3;++n,++iCurrent) nuclear@0: { nuclear@0: mPositions[iCurrent] = mesh.mPositions[(*i).mIndices[n]]; nuclear@0: nuclear@0: // add texture coordinates nuclear@0: for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { nuclear@0: if (mesh.amTexCoords[c].empty())break; nuclear@0: amTexCoords[c][iCurrent] = mesh.amTexCoords[c][(*i).amUVIndices[c][n]]; nuclear@0: } nuclear@0: // add vertex colors nuclear@0: if (!mesh.mVertexColors.empty()) { nuclear@0: mVertexColors[iCurrent] = mesh.mVertexColors[(*i).mColorIndices[n]]; nuclear@0: } nuclear@0: // add normal vectors nuclear@0: if (!mesh.mNormals.empty()) { nuclear@0: mNormals[iCurrent] = mesh.mNormals[fi*3+n]; nuclear@0: mNormals[iCurrent].Normalize(); nuclear@0: } nuclear@0: nuclear@0: // handle bone vertices nuclear@0: if ((*i).mIndices[n] < mesh.mBoneVertices.size()) { nuclear@0: // (sometimes this will cause bone verts to be duplicated nuclear@0: // however, I' quite sure Schrompf' JoinVerticesStep nuclear@0: // will fix that again ...) nuclear@0: mBoneVertices[iCurrent] = mesh.mBoneVertices[(*i).mIndices[n]]; nuclear@0: } nuclear@0: (*i).mIndices[n] = iCurrent; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // replace the old arrays nuclear@0: mesh.mNormals = mNormals; nuclear@0: mesh.mPositions = mPositions; nuclear@0: mesh.mVertexColors = mVertexColors; nuclear@0: nuclear@0: for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) nuclear@0: mesh.amTexCoords[c] = amTexCoords[c]; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Copy a texture from the ASE structs to the output material nuclear@0: void CopyASETexture(aiMaterial& mat, ASE::Texture& texture, aiTextureType type) nuclear@0: { nuclear@0: // Setup the texture name nuclear@0: aiString tex; nuclear@0: tex.Set( texture.mMapName); nuclear@0: mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0)); nuclear@0: nuclear@0: // Setup the texture blend factor nuclear@0: if (is_not_qnan(texture.mTextureBlend)) nuclear@0: mat.AddProperty( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0)); nuclear@0: nuclear@0: // Setup texture UV transformations nuclear@0: mat.AddProperty(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0)); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Convert from ASE material to output material nuclear@0: void ASEImporter::ConvertMaterial(ASE::Material& mat) nuclear@0: { nuclear@0: // LARGE TODO: Much code her is copied from 3DS ... join them maybe? nuclear@0: nuclear@0: // Allocate the output material nuclear@0: mat.pcInstance = new aiMaterial(); nuclear@0: nuclear@0: // At first add the base ambient color of the nuclear@0: // scene to the material nuclear@0: mat.mAmbient.r += mParser->m_clrAmbient.r; nuclear@0: mat.mAmbient.g += mParser->m_clrAmbient.g; nuclear@0: mat.mAmbient.b += mParser->m_clrAmbient.b; nuclear@0: nuclear@0: aiString name; nuclear@0: name.Set( mat.mName); nuclear@0: mat.pcInstance->AddProperty( &name, AI_MATKEY_NAME); nuclear@0: nuclear@0: // material colors nuclear@0: mat.pcInstance->AddProperty( &mat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); nuclear@0: mat.pcInstance->AddProperty( &mat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); nuclear@0: mat.pcInstance->AddProperty( &mat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); nuclear@0: mat.pcInstance->AddProperty( &mat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); nuclear@0: nuclear@0: // shininess nuclear@0: if (0.0f != mat.mSpecularExponent && 0.0f != mat.mShininessStrength) nuclear@0: { nuclear@0: mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS); nuclear@0: mat.pcInstance->AddProperty( &mat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH); nuclear@0: } nuclear@0: // If there is no shininess, we can disable phong lighting nuclear@0: else if (D3DS::Discreet3DS::Metal == mat.mShading || nuclear@0: D3DS::Discreet3DS::Phong == mat.mShading || nuclear@0: D3DS::Discreet3DS::Blinn == mat.mShading) nuclear@0: { nuclear@0: mat.mShading = D3DS::Discreet3DS::Gouraud; nuclear@0: } nuclear@0: nuclear@0: // opacity nuclear@0: mat.pcInstance->AddProperty( &mat.mTransparency,1,AI_MATKEY_OPACITY); nuclear@0: nuclear@0: // Two sided rendering? nuclear@0: if (mat.mTwoSided) nuclear@0: { nuclear@0: int i = 1; nuclear@0: mat.pcInstance->AddProperty(&i,1,AI_MATKEY_TWOSIDED); nuclear@0: } nuclear@0: nuclear@0: // shading mode nuclear@0: aiShadingMode eShading = aiShadingMode_NoShading; nuclear@0: switch (mat.mShading) nuclear@0: { nuclear@0: case D3DS::Discreet3DS::Flat: nuclear@0: eShading = aiShadingMode_Flat; break; nuclear@0: case D3DS::Discreet3DS::Phong : nuclear@0: eShading = aiShadingMode_Phong; break; nuclear@0: case D3DS::Discreet3DS::Blinn : nuclear@0: eShading = aiShadingMode_Blinn; break; nuclear@0: nuclear@0: // I don't know what "Wire" shading should be, nuclear@0: // assume it is simple lambertian diffuse (L dot N) shading nuclear@0: case D3DS::Discreet3DS::Wire: nuclear@0: { nuclear@0: // set the wireframe flag nuclear@0: unsigned int iWire = 1; nuclear@0: mat.pcInstance->AddProperty( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME); nuclear@0: } nuclear@0: case D3DS::Discreet3DS::Gouraud: nuclear@0: eShading = aiShadingMode_Gouraud; break; nuclear@0: case D3DS::Discreet3DS::Metal : nuclear@0: eShading = aiShadingMode_CookTorrance; break; nuclear@0: } nuclear@0: mat.pcInstance->AddProperty( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL); nuclear@0: nuclear@0: // DIFFUSE texture nuclear@0: if( mat.sTexDiffuse.mMapName.length() > 0) nuclear@0: CopyASETexture(*mat.pcInstance,mat.sTexDiffuse, aiTextureType_DIFFUSE); nuclear@0: nuclear@0: // SPECULAR texture nuclear@0: if( mat.sTexSpecular.mMapName.length() > 0) nuclear@0: CopyASETexture(*mat.pcInstance,mat.sTexSpecular, aiTextureType_SPECULAR); nuclear@0: nuclear@0: // AMBIENT texture nuclear@0: if( mat.sTexAmbient.mMapName.length() > 0) nuclear@0: CopyASETexture(*mat.pcInstance,mat.sTexAmbient, aiTextureType_AMBIENT); nuclear@0: nuclear@0: // OPACITY texture nuclear@0: if( mat.sTexOpacity.mMapName.length() > 0) nuclear@0: CopyASETexture(*mat.pcInstance,mat.sTexOpacity, aiTextureType_OPACITY); nuclear@0: nuclear@0: // EMISSIVE texture nuclear@0: if( mat.sTexEmissive.mMapName.length() > 0) nuclear@0: CopyASETexture(*mat.pcInstance,mat.sTexEmissive, aiTextureType_EMISSIVE); nuclear@0: nuclear@0: // BUMP texture nuclear@0: if( mat.sTexBump.mMapName.length() > 0) nuclear@0: CopyASETexture(*mat.pcInstance,mat.sTexBump, aiTextureType_HEIGHT); nuclear@0: nuclear@0: // SHININESS texture nuclear@0: if( mat.sTexShininess.mMapName.length() > 0) nuclear@0: CopyASETexture(*mat.pcInstance,mat.sTexShininess, aiTextureType_SHININESS); nuclear@0: nuclear@0: // store the name of the material itself, too nuclear@0: if( mat.mName.length() > 0) { nuclear@0: aiString tex;tex.Set( mat.mName); nuclear@0: mat.pcInstance->AddProperty( &tex, AI_MATKEY_NAME); nuclear@0: } nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Build output meshes nuclear@0: void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector& avOutMeshes) nuclear@0: { nuclear@0: // validate the material index of the mesh nuclear@0: if (mesh.iMaterialIndex >= mParser->m_vMaterials.size()) { nuclear@0: mesh.iMaterialIndex = (unsigned int)mParser->m_vMaterials.size()-1; nuclear@0: DefaultLogger::get()->warn("Material index is out of range"); nuclear@0: } nuclear@0: nuclear@0: // If the material the mesh is assigned to is consisting of submeshes, split it nuclear@0: if (!mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials.empty()) { nuclear@0: std::vector vSubMaterials = mParser-> nuclear@0: m_vMaterials[mesh.iMaterialIndex].avSubMaterials; nuclear@0: nuclear@0: std::vector* aiSplit = new std::vector[vSubMaterials.size()]; nuclear@0: nuclear@0: // build a list of all faces per submaterial nuclear@0: for (unsigned int i = 0; i < mesh.mFaces.size();++i) { nuclear@0: // check range nuclear@0: if (mesh.mFaces[i].iMaterial >= vSubMaterials.size()) { nuclear@0: DefaultLogger::get()->warn("Submaterial index is out of range"); nuclear@0: nuclear@0: // use the last material instead nuclear@0: aiSplit[vSubMaterials.size()-1].push_back(i); nuclear@0: } nuclear@0: else aiSplit[mesh.mFaces[i].iMaterial].push_back(i); nuclear@0: } nuclear@0: nuclear@0: // now generate submeshes nuclear@0: for (unsigned int p = 0; p < vSubMaterials.size();++p) { nuclear@0: if (!aiSplit[p].empty()) { nuclear@0: nuclear@0: aiMesh* p_pcOut = new aiMesh(); nuclear@0: p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; nuclear@0: nuclear@0: // let the sub material index nuclear@0: p_pcOut->mMaterialIndex = p; nuclear@0: nuclear@0: // we will need this material nuclear@0: mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials[p].bNeed = true; nuclear@0: nuclear@0: // store the real index here ... color channel 3 nuclear@0: p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex; nuclear@0: nuclear@0: // store a pointer to the mesh in color channel 2 nuclear@0: p_pcOut->mColors[2] = (aiColor4D*) &mesh; nuclear@0: avOutMeshes.push_back(p_pcOut); nuclear@0: nuclear@0: // convert vertices nuclear@0: p_pcOut->mNumVertices = (unsigned int)aiSplit[p].size()*3; nuclear@0: p_pcOut->mNumFaces = (unsigned int)aiSplit[p].size(); nuclear@0: nuclear@0: // receive output vertex weights nuclear@0: std::vector > *avOutputBones = NULL; nuclear@0: if (!mesh.mBones.empty()) { nuclear@0: avOutputBones = new std::vector >[mesh.mBones.size()]; nuclear@0: } nuclear@0: nuclear@0: // allocate enough storage for faces nuclear@0: p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; nuclear@0: nuclear@0: unsigned int iBase = 0,iIndex; nuclear@0: if (p_pcOut->mNumVertices) { nuclear@0: p_pcOut->mVertices = new aiVector3D[p_pcOut->mNumVertices]; nuclear@0: p_pcOut->mNormals = new aiVector3D[p_pcOut->mNumVertices]; nuclear@0: for (unsigned int q = 0; q < aiSplit[p].size();++q) { nuclear@0: nuclear@0: iIndex = aiSplit[p][q]; nuclear@0: nuclear@0: p_pcOut->mFaces[q].mIndices = new unsigned int[3]; nuclear@0: p_pcOut->mFaces[q].mNumIndices = 3; nuclear@0: nuclear@0: for (unsigned int t = 0; t < 3;++t, ++iBase) { nuclear@0: const uint32_t iIndex2 = mesh.mFaces[iIndex].mIndices[t]; nuclear@0: nuclear@0: p_pcOut->mVertices[iBase] = mesh.mPositions [iIndex2]; nuclear@0: p_pcOut->mNormals [iBase] = mesh.mNormals [iIndex2]; nuclear@0: nuclear@0: // convert bones, if existing nuclear@0: if (!mesh.mBones.empty()) { nuclear@0: // check whether there is a vertex weight for this vertex index nuclear@0: if (iIndex2 < mesh.mBoneVertices.size()) { nuclear@0: nuclear@0: for (std::vector >::const_iterator nuclear@0: blubb = mesh.mBoneVertices[iIndex2].mBoneWeights.begin(); nuclear@0: blubb != mesh.mBoneVertices[iIndex2].mBoneWeights.end();++blubb) { nuclear@0: nuclear@0: // NOTE: illegal cases have already been filtered out nuclear@0: avOutputBones[(*blubb).first].push_back(std::pair( nuclear@0: iBase,(*blubb).second)); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: p_pcOut->mFaces[q].mIndices[t] = iBase; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: // convert texture coordinates (up to AI_MAX_NUMBER_OF_TEXTURECOORDS sets supported) nuclear@0: for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { nuclear@0: if (!mesh.amTexCoords[c].empty()) nuclear@0: { nuclear@0: p_pcOut->mTextureCoords[c] = new aiVector3D[p_pcOut->mNumVertices]; nuclear@0: iBase = 0; nuclear@0: for (unsigned int q = 0; q < aiSplit[p].size();++q) { nuclear@0: iIndex = aiSplit[p][q]; nuclear@0: for (unsigned int t = 0; t < 3;++t) { nuclear@0: p_pcOut->mTextureCoords[c][iBase++] = mesh.amTexCoords[c][mesh.mFaces[iIndex].mIndices[t]]; nuclear@0: } nuclear@0: } nuclear@0: // Setup the number of valid vertex components nuclear@0: p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c]; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Convert vertex colors (only one set supported) nuclear@0: if (!mesh.mVertexColors.empty()){ nuclear@0: p_pcOut->mColors[0] = new aiColor4D[p_pcOut->mNumVertices]; nuclear@0: iBase = 0; nuclear@0: for (unsigned int q = 0; q < aiSplit[p].size();++q) { nuclear@0: iIndex = aiSplit[p][q]; nuclear@0: for (unsigned int t = 0; t < 3;++t) { nuclear@0: p_pcOut->mColors[0][iBase++] = mesh.mVertexColors[mesh.mFaces[iIndex].mIndices[t]]; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: // Copy bones nuclear@0: if (!mesh.mBones.empty()) { nuclear@0: p_pcOut->mNumBones = 0; nuclear@0: for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) nuclear@0: if (!avOutputBones[mrspock].empty())p_pcOut->mNumBones++; nuclear@0: nuclear@0: p_pcOut->mBones = new aiBone* [ p_pcOut->mNumBones ]; nuclear@0: aiBone** pcBone = p_pcOut->mBones; nuclear@0: for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) nuclear@0: { nuclear@0: if (!avOutputBones[mrspock].empty()) { nuclear@0: // we will need this bone. add it to the output mesh and nuclear@0: // add all per-vertex weights nuclear@0: aiBone* pc = *pcBone = new aiBone(); nuclear@0: pc->mName.Set(mesh.mBones[mrspock].mName); nuclear@0: nuclear@0: pc->mNumWeights = (unsigned int)avOutputBones[mrspock].size(); nuclear@0: pc->mWeights = new aiVertexWeight[pc->mNumWeights]; nuclear@0: nuclear@0: for (unsigned int captainkirk = 0; captainkirk < pc->mNumWeights;++captainkirk) nuclear@0: { nuclear@0: const std::pair& ref = avOutputBones[mrspock][captainkirk]; nuclear@0: pc->mWeights[captainkirk].mVertexId = ref.first; nuclear@0: pc->mWeights[captainkirk].mWeight = ref.second; nuclear@0: } nuclear@0: ++pcBone; nuclear@0: } nuclear@0: } nuclear@0: // delete allocated storage nuclear@0: delete[] avOutputBones; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: // delete storage nuclear@0: delete[] aiSplit; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // Otherwise we can simply copy the data to one output mesh nuclear@0: // This codepath needs less memory and uses fast memcpy()s nuclear@0: // to do the actual copying. So I think it is worth the nuclear@0: // effort here. nuclear@0: nuclear@0: aiMesh* p_pcOut = new aiMesh(); nuclear@0: p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; nuclear@0: nuclear@0: // set an empty sub material index nuclear@0: p_pcOut->mMaterialIndex = ASE::Face::DEFAULT_MATINDEX; nuclear@0: mParser->m_vMaterials[mesh.iMaterialIndex].bNeed = true; nuclear@0: nuclear@0: // store the real index here ... in color channel 3 nuclear@0: p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex; nuclear@0: nuclear@0: // store a pointer to the mesh in color channel 2 nuclear@0: p_pcOut->mColors[2] = (aiColor4D*) &mesh; nuclear@0: avOutMeshes.push_back(p_pcOut); nuclear@0: nuclear@0: // If the mesh hasn't faces or vertices, there are two cases nuclear@0: // possible: 1. the model is invalid. 2. This is a dummy nuclear@0: // helper object which we are going to remove later ... nuclear@0: if (mesh.mFaces.empty() || mesh.mPositions.empty()) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: // convert vertices nuclear@0: p_pcOut->mNumVertices = (unsigned int)mesh.mPositions.size(); nuclear@0: p_pcOut->mNumFaces = (unsigned int)mesh.mFaces.size(); nuclear@0: nuclear@0: // allocate enough storage for faces nuclear@0: p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; nuclear@0: nuclear@0: // copy vertices nuclear@0: p_pcOut->mVertices = new aiVector3D[mesh.mPositions.size()]; nuclear@0: memcpy(p_pcOut->mVertices,&mesh.mPositions[0], nuclear@0: mesh.mPositions.size() * sizeof(aiVector3D)); nuclear@0: nuclear@0: // copy normals nuclear@0: p_pcOut->mNormals = new aiVector3D[mesh.mNormals.size()]; nuclear@0: memcpy(p_pcOut->mNormals,&mesh.mNormals[0], nuclear@0: mesh.mNormals.size() * sizeof(aiVector3D)); nuclear@0: nuclear@0: // copy texture coordinates nuclear@0: for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { nuclear@0: if (!mesh.amTexCoords[c].empty()) { nuclear@0: p_pcOut->mTextureCoords[c] = new aiVector3D[mesh.amTexCoords[c].size()]; nuclear@0: memcpy(p_pcOut->mTextureCoords[c],&mesh.amTexCoords[c][0], nuclear@0: mesh.amTexCoords[c].size() * sizeof(aiVector3D)); nuclear@0: nuclear@0: // setup the number of valid vertex components nuclear@0: p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c]; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // copy vertex colors nuclear@0: if (!mesh.mVertexColors.empty()) { nuclear@0: p_pcOut->mColors[0] = new aiColor4D[mesh.mVertexColors.size()]; nuclear@0: memcpy(p_pcOut->mColors[0],&mesh.mVertexColors[0], nuclear@0: mesh.mVertexColors.size() * sizeof(aiColor4D)); nuclear@0: } nuclear@0: nuclear@0: // copy faces nuclear@0: for (unsigned int iFace = 0; iFace < p_pcOut->mNumFaces;++iFace) { nuclear@0: p_pcOut->mFaces[iFace].mNumIndices = 3; nuclear@0: p_pcOut->mFaces[iFace].mIndices = new unsigned int[3]; nuclear@0: nuclear@0: // copy indices nuclear@0: p_pcOut->mFaces[iFace].mIndices[0] = mesh.mFaces[iFace].mIndices[0]; nuclear@0: p_pcOut->mFaces[iFace].mIndices[1] = mesh.mFaces[iFace].mIndices[1]; nuclear@0: p_pcOut->mFaces[iFace].mIndices[2] = mesh.mFaces[iFace].mIndices[2]; nuclear@0: } nuclear@0: nuclear@0: // copy vertex bones nuclear@0: if (!mesh.mBones.empty() && !mesh.mBoneVertices.empty()) { nuclear@0: std::vector > avBonesOut( mesh.mBones.size() ); nuclear@0: nuclear@0: // find all vertex weights for this bone nuclear@0: unsigned int quak = 0; nuclear@0: for (std::vector::const_iterator harrypotter = mesh.mBoneVertices.begin(); nuclear@0: harrypotter != mesh.mBoneVertices.end();++harrypotter,++quak) { nuclear@0: nuclear@0: for (std::vector >::const_iterator nuclear@0: ronaldweasley = (*harrypotter).mBoneWeights.begin(); nuclear@0: ronaldweasley != (*harrypotter).mBoneWeights.end();++ronaldweasley) nuclear@0: { nuclear@0: aiVertexWeight weight; nuclear@0: weight.mVertexId = quak; nuclear@0: weight.mWeight = (*ronaldweasley).second; nuclear@0: avBonesOut[(*ronaldweasley).first].push_back(weight); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // now build a final bone list nuclear@0: p_pcOut->mNumBones = 0; nuclear@0: for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) nuclear@0: if (!avBonesOut[jfkennedy].empty())p_pcOut->mNumBones++; nuclear@0: nuclear@0: p_pcOut->mBones = new aiBone*[p_pcOut->mNumBones]; nuclear@0: aiBone** pcBone = p_pcOut->mBones; nuclear@0: for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) { nuclear@0: if (!avBonesOut[jfkennedy].empty()) { nuclear@0: aiBone* pc = *pcBone = new aiBone(); nuclear@0: pc->mName.Set(mesh.mBones[jfkennedy].mName); nuclear@0: pc->mNumWeights = (unsigned int)avBonesOut[jfkennedy].size(); nuclear@0: pc->mWeights = new aiVertexWeight[pc->mNumWeights]; nuclear@0: ::memcpy(pc->mWeights,&avBonesOut[jfkennedy][0], nuclear@0: sizeof(aiVertexWeight) * pc->mNumWeights); nuclear@0: ++pcBone; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Setup proper material indices and build output materials nuclear@0: void ASEImporter::BuildMaterialIndices() nuclear@0: { nuclear@0: ai_assert(NULL != pcScene); nuclear@0: nuclear@0: // iterate through all materials and check whether we need them nuclear@0: for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) nuclear@0: { nuclear@0: ASE::Material& mat = mParser->m_vMaterials[iMat]; nuclear@0: if (mat.bNeed) { nuclear@0: // Convert it to the aiMaterial layout nuclear@0: ConvertMaterial(mat); nuclear@0: ++pcScene->mNumMaterials; nuclear@0: } nuclear@0: for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) nuclear@0: { nuclear@0: ASE::Material& submat = mat.avSubMaterials[iSubMat]; nuclear@0: if (submat.bNeed) { nuclear@0: // Convert it to the aiMaterial layout nuclear@0: ConvertMaterial(submat); nuclear@0: ++pcScene->mNumMaterials; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // allocate the output material array nuclear@0: pcScene->mMaterials = new aiMaterial*[pcScene->mNumMaterials]; nuclear@0: D3DS::Material** pcIntMaterials = new D3DS::Material*[pcScene->mNumMaterials]; nuclear@0: nuclear@0: unsigned int iNum = 0; nuclear@0: for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) { nuclear@0: ASE::Material& mat = mParser->m_vMaterials[iMat]; nuclear@0: if (mat.bNeed) nuclear@0: { nuclear@0: ai_assert(NULL != mat.pcInstance); nuclear@0: pcScene->mMaterials[iNum] = mat.pcInstance; nuclear@0: nuclear@0: // Store the internal material, too nuclear@0: pcIntMaterials[iNum] = &mat; nuclear@0: nuclear@0: // Iterate through all meshes and search for one which is using nuclear@0: // this top-level material index nuclear@0: for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) nuclear@0: { nuclear@0: aiMesh* mesh = pcScene->mMeshes[iMesh]; nuclear@0: if (ASE::Face::DEFAULT_MATINDEX == mesh->mMaterialIndex && nuclear@0: iMat == (uintptr_t)mesh->mColors[3]) nuclear@0: { nuclear@0: mesh->mMaterialIndex = iNum; nuclear@0: mesh->mColors[3] = NULL; nuclear@0: } nuclear@0: } nuclear@0: iNum++; nuclear@0: } nuclear@0: for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) { nuclear@0: ASE::Material& submat = mat.avSubMaterials[iSubMat]; nuclear@0: if (submat.bNeed) { nuclear@0: ai_assert(NULL != submat.pcInstance); nuclear@0: pcScene->mMaterials[iNum] = submat.pcInstance; nuclear@0: nuclear@0: // Store the internal material, too nuclear@0: pcIntMaterials[iNum] = &submat; nuclear@0: nuclear@0: // Iterate through all meshes and search for one which is using nuclear@0: // this sub-level material index nuclear@0: for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) { nuclear@0: aiMesh* mesh = pcScene->mMeshes[iMesh]; nuclear@0: nuclear@0: if (iSubMat == mesh->mMaterialIndex && iMat == (uintptr_t)mesh->mColors[3]) { nuclear@0: mesh->mMaterialIndex = iNum; nuclear@0: mesh->mColors[3] = NULL; nuclear@0: } nuclear@0: } nuclear@0: iNum++; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Dekete our temporary array nuclear@0: delete[] pcIntMaterials; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Generate normal vectors basing on smoothing groups nuclear@0: bool ASEImporter::GenerateNormals(ASE::Mesh& mesh) { nuclear@0: nuclear@0: if (!mesh.mNormals.empty() && !configRecomputeNormals) nuclear@0: { nuclear@0: // Check whether there are only uninitialized normals. If there are nuclear@0: // some, skip all normals from the file and compute them on our own nuclear@0: for (std::vector::const_iterator qq = mesh.mNormals.begin();qq != mesh.mNormals.end();++qq) { nuclear@0: if ((*qq).x || (*qq).y || (*qq).z) nuclear@0: { nuclear@0: return true; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: // The array is reused. nuclear@0: ComputeNormalsWithSmoothingsGroups(mesh); nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: #endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER