Torque3D Documentation / _generateds / fxFoliageReplicator.cpp

fxFoliageReplicator.cpp

Engine/source/T3D/fx/fxFoliageReplicator.cpp

More...

Public Variables

Public Functions

ConsoleDocClass(fxFoliageReplicator , "@brief An emitter <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> replicate <a href="/coding/class/classfxfoliageitem/">fxFoliageItem</a> objects across an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">area.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Foliage\n</a>" )
DefineEngineFunction(StartFoliageReplication , void , () , "Activates the foliage <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">replicator.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Call the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">function\n</a>" "StartFoliageReplication();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup Foliage" )
GFXImplementVertexFormat(GFXVertexFoliage )

Detailed Description

Public Variables

const U32 AlphaTexLen 
bool gEditingMission 

For frame signal.

const F32 PeriodLenMinus 

Public Functions

ConsoleDocClass(fxFoliageReplicator , "@brief An emitter <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> replicate <a href="/coding/class/classfxfoliageitem/">fxFoliageItem</a> objects across an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">area.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Foliage\n</a>" )

DefineEngineFunction(StartFoliageReplication , void , () , "Activates the foliage <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">replicator.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Call the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">function\n</a>" "StartFoliageReplication();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup Foliage" )

GFXImplementVertexFormat(GFXVertexFoliage )

IMPLEMENT_CO_NETOBJECT_V1(fxFoliageReplicator )

   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// Written by Melvyn May, Started on 4th August 2002.
  25//
  26// "My code is written for the Torque community, so do your worst with it,
  27// just don't rip-it-off and call it your own without even thanking me".
  28//
  29// - Melv.
  30//
  31//
  32// Conversion to TSE By Brian "bzztbomb" Richardson 9/2005
  33//   This was a neat piece of code!  Thanks Melv!
  34//   I've switched this to use one large indexed primitive buffer.  All animation
  35//   is then done in the vertex shader.  This means we have a static vertex/primitive
  36//   buffer that never changes!  How spiff!  Because of this, the culling code was
  37//   changed to render out full quadtree nodes, we don't try to cull each individual
  38//   node ourselves anymore.  This means to get good performance, you probably need to do the 
  39//   following:
  40//     1.  If it's a small area to cover, turn off culling completely.
  41//     2.  You want to tune the parameters to make sure there are a lot of billboards within
  42//         each quadrant.
  43// 
  44// POTENTIAL TODO LIST:
  45//   TODO: Clamp item alpha to fog alpha
  46
  47//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  48// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  49// Copyright (C) 2015 Faust Logic, Inc.
  50//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  51
  52#include "platform/platform.h"
  53#include "T3D/fx/fxFoliageReplicator.h"
  54
  55#include "gfx/gfxDevice.h"
  56#include "gfx/primBuilder.h"  // Used for debug / mission edit rendering
  57#include "console/consoleTypes.h"
  58#include "core/stream/bitStream.h"
  59#include "math/mRandom.h"
  60#include "math/mathIO.h"
  61#include "console/simBase.h"
  62#include "scene/sceneManager.h"
  63#include "renderInstance/renderPassManager.h"
  64#include "scene/sceneRenderState.h"
  65#include "sim/netConnection.h"
  66#include "materials/shaderData.h"
  67#include "console/engineAPI.h"
  68
  69const U32 AlphaTexLen = 1024;
  70
  71GFXImplementVertexFormat( GFXVertexFoliage )
  72{
  73   addElement( "POSITION", GFXDeclType_Float3 );
  74   addElement( "NORMAL", GFXDeclType_Float3 );
  75   addElement( "TEXCOORD", GFXDeclType_Float2, 0 );
  76   addElement( "TEXCOORD", GFXDeclType_Float2, 1 );
  77}
  78
  79//------------------------------------------------------------------------------
  80//
  81// Put the function in /example/common/editor/ObjectBuilderGui.gui [around line 458] ...
  82//
  83// function ObjectBuilderGui::buildfxFoliageReplicator(%this)
  84// {
  85//    %this.className = "fxFoliageReplicator";
  86//    %this.process();
  87// }
  88//
  89//------------------------------------------------------------------------------
  90//
  91// Put this in /example/common/editor/EditorGui.tscript in [function Creator::init( %this )]
  92// 
  93//   %Environment_Item[8] = "fxFoliageReplicator";  <-- ADD THIS.
  94//
  95//------------------------------------------------------------------------------
  96//
  97// Put this in /example/common/client/missionDownload.tscript in [function clientCmdMissionStartPhase3(%seq,%missionName)] (line 65)
  98// after codeline 'onPhase2Complete();'.
  99//
 100// StartFoliageReplication();
 101//
 102//------------------------------------------------------------------------------
 103//
 104// Put this in /engine/console/simBase.h (around line 509) in
 105//
 106// namespace Sim
 107//  {
 108//    DeclareNamedSet(fxFoliageSet)  <-- ADD THIS (Note no semi-colon).
 109//
 110//------------------------------------------------------------------------------
 111//
 112// Put this in /engine/console/simBase.cc (around line 19) in
 113//
 114//  ImplementNamedSet(fxFoliageSet)  <-- ADD THIS (Note no semi-colon).
 115//
 116//------------------------------------------------------------------------------
 117//
 118// Put this in /engine/console/simManager.cc [function void init()] (around line 269).
 119//
 120// namespace Sim
 121//  {
 122//    InstantiateNamedSet(fxFoliageSet);  <-- ADD THIS (Including Semi-colon).
 123//
 124//------------------------------------------------------------------------------
 125extern bool gEditingMission;
 126
 127//------------------------------------------------------------------------------
 128
 129IMPLEMENT_CO_NETOBJECT_V1(fxFoliageReplicator);
 130
 131ConsoleDocClass( fxFoliageReplicator,
 132   "@brief An emitter to replicate fxFoliageItem objects across an area.\n"
 133   "@ingroup Foliage\n"
 134);
 135
 136//------------------------------------------------------------------------------
 137//
 138// Trig Table Lookups.
 139//
 140//------------------------------------------------------------------------------
 141const F32 PeriodLenMinus = (F32) (2.0f * M_PI) - 0.01f;
 142
 143//------------------------------------------------------------------------------
 144//
 145// Class: fxFoliageRenderList
 146//
 147//------------------------------------------------------------------------------
 148
 149void fxFoliageRenderList::SetupClipPlanes( SceneRenderState* state, const F32 farClipPlane )
 150{
 151   const F32 nearPlane = state->getNearPlane();
 152   const F32 farPlane = farClipPlane;
 153
 154   const Frustum& frustum = state->getCullingFrustum();
 155
 156   // [rene, 23-Feb-11] Why isn't this preserving the ortho state of the original frustum?
 157
 158   mFrustum.set(  false,//zoneState.frustum.isOrtho(),
 159                  frustum.getNearLeft(),
 160                  frustum.getNearRight(),
 161                  frustum.getNearTop(),
 162                  frustum.getNearBottom(),
 163                  nearPlane,
 164                  farPlane,
 165                  frustum.getTransform()
 166   );
 167
 168
 169   mBox = mFrustum.getBounds();
 170}
 171
 172//------------------------------------------------------------------------------
 173
 174
 175inline void fxFoliageRenderList::DrawQuadBox(const Box3F& QuadBox, const LinearColorF Colour)
 176{
 177   // Define our debug box.
 178   static Point3F BoxPnts[] = {
 179      Point3F(0,0,0),
 180      Point3F(0,0,1),
 181      Point3F(0,1,0),
 182      Point3F(0,1,1),
 183      Point3F(1,0,0),
 184      Point3F(1,0,1),
 185      Point3F(1,1,0),
 186      Point3F(1,1,1)
 187   };
 188
 189   static U32 BoxVerts[][4] = {
 190      {0,2,3,1},     // -x
 191      {7,6,4,5},     // +x
 192      {0,1,5,4},     // -y
 193      {3,2,6,7},     // +y
 194      {0,4,6,2},     // -z
 195      {3,7,5,1}      // +z
 196   };
 197
 198   // Project our Box Points.
 199   Point3F ProjectionPoints[8];
 200
 201   for( U32 i=0; i<8; i++ )
 202   {
 203      ProjectionPoints[i].set(BoxPnts[i].x ? QuadBox.maxExtents.x : QuadBox.minExtents.x,
 204         BoxPnts[i].y ? QuadBox.maxExtents.y : QuadBox.minExtents.y,
 205         BoxPnts[i].z ? (mHeightLerp * QuadBox.maxExtents.z) + (1-mHeightLerp) * QuadBox.minExtents.z : QuadBox.minExtents.z);
 206
 207   }
 208
 209   PrimBuild::color(Colour);
 210
 211   // Draw the Box.
 212   for(U32 x = 0; x < 6; x++)
 213   {
 214      // Draw a line-loop.
 215      PrimBuild::begin(GFXLineStrip, 5);
 216
 217      for(U32 y = 0; y < 4; y++)
 218      {
 219         PrimBuild::vertex3f(ProjectionPoints[BoxVerts[x][y]].x,
 220            ProjectionPoints[BoxVerts[x][y]].y,
 221            ProjectionPoints[BoxVerts[x][y]].z);
 222      }
 223      PrimBuild::vertex3f(ProjectionPoints[BoxVerts[x][0]].x,
 224         ProjectionPoints[BoxVerts[x][0]].y,
 225         ProjectionPoints[BoxVerts[x][0]].z);
 226      PrimBuild::end();
 227   } 
 228}
 229
 230//------------------------------------------------------------------------------
 231bool fxFoliageRenderList::IsQuadrantVisible(const Box3F VisBox, const MatrixF& RenderTransform)
 232{
 233   // Can we trivially accept the visible box?
 234   if ( !mFrustum.isCulled( VisBox ) )
 235      return true;
 236
 237   // Not visible.
 238   return false;
 239}
 240
 241
 242
 243//------------------------------------------------------------------------------
 244//
 245// Class: fxFoliageCulledList
 246//
 247//------------------------------------------------------------------------------
 248fxFoliageCulledList::fxFoliageCulledList(Box3F SearchBox, fxFoliageCulledList* InVec)
 249{
 250   // Find the Candidates.
 251   FindCandidates(SearchBox, InVec);
 252}
 253
 254//------------------------------------------------------------------------------
 255
 256void fxFoliageCulledList::FindCandidates(const Box3F& SearchBox, fxFoliageCulledList* InVec)
 257{
 258   // Search the Culled List.
 259   for (U32 i = 0; i < InVec->GetListCount(); i++)
 260   {
 261      // Is this Box overlapping our search box?
 262      if (SearchBox.isOverlapped(InVec->GetElement(i)->FoliageBox))
 263      {
 264         // Yes, so add it to our culled list.
 265         mCulledObjectSet.push_back(InVec->GetElement(i));
 266      }
 267   }
 268}
 269
 270
 271
 272//------------------------------------------------------------------------------
 273//
 274// Class: fxFoliageReplicator
 275//
 276//------------------------------------------------------------------------------
 277
 278fxFoliageReplicator::fxFoliageReplicator()
 279{
 280   // Setup NetObject.
 281   mTypeMask |= StaticObjectType;
 282   mNetFlags.set(Ghostable | ScopeAlways);
 283
 284   // Reset Client Replication Started.
 285   mClientReplicationStarted = false;
 286
 287   // Reset Foliage Count.
 288   mCurrentFoliageCount = 0;
 289
 290   dMemset(&mFrustumRenderSet, 0, sizeof(mFrustumRenderSet));
 291   // Reset Creation Area Angle Animation.
 292   mCreationAreaAngle = 0;
 293
 294   // Reset Last Render Time.
 295   mLastRenderTime = 0;
 296
 297   // Reset Foliage Nodes.
 298   mPotentialFoliageNodes = 0;
 299   // Reset Billboards Acquired.
 300   mBillboardsAcquired = 0;
 301
 302   // Reset Frame Serial ID.
 303   mFrameSerialID = 0;
 304   mQuadTreeLevels = 0;
 305   mNextAllocatedNodeIdx = 0;
 306   mAlphaLookup = NULL;
 307   mFadeInGradient = 0.0f;
 308   mFadeOutGradient = 0.0f;
 309   mGlobalSwayPhase = 0.0f;
 310   mGlobalSwayTimeRatio = 1.0f;
 311   mGlobalLightPhase = 0.0f;
 312   mGlobalLightTimeRatio = 1.0f;
 313
 314   mDirty = true;
 315
 316   mFoliageShaderProjectionSC = NULL;
 317   mFoliageShaderWorldSC = NULL;
 318   mFoliageShaderGlobalSwayPhaseSC = NULL;
 319   mFoliageShaderSwayMagnitudeSideSC = NULL;
 320   mFoliageShaderSwayMagnitudeFrontSC = NULL;
 321   mFoliageShaderGlobalLightPhaseSC = NULL;
 322   mFoliageShaderLuminanceMagnitudeSC = NULL;
 323   mFoliageShaderLuminanceMidpointSC = NULL;
 324   mFoliageShaderDistanceRangeSC = NULL;
 325   mFoliageShaderCameraPosSC = NULL;
 326   mFoliageShaderTrueBillboardSC = NULL;
 327   mFoliageShaderGroundAlphaSC = NULL;
 328   mFoliageShaderAmbientColorSC = NULL;
 329   mDiffuseTextureSC = NULL;
 330   mAlphaMapTextureSC = NULL;
 331
 332   mShaderData = NULL;
 333}
 334
 335//------------------------------------------------------------------------------
 336
 337fxFoliageReplicator::~fxFoliageReplicator()
 338{
 339   if (mAlphaLookup)
 340      delete mAlphaLookup;
 341
 342   mPlacementSB = NULL;
 343}
 344
 345//------------------------------------------------------------------------------
 346
 347void fxFoliageReplicator::initPersistFields()
 348{
 349   // Add out own persistent fields.
 350   addGroup( "Debugging" );   // MM: Added Group Header.
 351      addField( "UseDebugInfo",        TypeBool,      Offset( mFieldData.mUseDebugInfo,         fxFoliageReplicator ), "Culling bins are drawn when set to true." );
 352      addField( "DebugBoxHeight",      TypeF32,       Offset( mFieldData.mDebugBoxHeight,       fxFoliageReplicator ), "Height multiplier for drawn culling bins.");
 353      addField( "HideFoliage",         TypeBool,      Offset( mFieldData.mHideFoliage,          fxFoliageReplicator ), "Foliage is hidden when set to true." );
 354      addField( "ShowPlacementArea",   TypeBool,      Offset( mFieldData.mShowPlacementArea,    fxFoliageReplicator ), "Draw placement rings when set to true." );
 355      addField( "PlacementAreaHeight", TypeS32,       Offset( mFieldData.mPlacementBandHeight,  fxFoliageReplicator ), "Height of the placement ring in world units." );
 356      addField( "PlacementColour",     TypeColorF,    Offset( mFieldData.mPlaceAreaColour,      fxFoliageReplicator ), "Color of the placement ring." );
 357   endGroup( "Debugging" );   // MM: Added Group Footer.
 358
 359   addGroup( "Media" ); // MM: Added Group Header.
 360      addField( "Seed",                TypeS32,       Offset( mFieldData.mSeed,                 fxFoliageReplicator ), "Random seed for foliage placement." );
 361      addField( "FoliageFile",         TypeFilename,  Offset( mFieldData.mFoliageFile,          fxFoliageReplicator ), "Image file for the foliage texture." );
 362      addField( "FoliageCount",        TypeS32,       Offset( mFieldData.mFoliageCount,         fxFoliageReplicator ), "Maximum foliage instance count." );
 363      addField( "FoliageRetries",      TypeS32,       Offset( mFieldData.mFoliageRetries,       fxFoliageReplicator ), "Number of times to try placing a foliage instance before giving up." );
 364   endGroup( "Media" ); // MM: Added Group Footer.
 365
 366   addGroup( "Area" );  // MM: Added Group Header.
 367      addField( "InnerRadiusX",        TypeS32,       Offset( mFieldData.mInnerRadiusX,         fxFoliageReplicator ), "Placement area inner radius on the X axis" );
 368      addField( "InnerRadiusY",        TypeS32,       Offset( mFieldData.mInnerRadiusY,         fxFoliageReplicator ), "Placement area inner radius on the Y axis" );
 369      addField( "OuterRadiusX",        TypeS32,       Offset( mFieldData.mOuterRadiusX,         fxFoliageReplicator ), "Placement area outer radius on the X axis" );
 370      addField( "OuterRadiusY",        TypeS32,       Offset( mFieldData.mOuterRadiusY,         fxFoliageReplicator ), "Placement area outer radius on the Y axis" );
 371   endGroup( "Area" );  // MM: Added Group Footer.
 372
 373   addGroup( "Dimensions" );  // MM: Added Group Header.
 374      addField( "MinWidth",            TypeF32,       Offset( mFieldData.mMinWidth,             fxFoliageReplicator ), "Minimum width of foliage billboards" );
 375      addField( "MaxWidth",            TypeF32,       Offset( mFieldData.mMaxWidth,             fxFoliageReplicator ), "Maximum width of foliage billboards" );
 376      addField( "MinHeight",           TypeF32,       Offset( mFieldData.mMinHeight,            fxFoliageReplicator ), "Minimum height of foliage billboards" );
 377      addField( "MaxHeight",           TypeF32,       Offset( mFieldData.mMaxHeight,            fxFoliageReplicator ), "Maximum height of foliage billboards" );
 378      addField( "FixAspectRatio",      TypeBool,      Offset( mFieldData.mFixAspectRatio,       fxFoliageReplicator ), "Maintain aspect ratio of image if true. This option ignores MaxWidth." );
 379      addField( "FixSizeToMax",        TypeBool,      Offset( mFieldData.mFixSizeToMax,         fxFoliageReplicator ), "Use only MaxWidth and MaxHeight for billboard size. Ignores MinWidth and MinHeight." );
 380      addField( "OffsetZ",             TypeF32,       Offset( mFieldData.mOffsetZ,              fxFoliageReplicator ), "Offset billboards by this amount vertically." );
 381      addField( "RandomFlip",          TypeBool,      Offset( mFieldData.mRandomFlip,           fxFoliageReplicator ), "Randomly flip billboards left-to-right." );
 382      addField( "UseTrueBillboards",   TypeBool,      Offset( mFieldData.mUseTrueBillboards,    fxFoliageReplicator ), "Use camera facing billboards ( including the z axis )." );
 383   endGroup( "Dimensions" );  // MM: Added Group Footer.
 384
 385   addGroup( "Culling" );  // MM: Added Group Header.
 386      addField( "UseCulling",          TypeBool,      Offset( mFieldData.mUseCulling,           fxFoliageReplicator ), "Use culling bins when enabled." );
 387      addField( "CullResolution",      TypeS32,       Offset( mFieldData.mCullResolution,       fxFoliageReplicator ), "Minimum size of culling bins.  Must be >= 8 and <= OuterRadius." );
 388      addField( "ViewDistance",        TypeF32,       Offset( mFieldData.mViewDistance,         fxFoliageReplicator ), "Maximum distance from camera where foliage appears." );
 389      addField( "ViewClosest",         TypeF32,       Offset( mFieldData.mViewClosest,          fxFoliageReplicator ), "Minimum distance from camera where foliage appears." );
 390      addField( "FadeInRegion",        TypeF32,       Offset( mFieldData.mFadeInRegion,         fxFoliageReplicator ), "Region beyond ViewDistance where foliage fades in/out." );
 391      addField( "FadeOutRegion",       TypeF32,       Offset( mFieldData.mFadeOutRegion,        fxFoliageReplicator ), "Region before ViewClosest where foliage fades in/out." );
 392      addField( "AlphaCutoff",         TypeF32,       Offset( mFieldData.mAlphaCutoff,          fxFoliageReplicator ), "Minimum alpha value allowed on foliage instances." );
 393      addField( "GroundAlpha",         TypeF32,       Offset( mFieldData.mGroundAlpha,          fxFoliageReplicator ), "Alpha of the foliage at ground level. 0 = transparent, 1 = opaque." );
 394   endGroup( "Culling" );  // MM: Added Group Footer.
 395
 396   addGroup( "Animation" );   // MM: Added Group Header.
 397      addField( "SwayOn",              TypeBool,      Offset( mFieldData.mSwayOn,               fxFoliageReplicator ), "Foliage should sway randomly when true." );
 398      addField( "SwaySync",            TypeBool,      Offset( mFieldData.mSwaySync,             fxFoliageReplicator ), "Foliage instances should sway together when true and SwayOn is enabled." );
 399      addField( "SwayMagSide",         TypeF32,       Offset( mFieldData.mSwayMagnitudeSide,    fxFoliageReplicator ), "Left-to-right sway magnitude." );
 400      addField( "SwayMagFront",        TypeF32,       Offset( mFieldData.mSwayMagnitudeFront,   fxFoliageReplicator ), "Front-to-back sway magnitude." );
 401      addField( "MinSwayTime",         TypeF32,       Offset( mFieldData.mMinSwayTime,          fxFoliageReplicator ), "Minumum sway cycle time in seconds." );
 402      addField( "MaxSwayTime",         TypeF32,       Offset( mFieldData.mMaxSwayTime,          fxFoliageReplicator ), "Maximum sway cycle time in seconds." );
 403   endGroup( "Animation" );   // MM: Added Group Footer.
 404
 405   addGroup( "Lighting" ); // MM: Added Group Header.
 406      addField( "LightOn",             TypeBool,      Offset( mFieldData.mLightOn,              fxFoliageReplicator ), "Foliage should be illuminated with changing lights when true." );
 407      addField( "LightSync",           TypeBool,      Offset( mFieldData.mLightSync,            fxFoliageReplicator ), "Foliage instances have the same lighting when set and LightOn is set." );
 408      addField( "MinLuminance",        TypeF32,       Offset( mFieldData.mMinLuminance,         fxFoliageReplicator ), "Minimum luminance for foliage instances." );
 409      addField( "MaxLuminance",        TypeF32,       Offset( mFieldData.mMaxLuminance,         fxFoliageReplicator ), "Maximum luminance for foliage instances." );
 410      addField( "LightTime",           TypeF32,       Offset( mFieldData.mLightTime,            fxFoliageReplicator ), "Time before foliage illumination cycle repeats." );
 411   endGroup( "Lighting" ); // MM: Added Group Footer.
 412
 413   addGroup( "Restrictions" );   // MM: Added Group Header.
 414      addField( "AllowOnTerrain",      TypeBool,      Offset( mFieldData.mAllowOnTerrain,       fxFoliageReplicator ), "Foliage will be placed on terrain when set." );
 415      addField( "AllowOnStatics",      TypeBool,      Offset( mFieldData.mAllowStatics,         fxFoliageReplicator ), "Foliage will be placed on Static shapes when set." );
 416      addField( "AllowOnWater",        TypeBool,      Offset( mFieldData.mAllowOnWater,         fxFoliageReplicator ), "Foliage will be placed on/under water when set." );
 417      addField( "AllowWaterSurface",   TypeBool,      Offset( mFieldData.mAllowWaterSurface,    fxFoliageReplicator ), "Foliage will be placed on water when set. Requires AllowOnWater." );
 418      addField( "AllowedTerrainSlope", TypeS32,       Offset( mFieldData.mAllowedTerrainSlope,  fxFoliageReplicator ), "Maximum surface angle allowed for foliage instances." );
 419   endGroup( "Restrictions" );   // MM: Added Group Footer.
 420
 421   addGroup( "AFX" );
 422      addField( "AmbientModulationBias", TypeF32,     Offset( mFieldData.mAmbientModulationBias,fxFoliageReplicator ), "Multiplier controling amount foliage is modulated by sun's ambient." );
 423   endGroup( "AFX" );
 424   // Initialise parents' persistent fields.
 425   Parent::initPersistFields();
 426}
 427
 428//------------------------------------------------------------------------------
 429
 430void fxFoliageReplicator::CreateFoliage(void)
 431{
 432   F32            HypX, HypY;
 433   F32            Angle;
 434   U32            RelocationRetry;
 435   Point3F        FoliagePosition;
 436   Point3F        FoliageStart;
 437   Point3F        FoliageEnd;
 438   Point3F        FoliageScale;
 439   bool        CollisionResult;
 440   RayInfo        RayEvent;
 441
 442   // Let's get a minimum bounding volume.
 443   Point3F  MinPoint( -0.5, -0.5, -0.5 );
 444   Point3F  MaxPoint(  0.5,  0.5,  0.5 );
 445
 446   // Check Host.
 447   AssertFatal(isClientObject(), "Trying to create Foliage on Server, this is bad!");
 448
 449      // Cannot continue without Foliage Texture!
 450      if (dStrlen(mFieldData.mFoliageFile) == 0) 
 451         return;
 452
 453   // Check that we can position somewhere!
 454   if (!(   mFieldData.mAllowOnTerrain ||
 455      mFieldData.mAllowStatics ||
 456      mFieldData.mAllowOnWater))
 457   {
 458      // Problem ...
 459      Con::warnf(ConsoleLogEntry::General, "fxFoliageReplicator - Could not place Foliage, All alloweds are off!");
 460
 461      // Return here.
 462      return;
 463   }
 464
 465   // Destroy Foliage if we've already got some.
 466   if (mCurrentFoliageCount != 0) DestroyFoliage();
 467
 468   // Inform the user if culling has been disabled!
 469   if (!mFieldData.mUseCulling)
 470   {
 471      // Console Output.
 472      Con::printf("fxFoliageReplicator - Culling has been disabled!");
 473   }
 474
 475   // ----------------------------------------------------------------------------------------------------------------------
 476   // > Calculate the Potential Foliage Nodes Required to achieve the selected culling resolution.
 477   // > Populate Quad-tree structure to depth determined by culling resolution.
 478   //
 479   // A little explanation is called for here ...
 480   //
 481   //       The approach to this problem has been choosen to make it *much* easier for
 482   //       the user to control the quad-tree culling resolution.  The user enters a single
 483   //       world-space value 'mCullResolution' which controls the highest resolution at
 484   //       which the replicator will check visibility culling.
 485   //
 486   //       example: If 'mCullResolution' is 32 and the size of the replicated area is 128 radius
 487   //                (256 diameter) then this results in the replicator creating a quad-tree where
 488   //                there are 256/32 = 8x8 blocks.  Each of these can be checked to see if they
 489   //                reside within the viewing frustum and if not then they get culled therefore
 490   //                removing the need to parse all the billboards that occcupy that region.
 491   //                Most of the time you will get better than this as the culling algorithm will
 492   //                check the culling pyramid from the top to bottom e.g. the follow 'blocks'
 493   //                will be checked:-
 494   //
 495   //                 1 x 256 x 256 (All of replicated area)
 496   //                 4 x 128 x 128 (4 corners of above)
 497   //                16 x  64 x  64 (16 x 4 corners of above)
 498   //                etc.
 499   //
 500   //
 501   // 1.    First-up, the replicator needs to create a fixed-list of quad-tree nodes to work with.
 502   //
 503   //       To calculate this we take the largest outer-radius value set in the replicator and
 504   //       calculate how many quad-tree levels are required to achieve the selected 'mCullResolution'.
 505   //       One of the initial problems is that the replicator has seperate radii values for X & Y.
 506   //       This can lead to a culling resolution smaller in one axis than the other if there is a
 507   //       difference between the Outer-Radii.  Unfortunately, we just live with this as there is
 508   //       not much we can do here if we still want to allow the user to have this kind of
 509   //       elliptical placement control.
 510   //
 511   //       To calculate the number of nodes needed we using the following equation:-
 512   //
 513   //       Note:- We are changing the Logarithmic bases from 10 -> 2 ... grrrr!
 514   //
 515   //       Cr = mCullResolution
 516   //       Rs = Maximum Radii Diameter
 517   //
 518   //
 519   //          ( Log10( Rs / Cr )       )
 520   //       int ( ---------------- + 0.5 )
 521   //          ( Log10( 2 )             )
 522   //
 523   //             ---------|
 524   //              |
 525   //               |          n
 526   //               /         4
 527   //              /
 528   //             ---------|
 529   //                n = 0
 530   //
 531   //
 532   //       So basically we calculate the number of blocks in 1D at the highest resolution, then
 533   //       calculate the inverse exponential (base 2 - 1D) to achieve that quantity of blocks.
 534   //       We round that upto the next highest integer = e.  We then sum 4 to the power 0->e
 535   //       which gives us the correct number of nodes required.  e is also stored as the starting
 536   //       level value for populating the quad-tree (see 3. below).
 537   //
 538   // 2.    We then proceed to calculate the billboard positions as normal and calculate and assign
 539   //       each billboard a basic volume (rather than treat each as a point).  We need to take into
 540   //       account possible front/back swaying as well as the basic plane dimensions here.
 541   //       When all the billboards have been choosen we then proceed to populate the quad-tree.
 542   //
 543   // 3.    To populate the quad-tree we start with a box which completely encapsulates the volume
 544   //       occupied by all the billboards and enter into a recursive procedure to process that node.
 545   //       Processing this node involves splitting it into quadrants in X/Y untouched (for now).
 546   //       We then find candidate billboards with each of these quadrants searching using the
 547   //       current subset of shapes from the parent (this reduces the searching to a minimum and
 548   //       is very efficient).
 549   //
 550   //       If a quadrant does not enclose any billboards then the node is dropped otherwise it
 551   //       is processed again using the same procedure.
 552   //
 553   //       This happens until we have recursed through the maximum number of levels as calculated
 554   //       using the summation max (see equation above).  When level 0 is reached, the current list
 555   //       of enclosed objects is stored within the node (for the rendering algorithm).
 556   //
 557   // 4.    When this is complete we have finished here.  The next stage is when rendering takes place.
 558   //       An algorithm steps through the quad-tree from the top and does visibility culling on
 559   //       each box (with respect to the viewing frustum) and culls as appropriate.  If the box is
 560   //       visible then the next level is checked until we reach level 0 where the node contains
 561   //       a complete subset of billboards enclosed by the visible box.
 562   //
 563   //
 564   // Using the above algorithm we can now generate *massive* quantities of billboards and (using the
 565   // appropriate 'mCullResolution') only visible blocks of billboards will be processed.
 566   //
 567   // - Melv.
 568   //
 569   // ----------------------------------------------------------------------------------------------------------------------
 570
 571
 572
 573   // ----------------------------------------------------------------------------------------------------------------------
 574   // Step 1.
 575   // ----------------------------------------------------------------------------------------------------------------------
 576
 577   // Calculate the maximum dimension.
 578   F32 MaxDimension = 2.0f * ( (mFieldData.mOuterRadiusX > mFieldData.mOuterRadiusY) ? mFieldData.mOuterRadiusX : mFieldData.mOuterRadiusY );
 579
 580   // Let's check that our cull resolution is not greater than half our maximum dimension (and less than 1).
 581   if (mFieldData.mCullResolution > (MaxDimension/2) || mFieldData.mCullResolution < 8)
 582   {
 583      // Problem ...
 584      Con::warnf(ConsoleLogEntry::General, "fxFoliageReplicator - Could create Foliage, invalid Culling Resolution!");
 585      Con::warnf(ConsoleLogEntry::General, "fxFoliageReplicator - Culling Resolution *must* be >=8 or <= %0.2f!", (MaxDimension/2));
 586
 587      // Return here.
 588      return;
 589   }
 590
 591   // Take first Timestamp.
 592   F32 mStartCreationTime = (F32) Platform::getRealMilliseconds();
 593
 594   // Calculate the quad-tree levels needed for selected 'mCullResolution'.
 595   mQuadTreeLevels = (U32)(mCeil(mLog( MaxDimension / mFieldData.mCullResolution ) / mLog( 2.0f )));
 596
 597   // Calculate the number of potential nodes required.
 598   mPotentialFoliageNodes = 0;
 599   for (U32 n = 0; n <= mQuadTreeLevels; n++)
 600      mPotentialFoliageNodes += (U32)(mCeil(mPow(4.0f, (F32) n)));   // Ceil to be safe!
 601
 602   // ----------------------------------------------------------------------------------------------------------------------
 603   // Step 2.
 604   // ----------------------------------------------------------------------------------------------------------------------
 605
 606   // Set Seed.
 607   RandomGen.setSeed(mFieldData.mSeed);
 608
 609   // Add Foliage.
 610   for (U32 idx = 0; idx < mFieldData.mFoliageCount; idx++)
 611   {
 612      fxFoliageItem* pFoliageItem;
 613      Point3F        FoliageOffsetPos;
 614
 615      // Reset Relocation Retry.
 616      RelocationRetry = mFieldData.mFoliageRetries;
 617
 618      // Find it a home ...
 619      do
 620      {
 621         // Get the fxFoliageReplicator Position.
 622         FoliagePosition = getPosition();
 623
 624         // Calculate a random offset
 625         HypX  = RandomGen.randF((F32) mFieldData.mInnerRadiusX < mFieldData.mOuterRadiusX ? mFieldData.mInnerRadiusX : mFieldData.mOuterRadiusX, (F32) mFieldData.mOuterRadiusX);
 626         HypY  = RandomGen.randF((F32) mFieldData.mInnerRadiusY < mFieldData.mOuterRadiusY ? mFieldData.mInnerRadiusY : mFieldData.mOuterRadiusY, (F32) mFieldData.mOuterRadiusY);
 627         Angle = RandomGen.randF(0, (F32) M_2PI);
 628
 629         // Calcualte the new position.
 630         FoliagePosition.x += HypX * mCos(Angle);
 631         FoliagePosition.y += HypY * mSin(Angle);
 632
 633         // Initialise RayCast Search Start/End Positions.
 634         FoliageStart = FoliageEnd = FoliagePosition;
 635         FoliageStart.z = 2000.f;
 636         FoliageEnd.z= -2000.f;
 637
 638         // Perform Ray Cast Collision on Client.
 639         CollisionResult = gClientContainer.castRay(  FoliageStart, FoliageEnd, FXFOLIAGEREPLICATOR_COLLISION_MASK, &RayEvent);
 640
 641         // Did we hit anything?
 642         if (CollisionResult)
 643         {
 644            // For now, let's pretend we didn't get a collision.
 645            CollisionResult = false;
 646
 647            // Yes, so get it's type.
 648            U32 CollisionType = RayEvent.object->getTypeMask();
 649
 650            // Check Illegal Placements, fail if we hit a disallowed type.
 651            if (((CollisionType & TerrainObjectType) && !mFieldData.mAllowOnTerrain)   ||
 652               ((CollisionType & StaticShapeObjectType ) && !mFieldData.mAllowStatics) ||
 653               ((CollisionType & WaterObjectType) && !mFieldData.mAllowOnWater) ) continue;
 654
 655            // If we collided with water and are not allowing on the water surface then let's find the
 656            // terrain underneath and pass this on as the original collision else fail.
 657            if ((CollisionType & WaterObjectType) && !mFieldData.mAllowWaterSurface &&
 658               !gClientContainer.castRay( FoliageStart, FoliageEnd, FXFOLIAGEREPLICATOR_NOWATER_COLLISION_MASK, &RayEvent)) continue;
 659
 660            // We passed with flying colour so carry on.
 661            CollisionResult = true;
 662         }
 663
 664         // Invalidate if we are below Allowed Terrain Angle.
 665         if (RayEvent.normal.z < mSin(mDegToRad(90.0f-mFieldData.mAllowedTerrainSlope))) CollisionResult = false;
 666
 667         // Wait until we get a collision.
 668      } while(!CollisionResult && --RelocationRetry);
 669
 670      // Check for Relocation Problem.
 671      if (RelocationRetry > 0)
 672      {
 673         // Adjust Impact point.
 674         RayEvent.point.z += mFieldData.mOffsetZ;
 675
 676         // Set New Position.
 677         FoliagePosition = RayEvent.point;
 678      }
 679      else
 680      {
 681         // Warning.
 682         Con::warnf(ConsoleLogEntry::General, "fxFoliageReplicator - Could not find satisfactory position for Foliage!");
 683
 684         // Skip to next.
 685         continue;
 686      }
 687
 688      // Monitor the total volume.
 689      FoliageOffsetPos = FoliagePosition - getPosition();
 690      MinPoint.setMin(FoliageOffsetPos);
 691      MaxPoint.setMax(FoliageOffsetPos);
 692
 693      // Create our Foliage Item.
 694      pFoliageItem = new fxFoliageItem;
 695
 696      // Reset Frame Serial.
 697      pFoliageItem->LastFrameSerialID = 0;
 698
 699      // Reset Transform.
 700      pFoliageItem->Transform.identity();
 701
 702      // Set Position.
 703      pFoliageItem->Transform.setColumn(3, FoliagePosition);
 704
 705      // Are we fixing size @ max?
 706      if (mFieldData.mFixSizeToMax)
 707      {
 708         // Yes, so set height maximum height.
 709         pFoliageItem->Height = mFieldData.mMaxHeight;
 710         // Is the Aspect Ratio Fixed?
 711         if (mFieldData.mFixAspectRatio)
 712            // Yes, so lock to height.
 713            pFoliageItem->Width = pFoliageItem->Height;
 714         else
 715            // No, so set width to maximum width.
 716            pFoliageItem->Width = mFieldData.mMaxWidth;
 717      }
 718      else
 719      {
 720         // No, so choose a new Scale.
 721         pFoliageItem->Height = RandomGen.randF(mFieldData.mMinHeight, mFieldData.mMaxHeight);
 722         // Is the Aspect Ratio Fixed?
 723         if (mFieldData.mFixAspectRatio)
 724            // Yes, so lock to height.
 725            pFoliageItem->Width = pFoliageItem->Height;
 726         else
 727            // No, so choose a random width.
 728            pFoliageItem->Width = RandomGen.randF(mFieldData.mMinWidth, mFieldData.mMaxWidth);
 729      }
 730
 731      // Are we randomly flipping horizontally?
 732      if (mFieldData.mRandomFlip)
 733         // Yes, so choose a random flip for this object.
 734         pFoliageItem->Flipped = (RandomGen.randF(0, 1000) < 500.0f) ? false : true;
 735      else
 736         // No, so turn-off flipping.
 737         pFoliageItem->Flipped = false;      
 738
 739      // Calculate Foliage Item World Box.
 740      // NOTE:-   We generate a psuedo-volume here.  It's basically the volume to which the
 741      //       plane can move and this includes swaying!
 742      //
 743      // Is Sway On?
 744      if (mFieldData.mSwayOn)
 745      {
 746         // Yes, so take swaying into account...
 747         pFoliageItem->FoliageBox.minExtents =  FoliagePosition +
 748            Point3F(-pFoliageItem->Width / 2.0f - mFieldData.mSwayMagnitudeSide,
 749            -0.5f - mFieldData.mSwayMagnitudeFront,
 750            pFoliageItem->Height );
 751
 752         pFoliageItem->FoliageBox.maxExtents =  FoliagePosition +
 753            Point3F(+pFoliageItem->Width / 2.0f + mFieldData.mSwayMagnitudeSide,
 754            +0.5f + mFieldData.mSwayMagnitudeFront,
 755            pFoliageItem->Height );
 756      }
 757      else
 758      {
 759         // No, so give it a minimum volume...
 760         pFoliageItem->FoliageBox.minExtents =  FoliagePosition +
 761            Point3F(-pFoliageItem->Width / 2.0f,
 762            -0.5f,
 763            pFoliageItem->Height );
 764
 765         pFoliageItem->FoliageBox.maxExtents =  FoliagePosition +
 766            Point3F(+pFoliageItem->Width / 2.0f,
 767            +0.5f,
 768            pFoliageItem->Height );
 769      }
 770
 771      // Store Shape in Replicated Shapes Vector.
 772      mReplicatedFoliage.push_back(pFoliageItem);
 773
 774      // Increase Foliage Count.
 775      mCurrentFoliageCount++;
 776   }
 777
 778   // Is Lighting On?
 779   if (mFieldData.mLightOn)
 780   {
 781      // Yes, so reset Global Light phase.
 782      mGlobalLightPhase = 0.0f;
 783      // Set Global Light Time Ratio.
 784      mGlobalLightTimeRatio = PeriodLenMinus / mFieldData.mLightTime;
 785
 786      // Yes, so step through Foliage.
 787      for (U32 idx = 0; idx < mCurrentFoliageCount; idx++)
 788      {
 789         fxFoliageItem* pFoliageItem;
 790
 791         // Fetch the Foliage Item.
 792         pFoliageItem = mReplicatedFoliage[idx];
 793
 794         // Do we have an item?
 795         if (pFoliageItem)
 796         {
 797            // Yes, so are lights syncronised?
 798            if (mFieldData.mLightSync)
 799            {
 800               pFoliageItem->LightTimeRatio = 1.0f;
 801               pFoliageItem->LightPhase = 0.0f;
 802            }
 803            else
 804            {
 805               // No, so choose a random Light phase.
 806               pFoliageItem->LightPhase = RandomGen.randF(0, PeriodLenMinus);
 807               // Set Light Time Ratio.
 808               pFoliageItem->LightTimeRatio = PeriodLenMinus / mFieldData.mLightTime;
 809            }
 810         }
 811      }
 812
 813   }
 814
 815   // Is Swaying Enabled?
 816   if (mFieldData.mSwayOn)
 817   {
 818      // Yes, so reset Global Sway phase.
 819      mGlobalSwayPhase = 0.0f;
 820      // Always set Global Sway Time Ratio.
 821      mGlobalSwayTimeRatio = PeriodLenMinus / RandomGen.randF(mFieldData.mMinSwayTime, mFieldData.mMaxSwayTime);
 822
 823      // Yes, so step through Foliage.
 824      for (U32 idx = 0; idx < mCurrentFoliageCount; idx++)
 825      {        
 826         fxFoliageItem* pFoliageItem;
 827
 828         // Fetch the Foliage Item.
 829         pFoliageItem = mReplicatedFoliage[idx];
 830         // Do we have an item?
 831         if (pFoliageItem)
 832         {
 833            // Are we using Sway Sync?
 834            if (mFieldData.mSwaySync)
 835            {
 836               pFoliageItem->SwayPhase = 0;
 837               pFoliageItem->SwayTimeRatio = mGlobalSwayTimeRatio;
 838            }
 839            else
 840            {
 841               // No, so choose a random Sway phase.
 842               pFoliageItem->SwayPhase = RandomGen.randF(0, PeriodLenMinus);
 843               // Set to random Sway Time.
 844               pFoliageItem->SwayTimeRatio = PeriodLenMinus / RandomGen.randF(mFieldData.mMinSwayTime, mFieldData.mMaxSwayTime);
 845            }
 846         }
 847      }
 848   }
 849
 850   // Update our Object Volume.
 851   mObjBox.minExtents.set(MinPoint);
 852   mObjBox.maxExtents.set(MaxPoint);
 853   setTransform(mObjToWorld);
 854
 855   // ----------------------------------------------------------------------------------------------------------------------
 856   // Step 3.
 857   // ----------------------------------------------------------------------------------------------------------------------
 858
 859   // Reset Next Allocated Node to Stack base.
 860   mNextAllocatedNodeIdx = 0;
 861
 862   // Allocate a new Node.
 863   fxFoliageQuadrantNode* pNewNode = new fxFoliageQuadrantNode;
 864
 865   // Store it in the Quad-tree.
 866   mFoliageQuadTree.push_back(pNewNode);
 867
 868   // Populate Initial Node.
 869   //
 870   // Set Start Level.
 871   pNewNode->Level = mQuadTreeLevels;
 872   // Calculate Total Foliage Area.
 873   pNewNode->QuadrantBox = getWorldBox();
 874   // Reset Quadrant child nodes.
 875   pNewNode->QuadrantChildNode[0] =
 876      pNewNode->QuadrantChildNode[1] =
 877      pNewNode->QuadrantChildNode[2] =
 878      pNewNode->QuadrantChildNode[3] = NULL;
 879
 880   // Create our initial cull list with *all* billboards into.
 881   fxFoliageCulledList CullList;
 882   CullList.mCulledObjectSet = mReplicatedFoliage;
 883
 884   // Move to next node Index.
 885   mNextAllocatedNodeIdx++;
 886
 887   // Let's start this thing going by recursing it's children.
 888   ProcessNodeChildren(pNewNode, &CullList);
 889
 890   // Calculate Elapsed Time and take new Timestamp.
 891   F32 ElapsedTime = (Platform::getRealMilliseconds() - mStartCreationTime) * 0.001f;
 892
 893   // Console Output.
 894   Con::printf("fxFoliageReplicator - Lev: %d  PotNodes: %d  Used: %d  Objs: %d  Time: %0.4fs.",
 895      mQuadTreeLevels,
 896      mPotentialFoliageNodes,
 897      mNextAllocatedNodeIdx-1,
 898      mBillboardsAcquired,
 899      ElapsedTime);
 900
 901   // Dump (*very*) approximate allocated memory.
 902   F32 MemoryAllocated = (F32) ((mNextAllocatedNodeIdx-1) * sizeof(fxFoliageQuadrantNode));
 903   MemoryAllocated      += mCurrentFoliageCount * sizeof(fxFoliageItem);
 904   MemoryAllocated      += mCurrentFoliageCount * sizeof(fxFoliageItem*);
 905   Con::printf("fxFoliageReplicator - Approx. %0.2fMb allocated.", MemoryAllocated / 1048576.0f);
 906
 907   // ----------------------------------------------------------------------------------------------------------------------
 908
 909   SetupBuffers();
 910
 911   // Take first Timestamp.
 912   mLastRenderTime = Platform::getVirtualMilliseconds();
 913}
 914
 915void fxFoliageReplicator::SetupShader()
 916{
 917   if ( !mShaderData )
 918   {
 919      if ( !Sim::findObject( "fxFoliageReplicatorShader", mShaderData ) )
 920      {
 921         Con::errorf( "fxFoliageReplicator::SetupShader - could not find ShaderData named fxFoliageReplicatorShader" );
 922         return;
 923      }
 924   }
 925
 926   Vector<GFXShaderMacro> macros;
 927   if ( mFieldData.mUseTrueBillboards )   
 928      macros.push_back( GFXShaderMacro( "TRUE_BILLBOARD" ) );
 929   
 930   mShader = mShaderData->getShader( macros );
 931
 932   if ( !mShader )
 933      return;
 934
 935   
 936   mFoliageShaderConsts = mShader->allocConstBuffer();
 937
 938   mFoliageShaderProjectionSC          = mShader->getShaderConstHandle( "$projection" );
 939   mFoliageShaderWorldSC               = mShader->getShaderConstHandle( "$world" );
 940   mFoliageShaderGlobalSwayPhaseSC     = mShader->getShaderConstHandle( "$GlobalSwayPhase" );
 941   mFoliageShaderSwayMagnitudeSideSC   = mShader->getShaderConstHandle( "$SwayMagnitudeSide" );
 942   mFoliageShaderSwayMagnitudeFrontSC  = mShader->getShaderConstHandle( "$SwayMagnitudeFront" );
 943   mFoliageShaderGlobalLightPhaseSC    = mShader->getShaderConstHandle( "$GlobalLightPhase" );
 944   mFoliageShaderLuminanceMagnitudeSC  = mShader->getShaderConstHandle( "$LuminanceMagnitude" );
 945   mFoliageShaderLuminanceMidpointSC   = mShader->getShaderConstHandle( "$LuminanceMidpoint" );
 946   mFoliageShaderDistanceRangeSC       = mShader->getShaderConstHandle( "$DistanceRange" );
 947   mFoliageShaderCameraPosSC           = mShader->getShaderConstHandle( "$CameraPos" );
 948   mFoliageShaderTrueBillboardSC       = mShader->getShaderConstHandle( "$TrueBillboard" );
 949   mFoliageShaderGroundAlphaSC         = mShader->getShaderConstHandle( "$groundAlpha" );
 950   mFoliageShaderAmbientColorSC        = mShader->getShaderConstHandle( "$ambient" );     
 951   mDiffuseTextureSC                   = mShader->getShaderConstHandle( "$diffuseMap" );
 952   mAlphaMapTextureSC                  = mShader->getShaderConstHandle( "$alphaMap" ); 
 953}
 954
 955// Ok, what we do is let the older code setup the FoliageItem list and the QuadTree.
 956// Then we build the Vertex and Primitive buffers here.  It would probably be
 957// slightly more memory efficient to build the buffers directly, but we
 958// want to sort the items within the buffer by the quadtreenodes
 959void fxFoliageReplicator::SetupBuffers()
 960{
 961   // Following two arrays are used to build the vertex and primitive buffers.   
 962   Point3F basePoints[8];
 963   basePoints[0] = Point3F(-0.5f, 0.0f, 1.0f);
 964   basePoints[1] = Point3F(-0.5f, 0.0f, 0.0f);
 965   basePoints[2] = Point3F(0.5f, 0.0f, 0.0f);
 966   basePoints[3] = Point3F(0.5f, 0.0f, 1.0f);
 967
 968   Point2F texCoords[4];
 969   texCoords[0] = Point2F(0.0, 0.0);
 970   texCoords[1] = Point2F(0.0, 1.0);
 971   texCoords[2] = Point2F(1.0, 1.0);
 972   texCoords[3] = Point2F(1.0, 0.0);   
 973
 974   // Init our Primitive Buffer
 975   U32 indexSize = mFieldData.mFoliageCount * 6;
 976   U16* indices = new U16[indexSize];
 977   // Two triangles per particle
 978   for (U16 i = 0; i < mFieldData.mFoliageCount; i++) {
 979      U16* idx = &indices[i*6];     // hey, no offset math below, neat
 980      U16 vertOffset = i*4;
 981      idx[0] = vertOffset + 0;
 982      idx[1] = vertOffset + 1;
 983      idx[2] = vertOffset + 2;
 984      idx[3] = vertOffset + 2;
 985      idx[4] = vertOffset + 3;
 986      idx[5] = vertOffset + 0;
 987   }
 988   // Init the prim buffer and copy our indexes over
 989   U16 *ibIndices;
 990   mPrimBuffer.set(GFX, indexSize, 0, GFXBufferTypeStatic);
 991   mPrimBuffer.lock(&ibIndices);
 992   dMemcpy(ibIndices, indices, indexSize * sizeof(U16));
 993   mPrimBuffer.unlock();
 994   delete[] indices;
 995
 996   // Now, let's init the vertex buffer
 997   U32 currPrimitiveStartIndex = 0;
 998   mVertexBuffer.set(GFX, mFieldData.mFoliageCount * 4, GFXBufferTypeStatic);
 999   mVertexBuffer.lock();
1000   U32 idx = 0;   
1001   for (S32 qtIdx = 0; qtIdx < mFoliageQuadTree.size(); qtIdx++) {
1002      fxFoliageQuadrantNode* quadNode = mFoliageQuadTree[qtIdx];
1003      if (quadNode->Level == 0) {
1004         quadNode->startIndex = currPrimitiveStartIndex;
1005         quadNode->primitiveCount = 0;
1006         // Ok, there should be data in here!
1007         for (S32 i = 0; i < quadNode->RenderList.size(); i++) {
1008            fxFoliageItem* pFoliageItem = quadNode->RenderList[i];
1009            if (pFoliageItem->LastFrameSerialID == 0) {
1010               pFoliageItem->LastFrameSerialID++;
1011               // Dump it into the vertex buffer
1012               for (U32 vertIndex = 0; vertIndex < 4; vertIndex++) {
1013                  GFXVertexFoliage *vert = &mVertexBuffer[(idx*4) + vertIndex];
1014                  // This is the position of the billboard.
1015                  vert->point = pFoliageItem->Transform.getPosition();        
1016                  // Normal contains the point of the billboard (except for the y component, see below)
1017                  vert->normal = basePoints[vertIndex];
1018
1019                  vert->normal.x *= pFoliageItem->Width;
1020                  vert->normal.z *= pFoliageItem->Height;
1021                  // Handle texture coordinates
1022                  vert->texCoord = texCoords[vertIndex];          
1023                  if (pFoliageItem->Flipped)
1024                     vert->texCoord.x = 1.0f - vert->texCoord.x;
1025                  // Handle sway. Sway is stored in a texture coord. The x coordinate is the sway phase multiplier, 
1026                  // the y coordinate determines if this vertex actually sways or not.
1027                  if ((vertIndex == 0) || (vertIndex == 3)) {
1028                     vert->texCoord2.set(pFoliageItem->SwayTimeRatio / mGlobalSwayTimeRatio, 1.0f);
1029                  } else {
1030                     vert->texCoord2.set(0.0f, 0.0f);
1031                  }
1032                  // Handle lighting, lighting happens at the same time as global so this is just an offset.
1033                  vert->normal.y = pFoliageItem->LightPhase;
1034               }
1035               idx++;
1036               quadNode->primitiveCount += 2;
1037               currPrimitiveStartIndex += 6; 
1038            }
1039         }
1040      }
1041   }
1042   mVertexBuffer.unlock(); 
1043
1044   DestroyFoliageItems();
1045}
1046
1047//------------------------------------------------------------------------------
1048
1049Box3F fxFoliageReplicator::FetchQuadrant(const Box3F& Box, U32 Quadrant)
1050{
1051   Box3F QuadrantBox;
1052
1053   // Select Quadrant.
1054   switch(Quadrant)
1055   {
1056      // UL.
1057   case 0:
1058      QuadrantBox.minExtents = Box.minExtents + Point3F(0, Box.len_y()/2, 0);
1059      QuadrantBox.maxExtents = QuadrantBox.minExtents + Point3F(Box.len_x()/2, Box.len_y()/2, Box.len_z());
1060      break;
1061
1062      // UR.
1063   case 1:
1064      QuadrantBox.minExtents = Box.minExtents + Point3F(Box.len_x()/2, Box.len_y()/2, 0);
1065      QuadrantBox.maxExtents = QuadrantBox.minExtents + Point3F(Box.len_x()/2, Box.len_y()/2, Box.len_z());
1066      break;
1067
1068      // LL.
1069   case 2:
1070      QuadrantBox.minExtents = Box.minExtents;
1071      QuadrantBox.maxExtents = QuadrantBox.minExtents + Point3F(Box.len_x()/2, Box.len_y()/2, Box.len_z());
1072      break;
1073
1074      // LR.
1075   case 3:
1076      QuadrantBox.minExtents = Box.minExtents + Point3F(Box.len_x()/2, 0, 0);
1077      QuadrantBox.maxExtents = QuadrantBox.minExtents + Point3F(Box.len_x()/2, Box.len_y()/2, Box.len_z());
1078      break;
1079
1080   default:
1081      return Box;
1082   }
1083
1084   return QuadrantBox;
1085}
1086
1087//------------------------------------------------------------------------------
1088
1089void fxFoliageReplicator::ProcessNodeChildren(fxFoliageQuadrantNode* pParentNode, fxFoliageCulledList* pCullList)
1090{
1091   // ---------------------------------------------------------------
1092   // Split Node into Quadrants and Process each.
1093   // ---------------------------------------------------------------
1094
1095   // Process All Quadrants (UL/UR/LL/LR).
1096   for (U32 q = 0; q < 4; q++)
1097      ProcessQuadrant(pParentNode, pCullList, q);
1098}
1099
1100//------------------------------------------------------------------------------
1101
1102void fxFoliageReplicator::ProcessQuadrant(fxFoliageQuadrantNode* pParentNode, fxFoliageCulledList* pCullList, U32 Quadrant)
1103{
1104   // Fetch Quadrant Box.
1105   const Box3F QuadrantBox = FetchQuadrant(pParentNode->QuadrantBox, Quadrant);
1106
1107   // Create our new Cull List.
1108   fxFoliageCulledList CullList(QuadrantBox, pCullList);
1109
1110   // Did we get any objects?
1111   if (CullList.GetListCount() > 0)
1112   {
1113      // Yes, so allocate a new Node.
1114      fxFoliageQuadrantNode* pNewNode = new fxFoliageQuadrantNode;
1115
1116      // Store it in the Quad-tree.
1117      mFoliageQuadTree.push_back(pNewNode);
1118
1119      // Move to next node Index.
1120      mNextAllocatedNodeIdx++;
1121
1122      // Populate Quadrant Node.
1123      //
1124      // Next Sub-level.
1125      pNewNode->Level = pParentNode->Level - 1;
1126      // Calculate Quadrant Box.
1127      pNewNode->QuadrantBox = QuadrantBox;
1128      // Reset Child Nodes.
1129      pNewNode->QuadrantChildNode[0] =
1130         pNewNode->QuadrantChildNode[1] =
1131         pNewNode->QuadrantChildNode[2] =
1132         pNewNode->QuadrantChildNode[3] = NULL;
1133
1134      // Put a reference in parent.
1135      pParentNode->QuadrantChildNode[Quadrant] = pNewNode;
1136
1137      // If we're not at sub-level 0 then process this nodes children.
1138      if (pNewNode->Level != 0) ProcessNodeChildren(pNewNode, &CullList);
1139      // If we've reached sub-level 0 then store Cull List (for rendering).
1140      if (pNewNode->Level == 0)
1141      {
1142         // Store the render list from our culled object set.
1143         pNewNode->RenderList = CullList.mCulledObjectSet;
1144         // Keep track of the total billboard acquired.
1145         mBillboardsAcquired += CullList.GetListCount();
1146      }
1147   }
1148}
1149
1150//------------------------------------------------------------------------------
1151
1152void fxFoliageReplicator::SyncFoliageReplicators(void)
1153{
1154   // Check Host.
1155   AssertFatal(isServerObject(), "We *MUST* be on server when Synchronising Foliage!");
1156
1157      // Find the Replicator Set.
1158   SimSet *fxFoliageSet = dynamic_cast<SimSet*>(Sim::findObject("fxFoliageSet"));
1159
1160   // Return if Error.
1161   if (!fxFoliageSet)
1162   {
1163      // Console Warning.
1164      Con::warnf("fxFoliageReplicator - Cannot locate the 'fxFoliageSet', this is bad!");
1165      // Return here.
1166      return;
1167   }
1168
1169   // Parse Replication Object(s).
1170   for (SimSetIterator itr(fxFoliageSet); *itr; ++itr)
1171   {
1172      // Fetch the Replicator Object.
1173      fxFoliageReplicator* Replicator = static_cast<fxFoliageReplicator*>(*itr);
1174      // Set Foliage Replication Mask.
1175      if (Replicator->isServerObject())
1176      {
1177         Con::printf("fxFoliageReplicator - Restarting fxFoliageReplicator Object...");
1178         Replicator->setMaskBits(FoliageReplicationMask);
1179      }
1180   }
1181
1182   // Info ...
1183   Con::printf("fxFoliageReplicator - Client Foliage Sync has completed.");
1184}
1185
1186
1187//------------------------------------------------------------------------------
1188// Lets chill our memory requirements out a little
1189void fxFoliageReplicator::DestroyFoliageItems()
1190{
1191   // Remove shapes.
1192   for (S32 idx = 0; idx < mReplicatedFoliage.size(); idx++)
1193   {
1194      fxFoliageItem* pFoliageItem;
1195
1196      // Fetch the Foliage Item.
1197      pFoliageItem = mReplicatedFoliage[idx];
1198
1199      // Delete Shape.
1200      if (pFoliageItem) delete pFoliageItem;
1201   }
1202   // Clear the Replicated Foliage Vector.
1203   mReplicatedFoliage.clear();
1204
1205   // Clear out old references also
1206   for (S32 qtIdx = 0; qtIdx < mFoliageQuadTree.size(); qtIdx++) {
1207      fxFoliageQuadrantNode* quadNode = mFoliageQuadTree[qtIdx];
1208      if (quadNode->Level == 0) {
1209         quadNode->RenderList.clear();
1210      }
1211   }
1212}
1213
1214void fxFoliageReplicator::DestroyFoliage(void)
1215{
1216   // Check Host.
1217   AssertFatal(isClientObject(), "Trying to destroy Foliage on Server, this is bad!");
1218
1219      // Destroy Quad-tree.
1220      mPotentialFoliageNodes = 0;
1221   // Reset Billboards Acquired.
1222   mBillboardsAcquired = 0;
1223
1224   // Finish if we didn't create any shapes.
1225   if (mCurrentFoliageCount == 0) return;
1226
1227   DestroyFoliageItems();
1228
1229   // Let's remove the Quad-Tree allocations.
1230   for ( Vector<fxFoliageQuadrantNode*>::iterator QuadNodeItr = mFoliageQuadTree.begin();
1231      QuadNodeItr != mFoliageQuadTree.end();
1232      QuadNodeItr++ )
1233   {
1234      // Remove the node.
1235      delete *QuadNodeItr;
1236   }
1237
1238   // Clear the Foliage Quad-Tree Vector.
1239   mFoliageQuadTree.clear();
1240
1241   // Clear the Frustum Render Set Vector.
1242   mFrustumRenderSet.mVisObjectSet.clear();
1243
1244   // Reset Foliage Count.
1245   mCurrentFoliageCount = 0;
1246}
1247
1248//------------------------------------------------------------------------------
1249
1250void fxFoliageReplicator::StartUp(void)
1251{
1252   // Flag, Client Replication Started.
1253   mClientReplicationStarted = true;
1254
1255   // Create foliage on Client.
1256   if (isClientObject()) CreateFoliage();
1257}
1258
1259//------------------------------------------------------------------------------
1260
1261bool fxFoliageReplicator::onAdd()
1262{
1263   if(!Parent::onAdd()) return(false);
1264
1265   // Add the Replicator to the Replicator Set.
1266   dynamic_cast<SimSet*>(Sim::findObject("fxFoliageSet"))->addObject(this);
1267
1268   // Set Default Object Box.
1269   mObjBox.minExtents.set( -0.5, -0.5, -0.5 );
1270   mObjBox.maxExtents.set(  0.5,  0.5,  0.5 );
1271   resetWorldBox();
1272   setRenderTransform(mObjToWorld);
1273
1274   // Add to Scene.
1275   addToScene();
1276
1277   // Are we on the client?
1278   if ( isClientObject() )
1279   {
1280      // Yes, so load foliage texture.
1281      if( mFieldData.mFoliageFile != NULL && dStrlen(mFieldData.mFoliageFile) > 0 )
1282         mFieldData.mFoliageTexture = GFXTexHandle( mFieldData.mFoliageFile, &GFXStaticTextureSRGBProfile, avar("%s() - mFieldData.mFoliageTexture (line %d)", __FUNCTION__, __LINE__) );
1283
1284      if ((GFXTextureObject*) mFieldData.mFoliageTexture == NULL)
1285         Con::printf("fxFoliageReplicator:  %s is an invalid or missing foliage texture file.", mFieldData.mFoliageFile);
1286
1287      mAlphaLookup = new GBitmap(AlphaTexLen, 1);
1288      computeAlphaTex();
1289
1290      // Register for notification when GhostAlways objects are done loading
1291      NetConnection::smGhostAlwaysDone.notify( this, &fxFoliageReplicator::onGhostAlwaysDone );
1292
1293      SetupShader();
1294   }
1295
1296   // Return OK.
1297   return(true);
1298}
1299
1300//------------------------------------------------------------------------------
1301
1302void fxFoliageReplicator::onRemove()
1303{
1304   // Remove the Replicator from the Replicator Set.
1305   dynamic_cast<SimSet*>(Sim::findObject("fxFoliageSet"))->removeObject(this);
1306
1307   NetConnection::smGhostAlwaysDone.remove( this, &fxFoliageReplicator::onGhostAlwaysDone );
1308
1309   // Remove from Scene.
1310   removeFromScene();
1311
1312   // Are we on the Client?
1313   if (isClientObject())
1314   {
1315      // Yes, so destroy Foliage.
1316      DestroyFoliage();
1317
1318      // Remove Texture.
1319      mFieldData.mFoliageTexture = NULL;
1320
1321      mShader = NULL;
1322   }
1323
1324   // Do Parent.
1325   Parent::onRemove();
1326}
1327
1328//------------------------------------------------------------------------------
1329
1330void fxFoliageReplicator::onGhostAlwaysDone()
1331{
1332   if ( isClientObject() )
1333      CreateFoliage();
1334}
1335
1336//------------------------------------------------------------------------------
1337
1338void fxFoliageReplicator::inspectPostApply()
1339{
1340   // Set Parent.
1341   Parent::inspectPostApply();
1342
1343   // Set Foliage Replication Mask (this object only).
1344   setMaskBits(FoliageReplicationMask);
1345
1346   mDirty = true;
1347}
1348
1349//------------------------------------------------------------------------------
1350
1351DefineEngineFunction(StartFoliageReplication, void,(),, "Activates the foliage replicator.\n"
1352                                       "@tsexample\n"
1353                                          "// Call the function\n"
1354                                          "StartFoliageReplication();\n"
1355                                       "@endtsexample\n"
1356                                       "@ingroup Foliage")
1357{
1358   // Find the Replicator Set.
1359   SimSet *fxFoliageSet = dynamic_cast<SimSet*>(Sim::findObject("fxFoliageSet"));
1360
1361   // Return if Error.
1362   if (!fxFoliageSet)
1363   {
1364      // Console Warning.
1365      Con::warnf("fxFoliageReplicator - Cannot locate the 'fxFoliageSet', this is bad!");
1366      // Return here.
1367      return;
1368   }
1369
1370   // Parse Replication Object(s).
1371   U32 startupCount = 0;
1372   for (SimSetIterator itr(fxFoliageSet); *itr; ++itr)
1373   {
1374      // Fetch the Replicator Object.
1375      fxFoliageReplicator* Replicator = static_cast<fxFoliageReplicator*>(*itr);
1376
1377      // Start Client Objects Only.
1378      if (Replicator->isClientObject())
1379      {
1380         Replicator->StartUp();
1381         startupCount++;
1382      }
1383   }
1384
1385   // Info ...
1386   Con::printf("fxFoliageReplicator - replicated client foliage for %d objects", startupCount);
1387}
1388
1389//------------------------------------------------------------------------------
1390
1391void fxFoliageReplicator::prepRenderImage( SceneRenderState* state )
1392{
1393   ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
1394   ri->renderDelegate.bind(this, &fxFoliageReplicator::renderObject);
1395   ri->type = RenderPassManager::RIT_Foliage;
1396   state->getRenderPass()->addInst( ri );
1397}
1398
1399//
1400// RENDERING
1401//
1402void fxFoliageReplicator::computeAlphaTex()
1403{
1404   // Distances used in alpha
1405   const F32   ClippedViewDistance     = mFieldData.mViewDistance;
1406   const F32   MaximumViewDistance     = ClippedViewDistance + mFieldData.mFadeInRegion;
1407
1408   // This is used for the alpha computation in the shader.
1409   for (U32 i = 0; i < AlphaTexLen; i++) {
1410      F32 Distance = ((float) i / (float) AlphaTexLen) * MaximumViewDistance;
1411      F32 ItemAlpha = 1.0f;
1412      // Are we fading out?
1413      if (Distance < mFieldData.mViewClosest)
1414      {
1415         // Yes, so set fade-out.
1416         ItemAlpha = 1.0f - ((mFieldData.mViewClosest - Distance) * mFadeOutGradient);
1417      }
1418      // No, so are we fading in?
1419      else if (Distance > ClippedViewDistance)
1420      {
1421         // Yes, so set fade-in
1422         ItemAlpha = 1.0f - ((Distance - ClippedViewDistance) * mFadeInGradient);
1423      }
1424
1425      // Set texture info
1426      ColorI c((U8) (255.0f * ItemAlpha), 0, 0);
1427      mAlphaLookup->setColor(i, 0, c);
1428   }
1429   mAlphaTexture.set(mAlphaLookup, &GFXStaticTextureSRGBProfile, false, String("fxFoliage Replicator Alpha Texture") );
1430}
1431
1432// Renders a triangle stripped oval
1433void fxFoliageReplicator::renderArc(const F32 fRadiusX, const F32 fRadiusY)
1434{
1435   PrimBuild::begin(GFXTriangleStrip, 720);
1436   for (U32 Angle = mCreationAreaAngle; Angle < (mCreationAreaAngle+360); Angle++)
1437   {
1438      F32      XPos, YPos;
1439
1440      // Calculate Position.
1441      XPos = fRadiusX * mCos(mDegToRad(-(F32)Angle));
1442      YPos = fRadiusY * mSin(mDegToRad(-(F32)Angle));
1443
1444      // Set Colour.
1445      PrimBuild::color4f(mFieldData.mPlaceAreaColour.red,
1446         mFieldData.mPlaceAreaColour.green,
1447         mFieldData.mPlaceAreaColour.blue,
1448         AREA_ANIMATION_ARC * (Angle-mCreationAreaAngle));
1449
1450      PrimBuild::vertex3f(XPos, YPos, -(F32)mFieldData.mPlacementBandHeight/2.0f);
1451      PrimBuild::vertex3f(XPos, YPos, +(F32)mFieldData.mPlacementBandHeight/2.0f);
1452   }        
1453   PrimBuild::end();
1454}
1455
1456// This currently uses the primbuilder, could convert out, but why allocate the buffer if we
1457// never edit the misison?
1458void fxFoliageReplicator::renderPlacementArea(const F32 ElapsedTime)
1459{
1460   if (gEditingMission && mFieldData.mShowPlacementArea)
1461   {
1462      GFX->pushWorldMatrix();
1463      GFX->multWorld(getTransform());
1464
1465      if (!mPlacementSB)
1466      {
1467         GFXStateBlockDesc transparent;
1468         transparent.setCullMode(GFXCullNone);
1469         transparent.alphaTestEnable = true;
1470         transparent.setZReadWrite(true);
1471         transparent.zWriteEnable = false;
1472         transparent.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
1473         mPlacementSB = GFX->createStateBlock( transparent );
1474      }
1475
1476      GFX->setStateBlock(mPlacementSB);
1477
1478      // Do we need to draw the Outer Radius?
1479      if (mFieldData.mOuterRadiusX || mFieldData.mOuterRadiusY)
1480         renderArc((F32) mFieldData.mOuterRadiusX, (F32) mFieldData.mOuterRadiusY);
1481      // Inner radius?
1482      if (mFieldData.mInnerRadiusX || mFieldData.mInnerRadiusY)
1483         renderArc((F32) mFieldData.mInnerRadiusX, (F32) mFieldData.mInnerRadiusY);
1484
1485      GFX->popWorldMatrix();
1486      mCreationAreaAngle = (U32)(mCreationAreaAngle + (1000 * ElapsedTime));
1487      mCreationAreaAngle = mCreationAreaAngle % 360;
1488   }
1489}
1490
1491void fxFoliageReplicator::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat)
1492{
1493   if (overrideMat)
1494      return;
1495
1496   if ( !mShader )
1497      return;
1498
1499   // If we're rendering and we haven't placed any foliage yet - do it.
1500   if(!mClientReplicationStarted)
1501   {
1502      Con::warnf("fxFoliageReplicator::renderObject - tried to render a non replicated fxFoliageReplicator; replicating it now...");
1503
1504      StartUp();
1505   }
1506
1507   // Calculate Elapsed Time and take new Timestamp.
1508   S32 Time = Platform::getVirtualMilliseconds();
1509   F32 ElapsedTime = (Time - mLastRenderTime) * 0.001f;
1510   mLastRenderTime = Time; 
1511
1512   renderPlacementArea(ElapsedTime);
1513
1514   if (mCurrentFoliageCount > 0) {
1515
1516      if ( mRenderSB.isNull() || mDirty)
1517      {
1518         mDirty = false;
1519
1520         GFXStateBlockDesc desc;
1521
1522         // Debug SB
1523         desc.samplersDefined = true;
1524
1525         mDebugSB = GFX->createStateBlock(desc);
1526
1527         // Render SB
1528         desc.samplers[1].addressModeU = GFXAddressClamp;
1529         desc.samplers[1].addressModeV = GFXAddressClamp;
1530
1531         desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
1532         desc.setAlphaTest(true, GFXCmpGreater, (U8) (255.0f * mFieldData.mAlphaCutoff));
1533         desc.setCullMode(GFXCullNone);
1534
1535         mRenderSB = GFX->createStateBlock(desc);
1536      }
1537
1538      if (!mFieldData.mHideFoliage) {
1539         // Animate Global Sway Phase (Modulus).  
1540         mGlobalSwayPhase = mGlobalSwayPhase + (mGlobalSwayTimeRatio * ElapsedTime);
1541
1542         // Animate Global Light Phase (Modulus).
1543         mGlobalLightPhase = mGlobalLightPhase + (mGlobalLightTimeRatio * ElapsedTime);
1544
1545         // Compute other light parameters
1546         const F32   LuminanceMidPoint    = (mFieldData.mMinLuminance + mFieldData.mMaxLuminance) / 2.0f;
1547         const F32   LuminanceMagnitude      = mFieldData.mMaxLuminance - LuminanceMidPoint;
1548
1549         // Distances used in alpha
1550         const F32   ClippedViewDistance     = mFieldData.mViewDistance;
1551         const F32   MaximumViewDistance     = ClippedViewDistance + mFieldData.mFadeInRegion;
1552
1553         if (mFoliageShaderConsts.isValid())
1554         {
1555            mFoliageShaderConsts->setSafe(mFoliageShaderGlobalSwayPhaseSC, mGlobalSwayPhase);
1556            mFoliageShaderConsts->setSafe(mFoliageShaderSwayMagnitudeSideSC, mFieldData.mSwayMagnitudeSide);
1557            mFoliageShaderConsts->setSafe(mFoliageShaderSwayMagnitudeFrontSC, mFieldData.mSwayMagnitudeFront);
1558            mFoliageShaderConsts->setSafe(mFoliageShaderGlobalLightPhaseSC, mGlobalLightPhase);
1559            mFoliageShaderConsts->setSafe(mFoliageShaderLuminanceMagnitudeSC, LuminanceMagnitude);
1560            mFoliageShaderConsts->setSafe(mFoliageShaderLuminanceMidpointSC, LuminanceMidPoint);
1561
1562            // Set up our shader constants   
1563            // Projection matrix
1564            MatrixF proj = GFX->getProjectionMatrix();
1565            //proj.transpose();
1566            mFoliageShaderConsts->setSafe(mFoliageShaderProjectionSC, proj);
1567
1568            // World transform matrix
1569            MatrixF world = GFX->getWorldMatrix();
1570            //world.transpose();
1571
1572            mFoliageShaderConsts->setSafe(mFoliageShaderWorldSC, world);
1573
1574            Point3F camPos = state->getCameraPosition();
1575
1576            mFoliageShaderConsts->setSafe(mFoliageShaderDistanceRangeSC, MaximumViewDistance);
1577            mFoliageShaderConsts->setSafe(mFoliageShaderCameraPosSC, camPos);
1578            mFoliageShaderConsts->setSafe(mFoliageShaderTrueBillboardSC, mFieldData.mUseTrueBillboards ? 1.0f : 0.0f );
1579            mFoliageShaderConsts->setSafe(mFoliageShaderGroundAlphaSC, Point4F(mFieldData.mGroundAlpha, mFieldData.mGroundAlpha, mFieldData.mGroundAlpha, mFieldData.mGroundAlpha));
1580
1581            if (mFoliageShaderAmbientColorSC->isValid())
1582            {
1583               LinearColorF ambient = state->getAmbientLightColor();
1584               LinearColorF ambient_inv(1.0f-ambient.red, 1.0f-ambient.green, 1.0f-ambient.blue, 0.0f);
1585               ambient += ambient_inv*(1.0f - mFieldData.mAmbientModulationBias);
1586               mFoliageShaderConsts->set(mFoliageShaderAmbientColorSC, ambient);
1587            }
1588
1589            GFX->setShaderConstBuffer(mFoliageShaderConsts);
1590
1591         }
1592
1593         // Blend ops
1594         // Set up our texture and color ops.
1595
1596         GFX->setStateBlock(mRenderSB);
1597         GFX->setShader( mShader );
1598
1599         GFX->setTexture(mDiffuseTextureSC->getSamplerRegister(), mFieldData.mFoliageTexture);
1600         // computeAlphaTex();      // Uncomment if we figure out how to clamp to fogAndHaze
1601         GFX->setTexture(mAlphaMapTextureSC->getSamplerRegister(), mAlphaTexture);
1602
1603         // Setup our buffers
1604         GFX->setVertexBuffer(mVertexBuffer);
1605         GFX->setPrimitiveBuffer(mPrimBuffer);
1606
1607         // If we use culling, we're going to send chunks of our buffers to the card
1608         if (mFieldData.mUseCulling)
1609         {
1610            // Setup the Clip-Planes.
1611            F32 FarClipPlane = getMin((F32)state->getFarPlane(), 
1612               mFieldData.mViewDistance + mFieldData.mFadeInRegion);
1613            mFrustumRenderSet.SetupClipPlanes(state, FarClipPlane);
1614
1615            renderQuad(mFoliageQuadTree[0], getRenderTransform(), false);
1616
1617            // Multipass, don't want to interrupt the vb state 
1618            if (mFieldData.mUseDebugInfo) 
1619            {
1620               // hey man, we're done, so it doesn't matter if we kill it to render the next part
1621               GFX->setStateBlock(mDebugSB);
1622               renderQuad(mFoliageQuadTree[0], getRenderTransform(), true);
1623            }
1624         }
1625         else 
1626         {  
1627            // Draw the whole shebang!
1628            GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, mVertexBuffer->mNumVerts, 
1629               0, mPrimBuffer->mIndexCount / 3);
1630         }
1631      }
1632   }
1633}
1634
1635void fxFoliageReplicator::renderQuad(fxFoliageQuadrantNode* quadNode, const MatrixF& RenderTransform, const bool UseDebug)
1636{
1637   if (quadNode != NULL) {
1638      if (mFrustumRenderSet.IsQuadrantVisible(quadNode->QuadrantBox, RenderTransform))
1639      {
1640         // Draw the Quad Box (Debug Only).
1641         if (UseDebug) 
1642            mFrustumRenderSet.DrawQuadBox(quadNode->QuadrantBox, LinearColorF(0.0f, 1.0f, 0.1f, 1.0f));
1643         if (quadNode->Level != 0) {
1644            for (U32 i = 0; i < 4; i++)
1645               renderQuad(quadNode->QuadrantChildNode[i], RenderTransform, UseDebug);
1646         } else {
1647            if (!UseDebug)
1648               if(quadNode->primitiveCount)
1649                  GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, mVertexBuffer->mNumVerts, 
1650                  quadNode->startIndex, quadNode->primitiveCount);
1651         }
1652      } else {
1653         // Use a different color to say "I think I'm not visible!"
1654         if (UseDebug) 
1655            mFrustumRenderSet.DrawQuadBox(quadNode->QuadrantBox, LinearColorF(1.0f, 0.8f, 0.1f, 1.0f));
1656      }
1657   }
1658}
1659
1660//------------------------------------------------------------------------------
1661// NETWORK
1662//------------------------------------------------------------------------------
1663
1664U32 fxFoliageReplicator::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
1665{
1666   // Pack Parent.
1667   U32 retMask = Parent::packUpdate(con, mask, stream);
1668
1669   // Write Foliage Replication Flag.
1670   if (stream->writeFlag(mask & FoliageReplicationMask))
1671   {
1672      stream->writeAffineTransform(mObjToWorld);                  // Foliage Master-Object Position.
1673
1674      stream->writeFlag(mFieldData.mUseDebugInfo);             // Foliage Debug Information Flag.
1675      stream->write(mFieldData.mDebugBoxHeight);                  // Foliage Debug Height.
1676      stream->write(mFieldData.mSeed);                      // Foliage Seed.
1677      stream->write(mFieldData.mFoliageCount);                 // Foliage Count.
1678      stream->write(mFieldData.mFoliageRetries);                  // Foliage Retries.
1679      stream->writeString(mFieldData.mFoliageFile);               // Foliage File.
1680
1681      stream->write(mFieldData.mInnerRadiusX);                 // Foliage Inner Radius X.
1682      stream->write(mFieldData.mInnerRadiusY);                 // Foliage Inner Radius Y.
1683      stream->write(mFieldData.mOuterRadiusX);                 // Foliage Outer Radius X.
1684      stream->write(mFieldData.mOuterRadiusY);                 // Foliage Outer Radius Y.
1685
1686      stream->write(mFieldData.mMinWidth);                     // Foliage Minimum Width.
1687      stream->write(mFieldData.mMaxWidth);                     // Foliage Maximum Width.
1688      stream->write(mFieldData.mMinHeight);                    // Foliage Minimum Height.
1689      stream->write(mFieldData.mMaxHeight);                    // Foliage Maximum Height.
1690      stream->write(mFieldData.mFixAspectRatio);                  // Foliage Fix Aspect Ratio.
1691      stream->write(mFieldData.mFixSizeToMax);                 // Foliage Fix Size to Max.
1692      stream->write(mFieldData.mOffsetZ);                      // Foliage Offset Z.
1693      stream->writeFlag(mFieldData.mRandomFlip);               // Foliage Random Flip.
1694      stream->writeFlag(mFieldData.mUseTrueBillboards);        // Foliage faces the camera (including z axis)
1695
1696      stream->write(mFieldData.mUseCulling);                   // Foliage Use Culling.
1697      stream->write(mFieldData.mCullResolution);                  // Foliage Cull Resolution.
1698      stream->write(mFieldData.mViewDistance);                 // Foliage View Distance.
1699      stream->write(mFieldData.mViewClosest);                     // Foliage View Closest.
1700      stream->write(mFieldData.mFadeInRegion);                 // Foliage Fade-In Region.
1701      stream->write(mFieldData.mFadeOutRegion);                // Foliage Fade-Out Region.
1702      stream->write(mFieldData.mAlphaCutoff);                     // Foliage Alpha Cutoff.
1703      stream->write(mFieldData.mGroundAlpha);                     // Foliage Ground Alpha.
1704
1705      stream->writeFlag(mFieldData.mSwayOn);                   // Foliage Sway On Flag.
1706      stream->writeFlag(mFieldData.mSwaySync);                 // Foliage Sway Sync Flag.
1707      stream->write(mFieldData.mSwayMagnitudeSide);               // Foliage Sway Magnitude Side2Side.
1708      stream->write(mFieldData.mSwayMagnitudeFront);              // Foliage Sway Magnitude Front2Back.
1709      stream->write(mFieldData.mMinSwayTime);                     // Foliage Minimum Sway Time.
1710      stream->write(mFieldData.mMaxSwayTime);                     // Foliage Maximum way Time.
1711
1712      stream->writeFlag(mFieldData.mLightOn);                     // Foliage Light On Flag.
1713      stream->writeFlag(mFieldData.mLightSync);                // Foliage Light Sync
1714      stream->write(mFieldData.mMinLuminance);                 // Foliage Minimum Luminance.
1715      stream->write(mFieldData.mMaxLuminance);                 // Foliage Maximum Luminance.
1716      stream->write(mFieldData.mLightTime);                    // Foliage Light Time.
1717
1718      stream->writeFlag(mFieldData.mAllowOnTerrain);              // Allow on Terrain.
1719      stream->writeFlag(mFieldData.mAllowStatics);             // Allow on Statics.
1720      stream->writeFlag(mFieldData.mAllowOnWater);             // Allow on Water.
1721      stream->writeFlag(mFieldData.mAllowWaterSurface);           // Allow on Water Surface.
1722      stream->write(mFieldData.mAllowedTerrainSlope);             // Foliage Offset Z.
1723
1724      stream->writeFlag(mFieldData.mHideFoliage);                 // Hide Foliage.
1725      stream->writeFlag(mFieldData.mShowPlacementArea);           // Show Placement Area Flag.
1726      stream->write(mFieldData.mPlacementBandHeight);             // Placement Area Height.
1727      stream->write(mFieldData.mPlaceAreaColour);                 // Placement Area Colour.
1728      stream->write(mFieldData.mAmbientModulationBias);
1729   }
1730
1731   // Were done ...
1732   return(retMask);
1733}
1734
1735//------------------------------------------------------------------------------
1736
1737void fxFoliageReplicator::unpackUpdate(NetConnection * con, BitStream * stream)
1738{
1739   // Unpack Parent.
1740   Parent::unpackUpdate(con, stream);
1741
1742   // Read Replication Details.
1743   if(stream->readFlag())
1744   {
1745      MatrixF     ReplicatorObjectMatrix;
1746
1747      stream->readAffineTransform(&ReplicatorObjectMatrix);       // Foliage Master Object Position.
1748
1749      mFieldData.mUseDebugInfo = stream->readFlag();              // Foliage Debug Information Flag.
1750      stream->read(&mFieldData.mDebugBoxHeight);                  // Foliage Debug Height.
1751      stream->read(&mFieldData.mSeed);                      // Foliage Seed.
1752      stream->read(&mFieldData.mFoliageCount);                 // Foliage Count.
1753      stream->read(&mFieldData.mFoliageRetries);                  // Foliage Retries.
1754      mFieldData.mFoliageFile = stream->readSTString();           // Foliage File.
1755
1756      stream->read(&mFieldData.mInnerRadiusX);                 // Foliage Inner Radius X.
1757      stream->read(&mFieldData.mInnerRadiusY);                 // Foliage Inner Radius Y.
1758      stream->read(&mFieldData.mOuterRadiusX);                 // Foliage Outer Radius X.
1759      stream->read(&mFieldData.mOuterRadiusY);                 // Foliage Outer Radius Y.
1760
1761      stream->read(&mFieldData.mMinWidth);                     // Foliage Minimum Width.
1762      stream->read(&mFieldData.mMaxWidth);                     // Foliage Maximum Width.
1763      stream->read(&mFieldData.mMinHeight);                    // Foliage Minimum Height.
1764      stream->read(&mFieldData.mMaxHeight);                    // Foliage Maximum Height.
1765      stream->read(&mFieldData.mFixAspectRatio);                  // Foliage Fix Aspect Ratio.
1766      stream->read(&mFieldData.mFixSizeToMax);                 // Foliage Fix Size to Max.
1767      stream->read(&mFieldData.mOffsetZ);                      // Foliage Offset Z.
1768      mFieldData.mRandomFlip = stream->readFlag();             // Foliage Random Flip.
1769      
1770      bool wasTrueBB = mFieldData.mUseTrueBillboards;
1771      mFieldData.mUseTrueBillboards = stream->readFlag();      // Foliage is camera facing (including z axis).
1772
1773      stream->read(&mFieldData.mUseCulling);                   // Foliage Use Culling.
1774      stream->read(&mFieldData.mCullResolution);                  // Foliage Cull Resolution.
1775      stream->read(&mFieldData.mViewDistance);                 // Foliage View Distance.
1776      stream->read(&mFieldData.mViewClosest);                     // Foliage View Closest.
1777      stream->read(&mFieldData.mFadeInRegion);                 // Foliage Fade-In Region.
1778      stream->read(&mFieldData.mFadeOutRegion);                // Foliage Fade-Out Region.
1779      stream->read(&mFieldData.mAlphaCutoff);                     // Foliage Alpha Cutoff.
1780      stream->read(&mFieldData.mGroundAlpha);                     // Foliage Ground Alpha.
1781
1782      mFieldData.mSwayOn = stream->readFlag();                 // Foliage Sway On Flag.
1783      mFieldData.mSwaySync = stream->readFlag();                  // Foliage Sway Sync Flag.
1784      stream->read(&mFieldData.mSwayMagnitudeSide);               // Foliage Sway Magnitude Side2Side.
1785      stream->read(&mFieldData.mSwayMagnitudeFront);              // Foliage Sway Magnitude Front2Back.
1786      stream->read(&mFieldData.mMinSwayTime);                     // Foliage Minimum Sway Time.
1787      stream->read(&mFieldData.mMaxSwayTime);                     // Foliage Maximum way Time.
1788
1789      mFieldData.mLightOn = stream->readFlag();                // Foliage Light On Flag.
1790      mFieldData.mLightSync = stream->readFlag();                 // Foliage Light Sync
1791      stream->read(&mFieldData.mMinLuminance);                 // Foliage Minimum Luminance.
1792      stream->read(&mFieldData.mMaxLuminance);                 // Foliage Maximum Luminance.
1793      stream->read(&mFieldData.mLightTime);                    // Foliage Light Time.
1794
1795      mFieldData.mAllowOnTerrain = stream->readFlag();            // Allow on Terrain.
1796      mFieldData.mAllowStatics = stream->readFlag();              // Allow on Statics.
1797      mFieldData.mAllowOnWater = stream->readFlag();              // Allow on Water.
1798      mFieldData.mAllowWaterSurface = stream->readFlag();            // Allow on Water Surface.
1799      stream->read(&mFieldData.mAllowedTerrainSlope);             // Allowed Terrain Slope.
1800
1801      mFieldData.mHideFoliage = stream->readFlag();               // Hide Foliage.
1802      mFieldData.mShowPlacementArea = stream->readFlag();            // Show Placement Area Flag.
1803      stream->read(&mFieldData.mPlacementBandHeight);             // Placement Area Height.
1804      stream->read(&mFieldData.mPlaceAreaColour);
1805
1806      stream->read(&mFieldData.mAmbientModulationBias);
1807      // Calculate Fade-In/Out Gradients.
1808      mFadeInGradient      = 1.0f / mFieldData.mFadeInRegion;
1809      mFadeOutGradient  = 1.0f / mFieldData.mFadeOutRegion;
1810
1811      // Set Transform.
1812      setTransform(ReplicatorObjectMatrix);
1813
1814      // Load Foliage Texture on the client.
1815      if( mFieldData.mFoliageFile != NULL && dStrlen(mFieldData.mFoliageFile) > 0 )
1816         mFieldData.mFoliageTexture = GFXTexHandle( mFieldData.mFoliageFile, &GFXStaticTextureSRGBProfile, avar("%s() - mFieldData.mFoliageTexture (line %d)", __FUNCTION__, __LINE__) );
1817
1818      if ((GFXTextureObject*) mFieldData.mFoliageTexture == NULL)
1819         Con::printf("fxFoliageReplicator:  %s is an invalid or missing foliage texture file.", mFieldData.mFoliageFile);
1820
1821      // Set Quad-Tree Box Height Lerp.
1822      mFrustumRenderSet.mHeightLerp = mFieldData.mDebugBoxHeight;
1823
1824      // Create Foliage (if Replication has begun).
1825      if (mClientReplicationStarted)
1826      {
1827         CreateFoliage();
1828         mDirty = true;
1829      }
1830
1831      if ( isProperlyAdded() && mFieldData.mUseTrueBillboards != wasTrueBB )
1832         SetupShader();
1833   }
1834}
1835