/* Copyright (C) 1997-2001 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. */ #include "cg_local.h" #define MAX_CGPOLYS 256 #define MAX_CGPOLY_VERTS 128 #define MAX_CGPOLY_FRAGMENTS 64 // poly effects flags #define CGPOLY_EF_NOORIENT 0x00000001 #define CGPOLY_EF_AUTOSPRITE_PITCH 0x00000002 #define CGPOLY_EF_AUTOSPRITE_YAW 0x00000004 #define CGPOLY_EF_AUTOSPRITE_ROLL 0x00000008 #define CGPOLY_EF_DRAWONCE 0x00000010 typedef struct cpoly_s { struct cpoly_s *prev, *next; unsigned int die; // remove after this time unsigned int fadetime; float fadefreq; qboolean fadealpha; float color[4]; struct shader_s *shader; poly_t *poly; //jal int effects; vec3_t verts[MAX_CGPOLY_VERTS]; vec3_t origin; vec3_t angles; } cpoly_t; static cpoly_t cg_polys[MAX_CGPOLYS]; static cpoly_t cg_polys_headnode, *cg_free_polys; static poly_t cg_poly_polys[MAX_CGPOLYS]; static vec3_t cg_poly_verts[MAX_CGPOLYS][MAX_CGPOLY_VERTS]; static vec2_t cg_poly_stcoords[MAX_CGPOLYS][MAX_CGPOLY_VERTS]; static byte_vec4_t cg_poly_colors[MAX_CGPOLYS][MAX_CGPOLY_VERTS]; // //================= //CG_Clearpolys //================= // void CG_ClearPolys( void ) { int i; memset( cg_polys, 0, sizeof( cg_polys ) ); // link polys cg_free_polys = cg_polys; cg_polys_headnode.prev = &cg_polys_headnode; cg_polys_headnode.next = &cg_polys_headnode; for( i = 0; i < MAX_CGPOLYS; i++ ) { if( i < MAX_CGPOLYS - 1 ) cg_polys[i].next = &cg_polys[i+1]; cg_polys[i].poly = &cg_poly_polys[i]; cg_polys[i].poly->verts = cg_poly_verts[i]; cg_polys[i].poly->stcoords = cg_poly_stcoords[i]; cg_polys[i].poly->colors = cg_poly_colors[i]; } } // //================= //CG_Allocpoly // //Returns either a free poly or the oldest one //================= // cpoly_t *CG_AllocPoly( void ) { cpoly_t *pl; if ( cg_free_polys ) { // take a free poly if possible pl = cg_free_polys; cg_free_polys = pl->next; } else { // grab the oldest one otherwise pl = cg_polys_headnode.prev; pl->prev->next = pl->next; pl->next->prev = pl->prev; } // put the poly at the start of the list pl->prev = &cg_polys_headnode; pl->next = cg_polys_headnode.next; pl->next->prev = pl; pl->prev->next = pl; return pl; } // //================= //CG_FreePoly //================= // void CG_FreePoly( cpoly_t *dl ) { // remove from linked active list dl->prev->next = dl->next; dl->next->prev = dl->prev; // insert into linked free list dl->next = cg_free_polys; cg_free_polys = dl; } // //================= //CG_SpawnPolygon //================= // cpoly_t *CG_SpawnPolygon( float r, float g, float b, float a, float die, float fadetime, qboolean fadealpha, struct shader_s *shader ) { cpoly_t *pl; float dietime, fadefreq; // clamp and scale colors if( r < 0 ) r = 0; else if( r > 1 ) r = 255; else r *= 255; if( g < 0 ) g = 0; else if( g > 1 ) g = 255; else g *= 255; if( b < 0 ) b = 0; else if( b > 1 ) b = 255; else b *= 255; if( a < 0 ) a = 0; else if( a > 1 ) a = 255; else a *= 255; dietime = cg.time + die * 1000; fadefreq = 0.001f / min( fadetime, die ); fadetime = cg.time + (die - min( fadetime, die )) * 1000; // allocate poly pl = CG_AllocPoly (); pl->die = dietime; pl->fadetime = fadetime; pl->fadefreq = fadefreq; pl->fadealpha = fadealpha; pl->shader = shader; pl->color[0] = r; pl->color[1] = g; pl->color[2] = b; pl->color[3] = a; pl->effects = 0; if( !die ) { pl->die = cg.time + 100; pl->effects |= CGPOLY_EF_DRAWONCE; } return pl; } /* c = (float)cos (DEG2RAD (rotation)) * scale; s = (float)sin (DEG2RAD (rotation)) * scale; // Top left VectorSet (p->pVerts[0], org[0] - cg.v_right[0]*c + cg.v_up[0]*s, org[1] - cg.v_right[1]*c + cg.v_up[1]*s, org[2] - cg.v_right[2]*c + cg.v_up[2]*s); // Bottom left VectorSet (p->pVerts[1], org[0] + cg.v_right[0]*s + cg.v_up[0]*c, org[1] + cg.v_right[1]*s + cg.v_up[1]*c, org[2] + cg.v_right[2]*s + cg.v_up[2]*c); // Bottom right VectorSet (p->pVerts[2], org[0] + cg.v_right[0]*c - cg.v_up[0]*s, org[1] + cg.v_right[1]*c - cg.v_up[1]*s, org[2] + cg.v_right[2]*c - cg.v_up[2]*s); // Top right VectorSet (p->pVerts[3], org[0] - cg.v_right[0]*s - cg.v_up[0]*c, org[1] - cg.v_right[1]*s - cg.v_up[1]*c, org[2] - cg.v_right[2]*s - cg.v_up[2]*c); */ // //================= //CG_OrientPolygon //================= // void CG_OrientPolygon( vec3_t origin, vec3_t angles, poly_t *poly ) { int i, j; vec3_t center; vec3_t perp; //rotate the polygon around it's default origin (0,0,0) VectorClear(center); //axis_identity[0] = ROLL //axis_identity[1] = PITCH //axis_identity[2] = YAW //FIXME: needs optimization for( i = 0; i < poly->numverts ; i++ ) { VectorSubtract( poly->verts[i], center, perp ); RotatePointAroundVector( poly->verts[i], axis_identity[0], perp, angles[ROLL] ); VectorSubtract( poly->verts[i], center, perp ); RotatePointAroundVector( poly->verts[i], axis_identity[1], perp, angles[PITCH] ); VectorSubtract( poly->verts[i], center, perp ); RotatePointAroundVector( poly->verts[i], axis_identity[2], perp, angles[YAW] ); } //move to the desired origin for( i = 0; i < poly->numverts ; i++ ) { for( j = 0; j < 3; j++ ) poly->verts[i][j] += origin[j]; } } //================= //CG_SpawnPolyBeam // spawns a polygon from start to end points length and given width. // shaderlenght makes reference to size of the texture it will draw, so it can be repeated. //================= cpoly_t *CG_SpawnPolyBeam( vec3_t start, vec3_t end, int width, float dietime, float fadetime, qboolean fadealpha, struct shader_s *shader, int shaderlength ) { cpoly_t *cgpoly; poly_t *poly; vec3_t angles, dir; int vcolor, i; float xmin, ymin, xmax, ymax; float stx = 1.0f, sty = 1.0f; //find out beam polygon sizes VectorSubtract( end, start, dir ); VecToAngles( dir, angles ); xmin = 0; xmax = VectorNormalize( dir ); ymin = -(width*0.5); ymax = width*0.5; if( xmax > shaderlength ) { stx = xmax/shaderlength; } cgpoly = CG_SpawnPolygon( 1.0, 1.0, 1.0, 1.0, dietime, fadetime, fadealpha, shader ); VectorCopy( angles, cgpoly->angles ); VectorCopy( start, cgpoly->origin ); cgpoly->effects = CGPOLY_EF_AUTOSPRITE_ROLL; if( !dietime ) cgpoly->effects |= CGPOLY_EF_DRAWONCE; //create the polygon inside the cgpolygon poly = cgpoly->poly; poly->shader = cgpoly->shader; vcolor = COLOR_RGBA( (int)(cgpoly->color[0] * 255), (int)(cgpoly->color[1] * 255), (int)(cgpoly->color[2] * 255), (int)(cgpoly->color[3] * 255) ); poly->numverts = 0; //A VectorSet( poly->verts[poly->numverts], xmin, 0, ymin ); poly->stcoords[poly->numverts][0] = 0; poly->stcoords[poly->numverts][1] = 0; *(int *)poly->colors[poly->numverts] = vcolor; poly->numverts++; //B VectorSet( poly->verts[poly->numverts], xmin, 0, ymax ); poly->stcoords[poly->numverts][0] = 0; poly->stcoords[poly->numverts][1] = sty; *(int *)poly->colors[poly->numverts] = vcolor; poly->numverts++; //C VectorSet( poly->verts[poly->numverts], xmax, 0, ymax ); poly->stcoords[poly->numverts][0] = stx; poly->stcoords[poly->numverts][1] = sty; *(int *)poly->colors[poly->numverts] = vcolor; poly->numverts++; //D VectorSet( poly->verts[poly->numverts], xmax, 0, ymin ); poly->stcoords[poly->numverts][0] = stx; poly->stcoords[poly->numverts][1] = 0; *(int *)poly->colors[poly->numverts] = vcolor; poly->numverts++; //the verts data is stored inside cgpoly, cause it can be moved later for( i = 0; i < poly->numverts; i++ ) VectorCopy( poly->verts[i], cgpoly->verts[i] ); return cgpoly; } //================= //CG_QuickPolyBeam //================= void CG_QuickPolyBeam( vec3_t start, vec3_t end, int width, struct shader_s *shader ) { cpoly_t *cgpoly, *cgpoly2; cgpoly = CG_SpawnPolyBeam( start, end, width, 0, 0, qfalse, CG_MediaShader( cgs.media.shaderLaserGunBeam ), 64 ); cgpoly->effects |= CGPOLY_EF_DRAWONCE; // since autosprite doesn't work, spawn a second and rotate it 90º cgpoly2 = CG_SpawnPolyBeam( start, end, width, 0, 0, qfalse, CG_MediaShader( cgs.media.shaderLaserGunBeam ), 64 ); cgpoly2->angles[ROLL] += 90; cgpoly2->effects |= CGPOLY_EF_DRAWONCE; } //================= //CG_LaserGunPolyBeam //================= void CG_LaserGunPolyBeam( vec3_t start, vec3_t end ) { cpoly_t *cgpoly, *cgpoly2; cgpoly = CG_SpawnPolyBeam( start, end, 16, 0, 0, qfalse, CG_MediaShader( cgs.media.shaderLaserGunBeam ), 64 ); cgpoly->effects |= CGPOLY_EF_DRAWONCE; // since autosprite doesn't work, spawn a second and rotate it 90º cgpoly2 = CG_SpawnPolyBeam( start, end, 16, 0, 0, qfalse, CG_MediaShader( cgs.media.shaderLaserGunBeam ), 64 ); cgpoly2->angles[ROLL] += 90; cgpoly2->effects |= CGPOLY_EF_DRAWONCE; } void CG_BoltExplosionMode( vec3_t pos, vec3_t dir, int fire_mode ); //================= //CG_ElectroPolyBeam //================= void CG_ElectroPolyBeam( vec3_t start, vec3_t end ) { cpoly_t *cgpoly, *cgpoly2; vec3_t dir; cgpoly = CG_SpawnPolyBeam( start, end, 16, 0.6f, 0.3f, qtrue, CG_MediaShader( cgs.media.shaderElectroBeam ), 64 ); // since autosprite doesn't work, spawn a second and rotate it 90º cgpoly2 = CG_SpawnPolyBeam( start, end, 16, 0.6f, 0.3f, qtrue, CG_MediaShader( cgs.media.shaderElectroBeam ), 64 ); cgpoly2->angles[ROLL] += 90; // effect on end, since trail goes through players, end will always be wall VectorSubtract( start, end, dir ); VectorNormalizeFast( dir ); CG_BoltExplosionMode( end, dir, FIRE_MODE_STRONG ); } //================= //CG_Addpolys //================= void CG_AddPolys( void ) { int i; float fade; cpoly_t *cgpoly, *next, *hnode; poly_t *poly; static byte_vec4_t color; static vec3_t angles; // add polys in first-spawed - first-drawn order hnode = &cg_polys_headnode; for( cgpoly = hnode->prev; cgpoly != hnode; cgpoly = next ) { next = cgpoly->prev; // it's time to DIE if( cgpoly->die <= cg.time ) { CG_FreePoly( cgpoly ); continue; } poly = cgpoly->poly; //jal[start] composite the poly with cgpoly's data if( !(cgpoly->effects & CGPOLY_EF_NOORIENT) ) { for( i=0; inumverts; i++ ) VectorCopy( cgpoly->verts[i], poly->verts[i] ); VectorCopy( cgpoly->angles, angles ); if( cgpoly->effects & CGPOLY_EF_AUTOSPRITE_PITCH ) { } if( cgpoly->effects & CGPOLY_EF_AUTOSPRITE_YAW ) { } if( cgpoly->effects & CGPOLY_EF_AUTOSPRITE_ROLL ) { } CG_OrientPolygon( cgpoly->origin, angles, poly ); } //jal[end] // fade out if( cgpoly->fadetime < cg.time ) { fade = (cgpoly->die - cg.time) * cgpoly->fadefreq; if( cgpoly->fadealpha ) { color[0] = ( qbyte )( cgpoly->color[0] ); color[1] = ( qbyte )( cgpoly->color[1] ); color[2] = ( qbyte )( cgpoly->color[2] ); color[3] = ( qbyte )( cgpoly->color[3]*fade ); } else { color[0] = ( qbyte )( cgpoly->color[0]*fade ); color[1] = ( qbyte )( cgpoly->color[1]*fade ); color[2] = ( qbyte )( cgpoly->color[2]*fade ); color[3] = ( qbyte )( cgpoly->color[3] ); } for( i = 0; i < poly->numverts; i++ ) *( int * )poly->colors[i] = *( int * )color; } if( cgpoly->effects & CGPOLY_EF_DRAWONCE ) { cgpoly->die = cg.time; // will be ignored and freed the next time } trap_R_AddPolyToScene( poly ); } }