diff options
Diffstat (limited to 'libs/assimp/code/AssetLib/X/XFileImporter.cpp')
-rw-r--r-- | libs/assimp/code/AssetLib/X/XFileImporter.cpp | 691 |
1 files changed, 691 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/X/XFileImporter.cpp b/libs/assimp/code/AssetLib/X/XFileImporter.cpp new file mode 100644 index 0000000..d23eb57 --- /dev/null +++ b/libs/assimp/code/AssetLib/X/XFileImporter.cpp @@ -0,0 +1,691 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file XFileImporter.cpp + * @brief Implementation of the XFile importer class + */ + +#ifndef ASSIMP_BUILD_NO_X_IMPORTER + +#include "AssetLib/X/XFileImporter.h" +#include "AssetLib/X/XFileParser.h" +#include "PostProcessing/ConvertToLHProcess.h" + +#include <assimp/TinyFormatter.h> +#include <assimp/IOSystem.hpp> +#include <assimp/scene.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/importerdesc.h> + +#include <cctype> +#include <memory> + +using namespace Assimp; +using namespace Assimp::Formatter; + +static const aiImporterDesc desc = { + "Direct3D XFile Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour, + 1, + 3, + 1, + 5, + "x" +}; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +XFileImporter::XFileImporter() +: mBuffer() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +XFileImporter::~XFileImporter() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const { + static const uint32_t token[] = { AI_MAKE_MAGIC("xof ") }; + return CheckMagicToken(pIOHandler,pFile,token,AI_COUNT_OF(token)); +} + +// ------------------------------------------------------------------------------------------------ +// Get file extension list +const aiImporterDesc* XFileImporter::GetInfo () const { + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { + // read file into memory + std::unique_ptr<IOStream> file( pIOHandler->Open( pFile)); + if ( file.get() == nullptr ) { + throw DeadlyImportError( "Failed to open file ", pFile, "." ); + } + + static const size_t MinSize = 16; + size_t fileSize = file->FileSize(); + if ( fileSize < MinSize ) { + throw DeadlyImportError( "XFile is too small." ); + } + + // in the hope that binary files will never start with a BOM ... + mBuffer.resize( fileSize + 1); + file->Read( &mBuffer.front(), 1, fileSize); + ConvertToUTF8(mBuffer); + + // parse the file into a temporary representation + XFileParser parser( mBuffer); + + // and create the proper return structures out of it + CreateDataRepresentationFromImport( pScene, parser.GetImportedData()); + + // if nothing came from it, report it as error + if ( !pScene->mRootNode ) { + throw DeadlyImportError( "XFile is ill-formatted - no content imported." ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Constructs the return data structure out of the imported data. +void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, XFile::Scene* pData) +{ + // Read the global materials first so that meshes referring to them can find them later + ConvertMaterials( pScene, pData->mGlobalMaterials); + + // copy nodes, extracting meshes and materials on the way + pScene->mRootNode = CreateNodes( pScene, nullptr, pData->mRootNode); + + // extract animations + CreateAnimations( pScene, pData); + + // read the global meshes that were stored outside of any node + if( !pData->mGlobalMeshes.empty() ) { + // create a root node to hold them if there isn't any, yet + if( pScene->mRootNode == nullptr ) { + pScene->mRootNode = new aiNode; + pScene->mRootNode->mName.Set( "$dummy_node"); + } + + // convert all global meshes and store them in the root node. + // If there was one before, the global meshes now suddenly have its transformation matrix... + // Don't know what to do there, I don't want to insert another node under the present root node + // just to avoid this. + CreateMeshes( pScene, pScene->mRootNode, pData->mGlobalMeshes); + } + + if (!pScene->mRootNode) { + throw DeadlyImportError( "No root node" ); + } + + // Convert everything to OpenGL space... it's the same operation as the conversion back, so we can reuse the step directly + MakeLeftHandedProcess convertProcess; + convertProcess.Execute( pScene); + + FlipWindingOrderProcess flipper; + flipper.Execute(pScene); + + // finally: create a dummy material if not material was imported + if( pScene->mNumMaterials == 0) { + pScene->mNumMaterials = 1; + // create the Material + aiMaterial* mat = new aiMaterial; + int shadeMode = (int) aiShadingMode_Gouraud; + mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); + // material colours + int specExp = 1; + + aiColor3D clr = aiColor3D( 0, 0, 0); + mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_EMISSIVE); + mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_SPECULAR); + + clr = aiColor3D( 0.5f, 0.5f, 0.5f); + mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_DIFFUSE); + mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS); + + pScene->mMaterials = new aiMaterial*[1]; + pScene->mMaterials[0] = mat; + } +} + +// ------------------------------------------------------------------------------------------------ +// Recursively creates scene nodes from the imported hierarchy. +aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode) { + if ( !pNode ) { + return nullptr; + } + + // create node + aiNode* node = new aiNode; + node->mName.length = (ai_uint32)pNode->mName.length(); + node->mParent = pParent; + memcpy( node->mName.data, pNode->mName.c_str(), pNode->mName.length()); + node->mName.data[node->mName.length] = 0; + node->mTransformation = pNode->mTrafoMatrix; + + // convert meshes from the source node + CreateMeshes( pScene, node, pNode->mMeshes); + + // handle children + if( !pNode->mChildren.empty() ) { + node->mNumChildren = (unsigned int)pNode->mChildren.size(); + node->mChildren = new aiNode* [node->mNumChildren]; + + for ( unsigned int a = 0; a < pNode->mChildren.size(); ++a ) { + node->mChildren[ a ] = CreateNodes( pScene, node, pNode->mChildren[ a ] ); + } + } + + return node; +} + +// ------------------------------------------------------------------------------------------------ +// Creates the meshes for the given node. +void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector<XFile::Mesh*>& pMeshes) { + if (pMeshes.empty()) { + return; + } + + // create a mesh for each mesh-material combination in the source node + std::vector<aiMesh*> meshes; + for( unsigned int a = 0; a < pMeshes.size(); ++a ) { + XFile::Mesh* sourceMesh = pMeshes[a]; + if ( nullptr == sourceMesh ) { + continue; + } + + // first convert its materials so that we can find them with their index afterwards + ConvertMaterials( pScene, sourceMesh->mMaterials); + + unsigned int numMaterials = std::max( (unsigned int)sourceMesh->mMaterials.size(), 1u); + for( unsigned int b = 0; b < numMaterials; ++b ) { + // collect the faces belonging to this material + std::vector<unsigned int> faces; + unsigned int numVertices = 0; + if( !sourceMesh->mFaceMaterials.empty() ) { + // if there is a per-face material defined, select the faces with the corresponding material + for( unsigned int c = 0; c < sourceMesh->mFaceMaterials.size(); ++c ) { + if( sourceMesh->mFaceMaterials[c] == b) { + faces.push_back( c); + numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size(); + } + } + } else { + // if there is no per-face material, place everything into one mesh + for( unsigned int c = 0; c < sourceMesh->mPosFaces.size(); ++c ) { + faces.push_back( c); + numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size(); + } + } + + // no faces/vertices using this material? strange... + if ( numVertices == 0 ) { + continue; + } + + // create a submesh using this material + aiMesh* mesh = new aiMesh; + meshes.push_back( mesh); + + // find the material in the scene's material list. Either own material + // or referenced material, it should already have a valid index + if( !sourceMesh->mFaceMaterials.empty() ) { + mesh->mMaterialIndex = static_cast<unsigned int>(sourceMesh->mMaterials[b].sceneIndex); + } else { + mesh->mMaterialIndex = 0; + } + + // Create properly sized data arrays in the mesh. We store unique vertices per face, + // as specified + mesh->mNumVertices = numVertices; + mesh->mVertices = new aiVector3D[numVertices]; + mesh->mNumFaces = (unsigned int)faces.size(); + mesh->mFaces = new aiFace[mesh->mNumFaces]; + + // name + mesh->mName.Set(sourceMesh->mName); + + // normals? + if ( sourceMesh->mNormals.size() > 0 ) { + mesh->mNormals = new aiVector3D[ numVertices ]; + } + // texture coords + for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c ) { + if ( !sourceMesh->mTexCoords[ c ].empty() ) { + mesh->mTextureCoords[ c ] = new aiVector3D[ numVertices ]; + } + } + // vertex colors + for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c ) { + if ( !sourceMesh->mColors[ c ].empty() ) { + mesh->mColors[ c ] = new aiColor4D[ numVertices ]; + } + } + + // now collect the vertex data of all data streams present in the imported mesh + unsigned int newIndex( 0 ); + std::vector<unsigned int> orgPoints; // from which original point each new vertex stems + orgPoints.resize( numVertices, 0); + + for( unsigned int c = 0; c < faces.size(); ++c ) { + unsigned int f = faces[c]; // index of the source face + const XFile::Face& pf = sourceMesh->mPosFaces[f]; // position source face + + // create face. either triangle or triangle fan depending on the index count + aiFace& df = mesh->mFaces[c]; // destination face + df.mNumIndices = (unsigned int)pf.mIndices.size(); + df.mIndices = new unsigned int[ df.mNumIndices]; + + // collect vertex data for indices of this face + for( unsigned int d = 0; d < df.mNumIndices; ++d ) { + df.mIndices[ d ] = newIndex; + const unsigned int newIdx( pf.mIndices[ d ] ); + if ( newIdx > sourceMesh->mPositions.size() ) { + continue; + } + + orgPoints[newIndex] = pf.mIndices[d]; + + // Position + mesh->mVertices[newIndex] = sourceMesh->mPositions[pf.mIndices[d]]; + // Normal, if present + if ( mesh->HasNormals() ) { + if ( sourceMesh->mNormFaces[ f ].mIndices.size() > d ) { + const size_t idx( sourceMesh->mNormFaces[ f ].mIndices[ d ] ); + mesh->mNormals[ newIndex ] = sourceMesh->mNormals[ idx ]; + } + } + + // texture coord sets + for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++e ) { + if( mesh->HasTextureCoords( e)) { + aiVector2D tex = sourceMesh->mTexCoords[e][pf.mIndices[d]]; + mesh->mTextureCoords[e][newIndex] = aiVector3D( tex.x, 1.0f - tex.y, 0.0f); + } + } + // vertex color sets + for ( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; ++e ) { + if ( mesh->HasVertexColors( e ) ) { + mesh->mColors[ e ][ newIndex ] = sourceMesh->mColors[ e ][ pf.mIndices[ d ] ]; + } + } + + newIndex++; + } + } + + // there should be as much new vertices as we calculated before + ai_assert( newIndex == numVertices); + + // convert all bones of the source mesh which influence vertices in this newly created mesh + const std::vector<XFile::Bone>& bones = sourceMesh->mBones; + std::vector<aiBone*> newBones; + for( unsigned int c = 0; c < bones.size(); ++c ) { + const XFile::Bone& obone = bones[c]; + // set up a vertex-linear array of the weights for quick searching if a bone influences a vertex + std::vector<ai_real> oldWeights( sourceMesh->mPositions.size(), 0.0); + for ( unsigned int d = 0; d < obone.mWeights.size(); ++d ) { + oldWeights[ obone.mWeights[ d ].mVertex ] = obone.mWeights[ d ].mWeight; + } + + // collect all vertex weights that influence a vertex in the new mesh + std::vector<aiVertexWeight> newWeights; + newWeights.reserve( numVertices); + for( unsigned int d = 0; d < orgPoints.size(); ++d ) { + // does the new vertex stem from an old vertex which was influenced by this bone? + ai_real w = oldWeights[orgPoints[d]]; + if ( w > 0.0 ) { + newWeights.push_back( aiVertexWeight( d, w ) ); + } + } + + // if the bone has no weights in the newly created mesh, ignore it + if ( newWeights.empty() ) { + continue; + } + + // create + aiBone* nbone = new aiBone; + newBones.push_back( nbone); + // copy name and matrix + nbone->mName.Set( obone.mName); + nbone->mOffsetMatrix = obone.mOffsetMatrix; + nbone->mNumWeights = (unsigned int)newWeights.size(); + nbone->mWeights = new aiVertexWeight[nbone->mNumWeights]; + for ( unsigned int d = 0; d < newWeights.size(); ++d ) { + nbone->mWeights[ d ] = newWeights[ d ]; + } + } + + // store the bones in the mesh + mesh->mNumBones = (unsigned int)newBones.size(); + if( !newBones.empty()) { + mesh->mBones = new aiBone*[mesh->mNumBones]; + std::copy( newBones.begin(), newBones.end(), mesh->mBones); + } + } + } + + // reallocate scene mesh array to be large enough + aiMesh** prevArray = pScene->mMeshes; + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes + meshes.size()]; + if( prevArray) { + memcpy( pScene->mMeshes, prevArray, pScene->mNumMeshes * sizeof( aiMesh*)); + delete [] prevArray; + } + + // allocate mesh index array in the node + pNode->mNumMeshes = (unsigned int)meshes.size(); + pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; + + // store all meshes in the mesh library of the scene and store their indices in the node + for( unsigned int a = 0; a < meshes.size(); a++) { + pScene->mMeshes[pScene->mNumMeshes] = meshes[a]; + pNode->mMeshes[a] = pScene->mNumMeshes; + pScene->mNumMeshes++; + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts the animations from the given imported data and creates them in the scene. +void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData) { + std::vector<aiAnimation*> newAnims; + + for( unsigned int a = 0; a < pData->mAnims.size(); ++a ) { + const XFile::Animation* anim = pData->mAnims[a]; + // some exporters mock me with empty animation tags. + if ( anim->mAnims.empty() ) { + continue; + } + + // create a new animation to hold the data + aiAnimation* nanim = new aiAnimation; + newAnims.push_back( nanim); + nanim->mName.Set( anim->mName); + // duration will be determined by the maximum length + nanim->mDuration = 0; + nanim->mTicksPerSecond = pData->mAnimTicksPerSecond; + nanim->mNumChannels = (unsigned int)anim->mAnims.size(); + nanim->mChannels = new aiNodeAnim*[nanim->mNumChannels]; + + for( unsigned int b = 0; b < anim->mAnims.size(); ++b ) { + const XFile::AnimBone* bone = anim->mAnims[b]; + aiNodeAnim* nbone = new aiNodeAnim; + nbone->mNodeName.Set( bone->mBoneName); + nanim->mChannels[b] = nbone; + + // key-frames are given as combined transformation matrix keys + if( !bone->mTrafoKeys.empty() ) + { + nbone->mNumPositionKeys = (unsigned int)bone->mTrafoKeys.size(); + nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys]; + nbone->mNumRotationKeys = (unsigned int)bone->mTrafoKeys.size(); + nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys]; + nbone->mNumScalingKeys = (unsigned int)bone->mTrafoKeys.size(); + nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys]; + + for( unsigned int c = 0; c < bone->mTrafoKeys.size(); ++c) { + // deconstruct each matrix into separate position, rotation and scaling + double time = bone->mTrafoKeys[c].mTime; + aiMatrix4x4 trafo = bone->mTrafoKeys[c].mMatrix; + + // extract position + aiVector3D pos( trafo.a4, trafo.b4, trafo.c4); + + nbone->mPositionKeys[c].mTime = time; + nbone->mPositionKeys[c].mValue = pos; + + // extract scaling + aiVector3D scale; + scale.x = aiVector3D( trafo.a1, trafo.b1, trafo.c1).Length(); + scale.y = aiVector3D( trafo.a2, trafo.b2, trafo.c2).Length(); + scale.z = aiVector3D( trafo.a3, trafo.b3, trafo.c3).Length(); + nbone->mScalingKeys[c].mTime = time; + nbone->mScalingKeys[c].mValue = scale; + + // reconstruct rotation matrix without scaling + aiMatrix3x3 rotmat( + trafo.a1 / scale.x, trafo.a2 / scale.y, trafo.a3 / scale.z, + trafo.b1 / scale.x, trafo.b2 / scale.y, trafo.b3 / scale.z, + trafo.c1 / scale.x, trafo.c2 / scale.y, trafo.c3 / scale.z); + + // and convert it into a quaternion + nbone->mRotationKeys[c].mTime = time; + nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat); + } + + // longest lasting key sequence determines duration + nanim->mDuration = std::max( nanim->mDuration, bone->mTrafoKeys.back().mTime); + } else { + // separate key sequences for position, rotation, scaling + nbone->mNumPositionKeys = (unsigned int)bone->mPosKeys.size(); + if (nbone->mNumPositionKeys != 0) { + nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys]; + for( unsigned int c = 0; c < nbone->mNumPositionKeys; ++c ) { + aiVector3D pos = bone->mPosKeys[c].mValue; + + nbone->mPositionKeys[c].mTime = bone->mPosKeys[c].mTime; + nbone->mPositionKeys[c].mValue = pos; + } + } + + // rotation + nbone->mNumRotationKeys = (unsigned int)bone->mRotKeys.size(); + if (nbone->mNumRotationKeys != 0) { + nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys]; + for( unsigned int c = 0; c < nbone->mNumRotationKeys; ++c ) { + aiMatrix3x3 rotmat = bone->mRotKeys[c].mValue.GetMatrix(); + + nbone->mRotationKeys[c].mTime = bone->mRotKeys[c].mTime; + nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat); + nbone->mRotationKeys[c].mValue.w *= -1.0f; // needs quat inversion + } + } + + // scaling + nbone->mNumScalingKeys = (unsigned int)bone->mScaleKeys.size(); + if (nbone->mNumScalingKeys != 0) { + nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys]; + for( unsigned int c = 0; c < nbone->mNumScalingKeys; c++) + nbone->mScalingKeys[c] = bone->mScaleKeys[c]; + } + + // longest lasting key sequence determines duration + if( bone->mPosKeys.size() > 0) + nanim->mDuration = std::max( nanim->mDuration, bone->mPosKeys.back().mTime); + if( bone->mRotKeys.size() > 0) + nanim->mDuration = std::max( nanim->mDuration, bone->mRotKeys.back().mTime); + if( bone->mScaleKeys.size() > 0) + nanim->mDuration = std::max( nanim->mDuration, bone->mScaleKeys.back().mTime); + } + } + } + + // store all converted animations in the scene + if( newAnims.size() > 0) + { + pScene->mNumAnimations = (unsigned int)newAnims.size(); + pScene->mAnimations = new aiAnimation* [pScene->mNumAnimations]; + for( unsigned int a = 0; a < newAnims.size(); a++) + pScene->mAnimations[a] = newAnims[a]; + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts all materials in the given array and stores them in the scene's material list. +void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector<XFile::Material>& pMaterials) +{ + // count the non-referrer materials in the array + unsigned int numNewMaterials( 0 ); + for ( unsigned int a = 0; a < pMaterials.size(); ++a ) { + if ( !pMaterials[ a ].mIsReference ) { + ++numNewMaterials; + } + } + + // resize the scene's material list to offer enough space for the new materials + if( numNewMaterials > 0 ) { + aiMaterial** prevMats = pScene->mMaterials; + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials + numNewMaterials]; + if( nullptr != prevMats) { + ::memcpy( pScene->mMaterials, prevMats, pScene->mNumMaterials * sizeof( aiMaterial*)); + delete [] prevMats; + } + } + + // convert all the materials given in the array + for( unsigned int a = 0; a < pMaterials.size(); ++a ) { + XFile::Material& oldMat = pMaterials[a]; + if( oldMat.mIsReference) { + // find the material it refers to by name, and store its index + for( size_t b = 0; b < pScene->mNumMaterials; ++b ) { + aiString name; + pScene->mMaterials[b]->Get( AI_MATKEY_NAME, name); + if( strcmp( name.C_Str(), oldMat.mName.data()) == 0 ) { + oldMat.sceneIndex = a; + break; + } + } + + if( oldMat.sceneIndex == SIZE_MAX ) { + ASSIMP_LOG_WARN( "Could not resolve global material reference \"", oldMat.mName, "\"" ); + oldMat.sceneIndex = 0; + } + + continue; + } + + aiMaterial* mat = new aiMaterial; + aiString name; + name.Set( oldMat.mName); + mat->AddProperty( &name, AI_MATKEY_NAME); + + // Shading model: hard-coded to PHONG, there is no such information in an XFile + // FIX (aramis): If the specular exponent is 0, use gouraud shading. This is a bugfix + // for some models in the SDK (e.g. good old tiny.x) + int shadeMode = (int)oldMat.mSpecularExponent == 0.0f + ? aiShadingMode_Gouraud : aiShadingMode_Phong; + + mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); + // material colours + // Unclear: there's no ambient colour, but emissive. What to put for ambient? + // Probably nothing at all, let the user select a suitable default. + mat->AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); + mat->AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + mat->AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); + mat->AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS); + + + // texture, if there is one + if (1 == oldMat.mTextures.size() ) { + const XFile::TexEntry& otex = oldMat.mTextures.back(); + if (otex.mName.length()) { + // if there is only one texture assume it contains the diffuse color + aiString tex( otex.mName); + if ( otex.mIsNormalMap ) { + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS( 0 ) ); + } else { + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE( 0 ) ); + } + } + } else { + // Otherwise ... try to search for typical strings in the + // texture's file name like 'bump' or 'diffuse' + unsigned int iHM = 0,iNM = 0,iDM = 0,iSM = 0,iAM = 0,iEM = 0; + for( unsigned int b = 0; b < oldMat.mTextures.size(); ++b ) { + const XFile::TexEntry& otex = oldMat.mTextures[b]; + std::string sz = otex.mName; + if ( !sz.length() ) { + continue; + } + + // find the file name + std::string::size_type s = sz.find_last_of("\\/"); + if ( std::string::npos == s ) { + s = 0; + } + + // cut off the file extension + std::string::size_type sExt = sz.find_last_of('.'); + if (std::string::npos != sExt){ + sz[sExt] = '\0'; + } + + // convert to lower case for easier comparison + for ( unsigned int c = 0; c < sz.length(); ++c ) { + sz[ c ] = (char) tolower( (unsigned char) sz[ c ] ); + } + + // Place texture filename property under the corresponding name + aiString tex( oldMat.mTextures[b].mName); + + // bump map + if (std::string::npos != sz.find("bump", s) || std::string::npos != sz.find("height", s)) { + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_HEIGHT(iHM++)); + } else if (otex.mIsNormalMap || std::string::npos != sz.find( "normal", s) || std::string::npos != sz.find("nm", s)) { + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(iNM++)); + } else if (std::string::npos != sz.find( "spec", s) || std::string::npos != sz.find( "glanz", s)) { + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR(iSM++)); + } else if (std::string::npos != sz.find( "ambi", s) || std::string::npos != sz.find( "env", s)) { + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_AMBIENT(iAM++)); + } else if (std::string::npos != sz.find( "emissive", s) || std::string::npos != sz.find( "self", s)) { + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE(iEM++)); + } else { + // Assume it is a diffuse texture + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(iDM++)); + } + } + } + + pScene->mMaterials[pScene->mNumMaterials] = mat; + oldMat.sceneIndex = pScene->mNumMaterials; + pScene->mNumMaterials++; + } +} + +#endif // !! ASSIMP_BUILD_NO_X_IMPORTER |