// used for dlight push checking and other things
int r_framecount;
-// used for visibility checking
-qbyte r_pvsbits[(MAX_MAP_LEAFS+7)>>3];
-
-// TODO: dynamic resize? 65536 seems enough because q1bsp can't have more
-// than 32768, and q3bsp compilers have more like a 4096 limit
-qbyte r_worldsurfacevisible[MAX_MAP_LEAFS];
-
mplane_t frustum[4];
matrix4x4_t r_identitymatrix;
rtexturepool_t *r_main_texturepool;
rtexture_t *r_bloom_texture_screen;
rtexture_t *r_bloom_texture_bloom;
+rtexture_t *r_texture_blanknormalmap;
+rtexture_t *r_texture_white;
+rtexture_t *r_texture_black;
+rtexture_t *r_texture_notexture;
void R_ModulateColors(float *in, float *out, int verts, float r, float g, float b)
{
void gl_main_start(void)
{
+ int x, y;
+ qbyte pix[16][16][4];
+ qbyte data[4];
r_main_texturepool = R_AllocTexturePool();
r_bloom_texture_screen = NULL;
r_bloom_texture_bloom = NULL;
+ data[0] = 128; // normal X
+ data[1] = 128; // normal Y
+ data[2] = 255; // normal Z
+ data[3] = 128; // height
+ r_texture_blanknormalmap = R_LoadTexture2D(r_main_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
+ data[0] = 255;
+ data[1] = 255;
+ data[2] = 255;
+ data[3] = 255;
+ r_texture_white = R_LoadTexture2D(r_main_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 255;
+ r_texture_black = R_LoadTexture2D(r_main_texturepool, "blankblack", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
+ // this makes a light grey/dark grey checkerboard texture
+ for (y = 0;y < 16;y++)
+ {
+ for (x = 0;x < 16;x++)
+ {
+ if ((y < 8) ^ (x < 8))
+ {
+ pix[y][x][0] = 128;
+ pix[y][x][1] = 128;
+ pix[y][x][2] = 128;
+ pix[y][x][3] = 255;
+ }
+ else
+ {
+ pix[y][x][0] = 64;
+ pix[y][x][1] = 64;
+ pix[y][x][2] = 64;
+ pix[y][x][3] = 255;
+ }
+ }
+ }
+ r_texture_notexture = R_LoadTexture2D(mod_shared_texturepool, "notexture", 16, 16, &pix[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP, NULL);
}
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;
}
extern void CL_ParseEntityLump(char *entitystring);
static void R_MarkEntities (void)
{
- int i;
+ int i, renderimask;
entity_render_t *ent;
if (!r_drawentities.integer)
return;
- for (i = 0;i < r_refdef.numentities;i++)
+ renderimask = envmap ? (RENDER_EXTERIORMODEL | RENDER_VIEWMODEL) : (chase_active.integer ? 0 : RENDER_EXTERIORMODEL);
+ if (r_refdef.worldmodel && r_refdef.worldmodel->brush.BoxTouchingVisibleLeafs)
{
- ent = r_refdef.entities[i];
- Mod_CheckLoaded(ent->model);
- // some of the renderer still relies on origin...
- Matrix4x4_OriginFromMatrix(&ent->matrix, ent->origin);
- // some of the renderer still relies on scale...
- ent->scale = Matrix4x4_ScaleFromMatrix(&ent->matrix);
- R_UpdateEntLights(ent);
- if ((chase_active.integer || !(ent->flags & RENDER_EXTERIORMODEL))
- && (!VIS_CullBox(ent->mins, ent->maxs) || (ent->effects & EF_NODEPTHTEST))
- && (!envmap || !(ent->flags & (RENDER_VIEWMODEL | RENDER_EXTERIORMODEL))))
- ent->visframe = r_framecount;
+ // worldmodel can check visibility
+ for (i = 0;i < r_refdef.numentities;i++)
+ {
+ ent = r_refdef.entities[i];
+ Mod_CheckLoaded(ent->model);
+ // some of the renderer still relies on origin...
+ Matrix4x4_OriginFromMatrix(&ent->matrix, ent->origin);
+ // some of the renderer still relies on scale...
+ ent->scale = Matrix4x4_ScaleFromMatrix(&ent->matrix);
+ if (!(ent->flags & renderimask) && !R_CullBox(ent->mins, ent->maxs) && ((ent->effects & EF_NODEPTHTEST) || r_refdef.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.worldmodel, r_worldleafvisible, ent->mins, ent->maxs)))
+ {
+ R_UpdateEntLights(ent);
+ ent->visframe = r_framecount;
+ }
+ }
+ }
+ else
+ {
+ // no worldmodel or it can't check visibility
+ for (i = 0;i < r_refdef.numentities;i++)
+ {
+ ent = r_refdef.entities[i];
+ Mod_CheckLoaded(ent->model);
+ // some of the renderer still relies on origin...
+ Matrix4x4_OriginFromMatrix(&ent->matrix, ent->origin);
+ // some of the renderer still relies on scale...
+ ent->scale = Matrix4x4_ScaleFromMatrix(&ent->matrix);
+ if (!(ent->flags & renderimask) && !R_CullBox(ent->mins, ent->maxs) && (ent->effects & EF_NODEPTHTEST))
+ {
+ R_UpdateEntLights(ent);
+ ent->visframe = r_framecount;
+ }
+ }
}
}
GL_DepthMask(true);
GL_DepthTest(false);
R_Mesh_Matrix(&r_identitymatrix);
+ // vertex coordinates for a quad that covers the screen exactly
varray_vertex3f[0] = 0;varray_vertex3f[1] = 0;varray_vertex3f[2] = 0;
varray_vertex3f[3] = 1;varray_vertex3f[4] = 0;varray_vertex3f[5] = 0;
varray_vertex3f[6] = 1;varray_vertex3f[7] = 1;varray_vertex3f[8] = 0;
int screenwidth, screenheight, bloomwidth, bloomheight, x, dobloomblend, range;
float xoffset, yoffset, r;
c_bloom++;
+ // 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.realwidth;screenwidth *= 2);
for (screenheight = 1;screenheight < vid.realheight;screenheight *= 2);
+ // allocate textures as needed
+ 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);
+ // 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)
varray_texcoord2f[0][0] = 0;
varray_texcoord2f[0][1] = (float)r_view_height / (float)screenheight;
varray_texcoord2f[0][2] = (float)r_view_width / (float)screenwidth;
varray_texcoord2f[0][5] = 0;
varray_texcoord2f[0][6] = 0;
varray_texcoord2f[0][7] = 0;
+ // set up a texcoord array for the reduced resolution bloom image
+ // (which will be additive blended over the screen image)
varray_texcoord2f[1][0] = 0;
varray_texcoord2f[1][1] = (float)bloomheight / (float)screenheight;
varray_texcoord2f[1][2] = (float)bloomwidth / (float)screenwidth;
varray_texcoord2f[1][5] = 0;
varray_texcoord2f[1][6] = 0;
varray_texcoord2f[1][7] = 0;
- 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);
memset(&m, 0, sizeof(m));
m.pointer_vertex = varray_vertex3f;
m.pointer_texcoord[0] = varray_texcoord2f[0];
m.tex[0] = R_GetTexture(r_bloom_texture_screen);
R_Mesh_State(&m);
- // copy view to a texture
+ // copy view into the full resolution screen image texture
GL_ActiveTexture(0);
qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.realheight - (r_view_y + r_view_height), r_view_width, r_view_height);
c_bloomcopies++;
c_bloomcopypixels += r_view_width * r_view_height;
// now scale it down to the bloom size and raise to a power of itself
- qglViewport(r_view_x, vid.realheight - (r_view_y + bloomheight), bloomwidth, bloomheight);
+ // to darken it (this leaves the really bright stuff bright, and
+ // everything else becomes very dark)
// TODO: optimize with multitexture or GLSL
+ qglViewport(r_view_x, vid.realheight - (r_view_y + bloomheight), bloomwidth, bloomheight);
GL_BlendFunc(GL_ONE, GL_ZERO);
GL_Color(1, 1, 1, 1);
- R_Mesh_Draw(4, 2, polygonelements);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
c_bloomdraws++;
c_bloomdrawpixels += bloomwidth * bloomheight;
+ // 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(4, 2, polygonelements);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
c_bloomdraws++;
c_bloomdrawpixels += bloomwidth * bloomheight;
}
- // copy the bloom view to a texture
+ // we now have a darkened bloom image in the framebuffer, copy it into
+ // the bloom image texture for more processing
memset(&m, 0, sizeof(m));
m.pointer_vertex = varray_vertex3f;
m.tex[0] = R_GetTexture(r_bloom_texture_bloom);
qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.realheight - (r_view_y + bloomheight), bloomwidth, bloomheight);
c_bloomcopies++;
c_bloomcopypixels += bloomwidth * bloomheight;
- // blend on at multiple offsets vertically
+ // 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);
{
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
varray_texcoord2f[2][0] = xoffset+0;
varray_texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
varray_texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
varray_texcoord2f[2][5] = yoffset+0;
varray_texcoord2f[2][6] = xoffset+0;
varray_texcoord2f[2][7] = yoffset+0;
- r = r_bloom_intensity.value/(range*2+1)*(1 - fabs(x*x)/(float)(range*range));
+ // 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(4, 2, polygonelements);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
c_bloomdraws++;
c_bloomdrawpixels += bloomwidth * bloomheight;
GL_BlendFunc(GL_ONE, GL_ONE);
}
- // copy the blurred bloom view to a texture
+ // copy the vertically blurred bloom view to a texture
GL_ActiveTexture(0);
qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.realheight - (r_view_y + bloomheight), bloomwidth, bloomheight);
c_bloomcopies++;
c_bloomcopypixels += bloomwidth * bloomheight;
- // blend on at multiple offsets horizontally
+ // 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);
{
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
varray_texcoord2f[2][0] = xoffset+0;
varray_texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
varray_texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
varray_texcoord2f[2][5] = yoffset+0;
varray_texcoord2f[2][6] = xoffset+0;
varray_texcoord2f[2][7] = yoffset+0;
- r = r_bloom_intensity.value/(range*2+1)*(1 - fabs(x*x)/(float)(range*range));
+ // 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(4, 2, polygonelements);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
c_bloomdraws++;
c_bloomdrawpixels += bloomwidth * bloomheight;
GL_BlendFunc(GL_ONE, GL_ONE);
c_bloomcopypixels += bloomwidth * bloomheight;
// go back to full view area
qglViewport(r_view_x, vid.realheight - (r_view_y + r_view_height), r_view_width, r_view_height);
- // put the original view back in place
+ // put the original screen image back in place and blend the bloom
+ // texture on it
memset(&m, 0, sizeof(m));
m.pointer_vertex = varray_vertex3f;
m.tex[0] = R_GetTexture(r_bloom_texture_screen);
R_Mesh_State(&m);
GL_BlendFunc(GL_ONE, GL_ZERO);
GL_Color(1,1,1,1);
- R_Mesh_Draw(4, 2, polygonelements);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
c_bloomdraws++;
c_bloomdrawpixels += r_view_width * r_view_height;
// now blend on the bloom texture if multipass
R_Mesh_State(&m);
GL_BlendFunc(GL_ONE, GL_ONE);
GL_Color(1,1,1,1);
- R_Mesh_Draw(4, 2, polygonelements);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
c_bloomdraws++;
c_bloomdrawpixels += r_view_width * r_view_height;
}
R_Mesh_State(&m);
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]);
- R_Mesh_Draw(4, 2, polygonelements);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
}
}
R_SkyStartFrame();
- if (r_refdef.worldmodel && r_refdef.worldmodel->brush.FatPVS)
- r_refdef.worldmodel->brush.FatPVS(r_refdef.worldmodel, r_vieworigin, 2, r_pvsbits, sizeof(r_pvsbits));
R_WorldVisibility();
R_TimeReport("worldvis");
else
m.pointer_color = nomodelcolor4f;
R_Mesh_State(&m);
- R_Mesh_Draw(6, 8, nomodelelements);
+ R_Mesh_Draw(0, 6, 8, nomodelelements);
}
void R_DrawNoModel(entity_render_t *ent)
m.pointer_vertex = varray_vertex3f;
R_Mesh_State(&m);
GL_Color(cr, cg, cb, ca);
- R_Mesh_Draw(4, 2, polygonelements);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
}