colladaUtils.h

Engine/source/ts/collada/colladaUtils.h

More...

Classes:

Namespaces:

namespace

Public Typedefs

AnimatedBool 
AnimatedElement< double >
AnimatedFloat 
AnimatedElementList< domListOfFloats >
AnimatedFloatList 
AnimatedInt 
AnimatedString 

Public Functions

const char *
_GetNameOrId(const domInstance_controller * element)
const char *
_GetNameOrId(const domInstance_geometry * element)
const char *
_GetNameOrId(const T * element)

Try to get a name for the element using the following attributes (in order): name, sid, id, "null".

T
convert(const char * value)

Convert a custom parameter string to a particular type.

bool
convert(const char * value)
vecToMatrixF(const domListOfFloats & vec)

Convert from the Collada transform types to a Torque MatrixF.

vecToMatrixF< domLookat>(const domListOfFloats & vec)

Collada : [eye, target, up].

vecToMatrixF< domMatrix>(const domListOfFloats & vec)

Collada : same form as TGE (woohoo!)

vecToMatrixF< domRotate>(const domListOfFloats & vec)

Collada : [rotation_axis, angle_in_degrees].

vecToMatrixF< domScale>(const domListOfFloats & vec)

Collada : [x_scale, y_scale, z_scale].

vecToMatrixF< domSkew>(const domListOfFloats & vec)

Collada : [angle_in_degrees, rotation_axis, translation_axis] skew transform code adapted from GMANMatrix4 implementation.

vecToMatrixF< domTranslate>(const domListOfFloats & vec)

Collada : [x_translate, y_translate, z_translate].

Detailed Description

Public Typedefs

typedef AnimatedElement< bool > AnimatedBool 
typedef AnimatedElement< double > AnimatedFloat 
typedef AnimatedElementList< domListOfFloats > AnimatedFloatList 
typedef AnimatedElement< S32 > AnimatedInt 
typedef AnimatedElement< const char * > AnimatedString 

Public Functions

_GetNameOrId(const domInstance_controller * element)

_GetNameOrId(const domInstance_geometry * element)

_GetNameOrId(const T * element)

Try to get a name for the element using the following attributes (in order): name, sid, id, "null".

convert(const char * value)

Convert a custom parameter string to a particular type.

convert(const char * value)

vecToMatrixF(const domListOfFloats & vec)

Convert from the Collada transform types to a Torque MatrixF.

vecToMatrixF< domLookat>(const domListOfFloats & vec)

Collada : [eye, target, up].

vecToMatrixF< domMatrix>(const domListOfFloats & vec)

Collada : same form as TGE (woohoo!)

vecToMatrixF< domRotate>(const domListOfFloats & vec)

Collada : [rotation_axis, angle_in_degrees].

vecToMatrixF< domScale>(const domListOfFloats & vec)

Collada : [x_scale, y_scale, z_scale].

vecToMatrixF< domSkew>(const domListOfFloats & vec)

Collada : [angle_in_degrees, rotation_axis, translation_axis] skew transform code adapted from GMANMatrix4 implementation.

vecToMatrixF< domTranslate>(const domListOfFloats & vec)

Collada : [x_translate, y_translate, z_translate].

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