vrshoot
diff libs/assimp/ColladaLoader.cpp @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/assimp/ColladaLoader.cpp Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,1559 @@ 1.4 +/* 1.5 +--------------------------------------------------------------------------- 1.6 +Open Asset Import Library (assimp) 1.7 +--------------------------------------------------------------------------- 1.8 + 1.9 +Copyright (c) 2006-2012, assimp team 1.10 + 1.11 +All rights reserved. 1.12 + 1.13 +Redistribution and use of this software in source and binary forms, 1.14 +with or without modification, are permitted provided that the following 1.15 +conditions are met: 1.16 + 1.17 +* Redistributions of source code must retain the above 1.18 +copyright notice, this list of conditions and the 1.19 +following disclaimer. 1.20 + 1.21 +* Redistributions in binary form must reproduce the above 1.22 +copyright notice, this list of conditions and the 1.23 +following disclaimer in the documentation and/or other 1.24 +materials provided with the distribution. 1.25 + 1.26 +* Neither the name of the assimp team, nor the names of its 1.27 +contributors may be used to endorse or promote products 1.28 +derived from this software without specific prior 1.29 +written permission of the assimp team. 1.30 + 1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.42 +--------------------------------------------------------------------------- 1.43 +*/ 1.44 + 1.45 +/** @file Implementation of the Collada loader */ 1.46 + 1.47 +#include "AssimpPCH.h" 1.48 +#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER 1.49 + 1.50 +#include "assimp/anim.h" 1.51 +#include "ColladaLoader.h" 1.52 +#include "ColladaParser.h" 1.53 + 1.54 +#include "fast_atof.h" 1.55 +#include "ParsingUtils.h" 1.56 +#include "SkeletonMeshBuilder.h" 1.57 + 1.58 +#include "time.h" 1.59 + 1.60 +using namespace Assimp; 1.61 + 1.62 +static const aiImporterDesc desc = { 1.63 + "Collada Importer", 1.64 + "", 1.65 + "", 1.66 + "http://collada.org", 1.67 + aiImporterFlags_SupportTextFlavour, 1.68 + 1, 1.69 + 3, 1.70 + 1, 1.71 + 5, 1.72 + "dae" 1.73 +}; 1.74 + 1.75 + 1.76 +// ------------------------------------------------------------------------------------------------ 1.77 +// Constructor to be privately used by Importer 1.78 +ColladaLoader::ColladaLoader() 1.79 +: noSkeletonMesh() 1.80 +{} 1.81 + 1.82 +// ------------------------------------------------------------------------------------------------ 1.83 +// Destructor, private as well 1.84 +ColladaLoader::~ColladaLoader() 1.85 +{} 1.86 + 1.87 +// ------------------------------------------------------------------------------------------------ 1.88 +// Returns whether the class can handle the format of the given file. 1.89 +bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const 1.90 +{ 1.91 + // check file extension 1.92 + std::string extension = GetExtension(pFile); 1.93 + 1.94 + if( extension == "dae") 1.95 + return true; 1.96 + 1.97 + // XML - too generic, we need to open the file and search for typical keywords 1.98 + if( extension == "xml" || !extension.length() || checkSig) { 1.99 + /* If CanRead() is called in order to check whether we 1.100 + * support a specific file extension in general pIOHandler 1.101 + * might be NULL and it's our duty to return true here. 1.102 + */ 1.103 + if (!pIOHandler)return true; 1.104 + const char* tokens[] = {"collada"}; 1.105 + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); 1.106 + } 1.107 + return false; 1.108 +} 1.109 + 1.110 +// ------------------------------------------------------------------------------------------------ 1.111 +void ColladaLoader::SetupProperties(const Importer* pImp) 1.112 +{ 1.113 + noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; 1.114 +} 1.115 + 1.116 + 1.117 +// ------------------------------------------------------------------------------------------------ 1.118 +// Get file extension list 1.119 +const aiImporterDesc* ColladaLoader::GetInfo () const 1.120 +{ 1.121 + return &desc; 1.122 +} 1.123 + 1.124 +// ------------------------------------------------------------------------------------------------ 1.125 +// Imports the given file into the given scene structure. 1.126 +void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) 1.127 +{ 1.128 + mFileName = pFile; 1.129 + 1.130 + // clean all member arrays - just for safety, it should work even if we did not 1.131 + mMeshIndexByID.clear(); 1.132 + mMaterialIndexByName.clear(); 1.133 + mMeshes.clear(); 1.134 + newMats.clear(); 1.135 + mLights.clear(); 1.136 + mCameras.clear(); 1.137 + mTextures.clear(); 1.138 + 1.139 + // parse the input file 1.140 + ColladaParser parser( pIOHandler, pFile); 1.141 + 1.142 + if( !parser.mRootNode) 1.143 + throw DeadlyImportError( "Collada: File came out empty. Something is wrong here."); 1.144 + 1.145 + // reserve some storage to avoid unnecessary reallocs 1.146 + newMats.reserve(parser.mMaterialLibrary.size()*2); 1.147 + mMeshes.reserve(parser.mMeshLibrary.size()*2); 1.148 + 1.149 + mCameras.reserve(parser.mCameraLibrary.size()); 1.150 + mLights.reserve(parser.mLightLibrary.size()); 1.151 + 1.152 + // create the materials first, for the meshes to find 1.153 + BuildMaterials( parser, pScene); 1.154 + 1.155 + // build the node hierarchy from it 1.156 + pScene->mRootNode = BuildHierarchy( parser, parser.mRootNode); 1.157 + 1.158 + // ... then fill the materials with the now adjusted settings 1.159 + FillMaterials(parser, pScene); 1.160 + 1.161 + // Apply unitsize scale calculation 1.162 + pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0, 1.163 + 0, parser.mUnitSize, 0, 0, 1.164 + 0, 0, parser.mUnitSize, 0, 1.165 + 0, 0, 0, 1); 1.166 + 1.167 + // Convert to Y_UP, if different orientation 1.168 + if( parser.mUpDirection == ColladaParser::UP_X) 1.169 + pScene->mRootNode->mTransformation *= aiMatrix4x4( 1.170 + 0, -1, 0, 0, 1.171 + 1, 0, 0, 0, 1.172 + 0, 0, 1, 0, 1.173 + 0, 0, 0, 1); 1.174 + else if( parser.mUpDirection == ColladaParser::UP_Z) 1.175 + pScene->mRootNode->mTransformation *= aiMatrix4x4( 1.176 + 1, 0, 0, 0, 1.177 + 0, 0, 1, 0, 1.178 + 0, -1, 0, 0, 1.179 + 0, 0, 0, 1); 1.180 + 1.181 + // store all meshes 1.182 + StoreSceneMeshes( pScene); 1.183 + 1.184 + // store all materials 1.185 + StoreSceneMaterials( pScene); 1.186 + 1.187 + // store all lights 1.188 + StoreSceneLights( pScene); 1.189 + 1.190 + // store all cameras 1.191 + StoreSceneCameras( pScene); 1.192 + 1.193 + // store all animations 1.194 + StoreAnimations( pScene, parser); 1.195 + 1.196 + 1.197 + // If no meshes have been loaded, it's probably just an animated skeleton. 1.198 + if (!pScene->mNumMeshes) { 1.199 + 1.200 + if (!noSkeletonMesh) { 1.201 + SkeletonMeshBuilder hero(pScene); 1.202 + } 1.203 + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; 1.204 + } 1.205 +} 1.206 + 1.207 +// ------------------------------------------------------------------------------------------------ 1.208 +// Recursively constructs a scene node for the given parser node and returns it. 1.209 +aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode) 1.210 +{ 1.211 + // create a node for it 1.212 + aiNode* node = new aiNode(); 1.213 + 1.214 + // find a name for the new node. It's more complicated than you might think 1.215 + node->mName.Set( FindNameForNode( pNode)); 1.216 + 1.217 + // calculate the transformation matrix for it 1.218 + node->mTransformation = pParser.CalculateResultTransform( pNode->mTransforms); 1.219 + 1.220 + // now resolve node instances 1.221 + std::vector<const Collada::Node*> instances; 1.222 + ResolveNodeInstances(pParser,pNode,instances); 1.223 + 1.224 + // add children. first the *real* ones 1.225 + node->mNumChildren = pNode->mChildren.size()+instances.size(); 1.226 + node->mChildren = new aiNode*[node->mNumChildren]; 1.227 + 1.228 + for( size_t a = 0; a < pNode->mChildren.size(); a++) 1.229 + { 1.230 + node->mChildren[a] = BuildHierarchy( pParser, pNode->mChildren[a]); 1.231 + node->mChildren[a]->mParent = node; 1.232 + } 1.233 + 1.234 + // ... and finally the resolved node instances 1.235 + for( size_t a = 0; a < instances.size(); a++) 1.236 + { 1.237 + node->mChildren[pNode->mChildren.size() + a] = BuildHierarchy( pParser, instances[a]); 1.238 + node->mChildren[pNode->mChildren.size() + a]->mParent = node; 1.239 + } 1.240 + 1.241 + // construct meshes 1.242 + BuildMeshesForNode( pParser, pNode, node); 1.243 + 1.244 + // construct cameras 1.245 + BuildCamerasForNode(pParser, pNode, node); 1.246 + 1.247 + // construct lights 1.248 + BuildLightsForNode(pParser, pNode, node); 1.249 + return node; 1.250 +} 1.251 + 1.252 +// ------------------------------------------------------------------------------------------------ 1.253 +// Resolve node instances 1.254 +void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode, 1.255 + std::vector<const Collada::Node*>& resolved) 1.256 +{ 1.257 + // reserve enough storage 1.258 + resolved.reserve(pNode->mNodeInstances.size()); 1.259 + 1.260 + // ... and iterate through all nodes to be instanced as children of pNode 1.261 + for (std::vector<Collada::NodeInstance>::const_iterator it = pNode->mNodeInstances.begin(), 1.262 + end = pNode->mNodeInstances.end(); it != end; ++it) 1.263 + { 1.264 + // find the corresponding node in the library 1.265 + const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find((*it).mNode); 1.266 + const Collada::Node* nd = itt == pParser.mNodeLibrary.end() ? NULL : (*itt).second; 1.267 + 1.268 + // FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632 1.269 + // need to check for both name and ID to catch all. To avoid breaking valid files, 1.270 + // the workaround is only enabled when the first attempt to resolve the node has failed. 1.271 + if (!nd) { 1.272 + nd = FindNode(pParser.mRootNode,(*it).mNode); 1.273 + } 1.274 + if (!nd) 1.275 + DefaultLogger::get()->error("Collada: Unable to resolve reference to instanced node " + (*it).mNode); 1.276 + 1.277 + else { 1.278 + // attach this node to the list of children 1.279 + resolved.push_back(nd); 1.280 + } 1.281 + } 1.282 +} 1.283 + 1.284 +// ------------------------------------------------------------------------------------------------ 1.285 +// Resolve UV channels 1.286 +void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler, 1.287 + const Collada::SemanticMappingTable& table) 1.288 +{ 1.289 + std::map<std::string, Collada::InputSemanticMapEntry>::const_iterator it = table.mMap.find(sampler.mUVChannel); 1.290 + if (it != table.mMap.end()) { 1.291 + if (it->second.mType != Collada::IT_Texcoord) 1.292 + DefaultLogger::get()->error("Collada: Unexpected effect input mapping"); 1.293 + 1.294 + sampler.mUVId = it->second.mSet; 1.295 + } 1.296 +} 1.297 + 1.298 +// ------------------------------------------------------------------------------------------------ 1.299 +// Builds lights for the given node and references them 1.300 +void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget) 1.301 +{ 1.302 + BOOST_FOREACH( const Collada::LightInstance& lid, pNode->mLights) 1.303 + { 1.304 + // find the referred light 1.305 + ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find( lid.mLight); 1.306 + if( srcLightIt == pParser.mLightLibrary.end()) 1.307 + { 1.308 + DefaultLogger::get()->warn("Collada: Unable to find light for ID \"" + lid.mLight + "\". Skipping."); 1.309 + continue; 1.310 + } 1.311 + const Collada::Light* srcLight = &srcLightIt->second; 1.312 + if (srcLight->mType == aiLightSource_AMBIENT) { 1.313 + DefaultLogger::get()->error("Collada: Skipping ambient light for the moment"); 1.314 + continue; 1.315 + } 1.316 + 1.317 + // now fill our ai data structure 1.318 + aiLight* out = new aiLight(); 1.319 + out->mName = pTarget->mName; 1.320 + out->mType = (aiLightSourceType)srcLight->mType; 1.321 + 1.322 + // collada lights point in -Z by default, rest is specified in node transform 1.323 + out->mDirection = aiVector3D(0.f,0.f,-1.f); 1.324 + 1.325 + out->mAttenuationConstant = srcLight->mAttConstant; 1.326 + out->mAttenuationLinear = srcLight->mAttLinear; 1.327 + out->mAttenuationQuadratic = srcLight->mAttQuadratic; 1.328 + 1.329 + // collada doesn't differenciate between these color types 1.330 + out->mColorDiffuse = out->mColorSpecular = out->mColorAmbient = srcLight->mColor*srcLight->mIntensity; 1.331 + 1.332 + // convert falloff angle and falloff exponent in our representation, if given 1.333 + if (out->mType == aiLightSource_SPOT) { 1.334 + 1.335 + out->mAngleInnerCone = AI_DEG_TO_RAD( srcLight->mFalloffAngle ); 1.336 + 1.337 + // ... some extension magic. 1.338 + if (srcLight->mOuterAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f)) 1.339 + { 1.340 + // ... some deprecation magic. 1.341 + if (srcLight->mPenumbraAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f)) 1.342 + { 1.343 + // Need to rely on falloff_exponent. I don't know how to interpret it, so I need to guess .... 1.344 + // epsilon chosen to be 0.1 1.345 + out->mAngleOuterCone = AI_DEG_TO_RAD (acos(pow(0.1f,1.f/srcLight->mFalloffExponent))+ 1.346 + srcLight->mFalloffAngle); 1.347 + } 1.348 + else { 1.349 + out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD( srcLight->mPenumbraAngle ); 1.350 + if (out->mAngleOuterCone < out->mAngleInnerCone) 1.351 + std::swap(out->mAngleInnerCone,out->mAngleOuterCone); 1.352 + } 1.353 + } 1.354 + else out->mAngleOuterCone = AI_DEG_TO_RAD( srcLight->mOuterAngle ); 1.355 + } 1.356 + 1.357 + // add to light list 1.358 + mLights.push_back(out); 1.359 + } 1.360 +} 1.361 + 1.362 +// ------------------------------------------------------------------------------------------------ 1.363 +// Builds cameras for the given node and references them 1.364 +void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget) 1.365 +{ 1.366 + BOOST_FOREACH( const Collada::CameraInstance& cid, pNode->mCameras) 1.367 + { 1.368 + // find the referred light 1.369 + ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find( cid.mCamera); 1.370 + if( srcCameraIt == pParser.mCameraLibrary.end()) 1.371 + { 1.372 + DefaultLogger::get()->warn("Collada: Unable to find camera for ID \"" + cid.mCamera + "\". Skipping."); 1.373 + continue; 1.374 + } 1.375 + const Collada::Camera* srcCamera = &srcCameraIt->second; 1.376 + 1.377 + // orthographic cameras not yet supported in Assimp 1.378 + if (srcCamera->mOrtho) { 1.379 + DefaultLogger::get()->warn("Collada: Orthographic cameras are not supported."); 1.380 + } 1.381 + 1.382 + // now fill our ai data structure 1.383 + aiCamera* out = new aiCamera(); 1.384 + out->mName = pTarget->mName; 1.385 + 1.386 + // collada cameras point in -Z by default, rest is specified in node transform 1.387 + out->mLookAt = aiVector3D(0.f,0.f,-1.f); 1.388 + 1.389 + // near/far z is already ok 1.390 + out->mClipPlaneFar = srcCamera->mZFar; 1.391 + out->mClipPlaneNear = srcCamera->mZNear; 1.392 + 1.393 + // ... but for the rest some values are optional 1.394 + // and we need to compute the others in any combination. 1.395 + if (srcCamera->mAspect != 10e10f) 1.396 + out->mAspect = srcCamera->mAspect; 1.397 + 1.398 + if (srcCamera->mHorFov != 10e10f) { 1.399 + out->mHorizontalFOV = srcCamera->mHorFov; 1.400 + 1.401 + if (srcCamera->mVerFov != 10e10f && srcCamera->mAspect == 10e10f) { 1.402 + out->mAspect = tan(AI_DEG_TO_RAD(srcCamera->mHorFov)) / 1.403 + tan(AI_DEG_TO_RAD(srcCamera->mVerFov)); 1.404 + } 1.405 + } 1.406 + else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) { 1.407 + out->mHorizontalFOV = 2.0f * AI_RAD_TO_DEG(atan(srcCamera->mAspect * 1.408 + tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f))); 1.409 + } 1.410 + 1.411 + // Collada uses degrees, we use radians 1.412 + out->mHorizontalFOV = AI_DEG_TO_RAD(out->mHorizontalFOV); 1.413 + 1.414 + // add to camera list 1.415 + mCameras.push_back(out); 1.416 + } 1.417 +} 1.418 + 1.419 +// ------------------------------------------------------------------------------------------------ 1.420 +// Builds meshes for the given node and references them 1.421 +void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget) 1.422 +{ 1.423 + // accumulated mesh references by this node 1.424 + std::vector<size_t> newMeshRefs; 1.425 + newMeshRefs.reserve(pNode->mMeshes.size()); 1.426 + 1.427 + // add a mesh for each subgroup in each collada mesh 1.428 + BOOST_FOREACH( const Collada::MeshInstance& mid, pNode->mMeshes) 1.429 + { 1.430 + const Collada::Mesh* srcMesh = NULL; 1.431 + const Collada::Controller* srcController = NULL; 1.432 + 1.433 + // find the referred mesh 1.434 + ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMeshOrController); 1.435 + if( srcMeshIt == pParser.mMeshLibrary.end()) 1.436 + { 1.437 + // if not found in the mesh-library, it might also be a controller referring to a mesh 1.438 + ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find( mid.mMeshOrController); 1.439 + if( srcContrIt != pParser.mControllerLibrary.end()) 1.440 + { 1.441 + srcController = &srcContrIt->second; 1.442 + srcMeshIt = pParser.mMeshLibrary.find( srcController->mMeshId); 1.443 + if( srcMeshIt != pParser.mMeshLibrary.end()) 1.444 + srcMesh = srcMeshIt->second; 1.445 + } 1.446 + 1.447 + if( !srcMesh) 1.448 + { 1.449 + DefaultLogger::get()->warn( boost::str( boost::format( "Collada: Unable to find geometry for ID \"%s\". Skipping.") % mid.mMeshOrController)); 1.450 + continue; 1.451 + } 1.452 + } else 1.453 + { 1.454 + // ID found in the mesh library -> direct reference to an unskinned mesh 1.455 + srcMesh = srcMeshIt->second; 1.456 + } 1.457 + 1.458 + // build a mesh for each of its subgroups 1.459 + size_t vertexStart = 0, faceStart = 0; 1.460 + for( size_t sm = 0; sm < srcMesh->mSubMeshes.size(); ++sm) 1.461 + { 1.462 + const Collada::SubMesh& submesh = srcMesh->mSubMeshes[sm]; 1.463 + if( submesh.mNumFaces == 0) 1.464 + continue; 1.465 + 1.466 + // find material assigned to this submesh 1.467 + std::string meshMaterial; 1.468 + std::map<std::string, Collada::SemanticMappingTable >::const_iterator meshMatIt = mid.mMaterials.find( submesh.mMaterial); 1.469 + 1.470 + const Collada::SemanticMappingTable* table = NULL; 1.471 + if( meshMatIt != mid.mMaterials.end()) 1.472 + { 1.473 + table = &meshMatIt->second; 1.474 + meshMaterial = table->mMatName; 1.475 + } 1.476 + else 1.477 + { 1.478 + DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup <%s> in geometry <%s>.") % submesh.mMaterial % mid.mMeshOrController)); 1.479 + if( !mid.mMaterials.empty() ) 1.480 + meshMaterial = mid.mMaterials.begin()->second.mMatName; 1.481 + } 1.482 + 1.483 + // OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table 1.484 + // given. The only mapping stuff which we do actually support is the UV channel. 1.485 + std::map<std::string, size_t>::const_iterator matIt = mMaterialIndexByName.find( meshMaterial); 1.486 + unsigned int matIdx; 1.487 + if( matIt != mMaterialIndexByName.end()) 1.488 + matIdx = matIt->second; 1.489 + else 1.490 + matIdx = 0; 1.491 + 1.492 + if (table && !table->mMap.empty() ) { 1.493 + std::pair<Collada::Effect*, aiMaterial*>& mat = newMats[matIdx]; 1.494 + 1.495 + // Iterate through all texture channels assigned to the effect and 1.496 + // check whether we have mapping information for it. 1.497 + ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table); 1.498 + ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table); 1.499 + ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table); 1.500 + ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table); 1.501 + ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent,*table); 1.502 + ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table); 1.503 + } 1.504 + 1.505 + // built lookup index of the Mesh-Submesh-Material combination 1.506 + ColladaMeshIndex index( mid.mMeshOrController, sm, meshMaterial); 1.507 + 1.508 + // if we already have the mesh at the library, just add its index to the node's array 1.509 + std::map<ColladaMeshIndex, size_t>::const_iterator dstMeshIt = mMeshIndexByID.find( index); 1.510 + if( dstMeshIt != mMeshIndexByID.end()) { 1.511 + newMeshRefs.push_back( dstMeshIt->second); 1.512 + } 1.513 + else 1.514 + { 1.515 + // else we have to add the mesh to the collection and store its newly assigned index at the node 1.516 + aiMesh* dstMesh = CreateMesh( pParser, srcMesh, submesh, srcController, vertexStart, faceStart); 1.517 + 1.518 + // store the mesh, and store its new index in the node 1.519 + newMeshRefs.push_back( mMeshes.size()); 1.520 + mMeshIndexByID[index] = mMeshes.size(); 1.521 + mMeshes.push_back( dstMesh); 1.522 + vertexStart += dstMesh->mNumVertices; faceStart += submesh.mNumFaces; 1.523 + 1.524 + // assign the material index 1.525 + dstMesh->mMaterialIndex = matIdx; 1.526 + dstMesh->mName = mid.mMeshOrController; 1.527 + } 1.528 + } 1.529 + } 1.530 + 1.531 + // now place all mesh references we gathered in the target node 1.532 + pTarget->mNumMeshes = newMeshRefs.size(); 1.533 + if( newMeshRefs.size()) 1.534 + { 1.535 + pTarget->mMeshes = new unsigned int[pTarget->mNumMeshes]; 1.536 + std::copy( newMeshRefs.begin(), newMeshRefs.end(), pTarget->mMeshes); 1.537 + } 1.538 +} 1.539 + 1.540 +// ------------------------------------------------------------------------------------------------ 1.541 +// Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh 1.542 +aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh, 1.543 + const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace) 1.544 +{ 1.545 + aiMesh* dstMesh = new aiMesh; 1.546 + 1.547 + // count the vertices addressed by its faces 1.548 + const size_t numVertices = std::accumulate( pSrcMesh->mFaceSize.begin() + pStartFace, 1.549 + pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, 0); 1.550 + 1.551 + // copy positions 1.552 + dstMesh->mNumVertices = numVertices; 1.553 + dstMesh->mVertices = new aiVector3D[numVertices]; 1.554 + std::copy( pSrcMesh->mPositions.begin() + pStartVertex, pSrcMesh->mPositions.begin() + 1.555 + pStartVertex + numVertices, dstMesh->mVertices); 1.556 + 1.557 + // normals, if given. HACK: (thom) Due to the glorious Collada spec we never 1.558 + // know if we have the same number of normals as there are positions. So we 1.559 + // also ignore any vertex attribute if it has a different count 1.560 + if( pSrcMesh->mNormals.size() >= pStartVertex + numVertices) 1.561 + { 1.562 + dstMesh->mNormals = new aiVector3D[numVertices]; 1.563 + std::copy( pSrcMesh->mNormals.begin() + pStartVertex, pSrcMesh->mNormals.begin() + 1.564 + pStartVertex + numVertices, dstMesh->mNormals); 1.565 + } 1.566 + 1.567 + // tangents, if given. 1.568 + if( pSrcMesh->mTangents.size() >= pStartVertex + numVertices) 1.569 + { 1.570 + dstMesh->mTangents = new aiVector3D[numVertices]; 1.571 + std::copy( pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() + 1.572 + pStartVertex + numVertices, dstMesh->mTangents); 1.573 + } 1.574 + 1.575 + // bitangents, if given. 1.576 + if( pSrcMesh->mBitangents.size() >= pStartVertex + numVertices) 1.577 + { 1.578 + dstMesh->mBitangents = new aiVector3D[numVertices]; 1.579 + std::copy( pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() + 1.580 + pStartVertex + numVertices, dstMesh->mBitangents); 1.581 + } 1.582 + 1.583 + // same for texturecoords, as many as we have 1.584 + // empty slots are not allowed, need to pack and adjust UV indexes accordingly 1.585 + for( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) 1.586 + { 1.587 + if( pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices) 1.588 + { 1.589 + dstMesh->mTextureCoords[real] = new aiVector3D[numVertices]; 1.590 + for( size_t b = 0; b < numVertices; ++b) 1.591 + dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex+b]; 1.592 + 1.593 + dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a]; 1.594 + ++real; 1.595 + } 1.596 + } 1.597 + 1.598 + // same for vertex colors, as many as we have. again the same packing to avoid empty slots 1.599 + for( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) 1.600 + { 1.601 + if( pSrcMesh->mColors[a].size() >= pStartVertex + numVertices) 1.602 + { 1.603 + dstMesh->mColors[real] = new aiColor4D[numVertices]; 1.604 + std::copy( pSrcMesh->mColors[a].begin() + pStartVertex, pSrcMesh->mColors[a].begin() + pStartVertex + numVertices,dstMesh->mColors[real]); 1.605 + ++real; 1.606 + } 1.607 + } 1.608 + 1.609 + // create faces. Due to the fact that each face uses unique vertices, we can simply count up on each vertex 1.610 + size_t vertex = 0; 1.611 + dstMesh->mNumFaces = pSubMesh.mNumFaces; 1.612 + dstMesh->mFaces = new aiFace[dstMesh->mNumFaces]; 1.613 + for( size_t a = 0; a < dstMesh->mNumFaces; ++a) 1.614 + { 1.615 + size_t s = pSrcMesh->mFaceSize[ pStartFace + a]; 1.616 + aiFace& face = dstMesh->mFaces[a]; 1.617 + face.mNumIndices = s; 1.618 + face.mIndices = new unsigned int[s]; 1.619 + for( size_t b = 0; b < s; ++b) 1.620 + face.mIndices[b] = vertex++; 1.621 + } 1.622 + 1.623 + // create bones if given 1.624 + if( pSrcController) 1.625 + { 1.626 + // refuse if the vertex count does not match 1.627 +// if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices) 1.628 +// throw DeadlyImportError( "Joint Controller vertex count does not match mesh vertex count"); 1.629 + 1.630 + // resolve references - joint names 1.631 + const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource); 1.632 + const Collada::Data& jointNames = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointNamesAcc.mSource); 1.633 + // joint offset matrices 1.634 + const Collada::Accessor& jointMatrixAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource); 1.635 + const Collada::Data& jointMatrices = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointMatrixAcc.mSource); 1.636 + // joint vertex_weight name list - should refer to the same list as the joint names above. If not, report and reconsider 1.637 + const Collada::Accessor& weightNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor); 1.638 + if( &weightNamesAcc != &jointNamesAcc) 1.639 + throw DeadlyImportError( "Temporary implementational lazyness. If you read this, please report to the author."); 1.640 + // vertex weights 1.641 + const Collada::Accessor& weightsAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor); 1.642 + const Collada::Data& weights = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightsAcc.mSource); 1.643 + 1.644 + if( !jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray) 1.645 + throw DeadlyImportError( "Data type mismatch while resolving mesh joints"); 1.646 + // sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex 1.647 + if( pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1) 1.648 + throw DeadlyImportError( "Unsupported vertex_weight addressing scheme. "); 1.649 + 1.650 + // create containers to collect the weights for each bone 1.651 + size_t numBones = jointNames.mStrings.size(); 1.652 + std::vector<std::vector<aiVertexWeight> > dstBones( numBones); 1.653 + 1.654 + // build a temporary array of pointers to the start of each vertex's weights 1.655 + typedef std::vector< std::pair<size_t, size_t> > IndexPairVector; 1.656 + std::vector<IndexPairVector::const_iterator> weightStartPerVertex; 1.657 + weightStartPerVertex.resize(pSrcController->mWeightCounts.size(),pSrcController->mWeights.end()); 1.658 + 1.659 + IndexPairVector::const_iterator pit = pSrcController->mWeights.begin(); 1.660 + for( size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a) 1.661 + { 1.662 + weightStartPerVertex[a] = pit; 1.663 + pit += pSrcController->mWeightCounts[a]; 1.664 + } 1.665 + 1.666 + // now for each vertex put the corresponding vertex weights into each bone's weight collection 1.667 + for( size_t a = pStartVertex; a < pStartVertex + numVertices; ++a) 1.668 + { 1.669 + // which position index was responsible for this vertex? that's also the index by which 1.670 + // the controller assigns the vertex weights 1.671 + size_t orgIndex = pSrcMesh->mFacePosIndices[a]; 1.672 + // find the vertex weights for this vertex 1.673 + IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex]; 1.674 + size_t pairCount = pSrcController->mWeightCounts[orgIndex]; 1.675 + 1.676 + for( size_t b = 0; b < pairCount; ++b, ++iit) 1.677 + { 1.678 + size_t jointIndex = iit->first; 1.679 + size_t vertexIndex = iit->second; 1.680 + 1.681 + float weight = ReadFloat( weightsAcc, weights, vertexIndex, 0); 1.682 + 1.683 + // one day I gonna kill that XSI Collada exporter 1.684 + if( weight > 0.0f) 1.685 + { 1.686 + aiVertexWeight w; 1.687 + w.mVertexId = a - pStartVertex; 1.688 + w.mWeight = weight; 1.689 + dstBones[jointIndex].push_back( w); 1.690 + } 1.691 + } 1.692 + } 1.693 + 1.694 + // count the number of bones which influence vertices of the current submesh 1.695 + size_t numRemainingBones = 0; 1.696 + for( std::vector<std::vector<aiVertexWeight> >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it) 1.697 + if( it->size() > 0) 1.698 + numRemainingBones++; 1.699 + 1.700 + // create bone array and copy bone weights one by one 1.701 + dstMesh->mNumBones = numRemainingBones; 1.702 + dstMesh->mBones = new aiBone*[numRemainingBones]; 1.703 + size_t boneCount = 0; 1.704 + for( size_t a = 0; a < numBones; ++a) 1.705 + { 1.706 + // omit bones without weights 1.707 + if( dstBones[a].size() == 0) 1.708 + continue; 1.709 + 1.710 + // create bone with its weights 1.711 + aiBone* bone = new aiBone; 1.712 + bone->mName = ReadString( jointNamesAcc, jointNames, a); 1.713 + bone->mOffsetMatrix.a1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 0); 1.714 + bone->mOffsetMatrix.a2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 1); 1.715 + bone->mOffsetMatrix.a3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 2); 1.716 + bone->mOffsetMatrix.a4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 3); 1.717 + bone->mOffsetMatrix.b1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 4); 1.718 + bone->mOffsetMatrix.b2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 5); 1.719 + bone->mOffsetMatrix.b3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 6); 1.720 + bone->mOffsetMatrix.b4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 7); 1.721 + bone->mOffsetMatrix.c1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 8); 1.722 + bone->mOffsetMatrix.c2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 9); 1.723 + bone->mOffsetMatrix.c3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 10); 1.724 + bone->mOffsetMatrix.c4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 11); 1.725 + bone->mNumWeights = dstBones[a].size(); 1.726 + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; 1.727 + std::copy( dstBones[a].begin(), dstBones[a].end(), bone->mWeights); 1.728 + 1.729 + // apply bind shape matrix to offset matrix 1.730 + aiMatrix4x4 bindShapeMatrix; 1.731 + bindShapeMatrix.a1 = pSrcController->mBindShapeMatrix[0]; 1.732 + bindShapeMatrix.a2 = pSrcController->mBindShapeMatrix[1]; 1.733 + bindShapeMatrix.a3 = pSrcController->mBindShapeMatrix[2]; 1.734 + bindShapeMatrix.a4 = pSrcController->mBindShapeMatrix[3]; 1.735 + bindShapeMatrix.b1 = pSrcController->mBindShapeMatrix[4]; 1.736 + bindShapeMatrix.b2 = pSrcController->mBindShapeMatrix[5]; 1.737 + bindShapeMatrix.b3 = pSrcController->mBindShapeMatrix[6]; 1.738 + bindShapeMatrix.b4 = pSrcController->mBindShapeMatrix[7]; 1.739 + bindShapeMatrix.c1 = pSrcController->mBindShapeMatrix[8]; 1.740 + bindShapeMatrix.c2 = pSrcController->mBindShapeMatrix[9]; 1.741 + bindShapeMatrix.c3 = pSrcController->mBindShapeMatrix[10]; 1.742 + bindShapeMatrix.c4 = pSrcController->mBindShapeMatrix[11]; 1.743 + bindShapeMatrix.d1 = pSrcController->mBindShapeMatrix[12]; 1.744 + bindShapeMatrix.d2 = pSrcController->mBindShapeMatrix[13]; 1.745 + bindShapeMatrix.d3 = pSrcController->mBindShapeMatrix[14]; 1.746 + bindShapeMatrix.d4 = pSrcController->mBindShapeMatrix[15]; 1.747 + bone->mOffsetMatrix *= bindShapeMatrix; 1.748 + 1.749 + // HACK: (thom) Some exporters address the bone nodes by SID, others address them by ID or even name. 1.750 + // Therefore I added a little name replacement here: I search for the bone's node by either name, ID or SID, 1.751 + // and replace the bone's name by the node's name so that the user can use the standard 1.752 + // find-by-name method to associate nodes with bones. 1.753 + const Collada::Node* bnode = FindNode( pParser.mRootNode, bone->mName.data); 1.754 + if( !bnode) 1.755 + bnode = FindNodeBySID( pParser.mRootNode, bone->mName.data); 1.756 + 1.757 + // assign the name that we would have assigned for the source node 1.758 + if( bnode) 1.759 + bone->mName.Set( FindNameForNode( bnode)); 1.760 + else 1.761 + DefaultLogger::get()->warn( boost::str( boost::format( "ColladaLoader::CreateMesh(): could not find corresponding node for joint \"%s\".") % bone->mName.data)); 1.762 + 1.763 + // and insert bone 1.764 + dstMesh->mBones[boneCount++] = bone; 1.765 + } 1.766 + } 1.767 + 1.768 + return dstMesh; 1.769 +} 1.770 + 1.771 +// ------------------------------------------------------------------------------------------------ 1.772 +// Stores all meshes in the given scene 1.773 +void ColladaLoader::StoreSceneMeshes( aiScene* pScene) 1.774 +{ 1.775 + pScene->mNumMeshes = mMeshes.size(); 1.776 + if( mMeshes.size() > 0) 1.777 + { 1.778 + pScene->mMeshes = new aiMesh*[mMeshes.size()]; 1.779 + std::copy( mMeshes.begin(), mMeshes.end(), pScene->mMeshes); 1.780 + mMeshes.clear(); 1.781 + } 1.782 +} 1.783 + 1.784 +// ------------------------------------------------------------------------------------------------ 1.785 +// Stores all cameras in the given scene 1.786 +void ColladaLoader::StoreSceneCameras( aiScene* pScene) 1.787 +{ 1.788 + pScene->mNumCameras = mCameras.size(); 1.789 + if( mCameras.size() > 0) 1.790 + { 1.791 + pScene->mCameras = new aiCamera*[mCameras.size()]; 1.792 + std::copy( mCameras.begin(), mCameras.end(), pScene->mCameras); 1.793 + mCameras.clear(); 1.794 + } 1.795 +} 1.796 + 1.797 +// ------------------------------------------------------------------------------------------------ 1.798 +// Stores all lights in the given scene 1.799 +void ColladaLoader::StoreSceneLights( aiScene* pScene) 1.800 +{ 1.801 + pScene->mNumLights = mLights.size(); 1.802 + if( mLights.size() > 0) 1.803 + { 1.804 + pScene->mLights = new aiLight*[mLights.size()]; 1.805 + std::copy( mLights.begin(), mLights.end(), pScene->mLights); 1.806 + mLights.clear(); 1.807 + } 1.808 +} 1.809 + 1.810 +// ------------------------------------------------------------------------------------------------ 1.811 +// Stores all textures in the given scene 1.812 +void ColladaLoader::StoreSceneTextures( aiScene* pScene) 1.813 +{ 1.814 + pScene->mNumTextures = mTextures.size(); 1.815 + if( mTextures.size() > 0) 1.816 + { 1.817 + pScene->mTextures = new aiTexture*[mTextures.size()]; 1.818 + std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures); 1.819 + mTextures.clear(); 1.820 + } 1.821 +} 1.822 + 1.823 +// ------------------------------------------------------------------------------------------------ 1.824 +// Stores all materials in the given scene 1.825 +void ColladaLoader::StoreSceneMaterials( aiScene* pScene) 1.826 +{ 1.827 + pScene->mNumMaterials = newMats.size(); 1.828 + 1.829 + if (newMats.size() > 0) { 1.830 + pScene->mMaterials = new aiMaterial*[newMats.size()]; 1.831 + for (unsigned int i = 0; i < newMats.size();++i) 1.832 + pScene->mMaterials[i] = newMats[i].second; 1.833 + 1.834 + newMats.clear(); 1.835 + } 1.836 +} 1.837 + 1.838 +// ------------------------------------------------------------------------------------------------ 1.839 +// Stores all animations 1.840 +void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser) 1.841 +{ 1.842 + // recursivly collect all animations from the collada scene 1.843 + StoreAnimations( pScene, pParser, &pParser.mAnims, ""); 1.844 + 1.845 + // catch special case: many animations with the same length, each affecting only a single node. 1.846 + // we need to unite all those single-node-anims to a proper combined animation 1.847 + for( size_t a = 0; a < mAnims.size(); ++a) 1.848 + { 1.849 + aiAnimation* templateAnim = mAnims[a]; 1.850 + if( templateAnim->mNumChannels == 1) 1.851 + { 1.852 + // search for other single-channel-anims with the same duration 1.853 + std::vector<size_t> collectedAnimIndices; 1.854 + for( size_t b = a+1; b < mAnims.size(); ++b) 1.855 + { 1.856 + aiAnimation* other = mAnims[b]; 1.857 + if( other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && other->mTicksPerSecond == templateAnim->mTicksPerSecond ) 1.858 + collectedAnimIndices.push_back( b); 1.859 + } 1.860 + 1.861 + // if there are other animations which fit the template anim, combine all channels into a single anim 1.862 + if( !collectedAnimIndices.empty() ) 1.863 + { 1.864 + aiAnimation* combinedAnim = new aiAnimation(); 1.865 + combinedAnim->mName = aiString( std::string( "combinedAnim_") + char( '0' + a)); 1.866 + combinedAnim->mDuration = templateAnim->mDuration; 1.867 + combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond; 1.868 + combinedAnim->mNumChannels = collectedAnimIndices.size() + 1; 1.869 + combinedAnim->mChannels = new aiNodeAnim*[combinedAnim->mNumChannels]; 1.870 + // add the template anim as first channel by moving its aiNodeAnim to the combined animation 1.871 + combinedAnim->mChannels[0] = templateAnim->mChannels[0]; 1.872 + templateAnim->mChannels[0] = NULL; 1.873 + delete templateAnim; 1.874 + // combined animation replaces template animation in the anim array 1.875 + mAnims[a] = combinedAnim; 1.876 + 1.877 + // move the memory of all other anims to the combined anim and erase them from the source anims 1.878 + for( size_t b = 0; b < collectedAnimIndices.size(); ++b) 1.879 + { 1.880 + aiAnimation* srcAnimation = mAnims[collectedAnimIndices[b]]; 1.881 + combinedAnim->mChannels[1 + b] = srcAnimation->mChannels[0]; 1.882 + srcAnimation->mChannels[0] = NULL; 1.883 + delete srcAnimation; 1.884 + } 1.885 + 1.886 + // in a second go, delete all the single-channel-anims that we've stripped from their channels 1.887 + // back to front to preserve indices - you know, removing an element from a vector moves all elements behind the removed one 1.888 + while( !collectedAnimIndices.empty() ) 1.889 + { 1.890 + mAnims.erase( mAnims.begin() + collectedAnimIndices.back()); 1.891 + collectedAnimIndices.pop_back(); 1.892 + } 1.893 + } 1.894 + } 1.895 + } 1.896 + 1.897 + // now store all anims in the scene 1.898 + if( !mAnims.empty()) 1.899 + { 1.900 + pScene->mNumAnimations = mAnims.size(); 1.901 + pScene->mAnimations = new aiAnimation*[mAnims.size()]; 1.902 + std::copy( mAnims.begin(), mAnims.end(), pScene->mAnimations); 1.903 + } 1.904 +} 1.905 + 1.906 +// ------------------------------------------------------------------------------------------------ 1.907 +// Constructs the animations for the given source anim 1.908 +void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string pPrefix) 1.909 +{ 1.910 + std::string animName = pPrefix.empty() ? pSrcAnim->mName : pPrefix + "_" + pSrcAnim->mName; 1.911 + 1.912 + // create nested animations, if given 1.913 + for( std::vector<Collada::Animation*>::const_iterator it = pSrcAnim->mSubAnims.begin(); it != pSrcAnim->mSubAnims.end(); ++it) 1.914 + StoreAnimations( pScene, pParser, *it, animName); 1.915 + 1.916 + // create animation channels, if any 1.917 + if( !pSrcAnim->mChannels.empty()) 1.918 + CreateAnimation( pScene, pParser, pSrcAnim, animName); 1.919 +} 1.920 + 1.921 +// ------------------------------------------------------------------------------------------------ 1.922 +// Constructs the animation for the given source anim 1.923 +void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName) 1.924 +{ 1.925 + // collect a list of animatable nodes 1.926 + std::vector<const aiNode*> nodes; 1.927 + CollectNodes( pScene->mRootNode, nodes); 1.928 + 1.929 + std::vector<aiNodeAnim*> anims; 1.930 + for( std::vector<const aiNode*>::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit) 1.931 + { 1.932 + // find all the collada anim channels which refer to the current node 1.933 + std::vector<Collada::ChannelEntry> entries; 1.934 + std::string nodeName = (*nit)->mName.data; 1.935 + 1.936 + // find the collada node corresponding to the aiNode 1.937 + const Collada::Node* srcNode = FindNode( pParser.mRootNode, nodeName); 1.938 +// ai_assert( srcNode != NULL); 1.939 + if( !srcNode) 1.940 + continue; 1.941 + 1.942 + // now check all channels if they affect the current node 1.943 + for( std::vector<Collada::AnimationChannel>::const_iterator cit = pSrcAnim->mChannels.begin(); 1.944 + cit != pSrcAnim->mChannels.end(); ++cit) 1.945 + { 1.946 + const Collada::AnimationChannel& srcChannel = *cit; 1.947 + Collada::ChannelEntry entry; 1.948 + 1.949 + // we expect the animation target to be of type "nodeName/transformID.subElement". Ignore all others 1.950 + // find the slash that separates the node name - there should be only one 1.951 + std::string::size_type slashPos = srcChannel.mTarget.find( '/'); 1.952 + if( slashPos == std::string::npos) 1.953 + continue; 1.954 + if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos) 1.955 + continue; 1.956 + std::string targetID = srcChannel.mTarget.substr( 0, slashPos); 1.957 + if( targetID != srcNode->mID) 1.958 + continue; 1.959 + 1.960 + // find the dot that separates the transformID - there should be only one or zero 1.961 + std::string::size_type dotPos = srcChannel.mTarget.find( '.'); 1.962 + if( dotPos != std::string::npos) 1.963 + { 1.964 + if( srcChannel.mTarget.find( '.', dotPos+1) != std::string::npos) 1.965 + continue; 1.966 + 1.967 + entry.mTransformId = srcChannel.mTarget.substr( slashPos+1, dotPos - slashPos - 1); 1.968 + 1.969 + std::string subElement = srcChannel.mTarget.substr( dotPos+1); 1.970 + if( subElement == "ANGLE") 1.971 + entry.mSubElement = 3; // last number in an Axis-Angle-Transform is the angle 1.972 + else if( subElement == "X") 1.973 + entry.mSubElement = 0; 1.974 + else if( subElement == "Y") 1.975 + entry.mSubElement = 1; 1.976 + else if( subElement == "Z") 1.977 + entry.mSubElement = 2; 1.978 + else 1.979 + DefaultLogger::get()->warn( boost::str( boost::format( "Unknown anim subelement <%s>. Ignoring") % subElement)); 1.980 + } else 1.981 + { 1.982 + // no subelement following, transformId is remaining string 1.983 + entry.mTransformId = srcChannel.mTarget.substr( slashPos+1); 1.984 + } 1.985 + 1.986 + // determine which transform step is affected by this channel 1.987 + entry.mTransformIndex = SIZE_MAX; 1.988 + for( size_t a = 0; a < srcNode->mTransforms.size(); ++a) 1.989 + if( srcNode->mTransforms[a].mID == entry.mTransformId) 1.990 + entry.mTransformIndex = a; 1.991 + 1.992 + if( entry.mTransformIndex == SIZE_MAX) { 1.993 + continue; 1.994 + } 1.995 + 1.996 + entry.mChannel = &(*cit); 1.997 + entries.push_back( entry); 1.998 + } 1.999 + 1.1000 + // if there's no channel affecting the current node, we skip it 1.1001 + if( entries.empty()) 1.1002 + continue; 1.1003 + 1.1004 + // resolve the data pointers for all anim channels. Find the minimum time while we're at it 1.1005 + float startTime = 1e20f, endTime = -1e20f; 1.1006 + for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) 1.1007 + { 1.1008 + Collada::ChannelEntry& e = *it; 1.1009 + e.mTimeAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceTimes); 1.1010 + e.mTimeData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mTimeAccessor->mSource); 1.1011 + e.mValueAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceValues); 1.1012 + e.mValueData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mValueAccessor->mSource); 1.1013 + 1.1014 + // time count and value count must match 1.1015 + if( e.mTimeAccessor->mCount != e.mValueAccessor->mCount) 1.1016 + throw DeadlyImportError( boost::str( boost::format( "Time count / value count mismatch in animation channel \"%s\".") % e.mChannel->mTarget)); 1.1017 + 1.1018 + if( e.mTimeAccessor->mCount > 0 ) 1.1019 + { 1.1020 + // find bounding times 1.1021 + startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0)); 1.1022 + endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0)); 1.1023 + } 1.1024 + } 1.1025 + 1.1026 + std::vector<aiMatrix4x4> resultTrafos; 1.1027 + if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 ) 1.1028 + { 1.1029 + // create a local transformation chain of the node's transforms 1.1030 + std::vector<Collada::Transform> transforms = srcNode->mTransforms; 1.1031 + 1.1032 + // now for every unique point in time, find or interpolate the key values for that time 1.1033 + // and apply them to the transform chain. Then the node's present transformation can be calculated. 1.1034 + float time = startTime; 1.1035 + while( 1) 1.1036 + { 1.1037 + for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) 1.1038 + { 1.1039 + Collada::ChannelEntry& e = *it; 1.1040 + 1.1041 + // find the keyframe behind the current point in time 1.1042 + size_t pos = 0; 1.1043 + float postTime = 0.f; 1.1044 + while( 1) 1.1045 + { 1.1046 + if( pos >= e.mTimeAccessor->mCount) 1.1047 + break; 1.1048 + postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); 1.1049 + if( postTime >= time) 1.1050 + break; 1.1051 + ++pos; 1.1052 + } 1.1053 + 1.1054 + pos = std::min( pos, e.mTimeAccessor->mCount-1); 1.1055 + 1.1056 + // read values from there 1.1057 + float temp[16]; 1.1058 + for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) 1.1059 + temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c); 1.1060 + 1.1061 + // if not exactly at the key time, interpolate with previous value set 1.1062 + if( postTime > time && pos > 0) 1.1063 + { 1.1064 + float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0); 1.1065 + float factor = (time - postTime) / (preTime - postTime); 1.1066 + 1.1067 + for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) 1.1068 + { 1.1069 + float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c); 1.1070 + temp[c] += (v - temp[c]) * factor; 1.1071 + } 1.1072 + } 1.1073 + 1.1074 + // Apply values to current transformation 1.1075 + std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement); 1.1076 + } 1.1077 + 1.1078 + // Calculate resulting transformation 1.1079 + aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms); 1.1080 + 1.1081 + // out of lazyness: we store the time in matrix.d4 1.1082 + mat.d4 = time; 1.1083 + resultTrafos.push_back( mat); 1.1084 + 1.1085 + // find next point in time to evaluate. That's the closest frame larger than the current in any channel 1.1086 + float nextTime = 1e20f; 1.1087 + for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) 1.1088 + { 1.1089 + Collada::ChannelEntry& e = *it; 1.1090 + 1.1091 + // find the next time value larger than the current 1.1092 + size_t pos = 0; 1.1093 + while( pos < e.mTimeAccessor->mCount) 1.1094 + { 1.1095 + float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); 1.1096 + if( t > time) 1.1097 + { 1.1098 + nextTime = std::min( nextTime, t); 1.1099 + break; 1.1100 + } 1.1101 + ++pos; 1.1102 + } 1.1103 + } 1.1104 + 1.1105 + // no more keys on any channel after the current time -> we're done 1.1106 + if( nextTime > 1e19) 1.1107 + break; 1.1108 + 1.1109 + // else construct next keyframe at this following time point 1.1110 + time = nextTime; 1.1111 + } 1.1112 + } 1.1113 + 1.1114 + // there should be some keyframes, but we aren't that fixated on valid input data 1.1115 +// ai_assert( resultTrafos.size() > 0); 1.1116 + 1.1117 + // build an animation channel for the given node out of these trafo keys 1.1118 + if( !resultTrafos.empty() ) 1.1119 + { 1.1120 + aiNodeAnim* dstAnim = new aiNodeAnim; 1.1121 + dstAnim->mNodeName = nodeName; 1.1122 + dstAnim->mNumPositionKeys = resultTrafos.size(); 1.1123 + dstAnim->mNumRotationKeys= resultTrafos.size(); 1.1124 + dstAnim->mNumScalingKeys = resultTrafos.size(); 1.1125 + dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; 1.1126 + dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; 1.1127 + dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; 1.1128 + 1.1129 + for( size_t a = 0; a < resultTrafos.size(); ++a) 1.1130 + { 1.1131 + aiMatrix4x4 mat = resultTrafos[a]; 1.1132 + double time = double( mat.d4); // remember? time is stored in mat.d4 1.1133 + mat.d4 = 1.0f; 1.1134 + 1.1135 + dstAnim->mPositionKeys[a].mTime = time; 1.1136 + dstAnim->mRotationKeys[a].mTime = time; 1.1137 + dstAnim->mScalingKeys[a].mTime = time; 1.1138 + mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue); 1.1139 + } 1.1140 + 1.1141 + anims.push_back( dstAnim); 1.1142 + } else 1.1143 + { 1.1144 + DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter."); 1.1145 + } 1.1146 + } 1.1147 + 1.1148 + if( !anims.empty()) 1.1149 + { 1.1150 + aiAnimation* anim = new aiAnimation; 1.1151 + anim->mName.Set( pName); 1.1152 + anim->mNumChannels = anims.size(); 1.1153 + anim->mChannels = new aiNodeAnim*[anims.size()]; 1.1154 + std::copy( anims.begin(), anims.end(), anim->mChannels); 1.1155 + anim->mDuration = 0.0f; 1.1156 + for( size_t a = 0; a < anims.size(); ++a) 1.1157 + { 1.1158 + anim->mDuration = std::max( anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys-1].mTime); 1.1159 + anim->mDuration = std::max( anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys-1].mTime); 1.1160 + anim->mDuration = std::max( anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys-1].mTime); 1.1161 + } 1.1162 + anim->mTicksPerSecond = 1; 1.1163 + mAnims.push_back( anim); 1.1164 + } 1.1165 +} 1.1166 + 1.1167 +// ------------------------------------------------------------------------------------------------ 1.1168 +// Add a texture to a material structure 1.1169 +void ColladaLoader::AddTexture ( aiMaterial& mat, const ColladaParser& pParser, 1.1170 + const Collada::Effect& effect, 1.1171 + const Collada::Sampler& sampler, 1.1172 + aiTextureType type, unsigned int idx) 1.1173 +{ 1.1174 + // first of all, basic file name 1.1175 + const aiString name = FindFilenameForEffectTexture( pParser, effect, sampler.mName ); 1.1176 + mat.AddProperty( &name, _AI_MATKEY_TEXTURE_BASE, type, idx ); 1.1177 + 1.1178 + // mapping mode 1.1179 + int map = aiTextureMapMode_Clamp; 1.1180 + if (sampler.mWrapU) 1.1181 + map = aiTextureMapMode_Wrap; 1.1182 + if (sampler.mWrapU && sampler.mMirrorU) 1.1183 + map = aiTextureMapMode_Mirror; 1.1184 + 1.1185 + mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_U_BASE, type, idx); 1.1186 + 1.1187 + map = aiTextureMapMode_Clamp; 1.1188 + if (sampler.mWrapV) 1.1189 + map = aiTextureMapMode_Wrap; 1.1190 + if (sampler.mWrapV && sampler.mMirrorV) 1.1191 + map = aiTextureMapMode_Mirror; 1.1192 + 1.1193 + mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_V_BASE, type, idx); 1.1194 + 1.1195 + // UV transformation 1.1196 + mat.AddProperty(&sampler.mTransform, 1, 1.1197 + _AI_MATKEY_UVTRANSFORM_BASE, type, idx); 1.1198 + 1.1199 + // Blend mode 1.1200 + mat.AddProperty((int*)&sampler.mOp , 1, 1.1201 + _AI_MATKEY_TEXBLEND_BASE, type, idx); 1.1202 + 1.1203 + // Blend factor 1.1204 + mat.AddProperty((float*)&sampler.mWeighting , 1, 1.1205 + _AI_MATKEY_TEXBLEND_BASE, type, idx); 1.1206 + 1.1207 + // UV source index ... if we didn't resolve the mapping, it is actually just 1.1208 + // a guess but it works in most cases. We search for the frst occurence of a 1.1209 + // number in the channel name. We assume it is the zero-based index into the 1.1210 + // UV channel array of all corresponding meshes. It could also be one-based 1.1211 + // for some exporters, but we won't care of it unless someone complains about. 1.1212 + if (sampler.mUVId != UINT_MAX) 1.1213 + map = sampler.mUVId; 1.1214 + else { 1.1215 + map = -1; 1.1216 + for (std::string::const_iterator it = sampler.mUVChannel.begin();it != sampler.mUVChannel.end(); ++it){ 1.1217 + if (IsNumeric(*it)) { 1.1218 + map = strtoul10(&(*it)); 1.1219 + break; 1.1220 + } 1.1221 + } 1.1222 + if (-1 == map) { 1.1223 + DefaultLogger::get()->warn("Collada: unable to determine UV channel for texture"); 1.1224 + map = 0; 1.1225 + } 1.1226 + } 1.1227 + mat.AddProperty(&map,1,_AI_MATKEY_UVWSRC_BASE,type,idx); 1.1228 +} 1.1229 + 1.1230 +// ------------------------------------------------------------------------------------------------ 1.1231 +// Fills materials from the collada material definitions 1.1232 +void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pScene*/) 1.1233 +{ 1.1234 + for (std::vector<std::pair<Collada::Effect*, aiMaterial*> >::iterator it = newMats.begin(), 1.1235 + end = newMats.end(); it != end; ++it) 1.1236 + { 1.1237 + aiMaterial& mat = (aiMaterial&)*it->second; 1.1238 + Collada::Effect& effect = *it->first; 1.1239 + 1.1240 + // resolve shading mode 1.1241 + int shadeMode; 1.1242 + if (effect.mFaceted) /* fixme */ 1.1243 + shadeMode = aiShadingMode_Flat; 1.1244 + else { 1.1245 + switch( effect.mShadeType) 1.1246 + { 1.1247 + case Collada::Shade_Constant: 1.1248 + shadeMode = aiShadingMode_NoShading; 1.1249 + break; 1.1250 + case Collada::Shade_Lambert: 1.1251 + shadeMode = aiShadingMode_Gouraud; 1.1252 + break; 1.1253 + case Collada::Shade_Blinn: 1.1254 + shadeMode = aiShadingMode_Blinn; 1.1255 + break; 1.1256 + case Collada::Shade_Phong: 1.1257 + shadeMode = aiShadingMode_Phong; 1.1258 + break; 1.1259 + 1.1260 + default: 1.1261 + DefaultLogger::get()->warn("Collada: Unrecognized shading mode, using gouraud shading"); 1.1262 + shadeMode = aiShadingMode_Gouraud; 1.1263 + break; 1.1264 + } 1.1265 + } 1.1266 + mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); 1.1267 + 1.1268 + // double-sided? 1.1269 + shadeMode = effect.mDoubleSided; 1.1270 + mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_TWOSIDED); 1.1271 + 1.1272 + // wireframe? 1.1273 + shadeMode = effect.mWireframe; 1.1274 + mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME); 1.1275 + 1.1276 + // add material colors 1.1277 + mat.AddProperty( &effect.mAmbient, 1,AI_MATKEY_COLOR_AMBIENT); 1.1278 + mat.AddProperty( &effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); 1.1279 + mat.AddProperty( &effect.mSpecular, 1,AI_MATKEY_COLOR_SPECULAR); 1.1280 + mat.AddProperty( &effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); 1.1281 + mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT); 1.1282 + mat.AddProperty( &effect.mReflective, 1, AI_MATKEY_COLOR_REFLECTIVE); 1.1283 + 1.1284 + // scalar properties 1.1285 + mat.AddProperty( &effect.mShininess, 1, AI_MATKEY_SHININESS); 1.1286 + mat.AddProperty( &effect.mReflectivity, 1, AI_MATKEY_REFLECTIVITY); 1.1287 + mat.AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI); 1.1288 + 1.1289 + // transparency, a very hard one. seemingly not all files are following the 1.1290 + // specification here .. but we can trick. 1.1291 + if (effect.mTransparency >= 0.f && effect.mTransparency < 1.f) { 1.1292 + effect.mTransparency = 1.f- effect.mTransparency; 1.1293 + mat.AddProperty( &effect.mTransparency, 1, AI_MATKEY_OPACITY ); 1.1294 + mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT ); 1.1295 + } 1.1296 + 1.1297 + // add textures, if given 1.1298 + if( !effect.mTexAmbient.mName.empty()) 1.1299 + /* It is merely a lightmap */ 1.1300 + AddTexture( mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP); 1.1301 + 1.1302 + if( !effect.mTexEmissive.mName.empty()) 1.1303 + AddTexture( mat, pParser, effect, effect.mTexEmissive, aiTextureType_EMISSIVE); 1.1304 + 1.1305 + if( !effect.mTexSpecular.mName.empty()) 1.1306 + AddTexture( mat, pParser, effect, effect.mTexSpecular, aiTextureType_SPECULAR); 1.1307 + 1.1308 + if( !effect.mTexDiffuse.mName.empty()) 1.1309 + AddTexture( mat, pParser, effect, effect.mTexDiffuse, aiTextureType_DIFFUSE); 1.1310 + 1.1311 + if( !effect.mTexBump.mName.empty()) 1.1312 + AddTexture( mat, pParser, effect, effect.mTexBump, aiTextureType_NORMALS); 1.1313 + 1.1314 + if( !effect.mTexTransparent.mName.empty()) 1.1315 + AddTexture( mat, pParser, effect, effect.mTexTransparent, aiTextureType_OPACITY); 1.1316 + 1.1317 + if( !effect.mTexReflective.mName.empty()) 1.1318 + AddTexture( mat, pParser, effect, effect.mTexReflective, aiTextureType_REFLECTION); 1.1319 + } 1.1320 +} 1.1321 + 1.1322 +// ------------------------------------------------------------------------------------------------ 1.1323 +// Constructs materials from the collada material definitions 1.1324 +void ColladaLoader::BuildMaterials( ColladaParser& pParser, aiScene* /*pScene*/) 1.1325 +{ 1.1326 + newMats.reserve(pParser.mMaterialLibrary.size()); 1.1327 + 1.1328 + for( ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); matIt != pParser.mMaterialLibrary.end(); ++matIt) 1.1329 + { 1.1330 + const Collada::Material& material = matIt->second; 1.1331 + // a material is only a reference to an effect 1.1332 + ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find( material.mEffect); 1.1333 + if( effIt == pParser.mEffectLibrary.end()) 1.1334 + continue; 1.1335 + Collada::Effect& effect = effIt->second; 1.1336 + 1.1337 + // create material 1.1338 + aiMaterial* mat = new aiMaterial; 1.1339 + aiString name( matIt->first); 1.1340 + mat->AddProperty(&name,AI_MATKEY_NAME); 1.1341 + 1.1342 + // store the material 1.1343 + mMaterialIndexByName[matIt->first] = newMats.size(); 1.1344 + newMats.push_back( std::pair<Collada::Effect*, aiMaterial*>( &effect,mat) ); 1.1345 + } 1.1346 + // ScenePreprocessor generates a default material automatically if none is there. 1.1347 + // All further code here in this loader works well without a valid material so 1.1348 + // we can safely let it to ScenePreprocessor. 1.1349 +#if 0 1.1350 + if( newMats.size() == 0) 1.1351 + { 1.1352 + aiMaterial* mat = new aiMaterial; 1.1353 + aiString name( AI_DEFAULT_MATERIAL_NAME ); 1.1354 + mat->AddProperty( &name, AI_MATKEY_NAME); 1.1355 + 1.1356 + const int shadeMode = aiShadingMode_Phong; 1.1357 + mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); 1.1358 + 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); 1.1359 + mat->AddProperty( &colAmbient, 1, AI_MATKEY_COLOR_AMBIENT); 1.1360 + mat->AddProperty( &colDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); 1.1361 + mat->AddProperty( &colSpecular, 1, AI_MATKEY_COLOR_SPECULAR); 1.1362 + const float specExp = 5.0f; 1.1363 + mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS); 1.1364 + } 1.1365 +#endif 1.1366 +} 1.1367 + 1.1368 +// ------------------------------------------------------------------------------------------------ 1.1369 +// Resolves the texture name for the given effect texture entry 1.1370 +aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pParser, 1.1371 + const Collada::Effect& pEffect, const std::string& pName) 1.1372 +{ 1.1373 + // recurse through the param references until we end up at an image 1.1374 + std::string name = pName; 1.1375 + while( 1) 1.1376 + { 1.1377 + // the given string is a param entry. Find it 1.1378 + Collada::Effect::ParamLibrary::const_iterator it = pEffect.mParams.find( name); 1.1379 + // if not found, we're at the end of the recursion. The resulting string should be the image ID 1.1380 + if( it == pEffect.mParams.end()) 1.1381 + break; 1.1382 + 1.1383 + // else recurse on 1.1384 + name = it->second.mReference; 1.1385 + } 1.1386 + 1.1387 + // find the image referred by this name in the image library of the scene 1.1388 + ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find( name); 1.1389 + if( imIt == pParser.mImageLibrary.end()) 1.1390 + { 1.1391 + throw DeadlyImportError( boost::str( boost::format( 1.1392 + "Collada: Unable to resolve effect texture entry \"%s\", ended up at ID \"%s\".") % pName % name)); 1.1393 + } 1.1394 + 1.1395 + aiString result; 1.1396 + 1.1397 + // if this is an embedded texture image setup an aiTexture for it 1.1398 + if (imIt->second.mFileName.empty()) 1.1399 + { 1.1400 + if (imIt->second.mImageData.empty()) { 1.1401 + throw DeadlyImportError("Collada: Invalid texture, no data or file reference given"); 1.1402 + } 1.1403 + 1.1404 + aiTexture* tex = new aiTexture(); 1.1405 + 1.1406 + // setup format hint 1.1407 + if (imIt->second.mEmbeddedFormat.length() > 3) { 1.1408 + DefaultLogger::get()->warn("Collada: texture format hint is too long, truncating to 3 characters"); 1.1409 + } 1.1410 + strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3); 1.1411 + 1.1412 + // and copy texture data 1.1413 + tex->mHeight = 0; 1.1414 + tex->mWidth = imIt->second.mImageData.size(); 1.1415 + tex->pcData = (aiTexel*)new char[tex->mWidth]; 1.1416 + memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth); 1.1417 + 1.1418 + // setup texture reference string 1.1419 + result.data[0] = '*'; 1.1420 + result.length = 1 + ASSIMP_itoa10(result.data+1,MAXLEN-1,mTextures.size()); 1.1421 + 1.1422 + // and add this texture to the list 1.1423 + mTextures.push_back(tex); 1.1424 + } 1.1425 + else 1.1426 + { 1.1427 + result.Set( imIt->second.mFileName ); 1.1428 + ConvertPath(result); 1.1429 + } 1.1430 + return result; 1.1431 +} 1.1432 + 1.1433 +// ------------------------------------------------------------------------------------------------ 1.1434 +// Convert a path read from a collada file to the usual representation 1.1435 +void ColladaLoader::ConvertPath (aiString& ss) 1.1436 +{ 1.1437 + // TODO: collada spec, p 22. Handle URI correctly. 1.1438 + // For the moment we're just stripping the file:// away to make it work. 1.1439 + // Windoes doesn't seem to be able to find stuff like 1.1440 + // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg' 1.1441 + if (0 == strncmp(ss.data,"file://",7)) 1.1442 + { 1.1443 + ss.length -= 7; 1.1444 + memmove(ss.data,ss.data+7,ss.length); 1.1445 + ss.data[ss.length] = '\0'; 1.1446 + } 1.1447 + 1.1448 + // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... 1.1449 + // I need to filter it without destroying linux paths starting with "/somewhere" 1.1450 + if( ss.data[0] == '/' && isalpha( ss.data[1]) && ss.data[2] == ':' ) 1.1451 + { 1.1452 + ss.length--; 1.1453 + memmove( ss.data, ss.data+1, ss.length); 1.1454 + ss.data[ss.length] = 0; 1.1455 + } 1.1456 + 1.1457 + // find and convert all %xy special chars 1.1458 + char* out = ss.data; 1.1459 + for( const char* it = ss.data; it != ss.data + ss.length; /**/ ) 1.1460 + { 1.1461 + if( *it == '%' && (it + 3) < ss.data + ss.length ) 1.1462 + { 1.1463 + // separate the number to avoid dragging in chars from behind into the parsing 1.1464 + char mychar[3] = { it[1], it[2], 0 }; 1.1465 + size_t nbr = strtoul16( mychar); 1.1466 + it += 3; 1.1467 + *out++ = (char)(nbr & 0xFF); 1.1468 + } else 1.1469 + { 1.1470 + *out++ = *it++; 1.1471 + } 1.1472 + } 1.1473 + 1.1474 + // adjust length and terminator of the shortened string 1.1475 + *out = 0; 1.1476 + ss.length = (ptrdiff_t) (out - ss.data); 1.1477 +} 1.1478 + 1.1479 +// ------------------------------------------------------------------------------------------------ 1.1480 +// Reads a float value from an accessor and its data array. 1.1481 +float ColladaLoader::ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const 1.1482 +{ 1.1483 + // FIXME: (thom) Test for data type here in every access? For the moment, I leave this to the caller 1.1484 + size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset; 1.1485 + ai_assert( pos < pData.mValues.size()); 1.1486 + return pData.mValues[pos]; 1.1487 +} 1.1488 + 1.1489 +// ------------------------------------------------------------------------------------------------ 1.1490 +// Reads a string value from an accessor and its data array. 1.1491 +const std::string& ColladaLoader::ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const 1.1492 +{ 1.1493 + size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset; 1.1494 + ai_assert( pos < pData.mStrings.size()); 1.1495 + return pData.mStrings[pos]; 1.1496 +} 1.1497 + 1.1498 +// ------------------------------------------------------------------------------------------------ 1.1499 +// Collects all nodes into the given array 1.1500 +void ColladaLoader::CollectNodes( const aiNode* pNode, std::vector<const aiNode*>& poNodes) const 1.1501 +{ 1.1502 + poNodes.push_back( pNode); 1.1503 + 1.1504 + for( size_t a = 0; a < pNode->mNumChildren; ++a) 1.1505 + CollectNodes( pNode->mChildren[a], poNodes); 1.1506 +} 1.1507 + 1.1508 +// ------------------------------------------------------------------------------------------------ 1.1509 +// Finds a node in the collada scene by the given name 1.1510 +const Collada::Node* ColladaLoader::FindNode( const Collada::Node* pNode, const std::string& pName) const 1.1511 +{ 1.1512 + if( pNode->mName == pName || pNode->mID == pName) 1.1513 + return pNode; 1.1514 + 1.1515 + for( size_t a = 0; a < pNode->mChildren.size(); ++a) 1.1516 + { 1.1517 + const Collada::Node* node = FindNode( pNode->mChildren[a], pName); 1.1518 + if( node) 1.1519 + return node; 1.1520 + } 1.1521 + 1.1522 + return NULL; 1.1523 +} 1.1524 + 1.1525 +// ------------------------------------------------------------------------------------------------ 1.1526 +// Finds a node in the collada scene by the given SID 1.1527 +const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const 1.1528 +{ 1.1529 + if( pNode->mSID == pSID) 1.1530 + return pNode; 1.1531 + 1.1532 + for( size_t a = 0; a < pNode->mChildren.size(); ++a) 1.1533 + { 1.1534 + const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID); 1.1535 + if( node) 1.1536 + return node; 1.1537 + } 1.1538 + 1.1539 + return NULL; 1.1540 +} 1.1541 + 1.1542 +// ------------------------------------------------------------------------------------------------ 1.1543 +// Finds a proper name for a node derived from the collada-node's properties 1.1544 +std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode) const 1.1545 +{ 1.1546 + // now setup the name of the node. We take the name if not empty, otherwise the collada ID 1.1547 + // FIX: Workaround for XSI calling the instanced visual scene 'untitled' by default. 1.1548 + if (!pNode->mName.empty() && pNode->mName != "untitled") 1.1549 + return pNode->mName; 1.1550 + else if (!pNode->mID.empty()) 1.1551 + return pNode->mID; 1.1552 + else if (!pNode->mSID.empty()) 1.1553 + return pNode->mSID; 1.1554 + else 1.1555 + { 1.1556 + // No need to worry. Unnamed nodes are no problem at all, except 1.1557 + // if cameras or lights need to be assigned to them. 1.1558 + return boost::str( boost::format( "$ColladaAutoName$_%d") % clock()); 1.1559 + } 1.1560 +} 1.1561 + 1.1562 +#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER