+ }
+
+ // 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]);
+ 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;
+
+ // 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;
+ GL_Color(r, r, r, 1);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
+ r_refdef.stats.bloom_drawpixels += bloomwidth * 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 + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
+ r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
+
+ // 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);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
+ r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
+ GL_BlendFunc(GL_ONE, GL_ONE);
+ }
+
+ // 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;
+}
+
+void R_HDR_RenderBloomTexture(void)
+{
+ int oldwidth, oldheight;
+
+ 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;
+
+ // 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
+
+ r_view.colorscale = r_hdr_bloomintensity.value * r_hdr_scenebrightness.value;
+ R_RenderScene();
+
+ R_ResetViewRendering();
+
+ R_Bloom_MakeTexture(false);
+
+ 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)
+ {
+ // 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_Mesh_ColorPointer(NULL);
+ GL_Color(1, 1, 1, 1);
+ GL_BlendFunc(GL_ONE, GL_ONE);