]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_textures.c
446
[xonotic/darkplaces.git] / gl_textures.c
index 1a2889e7a74c7cf40d1a35b3b83cdc08b109aa9f..0e5812bd4d3d0b627f918d60882acc452524aa69 100644 (file)
@@ -2,13 +2,14 @@
 #include "quakedef.h"
 #include "image.h"
 #include "jpeg.h"
+#include "image_png.h"
 
-cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048"};
-cvar_t gl_max_scrapsize = {CVAR_SAVE, "gl_max_scrapsize", "256"};
-cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0"};
-cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1"};
-cvar_t r_precachetextures = {CVAR_SAVE, "r_precachetextures", "1"};
-cvar_t  gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1"};
+cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, note: this is automatically reduced to match video card capabilities (such as 256 on 3Dfx cards before Voodoo4/5)"};
+cvar_t gl_max_scrapsize = {CVAR_SAVE, "gl_max_scrapsize", "256", "size of scrap textures used to combine lightmaps"};
+cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0", "reduces resolution of textures by powers of 2, for example 1 will halve width/height, reducing texture memory usage by 75%"};
+cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1", "bilinear filters images when scaling them up to power of 2 size (mode 1), looks better than glquake (mode 0)"};
+cvar_t r_precachetextures = {CVAR_SAVE, "r_precachetextures", "1", "0 = never upload textures until used, 1 = upload most textures before use (exceptions: rarely used skin colormap layers), 2 = upload all textures before use (can increase texture memory usage significantly)"};
+cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
 
 int            gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
 int            gl_filter_mag = GL_LINEAR;
@@ -136,10 +137,13 @@ static int resizebuffersize = 0;
 static unsigned char *texturebuffer;
 static int texturebuffersize = 0;
 
-static int realmaxsize = 0;
-
 static textypeinfo_t *R_GetTexTypeInfo(int textype, int flags)
 {
+       if ((flags & (TEXF_PICMIP | TEXF_FRAGMENT)) == (TEXF_PICMIP | TEXF_FRAGMENT))
+       {
+               Host_Error("R_GetTexTypeInfo: TEXF_PICMIP can not be used with TEXF_FRAGMENT");
+               return NULL;
+       }
        if (flags & TEXF_ALPHA)
        {
                switch(textype)
@@ -147,12 +151,12 @@ static textypeinfo_t *R_GetTexTypeInfo(int textype, int flags)
                case TEXTYPE_PALETTE:
                        return &textype_palette_alpha;
                case TEXTYPE_RGB:
-                       Host_Error("R_GetTexTypeInfo: RGB format has no alpha, TEXF_ALPHA not allowed\n");
+                       Host_Error("R_GetTexTypeInfo: RGB format has no alpha, TEXF_ALPHA not allowed");
                        return NULL;
                case TEXTYPE_RGBA:
                        return &textype_rgba_alpha;
                default:
-                       Host_Error("R_GetTexTypeInfo: unknown texture format\n");
+                       Host_Error("R_GetTexTypeInfo: unknown texture format");
                        return NULL;
                }
        }
@@ -169,7 +173,7 @@ static textypeinfo_t *R_GetTexTypeInfo(int textype, int flags)
                case TEXTYPE_DSDT:
                        return &textype_dsdt;
                default:
-                       Host_Error("R_GetTexTypeInfo: unknown texture format\n");
+                       Host_Error("R_GetTexTypeInfo: unknown texture format");
                        return NULL;
                }
        }
@@ -215,13 +219,13 @@ void R_FreeTexture(rtexture_t *rt)
 
        glt = (gltexture_t *)rt;
        if (glt == NULL)
-               Host_Error("R_FreeTexture: texture == NULL\n");
+               Host_Error("R_FreeTexture: texture == NULL");
 
        for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
        if (*gltpointer == glt)
                *gltpointer = glt->chain;
        else
