Torque3D Documentation / _generateds / groundCover.cpp

groundCover.cpp

Engine/source/T3D/fx/groundCover.cpp

More...

Classes:

class

This defines one grid cell.

Public Functions

ConsoleDocClass(GroundCover , "@brief Covers the ground in a field of objects (IE: Grass, Flowers, etc)." "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Foliage\n</a>" )

This is used for rendering ground cover billboards.

Detailed Description

Public Functions

ConsoleDocClass(GroundCover , "@brief Covers the ground in a field of objects (IE: Grass, Flowers, etc)." "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Foliage\n</a>" )

GFXImplementVertexFormat(GCVertex )

This is used for rendering ground cover billboards.

IMPLEMENT_CO_NETOBJECT_V1(GroundCover )

   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 "T3D/fx/groundCover.h"
  26
  27#include "core/resourceManager.h"
  28#include "core/stream/bitStream.h"
  29#include "console/consoleTypes.h"
  30#include "scene/sceneRenderState.h"
  31#include "terrain/terrData.h"
  32#include "renderInstance/renderPassManager.h"
  33#include "gfx/gfxDrawUtil.h"
  34#include "gfx/primBuilder.h"
  35#include "T3D/gameBase/gameConnection.h"
  36#include "gfx/gfxVertexBuffer.h"
  37#include "gfx/gfxStructs.h"
  38#include "ts/tsShapeInstance.h"
  39#include "lighting/lightManager.h"
  40#include "lighting/lightInfo.h"
  41#include "materials/shaderData.h"
  42#include "gfx/gfxTransformSaver.h"
  43#include "shaderGen/shaderGenVars.h"
  44#include "materials/matTextureTarget.h"
  45#include "gfx/util/screenspace.h"
  46#include "materials/materialDefinition.h"
  47#include "materials/materialManager.h"
  48#include "materials/sceneData.h"
  49#include "materials/materialFeatureTypes.h"
  50#include "materials/matInstance.h"
  51#include "renderInstance/renderPrePassMgr.h"
  52#include "console/engineAPI.h"
  53
  54/// This is used for rendering ground cover billboards.
  55GFXImplementVertexFormat( GCVertex )
  56{
  57   addElement( "POSITION", GFXDeclType_Float3 );
  58   addElement( "NORMAL", GFXDeclType_Float3 );
  59   addElement( "COLOR", GFXDeclType_Color );
  60   addElement( "TEXCOORD", GFXDeclType_Float4, 0 );
  61};
  62
  63GroundCoverShaderConstHandles::GroundCoverShaderConstHandles()
  64 : mGroundCover( NULL ),
  65   mTypeRectsSC( NULL ),
  66   mFadeSC( NULL ),
  67   mWindDirSC( NULL ),
  68   mGustInfoSC( NULL ),
  69   mTurbInfoSC( NULL ),
  70   mCamRightSC( NULL ),
  71   mCamUpSC( NULL )
  72{
  73}
  74
  75void GroundCoverShaderConstHandles::init( GFXShader *shader )
  76{   
  77   mTypeRectsSC = shader->getShaderConstHandle( "$gc_typeRects" );      
  78   mFadeSC = shader->getShaderConstHandle( "$gc_fadeParams" );
  79   mWindDirSC = shader->getShaderConstHandle( "$gc_windDir" );
  80   mGustInfoSC = shader->getShaderConstHandle( "$gc_gustInfo" );
  81   mTurbInfoSC = shader->getShaderConstHandle( "$gc_turbInfo" );
  82   mCamRightSC = shader->getShaderConstHandle( "$gc_camRight" );
  83   mCamUpSC = shader->getShaderConstHandle( "$gc_camUp" );
  84}
  85
  86void GroundCoverShaderConstHandles::setConsts( SceneRenderState *state, const SceneData &sgData, GFXShaderConstBuffer *buffer )
  87{         
  88   AlignedArray<Point4F> rectData( MAX_COVERTYPES, sizeof( Point4F ), (U8*)(mGroundCover->mBillboardRects), false );          
  89   buffer->setSafe( mTypeRectsSC, rectData );
  90   
  91   const GroundCoverShaderConstData &data = mGroundCover->getShaderConstData();
  92      
  93   buffer->setSafe( mFadeSC, data.fadeInfo );   
  94   buffer->setSafe( mWindDirSC, mGroundCover->mWindDirection );
  95   buffer->setSafe( mGustInfoSC, data.gustInfo );
  96   buffer->setSafe( mTurbInfoSC, data.turbInfo );    
  97   buffer->setSafe( mCamRightSC, data.camRight );
  98   buffer->setSafe( mCamUpSC, data.camUp );
  99}
 100
 101/// This defines one grid cell.
 102class GroundCoverCell
 103{
 104protected:
 105
 106   friend class GroundCover;
 107
 108   struct Placement
 109   {
 110      Point3F     point;
 111      Point3F     normal;
 112      Point3F     size;
 113      F32         rotation;
 114      U32         type;
 115      F32         windAmplitude;
 116      Box3F       worldBox;
 117      ColorF      lmColor;
 118   };
 119
 120   /// This is the x,y index for this cell.
 121   Point2I mIndex;
 122
 123   /// The worldspace bounding box this cell.
 124   Box3F mBounds;
 125
 126   /// The worldspace bounding box of the renderable
 127   /// content within this cell.
 128   Box3F mRenderBounds;
 129
 130   /// The instances of billboard cover elements in this cell.
 131   Vector<Placement> mBillboards;
 132
 133   /// The instances of shape cover elements in this cell.
 134   Vector<Placement> mShapes;
 135
 136   typedef GFXVertexBufferHandle<GCVertex> VBHandle;
 137   typedef Vector< VBHandle> VBHandleVector;
 138
 139   /// The vertex buffers that hold all the 
 140   /// prepared billboards for this cell.
 141   VBHandleVector mVBs;
 142
 143   /// Used to mark the cell dirty and in need
 144   /// of a rebuild.
 145   bool mDirty;
 146
 147   /// Repacks the billboards into the vertex buffer.
 148   void _rebuildVB();
 149
 150public:
 151
 152   GroundCoverCell() : mDirty(false) {}
 153
 154   ~GroundCoverCell() 
 155   {
 156      mVBs.clear();
 157   }
 158
 159   const Point2I& shiftIndex( const Point2I& shift ) { return mIndex += shift; }
 160   
 161   /// The worldspace bounding box this cell.
 162   const Box3F& getBounds() const { return mBounds; }
 163
 164   /// The worldspace bounding box of the renderable
 165   /// content within this cell.
 166   const Box3F& getRenderBounds() const { return mRenderBounds; }
 167
 168   Point3F getCenter() const { return mBounds.getCenter(); }
 169
 170   VectorF getSize() const { return VectorF( mBounds.len_x() / 2.0f,
 171                                             mBounds.len_y() / 2.0f,
 172                                             mBounds.len_z() / 2.0f ); }
 173      
 174   void renderBillboards( SceneRenderState *state, BaseMatInstance *mat, GFXPrimitiveBufferHandle *pb );
 175
 176   U32 renderShapes(    const TSRenderState &rdata, 
 177                        Frustum *culler, 
 178                        TSShapeInstance** shapes );
 179};
 180
 181void GroundCoverCell::_rebuildVB()
 182{
 183   if ( mBillboards.empty() )
 184      return;
 185
 186   PROFILE_SCOPE(GroundCover_RebuildVB);
 187
 188   // The maximum verts we can put in one vertex buffer batch.
 189   const U32 MAX_BILLBOARDS = 0xFFFF / 4;
 190
 191   // How many batches will we need in total?
 192   const U32 batches = mCeil( (F32)mBillboards.size() / (F32)MAX_BILLBOARDS );
 193
 194   // So... how many billboards do we need in
 195   // each batch? We're trying to evenly divide
 196   // the amount across all the VBs.
 197   const U32 batchBB = mBillboards.size() / batches;
 198
 199   // Init the vertex buffer list to the right size.  Any
 200   // VBs already in there will remain unless we're truncating
 201   // the list... those are freed.
 202   mVBs.setSize( batches ); 
 203
 204   // Get the iter to the first billboard.
 205   Vector<Placement>::const_iterator iter = mBillboards.begin();
 206
 207   // Prepare each batch.
 208   U32 bb, remaining = mBillboards.size();
 209   for ( U32 b = 0; b < batches; b++ )
 210   {
 211      // Grab a reference to the vb.
 212      VBHandle &vb = mVBs[b];
 213
 214      // How many billboards in this batch?
 215      bb = getMin( batchBB, remaining );
 216      remaining -= bb;
 217
 218      // Ok... now how many verts is that?
 219      const U32 verts = bb * 4;
 220
 221      // Create the VB hasn't been created or if its
 222      // too small then resize it.
 223      if ( vb.isNull() || vb->mNumVerts < verts )
 224      {
 225         PROFILE_START(GroundCover_CreateVB);
 226         vb.set( GFX, verts, GFXBufferTypeStatic );
 227         PROFILE_END();
 228      }
 229
 230      // Fill this puppy!
 231      GCVertex* vertPtr = vb.lock( 0, verts );
 232      
 233      GFXVertexColor color;
 234
 235      Vector<Placement>::const_iterator last = iter + bb;
 236      for ( ; iter != last; iter++ )
 237      {
 238         const Point3F &position = (*iter).point;
 239         const Point3F &normal = (*iter).normal;
 240         const S32 &type = (*iter).type;
 241         const Point3F &size = (*iter).size;
 242         const F32 &windAmplitude = (*iter).windAmplitude;
 243         GFXVertexColor color = (ColorI)(*iter).lmColor;
 244         U8 *col = (U8 *)const_cast<U32 *>( (const U32 *)color );
 245
 246         vertPtr->point = position;
 247         vertPtr->normal = normal;
 248         vertPtr->params.x = size.x;
 249         vertPtr->params.y = size.y;
 250         vertPtr->params.z = type;
 251         vertPtr->params.w = 0;
 252         col[3] = 0;
 253         vertPtr->ambient = color;
 254         ++vertPtr;
 255
 256         vertPtr->point = position;
 257         vertPtr->normal = normal;
 258         vertPtr->params.x = size.x;
 259         vertPtr->params.y = size.y;
 260         vertPtr->params.z = type;
 261         vertPtr->params.w = 0;
 262         col[3] = 1;
 263         vertPtr->ambient = color;
 264         ++vertPtr;
 265
 266         vertPtr->point = position;
 267         vertPtr->normal = normal;
 268         vertPtr->params.x = size.x;
 269         vertPtr->params.y = size.y;
 270         vertPtr->params.z = type;
 271         vertPtr->params.w = windAmplitude;
 272         col[3] = 2;
 273         vertPtr->ambient = color;
 274         ++vertPtr;
 275
 276         vertPtr->point = position;
 277         vertPtr->normal = normal;
 278         vertPtr->params.x = size.x;
 279         vertPtr->params.y = size.y;
 280         vertPtr->params.z = type;
 281         vertPtr->params.w = windAmplitude;
 282         col[3] = 3;
 283         vertPtr->ambient = color;
 284         ++vertPtr;
 285      }
 286
 287      vb.unlock();
 288   }
 289}
 290
 291U32 GroundCoverCell::renderShapes(  const TSRenderState &rdata,
 292                                    Frustum *culler, 
 293                                    TSShapeInstance** shapes )
 294{
 295   MatrixF worldMat;
 296   TSShapeInstance* shape;
 297   Point3F camVector;
 298   F32 dist;
 299   F32 invScale;
 300
 301   const SceneRenderState *state = rdata.getSceneState();
 302
 303   U32 totalRendered = 0;
 304
 305   Vector<Placement>::const_iterator iter = mShapes.begin();
 306   for ( ; iter != mShapes.end(); iter++ )
 307   {
 308      // Grab a reference here once.
 309      const Placement& inst = (*iter);
 310
 311      // If we were pass a culler then us it to test the shape world box.
 312      if ( culler && culler->isCulled( inst.worldBox ) )
 313         continue;
 314
 315      shape = shapes[ inst.type ];
 316
 317      camVector = inst.point - state->getDiffuseCameraPosition();
 318      dist = getMax( camVector.len(), 0.01f );
 319
 320      worldMat.set( EulerF(0, 0, inst.rotation), inst.point );
 321
 322      // TSShapeInstance::render() uses the 
 323      // world matrix for the RenderInst.
 324      worldMat.scale( inst.size );
 325      GFX->setWorldMatrix( worldMat );
 326
 327      // Obey the normal screen space lod metrics.  The shapes should
 328      // be tuned to lod out quickly for ground cover.
 329      //
 330      // Note: The profile doesn't indicate that lod selection is
 331      // very expensive... in fact its less than 1/10th of the cost 
 332      // of the render() call below.
 333      PROFILE_START(GroundCover_RenderShapes_SelectDetail);
 334
 335         invScale = (1.0f/getMax(getMax(inst.size.x,inst.size.y),inst.size.z));
 336         shape->setDetailFromDistance( state, dist * invScale );
 337
 338      PROFILE_END(); // GroundCover_RenderShapes_SelectDetail
 339  
 340      // Note: This is the most expensive call of this loop.  We 
 341      // need to rework the render call completely to optimize it.
 342      PROFILE_START(GroundCover_RenderShapes_Render);
 343
 344         shape->render( rdata );
 345
 346      PROFILE_END(); // GroundCover_RenderShapes_Render
 347
 348      totalRendered++;
 349   }
 350
 351   return totalRendered;
 352}
 353
 354void GroundCoverCell::renderBillboards( SceneRenderState *state, BaseMatInstance *mat, GFXPrimitiveBufferHandle *pb  )
 355{
 356   if ( mDirty )
 357   {
 358      _rebuildVB();
 359      mDirty = false;
 360   }
 361
 362   // Do we have anything to render?
 363   if ( mBillboards.size() == 0 || mVBs.empty() || !mat )
 364      return;
 365
 366   // TODO: Maybe add support for non-facing billboards
 367   // with random rotations and optional crosses.  We could
 368   // stick them into the buffer after the normal billboards,
 369   // then change shader consts.
 370
 371   RenderPassManager *pass = state->getRenderPass();
 372      
 373   // Draw each batch.
 374   U32 remaining = mBillboards.size();
 375   const U32 batches = mVBs.size();
 376   const U32 batchBB = remaining / batches;
 377
 378   for ( U32 b = 0; b < batches; b++ )
 379   {
 380      // Grab a reference to the vb.
 381      VBHandle &vb = mVBs[b];
 382
 383      // How many billboards in this batch?
 384      U32 bb = getMin( batchBB, remaining );
 385      remaining -= bb;
 386
 387      MeshRenderInst *ri = pass->allocInst<MeshRenderInst>();
 388      ri->type = RenderPassManager::RIT_Mesh;
 389      ri->matInst = mat;
 390      ri->vertBuff = &vb;
 391      ri->primBuff = pb;
 392      ri->objectToWorld = &MatrixF::Identity;
 393      ri->worldToCamera = pass->allocSharedXform(RenderPassManager::View);
 394      ri->projection = pass->allocSharedXform(RenderPassManager::Projection);
 395      ri->defaultKey = mat->getStateHint();
 396      ri->prim = pass->allocPrim();
 397      ri->prim->numPrimitives = bb * 2;
 398      ri->prim->numVertices = bb * 4;
 399      ri->prim->startIndex = 0;
 400      ri->prim->startVertex = 0;
 401      ri->prim->minIndex = 0;
 402      ri->prim->type = GFXTriangleList;
 403
 404      // If we need lights then set them up.
 405      if ( mat->isForwardLit() )
 406      {
 407         LightQuery query;
 408         query.init( mBounds );
 409         query.getLights( ri->lights, 8 );
 410      }
 411
 412      pass->addInst( ri );
 413
 414      GroundCover::smStatRenderedBatches++;
 415      GroundCover::smStatRenderedBillboards += bb;
 416   }
 417
 418   GroundCover::smStatRenderedCells++;
 419}
 420
 421
 422U32 GroundCover::smStatRenderedCells = 0;
 423U32 GroundCover::smStatRenderedBillboards = 0;
 424U32 GroundCover::smStatRenderedBatches = 0;
 425U32 GroundCover::smStatRenderedShapes = 0;
 426F32 GroundCover::smDensityScale = 1.0f;
 427
 428ConsoleDocClass( GroundCover,
 429   "@brief Covers the ground in a field of objects (IE: Grass, Flowers, etc)."
 430   "@ingroup Foliage\n"
 431);
 432
 433GroundCover::GroundCover()
 434{
 435   mTypeMask |= StaticObjectType | StaticShapeObjectType;
 436   mNetFlags.set( Ghostable | ScopeAlways );
 437
 438   mRadius = 200.0f;
 439   mZOffset = 0.0f;
 440   mFadeRadius = 50.0f;
 441   mShapeCullRadius = 75.0f;
 442   mShapesCastShadows = true;
 443   mReflectRadiusScale = 0.25f;
 444
 445   mGridSize = 7;
 446
 447   // By initializing this to a big value we
 448   // ensure we warp on first render.
 449   mGridIndex.set( S32_MAX, S32_MAX );
 450
 451   mMaxPlacement = 1000;
 452   mLastPlacementCount = 0;
 453
 454   mDebugRenderCells = false;
 455   mDebugNoBillboards = false;
 456   mDebugNoShapes = false;
 457   mDebugLockFrustum = false;
 458
 459   mRandomSeed = 1;
 460
 461   mMaterial = NULL;
 462   mMatInst = NULL;
 463   mMatParams = NULL;
 464   mTypeRectsParam = NULL;
 465   mFadeParams = NULL;
 466   mWindDirParam = NULL;
 467   mGustInfoParam = NULL;
 468   mTurbInfoParam = NULL;
 469   mCamRightParam = NULL;
 470   mCamUpParam = NULL;
 471
 472   mMaxBillboardTiltAngle = 90.0f;
 473
 474   // TODO: This really doesn't belong here... we need a
 475   // real wind system for Torque scenes.  This data
 476   // would be part of a global scene wind or area wind
 477   // emitter.
 478   //
 479   // Tom Spilman - 10/16/2007
 480
 481   mWindGustLength = 20.0f;
 482   mWindGustFrequency = 0.5f;
 483   mWindGustStrength = 0.5f;
 484   mWindDirection.set( 1.0f, 0.0f );
 485   mWindTurbulenceFrequency = 1.2f;
 486   mWindTurbulenceStrength = 0.125f;
 487
 488   for ( S32 i=0; i < MAX_COVERTYPES; i++ )
 489   {
 490      mProbability[i] = 0.0f;
 491
 492      mSizeMin[i] = 1.0f;
 493      mSizeMax[i] = 1.0f;
 494      mSizeExponent[i] = 1.0f;
 495
 496      mWindScale[i] = 1.0f;
 497
 498      mMaxSlope[i] = 0.0f;
 499
 500      mMinElevation[i] = -99999.0f;
 501      mMaxElevation[i] = 99999.0f;
 502
 503      mLayer[i] = StringTable->EmptyString();
 504      mInvertLayer[i] = false;
 505
 506      mMinClumpCount[i] = 1;
 507      mMaxClumpCount[i] = 1;
 508      mClumpCountExponent[i] = 1.0f;
 509      mClumpRadius[i] = 1.0f;
 510
 511      mBillboardRects[i].point.set( 0.0f, 0.0f );
 512      mBillboardRects[i].extent.set( 1.0f, 1.0f );
 513
 514      mShapeFilenames[i] = NULL;
 515      mShapeInstances[i] = NULL;
 516
 517      mBillboardAspectScales[i] = 1.0f;
 518
 519      mNormalizedProbability[i] = 0.0f;
 520   }
 521}
 522
 523GroundCover::~GroundCover()
 524{
 525   SAFE_DELETE( mMatInst );
 526}
 527
 528IMPLEMENT_CO_NETOBJECT_V1(GroundCover);
 529
 530void GroundCover::initPersistFields()
 531{
 532   addGroup( "GroundCover General" );
 533      
 534      addField( "material",      TypeMaterialName, Offset( mMaterialName, GroundCover ),        "Material used by all GroundCover segments." );
 535
 536      addField( "radius",        TypeF32,          Offset( mRadius, GroundCover ),              "Outer generation radius from the current camera position." );
 537      addField( "dissolveRadius",TypeF32,          Offset( mFadeRadius, GroundCover ),          "This is less than or equal to radius and defines when fading of cover elements begins." );
 538      addField( "reflectScale",  TypeF32,          Offset( mReflectRadiusScale, GroundCover ),  "Scales the various culling radii when rendering a reflection. Typically for water." );
 539
 540      addField( "gridSize",      TypeS32,          Offset( mGridSize, GroundCover ),            "The number of cells per axis in the grid." );
 541      addField( "zOffset",       TypeF32,          Offset( mZOffset, GroundCover ),             "Offset along the Z axis to render the ground cover." );
 542
 543      addField( "seed",          TypeS32,          Offset( mRandomSeed, GroundCover ),          "This RNG seed is saved and sent to clients for generating the same cover." );
 544      addField( "maxElements",   TypeS32,          Offset( mMaxPlacement, GroundCover ),        "The maximum amount of cover elements to include in the grid at any one time." );
 545
 546      addField( "maxBillboardTiltAngle", TypeF32,  Offset( mMaxBillboardTiltAngle, GroundCover ),"The maximum amout of degrees the billboard will tilt down to match the camera." );
 547      addField( "shapeCullRadius", TypeF32,        Offset( mShapeCullRadius, GroundCover ),     "This is the distance at which DTS elements are  completely culled out." );      
 548      addField( "shapesCastShadows", TypeBool,     Offset( mShapesCastShadows, GroundCover ),   "Whether DTS elements should cast shadows or not." );
 549
 550      addArray( "Types", MAX_COVERTYPES );
 551
 552         addField( "billboardUVs",  TypeRectUV,    Offset( mBillboardRects, GroundCover ), MAX_COVERTYPES,  "Subset material UV coordinates for this cover billboard." );
 553
 554         addField( "shapeFilename", TypeFilename,  Offset( mShapeFilenames, GroundCover ), MAX_COVERTYPES,  "The cover shape filename. [Optional]" );
 555
 556         addField( "layer",         TypeTerrainMaterialName, Offset( mLayer, GroundCover ), MAX_COVERTYPES, "Terrain material name to limit coverage to, or blank to not limit." );
 557
 558         addField( "invertLayer",   TypeBool,      Offset( mInvertLayer, GroundCover ), MAX_COVERTYPES,     "Indicates that the terrain material index given in 'layer' is an exclusion mask." );
 559
 560         addField( "probability",   TypeF32,       Offset( mProbability, GroundCover ), MAX_COVERTYPES,     "The probability of one cover type verses another (relative to all cover types)." );
 561
 562         addField( "sizeMin",       TypeF32,       Offset( mSizeMin, GroundCover ), MAX_COVERTYPES,         "The minimum random size for each cover type." );
 563
 564         addField( "sizeMax",       TypeF32,       Offset( mSizeMax, GroundCover ), MAX_COVERTYPES,         "The maximum random size of this cover type." );
 565
 566         addField( "sizeExponent",  TypeF32,       Offset( mSizeExponent, GroundCover ), MAX_COVERTYPES,    "An exponent used to bias between the minimum and maximum random sizes." );
 567
 568         addField( "windScale",     TypeF32,       Offset( mWindScale, GroundCover ), MAX_COVERTYPES,       "The wind effect scale." );
 569
 570         addField( "maxSlope",      TypeF32,       Offset( mMaxSlope, GroundCover ), MAX_COVERTYPES,        "The maximum slope angle in degrees for placement." );
 571
 572         addField( "minElevation",  TypeF32,       Offset( mMinElevation, GroundCover ), MAX_COVERTYPES,    "The minimum world space elevation for placement." );
 573
 574         addField( "maxElevation",  TypeF32,       Offset( mMaxElevation, GroundCover ), MAX_COVERTYPES,    "The maximum world space elevation for placement." );
 575
 576         addField( "minClumpCount", TypeS32,       Offset( mMinClumpCount, GroundCover ), MAX_COVERTYPES,   "The minimum amount of elements in a clump." );
 577      
 578         addField( "maxClumpCount", TypeS32,       Offset( mMaxClumpCount, GroundCover ), MAX_COVERTYPES,   "The maximum amount of elements in a clump." );
 579
 580         addField( "clumpExponent", TypeF32,       Offset( mClumpCountExponent, GroundCover ), MAX_COVERTYPES, "An exponent used to bias between the minimum and maximum clump counts for a particular clump." );
 581
 582         addField( "clumpRadius",   TypeF32,       Offset( mClumpRadius, GroundCover ), MAX_COVERTYPES,     "The maximum clump radius." );
 583
 584      endArray( "Types" );
 585
 586   endGroup( "GroundCover General" );
 587
 588   addGroup( "GroundCover Wind" );
 589
 590      addField( "windDirection",    TypePoint2F,   Offset( mWindDirection, GroundCover ),             "The direction of the wind." );
 591
 592      addField( "windGustLength",   TypeF32,       Offset( mWindGustLength, GroundCover ),            "The length in meters between peaks in the wind gust." );
 593      addField( "windGustFrequency",TypeF32,       Offset( mWindGustFrequency, GroundCover ),         "Controls how often the wind gust peaks per second." );
 594      addField( "windGustStrength", TypeF32,       Offset( mWindGustStrength, GroundCover ),          "The maximum distance in meters that the peak wind  gust will displace an element." );
 595
 596      addField( "windTurbulenceFrequency",   TypeF32, Offset( mWindTurbulenceFrequency, GroundCover ),"Controls the overall rapidity of the wind turbulence." );
 597      addField( "windTurbulenceStrength",    TypeF32, Offset( mWindTurbulenceStrength, GroundCover ), "The maximum distance in meters that the turbulence can displace a ground cover element." );
 598
 599   endGroup( "GroundCover Wind" );
 600
 601   addGroup( "GroundCover Debug" );
 602
 603      addField( "lockFrustum",      TypeBool,      Offset( mDebugLockFrustum, GroundCover ),          "Debug parameter for locking the culling frustum which will freeze the cover generation." );
 604      addField( "renderCells",      TypeBool,      Offset( mDebugRenderCells, GroundCover ),          "Debug parameter for displaying the grid cells." );
 605      addField( "noBillboards",     TypeBool,      Offset( mDebugNoBillboards, GroundCover ),         "Debug parameter for turning off billboard rendering." );
 606      addField( "noShapes",         TypeBool,      Offset( mDebugNoShapes, GroundCover ),             "Debug parameter for turning off shape rendering." );
 607
 608   endGroup( "GroundCover Debug" );  
 609
 610   Parent::initPersistFields();
 611}
 612
 613void GroundCover::consoleInit()
 614{     
 615   Con::addVariable( "$pref::GroundCover::densityScale", TypeF32, &smDensityScale, "A global LOD scalar which can reduce the overall density of placed GroundCover.\n" 
 616      "@ingroup Foliage\n");
 617
 618   Con::addVariable( "$GroundCover::renderedCells", TypeS32, &smStatRenderedCells, "Stat for number of rendered cells.\n"
 619      "@ingroup Foliage\n");
 620   Con::addVariable( "$GroundCover::renderedBillboards", TypeS32, &smStatRenderedBillboards, "Stat for number of rendered billboards.\n"
 621      "@ingroup Foliage\n");
 622   Con::addVariable( "$GroundCover::renderedBatches", TypeS32, &smStatRenderedBatches, "Stat for number of rendered billboard batches.\n"
 623      "@ingroup Foliage\n");
 624   Con::addVariable( "$GroundCover::renderedShapes", TypeS32, &smStatRenderedShapes, "Stat for number of rendered shapes.\n"
 625      "@ingroup Foliage\n");
 626
 627   Parent::consoleInit();
 628}
 629
 630bool GroundCover::onAdd()
 631{
 632   if (!Parent::onAdd())
 633      return false;
 634
 635   // We don't use any bounds.
 636   setGlobalBounds();
 637
 638   resetWorldBox();
 639
 640   // Prepare some client side things.
 641   if ( isClientObject() )
 642   {            
 643      _initMaterial();
 644
 645      _initShapes();
 646
 647      // Hook ourselves up to get terrain change notifications.
 648      TerrainBlock::smUpdateSignal.notify( this, &GroundCover::onTerrainUpdated );
 649   }
 650
 651   addToScene();
 652
 653   return true;
 654}
 655
 656void GroundCover::onRemove()
 657{
 658   Parent::onRemove();
 659
 660   _deleteCells();
 661   _deleteShapes();
 662   
 663   if ( isClientObject() )
 664   {
 665      TerrainBlock::smUpdateSignal.remove( this, &GroundCover::onTerrainUpdated );      
 666   }
 667
 668   removeFromScene();
 669}
 670
 671void GroundCover::inspectPostApply()
 672{
 673   Parent::inspectPostApply();
 674
 675   // We flag all the parameters as changed because
 676   // we're feeling lazy and there is not a good way
 677   // to track what parameters changed.
 678   //
 679   // TODO: Add a mask bit option to addField() and/or
 680   // addGroup() which is passed to inspectPostApply
 681   // for detection of changed elements.
 682   //
 683   setMaskBits(U32(-1) );
 684}
 685
 686U32 GroundCover::packUpdate( NetConnection *connection, U32 mask, BitStream *stream )
 687{
 688   U32 retMask = Parent::packUpdate( connection, mask, stream );
 689
 690   if (stream->writeFlag(mask & InitialUpdateMask))
 691   {
 692      // TODO: We could probably optimize a few of these
 693      // based on reasonable units at some point.
 694
 695      stream->write( mMaterialName );
 696
 697      stream->write( mRadius );
 698      stream->write( mZOffset );
 699      stream->write( mFadeRadius );
 700      stream->write( mShapeCullRadius );
 701      stream->writeFlag( mShapesCastShadows );
 702      stream->write( mReflectRadiusScale );
 703      stream->write( mGridSize );
 704      stream->write( mRandomSeed );
 705      stream->write( mMaxPlacement );
 706      stream->write( mMaxBillboardTiltAngle );
 707
 708      stream->write( mWindDirection.x );
 709      stream->write( mWindDirection.y );
 710      stream->write( mWindGustLength );
 711      stream->write( mWindGustFrequency );
 712      stream->write( mWindGustStrength );
 713      stream->write( mWindTurbulenceFrequency );
 714      stream->write( mWindTurbulenceStrength );
 715
 716      for ( S32 i=0; i < MAX_COVERTYPES; i++ )
 717      {
 718         stream->write( mProbability[i] );
 719         stream->write( mSizeMin[i] );
 720         stream->write( mSizeMax[i] );
 721         stream->write( mSizeExponent[i] );
 722         stream->write( mWindScale[i] );
 723         
 724         stream->write( mMaxSlope[i] );
 725         
 726         stream->write( mMinElevation[i] );
 727         stream->write( mMaxElevation[i] );     
 728
 729         stream->writeString( mLayer[i] );
 730         stream->writeFlag( mInvertLayer[i] );      
 731
 732         stream->write( mMinClumpCount[i] );
 733         stream->write( mMaxClumpCount[i] );
 734         stream->write( mClumpCountExponent[i] );
 735         stream->write( mClumpRadius[i] );
 736
 737         stream->write( mBillboardRects[i].point.x );
 738         stream->write( mBillboardRects[i].point.y );
 739         stream->write( mBillboardRects[i].extent.x );
 740         stream->write( mBillboardRects[i].extent.y );
 741
 742         stream->writeString( mShapeFilenames[i] );
 743      }
 744
 745      stream->writeFlag( mDebugRenderCells );
 746      stream->writeFlag( mDebugNoBillboards );
 747      stream->writeFlag( mDebugNoShapes );
 748      stream->writeFlag( mDebugLockFrustum );
 749   }
 750
 751   return retMask;  
 752}
 753
 754void GroundCover::unpackUpdate( NetConnection *connection, BitStream *stream )
 755{
 756   Parent::unpackUpdate( connection, stream );
 757
 758   if (stream->readFlag())
 759   {
 760      stream->read( &mMaterialName );
 761
 762      stream->read( &mRadius );
 763      stream->read( &mZOffset );
 764      stream->read( &mFadeRadius );
 765      stream->read( &mShapeCullRadius );
 766      mShapesCastShadows = stream->readFlag();
 767      stream->read( &mReflectRadiusScale );   
 768      stream->read( &mGridSize );
 769      stream->read( &mRandomSeed );
 770      stream->read( &mMaxPlacement );
 771      stream->read( &mMaxBillboardTiltAngle );
 772
 773      stream->read( &mWindDirection.x );
 774      stream->read( &mWindDirection.y );
 775      stream->read( &mWindGustLength );
 776      stream->read( &mWindGustFrequency );
 777      stream->read( &mWindGustStrength );
 778      stream->read( &mWindTurbulenceFrequency );
 779      stream->read( &mWindTurbulenceStrength );
 780
 781      for ( S32 i=0; i < MAX_COVERTYPES; i++ )
 782      {
 783         stream->read( &mProbability[i] );
 784         stream->read( &mSizeMin[i] );
 785         stream->read( &mSizeMax[i] );
 786         stream->read( &mSizeExponent[i] );
 787         stream->read( &mWindScale[i] );
 788
 789         stream->read( &mMaxSlope[i] );
 790
 791         stream->read( &mMinElevation[i] );
 792         stream->read( &mMaxElevation[i] );     
 793
 794         mLayer[i] = stream->readSTString();
 795         mInvertLayer[i] = stream->readFlag();
 796
 797         stream->read( &mMinClumpCount[i] );
 798         stream->read( &mMaxClumpCount[i] );
 799         stream->read( &mClumpCountExponent[i] );
 800         stream->read( &mClumpRadius[i] );
 801
 802         stream->read( &mBillboardRects[i].point.x );
 803         stream->read( &mBillboardRects[i].point.y );
 804         stream->read( &mBillboardRects[i].extent.x );
 805         stream->read( &mBillboardRects[i].extent.y );
 806
 807         mShapeFilenames[i] = stream->readSTString();
 808      }
 809
 810      mDebugRenderCells    = stream->readFlag();
 811      mDebugNoBillboards   = stream->readFlag();
 812      mDebugNoShapes       = stream->readFlag();
 813      mDebugLockFrustum    = stream->readFlag();
 814
 815      // We have no way to easily know what changed, so by clearing
 816      // the cells we force a reinit and regeneration of the cells.
 817      // It's sloppy, but it works for now.
 818      _freeCells();
 819
 820      if ( isProperlyAdded() )
 821         _initMaterial();
 822   }
 823}
 824
 825void GroundCover::_initMaterial()
 826{   
 827   SAFE_DELETE( mMatInst );
 828   
 829   if ( mMaterialName.isNotEmpty() )
 830      if ( !Sim::findObject( mMaterialName, mMaterial ) )
 831         Con::errorf( "GroundCover::_initMaterial - Material %s was not found.", mMaterialName.c_str() );
 832
 833   if ( mMaterial )
 834      mMatInst = mMaterial->createMatInstance();
 835   else
 836      mMatInst = MATMGR->createMatInstance( "WarningMaterial" );
 837   
 838   // Add our special feature that makes it all work...
 839   FeatureSet features = MATMGR->getDefaultFeatures();
 840   features.addFeature( MFT_Foliage );
 841   
 842   // Our feature requires a pointer back to this object
 843   // to properly setup its shader consts.
 844   mMatInst->setUserObject( this );
 845
 846   // DO IT!
 847   mMatInst->init( features, getGFXVertexFormat<GCVertex>() );
 848}
 849
 850void GroundCover::_initShapes()
 851{
 852   _deleteShapes();
 853
 854   for ( S32 i=0; i < MAX_COVERTYPES; i++ )
 855   {
 856      if ( !mShapeFilenames[i] || !mShapeFilenames[i][0] )
 857         continue;
 858
 859      // Load the shape.
 860      Resource<TSShape> shape = ResourceManager::get().load(mShapeFilenames[i]);
 861      if ( !(bool)shape )
 862      {
 863         Con::warnf( "GroundCover::_initShapes() unable to load shape: %s", mShapeFilenames[i] );
 864         continue;
 865      }
 866
 867      if ( isClientObject() && !shape->preloadMaterialList(shape.getPath()) && NetConnection::filesWereDownloaded() )
 868      {
 869         Con::warnf( "GroundCover::_initShapes() material preload failed for shape: %s", mShapeFilenames[i] );
 870         continue;
 871      }
 872
 873      // Create the shape instance.
 874      mShapeInstances[i] = new TSShapeInstance( shape, isClientObject() );
 875   }
 876}
 877
 878void GroundCover::_deleteShapes()
 879{
 880   for ( S32 i=0; i < MAX_COVERTYPES; i++ )
 881   {
 882      delete mShapeInstances[i];
 883      mShapeInstances[i] = NULL;
 884   }
 885}
 886
 887void GroundCover::_deleteCells()
 888{
 889   // Delete the allocation list.
 890   for ( S32 i=0; i < mAllocCellList.size(); i++ )
 891      delete mAllocCellList[i];
 892   mAllocCellList.clear();
 893
 894   // Zero out the rest of the stuff.
 895   _freeCells();
 896}
 897
 898void GroundCover::_freeCells()
 899{
 900   // Zero the grid and scratch space.
 901   mCellGrid.clear();
 902   mScratchGrid.clear();
 903
 904   // Compact things... remove excess allocated cells.
 905   const U32 maxCells = mGridSize * mGridSize;
 906   if ( mAllocCellList.size() > maxCells )
 907   {
 908      for ( S32 i=maxCells; i < mAllocCellList.size(); i++ )
 909        delete mAllocCellList[i];
 910      mAllocCellList.setSize( maxCells );
 911   }
 912
 913   // Move all the alloced cells into the free list.
 914   mFreeCellList.clear();
 915   mFreeCellList.merge( mAllocCellList );
 916
 917   // Release the primitive buffer.
 918   mPrimBuffer = NULL;
 919}
 920
 921void GroundCover::_recycleCell( GroundCoverCell* cell )
 922{
 923   mFreeCellList.push_back( cell );
 924}
 925
 926void GroundCover::_initialize( U32 cellCount, U32 cellPlacementCount )
 927{
 928   // Cleanup everything... we're starting over.
 929   _freeCells();
 930   _deleteShapes();
 931
 932   // Nothing to do without a count!
 933   if ( cellPlacementCount == 0 )
 934      return;
 935
 936   // Reset the grid sizes.
 937   mCellGrid.setSize( cellCount );
 938   dMemset( mCellGrid.address(), 0, mCellGrid.memSize() );
 939   mScratchGrid.setSize( cellCount );
 940
 941   // Rebuild the texture aspect scales for each type.
 942   F32 textureAspect = 1.0f;
 943   if( mMatInst && mMatInst->isValid())
 944   {
 945      Material* mat = dynamic_cast<Material*>(mMatInst->getMaterial());
 946      if(mat)
 947      {
 948         GFXTexHandle tex(mat->mDiffuseMapFilename[0], &GFXDefaultStaticDiffuseProfile, "GroundCover texture aspect ratio check" );
 949         if(tex.isValid())
 950         {
 951            U32 w = tex.getWidth();
 952            U32 h = tex.getHeight();
 953            if(h > 0)
 954               textureAspect = F32(w) / F32(h);
 955         }
 956      }
 957   }
 958   for ( S32 i=0; i < MAX_COVERTYPES; i++ )
 959   {  
 960      if ( mBillboardRects[i].extent.y > 0.0f )
 961      {
 962         mBillboardAspectScales[i] = textureAspect * mBillboardRects[i].extent.x / mBillboardRects[i].extent.y;
 963      }
 964      else
 965         mBillboardAspectScales[i] = 0.0f;
 966   }
 967
 968   // Load the shapes again.
 969   _initShapes();
 970
 971   // Set the primitive buffer up for the maximum placement in a cell.
 972   mPrimBuffer.set( GFX, cellPlacementCount * 6, 0, GFXBufferTypeStatic );
 973   U16 *idxBuff;
 974   mPrimBuffer.lock(&idxBuff);
 975   for ( U32 i=0; i < cellPlacementCount; i++ )
 976   {
 977      //
 978      // The vertex pattern in the VB for each 
 979      // billboard is as follows...
 980      //
 981      //     0----1
 982      //     |\   |
 983      //     | \  |
 984      //     |  \ |
 985      //     |   \|
 986      //     3----2
 987      //
 988      // We setup the index order below to ensure
 989      // sequential, cache friendly, access.
 990      //
 991      U32 offset = i * 4;
 992      idxBuff[i*6+0] = 0 + offset;
 993      idxBuff[i*6+1] = 1 + offset;
 994      idxBuff[i*6+2] = 2 + offset;
 995      idxBuff[i*6+3] = 2 + offset;
 996      idxBuff[i*6+4] = 3 + offset;
 997      idxBuff[i*6+5] = 0 + offset;
 998   }   
 999   mPrimBuffer.unlock();
1000
1001   // Generate the normalized probability.
1002   F32 total = 0.0f;
1003   for ( S32 i=0; i < MAX_COVERTYPES; i++ )
1004   {
1005      // If the element isn't gonna render... then
1006      // set the probability to zero.
1007      if ( mShapeInstances[i] == NULL && mBillboardAspectScales[i] <= 0.0001f )
1008      {
1009         mNormalizedProbability[i] = 0.0f;
1010      }
1011      else
1012      {
1013         mNormalizedProbability[i] = mProbability[i];
1014
1015         total += mProbability[i];
1016      }
1017   }
1018   if ( total > 0.0f )
1019   {
1020      for ( S32 i=0; i < MAX_COVERTYPES; i++ )
1021         mNormalizedProbability[i] /= total;
1022   }
1023}
1024
1025GroundCoverCell* GroundCover::_generateCell( const Point2I& index, 
1026                                             const Box3F& bounds, 
1027                                             U32 placementCount,
1028                                             S32 randSeed )
1029{
1030   PROFILE_SCOPE(GroundCover_GenerateCell);
1031
1032   const Vector<SceneObject*> terrainBlocks = getContainer()->getTerrains();
1033   if ( terrainBlocks.empty() )
1034      return NULL;
1035
1036   // Grab a free cell or allocate a new one.
1037   GroundCoverCell* cell;
1038   if ( mFreeCellList.empty() )
1039   {
1040      cell = new GroundCoverCell();
1041      mAllocCellList.push_back( cell );
1042   }
1043   else
1044   {
1045      cell = mFreeCellList.last();
1046      mFreeCellList.pop_back();
1047   }
1048
1049   cell->mDirty = true;
1050   cell->mIndex = index;
1051   cell->mBounds = bounds;
1052
1053   Point3F pos( 0, 0, 0 );
1054
1055   Box3F renderBounds = bounds;
1056   Point3F point;
1057   Point3F normal;
1058   F32 h;
1059   Point2F cp, uv;
1060   bool hit;
1061   GroundCoverCell::Placement p;
1062   F32 rotation;
1063   F32 size;
1064   F32 sizeExponent;
1065   Point2I lpos;
1066   //F32 value;
1067   VectorF right;
1068   StringTableEntry matName = StringTable->EmptyString();
1069   bool firstElem = true;
1070
1071   TerrainBlock *terrainBlock = NULL;
1072
1073   cell->mBillboards.clear();
1074   cell->mBillboards.reserve( placementCount );
1075   cell->mShapes.clear();
1076   cell->mShapes.reserve( placementCount );
1077
1078   F32   terrainSquareSize, 
1079         oneOverTerrainLength, 
1080         oneOverTerrainSquareSize;
1081   const GBitmap* terrainLM = NULL;
1082
1083   // The RNG that we'll use in generation.
1084   MRandom rand( 0 );
1085
1086   // We process one type at a time.
1087   for ( U32 type=0; type < MAX_COVERTYPES; type++ )
1088   {
1089      // How many cover elements do we need to generate for this type?
1090      const S32 typeCount = mNormalizedProbability[type] * (F32)placementCount;
1091      if ( typeCount <= 0 )
1092         continue;
1093
1094      // Grab the terrain layer for this type.
1095      /*
1096      const TerrainDataLayer* dataLayer = NULL;
1097      const bool typeInvertLayer = mInvertLayer[type];
1098      if ( mLayer[type] > -1 )
1099      {
1100         dataLayer = mTerrainBlock->getDataLayer( mLayer[type] );
1101         if ( dataLayer )
1102         {
1103            // Do an initial check to see if we can place any place anything
1104            // at all...  if the layer area for this element is empty then 
1105            // there is nothing more to do.
1106
1107            RectI area( (S32)mFloor( ( bounds.minExtents.x - pos.x ) * oneOverTerrainSquareSize ),
1108                        (S32)mFloor( ( bounds.minExtents.y - pos.y ) * oneOverTerrainSquareSize ),
1109                        (S32)mCeil( ( bounds.maxExtents.x - pos.x ) * oneOverTerrainSquareSize ),
1110                        (S32)mCeil( ( bounds.maxExtents.y - pos.y ) * oneOverTerrainSquareSize ) );
1111            area.extent -= area.point;
1112
1113            if ( dataLayer->testFill( area, typeInvertLayer ? 255 : 0 ) )
1114               continue;
1115         }
1116      }
1117
1118      // If the layer is not inverted and we have no data 
1119      // then we have nothing to draw.
1120      if ( !typeInvertLayer && !dataLayer )
1121         continue;
1122      */
1123
1124      // We set the seed we were passed which is based on this grids position
1125      // in the world and add the type value.  This keeps changes to one type
1126      // from effecting the outcome of the others.
1127      rand.setSeed( randSeed + type );
1128
1129      // Setup for doing clumps.
1130      S32 clumps = 0;
1131      Point2F clumpCenter(0.0f, 0.0f);
1132      const S32 clumpMin = getMax( 1, (S32)mMinClumpCount[type] );
1133      F32 clumpExponent;   
1134
1135      // We mult this by -1 each billboard we make then use
1136      // it to scale the billboard x axis to flip them.  This
1137      // essentially gives us twice the variation for free.
1138      F32 flipBB = -1.0f;
1139
1140      // Precompute a few other type specific values.
1141      const F32 typeSizeRange = mSizeMax[type] - mSizeMin[type];
1142      const F32 typeMaxSlope = mMaxSlope[type];
1143      const F32 typeMaxElevation = mMaxElevation[type];
1144      const F32 typeMinElevation = mMinElevation[type];
1145      const bool typeIsShape = mShapeInstances[ type ] != NULL;
1146      const Box3F typeShapeBounds = typeIsShape ? mShapeInstances[ type ]->getShape()->bounds : Box3F();
1147      const F32 typeWindScale = mWindScale[type];
1148      StringTableEntry typeLayer = mLayer[type];
1149      const bool typeInvertLayer = mInvertLayer[type];
1150
1151      // We can set this once here... all the placements for this are the same.
1152      p.type = type;
1153      p.windAmplitude = typeWindScale;
1154      p.lmColor.set(1.0f,1.0f,1.0f);
1155
1156      // Generate all the cover elements for this type.
1157      for ( S32 i=0; i < typeCount; i++ )
1158      {
1159         // Do all the other random things here first as to not 
1160         // disturb the random sequence if the terrain geometry
1161         // or cover layers change.
1162
1163         // Get the random position.      
1164         cp.set( rand.randF(), rand.randF() );
1165
1166         // Prepare the clump info.
1167         clumpExponent = mClampF( mPow( rand.randF(), mClumpCountExponent[type] ), 0.0f, 1.0f );
1168         if ( clumps <= 0 )
1169         {
1170            // We're starting a new clump.
1171            clumps = ( clumpMin + mFloor( ( mMaxClumpCount[type] - clumpMin ) * clumpExponent ) ) - 1;
1172            cp.set(  bounds.minExtents.x + cp.x * bounds.len_x(),
1173                     bounds.minExtents.y + cp.y * bounds.len_y() );
1174            clumpCenter = cp;
1175         }
1176         else
1177         {
1178            clumps--;
1179            cp.set( clumpCenter.x - ( ( cp.x - 0.5f ) * mClumpRadius[type] ),
1180                    clumpCenter.y - ( ( cp.y - 0.5f ) * mClumpRadius[type] ) );
1181         }
1182
1183         // Which terrain do I place on?
1184         if ( terrainBlocks.size() == 1 )
1185            terrainBlock = dynamic_cast< TerrainBlock* >( terrainBlocks.first() );
1186         else
1187         {
1188            for ( U32 i = 0; i < terrainBlocks.size(); i++ )
1189            {
1190               TerrainBlock *terrain = dynamic_cast< TerrainBlock* >( terrainBlocks[ i ] );
1191               if( !terrain )
1192                  continue;
1193
1194               const Box3F &terrBounds = terrain->getWorldBox();
1195
1196               if (  cp.x < terrBounds.minExtents.x || cp.x > terrBounds.maxExtents.x ||
1197                     cp.y < terrBounds.minExtents.y || cp.y > terrBounds.maxExtents.y )
1198                  continue;
1199
1200               terrainBlock = terrain;
1201               break;
1202            }
1203         }
1204
1205         // This should only happen if the generation went off
1206         // the edge of the terrain blocks.
1207         if ( !terrainBlock )
1208            continue;
1209
1210         terrainLM = terrainBlock->getLightMap();
1211         pos = terrainBlock->getPosition();
1212
1213         terrainSquareSize = (F32)terrainBlock->getSquareSize();
1214         oneOverTerrainLength = 1.0f / terrainBlock->getWorldBlockSize();
1215         oneOverTerrainSquareSize = 1.0f / terrainSquareSize;
1216
1217         // The size is calculated using an exponent to control 
1218         // the frequency between min and max sizes.
1219         sizeExponent = mClampF( mPow( rand.randF(), mSizeExponent[type] ), 0.0f, 1.0f );
1220         size = mSizeMin[type] + ( typeSizeRange * sizeExponent );
1221
1222         // Generate a random z rotation.
1223         rotation = rand.randF() * M_2PI_F;
1224
1225         // Flip the billboard now for the next generation.
1226         flipBB *= -1.0f;
1227
1228         PROFILE_START( GroundCover_TerrainRayCast );
1229         // Transform billboard point into terrain's frame of reference.
1230         Point3F pp = Point3F(cp.x, cp.y, 0);
1231         terrainBlock->getWorldTransform().mulP(pp);
1232         hit = terrainBlock->getNormalHeightMaterial( Point2F ( pp.x, pp.y ),
1233                                                      &normal, &h, matName );
1234         PROFILE_END(); // GroundCover_TerrainRayCast
1235         
1236         // TODO: When did we loose the world space elevation when
1237         // getting the terrain height?
1238         h += pos.z + mZOffset;
1239
1240         if ( !hit || h > typeMaxElevation || h < typeMinElevation || 
1241              ( typeLayer[0] && !typeInvertLayer && matName != typeLayer ) ||
1242              ( typeLayer[0] && typeInvertLayer && matName == typeLayer ) )
1243            continue;
1244
1245         // Do we need to check slope?
1246         if ( !mIsZero( typeMaxSlope ) )
1247         {
1248            if (mAcos(normal.z) > mDegToRad(typeMaxSlope))
1249               continue;
1250         }
1251
1252         point.set( cp.x, cp.y, h );
1253         p.point = point;
1254         p.rotation = rotation;
1255         p.normal = normal;
1256
1257         // Grab the terrain lightmap color at this position.
1258         //
1259         // TODO: Can't we remove this test?  The terrain 
1260         // lightmap should never be null... NEVER!
1261         //
1262         if ( terrainLM )
1263         {
1264            // TODO: We could probably call terrainLM->getBits()
1265            // once outside the loop then pre-calculate the scalar
1266            // for converting a world position into a lexel...
1267            // avoiding the extra protections inside of sampleTexel().
1268
1269            uv.x = (point.x + pos.x) * oneOverTerrainLength;
1270            uv.y = (point.y + pos.y) * oneOverTerrainLength;
1271            uv.x -= mFloor(uv.x);
1272            uv.y -= mFloor(uv.y);
1273            p.lmColor = terrainLM->sampleTexel(uv.x,uv.y);
1274         }
1275
1276         // Put it into the right list by type.
1277         //
1278         // TODO: Could we break up the generation into
1279         // two separate loops for shapes and billboards
1280         // and gain performance?
1281         //
1282         if ( typeIsShape )
1283         {
1284            // TODO: Convert the size into a real size... not scale!
1285
1286            // TODO: We could probably cache the shape bounds
1287            // into a primitive array and avoid the double pointer
1288            // dereference per placement.
1289
1290            p.size.set( size, size, size );
1291            p.worldBox = typeShapeBounds;
1292            p.worldBox.minExtents *= size;
1293            p.worldBox.maxExtents *= size;
1294            p.worldBox.minExtents += point;
1295            p.worldBox.maxExtents += point;
1296
1297            cell->mShapes.push_back( p );
1298         }
1299         else
1300         {
1301            p.size.y = size;
1302            p.size.x = size * flipBB * mBillboardAspectScales[type]; 
1303            p.worldBox.maxExtents = p.worldBox.minExtents = point;
1304
1305            cell->mBillboards.push_back( p );
1306         }
1307
1308         // Update the render bounds.
1309         if ( firstElem )
1310         {
1311            renderBounds = p.worldBox;
1312            firstElem = false;
1313         }
1314         else
1315         {
1316            renderBounds.extend( p.worldBox.minExtents );
1317            renderBounds.extend( p.worldBox.maxExtents );
1318         }
1319
1320      } // for ( S32 i=0; i < typeCount; i++ )
1321
1322   } // for ( U32 type=0; type < NumCoverTypes; type++ )
1323      
1324
1325   cell->mRenderBounds = renderBounds;
1326   cell->mBounds.minExtents.z = renderBounds.minExtents.z;
1327   cell->mBounds.maxExtents.z = renderBounds.maxExtents.z;
1328
1329   return cell;
1330}
1331
1332void GroundCover::onTerrainUpdated( U32 flags, TerrainBlock *tblock, const Point2I& min, const Point2I& max )
1333{
1334   if ( isServerObject() ) 
1335      return;
1336
1337   // Free all the cells if we've gotten a lightmap update.
1338   if ( flags & TerrainBlock::LightmapUpdate )
1339   {
1340      _freeCells();
1341      return;
1342   }
1343
1344   // TODO: EmptyUpdate doesn't work yet... fix editor/terrain.
1345
1346   // If this is a height or opacity update only clear
1347   // the cells that have changed.
1348   if (  flags & TerrainBlock::HeightmapUpdate || 
1349         flags & TerrainBlock::LayersUpdate ||
1350         flags & TerrainBlock::EmptyUpdate )
1351   {
1352      // Convert the min and max into world space.
1353      const F32 size = tblock->getSquareSize();
1354      const Point3F pos = tblock->getPosition();
1355
1356      // TODO: I don't think this works right with tiling!
1357      Box3F dirty(   F32( min.x * size ) + pos.x, F32( min.y * size ) + pos.y, 0.0f,
1358                     F32( max.x * size ) + pos.x, F32( max.y * size ) + pos.y, 0.0f );
1359      
1360      // Now free any cells that overlap it!
1361      for ( S32 i = 0; i < mCellGrid.size(); i++ )
1362      {
1363         GroundCoverCell* cell = mCellGrid[ i ];
1364         if ( !cell )
1365            continue;
1366
1367         const Box3F& bounds = cell->getBounds();
1368         dirty.minExtents.z = bounds.minExtents.z;
1369         dirty.maxExtents.z = bounds.maxExtents.z;
1370         if ( bounds.isOverlapped( dirty ) )
1371         {
1372            mCellGrid[ i ] = NULL;
1373            _recycleCell( cell );
1374         }
1375      }
1376   }
1377}
1378
1379void GroundCover::_updateCoverGrid( const Frustum &culler )
1380{
1381   PROFILE_SCOPE( GroundCover_UpdateCoverGrid );
1382   
1383   mGridSize = getMax( mGridSize, (U32)2 );
1384
1385   // How many cells in the grid?
1386   const U32 cells = mGridSize * mGridSize;
1387
1388   // Whats the max placement count for each cell considering 
1389   // the grid size and quality scale LOD value.
1390   const S32 placementCount = getMax( ( (F32)mMaxPlacement * smDensityScale ) / F32( mGridSize * mGridSize ), 0.0f );
1391
1392   // If the cell grid isn't sized or the placement count
1393   // changed (most likely because of quality lod) then we
1394   // need to initialize the system again.
1395   if ( mCellGrid.empty() || placementCount != mLastPlacementCount )
1396   {
1397      _initialize( cells, placementCount );
1398      mLastPlacementCount = placementCount;
1399   }
1400
1401   // Without a count... we don't function at all.
1402   if ( placementCount == 0 )
1403      return;
1404
1405   // Clear the scratch grid.
1406   dMemset( mScratchGrid.address(), 0, mScratchGrid.memSize() );
1407
1408   // Calculate the normal cell size here.
1409   const F32 cellSize = ( mRadius * 2.0f ) / (F32)(mGridSize - 1);
1410
1411   // Figure out the root index of the new grid based on the camera position.
1412   Point2I index( (S32)mFloor( ( culler.getPosition().x - mRadius ) / cellSize  ),
1413                  (S32)mFloor( ( culler.getPosition().y - mRadius ) / cellSize ) );
1414
1415   // Figure out the cell shift between the old and new grid positions.
1416   Point2I shift = mGridIndex - index;
1417
1418   // If we've shifted more than one in either axis then we've warped.
1419   bool didWarp = shift.x > 1 || shift.x < -1 || 
1420                  shift.y > 1 || shift.y < -1 ? true : false;
1421
1422   // Go thru the grid shifting each cell we find and
1423   // placing them in the scratch grid.
1424   for ( S32 i = 0; i < mCellGrid.size(); i++ )
1425   {
1426      GroundCoverCell* cell = mCellGrid[ i ];
1427      if ( !cell )
1428         continue;
1429
1430      // Whats our new index?
1431      Point2I newIndex = cell->shiftIndex( shift );
1432
1433      // Is this cell outside of the new grid?
1434      if (  newIndex.x < 0 || newIndex.x >= mGridSize ||
1435            newIndex.y < 0 || newIndex.y >= mGridSize )
1436      {
1437         _recycleCell( cell );
1438         continue;
1439      }
1440
1441      // Place the cell in the scratch grid.
1442      mScratchGrid[ ( newIndex.y * mGridSize ) + newIndex.x ] = cell;
1443   }
1444
1445   // Get the terrain elevation range for setting the default cell bounds.
1446   F32   terrainMinHeight = -5000.0f, 
1447         terrainMaxHeight = 5000.0f;
1448
1449   // Go thru the scratch grid copying each cell back to the
1450   // cell grid and creating new cells as needed.
1451   //
1452   // By limiting ourselves to only one new cell generation per
1453   // update we're lowering the performance hiccup during movement
1454   // without getting into the complexity of threading.  The delay
1455   // in generation is rarely noticeable in normal play.
1456   //
1457   // The only caveat is that we need to generate the entire visible
1458   // grid when we warp.
1459   U32 cellsGenerated = 0;
1460   for ( S32 i = 0; i < mScratchGrid.size(); i++ )
1461   {
1462      GroundCoverCell* cell = mScratchGrid[ i ];
1463      if ( !cell && ( cellsGenerated == 0 || didWarp ) )
1464      {
1465         // Get the index point of this new cell.
1466         S32 y = i / mGridSize;
1467         S32 x = i - ( y * mGridSize );
1468         Point2I newIndex = index + Point2I( x, y );
1469
1470         // What will be the world placement bounds for this cell.
1471         Box3F bounds;
1472         bounds.minExtents.set( newIndex.x * cellSize, newIndex.y * cellSize, terrainMinHeight );
1473         bounds.maxExtents.set( bounds.minExtents.x + cellSize, bounds.minExtents.y + cellSize, terrainMaxHeight );
1474
1475         if ( mCuller.isCulled( bounds ) )
1476         {
1477            mCellGrid[ i ] = NULL;
1478            continue;
1479         }
1480
1481         // We need to allocate a new cell.
1482         //
1483         // TODO: This is the expensive call and where we should optimize. In
1484         // particular the next best optimization would be to take advantage of
1485         // multiple cores so that we can generate all the cells in one update.
1486         //
1487         // Instead of generating the cell here we would allocate a cell and stick
1488         // it into a thread safe queue (maybe lockless) as well as the mCellGrid.
1489         // Once all were allocated we would do something like this...
1490         //
1491         // TorqueParallelProcess( cellsToGenerateQueue, _generateCell );
1492         //
1493         // Internally this function would pass the queue to some global pre-allocated
1494         // worker threads which are locked to a particular core.  While the main 
1495         // thread waits for the worker threads to finish it will process cells itself.
1496         // 
1497
1498         cell = _generateCell(   newIndex - index, 
1499                                 bounds, 
1500                                 placementCount, 
1501                                 mRandomSeed + mAbs( newIndex.x ) + mAbs( newIndex.y ) );
1502
1503         // Increment our generation count.
1504         if ( cell )
1505            ++cellsGenerated;
1506      }
1507
1508      mCellGrid[ i ] = cell;
1509   }
1510
1511   // Store the new grid index.
1512   mGridIndex = index;
1513}
1514
1515void GroundCover::prepRenderImage( SceneRenderState *state )
1516{
1517   // Reset stats each time we hit the diffuse pass.
1518
1519   if( state->isDiffusePass() )
1520   {
1521      smStatRenderedCells = 0;
1522      smStatRenderedBillboards = 0;
1523      smStatRenderedBatches = 0;
1524      smStatRenderedShapes = 0;
1525   }
1526
1527   // TODO: Make sure that the ground cover stops rendering
1528   // if you're inside a zoned interior.
1529
1530   if ( state->isReflectPass() && mReflectRadiusScale <= 0.0f )
1531      return;
1532
1533   // Nothing to do if this is a shadow pass and shapes in this GoundCover
1534   // should not be casting shadows.
1535
1536   if( state->isShadowPass() && !mShapesCastShadows )
1537      return;
1538
1539   GFXTransformSaver saver;
1540
1541   // Setup the frustum culler.
1542   if ( ( mCuller.getPosition().isZero() || !mDebugLockFrustum ) && !state->isShadowPass() )
1543      mCuller = state->getCullingFrustum();
1544
1545   // Update the cells, but only during the diffuse pass. 
1546   // We don't want cell generation to thrash when the reflection camera 
1547   // position doesn't match the diffuse camera!
1548   if ( state->isDiffusePass() )
1549      _updateCoverGrid( mCuller );
1550
1551   // Render billboards but not into shadow passes.
1552
1553   if ( !state->isShadowPass() && mMatInst->isValid() && !mDebugNoBillboards )
1554   {
1555      PROFILE_SCOPE( GroundCover_RenderBillboards );
1556
1557      // Take zoom into account.
1558      F32 screenScale = state->getWorldToScreenScale().y / state->getViewport().extent.y;
1559
1560      // Set the far distance for billboards.
1561      mCuller.setFarDist( mRadius * screenScale );
1562
1563      F32 cullScale = 1.0f;
1564      if ( state->isReflectPass() )
1565         cullScale = mReflectRadiusScale;
1566      
1567      // Setup our shader const data.
1568      // Must be done prior to submitting our render instance.
1569
1570      mShaderConstData.fadeInfo.set( mFadeRadius * cullScale * screenScale, mRadius * cullScale * screenScale );    
1571
1572      const F32 simTime = Sim::getCurrentTime() * 0.001f;
1573
1574      mShaderConstData.gustInfo.set( mWindGustLength, mWindGustFrequency * simTime, mWindGustStrength );
1575      mShaderConstData.turbInfo.set( mWindTurbulenceFrequency * simTime, mWindTurbulenceStrength );      
1576
1577      // Use the camera's forward vector to calculate the camera's right
1578      // and up vectors.  This removes any camera banking from affecting
1579      // the ground cover.
1580      const MatrixF &camMat = state->getDiffuseCameraTransform();
1581      Point3F camDir, camUp, camRight;
1582      camMat.getColumn( 1, &camDir );
1583      mCross( camDir, Point3F::UnitZ, &camRight );
1584      if ( camRight.magnitudeSafe() == 0.0f )
1585      {
1586         camRight.set( 0.0f, -1.0f, 0.0f );
1587      }
1588      camRight.normalizeSafe();
1589      mCross( camRight, camDir, &camUp );
1590
1591      // Limit the camera up vector to keep the billboards 
1592      // from leaning too far down into the terrain.
1593      VectorF lookDir( camDir.x, camDir.y, 0.0f );
1594      F32 angle;
1595      if ( !lookDir.isZero() )
1596      {
1597         lookDir.normalize();
1598         angle = mAcos( mDot( camUp, lookDir ) );
1599      }
1600      else
1601      {
1602         angle = camDir.z < 0.0f ? 0.0f : ( M_PI_F / 2.0f );
1603      }
1604
1605      const F32 maxBillboardTiltRads = mDegToRad( mMaxBillboardTiltAngle );
1606      if ( angle < (M_PI_F / 2.0f) - maxBillboardTiltRads )
1607      {
1608         QuatF quat( AngAxisF( camRight, maxBillboardTiltRads ) );
1609         quat.mulP( VectorF( 0.0f, 0.0f, 1.0f ), &camUp );
1610      }
1611
1612      mShaderConstData.camRight = camRight;
1613      mShaderConstData.camUp = camUp;      
1614
1615
1616      // Cull and submit render instances for cells.
1617
1618      for ( S32 i = 0; i < mCellGrid.size(); i++ )
1619      {
1620         GroundCoverCell* cell = mCellGrid[ i ];
1621         if ( !cell )
1622            continue;
1623
1624         if ( mCuller.isCulled( cell->getRenderBounds() ) )
1625            continue;
1626
1627         cell->renderBillboards( state, mMatInst, &mPrimBuffer );
1628      }     
1629   }
1630
1631   // Render TS shapes.
1632
1633   if ( !mDebugNoShapes )
1634   {
1635      // Prepare to render the grid shapes.
1636      PROFILE_SCOPE(GroundCover_RenderShapes);
1637
1638      // Set up our TS render state.
1639      TSRenderState rdata;
1640      rdata.setSceneState( state );
1641
1642      // We might have some forward lit materials
1643      // so pass down a query to gather lights.
1644      LightQuery query;
1645      rdata.setLightQuery( &query );
1646
1647      // TODO: Add a special fade out for DTS?
1648      mCuller.setFarDist( mShapeCullRadius );
1649
1650      for ( S32 i = 0; i < mCellGrid.size(); i++ )
1651      {
1652         GroundCoverCell* cell = mCellGrid[ i ];
1653         if ( !cell || mDebugNoShapes )
1654            continue;
1655
1656         const Box3F &renderBounds = cell->getRenderBounds();
1657         U32 clipMask = mCuller.testPlanes( renderBounds, Frustum::PlaneMaskAll );
1658         if ( clipMask == -1 )
1659            continue;
1660
1661         smStatRenderedCells++;
1662
1663         // Use the cell bounds as the light query volume.
1664         //
1665         // This means all forward lit items in this cell will 
1666         // get the same lights, but it performs much better.
1667         query.init( renderBounds );
1668
1669         // Render the shapes in this cell... only pass the culler if the
1670         // cell wasn't fully within the frustum.
1671         smStatRenderedShapes += cell->renderShapes(  
1672                                    rdata, 
1673                                    clipMask != 0 ? &mCuller : NULL, 
1674                                    mShapeInstances );
1675      }
1676   }
1677
1678   if ( mDebugRenderCells )
1679   {
1680      RenderPassManager *pass = state->getRenderPass();
1681
1682      ObjectRenderInst *ri = pass->allocInst<ObjectRenderInst>();
1683      ri->type = RenderPassManager::RIT_Editor;
1684      ri->renderDelegate.bind( this, &GroundCover::_debugRender );
1685      pass->addInst( ri );
1686   }    
1687}
1688
1689void GroundCover::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
1690{   
1691   GFXDrawUtil* drawer = GFX->getDrawUtil();
1692 
1693   GFXStateBlockDesc desc;
1694   desc.setZReadWrite( true, false );
1695   desc.setBlend( true );
1696   desc.fillMode = GFXFillWireframe;
1697
1698   for ( S32 i = 0; i < mCellGrid.size(); i++ )
1699   {
1700      GroundCoverCell* cell = mCellGrid[ i ];
1701      if ( !cell || ( cell->mBillboards.size() + cell->mShapes.size() ) == 0 )
1702         continue;
1703
1704      if ( mCuller.isCulled( cell->getRenderBounds() ) )
1705         continue;
1706
1707      drawer->drawCube( desc, cell->getRenderBounds().getExtents(), cell->getRenderBounds().getCenter(), ColorI( 0, 255, 0 ) );
1708   }
1709}
1710