123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905 |
- //-----------------------------------------------------------------------------
- // 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;
- const U32 oldBaseNameLength = oldBaseName.length();
- // 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->hasAccumulation() )
- result = true;
- }
- return result;
- }
- void TSShapeInstance::setUseOwnBuffer()
- {
- mUseOwnBuffer = true;
- }
|