From 7678b8e1becce39ee9599c1ef0f376288730afd8 Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Sun, 8 Feb 2009 22:55:51 +0000 Subject: [PATCH] Collada - added support for lights and cameras - added support for tangents and bitangents - added support for more than 2 UV components. - fixed node naming - beta support for instance_node elements. Works in most cases. - added support for more complex materials - UV index is now set correctly. hopefully. Material system - fixed potential problems regarding aiUVTransform - added utility macros for the base keys git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@338 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/ColladaHelper.h | 289 +++++++++++++-- code/ColladaLoader.cpp | 556 ++++++++++++++++++++++++---- code/ColladaLoader.h | 49 ++- code/ColladaParser.cpp | 806 +++++++++++++++++++++++++++++++++-------- code/ColladaParser.h | 56 ++- code/MaterialSystem.h | 13 + include/aiMaterial.h | 78 ++-- 7 files changed, 1559 insertions(+), 288 deletions(-) diff --git a/code/ColladaHelper.h b/code/ColladaHelper.h index 36cf9a135..95f5814b5 100644 --- a/code/ColladaHelper.h +++ b/code/ColladaHelper.h @@ -43,10 +43,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_COLLADAHELPER_H_INC #define AI_COLLADAHELPER_H_INC -namespace Assimp -{ -namespace Collada -{ +namespace Assimp { +namespace Collada { /** Transformation types that can be applied to a node */ enum TransformType @@ -59,6 +57,19 @@ enum TransformType TF_MATRIX }; +/** Different types of input data to a vertex or face */ +enum InputType +{ + IT_Invalid, + IT_Vertex, // special type for per-index data referring to the element carrying the per-vertex data. + IT_Position, + IT_Normal, + IT_Texcoord, + IT_Color, + IT_Tangent, + IT_Bitangent, +}; + /** Contains all data for one of the different transformation types */ struct Transform { @@ -66,11 +77,135 @@ struct Transform float f[16]; ///< Interpretation of data depends on the type of the transformation }; +/** A collada camera. */ +struct Camera +{ + Camera() + : mOrtho (false) + , mHorFov (10e10f) + , mVerFov (10e10f) + , mAspect (10e10f) + , mZNear (0.1f) + , mZFar (1000.f) + {} + + // Name of camera + std::string mName; + + // True if it is an orthografic camera + bool mOrtho; + + //! Horizontal field of view in degrees + float mHorFov; + + //! Vertical field of view in degrees + float mVerFov; + + //! Screen aspect + float mAspect; + + //! Near& far z + float mZNear, mZFar; +}; + +#define aiLightSource_AMBIENT 0xdeaddead + +/** A collada light source. */ +struct Light +{ + Light() + : mAttConstant (1.f) + , mAttLinear (0.f) + , mAttQuadratic (0.f) + , mFalloffAngle (180.f) + , mFalloffExponent (0.f) + , mPenumbraAngle (10e10f) + , mOuterAngle (10e10f) + , mIntensity (1.f) + {} + + //! Type of the light source aiLightSourceType + ambient + unsigned int mType; + + //! Color of the light + aiColor3D mColor; + + //! Light attenuation + float mAttConstant,mAttLinear,mAttQuadratic; + + //! Spot light falloff + float mFalloffAngle; + float mFalloffExponent; + + // ----------------------------------------------------- + // FCOLLADA extension from here + + //! ... related stuff from maja and max extensions + float mPenumbraAngle; + float mOuterAngle; + + //! Common light intensity + float mIntensity; +}; + +/** Short vertex index description */ +struct InputSemanticMapEntry +{ + InputSemanticMapEntry() + : mSet (0) + {} + + //! Index of set, optional + unsigned int mSet; + + //! Name of referenced vertex input + InputType mType; +}; + +/** Table to map from effect to vertex input semantics */ +struct SemanticMappingTable +{ + //! Name of material + std::string mMatName; + + //! List of semantic map commands, grouped by effect semantic name + std::map mMap; + + //! For std::find + bool operator == (const std::string& s) const { + return s == mMatName; + } +}; + /** A reference to a mesh inside a node, including materials assigned to the various subgroups */ struct MeshInstance { - std::string mMesh; ///< ID of the mesh - std::map mMaterials; ///< Map of materials by the subgroup ID they're applied to + ///< ID of the mesh + std::string mMesh; + + ///< Map of materials by the subgroup ID they're applied to + std::map mMaterials; +}; + +/** A reference to a camera inside a node*/ +struct CameraInstance +{ + ///< ID of the camera + std::string mCamera; +}; + +/** A reference to a light inside a node*/ +struct LightInstance +{ + ///< ID of the camera + std::string mLight; +}; + +/** A reference to a node inside a node*/ +struct NodeInstance +{ + ///< ID of the node + std::string mNode; }; /** A node in a scene hierarchy */ @@ -84,10 +219,31 @@ struct Node /** Operations in order to calculate the resulting transformation to parent. */ std::vector mTransforms; - std::vector mMeshes; ///< Meshes at this node + /** Meshes at this node */ + std::vector mMeshes; - Node() { mParent = NULL; } - ~Node() { for( std::vector::iterator it = mChildren.begin(); it != mChildren.end(); ++it) delete *it; } + /** Lights at this node */ + std::vector mLights; + + /** Cameras at this node */ + std::vector mCameras; + + /** Node instances at this node */ + std::vector mNodeInstances; + + /** Rootnodes: Name of primary camera, if any */ + std::string mPrimaryCamera; + + //! Constructor. Begin with a zero parent + Node() { + mParent = NULL; + } + + //! Destructor: delete all children subsequently + ~Node() { + for( std::vector::iterator it = mChildren.begin(); it != mChildren.end(); ++it) + delete *it; + } }; /** Data source array */ @@ -121,17 +277,6 @@ struct Face std::vector mIndices; }; -/** Different types of input data to a vertex or face */ -enum InputType -{ - IT_Invalid, - IT_Vertex, // special type for per-index data referring to the element carrying the per-vertex data. - IT_Position, - IT_Normal, - IT_Texcoord, - IT_Color -}; - /** An input channel for mesh data, referring to a single accessor */ struct InputChannel { @@ -154,14 +299,24 @@ struct SubMesh /** Contains data for a single mesh */ struct Mesh { + Mesh() + { + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) + mNumUVComponents[i] = 2; + } + std::string mVertexID; // just to check if there's some sophisticated addressing involved... which we don't support, and therefore should warn about. std::vector mPerVertexData; // Vertex data addressed by vertex indices // actual mesh data, assembled on encounter of a

