fxFoliageReplicator.cpp 74 KB

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