colladaUtils.cpp
Engine/source/ts/collada/colladaUtils.cpp
Public Defines
define
CREATE_ELEMENT(container, name, type) type* name = daeSafeCast<type>(container->createAndPlace(#name));
define
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)
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