element. Verbose format, not indexed std::vector mPositions; std::vector mNormals; - std::vector mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; - std::vector mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; + std::vector mTangents; + std::vector mBitangents; + std::vector mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + std::vector mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; + + unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS]; // Faces. Stored are only the number of vertices for each face. 1 == point, 2 == line, 3 == triangle, 4+ == poly std::vector mFaceSize; @@ -213,30 +368,98 @@ enum ShadeType Shade_Blinn }; +/** Represents a texture sampler in collada */ +struct Sampler +{ + Sampler() + : mWrapU (true) + , mWrapV (true) + , mMirrorU (true) + , mMirrorV (true) + , mOp (aiTextureOp_Multiply) + , mUVId (0xffffffff) + , mWeighting (1.f) + , mMixWithPrevious (1.f) + {} + + // Name of image reference + std::string mName; + + // Wrap U? + bool mWrapU; + + // Wrap V? + bool mWrapV; + + // Mirror U? + bool mMirrorU; + + // Mirror V? + bool mMirrorV; + + // Blend mode + aiTextureOp mOp; + + // UV transformation + aiUVTransform mTransform; + + // Name of source UV channel + std::string mUVChannel; + + // Resolved UV channel index or 0xffffffff if not known + unsigned int mUVId; + + // OKINO/MAX3D extensions from here + // ------------------------------------------------------- + + // Weighting factor + float mWeighting; + + // Mixing factor from OKINO + float mMixWithPrevious; +}; /** A collada effect. Can contain about anything according to the Collada spec, but we limit our version to a reasonable subset. */ struct Effect { + // Shading mode ShadeType mShadeType; + + // Colors aiColor4D mEmissive, mAmbient, mDiffuse, mSpecular; - aiColor4D mReflective, mRefractive; - std::string mTexEmissive, mTexAmbient, mTexDiffuse, mTexSpecular; + aiColor4D mTransparent; + + // Textures + Sampler mTexEmissive, mTexAmbient, mTexDiffuse, mTexSpecular, + mTexTransparent, mTexBump; + + // Scalar factory float mShininess, mRefractIndex; - float mReflectivity, mRefractivity; + float mTransparency; // local params referring to each other by their SID typedef std::map ParamLibrary; ParamLibrary mParams; + + // MAX3D extensions + // --------------------------------------------------------- + // Double-sided? + bool mDoubleSided, mWireframe, mFaceted; - Effect() : mEmissive( 0, 0, 0, 1), mAmbient( 0.1f, 0.1f, 0.1f, 1), - mDiffuse( 0.6f, 0.6f, 0.6f, 1), mSpecular( 0.4f, 0.4f, 0.4f, 1), - mReflective( 0, 0, 0, 0), mRefractive( 0, 0, 0, 0) + Effect() + : mShadeType (Shade_Phong) + , mEmissive ( 0, 0, 0, 1) + , mAmbient ( 0.1f, 0.1f, 0.1f, 1) + , mDiffuse ( 0.6f, 0.6f, 0.6f, 1) + , mSpecular ( 0.4f, 0.4f, 0.4f, 1) + , mTransparent ( 0, 0, 0, 1) + , mShininess (10.0f) + , mRefractIndex (1.f) + , mTransparency (0.f) + , mDoubleSided (false) + , mWireframe (false) + , mFaceted (false) { - mShadeType = Shade_Phong; - mShininess = 10.0f; - mRefractIndex = 1.0f; - mReflectivity = 0.0f; - mRefractivity = 0.0f; } }; diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index fd120e06a..7ac18c415 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -48,6 +48,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "ColladaLoader.h" #include "ColladaParser.h" +#include "fast_atof.h" +#include "ParsingUtils.h" + +#include "time.h" + using namespace Assimp; // ------------------------------------------------------------------------------------------------ @@ -95,11 +100,19 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I { mFileName = pFile; + // clean all member arrays - just for safety, it should work even if we did not + mMeshIndexByID.clear(); + mMaterialIndexByName.clear(); + mMeshes.clear(); + newMats.clear(); + mLights.clear(); + mCameras.clear(); + // parse the input file ColladaParser parser( pFile); if( !parser.mRootNode) - throw new ImportErrorException( "File came out empty. Something is wrong here."); + throw new ImportErrorException( "Collada: File came out empty. Something is wrong here."); // create the materials first, for the meshes to find BuildMaterials( parser, pScene); @@ -107,6 +120,9 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I // build the node hierarchy from it pScene->mRootNode = BuildHierarchy( parser, parser.mRootNode); + // ... then fill the materials with the now adjusted settings + FillMaterials(parser, pScene); + // Convert to Z_UP, if different orientation if( parser.mUpDirection == ColladaParser::UP_X) pScene->mRootNode->mTransformation *= aiMatrix4x4( @@ -123,6 +139,25 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I // store all meshes StoreSceneMeshes( pScene); + + // store all materials + StoreSceneMaterials( pScene); + + // store all lights + StoreSceneLights( pScene); + + // if we know which camera is the primary camera, copy it to index 0 + if (0 == parser.mRootNode->mPrimaryCamera.length()) { + for (unsigned int i = 1; i < mCameras.size(); ++i) { + if (mCameras[i]->mName == parser.mRootNode->mPrimaryCamera) { + std::swap(mCameras[i],mCameras[0]); + break; + } + } + } + + // store all cameras + StoreSceneCameras( pScene); } // ------------------------------------------------------------------------------------------------ @@ -130,26 +165,229 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode) { // create a node for it - aiNode* node = new aiNode( pNode->mName); + aiNode* node = new aiNode(); + + // now setup the name of the node. We take the name if not empty, otherwise the collada ID + if (!pNode->mName.empty()) + node->mName.Set(pNode->mName); + else if (!pNode->mID.empty()) + node->mName.Set(pNode->mID); + else + { + // No need to worry. Unnamed nodes are no problem at all, except + // if cameras or lights need to be assigned to them. + if (!pNode->mLights.empty() || !pNode->mCameras.empty()) { + + ::strcpy(node->mName.data,"$ColladaAutoName$_"); + node->mName.length = 18 + ASSIMP_itoa10(node->mName.data+18,MAXLEN-18,(uint32_t)clock()); + } + } // calculate the transformation matrix for it node->mTransformation = pParser.CalculateResultTransform( pNode->mTransforms); - // add children - node->mNumChildren = pNode->mChildren.size(); + // now resolve node instances + std::vector instances; + ResolveNodeInstances(pParser,pNode,instances); + + // add children. first the *real* ones + node->mNumChildren = pNode->mChildren.size()+instances.size(); node->mChildren = new aiNode*[node->mNumChildren]; - for( unsigned int a = 0; a < pNode->mChildren.size(); a++) + + unsigned int a = 0; + for(; a < pNode->mChildren.size(); a++) { node->mChildren[a] = BuildHierarchy( pParser, pNode->mChildren[a]); node->mChildren[a]->mParent = node; } + // ... and finally the resolved node instances + for(; a < node->mNumChildren; a++) + { + node->mChildren[a] = BuildHierarchy( pParser, instances[a-pNode->mChildren.size()]); + node->mChildren[a]->mParent = node; + } + // construct meshes BuildMeshesForNode( pParser, pNode, node); + // construct cameras + BuildCamerasForNode(pParser, pNode, node); + + // construct lights + BuildLightsForNode(pParser, pNode, node); return node; } +// ------------------------------------------------------------------------------------------------ +// Resolve node instances +void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode, + std::vector& resolved) +{ + // reserve enough storage + resolved.reserve(pNode->mNodeInstances.size()); + + // ... and iterate through all nodes to be instanced as children of pNode + for (std::vector::const_iterator it = pNode->mNodeInstances.begin(), + end = pNode->mNodeInstances.end(); it != end; ++it) + { + // find the corresponding node in the library + ColladaParser::NodeLibrary::const_iterator fnd = pParser.mNodeLibrary.find((*it).mNode); + if (fnd == pParser.mNodeLibrary.end()) + DefaultLogger::get()->error("Collada: Unable to resolve reference to instanced node " + (*it).mNode); + + else { + // attach this node to the list of children + resolved.push_back((*fnd).second); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Resolve UV channels +void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler, + const Collada::SemanticMappingTable& table) +{ + std::map::const_iterator it = table.mMap.find(sampler.mUVChannel); + if (it != table.mMap.end()) { + if (it->second.mType != Collada::IT_Texcoord) + DefaultLogger::get()->error("Collada: Unexpected effect input mapping"); + + sampler.mUVId = it->second.mSet; + } +} + +// ------------------------------------------------------------------------------------------------ +// Builds lights for the given node and references them +void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget) +{ + BOOST_FOREACH( const Collada::LightInstance& lid, pNode->mLights) + { + // find the referred light + ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find( lid.mLight); + if( srcLightIt == pParser.mLightLibrary.end()) + { + DefaultLogger::get()->warn("Collada: Unable to find light for ID \"" + lid.mLight + "\". Skipping."); + continue; + } + const Collada::Light* srcLight = &srcLightIt->second; + if (srcLight->mType == aiLightSource_AMBIENT) { + DefaultLogger::get()->error("Collada: Skipping ambient light for the moment"); + continue; + } + + // now fill our ai data structure + aiLight* out = new aiLight(); + out->mName = pTarget->mName; + out->mType = (aiLightSourceType)srcLight->mType; + + // collada lights point in -Z by default, rest is specified in node transform + out->mDirection = aiVector3D(0.f,0.f,-1.f); + + out->mAttenuationConstant = srcLight->mAttConstant; + out->mAttenuationLinear = srcLight->mAttLinear; + out->mAttenuationQuadratic = srcLight->mAttQuadratic; + + // collada doesn't differenciate between these color types + out->mColorDiffuse = out->mColorSpecular = out->mColorAmbient = srcLight->mColor*srcLight->mIntensity; + + // convert falloff angle and falloff exponent in our representation, if given + if (out->mType == aiLightSource_SPOT) { + + out->mAngleInnerCone = AI_DEG_TO_RAD( srcLight->mFalloffAngle ); + + // ... some extension magic. FUCKING COLLADA. + if (srcLight->mOuterAngle == 10e10f) + { + // ... some deprecation magic. FUCKING FCOLLADA. + if (srcLight->mPenumbraAngle == 10e10f) + { + // Need to rely on falloff_exponent. I don't know how to interpret it, so I need to guess .... + // ci - inner cone angle + // co - outer cone angle + // fe - falloff exponent + // ld - spot direction - normalized + // rd - ray direction - normalized + // + // Formula is: + // 1. (cos(acos (ld dot rd) - ci))^fe == epsilon + // 2. (ld dot rd) == cos(acos(epsilon^(1/fe)) + ci) + // 3. co == acos (ld dot rd) + // 4. co == acos(epsilon^(1/fe)) + ci) + + // epsilon chosen to be 0.1 + out->mAngleOuterCone = AI_DEG_TO_RAD (acos(pow(0.1f,1.f/srcLight->mFalloffExponent))+ + srcLight->mFalloffAngle); + } + else { + out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD( srcLight->mPenumbraAngle ); + if (out->mAngleOuterCone < out->mAngleInnerCone) + std::swap(out->mAngleInnerCone,out->mAngleOuterCone); + } + } + else out->mAngleOuterCone = AI_DEG_TO_RAD( srcLight->mOuterAngle ); + } + + // add to light list + mLights.push_back(out); + } +} + +// ------------------------------------------------------------------------------------------------ +// Builds cameras for the given node and references them +void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget) +{ + BOOST_FOREACH( const Collada::CameraInstance& cid, pNode->mCameras) + { + // find the referred light + ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find( cid.mCamera); + if( srcCameraIt == pParser.mCameraLibrary.end()) + { + DefaultLogger::get()->warn("Collada: Unable to find camera for ID \"" + cid.mCamera + "\". Skipping."); + continue; + } + const Collada::Camera* srcCamera = &srcCameraIt->second; + + // orthographic cameras not yet supported in Assimp + if (srcCamera->mOrtho) { + DefaultLogger::get()->warn("Collada: Orthographic cameras are not supported."); + } + + // now fill our ai data structure + aiCamera* out = new aiCamera(); + out->mName = pTarget->mName; + + // collada cameras point in -Z by default, rest is specified in node transform + out->mLookAt = aiVector3D(0.f,0.f,-1.f); + + // near/far z is already ok + out->mClipPlaneFar = srcCamera->mZFar; + out->mClipPlaneNear = srcCamera->mZNear; + + // ... but for the rest some values are optional + // and we need to compute the others in any combination. FUCKING COLLADA. + if (srcCamera->mAspect != 10e10f) + out->mAspect = srcCamera->mAspect; + + if (srcCamera->mHorFov != 10e10f) { + out->mHorizontalFOV = srcCamera->mHorFov; + + if (srcCamera->mVerFov != 10e10f && srcCamera->mAspect != 10e10f) { + out->mAspect = srcCamera->mHorFov/srcCamera->mVerFov; + } + } + else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) { + out->mHorizontalFOV = srcCamera->mAspect*srcCamera->mVerFov; + } + + // Collada uses degrees, we use radians + out->mHorizontalFOV = AI_DEG_TO_RAD(out->mHorizontalFOV); + + // add to camera list + mCameras.push_back(out); + } +} + // ------------------------------------------------------------------------------------------------ // Builds meshes for the given node and references them void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget) @@ -164,7 +402,7 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMesh); if( srcMeshIt == pParser.mMeshLibrary.end()) { - DefaultLogger::get()->warn( boost::str( boost::format( "Unable to find geometry for ID \"%s\". Skipping.") % mid.mMesh)); + DefaultLogger::get()->warn( boost::str( boost::format( "Collada: Unable to find geometry for ID \"%s\". Skipping.") % mid.mMesh)); continue; } const Collada::Mesh* srcMesh = srcMeshIt->second; @@ -174,16 +412,42 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll for( size_t sm = 0; sm < srcMesh->mSubMeshes.size(); ++sm) { const Collada::SubMesh& submesh = srcMesh->mSubMeshes[sm]; - if( submesh.mNumFaces == 0) - continue; + if( submesh.mNumFaces == 0) + continue; // find material assigned to this submesh - std::map::const_iterator meshMatIt = mid.mMaterials.find( submesh.mMaterial); - std::string meshMaterial; + std::map::const_iterator meshMatIt = mid.mMaterials.find( submesh.mMaterial); + + const Collada::SemanticMappingTable* table; if( meshMatIt != mid.mMaterials.end()) - meshMaterial = meshMatIt->second; + table = &meshMatIt->second; + else { + table = NULL; + DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup \"%s\" in geometry \"%s\".") % submesh.mMaterial % mid.mMesh)); + } + std::string& meshMaterial = table ? table->mMatName : ""; + + // OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table + // given. The only mapping stuff which we do actually support is the UV channel. + std::map::const_iterator matIt = mMaterialIndexByName.find( meshMaterial); + unsigned int matIdx; + if( matIt != mMaterialIndexByName.end()) + matIdx = matIt->second; else - DefaultLogger::get()->warn( boost::str( boost::format( "No material specified for subgroup \"%s\" in geometry \"%s\".") % submesh.mMaterial % mid.mMesh)); + matIdx = 0; + + if (table && !table->mMap.empty() ) { + std::pair& mat = newMats[matIdx]; + + // Iterate through all texture channels assigned to the effect and + // check whether we have mapping information for it. + ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table); + ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table); + ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table); + ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table); + ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent,*table); + ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table); + } // built lookup index of the Mesh-Submesh-Material combination ColladaMeshIndex index( mid.mMesh, sm, meshMaterial); @@ -199,20 +463,39 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll aiMesh* dstMesh = new aiMesh; // count the vertices addressed by its faces - size_t numVertices = - std::accumulate( srcMesh->mFaceSize.begin() + faceStart, srcMesh->mFaceSize.begin() + faceStart + submesh.mNumFaces, 0); + const size_t numVertices = std::accumulate( srcMesh->mFaceSize.begin() + faceStart, + srcMesh->mFaceSize.begin() + faceStart + submesh.mNumFaces, 0); // copy positions dstMesh->mNumVertices = numVertices; dstMesh->mVertices = new aiVector3D[numVertices]; - std::copy( srcMesh->mPositions.begin() + vertexStart, srcMesh->mPositions.begin() + vertexStart + numVertices, dstMesh->mVertices); + std::copy( srcMesh->mPositions.begin() + vertexStart, srcMesh->mPositions.begin() + + vertexStart + numVertices, dstMesh->mVertices); - // normals, if given. HACK: (thom) Due to the fucking Collada spec we never know if we have the same - // number of normals as there are positions. So we also ignore any vertex attribute if it has a different count + // normals, if given. HACK: (thom) Due to the fucking Collada spec we never + // know if we have the same number of normals as there are positions. So we + // also ignore any vertex attribute if it has a different count if( srcMesh->mNormals.size() == srcMesh->mPositions.size()) { dstMesh->mNormals = new aiVector3D[numVertices]; - std::copy( srcMesh->mNormals.begin() + vertexStart, srcMesh->mNormals.begin() + vertexStart + numVertices, dstMesh->mNormals); + std::copy( srcMesh->mNormals.begin() + vertexStart, srcMesh->mNormals.begin() + + vertexStart + numVertices, dstMesh->mNormals); + } + + // tangents, if given. + if( srcMesh->mTangents.size() == srcMesh->mPositions.size()) + { + dstMesh->mTangents = new aiVector3D[numVertices]; + std::copy( srcMesh->mTangents.begin() + vertexStart, srcMesh->mTangents.begin() + + vertexStart + numVertices, dstMesh->mTangents); + } + + // bitangents, if given. + if( srcMesh->mBitangents.size() == srcMesh->mPositions.size()) + { + dstMesh->mBitangents = new aiVector3D[numVertices]; + std::copy( srcMesh->mBitangents.begin() + vertexStart, srcMesh->mBitangents.begin() + + vertexStart + numVertices, dstMesh->mBitangents); } // same for texturecoords, as many as we have @@ -222,8 +505,9 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll { dstMesh->mTextureCoords[a] = new aiVector3D[numVertices]; for( size_t b = 0; b < numVertices; ++b) - dstMesh->mTextureCoords[a][b].Set( srcMesh->mTexCoords[a][vertexStart+b].x, srcMesh->mTexCoords[a][vertexStart+b].y, 0.0f); - dstMesh->mNumUVComponents[a] = 2; + dstMesh->mTextureCoords[a][b] = srcMesh->mTexCoords[a][vertexStart+b]; + + dstMesh->mNumUVComponents[a] = srcMesh->mNumUVComponents[a]; } } @@ -258,11 +542,7 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll vertexStart += numVertices; faceStart += submesh.mNumFaces; // assign the material index - std::map::const_iterator matIt = mMaterialIndexByName.find( meshMaterial); - if( matIt != mMaterialIndexByName.end()) - dstMesh->mMaterialIndex = matIt->second; - else - dstMesh->mMaterialIndex = 0; + dstMesh->mMaterialIndex = matIdx; } } } @@ -288,11 +568,185 @@ void ColladaLoader::StoreSceneMeshes( aiScene* pScene) } } +// ------------------------------------------------------------------------------------------------ +// Stores all cameras in the given scene +void ColladaLoader::StoreSceneCameras( aiScene* pScene) +{ + pScene->mNumCameras = mCameras.size(); + if( mCameras.size() > 0) + { + pScene->mCameras = new aiCamera*[mCameras.size()]; + std::copy( mCameras.begin(), mCameras.end(), pScene->mCameras); + } +} + +// ------------------------------------------------------------------------------------------------ +// Stores all lights in the given scene +void ColladaLoader::StoreSceneLights( aiScene* pScene) +{ + pScene->mNumLights = mLights.size(); + if( mLights.size() > 0) + { + pScene->mLights = new aiLight*[mLights.size()]; + std::copy( mLights.begin(), mLights.end(), pScene->mLights); + } +} + +// ------------------------------------------------------------------------------------------------ +// Stores all materials in the given scene +void ColladaLoader::StoreSceneMaterials( aiScene* pScene) +{ + pScene->mNumMaterials = newMats.size(); + + pScene->mMaterials = new aiMaterial*[newMats.size()]; + for (unsigned int i = 0; i < newMats.size();++i) + pScene->mMaterials[i] = newMats[i].second; +} + +// ------------------------------------------------------------------------------------------------ +// Add a texture to a material structure +void ColladaLoader::AddTexture ( Assimp::MaterialHelper& mat, const ColladaParser& pParser, + const Collada::Effect& effect, + const Collada::Sampler& sampler, + aiTextureType type, unsigned int idx) +{ + // first of all, basic file name + mat.AddProperty( &FindFilenameForEffectTexture( pParser, effect, sampler.mName), + _AI_MATKEY_TEXTURE_BASE,type,idx); + + // mapping mode + int map = map = aiTextureMapMode_Clamp; + if (sampler.mWrapU) + map = aiTextureMapMode_Wrap; + if (sampler.mWrapU && sampler.mMirrorU) + map = aiTextureMapMode_Mirror; + + mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_U_BASE, type, idx); + + map = aiTextureMapMode_Clamp; + if (sampler.mWrapV) + map = aiTextureMapMode_Wrap; + if (sampler.mWrapV && sampler.mMirrorV) + map = aiTextureMapMode_Mirror; + + mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_V_BASE, type, idx); + + // UV transformation + mat.AddProperty(&sampler.mTransform, 1, + _AI_MATKEY_UVTRANSFORM_BASE, type, idx); + + // Blend mode + mat.AddProperty((int*)&sampler.mOp , 1, + _AI_MATKEY_TEXBLEND_BASE, type, idx); + + // Blend factor + mat.AddProperty((float*)&sampler.mWeighting , 1, + _AI_MATKEY_TEXBLEND_BASE, type, idx); + + // UV source index ... if we didn't resolve the mapping it is actually just + // a guess but it works in most cases. We search for the frst occurence of a + // number in the channel name. We assume it is the zero-based index into the + // UV channel array of all corresponding meshes. + if (sampler.mUVId != 0xffffffff) + map = sampler.mUVId; + else { + map = 0xffffffff; + for (std::string::const_iterator it = sampler.mUVChannel.begin(); + it != sampler.mUVChannel.end(); ++it) + { + if (IsNumeric(*it)) { + map = strtol10(&(*it)); + break; + } + } + if (0xffffffff == map) { + DefaultLogger::get()->warn("Collada: unable to determine UV channel for texture"); + map = 0; + } + } + mat.AddProperty(&map,1,_AI_MATKEY_UVWSRC_BASE,type,idx); +} + +// ------------------------------------------------------------------------------------------------ +// Fills materials from the collada material definitions +void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* pScene) +{ + for (std::vector >::iterator it = newMats.begin(), + end = newMats.end(); it != end; ++it) + { + MaterialHelper& mat = (MaterialHelper&)*it->second; + Collada::Effect& effect = *it->first; + + // resolve shading mode + int shadeMode; + if (effect.mFaceted) /* fixme */ + shadeMode = aiShadingMode_Flat; + else { + switch( effect.mShadeType) + { + case Collada::Shade_Constant: + shadeMode = aiShadingMode_NoShading; + break; + case Collada::Shade_Lambert: + shadeMode = aiShadingMode_Gouraud; + break; + case Collada::Shade_Blinn: + shadeMode = aiShadingMode_Blinn; + break; + case Collada::Shade_Phong: + shadeMode = aiShadingMode_Phong; + break; + + default: + DefaultLogger::get()->warn("Collada: Unrecognized shading mode, using gouraud shading"); + shadeMode = aiShadingMode_Gouraud; + break; + } + } + mat.AddProperty( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); + + // double-sided? + shadeMode = effect.mDoubleSided; + mat.AddProperty( &shadeMode, 1, AI_MATKEY_TWOSIDED); + + // wireframe? + shadeMode = effect.mWireframe; + mat.AddProperty( &shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME); + + // add material colors + mat.AddProperty( &effect.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); + mat.AddProperty( &effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + mat.AddProperty( &effect.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); + mat.AddProperty( &effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); + mat.AddProperty( &effect.mShininess, 1, AI_MATKEY_SHININESS); + mat.AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI); + + // add textures, if given + if( !effect.mTexAmbient.mName.empty()) + AddTexture( mat, pParser, effect, effect.mTexAmbient,aiTextureType_AMBIENT); + + if( !effect.mTexEmissive.mName.empty()) + AddTexture( mat, pParser, effect, effect.mTexEmissive,aiTextureType_EMISSIVE); + + if( !effect.mTexSpecular.mName.empty()) + AddTexture( mat, pParser, effect, effect.mTexSpecular,aiTextureType_SPECULAR); + + if( !effect.mTexDiffuse.mName.empty()) + AddTexture( mat, pParser, effect, effect.mTexDiffuse,aiTextureType_DIFFUSE); + + if( !effect.mTexBump.mName.empty()) + AddTexture( mat, pParser, effect, effect.mTexBump,aiTextureType_HEIGHT); + + if( !effect.mTexTransparent.mName.empty()) + AddTexture( mat, pParser, effect, effect.mTexBump,aiTextureType_OPACITY); + } +} + // ------------------------------------------------------------------------------------------------ // Constructs materials from the collada material definitions void ColladaLoader::BuildMaterials( const ColladaParser& pParser, aiScene* pScene) { - std::vector newMats; + newMats.reserve(pParser.mMaterialLibrary.size()); for( ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); matIt != pParser.mMaterialLibrary.end(); ++matIt) { @@ -306,61 +760,39 @@ void ColladaLoader::BuildMaterials( const ColladaParser& pParser, aiScene* pScen // create material Assimp::MaterialHelper* mat = new Assimp::MaterialHelper; aiString name( matIt->first); - mat->AddProperty( &name, AI_MATKEY_NAME); + mat->AddProperty(&name,AI_MATKEY_NAME); - int shadeMode; - switch( effect.mShadeType) - { - case Collada::Shade_Constant: shadeMode = aiShadingMode_NoShading; break; - case Collada::Shade_Lambert: shadeMode = aiShadingMode_Gouraud; break; - case Collada::Shade_Blinn: shadeMode = aiShadingMode_Blinn; break; - default: shadeMode = aiShadingMode_Phong; break; - } - mat->AddProperty( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); + // MEGA SUPER MONSTER HACK by Alex ... It's all my fault, yes. + // We store the reference to the effect in the material and + // return ... we'll add the actual material properties later + // after we processed all meshes. During mesh processing, + // we evaluate vertex input mappings. Afterwards we should be + // able to correctly setup source UV channels for textures. - mat->AddProperty( &effect.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); - mat->AddProperty( &effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - mat->AddProperty( &effect.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); - mat->AddProperty( &effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); - mat->AddProperty( &effect.mShininess, 1, AI_MATKEY_SHININESS); - mat->AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI); - - // add textures, if given - if( !effect.mTexAmbient.empty()) - mat->AddProperty( &FindFilenameForEffectTexture( pParser, effect, effect.mTexAmbient), AI_MATKEY_TEXTURE_AMBIENT( 0)); - if( !effect.mTexDiffuse.empty()) - mat->AddProperty( &FindFilenameForEffectTexture( pParser, effect, effect.mTexDiffuse), AI_MATKEY_TEXTURE_DIFFUSE( 0)); - if( !effect.mTexEmissive.empty()) - mat->AddProperty( &FindFilenameForEffectTexture( pParser, effect, effect.mTexEmissive), AI_MATKEY_TEXTURE_EMISSIVE( 0)); - if( !effect.mTexSpecular.empty()) - mat->AddProperty( &FindFilenameForEffectTexture( pParser, effect, effect.mTexSpecular), AI_MATKEY_TEXTURE_SPECULAR( 0)); + // ... moved to ColladaLoader::FillMaterials() + // *duck* // store the material mMaterialIndexByName[matIt->first] = newMats.size(); - newMats.push_back( mat); + newMats.push_back( std::pair(const_cast(&effect),mat) ); } // store a dummy material if none were given if( newMats.size() == 0) { Assimp::MaterialHelper* mat = new Assimp::MaterialHelper; - aiString name( std::string( "dummy")); + aiString name( AI_DEFAULT_MATERIAL_NAME ); mat->AddProperty( &name, AI_MATKEY_NAME); - int shadeMode = aiShadingMode_Phong; + const int shadeMode = aiShadingMode_Phong; mat->AddProperty( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); aiColor4D colAmbient( 0.2f, 0.2f, 0.2f, 1.0f), colDiffuse( 0.8f, 0.8f, 0.8f, 1.0f), colSpecular( 0.5f, 0.5f, 0.5f, 0.5f); mat->AddProperty( &colAmbient, 1, AI_MATKEY_COLOR_AMBIENT); mat->AddProperty( &colDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); mat->AddProperty( &colSpecular, 1, AI_MATKEY_COLOR_SPECULAR); - float specExp = 5.0f; + const float specExp = 5.0f; mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS); } - - // store the materials in the scene - pScene->mNumMaterials = newMats.size(); - pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; - std::copy( newMats.begin(), newMats.end(), pScene->mMaterials); } // ------------------------------------------------------------------------------------------------ @@ -384,7 +816,7 @@ const aiString& ColladaLoader::FindFilenameForEffectTexture( const ColladaParser // find the image referred by this name in the image library of the scene ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find( name); if( imIt == pParser.mImageLibrary.end()) - throw new ImportErrorException( boost::str( boost::format( "Unable to resolve effect texture entry \"%s\", ended up at ID \"%s\".") % pName % name)); + throw new ImportErrorException( boost::str( boost::format( "Collada: Unable to resolve effect texture entry \"%s\", ended up at ID \"%s\".") % pName % name)); static aiString result; result.Set( imIt->second.mFileName ); diff --git a/code/ColladaLoader.h b/code/ColladaLoader.h index 37f0ab656..f4f30bb83 100644 --- a/code/ColladaLoader.h +++ b/code/ColladaLoader.h @@ -109,17 +109,53 @@ protected: /** Recursively constructs a scene node for the given parser node and returns it. */ aiNode* BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode); + /** Resolve node instances */ + void ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode, + std::vector& resolved); + /** Builds meshes for the given node and references them */ - void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget); + void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, + aiNode* pTarget); + + /** Builds cameras for the given node and references them */ + void BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, + aiNode* pTarget); + + /** Builds lights for the given node and references them */ + void BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode, + aiNode* pTarget); /** Stores all meshes in the given scene */ void StoreSceneMeshes( aiScene* pScene); + /** Stores all materials in the given scene */ + void StoreSceneMaterials( aiScene* pScene); + + /** Stores all lights in the given scene */ + void StoreSceneLights( aiScene* pScene); + + /** Stores all cameras in the given scene */ + void StoreSceneCameras( aiScene* pScene); + /** Constructs materials from the collada material definitions */ void BuildMaterials( const ColladaParser& pParser, aiScene* pScene); + /** Fill materials from the collada material definitions */ + void FillMaterials( const ColladaParser& pParser, aiScene* pScene); + + /** Resolve UV channel mappings*/ + void ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler, + const Collada::SemanticMappingTable& table); + + /** Add a texture and all of its sampling properties to a material*/ + void AddTexture ( Assimp::MaterialHelper& mat, const ColladaParser& pParser, + const Collada::Effect& effect, + const Collada::Sampler& sampler, + aiTextureType type, unsigned int idx = 0); + /** Resolves the texture name for the given effect texture entry */ - const aiString& FindFilenameForEffectTexture( const ColladaParser& pParser, const Collada::Effect& pEffect, const std::string& pName); + const aiString& FindFilenameForEffectTexture( const ColladaParser& pParser, + const Collada::Effect& pEffect, const std::string& pName); /** Converts a path read from a collada file to the usual representation */ void ConvertPath (aiString& ss); @@ -136,6 +172,15 @@ protected: /** Accumulated meshes for the target scene */ std::vector mMeshes; + + /** Temporary material list */ + std::vector > newMats; + + /** Temporary camera list */ + std::vector mCameras; + + /** Temporary light list */ + std::vector mLights; }; } // end of namespace Assimp diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index 5736d2374..3ecb45796 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -1,4 +1,3 @@ -/** Implementation of the Collada parser helper*/ /* --------------------------------------------------------------------------- Open Asset Import Library (ASSIMP) @@ -40,6 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ +/** @file ColladaParser.cpp + * @brief Implementation of the Collada parser helper + */ + #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_DAE_IMPORTER @@ -62,7 +65,7 @@ ColladaParser::ColladaParser( const std::string& pFile) // generate a XML reader for it mReader = irr::io::createIrrXMLReader( pFile.c_str()); if( !mReader) - ThrowException( "Unable to open file."); + ThrowException( "Collada: Unable to open file."); // start reading ReadContents(); @@ -79,6 +82,22 @@ ColladaParser::~ColladaParser() delete it->second; } +// ------------------------------------------------------------------------------------------------ +// Read bool from text contents of current element +bool ColladaParser::ReadBoolFromTextContent() +{ + const char* cur = GetTextContent(); + return (!ASSIMP_strincmp(cur,"true",4) || '0' != *cur); +} + +// ------------------------------------------------------------------------------------------------ +// Read float from text contents of current element +float ColladaParser::ReadFloatFromTextContent() +{ + const char* cur = GetTextContent(); + return fast_atof(cur); +} + // ------------------------------------------------------------------------------------------------ // Reads the contents of the file void ColladaParser::ReadContents() @@ -110,8 +129,7 @@ void ColladaParser::ReadStructure() while( mReader->read()) { // beginning of elements - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "asset")) ReadAssetInfo(); else if( IsElement( "library_images")) @@ -124,13 +142,18 @@ void ColladaParser::ReadStructure() ReadGeometryLibrary(); else if( IsElement( "library_visual_scenes")) ReadSceneLibrary(); + else if( IsElement( "library_lights")) + ReadLightLibrary(); + else if( IsElement( "library_cameras")) + ReadCameraLibrary(); + else if( IsElement( "library_nodes")) + ReadSceneNode(NULL); /* some hacking to reuse this piece of code */ else if( IsElement( "scene")) ReadScene(); else SkipElement(); } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { break; } } @@ -188,8 +211,7 @@ void ColladaParser::ReadImageLibrary() { while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "image")) { // read ID. Another entry which is "optional" by design but obligatory in reality @@ -207,8 +229,7 @@ void ColladaParser::ReadImageLibrary() SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { if( strcmp( mReader->getNodeName(), "library_images") != 0) ThrowException( "Expected end of \"library_images\" element."); @@ -223,8 +244,7 @@ void ColladaParser::ReadImage( Collada::Image& pImage) { while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT){ if( IsElement( "init_from")) { // element content is filename - hopefully @@ -237,8 +257,7 @@ void ColladaParser::ReadImage( Collada::Image& pImage) SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { if( strcmp( mReader->getNodeName(), "image") != 0) ThrowException( "Expected end of \"image\" element."); @@ -253,8 +272,7 @@ void ColladaParser::ReadMaterialLibrary() { while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "material")) { // read ID. By now you propably know my opinion about this "specification" @@ -262,17 +280,14 @@ void ColladaParser::ReadMaterialLibrary() std::string id = mReader->getAttributeValue( attrID); // create an entry and store it in the library under its ID - mMaterialLibrary[id] = Material(); - // read on from there - ReadMaterial( mMaterialLibrary[id]); + ReadMaterial(mMaterialLibrary[id] = Material()); } else { // ignore the rest SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { if( strcmp( mReader->getNodeName(), "library_materials") != 0) ThrowException( "Expected end of \"library_materials\" element."); @@ -281,14 +296,80 @@ void ColladaParser::ReadMaterialLibrary() } } +// ------------------------------------------------------------------------------------------------ +// Reads the light library +void ColladaParser::ReadLightLibrary() +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if( IsElement( "light")) + { + // read ID. By now you propably know my opinion about this "specification" + int attrID = GetAttribute( "id"); + std::string id = mReader->getAttributeValue( attrID); + + // create an entry and store it in the library under its ID + ReadLight(mLightLibrary[id] = Light()); + + } else + { + // ignore the rest + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if( strcmp( mReader->getNodeName(), "library_lights") != 0) + ThrowException( "Expected end of \"library_lights\" element."); + + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads the camera library +void ColladaParser::ReadCameraLibrary() +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if( IsElement( "camera")) + { + // read ID. By now you propably know my opinion about this "specification" + int attrID = GetAttribute( "id"); + std::string id = mReader->getAttributeValue( attrID); + + // create an entry and store it in the library under its ID + Camera& cam = mCameraLibrary[id]; + attrID = TestAttribute( "name"); + if (attrID != -1) + cam.mName = mReader->getAttributeValue( attrID); + + ReadCamera(cam); + + } else + { + // ignore the rest + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if( strcmp( mReader->getNodeName(), "library_cameras") != 0) + ThrowException( "Expected end of \"library_cameras\" element."); + + break; + } + } +} + // ------------------------------------------------------------------------------------------------ // Reads a material entry into the given material void ColladaParser::ReadMaterial( Collada::Material& pMaterial) { while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "instance_effect")) { // referred effect by URL @@ -306,27 +387,145 @@ void ColladaParser::ReadMaterial( Collada::Material& pMaterial) SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { if( strcmp( mReader->getNodeName(), "material") != 0) - ThrowException( "Expected end of \"image\" element."); + ThrowException( "Expected end of \"material\" element."); break; } } } +// ------------------------------------------------------------------------------------------------ +// Reads a light entry into the given light +void ColladaParser::ReadLight( Collada::Light& pLight) +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if (IsElement("spot")) { + pLight.mType = aiLightSource_SPOT; + } + else if (IsElement("ambient")) { + pLight.mType = aiLightSource_AMBIENT; + } + else if (IsElement("directional")) { + pLight.mType = aiLightSource_DIRECTIONAL; + } + else if (IsElement("point")) { + pLight.mType = aiLightSource_POINT; + } + + else if (IsElement("color")) { + // text content contains 3 floats + const char* content = GetTextContent(); + + content = fast_atof_move( content, pLight.mColor.r); + SkipSpacesAndLineEnd( &content); + + content = fast_atof_move( content, pLight.mColor.g); + SkipSpacesAndLineEnd( &content); + + content = fast_atof_move( content, pLight.mColor.b); + SkipSpacesAndLineEnd( &content); + + TestClosing( "color"); + } + else if (IsElement("constant_attenuation")) { + pLight.mAttConstant = ReadFloatFromTextContent(); + TestClosing("constant_attenuation"); + } + else if (IsElement("linear_attenuation")) { + pLight.mAttLinear = ReadFloatFromTextContent(); + TestClosing("linear_attenuation"); + } + else if (IsElement("quadratic_attenuation")) { + pLight.mAttQuadratic = ReadFloatFromTextContent(); + TestClosing("quadratic_attenuation"); + } + else if (IsElement("falloff_angle")) { + pLight.mFalloffAngle = ReadFloatFromTextContent(); + TestClosing("falloff_angle"); + } + else if (IsElement("falloff_exponent")) { + pLight.mFalloffExponent = ReadFloatFromTextContent(); + TestClosing("falloff_exponent"); + } + // FCOLLADA extensions + // ------------------------------------------------------- + else if (IsElement("outer_cone")) { + pLight.mOuterAngle = ReadFloatFromTextContent(); + TestClosing("outer_cone"); + } + // ... and this one is even deprecated + else if (IsElement("penumbra_angle")) { + pLight.mPenumbraAngle = ReadFloatFromTextContent(); + TestClosing("penumbra_angle"); + } + else if (IsElement("intensity")) { + pLight.mIntensity = ReadFloatFromTextContent(); + TestClosing("intensity"); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if( strcmp( mReader->getNodeName(), "light") == 0) + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads a camera entry into the given light +void ColladaParser::ReadCamera( Collada::Camera& pCamera) +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + + if (IsElement("orthographic")) { + pCamera.mOrtho = true; + } + else if (IsElement("xfov") || IsElement("xmag")) { + pCamera.mHorFov = ReadFloatFromTextContent(); + TestClosing((pCamera.mOrtho ? "xmag" : "xfov")); + } + else if (IsElement("yfov") || IsElement("ymag")) { + pCamera.mVerFov = ReadFloatFromTextContent(); + TestClosing((pCamera.mOrtho ? "ymag" : "yfov")); + } + else if (IsElement("aspect_ratio")) { + pCamera.mAspect = ReadFloatFromTextContent(); + TestClosing("aspect_ratio"); + } + else if (IsElement("znear")) { + pCamera.mZNear = ReadFloatFromTextContent(); + TestClosing("znear"); + } + else if (IsElement("zfar")) { + pCamera.mZFar = ReadFloatFromTextContent(); + TestClosing("zfar"); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if( strcmp( mReader->getNodeName(), "camera") == 0) + break; + } + } +} + // ------------------------------------------------------------------------------------------------ // Reads the effect library void ColladaParser::ReadEffectLibrary() { while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "effect")) { // read ID. Do I have to repeat my ranting about "optional" attributes? + // Alex: .... no, not necessary. Please shut up and leave more space for + // me to complain about the fucking Collada spec with its fucking + // 'optional' attributes ... int attrID = GetAttribute( "id"); std::string id = mReader->getAttributeValue( attrID); @@ -340,8 +539,7 @@ void ColladaParser::ReadEffectLibrary() SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { if( strcmp( mReader->getNodeName(), "library_effects") != 0) ThrowException( "Expected end of \"library_effects\" element."); @@ -360,8 +558,7 @@ void ColladaParser::ReadEffect( Collada::Effect& pEffect) while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "newparam")) { // save ID @@ -374,6 +571,8 @@ void ColladaParser::ReadEffect( Collada::Effect& pEffect) { // just syntactic sugar } + + /* Shading modes */ else if( IsElement( "phong")) pEffect.mShadeType = Shade_Phong; else if( IsElement( "constant")) @@ -382,6 +581,8 @@ void ColladaParser::ReadEffect( Collada::Effect& pEffect) pEffect.mShadeType = Shade_Lambert; else if( IsElement( "blinn")) pEffect.mShadeType = Shade_Blinn; + + /* Color + texture properties */ else if( IsElement( "emission")) ReadEffectColor( pEffect.mEmissive, pEffect.mTexEmissive); else if( IsElement( "ambient")) @@ -390,30 +591,53 @@ void ColladaParser::ReadEffect( Collada::Effect& pEffect) ReadEffectColor( pEffect.mDiffuse, pEffect.mTexDiffuse); else if( IsElement( "specular")) ReadEffectColor( pEffect.mSpecular, pEffect.mTexSpecular); - else if( IsElement( "reflective")){ - std::string buf; - ReadEffectColor( pEffect.mReflective, buf); - } - else if( IsElement( "transparent")){ - std::string buf; - ReadEffectColor( pEffect.mRefractive,buf); - } + else if( IsElement( "reflective")) { + // Collada::Sampler dummy; + // ReadEffectColor( dummy,pEffect.mTexReflective); + } + else if( IsElement( "transparent")) + ReadEffectColor( pEffect.mTransparent,pEffect.mTexTransparent); else if( IsElement( "shininess")) ReadEffectFloat( pEffect.mShininess); - else if( IsElement( "reflectivity")) - ReadEffectFloat( pEffect.mReflectivity); + + /* Single scalar properties */ else if( IsElement( "transparency")) - ReadEffectFloat( pEffect.mRefractivity); + ReadEffectFloat( pEffect.mTransparency); else if( IsElement( "index_of_refraction")) ReadEffectFloat( pEffect.mRefractIndex); - else - { + // else if( IsElement( "reflectivity")) + // ReadEffectFloat( pEffect.mRefl); + + // GOOGLEEARTH/OKINO extensions + // ------------------------------------------------------- + else if( IsElement( "double_sided")) + pEffect.mDoubleSided = ReadBoolFromTextContent(); + + // FCOLLADA extensions + // ------------------------------------------------------- + else if( IsElement( "bump")) { + aiColor4D dummy; + ReadEffectColor( dummy,pEffect.mTexBump); + } + + // MAX3D extensions + // ------------------------------------------------------- + else if( IsElement( "wireframe")) { + pEffect.mWireframe = ReadBoolFromTextContent(); + TestClosing( "wireframe"); + } + else if( IsElement( "faceted")) { + pEffect.mFaceted = ReadBoolFromTextContent(); + TestClosing( "faceted"); + } +#if 0 + else { // ignore the rest SkipElement(); } +#endif } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { if( strcmp( mReader->getNodeName(), "effect") == 0) break; } @@ -421,47 +645,162 @@ void ColladaParser::ReadEffect( Collada::Effect& pEffect) } // ------------------------------------------------------------------------------------------------ -// Reads an effect entry containing a color or a texture defining that color -void ColladaParser::ReadEffectColor( aiColor4D& pColor, std::string& pSampler) +// Read texture wrapping + UV transform settings from a profile==Maya chunk +void ColladaParser::ReadSamplerProperties( Sampler& out ) { + if (mReader->isEmptyElement()) + return; + while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + + // MAYA extensions + // ------------------------------------------------------- + if( IsElement( "wrapU")) { + out.mWrapU = ReadBoolFromTextContent(); + TestClosing( "wrapU"); + } + else if( IsElement( "wrapV")) { + out.mWrapU = ReadBoolFromTextContent(); + TestClosing( "wrapV"); + } + if( IsElement( "mirrorU")) { + out.mMirrorU = ReadBoolFromTextContent(); + TestClosing( "mirrorU"); + } + else if( IsElement( "mirrorV")) { + out.mMirrorU = ReadBoolFromTextContent(); + TestClosing( "mirrorV"); + } + else if( IsElement( "repeatU")) { + out.mTransform.mScaling.x = ReadFloatFromTextContent(); + TestClosing( "repeatU"); + } + else if( IsElement( "repeatV")) { + out.mTransform.mScaling.y = ReadFloatFromTextContent(); + TestClosing( "repeatV"); + } + else if( IsElement( "offsetU")) { + out.mTransform.mTranslation.x = ReadFloatFromTextContent(); + TestClosing( "offsetU"); + } + else if( IsElement( "offsetV")) { + out.mTransform.mTranslation.x = ReadFloatFromTextContent(); + TestClosing( "offsetV"); + } + else if( IsElement( "rotateUV")) { + out.mTransform.mRotation = ReadFloatFromTextContent(); + TestClosing( "rotateUV"); + } + else if( IsElement( "blend_mode")) { + + const char* sz = GetTextContent(); + // http://www.feelingsoftware.com/content/view/55/72/lang,en/ + // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE + if (0 == ASSIMP_strincmp(sz,"ADD",3)) + out.mOp = aiTextureOp_Add; + + else if (0 == ASSIMP_strincmp(sz,"SUBTRACT",8)) + out.mOp = aiTextureOp_Subtract; + + else if (0 == ASSIMP_strincmp(sz,"MULTIPLY",8)) + out.mOp = aiTextureOp_Multiply; + + else { + DefaultLogger::get()->warn("Collada: Unsupported MAYA texture blend mode"); + } + TestClosing( "blend_mode"); + } + // OKINO extensions + // ------------------------------------------------------- + else if( IsElement( "weighting")) { + out.mWeighting = ReadFloatFromTextContent(); + TestClosing( "weighting"); + } + else if( IsElement( "mix_with_previous_layer")) { + out.mMixWithPrevious = ReadFloatFromTextContent(); + TestClosing( "mix_with_previous_layer"); + } + // MAX3D extensions + // ------------------------------------------------------- + else if( IsElement( "amount")) { + out.mWeighting = ReadFloatFromTextContent(); + TestClosing( "amount"); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if( strcmp( mReader->getNodeName(), "technique") == 0) + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads an effect entry containing a color or a texture defining that color +void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler) +{ + if (mReader->isEmptyElement()) + return; + + // Save current element name + const std::string curElem = mReader->getNodeName(); + + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "color")) { // text content contains 4 floats - const char* content = GetTextContent(); - float fBuf; - content = fast_atof_move( content, fBuf); - pColor.r = fBuf; - SkipSpacesAndLineEnd( &content); - content = fast_atof_move( content, fBuf); - pColor.g = fBuf; - SkipSpacesAndLineEnd( &content); - content = fast_atof_move( content, fBuf); - pColor.b = fBuf; - SkipSpacesAndLineEnd( &content); - content = fast_atof_move( content, fBuf); - pColor.a = fBuf; + const char* content = GetTextContent(); + + content = fast_atof_move( content, pColor.r); SkipSpacesAndLineEnd( &content); + content = fast_atof_move( content, pColor.g); + SkipSpacesAndLineEnd( &content); + + content = fast_atof_move( content, pColor.b); + SkipSpacesAndLineEnd( &content); + + content = fast_atof_move( content, pColor.a); + SkipSpacesAndLineEnd( &content); TestClosing( "color"); } else if( IsElement( "texture")) { + // get name of source textur/sampler int attrTex = GetAttribute( "texture"); - pSampler = mReader->getAttributeValue( attrTex); - SkipElement(); - } else + pSampler.mName = mReader->getAttributeValue( attrTex); + + // get name of UV source channel + attrTex = GetAttribute( "texcoord"); + pSampler.mUVChannel = mReader->getAttributeValue( attrTex); + //SkipElement(); + } + else if( IsElement( "technique")) + { + const int _profile = GetAttribute( "profile"); + const char* profile = mReader->getAttributeValue( _profile ); + + // Some extensions are quite useful ... ReadSamplerProperties processes + // several extensions in MAYA, OKINO and MAX3D profiles. + if (!::strcmp(profile,"MAYA") || !::strcmp(profile,"MAX3D") || !::strcmp(profile,"OKINO")) + { + // get more information on this sampler + ReadSamplerProperties(pSampler); + } + else SkipElement(); + } + else { // ignore the rest SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { - break; + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ + if (mReader->getNodeName() == curElem) + break; } } } @@ -472,8 +811,7 @@ void ColladaParser::ReadEffectFloat( float& pFloat) { while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT){ if( IsElement( "float")) { // text content contains a single floats @@ -488,8 +826,7 @@ void ColladaParser::ReadEffectFloat( float& pFloat) SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ break; } } @@ -501,8 +838,7 @@ void ColladaParser::ReadEffectParam( Collada::EffectParam& pParam) { while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "surface")) { // image ID given inside tags @@ -532,8 +868,7 @@ void ColladaParser::ReadEffectParam( Collada::EffectParam& pParam) SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { break; } } @@ -554,14 +889,14 @@ void ColladaParser::ReadGeometryLibrary() std::string id = mReader->getAttributeValue( indexID); // TODO: (thom) support SIDs - assert( TestAttribute( "sid") == -1); + // ai_assert( TestAttribute( "sid") == -1); // create a mesh and store it in the library under its ID Mesh* mesh = new Mesh; - mMeshLibrary[id] = mesh; + mMeshLibrary[id] = mesh; // read on from there - ReadGeometry( mesh); + ReadGeometry( mesh); } else { // ignore the rest @@ -752,15 +1087,28 @@ void ColladaParser::ReadAccessor( const std::string& pID) name = mReader->getAttributeValue( attrName); // analyse for common type components and store it's sub-offset in the corresponding field + + /* Cartesian coordinates */ if( name == "X") acc.mSubOffset[0] = acc.mParams.size(); else if( name == "Y") acc.mSubOffset[1] = acc.mParams.size(); else if( name == "Z") acc.mSubOffset[2] = acc.mParams.size(); + + /* RGBA colors */ else if( name == "R") acc.mSubOffset[0] = acc.mParams.size(); else if( name == "G") acc.mSubOffset[1] = acc.mParams.size(); else if( name == "B") acc.mSubOffset[2] = acc.mParams.size(); else if( name == "A") acc.mSubOffset[3] = acc.mParams.size(); + + /* UVWQ (STPQ) texture coordinates */ else if( name == "S") acc.mSubOffset[0] = acc.mParams.size(); else if( name == "T") acc.mSubOffset[1] = acc.mParams.size(); + else if( name == "P") acc.mSubOffset[2] = acc.mParams.size(); + // else if( name == "Q") acc.mSubOffset[3] = acc.mParams.size(); + /* 4D uv coordinates are not supported in Assimp */ + + /* Generic extra data, interpreted as UV data, too*/ + else if( name == "U") acc.mSubOffset[0] = acc.mParams.size(); + else if( name == "V") acc.mSubOffset[1] = acc.mParams.size(); else DefaultLogger::get()->warn( boost::str( boost::format( "Unknown accessor parameter \"%s\". Ignoring data channel.") % name)); } @@ -852,7 +1200,7 @@ void ColladaParser::ReadIndexData( Mesh* pMesh) else if( IsElement( "tristrips")) primType = Prim_TriStrips; - assert( primType != Prim_Invalid); + ai_assert( primType != Prim_Invalid); // also a number of elements, but in addition a

