ew-engine/hq engine src/gl_decals.c

649 lines
16 KiB
C
Raw Normal View History

2006-10-08 00:00:00 +00:00
/*
Copyright (C) 2001-2002 Charles Hollemeersch
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.
Adds decal support...
Decals are similar to particles currently...
The code to generate them is based on the article "Applying decals to arbitrary surfaces"
by "Eric Lengyel" in "Game Programming Gems 2"
*/
#include "quakedef.h"
//#include "r_local.h"
#ifdef USETENEBRAEDECALS
#define MAX_DECAL_VERTICES 128
#define MAX_DECAL_TRIANGLES 64
//PENTA: Decals
typedef enum {
dt_blood
} dtype_t;
typedef struct decal_s
{
vec3_t origin;
vec3_t normal;
vec3_t tangent;
float radius;
float color[4], startcolor[4], endcolor[4];
struct decal_s *next;
float die;
float lifetime;
dtype_t type;
int texture; //texture object of particle
int srcblend;
int dstblend;
//geometry of decail
int vertexCount, triangleCount;
vec3_t vertexArray[MAX_DECAL_VERTICES];
float texcoordArray[MAX_DECAL_VERTICES][2];
int triangleArray[MAX_DECAL_TRIANGLES][3];
} decal_t;
typedef enum {
pt_static, pt_grav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2
} ptype_t;
typedef enum {
pb_add, pb_subtract
} pblend_t;
typedef enum {
emt_box
} emtype_t;
typedef enum {
align_view, align_vel, align_surf
} align_t;
typedef struct ParticleEffect_s {
char name[64];
vec3_t emmiterParams1, emmiterParams2; //mins maxs of emmiter box
emtype_t emmiterType; //currently only box is supported
vec3_t startcolormin, startcolormax, endcolormin, endcolormax;
vec3_t velocitymin, velocitymax; //min max velocity
float lifemin, lifemax; //min max life
int movetype; //how it moves (gravity/collision...)
vec3_t gravity;
float rotmin, rotmax;//rotaiton speed
float growmin, growmax;//scale speed (make second smaller to shrink)
float sizemin, sizemax;//scale speed (make second smaller to shrink)
vec3_t drag; //drag of particle (how fast it looses it's speed)
int srcblend, dstblend; // gl enums for blend modes
int numbounces; //number of bounces before particle is deleted
int texture; //gl object of the texture
align_t align;//particle is aligned with its velocity
float velscale;
struct ParticleEffect_s *spawn; //particle effect to spawn on hit
struct ParticleEffect_s *next;
} ParticleEffect_t;
#define MAX_DECALS 1024 // default max # of decals at one
// time
#define ABSOLUTE_MIN_DECALS 0 // no fewer than this no matter what's
// on the command line
//PENTA: "modern" vertices
//WARNING: if you change the size of this also change VERTEXSIZE
typedef struct
{
vec3_t position;
float texture[2];
float lightmap[2];
} mmvertex_t;
decal_t *active_decals, *free_decals;
decal_t *decals;
int r_numdecals;
void DecalClipLeaf(decal_t *dec, mleaf_t *leaf);
void DecalWalkBsp_R(decal_t *dec, mnode_t *node);
int DecalClipPolygonAgainstPlane(plane_t *plane, int vertexCount, vec3_t *vertex, vec3_t *newVertex);
int DecalClipPolygon(int vertexCount, vec3_t *vertices, vec3_t *newVertex);
/*
===============
R_InitDecals
===============
*/
void R_InitDecals (void)
{
int i;
i = COM_CheckParm ("-decals");
if (i)
{
r_numdecals = (int)(Q_atoi(com_argv[i+1]));
if (r_numdecals < ABSOLUTE_MIN_DECALS)
r_numdecals = ABSOLUTE_MIN_DECALS;
}
else
{
r_numdecals = MAX_DECALS;
}
decals = (decal_t *)
Hunk_AllocName (r_numdecals * sizeof(decal_t), "decals");
}
/*
===============
R_ClearDecals
===============
*/
void R_ClearDecals (void)
{
int i;
free_decals = &decals[0];
active_decals = NULL;
for (i=0 ;i<r_numdecals ; i++)
decals[i].next = &decals[i+1];
decals[r_numdecals-1].next = NULL;
}
static plane_t leftPlane, rightPlane, bottomPlane, topPlane, backPlane, frontPlane;
#define RandomMinMax(min,max) lhrandom(min,max)
extern int blood_tex;
void R_SpawnDecal(vec3_t center, vec3_t normal, vec3_t tangent, ParticleEffect_t *effect)
{
float width, height, depth, d;
vec3_t binormal;
decal_t *dec;
float one_over_w, one_over_h;
int a;
vec3_t test = {0.5, 0.5, 0.5};
vec3_t t;
//allocate decal
if (!free_decals)
return;
dec = free_decals;
free_decals = dec->next;
dec->next = active_decals;
active_decals = dec;
//VectorNormalize(tangent);
VectorNormalize(test);
CrossProduct(normal,test,tangent);
//Con_Printf("StartDecail\n");
VectorCopy(center, dec->origin);
VectorCopy(tangent, dec->tangent);
VectorCopy(normal, dec->normal);
VectorNormalize(tangent);
VectorNormalize(normal);
CrossProduct(normal, tangent, binormal);
VectorNormalize(binormal);
//width = RandomMinMax(effect->sizemin, effect->sizemax);
//width = lhrandom(effect->sizemin, effect->sizemax);
width = lhrandom(10,20);
height = width;
depth = width*0.5;
dec->radius = max(max(width,height),depth);
dec->texture = blood_tex;
//dec->lifetime = RandomMinMax(effect->lifemin, effect->lifemax);
//dec->lifetime = lhrandom(effect->lifemin, effect->lifemax);
dec->lifetime = lhrandom(cl.time+20,cl.time+120);
dec->die = cl.time + dec->lifetime;
/*
dec->startcolor[0] = RandomMinMax(effect->startcolormin[0], effect->startcolormax[0]);
dec->startcolor[1] = RandomMinMax(effect->startcolormin[1], effect->startcolormax[1]);
dec->startcolor[2] = RandomMinMax(effect->startcolormin[2], effect->startcolormax[2]);
dec->endcolor[0] = RandomMinMax(effect->endcolormin[0], effect->endcolormax[0]);
dec->endcolor[1] = RandomMinMax(effect->endcolormin[1], effect->endcolormax[1]);
dec->endcolor[2] = RandomMinMax(effect->endcolormin[2], effect->endcolormax[2]);
dec->srcblend = effect->srcblend;
dec->dstblend = effect->dstblend;
*/
/* -= Hack: don't clip decal just draw a quad =-
VectorScale(binormal,10,binormal);
VectorScale(tangent,10,tangent);
VectorCopy(center,dec->vertexArray[0]);
VectorAdd(center,tangent,t);
VectorCopy(t,dec->vertexArray[1]);
VectorAdd(t,binormal,t);
VectorCopy(t,dec->vertexArray[2]);
VectorAdd(center,binormal,t);
VectorCopy(t,dec->vertexArray[3]);
dec->vertexCount = 4;
dec->triangleCount = 2;
dec->triangleArray[0][0] = 0;
dec->triangleArray[0][1] = 1;
dec->triangleArray[0][2] = 2;
dec->triangleArray[1][0] = 0;
dec->triangleArray[1][1] = 2;
dec->triangleArray[1][2] = 3;
/*/
// Calculate boundary planes
d = DotProduct(center, tangent);
VectorCopy(tangent,leftPlane.normal);
leftPlane.dist = -(width * 0.5 - d);
VectorInverse(tangent);
VectorCopy(tangent,rightPlane.normal);
VectorInverse(tangent);
rightPlane.dist = -(width * 0.5 + d);
d = DotProduct(center, binormal);
VectorCopy(binormal,bottomPlane.normal);
bottomPlane.dist = -(height * 0.5 - d);
VectorInverse(binormal);
VectorCopy(binormal,topPlane.normal);
VectorInverse(binormal);
topPlane.dist = -(height * 0.5 + d);
d = DotProduct(center, normal);
VectorCopy(normal,backPlane.normal);
backPlane.dist = -(depth - d);
VectorInverse(normal);
VectorCopy(normal,frontPlane.normal);
VectorInverse(normal);
frontPlane.dist = -(depth + d);
/*/*/
// Begin with empty mesh
dec->vertexCount = 0;
dec->triangleCount = 0;
//Clip decal to bsp
DecalWalkBsp_R(dec, cl.worldmodel->nodes);
//This happens when a decal is to far from any surface or the surface is to steeply sloped
if (dec->triangleCount == 0) {
//deallocate decal
active_decals = dec->next;
dec->next = free_decals;
free_decals = dec;
return;
}
//Assign texture mapping coordinates
one_over_w = 1.0F / width;
one_over_h = 1.0F / height;
for (a = 0; a < dec->vertexCount; a++)
{
float s, t;
vec3_t v;
VectorSubtract(dec->vertexArray[a],center,v);
s = DotProduct(v, tangent) * one_over_w + 0.5F;
t = DotProduct(v, binormal) * one_over_h + 0.5F;
dec->texcoordArray[a][0] = s;
dec->texcoordArray[a][1] = t;
}
}
void DecalWalkBsp_R(decal_t *dec, mnode_t *node)
{
mplane_t *plane;
float dist;
mleaf_t *leaf;
if (node->contents < 0) {
//we are in a leaf
leaf = (mleaf_t *)node;
DecalClipLeaf(dec, leaf);
return;
}
plane = node->plane;
dist = DotProduct (dec->origin, plane->normal) - plane->dist;
if (dist > dec->radius)
{
DecalWalkBsp_R (dec, node->children[0]);
return;
}
if (dist < -dec->radius)
{
DecalWalkBsp_R (dec, node->children[1]);
return;
}
DecalWalkBsp_R (dec, node->children[0]);
DecalWalkBsp_R (dec, node->children[1]);
}
qboolean DecalAddPolygon(decal_t *dec, int vertcount, vec3_t *vertices)
{
int *triangle;
int count;
int a, b;
float f;
//Con_Printf("AddPolygon %i %i\n",vertcount, dec->vertexCount);
count = dec->vertexCount;
if (count + vertcount >= MAX_DECAL_VERTICES)
return false;
if (dec->triangleCount + vertcount-2 >= MAX_DECAL_TRIANGLES)
return false;
// Add polygon as a triangle fan
triangle = &dec->triangleArray[dec->triangleCount][0];
for (a = 2; a < vertcount; a++)
{
dec->triangleArray[dec->triangleCount][0] = count;
dec->triangleArray[dec->triangleCount][1] = (count + a - 1);
dec->triangleArray[dec->triangleCount][2] = (count + a );
dec->triangleCount++;
}
// Assign vertex colors
for (b = 0; b < vertcount; b++)
{
VectorCopy(vertices[b],dec->vertexArray[count]);
count++;
}
dec->vertexCount = count;
return true;
}
const float decalEpsilon = 0.001;
mmvertex_t *globalVertexTable = NULL;
void DecalClipLeaf(decal_t *dec, mleaf_t *leaf)
{
int a;
vec3_t newVertex[64], t1, t2, t3;
int c;
msurface_t **surf;
//Con_Printf("Clipleaf\n");
c = leaf->nummarksurfaces;
surf = leaf->firstmarksurface;
//for all surfaces in the leaf
for (c=0; c<leaf->nummarksurfaces; c++, surf++) {
glpoly_t *poly;
int i,count;
float *v;
poly = (*surf)->polys;
//v = (float *)(&globalVertexTable[poly->firstvertex]);
v = (float *)(&globalVertexTable[0]);
for (i=0 ; i<poly->numverts ; i++, v+= VERTEXSIZE)
{
newVertex[i][0] = v[0];
newVertex[i][1] = v[1];
newVertex[i][2] = v[2];
}
VectorCopy((*surf)->plane->normal,t3);
if ((*surf)->flags & SURF_PLANEBACK) {
VectorInverse(t3);
}
//avoid backfacing and ortogonal facing faces to recieve decal parts
if (DotProduct(dec->normal, t3) > decalEpsilon)
{
count = DecalClipPolygon(poly->numverts, newVertex, newVertex);
if ((count != 0) && (!DecalAddPolygon(dec, count, newVertex))) break;
}
}
}
int DecalClipPolygon(int vertexCount, vec3_t *vertices, vec3_t *newVertex)
{
vec3_t tempVertex[64];
// Clip against all six planes
int count = DecalClipPolygonAgainstPlane(&leftPlane, vertexCount, vertices, tempVertex);
if (count != 0)
{
count = DecalClipPolygonAgainstPlane(&rightPlane, count, tempVertex, newVertex);
if (count != 0)
{
count = DecalClipPolygonAgainstPlane(&bottomPlane, count, newVertex, tempVertex);
if (count != 0)
{
count = DecalClipPolygonAgainstPlane(&topPlane, count, tempVertex, newVertex);
if (count != 0)
{
count = DecalClipPolygonAgainstPlane(&backPlane, count, newVertex, tempVertex);
if (count != 0)
{
count = DecalClipPolygonAgainstPlane(&frontPlane, count, tempVertex, newVertex);
}
}
}
}
}
return count;
}
int DecalClipPolygonAgainstPlane(plane_t *plane, int vertexCount, vec3_t *vertex, vec3_t *newVertex)
{
qboolean negative[65];
int a, count, b, c;
float sect, t;
vec3_t v, v1, v2;
// Classify vertices
int negativeCount = 0;
for (a = 0; a < vertexCount; a++)
{
qboolean neg = ((DotProduct(plane->normal, vertex[a]) - plane->dist) < 0.0);
negative[a] = neg;
negativeCount += neg;
}
// Discard this polygon if it's completely culled
if (negativeCount == vertexCount) {
return (0);
}
count = 0;
for (b = 0; b < vertexCount; b++)
{
// c is the index of the previous vertex
c = (b != 0) ? b - 1 : vertexCount - 1;
if (negative[b])
{
if (!negative[c])
{
// Current vertex is on negative side of plane,
// but previous vertex is on positive side.
VectorCopy(vertex[c],v1);
VectorCopy(vertex[b],v2);
t = (DotProduct(plane->normal, v1) - plane->dist) / (
plane->normal[0] * (v1[0] - v2[0])
+ plane->normal[1] * (v1[1] - v2[1])
+ plane->normal[2] * (v1[2] - v2[2]));
VectorScale(v1,(1.0 - t),newVertex[count]);
VectorMA(newVertex[count],t,v2,newVertex[count]);
count++;
}
}
else
{
if (negative[c])
{
// Current vertex is on positive side of plane,
// but previous vertex is on negative side.
VectorCopy(vertex[b],v1);
VectorCopy(vertex[c],v2);
t = (DotProduct(plane->normal, v1) - plane->dist) / (
plane->normal[0] * (v1[0] - v2[0])
+ plane->normal[1] * (v1[1] - v2[1])
+ plane->normal[2] * (v1[2] - v2[2]));
VectorScale(v1,(1.0 - t),newVertex[count]);
VectorMA(newVertex[count],t,v2,newVertex[count]);
count++;
}
// Include current vertex
VectorCopy(vertex[b],newVertex[count]);
count++;
}
}
// Return number of vertices in clipped polygon
return count;
}
/*
===============
R_DrawDecails
===============
*/
extern cvar_t sv_gravity;
extern float color_black[4];
void R_DrawDecals (void)
{
decal_t *p, *kill;
int i;
float blend, blend1;
vec3_t colors;
glFogfv(GL_FOG_COLOR, color_black);
// glEnable (GL_BLEND);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER,0.000);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glDepthMask(0);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1,-1);
for ( ;; )
{
kill = active_decals;
if (kill && kill->die < cl.time)
{
active_decals = kill->next;
kill->next = free_decals;
free_decals = kill;
continue;
}
break;
}
for (p=active_decals ; p ; p=p->next)
{
for ( ;; )
{
kill = p->next;
//XYZ
if (kill && (kill->die < cl.time))
{
p->next = kill->next;
kill->next = free_decals;
free_decals = kill;
continue;
}
break;
}
GL_Bind(p->texture);
glBlendFunc (p->srcblend, p->dstblend);
//calculate color based on life ...
blend = (p->die-cl.time)/p->lifetime;
blend1 = 1-blend;
for (i=0; i<3; i++) {
p->color[i] = p->startcolor[i] * blend + p->endcolor[i] * blend1;
}
if ((p->die - cl.time) < 0.5) {
float scale = 2*(p->die - cl.time);
glColor4f((p->color[0]*scale), (p->color[1]*scale), (p->color[2]*scale), scale);
} else {
glColor3fv(&p->color[0]);
}
//Con_Printf("Drawdec %i\n",p->triangleCount);
for (i=0; i<p->triangleCount; i++) {
int i1, i2, i3;
i1 = p->triangleArray[i][0];
i2 = p->triangleArray[i][1];
i3 = p->triangleArray[i][2];
glBegin(GL_TRIANGLES);
glTexCoord2fv(&p->texcoordArray[i1][0]);
glVertex3fv(&p->vertexArray[i1][0]);
glTexCoord2fv(&p->texcoordArray[i2][0]);
glVertex3fv(&p->vertexArray[i2][0]);
glTexCoord2fv(&p->texcoordArray[i3][0]);
glVertex3fv(&p->vertexArray[i3][0]);
glEnd();
}
}
glDisable(GL_POLYGON_OFFSET_FILL);
glEnable(GL_DEPTH_TEST);
glDepthMask(1);
// glDisable (GL_BLEND);
glDisable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER,0.666);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
//glFog(GL_FOG_COLOR, fog_color);
colors[0] = gl_fogred.value;
colors[1] = gl_foggreen.value;
colors[2] = gl_fogblue.value;
glFogfv(GL_FOG_COLOR, colors);
}
#endif