fxFoliageReplicator.cpp 75 KB

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