/* Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /******************************************************* QMB: new particle engine almost ready for release!! done: just need to make all the old functions call the new ones maybe add some more documentation work out what needs to be done to add to other quake engine needs 32bit texture loading (from www.quakesrc.org) ******************************************************* Shove this in to glquake insted of whats there at the moment for particles //QMB: particles //types of particles typedef enum { p_sparks, p_smoke, p_fire, p_blood, p_chunks, p_smoke_fade, p_custom } p_type_t; //QMB: particles //custom movement effects typedef enum { pt_static, pt_grav, pt_slowgrav, pt_rise, pt_slowrise, pt_explode, pt_explode2, pt_blob, pt_blob2, pt_fire } part_move_t; //QMB: particles //particle struct typedef struct particle_s { // driver-usable fields vec3_t org; vec3_t colour; //float color (stops the need for looking up the 8bit to 24bit everytime // drivers never touch the following fields struct particle_s *next; vec3_t vel; float ramp; //sort of diffrent purpose float die; float start; int hit; float size; float delay; part_move_t type; } particle_t; //new particles //add a trail of any particle type void AddTrail(vec3_t start, vec3_t end, int type, float time); void AddTrailColor(vec3_t start, vec3_t end, int type, float time, vec3_t colour); //QMB: end //add a particle or two //Times: smoke=3, spark=2, blood=3, chunk=4 void AddParticle(vec3_t org, int count, float size, float time, int type); void AddParticleColor(vec3_t org, int count, float size, float time, int type, vec3_t colour); *******************************************************/ #include "quakedef.h" //#include "r_local.h" //default max # of particles at one time //#define MAX_PARTICLES 2048 #define MAX_PARTICLES 4096 //no fewer than this no matter what's on the command line #define ABSOLUTE_MIN_PARTICLES 512 //colour ramps: need to be changed to 24-bit colour int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61}; int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66}; int ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3}; //sin and cos tables for the particle triangle fans float sint[7], cost[7]; //linked lists pointers for the first of the active, free and the a spare list particle_t *active_particles, *free_particles, *particles; particle_t *smoke, *fire, *blood, *sparks, *chunks, *env, *trails; //Holder of the particle texture int part_tex, smoke_tex, trail_tex; //number of particles in total int r_numparticles; int r_numdelayadds; int numParticles; //vec3_t r_pright, r_pup, r_ppn; cvar_t gl_texpart = {"gl_texpart","1"}; cvar_t gl_trail = {"gl_trail","1"}; cvar_t r_particle_dist = {"r_particle_dist","20"}; //internal functions void R_UpdateParticles(void); void TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal); void R_UpdateAll(void); void MakeParticleTexure(void); void DrawTriFans(void); void DrawTexQuad(void); void AddFadeSmoke(vec3_t org, int count); void DelayAddChunk(vec3_t org, int count, vec3_t colour, float time); /* =============== R_InitParticles =============== */ void R_InitParticles (void) { int i, a; i = COM_CheckParm ("-particles"); if (i){ r_numparticles = (int)(Q_atoi(com_argv[i+1])); if (r_numparticles < ABSOLUTE_MIN_PARTICLES) r_numparticles = ABSOLUTE_MIN_PARTICLES; } else{ r_numparticles = MAX_PARTICLES; } particles = (particle_t *)Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles"); //calculate sin/cos tables for (i=0;i<7;i++) { a = i/7.0 * M_PI*2; sint[i]=sin(a); cost[i]=cos(a); } R_ClearParticles(); MakeParticleTexure(); Cvar_RegisterVariable (&gl_texpart); Cvar_RegisterVariable (&gl_trail); Cvar_RegisterVariable (&r_particle_dist); } #ifdef QUAKE2 void R_DarkFieldParticles (entity_t *ent) { int i, j, k; particle_t *p; float vel; vec3_t dir; vec3_t org; byte *colourByte; org[0] = ent->origin[0]; org[1] = ent->origin[1]; org[2] = ent->origin[2]; for (i=-16 ; i<16 ; i+=8) for (j=-16 ; j<16 ; j+=8) for (k=0 ; k<32 ; k+=8) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = 0.2 + (rand()&7) * 0.02; p->color = 150 + rand()%6; p->type = pt_slowgrav; dir[0] = j*8; dir[1] = i*8; dir[2] = k*8; p->org[0] = org[0] + i + (rand()&3); p->org[1] = org[1] + j + (rand()&3); p->org[2] = org[2] + k + (rand()&3); VectorNormalize (dir); vel = 50 + (rand()&63); VectorScale (dir, vel, p->vel); } } #endif /* =============== R_EntityParticles =============== */ #define NUMVERTEXNORMALS 162 extern float r_avertexnormals[NUMVERTEXNORMALS][3]; vec3_t avelocities[NUMVERTEXNORMALS]; float beamlength = 16; vec3_t avelocity = {23, 7, 3}; float partstep = 0.01f; float timescale = 0.01f; //What the hell is all this stuff for???? void R_EntityParticles (entity_t *ent) { int count; int i; particle_t *p; float angle; float sr, sp, sy, cr, cp, cy; vec3_t forward; float dist; byte *colourByte; dist = 64; count = 50; if (!avelocities[0][0]) { for (i=0 ; inext; p->next = active_particles; active_particles = p; p->hit = 0; p->die = cl.time + 0.01; colourByte = (byte *)&d_8to24table[0x6f]; p->colour[0] = colourByte[0]/255.0; p->colour[1] = colourByte[1]/255.0; p->colour[2] = colourByte[2]/255.0; p->type = pt_explode; p->org[0] = ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength; p->org[1] = ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength; p->org[2] = ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength; } Con_Printf ("EntityParticles"); } /* =============== R_ClearParticles =============== */ void R_ClearParticles (void) { int i; free_particles = &particles[0]; active_particles = smoke = chunks = fire = blood = sparks = env = trails = NULL; for (i=0 ;inext; p->next = active_particles; active_particles = p; p->hit = 0; p->die = cl.time + 99999; colourByte = (byte *)&d_8to24table[(-c)&15]; p->colour[0] = colourByte[0]/255.0; p->colour[1] = colourByte[1]/255.0; p->colour[2] = colourByte[2]/255.0; p->type = pt_static; VectorCopy (vec3_origin, p->vel); VectorCopy (org, p->org); } fclose (f); Con_Printf ("%i points read\n", c); } /* =============== R_ParseParticleEffect Parse an effect out of the server message =============== */ void R_ParseParticleEffect (void) { vec3_t org, dir; int i, count, msgcount, color; for (i=0 ; i<3 ; i++) org[i] = MSG_ReadCoord (); for (i=0 ; i<3 ; i++) dir[i] = MSG_ReadChar () * (1.0/16); msgcount = MSG_ReadByte (); color = MSG_ReadByte (); if (msgcount == 255) count = 1024; else count = msgcount; R_RunParticleEffect (org, dir, color, count); } /*=============== R_ParticleExplosion ===============*/ void R_ParticleExplosion (vec3_t org) { AddParticle(org, 64, 300, 1.5f, p_sparks); AddParticle(org, 32, 200, 1.5f, p_sparks); AddParticle(org, 20, 25, 2.0f, p_fire); } /*=============== R_ParticleExplosion2 ===============*/ //Needs to be made to call new functions so that old ones can be removed void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength) { vec3_t colour; byte *colourByte; colourByte = (byte *)&d_8to24table[colorStart]; colour[0] = (float)colourByte[0]/255.0; colour[1] = (float)colourByte[1]/255.0; colour[2] = (float)colourByte[2]/255.0; AddParticleColor(org, 64, 200, 1.5f, p_sparks, colour); AddParticle(org, 64, 1, 3, p_smoke); } /*=============== R_BlobExplosion ===============*/ //also do colored fires void R_BlobExplosion (vec3_t org) { vec3_t colour; colour[0]=0; colour[1]=0; colour[2]=1; AddParticleColor(org, 64, 200, 1, p_sparks, colour); } /*=============== R_RunParticleEffect ===============*/ void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count) { byte *colourByte; vec3_t colour; colourByte = (byte *)&d_8to24table[color]; colour[0] = colourByte[0]/255.0; colour[1] = colourByte[1]/255.0; colour[2] = colourByte[2]/255.0; if (count == 15) { AddParticleColor(org, 10, 1, 4, p_chunks, colour); AddParticle(org, 1, 100, 1.0f, p_sparks); }else if (count == 10) { AddParticleColor(org, 10, 1, 4, p_chunks, colour); AddParticle(org, 3, 100, 1.0f, p_sparks); }else if (count == 20) { AddParticleColor(org, 10, 1, 4, p_chunks, colour); AddParticle(org, 10, 100, 1.0f, p_sparks); }else if (count == 30) { AddParticleColor(org, 10, 1, 4, p_chunks, colour); AddParticle(org, 5, 100, 1.0f, p_sparks); }else if (count == 1024) R_ParticleExplosion(org); else { AddParticleColor(org, count, 1, 3, p_blood, colour); } } /*=============== R_LavaSplash ===============*/ //Need to find out when this is called void R_LavaSplash (vec3_t org) { AddParticle(org, 1500, 1000, 10, p_sparks); AddParticle(org, 35, 55, 5, p_fire); } /*=============== R_TeleportSplash ===============*/ //Need to be changed so that they spin down into the ground //whould look very cool //maybe coloured blood (new type?) void R_TeleportSplash (vec3_t org) { vec3_t colour; colour[0]=0.9f; colour[1]=0.9f; colour[2]=0.9f; AddParticleColor(org, 256, 200, 0.1f, p_sparks, colour); } //Should be made to call the new functions to keep compatablity void R_RocketTrail (vec3_t start, vec3_t end, int type) { vec3_t colour; switch (type) { case 0: // rocket trail AddTrail(start, end, p_fire, 0.1f); AddTrail(start, end, p_smoke, 1); break; case 1: // smoke smoke AddTrail(start, end, p_smoke, 1); break; case 2: // blood AddTrail(start, end, p_blood, 2); break; case 3: //tracer 1 colour[0]=0; colour[1]=1; colour[2]=0; AddTrailColor (start, end, p_blood, 2, colour); break; case 5: // tracer 2 colour[0]=1; colour[1]=0.85f; colour[2]=0; AddTrailColor (start, end, p_blood, 2, colour); break; case 4: // slight blood AddParticle(end, 20, 1, 2, p_blood); break; case 6: // voor trail colour[0]=1; colour[1]=0; colour[2]=0.75f; AddTrailColor (start, end, p_blood, 2, colour); break; } } /* =============== R_DrawParticles =============== */ extern cvar_t sv_gravity; void R_DrawParticles (void) { //See if they can be compressed or made neater if (cl.time != cl.oldtime) R_UpdateAll(); glDepthMask(0); // glEnable (GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glShadeModel (GL_SMOOTH); DrawTexQuad(); glDepthMask(1); // glDisable (GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } void DrawTexQuad(void) { particle_t *p; vec3_t v; int i; float alphaF; int size; glDisable (GL_TEXTURE_2D); //Draw sparks particles for (p=sparks ; p ; p=p->next) { glBegin (GL_TRIANGLE_FAN); alphaF = (1-(cl.time-p->start)/(p->die-p->start)); glColor4f (p->colour[0],p->colour[1],p->colour[2],alphaF); glVertex3fv (p->org); glColor4f (p->colour[0]/2,p->colour[1]/2,p->colour[2]/2,0); for (i=7 ; i>=0 ; i--) { v[0] = p->org[0] - p->vel[0]/8 + vright[0]*cost[i%7]*p->size + vup[0]*sint[i%7]*p->size; v[1] = p->org[1] - p->vel[1]/8 + vright[1]*cost[i%7]*p->size + vup[1]*sint[i%7]*p->size; v[2] = p->org[2] - p->vel[2]/8 + vright[2]*cost[i%7]*p->size + vup[2]*sint[i%7]*p->size; glVertex3fv (v); } glEnd (); } glEnable (GL_TEXTURE_2D); GL_Bind(part_tex); glBegin (GL_QUADS); /* //Draw other particles for (p=active_particles ; p ; p=p->next) { colour = (byte *)&d_8to24table[(int)p->color]; glColor4ub (colour[0],colour[1],colour[2],(byte)(p->ramp*255)); glTexCoord2f (0, 1); VectorMA (p->org, -size, vup, v); VectorMA (v, -size, vright, v); glVertex3fv (v); glTexCoord2f (0, 0); VectorMA (p->org, size, vup, v); VectorMA (v, -size, vright, v); glVertex3fv (v); glTexCoord2f (1, 0); VectorMA (p->org, size, vup, v); VectorMA (v, size, vright, v); glVertex3fv (v); glTexCoord2f (1, 1); VectorMA (p->org, -size, vup, v); VectorMA (v, size, vright, v); glVertex3fv (v); }*/ //Draw blood particles size = 1.2; for (p=blood ; p ; p=p->next) { glColor4f (p->colour[0]*p->ramp,p->colour[1]*p->ramp,p->colour[2]*p->ramp,p->ramp); glTexCoord2f (0, 1); VectorMA (p->org, -p->size*p->ramp, vup, v); VectorMA (v, -p->size*p->ramp, vright, v); glVertex3fv (v); glTexCoord2f (0, 0); VectorMA (p->org, p->size*p->ramp, vup, v); VectorMA (v, -p->size*p->ramp, vright, v); glVertex3fv (v); glTexCoord2f (1, 0); VectorMA (p->org, p->size*p->ramp, vup, v); VectorMA (v, p->size*p->ramp, vright, v); glVertex3fv (v); glTexCoord2f (1, 1); VectorMA (p->org, -p->size*p->ramp, vup, v); VectorMA (v, p->size*p->ramp, vright, v); glVertex3fv (v); } //Draw fire particles //size = 25; for (p=fire ; p ; p=p->next) { VectorSubtract (p->org, r_origin, v); if (Length (v) > r_particle_dist.value*3) { glColor4f (p->colour[0]*p->ramp,p->colour[1]*p->ramp,p->colour[2]*p->ramp,p->ramp); glTexCoord2f (0, 1); VectorMA (p->org, -p->size, vup, v); VectorMA (v, -p->size, vright, v); glVertex3fv (v); glTexCoord2f (0, 0); VectorMA (p->org, p->size, vup, v); VectorMA (v, -p->size, vright, v); glVertex3fv (v); glTexCoord2f (1, 0); VectorMA (p->org, p->size, vup, v); VectorMA (v, p->size, vright, v); glVertex3fv (v); glTexCoord2f (1, 1); VectorMA (p->org, -p->size, vup, v); VectorMA (v, p->size, vright, v); glVertex3fv (v); } } //Draw chunks particles size=1; for (p=chunks ; p ; p=p->next) { glColor4f (p->colour[0]*p->ramp,p->colour[1]*p->ramp,p->colour[2]*p->ramp,p->ramp); glTexCoord2f (0, 1); VectorMA (p->org, -size, vup, v); VectorMA (v, -size, vright, v); glVertex3fv (v); glTexCoord2f (0, 0); VectorMA (p->org, size, vup, v); VectorMA (v, -size, vright, v); glVertex3fv (v); glTexCoord2f (1, 0); VectorMA (p->org, size, vup, v); VectorMA (v, size, vright, v); glVertex3fv (v); glTexCoord2f (1, 1); VectorMA (p->org, -size, vup, v); VectorMA (v, size, vright, v); glVertex3fv (v); } glEnd (); //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GL_Bind(smoke_tex); glBegin (GL_QUADS); //Draw smoke particles for (p=smoke ; p ; p=p->next) { VectorSubtract (p->org, r_origin, v); if (Length (v) > r_particle_dist.value) { glColor4f (p->colour[0],p->colour[1],p->colour[2],p->ramp/4); glTexCoord2f (0, 1); VectorMA (p->org, -p->size*(2-p->ramp), vup, v); VectorMA (v, -p->size*(2-p->ramp), vright, v); glVertex3fv (v); glTexCoord2f (0, 0); VectorMA (p->org, p->size*(2-p->ramp), vup, v); VectorMA (v, -p->size*(2-p->ramp), vright, v); glVertex3fv (v); glTexCoord2f (1, 0); VectorMA (p->org, p->size*(2-p->ramp), vup, v); VectorMA (v, p->size*(2-p->ramp), vright, v); glVertex3fv (v); glTexCoord2f (1, 1); VectorMA (p->org, -p->size*(2-p->ramp), vup, v); VectorMA (v, p->size*(2-p->ramp), vright, v); glVertex3fv (v); } } glEnd (); GL_Bind(trail_tex); glBegin (GL_QUADS); //Draw trails for (p=trails ; p ; p=p->next) { glColor4f (p->colour[0],p->colour[1],p->colour[2],p->ramp); glTexCoord2f (0, 1); VectorMA (p->org, p->size, vup, v); VectorMA (p->org, p->size, vright, v); glVertex3fv (v); glTexCoord2f (0, 0); //VectorMA (p->org, p->size, vup, v); VectorMA (p->org, -p->size, vright, v); glVertex3fv (v); glTexCoord2f (1, 0); //VectorMA (p->org2, -p->size, vup, v); VectorMA (p->org2, -p->size, vright, v); glVertex3fv (v); glTexCoord2f (1, 1); VectorMA (p->org2, p->size, vup, v); VectorMA (p->org2, p->size, vright, v); glVertex3fv (v); // reverse texture - for upside down trails, backwards textures are culled neway. glTexCoord2f (0, 1); VectorMA (p->org, p->size, vup, v); VectorMA (p->org2, p->size, vright, v); glVertex3fv (v); glTexCoord2f (0, 0); //VectorMA (p->org, p->size, vup, v); VectorMA (p->org2, -p->size, vright, v); glVertex3fv (v); glTexCoord2f (1, 0); //VectorMA (p->org2, -p->size, vup, v); VectorMA (p->org, -p->size, vright, v); glVertex3fv (v); glTexCoord2f (1, 1); VectorMA (p->org2, p->size, vup, v); VectorMA (p->org, p->size, vright, v); glVertex3fv (v); } glEnd(); } //p_sparks, p_smoke, p_smoke_fade, p_fire, p_blood, p_chunks void R_UpdateAll(void) { particle_t *p, *kill; float grav, frametime, dist; int j; vec3_t oldOrg, stop, normal; //Work out gravity and time frametime = (cl.time - cl.oldtime)*1.5; grav = frametime * 9.8*(sv_gravity.value/800); for (j=p_sparks;j<=p_chunks;j++) { if (j==p_smoke_fade) j++; switch (j) { case(p_smoke): p=smoke; break; case(p_sparks): p=sparks; break; case(p_fire): p=fire; break; case(p_blood): p=blood; break; case(p_chunks): p=chunks; break; } for (; p; p=p->next){ //Kill off dead particles for (kill = p->next ;kill; kill = p->next) { if (kill->die <= cl.time) // || CONTENTS_SOLID == SV_PointContents (kill->org) { p->next = kill->next; kill->next = free_particles; free_particles = kill; if (j==p_fire) AddParticle(kill->org,1,4,1,p_smoke); numParticles--; //AddFadeSmoke(kill->org, 1); continue; } break; } } } for (p=smoke; p; p=p->next){ //Update smoke movement VectorCopy(p->org, oldOrg); p->org[0] += p->vel[0]*frametime + (rand()%4-1.5)/16*frametime; p->org[1] += p->vel[1]*frametime + (rand()%4-1.5)/16*frametime; p->org[2] += p->vel[2]*frametime + (rand()%4-1.5)/16*frametime; p->vel[2] += grav; TraceLineN(oldOrg, p->org, stop, normal); if ((stop != p->org)&&(Length(stop)!=0)) p->die = 0; if (p->type) { if (cl.time > p->start) p->type=0; p->ramp = 1+(1-(cl.time-p->delay)/(p->start-p->delay)); }else{ p->ramp = (1-(cl.time-p->start)/(p->die-p->start)); p->size += p->ramp/16; } //Kill off dead particles for (kill = p->next ;kill; kill = p->next) { if (kill->die <= cl.time) { p->next = kill->next; kill->next = free_particles; free_particles = kill; numParticles--; continue; } break; } } for (p=fire; p; p=p->next){ //Update fire movement VectorCopy(p->org, oldOrg); p->org[0] += p->vel[0]*frametime; p->org[1] += p->vel[1]*frametime; p->org[2] += p->vel[2]*frametime; TraceLineN(oldOrg, p->org, stop, normal); if ((stop != p->org)&&(Length(stop)!=0)) { dist = DotProduct(p->vel, normal) * -2; VectorMA(p->vel, dist, normal, p->vel); VectorCopy(stop, p->org); } p->ramp = (1-((cl.time-p->start)/(p->die-p->start))*0.8); //Kill off dead particles for (kill = p->next ;kill; kill = p->next) { if (kill->die <= cl.time) { p->next = kill->next; kill->next = free_particles; free_particles = kill; AddParticle(kill->org,1,4,1,p_smoke); numParticles--; //AddFadeSmoke(kill->org, 1); continue; } break; } } for (p=chunks; p; p=p->next){ //Update chunk movement VectorCopy(p->org, oldOrg); p->org[0] += p->vel[0]*frametime; p->org[1] += p->vel[1]*frametime; p->org[2] += p->vel[2]*frametime; p->vel[2] -= grav*8; TraceLineN(oldOrg, p->org, stop, normal); if ((stop != p->org)&&(Length(stop)!=0)) { dist = DotProduct(p->vel, normal) * -1.3; VectorMA(p->vel, dist, normal, p->vel); VectorCopy(stop, p->org); } p->ramp = (1-(cl.time-p->start)/(p->die-p->start)); //Kill off dead particles for (kill = p->next;kill;kill = p->next) { if (kill->die <= cl.time) { p->next = kill->next; kill->next = free_particles; free_particles = kill; numParticles--; continue; } break; } } for (p=sparks; p; p=p->next){ //Update spark movement VectorCopy(p->org, oldOrg); p->org[0] += p->vel[0]*frametime; p->org[1] += p->vel[1]*frametime; p->org[2] += p->vel[2]*frametime; p->vel[2] -= grav*32; TraceLineN(oldOrg, p->org, stop, normal); if ((stop != p->org)&&(Length(stop)!=0)) { dist = DotProduct(p->vel, normal) * -1.5; VectorMA(p->vel, dist, normal, p->vel); VectorCopy(stop, p->org); if (rand()%100-90>0) p->die = cl.time-1; } p->ramp = (1-(cl.time-p->start)/(p->die-p->start)); //Kill off dead particles for (kill = p->next ;kill; kill = p->next) { if (kill->die <= cl.time) { p->next = kill->next; kill->next = free_particles; free_particles = kill; numParticles--; continue; } break; } } for (p=blood; p; p=p->next){ //Update blood movement if (p->hit == 0){ VectorCopy(p->org, oldOrg); p->org[0] += p->vel[0]*frametime; p->org[1] += p->vel[1]*frametime; p->org[2] += p->vel[2]*frametime; p->vel[2] -= grav*8; TraceLineN(oldOrg, p->org, stop, normal); if ((stop != p->org)&&(Length(stop)!=0)) { dist = DotProduct(p->vel, normal) * -0.1; VectorMA(p->vel, dist, normal, p->vel); VectorCopy(stop, p->org); if (Length(p->vel)<0.2) p->hit=1; } } p->ramp = (1-(cl.time-p->start)/(p->die-p->start)); //Kill off dead particles for (kill = p->next ;kill; kill = p->next) { if (kill->die <= cl.time) { p->next = kill->next; kill->next = free_particles; free_particles = kill; numParticles--; continue; } break; } } for (p=trails; p; p=p->next){ if (p->type == pt_grav){ p->vel[2] -= grav*10; } VectorCopy(p->org, oldOrg); p->org[0] += p->vel[0]*frametime; p->org[1] += p->vel[1]*frametime; p->org[2] += p->vel[2]*frametime; TraceLineN(oldOrg, p->org, stop, normal); if ((stop != p->org)&&(Length(stop)!=0)) { dist = DotProduct(p->vel, normal) * -0.1; VectorMA(p->vel, dist, normal, p->vel); VectorCopy(stop, p->org); if (Length(p->vel)<0.2) p->hit=1; } VectorCopy(p->org2, oldOrg); p->org2[0] += p->vel[0]*frametime; p->org2[1] += p->vel[1]*frametime; p->org2[2] += p->vel[2]*frametime; TraceLineN(oldOrg, p->org2, stop, normal); if ((stop != p->org2)&&(Length(stop)!=0)) { dist = DotProduct(p->vel, normal) * -0.1; VectorMA(p->vel, dist, normal, p->vel); VectorCopy(stop, p->org2); if (Length(p->vel)<0.2) p->hit=1; } p->ramp = (1-(cl.time-p->start)/(p->die-p->start)); //Kill off dead particles for (kill = p->next ;kill; kill = p->next) { if (kill->die <= cl.time) { p->next = kill->next; kill->next = free_particles; free_particles = kill; numParticles--; continue; } break; } } } /** TraceLineN * same as the TraceLine but returns the normal as well * which is needed for bouncing particles */ void TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal) { trace_t trace; memset (&trace, 0, sizeof(trace)); SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace); VectorCopy (trace.endpos, impact); VectorCopy (trace.plane.normal, normal); } /* Fire: Explosion, Rocket, Flame Smoke: Rocket, Gren trail, Explosion smoke Sparks: Explosion, Gunshot Chunks: Gunshot Blood: Gunshot, trail Enviromental: Snow, Rain Trails: Rail trail, Lightning(?) */ /** AddParticle * Just calls AddParticleColor with the default color variable for that particle */ void AddParticle(vec3_t org, int count, float size, float time, int type) { //Times: smoke=3, spark=2, blood=3, chunk=4 vec3_t colour; switch (type) { case (p_smoke): colour[0] = colour[1] = colour[2] = (rand()%255)/255.0; break; case (p_sparks): colour[0] = 1; colour[1] = 0.5; colour[2] = 0; break; case (p_fire): colour[0] = 1; colour[1] = 0.55f; colour[2] = 0; break; case (p_blood): colour[0] = (rand()%128)/256.0+0.5;; colour[1] = 0; colour[2] = 0; break; case (p_chunks): colour[0] = colour[1] = colour[2] = (rand()%182)/255.0; } AddParticleColor(org, count, size, time, type, colour); } //same as addparticle with a colour var /** AddParticleColor * This is where it all happends, well not really this is where * most of the particles (and soon all) are added execpt for trails */ void AddParticleColor(vec3_t org, int count, float size, float time, int type, vec3_t colour) { particle_t *p; vec3_t stop, normal; int i; float tempSize; for (i=0 ; (inext; //add the particle to the correct one p->size = size; switch (type) { case (p_fire): p->next = fire; fire = p; //pos p->org[0] = org[0] + ((rand()%80)-40)*(size/25); p->org[1] = org[1] + ((rand()%80)-40)*(size/25); p->org[2] = org[2] + ((rand()%80)-40)*(size/25); TraceLineN(org, p->org, stop, normal); if (Length(stop) != 0) VectorCopy(stop, p->org); //velocity p->vel[0] = ((rand()%40)-20)*(size/25); p->vel[1] = ((rand()%40)-20)*(size/25); p->vel[2] = ((rand()%40)-20)*(size/25); break; case (p_sparks): p->next = sparks; sparks = p; p->size = 1; //pos VectorCopy(org, p->org); //velocity tempSize = size * 2; p->vel[0] = (rand()%(int)tempSize)-((int)tempSize/2); p->vel[1] = (rand()%(int)tempSize)-((int)tempSize/2); p->vel[2] = (rand()%(int)tempSize); //-((int)size/2); break; case (p_smoke): p->next = smoke; smoke = p; //pos p->org[0] = org[0] + ((rand()%30)-15)/2; p->org[1] = org[1] + ((rand()%30)-15)/2; p->org[2] = org[2] + ((rand()%30)-15)/2; TraceLineN(org, p->org, stop, normal); if (Length(stop) != 0) VectorCopy(stop, p->org); //velocity p->vel[0] = ((rand()%10)-5)/20; p->vel[1] = ((rand()%10)-5)/20; p->vel[2] = ((rand()%10)-5)/20; break; case (p_blood): p->next = blood; blood = p; p->size = size * (rand()%20)/5; //pos VectorCopy(org, p->org); //p->org[0] = org[0] + ((rand()%30)-15)/2; //p->org[1] = org[1] + ((rand()%30)-15)/2; //p->org[2] = org[2] + ((rand()%30)-15)/2; TraceLineN(org, p->org, stop, normal); if (Length(stop) != 0) VectorCopy(stop, p->org); //velocity p->vel[0] = (rand()%40)-20; p->vel[1] = (rand()%40)-20; p->vel[2] = (rand()%40)-20; break; case (p_chunks): p->next = chunks; chunks = p; //pos VectorCopy(org, p->org); //velocity p->vel[0] = (rand()%40)-20; p->vel[1] = (rand()%40)-20; p->vel[2] = (rand()%40)-5; break; } p->hit = 0; p->start = cl.time; p->die = cl.time + time; p->type = 0; VectorCopy(colour, p->colour); numParticles++; } } /** DelayAddChunk * This is to do effects like max payne where the chunks from * a bullet hole are created from there for a few seconds * basicly it will slowly add chunks over a period of time */ void DelayAddChunk(vec3_t org, int count, vec3_t colour, float time) { //as you can see its not here yet //i hope to get it in by the next version } /** AddFadeSmoke * Specal type of smoke which is added when a fire * particle dies this is how the rocket trail is made * the somke has a quick fade in before acting normaly. * This should also work for particle fires if i implement them */ void AddFadeSmoke(vec3_t org, int count) { particle_t *p; int i; for (i=0 ; (inext; p->next = smoke; smoke = p; p->hit = 0; p->delay = cl.time; p->start = cl.time + 0.1; p->die = cl.time + 1.1; p->type = 1; p->size = 4; p->ramp = 2; p->colour[0] = p->colour[1] = p->colour[2] = (rand()%255)/255.0; p->org[0] = org[0]; p->org[1] = org[1]; p->org[2] = org[2]; p->vel[0] = ((rand()%10)-5)/20; p->vel[1] = ((rand()%10)-5)/20; p->vel[2] = ((rand()%10)-5)/20; } } /** AddEnv * This is ment to add enviroment effects when its * finished but currently its not working ill add it soon * -note there is the psudo code of how it will work there * if you do implemnt it send me the code */ void AddEnv(vec3_t org, vec3_t org2, int count) { //From top corner (org) to bottom corner (org2) //(org2 - org)*(1,0,0)->A //(org2 - org)*(0,1,0)->B //for i=0 to count //add effeect at: org + rand()*A + rand()*B //set take away effect when it gets below org2[3] } /** AddTrail * Calls AddTrailColor with the default colours */ void AddTrail(vec3_t start, vec3_t end, int type, float time) { vec3_t colour; //colour set when first update called (check if needs to be fixed) switch (type) { case (p_smoke): colour[0] = colour[1] = colour[2] = (rand()%128)/128.0+128; break; case (p_sparks): colour[0] = 1; colour[1] = 0.5; colour[2] = 0; break; case (p_fire): colour[0] = 1; colour[1] = 0.55f; colour[2] = 0; break; case (p_blood): colour[0] = 0.8f; colour[1] = 0; colour[2] = 0; break; case (p_chunks): colour[0] = colour[1] = colour[2] = (rand()%182)/255.0; } AddTrailColor(start, end, type, time, colour); } /** AddTrailColor * This will add a trail of particles of the specified type. * The particles may need thining but ill get some feed back * on it first */ void AddTrailColor(vec3_t start, vec3_t end, int type, float time, vec3_t color) { particle_t *p; int i; float count; vec3_t point; //work out vector for trail VectorSubtract(start, end, point); //work out the length and therefore the amount of particles count = Length(point); //make sure its at least 1 long //quater the amount of particles (speeds it up a bit) if (count == 0) return; else count = count/4; if (((type == p_blood)||(type == p_smoke))&&(free_particles)){ p = free_particles; free_particles = p->next; p->next = trails; trails = p; VectorCopy(start, p->org); VectorCopy(end, p->org2); p->die = cl.time + time +2; p->size = 2; VectorCopy(color, p->colour); p->hit = 0; p->start = cl.time; p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = 0; numParticles++; count /= 2; if (type == p_blood){ p->type = pt_grav; return; } else p->type = pt_static; } //the vector from the current pos to the next particle VectorScale(point, 1.0/count, point); for (i=0 ; (inext; //small starting velocity p->vel[0]=((rand()%16)-8)/2; p->vel[1]=((rand()%16)-8)/2; p->vel[2]=((rand()%16)-8)/2; //add the particle to the correct one switch (type) { case (p_fire): p->next = fire; fire = p; p->size = 8; break; case (p_sparks): //need bigger starting velocity (affected by grav) p->vel[0]=((rand()%32)-16)*2; p->vel[1]=((rand()%32)-16)*2; p->vel[2]=((rand()%32))*2; p->next = sparks; sparks = p; p->size = 1; break; case (p_smoke): p->next = smoke; smoke = p; p->size = 4; break; case (p_blood): p->next = blood; blood = p; p->size = 1.5 * (rand()%20)/10; break; case (p_chunks): p->next = chunks; chunks = p; p->size = 1; break; } //reset the particle vars p->hit = 0; p->start = cl.time; p->die = cl.time + time; VectorCopy(color, p->colour); //work out the pos VectorMA (start, i, point, p->org); //make it a bit more random p->org[0] += ((rand()%16)-8)/8; p->org[1] += ((rand()%16)-8)/8; p->org[2] += ((rand()%16)-8)/8; numParticles++; } } /** MakeParticleTexture * Makes the particle textures only 2 the * smoke texture (which could do with some work and * the others which is just a round circle. * * I should really make it an alpha texture which would save 32*32*3 * bytes of space but *shrug* it works so i wont stuff with it */ void MakeParticleTexure(void) { int i, j, k, centreX, centreY, separation, max; byte data[128][128][4]; //Normal texture (0->256 in a circle) //If you change this texture you will change the textures of //all particles except for smoke (other texture) and the //sparks which dont use textures for(j=0;j<128;j++){ for(i=0;i<128;i++){ data[i][j][0] = 255; data[i][j][1] = 255; data[i][j][2] = 255; separation = (int) sqrt((64-i)*(64-i) + (64-j)*(64-j)); if(separation<63) data[i][j][3] = 255 - separation * 256/63; else data[i][j][3] =0; } } //Load the texture into vid mem and save the number for later use part_tex = GL_LoadTexture ("particle", 128, 128, &data[0][0][0], true, true, 4); //Smoke texture (32 * 4 unit circles) //This needs a bit of work its still a bit square but it will do for now //Clear the data max=(int)(sqrt((64-0)*(64-0) + (64-0)*(64-0))); for(j=0;j<128;j++){ for(i=0;i<128;i++){ data[i][j][0] = 255; data[i][j][1] = 255; data[i][j][2] = 255; separation = (int) sqrt((64-i)*(64-i) + (64-j)*(64-j)); data[i][j][3] = (max - separation)*2; } } //Add 128 random 4 unit circles for(k=0;k<128;k++){ centreX = rand()%122+3; centreY = rand()%122+3; for(j=-3;j<3;j++){ for(i=-3;i<3;i++){ separation = (int) sqrt((i*i) + (j*j)); if (separation <= 5) data[i+centreX][j+centreY][3] += (5-separation); } } } smoke_tex = GL_LoadTexture ("smoke_part", 128, 128, &data[0][0][0], false, true, 4); //Load the texture and save the number for later use //Clear the data max=64; for(j=0;j<128;j++){ for(i=0;i<128;i++){ data[i][j][0] = 255; data[i][j][1] = 255; data[i][j][2] = 255; separation = (int) sqrt((i - 64)*(i - 64)); data[i][j][3] = (max - separation)*2; } } //Add 128 random 4 unit circles for(k=0;k<128;k++){ centreX = rand()%122+3; centreY = rand()%122+3; for(j=-3;j<3;j++){ for(i=-3;i<3;i++){ separation = (int) sqrt((i*i) + (j*j)); if (separation <= 5) data[i+centreX][j+centreY][3] += (5-separation); } } } trail_tex = GL_LoadTexture ("trail_part", 128, 128, &data[0][0][0], false, true, 4); free(data); } //Yep thats all only 1472 lines of code