DrawPort_Particles.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. /* Copyright (c) 2002-2012 Croteam Ltd.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of version 2 of the GNU General Public License as published by
  4. the Free Software Foundation
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along
  10. with this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  12. #include "stdh.h"
  13. #include <Engine/Graphics/DrawPort.h>
  14. #include <Engine/Math/Projection.h>
  15. #include <Engine/Graphics/Color.h>
  16. #include <Engine/Graphics/Vertex.h>
  17. #include <Engine/Graphics/Texture.h>
  18. #include <Engine/Graphics/Fog_internal.h>
  19. #include <Engine/Base/Statistics_internal.h>
  20. #include <Engine/Templates/StaticArray.cpp>
  21. #include <Engine/Templates/StaticStackArray.cpp>
  22. extern const FLOAT *pfSinTable;
  23. extern const FLOAT *pfCosTable;
  24. extern CEntity *_Particle_penCurrentViewer = NULL;
  25. extern INDEX _Particle_iCurrentDrawPort = 0;
  26. extern FLOAT _Particle_fCurrentMip = 0.0f;
  27. extern BOOL _Particle_bHasFog = FALSE;
  28. extern BOOL _Particle_bHasHaze = FALSE;
  29. // variables used for rendering particles
  30. static CProjection3D *_pprProjection;
  31. static FLOAT _fPerspectiveFactor;
  32. static MEX _mexTextureWidth, _mexTextureHeight;
  33. static FLOAT _fTextureCorrectionU, _fTextureCorrectionV;
  34. static FLOAT _fNearClipDistance;
  35. static GFXTexCoord _atex[4];
  36. static COLOR _colAttMask;
  37. static BOOL _bTransFogHaze = FALSE;
  38. static BOOL _bNeedsClipping = FALSE;
  39. static CDrawPort *_pDP;
  40. static CStaticStackArray<GFXTexCoord> _atexFogHaze;
  41. static CTextureData *_ptd = NULL;
  42. static INDEX _iFrame = 0;
  43. // prepare particles for rendering
  44. void Particle_PrepareSystem( CDrawPort *pdpDrawPort, CAnyProjection3D &prProjection)
  45. {
  46. _pDP = pdpDrawPort;
  47. _pprProjection = (CProjection3D*)&*prProjection;
  48. _fNearClipDistance = -prProjection->pr_NearClipDistance;
  49. _fPerspectiveFactor = 1.0f;
  50. _Particle_iCurrentDrawPort = pdpDrawPort->GetID();
  51. // prepare projection and scale factor
  52. pdpDrawPort->SetProjection(prProjection);
  53. if( prProjection.IsPerspective()) _fPerspectiveFactor = ((CPerspectiveProjection3D*)&*prProjection)->ppr_PerspectiveRatios(1);
  54. // setup rendering mode
  55. gfxEnableDepthTest();
  56. gfxCullFace(GFX_NONE);
  57. gfxEnableTexture();
  58. // prepare general texture parameters
  59. gfxSetTextureWrapping( GFX_REPEAT, GFX_REPEAT);
  60. // prepare arrays to draw from begining
  61. gfxResetArrays();
  62. }
  63. void Particle_EndSystem( BOOL bRestoreOrtho/*=TRUE*/)
  64. {
  65. // reset projection and re-enable clipping
  66. if( bRestoreOrtho) _pDP->SetOrtho();
  67. gfxEnableClipping();
  68. }
  69. FLOAT Particle_GetMipFactor(void)
  70. {
  71. return _Particle_fCurrentMip;
  72. }
  73. CEntity *Particle_GetViewer(void)
  74. {
  75. return _Particle_penCurrentViewer;
  76. }
  77. CProjection3D *Particle_GetProjection(void)
  78. {
  79. return _pprProjection;
  80. }
  81. INDEX Particle_GetDrawPortID(void)
  82. {
  83. return _Particle_iCurrentDrawPort;
  84. }
  85. void Particle_PrepareTexture( CTextureObject *pto, enum ParticleBlendType pbt)
  86. {
  87. // determine blend type
  88. switch( pbt) {
  89. case PBT_BLEND:
  90. gfxDisableDepthWrite();
  91. gfxDisableAlphaTest();
  92. gfxEnableBlend();
  93. gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
  94. _colAttMask = 0xFFFFFF00; // attenuate alpha
  95. break;
  96. case PBT_ADD:
  97. gfxDisableDepthWrite();
  98. gfxDisableAlphaTest();
  99. gfxEnableBlend();
  100. gfxBlendFunc( GFX_ONE, GFX_ONE);
  101. _colAttMask = 0x000000FF; // attenuate color
  102. break;
  103. case PBT_MULTIPLY:
  104. gfxDisableDepthWrite();
  105. gfxDisableAlphaTest();
  106. gfxEnableBlend();
  107. gfxBlendFunc( GFX_ZERO, GFX_INV_SRC_COLOR);
  108. _colAttMask = 0x000000FF; // attenuate color
  109. break;
  110. case PBT_ADDALPHA:
  111. gfxDisableDepthWrite();
  112. gfxDisableAlphaTest();
  113. gfxEnableBlend();
  114. gfxBlendFunc( GFX_SRC_ALPHA, GFX_ONE);
  115. _colAttMask = 0xFFFFFF00; // attenuate alpha
  116. break;
  117. case PBT_FLEX:
  118. gfxDisableDepthWrite();
  119. gfxDisableAlphaTest();
  120. gfxEnableBlend();
  121. gfxBlendFunc( GFX_ONE, GFX_INV_SRC_ALPHA);
  122. _colAttMask = 0xFFFFFFFF; // attenuate alpha
  123. break;
  124. case PBT_TRANSPARENT:
  125. gfxEnableDepthWrite();
  126. gfxEnableAlphaTest();
  127. gfxDisableBlend();
  128. _colAttMask = 0; // no attenuation - texture instead
  129. break;
  130. }
  131. // get texture parameters for current frame and needed mip factor
  132. _ptd = (CTextureData*)pto->GetData();
  133. _iFrame = pto->GetFrame();
  134. // prepare and upload texture
  135. _ptd->SetAsCurrent(_iFrame);
  136. // obtain curently used texture's width and height in mexes
  137. _mexTextureWidth = _ptd->GetWidth();
  138. _mexTextureHeight = _ptd->GetHeight();
  139. // calculate correction factor (relative to greater texture dimension)
  140. _fTextureCorrectionU = 1.0f/_mexTextureWidth;
  141. _fTextureCorrectionV = 1.0f/_mexTextureHeight;
  142. _atexFogHaze.Push(4); // temporary
  143. _bTransFogHaze = _colAttMask==0 && (_Particle_bHasFog || _Particle_bHasHaze);
  144. _bNeedsClipping = FALSE;
  145. }
  146. void Particle_SetTexturePart( MEX mexWidth, MEX mexHeight, INDEX iCol, INDEX iRow)
  147. {
  148. // prepare full texture for displaying
  149. MEXaabbox2D boxTextureClipped( MEX2D( mexWidth*(iCol+0), mexHeight*(iRow+0)),
  150. MEX2D( mexWidth*(iCol+1), mexHeight*(iRow+1)));
  151. // prepare coordinates of the rectangle
  152. _atex[0].s = boxTextureClipped.Min()(1) *_fTextureCorrectionU;
  153. _atex[0].t = boxTextureClipped.Min()(2) *_fTextureCorrectionV;
  154. _atex[1].s = boxTextureClipped.Min()(1) *_fTextureCorrectionU;
  155. _atex[1].t = boxTextureClipped.Max()(2) *_fTextureCorrectionV;
  156. _atex[2].s = boxTextureClipped.Max()(1) *_fTextureCorrectionU;
  157. _atex[2].t = boxTextureClipped.Max()(2) *_fTextureCorrectionV;
  158. _atex[3].s = boxTextureClipped.Max()(1) *_fTextureCorrectionU;
  159. _atex[3].t = boxTextureClipped.Min()(2) *_fTextureCorrectionV;
  160. }
  161. // add one particle square to rendering queue
  162. void Particle_RenderSquare( const FLOAT3D &vPos, FLOAT fSize, ANGLE aRotation, COLOR col, FLOAT fYRatio/*=1.0f*/)
  163. {
  164. // trivial rejection
  165. if( fSize<0.0001f || ((col&CT_AMASK)>>CT_ASHIFT)<2) return;
  166. // project point to screen
  167. FLOAT3D vProjected;
  168. _pprProjection->PreClip( vPos, vProjected);
  169. // skip if not in screen
  170. const INDEX iTest = _pprProjection->TestSphereToFrustum( vProjected, fSize);
  171. if( iTest<0) return;
  172. const FLOAT fPixSize = fSize * _fPerspectiveFactor / vProjected(3);
  173. if( fPixSize<0.5f) return;
  174. // adjust the need for clipping
  175. if( iTest==0) _bNeedsClipping = TRUE;
  176. // eventual tex coords for fog or haze
  177. const INDEX ctTexFG = _atexFogHaze.Count();
  178. GFXTexCoord *ptexFogHaze = &_atexFogHaze[ctTexFG-4];
  179. // if haze is active
  180. if( _Particle_bHasHaze)
  181. { // get haze strength at particle position
  182. ptexFogHaze[0].s = (-vProjected(3)+_haze_fAdd)*_haze_fMul;
  183. const ULONG ulH = 255-GetHazeAlpha(ptexFogHaze[0].s);
  184. if( ulH<4) return;
  185. if( _colAttMask) { // apply haze color (if not transparent)
  186. const COLOR colH = _colAttMask | RGBAToColor( ulH,ulH,ulH,ulH);
  187. col = MulColors( col, colH);
  188. } else ptexFogHaze[0].t = 0;
  189. }
  190. // if fog is active
  191. if( _Particle_bHasFog)
  192. { // get fog strength at particle position
  193. ptexFogHaze[0].s = -vProjected(3)*_fog_fMulZ;
  194. ptexFogHaze[0].t = (vProjected%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
  195. const ULONG ulF = 255-GetFogAlpha(ptexFogHaze[0]);
  196. if( ulF<4) return;
  197. if( _colAttMask) { // apply fog color (if not transparent)
  198. const COLOR colF = _colAttMask | RGBAToColor( ulF,ulF,ulF,ulF);
  199. col = MulColors( col, colF);
  200. }
  201. }
  202. // keep fog/haze tex coords (if needed)
  203. if( _bTransFogHaze) {
  204. ptexFogHaze[1] = ptexFogHaze[2] = ptexFogHaze[3] = ptexFogHaze[0];
  205. _atexFogHaze.Push(4);
  206. }
  207. // prepare screen coords
  208. const FLOAT fI0 = vProjected(1);
  209. const FLOAT fJ0 = vProjected(2);
  210. const FLOAT fOoK = vProjected(3);
  211. // add to vertex arrays
  212. GFXVertex4 *pvtx = _avtxCommon.Push(4);
  213. GFXTexCoord *ptex = _atexCommon.Push(4);
  214. GFXColor *pcol = _acolCommon.Push(4);
  215. // prepare vertices
  216. const FLOAT fRX = fSize;
  217. const FLOAT fRY = fSize*fYRatio;
  218. if( aRotation==0) {
  219. const FLOAT fIBeg = fI0-fRX; const FLOAT fIEnd = fI0+fRX;
  220. const FLOAT fJBeg = fJ0-fRY; const FLOAT fJEnd = fJ0+fRY;
  221. pvtx[0].x = fIBeg; pvtx[0].y = fJBeg; pvtx[0].z = fOoK;
  222. pvtx[1].x = fIBeg; pvtx[1].y = fJEnd; pvtx[1].z = fOoK;
  223. pvtx[2].x = fIEnd; pvtx[2].y = fJEnd; pvtx[2].z = fOoK;
  224. pvtx[3].x = fIEnd; pvtx[3].y = fJBeg; pvtx[3].z = fOoK;
  225. } else {
  226. const INDEX iRot256 = FloatToInt(aRotation*0.7111f) & 255; // *256/360
  227. const FLOAT fSinA = pfSinTable[iRot256];
  228. const FLOAT fCosA = pfCosTable[iRot256];
  229. const FLOAT fSinPCos = fCosA*fRX+fSinA*fRY;
  230. const FLOAT fSinMCos = fSinA*fRX-fCosA*fRY;
  231. pvtx[0].x = fI0-fSinPCos; pvtx[0].y = fJ0-fSinMCos; pvtx[0].z = fOoK;
  232. pvtx[1].x = fI0+fSinMCos; pvtx[1].y = fJ0-fSinPCos; pvtx[1].z = fOoK;
  233. pvtx[2].x = fI0+fSinPCos; pvtx[2].y = fJ0+fSinMCos; pvtx[2].z = fOoK;
  234. pvtx[3].x = fI0-fSinMCos; pvtx[3].y = fJ0+fSinPCos; pvtx[3].z = fOoK;
  235. }
  236. // prepare texture coords
  237. ptex[0] = _atex[1];
  238. ptex[1] = _atex[0];
  239. ptex[2] = _atex[3];
  240. ptex[3] = _atex[2];
  241. // prepare colors
  242. const GFXColor glcol( AdjustColor( col, _slTexHueShift, _slTexSaturation));
  243. pcol[0] = glcol;
  244. pcol[1] = glcol;
  245. pcol[2] = glcol;
  246. pcol[3] = glcol;
  247. }
  248. // add one particle line to rendering queue
  249. void Particle_RenderLine( const FLOAT3D &vPos0, const FLOAT3D &vPos1, FLOAT fWidth, COLOR col)
  250. {
  251. // trivial rejection
  252. if( fWidth<0 || ((col&CT_AMASK)>>CT_ASHIFT)<2) return;
  253. // project point to screen
  254. FLOAT3D vProjected0, vProjected1;
  255. _pprProjection->PreClip( vPos0, vProjected0);
  256. _pprProjection->PreClip( vPos1, vProjected1);
  257. // skip if not in screen
  258. if (vProjected0(3)>_fNearClipDistance || vProjected1(3)>_fNearClipDistance) return;
  259. const FLOAT fK0 = 1.0f / vProjected0(3);
  260. const FLOAT fK1 = 1.0f / vProjected1(3);
  261. const FLOAT fR0 = fWidth * _fPerspectiveFactor *fK0;
  262. const FLOAT fR1 = fWidth * _fPerspectiveFactor *fK1;
  263. if( fR0<0.5f && fR1<0.5f) return;
  264. // line might need clipping
  265. _bNeedsClipping = TRUE;
  266. COLOR col0, col1;
  267. col0 = col1 = col;
  268. // eventual tex coords for fog or haze
  269. const INDEX ctTexFG = _atexFogHaze.Count();
  270. GFXTexCoord *ptexFogHaze = &_atexFogHaze[ctTexFG-4];
  271. // if haze is active
  272. if( _Particle_bHasHaze)
  273. { // get haze strength at particle positions
  274. ptexFogHaze[0].s = (-vProjected0(3)+_haze_fAdd)*_haze_fMul;
  275. ptexFogHaze[1].s = (-vProjected1(3)+_haze_fAdd)*_haze_fMul;
  276. const ULONG ulH0 = 255-GetHazeAlpha(ptexFogHaze[0].s);
  277. const ULONG ulH1 = 255-GetHazeAlpha(ptexFogHaze[1].s);
  278. if( (ulH0|ulH1)<4) return;
  279. if( _colAttMask) { // apply haze color (if not transparent)
  280. COLOR colH;
  281. colH = _colAttMask | RGBAToColor( ulH0,ulH0,ulH0,ulH0); col0 = MulColors( col0, colH);
  282. colH = _colAttMask | RGBAToColor( ulH1,ulH1,ulH1,ulH1); col1 = MulColors( col1, colH);
  283. } else ptexFogHaze[0].t = ptexFogHaze[1].t = 0;
  284. }
  285. // if fog is active
  286. if( _Particle_bHasFog)
  287. { // get fog strength at particle position
  288. ptexFogHaze[0].s = -vProjected0(3)*_fog_fMulZ;
  289. ptexFogHaze[0].t = (vProjected0%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
  290. ptexFogHaze[1].s = -vProjected1(3)*_fog_fMulZ;
  291. ptexFogHaze[1].t = (vProjected1%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
  292. const ULONG ulF0 = 255-GetFogAlpha(ptexFogHaze[0]);
  293. const ULONG ulF1 = 255-GetFogAlpha(ptexFogHaze[1]);
  294. if( (ulF0|ulF1)<4) return;
  295. if( _colAttMask) { // apply fog color (if not transparent)
  296. COLOR colF; // apply fog color
  297. colF = _colAttMask | RGBAToColor( ulF0,ulF0,ulF0,ulF0); col0 = MulColors( col0, colF);
  298. colF = _colAttMask | RGBAToColor( ulF1,ulF1,ulF1,ulF1); col1 = MulColors( col1, colF);
  299. }
  300. }
  301. // keep fog/haze tex coords (if needed)
  302. if( _bTransFogHaze) {
  303. ptexFogHaze[2] = ptexFogHaze[1];
  304. ptexFogHaze[3] = ptexFogHaze[0];
  305. _atexFogHaze.Push(4);
  306. }
  307. // lets draw
  308. const FLOAT fI0 = vProjected0(1); const FLOAT fI1 = vProjected1(1);
  309. const FLOAT fJ0 = vProjected0(2); const FLOAT fJ1 = vProjected1(2);
  310. const FLOAT fOoK0 = vProjected0(3); const FLOAT fOoK1 = vProjected1(3);
  311. FLOAT fDI = fI1*fK1 - fI0*fK0;
  312. FLOAT fDJ = fJ1*fK1 - fJ0*fK0;
  313. const FLOAT fD = fWidth / Sqrt( fDI*fDI + fDJ*fDJ);
  314. fDI *= fD; // multiplied by width!
  315. fDJ *= fD;
  316. // add to vertex arrays
  317. GFXVertex *pvtx = _avtxCommon.Push(4);
  318. GFXTexCoord *ptex = _atexCommon.Push(4);
  319. GFXColor *pcol = _acolCommon.Push(4);
  320. // prepare vertices
  321. pvtx[0].x = fI0+fDJ; pvtx[0].y = fJ0-fDI; pvtx[0].z = fOoK0;
  322. pvtx[1].x = fI1+fDJ; pvtx[1].y = fJ1-fDI; pvtx[1].z = fOoK1;
  323. pvtx[2].x = fI1-fDJ; pvtx[2].y = fJ1+fDI; pvtx[2].z = fOoK1;
  324. pvtx[3].x = fI0-fDJ; pvtx[3].y = fJ0+fDI; pvtx[3].z = fOoK0;
  325. // prepare texture coords
  326. ptex[0] = _atex[0];
  327. ptex[1] = _atex[1];
  328. ptex[2] = _atex[2];
  329. ptex[3] = _atex[3];
  330. // prepare colors
  331. const GFXColor glcol0( AdjustColor( col0, _slTexHueShift, _slTexSaturation));
  332. const GFXColor glcol1( AdjustColor( col1, _slTexHueShift, _slTexSaturation));
  333. pcol[0] = glcol0;
  334. pcol[1] = glcol1;
  335. pcol[2] = glcol1;
  336. pcol[3] = glcol0;
  337. }
  338. // add one 3D particle quad to rendering queue
  339. void Particle_RenderQuad3D( const FLOAT3D &vPos0, const FLOAT3D &vPos1, const FLOAT3D &vPos2,
  340. const FLOAT3D &vPos3, COLOR col)
  341. {
  342. // trivial rejection
  343. if( ((col&CT_AMASK)>>CT_ASHIFT)<2) return;
  344. // project point to screen
  345. FLOAT3D vProjected0, vProjected1, vProjected2, vProjected3;
  346. _pprProjection->PreClip( vPos0, vProjected0);
  347. _pprProjection->PreClip( vPos1, vProjected1);
  348. _pprProjection->PreClip( vPos2, vProjected2);
  349. _pprProjection->PreClip( vPos3, vProjected3);
  350. // test for trivial rejection (sphere method)
  351. FLOAT3D vNearest = vProjected0; // find nearest-Z vertex
  352. if( vNearest(3)>vProjected1(3)) vNearest = vProjected1;
  353. if( vNearest(3)>vProjected2(3)) vNearest = vProjected2;
  354. if( vNearest(3)>vProjected3(3)) vNearest = vProjected3;
  355. // find center
  356. const FLOAT fX = (vProjected0(1)+vProjected1(1)+vProjected2(1)+vProjected3(1)) * 0.25f;
  357. const FLOAT fY = (vProjected0(2)+vProjected1(2)+vProjected2(2)+vProjected3(2)) * 0.25f;
  358. // find radius (approx. distance to nearest-Z vertex)
  359. // we won't do sqrt but rather larger distance * 0.7f (1/sqrt(2))
  360. const FLOAT fDX = Abs(fX-vNearest(1));
  361. const FLOAT fDY = Abs(fY-vNearest(2));
  362. const FLOAT fR = 0.7f * Max(fDX,fDY);
  363. // set center vertex location and test it
  364. vNearest(1) = fX;
  365. vNearest(2) = fY;
  366. const INDEX iTest = _pprProjection->TestSphereToFrustum( vNearest, fR);
  367. if( iTest<0) return;
  368. // adjust the need for clipping
  369. if( iTest==0) _bNeedsClipping = TRUE;
  370. // separate colors (for the sake of fog/haze)
  371. COLOR col0,col1,col2,col3;
  372. col0 = col1 = col2 = col3 = col;
  373. // eventual tex coords for fog or haze
  374. const INDEX ctTexFG = _atexFogHaze.Count();
  375. GFXTexCoord *ptexFogHaze = &_atexFogHaze[ctTexFG-4];
  376. // if haze is active
  377. if( _Particle_bHasHaze)
  378. { // get haze strength at particle position
  379. ptexFogHaze[0].s = (-vProjected0(3)+_haze_fAdd)*_haze_fMul;
  380. ptexFogHaze[1].s = (-vProjected1(3)+_haze_fAdd)*_haze_fMul;
  381. ptexFogHaze[2].s = (-vProjected2(3)+_haze_fAdd)*_haze_fMul;
  382. ptexFogHaze[3].s = (-vProjected3(3)+_haze_fAdd)*_haze_fMul;
  383. const ULONG ulH0 = 255-GetHazeAlpha(ptexFogHaze[0].s);
  384. const ULONG ulH1 = 255-GetHazeAlpha(ptexFogHaze[1].s);
  385. const ULONG ulH2 = 255-GetHazeAlpha(ptexFogHaze[2].s);
  386. const ULONG ulH3 = 255-GetHazeAlpha(ptexFogHaze[3].s);
  387. if( (ulH0|ulH1|ulH2|ulH3)<4) return;
  388. if( _colAttMask) { // apply haze color (if not transparent)
  389. COLOR colH;
  390. colH = _colAttMask | RGBAToColor( ulH0,ulH0,ulH0,ulH0); col0 = MulColors( col0, colH);
  391. colH = _colAttMask | RGBAToColor( ulH1,ulH1,ulH1,ulH1); col1 = MulColors( col1, colH);
  392. colH = _colAttMask | RGBAToColor( ulH2,ulH2,ulH2,ulH2); col2 = MulColors( col2, colH);
  393. colH = _colAttMask | RGBAToColor( ulH3,ulH3,ulH3,ulH3); col3 = MulColors( col3, colH);
  394. } else ptexFogHaze[0].t = ptexFogHaze[1].t = ptexFogHaze[2].t = ptexFogHaze[3].t = 0;
  395. }
  396. // if fog is active
  397. if( _Particle_bHasFog)
  398. { // get fog strength at particle position
  399. ptexFogHaze[0].s = -vProjected0(3)*_fog_fMulZ;
  400. ptexFogHaze[0].t = (vProjected0%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
  401. ptexFogHaze[1].s = -vProjected1(3)*_fog_fMulZ;
  402. ptexFogHaze[1].t = (vProjected1%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
  403. ptexFogHaze[2].s = -vProjected2(3)*_fog_fMulZ;
  404. ptexFogHaze[2].t = (vProjected2%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
  405. ptexFogHaze[3].s = -vProjected3(3)*_fog_fMulZ;
  406. ptexFogHaze[3].t = (vProjected3%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
  407. const ULONG ulF0 = 255-GetFogAlpha(ptexFogHaze[0]);
  408. const ULONG ulF1 = 255-GetFogAlpha(ptexFogHaze[1]);
  409. const ULONG ulF2 = 255-GetFogAlpha(ptexFogHaze[2]);
  410. const ULONG ulF3 = 255-GetFogAlpha(ptexFogHaze[3]);
  411. if( (ulF0|ulF1|ulF2|ulF3)<4) return;
  412. if( _colAttMask) { // apply fog color (if not transparent)
  413. COLOR colF;
  414. colF = _colAttMask | RGBAToColor( ulF0,ulF0,ulF0,ulF0); col0 = MulColors( col0, colF);
  415. colF = _colAttMask | RGBAToColor( ulF1,ulF1,ulF1,ulF1); col1 = MulColors( col1, colF);
  416. colF = _colAttMask | RGBAToColor( ulF2,ulF2,ulF2,ulF2); col2 = MulColors( col2, colF);
  417. colF = _colAttMask | RGBAToColor( ulF3,ulF3,ulF3,ulF3); col3 = MulColors( col3, colF);
  418. }
  419. }
  420. // keep fog/haze tex coords (if needed)
  421. if( _bTransFogHaze) _atexFogHaze.Push(4);
  422. // add to vertex arrays
  423. GFXVertex *pvtx = _avtxCommon.Push(4);
  424. GFXTexCoord *ptex = _atexCommon.Push(4);
  425. GFXColor *pcol = _acolCommon.Push(4);
  426. // prepare vertices
  427. pvtx[0].x = vProjected0(1); pvtx[0].y = vProjected0(2); pvtx[0].z = vProjected0(3);
  428. pvtx[1].x = vProjected1(1); pvtx[1].y = vProjected1(2); pvtx[1].z = vProjected1(3);
  429. pvtx[2].x = vProjected2(1); pvtx[2].y = vProjected2(2); pvtx[2].z = vProjected2(3);
  430. pvtx[3].x = vProjected3(1); pvtx[3].y = vProjected3(2); pvtx[3].z = vProjected3(3);
  431. // prepare texture coords
  432. ptex[0] = _atex[0];
  433. ptex[1] = _atex[1];
  434. ptex[2] = _atex[2];
  435. ptex[3] = _atex[3];
  436. // prepare colors
  437. const GFXColor glcol0( AdjustColor( col0, _slTexHueShift, _slTexSaturation));
  438. const GFXColor glcol1( AdjustColor( col1, _slTexHueShift, _slTexSaturation));
  439. const GFXColor glcol2( AdjustColor( col2, _slTexHueShift, _slTexSaturation));
  440. const GFXColor glcol3( AdjustColor( col3, _slTexHueShift, _slTexSaturation));
  441. pcol[0] = glcol0;
  442. pcol[1] = glcol1;
  443. pcol[2] = glcol2;
  444. pcol[3] = glcol3;
  445. }
  446. // flushes particle rendering queue (i.e. renders particle on screen)
  447. void Particle_Flush(void)
  448. {
  449. // update stats
  450. const INDEX ctParticles = _avtxCommon.Count()/4;
  451. _sfStats.IncrementCounter( CStatForm::SCI_PARTICLES, ctParticles);
  452. _pGfx->gl_ctParticleTriangles += ctParticles*2;
  453. // determine need for clipping
  454. if( _bNeedsClipping) gfxEnableClipping();
  455. else gfxDisableClipping();
  456. // flush 1st layer
  457. gfxFlushQuads();
  458. // maybe we need to render fog/haze layer
  459. if( _bTransFogHaze)
  460. { // setup haze/fog color and texture
  461. GFXColor glcolFH;
  462. gfxSetTextureWrapping( GFX_CLAMP, GFX_CLAMP);
  463. if( _Particle_bHasHaze) {
  464. gfxSetTexture( _haze_ulTexture, _haze_tpLocal);
  465. glcolFH.abgr = ByteSwap( AdjustColor( _haze_hp.hp_colColor, _slTexHueShift, _slTexSaturation));
  466. } else {
  467. gfxSetTexture( _fog_ulTexture, _fog_tpLocal);
  468. glcolFH.abgr = ByteSwap( AdjustColor( _fog_fp.fp_colColor, _slTexHueShift, _slTexSaturation));
  469. }
  470. // prepare haze rendering parameters
  471. gfxDisableAlphaTest();
  472. gfxEnableBlend();
  473. gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
  474. gfxDisableDepthWrite();
  475. gfxDepthFunc( GFX_EQUAL); // adjust z-buffer compare
  476. // copy fog/haze texture array to main texture array and set color to fog/haze
  477. const INDEX ctVertices = _atexCommon.Count();
  478. ASSERT( _atexFogHaze.Count()==ctVertices+4);
  479. memcpy( &_atexCommon[0], &_atexFogHaze[0], ctVertices*sizeof(GFXTexCoord));
  480. for( INDEX i=0; i<ctVertices; i++) _acolCommon[i] = glcolFH;
  481. // render it
  482. gfxFlushQuads();
  483. // revert to old settings
  484. gfxEnableAlphaTest();
  485. gfxDisableBlend();
  486. gfxDepthFunc( GFX_LESS_EQUAL);
  487. _ptd->SetAsCurrent(_iFrame);
  488. _pGfx->gl_ctParticleTriangles += ctParticles*2;
  489. }
  490. // all done
  491. gfxResetArrays();
  492. _atexFogHaze.PopAll();
  493. _bNeedsClipping = FALSE;
  494. }
  495. // SORTING ROUTINES
  496. static int qsort_CompareZ( const void *pI0, const void *pI1) {
  497. const INDEX i0 = (*(INDEX*)pI0) *4;
  498. const INDEX i1 = (*(INDEX*)pI1) *4;
  499. const FLOAT fZ0 = _avtxCommon[i0].z;
  500. const FLOAT fZ1 = _avtxCommon[i1].z;
  501. if( fZ0<fZ1) return +1;
  502. else if( fZ0>fZ1) return -1;
  503. else return 0;
  504. }
  505. static int qsort_CompareZ3D( const void *pI0, const void *pI1) {
  506. const INDEX i0 = (*(INDEX*)pI0) *4;
  507. const INDEX i1 = (*(INDEX*)pI1) *4;
  508. const FLOAT fZ0 = (_avtxCommon[i0].z + _avtxCommon[i0+1].z + _avtxCommon[i0+2].z + _avtxCommon[i0+3].z) / 4.0f;
  509. const FLOAT fZ1 = (_avtxCommon[i1].z + _avtxCommon[i1+1].z + _avtxCommon[i1+2].z + _avtxCommon[i1+3].z) / 4.0f;
  510. if( fZ0<fZ1) return +1;
  511. else if( fZ0>fZ1) return -1;
  512. else return 0;
  513. }
  514. // sorts particles by distance
  515. void Particle_Sort( BOOL b3D/*=FALSE*/)
  516. {
  517. INDEX i;
  518. const INDEX ctParticles = _avtxCommon.Count()/4;
  519. if( ctParticles<=0) return; // nothing to do!
  520. // generate sort array
  521. CStaticArray<INDEX> aiIndices;
  522. aiIndices.New(ctParticles);
  523. for( i=0; i<ctParticles; i++) aiIndices[i] = i;
  524. // bubble sort indices by vertex Z coord
  525. if(b3D) qsort( &aiIndices[0], ctParticles, sizeof(INDEX), qsort_CompareZ3D);
  526. else qsort( &aiIndices[0], ctParticles, sizeof(INDEX), qsort_CompareZ);
  527. // generate inverse table
  528. CStaticArray<INDEX> aiInverse;
  529. aiInverse.New(ctParticles);
  530. for( i=0; i<ctParticles; i++) {
  531. const INDEX iOrig = aiIndices[i];
  532. aiInverse[iOrig] = i;
  533. }
  534. // sort vertices by indices
  535. for( i=0; i<ctParticles;) // i is incremented in loop
  536. { // fetch destination
  537. INDEX &iWhere = aiInverse[i];
  538. ASSERT( iWhere<ctParticles);
  539. // if current is already in place, advance to next index
  540. if( iWhere==i) { i++; continue; }
  541. // swap vertices
  542. Swap( _avtxCommon[iWhere*4+0], _avtxCommon[i*4+0]);
  543. Swap( _avtxCommon[iWhere*4+1], _avtxCommon[i*4+1]);
  544. Swap( _avtxCommon[iWhere*4+2], _avtxCommon[i*4+2]);
  545. Swap( _avtxCommon[iWhere*4+3], _avtxCommon[i*4+3]);
  546. // swap texture coords
  547. Swap( _atexCommon[iWhere*4+0], _atexCommon[i*4+0]);
  548. Swap( _atexCommon[iWhere*4+1], _atexCommon[i*4+1]);
  549. Swap( _atexCommon[iWhere*4+2], _atexCommon[i*4+2]);
  550. Swap( _atexCommon[iWhere*4+3], _atexCommon[i*4+3]);
  551. // swap colors
  552. Swap( _acolCommon[iWhere*4+0], _acolCommon[i*4+0]);
  553. Swap( _acolCommon[iWhere*4+1], _acolCommon[i*4+1]);
  554. Swap( _acolCommon[iWhere*4+2], _acolCommon[i*4+2]);
  555. Swap( _acolCommon[iWhere*4+3], _acolCommon[i*4+3]);
  556. // swap indices
  557. Swap( aiInverse[iWhere], aiInverse[i]);
  558. }
  559. #ifndef NDEBUG
  560. // test to see whether the array is sorted
  561. INDEX *pidx = &aiInverse[0];
  562. GFXVertex4 *pvtx = &_avtxCommon[0];
  563. for( i=0; i<ctParticles-1; i++) {
  564. ASSERT( pidx[i] < pidx[i+1]);
  565. ASSERT( pvtx[i*4].z >= pvtx[(i+1)*4].z);
  566. }
  567. #endif
  568. }