1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834 |
- //-----------------------------------------------------------------------------
- // 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
- //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
- // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
- // Copyright (C) 2015 Faust Logic, Inc.
- //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
- #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();
- // }
- //
- //------------------------------------------------------------------------------
- //
- // Put this in /example/common/editor/EditorGui.tscript in [function Creator::init( %this )]
- //
- // %Environment_Item[8] = "fxFoliageReplicator"; <-- ADD THIS.
- //
- //------------------------------------------------------------------------------
- //
- // Put this in /example/common/client/missionDownload.tscript in [function clientCmdMissionStartPhase3(%seq,%missionName)] (line 65)
- // 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;
- const Frustum& frustum = state->getCullingFrustum();
- // [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();
- }
- //------------------------------------------------------------------------------
- inline void fxFoliageRenderList::DrawQuadBox(const Box3F& QuadBox, const LinearColorF Colour)
- {
- // 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);
- }
- //------------------------------------------------------------------------------
- void fxFoliageCulledList::FindCandidates(const Box3F& SearchBox, fxFoliageCulledList* InVec)
- {
- // 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;
- dMemset(&mFrustumRenderSet, 0, sizeof(mFrustumRenderSet));
- // 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;
- mQuadTreeLevels = 0;
- mNextAllocatedNodeIdx = 0;
- mAlphaLookup = NULL;
- mFadeInGradient = 0.0f;
- mFadeOutGradient = 0.0f;
- mGlobalSwayPhase = 0.0f;
- mGlobalSwayTimeRatio = 1.0f;
- mGlobalLightPhase = 0.0f;
- mGlobalLightTimeRatio = 1.0f;
- 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;
- mDiffuseTextureSC = NULL;
- mAlphaMapTextureSC = NULL;
- mShaderData = NULL;
- }
- //------------------------------------------------------------------------------
- fxFoliageReplicator::~fxFoliageReplicator()
- {
- if (mAlphaLookup)
- delete mAlphaLookup;
- mPlacementSB = NULL;
- }
- //------------------------------------------------------------------------------
- void fxFoliageReplicator::initPersistFields()
- {
- docsURL;
- // 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." );
- addField( "DebugBoxHeight", TypeF32, Offset( mFieldData.mDebugBoxHeight, fxFoliageReplicator ), "Height multiplier for drawn culling bins.");
- 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." );
- addField( "PlacementAreaHeight", TypeS32, Offset( mFieldData.mPlacementBandHeight, fxFoliageReplicator ), "Height of the placement ring in world units." );
- 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." );
- addField( "FoliageCount", TypeS32, Offset( mFieldData.mFoliageCount, fxFoliageReplicator ), "Maximum foliage instance count." );
- addField( "FoliageRetries", TypeS32, Offset( mFieldData.mFoliageRetries, fxFoliageReplicator ), "Number of times to try placing a foliage instance before giving up." );
- endGroup( "Media" ); // MM: Added Group Footer.
- addGroup( "Area" ); // MM: Added Group Header.
- addField( "InnerRadiusX", TypeS32, Offset( mFieldData.mInnerRadiusX, fxFoliageReplicator ), "Placement area inner radius on the X axis" );
- addField( "InnerRadiusY", TypeS32, Offset( mFieldData.mInnerRadiusY, fxFoliageReplicator ), "Placement area inner radius on the Y axis" );
- addField( "OuterRadiusX", TypeS32, Offset( mFieldData.mOuterRadiusX, fxFoliageReplicator ), "Placement area outer radius on the X axis" );
- addField( "OuterRadiusY", TypeS32, Offset( mFieldData.mOuterRadiusY, fxFoliageReplicator ), "Placement area outer radius on the Y axis" );
- endGroup( "Area" ); // MM: Added Group Footer.
- addGroup( "Dimensions" ); // MM: Added Group Header.
- addField( "MinWidth", TypeF32, Offset( mFieldData.mMinWidth, fxFoliageReplicator ), "Minimum width of foliage billboards" );
- addField( "MaxWidth", TypeF32, Offset( mFieldData.mMaxWidth, fxFoliageReplicator ), "Maximum width of foliage billboards" );
- addField( "MinHeight", TypeF32, Offset( mFieldData.mMinHeight, fxFoliageReplicator ), "Minimum height of foliage billboards" );
- addField( "MaxHeight", TypeF32, Offset( mFieldData.mMaxHeight, fxFoliageReplicator ), "Maximum height of foliage billboards" );
- 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." );
- addField( "OffsetZ", TypeF32, Offset( mFieldData.mOffsetZ, fxFoliageReplicator ), "Offset billboards by this amount vertically." );
- 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." );
- addField( "ViewDistance", TypeF32, Offset( mFieldData.mViewDistance, fxFoliageReplicator ), "Maximum distance from camera where foliage appears." );
- addField( "ViewClosest", TypeF32, Offset( mFieldData.mViewClosest, fxFoliageReplicator ), "Minimum distance from camera where foliage appears." );
- addField( "FadeInRegion", TypeF32, Offset( mFieldData.mFadeInRegion, fxFoliageReplicator ), "Region beyond ViewDistance where foliage fades in/out." );
- addField( "FadeOutRegion", TypeF32, Offset( mFieldData.mFadeOutRegion, fxFoliageReplicator ), "Region before ViewClosest where foliage fades in/out." );
- addField( "AlphaCutoff", TypeF32, Offset( mFieldData.mAlphaCutoff, fxFoliageReplicator ), "Minimum alpha value allowed on foliage instances." );
- addField( "GroundAlpha", TypeF32, Offset( mFieldData.mGroundAlpha, fxFoliageReplicator ), "Alpha of the foliage at ground level. 0 = transparent, 1 = opaque." );
- 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." );
- addField( "SwayMagSide", TypeF32, Offset( mFieldData.mSwayMagnitudeSide, fxFoliageReplicator ), "Left-to-right sway magnitude." );
- addField( "SwayMagFront", TypeF32, Offset( mFieldData.mSwayMagnitudeFront, fxFoliageReplicator ), "Front-to-back sway magnitude." );
- addField( "MinSwayTime", TypeF32, Offset( mFieldData.mMinSwayTime, fxFoliageReplicator ), "Minumum sway cycle time in seconds." );
- addField( "MaxSwayTime", TypeF32, Offset( mFieldData.mMaxSwayTime, fxFoliageReplicator ), "Maximum sway cycle time in seconds." );
- 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." );
- addField( "MinLuminance", TypeF32, Offset( mFieldData.mMinLuminance, fxFoliageReplicator ), "Minimum luminance for foliage instances." );
- addField( "MaxLuminance", TypeF32, Offset( mFieldData.mMaxLuminance, fxFoliageReplicator ), "Maximum luminance for foliage instances." );
- addField( "LightTime", TypeF32, Offset( mFieldData.mLightTime, fxFoliageReplicator ), "Time before foliage illumination cycle repeats." );
- 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." );
- addField( "AllowedTerrainSlope", TypeS32, Offset( mFieldData.mAllowedTerrainSlope, fxFoliageReplicator ), "Maximum surface angle allowed for foliage instances." );
- endGroup( "Restrictions" ); // MM: Added Group Footer.
- addGroup( "AFX" );
- addField( "AmbientModulationBias", TypeF32, Offset( mFieldData.mAmbientModulationBias,fxFoliageReplicator ), "Multiplier controling amount foliage is modulated by sun's ambient." );
- endGroup( "AFX" );
- // 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.
- AssertFatal(isClientObject(), "Trying to create Foliage on Server, this is bad!");
- // 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();
- }
- //------------------------------------------------------------------------------
- Box3F fxFoliageReplicator::FetchQuadrant(const Box3F& Box, U32 Quadrant)
- {
- 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.
- AssertFatal(isServerObject(), "We *MUST* be on server when Synchronising 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).
- 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.
- AssertFatal(isClientObject(), "Trying to destroy Foliage on Server, this is bad!");
- // 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 )
- mFieldData.mFoliageTexture = GFXTexHandle( mFieldData.mFoliageFile, &GFXStaticTextureSRGBProfile, avar("%s() - mFieldData.mFoliageTexture (line %d)", __FUNCTION__, __LINE__) );
- 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);
- }
- mAlphaTexture.set(mAlphaLookup, &GFXStaticTextureSRGBProfile, false, String("fxFoliage Replicator Alpha Texture") );
- }
- // 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())
- {
- 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);
- }
- 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)
- mFrustumRenderSet.DrawQuadBox(quadNode->QuadrantBox, LinearColorF(0.0f, 1.0f, 0.1f, 1.0f));
- 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)
- mFrustumRenderSet.DrawQuadBox(quadNode->QuadrantBox, LinearColorF(1.0f, 0.8f, 0.1f, 1.0f));
- }
- }
- }
- //------------------------------------------------------------------------------
- // 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.
- stream->write(mFieldData.mAmbientModulationBias);
- }
- // 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);
- stream->read(&mFieldData.mAmbientModulationBias);
- // 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 )
- mFieldData.mFoliageTexture = GFXTexHandle( mFieldData.mFoliageFile, &GFXStaticTextureSRGBProfile, avar("%s() - mFieldData.mFoliageTexture (line %d)", __FUNCTION__, __LINE__) );
- 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();
- }
- }
|