-               Host_Error("R_FreeTexture: texture \"%s\" not linked in pool\n", glt->identifier);
+               Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
 
        // note: if freeing a fragment texture, this will not make the claimed
        // space available for new textures unless all other fragments in the
@@ -236,7 +240,7 @@ void R_FreeTexture(rtexture_t *rt)
                        if (*gltimagepointer == image)
                                *gltimagepointer = image->imagechain;
                        else
-                               Host_Error("R_FreeTexture: image not linked in pool\n");
+                               Host_Error("R_FreeTexture: image not linked in pool");
                        if (image->texnum)
                                qglDeleteTextures(1, (GLuint *)&image->texnum);
                        if (image->blockallocation)
@@ -274,12 +278,12 @@ void R_FreeTexturePool(rtexturepool_t **rtexturepool)
        pool = (gltexturepool_t *)(*rtexturepool);
        *rtexturepool = NULL;
        if (pool->sentinel != TEXTUREPOOL_SENTINEL)
-               Host_Error("R_FreeTexturePool: pool already freed\n");
+               Host_Error("R_FreeTexturePool: pool already freed");
        for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
        if (*poolpointer == pool)
                *poolpointer = pool->next;
        else
-               Host_Error("R_FreeTexturePool: pool not linked\n");
+               Host_Error("R_FreeTexturePool: pool not linked");
        while (pool->gltchain)
                R_FreeTexture((rtexture_t *)pool->gltchain);
        if (pool->imagechain)
@@ -360,50 +364,81 @@ static void GL_TextureMode_f (void)
        }
 }
 
+static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
+{
+       int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
+
+       if (gl_max_size.integer > gl_max_texture_size)
+               Cvar_SetValue("gl_max_size", gl_max_texture_size);
+
+       switch (texturetype)
+       {
+       default:
+       case GLTEXTURETYPE_1D:
+       case GLTEXTURETYPE_2D:
+               maxsize = gl_max_texture_size;
+               break;
+       case GLTEXTURETYPE_3D:
+               maxsize = gl_max_3d_texture_size;
+               break;
+       case GLTEXTURETYPE_CUBEMAP:
+               maxsize = gl_max_cube_map_texture_size;
+               break;
+       }
+
+       if (flags & TEXF_PICMIP)
+       {
+               maxsize = min(maxsize, gl_max_size.integer);
+               picmip = gl_picmip.integer;
+       }
+
+       if (outwidth)
+       {
+               for (width2 = 1;width2 < inwidth;width2 <<= 1);
+               for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
+               *outwidth = max(1, width2);
+       }
+       if (outheight)
+       {
+               for (height2 = 1;height2 < inheight;height2 <<= 1);
+               for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
+               *outheight = max(1, height2);
+       }
+       if (outdepth)
+       {
+               for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
+               for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
+               *outdepth = max(1, depth2);
+       }
+}
+
+
 static int R_CalcTexelDataSize (gltexture_t *glt)
 {
-       int width2, height2, depth2, size, picmip;
+       int width2, height2, depth2, size;
+
        if (glt->flags & TEXF_FRAGMENT)
-               size = glt->width * glt->height * glt->depth;
-       else
+               return glt->width * glt->height * glt->depth * glt->textype->internalbytesperpixel * glt->image->sides;
+
+       GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->width, glt->height, glt->depth, &width2, &height2, &depth2);
+
+       size = width2 * height2 * depth2;
+
+       if (glt->flags & TEXF_MIPMAP)
        {
-               picmip = 0;
-               if (glt->flags & TEXF_PICMIP)
-                       picmip = gl_picmip.integer;
-               if (gl_max_size.integer > realmaxsize)
-                       Cvar_SetValue("gl_max_size", realmaxsize);
-               // calculate final size
-               for (width2 = 1;width2 < glt->width;width2 <<= 1);
-               for (height2 = 1;height2 < glt->height;height2 <<= 1);
-               for (depth2 = 1;depth2 < glt->depth;depth2 <<= 1);
-               for (width2 >>= picmip;width2 > gl_max_size.integer;width2 >>= 1);
-               for (height2 >>= picmip;height2 > gl_max_size.integer;height2 >>= 1);
-               for (depth2 >>= picmip;depth2 > gl_max_size.integer;depth2 >>= 1);
-               if (width2 < 1) width2 = 1;
-               if (height2 < 1) height2 = 1;
-               if (depth2 < 1) depth2 = 1;
-
-               size = 0;
-               if (glt->flags & TEXF_MIPMAP)
+               while (width2 > 1 || height2 > 1 || depth2 > 1)
                {
-                       while (width2 > 1 || height2 > 1 || depth2 > 1)
-                       {
-                               size += width2 * height2 * depth2;
-                               if (width2 > 1)
-                                       width2 >>= 1;
-                               if (height2 > 1)
-                                       height2 >>= 1;
-                               if (depth2 > 1)
-                                       depth2 >>= 1;
-                       }
-                       size++; // count the last 1x1 mipmap
+                       if (width2 > 1)
+                               width2 >>= 1;
+                       if (height2 > 1)
+                               height2 >>= 1;
+                       if (depth2 > 1)
+                               depth2 >>= 1;
+                       size += width2 * height2 * depth2;
                }
-               else
-                       size = width2 * height2 * depth2;
        }
