lightFlareData.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  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/lightFlareData.h"
  24. #include "core/stream/bitStream.h"
  25. #include "console/engineAPI.h"
  26. #include "lighting/lightInfo.h"
  27. #include "lighting/lightQuery.h"
  28. #include "math/mathUtils.h"
  29. #include "math/mathIO.h"
  30. #include "scene/sceneRenderState.h"
  31. #include "gfx/gfxOcclusionQuery.h"
  32. #include "gfx/gfxDrawUtil.h"
  33. #include "gfx/gfxTextureManager.h"
  34. #include "renderInstance/renderPassManager.h"
  35. #include "T3D/gameBase/gameConnection.h"
  36. #include "T3D/gameBase/processList.h"
  37. #include "collision/collision.h"
  38. #include "lighting/lightManager.h"
  39. const U32 LightFlareData::LosMask = STATIC_COLLISION_TYPEMASK |
  40. ShapeBaseObjectType |
  41. StaticShapeObjectType |
  42. ItemObjectType;
  43. LightFlareState::~LightFlareState()
  44. {
  45. }
  46. void LightFlareState::clear()
  47. {
  48. visChangedTime = 0;
  49. visible = false;
  50. scale = 1.0f;
  51. fullBrightness = 1.0f;
  52. lightMat = MatrixF::Identity;
  53. lightInfo = NULL;
  54. worldRadius = -1.0f;
  55. occlusion = -1.0f;
  56. }
  57. Point3F LightFlareData::sBasePoints[] =
  58. {
  59. Point3F( -0.5, 0.5, 0.0 ),
  60. Point3F( -0.5, -0.5, 0.0 ),
  61. Point3F( 0.5, -0.5, 0.0 ),
  62. Point3F( 0.5, 0.5, 0.0 )
  63. };
  64. IMPLEMENT_CO_DATABLOCK_V1( LightFlareData );
  65. ConsoleDocClass( LightFlareData,
  66. "@brief Defines a light flare effect usable by scene lights.\n\n"
  67. "%LightFlareData is a datablock which defines a type of flare effect. "
  68. "This may then be referenced by other classes which support the rendering "
  69. "of a flare: Sun, ScatterSky, LightBase.\n\n"
  70. "A flare contains one or more elements defined in the element* named fields "
  71. "of %LightFlareData, with a maximum of ten elements. Each element is rendered "
  72. "as a 2D sprite in screenspace.\n\n"
  73. "@tsexample\n"
  74. "// example from Full Template, core/art/datablocks/lights.cs\n"
  75. "datablock LightFlareData( LightFlareExample0 )\n"
  76. "{\n"
  77. " overallScale = 2.0;\n"
  78. " flareEnabled = true;\n"
  79. " renderReflectPass = true;\n"
  80. " flareTexture = \"./../special/lensFlareSheet1\";\n"
  81. " occlusionRadius = 0.25;\n"
  82. " \n"
  83. " elementRect[0] = \"0 512 512 512\";\n"
  84. " elementDist[0] = 0.0;\n"
  85. " elementScale[0] = 0.5;\n"
  86. " elementTint[0] = \"1.0 1.0 1.0\";\n"
  87. " elementRotate[0] = false;\n"
  88. " elementUseLightColor[0] = false;\n"
  89. " \n"
  90. " elementRect[1] = \"512 0 512 512\";\n"
  91. " elementDist[1] = 0.0;\n"
  92. " elementScale[1] = 2.0;\n"
  93. " elementTint[1] = \"0.5 0.5 0.5\";\n"
  94. " elementRotate[1] = false;\n"
  95. " elementUseLightColor[1] = false;\n"
  96. "};\n"
  97. "@endtsexample\n"
  98. "The elementDist field defines where along the flare's beam the element appears. "
  99. "A distance of 0.0 is directly over the light source, a distance of 1.0 "
  100. "is at the screen center, and a distance of 2.0 is at the position of the "
  101. "light source mirrored across the screen center.\n"
  102. "@image html images/lightFlareData_diagram.png\n"
  103. "@ingroup Lighting"
  104. );
  105. LightFlareData::LightFlareData()
  106. : mFlareEnabled( true ),
  107. mElementCount( 0 ),
  108. mScale( 1.0f ),
  109. mOcclusionRadius( 0.0f ),
  110. mRenderReflectPass( true )
  111. {
  112. dMemset( mElementRect, 0, sizeof( RectF ) * MAX_ELEMENTS );
  113. dMemset( mElementScale, 0, sizeof( F32 ) * MAX_ELEMENTS );
  114. dMemset( mElementTint, 0, sizeof( ColorF ) * MAX_ELEMENTS );
  115. dMemset( mElementRotate, 0, sizeof( bool ) * MAX_ELEMENTS );
  116. dMemset( mElementUseLightColor, 0, sizeof( bool ) * MAX_ELEMENTS );
  117. for ( U32 i = 0; i < MAX_ELEMENTS; i++ )
  118. mElementDist[i] = -1.0f;
  119. }
  120. LightFlareData::~LightFlareData()
  121. {
  122. }
  123. void LightFlareData::initPersistFields()
  124. {
  125. addGroup( "LightFlareData" );
  126. addField( "overallScale", TypeF32, Offset( mScale, LightFlareData ),
  127. "Size scale applied to all elements of the flare." );
  128. addField( "occlusionRadius", TypeF32, Offset( mOcclusionRadius, LightFlareData ),
  129. "If positive an occlusion query is used to test flare visibility, else it uses simple raycasts." );
  130. addField( "renderReflectPass", TypeBool, Offset( mRenderReflectPass, LightFlareData ),
  131. "If false the flare does not render in reflections, else only non-zero distance elements are rendered." );
  132. endGroup( "LightFlareData" );
  133. addGroup( "FlareElements" );
  134. addField( "flareEnabled", TypeBool, Offset( mFlareEnabled, LightFlareData ),
  135. "Allows the user to disable this flare globally for any lights referencing it." );
  136. addField( "flareTexture", TypeImageFilename, Offset( mFlareTextureName, LightFlareData ),
  137. "The texture / sprite sheet for this flare." );
  138. addArray( "Elements", MAX_ELEMENTS );
  139. addField( "elementRect", TypeRectF, Offset( mElementRect, LightFlareData ), MAX_ELEMENTS,
  140. "A rectangle specified in pixels of the flareTexture image." );
  141. addField( "elementDist", TypeF32, Offset( mElementDist, LightFlareData ), MAX_ELEMENTS,
  142. "Where this element appears along the flare beam." );
  143. addField( "elementScale", TypeF32, Offset( mElementScale, LightFlareData ), MAX_ELEMENTS,
  144. "Size scale applied to this element." );
  145. addField( "elementTint", TypeColorF, Offset( mElementTint, LightFlareData ), MAX_ELEMENTS,
  146. "Used to modulate this element's color if elementUseLightColor "
  147. "is false.\n"
  148. "@see elementUseLightColor" );
  149. addField( "elementRotate", TypeBool, Offset( mElementRotate, LightFlareData ), MAX_ELEMENTS,
  150. "Defines if this element orients to point along the flare beam "
  151. "or if it is always upright." );
  152. addField( "elementUseLightColor", TypeBool, Offset( mElementUseLightColor, LightFlareData ), MAX_ELEMENTS,
  153. "If true this element's color is modulated by the light color. "
  154. "If false, elementTint will be used.\n"
  155. "@see elementTint" );
  156. endArray( "FlareElements" );
  157. endGroup( "Flares" );
  158. Parent::initPersistFields();
  159. }
  160. void LightFlareData::inspectPostApply()
  161. {
  162. Parent::inspectPostApply();
  163. // Hack to allow changing properties in game.
  164. // Do the same work as preload.
  165. String str;
  166. _preload( false, str );
  167. }
  168. bool LightFlareData::preload( bool server, String &errorStr )
  169. {
  170. if ( !Parent::preload( server, errorStr ) )
  171. return false;
  172. return _preload( server, errorStr );
  173. }
  174. void LightFlareData::packData( BitStream *stream )
  175. {
  176. Parent::packData( stream );
  177. stream->writeFlag( mFlareEnabled );
  178. stream->write( mFlareTextureName );
  179. stream->write( mScale );
  180. stream->write( mOcclusionRadius );
  181. stream->writeFlag( mRenderReflectPass );
  182. stream->write( mElementCount );
  183. for ( U32 i = 0; i < mElementCount; i++ )
  184. {
  185. mathWrite( *stream, mElementRect[i] );
  186. stream->write( mElementDist[i] );
  187. stream->write( mElementScale[i] );
  188. stream->write( mElementTint[i] );
  189. stream->writeFlag( mElementRotate[i] );
  190. stream->writeFlag( mElementUseLightColor[i] );
  191. }
  192. }
  193. void LightFlareData::unpackData( BitStream *stream )
  194. {
  195. Parent::unpackData( stream );
  196. mFlareEnabled = stream->readFlag();
  197. stream->read( &mFlareTextureName );
  198. stream->read( &mScale );
  199. stream->read( &mOcclusionRadius );
  200. mRenderReflectPass = stream->readFlag();
  201. stream->read( &mElementCount );
  202. for ( U32 i = 0; i < mElementCount; i++ )
  203. {
  204. mathRead( *stream, &mElementRect[i] );
  205. stream->read( &mElementDist[i] );
  206. stream->read( &mElementScale[i] );
  207. stream->read( &mElementTint[i] );
  208. mElementRotate[i] = stream->readFlag();
  209. mElementUseLightColor[i] = stream->readFlag();
  210. }
  211. }
  212. bool LightFlareData::_testVisibility(const SceneRenderState *state, LightFlareState *flareState, U32 *outVisDelta, F32 *outOcclusionFade, Point3F *outLightPosSS)
  213. {
  214. // Reflections use the results from the last forward
  215. // render so we don't need multiple queries.
  216. if ( state->isReflectPass() )
  217. {
  218. *outOcclusionFade = flareState->occlusion;
  219. *outVisDelta = Sim::getCurrentTime() - flareState->visChangedTime;
  220. return flareState->visible;
  221. }
  222. // Initialize it to something first.
  223. *outOcclusionFade = 0;
  224. // First check to see if the flare point
  225. // is on scren at all... if not then return
  226. // the last result.
  227. const Point3F &lightPos = flareState->lightMat.getPosition();
  228. const RectI &viewport = GFX->getViewport();
  229. MatrixF projMatrix;
  230. state->getCameraFrustum().getProjectionMatrix(&projMatrix);
  231. if( state->isReflectPass() )
  232. projMatrix = state->getSceneManager()->getNonClipProjection();
  233. bool onScreen = MathUtils::mProjectWorldToScreen( lightPos, outLightPosSS, viewport, GFX->getWorldMatrix(), projMatrix );
  234. // It is onscreen, so raycast as a simple occlusion test.
  235. const LightInfo *lightInfo = flareState->lightInfo;
  236. const bool isVectorLight = lightInfo->getType() == LightInfo::Vector;
  237. const bool useOcclusionQuery = isVectorLight ? flareState->worldRadius > 0.0f : mOcclusionRadius > 0.0f;
  238. bool needsRaycast = true;
  239. // NOTE: if hardware does not support HOQ it will return NULL
  240. // and we will retry every time but there is not currently a good place
  241. // for one-shot initialization of LightFlareState
  242. if ( useOcclusionQuery )
  243. {
  244. // Always treat light as onscreen if using HOQ
  245. // it will be faded out if offscreen anyway.
  246. onScreen = true;
  247. needsRaycast = false;
  248. // Test the hardware queries for rendered pixels.
  249. U32 pixels = 0, fullPixels = 0;
  250. GFXOcclusionQuery::OcclusionQueryStatus status;
  251. flareState->occlusionQuery.getLastStatus( false, &status, &pixels );
  252. flareState->fullPixelQuery.getLastStatus( false, NULL, &fullPixels );
  253. if ( status == GFXOcclusionQuery::NotOccluded && fullPixels != 0 )
  254. *outOcclusionFade = mClampF( (F32)pixels / (F32)fullPixels, 0.0f, 1.0f );
  255. if( !flareState->occlusionQuery.isWaiting() )
  256. {
  257. // Setup the new queries.
  258. RenderPassManager *rpm = state->getRenderPass();
  259. OccluderRenderInst *ri = rpm->allocInst<OccluderRenderInst>();
  260. ri->type = RenderPassManager::RIT_Occluder;
  261. ri->query = flareState->occlusionQuery.getQuery();
  262. ri->query2 = flareState->fullPixelQuery.getQuery();
  263. ri->isSphere = true;
  264. ri->position = lightPos;
  265. if ( isVectorLight && flareState->worldRadius > 0.0f )
  266. ri->scale.set( flareState->worldRadius );
  267. else
  268. ri->scale.set( mOcclusionRadius );
  269. ri->orientation = rpm->allocUniqueXform( lightInfo->getTransform() );
  270. // Submit the queries.
  271. state->getRenderPass()->addInst( ri );
  272. }
  273. }
  274. const Point3F &camPos = state->getCameraPosition();
  275. if ( needsRaycast )
  276. {
  277. // Use a raycast to determine occlusion.
  278. GameConnection *conn = GameConnection::getConnectionToServer();
  279. if ( !conn )
  280. return false;
  281. const bool fps = conn->isFirstPerson();
  282. GameBase *control = conn->getControlObject();
  283. if ( control && fps )
  284. control->disableCollision();
  285. RayInfo rayInfo;
  286. if ( !gClientContainer.castRay( camPos, lightPos, LosMask, &rayInfo ) )
  287. *outOcclusionFade = 1.0f;
  288. if ( control && fps )
  289. control->enableCollision();
  290. }
  291. // The raycast and hardware occlusion query only calculate if
  292. // the flare is on screen... if does not account for being
  293. // partially offscreen.
  294. //
  295. // The code here clips a box against the viewport to
  296. // get an approximate percentage of onscreen area.
  297. //
  298. F32 worldRadius = flareState->worldRadius > 0 ? flareState->worldRadius : mOcclusionRadius;
  299. if ( worldRadius > 0.0f )
  300. {
  301. F32 dist = ( camPos - lightPos ).len();
  302. F32 pixelRadius = state->projectRadius(dist, worldRadius);
  303. RectI visRect( outLightPosSS->x - pixelRadius, outLightPosSS->y - pixelRadius,
  304. pixelRadius * 2.0f, pixelRadius * 2.0f );
  305. F32 fullArea = visRect.area();
  306. if ( visRect.intersect( viewport ) )
  307. {
  308. F32 visArea = visRect.area();
  309. *outOcclusionFade *= visArea / fullArea;
  310. onScreen = true;
  311. }
  312. else
  313. *outOcclusionFade = 0.0f;
  314. }
  315. const bool lightVisible = onScreen && *outOcclusionFade > 0.0f;
  316. // To perform a fade in/out when we gain or lose visibility
  317. // we must update/store the visibility state and time.
  318. const U32 currentTime = Sim::getCurrentTime();
  319. if ( lightVisible != flareState->visible )
  320. {
  321. flareState->visible = lightVisible;
  322. flareState->visChangedTime = currentTime;
  323. }
  324. // Return the visibility delta for time fading.
  325. *outVisDelta = currentTime - flareState->visChangedTime;
  326. // Store the final occlusion fade so that it can
  327. // be used in reflection rendering later.
  328. flareState->occlusion = *outOcclusionFade;
  329. return lightVisible;
  330. }
  331. void LightFlareData::prepRender( SceneRenderState *state, LightFlareState *flareState )
  332. {
  333. PROFILE_SCOPE( LightFlareData_prepRender );
  334. const LightInfo *lightInfo = flareState->lightInfo;
  335. if ( mIsZero( flareState->fullBrightness ) ||
  336. mIsZero( lightInfo->getBrightness() ) )
  337. return;
  338. // Figure out the element count to render.
  339. U32 elementCount = mElementCount;
  340. const bool isReflectPass = state->isReflectPass();
  341. if ( isReflectPass )
  342. {
  343. // Then we don't render anything this pass.
  344. if ( !mRenderReflectPass )
  345. return;
  346. // Find the zero distance elements which make
  347. // up the corona of the light flare.
  348. elementCount = 0.0f;
  349. for ( U32 i=0; i < mElementCount; i++ )
  350. if ( mIsZero( mElementDist[i] ) )
  351. elementCount++;
  352. }
  353. // Better have something to render.
  354. if ( elementCount == 0 )
  355. return;
  356. U32 visDelta = U32_MAX;
  357. F32 occlusionFade = 1.0f;
  358. Point3F lightPosSS;
  359. bool lightVisible = _testVisibility( state, flareState, &visDelta, &occlusionFade, &lightPosSS );
  360. // We can only skip rendering if the light is not
  361. // visible, and it has elapsed the fade out time.
  362. if ( mIsZero( occlusionFade ) ||
  363. !lightVisible && visDelta > FadeOutTime )
  364. return;
  365. const RectI &viewport = GFX->getViewport();
  366. Point3F oneOverViewportExtent( 1.0f / (F32)viewport.extent.x, 1.0f / (F32)viewport.extent.y, 0.0f );
  367. // Really convert it to screen space.
  368. lightPosSS.x -= viewport.point.x;
  369. lightPosSS.y -= viewport.point.y;
  370. lightPosSS *= oneOverViewportExtent;
  371. lightPosSS = ( lightPosSS * 2.0f ) - Point3F::One;
  372. lightPosSS.y = -lightPosSS.y;
  373. lightPosSS.z = 0.0f;
  374. // Take any projection offset into account so that the point where the flare's
  375. // elements converge is at the 'eye' point rather than the center of the viewport.
  376. const Point2F& projOffset = state->getCameraFrustum().getProjectionOffset();
  377. Point3F flareVec( -lightPosSS + Point3F(projOffset.x, projOffset.y, 0.0f) );
  378. const F32 flareLength = flareVec.len();
  379. if ( flareLength > 0.0f )
  380. flareVec *= 1.0f / flareLength;
  381. // Setup the flare quad points.
  382. Point3F rotatedBasePoints[4];
  383. dMemcpy(rotatedBasePoints, sBasePoints, sizeof( sBasePoints ));
  384. // Rotate the flare quad.
  385. F32 rot = mAcos( -1.0f * flareVec.x );
  386. rot *= flareVec.y > 0.0f ? -1.0f : 1.0f;
  387. MathUtils::vectorRotateZAxis( rot, rotatedBasePoints, 4 );
  388. // Here we calculate a the light source's influence on
  389. // the effect's size and brightness.
  390. // Scale based on the current light brightness compared to its normal output.
  391. F32 lightSourceBrightnessScale = lightInfo->getBrightness() / flareState->fullBrightness;
  392. const Point3F &camPos = state->getCameraPosition();
  393. const Point3F &lightPos = flareState->lightMat.getPosition();
  394. const bool isVectorLight = lightInfo->getType() == LightInfo::Vector;
  395. // Scale based on world space distance from camera to light source.
  396. F32 distToCamera = ( camPos - lightPos ).len();
  397. F32 lightSourceWSDistanceScale = isVectorLight && distToCamera > 0.0f ? 1.0f : getMin( 10.0f / distToCamera, 10.0f );
  398. // Scale based on screen space distance from screen position of light source to the screen center.
  399. F32 lightSourceSSDistanceScale = getMax( ( 1.5f - flareLength ) / 1.5f, 0.0f );
  400. // Scale based on recent visibility changes, fading in or out.
  401. F32 fadeInOutScale = 1.0f;
  402. if ( lightVisible &&
  403. visDelta < FadeInTime &&
  404. flareState->occlusion > 0.0f )
  405. fadeInOutScale = (F32)visDelta / (F32)FadeInTime;
  406. else if ( !lightVisible &&
  407. visDelta < FadeOutTime )
  408. fadeInOutScale = 1.0f - (F32)visDelta / (F32)FadeOutTime;
  409. // This combined scale influences the size of all elements this effect renders.
  410. // Note we also add in a scale that is user specified in the Light.
  411. F32 lightSourceIntensityScale = lightSourceBrightnessScale *
  412. lightSourceWSDistanceScale *
  413. lightSourceSSDistanceScale *
  414. fadeInOutScale *
  415. flareState->scale *
  416. occlusionFade;
  417. if ( mIsZero( lightSourceIntensityScale ) )
  418. return;
  419. // The baseColor which modulates the color of all elements.
  420. //
  421. // These are the factors which affect the "alpha" of the flare effect.
  422. // Modulate more in as appropriate.
  423. ColorF baseColor = ColorF::WHITE * lightSourceBrightnessScale * occlusionFade;
  424. // Setup the vertex buffer for the maximum flare elements.
  425. const U32 vertCount = 4 * mElementCount;
  426. if ( flareState->vertBuffer.isNull() ||
  427. flareState->vertBuffer->mNumVerts != vertCount )
  428. flareState->vertBuffer.set( GFX, vertCount, GFXBufferTypeDynamic );
  429. GFXVertexPCT *vert = flareState->vertBuffer.lock();
  430. const Point2F oneOverTexSize( 1.0f / (F32)mFlareTexture.getWidth(), 1.0f / (F32)mFlareTexture.getHeight() );
  431. for ( U32 i = 0; i < mElementCount; i++ )
  432. {
  433. // Skip non-zero elements for reflections.
  434. if ( isReflectPass && mElementDist[i] > 0.0f )
  435. continue;
  436. Point3F *basePos = mElementRotate[i] ? rotatedBasePoints : sBasePoints;
  437. ColorF color( baseColor * mElementTint[i] );
  438. if ( mElementUseLightColor[i] )
  439. color *= lightInfo->getColor();
  440. color.clamp();
  441. Point3F pos( lightPosSS + flareVec * mElementDist[i] * flareLength );
  442. const RectF &rect = mElementRect[i];
  443. Point3F size( rect.extent.x, rect.extent.y, 1.0f );
  444. size *= mElementScale[i] * mScale * lightSourceIntensityScale;
  445. AssertFatal( size.x >= 0.0f, "LightFlareData::prepRender - Got a negative element size?" );
  446. if ( size.x < 100.0f )
  447. {
  448. F32 alphaScale = mPow( size.x / 100.0f, 2 );
  449. color *= alphaScale;
  450. }
  451. Point2F texCoordMin, texCoordMax;
  452. texCoordMin = rect.point * oneOverTexSize;
  453. texCoordMax = ( rect.point + rect.extent ) * oneOverTexSize;
  454. size.x = getMax( size.x, 1.0f );
  455. size.y = getMax( size.y, 1.0f );
  456. size *= oneOverViewportExtent;
  457. vert->color = color;
  458. vert->point = ( basePos[0] * size ) + pos;
  459. vert->texCoord.set( texCoordMin.x, texCoordMax.y );
  460. vert++;
  461. vert->color = color;
  462. vert->point = ( basePos[1] * size ) + pos;
  463. vert->texCoord.set( texCoordMax.x, texCoordMax.y );
  464. vert++;
  465. vert->color = color;
  466. vert->point = ( basePos[2] * size ) + pos;
  467. vert->texCoord.set( texCoordMax.x, texCoordMin.y );
  468. vert++;
  469. vert->color = color;
  470. vert->point = ( basePos[3] * size ) + pos;
  471. vert->texCoord.set( texCoordMin.x, texCoordMin.y );
  472. vert++;
  473. }
  474. flareState->vertBuffer.unlock();
  475. RenderPassManager *rpm = state->getRenderPass();
  476. // Create and submit the render instance.
  477. ParticleRenderInst *ri = rpm->allocInst<ParticleRenderInst>();
  478. ri->type = RenderPassManager::RIT_Particle;
  479. ri->vertBuff = &flareState->vertBuffer;
  480. ri->primBuff = &mFlarePrimBuffer;
  481. ri->translucentSort = true;
  482. ri->sortDistSq = ( lightPos - camPos ).lenSquared();
  483. ri->modelViewProj = &MatrixF::Identity;
  484. ri->bbModelViewProj = &MatrixF::Identity;
  485. ri->count = elementCount;
  486. ri->blendStyle = ParticleRenderInst::BlendGreyscale;
  487. ri->diffuseTex = mFlareTexture;
  488. ri->softnessDistance = 1.0f;
  489. ri->defaultKey = ri->diffuseTex ? (uintptr_t)ri->diffuseTex : (uintptr_t)ri->vertBuff; // Sort by texture too.
  490. // NOTE: Offscreen partical code is currently disabled.
  491. ri->systemState = PSS_AwaitingHighResDraw;
  492. rpm->addInst( ri );
  493. }
  494. bool LightFlareData::_preload( bool server, String &errorStr )
  495. {
  496. mElementCount = 0;
  497. for ( U32 i = 0; i < MAX_ELEMENTS; i++ )
  498. {
  499. if ( mElementDist[i] == -1 )
  500. break;
  501. mElementCount = i + 1;
  502. }
  503. if ( mElementCount > 0 )
  504. _makePrimBuffer( &mFlarePrimBuffer, mElementCount );
  505. if ( !server )
  506. {
  507. if ( mFlareTextureName.isNotEmpty() )
  508. mFlareTexture.set( mFlareTextureName, &GFXDefaultStaticDiffuseProfile, "FlareTexture" );
  509. }
  510. return true;
  511. }
  512. void LightFlareData::_makePrimBuffer( GFXPrimitiveBufferHandle *pb, U32 count )
  513. {
  514. // create index buffer based on that size
  515. U32 indexListSize = count * 6; // 6 indices per particle
  516. U16 *indices = new U16[ indexListSize ];
  517. for ( U32 i = 0; i < count; i++ )
  518. {
  519. // this index ordering should be optimal (hopefully) for the vertex cache
  520. U16 *idx = &indices[i*6];
  521. volatile U32 offset = i * 4; // set to volatile to fix VC6 Release mode compiler bug
  522. idx[0] = 0 + offset;
  523. idx[1] = 1 + offset;
  524. idx[2] = 3 + offset;
  525. idx[3] = 1 + offset;
  526. idx[4] = 3 + offset;
  527. idx[5] = 2 + offset;
  528. }
  529. U16 *ibIndices;
  530. GFXBufferType bufferType = GFXBufferTypeStatic;
  531. #ifdef TORQUE_OS_XENON
  532. // Because of the way the volatile buffers work on Xenon this is the only
  533. // way to do this.
  534. bufferType = GFXBufferTypeVolatile;
  535. #endif
  536. pb->set( GFX, indexListSize, 0, bufferType );
  537. pb->lock( &ibIndices );
  538. dMemcpy( ibIndices, indices, indexListSize * sizeof(U16) );
  539. pb->unlock();
  540. delete [] indices;
  541. }
  542. DefineEngineMethod( LightFlareData, apply, void, (),,
  543. "Intended as a helper to developers and editor scripts.\n"
  544. "Force trigger an inspectPostApply"
  545. )
  546. {
  547. object->inspectPostApply();
  548. }