lightFlareData.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  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. addGroup( "LightFlareData" );
  128. addField( "overallScale", TypeF32, Offset( mScale, LightFlareData ),
  129. "Size scale applied to all elements of the flare." );
  130. addField( "occlusionRadius", TypeF32, Offset( mOcclusionRadius, LightFlareData ),
  131. "If positive an occlusion query is used to test flare visibility, else it uses simple raycasts." );
  132. addField( "renderReflectPass", TypeBool, Offset( mRenderReflectPass, LightFlareData ),
  133. "If false the flare does not render in reflections, else only non-zero distance elements are rendered." );
  134. endGroup( "LightFlareData" );
  135. addGroup( "FlareElements" );
  136. addField( "flareEnabled", TypeBool, Offset( mFlareEnabled, LightFlareData ),
  137. "Allows the user to disable this flare globally for any lights referencing it." );
  138. INITPERSISTFIELD_IMAGEASSET(FlareTexture, LightFlareData, "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. PACKDATA_ASSET(FlareTexture);
  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. UNPACKDATA_ASSET(FlareTexture);
  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 = RectI(Point2I(0, 0), GFX->getViewport().extent);
  230. MatrixF camProjMatrix = state->getSceneManager()->getNonClipProjection();
  231. bool onScreen = MathUtils::mProjectWorldToScreen( lightPos, outLightPosSS, viewport, GFX->getWorldMatrix(), camProjMatrix );
  232. // It is onscreen, so raycast as a simple occlusion test.
  233. const LightInfo *lightInfo = flareState->lightInfo;
  234. const bool isVectorLight = lightInfo->getType() == LightInfo::Vector;
  235. const bool useOcclusionQuery = isVectorLight ? flareState->worldRadius > 0.0f : mOcclusionRadius > 0.0f;
  236. bool needsRaycast = true;
  237. // NOTE: if hardware does not support HOQ it will return NULL
  238. // and we will retry every time but there is not currently a good place
  239. // for one-shot initialization of LightFlareState
  240. if ( useOcclusionQuery )
  241. {
  242. // Always treat light as onscreen if using HOQ
  243. // it will be faded out if offscreen anyway.
  244. onScreen = true;
  245. needsRaycast = false;
  246. // Test the hardware queries for rendered pixels.
  247. U32 pixels = 0, fullPixels = 0;
  248. GFXOcclusionQuery::OcclusionQueryStatus status;
  249. flareState->occlusionQuery.getLastStatus( false, &status, &pixels );
  250. flareState->fullPixelQuery.getLastStatus( false, NULL, &fullPixels );
  251. if ( status == GFXOcclusionQuery::NotOccluded && fullPixels != 0 )
  252. *outOcclusionFade = mClampF( (F32)pixels / (F32)fullPixels, 0.0f, 1.0f );
  253. if( !flareState->occlusionQuery.isWaiting() )
  254. {
  255. // Setup the new queries.
  256. RenderPassManager *rpm = state->getRenderPass();
  257. OccluderRenderInst *ri = rpm->allocInst<OccluderRenderInst>();
  258. ri->type = RenderPassManager::RIT_Occluder;
  259. ri->query = flareState->occlusionQuery.getQuery();
  260. ri->query2 = flareState->fullPixelQuery.getQuery();
  261. ri->isSphere = true;
  262. ri->position = lightPos;
  263. if ( isVectorLight && flareState->worldRadius > 0.0f )
  264. ri->scale.set( flareState->worldRadius );
  265. else
  266. ri->scale.set( mOcclusionRadius );
  267. ri->orientation = rpm->allocUniqueXform( lightInfo->getTransform() );
  268. // Submit the queries.
  269. state->getRenderPass()->addInst( ri );
  270. }
  271. }
  272. const Point3F &camPos = state->getCameraPosition();
  273. if ( needsRaycast )
  274. {
  275. // Use a raycast to determine occlusion.
  276. GameConnection *conn = GameConnection::getConnectionToServer();
  277. if ( !conn )
  278. return false;
  279. const bool fps = conn->isFirstPerson();
  280. GameBase *control = conn->getControlObject();
  281. if ( control && fps )
  282. control->disableCollision();
  283. RayInfo rayInfo;
  284. if ( !gClientContainer.castRay( camPos, lightPos, LosMask, &rayInfo ) )
  285. *outOcclusionFade = 1.0f;
  286. if ( control && fps )
  287. control->enableCollision();
  288. }
  289. // The raycast and hardware occlusion query only calculate if
  290. // the flare is on screen... if does not account for being
  291. // partially offscreen.
  292. //
  293. // The code here clips a box against the viewport to
  294. // get an approximate percentage of onscreen area.
  295. //
  296. F32 worldRadius = flareState->worldRadius > 0 ? flareState->worldRadius : mOcclusionRadius;
  297. if ( worldRadius > 0.0f )
  298. {
  299. F32 dist = ( camPos - lightPos ).len();
  300. F32 pixelRadius = state->projectRadius(dist, worldRadius);
  301. RectI visRect( outLightPosSS->x - pixelRadius, outLightPosSS->y - pixelRadius,
  302. pixelRadius * 2.0f, pixelRadius * 2.0f );
  303. F32 fullArea = visRect.area();
  304. if ( visRect.intersect( viewport ) )
  305. {
  306. F32 visArea = visRect.area();
  307. *outOcclusionFade *= visArea / fullArea;
  308. onScreen = true;
  309. }
  310. else
  311. *outOcclusionFade = 0.0f;
  312. }
  313. const bool lightVisible = onScreen && *outOcclusionFade > 0.0f;
  314. // To perform a fade in/out when we gain or lose visibility
  315. // we must update/store the visibility state and time.
  316. const U32 currentTime = Sim::getCurrentTime();
  317. if ( lightVisible != flareState->visible )
  318. {
  319. flareState->visible = lightVisible;
  320. flareState->visChangedTime = currentTime;
  321. }
  322. // Return the visibility delta for time fading.
  323. *outVisDelta = currentTime - flareState->visChangedTime;
  324. // Store the final occlusion fade so that it can
  325. // be used in reflection rendering later.
  326. flareState->occlusion = *outOcclusionFade;
  327. return lightVisible;
  328. }
  329. void LightFlareData::prepRender(SceneRenderState *state, LightFlareState *flareState)
  330. {
  331. PROFILE_SCOPE(LightFlareData_prepRender);
  332. const LightInfo *lightInfo = flareState->lightInfo;
  333. if (mIsZero(flareState->fullBrightness) ||
  334. mIsZero(lightInfo->getBrightness()))
  335. return;
  336. // Figure out the element count to render.
  337. U32 elementCount = mElementCount;
  338. const bool isReflectPass = state->isReflectPass();
  339. if (isReflectPass)
  340. {
  341. // Then we don't render anything this pass.
  342. if (!mRenderReflectPass)
  343. return;
  344. // Find the zero distance elements which make
  345. // up the corona of the light flare.
  346. elementCount = 0.0f;
  347. for (U32 i = 0; i < mElementCount; i++)
  348. if (mIsZero(mElementDist[i]))
  349. elementCount++;
  350. }
  351. // Better have something to render.
  352. if (elementCount == 0)
  353. return;
  354. U32 visDelta = U32_MAX;
  355. F32 occlusionFade = 1.0f;
  356. Point3F lightPosSS;
  357. bool lightVisible = _testVisibility(state, flareState, &visDelta, &occlusionFade, &lightPosSS);
  358. //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);
  359. // We can only skip rendering if the light is not
  360. // visible, and it has elapsed the fade out time.
  361. if (mIsZero(occlusionFade) ||
  362. (!lightVisible && visDelta > FadeOutTime))
  363. return;
  364. const RectI &viewport = GFX->getViewport();
  365. Point3F oneOverViewportExtent(1.0f / (F32)viewport.extent.x, 1.0f / (F32)viewport.extent.y, 0.0f);
  366. lightPosSS *= oneOverViewportExtent;
  367. lightPosSS = (lightPosSS * 2.0f) - Point3F::One;
  368. lightPosSS.y = -lightPosSS.y;
  369. lightPosSS.z = 0.0f;
  370. // Determine the center of the current projection so we can converge there
  371. Point3F centerProj(0);
  372. {
  373. MatrixF camProjMatrix = state->getSceneManager()->getNonClipProjection();
  374. Point3F outCenterPos;
  375. RectI centerViewport = RectI(Point2I(0, 0), viewport.extent);
  376. MathUtils::mProjectWorldToScreen(Point3F(0,state->getSceneManager()->getNearClip(),0), &outCenterPos, centerViewport, MatrixF::Identity, camProjMatrix);
  377. centerProj = outCenterPos;
  378. centerProj *= oneOverViewportExtent;
  379. centerProj = (centerProj * 2.0f) - Point3F::One;
  380. centerProj.y = -centerProj.y;
  381. centerProj.z = 0.0f;
  382. }
  383. // Take any projection offset into account so that the point where the flare's
  384. // elements converge is at the 'eye' point rather than the center of the viewport.
  385. Point3F flareVec( centerProj - lightPosSS );
  386. const F32 flareLength = flareVec.len();
  387. if ( flareLength > 0.0f )
  388. flareVec *= 1.0f / flareLength;
  389. // Setup the flare quad points.
  390. Point3F rotatedBasePoints[4];
  391. dMemcpy(rotatedBasePoints, sBasePoints, sizeof( sBasePoints ));
  392. // Rotate the flare quad.
  393. F32 rot = mAcos( -1.0f * flareVec.x );
  394. rot *= flareVec.y > 0.0f ? -1.0f : 1.0f;
  395. MathUtils::vectorRotateZAxis( rot, rotatedBasePoints, 4 );
  396. // Here we calculate a the light source's influence on
  397. // the effect's size and brightness.
  398. // Scale based on the current light brightness compared to its normal output.
  399. F32 lightSourceBrightnessScale = lightInfo->getBrightness() / flareState->fullBrightness;
  400. const Point3F &camPos = state->getCameraPosition();
  401. const Point3F &lightPos = flareState->lightMat.getPosition();
  402. const bool isVectorLight = lightInfo->getType() == LightInfo::Vector;
  403. // Scale based on world space distance from camera to light source.
  404. F32 distToCamera = ( camPos - lightPos ).len();
  405. F32 lightSourceWSDistanceScale = isVectorLight && distToCamera > 0.0f ? 1.0f : getMin( 10.0f / distToCamera, 10.0f );
  406. // Scale based on screen space distance from screen position of light source to the screen center.
  407. F32 lightSourceSSDistanceScale = getMax( ( 1.5f - flareLength ) / 1.5f, 0.0f );
  408. // Scale based on recent visibility changes, fading in or out.
  409. F32 fadeInOutScale = 1.0f;
  410. if ( lightVisible &&
  411. visDelta < FadeInTime &&
  412. flareState->occlusion > 0.0f )
  413. fadeInOutScale = (F32)visDelta / (F32)FadeInTime;
  414. else if ( !lightVisible &&
  415. visDelta < FadeOutTime )
  416. fadeInOutScale = 1.0f - (F32)visDelta / (F32)FadeOutTime;
  417. // This combined scale influences the size of all elements this effect renders.
  418. // Note we also add in a scale that is user specified in the Light.
  419. F32 lightSourceIntensityScale = lightSourceBrightnessScale *
  420. lightSourceWSDistanceScale *
  421. lightSourceSSDistanceScale *
  422. fadeInOutScale *
  423. flareState->scale *
  424. occlusionFade;
  425. if ( mIsZero( lightSourceIntensityScale ) )
  426. return;
  427. // The baseColor which modulates the color of all elements.
  428. //
  429. // These are the factors which affect the "alpha" of the flare effect.
  430. // Modulate more in as appropriate.
  431. LinearColorF baseColor = LinearColorF::WHITE * lightSourceBrightnessScale * occlusionFade;
  432. // Setup the vertex buffer for the maximum flare elements.
  433. const U32 vertCount = 4 * mElementCount;
  434. if ( flareState->vertBuffer.isNull() ||
  435. flareState->vertBuffer->mNumVerts != vertCount )
  436. flareState->vertBuffer.set( GFX, vertCount, GFXBufferTypeDynamic );
  437. GFXVertexPCT *vert = flareState->vertBuffer.lock();
  438. const Point2F oneOverTexSize( 1.0f / (F32)mFlareTexture.getWidth(), 1.0f / (F32)mFlareTexture.getHeight() );
  439. for ( U32 i = 0; i < mElementCount; i++ )
  440. {
  441. // Skip non-zero elements for reflections.
  442. if ( isReflectPass && mElementDist[i] > 0.0f )
  443. continue;
  444. Point3F *basePos = mElementRotate[i] ? rotatedBasePoints : sBasePoints;
  445. LinearColorF color( baseColor * mElementTint[i] );
  446. if ( mElementUseLightColor[i] )
  447. color *= lightInfo->getColor();
  448. color.clamp();
  449. Point3F pos( lightPosSS + flareVec * mElementDist[i] * flareLength );
  450. const RectF &rect = mElementRect[i];
  451. Point3F size( rect.extent.x, rect.extent.y, 1.0f );
  452. size *= mElementScale[i] * mScale * lightSourceIntensityScale;
  453. AssertFatal( size.x >= 0.0f, "LightFlareData::prepRender - Got a negative element size?" );
  454. if ( size.x < 100.0f )
  455. {
  456. F32 alphaScale = mPow( size.x / 100.0f, 2 );
  457. color *= alphaScale;
  458. }
  459. Point2F texCoordMin, texCoordMax;
  460. texCoordMin = rect.point * oneOverTexSize;
  461. texCoordMax = ( rect.point + rect.extent ) * oneOverTexSize;
  462. size.x = getMax( size.x, 1.0f );
  463. size.y = getMax( size.y, 1.0f );
  464. size *= oneOverViewportExtent;
  465. const ColorI colori = color.toColorI();
  466. vert->color = colori;
  467. vert->point = ( basePos[0] * size ) + pos;
  468. vert->texCoord.set( texCoordMin.x, texCoordMax.y );
  469. vert++;
  470. vert->color = colori;
  471. vert->point = ( basePos[1] * size ) + pos;
  472. vert->texCoord.set( texCoordMax.x, texCoordMax.y );
  473. vert++;
  474. vert->color = colori;
  475. vert->point = ( basePos[2] * size ) + pos;
  476. vert->texCoord.set( texCoordMax.x, texCoordMin.y );
  477. vert++;
  478. vert->color = colori;
  479. vert->point = ( basePos[3] * size ) + pos;
  480. vert->texCoord.set( texCoordMin.x, texCoordMin.y );
  481. vert++;
  482. }
  483. flareState->vertBuffer.unlock();
  484. RenderPassManager *rpm = state->getRenderPass();
  485. // Create and submit the render instance.
  486. ParticleRenderInst *ri = rpm->allocInst<ParticleRenderInst>();
  487. ri->type = RenderPassManager::RIT_Particle;
  488. ri->vertBuff = &flareState->vertBuffer;
  489. ri->primBuff = &mFlarePrimBuffer;
  490. ri->translucentSort = true;
  491. ri->sortDistSq = ( lightPos - camPos ).lenSquared();
  492. ri->modelViewProj = &MatrixF::Identity;
  493. ri->bbModelViewProj = &MatrixF::Identity;
  494. ri->count = elementCount;
  495. ri->blendStyle = ParticleRenderInst::BlendGreyscale;
  496. ri->diffuseTex = mFlareTexture;
  497. ri->softnessDistance = 1.0f;
  498. ri->defaultKey = ri->diffuseTex ? (uintptr_t)ri->diffuseTex : (uintptr_t)ri->vertBuff; // Sort by texture too.
  499. // NOTE: Offscreen partical code is currently disabled.
  500. ri->systemState = PSS_AwaitingHighResDraw;
  501. rpm->addInst( ri );
  502. }
  503. bool LightFlareData::_preload( bool server, String &errorStr )
  504. {
  505. mElementCount = 0;
  506. for ( U32 i = 0; i < MAX_ELEMENTS; i++ )
  507. {
  508. if ( mElementDist[i] == -1 )
  509. break;
  510. mElementCount = i + 1;
  511. }
  512. if ( mElementCount > 0 )
  513. _makePrimBuffer( &mFlarePrimBuffer, mElementCount );
  514. return true;
  515. }
  516. void LightFlareData::_makePrimBuffer( GFXPrimitiveBufferHandle *pb, U32 count )
  517. {
  518. // create index buffer based on that size
  519. U32 indexListSize = count * 6; // 6 indices per particle
  520. U16 *indices = new U16[ indexListSize ];
  521. for ( U32 i = 0; i < count; i++ )
  522. {
  523. // this index ordering should be optimal (hopefully) for the vertex cache
  524. U16 *idx = &indices[i*6];
  525. volatile U32 offset = i * 4; // set to volatile to fix VC6 Release mode compiler bug
  526. idx[0] = 0 + offset;
  527. idx[1] = 1 + offset;
  528. idx[2] = 3 + offset;
  529. idx[3] = 1 + offset;
  530. idx[4] = 3 + offset;
  531. idx[5] = 2 + offset;
  532. }
  533. U16 *ibIndices;
  534. GFXBufferType bufferType = GFXBufferTypeStatic;
  535. pb->set( GFX, indexListSize, 0, bufferType );
  536. pb->lock( &ibIndices );
  537. dMemcpy( ibIndices, indices, indexListSize * sizeof(U16) );
  538. pb->unlock();
  539. delete [] indices;
  540. }
  541. DefineEngineMethod( LightFlareData, apply, void, (),,
  542. "Intended as a helper to developers and editor scripts.\n"
  543. "Force trigger an inspectPostApply"
  544. )
  545. {
  546. object->inspectPostApply();
  547. }