2011-05-09 22:00:00 +02:00
|
|
|
/*
|
|
|
|
gl_decals.c - decal paste and rendering
|
|
|
|
Copyright (C) 2010 Uncle Mike
|
|
|
|
|
|
|
|
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 3 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.
|
|
|
|
*/
|
2010-12-02 22:00:00 +01:00
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "client.h"
|
|
|
|
#include "gl_local.h"
|
2010-12-23 22:00:00 +01:00
|
|
|
#include "cl_tent.h"
|
2010-12-02 22:00:00 +01:00
|
|
|
|
2010-12-23 22:00:00 +01:00
|
|
|
#define DECAL_DISTANCE 4 // too big values produce more clipped polygons
|
|
|
|
#define MAX_DECALCLIPVERT 32 // produced vertexes of fragmented decal
|
|
|
|
#define DECAL_CACHEENTRY 256 // MUST BE POWER OF 2 or code below needs to change!
|
2011-10-09 22:00:00 +02:00
|
|
|
#define DECAL_TRANSPARENT_THRESHOLD 230 // transparent decals draw with GL_MODULATE
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
// empirically determined constants for minimizing overalpping decals
|
2011-01-03 22:00:00 +01:00
|
|
|
#define MAX_OVERLAP_DECALS 6
|
2010-12-23 22:00:00 +01:00
|
|
|
#define DECAL_OVERLAP_DIST 8
|
2011-03-31 22:00:00 +02:00
|
|
|
#define FLOAT_TO_SHORT( x ) (short)(x * 32)
|
|
|
|
#define SHORT_TO_FLOAT( x ) ((float)x * (1.0f/32.0f))
|
2010-12-23 22:00:00 +01:00
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
// clip edges
|
|
|
|
#define LEFT_EDGE 0
|
|
|
|
#define RIGHT_EDGE 1
|
|
|
|
#define TOP_EDGE 2
|
|
|
|
#define BOTTOM_EDGE 3
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
// This structure contains the information used to create new decals
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
vec3_t m_Position; // world coordinates of the decal center
|
|
|
|
vec3_t m_SAxis; // the s axis for the decal in world coordinates
|
2011-10-09 22:00:00 +02:00
|
|
|
model_t *m_pModel; // the model the decal is going to be applied in
|
2010-12-23 22:00:00 +01:00
|
|
|
int m_iTexture; // The decal material
|
|
|
|
int m_Size; // Size of the decal (in world coords)
|
|
|
|
int m_Flags;
|
|
|
|
int m_Entity; // Entity the decal is applied to.
|
|
|
|
float m_scale;
|
|
|
|
int m_decalWidth;
|
|
|
|
int m_decalHeight;
|
|
|
|
vec3_t m_Basis[3];
|
|
|
|
} decalinfo_t;
|
2010-12-02 22:00:00 +01:00
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
static float g_DecalClipVerts[MAX_DECALCLIPVERT][VERTEXSIZE];
|
|
|
|
static float g_DecalClipVerts2[MAX_DECALCLIPVERT][VERTEXSIZE];
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
static decal_t gDecalPool[MAX_RENDER_DECALS];
|
|
|
|
static int gDecalCount;
|
|
|
|
|
|
|
|
void R_ClearDecals( void )
|
|
|
|
{
|
2011-03-10 22:00:00 +01:00
|
|
|
Q_memset( gDecalPool, 0, sizeof( gDecalPool ));
|
2010-12-23 22:00:00 +01:00
|
|
|
gDecalCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// unlink pdecal from any surface it's attached to
|
|
|
|
static void R_DecalUnlink( decal_t *pdecal )
|
|
|
|
{
|
|
|
|
decal_t *tmp;
|
|
|
|
|
|
|
|
if( pdecal->psurface )
|
|
|
|
{
|
|
|
|
if( pdecal->psurface->pdecals == pdecal )
|
|
|
|
{
|
|
|
|
pdecal->psurface->pdecals = pdecal->pnext;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tmp = pdecal->psurface->pdecals;
|
|
|
|
if( !tmp ) Host_Error( "D_DecalUnlink: bad decal list\n" );
|
|
|
|
|
|
|
|
while( tmp->pnext )
|
|
|
|
{
|
|
|
|
if( tmp->pnext == pdecal )
|
|
|
|
{
|
|
|
|
tmp->pnext = pdecal->pnext;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tmp = tmp->pnext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pdecal->psurface = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Just reuse next decal in list
|
2011-10-09 22:00:00 +02:00
|
|
|
// A decal that spans multiple surfaces will use multiple decal_t pool entries,
|
|
|
|
// as each surface needs it's own.
|
2010-12-23 22:00:00 +01:00
|
|
|
static decal_t *R_DecalAlloc( decal_t *pdecal )
|
|
|
|
{
|
|
|
|
int limit = MAX_RENDER_DECALS;
|
|
|
|
|
|
|
|
if( r_decals->integer < limit )
|
|
|
|
limit = r_decals->integer;
|
|
|
|
|
|
|
|
if( !limit ) return NULL;
|
|
|
|
|
|
|
|
if( !pdecal )
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
// check for the odd possiblity of infinte loop
|
|
|
|
do
|
|
|
|
{
|
|
|
|
gDecalCount++;
|
|
|
|
if( gDecalCount >= limit )
|
|
|
|
gDecalCount = 0;
|
|
|
|
pdecal = gDecalPool + gDecalCount; // reuse next decal
|
|
|
|
count++;
|
|
|
|
} while(( pdecal->flags & FDECAL_PERMANENT ) && count < limit );
|
|
|
|
}
|
|
|
|
|
|
|
|
// If decal is already linked to a surface, unlink it.
|
|
|
|
R_DecalUnlink( pdecal );
|
|
|
|
|
|
|
|
return pdecal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// find decal image and grab size from it
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void R_GetDecalDimensions( int texture, int *width, int *height )
|
|
|
|
{
|
|
|
|
if( width ) *width = 1; // to avoid divide by zero
|
|
|
|
if( height ) *height = 1;
|
|
|
|
|
|
|
|
R_GetTextureParms( width, height, texture );
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// compute the decal basis based on surface normal, and preferred saxis
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void R_DecalComputeBasis( msurface_t *surf, vec3_t pSAxis, vec3_t textureSpaceBasis[3] )
|
|
|
|
{
|
|
|
|
vec3_t surfaceNormal;
|
|
|
|
|
|
|
|
// setup normal
|
|
|
|
if( surf->flags & SURF_PLANEBACK )
|
|
|
|
VectorNegate( surf->plane->normal, surfaceNormal );
|
|
|
|
else VectorCopy( surf->plane->normal, surfaceNormal );
|
|
|
|
|
|
|
|
if( pSAxis )
|
|
|
|
{
|
|
|
|
// T = S cross N
|
|
|
|
CrossProduct( pSAxis, textureSpaceBasis[2], textureSpaceBasis[1] );
|
|
|
|
|
|
|
|
// Name sure they aren't parallel or antiparallel
|
|
|
|
// In that case, fall back to the normal algorithm.
|
|
|
|
if( DotProduct( textureSpaceBasis[1], textureSpaceBasis[1] ) > 1e-6 )
|
|
|
|
{
|
|
|
|
// S = N cross T
|
|
|
|
CrossProduct( textureSpaceBasis[2], textureSpaceBasis[1], textureSpaceBasis[0] );
|
|
|
|
|
|
|
|
VectorNormalizeFast( textureSpaceBasis[0] );
|
|
|
|
VectorNormalizeFast( textureSpaceBasis[1] );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Fall through to the standard algorithm for parallel or antiparallel
|
|
|
|
}
|
|
|
|
|
|
|
|
// original Half-Life algorithm: get textureBasis from linked surface
|
|
|
|
VectorCopy( surf->texinfo->vecs[0], textureSpaceBasis[0] );
|
|
|
|
VectorCopy( surf->texinfo->vecs[1], textureSpaceBasis[1] );
|
|
|
|
VectorCopy( surfaceNormal, textureSpaceBasis[2] );
|
|
|
|
|
|
|
|
VectorNormalizeFast( textureSpaceBasis[0] );
|
|
|
|
VectorNormalizeFast( textureSpaceBasis[1] );
|
|
|
|
}
|
|
|
|
|
|
|
|
void R_SetupDecalTextureSpaceBasis( decal_t *pDecal, msurface_t *surf, int texture, vec3_t textureSpaceBasis[3], float decalWorldScale[2] )
|
|
|
|
{
|
|
|
|
float *sAxis = NULL;
|
|
|
|
int width, height;
|
|
|
|
|
|
|
|
if( pDecal->flags & FDECAL_USESAXIS )
|
|
|
|
sAxis = pDecal->saxis;
|
|
|
|
|
|
|
|
// Compute the non-scaled decal basis
|
|
|
|
R_DecalComputeBasis( surf, sAxis, textureSpaceBasis );
|
|
|
|
R_GetDecalDimensions( texture, &width, &height );
|
|
|
|
|
|
|
|
// world width of decal = ptexture->width / pDecal->scale
|
|
|
|
// world height of decal = ptexture->height / pDecal->scale
|
|
|
|
// scale is inverse, scales world space to decal u/v space [0,1]
|
|
|
|
// OPTIMIZE: Get rid of these divides
|
2010-12-25 22:00:00 +01:00
|
|
|
decalWorldScale[0] = (float)pDecal->scale / width;
|
|
|
|
decalWorldScale[1] = (float)pDecal->scale / height;
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
VectorScale( textureSpaceBasis[0], decalWorldScale[0], textureSpaceBasis[0] );
|
|
|
|
VectorScale( textureSpaceBasis[1], decalWorldScale[1], textureSpaceBasis[1] );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the initial list of vertices from the surface verts into the global array, 'verts'.
|
2011-10-09 22:00:00 +02:00
|
|
|
void R_SetupDecalVertsForMSurface( decal_t *pDecal, msurface_t *surf, vec3_t textureSpaceBasis[3], float *verts )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
|
|
|
float *v;
|
2011-10-09 22:00:00 +02:00
|
|
|
int i;
|
2010-12-23 22:00:00 +01:00
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
for( i = 0, v = surf->polys->verts[0]; i < surf->polys->numverts; i++, v += VERTEXSIZE, verts += VERTEXSIZE )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
VectorCopy( v, verts ); // copy model space coordinates
|
|
|
|
verts[3] = DotProduct( verts, textureSpaceBasis[0] ) - SHORT_TO_FLOAT( pDecal->dx ) + 0.5f;
|
|
|
|
verts[4] = DotProduct( verts, textureSpaceBasis[1] ) - SHORT_TO_FLOAT( pDecal->dy ) + 0.5f;
|
|
|
|
verts[5] = verts[6] = 0.0f;
|
2010-12-23 22:00:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Figure out where the decal maps onto the surface.
|
2011-10-09 22:00:00 +02:00
|
|
|
void R_SetupDecalClip( decal_t *pDecal, msurface_t *surf, int texture, vec3_t textureSpaceBasis[3], float decalWorldScale[2] )
|
2010-12-02 22:00:00 +01:00
|
|
|
{
|
2010-12-23 22:00:00 +01:00
|
|
|
R_SetupDecalTextureSpaceBasis( pDecal, surf, texture, textureSpaceBasis, decalWorldScale );
|
|
|
|
|
|
|
|
// Generate texture coordinates for each vertex in decal s,t space
|
|
|
|
// probably should pre-generate this, store it and use it for decal-decal collisions
|
|
|
|
// as in R_DecalsIntersect()
|
2011-03-31 22:00:00 +02:00
|
|
|
pDecal->dx = FLOAT_TO_SHORT( DotProduct( pDecal->position, textureSpaceBasis[0] ));
|
|
|
|
pDecal->dy = FLOAT_TO_SHORT( DotProduct( pDecal->position, textureSpaceBasis[1] ));
|
2010-12-23 22:00:00 +01:00
|
|
|
}
|
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
// Quick and dirty sutherland Hodgman clipper
|
|
|
|
// Clip polygon to decal in texture space
|
|
|
|
// JAY: This code is lame, change it later. It does way too much work per frame
|
|
|
|
// It can be made to recursively call the clipping code and only copy the vertex list once
|
|
|
|
int R_ClipInside( float *vert, int edge )
|
|
|
|
{
|
|
|
|
switch( edge )
|
|
|
|
{
|
|
|
|
case LEFT_EDGE:
|
|
|
|
if( vert[3] > 0.0f )
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
case RIGHT_EDGE:
|
|
|
|
if( vert[3] < 1.0f )
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
case TOP_EDGE:
|
|
|
|
if( vert[4] > 0.0f )
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
case BOTTOM_EDGE:
|
|
|
|
if( vert[4] < 1.0f )
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void R_ClipIntersect( float *one, float *two, float *out, int edge )
|
|
|
|
{
|
|
|
|
float t;
|
|
|
|
|
|
|
|
// t is the parameter of the line between one and two clipped to the edge
|
|
|
|
// or the fraction of the clipped point between one & two
|
|
|
|
// vert[0], vert[1], vert[2] is X, Y, Z
|
|
|
|
// vert[3] is u
|
|
|
|
// vert[4] is v
|
|
|
|
// vert[5] is lightmap u
|
|
|
|
// vert[6] is lightmap v
|
|
|
|
|
|
|
|
if( edge < TOP_EDGE )
|
|
|
|
{
|
|
|
|
if( edge == LEFT_EDGE )
|
|
|
|
{
|
|
|
|
// left
|
|
|
|
t = ((one[3] - 0.0f) / (one[3] - two[3]));
|
|
|
|
out[3] = out[5] = 0.0f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// right
|
|
|
|
t = ((one[3] - 1.0f) / (one[3] - two[3]));
|
|
|
|
out[3] = out[5] = 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
out[4] = one[4] + (two[4] - one[4]) * t;
|
|
|
|
out[6] = one[6] + (two[6] - one[6]) * t;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( edge == TOP_EDGE )
|
|
|
|
{
|
|
|
|
// top
|
|
|
|
t = ((one[4] - 0.0f) / (one[4] - two[4]));
|
|
|
|
out[4] = out[6] = 0.0f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// bottom
|
|
|
|
t = ((one[4] - 1.0f) / (one[4] - two[4]));
|
|
|
|
out[4] = out[6] = 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
out[3] = one[3] + (two[3] - one[3]) * t;
|
|
|
|
out[5] = one[5] + (two[4] - one[5]) * t;
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorLerp( one, t, two, out );
|
|
|
|
}
|
|
|
|
|
|
|
|
static int SHClip( float *vert, int vertCount, float *out, int edge )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
int j, outCount;
|
|
|
|
float *s, *p;
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
outCount = 0;
|
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
s = &vert[(vertCount - 1) * VERTEXSIZE];
|
2010-12-23 22:00:00 +01:00
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
for( j = 0; j < vertCount; j++ )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
p = &vert[j * VERTEXSIZE];
|
2010-12-23 22:00:00 +01:00
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
if( R_ClipInside( p, edge ))
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
if( R_ClipInside( s, edge ))
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
// Add a vertex and advance out to next vertex
|
|
|
|
Q_memcpy( out, p, sizeof( float ) * VERTEXSIZE );
|
|
|
|
out += VERTEXSIZE;
|
2010-12-23 22:00:00 +01:00
|
|
|
outCount++;
|
|
|
|
}
|
2011-10-09 22:00:00 +02:00
|
|
|
else
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
R_ClipIntersect( s, p, out, edge );
|
|
|
|
out += VERTEXSIZE;
|
2010-12-23 22:00:00 +01:00
|
|
|
outCount++;
|
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
Q_memcpy( out, p, sizeof( float ) * VERTEXSIZE );
|
|
|
|
out += VERTEXSIZE;
|
2010-12-23 22:00:00 +01:00
|
|
|
outCount++;
|
|
|
|
}
|
|
|
|
}
|
2011-10-09 22:00:00 +02:00
|
|
|
else
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
if( R_ClipInside( s, edge ))
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
R_ClipIntersect( p, s, out, edge );
|
|
|
|
out += VERTEXSIZE;
|
2010-12-23 22:00:00 +01:00
|
|
|
outCount++;
|
|
|
|
}
|
|
|
|
}
|
2011-10-09 22:00:00 +02:00
|
|
|
|
2010-12-23 22:00:00 +01:00
|
|
|
s = p;
|
|
|
|
}
|
2011-10-09 22:00:00 +02:00
|
|
|
|
2010-12-23 22:00:00 +01:00
|
|
|
return outCount;
|
|
|
|
}
|
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
float *R_DoDecalSHClip( float *pInVerts, decal_t *pDecal, int nStartVerts, int *pVertCount )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
|
|
|
int outCount;
|
2011-10-09 22:00:00 +02:00
|
|
|
float *pOutVerts = g_DecalClipVerts[0];
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
// clip the polygon to the decal texture space
|
2011-10-09 22:00:00 +02:00
|
|
|
outCount = SHClip( pInVerts, nStartVerts, g_DecalClipVerts2[0], LEFT_EDGE );
|
|
|
|
outCount = SHClip( g_DecalClipVerts2[0], outCount, g_DecalClipVerts[0], RIGHT_EDGE );
|
|
|
|
outCount = SHClip( g_DecalClipVerts[0], outCount, g_DecalClipVerts2[0], TOP_EDGE );
|
|
|
|
outCount = SHClip( g_DecalClipVerts2[0], outCount, pOutVerts, BOTTOM_EDGE );
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
if( outCount )
|
|
|
|
{
|
|
|
|
if( pDecal->flags & FDECAL_CLIPTEST )
|
|
|
|
{
|
|
|
|
pDecal->flags &= ~FDECAL_CLIPTEST; // we're doing the test
|
|
|
|
|
|
|
|
// If there are exactly 4 verts and they are all 0,1 tex coords, then we've got an unclipped decal
|
|
|
|
// A more precise test would be to calculate the texture area and make sure it's one, but this
|
|
|
|
// should work as well.
|
|
|
|
if( outCount == 4 )
|
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
int j, clipped = 0;
|
|
|
|
float *v = pOutVerts;
|
2010-12-23 22:00:00 +01:00
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
for( j = 0; j < outCount && !clipped; j++, v += VERTEXSIZE )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
if(( v[3] != 0.0f && v[3] != 1.0f ) || ( v[4] != 0.0f && v[4] != 1.0f ))
|
2010-12-23 22:00:00 +01:00
|
|
|
clipped = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We didn't need to clip this decal, it's a quad covering
|
|
|
|
// the full texture space, optimize subsequent frames.
|
|
|
|
if( !clipped ) pDecal->flags |= FDECAL_NOCLIP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*pVertCount = outCount;
|
2011-10-09 22:00:00 +02:00
|
|
|
|
2010-12-23 22:00:00 +01:00
|
|
|
return pOutVerts;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Generate clipped vertex list for decal pdecal projected onto polygon psurf
|
|
|
|
//-----------------------------------------------------------------------------
|
2011-10-09 22:00:00 +02:00
|
|
|
float *R_DecalVertsClip( decal_t *pDecal, msurface_t *surf, int texture, int *pVertCount )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
|
|
|
float decalWorldScale[2];
|
|
|
|
vec3_t textureSpaceBasis[3];
|
|
|
|
|
|
|
|
// figure out where the decal maps onto the surface.
|
2011-10-09 22:00:00 +02:00
|
|
|
R_SetupDecalClip( pDecal, surf, texture, textureSpaceBasis, decalWorldScale );
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
// build the initial list of vertices from the surface verts.
|
2011-10-09 22:00:00 +02:00
|
|
|
R_SetupDecalVertsForMSurface( pDecal, surf, textureSpaceBasis, g_DecalClipVerts[0] );
|
2010-12-23 22:00:00 +01:00
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
return R_DoDecalSHClip( g_DecalClipVerts[0], pDecal, surf->polys->numverts, pVertCount );
|
2010-12-23 22:00:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate lighting coordinates at each vertex for decal vertices v[] on surface psurf
|
2011-10-09 22:00:00 +02:00
|
|
|
static void R_DecalVertsLight( float *v, msurface_t *surf, int vertCount )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
|
|
|
float s, t;
|
|
|
|
mtexinfo_t *tex;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
tex = surf->texinfo;
|
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
for( j = 0; j < vertCount; j++, v += VERTEXSIZE )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
|
|
|
// lightmap texture coordinates
|
2011-10-09 22:00:00 +02:00
|
|
|
s = DotProduct( v, tex->vecs[0] ) + tex->vecs[0][3] - surf->texturemins[0];
|
2010-12-23 22:00:00 +01:00
|
|
|
s += surf->light_s * LM_SAMPLE_SIZE;
|
|
|
|
s += LM_SAMPLE_SIZE >> 1;
|
|
|
|
s /= BLOCK_WIDTH * LM_SAMPLE_SIZE; //fa->texinfo->texture->width;
|
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
t = DotProduct( v, tex->vecs[1] ) + tex->vecs[1][3] - surf->texturemins[1];
|
2010-12-23 22:00:00 +01:00
|
|
|
t += surf->light_t * LM_SAMPLE_SIZE;
|
|
|
|
t += LM_SAMPLE_SIZE >> 1;
|
|
|
|
t /= BLOCK_HEIGHT * LM_SAMPLE_SIZE; //fa->texinfo->texture->height;
|
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
v[5] = s;
|
|
|
|
v[6] = t;
|
2010-12-23 22:00:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
static float *R_DecalVertsNoclip( decal_t *pdecal, msurface_t *surf, int texture )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
float *vlist;
|
|
|
|
int outCount;
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
// use the old code for now, and just cache them
|
2011-10-09 22:00:00 +02:00
|
|
|
vlist = R_DecalVertsClip( pdecal, surf, texture, &outCount );
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
R_DecalVertsLight( vlist, surf, 4 );
|
|
|
|
|
|
|
|
return vlist;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Check for intersecting decals on this surface
|
|
|
|
// Input : *psurf -
|
|
|
|
// *pcount -
|
|
|
|
// x -
|
|
|
|
// y -
|
|
|
|
// Output : static decal_t
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// UNDONE: This probably doesn't work quite right any more
|
|
|
|
// we should base overlap on the new decal basis matrix
|
|
|
|
// decal basis is constant per plane, perhaps we should store it (unscaled) in the shared plane struct
|
|
|
|
// BRJ: Note, decal basis is not constant when decals need to specify an s direction
|
|
|
|
// but that certainly isn't the majority case
|
|
|
|
static decal_t *R_DecalIntersect( decalinfo_t *decalinfo, msurface_t *surf, int *pcount )
|
|
|
|
{
|
|
|
|
int texture;
|
|
|
|
decal_t *plast, *pDecal;
|
|
|
|
vec3_t decalExtents[2];
|
|
|
|
float lastArea = 2;
|
|
|
|
int mapSize[2];
|
|
|
|
|
|
|
|
plast = NULL;
|
|
|
|
*pcount = 0;
|
|
|
|
|
|
|
|
// (Same as R_SetupDecalClip).
|
|
|
|
texture = decalinfo->m_iTexture;
|
|
|
|
|
|
|
|
// precalculate the extents of decalinfo's decal in world space.
|
|
|
|
R_GetDecalDimensions( texture, &mapSize[0], &mapSize[1] );
|
|
|
|
VectorScale( decalinfo->m_Basis[0], ((mapSize[0] / decalinfo->m_scale) * 0.5f), decalExtents[0] );
|
|
|
|
VectorScale( decalinfo->m_Basis[1], ((mapSize[1] / decalinfo->m_scale) * 0.5f), decalExtents[1] );
|
|
|
|
|
|
|
|
pDecal = surf->pdecals;
|
|
|
|
|
|
|
|
while( pDecal )
|
|
|
|
{
|
|
|
|
texture = pDecal->texture;
|
|
|
|
|
|
|
|
// Don't steal bigger decals and replace them with smaller decals
|
|
|
|
// Don't steal permanent decals
|
|
|
|
if(!( pDecal->flags & FDECAL_PERMANENT ))
|
|
|
|
{
|
|
|
|
vec3_t testBasis[3];
|
|
|
|
vec3_t testPosition[2];
|
|
|
|
float testWorldScale[2];
|
|
|
|
vec2_t vDecalMin, vDecalMax;
|
|
|
|
vec2_t vUnionMin, vUnionMax;
|
|
|
|
|
|
|
|
R_SetupDecalTextureSpaceBasis( pDecal, surf, texture, testBasis, testWorldScale );
|
|
|
|
|
|
|
|
VectorSubtract( decalinfo->m_Position, decalExtents[0], testPosition[0] );
|
|
|
|
VectorSubtract( decalinfo->m_Position, decalExtents[1], testPosition[1] );
|
|
|
|
|
|
|
|
// Here, we project the min and max extents of the decal that got passed in into
|
|
|
|
// this decal's (pDecal's) [0,0,1,1] clip space, just like we would if we were
|
|
|
|
// clipping a triangle into pDecal's clip space.
|
2011-03-31 22:00:00 +02:00
|
|
|
Vector2Set( vDecalMin,
|
|
|
|
DotProduct( testPosition[0], testBasis[0] ) - SHORT_TO_FLOAT( pDecal->dx ) + 0.5f,
|
|
|
|
DotProduct( testPosition[1], testBasis[1] ) - SHORT_TO_FLOAT( pDecal->dy ) + 0.5f );
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
VectorAdd( decalinfo->m_Position, decalExtents[0], testPosition[0] );
|
|
|
|
VectorAdd( decalinfo->m_Position, decalExtents[1], testPosition[1] );
|
|
|
|
|
|
|
|
Vector2Set( vDecalMax,
|
2011-03-31 22:00:00 +02:00
|
|
|
DotProduct( testPosition[0], testBasis[0] ) - SHORT_TO_FLOAT( pDecal->dx ) + 0.5f,
|
|
|
|
DotProduct( testPosition[1], testBasis[1] ) - SHORT_TO_FLOAT( pDecal->dy ) + 0.5f );
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
// Now figure out the part of the projection that intersects pDecal's
|
|
|
|
// clip box [0,0,1,1].
|
|
|
|
Vector2Set( vUnionMin, max( vDecalMin[0], 0 ), max( vDecalMin[1], 0 ));
|
|
|
|
Vector2Set( vUnionMax, min( vDecalMax[0], 1 ), min( vDecalMax[1], 1 ));
|
|
|
|
|
|
|
|
if( vUnionMin[0] < 1 && vUnionMin[1] < 1 && vUnionMax[0] > 0 && vUnionMax[1] > 0 )
|
|
|
|
{
|
|
|
|
// Figure out how much of this intersects the (0,0) - (1,1) bbox.
|
|
|
|
float flArea = (vUnionMax[0] - vUnionMin[1]) * (vUnionMax[1] - vUnionMin[1]);
|
|
|
|
|
|
|
|
if( flArea > 0.6f )
|
|
|
|
{
|
|
|
|
*pcount += 1;
|
|
|
|
|
|
|
|
if( !plast || flArea <= lastArea )
|
|
|
|
{
|
|
|
|
plast = pDecal;
|
|
|
|
lastArea = flArea;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pDecal = pDecal->pnext;
|
|
|
|
}
|
|
|
|
return plast;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the decal to the surface's list of decals.
|
|
|
|
static void R_AddDecalToSurface( decal_t *pdecal, msurface_t *surf, decalinfo_t *decalinfo )
|
|
|
|
{
|
|
|
|
decal_t *pold;
|
|
|
|
|
|
|
|
pdecal->pnext = NULL;
|
|
|
|
pold = surf->pdecals;
|
|
|
|
|
|
|
|
if( pold )
|
|
|
|
{
|
|
|
|
while( pold->pnext )
|
|
|
|
pold = pold->pnext;
|
|
|
|
pold->pnext = pdecal;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
surf->pdecals = pdecal;
|
|
|
|
}
|
|
|
|
|
|
|
|
// tag surface
|
|
|
|
pdecal->psurface = surf;
|
|
|
|
|
|
|
|
// at this point decal are linked with surface
|
|
|
|
// and will be culled, drawing and sorting
|
|
|
|
// together with surface
|
|
|
|
}
|
|
|
|
|
|
|
|
static void R_DecalCreate( decalinfo_t *decalinfo, msurface_t *surf, float x, float y )
|
|
|
|
{
|
|
|
|
decal_t *pdecal, *pold;
|
|
|
|
int count, vertCount;
|
|
|
|
|
|
|
|
if( !surf )
|
|
|
|
{
|
|
|
|
MsgDev( D_ERROR, "psurface NULL in R_DecalCreate!\n" );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pold = R_DecalIntersect( decalinfo, surf, &count );
|
|
|
|
if( count < MAX_OVERLAP_DECALS ) pold = NULL;
|
|
|
|
|
|
|
|
pdecal = R_DecalAlloc( pold );
|
2011-07-07 22:00:00 +02:00
|
|
|
if( !pdecal ) return; // r_decals == 0 ???
|
|
|
|
|
2010-12-23 22:00:00 +01:00
|
|
|
pdecal->flags = decalinfo->m_Flags;
|
|
|
|
|
|
|
|
VectorCopy( decalinfo->m_Position, pdecal->position );
|
|
|
|
|
|
|
|
if( pdecal->flags & FDECAL_USESAXIS )
|
|
|
|
VectorCopy( decalinfo->m_SAxis, pdecal->saxis );
|
|
|
|
|
2011-03-31 22:00:00 +02:00
|
|
|
pdecal->dx = FLOAT_TO_SHORT( x );
|
|
|
|
pdecal->dy = FLOAT_TO_SHORT( y );
|
2010-12-23 22:00:00 +01:00
|
|
|
pdecal->texture = decalinfo->m_iTexture;
|
|
|
|
|
|
|
|
// set scaling
|
|
|
|
pdecal->scale = decalinfo->m_scale;
|
|
|
|
pdecal->entityIndex = decalinfo->m_Entity;
|
|
|
|
|
|
|
|
// check to see if the decal actually intersects the surface
|
|
|
|
// if not, then remove the decal
|
2011-10-09 22:00:00 +02:00
|
|
|
R_DecalVertsClip( pdecal, surf, decalinfo->m_iTexture, &vertCount );
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
if( !vertCount )
|
|
|
|
{
|
|
|
|
R_DecalUnlink( pdecal );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add to the surface's list
|
|
|
|
R_AddDecalToSurface( pdecal, surf, decalinfo );
|
|
|
|
}
|
|
|
|
|
|
|
|
void R_DecalSurface( msurface_t *surf, decalinfo_t *decalinfo )
|
|
|
|
{
|
|
|
|
// get the texture associated with this surface
|
|
|
|
mtexinfo_t *tex = surf->texinfo;
|
|
|
|
vec4_t textureU, textureV;
|
|
|
|
float *sAxis = NULL;
|
|
|
|
float s, t, w, h;
|
2011-12-25 21:00:00 +01:00
|
|
|
decal_t *decal = surf->pdecals;
|
|
|
|
|
|
|
|
// we in restore mode
|
|
|
|
if( cls.state == ca_connected )
|
|
|
|
{
|
|
|
|
// NOTE: we may has the decal on this surface that come from another level.
|
|
|
|
// check duplicate with same position and texture
|
|
|
|
while( decal != NULL )
|
|
|
|
{
|
|
|
|
if( VectorCompare( decal->position, decalinfo->m_Position ) && decal->texture == decalinfo->m_iTexture )
|
|
|
|
return; // decal already exists, don't place it again
|
|
|
|
decal = decal->pnext;
|
|
|
|
}
|
|
|
|
}
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
Vector4Copy( tex->vecs[0], textureU );
|
|
|
|
Vector4Copy( tex->vecs[1], textureV );
|
|
|
|
|
|
|
|
// project decal center into the texture space of the surface
|
|
|
|
s = DotProduct( decalinfo->m_Position, textureU ) + textureU[3] - surf->texturemins[0];
|
|
|
|
t = DotProduct( decalinfo->m_Position, textureV ) + textureV[3] - surf->texturemins[1];
|
|
|
|
|
|
|
|
// Determine the decal basis (measured in world space)
|
|
|
|
// Note that the decal basis vectors 0 and 1 will always lie in the same
|
|
|
|
// plane as the texture space basis vectorstextureVecsTexelsPerWorldUnits.
|
|
|
|
|
|
|
|
if( decalinfo->m_Flags & FDECAL_USESAXIS )
|
|
|
|
sAxis = decalinfo->m_SAxis;
|
|
|
|
|
|
|
|
R_DecalComputeBasis( surf, sAxis, decalinfo->m_Basis );
|
|
|
|
|
|
|
|
// Compute an effective width and height (axis aligned) in the parent texture space
|
|
|
|
// How does this work? decalBasis[0] represents the u-direction (width)
|
|
|
|
// of the decal measured in world space, decalBasis[1] represents the
|
|
|
|
// v-direction (height) measured in world space.
|
|
|
|
// textureVecsTexelsPerWorldUnits[0] represents the u direction of
|
|
|
|
// the surface's texture space measured in world space (with the appropriate
|
|
|
|
// scale factor folded in), and textureVecsTexelsPerWorldUnits[1]
|
|
|
|
// represents the texture space v direction. We want to find the dimensions (w,h)
|
|
|
|
// of a square measured in texture space, axis aligned to that coordinate system.
|
|
|
|
// All we need to do is to find the components of the decal edge vectors
|
|
|
|
// (decalWidth * decalBasis[0], decalHeight * decalBasis[1])
|
|
|
|
// in texture coordinates:
|
|
|
|
|
|
|
|
w = fabs( decalinfo->m_decalWidth * DotProduct( textureU, decalinfo->m_Basis[0] )) +
|
|
|
|
fabs( decalinfo->m_decalHeight * DotProduct( textureU, decalinfo->m_Basis[1] ));
|
|
|
|
|
|
|
|
h = fabs( decalinfo->m_decalWidth * DotProduct( textureV, decalinfo->m_Basis[0] )) +
|
|
|
|
fabs( decalinfo->m_decalHeight * DotProduct( textureV, decalinfo->m_Basis[1] ));
|
|
|
|
|
|
|
|
// move s,t to upper left corner
|
|
|
|
s -= ( w * 0.5f );
|
|
|
|
t -= ( h * 0.5f );
|
|
|
|
|
|
|
|
// Is this rect within the surface? -- tex width & height are unsigned
|
|
|
|
if( s <= -w || t <= -h || s > (surf->extents[0] + w) || t > (surf->extents[1] + h))
|
|
|
|
{
|
|
|
|
return; // nope
|
|
|
|
}
|
|
|
|
|
|
|
|
// stamp it
|
|
|
|
R_DecalCreate( decalinfo, surf, s, t );
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// iterate over all surfaces on a node, looking for surfaces to decal
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void R_DecalNodeSurfaces( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
|
|
|
|
{
|
|
|
|
// iterate over all surfaces in the node
|
|
|
|
msurface_t *surf;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
surf = model->surfaces + node->firstsurface;
|
|
|
|
|
|
|
|
for( i = 0; i < node->numsurfaces; i++, surf++ )
|
|
|
|
{
|
|
|
|
// never apply decals on the water or sky surfaces
|
2010-12-27 22:00:00 +01:00
|
|
|
if( surf->flags & (SURF_DRAWTURB|SURF_DRAWSKY|SURF_TRANSPARENT|SURF_CONVEYOR))
|
2010-12-23 22:00:00 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
R_DecalSurface( surf, decalinfo );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Recursive routine to find surface to apply a decal to. World coordinates of
|
|
|
|
// the decal are passed in r_recalpos like the rest of the engine. This should
|
|
|
|
// be called through R_DecalShoot()
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void R_DecalNode( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
|
|
|
|
{
|
|
|
|
mplane_t *splitplane;
|
|
|
|
float dist;
|
|
|
|
|
|
|
|
ASSERT( node );
|
|
|
|
|
|
|
|
if( node->contents < 0 )
|
|
|
|
{
|
|
|
|
// hit a leaf
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
splitplane = node->plane;
|
|
|
|
dist = DotProduct( decalinfo->m_Position, splitplane->normal ) - splitplane->dist;
|
|
|
|
|
|
|
|
// This is arbitrarily set to 10 right now. In an ideal world we'd have the
|
|
|
|
// exact surface but we don't so, this tells me which planes are "sort of
|
|
|
|
// close" to the gunshot -- the gunshot is actually 4 units in front of the
|
|
|
|
// wall (see dlls\weapons.cpp). We also need to check to see if the decal
|
|
|
|
// actually intersects the texture space of the surface, as this method tags
|
|
|
|
// parallel surfaces in the same node always.
|
|
|
|
// JAY: This still tags faces that aren't correct at edges because we don't
|
|
|
|
// have a surface normal
|
|
|
|
if( dist > decalinfo->m_Size )
|
|
|
|
{
|
|
|
|
R_DecalNode( model, node->children[0], decalinfo );
|
|
|
|
}
|
|
|
|
else if( dist < -decalinfo->m_Size )
|
|
|
|
{
|
|
|
|
R_DecalNode( model, node->children[1], decalinfo );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( dist < DECAL_DISTANCE && dist > -DECAL_DISTANCE )
|
|
|
|
R_DecalNodeSurfaces( model, node, decalinfo );
|
|
|
|
|
|
|
|
R_DecalNode( model, node->children[0], decalinfo );
|
|
|
|
R_DecalNode( model, node->children[1], decalinfo );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shoots a decal onto the surface of the BSP. position is the center of the decal in world coords
|
|
|
|
void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos, int flags, vec3_t saxis )
|
|
|
|
{
|
|
|
|
decalinfo_t decalInfo;
|
|
|
|
hull_t *hull;
|
|
|
|
cl_entity_t *ent = NULL;
|
|
|
|
model_t *model = NULL;
|
|
|
|
int width, height;
|
|
|
|
|
2011-04-09 22:00:00 +02:00
|
|
|
if( textureIndex <= 0 || textureIndex >= MAX_TEXTURES )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
|
|
|
MsgDev( D_ERROR, "Decal has invalid texture!\n" );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( entityIndex > 0 )
|
|
|
|
{
|
|
|
|
ent = CL_GetEntityByIndex( entityIndex );
|
|
|
|
|
2011-02-20 22:00:00 +01:00
|
|
|
if( modelIndex > 0 ) model = Mod_Handle( modelIndex );
|
|
|
|
else if( ent != NULL ) model = Mod_Handle( ent->curstate.modelindex );
|
2011-04-09 22:00:00 +02:00
|
|
|
else return;
|
2010-12-23 22:00:00 +01:00
|
|
|
}
|
|
|
|
else if( modelIndex > 0 )
|
2011-02-20 22:00:00 +01:00
|
|
|
model = Mod_Handle( modelIndex );
|
2010-12-23 22:00:00 +01:00
|
|
|
else model = cl.worldmodel;
|
|
|
|
|
2011-04-09 22:00:00 +02:00
|
|
|
if( !model ) return;
|
|
|
|
|
|
|
|
if( model->type != mod_brush )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-12-24 21:00:00 +01:00
|
|
|
MsgDev( D_ERROR, "Decals must hit mod_brush!\n" );
|
2010-12-23 22:00:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
decalInfo.m_pModel = model;
|
|
|
|
hull = &model->hulls[0]; // always use #0 hull
|
|
|
|
|
|
|
|
if( ent )
|
|
|
|
{
|
2010-12-25 22:00:00 +01:00
|
|
|
vec3_t pos_l;
|
|
|
|
|
2010-12-23 22:00:00 +01:00
|
|
|
// transform decal position in local bmodel space
|
|
|
|
if( !VectorIsNull( ent->angles ))
|
|
|
|
{
|
2011-12-09 21:00:00 +01:00
|
|
|
matrix4x4 matrix;
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
Matrix4x4_CreateFromEntity( matrix, ent->angles, ent->origin, 1.0f );
|
2011-12-09 21:00:00 +01:00
|
|
|
Matrix4x4_VectorITransform( matrix, pos, pos_l );
|
2010-12-23 22:00:00 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VectorSubtract( pos, ent->origin, pos_l );
|
|
|
|
}
|
|
|
|
VectorCopy( pos_l, decalInfo.m_Position );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// pass position in global
|
|
|
|
VectorCopy( pos, decalInfo.m_Position );
|
|
|
|
}
|
|
|
|
|
|
|
|
// deal with the s axis if one was passed in
|
|
|
|
if( saxis )
|
|
|
|
{
|
|
|
|
flags |= FDECAL_USESAXIS;
|
|
|
|
VectorCopy( saxis, decalInfo.m_SAxis );
|
|
|
|
}
|
|
|
|
|
2011-12-25 21:00:00 +01:00
|
|
|
// this decal must use landmark for correct transition
|
|
|
|
if(!( model->flags & MODEL_HAS_ORIGIN ))
|
|
|
|
{
|
|
|
|
flags |= FDECAL_USE_LANDMARK;
|
|
|
|
}
|
|
|
|
|
2010-12-23 22:00:00 +01:00
|
|
|
// more state used by R_DecalNode()
|
|
|
|
decalInfo.m_iTexture = textureIndex;
|
|
|
|
decalInfo.m_Entity = entityIndex;
|
2011-12-25 21:00:00 +01:00
|
|
|
decalInfo.m_Flags = flags;
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
R_GetDecalDimensions( textureIndex, &width, &height );
|
|
|
|
decalInfo.m_Size = width >> 1;
|
|
|
|
if(( height >> 1 ) > decalInfo.m_Size )
|
|
|
|
decalInfo.m_Size = height >> 1;
|
|
|
|
|
|
|
|
decalInfo.m_scale = 1.0f;
|
|
|
|
|
|
|
|
// compute the decal dimensions in world space
|
|
|
|
decalInfo.m_decalWidth = width / decalInfo.m_scale;
|
|
|
|
decalInfo.m_decalHeight = height / decalInfo.m_scale;
|
|
|
|
|
|
|
|
R_DecalNode( model, &model->nodes[hull->firstclipnode], &decalInfo );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the vertex list for a decal on a surface and clip it to the surface.
|
|
|
|
// This is a template so it can work on world surfaces and dynamic displacement
|
|
|
|
// triangles the same way.
|
2011-10-09 22:00:00 +02:00
|
|
|
float *R_DecalSetupVerts( decal_t *pDecal, msurface_t *surf, int texture, int *outCount )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
float *v;
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
if( pDecal->flags & FDECAL_NOCLIP )
|
|
|
|
{
|
|
|
|
v = R_DecalVertsNoclip( pDecal, surf, texture );
|
|
|
|
*outCount = 4;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
v = R_DecalVertsClip( pDecal, surf, texture, outCount );
|
2010-12-23 22:00:00 +01:00
|
|
|
if( outCount ) R_DecalVertsLight( v, surf, *outCount );
|
|
|
|
}
|
2011-10-09 22:00:00 +02:00
|
|
|
|
2010-12-23 22:00:00 +01:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DrawSingleDecal( decal_t *pDecal, msurface_t *fa )
|
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
float *v;
|
|
|
|
gltexture_t *glt;
|
2010-12-23 22:00:00 +01:00
|
|
|
int i, numVerts;
|
|
|
|
|
|
|
|
v = R_DecalSetupVerts( pDecal, fa, pDecal->texture, &numVerts );
|
|
|
|
if( !numVerts ) return;
|
|
|
|
|
|
|
|
GL_Bind( GL_TEXTURE0, pDecal->texture );
|
2011-10-09 22:00:00 +02:00
|
|
|
glt = R_GetTexture( pDecal->texture );
|
|
|
|
|
2010-12-23 22:00:00 +01:00
|
|
|
pglBegin( GL_POLYGON );
|
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
for( i = 0; i < numVerts; i++, v += VERTEXSIZE )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
pglTexCoord2f( v[3], v[4] );
|
|
|
|
pglVertex3fv( v );
|
2010-12-23 22:00:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pglEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DrawSurfaceDecals( msurface_t *fa )
|
|
|
|
{
|
2011-02-26 22:00:00 +01:00
|
|
|
decal_t *p;
|
|
|
|
cl_entity_t *e;
|
2010-12-23 22:00:00 +01:00
|
|
|
|
2011-03-09 22:00:00 +01:00
|
|
|
if( !fa->pdecals ) return;
|
2010-12-23 22:00:00 +01:00
|
|
|
|
2011-02-26 22:00:00 +01:00
|
|
|
e = RI.currententity;
|
|
|
|
ASSERT( e != NULL );
|
|
|
|
|
2011-03-15 22:00:00 +01:00
|
|
|
if( e->curstate.rendermode == kRenderNormal || e->curstate.rendermode == kRenderTransAlpha )
|
|
|
|
{
|
|
|
|
pglDepthMask( GL_FALSE );
|
|
|
|
pglEnable( GL_BLEND );
|
2011-12-25 21:00:00 +01:00
|
|
|
|
|
|
|
if( e->curstate.rendermode == kRenderTransAlpha )
|
|
|
|
pglDisable( GL_ALPHA_TEST );
|
2011-03-15 22:00:00 +01:00
|
|
|
}
|
2011-02-26 22:00:00 +01:00
|
|
|
|
2011-12-25 21:00:00 +01:00
|
|
|
|
|
|
|
if( e->curstate.rendermode == kRenderTransTexture || e->curstate.rendermode == kRenderTransAdd )
|
|
|
|
GL_Cull( GL_NONE );
|
|
|
|
|
2011-02-25 22:00:00 +01:00
|
|
|
pglEnable( GL_POLYGON_OFFSET_FILL );
|
|
|
|
pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
for( p = fa->pdecals; p; p = p->pnext )
|
2012-01-27 21:00:00 +01:00
|
|
|
{
|
|
|
|
if( p->texture )
|
|
|
|
{
|
|
|
|
gltexture_t *glt = R_GetTexture( p->texture );
|
|
|
|
|
|
|
|
// draw transparent decals with GL_MODULATE
|
|
|
|
if( glt->fogParams[3] > DECAL_TRANSPARENT_THRESHOLD )
|
|
|
|
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
|
|
|
|
else pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
|
|
|
|
|
|
|
|
DrawSingleDecal( p, fa );
|
|
|
|
}
|
|
|
|
}
|
2010-12-23 22:00:00 +01:00
|
|
|
|
2011-03-15 22:00:00 +01:00
|
|
|
if( e->curstate.rendermode == kRenderNormal || e->curstate.rendermode == kRenderTransAlpha )
|
|
|
|
{
|
|
|
|
pglDepthMask( GL_TRUE );
|
|
|
|
pglDisable( GL_BLEND );
|
2011-12-25 21:00:00 +01:00
|
|
|
|
|
|
|
if( e->curstate.rendermode == kRenderTransAlpha )
|
|
|
|
pglEnable( GL_ALPHA_TEST );
|
2011-03-15 22:00:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pglDisable( GL_POLYGON_OFFSET_FILL );
|
|
|
|
|
2011-12-25 21:00:00 +01:00
|
|
|
if( e->curstate.rendermode == kRenderTransTexture || e->curstate.rendermode == kRenderTransAdd )
|
|
|
|
GL_Cull( GL_FRONT );
|
|
|
|
|
2011-03-15 22:00:00 +01:00
|
|
|
// restore blendfunc here
|
|
|
|
if( e->curstate.rendermode == kRenderTransAdd || e->curstate.rendermode == kRenderGlow )
|
|
|
|
pglBlendFunc( GL_SRC_ALPHA, GL_ONE );
|
2010-12-23 22:00:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============================================================
|
|
|
|
|
|
|
|
DECALS SERIALIZATION
|
|
|
|
|
|
|
|
=============================================================
|
|
|
|
*/
|
2010-12-25 22:00:00 +01:00
|
|
|
static qboolean R_DecalUnProject( decal_t *pdecal, decallist_t *entry )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
|
|
|
cl_entity_t *ent;
|
|
|
|
|
|
|
|
if( !pdecal || !( pdecal->psurface ))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ent = CL_GetEntityByIndex( pdecal->entityIndex );
|
|
|
|
|
2011-12-25 21:00:00 +01:00
|
|
|
VectorCopy( pdecal->position, entry->position );
|
2010-12-23 22:00:00 +01:00
|
|
|
entry->entityIndex = pdecal->entityIndex;
|
|
|
|
|
|
|
|
// Grab surface plane equation
|
|
|
|
if( pdecal->psurface->flags & SURF_PLANEBACK )
|
|
|
|
VectorNegate( pdecal->psurface->plane->normal, entry->impactPlaneNormal );
|
|
|
|
else VectorCopy( pdecal->psurface->plane->normal, entry->impactPlaneNormal );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose:
|
|
|
|
// Input : *pList -
|
|
|
|
// count -
|
|
|
|
// Output : static int
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static int DecalListAdd( decallist_t *pList, int count )
|
|
|
|
{
|
|
|
|
vec3_t tmp;
|
|
|
|
decallist_t *pdecal;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
pdecal = pList + count;
|
|
|
|
|
|
|
|
for( i = 0; i < count; i++ )
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
if( !Q_strcmp( pdecal->name, pList[i].name ) && pdecal->entityIndex == pList[i].entityIndex )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
|
|
|
VectorSubtract( pdecal->position, pList[i].position, tmp ); // Merge
|
2011-10-06 22:00:00 +02:00
|
|
|
|
|
|
|
if( VectorLength( tmp ) < 2 ) // UNDONE: Tune this '2' constant
|
2010-12-23 22:00:00 +01:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// this is a new decal
|
|
|
|
return count + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int DecalDepthCompare( const void *a, const void *b )
|
|
|
|
{
|
|
|
|
const decallist_t *elem1, *elem2;
|
|
|
|
|
|
|
|
elem1 = (const decallist_t *)a;
|
|
|
|
elem2 = (const decallist_t *)b;
|
|
|
|
|
|
|
|
if( elem1->depth > elem2->depth )
|
|
|
|
return 1;
|
2011-03-31 22:00:00 +02:00
|
|
|
if( elem1->depth < elem2->depth )
|
|
|
|
return -1;
|
2010-12-23 22:00:00 +01:00
|
|
|
|
2010-12-02 22:00:00 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-23 22:00:00 +01:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Called by CSaveRestore::SaveClientState
|
|
|
|
// Input : *pList -
|
|
|
|
// Output : int
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int R_CreateDecalList( decallist_t *pList, qboolean changelevel )
|
2010-12-02 22:00:00 +01:00
|
|
|
{
|
2010-12-23 22:00:00 +01:00
|
|
|
int total = 0;
|
|
|
|
int i, depth;
|
|
|
|
|
2011-12-24 21:00:00 +01:00
|
|
|
if( cl.worldmodel )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
|
|
|
for( i = 0; i < MAX_RENDER_DECALS; i++ )
|
|
|
|
{
|
|
|
|
decal_t *decal = &gDecalPool[i];
|
|
|
|
decal_t *pdecals;
|
|
|
|
|
|
|
|
// decal is in use and is not a custom decal
|
2011-01-08 22:00:00 +01:00
|
|
|
if( decal->psurface == NULL || ( decal->flags & FDECAL_DONTSAVE ))
|
2010-12-23 22:00:00 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// compute depth
|
|
|
|
depth = 0;
|
|
|
|
pdecals = decal->psurface->pdecals;
|
|
|
|
|
|
|
|
while( pdecals && pdecals != decal )
|
|
|
|
{
|
|
|
|
depth++;
|
|
|
|
pdecals = pdecals->pnext;
|
|
|
|
}
|
|
|
|
|
|
|
|
pList[total].depth = depth;
|
|
|
|
pList[total].flags = decal->flags;
|
|
|
|
|
2010-12-25 22:00:00 +01:00
|
|
|
R_DecalUnProject( decal, &pList[total] );
|
2012-01-27 21:00:00 +01:00
|
|
|
FS_FileBase( R_GetTexture( decal->texture )->name, pList[total].name );
|
2010-12-23 22:00:00 +01:00
|
|
|
|
|
|
|
// check to see if the decal should be added
|
|
|
|
total = DecalListAdd( pList, total );
|
|
|
|
}
|
2011-12-24 21:00:00 +01:00
|
|
|
|
|
|
|
if( clgame.drawFuncs.R_CreateStudioDecalList )
|
|
|
|
{
|
|
|
|
total += clgame.drawFuncs.R_CreateStudioDecalList( pList, total, changelevel );
|
|
|
|
}
|
2010-12-23 22:00:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// sort the decals lowest depth first, so they can be re-applied in order
|
|
|
|
qsort( pList, total, sizeof( decallist_t ), DecalDepthCompare );
|
|
|
|
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
R_DecalRemoveAll
|
|
|
|
|
2011-04-09 22:00:00 +02:00
|
|
|
remove all decals with specified texture
|
2010-12-23 22:00:00 +01:00
|
|
|
===============
|
|
|
|
*/
|
|
|
|
void R_DecalRemoveAll( int textureIndex )
|
|
|
|
{
|
|
|
|
decal_t *pdecal;
|
|
|
|
int i;
|
|
|
|
|
2011-08-14 22:00:00 +02:00
|
|
|
if( textureIndex < 0 || textureIndex >= MAX_TEXTURES )
|
2010-12-23 22:00:00 +01:00
|
|
|
{
|
|
|
|
MsgDev( D_ERROR, "Decal has invalid texture!\n" );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for( i = 0; i < gDecalCount; i++ )
|
|
|
|
{
|
|
|
|
pdecal = &gDecalPool[i];
|
|
|
|
|
2011-08-14 22:00:00 +02:00
|
|
|
if( !textureIndex || pdecal->texture == textureIndex )
|
2010-12-23 22:00:00 +01:00
|
|
|
R_DecalUnlink( pdecal );
|
|
|
|
}
|
2010-12-02 22:00:00 +01:00
|
|
|
}
|