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: /** @file XFileImporter.cpp nuclear@0: * @brief Implementation of the XFile importer class nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #ifndef ASSIMP_BUILD_NO_X_IMPORTER nuclear@0: nuclear@0: #include "XFileImporter.h" nuclear@0: #include "XFileParser.h" nuclear@0: #include "ConvertToLHProcess.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: static const aiImporterDesc desc = { nuclear@0: "Direct3D XFile Importer", nuclear@0: "", nuclear@0: "", nuclear@0: "", nuclear@0: aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour, nuclear@0: 1, nuclear@0: 3, nuclear@0: 1, nuclear@0: 5, nuclear@0: "x" nuclear@0: }; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: XFileImporter::XFileImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: XFileImporter::~XFileImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the class can handle the format of the given file. nuclear@0: bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const nuclear@0: { nuclear@0: std::string extension = GetExtension(pFile); nuclear@0: if(extension == "x") { nuclear@0: return true; nuclear@0: } nuclear@0: if (!extension.length() || checkSig) { nuclear@0: uint32_t token[1]; nuclear@0: token[0] = AI_MAKE_MAGIC("xof "); nuclear@0: return CheckMagicToken(pIOHandler,pFile,token,1,0); nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Get file extension list nuclear@0: const aiImporterDesc* XFileImporter::GetInfo () const nuclear@0: { nuclear@0: return &desc; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Imports the given file into the given scene structure. nuclear@0: void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) nuclear@0: { nuclear@0: // read file into memory nuclear@0: boost::scoped_ptr file( pIOHandler->Open( pFile)); nuclear@0: if( file.get() == NULL) nuclear@0: throw DeadlyImportError( "Failed to open file " + pFile + "."); nuclear@0: nuclear@0: size_t fileSize = file->FileSize(); nuclear@0: if( fileSize < 16) nuclear@0: throw DeadlyImportError( "XFile is too small."); nuclear@0: nuclear@0: // in the hope that binary files will never start with a BOM ... nuclear@0: mBuffer.resize( fileSize + 1); nuclear@0: file->Read( &mBuffer.front(), 1, fileSize); nuclear@0: ConvertToUTF8(mBuffer); nuclear@0: nuclear@0: // parse the file into a temporary representation nuclear@0: XFileParser parser( mBuffer); nuclear@0: nuclear@0: // and create the proper return structures out of it nuclear@0: CreateDataRepresentationFromImport( pScene, parser.GetImportedData()); nuclear@0: nuclear@0: // if nothing came from it, report it as error nuclear@0: if( !pScene->mRootNode) nuclear@0: throw DeadlyImportError( "XFile is ill-formatted - no content imported."); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructs the return data structure out of the imported data. nuclear@0: void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, XFile::Scene* pData) nuclear@0: { nuclear@0: // Read the global materials first so that meshes referring to them can find them later nuclear@0: ConvertMaterials( pScene, pData->mGlobalMaterials); nuclear@0: nuclear@0: // copy nodes, extracting meshes and materials on the way nuclear@0: pScene->mRootNode = CreateNodes( pScene, NULL, pData->mRootNode); nuclear@0: nuclear@0: // extract animations nuclear@0: CreateAnimations( pScene, pData); nuclear@0: nuclear@0: // read the global meshes that were stored outside of any node nuclear@0: if( pData->mGlobalMeshes.size() > 0) nuclear@0: { nuclear@0: // create a root node to hold them if there isn't any, yet nuclear@0: if( pScene->mRootNode == NULL) nuclear@0: { nuclear@0: pScene->mRootNode = new aiNode; nuclear@0: pScene->mRootNode->mName.Set( "$dummy_node"); nuclear@0: } nuclear@0: nuclear@0: // convert all global meshes and store them in the root node. nuclear@0: // If there was one before, the global meshes now suddenly have its transformation matrix... nuclear@0: // Don't know what to do there, I don't want to insert another node under the present root node nuclear@0: // just to avoid this. nuclear@0: CreateMeshes( pScene, pScene->mRootNode, pData->mGlobalMeshes); nuclear@0: } nuclear@0: nuclear@0: // Convert everything to OpenGL space... it's the same operation as the conversion back, so we can reuse the step directly nuclear@0: MakeLeftHandedProcess convertProcess; nuclear@0: convertProcess.Execute( pScene); nuclear@0: nuclear@0: FlipWindingOrderProcess flipper; nuclear@0: flipper.Execute(pScene); nuclear@0: nuclear@0: // finally: create a dummy material if not material was imported nuclear@0: if( pScene->mNumMaterials == 0) nuclear@0: { nuclear@0: pScene->mNumMaterials = 1; nuclear@0: // create the Material nuclear@0: aiMaterial* mat = new aiMaterial; nuclear@0: int shadeMode = (int) aiShadingMode_Gouraud; nuclear@0: mat->AddProperty( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); nuclear@0: // material colours nuclear@0: int specExp = 1; nuclear@0: nuclear@0: aiColor3D clr = aiColor3D( 0, 0, 0); nuclear@0: mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_EMISSIVE); nuclear@0: mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_SPECULAR); nuclear@0: nuclear@0: clr = aiColor3D( 0.5f, 0.5f, 0.5f); nuclear@0: mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_DIFFUSE); nuclear@0: mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS); nuclear@0: nuclear@0: pScene->mMaterials = new aiMaterial*[1]; nuclear@0: pScene->mMaterials[0] = mat; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Recursively creates scene nodes from the imported hierarchy. nuclear@0: aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode) nuclear@0: { nuclear@0: if( !pNode) nuclear@0: return NULL; nuclear@0: nuclear@0: // create node nuclear@0: aiNode* node = new aiNode; nuclear@0: node->mName.length = pNode->mName.length(); nuclear@0: node->mParent = pParent; nuclear@0: memcpy( node->mName.data, pNode->mName.c_str(), pNode->mName.length()); nuclear@0: node->mName.data[node->mName.length] = 0; nuclear@0: node->mTransformation = pNode->mTrafoMatrix; nuclear@0: nuclear@0: // convert meshes from the source node nuclear@0: CreateMeshes( pScene, node, pNode->mMeshes); nuclear@0: nuclear@0: // handle childs nuclear@0: if( pNode->mChildren.size() > 0) nuclear@0: { nuclear@0: node->mNumChildren = (unsigned int)pNode->mChildren.size(); nuclear@0: node->mChildren = new aiNode* [node->mNumChildren]; nuclear@0: nuclear@0: for( unsigned int a = 0; a < pNode->mChildren.size(); a++) nuclear@0: node->mChildren[a] = CreateNodes( pScene, node, pNode->mChildren[a]); nuclear@0: } nuclear@0: nuclear@0: return node; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Creates the meshes for the given node. nuclear@0: void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector& pMeshes) nuclear@0: { nuclear@0: if( pMeshes.size() == 0) nuclear@0: return; nuclear@0: nuclear@0: // create a mesh for each mesh-material combination in the source node nuclear@0: std::vector meshes; nuclear@0: for( unsigned int a = 0; a < pMeshes.size(); a++) nuclear@0: { nuclear@0: XFile::Mesh* sourceMesh = pMeshes[a]; nuclear@0: // first convert its materials so that we can find them with their index afterwards nuclear@0: ConvertMaterials( pScene, sourceMesh->mMaterials); nuclear@0: nuclear@0: unsigned int numMaterials = std::max( (unsigned int)sourceMesh->mMaterials.size(), 1u); nuclear@0: for( unsigned int b = 0; b < numMaterials; b++) nuclear@0: { nuclear@0: // collect the faces belonging to this material nuclear@0: std::vector faces; nuclear@0: unsigned int numVertices = 0; nuclear@0: if( sourceMesh->mFaceMaterials.size() > 0) nuclear@0: { nuclear@0: // if there is a per-face material defined, select the faces with the corresponding material nuclear@0: for( unsigned int c = 0; c < sourceMesh->mFaceMaterials.size(); c++) nuclear@0: { nuclear@0: if( sourceMesh->mFaceMaterials[c] == b) nuclear@0: { nuclear@0: faces.push_back( c); nuclear@0: numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size(); nuclear@0: } nuclear@0: } nuclear@0: } else nuclear@0: { nuclear@0: // if there is no per-face material, place everything into one mesh nuclear@0: for( unsigned int c = 0; c < sourceMesh->mPosFaces.size(); c++) nuclear@0: { nuclear@0: faces.push_back( c); nuclear@0: numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // no faces/vertices using this material? strange... nuclear@0: if( numVertices == 0) nuclear@0: continue; nuclear@0: nuclear@0: // create a submesh using this material nuclear@0: aiMesh* mesh = new aiMesh; nuclear@0: meshes.push_back( mesh); nuclear@0: nuclear@0: // find the material in the scene's material list. Either own material nuclear@0: // or referenced material, it should already have a valid index nuclear@0: if( sourceMesh->mFaceMaterials.size() > 0) nuclear@0: { nuclear@0: mesh->mMaterialIndex = sourceMesh->mMaterials[b].sceneIndex; nuclear@0: } else nuclear@0: { nuclear@0: mesh->mMaterialIndex = 0; nuclear@0: } nuclear@0: nuclear@0: // Create properly sized data arrays in the mesh. We store unique vertices per face, nuclear@0: // as specified nuclear@0: mesh->mNumVertices = numVertices; nuclear@0: mesh->mVertices = new aiVector3D[numVertices]; nuclear@0: mesh->mNumFaces = (unsigned int)faces.size(); nuclear@0: mesh->mFaces = new aiFace[mesh->mNumFaces]; nuclear@0: nuclear@0: // normals? nuclear@0: if( sourceMesh->mNormals.size() > 0) nuclear@0: mesh->mNormals = new aiVector3D[numVertices]; nuclear@0: // texture coords nuclear@0: for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; c++) nuclear@0: { nuclear@0: if( sourceMesh->mTexCoords[c].size() > 0) nuclear@0: mesh->mTextureCoords[c] = new aiVector3D[numVertices]; nuclear@0: } nuclear@0: // vertex colors nuclear@0: for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; c++) nuclear@0: { nuclear@0: if( sourceMesh->mColors[c].size() > 0) nuclear@0: mesh->mColors[c] = new aiColor4D[numVertices]; nuclear@0: } nuclear@0: nuclear@0: // now collect the vertex data of all data streams present in the imported mesh nuclear@0: unsigned int newIndex = 0; nuclear@0: std::vector orgPoints; // from which original point each new vertex stems nuclear@0: orgPoints.resize( numVertices, 0); nuclear@0: nuclear@0: for( unsigned int c = 0; c < faces.size(); c++) nuclear@0: { nuclear@0: unsigned int f = faces[c]; // index of the source face nuclear@0: const XFile::Face& pf = sourceMesh->mPosFaces[f]; // position source face nuclear@0: nuclear@0: // create face. either triangle or triangle fan depending on the index count nuclear@0: aiFace& df = mesh->mFaces[c]; // destination face nuclear@0: df.mNumIndices = (unsigned int)pf.mIndices.size(); nuclear@0: df.mIndices = new unsigned int[ df.mNumIndices]; nuclear@0: nuclear@0: // collect vertex data for indices of this face nuclear@0: for( unsigned int d = 0; d < df.mNumIndices; d++) nuclear@0: { nuclear@0: df.mIndices[d] = newIndex; nuclear@0: orgPoints[newIndex] = pf.mIndices[d]; nuclear@0: nuclear@0: // Position nuclear@0: mesh->mVertices[newIndex] = sourceMesh->mPositions[pf.mIndices[d]]; nuclear@0: // Normal, if present nuclear@0: if( mesh->HasNormals()) nuclear@0: mesh->mNormals[newIndex] = sourceMesh->mNormals[sourceMesh->mNormFaces[f].mIndices[d]]; nuclear@0: nuclear@0: // texture coord sets nuclear@0: for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_TEXTURECOORDS; e++) nuclear@0: { nuclear@0: if( mesh->HasTextureCoords( e)) nuclear@0: { nuclear@0: aiVector2D tex = sourceMesh->mTexCoords[e][pf.mIndices[d]]; nuclear@0: mesh->mTextureCoords[e][newIndex] = aiVector3D( tex.x, 1.0f - tex.y, 0.0f); nuclear@0: } nuclear@0: } nuclear@0: // vertex color sets nuclear@0: for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; e++) nuclear@0: if( mesh->HasVertexColors( e)) nuclear@0: mesh->mColors[e][newIndex] = sourceMesh->mColors[e][pf.mIndices[d]]; nuclear@0: nuclear@0: newIndex++; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // there should be as much new vertices as we calculated before nuclear@0: ai_assert( newIndex == numVertices); nuclear@0: nuclear@0: // convert all bones of the source mesh which influence vertices in this newly created mesh nuclear@0: const std::vector& bones = sourceMesh->mBones; nuclear@0: std::vector newBones; nuclear@0: for( unsigned int c = 0; c < bones.size(); c++) nuclear@0: { nuclear@0: const XFile::Bone& obone = bones[c]; nuclear@0: // set up a vertex-linear array of the weights for quick searching if a bone influences a vertex nuclear@0: std::vector oldWeights( sourceMesh->mPositions.size(), 0.0f); nuclear@0: for( unsigned int d = 0; d < obone.mWeights.size(); d++) nuclear@0: oldWeights[obone.mWeights[d].mVertex] = obone.mWeights[d].mWeight; nuclear@0: nuclear@0: // collect all vertex weights that influence a vertex in the new mesh nuclear@0: std::vector newWeights; nuclear@0: newWeights.reserve( numVertices); nuclear@0: for( unsigned int d = 0; d < orgPoints.size(); d++) nuclear@0: { nuclear@0: // does the new vertex stem from an old vertex which was influenced by this bone? nuclear@0: float w = oldWeights[orgPoints[d]]; nuclear@0: if( w > 0.0f) nuclear@0: newWeights.push_back( aiVertexWeight( d, w)); nuclear@0: } nuclear@0: nuclear@0: // if the bone has no weights in the newly created mesh, ignore it nuclear@0: if( newWeights.size() == 0) nuclear@0: continue; nuclear@0: nuclear@0: // create nuclear@0: aiBone* nbone = new aiBone; nuclear@0: newBones.push_back( nbone); nuclear@0: // copy name and matrix nuclear@0: nbone->mName.Set( obone.mName); nuclear@0: nbone->mOffsetMatrix = obone.mOffsetMatrix; nuclear@0: nbone->mNumWeights = (unsigned int)newWeights.size(); nuclear@0: nbone->mWeights = new aiVertexWeight[nbone->mNumWeights]; nuclear@0: for( unsigned int d = 0; d < newWeights.size(); d++) nuclear@0: nbone->mWeights[d] = newWeights[d]; nuclear@0: } nuclear@0: nuclear@0: // store the bones in the mesh nuclear@0: mesh->mNumBones = (unsigned int)newBones.size(); nuclear@0: if( newBones.size() > 0) nuclear@0: { nuclear@0: mesh->mBones = new aiBone*[mesh->mNumBones]; nuclear@0: std::copy( newBones.begin(), newBones.end(), mesh->mBones); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // reallocate scene mesh array to be large enough nuclear@0: aiMesh** prevArray = pScene->mMeshes; nuclear@0: pScene->mMeshes = new aiMesh*[pScene->mNumMeshes + meshes.size()]; nuclear@0: if( prevArray) nuclear@0: { nuclear@0: memcpy( pScene->mMeshes, prevArray, pScene->mNumMeshes * sizeof( aiMesh*)); nuclear@0: delete [] prevArray; nuclear@0: } nuclear@0: nuclear@0: // allocate mesh index array in the node nuclear@0: pNode->mNumMeshes = (unsigned int)meshes.size(); nuclear@0: pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; nuclear@0: nuclear@0: // store all meshes in the mesh library of the scene and store their indices in the node nuclear@0: for( unsigned int a = 0; a < meshes.size(); a++) nuclear@0: { nuclear@0: pScene->mMeshes[pScene->mNumMeshes] = meshes[a]; nuclear@0: pNode->mMeshes[a] = pScene->mNumMeshes; nuclear@0: pScene->mNumMeshes++; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Converts the animations from the given imported data and creates them in the scene. nuclear@0: void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData) nuclear@0: { nuclear@0: std::vector newAnims; nuclear@0: nuclear@0: for( unsigned int a = 0; a < pData->mAnims.size(); a++) nuclear@0: { nuclear@0: const XFile::Animation* anim = pData->mAnims[a]; nuclear@0: // some exporters mock me with empty animation tags. nuclear@0: if( anim->mAnims.size() == 0) nuclear@0: continue; nuclear@0: nuclear@0: // create a new animation to hold the data nuclear@0: aiAnimation* nanim = new aiAnimation; nuclear@0: newAnims.push_back( nanim); nuclear@0: nanim->mName.Set( anim->mName); nuclear@0: // duration will be determined by the maximum length nuclear@0: nanim->mDuration = 0; nuclear@0: nanim->mTicksPerSecond = pData->mAnimTicksPerSecond; nuclear@0: nanim->mNumChannels = (unsigned int)anim->mAnims.size(); nuclear@0: nanim->mChannels = new aiNodeAnim*[nanim->mNumChannels]; nuclear@0: nuclear@0: for( unsigned int b = 0; b < anim->mAnims.size(); b++) nuclear@0: { nuclear@0: const XFile::AnimBone* bone = anim->mAnims[b]; nuclear@0: aiNodeAnim* nbone = new aiNodeAnim; nuclear@0: nbone->mNodeName.Set( bone->mBoneName); nuclear@0: nanim->mChannels[b] = nbone; nuclear@0: nuclear@0: // keyframes are given as combined transformation matrix keys nuclear@0: if( bone->mTrafoKeys.size() > 0) nuclear@0: { nuclear@0: nbone->mNumPositionKeys = (unsigned int)bone->mTrafoKeys.size(); nuclear@0: nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys]; nuclear@0: nbone->mNumRotationKeys = (unsigned int)bone->mTrafoKeys.size(); nuclear@0: nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys]; nuclear@0: nbone->mNumScalingKeys = (unsigned int)bone->mTrafoKeys.size(); nuclear@0: nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys]; nuclear@0: nuclear@0: for( unsigned int c = 0; c < bone->mTrafoKeys.size(); c++) nuclear@0: { nuclear@0: // deconstruct each matrix into separate position, rotation and scaling nuclear@0: double time = bone->mTrafoKeys[c].mTime; nuclear@0: aiMatrix4x4 trafo = bone->mTrafoKeys[c].mMatrix; nuclear@0: nuclear@0: // extract position nuclear@0: aiVector3D pos( trafo.a4, trafo.b4, trafo.c4); nuclear@0: nuclear@0: nbone->mPositionKeys[c].mTime = time; nuclear@0: nbone->mPositionKeys[c].mValue = pos; nuclear@0: nuclear@0: // extract scaling nuclear@0: aiVector3D scale; nuclear@0: scale.x = aiVector3D( trafo.a1, trafo.b1, trafo.c1).Length(); nuclear@0: scale.y = aiVector3D( trafo.a2, trafo.b2, trafo.c2).Length(); nuclear@0: scale.z = aiVector3D( trafo.a3, trafo.b3, trafo.c3).Length(); nuclear@0: nbone->mScalingKeys[c].mTime = time; nuclear@0: nbone->mScalingKeys[c].mValue = scale; nuclear@0: nuclear@0: // reconstruct rotation matrix without scaling nuclear@0: aiMatrix3x3 rotmat( nuclear@0: trafo.a1 / scale.x, trafo.a2 / scale.y, trafo.a3 / scale.z, nuclear@0: trafo.b1 / scale.x, trafo.b2 / scale.y, trafo.b3 / scale.z, nuclear@0: trafo.c1 / scale.x, trafo.c2 / scale.y, trafo.c3 / scale.z); nuclear@0: nuclear@0: // and convert it into a quaternion nuclear@0: nbone->mRotationKeys[c].mTime = time; nuclear@0: nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat); nuclear@0: } nuclear@0: nuclear@0: // longest lasting key sequence determines duration nuclear@0: nanim->mDuration = std::max( nanim->mDuration, bone->mTrafoKeys.back().mTime); nuclear@0: } else nuclear@0: { nuclear@0: // separate key sequences for position, rotation, scaling nuclear@0: nbone->mNumPositionKeys = (unsigned int)bone->mPosKeys.size(); nuclear@0: nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys]; nuclear@0: for( unsigned int c = 0; c < nbone->mNumPositionKeys; c++) nuclear@0: { nuclear@0: aiVector3D pos = bone->mPosKeys[c].mValue; nuclear@0: nuclear@0: nbone->mPositionKeys[c].mTime = bone->mPosKeys[c].mTime; nuclear@0: nbone->mPositionKeys[c].mValue = pos; nuclear@0: } nuclear@0: nuclear@0: // rotation nuclear@0: nbone->mNumRotationKeys = (unsigned int)bone->mRotKeys.size(); nuclear@0: nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys]; nuclear@0: for( unsigned int c = 0; c < nbone->mNumRotationKeys; c++) nuclear@0: { nuclear@0: aiMatrix3x3 rotmat = bone->mRotKeys[c].mValue.GetMatrix(); nuclear@0: nuclear@0: nbone->mRotationKeys[c].mTime = bone->mRotKeys[c].mTime; nuclear@0: nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat); nuclear@0: nbone->mRotationKeys[c].mValue.w *= -1.0f; // needs quat inversion nuclear@0: } nuclear@0: nuclear@0: // scaling nuclear@0: nbone->mNumScalingKeys = (unsigned int)bone->mScaleKeys.size(); nuclear@0: nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys]; nuclear@0: for( unsigned int c = 0; c < nbone->mNumScalingKeys; c++) nuclear@0: nbone->mScalingKeys[c] = bone->mScaleKeys[c]; nuclear@0: nuclear@0: // longest lasting key sequence determines duration nuclear@0: if( bone->mPosKeys.size() > 0) nuclear@0: nanim->mDuration = std::max( nanim->mDuration, bone->mPosKeys.back().mTime); nuclear@0: if( bone->mRotKeys.size() > 0) nuclear@0: nanim->mDuration = std::max( nanim->mDuration, bone->mRotKeys.back().mTime); nuclear@0: if( bone->mScaleKeys.size() > 0) nuclear@0: nanim->mDuration = std::max( nanim->mDuration, bone->mScaleKeys.back().mTime); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // store all converted animations in the scene nuclear@0: if( newAnims.size() > 0) nuclear@0: { nuclear@0: pScene->mNumAnimations = (unsigned int)newAnims.size(); nuclear@0: pScene->mAnimations = new aiAnimation* [pScene->mNumAnimations]; nuclear@0: for( unsigned int a = 0; a < newAnims.size(); a++) nuclear@0: pScene->mAnimations[a] = newAnims[a]; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Converts all materials in the given array and stores them in the scene's material list. nuclear@0: void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector& pMaterials) nuclear@0: { nuclear@0: // count the non-referrer materials in the array nuclear@0: unsigned int numNewMaterials = 0; nuclear@0: for( unsigned int a = 0; a < pMaterials.size(); a++) nuclear@0: if( !pMaterials[a].mIsReference) nuclear@0: numNewMaterials++; nuclear@0: nuclear@0: // resize the scene's material list to offer enough space for the new materials nuclear@0: if( numNewMaterials > 0 ) nuclear@0: { nuclear@0: aiMaterial** prevMats = pScene->mMaterials; nuclear@0: pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials + numNewMaterials]; nuclear@0: if( prevMats) nuclear@0: { nuclear@0: memcpy( pScene->mMaterials, prevMats, pScene->mNumMaterials * sizeof( aiMaterial*)); nuclear@0: delete [] prevMats; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // convert all the materials given in the array nuclear@0: for( unsigned int a = 0; a < pMaterials.size(); a++) nuclear@0: { nuclear@0: XFile::Material& oldMat = pMaterials[a]; nuclear@0: if( oldMat.mIsReference) nuclear@0: { nuclear@0: // find the material it refers to by name, and store its index nuclear@0: for( size_t a = 0; a < pScene->mNumMaterials; ++a ) nuclear@0: { nuclear@0: aiString name; nuclear@0: pScene->mMaterials[a]->Get( AI_MATKEY_NAME, name); nuclear@0: if( strcmp( name.C_Str(), oldMat.mName.data()) == 0 ) nuclear@0: { nuclear@0: oldMat.sceneIndex = a; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if( oldMat.sceneIndex == SIZE_MAX ) nuclear@0: { nuclear@0: DefaultLogger::get()->warn( boost::str( boost::format( "Could not resolve global material reference \"%s\"") % oldMat.mName)); nuclear@0: oldMat.sceneIndex = 0; nuclear@0: } nuclear@0: nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: aiMaterial* mat = new aiMaterial; nuclear@0: aiString name; nuclear@0: name.Set( oldMat.mName); nuclear@0: mat->AddProperty( &name, AI_MATKEY_NAME); nuclear@0: nuclear@0: // Shading model: hardcoded to PHONG, there is no such information in an XFile nuclear@0: // FIX (aramis): If the specular exponent is 0, use gouraud shading. This is a bugfix nuclear@0: // for some models in the SDK (e.g. good old tiny.x) nuclear@0: int shadeMode = (int)oldMat.mSpecularExponent == 0.0f nuclear@0: ? aiShadingMode_Gouraud : aiShadingMode_Phong; nuclear@0: nuclear@0: mat->AddProperty( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); nuclear@0: // material colours nuclear@0: // Unclear: there's no ambient colour, but emissive. What to put for ambient? nuclear@0: // Probably nothing at all, let the user select a suitable default. nuclear@0: mat->AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); nuclear@0: mat->AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); nuclear@0: mat->AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); nuclear@0: mat->AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS); nuclear@0: nuclear@0: nuclear@0: // texture, if there is one nuclear@0: if (1 == oldMat.mTextures.size()) nuclear@0: { nuclear@0: const XFile::TexEntry& otex = oldMat.mTextures.back(); nuclear@0: if (otex.mName.length()) nuclear@0: { nuclear@0: // if there is only one texture assume it contains the diffuse color nuclear@0: aiString tex( otex.mName); nuclear@0: if( otex.mIsNormalMap) nuclear@0: mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(0)); nuclear@0: else nuclear@0: mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0)); nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // Otherwise ... try to search for typical strings in the nuclear@0: // texture's file name like 'bump' or 'diffuse' nuclear@0: unsigned int iHM = 0,iNM = 0,iDM = 0,iSM = 0,iAM = 0,iEM = 0; nuclear@0: for( unsigned int b = 0; b < oldMat.mTextures.size(); b++) nuclear@0: { nuclear@0: const XFile::TexEntry& otex = oldMat.mTextures[b]; nuclear@0: std::string sz = otex.mName; nuclear@0: if (!sz.length())continue; nuclear@0: nuclear@0: nuclear@0: // find the file name nuclear@0: //const size_t iLen = sz.length(); nuclear@0: std::string::size_type s = sz.find_last_of("\\/"); nuclear@0: if (std::string::npos == s) nuclear@0: s = 0; nuclear@0: nuclear@0: // cut off the file extension nuclear@0: std::string::size_type sExt = sz.find_last_of('.'); nuclear@0: if (std::string::npos != sExt){ nuclear@0: sz[sExt] = '\0'; nuclear@0: } nuclear@0: nuclear@0: // convert to lower case for easier comparision nuclear@0: for( unsigned int c = 0; c < sz.length(); c++) nuclear@0: if( isalpha( sz[c])) nuclear@0: sz[c] = tolower( sz[c]); nuclear@0: nuclear@0: nuclear@0: // Place texture filename property under the corresponding name nuclear@0: aiString tex( oldMat.mTextures[b].mName); nuclear@0: nuclear@0: // bump map nuclear@0: if (std::string::npos != sz.find("bump", s) || std::string::npos != sz.find("height", s)) nuclear@0: { nuclear@0: mat->AddProperty( &tex, AI_MATKEY_TEXTURE_HEIGHT(iHM++)); nuclear@0: } else nuclear@0: if (otex.mIsNormalMap || std::string::npos != sz.find( "normal", s) || std::string::npos != sz.find("nm", s)) nuclear@0: { nuclear@0: mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(iNM++)); nuclear@0: } else nuclear@0: if (std::string::npos != sz.find( "spec", s) || std::string::npos != sz.find( "glanz", s)) nuclear@0: { nuclear@0: mat->AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR(iSM++)); nuclear@0: } else nuclear@0: if (std::string::npos != sz.find( "ambi", s) || std::string::npos != sz.find( "env", s)) nuclear@0: { nuclear@0: mat->AddProperty( &tex, AI_MATKEY_TEXTURE_AMBIENT(iAM++)); nuclear@0: } else nuclear@0: if (std::string::npos != sz.find( "emissive", s) || std::string::npos != sz.find( "self", s)) nuclear@0: { nuclear@0: mat->AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE(iEM++)); nuclear@0: } else nuclear@0: { nuclear@0: // Assume it is a diffuse texture nuclear@0: mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(iDM++)); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: pScene->mMaterials[pScene->mNumMaterials] = mat; nuclear@0: oldMat.sceneIndex = pScene->mNumMaterials; nuclear@0: pScene->mNumMaterials++; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: #endif // !! ASSIMP_BUILD_NO_X_IMPORTER nuclear@0: