ref: gl: implement mirrors

This commit is contained in:
Alibek Omarov
2024-01-14 15:57:58 +03:00
parent 547a41ef2d
commit f5832faaa0
8 changed files with 709 additions and 11 deletions

View File

@@ -111,6 +111,9 @@ void GL_BackendEndFrame( void )
Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i tempents\n%3i viewbeams\n%3i particles",
r_stats.c_active_tents_count, r_stats.c_view_beams_count, r_stats.c_particle_count );
break;
case 6:
Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i mirrors\n", r_stats.c_mirror_passes );
break;
}
memset( &r_stats, 0, sizeof( r_stats ));

View File

@@ -74,6 +74,8 @@ extern poolhandle_t r_temppool;
#define SUBDIVIDE_SIZE 64
#define MAX_DECAL_SURFS 4096
#define MAX_DRAW_STACK 2 // normal view and menu view
#define MAX_MIRRORS 32 // per frame
#define MAX_MIRROR_ENTITIES MAX_VISIBLE_PACKET
#define SHADEDOT_QUANT 16 // precalculated dot products for quantized angles
#define SHADE_LAMBERT 1.4953241
@@ -137,6 +139,13 @@ typedef struct gltexture_s
struct gltexture_s *nextHash;
} gl_texture_t;
// mirror entity
typedef struct
{
cl_entity_t *ent;
mextrasurf_t *chain;
} gl_entity_t;
typedef struct
{
int params; // rendering parameters
@@ -201,9 +210,11 @@ typedef struct
cl_entity_t *solid_entities[MAX_VISIBLE_PACKET]; // opaque moving or alpha brushes
cl_entity_t *trans_entities[MAX_VISIBLE_PACKET]; // translucent brushes
cl_entity_t *beam_entities[MAX_VISIBLE_PACKET];
gl_entity_t mirror_entities[MAX_MIRROR_ENTITIES]; // an entities that has mirror
uint num_solid_entities;
uint num_trans_entities;
uint num_beam_entities;
uint num_mirror_entities;
} draw_list_t;
typedef struct
@@ -219,6 +230,8 @@ typedef struct
int dlightTexture; // custom dlight texture
int skyboxTextures[SKYBOX_MAX_SIDES]; // skybox sides
int cinTexture; // cinematic texture
int mirrorTextures[MAX_MIRRORS];
int num_mirrors_used;
int skytexturenum; // this not a gl_texturenum!
int skyboxbasenum; // start with 5800
@@ -281,6 +294,8 @@ typedef struct
uint c_client_ents; // entities that moved to client
double t_world_node;
double t_world_draw;
uint c_mirror_passes;
} ref_speeds_t;
extern ref_speeds_t r_stats;
@@ -498,7 +513,7 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse );
void R_InitRipples( void );
void R_ResetRipples( void );
void R_AnimateRipples( void );
void R_UploadRipples( texture_t *image );
void R_UploadRipples( texture_t *image, qboolean is_mirror );
//
// gl_vgui.c
@@ -517,7 +532,13 @@ void VGUI_DrawQuad( const vpoint_t *ul, const vpoint_t *lr );
void VGUI_GetTextureSizes( int *width, int *height );
int VGUI_GenerateTexture( void );
//#include "vid_common.h"
//
// gl_mirror.c
//
void R_BeginDrawMirror( msurface_t *fa );
void R_EndDrawMirror( void );
void R_DrawMirrors( void );
void R_FindMirrors( void );
//
// renderer exports
@@ -768,6 +789,7 @@ extern convar_t gl_test; // cvar to testify new effects
extern convar_t gl_msaa;
extern convar_t gl_stencilbits;
extern convar_t gl_overbright;
extern convar_t gl_allow_mirrors;
extern convar_t r_lighting_extended;
extern convar_t r_lighting_ambient;

615
ref/gl/gl_mirror.c Normal file
View File

