colladaUtils.h
Engine/source/ts/collada/colladaUtils.h
Classes:
Collada animation data.
Template child class for supported Collada primitive elements.
Namespaces:
Public Typedefs
AnimatedElement< bool >
AnimatedBool
AnimatedElement< double >
AnimatedFloat
AnimatedElementList< domListOfFloats >
AnimatedFloatList
AnimatedInt
AnimatedElement< const char * >
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".
vecToMatrixF(const domListOfFloats & vec)
vecToMatrixF< domLookat>(const domListOfFloats & vec)
Collada
vecToMatrixF< domMatrix>(const domListOfFloats & vec)
Collada
vecToMatrixF< domRotate>(const domListOfFloats & vec)
Collada
vecToMatrixF< domScale>(const domListOfFloats & vec)
Collada
vecToMatrixF< domSkew>(const domListOfFloats & vec)
Collada
vecToMatrixF< domTranslate>(const domListOfFloats & vec)
Collada
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
vecToMatrixF< domMatrix>(const domListOfFloats & vec)
Collada
vecToMatrixF< domRotate>(const domListOfFloats & vec)
Collada
vecToMatrixF< domScale>(const domListOfFloats & vec)
Collada
vecToMatrixF< domSkew>(const domListOfFloats & vec)
Collada
vecToMatrixF< domTranslate>(const domListOfFloats & vec)
Collada
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
