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#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