mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-05-13 05:56:56 +03:00
3495 lines
84 KiB
C
3495 lines
84 KiB
C
/*
|
||
===========================================================================
|
||
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();
|
||
}
|