/***************************************************************************** ** ** ** Neversoft Entertainment. ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Skate5 ** ** ** ** Module: Gfx ** ** ** ** File name: p_NxNewParticle.cpp ** ** ** ** Created by: 3/25/03 - SPG ** ** ** ** Description: Xbox new parametric particle system ** *****************************************************************************/ #include #include #include #include #include #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Nx { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ static int rand_seed; static int rand_a = 314159265; static int rand_b = 178453311; /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ static void seed_particle_rnd( int s, int a, int b ) { rand_seed = s; rand_a = a; rand_b = b; } /******************************************************************/ /* */ /* */ /******************************************************************/ static inline int particle_rnd( int n ) { rand_seed = rand_seed * rand_a + rand_b; rand_a = ( rand_a ^ rand_seed ) + ( rand_seed >> 4 ); rand_b += ( rand_seed >> 3 ) - 0x10101010L; return (int)(( rand_seed & 0xffff ) * n ) >> 16; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CParticleStream::AdvanceSeed( int num_places ) { // Seed the random number generator back to the current seed. seed_particle_rnd( m_rand_seed, m_rand_a, m_rand_b ); // Each particle will call the random function four times. for( int i = 0; i < ( num_places * 4 ); i++ ) { rand_seed = rand_seed * rand_a + rand_b; rand_a = ( rand_a ^ rand_seed ) + ( rand_seed >> 4 ); rand_b += ( rand_seed >> 3 ) - 0x10101010L; } m_rand_seed = rand_seed; m_rand_a = rand_a; m_rand_b = rand_b; } inline DWORD FtoDW( FLOAT f ) { return *((DWORD*)&f); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxNewParticle::plat_render( void ) { CParticleStream* p_stream; int i; // Process the streams. if( m_params.m_EmitRate && ( !m_emitting || ( m_params.m_EmitRate != mp_newest_stream->m_rate ))) { if( m_num_streams < m_max_streams ) { // Add new stream to cyclic buffer m_num_streams++; mp_newest_stream++; if( mp_newest_stream == mp_stream + m_max_streams ) { mp_newest_stream = mp_stream; } // Initialise new stream. mp_newest_stream->m_rate = m_params.m_EmitRate; mp_newest_stream->m_interval = 1.0f / m_params.m_EmitRate; mp_newest_stream->m_oldest_age = 0.0f; mp_newest_stream->m_num_particles = 0; mp_newest_stream->m_rand_seed = rand(); mp_newest_stream->m_rand_a = 314159265; mp_newest_stream->m_rand_b = 178453311; m_emitting = true; } else { m_emitting = false; } } else { m_emitting = m_params.m_EmitRate; } if( !m_num_streams ) return; // Age all streams. for( i = 0, p_stream = mp_oldest_stream; i < m_num_streams; ++i ) { // Increase age of oldest particle. p_stream->m_oldest_age += 1.0f / 60.0f; // Step pointer within cyclic buffer. p_stream++; if( p_stream == mp_stream + m_max_streams ) { p_stream = mp_stream; } } // Births into newest stream. if( m_emitting ) { // How many particles so far emitted? mp_newest_stream->m_num_particles = (int)( mp_newest_stream->m_oldest_age * mp_newest_stream->m_rate + 1.0f ); } // Deaths from oldest stream. if( mp_oldest_stream->m_oldest_age > m_params.m_Lifetime ) { // Work out number dead. int particles_dead = (int)(( mp_oldest_stream->m_oldest_age - m_params.m_Lifetime ) * mp_oldest_stream->m_rate + 1.0f ); // Remove dead particles. mp_oldest_stream->m_num_particles -= particles_dead; // Should we keep processing the oldest stream? if( mp_oldest_stream->m_num_particles > 0 || ( m_num_streams == 1 && m_emitting )) { // Adjust age of oldest particle. mp_oldest_stream->m_oldest_age -= (float)particles_dead * mp_oldest_stream->m_interval; // Advance seed. mp_oldest_stream->AdvanceSeed( particles_dead ); } else { // Remove oldest stream and wrap in cyclic buffer if necessary. m_num_streams--; mp_oldest_stream++; if( mp_oldest_stream == mp_stream + m_max_streams ) { mp_oldest_stream = mp_stream; } if( !m_num_streams ) return; } } // Now render the streams. after checking the bounding sphere is visible (for no dynamic systems). if( !m_params.m_LocalCoord ) { D3DXVECTOR3 center( m_bsphere[X], m_bsphere[Y], m_bsphere[Z] ); if( !NxXbox::frustum_check_sphere( ¢er, m_bsphere[W] )) { return; } } // Swap the r and b color components in the params. for( i = 0; i < vNUM_BOXES; ++i ) { uint8 swap = m_params.m_Color[i].r; m_params.m_Color[i].r = m_params.m_Color[i].b; m_params.m_Color[i].b = swap; } // Figure the distance from the camera to the system, and use a different technique accordingly. // Need to do this because point sprites are limited to 64 pixels in each dimension, and sometimes // you can get close enough to a system that a single particle will occupy more than this. // A more sophisticated approach would be to figure what the max particle size could be at a given distance, // rather than using the current hardcoded distance value of 20 feet. float dist_squared = Mth::DistanceSqr( Mth::Vector( m_bsphere[X], m_bsphere[Y], m_bsphere[Z], 0.0f ), Mth::Vector( NxXbox::EngineGlobals.cam_position.x, NxXbox::EngineGlobals.cam_position.y, NxXbox::EngineGlobals.cam_position.z, 0.0f )); if( dist_squared < 57600.0f ) { // Used to figure the right and up vectors for creating screen-aligned particle quads. D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix; // Concatenate p_matrix with the emmission angle to create the direction. Mth::Vector up( 0.0f, 1.0f, 0.0f ); // Get the 'right' vector as the cross product of camera 'at and world 'up'. Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] ); Mth::Vector screen_right = Mth::CrossProduct( at, up ); Mth::Vector screen_up = Mth::CrossProduct( screen_right, at ); screen_right.Normalize(); screen_up.Normalize(); // Submit particle material. mp_material->Submit(); // Set up correct vertex and pixel shader. NxXbox::set_vertex_shader( ParticleNewFlatVS ); NxXbox::set_pixel_shader( PixelShader0 ); // Load up the combined world->view_projection matrix. XGMATRIX dest_matrix; XGMATRIX projMatrix; XGMATRIX viewMatrix; // Projection matrix. XGMatrixTranspose( &projMatrix, &NxXbox::EngineGlobals.projection_matrix ); // View matrix. XGMatrixTranspose( &viewMatrix, &NxXbox::EngineGlobals.view_matrix ); viewMatrix.m[3][0] = 0.0f; viewMatrix.m[3][1] = 0.0f; viewMatrix.m[3][2] = 0.0f; viewMatrix.m[3][3] = 1.0f; // Calculate composite world->view->projection matrix (simplified since world transform will be indentity). XGMatrixMultiply( &dest_matrix, &projMatrix, &viewMatrix ); // Load up the combined world, camera & projection matrix. D3DDevice_SetVertexShaderConstantFast( 0, (void*)&dest_matrix, 4 ); float vector_upload[8]; vector_upload[0] = screen_right[X]; vector_upload[1] = screen_right[Y]; vector_upload[2] = screen_right[Z]; vector_upload[4] = screen_up[X]; vector_upload[5] = screen_up[Y]; vector_upload[6] = screen_up[Z]; D3DDevice_SetVertexShaderConstantFast( 4, (void*)( &vector_upload[0] ), 2 ); static float vconstants[32] = { 0.0f, 0.0f, 1.0f, 1.0f, // Vert tex coords in C8 through C11 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, // Vert w/h multipliers in C12 through C15 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f }; D3DDevice_SetVertexShaderConstantFast( 8, (void*)( &vconstants[0] ), 8 ); float stream_params[24]; stream_params[0] = m_s0[X]; stream_params[1] = m_s0[Y]; stream_params[2] = m_s0[Z]; stream_params[3] = m_s0[W]; stream_params[4] = m_s1[X]; stream_params[5] = m_s1[Y]; stream_params[6] = m_s1[Z]; stream_params[7] = m_s1[W]; stream_params[8] = m_s2[X]; stream_params[9] = m_s2[Y]; stream_params[10] = m_s2[Z]; stream_params[11] = m_s2[W]; stream_params[12] = m_p0[X]; stream_params[13] = m_p0[Y]; stream_params[14] = m_p0[Z]; stream_params[15] = m_p0[W]; stream_params[16] = m_p1[X]; stream_params[17] = m_p1[Y]; stream_params[18] = m_p1[Z]; stream_params[19] = m_p1[W]; stream_params[20] = m_p2[X]; stream_params[21] = m_p2[Y]; stream_params[22] = m_p2[Z]; stream_params[23] = m_p2[W]; D3DDevice_SetVertexShaderConstantFast( 18, (void*)( &stream_params[0] ), 6 ); // Construct a packet with data for each stream. for( i = 0, p_stream = mp_oldest_stream; i < m_num_streams; i++, p_stream++ ) { Dbg_MsgAssert( p_stream->m_num_particles < 65536, ( "particle limit reached" )); // Wrap at end of cyclic buffer. if( p_stream == mp_stream + m_max_streams ) { p_stream = mp_stream; } // Calculate space needed. DWORD dwords_per_particle = 28; DWORD dword_count = dwords_per_particle * p_stream->m_num_particles; // Obtain push buffer lock. // 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. DWORD *p_push; p_push = D3DDevice_BeginPush( dword_count ); float t = p_stream->m_oldest_age; float midpoint_time = m_params.m_Lifetime * ( m_params.m_ColorMidpointPct * 0.01f ); // Seed the random number generators for this stream. seed_particle_rnd( p_stream->m_rand_seed, p_stream->m_rand_a, p_stream->m_rand_b ); for( int p = 0; p < p_stream->m_num_particles; ++p ) { // Generate random vector. Each component in the range [1.0, 2.0]. float r[4]; r[0] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 ); r[1] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 ); r[2] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 ); r[3] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 ); float color_interpolator; Image::RGBA col0, col1; if( m_params.m_UseMidcolor ) { if( t > midpoint_time ) { color_interpolator = ( t - midpoint_time ) * ReciprocalEstimate_ASM( m_params.m_Lifetime - midpoint_time ); col0 = m_params.m_Color[1]; col1 = m_params.m_Color[2]; } else { color_interpolator = t * ReciprocalEstimate_ASM( midpoint_time ); col0 = m_params.m_Color[0]; col1 = m_params.m_Color[1]; } } else { color_interpolator = t * ReciprocalEstimate_ASM( m_params.m_Lifetime ); col0 = m_params.m_Color[0]; col1 = m_params.m_Color[2]; } // We're going to be loading constants. p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_TRANSFORM_CONSTANT_LOAD, 1 ); // Specify the starting register (physical registers are offset by 96 from the D3D logical register). p_push[1] = 96 + 16; // Specify the number of DWORDS to load. 8 DWORDS for 2 constants. p_push[2] = D3DPUSH_ENCODE( D3DPUSH_SET_TRANSFORM_CONSTANT, 8 ); // Load r vector. p_push[3] = *((DWORD*)&r[0] ); p_push[4] = *((DWORD*)&r[1] ); p_push[5] = *((DWORD*)&r[2] ); p_push[6] = *((DWORD*)&r[3] ); // Load interpolator values. p_push[7] = *((DWORD*)&t ); p_push[8] = *((DWORD*)&color_interpolator ); p_push += 11; 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. p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, 12 ); ++p_push; // Now we can start the actual vertex data. p_push[0] = *((DWORD*)&col0 ); p_push[1] = *((DWORD*)&col1 ); p_push[2] = 0x00000000UL; p_push[3] = *((DWORD*)&col0 ); p_push[4] = *((DWORD*)&col1 ); p_push[5] = 0x00010001UL; p_push[6] = *((DWORD*)&col0 ); p_push[7] = *((DWORD*)&col1 ); p_push[8] = 0x00020002UL; p_push[9] = *((DWORD*)&col0 ); p_push[10] = *((DWORD*)&col1 ); p_push[11] = 0x00030003UL; p_push += 12; // End of vertex data for this particle. p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 ); p_push[1] = 0; p_push += 2; // Reduce t by particle interval. t -= p_stream->m_interval; } D3DDevice_EndPush( p_push ); } } else { // Submit particle material. mp_material->Submit(); // Point sprites actually use texture stage 3... NxXbox::set_render_state( RS_UVADDRESSMODE3, 0x00000000UL ); mp_material->mp_tex[0]->Set( 3 ); // Set up point sprite rendering. D3DDevice_SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE ); D3DDevice_SetRenderState( D3DRS_POINTSCALEENABLE, TRUE ); // D3DDevice_SetRenderState( D3DRS_POINTSCALE_A, FtoDW( 0.00f )); // D3DDevice_SetRenderState( D3DRS_POINTSCALE_B, FtoDW( 0.00f )); // D3DDevice_SetRenderState( D3DRS_POINTSCALE_C, FtoDW( 1.00f )); // Set up correct vertex and pixel shader. NxXbox::set_vertex_shader( ParticleNewFlatPointSpriteVS ); NxXbox::set_pixel_shader( PixelShaderPointSprite ); // Load up the combined world->view_projection matrix. XGMATRIX dest_matrix; XGMATRIX projMatrix; XGMATRIX viewMatrix; // Projection matrix. XGMatrixTranspose( &projMatrix, &NxXbox::EngineGlobals.projection_matrix ); // View matrix. XGMatrixTranspose( &viewMatrix, &NxXbox::EngineGlobals.view_matrix ); viewMatrix.m[3][0] = 0.0f; viewMatrix.m[3][1] = 0.0f; viewMatrix.m[3][2] = 0.0f; viewMatrix.m[3][3] = 1.0f; // Calculate composite world->view->projection matrix (simplified since world transform will be indentity). XGMatrixMultiply( &dest_matrix, &projMatrix, &viewMatrix ); // Load up the combined world, camera & projection matrix. D3DDevice_SetVertexShaderConstantFast( 0, (void*)&dest_matrix, 4 ); // Load up the stream parameters. D3DDevice_SetVertexShaderConstantFast( 18, (void*)( &m_s0[X] ), 1 ); D3DDevice_SetVertexShaderConstantFast( 19, (void*)( &m_s1[X] ), 1 ); D3DDevice_SetVertexShaderConstantFast( 20, (void*)( &m_s2[X] ), 1 ); D3DDevice_SetVertexShaderConstantFast( 21, (void*)( &m_p0[X] ), 1 ); D3DDevice_SetVertexShaderConstantFast( 22, (void*)( &m_p1[X] ), 1 ); D3DDevice_SetVertexShaderConstantFast( 23, (void*)( &m_p2[X] ), 1 ); // Load up the camera position and viewport height. static float cam_pos_viewport_height[4]; cam_pos_viewport_height[0] = NxXbox::EngineGlobals.cam_position.x; cam_pos_viewport_height[1] = NxXbox::EngineGlobals.cam_position.y; cam_pos_viewport_height[2] = NxXbox::EngineGlobals.cam_position.z; cam_pos_viewport_height[3] = NxXbox::EngineGlobals.viewport.Height * 2.0f; D3DDevice_SetVertexShaderConstantFast( 24, (void*)( &cam_pos_viewport_height[0] ), 1 ); // Construct a packet with data for each stream. for( i = 0, p_stream = mp_oldest_stream; i < m_num_streams; i++, p_stream++ ) { Dbg_MsgAssert( p_stream->m_num_particles < 65536, ( "particle limit reached" )); // Wrap at end of cyclic buffer. if( p_stream == mp_stream + m_max_streams ) { p_stream = mp_stream; } // Calculate space needed. DWORD dwords_per_particle = 13; DWORD dword_count = dwords_per_particle * p_stream->m_num_particles; // Obtain push buffer lock. // 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. DWORD *p_push; p_push = D3DDevice_BeginPush( dword_count ); float t = p_stream->m_oldest_age; float midpoint_time = m_params.m_Lifetime * ( m_params.m_ColorMidpointPct * 0.01f ); // Seed the random number generators for this stream. seed_particle_rnd( p_stream->m_rand_seed, p_stream->m_rand_a, p_stream->m_rand_b ); for( int p = 0; p < p_stream->m_num_particles; ++p ) { // Generate random vector. Each component in the range [1.0, 2.0]. float r[4]; r[0] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 ); r[1] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 ); r[2] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 ); r[3] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 ); float color_interpolator; Image::RGBA col0, col1; if( m_params.m_UseMidcolor ) { if( t > midpoint_time ) { color_interpolator = ( t - midpoint_time ) * ReciprocalEstimate_ASM( m_params.m_Lifetime - midpoint_time ); col0 = m_params.m_Color[1]; col1 = m_params.m_Color[2]; } else { color_interpolator = t * ReciprocalEstimate_ASM( midpoint_time ); col0 = m_params.m_Color[0]; col1 = m_params.m_Color[1]; } } else { color_interpolator = t * ReciprocalEstimate_ASM( m_params.m_Lifetime ); col0 = m_params.m_Color[0]; col1 = m_params.m_Color[2]; } // Signal the primitive type to follow. 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. p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, 8 ); ++p_push; // Now we can start the actual vertex data. // Load r vector. p_push[0] = *((DWORD*)&r[0] ); p_push[1] = *((DWORD*)&r[1] ); p_push[2] = *((DWORD*)&r[2] ); p_push[3] = *((DWORD*)&r[3] ); // Load time and color interpolator values. p_push[4] = *((DWORD*)&t ); p_push[5] = *((DWORD*)&color_interpolator ); // Load colors. p_push[6] = *((DWORD*)&col0 ); p_push[7] = *((DWORD*)&col1 ); p_push += 8; // End of vertex data for this particle. p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 ); p_push[1] = 0; p_push += 2; // Reduce t by particle interval. t -= p_stream->m_interval; } D3DDevice_EndPush( p_push ); } // Restore render states. D3DDevice_SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE ); } // Swap the r and b color components back in the params. for( i = 0; i < vNUM_BOXES; ++i ) { uint8 swap = m_params.m_Color[i].r; m_params.m_Color[i].r = m_params.m_Color[i].b; m_params.m_Color[i].b = swap; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxNewParticle::update_position( void ) { // Convert 3-point -> PVA format float t1 = m_params.m_Lifetime * m_params.m_MidpointPct * 0.01f; float t2 = m_params.m_Lifetime; Mth::Vector u, a_; Mth::Vector x0 = m_params.m_BoxPos[0]; x0[3] = m_params.m_Radius[0]; Mth::Vector x1 = m_params.m_BoxPos[1]; x1[3] = m_params.m_Radius[1]; Mth::Vector x2 = m_params.m_BoxPos[2]; x2[3] = m_params.m_Radius[2]; if( m_params.m_UseMidpoint ) { u = ( t2 * t2 * ( x1 - x0 ) - t1 * t1 * ( x2 - x0 )) / ( t1 * t2 * ( t2 - t1 )); a_ = ( t1 * ( x2 - x0 ) - t2 * ( x1 - x0 )) / ( t1 * t2 * ( t2 - t1 )); } else { u = ( x2 - x0 ) / t2; a_.Set( 0, 0, 0, 0 ); } m_p0 = x0 - 1.5f * m_s0; m_p1 = u - 1.5f * m_s1; m_p2 = a_ - 1.5f * m_s2; m_p0[3] = x0[3] - 1.5f * m_s0[3]; m_p1[3] = u[3] - 1.5f * m_s1[3]; m_p2[3] = a_[3] - 1.5f * m_s2[3]; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxNewParticle::plat_update( void ) { if( m_params.m_LocalCoord ) { update_position(); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxNewParticle::plat_build( void ) { // Reduce emit rate selectively to improve performance. m_params.m_EmitRate = m_params.m_EmitRate * 0.5f; // Initialise streams. m_max_streams = 5; m_num_streams = 0; mp_stream = new CParticleStream[m_max_streams]; mp_newest_stream = mp_stream + m_max_streams - 1; mp_oldest_stream = mp_stream; m_emitting = false; // Create a (semi-transparent) material used to render the mesh. mp_material = new NxXbox::sMaterial; ZeroMemory( mp_material, sizeof( NxXbox::sMaterial )); mp_material->m_flags[0] = MATFLAG_TRANSPARENT | MATFLAG_TEXTURED; mp_material->m_passes = 1; mp_material->m_alpha_cutoff = 1; mp_material->m_no_bfc = true; mp_material->m_color[0][0] = 0.5f; mp_material->m_color[0][1] = 0.5f; mp_material->m_color[0][2] = 0.5f; mp_material->m_color[0][3] = m_params.m_FixedAlpha * ( 1.0f / 128.0f ); mp_material->m_reg_alpha[0] = NxXbox::GetBlendMode( m_params.m_BlendMode ); // Get texture. Nx::CTexture* p_texture; Nx::CXboxTexture* p_xbox_texture; mp_material->mp_tex[0] = NULL; p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( m_params.m_Texture ); p_xbox_texture = static_cast( p_texture ); if( p_xbox_texture ) { mp_material->mp_tex[0] = p_xbox_texture->GetEngineTexture(); } // Convert 3-point -> PVA format. float t1 = m_params.m_Lifetime * m_params.m_MidpointPct * 0.01f; float t2 = m_params.m_Lifetime; Mth::Vector x0,x1,x2,u,a_; x0 = m_params.m_BoxDims[0]; x0[3] = m_params.m_RadiusSpread[0]; x1 = m_params.m_BoxDims[1]; x1[3] = m_params.m_RadiusSpread[1]; x2 = m_params.m_BoxDims[2]; x2[3] = m_params.m_RadiusSpread[2]; if( m_params.m_UseMidpoint ) { u = ( t2 * t2 * ( x1 - x0 ) - t1 * t1 * ( x2 - x0 )) / ( t1 * t2 * ( t2 - t1 )); a_ = ( t1 * ( x2 - x0 ) - t2 * ( x1 - x0 )) / ( t1 * t2 * ( t2 - t1 )); } else { u = ( x2 - x0 ) / t2; a_.Set( 0.0f, 0.0f, 0.0f, 0.0f ); } m_s0 = x0; m_s1 = u; m_s2 = a_; x0 = m_params.m_BoxPos[0]; x0[3] = m_params.m_Radius[0]; x1 = m_params.m_BoxPos[1]; x1[3] = m_params.m_Radius[1]; x2 = m_params.m_BoxPos[2]; x2[3] = m_params.m_Radius[2]; if( m_params.m_UseMidpoint ) { u = ( t2 * t2 * ( x1 - x0 ) - t1 * t1 * ( x2 - x0 )) / ( t1 * t2 * ( t2 - t1 )); a_ = ( t1 * ( x2 - x0 ) - t2 * ( x1 - x0 )) / ( t1 * t2 * ( t2 - t1 )); } else { u = ( x2 - x0 ) / t2; a_.Set( 0.0f, 0.0f, 0.0f, 0.0f ); } m_p0 = x0 - 1.5f * m_s0; m_p1 = u - 1.5f * m_s1; m_p2 = a_ - 1.5f * m_s2; m_p0[3] = x0[3] - 1.5f * m_s0[3]; m_p1[3] = u[3] - 1.5f * m_s1[3]; m_p2[3] = a_[3] - 1.5f * m_s2[3]; update_position(); // Color. if( m_params.m_UseMidcolor ) { float q0 = 100.0f / ( m_params.m_Lifetime * m_params.m_ColorMidpointPct ); float q1 = 100.0f / ( m_params.m_Lifetime * ( 100.0f - m_params.m_ColorMidpointPct )); // m_systemDmaData.m_c0[0] = ((float)m_params.m_Color[1].r - (float)m_params.m_Color[0].r) * q0; // m_systemDmaData.m_c0[1] = ((float)m_params.m_Color[1].g - (float)m_params.m_Color[0].g) * q0; // m_systemDmaData.m_c0[2] = ((float)m_params.m_Color[1].b - (float)m_params.m_Color[0].b) * q0; // m_systemDmaData.m_c0[3] = ((float)m_params.m_Color[1].a - (float)m_params.m_Color[0].a) * q0; // m_systemDmaData.m_c1[0] = (float)m_params.m_Color[1].r; // m_systemDmaData.m_c1[1] = (float)m_params.m_Color[1].g; // m_systemDmaData.m_c1[2] = (float)m_params.m_Color[1].b; // m_systemDmaData.m_c1[3] = (float)m_params.m_Color[1].a; // m_systemDmaData.m_c2[0] = ((float)m_params.m_Color[2].r - (float)m_params.m_Color[1].r) * q1; // m_systemDmaData.m_c2[1] = ((float)m_params.m_Color[2].g - (float)m_params.m_Color[1].g) * q1; // m_systemDmaData.m_c2[2] = ((float)m_params.m_Color[2].b - (float)m_params.m_Color[1].b) * q1; // m_systemDmaData.m_c2[3] = ((float)m_params.m_Color[2].a - (float)m_params.m_Color[1].a) * q1; } else // else suppress mid-colour { float q = 1.0f / m_params.m_Lifetime; // m_systemDmaData.m_c1[0] = (float)m_params.m_Color[0].r; // m_systemDmaData.m_c1[1] = (float)m_params.m_Color[0].g; // m_systemDmaData.m_c1[2] = (float)m_params.m_Color[0].b; // m_systemDmaData.m_c1[3] = (float)m_params.m_Color[0].a; // m_systemDmaData.m_c2[0] = ((float)m_params.m_Color[2].r - (float)m_params.m_Color[0].r) * q; // m_systemDmaData.m_c2[1] = ((float)m_params.m_Color[2].g - (float)m_params.m_Color[0].g) * q; // m_systemDmaData.m_c2[2] = ((float)m_params.m_Color[2].b - (float)m_params.m_Color[0].b) * q; // m_systemDmaData.m_c2[3] = ((float)m_params.m_Color[2].a - (float)m_params.m_Color[0].a) * q; } // Rotation matrix. m_rotation.Identity(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxNewParticle::plat_destroy( void ) { if( mp_stream ) { delete [] mp_stream; } if( mp_material ) { delete mp_material; } } /***************************************************************************** ** Public Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Nx