groundCover.cpp 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708
  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. #include "platform/platform.h"
  23. #include "T3D/fx/groundCover.h"
  24. #include "core/resourceManager.h"
  25. #include "core/stream/bitStream.h"
  26. #include "console/consoleTypes.h"
  27. #include "scene/sceneRenderState.h"
  28. #include "terrain/terrData.h"
  29. #include "renderInstance/renderPassManager.h"
  30. #include "gfx/gfxDrawUtil.h"
  31. #include "gfx/primBuilder.h"
  32. #include "T3D/gameBase/gameConnection.h"
  33. #include "gfx/gfxVertexBuffer.h"
  34. #include "gfx/gfxStructs.h"
  35. #include "ts/tsShapeInstance.h"
  36. #include "lighting/lightManager.h"
  37. #include "lighting/lightInfo.h"
  38. #include "materials/shaderData.h"
  39. #include "gfx/gfxTransformSaver.h"
  40. #include "shaderGen/shaderGenVars.h"
  41. #include "materials/matTextureTarget.h"
  42. #include "gfx/util/screenspace.h"
  43. #include "materials/materialDefinition.h"
  44. #include "materials/materialManager.h"
  45. #include "materials/sceneData.h"
  46. #include "materials/materialFeatureTypes.h"
  47. #include "materials/matInstance.h"
  48. #include "renderInstance/renderPrePassMgr.h"
  49. #include "console/engineAPI.h"
  50. /// This is used for rendering ground cover billboards.
  51. GFXImplementVertexFormat( GCVertex )
  52. {
  53. addElement( "POSITION", GFXDeclType_Float3 );
  54. addElement( "NORMAL", GFXDeclType_Float3 );
  55. addElement( "COLOR", GFXDeclType_Color );
  56. addElement( "TEXCOORD", GFXDeclType_Float4, 0 );
  57. };
  58. GroundCoverShaderConstHandles::GroundCoverShaderConstHandles()
  59. : mGroundCover( NULL ),
  60. mTypeRectsSC( NULL ),
  61. mFadeSC( NULL ),
  62. mWindDirSC( NULL ),
  63. mGustInfoSC( NULL ),
  64. mTurbInfoSC( NULL ),
  65. mCamRightSC( NULL ),
  66. mCamUpSC( NULL )
  67. {
  68. }
  69. void GroundCoverShaderConstHandles::init( GFXShader *shader )
  70. {
  71. mTypeRectsSC = shader->getShaderConstHandle( "$gc_typeRects" );
  72. mFadeSC = shader->getShaderConstHandle( "$gc_fadeParams" );
  73. mWindDirSC = shader->getShaderConstHandle( "$gc_windDir" );
  74. mGustInfoSC = shader->getShaderConstHandle( "$gc_gustInfo" );
  75. mTurbInfoSC = shader->getShaderConstHandle( "$gc_turbInfo" );
  76. mCamRightSC = shader->getShaderConstHandle( "$gc_camRight" );
  77. mCamUpSC = shader->getShaderConstHandle( "$gc_camUp" );
  78. }
  79. void GroundCoverShaderConstHandles::setConsts( SceneRenderState *state, const SceneData &sgData, GFXShaderConstBuffer *buffer )
  80. {
  81. AlignedArray<Point4F> rectData( MAX_COVERTYPES, sizeof( Point4F ), (U8*)(mGroundCover->mBillboardRects), false );
  82. buffer->setSafe( mTypeRectsSC, rectData );
  83. const GroundCoverShaderConstData &data = mGroundCover->getShaderConstData();
  84. buffer->setSafe( mFadeSC, data.fadeInfo );
  85. buffer->setSafe( mWindDirSC, mGroundCover->mWindDirection );
  86. buffer->setSafe( mGustInfoSC, data.gustInfo );
  87. buffer->setSafe( mTurbInfoSC, data.turbInfo );
  88. buffer->setSafe( mCamRightSC, data.camRight );
  89. buffer->setSafe( mCamUpSC, data.camUp );
  90. }
  91. /// This defines one grid cell.
  92. class GroundCoverCell
  93. {
  94. protected:
  95. friend class GroundCover;
  96. struct Placement
  97. {
  98. Point3F point;
  99. Point3F normal;
  100. Point3F size;
  101. F32 rotation;
  102. U32 type;
  103. F32 windAmplitude;
  104. Box3F worldBox;
  105. ColorF lmColor;
  106. };
  107. /// This is the x,y index for this cell.
  108. Point2I mIndex;
  109. /// The worldspace bounding box this cell.
  110. Box3F mBounds;
  111. /// The worldspace bounding box of the renderable
  112. /// content within this cell.
  113. Box3F mRenderBounds;
  114. /// The instances of billboard cover elements in this cell.
  115. Vector<Placement> mBillboards;
  116. /// The instances of shape cover elements in this cell.
  117. Vector<Placement> mShapes;
  118. typedef GFXVertexBufferHandle<GCVertex> VBHandle;
  119. typedef Vector< VBHandle > VBHandleVector;
  120. /// The vertex buffers that hold all the
  121. /// prepared billboards for this cell.
  122. VBHandleVector mVBs;
  123. /// Used to mark the cell dirty and in need
  124. /// of a rebuild.
  125. bool mDirty;
  126. /// Repacks the billboards into the vertex buffer.
  127. void _rebuildVB();
  128. public:
  129. GroundCoverCell() : mDirty(false) {}
  130. ~GroundCoverCell()
  131. {
  132. mVBs.clear();
  133. }
  134. const Point2I& shiftIndex( const Point2I& shift ) { return mIndex += shift; }
  135. /// The worldspace bounding box this cell.
  136. const Box3F& getBounds() const { return mBounds; }
  137. /// The worldspace bounding box of the renderable
  138. /// content within this cell.
  139. const Box3F& getRenderBounds() const { return mRenderBounds; }
  140. Point3F getCenter() const { return mBounds.getCenter(); }
  141. VectorF getSize() const { return VectorF( mBounds.len_x() / 2.0f,
  142. mBounds.len_y() / 2.0f,
  143. mBounds.len_z() / 2.0f ); }
  144. void renderBillboards( SceneRenderState *state, BaseMatInstance *mat, GFXPrimitiveBufferHandle *pb );
  145. U32 renderShapes( const TSRenderState &rdata,
  146. Frustum *culler,
  147. TSShapeInstance** shapes );
  148. };
  149. void GroundCoverCell::_rebuildVB()
  150. {
  151. if ( mBillboards.empty() )
  152. return;
  153. PROFILE_SCOPE(GroundCover_RebuildVB);
  154. // The maximum verts we can put in one vertex buffer batch.
  155. const U32 MAX_BILLBOARDS = 0xFFFF / 4;
  156. // How many batches will we need in total?
  157. const U32 batches = mCeil( (F32)mBillboards.size() / (F32)MAX_BILLBOARDS );
  158. // So... how many billboards do we need in
  159. // each batch? We're trying to evenly divide
  160. // the amount across all the VBs.
  161. const U32 batchBB = mBillboards.size() / batches;
  162. // Init the vertex buffer list to the right size. Any
  163. // VBs already in there will remain unless we're truncating
  164. // the list... those are freed.
  165. mVBs.setSize( batches );
  166. // Get the iter to the first billboard.
  167. Vector<Placement>::const_iterator iter = mBillboards.begin();
  168. // Prepare each batch.
  169. U32 bb, remaining = mBillboards.size();
  170. for ( U32 b = 0; b < batches; b++ )
  171. {
  172. // Grab a reference to the vb.
  173. VBHandle &vb = mVBs[b];
  174. // How many billboards in this batch?
  175. bb = getMin( batchBB, remaining );
  176. remaining -= bb;
  177. // Ok... now how many verts is that?
  178. const U32 verts = bb * 4;
  179. // Create the VB hasn't been created or if its
  180. // too small then resize it.
  181. if ( vb.isNull() || vb->mNumVerts < verts )
  182. {
  183. PROFILE_START(GroundCover_CreateVB);
  184. vb.set( GFX, verts, GFXBufferTypeStatic );
  185. PROFILE_END();
  186. }
  187. // Fill this puppy!
  188. GCVertex* vertPtr = vb.lock( 0, verts );
  189. GFXVertexColor color;
  190. Vector<Placement>::const_iterator last = iter + bb;
  191. for ( ; iter != last; iter++ )
  192. {
  193. const Point3F &position = (*iter).point;
  194. const Point3F &normal = (*iter).normal;
  195. const S32 &type = (*iter).type;
  196. const Point3F &size = (*iter).size;
  197. const F32 &windAmplitude = (*iter).windAmplitude;
  198. GFXVertexColor color = (ColorI)(*iter).lmColor;
  199. U8 *col = (U8 *)const_cast<U32 *>( (const U32 *)color );
  200. vertPtr->point = position;
  201. vertPtr->normal = normal;
  202. vertPtr->params.x = size.x;
  203. vertPtr->params.y = size.y;
  204. vertPtr->params.z = type;
  205. vertPtr->params.w = 0;
  206. col[3] = 0;
  207. vertPtr->ambient = color;
  208. ++vertPtr;
  209. vertPtr->point = position;
  210. vertPtr->normal = normal;
  211. vertPtr->params.x = size.x;
  212. vertPtr->params.y = size.y;
  213. vertPtr->params.z = type;
  214. vertPtr->params.w = 0;
  215. col[3] = 1;
  216. vertPtr->ambient = color;
  217. ++vertPtr;
  218. vertPtr->point = position;
  219. vertPtr->normal = normal;
  220. vertPtr->params.x = size.x;
  221. vertPtr->params.y = size.y;
  222. vertPtr->params.z = type;
  223. vertPtr->params.w = windAmplitude;
  224. col[3] = 2;
  225. vertPtr->ambient = color;
  226. ++vertPtr;
  227. vertPtr->point = position;
  228. vertPtr->normal = normal;
  229. vertPtr->params.x = size.x;
  230. vertPtr->params.y = size.y;
  231. vertPtr->params.z = type;
  232. vertPtr->params.w = windAmplitude;
  233. col[3] = 3;
  234. vertPtr->ambient = color;
  235. ++vertPtr;
  236. }
  237. vb.unlock();
  238. }
  239. }
  240. U32 GroundCoverCell::renderShapes( const TSRenderState &rdata,
  241. Frustum *culler,
  242. TSShapeInstance** shapes )
  243. {
  244. MatrixF worldMat;
  245. TSShapeInstance* shape;
  246. Point3F camVector;
  247. F32 dist;
  248. F32 invScale;
  249. const SceneRenderState *state = rdata.getSceneState();
  250. U32 totalRendered = 0;
  251. Vector<Placement>::const_iterator iter = mShapes.begin();
  252. for ( ; iter != mShapes.end(); iter++ )
  253. {
  254. // Grab a reference here once.
  255. const Placement& inst = (*iter);
  256. // If we were pass a culler then us it to test the shape world box.
  257. if ( culler && culler->isCulled( inst.worldBox ) )
  258. continue;
  259. shape = shapes[ inst.type ];
  260. camVector = inst.point - state->getDiffuseCameraPosition();
  261. dist = getMax( camVector.len(), 0.01f );
  262. worldMat.set( EulerF(0, 0, inst.rotation), inst.point );
  263. // TSShapeInstance::render() uses the
  264. // world matrix for the RenderInst.
  265. worldMat.scale( inst.size );
  266. GFX->setWorldMatrix( worldMat );
  267. // Obey the normal screen space lod metrics. The shapes should
  268. // be tuned to lod out quickly for ground cover.
  269. //
  270. // Note: The profile doesn't indicate that lod selection is
  271. // very expensive... in fact its less than 1/10th of the cost
  272. // of the render() call below.
  273. PROFILE_START(GroundCover_RenderShapes_SelectDetail);
  274. invScale = (1.0f/getMax(getMax(inst.size.x,inst.size.y),inst.size.z));
  275. shape->setDetailFromDistance( state, dist * invScale );
  276. PROFILE_END(); // GroundCover_RenderShapes_SelectDetail
  277. // Note: This is the most expensive call of this loop. We
  278. // need to rework the render call completely to optimize it.
  279. PROFILE_START(GroundCover_RenderShapes_Render);
  280. shape->render( rdata );
  281. PROFILE_END(); // GroundCover_RenderShapes_Render
  282. totalRendered++;
  283. }
  284. return totalRendered;
  285. }
  286. void GroundCoverCell::renderBillboards( SceneRenderState *state, BaseMatInstance *mat, GFXPrimitiveBufferHandle *pb )
  287. {
  288. if ( mDirty )
  289. {
  290. _rebuildVB();
  291. mDirty = false;
  292. }
  293. // Do we have anything to render?
  294. if ( mBillboards.size() == 0 || mVBs.empty() || !mat )
  295. return;
  296. // TODO: Maybe add support for non-facing billboards
  297. // with random rotations and optional crosses. We could
  298. // stick them into the buffer after the normal billboards,
  299. // then change shader consts.
  300. RenderPassManager *pass = state->getRenderPass();
  301. // Draw each batch.
  302. U32 remaining = mBillboards.size();
  303. const U32 batches = mVBs.size();
  304. const U32 batchBB = remaining / batches;
  305. for ( U32 b = 0; b < batches; b++ )
  306. {
  307. // Grab a reference to the vb.
  308. VBHandle &vb = mVBs[b];
  309. // How many billboards in this batch?
  310. U32 bb = getMin( batchBB, remaining );
  311. remaining -= bb;
  312. MeshRenderInst *ri = pass->allocInst<MeshRenderInst>();
  313. ri->type = RenderPassManager::RIT_Mesh;
  314. ri->matInst = mat;
  315. ri->vertBuff = &vb;
  316. ri->primBuff = pb;
  317. ri->objectToWorld = &MatrixF::Identity;
  318. ri->worldToCamera = pass->allocSharedXform(RenderPassManager::View);
  319. ri->projection = pass->allocSharedXform(RenderPassManager::Projection);
  320. ri->defaultKey = mat->getStateHint();
  321. ri->prim = pass->allocPrim();
  322. ri->prim->numPrimitives = bb * 2;
  323. ri->prim->numVertices = bb * 4;
  324. ri->prim->startIndex = 0;
  325. ri->prim->startVertex = 0;
  326. ri->prim->minIndex = 0;
  327. ri->prim->type = GFXTriangleList;
  328. // If we need lights then set them up.
  329. if ( mat->isForwardLit() )
  330. {
  331. LightQuery query;
  332. query.init( mBounds );
  333. query.getLights( ri->lights, 8 );
  334. }
  335. pass->addInst( ri );
  336. GroundCover::smStatRenderedBatches++;
  337. GroundCover::smStatRenderedBillboards += bb;
  338. }
  339. GroundCover::smStatRenderedCells++;
  340. }
  341. U32 GroundCover::smStatRenderedCells = 0;
  342. U32 GroundCover::smStatRenderedBillboards = 0;
  343. U32 GroundCover::smStatRenderedBatches = 0;
  344. U32 GroundCover::smStatRenderedShapes = 0;
  345. F32 GroundCover::smDensityScale = 1.0f;
  346. ConsoleDocClass( GroundCover,
  347. "@brief Covers the ground in a field of objects (IE: Grass, Flowers, etc)."
  348. "@ingroup Foliage\n"
  349. );
  350. GroundCover::GroundCover()
  351. {
  352. mTypeMask |= StaticObjectType | StaticShapeObjectType;
  353. mNetFlags.set( Ghostable | ScopeAlways );
  354. mRadius = 200.0f;
  355. mZOffset = 0.0f;
  356. mFadeRadius = 50.0f;
  357. mShapeCullRadius = 75.0f;
  358. mShapesCastShadows = true;
  359. mReflectRadiusScale = 0.25f;
  360. mGridSize = 7;
  361. // By initializing this to a big value we
  362. // ensure we warp on first render.
  363. mGridIndex.set( S32_MAX, S32_MAX );
  364. mMaxPlacement = 1000;
  365. mLastPlacementCount = 0;
  366. mDebugRenderCells = false;
  367. mDebugNoBillboards = false;
  368. mDebugNoShapes = false;
  369. mDebugLockFrustum = false;
  370. mRandomSeed = 1;
  371. mMaterial = NULL;
  372. mMatInst = NULL;
  373. mMatParams = NULL;
  374. mTypeRectsParam = NULL;
  375. mFadeParams = NULL;
  376. mWindDirParam = NULL;
  377. mGustInfoParam = NULL;
  378. mTurbInfoParam = NULL;
  379. mCamRightParam = NULL;
  380. mCamUpParam = NULL;
  381. mMaxBillboardTiltAngle = 90.0f;
  382. // TODO: This really doesn't belong here... we need a
  383. // real wind system for Torque scenes. This data
  384. // would be part of a global scene wind or area wind
  385. // emitter.
  386. //
  387. // Tom Spilman - 10/16/2007
  388. mWindGustLength = 20.0f;
  389. mWindGustFrequency = 0.5f;
  390. mWindGustStrength = 0.5f;
  391. mWindDirection.set( 1.0f, 0.0f );
  392. mWindTurbulenceFrequency = 1.2f;
  393. mWindTurbulenceStrength = 0.125f;
  394. for ( S32 i=0; i < MAX_COVERTYPES; i++ )
  395. {
  396. mProbability[i] = 0.0f;
  397. mSizeMin[i] = 1.0f;
  398. mSizeMax[i] = 1.0f;
  399. mSizeExponent[i] = 1.0f;
  400. mWindScale[i] = 1.0f;
  401. mMaxSlope[i] = 0.0f;
  402. mMinElevation[i] = -99999.0f;
  403. mMaxElevation[i] = 99999.0f;
  404. mLayer[i] = StringTable->EmptyString();
  405. mInvertLayer[i] = false;
  406. mMinClumpCount[i] = 1;
  407. mMaxClumpCount[i] = 1;
  408. mClumpCountExponent[i] = 1.0f;
  409. mClumpRadius[i] = 1.0f;
  410. mBillboardRects[i].point.set( 0.0f, 0.0f );
  411. mBillboardRects[i].extent.set( 1.0f, 1.0f );
  412. mShapeFilenames[i] = NULL;
  413. mShapeInstances[i] = NULL;
  414. mBillboardAspectScales[i] = 1.0f;
  415. mNormalizedProbability[i] = 0.0f;
  416. }
  417. }
  418. GroundCover::~GroundCover()
  419. {
  420. SAFE_DELETE( mMatInst );
  421. }
  422. IMPLEMENT_CO_NETOBJECT_V1(GroundCover);
  423. void GroundCover::initPersistFields()
  424. {
  425. addGroup( "GroundCover General" );
  426. addField( "material", TypeMaterialName, Offset( mMaterialName, GroundCover ), "Material used by all GroundCover segments." );
  427. addField( "radius", TypeF32, Offset( mRadius, GroundCover ), "Outer generation radius from the current camera position." );
  428. addField( "dissolveRadius",TypeF32, Offset( mFadeRadius, GroundCover ), "This is less than or equal to radius and defines when fading of cover elements begins." );
  429. addField( "reflectScale", TypeF32, Offset( mReflectRadiusScale, GroundCover ), "Scales the various culling radii when rendering a reflection. Typically for water." );
  430. addField( "gridSize", TypeS32, Offset( mGridSize, GroundCover ), "The number of cells per axis in the grid." );
  431. addField( "zOffset", TypeF32, Offset( mZOffset, GroundCover ), "Offset along the Z axis to render the ground cover." );
  432. addField( "seed", TypeS32, Offset( mRandomSeed, GroundCover ), "This RNG seed is saved and sent to clients for generating the same cover." );
  433. addField( "maxElements", TypeS32, Offset( mMaxPlacement, GroundCover ), "The maximum amount of cover elements to include in the grid at any one time." );
  434. addField( "maxBillboardTiltAngle", TypeF32, Offset( mMaxBillboardTiltAngle, GroundCover ),"The maximum amout of degrees the billboard will tilt down to match the camera." );
  435. addField( "shapeCullRadius", TypeF32, Offset( mShapeCullRadius, GroundCover ), "This is the distance at which DTS elements are completely culled out." );
  436. addField( "shapesCastShadows", TypeBool, Offset( mShapesCastShadows, GroundCover ), "Whether DTS elements should cast shadows or not." );
  437. addArray( "Types", MAX_COVERTYPES );
  438. addField( "billboardUVs", TypeRectUV, Offset( mBillboardRects, GroundCover ), MAX_COVERTYPES, "Subset material UV coordinates for this cover billboard." );
  439. addField( "shapeFilename", TypeFilename, Offset( mShapeFilenames, GroundCover ), MAX_COVERTYPES, "The cover shape filename. [Optional]" );
  440. addField( "layer", TypeTerrainMaterialName, Offset( mLayer, GroundCover ), MAX_COVERTYPES, "Terrain material name to limit coverage to, or blank to not limit." );
  441. addField( "invertLayer", TypeBool, Offset( mInvertLayer, GroundCover ), MAX_COVERTYPES, "Indicates that the terrain material index given in 'layer' is an exclusion mask." );
  442. addField( "probability", TypeF32, Offset( mProbability, GroundCover ), MAX_COVERTYPES, "The probability of one cover type verses another (relative to all cover types)." );
  443. addField( "sizeMin", TypeF32, Offset( mSizeMin, GroundCover ), MAX_COVERTYPES, "The minimum random size for each cover type." );
  444. addField( "sizeMax", TypeF32, Offset( mSizeMax, GroundCover ), MAX_COVERTYPES, "The maximum random size of this cover type." );
  445. addField( "sizeExponent", TypeF32, Offset( mSizeExponent, GroundCover ), MAX_COVERTYPES, "An exponent used to bias between the minimum and maximum random sizes." );
  446. addField( "windScale", TypeF32, Offset( mWindScale, GroundCover ), MAX_COVERTYPES, "The wind effect scale." );
  447. addField( "maxSlope", TypeF32, Offset( mMaxSlope, GroundCover ), MAX_COVERTYPES, "The maximum slope angle in degrees for placement." );
  448. addField( "minElevation", TypeF32, Offset( mMinElevation, GroundCover ), MAX_COVERTYPES, "The minimum world space elevation for placement." );
  449. addField( "maxElevation", TypeF32, Offset( mMaxElevation, GroundCover ), MAX_COVERTYPES, "The maximum world space elevation for placement." );
  450. addField( "minClumpCount", TypeS32, Offset( mMinClumpCount, GroundCover ), MAX_COVERTYPES, "The minimum amount of elements in a clump." );
  451. addField( "maxClumpCount", TypeS32, Offset( mMaxClumpCount, GroundCover ), MAX_COVERTYPES, "The maximum amount of elements in a clump." );
  452. addField( "clumpExponent", TypeF32, Offset( mClumpCountExponent, GroundCover ), MAX_COVERTYPES, "An exponent used to bias between the minimum and maximum clump counts for a particular clump." );
  453. addField( "clumpRadius", TypeF32, Offset( mClumpRadius, GroundCover ), MAX_COVERTYPES, "The maximum clump radius." );
  454. endArray( "Types" );
  455. endGroup( "GroundCover General" );
  456. addGroup( "GroundCover Wind" );
  457. addField( "windDirection", TypePoint2F, Offset( mWindDirection, GroundCover ), "The direction of the wind." );
  458. addField( "windGustLength", TypeF32, Offset( mWindGustLength, GroundCover ), "The length in meters between peaks in the wind gust." );
  459. addField( "windGustFrequency",TypeF32, Offset( mWindGustFrequency, GroundCover ), "Controls how often the wind gust peaks per second." );
  460. addField( "windGustStrength", TypeF32, Offset( mWindGustStrength, GroundCover ), "The maximum distance in meters that the peak wind gust will displace an element." );
  461. addField( "windTurbulenceFrequency", TypeF32, Offset( mWindTurbulenceFrequency, GroundCover ),"Controls the overall rapidity of the wind turbulence." );
  462. addField( "windTurbulenceStrength", TypeF32, Offset( mWindTurbulenceStrength, GroundCover ), "The maximum distance in meters that the turbulence can displace a ground cover element." );
  463. endGroup( "GroundCover Wind" );
  464. addGroup( "GroundCover Debug" );
  465. addField( "lockFrustum", TypeBool, Offset( mDebugLockFrustum, GroundCover ), "Debug parameter for locking the culling frustum which will freeze the cover generation." );
  466. addField( "renderCells", TypeBool, Offset( mDebugRenderCells, GroundCover ), "Debug parameter for displaying the grid cells." );
  467. addField( "noBillboards", TypeBool, Offset( mDebugNoBillboards, GroundCover ), "Debug parameter for turning off billboard rendering." );
  468. addField( "noShapes", TypeBool, Offset( mDebugNoShapes, GroundCover ), "Debug parameter for turning off shape rendering." );
  469. endGroup( "GroundCover Debug" );
  470. Parent::initPersistFields();
  471. }
  472. void GroundCover::consoleInit()
  473. {
  474. Con::addVariable( "$pref::GroundCover::densityScale", TypeF32, &smDensityScale, "A global LOD scalar which can reduce the overall density of placed GroundCover.\n"
  475. "@ingroup Foliage\n");
  476. Con::addVariable( "$GroundCover::renderedCells", TypeS32, &smStatRenderedCells, "Stat for number of rendered cells.\n"
  477. "@ingroup Foliage\n");
  478. Con::addVariable( "$GroundCover::renderedBillboards", TypeS32, &smStatRenderedBillboards, "Stat for number of rendered billboards.\n"
  479. "@ingroup Foliage\n");
  480. Con::addVariable( "$GroundCover::renderedBatches", TypeS32, &smStatRenderedBatches, "Stat for number of rendered billboard batches.\n"
  481. "@ingroup Foliage\n");
  482. Con::addVariable( "$GroundCover::renderedShapes", TypeS32, &smStatRenderedShapes, "Stat for number of rendered shapes.\n"
  483. "@ingroup Foliage\n");
  484. Parent::consoleInit();
  485. }
  486. bool GroundCover::onAdd()
  487. {
  488. if (!Parent::onAdd())
  489. return false;
  490. // We don't use any bounds.
  491. setGlobalBounds();
  492. resetWorldBox();
  493. // Prepare some client side things.
  494. if ( isClientObject() )
  495. {
  496. _initMaterial();
  497. _initShapes();
  498. // Hook ourselves up to get terrain change notifications.
  499. TerrainBlock::smUpdateSignal.notify( this, &GroundCover::onTerrainUpdated );
  500. }
  501. addToScene();
  502. return true;
  503. }
  504. void GroundCover::onRemove()
  505. {
  506. Parent::onRemove();
  507. _deleteCells();
  508. _deleteShapes();
  509. if ( isClientObject() )
  510. {
  511. TerrainBlock::smUpdateSignal.remove( this, &GroundCover::onTerrainUpdated );
  512. }
  513. removeFromScene();
  514. }
  515. void GroundCover::inspectPostApply()
  516. {
  517. Parent::inspectPostApply();
  518. // We flag all the parameters as changed because
  519. // we're feeling lazy and there is not a good way
  520. // to track what parameters changed.
  521. //
  522. // TODO: Add a mask bit option to addField() and/or
  523. // addGroup() which is passed to inspectPostApply
  524. // for detection of changed elements.
  525. //
  526. setMaskBits(U32(-1) );
  527. }
  528. U32 GroundCover::packUpdate( NetConnection *connection, U32 mask, BitStream *stream )
  529. {
  530. U32 retMask = Parent::packUpdate( connection, mask, stream );
  531. if (stream->writeFlag(mask & InitialUpdateMask))
  532. {
  533. // TODO: We could probably optimize a few of these
  534. // based on reasonable units at some point.
  535. stream->write( mMaterialName );
  536. stream->write( mRadius );
  537. stream->write( mZOffset );
  538. stream->write( mFadeRadius );
  539. stream->write( mShapeCullRadius );
  540. stream->writeFlag( mShapesCastShadows );
  541. stream->write( mReflectRadiusScale );
  542. stream->write( mGridSize );
  543. stream->write( mRandomSeed );
  544. stream->write( mMaxPlacement );
  545. stream->write( mMaxBillboardTiltAngle );
  546. stream->write( mWindDirection.x );
  547. stream->write( mWindDirection.y );
  548. stream->write( mWindGustLength );
  549. stream->write( mWindGustFrequency );
  550. stream->write( mWindGustStrength );
  551. stream->write( mWindTurbulenceFrequency );
  552. stream->write( mWindTurbulenceStrength );
  553. for ( S32 i=0; i < MAX_COVERTYPES; i++ )
  554. {
  555. stream->write( mProbability[i] );
  556. stream->write( mSizeMin[i] );
  557. stream->write( mSizeMax[i] );
  558. stream->write( mSizeExponent[i] );
  559. stream->write( mWindScale[i] );
  560. stream->write( mMaxSlope[i] );
  561. stream->write( mMinElevation[i] );
  562. stream->write( mMaxElevation[i] );
  563. stream->writeString( mLayer[i] );
  564. stream->writeFlag( mInvertLayer[i] );
  565. stream->write( mMinClumpCount[i] );
  566. stream->write( mMaxClumpCount[i] );
  567. stream->write( mClumpCountExponent[i] );
  568. stream->write( mClumpRadius[i] );
  569. stream->write( mBillboardRects[i].point.x );
  570. stream->write( mBillboardRects[i].point.y );
  571. stream->write( mBillboardRects[i].extent.x );
  572. stream->write( mBillboardRects[i].extent.y );
  573. stream->writeString( mShapeFilenames[i] );
  574. }
  575. stream->writeFlag( mDebugRenderCells );
  576. stream->writeFlag( mDebugNoBillboards );
  577. stream->writeFlag( mDebugNoShapes );
  578. stream->writeFlag( mDebugLockFrustum );
  579. }
  580. return retMask;
  581. }
  582. void GroundCover::unpackUpdate( NetConnection *connection, BitStream *stream )
  583. {
  584. Parent::unpackUpdate( connection, stream );
  585. if (stream->readFlag())
  586. {
  587. stream->read( &mMaterialName );
  588. stream->read( &mRadius );
  589. stream->read( &mZOffset );
  590. stream->read( &mFadeRadius );
  591. stream->read( &mShapeCullRadius );
  592. mShapesCastShadows = stream->readFlag();
  593. stream->read( &mReflectRadiusScale );
  594. stream->read( &mGridSize );
  595. stream->read( &mRandomSeed );
  596. stream->read( &mMaxPlacement );
  597. stream->read( &mMaxBillboardTiltAngle );
  598. stream->read( &mWindDirection.x );
  599. stream->read( &mWindDirection.y );
  600. stream->read( &mWindGustLength );
  601. stream->read( &mWindGustFrequency );
  602. stream->read( &mWindGustStrength );
  603. stream->read( &mWindTurbulenceFrequency );
  604. stream->read( &mWindTurbulenceStrength );
  605. for ( S32 i=0; i < MAX_COVERTYPES; i++ )
  606. {
  607. stream->read( &mProbability[i] );
  608. stream->read( &mSizeMin[i] );
  609. stream->read( &mSizeMax[i] );
  610. stream->read( &mSizeExponent[i] );
  611. stream->read( &mWindScale[i] );
  612. stream->read( &mMaxSlope[i] );
  613. stream->read( &mMinElevation[i] );
  614. stream->read( &mMaxElevation[i] );
  615. mLayer[i] = stream->readSTString();
  616. mInvertLayer[i] = stream->readFlag();
  617. stream->read( &mMinClumpCount[i] );
  618. stream->read( &mMaxClumpCount[i] );
  619. stream->read( &mClumpCountExponent[i] );
  620. stream->read( &mClumpRadius[i] );
  621. stream->read( &mBillboardRects[i].point.x );
  622. stream->read( &mBillboardRects[i].point.y );
  623. stream->read( &mBillboardRects[i].extent.x );
  624. stream->read( &mBillboardRects[i].extent.y );
  625. mShapeFilenames[i] = stream->readSTString();
  626. }
  627. mDebugRenderCells = stream->readFlag();
  628. mDebugNoBillboards = stream->readFlag();
  629. mDebugNoShapes = stream->readFlag();
  630. mDebugLockFrustum = stream->readFlag();
  631. // We have no way to easily know what changed, so by clearing
  632. // the cells we force a reinit and regeneration of the cells.
  633. // It's sloppy, but it works for now.
  634. _freeCells();
  635. if ( isProperlyAdded() )
  636. _initMaterial();
  637. }
  638. }
  639. void GroundCover::_initMaterial()
  640. {
  641. SAFE_DELETE( mMatInst );
  642. if ( mMaterialName.isNotEmpty() )
  643. if ( !Sim::findObject( mMaterialName, mMaterial ) )
  644. Con::errorf( "GroundCover::_initMaterial - Material %s was not found.", mMaterialName.c_str() );
  645. if ( mMaterial )
  646. mMatInst = mMaterial->createMatInstance();
  647. else
  648. mMatInst = MATMGR->createMatInstance( "WarningMaterial" );
  649. // Add our special feature that makes it all work...
  650. FeatureSet features = MATMGR->getDefaultFeatures();
  651. features.addFeature( MFT_Foliage );
  652. // Our feature requires a pointer back to this object
  653. // to properly setup its shader consts.
  654. mMatInst->setUserObject( this );
  655. // DO IT!
  656. mMatInst->init( features, getGFXVertexFormat<GCVertex>() );
  657. }
  658. void GroundCover::_initShapes()
  659. {
  660. _deleteShapes();
  661. for ( S32 i=0; i < MAX_COVERTYPES; i++ )
  662. {
  663. if ( !mShapeFilenames[i] || !mShapeFilenames[i][0] )
  664. continue;
  665. // Load the shape.
  666. Resource<TSShape> shape = ResourceManager::get().load(mShapeFilenames[i]);
  667. if ( !(bool)shape )
  668. {
  669. Con::warnf( "GroundCover::_initShapes() unable to load shape: %s", mShapeFilenames[i] );
  670. continue;
  671. }
  672. if ( isClientObject() && !shape->preloadMaterialList(shape.getPath()) && NetConnection::filesWereDownloaded() )
  673. {
  674. Con::warnf( "GroundCover::_initShapes() material preload failed for shape: %s", mShapeFilenames[i] );
  675. continue;
  676. }
  677. // Create the shape instance.
  678. mShapeInstances[i] = new TSShapeInstance( shape, isClientObject() );
  679. }
  680. }
  681. void GroundCover::_deleteShapes()
  682. {
  683. for ( S32 i=0; i < MAX_COVERTYPES; i++ )
  684. {
  685. delete mShapeInstances[i];
  686. mShapeInstances[i] = NULL;
  687. }
  688. }
  689. void GroundCover::_deleteCells()
  690. {
  691. // Delete the allocation list.
  692. for ( S32 i=0; i < mAllocCellList.size(); i++ )
  693. delete mAllocCellList[i];
  694. mAllocCellList.clear();
  695. // Zero out the rest of the stuff.
  696. _freeCells();
  697. }
  698. void GroundCover::_freeCells()
  699. {
  700. // Zero the grid and scratch space.
  701. mCellGrid.clear();
  702. mScratchGrid.clear();
  703. // Compact things... remove excess allocated cells.
  704. const U32 maxCells = mGridSize * mGridSize;
  705. if ( mAllocCellList.size() > maxCells )
  706. {
  707. for ( S32 i=maxCells; i < mAllocCellList.size(); i++ )
  708. delete mAllocCellList[i];
  709. mAllocCellList.setSize( maxCells );
  710. }
  711. // Move all the alloced cells into the free list.
  712. mFreeCellList.clear();
  713. mFreeCellList.merge( mAllocCellList );
  714. // Release the primitive buffer.
  715. mPrimBuffer = NULL;
  716. }
  717. void GroundCover::_recycleCell( GroundCoverCell* cell )
  718. {
  719. mFreeCellList.push_back( cell );
  720. }
  721. void GroundCover::_initialize( U32 cellCount, U32 cellPlacementCount )
  722. {
  723. // Cleanup everything... we're starting over.
  724. _freeCells();
  725. _deleteShapes();
  726. // Nothing to do without a count!
  727. if ( cellPlacementCount == 0 )
  728. return;
  729. // Reset the grid sizes.
  730. mCellGrid.setSize( cellCount );
  731. dMemset( mCellGrid.address(), 0, mCellGrid.memSize() );
  732. mScratchGrid.setSize( cellCount );
  733. // Rebuild the texture aspect scales for each type.
  734. F32 textureAspect = 1.0f;
  735. if( mMatInst && mMatInst->isValid())
  736. {
  737. Material* mat = dynamic_cast<Material*>(mMatInst->getMaterial());
  738. if(mat)
  739. {
  740. GFXTexHandle tex(mat->mDiffuseMapFilename[0], &GFXDefaultStaticDiffuseProfile, "GroundCover texture aspect ratio check" );
  741. if(tex.isValid())
  742. {
  743. U32 w = tex.getWidth();
  744. U32 h = tex.getHeight();
  745. if(h > 0)
  746. textureAspect = F32(w) / F32(h);
  747. }
  748. }
  749. }
  750. for ( S32 i=0; i < MAX_COVERTYPES; i++ )
  751. {
  752. if ( mBillboardRects[i].extent.y > 0.0f )
  753. {
  754. mBillboardAspectScales[i] = textureAspect * mBillboardRects[i].extent.x / mBillboardRects[i].extent.y;
  755. }
  756. else
  757. mBillboardAspectScales[i] = 0.0f;
  758. }
  759. // Load the shapes again.
  760. _initShapes();
  761. // Set the primitive buffer up for the maximum placement in a cell.
  762. mPrimBuffer.set( GFX, cellPlacementCount * 6, 0, GFXBufferTypeStatic );
  763. U16 *idxBuff;
  764. mPrimBuffer.lock(&idxBuff);
  765. for ( U32 i=0; i < cellPlacementCount; i++ )
  766. {
  767. //
  768. // The vertex pattern in the VB for each
  769. // billboard is as follows...
  770. //
  771. // 0----1
  772. // |\ |
  773. // | \ |
  774. // | \ |
  775. // | \|
  776. // 3----2
  777. //
  778. // We setup the index order below to ensure
  779. // sequential, cache friendly, access.
  780. //
  781. U32 offset = i * 4;
  782. idxBuff[i*6+0] = 0 + offset;
  783. idxBuff[i*6+1] = 1 + offset;
  784. idxBuff[i*6+2] = 2 + offset;
  785. idxBuff[i*6+3] = 2 + offset;
  786. idxBuff[i*6+4] = 3 + offset;
  787. idxBuff[i*6+5] = 0 + offset;
  788. }
  789. mPrimBuffer.unlock();
  790. // Generate the normalized probability.
  791. F32 total = 0.0f;
  792. for ( S32 i=0; i < MAX_COVERTYPES; i++ )
  793. {
  794. // If the element isn't gonna render... then
  795. // set the probability to zero.
  796. if ( mShapeInstances[i] == NULL && mBillboardAspectScales[i] <= 0.0001f )
  797. {
  798. mNormalizedProbability[i] = 0.0f;
  799. }
  800. else
  801. {
  802. mNormalizedProbability[i] = mProbability[i];
  803. total += mProbability[i];
  804. }
  805. }
  806. if ( total > 0.0f )
  807. {
  808. for ( S32 i=0; i < MAX_COVERTYPES; i++ )
  809. mNormalizedProbability[i] /= total;
  810. }
  811. }
  812. GroundCoverCell* GroundCover::_generateCell( const Point2I& index,
  813. const Box3F& bounds,
  814. U32 placementCount,
  815. S32 randSeed )
  816. {
  817. PROFILE_SCOPE(GroundCover_GenerateCell);
  818. const Vector<SceneObject*> terrainBlocks = getContainer()->getTerrains();
  819. if ( terrainBlocks.empty() )
  820. return NULL;
  821. // Grab a free cell or allocate a new one.
  822. GroundCoverCell* cell;
  823. if ( mFreeCellList.empty() )
  824. {
  825. cell = new GroundCoverCell();
  826. mAllocCellList.push_back( cell );
  827. }
  828. else
  829. {
  830. cell = mFreeCellList.last();
  831. mFreeCellList.pop_back();
  832. }
  833. cell->mDirty = true;
  834. cell->mIndex = index;
  835. cell->mBounds = bounds;
  836. Point3F pos( 0, 0, 0 );
  837. Box3F renderBounds = bounds;
  838. Point3F point;
  839. Point3F normal;
  840. F32 h;
  841. Point2F cp, uv;
  842. bool hit;
  843. GroundCoverCell::Placement p;
  844. F32 rotation;
  845. F32 size;
  846. F32 sizeExponent;
  847. Point2I lpos;
  848. //F32 value;
  849. VectorF right;
  850. StringTableEntry matName = StringTable->EmptyString();
  851. bool firstElem = true;
  852. TerrainBlock *terrainBlock = NULL;
  853. cell->mBillboards.clear();
  854. cell->mBillboards.reserve( placementCount );
  855. cell->mShapes.clear();
  856. cell->mShapes.reserve( placementCount );
  857. F32 terrainSquareSize,
  858. oneOverTerrainLength,
  859. oneOverTerrainSquareSize;
  860. const GBitmap* terrainLM = NULL;
  861. // The RNG that we'll use in generation.
  862. MRandom rand( 0 );
  863. // We process one type at a time.
  864. for ( U32 type=0; type < MAX_COVERTYPES; type++ )
  865. {
  866. // How many cover elements do we need to generate for this type?
  867. const S32 typeCount = mNormalizedProbability[type] * (F32)placementCount;
  868. if ( typeCount <= 0 )
  869. continue;
  870. // Grab the terrain layer for this type.
  871. /*
  872. const TerrainDataLayer* dataLayer = NULL;
  873. const bool typeInvertLayer = mInvertLayer[type];
  874. if ( mLayer[type] > -1 )
  875. {
  876. dataLayer = mTerrainBlock->getDataLayer( mLayer[type] );
  877. if ( dataLayer )
  878. {
  879. // Do an initial check to see if we can place any place anything
  880. // at all... if the layer area for this element is empty then
  881. // there is nothing more to do.
  882. RectI area( (S32)mFloor( ( bounds.minExtents.x - pos.x ) * oneOverTerrainSquareSize ),
  883. (S32)mFloor( ( bounds.minExtents.y - pos.y ) * oneOverTerrainSquareSize ),
  884. (S32)mCeil( ( bounds.maxExtents.x - pos.x ) * oneOverTerrainSquareSize ),
  885. (S32)mCeil( ( bounds.maxExtents.y - pos.y ) * oneOverTerrainSquareSize ) );
  886. area.extent -= area.point;
  887. if ( dataLayer->testFill( area, typeInvertLayer ? 255 : 0 ) )
  888. continue;
  889. }
  890. }
  891. // If the layer is not inverted and we have no data
  892. // then we have nothing to draw.
  893. if ( !typeInvertLayer && !dataLayer )
  894. continue;
  895. */
  896. // We set the seed we were passed which is based on this grids position
  897. // in the world and add the type value. This keeps changes to one type
  898. // from effecting the outcome of the others.
  899. rand.setSeed( randSeed + type );
  900. // Setup for doing clumps.
  901. S32 clumps = 0;
  902. Point2F clumpCenter(0.0f, 0.0f);
  903. const S32 clumpMin = getMax( 1, (S32)mMinClumpCount[type] );
  904. F32 clumpExponent;
  905. // We mult this by -1 each billboard we make then use
  906. // it to scale the billboard x axis to flip them. This
  907. // essentially gives us twice the variation for free.
  908. F32 flipBB = -1.0f;
  909. // Precompute a few other type specific values.
  910. const F32 typeSizeRange = mSizeMax[type] - mSizeMin[type];
  911. const F32 typeMaxSlope = mMaxSlope[type];
  912. const F32 typeMaxElevation = mMaxElevation[type];
  913. const F32 typeMinElevation = mMinElevation[type];
  914. const bool typeIsShape = mShapeInstances[ type ] != NULL;
  915. const Box3F typeShapeBounds = typeIsShape ? mShapeInstances[ type ]->getShape()->bounds : Box3F();
  916. const F32 typeWindScale = mWindScale[type];
  917. StringTableEntry typeLayer = mLayer[type];
  918. const bool typeInvertLayer = mInvertLayer[type];
  919. // We can set this once here... all the placements for this are the same.
  920. p.type = type;
  921. p.windAmplitude = typeWindScale;
  922. p.lmColor.set(1.0f,1.0f,1.0f);
  923. // Generate all the cover elements for this type.
  924. for ( S32 i=0; i < typeCount; i++ )
  925. {
  926. // Do all the other random things here first as to not
  927. // disturb the random sequence if the terrain geometry
  928. // or cover layers change.
  929. // Get the random position.
  930. cp.set( rand.randF(), rand.randF() );
  931. // Prepare the clump info.
  932. clumpExponent = mClampF( mPow( rand.randF(), mClumpCountExponent[type] ), 0.0f, 1.0f );
  933. if ( clumps <= 0 )
  934. {
  935. // We're starting a new clump.
  936. clumps = ( clumpMin + mFloor( ( mMaxClumpCount[type] - clumpMin ) * clumpExponent ) ) - 1;
  937. cp.set( bounds.minExtents.x + cp.x * bounds.len_x(),
  938. bounds.minExtents.y + cp.y * bounds.len_y() );
  939. clumpCenter = cp;
  940. }
  941. else
  942. {
  943. clumps--;
  944. cp.set( clumpCenter.x - ( ( cp.x - 0.5f ) * mClumpRadius[type] ),
  945. clumpCenter.y - ( ( cp.y - 0.5f ) * mClumpRadius[type] ) );
  946. }
  947. // Which terrain do I place on?
  948. if ( terrainBlocks.size() == 1 )
  949. terrainBlock = dynamic_cast< TerrainBlock* >( terrainBlocks.first() );
  950. else
  951. {
  952. for ( U32 i = 0; i < terrainBlocks.size(); i++ )
  953. {
  954. TerrainBlock *terrain = dynamic_cast< TerrainBlock* >( terrainBlocks[ i ] );
  955. if( !terrain )
  956. continue;
  957. const Box3F &terrBounds = terrain->getWorldBox();
  958. if ( cp.x < terrBounds.minExtents.x || cp.x > terrBounds.maxExtents.x ||
  959. cp.y < terrBounds.minExtents.y || cp.y > terrBounds.maxExtents.y )
  960. continue;
  961. terrainBlock = terrain;
  962. break;
  963. }
  964. }
  965. // This should only happen if the generation went off
  966. // the edge of the terrain blocks.
  967. if ( !terrainBlock )
  968. continue;
  969. terrainLM = terrainBlock->getLightMap();
  970. pos = terrainBlock->getPosition();
  971. terrainSquareSize = (F32)terrainBlock->getSquareSize();
  972. oneOverTerrainLength = 1.0f / terrainBlock->getWorldBlockSize();
  973. oneOverTerrainSquareSize = 1.0f / terrainSquareSize;
  974. // The size is calculated using an exponent to control
  975. // the frequency between min and max sizes.
  976. sizeExponent = mClampF( mPow( rand.randF(), mSizeExponent[type] ), 0.0f, 1.0f );
  977. size = mSizeMin[type] + ( typeSizeRange * sizeExponent );
  978. // Generate a random z rotation.
  979. rotation = rand.randF() * M_2PI_F;
  980. // Flip the billboard now for the next generation.
  981. flipBB *= -1.0f;
  982. PROFILE_START( GroundCover_TerrainRayCast );
  983. // Transform billboard point into terrain's frame of reference.
  984. Point3F pp = Point3F(cp.x, cp.y, 0);
  985. terrainBlock->getWorldTransform().mulP(pp);
  986. hit = terrainBlock->getNormalHeightMaterial( Point2F ( pp.x, pp.y ),
  987. &normal, &h, matName );
  988. PROFILE_END(); // GroundCover_TerrainRayCast
  989. // TODO: When did we loose the world space elevation when
  990. // getting the terrain height?
  991. h += pos.z + mZOffset;
  992. if ( !hit || h > typeMaxElevation || h < typeMinElevation ||
  993. ( typeLayer[0] && !typeInvertLayer && matName != typeLayer ) ||
  994. ( typeLayer[0] && typeInvertLayer && matName == typeLayer ) )
  995. continue;
  996. // Do we need to check slope?
  997. if ( !mIsZero( typeMaxSlope ) )
  998. {
  999. if (mAcos(normal.z) > mDegToRad(typeMaxSlope))
  1000. continue;
  1001. }
  1002. point.set( cp.x, cp.y, h );
  1003. p.point = point;
  1004. p.rotation = rotation;
  1005. p.normal = normal;
  1006. // Grab the terrain lightmap color at this position.
  1007. //
  1008. // TODO: Can't we remove this test? The terrain
  1009. // lightmap should never be null... NEVER!
  1010. //
  1011. if ( terrainLM )
  1012. {
  1013. // TODO: We could probably call terrainLM->getBits()
  1014. // once outside the loop then pre-calculate the scalar
  1015. // for converting a world position into a lexel...
  1016. // avoiding the extra protections inside of sampleTexel().
  1017. uv.x = (point.x + pos.x) * oneOverTerrainLength;
  1018. uv.y = (point.y + pos.y) * oneOverTerrainLength;
  1019. uv.x -= mFloor(uv.x);
  1020. uv.y -= mFloor(uv.y);
  1021. p.lmColor = terrainLM->sampleTexel(uv.x,uv.y);
  1022. }
  1023. // Put it into the right list by type.
  1024. //
  1025. // TODO: Could we break up the generation into
  1026. // two separate loops for shapes and billboards
  1027. // and gain performance?
  1028. //
  1029. if ( typeIsShape )
  1030. {
  1031. // TODO: Convert the size into a real size... not scale!
  1032. // TODO: We could probably cache the shape bounds
  1033. // into a primitive array and avoid the double pointer
  1034. // dereference per placement.
  1035. p.size.set( size, size, size );
  1036. p.worldBox = typeShapeBounds;
  1037. p.worldBox.minExtents *= size;
  1038. p.worldBox.maxExtents *= size;
  1039. p.worldBox.minExtents += point;
  1040. p.worldBox.maxExtents += point;
  1041. cell->mShapes.push_back( p );
  1042. }
  1043. else
  1044. {
  1045. p.size.y = size;
  1046. p.size.x = size * flipBB * mBillboardAspectScales[type];
  1047. p.worldBox.maxExtents = p.worldBox.minExtents = point;
  1048. cell->mBillboards.push_back( p );
  1049. }
  1050. // Update the render bounds.
  1051. if ( firstElem )
  1052. {
  1053. renderBounds = p.worldBox;
  1054. firstElem = false;
  1055. }
  1056. else
  1057. {
  1058. renderBounds.extend( p.worldBox.minExtents );
  1059. renderBounds.extend( p.worldBox.maxExtents );
  1060. }
  1061. } // for ( S32 i=0; i < typeCount; i++ )
  1062. } // for ( U32 type=0; type < NumCoverTypes; type++ )
  1063. cell->mRenderBounds = renderBounds;
  1064. cell->mBounds.minExtents.z = renderBounds.minExtents.z;
  1065. cell->mBounds.maxExtents.z = renderBounds.maxExtents.z;
  1066. return cell;
  1067. }
  1068. void GroundCover::onTerrainUpdated( U32 flags, TerrainBlock *tblock, const Point2I& min, const Point2I& max )
  1069. {
  1070. if ( isServerObject() )
  1071. return;
  1072. // Free all the cells if we've gotten a lightmap update.
  1073. if ( flags & TerrainBlock::LightmapUpdate )
  1074. {
  1075. _freeCells();
  1076. return;
  1077. }
  1078. // TODO: EmptyUpdate doesn't work yet... fix editor/terrain.
  1079. // If this is a height or opacity update only clear
  1080. // the cells that have changed.
  1081. if ( flags & TerrainBlock::HeightmapUpdate ||
  1082. flags & TerrainBlock::LayersUpdate ||
  1083. flags & TerrainBlock::EmptyUpdate )
  1084. {
  1085. // Convert the min and max into world space.
  1086. const F32 size = tblock->getSquareSize();
  1087. const Point3F pos = tblock->getPosition();
  1088. // TODO: I don't think this works right with tiling!
  1089. Box3F dirty( F32( min.x * size ) + pos.x, F32( min.y * size ) + pos.y, 0.0f,
  1090. F32( max.x * size ) + pos.x, F32( max.y * size ) + pos.y, 0.0f );
  1091. // Now free any cells that overlap it!
  1092. for ( S32 i = 0; i < mCellGrid.size(); i++ )
  1093. {
  1094. GroundCoverCell* cell = mCellGrid[ i ];
  1095. if ( !cell )
  1096. continue;
  1097. const Box3F& bounds = cell->getBounds();
  1098. dirty.minExtents.z = bounds.minExtents.z;
  1099. dirty.maxExtents.z = bounds.maxExtents.z;
  1100. if ( bounds.isOverlapped( dirty ) )
  1101. {
  1102. mCellGrid[ i ] = NULL;
  1103. _recycleCell( cell );
  1104. }
  1105. }
  1106. }
  1107. }
  1108. void GroundCover::_updateCoverGrid( const Frustum &culler )
  1109. {
  1110. PROFILE_SCOPE( GroundCover_UpdateCoverGrid );
  1111. mGridSize = getMax( mGridSize, (U32)2 );
  1112. // How many cells in the grid?
  1113. const U32 cells = mGridSize * mGridSize;
  1114. // Whats the max placement count for each cell considering
  1115. // the grid size and quality scale LOD value.
  1116. const S32 placementCount = getMax( ( (F32)mMaxPlacement * smDensityScale ) / F32( mGridSize * mGridSize ), 0.0f );
  1117. // If the cell grid isn't sized or the placement count
  1118. // changed (most likely because of quality lod) then we
  1119. // need to initialize the system again.
  1120. if ( mCellGrid.empty() || placementCount != mLastPlacementCount )
  1121. {
  1122. _initialize( cells, placementCount );
  1123. mLastPlacementCount = placementCount;
  1124. }
  1125. // Without a count... we don't function at all.
  1126. if ( placementCount == 0 )
  1127. return;
  1128. // Clear the scratch grid.
  1129. dMemset( mScratchGrid.address(), 0, mScratchGrid.memSize() );
  1130. // Calculate the normal cell size here.
  1131. const F32 cellSize = ( mRadius * 2.0f ) / (F32)(mGridSize - 1);
  1132. // Figure out the root index of the new grid based on the camera position.
  1133. Point2I index( (S32)mFloor( ( culler.getPosition().x - mRadius ) / cellSize ),
  1134. (S32)mFloor( ( culler.getPosition().y - mRadius ) / cellSize ) );
  1135. // Figure out the cell shift between the old and new grid positions.
  1136. Point2I shift = mGridIndex - index;
  1137. // If we've shifted more than one in either axis then we've warped.
  1138. bool didWarp = shift.x > 1 || shift.x < -1 ||
  1139. shift.y > 1 || shift.y < -1 ? true : false;
  1140. // Go thru the grid shifting each cell we find and
  1141. // placing them in the scratch grid.
  1142. for ( S32 i = 0; i < mCellGrid.size(); i++ )
  1143. {
  1144. GroundCoverCell* cell = mCellGrid[ i ];
  1145. if ( !cell )
  1146. continue;
  1147. // Whats our new index?
  1148. Point2I newIndex = cell->shiftIndex( shift );
  1149. // Is this cell outside of the new grid?
  1150. if ( newIndex.x < 0 || newIndex.x >= mGridSize ||
  1151. newIndex.y < 0 || newIndex.y >= mGridSize )
  1152. {
  1153. _recycleCell( cell );
  1154. continue;
  1155. }
  1156. // Place the cell in the scratch grid.
  1157. mScratchGrid[ ( newIndex.y * mGridSize ) + newIndex.x ] = cell;
  1158. }
  1159. // Get the terrain elevation range for setting the default cell bounds.
  1160. F32 terrainMinHeight = -5000.0f,
  1161. terrainMaxHeight = 5000.0f;
  1162. // Go thru the scratch grid copying each cell back to the
  1163. // cell grid and creating new cells as needed.
  1164. //
  1165. // By limiting ourselves to only one new cell generation per
  1166. // update we're lowering the performance hiccup during movement
  1167. // without getting into the complexity of threading. The delay
  1168. // in generation is rarely noticeable in normal play.
  1169. //
  1170. // The only caveat is that we need to generate the entire visible
  1171. // grid when we warp.
  1172. U32 cellsGenerated = 0;
  1173. for ( S32 i = 0; i < mScratchGrid.size(); i++ )
  1174. {
  1175. GroundCoverCell* cell = mScratchGrid[ i ];
  1176. if ( !cell && ( cellsGenerated == 0 || didWarp ) )
  1177. {
  1178. // Get the index point of this new cell.
  1179. S32 y = i / mGridSize;
  1180. S32 x = i - ( y * mGridSize );
  1181. Point2I newIndex = index + Point2I( x, y );
  1182. // What will be the world placement bounds for this cell.
  1183. Box3F bounds;
  1184. bounds.minExtents.set( newIndex.x * cellSize, newIndex.y * cellSize, terrainMinHeight );
  1185. bounds.maxExtents.set( bounds.minExtents.x + cellSize, bounds.minExtents.y + cellSize, terrainMaxHeight );
  1186. if ( mCuller.isCulled( bounds ) )
  1187. {
  1188. mCellGrid[ i ] = NULL;
  1189. continue;
  1190. }
  1191. // We need to allocate a new cell.
  1192. //
  1193. // TODO: This is the expensive call and where we should optimize. In
  1194. // particular the next best optimization would be to take advantage of
  1195. // multiple cores so that we can generate all the cells in one update.
  1196. //
  1197. // Instead of generating the cell here we would allocate a cell and stick
  1198. // it into a thread safe queue (maybe lockless) as well as the mCellGrid.
  1199. // Once all were allocated we would do something like this...
  1200. //
  1201. // TorqueParallelProcess( cellsToGenerateQueue, _generateCell );
  1202. //
  1203. // Internally this function would pass the queue to some global pre-allocated
  1204. // worker threads which are locked to a particular core. While the main
  1205. // thread waits for the worker threads to finish it will process cells itself.
  1206. //
  1207. cell = _generateCell( newIndex - index,
  1208. bounds,
  1209. placementCount,
  1210. mRandomSeed + mAbs( newIndex.x ) + mAbs( newIndex.y ) );
  1211. // Increment our generation count.
  1212. if ( cell )
  1213. ++cellsGenerated;
  1214. }
  1215. mCellGrid[ i ] = cell;
  1216. }
  1217. // Store the new grid index.
  1218. mGridIndex = index;
  1219. }
  1220. void GroundCover::prepRenderImage( SceneRenderState *state )
  1221. {
  1222. // Reset stats each time we hit the diffuse pass.
  1223. if( state->isDiffusePass() )
  1224. {
  1225. smStatRenderedCells = 0;
  1226. smStatRenderedBillboards = 0;
  1227. smStatRenderedBatches = 0;
  1228. smStatRenderedShapes = 0;
  1229. }
  1230. // TODO: Make sure that the ground cover stops rendering
  1231. // if you're inside a zoned interior.
  1232. if ( state->isReflectPass() && mReflectRadiusScale <= 0.0f )
  1233. return;
  1234. // Nothing to do if this is a shadow pass and shapes in this GoundCover
  1235. // should not be casting shadows.
  1236. if( state->isShadowPass() && !mShapesCastShadows )
  1237. return;
  1238. GFXTransformSaver saver;
  1239. // Setup the frustum culler.
  1240. if ( ( mCuller.getPosition().isZero() || !mDebugLockFrustum ) && !state->isShadowPass() )
  1241. mCuller = state->getCullingFrustum();
  1242. // Update the cells, but only during the diffuse pass.
  1243. // We don't want cell generation to thrash when the reflection camera
  1244. // position doesn't match the diffuse camera!
  1245. if ( state->isDiffusePass() )
  1246. _updateCoverGrid( mCuller );
  1247. // Render billboards but not into shadow passes.
  1248. if ( !state->isShadowPass() && mMatInst->isValid() && !mDebugNoBillboards )
  1249. {
  1250. PROFILE_SCOPE( GroundCover_RenderBillboards );
  1251. // Take zoom into account.
  1252. F32 screenScale = state->getWorldToScreenScale().y / state->getViewport().extent.y;
  1253. // Set the far distance for billboards.
  1254. mCuller.setFarDist( mRadius * screenScale );
  1255. F32 cullScale = 1.0f;
  1256. if ( state->isReflectPass() )
  1257. cullScale = mReflectRadiusScale;
  1258. // Setup our shader const data.
  1259. // Must be done prior to submitting our render instance.
  1260. mShaderConstData.fadeInfo.set( mFadeRadius * cullScale * screenScale, mRadius * cullScale * screenScale );
  1261. const F32 simTime = Sim::getCurrentTime() * 0.001f;
  1262. mShaderConstData.gustInfo.set( mWindGustLength, mWindGustFrequency * simTime, mWindGustStrength );
  1263. mShaderConstData.turbInfo.set( mWindTurbulenceFrequency * simTime, mWindTurbulenceStrength );
  1264. // Use the camera's forward vector to calculate the camera's right
  1265. // and up vectors. This removes any camera banking from affecting
  1266. // the ground cover.
  1267. const MatrixF &camMat = state->getDiffuseCameraTransform();
  1268. Point3F camDir, camUp, camRight;
  1269. camMat.getColumn( 1, &camDir );
  1270. mCross( camDir, Point3F::UnitZ, &camRight );
  1271. if ( camRight.magnitudeSafe() == 0.0f )
  1272. {
  1273. camRight.set( 0.0f, -1.0f, 0.0f );
  1274. }
  1275. camRight.normalizeSafe();
  1276. mCross( camRight, camDir, &camUp );
  1277. // Limit the camera up vector to keep the billboards
  1278. // from leaning too far down into the terrain.
  1279. VectorF lookDir( camDir.x, camDir.y, 0.0f );
  1280. F32 angle;
  1281. if ( !lookDir.isZero() )
  1282. {
  1283. lookDir.normalize();
  1284. angle = mAcos( mDot( camUp, lookDir ) );
  1285. }
  1286. else
  1287. {
  1288. angle = camDir.z < 0.0f ? 0.0f : ( M_PI_F / 2.0f );
  1289. }
  1290. const F32 maxBillboardTiltRads = mDegToRad( mMaxBillboardTiltAngle );
  1291. if ( angle < (M_PI_F / 2.0f) - maxBillboardTiltRads )
  1292. {
  1293. QuatF quat( AngAxisF( camRight, maxBillboardTiltRads ) );
  1294. quat.mulP( VectorF( 0.0f, 0.0f, 1.0f ), &camUp );
  1295. }
  1296. mShaderConstData.camRight = camRight;
  1297. mShaderConstData.camUp = camUp;
  1298. // Cull and submit render instances for cells.
  1299. for ( S32 i = 0; i < mCellGrid.size(); i++ )
  1300. {
  1301. GroundCoverCell* cell = mCellGrid[ i ];
  1302. if ( !cell )
  1303. continue;
  1304. if ( mCuller.isCulled( cell->getRenderBounds() ) )
  1305. continue;
  1306. cell->renderBillboards( state, mMatInst, &mPrimBuffer );
  1307. }
  1308. }
  1309. // Render TS shapes.
  1310. if ( !mDebugNoShapes )
  1311. {
  1312. // Prepare to render the grid shapes.
  1313. PROFILE_SCOPE(GroundCover_RenderShapes);
  1314. // Set up our TS render state.
  1315. TSRenderState rdata;
  1316. rdata.setSceneState( state );
  1317. // We might have some forward lit materials
  1318. // so pass down a query to gather lights.
  1319. LightQuery query;
  1320. rdata.setLightQuery( &query );
  1321. // TODO: Add a special fade out for DTS?
  1322. mCuller.setFarDist( mShapeCullRadius );
  1323. for ( S32 i = 0; i < mCellGrid.size(); i++ )
  1324. {
  1325. GroundCoverCell* cell = mCellGrid[ i ];
  1326. if ( !cell || mDebugNoShapes )
  1327. continue;
  1328. const Box3F &renderBounds = cell->getRenderBounds();
  1329. U32 clipMask = mCuller.testPlanes( renderBounds, Frustum::PlaneMaskAll );
  1330. if ( clipMask == -1 )
  1331. continue;
  1332. smStatRenderedCells++;
  1333. // Use the cell bounds as the light query volume.
  1334. //
  1335. // This means all forward lit items in this cell will
  1336. // get the same lights, but it performs much better.
  1337. query.init( renderBounds );
  1338. // Render the shapes in this cell... only pass the culler if the
  1339. // cell wasn't fully within the frustum.
  1340. smStatRenderedShapes += cell->renderShapes(
  1341. rdata,
  1342. clipMask != 0 ? &mCuller : NULL,
  1343. mShapeInstances );
  1344. }
  1345. }
  1346. if ( mDebugRenderCells )
  1347. {
  1348. RenderPassManager *pass = state->getRenderPass();
  1349. ObjectRenderInst *ri = pass->allocInst<ObjectRenderInst>();
  1350. ri->type = RenderPassManager::RIT_Editor;
  1351. ri->renderDelegate.bind( this, &GroundCover::_debugRender );
  1352. pass->addInst( ri );
  1353. }
  1354. }
  1355. void GroundCover::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
  1356. {
  1357. GFXDrawUtil* drawer = GFX->getDrawUtil();
  1358. GFXStateBlockDesc desc;
  1359. desc.setZReadWrite( true, false );
  1360. desc.setBlend( true );
  1361. desc.fillMode = GFXFillWireframe;
  1362. for ( S32 i = 0; i < mCellGrid.size(); i++ )
  1363. {
  1364. GroundCoverCell* cell = mCellGrid[ i ];
  1365. if ( !cell || ( cell->mBillboards.size() + cell->mShapes.size() ) == 0 )
  1366. continue;
  1367. if ( mCuller.isCulled( cell->getRenderBounds() ) )
  1368. continue;
  1369. drawer->drawCube( desc, cell->getRenderBounds().getExtents(), cell->getRenderBounds().getCenter(), ColorI( 0, 255, 0 ) );
  1370. }
  1371. }