lightFlareData.cpp 24 KB

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