Serious-Engine/Sources/Engine/Graphics/DrawPort_Particles.cpp
Ryan C. Gordon 24cb244d43 First attempt to hand-merge Ryan's Linux and Mac OS X port.
This was a _ton_ of changes, made 15 years ago, so there are probably some
problems to work out still.

Among others: Engine/Base/Stream.* was mostly abandoned and will need to be
re-ported.

Still, this is a pretty good start, and probably holds a world record for
lines of changes or something.  :)
2016-03-28 23:46:13 -04:00

631 lines
22 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
#include "Engine/StdH.h"
#include <Engine/Graphics/DrawPort.h>
#include <Engine/Math/Projection.h>
#include <Engine/Graphics/Color.h>
#include <Engine/Graphics/Vertex.h>
#include <Engine/Graphics/Texture.h>
#include <Engine/Graphics/Fog_internal.h>
#include <Engine/Base/Statistics_Internal.h>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/StaticStackArray.cpp>
extern const FLOAT *pfSinTable;
extern const FLOAT *pfCosTable;
CEntity *_Particle_penCurrentViewer = NULL;
INDEX _Particle_iCurrentDrawPort = 0;
FLOAT _Particle_fCurrentMip = 0.0f;
BOOL _Particle_bHasFog = FALSE;
BOOL _Particle_bHasHaze = FALSE;
// variables used for rendering particles
static CProjection3D *_pprProjection;
static FLOAT _fPerspectiveFactor;
static MEX _mexTextureWidth, _mexTextureHeight;
static FLOAT _fTextureCorrectionU, _fTextureCorrectionV;
static FLOAT _fNearClipDistance;
static GFXTexCoord _atex[4];
static COLOR _colAttMask;
static BOOL _bTransFogHaze = FALSE;
static BOOL _bNeedsClipping = FALSE;
static CDrawPort *_pDP;
static CStaticStackArray<GFXTexCoord> _atexFogHaze;
static CTextureData *_ptd = NULL;
static INDEX _iFrame = 0;
// prepare particles for rendering
void Particle_PrepareSystem( CDrawPort *pdpDrawPort, CAnyProjection3D &prProjection)
{
_pDP = pdpDrawPort;
_pprProjection = (CProjection3D*)&*prProjection;
_fNearClipDistance = -prProjection->pr_NearClipDistance;
_fPerspectiveFactor = 1.0f;
_Particle_iCurrentDrawPort = pdpDrawPort->GetID();
// prepare projection and scale factor
pdpDrawPort->SetProjection(prProjection);
if( prProjection.IsPerspective()) _fPerspectiveFactor = ((CPerspectiveProjection3D*)&*prProjection)->ppr_PerspectiveRatios(1);
// setup rendering mode
gfxEnableDepthTest();
gfxCullFace(GFX_NONE);
gfxEnableTexture();
// prepare general texture parameters
gfxSetTextureWrapping( GFX_REPEAT, GFX_REPEAT);
// prepare arrays to draw from begining
gfxResetArrays();
}
void Particle_EndSystem( BOOL bRestoreOrtho/*=TRUE*/)
{
// reset projection and re-enable clipping
if( bRestoreOrtho) _pDP->SetOrtho();
gfxEnableClipping();
}
FLOAT Particle_GetMipFactor(void)
{
return _Particle_fCurrentMip;
}
CEntity *Particle_GetViewer(void)
{
return _Particle_penCurrentViewer;
}
CProjection3D *Particle_GetProjection(void)
{
return _pprProjection;
}
INDEX Particle_GetDrawPortID(void)
{
return _Particle_iCurrentDrawPort;
}
void Particle_PrepareTexture( CTextureObject *pto, enum ParticleBlendType pbt)
{
// determine blend type
switch( pbt) {
case PBT_BLEND:
gfxDisableDepthWrite();
gfxDisableAlphaTest();
gfxEnableBlend();
gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
_colAttMask = 0xFFFFFF00; // attenuate alpha
break;
case PBT_ADD:
gfxDisableDepthWrite();
gfxDisableAlphaTest();
gfxEnableBlend();
gfxBlendFunc( GFX_ONE, GFX_ONE);
_colAttMask = 0x000000FF; // attenuate color
break;
case PBT_MULTIPLY:
gfxDisableDepthWrite();
gfxDisableAlphaTest();
gfxEnableBlend();
gfxBlendFunc( GFX_ZERO, GFX_INV_SRC_COLOR);
_colAttMask = 0x000000FF; // attenuate color
break;
case PBT_ADDALPHA:
gfxDisableDepthWrite();
gfxDisableAlphaTest();
gfxEnableBlend();
gfxBlendFunc( GFX_SRC_ALPHA, GFX_ONE);
_colAttMask = 0xFFFFFF00; // attenuate alpha
break;
case PBT_FLEX:
gfxDisableDepthWrite();
gfxDisableAlphaTest();
gfxEnableBlend();
gfxBlendFunc( GFX_ONE, GFX_INV_SRC_ALPHA);
_colAttMask = 0xFFFFFFFF; // attenuate alpha
break;
case PBT_TRANSPARENT:
gfxEnableDepthWrite();
gfxEnableAlphaTest();
gfxDisableBlend();
_colAttMask = 0; // no attenuation - texture instead
break;
}
// get texture parameters for current frame and needed mip factor
_ptd = (CTextureData*)pto->GetData();
_iFrame = pto->GetFrame();
// prepare and upload texture
_ptd->SetAsCurrent(_iFrame);
// obtain curently used texture's width and height in mexes
_mexTextureWidth = _ptd->GetWidth();
_mexTextureHeight = _ptd->GetHeight();
// calculate correction factor (relative to greater texture dimension)
_fTextureCorrectionU = 1.0f/_mexTextureWidth;
_fTextureCorrectionV = 1.0f/_mexTextureHeight;
_atexFogHaze.Push(4); // temporary
_bTransFogHaze = _colAttMask==0 && (_Particle_bHasFog || _Particle_bHasHaze);
_bNeedsClipping = FALSE;
}
void Particle_SetTexturePart( MEX mexWidth, MEX mexHeight, INDEX iCol, INDEX iRow)
{
// prepare full texture for displaying
MEXaabbox2D boxTextureClipped( MEX2D( mexWidth*(iCol+0), mexHeight*(iRow+0)),
MEX2D( mexWidth*(iCol+1), mexHeight*(iRow+1)));
// prepare coordinates of the rectangle
_atex[0].st.s = boxTextureClipped.Min()(1) *_fTextureCorrectionU;
_atex[0].st.t = boxTextureClipped.Min()(2) *_fTextureCorrectionV;
_atex[1].st.s = boxTextureClipped.Min()(1) *_fTextureCorrectionU;
_atex[1].st.t = boxTextureClipped.Max()(2) *_fTextureCorrectionV;
_atex[2].st.s = boxTextureClipped.Max()(1) *_fTextureCorrectionU;
_atex[2].st.t = boxTextureClipped.Max()(2) *_fTextureCorrectionV;
_atex[3].st.s = boxTextureClipped.Max()(1) *_fTextureCorrectionU;
_atex[3].st.t = boxTextureClipped.Min()(2) *_fTextureCorrectionV;
}
// add one particle square to rendering queue
void Particle_RenderSquare( const FLOAT3D &vPos, FLOAT fSize, ANGLE aRotation, COLOR col, FLOAT fYRatio/*=1.0f*/)
{
// trivial rejection
if( fSize<0.0001f || ((col&CT_AMASK)>>CT_ASHIFT)<2) return;
// project point to screen
FLOAT3D vProjected;
_pprProjection->PreClip( vPos, vProjected);
// skip if not in screen
const INDEX iTest = _pprProjection->TestSphereToFrustum( vProjected, fSize);
if( iTest<0) return;
const FLOAT fPixSize = fSize * _fPerspectiveFactor / vProjected(3);
if( fPixSize<0.5f) return;
// adjust the need for clipping
if( iTest==0) _bNeedsClipping = TRUE;
// eventual tex coords for fog or haze
const INDEX ctTexFG = _atexFogHaze.Count();
GFXTexCoord *ptexFogHaze = &_atexFogHaze[ctTexFG-4];
// if haze is active
if( _Particle_bHasHaze)
{ // get haze strength at particle position
ptexFogHaze[0].st.s = (-vProjected(3)+_haze_fAdd)*_haze_fMul;
const ULONG ulH = 255-GetHazeAlpha(ptexFogHaze[0].st.s);
if( ulH<4) return;
if( _colAttMask) { // apply haze color (if not transparent)
const COLOR colH = _colAttMask | RGBAToColor( ulH,ulH,ulH,ulH);
col = MulColors( col, colH);
} else ptexFogHaze[0].st.t = 0;
}
// if fog is active
if( _Particle_bHasFog)
{ // get fog strength at particle position
ptexFogHaze[0].st.s = -vProjected(3)*_fog_fMulZ;
ptexFogHaze[0].st.t = (vProjected%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
const ULONG ulF = 255-GetFogAlpha(ptexFogHaze[0]);
if( ulF<4) return;
if( _colAttMask) { // apply fog color (if not transparent)
const COLOR colF = _colAttMask | RGBAToColor( ulF,ulF,ulF,ulF);
col = MulColors( col, colF);
}
}
// keep fog/haze tex coords (if needed)
if( _bTransFogHaze) {
ptexFogHaze[1] = ptexFogHaze[2] = ptexFogHaze[3] = ptexFogHaze[0];
_atexFogHaze.Push(4);
}
// prepare screen coords
const FLOAT fI0 = vProjected(1);
const FLOAT fJ0 = vProjected(2);
const FLOAT fOoK = vProjected(3);
// add to vertex arrays
GFXVertex4 *pvtx = _avtxCommon.Push(4);
GFXTexCoord *ptex = _atexCommon.Push(4);
GFXColor *pcol = _acolCommon.Push(4);
// prepare vertices
const FLOAT fRX = fSize;
const FLOAT fRY = fSize*fYRatio;
if( aRotation==0) {
const FLOAT fIBeg = fI0-fRX; const FLOAT fIEnd = fI0+fRX;
const FLOAT fJBeg = fJ0-fRY; const FLOAT fJEnd = fJ0+fRY;
pvtx[0].x = fIBeg; pvtx[0].y = fJBeg; pvtx[0].z = fOoK;
pvtx[1].x = fIBeg; pvtx[1].y = fJEnd; pvtx[1].z = fOoK;
pvtx[2].x = fIEnd; pvtx[2].y = fJEnd; pvtx[2].z = fOoK;
pvtx[3].x = fIEnd; pvtx[3].y = fJBeg; pvtx[3].z = fOoK;
} else {
const INDEX iRot256 = FloatToInt(aRotation*0.7111f) & 255; // *256/360
const FLOAT fSinA = pfSinTable[iRot256];
const FLOAT fCosA = pfCosTable[iRot256];
const FLOAT fSinPCos = fCosA*fRX+fSinA*fRY;
const FLOAT fSinMCos = fSinA*fRX-fCosA*fRY;
pvtx[0].x = fI0-fSinPCos; pvtx[0].y = fJ0-fSinMCos; pvtx[0].z = fOoK;
pvtx[1].x = fI0+fSinMCos; pvtx[1].y = fJ0-fSinPCos; pvtx[1].z = fOoK;
pvtx[2].x = fI0+fSinPCos; pvtx[2].y = fJ0+fSinMCos; pvtx[2].z = fOoK;
pvtx[3].x = fI0-fSinMCos; pvtx[3].y = fJ0+fSinPCos; pvtx[3].z = fOoK;
}
// prepare texture coords
ptex[0] = _atex[1];
ptex[1] = _atex[0];
ptex[2] = _atex[3];
ptex[3] = _atex[2];
// prepare colors
const GFXColor glcol( AdjustColor( col, _slTexHueShift, _slTexSaturation));
pcol[0] = glcol;
pcol[1] = glcol;
pcol[2] = glcol;
pcol[3] = glcol;
}
// add one particle line to rendering queue
void Particle_RenderLine( const FLOAT3D &vPos0, const FLOAT3D &vPos1, FLOAT fWidth, COLOR col)
{
// trivial rejection
if( fWidth<0 || ((col&CT_AMASK)>>CT_ASHIFT)<2) return;
// project point to screen
FLOAT3D vProjected0, vProjected1;
_pprProjection->PreClip( vPos0, vProjected0);
_pprProjection->PreClip( vPos1, vProjected1);
// skip if not in screen
if (vProjected0(3)>_fNearClipDistance || vProjected1(3)>_fNearClipDistance) return;
const FLOAT fK0 = 1.0f / vProjected0(3);
const FLOAT fK1 = 1.0f / vProjected1(3);
const FLOAT fR0 = fWidth * _fPerspectiveFactor *fK0;
const FLOAT fR1 = fWidth * _fPerspectiveFactor *fK1;
if( fR0<0.5f && fR1<0.5f) return;
// line might need clipping
_bNeedsClipping = TRUE;
COLOR col0, col1;
col0 = col1 = col;
// eventual tex coords for fog or haze
const INDEX ctTexFG = _atexFogHaze.Count();
GFXTexCoord *ptexFogHaze = &_atexFogHaze[ctTexFG-4];
// if haze is active
if( _Particle_bHasHaze)
{ // get haze strength at particle positions
ptexFogHaze[0].st.s = (-vProjected0(3)+_haze_fAdd)*_haze_fMul;
ptexFogHaze[1].st.s = (-vProjected1(3)+_haze_fAdd)*_haze_fMul;
const ULONG ulH0 = 255-GetHazeAlpha(ptexFogHaze[0].st.s);
const ULONG ulH1 = 255-GetHazeAlpha(ptexFogHaze[1].st.s);
if( (ulH0|ulH1)<4) return;
if( _colAttMask) { // apply haze color (if not transparent)
COLOR colH;
colH = _colAttMask | RGBAToColor( ulH0,ulH0,ulH0,ulH0); col0 = MulColors( col0, colH);
colH = _colAttMask | RGBAToColor( ulH1,ulH1,ulH1,ulH1); col1 = MulColors( col1, colH);
} else ptexFogHaze[0].st.t = ptexFogHaze[1].st.t = 0;
}
// if fog is active
if( _Particle_bHasFog)
{ // get fog strength at particle position
ptexFogHaze[0].st.s = -vProjected0(3)*_fog_fMulZ;
ptexFogHaze[0].st.t = (vProjected0%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
ptexFogHaze[1].st.s = -vProjected1(3)*_fog_fMulZ;
ptexFogHaze[1].st.t = (vProjected1%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
const ULONG ulF0 = 255-GetFogAlpha(ptexFogHaze[0]);
const ULONG ulF1 = 255-GetFogAlpha(ptexFogHaze[1]);
if( (ulF0|ulF1)<4) return;
if( _colAttMask) { // apply fog color (if not transparent)
COLOR colF; // apply fog color
colF = _colAttMask | RGBAToColor( ulF0,ulF0,ulF0,ulF0); col0 = MulColors( col0, colF);
colF = _colAttMask | RGBAToColor( ulF1,ulF1,ulF1,ulF1); col1 = MulColors( col1, colF);
}
}
// keep fog/haze tex coords (if needed)
if( _bTransFogHaze) {
ptexFogHaze[2] = ptexFogHaze[1];
ptexFogHaze[3] = ptexFogHaze[0];
_atexFogHaze.Push(4);
}
// lets draw
const FLOAT fI0 = vProjected0(1); const FLOAT fI1 = vProjected1(1);
const FLOAT fJ0 = vProjected0(2); const FLOAT fJ1 = vProjected1(2);
const FLOAT fOoK0 = vProjected0(3); const FLOAT fOoK1 = vProjected1(3);
FLOAT fDI = fI1*fK1 - fI0*fK0;
FLOAT fDJ = fJ1*fK1 - fJ0*fK0;
const FLOAT fD = fWidth / Sqrt( fDI*fDI + fDJ*fDJ);
fDI *= fD; // multiplied by width!
fDJ *= fD;
// add to vertex arrays
GFXVertex *pvtx = _avtxCommon.Push(4);
GFXTexCoord *ptex = _atexCommon.Push(4);
GFXColor *pcol = _acolCommon.Push(4);
// prepare vertices
pvtx[0].x = fI0+fDJ; pvtx[0].y = fJ0-fDI; pvtx[0].z = fOoK0;
pvtx[1].x = fI1+fDJ; pvtx[1].y = fJ1-fDI; pvtx[1].z = fOoK1;
pvtx[2].x = fI1-fDJ; pvtx[2].y = fJ1+fDI; pvtx[2].z = fOoK1;
pvtx[3].x = fI0-fDJ; pvtx[3].y = fJ0+fDI; pvtx[3].z = fOoK0;
// prepare texture coords
ptex[0] = _atex[0];
ptex[1] = _atex[1];
ptex[2] = _atex[2];
ptex[3] = _atex[3];
// prepare colors
const GFXColor glcol0( AdjustColor( col0, _slTexHueShift, _slTexSaturation));
const GFXColor glcol1( AdjustColor( col1, _slTexHueShift, _slTexSaturation));
pcol[0] = glcol0;
pcol[1] = glcol1;
pcol[2] = glcol1;
pcol[3] = glcol0;
}
// add one 3D particle quad to rendering queue
void Particle_RenderQuad3D( const FLOAT3D &vPos0, const FLOAT3D &vPos1, const FLOAT3D &vPos2,
const FLOAT3D &vPos3, COLOR col)
{
// trivial rejection
if( ((col&CT_AMASK)>>CT_ASHIFT)<2) return;
// project point to screen
FLOAT3D vProjected0, vProjected1, vProjected2, vProjected3;
_pprProjection->PreClip( vPos0, vProjected0);
_pprProjection->PreClip( vPos1, vProjected1);
_pprProjection->PreClip( vPos2, vProjected2);
_pprProjection->PreClip( vPos3, vProjected3);
// test for trivial rejection (sphere method)
FLOAT3D vNearest = vProjected0; // find nearest-Z vertex
if( vNearest(3)>vProjected1(3)) vNearest = vProjected1;
if( vNearest(3)>vProjected2(3)) vNearest = vProjected2;
if( vNearest(3)>vProjected3(3)) vNearest = vProjected3;
// find center
const FLOAT fX = (vProjected0(1)+vProjected1(1)+vProjected2(1)+vProjected3(1)) * 0.25f;
const FLOAT fY = (vProjected0(2)+vProjected1(2)+vProjected2(2)+vProjected3(2)) * 0.25f;
// find radius (approx. distance to nearest-Z vertex)
// we won't do sqrt but rather larger distance * 0.7f (1/sqrt(2))
const FLOAT fDX = Abs(fX-vNearest(1));
const FLOAT fDY = Abs(fY-vNearest(2));
const FLOAT fR = 0.7f * Max(fDX,fDY);
// set center vertex location and test it
vNearest(1) = fX;
vNearest(2) = fY;
const INDEX iTest = _pprProjection->TestSphereToFrustum( vNearest, fR);
if( iTest<0) return;
// adjust the need for clipping
if( iTest==0) _bNeedsClipping = TRUE;
// separate colors (for the sake of fog/haze)
COLOR col0,col1,col2,col3;
col0 = col1 = col2 = col3 = col;
// eventual tex coords for fog or haze
const INDEX ctTexFG = _atexFogHaze.Count();
GFXTexCoord *ptexFogHaze = &_atexFogHaze[ctTexFG-4];
// if haze is active
if( _Particle_bHasHaze)
{ // get haze strength at particle position
ptexFogHaze[0].st.s = (-vProjected0(3)+_haze_fAdd)*_haze_fMul;
ptexFogHaze[1].st.s = (-vProjected1(3)+_haze_fAdd)*_haze_fMul;
ptexFogHaze[2].st.s = (-vProjected2(3)+_haze_fAdd)*_haze_fMul;
ptexFogHaze[3].st.s = (-vProjected3(3)+_haze_fAdd)*_haze_fMul;
const ULONG ulH0 = 255-GetHazeAlpha(ptexFogHaze[0].st.s);
const ULONG ulH1 = 255-GetHazeAlpha(ptexFogHaze[1].st.s);
const ULONG ulH2 = 255-GetHazeAlpha(ptexFogHaze[2].st.s);
const ULONG ulH3 = 255-GetHazeAlpha(ptexFogHaze[3].st.s);
if( (ulH0|ulH1|ulH2|ulH3)<4) return;
if( _colAttMask) { // apply haze color (if not transparent)
COLOR colH;
colH = _colAttMask | RGBAToColor( ulH0,ulH0,ulH0,ulH0); col0 = MulColors( col0, colH);
colH = _colAttMask | RGBAToColor( ulH1,ulH1,ulH1,ulH1); col1 = MulColors( col1, colH);
colH = _colAttMask | RGBAToColor( ulH2,ulH2,ulH2,ulH2); col2 = MulColors( col2, colH);
colH = _colAttMask | RGBAToColor( ulH3,ulH3,ulH3,ulH3); col3 = MulColors( col3, colH);
} else ptexFogHaze[0].st.t = ptexFogHaze[1].st.t = ptexFogHaze[2].st.t = ptexFogHaze[3].st.t = 0;
}
// if fog is active
if( _Particle_bHasFog)
{ // get fog strength at particle position
ptexFogHaze[0].st.s = -vProjected0(3)*_fog_fMulZ;
ptexFogHaze[0].st.t = (vProjected0%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
ptexFogHaze[1].st.s = -vProjected1(3)*_fog_fMulZ;
ptexFogHaze[1].st.t = (vProjected1%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
ptexFogHaze[2].st.s = -vProjected2(3)*_fog_fMulZ;
ptexFogHaze[2].st.t = (vProjected2%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
ptexFogHaze[3].st.s = -vProjected3(3)*_fog_fMulZ;
ptexFogHaze[3].st.t = (vProjected3%_fog_vHDirView+_fog_fAddH)*_fog_fMulH;
const ULONG ulF0 = 255-GetFogAlpha(ptexFogHaze[0]);
const ULONG ulF1 = 255-GetFogAlpha(ptexFogHaze[1]);
const ULONG ulF2 = 255-GetFogAlpha(ptexFogHaze[2]);
const ULONG ulF3 = 255-GetFogAlpha(ptexFogHaze[3]);
if( (ulF0|ulF1|ulF2|ulF3)<4) return;
if( _colAttMask) { // apply fog color (if not transparent)
COLOR colF;
colF = _colAttMask | RGBAToColor( ulF0,ulF0,ulF0,ulF0); col0 = MulColors( col0, colF);
colF = _colAttMask | RGBAToColor( ulF1,ulF1,ulF1,ulF1); col1 = MulColors( col1, colF);
colF = _colAttMask | RGBAToColor( ulF2,ulF2,ulF2,ulF2); col2 = MulColors( col2, colF);
colF = _colAttMask | RGBAToColor( ulF3,ulF3,ulF3,ulF3); col3 = MulColors( col3, colF);
}
}
// keep fog/haze tex coords (if needed)
if( _bTransFogHaze) _atexFogHaze.Push(4);
// add to vertex arrays
GFXVertex *pvtx = _avtxCommon.Push(4);
GFXTexCoord *ptex = _atexCommon.Push(4);
GFXColor *pcol = _acolCommon.Push(4);
// prepare vertices
pvtx[0].x = vProjected0(1); pvtx[0].y = vProjected0(2); pvtx[0].z = vProjected0(3);
pvtx[1].x = vProjected1(1); pvtx[1].y = vProjected1(2); pvtx[1].z = vProjected1(3);
pvtx[2].x = vProjected2(1); pvtx[2].y = vProjected2(2); pvtx[2].z = vProjected2(3);
pvtx[3].x = vProjected3(1); pvtx[3].y = vProjected3(2); pvtx[3].z = vProjected3(3);
// prepare texture coords
ptex[0] = _atex[0];
ptex[1] = _atex[1];
ptex[2] = _atex[2];
ptex[3] = _atex[3];
// prepare colors
const GFXColor glcol0( AdjustColor( col0, _slTexHueShift, _slTexSaturation));
const GFXColor glcol1( AdjustColor( col1, _slTexHueShift, _slTexSaturation));
const GFXColor glcol2( AdjustColor( col2, _slTexHueShift, _slTexSaturation));
const GFXColor glcol3( AdjustColor( col3, _slTexHueShift, _slTexSaturation));
pcol[0] = glcol0;
pcol[1] = glcol1;
pcol[2] = glcol2;
pcol[3] = glcol3;
}
// flushes particle rendering queue (i.e. renders particle on screen)
void Particle_Flush(void)
{
// update stats
const INDEX ctParticles = _avtxCommon.Count()/4;
_sfStats.IncrementCounter( CStatForm::SCI_PARTICLES, ctParticles);
_pGfx->gl_ctParticleTriangles += ctParticles*2;
// determine need for clipping
if( _bNeedsClipping) gfxEnableClipping();
else gfxDisableClipping();
// flush 1st layer
gfxFlushQuads();
// maybe we need to render fog/haze layer
if( _bTransFogHaze)
{ // setup haze/fog color and texture
GFXColor glcolFH;
gfxSetTextureWrapping( GFX_CLAMP, GFX_CLAMP);
if( _Particle_bHasHaze) {
gfxSetTexture( _haze_ulTexture, _haze_tpLocal);
glcolFH.ul.abgr = ByteSwap( AdjustColor( _haze_hp.hp_colColor, _slTexHueShift, _slTexSaturation));
} else {
gfxSetTexture( _fog_ulTexture, _fog_tpLocal);
glcolFH.ul.abgr = ByteSwap( AdjustColor( _fog_fp.fp_colColor, _slTexHueShift, _slTexSaturation));
}
// prepare haze rendering parameters
gfxDisableAlphaTest();
gfxEnableBlend();
gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
gfxDisableDepthWrite();
gfxDepthFunc( GFX_EQUAL); // adjust z-buffer compare
// copy fog/haze texture array to main texture array and set color to fog/haze
const INDEX ctVertices = _atexCommon.Count();
ASSERT( _atexFogHaze.Count()==ctVertices+4);
memcpy( &_atexCommon[0], &_atexFogHaze[0], ctVertices*sizeof(GFXTexCoord));
for( INDEX i=0; i<ctVertices; i++) _acolCommon[i] = glcolFH;
// render it
gfxFlushQuads();
// revert to old settings
gfxEnableAlphaTest();
gfxDisableBlend();
gfxDepthFunc( GFX_LESS_EQUAL);
_ptd->SetAsCurrent(_iFrame);
_pGfx->gl_ctParticleTriangles += ctParticles*2;
}
// all done
gfxResetArrays();
_atexFogHaze.PopAll();
_bNeedsClipping = FALSE;
}
// SORTING ROUTINES
static int qsort_CompareZ( const void *pI0, const void *pI1) {
const INDEX i0 = (*(INDEX*)pI0) *4;
const INDEX i1 = (*(INDEX*)pI1) *4;
const FLOAT fZ0 = _avtxCommon[i0].z;
const FLOAT fZ1 = _avtxCommon[i1].z;
if( fZ0<fZ1) return +1;
else if( fZ0>fZ1) return -1;
else return 0;
}
static int qsort_CompareZ3D( const void *pI0, const void *pI1) {
const INDEX i0 = (*(INDEX*)pI0) *4;
const INDEX i1 = (*(INDEX*)pI1) *4;
const FLOAT fZ0 = (_avtxCommon[i0].z + _avtxCommon[i0+1].z + _avtxCommon[i0+2].z + _avtxCommon[i0+3].z) / 4.0f;
const FLOAT fZ1 = (_avtxCommon[i1].z + _avtxCommon[i1+1].z + _avtxCommon[i1+2].z + _avtxCommon[i1+3].z) / 4.0f;
if( fZ0<fZ1) return +1;
else if( fZ0>fZ1) return -1;
else return 0;
}
// sorts particles by distance
void Particle_Sort( BOOL b3D/*=FALSE*/)
{
INDEX i;
const INDEX ctParticles = _avtxCommon.Count()/4;
if( ctParticles<=0) return; // nothing to do!
// generate sort array
CStaticArray<INDEX> aiIndices;
aiIndices.New(ctParticles);
for( i=0; i<ctParticles; i++) aiIndices[i] = i;
// bubble sort indices by vertex Z coord
if(b3D) qsort( &aiIndices[0], ctParticles, sizeof(INDEX), qsort_CompareZ3D);
else qsort( &aiIndices[0], ctParticles, sizeof(INDEX), qsort_CompareZ);
// generate inverse table
CStaticArray<INDEX> aiInverse;
aiInverse.New(ctParticles);
for( i=0; i<ctParticles; i++) {
const INDEX iOrig = aiIndices[i];
aiInverse[iOrig] = i;
}
// sort vertices by indices
for( i=0; i<ctParticles;) // i is incremented in loop
{ // fetch destination
INDEX &iWhere = aiInverse[i];
ASSERT( iWhere<ctParticles);
// if current is already in place, advance to next index
if( iWhere==i) { i++; continue; }
// swap vertices
Swap( _avtxCommon[iWhere*4+0], _avtxCommon[i*4+0]);
Swap( _avtxCommon[iWhere*4+1], _avtxCommon[i*4+1]);
Swap( _avtxCommon[iWhere*4+2], _avtxCommon[i*4+2]);
Swap( _avtxCommon[iWhere*4+3], _avtxCommon[i*4+3]);
// swap texture coords
Swap( _atexCommon[iWhere*4+0], _atexCommon[i*4+0]);
Swap( _atexCommon[iWhere*4+1], _atexCommon[i*4+1]);
Swap( _atexCommon[iWhere*4+2], _atexCommon[i*4+2]);
Swap( _atexCommon[iWhere*4+3], _atexCommon[i*4+3]);
// swap colors
Swap( _acolCommon[iWhere*4+0], _acolCommon[i*4+0]);
Swap( _acolCommon[iWhere*4+1], _acolCommon[i*4+1]);
Swap( _acolCommon[iWhere*4+2], _acolCommon[i*4+2]);
Swap( _acolCommon[iWhere*4+3], _acolCommon[i*4+3]);
// swap indices
Swap( aiInverse[iWhere], aiInverse[i]);
}
#ifndef NDEBUG
// test to see whether the array is sorted
INDEX *pidx = &aiInverse[0];
GFXVertex4 *pvtx = &_avtxCommon[0];
for( i=0; i<ctParticles-1; i++) {
ASSERT( pidx[i] < pidx[i+1]);
ASSERT( pvtx[i*4].z >= pvtx[(i+1)*4].z);
}
#endif
}