OpenGL2: Load existing per-map cubemaps.

This commit is contained in:
SmileTheory 2016-01-28 16:39:42 -08:00
parent 558da25277
commit 2b2f1aeaa1
3 changed files with 208 additions and 175 deletions

View file

@ -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 );

View file

@ -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;

View file

@ -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;