//**************************************************************************** //* MODULE: Gfx //* FILENAME: p_NxWeather.cpp //* OWNER: Dave Cowling //* CREATION DATE: 6/19/2003 //**************************************************************************** #include #include "gfx/xbox/nx/render.h" #include #include #include "gfx/xbox/nx/mesh.h" #include "gfx/xbox/p_nxweather.h" #include #include #include #include #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( 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( 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