decalManager.cpp 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747
  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/decal/decalManager.h"
  24. #include "scene/sceneManager.h"
  25. #include "scene/sceneRenderState.h"
  26. #include "ts/tsShapeInstance.h"
  27. #include "console/console.h"
  28. #include "console/dynamicTypes.h"
  29. #include "gfx/primBuilder.h"
  30. #include "console/consoleTypes.h"
  31. #include "platform/profiler.h"
  32. #include "gfx/gfxTransformSaver.h"
  33. #include "lighting/lightManager.h"
  34. #include "lighting/lightInfo.h"
  35. #include "gfx/gfxDrawUtil.h"
  36. #include "gfx/sim/gfxStateBlockData.h"
  37. #include "materials/shaderData.h"
  38. #include "materials/matInstance.h"
  39. #include "renderInstance/renderPassManager.h"
  40. #include "core/resourceManager.h"
  41. #include "core/stream/fileStream.h"
  42. #include "gfx/gfxDebugEvent.h"
  43. #include "math/util/quadTransforms.h"
  44. #include "math/mathUtils.h"
  45. #include "core/volume.h"
  46. #include "core/module.h"
  47. #include "T3D/decal/decalData.h"
  48. #include "console/engineAPI.h"
  49. extern bool gEditingMission;
  50. MODULE_BEGIN( DecalManager )
  51. MODULE_INIT_AFTER( Scene )
  52. MODULE_SHUTDOWN_BEFORE( Scene )
  53. MODULE_INIT
  54. {
  55. gDecalManager = new DecalManager;
  56. gClientSceneGraph->addObjectToScene( gDecalManager );
  57. }
  58. MODULE_SHUTDOWN
  59. {
  60. gClientSceneGraph->removeObjectFromScene( gDecalManager );
  61. SAFE_DELETE( gDecalManager );
  62. }
  63. MODULE_END;
  64. /// A bias applied to the nearPlane for Decal and DecalRoad rendering.
  65. /// Is set by by LevelInfo.
  66. F32 gDecalBias = 0.0015f;
  67. bool DecalManager::smDecalsOn = true;
  68. bool DecalManager::smDebugRender = false;
  69. F32 DecalManager::smDecalLifeTimeScale = 1.0f;
  70. bool DecalManager::smPoolBuffers = true;
  71. const U32 DecalManager::smMaxVerts = 6000;
  72. const U32 DecalManager::smMaxIndices = 10000;
  73. DecalManager *gDecalManager = NULL;
  74. IMPLEMENT_CONOBJECT(DecalManager);
  75. DECLARE_CATEGORY("UNLISTED");
  76. ConsoleDoc(
  77. "@defgroup Decals\n"
  78. "@brief Decals are non-SimObject derived objects that are stored and loaded "
  79. "separately from the normal mission file.\n\n"
  80. "The DecalManager handles all aspects of decal management including loading, "
  81. "creation, saving, and automatically deleting decals that have exceeded their "
  82. "lifeSpan.\n\n"
  83. "The static decals associated with a mission are normally loaded immediately "
  84. "after the mission itself has loaded as shown below.\n"
  85. "@tsexample\n"
  86. "// Load the static mission decals.\n"
  87. "decalManagerLoad( %missionName @ \".decals\" );\n"
  88. "@endtsexample\n"
  89. "@ingroup FX\n"
  90. );
  91. ConsoleDocClass( DecalManager,
  92. "@brief The object that manages all of the decals in the active mission.\n\n"
  93. "@see Decals\n"
  94. "@ingroup Decals\n"
  95. "@ingroup FX\n"
  96. );
  97. namespace {
  98. S32 QSORT_CALLBACK cmpDecalInstance(const void* p1, const void* p2)
  99. {
  100. const DecalInstance** pd1 = (const DecalInstance**)p1;
  101. const DecalInstance** pd2 = (const DecalInstance**)p2;
  102. return int(((char *)(*pd1)->mDataBlock) - ((char *)(*pd2)->mDataBlock));
  103. }
  104. S32 QSORT_CALLBACK cmpPointsXY( const void *p1, const void *p2 )
  105. {
  106. const Point3F *pnt1 = (const Point3F*)p1;
  107. const Point3F *pnt2 = (const Point3F*)p2;
  108. if ( pnt1->x < pnt2->x )
  109. return -1;
  110. else if ( pnt1->x > pnt2->x )
  111. return 1;
  112. else if ( pnt1->y < pnt2->y )
  113. return -1;
  114. else if ( pnt1->y > pnt2->y )
  115. return 1;
  116. else
  117. return 0;
  118. }
  119. S32 QSORT_CALLBACK cmpQuadPointTheta( const void *p1, const void *p2 )
  120. {
  121. const Point4F *pnt1 = (const Point4F*)p1;
  122. const Point4F *pnt2 = (const Point4F*)p2;
  123. if ( mFabs( pnt1->w ) > mFabs( pnt2->w ) )
  124. return 1;
  125. else if ( mFabs( pnt1->w ) < mFabs( pnt2->w ) )
  126. return -1;
  127. else
  128. return 0;
  129. }
  130. static Point3F gSortPoint;
  131. S32 QSORT_CALLBACK cmpDecalDistance( const void *p1, const void *p2 )
  132. {
  133. const DecalInstance** pd1 = (const DecalInstance**)p1;
  134. const DecalInstance** pd2 = (const DecalInstance**)p2;
  135. F32 dist1 = ( (*pd1)->mPosition - gSortPoint ).lenSquared();
  136. F32 dist2 = ( (*pd2)->mPosition - gSortPoint ).lenSquared();
  137. return mSign( dist1 - dist2 );
  138. }
  139. S32 QSORT_CALLBACK cmpDecalRenderOrder( const void *p1, const void *p2 )
  140. {
  141. const DecalInstance** pd1 = (const DecalInstance**)p1;
  142. const DecalInstance** pd2 = (const DecalInstance**)p2;
  143. if ( ( (*pd2)->mFlags & SaveDecal ) && !( (*pd1)->mFlags & SaveDecal ) )
  144. return -1;
  145. else if ( !( (*pd2)->mFlags & SaveDecal ) && ( (*pd1)->mFlags & SaveDecal ) )
  146. return 1;
  147. else
  148. {
  149. S32 priority = (*pd1)->getRenderPriority() - (*pd2)->getRenderPriority();
  150. if ( priority != 0 )
  151. return priority;
  152. if ( (*pd2)->mFlags & SaveDecal )
  153. {
  154. S32 id = ( (*pd1)->mDataBlock->getMaterialDefinition()->getId() - (*pd2)->mDataBlock->getMaterialDefinition()->getId() );
  155. if ( id != 0 )
  156. return id;
  157. return (*pd1)->mCreateTime - (*pd2)->mCreateTime;
  158. }
  159. else
  160. return (*pd1)->mCreateTime - (*pd2)->mCreateTime;
  161. }
  162. }
  163. } // namespace {}
  164. //-------------------------------------------------------------------------
  165. // DecalManager
  166. //-------------------------------------------------------------------------
  167. DecalManager::DecalManager()
  168. {
  169. #ifdef DECALMANAGER_DEBUG
  170. VECTOR_SET_ASSOCIATION( mDebugPlanes );
  171. #endif
  172. setGlobalBounds();
  173. mDataFileName = NULL;
  174. mTypeMask |= EnvironmentObjectType;
  175. mDirty = false;
  176. GFXDevice::getDeviceEventSignal().notify(this, &DecalManager::_handleGFXEvent);
  177. }
  178. DecalManager::~DecalManager()
  179. {
  180. GFXDevice::getDeviceEventSignal().remove(this, &DecalManager::_handleGFXEvent);
  181. clearData();
  182. }
  183. void DecalManager::consoleInit()
  184. {
  185. Con::addVariable( "$pref::Decals::enabled", TypeBool, &smDecalsOn,
  186. "Controls whether decals are rendered.\n"
  187. "@ingroup Decals" );
  188. Con::addVariable( "$pref::Decals::lifeTimeScale", TypeF32, &smDecalLifeTimeScale,
  189. "@brief Lifetime that decals will last after being created in the world.\n"
  190. "Deprecated. Use DecalData::lifeSpan instead.\n"
  191. "@ingroup Decals" );
  192. Con::addVariable( "$Decals::poolBuffers", TypeBool, &smPoolBuffers,
  193. "If true, will merge all PrimitiveBuffers and VertexBuffers into a pair "
  194. "of pools before clearing them at the end of a frame.\n"
  195. "If false, will just clear them at the end of a frame.\n"
  196. "@ingroup Decals" );
  197. Con::addVariable( "$Decals::debugRender", TypeBool, &smDebugRender,
  198. "If true, the decal spheres will be visualized when in the editor.\n\n"
  199. "@ingroup Decals" );
  200. Con::addVariable( "$Decals::sphereDistanceTolerance", TypeF32, &DecalSphere::smDistanceTolerance,
  201. "The distance at which the decal system will start breaking up decal "
  202. "spheres when adding new decals.\n\n"
  203. "@ingroup Decals" );
  204. Con::addVariable( "$Decals::sphereRadiusTolerance", TypeF32, &DecalSphere::smRadiusTolerance,
  205. "The radius beyond which the decal system will start breaking up decal "
  206. "spheres when adding new decals.\n\n"
  207. "@ingroup Decals" );
  208. }
  209. bool DecalManager::_handleGFXEvent(GFXDevice::GFXDeviceEventType event)
  210. {
  211. switch(event)
  212. {
  213. case GFXDevice::deEndOfFrame:
  214. // Return PrimitiveBuffers and VertexBuffers used this frame to the pool.
  215. if ( smPoolBuffers )
  216. {
  217. mPBPool.merge( mPBs );
  218. mPBs.clear();
  219. mVBPool.merge( mVBs );
  220. mVBs.clear();
  221. }
  222. else
  223. {
  224. _freePools();
  225. }
  226. break;
  227. default: ;
  228. }
  229. return true;
  230. }
  231. bool DecalManager::clipDecal( DecalInstance *decal, Vector<Point3F> *edgeVerts, const Point2F *clipDepth )
  232. {
  233. PROFILE_SCOPE( DecalManager_clipDecal );
  234. // Free old verts and indices.
  235. _freeBuffers( decal );
  236. F32 halfSize = decal->mSize * 0.5f;
  237. // Ugly hack for ProjectedShadow!
  238. F32 halfSizeZ = clipDepth ? clipDepth->x : halfSize;
  239. F32 negHalfSize = clipDepth ? clipDepth->y : halfSize;
  240. Point3F decalHalfSize( halfSize, halfSize, halfSize );
  241. Point3F decalHalfSizeZ( halfSizeZ, halfSizeZ, halfSizeZ );
  242. MatrixF projMat( true );
  243. decal->getWorldMatrix( &projMat );
  244. const VectorF &crossVec = decal->mNormal;
  245. const Point3F &decalPos = decal->mPosition;
  246. VectorF newFwd, newRight;
  247. projMat.getColumn( 0, &newRight );
  248. projMat.getColumn( 1, &newFwd );
  249. VectorF objRight( 1.0f, 0, 0 );
  250. VectorF objFwd( 0, 1.0f, 0 );
  251. VectorF objUp( 0, 0, 1.0f );
  252. // See above re: decalHalfSizeZ hack.
  253. mClipper.clear();
  254. mClipper.mPlaneList.setSize(6);
  255. mClipper.mPlaneList[0].set( ( decalPos + ( -newRight * halfSize ) ), -newRight );
  256. mClipper.mPlaneList[1].set( ( decalPos + ( -newFwd * halfSize ) ), -newFwd );
  257. mClipper.mPlaneList[2].set( ( decalPos + ( -crossVec * decalHalfSizeZ ) ), -crossVec );
  258. mClipper.mPlaneList[3].set( ( decalPos + ( newRight * halfSize ) ), newRight );
  259. mClipper.mPlaneList[4].set( ( decalPos + ( newFwd * halfSize ) ), newFwd );
  260. mClipper.mPlaneList[5].set( ( decalPos + ( crossVec * negHalfSize ) ), crossVec );
  261. mClipper.mNormal = decal->mNormal;
  262. const DecalData *decalData = decal->mDataBlock;
  263. mClipper.mNormalTolCosineRadians = mCos( mDegToRad( decalData->clippingAngle ) );
  264. Box3F box( -decalHalfSizeZ, decalHalfSizeZ );
  265. projMat.mul( box );
  266. PROFILE_START( DecalManager_clipDecal_buildPolyList );
  267. getContainer()->buildPolyList( PLC_Decal, box, decalData->clippingMasks, &mClipper );
  268. PROFILE_END();
  269. mClipper.cullUnusedVerts();
  270. mClipper.triangulate();
  271. const U32 numVerts = mClipper.mVertexList.size();
  272. const U32 numIndices = mClipper.mIndexList.size();
  273. if ( !numVerts || !numIndices )
  274. return false;
  275. // Fail if either of the buffer metrics exceeds our limits
  276. // on dynamic geometry buffers.
  277. if ( numVerts > smMaxVerts ||
  278. numIndices > smMaxIndices )
  279. return false;
  280. if ( !decalData->skipVertexNormals )
  281. mClipper.generateNormals();
  282. #ifdef DECALMANAGER_DEBUG
  283. mDebugPlanes.clear();
  284. mDebugPlanes.merge( mClipper.mPlaneList );
  285. #endif
  286. decal->mVertCount = numVerts;
  287. decal->mIndxCount = numIndices;
  288. Vector<Point3F> tmpPoints;
  289. tmpPoints.push_back(( objFwd * decalHalfSize ) + ( objRight * decalHalfSize ));
  290. tmpPoints.push_back(( objFwd * decalHalfSize ) + ( -objRight * decalHalfSize ));
  291. tmpPoints.push_back(( -objFwd * decalHalfSize ) + ( -objRight * decalHalfSize ));
  292. Point3F lowerLeft(( -objFwd * decalHalfSize ) + ( objRight * decalHalfSize ));
  293. projMat.inverse();
  294. _generateWindingOrder( lowerLeft, &tmpPoints );
  295. BiQuadToSqr quadToSquare( Point2F( lowerLeft.x, lowerLeft.y ),
  296. Point2F( tmpPoints[0].x, tmpPoints[0].y ),
  297. Point2F( tmpPoints[1].x, tmpPoints[1].y ),
  298. Point2F( tmpPoints[2].x, tmpPoints[2].y ) );
  299. Point2F uv( 0, 0 );
  300. Point3F vecX(0.0f, 0.0f, 0.0f);
  301. // Allocate memory for vert and index arrays
  302. _allocBuffers( decal );
  303. // Mark this so that the color will be assigned on these verts the next
  304. // time it renders, since we just threw away the previous verts.
  305. decal->mLastAlpha = -1;
  306. Point3F vertPoint( 0, 0, 0 );
  307. for ( U32 i = 0; i < mClipper.mVertexList.size(); i++ )
  308. {
  309. const ClippedPolyList::Vertex &vert = mClipper.mVertexList[i];
  310. vertPoint = vert.point;
  311. // Transform this point to
  312. // object space to look up the
  313. // UV coordinate for this vertex.
  314. projMat.mulP( vertPoint );
  315. // Clamp the point to be within the quad.
  316. vertPoint.x = mClampF( vertPoint.x, -decalHalfSize.x, decalHalfSize.x );
  317. vertPoint.y = mClampF( vertPoint.y, -decalHalfSize.y, decalHalfSize.y );
  318. // Get our UV.
  319. uv = quadToSquare.transform( Point2F( vertPoint.x, vertPoint.y ) );
  320. const RectF &rect = decal->mDataBlock->texRect[decal->mTextureRectIdx];
  321. uv *= rect.extent;
  322. uv += rect.point;
  323. // Set the world space vertex position.
  324. decal->mVerts[i].point = vert.point;
  325. decal->mVerts[i].texCoord.set( uv.x, uv.y );
  326. if ( mClipper.mNormalList.empty() )
  327. continue;
  328. decal->mVerts[i].normal = mClipper.mNormalList[i];
  329. decal->mVerts[i].normal.normalize();
  330. if( mFabs( decal->mVerts[i].normal.z ) > 0.8f )
  331. mCross( decal->mVerts[i].normal, Point3F( 1.0f, 0.0f, 0.0f ), &vecX );
  332. else if ( mFabs( decal->mVerts[i].normal.x ) > 0.8f )
  333. mCross( decal->mVerts[i].normal, Point3F( 0.0f, 1.0f, 0.0f ), &vecX );
  334. else if ( mFabs( decal->mVerts[i].normal.y ) > 0.8f )
  335. mCross( decal->mVerts[i].normal, Point3F( 0.0f, 0.0f, 1.0f ), &vecX );
  336. decal->mVerts[i].tangent = mCross( decal->mVerts[i].normal, vecX );
  337. }
  338. U32 curIdx = 0;
  339. for ( U32 j = 0; j < mClipper.mPolyList.size(); j++ )
  340. {
  341. // Write indices for each Poly
  342. ClippedPolyList::Poly *poly = &mClipper.mPolyList[j];
  343. AssertFatal( poly->vertexCount == 3, "Got non-triangle poly!" );
  344. decal->mIndices[curIdx] = mClipper.mIndexList[poly->vertexStart];
  345. curIdx++;
  346. decal->mIndices[curIdx] = mClipper.mIndexList[poly->vertexStart + 1];
  347. curIdx++;
  348. decal->mIndices[curIdx] = mClipper.mIndexList[poly->vertexStart + 2];
  349. curIdx++;
  350. }
  351. if ( !edgeVerts )
  352. return true;
  353. Point3F tmpHullPt( 0, 0, 0 );
  354. Vector<Point3F> tmpHullPts;
  355. for ( U32 i = 0; i < mClipper.mVertexList.size(); i++ )
  356. {
  357. const ClippedPolyList::Vertex &vert = mClipper.mVertexList[i];
  358. tmpHullPt = vert.point;
  359. projMat.mulP( tmpHullPt );
  360. tmpHullPts.push_back( tmpHullPt );
  361. }
  362. edgeVerts->clear();
  363. U32 verts = _generateConvexHull( tmpHullPts, edgeVerts );
  364. edgeVerts->setSize( verts );
  365. projMat.inverse();
  366. for ( U32 i = 0; i < edgeVerts->size(); i++ )
  367. projMat.mulP( (*edgeVerts)[i] );
  368. return true;
  369. }
  370. DecalInstance* DecalManager::addDecal( const Point3F &pos,
  371. const Point3F &normal,
  372. F32 rotAroundNormal,
  373. DecalData *decalData,
  374. F32 decalScale,
  375. S32 decalTexIndex,
  376. U8 flags )
  377. {
  378. MatrixF mat( true );
  379. MathUtils::getMatrixFromUpVector( normal, &mat );
  380. AngAxisF rot( normal, rotAroundNormal );
  381. MatrixF rotmat;
  382. rot.setMatrix( &rotmat );
  383. mat.mul( rotmat );
  384. Point3F tangent;
  385. mat.getColumn( 1, &tangent );
  386. return addDecal( pos, normal, tangent, decalData, decalScale, decalTexIndex, flags );
  387. }
  388. DecalInstance* DecalManager::addDecal( const Point3F& pos,
  389. const Point3F& normal,
  390. const Point3F& tangent,
  391. DecalData* decalData,
  392. F32 decalScale,
  393. S32 decalTexIndex,
  394. U8 flags )
  395. {
  396. if ( !mData && !_createDataFile() )
  397. return NULL;
  398. // only dirty the manager if this decal should be saved
  399. if ( flags & SaveDecal )
  400. mDirty = true;
  401. return mData->addDecal( pos, normal, tangent, decalData, decalScale, decalTexIndex, flags );
  402. }
  403. void DecalManager::removeDecal( DecalInstance *inst )
  404. {
  405. // If this is a decal we save then we need
  406. // to set the dirty flag.
  407. if ( inst->mFlags & SaveDecal )
  408. mDirty = true;
  409. // Remove the decal from the instance vector.
  410. if( inst->mId != -1 && inst->mId < mDecalInstanceVec.size() )
  411. mDecalInstanceVec[ inst->mId ] = NULL;
  412. // Release its geometry (if it has any).
  413. _freeBuffers( inst );
  414. // Remove it from the decal file.
  415. if ( mData )
  416. mData->removeDecal( inst );
  417. }
  418. DecalInstance* DecalManager::getDecal( S32 id )
  419. {
  420. if( id < 0 || id >= mDecalInstanceVec.size() )
  421. return NULL;
  422. return mDecalInstanceVec[id];
  423. }
  424. void DecalManager::notifyDecalModified( DecalInstance *inst )
  425. {
  426. // If this is a decal we save then we need
  427. // to set the dirty flag.
  428. if ( inst->mFlags & SaveDecal )
  429. mDirty = true;
  430. if ( mData )
  431. mData->notifyDecalModified( inst );
  432. }
  433. DecalInstance* DecalManager::getClosestDecal( const Point3F &pos )
  434. {
  435. if ( !mData )
  436. return NULL;
  437. const Vector<DecalSphere*> &grid = mData->getSphereList();
  438. DecalInstance *inst = NULL;
  439. SphereF worldPickSphere( pos, 0.5f );
  440. SphereF worldInstSphere( Point3F( 0, 0, 0 ), 1.0f );
  441. Vector<DecalInstance*> collectedInsts;
  442. for ( U32 i = 0; i < grid.size(); i++ )
  443. {
  444. DecalSphere *decalSphere = grid[i];
  445. const SphereF &worldSphere = decalSphere->mWorldSphere;
  446. if ( !worldSphere.isIntersecting( worldPickSphere ) &&
  447. !worldSphere.isContained( pos ) )
  448. continue;
  449. const Vector<DecalInstance*> &items = decalSphere->mItems;
  450. for ( U32 n = 0; n < items.size(); n++ )
  451. {
  452. inst = items[n];
  453. if ( !inst )
  454. continue;
  455. worldInstSphere.center = inst->mPosition;
  456. worldInstSphere.radius = inst->mSize;
  457. if ( !worldInstSphere.isContained( inst->mPosition ) )
  458. continue;
  459. collectedInsts.push_back( inst );
  460. }
  461. }
  462. F32 closestDistance = F32_MAX;
  463. F32 currentDist = 0;
  464. U32 closestIndex = 0;
  465. for ( U32 i = 0; i < collectedInsts.size(); i++ )
  466. {
  467. inst = collectedInsts[i];
  468. currentDist = (inst->mPosition - pos).len();
  469. if ( currentDist < closestDistance )
  470. {
  471. closestIndex = i;
  472. closestDistance = currentDist;
  473. worldInstSphere.center = inst->mPosition;
  474. worldInstSphere.radius = inst->mSize;
  475. }
  476. }
  477. if ( (!collectedInsts.empty() &&
  478. collectedInsts[closestIndex] &&
  479. closestDistance < 1.0f) ||
  480. worldInstSphere.isContained( pos ) )
  481. return collectedInsts[closestIndex];
  482. else
  483. return NULL;
  484. }
  485. DecalInstance* DecalManager::raycast( const Point3F &start, const Point3F &end, bool savedDecalsOnly )
  486. {
  487. if ( !mData )
  488. return NULL;
  489. const Vector<DecalSphere*> &grid = mData->getSphereList();
  490. DecalInstance *inst = NULL;
  491. SphereF worldSphere( Point3F( 0, 0, 0 ), 1.0f );
  492. Vector<DecalInstance*> hitDecals;
  493. for ( U32 i = 0; i < grid.size(); i++ )
  494. {
  495. DecalSphere *decalSphere = grid[i];
  496. if ( !decalSphere->mWorldSphere.intersectsRay( start, end ) )
  497. continue;
  498. const Vector<DecalInstance*> &items = decalSphere->mItems;
  499. for ( U32 n = 0; n < items.size(); n++ )
  500. {
  501. inst = items[n];
  502. if ( !inst )
  503. continue;
  504. if ( savedDecalsOnly && !(inst->mFlags & SaveDecal) )
  505. continue;
  506. worldSphere.center = inst->mPosition;
  507. worldSphere.radius = inst->mSize;
  508. if ( !worldSphere.intersectsRay( start, end ) )
  509. continue;
  510. RayInfo ri;
  511. bool containsPoint = false;
  512. if ( gServerContainer.castRayRendered( start, end, STATIC_COLLISION_TYPEMASK, &ri ) )
  513. {
  514. Point2F poly[4];
  515. poly[0].set( inst->mPosition.x - (inst->mSize / 2), inst->mPosition.y + (inst->mSize / 2));
  516. poly[1].set( inst->mPosition.x - (inst->mSize / 2), inst->mPosition.y - (inst->mSize / 2));
  517. poly[2].set( inst->mPosition.x + (inst->mSize / 2), inst->mPosition.y - (inst->mSize / 2));
  518. poly[3].set( inst->mPosition.x + (inst->mSize / 2), inst->mPosition.y + (inst->mSize / 2));
  519. if ( MathUtils::pointInPolygon( poly, 4, Point2F(ri.point.x, ri.point.y) ) )
  520. containsPoint = true;
  521. }
  522. if( !containsPoint )
  523. continue;
  524. hitDecals.push_back( inst );
  525. }
  526. }
  527. if ( hitDecals.empty() )
  528. return NULL;
  529. gSortPoint = start;
  530. dQsort( hitDecals.address(), hitDecals.size(), sizeof(DecalInstance*), cmpDecalDistance );
  531. return hitDecals[0];
  532. }
  533. U32 DecalManager::_generateConvexHull( const Vector<Point3F> &points, Vector<Point3F> *outPoints )
  534. {
  535. PROFILE_SCOPE( DecalManager_generateConvexHull );
  536. // chainHull_2D(): Andrew's monotone chain 2D convex hull algorithm
  537. // Input: P[] = an array of 2D points
  538. // presorted by increasing x- and y-coordinates
  539. // n = the number of points in P[]
  540. // Output: H[] = an array of the convex hull vertices (max is n)
  541. // Return: the number of points in H[]
  542. //int
  543. if ( points.size() < 3 )
  544. {
  545. outPoints->merge( points );
  546. return outPoints->size();
  547. }
  548. // Sort our input points.
  549. dQsort( points.address(), points.size(), sizeof( Point3F ), cmpPointsXY );
  550. U32 n = points.size();
  551. Vector<Point3F> tmpPoints;
  552. tmpPoints.setSize( n );
  553. // the output array H[] will be used as the stack
  554. S32 bot=0, top=(-1); // indices for bottom and top of the stack
  555. S32 i; // array scan index
  556. S32 toptmp = 0;
  557. // Get the indices of points with min x-coord and min|max y-coord
  558. S32 minmin = 0, minmax;
  559. F32 xmin = points[0].x;
  560. for ( i = 1; i < n; i++ )
  561. if (points[i].x != xmin)
  562. break;
  563. minmax = i - 1;
  564. if ( minmax == n - 1 )
  565. {
  566. // degenerate case: all x-coords == xmin
  567. toptmp = top + 1;
  568. if ( toptmp < n )
  569. tmpPoints[++top] = points[minmin];
  570. if ( points[minmax].y != points[minmin].y ) // a nontrivial segment
  571. {
  572. toptmp = top + 1;
  573. if ( toptmp < n )
  574. tmpPoints[++top] = points[minmax];
  575. }
  576. toptmp = top + 1;
  577. if ( toptmp < n )
  578. tmpPoints[++top] = points[minmin]; // add polygon endpoint
  579. return top+1;
  580. }
  581. // Get the indices of points with max x-coord and min|max y-coord
  582. S32 maxmin, maxmax = n-1;
  583. F32 xmax = points[n-1].x;
  584. for ( i = n - 2; i >= 0; i-- )
  585. if ( points[i].x != xmax )
  586. break;
  587. maxmin = i + 1;
  588. // Compute the lower hull on the stack H
  589. toptmp = top + 1;
  590. if ( toptmp < n )
  591. tmpPoints[++top] = points[minmin]; // push minmin point onto stack
  592. i = minmax;
  593. while ( ++i <= maxmin )
  594. {
  595. // the lower line joins P[minmin] with P[maxmin]
  596. if (i < maxmin && isLeft(points[minmin], points[maxmin], points[i]) >= 0)
  597. continue; // ignore P[i] above or on the lower line
  598. while (top > 0) // there are at least 2 points on the stack
  599. {
  600. // test if P[i] is left of the line at the stack top
  601. if ( isLeft( tmpPoints[top-1], tmpPoints[top], points[i]) > 0)
  602. break; // P[i] is a new hull vertex
  603. else
  604. top--; // pop top point off stack
  605. }
  606. toptmp = top + 1;
  607. if ( toptmp < n )
  608. tmpPoints[++top] = points[i]; // push P[i] onto stack
  609. }
  610. // Next, compute the upper hull on the stack H above the bottom hull
  611. if (maxmax != maxmin) // if distinct xmax points
  612. {
  613. toptmp = top + 1;
  614. if ( toptmp < n )
  615. tmpPoints[++top] = points[maxmax]; // push maxmax point onto stack
  616. }
  617. bot = top; // the bottom point of the upper hull stack
  618. i = maxmin;
  619. while (--i >= minmax)
  620. {
  621. // the upper line joins P[maxmax] with P[minmax]
  622. if ( isLeft( points[maxmax], points[minmax], points[i] ) >= 0 && i > minmax )
  623. continue; // ignore P[i] below or on the upper line
  624. while ( top > bot ) // at least 2 points on the upper stack
  625. {
  626. // test if P[i] is left of the line at the stack top
  627. if ( isLeft( tmpPoints[top-1], tmpPoints[top], points[i] ) > 0 )
  628. break; // P[i] is a new hull vertex
  629. else
  630. top--; // pop top point off stack
  631. }
  632. toptmp = top + 1;
  633. if ( toptmp < n )
  634. tmpPoints[++top] = points[i]; // push P[i] onto stack
  635. }
  636. if (minmax != minmin)
  637. {
  638. toptmp = top + 1;
  639. if ( toptmp < n )
  640. tmpPoints[++top] = points[minmin]; // push joining endpoint onto stack
  641. }
  642. outPoints->merge( tmpPoints );
  643. return top + 1;
  644. }
  645. void DecalManager::_generateWindingOrder( const Point3F &cornerPoint, Vector<Point3F> *sortPoints )
  646. {
  647. // This block of code is used to find
  648. // the winding order for the points in our quad.
  649. // First, choose an arbitrary corner point.
  650. // We'll use the "lowerRight" point.
  651. Point3F relPoint( 0, 0, 0 );
  652. // See comment below about radius.
  653. //F32 radius = 0;
  654. F32 theta = 0;
  655. Vector<Point4F> tmpPoints;
  656. for ( U32 i = 0; i < (*sortPoints).size(); i++ )
  657. {
  658. const Point3F &pnt = (*sortPoints)[i];
  659. relPoint = cornerPoint - pnt;
  660. // Get the radius (r^2 = x^2 + y^2).
  661. // This is commented because for a quad
  662. // you typically can't have the same values
  663. // for theta, which is the caveat which would
  664. // require sorting by the radius.
  665. //radius = mSqrt( (relPoint.x * relPoint.x) + (relPoint.y * relPoint.y) );
  666. // Get the theta value for the
  667. // interval -PI, PI.
  668. // This algorithm for determining the
  669. // theta value is defined by
  670. // | arctan( y / x ) if x > 0
  671. // | arctan( y / x ) if x < 0 and y >= 0
  672. // theta = | arctan( y / x ) if x < 0 and y < 0
  673. // | PI / 2 if x = 0 and y > 0
  674. // | -( PI / 2 ) if x = 0 and y < 0
  675. if ( relPoint.x > 0.0f )
  676. theta = mAtan2( relPoint.y, relPoint.x );
  677. else if ( relPoint.x < 0.0f )
  678. {
  679. if ( relPoint.y >= 0.0f )
  680. theta = mAtan2( relPoint.y, relPoint.x ) + M_PI_F;
  681. else if ( relPoint.y < 0.0f )
  682. theta = mAtan2( relPoint.y, relPoint.x ) - M_PI_F;
  683. }
  684. else if ( relPoint.x == 0.0f )
  685. {
  686. if ( relPoint.y > 0.0f )
  687. theta = M_PI_F / 2.0f;
  688. else if ( relPoint.y < 0.0f )
  689. theta = -(M_PI_F / 2.0f);
  690. }
  691. tmpPoints.push_back( Point4F( pnt.x, pnt.y, pnt.z, theta ) );
  692. }
  693. dQsort( tmpPoints.address(), tmpPoints.size(), sizeof( Point4F ), cmpQuadPointTheta );
  694. for ( U32 i = 0; i < tmpPoints.size(); i++ )
  695. {
  696. const Point4F &tmpPoint = tmpPoints[i];
  697. (*sortPoints)[i].set( tmpPoint.x, tmpPoint.y, tmpPoint.z );
  698. }
  699. }
  700. void DecalManager::_allocBuffers( DecalInstance *inst )
  701. {
  702. inst->mAllocHandle = mChunkers.alloc(sizeof(DecalVertex) * inst->mVertCount + sizeof(U16) * inst->mIndxCount);
  703. U8* data = (U8*)inst->mAllocHandle.ptr;
  704. inst->mVerts = reinterpret_cast< DecalVertex* >( data );
  705. data = (U8*)data + sizeof( DecalVertex ) * inst->mVertCount;
  706. inst->mIndices = reinterpret_cast< U16* >( data );
  707. }
  708. void DecalManager::_freeBuffers( DecalInstance *inst )
  709. {
  710. if ( inst->mVerts != NULL )
  711. {
  712. mChunkers.free(inst->mAllocHandle);
  713. inst->mVerts = NULL;
  714. inst->mVertCount = 0;
  715. inst->mIndices = NULL;
  716. inst->mIndxCount = 0;
  717. }
  718. }
  719. void DecalManager::_freePools()
  720. {
  721. while ( !mVBPool.empty() )
  722. {
  723. delete mVBPool.last();
  724. mVBPool.pop_back();
  725. }
  726. while ( !mVBs.empty() )
  727. {
  728. delete mVBs.last();
  729. mVBs.pop_back();
  730. }
  731. while ( !mPBPool.empty() )
  732. {
  733. delete mPBPool.last();
  734. mPBPool.pop_back();
  735. }
  736. while ( !mPBs.empty() )
  737. {
  738. delete mPBs.last();
  739. mPBs.pop_back();
  740. }
  741. }
  742. void DecalManager::prepRenderImage( SceneRenderState* state )
  743. {
  744. PROFILE_SCOPE( DecalManager_RenderDecals );
  745. if ( !smDecalsOn || !mData )
  746. return;
  747. // Decals only render in the diffuse pass!
  748. // We technically could render them into reflections but prefer to save
  749. // the performance. This would also break the DecalInstance::mLastAlpha
  750. // optimization.
  751. if ( !state->isDiffusePass() )
  752. return;
  753. PROFILE_START( DecalManager_RenderDecals_SphereTreeCull );
  754. const Frustum& rootFrustum = state->getCameraFrustum();
  755. // Populate vector of decal instances to be rendered with all
  756. // decals from visible decal spheres.
  757. SceneManager* sceneManager = state->getSceneManager();
  758. SceneZoneSpaceManager* zoneManager = sceneManager->getZoneManager();
  759. AssertFatal( zoneManager, "DecalManager::prepRenderImage - No zone manager!" );
  760. const Vector<DecalSphere*> &grid = mData->getSphereList();
  761. const bool haveOnlyOutdoorZone = ( zoneManager->getNumActiveZones() == 1 );
  762. mDecalQueue.clear();
  763. for ( U32 i = 0; i < grid.size(); i++ )
  764. {
  765. DecalSphere* decalSphere = grid[i];
  766. const SphereF& worldSphere = decalSphere->mWorldSphere;
  767. // See if this decal sphere can be culled.
  768. const SceneCullingState& cullingState = state->getCullingState();
  769. if( haveOnlyOutdoorZone )
  770. {
  771. U32 outdoorZone = SceneZoneSpaceManager::RootZoneId;
  772. if( cullingState.isCulled( worldSphere, &outdoorZone, 1 ) )
  773. continue;
  774. }
  775. else
  776. {
  777. // Update the zoning state of the sphere, if we need to.
  778. if( decalSphere->mZones.size() == 0 )
  779. decalSphere->updateZoning( zoneManager );
  780. // Skip the sphere if it is not visible in any of its zones.
  781. if( cullingState.isCulled( worldSphere, decalSphere->mZones.address(), decalSphere->mZones.size() ) )
  782. continue;
  783. }
  784. // TODO: If each sphere stored its largest decal instance we
  785. // could do an LOD step on it here and skip adding any of the
  786. // decals in the sphere.
  787. mDecalQueue.merge( decalSphere->mItems );
  788. }
  789. PROFILE_END();
  790. PROFILE_START( DecalManager_RenderDecals_Update );
  791. const U32 &curSimTime = Sim::getCurrentTime();
  792. F32 pixelSize;
  793. U32 delta, diff;
  794. DecalInstance *dinst;
  795. DecalData *ddata;
  796. // Loop through DecalQueue once for preRendering work.
  797. // 1. Update DecalInstance fade (over time)
  798. // 2. Clip geometry if flagged to do so.
  799. // 3. Calculate lod - if decal is far enough away it will not render.
  800. for ( U32 i = 0; i < mDecalQueue.size(); i++ )
  801. {
  802. dinst = mDecalQueue[i];
  803. ddata = dinst->mDataBlock;
  804. // LOD calculation...
  805. pixelSize = dinst->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y );
  806. if ( pixelSize != F32_MAX && pixelSize < ddata->fadeEndPixelSize )
  807. {
  808. mDecalQueue.erase_fast( i );
  809. i--;
  810. continue;
  811. }
  812. // We will try to render this decal... so do any
  813. // final adjustments to it before rendering.
  814. // Update fade and delete expired.
  815. if ( !( dinst->mFlags & PermanentDecal || dinst->mFlags & CustomDecal ) )
  816. {
  817. delta = ( curSimTime - dinst->mCreateTime );
  818. if ( delta > dinst->mDataBlock->lifeSpan )
  819. {
  820. diff = delta - dinst->mDataBlock->lifeSpan;
  821. dinst->mVisibility = 1.0f - (F32)diff / (F32)dinst->mDataBlock->fadeTime;
  822. if ( dinst->mVisibility <= 0.0f )
  823. {
  824. mDecalQueue.erase_fast( i );
  825. removeDecal( dinst );
  826. i--;
  827. continue;
  828. }
  829. }
  830. }
  831. // Build clipped geometry for this decal if needed.
  832. if ( dinst->mFlags & ClipDecal && !( dinst->mFlags & CustomDecal ) )
  833. {
  834. // Turn off the flag so we don't continually try to clip
  835. // if it fails.
  836. dinst->mFlags = dinst->mFlags & ~ClipDecal;
  837. if ( !(dinst->mFlags & CustomDecal) && !clipDecal( dinst ) )
  838. {
  839. // Clipping failed to get any geometry...
  840. // Remove it from the render queue.
  841. mDecalQueue.erase_fast( i );
  842. i--;
  843. // If the decal is one placed at run-time (not the editor)
  844. // then we should also permanently delete the decal instance.
  845. if ( !(dinst->mFlags & SaveDecal) )
  846. {
  847. removeDecal( dinst );
  848. }
  849. // If this is a decal placed by the editor it will be
  850. // flagged to attempt clipping again the next time it is
  851. // modified. For now we just skip rendering it.
  852. continue;
  853. }
  854. }
  855. // If we get here and the decal still does not have any geometry
  856. // skip rendering it. It must be an editor placed decal that failed
  857. // to clip any geometry but has not yet been flagged to try again.
  858. if ( !dinst->mVerts || dinst->mVertCount == 0 || dinst->mIndxCount == 0 )
  859. {
  860. mDecalQueue.erase_fast( i );
  861. i--;
  862. continue;
  863. }
  864. // Calculate the alpha value for this decal and apply it to the verts.
  865. {
  866. PROFILE_START( DecalManager_RenderDecals_Update_SetAlpha );
  867. F32 alpha = 1.0f;
  868. // Only necessary for decals which fade over time or distance.
  869. if ( !( dinst->mFlags & PermanentDecal ) || dinst->mDataBlock->fadeStartPixelSize >= 0.0f )
  870. {
  871. if ( pixelSize < ddata->fadeStartPixelSize )
  872. {
  873. const F32 range = ddata->fadeStartPixelSize - ddata->fadeEndPixelSize;
  874. alpha = 1.0f - mClampF( ( ddata->fadeStartPixelSize - pixelSize ) / range, 0.0f, 1.0f );
  875. }
  876. alpha *= dinst->mVisibility;
  877. }
  878. // If the alpha value has not changed since last render avoid
  879. // looping through all the verts!
  880. if ( alpha != dinst->mLastAlpha )
  881. {
  882. // calculate the swizzles color once, outside the loop.
  883. GFXVertexColor color;
  884. color.set( 255, 255, 255, (U8)(alpha * 255.0f) );
  885. for ( U32 v = 0; v < dinst->mVertCount; v++ )
  886. dinst->mVerts[v].color = color;
  887. dinst->mLastAlpha = alpha;
  888. }
  889. PROFILE_END();
  890. }
  891. }
  892. PROFILE_END();
  893. if ( mDecalQueue.empty() )
  894. return;
  895. // Sort queued decals...
  896. // 1. Editor decals - in render priority order first, creation time second, and material third.
  897. // 2. Dynamic decals - in render priority order first and creation time second.
  898. //
  899. // With the constraint that decals with different render priority cannot
  900. // be rendered together in the same draw call.
  901. PROFILE_START( DecalManager_RenderDecals_Sort );
  902. dQsort( mDecalQueue.address(), mDecalQueue.size(), sizeof(DecalInstance*), cmpDecalRenderOrder );
  903. PROFILE_END();
  904. PROFILE_SCOPE( DecalManager_RenderDecals_RenderBatch );
  905. RenderPassManager *renderPass = state->getRenderPass();
  906. // Base render instance we use for convenience.
  907. // Data shared by all instances we allocate below can be copied
  908. // from the base instance at the same time.
  909. MeshRenderInst baseRenderInst;
  910. baseRenderInst.clear();
  911. MatrixF *tempMat = renderPass->allocUniqueXform( MatrixF( true ) );
  912. MathUtils::getZBiasProjectionMatrix( gDecalBias, rootFrustum, tempMat );
  913. baseRenderInst.projection = tempMat;
  914. baseRenderInst.objectToWorld = &MatrixF::Identity;
  915. baseRenderInst.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
  916. baseRenderInst.type = RenderPassManager::RIT_Decal;
  917. // Make it the sort distance the max distance so that
  918. // it renders after all the other opaque geometry in
  919. // the deferred bin.
  920. baseRenderInst.sortDistSq = F32_MAX;
  921. Vector<DecalBatch> batches;
  922. DecalBatch *currentBatch = NULL;
  923. // Loop through DecalQueue collecting them into render batches.
  924. for ( U32 i = 0; i < mDecalQueue.size(); i++ )
  925. {
  926. DecalInstance *decal = mDecalQueue[i];
  927. DecalData *data = decal->mDataBlock;
  928. Material *mat = data->getMaterialDefinition();
  929. if ( currentBatch == NULL )
  930. {
  931. // Start a new batch, beginning with this decal.
  932. batches.increment();
  933. currentBatch = &batches.last();
  934. currentBatch->startDecal = i;
  935. currentBatch->decalCount = 1;
  936. // Shrink and warning: preventing a potential crash.
  937. currentBatch->iCount =
  938. (decal->mIndxCount > smMaxIndices) ? smMaxIndices : decal->mIndxCount;
  939. currentBatch->vCount =
  940. (decal->mVertCount > smMaxVerts) ? smMaxVerts : decal->mVertCount;
  941. #ifdef TORQUE_DEBUG
  942. // we didn't mean send a spam to the console
  943. static U32 countMsgIndx = 0;
  944. if ( (decal->mIndxCount > smMaxIndices) && ((countMsgIndx++ % 1024) == 0) ) {
  945. Con::warnf(
  946. "DecalManager::prepRenderImage() - Shrinked indices of decal."
  947. " Lost %u.", (decal->mIndxCount - smMaxIndices)
  948. );
  949. }
  950. static U32 countMsgVert = 0;
  951. if ( (decal->mVertCount > smMaxVerts) && ((countMsgVert++ % 1024) == 0) ) {
  952. Con::warnf(
  953. "DecalManager::prepRenderImage() - Shrinked vertices of decal."
  954. " Lost %u.", (decal->mVertCount - smMaxVerts)
  955. );
  956. }
  957. #endif
  958. currentBatch->mat = mat;
  959. currentBatch->matInst = decal->mDataBlock->getMaterialInstance();
  960. currentBatch->priority = decal->getRenderPriority();
  961. currentBatch->dynamic = !(decal->mFlags & SaveDecal);
  962. continue;
  963. }
  964. if ( currentBatch->iCount + decal->mIndxCount >= smMaxIndices ||
  965. currentBatch->vCount + decal->mVertCount >= smMaxVerts ||
  966. currentBatch->mat != mat ||
  967. currentBatch->priority != decal->getRenderPriority() ||
  968. decal->mCustomTex )
  969. {
  970. // End batch.
  971. currentBatch = NULL;
  972. i--;
  973. continue;
  974. }
  975. // Add on to current batch.
  976. currentBatch->decalCount++;
  977. currentBatch->iCount += decal->mIndxCount;
  978. currentBatch->vCount += decal->mVertCount;
  979. }
  980. // Make sure our primitive and vertex buffer handle storage is
  981. // big enough to take all batches. Doing this now avoids reallocation
  982. // later on which would invalidate all the pointers we already had
  983. // passed into render instances.
  984. //mPBs.reserve( batches.size() );
  985. //mVBs.reserve( batches.size() );
  986. // System memory array of verts and indices so we can fill them incrementally
  987. // and then memcpy to the graphics device buffers in one call.
  988. static DecalVertex vertData[smMaxVerts];
  989. static U16 indexData[smMaxIndices];
  990. // Loop through batches allocating buffers and submitting render instances.
  991. for ( U32 i = 0; i < batches.size(); i++ )
  992. {
  993. currentBatch = &batches[i];
  994. // Copy data into the system memory arrays, from all decals in this batch...
  995. DecalVertex *vpPtr = vertData;
  996. U16 *pbPtr = indexData;
  997. U32 lastDecal = currentBatch->startDecal + currentBatch->decalCount;
  998. U32 voffset = 0;
  999. U32 ioffset = 0;
  1000. // This is an ugly hack for ProjectedShadow!
  1001. GFXTextureObject *customTex = NULL;
  1002. for ( U32 j = currentBatch->startDecal; j < lastDecal; j++ )
  1003. {
  1004. dinst = mDecalQueue[j];
  1005. const U32 indxCount =
  1006. (dinst->mIndxCount > currentBatch->iCount) ?
  1007. currentBatch->iCount : dinst->mIndxCount;
  1008. for ( U32 k = 0; k < indxCount; k++ )
  1009. {
  1010. *( pbPtr + ioffset + k ) = dinst->mIndices[k] + voffset;
  1011. }
  1012. ioffset += indxCount;
  1013. const U32 vertCount =
  1014. (dinst->mVertCount > currentBatch->vCount) ?
  1015. currentBatch->vCount : dinst->mVertCount;
  1016. dMemcpy( vpPtr + voffset, dinst->mVerts, sizeof( DecalVertex ) * vertCount );
  1017. voffset += vertCount;
  1018. // Ugly hack for ProjectedShadow!
  1019. if ( (dinst->mFlags & CustomDecal) && dinst->mCustomTex != NULL )
  1020. customTex = *dinst->mCustomTex;
  1021. }
  1022. AssertFatal( ioffset == currentBatch->iCount, "bad" );
  1023. AssertFatal( voffset == currentBatch->vCount, "bad" );
  1024. // Get handles to video memory buffers we will be filling...
  1025. GFXVertexBufferHandle<DecalVertex> *vb = NULL;
  1026. if ( mVBPool.empty() )
  1027. {
  1028. // If the Pool is empty allocate a new one.
  1029. vb = new GFXVertexBufferHandle<DecalVertex>;
  1030. vb->set( GFX, smMaxVerts, GFXBufferTypeDynamic );
  1031. }
  1032. else
  1033. {
  1034. // Otherwise grab from the pool.
  1035. vb = mVBPool.last();
  1036. mVBPool.pop_back();
  1037. }
  1038. // Push into our vector of 'in use' buffers.
  1039. mVBs.push_back( vb );
  1040. // Ready to start filling.
  1041. vpPtr = vb->lock();
  1042. // Same deal as above...
  1043. GFXPrimitiveBufferHandle *pb = NULL;
  1044. if ( mPBPool.empty() )
  1045. {
  1046. pb = new GFXPrimitiveBufferHandle;
  1047. pb->set( GFX, smMaxIndices, 0, GFXBufferTypeDynamic );
  1048. }
  1049. else
  1050. {
  1051. pb = mPBPool.last();
  1052. mPBPool.pop_back();
  1053. }
  1054. mPBs.push_back( pb );
  1055. pb->lock( &pbPtr );
  1056. // Memcpy from system to video memory.
  1057. const U32 vpCount = sizeof( DecalVertex ) * currentBatch->vCount;
  1058. dMemcpy( vpPtr, vertData, vpCount );
  1059. const U32 pbCount = sizeof( U16 ) * currentBatch->iCount;
  1060. dMemcpy( pbPtr, indexData, pbCount );
  1061. pb->unlock();
  1062. vb->unlock();
  1063. // DecalManager must hold handles to these buffers so they remain valid,
  1064. // we don't actually use them elsewhere.
  1065. //mPBs.push_back( pb );
  1066. //mVBs.push_back( vb );
  1067. // Get the best lights for the current camera position
  1068. // if the materail is forward lit and we haven't got them yet.
  1069. if ( currentBatch->matInst->isForwardLit() && !baseRenderInst.lights[0] )
  1070. {
  1071. LightQuery query;
  1072. query.init( rootFrustum.getPosition(),
  1073. rootFrustum.getTransform().getForwardVector(),
  1074. rootFrustum.getFarDist() );
  1075. query.getLights( baseRenderInst.lights, 8 );
  1076. }
  1077. // Submit render inst...
  1078. MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();
  1079. *ri = baseRenderInst;
  1080. ri->primBuff = pb;
  1081. ri->vertBuff = vb;
  1082. ri->matInst = currentBatch->matInst;
  1083. ri->prim = renderPass->allocPrim();
  1084. ri->prim->type = GFXTriangleList;
  1085. ri->prim->minIndex = 0;
  1086. ri->prim->startIndex = 0;
  1087. ri->prim->numPrimitives = currentBatch->iCount / 3;
  1088. ri->prim->startVertex = 0;
  1089. ri->prim->numVertices = currentBatch->vCount;
  1090. ri->translucentSort = !currentBatch->matInst->getMaterial()->isTranslucent();
  1091. // Ugly hack for ProjectedShadow!
  1092. if ( customTex )
  1093. ri->miscTex = customTex;
  1094. // The decal bin will contain render instances for both decals and decalRoad's.
  1095. // Dynamic decals render last, then editor decals and roads in priority order.
  1096. // DefaultKey is sorted in descending order.
  1097. ri->defaultKey = currentBatch->dynamic ? 0xFFFFFFFF : (U32)currentBatch->priority;
  1098. ri->defaultKey2 = 1;//(U32)lastDecal->mDataBlock;
  1099. renderPass->addInst( ri );
  1100. }
  1101. #ifdef TORQUE_GATHER_METRICS
  1102. Con::setIntVariable( "$Decal::Batches", batches.size() );
  1103. Con::setIntVariable( "$Decal::Buffers", mPBs.size() + mPBPool.size() );
  1104. Con::setIntVariable( "$Decal::DecalsRendered", mDecalQueue.size() );
  1105. #endif
  1106. if( smDebugRender && gEditingMission )
  1107. {
  1108. ObjectRenderInst* ri = state->getRenderPass()->allocInst< ObjectRenderInst >();
  1109. ri->renderDelegate.bind( this, &DecalManager::_renderDecalSpheres );
  1110. ri->type = RenderPassManager::RIT_Editor;
  1111. ri->defaultKey = 0;
  1112. ri->defaultKey2 = 0;
  1113. state->getRenderPass()->addInst( ri );
  1114. }
  1115. }
  1116. void DecalManager::_renderDecalSpheres( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat )
  1117. {
  1118. if( !mData )
  1119. return;
  1120. const Vector<DecalSphere*> &grid = mData->getSphereList();
  1121. GFXDrawUtil *drawUtil = GFX->getDrawUtil();
  1122. ColorI sphereColor( 0, 255, 0, 45 );
  1123. GFXStateBlockDesc desc;
  1124. desc.setBlend( true );
  1125. desc.setZReadWrite( true, false );
  1126. desc.setCullMode( GFXCullNone );
  1127. for ( U32 i = 0; i < grid.size(); i++ )
  1128. {
  1129. DecalSphere *decalSphere = grid[i];
  1130. const SphereF &worldSphere = decalSphere->mWorldSphere;
  1131. if( state->getCullingFrustum().isCulled( worldSphere ) )
  1132. continue;
  1133. drawUtil->drawSphere( desc, worldSphere.radius, worldSphere.center, sphereColor );
  1134. }
  1135. }
  1136. bool DecalManager::_createDataFile()
  1137. {
  1138. AssertFatal( !mData, "DecalManager::tried to create duplicate data file?" );
  1139. // We need to construct a default file name
  1140. char fileName[1024];
  1141. fileName[0] = 0;
  1142. // See if we know our current mission name
  1143. char missionName[1024];
  1144. dStrcpy( missionName, Con::getVariable( "$Client::MissionFile" ), 1024 );
  1145. char *dot = dStrstr((const char*)missionName, ".mis");
  1146. if(dot)
  1147. *dot = '\0';
  1148. dSprintf( fileName, sizeof(fileName), "%s.mis.decals", missionName );
  1149. mDataFileName = StringTable->insert( fileName );
  1150. // If the file doesn't exist, create an empty file.
  1151. if( !Torque::FS::IsFile( fileName ) )
  1152. {
  1153. FileStream stream;
  1154. if( stream.open( mDataFileName, Torque::FS::File::Write ) )
  1155. {
  1156. DecalDataFile dataFile;
  1157. dataFile.write( stream );
  1158. }
  1159. }
  1160. mData = ResourceManager::get().load( mDataFileName );
  1161. return (bool)mData;
  1162. }
  1163. void DecalManager::saveDecals( const UTF8* fileName )
  1164. {
  1165. if( !mData )
  1166. return;
  1167. // Create the file.
  1168. FileStream stream;
  1169. if ( !stream.open( fileName, Torque::FS::File::Write ) )
  1170. {
  1171. Con::errorf( "DecalManager::saveDecals - Could not open '%s' for writing!", fileName );
  1172. return;
  1173. }
  1174. // Write the data.
  1175. if( !mData->write( stream ) )
  1176. {
  1177. Con::errorf( "DecalManager::saveDecals - Failed to write '%s'", fileName );
  1178. return;
  1179. }
  1180. mDirty = false;
  1181. }
  1182. bool DecalManager::loadDecals( const UTF8 *fileName )
  1183. {
  1184. if( mData )
  1185. clearData();
  1186. mData = ResourceManager::get().load( fileName );
  1187. mDirty = false;
  1188. return mData != NULL;
  1189. }
  1190. void DecalManager::clearData()
  1191. {
  1192. mClearDataSignal.trigger();
  1193. // Free all geometry buffers.
  1194. if( mData )
  1195. {
  1196. const Vector< DecalSphere* > grid = mData->getSphereList();
  1197. for( U32 i = 0; i < grid.size(); ++ i )
  1198. {
  1199. DecalSphere* sphere = grid[ i ];
  1200. for( U32 n = 0; n < sphere->mItems.size(); ++ n )
  1201. _freeBuffers( sphere->mItems[ n ] );
  1202. }
  1203. }
  1204. mData = NULL;
  1205. mDecalInstanceVec.clear();
  1206. _freePools();
  1207. }
  1208. bool DecalManager::onSceneAdd()
  1209. {
  1210. if( !Parent::onSceneAdd() )
  1211. return false;
  1212. SceneZoneSpaceManager::getZoningChangedSignal().notify( this, &DecalManager::_handleZoningChangedEvent );
  1213. return true;
  1214. }
  1215. void DecalManager::onSceneRemove()
  1216. {
  1217. SceneZoneSpaceManager::getZoningChangedSignal().remove( this, &DecalManager::_handleZoningChangedEvent );
  1218. Parent::onSceneRemove();
  1219. }
  1220. void DecalManager::_handleZoningChangedEvent( SceneZoneSpaceManager* zoneManager )
  1221. {
  1222. if( zoneManager != getSceneManager()->getZoneManager() || !getDecalDataFile() )
  1223. return;
  1224. // Clear the zoning state of all DecalSpheres in the data file.
  1225. const Vector< DecalSphere* > grid = getDecalDataFile()->getSphereList();
  1226. const U32 numSpheres = grid.size();
  1227. for( U32 i = 0; i < numSpheres; ++ i )
  1228. grid[ i ]->mZones.clear();
  1229. }
  1230. DefineEngineFunction( decalManagerSave, void, ( String decalSaveFile ), ( "" ),
  1231. "Saves the decals for the active mission in the entered filename.\n"
  1232. "@param decalSaveFile Filename to save the decals to.\n"
  1233. "@tsexample\n"
  1234. "// Set the filename to save the decals in. If no filename is set, then the\n"
  1235. "// decals will default to <activeMissionName>.mis.decals\n"
  1236. "%fileName = \"./missionDecals.mis.decals\";\n"
  1237. "// Inform the decal manager to save the decals for the active mission.\n"
  1238. "decalManagerSave( %fileName );\n"
  1239. "@endtsexample\n"
  1240. "@ingroup Decals" )
  1241. {
  1242. // If not given a file name, synthesize one.
  1243. if( decalSaveFile.isEmpty() )
  1244. {
  1245. String fileName = String::ToString( "%s.decals", Con::getVariable( "$Client::MissionFile" ) );
  1246. char fullName[ 4096 ];
  1247. Platform::makeFullPathName( fileName, fullName, sizeof( fullName ) );
  1248. decalSaveFile = String( fullName );
  1249. }
  1250. // Write the data.
  1251. gDecalManager->saveDecals( decalSaveFile );
  1252. }
  1253. DefineEngineFunction( decalManagerLoad, bool, ( const char* fileName ),,
  1254. "Clears existing decals and replaces them with decals loaded from the specified file.\n"
  1255. "@param fileName Filename to load the decals from.\n"
  1256. "@return True if the decal manager was able to load the requested file, "
  1257. "false if it could not.\n"
  1258. "@tsexample\n"
  1259. "// Set the filename to load the decals from.\n"
  1260. "%fileName = \"./missionDecals.mis.decals\";\n"
  1261. "// Inform the decal manager to load the decals from the entered filename.\n"
  1262. "decalManagerLoad( %fileName );\n"
  1263. "@endtsexample\n"
  1264. "@ingroup Decals" )
  1265. {
  1266. return gDecalManager->loadDecals( fileName );
  1267. }
  1268. DefineEngineFunction( decalManagerDirty, bool, (),,
  1269. "Returns whether the decal manager has unsaved modifications.\n"
  1270. "@return True if the decal manager has unsaved modifications, false if "
  1271. "everything has been saved.\n"
  1272. "@tsexample\n"
  1273. "// Ask the decal manager if it has unsaved modifications.\n"
  1274. "%hasUnsavedModifications = decalManagerDirty();\n"
  1275. "@endtsexample\n"
  1276. "@ingroup Decals" )
  1277. {
  1278. return gDecalManager->isDirty();
  1279. }
  1280. DefineEngineFunction( decalManagerClear, void, (),,
  1281. "Removes all decals currently loaded in the decal manager.\n"
  1282. "@tsexample\n"
  1283. "// Tell the decal manager to remove all existing decals.\n"
  1284. "decalManagerClear();\n"
  1285. "@endtsexample\n"
  1286. "@ingroup Decals" )
  1287. {
  1288. gDecalManager->clearData();
  1289. }
  1290. DefineEngineFunction( decalManagerAddDecal, S32,
  1291. ( Point3F position, Point3F normal, F32 rot, F32 scale, DecalData* decalData, bool isImmortal), ( false ),
  1292. "Adds a new decal to the decal manager.\n"
  1293. "@param position World position for the decal.\n"
  1294. "@param normal Decal normal vector (if the decal was a tire lying flat on a "
  1295. "surface, this is the vector pointing in the direction of the axle).\n"
  1296. "@param rot Angle (in radians) to rotate this decal around its normal vector.\n"
  1297. "@param scale Scale factor applied to the decal.\n"
  1298. "@param decalData DecalData datablock to use for the new decal.\n"
  1299. "@param isImmortal Whether or not this decal is immortal. If immortal, it "
  1300. "does not expire automatically and must be removed explicitly.\n"
  1301. "@return Returns the ID of the new Decal object or -1 on failure.\n"
  1302. "@tsexample\n"
  1303. "// Specify the decal position\n"
  1304. "%position = \"1.0 1.0 1.0\";\n\n"
  1305. "// Specify the up vector\n"
  1306. "%normal = \"0.0 0.0 1.0\";\n\n"
  1307. "// Add the new decal.\n"
  1308. "%decalObj = decalManagerAddDecal( %position, %normal, 0.5, 0.35, ScorchBigDecal, false );\n"
  1309. "@endtsexample\n"
  1310. "@ingroup Decals" )
  1311. {
  1312. if( !decalData )
  1313. {
  1314. Con::errorf( "decalManagerAddDecal - Invalid Decal DataBlock" );
  1315. return -1;
  1316. }
  1317. U8 flags = 0;
  1318. if( isImmortal )
  1319. flags |= PermanentDecal;
  1320. DecalInstance* inst = gDecalManager->addDecal( position, normal, rot, decalData, scale, -1, flags );
  1321. if( !inst )
  1322. {
  1323. Con::errorf( "decalManagerAddDecal - Unable to create decal instance." );
  1324. return -1;
  1325. }
  1326. // Add the decal to the instance vector.
  1327. inst->mId = gDecalManager->mDecalInstanceVec.size();
  1328. gDecalManager->mDecalInstanceVec.push_back( inst );
  1329. return inst->mId;
  1330. }
  1331. DefineEngineFunction( decalManagerRemoveDecal, bool, ( S32 decalID ),,
  1332. "Remove specified decal from the scene.\n"
  1333. "@param decalID ID of the decal to remove.\n"
  1334. "@return Returns true if successful, false if decal ID not found.\n"
  1335. "@tsexample\n"
  1336. "// Specify a decal ID to be removed\n"
  1337. "%decalID = 1;\n\n"
  1338. "// Tell the decal manager to remove the specified decal ID.\n"
  1339. "decalManagerRemoveDecal( %decalId )\n"
  1340. "@endtsexample\n"
  1341. "@ingroup Decals" )
  1342. {
  1343. DecalInstance *inst = gDecalManager->getDecal( decalID );
  1344. if( !inst )
  1345. return false;
  1346. gDecalManager->removeDecal(inst);
  1347. return true;
  1348. }
  1349. DefineEngineFunction( decalManagerEditDecal, bool, ( S32 decalID, Point3F pos, Point3F normal, F32 rotAroundNormal, F32 decalScale ),,
  1350. "Edit specified decal of the decal manager.\n"
  1351. "@param decalID ID of the decal to edit.\n"
  1352. "@param pos World position for the decal.\n"
  1353. "@param normal Decal normal vector (if the decal was a tire lying flat on a "
  1354. "surface, this is the vector pointing in the direction of the axle).\n"
  1355. "@param rotAroundNormal Angle (in radians) to rotate this decal around its normal vector.\n"
  1356. "@param decalScale Scale factor applied to the decal.\n"
  1357. "@return Returns true if successful, false if decalID not found.\n"
  1358. "" )
  1359. {
  1360. DecalInstance *decalInstance = gDecalManager->getDecal( decalID );
  1361. if( !decalInstance )
  1362. return false;
  1363. //Internally we need Point3F tangent instead of the user friendly F32 rotAroundNormal
  1364. MatrixF mat( true );
  1365. MathUtils::getMatrixFromUpVector( normal, &mat );
  1366. AngAxisF rot( normal, rotAroundNormal );
  1367. MatrixF rotmat;
  1368. rot.setMatrix( &rotmat );
  1369. mat.mul( rotmat );
  1370. Point3F tangent;
  1371. mat.getColumn( 1, &tangent );
  1372. //if everything is unchanged just do nothing and return "everything is ok"
  1373. if ( pos.equal(decalInstance->mPosition) &&
  1374. normal.equal(decalInstance->mNormal) &&
  1375. tangent.equal(decalInstance->mTangent) &&
  1376. mFabs( decalInstance->mSize - (decalInstance->mDataBlock->size * decalScale) ) < POINT_EPSILON )
  1377. return true;
  1378. decalInstance->mPosition = pos;
  1379. decalInstance->mNormal = normal;
  1380. decalInstance->mTangent = tangent;
  1381. decalInstance->mSize = decalInstance->mDataBlock->size * decalScale;
  1382. gDecalManager->clipDecal( decalInstance, NULL, NULL);
  1383. gDecalManager->notifyDecalModified( decalInstance );
  1384. return true;
  1385. }