@@ -0,0 +1,615 @@
/*
gl_mirror.c - draw reflected surfaces
Copyright (C) 2010 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "gl_local.h"
#include "mod_local.h"
#include "xash3d_mathlib.h"
static void GL_LoadTexMatrix( const matrix4x4 m )
{
pglMatrixMode( GL_TEXTURE );
GL_LoadMatrix( m );
glState.texIdentityMatrix[glState.activeTMU] = false;
}
static void Matrix4x4_CreateScale( matrix4x4 out, float x )
{
out[0][0] = x;
out[0][1] = out[0][2] = out[0][3] = 0.0f;
out[1][0] = out[1][2] = out[1][3] = 0.0f;
out[1][1] = x;
out[2][0] = out[2][1] = out[2][3] = 0.0f;
out[2][2] = x;
out[3][0] = out[3][1] = out[3][2] = 0.0f;
out[3][3] = 1.0f;
}
static void Matrix4x4_ConcatScale( matrix4x4 out, float x )
{
matrix4x4 base, temp;
Matrix4x4_Copy( base, out );
Matrix4x4_CreateScale( temp, x );
Matrix4x4_Concat( out, base, temp );
}
/*
================
R_BeginDrawMirror
Setup texture matrix for mirror texture
================
*/
void R_BeginDrawMirror( msurface_t *fa )
{
matrix4x4 m1, m2, matrix;
GLfloat genVector[4][4];
int i;
Matrix4x4_Copy( matrix, fa->info->mirrormatrix );
Matrix4x4_LoadIdentity( m1 );
Matrix4x4_ConcatScale( m1, 0.5f );
Matrix4x4_Concat( m2, m1, matrix );
Matrix4x4_LoadIdentity( m1 );
Matrix4x4_ConcatTranslate( m1, 0.5f, 0.5f, 0.5f );
Matrix4x4_Concat( matrix, m1, m2 );
for( i = 0; i < 4; i++ )
{
genVector[0][i] = i == 0 ? 1 : 0;
genVector[1][i] = i == 1 ? 1 : 0;
genVector[2][i] = i == 2 ? 1 : 0;
genVector[3][i] = i == 3 ? 1 : 0;
}
GL_TexGen( GL_S, GL_OBJECT_LINEAR );
GL_TexGen( GL_T, GL_OBJECT_LINEAR );
GL_TexGen( GL_R, GL_OBJECT_LINEAR );
GL_TexGen( GL_Q, GL_OBJECT_LINEAR );
pglTexGenfv( GL_S, GL_OBJECT_PLANE, genVector[0] );
pglTexGenfv( GL_T, GL_OBJECT_PLANE, genVector[1] );
pglTexGenfv( GL_R, GL_OBJECT_PLANE, genVector[2] );
pglTexGenfv( GL_Q, GL_OBJECT_PLANE, genVector[3] );
GL_LoadTexMatrix( matrix );
}
/*
================
R_EndDrawMirror
Restore identity texmatrix
================
*/
void R_EndDrawMirror( void )
{
GL_CleanUpTextureUnits( 0 );
pglMatrixMode( GL_MODELVIEW );
}
/*
=============================================================
MIRROR RENDERING
=============================================================
*/
/*
================
R_PlaneForMirror
Get transformed mirrorplane and entity matrix
================
*/
static void R_PlaneForMirror( msurface_t *surf, mplane_t *out, matrix4x4 m )
{
cl_entity_t *ent;
ent = RI.currententity;
// setup mirror plane
*out = *surf->plane;
if( surf->flags & SURF_PLANEBACK )
{
VectorNegate( out->normal, out->normal );
out->dist = -out->dist;
}
if( !VectorIsNull( ent->origin ) || !VectorIsNull( ent->angles ))
{
mplane_t tmp;
if( !VectorIsNull( ent->angles )) Matrix4x4_CreateFromEntity( m, ent->angles, ent->origin, 1.0f );
else Matrix4x4_CreateFromEntity( m, vec3_origin, ent->origin, 1.0f );
tmp = *out;
// transform mirror plane by entity matrix
Matrix4x4_TransformPositivePlane( m, tmp.normal, tmp.dist, out->normal, &out->dist );
}
else Matrix4x4_LoadIdentity( m );
}
/*
================
R_AllocateMirrorTexture
Allocate the screen texture and make copy
================
*/
static int R_AllocateMirrorTexture( void )
{
rgbdata_t r_screen;
int i, texture;
char txName[16];
i = tr.num_mirrors_used;
if( i >= MAX_MIRRORS )
{
gEngfuncs.Con_Printf( S_ERROR "%s: mirror textures limit exceeded!\n", __func__ );
return 0; // disable
}
texture = tr.mirrorTextures[i];
tr.num_mirrors_used++;
if( !texture )
{
// not initialized ?
memset( &r_screen, 0, sizeof( r_screen ));
Q_snprintf( txName, sizeof( txName ), "*screen%i", i );
r_screen.width = RI.viewport[2];
r_screen.height = RI.viewport[3];
r_screen.type = PF_RGBA_32;
r_screen.size = r_screen.width * r_screen.height * 4;
r_screen.flags = IMAGE_HAS_COLOR;
r_screen.buffer = NULL; // create empty texture for now
tr.mirrorTextures[i] = GL_LoadTextureInternal( txName, &r_screen, TF_IMAGE );
texture = tr.mirrorTextures[i];
}
GL_Bind( XASH_TEXTURE0, texture );
pglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, RI.viewport[0], RI.viewport[1], RI.viewport[2], RI.viewport[3], 0 );
return texture;
}
/*
================
R_DrawMirrors
Draw all viewpasess from mirror position
Mirror textures will be drawn in normal pass
================
*/
void R_DrawMirrors( void )
{
ref_instance_t oldRI;
mplane_t plane;
msurface_t *surf, *surf2;
int i, oldframecount;
mextrasurf_t *es, *tmp, *mirrorchain;
vec3_t forward, right, up;
vec3_t origin, angles;
matrix4x4 mirrormatrix;
cl_entity_t *e;
model_t *m;
float d;
if( !tr.draw_list->num_mirror_entities ) return; // mo mirrors for this frame
oldRI = RI; // make refinst backup
oldframecount = tr.framecount;
for( i = 0; i < tr.draw_list->num_mirror_entities; i++ )
{
mirrorchain = tr.draw_list->mirror_entities[i].chain;
for( es = mirrorchain; es != NULL; es = es->mirrorchain )
{
RI.currententity = e = tr.draw_list->mirror_entities[i].ent;
RI.currentmodel = m = RI.currententity->model;
surf = es->surf;
ASSERT( RI.currententity != NULL );
ASSERT( RI.currentmodel != NULL );
// NOTE: copy mirrortexture and mirrormatrix from another surfaces
// from this entity\world that has same planes and reduce number of viewpasses
// make sure what we have one pass at least
if( es != mirrorchain )
{
for( tmp = mirrorchain; tmp != es; tmp = tmp->mirrorchain )
{
surf2 = tmp->surf;
if( !tmp->mirrortexturenum )
continue; // not filled?
if( surf->plane->dist != surf2->plane->dist )
continue;
if( !VectorCompare( surf->plane->normal, surf2->plane->normal ))
continue;
// found surface with same plane!
break;
}
if( tmp != es && tmp && tmp->mirrortexturenum )
{
// just copy reflection texture from surface with same plane
Matrix4x4_Copy( es->mirrormatrix, tmp->mirrormatrix );
es->mirrortexturenum = tmp->mirrortexturenum;
continue; // pass skiped
}
}
R_PlaneForMirror( surf, &plane, mirrormatrix );
d = -2.0f * ( DotProduct( RI.vieworg, plane.normal ) - plane.dist );
VectorMA( RI.vieworg, d, plane.normal, origin );
d = -2.0f * DotProduct( RI.vforward, plane.normal );
VectorMA( RI.vforward, d, plane.normal, forward );
VectorNormalize( forward );
d = -2.0f * DotProduct( RI.vright, plane.normal );
VectorMA( RI.vright, d, plane.normal, right );
VectorNormalize( right );
d = -2.0f * DotProduct( RI.vup, plane.normal );
VectorMA( RI.vup, d, plane.normal, up );
VectorNormalize( up );
VectorsAngles( forward, right, up, angles );
angles[ROLL] = -angles[ROLL];
RI.params = RP_MIRRORVIEW|RP_CLIPPLANE|RP_OLDVIEWLEAF;
RI.clipPlane = plane;
GL_FrustumSetPlane( &RI.frustum, FRUSTUM_NEAR, plane.normal, plane.dist );
RI.viewangles[0] = anglemod( angles[0] );
RI.viewangles[1] = anglemod( angles[1] );
RI.viewangles[2] = anglemod( angles[2] );
VectorCopy( origin, RI.vieworg );
VectorCopy( origin, RI.cullorigin );
// put pvsorigin before the mirror plane to avoid get full visibility on world mirrors
if( RI.currententity == tr.entities )
{
VectorMA( es->origin, 1.0f, plane.normal, origin );
}
else
{
Matrix4x4_VectorTransform( mirrormatrix, es->origin, origin );
VectorMA( origin, 1.0f, plane.normal, origin );
}
VectorCopy( origin, RI.pvsorigin );
if( GL_Support( GL_ARB_TEXTURE_NPOT_EXT ))
{
// allow screen size
RI.viewport[2] = bound( 96, RI.viewport[2], 1024 );
RI.viewport[3] = bound( 72, RI.viewport[3], 768 );
}
else
{
RI.viewport[2] = NearestPOW( RI.viewport[2], true );
RI.viewport[3] = NearestPOW( RI.viewport[3], true );
RI.viewport[2] = bound( 128, RI.viewport[2], 1024 );
RI.viewport[3] = bound( 64, RI.viewport[3], 512 );
}
R_RenderScene();
r_stats.c_mirror_passes++;
es->mirrortexturenum = R_AllocateMirrorTexture();
// create personal projection matrix for mirror
if( VectorIsNull( e->origin ) && VectorIsNull( e->angles ))
{
Matrix4x4_Copy( es->mirrormatrix, RI.worldviewProjectionMatrix );
}
else
{
Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, mirrormatrix );
Matrix4x4_Concat( es->mirrormatrix, RI.projectionMatrix, RI.modelviewMatrix );
}
RI = oldRI; // restore ref instance
}
// clear chain for this entity
for( es = mirrorchain; es != NULL; )
{
tmp = es->mirrorchain;
es->mirrorchain = NULL;
es = tmp;
}
tr.draw_list->mirror_entities[i].chain = NULL; // done
tr.draw_list->mirror_entities[i].ent = NULL;
}
RI.viewleaf = NULL; // force markleafs next frame
tr.draw_list->num_mirror_entities = 0;
tr.num_mirrors_used = 0;
}
/*
================
R_RecursiveMirrorNode
================
*/
static void R_RecursiveMirrorNode( mnode_t *node, uint clipflags )
{
int i, clipped;
msurface_t *surf, **mark;
mleaf_t *pleaf;
int c, side;
float dot;
if( node->contents == CONTENTS_SOLID )
return; // hit a solid leaf
if( node->visframe != tr.visframecount )
return;
if( clipflags )
{
for( i = 0; i < 6; i++ )
{
const mplane_t *p = &RI.frustum.planes[i];
if( !FBitSet( clipflags, BIT( i )))
continue;
clipped = BoxOnPlaneSide( node->minmaxs, node->minmaxs + 3, p );
if( clipped == 2 ) return;
if( clipped == 1 ) ClearBits( clipflags, BIT( i ));
}
}
// if a leaf node, draw stuff
if( node->contents < 0 )
{
pleaf = (mleaf_t *)node;
mark = pleaf->firstmarksurface;
c = pleaf->nummarksurfaces;
if( c )
{
do
{
(*mark)->visframe = tr.framecount;
mark++;
} while( --c );
}
return;
}
// node is just a decision point, so go down the apropriate sides
// find which side of the node we are on
dot = PlaneDiff( tr.modelorg, node->plane );
side = (dot >= 0) ? 0 : 1;
// recurse down the children, front side first
R_RecursiveMirrorNode( node->children[side], clipflags );
// draw stuff
for( c = node->numsurfaces, surf = WORLDMODEL->surfaces + node->firstsurface; c; c--, surf++ )
{
if( !FBitSet( surf->flags, SURF_REFLECT ))
continue;
if( R_CullSurface( surf, &RI.frustum, clipflags ))
continue;
surf->info->mirrorchain = tr.draw_list->mirror_entities[0].chain;
tr.draw_list->mirror_entities[0].chain = surf->info;
}
// recurse down the back side
R_RecursiveMirrorNode( node->children[!side], clipflags );
}
/*
=================
R_FindBmodelMirrors
Check all bmodel surfaces and make personal mirror chain
=================
*/
static void R_FindBmodelMirrors( cl_entity_t *e, qboolean static_entity )
{
vec3_t mins, maxs;
msurface_t *psurf;
model_t *clmodel;
qboolean rotated;
gl_frustum_t *frustum = NULL;
int i;
draw_list_t *const draw_list = tr.draw_list;
if( draw_list->num_mirror_entities >= MAX_MIRROR_ENTITIES )
return;
clmodel = e->model;
// don't draw any water reflections if we underwater
if( ENGINE_GET_PARM( PARM_WATER_LEVEL ) >= 3 && FBitSet( clmodel->flags, MODEL_LIQUID ))
return;
if( static_entity )
{
Matrix4x4_LoadIdentity( RI.objectMatrix );
if( R_CullBox( clmodel->mins, clmodel->maxs ))
return;
VectorCopy( RI.cullorigin, tr.modelorg );
frustum = &RI.frustum;
}
else
{
if( !VectorIsNull( e->angles ))
{
for( i = 0; i < 3; i++ )
{
mins[i] = e->origin[i] - clmodel->radius;
maxs[i] = e->origin[i] + clmodel->radius;
}
rotated = true;
}
else
{
VectorAdd( e->origin, clmodel->mins, mins );
VectorAdd( e->origin, clmodel->maxs, maxs );
rotated = false;
}
if( R_CullBox( mins, maxs ))
return;
if( !VectorIsNull( e->origin ) || !VectorIsNull( e->angles ))
{
if( rotated ) Matrix4x4_CreateFromEntity( RI.objectMatrix, e->angles, e->origin, 1.0f );
else Matrix4x4_CreateFromEntity( RI.objectMatrix, vec3_origin, e->origin, 1.0f );
}
else Matrix4x4_LoadIdentity( RI.objectMatrix );
e->visframe = tr.framecount; // visible
if( rotated ) Matrix4x4_VectorITransform( RI.objectMatrix, RI.cullorigin, tr.modelorg );
else VectorSubtract( RI.cullorigin, e->origin, tr.modelorg );
}
psurf = &clmodel->surfaces[clmodel->firstmodelsurface];
for( i = 0; i < clmodel->nummodelsurfaces; i++, psurf++ )
{
if( !FBitSet( psurf->flags, SURF_REFLECT ))
continue;
if( R_CullSurface( psurf, frustum, 0 ))
continue;
psurf->info->mirrorchain = draw_list->mirror_entities[draw_list->num_mirror_entities].chain;
draw_list->mirror_entities[draw_list->num_mirror_entities].chain = psurf->info;
}
// store new mirror entity
if( !static_entity && draw_list->mirror_entities[draw_list->num_mirror_entities].chain != NULL )
{
draw_list->mirror_entities[draw_list->num_mirror_entities].ent = RI.currententity;
draw_list->num_mirror_entities++;
}
}
/*
=================
R_CheckEntitiesOnList
Check all bmodels for mirror surfaces
=================
*/
static void R_CheckEntitiesOnList( void )
{
int i;
// world has mirror surfaces
if( tr.draw_list->mirror_entities[0].chain != NULL )
{
tr.draw_list->mirror_entities[0].ent = tr.entities;
tr.draw_list->num_mirror_entities++;
}
// check solid entities
for( i = 0; i < tr.draw_list->num_solid_entities; i++ )
{
RI.currententity = tr.draw_list->solid_entities[i];
RI.currentmodel = RI.currententity->model;
ASSERT( RI.currententity != NULL );
ASSERT( RI.currententity->model != NULL );
switch( RI.currentmodel->type )
{
case mod_brush:
R_FindBmodelMirrors( RI.currententity, false );
break;
}
}
// check translucent entities
for( i = 0; i < tr.draw_list->num_trans_entities; i++ )
{
RI.currententity = tr.draw_list->trans_entities[i];
RI.currentmodel = RI.currententity->model;
ASSERT( RI.currententity != NULL );
ASSERT( RI.currententity->model != NULL );
switch( RI.currentmodel->type )
{
case mod_brush:
R_FindBmodelMirrors( RI.currententity, false );
break;
}
}
}
/*
================
R_FindMirrors
Build mirror chains for this frame
================
*/
void R_FindMirrors( void )
{
if( !FBitSet( tr.world->flags, FWORLD_HAS_MIRRORS ) || RI.drawOrtho || !RI.drawWorld || RI.onlyClientDraw || !WORLDMODEL )
return;
// NOTE: we already has initial params at this point like vieworg, viewangles
// all other will be sets into R_SetupFrustum
R_FindViewLeaf ();
// player is outside world. Don't update mirrors for speedup reasons
if(( RI.viewleaf - WORLDMODEL->leafs - 1 ) == -1 )
return;
R_SetupFrustum ();
R_MarkLeaves ();
VectorCopy( RI.cullorigin, tr.modelorg );
RI.currententity = tr.entities;
RI.currentmodel = RI.currententity->model;
R_RecursiveMirrorNode( WORLDMODEL->nodes, RI.frustum.clipFlags );
R_CheckEntitiesOnList();
}

