ColladaLoader.cpp 33 KB

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