mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 00:41:21 +00:00
1127 lines
30 KiB
C
1127 lines
30 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1997-2001 Id Software, Inc.
|
|
|
|
This file is part of Quake 2 source code.
|
|
|
|
Quake 2 source code 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.
|
|
|
|
Quake 2 source code 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 Quake 2 source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// r_particle.c -- particle rendering
|
|
// moved from r_main.c
|
|
|
|
#include "r_local.h"
|
|
|
|
#define random() ((rand () & 0x7fff) / ((float)0x7fff))
|
|
#define crandom() (2.0 * (random() - 0.5))
|
|
|
|
//#define BINARY_PART_SORT
|
|
|
|
#ifdef BINARY_PART_SORT
|
|
/*
|
|
===================================================
|
|
BINARY PARTICLE SORT
|
|
===================================================
|
|
*/
|
|
int partstosort;
|
|
sortedelement_t theparts[MAX_PARTICLES];
|
|
sortedelement_t *parts_prerender;
|
|
// Is this really needed?
|
|
//sortedelement_t *parts_last;
|
|
|
|
void RenderParticle (particle_t *p);
|
|
void RenderDecal (particle_t *p);
|
|
|
|
void resetPartSortList (void)
|
|
{
|
|
partstosort = 0;
|
|
parts_prerender = NULL;
|
|
//parts_last = NULL;
|
|
}
|
|
|
|
qboolean particleClip( float len )
|
|
{
|
|
if (r_particle_min->value > 0)
|
|
{
|
|
if (len < r_particle_min->value)
|
|
return true;
|
|
}
|
|
if (r_particle_max->value > 0)
|
|
{
|
|
if (len > r_particle_max->value)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
sortedelement_t *NewSortPart ( particle_t *p)
|
|
{
|
|
vec3_t distance;
|
|
sortedelement_t *element;
|
|
|
|
element = &theparts[partstosort];
|
|
|
|
VectorSubtract(p->origin, r_origin, distance);
|
|
VectorCopy(p->origin, element->org);
|
|
|
|
element->data = p;
|
|
element->len = VectorLength(distance);
|
|
element->left = NULL;
|
|
element->right = NULL;
|
|
|
|
return element;
|
|
}
|
|
|
|
void AddPartTransTree( particle_t *p )
|
|
{
|
|
sortedelement_t *thisPart;
|
|
|
|
thisPart = NewSortPart(p);
|
|
|
|
if (particleClip(thisPart->len))
|
|
return;
|
|
|
|
if (parts_prerender)
|
|
ElementAddNode(parts_prerender, thisPart);
|
|
else
|
|
parts_prerender = thisPart;
|
|
|
|
//parts_last = thisPart;
|
|
partstosort++;
|
|
}
|
|
|
|
void R_BuildParticleList (void)
|
|
{
|
|
int i;
|
|
resetPartSortList();
|
|
|
|
for ( i=0; i < r_newrefdef.num_particles; i++)
|
|
AddPartTransTree(&r_newrefdef.particles[i]);
|
|
}
|
|
|
|
void RenderParticleTree (sortedelement_t *element)
|
|
{
|
|
if (!element)
|
|
return;
|
|
|
|
RenderParticleTree(element->left);
|
|
|
|
if (element->data)
|
|
R_RenderParticle ((particle_t *)element->data);
|
|
|
|
RenderParticleTree(element->right);
|
|
}
|
|
#endif // BINARY_PART_SORT
|
|
//===================================================
|
|
|
|
|
|
/*
|
|
===================================================
|
|
|
|
STANDARD PARTICLE SORT
|
|
|
|
===================================================
|
|
*/
|
|
|
|
typedef struct sortedpart_s
|
|
{
|
|
particle_t *p;
|
|
vec_t len;
|
|
} sortedpart_t;
|
|
|
|
sortedpart_t sorted_parts[MAX_PARTICLES];
|
|
|
|
int transCompare (const void *arg1, const void *arg2 )
|
|
{
|
|
const sortedpart_t *a1, *a2;
|
|
a1 = (sortedpart_t *) arg1;
|
|
a2 = (sortedpart_t *) arg2;
|
|
if ((r_transrendersort->integer == 1) && a1->p && a2->p)
|
|
{
|
|
int image1, image2;
|
|
// FIXME: use hash of imagenum, blendfuncs, & critical flags
|
|
// image1 = (a1->p->flags & PART_SPARK) ? 0 : a1->p->image;
|
|
// image2 = (a2->p->flags & PART_SPARK) ? 0 : a2->p->image;
|
|
image1 = a1->p->image;
|
|
image2 = a2->p->image;
|
|
return (image2 - image1)*10000 + (a2->len - a1->len);
|
|
}
|
|
else
|
|
return (a2->len - a1->len);
|
|
}
|
|
|
|
sortedpart_t NewSortedPart (particle_t *p)
|
|
{
|
|
vec3_t result;
|
|
sortedpart_t s_part;
|
|
|
|
s_part.p = p;
|
|
VectorSubtract(p->origin, r_origin, result);
|
|
s_part.len = (result[0] * result[0]) + (result[1] * result[1]) + (result[2] * result[2]);
|
|
|
|
return s_part;
|
|
}
|
|
|
|
|
|
void R_SortParticlesOnList (void)
|
|
{
|
|
int i, j=0, rangestart=0, rangecount=1;
|
|
|
|
for (i=0; i<r_newrefdef.num_particles; i++)
|
|
sorted_parts[i] = NewSortedPart(&r_newrefdef.particles[i]);
|
|
|
|
qsort((void *)sorted_parts, r_newrefdef.num_particles, sizeof(sortedpart_t), transCompare);
|
|
}
|
|
|
|
|
|
/*
|
|
===================================================
|
|
|
|
PARTICLE RENDERING
|
|
|
|
===================================================
|
|
*/
|
|
|
|
#define DEPTHHACK_RANGE_SHORT 0.9985f
|
|
#define DEPTHHACK_RANGE_MID 0.975f
|
|
#define DEPTHHACK_RANGE_LONG 0.95f
|
|
|
|
#define DEPTHHACK_NONE 0
|
|
#define DEPTHHACK_SHORT 1
|
|
#define DEPTHHACK_MID 2
|
|
#define DEPTHHACK_LONG 3
|
|
|
|
void SetParticleOverbright (qboolean toggle);
|
|
|
|
qboolean ParticleOverbright;
|
|
qboolean fog_on;
|
|
vec3_t particle_up;
|
|
vec3_t particle_right;
|
|
vec3_t particle_coord[4];
|
|
vec3_t ParticleVec[4];
|
|
vec3_t shadelight;
|
|
|
|
//===================================================
|
|
|
|
// Particle batching struct
|
|
// used for grouping particles by identical rendering state
|
|
// and rendering in vertex arrays
|
|
typedef struct particleRenderState_s particleRenderState_t;
|
|
typedef struct particleRenderState_s
|
|
{
|
|
GLenum polymode;
|
|
// qboolean texture2D;
|
|
qboolean overbright;
|
|
qboolean polygon_offset_fill;
|
|
qboolean alphatest;
|
|
|
|
GLenum blendfunc_src;
|
|
GLenum blendfunc_dst;
|
|
int imagenum;
|
|
int depth_hack;
|
|
};
|
|
particleRenderState_t currentParticleState;
|
|
|
|
//int particle_va, particle_index;
|
|
|
|
/*
|
|
===============
|
|
CompareParticleRenderState
|
|
Compares new particle state to current array
|
|
===============
|
|
*/
|
|
qboolean CompareParticleRenderState (particleRenderState_t *compare)
|
|
{
|
|
if (!currentParticleState.polymode || !compare) // not initialized
|
|
return false;
|
|
|
|
if ( (currentParticleState.polymode != compare->polymode)
|
|
// || (currentParticleState.texture2D != compare->texture2D)
|
|
|| (currentParticleState.overbright != compare->overbright)
|
|
|| (currentParticleState.polygon_offset_fill != compare->polygon_offset_fill)
|
|
|| (currentParticleState.alphatest != compare->alphatest)
|
|
|| (currentParticleState.blendfunc_src != compare->blendfunc_src)
|
|
|| (currentParticleState.blendfunc_dst != compare->blendfunc_dst)
|
|
|| (currentParticleState.imagenum != compare->imagenum)
|
|
|| (currentParticleState.depth_hack != compare->depth_hack) )
|
|
return false;
|
|
|
|
return true;
|
|
//return (memcmp(compare, ¤tParticleState, sizeof(particleRenderState_t)) == 0);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_DrawParticleArrays
|
|
===============
|
|
*/
|
|
void R_DrawParticleArrays (void)
|
|
{
|
|
//VID_Printf(PRINT_DEVELOPER, "R_DrawParticleArrays: rendering %i indices with %i verts\n", rb_index, rb_vertex);
|
|
c_part_polys += rb_index / 3;
|
|
|
|
RB_RenderMeshGeneric (true);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_CheckParticleRenderState
|
|
Draws array and sets new state if next particle is different
|
|
===============
|
|
*/
|
|
void R_CheckParticleRenderState (particleRenderState_t *check, int numVerts, int numIndex)
|
|
{
|
|
if (!check) return; // catch bad pointer
|
|
|
|
// check for overflow
|
|
if (RB_CheckArrayOverflow(numVerts, numIndex))
|
|
R_DrawParticleArrays();
|
|
|
|
if (!CompareParticleRenderState(check))
|
|
{
|
|
float depthmax;
|
|
|
|
if (currentParticleState.polymode) // render particle array
|
|
R_DrawParticleArrays();
|
|
|
|
// copy state data
|
|
memcpy(¤tParticleState, check, sizeof(particleRenderState_t));
|
|
|
|
// set new render state
|
|
GL_BlendFunc (currentParticleState.blendfunc_src, currentParticleState.blendfunc_dst);
|
|
|
|
GL_Bind(currentParticleState.imagenum);
|
|
/* if (currentParticleState.texture2D) {
|
|
GL_EnableTexture(0);
|
|
GL_Bind(currentParticleState.imagenum);
|
|
}
|
|
else
|
|
GL_DisableTexture(0);
|
|
*/
|
|
|
|
switch (currentParticleState.depth_hack)
|
|
{
|
|
case DEPTHHACK_LONG:
|
|
depthmax = gldepthmin + DEPTHHACK_RANGE_LONG*(gldepthmax-gldepthmin);
|
|
break;
|
|
case DEPTHHACK_MID:
|
|
depthmax = gldepthmin + DEPTHHACK_RANGE_MID*(gldepthmax-gldepthmin);
|
|
break;
|
|
case DEPTHHACK_SHORT:
|
|
depthmax = gldepthmin + DEPTHHACK_RANGE_SHORT*(gldepthmax-gldepthmin);
|
|
break;
|
|
case DEPTHHACK_NONE:
|
|
default:
|
|
depthmax = gldepthmax;
|
|
break;
|
|
}
|
|
GL_DepthRange (gldepthmin, depthmax);
|
|
|
|
if (currentParticleState.overbright)
|
|
SetParticleOverbright(true);
|
|
else
|
|
SetParticleOverbright(false);
|
|
|
|
if (currentParticleState.polygon_offset_fill) {
|
|
GL_Enable(GL_POLYGON_OFFSET_FILL);
|
|
GL_PolygonOffset(-2, -1);
|
|
}
|
|
else
|
|
GL_Disable(GL_POLYGON_OFFSET_FILL);
|
|
|
|
if (currentParticleState.alphatest)
|
|
GL_Enable (GL_ALPHA_TEST);
|
|
else
|
|
GL_Disable (GL_ALPHA_TEST);
|
|
}
|
|
// else just continue adding to array
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_BeginParticles
|
|
Clears particle render state and sets OpenGL state
|
|
===============
|
|
*/
|
|
void R_BeginParticles (qboolean decals)
|
|
{
|
|
if (!decals)
|
|
{
|
|
VectorSet (particle_up, vup[0] * 0.75f, vup[1] * 0.75f, vup[2] * 0.75f);
|
|
VectorSet (particle_right, vright[0] * 0.75f, vright[1] * 0.75f, vright[2] * 0.75f);
|
|
|
|
VectorAdd (particle_up, particle_right, particle_coord[0]);
|
|
VectorSubtract (particle_right, particle_up, particle_coord[1]);
|
|
VectorNegate (particle_coord[0], particle_coord[2]);
|
|
VectorNegate (particle_coord[1], particle_coord[3]);
|
|
|
|
// no fog on particles
|
|
fog_on = false;
|
|
if (qglIsEnabled(GL_FOG)) // check if fog is enabled
|
|
{
|
|
fog_on = true;
|
|
qglDisable(GL_FOG); // if so, disable it
|
|
}
|
|
}
|
|
// clear particle rendering state
|
|
memset(¤tParticleState, 0, sizeof(particleRenderState_t));
|
|
rb_vertex = rb_index = 0;
|
|
ParticleOverbright = false;
|
|
|
|
GL_TexEnv (GL_MODULATE);
|
|
GL_DepthMask (false);
|
|
GL_Enable (GL_BLEND);
|
|
GL_ShadeModel (GL_SMOOTH);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_FinishParticles
|
|
Renders any particles left in the vertex array and resets OpenGL state
|
|
===============
|
|
*/
|
|
void R_FinishParticles (qboolean decals)
|
|
{
|
|
if (currentParticleState.polymode && (rb_vertex > 0) && (rb_index > 0))
|
|
R_DrawParticleArrays();
|
|
|
|
GL_EnableTexture(0);
|
|
SetParticleOverbright(false);
|
|
GL_DepthRange (gldepthmin, gldepthmax);
|
|
GL_BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
GL_DepthMask (true);
|
|
GL_Disable (GL_POLYGON_OFFSET_FILL);
|
|
GL_Disable (GL_BLEND);
|
|
qglColor4f (1,1,1,1);
|
|
|
|
if (!decals)
|
|
{ // re-enable fog if it was on
|
|
if (fog_on)
|
|
qglEnable(GL_FOG);
|
|
}
|
|
}
|
|
|
|
//===================================================
|
|
|
|
/*
|
|
===============
|
|
TexParticle
|
|
===============
|
|
*/
|
|
int TexParticle (int type)
|
|
{
|
|
// check for out of bounds image num
|
|
type = max( type, 0 );
|
|
type = min( type, PARTICLE_TYPES-1 );
|
|
// check for bad particle image num
|
|
if (!glMedia.particletextures[type])
|
|
glMedia.particletextures[type] = glMedia.notexture;
|
|
return glMedia.particletextures[type]->texnum;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
AngleFind
|
|
===============
|
|
*/
|
|
float AngleFind (float input)
|
|
{
|
|
return 180.0/input;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
SetBeamAngles
|
|
===============
|
|
*/
|
|
void SetBeamAngles (vec3_t start, vec3_t end, vec3_t up, vec3_t right)
|
|
{
|
|
vec3_t move, delta;
|
|
|
|
VectorSubtract(end, start, move);
|
|
VectorNormalize(move);
|
|
|
|
VectorCopy(move, up);
|
|
VectorSubtract(r_newrefdef.vieworg, start, delta);
|
|
CrossProduct(up, delta, right);
|
|
//if(!VectorCompare(right, vec3_origin))
|
|
VectorNormalize(right);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
SetParticleOverbright
|
|
===============
|
|
*/
|
|
void SetParticleOverbright (qboolean toggle)
|
|
{
|
|
if ( (toggle && !ParticleOverbright) || (!toggle && ParticleOverbright) )
|
|
{
|
|
R_SetVertexRGBScale (toggle);
|
|
ParticleOverbright = toggle;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
GetParticleLight
|
|
===============
|
|
*/
|
|
void GetParticleLight (particle_t *p, vec3_t pos, float lighting, vec3_t shadelight)
|
|
{
|
|
int j;
|
|
float lightest = 0;
|
|
|
|
if ( (r_fullbright->integer != 0) || !lighting )
|
|
{
|
|
VectorSet(shadelight, p->red, p->green, p->blue);
|
|
return;
|
|
}
|
|
|
|
R_LightPoint (pos, shadelight, false);
|
|
|
|
shadelight[0]= (lighting*shadelight[0]+(1-lighting)) * p->red;
|
|
shadelight[1]= (lighting*shadelight[1]+(1-lighting)) * p->green;
|
|
shadelight[2]= (lighting*shadelight[2]+(1-lighting)) * p->blue;
|
|
|
|
//this cleans up the lighting
|
|
for (j=0; j<3; j++)
|
|
if (shadelight[j] > lightest)
|
|
lightest= shadelight[j];
|
|
if (lightest > 255)
|
|
for (j=0; j<3; j++)
|
|
{
|
|
shadelight[j]*= 255/lightest;
|
|
if (shadelight[j] > 255)
|
|
shadelight[j] = 255;
|
|
}
|
|
for (j=0; j<3; j++)
|
|
{
|
|
if (shadelight[j] < 0)
|
|
shadelight[j] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_RenderParticle
|
|
===============
|
|
*/
|
|
void R_RenderParticle (particle_t *p)
|
|
{
|
|
float size, len, lighting = r_particle_lighting->value;
|
|
int oldrender=0, rendertype=0, numVerts, numIndex;
|
|
|
|
vec3_t shadelight, move;
|
|
vec4_t partColor;
|
|
qboolean shaded;
|
|
vec2_t tex_coord[4];
|
|
vec3_t coord[4];
|
|
vec3_t angl_coord[4];
|
|
particleRenderState_t thisPart;
|
|
|
|
Vector2Set(tex_coord[0], 0, 1);
|
|
Vector2Set(tex_coord[1], 0, 0);
|
|
Vector2Set(tex_coord[2], 1, 0);
|
|
Vector2Set(tex_coord[3], 1, 1);
|
|
|
|
VectorCopy(particle_coord[0], coord[0]);
|
|
VectorCopy(particle_coord[1], coord[1]);
|
|
VectorCopy(particle_coord[2], coord[2]);
|
|
VectorCopy(particle_coord[3], coord[3]);
|
|
|
|
size = (p->size>0.1) ? p->size : 0.1;
|
|
shaded = (p->flags & PART_SHADED);
|
|
|
|
if (shaded) {
|
|
GetParticleLight (p, p->origin, lighting, shadelight);
|
|
Vector4Set(partColor, shadelight[0]*DIV255, shadelight[1]*DIV255, shadelight[2]*DIV255, p->alpha);
|
|
}
|
|
else
|
|
Vector4Set(partColor, p->red*DIV255, p->green*DIV255, p->blue*DIV255, p->alpha);
|
|
|
|
/* if (p->flags & PART_SPARK) {
|
|
thisPart.texture2D = false;
|
|
thisPart.imagenum = 0;
|
|
}
|
|
else {
|
|
thisPart.texture2D = true;
|
|
thisPart.imagenum = TexParticle(p->image);
|
|
}*/
|
|
|
|
thisPart.imagenum = TexParticle(p->image);
|
|
thisPart.polymode = GL_TRIANGLES;
|
|
thisPart.overbright = (p->flags & PART_OVERBRIGHT);
|
|
thisPart.polygon_offset_fill = false;
|
|
thisPart.alphatest = false;
|
|
thisPart.blendfunc_src = p->blendfunc_src;
|
|
thisPart.blendfunc_dst = p->blendfunc_dst;
|
|
|
|
if (p->flags & PART_DEPTHHACK_SHORT) // nice little poly-peeking - psychospaz
|
|
thisPart.depth_hack = DEPTHHACK_SHORT;
|
|
else if (p->flags & PART_DEPTHHACK_MID)
|
|
thisPart.depth_hack = DEPTHHACK_MID;
|
|
else if (p->flags & PART_DEPTHHACK_LONG)
|
|
thisPart.depth_hack = DEPTHHACK_LONG;
|
|
else
|
|
thisPart.depth_hack = DEPTHHACK_NONE;
|
|
|
|
// estimate number of verts for this particle
|
|
if (p->flags & PART_SPARK)
|
|
{ numVerts = numIndex = 3; }
|
|
else if (p->flags & PART_LIGHTNING) {
|
|
VectorSubtract(p->origin, p->angle, move);
|
|
len = VectorNormalize(move);
|
|
numVerts = numIndex = len / size + 1;
|
|
numVerts *= 4; numIndex *= 6;
|
|
}
|
|
else // PART_BEAM, PART_DIRECTION, PART_ANGLED, default
|
|
{ numVerts = 4; numIndex = 6; }
|
|
|
|
// check if render state changed from last particle
|
|
R_CheckParticleRenderState (&thisPart, numVerts, numIndex);
|
|
|
|
if (p->flags & PART_SPARK) // single triangles
|
|
{
|
|
#if 1
|
|
vec3_t base, ang_up, ang_right;
|
|
int i;
|
|
|
|
VectorMA (p->origin, -size, p->angle, base);
|
|
SetBeamAngles (base, p->origin, ang_up, ang_right);
|
|
|
|
VectorScale (ang_up, size*2.0f, ang_up);
|
|
VectorScale (ang_right, size*0.25f, ang_right);
|
|
|
|
VectorSubtract (ang_right, ang_up, angl_coord[0]);
|
|
VectorAdd (ang_up, ang_right, angl_coord[1]);
|
|
VectorNegate (angl_coord[1], angl_coord[1]);
|
|
VectorAdd (p->origin, angl_coord[0], ParticleVec[0]);
|
|
VectorAdd (p->origin, angl_coord[1], ParticleVec[1]);
|
|
|
|
VA_SetElem3(vertexArray[rb_vertex], p->origin[0], p->origin[1], p->origin[2]);
|
|
VA_SetElem4(colorArray[rb_vertex], partColor[0], partColor[1], partColor[2], partColor[3]);
|
|
indexArray[rb_index++] = rb_vertex;
|
|
rb_vertex++;
|
|
|
|
for (i=0; i<2; i++) {
|
|
VA_SetElem3(vertexArray[rb_vertex], ParticleVec[i][0], ParticleVec[i][1], ParticleVec[i][2]);
|
|
VA_SetElem4(colorArray[rb_vertex], 0, 0, 0, 0);
|
|
indexArray[rb_index++] = rb_vertex;
|
|
rb_vertex++;
|
|
}
|
|
#else // QMB style particles (8-sided cone)
|
|
float angle;
|
|
vec3_t v;
|
|
int i, j;
|
|
|
|
for (i=1; i<9; i++) {
|
|
j = (i<8) ? i+1 : 0;
|
|
indexArray[rb_index++] = rb_vertex;
|
|
indexArray[rb_index++] = rb_vertex+i;
|
|
indexArray[rb_index++] = rb_vertex+j;
|
|
}
|
|
|
|
VA_SetElem3(vertexArray[rb_vertex], p->origin[0], p->origin[1], p->origin[2]);
|
|
VA_SetElem4(colorArray[rb_vertex], partColor[0], partColor[1], partColor[2], partColor[3]);
|
|
rb_vertex++;
|
|
|
|
for (i=0; i<8; i++)
|
|
{
|
|
angle = i * 45; // was 22.5;
|
|
v[0]=p->origin[0] - p->angle[0]*size + (particle_right[0]*cos(angle) + particle_up[0]*sin(angle));
|
|
v[1]=p->origin[1] - p->angle[1]*size + (particle_right[1]*cos(angle) + particle_up[1]*sin(angle));
|
|
v[2]=p->origin[2] - p->angle[2]*size + (particle_right[2]*cos(angle) + particle_up[2]*sin(angle));
|
|
|
|
VA_SetElem3(vertexArray[rb_vertex], v[0], v[1], v[2]);
|
|
VA_SetElem4(colorArray[rb_vertex], 0, 0, 0, 0);
|
|
rb_vertex++;
|
|
}
|
|
#endif
|
|
}
|
|
else if (p->flags & PART_BEAM) // given start and end positions, will have fun here :)
|
|
{
|
|
vec3_t ang_up, ang_right;
|
|
int i;
|
|
|
|
SetBeamAngles(p->angle, p->origin, ang_up, ang_right);
|
|
VectorScale(ang_right, size, ang_right);
|
|
|
|
VectorAdd(p->origin, ang_right, angl_coord[0]);
|
|
VectorAdd(p->angle, ang_right, angl_coord[1]);
|
|
VectorSubtract(p->angle, ang_right, angl_coord[2]);
|
|
VectorSubtract(p->origin, ang_right, angl_coord[3]);
|
|
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+1;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+3;
|
|
for (i=0; i<4; i++)
|
|
{
|
|
VA_SetElem2(texCoordArray[0][rb_vertex], tex_coord[i][0], tex_coord[i][1]);
|
|
VA_SetElem3(vertexArray[rb_vertex], angl_coord[i][0], angl_coord[i][1], angl_coord[i][2]);
|
|
VA_SetElem4(colorArray[rb_vertex], partColor[0], partColor[1], partColor[2], partColor[3]);
|
|
rb_vertex++;
|
|
}
|
|
}
|
|
else if (p->flags & PART_LIGHTNING) // given start and end positions, will have fun here :)
|
|
{
|
|
float tlen, halflen, dec = size, warpfactor, warpadd, oldwarpadd=0, warpsize, time, i=0, factor;
|
|
vec3_t lastvec, thisvec, tempvec;
|
|
vec3_t ang_up, ang_right, vdelta;
|
|
int j;
|
|
|
|
warpsize = dec * 0.4; // Knightmare changed
|
|
warpfactor = M_PI*1.0;
|
|
time = -r_newrefdef.time*20.0;
|
|
|
|
VectorCopy(move, ang_up);
|
|
VectorSubtract(r_newrefdef.vieworg, p->angle, vdelta);
|
|
CrossProduct(ang_up, vdelta, ang_right);
|
|
if (!VectorCompare(ang_right, vec3_origin))
|
|
VectorNormalize(ang_right);
|
|
|
|
VectorScale (ang_right, dec, ang_right);
|
|
VectorScale (ang_up, dec, ang_up);
|
|
|
|
VectorScale(move, dec, move);
|
|
VectorCopy(p->angle, lastvec);
|
|
VectorAdd(lastvec, move, thisvec);
|
|
VectorCopy(thisvec, tempvec);
|
|
|
|
// lightning starts at point and then flares out
|
|
#define LIGHTNINGWARPFUNCTION 0.25*sin(time+i+warpfactor)*(dec/len)
|
|
|
|
warpadd = LIGHTNINGWARPFUNCTION;
|
|
factor = 1;
|
|
halflen = len/2.0;
|
|
|
|
thisvec[0]= (thisvec[0]*2 + crandom()*5)/2;
|
|
thisvec[1]= (thisvec[1]*2 + crandom()*5)/2;
|
|
thisvec[2]= (thisvec[2]*2 + crandom()*5)/2;
|
|
|
|
while (len > dec)
|
|
{
|
|
i += warpsize;
|
|
VectorAdd(thisvec, ang_right, angl_coord[0]);
|
|
VectorAdd(lastvec, ang_right, angl_coord[1]);
|
|
VectorSubtract(lastvec, ang_right, angl_coord[2]);
|
|
VectorSubtract(thisvec, ang_right, angl_coord[3]);
|
|
|
|
Vector2Set(tex_coord[0], 0+warpadd, 1);
|
|
Vector2Set(tex_coord[1], 0+oldwarpadd, 0);
|
|
Vector2Set(tex_coord[2], 1+oldwarpadd, 0);
|
|
Vector2Set(tex_coord[3], 1+warpadd, 1);
|
|
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+1;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+3;
|
|
for (j=0; j<4; j++)
|
|
{
|
|
VA_SetElem2(texCoordArray[0][rb_vertex], tex_coord[j][0], tex_coord[j][1]);
|
|
VA_SetElem3(vertexArray[rb_vertex], angl_coord[j][0], angl_coord[j][1], angl_coord[j][2]);
|
|
VA_SetElem4(colorArray[rb_vertex], partColor[0], partColor[1], partColor[2], partColor[3]);
|
|
rb_vertex++;
|
|
}
|
|
|
|
// tlen = (len<halflen) ? fabs(len-halflen) : fabs(len-halflen);
|
|
tlen = fabs(len-halflen);
|
|
factor = tlen / (size * size);
|
|
|
|
if (factor > 4)
|
|
factor = 4;
|
|
else if (factor < 1)
|
|
factor = 1;
|
|
|
|
oldwarpadd = warpadd;
|
|
warpadd = LIGHTNINGWARPFUNCTION; // was 0.30*sin(time+i+warpfactor)
|
|
|
|
len-=dec;
|
|
VectorCopy(thisvec, lastvec);
|
|
VectorAdd(tempvec, move, tempvec);
|
|
VectorAdd(thisvec, move, thisvec);
|
|
thisvec[0]= ((thisvec[0] + crandom()*size) + tempvec[0]*factor)/(factor+1);
|
|
thisvec[1]= ((thisvec[1] + crandom()*size) + tempvec[1]*factor)/(factor+1);
|
|
thisvec[2]= ((thisvec[2] + crandom()*size) + tempvec[2]*factor)/(factor+1);
|
|
}
|
|
|
|
i+=warpsize;
|
|
|
|
// one more time
|
|
if (len>0)
|
|
{
|
|
VectorAdd(p->origin, ang_right, angl_coord[0]);
|
|
VectorAdd(lastvec, ang_right, angl_coord[1]);
|
|
VectorSubtract(lastvec, ang_right, angl_coord[2]);
|
|
VectorSubtract(p->origin, ang_right, angl_coord[3]);
|
|
|
|
Vector2Set(tex_coord[0], 0+warpadd, 1);
|
|
Vector2Set(tex_coord[1], 0+oldwarpadd, 0);
|
|
Vector2Set(tex_coord[2], 1+oldwarpadd, 0);
|
|
Vector2Set(tex_coord[3], 1+warpadd, 1);
|
|
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+1;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+3;
|
|
for (j=0; j<4; j++)
|
|
{
|
|
VA_SetElem2(texCoordArray[0][rb_vertex], tex_coord[j][0], tex_coord[j][1]);
|
|
VA_SetElem3(vertexArray[rb_vertex], angl_coord[j][0], angl_coord[j][1], angl_coord[j][2]);
|
|
VA_SetElem4(colorArray[rb_vertex], partColor[0], partColor[1], partColor[2], partColor[3]);
|
|
rb_vertex++;
|
|
}
|
|
}
|
|
}
|
|
else if (p->flags & PART_DIRECTION) // streched out in direction- beams etc...
|
|
{
|
|
vec3_t ang_up, ang_right, vdelta;
|
|
int i;
|
|
|
|
VectorAdd(p->angle, p->origin, vdelta);
|
|
SetBeamAngles(vdelta, p->origin, ang_up, ang_right);
|
|
|
|
VectorScale (ang_right, 0.75f, ang_right);
|
|
VectorScale (ang_up, 0.75f * VectorLength(p->angle), ang_up);
|
|
|
|
VectorAdd (ang_up, ang_right, angl_coord[0]);
|
|
VectorSubtract (ang_right, ang_up, angl_coord[1]);
|
|
VectorNegate (angl_coord[0], angl_coord[2]);
|
|
VectorNegate (angl_coord[1], angl_coord[3]);
|
|
|
|
VectorMA(p->origin, size, angl_coord[0], ParticleVec[0]);
|
|
VectorMA(p->origin, size, angl_coord[1], ParticleVec[1]);
|
|
VectorMA(p->origin, size, angl_coord[2], ParticleVec[2]);
|
|
VectorMA(p->origin, size, angl_coord[3], ParticleVec[3]);
|
|
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+1;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+3;
|
|
for (i=0; i<4; i++)
|
|
{
|
|
VA_SetElem2(texCoordArray[0][rb_vertex], tex_coord[i][0], tex_coord[i][1]);
|
|
VA_SetElem3(vertexArray[rb_vertex], ParticleVec[i][0], ParticleVec[i][1], ParticleVec[i][2]);
|
|
VA_SetElem4(colorArray[rb_vertex], partColor[0], partColor[1], partColor[2], partColor[3]);
|
|
rb_vertex++;
|
|
}
|
|
}
|
|
else if (p->flags & PART_ANGLED) // facing direction...
|
|
{
|
|
vec3_t ang_up, ang_right, ang_forward;
|
|
int j;
|
|
|
|
AngleVectors(p->angle, ang_forward, ang_right, ang_up);
|
|
|
|
VectorScale (ang_right, 0.75f, ang_right);
|
|
VectorScale (ang_up, 0.75f, ang_up);
|
|
|
|
VectorAdd (ang_up, ang_right, angl_coord[0]);
|
|
VectorSubtract (ang_right, ang_up, angl_coord[1]);
|
|
VectorNegate (angl_coord[0], angl_coord[2]);
|
|
VectorNegate (angl_coord[1], angl_coord[3]);
|
|
|
|
VectorMA(p->origin, size, angl_coord[0], ParticleVec[0]);
|
|
VectorMA(p->origin, size, angl_coord[1], ParticleVec[1]);
|
|
VectorMA(p->origin, size, angl_coord[2], ParticleVec[2]);
|
|
VectorMA(p->origin, size, angl_coord[3], ParticleVec[3]);
|
|
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+1;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+3;
|
|
for (j=0; j<4; j++)
|
|
{
|
|
VA_SetElem2(texCoordArray[0][rb_vertex], tex_coord[j][0], tex_coord[j][1]);
|
|
VA_SetElem3(vertexArray[rb_vertex], ParticleVec[j][0],ParticleVec[j][1], ParticleVec[j][2]);
|
|
VA_SetElem4(colorArray[rb_vertex], partColor[0], partColor[1], partColor[2], partColor[3]);
|
|
rb_vertex++;
|
|
}
|
|
}
|
|
else // normal sprites
|
|
{
|
|
vec3_t ang_up, ang_right, ang_forward;
|
|
int j;
|
|
|
|
if (p->angle[2]) // if we have roll, we do calcs
|
|
{
|
|
VectorSubtract(p->origin, r_newrefdef.vieworg, angl_coord[0]);
|
|
|
|
VecToAngleRolled(angl_coord[0], p->angle[2], angl_coord[1]);
|
|
AngleVectors(angl_coord[1], ang_forward, ang_right, ang_up);
|
|
|
|
VectorScale (ang_forward, 0.75f, ang_forward);
|
|
VectorScale (ang_right, 0.75f, ang_right);
|
|
VectorScale (ang_up, 0.75f, ang_up);
|
|
|
|
VectorAdd (ang_up, ang_right, angl_coord[0]);
|
|
VectorSubtract (ang_right, ang_up, angl_coord[1]);
|
|
VectorNegate (angl_coord[0], angl_coord[2]);
|
|
VectorNegate (angl_coord[1], angl_coord[3]);
|
|
|
|
VectorMA(p->origin, size, angl_coord[0], ParticleVec[0]);
|
|
VectorMA(p->origin, size, angl_coord[1], ParticleVec[1]);
|
|
VectorMA(p->origin, size, angl_coord[2], ParticleVec[2]);
|
|
VectorMA(p->origin, size, angl_coord[3], ParticleVec[3]);
|
|
}
|
|
else
|
|
{
|
|
VectorMA(p->origin, size, coord[0], ParticleVec[0]);
|
|
VectorMA(p->origin, size, coord[1], ParticleVec[1]);
|
|
VectorMA(p->origin, size, coord[2], ParticleVec[2]);
|
|
VectorMA(p->origin, size, coord[3], ParticleVec[3]);
|
|
}
|
|
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+1;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+3;
|
|
for (j=0; j<4; j++)
|
|
{
|
|
VA_SetElem2(texCoordArray[0][rb_vertex], tex_coord[j][0], tex_coord[j][1]);
|
|
VA_SetElem3(vertexArray[rb_vertex], ParticleVec[j][0],ParticleVec[j][1], ParticleVec[j][2]);
|
|
VA_SetElem4(colorArray[rb_vertex], partColor[0], partColor[1], partColor[2], partColor[3]);
|
|
rb_vertex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_DrawAllParticles
|
|
===============
|
|
*/
|
|
void R_DrawAllParticles (void)
|
|
{
|
|
int i;
|
|
particle_t *p;
|
|
|
|
R_BeginParticles (false);
|
|
|
|
for ( i=0; i < r_newrefdef.num_particles; i++)
|
|
{
|
|
if (r_transrendersort->integer && !(r_newrefdef.rdflags & RDF_NOWORLDMODEL))
|
|
{
|
|
p = sorted_parts[i].p;
|
|
if ( r_particledistance->value > 0 && sorted_parts[i].len > (100.0*r_particledistance->value))
|
|
continue;
|
|
}
|
|
else
|
|
p=&r_newrefdef.particles[i];
|
|
R_RenderParticle(p);
|
|
}
|
|
|
|
R_FinishParticles (false);
|
|
}
|
|
|
|
|
|
#ifdef BINARY_PART_SORT
|
|
/*
|
|
===============
|
|
R_DrawParticles
|
|
===============
|
|
*/
|
|
void R_DrawParticles (sortedelement_t *list)
|
|
{
|
|
R_BeginParticles (false);
|
|
|
|
RenderParticleTree(list);
|
|
|
|
R_FinishParticles (false);
|
|
}
|
|
#endif // BINARY_PART_SORT
|
|
|
|
|
|
/*
|
|
===================================================
|
|
|
|
DECAL RENDERING
|
|
|
|
===================================================
|
|
*/
|
|
|
|
/*
|
|
===============
|
|
R_RenderDecal
|
|
===============
|
|
*/
|
|
void R_RenderDecal (particle_t *p)
|
|
{
|
|
float size, alpha;
|
|
vec2_t tex_coord[4];
|
|
vec3_t angl_coord[4];
|
|
vec3_t ang_up, ang_right, ang_forward, color;
|
|
vec4_t partColor;
|
|
int i, j, numVerts, numIndex;
|
|
int mask, aggregatemask = ~0;
|
|
|
|
particleRenderState_t thisPart;
|
|
|
|
// frustum cull
|
|
if (p->decal)
|
|
{
|
|
for (i=0; i<p->decal->numpolys; i++)
|
|
{
|
|
mask = 0;
|
|
for (j=0; j<4; j++) {
|
|
float dp = DotProduct(frustum[j].normal, p->decal->polys[i]);
|
|
if ( ( dp - frustum[j].dist ) < 0 )
|
|
mask |= (1<<j);
|
|
}
|
|
aggregatemask &= mask;
|
|
}
|
|
if (aggregatemask)
|
|
return;
|
|
}
|
|
|
|
size = (p->size>0.1) ? p->size : 0.1;
|
|
alpha = p->alpha;
|
|
|
|
if (p->decal)
|
|
thisPart.polygon_offset_fill = true;
|
|
else
|
|
thisPart.polygon_offset_fill = false;
|
|
|
|
thisPart.polymode = GL_TRIANGLES;
|
|
// thisPart.texture2D = true;
|
|
thisPart.overbright = false;
|
|
thisPart.alphatest = false;
|
|
thisPart.blendfunc_src = p->blendfunc_src;
|
|
thisPart.blendfunc_dst = p->blendfunc_dst;
|
|
thisPart.imagenum = TexParticle(p->image);
|
|
thisPart.depth_hack = DEPTHHACK_NONE;
|
|
|
|
numVerts = (p->decal) ? p->decal->numpolys : 4;
|
|
numIndex = (p->decal) ? (p->decal->numpolys-2)*3 : 4;
|
|
|
|
// check if render state changed from last particle
|
|
R_CheckParticleRenderState (&thisPart, numVerts, numIndex);
|
|
|
|
if (p->flags & PART_SHADED) {
|
|
GetParticleLight (p, p->origin, r_particle_lighting->value, shadelight);
|
|
VectorSet(color, shadelight[0]*DIV255, shadelight[1]*DIV255, shadelight[2]*DIV255);
|
|
}
|
|
else {
|
|
VectorSet(shadelight, p->red, p->green, p->blue);
|
|
VectorSet(color, p->red*DIV255, p->green*DIV255, p->blue*DIV255);
|
|
}
|
|
|
|
if (p->flags & PART_ALPHACOLOR)
|
|
Vector4Set(partColor, color[0]*alpha, color[1]*alpha, color[2]*alpha, alpha);
|
|
else
|
|
Vector4Set(partColor, color[0], color[1], color[2], alpha);
|
|
|
|
if (p->decal)
|
|
{
|
|
for (i = 0; i < p->decal->numpolys-2; i++) {
|
|
indexArray[rb_index++] = rb_vertex;
|
|
indexArray[rb_index++] = rb_vertex+i+1;
|
|
indexArray[rb_index++] = rb_vertex+i+2;
|
|
}
|
|
for (i = 0; i < p->decal->numpolys; i++)
|
|
{
|
|
VA_SetElem2(texCoordArray[0][rb_vertex], p->decal->coords[i][0], p->decal->coords[i][1]);
|
|
VA_SetElem3(vertexArray[rb_vertex], p->decal->polys[i][0], p->decal->polys[i][1], p->decal->polys[i][2]);
|
|
VA_SetElem4(colorArray[rb_vertex], partColor[0], partColor[1], partColor[2], partColor[3]);
|
|
rb_vertex++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AngleVectors(p->angle, ang_forward, ang_right, ang_up);
|
|
|
|
VectorScale (ang_right, 0.75f, ang_right);
|
|
VectorScale (ang_up, 0.75f, ang_up);
|
|
|
|
VectorAdd (ang_up, ang_right, angl_coord[0]);
|
|
VectorSubtract (ang_right, ang_up, angl_coord[1]);
|
|
VectorNegate (angl_coord[0], angl_coord[2]);
|
|
VectorNegate (angl_coord[1], angl_coord[3]);
|
|
|
|
VectorMA(p->origin, size, angl_coord[0], ParticleVec[0]);
|
|
VectorMA(p->origin, size, angl_coord[1], ParticleVec[1]);
|
|
VectorMA(p->origin, size, angl_coord[2], ParticleVec[2]);
|
|
VectorMA(p->origin, size, angl_coord[3], ParticleVec[3]);
|
|
|
|
Vector2Set (tex_coord[0], 0, 1);
|
|
Vector2Set (tex_coord[1], 0, 0);
|
|
Vector2Set (tex_coord[2], 1, 0);
|
|
Vector2Set (tex_coord[3], 1, 1);
|
|
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+1;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+0;
|
|
indexArray[rb_index++] = rb_vertex+2;
|
|
indexArray[rb_index++] = rb_vertex+3;
|
|
for (i=0; i<4; i++)
|
|
{
|
|
VA_SetElem2(texCoordArray[0][rb_vertex], tex_coord[i][0], tex_coord[i][1]);
|
|
VA_SetElem3(vertexArray[rb_vertex], ParticleVec[i][0], ParticleVec[i][1], ParticleVec[i][2]);
|
|
VA_SetElem4(colorArray[rb_vertex], partColor[0], partColor[1], partColor[2], partColor[3]);
|
|
rb_vertex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_DrawAllDecals
|
|
===============
|
|
*/
|
|
void R_DrawAllDecals (void)
|
|
{
|
|
int i;
|
|
particle_t *d;
|
|
|
|
R_BeginParticles (true);
|
|
|
|
for ( i=0; i < r_newrefdef.num_decals; i++)
|
|
{
|
|
d = &r_newrefdef.decals[i];
|
|
if (d->decal) // skip if not going to be visible
|
|
if ((d->decal->node == NULL) || (d->decal->node->visframe != r_visframecount))
|
|
continue;
|
|
R_RenderDecal(d);
|
|
}
|
|
|
|
R_FinishParticles (true);
|
|
}
|