//**************************************************************************** //* MODULE: Gfx //* FILENAME: p_nxParticle.cpp //* OWNER: Paul Robinson //* CREATION DATE: 3/27/2002 //**************************************************************************** #include #include "gfx/ngps/nx/render.h" #include "gfx/ngps/nx/dma.h" #include "gfx/ngps/nx/vif.h" #include "gfx/ngps/nx/vu1.h" #include "gfx/ngps/nx/gif.h" #include "gfx/ngps/nx/gs.h" #include "gfx/ngps/nx/line.h" #include #include #include "gfx/ngps/nx/immediate.h" #include "gfx/ngps/nx/vu1code.h" #include "gfx/ngps/nx/mesh.h" #include "gfx/ngps/nx/group.h" #include "gfx/ngps/p_nxparticlenewflat.h" namespace Nx { /******************************************************************/ /* */ /* */ /******************************************************************/ CPs2ParticleNewFlat::CPs2ParticleNewFlat() { } /******************************************************************/ /* */ /* */ /******************************************************************/ CPs2ParticleNewFlat::CPs2ParticleNewFlat( uint32 checksum, int max_streams, uint32 texture_checksum, uint32 blendmode_checksum, int fix ) { m_checksum = checksum; m_max_streams = max_streams; m_num_streams = 0; // Get the texture. Nx::CTexture *p_texture; Nx::CPs2Texture *p_ps2_texture; mp_engine_texture = NULL; p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum ); p_ps2_texture = static_cast( p_texture ); if ( p_ps2_texture ) { mp_engine_texture = p_ps2_texture->GetSingleTexture(); } // Set blendmode. m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix ); // Allocate space for span information & streams. m_num_spans = 1; // Currently defaulting to 1 span. mp_span = new CParticleSpan[m_num_spans]; mp_stream = new CParticleStream[m_max_streams]; mp_newest_stream = mp_stream + m_max_streams - 1; mp_oldest_stream = mp_stream; m_life = 0.5f * (m_life_min + m_life_max); m_emitting = false; // set up system dma data, apart from new coefficients, which are calculated in plat_build_path() m_systemDmaData.m_GScontext[0] = 0x00008004; m_systemDmaData.m_GScontext[1] = 0x10000000; m_systemDmaData.m_GScontext[2] = 0x0000000E; m_systemDmaData.m_GScontext[3] = 0x00000000; *(uint64 *)&m_systemDmaData.m_GScontext[4] = m_blend; m_systemDmaData.m_GScontext[6] = 0x00000042; if (mp_engine_texture) { m_systemDmaData.m_u0 = 8; m_systemDmaData.m_v0 = (mp_engine_texture->GetHeight()<<4) - 8; m_systemDmaData.m_u1 = (mp_engine_texture->GetWidth() <<4) - 8; m_systemDmaData.m_v1 = 8; //m_systemDmaData.m_GScontext[16] = ((mp_engine_texture->GetHeight()<<4) - 8) << 16 | 8; *(uint64 *)&m_systemDmaData.m_GScontext[8] = mp_engine_texture->m_RegTEX0; *(uint64 *)&m_systemDmaData.m_GScontext[12] = mp_engine_texture->m_RegTEX1; } m_systemDmaData.m_GScontext[10] = 0x00000006; m_systemDmaData.m_GScontext[14] = 0x00000014; m_systemDmaData.m_GScontext[16] = 0x0005000B /* | m_alphaCutoff<<4 */; //101 0000 xxxx xxxx 1011 m_systemDmaData.m_GScontext[18] = 0x00000047; m_systemDmaData.m_tagy = 0x60AB4000; m_systemDmaData.m_tagz = 0x00434312; m_mid_time = -1.0f; } /******************************************************************/ /* */ /* */ /******************************************************************/ CPs2ParticleNewFlat::~CPs2ParticleNewFlat() { delete [] mp_span; delete [] mp_stream; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2ParticleNewFlat::plat_get_position( int entry, int list, float * x, float * y, float * z ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2ParticleNewFlat::plat_set_position( int entry, int list, float x, float y, float z ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2ParticleNewFlat::plat_add_position( int entry, int list, float x, float y, float z ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ int CPs2ParticleNewFlat::plat_get_num_particle_colors( void ) { return 1; }; int CPs2ParticleNewFlat::plat_get_num_vertex_lists( void ) { return 0; }; void CPs2ParticleNewFlat::plat_set_sr( int entry, uint8 value ) { m_start_color.r = value; } void CPs2ParticleNewFlat::plat_set_sg( int entry, uint8 value ) { m_start_color.g = value; } void CPs2ParticleNewFlat::plat_set_sb( int entry, uint8 value ) { m_start_color.b = value; } void CPs2ParticleNewFlat::plat_set_sa( int entry, uint8 value ) { m_start_color.a = value >> 1; } void CPs2ParticleNewFlat::plat_set_mr( int entry, uint8 value ) { m_mid_color.r = value; } void CPs2ParticleNewFlat::plat_set_mg( int entry, uint8 value ) { m_mid_color.g = value; } void CPs2ParticleNewFlat::plat_set_mb( int entry, uint8 value ) { m_mid_color.b = value; } void CPs2ParticleNewFlat::plat_set_ma( int entry, uint8 value ) { m_mid_color.a = value >> 1; } void CPs2ParticleNewFlat::plat_set_er( int entry, uint8 value ) { m_end_color.r = value; } void CPs2ParticleNewFlat::plat_set_eg( int entry, uint8 value ) { m_end_color.g = value; } void CPs2ParticleNewFlat::plat_set_eb( int entry, uint8 value ) { m_end_color.b = value; } void CPs2ParticleNewFlat::plat_set_ea( int entry, uint8 value ) { m_end_color.a = value >> 1; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2ParticleNewFlat::plat_render( void ) { CParticleStream *p_stream; int i; //-------------------------------------------------------------------------------------------------------------------- // process the streams if (m_emit_rate && (!m_emitting || (m_emit_rate != 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_emit_rate; mp_newest_stream->m_interval = 1.0f/m_emit_rate; mp_newest_stream->m_oldest_age = 0.0f; mp_newest_stream->m_num_particles = 0; mp_newest_stream->m_seed[0] = rand(); mp_newest_stream->m_seed[1] = rand(); mp_newest_stream->m_seed[2] = rand(); mp_newest_stream->m_seed[3] = rand(); m_emitting = true; } else { m_emitting = false; } } else { m_emitting = m_emit_rate; } if (!m_num_streams) return; // age all streams for (i=0, p_stream=mp_oldest_stream; im_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_life) { // work out number dead int particles_dead = (int)((mp_oldest_stream->m_oldest_age - m_life) * 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 if (mp_engine_texture) { // Mick: If this is textured, we reset the TEX0, TEX1 regs as the texture might move // Since 2D textures are dynamically packed every frame *(uint64 *)&m_systemDmaData.m_GScontext[8] = mp_engine_texture->m_RegTEX0; *(uint64 *)&m_systemDmaData.m_GScontext[12] = mp_engine_texture->m_RegTEX1; } // matrix is system rotation times view transform m_systemDmaData.m_matrix = m_rotation * NxPs2::render::WorldToIntViewport; // in the per-frame setup dma we must have: // dma tag // base // offset // set the group NxPs2::dma::SetList(NxPs2::sGroup::pParticles); // the system and streams will be loaded to a double-buffered input area of VUMem1 // ref the system data, and include an unpack for the system and streams: NxPs2::dma::Tag(NxPs2::dma::ref, (sizeof(CSystemDmaDataFlat)>>4), (uint)&m_systemDmaData); NxPs2::vif::STCYCL(1,1); NxPs2::vif::UNPACK(0, V4_32, (sizeof(CSystemDmaDataFlat)>>4)+m_num_streams*2, REL, SIGNED, 0); // construct a packet with data for each stream NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0); for (i=0,p_stream=mp_oldest_stream; im_oldest_age; ((float *)NxPs2::dma::pLoc)[1] = p_stream->m_interval; ((uint32 *)NxPs2::dma::pLoc)[2] = p_stream->m_num_particles; ((uint32 *)NxPs2::dma::pLoc)[3] = (p_stream==mp_newest_stream) ? 0x8000 : 0; ((uint32 *)NxPs2::dma::pLoc)[4] = p_stream->m_seed[0]; ((uint32 *)NxPs2::dma::pLoc)[5] = p_stream->m_seed[1]; ((uint32 *)NxPs2::dma::pLoc)[6] = p_stream->m_seed[2]; ((uint32 *)NxPs2::dma::pLoc)[7] = p_stream->m_seed[3]; //printf("stream %d, num particles=%d, oldest age=%f\n", s, p_stream->m_num_particles, p_stream->m_oldest_age); // step dma pointer NxPs2::dma::pLoc += 8*4; // count particles NxPs2::render::sTotalNewParticles += p_stream->m_num_particles; } NxPs2::vif::MSCAL(10); // sprites NxPs2::dma::EndTag(); NxPs2::dma::SetList(NxPs2::sGroup::pEpilogue); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPs2ParticleNewFlat::plat_build_path( void ) { float midTime; // set m_life m_life = 0.5f * (m_life_min + m_life_max); // set clamped local midtime variable if (m_mid_time <= 0.0f) { midTime = 0.0f; } else if (m_mid_time >= 100.0f) { midTime = m_life; } else { midTime = m_mid_time * 0.01f * m_life; } // x-component will be overwritten by vu1 code, so can store midtime there *(float *)&m_systemDmaData.m_tagx = midTime; // constant part m_systemDmaData.m_s0x = m_emit_w; m_systemDmaData.m_s0y = 0.0f; m_systemDmaData.m_s0z = m_emit_h; m_systemDmaData.m_s0w = 0.0f; m_systemDmaData.m_c0x = m_pos[0] - 1.5f * m_systemDmaData.m_s0x; m_systemDmaData.m_c0y = m_pos[1] - 1.5f * m_systemDmaData.m_s0y; m_systemDmaData.m_c0z = m_pos[2] - 1.5f * m_systemDmaData.m_s0z; m_systemDmaData.m_c0w = m_sw - 1.5f * m_systemDmaData.m_s0w; // linear part Mth::Vector vel(m_tx,m_ty,m_tz,0.0f); float speed = 60.0f * 0.5 * (m_speed_min + m_speed_max); vel.Normalize(); vel *= speed; m_systemDmaData.m_s1x = speed * Mth::PI/2.0f * m_emit_angle_spread; m_systemDmaData.m_s1y = 60.0f * (m_speed_max - m_speed_min); m_systemDmaData.m_s1z = speed * Mth::PI/2.0f * m_emit_angle_spread; m_systemDmaData.m_s1w = 0.0f; m_systemDmaData.m_c1x = vel[0] - 1.5f * m_systemDmaData.m_s1x; m_systemDmaData.m_c1y = vel[1] - 1.5f * m_systemDmaData.m_s1y; m_systemDmaData.m_c1z = vel[2] - 1.5f * m_systemDmaData.m_s1z; m_systemDmaData.m_c1w = (m_ew-m_sw) / m_life - 1.5f * m_systemDmaData.m_s1w; // quadratic part m_systemDmaData.m_s2x = 0.0f; m_systemDmaData.m_s2y = 0.0f; m_systemDmaData.m_s2z = 0.0f; m_systemDmaData.m_s2w = 0.0f; m_systemDmaData.m_c2x = m_fx*0.5f*(60.0f*60.0f) - 1.5f * m_systemDmaData.m_s2x; m_systemDmaData.m_c2y = m_fy*0.5f*(60.0f*60.0f) - 1.5f * m_systemDmaData.m_s2y; m_systemDmaData.m_c2z = m_fz*0.5f*(60.0f*60.0f) - 1.5f * m_systemDmaData.m_s2z; m_systemDmaData.m_c2w = 0.0f - 1.5f * m_systemDmaData.m_s2w; // cubic part //m_systemDmaData.m_s3x = 0.0f; //m_systemDmaData.m_s3y = 0.0f; //m_systemDmaData.m_s3z = 0.0f; //m_systemDmaData.m_s3w = 0.0f; //m_systemDmaData.m_c3x = 0.0f - 1.5f * m_systemDmaData.m_s3x; //m_systemDmaData.m_c3y = 0.0f - 1.5f * m_systemDmaData.m_s3y; //m_systemDmaData.m_c3z = 0.0f - 1.5f * m_systemDmaData.m_s3z; //m_systemDmaData.m_c3w = 0.0f - 1.5f * m_systemDmaData.m_s3w; // colour if (m_mid_time >= 0.0f) // if using mid-colour { float q0 = 1.0f / midTime; float q1 = 1.0f / (m_life - midTime); m_systemDmaData.m_c0r = ((float)m_mid_color.r - (float)m_start_color.r) * q0; m_systemDmaData.m_c0g = ((float)m_mid_color.g - (float)m_start_color.g) * q0; m_systemDmaData.m_c0b = ((float)m_mid_color.b - (float)m_start_color.b) * q0; m_systemDmaData.m_c0a = ((float)m_mid_color.a - (float)m_start_color.a) * q0; m_systemDmaData.m_c1r = (float)m_mid_color.r; m_systemDmaData.m_c1g = (float)m_mid_color.g; m_systemDmaData.m_c1b = (float)m_mid_color.b; m_systemDmaData.m_c1a = (float)m_mid_color.a; m_systemDmaData.m_c2r = ((float)m_end_color.r - (float)m_mid_color.r) * q1; m_systemDmaData.m_c2g = ((float)m_end_color.g - (float)m_mid_color.g) * q1; m_systemDmaData.m_c2b = ((float)m_end_color.b - (float)m_mid_color.b) * q1; m_systemDmaData.m_c2a = ((float)m_end_color.a - (float)m_mid_color.a) * q1; } else // else suppress mid-colour { float q = 1.0f / m_life; m_systemDmaData.m_c1r = (float)m_start_color.r; m_systemDmaData.m_c1g = (float)m_start_color.g; m_systemDmaData.m_c1b = (float)m_start_color.b; m_systemDmaData.m_c1a = (float)m_start_color.a; m_systemDmaData.m_c2r = ((float)m_end_color.r - (float)m_start_color.r) * q; m_systemDmaData.m_c2g = ((float)m_end_color.g - (float)m_start_color.g) * q; m_systemDmaData.m_c2b = ((float)m_end_color.b - (float)m_start_color.b) * q; m_systemDmaData.m_c2a = ((float)m_end_color.a - (float)m_start_color.a) * q; } // rotation matrix, hardwired to the identity for now m_rotation.Identity(); // temp test //m_rotation[0][0] = 0.8; //m_rotation[0][1] = 0.6; //m_rotation[1][0] = -0.6; //m_rotation[1][1] = 0.8; // invert rotation and apply to spatial params // leaving this code a bit shoddy and slow until full transition to new-style params Mth::Matrix mat; mat=m_rotation; mat.Transpose(); Mth::Vector vec; vec[0] = m_systemDmaData.m_c0x + 1.5f * m_systemDmaData.m_s0x; vec[1] = m_systemDmaData.m_c0y + 1.5f * m_systemDmaData.m_s0y; vec[2] = m_systemDmaData.m_c0z + 1.5f * m_systemDmaData.m_s0z; vec[3] = 0; vec *= mat; m_systemDmaData.m_c0x = vec[0] - 1.5f * m_systemDmaData.m_s0x; m_systemDmaData.m_c0y = vec[1] - 1.5f * m_systemDmaData.m_s0y; m_systemDmaData.m_c0z = vec[2] - 1.5f * m_systemDmaData.m_s0z; vec[0] = m_systemDmaData.m_c1x + 1.5f * m_systemDmaData.m_s1x; vec[1] = m_systemDmaData.m_c1y + 1.5f * m_systemDmaData.m_s1y; vec[2] = m_systemDmaData.m_c1z + 1.5f * m_systemDmaData.m_s1z; vec[3] = 0; vec *= mat; m_systemDmaData.m_c1x = vec[0] - 1.5f * m_systemDmaData.m_s1x; m_systemDmaData.m_c1y = vec[1] - 1.5f * m_systemDmaData.m_s1y; m_systemDmaData.m_c1z = vec[2] - 1.5f * m_systemDmaData.m_s1z; vec[0] = m_systemDmaData.m_c2x + 1.5f * m_systemDmaData.m_s2x; vec[1] = m_systemDmaData.m_c2y + 1.5f * m_systemDmaData.m_s2y; vec[2] = m_systemDmaData.m_c2z + 1.5f * m_systemDmaData.m_s2z; vec[3] = 0; vec *= mat; m_systemDmaData.m_c2x = vec[0] - 1.5f * m_systemDmaData.m_s2x; m_systemDmaData.m_c2y = vec[1] - 1.5f * m_systemDmaData.m_s2y; m_systemDmaData.m_c2z = vec[2] - 1.5f * m_systemDmaData.m_s2z; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CParticleStream::AdvanceSeed(int num_places) { for (int i=0; i>4 ^ m_seed[3]>>22) & 1); m_seed[1] ^= m_seed[0]; m_seed[2] ^= m_seed[1]; m_seed[3] ^= m_seed[2]; } } /******************************************************************/ /* */ /* */ /******************************************************************/ int CPs2ParticleNewFlat::GetNumParticles() { int i, total=0; CParticleStream *p_stream; for (i=0, p_stream=mp_oldest_stream; im_num_particles; // step pointer within cyclic buffer p_stream++; if (p_stream == mp_stream + m_max_streams) { p_stream = mp_stream; } } return total; } } // Nx