#include "quakedef.h"
#include "image.h"
#include "jpeg.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"};
-
+#include "image_png.h"
+#include "intoverflow.h"
+
+#ifndef GL_TEXTURE_3D
+#define GL_TEXTURE_3D 0x806F
+#endif
+
+cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
+cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
+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 gl_picmip_world = {CVAR_SAVE, "gl_picmip_world", "0", "extra picmip level for world textures (may be negative, which will then reduce gl_picmip for these)"};
+cvar_t r_picmipworld = {CVAR_SAVE, "r_picmipworld", "1", "whether gl_picmip shall apply to world textures too (setting this to 0 is a shorthand for gl_picmip_world -9999999)"};
+cvar_t gl_picmip_sprites = {CVAR_SAVE, "gl_picmip_sprites", "0", "extra picmip level for sprite textures (may be negative, which will then reduce gl_picmip for these)"};
+cvar_t r_picmipsprites = {CVAR_SAVE, "r_picmipsprites", "1", "make gl_picmip affect sprites too (saves some graphics memory in sprite heavy games) (setting this to 0 is a shorthand for gl_picmip_sprites -9999999)"};
+cvar_t gl_picmip_other = {CVAR_SAVE, "gl_picmip_other", "0", "extra picmip level for other textures (may be negative, which will then reduce gl_picmip for these)"};
+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 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"};
+cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
+cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
+cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
+cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
+cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
+cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
+cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
+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_texturecompression_reflectmask = {CVAR_SAVE, "gl_texturecompression_reflectmask", "1", "whether to compress reflection cubemap masks (mask of which areas of the texture should reflect the generic shiny cubemap)"};
+cvar_t gl_texturecompression_sprites = {CVAR_SAVE, "gl_texturecompression_sprites", "1", "whether to compress sprites"};
+cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "0", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"};
+cvar_t r_texture_dds_load_alphamode = {0, "r_texture_dds_load_alphamode", "1", "0: trust DDPF_ALPHAPIXELS flag, 1: texture format and brute force search if ambiguous, 2: texture format only"};
+cvar_t r_texture_dds_load_logfailure = {0, "r_texture_dds_load_logfailure", "0", "log missing DDS textures to ddstexturefailures.log, 0: done log, 1: log with no optional textures (_norm, glow etc.). 2: log all"};
+cvar_t r_texture_dds_swdecode = {0, "r_texture_dds_swdecode", "0", "0: don't software decode DDS, 1: software decode DDS if unsupported, 2: always software decode DDS"};
+
+qboolean gl_filter_force = false;
int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
int gl_filter_mag = GL_LINEAR;
static mempool_t *texturemempool;
+static memexpandablearray_t texturearray;
// note: this must not conflict with TEXF_ flags in r_textures.h
-// cleared when a texture is uploaded
-#define GLTEXF_UPLOAD 0x00010000
// bitmask for mismatch checking
#define GLTEXF_IMPORTANTBITS (0)
-// set when image is uploaded and freed
-#define GLTEXF_DESTROYED 0x00040000
+// dynamic texture (treat texnum == 0 differently)
+#define GLTEXF_DYNAMIC 0x00080000
-// size of images which hold fragment textures, ignores picmip and max_size
-static int block_size;
-
-typedef struct
+typedef struct textypeinfo_s
{
- int textype;
+ const char *name;
+ textype_t textype;
int inputbytesperpixel;
int internalbytesperpixel;
- int glformat;
+ float glinternalbytesperpixel;
int glinternalformat;
+ int glformat;
+ int gltype;
}
textypeinfo_t;
-static textypeinfo_t textype_palette = {TEXTYPE_PALETTE, 1, 4, GL_RGBA , 3};
-static textypeinfo_t textype_rgb = {TEXTYPE_RGB , 3, 3, GL_RGB , 3};
-static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, GL_RGBA , 3};
-static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE, 1, 4, GL_RGBA , 4};
-static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, GL_RGBA , 4};
-static textypeinfo_t textype_dsdt = {TEXTYPE_DSDT , 2, 2, GL_DSDT_NV, GL_DSDT8_NV};
-
-// a tiling texture (most common type)
-#define GLIMAGETYPE_TILE 0
-// a fragments texture (contains one or more fragment textures)
-#define GLIMAGETYPE_FRAGMENTS 1
-
-#define GLTEXTURETYPE_1D 0
-#define GLTEXTURETYPE_2D 1
-#define GLTEXTURETYPE_3D 2
-#define GLTEXTURETYPE_CUBEMAP 3
-
-static int gltexturetypeenums[4] = {GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB};
-static int gltexturetypebindingenums[4] = {GL_TEXTURE_BINDING_1D, GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB};
-static int gltexturetypedimensions[4] = {1, 2, 3, 2};
-static int cubemapside[6] =
+#ifdef USE_GLES2
+
+// we use these internally even if we never deliver such data to the driver
+#define GL_BGR 0x80E0
+#define GL_BGRA 0x80E1
+
+// framebuffer texture formats
+// GLES2 devices rarely support depth textures, so we actually use a renderbuffer there
+static textypeinfo_t textype_shadowmap16_comp = {"shadowmap16_comp", TEXTYPE_SHADOWMAP16_COMP , 2, 2, 2.0f, GL_DEPTH_COMPONENT16 , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
+static textypeinfo_t textype_shadowmap16_raw = {"shadowmap16_raw", TEXTYPE_SHADOWMAP16_RAW , 2, 2, 2.0f, GL_DEPTH_COMPONENT16 , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
+static textypeinfo_t textype_shadowmap24_comp = {"shadowmap24_comp", TEXTYPE_SHADOWMAP24_COMP , 2, 2, 2.0f, GL_DEPTH_COMPONENT16 , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
+static textypeinfo_t textype_shadowmap24_raw = {"shadowmap24_raw", TEXTYPE_SHADOWMAP24_RAW , 2, 2, 2.0f, GL_DEPTH_COMPONENT16 , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
+static textypeinfo_t textype_depth16 = {"depth16", TEXTYPE_DEPTHBUFFER16 , 2, 2, 2.0f, GL_DEPTH_COMPONENT16 , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
+static textypeinfo_t textype_depth24 = {"depth24", TEXTYPE_DEPTHBUFFER24 , 2, 2, 2.0f, GL_DEPTH_COMPONENT16 , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
+static textypeinfo_t textype_depth24stencil8 = {"depth24stencil8", TEXTYPE_DEPTHBUFFER24STENCIL8, 2, 2, 2.0f, GL_DEPTH_COMPONENT16 , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
+static textypeinfo_t textype_colorbuffer = {"colorbuffer", TEXTYPE_COLORBUFFER , 2, 2, 2.0f, GL_RGB565 , GL_RGBA , GL_UNSIGNED_SHORT_5_6_5};
+static textypeinfo_t textype_colorbuffer16f = {"colorbuffer16f", TEXTYPE_COLORBUFFER16F , 2, 2, 2.0f, GL_RGBA16F , GL_RGBA , GL_HALF_FLOAT_ARB};
+static textypeinfo_t textype_colorbuffer32f = {"colorbuffer32f", TEXTYPE_COLORBUFFER32F , 2, 2, 2.0f, GL_RGBA32F , GL_RGBA , GL_FLOAT};
+
+// image formats:
+static textypeinfo_t textype_alpha = {"alpha", TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , GL_ALPHA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_palette = {"palette", TEXTYPE_PALETTE , 1, 4, 4.0f, GL_RGBA , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_palette_alpha = {"palette_alpha", TEXTYPE_PALETTE , 1, 4, 4.0f, GL_RGBA , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba = {"rgba", TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba_alpha = {"rgba_alpha", TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra = {"bgra", TEXTYPE_BGRA , 4, 4, 4.0f, GL_RGBA , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra_alpha = {"bgra_alpha", TEXTYPE_BGRA , 4, 4, 4.0f, GL_RGBA , GL_BGRA , GL_UNSIGNED_BYTE };
+#ifdef __ANDROID__
+static textypeinfo_t textype_etc1 = {"etc1", TEXTYPE_ETC1 , 1, 3, 0.5f, GL_ETC1_RGB8_OES , 0 , 0 };
+#endif
+#else
+// framebuffer texture formats
+static textypeinfo_t textype_shadowmap16_comp = {"shadowmap16_comp", TEXTYPE_SHADOWMAP16_COMP , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
+static textypeinfo_t textype_shadowmap16_raw = {"shadowmap16_raw", TEXTYPE_SHADOWMAP16_RAW , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
+static textypeinfo_t textype_shadowmap24_comp = {"shadowmap24_comp", TEXTYPE_SHADOWMAP24_COMP , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
+static textypeinfo_t textype_shadowmap24_raw = {"shadowmap24_raw", TEXTYPE_SHADOWMAP24_RAW , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
+static textypeinfo_t textype_depth16 = {"depth16", TEXTYPE_DEPTHBUFFER16 , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
+static textypeinfo_t textype_depth24 = {"depth24", TEXTYPE_DEPTHBUFFER24 , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
+static textypeinfo_t textype_depth24stencil8 = {"depth24stencil8", TEXTYPE_DEPTHBUFFER24STENCIL8, 4, 4, 4.0f, GL_DEPTH24_STENCIL8_EXT , GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT};
+static textypeinfo_t textype_colorbuffer = {"colorbuffer", TEXTYPE_COLORBUFFER , 4, 4, 4.0f, GL_RGBA , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_colorbuffer16f = {"colorbuffer16f", TEXTYPE_COLORBUFFER16F , 8, 8, 8.0f, GL_RGBA16F_ARB , GL_RGBA , GL_HALF_FLOAT_ARB};
+static textypeinfo_t textype_colorbuffer32f = {"colorbuffer32f", TEXTYPE_COLORBUFFER32F , 16, 16, 16.0f, GL_RGBA32F_ARB , GL_RGBA , GL_FLOAT };
+
+// image formats:
+static textypeinfo_t textype_alpha = {"alpha", TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , GL_ALPHA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_palette = {"palette", TEXTYPE_PALETTE , 1, 4, 4.0f, GL_RGB , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_palette_alpha = {"palette_alpha", TEXTYPE_PALETTE , 1, 4, 4.0f, GL_RGBA , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba = {"rgba", TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGB , GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba_alpha = {"rgba_alpha", TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba_compress = {"rgba_compress", TEXTYPE_RGBA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba_alpha_compress = {"rgba_alpha_compress", TEXTYPE_RGBA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra = {"bgra", TEXTYPE_BGRA , 4, 4, 4.0f, GL_RGB , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra_alpha = {"bgra_alpha", TEXTYPE_BGRA , 4, 4, 4.0f, GL_RGBA , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra_compress = {"bgra_compress", TEXTYPE_BGRA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra_alpha_compress = {"bgra_alpha_compress", TEXTYPE_BGRA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_dxt1 = {"dxt1", TEXTYPE_DXT1 , 4, 0, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , 0 , 0 };
+static textypeinfo_t textype_dxt1a = {"dxt1a", TEXTYPE_DXT1A , 4, 0, 0.5f, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT , 0 , 0 };
+static textypeinfo_t textype_dxt3 = {"dxt3", TEXTYPE_DXT3 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT , 0 , 0 };
+static textypeinfo_t textype_dxt5 = {"dxt5", TEXTYPE_DXT5 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT , 0 , 0 };
+static textypeinfo_t textype_sRGB_palette = {"sRGB_palette", TEXTYPE_PALETTE , 1, 4, 4.0f, GL_SRGB_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_sRGB_palette_alpha = {"sRGB_palette_alpha", TEXTYPE_PALETTE , 1, 4, 4.0f, GL_SRGB_ALPHA_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_sRGB_rgba = {"sRGB_rgba", TEXTYPE_RGBA , 4, 4, 4.0f, GL_SRGB_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_sRGB_rgba_alpha = {"sRGB_rgba_alpha", TEXTYPE_RGBA , 4, 4, 4.0f, GL_SRGB_ALPHA_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_sRGB_rgba_compress = {"sRGB_rgba_compress", TEXTYPE_RGBA , 4, 4, 0.5f, GL_COMPRESSED_SRGB_S3TC_DXT1_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_sRGB_rgba_alpha_compress = {"sRGB_rgba_alpha_compress", TEXTYPE_RGBA , 4, 4, 1.0f, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_sRGB_bgra = {"sRGB_bgra", TEXTYPE_BGRA , 4, 4, 4.0f, GL_SRGB_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_sRGB_bgra_alpha = {"sRGB_bgra_alpha", TEXTYPE_BGRA , 4, 4, 4.0f, GL_SRGB_ALPHA_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_sRGB_bgra_compress = {"sRGB_bgra_compress", TEXTYPE_BGRA , 4, 4, 0.5f, GL_COMPRESSED_SRGB_S3TC_DXT1_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_sRGB_bgra_alpha_compress = {"sRGB_bgra_alpha_compress", TEXTYPE_BGRA , 4, 4, 1.0f, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_sRGB_dxt1 = {"sRGB_dxt1", TEXTYPE_DXT1 , 4, 0, 0.5f, GL_COMPRESSED_SRGB_S3TC_DXT1_EXT , 0 , 0 };
+static textypeinfo_t textype_sRGB_dxt1a = {"sRGB_dxt1a", TEXTYPE_DXT1A , 4, 0, 0.5f, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, 0 , 0 };
+static textypeinfo_t textype_sRGB_dxt3 = {"sRGB_dxt3", TEXTYPE_DXT3 , 4, 0, 1.0f, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, 0 , 0 };
+static textypeinfo_t textype_sRGB_dxt5 = {"sRGB_dxt5", TEXTYPE_DXT5 , 4, 0, 1.0f, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, 0 , 0 };
+#endif
+
+typedef enum gltexturetype_e
{
- GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
- GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
- GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
- GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
- GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
- GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
-};
+ GLTEXTURETYPE_2D,
+ GLTEXTURETYPE_3D,
+ GLTEXTURETYPE_CUBEMAP,
+ GLTEXTURETYPE_TOTAL
+}
+gltexturetype_t;
-// a gltextureimage can have one (or more if fragments) gltextures inside
-typedef struct gltextureimage_s
+static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP};
+#ifdef GL_TEXTURE_WRAP_R
+static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2};
+#endif
+static int cubemapside[6] =
{
- struct gltextureimage_s *imagechain;
- int texturecount;
- int type; // one of the GLIMAGETYPE_ values
- int texturetype; // one of the GLTEXTURETYPE_ values
- int sides; // 1 or 6 depending on texturetype
- int texnum; // GL texture slot number
- int width, height, depth; // 3D texture support
- int bytesperpixel; // bytes per pixel
- int glformat; // GL_RGB or GL_RGBA
- int glinternalformat; // 3 or 4
- int flags;
- short *blockallocation; // fragment allocation (2D only)
-}
-gltextureimage_t;
+ GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
+};
typedef struct gltexture_s
{
- // this field is exposed to the R_GetTexture macro, for speed reasons
- // (must be identical in rtexture_t)
+ // this portion of the struct is exposed to the R_GetTexture macro for
+ // speed reasons, must be identical in rtexture_t!
int texnum; // GL texture slot number
+ int renderbuffernum; // GL renderbuffer slot number
+ qboolean dirty; // indicates that R_RealGetTexture should be called
+ qboolean glisdepthstencil; // indicates that FBO attachment has to be GL_DEPTH_STENCIL_ATTACHMENT
+ int gltexturetypeenum; // used by R_Mesh_TexBind
+
+ // dynamic texture stuff [11/22/2007 Black]
+ updatecallback_t updatecallback;
+ void *updatecallback_data;
+ // --- [11/22/2007 Black]
+
+ // stores backup copy of texture for deferred texture updates (gl_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
struct gltexture_s *chain;
- // pointer into gltextureimage array
- gltextureimage_t *image;
// name of the texture (this might be removed someday), no duplicates
- char identifier[32];
- // location in the image, and size
- int x, y, z, width, height, depth;
+ char identifier[MAX_QPATH + 32];
+ // original data size in *inputtexels
+ int inputwidth, inputheight, inputdepth;
// 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
// (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
int flags;
+ // picmip level
+ int miplevel;
// pointer to one of the textype_ structs
textypeinfo_t *textype;
// one of the GLTEXTURETYPE_ values
int texturetype;
// palette if the texture is TEXTYPE_PALETTE
const unsigned int *palette;
+ // actual stored texture size after gl_picmip and gl_max_size are applied
+ int tilewidth, tileheight, tiledepth;
+ // 1 or 6 depending on texturetype
+ int sides;
+ // how many mipmap levels in this texture
+ int miplevels;
+ // bytes per pixel
+ int bytesperpixel;
+ // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
+ int glformat;
+ // 3 or 4
+ int glinternalformat;
+ // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
+ int gltype;
}
gltexture_t;
typedef struct gltexturepool_s
{
unsigned int sentinel;
- struct gltextureimage_s *imagechain;
struct gltexture_s *gltchain;
struct gltexturepool_s *next;
}
static gltexturepool_t *gltexturepoolchain = NULL;
-static qbyte *resizebuffer = NULL, *colorconvertbuffer;
+static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
static int resizebuffersize = 0;
-static qbyte *texturebuffer;
-static int texturebuffersize = 0;
+static const unsigned char *texturebuffer;
-static int realmaxsize = 0;
-
-static textypeinfo_t *R_GetTexTypeInfo(int textype, int flags)
+static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
{
- if (flags & TEXF_ALPHA)
+ switch(textype)
{
- switch(textype)
- {
- case TEXTYPE_PALETTE:
- return &textype_palette_alpha;
- case TEXTYPE_RGB:
- Host_Error("R_GetTexTypeInfo: RGB format has no alpha, TEXF_ALPHA not allowed\n");
- return NULL;
- case TEXTYPE_RGBA:
- return &textype_rgba_alpha;
- default:
- Host_Error("R_GetTexTypeInfo: unknown texture format\n");
- return NULL;
- }
+#ifdef USE_GLES2
+ case TEXTYPE_PALETTE: return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
+ case TEXTYPE_RGBA: return ((flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba);
+ case TEXTYPE_BGRA: return ((flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra);
+#ifdef __ANDROID__
+ case TEXTYPE_ETC1: return &textype_etc1;
+#endif
+ case TEXTYPE_ALPHA: return &textype_alpha;
+ case TEXTYPE_COLORBUFFER: return &textype_colorbuffer;
+ case TEXTYPE_COLORBUFFER16F: return &textype_colorbuffer16f;
+ case TEXTYPE_COLORBUFFER32F: return &textype_colorbuffer32f;
+ case TEXTYPE_DEPTHBUFFER16: return &textype_depth16;
+ case TEXTYPE_DEPTHBUFFER24: return &textype_depth24;
+ case TEXTYPE_DEPTHBUFFER24STENCIL8: return &textype_depth24stencil8;
+ case TEXTYPE_SHADOWMAP16_COMP: return &textype_shadowmap16_comp;
+ case TEXTYPE_SHADOWMAP16_RAW: return &textype_shadowmap16_raw;
+ case TEXTYPE_SHADOWMAP24_COMP: return &textype_shadowmap24_comp;
+ case TEXTYPE_SHADOWMAP24_RAW: return &textype_shadowmap24_raw;
+#else
+ case TEXTYPE_DXT1: return &textype_dxt1;
+ case TEXTYPE_DXT1A: return &textype_dxt1a;
+ case TEXTYPE_DXT3: return &textype_dxt3;
+ case TEXTYPE_DXT5: return &textype_dxt5;
+ case TEXTYPE_PALETTE: return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
+ case TEXTYPE_RGBA: return ((flags & TEXF_COMPRESS) && vid.support.ext_texture_compression_s3tc) ? ((flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress) : ((flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba);
+ case TEXTYPE_BGRA: return ((flags & TEXF_COMPRESS) && vid.support.ext_texture_compression_s3tc) ? ((flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress) : ((flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra);
+ case TEXTYPE_ALPHA: return &textype_alpha;
+ case TEXTYPE_COLORBUFFER: return &textype_colorbuffer;
+ case TEXTYPE_COLORBUFFER16F: return &textype_colorbuffer16f;
+ case TEXTYPE_COLORBUFFER32F: return &textype_colorbuffer32f;
+ case TEXTYPE_DEPTHBUFFER16: return &textype_depth16;
+ case TEXTYPE_DEPTHBUFFER24: return &textype_depth24;
+ case TEXTYPE_DEPTHBUFFER24STENCIL8: return &textype_depth24stencil8;
+ case TEXTYPE_SHADOWMAP16_COMP: return &textype_shadowmap16_comp;
+ case TEXTYPE_SHADOWMAP16_RAW: return &textype_shadowmap16_raw;
+ case TEXTYPE_SHADOWMAP24_COMP: return &textype_shadowmap24_comp;
+ case TEXTYPE_SHADOWMAP24_RAW: return &textype_shadowmap24_raw;
+ case TEXTYPE_SRGB_DXT1: return &textype_sRGB_dxt1;
+ case TEXTYPE_SRGB_DXT1A: return &textype_sRGB_dxt1a;
+ case TEXTYPE_SRGB_DXT3: return &textype_sRGB_dxt3;
+ case TEXTYPE_SRGB_DXT5: return &textype_sRGB_dxt5;
+ case TEXTYPE_SRGB_PALETTE: return (flags & TEXF_ALPHA) ? &textype_sRGB_palette_alpha : &textype_sRGB_palette;
+ case TEXTYPE_SRGB_RGBA: return ((flags & TEXF_COMPRESS) && vid.support.ext_texture_compression_s3tc) ? ((flags & TEXF_ALPHA) ? &textype_sRGB_rgba_alpha_compress : &textype_sRGB_rgba_compress) : ((flags & TEXF_ALPHA) ? &textype_sRGB_rgba_alpha : &textype_sRGB_rgba);
+ case TEXTYPE_SRGB_BGRA: return ((flags & TEXF_COMPRESS) && vid.support.ext_texture_compression_s3tc) ? ((flags & TEXF_ALPHA) ? &textype_sRGB_bgra_alpha_compress : &textype_sRGB_bgra_compress) : ((flags & TEXF_ALPHA) ? &textype_sRGB_bgra_alpha : &textype_sRGB_bgra);
+#endif
+ default:
+ Host_Error("R_GetTexTypeInfo: unknown texture format %i with flags %x", (int)textype, flags);
+ break;
}
- else
+ return NULL;
+}
+
+// dynamic texture code [11/22/2007 Black]
+void R_MarkDirtyTexture(rtexture_t *rt) {
+ gltexture_t *glt = (gltexture_t*) rt;
+ if( !glt ) {
+ return;
+ }
+
+ // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
+ if (glt->flags & GLTEXF_DYNAMIC)
{
- switch(textype)
- {
- case TEXTYPE_PALETTE:
- return &textype_palette;
- case TEXTYPE_RGB:
- return &textype_rgb;
- case TEXTYPE_RGBA:
- return &textype_rgba;
- case TEXTYPE_DSDT:
- return &textype_dsdt;
- default:
- Host_Error("R_GetTexTypeInfo: unknown texture format\n");
- return NULL;
- }
+ // mark it as dirty, so R_RealGetTexture gets called
+ glt->dirty = true;
}
}
-static void R_UploadTexture(gltexture_t *t);
+void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
+ gltexture_t *glt = (gltexture_t*) rt;
+ if( !glt ) {
+ return;
+ }
-static void R_PrecacheTexture(gltexture_t *glt)
-{
- int precache;
- precache = false;
- if (glt->flags & TEXF_ALWAYSPRECACHE)
- precache = true;
- else if (r_precachetextures.integer >= 2)
- precache = true;
- else if (r_precachetextures.integer >= 1)
- if (glt->flags & TEXF_PRECACHE)
- precache = true;
-
- if (precache)
- R_UploadTexture(glt);
+ glt->flags |= GLTEXF_DYNAMIC;
+ glt->updatecallback = updatecallback;
+ glt->updatecallback_data = data;
}
-int R_RealGetTexture(rtexture_t *rt)
+static void R_UpdateDynamicTexture(gltexture_t *glt) {
+ glt->dirty = false;
+ if( glt->updatecallback ) {
+ glt->updatecallback( (rtexture_t*) glt, glt->updatecallback_data );
+ }
+}
+
+void R_PurgeTexture(rtexture_t *rt)
{
- if (rt)
- {
- gltexture_t *glt;
- glt = (gltexture_t *)rt;
- if (glt->flags & GLTEXF_UPLOAD)
- R_UploadTexture(glt);
- glt->texnum = glt->image->texnum;
- return glt->image->texnum;
+ if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
+ R_FreeTexture(rt);
}
- else
- return 0;
}
void R_FreeTexture(rtexture_t *rt)
{
gltexture_t *glt, **gltpointer;
- gltextureimage_t *image, **gltimagepointer;
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
- // image are also freed
- if (glt->image)
+ R_Mesh_ClearBindingsForTexture(glt->texnum);
+
+ switch(vid.renderpath)
{
- image = glt->image;
- image->texturecount--;
- if (image->texturecount < 1)
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
+ if (glt->texnum)
{
- for (gltimagepointer = &glt->pool->imagechain;*gltimagepointer && *gltimagepointer != image;gltimagepointer = &(*gltimagepointer)->imagechain);
- if (*gltimagepointer == image)
- *gltimagepointer = image->imagechain;
- else
- Host_Error("R_FreeTexture: image not linked in pool\n");
- if (image->texnum)
- qglDeleteTextures(1, (GLuint *)&image->texnum);
- if (image->blockallocation)
- Mem_Free(image->blockallocation);
- Mem_Free(image);
+ CHECKGLERROR
+ qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
+ }
+ if (glt->renderbuffernum)
+ {
+ CHECKGLERROR
+ qglDeleteRenderbuffers(1, (GLuint *)&glt->renderbuffernum);CHECKGLERROR
}
+ break;
}
if (glt->inputtexels)
Mem_Free(glt->inputtexels);
- Mem_Free(glt);
+ Mem_ExpandableArray_FreeRecord(&texturearray, glt);
}
rtexturepool_t *R_AllocTexturePool(void)
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)
- Con_Printf("R_FreeTexturePool: not all images freed\n");
Mem_Free(pool);
}
-typedef struct
+typedef struct glmode_s
{
- char *name;
+ const char *name;
int minification, magnification;
}
glmode_t;
-static glmode_t modes[] =
+static glmode_t modes[6] =
{
{"GL_NEAREST", GL_NEAREST, GL_NEAREST},
{"GL_LINEAR", GL_LINEAR, GL_LINEAR},
{
int i;
GLint oldbindtexnum;
- gltextureimage_t *image;
+ gltexture_t *glt;
gltexturepool_t *pool;
if (Cmd_Argc() == 1)
{
+ Con_Printf("Texture mode is %sforced\n", gl_filter_force ? "" : "not ");
for (i = 0;i < 6;i++)
{
if (gl_filter_min == modes[i].minification)
return;
}
- for (i = 0;i < 6;i++)
+ for (i = 0;i < (int)(sizeof(modes)/sizeof(*modes));i++)
if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
break;
if (i == 6)
gl_filter_min = modes[i].minification;
gl_filter_mag = modes[i].magnification;
+ gl_filter_force = ((Cmd_Argc() > 2) && !strcasecmp(Cmd_Argv(2), "force"));
- // change all the existing mipmap texture objects
- // FIXME: force renderer(/client/something?) restart instead?
- for (pool = gltexturepoolchain;pool;pool = pool->next)
+ switch(vid.renderpath)
{
- for (image = pool->imagechain;image;image = image->imagechain)
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
+ // 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)
{
- // only update already uploaded images
- if (!(image->flags & GLTEXF_UPLOAD) && !(image->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
+ for (glt = pool->gltchain;glt;glt = glt->chain)
{
- qglGetIntegerv(gltexturetypebindingenums[image->texturetype], &oldbindtexnum);
- qglBindTexture(gltexturetypeenums[image->texturetype], image->texnum);
- if (image->flags & TEXF_MIPMAP)
- qglTexParameteri(gltexturetypeenums[image->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);
- else
- qglTexParameteri(gltexturetypeenums[image->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);
- qglTexParameteri(gltexturetypeenums[image->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);
- qglBindTexture(gltexturetypeenums[image->texturetype], oldbindtexnum);
+ // only update already uploaded images
+ if (glt->texnum && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
+ {
+ oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+ qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+ if (glt->flags & TEXF_MIPMAP)
+ {
+ qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
+ }
+ else
+ {
+ qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
+ }
+ qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
+ qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+ }
}
}
+ break;
+ }
+}
+
+static void GL_Texture_CalcImageSize(int texturetype, int flags, int miplevel, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth, int *outmiplevels)
+{
+ int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1, miplevels = 1;
+
+ switch (texturetype)
+ {
+ default:
+ case GLTEXTURETYPE_2D:
+ maxsize = vid.maxtexturesize_2d;
+ if (flags & TEXF_PICMIP)
+ {
+ maxsize = bound(1, gl_max_size.integer, maxsize);
+ picmip = miplevel;
+ }
+ break;
+ case GLTEXTURETYPE_3D:
+ maxsize = vid.maxtexturesize_3d;
+ break;
+ case GLTEXTURETYPE_CUBEMAP:
+ maxsize = vid.maxtexturesize_cubemap;
+ break;
+ }
+
+ width2 = min(inwidth >> picmip, maxsize);
+ height2 = min(inheight >> picmip, maxsize);
+ depth2 = min(indepth >> picmip, maxsize);
+
+ miplevels = 1;
+ if (flags & TEXF_MIPMAP)
+ {
+ int extent = max(width2, max(height2, depth2));
+ while(extent >>= 1)
+ miplevels++;
}
+
+ if (outwidth)
+ *outwidth = max(1, width2);
+ if (outheight)
+ *outheight = max(1, height2);
+ if (outdepth)
+ *outdepth = max(1, depth2);
+ if (outmiplevels)
+ *outmiplevels = miplevels;
}
+
static int R_CalcTexelDataSize (gltexture_t *glt)
{
- int width2, height2, depth2, size, picmip;
- if (glt->flags & TEXF_FRAGMENT)
- size = glt->width * glt->height * glt->depth;
- else
+ int width2, height2, depth2, size;
+
+ GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2, NULL);
+
+ 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 (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
}
void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
for (glt = pool->gltchain;glt;glt = glt->chain)
{
glsize = R_CalcTexelDataSize(glt);
- isloaded = !(glt->flags & GLTEXF_UPLOAD);
+ isloaded = glt->texnum != 0 || glt->renderbuffernum != 0;
pooltotal++;
pooltotalt += glsize;
pooltotalp += glt->inputdatasize;
poolloadedp += glt->inputdatasize;
}
if (printeach)
- Con_Printf("%c%4i%c%c%4i%c %s %s %s %s\n", isloaded ? '[' : ' ', (glsize + 1023) / 1024, isloaded ? ']' : ' ', glt->inputtexels ? '[' : ' ', (glt->inputdatasize + 1023) / 1024, glt->inputtexels ? ']' : ' ', isloaded ? "loaded" : " ", (glt->flags & TEXF_MIPMAP) ? "mip" : " ", (glt->flags & TEXF_ALPHA) ? "alpha" : " ", glt->identifier);
+ Con_Printf("%c%4i%c%c%4i%c %-24s %s %s %s %s\n", isloaded ? '[' : ' ', (glsize + 1023) / 1024, isloaded ? ']' : ' ', glt->inputtexels ? '[' : ' ', (glt->inputdatasize + 1023) / 1024, glt->inputtexels ? ']' : ' ', glt->textype->name, isloaded ? "loaded" : " ", (glt->flags & TEXF_MIPMAP) ? "mip" : " ", (glt->flags & TEXF_ALPHA) ? "alpha" : " ", glt->identifier);
}
if (printpool)
- Con_Printf("texturepool %10p total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", pool, pooltotal, pooltotalt / 1048576.0, pooltotalp / 1048576.0, poolloaded, poolloadedt / 1048576.0, poolloadedp / 1048576.0, pooltotal - poolloaded, (pooltotalt - poolloadedt) / 1048576.0, (pooltotalp - poolloadedp) / 1048576.0);
+ Con_Printf("texturepool %10p total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", (void *)pool, pooltotal, pooltotalt / 1048576.0, pooltotalp / 1048576.0, poolloaded, poolloadedt / 1048576.0, poolloadedp / 1048576.0, pooltotal - poolloaded, (pooltotalt - poolloadedt) / 1048576.0, (pooltotalp - poolloadedp) / 1048576.0);
sumtotal += pooltotal;
sumtotalt += pooltotalt;
sumtotalp += pooltotalp;
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);
+ switch(vid.renderpath)
+ {
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
+ // LordHavoc: allow any alignment
+ CHECKGLERROR
+ qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
+ qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
+ break;
+ }
texturemempool = Mem_AllocPool("texture management", 0, NULL);
+ Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
// Disable JPEG screenshots if the DLL isn't loaded
if (! JPEG_OpenLibrary ())
Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
+ if (! PNG_OpenLibrary ())
+ Cvar_SetValueQuick (&scr_screenshot_png, 0);
}
static void r_textures_shutdown(void)
}
resizebuffersize = 0;
- texturebuffersize = 0;
resizebuffer = NULL;
colorconvertbuffer = NULL;
texturebuffer = NULL;
+ Mem_ExpandableArray_FreeArray(&texturearray);
Mem_FreePool(&texturemempool);
}
{
}
+static void r_textures_devicelost(void)
+{
+ int i, endindex;
+ gltexture_t *glt;
+ endindex = (int)Mem_ExpandableArray_IndexRange(&texturearray);
+ for (i = 0;i < endindex;i++)
+ {
+ glt = (gltexture_t *) Mem_ExpandableArray_RecordAtIndex(&texturearray, i);
+ if (!glt || !(glt->flags & TEXF_RENDERTARGET))
+ continue;
+ switch(vid.renderpath)
+ {
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
+ break;
+ }
+ }
+}
+
+static void r_textures_devicerestored(void)
+{
+ int i, endindex;
+ gltexture_t *glt;
+ endindex = (int)Mem_ExpandableArray_IndexRange(&texturearray);
+ for (i = 0;i < endindex;i++)
+ {
+ glt = (gltexture_t *) Mem_ExpandableArray_RecordAtIndex(&texturearray, i);
+ if (!glt || !(glt->flags & TEXF_RENDERTARGET))
+ continue;
+ switch(vid.renderpath)
+ {
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
+ break;
+ }
+ }
+}
+
+
void R_Textures_Init (void)
{
- Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f);
- Cmd_AddCommand("r_texturestats", R_TextureStats_f);
- Cvar_RegisterVariable (&gl_max_scrapsize);
+ Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc); an additional argument 'force' forces the texture mode even in cases where it may not be appropriate");
+ Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
Cvar_RegisterVariable (&gl_max_size);
Cvar_RegisterVariable (&gl_picmip);
+ Cvar_RegisterVariable (&gl_picmip_world);
+ Cvar_RegisterVariable (&r_picmipworld);
+ Cvar_RegisterVariable (&gl_picmip_sprites);
+ Cvar_RegisterVariable (&r_picmipsprites);
+ Cvar_RegisterVariable (&gl_picmip_other);
+ Cvar_RegisterVariable (&gl_max_lightmapsize);
Cvar_RegisterVariable (&r_lerpimages);
- Cvar_RegisterVariable (&r_precachetextures);
Cvar_RegisterVariable (&gl_texture_anisotropy);
-
- R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
+ Cvar_RegisterVariable (&gl_texturecompression);
+ Cvar_RegisterVariable (&gl_texturecompression_color);
+ Cvar_RegisterVariable (&gl_texturecompression_normal);
+ Cvar_RegisterVariable (&gl_texturecompression_gloss);
+ Cvar_RegisterVariable (&gl_texturecompression_glow);
+ Cvar_RegisterVariable (&gl_texturecompression_2d);
+ Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
+ Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
+ Cvar_RegisterVariable (&gl_texturecompression_sky);
+ Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
+ Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
+ Cvar_RegisterVariable (&gl_texturecompression_sprites);
+ Cvar_RegisterVariable (&gl_nopartialtextureupdates);
+ Cvar_RegisterVariable (&r_texture_dds_load_alphamode);
+ Cvar_RegisterVariable (&r_texture_dds_load_logfailure);
+ Cvar_RegisterVariable (&r_texture_dds_swdecode);
+
+ R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap, r_textures_devicelost, r_textures_devicerestored);
}
void R_Textures_Frame (void)
{
+#ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
static int old_aniso = 0;
+ static qboolean first_time_aniso = true;
+#endif
// could do procedural texture animation here, if we keep track of which
// textures were accessed this frame...
colorconvertbuffer = NULL;
}
+#ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
if (old_aniso != gl_texture_anisotropy.integer)
{
- gltextureimage_t *image;
+ gltexture_t *glt;
gltexturepool_t *pool;
GLint oldbindtexnum;
- old_aniso = bound(1, gl_texture_anisotropy.integer, gl_max_anisotropy);
+ old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
- for (pool = gltexturepoolchain;pool;pool = pool->next)
+ switch(vid.renderpath)
{
- for (image = pool->imagechain;image;image = image->imagechain)
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
+ // ignore the first difference, any textures loaded by now probably had the same aniso value
+ if (first_time_aniso)
{
- // only update already uploaded images
- if (!(image->flags & GLTEXF_UPLOAD) && (image->flags & TEXF_MIPMAP))
+ first_time_aniso = false;
+ break;
+ }
+ CHECKGLERROR
+ GL_ActiveTexture(0);
+ for (pool = gltexturepoolchain;pool;pool = pool->next)
+ {
+ for (glt = pool->gltchain;glt;glt = glt->chain)
{
- qglGetIntegerv(gltexturetypebindingenums[image->texturetype], &oldbindtexnum);
+ // only update already uploaded images
+ if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
+ {
+ oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
- qglBindTexture(gltexturetypeenums[image->texturetype], image->texnum);
- qglTexParameteri(gltexturetypeenums[image->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
+ qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+ qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
- qglBindTexture(gltexturetypeenums[image->texturetype], oldbindtexnum);
+ qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+ }
}
}
+ break;
}
}
+#endif
}
-void R_MakeResizeBufferBigger(int size)
+static void R_MakeResizeBufferBigger(int size)
{
if (resizebuffersize < 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");
}
}
-static void GL_SetupTextureParameters(int flags, int texturetype)
+static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
{
int textureenum = gltexturetypeenums[texturetype];
- int wrapmode = ((flags & TEXF_CLAMP) && gl_support_clamptoedge) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
+ int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
CHECKGLERROR
- if (gl_support_anisotropy && (flags & TEXF_MIPMAP))
+#ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
+ if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
{
- int aniso = bound(1, gl_texture_anisotropy.integer, gl_max_anisotropy);
+ int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
if (gl_texture_anisotropy.integer != aniso)
Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
}
+#endif
qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
+#ifdef GL_TEXTURE_WRAP_R
if (gltexturetypedimensions[texturetype] >= 3)
{
qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
}
+#endif
CHECKGLERROR
- if (flags & TEXF_FORCENEAREST)
+ if (!gl_filter_force && flags & TEXF_FORCENEAREST)
{
if (flags & TEXF_MIPMAP)
{
}
qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
}
- else if (flags & TEXF_FORCELINEAR)
+ else if (!gl_filter_force && flags & TEXF_FORCELINEAR)
{
if (flags & TEXF_MIPMAP)
{
qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
}
+#ifdef GL_TEXTURE_COMPARE_MODE_ARB
+ switch(textype)
+ {
+ case TEXTYPE_SHADOWMAP16_COMP:
+ case TEXTYPE_SHADOWMAP24_COMP:
+ qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
+ qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
+ break;
+ case TEXTYPE_SHADOWMAP16_RAW:
+ case TEXTYPE_SHADOWMAP24_RAW:
+ qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
+ qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
+ break;
+ default:
+ break;
+ }
+#endif
+
CHECKGLERROR
}
-static void R_Upload(gltexture_t *glt, qbyte *data)
+static void R_UploadPartialTexture(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
{
- int i, mip, width, height, depth;
- GLint oldbindtexnum;
- qbyte *prevbuffer;
- prevbuffer = data;
+ if (data == NULL)
+ Sys_Error("R_UploadPartialTexture \"%s\": partial update with NULL pixels", glt->identifier);
- CHECKGLERROR
+ if (glt->texturetype != GLTEXTURETYPE_2D)
+ Sys_Error("R_UploadPartialTexture \"%s\": partial update of type other than 2D", glt->identifier);
- glt->texnum = glt->image->texnum;
- // we need to restore the texture binding after finishing the upload
- qglGetIntegerv(gltexturetypebindingenums[glt->image->texturetype], &oldbindtexnum);
- qglBindTexture(gltexturetypeenums[glt->image->texturetype], glt->image->texnum);
- CHECKGLERROR
- glt->flags &= ~GLTEXF_UPLOAD;
+ if (glt->textype->textype == TEXTYPE_PALETTE)
+ Sys_Error("R_UploadPartialTexture \"%s\": partial update of paletted texture", glt->identifier);
- if (glt->flags & TEXF_FRAGMENT)
- {
- if (glt->image->flags & GLTEXF_UPLOAD)
- {
- glt->image->flags &= ~GLTEXF_UPLOAD;
- Con_DPrint("uploaded new fragments image\n");
- R_MakeResizeBufferBigger(glt->image->width * glt->image->height * glt->image->depth * glt->image->bytesperpixel);
- memset(resizebuffer, 255, glt->image->width * glt->image->height * glt->image->depth * glt->image->bytesperpixel);
- switch(glt->image->texturetype)
- {
- case GLTEXTURETYPE_1D:
- qglTexImage1D(GL_TEXTURE_1D, 0, glt->image->glinternalformat, glt->image->width, 0, glt->image->glformat, GL_UNSIGNED_BYTE, resizebuffer);
- CHECKGLERROR
- break;
- case GLTEXTURETYPE_2D:
- qglTexImage2D(GL_TEXTURE_2D, 0, glt->image->glinternalformat, glt->image->width, glt->image->height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, resizebuffer);
- CHECKGLERROR
- break;
- case GLTEXTURETYPE_3D:
- qglTexImage3D(GL_TEXTURE_3D, 0, glt->image->glinternalformat, glt->image->width, glt->image->height, glt->image->depth, 0, glt->image->glformat, GL_UNSIGNED_BYTE, resizebuffer);
- CHECKGLERROR
- break;
- default:
- Host_Error("R_Upload: fragment texture of type other than 1D, 2D, or 3D\n");
- break;
- }
- GL_SetupTextureParameters(glt->image->flags, glt->image->texturetype);
- }
+ if (glt->flags & (TEXF_MIPMAP | TEXF_PICMIP))
+ Sys_Error("R_UploadPartialTexture \"%s\": partial update not supported with MIPMAP or PICMIP flags", glt->identifier);
- if (prevbuffer == NULL)
- {
- R_MakeResizeBufferBigger(glt->image->width * glt->image->height * glt->image->depth * glt->image->bytesperpixel);
- memset(resizebuffer, 0, glt->width * glt->height * glt->image->depth * glt->image->bytesperpixel);
- prevbuffer = resizebuffer;
- }
- else if (glt->textype->textype == TEXTYPE_PALETTE)
- {
- // promote paletted to RGBA, so we only have to worry about RGB and
- // RGBA in the rest of this code
- R_MakeResizeBufferBigger(glt->image->width * glt->image->height * glt->image->depth * glt->image->sides * glt->image->bytesperpixel);
- Image_Copy8bitRGBA(prevbuffer, colorconvertbuffer, glt->width * glt->height * glt->depth, glt->palette);
- prevbuffer = colorconvertbuffer;
- }
+ if (glt->inputwidth != glt->tilewidth || glt->inputheight != glt->tileheight || glt->tiledepth != 1)
+ Sys_Error("R_UploadPartialTexture \"%s\": partial update not supported with stretched or special textures", glt->identifier);
+
+ // update a portion of the image
- switch(glt->image->texturetype)
+ switch(vid.renderpath)
+ {
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
{
- case GLTEXTURETYPE_1D:
- qglTexSubImage1D(GL_TEXTURE_1D, 0, glt->x, glt->width, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
- CHECKGLERROR
- break;
- case GLTEXTURETYPE_2D:
- qglTexSubImage2D(GL_TEXTURE_2D, 0, glt->x, glt->y, glt->width, glt->height, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
- CHECKGLERROR
- break;
- case GLTEXTURETYPE_3D:
- qglTexSubImage3D(GL_TEXTURE_3D, 0, glt->x, glt->y, glt->z, glt->width, glt->height, glt->depth, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
+ int oldbindtexnum;
CHECKGLERROR
- break;
- default:
- Host_Error("R_Upload: fragment texture of type other than 1D, 2D, or 3D\n");
- break;
+ // we need to restore the texture binding after finishing the upload
+ GL_ActiveTexture(0);
+ oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+ qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+ qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, data);CHECKGLERROR
+ qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
}
+ break;
}
- else
- {
- glt->image->flags &= ~GLTEXF_UPLOAD;
+}
- // these are rounded up versions of the size to do better resampling
- for (width = 1;width < glt->width ;width <<= 1);
- for (height = 1;height < glt->height;height <<= 1);
- for (depth = 1;depth < glt->depth ;depth <<= 1);
+static void R_UploadFullTexture(gltexture_t *glt, const unsigned char *data)
+{
+ int i, mip = 0, width, height, depth;
+ GLint oldbindtexnum = 0;
+ const unsigned char *prevbuffer;
+ prevbuffer = data;
- R_MakeResizeBufferBigger(width * height * depth * glt->image->sides * glt->image->bytesperpixel);
+ // error out if a stretch is needed on special texture types
+ if (glt->texturetype != GLTEXTURETYPE_2D && (glt->tilewidth != glt->inputwidth || glt->tileheight != glt->inputheight || glt->tiledepth != glt->inputdepth))
+ Sys_Error("R_UploadFullTexture \"%s\": stretch uploads allowed only on 2D textures\n", glt->identifier);
- if (prevbuffer == NULL)
+ // when picmip or maxsize is applied, we scale up to a power of 2 multiple
+ // of the target size and then use the mipmap reduction function to get
+ // high quality supersampled results
+ for (width = glt->tilewidth;width < glt->inputwidth ;width <<= 1);
+ for (height = glt->tileheight;height < glt->inputheight;height <<= 1);
+ for (depth = glt->tiledepth;depth < glt->inputdepth ;depth <<= 1);
+
+ if (prevbuffer == NULL)
+ {
+ width = glt->tilewidth;
+ height = glt->tileheight;
+ depth = glt->tiledepth;
+// R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
+// memset(resizebuffer, 0, width * height * depth * glt->sides * glt->bytesperpixel);
+// prevbuffer = resizebuffer;
+ }
+ else
+ {
+ if (glt->textype->textype == TEXTYPE_PALETTE)
{
- width = glt->image->width;
- height = glt->image->height;
- depth = glt->image->depth;
- memset(resizebuffer, 0, width * height * depth * glt->image->bytesperpixel);
- prevbuffer = resizebuffer;
+ // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
+ R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
+ Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, glt->inputwidth * glt->inputheight * glt->inputdepth * glt->sides, glt->palette);
+ prevbuffer = colorconvertbuffer;
}
- else
+ if (glt->flags & TEXF_RGBMULTIPLYBYALPHA)
{
- if (glt->textype->textype == TEXTYPE_PALETTE)
+ // multiply RGB channels by A channel before uploading
+ int alpha;
+ R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
+ for (i = 0;i < glt->inputwidth*glt->inputheight*glt->inputdepth*4;i += 4)
{
- // promote paletted to RGBA, so we only have to worry about RGB and
- // RGBA in the rest of this code
- Image_Copy8bitRGBA(prevbuffer, colorconvertbuffer, glt->width * glt->height * glt->depth * glt->image->sides, glt->palette);
- prevbuffer = colorconvertbuffer;
+ alpha = prevbuffer[i+3];
+ colorconvertbuffer[i] = (prevbuffer[i] * alpha) >> 8;
+ colorconvertbuffer[i+1] = (prevbuffer[i+1] * alpha) >> 8;
+ colorconvertbuffer[i+2] = (prevbuffer[i+2] * alpha) >> 8;
+ colorconvertbuffer[i+3] = alpha;
}
+ prevbuffer = colorconvertbuffer;
}
-
- // cubemaps contain multiple images and thus get processed a bit differently
- if (glt->image->texturetype != GLTEXTURETYPE_CUBEMAP)
+ // scale up to a power of 2 size (if appropriate)
+ if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
{
- if (glt->width != width || glt->height != height || glt->depth != depth)
- {
- Image_Resample(prevbuffer, glt->width, glt->height, glt->depth, resizebuffer, width, height, depth, glt->image->bytesperpixel, r_lerpimages.integer);
- prevbuffer = resizebuffer;
- }
- // picmip/max_size
- while (width > glt->image->width || height > glt->image->height || depth > glt->image->depth)
- {
- Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, glt->image->width, glt->image->height, glt->image->depth, glt->image->bytesperpixel);
- prevbuffer = resizebuffer;
- }
+ R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
+ Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
+ prevbuffer = resizebuffer;
}
- mip = 0;
- switch(glt->image->texturetype)
+ // apply mipmap reduction algorithm to get down to picmip/max_size
+ while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
+ {
+ R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
+ Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
+ prevbuffer = resizebuffer;
+ }
+ }
+
+ // do the appropriate upload type...
+ switch(vid.renderpath)
+ {
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
+ if (glt->texnum) // not renderbuffers
{
- case GLTEXTURETYPE_1D:
- qglTexImage1D(GL_TEXTURE_1D, mip++, glt->image->glinternalformat, width, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
- CHECKGLERROR
- if (glt->flags & TEXF_MIPMAP)
- {
- while (width > 1 || height > 1 || depth > 1)
- {
- Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1, glt->image->bytesperpixel);
- prevbuffer = resizebuffer;
- qglTexImage1D(GL_TEXTURE_1D, mip++, glt->image->glinternalformat, width, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
- CHECKGLERROR
- }
- }
- break;
- case GLTEXTURETYPE_2D:
- qglTexImage2D(GL_TEXTURE_2D, mip++, glt->image->glinternalformat, width, height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
- CHECKGLERROR
- if (glt->flags & TEXF_MIPMAP)
- {
- while (width > 1 || height > 1 || depth > 1)
- {
- Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1, glt->image->bytesperpixel);
- prevbuffer = resizebuffer;
- qglTexImage2D(GL_TEXTURE_2D, mip++, glt->image->glinternalformat, width, height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
- CHECKGLERROR
- }
- }
- break;
- case GLTEXTURETYPE_3D:
- qglTexImage3D(GL_TEXTURE_3D, mip++, glt->image->glinternalformat, width, height, depth, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
CHECKGLERROR
- if (glt->flags & TEXF_MIPMAP)
+
+ // we need to restore the texture binding after finishing the upload
+ GL_ActiveTexture(0);
+ oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+ qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+
+#ifndef USE_GLES2
+#ifdef GL_TEXTURE_COMPRESSION_HINT_ARB
+ if (qglGetCompressedTexImageARB)
{
- while (width > 1 || height > 1 || depth > 1)
- {
- Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1, glt->image->bytesperpixel);
- prevbuffer = resizebuffer;
- qglTexImage3D(GL_TEXTURE_3D, mip++, glt->image->glinternalformat, width, height, depth, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
- CHECKGLERROR
- }
+ if (gl_texturecompression.integer >= 2)
+ qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
+ else
+ qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
+ CHECKGLERROR
}
- break;
- case GLTEXTURETYPE_CUBEMAP:
- // convert and upload each side in turn,
- // from a continuous block of input texels
- texturebuffer = prevbuffer;
- for (i = 0;i < 6;i++)
+#endif
+#endif
+ switch(glt->texturetype)
{
- prevbuffer = texturebuffer;
- texturebuffer += glt->width * glt->height * glt->depth * glt->textype->inputbytesperpixel;
- if (glt->width != width || glt->height != height || glt->depth != depth)
- {
- Image_Resample(prevbuffer, glt->width, glt->height, glt->depth, resizebuffer, width, height, depth, glt->image->bytesperpixel, r_lerpimages.integer);
- prevbuffer = resizebuffer;
- }
- // picmip/max_size
- while (width > glt->image->width || height > glt->image->height || depth > glt->image->depth)
+ case GLTEXTURETYPE_2D:
+ qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
+ if (glt->flags & TEXF_MIPMAP)
{
- Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, glt->image->width, glt->image->height, glt->image->depth, glt->image->bytesperpixel);
- prevbuffer = resizebuffer;
+ while (width > 1 || height > 1 || depth > 1)
+ {
+ R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
+ Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
+ prevbuffer = resizebuffer;
+ qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
+ }
}
- mip = 0;
- qglTexImage2D(cubemapside[i], mip++, glt->image->glinternalformat, width, height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
- CHECKGLERROR
+ break;
+ case GLTEXTURETYPE_3D:
+#ifndef USE_GLES2
+ qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
if (glt->flags & TEXF_MIPMAP)
{
while (width > 1 || height > 1 || depth > 1)
{
- Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1, glt->image->bytesperpixel);
+ R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
+ Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
+ prevbuffer = resizebuffer;
+ qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
+ }
+ }
+#endif
+ break;
+ case GLTEXTURETYPE_CUBEMAP:
+ // convert and upload each side in turn,
+ // from a continuous block of input texels
+ texturebuffer = (unsigned char *)prevbuffer;
+ for (i = 0;i < 6;i++)
+ {
+ prevbuffer = texturebuffer;
+ texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
+ if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
+ {
+ R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
+ Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
+ prevbuffer = resizebuffer;
+ }
+ // picmip/max_size
+ while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
+ {
+ R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
+ Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
prevbuffer = resizebuffer;
- qglTexImage2D(cubemapside[i], mip++, glt->image->glinternalformat, width, height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
- CHECKGLERROR
+ }
+ mip = 0;
+ qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
+ if (glt->flags & TEXF_MIPMAP)
+ {
+ while (width > 1 || height > 1 || depth > 1)
+ {
+ R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
+ Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
+ prevbuffer = resizebuffer;
+ qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
+ }
}
}
+ break;
}
- break;
+ GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
+ qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
}
- GL_SetupTextureParameters(glt->image->flags, glt->image->texturetype);
+ break;
}
- qglBindTexture(gltexturetypeenums[glt->image->texturetype], oldbindtexnum);
}
-static void R_FindImageForTexture(gltexture_t *glt)
-{
- int i, j, best, best2, x, y, z, w, h, d, picmip;
- textypeinfo_t *texinfo;
- gltexturepool_t *pool;
- gltextureimage_t *image, **imagechainpointer;
- texinfo = glt->textype;
- pool = glt->pool;
-
- // remains -1 until uploaded
- glt->texnum = -1;
-
- x = 0;
- y = 0;
- z = 0;
- w = glt->width;
- h = glt->height;
- d = glt->depth;
- if (glt->flags & TEXF_FRAGMENT)
- {
- for (imagechainpointer = &pool->imagechain;*imagechainpointer;imagechainpointer = &(*imagechainpointer)->imagechain)
- {
- image = *imagechainpointer;
- if (image->type != GLIMAGETYPE_FRAGMENTS)
- continue;
- if (image->texturetype != glt->texturetype)
- continue;
- if ((image->flags ^ glt->flags) & (TEXF_MIPMAP | TEXF_ALPHA | TEXF_CLAMP | TEXF_FORCENEAREST | TEXF_FORCELINEAR))
- continue;
- if (image->glformat != texinfo->glformat || image->glinternalformat != texinfo->glinternalformat)
- continue;
- if (glt->width > image->width || glt->height > image->height || glt->depth > image->depth)
- continue;
-
- // got a fragments texture, find a place in it if we can
- for (best = image->width, i = 0;i < image->width - w;i++)
- {
- for (best2 = 0, j = 0;j < w;j++)
- {
- if (image->blockallocation[i+j] >= best)
- break;
- if (best2 < image->blockallocation[i+j])
- best2 = image->blockallocation[i+j];
- }
- if (j == w)
- {
- // this is a valid spot
- x = i;
- y = best = best2;
- }
- }
-
- if (best + h > image->height)
- continue;
-
- for (i = 0;i < w;i++)
- image->blockallocation[x + i] = best + h;
-
- glt->x = x;
- glt->y = y;
- glt->z = 0;
- glt->image = image;
- image->texturecount++;
- return;
- }
-
- image = (gltextureimage_t *)Mem_Alloc(texturemempool, sizeof(gltextureimage_t));
- if (image == NULL)
- {
- Con_Printf ("R_FindImageForTexture: ran out of memory\n");
- return;
- }
- image->type = GLIMAGETYPE_FRAGMENTS;
- // make sure the created image is big enough for the fragment
- for (image->width = block_size;image->width < glt->width;image->width <<= 1);
- image->height = 1;
- if (gltexturetypedimensions[glt->texturetype] >= 2)
- for (image->height = block_size;image->height < glt->height;image->height <<= 1);
- image->depth = 1;
- if (gltexturetypedimensions[glt->texturetype] >= 3)
- for (image->depth = block_size;image->depth < glt->depth;image->depth <<= 1);
- image->blockallocation = (short int *)Mem_Alloc(texturemempool, image->width * sizeof(short));
- memset(image->blockallocation, 0, image->width * sizeof(short));
-
- x = 0;
- y = 0;
- z = 0;
- for (i = 0;i < w;i++)
- image->blockallocation[x + i] = y + h;
- }
- else
- {
- for (imagechainpointer = &pool->imagechain;*imagechainpointer;imagechainpointer = &(*imagechainpointer)->imagechain);
-
- image = (gltextureimage_t *)Mem_Alloc(texturemempool, sizeof(gltextureimage_t));
- if (image == NULL)
- {
- Con_Printf ("R_FindImageForTexture: ran out of memory\n");
- return;
- }
- 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;
- }
- image->texturetype = glt->texturetype;
- image->glinternalformat = texinfo->glinternalformat;
- image->glformat = texinfo->glformat;
- image->flags = (glt->flags & (TEXF_MIPMAP | TEXF_ALPHA | TEXF_CLAMP | TEXF_PICMIP | TEXF_FORCENEAREST | TEXF_FORCELINEAR)) | GLTEXF_UPLOAD;
- image->bytesperpixel = texinfo->internalbytesperpixel;
- image->sides = image->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
- // get a texture number to use
- qglGenTextures(1, (GLuint *)&image->texnum);
- *imagechainpointer = image;
- image->texturecount++;
-
- glt->x = x;
- glt->y = y;
- glt->z = z;
- glt->image = image;
-}
-
-// note: R_FindImageForTexture must be called before this
-static void R_UploadTexture (gltexture_t *glt)
-{
- if (!(glt->flags & GLTEXF_UPLOAD))
- return;
-
- R_Upload(glt, glt->inputtexels);
- if (glt->inputtexels)
- {
- Mem_Free(glt->inputtexels);
- glt->inputtexels = NULL;
- glt->flags |= GLTEXF_DESTROYED;
- }
- else if (glt->flags & GLTEXF_DESTROYED)
- 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 miplevel, textype_t textype, int texturetype, const unsigned char *data, const unsigned int *palette)
{
int i, size;
gltexture_t *glt;
gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
- textypeinfo_t *texinfo;
+ textypeinfo_t *texinfo, *texinfo2;
+ unsigned char *temppixels = NULL;
+ qboolean swaprb;
if (cls.state == ca_dedicated)
return NULL;
- if (flags & TEXF_FRAGMENT && texturetype != GLTEXTURETYPE_2D)
+ // see if we need to swap red and blue (BGRA <-> RGBA conversion)
+ if (textype == TEXTYPE_PALETTE && vid.forcetextype == TEXTYPE_RGBA)
{
- Con_Printf ("R_LoadTexture: only 2D fragment textures implemented\n");
- return NULL;
+ int numpixels = width * height * depth * sides;
+ size = numpixels * 4;
+ temppixels = (unsigned char *)Mem_Alloc(tempmempool, size);
+ if (data)
+ {
+ const unsigned char *p;
+ unsigned char *o = temppixels;
+ for (i = 0;i < numpixels;i++, o += 4)
+ {
+ p = (const unsigned char *)palette + 4*data[i];
+ o[0] = p[2];
+ o[1] = p[1];
+ o[2] = p[0];
+ o[3] = p[3];
+ }
+ }
+ data = temppixels;
+ textype = TEXTYPE_RGBA;
}
- if (texturetype == GLTEXTURETYPE_CUBEMAP && !gl_texturecubemap)
+ swaprb = false;
+ switch(textype)
{
- Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
- return NULL;
+ case TEXTYPE_RGBA: if (vid.forcetextype == TEXTYPE_BGRA) {swaprb = true;textype = TEXTYPE_BGRA;} break;
+ case TEXTYPE_BGRA: if (vid.forcetextype == TEXTYPE_RGBA) {swaprb = true;textype = TEXTYPE_RGBA;} break;
+ case TEXTYPE_SRGB_RGBA: if (vid.forcetextype == TEXTYPE_BGRA) {swaprb = true;textype = TEXTYPE_SRGB_BGRA;} break;
+ case TEXTYPE_SRGB_BGRA: if (vid.forcetextype == TEXTYPE_RGBA) {swaprb = true;textype = TEXTYPE_SRGB_RGBA;} break;
+ default: break;
}
- if (texturetype == GLTEXTURETYPE_3D && !gl_texture3d)
+ if (swaprb)
{
- Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
- return NULL;
+ // swap bytes
+ static int rgbaswapindices[4] = {2, 1, 0, 3};
+ size = width * height * depth * sides * 4;
+ temppixels = (unsigned char *)Mem_Alloc(tempmempool, size);
+ if (data)
+ Image_CopyMux(temppixels, data, width, height*depth*sides, false, false, false, 4, 4, rgbaswapindices);
+ data = temppixels;
+ }
+
+ // if sRGB texture formats are not supported, convert input to linear and upload as normal types
+ if (!vid.support.ext_texture_srgb)
+ {
+ qboolean convertsRGB = false;
+ switch(textype)
+ {
+ case TEXTYPE_SRGB_DXT1: textype = TEXTYPE_DXT1 ;convertsRGB = true;break;
+ case TEXTYPE_SRGB_DXT1A: textype = TEXTYPE_DXT1A ;convertsRGB = true;break;
+ case TEXTYPE_SRGB_DXT3: textype = TEXTYPE_DXT3 ;convertsRGB = true;break;
+ case TEXTYPE_SRGB_DXT5: textype = TEXTYPE_DXT5 ;convertsRGB = true;break;
+ case TEXTYPE_SRGB_PALETTE: textype = TEXTYPE_PALETTE;/*convertsRGB = true;*/break;
+ case TEXTYPE_SRGB_RGBA: textype = TEXTYPE_RGBA ;convertsRGB = true;break;
+ case TEXTYPE_SRGB_BGRA: textype = TEXTYPE_BGRA ;convertsRGB = true;break;
+ default:
+ break;
+ }
+ if (convertsRGB && data)
+ {
+ size = width * height * depth * sides * 4;
+ if (!temppixels)
+ {
+ temppixels = (unsigned char *)Mem_Alloc(tempmempool, size);
+ memcpy(temppixels, data, size);
+ data = temppixels;
+ }
+ Image_MakeLinearColorsFromsRGB(temppixels, temppixels, width*height*depth*sides);
+ }
}
texinfo = R_GetTexTypeInfo(textype, flags);
size = width * height * depth * sides * texinfo->inputbytesperpixel;
if (size < 1)
{
- Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides);
+ Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
return NULL;
}
switch(textype)
{
case TEXTYPE_PALETTE:
+ case TEXTYPE_SRGB_PALETTE:
if (flags & TEXF_ALPHA)
{
flags &= ~TEXF_ALPHA;
{
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;
}
}
break;
- case TEXTYPE_RGB:
- if (flags & TEXF_ALPHA)
- Host_Error("R_LoadTexture: RGB has no alpha, don't specify TEXF_ALPHA\n");
- break;
case TEXTYPE_RGBA:
+ case TEXTYPE_BGRA:
+ case TEXTYPE_SRGB_RGBA:
+ case TEXTYPE_SRGB_BGRA:
if (flags & TEXF_ALPHA)
{
flags &= ~TEXF_ALPHA;
}
}
break;
- case TEXTYPE_DSDT:
+ case TEXTYPE_SHADOWMAP16_COMP:
+ case TEXTYPE_SHADOWMAP16_RAW:
+ case TEXTYPE_SHADOWMAP24_COMP:
+ case TEXTYPE_SHADOWMAP24_RAW:
+ break;
+ case TEXTYPE_DXT1:
+ case TEXTYPE_SRGB_DXT1:
+ break;
+ case TEXTYPE_DXT1A:
+ case TEXTYPE_SRGB_DXT1A:
+ case TEXTYPE_DXT3:
+ case TEXTYPE_SRGB_DXT3:
+ case TEXTYPE_DXT5:
+ case TEXTYPE_SRGB_DXT5:
+ flags |= TEXF_ALPHA;
+ break;
+ case TEXTYPE_ALPHA:
+ flags |= TEXF_ALPHA;
+ break;
+ case TEXTYPE_COLORBUFFER:
+ case TEXTYPE_COLORBUFFER16F:
+ case TEXTYPE_COLORBUFFER32F:
+ flags |= TEXF_ALPHA;
break;
default:
- Host_Error("R_LoadTexture: unknown texture type\n");
+ Sys_Error("R_LoadTexture: unknown texture type");
}
- glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
+ texinfo2 = R_GetTexTypeInfo(textype, flags);
+ if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
+ texinfo = texinfo2;
+ else
+ Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
+
+ glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
if (identifier)
strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
glt->pool = pool;
glt->chain = pool->gltchain;
pool->gltchain = glt;
- glt->width = width;
- glt->height = height;
- glt->depth = depth;
- glt->flags = flags | GLTEXF_UPLOAD;
+ glt->inputwidth = width;
+ glt->inputheight = height;
+ glt->inputdepth = depth;
+ glt->flags = flags;
+ glt->miplevel = (miplevel < 0) ? R_PicmipForFlags(flags) : miplevel; // note: if miplevel is -1, we know the texture is in original size and we can picmip it normally
glt->textype = texinfo;
glt->texturetype = texturetype;
glt->inputdatasize = size;
glt->palette = palette;
-
- if (data)
+ glt->glinternalformat = texinfo->glinternalformat;
+ glt->glformat = texinfo->glformat;
+ glt->gltype = texinfo->gltype;
+ glt->bytesperpixel = texinfo->internalbytesperpixel;
+ glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
+ glt->texnum = 0;
+ glt->dirty = false;
+ glt->glisdepthstencil = false;
+ glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
+ // init the dynamic texture attributes, too [11/22/2007 Black]
+ glt->updatecallback = NULL;
+ glt->updatecallback_data = NULL;
+
+ GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth, &glt->miplevels);
+
+ // upload the texture
+ // data may be NULL (blank texture for dynamic rendering)
+ switch(vid.renderpath)
{
- glt->inputtexels = (qbyte *)Mem_Alloc(texturemempool, size);
- if (glt->inputtexels == NULL)
- Con_Printf ("R_LoadTexture: out of memory\n");
- else
- memcpy(glt->inputtexels, data, size);
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
+ CHECKGLERROR
+ qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
+ break;
}
- else
- glt->inputtexels = NULL;
- R_FindImageForTexture(glt);
- R_PrecacheTexture(glt);
+ R_UploadFullTexture(glt, data);
+ if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
+ glt->bufferpixels = (unsigned char *)Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
+
+ // free any temporary processing buffer we allocated...
+ if (temppixels)
+ Mem_Free(temppixels);
+
+ // texture converting and uploading can take a while, so make sure we're sending keepalives
+ // FIXME: this causes rendering during R_Shadow_DrawLights
+// CL_KeepaliveMessage(false);
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_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
{
- return R_SetupTexture(rtexturepool, identifier, width, 1, 1, 1, flags, textype, GLTEXTURETYPE_1D, data, palette);
+ return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, miplevel, textype, GLTEXTURETYPE_2D, 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_LoadTexture3D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
{
- return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
+ return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, miplevel, textype, GLTEXTURETYPE_3D, 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_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
{
- return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
+ return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, miplevel, textype, GLTEXTURETYPE_CUBEMAP, 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_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, textype_t textype, qboolean filter)
{
- return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
+ return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, TEXF_RENDERTARGET | TEXF_CLAMP | (filter ? TEXF_FORCELINEAR : TEXF_FORCENEAREST), -1, textype, GLTEXTURETYPE_2D, NULL, NULL);
}
-int R_TextureHasAlpha(rtexture_t *rt)
+rtexture_t *R_LoadTextureRenderBuffer(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, textype_t textype)
{
- return rt ? (((gltexture_t *)rt)->flags & TEXF_ALPHA) != 0 : false;
-}
+ gltexture_t *glt;
+ gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
+ textypeinfo_t *texinfo;
-int R_TextureWidth(rtexture_t *rt)
-{
- return rt ? ((gltexture_t *)rt)->width : 0;
+ if (cls.state == ca_dedicated)
+ return NULL;
+
+ texinfo = R_GetTexTypeInfo(textype, TEXF_RENDERTARGET | TEXF_CLAMP);
+
+ glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
+ if (identifier)
+ strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
+ glt->pool = pool;
+ glt->chain = pool->gltchain;
+ pool->gltchain = glt;
+ glt->inputwidth = width;
+ glt->inputheight = height;
+ glt->inputdepth = 1;
+ glt->flags = TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_FORCENEAREST;
+ glt->miplevel = 0;
+ glt->textype = texinfo;
+ glt->texturetype = textype;
+ glt->inputdatasize = width*height*texinfo->internalbytesperpixel;
+ glt->palette = NULL;
+ glt->glinternalformat = texinfo->glinternalformat;
+ glt->glformat = texinfo->glformat;
+ glt->gltype = texinfo->gltype;
+ glt->bytesperpixel = texinfo->internalbytesperpixel;
+ glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
+ glt->texnum = 0;
+ glt->dirty = false;
+ glt->glisdepthstencil = textype == TEXTYPE_DEPTHBUFFER24STENCIL8;
+ glt->gltexturetypeenum = GL_TEXTURE_2D;
+ // init the dynamic texture attributes, too [11/22/2007 Black]
+ glt->updatecallback = NULL;
+ glt->updatecallback_data = NULL;
+
+ GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth, &glt->miplevels);
+
+ // upload the texture
+ // data may be NULL (blank texture for dynamic rendering)
+ switch(vid.renderpath)
+ {
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
+ CHECKGLERROR
+ qglGenRenderbuffers(1, (GLuint *)&glt->renderbuffernum);CHECKGLERROR
+ qglBindRenderbuffer(GL_RENDERBUFFER, glt->renderbuffernum);CHECKGLERROR
+ qglRenderbufferStorage(GL_RENDERBUFFER, glt->glinternalformat, glt->tilewidth, glt->tileheight);CHECKGLERROR
+ // note we can query the renderbuffer for info with glGetRenderbufferParameteriv for GL_WIDTH, GL_HEIGHt, GL_RED_SIZE, GL_GREEN_SIZE, GL_BLUE_SIZE, GL_GL_ALPHA_SIZE, GL_DEPTH_SIZE, GL_STENCIL_SIZE, GL_INTERNAL_FORMAT
+ qglBindRenderbuffer(GL_RENDERBUFFER, 0);CHECKGLERROR
+ break;
+ }
+
+ return (rtexture_t *)glt;
}
-int R_TextureHeight(rtexture_t *rt)
+int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed, qboolean hasalpha)
{
- return rt ? ((gltexture_t *)rt)->height : 0;
+#ifdef USE_GLES2
+ return -1; // unsupported on this platform
+#else
+ gltexture_t *glt = (gltexture_t *)rt;
+ unsigned char *dds;
+ int oldbindtexnum;
+ int bytesperpixel = 0;
+ int bytesperblock = 0;
+ int dds_flags;
+ int dds_format_flags;
+ int dds_caps1;
+ int dds_caps2;
+ int ret;
+ int mip;
+ int mipmaps;
+ int mipinfo[16][4];
+ int ddssize = 128;
+ GLint internalformat;
+ const char *ddsfourcc;
+ if (!rt)
+ return -1; // NULL pointer
+ if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
+ return -2; // broken driver - crashes on reading internal format
+ if (!qglGetTexLevelParameteriv)
+ return -2;
+ GL_ActiveTexture(0);
+ oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+ qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+ qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
+ switch(internalformat)
+ {
+ default: ddsfourcc = NULL;bytesperpixel = 4;break;
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
+ }
+ // if premultiplied alpha, say so in the DDS file
+ if(glt->flags & TEXF_RGBMULTIPLYBYALPHA)
+ {
+ switch(internalformat)
+ {
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT2";break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT4";break;
+ }
+ }
+ if (!bytesperblock && skipuncompressed)
+ return -3; // skipped
+ memset(mipinfo, 0, sizeof(mipinfo));
+ mipinfo[0][0] = glt->tilewidth;
+ mipinfo[0][1] = glt->tileheight;
+ mipmaps = 1;
+ if ((glt->flags & TEXF_MIPMAP) && !(glt->tilewidth == 1 && glt->tileheight == 1))
+ {
+ for (mip = 1;mip < 16;mip++)
+ {
+ mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
+ mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
+ if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
+ {
+ mip++;
+ break;
+ }
+ }
+ mipmaps = mip;
+ }
+ for (mip = 0;mip < mipmaps;mip++)
+ {
+ mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
+ mipinfo[mip][3] = ddssize;
+ ddssize += mipinfo[mip][2];
+ }
+ dds = (unsigned char *)Mem_Alloc(tempmempool, ddssize);
+ if (!dds)
+ return -4;
+ dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
+ dds_caps2 = 0;
+ if (bytesperblock)
+ {
+ dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
+ dds_format_flags = 0x4; // DDPF_FOURCC
+ }
+ else
+ {
+ dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
+ dds_format_flags = 0x40; // DDPF_RGB
+ }
+ if (mipmaps)
+ {
+ dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
+ dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
+ }
+ if(hasalpha)
+ dds_format_flags |= 0x1; // DDPF_ALPHAPIXELS
+ memcpy(dds, "DDS ", 4);
+ StoreLittleLong(dds+4, 124); // http://msdn.microsoft.com/en-us/library/bb943982%28v=vs.85%29.aspx says so
+ StoreLittleLong(dds+8, dds_flags);
+ StoreLittleLong(dds+12, mipinfo[0][1]); // height
+ StoreLittleLong(dds+16, mipinfo[0][0]); // width
+ StoreLittleLong(dds+24, 0); // depth
+ StoreLittleLong(dds+28, mipmaps); // mipmaps
+ StoreLittleLong(dds+76, 32); // format size
+ StoreLittleLong(dds+80, dds_format_flags);
+ StoreLittleLong(dds+108, dds_caps1);
+ StoreLittleLong(dds+112, dds_caps2);
+ if (bytesperblock)
+ {
+ StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
+ memcpy(dds+84, ddsfourcc, 4);
+ for (mip = 0;mip < mipmaps;mip++)
+ {
+ qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
+ }
+ }
+ else
+ {
+ StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
+ StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
+ dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
+ for (mip = 0;mip < mipmaps;mip++)
+ {
+ qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
+ }
+ }
+ qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+ ret = FS_WriteFile(filename, dds, ddssize);
+ Mem_Free(dds);
+ return ret ? ddssize : -5;
+#endif
}
-void R_FragmentLocation3D(rtexture_t *rt, int *x, int *y, int *z, float *fx1, float *fy1, float *fz1, float *fx2, float *fy2, float *fz2)
+#ifdef __ANDROID__
+// ELUAN: FIXME: separate this code
+#include "ktx10/include/ktx.h"
+#endif
+
+rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, qboolean srgb, int flags, qboolean *hasalphaflag, float *avgcolor, int miplevel, qboolean optionaltexture) // DDS textures are opaque, so miplevel isn't a pointer but just seen as a hint
{
+ int i, size, dds_format_flags, dds_miplevels, dds_width, dds_height;
+ //int dds_flags;
+ textype_t textype;
+ int bytesperblock, bytesperpixel;
+ int mipcomplete;
gltexture_t *glt;
- float iwidth, iheight, idepth;
+ gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
+ textypeinfo_t *texinfo;
+ int mip, mipwidth, mipheight, mipsize, mipsize_total;
+ unsigned int c, r, g, b;
+ GLint oldbindtexnum = 0;
+ unsigned char *mippixels;
+ unsigned char *mippixels_start;
+ unsigned char *ddspixels;
+ unsigned char *dds;
+ fs_offset_t ddsfilesize;
+ unsigned int ddssize;
+ qboolean force_swdecode;
+#ifdef __ANDROID__
+ // ELUAN: FIXME: separate this code
+ char vabuf[1024];
+ char vabuf2[1024];
+ int strsize;
+ KTX_dimensions sizes;
+#endif
+
if (cls.state == ca_dedicated)
+ return NULL;
+
+#ifdef __ANDROID__
+ // ELUAN: FIXME: separate this code
+ if (vid.renderpath != RENDERPATH_GLES2)
+ {
+ Con_DPrintf("KTX texture format is only supported on the GLES2 renderpath\n");
+ return NULL;
+ }
+
+ // some textures are specified with extensions, so it becomes .tga.dds
+ FS_StripExtension (filename, vabuf2, sizeof(vabuf2));
+ FS_StripExtension (vabuf2, vabuf, sizeof(vabuf));
+ FS_DefaultExtension (vabuf, ".ktx", sizeof(vabuf));
+ strsize = strlen(vabuf);
+ if (strsize > 5)
+ for (i = 0; i <= strsize - 4; i++) // copy null termination
+ vabuf[i] = vabuf[i + 4];
+
+ Con_DPrintf("Loading %s...\n", vabuf);
+ dds = FS_LoadFile(vabuf, tempmempool, true, &ddsfilesize);
+ ddssize = ddsfilesize;
+
+ if (!dds)
+ {
+ Con_DPrintf("Not found!\n");
+ return NULL; // not found
+ }
+ Con_DPrintf("Found!\n");
+
+ if (flags & TEXF_ALPHA)
+ {
+ Con_DPrintf("KTX texture with alpha not supported yet, disabling\n");
+ flags &= ~TEXF_ALPHA;
+ }
+
+ {
+ GLenum target;
+ GLenum glerror;
+ GLboolean isMipmapped;
+ KTX_error_code ktxerror;
+
+ glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
+
+ // texture uploading can take a while, so make sure we're sending keepalives
+ CL_KeepaliveMessage(false);
+
+ // create the texture object
+ CHECKGLERROR
+ GL_ActiveTexture(0);
+ oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[GLTEXTURETYPE_2D]);
+ qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
+ qglBindTexture(gltexturetypeenums[GLTEXTURETYPE_2D], glt->texnum);CHECKGLERROR
+
+ // upload the texture
+ // we need to restore the texture binding after finishing the upload
+
+ // NOTE: some drivers fail with ETC1 NPOT (only PowerVR?). This may make the driver crash later.
+ ktxerror = ktxLoadTextureM(dds, ddssize, &glt->texnum, &target, &sizes, &isMipmapped, &glerror,
+ 0, NULL);// can't CHECKGLERROR, the lib catches it
+
+ // FIXME: delete texture if we fail here
+ if (target != GL_TEXTURE_2D)
+ {
+ qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+ Mem_Free(dds);
+ Con_DPrintf("%s target != GL_TEXTURE_2D, target == %x\n", vabuf, target);
+ return NULL; // FIXME: delete the texture from memory
+ }
+
+ if (KTX_SUCCESS == ktxerror)
+ {
+ textype = TEXTYPE_ETC1;
+ flags &= ~TEXF_COMPRESS; // don't let the textype be wrong
+
+ // return whether this texture is transparent
+ if (hasalphaflag)
+ *hasalphaflag = (flags & TEXF_ALPHA) != 0;
+
+ // TODO: apply gl_picmip
+ // TODO: avgcolor
+ // TODO: srgb
+ // TODO: only load mipmaps if requested
+
+ if (isMipmapped)
+ flags |= TEXF_MIPMAP;
+ else
+ flags &= ~TEXF_MIPMAP;
+
+ texinfo = R_GetTexTypeInfo(textype, flags);
+
+ strlcpy (glt->identifier, vabuf, sizeof(glt->identifier));
+ glt->pool = pool;
+ glt->chain = pool->gltchain;
+ pool->gltchain = glt;
+ glt->inputwidth = sizes.width;
+ glt->inputheight = sizes.height;
+ glt->inputdepth = 1;
+ glt->flags = flags;
+ glt->textype = texinfo;
+ glt->texturetype = GLTEXTURETYPE_2D;
+ glt->inputdatasize = ddssize;
+ glt->glinternalformat = texinfo->glinternalformat;
+ glt->glformat = texinfo->glformat;
+ glt->gltype = texinfo->gltype;
+ glt->bytesperpixel = texinfo->internalbytesperpixel;
+ glt->sides = 1;
+ glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
+ glt->tilewidth = sizes.width;
+ glt->tileheight = sizes.height;
+ glt->tiledepth = 1;
+ glt->miplevels = isMipmapped ? 1 : 0; // FIXME
+
+ // after upload we have to set some parameters...
+#ifdef GL_TEXTURE_MAX_LEVEL
+ /* FIXME
+ if (dds_miplevels >= 1 && !mipcomplete)
+ {
+ // need to set GL_TEXTURE_MAX_LEVEL
+ qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
+ }
+ */
+#endif
+ GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
+
+ qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+ Mem_Free(dds);
+ return (rtexture_t *)glt;
+ }
+ else
+ {
+ qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+ Mem_Free(dds);
+ Con_DPrintf("KTX texture %s failed to load: %x\n", vabuf, ktxerror);
+ return NULL;
+ }
+ }
+#endif // __ANDROID__
+
+ dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
+ ddssize = ddsfilesize;
+
+ if (!dds)
+ {
+ if (r_texture_dds_load_logfailure.integer && (r_texture_dds_load_logfailure.integer >= 2 || !optionaltexture))
+ Log_Printf("ddstexturefailures.log", "%s\n", filename);
+ return NULL; // not found
+ }
+
+ if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: not a DDS image\n", filename);
+ return NULL;
+ }
+
+ //dds_flags = BuffLittleLong(dds+8);
+ dds_format_flags = BuffLittleLong(dds+80);
+ dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
+ dds_width = BuffLittleLong(dds+16);
+ dds_height = BuffLittleLong(dds+12);
+ ddspixels = dds + 128;
+
+ if(r_texture_dds_load_alphamode.integer == 0)
+ if(!(dds_format_flags & 0x1)) // DDPF_ALPHAPIXELS
+ flags &= ~TEXF_ALPHA;
+
+ //flags &= ~TEXF_ALPHA; // disabled, as we DISABLE TEXF_ALPHA in the alpha detection, not enable it!
+ if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
{
- if (x)
- *x = 0;
- if (y)
- *y = 0;
- if (z)
- *z = 0;
- if (fx1 || fy1 || fx2 || fy2)
+ // very sloppy BGRA 32bit identification
+ textype = TEXTYPE_BGRA;
+ flags &= ~TEXF_COMPRESS; // don't let the textype be wrong
+ bytesperblock = 0;
+ bytesperpixel = 4;
+ size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
+ if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
{
- if (fx1)
- *fx1 = 0;
- if (fy1)
- *fy1 = 0;
- if (fz1)
- *fz1 = 0;
- if (fx2)
- *fx2 = 1;
- if (fy2)
- *fy2 = 1;
- if (fz2)
- *fz2 = 1;
+ Mem_Free(dds);
+ Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
+ return NULL;
+ }
+ if((r_texture_dds_load_alphamode.integer == 1) && (flags & TEXF_ALPHA))
+ {
+ // check alpha
+ for (i = 3;i < size;i += 4)
+ if (ddspixels[i] < 255)
+ break;
+ if (i >= size)
+ flags &= ~TEXF_ALPHA;
}
- return;
}
- if (!rt)
- Host_Error("R_FragmentLocation: no texture supplied\n");
- glt = (gltexture_t *)rt;
- if (glt->flags & TEXF_FRAGMENT)
+ else if (!memcmp(dds+84, "DXT1", 4))
+ {
+ // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
+ // LordHavoc: it is my belief that this does not infringe on the
+ // patent because it is not decoding pixels...
+ textype = TEXTYPE_DXT1;
+ bytesperblock = 8;
+ bytesperpixel = 0;
+ //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
+ size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
+ if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
+ return NULL;
+ }
+ if (flags & TEXF_ALPHA)
+ {
+ if (r_texture_dds_load_alphamode.integer == 1)
+ {
+ // check alpha
+ for (i = 0;i < size;i += bytesperblock)
+ if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
+ {
+ // NOTE: this assumes sizeof(unsigned int) == 4
+ unsigned int data = * (unsigned int *) &(ddspixels[i+4]);
+ // check if data, in base 4, contains a digit 3 (DXT1: transparent pixel)
+ if(data & (data<<1) & 0xAAAAAAAA)//rgh
+ break;
+ }
+ if (i < size)
+ textype = TEXTYPE_DXT1A;
+ else
+ flags &= ~TEXF_ALPHA;
+ }
+ else if (r_texture_dds_load_alphamode.integer == 0)
+ textype = TEXTYPE_DXT1A;
+ else
+ {
+ flags &= ~TEXF_ALPHA;
+ }
+ }
+ }
+ else if (!memcmp(dds+84, "DXT3", 4) || !memcmp(dds+84, "DXT2", 4))
+ {
+ if(!memcmp(dds+84, "DXT2", 4))
+ {
+ if(!(flags & TEXF_RGBMULTIPLYBYALPHA))
+ {
+ Con_Printf("^1%s: expecting DXT3 image without premultiplied alpha, got DXT2 image with premultiplied alpha\n", filename);
+ }
+ }
+ else
+ {
+ if(flags & TEXF_RGBMULTIPLYBYALPHA)
+ {
+ Con_Printf("^1%s: expecting DXT2 image without premultiplied alpha, got DXT3 image without premultiplied alpha\n", filename);
+ }
+ }
+ textype = TEXTYPE_DXT3;
+ bytesperblock = 16;
+ bytesperpixel = 0;
+ size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
+ if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
+ return NULL;
+ }
+ // we currently always assume alpha
+ }
+ else if (!memcmp(dds+84, "DXT5", 4) || !memcmp(dds+84, "DXT4", 4))
+ {
+ if(!memcmp(dds+84, "DXT4", 4))
+ {
+ if(!(flags & TEXF_RGBMULTIPLYBYALPHA))
+ {
+ Con_Printf("^1%s: expecting DXT5 image without premultiplied alpha, got DXT4 image with premultiplied alpha\n", filename);
+ }
+ }
+ else
+ {
+ if(flags & TEXF_RGBMULTIPLYBYALPHA)
+ {
+ Con_Printf("^1%s: expecting DXT4 image without premultiplied alpha, got DXT5 image without premultiplied alpha\n", filename);
+ }
+ }
+ textype = TEXTYPE_DXT5;
+ bytesperblock = 16;
+ bytesperpixel = 0;
+ size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
+ if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
+ return NULL;
+ }
+ // we currently always assume alpha
+ }
+ else
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
+ return NULL;
+ }
+
+ // when requesting a non-alpha texture and we have DXT3/5, convert to DXT1
+ if(!(flags & TEXF_ALPHA) && (textype == TEXTYPE_DXT3 || textype == TEXTYPE_DXT5))
+ {
+ textype = TEXTYPE_DXT1;
+ bytesperblock = 8;
+ ddssize -= 128;
+ ddssize /= 2;
+ for (i = 0;i < (int)ddssize;i += bytesperblock)
+ memcpy(&ddspixels[i], &ddspixels[(i<<1)+8], 8);
+ ddssize += 128;
+ }
+
+ force_swdecode = false;
+ if(bytesperblock)
+ {
+ if(vid.support.arb_texture_compression && vid.support.ext_texture_compression_s3tc)
+ {
+ if(r_texture_dds_swdecode.integer > 1)
+ force_swdecode = true;
+ }
+ else
+ {
+ if(r_texture_dds_swdecode.integer < 1)
+ {
+ // unsupported
+ Mem_Free(dds);
+ return NULL;
+ }
+ force_swdecode = true;
+ }
+ }
+
+ // return whether this texture is transparent
+ if (hasalphaflag)
+ *hasalphaflag = (flags & TEXF_ALPHA) != 0;
+
+ // if we SW decode, choose 2 sizes bigger
+ if(force_swdecode)
+ {
+ // this is quarter res, so do not scale down more than we have to
+ miplevel -= 2;
+
+ if(miplevel < 0)
+ Con_DPrintf("WARNING: fake software decoding of compressed texture %s degraded quality\n", filename);
+ }
+
+ // this is where we apply gl_picmip
+ mippixels_start = ddspixels;
+ mipwidth = dds_width;
+ mipheight = dds_height;
+ while(miplevel >= 1 && dds_miplevels >= 1)
+ {
+ if (mipwidth <= 1 && mipheight <= 1)
+ break;
+ mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
+ mippixels_start += mipsize; // just skip
+ --dds_miplevels;
+ --miplevel;
+ if (mipwidth > 1)
+ mipwidth >>= 1;
+ if (mipheight > 1)
+ mipheight >>= 1;
+ }
+ mipsize_total = ddssize - 128 - (mippixels_start - ddspixels);
+ mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
+
+ // from here on, we do not need the ddspixels and ddssize any more (apart from the statistics entry in glt)
+
+ // fake decode S3TC if needed
+ if(force_swdecode)
+ {
+ int mipsize_new = mipsize_total / bytesperblock * 4;
+ unsigned char *mipnewpixels = (unsigned char *) Mem_Alloc(tempmempool, mipsize_new);
+ unsigned char *p = mipnewpixels;
+ for (i = bytesperblock == 16 ? 8 : 0;i < (int)mipsize_total;i += bytesperblock, p += 4)
+ {
+ c = mippixels_start[i] + 256*mippixels_start[i+1] + 65536*mippixels_start[i+2] + 16777216*mippixels_start[i+3];
+ p[2] = (((c >> 11) & 0x1F) + ((c >> 27) & 0x1F)) * (0.5f / 31.0f * 255.0f);
+ p[1] = (((c >> 5) & 0x3F) + ((c >> 21) & 0x3F)) * (0.5f / 63.0f * 255.0f);
+ p[0] = (((c ) & 0x1F) + ((c >> 16) & 0x1F)) * (0.5f / 31.0f * 255.0f);
+ if(textype == TEXTYPE_DXT5)
+ p[3] = (0.5 * mippixels_start[i-8] + 0.5 * mippixels_start[i-7]);
+ else if(textype == TEXTYPE_DXT3)
+ p[3] = (
+ (mippixels_start[i-8] & 0x0F)
+ + (mippixels_start[i-8] >> 4)
+ + (mippixels_start[i-7] & 0x0F)
+ + (mippixels_start[i-7] >> 4)
+ + (mippixels_start[i-6] & 0x0F)
+ + (mippixels_start[i-6] >> 4)
+ + (mippixels_start[i-5] & 0x0F)
+ + (mippixels_start[i-5] >> 4)
+ ) * (0.125f / 15.0f * 255.0f);
+ else
+ p[3] = 255;
+ }
+
+ textype = TEXTYPE_BGRA;
+ bytesperblock = 0;
+ bytesperpixel = 4;
+
+ // as each block becomes a pixel, we must use pixel count for this
+ mipwidth = (mipwidth + 3) / 4;
+ mipheight = (mipheight + 3) / 4;
+ mipsize = bytesperpixel * mipwidth * mipheight;
+ mippixels_start = mipnewpixels;
+ mipsize_total = mipsize_new;
+ }
+
+ // start mip counting
+ mippixels = mippixels_start;
+
+ // calculate average color if requested
+ if (avgcolor)
{
- if (x)
- *x = glt->x;
- if (y)
- *y = glt->y;
- if (fx1 || fy1 || fx2 || fy2)
+ float f;
+ Vector4Clear(avgcolor);
+ if (bytesperblock)
+ {
+ for (i = bytesperblock == 16 ? 8 : 0;i < mipsize;i += bytesperblock)
+ {
+ c = mippixels[i] + 256*mippixels[i+1] + 65536*mippixels[i+2] + 16777216*mippixels[i+3];
+ avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
+ avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
+ avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
+ if(textype == TEXTYPE_DXT5)
+ avgcolor[3] += (mippixels[i-8] + (int) mippixels[i-7]) * (0.5f / 255.0f);
+ else if(textype == TEXTYPE_DXT3)
+ avgcolor[3] += (
+ (mippixels_start[i-8] & 0x0F)
+ + (mippixels_start[i-8] >> 4)
+ + (mippixels_start[i-7] & 0x0F)
+ + (mippixels_start[i-7] >> 4)
+ + (mippixels_start[i-6] & 0x0F)
+ + (mippixels_start[i-6] >> 4)
+ + (mippixels_start[i-5] & 0x0F)
+ + (mippixels_start[i-5] >> 4)
+ ) * (0.125f / 15.0f);
+ else
+ avgcolor[3] += 1.0f;
+ }
+ f = (float)bytesperblock / mipsize;
+ avgcolor[0] *= (0.5f / 31.0f) * f;
+ avgcolor[1] *= (0.5f / 63.0f) * f;
+ avgcolor[2] *= (0.5f / 31.0f) * f;
+ avgcolor[3] *= f;
+ }
+ else
+ {
+ for (i = 0;i < mipsize;i += 4)
+ {
+ avgcolor[0] += mippixels[i+2];
+ avgcolor[1] += mippixels[i+1];
+ avgcolor[2] += mippixels[i];
+ avgcolor[3] += mippixels[i+3];
+ }
+ f = (1.0f / 255.0f) * bytesperpixel / mipsize;
+ avgcolor[0] *= f;
+ avgcolor[1] *= f;
+ avgcolor[2] *= f;
+ avgcolor[3] *= f;
+ }
+ }
+
+ // if we want sRGB, convert now
+ if(srgb)
+ {
+ if (vid.support.ext_texture_srgb)
+ {
+ switch(textype)
+ {
+ case TEXTYPE_DXT1: textype = TEXTYPE_SRGB_DXT1 ;break;
+ case TEXTYPE_DXT1A: textype = TEXTYPE_SRGB_DXT1A ;break;
+ case TEXTYPE_DXT3: textype = TEXTYPE_SRGB_DXT3 ;break;
+ case TEXTYPE_DXT5: textype = TEXTYPE_SRGB_DXT5 ;break;
+ case TEXTYPE_RGBA: textype = TEXTYPE_SRGB_RGBA ;break;
+ default:
+ break;
+ }
+ }
+ else
{
- iwidth = 1.0f / glt->image->width;
- iheight = 1.0f / glt->image->height;
- idepth = 1.0f / glt->image->depth;
- if (fx1)
- *fx1 = glt->x * iwidth;
- if (fy1)
- *fy1 = glt->y * iheight;
- if (fz1)
- *fz1 = glt->z * idepth;
- if (fx2)
- *fx2 = (glt->x + glt->width) * iwidth;
- if (fy2)
- *fy2 = (glt->y + glt->height) * iheight;
- if (fz2)
- *fz2 = (glt->z + glt->depth) * idepth;
+ switch(textype)
+ {
+ case TEXTYPE_DXT1:
+ case TEXTYPE_DXT1A:
+ case TEXTYPE_DXT3:
+ case TEXTYPE_DXT5:
+ {
+ for (i = bytesperblock == 16 ? 8 : 0;i < mipsize_total;i += bytesperblock)
+ {
+ int c0, c1, c0new, c1new;
+ c0 = mippixels_start[i] + 256*mippixels_start[i+1];
+ r = ((c0 >> 11) & 0x1F);
+ g = ((c0 >> 5) & 0x3F);
+ b = ((c0 ) & 0x1F);
+ r = floor(Image_LinearFloatFromsRGB(r * (255.0f / 31.0f)) * 31.0f + 0.5f); // these multiplications here get combined with multiplications in Image_LinearFloatFromsRGB
+ g = floor(Image_LinearFloatFromsRGB(g * (255.0f / 63.0f)) * 63.0f + 0.5f); // these multiplications here get combined with multiplications in Image_LinearFloatFromsRGB
+ b = floor(Image_LinearFloatFromsRGB(b * (255.0f / 31.0f)) * 31.0f + 0.5f); // these multiplications here get combined with multiplications in Image_LinearFloatFromsRGB
+ c0new = (r << 11) | (g << 5) | b;
+ c1 = mippixels_start[i+2] + 256*mippixels_start[i+3];
+ r = ((c1 >> 11) & 0x1F);
+ g = ((c1 >> 5) & 0x3F);
+ b = ((c1 ) & 0x1F);
+ r = floor(Image_LinearFloatFromsRGB(r * (255.0f / 31.0f)) * 31.0f + 0.5f); // these multiplications here get combined with multiplications in Image_LinearFloatFromsRGB
+ g = floor(Image_LinearFloatFromsRGB(g * (255.0f / 63.0f)) * 63.0f + 0.5f); // these multiplications here get combined with multiplications in Image_LinearFloatFromsRGB
+ b = floor(Image_LinearFloatFromsRGB(b * (255.0f / 31.0f)) * 31.0f + 0.5f); // these multiplications here get combined with multiplications in Image_LinearFloatFromsRGB
+ c1new = (r << 11) | (g << 5) | b;
+ // swap the colors if needed to fix order
+ if(c0 > c1) // thirds
+ {
+ if(c0new < c1new)
+ {
+ c = c0new;
+ c0new = c1new;
+ c1new = c;
+ if(c0new == c1new)
+ mippixels_start[i+4] ^= 0x55;
+ mippixels_start[i+5] ^= 0x55;
+ mippixels_start[i+6] ^= 0x55;
+ mippixels_start[i+7] ^= 0x55;
+ }
+ else if(c0new == c1new)
+ {
+ mippixels_start[i+4] = 0x00;
+ mippixels_start[i+5] = 0x00;
+ mippixels_start[i+6] = 0x00;
+ mippixels_start[i+7] = 0x00;
+ }
+ }
+ else // half + transparent
+ {
+ if(c0new > c1new)
+ {
+ c = c0new;
+ c0new = c1new;
+ c1new = c;
+ mippixels_start[i+4] ^= (~mippixels_start[i+4] >> 1) & 0x55;
+ mippixels_start[i+5] ^= (~mippixels_start[i+5] >> 1) & 0x55;
+ mippixels_start[i+6] ^= (~mippixels_start[i+6] >> 1) & 0x55;
+ mippixels_start[i+7] ^= (~mippixels_start[i+7] >> 1) & 0x55;
+ }
+ }
+ mippixels_start[i] = c0new & 255;
+ mippixels_start[i+1] = c0new >> 8;
+ mippixels_start[i+2] = c1new & 255;
+ mippixels_start[i+3] = c1new >> 8;
+ }
+ }
+ break;
+ case TEXTYPE_RGBA:
+ Image_MakeLinearColorsFromsRGB(mippixels, mippixels, mipsize_total / bytesperblock);
+ break;
+ default:
+ break;
+ }
}
}
+
+ // when not requesting mipmaps, do not load them
+ if(!(flags & TEXF_MIPMAP))
+ dds_miplevels = 0;
+
+ if (dds_miplevels >= 1)
+ flags |= TEXF_MIPMAP;
else
+ flags &= ~TEXF_MIPMAP;
+
+ texinfo = R_GetTexTypeInfo(textype, flags);
+
+ glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
+ strlcpy (glt->identifier, filename, sizeof(glt->identifier));
+ glt->pool = pool;
+ glt->chain = pool->gltchain;
+ pool->gltchain = glt;
+ glt->inputwidth = mipwidth;
+ glt->inputheight = mipheight;
+ glt->inputdepth = 1;
+ glt->flags = flags;
+ glt->textype = texinfo;
+ glt->texturetype = GLTEXTURETYPE_2D;
+ glt->inputdatasize = ddssize;
+ glt->glinternalformat = texinfo->glinternalformat;
+ glt->glformat = texinfo->glformat;
+ glt->gltype = texinfo->gltype;
+ glt->bytesperpixel = texinfo->internalbytesperpixel;
+ glt->sides = 1;
+ glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
+ glt->tilewidth = mipwidth;
+ glt->tileheight = mipheight;
+ glt->tiledepth = 1;
+ glt->miplevels = dds_miplevels;
+
+ // texture uploading can take a while, so make sure we're sending keepalives
+ CL_KeepaliveMessage(false);
+
+ // create the texture object
+ switch(vid.renderpath)
{
- if (x)
- *x = 0;
- if (y)
- *y = 0;
- if (z)
- *z = 0;
- if (fx1 || fy1 || fx2 || fy2)
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
+ CHECKGLERROR
+ GL_ActiveTexture(0);
+ oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+ qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
+ qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+ break;
+ }
+
+ // upload the texture
+ // we need to restore the texture binding after finishing the upload
+ mipcomplete = false;
+
+ for (mip = 0;mip <= dds_miplevels;mip++) // <= to include the not-counted "largest" miplevel
+ {
+ unsigned char *upload_mippixels = mippixels;
+ int upload_mipwidth = mipwidth;
+ int upload_mipheight = mipheight;
+ mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
+ if (mippixels + mipsize > mippixels_start + mipsize_total)
+ break;
+ switch(vid.renderpath)
{
- if (fx1)
- *fx1 = 0;
- if (fy1)
- *fy1 = 0;
- if (fz1)
- *fz1 = 0;
- if (fx2)
- *fx2 = 1;
- if (fy2)
- *fy2 = 1;
- if (fz2)
- *fz2 = 1;
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
+ if (bytesperblock)
+ {
+ qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, upload_mipwidth, upload_mipheight, 0, mipsize, upload_mippixels);CHECKGLERROR
+ }
+ else
+ {
+ qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, upload_mipwidth, upload_mipheight, 0, glt->glformat, glt->gltype, upload_mippixels);CHECKGLERROR
+ }
+ break;
+ }
+ if(upload_mippixels != mippixels)
+ Mem_Free(upload_mippixels);
+ mippixels += mipsize;
+ if (mipwidth <= 1 && mipheight <= 1)
+ {
+ mipcomplete = true;
+ break;
}
+ if (mipwidth > 1)
+ mipwidth >>= 1;
+ if (mipheight > 1)
+ mipheight >>= 1;
}
+
+ // after upload we have to set some parameters...
+ switch(vid.renderpath)
+ {
+ case RENDERPATH_GL32:
+ case RENDERPATH_GLES2:
+#ifdef GL_TEXTURE_MAX_LEVEL
+ if (dds_miplevels >= 1 && !mipcomplete)
+ {
+ // need to set GL_TEXTURE_MAX_LEVEL
+ qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
+ }
+#endif
+ GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
+ qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+ break;
+ }
+
+ Mem_Free(dds);
+ if(force_swdecode)
+ Mem_Free((unsigned char *) mippixels_start);
+ return (rtexture_t *)glt;
}
-void R_FragmentLocation(rtexture_t *rt, int *x, int *y, float *fx1, float *fy1, float *fx2, float *fy2)
+int R_TextureWidth(rtexture_t *rt)
{
- R_FragmentLocation3D(rt, x, y, NULL, fx1, fy1, NULL, fx2, fy2, NULL);
+ return rt ? ((gltexture_t *)rt)->inputwidth : 0;
}
-int R_CompatibleFragmentWidth(int width, int textype, int flags)
+int R_TextureHeight(rtexture_t *rt)
{
- return width;
+ return rt ? ((gltexture_t *)rt)->inputheight : 0;
}
-void R_UpdateTexture(rtexture_t *rt, qbyte *data)
+int R_TextureFlags(rtexture_t *rt)
{
- gltexture_t *glt;
- if (rt == NULL)
- Host_Error("R_UpdateTexture: no texture supplied\n");
+ return rt ? ((gltexture_t *)rt)->flags : 0;
+}
+
+void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int z, int width, int height, int depth)
+{
+ gltexture_t *glt = (gltexture_t *)rt;
if (data == NULL)
- Host_Error("R_UpdateTexture: no data supplied\n");
- glt = (gltexture_t *)rt;
+ Host_Error("R_UpdateTexture: no data supplied");
+ if (glt == NULL)
+ Host_Error("R_UpdateTexture: no texture supplied");
+ if (!glt->texnum)
+ {
+ Con_DPrintf("R_UpdateTexture: texture %p \"%s\" in pool %p has not been uploaded yet\n", (void *)glt, glt->identifier, (void *)glt->pool);
+ return;
+ }
+ // update part of the texture
+ 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 (glt->inputdepth != 1 || glt->sides != 1)
+ Sys_Error("R_UpdateTexture on buffered texture that is not 2D\n");
+ 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->dirty = true;
+ glt->buffermodified = true;
+ output += y*outputskip + x*bpp;
+ for (j = 0;j < height;j++, output += outputskip, input += inputskip)
+ memcpy(output, input, width*bpp);
+ }
+ else if (x || y || z || width != glt->inputwidth || height != glt->inputheight || depth != glt->inputdepth)
+ R_UploadPartialTexture(glt, data, x, y, z, width, height, depth);
+ else
+ R_UploadFullTexture(glt, data);
+}
- // if it has not been uploaded yet, update the data that will be used when it is
- if (glt->inputtexels)
- memcpy(glt->inputtexels, data, glt->inputdatasize);
+int R_RealGetTexture(rtexture_t *rt)
+{
+ if (rt)
+ {
+ gltexture_t *glt;
+ glt = (gltexture_t *)rt;
+ if (glt->flags & GLTEXF_DYNAMIC)
+ R_UpdateDynamicTexture(glt);
+ if (glt->buffermodified && glt->bufferpixels)
+ {
+ glt->buffermodified = false;
+ R_UploadFullTexture(glt, glt->bufferpixels);
+ }
+ glt->dirty = false;
+ return glt->texnum;
+ }
else
- R_Upload(glt, data);
+ return r_texture_white->texnum;
+}
+
+void R_ClearTexture (rtexture_t *rt)
+{
+ gltexture_t *glt = (gltexture_t *)rt;
+
+ R_UploadFullTexture(glt, NULL);
}
+int R_PicmipForFlags(int flags)
+{
+ int miplevel = 0;
+ if(flags & TEXF_PICMIP)
+ {
+ miplevel += gl_picmip.integer;
+ if (flags & TEXF_ISWORLD)
+ {
+ if (r_picmipworld.integer)
+ miplevel += gl_picmip_world.integer;
+ else
+ miplevel = 0;
+ }
+ else if (flags & TEXF_ISSPRITE)
+ {
+ if (r_picmipsprites.integer)
+ miplevel += gl_picmip_sprites.integer;
+ else
+ miplevel = 0;
+ }
+ else
+ miplevel += gl_picmip_other.integer;
+ }
+ return max(0, miplevel);
+}