View File

@@ -20,6 +20,7 @@ CVAR_DEFINE_AUTO( gl_test, "0", 0, "engine developer cvar for quick testing new
CVAR_DEFINE_AUTO( gl_msaa, "1", FCVAR_GLCONFIG, "enable or disable multisample anti-aliasing" );
CVAR_DEFINE_AUTO( gl_stencilbits, "8", FCVAR_GLCONFIG|FCVAR_READ_ONLY, "pixelformat stencil bits (0 - auto)" );
CVAR_DEFINE_AUTO( gl_overbright, "1", FCVAR_GLCONFIG, "overbrights" );
CVAR_DEFINE_AUTO( gl_allow_mirrors, "1", FCVAR_GLCONFIG, "allow to draw mirror surfaces" );
CVAR_DEFINE_AUTO( r_lighting_extended, "1", FCVAR_GLCONFIG, "allow to get lighting from world and bmodels" );
CVAR_DEFINE_AUTO( r_lighting_ambient, "0.3", FCVAR_GLCONFIG, "map ambient lighting scale" );
CVAR_DEFINE_AUTO( r_detailtextures, "1", FCVAR_ARCHIVE, "enable detail textures support" );
@@ -1207,6 +1208,7 @@ static void GL_InitCommands( void )
gEngfuncs.Cvar_RegisterVariable( &gl_stencilbits );
gEngfuncs.Cvar_RegisterVariable( &gl_round_down );
gEngfuncs.Cvar_RegisterVariable( &gl_overbright );
gEngfuncs.Cvar_RegisterVariable( &gl_allow_mirrors );
// these cvar not used by engine but some mods requires this
gEngfuncs.Cvar_RegisterVariable( &gl_polyoffset );

View File

@@ -218,6 +218,7 @@ void R_ClearScene( void )
tr.draw_list->num_solid_entities = 0;
tr.draw_list->num_trans_entities = 0;
tr.draw_list->num_beam_entities = 0;
tr.draw_list->num_mirror_entities = 0;
// clear the scene befor start new frame
if( gEngfuncs.drawFuncs->R_ClearScene != NULL )
@@ -1127,6 +1128,14 @@ void R_RenderFrame( const ref_viewpass_t *rvp )
R_RunViewmodelEvents();
tr.realframecount++; // right called after viewmodel events
if( gl_allow_mirrors.value )
{
// render mirrors
R_FindMirrors();
R_DrawMirrors();
}
R_RenderScene();
return;

View File

@@ -1142,7 +1142,7 @@ R_RenderBrushPoly
*/
static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
{
qboolean is_dynamic = false;
qboolean is_dynamic = false, is_mirror = false;
int maps;
texture_t *t;
@@ -1153,15 +1153,46 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
t = R_TextureAnimation( fa );
// set mirror texture
if( RP_NORMALPASS() && FBitSet( fa->flags, SURF_REFLECT ))
{
if( fa->info->mirrortexturenum )
{
GL_Bind( XASH_TEXTURE0, fa->info->mirrortexturenum );
is_mirror = true;
// reset just in case if mirror failed to render
fa->info->mirrortexturenum = 0;
// blend-in water
if( FBitSet( fa->flags, SURF_DRAWTURB ))
{
R_BeginDrawMirror( fa );
R_UploadRipples( t, true );
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
// warp texture, no lightmaps
EmitWaterPolys( fa, (cull_type == CULL_BACKSIDE));
R_EndDrawMirror();
return;
}
}
}
if( FBitSet( fa->flags, SURF_DRAWTURB ))
{
R_UploadRipples( t );
R_UploadRipples( t, false );
// warp texture, no lightmaps
EmitWaterPolys( fa, (cull_type == CULL_BACKSIDE));
return;
}
else GL_Bind( XASH_TEXTURE0, t->gl_texturenum );
else if( !is_mirror ) // already set up mirror texture
{
GL_Bind( XASH_TEXTURE0, t->gl_texturenum );
}
if( t->fb_texturenum )
{
@@ -1199,7 +1230,9 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
}
}
if( is_mirror ) R_BeginDrawMirror( fa );
DrawGLPoly( fa->polys, 0.0f, 0.0f );
if( is_mirror ) R_EndDrawMirror();
if( RI.currententity->curstate.rendermode == kRenderNormal )
{
@@ -1213,6 +1246,10 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
DrawSurfaceDecals( fa, true, (cull_type == CULL_BACKSIDE));
}
// NOTE: draw mirror through in mirror show dummy lightmapped texture
if( RP_NORMALPASS() && FBitSet( fa->flags, SURF_REFLECT ))
return; // no lightmaps for mirror
if( FBitSet( fa->flags, SURF_DRAWTILED ))
return; // no lightmaps anyway
@@ -1432,7 +1469,7 @@ void R_DrawWaterSurfaces( void )
continue;
// set modulate mode explicitly
R_UploadRipples( t );
R_UploadRipples( t, false );
for( ; s; s = s->texturechain )
EmitWaterPolys( s, false );
@@ -3824,7 +3861,14 @@ void GL_BuildLightmaps( void )
GL_FreeTexture( tr.lightmapTextures[i] );
}
for( i = 0; i < MAX_MIRRORS; i++ )
{
if( !tr.mirrorTextures[i] ) break;
GL_FreeTexture( tr.mirrorTextures[i] );
}
memset( tr.lightmapTextures, 0, sizeof( tr.lightmapTextures ));
memset( tr.mirrorTextures, 0, sizeof( tr.mirrorTextures ));
memset( &RI, 0, sizeof( RI ));
// update the lightmap blocksize
@@ -3840,6 +3884,8 @@ void GL_BuildLightmaps( void )
tr.realframecount = 1;
nColinElim = 0;
tr.num_mirrors_used = 0;
// setup the texture for dlights
R_InitDlightTexture();

