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