/* gl_rmain.c - renderer main loop 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 "r_local.h" #include "mathlib.h" #include "library.h" //#include "beamdef.h" //#include "particledef.h" #include "entity_types.h" #include "mod_local.h" #define IsLiquidContents( cnt ) ( cnt == CONTENTS_WATER || cnt == CONTENTS_SLIME || cnt == CONTENTS_LAVA ) float gldepthmin, gldepthmax; ref_instance_t RI; // quake defines. will be refactored // view origin // vec3_t vup, base_vup; vec3_t vpn, base_vpn; vec3_t vright, base_vright; vec3_t r_origin; // // screen size info // float xcenter, ycenter; float xscale, yscale; float xscaleinv, yscaleinv; float xscaleshrink, yscaleshrink; float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; int r_screenwidth; float verticalFieldOfView; float xOrigin, yOrigin; mplane_t screenedge[4]; // // refresh flags // int r_framecount = 1; // so frame counts initialized to 0 don't match int r_visframecount; int d_spanpixcount; int r_polycount; int r_drawnpolycount; int r_wholepolycount; int *pfrustum_indexes[4]; int r_frustum_indexes[4*6]; mleaf_t *r_viewleaf; int r_viewcluster, r_oldviewcluster; cvar_t *r_lefthand; cvar_t *sw_aliasstats; cvar_t *sw_allow_modex; cvar_t *sw_clearcolor; cvar_t *sw_drawflat; cvar_t *sw_draworder; cvar_t *sw_maxedges; cvar_t *sw_maxsurfs; cvar_t *sw_mode; cvar_t *sw_reportedgeout; cvar_t *sw_reportsurfout; cvar_t *sw_stipplealpha; cvar_t *sw_surfcacheoverride; cvar_t *sw_waterwarp; cvar_t *sw_texfilt; cvar_t *r_drawworld; cvar_t *r_drawentities; cvar_t *r_dspeeds; cvar_t *r_fullbright; cvar_t *r_lerpmodels; cvar_t *r_novis; cvar_t *r_speeds; cvar_t *r_lightlevel; //FIXME HACK cvar_t *vid_fullscreen; cvar_t *vid_gamma; //PGM cvar_t *sw_lockpvs; //PGM mleaf_t *r_viewleaf; int r_viewcluster, r_oldviewcluster; float d_sdivzstepu, d_tdivzstepu, d_zistepu; float d_sdivzstepv, d_tdivzstepv, d_zistepv; float d_sdivzorigin, d_tdivzorigin, d_ziorigin; fixed16_t sadjust, tadjust, bbextents, bbextentt; pixel_t *cacheblock; int cachewidth; pixel_t *d_viewbuffer; short *d_pzbuffer; unsigned int d_zrowbytes; unsigned int d_zwidth; qboolean r_dowarp; mvertex_t *r_pcurrentvertbase; int c_surf; int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; qboolean r_surfsonstack; int r_clipflags; byte r_warpbuffer[WARP_WIDTH * WARP_HEIGHT]; int r_numallocatededges; /* ================ R_ConcatRotations ================ */ void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]) { out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; } static int R_RankForRenderMode( int rendermode ) { switch( rendermode ) { case kRenderTransTexture: return 1; // draw second case kRenderTransAdd: return 2; // draw third case kRenderGlow: return 3; // must be last! } return 0; } /* ================ R_GetEntityRenderMode check for texture flags ================ */ int R_GetEntityRenderMode( cl_entity_t *ent ) { int i, opaque, trans; mstudiotexture_t *ptexture; cl_entity_t *oldent; model_t *model; studiohdr_t *phdr; oldent = RI.currententity; RI.currententity = ent; return ent->curstate.rendermode; } void R_AllowFog( qboolean allowed ) { } /* =============== R_OpaqueEntity Opaque entity can be brush or studio model but sprite =============== */ static qboolean R_OpaqueEntity( cl_entity_t *ent ) { if( R_GetEntityRenderMode( ent ) == kRenderNormal ) return true; return false; } /* =============== R_TransEntityCompare Sorting translucent entities by rendermode then by distance =============== */ static int R_TransEntityCompare( const cl_entity_t **a, const cl_entity_t **b ) { cl_entity_t *ent1, *ent2; vec3_t vecLen, org; float dist1, dist2; int rendermode1; int rendermode2; ent1 = (cl_entity_t *)*a; ent2 = (cl_entity_t *)*b; rendermode1 = R_GetEntityRenderMode( ent1 ); rendermode2 = R_GetEntityRenderMode( ent2 ); // sort by distance if( ent1->model->type != mod_brush || rendermode1 != kRenderTransAlpha ) { VectorAverage( ent1->model->mins, ent1->model->maxs, org ); VectorAdd( ent1->origin, org, org ); VectorSubtract( RI.vieworg, org, vecLen ); dist1 = DotProduct( vecLen, vecLen ); } else dist1 = 1000000000; if( ent2->model->type != mod_brush || rendermode2 != kRenderTransAlpha ) { VectorAverage( ent2->model->mins, ent2->model->maxs, org ); VectorAdd( ent2->origin, org, org ); VectorSubtract( RI.vieworg, org, vecLen ); dist2 = DotProduct( vecLen, vecLen ); } else dist2 = 1000000000; if( dist1 > dist2 ) return -1; if( dist1 < dist2 ) return 1; // then sort by rendermode if( R_RankForRenderMode( rendermode1 ) > R_RankForRenderMode( rendermode2 )) return 1; if( R_RankForRenderMode( rendermode1 ) < R_RankForRenderMode( rendermode2 )) return -1; return 0; } #if 0 /* =============== R_WorldToScreen Convert a given point from world into screen space Returns true if we behind to screen =============== */ int R_WorldToScreen( const vec3_t point, vec3_t screen ) { matrix4x4 worldToScreen; qboolean behind; float w; if( !point || !screen ) return true; Matrix4x4_Copy( worldToScreen, RI.worldviewProjectionMatrix ); screen[0] = worldToScreen[0][0] * point[0] + worldToScreen[0][1] * point[1] + worldToScreen[0][2] * point[2] + worldToScreen[0][3]; screen[1] = worldToScreen[1][0] * point[0] + worldToScreen[1][1] * point[1] + worldToScreen[1][2] * point[2] + worldToScreen[1][3]; w = worldToScreen[3][0] * point[0] + worldToScreen[3][1] * point[1] + worldToScreen[3][2] * point[2] + worldToScreen[3][3]; screen[2] = 0.0f; // just so we have something valid here if( w < 0.001f ) { screen[0] *= 100000; screen[1] *= 100000; behind = true; } else { float invw = 1.0f / w; screen[0] *= invw; screen[1] *= invw; behind = false; } return behind; } /* =============== R_ScreenToWorld Convert a given point from screen into world space =============== */ void R_ScreenToWorld( const vec3_t screen, vec3_t point ) { matrix4x4 screenToWorld; float w; if( !point || !screen ) return; Matrix4x4_Invert_Full( screenToWorld, RI.worldviewProjectionMatrix ); point[0] = screen[0] * screenToWorld[0][0] + screen[1] * screenToWorld[0][1] + screen[2] * screenToWorld[0][2] + screenToWorld[0][3]; point[1] = screen[0] * screenToWorld[1][0] + screen[1] * screenToWorld[1][1] + screen[2] * screenToWorld[1][2] + screenToWorld[1][3]; point[2] = screen[0] * screenToWorld[2][0] + screen[1] * screenToWorld[2][1] + screen[2] * screenToWorld[2][2] + screenToWorld[2][3]; w = screen[0] * screenToWorld[3][0] + screen[1] * screenToWorld[3][1] + screen[2] * screenToWorld[3][2] + screenToWorld[3][3]; if( w != 0.0f ) VectorScale( point, ( 1.0f / w ), point ); } #endif /* =============== R_PushScene =============== */ void R_PushScene( void ) { if( ++tr.draw_stack_pos >= MAX_DRAW_STACK ) gEngfuncs.Host_Error( "draw stack overflow\n" ); tr.draw_list = &tr.draw_stack[tr.draw_stack_pos]; } /* =============== R_PopScene =============== */ void R_PopScene( void ) { if( --tr.draw_stack_pos < 0 ) gEngfuncs.Host_Error( "draw stack underflow\n" ); tr.draw_list = &tr.draw_stack[tr.draw_stack_pos]; } /* =============== R_ClearScene =============== */ 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; // clear the scene befor start new frame if( gEngfuncs.drawFuncs->R_ClearScene != NULL ) gEngfuncs.drawFuncs->R_ClearScene(); } /* =============== R_AddEntity =============== */ qboolean R_AddEntity( struct cl_entity_s *clent, int type ) { if( !r_drawentities->value ) return false; // not allow to drawing if( !clent || !clent->model ) return false; // if set to invisible, skip if( FBitSet( clent->curstate.effects, EF_NODRAW )) return false; // done if( !R_ModelOpaque( clent->curstate.rendermode ) && gEngfuncs.CL_FxBlend( clent ) <= 0 ) return true; // invisible if( type == ET_FRAGMENTED ) r_stats.c_client_ents++; if( R_OpaqueEntity( clent )) { // opaque if( tr.draw_list->num_solid_entities >= MAX_VISIBLE_PACKET ) return false; tr.draw_list->solid_entities[tr.draw_list->num_solid_entities] = clent; tr.draw_list->num_solid_entities++; } else { // translucent if( tr.draw_list->num_trans_entities >= MAX_VISIBLE_PACKET ) return false; tr.draw_list->trans_entities[tr.draw_list->num_trans_entities] = clent; tr.draw_list->num_trans_entities++; } return true; } /* ============= R_Clear ============= */ static void R_Clear( int bitMask ) { int bits; #if 0 if( gEngfuncs.CL_IsDevOverviewMode( )) pglClearColor( 0.0f, 1.0f, 0.0f, 1.0f ); // green background (Valve rules) else pglClearColor( 0.5f, 0.5f, 0.5f, 1.0f ); bits = GL_DEPTH_BUFFER_BIT; if( glState.stencilEnabled ) bits |= GL_STENCIL_BUFFER_BIT; bits &= bitMask; pglClear( bits ); // change ordering for overview if( RI.drawOrtho ) { gldepthmin = 1.0f; gldepthmax = 0.0f; } else { gldepthmin = 0.0f; gldepthmax = 1.0f; } pglDepthFunc( GL_LEQUAL ); pglDepthRange( gldepthmin, gldepthmax ); #endif memset( vid.buffer, 0, 1920*1080*2); } //============================================================================= /* =============== R_GetFarClip =============== */ static float R_GetFarClip( void ) { if( WORLDMODEL && RI.drawWorld ) return MOVEVARS->zmax * 1.73f; return 2048.0f; } /* =============== R_SetupFrustum =============== */ void R_SetupFrustum( void ) { #if 0 ref_overview_t *ov = gEngfuncs.GetOverviewParms(); if( RP_NORMALPASS() && ( gEngfuncs.GetWaterLevel() >= 3 )) { RI.fov_x = atan( tan( DEG2RAD( RI.fov_x ) / 2 ) * ( 0.97 + sin( gpGlobals->time * 1.5 ) * 0.03 )) * 2 / (M_PI / 180.0); RI.fov_y = atan( tan( DEG2RAD( RI.fov_y ) / 2 ) * ( 1.03 - sin( gpGlobals->time * 1.5 ) * 0.03 )) * 2 / (M_PI / 180.0); } // build the transformation matrix for the given view angles AngleVectors( RI.viewangles, RI.vforward, RI.vright, RI.vup ); if( !r_lockfrustum->value ) { VectorCopy( RI.vieworg, RI.cullorigin ); VectorCopy( RI.vforward, RI.cull_vforward ); VectorCopy( RI.vright, RI.cull_vright ); VectorCopy( RI.vup, RI.cull_vup ); } if( RI.drawOrtho ) GL_FrustumInitOrtho( &RI.frustum, ov->xLeft, ov->xRight, ov->yTop, ov->yBottom, ov->zNear, ov->zFar ); else GL_FrustumInitProj( &RI.frustum, 0.0f, R_GetFarClip(), RI.fov_x, RI.fov_y ); // NOTE: we ignore nearplane here (mirrors only) #endif } /* ============= R_SetupProjectionMatrix ============= */ static void R_SetupProjectionMatrix( matrix4x4 m ) { #if 0 GLdouble xMin, xMax, yMin, yMax, zNear, zFar; if( RI.drawOrtho ) { ref_overview_t *ov = gEngfuncs.GetOverviewParms(); Matrix4x4_CreateOrtho( m, ov->xLeft, ov->xRight, ov->yTop, ov->yBottom, ov->zNear, ov->zFar ); return; } RI.farClip = R_GetFarClip(); zNear = 4.0f; zFar = max( 256.0f, RI.farClip ); yMax = zNear * tan( RI.fov_y * M_PI / 360.0 ); yMin = -yMax; xMax = zNear * tan( RI.fov_x * M_PI / 360.0 ); xMin = -xMax; Matrix4x4_CreateProjection( m, xMax, xMin, yMax, yMin, zNear, zFar ); #endif } /* ============= R_SetupModelviewMatrix ============= */ static void R_SetupModelviewMatrix( matrix4x4 m ) { #if 0 Matrix4x4_CreateModelview( m ); Matrix4x4_ConcatRotate( m, -RI.viewangles[2], 1, 0, 0 ); Matrix4x4_ConcatRotate( m, -RI.viewangles[0], 0, 1, 0 ); Matrix4x4_ConcatRotate( m, -RI.viewangles[1], 0, 0, 1 ); Matrix4x4_ConcatTranslate( m, -RI.vieworg[0], -RI.vieworg[1], -RI.vieworg[2] ); #endif } /* ============= R_LoadIdentity ============= */ void R_LoadIdentity( void ) { #if 0 if( tr.modelviewIdentity ) return; Matrix4x4_LoadIdentity( RI.objectMatrix ); Matrix4x4_Copy( RI.modelviewMatrix, RI.worldviewMatrix ); pglMatrixMode( GL_MODELVIEW ); GL_LoadMatrix( RI.modelviewMatrix ); tr.modelviewIdentity = true; #endif } /* ============= R_RotateForEntity ============= */ void R_RotateForEntity( cl_entity_t *e ) { #if 0 float scale = 1.0f; if( e == gEngfuncs.GetEntityByIndex( 0 ) ) { R_LoadIdentity(); return; } if( e->model->type != mod_brush && e->curstate.scale > 0.0f ) scale = e->curstate.scale; Matrix4x4_CreateFromEntity( RI.objectMatrix, e->angles, e->origin, scale ); Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, RI.objectMatrix ); pglMatrixMode( GL_MODELVIEW ); GL_LoadMatrix( RI.modelviewMatrix ); tr.modelviewIdentity = false; #endif } /* ============= R_TranslateForEntity ============= */ void R_TranslateForEntity( cl_entity_t *e ) { #if 0 float scale = 1.0f; if( e == gEngfuncs.GetEntityByIndex( 0 ) ) { R_LoadIdentity(); return; } if( e->model->type != mod_brush && e->curstate.scale > 0.0f ) scale = e->curstate.scale; Matrix4x4_CreateFromEntity( RI.objectMatrix, vec3_origin, e->origin, scale ); Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, RI.objectMatrix ); pglMatrixMode( GL_MODELVIEW ); GL_LoadMatrix( RI.modelviewMatrix ); tr.modelviewIdentity = false; #endif } /* =============== R_FindViewLeaf =============== */ void R_FindViewLeaf( void ) { RI.oldviewleaf = RI.viewleaf; RI.viewleaf = gEngfuncs.Mod_PointInLeaf( RI.pvsorigin, WORLDMODEL->nodes ); } /* =============== R_SetupFrame =============== */ static void R_SetupFrame( void ) { // setup viewplane dist RI.viewplanedist = DotProduct( RI.vieworg, RI.vforward ); // if( !gl_nosort->value ) { // sort translucents entities by rendermode and distance qsort( tr.draw_list->trans_entities, tr.draw_list->num_trans_entities, sizeof( cl_entity_t* ), R_TransEntityCompare ); } // current viewleaf if( RI.drawWorld ) { RI.isSkyVisible = false; // unknown at this moment R_FindViewLeaf(); } // setup twice until globals fully refactored R_SetupFrameQ(); } #if 0 /* ============= R_SetupGL ============= */ void R_SetupGL( qboolean set_gl_state ) { R_SetupModelviewMatrix( RI.worldviewMatrix ); R_SetupProjectionMatrix( RI.projectionMatrix ); Matrix4x4_Concat( RI.worldviewProjectionMatrix, RI.projectionMatrix, RI.worldviewMatrix ); if( !set_gl_state ) return; if( RP_NORMALPASS( )) { int x, x2, y, y2; // set up viewport (main, playersetup) x = floor( RI.viewport[0] * gpGlobals->width / gpGlobals->width ); x2 = ceil(( RI.viewport[0] + RI.viewport[2] ) * gpGlobals->width / gpGlobals->width ); y = floor( gpGlobals->height - RI.viewport[1] * gpGlobals->height / gpGlobals->height ); y2 = ceil( gpGlobals->height - ( RI.viewport[1] + RI.viewport[3] ) * gpGlobals->height / gpGlobals->height ); pglViewport( x, y2, x2 - x, y - y2 ); } else { // envpass, mirrorpass pglViewport( RI.viewport[0], RI.viewport[1], RI.viewport[2], RI.viewport[3] ); } pglMatrixMode( GL_PROJECTION ); GL_LoadMatrix( RI.projectionMatrix ); pglMatrixMode( GL_MODELVIEW ); GL_LoadMatrix( RI.worldviewMatrix ); if( FBitSet( RI.params, RP_CLIPPLANE )) { GLdouble clip[4]; mplane_t *p = &RI.clipPlane; clip[0] = p->normal[0]; clip[1] = p->normal[1]; clip[2] = p->normal[2]; clip[3] = -p->dist; pglClipPlane( GL_CLIP_PLANE0, clip ); pglEnable( GL_CLIP_PLANE0 ); } GL_Cull( GL_FRONT ); pglDisable( GL_BLEND ); pglDisable( GL_ALPHA_TEST ); pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); } /* ============= R_EndGL ============= */ static void R_EndGL( void ) { if( RI.params & RP_CLIPPLANE ) pglDisable( GL_CLIP_PLANE0 ); } #endif /* ============= R_RecursiveFindWaterTexture using to find source waterleaf with watertexture to grab fog values from it ============= */ static image_t *R_RecursiveFindWaterTexture( const mnode_t *node, const mnode_t *ignore, qboolean down ) { image_t *tex = NULL; // assure the initial node is not null // we could check it here, but we would rather check it // outside the call to get rid of one additional recursion level Assert( node != NULL ); // ignore solid nodes if( node->contents == CONTENTS_SOLID ) return NULL; if( node->contents < 0 ) { mleaf_t *pleaf; msurface_t **mark; int i, c; // ignore non-liquid leaves if( node->contents != CONTENTS_WATER && node->contents != CONTENTS_LAVA && node->contents != CONTENTS_SLIME ) return NULL; // find texture pleaf = (mleaf_t *)node; mark = pleaf->firstmarksurface; c = pleaf->nummarksurfaces; for( i = 0; i < c; i++, mark++ ) { if( (*mark)->flags & SURF_DRAWTURB && (*mark)->texinfo && (*mark)->texinfo->texture ) return R_GetTexture( (*mark)->texinfo->texture->gl_texturenum ); } // texture not found return NULL; } // this is a regular node // traverse children if( node->children[0] && ( node->children[0] != ignore )) { tex = R_RecursiveFindWaterTexture( node->children[0], node, true ); if( tex ) return tex; } if( node->children[1] && ( node->children[1] != ignore )) { tex = R_RecursiveFindWaterTexture( node->children[1], node, true ); if( tex ) return tex; } // for down recursion, return immediately if( down ) return NULL; // texture not found, step up if any if( node->parent ) return R_RecursiveFindWaterTexture( node->parent, node, false ); // top-level node, bail out return NULL; } /* ============= R_CheckFog check for underwater fog Using backward recursion to find waterline leaf from underwater leaf (idea: XaeroX) ============= */ static void R_CheckFog( void ) { cl_entity_t *ent; image_t *tex; int i, cnt, count; // quake global fog if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE )) { if( !MOVEVARS->fog_settings ) { // if( pglIsEnabled( GL_FOG )) // pglDisable( GL_FOG ); RI.fogEnabled = false; return; } // quake-style global fog RI.fogColor[0] = ((MOVEVARS->fog_settings & 0xFF000000) >> 24) / 255.0f; RI.fogColor[1] = ((MOVEVARS->fog_settings & 0xFF0000) >> 16) / 255.0f; RI.fogColor[2] = ((MOVEVARS->fog_settings & 0xFF00) >> 8) / 255.0f; RI.fogDensity = ((MOVEVARS->fog_settings & 0xFF) / 255.0f) * 0.01f; RI.fogStart = RI.fogEnd = 0.0f; RI.fogColor[3] = 1.0f; RI.fogCustom = false; RI.fogEnabled = true; RI.fogSkybox = true; return; } RI.fogEnabled = false; if( RI.onlyClientDraw || ENGINE_GET_PARM( PARM_WATER_LEVEL ) < 3 || !RI.drawWorld || !RI.viewleaf ) { if( RI.cached_waterlevel == 3 ) { // in some cases waterlevel jumps from 3 to 1. Catch it RI.cached_waterlevel = ENGINE_GET_PARM( PARM_WATER_LEVEL ); RI.cached_contents = CONTENTS_EMPTY; //if( !RI.fogCustom ) pglDisable( GL_FOG ); } return; } ent = gEngfuncs.CL_GetWaterEntity( RI.vieworg ); if( ent && ent->model && ent->model->type == mod_brush && ent->curstate.skin < 0 ) cnt = ent->curstate.skin; else cnt = RI.viewleaf->contents; RI.cached_waterlevel = ENGINE_GET_PARM( PARM_WATER_LEVEL ); if( !IsLiquidContents( RI.cached_contents ) && IsLiquidContents( cnt )) { tex = NULL; // check for water texture if( ent && ent->model && ent->model->type == mod_brush ) { msurface_t *surf; count = ent->model->nummodelsurfaces; for( i = 0, surf = &ent->model->surfaces[ent->model->firstmodelsurface]; i < count; i++, surf++ ) { if( surf->flags & SURF_DRAWTURB && surf->texinfo && surf->texinfo->texture ) { tex = R_GetTexture( surf->texinfo->texture->gl_texturenum ); RI.cached_contents = ent->curstate.skin; break; } } } else { tex = R_RecursiveFindWaterTexture( RI.viewleaf->parent, NULL, false ); if( tex ) RI.cached_contents = RI.viewleaf->contents; } if( !tex ) return; // no valid fogs // copy fog params RI.fogColor[0] = tex->fogParams[0] / 255.0f; RI.fogColor[1] = tex->fogParams[1] / 255.0f; RI.fogColor[2] = tex->fogParams[2] / 255.0f; RI.fogDensity = tex->fogParams[3] * 0.000025f; RI.fogStart = RI.fogEnd = 0.0f; RI.fogColor[3] = 1.0f; RI.fogCustom = false; RI.fogEnabled = true; RI.fogSkybox = true; } else { RI.fogCustom = false; RI.fogEnabled = true; RI.fogSkybox = true; } } /* ============= R_CheckGLFog special condition for Spirit 1.9 that used direct calls of glFog-functions ============= */ static void R_CheckGLFog( void ) { #ifdef HACKS_RELATED_HLMODS if(( !RI.fogEnabled && !RI.fogCustom ) && pglIsEnabled( GL_FOG ) && VectorIsNull( RI.fogColor )) { // fill the fog color from GL-state machine pglGetFloatv( GL_FOG_COLOR, RI.fogColor ); RI.fogSkybox = true; } #endif } /* ============= R_DrawFog ============= */ void R_DrawFog( void ) { if( !RI.fogEnabled ) return; #if 0 pglEnable( GL_FOG ); if( gEngfuncs.Host_IsQuakeCompatible( )) pglFogi( GL_FOG_MODE, GL_EXP2 ); else pglFogi( GL_FOG_MODE, GL_EXP ); pglFogf( GL_FOG_DENSITY, RI.fogDensity ); pglFogfv( GL_FOG_COLOR, RI.fogColor ); pglHint( GL_FOG_HINT, GL_NICEST ); #endif } /* ============= R_DrawEntitiesOnList ============= */ void R_DrawEntitiesOnList( void ) { int i; tr.blend = 1.0f; // GL_CheckForErrors(); // first draw solid entities for( i = 0; i < tr.draw_list->num_solid_entities && !RI.onlyClientDraw; i++ ) { RI.currententity = tr.draw_list->solid_entities[i]; RI.currentmodel = RI.currententity->model; Assert( RI.currententity != NULL ); Assert( RI.currentmodel != NULL ); switch( RI.currentmodel->type ) { case mod_brush: //R_DrawBrushModel( RI.currententity ); break; case mod_alias: //R_DrawAliasModel( RI.currententity ); break; case mod_studio: //R_DrawStudioModel( RI.currententity ); break; default: break; } } // GL_CheckForErrors(); // quake-specific feature // R_DrawAlphaTextureChains(); // GL_CheckForErrors(); // draw sprites seperately, because of alpha blending for( i = 0; i < tr.draw_list->num_solid_entities && !RI.onlyClientDraw; i++ ) { RI.currententity = tr.draw_list->solid_entities[i]; RI.currentmodel = RI.currententity->model; Assert( RI.currententity != NULL ); Assert( RI.currentmodel != NULL ); switch( RI.currentmodel->type ) { case mod_sprite: // R_DrawSpriteModel( RI.currententity ); break; } } // GL_CheckForErrors(); if( !RI.onlyClientDraw ) { gEngfuncs.CL_DrawEFX( tr.frametime, false ); } // GL_CheckForErrors(); if( RI.drawWorld ) gEngfuncs.pfnDrawNormalTriangles(); // GL_CheckForErrors(); // then draw translucent entities for( i = 0; i < tr.draw_list->num_trans_entities && !RI.onlyClientDraw; i++ ) { RI.currententity = tr.draw_list->trans_entities[i]; RI.currentmodel = RI.currententity->model; // handle studiomodels with custom rendermodes on texture if( RI.currententity->curstate.rendermode != kRenderNormal ) tr.blend = gEngfuncs.CL_FxBlend( RI.currententity ) / 255.0f; else tr.blend = 1.0f; // draw as solid but sorted by distance if( tr.blend <= 0.0f ) continue; Assert( RI.currententity != NULL ); Assert( RI.currentmodel != NULL ); switch( RI.currentmodel->type ) { case mod_brush: // R_DrawBrushModel( RI.currententity ); break; case mod_alias: //R_DrawAliasModel( RI.currententity ); break; case mod_studio: // R_DrawStudioModel( RI.currententity ); break; case mod_sprite: // R_DrawSpriteModel( RI.currententity ); break; default: break; } } // GL_CheckForErrors(); if( RI.drawWorld ) { // pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); gEngfuncs.pfnDrawTransparentTriangles (); } // GL_CheckForErrors(); if( !RI.onlyClientDraw ) { R_AllowFog( false ); gEngfuncs.CL_DrawEFX( tr.frametime, true ); R_AllowFog( true ); } //GL_CheckForErrors(); // pglDisable( GL_BLEND ); // Trinity Render issues //if( !RI.onlyClientDraw ) //R_DrawViewModel(); gEngfuncs.CL_ExtraUpdate(); //GL_CheckForErrors(); } #if 0 /* ============= R_DrawBEntitiesOnList ============= */ void R_DrawBEntitiesOnList (void) { int i, clipflags; vec3_t oldorigin; vec3_t mins, maxs; float minmaxs[6]; mnode_t *topnode; if (!r_drawentities->value) return; VectorCopy (modelorg, oldorigin); insubmodel = true; r_dlightframecount = r_framecount; for (i=0 ; imodel; if (!currentmodel) continue; if (currentmodel->nummodelsurfaces == 0) continue; // clip brush only if ( currententity->flags & RF_BEAM ) continue; if (currentmodel->type != mod_brush) continue; // see if the bounding box lets us trivially reject, also sets // trivial accept status RotatedBBox (currentmodel->mins, currentmodel->maxs, currententity->angles, mins, maxs); VectorAdd (mins, currententity->origin, minmaxs); VectorAdd (maxs, currententity->origin, (minmaxs+3)); clipflags = R_BmodelCheckBBox (minmaxs); if (clipflags == BMODEL_FULLY_CLIPPED) continue; // off the edge of the screen topnode = R_FindTopnode (minmaxs, minmaxs+3); if (!topnode) continue; // no part in a visible leaf VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); r_pcurrentvertbase = currentmodel->vertexes; // FIXME: stop transforming twice R_RotateBmodel (); // calculate dynamic lighting for bmodel R_PushDlights (currentmodel); if (topnode->contents == CONTENTS_NODE) { // not a leaf; has to be clipped to the world BSP r_clipflags = clipflags; R_DrawSolidClippedSubmodelPolygons (currentmodel, topnode); } else { // falls entirely in one leaf, so we just put all the // edges in the edge list and let 1/z sorting handle // drawing order R_DrawSubmodelPolygons (currentmodel, clipflags, topnode); } // put back world rotation and frustum clipping // FIXME: R_RotateBmodel should just work off base_vxx VectorCopy (base_vpn, vpn); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); VectorCopy (oldorigin, modelorg); R_TransformFrustum (); } insubmodel = false; } #endif /* ================ R_EdgeDrawing ================ */ void R_EdgeDrawing (void) { edge_t ledges[NUMSTACKEDGES + ((CACHE_SIZE - 1) / sizeof(edge_t)) + 1]; surf_t lsurfs[NUMSTACKSURFACES + ((CACHE_SIZE - 1) / sizeof(surf_t)) + 1]; if ( !RI.drawWorld ) return; if (auxedges) { r_edges = auxedges; } else { r_edges = (edge_t *) (((long)&ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); } if (r_surfsonstack) { surfaces = (surf_t *) (((long)&lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); surf_max = &surfaces[r_cnumsurfs]; // surface 0 doesn't really exist; it's just a dummy because index 0 // is used to indicate no edge attached to surface surfaces--; R_SurfacePatch (); } R_BeginEdgeFrame (); // this will prepare edges R_RenderWorld (); // move brushes to separate list to merge with edges? //R_DrawBEntitiesOnList (); // display all edges R_ScanEdges (); } #if 0 /* =============== R_MarkLeaves Mark the leaves and nodes that are in the PVS for the current leaf =============== */ void R_MarkLeaves( void ) { qboolean novis = false; qboolean force = false; mleaf_t *leaf = NULL; mnode_t *node; vec3_t test; int i; if( !RI.drawWorld ) return; /*if( FBitSet( r_novis->flags, FCVAR_CHANGED ) || tr.fResetVis ) { // force recalc viewleaf ClearBits( r_novis->flags, FCVAR_CHANGED ); tr.fResetVis = false; RI.viewleaf = NULL; }*/ VectorCopy( RI.pvsorigin, test ); if( RI.viewleaf != NULL ) { // merge two leafs that can be a crossed-line contents if( RI.viewleaf->contents == CONTENTS_EMPTY ) { VectorSet( test, RI.pvsorigin[0], RI.pvsorigin[1], RI.pvsorigin[2] - 16.0f ); leaf = gEngfuncs.Mod_PointInLeaf( test, WORLDMODEL->nodes ); } else { VectorSet( test, RI.pvsorigin[0], RI.pvsorigin[1], RI.pvsorigin[2] + 16.0f ); leaf = gEngfuncs.Mod_PointInLeaf( test, WORLDMODEL->nodes ); } if(( leaf->contents != CONTENTS_SOLID ) && ( RI.viewleaf != leaf )) force = true; } if( RI.viewleaf == RI.oldviewleaf && RI.viewleaf != NULL && !force ) return; // development aid to let you run around // and see exactly where the pvs ends //if( sw_lockpvs->value ) return; RI.oldviewleaf = RI.viewleaf; tr.visframecount++; if( RI.drawOrtho || !RI.viewleaf || !WORLDMODEL->visdata ) novis = true; gEngfuncs.R_FatPVS( RI.pvsorigin, REFPVS_RADIUS, RI.visbytes, FBitSet( RI.params, RP_OLDVIEWLEAF ), novis ); if( force && !novis ) gEngfuncs.R_FatPVS( test, REFPVS_RADIUS, RI.visbytes, true, novis ); for( i = 0; i < WORLDMODEL->numleafs; i++ ) { if( CHECKVISBIT( RI.visbytes, i )) { node = (mnode_t *)&WORLDMODEL->leafs[i+1]; do { if( node->visframe == tr.visframecount ) break; node->visframe = tr.visframecount; node = node->parent; } while( node ); } } } #else /* =============== R_MarkLeaves =============== */ void R_MarkLeaves (void) { byte *vis; mnode_t *node; int i; if (r_oldviewcluster == r_viewcluster && !r_novis->value && r_viewcluster != -1) return; r_visframecount++; r_oldviewcluster = r_viewcluster; gEngfuncs.R_FatPVS( RI.pvsorigin, REFPVS_RADIUS, RI.visbytes, FBitSet( RI.params, RP_OLDVIEWLEAF ), false ); vis = RI.visbytes; for (i = 0; i < WORLDMODEL->numleafs; i++) { if (vis[i>>3] & (1<<(i&7))) { node = (mnode_t *) &WORLDMODEL->leafs[i+1]; do { if (node->visframe == r_visframecount) break; node->visframe = r_visframecount; node = node->parent; } while (node); } } } #endif /* ================ R_RenderScene R_SetupRefParams must be called right before ================ */ void R_RenderScene( void ) { if( !WORLDMODEL && RI.drawWorld ) gEngfuncs.Host_Error( "R_RenderView: NULL worldmodel\n" ); // frametime is valid only for normal pass if( RP_NORMALPASS( )) tr.frametime = gpGlobals->time - gpGlobals->oldtime; else tr.frametime = 0.0; // begin a new frame tr.framecount++; // R_PushDlights(); R_SetupFrustum(); R_SetupFrame(); // R_SetupGL( true ); R_Clear( ~0 ); R_MarkLeaves(); R_DrawFog (); // R_PushDlights (r_worldmodel); ?? R_CheckGLFog(); //R_DrawWorld(); R_EdgeDrawing (); R_CheckFog(); gEngfuncs.CL_ExtraUpdate (); // don't let sound get messed up if going slow R_DrawEntitiesOnList(); // R_DrawWaterSurfaces(); // R_EndGL(); } /* =============== R_DoResetGamma gamma will be reset for some type of screenshots =============== */ qboolean R_DoResetGamma( void ) { // FIXME: this looks ugly. apply the backward gamma changes to the output image return false; #if 0 switch( cls.scrshot_action ) { case scrshot_normal: if( CL_IsDevOverviewMode( )) return true; return false; case scrshot_snapshot: if( CL_IsDevOverviewMode( )) return true; return false; case scrshot_plaque: case scrshot_savegame: case scrshot_envshot: case scrshot_skyshot: case scrshot_mapshot: return true; default: return false; } #endif } /* =============== R_BeginFrame =============== */ void R_BeginFrame( qboolean clearScene ) { if( R_DoResetGamma( )) { gEngfuncs.BuildGammaTable( 1.8f, 0.0f ); // glConfig.softwareGammaUpdate = true; // GL_RebuildLightmaps(); // glConfig.softwareGammaUpdate = false; // next frame will be restored gamma // SetBits( vid_brightness->flags, FCVAR_CHANGED ); // SetBits( vid_gamma->flags, FCVAR_CHANGED ); } else if( FBitSet( vid_gamma->flags, FCVAR_CHANGED ) || FBitSet( vid_brightness->flags, FCVAR_CHANGED )) { // gEngfuncs.BuildGammaTable( vid_gamma->value, vid_brightness->value ); //glConfig.softwareGammaUpdate = true; // GL_RebuildLightmaps(); //glConfig.softwareGammaUpdate = false; } R_Set2DMode( true ); // draw buffer stuff //pglDrawBuffer( GL_BACK ); // update texture parameters //if( FBitSet( gl_texture_nearest->flags|gl_lightmap_nearest->flags|gl_texture_anisotropy->flags|gl_texture_lodbias->flags, FCVAR_CHANGED )) //R_SetTextureParameters(); gEngfuncs.CL_ExtraUpdate (); } /* =============== R_SetupRefParams set initial params for renderer =============== */ void R_SetupRefParams( const ref_viewpass_t *rvp ) { RI.params = RP_NONE; RI.drawWorld = FBitSet( rvp->flags, RF_DRAW_WORLD ); RI.onlyClientDraw = FBitSet( rvp->flags, RF_ONLY_CLIENTDRAW ); RI.farClip = 0; if( !FBitSet( rvp->flags, RF_DRAW_CUBEMAP )) RI.drawOrtho = FBitSet( rvp->flags, RF_DRAW_OVERVIEW ); else RI.drawOrtho = false; // setup viewport RI.viewport[0] = rvp->viewport[0]; RI.viewport[1] = rvp->viewport[1]; RI.viewport[2] = rvp->viewport[2]; RI.viewport[3] = rvp->viewport[3]; // calc FOV RI.fov_x = rvp->fov_x; RI.fov_y = rvp->fov_y; VectorCopy( rvp->vieworigin, RI.vieworg ); VectorCopy( rvp->viewangles, RI.viewangles ); VectorCopy( rvp->vieworigin, RI.pvsorigin ); } /* =============== R_RenderFrame =============== */ int R_RenderFrame( const ref_viewpass_t *rvp ) { if( r_norefresh->value ) return 1; // setup the initial render params R_SetupRefParams( rvp ); // completely override rendering if( gEngfuncs.drawFuncs->GL_RenderFrame != NULL ) { tr.fCustomRendering = true; if( gEngfuncs.drawFuncs->GL_RenderFrame( rvp )) { //R_GatherPlayerLight(); tr.realframecount++; tr.fResetVis = true; return 1; } } tr.fCustomRendering = false; // if( !RI.onlyClientDraw ) // R_RunViewmodelEvents(); tr.realframecount++; // right called after viewmodel events R_RenderScene(); return 1; } /* =============== R_EndFrame =============== */ void R_EndFrame( void ) { // flush any remaining 2D bits R_Set2DMode( false ); // blit pixels with GL until engine supports REF_SOFT context R_BlitScreen(); } /* =============== R_DrawCubemapView =============== */ void R_DrawCubemapView( const vec3_t origin, const vec3_t angles, int size ) { ref_viewpass_t rvp; // basic params rvp.flags = rvp.viewentity = 0; SetBits( rvp.flags, RF_DRAW_WORLD ); SetBits( rvp.flags, RF_DRAW_CUBEMAP ); rvp.viewport[0] = rvp.viewport[1] = 0; rvp.viewport[2] = rvp.viewport[3] = size; rvp.fov_x = rvp.fov_y = 90.0f; // this is a final fov value // setup origin & angles VectorCopy( origin, rvp.vieworigin ); VectorCopy( angles, rvp.viewangles ); R_RenderFrame( &rvp ); RI.viewleaf = NULL; // force markleafs next frame } /* =============== R_NewMap =============== */ void R_NewMap (void) { r_viewcluster = -1; r_cnumsurfs = sw_maxsurfs->value; if (r_cnumsurfs <= MINSURFACES) r_cnumsurfs = MINSURFACES; if (r_cnumsurfs > NUMSTACKSURFACES) { surfaces = malloc (r_cnumsurfs * sizeof(surf_t)); surface_p = surfaces; surf_max = &surfaces[r_cnumsurfs]; r_surfsonstack = false; // surface 0 doesn't really exist; it's just a dummy because index 0 // is used to indicate no edge attached to surface surfaces--; R_SurfacePatch (); } else { r_surfsonstack = true; } r_maxedgesseen = 0; r_maxsurfsseen = 0; r_numallocatededges = sw_maxedges->value; if (r_numallocatededges < MINEDGES) r_numallocatededges = MINEDGES; if (r_numallocatededges <= NUMSTACKEDGES) { auxedges = NULL; } else { auxedges = malloc (r_numallocatededges * sizeof(edge_t)); } } qboolean R_Init() { gl_emboss_scale = gEngfuncs.Cvar_Get( "gl_emboss_scale", "0", FCVAR_ARCHIVE|FCVAR_LATCH, "fake bumpmapping scale" ); vid_gamma = gEngfuncs.pfnGetCvarPointer( "gamma", 0 ); r_norefresh = gEngfuncs.Cvar_Get( "r_norefresh", "0", 0, "disable 3D rendering (use with caution)" ); r_drawentities = gEngfuncs.Cvar_Get( "r_drawentities", "1", FCVAR_CHEAT, "render entities" ); vid_brightness = gEngfuncs.pfnGetCvarPointer( "brightness", 0 ); r_fullbright = gEngfuncs.Cvar_Get( "r_fullbright", "0", FCVAR_CHEAT, "disable lightmaps, get fullbright for entities" ); sw_mipcap = gEngfuncs.Cvar_Get( "r_fullbright", "0", FCVAR_CHEAT, "disable lightmaps, get fullbright for entities" ); // sw_aliasstats = ri.Cvar_Get ("sw_polymodelstats", "0", 0); // sw_allow_modex = ri.Cvar_Get( "sw_allow_modex", "1", CVAR_ARCHIVE ); sw_clearcolor = gEngfuncs.Cvar_Get ("sw_clearcolor", "2", 0, "screen clear color"); sw_drawflat = gEngfuncs.Cvar_Get ("sw_drawflat", "0", 0, ""); sw_draworder = gEngfuncs.Cvar_Get ("sw_draworder", "0", 0, ""); sw_maxedges = gEngfuncs.Cvar_Get ("sw_maxedges", "32", 0, ""); sw_maxsurfs = gEngfuncs.Cvar_Get ("sw_maxsurfs", "0", 0, ""); sw_mipscale = gEngfuncs.Cvar_Get ("sw_mipscale", "1", 0, ""); sw_reportedgeout = gEngfuncs.Cvar_Get ("sw_reportedgeout", "0", 0, ""); sw_reportsurfout = gEngfuncs.Cvar_Get ("sw_reportsurfout", "0", 0, ""); sw_stipplealpha = gEngfuncs.Cvar_Get( "sw_stipplealpha", "0", FCVAR_ARCHIVE, "" ); sw_surfcacheoverride = gEngfuncs.Cvar_Get ("sw_surfcacheoverride", "0", 0, ""); sw_waterwarp = gEngfuncs.Cvar_Get ("sw_waterwarp", "1", 0, ""); sw_mode = gEngfuncs.Cvar_Get( "sw_mode", "0", FCVAR_ARCHIVE, ""); sw_texfilt = gEngfuncs.Cvar_Get ("sw_texfilt", "0", 0, "texture dither"); //r_lefthand = ri.Cvar_Get( "hand", "0", FCVAR_USERINFO | FCVAR_ARCHIVE ); // r_speeds = ri.Cvar_Get ("r_speeds", "0", 0); //r_drawworld = ri.Cvar_Get ("r_drawworld", "1", 0); //r_dspeeds = ri.Cvar_Get ("r_dspeeds", "0", 0); // r_lightlevel = ri.Cvar_Get ("r_lightlevel", "0", 0); //r_lerpmodels = ri.Cvar_Get( "r_lerpmodels", "1", 0 ); r_novis = gEngfuncs.Cvar_Get( "r_novis", "0", 0, "" ); // create the window and set up the context r_temppool = Mem_AllocPool( "ref_sw zone" ); vid.width = 1920; vid.height = 1080; vid.rowbytes = 1920; // rowpixels vid.buffer = Mem_Malloc( r_temppool, 1920*1080*sizeof( pixel_t ) ); if( !gEngfuncs.R_Init_Video( REF_GL )) // request GL context { gEngfuncs.R_Free_Video(); gEngfuncs.Host_Error( "Can't initialize video subsystem\nProbably driver was not installed" ); return false; } R_InitImages(); // init draw stack tr.draw_list = &tr.draw_stack[0]; tr.draw_stack_pos = 0; return true; } void R_Shutdown() { R_ShutdownImages(); gEngfuncs.R_Free_Video(); }