tsShapeInstance.cpp
Engine/source/ts/tsShapeInstance.cpp
Public Variables
Detailed Description
Public Variables
MODULE_END
MODULE_INIT
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 "ts/tsShapeInstance.h" 26 27#include "ts/tsLastDetail.h" 28#include "ts/tsMaterialList.h" 29#include "console/consoleTypes.h" 30#include "ts/tsDecal.h" 31#include "platform/profiler.h" 32#include "core/frameAllocator.h" 33#include "gfx/gfxDevice.h" 34#include "materials/materialManager.h" 35#include "materials/materialFeatureTypes.h" 36#include "materials/sceneData.h" 37#include "materials/matInstance.h" 38#include "scene/sceneRenderState.h" 39#include "gfx/primBuilder.h" 40#include "gfx/gfxDrawUtil.h" 41#include "core/module.h" 42 43MODULE_BEGIN( TSShapeInstance ) 44 45 MODULE_INIT 46 { 47 Con::addVariable("$pref::TS::detailAdjust", TypeF32, &TSShapeInstance::smDetailAdjust, 48 "@brief User perference for scaling the TSShape level of detail.\n" 49 "The smaller the value the closer the camera must get to see the " 50 "highest LOD. This setting can have a huge impact on performance in " 51 "mesh heavy scenes. The default value is 1.\n" 52 "@ingroup Rendering\n" ); 53 54 Con::addVariable("$pref::TS::skipLoadDLs", TypeS32, &TSShape::smNumSkipLoadDetails, 55 "@brief User perference which causes TSShapes to skip loading higher lods.\n" 56 "This potentialy reduces the GPU resources and materials generated as well as " 57 "limits the LODs rendered. The default value is 0.\n" 58 "@see $pref::TS::skipRenderDLs\n" 59 "@ingroup Rendering\n" ); 60 61 Con::addVariable("$pref::TS::skipRenderDLs", TypeS32, &TSShapeInstance::smNumSkipRenderDetails, 62 "@brief User perference which causes TSShapes to skip rendering higher lods.\n" 63 "This will reduce the number of draw calls and triangles rendered and improve " 64 "rendering performance when proper LODs have been created for your models. " 65 "The default value is 0.\n" 66 "@see $pref::TS::skipLoadDLs\n" 67 "@ingroup Rendering\n" ); 68 69 Con::addVariable("$pref::TS::smallestVisiblePixelSize", TypeF32, &TSShapeInstance::smSmallestVisiblePixelSize, 70 "@brief User perference which sets the smallest pixel size at which TSShapes will skip rendering.\n" 71 "This will force all shapes to stop rendering when they get smaller than this size. " 72 "The default value is -1 which disables it.\n" 73 "@ingroup Rendering\n" ); 74 75 Con::addVariable("$pref::TS::maxInstancingVerts", TypeS32, &TSMesh::smMaxInstancingVerts, 76 "@brief Enables mesh instancing on non-skin meshes that have less that this count of verts.\n" 77 "The default value is 2000. Higher values can degrade performance.\n" 78 "@ingroup Rendering\n" ); 79 } 80 81MODULE_END; 82 83 84F32 TSShapeInstance::smDetailAdjust = 1.0f; 85F32 TSShapeInstance::smSmallestVisiblePixelSize = -1.0f; 86S32 TSShapeInstance::smNumSkipRenderDetails = 0; 87 88F32 TSShapeInstance::smLastScreenErrorTolerance = 0.0f; 89F32 TSShapeInstance::smLastScaledDistance = 0.0f; 90F32 TSShapeInstance::smLastPixelSize = 0.0f; 91 92Vector<QuatF> TSShapeInstance::smNodeCurrentRotations(__FILE__, __LINE__); 93Vector<Point3F> TSShapeInstance::smNodeCurrentTranslations(__FILE__, __LINE__); 94Vector<F32> TSShapeInstance::smNodeCurrentUniformScales(__FILE__, __LINE__); 95Vector<Point3F> TSShapeInstance::smNodeCurrentAlignedScales(__FILE__, __LINE__); 96Vector<TSScale> TSShapeInstance::smNodeCurrentArbitraryScales(__FILE__, __LINE__); 97Vector<MatrixF> TSShapeInstance::smNodeLocalTransforms(__FILE__, __LINE__); 98TSIntegerSet TSShapeInstance::smNodeLocalTransformDirty; 99 100Vector<TSThread*> TSShapeInstance::smRotationThreads(__FILE__, __LINE__); 101Vector<TSThread*> TSShapeInstance::smTranslationThreads(__FILE__, __LINE__); 102Vector<TSThread*> TSShapeInstance::smScaleThreads(__FILE__, __LINE__); 103 104//------------------------------------------------------------------------------------- 105// constructors, destructors, initialization 106//------------------------------------------------------------------------------------- 107 108TSShapeInstance::TSShapeInstance( const Resource<TSShape> &shape, bool loadMaterials ) 109{ 110 VECTOR_SET_ASSOCIATION(mMeshObjects); 111 VECTOR_SET_ASSOCIATION(mNodeTransforms); 112 VECTOR_SET_ASSOCIATION(mNodeReferenceRotations); 113 VECTOR_SET_ASSOCIATION(mNodeReferenceTranslations); 114 VECTOR_SET_ASSOCIATION(mNodeReferenceUniformScales); 115 VECTOR_SET_ASSOCIATION(mNodeReferenceScaleFactors); 116 VECTOR_SET_ASSOCIATION(mNodeReferenceArbitraryScaleRots); 117 VECTOR_SET_ASSOCIATION(mThreadList); 118 VECTOR_SET_ASSOCIATION(mTransitionThreads); 119 120 mShapeResource = shape; 121 mShape = mShapeResource; 122 mUseOverrideTexture = false; 123 buildInstanceData( mShape, loadMaterials ); 124} 125 126TSShapeInstance::TSShapeInstance( TSShape *shape, bool loadMaterials ) 127{ 128 VECTOR_SET_ASSOCIATION(mMeshObjects); 129 VECTOR_SET_ASSOCIATION(mNodeTransforms); 130 VECTOR_SET_ASSOCIATION(mNodeReferenceRotations); 131 VECTOR_SET_ASSOCIATION(mNodeReferenceTranslations); 132 VECTOR_SET_ASSOCIATION(mNodeReferenceUniformScales); 133 VECTOR_SET_ASSOCIATION(mNodeReferenceScaleFactors); 134 VECTOR_SET_ASSOCIATION(mNodeReferenceArbitraryScaleRots); 135 VECTOR_SET_ASSOCIATION(mThreadList); 136 VECTOR_SET_ASSOCIATION(mTransitionThreads); 137 138 mShapeResource = NULL; 139 mShape = shape; 140 mUseOverrideTexture = false; 141 buildInstanceData( mShape, loadMaterials ); 142} 143 144TSShapeInstance::~TSShapeInstance() 145{ 146 mMeshObjects.clear(); 147 148 while (mThreadList.size()) 149 destroyThread(mThreadList.last()); 150 151 setMaterialList(NULL); 152 153 delete [] mDirtyFlags; 154} 155 156void TSShapeInstance::buildInstanceData(TSShape * _shape, bool loadMaterials) 157{ 158 mShape = _shape; 159 160 debrisRefCount = 0; 161 162 mCurrentDetailLevel = 0; 163 mCurrentIntraDetailLevel = 1.0f; 164 165 // all triggers off at start 166 mTriggerStates = 0; 167 168 // 169 mAlphaAlways = false; 170 mAlphaAlwaysValue = 1.0f; 171 172 // material list... 173 mMaterialList = NULL; 174 mOwnMaterialList = false; 175 mUseOwnBuffer = false; 176 177 // 178 mData = 0; 179 mScaleCurrentlyAnimated = false; 180 181 if(loadMaterials) 182 setMaterialList(mShape->materialList); 183 184 // set up node data 185 initNodeTransforms(); 186 187 // add objects to trees 188 initMeshObjects(); 189 190 // set up subtree data 191 S32 ss = mShape->subShapeFirstNode.size(); // we have this many subtrees 192 mDirtyFlags = new U32[ss]; 193 194 mGroundThread = NULL; 195 mCurrentDetailLevel = 0; 196 197 animateSubtrees(); 198 199 // Construct billboards if not done already 200 if ( loadMaterials && mShapeResource && GFXDevice::devicePresent() ) 201 mShape->setupBillboardDetails( mShapeResource.getPath().getFullPath() ); 202} 203 204void TSShapeInstance::initNodeTransforms() 205{ 206 // set up node data 207 S32 numNodes = mShape->nodes.size(); 208 mNodeTransforms.setSize(numNodes); 209} 210 211void TSShapeInstance::initMeshObjects() 212{ 213 // add objects to trees 214 S32 numObjects = mShape->objects.size(); 215 mMeshObjects.setSize(numObjects); 216 for (S32 i=0; i<numObjects; i++) 217 { 218 const TSObject * obj = &mShape->objects[i]; 219 MeshObjectInstance * objInst = &mMeshObjects[i]; 220 221 // hook up the object to it's node and transforms. 222 objInst->mTransforms = &mNodeTransforms; 223 objInst->nodeIndex = obj->nodeIndex; 224 225 // set up list of meshes 226 if (obj->numMeshes) 227 objInst->meshList = &mShape->meshes[obj->startMeshIndex]; 228 else 229 objInst->meshList = NULL; 230 231 objInst->object = obj; 232 objInst->forceHidden = false; 233 } 234} 235 236void TSShapeInstance::setMaterialList( TSMaterialList *matList ) 237{ 238 // get rid of old list 239 if ( mOwnMaterialList ) 240 delete mMaterialList; 241 242 mMaterialList = matList; 243 mOwnMaterialList = false; 244 245 // If the material list is already be mapped then 246 // don't bother doing the initializing a second time. 247 // Note: only check the last material instance as this will catch both 248 // uninitialised lists, as well as initialised lists that have had new 249 // materials appended 250 if ( mMaterialList && !mMaterialList->getMaterialInst( mMaterialList->size()-1 ) ) 251 { 252 mMaterialList->setTextureLookupPath( mShapeResource.getPath().getPath() ); 253 mMaterialList->mapMaterials(); 254 Material::sAllowTextureTargetAssignment = true; 255 initMaterialList(); 256 Material::sAllowTextureTargetAssignment = false; 257 } 258} 259 260void TSShapeInstance::cloneMaterialList( const FeatureSet *features ) 261{ 262 if ( mOwnMaterialList ) 263 return; 264 265 Material::sAllowTextureTargetAssignment = true; 266 mMaterialList = new TSMaterialList(mMaterialList); 267 initMaterialList( features ); 268 Material::sAllowTextureTargetAssignment = false; 269 270 mOwnMaterialList = true; 271} 272 273void TSShapeInstance::initMaterialList( const FeatureSet *features ) 274{ 275 // If we don't have features then use the default. 276 if ( !features ) 277 features = &MATMGR->getDefaultFeatures(); 278 279 // Initialize the materials. 280 mMaterialList->initMatInstances( *features, mShape->getVertexFormat() ); 281 282 // TODO: It would be good to go thru all the meshes and 283 // pre-create all the active material hooks for shadows, 284 // reflections, and instancing. This would keep these 285 // hiccups from happening at runtime. 286} 287 288void TSShapeInstance::reSkin( String newBaseName, String oldBaseName ) 289{ 290 if( newBaseName.isEmpty() ) 291 newBaseName = "base"; 292 if( oldBaseName.isEmpty() ) 293 oldBaseName = "base"; 294 295 if ( newBaseName.equal( oldBaseName, String::NoCase ) ) 296 return; 297 298 const U32 oldBaseNameLength = oldBaseName.length(); 299 300 // Make our own copy of the materials list from the resource if necessary 301 if (ownMaterialList() == false) 302 cloneMaterialList(); 303 304 TSMaterialList* pMatList = getMaterialList(); 305 pMatList->setTextureLookupPath( mShapeResource.getPath().getPath() ); 306 307 // Cycle through the materials 308 const Vector<String> &materialNames = pMatList->getMaterialNameList(); 309 for ( S32 i = 0; i < materialNames.size(); i++ ) 310 { 311 // Try changing base 312 const String &pName = materialNames[i]; 313 String newName( String::ToLower(pName) ); 314 newName.replace( String::ToLower(oldBaseName), String::ToLower(newBaseName) ); 315 pMatList->renameMaterial( i, newName ); 316 } 317 318 // Initialize the material instances 319 initMaterialList(); 320} 321 322void TSShapeInstance::resetMaterialList() 323{ 324 TSMaterialList* oMatlist = mShape->materialList; 325 setMaterialList(oMatlist); 326} 327 328//------------------------------------------------------------------------------------- 329// Render & detail selection 330//------------------------------------------------------------------------------------- 331 332void TSShapeInstance::renderDebugNormals( F32 normalScalar, S32 dl ) 333{ 334 if ( dl < 0 ) 335 return; 336 337 AssertFatal( dl >= 0 && dl < mShape->details.size(), 338 "TSShapeInstance::renderDebugNormals() - Bad detail level!" ); 339 340 static GFXStateBlockRef sb; 341 if ( sb.isNull() ) 342 { 343 GFXStateBlockDesc desc; 344 desc.setCullMode( GFXCullNone ); 345 desc.setZReadWrite( true ); 346 desc.zWriteEnable = false; 347 desc.vertexColorEnable = true; 348 349 sb = GFX->createStateBlock( desc ); 350 } 351 GFX->setStateBlock( sb ); 352 353 const TSDetail *detail = &mShape->details[dl]; 354 const S32 ss = detail->subShapeNum; 355 if ( ss < 0 ) 356 return; 357 358 const S32 start = mShape->subShapeFirstObject[ss]; 359 const S32 end = start + mShape->subShapeNumObjects[ss]; 360 361 for ( S32 i = start; i < end; i++ ) 362 { 363 MeshObjectInstance *meshObj = &mMeshObjects[i]; 364 if ( !meshObj ) 365 continue; 366 367 const MatrixF &meshMat = meshObj->getTransform(); 368 369 // Then go through each TSMesh... 370 U32 m = 0; 371 for( TSMesh *mesh = meshObj->getMesh(m); mesh != NULL; mesh = meshObj->getMesh(m++) ) 372 { 373 // and pull out the list of normals. 374 const U32 numNrms = mesh->mNumVerts; 375 PrimBuild::begin( GFXLineList, 2 * numNrms ); 376 for ( U32 n = 0; n < numNrms; n++ ) 377 { 378 const TSMesh::__TSMeshVertexBase &v = mesh->mVertexData.getBase(n); 379 Point3F norm = v.normal(); 380 Point3F vert = v.vert(); 381 382 meshMat.mulP( vert ); 383 meshMat.mulV( norm ); 384 385 // Then render them. 386 PrimBuild::color4f( mFabs( norm.x ), mFabs( norm.y ), mFabs( norm.z ), 1.0f ); 387 PrimBuild::vertex3fv( vert ); 388 PrimBuild::vertex3fv( vert + (norm * normalScalar) ); 389 } 390 391 PrimBuild::end(); 392 } 393 } 394} 395 396void TSShapeInstance::renderDebugNodes() 397{ 398 GFXDrawUtil *drawUtil = GFX->getDrawUtil(); 399 ColorI color( 255, 0, 0, 255 ); 400 401 GFXStateBlockDesc desc; 402 desc.setBlend( false ); 403 desc.setZReadWrite( false, false ); 404 405 for ( U32 i = 0; i < mNodeTransforms.size(); i++ ) 406 drawUtil->drawTransform( desc, mNodeTransforms[i], NULL, NULL ); 407} 408 409void TSShapeInstance::listMeshes( const String &state ) const 410{ 411 if ( state.equal( "All", String::NoCase ) ) 412 { 413 for ( U32 i = 0; i < mMeshObjects.size(); i++ ) 414 { 415 const MeshObjectInstance &mesh = mMeshObjects[i]; 416 Con::warnf( "meshidx %3d, %8s, %s", i, ( mesh.forceHidden ) ? "Hidden" : "Visible", mShape->getMeshName(i).c_str() ); 417 } 418 } 419 else if ( state.equal( "Hidden", String::NoCase ) ) 420 { 421 for ( U32 i = 0; i < mMeshObjects.size(); i++ ) 422 { 423 const MeshObjectInstance &mesh = mMeshObjects[i]; 424 if ( mesh.forceHidden ) 425 Con::warnf( "meshidx %3d, %8s, %s", i, "Visible", mShape->getMeshName(i).c_str() ); 426 } 427 } 428 else if ( state.equal( "Visible", String::NoCase ) ) 429 { 430 for ( U32 i = 0; i < mMeshObjects.size(); i++ ) 431 { 432 const MeshObjectInstance &mesh = mMeshObjects[i]; 433 if ( !mesh.forceHidden ) 434 Con::warnf( "meshidx %3d, %8s, %s", i, "Hidden", mShape->getMeshName(i).c_str() ); 435 } 436 } 437 else 438 { 439 Con::warnf( "TSShapeInstance::listMeshes( %s ) - only All/Hidden/Visible are valid parameters." ); 440 } 441} 442 443void TSShapeInstance::render( const TSRenderState &rdata ) 444{ 445 if (mCurrentDetailLevel<0) 446 return; 447 448 PROFILE_SCOPE( TSShapeInstance_Render ); 449 450 // alphaIn: we start to alpha-in next detail level when intraDL > 1-alphaIn-alphaOut 451 // (finishing when intraDL = 1-alphaOut) 452 // alphaOut: start to alpha-out this detail level when intraDL > 1-alphaOut 453 // NOTE: 454 // intraDL is at 1 when if shape were any closer to us we'd be at dl-1, 455 // intraDL is at 0 when if shape were any farther away we'd be at dl+1 456 F32 alphaOut = mShape->alphaOut[mCurrentDetailLevel]; 457 F32 alphaIn = mShape->alphaIn[mCurrentDetailLevel]; 458 F32 saveAA = mAlphaAlways ? mAlphaAlwaysValue : 1.0f; 459 460 /// This first case is the single detail level render. 461 if ( mCurrentIntraDetailLevel > alphaIn + alphaOut ) 462 render( rdata, mCurrentDetailLevel, mCurrentIntraDetailLevel ); 463 else if ( mCurrentIntraDetailLevel > alphaOut ) 464 { 465 // draw this detail level w/ alpha=1 and next detail level w/ 466 // alpha=1-(intraDl-alphaOut)/alphaIn 467 468 // first draw next detail level 469 if ( mCurrentDetailLevel + 1 < mShape->details.size() && mShape->details[ mCurrentDetailLevel + 1 ].size > 0.0f ) 470 { 471 setAlphaAlways( saveAA * ( alphaIn + alphaOut - mCurrentIntraDetailLevel ) / alphaIn ); 472 render( rdata, mCurrentDetailLevel + 1, 0.0f ); 473 } 474 475 setAlphaAlways( saveAA ); 476 render( rdata, mCurrentDetailLevel, mCurrentIntraDetailLevel ); 477 } 478 else 479 { 480 // draw next detail level w/ alpha=1 and this detail level w/ 481 // alpha = 1-intraDL/alphaOut 482 483 // first draw next detail level 484 if ( mCurrentDetailLevel + 1 < mShape->details.size() && mShape->details[ mCurrentDetailLevel + 1 ].size > 0.0f ) 485 render( rdata, mCurrentDetailLevel+1, 0.0f ); 486 487 setAlphaAlways( saveAA * mCurrentIntraDetailLevel / alphaOut ); 488 render( rdata, mCurrentDetailLevel, mCurrentIntraDetailLevel ); 489 setAlphaAlways( saveAA ); 490 } 491} 492 493void TSShapeInstance::setMeshForceHidden( const char *meshName, bool hidden ) 494{ 495 Vector<MeshObjectInstance>::iterator iter = mMeshObjects.begin(); 496 for ( ; iter != mMeshObjects.end(); iter++ ) 497 { 498 S32 nameIndex = iter->object->nameIndex; 499 const char *name = mShape->names[ nameIndex ]; 500 501 if ( String::compare( meshName, name ) == 0 ) 502 { 503 iter->forceHidden = hidden; 504 return; 505 } 506 } 507} 508 509void TSShapeInstance::setMeshForceHidden( S32 meshIndex, bool hidden ) 510{ 511 AssertFatal( meshIndex > -1 && meshIndex < mMeshObjects.size(), 512 "TSShapeInstance::setMeshForceHidden - Invalid index!" ); 513 514 mMeshObjects[meshIndex].forceHidden = hidden; 515} 516 517void TSShapeInstance::render( const TSRenderState &rdata, S32 dl, F32 intraDL ) 518{ 519 AssertFatal( dl >= 0 && dl < mShape->details.size(),"TSShapeInstance::render" ); 520 521 S32 i; 522 523 const TSDetail * detail = &mShape->details[dl]; 524 S32 ss = detail->subShapeNum; 525 S32 od = detail->objectDetailNum; 526 527 // if we're a billboard detail, draw it and exit 528 if ( ss < 0 ) 529 { 530 PROFILE_SCOPE( TSShapeInstance_RenderBillboards ); 531 532 if ( !rdata.isNoRenderTranslucent() && ( TSLastDetail::smCanShadow || !rdata.getSceneState()->isShadowPass() ) ) 533 mShape->billboardDetails[ dl ]->render( rdata, mAlphaAlways ? mAlphaAlwaysValue : 1.0f ); 534 535 return; 536 } 537 538 S32 start = rdata.isNoRenderNonTranslucent() ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss]; 539 S32 end = rdata.isNoRenderTranslucent() ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss]; 540 TSVertexBufferHandle *realBuffer; 541 542 if (TSShape::smUseHardwareSkinning && !mUseOwnBuffer) 543 { 544 // For hardware skinning, just using the buffer associated with the shape will work fine 545 realBuffer = &mShape->mShapeVertexBuffer; 546 } 547 else 548 { 549 // For software skinning, we need to update our own buffer each frame 550 realBuffer = &mSoftwareVertexBuffer; 551 if (realBuffer->getPointer() == NULL) 552 { 553 mShape->getVertexBuffer(*realBuffer, GFXBufferTypeDynamic); 554 } 555 556 if (bufferNeedsUpdate(od, start, end)) 557 { 558 U8 *buffer = realBuffer->lock(); 559 if (!buffer) 560 return; 561 562 // Base vertex data 563 dMemcpy(buffer, mShape->mShapeVertexData.base, mShape->mShapeVertexData.size); 564 565 // Apply skinned verts (where applicable) 566 for (i = start; i < end; i++) 567 { 568 mMeshObjects[i].updateVertexBuffer(od, buffer); 569 } 570 571 realBuffer->unlock(); 572 } 573 } 574 575 // run through the meshes 576 for (i=start; i<end; i++) 577 { 578 TSRenderState objState = rdata; 579 // following line is handy for debugging, to see what part of the shape that it is rendering 580 const char *name = mShape->names[ mMeshObjects[i].object->nameIndex ]; 581 mMeshObjects[i].render( od, *realBuffer, mMaterialList, objState, mAlphaAlways ? mAlphaAlwaysValue : 1.0f, name ); 582 } 583} 584 585bool TSShapeInstance::bufferNeedsUpdate(S32 objectDetail, S32 start, S32 end) 586{ 587 // run through the meshes 588 for (U32 i = start; i<end; i++) 589 { 590 if (mMeshObjects[i].bufferNeedsUpdate(objectDetail)) 591 return true; 592 } 593 594 return false; 595} 596 597void TSShapeInstance::setCurrentDetail( S32 dl, F32 intraDL ) 598{ 599 PROFILE_SCOPE( TSShapeInstance_setCurrentDetail ); 600 601 mCurrentDetailLevel = mClamp( dl, -1, mShape->mSmallestVisibleDL ); 602 mCurrentIntraDetailLevel = intraDL > 1.0f ? 1.0f : (intraDL < 0.0f ? 0.0f : intraDL); 603 604 // Restrict the chosen detail level by cutoff value. 605 if ( smNumSkipRenderDetails > 0 && mCurrentDetailLevel >= 0 ) 606 { 607 S32 cutoff = getMin( smNumSkipRenderDetails, mShape->mSmallestVisibleDL ); 608 if ( mCurrentDetailLevel < cutoff ) 609 { 610 mCurrentDetailLevel = cutoff; 611 mCurrentIntraDetailLevel = 1.0f; 612 } 613 } 614} 615 616S32 TSShapeInstance::setDetailFromPosAndScale( const SceneRenderState *state, 617 const Point3F &pos, 618 const Point3F &scale ) 619{ 620 VectorF camVector = pos - state->getDiffuseCameraPosition(); 621 F32 dist = getMax( camVector.len(), 0.01f ); 622 F32 invScale = ( 1.0f / getMax( getMax( scale.x, scale.y ), scale.z ) ); 623 624 return setDetailFromDistance( state, dist * invScale ); 625} 626 627S32 TSShapeInstance::setDetailFromDistance( const SceneRenderState *state, F32 scaledDistance ) 628{ 629 PROFILE_SCOPE( TSShapeInstance_setDetailFromDistance ); 630 631 // For debugging/metrics. 632 smLastScaledDistance = scaledDistance; 633 634 // Shortcut if the distance is really close or negative. 635 if ( scaledDistance <= 0.0f ) 636 { 637 mShape->mDetailLevelLookup[0].get( mCurrentDetailLevel, mCurrentIntraDetailLevel ); 638 return mCurrentDetailLevel; 639 } 640 641 // The pixel scale is used the linearly scale the lod 642 // selection based on the viewport size. 643 // 644 // The original calculation from TGEA was... 645 // 646 // pixelScale = viewport.extent.x * 1.6f / 640.0f; 647 // 648 // Since we now work on the viewport height, assuming 649 // 4:3 aspect ratio, we've changed the reference value 650 // to 300 to be more compatible with legacy shapes. 651 // 652 const F32 pixelScale = (state->getViewport().extent.x / state->getViewport().extent.y)*2; 653 654 // This is legacy DTS support for older "multires" based 655 // meshes. The original crossbow weapon uses this. 656 // 657 // If we have more than one detail level and the maxError 658 // is non-negative then we do some sort of screen error 659 // metric for detail selection. 660 // 661 if ( mShape->mUseDetailFromScreenError ) 662 { 663 // The pixel size of 1 meter at the input distance. 664 F32 pixelRadius = state->projectRadius( scaledDistance, 1.0f ) * pixelScale; 665 static const F32 smScreenError = 5.0f; 666 return setDetailFromScreenError( smScreenError / pixelRadius ); 667 } 668 669 // We're inlining SceneRenderState::projectRadius here to 670 // skip the unnessasary divide by zero protection. 671 F32 pixelRadius = ( mShape->mRadius / scaledDistance ) * state->getWorldToScreenScale().y * pixelScale; 672 F32 pixelSize = pixelRadius * smDetailAdjust; 673 674 if ( pixelSize < smSmallestVisiblePixelSize ) { 675 mCurrentDetailLevel = -1; 676 return mCurrentDetailLevel; 677 } 678 679 if ( pixelSize > smSmallestVisiblePixelSize && 680 pixelSize <= mShape->mSmallestVisibleSize ) 681 pixelSize = mShape->mSmallestVisibleSize + 0.01f; 682 683 // For debugging/metrics. 684 smLastPixelSize = pixelSize; 685 686 // Clamp it to an acceptable range for the lookup table. 687 U32 index = (U32)mClampF( pixelSize, 0, mShape->mDetailLevelLookup.size() - 1 ); 688 689 // Check the lookup table for the detail and intra detail levels. 690 mShape->mDetailLevelLookup[ index ].get( mCurrentDetailLevel, mCurrentIntraDetailLevel ); 691 692 // Restrict the chosen detail level by cutoff value. 693 if ( smNumSkipRenderDetails > 0 && mCurrentDetailLevel >= 0 ) 694 { 695 S32 cutoff = getMin( smNumSkipRenderDetails, mShape->mSmallestVisibleDL ); 696 if ( mCurrentDetailLevel < cutoff ) 697 { 698 mCurrentDetailLevel = cutoff; 699 mCurrentIntraDetailLevel = 1.0f; 700 } 701 } 702 703 return mCurrentDetailLevel; 704} 705 706S32 TSShapeInstance::setDetailFromScreenError( F32 errorTolerance ) 707{ 708 PROFILE_SCOPE( TSShapeInstance_setDetailFromScreenError ); 709 710 // For debugging/metrics. 711 smLastScreenErrorTolerance = errorTolerance; 712 713 // note: we use 10 time the average error as the metric...this is 714 // more robust than the maxError...the factor of 10 is to put average error 715 // on about the same scale as maxError. The errorTOL is how much 716 // error we are able to tolerate before going to a more detailed version of the 717 // shape. We look for a pair of details with errors bounding our errorTOL, 718 // and then we select an interpolation parameter to tween betwen them. Ok, so 719 // this isn't exactly an error tolerance. A tween value of 0 is the lower poly 720 // model (higher detail number) and a value of 1 is the higher poly model (lower 721 // detail number). 722 723 // deal with degenerate case first... 724 // if smallest detail corresponds to less than half tolerable error, then don't even draw 725 F32 prevErr; 726 if ( mShape->mSmallestVisibleDL < 0 ) 727 prevErr = 0.0f; 728 else 729 prevErr = 10.0f * mShape->details[mShape->mSmallestVisibleDL].averageError * 20.0f; 730 if ( mShape->mSmallestVisibleDL < 0 || prevErr < errorTolerance ) 731 { 732 // draw last detail 733 mCurrentDetailLevel = mShape->mSmallestVisibleDL; 734 mCurrentIntraDetailLevel = 0.0f; 735 return mCurrentDetailLevel; 736 } 737 738 // this function is a little odd 739 // the reason is that the detail numbers correspond to 740 // when we stop using a given detail level... 741 // we search the details from most error to least error 742 // until we fit under the tolerance (errorTOL) and then 743 // we use the next highest detail (higher error) 744 for (S32 i = mShape->mSmallestVisibleDL; i >= 0; i-- ) 745 { 746 F32 err0 = 10.0f * mShape->details[i].averageError; 747 if ( err0 < errorTolerance ) 748 { 749 // ok, stop here 750 751 // intraDL = 1 corresponds to fully this detail 752 // intraDL = 0 corresponds to the next lower (higher number) detail 753 mCurrentDetailLevel = i; 754 mCurrentIntraDetailLevel = 1.0f - (errorTolerance - err0) / (prevErr - err0); 755 return mCurrentDetailLevel; 756 } 757 prevErr = err0; 758 } 759 760 // get here if we are drawing at DL==0 761 mCurrentDetailLevel = 0; 762 mCurrentIntraDetailLevel = 1.0f; 763 return mCurrentDetailLevel; 764} 765 766//------------------------------------------------------------------------------------- 767// Object (MeshObjectInstance & PluginObjectInstance) render methods 768//------------------------------------------------------------------------------------- 769 770void TSShapeInstance::ObjectInstance::render( S32, TSVertexBufferHandle &vb, TSMaterialList *, TSRenderState &rdata, F32 alpha, const char *meshName ) 771{ 772 AssertFatal(0,"TSShapeInstance::ObjectInstance::render: no default render method."); 773} 774 775void TSShapeInstance::ObjectInstance::updateVertexBuffer( S32 objectDetail, U8 *buffer ) 776{ 777 AssertFatal(0, "TSShapeInstance::ObjectInstance::updateVertexBuffer: no default vertex buffer update method."); 778} 779 780bool TSShapeInstance::ObjectInstance::bufferNeedsUpdate( S32 objectDetai ) 781{ 782 return false; 783} 784 785void TSShapeInstance::MeshObjectInstance::render( S32 objectDetail, 786 TSVertexBufferHandle &vb, 787 TSMaterialList *materials, 788 TSRenderState &rdata, 789 F32 alpha, 790 const char *meshName ) 791{ 792 PROFILE_SCOPE( TSShapeInstance_MeshObjectInstance_render ); 793 794 if ( forceHidden || ( ( visible * alpha ) <= 0.01f ) ) 795 return; 796 797 TSMesh *mesh = getMesh(objectDetail); 798 if ( !mesh ) 799 return; 800 801 const MatrixF &transform = getTransform(); 802 803 if ( rdata.getCuller() ) 804 { 805 Box3F box( mesh->getBounds() ); 806 transform.mul( box ); 807 if ( rdata.getCuller()->isCulled( box ) ) 808 return; 809 } 810 811 GFX->pushWorldMatrix(); 812 GFX->multWorld( transform ); 813 814 mesh->setFade( visible * alpha ); 815 816 // Pass a hint to the mesh that time has advanced and that the 817 // skin is dirty and needs to be updated. This should result 818 // in the skin only updating once per frame in most cases. 819 const U32 currTime = Sim::getCurrentTime(); 820 bool isSkinDirty = (currTime != mLastTime) || (objectDetail != mLastObjectDetail); 821 822 // Update active transform list for bones for GPU skinning 823 if ( mesh->getMeshType() == TSMesh::SkinMeshType ) 824 { 825 if (isSkinDirty) 826 { 827 static_cast<TSSkinMesh*>(mesh)->updateSkinBones(*mTransforms, mActiveTransforms); 828 } 829 rdata.setNodeTransforms(mActiveTransforms.address(), mActiveTransforms.size()); 830 } 831 832 mesh->render( materials, 833 rdata, 834 isSkinDirty, 835 *mTransforms, 836 vb, 837 meshName ); 838 839 // Update the last render time. 840 mLastTime = currTime; 841 mLastObjectDetail = objectDetail; 842 GFX->popWorldMatrix(); 843} 844 845void TSShapeInstance::MeshObjectInstance::updateVertexBuffer(S32 objectDetail, U8 *buffer) 846{ 847 PROFILE_SCOPE(TSShapeInstance_MeshObjectInstance_updateVertexBuffer); 848 849 if (forceHidden || ((visible) <= 0.01f)) 850 return; 851 852 TSMesh *mesh = getMesh(objectDetail); 853 if (!mesh) 854 return; 855 856 // Update the buffer here 857 if (mesh->getMeshType() == TSMesh::SkinMeshType) 858 { 859 static_cast<TSSkinMesh*>(mesh)->updateSkinBuffer(*mTransforms, buffer); 860 } 861 862 mLastTime = Sim::getCurrentTime(); 863} 864 865bool TSShapeInstance::MeshObjectInstance::bufferNeedsUpdate( S32 objectDetail ) 866{ 867 TSMesh *mesh = getMesh(objectDetail); 868 const U32 currTime = Sim::getCurrentTime(); 869 return mesh && mesh->getMeshType() == TSMesh::SkinMeshType && currTime != mLastTime; 870} 871 872TSShapeInstance::MeshObjectInstance::MeshObjectInstance() 873 : meshList(0), object(0), frame(0), matFrame(0), 874 visible(1.0f), forceHidden(false), mLastTime(0), mLastObjectDetail(0) 875{ 876} 877 878void TSShapeInstance::prepCollision() 879{ 880 PROFILE_SCOPE( TSShapeInstance_PrepCollision ); 881 882 // Iterate over all our meshes and call prepCollision on them... 883 for(S32 i=0; i<mShape->meshes.size(); i++) 884 { 885 if(mShape->meshes[i]) 886 mShape->meshes[i]->prepOpcodeCollision(); 887 } 888} 889 890// Returns true is the shape contains any materials with accumulation enabled. 891bool TSShapeInstance::hasAccumulation() 892{ 893 bool result = false; 894 for ( U32 i = 0; i < mMaterialList->size(); ++i ) 895 { 896 BaseMatInstance* mat = mMaterialList->getMaterialInst(i); 897 if ( mat->hasAccumulation() ) 898 result = true; 899 } 900 return result; 901} 902 903void TSShapeInstance::setUseOwnBuffer() 904{ 905 mUseOwnBuffer = true; 906} 907