lightFlareData.cpp 24 KB

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