]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_textures.c
some work on SV_TestEntityPosition and entity unsticking, now only checks against...
[xonotic/darkplaces.git] / gl_textures.c
index e8476796cdacb73a09ed4883dff79be2f8db27ac..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;
@@ -103,7 +104,7 @@ typedef struct gltexture_s
        int x, y, z, width, height, depth;
        // copy of the original texture(s) supplied to the upload function, for
        // delayed uploads (non-precached)
-       qbyte *inputtexels;
+       unsigned char *inputtexels;
        // original data size in *inputtexels
        int inputdatasize;
        // flags supplied to the LoadTexture function
@@ -131,15 +132,18 @@ gltexturepool_t;
 
 static gltexturepool_t *gltexturepoolchain = NULL;
 
-static qbyte *resizebuffer = NULL, *colorconvertbuffer;
+static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
 static int resizebuffersize = 0;
-static qbyte *texturebuffer;
+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);
@@ -573,10 +607,10 @@ void R_MakeResizeBufferBigger(int size)
                        Mem_Free(resizebuffer);
                if (colorconvertbuffer)
                        Mem_Free(colorconvertbuffer);
-               resizebuffer = (qbyte *)Mem_Alloc(texturemempool, resizebuffersize);
-               colorconvertbuffer = (qbyte *)Mem_Alloc(texturemempool, resizebuffersize);
+               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");
        }
 }
 
@@ -649,11 +683,11 @@ static void GL_SetupTextureParameters(int flags, int texturetype)
        CHECKGLERROR
 }
 
-static void R_Upload(gltexture_t *glt, qbyte *data)
+static void R_Upload(gltexture_t *glt, unsigned char *data)
 {
        int i, mip, width, height, depth;
        GLint oldbindtexnum;
-       qbyte *prevbuffer;
+       unsigned char *prevbuffer;
        prevbuffer = data;
 
        CHECKGLERROR
@@ -688,7 +722,7 @@ static void R_Upload(gltexture_t *glt, qbyte *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, qbyte *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, qbyte *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;
@@ -1011,7 +1031,7 @@ static void R_UploadTexture (gltexture_t *glt)
                Con_Printf("R_UploadTexture: Texture %s already uploaded and destroyed.  Can not upload original image again.  Uploaded blank texture.\n", glt->identifier);
 }
 
-static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, int sides, int flags, int textype, int texturetype, const qbyte *data, const unsigned int *palette)
+static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, int sides, int flags, int textype, int texturetype, const unsigned char *data, const unsigned int *palette)
 {
        int i, size;
        gltexture_t *glt;
@@ -1056,7 +1076,7 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
                        {
                                for (i = 0;i < size;i++)
                                {
-                                       if (((qbyte *)&palette[data[i]])[3] < 255)
+                                       if (((unsigned char *)&palette[data[i]])[3] < 255)
                                        {
                                                flags |= TEXF_ALPHA;
                                                break;
@@ -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));
@@ -1109,7 +1129,7 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
 
        if (data)
        {
-               glt->inputtexels = (qbyte *)Mem_Alloc(texturemempool, size);
+               glt->inputtexels = (unsigned char *)Mem_Alloc(texturemempool, size);
                if (glt->inputtexels == NULL)
                        Con_Printf ("R_LoadTexture: out of memory\n");
                else
@@ -1124,22 +1144,22 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
        return (rtexture_t *)glt;
 }
 
-rtexture_t *R_LoadTexture1D(rtexturepool_t *rtexturepool, const char *identifier, int width, const qbyte *data, int textype, int flags, const unsigned int *palette)
+rtexture_t *R_LoadTexture1D(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, int textype, int flags, const unsigned int *palette)
 {
        return R_SetupTexture(rtexturepool, identifier, width, 1, 1, 1, flags, textype, GLTEXTURETYPE_1D, data, palette);
 }
 
-rtexture_t *R_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const qbyte *data, int textype, int flags, const unsigned int *palette)
+rtexture_t *R_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, int textype, int flags, const unsigned int *palette)
 {
        return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
 }
 
-rtexture_t *R_LoadTexture3D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, const qbyte *data, int textype, int flags, const unsigned int *palette)
+rtexture_t *R_LoadTexture3D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, const unsigned char *data, int textype, int flags, const unsigned int *palette)
 {
        return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
 }
 
-rtexture_t *R_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *identifier, int width, const qbyte *data, int textype, int flags, const unsigned int *palette)
+rtexture_t *R_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, int textype, int flags, const unsigned int *palette)
 {
        return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
 }
@@ -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)
        {
@@ -1252,13 +1272,13 @@ int R_CompatibleFragmentWidth(int width, int textype, int flags)
        return width;
 }
 
-void R_UpdateTexture(rtexture_t *rt, qbyte *data)
+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