-       size *= glt->textype->internalbytesperpixel * glt->image->sides;
 
-       return size;
+       return size * glt->textype->internalbytesperpixel * glt->image->sides;
 }
 
 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
@@ -460,21 +495,20 @@ static void R_TextureStats_f(void)
 
 static void r_textures_start(void)
 {
-       // deal with size limits of various drivers (3dfx in particular)
-       qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &realmaxsize);
-       CHECKGLERROR
        // LordHavoc: allow any alignment
        qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        CHECKGLERROR
 
        // use the largest scrap texture size we can (not sure if this is really a good idea)
-       for (block_size = 1;block_size < realmaxsize && block_size < gl_max_scrapsize.integer;block_size <<= 1);
+       for (block_size = 1;block_size * 2 <= gl_max_texture_size && block_size * 2 <= gl_max_scrapsize.integer;block_size *= 2);
 
        texturemempool = Mem_AllocPool("texture management", 0, NULL);
 
        // Disable JPEG screenshots if the DLL isn't loaded
        if (! JPEG_OpenLibrary ())
                Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
+       // TODO: support png screenshots?
+       PNG_OpenLibrary ();
 }
 
 static void r_textures_shutdown(void)
@@ -503,8 +537,8 @@ static void r_textures_newmap(void)
 
 void R_Textures_Init (void)
 {
-       Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f);
-       Cmd_AddCommand("r_texturestats", R_TextureStats_f);
+       Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
+       Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
        Cvar_RegisterVariable (&gl_max_scrapsize);
        Cvar_RegisterVariable (&gl_max_size);
        Cvar_RegisterVariable (&gl_picmip);
@@ -576,7 +610,7 @@ void R_MakeResizeBufferBigger(int size)
                resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
                colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
                if (!resizebuffer || !colorconvertbuffer)
-                       Host_Error("R_Upload: out of memory\n");
+                       Host_Error("R_Upload: out of memory");
        }
 }
 
@@ -688,7 +722,7 @@ static void R_Upload(gltexture_t *glt, unsigned char *data)
                                CHECKGLERROR
                                break;
                        default:
-                               Host_Error("R_Upload: fragment texture of type other than 1D, 2D, or 3D\n");
+                               Host_Error("R_Upload: fragment texture of type other than 1D, 2D, or 3D");
                                break;
                        }
                        GL_SetupTextureParameters(glt->image->flags, glt->image->texturetype);
