Torque3D Documentation / _generateds / tsShapeInstance.cpp

tsShapeInstance.cpp

Engine/source/ts/tsShapeInstance.cpp

More...

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