static void cl_gecko_linktexture( clgecko_t *instance ) {
// TODO: assert that instance->texture == NULL
instance->texture = R_LoadTexture2D( cl_geckotexturepool, instance->name,
- instance->texWidth, instance->texHeight, NULL, TEXTYPE_BGRA, TEXF_ALWAYSPRECACHE | TEXF_ALPHA | TEXF_PERSISTENT, NULL );
+ instance->texWidth, instance->texHeight, NULL, TEXTYPE_BGRA, TEXF_ALWAYSPRECACHE | TEXF_ALPHA | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, NULL );
R_MakeTextureDynamic( instance->texture, cl_gecko_updatecallback, instance );
CL_LinkDynTexture( instance->name, instance->texture );
}
static void LinkVideoTexture( clvideo_t *video ) {
video->cpif.tex = R_LoadTexture2D( cl_videotexturepool, video->cpif.name,
- video->cpif.width, video->cpif.height, NULL, TEXTYPE_BGRA, TEXF_ALWAYSPRECACHE | TEXF_PERSISTENT, NULL );
+ video->cpif.width, video->cpif.height, NULL, TEXTYPE_BGRA, TEXF_ALWAYSPRECACHE | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, NULL );
R_MakeTextureDynamic( video->cpif.tex, VideoUpdateCallback, video );
CL_LinkDynTexture( video->cpif.name, video->cpif.tex );
}
}
}
+int R_Mesh_TexBound(unsigned int unitnum, int id)
+{
+ gltextureunit_t *unit = gl_state.units + unitnum;
+ if (unitnum >= vid.teximageunits)
+ return 0;
+ if (id == GL_TEXTURE_2D)
+ return unit->t2d;
+ if (id == GL_TEXTURE_3D)
+ return unit->t3d;
+ if (id == GL_TEXTURE_CUBE_MAP_ARB)
+ return unit->tcubemap;
+ if (id == GL_TEXTURE_RECTANGLE_ARB)
+ return unit->trectangle;
+ return 0;
+}
+
void R_Mesh_TexBindAll(unsigned int unitnum, int tex2d, int tex3d, int texcubemap, int texrectangle)
{
gltextureunit_t *unit = gl_state.units + unitnum;
void R_Mesh_ColorPointer(const float *color4f, int bufferobject, size_t bufferoffset);
// sets the texcoord array pointer for an array unit
void R_Mesh_TexCoordPointer(unsigned int unitnum, unsigned int numcomponents, const float *texcoord, int bufferobject, size_t bufferoffset);
+// returns current texture bound to this identifier
+int R_Mesh_TexBound(unsigned int unitnum, int id);
// sets all textures bound to an image unit (multiple can be non-zero at once, according to OpenGL rules the highest one overrides the others)
void R_Mesh_TexBindAll(unsigned int unitnum, int tex2d, int tex3d, int texcubemap, int texrectangle);
// equivalent to R_Mesh_TexBindAll(unitnum,tex2d,0,0,0)
pic->height = height;
if (pic->tex)
R_FreeTexture(pic->tex);
- pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, TEXF_PRECACHE | (alpha ? TEXF_ALPHA : 0), NULL);
+ pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, TEXF_PRECACHE | (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, NULL);
return pic;
}
}
else
{
- r_texture_fogattenuation = R_LoadTexture2D(r_main_texturepool, "fogattenuation", FOGWIDTH, 1, &data1[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT, NULL);
- //r_texture_fogintensity = R_LoadTexture2D(r_main_texturepool, "fogintensity", FOGWIDTH, 1, &data2[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
+ r_texture_fogattenuation = R_LoadTexture2D(r_main_texturepool, "fogattenuation", FOGWIDTH, 1, &data1[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, NULL);
+ //r_texture_fogintensity = R_LoadTexture2D(r_main_texturepool, "fogintensity", FOGWIDTH, 1, &data2[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALLOWUPDATES, NULL);
}
}
}
else
{
- r_texture_gammaramps = R_LoadTexture2D(r_main_texturepool, "gammaramps", RAMPWIDTH, 1, &rampbgr[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT, NULL);
+ r_texture_gammaramps = R_LoadTexture2D(r_main_texturepool, "gammaramps", RAMPWIDTH, 1, &rampbgr[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, NULL);
}
}
}
}
// update lightmaps if needed
if (update)
+ {
+ int updated = 0;
for (j = model->firstmodelsurface, endj = model->firstmodelsurface + model->nummodelsurfaces;j < endj;j++)
+ {
if (r_refdef.viewcache.world_surfacevisible[j])
+ {
if (update[j])
+ {
+ updated++;
R_BuildLightMap(r_refdef.scene.worldentity, surfaces + j);
+ }
+ }
+ }
+ if (updated)
+ {
+ int count = model->brushq3.num_mergedlightmaps;
+ for (i = 0;i < count;i++)
+ {
+ if (model->brushq3.data_deluxemaps[i])
+ R_FlushTexture(model->brushq3.data_deluxemaps[i]);
+ if (model->brushq3.data_lightmaps[i])
+ R_FlushTexture(model->brushq3.data_lightmaps[i]);
+ }
+ }
+ }
// don't do anything if there were no surfaces
if (!numsurfacelist)
{
return;
}
// update lightmaps if needed
+ if (update)
+ {
+ int updated = 0;
+ for (j = model->firstmodelsurface, endj = model->firstmodelsurface + model->nummodelsurfaces;j < endj;j++)
+ {
+ if (update[j])
+ {
+ updated++;
+ R_BuildLightMap(ent, surfaces + j);
+ }
+ }
+ if (updated)
+ {
+ int count = model->brushq3.num_mergedlightmaps;
+ for (i = 0;i < count;i++)
+ {
+ if (model->brushq3.data_deluxemaps[i])
+ R_FlushTexture(model->brushq3.data_deluxemaps[i]);
+ if (model->brushq3.data_lightmaps[i])
+ R_FlushTexture(model->brushq3.data_lightmaps[i]);
+ }
+ }
+ }
if (update)
for (j = model->firstmodelsurface, endj = model->firstmodelsurface + model->nummodelsurfaces;j < endj;j++)
if (update[j])
cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
+cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "0", "use alternate path for dynamic lightmap updates"};
int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
int gl_filter_mag = GL_LINEAR;
void *updatacallback_data;
// --- [11/22/2007 Black]
+ // stores backup copy of texture for deferred texture updates (r_nopartialtextureupdates cvar)
+ unsigned char *bufferpixels;
+ qboolean buffermodified;
+
// pointer to texturepool (check this to see if the texture is allocated)
struct gltexturepool_s *pool;
// pointer to next texture in texturepool chain
// change all the existing mipmap texture objects
// FIXME: force renderer(/client/something?) restart instead?
CHECKGLERROR
+ GL_ActiveTexture(0);
for (pool = gltexturepoolchain;pool;pool = pool->next)
{
for (glt = pool->gltchain;glt;glt = glt->chain)
// only update already uploaded images
if (!(glt->flags & (GLTEXF_UPLOAD | TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
{
- qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
+ oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
if (glt->flags & TEXF_MIPMAP)
{
Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
Cvar_RegisterVariable (&gl_texturecompression_sky);
Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
+ Cvar_RegisterVariable (&gl_nopartialtextureupdates);
R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
}
Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
CHECKGLERROR
+ GL_ActiveTexture(0);
for (pool = gltexturepoolchain;pool;pool = pool->next)
{
for (glt = pool->gltchain;glt;glt = glt->chain)
// only update already uploaded images
if ((glt->flags & (GLTEXF_UPLOAD | TEXF_MIPMAP)) == TEXF_MIPMAP)
{
- qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
+ oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
CHECKGLERROR
// we need to restore the texture binding after finishing the upload
- qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
+ GL_ActiveTexture(0);
+ oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
// these are rounded up versions of the size to do better resampling
prevbuffer = colorconvertbuffer;
}
- if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP | GLTEXF_UPLOAD)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth)
+ if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP | GLTEXF_UPLOAD)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth && (fragx != 0 || fragy != 0 || fragwidth != glt->tilewidth || fragheight != glt->tileheight))
{
// update a portion of the image
switch(glt->texturetype)
CHECKGLERROR
qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
+ if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
+ glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
}
else if (data)
{
if (!glt->texnum)
Host_Error("R_UpdateTexture: texture has not been uploaded yet");
// update part of the texture
- R_Upload(glt, data, x, y, 0, width, height, 1);
+ if (glt->bufferpixels)
+ {
+ int j;
+ int bpp = glt->bytesperpixel;
+ int inputskip = width*bpp;
+ int outputskip = glt->tilewidth*bpp;
+ const unsigned char *input = data;
+ unsigned char *output = glt->bufferpixels;
+ if (x < 0)
+ {
+ width += x;
+ input -= x*bpp;
+ x = 0;
+ }
+ if (y < 0)
+ {
+ height += y;
+ input -= y*inputskip;
+ y = 0;
+ }
+ if (width > glt->tilewidth - x)
+ width = glt->tilewidth - x;
+ if (height > glt->tileheight - y)
+ height = glt->tileheight - y;
+ if (width < 1 || height < 1)
+ return;
+ glt->buffermodified = true;
+ output += y*outputskip + x*bpp;
+ for (j = 0;j < height;j++, output += outputskip, input += inputskip)
+ memcpy(output, input, width*bpp);
+ if (!(glt->flags & TEXF_MANUALFLUSHUPDATES))
+ R_FlushTexture(rt);
+ }
+ else
+ R_Upload(glt, data, x, y, 0, width, height, 1);
+}
+
+void R_FlushTexture(rtexture_t *rt)
+{
+ gltexture_t *glt;
+ if (rt == NULL)
+ Host_Error("R_FlushTexture: no texture supplied");
+
+ // update part of the texture
+ glt = (gltexture_t *)rt;
+
+ if (!glt->buffermodified || !glt->bufferpixels)
+ return;
+ glt->buffermodified = false;
+ R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
}
void R_ClearTexture (rtexture_t *rt)
lightmaptexture = NULL;
deluxemaptexture = r_texture_blanknormalmap;
- lightmapnumber = 1;
+ lightmapnumber = 0;
lightmapsize = bound(256, gl_max_lightmapsize.integer, (int)vid.maxtexturesize_2d);
totallightmapsamples = 0;
if (loadmodel->texturepool == NULL)
loadmodel->texturepool = R_AllocTexturePool();
// could not find room, make a new lightmap
- lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+ loadmodel->brushq3.num_mergedlightmaps = lightmapnumber + 1;
+ loadmodel->brushq3.data_lightmaps = Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_lightmaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_lightmaps[0]));
+ loadmodel->brushq3.data_deluxemaps = Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_deluxemaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_deluxemaps[0]));
+ loadmodel->brushq3.data_lightmaps[lightmapnumber] = lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE | TEXF_ALLOWUPDATES | TEXF_MANUALFLUSHUPDATES, NULL);
if (loadmodel->brushq1.nmaplightdata)
- deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+ loadmodel->brushq3.data_deluxemaps[lightmapnumber] = deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE | TEXF_ALLOWUPDATES | TEXF_MANUALFLUSHUPDATES, NULL);
lightmapnumber++;
Mod_AllocLightmap_Reset(&allocState);
Mod_AllocLightmap_Block(&allocState, ssize, tsize, &lightmapx, &lightmapy);
;
if (developer_loading.integer)
Con_Printf("lightmap merge texture #%i is %ix%i (%i of %i used)\n", lightmapindex, mergewidth*size, mergeheight*size, min(j, mergewidth*mergeheight), mergewidth*mergeheight);
- loadmodel->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : 0), NULL);
+ loadmodel->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : TEXF_ALLOWUPDATES | TEXF_MANUALFLUSHUPDATES), NULL);
if (loadmodel->brushq3.data_deluxemaps)
- loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : 0), NULL);
+ loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : TEXF_ALLOWUPDATES | TEXF_MANUALFLUSHUPDATES), NULL);
}
mergewidth = R_TextureWidth(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
mergeheight = R_TextureHeight(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
}
}
+ for (i = 0;i < loadmodel->brushq3.num_mergedlightmaps;i++)
+ {
+ if (loadmodel->brushq3.data_deluxemaps[i])
+ R_FlushTexture(loadmodel->brushq3.data_deluxemaps[i]);
+ if (loadmodel->brushq3.data_lightmaps[i])
+ R_FlushTexture(loadmodel->brushq3.data_lightmaps[i]);
+ }
+
Mem_Free(convertedpixels);
if(external)
{
#define TEXF_COMPARE 0x00000800
// indicates texture should use lower precision where supported
#define TEXF_LOWPRECISION 0x00001000
+// indicates texture should support R_UpdateTexture
+#define TEXF_ALLOWUPDATES 0x00002000
+// indicates texture should support R_FlushTexture (improving speed on multiple partial updates per draw)
+#define TEXF_MANUALFLUSHUPDATES 0x00004000
// used for checking if textures mismatch
#define TEXF_IMPORTANTBITS (TEXF_ALPHA | TEXF_MIPMAP | TEXF_CLAMP | TEXF_FORCENEAREST | TEXF_FORCELINEAR | TEXF_PICMIP | TEXF_COMPRESS | TEXF_COMPARE | TEXF_LOWPRECISION)
// update a portion of the image data of a texture, used by lightmap updates
// and procedural textures such as video playback.
+// if TEXF_MANUALFLUSHUPDATES is used, you MUST call R_FlushTexture to apply the updates
void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height);
+// if TEXF_MANUALFLUSHUPDATES is used, call this to apply the updates,
+// otherwise this function does nothing
+void R_FlushTexture(rtexture_t *rt);
// returns the renderer dependent texture slot number (call this before each
// use, as a texture might not have been precached)