mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-05-13 22:12:20 +03:00
1012 lines
25 KiB
C
1012 lines
25 KiB
C
/*
|
||
===========================================================================
|
||
|
||
Wolfenstein: Enemy Territory GPL Source Code
|
||
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
|
||
|
||
This file is part of the Wolfenstein: Enemy Territory GPL Source Code (Wolf ET Source Code).
|
||
|
||
Wolf ET 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 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
Wolf ET 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 Wolf ET Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code. If not, please request a copy in writing from id Software at the address below.
|
||
|
||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||
|
||
===========================================================================
|
||
*/
|
||
|
||
// tr_decal.c - ydnar
|
||
// handles projection of decals (nee marks) onto brush model surfaces
|
||
|
||
#include "tr_local.h"
|
||
|
||
|
||
extern int r_numDecalProjectors;
|
||
|
||
typedef struct decalVert_s
|
||
{
|
||
vec3_t xyz;
|
||
float st[2];
|
||
}
|
||
decalVert_t;
|
||
|
||
|
||
/*
|
||
MakeTextureMatrix()
|
||
generates a texture projection matrix for a triangle
|
||
returns qfalse if a texture matrix cannot be created
|
||
*/
|
||
|
||
typedef double dvec3_t[3];
|
||
|
||
static qboolean MakeTextureMatrix(vec4_t texMat[2], vec4_t projection, decalVert_t * a, decalVert_t * b, decalVert_t * c)
|
||
{
|
||
int i, j;
|
||
double bb, s, t, d;
|
||
dvec3_t pa, pb, pc;
|
||
dvec3_t bary, origin, xyz;
|
||
vec3_t vecs[3], axis[3], lengths;
|
||
|
||
|
||
/* project triangle onto plane of projection */
|
||
d = DotProduct(a->xyz, projection) - projection[3];
|
||
VectorMA(a->xyz, -d, projection, pa);
|
||
d = DotProduct(b->xyz, projection) - projection[3];
|
||
VectorMA(b->xyz, -d, projection, pb);
|
||
d = DotProduct(c->xyz, projection) - projection[3];
|
||
VectorMA(c->xyz, -d, projection, pc);
|
||
|
||
/* calculate barycentric basis for the triangle */
|
||
bb = (b->st[0] - a->st[0]) * (c->st[1] - a->st[1]) - (c->st[0] - a->st[0]) * (b->st[1] - a->st[1]);
|
||
if(fabs(bb) < 0.00000001f)
|
||
{
|
||
return qfalse;
|
||
}
|
||
|
||
/* calculate texture origin */
|
||
s = 0.0f;
|
||
t = 0.0f;
|
||
bary[0] = ((b->st[0] - s) * (c->st[1] - t) - (c->st[0] - s) * (b->st[1] - t)) / bb;
|
||
bary[1] = ((c->st[0] - s) * (a->st[1] - t) - (a->st[0] - s) * (c->st[1] - t)) / bb;
|
||
bary[2] = ((a->st[0] - s) * (b->st[1] - t) - (b->st[0] - s) * (a->st[1] - t)) / bb;
|
||
|
||
origin[0] = bary[0] * pa[0] + bary[1] * pb[0] + bary[2] * pc[0];
|
||
origin[1] = bary[0] * pa[1] + bary[1] * pb[1] + bary[2] * pc[1];
|
||
origin[2] = bary[0] * pa[2] + bary[1] * pb[2] + bary[2] * pc[2];
|
||
|
||
/* calculate s vector */
|
||
s = 1.0f;
|
||
t = 0.0f;
|
||
bary[0] = ((b->st[0] - s) * (c->st[1] - t) - (c->st[0] - s) * (b->st[1] - t)) / bb;
|
||
bary[1] = ((c->st[0] - s) * (a->st[1] - t) - (a->st[0] - s) * (c->st[1] - t)) / bb;
|
||
bary[2] = ((a->st[0] - s) * (b->st[1] - t) - (b->st[0] - s) * (a->st[1] - t)) / bb;
|
||
|
||
xyz[0] = bary[0] * pa[0] + bary[1] * pb[0] + bary[2] * pc[0];
|
||
xyz[1] = bary[0] * pa[1] + bary[1] * pb[1] + bary[2] * pc[1];
|
||
xyz[2] = bary[0] * pa[2] + bary[1] * pb[2] + bary[2] * pc[2];
|
||
|
||
VectorSubtract(xyz, origin, vecs[0]);
|
||
|
||
/* calculate t vector */
|
||
s = 0.0f;
|
||
t = 1.0f;
|
||
bary[0] = ((b->st[0] - s) * (c->st[1] - t) - (c->st[0] - s) * (b->st[1] - t)) / bb;
|
||
bary[1] = ((c->st[0] - s) * (a->st[1] - t) - (a->st[0] - s) * (c->st[1] - t)) / bb;
|
||
bary[2] = ((a->st[0] - s) * (b->st[1] - t) - (b->st[0] - s) * (a->st[1] - t)) / bb;
|
||
|
||
xyz[0] = bary[0] * pa[0] + bary[1] * pb[0] + bary[2] * pc[0];
|
||
xyz[1] = bary[0] * pa[1] + bary[1] * pb[1] + bary[2] * pc[1];
|
||
xyz[2] = bary[0] * pa[2] + bary[1] * pb[2] + bary[2] * pc[2];
|
||
|
||
VectorSubtract(xyz, origin, vecs[1]);
|
||
|
||
/* calcuate r vector */
|
||
VectorScale(projection, -1.0f, vecs[2]);
|
||
|
||
/* calculate transform axis */
|
||
for(i = 0; i < 3; i++)
|
||
lengths[i] = VectorNormalize2(vecs[i], axis[i]);
|
||
|
||
for(i = 0; i < 2; i++)
|
||
for(j = 0; j < 3; j++)
|
||
texMat[i][j] = lengths[i] > 0.0f ? (axis[i][j] / lengths[i]) : 0.0f;
|
||
texMat[0][3] = a->st[0] - DotProduct(pa, texMat[0]);
|
||
texMat[1][3] = a->st[1] - DotProduct(pa, texMat[1]);
|
||
|
||
/* disco */
|
||
return qtrue;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
RE_ProjectDecal()
|
||
creates a new decal projector from a triangle
|
||
projected polygons should be 3 or 4 points
|
||
if a single point is passed in (numPoints == 1) then the decal will be omnidirectional
|
||
omnidirectional decals use points[ 0 ] as center and projection[ 3 ] as radius
|
||
pass in lifeTime < 0 for a temporary mark
|
||
*/
|
||
|
||
void RE_ProjectDecal(qhandle_t hShader, int numPoints, vec3_t * points, vec4_t projection, vec4_t color, int lifeTime,
|
||
int fadeTime)
|
||
{
|
||
int i;
|
||
float radius, iDist;
|
||
vec3_t xyz;
|
||
vec4_t omniProjection;
|
||
decalVert_t dv[4];
|
||
decalProjector_t *dp, temp;
|
||
|
||
|
||
/* first frame rendered does not have a valid decals list */
|
||
if(tr.refdef.decalProjectors == NULL)
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* dummy check */
|
||
if(numPoints != 1 && numPoints != 3 && numPoints != 4)
|
||
{
|
||
ri.Printf(PRINT_WARNING, "WARNING: Invalid number of decal points (%d)\n", numPoints);
|
||
return;
|
||
}
|
||
|
||
/* early outs */
|
||
if(lifeTime == 0)
|
||
{
|
||
return;
|
||
}
|
||
if(projection[3] <= 0.0f)
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* set times properly */
|
||
if(lifeTime < 0 || fadeTime < 0)
|
||
{
|
||
lifeTime = 0;
|
||
fadeTime = 0;
|
||
}
|
||
|
||
/* basic setup */
|
||
temp.shader = R_GetShaderByHandle(hShader); /* debug code */
|
||
temp.numPlanes = temp.shader->entityMergable;
|
||
temp.color[0] = color[0] * 255;
|
||
temp.color[1] = color[1] * 255;
|
||
temp.color[2] = color[2] * 255;
|
||
temp.color[3] = color[3] * 255;
|
||
temp.numPlanes = numPoints + 2;
|
||
temp.fadeStartTime = tr.refdef.time + lifeTime - fadeTime;
|
||
temp.fadeEndTime = temp.fadeStartTime + fadeTime;
|
||
|
||
/* set up decal texcoords (fixme: support arbitrary projector st coordinates in trapcall) */
|
||
dv[0].st[0] = 0.0f;
|
||
dv[0].st[1] = 0.0f;
|
||
dv[1].st[0] = 0.0f;
|
||
dv[1].st[1] = 1.0f;
|
||
dv[2].st[0] = 1.0f;
|
||
dv[2].st[1] = 1.0f;
|
||
dv[3].st[0] = 1.0f;
|
||
dv[3].st[1] = 0.0f;
|
||
|
||
/* omnidirectional? */
|
||
if(numPoints == 1)
|
||
{
|
||
/* set up omnidirectional */
|
||
numPoints = 4;
|
||
temp.numPlanes = 6;
|
||
temp.omnidirectional = qtrue;
|
||
radius = projection[3];
|
||
VectorSet4(omniProjection, 0.0f, 0.0f, -1.0f, radius * 2.0f);
|
||
projection = omniProjection;
|
||
iDist = 1.0f / (radius * 2.0f);
|
||
|
||
/* set corner */
|
||
VectorSet(xyz, points[0][0] - radius, points[0][1] - radius, points[0][2] + radius);
|
||
|
||
/* make x axis texture matrix (yz) */
|
||
VectorSet(temp.texMat[0][0], 0.0f, iDist, 0.0f);
|
||
temp.texMat[0][0][3] = -DotProduct(temp.texMat[0][0], xyz);
|
||
VectorSet(temp.texMat[0][1], 0.0f, 0.0f, iDist);
|
||
temp.texMat[0][1][3] = -DotProduct(temp.texMat[0][1], xyz);
|
||
|
||
/* make y axis texture matrix (xz) */
|
||
VectorSet(temp.texMat[1][0], iDist, 0.0f, 0.0f);
|
||
temp.texMat[1][0][3] = -DotProduct(temp.texMat[1][0], xyz);
|
||
VectorSet(temp.texMat[1][1], 0.0f, 0.0f, iDist);
|
||
temp.texMat[1][1][3] = -DotProduct(temp.texMat[1][1], xyz);
|
||
|
||
/* make z axis texture matrix (xy) */
|
||
VectorSet(temp.texMat[2][0], iDist, 0.0f, 0.0f);
|
||
temp.texMat[2][0][3] = -DotProduct(temp.texMat[2][0], xyz);
|
||
VectorSet(temp.texMat[2][1], 0.0f, iDist, 0.0f);
|
||
temp.texMat[2][1][3] = -DotProduct(temp.texMat[2][1], xyz);
|
||
|
||
/* setup decal points */
|
||
VectorSet(dv[0].xyz, points[0][0] - radius, points[0][1] - radius, points[0][2] + radius);
|
||
VectorSet(dv[1].xyz, points[0][0] - radius, points[0][1] + radius, points[0][2] + radius);
|
||
VectorSet(dv[2].xyz, points[0][0] + radius, points[0][1] + radius, points[0][2] + radius);
|
||
VectorSet(dv[3].xyz, points[0][0] + radius, points[0][1] - radius, points[0][2] + radius);
|
||
}
|
||
else
|
||
{
|
||
/* set up unidirectional */
|
||
temp.omnidirectional = qfalse;
|
||
|
||
/* set up decal points */
|
||
VectorCopy(points[0], dv[0].xyz);
|
||
VectorCopy(points[1], dv[1].xyz);
|
||
VectorCopy(points[2], dv[2].xyz);
|
||
VectorCopy(points[3], dv[3].xyz);
|
||
|
||
/* make texture matrix */
|
||
if(!MakeTextureMatrix(temp.texMat[0], projection, &dv[0], &dv[1], &dv[2]))
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
/* bound the projector */
|
||
ClearBounds(temp.mins, temp.maxs);
|
||
for(i = 0; i < numPoints; i++)
|
||
{
|
||
AddPointToBounds(dv[i].xyz, temp.mins, temp.maxs);
|
||
VectorMA(dv[i].xyz, projection[3], projection, xyz);
|
||
AddPointToBounds(xyz, temp.mins, temp.maxs);
|
||
}
|
||
|
||
/* make bounding sphere */
|
||
VectorAdd(temp.mins, temp.maxs, temp.center);
|
||
VectorScale(temp.center, 0.5f, temp.center);
|
||
VectorSubtract(temp.maxs, temp.center, xyz);
|
||
temp.radius = VectorLength(xyz);
|
||
temp.radius2 = temp.radius * temp.radius;
|
||
|
||
/* frustum cull the projector (fixme: this uses a stale frustum!) */
|
||
if(R_CullPointAndRadius(temp.center, temp.radius) == CULL_OUT)
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* make the front plane */
|
||
if(!PlaneFromPoints(temp.planes[0], dv[0].xyz, dv[1].xyz, dv[2].xyz, qtrue))
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* make the back plane */
|
||
VectorSubtract(vec3_origin, temp.planes[0], temp.planes[1]);
|
||
VectorMA(dv[0].xyz, projection[3], projection, xyz);
|
||
temp.planes[1][3] = DotProduct(xyz, temp.planes[1]);
|
||
|
||
/* make the side planes */
|
||
for(i = 0; i < numPoints; i++)
|
||
{
|
||
VectorMA(dv[i].xyz, projection[3], projection, xyz);
|
||
if(!PlaneFromPoints(temp.planes[i + 2], dv[(i + 1) % numPoints].xyz, dv[i].xyz, xyz, qtrue))
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
/* create a new projector */
|
||
dp = &tr.refdef.decalProjectors[r_numDecalProjectors & DECAL_PROJECTOR_MASK];
|
||
Com_Memcpy(dp, &temp, sizeof(*dp));
|
||
|
||
/* we have a winner */
|
||
r_numDecalProjectors++;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/*
|
||
RE_ClearDecals()
|
||
clears decals from the world and entities
|
||
*/
|
||
|
||
void RE_ClearDecals(void)
|
||
{
|
||
int i, j;
|
||
|
||
/* dummy check */
|
||
if(tr.world == NULL || tr.world->numModels <= 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* clear world decals */
|
||
for(j = 0; j < MAX_WORLD_DECALS; j++)
|
||
tr.world->models[0].decals[j].shader = NULL;
|
||
|
||
/* clear entity decals */
|
||
for(i = 0; i < tr.world->numModels; i++)
|
||
for(j = 0; j < MAX_ENTITY_DECALS; j++)
|
||
tr.world->models[i].decals[j].shader = NULL;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
TransformDecalProjector()
|
||
transforms a decal projector
|
||
note: non-normalized axes will screw up the plane transform
|
||
*/
|
||
|
||
void R_TransformDecalProjector(decalProjector_t * in, vec3_t axis[3], vec3_t origin, decalProjector_t * out)
|
||
{
|
||
int i, m;
|
||
vec3_t center;
|
||
|
||
|
||
/* copy misc stuff */
|
||
out->shader = in->shader;
|
||
*((int *)out->color) = *((int *)in->color);
|
||
out->fadeStartTime = in->fadeStartTime;
|
||
out->fadeEndTime = in->fadeEndTime;
|
||
out->omnidirectional = in->omnidirectional;
|
||
out->numPlanes = in->numPlanes;
|
||
|
||
/* translate bounding box and sphere (note: rotated projector bounding box will be invalid!) */
|
||
VectorSubtract(in->mins, origin, out->mins);
|
||
VectorSubtract(in->maxs, origin, out->maxs);
|
||
VectorSubtract(in->center, origin, center);
|
||
out->center[0] = DotProduct(center, axis[0]);
|
||
out->center[1] = DotProduct(center, axis[1]);
|
||
out->center[2] = DotProduct(center, axis[2]);
|
||
out->radius = in->radius;
|
||
out->radius2 = in->radius2;
|
||
|
||
/* translate planes */
|
||
for(i = 0; i < in->numPlanes; i++)
|
||
{
|
||
/* transform by transposed inner 3x3 matrix */
|
||
out->planes[i][0] = DotProduct(in->planes[i], axis[0]);
|
||
out->planes[i][1] = DotProduct(in->planes[i], axis[1]);
|
||
out->planes[i][2] = DotProduct(in->planes[i], axis[2]);
|
||
out->planes[i][3] = in->planes[i][3] - DotProduct(in->planes[i], origin);
|
||
}
|
||
|
||
//% ri.Printf( PRINT_ALL, "plane 0: %f %f %f in dist: %f out dist: %f z: %f\n",
|
||
//% out->planes[ 0 ][ 0 ], out->planes[ 0 ][ 1 ], out->planes[ 0 ][ 2 ],
|
||
//% in->planes[ 0 ][ 3 ], out->planes[ 0 ][ 3 ], origin[ 2 ] );
|
||
|
||
/* translate texture matrices */
|
||
for(m = 0; m < 3; m++)
|
||
{
|
||
for(i = 0; i < 2; i++)
|
||
{
|
||
out->texMat[m][i][0] = DotProduct(in->texMat[m][i], axis[0]);
|
||
out->texMat[m][i][1] = DotProduct(in->texMat[m][i], axis[1]);
|
||
out->texMat[m][i][2] = DotProduct(in->texMat[m][i], axis[2]);
|
||
out->texMat[m][i][3] = in->texMat[m][i][3] + DotProduct(in->texMat[m][i], origin);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
R_TestDecalBoundingBox()
|
||
return qtrue if the decal projector intersects the bounding box
|
||
*/
|
||
|
||
qboolean R_TestDecalBoundingBox(decalProjector_t * dp, vec3_t mins, vec3_t maxs)
|
||
{
|
||
if(mins[0] >= (dp->center[0] + dp->radius) || maxs[0] <= (dp->center[0] - dp->radius) ||
|
||
mins[1] >= (dp->center[1] + dp->radius) || maxs[1] <= (dp->center[1] - dp->radius) ||
|
||
mins[2] >= (dp->center[2] + dp->radius) || maxs[2] <= (dp->center[2] - dp->radius))
|
||
{
|
||
return qfalse;
|
||
}
|
||
return qtrue;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
R_TestDecalBoundingSphere()
|
||
return qtrue if the decal projector intersects the bounding sphere
|
||
*/
|
||
|
||
qboolean R_TestDecalBoundingSphere(decalProjector_t * dp, vec3_t center, float radius2)
|
||
{
|
||
vec3_t delta;
|
||
float distance2;
|
||
|
||
|
||
VectorSubtract(center, dp->center, delta);
|
||
distance2 = DotProduct(delta, delta);
|
||
if(distance2 >= (radius2 + dp->radius2))
|
||
{
|
||
return qfalse;
|
||
}
|
||
return qtrue;
|
||
}
|
||
|
||
|
||
/*
|
||
ChopWindingBehindPlane()
|
||
clips a winding to the fragment behind the plane
|
||
*/
|
||
|
||
#define SIDE_FRONT 0
|
||
#define SIDE_BACK 1
|
||
#define SIDE_ON 2
|
||
|
||
static void ChopWindingBehindPlane(int numInPoints, vec3_t inPoints[MAX_DECAL_VERTS],
|
||
int *numOutPoints, vec3_t outPoints[MAX_DECAL_VERTS], vec4_t plane, vec_t epsilon)
|
||
{
|
||
int i, j;
|
||
float dot;
|
||
float *p1, *p2, *clip;
|
||
float d;
|
||
float dists[MAX_DECAL_VERTS + 4];
|
||
int sides[MAX_DECAL_VERTS + 4];
|
||
int counts[3];
|
||
|
||
|
||
/* set initial count */
|
||
*numOutPoints = 0;
|
||
|
||
/* don't clip if it might overflow */
|
||
if(numInPoints >= MAX_DECAL_VERTS - 1)
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* determine sides for each point */
|
||
counts[SIDE_FRONT] = 0;
|
||
counts[SIDE_BACK] = 0;
|
||
counts[SIDE_ON] = 0;
|
||
for(i = 0; i < numInPoints; i++)
|
||
{
|
||
dists[i] = DotProduct(inPoints[i], plane) - plane[3];
|
||
if(dists[i] > epsilon)
|
||
{
|
||
sides[i] = SIDE_FRONT;
|
||
}
|
||
else if(dists[i] < -epsilon)
|
||
{
|
||
sides[i] = SIDE_BACK;
|
||
}
|
||
else
|
||
{
|
||
sides[i] = SIDE_ON;
|
||
}
|
||
counts[sides[i]]++;
|
||
}
|
||
sides[i] = sides[0];
|
||
dists[i] = dists[0];
|
||
|
||
/* all points on front */
|
||
if(counts[SIDE_BACK] == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* all points on back */
|
||
if(counts[SIDE_FRONT] == 0)
|
||
{
|
||
*numOutPoints = numInPoints;
|
||
Com_Memcpy(outPoints, inPoints, numInPoints * sizeof(vec3_t));
|
||
return;
|
||
}
|
||
|
||
/* clip the winding */
|
||
for(i = 0; i < numInPoints; i++)
|
||
{
|
||
p1 = inPoints[i];
|
||
clip = outPoints[*numOutPoints];
|
||
|
||
if(sides[i] == SIDE_ON || sides[i] == SIDE_BACK)
|
||
{
|
||
VectorCopy(p1, clip);
|
||
(*numOutPoints)++;
|
||
}
|
||
|
||
if(sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])
|
||
{
|
||
continue;
|
||
}
|
||
|
||
/* generate a split point */
|
||
p2 = inPoints[(i + 1) % numInPoints];
|
||
|
||
d = dists[i] - dists[i + 1];
|
||
if(d == 0)
|
||
{
|
||
dot = 0;
|
||
}
|
||
else
|
||
{
|
||
dot = dists[i] / d;
|
||
}
|
||
|
||
/* clip xyz */
|
||
clip = outPoints[*numOutPoints];
|
||
for(j = 0; j < 3; j++)
|
||
clip[j] = p1[j] + dot * (p2[j] - p1[j]);
|
||
|
||
(*numOutPoints)++;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
ProjectDecalOntoWinding()
|
||
projects decal onto a polygon
|
||
*/
|
||
|
||
static void ProjectDecalOntoWinding(decalProjector_t * dp, int numPoints, vec3_t points[2][MAX_DECAL_VERTS], bspSurface_t * surf,
|
||
bspModel_t * bmodel)
|
||
{
|
||
int i, pingPong, count, axis;
|
||
float pd, d, d2, alpha = 1.f;
|
||
vec4_t plane;
|
||
vec3_t absNormal;
|
||
decal_t *decal, *oldest;
|
||
polyVert_t *vert;
|
||
|
||
|
||
/* make a plane from the winding */
|
||
if(!PlaneFromPoints(plane, points[0][0], points[0][1], points[0][2], qtrue))
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* omnidirectional projectors need plane type */
|
||
if(dp->omnidirectional)
|
||
{
|
||
/* compiler warnings be gone */
|
||
pd = 1.0f;
|
||
|
||
/* fade by distance from plane */
|
||
d = DotProduct(dp->center, plane) - plane[3];
|
||
alpha = 1.0f - (fabs(d) / dp->radius);
|
||
if(alpha < 0.0f)
|
||
{
|
||
return;
|
||
}
|
||
if(alpha > 1.0f)
|
||
{
|
||
alpha = 1.0f;
|
||
}
|
||
|
||
/* set projection axis */
|
||
absNormal[0] = fabs(plane[0]);
|
||
absNormal[1] = fabs(plane[1]);
|
||
absNormal[2] = fabs(plane[2]);
|
||
if(absNormal[2] >= absNormal[0] && absNormal[2] >= absNormal[1])
|
||
{
|
||
axis = 2;
|
||
}
|
||
else if(absNormal[0] >= absNormal[1] && absNormal[0] >= absNormal[2])
|
||
{
|
||
axis = 0;
|
||
}
|
||
else
|
||
{
|
||
axis = 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* backface check */
|
||
pd = DotProduct(dp->planes[0], plane);
|
||
if(pd < -0.0001f)
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* directional decals use first texture matrix */
|
||
axis = 0;
|
||
}
|
||
|
||
/* chop the winding by all the projector planes */
|
||
pingPong = 0;
|
||
for(i = 0; i < dp->numPlanes; i++) //% dp->numPlanes
|
||
{
|
||
ChopWindingBehindPlane(numPoints, points[pingPong], &numPoints, points[!pingPong], dp->planes[i], 0.0f);
|
||
pingPong ^= 1;
|
||
if(numPoints < 3)
|
||
{
|
||
return;
|
||
}
|
||
if(numPoints == MAX_DECAL_VERTS)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* find first free decal (fixme: optimize this) */
|
||
count = (bmodel == tr.world->models ? MAX_WORLD_DECALS : MAX_ENTITY_DECALS);
|
||
oldest = &bmodel->decals[0];
|
||
decal = bmodel->decals;
|
||
for(i = 0; i < count; i++, decal++)
|
||
{
|
||
/* try to find an empty decal slot */
|
||
if(decal->shader == NULL)
|
||
{
|
||
break;
|
||
}
|
||
|
||
/* find oldest decal */
|
||
if(decal->fadeEndTime < oldest->fadeEndTime)
|
||
{
|
||
oldest = decal;
|
||
}
|
||
}
|
||
|
||
/* guess we have to use the oldest decal */
|
||
if(i >= count)
|
||
{
|
||
decal = oldest;
|
||
}
|
||
|
||
/* r_speeds useful info */
|
||
tr.pc.c_decalSurfacesCreated++;
|
||
|
||
/* set it up (fixme: get the shader before this happens) */
|
||
decal->parent = surf;
|
||
decal->shader = dp->shader;
|
||
decal->fadeStartTime = dp->fadeStartTime;
|
||
decal->fadeEndTime = dp->fadeEndTime;
|
||
decal->fogIndex = surf->fogIndex;
|
||
|
||
/* add points */
|
||
decal->numVerts = numPoints;
|
||
vert = decal->verts;
|
||
for(i = 0; i < numPoints; i++, vert++)
|
||
{
|
||
/* set xyz */
|
||
VectorCopy(points[pingPong][i], vert->xyz);
|
||
|
||
/* set st */
|
||
vert->st[0] = DotProduct(vert->xyz, dp->texMat[axis][0]) + dp->texMat[axis][0][3];
|
||
vert->st[1] = DotProduct(vert->xyz, dp->texMat[axis][1]) + dp->texMat[axis][1][3];
|
||
|
||
/* unidirectional decals fade by half distance from front->back planes */
|
||
if(!dp->omnidirectional)
|
||
{
|
||
/* set alpha */
|
||
d = DotProduct(vert->xyz, dp->planes[0]) - dp->planes[0][3];
|
||
d2 = DotProduct(vert->xyz, dp->planes[1]) - dp->planes[1][3];
|
||
alpha = 2.0f * d2 / (d + d2);
|
||
if(alpha > 1.0f)
|
||
{
|
||
alpha = 1.0f;
|
||
}
|
||
else if(alpha < 0.0f)
|
||
{
|
||
alpha = 0.0f;
|
||
}
|
||
}
|
||
|
||
/* set color */
|
||
vert->modulate[0] = Q_ftol(pd * alpha * dp->color[0]);
|
||
vert->modulate[1] = Q_ftol(pd * alpha * dp->color[1]);
|
||
vert->modulate[2] = Q_ftol(pd * alpha * dp->color[2]);
|
||
vert->modulate[3] = Q_ftol(alpha * dp->color[3]);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
ProjectDecalOntoTriangles()
|
||
projects a decal onto a triangle surface (brush faces, misc_models, metasurfaces)
|
||
*/
|
||
|
||
static void ProjectDecalOntoTriangles(decalProjector_t * dp, bspSurface_t * surf, bspModel_t * bmodel)
|
||
{
|
||
int i;
|
||
srfTriangle_t *tri;
|
||
vec3_t points[2][MAX_DECAL_VERTS];
|
||
|
||
|
||
if(*surf->data == SF_FACE)
|
||
{
|
||
/* get surface */
|
||
srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surf->data;
|
||
|
||
/* walk triangle list */
|
||
for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++)
|
||
{
|
||
/* make triangle */
|
||
VectorCopy(srf->verts[tri->indexes[0]].xyz, points[0][0]);
|
||
VectorCopy(srf->verts[tri->indexes[1]].xyz, points[0][1]);
|
||
VectorCopy(srf->verts[tri->indexes[2]].xyz, points[0][2]);
|
||
|
||
/* chop it */
|
||
ProjectDecalOntoWinding(dp, 3, points, surf, bmodel);
|
||
}
|
||
}
|
||
else if(*surf->data == SF_TRIANGLES)
|
||
{
|
||
/* get surface */
|
||
srfTriangles_t *srf = (srfTriangles_t *) surf->data;
|
||
|
||
/* walk triangle list */
|
||
for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++)
|
||
{
|
||
/* make triangle */
|
||
VectorCopy(srf->verts[tri->indexes[0]].xyz, points[0][0]);
|
||
VectorCopy(srf->verts[tri->indexes[1]].xyz, points[0][1]);
|
||
VectorCopy(srf->verts[tri->indexes[2]].xyz, points[0][2]);
|
||
|
||
/* chop it */
|
||
ProjectDecalOntoWinding(dp, 3, points, surf, bmodel);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
ProjectDecalOntoGrid()
|
||
projects a decal onto a grid (patch) surface
|
||
*/
|
||
|
||
static void ProjectDecalOntoGrid(decalProjector_t * dp, bspSurface_t * surf, bspModel_t * bmodel)
|
||
{
|
||
int x, y;
|
||
srfGridMesh_t *srf;
|
||
srfVert_t *dv;
|
||
vec3_t points[2][MAX_DECAL_VERTS];
|
||
|
||
|
||
/* get surface */
|
||
srf = (srfGridMesh_t *) surf->data;
|
||
|
||
/* walk mesh rows */
|
||
for(y = 0; y < (srf->height - 1); y++)
|
||
{
|
||
/* walk mesh cols */
|
||
for(x = 0; x < (srf->width - 1); x++)
|
||
{
|
||
/* get vertex */
|
||
dv = srf->verts + y * srf->width + x;
|
||
|
||
/* first triangle */
|
||
VectorCopy(dv[0].xyz, points[0][0]);
|
||
VectorCopy(dv[srf->width].xyz, points[0][1]);
|
||
VectorCopy(dv[1].xyz, points[0][2]);
|
||
ProjectDecalOntoWinding(dp, 3, points, surf, bmodel);
|
||
|
||
/* second triangle */
|
||
VectorCopy(dv[1].xyz, points[0][0]);
|
||
VectorCopy(dv[srf->width].xyz, points[0][1]);
|
||
VectorCopy(dv[srf->width + 1].xyz, points[0][2]);
|
||
ProjectDecalOntoWinding(dp, 3, points, surf, bmodel);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
R_ProjectDecalOntoSurface()
|
||
projects a decal onto a world surface
|
||
*/
|
||
|
||
void R_ProjectDecalOntoSurface(decalProjector_t * dp, bspSurface_t * surf, bspModel_t * bmodel)
|
||
{
|
||
float d;
|
||
srfGeneric_t *gen;
|
||
|
||
/* early outs */
|
||
if(dp->shader == NULL)
|
||
{
|
||
return;
|
||
}
|
||
//% if( surf->viewCount == tr.viewCount )
|
||
//% return;
|
||
if((surf->shader->surfaceFlags & (SURF_NOIMPACT | SURF_NOMARKS)) || (surf->shader->contentFlags & CONTENTS_FOG))
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* add to counts */
|
||
tr.pc.c_decalTestSurfaces++;
|
||
|
||
/* get generic surface */
|
||
gen = (srfGeneric_t *) surf->data;
|
||
|
||
/* ignore certain surfacetypes */
|
||
if(gen->surfaceType != SF_FACE && gen->surfaceType != SF_TRIANGLES && gen->surfaceType != SF_GRID)
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* test bounding sphere */
|
||
if(!R_TestDecalBoundingSphere(dp, gen->origin, (gen->radius * gen->radius)))
|
||
// if(!R_TestDecalBoundingBox(dp, gen->bounds[0], gen->bounds[1]))
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* planar surface */
|
||
if(gen->plane.normal[0] || gen->plane.normal[1] || gen->plane.normal[2])
|
||
{
|
||
/* backface check */
|
||
d = DotProduct(dp->planes[0], gen->plane.normal);
|
||
if(d < -0.0001)
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* plane-sphere check */
|
||
d = DotProduct(dp->center, gen->plane.normal) - gen->plane.dist;
|
||
if(fabs(d) >= dp->radius)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
/* add to counts */
|
||
tr.pc.c_decalClipSurfaces++;
|
||
|
||
/* switch on type */
|
||
switch (gen->surfaceType)
|
||
{
|
||
case SF_FACE:
|
||
case SF_TRIANGLES:
|
||
ProjectDecalOntoTriangles(dp, surf, bmodel);
|
||
break;
|
||
|
||
case SF_GRID:
|
||
ProjectDecalOntoGrid(dp, surf, bmodel);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
AddDecalSurface()
|
||
adds a decal surface to the scene
|
||
*/
|
||
|
||
void R_AddDecalSurface(decal_t * decal)
|
||
{
|
||
int i;//, dlightMap;
|
||
float fade;
|
||
srfDecal_t *srf;
|
||
// srfGeneric_t *gen;
|
||
|
||
|
||
/* early outs */
|
||
if(decal->shader == NULL || decal->parent->viewCount != tr.viewCountNoReset || tr.refdef.numDecals >= MAX_DECALS)
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* get decal surface */
|
||
srf = &tr.refdef.decals[tr.refdef.numDecals];
|
||
tr.refdef.numDecals++;
|
||
|
||
/* set it up */
|
||
srf->surfaceType = SF_DECAL;
|
||
srf->numVerts = decal->numVerts;
|
||
Com_Memcpy(srf->verts, decal->verts, srf->numVerts * sizeof(*srf->verts));
|
||
|
||
/* fade colors */
|
||
if(decal->fadeStartTime < tr.refdef.time && decal->fadeStartTime < decal->fadeEndTime)
|
||
{
|
||
fade = (float)(decal->fadeEndTime - tr.refdef.time) / (float)(decal->fadeEndTime - decal->fadeStartTime);
|
||
for(i = 0; i < decal->numVerts; i++)
|
||
{
|
||
decal->verts[i].modulate[0] *= fade;
|
||
decal->verts[i].modulate[1] *= fade;
|
||
decal->verts[i].modulate[2] *= fade;
|
||
decal->verts[i].modulate[3] *= fade;
|
||
}
|
||
}
|
||
|
||
/* dynamic lights? */
|
||
/*
|
||
if(decal->parent != NULL)
|
||
{
|
||
gen = (srfGeneric_t *) decal->parent->data;
|
||
dlightMap = (gen->dlightBits[tr.smpFrame] != 0);
|
||
}
|
||
else
|
||
{
|
||
dlightMap = 0;
|
||
}
|
||
*/
|
||
|
||
/* add surface to scene */
|
||
//R_AddDrawSurf((void *)srf, decal->shader, decal->fogIndex, 0, dlightMap);
|
||
R_AddDrawSurf((void *)srf, decal->shader, -1, decal->fogIndex);
|
||
tr.pc.c_decalSurfaces++;
|
||
|
||
/* free temporary decal */
|
||
if(decal->fadeEndTime <= tr.refdef.time)
|
||
{
|
||
decal->shader = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
R_AddDecalSurfaces()
|
||
adds decal surfaces to the scene
|
||
*/
|
||
|
||
void R_AddDecalSurfaces(bspModel_t * bmodel)
|
||
{
|
||
int i, count;
|
||
decal_t *decal;
|
||
|
||
/* get decal count */
|
||
count = (bmodel == tr.world->models ? MAX_WORLD_DECALS : MAX_ENTITY_DECALS);
|
||
|
||
/* iterate through decals */
|
||
decal = bmodel->decals;
|
||
for(i = 0; i < count; i++, decal++)
|
||
R_AddDecalSurface(decal);
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
R_CullDecalProjectors()
|
||
frustum culls decal projector list
|
||
*/
|
||
|
||
void R_CullDecalProjectors(void)
|
||
{
|
||
int i, numDecalProjectors, decalBits;
|
||
decalProjector_t *dp;
|
||
|
||
|
||
/* limit */
|
||
if(tr.refdef.numDecalProjectors > MAX_DECAL_PROJECTORS)
|
||
{
|
||
tr.refdef.numDecalProjectors = MAX_DECAL_PROJECTORS;
|
||
}
|
||
|
||
/* walk decal projector list */
|
||
numDecalProjectors = 0;
|
||
decalBits = 0;
|
||
for(i = 0, dp = tr.refdef.decalProjectors; i < tr.refdef.numDecalProjectors; i++, dp++)
|
||
{
|
||
if(R_CullPointAndRadius(dp->center, dp->radius) == CULL_OUT)
|
||
{
|
||
dp->shader = NULL;
|
||
}
|
||
else
|
||
{
|
||
numDecalProjectors = i + 1;
|
||
decalBits |= (1 << i);
|
||
}
|
||
}
|
||
|
||
/* reset count */
|
||
tr.refdef.numDecalProjectors = numDecalProjectors;
|
||
tr.pc.c_decalProjectors = numDecalProjectors;
|
||
|
||
/* set bits */
|
||
tr.refdef.decalBits = decalBits;
|
||
}
|