View File

@@ -702,7 +702,7 @@ static qboolean R_SpriteOccluded( cl_entity_t *e, vec3_t origin, float *pscale )
vec3_t v;
if( R_CullEntityInMirror( e ))
return;
return true;
TriWorldToScreen( origin, v );

View File

@@ -963,28 +963,29 @@ void R_AnimateRipples( void )
R_RunRipplesAnimation( g_ripple.oldbuf, g_ripple.curbuf );
}
void R_UploadRipples( texture_t *image )
void R_UploadRipples( texture_t *image, qboolean is_mirror )
{
gl_texture_t *glt;
uint32_t *pixels;
int wbits, wmask, wshft;
int y;
int tmu = is_mirror ? XASH_TEXTURE1 : XASH_TEXTURE0;
// discard unuseful textures
if( !r_ripple.value || image->width > RIPPLES_CACHEWIDTH || image->width != image->height )
{
GL_Bind( XASH_TEXTURE0, image->gl_texturenum );
GL_Bind( tmu, image->gl_texturenum );
return;
}
glt = R_GetTexture( image->gl_texturenum );
if( !glt || !glt->original || !glt->original->buffer || !FBitSet( glt->flags, TF_EXPAND_SOURCE ))
{
GL_Bind( XASH_TEXTURE0, image->gl_texturenum );
GL_Bind( tmu, image->gl_texturenum );
return;
}
GL_Bind( XASH_TEXTURE0, g_ripple.rippletexturenum );
GL_Bind( tmu, g_ripple.rippletexturenum );
// no updates this frame
if( !g_ripple.update && image->gl_texturenum == g_ripple.gl_texturenum )