X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=gl_rmain.c;h=3ca1f219a3e1376d451c599d8a83e1efb7616ef2;hb=bba735499244568312faac2b77abcdd6a8449b31;hp=3b58db0734bbdd2377147330df5b5db4605ae209;hpb=6c09dc11879f3cb343c2698c193774cc1fda8f16;p=xonotic%2Fdarkplaces.git diff --git a/gl_rmain.c b/gl_rmain.c index 3b58db07..3ca1f219 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -51,6 +51,8 @@ cvar_t r_fullbright = {0, "r_fullbright","0", "make everything bright cheat (not cvar_t r_wateralpha = {CVAR_SAVE, "r_wateralpha","1", "opacity of water polygons"}; cvar_t r_dynamic = {CVAR_SAVE, "r_dynamic","1", "enables dynamic lights (rocket glow and such)"}; cvar_t r_fullbrights = {CVAR_SAVE, "r_fullbrights", "1", "enables glowing pixels in quake textures (changes need r_restart to take effect)"}; +cvar_t r_shadows = {CVAR_SAVE, "r_shadows", "0", "casts fake stencil shadows from models onto the world (rtlights are unaffected by this)"}; +cvar_t r_shadows_throwdistance = {CVAR_SAVE, "r_shadows_throwdistance", "500", "how far to cast shadows from models"}; cvar_t r_q1bsp_skymasking = {0, "r_qb1sp_skymasking", "1", "allows sky polygons in quake1 maps to obscure other geometry"}; cvar_t gl_fogenable = {0, "gl_fogenable", "0", "nehahra fog enable (for Nehahra compatibility only)"}; @@ -74,14 +76,15 @@ cvar_t r_lerpmodels = {CVAR_SAVE, "r_lerpmodels", "1", "enables animation smooth cvar_t r_waterscroll = {CVAR_SAVE, "r_waterscroll", "1", "makes water scroll around, value controls how much"}; cvar_t r_bloom = {CVAR_SAVE, "r_bloom", "0", "enables bloom effect (makes bright pixels affect neighboring pixels)"}; -cvar_t r_bloom_intensity = {CVAR_SAVE, "r_bloom_intensity", "1.5", "how bright the glow is"}; +cvar_t r_bloom_colorscale = {CVAR_SAVE, "r_bloom_colorscale", "1", "how bright the glow is"}; +cvar_t r_bloom_brighten = {CVAR_SAVE, "r_bloom_brighten", "2", "how bright the glow is, after subtract/power"}; cvar_t r_bloom_blur = {CVAR_SAVE, "r_bloom_blur", "4", "how large the glow is"}; cvar_t r_bloom_resolution = {CVAR_SAVE, "r_bloom_resolution", "320", "what resolution to perform the bloom effect at (independent of screen resolution)"}; -cvar_t r_bloom_power = {CVAR_SAVE, "r_bloom_power", "2", "how much to darken the image before blurring to make the bloom effect"}; +cvar_t r_bloom_colorexponent = {CVAR_SAVE, "r_bloom_colorexponent", "1", "how exagerated the glow is"}; +cvar_t r_bloom_colorsubtract = {CVAR_SAVE, "r_bloom_colorsubtract", "0.125", "reduces bloom colors by a certain amount"}; cvar_t r_hdr = {CVAR_SAVE, "r_hdr", "0", "enables High Dynamic Range bloom effect (higher quality version of r_bloom)"}; cvar_t r_hdr_scenebrightness = {CVAR_SAVE, "r_hdr_scenebrightness", "1", "global rendering brightness"}; -cvar_t r_hdr_bloomintensity = {CVAR_SAVE, "r_hdr_bloomintensity", "0.5", "amount of bloom"}; cvar_t r_hdr_glowintensity = {CVAR_SAVE, "r_hdr_glowintensity", "1", "how bright light emitting textures should appear"}; cvar_t r_smoothnormals_areaweighting = {0, "r_smoothnormals_areaweighting", "1", "uses significantly faster (and supposedly higher quality) area-weighted vertex normals and tangent vectors rather than summing normalized triangle normals and tangents"}; @@ -93,8 +96,35 @@ cvar_t gl_lightmaps = {0, "gl_lightmaps", "0", "draws only lightmaps, no texture cvar_t r_test = {0, "r_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"}; // used for testing renderer code changes, otherwise does nothing cvar_t r_batchmode = {0, "r_batchmode", "1", "selects method of rendering multiple surfaces with one driver call (values are 0, 1, 2, etc...)"}; -rtexture_t *r_bloom_texture_screen; -rtexture_t *r_bloom_texture_bloom; +typedef struct r_glsl_bloomshader_s +{ + int program; + int loc_Texture_Bloom; +} +r_glsl_bloomshader_t; + +static struct r_bloomstate_s +{ + qboolean enabled; + qboolean hdr; + + int bloomwidth, bloomheight; + + int screentexturewidth, screentextureheight; + rtexture_t *texture_screen; + + int bloomtexturewidth, bloomtextureheight; + rtexture_t *texture_bloom; + + r_glsl_bloomshader_t *shader; + + // arrays for rendering the screen passes + float screentexcoord2f[8]; + float bloomtexcoord2f[8]; + float offsettexcoord2f[8]; +} +r_bloomstate; + rtexture_t *r_texture_blanknormalmap; rtexture_t *r_texture_white; rtexture_t *r_texture_black; @@ -112,6 +142,17 @@ r_glsl_permutation_t *r_glsl_permutation; // temporary variable used by a macro int fogtableindex; +// vertex coordinates for a quad that covers the screen exactly +const static float r_screenvertex3f[12] = +{ + 0, 0, 0, + 1, 0, 0, + 1, 1, 0, + 0, 1, 0 +}; + +extern void R_DrawModelShadows(void); + void R_ModulateColors(float *in, float *out, int verts, float r, float g, float b) { int i; @@ -928,8 +969,6 @@ void R_SwitchSurfaceShader(int permutation) void gl_main_start(void) { r_main_texturepool = R_AllocTexturePool(); - r_bloom_texture_screen = NULL; - r_bloom_texture_bloom = NULL; R_BuildBlankTextures(); R_BuildNoTexture(); if (gl_texturecubemap) @@ -938,19 +977,19 @@ void gl_main_start(void) R_BuildNormalizationCube(); } R_BuildFogTexture(); + memset(&r_bloomstate, 0, sizeof(r_bloomstate)); memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations)); } void gl_main_shutdown(void) { R_FreeTexturePool(&r_main_texturepool); - r_bloom_texture_screen = NULL; - r_bloom_texture_bloom = NULL; r_texture_blanknormalmap = NULL; r_texture_white = NULL; r_texture_black = NULL; r_texture_whitecube = NULL; r_texture_normalizationcube = NULL; + memset(&r_bloomstate, 0, sizeof(r_bloomstate)); R_GLSL_Restart_f(); } @@ -1003,6 +1042,8 @@ void GL_Main_Init(void) Cvar_RegisterVariable(&r_wateralpha); Cvar_RegisterVariable(&r_dynamic); Cvar_RegisterVariable(&r_fullbright); + Cvar_RegisterVariable(&r_shadows); + Cvar_RegisterVariable(&r_shadows_throwdistance); Cvar_RegisterVariable(&r_q1bsp_skymasking); Cvar_RegisterVariable(&r_textureunits); Cvar_RegisterVariable(&r_glsl); @@ -1014,13 +1055,14 @@ void GL_Main_Init(void) Cvar_RegisterVariable(&r_lerpmodels); Cvar_RegisterVariable(&r_waterscroll); Cvar_RegisterVariable(&r_bloom); - Cvar_RegisterVariable(&r_bloom_intensity); + Cvar_RegisterVariable(&r_bloom_colorscale); + Cvar_RegisterVariable(&r_bloom_brighten); Cvar_RegisterVariable(&r_bloom_blur); Cvar_RegisterVariable(&r_bloom_resolution); - Cvar_RegisterVariable(&r_bloom_power); + Cvar_RegisterVariable(&r_bloom_colorexponent); + Cvar_RegisterVariable(&r_bloom_colorsubtract); Cvar_RegisterVariable(&r_hdr); Cvar_RegisterVariable(&r_hdr_scenebrightness); - Cvar_RegisterVariable(&r_hdr_bloomintensity); Cvar_RegisterVariable(&r_hdr_glowintensity); Cvar_RegisterVariable(&r_smoothnormals_areaweighting); Cvar_RegisterVariable(&developer_texturelogging); @@ -1134,9 +1176,11 @@ int R_CullBox(const vec3_t mins, const vec3_t maxs) static void R_UpdateEntityLighting(entity_render_t *ent) { vec3_t tempdiffusenormal; + + // fetch the lighting from the worldmodel data VectorSet(ent->modellight_ambient, r_ambient.value * (2.0f / 128.0f), r_ambient.value * (2.0f / 128.0f), r_ambient.value * (2.0f / 128.0f)); VectorClear(ent->modellight_diffuse); - VectorClear(ent->modellight_lightdir); + VectorClear(tempdiffusenormal); if ((ent->flags & RENDER_LIGHT) && r_refdef.worldmodel && r_refdef.worldmodel->brush.LightPoint) { vec3_t org; @@ -1145,8 +1189,12 @@ static void R_UpdateEntityLighting(entity_render_t *ent) } else // highly rare VectorSet(ent->modellight_ambient, 1, 1, 1); + + // move the light direction into modelspace coordinates for lighting code Matrix4x4_Transform3x3(&ent->inversematrix, tempdiffusenormal, ent->modellight_lightdir); VectorNormalize(ent->modellight_lightdir); + + // scale ambient and directional light contributions according to rendering variables ent->modellight_ambient[0] *= ent->colormod[0] * r_refdef.lightmapintensity; ent->modellight_ambient[1] *= ent->colormod[1] * r_refdef.lightmapintensity; ent->modellight_ambient[2] *= ent->colormod[2] * r_refdef.lightmapintensity; @@ -1171,8 +1219,6 @@ static void R_View_UpdateEntityVisible (void) { ent = r_refdef.entities[i]; r_viewcache.entityvisible[i] = !(ent->flags & renderimask) && !R_CullBox(ent->mins, ent->maxs) && ((ent->effects & EF_NODEPTHTEST) || r_refdef.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.worldmodel, r_viewcache.world_leafvisible, ent->mins, ent->maxs)); - if (r_viewcache.entityvisible[i]) - R_UpdateEntityLighting(ent); } } else @@ -1182,10 +1228,12 @@ static void R_View_UpdateEntityVisible (void) { ent = r_refdef.entities[i]; r_viewcache.entityvisible[i] = !(ent->flags & renderimask) && !R_CullBox(ent->mins, ent->maxs); - if (r_viewcache.entityvisible[i]) - R_UpdateEntityLighting(ent); } } + + // update entity lighting (even on hidden entities for r_shadows) + for (i = 0;i < r_refdef.numentities;i++) + R_UpdateEntityLighting(r_refdef.entities[i]); } // only used if skyrendermasked, and normally returns false @@ -1360,97 +1408,251 @@ void R_View_Update(void) R_View_UpdateEntityVisible(); } -void R_ResetViewRendering(void) +void R_SetupView(const matrix4x4_t *matrix) +{ + if (r_refdef.rtworldshadows || r_refdef.rtdlightshadows) + GL_SetupView_Mode_PerspectiveInfiniteFarClip(r_view.frustum_x, r_view.frustum_y, r_refdef.nearclip); + else + GL_SetupView_Mode_Perspective(r_view.frustum_x, r_view.frustum_y, r_refdef.nearclip, r_refdef.farclip); + + GL_SetupView_Orientation_FromEntity(matrix); +} + +void R_ResetViewRendering2D(void) { if (gl_support_fragment_shader) { qglUseProgramObjectARB(0);CHECKGLERROR } + DrawQ_Finish(); + // GL is weird because it's bottom to top, r_view.y is top to bottom qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100); GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height); + GL_Color(1, 1, 1, 1); + GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1); + GL_BlendFunc(GL_ONE, GL_ZERO); + GL_AlphaTest(false); + GL_ScissorTest(false); + GL_DepthMask(false); + GL_DepthTest(false); + R_Mesh_Matrix(&identitymatrix); + R_Mesh_ResetTextureState(); + qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR + qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR + qglDepthFunc(GL_LEQUAL);CHECKGLERROR + qglDisable(GL_STENCIL_TEST);CHECKGLERROR + qglStencilMask(~0);CHECKGLERROR + qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR + qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR + GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces +} + +void R_ResetViewRendering3D(void) +{ + if (gl_support_fragment_shader) + { + qglUseProgramObjectARB(0);CHECKGLERROR + } + + DrawQ_Finish(); + + // GL is weird because it's bottom to top, r_view.y is top to bottom + qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR + R_SetupView(&r_view.matrix); + GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height); + GL_Color(1, 1, 1, 1); GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1); + GL_BlendFunc(GL_ONE, GL_ZERO); + GL_AlphaTest(false); GL_ScissorTest(true); GL_DepthMask(true); GL_DepthTest(true); R_Mesh_Matrix(&identitymatrix); R_Mesh_ResetTextureState(); + qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR + qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR + qglDepthFunc(GL_LEQUAL);CHECKGLERROR + qglDisable(GL_STENCIL_TEST);CHECKGLERROR + qglStencilMask(~0);CHECKGLERROR + qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR + qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR + GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces } +/* + R_Bloom_SetupShader( +"// bloom shader\n" +"// written by Forest 'LordHavoc' Hale\n" +"\n" +"// common definitions between vertex shader and fragment shader:\n" +"\n" +"#ifdef __GLSL_CG_DATA_TYPES\n" +"#define myhalf half\n" +"#define myhvec2 hvec2\n" +"#define myhvec3 hvec3\n" +"#define myhvec4 hvec4\n" +"#else\n" +"#define myhalf float\n" +"#define myhvec2 vec2\n" +"#define myhvec3 vec3\n" +"#define myhvec4 vec4\n" +"#endif\n" +"\n" +"varying vec2 ScreenTexCoord;\n" +"varying vec2 BloomTexCoord;\n" +"\n" +"\n" +"\n" +"\n" +"// vertex shader specific:\n" +"#ifdef VERTEX_SHADER\n" +"\n" +"void main(void)\n" +"{\n" +" ScreenTexCoord = vec2(gl_MultiTexCoord0);\n" +" BloomTexCoord = vec2(gl_MultiTexCoord1);\n" +" // transform vertex to camera space, using ftransform to match non-VS\n" +" // rendering\n" +" gl_Position = ftransform();\n" +"}\n" +"\n" +"#endif // VERTEX_SHADER\n" +"\n" +"\n" +"\n" +"\n" +"// fragment shader specific:\n" +"#ifdef FRAGMENT_SHADER\n" +"\n" +"void main(void)\n" +"{\n" +" int x, y; +" myhvec3 color = myhvec3(texture2D(Texture_Screen, ScreenTexCoord));\n" +" for (x = -BLUR_X;x <= BLUR_X;x++) +" color.rgb += myhvec3(texture2D(Texture_Bloom, BloomTexCoord));\n" +" color.rgb += myhvec3(texture2D(Texture_Bloom, BloomTexCoord));\n" +" color.rgb += myhvec3(texture2D(Texture_Bloom, BloomTexCoord));\n" +" color.rgb += myhvec3(texture2D(Texture_Bloom, BloomTexCoord));\n" + +" gl_FragColor = vec4(color);\n" +"}\n" +"\n" +"#endif // FRAGMENT_SHADER\n" +*/ + void R_RenderScene(void); -void R_Bloom_MakeTexture(qboolean darken) +void R_Bloom_StartFrame(void) { - int screenwidth, screenheight; - int screentexturewidth, screentextureheight; - int bloomtexturewidth, bloomtextureheight; - int bloomwidth, bloomheight, x, range; - float xoffset, yoffset, r; - float vertex3f[12]; - float texcoord2f[3][8]; + int bloomtexturewidth, bloomtextureheight, screentexturewidth, screentextureheight; // set bloomwidth and bloomheight to the bloom resolution that will be // used (often less than the screen resolution for faster rendering) - bloomwidth = bound(1, r_bloom_resolution.integer, r_view.width); - bloomheight = bound(1, bloomwidth * r_view.height / r_view.width, r_view.height); + r_bloomstate.bloomwidth = bound(1, r_bloom_resolution.integer, r_view.width); + r_bloomstate.bloomheight = r_bloomstate.bloomwidth * r_view.height / r_view.width; + r_bloomstate.bloomheight = bound(1, r_bloomstate.bloomheight, r_view.height); - // set the (poorly named) screenwidth and screenheight variables to - // a power of 2 at least as large as the screen, these will define the - // size of the texture to allocate - for (screenwidth = 1;screenwidth < vid.width;screenwidth *= 2); - for (screenheight = 1;screenheight < vid.height;screenheight *= 2); + // calculate desired texture sizes + if (gl_support_arb_texture_non_power_of_two) + { + screentexturewidth = r_view.width; + screentextureheight = r_view.height; + bloomtexturewidth = r_bloomstate.bloomwidth; + bloomtextureheight = r_bloomstate.bloomheight; + } + else + { + for (screentexturewidth = 1;screentexturewidth < vid.width ;screentexturewidth *= 2); + for (screentextureheight = 1;screentextureheight < vid.height ;screentextureheight *= 2); + for (bloomtexturewidth = 1;bloomtexturewidth < r_bloomstate.bloomwidth ;bloomtexturewidth *= 2); + for (bloomtextureheight = 1;bloomtextureheight < r_bloomstate.bloomheight;bloomtextureheight *= 2); + } - r_refdef.stats.bloom++; + if (r_hdr.integer) + { + screentexturewidth = screentextureheight = 0; + } + else if (r_bloom.integer) + { + } + else + { + screentexturewidth = screentextureheight = 0; + bloomtexturewidth = bloomtextureheight = 0; + } + + if ((!bloomtexturewidth && !bloomtextureheight) || r_bloom_resolution.integer < 4 || r_bloom_blur.value < 1 || r_bloom_blur.value >= 512 || screentexturewidth > gl_max_texture_size || screentextureheight > gl_max_texture_size || bloomtexturewidth > gl_max_texture_size || bloomtextureheight > gl_max_texture_size) + { + // can't use bloom if the parameters are too weird + // can't use bloom if the card does not support the texture size + if (r_bloomstate.texture_screen) + R_FreeTexture(r_bloomstate.texture_screen); + if (r_bloomstate.texture_bloom) + R_FreeTexture(r_bloomstate.texture_bloom); + memset(&r_bloomstate, 0, sizeof(r_bloomstate)); + return; + } + + r_bloomstate.enabled = true; + r_bloomstate.hdr = r_hdr.integer != 0; // allocate textures as needed - // TODO: reallocate these when size settings change - if (!r_bloom_texture_screen) - r_bloom_texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL); - if (!r_bloom_texture_bloom) - r_bloom_texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL); - - screentexturewidth = R_TextureWidth(r_bloom_texture_screen); - screentextureheight = R_TextureHeight(r_bloom_texture_screen); - bloomtexturewidth = R_TextureWidth(r_bloom_texture_bloom); - bloomtextureheight = R_TextureHeight(r_bloom_texture_bloom); - - // vertex coordinates for a quad that covers the screen exactly - vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0; - vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0; - vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0; - vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0; + if (r_bloomstate.screentexturewidth != screentexturewidth || r_bloomstate.screentextureheight != screentextureheight) + { + if (r_bloomstate.texture_screen) + R_FreeTexture(r_bloomstate.texture_screen); + r_bloomstate.texture_screen = NULL; + r_bloomstate.screentexturewidth = screentexturewidth; + r_bloomstate.screentextureheight = screentextureheight; + if (r_bloomstate.screentexturewidth && r_bloomstate.screentextureheight) + r_bloomstate.texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, NULL, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL); + } + if (r_bloomstate.bloomtexturewidth != bloomtexturewidth || r_bloomstate.bloomtextureheight != bloomtextureheight) + { + if (r_bloomstate.texture_bloom) + R_FreeTexture(r_bloomstate.texture_bloom); + r_bloomstate.texture_bloom = NULL; + r_bloomstate.bloomtexturewidth = bloomtexturewidth; + r_bloomstate.bloomtextureheight = bloomtextureheight; + if (r_bloomstate.bloomtexturewidth && r_bloomstate.bloomtextureheight) + r_bloomstate.texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", r_bloomstate.bloomtexturewidth, r_bloomstate.bloomtextureheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL); + } // set up a texcoord array for the full resolution screen image // (we have to keep this around to copy back during final render) - texcoord2f[0][0] = 0; - texcoord2f[0][1] = (float)r_view.height / (float)screentextureheight; - texcoord2f[0][2] = (float)r_view.width / (float)screentexturewidth; - texcoord2f[0][3] = (float)r_view.height / (float)screentextureheight; - texcoord2f[0][4] = (float)r_view.width / (float)screentexturewidth; - texcoord2f[0][5] = 0; - texcoord2f[0][6] = 0; - texcoord2f[0][7] = 0; + r_bloomstate.screentexcoord2f[0] = 0; + r_bloomstate.screentexcoord2f[1] = (float)r_view.height / (float)r_bloomstate.screentextureheight; + r_bloomstate.screentexcoord2f[2] = (float)r_view.width / (float)r_bloomstate.screentexturewidth; + r_bloomstate.screentexcoord2f[3] = (float)r_view.height / (float)r_bloomstate.screentextureheight; + r_bloomstate.screentexcoord2f[4] = (float)r_view.width / (float)r_bloomstate.screentexturewidth; + r_bloomstate.screentexcoord2f[5] = 0; + r_bloomstate.screentexcoord2f[6] = 0; + r_bloomstate.screentexcoord2f[7] = 0; // set up a texcoord array for the reduced resolution bloom image // (which will be additive blended over the screen image) - texcoord2f[1][0] = 0; - texcoord2f[1][1] = (float)bloomheight / (float)bloomtextureheight; - texcoord2f[1][2] = (float)bloomwidth / (float)bloomtexturewidth; - texcoord2f[1][3] = (float)bloomheight / (float)bloomtextureheight; - texcoord2f[1][4] = (float)bloomwidth / (float)bloomtexturewidth; - texcoord2f[1][5] = 0; - texcoord2f[1][6] = 0; - texcoord2f[1][7] = 0; - - R_ResetViewRendering(); - GL_DepthTest(false); - R_Mesh_VertexPointer(vertex3f); - R_Mesh_ColorPointer(NULL); + r_bloomstate.bloomtexcoord2f[0] = 0; + r_bloomstate.bloomtexcoord2f[1] = (float)r_bloomstate.bloomheight / (float)r_bloomstate.bloomtextureheight; + r_bloomstate.bloomtexcoord2f[2] = (float)r_bloomstate.bloomwidth / (float)r_bloomstate.bloomtexturewidth; + r_bloomstate.bloomtexcoord2f[3] = (float)r_bloomstate.bloomheight / (float)r_bloomstate.bloomtextureheight; + r_bloomstate.bloomtexcoord2f[4] = (float)r_bloomstate.bloomwidth / (float)r_bloomstate.bloomtexturewidth; + r_bloomstate.bloomtexcoord2f[5] = 0; + r_bloomstate.bloomtexcoord2f[6] = 0; + r_bloomstate.bloomtexcoord2f[7] = 0; +} - R_Mesh_TexCoordPointer(0, 2, texcoord2f[0]); - R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_screen)); +void R_Bloom_CopyScreenTexture(float colorscale) +{ + r_refdef.stats.bloom++; + + R_ResetViewRendering2D(); + R_Mesh_VertexPointer(r_screenvertex3f); + R_Mesh_ColorPointer(NULL); + R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f); + R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen)); // copy view into the screen texture GL_ActiveTexture(0); @@ -1460,105 +1662,134 @@ void R_Bloom_MakeTexture(qboolean darken) // now scale it down to the bloom texture size CHECKGLERROR - qglViewport(r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR + qglViewport(r_view.x, vid.height - (r_view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR GL_BlendFunc(GL_ONE, GL_ZERO); - GL_Color(1, 1, 1, 1); + GL_Color(colorscale, colorscale, colorscale, 1); // TODO: optimize with multitexture or GLSL R_Mesh_Draw(0, 4, 2, polygonelements); - r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight; + r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight; - if (darken) - { - // raise to a power of itself to darken it (this leaves the really - // bright stuff bright, and everything else becomes very dark) - // render multiple times with a multiply blendfunc to raise to a power - GL_BlendFunc(GL_DST_COLOR, GL_ZERO); - for (x = 1;x < r_bloom_power.integer;x++) - { - R_Mesh_Draw(0, 4, 2, polygonelements); - r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight; - } - } + // we now have a bloom image in the framebuffer + // copy it into the bloom image texture for later processing + R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom)); + GL_ActiveTexture(0); + CHECKGLERROR + qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR + r_refdef.stats.bloom_copypixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight; +} - // we now have a darkened bloom image in the framebuffer - // copy it into the bloom image texture for more processing - R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_bloom)); - R_Mesh_TexCoordPointer(0, 2, texcoord2f[2]); +void R_Bloom_CopyHDRTexture(void) +{ + R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom)); GL_ActiveTexture(0); CHECKGLERROR - qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR - r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight; + qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR + r_refdef.stats.bloom_copypixels += r_view.width * r_view.height; +} - // blend on at multiple vertical offsets to achieve a vertical blur - // TODO: do offset blends using GLSL - range = r_bloom_blur.integer * bloomwidth / 320; - GL_BlendFunc(GL_ONE, GL_ZERO); - for (x = -range;x <= range;x++) - { - xoffset = 0 / (float)bloomwidth * (float)bloomwidth / (float)screenwidth; - yoffset = x / (float)bloomheight * (float)bloomheight / (float)screenheight; - // compute a texcoord array with the specified x and y offset - texcoord2f[2][0] = xoffset+0; - texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight; - texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth; - texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight; - texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth; - texcoord2f[2][5] = yoffset+0; - texcoord2f[2][6] = xoffset+0; - texcoord2f[2][7] = yoffset+0; - // this r value looks like a 'dot' particle, fading sharply to - // black at the edges - // (probably not realistic but looks good enough) - r = r_bloom_intensity.value/(range*2+1)*(1 - x*x/(float)(range*range)); - if (r < 0.01f) - continue; +void R_Bloom_MakeTexture(void) +{ + int x, range, dir; + float xoffset, yoffset, r; + + r_refdef.stats.bloom++; + + R_ResetViewRendering2D(); + R_Mesh_VertexPointer(r_screenvertex3f); + R_Mesh_ColorPointer(NULL); + + // we have a bloom image in the framebuffer + CHECKGLERROR + qglViewport(r_view.x, vid.height - (r_view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR + + for (x = 1;x < r_bloom_colorexponent.value;) + { + x *= 2; + r = bound(0, r_bloom_colorexponent.value / x, 1); + GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR); GL_Color(r, r, r, 1); + R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom)); + R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f); R_Mesh_Draw(0, 4, 2, polygonelements); - r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight; - GL_BlendFunc(GL_ONE, GL_ONE); + r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight; + + // copy the vertically blurred bloom view to a texture + GL_ActiveTexture(0); + CHECKGLERROR + qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR + r_refdef.stats.bloom_copypixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight; } - // copy the vertically blurred bloom view to a texture - GL_ActiveTexture(0); - CHECKGLERROR - qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR - r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight; + range = r_bloom_blur.integer * r_bloomstate.bloomwidth / 320; + R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom)); + R_Mesh_TexCoordPointer(0, 2, r_bloomstate.offsettexcoord2f); - // blend the vertically blurred image at multiple offsets horizontally - // to finish the blur effect - // TODO: do offset blends using GLSL - range = r_bloom_blur.integer * bloomwidth / 320; - GL_BlendFunc(GL_ONE, GL_ZERO); - for (x = -range;x <= range;x++) - { - xoffset = x / (float)bloomwidth * (float)bloomwidth / (float)screenwidth; - yoffset = 0 / (float)bloomheight * (float)bloomheight / (float)screenheight; - // compute a texcoord array with the specified x and y offset - texcoord2f[2][0] = xoffset+0; - texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight; - texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth; - texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight; - texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth; - texcoord2f[2][5] = yoffset+0; - texcoord2f[2][6] = xoffset+0; - texcoord2f[2][7] = yoffset+0; - // this r value looks like a 'dot' particle, fading sharply to - // black at the edges - // (probably not realistic but looks good enough) - r = r_bloom_intensity.value/(range*2+1)*(1 - x*x/(float)(range*range)); - if (r < 0.01f) - continue; - GL_Color(r, r, r, 1); + for (dir = 0;dir < 2;dir++) + { + // blend on at multiple vertical offsets to achieve a vertical blur + // TODO: do offset blends using GLSL + GL_BlendFunc(GL_ONE, GL_ZERO); + for (x = -range;x <= range;x++) + { + if (!dir){xoffset = 0;yoffset = x;} + else {xoffset = x;yoffset = 0;} + xoffset /= (float)r_bloomstate.bloomtexturewidth; + yoffset /= (float)r_bloomstate.bloomtextureheight; + // compute a texcoord array with the specified x and y offset + r_bloomstate.offsettexcoord2f[0] = xoffset+0; + r_bloomstate.offsettexcoord2f[1] = yoffset+(float)r_bloomstate.bloomheight / (float)r_bloomstate.bloomtextureheight; + r_bloomstate.offsettexcoord2f[2] = xoffset+(float)r_bloomstate.bloomwidth / (float)r_bloomstate.bloomtexturewidth; + r_bloomstate.offsettexcoord2f[3] = yoffset+(float)r_bloomstate.bloomheight / (float)r_bloomstate.bloomtextureheight; + r_bloomstate.offsettexcoord2f[4] = xoffset+(float)r_bloomstate.bloomwidth / (float)r_bloomstate.bloomtexturewidth; + r_bloomstate.offsettexcoord2f[5] = yoffset+0; + r_bloomstate.offsettexcoord2f[6] = xoffset+0; + r_bloomstate.offsettexcoord2f[7] = yoffset+0; + // this r value looks like a 'dot' particle, fading sharply to + // black at the edges + // (probably not realistic but looks good enough) + //r = ((range*range+1)/((float)(x*x+1)))/(range*2+1); + //r = (dir ? 1.0f : r_bloom_brighten.value)/(range*2+1); + r = (dir ? 1.0f : r_bloom_brighten.value)/(range*2+1)*(1 - x*x/(float)(range*range)); + GL_Color(r, r, r, 1); + R_Mesh_Draw(0, 4, 2, polygonelements); + r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight; + GL_BlendFunc(GL_ONE, GL_ONE); + } + + // copy the vertically blurred bloom view to a texture + GL_ActiveTexture(0); + CHECKGLERROR + qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR + r_refdef.stats.bloom_copypixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight; + } + + // apply subtract last + // (just like it would be in a GLSL shader) + if (r_bloom_colorsubtract.value > 0 && gl_support_ext_blend_subtract) + { + GL_BlendFunc(GL_ONE, GL_ZERO); + R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom)); + R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f); + GL_Color(1, 1, 1, 1); R_Mesh_Draw(0, 4, 2, polygonelements); - r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight; + r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight; + GL_BlendFunc(GL_ONE, GL_ONE); - } + qglBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT); + R_Mesh_TexBind(0, R_GetTexture(r_texture_white)); + R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f); + GL_Color(r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, 1); + R_Mesh_Draw(0, 4, 2, polygonelements); + r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight; + qglBlendEquationEXT(GL_FUNC_ADD_EXT); - // copy the blurred bloom view to a texture - GL_ActiveTexture(0); - CHECKGLERROR - qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR - r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight; + // copy the darkened bloom view to a texture + R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom)); + GL_ActiveTexture(0); + CHECKGLERROR + qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR + r_refdef.stats.bloom_copypixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight; + } } void R_HDR_RenderBloomTexture(void) @@ -1567,124 +1798,72 @@ void R_HDR_RenderBloomTexture(void) oldwidth = r_view.width; oldheight = r_view.height; - r_view.width = bound(1, r_bloom_resolution.integer, min(r_view.width, gl_max_texture_size)); - r_view.height = r_view.width * oldheight / oldwidth; + r_view.width = r_bloomstate.bloomwidth; + r_view.height = r_bloomstate.bloomheight; // TODO: support GL_EXT_framebuffer_object rather than reusing the framebuffer? it might improve SLI performance. - // FIXME: change global lightmapintensity and light intensity according to r_hdr_bloomintensity cvar - // FIXME: change global lightmapintensity and light intensity according to r_hdr_scenebrightness cvar // TODO: add exposure compensation features + // TODO: add fp16 framebuffer support - r_view.colorscale = r_hdr_bloomintensity.value * r_hdr_scenebrightness.value; + r_view.colorscale = r_bloom_colorscale.value * r_hdr_scenebrightness.value; R_RenderScene(); - R_ResetViewRendering(); + R_ResetViewRendering2D(); + + R_Bloom_CopyHDRTexture(); + R_Bloom_MakeTexture(); - R_Bloom_MakeTexture(false); + R_ResetViewRendering3D(); R_ClearScreen(); if (r_timereport_active) R_TimeReport("clear"); + // restore the view settings r_view.width = oldwidth; r_view.height = oldheight; - - // go back to full view area - R_ResetViewRendering(); } static void R_BlendView(void) { - int screenwidth, screenheight; - int bloomwidth, bloomheight; - qboolean dobloom; - qboolean dohdr; - qboolean doblend; - float vertex3f[12]; - float texcoord2f[3][8]; - - // set the (poorly named) screenwidth and screenheight variables to - // a power of 2 at least as large as the screen, these will define the - // size of the texture to allocate - for (screenwidth = 1;screenwidth < vid.width;screenwidth *= 2); - for (screenheight = 1;screenheight < vid.height;screenheight *= 2); - - doblend = r_refdef.viewblend[3] >= 0.01f; - dobloom = !r_hdr.integer && r_bloom.integer && screenwidth <= gl_max_texture_size && screenheight <= gl_max_texture_size && r_bloom_resolution.value >= 32 && r_bloom_power.integer >= 1 && r_bloom_power.integer < 100 && r_bloom_blur.value >= 0 && r_bloom_blur.value < 512; - dohdr = r_hdr.integer && screenwidth <= gl_max_texture_size && screenheight <= gl_max_texture_size && r_bloom_resolution.value >= 32 && r_bloom_power.integer >= 1 && r_bloom_power.integer < 100 && r_bloom_blur.value >= 0 && r_bloom_blur.value < 512; - - if (!dobloom && !dohdr && !doblend) - return; - - // vertex coordinates for a quad that covers the screen exactly - vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0; - vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0; - vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0; - vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0; - - // set bloomwidth and bloomheight to the bloom resolution that will be - // used (often less than the screen resolution for faster rendering) - bloomwidth = min(r_view.width, r_bloom_resolution.integer); - bloomheight = min(r_view.height, bloomwidth * r_view.height / r_view.width); - // set up a texcoord array for the full resolution screen image - // (we have to keep this around to copy back during final render) - texcoord2f[0][0] = 0; - texcoord2f[0][1] = (float)r_view.height / (float)screenheight; - texcoord2f[0][2] = (float)r_view.width / (float)screenwidth; - texcoord2f[0][3] = (float)r_view.height / (float)screenheight; - texcoord2f[0][4] = (float)r_view.width / (float)screenwidth; - texcoord2f[0][5] = 0; - texcoord2f[0][6] = 0; - texcoord2f[0][7] = 0; - // set up a texcoord array for the reduced resolution bloom image - // (which will be additive blended over the screen image) - texcoord2f[1][0] = 0; - texcoord2f[1][1] = (float)bloomheight / (float)screenheight; - texcoord2f[1][2] = (float)bloomwidth / (float)screenwidth; - texcoord2f[1][3] = (float)bloomheight / (float)screenheight; - texcoord2f[1][4] = (float)bloomwidth / (float)screenwidth; - texcoord2f[1][5] = 0; - texcoord2f[1][6] = 0; - texcoord2f[1][7] = 0; - - if (dohdr) + if (r_bloomstate.enabled && r_bloomstate.hdr) { // render high dynamic range bloom effect // the bloom texture was made earlier this render, so we just need to // blend it onto the screen... - R_ResetViewRendering(); - GL_DepthTest(false); - R_Mesh_VertexPointer(vertex3f); + R_ResetViewRendering2D(); + R_Mesh_VertexPointer(r_screenvertex3f); R_Mesh_ColorPointer(NULL); GL_Color(1, 1, 1, 1); GL_BlendFunc(GL_ONE, GL_ONE); - R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_bloom)); - R_Mesh_TexCoordPointer(0, 2, texcoord2f[1]); + R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom)); + R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f); R_Mesh_Draw(0, 4, 2, polygonelements); r_refdef.stats.bloom_drawpixels += r_view.width * r_view.height; } - if (dobloom) + else if (r_bloomstate.enabled) { // render simple bloom effect + // copy the screen and shrink it and darken it for the bloom process + R_Bloom_CopyScreenTexture(r_bloom_colorscale.value); // make the bloom texture - R_Bloom_MakeTexture(true); + R_Bloom_MakeTexture(); // put the original screen image back in place and blend the bloom // texture on it - R_ResetViewRendering(); - GL_DepthTest(false); - R_Mesh_VertexPointer(vertex3f); + R_ResetViewRendering2D(); + R_Mesh_VertexPointer(r_screenvertex3f); R_Mesh_ColorPointer(NULL); GL_Color(1, 1, 1, 1); GL_BlendFunc(GL_ONE, GL_ZERO); // do both in one pass if possible - R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_screen)); - R_Mesh_TexCoordPointer(0, 2, texcoord2f[0]); + R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom)); + R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f); if (r_textureunits.integer >= 2 && gl_combine.integer) { R_Mesh_TexCombine(1, GL_ADD, GL_ADD, 1, 1); - R_Mesh_TexBind(1, R_GetTexture(r_bloom_texture_bloom)); - R_Mesh_TexCoordPointer(1, 2, texcoord2f[1]); + R_Mesh_TexBind(1, R_GetTexture(r_bloomstate.texture_screen)); + R_Mesh_TexCoordPointer(1, 2, r_bloomstate.screentexcoord2f); } else { @@ -1692,18 +1871,17 @@ static void R_BlendView(void) r_refdef.stats.bloom_drawpixels += r_view.width * r_view.height; // now blend on the bloom texture GL_BlendFunc(GL_ONE, GL_ONE); - R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_bloom)); - R_Mesh_TexCoordPointer(0, 2, texcoord2f[1]); + R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen)); + R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f); } R_Mesh_Draw(0, 4, 2, polygonelements); r_refdef.stats.bloom_drawpixels += r_view.width * r_view.height; } - if (doblend) + if (r_refdef.viewblend[3] >= (1.0f / 256.0f)) { // apply a color tint to the whole view - R_ResetViewRendering(); - GL_DepthTest(false); - R_Mesh_VertexPointer(vertex3f); + R_ResetViewRendering2D(); + R_Mesh_VertexPointer(r_screenvertex3f); R_Mesh_ColorPointer(NULL); GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]); @@ -1793,9 +1971,9 @@ void R_RenderView(void) if (!r_refdef.entities/* || !r_refdef.worldmodel*/) return; //Host_Error ("R_RenderView: NULL worldmodel"); + R_Shadow_UpdateWorldLightSelection(); + CHECKGLERROR - GL_ScissorTest(true); - GL_DepthMask(true); if (r_timereport_active) R_TimeReport("setup"); @@ -1803,12 +1981,14 @@ void R_RenderView(void) if (r_timereport_active) R_TimeReport("visibility"); - R_ResetViewRendering(); + R_ResetViewRendering3D(); R_ClearScreen(); if (r_timereport_active) R_TimeReport("clear"); + R_Bloom_StartFrame(); + // this produces a bloom texture to be used in R_BlendView() later if (r_hdr.integer) R_HDR_RenderBloomTexture(); @@ -1830,34 +2010,14 @@ extern void VM_AddPolygonsToMeshQueue (void); extern void R_DrawPortals (void); void R_RenderScene(void) { - DrawQ_Finish(); - // don't let sound skip if going slow if (r_refdef.extraupdate) S_ExtraUpdate (); - CHECKGLERROR - if (gl_support_fragment_shader) - { - qglUseProgramObjectARB(0);CHECKGLERROR - } - qglDepthFunc(GL_LEQUAL);CHECKGLERROR - qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR - qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR - - R_ResetViewRendering(); + R_ResetViewRendering3D(); R_MeshQueue_BeginScene(); - if (r_refdef.rtworldshadows || r_refdef.rtdlightshadows) - GL_SetupView_Mode_PerspectiveInfiniteFarClip(r_view.frustum_x, r_view.frustum_y, r_refdef.nearclip); - else - GL_SetupView_Mode_Perspective(r_view.frustum_x, r_view.frustum_y, r_refdef.nearclip, r_refdef.farclip); - - GL_SetupView_Orientation_FromEntity(&r_view.matrix); - - R_Shadow_UpdateWorldLightSelection(); - R_SkyStartFrame(); Matrix4x4_CreateTranslate(&r_waterscrollmatrix, sin(r_refdef.time) * 0.025 * r_waterscroll.value, sin(r_refdef.time * 0.8f) * 0.025 * r_waterscroll.value, 0); @@ -1898,6 +2058,17 @@ void R_RenderScene(void) if (r_refdef.extraupdate) S_ExtraUpdate (); + if (r_shadows.integer > 0 && r_refdef.lightmapintensity > 0) + { + R_DrawModelShadows(); + + R_ResetViewRendering3D(); + + // don't let sound skip if going slow + if (r_refdef.extraupdate) + S_ExtraUpdate (); + } + R_ShadowVolumeLighting(false); if (r_timereport_active) R_TimeReport("rtlights"); @@ -1958,13 +2129,7 @@ void R_RenderScene(void) if (r_refdef.extraupdate) S_ExtraUpdate (); - CHECKGLERROR - if (gl_support_fragment_shader) - { - qglUseProgramObjectARB(0);CHECKGLERROR - } - qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR - qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR + R_ResetViewRendering2D(); } /* @@ -2061,6 +2226,7 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight GL_DepthMask(true); } GL_DepthTest(!(ent->effects & EF_NODEPTHTEST)); + GL_CullFace((ent->flags & RENDER_NOCULLFACE) ? GL_NONE : GL_FRONT); // quake is backwards, this culls back faces R_Mesh_VertexPointer(nomodelvertex3f); if (r_refdef.fogenabled) { @@ -2318,13 +2484,14 @@ static void R_Texture_AddLayer(texture_t *t, qboolean depthmask, int blendfunc1, void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t) { + model_t *model = ent->model; + // FIXME: identify models using a better check than ent->model->brush.shadowmesh //int lightmode = ((ent->effects & EF_FULLBRIGHT) || ent->model->brush.shadowmesh) ? 0 : 2; // switch to an alternate material if this is a q1bsp animated material { texture_t *texture = t; - model_t *model = ent->model; int s = ent->skinnum; if ((unsigned int)s >= (unsigned int)model->numskins) s = 0; @@ -2355,7 +2522,7 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t) t->currentmaterialflags = t->basematerialflags; t->currentalpha = ent->alpha; - if (t->basematerialflags & MATERIALFLAG_WATERALPHA) + if (t->basematerialflags & MATERIALFLAG_WATERALPHA && (model->brush.supportwateralpha || r_novis.integer)) t->currentalpha *= r_wateralpha.value; if (!(ent->flags & RENDER_LIGHT)) t->currentmaterialflags |= MATERIALFLAG_FULLBRIGHT; @@ -2373,7 +2540,7 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t) t->colormapping = VectorLength2(ent->colormap_pantscolor) + VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f); t->basetexture = (!t->colormapping && t->currentskinframe->merged) ? t->currentskinframe->merged : t->currentskinframe->base; t->glosstexture = r_texture_white; - t->specularpower = 8; + t->specularpower = r_shadow_glossexponent.value; t->specularscale = 0; if (r_shadow_gloss.integer > 0) { @@ -2542,8 +2709,24 @@ rsurfmode_t rsurface_mode; texture_t *rsurface_glsl_texture; qboolean rsurface_glsl_uselightmap; +void RSurf_CleanUp(void) +{ + CHECKGLERROR + if (rsurface_mode == RSURFMODE_GLSL) + { + qglUseProgramObjectARB(0);CHECKGLERROR + } + GL_AlphaTest(false); + rsurface_mode = RSURFMODE_NONE; + rsurface_lightmaptexture = NULL; + rsurface_texture = NULL; + rsurface_glsl_texture = NULL; + rsurface_glsl_uselightmap = false; +} + void RSurf_ActiveEntity(const entity_render_t *ent, qboolean wantnormals, qboolean wanttangents) { + RSurf_CleanUp(); Matrix4x4_Transform(&ent->inversematrix, r_view.origin, rsurface_modelorg); rsurface_entity = ent; rsurface_model = ent->model; @@ -2591,26 +2774,6 @@ void RSurf_ActiveEntity(const entity_render_t *ent, qboolean wantnormals, qboole rsurface_svector3f = rsurface_modelsvector3f; rsurface_tvector3f = rsurface_modeltvector3f; rsurface_normal3f = rsurface_modelnormal3f; - rsurface_mode = RSURFMODE_NONE; - rsurface_lightmaptexture = NULL; - rsurface_texture = NULL; - rsurface_glsl_texture = NULL; - rsurface_glsl_uselightmap = false; -} - -void RSurf_CleanUp(void) -{ - CHECKGLERROR - if (rsurface_mode == RSURFMODE_GLSL) - { - qglUseProgramObjectARB(0);CHECKGLERROR - } - GL_AlphaTest(false); - rsurface_mode = RSURFMODE_NONE; - rsurface_lightmaptexture = NULL; - rsurface_texture = NULL; - rsurface_glsl_texture = NULL; - rsurface_glsl_uselightmap = false; } void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generatetangents, int texturenumsurfaces, msurface_t **texturesurfacelist) @@ -2682,6 +2845,13 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta rsurface_tvector3f = rsurface_array_deformedtvector3f; rsurface_normal3f = rsurface_array_deformednormal3f; } + else + { + rsurface_vertex3f = rsurface_modelvertex3f; + rsurface_svector3f = rsurface_modelsvector3f; + rsurface_tvector3f = rsurface_modeltvector3f; + rsurface_normal3f = rsurface_modelnormal3f; + } R_Mesh_VertexPointer(rsurface_vertex3f); } @@ -3350,20 +3520,20 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const if (t != surface->texture || rsurface_lightmaptexture != surface->lightmaptexture) { if (batchcount > 0) - R_DrawTextureSurfaceList(batchcount, texturesurfacelist); + if (!(rsurface_texture->currentmaterialflags & MATERIALFLAG_SKY)) // transparent sky is too difficult + R_DrawTextureSurfaceList(batchcount, texturesurfacelist); batchcount = 0; t = surface->texture; rsurface_lightmaptexture = surface->lightmaptexture; R_UpdateTextureInfo(ent, t); rsurface_texture = t->currentframe; } - if (rsurface_texture->currentmaterialflags & MATERIALFLAG_SKY) - continue; // transparent sky is too difficult texturesurfacelist[batchcount++] = surface; } if (batchcount > 0) - R_DrawTextureSurfaceList(batchcount, texturesurfacelist); + if (!(rsurface_texture->currentmaterialflags & MATERIALFLAG_SKY)) // transparent sky is too difficult + R_DrawTextureSurfaceList(batchcount, texturesurfacelist); RSurf_CleanUp(); } @@ -3537,12 +3707,8 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces) const int *elements; vec3_t v; CHECKGLERROR - GL_DepthTest(true); + GL_DepthTest(!r_showdisabledepthtest.integer); GL_DepthMask(true); - if (r_showdisabledepthtest.integer) - { - qglDepthFunc(GL_ALWAYS);CHECKGLERROR - } GL_BlendFunc(GL_ONE, GL_ZERO); R_Mesh_ColorPointer(NULL); R_Mesh_ResetTextureState(); @@ -3613,9 +3779,5 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces) } } rsurface_texture = NULL; - if (r_showdisabledepthtest.integer) - { - qglDepthFunc(GL_LEQUAL);CHECKGLERROR - } } }