fxFoliageReplicator.cpp
Engine/source/T3D/fx/fxFoliageReplicator.cpp
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