@@ -724,7 +758,7 @@ static void R_Upload(gltexture_t *glt, unsigned char *data)
                        CHECKGLERROR
                        break;
                default:
-                       Host_Error("R_Upload: fragment texture of type other than 1D, 2D, or 3D\n");
+                       Host_Error("R_Upload: fragment texture of type other than 1D, 2D, or 3D");
                        break;
                }
        }
@@ -860,7 +894,7 @@ static void R_Upload(gltexture_t *glt, unsigned char *data)
 
 static void R_FindImageForTexture(gltexture_t *glt)
 {
-       int i, j, best, best2, x, y, z, w, h, d, picmip;
+       int i, j, best, best2, x, y, z, w, h, d;
        textypeinfo_t *texinfo;
        gltexturepool_t *pool;
        gltextureimage_t *image, **imagechainpointer;
@@ -961,21 +995,7 @@ static void R_FindImageForTexture(gltexture_t *glt)
                image->type = GLIMAGETYPE_TILE;
                image->blockallocation = NULL;
 
-               picmip = 0;
-               if (glt->flags & TEXF_PICMIP)
-                       picmip = gl_picmip.integer;
-               // calculate final size
-               if (gl_max_size.integer > realmaxsize)
-                       Cvar_SetValue("gl_max_size", realmaxsize);
-               for (image->width = 1;image->width < glt->width;image->width <<= 1);
-               for (image->height = 1;image->height < glt->height;image->height <<= 1);
-               for (image->depth = 1;image->depth < glt->depth;image->depth <<= 1);
-               for (image->width >>= picmip;image->width > gl_max_size.integer;image->width >>= 1);
-               for (image->height >>= picmip;image->height > gl_max_size.integer;image->height >>= 1);
-               for (image->depth >>= picmip;image->depth > gl_max_size.integer;image->depth >>= 1);
-               if (image->width < 1) image->width = 1;
-               if (image->height < 1) image->height = 1;
-               if (image->depth < 1) image->depth = 1;
+               GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->width, glt->height, glt->depth, &image->width, &image->height, &image->depth);
        }
        image->texturetype = glt->texturetype;
        image->glinternalformat = texinfo->glinternalformat;
@@ -1067,7 +1087,7 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
                break;
        case TEXTYPE_RGB:
                if (flags & TEXF_ALPHA)
-                       Host_Error("R_LoadTexture: RGB has no alpha, don't specify TEXF_ALPHA\n");
+                       Host_Error("R_LoadTexture: RGB has no alpha, don't specify TEXF_ALPHA");
                break;
        case TEXTYPE_RGBA:
                if (flags & TEXF_ALPHA)
@@ -1089,7 +1109,7 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
        case TEXTYPE_DSDT:
                break;
        default:
-               Host_Error("R_LoadTexture: unknown texture type\n");
+               Host_Error("R_LoadTexture: unknown texture type");
        }
 
        glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
@@ -1189,7 +1209,7 @@ void R_FragmentLocation3D(rtexture_t *rt, int *x, int *y, int *z, float *fx1, fl
                return;
        }
        if (!rt)
-               Host_Error("R_FragmentLocation: no texture supplied\n");
+               Host_Error("R_FragmentLocation: no texture supplied");
        glt = (gltexture_t *)rt;
        if (glt->flags & TEXF_FRAGMENT)
        {
@@ -1256,9 +1276,9 @@ void R_UpdateTexture(rtexture_t *rt, unsigned char *data)
 {
        gltexture_t *glt;
        if (rt == NULL)
-               Host_Error("R_UpdateTexture: no texture supplied\n");
+               Host_Error("R_UpdateTexture: no texture supplied");
        if (data == NULL)
-               Host_Error("R_UpdateTexture: no data supplied\n");
+               Host_Error("R_UpdateTexture: no data supplied");
        glt = (gltexture_t *)rt;
 
        // if it has not been uploaded yet, update the data that will be used when it is