openmohaa/code/renderer_gl3/tr_main.c
2016-03-27 11:49:47 +02:00

3495 lines
84 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2006-2011 Robert Beckebans <trebor_7@users.sourceforge.net>
This file is part of XreaL source code.
XreaL source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
XreaL 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 XreaL source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_main.c -- main control flow for each frame
#include "tr_local.h"
trGlobals_t tr;
// convert from our coordinate system (looking down X)
// to OpenGL's coordinate system (looking down -Z)
const matrix_t quakeToOpenGLMatrix = {
0, 0, -1, 0,
-1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 0, 1
};
// inverse of quakeToOpenGL matrix
const matrix_t openGLToQuakeMatrix = {
0, -1, 0, 0,
0, 0, 1, 0,
-1, 0, 0, 0,
0, 0, 0, 1
};
// convert from our right handed coordinate system (looking down X)
// to D3D's left handed coordinate system (looking down Z)
const matrix_t quakeToD3DMatrix = {
0, 0, 1, 0,
-1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 0, 1
};
const matrix_t flipZMatrix = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1
};
const GLenum geometricRenderTargets[] = {
GL_COLOR_ATTACHMENT0_EXT,
GL_COLOR_ATTACHMENT1_EXT,
GL_COLOR_ATTACHMENT2_EXT,
GL_COLOR_ATTACHMENT3_EXT
};
int shadowMapResolutions[5] = { 2048, 1024, 512, 256, 128 };
int sunShadowMapResolutions[5] = { 2048, 2048, 1024, 1024, 1024 };
refimport_t ri;
// entities that will have procedurally generated surfaces will just
// point at this for their sorting surface
surfaceType_t entitySurface = SF_ENTITY;
/*
================
R_CompareVert
================
*/
qboolean R_CompareVert(srfVert_t * v1, srfVert_t * v2, qboolean checkST)
{
int i;
for(i = 0; i < 3; i++)
{
if(floor(v1->xyz[i] + 0.1) != floor(v2->xyz[i] + 0.1))
{
return qfalse;
}
if(checkST && ((v1->st[0] != v2->st[0]) || (v1->st[1] != v2->st[1])))
{
return qfalse;
}
}
return qtrue;
}
/*
=============
R_CalcNormalForTriangle
=============
*/
void R_CalcNormalForTriangle(vec3_t normal, const vec3_t v0, const vec3_t v1, const vec3_t v2)
{
vec3_t udir, vdir;
// compute the face normal based on vertex points
VectorSubtract(v2, v0, udir);
VectorSubtract(v1, v0, vdir);
CrossProduct(udir, vdir, normal);
VectorNormalize(normal);
}
/*
=============
R_CalcTangentsForTriangle
http://members.rogers.com/deseric/tangentspace.htm
=============
*/
void R_CalcTangentsForTriangle(vec3_t tangent, vec3_t binormal,
const vec3_t v0, const vec3_t v1, const vec3_t v2,
const vec2_t t0, const vec2_t t1, const vec2_t t2)
{
int i;
vec3_t planes[3];
vec3_t u, v;
for(i = 0; i < 3; i++)
{
VectorSet(u, v1[i] - v0[i], t1[0] - t0[0], t1[1] - t0[1]);
VectorSet(v, v2[i] - v0[i], t2[0] - t0[0], t2[1] - t0[1]);
VectorNormalize(u);
VectorNormalize(v);
CrossProduct(u, v, planes[i]);
}
//So your tangent space will be defined by this :
//Normal = Normal of the triangle or Tangent X Binormal (careful with the cross product,
// you have to make sure the normal points in the right direction)
//Tangent = ( dp(Fx(s,t)) / ds, dp(Fy(s,t)) / ds, dp(Fz(s,t)) / ds ) or ( -Bx/Ax, -By/Ay, - Bz/Az )
//Binormal = ( dp(Fx(s,t)) / dt, dp(Fy(s,t)) / dt, dp(Fz(s,t)) / dt ) or ( -Cx/Ax, -Cy/Ay, -Cz/Az )
// tangent...
tangent[0] = -planes[0][1] / planes[0][0];
tangent[1] = -planes[1][1] / planes[1][0];
tangent[2] = -planes[2][1] / planes[2][0];
VectorNormalize(tangent);
// binormal...
binormal[0] = -planes[0][2] / planes[0][0];
binormal[1] = -planes[1][2] / planes[1][0];
binormal[2] = -planes[2][2] / planes[2][0];
VectorNormalize(binormal);
}
/*
=============
R_CalcTangentSpace
=============
*/
void R_CalcTangentSpace(vec3_t tangent, vec3_t binormal, vec3_t normal,
const vec3_t v0, const vec3_t v1, const vec3_t v2, const vec2_t t0, const vec2_t t1, const vec2_t t2)
{
vec3_t cp, u, v;
vec3_t faceNormal;
VectorSet(u, v1[0] - v0[0], t1[0] - t0[0], t1[1] - t0[1]);
VectorSet(v, v2[0] - v0[0], t2[0] - t0[0], t2[1] - t0[1]);
CrossProduct(u, v, cp);
if(fabs(cp[0]) > 10e-6)
{
tangent[0] = -cp[1] / cp[0];
binormal[0] = -cp[2] / cp[0];
}
u[0] = v1[1] - v0[1];
v[0] = v2[1] - v0[1];
CrossProduct(u, v, cp);
if(fabs(cp[0]) > 10e-6)
{
tangent[1] = -cp[1] / cp[0];
binormal[1] = -cp[2] / cp[0];
}
u[0] = v1[2] - v0[2];
v[0] = v2[2] - v0[2];
CrossProduct(u, v, cp);
if(fabs(cp[0]) > 10e-6)
{
tangent[2] = -cp[1] / cp[0];
binormal[2] = -cp[2] / cp[0];
}
VectorNormalize(tangent);
VectorNormalize(binormal);
// compute the face normal based on vertex points
VectorSubtract(v2, v0, u);
VectorSubtract(v1, v0, v);
CrossProduct(u, v, faceNormal);
VectorNormalize(faceNormal);
#if 1
// Gram-Schmidt orthogonalize
//tangent[a] = (t - n * Dot(n, t)).Normalize();
VectorMA(tangent, -DotProduct(faceNormal, tangent), faceNormal, tangent);
VectorNormalize(tangent);
// compute the cross product B=NxT
//CrossProduct(normal, tangent, binormal);
#else
// normal, compute the cross product N=TxB
CrossProduct(tangent, binormal, normal);
VectorNormalize(normal);
if(DotProduct(normal, faceNormal) < 0)
{
//VectorInverse(normal);
//VectorInverse(tangent);
//VectorInverse(binormal);
// compute the cross product T=BxN
CrossProduct(binormal, faceNormal, tangent);
// compute the cross product B=NxT
//CrossProduct(normal, tangent, binormal);
}
#endif
VectorCopy(faceNormal, normal);
}
void R_CalcTangentSpaceFast(vec3_t tangent, vec3_t binormal, vec3_t normal,
const vec3_t v0, const vec3_t v1, const vec3_t v2, const vec2_t t0, const vec2_t t1, const vec2_t t2)
{
vec3_t cp, u, v;
vec3_t faceNormal;
VectorSet(u, v1[0] - v0[0], t1[0] - t0[0], t1[1] - t0[1]);
VectorSet(v, v2[0] - v0[0], t2[0] - t0[0], t2[1] - t0[1]);
CrossProduct(u, v, cp);
if(fabs(cp[0]) > 10e-6)
{
tangent[0] = -cp[1] / cp[0];
binormal[0] = -cp[2] / cp[0];
}
u[0] = v1[1] - v0[1];
v[0] = v2[1] - v0[1];
CrossProduct(u, v, cp);
if(fabs(cp[0]) > 10e-6)
{
tangent[1] = -cp[1] / cp[0];
binormal[1] = -cp[2] / cp[0];
}
u[0] = v1[2] - v0[2];
v[0] = v2[2] - v0[2];
CrossProduct(u, v, cp);
if(fabs(cp[0]) > 10e-6)
{
tangent[2] = -cp[1] / cp[0];
binormal[2] = -cp[2] / cp[0];
}
VectorNormalizeFast(tangent);
VectorNormalizeFast(binormal);
// compute the face normal based on vertex points
VectorSubtract(v2, v0, u);
VectorSubtract(v1, v0, v);
CrossProduct(u, v, faceNormal);
VectorNormalizeFast(faceNormal);
#if 0
// normal, compute the cross product N=TxB
CrossProduct(tangent, binormal, normal);
VectorNormalizeFast(normal);
if(DotProduct(normal, faceNormal) < 0)
{
VectorInverse(normal);
//VectorInverse(tangent);
//VectorInverse(binormal);
CrossProduct(normal, tangent, binormal);
}
VectorCopy(faceNormal, normal);
#else
// Gram-Schmidt orthogonalize
//tangent[a] = (t - n * Dot(n, t)).Normalize();
VectorMA(tangent, -DotProduct(faceNormal, tangent), faceNormal, tangent);
VectorNormalizeFast(tangent);
#endif
VectorCopy(faceNormal, normal);
}
/*
http://www.terathon.com/code/tangent.html
*/
void R_CalcTBN(vec3_t tangent, vec3_t bitangent, vec3_t normal,
const vec3_t v1, const vec3_t v2, const vec3_t v3, const vec2_t w1, const vec2_t w2, const vec2_t w3)
{
vec3_t u, v;
float x1, x2, y1, y2, z1, z2;
float s1, s2, t1, t2;
float r, dot;
x1 = v2[0] - v1[0];
x2 = v3[0] - v1[0];
y1 = v2[1] - v1[1];
y2 = v3[1] - v1[1];
z1 = v2[2] - v1[2];
z2 = v3[2] - v1[2];
s1 = w2[0] - w1[0];
s2 = w3[0] - w1[0];
t1 = w2[1] - w1[1];
t2 = w3[1] - w1[1];
r = 1.0f / (s1 * t2 - s2 * t1);
VectorSet(tangent, (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
VectorSet(bitangent, (s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
// compute the face normal based on vertex points
VectorSubtract(v3, v1, u);
VectorSubtract(v2, v1, v);
CrossProduct(u, v, normal);
VectorNormalize(normal);
// Gram-Schmidt orthogonalize
//tangent[a] = (t - n * Dot(n, t)).Normalize();
dot = DotProduct(normal, tangent);
VectorMA(tangent, -dot, normal, tangent);
VectorNormalize(tangent);
// B=NxT
//CrossProduct(normal, tangent, bitangent);
}
void R_CalcTBN2(vec3_t tangent, vec3_t binormal, vec3_t normal,
const vec3_t v1, const vec3_t v2, const vec3_t v3, const vec2_t t1, const vec2_t t2, const vec2_t t3)
{
vec3_t v2v1;
vec3_t v3v1;
float c2c1_T;
float c2c1_B;
float c3c1_T;
float c3c1_B;
float denominator;
float scale1, scale2;
vec3_t T, B, N, C;
// Calculate the tangent basis for each vertex of the triangle
// UPDATE: In the 3rd edition of the accompanying article, the for-loop located here has
// been removed as it was redundant (the entire TBN matrix was calculated three times
// instead of just one).
//
// Please note, that this function relies on the fact that the input geometry are triangles
// and the tangent basis for each vertex thus is identical!
//
// Calculate the vectors from the current vertex to the two other vertices in the triangle
VectorSubtract(v2, v1, v2v1);
VectorSubtract(v3, v1, v3v1);
// The equation presented in the article states that:
// c2c1_T = V2.texcoord.x V1.texcoord.x
// c2c1_B = V2.texcoord.y V1.texcoord.y
// c3c1_T = V3.texcoord.x V1.texcoord.x
// c3c1_B = V3.texcoord.y V1.texcoord.y
// Calculate c2c1_T and c2c1_B
c2c1_T = t2[0] - t1[0];
c2c1_B = t2[1] - t2[1];
// Calculate c3c1_T and c3c1_B
c3c1_T = t3[0] - t1[0];
c3c1_B = t3[1] - t1[1];
denominator = c2c1_T * c3c1_B - c3c1_T * c2c1_B;
//if(ROUNDOFF(fDenominator) == 0.0f)
if(denominator == 0.0f)
{
// We won't risk a divide by zero, so set the tangent matrix to the identity matrix
VectorSet(tangent, 1, 0, 0);
VectorSet(binormal, 0, 1, 0);
VectorSet(normal, 0, 0, 1);
}
else
{
// Calculate the reciprocal value once and for all (to achieve speed)
scale1 = 1.0f / denominator;
// T and B are calculated just as the equation in the article states
VectorSet(T, (c3c1_B * v2v1[0] - c2c1_B * v3v1[0]) * scale1,
(c3c1_B * v2v1[1] - c2c1_B * v3v1[1]) * scale1,
(c3c1_B * v2v1[2] - c2c1_B * v3v1[2]) * scale1);
VectorSet(B, (-c3c1_T * v2v1[0] + c2c1_T * v3v1[0]) * scale1,
(-c3c1_T * v2v1[1] + c2c1_T * v3v1[1]) * scale1,
(-c3c1_T * v2v1[2] + c2c1_T * v3v1[2]) * scale1);
// The normal N is calculated as the cross product between T and B
CrossProduct(T, B, N);
#if 0
VectorCopy(T, tangent);
VectorCopy(B, binormal);
VectorCopy(N, normal);
#else
// Calculate the reciprocal value once and for all (to achieve speed)
scale2 = 1.0f / ((T[0] * B[1] * N[2] - T[2] * B[1] * N[0]) +
(B[0] * N[1] * T[2] - B[2] * N[1] * T[0]) +
(N[0] * T[1] * B[2] - N[2] * T[1] * B[0]));
// Calculate the inverse if the TBN matrix using the formula described in the article.
// We store the basis vectors directly in the provided TBN matrix: pvTBNMatrix
CrossProduct(B, N, C); tangent[0] = C[0] * scale2;
CrossProduct(N, T, C); tangent[1] = -C[0] * scale2;
CrossProduct(T, B, C); tangent[2] = C[0] * scale2;
VectorNormalize(tangent);
CrossProduct(B, N, C); binormal[0] = -C[1] * scale2;
CrossProduct(N, T, C); binormal[1] = C[1] * scale2;
CrossProduct(T, B, C); binormal[2] = -C[1] * scale2;
VectorNormalize(binormal);
CrossProduct(B, N, C); normal[0] = C[2] * scale2;
CrossProduct(N, T, C); normal[1] = -C[2] * scale2;
CrossProduct(T, B, C); normal[2] = C[2] * scale2;
VectorNormalize(normal);
#endif
}
}
qboolean R_CalcTangentVectors(srfVert_t * dv[3])
{
int i;
float bb, s, t;
vec3_t bary;
/* calculate barycentric basis for the triangle */
bb = (dv[1]->st[0] - dv[0]->st[0]) * (dv[2]->st[1] - dv[0]->st[1]) - (dv[2]->st[0] - dv[0]->st[0]) * (dv[1]->st[1] - dv[0]->st[1]);
if(fabs(bb) < 0.00000001f)
return qfalse;
/* do each vertex */
for(i = 0; i < 3; i++)
{
// calculate s tangent vector
s = dv[i]->st[0] + 10.0f;
t = dv[i]->st[1];
bary[0] = ((dv[1]->st[0] - s) * (dv[2]->st[1] - t) - (dv[2]->st[0] - s) * (dv[1]->st[1] - t)) / bb;
bary[1] = ((dv[2]->st[0] - s) * (dv[0]->st[1] - t) - (dv[0]->st[0] - s) * (dv[2]->st[1] - t)) / bb;
bary[2] = ((dv[0]->st[0] - s) * (dv[1]->st[1] - t) - (dv[1]->st[0] - s) * (dv[0]->st[1] - t)) / bb;
dv[i]->tangent[0] = bary[0] * dv[0]->xyz[0] + bary[1] * dv[1]->xyz[0] + bary[2] * dv[2]->xyz[0];
dv[i]->tangent[1] = bary[0] * dv[0]->xyz[1] + bary[1] * dv[1]->xyz[1] + bary[2] * dv[2]->xyz[1];
dv[i]->tangent[2] = bary[0] * dv[0]->xyz[2] + bary[1] * dv[1]->xyz[2] + bary[2] * dv[2]->xyz[2];
VectorSubtract(dv[i]->tangent, dv[i]->xyz, dv[i]->tangent);
VectorNormalize(dv[i]->tangent);
// calculate t tangent vector
s = dv[i]->st[0];
t = dv[i]->st[1] + 10.0f;
bary[0] = ((dv[1]->st[0] - s) * (dv[2]->st[1] - t) - (dv[2]->st[0] - s) * (dv[1]->st[1] - t)) / bb;
bary[1] = ((dv[2]->st[0] - s) * (dv[0]->st[1] - t) - (dv[0]->st[0] - s) * (dv[2]->st[1] - t)) / bb;
bary[2] = ((dv[0]->st[0] - s) * (dv[1]->st[1] - t) - (dv[1]->st[0] - s) * (dv[0]->st[1] - t)) / bb;
dv[i]->binormal[0] = bary[0] * dv[0]->xyz[0] + bary[1] * dv[1]->xyz[0] + bary[2] * dv[2]->xyz[0];
dv[i]->binormal[1] = bary[0] * dv[0]->xyz[1] + bary[1] * dv[1]->xyz[1] + bary[2] * dv[2]->xyz[1];
dv[i]->binormal[2] = bary[0] * dv[0]->xyz[2] + bary[1] * dv[1]->xyz[2] + bary[2] * dv[2]->xyz[2];
VectorSubtract(dv[i]->binormal, dv[i]->xyz, dv[i]->binormal);
VectorNormalize(dv[i]->binormal);
// debug code
//% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
//% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
}
return qtrue;
}
/*
=================
R_FindSurfaceTriangleWithEdge
Tr3B - recoded from Q2E
=================
*/
static int R_FindSurfaceTriangleWithEdge(int numTriangles, srfTriangle_t * triangles, int start, int end, int ignore)
{
srfTriangle_t *tri;
int count, match;
int i;
count = 0;
match = -1;
for(i = 0, tri = triangles; i < numTriangles; i++, tri++)
{
if((tri->indexes[0] == start && tri->indexes[1] == end) ||
(tri->indexes[1] == start && tri->indexes[2] == end) || (tri->indexes[2] == start && tri->indexes[0] == end))
{
if(i != ignore)
{
match = i;
}
count++;
}
else if((tri->indexes[1] == start && tri->indexes[0] == end) ||
(tri->indexes[2] == start && tri->indexes[1] == end) || (tri->indexes[0] == start && tri->indexes[2] == end))
{
count++;
}
}
// detect edges shared by three triangles and make them seams
if(count > 2)
{
match = -1;
}
return match;
}
/*
=================
R_CalcSurfaceTriangleNeighbors
Tr3B - recoded from Q2E
=================
*/
void R_CalcSurfaceTriangleNeighbors(int numTriangles, srfTriangle_t * triangles)
{
int i;
srfTriangle_t *tri;
for(i = 0, tri = triangles; i < numTriangles; i++, tri++)
{
tri->neighbors[0] = R_FindSurfaceTriangleWithEdge(numTriangles, triangles, tri->indexes[1], tri->indexes[0], i);
tri->neighbors[1] = R_FindSurfaceTriangleWithEdge(numTriangles, triangles, tri->indexes[2], tri->indexes[1], i);
tri->neighbors[2] = R_FindSurfaceTriangleWithEdge(numTriangles, triangles, tri->indexes[0], tri->indexes[2], i);
}
}
/*
=================
R_CalcSurfaceTrianglePlanes
=================
*/
void R_CalcSurfaceTrianglePlanes(int numTriangles, srfTriangle_t * triangles, srfVert_t * verts)
{
int i;
srfTriangle_t *tri;
for(i = 0, tri = triangles; i < numTriangles; i++, tri++)
{
float *v1, *v2, *v3;
vec3_t d1, d2;
v1 = verts[tri->indexes[0]].xyz;
v2 = verts[tri->indexes[1]].xyz;
v3 = verts[tri->indexes[2]].xyz;
VectorSubtract(v2, v1, d1);
VectorSubtract(v3, v1, d2);
CrossProduct(d2, d1, tri->plane);
tri->plane[3] = DotProduct(tri->plane, v1);
}
}
/*
Tr3B: this function breaks the VC9 compiler for some unknown reason ...
float R_CalcFov(float fovX, float width, float height)
{
static float x;
static float fovY;
x = width / tan(fovX / 360.0f * M_PI);
fovY = atan2(height, x);
fovY = fovY * 360.0f / M_PI;
return fovY;
}
*/
/*
=================
R_CullLocalBox
Returns CULL_IN, CULL_CLIP, or CULL_OUT
=================
*/
cullResult_t R_CullLocalBox(vec3_t localBounds[2])
{
#if 0
int i, j;
vec3_t transformed[8];
float dists[8];
vec3_t v;
cplane_t *frust;
int anyBack;
int front, back;
if(r_nocull->integer)
{
return CULL_CLIP;
}
// transform into world space
for(i = 0; i < 8; i++)
{
v[0] = localBounds[i & 1][0];
v[1] = localBounds[(i >> 1) & 1][1];
v[2] = localBounds[(i >> 2) & 1][2];
R_LocalPointToWorld(v, transformed[i]);
}
// check against frustum planes
anyBack = 0;
for(i = 0; i < FRUSTUM_PLANES; i++)
{
frust = &tr.viewParms.frustums[0][i];
front = back = 0;
for(j = 0; j < 8; j++)
{
dists[j] = DotProduct(transformed[j], frust->normal);
if(dists[j] > frust->dist)
{
front = 1;
if(back)
{
break; // a point is in front
}
}
else
{
back = 1;
}
}
if(!front)
{
// all points were behind one of the planes
return CULL_OUT;
}
anyBack |= back;
}
if(!anyBack)
{
return CULL_IN; // completely inside frustum
}
return CULL_CLIP; // partially clipped
#else
int i, j;
vec3_t transformed;
vec3_t v;
cplane_t *frust;
qboolean anyClip;
int r;
vec3_t worldBounds[2];
if(r_nocull->integer)
{
return CULL_CLIP;
}
// transform into world space
ClearBounds(worldBounds[0], worldBounds[1]);
for(j = 0; j < 8; j++)
{
v[0] = localBounds[j & 1][0];
v[1] = localBounds[(j >> 1) & 1][1];
v[2] = localBounds[(j >> 2) & 1][2];
R_LocalPointToWorld(v, transformed);
AddPointToBounds(transformed, worldBounds[0], worldBounds[1]);
}
// check against frustum planes
anyClip = qfalse;
for(i = 0; i < FRUSTUM_PLANES; i++)
{
frust = &tr.viewParms.frustums[0][i];
r = BoxOnPlaneSide(worldBounds[0], worldBounds[1], frust);
if(r == 2)
{
// completely outside frustum
return CULL_OUT;
}
if(r == 3)
{
anyClip = qtrue;
}
}
if(!anyClip)
{
// completely inside frustum
return CULL_IN;
}
// partially clipped
return CULL_CLIP;
#endif
}
/*
=================
R_CullLocalPointAndRadius
=================
*/
int R_CullLocalPointAndRadius(vec3_t pt, float radius)
{
vec3_t transformed;
R_LocalPointToWorld(pt, transformed);
return R_CullPointAndRadius(transformed, radius);
}
/*
=================
R_CullPointAndRadius
=================
*/
int R_CullPointAndRadius(vec3_t pt, float radius)
{
int i;
float dist;
cplane_t *frust;
qboolean mightBeClipped = qfalse;
if(r_nocull->integer)
{
return CULL_CLIP;
}
// check against frustum planes
for(i = 0; i < FRUSTUM_PLANES; i++)
{
frust = &tr.viewParms.frustums[0][i];
dist = DotProduct(pt, frust->normal) - frust->dist;
if(dist < -radius)
{
return CULL_OUT;
}
else if(dist <= radius)
{
mightBeClipped = qtrue;
}
}
if(mightBeClipped)
{
return CULL_CLIP;
}
return CULL_IN; // completely inside frustum
}
/*
=================
R_FogLocalPointAndRadius
=================
*/
int R_FogLocalPointAndRadius(const vec3_t pt, float radius)
{
vec3_t transformed;
R_LocalPointToWorld(pt, transformed);
return R_FogPointAndRadius(transformed, radius);
}
/*
=================
R_FogPointAndRadius
=================
*/
int R_FogPointAndRadius(const vec3_t pt, float radius)
{
int i, j;
fog_t *fog;
if(tr.refdef.rdflags & RDF_NOWORLDMODEL)
{
return 0;
}
// FIXME: non-normalized axis issues
for(i = 1; i < tr.world->numFogs; i++)
{
fog = &tr.world->fogs[i];
for(j = 0; j < 3; j++)
{
if(pt[j] - radius >= fog->bounds[1][j])
{
break;
}
if(pt[j] + radius <= fog->bounds[0][j])
{
break;
}
}
if(j == 3)
{
return i;
}
}
return 0;
}
/*
=================
R_FogWorldBox
=================
*/
int R_FogWorldBox(vec3_t bounds[2])
{
int i, j;
fog_t *fog;
if(tr.refdef.rdflags & RDF_NOWORLDMODEL)
{
return 0;
}
for(i = 1; i < tr.world->numFogs; i++)
{
fog = &tr.world->fogs[i];
for(j = 0; j < 3; j++)
{
if(bounds[0][j] >= fog->bounds[1][j])
{
break;
}
if(bounds[1][j] <= fog->bounds[0][j])
{
break;
}
}
if(j == 3)
{
return i;
}
}
return 0;
}
/*
=================
R_LocalNormalToWorld
=================
*/
void R_LocalNormalToWorld(const vec3_t local, vec3_t world)
{
MatrixTransformNormal(tr.orientation.transformMatrix, local, world);
}
/*
=================
R_LocalPointToWorld
=================
*/
void R_LocalPointToWorld(const vec3_t local, vec3_t world)
{
MatrixTransformPoint(tr.orientation.transformMatrix, local, world);
}
/*
==========================
R_TransformWorldToClip
==========================
*/
void R_TransformWorldToClip(const vec3_t src, const float *cameraViewMatrix, const float *projectionMatrix, vec4_t eye,
vec4_t dst)
{
vec4_t src2;
VectorCopy(src, src2);
src2[3] = 1;
MatrixTransform4(cameraViewMatrix, src2, eye);
MatrixTransform4(projectionMatrix, eye, dst);
}
/*
==========================
R_TransformModelToClip
==========================
*/
void R_TransformModelToClip(const vec3_t src, const float *modelViewMatrix, const float *projectionMatrix, vec4_t eye, vec4_t dst)
{
vec4_t src2;
VectorCopy(src, src2);
src2[3] = 1;
MatrixTransform4(modelViewMatrix, src2, eye);
MatrixTransform4(projectionMatrix, eye, dst);
}
/*
==========================
R_TransformClipToWindow
==========================
*/
void R_TransformClipToWindow(const vec4_t clip, const viewParms_t * view, vec4_t normalized, vec4_t window)
{
normalized[0] = clip[0] / clip[3];
normalized[1] = clip[1] / clip[3];
normalized[2] = (clip[2] + clip[3]) / (2 * clip[3]);
window[0] = view->viewportX + (0.5f * (1.0f + normalized[0]) * view->viewportWidth);
window[1] = view->viewportY + (0.5f * (1.0f + normalized[1]) * view->viewportHeight);
window[2] = normalized[2];
window[0] = (int)(window[0] + 0.5);
window[1] = (int)(window[1] + 0.5);
}
/*
================
R_ProjectRadius
================
*/
float R_ProjectRadius(float r, vec3_t location)
{
float pr;
float dist;
float c;
vec3_t p;
float projected[4];
c = DotProduct(tr.viewParms.orientation.axis[0], tr.viewParms.orientation.origin);
dist = DotProduct(tr.viewParms.orientation.axis[0], location) - c;
if(dist <= 0)
return 0;
p[0] = 0;
p[1] = fabs(r);
p[2] = -dist;
projected[0] = p[0] * tr.viewParms.projectionMatrix[0] +
p[1] * tr.viewParms.projectionMatrix[4] + p[2] * tr.viewParms.projectionMatrix[8] + tr.viewParms.projectionMatrix[12];
projected[1] = p[0] * tr.viewParms.projectionMatrix[1] +
p[1] * tr.viewParms.projectionMatrix[5] + p[2] * tr.viewParms.projectionMatrix[9] + tr.viewParms.projectionMatrix[13];
projected[2] = p[0] * tr.viewParms.projectionMatrix[2] +
p[1] * tr.viewParms.projectionMatrix[6] + p[2] * tr.viewParms.projectionMatrix[10] + tr.viewParms.projectionMatrix[14];
projected[3] = p[0] * tr.viewParms.projectionMatrix[3] +
p[1] * tr.viewParms.projectionMatrix[7] + p[2] * tr.viewParms.projectionMatrix[11] + tr.viewParms.projectionMatrix[15];
pr = projected[1] / projected[3];
if(pr > 1.0f)
pr = 1.0f;
return pr;
}
/*
=================
R_SetupEntityWorldBounds
Tr3B - needs R_RotateEntityForViewParms
=================
*/
void R_SetupEntityWorldBounds(trRefEntity_t * ent)
{
int j;
vec3_t v;
ClearBounds(ent->worldBounds[0], ent->worldBounds[1]);
for(j = 0; j < 8; j++)
{
v[0] = ent->localBounds[j & 1][0];
v[1] = ent->localBounds[(j >> 1) & 1][1];
v[2] = ent->localBounds[(j >> 2) & 1][2];
// transform local bounds vertices into world space
R_LocalPointToWorld(v, ent->worldCorners[j]);
AddPointToBounds(ent->worldCorners[j], ent->worldBounds[0], ent->worldBounds[1]);
}
}
/*
=================
R_RotateEntityForViewParms
Generates an orientation for an entity and viewParms
Does NOT produce any GL calls
Called by both the front end and the back end
=================
*/
void R_RotateEntityForViewParms(const trRefEntity_t * ent, const viewParms_t * viewParms, orientationr_t * or)
{
vec3_t delta;
float axisLength;
if(ent->e.reType != RT_MODEL)
{
*or = viewParms->world;
return;
}
VectorCopy(ent->e.origin, or->origin);
VectorCopy(ent->e.axis[0], or->axis[0]);
VectorCopy(ent->e.axis[1], or->axis[1]);
VectorCopy(ent->e.axis[2], or->axis[2]);
MatrixSetupTransformFromVectorsFLU(or->transformMatrix, or->axis[0], or->axis[1], or->axis[2], or->origin);
MatrixAffineInverse(or->transformMatrix, or->viewMatrix);
Matrix4x4Multiply(viewParms->world.viewMatrix, or->transformMatrix, or->modelViewMatrix);
// calculate the viewer origin in the model's space
// needed for fog, specular, and environment mapping
VectorSubtract(viewParms->orientation.origin, or->origin, delta);
// compensate for scale in the axes if necessary
if(ent->e.nonNormalizedAxes)
{
axisLength = VectorLength(ent->e.axis[0]);
if(!axisLength)
{
axisLength = 0;
}
else
{
axisLength = 1.0f / axisLength;
}
}
else
{
axisLength = 1.0f;
}
or->viewOrigin[0] = DotProduct(delta, or->axis[0]) * axisLength;
or->viewOrigin[1] = DotProduct(delta, or->axis[1]) * axisLength;
or->viewOrigin[2] = DotProduct(delta, or->axis[2]) * axisLength;
}
/*
=================
R_RotateEntityForLight
Generates an orientation for an entity and light
Does NOT produce any GL calls
Called by both the front end and the back end
=================
*/
void R_RotateEntityForLight(const trRefEntity_t * ent, const trRefLight_t * light, orientationr_t * or)
{
vec3_t delta;
float axisLength;
if(ent->e.reType != RT_MODEL)
{
Com_Memset(or, 0, sizeof(*or));
or->axis[0][0] = 1;
or->axis[1][1] = 1;
or->axis[2][2] = 1;
VectorCopy(light->l.origin, or->viewOrigin);
MatrixIdentity(or->transformMatrix);
//MatrixAffineInverse(or->transformMatrix, or->viewMatrix);
Matrix4x4Multiply(light->viewMatrix, or->transformMatrix, or->viewMatrix);
MatrixCopy(or->viewMatrix, or->modelViewMatrix);
return;
}
VectorCopy(ent->e.origin, or->origin);
VectorCopy(ent->e.axis[0], or->axis[0]);
VectorCopy(ent->e.axis[1], or->axis[1]);
VectorCopy(ent->e.axis[2], or->axis[2]);
MatrixSetupTransformFromVectorsFLU(or->transformMatrix, or->axis[0], or->axis[1], or->axis[2], or->origin);
MatrixAffineInverse(or->transformMatrix, or->viewMatrix);
Matrix4x4Multiply(light->viewMatrix, or->transformMatrix, or->modelViewMatrix);
// calculate the viewer origin in the model's space
// needed for fog, specular, and environment mapping
VectorSubtract(light->l.origin, or->origin, delta);
// compensate for scale in the axes if necessary
if(ent->e.nonNormalizedAxes)
{
axisLength = VectorLength(ent->e.axis[0]);
if(!axisLength)
{
axisLength = 0;
}
else
{
axisLength = 1.0f / axisLength;
}
}
else
{
axisLength = 1.0f;
}
or->viewOrigin[0] = DotProduct(delta, or->axis[0]) * axisLength;
or->viewOrigin[1] = DotProduct(delta, or->axis[1]) * axisLength;
or->viewOrigin[2] = DotProduct(delta, or->axis[2]) * axisLength;
}
/*
=================
R_RotateLightForViewParms
=================
*/
void R_RotateLightForViewParms(const trRefLight_t * light, const viewParms_t * viewParms, orientationr_t * or)
{
vec3_t delta;
VectorCopy(light->l.origin, or->origin);
QuatToAxis(light->l.rotation, or->axis);
MatrixSetupTransformFromVectorsFLU(or->transformMatrix, or->axis[0], or->axis[1], or->axis[2], or->origin);
MatrixAffineInverse(or->transformMatrix, or->viewMatrix);
Matrix4x4Multiply(viewParms->world.viewMatrix, or->transformMatrix, or->modelViewMatrix);
// calculate the viewer origin in the light's space
// needed for fog, specular, and environment mapping
VectorSubtract(viewParms->orientation.origin, or->origin, delta);
or->viewOrigin[0] = DotProduct(delta, or->axis[0]);
or->viewOrigin[1] = DotProduct(delta, or->axis[1]);
or->viewOrigin[2] = DotProduct(delta, or->axis[2]);
}
/*
=================
R_RotateForViewer
Sets up the modelview matrix for a given viewParm
=================
*/
void R_RotateForViewer(void)
{
matrix_t transformMatrix;
// axis_t viewAxis;
// vec3_t forward, right, up;
matrix_t viewMatrix;
Com_Memset(&tr.orientation, 0, sizeof(tr.orientation));
tr.orientation.axis[0][0] = 1;
tr.orientation.axis[1][1] = 1;
tr.orientation.axis[2][2] = 1;
VectorCopy(tr.viewParms.orientation.origin, tr.orientation.viewOrigin);
MatrixIdentity(tr.orientation.transformMatrix);
#if 0
ri.Printf(PRINT_ALL, "transform forward = (%5.3f, %5.3f, %5.3f), left = (%5.3f, %5.3f, %5.3f), up = (%5.3f, %5.3f, %5.3f)\n",
tr.viewParms.orientation.axis[0][0], tr.viewParms.orientation.axis[0][1], tr.viewParms.orientation.axis[0][2],
tr.viewParms.orientation.axis[1][0], tr.viewParms.orientation.axis[1][1], tr.viewParms.orientation.axis[1][2],
tr.viewParms.orientation.axis[2][0], tr.viewParms.orientation.axis[2][1], tr.viewParms.orientation.axis[2][2]);
#endif
// transform by the camera placement
MatrixSetupTransformFromVectorsFLU(transformMatrix,
tr.viewParms.orientation.axis[0], tr.viewParms.orientation.axis[1], tr.viewParms.orientation.axis[2], tr.viewParms.orientation.origin);
MatrixAffineInverse(transformMatrix, tr.orientation.viewMatrix2);
// MatrixAffineInverse(transformMatrix, tr.orientation.viewMatrix);
#if 0
// convert from our right handed coordinate system (looking down X)
// to D3D's left handed coordinate system (looking down Z)
Matrix4x4Multiply(quakeToD3DMatrix, tr.orientation.viewMatrix2, viewMatrix);
#elif 1
// convert from our right handed coordinate system (looking down X)
// to OpenGL's right handed coordinate system (looking down -Z)
Matrix4x4Multiply(quakeToOpenGLMatrix, tr.orientation.viewMatrix2, viewMatrix);
#else
// Tr3B: !!! THIS BREAKS MIRRORS !!!
// http://redmine.xreal-project.net/issues/41
// Bug #41 - Mirrors(and possibly portal cameras) are broken
// convert from our right handed coordinate system (looking down X)
// to OpenGL's right handed coordinate system (looking down -Z)
MatrixLookAtRH(viewMatrix, tr.viewParms.orientation.origin, tr.viewParms.orientation.axis[0], tr.viewParms.orientation.axis[2]);
#endif
#if 0
MatrixToVectorsFLU(viewMatrix, viewAxis[0], viewAxis[1], viewAxis[2]);
ri.Printf(PRINT_ALL, "view forward = (%5.3f, %5.3f, %5.3f), left = (%5.3f, %5.3f, %5.3f), up = (%5.3f, %5.3f, %5.3f)\n",
viewAxis[0][0], viewAxis[0][1], viewAxis[0][2],
viewAxis[1][0], viewAxis[1][1], viewAxis[1][2],
viewAxis[2][0], viewAxis[2][1], viewAxis[2][2]);
VectorSet(forward, 1, 0, 0);
VectorSet(right, 0, 1, 0);
VectorSet(up, 0, 0, 1);
MatrixTransformNormal2(viewMatrix, forward);
MatrixTransformNormal2(viewMatrix, right);
MatrixTransformNormal2(viewMatrix, up);
ri.Printf(PRINT_ALL, "transformed forward = (%5.3f, %5.3f, %5.3f), right = (%5.3f, %5.3f, %5.3f), up = (%5.3f, %5.3f, %5.3f)\n",
forward[0], forward[1], forward[2],
right[0], right[1], right[2],
up[0], up[1], up[2]);
#endif
#if 0
// Tr3B: support mirrors
if(tr.viewParms.isMirror)
{
vec4_t plane;
vec4_t plane2;
matrix_t mirrorMatrix, mirrorMatrix2;
// clipping plane in world space
plane[0] = tr.viewParms.portalPlane.normal[0];
plane[1] = tr.viewParms.portalPlane.normal[1];
plane[2] = tr.viewParms.portalPlane.normal[2];
plane[3] = tr.viewParms.portalPlane.dist;
#if 1
MatrixPlaneReflection(mirrorMatrix, plane);
//MatrixInverse(mirrorMatrix);
Matrix4x4Multiply(mirrorMatrix, viewMatrix, tr.orientation.viewMatrix);
//Matrix4x4Multiply(viewMatrix, mirrorMatrix, tr.orientation.viewMatrix);
#else
// clipping plane in view space
plane2[0] = DotProduct(tr.viewParms.orientation.axis[0], plane);
plane2[1] = DotProduct(tr.viewParms.orientation.axis[1], plane);
plane2[2] = DotProduct(tr.viewParms.orientation.axis[2], plane);
plane2[3] = (DotProduct(plane, tr.viewParms.orientation.origin) - plane[3]);
//MatrixTransformPlane(viewMatrix, plane, plane2);
//plane2[3] = -plane2[3];
MatrixPlaneReflection(mirrorMatrix, plane2);
//Matrix4x4Multiply(mirrorMatrix, quakeToOpenGLMatrix, mirrorMatrix2);
//Matrix4x4Multiply(quakeToOpenGLMatrix, mirrorMatrix, mirrorMatrix2);
Matrix4x4Multiply(mirrorMatrix, viewMatrix, tr.orientation.viewMatrix);
//Matrix4x4Multiply(viewMatrix, mirrorMatrix, tr.orientation.viewMatrix);
#endif
}
else
#endif
{
MatrixCopy(viewMatrix, tr.orientation.viewMatrix);
}
MatrixCopy(tr.orientation.viewMatrix, tr.orientation.modelViewMatrix);
tr.viewParms.world = tr.orientation;
}
/*
** SetFarClip
*/
static void SetFarClip(void)
{
float farthestCornerDistance;
int i, j;
// vec3_t v;
// vec3_t eye;
// float *modelMatrix;
// if not rendering the world (icons, menus, etc)
// set a 2k far clip plane
if(tr.refdef.rdflags & RDF_NOWORLDMODEL)
{
tr.viewParms.zFar = 2048;
return;
}
//
// set far clipping planes dynamically
//
/// Berserker: setup the more precision zFar - convert corners from world to eye coordinates, and take the farthest X local coordinate
#if 0
modelMatrix = tr.viewParms.world.viewMatrix2;
farthestCornerDistance = 0;
// check visBounds
for(i = 0; i < 8; i++)
{
if(i & 1)
{
v[0] = tr.viewParms.visBounds[0][0];
}
else
{
v[0] = tr.viewParms.visBounds[1][0];
}
if(i & 2)
{
v[1] = tr.viewParms.visBounds[0][1];
}
else
{
v[1] = tr.viewParms.visBounds[1][1];
}
if(i & 4)
{
v[2] = tr.viewParms.visBounds[0][2];
}
else
{
v[2] = tr.viewParms.visBounds[1][2];
}
for(j = 0; j < 3; j++)
{
eye[j] =
v[0] * modelMatrix[j + 0 * 4] +
v[1] * modelMatrix[j + 1 * 4] + v[2] * modelMatrix[j + 2 * 4] + modelMatrix[j + 3 * 4];
}
farthestCornerDistance = max(farthestCornerDistance, eye[0] * eye[0] + eye[1] * eye[1] + eye[2] * eye[2]);
}
#if 0
// check lightBounds
if(tr.refdef.numLights)
{
for(i = 0; i < 8; i++)
{
if(i & 1)
{
v[0] = tr.viewParms.lightBounds[0][0];
}
else
{
v[0] = tr.viewParms.lightBounds[1][0];
}
if(i & 2)
{
v[1] = tr.viewParms.lightBounds[0][1];
}
else
{
v[1] = tr.viewParms.lightBounds[1][1];
}
if(i & 4)
{
v[2] = tr.viewParms.lightBounds[0][2];
}
else
{
v[2] = tr.viewParms.lightBounds[1][2];
}
for(j = 0; j < 3; j++)
{
eye[j] =
v[0] * modelMatrix[j + 0 * 4] +
v[1] * modelMatrix[j + 1 * 4] + v[2] * modelMatrix[j + 2 * 4] + modelMatrix[j + 3 * 4];
}
farthestCornerDistance = max(farthestCornerDistance, eye[0] * eye[0] + eye[1] * eye[1] + eye[2] * eye[2]);
}
}
#endif
#else
farthestCornerDistance = 0;
// check visBounds
for(i = 0; i < 8; i++)
{
vec3_t v;
float distance;
if(i & 1)
{
v[0] = tr.viewParms.visBounds[0][0];
}
else
{
v[0] = tr.viewParms.visBounds[1][0];
}
if(i & 2)
{
v[1] = tr.viewParms.visBounds[0][1];
}
else
{
v[1] = tr.viewParms.visBounds[1][1];
}
if(i & 4)
{
v[2] = tr.viewParms.visBounds[0][2];
}
else
{
v[2] = tr.viewParms.visBounds[1][2];
}
distance = DistanceSquared(v, tr.viewParms.orientation.origin);
if(distance > farthestCornerDistance)
{
farthestCornerDistance = distance;
}
}
#if 0
// check lightBounds
for(i = 0; i < 8; i++)
{
vec3_t v;
vec3_t vecTo;
float distance;
if(i & 1)
{
v[0] = tr.viewParms.lightBounds[0][0];
}
else
{
v[0] = tr.viewParms.lightBounds[1][0];
}
if(i & 2)
{
v[1] = tr.viewParms.lightBounds[0][1];
}
else
{
v[1] = tr.viewParms.lightBounds[1][1];
}
if(i & 4)
{
v[2] = tr.viewParms.lightBounds[0][2];
}
else
{
v[2] = tr.viewParms.lightBounds[1][2];
}
VectorSubtract(v, tr.viewParms.orientation.origin, vecTo);
distance = vecTo[0] * vecTo[0] + vecTo[1] * vecTo[1] + vecTo[2] * vecTo[2];
if(distance > farthestCornerDistance)
{
farthestCornerDistance = distance;
}
}
#endif
#endif
tr.viewParms.zFar = sqrt(farthestCornerDistance);
// ydnar: add global q3 fog
#if 0
if(tr.world != NULL && tr.world->globalFog >= 0 &&
tr.world->fogs[tr.world->globalFog].fogParms.depthForOpaque < tr.viewParms.zFar)
{
tr.viewParms.zFar = tr.world->fogs[tr.world->globalFog].fogParms.depthForOpaque;
}
#endif
}
/*
===============
R_SetupProjection
===============
*/
// *INDENT-OFF*
static void R_SetupProjection(qboolean infiniteFarClip)
{
float xMin, xMax, yMin, yMax;
float width, height, depth;
float zNear, zFar;
float *proj = tr.viewParms.projectionMatrix;
if(r_zfar->value > 0)
{
// dynamically compute far clip plane distance
SetFarClip();
// sanity check
zNear = tr.viewParms.zNear = r_znear->value;
zFar = tr.viewParms.zFar = Q_max(tr.viewParms.zFar, r_zfar->value);
}
else if(infiniteFarClip)
{
zNear = tr.viewParms.zNear = r_znear->value;
zFar = tr.viewParms.zFar = 0;
}
else
{
zNear = tr.viewParms.zNear = r_znear->value;
zFar = tr.viewParms.zFar = r_zfar->value;
}
yMax = zNear * tan(tr.refdef.fov_y * M_PI / 360.0f);
yMin = -yMax;
xMax = zNear * tan(tr.refdef.fov_x * M_PI / 360.0f);
xMin = -xMax;
width = xMax - xMin;
height = yMax - yMin;
depth = zFar - zNear;
#if 0
if(zFar <= 0 || infiniteFarClip)
{
// Tr3B: far plane at infinity, see RobustShadowVolumes.pdf by Nvidia
proj[0] = 2 * zNear / width; proj[4] = 0; proj[8] = (xMax + xMin) / width; proj[12] = 0;
proj[1] = 0; proj[5] = 2 * zNear / height; proj[9] = (yMax + yMin) / height; proj[13] = 0;
proj[2] = 0; proj[6] = 0; proj[10] = -1; proj[14] = -2 * zNear;
proj[3] = 0; proj[7] = 0; proj[11] = -1; proj[15] = 0;
}
else
{
// standard OpenGL projection matrix
proj[0] = 2 * zNear / width; proj[4] = 0; proj[8] = (xMax + xMin) / width; proj[12] = 0;
proj[1] = 0; proj[5] = 2 * zNear / height; proj[9] = (yMax + yMin) / height; proj[13] = 0;
proj[2] = 0; proj[6] = 0; proj[10] = -(zFar + zNear) / depth; proj[14] = -2 * zFar * zNear / depth;
proj[3] = 0; proj[7] = 0; proj[11] = -1; proj[15] = 0;
}
#else
if(zFar <= 0 || infiniteFarClip)// || r_showBspNodes->integer)
{
MatrixPerspectiveProjectionFovXYInfiniteRH(proj, tr.refdef.fov_x, tr.refdef.fov_y, zNear);
}
else
{
MatrixPerspectiveProjectionFovXYRH(proj, tr.refdef.fov_x, tr.refdef.fov_y, zNear, zFar);
}
#endif
#if 0
int i;
matrix_t mvp;
vec4_t axis[3];
vec4_t trans;
Matrix4x4Multiply(tr.viewParms.projectionMatrix, tr.orientation.modelViewMatrix, mvp);
MatrixCopy(tr.orientation.modelViewMatrix, mvp);
VectorSet4(axis[0], 1, 0, 0, 1);
VectorSet4(axis[1], 0, 1, 0, 1);
VectorSet4(axis[2], 0, 0, 1, 1);
for(i = 0; i < 3; i++)
{
MatrixTransform4(mvp, axis[i], trans);
trans[0] /= trans[3];
trans[1] /= trans[3];
trans[2] /= trans[3];
ri.Printf(PRINT_ALL, "transformed[%i] = (%5.3f, %5.3f, %5.3f)\n", i, trans[0], trans[1], trans[2]);
}
#endif
}
// *INDENT-ON*
/*
=================
R_SetupUnprojection
create a matrix with similar functionality like gluUnproject, project from window space to world space
=================
*/
static void R_SetupUnprojection(void)
{
float *unprojectMatrix = tr.viewParms.unprojectionMatrix;
MatrixCopy(tr.viewParms.projectionMatrix, unprojectMatrix);
MatrixMultiply2(unprojectMatrix, quakeToOpenGLMatrix);
MatrixMultiply2(unprojectMatrix, tr.viewParms.world.viewMatrix2);
MatrixInverse(unprojectMatrix);
// FIXME ?
// MatrixMultiplyTranslation(unprojectMatrix, -(float)glConfig.vidWidth / (float)tr.viewParms.viewportWidth,
// -(float)glConfig.vidHeight / (float)tr.viewParms.viewportHeight, -1.0);
MatrixMultiplyTranslation(unprojectMatrix, -1.0, -1.0, -1.0);
MatrixMultiplyScale(unprojectMatrix, 2.0 * Q_rsqrt((float)glConfig.vidWidth), 2.0 * Q_rsqrt((float)glConfig.vidHeight), 2.0);
}
/*
=================
R_SetupFrustum
Setup that culling frustum planes for the current view
=================
*/
static void R_SetupFrustum(void)
{
int i;
float xs, xc;
float ang;
vec3_t planeOrigin;
ang = tr.viewParms.fovX / 180 * M_PI * 0.5f;
xs = sin(ang);
xc = cos(ang);
VectorScale(tr.viewParms.orientation.axis[0], xs, tr.viewParms.frustums[0][0].normal);
VectorMA(tr.viewParms.frustums[0][0].normal, xc, tr.viewParms.orientation.axis[1], tr.viewParms.frustums[0][0].normal);
VectorScale(tr.viewParms.orientation.axis[0], xs, tr.viewParms.frustums[0][1].normal);
VectorMA(tr.viewParms.frustums[0][1].normal, -xc, tr.viewParms.orientation.axis[1], tr.viewParms.frustums[0][1].normal);
ang = tr.viewParms.fovY / 180 * M_PI * 0.5f;
xs = sin(ang);
xc = cos(ang);
VectorScale(tr.viewParms.orientation.axis[0], xs, tr.viewParms.frustums[0][2].normal);
VectorMA(tr.viewParms.frustums[0][2].normal, xc, tr.viewParms.orientation.axis[2], tr.viewParms.frustums[0][2].normal);
VectorScale(tr.viewParms.orientation.axis[0], xs, tr.viewParms.frustums[0][3].normal);
VectorMA(tr.viewParms.frustums[0][3].normal, -xc, tr.viewParms.orientation.axis[2], tr.viewParms.frustums[0][3].normal);
for(i = 0; i < 4; i++)
{
tr.viewParms.frustums[0][i].type = PLANE_NON_AXIAL;
tr.viewParms.frustums[0][i].dist = DotProduct(tr.viewParms.orientation.origin, tr.viewParms.frustums[0][i].normal);
SetPlaneSignbits(&tr.viewParms.frustums[0][i]);
}
// Tr3B: set extra near plane which is required by the dynamic occlusion culling
tr.viewParms.frustums[0][FRUSTUM_NEAR].type = PLANE_NON_AXIAL;
VectorCopy(tr.viewParms.orientation.axis[0], tr.viewParms.frustums[0][FRUSTUM_NEAR].normal);
VectorMA(tr.viewParms.orientation.origin, r_znear->value, tr.viewParms.frustums[0][FRUSTUM_NEAR].normal, planeOrigin);
tr.viewParms.frustums[0][FRUSTUM_NEAR].dist = DotProduct(planeOrigin, tr.viewParms.frustums[0][FRUSTUM_NEAR].normal);
SetPlaneSignbits(&tr.viewParms.frustums[0][FRUSTUM_NEAR]);
}
/*
=================
R_SetupFrustum
Setup that culling frustum planes for the current view
=================
*/
// *INDENT-OFF*
void R_SetupFrustum2(frustum_t frustum, const matrix_t mvp)
{
// http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf
int i;
// left
frustum[FRUSTUM_LEFT].normal[0] = mvp[ 3] + mvp[ 0];
frustum[FRUSTUM_LEFT].normal[1] = mvp[ 7] + mvp[ 4];
frustum[FRUSTUM_LEFT].normal[2] = mvp[11] + mvp[ 8];
frustum[FRUSTUM_LEFT].dist =-(mvp[15] + mvp[12]);
// right
frustum[FRUSTUM_RIGHT].normal[0] = mvp[ 3] - mvp[ 0];
frustum[FRUSTUM_RIGHT].normal[1] = mvp[ 7] - mvp[ 4];
frustum[FRUSTUM_RIGHT].normal[2] = mvp[11] - mvp[ 8];
frustum[FRUSTUM_RIGHT].dist =-(mvp[15] - mvp[12]);
// bottom
frustum[FRUSTUM_BOTTOM].normal[0] = mvp[ 3] + mvp[ 1];
frustum[FRUSTUM_BOTTOM].normal[1] = mvp[ 7] + mvp[ 5];
frustum[FRUSTUM_BOTTOM].normal[2] = mvp[11] + mvp[ 9];
frustum[FRUSTUM_BOTTOM].dist =-(mvp[15] + mvp[13]);
// top
frustum[FRUSTUM_TOP].normal[0] = mvp[ 3] - mvp[ 1];
frustum[FRUSTUM_TOP].normal[1] = mvp[ 7] - mvp[ 5];
frustum[FRUSTUM_TOP].normal[2] = mvp[11] - mvp[ 9];
frustum[FRUSTUM_TOP].dist =-(mvp[15] - mvp[13]);
// near
frustum[FRUSTUM_NEAR].normal[0] = mvp[ 3] + mvp[ 2];
frustum[FRUSTUM_NEAR].normal[1] = mvp[ 7] + mvp[ 6];
frustum[FRUSTUM_NEAR].normal[2] = mvp[11] + mvp[10];
frustum[FRUSTUM_NEAR].dist =-(mvp[15] + mvp[14]);
// far
frustum[FRUSTUM_FAR].normal[0] = mvp[ 3] - mvp[ 2];
frustum[FRUSTUM_FAR].normal[1] = mvp[ 7] - mvp[ 6];
frustum[FRUSTUM_FAR].normal[2] = mvp[11] - mvp[10];
frustum[FRUSTUM_FAR].dist =-(mvp[15] - mvp[14]);
for(i = 0; i < 6; i++)
{
vec_t length, ilength;
frustum[i].type = PLANE_NON_AXIAL;
// normalize
length = VectorLength(frustum[i].normal);
if(length)
{
ilength = 1.0 / length;
frustum[i].normal[0] *= ilength;
frustum[i].normal[1] *= ilength;
frustum[i].normal[2] *= ilength;
frustum[i].dist *= ilength;
}
SetPlaneSignbits(&frustum[i]);
}
}
// *INDENT-ON*
static void CopyPlane(const cplane_t * in, cplane_t * out)
{
VectorCopy(in->normal, out->normal);
out->dist = in->dist;
out->type = in->type;
out->signbits = in->signbits;
out->pad[0] = in->pad[0];
out->pad[1] = in->pad[1];
}
static void R_SetupSplitFrustums(void)
{
int i, j;
float lambda;
float ratio;
vec3_t planeOrigin;
float zNear, zFar;
lambda = r_parallelShadowSplitWeight->value;
ratio = tr.viewParms.zFar / tr.viewParms.zNear;
for(j = 0; j < 5; j++)
{
CopyPlane(&tr.viewParms.frustums[0][j], &tr.viewParms.frustums[1][j]);
}
for(i = 1; i <= (r_parallelShadowSplits->integer + 1); i++)
{
float si = i / (float)(r_parallelShadowSplits->integer + 1);
zFar = 1.005f * lambda * (tr.viewParms.zNear * powf(ratio, si)) + (1 - lambda) * (tr.viewParms.zNear + (tr.viewParms.zFar - tr.viewParms.zNear) * si);
if(i <= r_parallelShadowSplits->integer)
{
tr.viewParms.parallelSplitDistances[i - 1] = zFar;
}
tr.viewParms.frustums[i][FRUSTUM_FAR].type = PLANE_NON_AXIAL;
VectorNegate(tr.viewParms.orientation.axis[0], tr.viewParms.frustums[i][FRUSTUM_FAR].normal);
VectorMA(tr.viewParms.orientation.origin, zFar, tr.viewParms.orientation.axis[0], planeOrigin);
tr.viewParms.frustums[i][FRUSTUM_FAR].dist = DotProduct(planeOrigin, tr.viewParms.frustums[i][FRUSTUM_FAR].normal);
SetPlaneSignbits(&tr.viewParms.frustums[i][FRUSTUM_FAR]);
if(i <= (r_parallelShadowSplits->integer))
{
zNear = zFar - (zFar * 0.005f);
tr.viewParms.frustums[i + 1][FRUSTUM_NEAR].type = PLANE_NON_AXIAL;
VectorCopy(tr.viewParms.orientation.axis[0], tr.viewParms.frustums[i + 1][FRUSTUM_NEAR].normal);
VectorMA(tr.viewParms.orientation.origin, zNear, tr.viewParms.orientation.axis[0], planeOrigin);
tr.viewParms.frustums[i + 1][FRUSTUM_NEAR].dist = DotProduct(planeOrigin, tr.viewParms.frustums[i + 1][FRUSTUM_NEAR].normal);
SetPlaneSignbits(&tr.viewParms.frustums[i + 1][FRUSTUM_NEAR]);
}
for(j = 0; j < 4; j++)
{
CopyPlane(&tr.viewParms.frustums[0][j], &tr.viewParms.frustums[i][j]);
}
}
}
/*
=================
R_MirrorPoint
=================
*/
void R_MirrorPoint(vec3_t in, orientation_t * surface, orientation_t * camera, vec3_t out)
{
int i;
vec3_t local;
vec3_t transformed;
float d;
VectorSubtract(in, surface->origin, local);
VectorClear(transformed);
for(i = 0; i < 3; i++)
{
d = DotProduct(local, surface->axis[i]);
VectorMA(transformed, d, camera->axis[i], transformed);
}
VectorAdd(transformed, camera->origin, out);
}
void R_MirrorVector(vec3_t in, orientation_t * surface, orientation_t * camera, vec3_t out)
{
int i;
float d;
VectorClear(out);
for(i = 0; i < 3; i++)
{
d = DotProduct(in, surface->axis[i]);
VectorMA(out, d, camera->axis[i], out);
}
}
/*
=============
R_PlaneForSurface
=============
*/
void R_PlaneForSurface(surfaceType_t * surfType, cplane_t * plane)
{
srfTriangles_t *tri;
srfPoly_t *poly;
srfVert_t *v1, *v2, *v3;
vec4_t plane4;
if(!surfType)
{
Com_Memset(plane, 0, sizeof(*plane));
plane->normal[0] = 1;
return;
}
switch (*surfType)
{
case SF_FACE:
*plane = ((srfSurfaceFace_t *) surfType)->plane;
return;
case SF_TRIANGLES:
tri = (srfTriangles_t *) surfType;
v1 = tri->verts + tri->triangles[0].indexes[0];
v2 = tri->verts + tri->triangles[0].indexes[1];
v3 = tri->verts + tri->triangles[0].indexes[2];
PlaneFromPoints(plane4, v1->xyz, v2->xyz, v3->xyz, qtrue);
VectorCopy(plane4, plane->normal);
plane->dist = plane4[3];
return;
case SF_POLY:
poly = (srfPoly_t *) surfType;
PlaneFromPoints(plane4, poly->verts[0].xyz, poly->verts[1].xyz, poly->verts[2].xyz, qtrue);
VectorCopy(plane4, plane->normal);
plane->dist = plane4[3];
return;
default:
Com_Memset(plane, 0, sizeof(*plane));
plane->normal[0] = 1;
return;
}
}
/*
=================
R_GetPortalOrientation
entityNum is the entity that the portal surface is a part of, which may
be moving and rotating.
Returns qtrue if it should be mirrored
=================
*/
static qboolean R_GetPortalOrientations(drawSurf_t * drawSurf, orientation_t * surface, orientation_t * camera, vec3_t pvsOrigin,
qboolean * mirror)
{
int i;
cplane_t originalPlane, plane;
trRefEntity_t *e;
float d;
vec3_t transformed;
// create plane axis for the portal we are seeing
R_PlaneForSurface(drawSurf->surface, &originalPlane);
//ri.Printf(PRINT_ALL, "R_GetPortalOrientations: original plane = (%5.3f, %5.3f, %5.3f)\n", originalPlane.normal[0], originalPlane.normal[1], originalPlane.normal[2]);
// rotate the plane if necessary
if(drawSurf->entity != &tr.worldEntity)
{
tr.currentEntity = drawSurf->entity;
// get the orientation of the entity
R_RotateEntityForViewParms(tr.currentEntity, &tr.viewParms, &tr.orientation);
// rotate the plane, but keep the non-rotated version for matching
// against the portalSurface entities
R_LocalNormalToWorld(originalPlane.normal, plane.normal);
plane.dist = originalPlane.dist + DotProduct(plane.normal, tr.orientation.origin);
// translate the original plane
originalPlane.dist = originalPlane.dist + DotProduct(originalPlane.normal, tr.orientation.origin);
}
else
{
plane = originalPlane;
}
VectorCopy(plane.normal, surface->axis[0]);
PerpendicularVector(surface->axis[1], surface->axis[0]);
CrossProduct(surface->axis[0], surface->axis[1], surface->axis[2]);
#if 0
// Doom3 style mirror support
shader = tr.sortedShaders[drawSurf->shaderNum];
if(shader->isMirror)
{
//ri.Printf(PRINT_ALL, "Portal surface with a mirror\n");
VectorScale(plane.normal, plane.dist, surface->origin);
VectorCopy(surface->origin, camera->origin);
VectorSubtract(vec3_origin, surface->axis[0], camera->axis[0]);
VectorCopy(surface->axis[1], camera->axis[1]);
VectorCopy(surface->axis[2], camera->axis[2]);
*mirror = qtrue;
return qtrue;
}
#endif
// locate the portal entity closest to this plane.
// origin will be the origin of the portal, origin2 will be
// the origin of the camera
for(i = 0; i < tr.refdef.numEntities; i++)
{
e = &tr.refdef.entities[i];
if(e->e.reType != RT_PORTALSURFACE)
{
continue;
}
d = DotProduct(e->e.origin, originalPlane.normal) - originalPlane.dist;
if(d > 64 || d < -64)
{
continue;
}
// get the pvsOrigin from the entity
VectorCopy(e->e.oldorigin, pvsOrigin);
// if the entity is just a mirror, don't use as a camera point
if(e->e.oldorigin[0] == e->e.origin[0] && e->e.oldorigin[1] == e->e.origin[1] && e->e.oldorigin[2] == e->e.origin[2])
{
//ri.Printf(PRINT_ALL, "Portal surface with a mirror entity\n");
VectorScale(plane.normal, plane.dist, surface->origin);
VectorCopy(surface->origin, camera->origin);
VectorSubtract(vec3_origin, surface->axis[0], camera->axis[0]);
VectorCopy(surface->axis[1], camera->axis[1]);
VectorCopy(surface->axis[2], camera->axis[2]);
*mirror = qtrue;
return qtrue;
}
// project the origin onto the surface plane to get
// an origin point we can rotate around
d = DotProduct(e->e.origin, plane.normal) - plane.dist;
VectorMA(e->e.origin, -d, surface->axis[0], surface->origin);
// now get the camera origin and orientation
VectorCopy(e->e.oldorigin, camera->origin);
AxisCopy(e->e.axis, camera->axis);
VectorSubtract(vec3_origin, camera->axis[0], camera->axis[0]);
VectorSubtract(vec3_origin, camera->axis[1], camera->axis[1]);
// optionally rotate
if(e->e.oldframe)
{
// if a speed is specified
if(e->e.frame)
{
// continuous rotate
d = (tr.refdef.time / 1000.0f) * e->e.frame;
VectorCopy(camera->axis[1], transformed);
RotatePointAroundVector(camera->axis[1], camera->axis[0], transformed, d);
CrossProduct(camera->axis[0], camera->axis[1], camera->axis[2]);
}
else
{
// bobbing rotate, with skinNum being the rotation offset
d = sin(tr.refdef.time * 0.003f);
d = e->e.skinNum + d * 4;
VectorCopy(camera->axis[1], transformed);
RotatePointAroundVector(camera->axis[1], camera->axis[0], transformed, d);
CrossProduct(camera->axis[0], camera->axis[1], camera->axis[2]);
}
}
else if(e->e.skinNum)
{
d = e->e.skinNum;
VectorCopy(camera->axis[1], transformed);
RotatePointAroundVector(camera->axis[1], camera->axis[0], transformed, d);
CrossProduct(camera->axis[0], camera->axis[1], camera->axis[2]);
}
//ri.Printf(PRINT_ALL, "Portal surface with a portal entity\n");
*mirror = qfalse;
return qtrue;
}
// if we didn't locate a portal entity, don't render anything.
// We don't want to just treat it as a mirror, because without a
// portal entity the server won't have communicated a proper entity set
// in the snapshot
// unfortunately, with local movement prediction it is easily possible
// to see a surface before the server has communicated the matching
// portal surface entity, so we don't want to print anything here...
//ri.Printf(PRINT_ALL, "Portal surface without a portal entity\n");
return qfalse;
}
static qboolean IsMirror(const drawSurf_t * drawSurf)
{
int i;
cplane_t originalPlane, plane;
trRefEntity_t *e;
float d;
// create plane axis for the portal we are seeing
R_PlaneForSurface(drawSurf->surface, &originalPlane);
// rotate the plane if necessary
if(tr.currentEntity != &tr.worldEntity)
{
// get the orientation of the entity
R_RotateEntityForViewParms(tr.currentEntity, &tr.viewParms, &tr.orientation);
// rotate the plane, but keep the non-rotated version for matching
// against the portalSurface entities
R_LocalNormalToWorld(originalPlane.normal, plane.normal);
plane.dist = originalPlane.dist + DotProduct(plane.normal, tr.orientation.origin);
// translate the original plane
originalPlane.dist = originalPlane.dist + DotProduct(originalPlane.normal, tr.orientation.origin);
}
else
{
plane = originalPlane;
}
// locate the portal entity closest to this plane.
// origin will be the origin of the portal, origin2 will be
// the origin of the camera
for(i = 0; i < tr.refdef.numEntities; i++)
{
e = &tr.refdef.entities[i];
if(e->e.reType != RT_PORTALSURFACE)
{
continue;
}
d = DotProduct(e->e.origin, originalPlane.normal) - originalPlane.dist;
if(d > 64 || d < -64)
{
continue;
}
// if the entity is just a mirror, don't use as a camera point
if(e->e.oldorigin[0] == e->e.origin[0] && e->e.oldorigin[1] == e->e.origin[1] && e->e.oldorigin[2] == e->e.origin[2])
{
return qtrue;
}
return qfalse;
}
return qfalse;
}
/*
** SurfIsOffscreen
**
** Determines if a surface is completely offscreen.
*/
static qboolean SurfIsOffscreen(const drawSurf_t * drawSurf, vec4_t clipDest[128])
{
float shortest = 100000000;
shader_t *shader;
int numTriangles;
vec4_t clip, eye;
int i;
unsigned int pointOr = 0;
unsigned int pointAnd = (unsigned int)~0;
if(glConfig.smpActive)
{
// FIXME! we can't do Tess_Begin/Tess_End stuff with smp!
return qfalse;
}
tr.currentEntity = drawSurf->entity;
shader = tr.sortedShaders[drawSurf->shaderNum];
// rotate if necessary
if(tr.currentEntity != &tr.worldEntity)
{
//ri.Printf(PRINT_ALL, "entity portal surface\n");
R_RotateEntityForViewParms(tr.currentEntity, &tr.viewParms, &tr.orientation);
}
else
{
//ri.Printf(PRINT_ALL, "world portal surface\n");
tr.orientation = tr.viewParms.world;
}
Tess_Begin(Tess_StageIteratorGeneric, NULL, shader, NULL, qtrue, qtrue, -1, 0);
rb_surfaceTable[*drawSurf->surface] (drawSurf->surface);
// Tr3B: former assertion
if(tess.numVertexes >= 128)
{
return qfalse;
}
for(i = 0; i < tess.numVertexes; i++)
{
int j;
unsigned int pointFlags = 0;
R_TransformModelToClip(tess.xyz[i], tr.orientation.modelViewMatrix, tr.viewParms.projectionMatrix, eye, clip);
for(j = 0; j < 3; j++)
{
if(clip[j] >= clip[3])
{
pointFlags |= (1 << (j * 2));
}
else if(clip[j] <= -clip[3])
{
pointFlags |= (1 << (j * 2 + 1));
}
}
pointAnd &= pointFlags;
pointOr |= pointFlags;
}
// trivially reject
if(pointAnd)
{
return qtrue;
}
// determine if this surface is backfaced and also determine the distance
// to the nearest vertex so we can cull based on portal range. Culling
// based on vertex distance isn't 100% correct (we should be checking for
// range to the surface), but it's good enough for the types of portals
// we have in the game right now.
numTriangles = tess.numIndexes / 3;
for(i = 0; i < tess.numIndexes; i += 3)
{
vec3_t normal;
float dot;
float len;
VectorSubtract(tess.xyz[tess.indexes[i]], tr.orientation.viewOrigin, normal);
len = VectorLengthSquared(normal); // lose the sqrt
if(len < shortest)
{
shortest = len;
}
if((dot = DotProduct(normal, tess.normals[tess.indexes[i]])) >= 0)
{
numTriangles--;
}
}
if(!numTriangles)
{
//ri.Printf(PRINT_ALL, "entity portal surface triangles culled\n");
return qtrue;
}
// mirrors can early out at this point, since we don't do a fade over distance
// with them (although we could)
if(IsMirror(drawSurf))
{
return qfalse;
}
if(shortest > (tess.surfaceShader->portalRange * tess.surfaceShader->portalRange))
{
return qtrue;
}
return qfalse;
}
/*
========================
R_MirrorViewBySurface
Returns qtrue if another view has been rendered
========================
*/
static qboolean R_MirrorViewBySurface(drawSurf_t * drawSurf)
{
vec4_t clipDest[128];
viewParms_t newParms;
viewParms_t oldParms;
orientation_t surface, camera;
// don't recursively mirror
if(tr.viewParms.isPortal)
{
ri.Printf(PRINT_DEVELOPER, "WARNING: recursive mirror/portal found\n");
return qfalse;
}
if(r_noportals->integer)
{
return qfalse;
}
// trivially reject portal/mirror
if(SurfIsOffscreen(drawSurf, clipDest))
{
//ri.Printf(PRINT_ALL, "WARNING: offscreen mirror/portal surface\n");
return qfalse;
}
// save old viewParms so we can return to it after the mirror view
oldParms = tr.viewParms;
newParms = tr.viewParms;
newParms.isPortal = qtrue;
if(!R_GetPortalOrientations(drawSurf, &surface, &camera, newParms.pvsOrigin, &newParms.isMirror))
{
return qfalse; // bad portal, no portalentity
}
R_MirrorPoint(oldParms.orientation.origin, &surface, &camera, newParms.orientation.origin);
#if 1
VectorSubtract(vec3_origin, camera.axis[0], newParms.portalPlane.normal);
#else
VectorCopy(camera.axis[0], newParms.portalPlane.normal);
#endif
newParms.portalPlane.dist = DotProduct(camera.origin, newParms.portalPlane.normal);
R_MirrorVector(oldParms.orientation.axis[0], &surface, &camera, newParms.orientation.axis[0]);
R_MirrorVector(oldParms.orientation.axis[1], &surface, &camera, newParms.orientation.axis[1]);
R_MirrorVector(oldParms.orientation.axis[2], &surface, &camera, newParms.orientation.axis[2]);
// OPTIMIZE: restrict the viewport on the mirrored view
// render the mirror view
R_RenderView(&newParms);
tr.viewParms = oldParms;
return qtrue;
}
/*
=================
R_SpriteFogNum
See if a sprite is inside a fog volume
=================
*/
int R_SpriteFogNum(trRefEntity_t * ent)
{
int i, j;
fog_t *fog;
if(tr.refdef.rdflags & RDF_NOWORLDMODEL)
{
return 0;
}
for(i = 1; i < tr.world->numFogs; i++)
{
fog = &tr.world->fogs[i];
for(j = 0; j < 3; j++)
{
if(ent->e.origin[j] - ent->e.radius >= fog->bounds[1][j])
{
break;
}
if(ent->e.origin[j] + ent->e.radius <= fog->bounds[0][j])
{
break;
}
}
if(j == 3)
{
return i;
}
}
return 0;
}
/*
=================
R_AddDrawSurf
=================
*/
void R_AddDrawSurf(surfaceType_t * surface, shader_t * shader, int lightmapNum, int fogNum)
{
int index;
drawSurf_t *drawSurf;
// instead of checking for overflow, we just mask the index
// so it wraps around
index = tr.refdef.numDrawSurfs & DRAWSURF_MASK;
drawSurf = &tr.refdef.drawSurfs[index];
drawSurf->entity = tr.currentEntity;
drawSurf->surface = surface;
drawSurf->shaderNum = shader->sortedIndex;
drawSurf->lightmapNum = lightmapNum;
drawSurf->fogNum = fogNum;
tr.refdef.numDrawSurfs++;
}
/*
=================
DrawSurfCompare
compare function for qsort()
=================
*/
static int DrawSurfCompare(const void *a, const void *b)
{
#if 1
// by shader
if(((drawSurf_t *) a)->shaderNum < ((drawSurf_t *) b)->shaderNum)
return -1;
else if(((drawSurf_t *) a)->shaderNum > ((drawSurf_t *) b)->shaderNum)
return 1;
#endif
#if 1
// by lightmap
if(((drawSurf_t *) a)->lightmapNum < ((drawSurf_t *) b)->lightmapNum)
return -1;
else if(((drawSurf_t *) a)->lightmapNum > ((drawSurf_t *) b)->lightmapNum)
return 1;
#endif
#if 1
// by entity
if(((drawSurf_t *) a)->entity == &tr.worldEntity && ((drawSurf_t *) b)->entity != &tr.worldEntity)
return -1;
else if(((drawSurf_t *) a)->entity != &tr.worldEntity && ((drawSurf_t *) b)->entity == &tr.worldEntity)
return 1;
else if(((drawSurf_t *) a)->entity < ((drawSurf_t *) b)->entity)
return -1;
else if(((drawSurf_t *) a)->entity > ((drawSurf_t *) b)->entity)
return 1;
#endif
#if 1
// by fog
if(((drawSurf_t *) a)->fogNum < ((drawSurf_t *) b)->fogNum)
return -1;
else if(((drawSurf_t *) a)->fogNum > ((drawSurf_t *) b)->fogNum)
return 1;
#endif
return 0;
}
/*
=================
R_SortDrawSurfs
=================
*/
static void R_SortDrawSurfs()
{
drawSurf_t *drawSurf;
shader_t *shader;
int i;
// it is possible for some views to not have any surfaces
if(tr.viewParms.numDrawSurfs < 1)
{
// we still need to add it for hyperspace cases
R_AddDrawViewCmd();
return;
}
// if we overflowed MAX_DRAWSURFS, the drawsurfs
// wrapped around in the buffer and we will be missing
// the first surfaces, not the last ones
if(tr.viewParms.numDrawSurfs > MAX_DRAWSURFS)
{
tr.viewParms.numDrawSurfs = MAX_DRAWSURFS;
}
// if we overflowed MAX_INTERACTIONS, the interactions
// wrapped around in the buffer and we will be missing
// the first interactions, not the last ones
if(tr.viewParms.numInteractions > MAX_INTERACTIONS)
{
interaction_t *ia;
tr.viewParms.numInteractions = MAX_INTERACTIONS;
// reset last interaction's next pointer
ia = &tr.viewParms.interactions[tr.viewParms.numInteractions - 1];
ia->next = NULL;
}
// sort the drawsurfs by sort type, then orientation, then shader
// qsortFast(drawSurfs, numDrawSurfs, sizeof(drawSurf_t));
qsort(tr.viewParms.drawSurfs, tr.viewParms.numDrawSurfs, sizeof(drawSurf_t), DrawSurfCompare);
// check for any pass through drawing, which
// may cause another view to be rendered first
for(i = 0, drawSurf = tr.viewParms.drawSurfs; i < tr.viewParms.numDrawSurfs; i++, drawSurf++)
{
shader = tr.sortedShaders[drawSurf->shaderNum];
#if 1
if(shader->sort > SS_PORTAL)
{
break;
}
#else
if(!shader->isPortal)
{
continue;
}
#endif
//ri.Printf(PRINT_ALL, "portal or mirror surface\n");
// no shader should ever have this sort type
if(shader->sort == SS_BAD)
{
ri.Error(ERR_DROP, "Shader '%s'with sort == SS_BAD", shader->name);
}
// if the mirror was completely clipped away, we may need to check another surface
if(R_MirrorViewBySurface(drawSurf))
{
// this is a debug option to see exactly what is being mirrored
if(r_portalOnly->integer)
{
return;
}
break; // only one mirror view at a time
}
}
// tell renderer backend to render this view
R_AddDrawViewCmd();
}
/*
=============
R_AddEntitySurfaces
=============
*/
void R_AddEntitySurfaces(void)
{
int i;
trRefEntity_t *ent;
shader_t *shader;
if(!r_drawentities->integer)
{
return;
}
for(i = 0; i < tr.refdef.numEntities; i++)
{
ent = tr.currentEntity = &tr.refdef.entities[i];
//
// the weapon model must be handled special --
// we don't want the hacked weapon position showing in
// mirrors, because the true body position will already be drawn
//
if((ent->e.renderfx & RF_FIRST_PERSON) && (tr.viewParms.isPortal || tr.viewParms.isMirror))
{
continue;
}
// simple generated models, like sprites and beams, are not culled
switch (ent->e.reType)
{
case RT_PORTALSURFACE:
break; // don't draw anything
case RT_SPRITE:
case RT_SPLASH:
case RT_BEAM:
case RT_LIGHTNING:
case RT_RAIL_CORE:
// case RT_RAIL_CORE_TAPER:
case RT_RAIL_RINGS:
// self blood sprites, talk balloons, etc should not be drawn in the primary
// view. We can't just do this check for all entities, because md3
// entities may still want to cast shadows from them
if((ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal)
{
continue;
}
shader = R_GetShaderByHandle(ent->e.customShader);
R_AddDrawSurf(&entitySurface, shader, -1, R_SpriteFogNum(ent));
break;
case RT_MODEL:
// we must set up parts of tr.or for model culling
R_RotateEntityForViewParms(ent, &tr.viewParms, &tr.orientation);
tr.currentModel = R_GetModelByHandle(ent->e.hModel);
if(!tr.currentModel)
{
R_AddDrawSurf(&entitySurface, tr.defaultShader, -1, 0);
}
else
{
switch (tr.currentModel->type)
{
case MOD_MESH:
R_AddMDVSurfaces(ent);
break;
#if defined(COMPAT_ET)
case MOD_MDX:
// not a model, just a skeleton
break;
case MOD_MDM:
R_MDM_AddAnimSurfaces(ent);
break;
#endif
#if defined(USE_REFENTITY_ANIMATIONSYSTEM)
case MOD_MD5:
R_AddMD5Surfaces(ent);
break;
#endif
case MOD_BSP:
R_AddBSPModelSurfaces(ent);
break;
case MOD_BAD: // null model axis
if((ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal)
{
break;
}
VectorClear(ent->localBounds[0]);
VectorClear(ent->localBounds[1]);
VectorClear(ent->worldBounds[0]);
VectorClear(ent->worldBounds[1]);
shader = R_GetShaderByHandle(ent->e.customShader);
R_AddDrawSurf(&entitySurface, tr.defaultShader, -1, 0);
break;
default:
ri.Error(ERR_DROP, "R_AddEntitySurfaces: Bad modeltype");
break;
}
}
break;
default:
ri.Error(ERR_DROP, "R_AddEntitySurfaces: Bad reType");
}
}
}
/*
=============
R_AddEntityInteractions
=============
*/
void R_AddEntityInteractions(trRefLight_t * light)
{
int i;
trRefEntity_t *ent;
if(!r_drawentities->integer)
{
return;
}
for(i = 0; i < tr.refdef.numEntities; i++)
{
ent = tr.currentEntity = &tr.refdef.entities[i];
//
// the weapon model must be handled special --
// we don't want the hacked weapon position showing in
// mirrors, because the true body position will already be drawn
//
if((ent->e.renderfx & RF_FIRST_PERSON) && (tr.viewParms.isPortal || tr.viewParms.isMirror))
{
continue;
}
// simple generated models, like sprites and beams, are not culled
switch (ent->e.reType)
{
case RT_PORTALSURFACE:
break; // don't draw anything
case RT_SPRITE:
case RT_SPLASH:
case RT_BEAM:
case RT_LIGHTNING:
case RT_RAIL_CORE:
// case RT_RAIL_CORE_TAPER:
case RT_RAIL_RINGS:
break;
case RT_MODEL:
tr.currentModel = R_GetModelByHandle(ent->e.hModel);
if(!tr.currentModel)
{
//R_AddDrawSurf(&entitySurface, tr.defaultShader, 0);
}
else
{
switch (tr.currentModel->type)
{
case MOD_MESH:
R_AddMDVInteractions(ent, light);
break;
#if defined(COMPAT_ET)
case MOD_MDX:
// not a model, just a skeleton
break;
case MOD_MDM:
R_AddMDMInteractions(ent, light);
break;
#endif
#if defined(USE_REFENTITY_ANIMATIONSYSTEM)
case MOD_MD5:
R_AddMD5Interactions(ent, light);
break;
#endif
case MOD_BSP:
R_AddBrushModelInteractions(ent, light);
break;
case MOD_BAD: // null model axis
break;
default:
ri.Error(ERR_DROP, "R_AddEntityInteractions: Bad modeltype");
break;
}
}
break;
default:
ri.Error(ERR_DROP, "R_AddEntityInteractions: Bad reType");
}
}
}
/*
=====================
R_AddPolygonInteractions
=====================
*/
void R_AddPolygonInteractions(trRefLight_t * light)
{
int i, j;
shader_t *shader;
srfPoly_t *poly;
vec3_t worldBounds[2];
if(light->l.inverseShadows)
return;
tr.currentEntity = &tr.worldEntity;
for(i = 0, poly = tr.refdef.polys; i < tr.refdef.numPolys; i++, poly++)
{
shader = R_GetShaderByHandle(poly->hShader);
if(!shader->interactLight)
continue;
// calc AABB of the triangle
ClearBounds(worldBounds[0], worldBounds[1]);
for(j = 0; j < poly->numVerts; j++)
{
AddPointToBounds(poly->verts[j].xyz, worldBounds[0], worldBounds[1]);
}
// do a quick AABB cull
if(!BoundsIntersect(light->worldBounds[0], light->worldBounds[1], worldBounds[0], worldBounds[1]))
continue;
R_AddLightInteraction(light, (void *)poly, shader, CUBESIDE_CLIPALL, IA_LIGHTONLY);
}
}
/*
=============
R_AddLightInteractions
=============
*/
void R_AddLightInteractions()
{
int i, j;
trRefLight_t *light;
bspNode_t **leafs;
bspNode_t *leaf;
link_t *l, *sentinel;
for(i = 0; i < tr.refdef.numLights; i++)
{
light = tr.currentLight = &tr.refdef.lights[i];
if(light->isStatic)
{
if(!r_staticLight->integer || ((r_precomputedLighting->integer || r_vertexLighting->integer) && !light->noRadiosity))
{
light->cull = CULL_OUT;
continue;
}
}
else
{
if(!r_dynamicLight->integer)
{
light->cull = CULL_OUT;
continue;
}
}
// we must set up parts of tr.or for light culling
R_RotateLightForViewParms(light, &tr.viewParms, &tr.orientation);
// calc local bounds for culling
if(light->isStatic)
{
#if 1
// ignore if not in PVS
if(!r_noLightVisCull->integer)
{
if(glConfig2.occlusionQueryBits && glConfig.driverType != GLDRV_MESA && r_dynamicBspOcclusionCulling->integer)
{
int numVisibleLeafs = 0;
for(l = light->leafs.next; l != &light->leafs; l = l->next)
{
if(!l || !l->data)
{
// something odd happens with the prev/next pointers if ri.Hunk_Alloc was used
break;
}
leaf = (bspNode_t *) l->data;
//ri.Printf(PRINT_ALL, "leaf %i: visible = %i, %i\n", leaf - tr.world->nodes, leaf->visible[tr.viewCount], tr.viewCount);
if(leaf->visible[tr.viewCount] && (tr.frameCount - leaf->lastVisited[tr.viewCount]) <= r_chcMaxVisibleFrames->integer)
{
numVisibleLeafs++;
}
}
if(numVisibleLeafs == 0)
{
tr.pc.c_pvs_cull_light_out++;
light->cull = CULL_OUT;
continue;
}
}
else
{
for(l = light->leafs.next; l != &light->leafs; l = l->next)
{
if(!l || !l->data)
{
// something odd happens with the prev/next pointers if ri.Hunk_Alloc was used
break;
}
leaf = (bspNode_t *) l->data;
if(leaf->visCounts[tr.visIndex] == tr.visCounts[tr.visIndex])
{
light->visCounts[tr.visIndex] = tr.visCounts[tr.visIndex];
}
}
if(light->visCounts[tr.visIndex] != tr.visCounts[tr.visIndex])
{
tr.pc.c_pvs_cull_light_out++;
light->cull = CULL_OUT;
continue;
}
}
}
// look if we have to draw the light including its interactions
switch (R_CullLocalBox(light->localBounds))
{
case CULL_IN:
default:
tr.pc.c_box_cull_light_in++;
light->cull = CULL_IN;
break;
case CULL_CLIP:
tr.pc.c_box_cull_light_clip++;
light->cull = CULL_CLIP;
break;
case CULL_OUT:
// light is not visible so skip other light setup stuff to save speed
tr.pc.c_box_cull_light_out++;
light->cull = CULL_OUT;
continue;
}
#endif
}
else
{
// set up light transform matrix
MatrixSetupTransformFromQuat(light->transformMatrix, light->l.rotation, light->l.origin);
// set up light origin for lighting and shadowing
R_SetupLightOrigin(light);
// set up model to light view matrix
R_SetupLightView(light);
// set up projection
R_SetupLightProjection(light);
// calc local bounds for culling
R_SetupLightLocalBounds(light);
// look if we have to draw the light including its interactions
switch (R_CullLocalBox(light->localBounds))
{
case CULL_IN:
default:
tr.pc.c_box_cull_light_in++;
light->cull = CULL_IN;
break;
case CULL_CLIP:
tr.pc.c_box_cull_light_clip++;
light->cull = CULL_CLIP;
break;
case CULL_OUT:
// light is not visible so skip other light setup stuff to save speed
tr.pc.c_box_cull_light_out++;
light->cull = CULL_OUT;
continue;
}
// setup world bounds for intersection tests
R_SetupLightWorldBounds(light);
// setup frustum planes for intersection tests
R_SetupLightFrustum(light);
// ignore if not in visible bounds
if(!BoundsIntersect
(light->worldBounds[0], light->worldBounds[1], tr.viewParms.visBounds[0], tr.viewParms.visBounds[1]))
{
light->cull = CULL_OUT;
continue;
}
}
// set up view dependent light scissor
R_SetupLightScissor(light);
// set up view dependent light depth bounds
R_SetupLightDepthBounds(light);
// set up view dependent light Level of Detail
R_SetupLightLOD(light);
// look for proper attenuation shader
R_SetupLightShader(light);
// setup interactions
light->firstInteraction = NULL;
light->lastInteraction = NULL;
light->numInteractions = 0;
light->numShadowOnlyInteractions = 0;
light->numLightOnlyInteractions = 0;
light->noSort = qfalse;
if(r_deferredShading->integer && r_shadows->integer < SHADOWING_ESM16)
{
// add one fake interaction for this light
// because the renderer backend only loops through interactions
R_AddLightInteraction(light, NULL, NULL, CUBESIDE_CLIPALL, IA_DEFAULT);
}
else
{
if(light->isStatic)
{
R_AddPrecachedWorldInteractions(light);
}
else
{
R_AddWorldInteractions(light);
}
R_AddEntityInteractions(light);
// Tr3B: fun but slow
//R_AddPolygonInteractions(light);
}
if(light->numInteractions && light->numInteractions != light->numShadowOnlyInteractions)
{
R_SortInteractions(light);
if(light->isStatic)
{
tr.pc.c_slights++;
}
else
{
tr.pc.c_dlights++;
}
}
else
{
// skip all interactions of this light because it caused only shadow volumes
// but no lighting
tr.refdef.numInteractions -= light->numInteractions;
light->cull = CULL_OUT;
}
}
}
void R_AddLightBoundsToVisBounds()
{
int i, j;
trRefLight_t *light;
bspNode_t **leafs;
bspNode_t *leaf;
link_t *l, *sentinel;
for(i = 0; i < tr.refdef.numLights; i++)
{
light = tr.currentLight = &tr.refdef.lights[i];
if(light->isStatic)
{
if(!r_staticLight->integer || ((r_precomputedLighting->integer || r_vertexLighting->integer) && !light->noRadiosity))
{
//light->cull = CULL_OUT;
continue;
}
}
else
{
if(!r_dynamicLight->integer)
{
//light->cull = CULL_OUT;
continue;
}
}
// we must set up parts of tr.or for light culling
R_RotateLightForViewParms(light, &tr.viewParms, &tr.orientation);
// calc local bounds for culling
if(light->isStatic)
{
// ignore if not in PVS
if(!r_noLightVisCull->integer)
{
if(glConfig2.occlusionQueryBits && glConfig.driverType != GLDRV_MESA && r_dynamicBspOcclusionCulling->integer)
{
int numVisibleLeafs = 0;
for(l = light->leafs.next; l != &light->leafs; l = l->next)
{
if(!l || !l->data)
{
// something odd happens with the prev/next pointers if ri.Hunk_Alloc was used
break;
}
leaf = (bspNode_t *) l->data;
//ri.Printf(PRINT_ALL, "leaf %i: visible = %i, %i\n", leaf - tr.world->nodes, leaf->visible[tr.viewCount], tr.viewCount);
if(leaf->visible[tr.viewCount] && (tr.frameCount - leaf->lastVisited[tr.viewCount]) <= r_chcMaxVisibleFrames->integer)
{
numVisibleLeafs++;
}
}
if(numVisibleLeafs == 0)
{
//tr.pc.c_pvs_cull_light_out++;
//light->cull = CULL_OUT;
continue;
}
}
else
{
for(l = light->leafs.next; l != &light->leafs; l = l->next)
{
if(!l || !l->data)
{
// something odd happens with the prev/next pointers if ri.Hunk_Alloc was used
break;
}
leaf = (bspNode_t *) l->data;
if(leaf->visCounts[tr.visIndex] == tr.visCounts[tr.visIndex])
{
light->visCounts[tr.visIndex] = tr.visCounts[tr.visIndex];
}
}
if(light->visCounts[tr.visIndex] != tr.visCounts[tr.visIndex])
{
//tr.pc.c_pvs_cull_light_out++;
//light->cull = CULL_OUT;
continue;
}
}
}
// look if we have to draw the light including its interactions
switch (R_CullLocalBox(light->localBounds))
{
case CULL_IN:
default:
break;
case CULL_CLIP:
break;
case CULL_OUT:
continue;
}
}
else
{
// set up light transform matrix
MatrixSetupTransformFromQuat(light->transformMatrix, light->l.rotation, light->l.origin);
// set up light origin for lighting and shadowing
R_SetupLightOrigin(light);
// set up model to light view matrix
R_SetupLightView(light);
// set up projection
R_SetupLightProjection(light);
// calc local bounds for culling
R_SetupLightLocalBounds(light);
// look if we have to draw the light including its interactions
switch (R_CullLocalBox(light->localBounds))
{
case CULL_IN:
default:
break;
case CULL_CLIP:
break;
case CULL_OUT:
continue;
}
// setup world bounds for intersection tests
R_SetupLightWorldBounds(light);
}
// add to z buffer bounds
if(light->worldBounds[0][0] < tr.viewParms.visBounds[0][0])
{
tr.viewParms.visBounds[0][0] = light->worldBounds[0][0];
}
if(light->worldBounds[0][1] < tr.viewParms.visBounds[0][1])
{
tr.viewParms.visBounds[0][1] = light->worldBounds[0][1];
}
if(light->worldBounds[0][2] < tr.viewParms.visBounds[0][2])
{
tr.viewParms.visBounds[0][2] = light->worldBounds[0][2];
}
if(light->worldBounds[1][0] > tr.viewParms.visBounds[1][0])
{
tr.viewParms.visBounds[1][0] = light->worldBounds[1][0];
}
if(light->worldBounds[1][1] > tr.viewParms.visBounds[1][1])
{
tr.viewParms.visBounds[1][1] = light->worldBounds[1][1];
}
if(light->worldBounds[1][2] > tr.viewParms.visBounds[1][2])
{
tr.viewParms.visBounds[1][2] = light->worldBounds[1][2];
}
}
}
void R_DebugAxis(const vec3_t origin, const matrix_t transformMatrix)
{
#if 0
vec3_t forward, left, up;
MatrixToVectorsFLU(transformMatrix, forward, left, up);
VectorMA(origin, 16, forward, forward);
VectorMA(origin, 16, left, left);
VectorMA(origin, 16, up, up);
// draw axis
glLineWidth(3);
glBegin(GL_LINES);
glColor3f(1, 0, 0);
glVertex3fv(origin);
glVertex3fv(forward);
glColor3f(0, 1, 0);
glVertex3fv(origin);
glVertex3fv(left);
glColor3f(0, 0, 1);
glVertex3fv(origin);
glVertex3fv(up);
glEnd();
glLineWidth(1);
#endif
}
// Tr3B - from botlib
void R_DebugBoundingBox(const vec3_t origin, const vec3_t mins, const vec3_t maxs, vec4_t color)
{
#if 0
vec3_t corners[8];
int i;
// upper corners
corners[0][0] = origin[0] + maxs[0];
corners[0][1] = origin[1] + maxs[1];
corners[0][2] = origin[2] + maxs[2];
corners[1][0] = origin[0] + mins[0];
corners[1][1] = origin[1] + maxs[1];
corners[1][2] = origin[2] + maxs[2];
corners[2][0] = origin[0] + mins[0];
corners[2][1] = origin[1] + mins[1];
corners[2][2] = origin[2] + maxs[2];
corners[3][0] = origin[0] + maxs[0];
corners[3][1] = origin[1] + mins[1];
corners[3][2] = origin[2] + maxs[2];
// lower corners
Com_Memcpy(corners[4], corners[0], sizeof(vec3_t) * 4);
for(i = 0; i < 4; i++)
corners[4 + i][2] = origin[2] + mins[2];
// draw bounding box
glBegin(GL_LINES);
glVertexAttrib4fvARB(ATTR_INDEX_COLOR, color);
for(i = 0; i < 4; i++)
{
// top plane
glVertex3fv(corners[i]);
glVertex3fv(corners[(i + 1) & 3]);
// bottom plane
glVertex3fv(corners[4 + i]);
glVertex3fv(corners[4 + ((i + 1) & 3)]);
// vertical lines
glVertex3fv(corners[i]);
glVertex3fv(corners[4 + i]);
}
glEnd();
#endif
}
/*
================
R_DebugPolygon
================
*/
void R_DebugPolygon(int color, int numPoints, float *points)
{
#if 0
int i;
GL_BindProgram(0);
GL_VertexAttribsState(ATTR_DEFAULT);
GL_State(GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE);
// draw solid shade
glColor3f(color & 1, (color >> 1) & 1, (color >> 2) & 1);
glBegin(GL_POLYGON);
for(i = 0; i < numPoints; i++)
{
glVertex3fv(points + i * 3);
}
glEnd();
// draw wireframe outline
GL_State(GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE);
glDepthRange(0, 0);
glColor3f(1, 1, 1);
glBegin(GL_POLYGON);
for(i = 0; i < numPoints; i++)
{
glVertex3fv(points + i * 3);
}
glEnd();
glDepthRange(0, 1);
#endif
}
/*
================
R_DebugText
================
*/
void R_DebugText(const vec3_t org, float r, float g, float b, const char *text, qboolean neverOcclude)
{
#if 0
if(neverOcclude)
{
glDepthRange(0, 0); // never occluded
}
glColor3f(r, g, b);
glRasterPos3fv(org);
glPushAttrib(GL_LIST_BIT);
glListBase(gl_NormalFontBase);
glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);
glListBase(0);
glPopAttrib();
if(neverOcclude)
{
glDepthRange(0, 1);
}
#endif
}
/*
====================
R_DebugGraphics
Visualization aid for movement clipping debugging
====================
*/
static void R_DebugGraphics(void)
{
#if defined(USE_D3D10)
// TODO
#else
if(r_debugSurface->integer)
{
// the render thread can't make callbacks to the main thread
R_SyncRenderThread();
GL_BindProgram(0);
GL_SelectTexture(0);
GL_Bind(tr.whiteImage);
GL_Cull(CT_FRONT_SIDED);
ri.CM_DrawDebugSurface(R_DebugPolygon);
}
#endif
}
/*
================
R_RenderView
A view may be either the actual camera view,
or a mirror / remote location
================
*/
void R_RenderView(viewParms_t * parms)
{
int firstDrawSurf;
int firstInteraction;
matrix_t mvp;
if(parms->viewportWidth <= 0 || parms->viewportHeight <= 0)
{
return;
}
tr.viewCount++;
tr.viewCountNoReset++;
if(tr.viewCount >= MAX_VIEWS)
{
ri.Printf(PRINT_ALL, "MAX_VIEWS (%i) hit. Don't add more mirrors or portals. Skipping view ...\n", MAX_VIEWS);
return;
}
tr.viewParms = *parms;
tr.viewParms.frameSceneNum = tr.frameSceneNum;
tr.viewParms.frameCount = tr.frameCount;
tr.viewParms.viewCount = tr.viewCount;// % MAX_VIEWS;
firstDrawSurf = tr.refdef.numDrawSurfs;
firstInteraction = tr.refdef.numInteractions;
// set viewParms.world
R_RotateForViewer();
// set the projection matrix with the far clip plane set at infinity
// this required for the CHC++ algorithm
R_SetupProjection(qtrue);
R_SetupFrustum();
// RB: cull decal projects before calling R_AddWorldSurfaces
// because it requires the decalBits
R_CullDecalProjectors();
R_AddWorldSurfaces();
R_AddPolygonSurfaces();
R_AddPolygonBufferSurfaces();
// we have tr.viewParms.visBounds set and now we need to add the light bounds
// or we get wrong occlusion query results
R_AddLightBoundsToVisBounds();
// set the projection matrix now that we have the world bounded
// this needs to be done before entities are
// added, because they use the projection
// matrix for lod calculation
R_SetupProjection(qfalse);
#if defined(COMPAT_ET)
R_SetFrameFog();
#endif
R_SetupUnprojection();
// set camera frustum planes in world space again, but this time including the far plane
tr.orientation = tr.viewParms.world;
Matrix4x4Multiply(tr.viewParms.projectionMatrix, tr.orientation.modelViewMatrix, mvp);
R_SetupFrustum2(tr.viewParms.frustums[0], mvp);
// for parallel split shadow mapping
R_SetupSplitFrustums();
//R_AddWorldSurfaces();
R_AddEntitySurfaces();
R_AddLightInteractions();
tr.viewParms.drawSurfs = tr.refdef.drawSurfs + firstDrawSurf;
tr.viewParms.numDrawSurfs = tr.refdef.numDrawSurfs - firstDrawSurf;
tr.viewParms.interactions = tr.refdef.interactions + firstInteraction;
tr.viewParms.numInteractions = tr.refdef.numInteractions - firstInteraction;
R_SortDrawSurfs(tr.viewParms.drawSurfs, tr.viewParms.numDrawSurfs, tr.viewParms.interactions, tr.viewParms.numInteractions);
// draw main system development information (surface outlines, etc)
R_DebugGraphics();
}