lightFlareData.cpp 23 KB

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