diff --git a/code/renderergl2/tr_bsp.c b/code/renderergl2/tr_bsp.c index d73242b8..6c4cbe21 100644 --- a/code/renderergl2/tr_bsp.c +++ b/code/renderergl2/tr_bsp.c @@ -3068,23 +3068,41 @@ void R_AssignCubemapsToWorldSurfaces(void) } -void R_RenderAllCubemaps(void) +void R_LoadCubemaps(void) +{ + int i; + imgFlags_t flags = IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_NOLIGHTSCALE | IMGFLAG_CUBEMAP; + + for (i = 0; i < tr.numCubemaps; i++) + { + char filename[MAX_QPATH]; + cubemap_t *cubemap = &tr.cubemaps[i]; + + Com_sprintf(filename, MAX_QPATH, "cubemaps/%s/%03d.dds", tr.world->baseName, i); + + cubemap->image = R_FindImageFile(filename, IMGTYPE_COLORALPHA, flags); + } +} + + +void R_RenderMissingCubemaps(void) { int i, j; + imgFlags_t flags = IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_NOLIGHTSCALE | IMGFLAG_CUBEMAP; for (i = 0; i < tr.numCubemaps; i++) { - tr.cubemaps[i].image = R_CreateImage(va("*cubeMap%d", i), NULL, r_cubemapSize->integer, r_cubemapSize->integer, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_CUBEMAP, GL_RGBA8); - } - - for (i = 0; i < tr.numCubemaps; i++) - { - for (j = 0; j < 6; j++) + if (!tr.cubemaps[i].image) { - RE_ClearScene(); - R_RenderCubemapSide(i, j, qfalse); - R_IssuePendingRenderCommands(); - R_InitNextFrame(); + tr.cubemaps[i].image = R_CreateImage(va("*cubeMap%d", i), NULL, r_cubemapSize->integer, r_cubemapSize->integer, IMGTYPE_COLORALPHA, flags, GL_RGBA8); + + for (j = 0; j < 6; j++) + { + RE_ClearScene(); + R_RenderCubemapSide(i, j, qfalse); + R_IssuePendingRenderCommands(); + R_InitNextFrame(); + } } } } @@ -3425,10 +3443,11 @@ void RE_LoadWorldMap( const char *name ) { // make sure the VAO glState entry is safe R_BindNullVao(); - // Render all cubemaps + // Render or load all cubemaps if (r_cubeMapping->integer && tr.numCubemaps) { - R_RenderAllCubemaps(); + R_LoadCubemaps(); + R_RenderMissingCubemaps(); } ri.FS_FreeFile( buffer.v ); diff --git a/code/renderergl2/tr_dsa.c b/code/renderergl2/tr_dsa.c index aeb39d78..a766263a 100644 --- a/code/renderergl2/tr_dsa.c +++ b/code/renderergl2/tr_dsa.c @@ -68,6 +68,9 @@ int GL_BindMultiTexture(GLenum texunit, GLenum target, GLuint texture) if (glDsaState.textures[tmu] == texture) return 0; + if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) + target = GL_TEXTURE_CUBE_MAP; + qglBindMultiTexture(texunit, target, texture); glDsaState.textures[tmu] = texture; return 1; diff --git a/code/renderergl2/tr_image.c b/code/renderergl2/tr_image.c index 7d53d463..e508b15d 100644 --- a/code/renderergl2/tr_image.c +++ b/code/renderergl2/tr_image.c @@ -1863,82 +1863,57 @@ static void RawImage_UploadToRgtc2Texture(GLuint texture, byte *data, int width, ri.Hunk_FreeTempMemory(compressedData); } +static int CalculateMipSize(int width, int height, GLenum picFormat) +{ + int numBlocks = ((width + 3) / 4) * ((height + 3) / 4); + int numPixels = width * height; -static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int width, int height, GLenum picFormat, int numMips, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture ) + switch (picFormat) + { + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RED_RGTC1: + case GL_COMPRESSED_SIGNED_RED_RGTC1: + return numBlocks * 8; + + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + case GL_COMPRESSED_RG_RGTC2: + case GL_COMPRESSED_SIGNED_RG_RGTC2: + case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB: + case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB: + case GL_COMPRESSED_RGBA_BPTC_UNORM_ARB: + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: + return numBlocks * 16; + + case GL_RGBA8: + case GL_SRGB8_ALPHA8_EXT: + return numPixels * 4; + + default: + ri.Printf(PRINT_ALL, "Unsupported texture format %08x\n", picFormat); + return 0; + } + + return 0; +} + + +static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int width, int height, GLenum target, GLenum picFormat, int numMips, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture ) { int dataFormat, dataType; qboolean rgtc = (internalFormat == GL_COMPRESSED_RG_RGTC2); + qboolean compressed = (!(picFormat == GL_RGBA8) || (picFormat == GL_SRGB8_ALPHA8_EXT)); + qboolean mipmap = !!(flags & IMGFLAG_MIPMAP); + qboolean picmip = !!(flags & IMGFLAG_PICMIP); + int size, miplevel; + qboolean lastMip = qfalse; - if (data && picFormat != GL_RGBA8 && picFormat != GL_SRGB8_ALPHA8_EXT) - { - int bytesPer4x4Block = 0; - int miplevel = 0; - - switch (picFormat) - { - case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: - case GL_COMPRESSED_RED_RGTC1: - case GL_COMPRESSED_SIGNED_RED_RGTC1: - bytesPer4x4Block = 8; - break; - case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: - case GL_COMPRESSED_RG_RGTC2: - case GL_COMPRESSED_SIGNED_RG_RGTC2: - case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB: - case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB: - case GL_COMPRESSED_RGBA_BPTC_UNORM_ARB: - case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: - bytesPer4x4Block = 16; - break; - default: - ri.Printf(PRINT_ALL, "Unsupported texture format %08x\n", picFormat); - return; - break; - } - - if (flags & IMGFLAG_PICMIP) - { - for (miplevel = r_picmip->integer; miplevel > 0 && numMips > 1; miplevel--, numMips--) - { - int size = ((width + 3) / 4) * ((height + 3) / 4) * bytesPer4x4Block; - - x >>= 1; - y >>= 1; - width = MAX(1, width >> 1); - height = MAX(1, height >> 1); - data += size; - } - } - - if (!(flags & IMGFLAG_MIPMAP)) - numMips = 1; - - for (miplevel = 0; miplevel < numMips; miplevel++) - { - int size = ((width + 3) / 4) * ((height + 3) / 4) * bytesPer4x4Block; - - if (subtexture) - qglCompressedTextureSubImage2D(texture, GL_TEXTURE_2D, miplevel, x, y, width, height, internalFormat, size, data); - else - qglCompressedTextureImage2D(texture, GL_TEXTURE_2D, miplevel, internalFormat, width, height, 0, size, data); - - x >>= 1; - y >>= 1; - width = MAX(1, width >> 1); - height = MAX(1, height >> 1); - data += size; - } - - return; - } - - switch(internalFormat) + switch (internalFormat) { case GL_DEPTH_COMPONENT: case GL_DEPTH_COMPONENT16_ARB: @@ -1957,52 +1932,83 @@ static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int break; } - if ( subtexture ) - qglTextureSubImage2D(texture, GL_TEXTURE_2D, 0, x, y, width, height, dataFormat, dataType, data ); - else + if (!data) { - if (rgtc) - RawImage_UploadToRgtc2Texture(texture, data, width, height, 0); - else - qglTextureImage2D(texture, GL_TEXTURE_2D, 0, internalFormat, width, height, 0, dataFormat, dataType, data); - } - - if (flags & IMGFLAG_MIPMAP) - { - int miplevel = 0; - - while (width > 1 || height > 1) + miplevel = 0; + do { - if (data) - { - if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) - R_MipMapNormalHeight( data, data, width, height, glRefConfig.swizzleNormalmap ); - else - R_MipMapsRGB( data, width, height ); - } - + lastMip = (width == 1 && height == 1) || !mipmap; + qglTextureImage2D(texture, target, miplevel, internalFormat, width, height, 0, dataFormat, dataType, NULL); + width = MAX(1, width >> 1); height = MAX(1, height >> 1); miplevel++; + } + while (!lastMip); - if ( data && r_colorMipLevels->integer ) - R_BlendOverTexture( (byte *)data, width * height, mipBlendColors[miplevel] ); + return; + } - if ( subtexture ) - { - x >>= 1; - y >>= 1; - qglTextureSubImage2D(texture, GL_TEXTURE_2D, miplevel, x, y, width, height, dataFormat, dataType, data ); - } - else - { - if (rgtc) - RawImage_UploadToRgtc2Texture(texture, data, width, height, miplevel); - else - qglTextureImage2D(texture, GL_TEXTURE_2D, miplevel, internalFormat, width, height, 0, dataFormat, dataType, data); - } + if (compressed && picmip) + { + for (miplevel = r_picmip->integer; miplevel > 0 && numMips > 1; miplevel--, numMips--) + { + size = CalculateMipSize(width, height, picFormat); + x >>= 1; + y >>= 1; + width = MAX(1, width >> 1); + height = MAX(1, height >> 1); + data += size; } } + + miplevel = 0; + do + { + lastMip = (width == 1 && height == 1) || !mipmap; + size = CalculateMipSize(width, height, picFormat); + + if (compressed) + { + if (subtexture) + qglCompressedTextureSubImage2D(texture, target, miplevel, x, y, width, height, picFormat, size, data); + else + qglCompressedTextureImage2D(texture, target, miplevel, picFormat, width, height, 0, size, data); + } + else + { + if (miplevel != 0 && r_colorMipLevels->integer) + R_BlendOverTexture((byte *)data, width * height, mipBlendColors[miplevel]); + + if (rgtc) + RawImage_UploadToRgtc2Texture(texture, data, width, height, miplevel); + else if (subtexture) + qglTextureSubImage2D(texture, target, miplevel, x, y, width, height, dataFormat, dataType, data); + else + qglTextureImage2D(texture, target, miplevel, internalFormat, width, height, 0, dataFormat, dataType, data); + + if (!lastMip && numMips < 2) + { + if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) + R_MipMapNormalHeight(data, data, width, height, glRefConfig.swizzleNormalmap); + else + R_MipMapsRGB(data, width, height); + } + } + + x >>= 1; + y >>= 1; + width = MAX(1, width >> 1); + height = MAX(1, height >> 1); + miplevel++; + + if (numMips > 1) + { + data += size; + numMips--; + } + } + while (!lastMip); } @@ -2023,19 +2029,35 @@ static void Upload32(byte *data, int x, int y, int width, int height, GLenum pic GLenum internalFormat = image->internalFormat; qboolean subtexture = (x != 0) || (y != 0) || (width != image->width) || (height != image->height); qboolean notScaled = qtrue; - qboolean mipmap = !!(flags & IMGFLAG_MIPMAP); + qboolean compressed = (picFormat != GL_RGBA8 && picFormat != GL_SRGB8_ALPHA8_EXT); + qboolean mipmap = !!(flags & IMGFLAG_MIPMAP) && (!compressed || numMips > 1); + qboolean cubemap = !!(flags & IMGFLAG_CUBEMAP); + GLenum uploadTarget = cubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : GL_TEXTURE_2D; + GLenum textureTarget = cubemap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + int depth = cubemap ? 6 : 1; if (!data) { RawImage_ScaleToPower2(NULL, &width, &height, type, flags, NULL); - RawImage_UploadTexture(image->texnum, NULL, 0, 0, width, height, GL_RGBA8, 0, internalFormat, type, flags, qfalse); + for (i = 0; i < depth; i++) + RawImage_UploadTexture(image->texnum, NULL, 0, 0, width, height, uploadTarget + i, GL_RGBA8, 0, internalFormat, type, flags, qfalse); goto done; } else if (!subtexture) { - if (picFormat != GL_RGBA8 && picFormat != GL_SRGB8_ALPHA8_EXT) + if (compressed || cubemap) { - RawImage_UploadTexture(image->texnum, data, 0, 0, width, height, picFormat, numMips, internalFormat, type, flags, qfalse); + for (i = 0; i < depth; i++) + { + int w2 = width, h2 = height; + RawImage_UploadTexture(image->texnum, data, 0, 0, width, height, uploadTarget + i, picFormat, numMips, internalFormat, type, flags, qfalse); + for (c = numMips; c; c--) + { + data += CalculateMipSize(w2, h2, picFormat); + w2 = MAX(1, w2 >> 1); + h2 = MAX(1, h2 >> 1); + } + } goto done; } notScaled = RawImage_ScaleToPower2(&data, &width, &height, type, flags, &resampledBuffer); @@ -2075,12 +2097,12 @@ static void Upload32(byte *data, int x, int y, int width, int height, GLenum pic if (subtexture) { // FIXME: Incorrect if original texture was not a power of 2 texture or picmipped - RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_RGBA8, 0, internalFormat, type, flags, qtrue); + RawImage_UploadTexture(image->texnum, data, x, y, width, height, uploadTarget, GL_RGBA8, 0, internalFormat, type, flags, qtrue); GL_CheckErrors(); return; } - RawImage_UploadTexture(image->texnum, data, 0, 0, width, height, GL_RGBA8, 0, internalFormat, type, flags, qfalse); + RawImage_UploadTexture(image->texnum, data, 0, 0, width, height, uploadTarget, GL_RGBA8, 0, internalFormat, type, flags, qfalse); done: @@ -2089,20 +2111,20 @@ done: if (mipmap) { - if ( textureFilterAnisotropic ) - qglTextureParameteri( image->texnum, GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, - (GLint)Com_Clamp( 1, maxAnisotropy, r_ext_max_anisotropy->integer ) ); + if (textureFilterAnisotropic && !cubemap) + qglTextureParameteri(image->texnum, textureTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, + (GLint)Com_Clamp(1, maxAnisotropy, r_ext_max_anisotropy->integer)); - qglTextureParameterf(image->texnum, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - qglTextureParameterf(image->texnum, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + qglTextureParameterf(image->texnum, textureTarget, GL_TEXTURE_MIN_FILTER, gl_filter_min); + qglTextureParameterf(image->texnum, textureTarget, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { - if ( textureFilterAnisotropic ) - qglTextureParameteri(image->texnum, GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1); + if (textureFilterAnisotropic && !cubemap) + qglTextureParameteri(image->texnum, textureTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1); - qglTextureParameterf(image->texnum, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - qglTextureParameterf(image->texnum, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + qglTextureParameterf(image->texnum, textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qglTextureParameterf(image->texnum, textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } // Fix for sampling depth buffer on old nVidia cards @@ -2113,9 +2135,9 @@ done: case GL_DEPTH_COMPONENT16_ARB: case GL_DEPTH_COMPONENT24_ARB: case GL_DEPTH_COMPONENT32_ARB: - qglTextureParameterf(image->texnum, GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE); - qglTextureParameterf(image->texnum, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - qglTextureParameterf(image->texnum, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + qglTextureParameterf(image->texnum, textureTarget, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE); + qglTextureParameterf(image->texnum, textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + qglTextureParameterf(image->texnum, textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); break; default: break; @@ -2169,49 +2191,20 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe glWrapClampMode = GL_REPEAT; if (!internalFormat) - { - if (image->flags & IMGFLAG_CUBEMAP) - internalFormat = GL_RGBA8; - else - internalFormat = RawImage_GetFormat(pic, width * height, picFormat, isLightmap, image->type, image->flags); - } + internalFormat = RawImage_GetFormat(pic, width * height, picFormat, isLightmap, image->type, image->flags); image->internalFormat = internalFormat; + Upload32(pic, 0, 0, image->width, image->height, picFormat, numMips, image); + if (image->flags & IMGFLAG_CUBEMAP) { - qglTextureParameteri(image->texnum, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - qglTextureParameteri(image->texnum, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - qglTextureParameteri(image->texnum, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - qglTextureParameteri(image->texnum, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - if (image->flags & IMGFLAG_MIPMAP) - { - qglTextureParameteri(image->texnum, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } - else - { - qglTextureParameteri(image->texnum, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - - qglTextureImage2D(image->texnum, GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTextureImage2D(image->texnum, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTextureImage2D(image->texnum, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTextureImage2D(image->texnum, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTextureImage2D(image->texnum, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTextureImage2D(image->texnum, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - - if (image->flags & IMGFLAG_MIPMAP) - qglGenerateTextureMipmap(image->texnum, GL_TEXTURE_CUBE_MAP); - - image->uploadWidth = width; - image->uploadHeight = height; + qglTextureParameterf(image->texnum, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, glWrapClampMode); + qglTextureParameterf(image->texnum, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, glWrapClampMode); + qglTextureParameteri(image->texnum, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, glWrapClampMode); } else { - Upload32( pic, 0, 0, image->width, image->height, picFormat, numMips, image ); - qglTextureParameterf(image->texnum, GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode); qglTextureParameterf(image->texnum, GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode); } @@ -2383,6 +2376,7 @@ image_t *R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags ) GLenum picFormat; int picNumMips; long hash; + imgFlags_t checkFlagsTrue, checkFlagsFalse; if (!name) { return NULL; @@ -2413,7 +2407,10 @@ image_t *R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags ) return NULL; } - if (r_normalMapping->integer && (picFormat == GL_RGBA8) && !(type == IMGTYPE_NORMAL) && (flags & IMGFLAG_PICMIP) && (flags & IMGFLAG_MIPMAP) && (flags & IMGFLAG_GENNORMALMAP)) + checkFlagsTrue = IMGFLAG_PICMIP | IMGFLAG_MIPMAP | IMGFLAG_GENNORMALMAP; + checkFlagsFalse = IMGFLAG_CUBEMAP; + if (r_normalMapping->integer && (picFormat == GL_RGBA8) && (type == IMGTYPE_COLORALPHA) && + ((flags & checkFlagsTrue) == checkFlagsTrue) && !(flags & checkFlagsFalse)) { char normalName[MAX_QPATH]; image_t *normalImage; @@ -2516,6 +2513,20 @@ image_t *R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags ) } } + // force mipmaps off if image is compressed but doesn't have enough mips + if ((flags & IMGFLAG_MIPMAP) && picFormat != GL_RGBA8 && picFormat != GL_SRGB8_ALPHA8_EXT) + { + int wh = MAX(width, height); + int neededMips = 0; + while (wh) + { + neededMips++; + wh >>= 1; + } + if (neededMips > picNumMips) + flags &= ~IMGFLAG_MIPMAP; + } + image = R_CreateImage2( ( char * ) name, pic, width, height, picFormat, picNumMips, type, flags, 0 ); ri.Free( pic ); return image;