tsShapeLoader.cpp
Engine/source/ts/loader/tsShapeLoader.cpp
Public Typedefs
bool(*
NameCmpFunc )(const String &, const Vector< String > &, void *, void *)
Public Variables
Public Functions
bool
DefineEngineFunction(getFormatExtensions , const char * , () , "Returns <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> list of supported shape format extensions separated by tabs." "Example output: *.dsq TAB *.dae TAB" )
DefineEngineFunction(getFormatFilters , const char * , () , "Returns <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> list of supported shape formats in filter <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">form.\n</a>" "Example output: DSQ Files|*.dsq|COLLADA Files|*.dae|" )
DefineEngineFunction(isSupportedFormat , bool , (const char *extension) , "" )
getUniqueName(const char * name, NameCmpFunc isNameUnique, const Vector< String > & names, void * arg1, void * arg2)
Detailed Description
Public Typedefs
typedef bool(* NameCmpFunc )(const String &, const Vector< String > &, void *, void *)
Public Variables
MODULE_END
MODULE_INIT
Public Functions
cmpMeshNameAndSize(const String & key, const Vector< String > & names, void * arg1, void * arg2)
cmpShapeName(const String & key, const Vector< String > & names, void * arg1, void * arg2)
DefineEngineFunction(getFormatExtensions , const char * , () , "Returns <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> list of supported shape format extensions separated by tabs." "Example output: *.dsq TAB *.dae TAB" )
DefineEngineFunction(getFormatFilters , const char * , () , "Returns <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> list of supported shape formats in filter <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">form.\n</a>" "Example output: DSQ Files|*.dsq|COLLADA Files|*.dae|" )
DefineEngineFunction(isSupportedFormat , bool , (const char *extension) , "" )
getUniqueName(const char * name, NameCmpFunc isNameUnique, const Vector< String > & names, void * arg1, void * arg2)
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 "platform/platform.h" 25#include "console/engineAPI.h" 26#include "ts/loader/tsShapeLoader.h" 27 28#include "core/volume.h" 29#include "materials/materialList.h" 30#include "materials/matInstance.h" 31#include "materials/materialManager.h" 32#include "ts/tsShapeInstance.h" 33#include "ts/tsMaterialList.h" 34 35MODULE_BEGIN( ShapeLoader ) 36 MODULE_INIT_AFTER( GFX ) 37 MODULE_INIT 38 { 39 TSShapeLoader::addFormat("Torque DTS", "dts"); 40 TSShapeLoader::addFormat("Torque DSQ", "dsq"); 41 } 42MODULE_END; 43 44const F32 TSShapeLoader::DefaultTime = -1.0f; 45const F64 TSShapeLoader::MinFrameRate = 15.0f; 46const F64 TSShapeLoader::MaxFrameRate = 60.0f; 47const F64 TSShapeLoader::AppGroundFrameRate = 10.0f; 48Torque::Path TSShapeLoader::shapePath; 49 50Vector<TSShapeLoader::ShapeFormat> TSShapeLoader::smFormats; 51 52//------------------------------------------------------------------------------ 53// Utility functions 54 55void TSShapeLoader::zapScale(MatrixF& mat) 56{ 57 Point3F invScale = mat.getScale(); 58 invScale.x = invScale.x ? (1.0f / invScale.x) : 0; 59 invScale.y = invScale.y ? (1.0f / invScale.y) : 0; 60 invScale.z = invScale.z ? (1.0f / invScale.z) : 0; 61 mat.scale(invScale); 62} 63 64//------------------------------------------------------------------------------ 65// Shape utility functions 66 67MatrixF TSShapeLoader::getLocalNodeMatrix(AppNode* node, F32 t) 68{ 69 MatrixF m1 = node->getNodeTransform(t); 70 71 // multiply by inverse scale at t=0 72 MatrixF m10 = node->getNodeTransform(DefaultTime); 73 m1.scale(Point3F(1.0f/m10.getScale().x, 1.0f/m10.getScale().y, 1.0f/m10.getScale().z)); 74 75 if (node->mParentIndex >= 0) 76 { 77 AppNode *parent = appNodes[node->mParentIndex]; 78 79 MatrixF m2 = parent->getNodeTransform(t); 80 81 // multiply by inverse scale at t=0 82 MatrixF m20 = parent->getNodeTransform(DefaultTime); 83 m2.scale(Point3F(1.0f/m20.getScale().x, 1.0f/m20.getScale().y, 1.0f/m20.getScale().z)); 84 85 // get local transform by pre-multiplying by inverted parent transform 86 m1 = m2.inverse() * m1; 87 } 88 else if (boundsNode && node != boundsNode) 89 { 90 // make transform relative to bounds node transform at time=t 91 MatrixF mb = boundsNode->getNodeTransform(t); 92 zapScale(mb); 93 m1 = mb.inverse() * m1; 94 } 95 96 return m1; 97} 98 99void TSShapeLoader::generateNodeTransform(AppNode* node, F32 t, bool blend, F32 referenceTime, 100 QuatF& rot, Point3F& trans, QuatF& srot, Point3F& scale) 101{ 102 MatrixF m1 = getLocalNodeMatrix(node, t); 103 if (blend) 104 { 105 MatrixF m0 = getLocalNodeMatrix(node, referenceTime); 106 m1 = m0.inverse() * m1; 107 } 108 109 rot.set(m1); 110 trans = m1.getPosition(); 111 srot.identity(); //@todo: srot not supported yet 112 scale = m1.getScale(); 113} 114 115//----------------------------------------------------------------------------- 116 117void TSShapeLoader::updateProgress(S32 major, const char* msg, S32 numMinor, S32 minor) 118{ 119 // Calculate progress value 120 F32 progress = (F32)major / NumLoadPhases; 121 const char *progressMsg = msg; 122 123 if (numMinor) 124 { 125 progress += (minor * (1.0f / NumLoadPhases) / numMinor); 126 progressMsg = avar("%s (%d of %d)", msg, minor + 1, numMinor); 127 } 128 129 Con::executef("updateTSShapeLoadProgress", Con::getFloatArg(progress), progressMsg); 130} 131 132//----------------------------------------------------------------------------- 133// Shape creation entry point 134 135TSShape* TSShapeLoader::generateShape(const Torque::Path& path) 136{ 137 shapePath = path; 138 shape = new TSShape(); 139 140 shape->mExporterVersion = 124; 141 shape->mSmallestVisibleSize = 999999; 142 shape->mSmallestVisibleDL = 0; 143 shape->mReadVersion = 24; 144 shape->mFlags = 0; 145 shape->mSequencesConstructed = 0; 146 147 // Get all nodes, objects and sequences in the shape 148 updateProgress(Load_EnumerateScene, "Enumerating scene..."); 149 enumerateScene(); 150 if (!subshapes.size()) 151 { 152 delete shape; 153 Con::errorf("Failed to load shape \"%s\", no subshapes found", path.getFullPath().c_str()); 154 return NULL; 155 } 156 157 // Create the TSShape::Node hierarchy 158 generateSubshapes(); 159 160 // Create objects (meshes and details) 161 generateObjects(); 162 163 // Generate initial object states and node transforms 164 generateDefaultStates(); 165 166 // Generate skins 167 generateSkins(); 168 169 // Generate material list 170 generateMaterialList(); 171 172 // Generate animation sequences 173 generateSequences(); 174 175 // Sort detail levels and meshes 176 updateProgress(Load_InitShape, "Initialising shape..."); 177 sortDetails(); 178 179 // Install the TS memory helper into a TSShape object. 180 install(); 181 182 return shape; 183} 184 185bool TSShapeLoader::processNode(AppNode* node) 186{ 187 // Detect bounds node 188 if ( node->isBounds() ) 189 { 190 if ( boundsNode ) 191 { 192 Con::warnf( "More than one bounds node found" ); 193 return false; 194 } 195 boundsNode = node; 196 197 // Process bounds geometry 198 MatrixF boundsMat(boundsNode->getNodeTransform(DefaultTime)); 199 boundsMat.inverse(); 200 zapScale(boundsMat); 201 for (S32 iMesh = 0; iMesh < boundsNode->getNumMesh(); iMesh++) 202 { 203 AppMesh* mesh = boundsNode->getMesh(iMesh); 204 MatrixF transform = mesh->getMeshTransform(DefaultTime); 205 transform.mulL(boundsMat); 206 mesh->lockMesh(DefaultTime, transform); 207 } 208 return true; 209 } 210 211 // Detect sequence markers 212 if ( node->isSequence() ) 213 { 214 //appSequences.push_back(new AppSequence(node)); 215 return false; 216 } 217 218 // Add this node to the subshape (create one if needed) 219 if ( subshapes.size() == 0 ) 220 subshapes.push_back( new TSShapeLoader::Subshape ); 221 222 subshapes.last()->branches.push_back( node ); 223 224 return true; 225} 226 227//----------------------------------------------------------------------------- 228// Nodes, meshes and skins 229 230typedef bool (*NameCmpFunc)(const String&, const Vector<String>&, void*, void*); 231 232bool cmpShapeName(const String& key, const Vector<String>& names, void* arg1, void* arg2) 233{ 234 for (S32 i = 0; i < names.size(); i++) 235 { 236 if (names[i].compare(key, 0, String::NoCase) == 0) 237 return false; 238 } 239 return true; 240} 241 242String getUniqueName(const char* name, NameCmpFunc isNameUnique, const Vector<String>& names, void* arg1=0, void* arg2=0) 243{ 244 const S32 MAX_ITERATIONS = 0x10000; // maximum of 4 characters (A-P) will be appended 245 246 String suffix; 247 for (S32 i = 0; i < MAX_ITERATIONS; i++) 248 { 249 // Generate a suffix using the first 16 characters of the alphabet 250 suffix.clear(); 251 for (S32 value = i; value != 0; value >>= 4) 252 suffix = suffix + (char)('A' + (value & 0xF)); 253 254 String uname = name + suffix; 255 if (isNameUnique(uname, names, arg1, arg2)) 256 return uname; 257 } 258 return name; 259} 260 261void TSShapeLoader::recurseSubshape(AppNode* appNode, S32 parentIndex, bool recurseChildren) 262{ 263 // Ignore local bounds nodes 264 if (appNode->isBounds()) 265 return; 266 267 S32 subShapeNum = shape->subShapeFirstNode.size()-1; 268 Subshape* subshape = subshapes[subShapeNum]; 269 270 // Check if we should collapse this node 271 S32 myIndex; 272 if (ignoreNode(appNode->getName())) 273 { 274 myIndex = parentIndex; 275 } 276 else 277 { 278 // Check that adding this node will not exceed the maximum node count 279 if (shape->nodes.size() >= MAX_TS_SET_SIZE) 280 return; 281 282 myIndex = shape->nodes.size(); 283 String nodeName = getUniqueName(appNode->getName(), cmpShapeName, shape->names); 284 285 // Create the 3space node 286 shape->nodes.increment(); 287 TSShape::Node& lastNode = shape->nodes.last(); 288 lastNode.nameIndex = shape->addName(nodeName); 289 lastNode.parentIndex = parentIndex; 290 lastNode.firstObject = -1; 291 lastNode.firstChild = -1; 292 lastNode.nextSibling = -1; 293 294 // Add the AppNode to a matching list (so AppNodes can be accessed using 3space 295 // node indices) 296 appNodes.push_back(appNode); 297 appNodes.last()->mParentIndex = parentIndex; 298 299 // Check for NULL detail or AutoBillboard nodes (no children or geometry) 300 if ((appNode->getNumChildNodes() == 0) && 301 (appNode->getNumMesh() == 0)) 302 { 303 S32 size = 0x7FFFFFFF; 304 String dname(String::GetTrailingNumber(appNode->getName(), size)); 305 306 if (dStrEqual(dname, "nulldetail") && (size != 0x7FFFFFFF)) 307 { 308 shape->addDetail("detail", size, subShapeNum); 309 } 310 else if (appNode->isBillboard() && (size != 0x7FFFFFFF)) 311 { 312 // AutoBillboard detail 313 S32 numEquatorSteps = 4; 314 S32 numPolarSteps = 0; 315 F32 polarAngle = 0.0f; 316 S32 dl = 0; 317 S32 dim = 64; 318 bool includePoles = true; 319 320 appNode->getInt("BB::EQUATOR_STEPS", numEquatorSteps); 321 appNode->getInt("BB::POLAR_STEPS", numPolarSteps); 322 appNode->getFloat("BB::POLAR_ANGLE", polarAngle); 323 appNode->getInt("BB::DL", dl); 324 appNode->getInt("BB::DIM", dim); 325 appNode->getBool("BB::INCLUDE_POLES", includePoles); 326 327 S32 detIndex = shape->addDetail( "bbDetail", size, -1 ); 328 329 TSShape::Detail& detIndexDetail = shape->details[detIndex]; 330 detIndexDetail.bbEquatorSteps = numEquatorSteps; 331 detIndexDetail.bbPolarSteps = numPolarSteps; 332 detIndexDetail.bbDetailLevel = dl; 333 detIndexDetail.bbDimension = dim; 334 detIndexDetail.bbIncludePoles = includePoles; 335 detIndexDetail.bbPolarAngle = polarAngle; 336 } 337 } 338 } 339 340 // Collect geometry 341 for (U32 iMesh = 0; iMesh < appNode->getNumMesh(); iMesh++) 342 { 343 AppMesh* mesh = appNode->getMesh(iMesh); 344 if (!ignoreMesh(mesh->getName())) 345 { 346 subshape->objMeshes.push_back(mesh); 347 subshape->objNodes.push_back(mesh->isSkin() ? -1 : myIndex); 348 } 349 } 350 351 // Create children 352 if (recurseChildren) 353 { 354 for (S32 iChild = 0; iChild < appNode->getNumChildNodes(); iChild++) 355 recurseSubshape(appNode->getChildNode(iChild), myIndex, true); 356 } 357} 358 359void TSShapeLoader::generateSubshapes() 360{ 361 for (U32 iSub = 0; iSub < subshapes.size(); iSub++) 362 { 363 updateProgress(Load_GenerateSubshapes, "Generating subshapes...", subshapes.size(), iSub); 364 365 Subshape* subshape = subshapes[iSub]; 366 367 // Recurse through the node hierarchy, adding 3space nodes and 368 // collecting geometry 369 S32 firstNode = shape->nodes.size(); 370 shape->subShapeFirstNode.push_back(firstNode); 371 372 for (U32 iBranch = 0; iBranch < subshape->branches.size(); iBranch++) 373 recurseSubshape(subshape->branches[iBranch], -1, true); 374 375 shape->subShapeNumNodes.push_back(shape->nodes.size() - firstNode); 376 377 if (shape->nodes.size() >= MAX_TS_SET_SIZE) 378 { 379 Con::warnf("Shape exceeds the maximum node count (%d). Ignoring additional nodes.", 380 MAX_TS_SET_SIZE); 381 } 382 } 383} 384 385// Custom name comparison function to compare mesh name and detail size 386bool cmpMeshNameAndSize(const String& key, const Vector<String>& names, void* arg1, void* arg2) 387{ 388 const Vector<AppMesh*>& meshes = *(Vector<AppMesh*>*)arg1; 389 S32 meshSize = (intptr_t)arg2; 390 391 for (S32 i = 0; i < names.size(); i++) 392 { 393 if (names[i].compare(key, 0, String::NoCase) == 0) 394 { 395 if (meshes[i]->detailSize == meshSize) 396 return false; 397 } 398 } 399 return true; 400} 401 402void TSShapeLoader::generateObjects() 403{ 404 for (S32 iSub = 0; iSub < subshapes.size(); iSub++) 405 { 406 Subshape* subshape = subshapes[iSub]; 407 shape->subShapeFirstObject.push_back(shape->objects.size()); 408 409 // Get the names and sizes of the meshes for this subshape 410 Vector<String> meshNames; 411 for (S32 iMesh = 0; iMesh < subshape->objMeshes.size(); iMesh++) 412 { 413 AppMesh* mesh = subshape->objMeshes[iMesh]; 414 mesh->detailSize = 2; 415 String name = String::GetTrailingNumber( mesh->getName(), mesh->detailSize ); 416 name = getUniqueName( name, cmpMeshNameAndSize, meshNames, &(subshape->objMeshes), (void*)(uintptr_t)mesh->detailSize ); 417 meshNames.push_back( name ); 418 419 // Fix up any collision details that don't have a negative detail level. 420 if ( dStrStartsWith(meshNames[iMesh], "Collision") || 421 dStrStartsWith(meshNames[iMesh], "LOSCol") ) 422 { 423 if (mesh->detailSize > 0) 424 mesh->detailSize = -mesh->detailSize; 425 } 426 } 427 428 // An 'object' is a collection of meshes with the same base name and 429 // different detail sizes. The object is attached to the node of the 430 // highest detail mesh. 431 432 // Sort the 3 arrays (objMeshes, objNodes, meshNames) by name and size 433 for (S32 i = 0; i < subshape->objMeshes.size()-1; i++) 434 { 435 for (S32 j = i+1; j < subshape->objMeshes.size(); j++) 436 { 437 if ((meshNames[i].compare(meshNames[j]) < 0) || 438 ((meshNames[i].compare(meshNames[j]) == 0) && 439 (subshape->objMeshes[i]->detailSize < subshape->objMeshes[j]->detailSize))) 440 { 441 { 442 AppMesh* tmp = subshape->objMeshes[i]; 443 subshape->objMeshes[i] = subshape->objMeshes[j]; 444 subshape->objMeshes[j] = tmp; 445 } 446 { 447 S32 tmp = subshape->objNodes[i]; 448 subshape->objNodes[i] = subshape->objNodes[j]; 449 subshape->objNodes[j] = tmp; 450 } 451 { 452 String tmp = meshNames[i]; 453 meshNames[i] = meshNames[j]; 454 meshNames[j] = tmp; 455 } 456 } 457 } 458 } 459 460 // Now create objects 461 const String* lastName = 0; 462 for (S32 iMesh = 0; iMesh < subshape->objMeshes.size(); iMesh++) 463 { 464 AppMesh* mesh = subshape->objMeshes[iMesh]; 465 466 if (!lastName || (meshNames[iMesh] != *lastName)) 467 { 468 shape->objects.increment(); 469 TSShape::Object& lastObject = shape->objects.last(); 470 lastObject.nameIndex = shape->addName(meshNames[iMesh]); 471 lastObject.nodeIndex = subshape->objNodes[iMesh]; 472 lastObject.startMeshIndex = appMeshes.size(); 473 lastObject.numMeshes = 0; 474 lastName = &meshNames[iMesh]; 475 } 476 477 // Add this mesh to the object 478 appMeshes.push_back(mesh); 479 shape->objects.last().numMeshes++; 480 481 // Set mesh flags 482 mesh->flags = 0; 483 if (mesh->isBillboard()) 484 { 485 mesh->flags |= TSMesh::Billboard; 486 if (mesh->isBillboardZAxis()) 487 mesh->flags |= TSMesh::BillboardZAxis; 488 } 489 490 // Set the detail name... do fixups for collision details. 491 const char* detailName = "detail"; 492 if ( mesh->detailSize < 0 ) 493 { 494 if ( dStrStartsWith(meshNames[iMesh], "Collision") || 495 dStrStartsWith(meshNames[iMesh], "Col") ) 496 detailName = "Collision"; 497 else if (dStrStartsWith(meshNames[iMesh], "LOSCol")) 498 detailName = "LOS"; 499 } 500 501 // Attempt to add the detail (will fail if it already exists) 502 S32 oldNumDetails = shape->details.size(); 503 shape->addDetail(detailName, mesh->detailSize, iSub); 504 if (shape->details.size() > oldNumDetails) 505 { 506 Con::warnf("Object mesh \"%s\" has no matching detail (\"%s%d\" has" 507 " been added automatically)", mesh->getName(false), detailName, mesh->detailSize); 508 } 509 } 510 511 // Get object count for this subshape 512 shape->subShapeNumObjects.push_back(shape->objects.size() - shape->subShapeFirstObject.last()); 513 } 514} 515 516void TSShapeLoader::generateSkins() 517{ 518 Vector<AppMesh*> skins; 519 for (S32 iObject = 0; iObject < shape->objects.size(); iObject++) 520 { 521 for (S32 iMesh = 0; iMesh < shape->objects[iObject].numMeshes; iMesh++) 522 { 523 AppMesh* mesh = appMeshes[shape->objects[iObject].startMeshIndex + iMesh]; 524 if (mesh->isSkin()) 525 skins.push_back(mesh); 526 } 527 } 528 529 for (S32 iSkin = 0; iSkin < skins.size(); iSkin++) 530 { 531 updateProgress(Load_GenerateSkins, "Generating skins...", skins.size(), iSkin); 532 533 // Get skin data (bones, vertex weights etc) 534 AppMesh* skin = skins[iSkin]; 535 skin->lookupSkinData(); 536 537 // Just copy initial verts and norms for now 538 skin->initialVerts.set(skin->points.address(), skin->vertsPerFrame); 539 skin->initialNorms.set(skin->normals.address(), skin->vertsPerFrame); 540 541 // Map bones to nodes 542 skin->nodeIndex.setSize(skin->bones.size()); 543 for (S32 iBone = 0; iBone < skin->bones.size(); iBone++) 544 { 545 // Find the node that matches this bone 546 skin->nodeIndex[iBone] = -1; 547 for (S32 iNode = 0; iNode < appNodes.size(); iNode++) 548 { 549 if (appNodes[iNode]->isEqual(skin->bones[iBone])) 550 { 551 delete skin->bones[iBone]; 552 skin->bones[iBone] = appNodes[iNode]; 553 skin->nodeIndex[iBone] = iNode; 554 break; 555 } 556 } 557 558 if (skin->nodeIndex[iBone] == -1) 559 { 560 Con::warnf("Could not find bone %d. Defaulting to first node", iBone); 561 skin->nodeIndex[iBone] = 0; 562 } 563 } 564 } 565} 566 567void TSShapeLoader::generateDefaultStates() 568{ 569 // Generate default object states (includes initial geometry) 570 for (S32 iObject = 0; iObject < shape->objects.size(); iObject++) 571 { 572 updateProgress(Load_GenerateDefaultStates, "Generating initial mesh and node states...", 573 shape->objects.size(), iObject); 574 575 TSShape::Object& obj = shape->objects[iObject]; 576 577 // Calculate the objectOffset for each mesh at T=0 578 for (S32 iMesh = 0; iMesh < obj.numMeshes; iMesh++) 579 { 580 AppMesh* appMesh = appMeshes[obj.startMeshIndex + iMesh]; 581 AppNode* appNode = obj.nodeIndex >= 0 ? appNodes[obj.nodeIndex] : boundsNode; 582 583 MatrixF meshMat(appMesh->getMeshTransform(DefaultTime)); 584 MatrixF nodeMat(appMesh->isSkin() ? meshMat : appNode->getNodeTransform(DefaultTime)); 585 586 zapScale(nodeMat); 587 588 appMesh->objectOffset = nodeMat.inverse() * meshMat; 589 } 590 591 generateObjectState(shape->objects[iObject], DefaultTime, true, true); 592 } 593 594 // Generate default node transforms 595 for (S32 iNode = 0; iNode < appNodes.size(); iNode++) 596 { 597 // Determine the default translation and rotation for the node 598 QuatF rot, srot; 599 Point3F trans, scale; 600 generateNodeTransform(appNodes[iNode], DefaultTime, false, 0, rot, trans, srot, scale); 601 602 // Add default node translation and rotation 603 addNodeRotation(rot, true); 604 addNodeTranslation(trans, true); 605 } 606} 607 608void TSShapeLoader::generateObjectState(TSShape::Object& obj, F32 t, bool addFrame, bool addMatFrame) 609{ 610 shape->objectStates.increment(); 611 TSShape::ObjectState& state = shape->objectStates.last(); 612 613 state.frameIndex = 0; 614 state.matFrameIndex = 0; 615 state.vis = mClampF(appMeshes[obj.startMeshIndex]->getVisValue(t), 0.0f, 1.0f); 616 617 if (addFrame || addMatFrame) 618 { 619 generateFrame(obj, t, addFrame, addMatFrame); 620 621 // set the frame number for the object state 622 state.frameIndex = appMeshes[obj.startMeshIndex]->numFrames - 1; 623 state.matFrameIndex = appMeshes[obj.startMeshIndex]->numMatFrames - 1; 624 } 625} 626 627void TSShapeLoader::generateFrame(TSShape::Object& obj, F32 t, bool addFrame, bool addMatFrame) 628{ 629 for (S32 iMesh = 0; iMesh < obj.numMeshes; iMesh++) 630 { 631 AppMesh* appMesh = appMeshes[obj.startMeshIndex + iMesh]; 632 633 U32 oldNumPoints = appMesh->points.size(); 634 U32 oldNumUvs = appMesh->uvs.size(); 635 636 // Get the mesh geometry at time, 't' 637 // Geometry verts, normals and tverts can be animated (different set for 638 // each frame), but the TSDrawPrimitives stay the same, so the way lockMesh 639 // works is that it will only generate the primitives once, then after that 640 // will just append verts, normals and tverts each time it is called. 641 appMesh->lockMesh(t, appMesh->objectOffset); 642 643 // Calculate vertex normals if required 644 if (appMesh->normals.size() != appMesh->points.size()) 645 appMesh->computeNormals(); 646 647 // If this is the first call, set the number of points per frame 648 if (appMesh->numFrames == 0) 649 { 650 appMesh->vertsPerFrame = appMesh->points.size(); 651 } 652 else 653 { 654 // Check frame topology => ie. that the right number of points, normals 655 // and tverts was added 656 if ((appMesh->points.size() - oldNumPoints) != appMesh->vertsPerFrame) 657 { 658 Con::warnf("Wrong number of points (%d) added at time=%f (expected %d)", 659 appMesh->points.size() - oldNumPoints, t, appMesh->vertsPerFrame); 660 addFrame = false; 661 } 662 if ((appMesh->normals.size() - oldNumPoints) != appMesh->vertsPerFrame) 663 { 664 Con::warnf("Wrong number of normals (%d) added at time=%f (expected %d)", 665 appMesh->normals.size() - oldNumPoints, t, appMesh->vertsPerFrame); 666 addFrame = false; 667 } 668 if ((appMesh->uvs.size() - oldNumUvs) != appMesh->vertsPerFrame) 669 { 670 Con::warnf("Wrong number of tverts (%d) added at time=%f (expected %d)", 671 appMesh->uvs.size() - oldNumUvs, t, appMesh->vertsPerFrame); 672 addMatFrame = false; 673 } 674 } 675 676 // Because lockMesh adds points, normals AND tverts each call, if we didn't 677 // actually want another frame or matFrame, we need to remove them afterwards. 678 // In the common case (we DO want the frame), we can do nothing => the 679 // points/normals/tverts are already in place! 680 if (addFrame) 681 { 682 appMesh->numFrames++; 683 } 684 else 685 { 686 appMesh->points.setSize(oldNumPoints); 687 appMesh->normals.setSize(oldNumPoints); 688 } 689 690 if (addMatFrame) 691 { 692 appMesh->numMatFrames++; 693 } 694 else 695 { 696 appMesh->uvs.setSize(oldNumPoints); 697 } 698 } 699} 700 701//----------------------------------------------------------------------------- 702// Materials 703 704/// Convert all Collada materials into a single TSMaterialList 705void TSShapeLoader::generateMaterialList() 706{ 707 // Install the materials into the material list 708 shape->materialList = new TSMaterialList; 709 for (S32 iMat = 0; iMat < AppMesh::appMaterials.size(); iMat++) 710 { 711 updateProgress(Load_GenerateMaterials, "Generating materials...", AppMesh::appMaterials.size(), iMat); 712 713 AppMaterial* appMat = AppMesh::appMaterials[iMat]; 714 shape->materialList->push_back(appMat->getName(), appMat->getFlags(), U32(-1), U32(-1), U32(-1), 1.0f, appMat->getReflectance()); 715 } 716} 717 718 719//----------------------------------------------------------------------------- 720// Animation Sequences 721 722void TSShapeLoader::generateSequences() 723{ 724 for (S32 iSeq = 0; iSeq < appSequences.size(); iSeq++) 725 { 726 updateProgress(Load_GenerateSequences, "Generating sequences...", appSequences.size(), iSeq); 727 728 // Initialize the sequence 729 appSequences[iSeq]->setActive(true); 730 731 shape->sequences.increment(); 732 TSShape::Sequence& seq = shape->sequences.last(); 733 734 seq.nameIndex = shape->addName(appSequences[iSeq]->getName()); 735 seq.toolBegin = appSequences[iSeq]->getStart(); 736 seq.priority = appSequences[iSeq]->getPriority(); 737 seq.flags = appSequences[iSeq]->getFlags(); 738 739 // Compute duration and number of keyframes (then adjust time between frames to match) 740 seq.duration = appSequences[iSeq]->getEnd() - appSequences[iSeq]->getStart(); 741 seq.numKeyframes = (S32)(seq.duration * appSequences[iSeq]->fps + 0.5f) + 1; 742 743 seq.sourceData.start = 0; 744 seq.sourceData.end = seq.numKeyframes-1; 745 seq.sourceData.total = seq.numKeyframes; 746 747 // Set membership arrays (ie. which nodes and objects are affected by this sequence) 748 setNodeMembership(seq, appSequences[iSeq]); 749 setObjectMembership(seq, appSequences[iSeq]); 750 751 // Generate keyframes 752 generateNodeAnimation(seq); 753 generateObjectAnimation(seq, appSequences[iSeq]); 754 generateGroundAnimation(seq, appSequences[iSeq]); 755 generateFrameTriggers(seq, appSequences[iSeq]); 756 757 // Set sequence flags 758 seq.dirtyFlags = 0; 759 if (seq.rotationMatters.testAll() || seq.translationMatters.testAll() || seq.scaleMatters.testAll()) 760 seq.dirtyFlags |= TSShapeInstance::TransformDirty; 761 if (seq.visMatters.testAll()) 762 seq.dirtyFlags |= TSShapeInstance::VisDirty; 763 if (seq.frameMatters.testAll()) 764 seq.dirtyFlags |= TSShapeInstance::FrameDirty; 765 if (seq.matFrameMatters.testAll()) 766 seq.dirtyFlags |= TSShapeInstance::MatFrameDirty; 767 768 // Set shape flags (only the most significant scale type) 769 U32 curVal = shape->mFlags & TSShape::AnyScale; 770 shape->mFlags &= ~(TSShape::AnyScale); 771 shape->mFlags |= getMax(curVal, seq.flags & TSShape::AnyScale); // take the larger value (can only convert upwards) 772 773 appSequences[iSeq]->setActive(false); 774 } 775} 776 777void TSShapeLoader::setNodeMembership(TSShape::Sequence& seq, const AppSequence* appSeq) 778{ 779 seq.rotationMatters.clearAll(); // node rotation (size = nodes.size()) 780 seq.translationMatters.clearAll(); // node translation (size = nodes.size()) 781 seq.scaleMatters.clearAll(); // node scale (size = nodes.size()) 782 783 // This shouldn't be allowed, but check anyway... 784 if (seq.numKeyframes < 2) 785 return; 786 787 // Note: this fills the cache with current sequence data. Methods that get 788 // called later (e.g. generateNodeAnimation) use this info (and assume it's set). 789 fillNodeTransformCache(seq, appSeq); 790 791 // Test to see if the transform changes over the interval in order to decide 792 // whether to animate the transform in 3space. We don't use app's mechanism 793 // for doing this because it functions different in different apps and we do 794 // some special stuff with scale. 795 setRotationMembership(seq); 796 setTranslationMembership(seq); 797 setScaleMembership(seq); 798} 799 800void TSShapeLoader::setRotationMembership(TSShape::Sequence& seq) 801{ 802 for (S32 iNode = 0; iNode < appNodes.size(); iNode++) 803 { 804 // Check if any of the node rotations are different to 805 // the default rotation 806 QuatF defaultRot; 807 shape->defaultRotations[iNode].getQuatF(&defaultRot); 808 809 for (S32 iFrame = 0; iFrame < seq.numKeyframes; iFrame++) 810 { 811 if (nodeRotCache[iNode][iFrame] != defaultRot) 812 { 813 seq.rotationMatters.set(iNode); 814 break; 815 } 816 } 817 } 818} 819 820void TSShapeLoader::setTranslationMembership(TSShape::Sequence& seq) 821{ 822 for (S32 iNode = 0; iNode < appNodes.size(); iNode++) 823 { 824 // Check if any of the node translations are different to 825 // the default translation 826 Point3F& defaultTrans = shape->defaultTranslations[iNode]; 827 828 for (S32 iFrame = 0; iFrame < seq.numKeyframes; iFrame++) 829 { 830 if (!nodeTransCache[iNode][iFrame].equal(defaultTrans)) 831 { 832 seq.translationMatters.set(iNode); 833 break; 834 } 835 } 836 } 837} 838 839void TSShapeLoader::setScaleMembership(TSShape::Sequence& seq) 840{ 841 Point3F unitScale(1,1,1); 842 843 U32 arbitraryScaleCount = 0; 844 U32 alignedScaleCount = 0; 845 U32 uniformScaleCount = 0; 846 847 for (S32 iNode = 0; iNode < appNodes.size(); iNode++) 848 { 849 // Check if any of the node scales are not the unit scale 850 for (S32 iFrame = 0; iFrame < seq.numKeyframes; iFrame++) 851 { 852 Point3F& scale = nodeScaleCache[iNode][iFrame]; 853 if (!unitScale.equal(scale)) 854 { 855 // Determine what type of scale this is 856 if (!nodeScaleRotCache[iNode][iFrame].isIdentity()) 857 arbitraryScaleCount++; 858 else if (scale.x != scale.y || scale.y != scale.z) 859 alignedScaleCount++; 860 else 861 uniformScaleCount++; 862 863 seq.scaleMatters.set(iNode); 864 break; 865 } 866 } 867 } 868 869 // Only one type of scale is animated 870 if (arbitraryScaleCount) 871 seq.flags |= TSShape::ArbitraryScale; 872 else if (alignedScaleCount) 873 seq.flags |= TSShape::AlignedScale; 874 else if (uniformScaleCount) 875 seq.flags |= TSShape::UniformScale; 876} 877 878void TSShapeLoader::setObjectMembership(TSShape::Sequence& seq, const AppSequence* appSeq) 879{ 880 seq.visMatters.clearAll(); // object visibility (size = objects.size()) 881 seq.frameMatters.clearAll(); // vert animation (morph) (size = objects.size()) 882 seq.matFrameMatters.clearAll(); // UV animation (size = objects.size()) 883 884 for (S32 iObject = 0; iObject < shape->objects.size(); iObject++) 885 { 886 if (!appMeshes[shape->objects[iObject].startMeshIndex]) 887 continue; 888 889 if (appMeshes[shape->objects[iObject].startMeshIndex]->animatesVis(appSeq)) 890 seq.visMatters.set(iObject); 891 // Morph and UV animation has been deprecated 892 //if (appMeshes[shape->objects[iObject].startMeshIndex]->animatesFrame(appSeq)) 893 //seq.frameMatters.set(iObject); 894 //if (appMeshes[shape->objects[iObject].startMeshIndex]->animatesMatFrame(appSeq)) 895 //seq.matFrameMatters.set(iObject); 896 } 897} 898 899void TSShapeLoader::clearNodeTransformCache() 900{ 901 // clear out the transform caches 902 for (S32 i = 0; i < nodeRotCache.size(); i++) 903 delete [] nodeRotCache[i]; 904 nodeRotCache.clear(); 905 for (S32 i = 0; i < nodeTransCache.size(); i++) 906 delete [] nodeTransCache[i]; 907 nodeTransCache.clear(); 908 for (S32 i = 0; i < nodeScaleRotCache.size(); i++) 909 delete [] nodeScaleRotCache[i]; 910 nodeScaleRotCache.clear(); 911 for (S32 i = 0; i < nodeScaleCache.size(); i++) 912 delete [] nodeScaleCache[i]; 913 nodeScaleCache.clear(); 914} 915 916void TSShapeLoader::fillNodeTransformCache(TSShape::Sequence& seq, const AppSequence* appSeq) 917{ 918 // clear out the transform caches and set it up for this sequence 919 clearNodeTransformCache(); 920 921 nodeRotCache.setSize(appNodes.size()); 922 for (S32 i = 0; i < nodeRotCache.size(); i++) 923 nodeRotCache[i] = new QuatF[seq.numKeyframes]; 924 nodeTransCache.setSize(appNodes.size()); 925 for (S32 i = 0; i < nodeTransCache.size(); i++) 926 nodeTransCache[i] = new Point3F[seq.numKeyframes]; 927 nodeScaleRotCache.setSize(appNodes.size()); 928 for (S32 i = 0; i < nodeScaleRotCache.size(); i++) 929 nodeScaleRotCache[i] = new QuatF[seq.numKeyframes]; 930 nodeScaleCache.setSize(appNodes.size()); 931 for (S32 i = 0; i < nodeScaleCache.size(); i++) 932 nodeScaleCache[i] = new Point3F[seq.numKeyframes]; 933 934 // get the node transforms for every frame 935 for (S32 iFrame = 0; iFrame < seq.numKeyframes; iFrame++) 936 { 937 F32 time = appSeq->getStart() + seq.duration * iFrame / getMax(1, seq.numKeyframes - 1); 938 for (S32 iNode = 0; iNode < appNodes.size(); iNode++) 939 { 940 generateNodeTransform(appNodes[iNode], time, seq.isBlend(), appSeq->getBlendRefTime(), 941 nodeRotCache[iNode][iFrame], nodeTransCache[iNode][iFrame], 942 nodeScaleRotCache[iNode][iFrame], nodeScaleCache[iNode][iFrame]); 943 } 944 } 945} 946 947void TSShapeLoader::addNodeRotation(QuatF& rot, bool defaultVal) 948{ 949 Quat16 rot16; 950 rot16.set(rot); 951 952 if (!defaultVal) 953 shape->nodeRotations.push_back(rot16); 954 else 955 shape->defaultRotations.push_back(rot16); 956} 957 958void TSShapeLoader::addNodeTranslation(Point3F& trans, bool defaultVal) 959{ 960 if (!defaultVal) 961 shape->nodeTranslations.push_back(trans); 962 else 963 shape->defaultTranslations.push_back(trans); 964} 965 966void TSShapeLoader::addNodeUniformScale(F32 scale) 967{ 968 shape->nodeUniformScales.push_back(scale); 969} 970 971void TSShapeLoader::addNodeAlignedScale(Point3F& scale) 972{ 973 shape->nodeAlignedScales.push_back(scale); 974} 975 976void TSShapeLoader::addNodeArbitraryScale(QuatF& qrot, Point3F& scale) 977{ 978 Quat16 rot16; 979 rot16.set(qrot); 980 shape->nodeArbitraryScaleRots.push_back(rot16); 981 shape->nodeArbitraryScaleFactors.push_back(scale); 982} 983 984void TSShapeLoader::generateNodeAnimation(TSShape::Sequence& seq) 985{ 986 seq.baseRotation = shape->nodeRotations.size(); 987 seq.baseTranslation = shape->nodeTranslations.size(); 988 seq.baseScale = (seq.flags & TSShape::ArbitraryScale) ? shape->nodeArbitraryScaleRots.size() : 989 (seq.flags & TSShape::AlignedScale) ? shape->nodeAlignedScales.size() : 990 shape->nodeUniformScales.size(); 991 992 for (S32 iNode = 0; iNode < appNodes.size(); iNode++) 993 { 994 for (S32 iFrame = 0; iFrame < seq.numKeyframes; iFrame++) 995 { 996 if (seq.rotationMatters.test(iNode)) 997 addNodeRotation(nodeRotCache[iNode][iFrame], false); 998 if (seq.translationMatters.test(iNode)) 999 addNodeTranslation(nodeTransCache[iNode][iFrame], false); 1000 if (seq.scaleMatters.test(iNode)) 1001 { 1002 QuatF& rot = nodeScaleRotCache[iNode][iFrame]; 1003 Point3F scale = nodeScaleCache[iNode][iFrame]; 1004 1005 if (seq.flags & TSShape::ArbitraryScale) 1006 addNodeArbitraryScale(rot, scale); 1007 else if (seq.flags & TSShape::AlignedScale) 1008 addNodeAlignedScale(scale); 1009 else if (seq.flags & TSShape::UniformScale) 1010 addNodeUniformScale((scale.x+scale.y+scale.z)/3.0f); 1011 } 1012 } 1013 } 1014} 1015 1016void TSShapeLoader::generateObjectAnimation(TSShape::Sequence& seq, const AppSequence* appSeq) 1017{ 1018 seq.baseObjectState = shape->objectStates.size(); 1019 1020 for (S32 iObject = 0; iObject < shape->objects.size(); iObject++) 1021 { 1022 bool visMatters = seq.visMatters.test(iObject); 1023 bool frameMatters = seq.frameMatters.test(iObject); 1024 bool matFrameMatters = seq.matFrameMatters.test(iObject); 1025 1026 if (visMatters || frameMatters || matFrameMatters) 1027 { 1028 for (S32 iFrame = 0; iFrame < seq.numKeyframes; iFrame++) 1029 { 1030 F32 time = appSeq->getStart() + seq.duration * iFrame / getMax(1, seq.numKeyframes - 1); 1031 generateObjectState(shape->objects[iObject], time, frameMatters, matFrameMatters); 1032 } 1033 } 1034 } 1035} 1036 1037void TSShapeLoader::generateGroundAnimation(TSShape::Sequence& seq, const AppSequence* appSeq) 1038{ 1039 seq.firstGroundFrame = shape->groundTranslations.size(); 1040 seq.numGroundFrames = 0; 1041 1042 if (!boundsNode) 1043 return; 1044 1045 // Check if the bounds node is animated by this sequence 1046 seq.numGroundFrames = (S32)((seq.duration + 0.25f/<a href="/coding/class/classtsshapeloader/#classtsshapeloader_1a79866e12c8b88b2f5a4e6dc9e24a15c6">AppGroundFrameRate</a>) * AppGroundFrameRate); 1047 1048 seq.flags |= TSShape::MakePath; 1049 1050 // Get ground transform at the start of the sequence 1051 MatrixF invStartMat = boundsNode->getNodeTransform(appSeq->getStart()); 1052 zapScale(invStartMat); 1053 invStartMat.inverse(); 1054 1055 for (S32 iFrame = 0; iFrame < seq.numGroundFrames; iFrame++) 1056 { 1057 F32 time = appSeq->getStart() + seq.duration * iFrame / getMax(1, seq.numGroundFrames - 1); 1058 1059 // Determine delta bounds node transform at 't' 1060 MatrixF mat = boundsNode->getNodeTransform(time); 1061 zapScale(mat); 1062 mat = invStartMat * mat; 1063 1064 // Add ground transform 1065 Quat16 rotation; 1066 rotation.set(QuatF(mat)); 1067 shape->groundTranslations.push_back(mat.getPosition()); 1068 shape->groundRotations.push_back(rotation); 1069 } 1070} 1071 1072void TSShapeLoader::generateFrameTriggers(TSShape::Sequence& seq, const AppSequence* appSeq) 1073{ 1074 // Initialize triggers 1075 seq.firstTrigger = shape->triggers.size(); 1076 seq.numTriggers = appSeq->getNumTriggers(); 1077 if (!seq.numTriggers) 1078 return; 1079 1080 seq.flags |= TSShape::MakePath; 1081 1082 // Add triggers 1083 for (S32 iTrigger = 0; iTrigger < seq.numTriggers; iTrigger++) 1084 { 1085 shape->triggers.increment(); 1086 appSeq->getTrigger(iTrigger, shape->triggers.last()); 1087 } 1088 1089 // Track the triggers that get turned off by this shape...normally, triggers 1090 // aren't turned on/off, just on...if we are a trigger that does both then we 1091 // need to mark ourselves as such so that on/off can become off/on when sequence 1092 // is played in reverse... 1093 U32 offTriggers = 0; 1094 for (S32 iTrigger = 0; iTrigger < seq.numTriggers; iTrigger++) 1095 { 1096 U32 state = shape->triggers[seq.firstTrigger+iTrigger].state; 1097 if ((state & TSShape::Trigger::StateOn) == 0) 1098 offTriggers |= (state & TSShape::Trigger::StateMask); 1099 } 1100 1101 // We now know which states are turned off, set invert on all those (including when turned on) 1102 for (int iTrigger = 0; iTrigger < seq.numTriggers; iTrigger++) 1103 { 1104 if (shape->triggers[seq.firstTrigger + iTrigger].state & offTriggers) 1105 shape->triggers[seq.firstTrigger + iTrigger].state |= TSShape::Trigger::InvertOnReverse; 1106 } 1107} 1108 1109//----------------------------------------------------------------------------- 1110 1111void TSShapeLoader::sortDetails() 1112{ 1113 // Sort objects by: transparency, material index and node index 1114 1115 1116 // Insert NULL meshes where required 1117 for (S32 iSub = 0; iSub < subshapes.size(); iSub++) 1118 { 1119 Vector<S32> validDetails; 1120 shape->getSubShapeDetails(iSub, validDetails); 1121 1122 for (S32 iDet = 0; iDet < validDetails.size(); iDet++) 1123 { 1124 TSShape::Detail &detail = shape->details[validDetails[iDet]]; 1125 if (detail.subShapeNum >= 0) 1126 detail.objectDetailNum = iDet; 1127 1128 for (S32 iObj = shape->subShapeFirstObject[iSub]; 1129 iObj < (shape->subShapeFirstObject[iSub] + shape->subShapeNumObjects[iSub]); 1130 iObj++) 1131 { 1132 TSShape::Object &object = shape->objects[iObj]; 1133 1134 // Insert a NULL mesh for this detail level if required (ie. if the 1135 // object does not already have a mesh with an equal or higher detail) 1136 S32 meshIndex = (iDet < object.numMeshes) ? iDet : object.numMeshes-1; 1137 1138 if (appMeshes[object.startMeshIndex + meshIndex]->detailSize < shape->details[iDet].size) 1139 { 1140 // Add a NULL mesh 1141 appMeshes.insert(object.startMeshIndex + iDet, NULL); 1142 object.numMeshes++; 1143 1144 // Fixup the start index for the other objects 1145 for (S32 k = iObj+1; k < shape->objects.size(); k++) 1146 shape->objects[k].startMeshIndex++; 1147 } 1148 } 1149 } 1150 } 1151} 1152 1153// Install into the TSShape, the shape is expected to be empty. 1154// Data is not copied, the TSShape is modified to point to memory 1155// managed by this object. This object is also bound to the TSShape 1156// object and will be deleted when it's deleted. 1157void TSShapeLoader::install() 1158{ 1159 // Arrays that are filled in by ts shape init, but need 1160 // to be allocated beforehand. 1161 shape->subShapeFirstTranslucentObject.setSize(shape->subShapeFirstObject.size()); 1162 1163 // Construct TS sub-meshes 1164 shape->meshes.setSize(appMeshes.size()); 1165 for (U32 m = 0; m < appMeshes.size(); m++) 1166 shape->meshes[m] = appMeshes[m] ? appMeshes[m]->constructTSMesh() : NULL; 1167 1168 // Remove empty meshes and objects 1169 for (S32 iObj = shape->objects.size()-1; iObj >= 0; iObj--) 1170 { 1171 TSShape::Object& obj = shape->objects[iObj]; 1172 for (S32 iMesh = obj.numMeshes-1; iMesh >= 0; iMesh--) 1173 { 1174 TSMesh *mesh = shape->meshes[obj.startMeshIndex + iMesh]; 1175 1176 if (mesh && !mesh->mPrimitives.size()) 1177 { 1178 S32 oldMeshCount = obj.numMeshes; 1179 destructInPlace(mesh); 1180 shape->removeMeshFromObject(iObj, iMesh); 1181 iMesh -= (oldMeshCount - obj.numMeshes - 1); // handle when more than one mesh is removed 1182 } 1183 } 1184 1185 if (!obj.numMeshes) 1186 shape->removeObject(shape->getName(obj.nameIndex)); 1187 } 1188 1189 // Add a dummy object if needed so the shape loads and renders ok 1190 if (!shape->details.size()) 1191 { 1192 shape->addDetail("detail", 2, 0); 1193 shape->subShapeNumObjects.last() = 1; 1194 1195 shape->meshes.push_back(NULL); 1196 1197 shape->objects.increment(); 1198 1199 TSShape::Object& lastObject = shape->objects.last(); 1200 lastObject.nameIndex = shape->addName("dummy"); 1201 lastObject.nodeIndex = 0; 1202 lastObject.startMeshIndex = 0; 1203 lastObject.numMeshes = 1; 1204 1205 shape->objectStates.increment(); 1206 shape->objectStates.last().frameIndex = 0; 1207 shape->objectStates.last().matFrameIndex = 0; 1208 shape->objectStates.last().vis = 1.0f; 1209 } 1210 1211 // Update smallest visible detail 1212 shape->mSmallestVisibleDL = -1; 1213 shape->mSmallestVisibleSize = 999999; 1214 for (S32 i = 0; i < shape->details.size(); i++) 1215 { 1216 if ((shape->details[i].size >= 0) && 1217 (shape->details[i].size < shape->mSmallestVisibleSize)) 1218 { 1219 shape->mSmallestVisibleDL = i; 1220 shape->mSmallestVisibleSize = shape->details[i].size; 1221 } 1222 } 1223 1224 computeBounds(shape->mBounds); 1225 if (!shape->mBounds.isValidBox()) 1226 shape->mBounds = Box3F(1.0f); 1227 1228 shape->mBounds.getCenter(&shape->center); 1229 shape->mRadius = (shape->mBounds.maxExtents - shape->center).len(); 1230 shape->tubeRadius = shape->mRadius; 1231 1232 shape->init(); 1233 shape->finalizeEditable(); 1234} 1235 1236void TSShapeLoader::computeBounds(Box3F& bounds) 1237{ 1238 // Compute the box that encloses the model geometry 1239 bounds = Box3F::Invalid; 1240 1241 // Use bounds node geometry if present 1242 if ( boundsNode && boundsNode->getNumMesh() ) 1243 { 1244 for (S32 iMesh = 0; iMesh < boundsNode->getNumMesh(); iMesh++) 1245 { 1246 AppMesh* mesh = boundsNode->getMesh( iMesh ); 1247 if ( !mesh ) 1248 continue; 1249 1250 Box3F meshBounds; 1251 mesh->computeBounds( meshBounds ); 1252 if ( meshBounds.isValidBox() ) 1253 bounds.intersect( meshBounds ); 1254 } 1255 } 1256 else 1257 { 1258 // Compute bounds based on all geometry in the model 1259 for (S32 iMesh = 0; iMesh < appMeshes.size(); iMesh++) 1260 { 1261 AppMesh* mesh = appMeshes[iMesh]; 1262 if ( !mesh ) 1263 continue; 1264 1265 Box3F meshBounds; 1266 mesh->computeBounds( meshBounds ); 1267 if ( meshBounds.isValidBox() ) 1268 bounds.intersect( meshBounds ); 1269 } 1270 } 1271} 1272 1273TSShapeLoader::~TSShapeLoader() 1274{ 1275 clearNodeTransformCache(); 1276 1277 // Clear shared AppMaterial list 1278 for (S32 iMat = 0; iMat < AppMesh::appMaterials.size(); iMat++) 1279 delete AppMesh::appMaterials[iMat]; 1280 AppMesh::appMaterials.clear(); 1281 1282 // Delete Subshapes 1283 delete boundsNode; 1284 for (S32 iSub = 0; iSub < subshapes.size(); iSub++) 1285 delete subshapes[iSub]; 1286 1287 // Delete AppSequences 1288 for (S32 iSeq = 0; iSeq < appSequences.size(); iSeq++) 1289 delete appSequences[iSeq]; 1290 appSequences.clear(); 1291} 1292 1293// Static functions to handle supported formats for shape loader. 1294void TSShapeLoader::addFormat(String name, String extension) 1295{ 1296 ShapeFormat newFormat; 1297 newFormat.mName = name; 1298 newFormat.mExtension = extension; 1299 smFormats.push_back(newFormat); 1300} 1301 1302String TSShapeLoader::getFormatExtensions() 1303{ 1304 // "*.dsq TAB *.dae TAB 1305 StringBuilder output; 1306 for(U32 n = 0; n < smFormats.size(); ++n) 1307 { 1308 output.append("*."); 1309 output.append(smFormats[n].mExtension); 1310 output.append("\t"); 1311 } 1312 return output.end(); 1313} 1314 1315String TSShapeLoader::getFormatFilters() 1316{ 1317 // "DSQ Files|*.dsq|COLLADA Files|*.dae|" 1318 StringBuilder output; 1319 for(U32 n = 0; n < smFormats.size(); ++n) 1320 { 1321 output.append(smFormats[n].mName); 1322 output.append("|*."); 1323 output.append(smFormats[n].mExtension); 1324 output.append("|"); 1325 } 1326 return output.end(); 1327} 1328 1329bool TSShapeLoader::isSupportedFormat(String extension) 1330{ 1331 String extLower = String::ToLower(extension); 1332 for (U32 n = 0; n < smFormats.size(); ++n) 1333 { 1334 if (smFormats[n].mExtension.equal(extLower)) 1335 return true; 1336 } 1337 return false; 1338} 1339 1340DefineEngineFunction( getFormatExtensions, const char*, ( ),, 1341 "Returns a list of supported shape format extensions separated by tabs." 1342 "Example output: *.dsq TAB *.dae TAB") 1343{ 1344 return Con::getReturnBuffer(TSShapeLoader::getFormatExtensions()); 1345} 1346 1347DefineEngineFunction( getFormatFilters, const char*, ( ),, 1348 "Returns a list of supported shape formats in filter form.\n" 1349 "Example output: DSQ Files|*.dsq|COLLADA Files|*.dae|") 1350{ 1351 return Con::getReturnBuffer(TSShapeLoader::getFormatFilters()); 1352} 1353 1354DefineEngineFunction(isSupportedFormat, bool, (const char* extension), , "") 1355{ 1356 return TSShapeLoader::isSupportedFormat(extension); 1357} 1358