ColladaLoader.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. /*
  2. ---------------------------------------------------------------------------
  3. Open Asset Import Library (ASSIMP)
  4. ---------------------------------------------------------------------------
  5. Copyright (c) 2006-2008, ASSIMP Development Team
  6. All rights reserved.
  7. Redistribution and use of this software in source and binary forms,
  8. with or without modification, are permitted provided that the following
  9. conditions are met:
  10. * Redistributions of source code must retain the above
  11. copyright notice, this list of conditions and the
  12. following disclaimer.
  13. * Redistributions in binary form must reproduce the above
  14. copyright notice, this list of conditions and the
  15. following disclaimer in the documentation and/or other
  16. materials provided with the distribution.
  17. * Neither the name of the ASSIMP team, nor the names of its
  18. contributors may be used to endorse or promote products
  19. derived from this software without specific prior
  20. written permission of the ASSIMP Development Team.
  21. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. ---------------------------------------------------------------------------
  33. */
  34. /** @file Implementation of the Collada loader */
  35. #include "AssimpPCH.h"
  36. #ifndef ASSIMP_BUILD_NO_DAE_IMPORTER
  37. #include "../include/aiAnim.h"
  38. #include "ColladaLoader.h"
  39. #include "ColladaParser.h"
  40. #include "fast_atof.h"
  41. #include "ParsingUtils.h"
  42. #include "time.h"
  43. using namespace Assimp;
  44. // ------------------------------------------------------------------------------------------------
  45. // Constructor to be privately used by Importer
  46. ColladaLoader::ColladaLoader()
  47. {}
  48. // ------------------------------------------------------------------------------------------------
  49. // Destructor, private as well
  50. ColladaLoader::~ColladaLoader()
  51. {}
  52. // ------------------------------------------------------------------------------------------------
  53. // Returns whether the class can handle the format of the given file.
  54. bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
  55. {
  56. // check file extension
  57. std::string extension = GetExtension(pFile);
  58. if( extension == "dae")
  59. return true;
  60. // XML - too generic, we need to open the file and search for typical keywords
  61. if( extension == "xml" || !extension.length() || checkSig) {
  62. /* If CanRead() is called in order to check whether we
  63. * support a specific file extension in general pIOHandler
  64. * might be NULL and it's our duty to return true here.
  65. */
  66. if (!pIOHandler)return true;
  67. const char* tokens[] = {"collada"};
  68. return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
  69. }
  70. return false;
  71. }
  72. // ------------------------------------------------------------------------------------------------
  73. // Get file extension list
  74. void ColladaLoader::GetExtensionList( std::string& append)
  75. {
  76. append.append("*.dae");
  77. }
  78. // ------------------------------------------------------------------------------------------------
  79. // Imports the given file into the given scene structure.
  80. void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
  81. {
  82. mFileName = pFile;
  83. // clean all member arrays - just for safety, it should work even if we did not
  84. mMeshIndexByID.clear();
  85. mMaterialIndexByName.clear();
  86. mMeshes.clear();
  87. newMats.clear();
  88. mLights.clear();
  89. mCameras.clear();
  90. mTextures.clear();
  91. // parse the input file
  92. ColladaParser parser( pIOHandler, pFile);
  93. if( !parser.mRootNode)
  94. throw new ImportErrorException( "Collada: File came out empty. Something is wrong here.");
  95. // reserve some storage to avoid unnecessary reallocs
  96. newMats.reserve(parser.mMaterialLibrary.size()*2);
  97. mMeshes.reserve(parser.mMeshLibrary.size()*2);
  98. mCameras.reserve(parser.mCameraLibrary.size());
  99. mLights.reserve(parser.mLightLibrary.size());
  100. // create the materials first, for the meshes to find
  101. BuildMaterials( parser, pScene);
  102. // build the node hierarchy from it
  103. pScene->mRootNode = BuildHierarchy( parser, parser.mRootNode);
  104. // ... then fill the materials with the now adjusted settings
  105. FillMaterials(parser, pScene);
  106. // Convert to Z_UP, if different orientation
  107. if( parser.mUpDirection == ColladaParser::UP_X)
  108. pScene->mRootNode->mTransformation *= aiMatrix4x4(
  109. 0, -1, 0, 0,
  110. 0, 0, -1, 0,
  111. 1, 0, 0, 0,
  112. 0, 0, 0, 1);
  113. else if( parser.mUpDirection == ColladaParser::UP_Y)
  114. pScene->mRootNode->mTransformation *= aiMatrix4x4(
  115. 1, 0, 0, 0,
  116. 0, 0, -1, 0,
  117. 0, 1, 0, 0,
  118. 0, 0, 0, 1);
  119. // store all meshes
  120. StoreSceneMeshes( pScene);
  121. // store all materials
  122. StoreSceneMaterials( pScene);
  123. // store all lights
  124. StoreSceneLights( pScene);
  125. // store all cameras
  126. StoreSceneCameras( pScene);
  127. }
  128. // ------------------------------------------------------------------------------------------------
  129. // Recursively constructs a scene node for the given parser node and returns it.
  130. aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode)
  131. {
  132. // create a node for it
  133. aiNode* node = new aiNode();
  134. // now setup the name of the node. We take the name if not empty, otherwise the collada ID
  135. // FIX: Workaround for XSI calling the instanced visual scene 'untitled' by default.
  136. if (!pNode->mName.empty() && pNode->mName != "untitled")
  137. node->mName.Set(pNode->mName);
  138. else if (!pNode->mID.empty())
  139. node->mName.Set(pNode->mID);
  140. else
  141. {
  142. // No need to worry. Unnamed nodes are no problem at all, except
  143. // if cameras or lights need to be assigned to them.
  144. if (!pNode->mLights.empty() || !pNode->mCameras.empty()) {
  145. ::strcpy(node->mName.data,"$ColladaAutoName$_");
  146. node->mName.length = 17 + ASSIMP_itoa10(node->mName.data+18,MAXLEN-18,(uint32_t)clock());
  147. }
  148. }
  149. // calculate the transformation matrix for it
  150. node->mTransformation = pParser.CalculateResultTransform( pNode->mTransforms);
  151. // now resolve node instances
  152. std::vector<Collada::Node*> instances;
  153. ResolveNodeInstances(pParser,pNode,instances);
  154. // add children. first the *real* ones
  155. node->mNumChildren = pNode->mChildren.size()+instances.size();
  156. node->mChildren = new aiNode*[node->mNumChildren];
  157. unsigned int a = 0;
  158. for(; a < pNode->mChildren.size(); a++)
  159. {
  160. node->mChildren[a] = BuildHierarchy( pParser, pNode->mChildren[a]);
  161. node->mChildren[a]->mParent = node;
  162. }
  163. // ... and finally the resolved node instances
  164. for(; a < node->mNumChildren; a++)
  165. {
  166. node->mChildren[a] = BuildHierarchy( pParser, instances[a-pNode->mChildren.size()]);
  167. node->mChildren[a]->mParent = node;
  168. }
  169. // construct meshes
  170. BuildMeshesForNode( pParser, pNode, node);
  171. // construct cameras
  172. BuildCamerasForNode(pParser, pNode, node);
  173. // construct lights
  174. BuildLightsForNode(pParser, pNode, node);
  175. return node;
  176. }
  177. // ------------------------------------------------------------------------------------------------
  178. // Resolve node instances
  179. void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode,
  180. std::vector<Collada::Node*>& resolved)
  181. {
  182. // reserve enough storage
  183. resolved.reserve(pNode->mNodeInstances.size());
  184. // ... and iterate through all nodes to be instanced as children of pNode
  185. for (std::vector<Collada::NodeInstance>::const_iterator it = pNode->mNodeInstances.begin(),
  186. end = pNode->mNodeInstances.end(); it != end; ++it)
  187. {
  188. // find the corresponding node in the library
  189. ColladaParser::NodeLibrary::const_iterator fnd = pParser.mNodeLibrary.find((*it).mNode);
  190. if (fnd == pParser.mNodeLibrary.end())
  191. DefaultLogger::get()->error("Collada: Unable to resolve reference to instanced node " + (*it).mNode);
  192. else {
  193. // attach this node to the list of children
  194. resolved.push_back((*fnd).second);
  195. }
  196. }
  197. }
  198. // ------------------------------------------------------------------------------------------------
  199. // Resolve UV channels
  200. void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler,
  201. const Collada::SemanticMappingTable& table)
  202. {
  203. std::map<std::string, Collada::InputSemanticMapEntry>::const_iterator it = table.mMap.find(sampler.mUVChannel);
  204. if (it != table.mMap.end()) {
  205. if (it->second.mType != Collada::IT_Texcoord)
  206. DefaultLogger::get()->error("Collada: Unexpected effect input mapping");
  207. sampler.mUVId = it->second.mSet;
  208. }
  209. }
  210. // ------------------------------------------------------------------------------------------------
  211. // Builds lights for the given node and references them
  212. void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
  213. {
  214. BOOST_FOREACH( const Collada::LightInstance& lid, pNode->mLights)
  215. {
  216. // find the referred light
  217. ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find( lid.mLight);
  218. if( srcLightIt == pParser.mLightLibrary.end())
  219. {
  220. DefaultLogger::get()->warn("Collada: Unable to find light for ID \"" + lid.mLight + "\". Skipping.");
  221. continue;
  222. }
  223. const Collada::Light* srcLight = &srcLightIt->second;
  224. if (srcLight->mType == aiLightSource_AMBIENT) {
  225. DefaultLogger::get()->error("Collada: Skipping ambient light for the moment");
  226. continue;
  227. }
  228. // now fill our ai data structure
  229. aiLight* out = new aiLight();
  230. out->mName = pTarget->mName;
  231. out->mType = (aiLightSourceType)srcLight->mType;
  232. // collada lights point in -Z by default, rest is specified in node transform
  233. out->mDirection = aiVector3D(0.f,0.f,-1.f);
  234. out->mAttenuationConstant = srcLight->mAttConstant;
  235. out->mAttenuationLinear = srcLight->mAttLinear;
  236. out->mAttenuationQuadratic = srcLight->mAttQuadratic;
  237. // collada doesn't differenciate between these color types
  238. out->mColorDiffuse = out->mColorSpecular = out->mColorAmbient = srcLight->mColor*srcLight->mIntensity;
  239. // convert falloff angle and falloff exponent in our representation, if given
  240. if (out->mType == aiLightSource_SPOT) {
  241. out->mAngleInnerCone = AI_DEG_TO_RAD( srcLight->mFalloffAngle );
  242. // ... some extension magic. FUCKING COLLADA.
  243. if (srcLight->mOuterAngle == 10e10f)
  244. {
  245. // ... some deprecation magic. FUCKING FCOLLADA.
  246. if (srcLight->mPenumbraAngle == 10e10f)
  247. {
  248. // Need to rely on falloff_exponent. I don't know how to interpret it, so I need to guess ....
  249. // ci - inner cone angle
  250. // co - outer cone angle
  251. // fe - falloff exponent
  252. // ld - spot direction - normalized
  253. // rd - ray direction - normalized
  254. //
  255. // Formula is:
  256. // 1. (cos(acos (ld dot rd) - ci))^fe == epsilon
  257. // 2. (ld dot rd) == cos(acos(epsilon^(1/fe)) + ci)
  258. // 3. co == acos (ld dot rd)
  259. // 4. co == acos(epsilon^(1/fe)) + ci)
  260. // epsilon chosen to be 0.1
  261. out->mAngleOuterCone = AI_DEG_TO_RAD (acos(pow(0.1f,1.f/srcLight->mFalloffExponent))+
  262. srcLight->mFalloffAngle);
  263. }
  264. else {
  265. out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD( srcLight->mPenumbraAngle );
  266. if (out->mAngleOuterCone < out->mAngleInnerCone)
  267. std::swap(out->mAngleInnerCone,out->mAngleOuterCone);
  268. }
  269. }
  270. else out->mAngleOuterCone = AI_DEG_TO_RAD( srcLight->mOuterAngle );
  271. }
  272. // add to light list
  273. mLights.push_back(out);
  274. }
  275. }
  276. // ------------------------------------------------------------------------------------------------
  277. // Builds cameras for the given node and references them
  278. void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
  279. {
  280. BOOST_FOREACH( const Collada::CameraInstance& cid, pNode->mCameras)
  281. {
  282. // find the referred light
  283. ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find( cid.mCamera);
  284. if( srcCameraIt == pParser.mCameraLibrary.end())
  285. {
  286. DefaultLogger::get()->warn("Collada: Unable to find camera for ID \"" + cid.mCamera + "\". Skipping.");
  287. continue;
  288. }
  289. const Collada::Camera* srcCamera = &srcCameraIt->second;
  290. // orthographic cameras not yet supported in Assimp
  291. if (srcCamera->mOrtho) {
  292. DefaultLogger::get()->warn("Collada: Orthographic cameras are not supported.");
  293. }
  294. // now fill our ai data structure
  295. aiCamera* out = new aiCamera();
  296. out->mName = pTarget->mName;
  297. // collada cameras point in -Z by default, rest is specified in node transform
  298. out->mLookAt = aiVector3D(0.f,0.f,-1.f);
  299. // near/far z is already ok
  300. out->mClipPlaneFar = srcCamera->mZFar;
  301. out->mClipPlaneNear = srcCamera->mZNear;
  302. // ... but for the rest some values are optional
  303. // and we need to compute the others in any combination. FUCKING COLLADA.
  304. if (srcCamera->mAspect != 10e10f)
  305. out->mAspect = srcCamera->mAspect;
  306. if (srcCamera->mHorFov != 10e10f) {
  307. out->mHorizontalFOV = srcCamera->mHorFov;
  308. if (srcCamera->mVerFov != 10e10f && srcCamera->mAspect != 10e10f) {
  309. out->mAspect = srcCamera->mHorFov/srcCamera->mVerFov;
  310. }
  311. }
  312. else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) {
  313. out->mHorizontalFOV = srcCamera->mAspect*srcCamera->mVerFov;
  314. }
  315. // Collada uses degrees, we use radians
  316. out->mHorizontalFOV = AI_DEG_TO_RAD(out->mHorizontalFOV);
  317. // add to camera list
  318. mCameras.push_back(out);
  319. }
  320. }
  321. // ------------------------------------------------------------------------------------------------
  322. // Builds meshes for the given node and references them
  323. void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
  324. {
  325. // accumulated mesh references by this node
  326. std::vector<size_t> newMeshRefs;
  327. newMeshRefs.reserve(pNode->mMeshes.size());
  328. // add a mesh for each subgroup in each collada mesh
  329. BOOST_FOREACH( const Collada::MeshInstance& mid, pNode->mMeshes)
  330. {
  331. // find the referred mesh
  332. ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMesh);
  333. if( srcMeshIt == pParser.mMeshLibrary.end())
  334. {
  335. DefaultLogger::get()->warn( boost::str( boost::format( "Collada: Unable to find geometry for ID \"%s\". Skipping.") % mid.mMesh));
  336. continue;
  337. }
  338. const Collada::Mesh* srcMesh = srcMeshIt->second;
  339. // build a mesh for each of its subgroups
  340. size_t vertexStart = 0, faceStart = 0;
  341. for( size_t sm = 0; sm < srcMesh->mSubMeshes.size(); ++sm)
  342. {
  343. const Collada::SubMesh& submesh = srcMesh->mSubMeshes[sm];
  344. if( submesh.mNumFaces == 0)
  345. continue;
  346. // find material assigned to this submesh
  347. std::map<std::string, Collada::SemanticMappingTable >::const_iterator meshMatIt = mid.mMaterials.find( submesh.mMaterial);
  348. const Collada::SemanticMappingTable* table;
  349. if( meshMatIt != mid.mMaterials.end())
  350. table = &meshMatIt->second;
  351. else {
  352. table = NULL;
  353. DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup \"%s\" in geometry \"%s\".") % submesh.mMaterial % mid.mMesh));
  354. }
  355. const std::string& meshMaterial = table ? table->mMatName : "";
  356. // OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table
  357. // given. The only mapping stuff which we do actually support is the UV channel.
  358. std::map<std::string, size_t>::const_iterator matIt = mMaterialIndexByName.find( meshMaterial);
  359. unsigned int matIdx;
  360. if( matIt != mMaterialIndexByName.end())
  361. matIdx = matIt->second;
  362. else
  363. matIdx = 0;
  364. if (table && !table->mMap.empty() ) {
  365. std::pair<Collada::Effect*, aiMaterial*>& mat = newMats[matIdx];
  366. // Iterate through all texture channels assigned to the effect and
  367. // check whether we have mapping information for it.
  368. ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table);
  369. ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table);
  370. ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table);
  371. ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table);
  372. ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent,*table);
  373. ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table);
  374. }
  375. // built lookup index of the Mesh-Submesh-Material combination
  376. ColladaMeshIndex index( mid.mMesh, sm, meshMaterial);
  377. // if we already have the mesh at the library, just add its index to the node's array
  378. std::map<ColladaMeshIndex, size_t>::const_iterator dstMeshIt = mMeshIndexByID.find( index);
  379. if( dstMeshIt != mMeshIndexByID.end())
  380. {
  381. newMeshRefs.push_back( dstMeshIt->second);
  382. } else
  383. {
  384. // else we have to add the mesh to the collection and store its newly assigned index at the node
  385. aiMesh* dstMesh = new aiMesh;
  386. // count the vertices addressed by its faces
  387. const size_t numVertices = std::accumulate( srcMesh->mFaceSize.begin() + faceStart,
  388. srcMesh->mFaceSize.begin() + faceStart + submesh.mNumFaces, 0);
  389. // copy positions
  390. dstMesh->mNumVertices = numVertices;
  391. dstMesh->mVertices = new aiVector3D[numVertices];
  392. std::copy( srcMesh->mPositions.begin() + vertexStart, srcMesh->mPositions.begin() +
  393. vertexStart + numVertices, dstMesh->mVertices);
  394. // normals, if given. HACK: (thom) Due to the fucking Collada spec we never
  395. // know if we have the same number of normals as there are positions. So we
  396. // also ignore any vertex attribute if it has a different count
  397. if( srcMesh->mNormals.size() == srcMesh->mPositions.size())
  398. {
  399. dstMesh->mNormals = new aiVector3D[numVertices];
  400. std::copy( srcMesh->mNormals.begin() + vertexStart, srcMesh->mNormals.begin() +
  401. vertexStart + numVertices, dstMesh->mNormals);
  402. }
  403. // tangents, if given.
  404. if( srcMesh->mTangents.size() == srcMesh->mPositions.size())
  405. {
  406. dstMesh->mTangents = new aiVector3D[numVertices];
  407. std::copy( srcMesh->mTangents.begin() + vertexStart, srcMesh->mTangents.begin() +
  408. vertexStart + numVertices, dstMesh->mTangents);
  409. }
  410. // bitangents, if given.
  411. if( srcMesh->mBitangents.size() == srcMesh->mPositions.size())
  412. {
  413. dstMesh->mBitangents = new aiVector3D[numVertices];
  414. std::copy( srcMesh->mBitangents.begin() + vertexStart, srcMesh->mBitangents.begin() +
  415. vertexStart + numVertices, dstMesh->mBitangents);
  416. }
  417. // same for texturecoords, as many as we have
  418. for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
  419. {
  420. if( srcMesh->mTexCoords[a].size() == srcMesh->mPositions.size())
  421. {
  422. dstMesh->mTextureCoords[a] = new aiVector3D[numVertices];
  423. for( size_t b = 0; b < numVertices; ++b)
  424. dstMesh->mTextureCoords[a][b] = srcMesh->mTexCoords[a][vertexStart+b];
  425. dstMesh->mNumUVComponents[a] = srcMesh->mNumUVComponents[a];
  426. }
  427. }
  428. // same for vertex colors, as many as we have
  429. for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
  430. {
  431. if( srcMesh->mColors[a].size() == srcMesh->mPositions.size())
  432. {
  433. dstMesh->mColors[a] = new aiColor4D[numVertices];
  434. std::copy( srcMesh->mColors[a].begin() + vertexStart, srcMesh->mColors[a].begin() + vertexStart + numVertices, dstMesh->mColors[a]);
  435. }
  436. }
  437. // create faces. Due to the fact that each face uses unique vertices, we can simply count up on each vertex
  438. size_t vertex = 0;
  439. dstMesh->mNumFaces = submesh.mNumFaces;
  440. dstMesh->mFaces = new aiFace[dstMesh->mNumFaces];
  441. for( size_t a = 0; a < dstMesh->mNumFaces; ++a)
  442. {
  443. size_t s = srcMesh->mFaceSize[ faceStart + a];
  444. aiFace& face = dstMesh->mFaces[a];
  445. face.mNumIndices = s;
  446. face.mIndices = new unsigned int[s];
  447. for( size_t b = 0; b < s; ++b)
  448. face.mIndices[b] = vertex++;
  449. }
  450. // store the mesh, and store its new index in the node
  451. newMeshRefs.push_back( mMeshes.size());
  452. mMeshIndexByID[index] = mMeshes.size();
  453. mMeshes.push_back( dstMesh);
  454. vertexStart += numVertices; faceStart += submesh.mNumFaces;
  455. // assign the material index
  456. dstMesh->mMaterialIndex = matIdx;
  457. }
  458. }
  459. }
  460. // now place all mesh references we gathered in the target node
  461. pTarget->mNumMeshes = newMeshRefs.size();
  462. if( newMeshRefs.size())
  463. {
  464. pTarget->mMeshes = new unsigned int[pTarget->mNumMeshes];
  465. std::copy( newMeshRefs.begin(), newMeshRefs.end(), pTarget->mMeshes);
  466. }
  467. }
  468. // ------------------------------------------------------------------------------------------------
  469. // Stores all meshes in the given scene
  470. void ColladaLoader::StoreSceneMeshes( aiScene* pScene)
  471. {
  472. pScene->mNumMeshes = mMeshes.size();
  473. if( mMeshes.size() > 0)
  474. {
  475. pScene->mMeshes = new aiMesh*[mMeshes.size()];
  476. std::copy( mMeshes.begin(), mMeshes.end(), pScene->mMeshes);
  477. }
  478. mMeshes.clear();
  479. }
  480. // ------------------------------------------------------------------------------------------------
  481. // Stores all cameras in the given scene
  482. void ColladaLoader::StoreSceneCameras( aiScene* pScene)
  483. {
  484. pScene->mNumCameras = mCameras.size();
  485. if( mCameras.size() > 0)
  486. {
  487. pScene->mCameras = new aiCamera*[mCameras.size()];
  488. std::copy( mCameras.begin(), mCameras.end(), pScene->mCameras);
  489. }
  490. mCameras.clear();
  491. }
  492. // ------------------------------------------------------------------------------------------------
  493. // Stores all lights in the given scene
  494. void ColladaLoader::StoreSceneLights( aiScene* pScene)
  495. {
  496. pScene->mNumLights = mLights.size();
  497. if( mLights.size() > 0)
  498. {
  499. pScene->mLights = new aiLight*[mLights.size()];
  500. std::copy( mLights.begin(), mLights.end(), pScene->mLights);
  501. }
  502. mLights.clear();
  503. }
  504. // ------------------------------------------------------------------------------------------------
  505. // Stores all textures in the given scene
  506. void ColladaLoader::StoreSceneTextures( aiScene* pScene)
  507. {
  508. pScene->mNumTextures = mTextures.size();
  509. if( mTextures.size() > 0)
  510. {
  511. pScene->mTextures = new aiTexture*[mTextures.size()];
  512. std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures);
  513. }
  514. mTextures.clear();
  515. }
  516. // ------------------------------------------------------------------------------------------------
  517. // Stores all materials in the given scene
  518. void ColladaLoader::StoreSceneMaterials( aiScene* pScene)
  519. {
  520. pScene->mNumMaterials = newMats.size();
  521. pScene->mMaterials = new aiMaterial*[newMats.size()];
  522. for (unsigned int i = 0; i < newMats.size();++i)
  523. pScene->mMaterials[i] = newMats[i].second;
  524. newMats.clear();
  525. }
  526. // ------------------------------------------------------------------------------------------------
  527. // Add a texture to a material structure
  528. void ColladaLoader::AddTexture ( Assimp::MaterialHelper& mat, const ColladaParser& pParser,
  529. const Collada::Effect& effect,
  530. const Collada::Sampler& sampler,
  531. aiTextureType type, unsigned int idx)
  532. {
  533. // first of all, basic file name
  534. mat.AddProperty( &FindFilenameForEffectTexture( pParser, effect, sampler.mName),
  535. _AI_MATKEY_TEXTURE_BASE,type,idx);
  536. // mapping mode
  537. int map = aiTextureMapMode_Clamp;
  538. if (sampler.mWrapU)
  539. map = aiTextureMapMode_Wrap;
  540. if (sampler.mWrapU && sampler.mMirrorU)
  541. map = aiTextureMapMode_Mirror;
  542. mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_U_BASE, type, idx);
  543. map = aiTextureMapMode_Clamp;
  544. if (sampler.mWrapV)
  545. map = aiTextureMapMode_Wrap;
  546. if (sampler.mWrapV && sampler.mMirrorV)
  547. map = aiTextureMapMode_Mirror;
  548. mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_V_BASE, type, idx);
  549. // UV transformation
  550. mat.AddProperty(&sampler.mTransform, 1,
  551. _AI_MATKEY_UVTRANSFORM_BASE, type, idx);
  552. // Blend mode
  553. mat.AddProperty((int*)&sampler.mOp , 1,
  554. _AI_MATKEY_TEXBLEND_BASE, type, idx);
  555. // Blend factor
  556. mat.AddProperty((float*)&sampler.mWeighting , 1,
  557. _AI_MATKEY_TEXBLEND_BASE, type, idx);
  558. // UV source index ... if we didn't resolve the mapping it is actually just
  559. // a guess but it works in most cases. We search for the frst occurence of a
  560. // number in the channel name. We assume it is the zero-based index into the
  561. // UV channel array of all corresponding meshes.
  562. if (sampler.mUVId != 0xffffffff)
  563. map = sampler.mUVId;
  564. else {
  565. map = 0xffffffff;
  566. for (std::string::const_iterator it = sampler.mUVChannel.begin();
  567. it != sampler.mUVChannel.end(); ++it)
  568. {
  569. if (IsNumeric(*it)) {
  570. map = strtol10(&(*it));
  571. break;
  572. }
  573. }
  574. if (0xffffffff == map) {
  575. DefaultLogger::get()->warn("Collada: unable to determine UV channel for texture");
  576. map = 0;
  577. }
  578. }
  579. mat.AddProperty(&map,1,_AI_MATKEY_UVWSRC_BASE,type,idx);
  580. }
  581. // ------------------------------------------------------------------------------------------------
  582. // Fills materials from the collada material definitions
  583. void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* pScene)
  584. {
  585. for (std::vector<std::pair<Collada::Effect*, aiMaterial*> >::iterator it = newMats.begin(),
  586. end = newMats.end(); it != end; ++it)
  587. {
  588. MaterialHelper& mat = (MaterialHelper&)*it->second;
  589. Collada::Effect& effect = *it->first;
  590. // resolve shading mode
  591. int shadeMode;
  592. if (effect.mFaceted) /* fixme */
  593. shadeMode = aiShadingMode_Flat;
  594. else {
  595. switch( effect.mShadeType)
  596. {
  597. case Collada::Shade_Constant:
  598. shadeMode = aiShadingMode_NoShading;
  599. break;
  600. case Collada::Shade_Lambert:
  601. shadeMode = aiShadingMode_Gouraud;
  602. break;
  603. case Collada::Shade_Blinn:
  604. shadeMode = aiShadingMode_Blinn;
  605. break;
  606. case Collada::Shade_Phong:
  607. shadeMode = aiShadingMode_Phong;
  608. break;
  609. default:
  610. DefaultLogger::get()->warn("Collada: Unrecognized shading mode, using gouraud shading");
  611. shadeMode = aiShadingMode_Gouraud;
  612. break;
  613. }
  614. }
  615. mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
  616. // double-sided?
  617. shadeMode = effect.mDoubleSided;
  618. mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_TWOSIDED);
  619. // wireframe?
  620. shadeMode = effect.mWireframe;
  621. mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME);
  622. // add material colors
  623. mat.AddProperty( &effect.mAmbient, 1,AI_MATKEY_COLOR_AMBIENT);
  624. mat.AddProperty( &effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
  625. mat.AddProperty( &effect.mSpecular, 1,AI_MATKEY_COLOR_SPECULAR);
  626. mat.AddProperty( &effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
  627. mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT);
  628. mat.AddProperty( &effect.mReflective, 1, AI_MATKEY_COLOR_REFLECTIVE);
  629. // scalar properties
  630. mat.AddProperty( &effect.mShininess, 1, AI_MATKEY_SHININESS);
  631. mat.AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI);
  632. // add textures, if given
  633. if( !effect.mTexAmbient.mName.empty())
  634. /* It is merely a lightmap */
  635. AddTexture( mat, pParser, effect, effect.mTexAmbient,aiTextureType_LIGHTMAP);
  636. if( !effect.mTexEmissive.mName.empty())
  637. AddTexture( mat, pParser, effect, effect.mTexEmissive,aiTextureType_EMISSIVE);
  638. if( !effect.mTexSpecular.mName.empty())
  639. AddTexture( mat, pParser, effect, effect.mTexSpecular,aiTextureType_SPECULAR);
  640. if( !effect.mTexDiffuse.mName.empty())
  641. AddTexture( mat, pParser, effect, effect.mTexDiffuse,aiTextureType_DIFFUSE);
  642. if( !effect.mTexBump.mName.empty())
  643. AddTexture( mat, pParser, effect, effect.mTexBump,aiTextureType_HEIGHT);
  644. if( !effect.mTexTransparent.mName.empty())
  645. AddTexture( mat, pParser, effect, effect.mTexBump,aiTextureType_OPACITY);
  646. if( !effect.mTexReflective.mName.empty())
  647. AddTexture( mat, pParser, effect, effect.mTexReflective,aiTextureType_REFLECTION);
  648. }
  649. }
  650. // ------------------------------------------------------------------------------------------------
  651. // Constructs materials from the collada material definitions
  652. void ColladaLoader::BuildMaterials( const ColladaParser& pParser, aiScene* pScene)
  653. {
  654. newMats.reserve(pParser.mMaterialLibrary.size());
  655. for( ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); matIt != pParser.mMaterialLibrary.end(); ++matIt)
  656. {
  657. const Collada::Material& material = matIt->second;
  658. // a material is only a reference to an effect
  659. ColladaParser::EffectLibrary::const_iterator effIt = pParser.mEffectLibrary.find( material.mEffect);
  660. if( effIt == pParser.mEffectLibrary.end())
  661. continue;
  662. const Collada::Effect& effect = effIt->second;
  663. // create material
  664. Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
  665. aiString name( matIt->first);
  666. mat->AddProperty(&name,AI_MATKEY_NAME);
  667. // MEGA SUPER MONSTER HACK by Alex ... It's all my fault, yes.
  668. // We store the reference to the effect in the material and
  669. // return ... we'll add the actual material properties later
  670. // after we processed all meshes. During mesh processing,
  671. // we evaluate vertex input mappings. Afterwards we should be
  672. // able to correctly setup source UV channels for textures.
  673. // ... moved to ColladaLoader::FillMaterials()
  674. // *duck*
  675. // store the material
  676. mMaterialIndexByName[matIt->first] = newMats.size();
  677. newMats.push_back( std::pair<Collada::Effect*, aiMaterial*>(const_cast<Collada::Effect*>(&effect),mat) );
  678. }
  679. // store a dummy material if none were given
  680. if( newMats.size() == 0)
  681. {
  682. Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
  683. aiString name( AI_DEFAULT_MATERIAL_NAME );
  684. mat->AddProperty( &name, AI_MATKEY_NAME);
  685. const int shadeMode = aiShadingMode_Phong;
  686. mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
  687. 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);
  688. mat->AddProperty( &colAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
  689. mat->AddProperty( &colDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
  690. mat->AddProperty( &colSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
  691. const float specExp = 5.0f;
  692. mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS);
  693. }
  694. }
  695. // ------------------------------------------------------------------------------------------------
  696. // Resolves the texture name for the given effect texture entry
  697. const aiString& ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pParser,
  698. const Collada::Effect& pEffect, const std::string& pName)
  699. {
  700. // recurse through the param references until we end up at an image
  701. std::string name = pName;
  702. while( 1)
  703. {
  704. // the given string is a param entry. Find it
  705. Collada::Effect::ParamLibrary::const_iterator it = pEffect.mParams.find( name);
  706. // if not found, we're at the end of the recursion. The resulting string should be the image ID
  707. if( it == pEffect.mParams.end())
  708. break;
  709. // else recurse on
  710. name = it->second.mReference;
  711. }
  712. // find the image referred by this name in the image library of the scene
  713. ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find( name);
  714. if( imIt == pParser.mImageLibrary.end()) {
  715. throw new ImportErrorException( boost::str( boost::format(
  716. "Collada: Unable to resolve effect texture entry \"%s\", ended up at ID \"%s\".") % pName % name));
  717. }
  718. static aiString result;
  719. // if this is an embedded texture image setup an aiTexture for it
  720. if (imIt->second.mFileName.empty()) {
  721. if (imIt->second.mImageData.empty()) {
  722. throw new ImportErrorException("Collada: Invalid texture, no data or file reference given");
  723. }
  724. aiTexture* tex = new aiTexture();
  725. // setup format hint
  726. if (imIt->second.mEmbeddedFormat.length() > 3) {
  727. DefaultLogger::get()->warn("Collada: texture format hint is too long, truncating to 3 characters");
  728. }
  729. ::strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3);
  730. // and copy texture data
  731. tex->mHeight = 0;
  732. tex->mWidth = imIt->second.mImageData.size();
  733. tex->pcData = (aiTexel*)new char[tex->mWidth];
  734. ::memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth);
  735. // setup texture reference string
  736. result.data[0] = '*';
  737. result.length = 1 + ASSIMP_itoa10(result.data+1,MAXLEN-1,mTextures.size());
  738. // and add this texture to the list
  739. mTextures.push_back(tex);
  740. }
  741. else {
  742. result.Set( imIt->second.mFileName );
  743. ConvertPath(result);
  744. }
  745. return result;
  746. }
  747. // ------------------------------------------------------------------------------------------------
  748. // Convert a path read from a collada file to the usual representation
  749. void ColladaLoader::ConvertPath (aiString& ss)
  750. {
  751. // TODO: collada spec, p 22. Handle URI correctly.
  752. // For the moment we're just stripping the file:// away to make it work.
  753. // Windoes doesn't seem to be able to find stuff like
  754. // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
  755. if (0 == ::strncmp(ss.data,"file://",7))
  756. {
  757. ss.length -= 7;
  758. ::memmove(ss.data,ss.data+7,ss.length);
  759. ss.data[ss.length] = '\0';
  760. }
  761. }
  762. #endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER