groundCover.cpp 61 KB

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