diff --git a/ref/gl/gl_backend.c b/ref/gl/gl_backend.c index 68247f78..17f9a1a3 100644 --- a/ref/gl/gl_backend.c +++ b/ref/gl/gl_backend.c @@ -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 )); diff --git a/ref/gl/gl_local.h b/ref/gl/gl_local.h index f1ac35d5..35c98797 100644 --- a/ref/gl/gl_local.h +++ b/ref/gl/gl_local.h @@ -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; diff --git a/ref/gl/gl_mirror.c b/ref/gl/gl_mirror.c new file mode 100644 index 00000000..9b904f36 --- /dev/null +++ b/ref/gl/gl_mirror.c @@ -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(); +} diff --git a/ref/gl/gl_opengl.c b/ref/gl/gl_opengl.c index 7e4a18fa..d0be3cd1 100644 --- a/ref/gl/gl_opengl.c +++ b/ref/gl/gl_opengl.c @@ -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 ); diff --git a/ref/gl/gl_rmain.c b/ref/gl/gl_rmain.c index 62850504..56ee88b4 100644 --- a/ref/gl/gl_rmain.c +++ b/ref/gl/gl_rmain.c @@ -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; diff --git a/ref/gl/gl_rsurf.c b/ref/gl/gl_rsurf.c index 3f258f13..1eb93d16 100644 --- a/ref/gl/gl_rsurf.c +++ b/ref/gl/gl_rsurf.c @@ -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(); diff --git a/ref/gl/gl_sprite.c b/ref/gl/gl_sprite.c index e26fdd62..e5b9a097 100644 --- a/ref/gl/gl_sprite.c +++ b/ref/gl/gl_sprite.c @@ -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 ); diff --git a/ref/gl/gl_warp.c b/ref/gl/gl_warp.c index 146937bf..bff62d6e 100644 --- a/ref/gl/gl_warp.c +++ b/ref/gl/gl_warp.c @@ -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 )