colladaUtils.h 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifndef _COLLADA_UTILS_H_
  23. #define _COLLADA_UTILS_H_
  24. #ifdef _MSC_VER
  25. #pragma warning(disable : 4786) // disable warning about long debug symbol names
  26. #pragma warning(disable : 4355) // disable "'this' : used in base member initializer list" warnings
  27. #endif
  28. #ifndef _MMATRIX_H_
  29. #include "math/mMatrix.h"
  30. #endif
  31. #ifndef _MQUAT_H_
  32. #include "math/mQuat.h"
  33. #endif
  34. #ifndef _TVECTOR_H_
  35. #include "core/util/tVector.h"
  36. #endif
  37. #ifndef _TSSHAPE_LOADER_H_
  38. #include "ts/loader/tsShapeLoader.h"
  39. #endif
  40. #ifndef _OPTIMIZEDPOLYLIST_H_
  41. #include "collision/optimizedPolyList.h"
  42. #endif
  43. #ifndef TINYXML2_INCLUDED
  44. #include "tinyxml2.h"
  45. #endif
  46. #ifndef _CONSOLE_H_
  47. #include "console/console.h"
  48. #endif
  49. #ifndef _TSSHAPEINSTANCE_H_
  50. #include "ts/tsShapeInstance.h"
  51. #endif
  52. #include "platform/tmm_off.h"
  53. #include "dae.h"
  54. #include "dae/daeErrorHandler.h"
  55. #include "dae/domAny.h"
  56. #include "dom/domProfile_COMMON.h"
  57. #include "dom/domMaterial.h"
  58. #include "dom/domGeometry.h"
  59. #include "dom/domMorph.h"
  60. #include "dom/domNode.h"
  61. #include "dom/domCOLLADA.h"
  62. #include "platform/tmm_on.h"
  63. #include "core/strings/findMatch.h"
  64. namespace ColladaUtils
  65. {
  66. struct ImportOptions
  67. {
  68. enum eLodType
  69. {
  70. DetectDTS = 0,
  71. SingleSize,
  72. TrailingNumber,
  73. NumLodTypes
  74. };
  75. enum eAnimTimingType
  76. {
  77. FrameCount = 0,
  78. Seconds = 1,
  79. Milliseconds = 1000
  80. };
  81. domUpAxisType upAxis; // Override for the collada <up_axis> element
  82. F32 unit; // Override for the collada <unit> element
  83. eLodType lodType; // LOD type option
  84. S32 singleDetailSize; // Detail size for all meshes in the model
  85. String matNamePrefix; // Prefix to apply to collada material names
  86. String alwaysImport; // List of node names (with wildcards) to import, even if in the neverImport list
  87. String neverImport; // List of node names (with wildcards) to ignore on loading
  88. String alwaysImportMesh; // List of mesh names (with wildcards) to import, even if in the neverImportMesh list
  89. String neverImportMesh; // List of mesh names (with wildcards) to ignore on loading
  90. String neverImportMat; // List of material names (with wildcards) to ignore on loading
  91. bool ignoreNodeScale; // Ignore <scale> elements in <node>s
  92. bool adjustCenter; // Translate model so origin is at the center
  93. bool adjustFloor; // Translate model so origin is at the bottom
  94. bool forceUpdateMaterials; // Force update of materials.tscript
  95. bool useDiffuseNames; // Use diffuse texture as the material name
  96. // Assimp specific preprocess import options
  97. bool convertLeftHanded; // Convert to left handed coordinate system.
  98. bool calcTangentSpace; // Calculate tangents and bitangents, if possible.
  99. bool genUVCoords; // Convert spherical, cylindrical, box and planar mapping to proper UVs.
  100. bool transformUVCoords; // Preprocess UV transformations (scaling, translation ...)
  101. bool flipUVCoords; // This step flips all UV coordinates along the y-axis and adjusts material settings
  102. // and bitangents accordingly.\nAssimp uses TL(0,0):BR(1,1). T3D uses TL(0,1):BR(1,0).
  103. bool findInstances; // Search for instanced meshes and remove them by references to one master.
  104. bool limitBoneWeights; // Limit bone weights to 4 per vertex.
  105. bool joinIdenticalVerts; // Identifies and joins identical vertex data sets within all imported meshes.
  106. bool reverseWindingOrder; // This step adjusts the output face winding order to be clockwise. The default face winding order is counter clockwise.
  107. bool invertNormals; // Reverse the normal vector direction for all normals.
  108. bool removeRedundantMats; // Removes redundant materials.
  109. eAnimTimingType animTiming; // How to import timing data as frames, seconds or milliseconds
  110. S32 animFPS; // FPS value to use if timing is set in frames and the animations does not have an fps set
  111. F32 formatScaleFactor; // Scale factor applied to convert the shape format default unit to meters
  112. ImportOptions()
  113. {
  114. reset();
  115. }
  116. void reset()
  117. {
  118. upAxis = UPAXISTYPE_COUNT;
  119. unit = -1.0f;
  120. lodType = TrailingNumber;
  121. singleDetailSize = 2;
  122. matNamePrefix = "";
  123. alwaysImport = "";
  124. neverImport = String(Con::getVariable("$TSShapeConstructor::neverImport"));
  125. alwaysImportMesh = "";
  126. neverImportMesh = String(Con::getVariable("$TSShapeConstructor::neverImportMesh"));
  127. neverImportMat = String(Con::getVariable("$TSShapeConstructor::neverImportMat"));
  128. ignoreNodeScale = false;
  129. adjustCenter = false;
  130. adjustFloor = false;
  131. forceUpdateMaterials = false;
  132. useDiffuseNames = false;
  133. convertLeftHanded = false;
  134. calcTangentSpace = false;
  135. genUVCoords = false;
  136. transformUVCoords = false;
  137. flipUVCoords = true;
  138. findInstances = false;
  139. limitBoneWeights = false;
  140. joinIdenticalVerts = true;
  141. reverseWindingOrder = true;
  142. invertNormals = false;
  143. removeRedundantMats = true;
  144. animTiming = Seconds;
  145. animFPS = 30;
  146. formatScaleFactor = 1.0f;
  147. }
  148. };
  149. ImportOptions& getOptions();
  150. struct ExportData
  151. {
  152. struct detailLevel
  153. {
  154. OptimizedPolyList mesh;
  155. S32 size;
  156. Map<int, int> materialRefList;
  157. };
  158. struct meshLODData
  159. {
  160. Vector<detailLevel> meshDetailLevels;
  161. TSShapeInstance* shapeInst;
  162. MatrixF meshTransform;
  163. SceneObject* originatingObject;
  164. Point3F scale;
  165. S32 hasDetailLevel(S32 size)
  166. {
  167. for (U32 i = 0; i < meshDetailLevels.size(); ++i)
  168. {
  169. U32 mdlSize = meshDetailLevels[i].size;
  170. if (mdlSize == size)
  171. return i;
  172. }
  173. return -1;
  174. }
  175. meshLODData() : shapeInst(nullptr), meshTransform(true), originatingObject(nullptr), scale(0)
  176. {}
  177. };
  178. struct colMesh
  179. {
  180. OptimizedPolyList mesh;
  181. String colMeshName;
  182. };
  183. Vector<detailLevel> detailLevels;
  184. Vector<meshLODData> meshData;
  185. Vector<colMesh> colMeshes;
  186. Vector<BaseMatInstance*> materials;
  187. void processData();
  188. S32 hasDetailLevel(U32 dl)
  189. {
  190. for (U32 i = 0; i < detailLevels.size(); i++)
  191. {
  192. if (detailLevels[i].size == dl)
  193. return i;
  194. }
  195. return -1;
  196. }
  197. S32 hasMaterialInstance(BaseMatInstance* matInst)
  198. {
  199. for (U32 i = 0; i < materials.size(); i++)
  200. {
  201. if (materials[i] == matInst)
  202. return i;
  203. }
  204. return -1;
  205. }
  206. S32 numberOfDetailLevels()
  207. {
  208. Vector<S32> detailLevelIdxs;
  209. for (U32 i = 0; i < meshData.size(); ++i)
  210. {
  211. for (U32 d = 0; d < meshData[i].meshDetailLevels.size(); ++d)
  212. {
  213. detailLevelIdxs.push_back_unique(meshData[i].meshDetailLevels[d].size);
  214. }
  215. }
  216. return detailLevelIdxs.size();
  217. }
  218. static S32 _Sort(const S32 *p1, const S32 *p2)
  219. {
  220. S32 e1 = (*p1);
  221. S32 e2 = (*p2);
  222. if (e1 > e2)
  223. return 1;
  224. else if (e1 < e2)
  225. return -1;
  226. return 0;
  227. }
  228. S32 getDetailLevelSize(U32 detailIdx)
  229. {
  230. Vector<S32> detailLevelIdxs;
  231. for (U32 i = 0; i < meshData.size(); ++i)
  232. {
  233. for (U32 d = 0; d < meshData[i].meshDetailLevels.size(); ++d)
  234. {
  235. S32 mdlSize = meshData[i].meshDetailLevels[d].size;
  236. detailLevelIdxs.push_back_unique(mdlSize);
  237. }
  238. }
  239. if (detailIdx >= detailLevelIdxs.size())
  240. return -1;
  241. detailLevelIdxs.sort(&_Sort);
  242. return detailLevelIdxs[detailIdx];
  243. }
  244. };
  245. void convertTransform(MatrixF& m);
  246. void collapsePath(std::string& path);
  247. // Apply the set of Collada conditioners (suited for loading Collada models into Torque)
  248. void applyConditioners(domCOLLADA* root);
  249. const domProfile_COMMON* findEffectCommonProfile(const domEffect* effect);
  250. const domCommon_color_or_texture_type_complexType* findEffectDiffuse(const domEffect* effect);
  251. const domCommon_color_or_texture_type_complexType* findEffectSpecular(const domEffect* effect);
  252. const domFx_sampler2D_common_complexType* getTextureSampler(const domEffect* effect, const domCommon_color_or_texture_type_complexType* texture);
  253. String getSamplerImagePath(const domEffect* effect, const domFx_sampler2D_common_complexType* sampler2D);
  254. String resolveImagePath(const domImage* image);
  255. // Collada export helper functions
  256. Torque::Path findTexture(const Torque::Path& diffuseMap);
  257. void exportColladaHeader(tinyxml2::XMLElement* rootNode);
  258. void exportColladaMaterials(tinyxml2::XMLElement* rootNode, const OptimizedPolyList& mesh, Vector<String>& matNames, const Torque::Path& colladaFile);
  259. void exportColladaTriangles(tinyxml2::XMLElement* meshNode, const OptimizedPolyList& mesh, const String& meshName, const Vector<String>& matNames);
  260. void exportColladaMesh(tinyxml2::XMLElement* rootNode, const OptimizedPolyList& mesh, const String& meshName, const Vector<String>& matNames);
  261. void exportColladaScene(tinyxml2::XMLElement* rootNode, const String& meshName, const Vector<String>& matNames);
  262. void exportColladaMaterials(tinyxml2::XMLElement* rootNode, const ExportData& exportData, const Torque::Path& colladaFile);
  263. void exportColladaMesh(tinyxml2::XMLElement* rootNode, const ExportData& exportData, const String& meshName);
  264. void exportColladaCollisionTriangles(tinyxml2::XMLElement* meshNode, const ExportData& exportData, const U32 collisionIdx);
  265. void exportColladaTriangles(tinyxml2::XMLElement* meshNode, const ExportData& exportData, const U32 detailLevel, const String& meshName);
  266. void exportColladaScene(tinyxml2::XMLElement* rootNode, const ExportData& exportData, const String& meshName);
  267. // Export an OptimizedPolyList to a simple Collada file
  268. void exportToCollada(const Torque::Path& colladaFile, const OptimizedPolyList& mesh, const String& meshName = String::EmptyString);
  269. void exportToCollada(const Torque::Path& colladaFile, const ExportData& exportData);
  270. };
  271. //-----------------------------------------------------------------------------
  272. // Helper Classes
  273. //
  274. // The Collada DOM uses a different class for each XML element, and there is very
  275. // little class inheritance, even though many elements have the same attributes
  276. // and children. This makes the DOM a bit ugly to work with, and the following
  277. // templates attempt to make this situation a bit nicer by providing a common way
  278. // to access common elements, while retaining the strong typing of the DOM classes.
  279. //-----------------------------------------------------------------------------
  280. /// Convert from the Collada transform types to a Torque MatrixF
  281. template<class T> inline MatrixF vecToMatrixF(const domListOfFloats& vec) { return MatrixF(true); }
  282. /// Collada <translate>: [x_translate, y_translate, z_translate]
  283. template<> inline MatrixF vecToMatrixF<domTranslate>(const domListOfFloats& vec)
  284. {
  285. MatrixF mat(true);
  286. mat.setPosition(Point3F(vec[0], vec[1], vec[2]));
  287. return mat;
  288. }
  289. /// Collada <scale>: [x_scale, y_scale, z_scale]
  290. template<> inline MatrixF vecToMatrixF<domScale>(const domListOfFloats& vec)
  291. {
  292. MatrixF mat(true);
  293. mat.scale(Point3F(vec[0], vec[1], vec[2]));
  294. return mat;
  295. }
  296. /// Collada <rotate>: [rotation_axis, angle_in_degrees]
  297. template<> inline MatrixF vecToMatrixF<domRotate>(const domListOfFloats& vec)
  298. {
  299. AngAxisF aaxis(Point3F(vec[0], vec[1], vec[2]), -(vec[3] * M_PI) / 180.0f);
  300. MatrixF mat(true);
  301. aaxis.setMatrix(&mat);
  302. return mat;
  303. }
  304. /// Collada <matrix>: same form as TGE (woohoo!)
  305. template<> inline MatrixF vecToMatrixF<domMatrix>(const domListOfFloats& vec)
  306. {
  307. MatrixF mat;
  308. for (S32 i = 0; i < 16; i++)
  309. mat[i] = vec[i];
  310. return mat;
  311. }
  312. /// Collada <skew>: [angle_in_degrees, rotation_axis, translation_axis]
  313. /// skew transform code adapted from GMANMatrix4 implementation
  314. template<> inline MatrixF vecToMatrixF<domSkew>(const domListOfFloats& vec)
  315. {
  316. F32 angle = -(vec[0] * M_PI) / 180.0f;
  317. Point3F rotAxis(vec[1], vec[2], vec[3]);
  318. Point3F transAxis(vec[4], vec[5], vec[6]);
  319. transAxis.normalize();
  320. Point3F a1 = transAxis * mDot(rotAxis, transAxis);
  321. Point3F a2 = rotAxis - a1;
  322. a2.normalize();
  323. F32 an1 = mDot(rotAxis, a2);
  324. F32 an2 = mDot(rotAxis, transAxis);
  325. F32 rx = an1 * mCos(angle) - an2 * mSin(angle);
  326. F32 ry = an1 * mSin(angle) + an2 * mCos(angle);
  327. // Check for rotation parallel to translation
  328. F32 alpha = (an1 == 0) ? 0 : (ry/rx - an2/an1);
  329. MatrixF mat(true);
  330. mat(0,0) = a2.x * transAxis.x * alpha + 1.0;
  331. mat(1,0) = a2.y * transAxis.x * alpha;
  332. mat(2,0) = a2.z * transAxis.x * alpha;
  333. mat(0,1) = a2.x * transAxis.y * alpha;
  334. mat(1,1) = a2.y * transAxis.y * alpha + 1.0;
  335. mat(2,1) = a2.z * transAxis.y * alpha;
  336. mat(0,2) = a2.x * transAxis.z * alpha;
  337. mat(1,2) = a2.y * transAxis.z * alpha;
  338. mat(2,2) = a2.z * transAxis.z * alpha + 1.0;
  339. return mat;
  340. }
  341. /// Collada <lookat>: [eye, target, up]
  342. template<> inline MatrixF vecToMatrixF<domLookat>(const domListOfFloats& vec)
  343. {
  344. Point3F eye(vec[0], vec[1], vec[2]);
  345. Point3F target(vec[3], vec[4], vec[5]);
  346. Point3F up(vec[6], vec[7], vec[8]);
  347. Point3F fwd = target - eye;
  348. fwd.normalizeSafe();
  349. Point3F right = mCross(fwd, up);
  350. right.normalizeSafe();
  351. up = mCross(right, fwd);
  352. up.normalizeSafe();
  353. MatrixF mat(true);
  354. mat.setColumn(0, right);
  355. mat.setColumn(1, fwd);
  356. mat.setColumn(2, up);
  357. mat.setColumn(3, eye);
  358. return mat;
  359. }
  360. //-----------------------------------------------------------------------------
  361. /// Try to get a name for the element using the following attributes (in order):
  362. /// name, sid, id, "null"
  363. template<class T> inline const char* _GetNameOrId(const T* element)
  364. {
  365. return element ? (element->getName() ? element->getName() : (element->getId() ? element->getId() : "null")) : "null";
  366. }
  367. template<> inline const char* _GetNameOrId(const domInstance_geometry* element)
  368. {
  369. return element ? (element->getName() ? element->getName() : (element->getSid() ? element->getSid() : "null")) : "null";
  370. }
  371. template<> inline const char* _GetNameOrId(const domInstance_controller* element)
  372. {
  373. return element ? (element->getName() ? element->getName() : (element->getSid() ? element->getSid() : "null")) : "null";
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Collada <source>s are extremely flexible, and thus difficult to access in a nice
  377. // way. This class attempts to provide a clean interface to convert Collada source
  378. // data to the appropriate Torque data structure without losing any of the flexibility
  379. // of the underlying Collada DOM.
  380. //
  381. // Some of the conversions we need to handle are:
  382. // - daeString to const char*
  383. // - daeIDRef to const char*
  384. // - double to F32
  385. // - double to Point2F
  386. // - double to Point3F
  387. // - double to MatrixF
  388. //
  389. // The _SourceReader object is initialized with a list of parameter names that it
  390. // tries to match to <param> elements in the source accessor to figure out how to
  391. // pull values out of the 1D source array. Note that no type checking of any kind
  392. // is done until we actually try to extract values from the source.
  393. class _SourceReader
  394. {
  395. const domSource* source; // the wrapped Collada source
  396. const domAccessor* accessor; // shortcut to the source accessor
  397. Vector<U32> offsets; // offset of each of the desired values to pull from the source array
  398. public:
  399. _SourceReader() : source(0), accessor(0) {}
  400. void reset()
  401. {
  402. source = 0;
  403. accessor = 0;
  404. offsets.clear();
  405. }
  406. //------------------------------------------------------
  407. // Initialize the _SourceReader object
  408. bool initFromSource(const domSource* src, const char* paramNames[] = 0)
  409. {
  410. source = src;
  411. accessor = source->getTechnique_common()->getAccessor();
  412. offsets.clear();
  413. // The source array has groups of values in a 1D stream => need to map the
  414. // input param names to source params to determine the offset within the
  415. // group for each desired value
  416. U32 paramCount = 0;
  417. while (paramNames && paramNames[paramCount][0]) {
  418. // lookup the index of the source param that matches the input param
  419. offsets.push_back(paramCount);
  420. for (U32 iParam = 0; iParam < accessor->getParam_array().getCount(); iParam++) {
  421. if (accessor->getParam_array()[iParam]->getName() &&
  422. dStrEqual(accessor->getParam_array()[iParam]->getName(), paramNames[paramCount])) {
  423. offsets.last() = iParam;
  424. break;
  425. }
  426. }
  427. paramCount++;
  428. }
  429. // If no input params were specified, just map the source params directly
  430. if (!offsets.size()) {
  431. for (S32 iParam = 0; iParam < accessor->getParam_array().getCount(); iParam++)
  432. offsets.push_back(iParam);
  433. }
  434. return true;
  435. }
  436. //------------------------------------------------------
  437. // Shortcut to the size of the array (should be the number of destination objects)
  438. S32 size() const { return accessor ? accessor->getCount() : 0; }
  439. // Get the number of elements per group in the source
  440. S32 stride() const { return accessor ? accessor->getStride() : 0; }
  441. //------------------------------------------------------
  442. // Get a pointer to the start of a group of values (index advances by stride)
  443. //template<class T> T getArrayData(S32 index) const { return 0; }
  444. const double* getStringArrayData(S32 index) const
  445. {
  446. if ((index >= 0) && (index < size())) {
  447. if (source->getFloat_array())
  448. return &source->getFloat_array()->getValue()[index*stride()];
  449. }
  450. return 0;
  451. }
  452. //------------------------------------------------------
  453. // Read a single value from the source array
  454. //template<class T> T getValue(S32 index) const { return T; }
  455. const char* getStringValue(S32 index) const
  456. {
  457. if ((index >= 0) && (index < size())) {
  458. // could be plain strings or IDREFs
  459. if (source->getName_array())
  460. return source->getName_array()->getValue()[index*stride()];
  461. else if (source->getIDREF_array())
  462. return source->getIDREF_array()->getValue()[index*stride()].getID();
  463. }
  464. return "";
  465. }
  466. F32 getFloatValue(S32 index) const
  467. {
  468. F32 value(0);
  469. if (const double* data = getStringArrayData(index))
  470. return data[offsets[0]];
  471. return value;
  472. }
  473. Point2F getPoint2FValue(S32 index) const
  474. {
  475. Point2F value(0, 0);
  476. if (const double* data = getStringArrayData(index))
  477. value.set(data[offsets[0]], data[offsets[1]]);
  478. return value;
  479. }
  480. Point3F getPoint3FValue(S32 index) const
  481. {
  482. Point3F value(1, 0, 0);
  483. if (const double* data = getStringArrayData(index))
  484. value.set(data[offsets[0]], data[offsets[1]], data[offsets[2]]);
  485. return value;
  486. }
  487. ColorI getColorIValue(S32 index) const
  488. {
  489. ColorI value(255, 255, 255, 255);
  490. if (const double* data = getStringArrayData(index))
  491. {
  492. value.red = data[offsets[0]] * 255.0;
  493. value.green = data[offsets[1]] * 255.0;
  494. value.blue = data[offsets[2]] * 255.0;
  495. if ( stride() == 4 )
  496. value.alpha = data[offsets[3]] * 255.0;
  497. }
  498. return value;
  499. }
  500. MatrixF getMatrixFValue(S32 index) const
  501. {
  502. MatrixF value(true);
  503. if (const double* data = getStringArrayData(index)) {
  504. for (S32 i = 0; i < 16; i++)
  505. value[i] = data[i];
  506. }
  507. return value;
  508. }
  509. };
  510. //-----------------------------------------------------------------------------
  511. // Collada geometric primitives: Use the BasePrimitive class to access the
  512. // different primitive types in a nice way.
  513. class BasePrimitive
  514. {
  515. public:
  516. virtual ~BasePrimitive() { }
  517. /// Return true if the element is a geometric primitive type
  518. static bool isPrimitive(const daeElement* element)
  519. {
  520. switch (element->getElementType()) {
  521. case COLLADA_TYPE::TRIANGLES: case COLLADA_TYPE::POLYLIST:
  522. case COLLADA_TYPE::POLYGONS: case COLLADA_TYPE::TRIFANS:
  523. case COLLADA_TYPE::TRISTRIPS: case COLLADA_TYPE::CAPSULE:
  524. case COLLADA_TYPE::CYLINDER: case COLLADA_TYPE::LINES:
  525. case COLLADA_TYPE::LINESTRIPS: case COLLADA_TYPE::PLANE:
  526. case COLLADA_TYPE::SPLINE: case COLLADA_TYPE::SPHERE:
  527. case COLLADA_TYPE::TAPERED_CAPSULE: case COLLADA_TYPE::TAPERED_CYLINDER:
  528. return true;
  529. }
  530. return false;
  531. }
  532. /// Return true if the element is a supported primitive type
  533. static bool isSupportedPrimitive(const daeElement* element)
  534. {
  535. switch (element->getElementType()) {
  536. case COLLADA_TYPE::TRIANGLES:
  537. case COLLADA_TYPE::TRISTRIPS:
  538. case COLLADA_TYPE::TRIFANS:
  539. case COLLADA_TYPE::POLYLIST:
  540. case COLLADA_TYPE::POLYGONS:
  541. return true;
  542. }
  543. return false;
  544. }
  545. /// Construct a child class based on the type of Collada element
  546. static BasePrimitive* get(const daeElement* element);
  547. /// Methods to be implemented for each supported Collada geometric element
  548. virtual const char* getElementName() = 0;
  549. virtual const char* getMaterial() = 0;
  550. virtual const domInputLocalOffset_Array& getInputs() = 0;
  551. virtual S32 getStride() const = 0;
  552. virtual const domListOfUInts *getTriangleData() = 0;
  553. };
  554. /// Template child class for supported Collada primitive elements
  555. template<class T> class ColladaPrimitive : public BasePrimitive
  556. {
  557. T* primitive;
  558. domListOfUInts *pTriangleData;
  559. S32 stride;
  560. public:
  561. ColladaPrimitive(const daeElement* e) : pTriangleData(0)
  562. {
  563. // Cast to geometric primitive element
  564. primitive = daeSafeCast<T>(const_cast<daeElement*>(e));
  565. // Determine stride
  566. stride = 0;
  567. for (S32 iInput = 0; iInput < getInputs().getCount(); iInput++) {
  568. if (getInputs()[iInput]->getOffset() >= stride)
  569. stride = getInputs()[iInput]->getOffset() + 1;
  570. }
  571. }
  572. ~ColladaPrimitive()
  573. {
  574. delete pTriangleData;
  575. }
  576. /// Most primitives can use these common implementations
  577. const char* getElementName() { return primitive->getElementName(); }
  578. const char* getMaterial() { return (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImportMat, primitive->getMaterial(), false)) ? NULL : primitive->getMaterial(); }
  579. const domInputLocalOffset_Array& getInputs() { return primitive->getInput_array(); }
  580. S32 getStride() const { return stride; }
  581. /// Each supported primitive needs to implement this method (and convert
  582. /// to triangles if required)
  583. const domListOfUInts *getTriangleData() { return NULL; }
  584. };
  585. //-----------------------------------------------------------------------------
  586. // <triangles>
  587. template<> inline const domListOfUInts *ColladaPrimitive<domTriangles>::getTriangleData()
  588. {
  589. // Return the <p> integer list directly
  590. return (primitive->getP() ? &(primitive->getP()->getValue()) : NULL);
  591. }
  592. //-----------------------------------------------------------------------------
  593. // <tristrips>
  594. template<> inline const domListOfUInts *ColladaPrimitive<domTristrips>::getTriangleData()
  595. {
  596. if (!pTriangleData)
  597. {
  598. // Convert strips to triangles
  599. pTriangleData = new domListOfUInts();
  600. for (S32 iStrip = 0; iStrip < primitive->getCount(); iStrip++) {
  601. domP* P = primitive->getP_array()[iStrip];
  602. // Ignore invalid P arrays
  603. if (!P || !P->getValue().getCount())
  604. continue;
  605. domUint* pSrcData = &(P->getValue()[0]);
  606. size_t numTriangles = (P->getValue().getCount() / stride) - 2;
  607. // Convert the strip back to a triangle list
  608. domUint* v0 = pSrcData;
  609. for (S32 iTri = 0; iTri < numTriangles; iTri++, v0 += stride) {
  610. if (iTri & 0x1)
  611. {
  612. // CW triangle
  613. pTriangleData->appendArray(stride, v0);
  614. pTriangleData->appendArray(stride, v0 + 2*stride);
  615. pTriangleData->appendArray(stride, v0 + stride);
  616. }
  617. else
  618. {
  619. // CCW triangle
  620. pTriangleData->appendArray(stride*3, v0);
  621. }
  622. }
  623. }
  624. }
  625. return pTriangleData;
  626. }
  627. //-----------------------------------------------------------------------------
  628. // <trifans>
  629. template<> inline const domListOfUInts *ColladaPrimitive<domTrifans>::getTriangleData()
  630. {
  631. if (!pTriangleData)
  632. {
  633. // Convert strips to triangles
  634. pTriangleData = new domListOfUInts();
  635. for (S32 iStrip = 0; iStrip < primitive->getCount(); iStrip++) {
  636. domP* P = primitive->getP_array()[iStrip];
  637. // Ignore invalid P arrays
  638. if (!P || !P->getValue().getCount())
  639. continue;
  640. domUint* pSrcData = &(P->getValue()[0]);
  641. size_t numTriangles = (P->getValue().getCount() / stride) - 2;
  642. // Convert the fan back to a triangle list
  643. domUint* v0 = pSrcData + stride;
  644. for (S32 iTri = 0; iTri < numTriangles; iTri++, v0 += stride) {
  645. pTriangleData->appendArray(stride, pSrcData); // shared vertex
  646. pTriangleData->appendArray(stride, v0); // previous vertex
  647. pTriangleData->appendArray(stride, v0+stride); // current vertex
  648. }
  649. }
  650. }
  651. return pTriangleData;
  652. }
  653. //-----------------------------------------------------------------------------
  654. // <polygons>
  655. template<> inline const domListOfUInts *ColladaPrimitive<domPolygons>::getTriangleData()
  656. {
  657. if (!pTriangleData)
  658. {
  659. // Convert polygons to triangles
  660. pTriangleData = new domListOfUInts();
  661. for (S32 iPoly = 0; iPoly < primitive->getCount(); iPoly++) {
  662. domP* P = primitive->getP_array()[iPoly];
  663. // Ignore invalid P arrays
  664. if (!P || !P->getValue().getCount())
  665. continue;
  666. domUint* pSrcData = &(P->getValue()[0]);
  667. size_t numPoints = P->getValue().getCount() / stride;
  668. // Use a simple tri-fan (centered at the first point) method of
  669. // converting the polygon to triangles.
  670. domUint* v0 = pSrcData;
  671. pSrcData += stride;
  672. for (S32 iTri = 0; iTri < numPoints-2; iTri++) {
  673. pTriangleData->appendArray(stride, v0);
  674. pTriangleData->appendArray(stride*2, pSrcData);
  675. pSrcData += stride;
  676. }
  677. }
  678. }
  679. return pTriangleData;
  680. }
  681. //-----------------------------------------------------------------------------
  682. // <polylist>
  683. template<> inline const domListOfUInts *ColladaPrimitive<domPolylist>::getTriangleData()
  684. {
  685. if (!pTriangleData)
  686. {
  687. // Convert polygons to triangles
  688. pTriangleData = new domListOfUInts();
  689. // Check that the P element has the right number of values (this
  690. // has been seen with certain models exported using COLLADAMax)
  691. const domListOfUInts& vcount = primitive->getVcount()->getValue();
  692. U32 expectedCount = 0;
  693. for (S32 iPoly = 0; iPoly < vcount.getCount(); iPoly++)
  694. expectedCount += vcount[iPoly];
  695. expectedCount *= stride;
  696. if (!primitive->getP() || !primitive->getP()->getValue().getCount() ||
  697. (primitive->getP()->getValue().getCount() != expectedCount) )
  698. {
  699. Con::warnf("<polylist> element found with invalid <p> array. This primitive will be ignored.");
  700. return pTriangleData;
  701. }
  702. domUint* pSrcData = &(primitive->getP()->getValue()[0]);
  703. for (S32 iPoly = 0; iPoly < vcount.getCount(); iPoly++) {
  704. // Use a simple tri-fan (centered at the first point) method of
  705. // converting the polygon to triangles.
  706. domUint* v0 = pSrcData;
  707. pSrcData += stride;
  708. for (S32 iTri = 0; iTri < vcount[iPoly]-2; iTri++) {
  709. pTriangleData->appendArray(stride, v0);
  710. pTriangleData->appendArray(stride*2, pSrcData);
  711. pSrcData += stride;
  712. }
  713. pSrcData += stride;
  714. }
  715. }
  716. return pTriangleData;
  717. }
  718. //-----------------------------------------------------------------------------
  719. /// Convert a custom parameter string to a particular type
  720. template<typename T> inline T convert(const char* value) { return value; }
  721. template<> inline bool convert(const char* value) { return dAtob(value); }
  722. template<> inline S32 convert(const char* value) { return dAtoi(value); }
  723. template<> inline F64 convert(const char* value) { return dAtof(value); }
  724. template<> inline F32 convert(const char* value) { return convert<double>(value); }
  725. //-----------------------------------------------------------------------------
  726. /// Collada animation data
  727. struct AnimChannels : public Vector<struct AnimData*>
  728. {
  729. daeElement *element;
  730. AnimChannels(daeElement* el) : element(el)
  731. {
  732. element->setUserData(this);
  733. }
  734. ~AnimChannels()
  735. {
  736. if (element)
  737. element->setUserData(0);
  738. }
  739. };
  740. struct AnimData
  741. {
  742. bool enabled; ///!< Used to select animation channels for the current clip
  743. _SourceReader input;
  744. _SourceReader output;
  745. _SourceReader inTangent;
  746. _SourceReader outTangent;
  747. _SourceReader interpolation;
  748. U32 targetValueOffset; ///< Offset into the target element (for arrays of values)
  749. U32 targetValueCount; ///< Number of values animated (from OUTPUT source array)
  750. /// Get the animation channels for the Collada element (if any)
  751. static AnimChannels* getAnimChannels(const daeElement* element)
  752. {
  753. return element ? (AnimChannels*)const_cast<daeElement*>(element)->getUserData() : 0;
  754. }
  755. AnimData() : enabled(false), targetValueOffset(0), targetValueCount(0){ }
  756. void parseTargetString(const char* target, S32 fullCount, const char* elements[]);
  757. F32 invertParamCubic(F32 param, F32 x0, F32 x1, F32 x2, F32 x3) const;
  758. void interpValue(F32 t, U32 offset, double* value) const;
  759. void interpValue(F32 t, U32 offset, const char** value) const;
  760. };
  761. //-----------------------------------------------------------------------------
  762. // Collada allows any element with an SID or ID attribute to be the target of
  763. // an animation channel, which is very flexible, but awkward to work with. Some
  764. // examples of animated values are:
  765. // - single float
  766. // - single int
  767. // - single bool
  768. // - single string
  769. // - list of floats (transform elements or morph weights)
  770. //
  771. // This class provides a generic way to check if an element is animated, and
  772. // to get the value of the element at a given time.
  773. template<class T>
  774. struct AnimatedElement
  775. {
  776. const daeElement* element; ///< The Collada element (can be NULL)
  777. T defaultVal; ///< Default value (used when element is NULL)
  778. AnimatedElement(const daeElement* e=0) : element(e) { }
  779. /// Check if the element has any animations channels
  780. bool isAnimated() { return (AnimData::getAnimChannels(element) != 0); }
  781. bool isAnimated(F32 start, F32 end) { return isAnimated(); }
  782. /// Get the value of the element at the specified time
  783. T getValue(F32 time)
  784. {
  785. // If the element is NULL, just use the default (handy for <extra> profiles which
  786. // may or may not be present in the document)
  787. T value(defaultVal);
  788. if (const domAny* param = daeSafeCast<domAny>(const_cast<daeElement*>(element))) {
  789. // If the element is not animated, just use its current value
  790. value = convert<T>(param->getValue());
  791. // Animate the value
  792. const AnimChannels* channels = AnimData::getAnimChannels(element);
  793. if (channels && (time >= 0)) {
  794. for (S32 iChannel = 0; iChannel < channels->size(); iChannel++) {
  795. const AnimData* animData = (*channels)[iChannel];
  796. if (animData->enabled)
  797. animData->interpValue(time, 0, &value);
  798. }
  799. }
  800. }
  801. return value;
  802. }
  803. };
  804. template<class T> struct AnimatedElementList : public AnimatedElement<T>
  805. {
  806. AnimatedElementList(const daeElement* e=0) : AnimatedElement<T>(e) { }
  807. // @todo: Disable morph animations for now since they are not supported by T3D
  808. bool isAnimated() { return false; }
  809. bool isAnimated(F32 start, F32 end) { return false; }
  810. // Get the value of the element list at the specified time
  811. T getValue(F32 time)
  812. {
  813. T vec(this->defaultVal);
  814. if (this->element) {
  815. // Get a copy of the vector
  816. vec = *(T*)const_cast<daeElement*>(this->element)->getValuePointer();
  817. // Animate the vector
  818. const AnimChannels* channels = AnimData::getAnimChannels(this->element);
  819. if (channels && (time >= 0)) {
  820. for (S32 iChannel = 0; iChannel < channels->size(); iChannel++) {
  821. const AnimData* animData = (*channels)[iChannel];
  822. if (animData->enabled) {
  823. for (S32 iValue = 0; iValue < animData->targetValueCount; iValue++)
  824. animData->interpValue(time, iValue, &vec[animData->targetValueOffset + iValue]);
  825. }
  826. }
  827. }
  828. }
  829. return vec;
  830. }
  831. };
  832. // Strongly typed animated values
  833. typedef AnimatedElement<double> AnimatedFloat;
  834. typedef AnimatedElement<bool> AnimatedBool;
  835. typedef AnimatedElement<S32> AnimatedInt;
  836. typedef AnimatedElement<const char*> AnimatedString;
  837. typedef AnimatedElementList<domListOfFloats> AnimatedFloatList;
  838. #endif // _COLLADA_UTILS_H_