vrshoot

annotate 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
rev   line source
nuclear@0 1 /*
nuclear@0 2 ---------------------------------------------------------------------------
nuclear@0 3 Open Asset Import Library (assimp)
nuclear@0 4 ---------------------------------------------------------------------------
nuclear@0 5
nuclear@0 6 Copyright (c) 2006-2012, assimp team
nuclear@0 7
nuclear@0 8 All rights reserved.
nuclear@0 9
nuclear@0 10 Redistribution and use of this software in source and binary forms,
nuclear@0 11 with or without modification, are permitted provided that the following
nuclear@0 12 conditions are met:
nuclear@0 13
nuclear@0 14 * Redistributions of source code must retain the above
nuclear@0 15 copyright notice, this list of conditions and the
nuclear@0 16 following disclaimer.
nuclear@0 17
nuclear@0 18 * Redistributions in binary form must reproduce the above
nuclear@0 19 copyright notice, this list of conditions and the
nuclear@0 20 following disclaimer in the documentation and/or other
nuclear@0 21 materials provided with the distribution.
nuclear@0 22
nuclear@0 23 * Neither the name of the assimp team, nor the names of its
nuclear@0 24 contributors may be used to endorse or promote products
nuclear@0 25 derived from this software without specific prior
nuclear@0 26 written permission of the assimp team.
nuclear@0 27
nuclear@0 28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
nuclear@0 29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
nuclear@0 30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
nuclear@0 31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
nuclear@0 32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
nuclear@0 33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
nuclear@0 34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
nuclear@0 35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
nuclear@0 36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
nuclear@0 37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
nuclear@0 38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nuclear@0 39 ---------------------------------------------------------------------------
nuclear@0 40 */
nuclear@0 41
nuclear@0 42 /** @file Implementation of the Collada loader */
nuclear@0 43
nuclear@0 44 #include "AssimpPCH.h"
nuclear@0 45 #ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
nuclear@0 46
nuclear@0 47 #include "assimp/anim.h"
nuclear@0 48 #include "ColladaLoader.h"
nuclear@0 49 #include "ColladaParser.h"
nuclear@0 50
nuclear@0 51 #include "fast_atof.h"
nuclear@0 52 #include "ParsingUtils.h"
nuclear@0 53 #include "SkeletonMeshBuilder.h"
nuclear@0 54
nuclear@0 55 #include "time.h"
nuclear@0 56
nuclear@0 57 using namespace Assimp;
nuclear@0 58
nuclear@0 59 static const aiImporterDesc desc = {
nuclear@0 60 "Collada Importer",
nuclear@0 61 "",
nuclear@0 62 "",
nuclear@0 63 "http://collada.org",
nuclear@0 64 aiImporterFlags_SupportTextFlavour,
nuclear@0 65 1,
nuclear@0 66 3,
nuclear@0 67 1,
nuclear@0 68 5,
nuclear@0 69 "dae"
nuclear@0 70 };
nuclear@0 71
nuclear@0 72
nuclear@0 73 // ------------------------------------------------------------------------------------------------
nuclear@0 74 // Constructor to be privately used by Importer
nuclear@0 75 ColladaLoader::ColladaLoader()
nuclear@0 76 : noSkeletonMesh()
nuclear@0 77 {}
nuclear@0 78
nuclear@0 79 // ------------------------------------------------------------------------------------------------
nuclear@0 80 // Destructor, private as well
nuclear@0 81 ColladaLoader::~ColladaLoader()
nuclear@0 82 {}
nuclear@0 83
nuclear@0 84 // ------------------------------------------------------------------------------------------------
nuclear@0 85 // Returns whether the class can handle the format of the given file.
nuclear@0 86 bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
nuclear@0 87 {
nuclear@0 88 // check file extension
nuclear@0 89 std::string extension = GetExtension(pFile);
nuclear@0 90
nuclear@0 91 if( extension == "dae")
nuclear@0 92 return true;
nuclear@0 93
nuclear@0 94 // XML - too generic, we need to open the file and search for typical keywords
nuclear@0 95 if( extension == "xml" || !extension.length() || checkSig) {
nuclear@0 96 /* If CanRead() is called in order to check whether we
nuclear@0 97 * support a specific file extension in general pIOHandler
nuclear@0 98 * might be NULL and it's our duty to return true here.
nuclear@0 99 */
nuclear@0 100 if (!pIOHandler)return true;
nuclear@0 101 const char* tokens[] = {"collada"};
nuclear@0 102 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
nuclear@0 103 }
nuclear@0 104 return false;
nuclear@0 105 }
nuclear@0 106
nuclear@0 107 // ------------------------------------------------------------------------------------------------
nuclear@0 108 void ColladaLoader::SetupProperties(const Importer* pImp)
nuclear@0 109 {
nuclear@0 110 noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
nuclear@0 111 }
nuclear@0 112
nuclear@0 113
nuclear@0 114 // ------------------------------------------------------------------------------------------------
nuclear@0 115 // Get file extension list
nuclear@0 116 const aiImporterDesc* ColladaLoader::GetInfo () const
nuclear@0 117 {
nuclear@0 118 return &desc;
nuclear@0 119 }
nuclear@0 120
nuclear@0 121 // ------------------------------------------------------------------------------------------------
nuclear@0 122 // Imports the given file into the given scene structure.
nuclear@0 123 void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
nuclear@0 124 {
nuclear@0 125 mFileName = pFile;
nuclear@0 126
nuclear@0 127 // clean all member arrays - just for safety, it should work even if we did not
nuclear@0 128 mMeshIndexByID.clear();
nuclear@0 129 mMaterialIndexByName.clear();
nuclear@0 130 mMeshes.clear();
nuclear@0 131 newMats.clear();
nuclear@0 132 mLights.clear();
nuclear@0 133 mCameras.clear();
nuclear@0 134 mTextures.clear();
nuclear@0 135
nuclear@0 136 // parse the input file
nuclear@0 137 ColladaParser parser( pIOHandler, pFile);
nuclear@0 138
nuclear@0 139 if( !parser.mRootNode)
nuclear@0 140 throw DeadlyImportError( "Collada: File came out empty. Something is wrong here.");
nuclear@0 141
nuclear@0 142 // reserve some storage to avoid unnecessary reallocs
nuclear@0 143 newMats.reserve(parser.mMaterialLibrary.size()*2);
nuclear@0 144 mMeshes.reserve(parser.mMeshLibrary.size()*2);
nuclear@0 145
nuclear@0 146 mCameras.reserve(parser.mCameraLibrary.size());
nuclear@0 147 mLights.reserve(parser.mLightLibrary.size());
nuclear@0 148
nuclear@0 149 // create the materials first, for the meshes to find
nuclear@0 150 BuildMaterials( parser, pScene);
nuclear@0 151
nuclear@0 152 // build the node hierarchy from it
nuclear@0 153 pScene->mRootNode = BuildHierarchy( parser, parser.mRootNode);
nuclear@0 154
nuclear@0 155 // ... then fill the materials with the now adjusted settings
nuclear@0 156 FillMaterials(parser, pScene);
nuclear@0 157
nuclear@0 158 // Apply unitsize scale calculation
nuclear@0 159 pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0,
nuclear@0 160 0, parser.mUnitSize, 0, 0,
nuclear@0 161 0, 0, parser.mUnitSize, 0,
nuclear@0 162 0, 0, 0, 1);
nuclear@0 163
nuclear@0 164 // Convert to Y_UP, if different orientation
nuclear@0 165 if( parser.mUpDirection == ColladaParser::UP_X)
nuclear@0 166 pScene->mRootNode->mTransformation *= aiMatrix4x4(
nuclear@0 167 0, -1, 0, 0,
nuclear@0 168 1, 0, 0, 0,
nuclear@0 169 0, 0, 1, 0,
nuclear@0 170 0, 0, 0, 1);
nuclear@0 171 else if( parser.mUpDirection == ColladaParser::UP_Z)
nuclear@0 172 pScene->mRootNode->mTransformation *= aiMatrix4x4(
nuclear@0 173 1, 0, 0, 0,
nuclear@0 174 0, 0, 1, 0,
nuclear@0 175 0, -1, 0, 0,
nuclear@0 176 0, 0, 0, 1);
nuclear@0 177
nuclear@0 178 // store all meshes
nuclear@0 179 StoreSceneMeshes( pScene);
nuclear@0 180
nuclear@0 181 // store all materials
nuclear@0 182 StoreSceneMaterials( pScene);
nuclear@0 183
nuclear@0 184 // store all lights
nuclear@0 185 StoreSceneLights( pScene);
nuclear@0 186
nuclear@0 187 // store all cameras
nuclear@0 188 StoreSceneCameras( pScene);
nuclear@0 189
nuclear@0 190 // store all animations
nuclear@0 191 StoreAnimations( pScene, parser);
nuclear@0 192
nuclear@0 193
nuclear@0 194 // If no meshes have been loaded, it's probably just an animated skeleton.
nuclear@0 195 if (!pScene->mNumMeshes) {
nuclear@0 196
nuclear@0 197 if (!noSkeletonMesh) {
nuclear@0 198 SkeletonMeshBuilder hero(pScene);
nuclear@0 199 }
nuclear@0 200 pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
nuclear@0 201 }
nuclear@0 202 }
nuclear@0 203
nuclear@0 204 // ------------------------------------------------------------------------------------------------
nuclear@0 205 // Recursively constructs a scene node for the given parser node and returns it.
nuclear@0 206 aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode)
nuclear@0 207 {
nuclear@0 208 // create a node for it
nuclear@0 209 aiNode* node = new aiNode();
nuclear@0 210
nuclear@0 211 // find a name for the new node. It's more complicated than you might think
nuclear@0 212 node->mName.Set( FindNameForNode( pNode));
nuclear@0 213
nuclear@0 214 // calculate the transformation matrix for it
nuclear@0 215 node->mTransformation = pParser.CalculateResultTransform( pNode->mTransforms);
nuclear@0 216
nuclear@0 217 // now resolve node instances
nuclear@0 218 std::vector<const Collada::Node*> instances;
nuclear@0 219 ResolveNodeInstances(pParser,pNode,instances);
nuclear@0 220
nuclear@0 221 // add children. first the *real* ones
nuclear@0 222 node->mNumChildren = pNode->mChildren.size()+instances.size();
nuclear@0 223 node->mChildren = new aiNode*[node->mNumChildren];
nuclear@0 224
nuclear@0 225 for( size_t a = 0; a < pNode->mChildren.size(); a++)
nuclear@0 226 {
nuclear@0 227 node->mChildren[a] = BuildHierarchy( pParser, pNode->mChildren[a]);
nuclear@0 228 node->mChildren[a]->mParent = node;
nuclear@0 229 }
nuclear@0 230
nuclear@0 231 // ... and finally the resolved node instances
nuclear@0 232 for( size_t a = 0; a < instances.size(); a++)
nuclear@0 233 {
nuclear@0 234 node->mChildren[pNode->mChildren.size() + a] = BuildHierarchy( pParser, instances[a]);
nuclear@0 235 node->mChildren[pNode->mChildren.size() + a]->mParent = node;
nuclear@0 236 }
nuclear@0 237
nuclear@0 238 // construct meshes
nuclear@0 239 BuildMeshesForNode( pParser, pNode, node);
nuclear@0 240
nuclear@0 241 // construct cameras
nuclear@0 242 BuildCamerasForNode(pParser, pNode, node);
nuclear@0 243
nuclear@0 244 // construct lights
nuclear@0 245 BuildLightsForNode(pParser, pNode, node);
nuclear@0 246 return node;
nuclear@0 247 }
nuclear@0 248
nuclear@0 249 // ------------------------------------------------------------------------------------------------
nuclear@0 250 // Resolve node instances
nuclear@0 251 void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode,
nuclear@0 252 std::vector<const Collada::Node*>& resolved)
nuclear@0 253 {
nuclear@0 254 // reserve enough storage
nuclear@0 255 resolved.reserve(pNode->mNodeInstances.size());
nuclear@0 256
nuclear@0 257 // ... and iterate through all nodes to be instanced as children of pNode
nuclear@0 258 for (std::vector<Collada::NodeInstance>::const_iterator it = pNode->mNodeInstances.begin(),
nuclear@0 259 end = pNode->mNodeInstances.end(); it != end; ++it)
nuclear@0 260 {
nuclear@0 261 // find the corresponding node in the library
nuclear@0 262 const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find((*it).mNode);
nuclear@0 263 const Collada::Node* nd = itt == pParser.mNodeLibrary.end() ? NULL : (*itt).second;
nuclear@0 264
nuclear@0 265 // FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632
nuclear@0 266 // need to check for both name and ID to catch all. To avoid breaking valid files,
nuclear@0 267 // the workaround is only enabled when the first attempt to resolve the node has failed.
nuclear@0 268 if (!nd) {
nuclear@0 269 nd = FindNode(pParser.mRootNode,(*it).mNode);
nuclear@0 270 }
nuclear@0 271 if (!nd)
nuclear@0 272 DefaultLogger::get()->error("Collada: Unable to resolve reference to instanced node " + (*it).mNode);
nuclear@0 273
nuclear@0 274 else {
nuclear@0 275 // attach this node to the list of children
nuclear@0 276 resolved.push_back(nd);
nuclear@0 277 }
nuclear@0 278 }
nuclear@0 279 }
nuclear@0 280
nuclear@0 281 // ------------------------------------------------------------------------------------------------
nuclear@0 282 // Resolve UV channels
nuclear@0 283 void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler,
nuclear@0 284 const Collada::SemanticMappingTable& table)
nuclear@0 285 {
nuclear@0 286 std::map<std::string, Collada::InputSemanticMapEntry>::const_iterator it = table.mMap.find(sampler.mUVChannel);
nuclear@0 287 if (it != table.mMap.end()) {
nuclear@0 288 if (it->second.mType != Collada::IT_Texcoord)
nuclear@0 289 DefaultLogger::get()->error("Collada: Unexpected effect input mapping");
nuclear@0 290
nuclear@0 291 sampler.mUVId = it->second.mSet;
nuclear@0 292 }
nuclear@0 293 }
nuclear@0 294
nuclear@0 295 // ------------------------------------------------------------------------------------------------
nuclear@0 296 // Builds lights for the given node and references them
nuclear@0 297 void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
nuclear@0 298 {
nuclear@0 299 BOOST_FOREACH( const Collada::LightInstance& lid, pNode->mLights)
nuclear@0 300 {
nuclear@0 301 // find the referred light
nuclear@0 302 ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find( lid.mLight);
nuclear@0 303 if( srcLightIt == pParser.mLightLibrary.end())
nuclear@0 304 {
nuclear@0 305 DefaultLogger::get()->warn("Collada: Unable to find light for ID \"" + lid.mLight + "\". Skipping.");
nuclear@0 306 continue;
nuclear@0 307 }
nuclear@0 308 const Collada::Light* srcLight = &srcLightIt->second;
nuclear@0 309 if (srcLight->mType == aiLightSource_AMBIENT) {
nuclear@0 310 DefaultLogger::get()->error("Collada: Skipping ambient light for the moment");
nuclear@0 311 continue;
nuclear@0 312 }
nuclear@0 313
nuclear@0 314 // now fill our ai data structure
nuclear@0 315 aiLight* out = new aiLight();
nuclear@0 316 out->mName = pTarget->mName;
nuclear@0 317 out->mType = (aiLightSourceType)srcLight->mType;
nuclear@0 318
nuclear@0 319 // collada lights point in -Z by default, rest is specified in node transform
nuclear@0 320 out->mDirection = aiVector3D(0.f,0.f,-1.f);
nuclear@0 321
nuclear@0 322 out->mAttenuationConstant = srcLight->mAttConstant;
nuclear@0 323 out->mAttenuationLinear = srcLight->mAttLinear;
nuclear@0 324 out->mAttenuationQuadratic = srcLight->mAttQuadratic;
nuclear@0 325
nuclear@0 326 // collada doesn't differenciate between these color types
nuclear@0 327 out->mColorDiffuse = out->mColorSpecular = out->mColorAmbient = srcLight->mColor*srcLight->mIntensity;
nuclear@0 328
nuclear@0 329 // convert falloff angle and falloff exponent in our representation, if given
nuclear@0 330 if (out->mType == aiLightSource_SPOT) {
nuclear@0 331
nuclear@0 332 out->mAngleInnerCone = AI_DEG_TO_RAD( srcLight->mFalloffAngle );
nuclear@0 333
nuclear@0 334 // ... some extension magic.
nuclear@0 335 if (srcLight->mOuterAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f))
nuclear@0 336 {
nuclear@0 337 // ... some deprecation magic.
nuclear@0 338 if (srcLight->mPenumbraAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f))
nuclear@0 339 {
nuclear@0 340 // Need to rely on falloff_exponent. I don't know how to interpret it, so I need to guess ....
nuclear@0 341 // epsilon chosen to be 0.1
nuclear@0 342 out->mAngleOuterCone = AI_DEG_TO_RAD (acos(pow(0.1f,1.f/srcLight->mFalloffExponent))+
nuclear@0 343 srcLight->mFalloffAngle);
nuclear@0 344 }
nuclear@0 345 else {
nuclear@0 346 out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD( srcLight->mPenumbraAngle );
nuclear@0 347 if (out->mAngleOuterCone < out->mAngleInnerCone)
nuclear@0 348 std::swap(out->mAngleInnerCone,out->mAngleOuterCone);
nuclear@0 349 }
nuclear@0 350 }
nuclear@0 351 else out->mAngleOuterCone = AI_DEG_TO_RAD( srcLight->mOuterAngle );
nuclear@0 352 }
nuclear@0 353
nuclear@0 354 // add to light list
nuclear@0 355 mLights.push_back(out);
nuclear@0 356 }
nuclear@0 357 }
nuclear@0 358
nuclear@0 359 // ------------------------------------------------------------------------------------------------
nuclear@0 360 // Builds cameras for the given node and references them
nuclear@0 361 void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
nuclear@0 362 {
nuclear@0 363 BOOST_FOREACH( const Collada::CameraInstance& cid, pNode->mCameras)
nuclear@0 364 {
nuclear@0 365 // find the referred light
nuclear@0 366 ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find( cid.mCamera);
nuclear@0 367 if( srcCameraIt == pParser.mCameraLibrary.end())
nuclear@0 368 {
nuclear@0 369 DefaultLogger::get()->warn("Collada: Unable to find camera for ID \"" + cid.mCamera + "\". Skipping.");
nuclear@0 370 continue;
nuclear@0 371 }
nuclear@0 372 const Collada::Camera* srcCamera = &srcCameraIt->second;
nuclear@0 373
nuclear@0 374 // orthographic cameras not yet supported in Assimp
nuclear@0 375 if (srcCamera->mOrtho) {
nuclear@0 376 DefaultLogger::get()->warn("Collada: Orthographic cameras are not supported.");
nuclear@0 377 }
nuclear@0 378
nuclear@0 379 // now fill our ai data structure
nuclear@0 380 aiCamera* out = new aiCamera();
nuclear@0 381 out->mName = pTarget->mName;
nuclear@0 382
nuclear@0 383 // collada cameras point in -Z by default, rest is specified in node transform
nuclear@0 384 out->mLookAt = aiVector3D(0.f,0.f,-1.f);
nuclear@0 385
nuclear@0 386 // near/far z is already ok
nuclear@0 387 out->mClipPlaneFar = srcCamera->mZFar;
nuclear@0 388 out->mClipPlaneNear = srcCamera->mZNear;
nuclear@0 389
nuclear@0 390 // ... but for the rest some values are optional
nuclear@0 391 // and we need to compute the others in any combination.
nuclear@0 392 if (srcCamera->mAspect != 10e10f)
nuclear@0 393 out->mAspect = srcCamera->mAspect;
nuclear@0 394
nuclear@0 395 if (srcCamera->mHorFov != 10e10f) {
nuclear@0 396 out->mHorizontalFOV = srcCamera->mHorFov;
nuclear@0 397
nuclear@0 398 if (srcCamera->mVerFov != 10e10f && srcCamera->mAspect == 10e10f) {
nuclear@0 399 out->mAspect = tan(AI_DEG_TO_RAD(srcCamera->mHorFov)) /
nuclear@0 400 tan(AI_DEG_TO_RAD(srcCamera->mVerFov));
nuclear@0 401 }
nuclear@0 402 }
nuclear@0 403 else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) {
nuclear@0 404 out->mHorizontalFOV = 2.0f * AI_RAD_TO_DEG(atan(srcCamera->mAspect *
nuclear@0 405 tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f)));
nuclear@0 406 }
nuclear@0 407
nuclear@0 408 // Collada uses degrees, we use radians
nuclear@0 409 out->mHorizontalFOV = AI_DEG_TO_RAD(out->mHorizontalFOV);
nuclear@0 410
nuclear@0 411 // add to camera list
nuclear@0 412 mCameras.push_back(out);
nuclear@0 413 }
nuclear@0 414 }
nuclear@0 415
nuclear@0 416 // ------------------------------------------------------------------------------------------------
nuclear@0 417 // Builds meshes for the given node and references them
nuclear@0 418 void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
nuclear@0 419 {
nuclear@0 420 // accumulated mesh references by this node
nuclear@0 421 std::vector<size_t> newMeshRefs;
nuclear@0 422 newMeshRefs.reserve(pNode->mMeshes.size());
nuclear@0 423
nuclear@0 424 // add a mesh for each subgroup in each collada mesh
nuclear@0 425 BOOST_FOREACH( const Collada::MeshInstance& mid, pNode->mMeshes)
nuclear@0 426 {
nuclear@0 427 const Collada::Mesh* srcMesh = NULL;
nuclear@0 428 const Collada::Controller* srcController = NULL;
nuclear@0 429
nuclear@0 430 // find the referred mesh
nuclear@0 431 ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMeshOrController);
nuclear@0 432 if( srcMeshIt == pParser.mMeshLibrary.end())
nuclear@0 433 {
nuclear@0 434 // if not found in the mesh-library, it might also be a controller referring to a mesh
nuclear@0 435 ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find( mid.mMeshOrController);
nuclear@0 436 if( srcContrIt != pParser.mControllerLibrary.end())
nuclear@0 437 {
nuclear@0 438 srcController = &srcContrIt->second;
nuclear@0 439 srcMeshIt = pParser.mMeshLibrary.find( srcController->mMeshId);
nuclear@0 440 if( srcMeshIt != pParser.mMeshLibrary.end())
nuclear@0 441 srcMesh = srcMeshIt->second;
nuclear@0 442 }
nuclear@0 443
nuclear@0 444 if( !srcMesh)
nuclear@0 445 {
nuclear@0 446 DefaultLogger::get()->warn( boost::str( boost::format( "Collada: Unable to find geometry for ID \"%s\". Skipping.") % mid.mMeshOrController));
nuclear@0 447 continue;
nuclear@0 448 }
nuclear@0 449 } else
nuclear@0 450 {
nuclear@0 451 // ID found in the mesh library -> direct reference to an unskinned mesh
nuclear@0 452 srcMesh = srcMeshIt->second;
nuclear@0 453 }
nuclear@0 454
nuclear@0 455 // build a mesh for each of its subgroups
nuclear@0 456 size_t vertexStart = 0, faceStart = 0;
nuclear@0 457 for( size_t sm = 0; sm < srcMesh->mSubMeshes.size(); ++sm)
nuclear@0 458 {
nuclear@0 459 const Collada::SubMesh& submesh = srcMesh->mSubMeshes[sm];
nuclear@0 460 if( submesh.mNumFaces == 0)
nuclear@0 461 continue;
nuclear@0 462
nuclear@0 463 // find material assigned to this submesh
nuclear@0 464 std::string meshMaterial;
nuclear@0 465 std::map<std::string, Collada::SemanticMappingTable >::const_iterator meshMatIt = mid.mMaterials.find( submesh.mMaterial);
nuclear@0 466
nuclear@0 467 const Collada::SemanticMappingTable* table = NULL;
nuclear@0 468 if( meshMatIt != mid.mMaterials.end())
nuclear@0 469 {
nuclear@0 470 table = &meshMatIt->second;
nuclear@0 471 meshMaterial = table->mMatName;
nuclear@0 472 }
nuclear@0 473 else
nuclear@0 474 {
nuclear@0 475 DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup <%s> in geometry <%s>.") % submesh.mMaterial % mid.mMeshOrController));
nuclear@0 476 if( !mid.mMaterials.empty() )
nuclear@0 477 meshMaterial = mid.mMaterials.begin()->second.mMatName;
nuclear@0 478 }
nuclear@0 479
nuclear@0 480 // OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table
nuclear@0 481 // given. The only mapping stuff which we do actually support is the UV channel.
nuclear@0 482 std::map<std::string, size_t>::const_iterator matIt = mMaterialIndexByName.find( meshMaterial);
nuclear@0 483 unsigned int matIdx;
nuclear@0 484 if( matIt != mMaterialIndexByName.end())
nuclear@0 485 matIdx = matIt->second;
nuclear@0 486 else
nuclear@0 487 matIdx = 0;
nuclear@0 488
nuclear@0 489 if (table && !table->mMap.empty() ) {
nuclear@0 490 std::pair<Collada::Effect*, aiMaterial*>& mat = newMats[matIdx];
nuclear@0 491
nuclear@0 492 // Iterate through all texture channels assigned to the effect and
nuclear@0 493 // check whether we have mapping information for it.
nuclear@0 494 ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table);
nuclear@0 495 ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table);
nuclear@0 496 ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table);
nuclear@0 497 ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table);
nuclear@0 498 ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent,*table);
nuclear@0 499 ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table);
nuclear@0 500 }
nuclear@0 501
nuclear@0 502 // built lookup index of the Mesh-Submesh-Material combination
nuclear@0 503 ColladaMeshIndex index( mid.mMeshOrController, sm, meshMaterial);
nuclear@0 504
nuclear@0 505 // if we already have the mesh at the library, just add its index to the node's array
nuclear@0 506 std::map<ColladaMeshIndex, size_t>::const_iterator dstMeshIt = mMeshIndexByID.find( index);
nuclear@0 507 if( dstMeshIt != mMeshIndexByID.end()) {
nuclear@0 508 newMeshRefs.push_back( dstMeshIt->second);
nuclear@0 509 }
nuclear@0 510 else
nuclear@0 511 {
nuclear@0 512 // else we have to add the mesh to the collection and store its newly assigned index at the node
nuclear@0 513 aiMesh* dstMesh = CreateMesh( pParser, srcMesh, submesh, srcController, vertexStart, faceStart);
nuclear@0 514
nuclear@0 515 // store the mesh, and store its new index in the node
nuclear@0 516 newMeshRefs.push_back( mMeshes.size());
nuclear@0 517 mMeshIndexByID[index] = mMeshes.size();
nuclear@0 518 mMeshes.push_back( dstMesh);
nuclear@0 519 vertexStart += dstMesh->mNumVertices; faceStart += submesh.mNumFaces;
nuclear@0 520
nuclear@0 521 // assign the material index
nuclear@0 522 dstMesh->mMaterialIndex = matIdx;
nuclear@0 523 dstMesh->mName = mid.mMeshOrController;
nuclear@0 524 }
nuclear@0 525 }
nuclear@0 526 }
nuclear@0 527
nuclear@0 528 // now place all mesh references we gathered in the target node
nuclear@0 529 pTarget->mNumMeshes = newMeshRefs.size();
nuclear@0 530 if( newMeshRefs.size())
nuclear@0 531 {
nuclear@0 532 pTarget->mMeshes = new unsigned int[pTarget->mNumMeshes];
nuclear@0 533 std::copy( newMeshRefs.begin(), newMeshRefs.end(), pTarget->mMeshes);
nuclear@0 534 }
nuclear@0 535 }
nuclear@0 536
nuclear@0 537 // ------------------------------------------------------------------------------------------------
nuclear@0 538 // Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh
nuclear@0 539 aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
nuclear@0 540 const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace)
nuclear@0 541 {
nuclear@0 542 aiMesh* dstMesh = new aiMesh;
nuclear@0 543
nuclear@0 544 // count the vertices addressed by its faces
nuclear@0 545 const size_t numVertices = std::accumulate( pSrcMesh->mFaceSize.begin() + pStartFace,
nuclear@0 546 pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, 0);
nuclear@0 547
nuclear@0 548 // copy positions
nuclear@0 549 dstMesh->mNumVertices = numVertices;
nuclear@0 550 dstMesh->mVertices = new aiVector3D[numVertices];
nuclear@0 551 std::copy( pSrcMesh->mPositions.begin() + pStartVertex, pSrcMesh->mPositions.begin() +
nuclear@0 552 pStartVertex + numVertices, dstMesh->mVertices);
nuclear@0 553
nuclear@0 554 // normals, if given. HACK: (thom) Due to the glorious Collada spec we never
nuclear@0 555 // know if we have the same number of normals as there are positions. So we
nuclear@0 556 // also ignore any vertex attribute if it has a different count
nuclear@0 557 if( pSrcMesh->mNormals.size() >= pStartVertex + numVertices)
nuclear@0 558 {
nuclear@0 559 dstMesh->mNormals = new aiVector3D[numVertices];
nuclear@0 560 std::copy( pSrcMesh->mNormals.begin() + pStartVertex, pSrcMesh->mNormals.begin() +
nuclear@0 561 pStartVertex + numVertices, dstMesh->mNormals);
nuclear@0 562 }
nuclear@0 563
nuclear@0 564 // tangents, if given.
nuclear@0 565 if( pSrcMesh->mTangents.size() >= pStartVertex + numVertices)
nuclear@0 566 {
nuclear@0 567 dstMesh->mTangents = new aiVector3D[numVertices];
nuclear@0 568 std::copy( pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() +
nuclear@0 569 pStartVertex + numVertices, dstMesh->mTangents);
nuclear@0 570 }
nuclear@0 571
nuclear@0 572 // bitangents, if given.
nuclear@0 573 if( pSrcMesh->mBitangents.size() >= pStartVertex + numVertices)
nuclear@0 574 {
nuclear@0 575 dstMesh->mBitangents = new aiVector3D[numVertices];
nuclear@0 576 std::copy( pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() +
nuclear@0 577 pStartVertex + numVertices, dstMesh->mBitangents);
nuclear@0 578 }
nuclear@0 579
nuclear@0 580 // same for texturecoords, as many as we have
nuclear@0 581 // empty slots are not allowed, need to pack and adjust UV indexes accordingly
nuclear@0 582 for( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
nuclear@0 583 {
nuclear@0 584 if( pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices)
nuclear@0 585 {
nuclear@0 586 dstMesh->mTextureCoords[real] = new aiVector3D[numVertices];
nuclear@0 587 for( size_t b = 0; b < numVertices; ++b)
nuclear@0 588 dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex+b];
nuclear@0 589
nuclear@0 590 dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a];
nuclear@0 591 ++real;
nuclear@0 592 }
nuclear@0 593 }
nuclear@0 594
nuclear@0 595 // same for vertex colors, as many as we have. again the same packing to avoid empty slots
nuclear@0 596 for( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
nuclear@0 597 {
nuclear@0 598 if( pSrcMesh->mColors[a].size() >= pStartVertex + numVertices)
nuclear@0 599 {
nuclear@0 600 dstMesh->mColors[real] = new aiColor4D[numVertices];
nuclear@0 601 std::copy( pSrcMesh->mColors[a].begin() + pStartVertex, pSrcMesh->mColors[a].begin() + pStartVertex + numVertices,dstMesh->mColors[real]);
nuclear@0 602 ++real;
nuclear@0 603 }
nuclear@0 604 }
nuclear@0 605
nuclear@0 606 // create faces. Due to the fact that each face uses unique vertices, we can simply count up on each vertex
nuclear@0 607 size_t vertex = 0;
nuclear@0 608 dstMesh->mNumFaces = pSubMesh.mNumFaces;
nuclear@0 609 dstMesh->mFaces = new aiFace[dstMesh->mNumFaces];
nuclear@0 610 for( size_t a = 0; a < dstMesh->mNumFaces; ++a)
nuclear@0 611 {
nuclear@0 612 size_t s = pSrcMesh->mFaceSize[ pStartFace + a];
nuclear@0 613 aiFace& face = dstMesh->mFaces[a];
nuclear@0 614 face.mNumIndices = s;
nuclear@0 615 face.mIndices = new unsigned int[s];
nuclear@0 616 for( size_t b = 0; b < s; ++b)
nuclear@0 617 face.mIndices[b] = vertex++;
nuclear@0 618 }
nuclear@0 619
nuclear@0 620 // create bones if given
nuclear@0 621 if( pSrcController)
nuclear@0 622 {
nuclear@0 623 // refuse if the vertex count does not match
nuclear@0 624 // if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices)
nuclear@0 625 // throw DeadlyImportError( "Joint Controller vertex count does not match mesh vertex count");
nuclear@0 626
nuclear@0 627 // resolve references - joint names
nuclear@0 628 const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource);
nuclear@0 629 const Collada::Data& jointNames = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointNamesAcc.mSource);
nuclear@0 630 // joint offset matrices
nuclear@0 631 const Collada::Accessor& jointMatrixAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource);
nuclear@0 632 const Collada::Data& jointMatrices = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointMatrixAcc.mSource);
nuclear@0 633 // joint vertex_weight name list - should refer to the same list as the joint names above. If not, report and reconsider
nuclear@0 634 const Collada::Accessor& weightNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor);
nuclear@0 635 if( &weightNamesAcc != &jointNamesAcc)
nuclear@0 636 throw DeadlyImportError( "Temporary implementational lazyness. If you read this, please report to the author.");
nuclear@0 637 // vertex weights
nuclear@0 638 const Collada::Accessor& weightsAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor);
nuclear@0 639 const Collada::Data& weights = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightsAcc.mSource);
nuclear@0 640
nuclear@0 641 if( !jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray)
nuclear@0 642 throw DeadlyImportError( "Data type mismatch while resolving mesh joints");
nuclear@0 643 // sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex
nuclear@0 644 if( pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1)
nuclear@0 645 throw DeadlyImportError( "Unsupported vertex_weight addressing scheme. ");
nuclear@0 646
nuclear@0 647 // create containers to collect the weights for each bone
nuclear@0 648 size_t numBones = jointNames.mStrings.size();
nuclear@0 649 std::vector<std::vector<aiVertexWeight> > dstBones( numBones);
nuclear@0 650
nuclear@0 651 // build a temporary array of pointers to the start of each vertex's weights
nuclear@0 652 typedef std::vector< std::pair<size_t, size_t> > IndexPairVector;
nuclear@0 653 std::vector<IndexPairVector::const_iterator> weightStartPerVertex;
nuclear@0 654 weightStartPerVertex.resize(pSrcController->mWeightCounts.size(),pSrcController->mWeights.end());
nuclear@0 655
nuclear@0 656 IndexPairVector::const_iterator pit = pSrcController->mWeights.begin();
nuclear@0 657 for( size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a)
nuclear@0 658 {
nuclear@0 659 weightStartPerVertex[a] = pit;
nuclear@0 660 pit += pSrcController->mWeightCounts[a];
nuclear@0 661 }
nuclear@0 662
nuclear@0 663 // now for each vertex put the corresponding vertex weights into each bone's weight collection
nuclear@0 664 for( size_t a = pStartVertex; a < pStartVertex + numVertices; ++a)
nuclear@0 665 {
nuclear@0 666 // which position index was responsible for this vertex? that's also the index by which
nuclear@0 667 // the controller assigns the vertex weights
nuclear@0 668 size_t orgIndex = pSrcMesh->mFacePosIndices[a];
nuclear@0 669 // find the vertex weights for this vertex
nuclear@0 670 IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex];
nuclear@0 671 size_t pairCount = pSrcController->mWeightCounts[orgIndex];
nuclear@0 672
nuclear@0 673 for( size_t b = 0; b < pairCount; ++b, ++iit)
nuclear@0 674 {
nuclear@0 675 size_t jointIndex = iit->first;
nuclear@0 676 size_t vertexIndex = iit->second;
nuclear@0 677
nuclear@0 678 float weight = ReadFloat( weightsAcc, weights, vertexIndex, 0);
nuclear@0 679
nuclear@0 680 // one day I gonna kill that XSI Collada exporter
nuclear@0 681 if( weight > 0.0f)
nuclear@0 682 {
nuclear@0 683 aiVertexWeight w;
nuclear@0 684 w.mVertexId = a - pStartVertex;
nuclear@0 685 w.mWeight = weight;
nuclear@0 686 dstBones[jointIndex].push_back( w);
nuclear@0 687 }
nuclear@0 688 }
nuclear@0 689 }
nuclear@0 690
nuclear@0 691 // count the number of bones which influence vertices of the current submesh
nuclear@0 692 size_t numRemainingBones = 0;
nuclear@0 693 for( std::vector<std::vector<aiVertexWeight> >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it)
nuclear@0 694 if( it->size() > 0)
nuclear@0 695 numRemainingBones++;
nuclear@0 696
nuclear@0 697 // create bone array and copy bone weights one by one
nuclear@0 698 dstMesh->mNumBones = numRemainingBones;
nuclear@0 699 dstMesh->mBones = new aiBone*[numRemainingBones];
nuclear@0 700 size_t boneCount = 0;
nuclear@0 701 for( size_t a = 0; a < numBones; ++a)
nuclear@0 702 {
nuclear@0 703 // omit bones without weights
nuclear@0 704 if( dstBones[a].size() == 0)
nuclear@0 705 continue;
nuclear@0 706
nuclear@0 707 // create bone with its weights
nuclear@0 708 aiBone* bone = new aiBone;
nuclear@0 709 bone->mName = ReadString( jointNamesAcc, jointNames, a);
nuclear@0 710 bone->mOffsetMatrix.a1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 0);
nuclear@0 711 bone->mOffsetMatrix.a2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 1);
nuclear@0 712 bone->mOffsetMatrix.a3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 2);
nuclear@0 713 bone->mOffsetMatrix.a4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 3);
nuclear@0 714 bone->mOffsetMatrix.b1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 4);
nuclear@0 715 bone->mOffsetMatrix.b2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 5);
nuclear@0 716 bone->mOffsetMatrix.b3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 6);
nuclear@0 717 bone->mOffsetMatrix.b4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 7);
nuclear@0 718 bone->mOffsetMatrix.c1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 8);
nuclear@0 719 bone->mOffsetMatrix.c2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 9);
nuclear@0 720 bone->mOffsetMatrix.c3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 10);
nuclear@0 721 bone->mOffsetMatrix.c4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 11);
nuclear@0 722 bone->mNumWeights = dstBones[a].size();
nuclear@0 723 bone->mWeights = new aiVertexWeight[bone->mNumWeights];
nuclear@0 724 std::copy( dstBones[a].begin(), dstBones[a].end(), bone->mWeights);
nuclear@0 725
nuclear@0 726 // apply bind shape matrix to offset matrix
nuclear@0 727 aiMatrix4x4 bindShapeMatrix;
nuclear@0 728 bindShapeMatrix.a1 = pSrcController->mBindShapeMatrix[0];
nuclear@0 729 bindShapeMatrix.a2 = pSrcController->mBindShapeMatrix[1];
nuclear@0 730 bindShapeMatrix.a3 = pSrcController->mBindShapeMatrix[2];
nuclear@0 731 bindShapeMatrix.a4 = pSrcController->mBindShapeMatrix[3];
nuclear@0 732 bindShapeMatrix.b1 = pSrcController->mBindShapeMatrix[4];
nuclear@0 733 bindShapeMatrix.b2 = pSrcController->mBindShapeMatrix[5];
nuclear@0 734 bindShapeMatrix.b3 = pSrcController->mBindShapeMatrix[6];
nuclear@0 735 bindShapeMatrix.b4 = pSrcController->mBindShapeMatrix[7];
nuclear@0 736 bindShapeMatrix.c1 = pSrcController->mBindShapeMatrix[8];
nuclear@0 737 bindShapeMatrix.c2 = pSrcController->mBindShapeMatrix[9];
nuclear@0 738 bindShapeMatrix.c3 = pSrcController->mBindShapeMatrix[10];
nuclear@0 739 bindShapeMatrix.c4 = pSrcController->mBindShapeMatrix[11];
nuclear@0 740 bindShapeMatrix.d1 = pSrcController->mBindShapeMatrix[12];
nuclear@0 741 bindShapeMatrix.d2 = pSrcController->mBindShapeMatrix[13];
nuclear@0 742 bindShapeMatrix.d3 = pSrcController->mBindShapeMatrix[14];
nuclear@0 743 bindShapeMatrix.d4 = pSrcController->mBindShapeMatrix[15];
nuclear@0 744 bone->mOffsetMatrix *= bindShapeMatrix;
nuclear@0 745
nuclear@0 746 // HACK: (thom) Some exporters address the bone nodes by SID, others address them by ID or even name.
nuclear@0 747 // Therefore I added a little name replacement here: I search for the bone's node by either name, ID or SID,
nuclear@0 748 // and replace the bone's name by the node's name so that the user can use the standard
nuclear@0 749 // find-by-name method to associate nodes with bones.
nuclear@0 750 const Collada::Node* bnode = FindNode( pParser.mRootNode, bone->mName.data);
nuclear@0 751 if( !bnode)
nuclear@0 752 bnode = FindNodeBySID( pParser.mRootNode, bone->mName.data);
nuclear@0 753
nuclear@0 754 // assign the name that we would have assigned for the source node
nuclear@0 755 if( bnode)
nuclear@0 756 bone->mName.Set( FindNameForNode( bnode));
nuclear@0 757 else
nuclear@0 758 DefaultLogger::get()->warn( boost::str( boost::format( "ColladaLoader::CreateMesh(): could not find corresponding node for joint \"%s\".") % bone->mName.data));
nuclear@0 759
nuclear@0 760 // and insert bone
nuclear@0 761 dstMesh->mBones[boneCount++] = bone;
nuclear@0 762 }
nuclear@0 763 }
nuclear@0 764
nuclear@0 765 return dstMesh;
nuclear@0 766 }
nuclear@0 767
nuclear@0 768 // ------------------------------------------------------------------------------------------------
nuclear@0 769 // Stores all meshes in the given scene
nuclear@0 770 void ColladaLoader::StoreSceneMeshes( aiScene* pScene)
nuclear@0 771 {
nuclear@0 772 pScene->mNumMeshes = mMeshes.size();
nuclear@0 773 if( mMeshes.size() > 0)
nuclear@0 774 {
nuclear@0 775 pScene->mMeshes = new aiMesh*[mMeshes.size()];
nuclear@0 776 std::copy( mMeshes.begin(), mMeshes.end(), pScene->mMeshes);
nuclear@0 777 mMeshes.clear();
nuclear@0 778 }
nuclear@0 779 }
nuclear@0 780
nuclear@0 781 // ------------------------------------------------------------------------------------------------
nuclear@0 782 // Stores all cameras in the given scene
nuclear@0 783 void ColladaLoader::StoreSceneCameras( aiScene* pScene)
nuclear@0 784 {
nuclear@0 785 pScene->mNumCameras = mCameras.size();
nuclear@0 786 if( mCameras.size() > 0)
nuclear@0 787 {
nuclear@0 788 pScene->mCameras = new aiCamera*[mCameras.size()];
nuclear@0 789 std::copy( mCameras.begin(), mCameras.end(), pScene->mCameras);
nuclear@0 790 mCameras.clear();
nuclear@0 791 }
nuclear@0 792 }
nuclear@0 793
nuclear@0 794 // ------------------------------------------------------------------------------------------------
nuclear@0 795 // Stores all lights in the given scene
nuclear@0 796 void ColladaLoader::StoreSceneLights( aiScene* pScene)
nuclear@0 797 {
nuclear@0 798 pScene->mNumLights = mLights.size();
nuclear@0 799 if( mLights.size() > 0)
nuclear@0 800 {
nuclear@0 801 pScene->mLights = new aiLight*[mLights.size()];
nuclear@0 802 std::copy( mLights.begin(), mLights.end(), pScene->mLights);
nuclear@0 803 mLights.clear();
nuclear@0 804 }
nuclear@0 805 }
nuclear@0 806
nuclear@0 807 // ------------------------------------------------------------------------------------------------
nuclear@0 808 // Stores all textures in the given scene
nuclear@0 809 void ColladaLoader::StoreSceneTextures( aiScene* pScene)
nuclear@0 810 {
nuclear@0 811 pScene->mNumTextures = mTextures.size();
nuclear@0 812 if( mTextures.size() > 0)
nuclear@0 813 {
nuclear@0 814 pScene->mTextures = new aiTexture*[mTextures.size()];
nuclear@0 815 std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures);
nuclear@0 816 mTextures.clear();
nuclear@0 817 }
nuclear@0 818 }
nuclear@0 819
nuclear@0 820 // ------------------------------------------------------------------------------------------------
nuclear@0 821 // Stores all materials in the given scene
nuclear@0 822 void ColladaLoader::StoreSceneMaterials( aiScene* pScene)
nuclear@0 823 {
nuclear@0 824 pScene->mNumMaterials = newMats.size();
nuclear@0 825
nuclear@0 826 if (newMats.size() > 0) {
nuclear@0 827 pScene->mMaterials = new aiMaterial*[newMats.size()];
nuclear@0 828 for (unsigned int i = 0; i < newMats.size();++i)
nuclear@0 829 pScene->mMaterials[i] = newMats[i].second;
nuclear@0 830
nuclear@0 831 newMats.clear();
nuclear@0 832 }
nuclear@0 833 }
nuclear@0 834
nuclear@0 835 // ------------------------------------------------------------------------------------------------
nuclear@0 836 // Stores all animations
nuclear@0 837 void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser)
nuclear@0 838 {
nuclear@0 839 // recursivly collect all animations from the collada scene
nuclear@0 840 StoreAnimations( pScene, pParser, &pParser.mAnims, "");
nuclear@0 841
nuclear@0 842 // catch special case: many animations with the same length, each affecting only a single node.
nuclear@0 843 // we need to unite all those single-node-anims to a proper combined animation
nuclear@0 844 for( size_t a = 0; a < mAnims.size(); ++a)
nuclear@0 845 {
nuclear@0 846 aiAnimation* templateAnim = mAnims[a];
nuclear@0 847 if( templateAnim->mNumChannels == 1)
nuclear@0 848 {
nuclear@0 849 // search for other single-channel-anims with the same duration
nuclear@0 850 std::vector<size_t> collectedAnimIndices;
nuclear@0 851 for( size_t b = a+1; b < mAnims.size(); ++b)
nuclear@0 852 {
nuclear@0 853 aiAnimation* other = mAnims[b];
nuclear@0 854 if( other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && other->mTicksPerSecond == templateAnim->mTicksPerSecond )
nuclear@0 855 collectedAnimIndices.push_back( b);
nuclear@0 856 }
nuclear@0 857
nuclear@0 858 // if there are other animations which fit the template anim, combine all channels into a single anim
nuclear@0 859 if( !collectedAnimIndices.empty() )
nuclear@0 860 {
nuclear@0 861 aiAnimation* combinedAnim = new aiAnimation();
nuclear@0 862 combinedAnim->mName = aiString( std::string( "combinedAnim_") + char( '0' + a));
nuclear@0 863 combinedAnim->mDuration = templateAnim->mDuration;
nuclear@0 864 combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond;
nuclear@0 865 combinedAnim->mNumChannels = collectedAnimIndices.size() + 1;
nuclear@0 866 combinedAnim->mChannels = new aiNodeAnim*[combinedAnim->mNumChannels];
nuclear@0 867 // add the template anim as first channel by moving its aiNodeAnim to the combined animation
nuclear@0 868 combinedAnim->mChannels[0] = templateAnim->mChannels[0];
nuclear@0 869 templateAnim->mChannels[0] = NULL;
nuclear@0 870 delete templateAnim;
nuclear@0 871 // combined animation replaces template animation in the anim array
nuclear@0 872 mAnims[a] = combinedAnim;
nuclear@0 873
nuclear@0 874 // move the memory of all other anims to the combined anim and erase them from the source anims
nuclear@0 875 for( size_t b = 0; b < collectedAnimIndices.size(); ++b)
nuclear@0 876 {
nuclear@0 877 aiAnimation* srcAnimation = mAnims[collectedAnimIndices[b]];
nuclear@0 878 combinedAnim->mChannels[1 + b] = srcAnimation->mChannels[0];
nuclear@0 879 srcAnimation->mChannels[0] = NULL;
nuclear@0 880 delete srcAnimation;
nuclear@0 881 }
nuclear@0 882
nuclear@0 883 // in a second go, delete all the single-channel-anims that we've stripped from their channels
nuclear@0 884 // back to front to preserve indices - you know, removing an element from a vector moves all elements behind the removed one
nuclear@0 885 while( !collectedAnimIndices.empty() )
nuclear@0 886 {
nuclear@0 887 mAnims.erase( mAnims.begin() + collectedAnimIndices.back());
nuclear@0 888 collectedAnimIndices.pop_back();
nuclear@0 889 }
nuclear@0 890 }
nuclear@0 891 }
nuclear@0 892 }
nuclear@0 893
nuclear@0 894 // now store all anims in the scene
nuclear@0 895 if( !mAnims.empty())
nuclear@0 896 {
nuclear@0 897 pScene->mNumAnimations = mAnims.size();
nuclear@0 898 pScene->mAnimations = new aiAnimation*[mAnims.size()];
nuclear@0 899 std::copy( mAnims.begin(), mAnims.end(), pScene->mAnimations);
nuclear@0 900 }
nuclear@0 901 }
nuclear@0 902
nuclear@0 903 // ------------------------------------------------------------------------------------------------
nuclear@0 904 // Constructs the animations for the given source anim
nuclear@0 905 void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string pPrefix)
nuclear@0 906 {
nuclear@0 907 std::string animName = pPrefix.empty() ? pSrcAnim->mName : pPrefix + "_" + pSrcAnim->mName;
nuclear@0 908
nuclear@0 909 // create nested animations, if given
nuclear@0 910 for( std::vector<Collada::Animation*>::const_iterator it = pSrcAnim->mSubAnims.begin(); it != pSrcAnim->mSubAnims.end(); ++it)
nuclear@0 911 StoreAnimations( pScene, pParser, *it, animName);
nuclear@0 912
nuclear@0 913 // create animation channels, if any
nuclear@0 914 if( !pSrcAnim->mChannels.empty())
nuclear@0 915 CreateAnimation( pScene, pParser, pSrcAnim, animName);
nuclear@0 916 }
nuclear@0 917
nuclear@0 918 // ------------------------------------------------------------------------------------------------
nuclear@0 919 // Constructs the animation for the given source anim
nuclear@0 920 void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName)
nuclear@0 921 {
nuclear@0 922 // collect a list of animatable nodes
nuclear@0 923 std::vector<const aiNode*> nodes;
nuclear@0 924 CollectNodes( pScene->mRootNode, nodes);
nuclear@0 925
nuclear@0 926 std::vector<aiNodeAnim*> anims;
nuclear@0 927 for( std::vector<const aiNode*>::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit)
nuclear@0 928 {
nuclear@0 929 // find all the collada anim channels which refer to the current node
nuclear@0 930 std::vector<Collada::ChannelEntry> entries;
nuclear@0 931 std::string nodeName = (*nit)->mName.data;
nuclear@0 932
nuclear@0 933 // find the collada node corresponding to the aiNode
nuclear@0 934 const Collada::Node* srcNode = FindNode( pParser.mRootNode, nodeName);
nuclear@0 935 // ai_assert( srcNode != NULL);
nuclear@0 936 if( !srcNode)
nuclear@0 937 continue;
nuclear@0 938
nuclear@0 939 // now check all channels if they affect the current node
nuclear@0 940 for( std::vector<Collada::AnimationChannel>::const_iterator cit = pSrcAnim->mChannels.begin();
nuclear@0 941 cit != pSrcAnim->mChannels.end(); ++cit)
nuclear@0 942 {
nuclear@0 943 const Collada::AnimationChannel& srcChannel = *cit;
nuclear@0 944 Collada::ChannelEntry entry;
nuclear@0 945
nuclear@0 946 // we expect the animation target to be of type "nodeName/transformID.subElement". Ignore all others
nuclear@0 947 // find the slash that separates the node name - there should be only one
nuclear@0 948 std::string::size_type slashPos = srcChannel.mTarget.find( '/');
nuclear@0 949 if( slashPos == std::string::npos)
nuclear@0 950 continue;
nuclear@0 951 if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos)
nuclear@0 952 continue;
nuclear@0 953 std::string targetID = srcChannel.mTarget.substr( 0, slashPos);
nuclear@0 954 if( targetID != srcNode->mID)
nuclear@0 955 continue;
nuclear@0 956
nuclear@0 957 // find the dot that separates the transformID - there should be only one or zero
nuclear@0 958 std::string::size_type dotPos = srcChannel.mTarget.find( '.');
nuclear@0 959 if( dotPos != std::string::npos)
nuclear@0 960 {
nuclear@0 961 if( srcChannel.mTarget.find( '.', dotPos+1) != std::string::npos)
nuclear@0 962 continue;
nuclear@0 963
nuclear@0 964 entry.mTransformId = srcChannel.mTarget.substr( slashPos+1, dotPos - slashPos - 1);
nuclear@0 965
nuclear@0 966 std::string subElement = srcChannel.mTarget.substr( dotPos+1);
nuclear@0 967 if( subElement == "ANGLE")
nuclear@0 968 entry.mSubElement = 3; // last number in an Axis-Angle-Transform is the angle
nuclear@0 969 else if( subElement == "X")
nuclear@0 970 entry.mSubElement = 0;
nuclear@0 971 else if( subElement == "Y")
nuclear@0 972 entry.mSubElement = 1;
nuclear@0 973 else if( subElement == "Z")
nuclear@0 974 entry.mSubElement = 2;
nuclear@0 975 else
nuclear@0 976 DefaultLogger::get()->warn( boost::str( boost::format( "Unknown anim subelement <%s>. Ignoring") % subElement));
nuclear@0 977 } else
nuclear@0 978 {
nuclear@0 979 // no subelement following, transformId is remaining string
nuclear@0 980 entry.mTransformId = srcChannel.mTarget.substr( slashPos+1);
nuclear@0 981 }
nuclear@0 982
nuclear@0 983 // determine which transform step is affected by this channel
nuclear@0 984 entry.mTransformIndex = SIZE_MAX;
nuclear@0 985 for( size_t a = 0; a < srcNode->mTransforms.size(); ++a)
nuclear@0 986 if( srcNode->mTransforms[a].mID == entry.mTransformId)
nuclear@0 987 entry.mTransformIndex = a;
nuclear@0 988
nuclear@0 989 if( entry.mTransformIndex == SIZE_MAX) {
nuclear@0 990 continue;
nuclear@0 991 }
nuclear@0 992
nuclear@0 993 entry.mChannel = &(*cit);
nuclear@0 994 entries.push_back( entry);
nuclear@0 995 }
nuclear@0 996
nuclear@0 997 // if there's no channel affecting the current node, we skip it
nuclear@0 998 if( entries.empty())
nuclear@0 999 continue;
nuclear@0 1000
nuclear@0 1001 // resolve the data pointers for all anim channels. Find the minimum time while we're at it
nuclear@0 1002 float startTime = 1e20f, endTime = -1e20f;
nuclear@0 1003 for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
nuclear@0 1004 {
nuclear@0 1005 Collada::ChannelEntry& e = *it;
nuclear@0 1006 e.mTimeAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceTimes);
nuclear@0 1007 e.mTimeData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mTimeAccessor->mSource);
nuclear@0 1008 e.mValueAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceValues);
nuclear@0 1009 e.mValueData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mValueAccessor->mSource);
nuclear@0 1010
nuclear@0 1011 // time count and value count must match
nuclear@0 1012 if( e.mTimeAccessor->mCount != e.mValueAccessor->mCount)
nuclear@0 1013 throw DeadlyImportError( boost::str( boost::format( "Time count / value count mismatch in animation channel \"%s\".") % e.mChannel->mTarget));
nuclear@0 1014
nuclear@0 1015 if( e.mTimeAccessor->mCount > 0 )
nuclear@0 1016 {
nuclear@0 1017 // find bounding times
nuclear@0 1018 startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0));
nuclear@0 1019 endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0));
nuclear@0 1020 }
nuclear@0 1021 }
nuclear@0 1022
nuclear@0 1023 std::vector<aiMatrix4x4> resultTrafos;
nuclear@0 1024 if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 )
nuclear@0 1025 {
nuclear@0 1026 // create a local transformation chain of the node's transforms
nuclear@0 1027 std::vector<Collada::Transform> transforms = srcNode->mTransforms;
nuclear@0 1028
nuclear@0 1029 // now for every unique point in time, find or interpolate the key values for that time
nuclear@0 1030 // and apply them to the transform chain. Then the node's present transformation can be calculated.
nuclear@0 1031 float time = startTime;
nuclear@0 1032 while( 1)
nuclear@0 1033 {
nuclear@0 1034 for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
nuclear@0 1035 {
nuclear@0 1036 Collada::ChannelEntry& e = *it;
nuclear@0 1037
nuclear@0 1038 // find the keyframe behind the current point in time
nuclear@0 1039 size_t pos = 0;
nuclear@0 1040 float postTime = 0.f;
nuclear@0 1041 while( 1)
nuclear@0 1042 {
nuclear@0 1043 if( pos >= e.mTimeAccessor->mCount)
nuclear@0 1044 break;
nuclear@0 1045 postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
nuclear@0 1046 if( postTime >= time)
nuclear@0 1047 break;
nuclear@0 1048 ++pos;
nuclear@0 1049 }
nuclear@0 1050
nuclear@0 1051 pos = std::min( pos, e.mTimeAccessor->mCount-1);
nuclear@0 1052
nuclear@0 1053 // read values from there
nuclear@0 1054 float temp[16];
nuclear@0 1055 for( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
nuclear@0 1056 temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c);
nuclear@0 1057
nuclear@0 1058 // if not exactly at the key time, interpolate with previous value set
nuclear@0 1059 if( postTime > time && pos > 0)
nuclear@0 1060 {
nuclear@0 1061 float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0);
nuclear@0 1062 float factor = (time - postTime) / (preTime - postTime);
nuclear@0 1063
nuclear@0 1064 for( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
nuclear@0 1065 {
nuclear@0 1066 float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c);
nuclear@0 1067 temp[c] += (v - temp[c]) * factor;
nuclear@0 1068 }
nuclear@0 1069 }
nuclear@0 1070
nuclear@0 1071 // Apply values to current transformation
nuclear@0 1072 std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement);
nuclear@0 1073 }
nuclear@0 1074
nuclear@0 1075 // Calculate resulting transformation
nuclear@0 1076 aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms);
nuclear@0 1077
nuclear@0 1078 // out of lazyness: we store the time in matrix.d4
nuclear@0 1079 mat.d4 = time;
nuclear@0 1080 resultTrafos.push_back( mat);
nuclear@0 1081
nuclear@0 1082 // find next point in time to evaluate. That's the closest frame larger than the current in any channel
nuclear@0 1083 float nextTime = 1e20f;
nuclear@0 1084 for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
nuclear@0 1085 {
nuclear@0 1086 Collada::ChannelEntry& e = *it;
nuclear@0 1087
nuclear@0 1088 // find the next time value larger than the current
nuclear@0 1089 size_t pos = 0;
nuclear@0 1090 while( pos < e.mTimeAccessor->mCount)
nuclear@0 1091 {
nuclear@0 1092 float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
nuclear@0 1093 if( t > time)
nuclear@0 1094 {
nuclear@0 1095 nextTime = std::min( nextTime, t);
nuclear@0 1096 break;
nuclear@0 1097 }
nuclear@0 1098 ++pos;
nuclear@0 1099 }
nuclear@0 1100 }
nuclear@0 1101
nuclear@0 1102 // no more keys on any channel after the current time -> we're done
nuclear@0 1103 if( nextTime > 1e19)
nuclear@0 1104 break;
nuclear@0 1105
nuclear@0 1106 // else construct next keyframe at this following time point
nuclear@0 1107 time = nextTime;
nuclear@0 1108 }
nuclear@0 1109 }
nuclear@0 1110
nuclear@0 1111 // there should be some keyframes, but we aren't that fixated on valid input data
nuclear@0 1112 // ai_assert( resultTrafos.size() > 0);
nuclear@0 1113
nuclear@0 1114 // build an animation channel for the given node out of these trafo keys
nuclear@0 1115 if( !resultTrafos.empty() )
nuclear@0 1116 {
nuclear@0 1117 aiNodeAnim* dstAnim = new aiNodeAnim;
nuclear@0 1118 dstAnim->mNodeName = nodeName;
nuclear@0 1119 dstAnim->mNumPositionKeys = resultTrafos.size();
nuclear@0 1120 dstAnim->mNumRotationKeys= resultTrafos.size();
nuclear@0 1121 dstAnim->mNumScalingKeys = resultTrafos.size();
nuclear@0 1122 dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()];
nuclear@0 1123 dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()];
nuclear@0 1124 dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()];
nuclear@0 1125
nuclear@0 1126 for( size_t a = 0; a < resultTrafos.size(); ++a)
nuclear@0 1127 {
nuclear@0 1128 aiMatrix4x4 mat = resultTrafos[a];
nuclear@0 1129 double time = double( mat.d4); // remember? time is stored in mat.d4
nuclear@0 1130 mat.d4 = 1.0f;
nuclear@0 1131
nuclear@0 1132 dstAnim->mPositionKeys[a].mTime = time;
nuclear@0 1133 dstAnim->mRotationKeys[a].mTime = time;
nuclear@0 1134 dstAnim->mScalingKeys[a].mTime = time;
nuclear@0 1135 mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue);
nuclear@0 1136 }
nuclear@0 1137
nuclear@0 1138 anims.push_back( dstAnim);
nuclear@0 1139 } else
nuclear@0 1140 {
nuclear@0 1141 DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter.");
nuclear@0 1142 }
nuclear@0 1143 }
nuclear@0 1144
nuclear@0 1145 if( !anims.empty())
nuclear@0 1146 {
nuclear@0 1147 aiAnimation* anim = new aiAnimation;
nuclear@0 1148 anim->mName.Set( pName);
nuclear@0 1149 anim->mNumChannels = anims.size();
nuclear@0 1150 anim->mChannels = new aiNodeAnim*[anims.size()];
nuclear@0 1151 std::copy( anims.begin(), anims.end(), anim->mChannels);
nuclear@0 1152 anim->mDuration = 0.0f;
nuclear@0 1153 for( size_t a = 0; a < anims.size(); ++a)
nuclear@0 1154 {
nuclear@0 1155 anim->mDuration = std::max( anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys-1].mTime);
nuclear@0 1156 anim->mDuration = std::max( anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys-1].mTime);
nuclear@0 1157 anim->mDuration = std::max( anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys-1].mTime);
nuclear@0 1158 }
nuclear@0 1159 anim->mTicksPerSecond = 1;
nuclear@0 1160 mAnims.push_back( anim);
nuclear@0 1161 }
nuclear@0 1162 }
nuclear@0 1163
nuclear@0 1164 // ------------------------------------------------------------------------------------------------
nuclear@0 1165 // Add a texture to a material structure
nuclear@0 1166 void ColladaLoader::AddTexture ( aiMaterial& mat, const ColladaParser& pParser,
nuclear@0 1167 const Collada::Effect& effect,
nuclear@0 1168 const Collada::Sampler& sampler,
nuclear@0 1169 aiTextureType type, unsigned int idx)
nuclear@0 1170 {
nuclear@0 1171 // first of all, basic file name
nuclear@0 1172 const aiString name = FindFilenameForEffectTexture( pParser, effect, sampler.mName );
nuclear@0 1173 mat.AddProperty( &name, _AI_MATKEY_TEXTURE_BASE, type, idx );
nuclear@0 1174
nuclear@0 1175 // mapping mode
nuclear@0 1176 int map = aiTextureMapMode_Clamp;
nuclear@0 1177 if (sampler.mWrapU)
nuclear@0 1178 map = aiTextureMapMode_Wrap;
nuclear@0 1179 if (sampler.mWrapU && sampler.mMirrorU)
nuclear@0 1180 map = aiTextureMapMode_Mirror;
nuclear@0 1181
nuclear@0 1182 mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_U_BASE, type, idx);
nuclear@0 1183
nuclear@0 1184 map = aiTextureMapMode_Clamp;
nuclear@0 1185 if (sampler.mWrapV)
nuclear@0 1186 map = aiTextureMapMode_Wrap;
nuclear@0 1187 if (sampler.mWrapV && sampler.mMirrorV)
nuclear@0 1188 map = aiTextureMapMode_Mirror;
nuclear@0 1189
nuclear@0 1190 mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_V_BASE, type, idx);
nuclear@0 1191
nuclear@0 1192 // UV transformation
nuclear@0 1193 mat.AddProperty(&sampler.mTransform, 1,
nuclear@0 1194 _AI_MATKEY_UVTRANSFORM_BASE, type, idx);
nuclear@0 1195
nuclear@0 1196 // Blend mode
nuclear@0 1197 mat.AddProperty((int*)&sampler.mOp , 1,
nuclear@0 1198 _AI_MATKEY_TEXBLEND_BASE, type, idx);
nuclear@0 1199
nuclear@0 1200 // Blend factor
nuclear@0 1201 mat.AddProperty((float*)&sampler.mWeighting , 1,
nuclear@0 1202 _AI_MATKEY_TEXBLEND_BASE, type, idx);
nuclear@0 1203
nuclear@0 1204 // UV source index ... if we didn't resolve the mapping, it is actually just
nuclear@0 1205 // a guess but it works in most cases. We search for the frst occurence of a
nuclear@0 1206 // number in the channel name. We assume it is the zero-based index into the
nuclear@0 1207 // UV channel array of all corresponding meshes. It could also be one-based
nuclear@0 1208 // for some exporters, but we won't care of it unless someone complains about.
nuclear@0 1209 if (sampler.mUVId != UINT_MAX)
nuclear@0 1210 map = sampler.mUVId;
nuclear@0 1211 else {
nuclear@0 1212 map = -1;
nuclear@0 1213 for (std::string::const_iterator it = sampler.mUVChannel.begin();it != sampler.mUVChannel.end(); ++it){
nuclear@0 1214 if (IsNumeric(*it)) {
nuclear@0 1215 map = strtoul10(&(*it));
nuclear@0 1216 break;
nuclear@0 1217 }
nuclear@0 1218 }
nuclear@0 1219 if (-1 == map) {
nuclear@0 1220 DefaultLogger::get()->warn("Collada: unable to determine UV channel for texture");
nuclear@0 1221 map = 0;
nuclear@0 1222 }
nuclear@0 1223 }
nuclear@0 1224 mat.AddProperty(&map,1,_AI_MATKEY_UVWSRC_BASE,type,idx);
nuclear@0 1225 }
nuclear@0 1226
nuclear@0 1227 // ------------------------------------------------------------------------------------------------
nuclear@0 1228 // Fills materials from the collada material definitions
nuclear@0 1229 void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pScene*/)
nuclear@0 1230 {
nuclear@0 1231 for (std::vector<std::pair<Collada::Effect*, aiMaterial*> >::iterator it = newMats.begin(),
nuclear@0 1232 end = newMats.end(); it != end; ++it)
nuclear@0 1233 {
nuclear@0 1234 aiMaterial& mat = (aiMaterial&)*it->second;
nuclear@0 1235 Collada::Effect& effect = *it->first;
nuclear@0 1236
nuclear@0 1237 // resolve shading mode
nuclear@0 1238 int shadeMode;
nuclear@0 1239 if (effect.mFaceted) /* fixme */
nuclear@0 1240 shadeMode = aiShadingMode_Flat;
nuclear@0 1241 else {
nuclear@0 1242 switch( effect.mShadeType)
nuclear@0 1243 {
nuclear@0 1244 case Collada::Shade_Constant:
nuclear@0 1245 shadeMode = aiShadingMode_NoShading;
nuclear@0 1246 break;
nuclear@0 1247 case Collada::Shade_Lambert:
nuclear@0 1248 shadeMode = aiShadingMode_Gouraud;
nuclear@0 1249 break;
nuclear@0 1250 case Collada::Shade_Blinn:
nuclear@0 1251 shadeMode = aiShadingMode_Blinn;
nuclear@0 1252 break;
nuclear@0 1253 case Collada::Shade_Phong:
nuclear@0 1254 shadeMode = aiShadingMode_Phong;
nuclear@0 1255 break;
nuclear@0 1256
nuclear@0 1257 default:
nuclear@0 1258 DefaultLogger::get()->warn("Collada: Unrecognized shading mode, using gouraud shading");
nuclear@0 1259 shadeMode = aiShadingMode_Gouraud;
nuclear@0 1260 break;
nuclear@0 1261 }
nuclear@0 1262 }
nuclear@0 1263 mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
nuclear@0 1264
nuclear@0 1265 // double-sided?
nuclear@0 1266 shadeMode = effect.mDoubleSided;
nuclear@0 1267 mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_TWOSIDED);
nuclear@0 1268
nuclear@0 1269 // wireframe?
nuclear@0 1270 shadeMode = effect.mWireframe;
nuclear@0 1271 mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME);
nuclear@0 1272
nuclear@0 1273 // add material colors
nuclear@0 1274 mat.AddProperty( &effect.mAmbient, 1,AI_MATKEY_COLOR_AMBIENT);
nuclear@0 1275 mat.AddProperty( &effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
nuclear@0 1276 mat.AddProperty( &effect.mSpecular, 1,AI_MATKEY_COLOR_SPECULAR);
nuclear@0 1277 mat.AddProperty( &effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
nuclear@0 1278 mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT);
nuclear@0 1279 mat.AddProperty( &effect.mReflective, 1, AI_MATKEY_COLOR_REFLECTIVE);
nuclear@0 1280
nuclear@0 1281 // scalar properties
nuclear@0 1282 mat.AddProperty( &effect.mShininess, 1, AI_MATKEY_SHININESS);
nuclear@0 1283 mat.AddProperty( &effect.mReflectivity, 1, AI_MATKEY_REFLECTIVITY);
nuclear@0 1284 mat.AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI);
nuclear@0 1285
nuclear@0 1286 // transparency, a very hard one. seemingly not all files are following the
nuclear@0 1287 // specification here .. but we can trick.
nuclear@0 1288 if (effect.mTransparency >= 0.f && effect.mTransparency < 1.f) {
nuclear@0 1289 effect.mTransparency = 1.f- effect.mTransparency;
nuclear@0 1290 mat.AddProperty( &effect.mTransparency, 1, AI_MATKEY_OPACITY );
nuclear@0 1291 mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT );
nuclear@0 1292 }
nuclear@0 1293
nuclear@0 1294 // add textures, if given
nuclear@0 1295 if( !effect.mTexAmbient.mName.empty())
nuclear@0 1296 /* It is merely a lightmap */
nuclear@0 1297 AddTexture( mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP);
nuclear@0 1298
nuclear@0 1299 if( !effect.mTexEmissive.mName.empty())
nuclear@0 1300 AddTexture( mat, pParser, effect, effect.mTexEmissive, aiTextureType_EMISSIVE);
nuclear@0 1301
nuclear@0 1302 if( !effect.mTexSpecular.mName.empty())
nuclear@0 1303 AddTexture( mat, pParser, effect, effect.mTexSpecular, aiTextureType_SPECULAR);
nuclear@0 1304
nuclear@0 1305 if( !effect.mTexDiffuse.mName.empty())
nuclear@0 1306 AddTexture( mat, pParser, effect, effect.mTexDiffuse, aiTextureType_DIFFUSE);
nuclear@0 1307
nuclear@0 1308 if( !effect.mTexBump.mName.empty())
nuclear@0 1309 AddTexture( mat, pParser, effect, effect.mTexBump, aiTextureType_NORMALS);
nuclear@0 1310
nuclear@0 1311 if( !effect.mTexTransparent.mName.empty())
nuclear@0 1312 AddTexture( mat, pParser, effect, effect.mTexTransparent, aiTextureType_OPACITY);
nuclear@0 1313
nuclear@0 1314 if( !effect.mTexReflective.mName.empty())
nuclear@0 1315 AddTexture( mat, pParser, effect, effect.mTexReflective, aiTextureType_REFLECTION);
nuclear@0 1316 }
nuclear@0 1317 }
nuclear@0 1318
nuclear@0 1319 // ------------------------------------------------------------------------------------------------
nuclear@0 1320 // Constructs materials from the collada material definitions
nuclear@0 1321 void ColladaLoader::BuildMaterials( ColladaParser& pParser, aiScene* /*pScene*/)
nuclear@0 1322 {
nuclear@0 1323 newMats.reserve(pParser.mMaterialLibrary.size());
nuclear@0 1324
nuclear@0 1325 for( ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); matIt != pParser.mMaterialLibrary.end(); ++matIt)
nuclear@0 1326 {
nuclear@0 1327 const Collada::Material& material = matIt->second;
nuclear@0 1328 // a material is only a reference to an effect
nuclear@0 1329 ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find( material.mEffect);
nuclear@0 1330 if( effIt == pParser.mEffectLibrary.end())
nuclear@0 1331 continue;
nuclear@0 1332 Collada::Effect& effect = effIt->second;
nuclear@0 1333
nuclear@0 1334 // create material
nuclear@0 1335 aiMaterial* mat = new aiMaterial;
nuclear@0 1336 aiString name( matIt->first);
nuclear@0 1337 mat->AddProperty(&name,AI_MATKEY_NAME);
nuclear@0 1338
nuclear@0 1339 // store the material
nuclear@0 1340 mMaterialIndexByName[matIt->first] = newMats.size();
nuclear@0 1341 newMats.push_back( std::pair<Collada::Effect*, aiMaterial*>( &effect,mat) );
nuclear@0 1342 }
nuclear@0 1343 // ScenePreprocessor generates a default material automatically if none is there.
nuclear@0 1344 // All further code here in this loader works well without a valid material so
nuclear@0 1345 // we can safely let it to ScenePreprocessor.
nuclear@0 1346 #if 0
nuclear@0 1347 if( newMats.size() == 0)
nuclear@0 1348 {
nuclear@0 1349 aiMaterial* mat = new aiMaterial;
nuclear@0 1350 aiString name( AI_DEFAULT_MATERIAL_NAME );
nuclear@0 1351 mat->AddProperty( &name, AI_MATKEY_NAME);
nuclear@0 1352
nuclear@0 1353 const int shadeMode = aiShadingMode_Phong;
nuclear@0 1354 mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
nuclear@0 1355 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);
nuclear@0 1356 mat->AddProperty( &colAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
nuclear@0 1357 mat->AddProperty( &colDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
nuclear@0 1358 mat->AddProperty( &colSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
nuclear@0 1359 const float specExp = 5.0f;
nuclear@0 1360 mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS);
nuclear@0 1361 }
nuclear@0 1362 #endif
nuclear@0 1363 }
nuclear@0 1364
nuclear@0 1365 // ------------------------------------------------------------------------------------------------
nuclear@0 1366 // Resolves the texture name for the given effect texture entry
nuclear@0 1367 aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pParser,
nuclear@0 1368 const Collada::Effect& pEffect, const std::string& pName)
nuclear@0 1369 {
nuclear@0 1370 // recurse through the param references until we end up at an image
nuclear@0 1371 std::string name = pName;
nuclear@0 1372 while( 1)
nuclear@0 1373 {
nuclear@0 1374 // the given string is a param entry. Find it
nuclear@0 1375 Collada::Effect::ParamLibrary::const_iterator it = pEffect.mParams.find( name);
nuclear@0 1376 // if not found, we're at the end of the recursion. The resulting string should be the image ID
nuclear@0 1377 if( it == pEffect.mParams.end())
nuclear@0 1378 break;
nuclear@0 1379
nuclear@0 1380 // else recurse on
nuclear@0 1381 name = it->second.mReference;
nuclear@0 1382 }
nuclear@0 1383
nuclear@0 1384 // find the image referred by this name in the image library of the scene
nuclear@0 1385 ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find( name);
nuclear@0 1386 if( imIt == pParser.mImageLibrary.end())
nuclear@0 1387 {
nuclear@0 1388 throw DeadlyImportError( boost::str( boost::format(
nuclear@0 1389 "Collada: Unable to resolve effect texture entry \"%s\", ended up at ID \"%s\".") % pName % name));
nuclear@0 1390 }
nuclear@0 1391
nuclear@0 1392 aiString result;
nuclear@0 1393
nuclear@0 1394 // if this is an embedded texture image setup an aiTexture for it
nuclear@0 1395 if (imIt->second.mFileName.empty())
nuclear@0 1396 {
nuclear@0 1397 if (imIt->second.mImageData.empty()) {
nuclear@0 1398 throw DeadlyImportError("Collada: Invalid texture, no data or file reference given");
nuclear@0 1399 }
nuclear@0 1400
nuclear@0 1401 aiTexture* tex = new aiTexture();
nuclear@0 1402
nuclear@0 1403 // setup format hint
nuclear@0 1404 if (imIt->second.mEmbeddedFormat.length() > 3) {
nuclear@0 1405 DefaultLogger::get()->warn("Collada: texture format hint is too long, truncating to 3 characters");
nuclear@0 1406 }
nuclear@0 1407 strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3);
nuclear@0 1408
nuclear@0 1409 // and copy texture data
nuclear@0 1410 tex->mHeight = 0;
nuclear@0 1411 tex->mWidth = imIt->second.mImageData.size();
nuclear@0 1412 tex->pcData = (aiTexel*)new char[tex->mWidth];
nuclear@0 1413 memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth);
nuclear@0 1414
nuclear@0 1415 // setup texture reference string
nuclear@0 1416 result.data[0] = '*';
nuclear@0 1417 result.length = 1 + ASSIMP_itoa10(result.data+1,MAXLEN-1,mTextures.size());
nuclear@0 1418
nuclear@0 1419 // and add this texture to the list
nuclear@0 1420 mTextures.push_back(tex);
nuclear@0 1421 }
nuclear@0 1422 else
nuclear@0 1423 {
nuclear@0 1424 result.Set( imIt->second.mFileName );
nuclear@0 1425 ConvertPath(result);
nuclear@0 1426 }
nuclear@0 1427 return result;
nuclear@0 1428 }
nuclear@0 1429
nuclear@0 1430 // ------------------------------------------------------------------------------------------------
nuclear@0 1431 // Convert a path read from a collada file to the usual representation
nuclear@0 1432 void ColladaLoader::ConvertPath (aiString& ss)
nuclear@0 1433 {
nuclear@0 1434 // TODO: collada spec, p 22. Handle URI correctly.
nuclear@0 1435 // For the moment we're just stripping the file:// away to make it work.
nuclear@0 1436 // Windoes doesn't seem to be able to find stuff like
nuclear@0 1437 // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
nuclear@0 1438 if (0 == strncmp(ss.data,"file://",7))
nuclear@0 1439 {
nuclear@0 1440 ss.length -= 7;
nuclear@0 1441 memmove(ss.data,ss.data+7,ss.length);
nuclear@0 1442 ss.data[ss.length] = '\0';
nuclear@0 1443 }
nuclear@0 1444
nuclear@0 1445 // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes...
nuclear@0 1446 // I need to filter it without destroying linux paths starting with "/somewhere"
nuclear@0 1447 if( ss.data[0] == '/' && isalpha( ss.data[1]) && ss.data[2] == ':' )
nuclear@0 1448 {
nuclear@0 1449 ss.length--;
nuclear@0 1450 memmove( ss.data, ss.data+1, ss.length);
nuclear@0 1451 ss.data[ss.length] = 0;
nuclear@0 1452 }
nuclear@0 1453
nuclear@0 1454 // find and convert all %xy special chars
nuclear@0 1455 char* out = ss.data;
nuclear@0 1456 for( const char* it = ss.data; it != ss.data + ss.length; /**/ )
nuclear@0 1457 {
nuclear@0 1458 if( *it == '%' && (it + 3) < ss.data + ss.length )
nuclear@0 1459 {
nuclear@0 1460 // separate the number to avoid dragging in chars from behind into the parsing
nuclear@0 1461 char mychar[3] = { it[1], it[2], 0 };
nuclear@0 1462 size_t nbr = strtoul16( mychar);
nuclear@0 1463 it += 3;
nuclear@0 1464 *out++ = (char)(nbr & 0xFF);
nuclear@0 1465 } else
nuclear@0 1466 {
nuclear@0 1467 *out++ = *it++;
nuclear@0 1468 }
nuclear@0 1469 }
nuclear@0 1470
nuclear@0 1471 // adjust length and terminator of the shortened string
nuclear@0 1472 *out = 0;
nuclear@0 1473 ss.length = (ptrdiff_t) (out - ss.data);
nuclear@0 1474 }
nuclear@0 1475
nuclear@0 1476 // ------------------------------------------------------------------------------------------------
nuclear@0 1477 // Reads a float value from an accessor and its data array.
nuclear@0 1478 float ColladaLoader::ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const
nuclear@0 1479 {
nuclear@0 1480 // FIXME: (thom) Test for data type here in every access? For the moment, I leave this to the caller
nuclear@0 1481 size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset;
nuclear@0 1482 ai_assert( pos < pData.mValues.size());
nuclear@0 1483 return pData.mValues[pos];
nuclear@0 1484 }
nuclear@0 1485
nuclear@0 1486 // ------------------------------------------------------------------------------------------------
nuclear@0 1487 // Reads a string value from an accessor and its data array.
nuclear@0 1488 const std::string& ColladaLoader::ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const
nuclear@0 1489 {
nuclear@0 1490 size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset;
nuclear@0 1491 ai_assert( pos < pData.mStrings.size());
nuclear@0 1492 return pData.mStrings[pos];
nuclear@0 1493 }
nuclear@0 1494
nuclear@0 1495 // ------------------------------------------------------------------------------------------------
nuclear@0 1496 // Collects all nodes into the given array
nuclear@0 1497 void ColladaLoader::CollectNodes( const aiNode* pNode, std::vector<const aiNode*>& poNodes) const
nuclear@0 1498 {
nuclear@0 1499 poNodes.push_back( pNode);
nuclear@0 1500
nuclear@0 1501 for( size_t a = 0; a < pNode->mNumChildren; ++a)
nuclear@0 1502 CollectNodes( pNode->mChildren[a], poNodes);
nuclear@0 1503 }
nuclear@0 1504
nuclear@0 1505 // ------------------------------------------------------------------------------------------------
nuclear@0 1506 // Finds a node in the collada scene by the given name
nuclear@0 1507 const Collada::Node* ColladaLoader::FindNode( const Collada::Node* pNode, const std::string& pName) const
nuclear@0 1508 {
nuclear@0 1509 if( pNode->mName == pName || pNode->mID == pName)
nuclear@0 1510 return pNode;
nuclear@0 1511
nuclear@0 1512 for( size_t a = 0; a < pNode->mChildren.size(); ++a)
nuclear@0 1513 {
nuclear@0 1514 const Collada::Node* node = FindNode( pNode->mChildren[a], pName);
nuclear@0 1515 if( node)
nuclear@0 1516 return node;
nuclear@0 1517 }
nuclear@0 1518
nuclear@0 1519 return NULL;
nuclear@0 1520 }
nuclear@0 1521
nuclear@0 1522 // ------------------------------------------------------------------------------------------------
nuclear@0 1523 // Finds a node in the collada scene by the given SID
nuclear@0 1524 const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const
nuclear@0 1525 {
nuclear@0 1526 if( pNode->mSID == pSID)
nuclear@0 1527 return pNode;
nuclear@0 1528
nuclear@0 1529 for( size_t a = 0; a < pNode->mChildren.size(); ++a)
nuclear@0 1530 {
nuclear@0 1531 const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID);
nuclear@0 1532 if( node)
nuclear@0 1533 return node;
nuclear@0 1534 }
nuclear@0 1535
nuclear@0 1536 return NULL;
nuclear@0 1537 }
nuclear@0 1538
nuclear@0 1539 // ------------------------------------------------------------------------------------------------
nuclear@0 1540 // Finds a proper name for a node derived from the collada-node's properties
nuclear@0 1541 std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode) const
nuclear@0 1542 {
nuclear@0 1543 // now setup the name of the node. We take the name if not empty, otherwise the collada ID
nuclear@0 1544 // FIX: Workaround for XSI calling the instanced visual scene 'untitled' by default.
nuclear@0 1545 if (!pNode->mName.empty() && pNode->mName != "untitled")
nuclear@0 1546 return pNode->mName;
nuclear@0 1547 else if (!pNode->mID.empty())
nuclear@0 1548 return pNode->mID;
nuclear@0 1549 else if (!pNode->mSID.empty())
nuclear@0 1550 return pNode->mSID;
nuclear@0 1551 else
nuclear@0 1552 {
nuclear@0 1553 // No need to worry. Unnamed nodes are no problem at all, except
nuclear@0 1554 // if cameras or lights need to be assigned to them.
nuclear@0 1555 return boost::str( boost::format( "$ColladaAutoName$_%d") % clock());
nuclear@0 1556 }
nuclear@0 1557 }
nuclear@0 1558
nuclear@0 1559 #endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER