575 lines
14 KiB
C
575 lines
14 KiB
C
/*
|
|
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"
|
|
|
|
#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
|
|
|
|
|
|
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;
|
|
|
|
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);
|
|
height = width;
|
|
depth = width*0.5;
|
|
dec->radius = max(max(width,height),depth);
|
|
dec->texture = effect->texture;
|
|
dec->lifetime = RandomMinMax(effect->lifemin, effect->lifemax);
|
|
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 & CONTENTS_LEAF) {
|
|
|
|
//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;
|
|
|
|
void DecalClipLeaf(decal_t *dec, mleaf_t *leaf)
|
|
{
|
|
int a, i, count, k;
|
|
vec3_t newVertex[64], t1, t2, t3;
|
|
int c;
|
|
msurface_t **surf;
|
|
float *v;
|
|
|
|
//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;
|
|
|
|
poly = (*surf)->polys;
|
|
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)
|
|
{
|
|
|
|
for (i=0 ; i<poly->numindecies ; i+=3)
|
|
{
|
|
int j;
|
|
for (j=0; j<3; j++) {
|
|
v = (float *)(&globalVertexTable[poly->indecies[i+j]]);
|
|
newVertex[j][0] = v[0];
|
|
newVertex[j][1] = v[1];
|
|
newVertex[j][2] = v[2];
|
|
}
|
|
|
|
count = DecalClipPolygon(3, newVertex, newVertex);
|
|
if ((count != 0) && (!DecalAddPolygon(dec, count, newVertex))) break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//for all surfaces in the leaf
|
|
for (k=0; k<leaf->nummeshes; k++) {
|
|
mesh_t *mesh = &cl.worldmodel->meshes[cl.worldmodel->leafmeshes[leaf->firstmesh+k]];
|
|
|
|
for (i=0; i<mesh->numindecies ; i+=3)
|
|
{
|
|
int j;
|
|
|
|
if (DotProduct(dec->normal, mesh->normals[mesh->indecies[i]]) < decalEpsilon) continue;
|
|
|
|
for (j=0; j<3; j++) {
|
|
v = (float *)(&globalVertexTable[mesh->firstvertex+mesh->indecies[i+j]]);
|
|
newVertex[j][0] = v[0];
|
|
newVertex[j][1] = v[1];
|
|
newVertex[j][2] = v[2];
|
|
}
|
|
|
|
count = DecalClipPolygon(3, 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;
|
|
|
|
void R_DrawDecals (void)
|
|
{
|
|
decal_t *p, *kill;
|
|
int i;
|
|
float blend, blend1;
|
|
|
|
glFogfv(GL_FOG_COLOR, color_black);
|
|
if (!gl_wireframe.value) 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 (!gl_wireframe.value) {
|
|
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]);
|
|
}
|
|
} else {
|
|
glColor3ub(0,0,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);
|
|
glFogfv(GL_FOG_COLOR, fog_color);
|
|
}
|
|
|