/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */ #include "Engine/StdH.h" #include #include #include #include #include #include #include #include #include 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 _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; iSetAsCurrent(_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( fZ0fZ1) 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( fZ0fZ1) 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 aiIndices; aiIndices.New(ctParticles); for( i=0; i aiInverse; aiInverse.New(ctParticles); for( i=0; i= pvtx[(i+1)*4].z); } #endif }