Torque3D Documentation / _generateds / colladaUtils.cpp

colladaUtils.cpp

Engine/source/ts/collada/colladaUtils.cpp

More...

Public Defines

define
CREATE_ELEMENT(container, name, type)    type* name = daeSafeCast<type>(container->createAndPlace(#name));
define

Public Functions

Detailed Description

Public Defines

CREATE_ELEMENT(container, name, type)    type* name = daeSafeCast<type>(container->createAndPlace(#name));
MAX_PATH_LENGTH() 256

Public Functions

conditioner_checkBindShapeMatrix(domCOLLADA * root)

conditioner_createDefaultClip(domCOLLADA * root)

conditioner_fixupAnimation(domAnimation * anim)

conditioner_fixupImageURIs(domCOLLADA * root)

conditioner_fixupTextureSIDs(domCOLLADA * root)

conditioner_fixupTransparency(domCOLLADA * root)

conditioner_fixupVertexWeightJoints(domCOLLADA * root)

   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#include <algorithm>
  25#include "console/console.h"
  26#include "gfx/bitmap/gBitmap.h"
  27#include "ts/collada/colladaUtils.h"
  28#include "materials/matInstance.h"
  29
  30//special handling for export classes
  31#include "T3D/convexShape.h"
  32
  33using namespace ColladaUtils;
  34
  35#define MAX_PATH_LENGTH 256
  36
  37// Helper macro to create Collada elements
  38#define CREATE_ELEMENT(container, name, type)   \
  39   type* name = daeSafeCast<type>(container->createAndPlace(#name));
  40
  41ColladaUtils::ImportOptions& ColladaUtils::getOptions()
  42{
  43   static ImportOptions options;
  44   return options;
  45}
  46
  47//------------------------------------------------------------------------------
  48// Utility functions
  49
  50// Convert a transform from the Collada model coordinate system to the DTS coordinate
  51// system
  52void ColladaUtils::convertTransform(MatrixF& mat)
  53{
  54   MatrixF rot(true);
  55
  56   switch (ColladaUtils::getOptions().upAxis)
  57   {
  58      case UPAXISTYPE_X_UP:
  59         // rotate 90 around Y-axis, then 90 around Z-axis
  60         rot(0,0) = 0.0f;  rot(1,0) = 1.0f;
  61         rot(1,1) = 0.0f;  rot(2,1) = 1.0f;
  62         rot(0,2) = 1.0f;  rot(2,2) = 0.0f;
  63
  64         // pre-multiply the transform by the rotation matrix
  65         mat.mulL(rot);
  66         break;
  67
  68      case UPAXISTYPE_Y_UP:
  69         // rotate 180 around Y-axis, then 90 around X-axis
  70         rot(0,0) = -1.0f;
  71         rot(1,1) = 0.0f;  rot(2,1) = 1.0f;
  72         rot(1,2) = 1.0f;  rot(2,2) = 0.0f;
  73
  74         // pre-multiply the transform by the rotation matrix
  75         mat.mulL(rot);
  76         break;
  77
  78      case UPAXISTYPE_Z_UP:
  79      default:
  80         // nothing to do
  81         break;
  82   }
  83}
  84
  85/// Find the COMMON profile element in an effect
  86const domProfile_COMMON* ColladaUtils::findEffectCommonProfile(const domEffect* effect)
  87{
  88   if (effect) {
  89      // Find the COMMON profile
  90      const domFx_profile_abstract_Array& profiles = effect->getFx_profile_abstract_array();
  91      for (S32 iProfile = 0; iProfile < profiles.getCount(); iProfile++) {
  92         if (profiles[iProfile]->getElementType() == COLLADA_TYPE::PROFILE_COMMON)
  93            return daeSafeCast<domProfile_COMMON>(profiles[iProfile]);
  94      }
  95   }
  96   return NULL;
  97}
  98
  99/// Find the <diffuse> element in the COMMON profile of an effect
 100const domCommon_color_or_texture_type_complexType* ColladaUtils::findEffectDiffuse(const domEffect* effect)
 101{
 102   const domProfile_COMMON* profile = findEffectCommonProfile(effect);
 103   if (profile) {
 104
 105      if (profile->getTechnique()->getLambert())
 106         return profile->getTechnique()->getLambert()->getDiffuse();
 107      else if (profile->getTechnique()->getPhong())
 108         return profile->getTechnique()->getPhong()->getDiffuse();
 109      else if (profile->getTechnique()->getBlinn())
 110         return profile->getTechnique()->getBlinn()->getDiffuse();
 111   }
 112
 113   return NULL;
 114}
 115
 116/// Find the <specular> element in the COMMON profile of an effect
 117const domCommon_color_or_texture_type_complexType* ColladaUtils::findEffectSpecular(const domEffect* effect)
 118{
 119   const domProfile_COMMON* profile = findEffectCommonProfile(effect);
 120   if (profile) {
 121
 122      if (profile->getTechnique()->getLambert())
 123         return NULL;      // no <specular> element for Lambert shader
 124      else if (profile->getTechnique()->getPhong())
 125         return profile->getTechnique()->getPhong()->getSpecular();
 126      else if (profile->getTechnique()->getBlinn())
 127         return profile->getTechnique()->getBlinn()->getSpecular();
 128   }
 129
 130   return NULL;
 131}
 132
 133const domFx_sampler2D_common_complexType* ColladaUtils::getTextureSampler(const domEffect* effect,
 134                                                                          const domCommon_color_or_texture_type_complexType* texture)
 135{
 136   // <texture texture="new_param_SID">.<newparam>.<sampler2D>
 137   if (texture) {
 138      const domCommon_color_or_texture_type_complexType::domTexture* domTex = texture->getTexture();
 139      if (domTex && domTex->getTexture()) {
 140         daeSIDResolver resolver(const_cast<domEffect*>(effect), domTex->getTexture());
 141         const domCommon_newparam_type* param = daeSafeCast<domCommon_newparam_type>(resolver.getElement());
 142         if (param)
 143            return param->getSampler2D();
 144      }
 145   }
 146
 147   return NULL;
 148}
 149
 150String ColladaUtils::getSamplerImagePath(const domEffect* effect,
 151                                         const domFx_sampler2D_common_complexType* sampler2D)
 152{
 153   // <sampler2D>.<source>.<newparam>.<surface>.<init_from>.<image>.<init_from>
 154   const domProfile_COMMON* profile = findEffectCommonProfile(effect);
 155   if (profile && sampler2D && sampler2D->getSource()) {
 156
 157      // Resolve the SID to get the <surface> param
 158      daeSIDResolver resolver(const_cast<domProfile_COMMON*>(profile), sampler2D->getSource()->getValue());
 159      domCommon_newparam_type* surfaceParam = daeSafeCast<domCommon_newparam_type>(resolver.getElement());
 160
 161      // Get the surface <init_from> element
 162      if (surfaceParam && surfaceParam->getSurface()) {
 163
 164         const domFx_surface_init_common* surfaceInit = surfaceParam->getSurface()->getFx_surface_init_common();
 165         if (surfaceInit && surfaceInit->getInit_from_array().getCount()) {
 166            // Resolve the ID to get the <image>, then read the texture path
 167            xsIDREF& idRef = surfaceInit->getInit_from_array()[0]->getValue();
 168            const domImage* image = daeSafeCast<domImage>(idRef.getElement());
 169            if (image && image->getInit_from())
 170               return resolveImagePath(image);
 171         }
 172      }
 173   }
 174
 175   return "";
 176}
 177
 178// Resolve image path into something we can use.
 179String ColladaUtils::resolveImagePath(const domImage* image)
 180{
 181   // 1. If the URI string contains an absolute path, use it if
 182   //    it is inside the Torque folder, otherwise force textures
 183   //    to be in the same folder as the shape.
 184   // 2. If the URI string contains a relative path, append it
 185   //    to the shape path (since materials.tscript cannot handle
 186   //    relative paths).
 187
 188   Torque::Path imagePath;
 189   String imageStr(image->getInit_from()->getValue().originalStr().c_str());
 190
 191   // Trim leading "file://"
 192   if (imageStr.compare("file://", 7) == 0)
 193      imageStr.erase(0, 7);
 194
 195   // Trim leading slash from absolute windows paths. eg. /D:/
 196   if ((imageStr.compare("/", 1) == 0) && (imageStr.find(':') == 2))
 197      imageStr.erase(0, 1);
 198
 199   // Replace %20 with space
 200   imageStr.replace("%20", " ");
 201
 202   if (Platform::isFullPath(imageStr))
 203   {
 204      // Absolute path => check for outside the Torque game folder
 205      imagePath = String( Platform::makeRelativePathName(imageStr, Platform::getMainDotCsDir()) );
 206      if ( !imagePath.getRoot().isEmpty()       ||    // different drive (eg. C:/ vs D:/)
 207           (imagePath.getPath().find("/") == 0) ||    // different OS (eg. /home vs C:/home)
 208           (imagePath.getPath().find("../") == 0) )   // same drive, outside Torque game folder
 209      {
 210         // Force these to the shape folder
 211         imagePath.setRoot("");
 212         imagePath.setPath("");
 213      }
 214   }
 215   else
 216   {
 217      // Relative path => prepend with shape path
 218      Torque::Path tempPath(imageStr);
 219      imagePath = TSShapeLoader::getShapePath();
 220      imagePath.appendPath(tempPath);
 221      imagePath.setFileName(tempPath.getFileName());
 222   }
 223
 224   // No need to specify the path if it is in the same folder as the model
 225   if (imagePath.getPath() == TSShapeLoader::getShapePath().getPath())
 226      imagePath.setPath("");
 227
 228   // Don't care about the extension
 229   imagePath.setExtension("");
 230
 231   return imagePath.getFullPath();
 232}
 233
 234//-----------------------------------------------------------------------------
 235// Construct the appropriate child class
 236BasePrimitive* BasePrimitive::get(const daeElement* element)
 237{
 238   switch (element->getElementType()) {
 239      case COLLADA_TYPE::TRIANGLES: return new ColladaPrimitive<domTriangles>(element);
 240      case COLLADA_TYPE::TRISTRIPS: return new ColladaPrimitive<domTristrips>(element);
 241      case COLLADA_TYPE::TRIFANS:   return new ColladaPrimitive<domTrifans>(element);
 242      case COLLADA_TYPE::POLYGONS:  return new ColladaPrimitive<domPolygons>(element);
 243      case COLLADA_TYPE::POLYLIST:  return new ColladaPrimitive<domPolylist>(element);
 244      default:                      return 0;
 245   }
 246}
 247
 248//------------------------------------------------------------------------------
 249// Collada animation curves
 250
 251/// Determine which elements are being targeted
 252void AnimData::parseTargetString(const char* target, S32 fullCount, const char* elements[])
 253{
 254   // Assume targeting all elements at offset 0
 255   targetValueCount = fullCount;
 256   targetValueOffset = 0;
 257
 258   // Check for array syntax: (n) or (n)(m)
 259   if (const char* p = dStrchr(target, '(')) {
 260      S32 indN, indM;
 261      if (dSscanf(p, "(%d)(%d)", &indN, &indM) == 2) {
 262         targetValueOffset = (indN * 4) + indM;   // @todo: 4x4 matrix only
 263         targetValueCount = 1;
 264      }
 265      else if (dSscanf(p, "(%d)", &indN) == 1) {
 266         targetValueOffset = indN;
 267         targetValueCount = 1;
 268      }
 269   }
 270   else if (const char* p2 = dStrrchr(target, '.')) {
 271      // Check for named elements
 272      for (S32 iElem = 0; elements[iElem][0] != 0; iElem++) {
 273         if (!String::compare(p2, elements[iElem])) {
 274            targetValueOffset = iElem;
 275            targetValueCount = 1;
 276            break;
 277         }
 278      }
 279   }
 280}
 281
 282/// Solve the cubic spline B(s) = param for s
 283F32 AnimData::invertParamCubic(F32 param, F32 x0, F32 x1, F32 x2, F32 x3) const
 284{
 285   const F64 INVERTPARAMCUBIC_TOL         = 1.0e-09;
 286   const F64 INVERTPARAMCUBIC_SMALLERTOL  = 1.0e-20;
 287   const F64 INVERTPARAMCUBIC_MAXIT       = 100;
 288
 289   // check input value for outside range
 290   if ((param - x0) < INVERTPARAMCUBIC_SMALLERTOL)
 291      return 0.0f;
 292   else if ((x3 - param) < INVERTPARAMCUBIC_SMALLERTOL)
 293      return 1.0f;
 294
 295   U32 iterations = 0;
 296
 297   // de Casteljau Subdivision.
 298   F32 u = 0.0f;
 299   F32 v = 1.0f;
 300
 301   while (iterations < INVERTPARAMCUBIC_MAXIT) {
 302      F64 a = (x0 + x1)*0.5f;
 303      F64 b = (x1 + x2)*0.5f;
 304      F64 c = (x2 + x3)*0.5f;
 305      F64 d = (a + b)*0.5f;
 306      F64 e = (b + c)*0.5f;
 307      F64 f = (d + e)*0.5f;
 308
 309      if (mFabs(f - param) < INVERTPARAMCUBIC_TOL)
 310         break;
 311
 312      if (f < param) {
 313         x0 = f;
 314         x1 = e;
 315         x2 = c;
 316         u = (u + v)*0.5f;
 317      }
 318      else {
 319         x1 = a;
 320         x2 = d;
 321         x3 = f;
 322         v = (u + v)*0.5f;
 323      }
 324      iterations++;
 325   }
 326
 327   return mClampF((u+v)*0.5f, 0.0f, 1.0f);
 328}
 329
 330/// Get the interpolated value at time 't'
 331void AnimData::interpValue(F32 t, U32 offset, double* value) const
 332{
 333   // handle degenerate animation data
 334   if (input.size() == 0)
 335   {
 336      *value = 0.0f;
 337      return;
 338   }
 339   else if (input.size() == 1)
 340   {
 341      *value = output.getStringArrayData(0)[offset];
 342      return;
 343   }
 344
 345   // clamp time to valid range
 346   F32 curveStart = input.getFloatValue(0);
 347   F32 curveEnd = input.getFloatValue(input.size()-1);
 348   t = mClampF(t, curveStart, curveEnd);
 349
 350   // find the index of the input keyframe BEFORE 't'
 351   S32 index;
 352   for (index = 0; index < input.size()-2; index++) {
 353      if (input.getFloatValue(index + 1) > t)
 354         break;
 355   }
 356
 357   // get the data for the two control points either side of 't'
 358   Point2F v0;
 359   v0.x = input.getFloatValue(index);
 360   v0.y = output.getStringArrayData(index)[offset];
 361
 362   Point2F v3;
 363   v3.x = input.getFloatValue(index + 1);
 364   v3.y = output.getStringArrayData(index + 1)[offset];
 365
 366   // If spline interpolation is specified but the tangents are not available,
 367   // default to LINEAR.
 368   const char* interp_method = interpolation.getStringValue(index);
 369   if (dStrEqual(interp_method, "BEZIER") ||
 370       dStrEqual(interp_method, "HERMITE") ||
 371       dStrEqual(interp_method, "CARDINAL")) {
 372
 373      const double* inArray = inTangent.getStringArrayData(index + 1);
 374      const double* outArray = outTangent.getStringArrayData(index);
 375      if (!inArray || !outArray)
 376         interp_method = "LINEAR";
 377   }
 378
 379   if (dStrEqual(interp_method, "STEP")) {
 380      // STEP interpolation
 381      *value = v0.y;
 382   }
 383   else if (dStrEqual(interp_method, "BEZIER") ||
 384            dStrEqual(interp_method, "HERMITE") ||
 385            dStrEqual(interp_method, "CARDINAL") ||
 386            dStrEqual(interp_method, "BSPLINE"))
 387   {
 388      // Cubic spline interpolation. The only difference between the 4 supported
 389      // forms is in the calculation of the other 2 control points:
 390      // BEZIER: control points are specified explicitly
 391      // HERMITE: tangents are specified, need to offset to get the control points
 392      // CARDINAL: (baked) tangents are specified, need to offset to get the control points
 393      // BSPLINE: control points are based on previous and next points
 394
 395      // Get the 2 extra control points
 396      Point2F v1, v2;
 397
 398      if (dStrEqual(interp_method, "BSPLINE")) {
 399         // v0 and v3 are the center points => need to
 400         // get the control points before and after them
 401         v1 = v0;
 402         v2 = v3;
 403
 404         if (index > 0) {
 405            v0.x = input.getFloatValue(index-1);
 406            v0.y = output.getStringArrayData(index-1)[offset];
 407         }
 408         else {
 409            // mirror P1 through P0
 410            v0 = v1 + (v1 - v2);
 411         }
 412
 413         if (index < (input.size()-2)) {
 414            v3.x = input.getFloatValue(index+2);
 415            v3.y = output.getStringArrayData(index+2)[offset];
 416         }
 417         else {
 418            // mirror P0 through P1
 419            v3 = v2 + (v2 - v1);
 420         }
 421      }
 422      else {
 423         const double* inArray = inTangent.getStringArrayData(index + 1);
 424         const double* outArray = outTangent.getStringArrayData(index);
 425
 426         if (output.stride() == inTangent.stride()) {
 427            // This degenerate form (1D control points) does 2 things wrong:
 428            // 1) it does not specify the key (time) value
 429            // 2) the control point is specified as a tangent for both bezier and hermite
 430            // => interpolate to get the key values, and offset the tangent values
 431            v1.set((v0.x*2 + v3.x)/3, v0.y + outArray[offset]);
 432            v2.set((v0.x + v3.x*2)/3, v3.y - inArray[offset]);
 433         }
 434         else {
 435            // the expected form (2D control points)
 436            v1.set(outArray[offset*2], outArray[offset*2+1]);
 437            v2.set(inArray[offset*2], inArray[offset*2+1]);
 438
 439            // if this is a hermite or cardinal spline, treat the values as tangents
 440            if (dStrEqual(interp_method, "HERMITE") || dStrEqual(interp_method, "CARDINAL")) {
 441               v1.set(v0.x + v1.x, v3.y - v1.y);
 442               v2.set(v0.x + v2.x, v3.x - v2.y);
 443            }
 444         }
 445      }
 446
 447      // find 's' that gives the desired 't' value
 448      F32 s = invertParamCubic(t, v0.x, v1.x, v2.x, v3.x);
 449
 450      // Calculate the output value using Bernstein evaluation and the
 451      // computed 's' value
 452      F32 c = 3.0f*(v1.y - v0.y);
 453      F32 e = 3.0f*(v2.y - v1.y);
 454      *value = (((v3.y - v0.y - e)*s + e - c)*s + c)*s + v0.y;
 455   }
 456   else {
 457      // default to LINEAR interpolation
 458      F32 s = mClampF((t - v0.x) / (v3.x - v0.x), 0.0f, 1.0f);
 459      *value = v0.y + (v3.y - v0.y) * s;
 460   }
 461}
 462
 463void AnimData::interpValue(F32 t, U32 offset, const char** value) const
 464{
 465   if (input.size() == 0)
 466      *value = "";
 467   else if (input.size() == 1)
 468      *value = output.getStringValue(0);
 469   else
 470   {
 471      // clamp time to valid range
 472      F32 curveStart = input.getFloatValue(0);
 473      F32 curveEnd = input.getFloatValue(input.size()-1);
 474      t = mClampF(t, curveStart, curveEnd);
 475
 476      // find the index of the input keyframe BEFORE 't'
 477      S32 index;
 478      for (index = 0; index < input.size()-2; index++) {
 479         if (input.getFloatValue(index + 1) > t)
 480            break;
 481      }
 482
 483      // String values only support STEP interpolation, so just get the
 484      // value at the input keyframe
 485      *value = output.getStringValue(index);
 486   }
 487}
 488
 489//------------------------------------------------------------------------------
 490// Collada document conditioners
 491
 492static void conditioner_fixupTextureSIDs(domCOLLADA* root)
 493{
 494   for (S32 iLib = 0; iLib < root->getLibrary_effects_array().getCount(); iLib++) {
 495      domLibrary_effects* lib = root->getLibrary_effects_array()[iLib];
 496      for (S32 iEffect = 0; iEffect < lib->getEffect_array().getCount(); iEffect++) {
 497         domEffect* effect = lib->getEffect_array()[iEffect];
 498         const domCommon_color_or_texture_type_complexType* diffuse = findEffectDiffuse(effect);
 499         if (!diffuse || !diffuse->getTexture())
 500            continue;
 501
 502         // Resolve the SID => if it is an <image>, add <sampler2D> and
 503         // <surface> elements to conform to the Collada spec.
 504         const char *image_sid = diffuse->getTexture()->getTexture();
 505         daeSIDResolver resolver(effect, image_sid);
 506         if (!daeSafeCast<domImage>(resolver.getElement()))
 507            continue;
 508
 509         daeErrorHandler::get()->handleWarning(avar("Fixup %s <diffuse>.<texture> "
 510            "pointing at <image> instead of <sampler2D>", effect->getID()));
 511
 512         // Generate SIDs for the new sampler2D and surface elements
 513         std::string sampler_sid(std::string(image_sid) + "-sampler");
 514         std::string surface_sid(std::string(image_sid) + "-surface");
 515
 516         domProfile_COMMON* profile = const_cast<domProfile_COMMON*>(findEffectCommonProfile(effect));
 517
 518         // Create <newparam>.<sampler2D>.<source>
 519         {
 520            CREATE_ELEMENT(profile,    newparam,   domCommon_newparam_type)
 521            CREATE_ELEMENT(newparam,   sampler2D,  domFx_sampler2D_common)
 522            CREATE_ELEMENT(sampler2D,  source,     domFx_sampler2D_common_complexType::domSource)
 523
 524            newparam->setSid(sampler_sid.c_str());
 525            source->setValue(surface_sid.c_str());
 526         }
 527
 528         // Create <newparam>.<surface>.<init_from>
 529         {
 530            CREATE_ELEMENT(profile,    newparam,   domCommon_newparam_type)
 531            CREATE_ELEMENT(newparam,   surface,    domFx_surface_common)
 532            CREATE_ELEMENT(surface,    init_from,  domFx_surface_init_from_common)
 533            CREATE_ELEMENT(surface,    format,     domFx_surface_common_complexType::domFormat)
 534
 535            newparam->setSid(surface_sid.c_str());
 536            surface->setType(FX_SURFACE_TYPE_ENUM_2D);
 537            format->setValue("A8R8G8B8");
 538            init_from->setValue(image_sid);
 539         }
 540
 541         // Store sampler2D sid in the <diffuse>.<texture> "texture" attribute
 542         diffuse->getTexture()->setTexture(sampler_sid.c_str());
 543      }
 544   }
 545}
 546
 547static void conditioner_fixupImageURIs(domCOLLADA* root)
 548{
 549   for (S32 iLib = 0; iLib < root->getLibrary_images_array().getCount(); iLib++) {
 550      domLibrary_images* lib = root->getLibrary_images_array()[iLib];
 551      for (S32 iImage = 0; iImage < lib->getImage_array().getCount(); iImage++) {
 552         domImage* image = lib->getImage_array()[iImage];
 553         if (image->getInit_from()) {
 554            xsAnyURI& uri = image->getInit_from()->getValue();
 555
 556            // Replace '\' with '/'
 557            if (uri.originalStr().find("\\") != std::string::npos) {
 558               daeErrorHandler::get()->handleWarning(avar("Fixup invalid URI "
 559                  "in %s: \"%s\"", image->getID(), uri.originalStr().c_str()));
 560
 561               std::string str(uri.originalStr());
 562               std::replace(str.begin(), str.end(), '\\', '/');
 563               uri.set(str);
 564            }
 565
 566            // Detect file://texture.jpg => this is an invalid URI and will
 567            // not be parsed correctly
 568            if (uri.scheme() == "file" &&
 569               uri.pathFile().empty() &&
 570               !uri.authority().empty()) {
 571               daeErrorHandler::get()->handleWarning(avar("Fixup invalid URI "
 572                  "in %s: \"%s\"", image->getID(), uri.originalStr().c_str()));
 573
 574               uri.set(uri.authority());
 575            }
 576         }
 577      }
 578   }
 579}
 580
 581static void conditioner_fixupTransparency(domCOLLADA* root)
 582{
 583   // Transparency is another example of something simple made complicated by
 584   // Collada. There are two (optional) elements that determine transparency:
 585   //
 586   // <transparent>: a color
 587   // <transparency>: a percentage applied to the color values
 588   //
 589   // Additionally, <transparent> has an optional "opaque" attribute that changes
 590   // the way transparency is determined. If set to A_ONE (the default), only the
 591   // alpha value of the transparent color is used, and a value of "1" means fully
 592   // opaque. If set to RGB_ZERO, only the RGB values of transparent are used, and
 593   // a value of "0" means fully opaque.
 594   //
 595   // To further complicate matters, Google Sketchup (all versions) and FeelingSoftware
 596   // ColladaMax (pre 3.03) export materials with the transparency element inverted
 597   // (1-transparency)
 598
 599   // Get the <authoring_tool> string
 600   const char *authoringTool = "";
 601   if (const domAsset* asset = root->getAsset()) {
 602      for (S32 iContrib = 0; iContrib < asset->getContributor_array().getCount(); iContrib++) {
 603         const domAsset::domContributor* contrib = asset->getContributor_array()[iContrib];
 604         if (contrib->getAuthoring_tool()) {
 605            authoringTool = contrib->getAuthoring_tool()->getValue();
 606            break;
 607         }
 608      }
 609   }
 610
 611   // Check for a match with the known problem-tools
 612   bool invertTransparency = false;
 613   const char *toolNames[] = {   "FBX COLLADA exporter", "Google SketchUp",
 614                                 "Illusoft Collada Exporter", "FCollada" };
 615   for (S32 iName = 0; iName < (sizeof(toolNames)/sizeof(toolNames[0])); iName++) {
 616      if (dStrstr(authoringTool, toolNames[iName])) {
 617         invertTransparency = true;
 618         break;
 619      }
 620   }
 621
 622   if (!invertTransparency)
 623      return;
 624
 625   // Invert transparency as required for each effect
 626   for (S32 iLib = 0; iLib < root->getLibrary_effects_array().getCount(); iLib++) {
 627      domLibrary_effects* lib = root->getLibrary_effects_array()[iLib];
 628      for (S32 iEffect = 0; iEffect < lib->getEffect_array().getCount(); iEffect++) {
 629         domEffect* effect = lib->getEffect_array()[iEffect];
 630
 631         // Find the common profile
 632         const domProfile_COMMON* commonProfile = findEffectCommonProfile(effect);
 633         if (!commonProfile)
 634            continue;
 635
 636         domCommon_transparent_type* transparent = 0;
 637         if (commonProfile->getTechnique()->getConstant())
 638            transparent = commonProfile->getTechnique()->getConstant()->getTransparent();
 639         else if (commonProfile->getTechnique()->getLambert())
 640            transparent = commonProfile->getTechnique()->getLambert()->getTransparent();
 641         else if (commonProfile->getTechnique()->getPhong())
 642            transparent = commonProfile->getTechnique()->getPhong()->getTransparent();
 643         else if (commonProfile->getTechnique()->getBlinn())
 644            transparent = commonProfile->getTechnique()->getBlinn()->getTransparent();
 645
 646         if (!transparent)
 647            continue;
 648
 649         // If the shader "opaque" attribute is not specified, set it to
 650         // RGB_ZERO (the opposite of the Collada default), as this is what
 651         // the bad exporter tools seem to assume.
 652         if (!transparent->isAttributeSet("opaque")) {
 653
 654            daeErrorHandler::get()->handleWarning(avar("Setting <transparent> "
 655               "\"opaque\" attribute to RGB_ZERO for %s <effect>", effect->getID()));
 656
 657            transparent->setOpaque(FX_OPAQUE_ENUM_RGB_ZERO);
 658         }
 659      }
 660   }
 661}
 662
 663static void conditioner_checkBindShapeMatrix(domCOLLADA* root)
 664{
 665   for (S32 iLib = 0; iLib < root->getLibrary_controllers_array().getCount(); iLib++) {
 666      domLibrary_controllers* lib = root->getLibrary_controllers_array().get(iLib);
 667      for (S32 iCon = 0; iCon < lib->getController_array().getCount(); iCon++) {
 668         domController* con = lib->getController_array().get(iCon);
 669         if (con->getSkin() && con->getSkin()->getBind_shape_matrix()) {
 670
 671            MatrixF mat = vecToMatrixF<domMatrix>(con->getSkin()->getBind_shape_matrix()->getValue());
 672            if (!mat.fullInverse()) {
 673               daeErrorHandler::get()->handleWarning(avar("<bind_shape_matrix> "
 674                  "in %s <controller> is not invertible (may cause problems with "
 675                  "skinning)", con->getID()));
 676            }
 677         }
 678      }
 679   }
 680}
 681
 682static void conditioner_fixupVertexWeightJoints(domCOLLADA* root)
 683{
 684   for (S32 iLib = 0; iLib < root->getLibrary_controllers_array().getCount(); iLib++) {
 685      domLibrary_controllers* lib = root->getLibrary_controllers_array().get(iLib);
 686      for (S32 iCon = 0; iCon < lib->getController_array().getCount(); iCon++) {
 687         domController* con = lib->getController_array().get(iCon);
 688         if (con->getSkin() && con->getSkin()->getVertex_weights())
 689         {
 690            domInputLocalOffset_Array& vw_inputs = con->getSkin()->getVertex_weights()->getInput_array();
 691            for (S32 vInput = 0; vInput < vw_inputs.getCount(); vInput++) {
 692
 693               domInputLocalOffset *vw_input = vw_inputs.get(vInput);
 694               if (dStrEqual(vw_input->getSemantic(), "JOINT")) {
 695
 696                  // Check if this input points at a float array (bad)
 697                  domSource* vw_source = daeSafeCast<domSource>(vw_input->getSource().getElement());
 698                  if (vw_source->getFloat_array()) {
 699
 700                     // Copy the value from the <joints> JOINTS input instead
 701                     domInputLocal_Array& joint_inputs = con->getSkin()->getJoints()->getInput_array();
 702                     for (S32 jInput = 0; jInput < joint_inputs.getCount(); jInput++) {
 703
 704                        domInputLocal *joint_input = joint_inputs.get(jInput);
 705                        if (dStrEqual(joint_input->getSemantic(), "JOINT")) {
 706                           vw_input->setSource(joint_input->getSource());
 707                           break;
 708                        }
 709                     }
 710                  }
 711               }
 712            }
 713         }
 714      }
 715   }
 716}
 717
 718static void conditioner_createDefaultClip(domCOLLADA* root)
 719{
 720   // Check if the document has any <animation_clip>s
 721   for (S32 iLib = 0; iLib < root->getLibrary_animation_clips_array().getCount(); iLib++) {
 722      if (root->getLibrary_animation_clips_array()[iLib]->getAnimation_clip_array().getCount())
 723         return;
 724   }
 725
 726   // Get all top-level <animation>s into an array
 727   domAnimation_Array animations;
 728   for (S32 iAnimLib = 0; iAnimLib < root->getLibrary_animations_array().getCount(); iAnimLib++) {
 729      const domLibrary_animations* libraryAnims = root->getLibrary_animations_array()[iAnimLib];
 730      for (S32 iAnim = 0; iAnim < libraryAnims->getAnimation_array().getCount(); iAnim++)
 731         animations.append(libraryAnims->getAnimation_array()[iAnim]);
 732   }
 733
 734   if (!animations.getCount())
 735      return;
 736
 737   daeErrorHandler::get()->handleWarning("Creating cyclic animation clip to "
 738      "hold all animations");
 739
 740   // Get animation_clip library (create one if necessary)
 741   if (!root->getLibrary_animation_clips_array().getCount()) {
 742      root->createAndPlace("library_animation_clips");
 743   }
 744   domLibrary_animation_clips* libraryClips = root->getLibrary_animation_clips_array()[0];
 745
 746   // Create new animation_clip for the default sequence
 747   CREATE_ELEMENT(libraryClips, animation_clip, domAnimation_clip)
 748   animation_clip->setName("ambient");
 749   animation_clip->setId("dummy_ambient_clip");
 750   animation_clip->setStart(0);
 751   animation_clip->setEnd(0);
 752
 753   // Add all top_level animations to the clip (sub-animations will be included
 754   // when the clip is procesed)
 755   for (S32 iAnim = 0; iAnim < animations.getCount(); iAnim++) {
 756      if (!animations[iAnim]->getId())
 757         animations[iAnim]->setId(avar("dummy-animation-id%d", iAnim));
 758      CREATE_ELEMENT(animation_clip, instance_animation, domInstanceWithExtra)
 759      std::string url(std::string("#") + animations[iAnim]->getId());
 760      instance_animation->setUrl(url.c_str());
 761   }
 762
 763   // Add the 'Torque' profile to specify the 'Cyclic' flag
 764   CREATE_ELEMENT(animation_clip, extra, domExtra)
 765   CREATE_ELEMENT(extra, technique, domTechnique)
 766   CREATE_ELEMENT(technique, any, domAny)
 767   technique->setProfile("Torque");
 768   any->setElementName("cyclic");
 769   any->setValue("1");
 770}
 771
 772static void conditioner_fixupAnimation(domAnimation* anim)
 773{
 774   S32 visibilityLen = dStrlen("/visibility");
 775
 776   for (S32 iChannel = 0; iChannel < anim->getChannel_array().getCount(); iChannel++) {
 777
 778      // Get the animation elements: <channel>, <sampler>
 779      domChannel* channel = anim->getChannel_array()[iChannel];
 780      domSampler* sampler = daeSafeCast<domSampler>(channel->getSource().getElement());
 781      if (!sampler)
 782         continue;
 783/*
 784      // If using a spline interpolation type but no tangents are specified,
 785      // fall back to LINEAR interpolation.
 786      bool isSpline = false;
 787      bool foundInTangent = false;
 788      bool foundOutTangent = false;
 789      for (int iInput = 0; iInput < sampler->getInput_array().getCount(); iInput++) {
 790         const char *semantic = sampler->getInput_array()[iInput]->getSemantic();
 791         if (dStrEqual(semantic, "INTERPOLATION")) {
 792            if (
 793         }
 794         if (dStrEqual(semantic, "IN_TANGENT"))
 795            foundInTangent = true;
 796         if (dStrEqual(semantic, "OUT_TANGENT"))
 797            foundOutTangent = true;
 798      }
 799
 800      if (isSpline && (!foundInTangent || !foundOutTangent)) {
 801         daeErrorHandler::get()->handleWarning(avar("%s type interpolation "
 802            "specified for %s, but IN/OUT TANGENTS are not provided. Using "
 803            "LINEAR interpolation instead.");
 804
 805      }
 806*/
 807
 808      // Find the animation channel target
 809      daeSIDResolver resolver(channel, channel->getTarget());
 810      daeElement* target = resolver.getElement();
 811      if (!target) {
 812
 813         // Some exporters generate visibility animations but don't add the
 814         // FCOLLADA extension, so the target doesn't actually exist! Detect
 815         // this situation and add the extension manually so the animation
 816         // still works.
 817         if (dStrEndsWith(channel->getTarget(), "/visibility")) {
 818
 819            // Get parent SID string
 820            char *parentSID = dStrdup(channel->getTarget());
 821            parentSID[dStrlen(parentSID) - visibilityLen] = '\0';
 822
 823            // Find the parent element (should be a <node>)
 824            daeSIDResolver parentResolver(channel, parentSID);
 825            daeElement* parent = parentResolver.getElement();
 826            delete [] parentSID;
 827
 828            if (parent && (parent->getElementType() == COLLADA_TYPE::NODE)) {
 829
 830               // Create the FCOLLADA extension
 831               daeErrorHandler::get()->handleWarning(avar("Creating missing "
 832                  "visibility animation target: %s", channel->getTarget()));
 833
 834               // Check if the <visibility> element exists but is missing the SID
 835               daeElement* vis = parent->getDescendant("visibility");
 836               if (vis)
 837               {
 838                  vis->setAttribute("sid", "visibility");
 839               }
 840               else
 841               {
 842                  CREATE_ELEMENT(parent, extra, domExtra)
 843                  CREATE_ELEMENT(extra, technique, domTechnique)
 844                  CREATE_ELEMENT(technique, any, domAny)
 845
 846                  technique->setProfile("FCOLLADA");
 847                  any->setElementName("visibility");
 848                  any->setAttribute("sid", "visibility");
 849                  any->setValue("");  // real initial value will be set when animation is processed in ColladaShapeLoader::processAnimation
 850               }
 851            }
 852         }
 853      }
 854   }
 855
 856   // Process child animations
 857   for (S32 iAnim = 0; iAnim < anim->getAnimation_array().getCount(); iAnim++)
 858      conditioner_fixupAnimation(anim->getAnimation_array()[iAnim]);
 859}
 860
 861/// Apply the set of model conditioners
 862void ColladaUtils::applyConditioners(domCOLLADA* root)
 863{
 864   //--------------------------------------------------------------------------
 865   // The built-in MAX FBX exporter specifies an <image> SID in the <texture>
 866   // "texture" attribute instead of a <newparam> SID. Detect and fix this.
 867   conditioner_fixupTextureSIDs(root);
 868
 869   //--------------------------------------------------------------------------
 870   // The built-in MAX FBX exporter also generates invalid URI paths in the
 871   // <image>.<init_from> tag, so fix that up too.
 872   conditioner_fixupImageURIs(root);
 873
 874   //--------------------------------------------------------------------------
 875   // Many exporters get transparency backwards. Check if the model was exported
 876   // by one with a known issue and correct it.
 877   conditioner_fixupTransparency(root);
 878
 879   //--------------------------------------------------------------------------
 880   // Some exporters (AutoDesk) generate invalid bind_shape matrices. Warn if
 881   // the bind_shape_matrix is not invertible.
 882   conditioner_checkBindShapeMatrix(root);
 883
 884   //--------------------------------------------------------------------------
 885   // The PoserPro exporter points the <vertex_weights> JOINT input to the
 886   // inverse bind matrices instead of the joint names array. Detect and fix it.
 887   conditioner_fixupVertexWeightJoints(root);
 888
 889   //--------------------------------------------------------------------------
 890   // If the model contains <animation>s but no <animation_clip>s, just put all
 891   // top level animations into a single clip.
 892   conditioner_createDefaultClip(root);
 893
 894   //--------------------------------------------------------------------------
 895   // Apply some animation fixups:
 896   // 1) Some exporters (eg. Blender) generate "BEZIER" type animation curves,
 897   //    but do not specify the IN and OUT tangent data arrays. Detect this and
 898   //    fall back to LINEAR interpolation.
 899   // 2) Some exporters generate visibility animations but don't add the FCOLLADA
 900   //    extension, so the target doesn't actually exist! Detect this situation
 901   //    and add the extension manually so the animation still works.
 902   for (S32 iLib = 0; iLib < root->getLibrary_animations_array().getCount(); iLib++) {
 903      const domLibrary_animations* lib = root->getLibrary_animations_array()[iLib];
 904      for (S32 iAnim = 0; iAnim < lib->getAnimation_array().getCount(); iAnim++)
 905         conditioner_fixupAnimation(lib->getAnimation_array()[iAnim]);
 906   }
 907}
 908
 909Torque::Path ColladaUtils::findTexture(const Torque::Path& diffuseMap)
 910{
 911   Vector<Torque::Path> foundPaths;
 912
 913   GBitmap::sFindFiles(diffuseMap, &foundPaths);
 914
 915   if (foundPaths.size() > 0)
 916      return Torque::Path(foundPaths[0]);
 917
 918   // If unable to load texture in current directory
 919   // look in the parent directory.  But never look in the root.
 920   Torque::Path newPath(diffuseMap);
 921
 922   String filePath = newPath.getPath();
 923
 924   String::SizeType  slash = filePath.find('/', filePath.length(), String::Right);
 925
 926   if (slash != String::NPos)
 927   {
 928      slash = filePath.find('/', filePath.length(), String::Right);
 929
 930      if (slash != String::NPos)
 931      {
 932         String truncPath = filePath.substr(0, slash);
 933         newPath.setPath(truncPath);
 934
 935         return findTexture(newPath);
 936      }
 937   }
 938
 939   return String::EmptyString;
 940}
 941
 942void ColladaUtils::exportColladaHeader(TiXmlElement* rootNode)
 943{
 944   TiXmlElement* assetNode = new TiXmlElement("asset");
 945   rootNode->LinkEndChild(assetNode);
 946
 947   TiXmlElement* contributorNode = new TiXmlElement("contributor");
 948   assetNode->LinkEndChild(contributorNode);
 949
 950   TiXmlElement* authorNode = new TiXmlElement("author");
 951   contributorNode->LinkEndChild(authorNode);
 952   TiXmlText* authorNodeText = new TiXmlText("Torque3D MIT User");
 953   authorNode->LinkEndChild(authorNodeText);
 954
 955   TiXmlElement* authoringToolNode = new TiXmlElement("authoring_tool");
 956   contributorNode->LinkEndChild(authoringToolNode);
 957   TiXmlText* authorText = new TiXmlText(avar("%s %s Object Exporter", getEngineProductString(), getVersionString()));
 958   authoringToolNode->LinkEndChild(authorText);
 959
 960   //TiXmlElement* commentsNode = new TiXmlElement("comments");
 961   //contributorNode->LinkEndChild(commentsNode);
 962
 963   // Get the current time
 964   Platform::LocalTime lt;
 965   Platform::getLocalTime(lt);
 966   String localTime = Platform::localTimeToString(lt);
 967
 968   localTime.replace('\t', ' ');
 969
 970   TiXmlElement* createdNode = new TiXmlElement("created");
 971   assetNode->LinkEndChild(createdNode);
 972   TiXmlText* createdText = new TiXmlText(avar("%s", localTime.c_str()));
 973   createdNode->LinkEndChild(createdText);
 974
 975   TiXmlElement* modifiedNode = new TiXmlElement("modified");
 976   assetNode->LinkEndChild(modifiedNode);
 977   TiXmlText* modifiedText = new TiXmlText(avar("%s", localTime.c_str()));
 978   modifiedNode->LinkEndChild(modifiedText);
 979
 980   //TiXmlElement* revisionNode = new TiXmlElement("revision");
 981   //assetNode->LinkEndChild(revisionNode);
 982
 983   //TiXmlElement* titleNode = new TiXmlElement("title");
 984   //assetNode->LinkEndChild(titleNode);
 985
 986   //TiXmlElement* subjectNode = new TiXmlElement("subject");
 987   //assetNode->LinkEndChild(subjectNode);
 988
 989   //TiXmlElement* keywordsNode = new TiXmlElement("keywords");
 990   //assetNode->LinkEndChild(keywordsNode);
 991
 992   // Torque uses Z_UP with 1 unit equal to 1 meter by default
 993   TiXmlElement* unitNode = new TiXmlElement("unit");
 994   assetNode->LinkEndChild(unitNode);
 995   unitNode->SetAttribute("name", "meter");
 996   unitNode->SetAttribute("meter", "1");
 997
 998   TiXmlElement* axisNode = new TiXmlElement("up_axis");
 999   assetNode->LinkEndChild(axisNode);
1000   TiXmlText* axisText = new TiXmlText("Z_UP");
1001   axisNode->LinkEndChild(axisText);
1002}
1003
1004void ColladaUtils::exportColladaMaterials(TiXmlElement* rootNode, const OptimizedPolyList& mesh, Vector<String>& matNames, const Torque::Path& colladaFile)
1005{
1006   // First the image library
1007   TiXmlElement* imgLibNode = new TiXmlElement("library_images");
1008   rootNode->LinkEndChild(imgLibNode);
1009
1010   for (U32 i = 0; i < mesh.mMaterialList.size(); i++)
1011   {
1012      BaseMatInstance* baseInst = mesh.mMaterialList[i];
1013
1014      matNames.push_back(String::ToString("Material%d", i));
1015
1016      Material* mat = dynamic_cast<Material*>(baseInst->getMaterial());
1017      if (!mat)
1018         continue;
1019
1020      String diffuseMap;
1021
1022     if (mat->getName() && mat->getName()[0])
1023        matNames.last() = mat->mMapTo;
1024
1025      // Handle an auto-generated "Default Material" specially
1026      if (mat->isAutoGenerated())
1027      {
1028         Torque::Path diffusePath;
1029
1030         if (mat->mDiffuseMapFilename[0].isNotEmpty())
1031            diffusePath = mat->mDiffuseMapFilename[0];
1032         else
1033            diffusePath = String("warningMat");
1034
1035         matNames.last() = diffusePath.getFileName();
1036         diffuseMap += diffusePath.getFullFileName();
1037      }
1038      else
1039      {
1040         if (mat->mDiffuseMapFilename[0].isNotEmpty())
1041            diffuseMap += mat->mDiffuseMapFilename[0];
1042         else
1043            diffuseMap += "warningMat";
1044      }
1045
1046      Torque::Path diffusePath = findTexture(colladaFile.getPath() + "/" + diffuseMap);
1047
1048      // If we didn't get a path
1049      if (diffusePath.getFullPath().isNotEmpty())
1050         diffuseMap = Torque::Path::MakeRelativePath(diffusePath, colladaFile);
1051
1052      TiXmlElement* imageNode = new TiXmlElement("image");
1053      imgLibNode->LinkEndChild(imageNode);
1054      imageNode->SetAttribute("id", avar("%s", matNames.last().c_str()));
1055      imageNode->SetAttribute("name", avar("%s", matNames.last().c_str()));
1056
1057      TiXmlElement* initNode = new TiXmlElement("init_from");
1058      imageNode->LinkEndChild(initNode);
1059     TiXmlText* initText = new TiXmlText(avar("file://%s", diffuseMap.c_str())); // "the file://" is needed to load the texture in some old apps (ex: 3ds Max 2009)
1060      initNode->LinkEndChild(initText);
1061
1062   }
1063
1064   // Next the effects library
1065   TiXmlElement* effectLibNode = new TiXmlElement("library_effects");
1066   rootNode->LinkEndChild(effectLibNode);
1067
1068   for (U32 i = 0; i < mesh.mMaterialList.size(); i++)
1069   {
1070      BaseMatInstance* baseInst = mesh.mMaterialList[i];
1071
1072      Material* mat = dynamic_cast<Material*>(baseInst->getMaterial());
1073      if (!mat)
1074         continue;
1075
1076      TiXmlElement* effectNode = new TiXmlElement("effect");
1077      effectLibNode->LinkEndChild(effectNode);
1078      effectNode->SetAttribute("id", avar("%s-effect", matNames[i].c_str()));
1079      effectNode->SetAttribute("name", avar("%s-effect", matNames[i].c_str()));
1080
1081      TiXmlElement* profileNode = new TiXmlElement("profile_COMMON");
1082      effectNode->LinkEndChild(profileNode);
1083     
1084     // ---------------------------
1085      TiXmlElement* newParamNode = new TiXmlElement("newparam");
1086      profileNode->LinkEndChild(newParamNode);
1087      newParamNode->SetAttribute("sid", avar("%s-surface", matNames[i].c_str()));     
1088
1089      TiXmlElement* surfaceNode = new TiXmlElement("surface");
1090      newParamNode->LinkEndChild(surfaceNode);
1091     surfaceNode->SetAttribute("type", "2D");
1092
1093      TiXmlElement* initNode2 = new TiXmlElement("init_from");
1094      surfaceNode->LinkEndChild(initNode2);
1095      TiXmlText* init2Text = new TiXmlText(avar("%s", matNames[i].c_str()));
1096      initNode2->LinkEndChild(init2Text);
1097
1098      TiXmlElement* formatNode = new TiXmlElement("format");
1099      surfaceNode->LinkEndChild(formatNode);
1100      TiXmlText* formatText = new TiXmlText("A8R8G8B8");
1101      formatNode->LinkEndChild(formatText);
1102
1103     // ---------------------------
1104      TiXmlElement* newParam2Node = new TiXmlElement("newparam");
1105      profileNode->LinkEndChild(newParam2Node);
1106      newParam2Node->SetAttribute("sid", avar("%s-sampler", matNames[i].c_str()));    
1107
1108      TiXmlElement* sampler2DNode = new TiXmlElement("sampler2D");
1109      newParam2Node->LinkEndChild(sampler2DNode);
1110     
1111      TiXmlElement* sourceSampler2DNode = new TiXmlElement("source");
1112      sampler2DNode->LinkEndChild(sourceSampler2DNode);  
1113      TiXmlText* sourceSampler2DText = new TiXmlText(avar("%s-surface", matNames[i].c_str()));
1114      sourceSampler2DNode->LinkEndChild(sourceSampler2DText);
1115
1116     // ---------------------------
1117
1118      TiXmlElement* techniqueNode = new TiXmlElement("technique");
1119      profileNode->LinkEndChild(techniqueNode);
1120      techniqueNode->SetAttribute("sid", "common");
1121
1122      TiXmlElement* blinnNode = new TiXmlElement("blinn");
1123      techniqueNode->LinkEndChild(blinnNode);
1124     
1125     // ---------------------------
1126     TiXmlElement* emissionNode = new TiXmlElement("emission");
1127     blinnNode->LinkEndChild(emissionNode);
1128
1129      TiXmlElement* colorEmissionNode = new TiXmlElement("color");
1130      emissionNode->LinkEndChild(colorEmissionNode);
1131     colorEmissionNode->SetAttribute("sid", "emission"); 
1132
1133     TiXmlText* colorEmissionNodeText = new TiXmlText("0.0 0.0 0.0 1.0");
1134     colorEmissionNode->LinkEndChild(colorEmissionNodeText);
1135     
1136     // ---------------------------
1137     TiXmlElement* ambientNode = new TiXmlElement("ambient");
1138     blinnNode->LinkEndChild(ambientNode);
1139
1140      TiXmlElement* colorAmbientNode = new TiXmlElement("color");
1141      ambientNode->LinkEndChild(colorAmbientNode);
1142     colorAmbientNode->SetAttribute("sid", "ambient"); 
1143
1144     TiXmlText* colorAmbientNodeText = new TiXmlText("0.0 0.0 0.0 1.0");
1145     colorAmbientNode->LinkEndChild(colorAmbientNodeText);    
1146     
1147     // ---------------------------
1148     TiXmlElement* diffuseNode = new TiXmlElement("diffuse");
1149      blinnNode->LinkEndChild(diffuseNode);
1150     TiXmlElement* textureDiffuseNode = new TiXmlElement("texture");
1151      diffuseNode->LinkEndChild(textureDiffuseNode);
1152     textureDiffuseNode->SetAttribute("texture", avar("%s-sampler", matNames[i].c_str()));
1153     textureDiffuseNode->SetAttribute("texcoord", "UVMap");
1154     
1155      // Extra info useful for getting the texture to show up correctly in MAYA and 3DS Max
1156      TiXmlElement* extraNode = new TiXmlElement("extra");
1157      textureDiffuseNode->LinkEndChild(extraNode);
1158
1159      TiXmlElement* extraTechNode = new TiXmlElement("technique");
1160      extraNode->LinkEndChild(extraTechNode);
1161      extraTechNode->SetAttribute("profile", "MAYA");
1162
1163      TiXmlElement* extraWrapUNode = new TiXmlElement("wrapU");
1164      extraTechNode->LinkEndChild(extraWrapUNode);
1165      extraWrapUNode->SetAttribute("sid", "wrapU0");
1166
1167      TiXmlText* extraWrapUText = new TiXmlText("TRUE");
1168      extraWrapUNode->LinkEndChild(extraWrapUText);
1169
1170      TiXmlElement* extraWrapVNode = new TiXmlElement("wrapV");
1171      extraTechNode->LinkEndChild(extraWrapVNode);
1172      extraWrapVNode->SetAttribute("sid", "wrapV0");
1173
1174      TiXmlText* extraWrapVText = new TiXmlText("TRUE");
1175      extraWrapVNode->LinkEndChild(extraWrapVText);
1176
1177      TiXmlElement* extraBlendNode = new TiXmlElement("blend_mode");
1178      extraTechNode->LinkEndChild(extraBlendNode);
1179
1180      TiXmlText* extraBlendText = new TiXmlText("ADD");
1181      extraBlendNode->LinkEndChild(extraBlendText);
1182     
1183     // ---------------------------
1184      TiXmlElement* specularNode = new TiXmlElement("specular");
1185      blinnNode->LinkEndChild(specularNode);
1186
1187      TiXmlElement* colorSpecularNode = new TiXmlElement("color");
1188      specularNode->LinkEndChild(colorSpecularNode);
1189     colorSpecularNode->SetAttribute("sid", "specular");
1190        
1191     TiXmlText* colorSpecularNodeText = new TiXmlText("0.5 0.5 0.5 1.0");
1192     colorSpecularNode->LinkEndChild(colorSpecularNodeText);
1193
1194     // --------------------------- 
1195      TiXmlElement* shininessNode = new TiXmlElement("shininess");
1196      blinnNode->LinkEndChild(shininessNode);
1197
1198      TiXmlElement* colorShininessNode = new TiXmlElement("float");
1199      shininessNode->LinkEndChild(colorShininessNode);
1200     colorShininessNode->SetAttribute("sid", "shininess");
1201        
1202     TiXmlText* colorShininessNodeText = new TiXmlText("1.0");
1203     colorShininessNode->LinkEndChild(colorShininessNodeText);
1204
1205     // --------------------------- 
1206      TiXmlElement* reflectiveNode = new TiXmlElement("reflective");
1207      blinnNode->LinkEndChild(reflectiveNode);
1208
1209      TiXmlElement* colorReflectiveNode = new TiXmlElement("color");
1210      reflectiveNode->LinkEndChild(colorReflectiveNode);
1211     colorReflectiveNode->SetAttribute("sid", "reflective");
1212        
1213     TiXmlText* colorReflectiveNodeText = new TiXmlText("0.0 0.0 0.0 1.0");
1214     colorReflectiveNode->LinkEndChild(colorReflectiveNodeText);    
1215
1216     // --------------------------- 
1217      TiXmlElement* reflectivityNode = new TiXmlElement("reflectivity");
1218      blinnNode->LinkEndChild(reflectivityNode);
1219
1220      TiXmlElement* floatReflectivityNode = new TiXmlElement("float");
1221      reflectivityNode->LinkEndChild(floatReflectivityNode);
1222     floatReflectivityNode->SetAttribute("sid", "reflectivity");
1223        
1224     TiXmlText* floatReflectivityText = new TiXmlText("0.5");
1225     floatReflectivityNode->LinkEndChild(floatReflectivityText);  
1226     
1227     // --------------------------- 
1228      TiXmlElement* transparentNode = new TiXmlElement("transparent");
1229      blinnNode->LinkEndChild(transparentNode);
1230     transparentNode->SetAttribute("opaque", "RGB_ZERO");
1231
1232      TiXmlElement* colorTransparentNode = new TiXmlElement("color");
1233      transparentNode->LinkEndChild(colorTransparentNode);
1234     colorTransparentNode->SetAttribute("sid", "transparent");
1235        
1236     TiXmlText* colorTransparentNodeText = new TiXmlText("0.0 0.0 0.0 1.0");
1237     colorTransparentNode->LinkEndChild(colorTransparentNodeText);   
1238
1239     // --------------------------- 
1240      TiXmlElement* transparencyNode = new TiXmlElement("transparency");
1241      blinnNode->LinkEndChild(transparencyNode);
1242
1243      TiXmlElement* floatTransparencyNode = new TiXmlElement("float");
1244      transparencyNode->LinkEndChild(floatTransparencyNode);
1245     floatTransparencyNode->SetAttribute("sid", "transparency");
1246        
1247     TiXmlText* floatTransparencyText = new TiXmlText("0.0");
1248     floatTransparencyNode->LinkEndChild(floatTransparencyText);   
1249     
1250     // ---------------------------   
1251      TiXmlElement* refractionNode = new TiXmlElement("index_of_refraction");
1252      blinnNode->LinkEndChild(refractionNode);
1253
1254      TiXmlElement* colorRefractionNode = new TiXmlElement("float");
1255      refractionNode->LinkEndChild(colorRefractionNode);
1256     colorRefractionNode->SetAttribute("sid", "index_of_refraction");
1257        
1258     TiXmlText* colorRefractionNodeText = new TiXmlText("1");
1259     colorRefractionNode->LinkEndChild(colorRefractionNodeText); 
1260   }
1261
1262   // Finally the material library
1263   TiXmlElement* matLibNode = new TiXmlElement("library_materials");
1264   rootNode->LinkEndChild(matLibNode);
1265
1266   for (U32 i = 0; i < mesh.mMaterialList.size(); i++)
1267   {
1268      BaseMatInstance* baseInst = mesh.mMaterialList[i];
1269
1270      Material* mat = dynamic_cast<Material*>(baseInst->getMaterial());
1271      if (!mat)
1272         continue;
1273
1274      TiXmlElement* materialNode = new TiXmlElement("material");
1275      matLibNode->LinkEndChild(materialNode);
1276      materialNode->SetAttribute("id", avar("%s-material", matNames[i].c_str()));
1277      materialNode->SetAttribute("name", matNames[i].c_str());
1278
1279      TiXmlElement* instEffectNode = new TiXmlElement("instance_effect");
1280      materialNode->LinkEndChild(instEffectNode);
1281      instEffectNode->SetAttribute("url", avar("#%s-effect", matNames[i].c_str()));
1282   }   
1283   
1284}
1285
1286void ColladaUtils::exportColladaMaterials(TiXmlElement* rootNode, const ExportData& exportData, const Torque::Path& colladaFile)
1287{
1288   // First the image library
1289   TiXmlElement* imgLibNode = new TiXmlElement("library_images");
1290   rootNode->LinkEndChild(imgLibNode);
1291
1292   Vector<String> matNames;
1293
1294   for (U32 i = 0; i < exportData.materials.size(); i++)
1295   {
1296      BaseMatInstance* baseInst = exportData.materials[i];
1297
1298      matNames.push_back(String::ToString("Material%d", i));
1299
1300      Material* mat = dynamic_cast<Material*>(baseInst->getMaterial());
1301      if (!mat)
1302         continue;
1303
1304      String diffuseMap;
1305
1306      if (mat->getName() && mat->getName()[0])
1307         matNames.last() = mat->mMapTo;
1308
1309      // Handle an auto-generated "Default Material" specially
1310      if (mat->isAutoGenerated())
1311      {
1312         Torque::Path diffusePath;
1313
1314         if (mat->mDiffuseMapFilename[0].isNotEmpty())
1315            diffusePath = mat->mDiffuseMapFilename[0];
1316         else
1317            diffusePath = String("warningMat");
1318
1319         matNames.last() = diffusePath.getFileName();
1320         diffuseMap += diffusePath.getFullFileName();
1321      }
1322      else
1323      {
1324         if (mat->mDiffuseMapFilename[0].isNotEmpty())
1325            diffuseMap += mat->mDiffuseMapFilename[0];
1326         else
1327            diffuseMap += "warningMat";
1328      }
1329
1330      Torque::Path diffusePath = findTexture(colladaFile.getPath() + "/" + diffuseMap);
1331
1332      // If we didn't get a path
1333      if (diffusePath.getFullPath().isNotEmpty())
1334         diffuseMap = Torque::Path::MakeRelativePath(diffusePath, colladaFile);
1335
1336      TiXmlElement* imageNode = new TiXmlElement("image");
1337      imgLibNode->LinkEndChild(imageNode);
1338      imageNode->SetAttribute("id", avar("%s", matNames.last().c_str()));
1339      imageNode->SetAttribute("name", avar("%s", matNames.last().c_str()));
1340
1341      TiXmlElement* initNode = new TiXmlElement("init_from");
1342      imageNode->LinkEndChild(initNode);
1343      TiXmlText* initText = new TiXmlText(avar("file://%s", diffuseMap.c_str())); // "the file://" is needed to load the texture in some old apps (ex: 3ds Max 2009)
1344      initNode->LinkEndChild(initText);
1345   }
1346
1347   // Next the effects library
1348   TiXmlElement* effectLibNode = new TiXmlElement("library_effects");
1349   rootNode->LinkEndChild(effectLibNode);
1350
1351   for (U32 i = 0; i < exportData.materials.size(); i++)
1352   {
1353      BaseMatInstance* baseInst = exportData.materials[i];
1354
1355      Material* mat = dynamic_cast<Material*>(baseInst->getMaterial());
1356      if (!mat)
1357         continue;
1358
1359      TiXmlElement* effectNode = new TiXmlElement("effect");
1360      effectLibNode->LinkEndChild(effectNode);
1361      effectNode->SetAttribute("id", avar("%s-effect", matNames[i].c_str()));
1362      effectNode->SetAttribute("name", avar("%s-effect", matNames[i].c_str()));
1363
1364      TiXmlElement* profileNode = new TiXmlElement("profile_COMMON");
1365      effectNode->LinkEndChild(profileNode);
1366
1367      // ---------------------------
1368      TiXmlElement* newParamNode = new TiXmlElement("newparam");
1369      profileNode->LinkEndChild(newParamNode);
1370      newParamNode->SetAttribute("sid", avar("%s-surface", matNames[i].c_str()));
1371
1372      TiXmlElement* surfaceNode = new TiXmlElement("surface");
1373      newParamNode->LinkEndChild(surfaceNode);
1374      surfaceNode->SetAttribute("type", "2D");
1375
1376      TiXmlElement* initNode2 = new TiXmlElement("init_from");
1377      surfaceNode->LinkEndChild(initNode2);
1378      TiXmlText* init2Text = new TiXmlText(avar("%s", matNames[i].c_str()));
1379      initNode2->LinkEndChild(init2Text);
1380
1381      TiXmlElement* formatNode = new TiXmlElement("format");
1382      surfaceNode->LinkEndChild(formatNode);
1383      TiXmlText* formatText = new TiXmlText("A8R8G8B8");
1384      formatNode->LinkEndChild(formatText);
1385
1386      // ---------------------------
1387      TiXmlElement* newParam2Node = new TiXmlElement("newparam");
1388      profileNode->LinkEndChild(newParam2Node);
1389      newParam2Node->SetAttribute("sid", avar("%s-sampler", matNames[i].c_str()));
1390
1391      TiXmlElement* sampler2DNode = new TiXmlElement("sampler2D");
1392      newParam2Node->LinkEndChild(sampler2DNode);
1393
1394      TiXmlElement* sourceSampler2DNode = new TiXmlElement("source");
1395      sampler2DNode->LinkEndChild(sourceSampler2DNode);
1396      TiXmlText* sourceSampler2DText = new TiXmlText(avar("%s-surface", matNames[i].c_str()));
1397      sourceSampler2DNode->LinkEndChild(sourceSampler2DText);
1398
1399      // ---------------------------
1400
1401      TiXmlElement* techniqueNode = new TiXmlElement("technique");
1402      profileNode->LinkEndChild(techniqueNode);
1403      techniqueNode->SetAttribute("sid", "common");
1404
1405      TiXmlElement* blinnNode = new TiXmlElement("blinn");
1406      techniqueNode->LinkEndChild(blinnNode);
1407
1408      // ---------------------------
1409      TiXmlElement* emissionNode = new TiXmlElement("emission");
1410      blinnNode->LinkEndChild(emissionNode);
1411
1412      TiXmlElement* colorEmissionNode = new TiXmlElement("color");
1413      emissionNode->LinkEndChild(colorEmissionNode);
1414      colorEmissionNode->SetAttribute("sid", "emission");
1415
1416      TiXmlText* colorEmissionNodeText = new TiXmlText("0.0 0.0 0.0 1.0");
1417      colorEmissionNode->LinkEndChild(colorEmissionNodeText);
1418
1419      // ---------------------------
1420      TiXmlElement* ambientNode = new TiXmlElement("ambient");
1421      blinnNode->LinkEndChild(ambientNode);
1422
1423      TiXmlElement* colorAmbientNode = new TiXmlElement("color");
1424      ambientNode->LinkEndChild(colorAmbientNode);
1425      colorAmbientNode->SetAttribute("sid", "ambient");
1426
1427      TiXmlText* colorAmbientNodeText = new TiXmlText("0.0 0.0 0.0 1.0");
1428      colorAmbientNode->LinkEndChild(colorAmbientNodeText);
1429
1430      // ---------------------------
1431      TiXmlElement* diffuseNode = new TiXmlElement("diffuse");
1432      blinnNode->LinkEndChild(diffuseNode);
1433      TiXmlElement* textureDiffuseNode = new TiXmlElement("texture");
1434      diffuseNode->LinkEndChild(textureDiffuseNode);
1435      textureDiffuseNode->SetAttribute("texture", avar("%s-sampler", matNames[i].c_str()));
1436      textureDiffuseNode->SetAttribute("texcoord", "UVMap");
1437
1438      // Extra info useful for getting the texture to show up correctly in MAYA and 3DS Max
1439      TiXmlElement* extraNode = new TiXmlElement("extra");
1440      textureDiffuseNode->LinkEndChild(extraNode);
1441
1442      TiXmlElement* extraTechNode = new TiXmlElement("technique");
1443      extraNode->LinkEndChild(extraTechNode);
1444      extraTechNode->SetAttribute("profile", "MAYA");
1445
1446      TiXmlElement* extraWrapUNode = new TiXmlElement("wrapU");
1447      extraTechNode->LinkEndChild(extraWrapUNode);
1448      extraWrapUNode->SetAttribute("sid", "wrapU0");
1449
1450      TiXmlText* extraWrapUText = new TiXmlText("TRUE");
1451      extraWrapUNode->LinkEndChild(extraWrapUText);
1452
1453      TiXmlElement* extraWrapVNode = new TiXmlElement("wrapV");
1454      extraTechNode->LinkEndChild(extraWrapVNode);
1455      extraWrapVNode->SetAttribute("sid", "wrapV0");
1456
1457      TiXmlText* extraWrapVText = new TiXmlText("TRUE");
1458      extraWrapVNode->LinkEndChild(extraWrapVText);
1459
1460      TiXmlElement* extraBlendNode = new TiXmlElement("blend_mode");
1461      extraTechNode->LinkEndChild(extraBlendNode);
1462
1463      TiXmlText* extraBlendText = new TiXmlText("ADD");
1464      extraBlendNode->LinkEndChild(extraBlendText);
1465
1466      // ---------------------------
1467      TiXmlElement* specularNode = new TiXmlElement("specular");
1468      blinnNode->LinkEndChild(specularNode);
1469
1470      TiXmlElement* colorSpecularNode = new TiXmlElement("color");
1471      specularNode->LinkEndChild(colorSpecularNode);
1472      colorSpecularNode->SetAttribute("sid", "specular");
1473
1474      TiXmlText* colorSpecularNodeText = new TiXmlText("0.5 0.5 0.5 1.0");
1475      colorSpecularNode->LinkEndChild(colorSpecularNodeText);
1476
1477      // ---------------------------   
1478      TiXmlElement* shininessNode = new TiXmlElement("shininess");
1479      blinnNode->LinkEndChild(shininessNode);
1480
1481      TiXmlElement* colorShininessNode = new TiXmlElement("float");
1482      shininessNode->LinkEndChild(colorShininessNode);
1483      colorShininessNode->SetAttribute("sid", "shininess");
1484
1485      TiXmlText* colorShininessNodeText = new TiXmlText("1.0");
1486      colorShininessNode->LinkEndChild(colorShininessNodeText);
1487
1488      // ---------------------------   
1489      TiXmlElement* reflectiveNode = new TiXmlElement("reflective");
1490      blinnNode->LinkEndChild(reflectiveNode);
1491
1492      TiXmlElement* colorReflectiveNode = new TiXmlElement("color");
1493      reflectiveNode->LinkEndChild(colorReflectiveNode);
1494      colorReflectiveNode->SetAttribute("sid", "reflective");
1495
1496      TiXmlText* colorReflectiveNodeText = new TiXmlText("0.0 0.0 0.0 1.0");
1497      colorReflectiveNode->LinkEndChild(colorReflectiveNodeText);
1498
1499      // ---------------------------   
1500      TiXmlElement* reflectivityNode = new TiXmlElement("reflectivity");
1501      blinnNode->LinkEndChild(reflectivityNode);
1502
1503      TiXmlElement* floatReflectivityNode = new TiXmlElement("float");
1504      reflectivityNode->LinkEndChild(floatReflectivityNode);
1505      floatReflectivityNode->SetAttribute("sid", "reflectivity");
1506
1507      TiXmlText* floatReflectivityText = new TiXmlText("0.5");
1508      floatReflectivityNode->LinkEndChild(floatReflectivityText);
1509
1510      // ---------------------------   
1511      TiXmlElement* transparentNode = new TiXmlElement("transparent");
1512      blinnNode->LinkEndChild(transparentNode);
1513      transparentNode->SetAttribute("opaque", "RGB_ZERO");
1514
1515      TiXmlElement* colorTransparentNode = new TiXmlElement("color");
1516      transparentNode->LinkEndChild(colorTransparentNode);
1517      colorTransparentNode->SetAttribute("sid", "transparent");
1518
1519      TiXmlText* colorTransparentNodeText = new TiXmlText("0.0 0.0 0.0 1.0");
1520      colorTransparentNode->LinkEndChild(colorTransparentNodeText);
1521
1522      // ---------------------------   
1523      TiXmlElement* transparencyNode = new TiXmlElement("transparency");
1524      blinnNode->LinkEndChild(transparencyNode);
1525
1526      TiXmlElement* floatTransparencyNode = new TiXmlElement("float");
1527      transparencyNode->LinkEndChild(floatTransparencyNode);
1528      floatTransparencyNode->SetAttribute("sid", "transparency");
1529
1530      TiXmlText* floatTransparencyText = new TiXmlText("0.0");
1531      floatTransparencyNode->LinkEndChild(floatTransparencyText);
1532
1533      // ---------------------------     
1534      TiXmlElement* refractionNode = new TiXmlElement("index_of_refraction");
1535      blinnNode->LinkEndChild(refractionNode);
1536
1537      TiXmlElement* colorRefractionNode = new TiXmlElement("float");
1538      refractionNode->LinkEndChild(colorRefractionNode);
1539      colorRefractionNode->SetAttribute("sid", "index_of_refraction");
1540
1541      TiXmlText* colorRefractionNodeText = new TiXmlText("1");
1542      colorRefractionNode->LinkEndChild(colorRefractionNodeText);
1543   }
1544
1545   // Finally the material library
1546   TiXmlElement* matLibNode = new TiXmlElement("library_materials");
1547   rootNode->LinkEndChild(matLibNode);
1548
1549   for (U32 i = 0; i < exportData.materials.size(); i++)
1550   {
1551      BaseMatInstance* baseInst = exportData.materials[i];
1552
1553      Material* mat = dynamic_cast<Material*>(baseInst->getMaterial());
1554      if (!mat)
1555         continue;
1556
1557      TiXmlElement* materialNode = new TiXmlElement("material");
1558      matLibNode->LinkEndChild(materialNode);
1559      materialNode->SetAttribute("id", avar("%s-material", matNames[i].c_str()));
1560      materialNode->SetAttribute("name", matNames[i].c_str());
1561
1562      TiXmlElement* instEffectNode = new TiXmlElement("instance_effect");
1563      materialNode->LinkEndChild(instEffectNode);
1564      instEffectNode->SetAttribute("url", avar("#%s-effect", matNames[i].c_str()));
1565   }
1566
1567}
1568
1569void ColladaUtils::exportColladaTriangles(TiXmlElement* meshNode, const OptimizedPolyList& mesh, const String& meshName, const Vector<String>& matNames)
1570{
1571   // Start at -1 so we will export polygons that do not have a material.
1572   for (S32 i = -1; i < matNames.size(); i++)
1573   {
1574      // Calculate the number of triangles that uses this Material
1575      U32 triangleCount = 0;
1576
1577      for (U32 j = 0; j < mesh.mPolyList.size(); j++)
1578      {
1579         const OptimizedPolyList::Poly& poly = mesh.mPolyList[j];
1580
1581         if (poly.material != i)
1582            continue;
1583
1584         if (poly.vertexCount < 3)
1585            continue;
1586
1587         if (poly.type == OptimizedPolyList::TriangleList ||
1588             poly.type == OptimizedPolyList::TriangleFan ||
1589             poly.type == OptimizedPolyList::TriangleStrip)
1590         {
1591            triangleCount += poly.vertexCount - 2;
1592         }
1593         else
1594            AssertISV(false, "ColladaUtils::exportColladaTriangles(): Unknown Poly type!");
1595      }
1596
1597      // Make sure that we are actually using this Material
1598      if (triangleCount == 0)
1599         continue;
1600
1601      TiXmlElement* trianglesNode = new TiXmlElement("triangles");
1602      meshNode->LinkEndChild(trianglesNode);
1603      trianglesNode->SetAttribute("material", ( i > -1 ) ? avar("%s-material", matNames[i].c_str()) : "" );
1604      trianglesNode->SetAttribute("count", avar("%d", triangleCount));
1605
1606      TiXmlElement* trianglesVertInputNode = new TiXmlElement("input");
1607      trianglesNode->LinkEndChild(trianglesVertInputNode);
1608      trianglesVertInputNode->SetAttribute("semantic", "VERTEX");
1609      trianglesVertInputNode->SetAttribute("source", avar("#%s-mesh-vertices", meshName.c_str()));
1610      trianglesVertInputNode->SetAttribute("offset", "0");
1611
1612      TiXmlElement* trianglesNormalInputNode = new TiXmlElement("input");
1613      trianglesNode->LinkEndChild(trianglesNormalInputNode);
1614      trianglesNormalInputNode->SetAttribute("semantic", "NORMAL");
1615      trianglesNormalInputNode->SetAttribute("source", avar("#%s-mesh-normals", meshName.c_str()));
1616      trianglesNormalInputNode->SetAttribute("offset", "1");
1617
1618      TiXmlElement* trianglesUV0InputNode = new TiXmlElement("input");
1619      trianglesNode->LinkEndChild(trianglesUV0InputNode);
1620      trianglesUV0InputNode->SetAttribute("semantic", "TEXCOORD");
1621      trianglesUV0InputNode->SetAttribute("source", avar("#%s-mesh-map-0", meshName.c_str()));
1622      trianglesUV0InputNode->SetAttribute("offset", "2");
1623      trianglesUV0InputNode->SetAttribute("set", "0");
1624
1625      TiXmlElement* polyNode = new TiXmlElement("p");
1626      trianglesNode->LinkEndChild(polyNode);
1627
1628      Vector<U32> tempIndices;
1629      tempIndices.reserve(4);
1630
1631      for (U32 j = 0; j < mesh.mPolyList.size(); j++)
1632      {
1633         const OptimizedPolyList::Poly& poly = mesh.mPolyList[j];
1634
1635         if (poly.vertexCount < 3)
1636            continue;
1637
1638         if (poly.material != i)
1639            continue;
1640
1641         tempIndices.setSize(poly.vertexCount);
1642         dMemset(tempIndices.address(), 0, poly.vertexCount);
1643
1644         if (poly.type == OptimizedPolyList::TriangleStrip)
1645         {
1646            tempIndices[0] = 0;
1647            U32 idx = 1;
1648            
1649            for (U32 k = 1; k < poly.vertexCount; k += 2)
1650               tempIndices[idx++] = k;
1651
1652            for (U32 k = ((poly.vertexCount - 1) & (~0x1)); k > 0; k -= 2)
1653               tempIndices[idx++] = k;
1654         }
1655         else if (poly.type == OptimizedPolyList::TriangleList ||
1656                  poly.type == OptimizedPolyList::TriangleFan)
1657         {
1658            for (U32 k = 0; k < poly.vertexCount; k++)
1659               tempIndices[k] = k;
1660         }
1661         else
1662            AssertISV(false, "ColladaUtils::exportColladaTriangles(): Unknown Poly type!");
1663
1664         const U32& firstIdx = mesh.mIndexList[poly.vertexStart];
1665         const OptimizedPolyList::VertIndex& firstVertIdx = mesh.mVertexList[firstIdx];
1666
1667         for (U32 k = 1; k < poly.vertexCount - 1; k++)
1668         {
1669            const U32& secondIdx = mesh.mIndexList[poly.vertexStart + tempIndices[k]];
1670            const U32& thirdIdx  = mesh.mIndexList[poly.vertexStart + tempIndices[k + 1]];
1671
1672            const OptimizedPolyList::VertIndex& secondVertIdx = mesh.mVertexList[secondIdx];
1673            const OptimizedPolyList::VertIndex& thirdVertIdx = mesh.mVertexList[thirdIdx];
1674
1675            // Note the reversed winding on the triangles
1676            const char* tri = avar("%d %d %d %d %d %d %d %d %d ",
1677                                   thirdVertIdx.vertIdx, thirdVertIdx.normalIdx, thirdVertIdx.uv0Idx,
1678                                   secondVertIdx.vertIdx, secondVertIdx.normalIdx, secondVertIdx.uv0Idx,
1679                                   firstVertIdx.vertIdx, firstVertIdx.normalIdx, firstVertIdx.uv0Idx);
1680
1681            TiXmlText* triangleText = new TiXmlText(tri);
1682            polyNode->LinkEndChild(triangleText);
1683         }
1684      }
1685   }
1686}
1687
1688void ColladaUtils::exportColladaTriangles(TiXmlElement* meshNode, const ExportData& exportData, const U32 detailLevel, const String& meshName)
1689{
1690   // Calculate the number of triangles that uses this Material
1691   U32 triangleCount = 0;
1692
1693   const ExportData::detailLevel* dl = &exportData.detailLevels[detailLevel];
1694
1695   for (S32 i = 0; i < dl->materialRefList.size(); i++)
1696   {
1697      int matIdx;
1698      dl->materialRefList.tryGetValue(i, matIdx);
1699      BaseMatInstance* baseInst = exportData.materials[matIdx];
1700
1701      Material* mat = dynamic_cast<Material*>(baseInst->getMaterial());
1702      if (!mat)
1703         continue;
1704
1705      String matName;
1706
1707      if (mat->getName() && mat->getName()[0])
1708         matName = mat->mMapTo;
1709
1710      for (U32 j = 0; j < dl->mesh.mPolyList.size(); j++)
1711      {
1712         const OptimizedPolyList::Poly& poly = dl->mesh.mPolyList[j];
1713
1714         if (poly.material != i)
1715            continue;
1716
1717         if (poly.vertexCount < 3)
1718            continue;
1719
1720         if (poly.type == OptimizedPolyList::TriangleList ||
1721            poly.type == OptimizedPolyList::TriangleFan ||
1722            poly.type == OptimizedPolyList::TriangleStrip)
1723         {
1724            triangleCount += poly.vertexCount - 2;
1725         }
1726         else
1727            AssertISV(false, "ColladaUtils::exportColladaTriangles(): Unknown Poly type!");
1728      }
1729
1730      // Make sure that we are actually using this Material
1731      if (triangleCount == 0)
1732         continue;
1733
1734      TiXmlElement* trianglesNode = new TiXmlElement("triangles");
1735      meshNode->LinkEndChild(trianglesNode);
1736      trianglesNode->SetAttribute("material", (i > -1) ? avar("%s-material", matName.c_str()) : "");
1737      trianglesNode->SetAttribute("count", avar("%d", triangleCount));
1738
1739      TiXmlElement* trianglesVertInputNode = new TiXmlElement("input");
1740      trianglesNode->LinkEndChild(trianglesVertInputNode);
1741      trianglesVertInputNode->SetAttribute("semantic", "VERTEX");
1742      trianglesVertInputNode->SetAttribute("source", avar("#%s-mesh-vertices", meshName.c_str()));
1743      trianglesVertInputNode->SetAttribute("offset", "0");
1744
1745      TiXmlElement* trianglesNormalInputNode = new TiXmlElement("input");
1746      trianglesNode->LinkEndChild(trianglesNormalInputNode);
1747      trianglesNormalInputNode->SetAttribute("semantic", "NORMAL");
1748      trianglesNormalInputNode->SetAttribute("source", avar("#%s-mesh-normals", meshName.c_str()));
1749      trianglesNormalInputNode->SetAttribute("offset", "1");
1750
1751      TiXmlElement* trianglesUV0InputNode = new TiXmlElement("input");
1752      trianglesNode->LinkEndChild(trianglesUV0InputNode);
1753      trianglesUV0InputNode->SetAttribute("semantic", "TEXCOORD");
1754      trianglesUV0InputNode->SetAttribute("source", avar("#%s-mesh-map-0", meshName.c_str()));
1755      trianglesUV0InputNode->SetAttribute("offset", "2");
1756      trianglesUV0InputNode->SetAttribute("set", "0");
1757
1758      TiXmlElement* polyNode = new TiXmlElement("p");
1759      trianglesNode->LinkEndChild(polyNode);
1760
1761      Vector<U32> tempIndices;
1762      tempIndices.reserve(4);
1763
1764      for (U32 j = 0; j < dl->mesh.mPolyList.size(); j++)
1765      {
1766         const OptimizedPolyList::Poly& poly = dl->mesh.mPolyList[j];
1767
1768         if (poly.vertexCount < 3)
1769            continue;
1770
1771         if (poly.material != i)
1772            continue;
1773
1774         tempIndices.setSize(poly.vertexCount);
1775         dMemset(tempIndices.address(), 0, poly.vertexCount);
1776
1777         if (poly.type == OptimizedPolyList::TriangleStrip)
1778         {
1779            tempIndices[0] = 0;
1780            U32 idx = 1;
1781
1782            for (U32 k = 1; k < poly.vertexCount; k += 2)
1783               tempIndices[idx++] = k;
1784
1785            for (U32 k = ((poly.vertexCount - 1) & (~0x1)); k > 0; k -= 2)
1786               tempIndices[idx++] = k;
1787         }
1788         else if (poly.type == OptimizedPolyList::TriangleList ||
1789            poly.type == OptimizedPolyList::TriangleFan)
1790         {
1791            for (U32 k = 0; k < poly.vertexCount; k++)
1792               tempIndices[k] = k;
1793         }
1794         else
1795            AssertISV(false, "ColladaUtils::exportColladaTriangles(): Unknown Poly type!");
1796
1797         const U32& firstIdx = dl->mesh.mIndexList[poly.vertexStart];
1798         const OptimizedPolyList::VertIndex& firstVertIdx = dl->mesh.mVertexList[firstIdx];
1799
1800         for (U32 k = 1; k < poly.vertexCount - 1; k++)
1801         {
1802            const U32& secondIdx = dl->mesh.mIndexList[poly.vertexStart + tempIndices[k]];
1803            const U32& thirdIdx = dl->mesh.mIndexList[poly.vertexStart + tempIndices[k + 1]];
1804
1805            const OptimizedPolyList::VertIndex& secondVertIdx = dl->mesh.mVertexList[secondIdx];
1806            const OptimizedPolyList::VertIndex& thirdVertIdx = dl->mesh.mVertexList[thirdIdx];
1807
1808            // Note the reversed winding on the triangles
1809            const char* tri = avar("%d %d %d %d %d %d %d %d %d ",
1810               thirdVertIdx.vertIdx, thirdVertIdx.normalIdx, thirdVertIdx.uv0Idx,
1811               secondVertIdx.vertIdx, secondVertIdx.normalIdx, secondVertIdx.uv0Idx,
1812               firstVertIdx.vertIdx, firstVertIdx.normalIdx, firstVertIdx.uv0Idx);
1813
1814            TiXmlText* triangleText = new TiXmlText(tri);
1815            polyNode->LinkEndChild(triangleText);
1816         }
1817      }
1818   }
1819}
1820
1821void ColladaUtils::exportColladaCollisionTriangles(TiXmlElement* meshNode, const ExportData& exportData, const U32 collisionIdx)
1822{
1823   // Calculate the number of triangles that uses this Material
1824   U32 triangleCount = 0;
1825
1826   const ExportData::colMesh* col = &exportData.colMeshes[collisionIdx];
1827
1828   String meshName = col->colMeshName;
1829
1830   for (U32 j = 0; j < col->mesh.mPolyList.size(); j++)
1831   {
1832      const OptimizedPolyList::Poly& poly = col->mesh.mPolyList[j];
1833
1834      if (poly.vertexCount < 3)
1835         continue;
1836
1837      if (poly.type == OptimizedPolyList::TriangleList ||
1838         poly.type == OptimizedPolyList::TriangleFan ||
1839         poly.type == OptimizedPolyList::TriangleStrip)
1840      {
1841         triangleCount += poly.vertexCount - 2;
1842      }
1843      else
1844         AssertISV(false, "ColladaUtils::exportColladaCollisionTriangles(): Unknown Poly type!");
1845   }
1846
1847   // Make sure that we are actually using this Material
1848   if (triangleCount == 0)
1849      return;
1850
1851   TiXmlElement* trianglesNode = new TiXmlElement("triangles");
1852   meshNode->LinkEndChild(trianglesNode);
1853   trianglesNode->SetAttribute("material", "");
1854   trianglesNode->SetAttribute("count", avar("%d", triangleCount));
1855
1856   TiXmlElement* trianglesVertInputNode = new TiXmlElement("input");
1857   trianglesNode->LinkEndChild(trianglesVertInputNode);
1858   trianglesVertInputNode->SetAttribute("semantic", "VERTEX");
1859   trianglesVertInputNode->SetAttribute("source", avar("#%s-mesh-vertices", meshName.c_str()));
1860   trianglesVertInputNode->SetAttribute("offset", "0");
1861
1862   TiXmlElement* trianglesNormalInputNode = new TiXmlElement("input");
1863   trianglesNode->LinkEndChild(trianglesNormalInputNode);
1864   trianglesNormalInputNode->SetAttribute("semantic", "NORMAL");
1865   trianglesNormalInputNode->SetAttribute("source", avar("#%s-mesh-normals", meshName.c_str()));
1866   trianglesNormalInputNode->SetAttribute("offset", "1");
1867
1868   TiXmlElement* trianglesUV0InputNode = new TiXmlElement("input");
1869   trianglesNode->LinkEndChild(trianglesUV0InputNode);
1870   trianglesUV0InputNode->SetAttribute("semantic", "TEXCOORD");
1871   trianglesUV0InputNode->SetAttribute("source", avar("#%s-mesh-map-0", meshName.c_str()));
1872   trianglesUV0InputNode->SetAttribute("offset", "2");
1873   trianglesUV0InputNode->SetAttribute("set", "0");
1874
1875   TiXmlElement* polyNode = new TiXmlElement("p");
1876   trianglesNode->LinkEndChild(polyNode);
1877
1878   Vector<U32> tempIndices;
1879   tempIndices.reserve(4);
1880
1881   for (U32 j = 0; j < col->mesh.mPolyList.size(); j++)
1882   {
1883      const OptimizedPolyList::Poly& poly = col->mesh.mPolyList[j];
1884
1885      if (poly.vertexCount < 3)
1886         continue;
1887
1888      tempIndices.setSize(poly.vertexCount);
1889      dMemset(tempIndices.address(), 0, poly.vertexCount);
1890
1891      if (poly.type == OptimizedPolyList::TriangleStrip)
1892      {
1893         tempIndices[0] = 0;
1894         U32 idx = 1;
1895
1896         for (U32 k = 1; k < poly.vertexCount; k += 2)
1897            tempIndices[idx++] = k;
1898
1899         for (U32 k = ((poly.vertexCount - 1) & (~0x1)); k > 0; k -= 2)
1900            tempIndices[idx++] = k;
1901      }
1902      else if (poly.type == OptimizedPolyList::TriangleList ||
1903         poly.type == OptimizedPolyList::TriangleFan)
1904      {
1905         for (U32 k = 0; k < poly.vertexCount; k++)
1906            tempIndices[k] = k;
1907      }
1908      else
1909         AssertISV(false, "ColladaUtils::exportColladaTriangles(): Unknown Poly type!");
1910
1911      const U32& firstIdx = col->mesh.mIndexList[poly.vertexStart];
1912      const OptimizedPolyList::VertIndex& firstVertIdx = col->mesh.mVertexList[firstIdx];
1913
1914      for (U32 k = 1; k < poly.vertexCount - 1; k++)
1915      {
1916         const U32& secondIdx = col->mesh.mIndexList[poly.vertexStart + tempIndices[k]];
1917         const U32& thirdIdx = col->mesh.mIndexList[poly.vertexStart + tempIndices[k + 1]];
1918
1919         const OptimizedPolyList::VertIndex& secondVertIdx = col->mesh.mVertexList[secondIdx];
1920         const OptimizedPolyList::VertIndex& thirdVertIdx = col->mesh.mVertexList[thirdIdx];
1921
1922         // Note the reversed winding on the triangles
1923         const char* tri = avar("%d %d %d %d %d %d %d %d %d ",
1924            thirdVertIdx.vertIdx, thirdVertIdx.normalIdx, thirdVertIdx.uv0Idx,
1925            secondVertIdx.vertIdx, secondVertIdx.normalIdx, secondVertIdx.uv0Idx,
1926            firstVertIdx.vertIdx, firstVertIdx.normalIdx, firstVertIdx.uv0Idx);
1927
1928         TiXmlText* triangleText = new TiXmlText(tri);
1929         polyNode->LinkEndChild(triangleText);
1930      }
1931   }
1932}
1933
1934void ColladaUtils::exportColladaMesh(TiXmlElement* rootNode, const OptimizedPolyList& mesh, const String& meshName, const Vector<String>& matNames)
1935{
1936   TiXmlElement* libGeomsNode = new TiXmlElement("library_geometries");
1937   rootNode->LinkEndChild(libGeomsNode);
1938
1939   TiXmlElement* geometryNode = new TiXmlElement("geometry");
1940   libGeomsNode->LinkEndChild(geometryNode);
1941   geometryNode->SetAttribute("id", avar("%s-mesh", meshName.c_str()));
1942   geometryNode->SetAttribute("name", avar("%s", meshName.c_str()));
1943
1944   TiXmlElement* meshNode = new TiXmlElement("mesh");
1945   geometryNode->LinkEndChild(meshNode);
1946
1947   // Save out the vertices
1948   TiXmlElement* vertsSourceNode = new TiXmlElement("source");
1949   meshNode->LinkEndChild(vertsSourceNode);
1950   vertsSourceNode->SetAttribute("id", avar("%s-mesh-positions", meshName.c_str()));
1951
1952   TiXmlElement* vertsNode = new TiXmlElement("float_array");
1953   vertsSourceNode->LinkEndChild(vertsNode);
1954   vertsNode->SetAttribute("id", avar("%s-mesh-positions-array", meshName.c_str()));
1955   vertsNode->SetAttribute("count", avar("%d", mesh.mPoints.size() * 3));
1956
1957   for (U32 i = 0; i < mesh.mPoints.size(); i++)
1958   {
1959      const Point3F& vert = mesh.mPoints[i];
1960
1961      TiXmlText* vertText = new TiXmlText(avar("%.4f %.4f %.4f ", vert.x, vert.y, vert.z));
1962      vertsNode->LinkEndChild(vertText);
1963   }
1964
1965   // Save the vertex accessor
1966   TiXmlElement* vertsTechNode = new TiXmlElement("technique_common");
1967   vertsSourceNode->LinkEndChild(vertsTechNode);
1968
1969   TiXmlElement* vertsAccNode = new TiXmlElement("accessor");
1970   vertsTechNode->LinkEndChild(vertsAccNode);
1971   vertsAccNode->SetAttribute("source", avar("#%s-mesh-positions-array", meshName.c_str()));
1972   vertsAccNode->SetAttribute("count", avar("%d", mesh.mPoints.size()));
1973   vertsAccNode->SetAttribute("stride", "3");
1974
1975   TiXmlElement* vertsAccXNode = new TiXmlElement("param");
1976   vertsAccNode->LinkEndChild(vertsAccXNode);
1977   vertsAccXNode->SetAttribute("name", "X");
1978   vertsAccXNode->SetAttribute("type", "float");
1979
1980   TiXmlElement* vertsAccYNode = new TiXmlElement("param");
1981   vertsAccNode->LinkEndChild(vertsAccYNode);
1982   vertsAccYNode->SetAttribute("name", "Y");
1983   vertsAccYNode->SetAttribute("type", "float");
1984
1985   TiXmlElement* vertsAccZNode = new TiXmlElement("param");
1986   vertsAccNode->LinkEndChild(vertsAccZNode);
1987   vertsAccZNode->SetAttribute("name", "Z");
1988   vertsAccZNode->SetAttribute("type", "float");
1989
1990   // Save out the normals
1991   TiXmlElement* normalsSourceNode = new TiXmlElement("source");
1992   meshNode->LinkEndChild(normalsSourceNode);
1993   normalsSourceNode->SetAttribute("id", avar("%s-mesh-normals", meshName.c_str()));
1994
1995   TiXmlElement* normalsNode = new TiXmlElement("float_array");
1996   normalsSourceNode->LinkEndChild(normalsNode);
1997   normalsNode->SetAttribute("id", avar("%s-mesh-normals-array", meshName.c_str()));
1998   normalsNode->SetAttribute("count", avar("%d", mesh.mNormals.size() * 3));
1999
2000   for (U32 i = 0; i < mesh.mNormals.size(); i++)
2001   {
2002      const Point3F& normal = mesh.mNormals[i];
2003
2004      TiXmlText* normalText = new TiXmlText(avar("%.4f %.4f %.4f ", normal.x, normal.y, normal.z));
2005      normalsNode->LinkEndChild(normalText);
2006   }
2007
2008   // Save the normals accessor
2009   TiXmlElement* normalsTechNode = new TiXmlElement("technique_common");
2010   normalsSourceNode->LinkEndChild(normalsTechNode);
2011
2012   TiXmlElement* normalsAccNode = new TiXmlElement("accessor");
2013   normalsTechNode->LinkEndChild(normalsAccNode);
2014   normalsAccNode->SetAttribute("source", avar("#%s-mesh-normals-array", meshName.c_str()));
2015   normalsAccNode->SetAttribute("count", avar("%d", mesh.mNormals.size()));
2016   normalsAccNode->SetAttribute("stride", "3");
2017
2018   TiXmlElement* normalsAccXNode = new TiXmlElement("param");
2019   normalsAccNode->LinkEndChild(normalsAccXNode);
2020   normalsAccXNode->SetAttribute("name", "X");
2021   normalsAccXNode->SetAttribute("type", "float");
2022
2023   TiXmlElement* normalsAccYNode = new TiXmlElement("param");
2024   normalsAccNode->LinkEndChild(normalsAccYNode);
2025   normalsAccYNode->SetAttribute("name", "Y");
2026   normalsAccYNode->SetAttribute("type", "float");
2027
2028   TiXmlElement* normalsAccZNode = new TiXmlElement("param");
2029   normalsAccNode->LinkEndChild(normalsAccZNode);
2030   normalsAccZNode->SetAttribute("name", "Z");
2031   normalsAccZNode->SetAttribute("type", "float");
2032
2033   // Save out the uvs
2034   TiXmlElement* uv0SourceNode = new TiXmlElement("source");
2035   meshNode->LinkEndChild(uv0SourceNode);
2036   uv0SourceNode->SetAttribute("id", avar("%s-mesh-map-0", meshName.c_str()));
2037
2038   TiXmlElement* uv0Node = new TiXmlElement("float_array");
2039   uv0SourceNode->LinkEndChild(uv0Node);
2040   uv0Node->SetAttribute("id", avar("%s-mesh-map-0-array", meshName.c_str()));
2041   uv0Node->SetAttribute("count", avar("%d", mesh.mUV0s.size() * 2));
2042
2043   for (U32 i = 0; i < mesh.mUV0s.size(); i++)
2044   {
2045      const Point2F& uv0 = mesh.mUV0s[i];
2046
2047      TiXmlText* uv0Text = new TiXmlText(avar("%.4f %.4f ", uv0.x, 1.0f - uv0.y));   // COLLADA uvs are upside down compared to Torque
2048      uv0Node->LinkEndChild(uv0Text);
2049   }
2050
2051   // Save the uv0 accessor
2052   TiXmlElement* uv0TechNode = new TiXmlElement("technique_common");
2053   uv0SourceNode->LinkEndChild(uv0TechNode);
2054
2055   TiXmlElement* uv0AccNode = new TiXmlElement("accessor");
2056   uv0TechNode->LinkEndChild(uv0AccNode);
2057   uv0AccNode->SetAttribute("source", avar("#%s-mesh-map-0-array", meshName.c_str()));
2058   uv0AccNode->SetAttribute("count", avar("%d", mesh.mUV0s.size()));
2059   uv0AccNode->SetAttribute("stride", "2");
2060
2061   TiXmlElement* uv0AccSNode = new TiXmlElement("param");
2062   uv0AccNode->LinkEndChild(uv0AccSNode);
2063   uv0AccSNode->SetAttribute("name", "S");
2064   uv0AccSNode->SetAttribute("type", "float");
2065
2066   TiXmlElement* uv0AccTNode = new TiXmlElement("param");
2067   uv0AccNode->LinkEndChild(uv0AccTNode);
2068   uv0AccTNode->SetAttribute("name", "T");
2069   uv0AccTNode->SetAttribute("type", "float");
2070
2071   // Define the vertices position array
2072   TiXmlElement* verticesNode = new TiXmlElement("vertices");
2073   meshNode->LinkEndChild(verticesNode);
2074   verticesNode->SetAttribute("id", avar("%s-mesh-vertices", meshName.c_str()));
2075
2076   TiXmlElement* verticesInputNode = new TiXmlElement("input");
2077   verticesNode->LinkEndChild(verticesInputNode);
2078   verticesInputNode->SetAttribute("semantic", "POSITION");
2079   verticesInputNode->SetAttribute("source", avar("#%s-mesh-positions", meshName.c_str()));
2080
2081   exportColladaTriangles(meshNode, mesh, meshName, matNames);
2082   
2083   // Extra info useful for COLLADAMaya importer (OpenCOLLADA)
2084   TiXmlElement* extraGeoNode = new TiXmlElement("extra");
2085   libGeomsNode->LinkEndChild(extraGeoNode);
2086
2087   TiXmlElement* extraGeoNodeTech = new TiXmlElement("technique");
2088   extraGeoNode->LinkEndChild(extraGeoNodeTech);
2089   extraGeoNodeTech->SetAttribute("profile", "OpenCOLLADAMaya");
2090
2091   TiXmlElement* mayaNode2Id = new TiXmlElement("originalMayaNodeId");
2092   extraGeoNodeTech->LinkEndChild(mayaNode2Id);
2093   mayaNode2Id->SetAttribute("sid", "originalMayaNodeId");
2094   TiXmlText* mayaIdMesh = new TiXmlText(avar("%s", meshName.c_str()));
2095   mayaNode2Id->LinkEndChild(mayaIdMesh);
2096
2097   TiXmlElement* doubleSidedId = new TiXmlElement("double_sided");
2098   extraGeoNodeTech->LinkEndChild(doubleSidedId);
2099   doubleSidedId->SetAttribute("sid", "double_sided");
2100   TiXmlText* doubleSideIdText = new TiXmlText("1");
2101   doubleSidedId->LinkEndChild(doubleSideIdText);
2102   
2103   TiXmlElement* paramExtraNode = new TiXmlElement("param");
2104   extraGeoNodeTech->LinkEndChild(paramExtraNode);
2105   paramExtraNode->SetAttribute("sid", "colladaId");
2106   paramExtraNode->SetAttribute("type", "string");
2107
2108   TiXmlText* mayaParamMesh = new TiXmlText(avar("%s-mesh", meshName.c_str()));
2109   paramExtraNode->LinkEndChild(mayaParamMesh);
2110   
2111
2112}
2113
2114void ColladaUtils::exportColladaMesh(TiXmlElement* rootNode, const ExportData& exportData, const String& meshName)
2115{
2116   TiXmlElement* libGeomsNode = new TiXmlElement("library_geometries");
2117   rootNode->LinkEndChild(libGeomsNode);
2118
2119   for (U32 d = 0; d < exportData.detailLevels.size(); d++)
2120   {
2121      char lodMeshName[256];
2122      dSprintf(lodMeshName, 256, "%s%d", meshName.c_str(), exportData.detailLevels[d].size);
2123
2124      char lodMeshID[256];
2125      dSprintf(lodMeshID, 256, "%s-mesh", lodMeshName);
2126
2127      TiXmlElement* geometryNode = new TiXmlElement("geometry");
2128      libGeomsNode->LinkEndChild(geometryNode);
2129      geometryNode->SetAttribute("id", lodMeshID);
2130      geometryNode->SetAttribute("name", lodMeshName);
2131
2132      TiXmlElement* meshNode = new TiXmlElement("mesh");
2133      geometryNode->LinkEndChild(meshNode);
2134
2135      // Save out the vertices
2136      TiXmlElement* vertsSourceNode = new TiXmlElement("source");
2137      meshNode->LinkEndChild(vertsSourceNode);
2138      vertsSourceNode->SetAttribute("id", avar("%s-mesh-positions", lodMeshName));
2139
2140      TiXmlElement* vertsNode = new TiXmlElement("float_array");
2141      vertsSourceNode->LinkEndChild(vertsNode);
2142      vertsNode->SetAttribute("id", avar("%s-mesh-positions-array", lodMeshName));
2143      vertsNode->SetAttribute("count", avar("%d", exportData.detailLevels[d].mesh.mPoints.size() * 3));
2144
2145      for (U32 i = 0; i < exportData.detailLevels[d].mesh.mPoints.size(); i++)
2146      {
2147         const Point3F& vert = exportData.detailLevels[d].mesh.mPoints[i];
2148
2149         TiXmlText* vertText = new TiXmlText(avar("%.4f %.4f %.4f ", vert.x, vert.y, vert.z));
2150         vertsNode->LinkEndChild(vertText);
2151      }
2152
2153      // Save the vertex accessor
2154      TiXmlElement* vertsTechNode = new TiXmlElement("technique_common");
2155      vertsSourceNode->LinkEndChild(vertsTechNode);
2156
2157      TiXmlElement* vertsAccNode = new TiXmlElement("accessor");
2158      vertsTechNode->LinkEndChild(vertsAccNode);
2159      vertsAccNode->SetAttribute("source", avar("#%s-mesh-positions-array", lodMeshName));
2160      vertsAccNode->SetAttribute("count", avar("%d", exportData.detailLevels[d].mesh.mPoints.size()));
2161      vertsAccNode->SetAttribute("stride", "3");
2162
2163      TiXmlElement* vertsAccXNode = new TiXmlElement("param");
2164      vertsAccNode->LinkEndChild(vertsAccXNode);
2165      vertsAccXNode->SetAttribute("name", "X");
2166      vertsAccXNode->SetAttribute("type", "float");
2167
2168      TiXmlElement* vertsAccYNode = new TiXmlElement("param");
2169      vertsAccNode->LinkEndChild(vertsAccYNode);
2170      vertsAccYNode->SetAttribute("name", "Y");
2171      vertsAccYNode->SetAttribute("type", "float");
2172
2173      TiXmlElement* vertsAccZNode = new TiXmlElement("param");
2174      vertsAccNode->LinkEndChild(vertsAccZNode);
2175      vertsAccZNode->SetAttribute("name", "Z");
2176      vertsAccZNode->SetAttribute("type", "float");
2177
2178      // Save out the normals
2179      TiXmlElement* normalsSourceNode = new TiXmlElement("source");
2180      meshNode->LinkEndChild(normalsSourceNode);
2181      normalsSourceNode->SetAttribute("id", avar("%s-mesh-normals", lodMeshName));
2182
2183      TiXmlElement* normalsNode = new TiXmlElement("float_array");
2184      normalsSourceNode->LinkEndChild(normalsNode);
2185      normalsNode->SetAttribute("id", avar("%s-mesh-normals-array", lodMeshName));
2186      normalsNode->SetAttribute("count", avar("%d", exportData.detailLevels[d].mesh.mNormals.size() * 3));
2187
2188      for (U32 i = 0; i < exportData.detailLevels[d].mesh.mNormals.size(); i++)
2189      {
2190         const Point3F& normal = exportData.detailLevels[d].mesh.mNormals[i];
2191
2192         TiXmlText* normalText = new TiXmlText(avar("%.4f %.4f %.4f ", normal.x, normal.y, normal.z));
2193         normalsNode->LinkEndChild(normalText);
2194      }
2195
2196      // Save the normals accessor
2197      TiXmlElement* normalsTechNode = new TiXmlElement("technique_common");
2198      normalsSourceNode->LinkEndChild(normalsTechNode);
2199
2200      TiXmlElement* normalsAccNode = new TiXmlElement("accessor");
2201      normalsTechNode->LinkEndChild(normalsAccNode);
2202      normalsAccNode->SetAttribute("source", avar("#%s-mesh-normals-array", lodMeshName));
2203      normalsAccNode->SetAttribute("count", avar("%d", exportData.detailLevels[d].mesh.mNormals.size()));
2204      normalsAccNode->SetAttribute("stride", "3");
2205
2206      TiXmlElement* normalsAccXNode = new TiXmlElement("param");
2207      normalsAccNode->LinkEndChild(normalsAccXNode);
2208      normalsAccXNode->SetAttribute("name", "X");
2209      normalsAccXNode->SetAttribute("type", "float");
2210
2211      TiXmlElement* normalsAccYNode = new TiXmlElement("param");
2212      normalsAccNode->LinkEndChild(normalsAccYNode);
2213      normalsAccYNode->SetAttribute("name", "Y");
2214      normalsAccYNode->SetAttribute("type", "float");
2215
2216      TiXmlElement* normalsAccZNode = new TiXmlElement("param");
2217      normalsAccNode->LinkEndChild(normalsAccZNode);
2218      normalsAccZNode->SetAttribute("name", "Z");
2219      normalsAccZNode->SetAttribute("type", "float");
2220
2221      // Save out the uvs
2222      TiXmlElement* uv0SourceNode = new TiXmlElement("source");
2223      meshNode->LinkEndChild(uv0SourceNode);
2224      uv0SourceNode->SetAttribute("id", avar("%s-mesh-map-0", lodMeshName));
2225
2226      TiXmlElement* uv0Node = new TiXmlElement("float_array");
2227      uv0SourceNode->LinkEndChild(uv0Node);
2228      uv0Node->SetAttribute("id", avar("%s-mesh-map-0-array", lodMeshName));
2229      uv0Node->SetAttribute("count", avar("%d", exportData.detailLevels[d].mesh.mUV0s.size() * 2));
2230
2231      for (U32 i = 0; i < exportData.detailLevels[d].mesh.mUV0s.size(); i++)
2232      {
2233         const Point2F& uv0 = exportData.detailLevels[d].mesh.mUV0s[i];
2234
2235         TiXmlText* uv0Text = new TiXmlText(avar("%.4f %.4f ", uv0.x, 1.0f - uv0.y));   // COLLADA uvs are upside down compared to Torque
2236         uv0Node->LinkEndChild(uv0Text);
2237      }
2238
2239      // Save the uv0 accessor
2240      TiXmlElement* uv0TechNode = new TiXmlElement("technique_common");
2241      uv0SourceNode->LinkEndChild(uv0TechNode);
2242
2243      TiXmlElement* uv0AccNode = new TiXmlElement("accessor");
2244      uv0TechNode->LinkEndChild(uv0AccNode);
2245      uv0AccNode->SetAttribute("source", avar("#%s-mesh-map-0-array", lodMeshName));
2246      uv0AccNode->SetAttribute("count", avar("%d", exportData.detailLevels[d].mesh.mUV0s.size()));
2247      uv0AccNode->SetAttribute("stride", "2");
2248
2249      TiXmlElement* uv0AccSNode = new TiXmlElement("param");
2250      uv0AccNode->LinkEndChild(uv0AccSNode);
2251      uv0AccSNode->SetAttribute("name", "S");
2252      uv0AccSNode->SetAttribute("type", "float");
2253
2254      TiXmlElement* uv0AccTNode = new TiXmlElement("param");
2255      uv0AccNode->LinkEndChild(uv0AccTNode);
2256      uv0AccTNode->SetAttribute("name", "T");
2257      uv0AccTNode->SetAttribute("type", "float");
2258
2259      // Define the vertices position array
2260      TiXmlElement* verticesNode = new TiXmlElement("vertices");
2261      meshNode->LinkEndChild(verticesNode);
2262      verticesNode->SetAttribute("id", avar("%s-mesh-vertices", lodMeshName));
2263
2264      TiXmlElement* verticesInputNode = new TiXmlElement("input");
2265      verticesNode->LinkEndChild(verticesInputNode);
2266      verticesInputNode->SetAttribute("semantic", "POSITION");
2267      verticesInputNode->SetAttribute("source", avar("#%s-mesh-positions", lodMeshName));
2268
2269      Vector<String> mapNames;
2270
2271      //exportColladaTriangles(meshNode, exportData.detailLevels[d].mesh, lodMeshName, mapNames);
2272      exportColladaTriangles(meshNode, exportData, d, lodMeshName);
2273
2274      // Extra info useful for COLLADAMaya importer (OpenCOLLADA)
2275      TiXmlElement* extraGeoNode = new TiXmlElement("extra");
2276      libGeomsNode->LinkEndChild(extraGeoNode);
2277
2278      TiXmlElement* extraGeoNodeTech = new TiXmlElement("technique");
2279      extraGeoNode->LinkEndChild(extraGeoNodeTech);
2280      extraGeoNodeTech->SetAttribute("profile", "OpenCOLLADAMaya");
2281
2282      TiXmlElement* mayaNode2Id = new TiXmlElement("originalMayaNodeId");
2283      extraGeoNodeTech->LinkEndChild(mayaNode2Id);
2284      mayaNode2Id->SetAttribute("sid", "originalMayaNodeId");
2285      TiXmlText* mayaIdMesh = new TiXmlText(avar("%s", lodMeshName));
2286      mayaNode2Id->LinkEndChild(mayaIdMesh);
2287
2288      TiXmlElement* doubleSidedId = new TiXmlElement("double_sided");
2289      extraGeoNodeTech->LinkEndChild(doubleSidedId);
2290      doubleSidedId->SetAttribute("sid", "double_sided");
2291      TiXmlText* doubleSideIdText = new TiXmlText("1");
2292      doubleSidedId->LinkEndChild(doubleSideIdText);
2293
2294      TiXmlElement* paramExtraNode = new TiXmlElement("param");
2295      extraGeoNodeTech->LinkEndChild(paramExtraNode);
2296      paramExtraNode->SetAttribute("sid", "colladaId");
2297      paramExtraNode->SetAttribute("type", "string");
2298
2299      TiXmlText* mayaParamMesh = new TiXmlText(avar("%s-mesh", lodMeshName));
2300      paramExtraNode->LinkEndChild(mayaParamMesh);
2301   }
2302
2303   //And now collisions
2304   for (U32 d = 0; d < exportData.colMeshes.size(); d++)
2305   {
2306      const char* colMeshName = exportData.colMeshes[d].colMeshName;
2307
2308      char colMeshId[256];
2309      dSprintf(colMeshId, 256, "%s-mesh", colMeshName);
2310
2311      TiXmlElement* geometryNode = new TiXmlElement("geometry");
2312      libGeomsNode->LinkEndChild(geometryNode);
2313      geometryNode->SetAttribute("id", colMeshId);
2314      geometryNode->SetAttribute("name", colMeshName);
2315
2316      TiXmlElement* meshNode = new TiXmlElement("mesh");
2317      geometryNode->LinkEndChild(meshNode);
2318
2319      // Save out the vertices
2320      TiXmlElement* vertsSourceNode = new TiXmlElement("source");
2321      meshNode->LinkEndChild(vertsSourceNode);
2322      vertsSourceNode->SetAttribute("id", avar("%s-mesh-positions", colMeshName));
2323
2324      TiXmlElement* vertsNode = new TiXmlElement("float_array");
2325      vertsSourceNode->LinkEndChild(vertsNode);
2326      vertsNode->SetAttribute("id", avar("%s-mesh-positions-array", colMeshName));
2327      vertsNode->SetAttribute("count", avar("%d", exportData.colMeshes[d].mesh.mPoints.size() * 3));
2328
2329      for (U32 i = 0; i < exportData.colMeshes[d].mesh.mPoints.size(); i++)
2330      {
2331         const Point3F& vert = exportData.colMeshes[d].mesh.mPoints[i];
2332
2333         TiXmlText* vertText = new TiXmlText(avar("%.4f %.4f %.4f ", vert.x, vert.y, vert.z));
2334         vertsNode->LinkEndChild(vertText);
2335      }
2336
2337      // Save the vertex accessor
2338      TiXmlElement* vertsTechNode = new TiXmlElement("technique_common");
2339      vertsSourceNode->LinkEndChild(vertsTechNode);
2340
2341      TiXmlElement* vertsAccNode = new TiXmlElement("accessor");
2342      vertsTechNode->LinkEndChild(vertsAccNode);
2343      vertsAccNode->SetAttribute("source", avar("#%s-mesh-positions-array", colMeshName));
2344      vertsAccNode->SetAttribute("count", avar("%d", exportData.colMeshes[d].mesh.mPoints.size()));
2345      vertsAccNode->SetAttribute("stride", "3");
2346
2347      TiXmlElement* vertsAccXNode = new TiXmlElement("param");
2348      vertsAccNode->LinkEndChild(vertsAccXNode);
2349      vertsAccXNode->SetAttribute("name", "X");
2350      vertsAccXNode->SetAttribute("type", "float");
2351
2352      TiXmlElement* vertsAccYNode = new TiXmlElement("param");
2353      vertsAccNode->LinkEndChild(vertsAccYNode);
2354      vertsAccYNode->SetAttribute("name", "Y");
2355      vertsAccYNode->SetAttribute("type", "float");
2356
2357      TiXmlElement* vertsAccZNode = new TiXmlElement("param");
2358      vertsAccNode->LinkEndChild(vertsAccZNode);
2359      vertsAccZNode->SetAttribute("name", "Z");
2360      vertsAccZNode->SetAttribute("type", "float");
2361
2362      // Save out the normals
2363      TiXmlElement* normalsSourceNode = new TiXmlElement("source");
2364      meshNode->LinkEndChild(normalsSourceNode);
2365      normalsSourceNode->SetAttribute("id", avar("%s-mesh-normals", colMeshName));
2366
2367      TiXmlElement* normalsNode = new TiXmlElement("float_array");
2368      normalsSourceNode->LinkEndChild(normalsNode);
2369      normalsNode->SetAttribute("id", avar("%s-mesh-normals-array", colMeshName));
2370      normalsNode->SetAttribute("count", avar("%d", exportData.colMeshes[d].mesh.mNormals.size() * 3));
2371
2372      for (U32 i = 0; i < exportData.colMeshes[d].mesh.mNormals.size(); i++)
2373      {
2374         const Point3F& normal = exportData.colMeshes[d].mesh.mNormals[i];
2375
2376         TiXmlText* normalText = new TiXmlText(avar("%.4f %.4f %.4f ", normal.x, normal.y, normal.z));
2377         normalsNode->LinkEndChild(normalText);
2378      }
2379
2380      // Save the normals accessor
2381      TiXmlElement* normalsTechNode = new TiXmlElement("technique_common");
2382      normalsSourceNode->LinkEndChild(normalsTechNode);
2383
2384      TiXmlElement* normalsAccNode = new TiXmlElement("accessor");
2385      normalsTechNode->LinkEndChild(normalsAccNode);
2386      normalsAccNode->SetAttribute("source", avar("#%s-mesh-normals-array", colMeshName));
2387      normalsAccNode->SetAttribute("count", avar("%d", exportData.colMeshes[d].mesh.mNormals.size()));
2388      normalsAccNode->SetAttribute("stride", "3");
2389
2390      TiXmlElement* normalsAccXNode = new TiXmlElement("param");
2391      normalsAccNode->LinkEndChild(normalsAccXNode);
2392      normalsAccXNode->SetAttribute("name", "X");
2393      normalsAccXNode->SetAttribute("type", "float");
2394
2395      TiXmlElement* normalsAccYNode = new TiXmlElement("param");
2396      normalsAccNode->LinkEndChild(normalsAccYNode);
2397      normalsAccYNode->SetAttribute("name", "Y");
2398      normalsAccYNode->SetAttribute("type", "float");
2399
2400      TiXmlElement* normalsAccZNode = new TiXmlElement("param");
2401      normalsAccNode->LinkEndChild(normalsAccZNode);
2402      normalsAccZNode->SetAttribute("name", "Z");
2403      normalsAccZNode->SetAttribute("type", "float");
2404
2405      // Save out the uvs
2406      TiXmlElement* uv0SourceNode = new TiXmlElement("source");
2407      meshNode->LinkEndChild(uv0SourceNode);
2408      uv0SourceNode->SetAttribute("id", avar("%s-mesh-map-0", colMeshName));
2409
2410      TiXmlElement* uv0Node = new TiXmlElement("float_array");
2411      uv0SourceNode->LinkEndChild(uv0Node);
2412      uv0Node->SetAttribute("id", avar("%s-mesh-map-0-array", colMeshName));
2413      uv0Node->SetAttribute("count", avar("%d", exportData.colMeshes[d].mesh.mUV0s.size() * 2));
2414
2415      for (U32 i = 0; i < exportData.colMeshes[d].mesh.mUV0s.size(); i++)
2416      {
2417         const Point2F& uv0 = exportData.colMeshes[d].mesh.mUV0s[i];
2418
2419         TiXmlText* uv0Text = new TiXmlText(avar("%.4f %.4f ", uv0.x, 1.0f - uv0.y));   // COLLADA uvs are upside down compared to Torque
2420         uv0Node->LinkEndChild(uv0Text);
2421      }
2422
2423      // Save the uv0 accessor
2424      TiXmlElement* uv0TechNode = new TiXmlElement("technique_common");
2425      uv0SourceNode->LinkEndChild(uv0TechNode);
2426
2427      TiXmlElement* uv0AccNode = new TiXmlElement("accessor");
2428      uv0TechNode->LinkEndChild(uv0AccNode);
2429      uv0AccNode->SetAttribute("source", avar("#%s-mesh-map-0-array", colMeshName));
2430      uv0AccNode->SetAttribute("count", avar("%d", exportData.colMeshes[d].mesh.mUV0s.size()));
2431      uv0AccNode->SetAttribute("stride", "2");
2432
2433      TiXmlElement* uv0AccSNode = new TiXmlElement("param");
2434      uv0AccNode->LinkEndChild(uv0AccSNode);
2435      uv0AccSNode->SetAttribute("name", "S");
2436      uv0AccSNode->SetAttribute("type", "float");
2437
2438      TiXmlElement* uv0AccTNode = new TiXmlElement("param");
2439      uv0AccNode->LinkEndChild(uv0AccTNode);
2440      uv0AccTNode->SetAttribute("name", "T");
2441      uv0AccTNode->SetAttribute("type", "float");
2442
2443      // Define the vertices position array
2444      TiXmlElement* verticesNode = new TiXmlElement("vertices");
2445      meshNode->LinkEndChild(verticesNode);
2446      verticesNode->SetAttribute("id", avar("%s-mesh-vertices", colMeshName));
2447
2448      TiXmlElement* verticesInputNode = new TiXmlElement("input");
2449      verticesNode->LinkEndChild(verticesInputNode);
2450      verticesInputNode->SetAttribute("semantic", "POSITION");
2451      verticesInputNode->SetAttribute("source", avar("#%s-mesh-positions", colMeshName));
2452
2453      Vector<String> mapNames;
2454
2455      //exportColladaTriangles(meshNode, exportData.detailLevels[d].mesh, lodMeshName, mapNames);
2456      exportColladaCollisionTriangles(meshNode, exportData, d);
2457
2458      // Extra info useful for COLLADAMaya importer (OpenCOLLADA)
2459      TiXmlElement* extraGeoNode = new TiXmlElement("extra");
2460      libGeomsNode->LinkEndChild(extraGeoNode);
2461
2462      TiXmlElement* extraGeoNodeTech = new TiXmlElement("technique");
2463      extraGeoNode->LinkEndChild(extraGeoNodeTech);
2464      extraGeoNodeTech->SetAttribute("profile", "OpenCOLLADAMaya");
2465
2466      TiXmlElement* mayaNode2Id = new TiXmlElement("originalMayaNodeId");
2467      extraGeoNodeTech->LinkEndChild(mayaNode2Id);
2468      mayaNode2Id->SetAttribute("sid", "originalMayaNodeId");
2469      TiXmlText* mayaIdMesh = new TiXmlText(avar("%s", colMeshName));
2470      mayaNode2Id->LinkEndChild(mayaIdMesh);
2471
2472      TiXmlElement* doubleSidedId = new TiXmlElement("double_sided");
2473      extraGeoNodeTech->LinkEndChild(doubleSidedId);
2474      doubleSidedId->SetAttribute("sid", "double_sided");
2475      TiXmlText* doubleSideIdText = new TiXmlText("1");
2476      doubleSidedId->LinkEndChild(doubleSideIdText);
2477
2478      TiXmlElement* paramExtraNode = new TiXmlElement("param");
2479      extraGeoNodeTech->LinkEndChild(paramExtraNode);
2480      paramExtraNode->SetAttribute("sid", "colladaId");
2481      paramExtraNode->SetAttribute("type", "string");
2482
2483      TiXmlText* mayaParamMesh = new TiXmlText(avar("%s-mesh", colMeshName));
2484      paramExtraNode->LinkEndChild(mayaParamMesh);
2485   }
2486}
2487
2488void ColladaUtils::exportColladaScene(TiXmlElement* rootNode, const String& meshName, const Vector<String>& matNames)
2489{
2490   TiXmlElement* libSceneNode = new TiXmlElement("library_visual_scenes");
2491   rootNode->LinkEndChild(libSceneNode);
2492
2493   TiXmlElement* visSceneNode = new TiXmlElement("visual_scene");
2494   libSceneNode->LinkEndChild(visSceneNode);
2495   visSceneNode->SetAttribute("id", "RootNode");
2496   visSceneNode->SetAttribute("name", "RootNode");
2497
2498   TiXmlElement* nodeNode = new TiXmlElement("node");
2499   visSceneNode->LinkEndChild(nodeNode);
2500   nodeNode->SetAttribute("id", avar("%s", meshName.c_str()));
2501   nodeNode->SetAttribute("name", avar("%s", meshName.c_str()));
2502   nodeNode->SetAttribute("type", "NODE");
2503   
2504   TiXmlElement* instanceGeomNode = new TiXmlElement("instance_geometry");
2505   nodeNode->LinkEndChild(instanceGeomNode);
2506   instanceGeomNode->SetAttribute("url", avar("#%s-mesh", meshName.c_str()));
2507   instanceGeomNode->SetAttribute("name", avar("%s", meshName.c_str()));
2508   
2509   TiXmlElement* bindMatNode = new TiXmlElement("bind_material");
2510   instanceGeomNode->LinkEndChild(bindMatNode);
2511
2512   TiXmlElement* techniqueNode = new TiXmlElement("technique_common");
2513   bindMatNode->LinkEndChild(techniqueNode);
2514
2515   // Bind the materials
2516   for (U32 i = 0; i < matNames.size(); i++)
2517   {
2518      TiXmlElement* instMatNode = new TiXmlElement("instance_material");
2519      techniqueNode->LinkEndChild(instMatNode);
2520      instMatNode->SetAttribute("symbol", avar("%s-material", matNames[i].c_str()));
2521      instMatNode->SetAttribute("target", avar("#%s-material", matNames[i].c_str()));
2522      TiXmlElement* bindVertexNode = new TiXmlElement("bind_vertex_input");
2523      instMatNode->LinkEndChild(bindVertexNode);
2524      //bindVertexNode->SetAttribute("semantic", avar("%s-mesh-map-0", meshName.c_str()));
2525      bindVertexNode->SetAttribute("semantic", "UVMap");
2526      bindVertexNode->SetAttribute("input_semantic", "TEXCOORD");
2527      bindVertexNode->SetAttribute("input_set", "0"); 
2528    }
2529   
2530   // Extra info useful for COLLADAMax importer (OpenCOLLADA)
2531    TiXmlElement* extraInsGeoNode = new TiXmlElement("extra");
2532    nodeNode->LinkEndChild(extraInsGeoNode);
2533
2534    TiXmlElement* extraInsGeoTechNode = new TiXmlElement("technique");
2535    extraInsGeoNode->LinkEndChild(extraInsGeoTechNode);
2536    extraInsGeoTechNode->SetAttribute("profile", "OpenCOLLADA");
2537
2538    TiXmlElement* castShadowsNode = new TiXmlElement("cast_shadows");
2539    extraInsGeoTechNode->LinkEndChild(castShadowsNode);
2540    castShadowsNode->SetAttribute("sid", "cast_shadows");
2541    castShadowsNode->SetAttribute("type", "bool");
2542   
2543   TiXmlText* castShadowsText = new TiXmlText("1");
2544   castShadowsNode->LinkEndChild(castShadowsText);
2545   
2546   //-----------------------------
2547    TiXmlElement* receiveShadowsNode = new TiXmlElement("receive_shadows");
2548    extraInsGeoTechNode->LinkEndChild(receiveShadowsNode);
2549    receiveShadowsNode->SetAttribute("sid", "receive_shadows");
2550    receiveShadowsNode->SetAttribute("type", "bool");
2551   
2552   TiXmlText* receiveShadowsText = new TiXmlText("1");
2553   receiveShadowsNode->LinkEndChild(receiveShadowsText); 
2554   
2555   //-----------------------------
2556    TiXmlElement* primaryVisibiltyNode = new TiXmlElement("primary_visibility");
2557    extraInsGeoTechNode->LinkEndChild(primaryVisibiltyNode);
2558    primaryVisibiltyNode->SetAttribute("sid", "primary_visibility");
2559    primaryVisibiltyNode->SetAttribute("type", "int");
2560   
2561   TiXmlText* primaryVisibiltyText = new TiXmlText("1");
2562   primaryVisibiltyNode->LinkEndChild(primaryVisibiltyText);
2563
2564   //-----------------------------
2565    TiXmlElement* secondaryVisibilityNode = new TiXmlElement("secondary_visibility");
2566    extraInsGeoTechNode->LinkEndChild(secondaryVisibilityNode);
2567    secondaryVisibilityNode->SetAttribute("sid", "secondary_visibility");
2568    secondaryVisibilityNode->SetAttribute("type", "int");
2569   
2570   TiXmlText* secondaryVisibilityText = new TiXmlText("1");
2571   secondaryVisibilityNode->LinkEndChild(secondaryVisibilityText);   
2572
2573   // Extra info useful for COLLADAMaya importer (OpenCOLLADA)
2574    TiXmlElement* extra2InsGeoNode = new TiXmlElement("extra");
2575    nodeNode->LinkEndChild(extra2InsGeoNode);
2576
2577    TiXmlElement* extra2InsGeoTechNode = new TiXmlElement("technique");
2578    extra2InsGeoNode->LinkEndChild(extra2InsGeoTechNode);
2579    extra2InsGeoTechNode->SetAttribute("profile", "OpenCOLLADAMaya");
2580
2581    TiXmlElement* mayaNodeId = new TiXmlElement("originalMayaNodeId");
2582    extra2InsGeoTechNode->LinkEndChild(mayaNodeId);
2583    mayaNodeId->SetAttribute("sid", "originalMayaNodeId");
2584    mayaNodeId->SetAttribute("type", "string");
2585
2586   TiXmlText* mayaNodeIdMesh = new TiXmlText(avar("%s", meshName.c_str()));
2587   mayaNodeId->LinkEndChild(mayaNodeIdMesh); 
2588
2589    TiXmlElement* paramExtraNode = new TiXmlElement("param");
2590    extra2InsGeoTechNode->LinkEndChild(paramExtraNode);
2591    paramExtraNode->SetAttribute("sid", "colladaId");
2592    paramExtraNode->SetAttribute("type", "string");   
2593
2594   TiXmlText* mayaParamMesh = new TiXmlText(avar("%s", meshName.c_str()));
2595   paramExtraNode->LinkEndChild(mayaParamMesh); 
2596   
2597   //-----------------------------
2598   
2599   TiXmlElement* sceneNode = new TiXmlElement("scene");
2600   rootNode->LinkEndChild(sceneNode);
2601
2602   TiXmlElement* instVisSceneNode = new TiXmlElement("instance_visual_scene");
2603   sceneNode->LinkEndChild(instVisSceneNode);
2604   instVisSceneNode->SetAttribute("url", "#RootNode");
2605}
2606
2607void ColladaUtils::exportColladaScene(TiXmlElement* rootNode, const ExportData& exportData, const String& meshName)
2608{
2609   TiXmlElement* libSceneNode = new TiXmlElement("library_visual_scenes");
2610   rootNode->LinkEndChild(libSceneNode);
2611
2612   TiXmlElement* visSceneNode = new TiXmlElement("visual_scene");
2613   libSceneNode->LinkEndChild(visSceneNode);
2614   visSceneNode->SetAttribute("id", "RootNode");
2615   visSceneNode->SetAttribute("name", "RootNode");
2616
2617   for (U32 d = 0; d < exportData.detailLevels.size(); d++)
2618   {
2619      char lodMeshName[256];
2620      dSprintf(lodMeshName, 256, "%s%d", meshName.c_str(), exportData.detailLevels[d].size);
2621
2622      TiXmlElement* nodeNode = new TiXmlElement("node");
2623      visSceneNode->LinkEndChild(nodeNode);
2624      nodeNode->SetAttribute("id", lodMeshName);
2625      nodeNode->SetAttribute("name", lodMeshName);
2626      nodeNode->SetAttribute("type", "NODE");
2627
2628      TiXmlElement* instanceGeomNode = new TiXmlElement("instance_geometry");
2629      nodeNode->LinkEndChild(instanceGeomNode);
2630      instanceGeomNode->SetAttribute("url", avar("#%s%d-mesh", meshName.c_str(), exportData.detailLevels[d].size));
2631      instanceGeomNode->SetAttribute("name", lodMeshName);
2632
2633      TiXmlElement* bindMatNode = new TiXmlElement("bind_material");
2634      instanceGeomNode->LinkEndChild(bindMatNode);
2635
2636      TiXmlElement* techniqueNode = new TiXmlElement("technique_common");
2637      bindMatNode->LinkEndChild(techniqueNode);
2638
2639      // Bind the materials
2640      for (U32 i = 0; i < exportData.detailLevels[d].materialRefList.size(); i++)
2641      {
2642         int matIdx;
2643         exportData.detailLevels[d].materialRefList.tryGetValue(i, matIdx);
2644         BaseMatInstance* baseInst = exportData.materials[matIdx];
2645
2646         Material* mat = dynamic_cast<Material*>(baseInst->getMaterial());
2647         if (!mat)
2648            continue;
2649
2650         String matName;
2651
2652         if (mat->getName() && mat->getName()[0])
2653            matName = mat->mMapTo;
2654
2655         TiXmlElement* instMatNode = new TiXmlElement("instance_material");
2656         techniqueNode->LinkEndChild(instMatNode);
2657         instMatNode->SetAttribute("symbol", avar("%s-material", matName.c_str()));
2658         instMatNode->SetAttribute("target", avar("#%s-material", matName.c_str()));
2659         TiXmlElement* bindVertexNode = new TiXmlElement("bind_vertex_input");
2660         instMatNode->LinkEndChild(bindVertexNode);
2661         //bindVertexNode->SetAttribute("semantic", avar("%s-mesh-map-0", meshName.c_str()));
2662         bindVertexNode->SetAttribute("semantic", "UVMap");
2663         bindVertexNode->SetAttribute("input_semantic", "TEXCOORD");
2664         bindVertexNode->SetAttribute("input_set", "0");
2665      }
2666
2667      // Extra info useful for COLLADAMax importer (OpenCOLLADA)
2668      TiXmlElement* extraInsGeoNode = new TiXmlElement("extra");
2669      nodeNode->LinkEndChild(extraInsGeoNode);
2670
2671      TiXmlElement* extraInsGeoTechNode = new TiXmlElement("technique");
2672      extraInsGeoNode->LinkEndChild(extraInsGeoTechNode);
2673      extraInsGeoTechNode->SetAttribute("profile", "OpenCOLLADA");
2674
2675      TiXmlElement* castShadowsNode = new TiXmlElement("cast_shadows");
2676      extraInsGeoTechNode->LinkEndChild(castShadowsNode);
2677      castShadowsNode->SetAttribute("sid", "cast_shadows");
2678      castShadowsNode->SetAttribute("type", "bool");
2679
2680      TiXmlText* castShadowsText = new TiXmlText("1");
2681      castShadowsNode->LinkEndChild(castShadowsText);
2682
2683      //-----------------------------
2684      TiXmlElement* receiveShadowsNode = new TiXmlElement("receive_shadows");
2685      extraInsGeoTechNode->LinkEndChild(receiveShadowsNode);
2686      receiveShadowsNode->SetAttribute("sid", "receive_shadows");
2687      receiveShadowsNode->SetAttribute("type", "bool");
2688
2689      TiXmlText* receiveShadowsText = new TiXmlText("1");
2690      receiveShadowsNode->LinkEndChild(receiveShadowsText);
2691
2692      //-----------------------------
2693      TiXmlElement* primaryVisibiltyNode = new TiXmlElement("primary_visibility");
2694      extraInsGeoTechNode->LinkEndChild(primaryVisibiltyNode);
2695      primaryVisibiltyNode->SetAttribute("sid", "primary_visibility");
2696      primaryVisibiltyNode->SetAttribute("type", "int");
2697
2698      TiXmlText* primaryVisibiltyText = new TiXmlText("1");
2699      primaryVisibiltyNode->LinkEndChild(primaryVisibiltyText);
2700
2701      //-----------------------------
2702      TiXmlElement* secondaryVisibilityNode = new TiXmlElement("secondary_visibility");
2703      extraInsGeoTechNode->LinkEndChild(secondaryVisibilityNode);
2704      secondaryVisibilityNode->SetAttribute("sid", "secondary_visibility");
2705      secondaryVisibilityNode->SetAttribute("type", "int");
2706
2707      TiXmlText* secondaryVisibilityText = new TiXmlText("1");
2708      secondaryVisibilityNode->LinkEndChild(secondaryVisibilityText);
2709
2710      // Extra info useful for COLLADAMaya importer (OpenCOLLADA)
2711      TiXmlElement* extra2InsGeoNode = new TiXmlElement("extra");
2712      nodeNode->LinkEndChild(extra2InsGeoNode);
2713
2714      TiXmlElement* extra2InsGeoTechNode = new TiXmlElement("technique");
2715      extra2InsGeoNode->LinkEndChild(extra2InsGeoTechNode);
2716      extra2InsGeoTechNode->SetAttribute("profile", "OpenCOLLADAMaya");
2717
2718      TiXmlElement* mayaNodeId = new TiXmlElement("originalMayaNodeId");
2719      extra2InsGeoTechNode->LinkEndChild(mayaNodeId);
2720      mayaNodeId->SetAttribute("sid", "originalMayaNodeId");
2721      mayaNodeId->SetAttribute("type", "string");
2722
2723      TiXmlText* mayaNodeIdMesh = new TiXmlText(lodMeshName);
2724      mayaNodeId->LinkEndChild(mayaNodeIdMesh);
2725
2726      TiXmlElement* paramExtraNode = new TiXmlElement("param");
2727      extra2InsGeoTechNode->LinkEndChild(paramExtraNode);
2728      paramExtraNode->SetAttribute("sid", "colladaId");
2729      paramExtraNode->SetAttribute("type", "string");
2730
2731      TiXmlText* mayaParamMesh = new TiXmlText(lodMeshName);
2732      paramExtraNode->LinkEndChild(mayaParamMesh);
2733   }
2734
2735   //Collisions
2736   for (U32 d = 0; d < exportData.colMeshes.size(); d++)
2737   {
2738      const char* colMeshName = exportData.colMeshes[d].colMeshName;
2739
2740      TiXmlElement* nodeNode = new TiXmlElement("node");
2741      visSceneNode->LinkEndChild(nodeNode);
2742      nodeNode->SetAttribute("id", colMeshName);
2743      nodeNode->SetAttribute("name", colMeshName);
2744      nodeNode->SetAttribute("type", "NODE");
2745
2746      TiXmlElement* instanceGeomNode = new TiXmlElement("instance_geometry");
2747      nodeNode->LinkEndChild(instanceGeomNode);
2748      instanceGeomNode->SetAttribute("url", avar("#%s-mesh", colMeshName));
2749      instanceGeomNode->SetAttribute("name", colMeshName);
2750
2751      TiXmlElement* bindMatNode = new TiXmlElement("bind_material");
2752      instanceGeomNode->LinkEndChild(bindMatNode);
2753
2754      TiXmlElement* techniqueNode = new TiXmlElement("technique_common");
2755      bindMatNode->LinkEndChild(techniqueNode);
2756
2757      TiXmlElement* instMatNode = new TiXmlElement("instance_material");
2758      techniqueNode->LinkEndChild(instMatNode);
2759      instMatNode->SetAttribute("symbol", avar("%s-material", colMeshName));
2760      instMatNode->SetAttribute("target", avar("#%s-material", colMeshName));
2761      TiXmlElement* bindVertexNode = new TiXmlElement("bind_vertex_input");
2762      instMatNode->LinkEndChild(bindVertexNode);
2763      //bindVertexNode->SetAttribute("semantic", avar("%s-mesh-map-0", meshName.c_str()));
2764      bindVertexNode->SetAttribute("semantic", "UVMap");
2765      bindVertexNode->SetAttribute("input_semantic", "TEXCOORD");
2766      bindVertexNode->SetAttribute("input_set", "0");
2767
2768      // Extra info useful for COLLADAMax importer (OpenCOLLADA)
2769      TiXmlElement* extraInsGeoNode = new TiXmlElement("extra");
2770      nodeNode->LinkEndChild(extraInsGeoNode);
2771
2772      TiXmlElement* extraInsGeoTechNode = new TiXmlElement("technique");
2773      extraInsGeoNode->LinkEndChild(extraInsGeoTechNode);
2774      extraInsGeoTechNode->SetAttribute("profile", "OpenCOLLADA");
2775
2776      TiXmlElement* castShadowsNode = new TiXmlElement("cast_shadows");
2777      extraInsGeoTechNode->LinkEndChild(castShadowsNode);
2778      castShadowsNode->SetAttribute("sid", "cast_shadows");
2779      castShadowsNode->SetAttribute("type", "bool");
2780
2781      TiXmlText* castShadowsText = new TiXmlText("1");
2782      castShadowsNode->LinkEndChild(castShadowsText);
2783
2784      //-----------------------------
2785      TiXmlElement* receiveShadowsNode = new TiXmlElement("receive_shadows");
2786      extraInsGeoTechNode->LinkEndChild(receiveShadowsNode);
2787      receiveShadowsNode->SetAttribute("sid", "receive_shadows");
2788      receiveShadowsNode->SetAttribute("type", "bool");
2789
2790      TiXmlText* receiveShadowsText = new TiXmlText("1");
2791      receiveShadowsNode->LinkEndChild(receiveShadowsText);
2792
2793      //-----------------------------
2794      TiXmlElement* primaryVisibiltyNode = new TiXmlElement("primary_visibility");
2795      extraInsGeoTechNode->LinkEndChild(primaryVisibiltyNode);
2796      primaryVisibiltyNode->SetAttribute("sid", "primary_visibility");
2797      primaryVisibiltyNode->SetAttribute("type", "int");
2798
2799      TiXmlText* primaryVisibiltyText = new TiXmlText("1");
2800      primaryVisibiltyNode->LinkEndChild(primaryVisibiltyText);
2801
2802      //-----------------------------
2803      TiXmlElement* secondaryVisibilityNode = new TiXmlElement("secondary_visibility");
2804      extraInsGeoTechNode->LinkEndChild(secondaryVisibilityNode);
2805      secondaryVisibilityNode->SetAttribute("sid", "secondary_visibility");
2806      secondaryVisibilityNode->SetAttribute("type", "int");
2807
2808      TiXmlText* secondaryVisibilityText = new TiXmlText("1");
2809      secondaryVisibilityNode->LinkEndChild(secondaryVisibilityText);
2810
2811      // Extra info useful for COLLADAMaya importer (OpenCOLLADA)
2812      TiXmlElement* extra2InsGeoNode = new TiXmlElement("extra");
2813      nodeNode->LinkEndChild(extra2InsGeoNode);
2814
2815      TiXmlElement* extra2InsGeoTechNode = new TiXmlElement("technique");
2816      extra2InsGeoNode->LinkEndChild(extra2InsGeoTechNode);
2817      extra2InsGeoTechNode->SetAttribute("profile", "OpenCOLLADAMaya");
2818
2819      TiXmlElement* mayaNodeId = new TiXmlElement("originalMayaNodeId");
2820      extra2InsGeoTechNode->LinkEndChild(mayaNodeId);
2821      mayaNodeId->SetAttribute("sid", "originalMayaNodeId");
2822      mayaNodeId->SetAttribute("type", "string");
2823
2824      TiXmlText* mayaNodeIdMesh = new TiXmlText(colMeshName);
2825      mayaNodeId->LinkEndChild(mayaNodeIdMesh);
2826
2827      TiXmlElement* paramExtraNode = new TiXmlElement("param");
2828      extra2InsGeoTechNode->LinkEndChild(paramExtraNode);
2829      paramExtraNode->SetAttribute("sid", "colladaId");
2830      paramExtraNode->SetAttribute("type", "string");
2831
2832      TiXmlText* mayaParamMesh = new TiXmlText(colMeshName);
2833      paramExtraNode->LinkEndChild(mayaParamMesh);
2834   }
2835   //-----------------------------
2836
2837   TiXmlElement* sceneNode = new TiXmlElement("scene");
2838   rootNode->LinkEndChild(sceneNode);
2839
2840   TiXmlElement* instVisSceneNode = new TiXmlElement("instance_visual_scene");
2841   sceneNode->LinkEndChild(instVisSceneNode);
2842   instVisSceneNode->SetAttribute("url", "#RootNode");
2843}
2844
2845void ColladaUtils::exportToCollada(const Torque::Path& colladaFile, const OptimizedPolyList& mesh, const String& meshName)
2846{
2847   // Get the mesh name
2848   String outMeshName = meshName;
2849
2850   if (outMeshName.isEmpty())
2851      outMeshName = colladaFile.getFileName();
2852
2853   // The XML document that will hold all of our data
2854   TiXmlDocument doc;
2855
2856   // Add a standard XML declaration to the top
2857   TiXmlDeclaration* xmlDecl = new TiXmlDeclaration("1.0", "utf-8", "");
2858   doc.LinkEndChild(xmlDecl);
2859
2860   // Create our Collada root node and populate a couple standard attributes
2861   TiXmlElement* rootNode = new TiXmlElement("COLLADA");
2862   rootNode->SetAttribute("xmlns", "http://www.collada.org/2005/11/COLLADASchema");
2863   rootNode->SetAttribute("version", "1.4.1");
2864   //rootNode->SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); //T3D Collada loader complaint about this.
2865   
2866
2867   // Add the root node to the document
2868   doc.LinkEndChild(rootNode);
2869
2870   // Save out our header info
2871   exportColladaHeader(rootNode);
2872
2873   // Save out the materials
2874   Vector<String> mapNames;
2875
2876   exportColladaMaterials(rootNode, mesh, mapNames, colladaFile);
2877
2878   S32 suffix;
2879   String baseMeshName = String::GetTrailingNumber(outMeshName, suffix);
2880
2881   // Save out our geometry
2882   exportColladaMesh(rootNode, mesh, baseMeshName, mapNames);
2883
2884   // Save out our scene nodes
2885   exportColladaScene(rootNode, baseMeshName, mapNames);
2886
2887   // Write out the actual Collada file
2888   char fullPath[MAX_PATH_LENGTH];
2889   Platform::makeFullPathName(colladaFile.getFullPath(), fullPath, MAX_PATH_LENGTH);
2890
2891   if (!doc.SaveFile(fullPath))
2892      Con::errorf("ColladaUtils::exportToCollada(): Unable to export to %s", fullPath);
2893}
2894
2895void ColladaUtils::exportToCollada(const Torque::Path& colladaFile, const ExportData& exportData)
2896{
2897   // Get the mesh name
2898   String outMeshName = colladaFile.getFileName();
2899
2900   // The XML document that will hold all of our data
2901   TiXmlDocument doc;
2902
2903   // Add a standard XML declaration to the top
2904   TiXmlDeclaration* xmlDecl = new TiXmlDeclaration("1.0", "utf-8", "");
2905   doc.LinkEndChild(xmlDecl);
2906
2907   // Create our Collada root node and populate a couple standard attributes
2908   TiXmlElement* rootNode = new TiXmlElement("COLLADA");
2909   rootNode->SetAttribute("xmlns", "http://www.collada.org/2005/11/COLLADASchema");
2910   rootNode->SetAttribute("version", "1.4.1");
2911   //rootNode->SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); //T3D Collada loader complaint about this.
2912
2913
2914   // Add the root node to the document
2915   doc.LinkEndChild(rootNode);
2916
2917   // Save out our header info
2918   exportColladaHeader(rootNode);
2919
2920   // Save out the materials
2921   Vector<String> mapNames;
2922
2923   exportColladaMaterials(rootNode, exportData, colladaFile);
2924
2925   S32 suffix;
2926   String baseMeshName = String::GetTrailingNumber(outMeshName, suffix);
2927
2928   // Save out our geometry
2929   exportColladaMesh(rootNode, exportData, baseMeshName);
2930
2931   // Save out our scene nodes
2932   exportColladaScene(rootNode, exportData, baseMeshName);
2933
2934   // Write out the actual Collada file
2935   char fullPath[MAX_PATH_LENGTH];
2936   Platform::makeFullPathName(colladaFile.getFullPath(), fullPath, MAX_PATH_LENGTH);
2937
2938   if (!doc.SaveFile(fullPath))
2939      Con::errorf("ColladaUtils::exportToCollada(): Unable to export to %s", fullPath);
2940}
2941
2942void ColladaUtils::ExportData::processData()
2943{
2944   //This pref dictates if we 'backfill' lower LODs with higher ones if any given mesh being exported lacks that level.
2945   //For example, if there are 2 meshes, and one has 500, 200, 100 and the other has 500 and 200 - if this setting is on, the second mesh
2946   //will backfill the 200 to the 100 so it has all levels filled. If it's off, the second mesh will not render at the 100 level
2947   bool fillLowDetailLevels = dAtob(Con::getVariable("$exportMesh::fillLowDetailLevels", "1"));
2948
2949   S32 numDetailLevels = numberOfDetailLevels();
2950
2951   detailLevels.clear();
2952   detailLevels.setSize(numDetailLevels);
2953
2954   for (U32 meshNum = 0; meshNum < meshData.size(); ++meshNum)
2955   {
2956      for (U32 i = 0; i < numDetailLevels; ++i)
2957      {
2958         //Get our target size
2959         S32 targetDetailLevelSize = getDetailLevelSize(i);
2960
2961         //alright, step through each meshdata and propagate the polyList info 'up' to fill 
2962         detailLevel* curDetail = &detailLevels[i];
2963
2964         curDetail->size = targetDetailLevelSize;
2965      
2966         //Do we have a detail level for this?
2967         S32 detailLevelIdx = -1;
2968
2969         for (S32 mdl = i; mdl >= 0; mdl--)
2970         {
2971            //walk backwards as needed to find our first valid detail level for this mesh. if we find none, just move on
2972            S32 testDetailLevelSize = getDetailLevelSize(mdl);
2973            detailLevelIdx = meshData[meshNum].hasDetailLevel(testDetailLevelSize);
2974
2975            if (detailLevelIdx != -1)
2976               break;
2977         }
2978
2979         if (detailLevelIdx == -1)
2980         {
2981            //found nothing backwards, so lets check if we're configured to back-fill the first detail levels
2982            if (fillLowDetailLevels)
2983            {
2984               //if so, search forward, find the first valid detail and fill it in
2985               for (S32 mdl = 0; mdl < numDetailLevels; mdl++)
2986               {
2987                  //walk backwards as needed to find our first valid detail level for this mesh. if we find none, just move on
2988                  S32 testDetailLevelSize = getDetailLevelSize(mdl);
2989                  detailLevelIdx = meshData[meshNum].hasDetailLevel(testDetailLevelSize);
2990
2991                  if (detailLevelIdx != -1)
2992                     break;
2993               }
2994            }
2995         }
2996
2997         //If we found the detail level index, go ahead and build out the data for it
2998         if (detailLevelIdx != -1)
2999         {
3000            curDetail->mesh.setTransform(&meshData[meshNum].meshTransform, meshData[meshNum].scale);
3001            curDetail->mesh.setObject(meshData[meshNum].originatingObject);
3002
3003            if (meshData[meshNum].shapeInst != nullptr)
3004            {
3005
3006               if (!meshData[meshNum].shapeInst->buildPolyList(&curDetail->mesh, detailLevelIdx))
3007               {
3008                  Con::errorf("TSStatic::buildExportPolyList - failed to build polylist for LOD %i", i);
3009                  continue;
3010               }
3011            }
3012            else
3013            {
3014               //special handling classes
3015               ConvexShape* convexShp = dynamic_cast<ConvexShape*>(meshData[meshNum].originatingObject);
3016               if (convexShp != nullptr)
3017               {
3018                  if (!convexShp->buildPolyList(PLC_Export, &curDetail->mesh, meshData[meshNum].originatingObject->getWorldBox(), meshData[meshNum].originatingObject->getWorldSphere()))
3019                  {
3020                     Con::errorf("TSStatic::buildExportPolyList - failed to build ConvexShape polylist for LOD %i", i);
3021                     continue;
3022                  }
3023               }
3024            }
3025
3026            //lastly, get material
3027            for (U32 matNum = 0; matNum < curDetail->mesh.mMaterialList.size(); matNum++)
3028            {
3029               S32 matIdx = hasMaterialInstance(curDetail->mesh.mMaterialList[matNum]);
3030
3031               if (matIdx == -1)
3032               {
3033                  //cool, haven't already got this material, so lets store it out
3034                  materials.push_back(curDetail->mesh.mMaterialList[matNum]);
3035                  curDetail->materialRefList.insert(matNum, materials.size() - 1);
3036               }
3037               else
3038               {
3039                  curDetail->materialRefList.insert(matNum, matIdx);
3040               }
3041            }
3042         }
3043      }
3044   }
3045}
3046