thug/Code/Gfx/XBox/p_nxweather.cpp
2016-02-14 08:39:12 +11:00

1087 lines
33 KiB
C++

//****************************************************************************
//* MODULE: Gfx
//* FILENAME: p_NxWeather.cpp
//* OWNER: Dave Cowling
//* CREATION DATE: 6/19/2003
//****************************************************************************
#include <core/defines.h>
#include "gfx/xbox/nx/render.h"
#include <gfx/NxTexMan.h>
#include <gfx/xbox/p_nxtexture.h>
#include "gfx/xbox/nx/mesh.h"
#include "gfx/xbox/p_nxweather.h"
#include <sk/modules/skate/skate.h>
#include <sk/objects/skater.h>
#include <sk/engine/feeler.h>
#include <engine/SuperSector.h>
#include "gfx/nx.h"
#include "gfx/xbox/nx/sprite.h"
#define FEELER_START 50000.0f
#define NUM_LINES_PER_BATCH 80
#define NUM_SPRITES_PER_BATCH 80
#define PRECISION_SHIFT 4
#define RENDER_DIST 16
#define SEQ_START 411 // Between 1 and 4095.
#define SEQ_MASK 0x0240
unsigned char grid_bytes[70*1024];
//1-3: 0x03
//1-7: 0x06
//1-15: 0x0C
//1-31: 0x14
//1-63: 0x30
//1-127: 0x60
//1-255: 0xB8
//1-511: 0x0110
//1-1023: 0x0240
//1-2047: 0x0500
//1-4095: 0x0CA0
//1-8191: 0x1B00
//1-16383: 0x3500
//1-32767: 0x6000
//1-65535: 0xB400
//0x00012000
//0x00020400
//0x00072000
//0x00090000
//0x00140000
//0x00300000
//0x00400000
//0x00D80000
//0x01200000
//0x03880000
//0x07200000
//0x09000000
//0x14000000
//0x32800000
//0x48000000
//0xA3000000
namespace Nx
{
/******************************************************************/
/* */
/* */
/******************************************************************/
CXboxWeather::CXboxWeather()
{
mp_roof_height_index = NULL;
mp_rain_texture = NULL;
m_rain_blend = NxXbox::GetBlendMode( CRCD( 0xa86285a1, "fixadd" )) | 0x80000000UL;
m_splash_blend = NxXbox::GetBlendMode( CRCD( 0xa86285a1, "fixadd" )) | 0x80000000UL;
m_snow_blend = NxXbox::GetBlendMode( CRCD( 0xa86285a1, "fixadd" )) | 0x80000000UL;
m_rain_rate = 0.0f;
m_splash_rate = 0.0f;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CXboxWeather::~CXboxWeather()
{
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CXboxWeather::plat_update_grid( void )
{
m_system_active = false;
// Get super sector manager.
SSec::Manager *ss_man;
Mth::Line line;
ss_man = Nx::CEngine::sGetNearestSuperSectorManager( line ); // Line is ignored, 1st manager is returned.
if ( !ss_man ) return;
// Calculate the size of the world in cels.
int min_x = ( ((int)ss_man->GetWorldBBox()->GetMin()[X]) / WEATHER_CEL_SIZE ) - 1;
int max_x = ( ((int)ss_man->GetWorldBBox()->GetMax()[X]) / WEATHER_CEL_SIZE ) + 1;
int min_z = ( ((int)ss_man->GetWorldBBox()->GetMin()[Z]) / WEATHER_CEL_SIZE ) - 1;
int max_z = ( ((int)ss_man->GetWorldBBox()->GetMax()[Z]) / WEATHER_CEL_SIZE ) + 1;
// Define a maximum...
if(( max_x - min_x ) > 350 )
{
int wid = ( max_x - min_x );
int excess = ( wid - 350 );
min_x += ( excess / 2 );
max_x -= ( excess / 2 );
}
if(( max_z - min_z ) > 300 )
{
int wid = ( max_z - min_z );
int excess = ( wid - 300 );
min_z += ( excess / 2 );
max_z -= ( excess / 2 );
}
// This is the actual width.
m_width = ( max_x - min_x ) + 1;
m_height = ( max_z - min_z ) + 1;
m_min_x = (float)( min_x * WEATHER_CEL_SIZE );
m_min_z = (float)( min_z * WEATHER_CEL_SIZE );
// Temporary buffer for the raw array.
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
unsigned char * p8 = new unsigned char[m_width*m_height];
Mem::Manager::sHandle().PopContext();
// Go through and get the height for each cel corner.
CFeeler feeler;
int num_heights = 0;
int xx, zz;
for ( zz = min_z; zz <= max_z; zz++ )
{
for ( xx = min_x; xx <= max_x; xx++ )
{
// The cel to fill.
int cel = ( ( zz - min_z ) * m_width ) + ( xx - min_x );
// The position to check.
Mth::Vector pos;
pos[X] = (float)( xx * WEATHER_CEL_SIZE );
pos[Y] = FEELER_START;
pos[Z] = (float)( zz * WEATHER_CEL_SIZE );
pos[W] = 1.0f;
feeler.SetStart( pos );
pos[Y] = -FEELER_START;
feeler.SetEnd( pos );
// Get the y position.
sint32 y;
if ( feeler.GetCollision( false, false ) ) // No movables, nearest collision.
{
y = (sint32)( feeler.GetPoint()[Y] * SUB_INCH_PRECISION );
}
else
{
y = (sint32)( FEELER_START * SUB_INCH_PRECISION );
}
// See if a close enough y pos already exists.
int found = -1;
for ( int lp = 0; lp < num_heights; lp++ )
{
if ( abs( ( m_roof_height[lp] - y ) ) < HEIGHT_TOLERANCE )
{
found = lp;
break;
}
}
// Fill in the cel.
if ( found != -1 )
{
// Existing height.
p8[cel] = found;
}
else
{
// New height.
p8[cel] = num_heights;
m_roof_height[num_heights] = y;
num_heights++;
}
}
}
// Work out highest height for each cel.
for ( zz = min_z; zz <= max_z; zz++ )
{
for ( xx = min_x; xx <= max_x; xx++ )
{
// The cel to fill.
int cel = ( ( zz - min_z ) * m_width ) + ( xx - min_x );
int celx = ( ( zz - min_z ) * m_width ) + ( ( xx + 1 ) - min_x );
int celz = ( ( ( zz + 1 ) - min_z ) * m_width ) + ( xx - min_x );
int celxz = ( ( ( zz + 1 ) - min_z ) * m_width ) + ( ( xx + 1 ) - min_x );
if ( m_roof_height[p8[celx]] > m_roof_height[p8[cel]] ) p8[cel] = p8[celx];
if ( m_roof_height[p8[celz]] > m_roof_height[p8[cel]] ) p8[cel] = p8[celz];
if ( m_roof_height[p8[celxz]] > m_roof_height[p8[cel]] ) p8[cel] = p8[celxz];
}
}
// Create a sparse array.
mp_roof_row = (sRowEntry *)grid_bytes;
mp_roof_height_index = (unsigned char *)&mp_roof_row[m_height];
// 0 = offset
// 1 = width
// 2 = index
unsigned short index = 0;
for ( zz = 0; zz <= m_height; zz++ )
{
unsigned short start = 0;
unsigned short end = m_width - 1;
// Scan to find the start.
bool start_set = false;
for ( xx = 0; xx < m_width; xx++ )
{
int cel = ( zz * m_width ) + xx;
if ( m_roof_height[p8[cel]] != (sint32)( FEELER_START * SUB_INCH_PRECISION ) )
{
if ( !start_set )
{
// Set start value.
start = xx;
start_set = true;
}
else
{
// Set end value.
end = xx;
}
}
}
// Copy data & set row entry.
if ( start < end )
{
mp_roof_row[zz].start = start;
mp_roof_row[zz].end = end;
mp_roof_row[zz].index = index;
for ( xx = start; xx <= end ; xx++ )
{
int cel = ( zz * m_width ) + xx;
mp_roof_height_index[index] = p8[cel];
index++;
}
}
else
{
// Row doesn't exist.
mp_roof_row[zz].start = 16384;
mp_roof_row[zz].end = 0;
mp_roof_row[zz].index = 0;
}
}
delete p8;
int new_size = ( m_height * 6 ) + index;
Dbg_Message( "Grid Size: Old: %d New: %d Num Heights: %d\n", ( m_width * m_height ), new_size, num_heights );
// Set all drip time counters to 255.
// 255 means the drop won't be rendered.
// Setting to anything other than 255 will mean that it will increment to 255 and stop.
for ( int lp = 0; lp < ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ); lp++ )
{
m_drop_time[lp] = 255;
m_x_offset[lp] = Mth::Rnd( 255 );
m_z_offset[lp] = Mth::Rnd( 255 );
}
m_active_drops = 0;
m_seq = SEQ_START;
// Get the texture.
Nx::CTexture* p_texture;
Nx::CXboxTexture* p_xbox_texture;
mp_rain_texture = NULL;
// Set Rain Texture.
p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( CRCD( 0x45c7eb0f,"splash" ));
p_xbox_texture = static_cast<Nx::CXboxTexture*>( p_texture );
if( p_xbox_texture )
{
mp_rain_texture = p_xbox_texture->GetEngineTexture();
}
// Set Snow Texture.
p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( CRCD( 0xc97c5a4c,"snow" ));
p_xbox_texture = static_cast<Nx::CXboxTexture*>( p_texture );
if( p_xbox_texture )
{
mp_snow_texture = p_xbox_texture->GetEngineTexture();
}
m_system_active = true;
// Zero out the splashes.
for( int sp = 0; sp < NUM_SPLASH_ACTIVE; sp++ )
{
m_splash_current_life[sp] = 0;
}
m_current_splash = 0;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CXboxWeather::plat_process( float delta_time )
{
if( !m_system_active ) return;
if( m_raining )
{
// It's raining.
float rate = m_rain_drops_per_frame;
if( rate > (float)(( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_rain_frames ))
{
rate = (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_rain_frames );
}
float last = m_rain_rate;
m_rain_rate += rate;
int new_drops = (int)m_rain_rate - (int)last;
for( int lp = 0; lp < new_drops; lp++ )
{
// If this cel is not inactive, we caught up with ourselves.
if ( m_drop_time[m_seq] != 255 ) break;
// Setup the drop.
m_drop_time[m_seq] = ( 254 - m_rain_frames );
m_x_offset[m_seq] = Mth::Rnd( 255 );
m_z_offset[m_seq] = Mth::Rnd( 255 );
m_active_drops++;
// Calculate next sequence value. Notice hack to get entry 0 into the sequence.
switch ( m_seq )
{
case SEQ_START:
m_seq = 0;
break;
case 0:
m_seq = SEQ_START;
// Fall through to default case...
default:
if ( m_seq & 1 )
{
m_seq = ( m_seq >> 1 ) ^ SEQ_MASK;
}
else
{
m_seq = ( m_seq >> 1 );
}
break;
}
}
}
else
{
// It's snowing.
float rate = m_snow_flakes_per_frame;
if ( rate > (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_snow_frames ) ) rate = (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_snow_frames );
float last = m_snow_rate;
m_snow_rate += rate;
int new_drops = (int)m_snow_rate - (int)last;
for ( int lp = 0; lp < new_drops; lp++ )
{
// If this cel is not inactive, we caught up with ourselves.
if ( m_drop_time[m_seq] != 255 ) break;
// Setup the drop.
m_drop_time[m_seq] = ( 254 - m_snow_frames );
m_x_offset[m_seq] = Mth::Rnd( 255 );
m_z_offset[m_seq] = Mth::Rnd( 255 );
m_active_drops++;
// Calculate next sequence value. Notice hack to get entry 0 into the sequence.
switch ( m_seq )
{
case SEQ_START:
{
m_seq = 0;
break;
}
case 0:
{
m_seq = SEQ_START;
// Fall through to default case...
}
default:
{
if ( m_seq & 1 )
{
m_seq = ( m_seq >> 1 ) ^ SEQ_MASK;
}
else
{
m_seq = ( m_seq >> 1 );
}
break;
}
}
}
}
}
inline DWORD FtoDW( FLOAT f ) { return *((DWORD*)&f); }
/******************************************************************/
/* */
/* */
/******************************************************************/
void CXboxWeather::plat_render_snow( float skx, float skz )
{
// Early out if no drops to draw.
if( !m_active_drops ) return;
// Get skater position.
int x = (int)(( skx - m_min_x ) / WEATHER_CEL_SIZE );
int z = (int)(( skz - m_min_z ) / WEATHER_CEL_SIZE );
// Calculate area to render.
int sx = x - RENDER_DIST;
int ex = x + DROP_SIZE; //RENDER_DIST;
int sz = z - RENDER_DIST;
int ez = z + DROP_SIZE; //RENDER_DIST;
// Clip z values.
if( ez < 0 ) return;
if( sz > ( m_height - 1 )) return;
// Pointsprites use texture stage 3 for some reason...
NxXbox::set_blend_mode( m_snow_blend );
NxXbox::set_render_state( RS_ALPHACUTOFF, 0 );
NxXbox::set_render_state( RS_UVADDRESSMODE3, 0x00000000UL );
mp_snow_texture->Set( 3 );
// Set up point sprite rendering.
D3DDevice_SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );
D3DDevice_SetRenderState( D3DRS_POINTSCALEENABLE, TRUE );
D3DDevice_SetRenderState( D3DRS_POINTSIZE, FtoDW( m_snow_size ));
D3DDevice_SetRenderState( D3DRS_POINTSCALE_A, FtoDW( 0.00f ));
D3DDevice_SetRenderState( D3DRS_POINTSCALE_B, FtoDW( 0.00f ));
D3DDevice_SetRenderState( D3DRS_POINTSCALE_C, FtoDW( 1.00f ));
NxXbox::set_pixel_shader( PixelShaderPointSprite );
NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
float minx = m_min_x;
float minz = m_min_z;
// Calculate drop height list.
int lp;
float y_off[256];
for( lp = ( 254 - m_snow_frames ); lp < 256; lp++ )
{
y_off[lp] = m_snow_height - (((float)(( lp - 254 ) + m_snow_frames ) / (float)m_snow_frames ) * m_snow_height );
}
// Calculate xz offset list.
float xz_off[256];
for( lp = 0; lp < 256; lp++ )
{
xz_off[lp] = ((float)lp * (float)( WEATHER_CEL_SIZE / 2 )) / 255.0f;
}
// Calculate sine wave.
float sin_off[256];
for( lp = 0; lp < 256; lp++ )
{
sin_off[lp] = sinf( Mth::PI * 2.0f * ((float)lp / 256.0f )) * ( WEATHER_CEL_SIZE / 2 );
}
// Get Xbox format color.
uint32 snow_color = ((uint32)m_snow_color.a << 24 ) | ((uint32)m_snow_color.r << 16 ) | ((uint32)m_snow_color.g << 8 ) | ((uint32)m_snow_color.b << 0 );
int flakes = 0;
DWORD* p_push = NULL;
DWORD* p_push_encode_fixup = NULL;
uint32 dwords_written = 0;
uint32 total_dwords_written = 2;
for( int lzz = sz; lzz <= ez; lzz++ )
{
int zz = ( lzz < 0 ) ? 0 : ( lzz > ( m_height - 1 ) ) ? ( m_height - 1 ) : lzz;
if( mp_roof_row[zz].start == 16384 ) continue;
float vx = ((float)sx * (float)WEATHER_CEL_SIZE ) + minx;
float vz = ((float)zz * (float)WEATHER_CEL_SIZE ) + minz;
int cel = mp_roof_row[zz].index + ( sx - mp_roof_row[zz].start );
int drop_cel_z = (( zz & ( DROP_SIZE - 1 )) << DROP_SIZE_SHIFT );
for( int lxx = sx; lxx <= ex; lxx++, cel++ )
{
int xx = ( lxx > mp_roof_row[zz].start ) ? ( ( lxx < mp_roof_row[zz].end ) ? lxx : mp_roof_row[zz].end ) : mp_roof_row[zz].start;
// Get the current drop value. Skip this one if it's inactive.
int drop_cel = drop_cel_z + ( xx & ( DROP_SIZE - 1 ));
vx += (float)WEATHER_CEL_SIZE;
float vy = (float)( m_roof_height[mp_roof_height_index[cel]] >> PRECISION_SHIFT );
for( int d = 0; d < DROP_LAYERS; d++, drop_cel += ( DROP_SIZE * DROP_SIZE ))
{
if( m_drop_time[drop_cel] == 255 )
continue;
// Create the position for rendering.
float v0x = vx + xz_off[m_x_offset[drop_cel]] + sin_off[(m_z_offset[drop_cel]+m_drop_time[drop_cel])&255];
float v0y = vy + y_off[m_drop_time[drop_cel]] + m_snow_size;
float v0z = vz + xz_off[m_z_offset[drop_cel]] + sin_off[(m_x_offset[drop_cel]+m_drop_time[drop_cel])&255];
if( flakes == 0 )
{
flakes = NUM_SPRITES_PER_BATCH;
}
// Grab the push buffer if we don't have it yet.
if( p_push == NULL )
{
// Grab 32k of push buffer.
p_push = D3DDevice_BeginPush( 32 * 1024 / 4 );
// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
p_push[1] = D3DPT_POINTLIST;
p_push += 2;
// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
// As yet, we don't know how many dwords we will write, so save a pointer to the current push buffer location
// so we can fix up this value at the end.
p_push_encode_fixup = p_push;
++p_push;
}
p_push[0] = *((uint32*)&v0x );
p_push[1] = *((uint32*)&v0y );
p_push[2] = *((uint32*)&v0z );
p_push[3] = snow_color;
p_push += 4;
dwords_written += 4;
total_dwords_written += 4;
// Check for hitting the max dwords for this encode block, in which case we need to end this block
// and start another one.
if( dwords_written >= 2044 )
{
// Now we know exactly how many dwords written, fix-up our earlier entry.
p_push_encode_fixup[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, dwords_written );
// Are we approaching the end of this push buffer? If so, close it out and start a new one.
if( total_dwords_written > 6000 )
{
// End the push buffer.
total_dwords_written = 0;
p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
p_push[1] = 0;
p_push += 2;
D3DDevice_EndPush( p_push );
p_push = NULL;
}
else
{
// As yet, we don't know how many dwords we will write, so save a pointer to the current push buffer location
// so we can fix up this value at the end.
p_push_encode_fixup = p_push;
++p_push;
}
dwords_written = 0;
}
flakes--;
}
}
}
if( p_push )
{
// Now we know exactly how many dwords written, fix-up our earlier entry.
p_push_encode_fixup[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, dwords_written );
// End the push buffer.
p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
p_push[1] = 0;
p_push += 2;
D3DDevice_EndPush( p_push );
}
// Restore render states.
D3DDevice_SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CXboxWeather::plat_render_rain( float skx, float skz )
{
// Early out if no drops to draw.
if ( !m_active_drops ) return;
// Get skater position.
int x = (int)(( skx - m_min_x ) / WEATHER_CEL_SIZE );
int z = (int)(( skz - m_min_z ) / WEATHER_CEL_SIZE );
// Calculate area to render.
int sx = x - RENDER_DIST;
int ex = x + DROP_SIZE; //RENDER_DIST;
int sz = z - RENDER_DIST;
int ez = z + DROP_SIZE; //RENDER_DIST;
// Clip z values.
if ( ez < 0 ) return;
if ( sz > ( m_height - 1 ) ) return;
// Get Xbox format top and bottom color.
uint32 top_color = ((uint32)m_rain_top_color.a << 24 ) | ((uint32)m_rain_top_color.r << 16 ) | ((uint32)m_rain_top_color.g << 8 ) | ((uint32)m_rain_top_color.b << 0 );
uint32 bot_color = ((uint32)m_rain_bottom_color.a << 24 ) | ((uint32)m_rain_bottom_color.r << 16 ) | ((uint32)m_rain_bottom_color.g << 8 ) | ((uint32)m_rain_bottom_color.b << 0 );
// Deal with FIXED_BRIGHTEN mode, which doesn't work.
if(( m_rain_blend & NxXbox::sMaterial::BLEND_MODE_MASK ) == NxXbox::vBLEND_MODE_BRIGHTEN_FIXED )
{
set_blend_mode( NxXbox::vBLEND_MODE_BRIGHTEN );
top_color = ( top_color & 0x00FFFFFFUL ) | ( m_rain_blend & 0xFF000000UL );
bot_color = ( bot_color & 0x00FFFFFFUL ) | ( m_rain_blend & 0xFF000000UL );
}
else
{
NxXbox::set_blend_mode( m_rain_blend );
}
NxXbox::set_render_state( RS_ALPHACUTOFF, 0 );
NxXbox::set_pixel_shader( PixelShader5 );
NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
sint32 minx = (sint32)( m_min_x * SUB_INCH_PRECISION );
sint32 minz = (sint32)( m_min_z * SUB_INCH_PRECISION );
sint32 rlength = (sint32)( m_rain_length * SUB_INCH_PRECISION );
// Calculate drop height list.
int lp;
sint32 y_off[256];
for( lp = ( 254 - m_rain_frames ); lp < 256; lp++ )
{
y_off[lp] = (sint32)(( m_rain_height - (((float)(( lp - 254 ) + m_rain_frames ) / (float)m_rain_frames ) * m_rain_height )) * SUB_INCH_PRECISION );
}
// Calculate xz offset list.
sint32 xz_off[256];
for( lp = 0; lp < 256; lp++ )
{
xz_off[lp] = (sint32)((((float)lp * (float)WEATHER_CEL_SIZE * SUB_INCH_PRECISION ) / 255.0f ));
}
int lines = 0;
DWORD* p_push = NULL;
DWORD* p_push_encode_fixup = NULL;
uint32 dwords_written = 0;
uint32 total_dwords_written = 2;
for ( int lzz = sz; lzz <= ez; lzz++ )
{
int zz = ( lzz < 0 ) ? 0 : ( lzz > ( m_height - 1 ) ) ? ( m_height - 1 ) : lzz;
if ( mp_roof_row[zz].start == 16384 ) continue;
sint32 vx = ( (sint32)sx << ( WEATHER_CEL_SIZE_SHIFT + PRECISION_SHIFT ) ) + minx;
sint32 vz = ( (sint32)zz << ( WEATHER_CEL_SIZE_SHIFT + PRECISION_SHIFT ) ) + minz;
int cel = mp_roof_row[zz].index + ( sx - mp_roof_row[zz].start );
int drop_cel_z = ( ( zz & ( DROP_SIZE - 1 ) ) << DROP_SIZE_SHIFT );
for ( int lxx = sx; lxx <= ex; lxx++, cel++ )
{
int xx = ( lxx > mp_roof_row[zz].start ) ? ( ( lxx < mp_roof_row[zz].end ) ? lxx : mp_roof_row[zz].end ) : mp_roof_row[zz].start;
// Get the current drop value. Skip this one if it's inactive.
int drop_cel = drop_cel_z + ( xx & ( DROP_SIZE - 1 ) );
vx += ( WEATHER_CEL_SIZE << PRECISION_SHIFT );
sint32 vy = m_roof_height[mp_roof_height_index[cel]];
for ( int d = 0; d < DROP_LAYERS; d++, drop_cel += ( DROP_SIZE * DROP_SIZE ) )
{
if ( m_drop_time[drop_cel] == 255 )
continue;
// Create the position for rendering.
float v0x = ( vx + xz_off[m_x_offset[drop_cel]] ) * ( 1.0f / (float)( 1 << PRECISION_SHIFT ));
float v0y = ( vy + y_off[m_drop_time[drop_cel]] ) * ( 1.0f / (float)( 1 << PRECISION_SHIFT ));
float v0z = ( vz + xz_off[m_z_offset[drop_cel]] ) * ( 1.0f / (float)( 1 << PRECISION_SHIFT ));
float v1y = v0y + ( rlength * ( 1.0f / (float)( 1 << PRECISION_SHIFT )));
if ( lines == 0 )
{
lines = NUM_LINES_PER_BATCH;
}
// Grab the push buffer if we don't have it yet.
if( p_push == NULL )
{
// Grab 32k of push buffer.
p_push = D3DDevice_BeginPush( 32 * 1024 / 4 );
// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
p_push[1] = D3DPT_LINELIST;
p_push += 2;
// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
// As yet, we don't know how many dwords we will write, so save a pointer to the current push buffer location
// so we can fix up this value at the end.
p_push_encode_fixup = p_push;
++p_push;
}
p_push[0] = *((DWORD*)&v0x);
p_push[1] = *((DWORD*)&v0y);
p_push[2] = *((DWORD*)&v0z);
p_push[3] = bot_color;
p_push[4] = *((DWORD*)&v0x);
p_push[5] = *((DWORD*)&v1y);
p_push[6] = *((DWORD*)&v0z);
p_push[7] = top_color;
p_push += 8;
dwords_written += 8;
total_dwords_written += 8;
// Check for hitting the max dwords for this encode block, in which case we need to end this block
// and start another one.
if( dwords_written >= 2040 )
{
// Now we know exactly how many dwords written, fix-up our earlier entry.
p_push_encode_fixup[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, dwords_written );
// Are we approaching the end of this push buffer? If so, close it out and start a new one.
if( total_dwords_written > 6000 )
{
// End the push buffer.
total_dwords_written = 0;
p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
p_push[1] = 0;
p_push += 2;
D3DDevice_EndPush( p_push );
p_push = NULL;
}
else
{
// As yet, we don't know how many dwords we will write, so save a pointer to the current push buffer location
// so we can fix up this value at the end.
p_push_encode_fixup = p_push;
++p_push;
}
dwords_written = 0;
}
lines--;
}
}
}
if( p_push )
{
// Now we know exactly how many dwords written, fix-up our earlier entry.
p_push_encode_fixup[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, dwords_written );
// End the push buffer.
p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
p_push[1] = 0;
p_push += 2;
D3DDevice_EndPush( p_push );
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CXboxWeather::plat_render_splashes( float skx, float skz )
{
// Create a new splash if required.
float last = m_splash_rate;
m_splash_rate += m_splash_per_frame;
int new_splashes = (int)m_splash_rate - (int)last;
if ( new_splashes )
{
CFeeler feeler;
Mth::Vector pos;
pos[X] = NxXbox::EngineGlobals.cam_position[X];
pos[Y] = FEELER_START;
pos[Z] = NxXbox::EngineGlobals.cam_position[Z];
pos[W] = 1.0f;
Mth::Vector dir;
dir[X] = skx - NxXbox::EngineGlobals.cam_position[X];
dir[Y] = 0.0f;
dir[Z] = skz - NxXbox::EngineGlobals.cam_position[Z];
dir[W] = 1.0f;
dir.Normalize();
// Add distance.
float r1 = (float)Mth::Rnd( 32768 ) / 32768.0f;
pos += ( dir * ((float)WEATHER_CEL_SIZE * RENDER_DIST * r1 ));
// Add lateral.
Mth::Vector lat( dir[Z], 0.0f, -dir[X], 1.0f );
float r2 = 1.0f - ( (float)Mth::Rnd( 32768 ) / 32768.0f );
if ( m_current_splash & 1 )
{
pos += ( lat * ( (float)WEATHER_CEL_SIZE * RENDER_DIST * r1 * r2 ) );
}
else
{
pos += ( lat * ( (float)WEATHER_CEL_SIZE * RENDER_DIST * r1 * -r2 ) );
}
feeler.SetStart( pos );
pos[Y] = -FEELER_START;
feeler.SetEnd( pos );
// Get the y position.
float y;
if ( feeler.GetCollision( false, false ) ) // No movables, nearest collision.
{
y = feeler.GetPoint()[Y];
}
else
{
y = FEELER_START;
}
m_splash_x[m_current_splash] = pos[X];
m_splash_y[m_current_splash] = y + 4.0f; // Move up slightly.
m_splash_z[m_current_splash] = pos[Z];
m_splash_current_life[m_current_splash] = m_splash_life;
m_current_splash++;
m_current_splash %= NUM_SPLASH_ACTIVE;
}
// Render the splashes.
NxXbox::set_blend_mode( m_splash_blend );
NxXbox::set_render_state( RS_ALPHACUTOFF, 0 );
NxXbox::set_render_state( RS_UVADDRESSMODE0, 0x00000000UL );
mp_rain_texture->Set( 0 );
NxXbox::set_pixel_shader( PixelShader4 );
NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));
uint32 dword_count = 24 * NUM_SPLASH_ACTIVE;
uint32 dwords_written = 0;
Dbg_Assert( dword_count < 2048 );
// Obtain push buffer lock.
// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
DWORD* p_push = D3DDevice_BeginPush( dword_count + 16 );
DWORD* p_push_encode_fixup = NULL;
// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
p_push[1] = D3DPT_QUADLIST;
p_push += 2;
// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
// As yet, we don't know how many dwords we will write, so save a pointer to the current push buffer location
// so we can fix up this value at the end.
p_push_encode_fixup = p_push;
++p_push;
for ( int spl = 0; spl < NUM_SPLASH_ACTIVE; spl++ )
{
if( m_splash_current_life[spl] == 0 )
continue;
// Interpolate color (and switch to Xbox format at the same time).
Image::RGBA icol;
icol.b = (uint8)(((int)m_splash_color.r * m_splash_current_life[spl] ) / m_splash_life );
icol.g = (uint8)(((int)m_splash_color.g * m_splash_current_life[spl] ) / m_splash_life );
icol.r = (uint8)(((int)m_splash_color.b * m_splash_current_life[spl] ) / m_splash_life );
icol.a = (uint8)(((int)m_splash_color.a * m_splash_current_life[spl] ) / m_splash_life );
// Draw splashes...
float vx0 = m_splash_x[spl] - ( m_splash_size * 0.5f );
float vx1 = m_splash_x[spl] + ( m_splash_size * 0.5f );
float vy = m_splash_y[spl];
float vz0 = m_splash_z[spl] - ( m_splash_size * 0.5f );
float vz1 = m_splash_z[spl] + ( m_splash_size * 0.5f );
p_push[0] = *((DWORD*)&vx0 );
p_push[1] = *((DWORD*)&vy );
p_push[2] = *((DWORD*)&vz0 );
p_push[3] = *((DWORD*)&icol );
p_push[4] = 0x00000000UL;
p_push[5] = 0x00000000UL;
p_push[6] = *((DWORD*)&vx1 );
p_push[7] = *((DWORD*)&vy );
p_push[8] = *((DWORD*)&vz0 );
p_push[9] = *((DWORD*)&icol );
p_push[10] = 0x3F800000UL;
p_push[11] = 0x00000000UL;
p_push[12] = *((DWORD*)&vx1 );
p_push[13] = *((DWORD*)&vy );
p_push[14] = *((DWORD*)&vz1 );
p_push[15] = *((DWORD*)&icol );
p_push[16] = 0x3F800000UL;
p_push[17] = 0x3F800000UL;
p_push[18] = *((DWORD*)&vx0 );
p_push[19] = *((DWORD*)&vy );
p_push[20] = *((DWORD*)&vz1 );
p_push[21] = *((DWORD*)&icol );
p_push[22] = 0x00000000UL;
p_push[23] = 0x3F800000UL;
p_push += 24;
dwords_written += 24;
m_splash_current_life[spl]--;
}
p_push_encode_fixup[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, dwords_written );
p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
p_push[1] = 0;
D3DDevice_EndPush( p_push + 2 );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CXboxWeather::plat_render( void )
{
if ( !m_system_active ) return;
Mdl::Skate * pSkate = Mdl::Skate::Instance();
Obj::CSkater* pSkater = pSkate->GetSkater(0);
if (!pSkater) return;
float skx = pSkater->m_pos[X];
float skz = pSkater->m_pos[Z];
if ( m_raining )
{
plat_render_splashes( skx, skz );
plat_render_rain( skx, skz );
}
else
{
plat_render_snow( skx, skz );
}
// Increment rain drop/snow flake positions.
if ( m_active_drops )
{
for ( int lp = 0; lp < ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ); lp++ )
{
switch ( m_drop_time[lp] )
{
case 255:
// Inactive.
break;
case 254:
// About to become inactive, so decrement active drops.
m_active_drops--;
// Deliberately falls through...
default:
// Increment time.
m_drop_time[lp]++;
break;
}
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CXboxWeather::plat_set_rain_blend_mode( uint32 blendmode_checksum, int fix )
{
m_rain_blend = NxXbox::GetBlendMode( blendmode_checksum ) | ((uint32)fix << 24 );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CXboxWeather::plat_set_splash_blend_mode( uint32 blendmode_checksum, int fix )
{
m_splash_blend = NxXbox::GetBlendMode( blendmode_checksum ) | ((uint32)fix << 24 );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CXboxWeather::plat_set_snow_blend_mode( uint32 blendmode_checksum, int fix )
{
m_snow_blend = NxXbox::GetBlendMode( blendmode_checksum ) | ((uint32)fix << 24 );
}
} // Nx