mirror of
https://github.com/DrBeef/JKXR.git
synced 2024-11-29 23:42:38 +00:00
1695 lines
40 KiB
C++
1695 lines
40 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 1999 - 2005, Id Software, Inc.
|
|
Copyright (C) 2000 - 2013, Raven Software, Inc.
|
|
Copyright (C) 2001 - 2013, Activision, Inc.
|
|
Copyright (C) 2013 - 2015, OpenJK contributors
|
|
|
|
This file is part of the OpenJK source code.
|
|
|
|
OpenJK is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License version 2 as
|
|
published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "tr_local.h"
|
|
|
|
inline void Q_CastShort2Float(float *f, const short *s)
|
|
{
|
|
*f = ((float)*s);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
R_CullTriSurf
|
|
|
|
Returns true if the grid is completely culled away.
|
|
Also sets the clipped hint bit in tess
|
|
=================
|
|
*/
|
|
static qboolean R_CullTriSurf( srfTriangles_t *cv ) {
|
|
int boxCull;
|
|
|
|
boxCull = R_CullLocalBox( cv->bounds );
|
|
|
|
if ( boxCull == CULL_OUT ) {
|
|
return qtrue;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_CullGrid
|
|
|
|
Returns true if the grid is completely culled away.
|
|
Also sets the clipped hint bit in tess
|
|
=================
|
|
*/
|
|
static qboolean R_CullGrid( srfGridMesh_t *cv ) {
|
|
int boxCull;
|
|
int sphereCull;
|
|
|
|
if ( r_nocurves->integer ) {
|
|
return qtrue;
|
|
}
|
|
|
|
if ( tr.currentEntityNum != REFENTITYNUM_WORLD ) {
|
|
sphereCull = R_CullLocalPointAndRadius( cv->localOrigin, cv->meshRadius );
|
|
} else {
|
|
sphereCull = R_CullPointAndRadius( cv->localOrigin, cv->meshRadius );
|
|
}
|
|
boxCull = CULL_OUT;
|
|
|
|
// check for trivial reject
|
|
if ( sphereCull == CULL_OUT )
|
|
{
|
|
tr.pc.c_sphere_cull_patch_out++;
|
|
return qtrue;
|
|
}
|
|
// check bounding box if necessary
|
|
else if ( sphereCull == CULL_CLIP )
|
|
{
|
|
tr.pc.c_sphere_cull_patch_clip++;
|
|
|
|
boxCull = R_CullLocalBox( cv->meshBounds );
|
|
|
|
if ( boxCull == CULL_OUT )
|
|
{
|
|
tr.pc.c_box_cull_patch_out++;
|
|
return qtrue;
|
|
}
|
|
else if ( boxCull == CULL_IN )
|
|
{
|
|
tr.pc.c_box_cull_patch_in++;
|
|
}
|
|
else
|
|
{
|
|
tr.pc.c_box_cull_patch_clip++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tr.pc.c_sphere_cull_patch_in++;
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
R_CullSurface
|
|
|
|
Tries to back face cull surfaces before they are lighted or
|
|
added to the sorting list.
|
|
|
|
This will also allow mirrors on both sides of a model without recursion.
|
|
================
|
|
*/
|
|
static qboolean R_CullSurface( surfaceType_t *surface, shader_t *shader ) {
|
|
srfSurfaceFace_t *sface;
|
|
float d;
|
|
|
|
if ( r_nocull->integer ) {
|
|
return qfalse;
|
|
}
|
|
|
|
if ( *surface == SF_GRID ) {
|
|
return R_CullGrid( (srfGridMesh_t *)surface );
|
|
}
|
|
|
|
if ( *surface == SF_TRIANGLES ) {
|
|
return R_CullTriSurf( (srfTriangles_t *)surface );
|
|
}
|
|
|
|
if ( *surface != SF_FACE ) {
|
|
return qfalse;
|
|
}
|
|
|
|
if ( shader->cullType == CT_TWO_SIDED ) {
|
|
return qfalse;
|
|
}
|
|
|
|
// face culling
|
|
if ( !r_facePlaneCull->integer ) {
|
|
return qfalse;
|
|
}
|
|
|
|
sface = ( srfSurfaceFace_t * ) surface;
|
|
|
|
if (r_cullRoofFaces->integer)
|
|
{ //Very slow, but this is only intended for taking shots for automap images.
|
|
if (sface->plane.normal[2] > 0.0f &&
|
|
sface->numPoints > 0)
|
|
{ //it's facing up I guess
|
|
static int i;
|
|
static trace_t tr;
|
|
static vec3_t basePoint;
|
|
static vec3_t endPoint;
|
|
static vec3_t nNormal;
|
|
static vec3_t v;
|
|
|
|
//The fact that this point is in the middle of the array has no relation to the
|
|
//orientation in the surface outline.
|
|
basePoint[0] = sface->points[sface->numPoints/2][0];
|
|
basePoint[1] = sface->points[sface->numPoints/2][1];
|
|
basePoint[2] = sface->points[sface->numPoints/2][2];
|
|
basePoint[2] += 2.0f;
|
|
|
|
//the endpoint will be 8192 units from the chosen point
|
|
//in the direction of the surface normal
|
|
|
|
//just go straight up I guess, for now (slight hack)
|
|
VectorSet(nNormal, 0.0f, 0.0f, 1.0f);
|
|
VectorMA(basePoint, 8192.0f, nNormal, endPoint);
|
|
|
|
ri.CM_BoxTrace(&tr, basePoint, endPoint, NULL, NULL, 0, (CONTENTS_SOLID|CONTENTS_TERRAIN), qfalse);
|
|
|
|
if (!tr.startsolid &&
|
|
!tr.allsolid &&
|
|
(tr.fraction == 1.0f || (tr.surfaceFlags & SURF_NOIMPACT)))
|
|
{ //either hit nothing or sky, so this surface is near the top of the level I guess. Or the floor of a really tall room, but if that's the case we're just screwed.
|
|
VectorSubtract(basePoint, tr.endpos, v);
|
|
if (tr.fraction == 1.0f || VectorLength(v) < r_roofCullCeilDist->value)
|
|
{ //ignore it if it's not close to the top, unless it just hit nothing
|
|
//Let's try to dig back into the brush based on the negative direction of the plane,
|
|
//and if we pop out on the other side we'll see if it's ground or not.
|
|
i = 4;
|
|
VectorCopy(sface->plane.normal, nNormal);
|
|
VectorInverse(nNormal);
|
|
|
|
while (i < 4096)
|
|
{
|
|
VectorMA(basePoint, i, nNormal, endPoint);
|
|
ri.CM_BoxTrace(&tr, endPoint, endPoint, NULL, NULL, 0, (CONTENTS_SOLID|CONTENTS_TERRAIN), qfalse);
|
|
if (!tr.startsolid &&
|
|
!tr.allsolid &&
|
|
tr.fraction == 1.0f)
|
|
{ //in the clear
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
if (i < 4096)
|
|
{ //Make sure we got into clearance
|
|
VectorCopy(endPoint, basePoint);
|
|
basePoint[2] -= 2.0f;
|
|
|
|
//just go straight down I guess, for now (slight hack)
|
|
VectorSet(nNormal, 0.0f, 0.0f, -1.0f);
|
|
VectorMA(basePoint, 4096.0f, nNormal, endPoint);
|
|
|
|
//trace a second time from the clear point in the inverse normal direction of the surface.
|
|
//If we hit something within a set amount of units, we will assume it's a bridge type object
|
|
//and leave it to be drawn. Otherwise we will assume it is a roof or other obstruction and
|
|
//cull it out.
|
|
ri.CM_BoxTrace(&tr, basePoint, endPoint, NULL, NULL, 0, (CONTENTS_SOLID|CONTENTS_TERRAIN), qfalse);
|
|
|
|
if (!tr.startsolid &&
|
|
!tr.allsolid &&
|
|
(tr.fraction != 1.0f && !(tr.surfaceFlags & SURF_NOIMPACT)))
|
|
{ //if we hit nothing or a noimpact going down then this is probably "ground".
|
|
VectorSubtract(basePoint, tr.endpos, endPoint);
|
|
if (VectorLength(endPoint) > r_roofCullCeilDist->value)
|
|
{ //128 (by default) is our maximum tolerance, above that will be removed
|
|
return qtrue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
d = DotProduct (tr.ori.viewOrigin, sface->plane.normal);
|
|
|
|
// don't cull exactly on the plane, because there are levels of rounding
|
|
// through the BSP, ICD, and hardware that may cause pixel gaps if an
|
|
// epsilon isn't allowed here
|
|
if ( shader->cullType == CT_FRONT_SIDED ) {
|
|
if ( d < sface->plane.dist - 8 ) {
|
|
return qtrue;
|
|
}
|
|
} else {
|
|
if ( d > sface->plane.dist + 8 ) {
|
|
return qtrue;
|
|
}
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
static int R_DlightFace( srfSurfaceFace_t *face, int dlightBits ) {
|
|
float d;
|
|
int i;
|
|
dlight_t *dl;
|
|
|
|
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
|
|
if ( ! ( dlightBits & ( 1 << i ) ) ) {
|
|
continue;
|
|
}
|
|
dl = &tr.refdef.dlights[i];
|
|
d = DotProduct( dl->origin, face->plane.normal ) - face->plane.dist;
|
|
if ( !VectorCompare(face->plane.normal, vec3_origin) && (d < -dl->radius || d > dl->radius) ) {
|
|
// dlight doesn't reach the plane
|
|
dlightBits &= ~( 1 << i );
|
|
}
|
|
}
|
|
|
|
if ( !dlightBits ) {
|
|
tr.pc.c_dlightSurfacesCulled++;
|
|
}
|
|
|
|
face->dlightBits = dlightBits;
|
|
return dlightBits;
|
|
}
|
|
|
|
static int R_DlightGrid( srfGridMesh_t *grid, int dlightBits ) {
|
|
int i;
|
|
dlight_t *dl;
|
|
|
|
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
|
|
if ( ! ( dlightBits & ( 1 << i ) ) ) {
|
|
continue;
|
|
}
|
|
dl = &tr.refdef.dlights[i];
|
|
if ( dl->origin[0] - dl->radius > grid->meshBounds[1][0]
|
|
|| dl->origin[0] + dl->radius < grid->meshBounds[0][0]
|
|
|| dl->origin[1] - dl->radius > grid->meshBounds[1][1]
|
|
|| dl->origin[1] + dl->radius < grid->meshBounds[0][1]
|
|
|| dl->origin[2] - dl->radius > grid->meshBounds[1][2]
|
|
|| dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) {
|
|
// dlight doesn't reach the bounds
|
|
dlightBits &= ~( 1 << i );
|
|
}
|
|
}
|
|
|
|
if ( !dlightBits ) {
|
|
tr.pc.c_dlightSurfacesCulled++;
|
|
}
|
|
|
|
grid->dlightBits = dlightBits;
|
|
return dlightBits;
|
|
}
|
|
|
|
|
|
static int R_DlightTrisurf( srfTriangles_t *surf, int dlightBits ) {
|
|
// FIXME: more dlight culling to trisurfs...
|
|
surf->dlightBits = dlightBits;
|
|
return dlightBits;
|
|
#if 0
|
|
int i;
|
|
dlight_t *dl;
|
|
|
|
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
|
|
if ( ! ( dlightBits & ( 1 << i ) ) ) {
|
|
continue;
|
|
}
|
|
dl = &tr.refdef.dlights[i];
|
|
if ( dl->origin[0] - dl->radius > grid->meshBounds[1][0]
|
|
|| dl->origin[0] + dl->radius < grid->meshBounds[0][0]
|
|
|| dl->origin[1] - dl->radius > grid->meshBounds[1][1]
|
|
|| dl->origin[1] + dl->radius < grid->meshBounds[0][1]
|
|
|| dl->origin[2] - dl->radius > grid->meshBounds[1][2]
|
|
|| dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) {
|
|
// dlight doesn't reach the bounds
|
|
dlightBits &= ~( 1 << i );
|
|
}
|
|
}
|
|
|
|
if ( !dlightBits ) {
|
|
tr.pc.c_dlightSurfacesCulled++;
|
|
}
|
|
|
|
grid->dlightBits = dlightBits;
|
|
return dlightBits;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
====================
|
|
R_DlightSurface
|
|
|
|
The given surface is going to be drawn, and it touches a leaf
|
|
that is touched by one or more dlights, so try to throw out
|
|
more dlights if possible.
|
|
====================
|
|
*/
|
|
static int R_DlightSurface( msurface_t *surf, int dlightBits ) {
|
|
if ( *surf->data == SF_FACE ) {
|
|
dlightBits = R_DlightFace( (srfSurfaceFace_t *)surf->data, dlightBits );
|
|
} else if ( *surf->data == SF_GRID ) {
|
|
dlightBits = R_DlightGrid( (srfGridMesh_t *)surf->data, dlightBits );
|
|
} else if ( *surf->data == SF_TRIANGLES ) {
|
|
dlightBits = R_DlightTrisurf( (srfTriangles_t *)surf->data, dlightBits );
|
|
} else {
|
|
dlightBits = 0;
|
|
}
|
|
|
|
if ( dlightBits ) {
|
|
tr.pc.c_dlightSurfaces++;
|
|
}
|
|
|
|
return dlightBits;
|
|
}
|
|
|
|
|
|
|
|
#ifdef _ALT_AUTOMAP_METHOD
|
|
static bool tr_drawingAutoMap = false;
|
|
#endif
|
|
static float g_playerHeight = 0.0f;
|
|
|
|
/*
|
|
======================
|
|
R_AddWorldSurface
|
|
======================
|
|
*/
|
|
static void R_AddWorldSurface( msurface_t *surf, int dlightBits, qboolean noViewCount = qfalse )
|
|
{
|
|
if (!noViewCount)
|
|
{
|
|
if ( surf->viewCount == tr.viewCount )
|
|
{
|
|
// already in this view, but lets make sure all the dlight bits are set
|
|
if ( *surf->data == SF_FACE )
|
|
{
|
|
((srfSurfaceFace_t *)surf->data)->dlightBits |= dlightBits;
|
|
}
|
|
else if ( *surf->data == SF_GRID )
|
|
{
|
|
((srfGridMesh_t *)surf->data)->dlightBits |= dlightBits;
|
|
}
|
|
else if ( *surf->data == SF_TRIANGLES )
|
|
{
|
|
((srfTriangles_t *)surf->data)->dlightBits |= dlightBits;
|
|
}
|
|
return;
|
|
}
|
|
surf->viewCount = tr.viewCount;
|
|
// FIXME: bmodel fog?
|
|
}
|
|
|
|
/*
|
|
if (r_shadows->integer == 2)
|
|
{
|
|
dlightBits = R_DlightSurface( surf, dlightBits );
|
|
//dlightBits = ( dlightBits != 0 );
|
|
R_AddDrawSurf( surf->data, tr.shadowShader, surf->fogIndex, dlightBits );
|
|
}
|
|
*/
|
|
//world shadows?
|
|
|
|
// try to cull before dlighting or adding
|
|
#ifdef _ALT_AUTOMAP_METHOD
|
|
if (!tr_drawingAutoMap && R_CullSurface( surf->data, surf->shader ) )
|
|
#else
|
|
if (R_CullSurface(surf->data, surf->shader))
|
|
#endif
|
|
{
|
|
return;
|
|
}
|
|
|
|
// check for dlighting
|
|
if ( dlightBits ) {
|
|
dlightBits = R_DlightSurface( surf, dlightBits );
|
|
dlightBits = ( dlightBits != 0 );
|
|
}
|
|
|
|
#ifdef _ALT_AUTOMAP_METHOD
|
|
if (tr_drawingAutoMap)
|
|
{
|
|
// if (g_playerHeight != g_lastHeight ||
|
|
// !g_lastHeightValid)
|
|
if (*surf->data == SF_FACE)
|
|
{ //only do this if we need to
|
|
bool completelyTransparent = true;
|
|
int i = 0;
|
|
srfSurfaceFace_t *face = (srfSurfaceFace_t *)surf->data;
|
|
byte *indices = (byte *)(face + face->ofsIndices);
|
|
float *point;
|
|
vec3_t color;
|
|
float alpha;
|
|
float e;
|
|
bool polyStarted = false;
|
|
|
|
while (i < face->numIndices)
|
|
{
|
|
point = &face->points[indices[i]][0];
|
|
|
|
//base the color on the elevation... for now, just check the first point height
|
|
if (point[2] < g_playerHeight)
|
|
{
|
|
e = point[2]-g_playerHeight;
|
|
}
|
|
else
|
|
{
|
|
e = g_playerHeight-point[2];
|
|
}
|
|
if (e < 0.0f)
|
|
{
|
|
e = -e;
|
|
}
|
|
|
|
//set alpha and color based on relative height of point
|
|
alpha = e/256.0f;
|
|
e /= 512.0f;
|
|
|
|
//cap color
|
|
if (e > 1.0f)
|
|
{
|
|
e = 1.0f;
|
|
}
|
|
else if (e < 0.0f)
|
|
{
|
|
e = 0.0f;
|
|
}
|
|
VectorSet(color, e, 1.0f-e, 0.0f);
|
|
|
|
//cap alpha
|
|
if (alpha > 1.0f)
|
|
{
|
|
alpha = 1.0f;
|
|
}
|
|
else if (alpha < 0.0f)
|
|
{
|
|
alpha = 0.0f;
|
|
}
|
|
|
|
if (alpha != 1.0f)
|
|
{ //this point is not entirely alpha'd out, so still draw the surface
|
|
completelyTransparent = false;
|
|
}
|
|
|
|
if (!completelyTransparent)
|
|
{
|
|
if (!polyStarted)
|
|
{
|
|
qglBegin(GL_POLYGON);
|
|
polyStarted = true;
|
|
}
|
|
|
|
qglColor4f(color[0], color[1], color[2], 1.0f-alpha);
|
|
qglVertex3f(point[i], point[i], point[2]);
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
if (polyStarted)
|
|
{
|
|
qglEnd();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlightBits );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============================================================
|
|
|
|
BRUSH MODELS
|
|
|
|
=============================================================
|
|
*/
|
|
|
|
/*
|
|
=================
|
|
R_AddBrushModelSurfaces
|
|
=================
|
|
*/
|
|
void R_AddBrushModelSurfaces ( trRefEntity_t *ent ) {
|
|
bmodel_t *bmodel;
|
|
int clip;
|
|
model_t *pModel;
|
|
int i;
|
|
|
|
pModel = R_GetModelByHandle( ent->e.hModel );
|
|
|
|
bmodel = pModel->bmodel;
|
|
|
|
clip = R_CullLocalBox( bmodel->bounds );
|
|
if ( clip == CULL_OUT ) {
|
|
return;
|
|
}
|
|
|
|
if(pModel->bspInstance)
|
|
{ //rwwRMG - added
|
|
R_SetupEntityLighting(&tr.refdef, ent);
|
|
}
|
|
|
|
//rww - Take this into account later?
|
|
// if ( !ri.Cvar_VariableIntegerValue( "com_RMG" ) )
|
|
// { // don't dlight bmodels on rmg, as multiple copies of the same instance will light up
|
|
R_DlightBmodel( bmodel, false );
|
|
// }
|
|
// else
|
|
// {
|
|
// R_DlightBmodel( bmodel, true );
|
|
// }
|
|
|
|
for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) {
|
|
R_AddWorldSurface( bmodel->firstSurface + i, tr.currentEntity->dlightBits, qtrue );
|
|
}
|
|
}
|
|
|
|
float GetQuadArea( vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4 )
|
|
{
|
|
vec3_t vec1, vec2, dis1, dis2;
|
|
|
|
// Get area of tri1
|
|
VectorSubtract( v1, v2, vec1 );
|
|
VectorSubtract( v1, v4, vec2 );
|
|
CrossProduct( vec1, vec2, dis1 );
|
|
VectorScale( dis1, 0.25f, dis1 );
|
|
|
|
// Get area of tri2
|
|
VectorSubtract( v3, v2, vec1 );
|
|
VectorSubtract( v3, v4, vec2 );
|
|
CrossProduct( vec1, vec2, dis2 );
|
|
VectorScale( dis2, 0.25f, dis2 );
|
|
|
|
// Return addition of disSqr of each tri area
|
|
return ( dis1[0] * dis1[0] + dis1[1] * dis1[1] + dis1[2] * dis1[2] +
|
|
dis2[0] * dis2[0] + dis2[1] * dis2[1] + dis2[2] * dis2[2] );
|
|
}
|
|
|
|
void RE_GetBModelVerts( int bmodelIndex, vec3_t *verts, vec3_t normal )
|
|
{
|
|
msurface_t *surfs;
|
|
srfSurfaceFace_t *face;
|
|
bmodel_t *bmodel;
|
|
model_t *pModel;
|
|
int i;
|
|
// Not sure if we really need to track the best two candidates
|
|
int maxDist[2]={0,0};
|
|
int maxIndx[2]={0,0};
|
|
int dist = 0;
|
|
float dot1, dot2;
|
|
|
|
pModel = R_GetModelByHandle( bmodelIndex );
|
|
bmodel = pModel->bmodel;
|
|
|
|
// Loop through all surfaces on the brush and find the best two candidates
|
|
for ( i = 0 ; i < bmodel->numSurfaces; i++ )
|
|
{
|
|
surfs = bmodel->firstSurface + i;
|
|
face = ( srfSurfaceFace_t *)surfs->data;
|
|
|
|
// It seems that the safest way to handle this is by finding the area of the faces
|
|
dist = GetQuadArea( face->points[0], face->points[1], face->points[2], face->points[3] );
|
|
|
|
// Check against the highest max
|
|
if ( dist > maxDist[0] )
|
|
{
|
|
// Shuffle our current maxes down
|
|
maxDist[1] = maxDist[0];
|
|
maxIndx[1] = maxIndx[0];
|
|
|
|
maxDist[0] = dist;
|
|
maxIndx[0] = i;
|
|
}
|
|
// Check against the second highest max
|
|
else if ( dist >= maxDist[1] )
|
|
{
|
|
// just stomp the old
|
|
maxDist[1] = dist;
|
|
maxIndx[1] = i;
|
|
}
|
|
}
|
|
|
|
// Hopefully we've found two best case candidates. Now we should see which of these faces the viewer
|
|
surfs = bmodel->firstSurface + maxIndx[0];
|
|
face = ( srfSurfaceFace_t *)surfs->data;
|
|
dot1 = DotProduct( face->plane.normal, tr.refdef.viewaxis[0] );
|
|
|
|
surfs = bmodel->firstSurface + maxIndx[1];
|
|
face = ( srfSurfaceFace_t *)surfs->data;
|
|
dot2 = DotProduct( face->plane.normal, tr.refdef.viewaxis[0] );
|
|
|
|
if ( dot2 < dot1 && dot2 < 0.0f )
|
|
{
|
|
i = maxIndx[1]; // use the second face
|
|
}
|
|
else if ( dot1 < dot2 && dot1 < 0.0f )
|
|
{
|
|
i = maxIndx[0]; // use the first face
|
|
}
|
|
else
|
|
{ // Possibly only have one face, so may as well use the first face, which also should be the best one
|
|
//i = rand() & 1; // ugh, we don't know which to use. I'd hope this would never happen
|
|
i = maxIndx[0]; // use the first face
|
|
}
|
|
|
|
surfs = bmodel->firstSurface + i;
|
|
face = ( srfSurfaceFace_t *)surfs->data;
|
|
|
|
for ( int t = 0; t < 4; t++ )
|
|
{
|
|
VectorCopy( face->points[t], verts[t] );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============================================================
|
|
|
|
WORLD MODEL
|
|
|
|
=============================================================
|
|
*/
|
|
|
|
/*
|
|
=============================================================
|
|
WIREFRAME AUTOMAP GENERATION SYSTEM - BEGIN
|
|
=============================================================
|
|
*/
|
|
#ifndef _ALT_AUTOMAP_METHOD
|
|
typedef struct wireframeSurfPoint_s
|
|
{
|
|
vec3_t xyz;
|
|
float alpha;
|
|
vec3_t color;
|
|
} wireframeSurfPoint_t;
|
|
|
|
typedef struct wireframeMapSurf_s
|
|
{
|
|
bool completelyTransparent;
|
|
|
|
int numPoints;
|
|
wireframeSurfPoint_t *points;
|
|
|
|
wireframeMapSurf_s *next;
|
|
} wireframeMapSurf_t;
|
|
|
|
typedef struct wireframeMap_s
|
|
{
|
|
wireframeMapSurf_t *surfs;
|
|
} wireframeMap_t;
|
|
|
|
static wireframeMap_t g_autoMapFrame;
|
|
static wireframeMapSurf_t **g_autoMapNextFree = NULL;
|
|
static bool g_autoMapValid = false; //set to true of g_autoMapFrame is valid.
|
|
|
|
//get the next available wireframe automap surface. -rww
|
|
static inline wireframeMapSurf_t *R_GetNewWireframeMapSurf(void)
|
|
{
|
|
wireframeMapSurf_t **next = &g_autoMapFrame.surfs;
|
|
|
|
if (g_autoMapNextFree)
|
|
{ //save us the time of going through the entire linked list from root
|
|
next = g_autoMapNextFree;
|
|
}
|
|
|
|
while (*next)
|
|
{ //iterate through until we find the next unused one
|
|
next = &(*next)->next;
|
|
}
|
|
|
|
//allocate memory for it and pass it back
|
|
(*next) = (wireframeMapSurf_t *)Z_Malloc(sizeof(wireframeMapSurf_t), TAG_ALL, qtrue);
|
|
g_autoMapNextFree = &(*next)->next;
|
|
return (*next);
|
|
}
|
|
|
|
//evaluate a surface, see if it is valid for being part of the
|
|
//wireframe map render. -rww
|
|
static inline void R_EvaluateWireframeSurf(msurface_t *surf)
|
|
{
|
|
if (*surf->data == SF_FACE)
|
|
{
|
|
srfSurfaceFace_t *face = (srfSurfaceFace_t *)surf->data;
|
|
float *points = &face->points[0][0];
|
|
int numPoints = face->numIndices;
|
|
int *indices = (int *)((byte *)face + face->ofsIndices);
|
|
//byte *indices = (byte *)(face + face->ofsIndices);
|
|
|
|
if (points && numPoints > 0)
|
|
{ //we can add it
|
|
int i = 0;
|
|
wireframeMapSurf_t *nextSurf = R_GetNewWireframeMapSurf();
|
|
|
|
#if 0 //doing in realtime now
|
|
float e;
|
|
|
|
//base the color on the elevation... for now, just check the first point height
|
|
if (points[2] < 0.0f)
|
|
{
|
|
e = -points[2];
|
|
}
|
|
else
|
|
{
|
|
e = points[2];
|
|
}
|
|
e /= 2048.0f;
|
|
if (e > 1.0f)
|
|
{
|
|
e = 1.0f;
|
|
}
|
|
else if (e < 0.0f)
|
|
{
|
|
e = 0.0f;
|
|
}
|
|
VectorSet(color, e, 1.0f-e, 0.0f);
|
|
#endif
|
|
|
|
//now go through the indices and add a point for each
|
|
nextSurf->points = (wireframeSurfPoint_t *)Z_Malloc(sizeof(wireframeSurfPoint_t)*face->numIndices, TAG_ALL, qtrue);
|
|
nextSurf->numPoints = face->numIndices;
|
|
while (i < face->numIndices)
|
|
{
|
|
points = &face->points[indices[i]][0];
|
|
VectorCopy(points, nextSurf->points[i].xyz);
|
|
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
else if (*surf->data == SF_TRIANGLES)
|
|
{
|
|
//srfTriangles_t *surfTri = (srfTriangles_t *)surf->data;
|
|
return; //not handled
|
|
}
|
|
else if (*surf->data == SF_GRID)
|
|
{
|
|
//srfGridMesh_t *gridMesh = (srfGridMesh_t *)surf->data;
|
|
return; //not handled
|
|
}
|
|
else
|
|
{ //...unknown type?
|
|
return;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
//see if any surfaces on the node are facing opposite directions
|
|
//using plane normals. -rww
|
|
static inline bool R_NodeHasOppositeFaces(mnode_t *node)
|
|
{
|
|
int c, d;
|
|
msurface_t *surf, *surf2, **mark, **mark2;
|
|
srfSurfaceFace_t *face, *face2;
|
|
vec3_t normalDif;
|
|
|
|
mark = node->firstmarksurface;
|
|
c = node->nummarksurfaces;
|
|
|
|
while (c--)
|
|
{
|
|
surf = *mark;
|
|
|
|
if (*surf->data != SF_FACE)
|
|
{ //if this node is not entirely comprised of faces, I guess we shouldn't check it?
|
|
return false;
|
|
}
|
|
|
|
face = (srfSurfaceFace_t *)surf->data;
|
|
|
|
//go through other surfs and compare against this surf
|
|
d = node->nummarksurfaces;
|
|
mark2 = node->firstmarksurface;
|
|
while (d--)
|
|
{
|
|
surf2 = *mark2;
|
|
|
|
if (*surf2->data != SF_FACE)
|
|
{
|
|
return false;
|
|
}
|
|
face2 = (srfSurfaceFace_t *)surf2->data;
|
|
//see if this normal has a drastic angular change
|
|
VectorSubtract(face->plane.normal, face2->plane.normal, normalDif);
|
|
if (VectorLength(normalDif) >= 1.8f)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
mark2++;
|
|
}
|
|
mark++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
//recursively called for each node to go through the surfaces on that
|
|
//node and generate the wireframe map. -rww
|
|
static inline void R_RecursiveWireframeSurf(mnode_t *node)
|
|
{
|
|
int c;
|
|
msurface_t *surf, **mark;
|
|
|
|
if (!node)
|
|
{
|
|
return;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
if (!node ||
|
|
node->visframe != tr.visCount)
|
|
{ //not valid, stop this chain of recursion
|
|
return;
|
|
}
|
|
|
|
if ( node->contents != -1 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
R_RecursiveWireframeSurf(node->children[0]);
|
|
|
|
node = node->children[1];
|
|
}
|
|
|
|
// add the individual surfaces
|
|
mark = node->firstmarksurface;
|
|
c = node->nummarksurfaces;
|
|
while (c--)
|
|
{
|
|
// the surface may have already been added if it
|
|
// spans multiple leafs
|
|
surf = *mark;
|
|
R_EvaluateWireframeSurf(surf);
|
|
mark++;
|
|
}
|
|
}
|
|
|
|
//generates a wireframe model of the map for the automap view -rww
|
|
static void R_GenerateWireframeMap(mnode_t *baseNode)
|
|
{
|
|
int i;
|
|
|
|
//initialize data to all 0
|
|
memset(&g_autoMapFrame, 0, sizeof(g_autoMapFrame));
|
|
|
|
//take the hit for this frame, mark all of these things as visible
|
|
//so we know which are valid for automap generation, but only the
|
|
//ones that are facing outside the world! (well, ideally.)
|
|
for (i = 0; i < tr.world->numnodes; i++)
|
|
{
|
|
if (tr.world->nodes[i].contents != CONTENTS_SOLID)
|
|
{
|
|
#if 0 //doesn't work, I take it surfs on nodes are not related to surfs on brushes
|
|
if (!R_NodeHasOppositeFaces(&tr.world->nodes[i]))
|
|
#endif
|
|
{
|
|
tr.world->nodes[i].visframe = tr.visCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
//now start the recursive evaluation
|
|
R_RecursiveWireframeSurf(baseNode);
|
|
}
|
|
|
|
//clear out the wireframe map data -rww
|
|
void R_DestroyWireframeMap(void)
|
|
{
|
|
wireframeMapSurf_t *next;
|
|
wireframeMapSurf_t *last;
|
|
|
|
if (!g_autoMapValid)
|
|
{ //not valid to begin with
|
|
return;
|
|
}
|
|
|
|
next = g_autoMapFrame.surfs;
|
|
while (next)
|
|
{
|
|
//free memory allocated for points on this surface
|
|
Z_Free(next->points);
|
|
|
|
//get the next surface
|
|
last = next;
|
|
next = next->next;
|
|
|
|
//free memory for this surface
|
|
Z_Free(last);
|
|
}
|
|
|
|
//invalidate everything
|
|
memset(&g_autoMapFrame, 0, sizeof(g_autoMapFrame));
|
|
g_autoMapValid = false;
|
|
g_autoMapNextFree = NULL;
|
|
}
|
|
|
|
//save 3d automap data to file -rww
|
|
qboolean R_WriteWireframeMapToFile(void)
|
|
{
|
|
fileHandle_t f;
|
|
int requiredSize = 0;
|
|
wireframeMapSurf_t *surf = g_autoMapFrame.surfs;
|
|
byte *out, *rOut;
|
|
|
|
//let's go through and see how much space we're going to need to stuff all this
|
|
//data into
|
|
while (surf)
|
|
{
|
|
//memory for each point
|
|
requiredSize += sizeof(wireframeSurfPoint_t)*surf->numPoints;
|
|
|
|
//memory for numPoints
|
|
requiredSize += sizeof(int);
|
|
|
|
surf = surf->next;
|
|
}
|
|
|
|
if (requiredSize <= 0)
|
|
{ //nothing to do..?
|
|
return qfalse;
|
|
}
|
|
|
|
|
|
f = ri.FS_FOpenFileWrite("blahblah.bla", qtrue);
|
|
if (!f)
|
|
{ //can't create?
|
|
return qfalse;
|
|
}
|
|
|
|
//allocate the memory we will need
|
|
out = (byte *)Z_Malloc(requiredSize, TAG_ALL, qtrue);
|
|
rOut = out;
|
|
|
|
//now go through and put the data into the memory
|
|
surf = g_autoMapFrame.surfs;
|
|
while (surf)
|
|
{
|
|
memcpy(out, surf, (sizeof(wireframeSurfPoint_t)*surf->numPoints) + sizeof(int));
|
|
|
|
//memory for each point
|
|
out += sizeof(wireframeSurfPoint_t)*surf->numPoints;
|
|
|
|
//memory for numPoints
|
|
out += sizeof(int);
|
|
|
|
surf = surf->next;
|
|
}
|
|
|
|
//now write the buffer, and close
|
|
ri.FS_Write(rOut, requiredSize, f);
|
|
Z_Free(rOut);
|
|
ri.FS_FCloseFile(f);
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
//load 3d automap data from file -rww
|
|
qboolean R_GetWireframeMapFromFile(void)
|
|
{
|
|
wireframeMapSurf_t *surfs, *rSurfs;
|
|
wireframeMapSurf_t *newSurf;
|
|
fileHandle_t f;
|
|
int i = 0;
|
|
int len;
|
|
int stepBytes;
|
|
|
|
len = ri.FS_FOpenFileRead("blahblah.bla", &f, qfalse);
|
|
if (!f || len <= 0)
|
|
{ //it doesn't exist
|
|
return qfalse;
|
|
}
|
|
|
|
surfs = (wireframeMapSurf_t *)Z_Malloc(len, TAG_ALL, qtrue);
|
|
rSurfs = surfs;
|
|
ri.FS_Read(surfs, len, f);
|
|
|
|
while (i < len)
|
|
{
|
|
newSurf = R_GetNewWireframeMapSurf();
|
|
newSurf->points = (wireframeSurfPoint_t *)Z_Malloc(sizeof(wireframeSurfPoint_t)*surfs->numPoints, TAG_ALL, qtrue);
|
|
|
|
//copy the surf data into the new surf
|
|
//note - the surfs->points pointer is NOT pointing to valid memory, a pointer to that
|
|
//pointer is actually what we want to use as the location of the point offsets.
|
|
memcpy(newSurf->points, &surfs->points, sizeof(wireframeSurfPoint_t)*surfs->numPoints);
|
|
newSurf->numPoints = surfs->numPoints;
|
|
|
|
//the size of the point data, plus an int (the number of points)
|
|
stepBytes = (sizeof(wireframeSurfPoint_t)*surfs->numPoints) + sizeof(int);
|
|
i += stepBytes;
|
|
|
|
//increment the pointer to the start of the next surface
|
|
surfs = (wireframeMapSurf_t *)((byte *)surfs+stepBytes);
|
|
}
|
|
|
|
//it should end up being equal, if not something was wrong with this file.
|
|
assert(i == len);
|
|
|
|
ri.FS_FCloseFile(f);
|
|
Z_Free(rSurfs);
|
|
return qtrue;
|
|
}
|
|
|
|
//create everything, after destroying any existing data -rww
|
|
qboolean R_InitializeWireframeAutomap(void)
|
|
{
|
|
if (r_autoMapDisable && r_autoMapDisable->integer)
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if (tr.world &&
|
|
tr.world->nodes)
|
|
{
|
|
R_DestroyWireframeMap();
|
|
#if 0 //file load-save
|
|
if (!R_GetWireframeMapFromFile())
|
|
{ //first try loading the data from a file. If there is none, generate it.
|
|
R_GenerateWireframeMap(tr.world->nodes);
|
|
|
|
//now write it to file, since we have generated it successfully.
|
|
R_WriteWireframeMapToFile();
|
|
}
|
|
#else //always generate
|
|
R_GenerateWireframeMap(tr.world->nodes);
|
|
#endif
|
|
g_autoMapValid = true;
|
|
}
|
|
|
|
return (qboolean)g_autoMapValid;
|
|
}
|
|
#endif //0
|
|
/*
|
|
=============================================================
|
|
WIREFRAME AUTOMAP GENERATION SYSTEM - END
|
|
=============================================================
|
|
*/
|
|
|
|
void R_AutomapElevationAdjustment(float newHeight)
|
|
{
|
|
g_playerHeight = newHeight;
|
|
}
|
|
|
|
#ifdef _ALT_AUTOMAP_METHOD
|
|
//adjust the player height for gradient elevation colors -rww
|
|
qboolean R_InitializeWireframeAutomap(void)
|
|
{ //yoink
|
|
return qtrue;
|
|
}
|
|
#endif
|
|
|
|
//draw the automap with the given transformation matrix -rww
|
|
#define QUADINFINITY 16777216
|
|
static float g_lastHeight = 0.0f;
|
|
static bool g_lastHeightValid = false;
|
|
static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits );
|
|
const void *R_DrawWireframeAutomap(const void *data)
|
|
{
|
|
const drawBufferCommand_t *cmd = (const drawBufferCommand_t *)data;
|
|
float e = 0.0f;
|
|
float alpha;
|
|
wireframeMapSurf_t *s = g_autoMapFrame.surfs;
|
|
#ifndef _ALT_AUTOMAP_METHOD
|
|
int i;
|
|
#endif
|
|
|
|
if (!r_autoMap || !r_autoMap->integer)
|
|
{
|
|
return (const void *)(cmd + 1);
|
|
}
|
|
|
|
#ifndef _ALT_AUTOMAP_METHOD
|
|
if (!g_autoMapValid)
|
|
{ //data is not valid, don't draw
|
|
return (const void *)(cmd + 1);
|
|
}
|
|
#endif
|
|
|
|
#if 0 //instead of this method, just do the automap as a new "scene"
|
|
//projection matrix mode
|
|
qglMatrixMode(GL_PROJECTION);
|
|
|
|
//store the current matrix
|
|
qglPushMatrix();
|
|
//translate to our proper pos/angles from identity
|
|
qglLoadIdentity();
|
|
qglTranslatef(pos[0], pos[1], pos[2]);
|
|
//presumeably this is correct for compensating for quake's
|
|
//crazy angle system.
|
|
qglRotatef(angles[1], 0.0f, 0.0f, 1.0f);
|
|
qglRotatef(-angles[0], 0.0f, 1.0f, 0.0f);
|
|
qglRotatef(angles[2], 1.0f, 0.0f, 0.0f);
|
|
#endif
|
|
|
|
//disable 2d texturing
|
|
qglDisable( GL_TEXTURE_2D );
|
|
|
|
//now draw the backdrop
|
|
#if 0 //this does no good sadly, because of the issue of having to clear with a second scene
|
|
//in order for global fog clearing to work.
|
|
if (r_autoMapBackAlpha && r_autoMapBackAlpha->value)
|
|
{ //specify the automap background alpha
|
|
alpha = r_autoMapBackAlpha->value;
|
|
|
|
//cap it reasonably
|
|
if (alpha < 0.0f)
|
|
{
|
|
alpha = 0.0f;
|
|
}
|
|
else if (alpha > 1.0f)
|
|
{
|
|
alpha = 1.0f;
|
|
}
|
|
GL_State(GLS_SRCBLEND_SRC_ALPHA|GLS_DSTBLEND_SRC_ALPHA);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
alpha = 1.0f;
|
|
GL_State(0);
|
|
}
|
|
//black
|
|
qglColor4f(0.0f, 0.0f, 0.0f, alpha);
|
|
|
|
//draw a black backdrop
|
|
qglPushMatrix();
|
|
qglLoadIdentity(); //get the ident matrix
|
|
|
|
qglBegin( GL_QUADS );
|
|
qglVertex3f( -QUADINFINITY, QUADINFINITY, -(backEnd.viewParms.zFar-1) );
|
|
qglVertex3f( QUADINFINITY, QUADINFINITY, -(backEnd.viewParms.zFar-1) );
|
|
qglVertex3f( QUADINFINITY, -QUADINFINITY, -(backEnd.viewParms.zFar-1) );
|
|
qglVertex3f( -QUADINFINITY, -QUADINFINITY, -(backEnd.viewParms.zFar-1) );
|
|
qglEnd ();
|
|
|
|
//pop back the viewmatrix
|
|
qglPopMatrix();
|
|
|
|
|
|
//set the mode to line draw
|
|
if (r_autoMap->integer == 2)
|
|
{ //line mode
|
|
GL_State(GLS_POLYMODE_LINE|GLS_SRCBLEND_SRC_ALPHA|GLS_DSTBLEND_SRC_COLOR|GLS_DEPTHMASK_TRUE);
|
|
}
|
|
else
|
|
{ //fill mode
|
|
//GL_State(GLS_SRCBLEND_SRC_ALPHA|GLS_DSTBLEND_SRC_COLOR|GLS_DEPTHMASK_TRUE);
|
|
GL_State(GLS_DEPTHMASK_TRUE);
|
|
}
|
|
|
|
//set culling
|
|
GL_Cull(CT_TWO_SIDED);
|
|
|
|
#ifndef _ALT_AUTOMAP_METHOD
|
|
//Draw the triangles
|
|
while (s)
|
|
{
|
|
//first, loop through and set the alpha on every point for this surf.
|
|
//if the alpha ends up being completely transparent for every point, we don't even
|
|
//need to draw it
|
|
if (g_playerHeight != g_lastHeight ||
|
|
!g_lastHeightValid)
|
|
{ //only do this if we need to
|
|
i = 0;
|
|
s->completelyTransparent = true;
|
|
while (i < s->numPoints)
|
|
{
|
|
//base the color on the elevation... for now, just check the first point height
|
|
if (s->points[i].xyz[2] < g_playerHeight)
|
|
{
|
|
e = s->points[i].xyz[2]-g_playerHeight;
|
|
}
|
|
else
|
|
{
|
|
e = g_playerHeight-s->points[i].xyz[2];
|
|
}
|
|
if (e < 0.0f)
|
|
{
|
|
e = -e;
|
|
}
|
|
|
|
if (r_autoMap->integer != 2)
|
|
{ //fill mode
|
|
if (s->points[i].xyz[2] > (g_playerHeight+64.0f))
|
|
{
|
|
s->points[i].alpha = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
s->points[i].alpha = e/256.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//set alpha and color based on relative height of point
|
|
s->points[i].alpha = e/256.0f;
|
|
}
|
|
e /= 512.0f;
|
|
|
|
//cap color
|
|
if (e > 1.0f)
|
|
{
|
|
e = 1.0f;
|
|
}
|
|
else if (e < 0.0f)
|
|
{
|
|
e = 0.0f;
|
|
}
|
|
VectorSet(s->points[i].color, e, 1.0f-e, 0.0f);
|
|
|
|
//cap alpha
|
|
if (s->points[i].alpha > 1.0f)
|
|
{
|
|
s->points[i].alpha = 1.0f;
|
|
}
|
|
else if (s->points[i].alpha < 0.0f)
|
|
{
|
|
s->points[i].alpha = 0.0f;
|
|
}
|
|
|
|
if (s->points[i].alpha != 1.0f)
|
|
{ //this point is not entirely alpha'd out, so still draw the surface
|
|
s->completelyTransparent = false;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (s->completelyTransparent)
|
|
{
|
|
s = s->next;
|
|
continue;
|
|
}
|
|
|
|
i = 0;
|
|
qglBegin(GL_TRIANGLES);
|
|
while (i < s->numPoints)
|
|
{
|
|
if (r_autoMap->integer == 2 || s->numPoints < 3)
|
|
{ //line mode or not enough verts on surface
|
|
qglColor4f(s->points[i].color[0], s->points[i].color[1], s->points[i].color[2], s->points[i].alpha);
|
|
}
|
|
else
|
|
{ //fill mode
|
|
vec3_t planeNormal;
|
|
float fAlpha = s->points[i].alpha;
|
|
planeNormal[0] = s->points[0].xyz[1]*(s->points[1].xyz[2]-s->points[2].xyz[2]) + s->points[1].xyz[1]*(s->points[2].xyz[2]-s->points[0].xyz[2]) + s->points[2].xyz[1]*(s->points[0].xyz[2]-s->points[1].xyz[2]);
|
|
planeNormal[1] = s->points[0].xyz[2]*(s->points[1].xyz[0]-s->points[2].xyz[0]) + s->points[1].xyz[2]*(s->points[2].xyz[0]-s->points[0].xyz[0]) + s->points[2].xyz[2]*(s->points[0].xyz[0]-s->points[1].xyz[0]);
|
|
planeNormal[2] = s->points[0].xyz[0]*(s->points[1].xyz[1]-s->points[2].xyz[1]) + s->points[1].xyz[0]*(s->points[2].xyz[1]-s->points[0].xyz[1]) + s->points[2].xyz[0]*(s->points[0].xyz[1]-s->points[1].xyz[1]);
|
|
|
|
if (planeNormal[0] < 0.0f) planeNormal[0] = -planeNormal[0];
|
|
if (planeNormal[1] < 0.0f) planeNormal[1] = -planeNormal[1];
|
|
if (planeNormal[2] < 0.0f) planeNormal[2] = -planeNormal[2];
|
|
|
|
/*
|
|
if (s->points[i].xyz[2] > g_playerHeight+64.0f &&
|
|
planeNormal[2] > 0.7f)
|
|
{ //surface above player facing up/down directly
|
|
fAlpha = 1.0f-planeNormal[2];
|
|
}
|
|
*/
|
|
|
|
//qglColor4f(planeNormal[0], planeNormal[1], planeNormal[2], fAlpha);
|
|
qglColor4f(s->points[i].color[0], s->points[i].color[1], 1.0f-planeNormal[2], fAlpha);
|
|
}
|
|
qglVertex3f(s->points[i].xyz[0], s->points[i].xyz[1], s->points[i].xyz[2]);
|
|
i++;
|
|
}
|
|
qglEnd();
|
|
s = s->next;
|
|
}
|
|
#else
|
|
tr_drawingAutoMap = true;
|
|
R_RecursiveWorldNode( tr.world->nodes, 15, 0 );
|
|
tr_drawingAutoMap = false;
|
|
#endif
|
|
|
|
g_lastHeight = g_playerHeight;
|
|
g_lastHeightValid = true;
|
|
|
|
#if 0 //instead of this method, just do the automap as a new "scene"
|
|
//pop back the view matrix
|
|
qglPopMatrix();
|
|
#endif
|
|
|
|
//reenable 2d texturing
|
|
qglEnable( GL_TEXTURE_2D );
|
|
|
|
//white color/full alpha
|
|
qglColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
return (const void *)(cmd + 1);
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
R_RecursiveWorldNode
|
|
================
|
|
*/
|
|
static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits ) {
|
|
|
|
do
|
|
{
|
|
int newDlights[2];
|
|
|
|
#ifdef _ALT_AUTOMAP_METHOD
|
|
if (tr_drawingAutoMap)
|
|
{
|
|
node->visframe = tr.visCount;
|
|
}
|
|
#endif
|
|
|
|
// if the node wasn't marked as potentially visible, exit
|
|
if (node->visframe != tr.visCount)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if the bounding volume is outside the frustum, nothing
|
|
// inside can be visible OPTIMIZE: don't do this all the way to leafs?
|
|
|
|
#ifdef _ALT_AUTOMAP_METHOD
|
|
if ( r_nocull->integer!=1 && !tr_drawingAutoMap )
|
|
#else
|
|
if (r_nocull->integer!=1)
|
|
#endif
|
|
{
|
|
int r;
|
|
|
|
if ( planeBits & 1 ) {
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[0]);
|
|
if (r == 2) {
|
|
return; // culled
|
|
}
|
|
if ( r == 1 ) {
|
|
planeBits &= ~1; // all descendants will also be in front
|
|
}
|
|
}
|
|
|
|
if ( planeBits & 2 ) {
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[1]);
|
|
if (r == 2) {
|
|
return; // culled
|
|
}
|
|
if ( r == 1 ) {
|
|
planeBits &= ~2; // all descendants will also be in front
|
|
}
|
|
}
|
|
|
|
if ( planeBits & 4 ) {
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[2]);
|
|
if (r == 2) {
|
|
return; // culled
|
|
}
|
|
if ( r == 1 ) {
|
|
planeBits &= ~4; // all descendants will also be in front
|
|
}
|
|
}
|
|
|
|
if ( planeBits & 8 ) {
|
|
r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[3]);
|
|
if (r == 2) {
|
|
return; // culled
|
|
}
|
|
if ( r == 1 ) {
|
|
planeBits &= ~8; // all descendants will also be in front
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if ( node->contents != -1 ) {
|
|
break;
|
|
}
|
|
|
|
// node is just a decision point, so go down both sides
|
|
// since we don't care about sort orders, just go positive to negative
|
|
|
|
// determine which dlights are needed
|
|
if ( r_nocull->integer!=2 )
|
|
{
|
|
newDlights[0] = 0;
|
|
newDlights[1] = 0;
|
|
if ( dlightBits )
|
|
{
|
|
int i;
|
|
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ )
|
|
{
|
|
dlight_t *dl;
|
|
float dist;
|
|
|
|
if ( dlightBits & ( 1 << i ) ) {
|
|
dl = &tr.refdef.dlights[i];
|
|
dist = DotProduct( dl->origin, node->plane->normal ) - node->plane->dist;
|
|
|
|
if ( dist > -dl->radius ) {
|
|
newDlights[0] |= ( 1 << i );
|
|
}
|
|
if ( dist < dl->radius ) {
|
|
newDlights[1] |= ( 1 << i );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newDlights[0] = dlightBits;
|
|
newDlights[1] = dlightBits;
|
|
}
|
|
|
|
// recurse down the children, front side first
|
|
R_RecursiveWorldNode (node->children[0], planeBits, newDlights[0] );
|
|
|
|
// tail recurse
|
|
node = node->children[1];
|
|
dlightBits = newDlights[1];
|
|
} while ( 1 );
|
|
|
|
{
|
|
// leaf node, so add mark surfaces
|
|
int c;
|
|
msurface_t *surf, **mark;
|
|
|
|
tr.pc.c_leafs++;
|
|
|
|
// add to z buffer bounds
|
|
if ( node->mins[0] < tr.viewParms.visBounds[0][0] ) {
|
|
tr.viewParms.visBounds[0][0] = node->mins[0];
|
|
}
|
|
if ( node->mins[1] < tr.viewParms.visBounds[0][1] ) {
|
|
tr.viewParms.visBounds[0][1] = node->mins[1];
|
|
}
|
|
if ( node->mins[2] < tr.viewParms.visBounds[0][2] ) {
|
|
tr.viewParms.visBounds[0][2] = node->mins[2];
|
|
}
|
|
|
|
if ( node->maxs[0] > tr.viewParms.visBounds[1][0] ) {
|
|
tr.viewParms.visBounds[1][0] = node->maxs[0];
|
|
}
|
|
if ( node->maxs[1] > tr.viewParms.visBounds[1][1] ) {
|
|
tr.viewParms.visBounds[1][1] = node->maxs[1];
|
|
}
|
|
if ( node->maxs[2] > tr.viewParms.visBounds[1][2] ) {
|
|
tr.viewParms.visBounds[1][2] = node->maxs[2];
|
|
}
|
|
|
|
// add the individual surfaces
|
|
mark = node->firstmarksurface;
|
|
c = node->nummarksurfaces;
|
|
while (c--) {
|
|
// the surface may have already been added if it
|
|
// spans multiple leafs
|
|
surf = *mark;
|
|
R_AddWorldSurface( surf, dlightBits );
|
|
mark++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_PointInLeaf
|
|
===============
|
|
*/
|
|
static mnode_t *R_PointInLeaf( const vec3_t p ) {
|
|
mnode_t *node;
|
|
float d;
|
|
cplane_t *plane;
|
|
|
|
if ( !tr.world ) {
|
|
Com_Error (ERR_DROP, "R_PointInLeaf: bad model");
|
|
}
|
|
|
|
node = tr.world->nodes;
|
|
while( 1 ) {
|
|
if (node->contents != -1) {
|
|
break;
|
|
}
|
|
plane = node->plane;
|
|
d = DotProduct (p,plane->normal) - plane->dist;
|
|
if (d > 0) {
|
|
node = node->children[0];
|
|
} else {
|
|
node = node->children[1];
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_ClusterPVS
|
|
==============
|
|
*/
|
|
static const byte *R_ClusterPVS (int cluster) {
|
|
if (!tr.world || !tr.world->vis || cluster < 0 || cluster >= tr.world->numClusters ) {
|
|
return tr.world->novis;
|
|
}
|
|
|
|
return tr.world->vis + cluster * tr.world->clusterBytes;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_inPVS
|
|
=================
|
|
*/
|
|
qboolean R_inPVS( const vec3_t p1, const vec3_t p2, byte *mask ) {
|
|
int leafnum;
|
|
int cluster;
|
|
|
|
leafnum = ri.CM_PointLeafnum (p1);
|
|
cluster = ri.CM_LeafCluster (leafnum);
|
|
|
|
//agh, the damn snapshot mask doesn't work for this
|
|
mask = (byte *) ri.CM_ClusterPVS (cluster);
|
|
|
|
leafnum = ri.CM_PointLeafnum (p2);
|
|
cluster = ri.CM_LeafCluster (leafnum);
|
|
if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
|
|
return qfalse;
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_MarkLeaves
|
|
|
|
Mark the leaves and nodes that are in the PVS for the current
|
|
cluster
|
|
===============
|
|
*/
|
|
static void R_MarkLeaves (void) {
|
|
const byte *vis;
|
|
mnode_t *leaf, *parent;
|
|
int i;
|
|
int cluster;
|
|
|
|
// lockpvs lets designers walk around to determine the
|
|
// extent of the current pvs
|
|
if ( r_lockpvs->integer ) {
|
|
return;
|
|
}
|
|
|
|
// current viewcluster
|
|
leaf = R_PointInLeaf( tr.viewParms.pvsOrigin );
|
|
cluster = leaf->cluster;
|
|
|
|
// if the cluster is the same and the area visibility matrix
|
|
// hasn't changed, we don't need to mark everything again
|
|
|
|
// if r_showcluster was just turned on, remark everything
|
|
if ( tr.viewCluster == cluster && !tr.refdef.areamaskModified
|
|
&& !r_showcluster->modified ) {
|
|
return;
|
|
}
|
|
|
|
if ( r_showcluster->modified || r_showcluster->integer ) {
|
|
r_showcluster->modified = qfalse;
|
|
if ( r_showcluster->integer ) {
|
|
ri.Printf( PRINT_ALL, "cluster:%i area:%i\n", cluster, leaf->area );
|
|
}
|
|
}
|
|
|
|
tr.visCount++;
|
|
tr.viewCluster = cluster;
|
|
|
|
if ( r_novis->integer || tr.viewCluster == -1 ) {
|
|
for (i=0 ; i<tr.world->numnodes ; i++) {
|
|
if (tr.world->nodes[i].contents != CONTENTS_SOLID) {
|
|
tr.world->nodes[i].visframe = tr.visCount;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
vis = R_ClusterPVS (tr.viewCluster);
|
|
|
|
for (i=0,leaf=tr.world->nodes ; i<tr.world->numnodes ; i++, leaf++) {
|
|
cluster = leaf->cluster;
|
|
if ( cluster < 0 || cluster >= tr.world->numClusters ) {
|
|
continue;
|
|
}
|
|
|
|
// check general pvs
|
|
if ( !(vis[cluster>>3] & (1<<(cluster&7))) ) {
|
|
continue;
|
|
}
|
|
|
|
// check for door connection
|
|
if ( (tr.refdef.areamask[leaf->area>>3] & (1<<(leaf->area&7)) ) ) {
|
|
continue; // not visible
|
|
}
|
|
|
|
parent = leaf;
|
|
do {
|
|
if (parent->visframe == tr.visCount)
|
|
break;
|
|
parent->visframe = tr.visCount;
|
|
parent = parent->parent;
|
|
} while (parent);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_AddWorldSurfaces
|
|
=============
|
|
*/
|
|
void R_AddWorldSurfaces (void) {
|
|
if ( !r_drawworld->integer ) {
|
|
return;
|
|
}
|
|
|
|
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
|
|
return;
|
|
}
|
|
|
|
tr.currentEntityNum = REFENTITYNUM_WORLD;
|
|
tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT;
|
|
|
|
// determine which leaves are in the PVS / areamask
|
|
R_MarkLeaves ();
|
|
|
|
// clear out the visible min/max
|
|
ClearBounds( tr.viewParms.visBounds[0], tr.viewParms.visBounds[1] );
|
|
|
|
// perform frustum culling and add all the potentially visible surfaces
|
|
if ( tr.refdef.num_dlights > 32 ) {
|
|
tr.refdef.num_dlights = 32 ;
|
|
}
|
|
|
|
R_RecursiveWorldNode( tr.world->nodes, 15, ( 1 << tr.refdef.num_dlights ) - 1 );
|
|
}
|