Paranoia2_original/cl_dll/render/rain.cpp

794 lines
23 KiB
C++

//=========================================
// rain.cpp
// written by BUzer
//=========================================
#include <memory.h>
#include "hud.h"
#include "cl_util.h"
#include "const.h"
#include "parsemsg.h"
#include "entity_types.h"
#include "cdll_int.h"
#include "pm_defs.h"
#include "event_api.h"
#include "custom_alloc.h"
#include "triangleapi.h"
#include "gl_local.h"
#define DRIPSPEED 900 // ñêîðîñòü ïàäåíèÿ êàïåëü (ïèêñ â ñåê)
#define SNOWSPEED 200 // ñêîðîñòü ïàäåíèÿ ñíåæèíîê
#define SNOWFADEDIST 80
#define MAX_RAIN_VERTICES 65536 // snowflakes and waterrings draw as quads
#define MAX_RAIN_INDICES MAX_RAIN_VERTICES * 4
#define MAXDRIPS 40000 // ëèìèò êàïåëü (ìîæíî óâåëè÷èòü ïðè íåîáõîäèìîñòè)
#define MAXFX 20000 // ëèìèò äîïîëíèòåëüíûõ ÷àñòèö (êðóãè ïî âîäå è ò.ï.)
#define MODE_RAIN 0
#define MODE_SNOW 1
#define DRIP_SPRITE_HALFHEIGHT 46
#define DRIP_SPRITE_HALFWIDTH 8
#define SNOW_SPRITE_HALFSIZE 3
#define MAX_RING_HALFSIZE 25 // "ðàäèóñ" êðóãà íà âîäå, äî êîòîðîãî îí ðàçðàñòàåòñÿ çà ñåêóíäó
typedef struct
{
int dripsPerSecond;
float distFromPlayer;
float windX, windY;
float randX, randY;
int weatherMode; // 0 - snow, 1 - rain
float globalHeight;
} rain_properties;
typedef struct cl_drip
{
float birthTime;
float minHeight; // êàïëÿ áóäåò óíè÷òîæåíà íà ýòîé âûñîòå.
Vector origin;
float alpha;
float xDelta; // side speed
float yDelta;
int landInWater;
} cl_drip_t;
typedef struct cl_rainfx
{
float birthTime;
float life;
Vector origin;
float alpha;
} cl_rainfx_t;
void WaterLandingEffect(cl_drip *drip);
rain_properties Rain;
MemBlock<cl_drip> g_dripsArray( MAXDRIPS );
MemBlock<cl_rainfx> g_fxArray( MAXFX );
cvar_t *cl_draw_rain = NULL;
cvar_t *cl_debug_rain = NULL;
double rain_curtime; // current time
double rain_oldtime; // last time we have updated drips
double rain_timedelta; // difference between old time and current time
double rain_nextspawntime; // when the next drip should be spawned
int dripcounter = 0;
int fxcounter = 0;
static Vector rain_mins, rain_maxs; // for vis culling
static Vector m_vertexarray[MAX_RAIN_VERTICES];
static byte m_colorarray[MAX_RAIN_VERTICES][4];
static Vector2D m_coordsarray[MAX_RAIN_VERTICES];
static word m_indexarray[MAX_RAIN_INDICES];
static int m_iNumVerts, m_iNumIndex;
/*
=================================
ProcessRain
Ïåðåìåùàåò ñóùåñòâóþùèå îáúåêòû, óäàëÿåò èõ ïðè íàäîáíîñòè,
è, åñëè äîæäü âêëþ÷åí, ñîçäàåò íîâûå.
Äîëæíà âûçûâàòüñÿ êàæäûé êàäð.
=================================
*/
void ProcessRain( void )
{
rain_oldtime = rain_curtime; // save old time
rain_curtime = GET_CLIENT_TIME();
rain_timedelta = rain_curtime - rain_oldtime;
// first frame
if( rain_oldtime == 0 )
{
// fix first frame bug with nextspawntime
rain_nextspawntime = rain_curtime;
return;
}
if(( Rain.dripsPerSecond == 0 && g_dripsArray.IsClear( )) || rain_timedelta > 0.1f )
{
rain_timedelta = min( rain_timedelta, 0.1f );
// keep nextspawntime correct
rain_nextspawntime = rain_curtime;
return;
}
if( rain_timedelta == 0 ) return; // not in pause
double timeBetweenDrips = 1.0 / (double)Rain.dripsPerSecond;
if( !g_dripsArray.StartPass( ))
{
Rain.dripsPerSecond = 0; // disable rain
ALERT( at_error, "rain: failed to allocate memory block!\n" );
return;
}
cl_drip *curDrip = g_dripsArray.GetCurrent();
// õðàíåíèå îòëàäî÷íîé èíôîðìàöèè
float debug_lifetime = 0;
int debug_howmany = 0;
int debug_attempted = 0;
int debug_dropped = 0;
ClearBounds( rain_mins, rain_maxs );
while( curDrip != NULL ) // go through list
{
if( Rain.weatherMode == MODE_RAIN )
curDrip->origin.z -= rain_timedelta * DRIPSPEED;
else if( Rain.weatherMode == MODE_SNOW )
curDrip->origin.z -= rain_timedelta * SNOWSPEED;
else return;
curDrip->origin.x += rain_timedelta * curDrip->xDelta;
curDrip->origin.y += rain_timedelta * curDrip->yDelta;
#if 1
// unrolled version of AddPointToBounds (perf)
if( curDrip->origin[0] < rain_mins[0] ) rain_mins[0] = curDrip->origin[0];
if( curDrip->origin[0] > rain_maxs[0] ) rain_maxs[0] = curDrip->origin[0];
if( curDrip->origin[1] < rain_mins[1] ) rain_mins[1] = curDrip->origin[1];
if( curDrip->origin[1] > rain_maxs[1] ) rain_maxs[1] = curDrip->origin[1];
if( curDrip->origin[2] < rain_mins[2] ) rain_mins[2] = curDrip->origin[2];
if( curDrip->origin[2] > rain_maxs[2] ) rain_maxs[2] = curDrip->origin[2];
#else
AddPointToBounds( curDrip->origin, rain_mins, rain_maxs );
#endif
// remove drip if its origin lower than minHeight
if( curDrip->origin.z < curDrip->minHeight )
{
if( curDrip->landInWater )
WaterLandingEffect( curDrip ); // create water rings
if( cl_debug_rain->value )
{
debug_lifetime += (rain_curtime - curDrip->birthTime);
debug_howmany++;
}
g_dripsArray.DeleteCurrent();
dripcounter--;
}
else
g_dripsArray.MoveNext();
curDrip = g_dripsArray.GetCurrent();
}
int maxDelta; // maximum height randomize distance
float falltime;
if( Rain.weatherMode == MODE_RAIN )
{
maxDelta = DRIPSPEED * rain_timedelta; // for rain
falltime = (Rain.globalHeight + 4096) / DRIPSPEED;
}
else
{
maxDelta = SNOWSPEED * rain_timedelta; // for snow
falltime = (Rain.globalHeight + 4096) / SNOWSPEED;
}
while( rain_nextspawntime < rain_curtime )
{
rain_nextspawntime += timeBetweenDrips;
if( cl_debug_rain->value )
debug_attempted++;
// check for overflow
if( dripcounter < MAXDRIPS )
{
float deathHeight;
vec3_t vecStart, vecEnd;
vecStart[0] = RANDOM_FLOAT( GetVieworg().x - Rain.distFromPlayer, GetVieworg().x + Rain.distFromPlayer );
vecStart[1] = RANDOM_FLOAT( GetVieworg().y - Rain.distFromPlayer, GetVieworg().y + Rain.distFromPlayer );
vecStart[2] = Rain.globalHeight;
float xDelta = Rain.windX + RANDOM_FLOAT( Rain.randX * -1, Rain.randX );
float yDelta = Rain.windY + RANDOM_FLOAT( Rain.randY * -1, Rain.randY );
// find a point at bottom of map
vecEnd[0] = falltime * xDelta;
vecEnd[1] = falltime * yDelta;
vecEnd[2] = -4096;
pmtrace_t pmtrace;
gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
gEngfuncs.pEventAPI->EV_PlayerTrace( vecStart, vecStart + vecEnd, PM_STUDIO_IGNORE|PM_CUSTOM_IGNORE, -1, &pmtrace );
if( pmtrace.startsolid || pmtrace.allsolid )
{
if( cl_debug_rain->value )
debug_dropped++;
continue; // drip cannot be placed
}
// falling to water?
int contents = gEngfuncs.PM_PointContents( pmtrace.endpos, NULL );
if( contents == CONTENTS_WATER )
{
int waterEntity = WATER_ENTITY( pmtrace.endpos );
if( waterEntity > 0 )
{
cl_entity_t *pwater = gEngfuncs.GetEntityByIndex( waterEntity );
if( pwater && ( pwater->model != NULL ))
{
deathHeight = pwater->curstate.maxs.z - 1.0f;
if( !Mod_BoxVisible( pwater->curstate.mins, pwater->curstate.maxs, Mod_GetCurrentVis( )))
contents = CONTENTS_EMPTY; // not error, just water out of PVS
}
else
{
ALERT( at_error, "rain: can't get water entity\n" );
continue;
}
}
else
{
ALERT( at_error, "rain: water is not func_water entity\n" );
continue;
}
}
else
{
deathHeight = pmtrace.endpos.z;
}
// just in case..
if( deathHeight > vecStart.z )
{
ALERT( at_error, "rain: can't create drip in water\n");
continue;
}
cl_drip *newClDrip = g_dripsArray.Allocate();
if( !newClDrip )
{
Rain.dripsPerSecond = 0; // disable rain
ALERT( at_error, "rain: failed to allocate object!\n" );
return;
}
vecStart.z -= RANDOM_FLOAT( 0, maxDelta ); // randomize a bit
newClDrip->alpha = RANDOM_FLOAT( 0.12f, 0.2f );
newClDrip->origin = vecStart;
newClDrip->xDelta = xDelta;
newClDrip->yDelta = yDelta;
newClDrip->birthTime = rain_curtime; // store time when it was spawned
newClDrip->minHeight = deathHeight;
if( contents == CONTENTS_WATER )
newClDrip->landInWater = 1;
else
newClDrip->landInWater = 0;
// add to first place in chain
dripcounter++;
}
else
{
ALERT( at_error, "rain: Drip limit overflow!\n" );
return;
}
}
if( cl_debug_rain->value )
{
// print debug info
gEngfuncs.Con_NPrintf( 1, "rain info: Drips exist: %i\n", dripcounter );
gEngfuncs.Con_NPrintf( 2, "rain info: FX's exist: %i\n", fxcounter );
gEngfuncs.Con_NPrintf( 3, "rain info: Attempted/Dropped: %i, %i\n", debug_attempted, debug_dropped );
if( debug_howmany )
{
float ave = debug_lifetime / (float)debug_howmany;
gEngfuncs.Con_NPrintf( 4, "rain info: Average drip life time: %f\n", ave );
}
else
gEngfuncs.Con_NPrintf( 4, "rain info: Average drip life time: --\n" );
}
}
/*
=================================
WaterLandingEffect
ñîçäàåò êðóã íà âîäíîé ïîâåðõíîñòè
=================================
*/
void WaterLandingEffect( cl_drip *drip )
{
if( fxcounter >= MAXFX )
{
ALERT( at_error, "rain: FX limit overflow!\n" );
return;
}
cl_rainfx *newFX = g_fxArray.Allocate();
if( !newFX )
{
ALERT( at_error, "rain: failed to allocate FX object!\n" );
return;
}
newFX->alpha = RANDOM_FLOAT( 0.6f, 0.9f );
newFX->origin = drip->origin;
newFX->origin.z = drip->minHeight - 1; // correct position
newFX->birthTime = GET_CLIENT_TIME();
newFX->life = RANDOM_FLOAT( 0.7f, 1.0f );
// add to first place in chain
fxcounter++;
}
/*
=================================
ProcessFXObjects
óäàëÿåò FX îáúåêòû, ó êîòîðûõ âûøåë ñðîê æèçíè
Êàæäûé êàäð âûçûâàåòñÿ ïåðåä ProcessRain
=================================
*/
void ProcessFXObjects( void )
{
if( !g_fxArray.StartPass( ))
{
Rain.dripsPerSecond = 0; // disable rain
ALERT( at_error, "rain: failed to allocate FX object!\n" );
return;
}
cl_rainfx* curFX = g_fxArray.GetCurrent();
while( curFX != NULL ) // go through FX objects list
{
// delete current?
if(( curFX->birthTime + curFX->life ) < rain_curtime )
{
g_fxArray.DeleteCurrent();
fxcounter--;
}
else
g_fxArray.MoveNext();
curFX = g_fxArray.GetCurrent();
}
}
/*
=================================
ResetRain
î÷èùàåò ïàìÿòü, óäàëÿÿ âñå îáúåêòû.
=================================
*/
void ResetRain( void )
{
// delete all drips
g_dripsArray.Clear();
g_fxArray.Clear();
dripcounter = 0;
fxcounter = 0;
InitRain();
}
/*
=================================
DrawRain
Ðèñîâàíèå êàïåëü è ñíåæèíîê.
=================================
*/
void DrawRain( void )
{
if( g_dripsArray.IsClear( ))
return; // no drips to draw
if( !Mod_BoxVisible( rain_mins, rain_maxs, Mod_GetCurrentVis( )))
return; // rain volume is invisible
HSPRITE hsprTexture;
if( Rain.weatherMode == MODE_RAIN )
hsprTexture = LoadSprite("sprites/hi_rain.spr" ); // load rain sprite
else if( Rain.weatherMode == MODE_SNOW )
hsprTexture = LoadSprite( "sprites/snowflake.spr" ); // load snow sprite
else hsprTexture = 0;
if( !hsprTexture ) return;
GL_SelectTexture( GL_TEXTURE0 ); // keep texcoords at 0-th unit
// usual triapi stuff
const model_s *pTexture = gEngfuncs.GetSpritePointer( hsprTexture );
if( !gEngfuncs.pTriAPI->SpriteTexture(( struct model_s *)pTexture, 0 ))
return;
gEngfuncs.pTriAPI->RenderMode( kRenderTransAdd );
float visibleHeight = Rain.globalHeight - SNOWFADEDIST;
m_iNumVerts = m_iNumIndex = 0;
// go through drips list
g_dripsArray.StartPass();
cl_drip *Drip = g_dripsArray.GetCurrent();
if( Rain.weatherMode == MODE_RAIN )
{
while( Drip != NULL )
{
// cull invisible drips
if( R_CullSphere( Drip->origin, SNOW_SPRITE_HALFSIZE + 1 ))
{
g_dripsArray.MoveNext();
Drip = g_dripsArray.GetCurrent();
continue;
}
if(( m_iNumVerts + 3 ) >= MAX_RAIN_VERTICES )
{
// this should never happens because we used vertex array only at 75%
ALERT( at_error, "Too many drips specified\n" );
break;
}
Vector2D toPlayer;
toPlayer.x = GetVieworg().x - Drip->origin[0];
toPlayer.y = GetVieworg().y - Drip->origin[1];
toPlayer = toPlayer.Normalize();
toPlayer.x *= DRIP_SPRITE_HALFWIDTH;
toPlayer.y *= DRIP_SPRITE_HALFWIDTH;
float shiftX = (Drip->xDelta / DRIPSPEED) * DRIP_SPRITE_HALFHEIGHT;
float shiftY = (Drip->yDelta / DRIPSPEED) * DRIP_SPRITE_HALFHEIGHT;
byte alpha = Drip->alpha * 255;
m_coordsarray[m_iNumVerts].x = 0.0f;
m_coordsarray[m_iNumVerts].y = 0.0f;
m_colorarray[m_iNumVerts][0] = 255;
m_colorarray[m_iNumVerts][1] = 255;
m_colorarray[m_iNumVerts][2] = 255;
m_colorarray[m_iNumVerts][3] = alpha;
m_vertexarray[m_iNumVerts] = Vector( Drip->origin.x - toPlayer.y - shiftX, Drip->origin.y + toPlayer.x - shiftY, Drip->origin.z + DRIP_SPRITE_HALFHEIGHT );
m_indexarray[m_iNumIndex++] = m_iNumVerts++;
m_coordsarray[m_iNumVerts].x = 0.5f;
m_coordsarray[m_iNumVerts].y = 1.0f;
m_colorarray[m_iNumVerts][0] = 255;
m_colorarray[m_iNumVerts][1] = 255;
m_colorarray[m_iNumVerts][2] = 255;
m_colorarray[m_iNumVerts][3] = alpha;
m_vertexarray[m_iNumVerts] = Vector( Drip->origin.x + shiftX, Drip->origin.y + shiftY, Drip->origin.z - DRIP_SPRITE_HALFHEIGHT );
m_indexarray[m_iNumIndex++] = m_iNumVerts++;
// set right top corner
m_coordsarray[m_iNumVerts].x = 1.0f;
m_coordsarray[m_iNumVerts].y = 0.0f;
m_colorarray[m_iNumVerts][0] = 255;
m_colorarray[m_iNumVerts][1] = 255;
m_colorarray[m_iNumVerts][2] = 255;
m_colorarray[m_iNumVerts][3] = alpha;
m_vertexarray[m_iNumVerts] = Vector( Drip->origin.x + toPlayer.y - shiftX, Drip->origin.y - toPlayer.x - shiftY, Drip->origin.z + DRIP_SPRITE_HALFHEIGHT );
m_indexarray[m_iNumIndex++] = m_iNumVerts++;
g_dripsArray.MoveNext();
Drip = g_dripsArray.GetCurrent();
}
pglEnableClientState( GL_VERTEX_ARRAY );
pglVertexPointer( 3, GL_FLOAT, 0, m_vertexarray );
pglEnableClientState( GL_TEXTURE_COORD_ARRAY );
pglTexCoordPointer( 2, GL_FLOAT, 0, m_coordsarray );
pglEnableClientState( GL_COLOR_ARRAY );
pglColorPointer( 4, GL_UNSIGNED_BYTE, 0, m_colorarray );
if( GL_Support( R_DRAW_RANGEELEMENTS_EXT ))
pglDrawRangeElementsEXT( GL_TRIANGLES, 0, m_iNumVerts - 1, m_iNumIndex, GL_UNSIGNED_SHORT, m_indexarray );
else pglDrawElements( GL_TRIANGLES, m_iNumIndex, GL_UNSIGNED_SHORT, m_indexarray );
r_stats.c_total_tris += (m_iNumIndex / 3);
pglDisableClientState( GL_TEXTURE_COORD_ARRAY );
pglDisableClientState( GL_VERTEX_ARRAY );
pglDisableClientState( GL_COLOR_ARRAY );
}
else if( Rain.weatherMode == MODE_SNOW )
{
while( Drip != NULL )
{
// cull invisible flakes
if( R_CullSphere( Drip->origin, SNOW_SPRITE_HALFSIZE + 1 ))
{
g_dripsArray.MoveNext();
Drip = g_dripsArray.GetCurrent();
continue;
}
if(( m_iNumVerts + 4 ) >= MAX_RAIN_VERTICES )
{
ALERT( at_error, "Too many snowflakes specified\n" );
break;
}
// apply start fading effect
byte alpha;
if( Drip->origin.z <= visibleHeight )
alpha = Drip->alpha * 255;
else alpha = ((( Rain.globalHeight - Drip->origin.z ) / (float)SNOWFADEDIST ) * Drip->alpha) * 255;
// set left bottom corner
m_coordsarray[m_iNumVerts].x = 0.0f;
m_coordsarray[m_iNumVerts].y = 1.0f;
m_colorarray[m_iNumVerts][0] = 255;
m_colorarray[m_iNumVerts][1] = 255;
m_colorarray[m_iNumVerts][2] = 255;
m_colorarray[m_iNumVerts][3] = alpha;
m_vertexarray[m_iNumVerts] = Drip->origin + GetVLeft() * -SNOW_SPRITE_HALFSIZE + GetVUp() * -SNOW_SPRITE_HALFSIZE;
m_indexarray[m_iNumIndex++] = m_iNumVerts++;
// set left top corner
m_coordsarray[m_iNumVerts].x = 0.0f;
m_coordsarray[m_iNumVerts].y = 0.0f;
m_colorarray[m_iNumVerts][0] = 255;
m_colorarray[m_iNumVerts][1] = 255;
m_colorarray[m_iNumVerts][2] = 255;
m_colorarray[m_iNumVerts][3] = alpha;
m_vertexarray[m_iNumVerts] = Drip->origin + GetVLeft() * -SNOW_SPRITE_HALFSIZE + GetVUp() * SNOW_SPRITE_HALFSIZE;
m_indexarray[m_iNumIndex++] = m_iNumVerts++;
// set right top corner
m_coordsarray[m_iNumVerts].x = 1.0f;
m_coordsarray[m_iNumVerts].y = 0.0f;
m_colorarray[m_iNumVerts][0] = 255;
m_colorarray[m_iNumVerts][1] = 255;
m_colorarray[m_iNumVerts][2] = 255;
m_colorarray[m_iNumVerts][3] = alpha;
m_vertexarray[m_iNumVerts] = Drip->origin + GetVLeft() * SNOW_SPRITE_HALFSIZE + GetVUp() * SNOW_SPRITE_HALFSIZE;
m_indexarray[m_iNumIndex++] = m_iNumVerts++;
// set right bottom corner
m_coordsarray[m_iNumVerts].x = 1.0f;
m_coordsarray[m_iNumVerts].y = 1.0f;
m_colorarray[m_iNumVerts][0] = 255;
m_colorarray[m_iNumVerts][1] = 255;
m_colorarray[m_iNumVerts][2] = 255;
m_colorarray[m_iNumVerts][3] = alpha;
m_vertexarray[m_iNumVerts] = Drip->origin + GetVLeft() * SNOW_SPRITE_HALFSIZE + GetVUp() * -SNOW_SPRITE_HALFSIZE;
m_indexarray[m_iNumIndex++] = m_iNumVerts++;
g_dripsArray.MoveNext();
Drip = g_dripsArray.GetCurrent();
}
pglEnableClientState( GL_VERTEX_ARRAY );
pglVertexPointer( 3, GL_FLOAT, 0, m_vertexarray );
pglEnableClientState( GL_TEXTURE_COORD_ARRAY );
pglTexCoordPointer( 2, GL_FLOAT, 0, m_coordsarray );
pglEnableClientState( GL_COLOR_ARRAY );
pglColorPointer( 4, GL_UNSIGNED_BYTE, 0, m_colorarray );
if( GL_Support( R_DRAW_RANGEELEMENTS_EXT ))
pglDrawRangeElementsEXT( GL_QUADS, 0, m_iNumVerts - 1, m_iNumIndex, GL_UNSIGNED_SHORT, m_indexarray );
else pglDrawElements( GL_QUADS, m_iNumIndex, GL_UNSIGNED_SHORT, m_indexarray );
r_stats.c_total_tris += (m_iNumIndex / 4); // FIXME: this is correct?
pglDisableClientState( GL_TEXTURE_COORD_ARRAY );
pglDisableClientState( GL_VERTEX_ARRAY );
pglDisableClientState( GL_COLOR_ARRAY );
}
}
/* rain
=================================
DrawFXObjects
Ðèñîâàíèå âîäÿíûõ êðóãîâ
=================================
*/
void DrawFXObjects( void )
{
if( g_fxArray.IsClear( ))
return; // no objects to draw
HSPRITE hsprTexture;
hsprTexture = LoadSprite( "sprites/waterring.spr" ); // load water ring sprite
if( !hsprTexture ) return;
const model_s *pTexture = gEngfuncs.GetSpritePointer( hsprTexture );
if( !gEngfuncs.pTriAPI->SpriteTexture(( struct model_s *)pTexture, 0 ))
return;
gEngfuncs.pTriAPI->RenderMode( kRenderTransAdd );
gEngfuncs.pTriAPI->CullFace( TRI_NONE ); // because we also want to view water rings underwater
// go through objects list
g_fxArray.StartPass();
cl_rainfx *curFX = g_fxArray.GetCurrent();
m_iNumVerts = m_iNumIndex = 0;
while( curFX != NULL )
{
if(( m_iNumVerts + 4 ) >= MAX_RAIN_VERTICES )
{
ALERT( at_error, "Too many water rings\n" );
break;
}
// cull invisible rings
if( R_CullSphere( curFX->origin, MAX_RING_HALFSIZE + 1 ))
{
g_fxArray.MoveNext();
curFX = g_fxArray.GetCurrent();
continue;
}
// fadeout
byte alpha = (((curFX->birthTime + curFX->life - rain_curtime) / curFX->life) * curFX->alpha) * 255;
float size = (rain_curtime - curFX->birthTime) * MAX_RING_HALFSIZE;
m_coordsarray[m_iNumVerts].x = 0.0f;
m_coordsarray[m_iNumVerts].y = 0.0f;
m_colorarray[m_iNumVerts][0] = 255;
m_colorarray[m_iNumVerts][1] = 255;
m_colorarray[m_iNumVerts][2] = 255;
m_colorarray[m_iNumVerts][3] = alpha;
m_vertexarray[m_iNumVerts] = Vector( curFX->origin.x - size, curFX->origin.y - size, curFX->origin.z );
m_indexarray[m_iNumIndex++] = m_iNumVerts++;
m_coordsarray[m_iNumVerts].x = 0.0f;
m_coordsarray[m_iNumVerts].y = 1.0f;
m_colorarray[m_iNumVerts][0] = 255;
m_colorarray[m_iNumVerts][1] = 255;
m_colorarray[m_iNumVerts][2] = 255;
m_colorarray[m_iNumVerts][3] = alpha;
m_vertexarray[m_iNumVerts] = Vector( curFX->origin.x - size, curFX->origin.y + size, curFX->origin.z );
m_indexarray[m_iNumIndex++] = m_iNumVerts++;
m_coordsarray[m_iNumVerts].x = 1.0f;
m_coordsarray[m_iNumVerts].y = 1.0f;
m_colorarray[m_iNumVerts][0] = 255;
m_colorarray[m_iNumVerts][1] = 255;
m_colorarray[m_iNumVerts][2] = 255;
m_colorarray[m_iNumVerts][3] = alpha;
m_vertexarray[m_iNumVerts] = Vector( curFX->origin.x + size, curFX->origin.y + size, curFX->origin.z );
m_indexarray[m_iNumIndex++] = m_iNumVerts++;
m_coordsarray[m_iNumVerts].x = 1.0f;
m_coordsarray[m_iNumVerts].y = 0.0f;
m_colorarray[m_iNumVerts][0] = 255;
m_colorarray[m_iNumVerts][1] = 255;
m_colorarray[m_iNumVerts][2] = 255;
m_colorarray[m_iNumVerts][3] = alpha;
m_vertexarray[m_iNumVerts] = Vector( curFX->origin.x + size, curFX->origin.y - size, curFX->origin.z );
m_indexarray[m_iNumIndex++] = m_iNumVerts++;
g_fxArray.MoveNext();
curFX = g_fxArray.GetCurrent();
}
pglEnableClientState( GL_VERTEX_ARRAY );
pglVertexPointer( 3, GL_FLOAT, 0, m_vertexarray );
pglEnableClientState( GL_TEXTURE_COORD_ARRAY );
pglTexCoordPointer( 2, GL_FLOAT, 0, m_coordsarray );
pglEnableClientState( GL_COLOR_ARRAY );
pglColorPointer( 4, GL_UNSIGNED_BYTE, 0, m_colorarray );
if( GL_Support( R_DRAW_RANGEELEMENTS_EXT ))
pglDrawRangeElementsEXT( GL_QUADS, 0, m_iNumVerts - 1, m_iNumIndex, GL_UNSIGNED_SHORT, m_indexarray );
else pglDrawElements( GL_QUADS, m_iNumIndex, GL_UNSIGNED_SHORT, m_indexarray );
r_stats.c_total_tris += (m_iNumIndex / 4); // FIXME: this is correct?
pglDisableClientState( GL_TEXTURE_COORD_ARRAY );
pglDisableClientState( GL_VERTEX_ARRAY );
pglDisableClientState( GL_COLOR_ARRAY );
gEngfuncs.pTriAPI->CullFace( TRI_FRONT );
}
/*
=================================
InitRain
Èíèöèàëèçèðóåò âñå ïåðåìåííûå íóëåâûìè çíà÷åíèÿìè.
=================================
*/
void InitRain( void )
{
cl_draw_rain = CVAR_REGISTER( "cl_draw_rain", "1", FCVAR_ARCHIVE );
cl_debug_rain = CVAR_REGISTER( "cl_debug_rain", "0", 0 );
memset( &Rain, 0, sizeof( Rain ));
rain_oldtime = 0;
rain_curtime = 0;
rain_nextspawntime = 0;
}
/*
=================================
ParseRain
=================================
*/
void ParseRain( void )
{
Rain.dripsPerSecond = READ_SHORT();
Rain.distFromPlayer = READ_COORD();
Rain.windX = READ_COORD();
Rain.windY = READ_COORD();
Rain.randX = READ_COORD();
Rain.randY = READ_COORD();
Rain.weatherMode = READ_SHORT();
Rain.globalHeight = READ_COORD();
}
/*
=================================
R_DrawWeather
=================================
*/
void R_DrawWeather( void )
{
if( !CVAR_TO_BOOL( cl_draw_rain ))
return;
if( FBitSet( RI->params, ( RP_ENVVIEW|RP_SKYVIEW )))
return;
GL_CleanupDrawState();
GL_AlphaTest( GL_FALSE );
GL_DepthMask( GL_FALSE );
ProcessRain();
ProcessFXObjects();
DrawRain();
DrawFXObjects();
}