colladaUtils.h

Engine/source/ts/collada/colladaUtils.h

More...

Classes:

class

Collada animation data.

class

Template child class for supported Collada primitive elements.

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