2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
// Written by Melvyn May, Started on 4th August 2002.
//
// "My code is written for the Torque community, so do your worst with it,
// just don't rip-it-off and call it your own without even thanking me".
//
// - Melv.
//
//
// Conversion to TSE By Brian "bzztbomb" Richardson 9/2005
// This was a neat piece of code! Thanks Melv!
// I've switched this to use one large indexed primitive buffer. All animation
// is then done in the vertex shader. This means we have a static vertex/primitive
// buffer that never changes! How spiff! Because of this, the culling code was
// changed to render out full quadtree nodes, we don't try to cull each individual
// node ourselves anymore. This means to get good performance, you probably need to do the
// following:
// 1. If it's a small area to cover, turn off culling completely.
// 2. You want to tune the parameters to make sure there are a lot of billboards within
// each quadrant.
//
// POTENTIAL TODO LIST:
// TODO: Clamp item alpha to fog alpha
2017-07-26 23:18:38 +00:00
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
2012-09-19 15:15:01 +00:00
# include "platform/platform.h"
# include "T3D/fx/fxFoliageReplicator.h"
# include "gfx/gfxDevice.h"
# include "gfx/primBuilder.h" // Used for debug / mission edit rendering
# include "console/consoleTypes.h"
# include "core/stream/bitStream.h"
# include "math/mRandom.h"
# include "math/mathIO.h"
# include "console/simBase.h"
# include "scene/sceneManager.h"
# include "renderInstance/renderPassManager.h"
# include "scene/sceneRenderState.h"
# include "sim/netConnection.h"
# include "materials/shaderData.h"
# include "console/engineAPI.h"
const U32 AlphaTexLen = 1024 ;
GFXImplementVertexFormat ( GFXVertexFoliage )
{
addElement ( " POSITION " , GFXDeclType_Float3 ) ;
addElement ( " NORMAL " , GFXDeclType_Float3 ) ;
addElement ( " TEXCOORD " , GFXDeclType_Float2 , 0 ) ;
addElement ( " TEXCOORD " , GFXDeclType_Float2 , 1 ) ;
}
//------------------------------------------------------------------------------
//
// Put the function in /example/common/editor/ObjectBuilderGui.gui [around line 458] ...
//
// function ObjectBuilderGui::buildfxFoliageReplicator(%this)
// {
// %this.className = "fxFoliageReplicator";
// %this.process();
// }
//
//------------------------------------------------------------------------------
//
2020-12-12 15:54:16 +00:00
// Put this in /example/common/editor/EditorGui.tscript in [function Creator::init( %this )]
2012-09-19 15:15:01 +00:00
//
// %Environment_Item[8] = "fxFoliageReplicator"; <-- ADD THIS.
//
//------------------------------------------------------------------------------
//
2020-12-12 15:54:16 +00:00
// Put this in /example/common/client/missionDownload.tscript in [function clientCmdMissionStartPhase3(%seq,%missionName)] (line 65)
2012-09-19 15:15:01 +00:00
// after codeline 'onPhase2Complete();'.
//
// StartFoliageReplication();
//
//------------------------------------------------------------------------------
//
// Put this in /engine/console/simBase.h (around line 509) in
//
// namespace Sim
// {
// DeclareNamedSet(fxFoliageSet) <-- ADD THIS (Note no semi-colon).
//
//------------------------------------------------------------------------------
//
// Put this in /engine/console/simBase.cc (around line 19) in
//
// ImplementNamedSet(fxFoliageSet) <-- ADD THIS (Note no semi-colon).
//
//------------------------------------------------------------------------------
//
// Put this in /engine/console/simManager.cc [function void init()] (around line 269).
//
// namespace Sim
// {
// InstantiateNamedSet(fxFoliageSet); <-- ADD THIS (Including Semi-colon).
//
//------------------------------------------------------------------------------
extern bool gEditingMission ;
//------------------------------------------------------------------------------
IMPLEMENT_CO_NETOBJECT_V1 ( fxFoliageReplicator ) ;
ConsoleDocClass ( fxFoliageReplicator ,
" @brief An emitter to replicate fxFoliageItem objects across an area. \n "
" @ingroup Foliage \n "
) ;
//------------------------------------------------------------------------------
//
// Trig Table Lookups.
//
//------------------------------------------------------------------------------
const F32 PeriodLenMinus = ( F32 ) ( 2.0f * M_PI ) - 0.01f ;
//------------------------------------------------------------------------------
//
// Class: fxFoliageRenderList
//
//------------------------------------------------------------------------------
void fxFoliageRenderList : : SetupClipPlanes ( SceneRenderState * state , const F32 farClipPlane )
{
const F32 nearPlane = state - > getNearPlane ( ) ;
const F32 farPlane = farClipPlane ;
2013-11-07 20:07:16 +00:00
const Frustum & frustum = state - > getCullingFrustum ( ) ;
2012-09-19 15:15:01 +00:00
// [rene, 23-Feb-11] Why isn't this preserving the ortho state of the original frustum?
mFrustum . set ( false , //zoneState.frustum.isOrtho(),
frustum . getNearLeft ( ) ,
frustum . getNearRight ( ) ,
frustum . getNearTop ( ) ,
frustum . getNearBottom ( ) ,
nearPlane ,
farPlane ,
frustum . getTransform ( )
) ;
mBox = mFrustum . getBounds ( ) ;
}
//------------------------------------------------------------------------------
2017-06-23 16:36:20 +00:00
inline void fxFoliageRenderList : : DrawQuadBox ( const Box3F & QuadBox , const LinearColorF Colour )
2012-09-19 15:15:01 +00:00
{
// Define our debug box.
static Point3F BoxPnts [ ] = {
Point3F ( 0 , 0 , 0 ) ,
Point3F ( 0 , 0 , 1 ) ,
Point3F ( 0 , 1 , 0 ) ,
Point3F ( 0 , 1 , 1 ) ,
Point3F ( 1 , 0 , 0 ) ,
Point3F ( 1 , 0 , 1 ) ,
Point3F ( 1 , 1 , 0 ) ,
Point3F ( 1 , 1 , 1 )
} ;
static U32 BoxVerts [ ] [ 4 ] = {
{ 0 , 2 , 3 , 1 } , // -x
{ 7 , 6 , 4 , 5 } , // +x
{ 0 , 1 , 5 , 4 } , // -y
{ 3 , 2 , 6 , 7 } , // +y
{ 0 , 4 , 6 , 2 } , // -z
{ 3 , 7 , 5 , 1 } // +z
} ;
// Project our Box Points.
Point3F ProjectionPoints [ 8 ] ;
for ( U32 i = 0 ; i < 8 ; i + + )
{
ProjectionPoints [ i ] . set ( BoxPnts [ i ] . x ? QuadBox . maxExtents . x : QuadBox . minExtents . x ,
BoxPnts [ i ] . y ? QuadBox . maxExtents . y : QuadBox . minExtents . y ,
BoxPnts [ i ] . z ? ( mHeightLerp * QuadBox . maxExtents . z ) + ( 1 - mHeightLerp ) * QuadBox . minExtents . z : QuadBox . minExtents . z ) ;
}
PrimBuild : : color ( Colour ) ;
// Draw the Box.
for ( U32 x = 0 ; x < 6 ; x + + )
{
// Draw a line-loop.
PrimBuild : : begin ( GFXLineStrip , 5 ) ;
for ( U32 y = 0 ; y < 4 ; y + + )
{
PrimBuild : : vertex3f ( ProjectionPoints [ BoxVerts [ x ] [ y ] ] . x ,
ProjectionPoints [ BoxVerts [ x ] [ y ] ] . y ,
ProjectionPoints [ BoxVerts [ x ] [ y ] ] . z ) ;
}
PrimBuild : : vertex3f ( ProjectionPoints [ BoxVerts [ x ] [ 0 ] ] . x ,
ProjectionPoints [ BoxVerts [ x ] [ 0 ] ] . y ,
ProjectionPoints [ BoxVerts [ x ] [ 0 ] ] . z ) ;
PrimBuild : : end ( ) ;
}
}
//------------------------------------------------------------------------------
bool fxFoliageRenderList : : IsQuadrantVisible ( const Box3F VisBox , const MatrixF & RenderTransform )
{
// Can we trivially accept the visible box?
if ( ! mFrustum . isCulled ( VisBox ) )
return true ;
// Not visible.
return false ;
}
//------------------------------------------------------------------------------
//
// Class: fxFoliageCulledList
//
//------------------------------------------------------------------------------
fxFoliageCulledList : : fxFoliageCulledList ( Box3F SearchBox , fxFoliageCulledList * InVec )
{
// Find the Candidates.
FindCandidates ( SearchBox , InVec ) ;
}
//------------------------------------------------------------------------------
2015-07-17 03:02:18 +00:00
void fxFoliageCulledList : : FindCandidates ( const Box3F & SearchBox , fxFoliageCulledList * InVec )
2012-09-19 15:15:01 +00:00
{
// Search the Culled List.
for ( U32 i = 0 ; i < InVec - > GetListCount ( ) ; i + + )
{
// Is this Box overlapping our search box?
if ( SearchBox . isOverlapped ( InVec - > GetElement ( i ) - > FoliageBox ) )
{
// Yes, so add it to our culled list.
mCulledObjectSet . push_back ( InVec - > GetElement ( i ) ) ;
}
}
}
//------------------------------------------------------------------------------
//
// Class: fxFoliageReplicator
//
//------------------------------------------------------------------------------
fxFoliageReplicator : : fxFoliageReplicator ( )
{
// Setup NetObject.
mTypeMask | = StaticObjectType ;
mNetFlags . set ( Ghostable | ScopeAlways ) ;
// Reset Client Replication Started.
mClientReplicationStarted = false ;
// Reset Foliage Count.
mCurrentFoliageCount = 0 ;
2020-05-11 20:30:21 +00:00
dMemset ( & mFrustumRenderSet , 0 , sizeof ( mFrustumRenderSet ) ) ;
2012-09-19 15:15:01 +00:00
// Reset Creation Area Angle Animation.
mCreationAreaAngle = 0 ;
// Reset Last Render Time.
mLastRenderTime = 0 ;
// Reset Foliage Nodes.
mPotentialFoliageNodes = 0 ;
// Reset Billboards Acquired.
mBillboardsAcquired = 0 ;
// Reset Frame Serial ID.
mFrameSerialID = 0 ;
2020-05-11 20:30:21 +00:00
mQuadTreeLevels = 0 ;
mNextAllocatedNodeIdx = 0 ;
2012-09-19 15:15:01 +00:00
mAlphaLookup = NULL ;
2020-05-11 20:30:21 +00:00
mFadeInGradient = 0.0f ;
mFadeOutGradient = 0.0f ;
mGlobalSwayPhase = 0.0f ;
mGlobalSwayTimeRatio = 1.0f ;
mGlobalLightPhase = 0.0f ;
mGlobalLightTimeRatio = 1.0f ;
2012-09-19 15:15:01 +00:00
mDirty = true ;
mFoliageShaderProjectionSC = NULL ;
mFoliageShaderWorldSC = NULL ;
mFoliageShaderGlobalSwayPhaseSC = NULL ;
mFoliageShaderSwayMagnitudeSideSC = NULL ;
mFoliageShaderSwayMagnitudeFrontSC = NULL ;
mFoliageShaderGlobalLightPhaseSC = NULL ;
mFoliageShaderLuminanceMagnitudeSC = NULL ;
mFoliageShaderLuminanceMidpointSC = NULL ;
mFoliageShaderDistanceRangeSC = NULL ;
mFoliageShaderCameraPosSC = NULL ;
mFoliageShaderTrueBillboardSC = NULL ;
mFoliageShaderGroundAlphaSC = NULL ;
mFoliageShaderAmbientColorSC = NULL ;
2020-05-11 20:30:21 +00:00
mDiffuseTextureSC = NULL ;
mAlphaMapTextureSC = NULL ;
2012-09-19 15:15:01 +00:00
mShaderData = NULL ;
}
//------------------------------------------------------------------------------
fxFoliageReplicator : : ~ fxFoliageReplicator ( )
{
if ( mAlphaLookup )
delete mAlphaLookup ;
mPlacementSB = NULL ;
}
//------------------------------------------------------------------------------
void fxFoliageReplicator : : initPersistFields ( )
{
2023-01-27 07:13:15 +00:00
docsURL ;
2012-09-19 15:15:01 +00:00
// Add out own persistent fields.
addGroup ( " Debugging " ) ; // MM: Added Group Header.
addField ( " UseDebugInfo " , TypeBool , Offset ( mFieldData . mUseDebugInfo , fxFoliageReplicator ) , " Culling bins are drawn when set to true. " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " DebugBoxHeight " , TypeRangedF32 , Offset ( mFieldData . mDebugBoxHeight , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Height multiplier for drawn culling bins. " ) ;
2012-09-19 15:15:01 +00:00
addField ( " HideFoliage " , TypeBool , Offset ( mFieldData . mHideFoliage , fxFoliageReplicator ) , " Foliage is hidden when set to true. " ) ;
addField ( " ShowPlacementArea " , TypeBool , Offset ( mFieldData . mShowPlacementArea , fxFoliageReplicator ) , " Draw placement rings when set to true. " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " PlacementAreaHeight " , TypeRangedS32 , Offset ( mFieldData . mPlacementBandHeight , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Height of the placement ring in world units. " ) ;
2012-09-19 15:15:01 +00:00
addField ( " PlacementColour " , TypeColorF , Offset ( mFieldData . mPlaceAreaColour , fxFoliageReplicator ) , " Color of the placement ring. " ) ;
endGroup ( " Debugging " ) ; // MM: Added Group Footer.
addGroup ( " Media " ) ; // MM: Added Group Header.
addField ( " Seed " , TypeS32 , Offset ( mFieldData . mSeed , fxFoliageReplicator ) , " Random seed for foliage placement. " ) ;
addField ( " FoliageFile " , TypeFilename , Offset ( mFieldData . mFoliageFile , fxFoliageReplicator ) , " Image file for the foliage texture. " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " FoliageCount " , TypeRangedS32 , Offset ( mFieldData . mFoliageCount , fxFoliageReplicator ) , & CommonValidators : : NaturalNumber , " Maximum foliage instance count. " ) ;
addFieldV ( " FoliageRetries " , TypeRangedS32 , Offset ( mFieldData . mFoliageRetries , fxFoliageReplicator ) , & CommonValidators : : PositiveInt , " Number of times to try placing a foliage instance before giving up. " ) ;
2012-09-19 15:15:01 +00:00
endGroup ( " Media " ) ; // MM: Added Group Footer.
addGroup ( " Area " ) ; // MM: Added Group Header.
2025-03-09 16:53:23 +00:00
addFieldV ( " InnerRadiusX " , TypeRangedS32 , Offset ( mFieldData . mInnerRadiusX , fxFoliageReplicator ) , & CommonValidators : : PositiveInt , " Placement area inner radius on the X axis " ) ;
addFieldV ( " InnerRadiusY " , TypeRangedS32 , Offset ( mFieldData . mInnerRadiusY , fxFoliageReplicator ) , & CommonValidators : : PositiveInt , " Placement area inner radius on the Y axis " ) ;
addFieldV ( " OuterRadiusX " , TypeRangedS32 , Offset ( mFieldData . mOuterRadiusX , fxFoliageReplicator ) , & CommonValidators : : PositiveInt , " Placement area outer radius on the X axis " ) ;
addFieldV ( " OuterRadiusY " , TypeRangedS32 , Offset ( mFieldData . mOuterRadiusY , fxFoliageReplicator ) , & CommonValidators : : PositiveInt , " Placement area outer radius on the Y axis " ) ;
2012-09-19 15:15:01 +00:00
endGroup ( " Area " ) ; // MM: Added Group Footer.
addGroup ( " Dimensions " ) ; // MM: Added Group Header.
2025-03-09 16:53:23 +00:00
addFieldV ( " MinWidth " , TypeRangedF32 , Offset ( mFieldData . mMinWidth , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Minimum width of foliage billboards " ) ;
addFieldV ( " MaxWidth " , TypeRangedF32 , Offset ( mFieldData . mMaxWidth , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Maximum width of foliage billboards " ) ;
addFieldV ( " MinHeight " , TypeRangedF32 , Offset ( mFieldData . mMinHeight , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Minimum height of foliage billboards " ) ;
addFieldV ( " MaxHeight " , TypeRangedF32 , Offset ( mFieldData . mMaxHeight , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Maximum height of foliage billboards " ) ;
2012-09-19 15:15:01 +00:00
addField ( " FixAspectRatio " , TypeBool , Offset ( mFieldData . mFixAspectRatio , fxFoliageReplicator ) , " Maintain aspect ratio of image if true. This option ignores MaxWidth. " ) ;
addField ( " FixSizeToMax " , TypeBool , Offset ( mFieldData . mFixSizeToMax , fxFoliageReplicator ) , " Use only MaxWidth and MaxHeight for billboard size. Ignores MinWidth and MinHeight. " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " OffsetZ " , TypeRangedF32 , Offset ( mFieldData . mOffsetZ , fxFoliageReplicator ) , & CommonValidators : : F32Range , " Offset billboards by this amount vertically. " ) ;
2012-09-19 15:15:01 +00:00
addField ( " RandomFlip " , TypeBool , Offset ( mFieldData . mRandomFlip , fxFoliageReplicator ) , " Randomly flip billboards left-to-right. " ) ;
addField ( " UseTrueBillboards " , TypeBool , Offset ( mFieldData . mUseTrueBillboards , fxFoliageReplicator ) , " Use camera facing billboards ( including the z axis ). " ) ;
endGroup ( " Dimensions " ) ; // MM: Added Group Footer.
addGroup ( " Culling " ) ; // MM: Added Group Header.
addField ( " UseCulling " , TypeBool , Offset ( mFieldData . mUseCulling , fxFoliageReplicator ) , " Use culling bins when enabled. " ) ;
addField ( " CullResolution " , TypeS32 , Offset ( mFieldData . mCullResolution , fxFoliageReplicator ) , " Minimum size of culling bins. Must be >= 8 and <= OuterRadius. " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " ViewDistance " , TypeRangedF32 , Offset ( mFieldData . mViewDistance , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Maximum distance from camera where foliage appears. " ) ;
addFieldV ( " ViewClosest " , TypeRangedF32 , Offset ( mFieldData . mViewClosest , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Minimum distance from camera where foliage appears. " ) ;
addFieldV ( " FadeInRegion " , TypeRangedF32 , Offset ( mFieldData . mFadeInRegion , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Region beyond ViewDistance where foliage fades in/out. " ) ;
addFieldV ( " FadeOutRegion " , TypeRangedF32 , Offset ( mFieldData . mFadeOutRegion , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Region before ViewClosest where foliage fades in/out. " ) ;
addFieldV ( " AlphaCutoff " , TypeRangedF32 , Offset ( mFieldData . mAlphaCutoff , fxFoliageReplicator ) , & CommonValidators : : NormalizedFloat , " Minimum alpha value allowed on foliage instances. " ) ;
addFieldV ( " GroundAlpha " , TypeRangedF32 , Offset ( mFieldData . mGroundAlpha , fxFoliageReplicator ) , & CommonValidators : : NormalizedFloat , " Alpha of the foliage at ground level. 0 = transparent, 1 = opaque. " ) ;
2012-09-19 15:15:01 +00:00
endGroup ( " Culling " ) ; // MM: Added Group Footer.
addGroup ( " Animation " ) ; // MM: Added Group Header.
addField ( " SwayOn " , TypeBool , Offset ( mFieldData . mSwayOn , fxFoliageReplicator ) , " Foliage should sway randomly when true. " ) ;
addField ( " SwaySync " , TypeBool , Offset ( mFieldData . mSwaySync , fxFoliageReplicator ) , " Foliage instances should sway together when true and SwayOn is enabled. " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " SwayMagSide " , TypeRangedF32 , Offset ( mFieldData . mSwayMagnitudeSide , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Left-to-right sway magnitude. " ) ;
addFieldV ( " SwayMagFront " , TypeRangedF32 , Offset ( mFieldData . mSwayMagnitudeFront , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Front-to-back sway magnitude. " ) ;
addFieldV ( " MinSwayTime " , TypeRangedF32 , Offset ( mFieldData . mMinSwayTime , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Minumum sway cycle time in seconds. " ) ;
addFieldV ( " MaxSwayTime " , TypeRangedF32 , Offset ( mFieldData . mMaxSwayTime , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Maximum sway cycle time in seconds. " ) ;
2012-09-19 15:15:01 +00:00
endGroup ( " Animation " ) ; // MM: Added Group Footer.
addGroup ( " Lighting " ) ; // MM: Added Group Header.
addField ( " LightOn " , TypeBool , Offset ( mFieldData . mLightOn , fxFoliageReplicator ) , " Foliage should be illuminated with changing lights when true. " ) ;
addField ( " LightSync " , TypeBool , Offset ( mFieldData . mLightSync , fxFoliageReplicator ) , " Foliage instances have the same lighting when set and LightOn is set. " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " MinLuminance " , TypeRangedF32 , Offset ( mFieldData . mMinLuminance , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Minimum luminance for foliage instances. " ) ;
addFieldV ( " MaxLuminance " , TypeRangedF32 , Offset ( mFieldData . mMaxLuminance , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Maximum luminance for foliage instances. " ) ;
addFieldV ( " LightTime " , TypeRangedF32 , Offset ( mFieldData . mLightTime , fxFoliageReplicator ) , & CommonValidators : : PositiveFloat , " Time before foliage illumination cycle repeats. " ) ;
2012-09-19 15:15:01 +00:00
endGroup ( " Lighting " ) ; // MM: Added Group Footer.
addGroup ( " Restrictions " ) ; // MM: Added Group Header.
addField ( " AllowOnTerrain " , TypeBool , Offset ( mFieldData . mAllowOnTerrain , fxFoliageReplicator ) , " Foliage will be placed on terrain when set. " ) ;
addField ( " AllowOnStatics " , TypeBool , Offset ( mFieldData . mAllowStatics , fxFoliageReplicator ) , " Foliage will be placed on Static shapes when set. " ) ;
addField ( " AllowOnWater " , TypeBool , Offset ( mFieldData . mAllowOnWater , fxFoliageReplicator ) , " Foliage will be placed on/under water when set. " ) ;
addField ( " AllowWaterSurface " , TypeBool , Offset ( mFieldData . mAllowWaterSurface , fxFoliageReplicator ) , " Foliage will be placed on water when set. Requires AllowOnWater. " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " AllowedTerrainSlope " , TypeRangedS32 , Offset ( mFieldData . mAllowedTerrainSlope , fxFoliageReplicator ) , & CommonValidators : : S32_PosDegreeRangeQuarter , " Maximum surface angle allowed for foliage instances. " ) ;
2012-09-19 15:15:01 +00:00
endGroup ( " Restrictions " ) ; // MM: Added Group Footer.
2017-07-26 23:18:38 +00:00
addGroup ( " AFX " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " AmbientModulationBias " , TypeRangedF32 , Offset ( mFieldData . mAmbientModulationBias , fxFoliageReplicator ) , & CommonValidators : : NormalizedFloat , " Multiplier controling amount foliage is modulated by sun's ambient. " ) ;
2017-07-26 23:18:38 +00:00
endGroup ( " AFX " ) ;
2012-09-19 15:15:01 +00:00
// Initialise parents' persistent fields.
Parent : : initPersistFields ( ) ;
}
//------------------------------------------------------------------------------
void fxFoliageReplicator : : CreateFoliage ( void )
{
F32 HypX , HypY ;
F32 Angle ;
U32 RelocationRetry ;
Point3F FoliagePosition ;
Point3F FoliageStart ;
Point3F FoliageEnd ;
Point3F FoliageScale ;
bool CollisionResult ;
RayInfo RayEvent ;
// Let's get a minimum bounding volume.
Point3F MinPoint ( - 0.5 , - 0.5 , - 0.5 ) ;
Point3F MaxPoint ( 0.5 , 0.5 , 0.5 ) ;
// Check Host.
2015-02-02 23:24:01 +00:00
AssertFatal ( isClientObject ( ) , " Trying to create Foliage on Server, this is bad! " ) ;
2012-09-19 15:15:01 +00:00
// Cannot continue without Foliage Texture!
if ( dStrlen ( mFieldData . mFoliageFile ) = = 0 )
return ;
// Check that we can position somewhere!
if ( ! ( mFieldData . mAllowOnTerrain | |
mFieldData . mAllowStatics | |
mFieldData . mAllowOnWater ) )
{
// Problem ...
Con : : warnf ( ConsoleLogEntry : : General , " fxFoliageReplicator - Could not place Foliage, All alloweds are off! " ) ;
// Return here.
return ;
}
// Destroy Foliage if we've already got some.
if ( mCurrentFoliageCount ! = 0 ) DestroyFoliage ( ) ;
// Inform the user if culling has been disabled!
if ( ! mFieldData . mUseCulling )
{
// Console Output.
Con : : printf ( " fxFoliageReplicator - Culling has been disabled! " ) ;
}
// ----------------------------------------------------------------------------------------------------------------------
// > Calculate the Potential Foliage Nodes Required to achieve the selected culling resolution.
// > Populate Quad-tree structure to depth determined by culling resolution.
//
// A little explanation is called for here ...
//
// The approach to this problem has been choosen to make it *much* easier for
// the user to control the quad-tree culling resolution. The user enters a single
// world-space value 'mCullResolution' which controls the highest resolution at
// which the replicator will check visibility culling.
//
// example: If 'mCullResolution' is 32 and the size of the replicated area is 128 radius
// (256 diameter) then this results in the replicator creating a quad-tree where
// there are 256/32 = 8x8 blocks. Each of these can be checked to see if they
// reside within the viewing frustum and if not then they get culled therefore
// removing the need to parse all the billboards that occcupy that region.
// Most of the time you will get better than this as the culling algorithm will
// check the culling pyramid from the top to bottom e.g. the follow 'blocks'
// will be checked:-
//
// 1 x 256 x 256 (All of replicated area)
// 4 x 128 x 128 (4 corners of above)
// 16 x 64 x 64 (16 x 4 corners of above)
// etc.
//
//
// 1. First-up, the replicator needs to create a fixed-list of quad-tree nodes to work with.
//
// To calculate this we take the largest outer-radius value set in the replicator and
// calculate how many quad-tree levels are required to achieve the selected 'mCullResolution'.
// One of the initial problems is that the replicator has seperate radii values for X & Y.
// This can lead to a culling resolution smaller in one axis than the other if there is a
// difference between the Outer-Radii. Unfortunately, we just live with this as there is
// not much we can do here if we still want to allow the user to have this kind of
// elliptical placement control.
//
// To calculate the number of nodes needed we using the following equation:-
//
// Note:- We are changing the Logarithmic bases from 10 -> 2 ... grrrr!
//
// Cr = mCullResolution
// Rs = Maximum Radii Diameter
//
//
// ( Log10( Rs / Cr ) )
// int ( ---------------- + 0.5 )
// ( Log10( 2 ) )
//
// ---------|
// |
// | n
// / 4
// /
// ---------|
// n = 0
//
//
// So basically we calculate the number of blocks in 1D at the highest resolution, then
// calculate the inverse exponential (base 2 - 1D) to achieve that quantity of blocks.
// We round that upto the next highest integer = e. We then sum 4 to the power 0->e
// which gives us the correct number of nodes required. e is also stored as the starting
// level value for populating the quad-tree (see 3. below).
//
// 2. We then proceed to calculate the billboard positions as normal and calculate and assign
// each billboard a basic volume (rather than treat each as a point). We need to take into
// account possible front/back swaying as well as the basic plane dimensions here.
// When all the billboards have been choosen we then proceed to populate the quad-tree.
//
// 3. To populate the quad-tree we start with a box which completely encapsulates the volume
// occupied by all the billboards and enter into a recursive procedure to process that node.
// Processing this node involves splitting it into quadrants in X/Y untouched (for now).
// We then find candidate billboards with each of these quadrants searching using the
// current subset of shapes from the parent (this reduces the searching to a minimum and
// is very efficient).
//
// If a quadrant does not enclose any billboards then the node is dropped otherwise it
// is processed again using the same procedure.
//
// This happens until we have recursed through the maximum number of levels as calculated
// using the summation max (see equation above). When level 0 is reached, the current list
// of enclosed objects is stored within the node (for the rendering algorithm).
//
// 4. When this is complete we have finished here. The next stage is when rendering takes place.
// An algorithm steps through the quad-tree from the top and does visibility culling on
// each box (with respect to the viewing frustum) and culls as appropriate. If the box is
// visible then the next level is checked until we reach level 0 where the node contains
// a complete subset of billboards enclosed by the visible box.
//
//
// Using the above algorithm we can now generate *massive* quantities of billboards and (using the
// appropriate 'mCullResolution') only visible blocks of billboards will be processed.
//
// - Melv.
//
// ----------------------------------------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------------------------------------
// Step 1.
// ----------------------------------------------------------------------------------------------------------------------
// Calculate the maximum dimension.
F32 MaxDimension = 2.0f * ( ( mFieldData . mOuterRadiusX > mFieldData . mOuterRadiusY ) ? mFieldData . mOuterRadiusX : mFieldData . mOuterRadiusY ) ;
// Let's check that our cull resolution is not greater than half our maximum dimension (and less than 1).
if ( mFieldData . mCullResolution > ( MaxDimension / 2 ) | | mFieldData . mCullResolution < 8 )
{
// Problem ...
Con : : warnf ( ConsoleLogEntry : : General , " fxFoliageReplicator - Could create Foliage, invalid Culling Resolution! " ) ;
Con : : warnf ( ConsoleLogEntry : : General , " fxFoliageReplicator - Culling Resolution *must* be >=8 or <= %0.2f! " , ( MaxDimension / 2 ) ) ;
// Return here.
return ;
}
// Take first Timestamp.
F32 mStartCreationTime = ( F32 ) Platform : : getRealMilliseconds ( ) ;
// Calculate the quad-tree levels needed for selected 'mCullResolution'.
mQuadTreeLevels = ( U32 ) ( mCeil ( mLog ( MaxDimension / mFieldData . mCullResolution ) / mLog ( 2.0f ) ) ) ;
// Calculate the number of potential nodes required.
mPotentialFoliageNodes = 0 ;
for ( U32 n = 0 ; n < = mQuadTreeLevels ; n + + )
mPotentialFoliageNodes + = ( U32 ) ( mCeil ( mPow ( 4.0f , ( F32 ) n ) ) ) ; // Ceil to be safe!
// ----------------------------------------------------------------------------------------------------------------------
// Step 2.
// ----------------------------------------------------------------------------------------------------------------------
// Set Seed.
RandomGen . setSeed ( mFieldData . mSeed ) ;
// Add Foliage.
for ( U32 idx = 0 ; idx < mFieldData . mFoliageCount ; idx + + )
{
fxFoliageItem * pFoliageItem ;
Point3F FoliageOffsetPos ;
// Reset Relocation Retry.
RelocationRetry = mFieldData . mFoliageRetries ;
// Find it a home ...
do
{
// Get the fxFoliageReplicator Position.
FoliagePosition = getPosition ( ) ;
// Calculate a random offset
HypX = RandomGen . randF ( ( F32 ) mFieldData . mInnerRadiusX < mFieldData . mOuterRadiusX ? mFieldData . mInnerRadiusX : mFieldData . mOuterRadiusX , ( F32 ) mFieldData . mOuterRadiusX ) ;
HypY = RandomGen . randF ( ( F32 ) mFieldData . mInnerRadiusY < mFieldData . mOuterRadiusY ? mFieldData . mInnerRadiusY : mFieldData . mOuterRadiusY , ( F32 ) mFieldData . mOuterRadiusY ) ;
Angle = RandomGen . randF ( 0 , ( F32 ) M_2PI ) ;
// Calcualte the new position.
FoliagePosition . x + = HypX * mCos ( Angle ) ;
FoliagePosition . y + = HypY * mSin ( Angle ) ;
// Initialise RayCast Search Start/End Positions.
FoliageStart = FoliageEnd = FoliagePosition ;
FoliageStart . z = 2000.f ;
FoliageEnd . z = - 2000.f ;
// Perform Ray Cast Collision on Client.
CollisionResult = gClientContainer . castRay ( FoliageStart , FoliageEnd , FXFOLIAGEREPLICATOR_COLLISION_MASK , & RayEvent ) ;
// Did we hit anything?
if ( CollisionResult )
{
// For now, let's pretend we didn't get a collision.
CollisionResult = false ;
// Yes, so get it's type.
U32 CollisionType = RayEvent . object - > getTypeMask ( ) ;
// Check Illegal Placements, fail if we hit a disallowed type.
if ( ( ( CollisionType & TerrainObjectType ) & & ! mFieldData . mAllowOnTerrain ) | |
( ( CollisionType & StaticShapeObjectType ) & & ! mFieldData . mAllowStatics ) | |
( ( CollisionType & WaterObjectType ) & & ! mFieldData . mAllowOnWater ) ) continue ;
// If we collided with water and are not allowing on the water surface then let's find the
// terrain underneath and pass this on as the original collision else fail.
if ( ( CollisionType & WaterObjectType ) & & ! mFieldData . mAllowWaterSurface & &
! gClientContainer . castRay ( FoliageStart , FoliageEnd , FXFOLIAGEREPLICATOR_NOWATER_COLLISION_MASK , & RayEvent ) ) continue ;
// We passed with flying colour so carry on.
CollisionResult = true ;
}
// Invalidate if we are below Allowed Terrain Angle.
if ( RayEvent . normal . z < mSin ( mDegToRad ( 90.0f - mFieldData . mAllowedTerrainSlope ) ) ) CollisionResult = false ;
// Wait until we get a collision.
} while ( ! CollisionResult & & - - RelocationRetry ) ;
// Check for Relocation Problem.
if ( RelocationRetry > 0 )
{
// Adjust Impact point.
RayEvent . point . z + = mFieldData . mOffsetZ ;
// Set New Position.
FoliagePosition = RayEvent . point ;
}
else
{
// Warning.
Con : : warnf ( ConsoleLogEntry : : General , " fxFoliageReplicator - Could not find satisfactory position for Foliage! " ) ;
// Skip to next.
continue ;
}
// Monitor the total volume.
FoliageOffsetPos = FoliagePosition - getPosition ( ) ;
MinPoint . setMin ( FoliageOffsetPos ) ;
MaxPoint . setMax ( FoliageOffsetPos ) ;
// Create our Foliage Item.
pFoliageItem = new fxFoliageItem ;
// Reset Frame Serial.
pFoliageItem - > LastFrameSerialID = 0 ;
// Reset Transform.
pFoliageItem - > Transform . identity ( ) ;
// Set Position.
pFoliageItem - > Transform . setColumn ( 3 , FoliagePosition ) ;
// Are we fixing size @ max?
if ( mFieldData . mFixSizeToMax )
{
// Yes, so set height maximum height.
pFoliageItem - > Height = mFieldData . mMaxHeight ;
// Is the Aspect Ratio Fixed?
if ( mFieldData . mFixAspectRatio )
// Yes, so lock to height.
pFoliageItem - > Width = pFoliageItem - > Height ;
else
// No, so set width to maximum width.
pFoliageItem - > Width = mFieldData . mMaxWidth ;
}
else
{
// No, so choose a new Scale.
pFoliageItem - > Height = RandomGen . randF ( mFieldData . mMinHeight , mFieldData . mMaxHeight ) ;
// Is the Aspect Ratio Fixed?
if ( mFieldData . mFixAspectRatio )
// Yes, so lock to height.
pFoliageItem - > Width = pFoliageItem - > Height ;
else
// No, so choose a random width.
pFoliageItem - > Width = RandomGen . randF ( mFieldData . mMinWidth , mFieldData . mMaxWidth ) ;
}
// Are we randomly flipping horizontally?
if ( mFieldData . mRandomFlip )
// Yes, so choose a random flip for this object.
pFoliageItem - > Flipped = ( RandomGen . randF ( 0 , 1000 ) < 500.0f ) ? false : true ;
else
// No, so turn-off flipping.
pFoliageItem - > Flipped = false ;
// Calculate Foliage Item World Box.
// NOTE:- We generate a psuedo-volume here. It's basically the volume to which the
// plane can move and this includes swaying!
//
// Is Sway On?
if ( mFieldData . mSwayOn )
{
// Yes, so take swaying into account...
pFoliageItem - > FoliageBox . minExtents = FoliagePosition +
Point3F ( - pFoliageItem - > Width / 2.0f - mFieldData . mSwayMagnitudeSide ,
- 0.5f - mFieldData . mSwayMagnitudeFront ,
pFoliageItem - > Height ) ;
pFoliageItem - > FoliageBox . maxExtents = FoliagePosition +
Point3F ( + pFoliageItem - > Width / 2.0f + mFieldData . mSwayMagnitudeSide ,
+ 0.5f + mFieldData . mSwayMagnitudeFront ,
pFoliageItem - > Height ) ;
}
else
{
// No, so give it a minimum volume...
pFoliageItem - > FoliageBox . minExtents = FoliagePosition +
Point3F ( - pFoliageItem - > Width / 2.0f ,
- 0.5f ,
pFoliageItem - > Height ) ;
pFoliageItem - > FoliageBox . maxExtents = FoliagePosition +
Point3F ( + pFoliageItem - > Width / 2.0f ,
+ 0.5f ,
pFoliageItem - > Height ) ;
}
// Store Shape in Replicated Shapes Vector.
mReplicatedFoliage . push_back ( pFoliageItem ) ;
// Increase Foliage Count.
mCurrentFoliageCount + + ;
}
// Is Lighting On?
if ( mFieldData . mLightOn )
{
// Yes, so reset Global Light phase.
mGlobalLightPhase = 0.0f ;
// Set Global Light Time Ratio.
mGlobalLightTimeRatio = PeriodLenMinus / mFieldData . mLightTime ;
// Yes, so step through Foliage.
for ( U32 idx = 0 ; idx < mCurrentFoliageCount ; idx + + )
{
fxFoliageItem * pFoliageItem ;
// Fetch the Foliage Item.
pFoliageItem = mReplicatedFoliage [ idx ] ;
// Do we have an item?
if ( pFoliageItem )
{
// Yes, so are lights syncronised?
if ( mFieldData . mLightSync )
{
pFoliageItem - > LightTimeRatio = 1.0f ;
pFoliageItem - > LightPhase = 0.0f ;
}
else
{
// No, so choose a random Light phase.
pFoliageItem - > LightPhase = RandomGen . randF ( 0 , PeriodLenMinus ) ;
// Set Light Time Ratio.
pFoliageItem - > LightTimeRatio = PeriodLenMinus / mFieldData . mLightTime ;
}
}
}
}
// Is Swaying Enabled?
if ( mFieldData . mSwayOn )
{
// Yes, so reset Global Sway phase.
mGlobalSwayPhase = 0.0f ;
// Always set Global Sway Time Ratio.
mGlobalSwayTimeRatio = PeriodLenMinus / RandomGen . randF ( mFieldData . mMinSwayTime , mFieldData . mMaxSwayTime ) ;
// Yes, so step through Foliage.
for ( U32 idx = 0 ; idx < mCurrentFoliageCount ; idx + + )
{
fxFoliageItem * pFoliageItem ;
// Fetch the Foliage Item.
pFoliageItem = mReplicatedFoliage [ idx ] ;
// Do we have an item?
if ( pFoliageItem )
{
// Are we using Sway Sync?
if ( mFieldData . mSwaySync )
{
pFoliageItem - > SwayPhase = 0 ;
pFoliageItem - > SwayTimeRatio = mGlobalSwayTimeRatio ;
}
else
{
// No, so choose a random Sway phase.
pFoliageItem - > SwayPhase = RandomGen . randF ( 0 , PeriodLenMinus ) ;
// Set to random Sway Time.
pFoliageItem - > SwayTimeRatio = PeriodLenMinus / RandomGen . randF ( mFieldData . mMinSwayTime , mFieldData . mMaxSwayTime ) ;
}
}
}
}
// Update our Object Volume.
mObjBox . minExtents . set ( MinPoint ) ;
mObjBox . maxExtents . set ( MaxPoint ) ;
setTransform ( mObjToWorld ) ;
// ----------------------------------------------------------------------------------------------------------------------
// Step 3.
// ----------------------------------------------------------------------------------------------------------------------
// Reset Next Allocated Node to Stack base.
mNextAllocatedNodeIdx = 0 ;
// Allocate a new Node.
fxFoliageQuadrantNode * pNewNode = new fxFoliageQuadrantNode ;
// Store it in the Quad-tree.
mFoliageQuadTree . push_back ( pNewNode ) ;
// Populate Initial Node.
//
// Set Start Level.
pNewNode - > Level = mQuadTreeLevels ;
// Calculate Total Foliage Area.
pNewNode - > QuadrantBox = getWorldBox ( ) ;
// Reset Quadrant child nodes.
pNewNode - > QuadrantChildNode [ 0 ] =
pNewNode - > QuadrantChildNode [ 1 ] =
pNewNode - > QuadrantChildNode [ 2 ] =
pNewNode - > QuadrantChildNode [ 3 ] = NULL ;
// Create our initial cull list with *all* billboards into.
fxFoliageCulledList CullList ;
CullList . mCulledObjectSet = mReplicatedFoliage ;
// Move to next node Index.
mNextAllocatedNodeIdx + + ;
// Let's start this thing going by recursing it's children.
ProcessNodeChildren ( pNewNode , & CullList ) ;
// Calculate Elapsed Time and take new Timestamp.
F32 ElapsedTime = ( Platform : : getRealMilliseconds ( ) - mStartCreationTime ) * 0.001f ;
// Console Output.
Con : : printf ( " fxFoliageReplicator - Lev: %d PotNodes: %d Used: %d Objs: %d Time: %0.4fs. " ,
mQuadTreeLevels ,
mPotentialFoliageNodes ,
mNextAllocatedNodeIdx - 1 ,
mBillboardsAcquired ,
ElapsedTime ) ;
// Dump (*very*) approximate allocated memory.
F32 MemoryAllocated = ( F32 ) ( ( mNextAllocatedNodeIdx - 1 ) * sizeof ( fxFoliageQuadrantNode ) ) ;
MemoryAllocated + = mCurrentFoliageCount * sizeof ( fxFoliageItem ) ;
MemoryAllocated + = mCurrentFoliageCount * sizeof ( fxFoliageItem * ) ;
Con : : printf ( " fxFoliageReplicator - Approx. %0.2fMb allocated. " , MemoryAllocated / 1048576.0f ) ;
// ----------------------------------------------------------------------------------------------------------------------
SetupBuffers ( ) ;
// Take first Timestamp.
mLastRenderTime = Platform : : getVirtualMilliseconds ( ) ;
}
void fxFoliageReplicator : : SetupShader ( )
{
if ( ! mShaderData )
{
if ( ! Sim : : findObject ( " fxFoliageReplicatorShader " , mShaderData ) )
{
Con : : errorf ( " fxFoliageReplicator::SetupShader - could not find ShaderData named fxFoliageReplicatorShader " ) ;
return ;
}
}
Vector < GFXShaderMacro > macros ;
if ( mFieldData . mUseTrueBillboards )
macros . push_back ( GFXShaderMacro ( " TRUE_BILLBOARD " ) ) ;
mShader = mShaderData - > getShader ( macros ) ;
if ( ! mShader )
return ;
mFoliageShaderConsts = mShader - > allocConstBuffer ( ) ;
mFoliageShaderProjectionSC = mShader - > getShaderConstHandle ( " $projection " ) ;
mFoliageShaderWorldSC = mShader - > getShaderConstHandle ( " $world " ) ;
mFoliageShaderGlobalSwayPhaseSC = mShader - > getShaderConstHandle ( " $GlobalSwayPhase " ) ;
mFoliageShaderSwayMagnitudeSideSC = mShader - > getShaderConstHandle ( " $SwayMagnitudeSide " ) ;
mFoliageShaderSwayMagnitudeFrontSC = mShader - > getShaderConstHandle ( " $SwayMagnitudeFront " ) ;
mFoliageShaderGlobalLightPhaseSC = mShader - > getShaderConstHandle ( " $GlobalLightPhase " ) ;
mFoliageShaderLuminanceMagnitudeSC = mShader - > getShaderConstHandle ( " $LuminanceMagnitude " ) ;
mFoliageShaderLuminanceMidpointSC = mShader - > getShaderConstHandle ( " $LuminanceMidpoint " ) ;
mFoliageShaderDistanceRangeSC = mShader - > getShaderConstHandle ( " $DistanceRange " ) ;
mFoliageShaderCameraPosSC = mShader - > getShaderConstHandle ( " $CameraPos " ) ;
mFoliageShaderTrueBillboardSC = mShader - > getShaderConstHandle ( " $TrueBillboard " ) ;
mFoliageShaderGroundAlphaSC = mShader - > getShaderConstHandle ( " $groundAlpha " ) ;
mFoliageShaderAmbientColorSC = mShader - > getShaderConstHandle ( " $ambient " ) ;
mDiffuseTextureSC = mShader - > getShaderConstHandle ( " $diffuseMap " ) ;
mAlphaMapTextureSC = mShader - > getShaderConstHandle ( " $alphaMap " ) ;
}
// Ok, what we do is let the older code setup the FoliageItem list and the QuadTree.
// Then we build the Vertex and Primitive buffers here. It would probably be
// slightly more memory efficient to build the buffers directly, but we
// want to sort the items within the buffer by the quadtreenodes
void fxFoliageReplicator : : SetupBuffers ( )
{
// Following two arrays are used to build the vertex and primitive buffers.
Point3F basePoints [ 8 ] ;
basePoints [ 0 ] = Point3F ( - 0.5f , 0.0f , 1.0f ) ;
basePoints [ 1 ] = Point3F ( - 0.5f , 0.0f , 0.0f ) ;
basePoints [ 2 ] = Point3F ( 0.5f , 0.0f , 0.0f ) ;
basePoints [ 3 ] = Point3F ( 0.5f , 0.0f , 1.0f ) ;
Point2F texCoords [ 4 ] ;
texCoords [ 0 ] = Point2F ( 0.0 , 0.0 ) ;
texCoords [ 1 ] = Point2F ( 0.0 , 1.0 ) ;
texCoords [ 2 ] = Point2F ( 1.0 , 1.0 ) ;
texCoords [ 3 ] = Point2F ( 1.0 , 0.0 ) ;
// Init our Primitive Buffer
U32 indexSize = mFieldData . mFoliageCount * 6 ;
U16 * indices = new U16 [ indexSize ] ;
// Two triangles per particle
for ( U16 i = 0 ; i < mFieldData . mFoliageCount ; i + + ) {
U16 * idx = & indices [ i * 6 ] ; // hey, no offset math below, neat
U16 vertOffset = i * 4 ;
idx [ 0 ] = vertOffset + 0 ;
idx [ 1 ] = vertOffset + 1 ;
idx [ 2 ] = vertOffset + 2 ;
idx [ 3 ] = vertOffset + 2 ;
idx [ 4 ] = vertOffset + 3 ;
idx [ 5 ] = vertOffset + 0 ;
}
// Init the prim buffer and copy our indexes over
U16 * ibIndices ;
mPrimBuffer . set ( GFX , indexSize , 0 , GFXBufferTypeStatic ) ;
mPrimBuffer . lock ( & ibIndices ) ;
dMemcpy ( ibIndices , indices , indexSize * sizeof ( U16 ) ) ;
mPrimBuffer . unlock ( ) ;
delete [ ] indices ;
// Now, let's init the vertex buffer
U32 currPrimitiveStartIndex = 0 ;
mVertexBuffer . set ( GFX , mFieldData . mFoliageCount * 4 , GFXBufferTypeStatic ) ;
mVertexBuffer . lock ( ) ;
U32 idx = 0 ;
for ( S32 qtIdx = 0 ; qtIdx < mFoliageQuadTree . size ( ) ; qtIdx + + ) {
fxFoliageQuadrantNode * quadNode = mFoliageQuadTree [ qtIdx ] ;
if ( quadNode - > Level = = 0 ) {
quadNode - > startIndex = currPrimitiveStartIndex ;
quadNode - > primitiveCount = 0 ;
// Ok, there should be data in here!
for ( S32 i = 0 ; i < quadNode - > RenderList . size ( ) ; i + + ) {
fxFoliageItem * pFoliageItem = quadNode - > RenderList [ i ] ;
if ( pFoliageItem - > LastFrameSerialID = = 0 ) {
pFoliageItem - > LastFrameSerialID + + ;
// Dump it into the vertex buffer
for ( U32 vertIndex = 0 ; vertIndex < 4 ; vertIndex + + ) {
GFXVertexFoliage * vert = & mVertexBuffer [ ( idx * 4 ) + vertIndex ] ;
// This is the position of the billboard.
vert - > point = pFoliageItem - > Transform . getPosition ( ) ;
// Normal contains the point of the billboard (except for the y component, see below)
vert - > normal = basePoints [ vertIndex ] ;
vert - > normal . x * = pFoliageItem - > Width ;
vert - > normal . z * = pFoliageItem - > Height ;
// Handle texture coordinates
vert - > texCoord = texCoords [ vertIndex ] ;
if ( pFoliageItem - > Flipped )
vert - > texCoord . x = 1.0f - vert - > texCoord . x ;
// Handle sway. Sway is stored in a texture coord. The x coordinate is the sway phase multiplier,
// the y coordinate determines if this vertex actually sways or not.
if ( ( vertIndex = = 0 ) | | ( vertIndex = = 3 ) ) {
vert - > texCoord2 . set ( pFoliageItem - > SwayTimeRatio / mGlobalSwayTimeRatio , 1.0f ) ;
} else {
vert - > texCoord2 . set ( 0.0f , 0.0f ) ;
}
// Handle lighting, lighting happens at the same time as global so this is just an offset.
vert - > normal . y = pFoliageItem - > LightPhase ;
}
idx + + ;
quadNode - > primitiveCount + = 2 ;
currPrimitiveStartIndex + = 6 ;
}
}
}
}
mVertexBuffer . unlock ( ) ;
DestroyFoliageItems ( ) ;
}
//------------------------------------------------------------------------------
2015-07-17 03:02:18 +00:00
Box3F fxFoliageReplicator : : FetchQuadrant ( const Box3F & Box , U32 Quadrant )
2012-09-19 15:15:01 +00:00
{
Box3F QuadrantBox ;
// Select Quadrant.
switch ( Quadrant )
{
// UL.
case 0 :
QuadrantBox . minExtents = Box . minExtents + Point3F ( 0 , Box . len_y ( ) / 2 , 0 ) ;
QuadrantBox . maxExtents = QuadrantBox . minExtents + Point3F ( Box . len_x ( ) / 2 , Box . len_y ( ) / 2 , Box . len_z ( ) ) ;
break ;
// UR.
case 1 :
QuadrantBox . minExtents = Box . minExtents + Point3F ( Box . len_x ( ) / 2 , Box . len_y ( ) / 2 , 0 ) ;
QuadrantBox . maxExtents = QuadrantBox . minExtents + Point3F ( Box . len_x ( ) / 2 , Box . len_y ( ) / 2 , Box . len_z ( ) ) ;
break ;
// LL.
case 2 :
QuadrantBox . minExtents = Box . minExtents ;
QuadrantBox . maxExtents = QuadrantBox . minExtents + Point3F ( Box . len_x ( ) / 2 , Box . len_y ( ) / 2 , Box . len_z ( ) ) ;
break ;
// LR.
case 3 :
QuadrantBox . minExtents = Box . minExtents + Point3F ( Box . len_x ( ) / 2 , 0 , 0 ) ;
QuadrantBox . maxExtents = QuadrantBox . minExtents + Point3F ( Box . len_x ( ) / 2 , Box . len_y ( ) / 2 , Box . len_z ( ) ) ;
break ;
default :
return Box ;
}
return QuadrantBox ;
}
//------------------------------------------------------------------------------
void fxFoliageReplicator : : ProcessNodeChildren ( fxFoliageQuadrantNode * pParentNode , fxFoliageCulledList * pCullList )
{
// ---------------------------------------------------------------
// Split Node into Quadrants and Process each.
// ---------------------------------------------------------------
// Process All Quadrants (UL/UR/LL/LR).
for ( U32 q = 0 ; q < 4 ; q + + )
ProcessQuadrant ( pParentNode , pCullList , q ) ;
}
//------------------------------------------------------------------------------
void fxFoliageReplicator : : ProcessQuadrant ( fxFoliageQuadrantNode * pParentNode , fxFoliageCulledList * pCullList , U32 Quadrant )
{
// Fetch Quadrant Box.
const Box3F QuadrantBox = FetchQuadrant ( pParentNode - > QuadrantBox , Quadrant ) ;
// Create our new Cull List.
fxFoliageCulledList CullList ( QuadrantBox , pCullList ) ;
// Did we get any objects?
if ( CullList . GetListCount ( ) > 0 )
{
// Yes, so allocate a new Node.
fxFoliageQuadrantNode * pNewNode = new fxFoliageQuadrantNode ;
// Store it in the Quad-tree.
mFoliageQuadTree . push_back ( pNewNode ) ;
// Move to next node Index.
mNextAllocatedNodeIdx + + ;
// Populate Quadrant Node.
//
// Next Sub-level.
pNewNode - > Level = pParentNode - > Level - 1 ;
// Calculate Quadrant Box.
pNewNode - > QuadrantBox = QuadrantBox ;
// Reset Child Nodes.
pNewNode - > QuadrantChildNode [ 0 ] =
pNewNode - > QuadrantChildNode [ 1 ] =
pNewNode - > QuadrantChildNode [ 2 ] =
pNewNode - > QuadrantChildNode [ 3 ] = NULL ;
// Put a reference in parent.
pParentNode - > QuadrantChildNode [ Quadrant ] = pNewNode ;
// If we're not at sub-level 0 then process this nodes children.
if ( pNewNode - > Level ! = 0 ) ProcessNodeChildren ( pNewNode , & CullList ) ;
// If we've reached sub-level 0 then store Cull List (for rendering).
if ( pNewNode - > Level = = 0 )
{
// Store the render list from our culled object set.
pNewNode - > RenderList = CullList . mCulledObjectSet ;
// Keep track of the total billboard acquired.
mBillboardsAcquired + = CullList . GetListCount ( ) ;
}
}
}
//------------------------------------------------------------------------------
void fxFoliageReplicator : : SyncFoliageReplicators ( void )
{
// Check Host.
2015-02-02 23:24:01 +00:00
AssertFatal ( isServerObject ( ) , " We *MUST* be on server when Synchronising Foliage! " ) ;
2012-09-19 15:15:01 +00:00
// Find the Replicator Set.
SimSet * fxFoliageSet = dynamic_cast < SimSet * > ( Sim : : findObject ( " fxFoliageSet " ) ) ;
// Return if Error.
if ( ! fxFoliageSet )
{
// Console Warning.
Con : : warnf ( " fxFoliageReplicator - Cannot locate the 'fxFoliageSet', this is bad! " ) ;
// Return here.
return ;
}
// Parse Replication Object(s).
for ( SimSetIterator itr ( fxFoliageSet ) ; * itr ; + + itr )
{
// Fetch the Replicator Object.
fxFoliageReplicator * Replicator = static_cast < fxFoliageReplicator * > ( * itr ) ;
// Set Foliage Replication Mask.
if ( Replicator - > isServerObject ( ) )
{
Con : : printf ( " fxFoliageReplicator - Restarting fxFoliageReplicator Object... " ) ;
Replicator - > setMaskBits ( FoliageReplicationMask ) ;
}
}
// Info ...
Con : : printf ( " fxFoliageReplicator - Client Foliage Sync has completed. " ) ;
}
//------------------------------------------------------------------------------
// Lets chill our memory requirements out a little
void fxFoliageReplicator : : DestroyFoliageItems ( )
{
// Remove shapes.
for ( S32 idx = 0 ; idx < mReplicatedFoliage . size ( ) ; idx + + )
{
fxFoliageItem * pFoliageItem ;
// Fetch the Foliage Item.
pFoliageItem = mReplicatedFoliage [ idx ] ;
// Delete Shape.
if ( pFoliageItem ) delete pFoliageItem ;
}
// Clear the Replicated Foliage Vector.
mReplicatedFoliage . clear ( ) ;
// Clear out old references also
for ( S32 qtIdx = 0 ; qtIdx < mFoliageQuadTree . size ( ) ; qtIdx + + ) {
fxFoliageQuadrantNode * quadNode = mFoliageQuadTree [ qtIdx ] ;
if ( quadNode - > Level = = 0 ) {
quadNode - > RenderList . clear ( ) ;
}
}
}
void fxFoliageReplicator : : DestroyFoliage ( void )
{
// Check Host.
2015-02-02 23:24:01 +00:00
AssertFatal ( isClientObject ( ) , " Trying to destroy Foliage on Server, this is bad! " ) ;
2012-09-19 15:15:01 +00:00
// Destroy Quad-tree.
mPotentialFoliageNodes = 0 ;
// Reset Billboards Acquired.
mBillboardsAcquired = 0 ;
// Finish if we didn't create any shapes.
if ( mCurrentFoliageCount = = 0 ) return ;
DestroyFoliageItems ( ) ;
// Let's remove the Quad-Tree allocations.
for ( Vector < fxFoliageQuadrantNode * > : : iterator QuadNodeItr = mFoliageQuadTree . begin ( ) ;
QuadNodeItr ! = mFoliageQuadTree . end ( ) ;
QuadNodeItr + + )
{
// Remove the node.
delete * QuadNodeItr ;
}
// Clear the Foliage Quad-Tree Vector.
mFoliageQuadTree . clear ( ) ;
// Clear the Frustum Render Set Vector.
mFrustumRenderSet . mVisObjectSet . clear ( ) ;
// Reset Foliage Count.
mCurrentFoliageCount = 0 ;
}
//------------------------------------------------------------------------------
void fxFoliageReplicator : : StartUp ( void )
{
// Flag, Client Replication Started.
mClientReplicationStarted = true ;
// Create foliage on Client.
if ( isClientObject ( ) ) CreateFoliage ( ) ;
}
//------------------------------------------------------------------------------
bool fxFoliageReplicator : : onAdd ( )
{
if ( ! Parent : : onAdd ( ) ) return ( false ) ;
// Add the Replicator to the Replicator Set.
dynamic_cast < SimSet * > ( Sim : : findObject ( " fxFoliageSet " ) ) - > addObject ( this ) ;
// Set Default Object Box.
mObjBox . minExtents . set ( - 0.5 , - 0.5 , - 0.5 ) ;
mObjBox . maxExtents . set ( 0.5 , 0.5 , 0.5 ) ;
resetWorldBox ( ) ;
setRenderTransform ( mObjToWorld ) ;
// Add to Scene.
addToScene ( ) ;
// Are we on the client?
if ( isClientObject ( ) )
{
// Yes, so load foliage texture.
if ( mFieldData . mFoliageFile ! = NULL & & dStrlen ( mFieldData . mFoliageFile ) > 0 )
2017-06-23 16:36:20 +00:00
mFieldData . mFoliageTexture = GFXTexHandle ( mFieldData . mFoliageFile , & GFXStaticTextureSRGBProfile , avar ( " %s() - mFieldData.mFoliageTexture (line %d) " , __FUNCTION__ , __LINE__ ) ) ;
2012-09-19 15:15:01 +00:00
if ( ( GFXTextureObject * ) mFieldData . mFoliageTexture = = NULL )
Con : : printf ( " fxFoliageReplicator: %s is an invalid or missing foliage texture file. " , mFieldData . mFoliageFile ) ;
mAlphaLookup = new GBitmap ( AlphaTexLen , 1 ) ;
computeAlphaTex ( ) ;
// Register for notification when GhostAlways objects are done loading
NetConnection : : smGhostAlwaysDone . notify ( this , & fxFoliageReplicator : : onGhostAlwaysDone ) ;
SetupShader ( ) ;
}
// Return OK.
return ( true ) ;
}
//------------------------------------------------------------------------------
void fxFoliageReplicator : : onRemove ( )
{
// Remove the Replicator from the Replicator Set.
dynamic_cast < SimSet * > ( Sim : : findObject ( " fxFoliageSet " ) ) - > removeObject ( this ) ;
NetConnection : : smGhostAlwaysDone . remove ( this , & fxFoliageReplicator : : onGhostAlwaysDone ) ;
// Remove from Scene.
removeFromScene ( ) ;
// Are we on the Client?
if ( isClientObject ( ) )
{
// Yes, so destroy Foliage.
DestroyFoliage ( ) ;
// Remove Texture.
mFieldData . mFoliageTexture = NULL ;
mShader = NULL ;
}
// Do Parent.
Parent : : onRemove ( ) ;
}
//------------------------------------------------------------------------------
void fxFoliageReplicator : : onGhostAlwaysDone ( )
{
if ( isClientObject ( ) )
CreateFoliage ( ) ;
}
//------------------------------------------------------------------------------
void fxFoliageReplicator : : inspectPostApply ( )
{
// Set Parent.
Parent : : inspectPostApply ( ) ;
// Set Foliage Replication Mask (this object only).
setMaskBits ( FoliageReplicationMask ) ;
mDirty = true ;
}
//------------------------------------------------------------------------------
DefineEngineFunction ( StartFoliageReplication , void , ( ) , , " Activates the foliage replicator. \n "
" @tsexample \n "
" // Call the function \n "
" StartFoliageReplication(); \n "
" @endtsexample \n "
" @ingroup Foliage " )
{
// Find the Replicator Set.
SimSet * fxFoliageSet = dynamic_cast < SimSet * > ( Sim : : findObject ( " fxFoliageSet " ) ) ;
// Return if Error.
if ( ! fxFoliageSet )
{
// Console Warning.
Con : : warnf ( " fxFoliageReplicator - Cannot locate the 'fxFoliageSet', this is bad! " ) ;
// Return here.
return ;
}
// Parse Replication Object(s).
U32 startupCount = 0 ;
for ( SimSetIterator itr ( fxFoliageSet ) ; * itr ; + + itr )
{
// Fetch the Replicator Object.
fxFoliageReplicator * Replicator = static_cast < fxFoliageReplicator * > ( * itr ) ;
// Start Client Objects Only.
if ( Replicator - > isClientObject ( ) )
{
Replicator - > StartUp ( ) ;
startupCount + + ;
}
}
// Info ...
Con : : printf ( " fxFoliageReplicator - replicated client foliage for %d objects " , startupCount ) ;
}
//------------------------------------------------------------------------------
void fxFoliageReplicator : : prepRenderImage ( SceneRenderState * state )
{
ObjectRenderInst * ri = state - > getRenderPass ( ) - > allocInst < ObjectRenderInst > ( ) ;
ri - > renderDelegate . bind ( this , & fxFoliageReplicator : : renderObject ) ;
ri - > type = RenderPassManager : : RIT_Foliage ;
state - > getRenderPass ( ) - > addInst ( ri ) ;
}
//
// RENDERING
//
void fxFoliageReplicator : : computeAlphaTex ( )
{
// Distances used in alpha
const F32 ClippedViewDistance = mFieldData . mViewDistance ;
const F32 MaximumViewDistance = ClippedViewDistance + mFieldData . mFadeInRegion ;
// This is used for the alpha computation in the shader.
for ( U32 i = 0 ; i < AlphaTexLen ; i + + ) {
F32 Distance = ( ( float ) i / ( float ) AlphaTexLen ) * MaximumViewDistance ;
F32 ItemAlpha = 1.0f ;
// Are we fading out?
if ( Distance < mFieldData . mViewClosest )
{
// Yes, so set fade-out.
ItemAlpha = 1.0f - ( ( mFieldData . mViewClosest - Distance ) * mFadeOutGradient ) ;
}
// No, so are we fading in?
else if ( Distance > ClippedViewDistance )
{
// Yes, so set fade-in
ItemAlpha = 1.0f - ( ( Distance - ClippedViewDistance ) * mFadeInGradient ) ;
}
// Set texture info
ColorI c ( ( U8 ) ( 255.0f * ItemAlpha ) , 0 , 0 ) ;
mAlphaLookup - > setColor ( i , 0 , c ) ;
}
2017-06-23 16:36:20 +00:00
mAlphaTexture . set ( mAlphaLookup , & GFXStaticTextureSRGBProfile , false , String ( " fxFoliage Replicator Alpha Texture " ) ) ;
2012-09-19 15:15:01 +00:00
}
// Renders a triangle stripped oval
void fxFoliageReplicator : : renderArc ( const F32 fRadiusX , const F32 fRadiusY )
{
PrimBuild : : begin ( GFXTriangleStrip , 720 ) ;
for ( U32 Angle = mCreationAreaAngle ; Angle < ( mCreationAreaAngle + 360 ) ; Angle + + )
{
F32 XPos , YPos ;
// Calculate Position.
XPos = fRadiusX * mCos ( mDegToRad ( - ( F32 ) Angle ) ) ;
YPos = fRadiusY * mSin ( mDegToRad ( - ( F32 ) Angle ) ) ;
// Set Colour.
PrimBuild : : color4f ( mFieldData . mPlaceAreaColour . red ,
mFieldData . mPlaceAreaColour . green ,
mFieldData . mPlaceAreaColour . blue ,
AREA_ANIMATION_ARC * ( Angle - mCreationAreaAngle ) ) ;
PrimBuild : : vertex3f ( XPos , YPos , - ( F32 ) mFieldData . mPlacementBandHeight / 2.0f ) ;
PrimBuild : : vertex3f ( XPos , YPos , + ( F32 ) mFieldData . mPlacementBandHeight / 2.0f ) ;
}
PrimBuild : : end ( ) ;
}
// This currently uses the primbuilder, could convert out, but why allocate the buffer if we
// never edit the misison?
void fxFoliageReplicator : : renderPlacementArea ( const F32 ElapsedTime )
{
if ( gEditingMission & & mFieldData . mShowPlacementArea )
{
GFX - > pushWorldMatrix ( ) ;
GFX - > multWorld ( getTransform ( ) ) ;
if ( ! mPlacementSB )
{
GFXStateBlockDesc transparent ;
transparent . setCullMode ( GFXCullNone ) ;
transparent . alphaTestEnable = true ;
transparent . setZReadWrite ( true ) ;
transparent . zWriteEnable = false ;
transparent . setBlend ( true , GFXBlendSrcAlpha , GFXBlendInvSrcAlpha ) ;
mPlacementSB = GFX - > createStateBlock ( transparent ) ;
}
GFX - > setStateBlock ( mPlacementSB ) ;
// Do we need to draw the Outer Radius?
if ( mFieldData . mOuterRadiusX | | mFieldData . mOuterRadiusY )
renderArc ( ( F32 ) mFieldData . mOuterRadiusX , ( F32 ) mFieldData . mOuterRadiusY ) ;
// Inner radius?
if ( mFieldData . mInnerRadiusX | | mFieldData . mInnerRadiusY )
renderArc ( ( F32 ) mFieldData . mInnerRadiusX , ( F32 ) mFieldData . mInnerRadiusY ) ;
GFX - > popWorldMatrix ( ) ;
mCreationAreaAngle = ( U32 ) ( mCreationAreaAngle + ( 1000 * ElapsedTime ) ) ;
mCreationAreaAngle = mCreationAreaAngle % 360 ;
}
}
void fxFoliageReplicator : : renderObject ( ObjectRenderInst * ri , SceneRenderState * state , BaseMatInstance * overrideMat )
{
if ( overrideMat )
return ;
if ( ! mShader )
return ;
// If we're rendering and we haven't placed any foliage yet - do it.
if ( ! mClientReplicationStarted )
{
Con : : warnf ( " fxFoliageReplicator::renderObject - tried to render a non replicated fxFoliageReplicator; replicating it now... " ) ;
StartUp ( ) ;
}
// Calculate Elapsed Time and take new Timestamp.
S32 Time = Platform : : getVirtualMilliseconds ( ) ;
F32 ElapsedTime = ( Time - mLastRenderTime ) * 0.001f ;
mLastRenderTime = Time ;
renderPlacementArea ( ElapsedTime ) ;
if ( mCurrentFoliageCount > 0 ) {
if ( mRenderSB . isNull ( ) | | mDirty )
{
mDirty = false ;
GFXStateBlockDesc desc ;
// Debug SB
desc . samplersDefined = true ;
mDebugSB = GFX - > createStateBlock ( desc ) ;
// Render SB
desc . samplers [ 1 ] . addressModeU = GFXAddressClamp ;
desc . samplers [ 1 ] . addressModeV = GFXAddressClamp ;
desc . setBlend ( true , GFXBlendSrcAlpha , GFXBlendInvSrcAlpha ) ;
desc . setAlphaTest ( true , GFXCmpGreater , ( U8 ) ( 255.0f * mFieldData . mAlphaCutoff ) ) ;
desc . setCullMode ( GFXCullNone ) ;
mRenderSB = GFX - > createStateBlock ( desc ) ;
}
if ( ! mFieldData . mHideFoliage ) {
// Animate Global Sway Phase (Modulus).
mGlobalSwayPhase = mGlobalSwayPhase + ( mGlobalSwayTimeRatio * ElapsedTime ) ;
// Animate Global Light Phase (Modulus).
mGlobalLightPhase = mGlobalLightPhase + ( mGlobalLightTimeRatio * ElapsedTime ) ;
// Compute other light parameters
const F32 LuminanceMidPoint = ( mFieldData . mMinLuminance + mFieldData . mMaxLuminance ) / 2.0f ;
const F32 LuminanceMagnitude = mFieldData . mMaxLuminance - LuminanceMidPoint ;
// Distances used in alpha
const F32 ClippedViewDistance = mFieldData . mViewDistance ;
const F32 MaximumViewDistance = ClippedViewDistance + mFieldData . mFadeInRegion ;
if ( mFoliageShaderConsts . isValid ( ) )
{
mFoliageShaderConsts - > setSafe ( mFoliageShaderGlobalSwayPhaseSC , mGlobalSwayPhase ) ;
mFoliageShaderConsts - > setSafe ( mFoliageShaderSwayMagnitudeSideSC , mFieldData . mSwayMagnitudeSide ) ;
mFoliageShaderConsts - > setSafe ( mFoliageShaderSwayMagnitudeFrontSC , mFieldData . mSwayMagnitudeFront ) ;
mFoliageShaderConsts - > setSafe ( mFoliageShaderGlobalLightPhaseSC , mGlobalLightPhase ) ;
mFoliageShaderConsts - > setSafe ( mFoliageShaderLuminanceMagnitudeSC , LuminanceMagnitude ) ;
mFoliageShaderConsts - > setSafe ( mFoliageShaderLuminanceMidpointSC , LuminanceMidPoint ) ;
// Set up our shader constants
// Projection matrix
MatrixF proj = GFX - > getProjectionMatrix ( ) ;
//proj.transpose();
mFoliageShaderConsts - > setSafe ( mFoliageShaderProjectionSC , proj ) ;
// World transform matrix
MatrixF world = GFX - > getWorldMatrix ( ) ;
//world.transpose();
mFoliageShaderConsts - > setSafe ( mFoliageShaderWorldSC , world ) ;
Point3F camPos = state - > getCameraPosition ( ) ;
mFoliageShaderConsts - > setSafe ( mFoliageShaderDistanceRangeSC , MaximumViewDistance ) ;
mFoliageShaderConsts - > setSafe ( mFoliageShaderCameraPosSC , camPos ) ;
mFoliageShaderConsts - > setSafe ( mFoliageShaderTrueBillboardSC , mFieldData . mUseTrueBillboards ? 1.0f : 0.0f ) ;
mFoliageShaderConsts - > setSafe ( mFoliageShaderGroundAlphaSC , Point4F ( mFieldData . mGroundAlpha , mFieldData . mGroundAlpha , mFieldData . mGroundAlpha , mFieldData . mGroundAlpha ) ) ;
if ( mFoliageShaderAmbientColorSC - > isValid ( ) )
2017-07-26 23:18:38 +00:00
{
LinearColorF ambient = state - > getAmbientLightColor ( ) ;
LinearColorF ambient_inv ( 1.0f - ambient . red , 1.0f - ambient . green , 1.0f - ambient . blue , 0.0f ) ;
ambient + = ambient_inv * ( 1.0f - mFieldData . mAmbientModulationBias ) ;
mFoliageShaderConsts - > set ( mFoliageShaderAmbientColorSC , ambient ) ;
}
2012-09-19 15:15:01 +00:00
GFX - > setShaderConstBuffer ( mFoliageShaderConsts ) ;
}
// Blend ops
// Set up our texture and color ops.
GFX - > setStateBlock ( mRenderSB ) ;
GFX - > setShader ( mShader ) ;
GFX - > setTexture ( mDiffuseTextureSC - > getSamplerRegister ( ) , mFieldData . mFoliageTexture ) ;
// computeAlphaTex(); // Uncomment if we figure out how to clamp to fogAndHaze
GFX - > setTexture ( mAlphaMapTextureSC - > getSamplerRegister ( ) , mAlphaTexture ) ;
// Setup our buffers
GFX - > setVertexBuffer ( mVertexBuffer ) ;
GFX - > setPrimitiveBuffer ( mPrimBuffer ) ;
// If we use culling, we're going to send chunks of our buffers to the card
if ( mFieldData . mUseCulling )
{
// Setup the Clip-Planes.
F32 FarClipPlane = getMin ( ( F32 ) state - > getFarPlane ( ) ,
mFieldData . mViewDistance + mFieldData . mFadeInRegion ) ;
mFrustumRenderSet . SetupClipPlanes ( state , FarClipPlane ) ;
renderQuad ( mFoliageQuadTree [ 0 ] , getRenderTransform ( ) , false ) ;
// Multipass, don't want to interrupt the vb state
if ( mFieldData . mUseDebugInfo )
{
// hey man, we're done, so it doesn't matter if we kill it to render the next part
GFX - > setStateBlock ( mDebugSB ) ;
renderQuad ( mFoliageQuadTree [ 0 ] , getRenderTransform ( ) , true ) ;
}
}
else
{
// Draw the whole shebang!
GFX - > drawIndexedPrimitive ( GFXTriangleList , 0 , 0 , mVertexBuffer - > mNumVerts ,
0 , mPrimBuffer - > mIndexCount / 3 ) ;
}
}
}
}
void fxFoliageReplicator : : renderQuad ( fxFoliageQuadrantNode * quadNode , const MatrixF & RenderTransform , const bool UseDebug )
{
if ( quadNode ! = NULL ) {
if ( mFrustumRenderSet . IsQuadrantVisible ( quadNode - > QuadrantBox , RenderTransform ) )
{
// Draw the Quad Box (Debug Only).
if ( UseDebug )
2017-06-23 16:36:20 +00:00
mFrustumRenderSet . DrawQuadBox ( quadNode - > QuadrantBox , LinearColorF ( 0.0f , 1.0f , 0.1f , 1.0f ) ) ;
2012-09-19 15:15:01 +00:00
if ( quadNode - > Level ! = 0 ) {
for ( U32 i = 0 ; i < 4 ; i + + )
renderQuad ( quadNode - > QuadrantChildNode [ i ] , RenderTransform , UseDebug ) ;
} else {
if ( ! UseDebug )
if ( quadNode - > primitiveCount )
GFX - > drawIndexedPrimitive ( GFXTriangleList , 0 , 0 , mVertexBuffer - > mNumVerts ,
quadNode - > startIndex , quadNode - > primitiveCount ) ;
}
} else {
// Use a different color to say "I think I'm not visible!"
if ( UseDebug )
2017-06-23 16:36:20 +00:00
mFrustumRenderSet . DrawQuadBox ( quadNode - > QuadrantBox , LinearColorF ( 1.0f , 0.8f , 0.1f , 1.0f ) ) ;
2012-09-19 15:15:01 +00:00
}
}
}
//------------------------------------------------------------------------------
// NETWORK
//------------------------------------------------------------------------------
U32 fxFoliageReplicator : : packUpdate ( NetConnection * con , U32 mask , BitStream * stream )
{
// Pack Parent.
U32 retMask = Parent : : packUpdate ( con , mask , stream ) ;
// Write Foliage Replication Flag.
if ( stream - > writeFlag ( mask & FoliageReplicationMask ) )
{
stream - > writeAffineTransform ( mObjToWorld ) ; // Foliage Master-Object Position.
stream - > writeFlag ( mFieldData . mUseDebugInfo ) ; // Foliage Debug Information Flag.
stream - > write ( mFieldData . mDebugBoxHeight ) ; // Foliage Debug Height.
stream - > write ( mFieldData . mSeed ) ; // Foliage Seed.
stream - > write ( mFieldData . mFoliageCount ) ; // Foliage Count.
stream - > write ( mFieldData . mFoliageRetries ) ; // Foliage Retries.
stream - > writeString ( mFieldData . mFoliageFile ) ; // Foliage File.
stream - > write ( mFieldData . mInnerRadiusX ) ; // Foliage Inner Radius X.
stream - > write ( mFieldData . mInnerRadiusY ) ; // Foliage Inner Radius Y.
stream - > write ( mFieldData . mOuterRadiusX ) ; // Foliage Outer Radius X.
stream - > write ( mFieldData . mOuterRadiusY ) ; // Foliage Outer Radius Y.
stream - > write ( mFieldData . mMinWidth ) ; // Foliage Minimum Width.
stream - > write ( mFieldData . mMaxWidth ) ; // Foliage Maximum Width.
stream - > write ( mFieldData . mMinHeight ) ; // Foliage Minimum Height.
stream - > write ( mFieldData . mMaxHeight ) ; // Foliage Maximum Height.
stream - > write ( mFieldData . mFixAspectRatio ) ; // Foliage Fix Aspect Ratio.
stream - > write ( mFieldData . mFixSizeToMax ) ; // Foliage Fix Size to Max.
stream - > write ( mFieldData . mOffsetZ ) ; // Foliage Offset Z.
stream - > writeFlag ( mFieldData . mRandomFlip ) ; // Foliage Random Flip.
stream - > writeFlag ( mFieldData . mUseTrueBillboards ) ; // Foliage faces the camera (including z axis)
stream - > write ( mFieldData . mUseCulling ) ; // Foliage Use Culling.
stream - > write ( mFieldData . mCullResolution ) ; // Foliage Cull Resolution.
stream - > write ( mFieldData . mViewDistance ) ; // Foliage View Distance.
stream - > write ( mFieldData . mViewClosest ) ; // Foliage View Closest.
stream - > write ( mFieldData . mFadeInRegion ) ; // Foliage Fade-In Region.
stream - > write ( mFieldData . mFadeOutRegion ) ; // Foliage Fade-Out Region.
stream - > write ( mFieldData . mAlphaCutoff ) ; // Foliage Alpha Cutoff.
stream - > write ( mFieldData . mGroundAlpha ) ; // Foliage Ground Alpha.
stream - > writeFlag ( mFieldData . mSwayOn ) ; // Foliage Sway On Flag.
stream - > writeFlag ( mFieldData . mSwaySync ) ; // Foliage Sway Sync Flag.
stream - > write ( mFieldData . mSwayMagnitudeSide ) ; // Foliage Sway Magnitude Side2Side.
stream - > write ( mFieldData . mSwayMagnitudeFront ) ; // Foliage Sway Magnitude Front2Back.
stream - > write ( mFieldData . mMinSwayTime ) ; // Foliage Minimum Sway Time.
stream - > write ( mFieldData . mMaxSwayTime ) ; // Foliage Maximum way Time.
stream - > writeFlag ( mFieldData . mLightOn ) ; // Foliage Light On Flag.
stream - > writeFlag ( mFieldData . mLightSync ) ; // Foliage Light Sync
stream - > write ( mFieldData . mMinLuminance ) ; // Foliage Minimum Luminance.
stream - > write ( mFieldData . mMaxLuminance ) ; // Foliage Maximum Luminance.
stream - > write ( mFieldData . mLightTime ) ; // Foliage Light Time.
stream - > writeFlag ( mFieldData . mAllowOnTerrain ) ; // Allow on Terrain.
stream - > writeFlag ( mFieldData . mAllowStatics ) ; // Allow on Statics.
stream - > writeFlag ( mFieldData . mAllowOnWater ) ; // Allow on Water.
stream - > writeFlag ( mFieldData . mAllowWaterSurface ) ; // Allow on Water Surface.
stream - > write ( mFieldData . mAllowedTerrainSlope ) ; // Foliage Offset Z.
stream - > writeFlag ( mFieldData . mHideFoliage ) ; // Hide Foliage.
stream - > writeFlag ( mFieldData . mShowPlacementArea ) ; // Show Placement Area Flag.
stream - > write ( mFieldData . mPlacementBandHeight ) ; // Placement Area Height.
stream - > write ( mFieldData . mPlaceAreaColour ) ; // Placement Area Colour.
2017-07-26 23:18:38 +00:00
stream - > write ( mFieldData . mAmbientModulationBias ) ;
2012-09-19 15:15:01 +00:00
}
// Were done ...
return ( retMask ) ;
}
//------------------------------------------------------------------------------
void fxFoliageReplicator : : unpackUpdate ( NetConnection * con , BitStream * stream )
{
// Unpack Parent.
Parent : : unpackUpdate ( con , stream ) ;
// Read Replication Details.
if ( stream - > readFlag ( ) )
{
MatrixF ReplicatorObjectMatrix ;
stream - > readAffineTransform ( & ReplicatorObjectMatrix ) ; // Foliage Master Object Position.
mFieldData . mUseDebugInfo = stream - > readFlag ( ) ; // Foliage Debug Information Flag.
stream - > read ( & mFieldData . mDebugBoxHeight ) ; // Foliage Debug Height.
stream - > read ( & mFieldData . mSeed ) ; // Foliage Seed.
stream - > read ( & mFieldData . mFoliageCount ) ; // Foliage Count.
stream - > read ( & mFieldData . mFoliageRetries ) ; // Foliage Retries.
mFieldData . mFoliageFile = stream - > readSTString ( ) ; // Foliage File.
stream - > read ( & mFieldData . mInnerRadiusX ) ; // Foliage Inner Radius X.
stream - > read ( & mFieldData . mInnerRadiusY ) ; // Foliage Inner Radius Y.
stream - > read ( & mFieldData . mOuterRadiusX ) ; // Foliage Outer Radius X.
stream - > read ( & mFieldData . mOuterRadiusY ) ; // Foliage Outer Radius Y.
stream - > read ( & mFieldData . mMinWidth ) ; // Foliage Minimum Width.
stream - > read ( & mFieldData . mMaxWidth ) ; // Foliage Maximum Width.
stream - > read ( & mFieldData . mMinHeight ) ; // Foliage Minimum Height.
stream - > read ( & mFieldData . mMaxHeight ) ; // Foliage Maximum Height.
stream - > read ( & mFieldData . mFixAspectRatio ) ; // Foliage Fix Aspect Ratio.
stream - > read ( & mFieldData . mFixSizeToMax ) ; // Foliage Fix Size to Max.
stream - > read ( & mFieldData . mOffsetZ ) ; // Foliage Offset Z.
mFieldData . mRandomFlip = stream - > readFlag ( ) ; // Foliage Random Flip.
bool wasTrueBB = mFieldData . mUseTrueBillboards ;
mFieldData . mUseTrueBillboards = stream - > readFlag ( ) ; // Foliage is camera facing (including z axis).
stream - > read ( & mFieldData . mUseCulling ) ; // Foliage Use Culling.
stream - > read ( & mFieldData . mCullResolution ) ; // Foliage Cull Resolution.
stream - > read ( & mFieldData . mViewDistance ) ; // Foliage View Distance.
stream - > read ( & mFieldData . mViewClosest ) ; // Foliage View Closest.
stream - > read ( & mFieldData . mFadeInRegion ) ; // Foliage Fade-In Region.
stream - > read ( & mFieldData . mFadeOutRegion ) ; // Foliage Fade-Out Region.
stream - > read ( & mFieldData . mAlphaCutoff ) ; // Foliage Alpha Cutoff.
stream - > read ( & mFieldData . mGroundAlpha ) ; // Foliage Ground Alpha.
mFieldData . mSwayOn = stream - > readFlag ( ) ; // Foliage Sway On Flag.
mFieldData . mSwaySync = stream - > readFlag ( ) ; // Foliage Sway Sync Flag.
stream - > read ( & mFieldData . mSwayMagnitudeSide ) ; // Foliage Sway Magnitude Side2Side.
stream - > read ( & mFieldData . mSwayMagnitudeFront ) ; // Foliage Sway Magnitude Front2Back.
stream - > read ( & mFieldData . mMinSwayTime ) ; // Foliage Minimum Sway Time.
stream - > read ( & mFieldData . mMaxSwayTime ) ; // Foliage Maximum way Time.
mFieldData . mLightOn = stream - > readFlag ( ) ; // Foliage Light On Flag.
mFieldData . mLightSync = stream - > readFlag ( ) ; // Foliage Light Sync
stream - > read ( & mFieldData . mMinLuminance ) ; // Foliage Minimum Luminance.
stream - > read ( & mFieldData . mMaxLuminance ) ; // Foliage Maximum Luminance.
stream - > read ( & mFieldData . mLightTime ) ; // Foliage Light Time.
mFieldData . mAllowOnTerrain = stream - > readFlag ( ) ; // Allow on Terrain.
mFieldData . mAllowStatics = stream - > readFlag ( ) ; // Allow on Statics.
mFieldData . mAllowOnWater = stream - > readFlag ( ) ; // Allow on Water.
mFieldData . mAllowWaterSurface = stream - > readFlag ( ) ; // Allow on Water Surface.
stream - > read ( & mFieldData . mAllowedTerrainSlope ) ; // Allowed Terrain Slope.
mFieldData . mHideFoliage = stream - > readFlag ( ) ; // Hide Foliage.
mFieldData . mShowPlacementArea = stream - > readFlag ( ) ; // Show Placement Area Flag.
stream - > read ( & mFieldData . mPlacementBandHeight ) ; // Placement Area Height.
stream - > read ( & mFieldData . mPlaceAreaColour ) ;
2017-07-26 23:18:38 +00:00
stream - > read ( & mFieldData . mAmbientModulationBias ) ;
2012-09-19 15:15:01 +00:00
// Calculate Fade-In/Out Gradients.
mFadeInGradient = 1.0f / mFieldData . mFadeInRegion ;
mFadeOutGradient = 1.0f / mFieldData . mFadeOutRegion ;
// Set Transform.
setTransform ( ReplicatorObjectMatrix ) ;
// Load Foliage Texture on the client.
if ( mFieldData . mFoliageFile ! = NULL & & dStrlen ( mFieldData . mFoliageFile ) > 0 )
2017-06-23 16:36:20 +00:00
mFieldData . mFoliageTexture = GFXTexHandle ( mFieldData . mFoliageFile , & GFXStaticTextureSRGBProfile , avar ( " %s() - mFieldData.mFoliageTexture (line %d) " , __FUNCTION__ , __LINE__ ) ) ;
2012-09-19 15:15:01 +00:00
if ( ( GFXTextureObject * ) mFieldData . mFoliageTexture = = NULL )
Con : : printf ( " fxFoliageReplicator: %s is an invalid or missing foliage texture file. " , mFieldData . mFoliageFile ) ;
// Set Quad-Tree Box Height Lerp.
mFrustumRenderSet . mHeightLerp = mFieldData . mDebugBoxHeight ;
// Create Foliage (if Replication has begun).
if ( mClientReplicationStarted )
{
CreateFoliage ( ) ;
mDirty = true ;
}
if ( isProperlyAdded ( ) & & mFieldData . mUseTrueBillboards ! = wasTrueBB )
SetupShader ( ) ;
}
}