| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903 | //-----------------------------------------------------------------------------// Copyright (c) 2012 GarageGames, LLC//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to// deal in the Software without restriction, including without limitation the// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or// sell copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS// IN THE SOFTWARE.//-----------------------------------------------------------------------------#include "platform/platform.h"#include "ts/tsShapeInstance.h"#include "ts/tsLastDetail.h"#include "ts/tsMaterialList.h"#include "console/consoleTypes.h"#include "ts/tsDecal.h"#include "platform/profiler.h"#include "core/frameAllocator.h"#include "gfx/gfxDevice.h"#include "materials/materialManager.h"#include "materials/materialFeatureTypes.h"#include "materials/sceneData.h"#include "materials/matInstance.h"#include "scene/sceneRenderState.h"#include "gfx/primBuilder.h"#include "gfx/gfxDrawUtil.h"#include "core/module.h"MODULE_BEGIN( TSShapeInstance )   MODULE_INIT   {      Con::addVariable("$pref::TS::detailAdjust", TypeF32, &TSShapeInstance::smDetailAdjust,         "@brief User perference for scaling the TSShape level of detail.\n"         "The smaller the value the closer the camera must get to see the "         "highest LOD.  This setting can have a huge impact on performance in "         "mesh heavy scenes.  The default value is 1.\n"         "@ingroup Rendering\n" );      Con::addVariable("$pref::TS::skipLoadDLs", TypeS32, &TSShape::smNumSkipLoadDetails,         "@brief User perference which causes TSShapes to skip loading higher lods.\n"         "This potentialy reduces the GPU resources and materials generated as well as "         "limits the LODs rendered.  The default value is 0.\n"         "@see $pref::TS::skipRenderDLs\n"         "@ingroup Rendering\n" );      Con::addVariable("$pref::TS::skipRenderDLs", TypeS32, &TSShapeInstance::smNumSkipRenderDetails,         "@brief User perference which causes TSShapes to skip rendering higher lods.\n"         "This will reduce the number of draw calls and triangles rendered and improve "         "rendering performance when proper LODs have been created for your models. "         "The default value is 0.\n"         "@see $pref::TS::skipLoadDLs\n"         "@ingroup Rendering\n" );      Con::addVariable("$pref::TS::smallestVisiblePixelSize", TypeF32, &TSShapeInstance::smSmallestVisiblePixelSize,         "@brief User perference which sets the smallest pixel size at which TSShapes will skip rendering.\n"         "This will force all shapes to stop rendering when they get smaller than this size. "         "The default value is -1 which disables it.\n"         "@ingroup Rendering\n" );      Con::addVariable("$pref::TS::maxInstancingVerts", TypeS32, &TSMesh::smMaxInstancingVerts,         "@brief Enables mesh instancing on non-skin meshes that have less that this count of verts.\n"         "The default value is 2000.  Higher values can degrade performance.\n"         "@ingroup Rendering\n" );   }MODULE_END;F32                           TSShapeInstance::smDetailAdjust = 1.0f;F32                           TSShapeInstance::smSmallestVisiblePixelSize = -1.0f;S32                           TSShapeInstance::smNumSkipRenderDetails = 0;F32                           TSShapeInstance::smLastScreenErrorTolerance = 0.0f;F32                           TSShapeInstance::smLastScaledDistance = 0.0f;F32                           TSShapeInstance::smLastPixelSize = 0.0f;Vector<QuatF>                 TSShapeInstance::smNodeCurrentRotations(__FILE__, __LINE__);Vector<Point3F>               TSShapeInstance::smNodeCurrentTranslations(__FILE__, __LINE__);Vector<F32>                   TSShapeInstance::smNodeCurrentUniformScales(__FILE__, __LINE__);Vector<Point3F>               TSShapeInstance::smNodeCurrentAlignedScales(__FILE__, __LINE__);Vector<TSScale>               TSShapeInstance::smNodeCurrentArbitraryScales(__FILE__, __LINE__);Vector<MatrixF>               TSShapeInstance::smNodeLocalTransforms(__FILE__, __LINE__);TSIntegerSet                  TSShapeInstance::smNodeLocalTransformDirty;Vector<TSThread*>             TSShapeInstance::smRotationThreads(__FILE__, __LINE__);Vector<TSThread*>             TSShapeInstance::smTranslationThreads(__FILE__, __LINE__);Vector<TSThread*>             TSShapeInstance::smScaleThreads(__FILE__, __LINE__);//-------------------------------------------------------------------------------------// constructors, destructors, initialization//-------------------------------------------------------------------------------------TSShapeInstance::TSShapeInstance( const Resource<TSShape> &shape, bool loadMaterials ){   VECTOR_SET_ASSOCIATION(mMeshObjects);   VECTOR_SET_ASSOCIATION(mNodeTransforms);   VECTOR_SET_ASSOCIATION(mNodeReferenceRotations);   VECTOR_SET_ASSOCIATION(mNodeReferenceTranslations);   VECTOR_SET_ASSOCIATION(mNodeReferenceUniformScales);   VECTOR_SET_ASSOCIATION(mNodeReferenceScaleFactors);   VECTOR_SET_ASSOCIATION(mNodeReferenceArbitraryScaleRots);   VECTOR_SET_ASSOCIATION(mThreadList);   VECTOR_SET_ASSOCIATION(mTransitionThreads);   mShapeResource = shape;   mShape = mShapeResource;   mUseOverrideTexture = false;   buildInstanceData( mShape, loadMaterials );}TSShapeInstance::TSShapeInstance( TSShape *shape, bool loadMaterials ){   VECTOR_SET_ASSOCIATION(mMeshObjects);   VECTOR_SET_ASSOCIATION(mNodeTransforms);   VECTOR_SET_ASSOCIATION(mNodeReferenceRotations);   VECTOR_SET_ASSOCIATION(mNodeReferenceTranslations);   VECTOR_SET_ASSOCIATION(mNodeReferenceUniformScales);   VECTOR_SET_ASSOCIATION(mNodeReferenceScaleFactors);   VECTOR_SET_ASSOCIATION(mNodeReferenceArbitraryScaleRots);   VECTOR_SET_ASSOCIATION(mThreadList);   VECTOR_SET_ASSOCIATION(mTransitionThreads);   mShapeResource = NULL;   mShape = shape;   mUseOverrideTexture = false;   buildInstanceData( mShape, loadMaterials );}TSShapeInstance::~TSShapeInstance(){   mMeshObjects.clear();   while (mThreadList.size())      destroyThread(mThreadList.last());   setMaterialList(NULL);   delete [] mDirtyFlags;}void TSShapeInstance::buildInstanceData(TSShape * _shape, bool loadMaterials){   mShape = _shape;   debrisRefCount = 0;   mCurrentDetailLevel = 0;   mCurrentIntraDetailLevel = 1.0f;   // all triggers off at start   mTriggerStates = 0;   //   mAlphaAlways = false;   mAlphaAlwaysValue = 1.0f;   // material list...   mMaterialList = NULL;   mOwnMaterialList = false;   mUseOwnBuffer = false;   //   mData = 0;   mScaleCurrentlyAnimated = false;   if(loadMaterials)      setMaterialList(mShape->materialList);   // set up node data   initNodeTransforms();   // add objects to trees   initMeshObjects();   // set up subtree data   S32 ss = mShape->subShapeFirstNode.size(); // we have this many subtrees   mDirtyFlags = new U32[ss];   mGroundThread = NULL;   mCurrentDetailLevel = 0;   animateSubtrees();   // Construct billboards if not done already   if ( loadMaterials && mShapeResource && GFXDevice::devicePresent() )      mShape->setupBillboardDetails( mShapeResource.getPath().getFullPath() );}void TSShapeInstance::initNodeTransforms(){   // set up node data   S32 numNodes = mShape->nodes.size();   mNodeTransforms.setSize(numNodes);}void TSShapeInstance::initMeshObjects(){   // add objects to trees   S32 numObjects = mShape->objects.size();   mMeshObjects.setSize(numObjects);   for (S32 i=0; i<numObjects; i++)   {      const TSObject * obj = &mShape->objects[i];      MeshObjectInstance * objInst = &mMeshObjects[i];      // hook up the object to it's node and transforms.      objInst->mTransforms = &mNodeTransforms;      objInst->nodeIndex = obj->nodeIndex;      // set up list of meshes      if (obj->numMeshes)         objInst->meshList = &mShape->meshes[obj->startMeshIndex];      else         objInst->meshList = NULL;      objInst->object = obj;      objInst->forceHidden = false;   }}void TSShapeInstance::setMaterialList( TSMaterialList *matList ){   // get rid of old list   if ( mOwnMaterialList )      delete mMaterialList;   mMaterialList = matList;   mOwnMaterialList = false;   // If the material list is already be mapped then   // don't bother doing the initializing a second time.   // Note: only check the last material instance as this will catch both   // uninitialised lists, as well as initialised lists that have had new   // materials appended   if ( mMaterialList && !mMaterialList->getMaterialInst( mMaterialList->size()-1 ) )   {      mMaterialList->setTextureLookupPath( mShapeResource.getPath().getPath() );      mMaterialList->mapMaterials();      Material::sAllowTextureTargetAssignment = true;      initMaterialList();      Material::sAllowTextureTargetAssignment = false;   }}void TSShapeInstance::cloneMaterialList( const FeatureSet *features ){   if ( mOwnMaterialList )      return;   Material::sAllowTextureTargetAssignment = true;   mMaterialList = new TSMaterialList(mMaterialList);   initMaterialList( features );   Material::sAllowTextureTargetAssignment = false;   mOwnMaterialList = true;}void TSShapeInstance::initMaterialList( const FeatureSet *features ){   // If we don't have features then use the default.   if ( !features )      features = &MATMGR->getDefaultFeatures();   // Initialize the materials.   mMaterialList->initMatInstances( *features, mShape->getVertexFormat() );   // TODO: It would be good to go thru all the meshes and   // pre-create all the active material hooks for shadows,   // reflections, and instancing.  This would keep these   // hiccups from happening at runtime.}void TSShapeInstance::reSkin( String newBaseName, String oldBaseName ){   if( newBaseName.isEmpty() )      newBaseName = "base";   if( oldBaseName.isEmpty() )      oldBaseName = "base";   if ( newBaseName.equal( oldBaseName, String::NoCase ) )      return;   // Make our own copy of the materials list from the resource if necessary   if (ownMaterialList() == false)      cloneMaterialList();   TSMaterialList* pMatList = getMaterialList();   pMatList->setTextureLookupPath( mShapeResource.getPath().getPath() );   // Cycle through the materials   const Vector<String> &materialNames = pMatList->getMaterialNameList();   for ( S32 i = 0; i < materialNames.size(); i++ )   {      // Try changing base      const String &pName = materialNames[i];	  String newName( String::ToLower(pName) );	  newName.replace( String::ToLower(oldBaseName), String::ToLower(newBaseName) );	  pMatList->renameMaterial( i, newName );   }   // Initialize the material instances   initMaterialList();}void TSShapeInstance::resetMaterialList(){	TSMaterialList* oMatlist = mShape->materialList;	setMaterialList(oMatlist);}//-------------------------------------------------------------------------------------// Render & detail selection//-------------------------------------------------------------------------------------void TSShapeInstance::renderDebugNormals( F32 normalScalar, S32 dl ){   if ( dl < 0 )      return;   AssertFatal( dl >= 0 && dl < mShape->details.size(),      "TSShapeInstance::renderDebugNormals() - Bad detail level!" );   static GFXStateBlockRef sb;   if ( sb.isNull() )   {      GFXStateBlockDesc desc;      desc.setCullMode( GFXCullNone );      desc.setZReadWrite( true );      desc.zWriteEnable = false;      desc.vertexColorEnable = true;      sb = GFX->createStateBlock( desc );   }   GFX->setStateBlock( sb );   const TSDetail *detail = &mShape->details[dl];   const S32 ss = detail->subShapeNum;   if ( ss < 0 )      return;   const S32 start = mShape->subShapeFirstObject[ss];   const S32 end   = start + mShape->subShapeNumObjects[ss];   for ( S32 i = start; i < end; i++ )   {      MeshObjectInstance *meshObj = &mMeshObjects[i];      if ( !meshObj )         continue;      const MatrixF &meshMat = meshObj->getTransform();      // Then go through each TSMesh...      U32 m = 0;      for( TSMesh *mesh = meshObj->getMesh(m); mesh != NULL; mesh = meshObj->getMesh(m++) )      {         // and pull out the list of normals.         const U32 numNrms = mesh->mNumVerts;         PrimBuild::begin( GFXLineList, 2 * numNrms );         for ( U32 n = 0; n < numNrms; n++ )         {            const TSMesh::__TSMeshVertexBase &v = mesh->mVertexData.getBase(n);            Point3F norm = v.normal();            Point3F vert = v.vert();            meshMat.mulP( vert );            meshMat.mulV( norm );            // Then render them.            PrimBuild::color4f( mFabs( norm.x ), mFabs( norm.y ), mFabs( norm.z ), 1.0f );            PrimBuild::vertex3fv( vert );            PrimBuild::vertex3fv( vert + (norm * normalScalar) );         }         PrimBuild::end();      }   }}void TSShapeInstance::renderDebugNodes(){   GFXDrawUtil *drawUtil = GFX->getDrawUtil();   ColorI color( 255, 0, 0, 255 );   GFXStateBlockDesc desc;   desc.setBlend( false );   desc.setZReadWrite( false, false );   for ( U32 i = 0; i < mNodeTransforms.size(); i++ )      drawUtil->drawTransform( desc, mNodeTransforms[i], NULL, NULL );}void TSShapeInstance::listMeshes( const String &state ) const{   if ( state.equal( "All", String::NoCase ) )   {      for ( U32 i = 0; i < mMeshObjects.size(); i++ )      {         const MeshObjectInstance &mesh = mMeshObjects[i];         Con::warnf( "meshidx %3d, %8s, %s", i, ( mesh.forceHidden ) ? "Hidden" : "Visible", mShape->getMeshName(i).c_str() );               }   }   else if ( state.equal( "Hidden", String::NoCase ) )   {      for ( U32 i = 0; i < mMeshObjects.size(); i++ )      {         const MeshObjectInstance &mesh = mMeshObjects[i];         if ( mesh.forceHidden )            Con::warnf( "meshidx %3d, %8s, %s", i, "Visible", mShape->getMeshName(i).c_str() );               }   }   else if ( state.equal( "Visible", String::NoCase ) )   {      for ( U32 i = 0; i < mMeshObjects.size(); i++ )      {         const MeshObjectInstance &mesh = mMeshObjects[i];         if ( !mesh.forceHidden )            Con::warnf( "meshidx %3d, %8s, %s", i, "Hidden", mShape->getMeshName(i).c_str() );               }   }   else   {      Con::warnf( "TSShapeInstance::listMeshes( %s ) - only All/Hidden/Visible are valid parameters." );   }}void TSShapeInstance::render( const TSRenderState &rdata ){   if (mCurrentDetailLevel<0)      return;   PROFILE_SCOPE( TSShapeInstance_Render );   // alphaIn:  we start to alpha-in next detail level when intraDL > 1-alphaIn-alphaOut   //           (finishing when intraDL = 1-alphaOut)   // alphaOut: start to alpha-out this detail level when intraDL > 1-alphaOut   // NOTE:   //   intraDL is at 1 when if shape were any closer to us we'd be at dl-1,   //   intraDL is at 0 when if shape were any farther away we'd be at dl+1   F32 alphaOut = mShape->alphaOut[mCurrentDetailLevel];   F32 alphaIn  = mShape->alphaIn[mCurrentDetailLevel];   F32 saveAA = mAlphaAlways ? mAlphaAlwaysValue : 1.0f;   /// This first case is the single detail level render.   if ( mCurrentIntraDetailLevel > alphaIn + alphaOut )      render( rdata, mCurrentDetailLevel, mCurrentIntraDetailLevel );   else if ( mCurrentIntraDetailLevel > alphaOut )   {      // draw this detail level w/ alpha=1 and next detail level w/      // alpha=1-(intraDl-alphaOut)/alphaIn      // first draw next detail level      if ( mCurrentDetailLevel + 1 < mShape->details.size() && mShape->details[ mCurrentDetailLevel + 1 ].size > 0.0f )      {         setAlphaAlways( saveAA * ( alphaIn + alphaOut - mCurrentIntraDetailLevel ) / alphaIn );         render( rdata, mCurrentDetailLevel + 1, 0.0f );      }      setAlphaAlways( saveAA );      render( rdata, mCurrentDetailLevel, mCurrentIntraDetailLevel );   }   else   {      // draw next detail level w/ alpha=1 and this detail level w/      // alpha = 1-intraDL/alphaOut      // first draw next detail level      if ( mCurrentDetailLevel + 1 < mShape->details.size() && mShape->details[ mCurrentDetailLevel + 1 ].size > 0.0f )         render( rdata, mCurrentDetailLevel+1, 0.0f );      setAlphaAlways( saveAA * mCurrentIntraDetailLevel / alphaOut );      render( rdata, mCurrentDetailLevel, mCurrentIntraDetailLevel );      setAlphaAlways( saveAA );   }}void TSShapeInstance::setMeshForceHidden( const char *meshName, bool hidden ){   Vector<MeshObjectInstance>::iterator iter = mMeshObjects.begin();   for ( ; iter != mMeshObjects.end(); iter++ )   {      S32 nameIndex = iter->object->nameIndex;      const char *name = mShape->names[ nameIndex ];      if ( String::compare( meshName, name ) == 0 )      {         iter->forceHidden = hidden;         return;      }   }}void TSShapeInstance::setMeshForceHidden( S32 meshIndex, bool hidden ){   AssertFatal( meshIndex > -1 && meshIndex < mMeshObjects.size(),      "TSShapeInstance::setMeshForceHidden - Invalid index!" );                     mMeshObjects[meshIndex].forceHidden = hidden;}void TSShapeInstance::render( const TSRenderState &rdata, S32 dl, F32 intraDL ){   AssertFatal( dl >= 0 && dl < mShape->details.size(),"TSShapeInstance::render" );   S32 i;   const TSDetail * detail = &mShape->details[dl];   S32 ss = detail->subShapeNum;   S32 od = detail->objectDetailNum;   // if we're a billboard detail, draw it and exit   if ( ss < 0 )   {      PROFILE_SCOPE( TSShapeInstance_RenderBillboards );            if ( !rdata.isNoRenderTranslucent() && ( TSLastDetail::smCanShadow || !rdata.getSceneState()->isShadowPass() ) )         mShape->billboardDetails[ dl ]->render( rdata, mAlphaAlways ? mAlphaAlwaysValue : 1.0f );      return;   }   S32 start = rdata.isNoRenderNonTranslucent() ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss];   S32 end = rdata.isNoRenderTranslucent() ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss];   TSVertexBufferHandle *realBuffer;   if (TSShape::smUseHardwareSkinning && !mUseOwnBuffer)   {      // For hardware skinning, just using the buffer associated with the shape will work fine      realBuffer = &mShape->mShapeVertexBuffer;   }   else   {      // For software skinning, we need to update our own buffer each frame      realBuffer = &mSoftwareVertexBuffer;      if (realBuffer->getPointer() == NULL)      {         mShape->getVertexBuffer(*realBuffer, GFXBufferTypeDynamic);      }      if (bufferNeedsUpdate(od, start, end))      {         U8 *buffer = realBuffer->lock();         if (!buffer)            return;         // Base vertex data         dMemcpy(buffer, mShape->mShapeVertexData.base, mShape->mShapeVertexData.size);         // Apply skinned verts (where applicable)         for (i = start; i < end; i++)         {            mMeshObjects[i].updateVertexBuffer(od, buffer);         }         realBuffer->unlock();      }   }   // run through the meshes   for (i=start; i<end; i++)   {      TSRenderState objState = rdata;      // following line is handy for debugging, to see what part of the shape that it is rendering      const char *name = mShape->names[ mMeshObjects[i].object->nameIndex ];      mMeshObjects[i].render( od, *realBuffer, mMaterialList, objState, mAlphaAlways ? mAlphaAlwaysValue : 1.0f, name );   }}bool TSShapeInstance::bufferNeedsUpdate(S32 objectDetail, S32 start, S32 end){   // run through the meshes   for (U32 i = start; i<end; i++)   {      if (mMeshObjects[i].bufferNeedsUpdate(objectDetail))         return true;   }   return false;}void TSShapeInstance::setCurrentDetail( S32 dl, F32 intraDL ){   PROFILE_SCOPE( TSShapeInstance_setCurrentDetail );   mCurrentDetailLevel = mClamp( dl, -1, mShape->mSmallestVisibleDL );   mCurrentIntraDetailLevel = intraDL > 1.0f ? 1.0f : (intraDL < 0.0f ? 0.0f : intraDL);   // Restrict the chosen detail level by cutoff value.   if ( smNumSkipRenderDetails > 0 && mCurrentDetailLevel >= 0 )   {      S32 cutoff = getMin( smNumSkipRenderDetails, mShape->mSmallestVisibleDL );      if ( mCurrentDetailLevel < cutoff )      {         mCurrentDetailLevel = cutoff;         mCurrentIntraDetailLevel = 1.0f;      }   }}S32 TSShapeInstance::setDetailFromPosAndScale(  const SceneRenderState *state,                                                const Point3F &pos,                                                 const Point3F &scale ){   VectorF camVector = pos - state->getDiffuseCameraPosition();   F32 dist = getMax( camVector.len(), 0.01f );   F32 invScale = ( 1.0f / getMax( getMax( scale.x, scale.y ), scale.z ) );   return setDetailFromDistance( state, dist * invScale );}S32 TSShapeInstance::setDetailFromDistance( const SceneRenderState *state, F32 scaledDistance ){   PROFILE_SCOPE( TSShapeInstance_setDetailFromDistance );   // For debugging/metrics.   smLastScaledDistance = scaledDistance;   // Shortcut if the distance is really close or negative.   if ( scaledDistance <= 0.0f )   {      mShape->mDetailLevelLookup[0].get( mCurrentDetailLevel, mCurrentIntraDetailLevel );      return mCurrentDetailLevel;   }   // The pixel scale is used the linearly scale the lod   // selection based on the viewport size.   //   // The original calculation from TGEA was...   //   // pixelScale = viewport.extent.x * 1.6f / 640.0f;   //   // Since we now work on the viewport height, assuming   // 4:3 aspect ratio, we've changed the reference value   // to 300 to be more compatible with legacy shapes.   //   const F32 pixelScale = (state->getViewport().extent.x / state->getViewport().extent.y)*2;   // This is legacy DTS support for older "multires" based   // meshes.  The original crossbow weapon uses this.   //   // If we have more than one detail level and the maxError   // is non-negative then we do some sort of screen error    // metric for detail selection.   //   if ( mShape->mUseDetailFromScreenError )   {      // The pixel size of 1 meter at the input distance.      F32 pixelRadius = state->projectRadius( scaledDistance, 1.0f ) * pixelScale;      static const F32 smScreenError = 5.0f;      return setDetailFromScreenError( smScreenError / pixelRadius );   }   // We're inlining SceneRenderState::projectRadius here to    // skip the unnessasary divide by zero protection.   F32 pixelRadius = ( mShape->mRadius / scaledDistance ) * state->getWorldToScreenScale().y * pixelScale;   F32 pixelSize = pixelRadius * smDetailAdjust;   if ( pixelSize < smSmallestVisiblePixelSize ) {      mCurrentDetailLevel = -1;      return mCurrentDetailLevel;   }   if (  pixelSize > smSmallestVisiblePixelSize &&          pixelSize <= mShape->mSmallestVisibleSize )      pixelSize = mShape->mSmallestVisibleSize + 0.01f;   // For debugging/metrics.   smLastPixelSize = pixelSize;   // Clamp it to an acceptable range for the lookup table.   U32 index = (U32)mClampF( pixelSize, 0, mShape->mDetailLevelLookup.size() - 1 );   // Check the lookup table for the detail and intra detail levels.   mShape->mDetailLevelLookup[ index ].get( mCurrentDetailLevel, mCurrentIntraDetailLevel );   // Restrict the chosen detail level by cutoff value.   if ( smNumSkipRenderDetails > 0 && mCurrentDetailLevel >= 0 )   {      S32 cutoff = getMin( smNumSkipRenderDetails, mShape->mSmallestVisibleDL );      if ( mCurrentDetailLevel < cutoff )      {         mCurrentDetailLevel = cutoff;         mCurrentIntraDetailLevel = 1.0f;      }   }   return mCurrentDetailLevel;}S32 TSShapeInstance::setDetailFromScreenError( F32 errorTolerance ){   PROFILE_SCOPE( TSShapeInstance_setDetailFromScreenError );   // For debugging/metrics.   smLastScreenErrorTolerance = errorTolerance;   // note:  we use 10 time the average error as the metric...this is   // more robust than the maxError...the factor of 10 is to put average error   // on about the same scale as maxError.  The errorTOL is how much   // error we are able to tolerate before going to a more detailed version of the   // shape.  We look for a pair of details with errors bounding our errorTOL,   // and then we select an interpolation parameter to tween betwen them.  Ok, so   // this isn't exactly an error tolerance.  A tween value of 0 is the lower poly   // model (higher detail number) and a value of 1 is the higher poly model (lower   // detail number).   // deal with degenerate case first...   // if smallest detail corresponds to less than half tolerable error, then don't even draw   F32 prevErr;   if ( mShape->mSmallestVisibleDL < 0 )      prevErr = 0.0f;   else      prevErr = 10.0f * mShape->details[mShape->mSmallestVisibleDL].averageError * 20.0f;   if ( mShape->mSmallestVisibleDL < 0 || prevErr < errorTolerance )   {      // draw last detail      mCurrentDetailLevel = mShape->mSmallestVisibleDL;      mCurrentIntraDetailLevel = 0.0f;      return mCurrentDetailLevel;   }   // this function is a little odd   // the reason is that the detail numbers correspond to   // when we stop using a given detail level...   // we search the details from most error to least error   // until we fit under the tolerance (errorTOL) and then   // we use the next highest detail (higher error)   for (S32 i = mShape->mSmallestVisibleDL; i >= 0; i-- )   {      F32 err0 = 10.0f * mShape->details[i].averageError;      if ( err0 < errorTolerance )      {         // ok, stop here         // intraDL = 1 corresponds to fully this detail         // intraDL = 0 corresponds to the next lower (higher number) detail         mCurrentDetailLevel = i;         mCurrentIntraDetailLevel = 1.0f - (errorTolerance - err0) / (prevErr - err0);         return mCurrentDetailLevel;      }      prevErr = err0;   }   // get here if we are drawing at DL==0   mCurrentDetailLevel = 0;   mCurrentIntraDetailLevel = 1.0f;   return mCurrentDetailLevel;}//-------------------------------------------------------------------------------------// Object (MeshObjectInstance & PluginObjectInstance) render methods//-------------------------------------------------------------------------------------void TSShapeInstance::ObjectInstance::render( S32, TSVertexBufferHandle &vb, TSMaterialList *, TSRenderState &rdata, F32 alpha, const char *meshName ){   AssertFatal(0,"TSShapeInstance::ObjectInstance::render:  no default render method.");}void TSShapeInstance::ObjectInstance::updateVertexBuffer( S32 objectDetail, U8 *buffer ){   AssertFatal(0, "TSShapeInstance::ObjectInstance::updateVertexBuffer:  no default vertex buffer update method.");}bool TSShapeInstance::ObjectInstance::bufferNeedsUpdate( S32 objectDetai ){   return false;}void TSShapeInstance::MeshObjectInstance::render(  S32 objectDetail,                                                    TSVertexBufferHandle &vb,                                                   TSMaterialList *materials,                                                    TSRenderState &rdata,                                                    F32 alpha,                                                   const char *meshName ){   PROFILE_SCOPE( TSShapeInstance_MeshObjectInstance_render );   if ( forceHidden || ( ( visible * alpha ) <= 0.01f ) )      return;   TSMesh *mesh = getMesh(objectDetail);   if ( !mesh )      return;   const MatrixF &transform = getTransform();   if ( rdata.getCuller() )   {      Box3F box( mesh->getBounds() );      transform.mul( box );      if ( rdata.getCuller()->isCulled( box ) )         return;   }   GFX->pushWorldMatrix();   GFX->multWorld( transform );   mesh->setFade( visible * alpha );   // Pass a hint to the mesh that time has advanced and that the   // skin is dirty and needs to be updated.  This should result   // in the skin only updating once per frame in most cases.   const U32 currTime = Sim::getCurrentTime();   bool isSkinDirty = (currTime != mLastTime) || (objectDetail != mLastObjectDetail);   // Update active transform list for bones for GPU skinning   if ( mesh->getMeshType() == TSMesh::SkinMeshType )   {      if (isSkinDirty)      {         static_cast<TSSkinMesh*>(mesh)->updateSkinBones(*mTransforms, mActiveTransforms);      }      rdata.setNodeTransforms(mActiveTransforms.address(), mActiveTransforms.size());   }   mesh->render(  materials,                   rdata,                   isSkinDirty,                  *mTransforms,                   vb,                  meshName );   // Update the last render time.   mLastTime = currTime;   mLastObjectDetail = objectDetail;   GFX->popWorldMatrix();}void TSShapeInstance::MeshObjectInstance::updateVertexBuffer(S32 objectDetail, U8 *buffer){   PROFILE_SCOPE(TSShapeInstance_MeshObjectInstance_updateVertexBuffer);   if (forceHidden || ((visible) <= 0.01f))      return;   TSMesh *mesh = getMesh(objectDetail);   if (!mesh)      return;   // Update the buffer here   if (mesh->getMeshType() == TSMesh::SkinMeshType)   {      static_cast<TSSkinMesh*>(mesh)->updateSkinBuffer(*mTransforms, buffer);   }   mLastTime = Sim::getCurrentTime();}bool TSShapeInstance::MeshObjectInstance::bufferNeedsUpdate( S32 objectDetail ){   TSMesh *mesh = getMesh(objectDetail);   const U32 currTime = Sim::getCurrentTime();   return mesh && mesh->getMeshType() == TSMesh::SkinMeshType && currTime != mLastTime;}TSShapeInstance::MeshObjectInstance::MeshObjectInstance()	: meshList(0), object(0), frame(0), matFrame(0),	visible(1.0f), forceHidden(false), mLastTime(0), mLastObjectDetail(0){}void TSShapeInstance::prepCollision(){   PROFILE_SCOPE( TSShapeInstance_PrepCollision );   // Iterate over all our meshes and call prepCollision on them...   for(S32 i=0; i<mShape->meshes.size(); i++)   {      if(mShape->meshes[i])         mShape->meshes[i]->prepOpcodeCollision();   }}// Returns true is the shape contains any materials with accumulation enabled.bool TSShapeInstance::hasAccumulation(){   bool result = false;   for ( U32 i = 0; i < mMaterialList->size(); ++i )   {      BaseMatInstance* mat = mMaterialList->getMaterialInst(i);      if (mat != nullptr && mat->hasAccumulation() )         result = true;   }   return result;}void TSShapeInstance::setUseOwnBuffer(){   mUseOwnBuffer = true;}
 |