primitive collection and propably index counts for all primitives while( mReader->read()) @@ -865,31 +1213,31 @@ void ColladaParser::ReadIndexData( Mesh* pMesh) } else if( IsElement( "vcount")) { - if( !mReader->isEmptyElement()) - { - // case - specifies the number of indices for each polygon - const char* content = GetTextContent(); - vcount.reserve( numPrimitives); - for( unsigned int a = 0; a < numPrimitives; a++) - { - if( *content == 0) - ThrowException( "Expected more values while reading vcount contents."); - // read a number - vcount.push_back( (size_t) strtol10( content, &content)); - // skip whitespace after it - SkipSpacesAndLineEnd( &content); - } - - TestClosing( "vcount"); - } + if( !mReader->isEmptyElement()) + { + // case - specifies the number of indices for each polygon + const char* content = GetTextContent(); + vcount.reserve( numPrimitives); + for( unsigned int a = 0; a < numPrimitives; a++) + { + if( *content == 0) + ThrowException( "Expected more values while reading vcount contents."); + // read a number + vcount.push_back( (size_t) strtol10( content, &content)); + // skip whitespace after it + SkipSpacesAndLineEnd( &content); + } + + TestClosing( "vcount"); + } } else if( IsElement( "p")) { - if( !mReader->isEmptyElement()) - { - // now here the actual fun starts - these are the indices to construct the mesh data from - ReadPrimitives( pMesh, perIndexData, numPrimitives, vcount, primType); - } + if( !mReader->isEmptyElement()) + { + // now here the actual fun starts - these are the indices to construct the mesh data from + ReadPrimitives( pMesh, perIndexData, numPrimitives, vcount, primType); + } } else { ThrowException( "Unexpected sub element in tag \"vertices\"."); @@ -939,7 +1287,7 @@ void ColladaParser::ReadInputChannel( std::vector& poChannels) // ------------------------------------------------------------------------------------------------ // Reads a

primitive index list and assembles the mesh data into the given mesh void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector& pPerIndexChannels, - size_t pNumPrimitives, const std::vector& pVCount, PrimitiveType pPrimType) + size_t pNumPrimitives, const std::vector& pVCount, PrimitiveType pPrimType) { // determine number of indices coming per vertex // find the offset index for all per-vertex channels @@ -1045,11 +1393,19 @@ void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector& pPer size_t numPoints = 0; switch( pPrimType) { - case Prim_Lines: numPoints = 2; break; - case Prim_Triangles: numPoints = 3; break; - case Prim_Polylist: numPoints = pVCount[a]; break; + case Prim_Lines: + numPoints = 2; + break; + case Prim_Triangles: + numPoints = 3; + break; + case Prim_Polylist: + numPoints = pVCount[a]; + break; case Prim_TriFans: - case Prim_Polygon: numPoints = indices.size() / numOffsets; break; + case Prim_Polygon: + numPoints = indices.size() / numOffsets; + break; default: // LineStrip and TriStrip not supported due to expected index unmangling ThrowException( "Unsupported primitive type."); @@ -1096,7 +1452,8 @@ void ColladaParser::ExtractDataObjectFromChannel( const InputChannel& pInput, si // get a pointer to the start of the data object referred to by the accessor and the local index const float* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex* acc.mStride; - // assemble according to the accessors component sub-offset list. We don't care, yet, what kind of object exactly we're extracting here + // assemble according to the accessors component sub-offset list. We don't care, yet, + // what kind of object exactly we're extracting here float obj[4]; for( size_t c = 0; c < 4; ++c) obj[c] = dataObject[acc.mSubOffset[c]]; @@ -1107,18 +1464,41 @@ void ColladaParser::ExtractDataObjectFromChannel( const InputChannel& pInput, si case IT_Position: // ignore all position streams except 0 - there can be only one position if( pInput.mIndex == 0) pMesh->mPositions.push_back( aiVector3D( obj[0], obj[1], obj[2])); + else + DefaultLogger::get()->error("Collada: just one vertex position stream supported"); break; case IT_Normal: // ignore all normal streams except 0 - there can be only one normal if( pInput.mIndex == 0) pMesh->mNormals.push_back( aiVector3D( obj[0], obj[1], obj[2])); + else + DefaultLogger::get()->error("Collada: just one vertex normal stream supported"); + break; + case IT_Tangent: // ignore all tangent streams except 0 - there can be only one tangent + if( pInput.mIndex == 0) + pMesh->mTangents.push_back( aiVector3D( obj[0], obj[1], obj[2])); + else + DefaultLogger::get()->error("Collada: just one vertex tangent stream supported"); + break; + case IT_Bitangent: // ignore all bitangent streams except 0 - there can be only one bitangent + if( pInput.mIndex == 0) + pMesh->mBitangents.push_back( aiVector3D( obj[0], obj[1], obj[2])); + else + DefaultLogger::get()->error("Collada: just one vertex bitangent stream supported"); break; case IT_Texcoord: // up to 4 texture coord sets are fine, ignore the others - if( pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) - pMesh->mTexCoords[pInput.mIndex].push_back( aiVector2D( obj[0], obj[1])); + if( pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) { + pMesh->mTexCoords[pInput.mIndex].push_back( aiVector3D( obj[0], obj[1], obj[2])); + if (0 != acc.mSubOffset[2]) /* hack ... consider cleaner solution */ + pMesh->mNumUVComponents[pInput.mIndex]=3; + } + else + DefaultLogger::get()->error("Collada: too many texture coordinate sets. Skipping."); break; case IT_Color: // up to 4 color sets are fine, ignore the others if( pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) pMesh->mColors[pInput.mIndex].push_back( aiColor4D( obj[0], obj[1], obj[2], obj[3])); + else + DefaultLogger::get()->error("Collada: too many vertex color sets. Skipping."); break; } } @@ -1145,7 +1525,7 @@ void ColladaParser::ReadSceneLibrary() attrName = mReader->getAttributeValue( indexName); // TODO: (thom) support SIDs - assert( TestAttribute( "sid") == -1); + // assert( TestAttribute( "sid") == -1); // create a node and store it in the library under its ID Node* node = new Node; @@ -1180,8 +1560,40 @@ void ColladaParser::ReadSceneNode( Node* pNode) while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if( IsElement( "node")) + { + Node* child = new Node; + int attrID = TestAttribute( "id"); + if( attrID > -1) + child->mID = mReader->getAttributeValue( attrID); + + int attrName = TestAttribute( "name"); + if( attrName > -1) + child->mName = mReader->getAttributeValue( attrName); + + // TODO: (thom) support SIDs + // assert( TestAttribute( "sid") == -1); + + if (pNode) { + + pNode->mChildren.push_back( child); + child->mParent = pNode; + } + else { + // no parent node given, probably called from element. + // create new node in node library + mNodeLibrary[child->mID] = pNode = child; + } + + // read on recursively from there + ReadSceneNode( child); + continue; + } + // For any further stuff we need a valid node to work on + else if (!pNode) + continue; + if( IsElement( "lookat")) ReadNodeTransformation( pNode, TF_LOOKAT); else if( IsElement( "matrix")) @@ -1194,42 +1606,72 @@ void ColladaParser::ReadSceneNode( Node* pNode) ReadNodeTransformation( pNode, TF_SKEW); else if( IsElement( "translate")) ReadNodeTransformation( pNode, TF_TRANSLATE); - else if( IsElement( "node")) + else if( IsElement( "render") && pNode->mParent == NULL && 0 == pNode->mPrimaryCamera.length()) { - Node* child = new Node; - int attrID = TestAttribute( "id"); - if( attrID > -1) - child->mID = mReader->getAttributeValue( attrID); - - int attrName = TestAttribute( "name"); - if( attrName > -1) - child->mName = mReader->getAttributeValue( attrName); - - // TODO: (thom) support SIDs -// assert( TestAttribute( "sid") == -1); - - pNode->mChildren.push_back( child); - child->mParent = pNode; - - // read on recursively from there - ReadSceneNode( child); - } else if( IsElement( "instance_node")) - { - // test for it, in case we need to implement it - assert( false); - SkipElement(); - } else if( IsElement( "instance_geometry")) - { - // Reference to a mesh, we possible material associations + // ... scene evaluation or, in other words, postprocessing pipeline, + // or, again in other words, a turing-complete description how to + // render a Collada scene. The only thing that is interesting for + // us is the primary camera. + int attrId = TestAttribute("camera_node"); + if (-1 != attrId) { + const char* s = mReader->getAttributeValue(attrId); + if (s[0] != '#') + DefaultLogger::get()->error("Collada: Unresolved reference format of camera"); + else pNode->mPrimaryCamera = s+1; + } + } + else if( IsElement( "instance_node")) { + // find the node in the library + int attrID = TestAttribute( "url"); + if( attrID != -1) { + const char* s = mReader->getAttributeValue(attrID); + if (s[0] != '#') + DefaultLogger::get()->error("Collada: Unresolved reference format of node"); + else { + pNode->mNodeInstances.push_back(NodeInstance()); + pNode->mNodeInstances.back().mNode = s+1; + } + } + } + else if( IsElement( "instance_geometry")) { + // Reference to a mesh, with possible material associations ReadNodeGeometry( pNode); - } else + } + else if( IsElement( "instance_light")) { + // Reference to a light, name given in 'url' attribute + int attrID = TestAttribute("url"); + if (-1 == attrID) + DefaultLogger::get()->warn("Collada: Expected url attribute in element"); + else { + const char* url = mReader->getAttributeValue( attrID); + if( url[0] != '#') + ThrowException( "Unknown reference format in element"); + + pNode->mLights.push_back(LightInstance()); + pNode->mLights.back().mLight = url+1; + } + } + else if( IsElement( "instance_camera")) { + // Reference to a camera, name given in 'url' attribute + int attrID = TestAttribute("url"); + if (-1 == attrID) + DefaultLogger::get()->warn("Collada: Expected url attribute in element"); + else { + const char* url = mReader->getAttributeValue( attrID); + if( url[0] != '#') + ThrowException( "Unknown reference format in element"); + + pNode->mCameras.push_back(CameraInstance()); + pNode->mCameras.back().mCamera = url+1; + } + } + else { // skip everything else for the moment SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { break; } } @@ -1263,6 +1705,43 @@ void ColladaParser::ReadNodeTransformation( Node* pNode, TransformType pType) TestClosing( tagName.c_str()); } +// ------------------------------------------------------------------------------------------------ +// Processes bind_vertex_input and bind elements +void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl) +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if( IsElement( "bind_vertex_input")) + { + Collada::InputSemanticMapEntry vn; + + // effect semantic + int n = GetAttribute("semantic"); + std::string s = mReader->getAttributeValue(n); + + // input semantic + n = GetAttribute("input_semantic"); + vn.mType = GetTypeForSemantic( mReader->getAttributeValue(n) ); + + // index of input set + n = TestAttribute("input_set"); + if (-1 != n) + vn.mSet = mReader->getAttributeValueAsInt(n); + + tbl.mMap[s] = vn; + } + else if( IsElement( "bind")) { + DefaultLogger::get()->warn("Collada: Found unsupported element"); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + if( strcmp( mReader->getNodeName(), "instance_material") == 0) + break; + } + } +} + // ------------------------------------------------------------------------------------------------ // Reads a mesh reference in a node and adds it to the node's mesh list void ColladaParser::ReadNodeGeometry( Node* pNode) @@ -1281,8 +1760,7 @@ void ColladaParser::ReadNodeGeometry( Node* pNode) // read material associations. Ignore additional elements inbetween while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "instance_material")) { // read ID of the geometry subgroup and the target material @@ -1290,16 +1768,21 @@ void ColladaParser::ReadNodeGeometry( Node* pNode) std::string group = mReader->getAttributeValue( attrGroup); int attrMaterial = GetAttribute( "target"); const char* urlMat = mReader->getAttributeValue( attrMaterial); + Collada::SemanticMappingTable s; if( urlMat[0] != '#') ThrowException( "Unknown reference format"); - std::string mat = urlMat+1; + + s.mMatName = urlMat+1; + + // resolve further material details + THIS UGLY AND NASTY semantic mapping stuff + if( !mReader->isEmptyElement()) + ReadMaterialVertexInputBinding(s); // store the association - instance.mMaterials[group] = mat; + instance.mMaterials[group] = s; } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { if( strcmp( mReader->getNodeName(), "instance_geometry") == 0) break; } @@ -1316,8 +1799,7 @@ void ColladaParser::ReadScene() { while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) - { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "instance_visual_scene")) { // should be the first and only occurence @@ -1333,15 +1815,13 @@ void ColladaParser::ReadScene() // find the referred scene, skip the leading # NodeLibrary::const_iterator sit = mNodeLibrary.find( url+1); if( sit == mNodeLibrary.end()) - ThrowException( boost::str( boost::format( "Unable to resolve visual_scene reference \"%s\".") % url)); + ThrowException( "Unable to resolve visual_scene reference \"" + std::string(url) + "\"."); mRootNode = sit->second; - } else - { + } else { SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) - { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ break; } } @@ -1351,7 +1831,7 @@ void ColladaParser::ReadScene() // Aborts the file reading with an exception void ColladaParser::ThrowException( const std::string& pError) const { - throw new ImportErrorException( boost::str( boost::format( "%s - %s") % mFileName % pError)); + throw new ImportErrorException( boost::str( boost::format( "Collada: %s - %s") % mFileName % pError)); } // ------------------------------------------------------------------------------------------------ @@ -1447,9 +1927,9 @@ const char* ColladaParser::GetTextContent() // read contents of the element if( !mReader->read()) - ThrowException( "Unexpected end of file while reading asset up_axis element."); + ThrowException( "Unexpected end of file while reading n element."); if( mReader->getNodeType() != irr::io::EXN_TEXT) - ThrowException( "Invalid contents in element \"up_axis\"."); + ThrowException( "Invalid contents in element \"n\"."); // skip leading whitespace const char* text = mReader->getNodeData(); @@ -1498,7 +1978,7 @@ aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vectorwarn( boost::str( boost::format( "Unknown vertex input type \"%s\". Ignoring.") % pSemantic)); return IT_Invalid; diff --git a/code/ColladaParser.h b/code/ColladaParser.h index 8f05a6c45..be9f92937 100644 --- a/code/ColladaParser.h +++ b/code/ColladaParser.h @@ -84,14 +84,30 @@ protected: /** Reads a material entry into the given material */ void ReadMaterial( Collada::Material& pMaterial); + /** Reads the camera library */ + void ReadCameraLibrary(); + + /** Reads a camera entry into the given camera */ + void ReadCamera( Collada::Camera& pCamera); + + /** Reads the light library */ + void ReadLightLibrary(); + + /** Reads a light entry into the given light */ + void ReadLight( Collada::Light& pLight); + /** Reads the effect library */ void ReadEffectLibrary(); /** Reads an effect entry into the given effect*/ void ReadEffect( Collada::Effect& pEffect); + /** Read sampler properties */ + void ReadSamplerProperties( Collada::Sampler& pSampler); + /** Reads an effect entry containing a color or a texture defining that color */ - void ReadEffectColor( aiColor4D& pColor, std::string& pSampler); + void ReadEffectColor( aiColor4D& pColor, Collada::Sampler& pSampler); + /** Reads an effect entry containing a float */ void ReadEffectFloat( float& pFloat); @@ -101,8 +117,8 @@ protected: /** Reads the geometry library contents */ void ReadGeometryLibrary(); - /** Reads a geometry from the geometry library. */ - void ReadGeometry( Collada::Mesh* pMesh); + /** Reads a geometry from the geometry library. */ + void ReadGeometry( Collada::Mesh* pMesh); /** Reads a mesh from the geometry library */ void ReadMesh( Collada::Mesh* pMesh); @@ -146,6 +162,9 @@ protected: /** Reads the collada scene */ void ReadScene(); + // Processes bind_vertex_input and bind elements + void ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl); + protected: /** Aborts the file reading with an exception */ void ThrowException( const std::string& pError) const; @@ -157,7 +176,10 @@ protected: void SkipElement( const char* pElement); /** Compares the current xml element name to the given string and returns true if equal */ - bool IsElement( const char* pName) const { assert( mReader->getNodeType() == irr::io::EXN_ELEMENT); return strcmp( mReader->getNodeName(), pName) == 0; } + bool IsElement( const char* pName) const { + ai_assert( mReader->getNodeType() == irr::io::EXN_ELEMENT); + return ::strcmp( mReader->getNodeName(), pName) == 0; + } /** Tests for the opening tag of the given element, throws an exception if not found */ void TestOpening( const char* pName); @@ -165,15 +187,24 @@ protected: /** Tests for the closing tag of the given element, throws an exception if not found */ void TestClosing( const char* pName); - /** Checks the present element for the presence of the attribute, returns its index or throws an exception if not found */ + /** Checks the present element for the presence of the attribute, returns its index + or throws an exception if not found */ int GetAttribute( const char* pAttr) const; - /** Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes */ + /** Returns the index of the named attribute or -1 if not found. Does not throw, + therefore useful for optional attributes */ int TestAttribute( const char* pAttr) const; - /** Reads the text contents of an element, throws an exception if not given. Skips leading whitespace. */ + /** Reads the text contents of an element, throws an exception if not given. + Skips leading whitespace. */ const char* GetTextContent(); + /** Reads a single bool from current text content */ + bool ReadBoolFromTextContent(); + + /** Reads a single float from current text content */ + float ReadFloatFromTextContent(); + /** Calculates the resulting transformation fromm all the given transform steps */ aiMatrix4x4 CalculateResultTransform( const std::vector& pTransforms) const; @@ -181,7 +212,8 @@ protected: Collada::InputType GetTypeForSemantic( const std::string& pSemantic); /** Finds the item in the given library by its reference, throws if not found */ - template const Type& ResolveLibraryReference( const std::map& pLibrary, const std::string& pURL) const; + template const Type& ResolveLibraryReference( + const std::map& pLibrary, const std::string& pURL) const; protected: /** Filename, for a verbose error message */ @@ -218,6 +250,14 @@ protected: typedef std::map MaterialLibrary; MaterialLibrary mMaterialLibrary; + /** Light library: surface light by ID */ + typedef std::map LightLibrary; + LightLibrary mLightLibrary; + + /** Camera library: surface material by ID */ + typedef std::map CameraLibrary; + CameraLibrary mCameraLibrary; + /** Pointer to the root node. Don't delete, it just points to one of the nodes in the node library. */ Collada::Node* mRootNode; diff --git a/code/MaterialSystem.h b/code/MaterialSystem.h index 70a4f0209..f2370cc0e 100644 --- a/code/MaterialSystem.h +++ b/code/MaterialSystem.h @@ -177,6 +177,19 @@ inline aiReturn MaterialHelper::AddProperty (const float* pInput, pKey,type,index,aiPTI_Float); } +// ---------------------------------------------------------------------------------------- +template<> +inline aiReturn MaterialHelper::AddProperty (const aiUVTransform* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiUVTransform), + pKey,type,index,aiPTI_Float); +} + // ---------------------------------------------------------------------------------------- template<> inline aiReturn MaterialHelper::AddProperty (const aiColor4D* pInput, diff --git a/include/aiMaterial.h b/include/aiMaterial.h index 8179400d9..7f3a2ac0c 100644 --- a/include/aiMaterial.h +++ b/include/aiMaterial.h @@ -237,30 +237,34 @@ enum aiAxis */ enum aiTextureType { + // Set as texture semantic for material properties not related to textures + aiTextureType_NONE = 0x0, + + /** The texture is combined with the result of the diffuse * lighting equation. */ - aiTextureType_DIFFUSE = 0x0, + aiTextureType_DIFFUSE = 0x1, /** The texture is combined with the result of the specular * lighting equation. */ - aiTextureType_SPECULAR = 0x1, + aiTextureType_SPECULAR = 0x2, /** The texture is combined with the result of the ambient * lighting equation. */ - aiTextureType_AMBIENT = 0x2, + aiTextureType_AMBIENT = 0x3, /** The texture is added to the result of the lighting - * calculation. It isn't influenced by any lighting. + * calculation. It isn't influenced by lights. */ - aiTextureType_EMISSIVE = 0x3, + aiTextureType_EMISSIVE = 0x4, /** The texture is a height map and serves as input for * a normal map generator. */ - aiTextureType_HEIGHT = 0x4, + aiTextureType_HEIGHT = 0x5, /** The texture is a (tangent space) normal-map. * @@ -268,7 +272,7 @@ enum aiTextureType * for use with techniques such as Parallax Occlusion Mapping * it is registered once as a normalmap. */ - aiTextureType_NORMALS = 0x5, + aiTextureType_NORMALS = 0x6, /** The texture defines the glossiness of the material. * @@ -277,19 +281,19 @@ enum aiTextureType * function define to map the linear color values in the * texture to a suitable exponent. Have fun. */ - aiTextureType_SHININESS = 0x6, + aiTextureType_SHININESS = 0x7, /** The texture defines a per-pixel opacity. * * Normally 'white' means opaque and 'black' means * 'transparency'. Or quite the opposite. Have fun. */ - aiTextureType_OPACITY = 0x7, + aiTextureType_OPACITY = 0x8, /** This value is not used. It is just here to force the - * compiler to map this enum to a 32 Bit integer. - */ + * compiler to map this enum to a 32 Bit integer. + */ _aiTextureType_Force32Bit = 0x9fffffff }; @@ -369,6 +373,25 @@ enum aiShadingMode _aiShadingMode_Force32Bit = 0x9fffffff }; + +// --------------------------------------------------------------------------- +/** @brief Defines flags for a texture. + * + * See +*/ +enum aiTextureFlags +{ + /** The texture's color values have to be inverted (componentwise 1-n) + */ + aiTextureFlags_Invert = 0x1, + + + /** This value is not used. It is just there to force the + * compiler to map this enum to a 32 Bit integer. + */ + _aiTextureFlags_Force32Bit = 0x9fffffff +}; + #include "./Compiler/pushpack1.h" // --------------------------------------------------------------------------- @@ -707,7 +730,8 @@ extern "C" { * Default value to be assumed if this key isn't there: n/a
*/ // --------------------------------------------------------------------------- -#define AI_MATKEY_TEXTURE(type, N) "$tex.file",type,N +#define _AI_MATKEY_TEXTURE_BASE "$tex.file" +#define AI_MATKEY_TEXTURE(type, N) _AI_MATKEY_TEXTURE_BASE,type,N // for backward compatibility #define AI_MATKEY_TEXTURE_DIFFUSE(N) \ @@ -747,7 +771,8 @@ extern "C" { * and AI_MATKEY_MAPPING(type,N) == UV
*/ // --------------------------------------------------------------------------- -#define AI_MATKEY_UVWSRC(type, N) "$tex.uvwsrc",type,N +#define _AI_MATKEY_UVWSRC_BASE "$tex.uvwsrc" +#define AI_MATKEY_UVWSRC(type, N) _AI_MATKEY_UVWSRC_BASE,type,N // for backward compatibility #define AI_MATKEY_UVWSRC_DIFFUSE(N) \ @@ -786,7 +811,8 @@ extern "C" { * Requires: AI_MATKEY_TEXTURE(type,N)
*/ // --------------------------------------------------------------------------- -#define AI_MATKEY_TEXOP(type, N) "$tex.op",type,N +#define _AI_MATKEY_TEXOP_BASE "$tex.op" +#define AI_MATKEY_TEXOP(type, N) _AI_MATKEY_TEXOP_BASE,type,N // for backward compatibility #define AI_MATKEY_TEXOP_DIFFUSE(N) \ @@ -824,7 +850,8 @@ extern "C" { * Requires: AI_MATKEY_TEXTURE(type,N)
*/ // --------------------------------------------------------------------------- -#define AI_MATKEY_MAPPING(type, N) "$tex.mapping",type,N +#define _AI_MATKEY_MAPPING_BASE "$tex.mapping" +#define AI_MATKEY_MAPPING(type, N) _AI_MATKEY_MAPPING_BASE,type,N // for backward compatibility #define AI_MATKEY_MAPPING_DIFFUSE(N) \ @@ -855,14 +882,16 @@ extern "C" { /** @def AI_MATKEY_TEXBLEND ( * Parameters: type, N
* Specifies the strength of the th texture of type . This is just - * a multiplier for the texture's color values. + * a multiplier for the texture's color values. It may have any value, even + * outside [0..1] *
* Type: float
* Default value to be assumed if this key isn't there: 1.f
* Requires: AI_MATKEY_TEXTURE(type,N)
*/ // --------------------------------------------------------------------------- -#define AI_MATKEY_TEXBLEND(type, N) "$tex.blend",type,N +#define _AI_MATKEY_TEXBLEND_BASE "$tex.blend" +#define AI_MATKEY_TEXBLEND(type, N) _AI_MATKEY_TEXBLEND_BASE,type,N // for backward compatibility #define AI_MATKEY_TEXBLEND_DIFFUSE(N) \ @@ -900,7 +929,8 @@ extern "C" { * Requires: AI_MATKEY_TEXTURE(type,N)
*/ // --------------------------------------------------------------------------- -#define AI_MATKEY_MAPPINGMODE_U(type, N) "$tex.mapmodeu",type,N +#define _AI_MATKEY_MAPPINGMODE_U_BASE "$tex.mapmodeu" +#define AI_MATKEY_MAPPINGMODE_U(type, N) _AI_MATKEY_MAPPINGMODE_U_BASE,type,N // for backward compatibility #define AI_MATKEY_MAPPINGMODE_U_DIFFUSE(N) \ @@ -938,7 +968,8 @@ extern "C" { * Requires: AI_MATKEY_TEXTURE(type,N)
*/ // --------------------------------------------------------------------------- -#define AI_MATKEY_MAPPINGMODE_V(type, N) "$tex.mapmodev",type,N +#define _AI_MATKEY_MAPPINGMODE_V_BASE "$tex.mapmodev" +#define AI_MATKEY_MAPPINGMODE_V(type, N) _AI_MATKEY_MAPPINGMODE_V_BASE,type,N // for backward compatibility #define AI_MATKEY_MAPPINGMODE_V_DIFFUSE(N) \ @@ -976,7 +1007,8 @@ extern "C" { * Requires: AI_MATKEY_TEXTURE(type,N)
*/ // --------------------------------------------------------------------------- -#define AI_MATKEY_MAPPINGMODE_W(type, N) "$tex.mapmodew",type,N +#define _AI_MATKEY_MAPPINGMODE_W_BASE "$tex.mapmodew" +#define AI_MATKEY_MAPPINGMODE_W(type, N) _AI_MATKEY_MAPPINGMODE_W_BASE,type,N // for backward compatibility #define AI_MATKEY_MAPPINGMODE_W_DIFFUSE(N) \ @@ -1017,7 +1049,8 @@ extern "C" { * AI_MATKEY_MAPPING(type,N) != UV
*/ // --------------------------------------------------------------------------- -#define AI_MATKEY_TEXMAP_AXIS(type, N) "$tex.mapaxis",type,N +#define _AI_MATKEY_TEXMAP_AXIS_BASE "$tex.mapaxis" +#define AI_MATKEY_TEXMAP_AXIS(type, N) _AI_MATKEY_TEXMAP_AXIS_BASE,type,N // --------------------------------------------------------------------------- /** @def AI_MATKEY_UVTRANSFORM @@ -1033,7 +1066,8 @@ extern "C" { * Note:Transformed 3D texture coordinates are not supported */ // --------------------------------------------------------------------------- -#define AI_MATKEY_UVTRANSFORM(type, N) "$tex.uvtrafo",type,N +#define _AI_MATKEY_UVTRANSFORM_BASE "$tex.uvtrafo" +#define AI_MATKEY_UVTRANSFORM(type, N) _AI_MATKEY_UVTRANSFORM_BASE,type,N // for backward compatibility #define AI_MATKEY_UVTRANSFORM_DIFFUSE(N) \