[iortcw] 03/152: All: Rend2: Merge DDS support / r_cubemapsize / exportCubemaps command

Simon McVittie smcv at debian.org
Fri Sep 8 10:39:33 UTC 2017


This is an automated email from the git hooks/post-receive script.

smcv pushed a commit to annotated tag 1.5a
in repository iortcw.

commit 721dead5f5ff2930b3241b3e6fe9a334a0fd3a73
Author: MAN-AT-ARMS <M4N4T4RMS at gmail.com>
Date:   Sun Dec 27 08:48:09 2015 -0500

    All: Rend2: Merge DDS support / r_cubemapsize / exportCubemaps command
---
 MP/Makefile                  |   1 +
 MP/code/rend2/tr_backend.c   |  61 ++++++
 MP/code/rend2/tr_bsp.c       |   2 +-
 MP/code/rend2/tr_image.c     | 162 +++++++++++---
 MP/code/rend2/tr_image_dds.c | 498 +++++++++++++++++++++++++++++++++++++++++++
 MP/code/rend2/tr_init.c      |  31 +++
 MP/code/rend2/tr_local.h     |  10 +-
 SP/Makefile                  |   1 +
 SP/code/rend2/tr_backend.c   |  61 ++++++
 SP/code/rend2/tr_bsp.c       |   2 +-
 SP/code/rend2/tr_image.c     | 165 +++++++++++---
 SP/code/rend2/tr_image_dds.c | 498 +++++++++++++++++++++++++++++++++++++++++++
 SP/code/rend2/tr_init.c      |  31 +++
 SP/code/rend2/tr_local.h     |  10 +-
 14 files changed, 1461 insertions(+), 72 deletions(-)

diff --git a/MP/Makefile b/MP/Makefile
index 01b2ff6..9aa73ed 100644
--- a/MP/Makefile
+++ b/MP/Makefile
@@ -1747,6 +1747,7 @@ Q3R2OBJ = \
   $(B)/rend2/tr_image_pcx.o \
   $(B)/rend2/tr_image_png.o \
   $(B)/rend2/tr_image_tga.o \
+  $(B)/rend2/tr_image_dds.o \
   $(B)/rend2/tr_init.o \
   $(B)/rend2/tr_light.o \
   $(B)/rend2/tr_main.o \
diff --git a/MP/code/rend2/tr_backend.c b/MP/code/rend2/tr_backend.c
index 708a657..34550ae 100644
--- a/MP/code/rend2/tr_backend.c
+++ b/MP/code/rend2/tr_backend.c
@@ -2004,6 +2004,64 @@ const void *RB_PostProcess(const void *data)
 	return (const void *)(cmd + 1);
 }
 
+// FIXME: put this function declaration elsewhere
+void R_SaveDDS(const char *filename, byte *pic, int width, int height, int depth);
+
+/*
+=============
+RB_ExportCubemaps
+
+=============
+*/
+const void *RB_ExportCubemaps(const void *data)
+{
+	const exportCubemapsCommand_t *cmd = data;
+
+	// finish any 2D drawing if needed
+	if (tess.numIndexes)
+		RB_EndSurface();
+
+	if (!glRefConfig.framebufferObject || !tr.world || tr.numCubemaps == 0)
+	{
+		// do nothing
+		ri.Printf(PRINT_ALL, "Nothing to export!\n");
+		return (const void *)(cmd + 1);
+	}
+
+	if (cmd)
+	{
+		FBO_t *oldFbo = glState.currentFBO;
+		int sideSize = r_cubemapSize->integer * r_cubemapSize->integer * 4;
+		byte *cubemapPixels = ri.Z_Malloc(sideSize * 6);
+		int i, j;
+
+		FBO_Bind(tr.renderCubeFbo);
+
+		for (i = 0; i < tr.numCubemaps; i++)
+		{
+			char filename[MAX_QPATH];
+			cubemap_t *cubemap = &tr.cubemaps[i];
+			unsigned char *p = cubemapPixels;
+
+			for (j = 0; j < 6; j++)
+			{
+				qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, cubemap->image->texnum, 0);
+				qglReadPixels(0, 0, r_cubemapSize->integer, r_cubemapSize->integer, GL_RGBA, GL_UNSIGNED_BYTE, p);
+				p += sideSize;
+			}
+
+			Com_sprintf(filename, MAX_QPATH, "cubemaps/%s/%03d.dds", tr.world->baseName, i);
+			R_SaveDDS(filename, cubemapPixels, r_cubemapSize->integer, r_cubemapSize->integer, 6);
+			ri.Printf(PRINT_ALL, "Saved cubemap %d as %s\n", i, filename);
+		}
+
+		FBO_Bind(oldFbo);
+
+		ri.Free(cubemapPixels);
+	}
+
+	return (const void *)(cmd + 1);
+}
 
 /*
 ====================
@@ -2058,6 +2116,9 @@ void RB_ExecuteRenderCommands( const void *data ) {
 		case RC_POSTPROCESS:
 			data = RB_PostProcess(data);
 			break;
+		case RC_EXPORT_CUBEMAPS:
+			data = RB_ExportCubemaps(data);
+			break;
 		case RC_END_OF_LIST:
 		default:
 			// finish any 2D drawing if needed
diff --git a/MP/code/rend2/tr_bsp.c b/MP/code/rend2/tr_bsp.c
index 27f4242..be2170e 100644
--- a/MP/code/rend2/tr_bsp.c
+++ b/MP/code/rend2/tr_bsp.c
@@ -3425,7 +3425,7 @@ void R_RenderAllCubemaps(void)
 
 	for (i = 0; i < tr.numCubemaps; i++)
 	{
-		tr.cubemaps[i].image = R_CreateImage(va("*cubeMap%d", i), NULL, CUBE_MAP_SIZE, CUBE_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_CUBEMAP, GL_RGBA8);
+		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);
 	}
 	
 	ri.Printf(PRINT_ALL, "Total cubemaps: %d\n", tr.numCubemaps );
diff --git a/MP/code/rend2/tr_image.c b/MP/code/rend2/tr_image.c
index 6b2d124..0b01a25 100644
--- a/MP/code/rend2/tr_image.c
+++ b/MP/code/rend2/tr_image.c
@@ -1777,13 +1777,16 @@ static qboolean RawImage_HasAlpha(const byte *scan, int numPixels)
 	return qfalse;
 }
 
-static GLenum RawImage_GetFormat(const byte *data, int numPixels, qboolean lightMap, imgType_t type, imgFlags_t flags)
+static GLenum RawImage_GetFormat(const byte *data, int numPixels, GLenum picFormat, qboolean lightMap, imgType_t type, imgFlags_t flags)
 {
 	int samples = 3;
 	GLenum internalFormat = GL_RGB;
 	qboolean forceNoCompression = (flags & IMGFLAG_NO_COMPRESSION);
 	qboolean normalmap = (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT);
 
+	if (picFormat != GL_RGBA8)
+		return picFormat;
+
 	if(normalmap)
 	{
 		if ((type == IMGTYPE_NORMALHEIGHT) && RawImage_HasAlpha(data, numPixels))
@@ -2021,11 +2024,80 @@ static void RawImage_UploadToRgtc2Texture(byte *data, int width, int height, int
 	ri.Hunk_FreeTempMemory(compressedData);
 }
 
-static void RawImage_UploadTexture( byte *data, int x, int y, int width, int height, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture )
+static void RawImage_UploadTexture( byte *data, int x, int y, int width, int height, GLenum picFormat, int numMips, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture )
 {
 	int dataFormat, dataType;
 	qboolean rgtc = (internalFormat == GL_COMPRESSED_RG_RGTC2);
 
+	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)
+				qglCompressedTexSubImage2DARB(GL_TEXTURE_2D, miplevel, x, y, width, height, internalFormat, size, data);
+			else
+				qglCompressedTexImage2DARB(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)
 	{
 		case GL_DEPTH_COMPONENT:
@@ -2057,29 +2129,20 @@ static void RawImage_UploadTexture( byte *data, int x, int y, int width, int hei
 
 	if (flags & IMGFLAG_MIPMAP)
 	{
-		int miplevel;
+		int miplevel = 0;
 
-		miplevel = 0;
 		while (width > 1 || height > 1)
 		{
 			if (data)
 			{
 				if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT)
-				{
 					R_MipMapNormalHeight( data, data, width, height, glRefConfig.swizzleNormalmap );
-				}
 				else
-				{
 					R_MipMapsRGB( data, width, height );
-				}
 			}
 			
-			width >>= 1;
-			height >>= 1;
-			if (width < 1)
-				width = 1;
-			if (height < 1)
-				height = 1;
+			width = MAX(1, width >> 1);
+			height = MAX(1, height >> 1);
 			miplevel++;
 
 			if ( data && r_colorMipLevels->integer )
@@ -2108,7 +2171,7 @@ Upload32
 
 ===============
 */
-static void Upload32(byte *data, int x, int y, int width, int height, image_t *image)
+static void Upload32(byte *data, int x, int y, int width, int height, GLenum picFormat, int numMips, image_t *image)
 {
 	byte		*resampledBuffer = NULL;
 	int			i, c;
@@ -2138,11 +2201,16 @@ static void Upload32(byte *data, int x, int y, int width, int height, image_t *i
 	if (!data)
 	{
 		RawImage_ScaleToPower2(NULL, &width, &height, type, flags, NULL);
-		RawImage_UploadTexture(NULL, 0, 0, width, height, internalFormat, type, flags, qfalse);
+		RawImage_UploadTexture(NULL, 0, 0, width, height, GL_RGBA8, 0, internalFormat, type, flags, qfalse);
 		goto done;
 	}
 	else if (!subtexture)
 	{
+		if (picFormat != GL_RGBA8 && picFormat != GL_SRGB8_ALPHA8_EXT)
+		{
+			RawImage_UploadTexture(data, 0, 0, width, height, picFormat, numMips, internalFormat, type, flags, qfalse);
+			goto done;
+		}
 		notScaled = RawImage_ScaleToPower2(&data, &width, &height, type, flags, &resampledBuffer);
 	}
 
@@ -2180,12 +2248,12 @@ static void Upload32(byte *data, int x, int y, int width, int height, image_t *i
 	if (subtexture)
 	{
 		// FIXME: Incorrect if original texture was not a power of 2 texture or picmipped
-		RawImage_UploadTexture(data, x, y, width, height, internalFormat, type, flags, qtrue);
+		RawImage_UploadTexture(data, x, y, width, height, GL_RGBA8, 0, internalFormat, type, flags, qtrue);
 		GL_CheckErrors();
 		return;
 	}
 
-	RawImage_UploadTexture(data, 0, 0, width, height, internalFormat, type, flags, qfalse);
+	RawImage_UploadTexture(data, 0, 0, width, height, GL_RGBA8, 0, internalFormat, type, flags, qfalse);
 
 done:
 
@@ -2234,12 +2302,12 @@ done:
 
 /*
 ================
-R_CreateImage
+R_CreateImage2
 
 This is the only way any image_t are created
 ================
 */
-image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgType_t type, imgFlags_t flags, int internalFormat ) {
+image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLenum picFormat, int numMips, imgType_t type, imgFlags_t flags, int internalFormat ) {
 	image_t     *image;
 	qboolean isLightmap = qfalse;
 	long hash;
@@ -2277,7 +2345,7 @@ image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgT
 		if (image->flags & IMGFLAG_CUBEMAP)
 			internalFormat = GL_RGBA8;
 		else
-			internalFormat = RawImage_GetFormat(pic, width * height, isLightmap, image->type, image->flags);
+			internalFormat = RawImage_GetFormat(pic, width * height, picFormat, isLightmap, image->type, image->flags);
 	}
 
 	image->internalFormat = internalFormat;
@@ -2327,7 +2395,7 @@ image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgT
 	}
 	else
 	{
-		Upload32( pic, 0, 0, image->width, image->height, image );
+		Upload32( pic, 0, 0, image->width, image->height, picFormat, numMips, image );
 
 		qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode );
 		qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode );
@@ -2345,6 +2413,18 @@ image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgT
 	return image;
 }
 
+/*
+================
+R_CreateImage
+
+Wrapper for R_CreateImage2(), for the old parameters.
+================
+*/
+image_t *R_CreateImage(const char *name, byte *pic, int width, int height, imgType_t type, imgFlags_t flags, int internalFormat)
+{
+	return R_CreateImage2(name, pic, width, height, GL_RGBA8, 0, type, flags, internalFormat);
+}
+
 void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int height )
 {
 	if (qglActiveTextureARB) {
@@ -2353,13 +2433,16 @@ void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int h
 
 	GL_Bind(image);
 
-	Upload32(pic, x, y, width, height, image);
+	Upload32(pic, x, y, width, height, GL_RGBA8, 0, image);
 
 	GL_SelectTexture(0);
 }
 
 //===================================================================
 
+// Prototype for dds loader function which isn't common to both renderers
+void R_LoadDDS(const char *filename, byte **pic, int *width, int *height, GLenum *picFormat, int *numMips);
+
 typedef struct
 {
 	char *ext;
@@ -2389,7 +2472,7 @@ Loads any of the supported image types into a cannonical
 32 bit format.
 =================
 */
-void R_LoadImage( const char *name, byte **pic, int *width, int *height )
+void R_LoadImage( const char *name, byte **pic, int *width, int *height, GLenum *picFormat, int *numMips )
 {
 	qboolean orgNameFailed = qfalse;
 	int orgLoader = -1;
@@ -2401,11 +2484,28 @@ void R_LoadImage( const char *name, byte **pic, int *width, int *height )
 	*pic = NULL;
 	*width = 0;
 	*height = 0;
+	*picFormat = GL_RGBA8;
+	*numMips = 0;
 
 	Q_strncpyz( localName, name, MAX_QPATH );
 
 	ext = COM_GetExtension( localName );
 
+	// If compressed textures are enabled, try loading a DDS first, it'll load fastest
+	if (r_ext_compressed_textures->integer)
+	{
+		char ddsName[MAX_QPATH];
+
+		COM_StripExtension(name, ddsName, MAX_QPATH);
+		Q_strcat(ddsName, MAX_QPATH, ".dds");
+
+		R_LoadDDS(ddsName, pic, width, height, picFormat, numMips);
+
+		// If loaded, we're done.
+		if (*pic)
+			return;
+	}
+
 	if( *ext )
 	{
 		// Look for the correct loader and use it
@@ -2476,6 +2576,8 @@ image_t	*R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags )
 	image_t *image;
 	int width, height;
 	byte    *pic;
+	GLenum  picFormat;
+	int picNumMips;
 	long hash;
 
 	if ( !name ) {
@@ -2502,12 +2604,12 @@ image_t	*R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags )
 	//
 	// load the pic from disk
 	//
-	R_LoadImage( name, &pic, &width, &height );
+	R_LoadImage( name, &pic, &width, &height, &picFormat, &picNumMips );
 	if ( pic == NULL ) {
 		return NULL;
 	}
 
-	if (r_normalMapping->integer && !(type == IMGTYPE_NORMAL) && (flags & IMGFLAG_PICMIP) && (flags & IMGFLAG_MIPMAP) && (flags & IMGFLAG_GENNORMALMAP))
+	if (r_normalMapping->integer && (picFormat == GL_RGBA8) && !(type == IMGTYPE_NORMAL) && (flags & IMGFLAG_PICMIP) && (flags & IMGFLAG_MIPMAP) && (flags & IMGFLAG_GENNORMALMAP))
 	{
 		char normalName[MAX_QPATH];
 		image_t *normalImage;
@@ -2610,7 +2712,7 @@ image_t	*R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags )
 		}
 	}
 
-	image = R_CreateImage( ( char * ) name, pic, width, height, type, flags, 0 );
+	image = R_CreateImage2( ( char * ) name, pic, width, height, picFormat, picNumMips, type, flags, 0 );
 	ri.Free( pic );
 	return image;
 }
@@ -2912,7 +3014,7 @@ void R_CreateBuiltinImages( void ) {
 
 		if (r_cubeMapping->integer)
 		{
-			tr.renderCubeImage = R_CreateImage("*renderCube", NULL, CUBE_MAP_SIZE, CUBE_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_CUBEMAP, rgbFormat);
+			tr.renderCubeImage = R_CreateImage("*renderCube", NULL, r_cubemapSize->integer, r_cubemapSize->integer, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_CUBEMAP, rgbFormat);
 		}
 	}
 }
@@ -3881,6 +3983,8 @@ void    R_CropAndNumberImagesInDirectory( char *dir, char *ext, int maxWidth, in
 #endif
 	byte    *pic, *temppic;
 	int width, height, newWidth, newHeight;
+	GLenum  picFormat;
+	int picNumMips;
 	char    *pch;
 	int b,c,d,lastNumber;
 	int lastBox[2] = {0,0};
@@ -3904,7 +4008,7 @@ void    R_CropAndNumberImagesInDirectory( char *dir, char *ext, int maxWidth, in
 		Com_sprintf( filename, sizeof( filename ), "%s/%s", dir, fileList[j] );
 		ri.Printf( PRINT_ALL, "...cropping '%s'.. ", filename );
 
-		R_LoadImage( filename, &pic, &width, &height );
+		R_LoadImage( filename, &pic, &width, &height, &picFormat, &picNumMips );
 		if ( !pic ) {
 			ri.Printf( PRINT_ALL, "error reading file, ignoring.\n" );
 			continue;
diff --git a/MP/code/rend2/tr_image_dds.c b/MP/code/rend2/tr_image_dds.c
new file mode 100644
index 0000000..26318be
--- /dev/null
+++ b/MP/code/rend2/tr_image_dds.c
@@ -0,0 +1,498 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+              2015 James Canete
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "tr_local.h"
+
+typedef unsigned int   ui32_t;
+
+typedef struct ddsHeader_s
+{
+	ui32_t headerSize;
+	ui32_t flags;
+	ui32_t height;
+	ui32_t width;
+	ui32_t pitchOrFirstMipSize;
+	ui32_t volumeDepth;
+	ui32_t numMips;
+	ui32_t reserved1[11];
+	ui32_t always_0x00000020;
+	ui32_t pixelFormatFlags;
+	ui32_t fourCC;
+	ui32_t rgbBitCount;
+	ui32_t rBitMask;
+	ui32_t gBitMask;
+	ui32_t bBitMask;
+	ui32_t aBitMask;
+	ui32_t caps;
+	ui32_t caps2;
+	ui32_t caps3;
+	ui32_t caps4;
+	ui32_t reserved2;
+}
+ddsHeader_t;
+
+// flags:
+#define _DDSFLAGS_REQUIRED     0x001007
+#define _DDSFLAGS_PITCH        0x8
+#define _DDSFLAGS_MIPMAPCOUNT  0x20000
+#define _DDSFLAGS_FIRSTMIPSIZE 0x80000
+#define _DDSFLAGS_VOLUMEDEPTH  0x800000
+
+// pixelFormatFlags:
+#define DDSPF_ALPHAPIXELS 0x1
+#define DDSPF_ALPHA       0x2
+#define DDSPF_FOURCC      0x4
+#define DDSPF_RGB         0x40
+#define DDSPF_YUV         0x200
+#define DDSPF_LUMINANCE   0x20000
+
+// caps:
+#define DDSCAPS_COMPLEX  0x8
+#define DDSCAPS_MIPMAP   0x400000
+#define DDSCAPS_REQUIRED 0x1000
+
+// caps2:
+#define DDSCAPS2_CUBEMAP 0xFE00
+#define DDSCAPS2_VOLUME  0x200000
+
+typedef struct ddsHeaderDxt10_s
+{
+	ui32_t dxgiFormat;
+	ui32_t dimensions;
+	ui32_t miscFlags;
+	ui32_t arraySize;
+	ui32_t miscFlags2;
+}
+ddsHeaderDxt10_t;
+
+// dxgiFormat
+// from http://msdn.microsoft.com/en-us/library/windows/desktop/bb173059%28v=vs.85%29.aspx
+typedef enum DXGI_FORMAT {
+	DXGI_FORMAT_UNKNOWN = 0,
+	DXGI_FORMAT_R32G32B32A32_TYPELESS = 1,
+	DXGI_FORMAT_R32G32B32A32_FLOAT = 2,
+	DXGI_FORMAT_R32G32B32A32_UINT = 3,
+	DXGI_FORMAT_R32G32B32A32_SINT = 4,
+	DXGI_FORMAT_R32G32B32_TYPELESS = 5,
+	DXGI_FORMAT_R32G32B32_FLOAT = 6,
+	DXGI_FORMAT_R32G32B32_UINT = 7,
+	DXGI_FORMAT_R32G32B32_SINT = 8,
+	DXGI_FORMAT_R16G16B16A16_TYPELESS = 9,
+	DXGI_FORMAT_R16G16B16A16_FLOAT = 10,
+	DXGI_FORMAT_R16G16B16A16_UNORM = 11,
+	DXGI_FORMAT_R16G16B16A16_UINT = 12,
+	DXGI_FORMAT_R16G16B16A16_SNORM = 13,
+	DXGI_FORMAT_R16G16B16A16_SINT = 14,
+	DXGI_FORMAT_R32G32_TYPELESS = 15,
+	DXGI_FORMAT_R32G32_FLOAT = 16,
+	DXGI_FORMAT_R32G32_UINT = 17,
+	DXGI_FORMAT_R32G32_SINT = 18,
+	DXGI_FORMAT_R32G8X24_TYPELESS = 19,
+	DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20,
+	DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21,
+	DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22,
+	DXGI_FORMAT_R10G10B10A2_TYPELESS = 23,
+	DXGI_FORMAT_R10G10B10A2_UNORM = 24,
+	DXGI_FORMAT_R10G10B10A2_UINT = 25,
+	DXGI_FORMAT_R11G11B10_FLOAT = 26,
+	DXGI_FORMAT_R8G8B8A8_TYPELESS = 27,
+	DXGI_FORMAT_R8G8B8A8_UNORM = 28,
+	DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29,
+	DXGI_FORMAT_R8G8B8A8_UINT = 30,
+	DXGI_FORMAT_R8G8B8A8_SNORM = 31,
+	DXGI_FORMAT_R8G8B8A8_SINT = 32,
+	DXGI_FORMAT_R16G16_TYPELESS = 33,
+	DXGI_FORMAT_R16G16_FLOAT = 34,
+	DXGI_FORMAT_R16G16_UNORM = 35,
+	DXGI_FORMAT_R16G16_UINT = 36,
+	DXGI_FORMAT_R16G16_SNORM = 37,
+	DXGI_FORMAT_R16G16_SINT = 38,
+	DXGI_FORMAT_R32_TYPELESS = 39,
+	DXGI_FORMAT_D32_FLOAT = 40,
+	DXGI_FORMAT_R32_FLOAT = 41,
+	DXGI_FORMAT_R32_UINT = 42,
+	DXGI_FORMAT_R32_SINT = 43,
+	DXGI_FORMAT_R24G8_TYPELESS = 44,
+	DXGI_FORMAT_D24_UNORM_S8_UINT = 45,
+	DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46,
+	DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47,
+	DXGI_FORMAT_R8G8_TYPELESS = 48,
+	DXGI_FORMAT_R8G8_UNORM = 49,
+	DXGI_FORMAT_R8G8_UINT = 50,
+	DXGI_FORMAT_R8G8_SNORM = 51,
+	DXGI_FORMAT_R8G8_SINT = 52,
+	DXGI_FORMAT_R16_TYPELESS = 53,
+	DXGI_FORMAT_R16_FLOAT = 54,
+	DXGI_FORMAT_D16_UNORM = 55,
+	DXGI_FORMAT_R16_UNORM = 56,
+	DXGI_FORMAT_R16_UINT = 57,
+	DXGI_FORMAT_R16_SNORM = 58,
+	DXGI_FORMAT_R16_SINT = 59,
+	DXGI_FORMAT_R8_TYPELESS = 60,
+	DXGI_FORMAT_R8_UNORM = 61,
+	DXGI_FORMAT_R8_UINT = 62,
+	DXGI_FORMAT_R8_SNORM = 63,
+	DXGI_FORMAT_R8_SINT = 64,
+	DXGI_FORMAT_A8_UNORM = 65,
+	DXGI_FORMAT_R1_UNORM = 66,
+	DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67,
+	DXGI_FORMAT_R8G8_B8G8_UNORM = 68,
+	DXGI_FORMAT_G8R8_G8B8_UNORM = 69,
+	DXGI_FORMAT_BC1_TYPELESS = 70,
+	DXGI_FORMAT_BC1_UNORM = 71,
+	DXGI_FORMAT_BC1_UNORM_SRGB = 72,
+	DXGI_FORMAT_BC2_TYPELESS = 73,
+	DXGI_FORMAT_BC2_UNORM = 74,
+	DXGI_FORMAT_BC2_UNORM_SRGB = 75,
+	DXGI_FORMAT_BC3_TYPELESS = 76,
+	DXGI_FORMAT_BC3_UNORM = 77,
+	DXGI_FORMAT_BC3_UNORM_SRGB = 78,
+	DXGI_FORMAT_BC4_TYPELESS = 79,
+	DXGI_FORMAT_BC4_UNORM = 80,
+	DXGI_FORMAT_BC4_SNORM = 81,
+	DXGI_FORMAT_BC5_TYPELESS = 82,
+	DXGI_FORMAT_BC5_UNORM = 83,
+	DXGI_FORMAT_BC5_SNORM = 84,
+	DXGI_FORMAT_B5G6R5_UNORM = 85,
+	DXGI_FORMAT_B5G5R5A1_UNORM = 86,
+	DXGI_FORMAT_B8G8R8A8_UNORM = 87,
+	DXGI_FORMAT_B8G8R8X8_UNORM = 88,
+	DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89,
+	DXGI_FORMAT_B8G8R8A8_TYPELESS = 90,
+	DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91,
+	DXGI_FORMAT_B8G8R8X8_TYPELESS = 92,
+	DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93,
+	DXGI_FORMAT_BC6H_TYPELESS = 94,
+	DXGI_FORMAT_BC6H_UF16 = 95,
+	DXGI_FORMAT_BC6H_SF16 = 96,
+	DXGI_FORMAT_BC7_TYPELESS = 97,
+	DXGI_FORMAT_BC7_UNORM = 98,
+	DXGI_FORMAT_BC7_UNORM_SRGB = 99,
+	DXGI_FORMAT_AYUV = 100,
+	DXGI_FORMAT_Y410 = 101,
+	DXGI_FORMAT_Y416 = 102,
+	DXGI_FORMAT_NV12 = 103,
+	DXGI_FORMAT_P010 = 104,
+	DXGI_FORMAT_P016 = 105,
+	DXGI_FORMAT_420_OPAQUE = 106,
+	DXGI_FORMAT_YUY2 = 107,
+	DXGI_FORMAT_Y210 = 108,
+	DXGI_FORMAT_Y216 = 109,
+	DXGI_FORMAT_NV11 = 110,
+	DXGI_FORMAT_AI44 = 111,
+	DXGI_FORMAT_IA44 = 112,
+	DXGI_FORMAT_P8 = 113,
+	DXGI_FORMAT_A8P8 = 114,
+	DXGI_FORMAT_B4G4R4A4_UNORM = 115,
+	DXGI_FORMAT_FORCE_UINT = 0xffffffffUL
+} DXGI_FORMAT;
+
+#define EncodeFourCC(x) ((((ui32_t)((x)[0]))      ) | \
+                         (((ui32_t)((x)[1])) << 8 ) | \
+                         (((ui32_t)((x)[2])) << 16) | \
+                         (((ui32_t)((x)[3])) << 24) )
+
+
+void R_LoadDDS ( const char *filename, byte **pic, int *width, int *height, GLenum *picFormat, int *numMips )
+{
+	union {
+		byte *b;
+		void *v;
+	} buffer;
+	int len;
+	ddsHeader_t *ddsHeader = NULL;
+	ddsHeaderDxt10_t *ddsHeaderDxt10 = NULL;
+	byte *data;
+
+	if (!picFormat)
+	{
+		ri.Printf(PRINT_ERROR, "R_LoadDDS() called without picFormat parameter!");
+		return;
+	}
+
+	if (width)
+		*width = 0;
+	if (height)
+		*height = 0;
+	if (picFormat)
+		*picFormat = GL_RGBA8;
+	if (numMips)
+		*numMips = 1;
+
+	*pic = NULL;
+
+	//
+	// load the file
+	//
+	len = ri.FS_ReadFile( ( char * ) filename, &buffer.v);
+	if (!buffer.b || len < 0) {
+		return;
+	}
+
+	//
+	// reject files that are too small to hold even a header
+	//
+	if (len < 4 + sizeof(*ddsHeader))
+	{
+		ri.Printf(PRINT_ALL, "File %s is too small to be a DDS file.\n", filename);
+		ri.FS_FreeFile(buffer.v);
+		return;
+	}
+
+	//
+	// reject files that don't start with "DDS "
+	//
+	if (*((ui32_t *)(buffer.b)) != EncodeFourCC("DDS "))
+	{
+		ri.Printf(PRINT_ALL, "File %s is not a DDS file.\n", filename);
+		ri.FS_FreeFile(buffer.v);
+		return;
+	}
+
+	//
+	// parse header and dx10 header if available
+	//
+	ddsHeader = (ddsHeader_t *)(buffer.b + 4);
+	if ((ddsHeader->pixelFormatFlags & DDSPF_FOURCC) && ddsHeader->fourCC == EncodeFourCC("DX10"))
+	{
+		if (len < 4 + sizeof(*ddsHeader) + sizeof(*ddsHeaderDxt10))
+		{
+			ri.Printf(PRINT_ALL, "File %s indicates a DX10 header it is too small to contain.\n", filename);
+			ri.FS_FreeFile(buffer.v);
+			return;
+		}
+
+		ddsHeaderDxt10 = (ddsHeaderDxt10_t *)(buffer.b + 4 + sizeof(ddsHeader_t));
+		data = buffer.b + 4 + sizeof(*ddsHeader) + sizeof(*ddsHeaderDxt10);
+		len -= 4 + sizeof(*ddsHeader) + sizeof(*ddsHeaderDxt10);
+	}
+	else
+	{
+		data = buffer.b + 4 + sizeof(*ddsHeader);
+		len -= 4 + sizeof(*ddsHeader);
+	}
+
+	if (width)
+		*width = ddsHeader->width;
+	if (height)
+		*height = ddsHeader->height;
+
+	if (numMips)
+	{
+		if (ddsHeader->flags & _DDSFLAGS_MIPMAPCOUNT)
+			*numMips = ddsHeader->numMips;
+		else
+			*numMips = 1;
+	}
+
+	// FIXME: handle cube map
+	//if ((ddsHeader->caps2 & DDSCAPS2_CUBEMAP) == DDSCAPS2_CUBEMAP)
+
+	//
+	// Convert DXGI format/FourCC into OpenGL format
+	//
+	if (ddsHeaderDxt10)
+	{
+		switch (ddsHeaderDxt10->dxgiFormat)
+		{
+			case DXGI_FORMAT_BC1_TYPELESS:
+			case DXGI_FORMAT_BC1_UNORM:
+				// FIXME: check for GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
+				*picFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
+				break;
+
+			case DXGI_FORMAT_BC1_UNORM_SRGB:
+				// FIXME: check for GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT
+				*picFormat = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
+				break;
+
+			case DXGI_FORMAT_BC2_TYPELESS:
+			case DXGI_FORMAT_BC2_UNORM:
+				*picFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+				break;
+
+			case DXGI_FORMAT_BC2_UNORM_SRGB:
+				*picFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
+				break;
+
+			case DXGI_FORMAT_BC3_TYPELESS:
+			case DXGI_FORMAT_BC3_UNORM:
+				*picFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+				break;
+
+			case DXGI_FORMAT_BC3_UNORM_SRGB:
+				*picFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
+				break;
+
+			case DXGI_FORMAT_BC4_TYPELESS:
+			case DXGI_FORMAT_BC4_UNORM:
+				*picFormat = GL_COMPRESSED_RED_RGTC1;
+				break;
+
+			case DXGI_FORMAT_BC4_SNORM:
+				*picFormat = GL_COMPRESSED_SIGNED_RED_RGTC1;
+				break;
+
+			case DXGI_FORMAT_BC5_TYPELESS:
+			case DXGI_FORMAT_BC5_UNORM:
+				*picFormat = GL_COMPRESSED_RG_RGTC2;
+				break;
+
+			case DXGI_FORMAT_BC5_SNORM:
+				*picFormat = GL_COMPRESSED_SIGNED_RG_RGTC2;
+				break;
+
+			case DXGI_FORMAT_BC6H_TYPELESS:
+			case DXGI_FORMAT_BC6H_UF16:
+				*picFormat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB;
+				break;
+
+			case DXGI_FORMAT_BC6H_SF16:
+				*picFormat = GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB;
+				break;
+
+			case DXGI_FORMAT_BC7_TYPELESS:
+			case DXGI_FORMAT_BC7_UNORM:
+				*picFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
+				break;
+
+			case DXGI_FORMAT_BC7_UNORM_SRGB:
+				*picFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB;
+				break;
+
+			case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
+				*picFormat = GL_SRGB8_ALPHA8_EXT;
+				break;
+
+			case DXGI_FORMAT_R8G8B8A8_UNORM:
+			case DXGI_FORMAT_R8G8B8A8_SNORM:
+				*picFormat = GL_RGBA8;
+				break;
+
+			default:
+				ri.Printf(PRINT_ALL, "DDS File %s has unsupported DXGI format %d.", filename, ddsHeaderDxt10->dxgiFormat);
+				ri.FS_FreeFile(buffer.v);
+				return;
+				break;
+		}
+	}
+	else
+	{
+		if (ddsHeader->pixelFormatFlags & DDSPF_FOURCC)
+		{
+			if (ddsHeader->fourCC == EncodeFourCC("DXT1"))
+				*picFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
+			else if (ddsHeader->fourCC == EncodeFourCC("DXT2"))
+				*picFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+			else if (ddsHeader->fourCC == EncodeFourCC("DXT3"))
+				*picFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+			else if (ddsHeader->fourCC == EncodeFourCC("DXT4"))
+				*picFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+			else if (ddsHeader->fourCC == EncodeFourCC("DXT5"))
+				*picFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+			else if (ddsHeader->fourCC == EncodeFourCC("ATI1"))
+				*picFormat = GL_COMPRESSED_RED_RGTC1;
+			else if (ddsHeader->fourCC == EncodeFourCC("BC4U"))
+				*picFormat = GL_COMPRESSED_RED_RGTC1;
+			else if (ddsHeader->fourCC == EncodeFourCC("BC4S"))
+				*picFormat = GL_COMPRESSED_SIGNED_RED_RGTC1;
+			else if (ddsHeader->fourCC == EncodeFourCC("ATI2"))
+				*picFormat = GL_COMPRESSED_RG_RGTC2;
+			else if (ddsHeader->fourCC == EncodeFourCC("BC5U"))
+				*picFormat = GL_COMPRESSED_RG_RGTC2;
+			else if (ddsHeader->fourCC == EncodeFourCC("BC5S"))
+				*picFormat = GL_COMPRESSED_SIGNED_RG_RGTC2;
+			else
+			{
+				ri.Printf(PRINT_ALL, "DDS File %s has unsupported FourCC.", filename);
+				ri.FS_FreeFile(buffer.v);
+				return;
+			}
+		}
+		else if (ddsHeader->pixelFormatFlags == (DDSPF_RGB | DDSPF_ALPHAPIXELS)
+			&& ddsHeader->rgbBitCount == 32
+			&& ddsHeader->rBitMask == 0x000000ff
+			&& ddsHeader->gBitMask == 0x0000ff00
+			&& ddsHeader->bBitMask == 0x00ff0000
+			&& ddsHeader->aBitMask == 0xff000000)
+		{
+			*picFormat = GL_RGBA8;
+		}
+		else
+		{
+			ri.Printf(PRINT_ALL, "DDS File %s has unsupported RGBA format.", filename);
+			ri.FS_FreeFile(buffer.v);
+			return;
+		}
+	}
+
+	*pic = ri.Z_Malloc(len);
+	Com_Memcpy(*pic, data, len);
+
+	ri.FS_FreeFile(buffer.v);
+}
+
+void R_SaveDDS(const char *filename, byte *pic, int width, int height, int depth)
+{
+	byte *data;
+	ddsHeader_t *ddsHeader;
+	int picSize, size;
+
+	if (!depth)
+		depth = 1;
+
+	picSize = width * height * depth * 4;
+	size = 4 + sizeof(*ddsHeader) + picSize;
+	data = ri.Z_Malloc(size);
+
+	data[0] = 'D';
+	data[1] = 'D';
+	data[2] = 'S';
+	data[3] = ' ';
+
+	ddsHeader = (ddsHeader_t *)(data + 4);
+	memset(ddsHeader, 0, sizeof(ddsHeader_t));
+
+	ddsHeader->headerSize = 0x7c;
+	ddsHeader->flags = _DDSFLAGS_REQUIRED;
+	ddsHeader->height = height;
+	ddsHeader->width = width;
+	ddsHeader->always_0x00000020 = 0x00000020;
+	ddsHeader->caps = DDSCAPS_COMPLEX | DDSCAPS_REQUIRED;
+
+	if (depth == 6)
+		ddsHeader->caps2 = DDSCAPS2_CUBEMAP;
+
+	ddsHeader->pixelFormatFlags = DDSPF_RGB | DDSPF_ALPHAPIXELS;
+	ddsHeader->rgbBitCount = 32;
+	ddsHeader->rBitMask = 0x000000ff;
+	ddsHeader->gBitMask = 0x0000ff00;
+	ddsHeader->bBitMask = 0x00ff0000;
+	ddsHeader->aBitMask = 0xff000000;
+
+	Com_Memcpy(data + 4 + sizeof(*ddsHeader), pic, picSize);
+
+	ri.FS_WriteFile(filename, data, size);
+
+	ri.Free(data);
+}
diff --git a/MP/code/rend2/tr_init.c b/MP/code/rend2/tr_init.c
index 0bae878..56a1adb 100644
--- a/MP/code/rend2/tr_init.c
+++ b/MP/code/rend2/tr_init.c
@@ -161,6 +161,7 @@ cvar_t  *r_specularMapping;
 cvar_t  *r_deluxeMapping;
 cvar_t  *r_parallaxMapping;
 cvar_t  *r_cubeMapping;
+cvar_t  *r_cubemapSize;
 cvar_t  *r_specularIsMetallic;
 cvar_t  *r_glossIsRoughness;
 cvar_t  *r_baseNormalX;
@@ -903,6 +904,33 @@ void R_ScreenShotJPEG_f (void) {
 	}
 } 
 
+/*
+==================
+R_ExportCubemaps
+==================
+*/
+void R_ExportCubemaps(void)
+{
+	exportCubemapsCommand_t	*cmd;
+
+	cmd = R_GetCommandBuffer(sizeof(*cmd));
+	if (!cmd) {
+		return;
+	}
+	cmd->commandId = RC_EXPORT_CUBEMAPS;
+}
+
+
+/*
+==================
+R_ExportCubemaps_f
+==================
+*/
+void R_ExportCubemaps_f(void)
+{
+	R_ExportCubemaps();
+}
+
 //============================================================================
 
 /*
@@ -1327,6 +1355,7 @@ void R_Register( void ) {
 	r_deluxeMapping = ri.Cvar_Get( "r_deluxeMapping", "1", CVAR_ARCHIVE | CVAR_LATCH );
 	r_parallaxMapping = ri.Cvar_Get( "r_parallaxMapping", "0", CVAR_ARCHIVE | CVAR_LATCH );
 	r_cubeMapping = ri.Cvar_Get( "r_cubeMapping", "0", CVAR_ARCHIVE | CVAR_LATCH );
+	r_cubemapSize = ri.Cvar_Get( "r_cubemapSize", "128", CVAR_ARCHIVE | CVAR_LATCH );
 	r_specularIsMetallic = ri.Cvar_Get( "r_specularIsMetallic", "0", CVAR_ARCHIVE | CVAR_LATCH );
 	r_glossIsRoughness = ri.Cvar_Get("r_glossIsRoughness", "0", CVAR_ARCHIVE | CVAR_LATCH);
 	r_baseNormalX = ri.Cvar_Get( "r_baseNormalX", "1.0", CVAR_ARCHIVE | CVAR_LATCH );
@@ -1487,6 +1516,7 @@ void R_Register( void ) {
 	ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f );
 	ri.Cmd_AddCommand( "minimize", GLimp_Minimize );
 	ri.Cmd_AddCommand( "gfxmeminfo", GfxMemInfo_f );
+	ri.Cmd_AddCommand( "exportCubemaps", R_ExportCubemaps_f );
 	ri.Cmd_AddCommand( "taginfo", R_TagInfo_f );
 
 	// Ridah
@@ -1640,6 +1670,7 @@ void RE_Shutdown( qboolean destroyWindow ) {
 	ri.Cmd_RemoveCommand( "modelist" );
 	ri.Cmd_RemoveCommand( "shaderstate" );
 	ri.Cmd_RemoveCommand( "gfxmeminfo" );
+	ri.Cmd_RemoveCommand( "exportCubemaps" );
 	ri.Cmd_RemoveCommand( "taginfo" );
 
 	// Ridah
diff --git a/MP/code/rend2/tr_local.h b/MP/code/rend2/tr_local.h
index 7411e11..da99449 100644
--- a/MP/code/rend2/tr_local.h
+++ b/MP/code/rend2/tr_local.h
@@ -61,8 +61,6 @@ typedef unsigned int glIndex_t;
 #define MAX_CALC_PSHADOWS    64
 #define MAX_DRAWN_PSHADOWS    32 // do not increase past 32, because bit flags are used on surfaces
 #define PSHADOW_MAP_SIZE      512
-#define CUBE_MAP_MIPS      7
-#define CUBE_MAP_SIZE      (1 << CUBE_MAP_MIPS)
  
 #define USE_VERT_TANGENT_SPACE
 #define USE_OVERBRIGHT
@@ -1992,6 +1990,7 @@ extern  cvar_t  *r_specularMapping;
 extern  cvar_t  *r_deluxeMapping;
 extern  cvar_t  *r_parallaxMapping;
 extern  cvar_t  *r_cubeMapping;
+extern  cvar_t  *r_cubemapSize;
 extern  cvar_t  *r_specularIsMetallic;
 extern  cvar_t  *r_glossIsRoughness;
 extern  cvar_t  *r_baseNormalX;
@@ -2689,6 +2688,10 @@ typedef struct {
 	viewParms_t	viewParms;
 } postProcessCommand_t;
 
+typedef struct {
+	int commandId;
+} exportCubemapsCommand_t;
+
 typedef enum {
 	RC_END_OF_LIST,
 	RC_SET_COLOR,
@@ -2703,7 +2706,8 @@ typedef enum {
 	RC_COLORMASK,
 	RC_CLEARDEPTH,
 	RC_CAPSHADOWMAP,
-	RC_POSTPROCESS
+	RC_POSTPROCESS,
+	RC_EXPORT_CUBEMAPS
 } renderCommand_t;
 
 
diff --git a/SP/Makefile b/SP/Makefile
index 94205e6..13284eb 100644
--- a/SP/Makefile
+++ b/SP/Makefile
@@ -1723,6 +1723,7 @@ Q3R2OBJ = \
   $(B)/rend2/tr_image_pcx.o \
   $(B)/rend2/tr_image_png.o \
   $(B)/rend2/tr_image_tga.o \
+  $(B)/rend2/tr_image_dds.o \
   $(B)/rend2/tr_init.o \
   $(B)/rend2/tr_light.o \
   $(B)/rend2/tr_main.o \
diff --git a/SP/code/rend2/tr_backend.c b/SP/code/rend2/tr_backend.c
index 146f35d..3f02686 100644
--- a/SP/code/rend2/tr_backend.c
+++ b/SP/code/rend2/tr_backend.c
@@ -2257,6 +2257,64 @@ const void *RB_PostProcess(const void *data)
 	return (const void *)(cmd + 1);
 }
 
+// FIXME: put this function declaration elsewhere
+void R_SaveDDS(const char *filename, byte *pic, int width, int height, int depth);
+
+/*
+=============
+RB_ExportCubemaps
+
+=============
+*/
+const void *RB_ExportCubemaps(const void *data)
+{
+	const exportCubemapsCommand_t *cmd = data;
+
+	// finish any 2D drawing if needed
+	if (tess.numIndexes)
+		RB_EndSurface();
+
+	if (!glRefConfig.framebufferObject || !tr.world || tr.numCubemaps == 0)
+	{
+		// do nothing
+		ri.Printf(PRINT_ALL, "Nothing to export!\n");
+		return (const void *)(cmd + 1);
+	}
+
+	if (cmd)
+	{
+		FBO_t *oldFbo = glState.currentFBO;
+		int sideSize = r_cubemapSize->integer * r_cubemapSize->integer * 4;
+		byte *cubemapPixels = ri.Z_Malloc(sideSize * 6);
+		int i, j;
+
+		FBO_Bind(tr.renderCubeFbo);
+
+		for (i = 0; i < tr.numCubemaps; i++)
+		{
+			char filename[MAX_QPATH];
+			cubemap_t *cubemap = &tr.cubemaps[i];
+			unsigned char *p = cubemapPixels;
+
+			for (j = 0; j < 6; j++)
+			{
+				qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, cubemap->image->texnum, 0);
+				qglReadPixels(0, 0, r_cubemapSize->integer, r_cubemapSize->integer, GL_RGBA, GL_UNSIGNED_BYTE, p);
+				p += sideSize;
+			}
+
+			Com_sprintf(filename, MAX_QPATH, "cubemaps/%s/%03d.dds", tr.world->baseName, i);
+			R_SaveDDS(filename, cubemapPixels, r_cubemapSize->integer, r_cubemapSize->integer, 6);
+			ri.Printf(PRINT_ALL, "Saved cubemap %d as %s\n", i, filename);
+		}
+
+		FBO_Bind(oldFbo);
+
+		ri.Free(cubemapPixels);
+	}
+
+	return (const void *)(cmd + 1);
+}
 
 /*
 ====================
@@ -2308,6 +2366,9 @@ void RB_ExecuteRenderCommands( const void *data ) {
 		case RC_POSTPROCESS:
 			data = RB_PostProcess(data);
 			break;
+		case RC_EXPORT_CUBEMAPS:
+			data = RB_ExportCubemaps(data);
+			break;
 		case RC_END_OF_LIST:
 		default:
 			// finish any 2D drawing if needed
diff --git a/SP/code/rend2/tr_bsp.c b/SP/code/rend2/tr_bsp.c
index 8fc2987..8bd2c00 100644
--- a/SP/code/rend2/tr_bsp.c
+++ b/SP/code/rend2/tr_bsp.c
@@ -3412,7 +3412,7 @@ void R_RenderAllCubemaps(void)
 
 	for (i = 0; i < tr.numCubemaps; i++)
 	{
-		tr.cubemaps[i].image = R_CreateImage(va("*cubeMap%d", i), NULL, CUBE_MAP_SIZE, CUBE_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_CUBEMAP, GL_RGBA8);
+		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);
 	}
 
 	ri.Printf(PRINT_ALL, "Total cubemaps: %d\n", tr.numCubemaps );
diff --git a/SP/code/rend2/tr_image.c b/SP/code/rend2/tr_image.c
index f5d6c98..aa228d3 100644
--- a/SP/code/rend2/tr_image.c
+++ b/SP/code/rend2/tr_image.c
@@ -1771,13 +1771,16 @@ static qboolean RawImage_HasAlpha(const byte *scan, int numPixels)
 	return qfalse;
 }
 
-static GLenum RawImage_GetFormat(const byte *data, int numPixels, qboolean lightMap, imgType_t type, imgFlags_t flags)
+static GLenum RawImage_GetFormat(const byte *data, int numPixels, GLenum picFormat, qboolean lightMap, imgType_t type, imgFlags_t flags)
 {
 	int samples = 3;
 	GLenum internalFormat = GL_RGB;
 	qboolean forceNoCompression = (flags & IMGFLAG_NO_COMPRESSION);
 	qboolean normalmap = (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT);
 
+	if (picFormat != GL_RGBA8)
+		return picFormat;
+
 	if(normalmap)
 	{
 		if ((type == IMGTYPE_NORMALHEIGHT) && RawImage_HasAlpha(data, numPixels))
@@ -2015,11 +2018,80 @@ static void RawImage_UploadToRgtc2Texture(byte *data, int width, int height, int
 	ri.Hunk_FreeTempMemory(compressedData);
 }
 
-static void RawImage_UploadTexture( byte *data, int x, int y, int width, int height, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture )
+static void RawImage_UploadTexture( byte *data, int x, int y, int width, int height, GLenum picFormat, int numMips, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture )
 {
 	int dataFormat, dataType;
 	qboolean rgtc = (internalFormat == GL_COMPRESSED_RG_RGTC2);
 
+	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)
+				qglCompressedTexSubImage2DARB(GL_TEXTURE_2D, miplevel, x, y, width, height, internalFormat, size, data);
+			else
+				qglCompressedTexImage2DARB(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)
 	{
 		case GL_DEPTH_COMPONENT:
@@ -2051,29 +2123,20 @@ static void RawImage_UploadTexture( byte *data, int x, int y, int width, int hei
 
 	if (flags & IMGFLAG_MIPMAP)
 	{
-		int miplevel;
+		int miplevel = 0;
 
-		miplevel = 0;
 		while (width > 1 || height > 1)
 		{
 			if (data)
 			{
 				if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT)
-				{
 					R_MipMapNormalHeight( data, data, width, height, glRefConfig.swizzleNormalmap );
-				}
 				else
-				{
 					R_MipMapsRGB( data, width, height );
-				}
 			}
 			
-			width >>= 1;
-			height >>= 1;
-			if (width < 1)
-				width = 1;
-			if (height < 1)
-				height = 1;
+			width = MAX(1, width >> 1);
+			height = MAX(1, height >> 1);
 			miplevel++;
 
 			if ( data && r_colorMipLevels->integer )
@@ -2102,7 +2165,7 @@ Upload32
 
 ===============
 */
-static void Upload32(byte *data, int x, int y, int width, int height, image_t *image)
+static void Upload32(byte *data, int x, int y, int width, int height, GLenum picFormat, int numMips, image_t *image)
 {
 	byte		*resampledBuffer = NULL;
 	int			i, c;
@@ -2132,11 +2195,16 @@ static void Upload32(byte *data, int x, int y, int width, int height, image_t *i
 	if (!data)
 	{
 		RawImage_ScaleToPower2(NULL, &width, &height, type, flags, NULL);
-		RawImage_UploadTexture(NULL, 0, 0, width, height, internalFormat, type, flags, qfalse);
+		RawImage_UploadTexture(NULL, 0, 0, width, height, GL_RGBA8, 0, internalFormat, type, flags, qfalse);
 		goto done;
 	}
 	else if (!subtexture)
 	{
+		if (picFormat != GL_RGBA8 && picFormat != GL_SRGB8_ALPHA8_EXT)
+		{
+			RawImage_UploadTexture(data, 0, 0, width, height, picFormat, numMips, internalFormat, type, flags, qfalse);
+			goto done;
+		}
 		notScaled = RawImage_ScaleToPower2(&data, &width, &height, type, flags, &resampledBuffer);
 	}
 
@@ -2174,12 +2242,12 @@ static void Upload32(byte *data, int x, int y, int width, int height, image_t *i
 	if (subtexture)
 	{
 		// FIXME: Incorrect if original texture was not a power of 2 texture or picmipped
-		RawImage_UploadTexture(data, x, y, width, height, internalFormat, type, flags, qtrue);
+		RawImage_UploadTexture(data, x, y, width, height, GL_RGBA8, 0, internalFormat, type, flags, qtrue);
 		GL_CheckErrors();
 		return;
 	}
 
-	RawImage_UploadTexture(data, 0, 0, width, height, internalFormat, type, flags, qfalse);
+	RawImage_UploadTexture(data, 0, 0, width, height, GL_RGBA8, 0, internalFormat, type, flags, qfalse);
 
 done:
 
@@ -2231,13 +2299,12 @@ done:
 
 /*
 ================
-R_CreateImage
+R_CreateImageExt2
 
 This is the only way any image_t are created
 ================
 */
-image_t *R_CreateImageExt( const char *name, byte *pic, int width, int height, imgType_t type, imgFlags_t flags, int internalFormat, qboolean characterMip )
-{
+image_t *R_CreateImageExt2( const char *name, byte *pic, int width, int height, GLenum picFormat, int numMips, imgType_t type, imgFlags_t flags, int internalFormat, qboolean characterMip ) {
 	image_t     *image;
 	qboolean isLightmap = qfalse;
 	int         glWrapClampMode;
@@ -2275,7 +2342,7 @@ image_t *R_CreateImageExt( const char *name, byte *pic, int width, int height, i
 		if (image->flags & IMGFLAG_CUBEMAP)
 			internalFormat = GL_RGBA8;
 		else
-			internalFormat = RawImage_GetFormat(pic, width * height, isLightmap, image->type, image->flags);
+			internalFormat = RawImage_GetFormat(pic, width * height, picFormat, isLightmap, image->type, image->flags);
 	}
 
 	image->internalFormat = internalFormat;
@@ -2325,7 +2392,7 @@ image_t *R_CreateImageExt( const char *name, byte *pic, int width, int height, i
 	}
 	else
 	{
-		Upload32( pic, 0, 0, image->width, image->height, image );
+		Upload32( pic, 0, 0, image->width, image->height, picFormat, numMips, image );
 
 		qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode );
 		qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode );
@@ -2343,9 +2410,15 @@ image_t *R_CreateImageExt( const char *name, byte *pic, int width, int height, i
 	return image;
 }
 
-image_t *R_CreateImage( const char *name, byte *pic, int width, int height,
-		imgType_t type, imgFlags_t flags, int internalFormat ) {
-	return R_CreateImageExt( name, pic, width, height, type, flags, internalFormat, qfalse );
+/*
+================
+R_CreateImage
+
+Wrapper for R_CreateImageExt2(), for the old parameters.
+================
+*/
+image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgType_t type, imgFlags_t flags, int internalFormat ) {
+	return R_CreateImageExt2(name, pic, width, height, GL_RGBA8, 0, type, flags, internalFormat, qfalse);
 }
 
 //----(SA)	end
@@ -2358,13 +2431,16 @@ void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int h
 
 	GL_Bind(image);
 
-	Upload32(pic, x, y, width, height, image);
+	Upload32(pic, x, y, width, height, GL_RGBA8, 0, image);
 
 	GL_SelectTexture(0);
 }
 
 //===================================================================
 
+// Prototype for dds loader function which isn't common to both renderers
+void R_LoadDDS(const char *filename, byte **pic, int *width, int *height, GLenum *picFormat, int *numMips);
+
 typedef struct
 {
 	char *ext;
@@ -2394,7 +2470,7 @@ Loads any of the supported image types into a cannonical
 32 bit format.
 =================
 */
-void R_LoadImage( const char *name, byte **pic, int *width, int *height )
+void R_LoadImage( const char *name, byte **pic, int *width, int *height, GLenum *picFormat, int *numMips )
 {
 	qboolean orgNameFailed = qfalse;
 	int orgLoader = -1;
@@ -2406,11 +2482,28 @@ void R_LoadImage( const char *name, byte **pic, int *width, int *height )
 	*pic = NULL;
 	*width = 0;
 	*height = 0;
+	*picFormat = GL_RGBA8;
+	*numMips = 0;
 
 	Q_strncpyz( localName, name, MAX_QPATH );
 
 	ext = COM_GetExtension( localName );
 
+	// If compressed textures are enabled, try loading a DDS first, it'll load fastest
+	if (r_ext_compressed_textures->integer)
+	{
+		char ddsName[MAX_QPATH];
+
+		COM_StripExtension(name, ddsName, MAX_QPATH);
+		Q_strcat(ddsName, MAX_QPATH, ".dds");
+
+		R_LoadDDS(ddsName, pic, width, height, picFormat, numMips);
+
+		// If loaded, we're done.
+		if (*pic)
+			return;
+	}
+
 	if( *ext )
 	{
 		// Look for the correct loader and use it
@@ -2478,12 +2571,12 @@ Finds or loads the given image.
 Returns NULL if it fails, not a default image.
 ==============
 */
-
-
 image_t *R_FindImageFileExt( const char *name, imgType_t type, imgFlags_t flags, qboolean characterMIP ) {
 	image_t *image;
 	int width, height;
 	byte    *pic;
+	GLenum  picFormat;
+	int picNumMips;
 	long hash;
 
 	if ( !name ) {
@@ -2513,12 +2606,12 @@ image_t *R_FindImageFileExt( const char *name, imgType_t type, imgFlags_t flags,
 	//
 	// load the pic from disk
 	//
-	R_LoadImage( name, &pic, &width, &height );
+	R_LoadImage( name, &pic, &width, &height, &picFormat, &picNumMips );
 	if ( pic == NULL ) {
 		return NULL;
 	}
 
-	if (r_normalMapping->integer && !(type == IMGTYPE_NORMAL) && (flags & IMGFLAG_PICMIP) && (flags & IMGFLAG_MIPMAP) && (flags & IMGFLAG_GENNORMALMAP))
+	if (r_normalMapping->integer && (picFormat == GL_RGBA8) && !(type == IMGTYPE_NORMAL) && (flags & IMGFLAG_PICMIP) && (flags & IMGFLAG_MIPMAP) && (flags & IMGFLAG_GENNORMALMAP))
 	{
 		char normalName[MAX_QPATH];
 		image_t *normalImage;
@@ -2621,7 +2714,7 @@ image_t *R_FindImageFileExt( const char *name, imgType_t type, imgFlags_t flags,
 		}
 	}
 
-	image = R_CreateImageExt( ( char * ) name, pic, width, height, type, flags, 0, characterMIP );
+	image = R_CreateImageExt2( ( char * ) name, pic, width, height, picFormat, picNumMips, type, flags, 0, characterMIP );
 	ri.Free( pic );
 	return image;
 }
@@ -2929,7 +3022,7 @@ void R_CreateBuiltinImages( void ) {
 
 		if (r_cubeMapping->integer)
 		{
-			tr.renderCubeImage = R_CreateImage("*renderCube", NULL, CUBE_MAP_SIZE, CUBE_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_CUBEMAP, rgbFormat);
+			tr.renderCubeImage = R_CreateImage("*renderCube", NULL, r_cubemapSize->integer, r_cubemapSize->integer, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_CUBEMAP, rgbFormat);
 		}
 	}
 }
@@ -3898,6 +3991,8 @@ void    R_CropAndNumberImagesInDirectory( char *dir, char *ext, int maxWidth, in
 #endif
 	byte    *pic, *temppic;
 	int width, height, newWidth, newHeight;
+	GLenum  picFormat;
+	int picNumMips;
 	char    *pch;
 	int b,c,d,lastNumber;
 	int lastBox[2] = {0,0};
@@ -3921,7 +4016,7 @@ void    R_CropAndNumberImagesInDirectory( char *dir, char *ext, int maxWidth, in
 		Com_sprintf( filename, sizeof( filename ), "%s/%s", dir, fileList[j] );
 		ri.Printf( PRINT_ALL, "...cropping '%s'.. ", filename );
 
-		R_LoadImage( filename, &pic, &width, &height );
+		R_LoadImage( filename, &pic, &width, &height, &picFormat, &picNumMips );
 		if ( !pic ) {
 			ri.Printf( PRINT_ALL, "error reading file, ignoring.\n" );
 			continue;
diff --git a/SP/code/rend2/tr_image_dds.c b/SP/code/rend2/tr_image_dds.c
new file mode 100644
index 0000000..26318be
--- /dev/null
+++ b/SP/code/rend2/tr_image_dds.c
@@ -0,0 +1,498 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+              2015 James Canete
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "tr_local.h"
+
+typedef unsigned int   ui32_t;
+
+typedef struct ddsHeader_s
+{
+	ui32_t headerSize;
+	ui32_t flags;
+	ui32_t height;
+	ui32_t width;
+	ui32_t pitchOrFirstMipSize;
+	ui32_t volumeDepth;
+	ui32_t numMips;
+	ui32_t reserved1[11];
+	ui32_t always_0x00000020;
+	ui32_t pixelFormatFlags;
+	ui32_t fourCC;
+	ui32_t rgbBitCount;
+	ui32_t rBitMask;
+	ui32_t gBitMask;
+	ui32_t bBitMask;
+	ui32_t aBitMask;
+	ui32_t caps;
+	ui32_t caps2;
+	ui32_t caps3;
+	ui32_t caps4;
+	ui32_t reserved2;
+}
+ddsHeader_t;
+
+// flags:
+#define _DDSFLAGS_REQUIRED     0x001007
+#define _DDSFLAGS_PITCH        0x8
+#define _DDSFLAGS_MIPMAPCOUNT  0x20000
+#define _DDSFLAGS_FIRSTMIPSIZE 0x80000
+#define _DDSFLAGS_VOLUMEDEPTH  0x800000
+
+// pixelFormatFlags:
+#define DDSPF_ALPHAPIXELS 0x1
+#define DDSPF_ALPHA       0x2
+#define DDSPF_FOURCC      0x4
+#define DDSPF_RGB         0x40
+#define DDSPF_YUV         0x200
+#define DDSPF_LUMINANCE   0x20000
+
+// caps:
+#define DDSCAPS_COMPLEX  0x8
+#define DDSCAPS_MIPMAP   0x400000
+#define DDSCAPS_REQUIRED 0x1000
+
+// caps2:
+#define DDSCAPS2_CUBEMAP 0xFE00
+#define DDSCAPS2_VOLUME  0x200000
+
+typedef struct ddsHeaderDxt10_s
+{
+	ui32_t dxgiFormat;
+	ui32_t dimensions;
+	ui32_t miscFlags;
+	ui32_t arraySize;
+	ui32_t miscFlags2;
+}
+ddsHeaderDxt10_t;
+
+// dxgiFormat
+// from http://msdn.microsoft.com/en-us/library/windows/desktop/bb173059%28v=vs.85%29.aspx
+typedef enum DXGI_FORMAT {
+	DXGI_FORMAT_UNKNOWN = 0,
+	DXGI_FORMAT_R32G32B32A32_TYPELESS = 1,
+	DXGI_FORMAT_R32G32B32A32_FLOAT = 2,
+	DXGI_FORMAT_R32G32B32A32_UINT = 3,
+	DXGI_FORMAT_R32G32B32A32_SINT = 4,
+	DXGI_FORMAT_R32G32B32_TYPELESS = 5,
+	DXGI_FORMAT_R32G32B32_FLOAT = 6,
+	DXGI_FORMAT_R32G32B32_UINT = 7,
+	DXGI_FORMAT_R32G32B32_SINT = 8,
+	DXGI_FORMAT_R16G16B16A16_TYPELESS = 9,
+	DXGI_FORMAT_R16G16B16A16_FLOAT = 10,
+	DXGI_FORMAT_R16G16B16A16_UNORM = 11,
+	DXGI_FORMAT_R16G16B16A16_UINT = 12,
+	DXGI_FORMAT_R16G16B16A16_SNORM = 13,
+	DXGI_FORMAT_R16G16B16A16_SINT = 14,
+	DXGI_FORMAT_R32G32_TYPELESS = 15,
+	DXGI_FORMAT_R32G32_FLOAT = 16,
+	DXGI_FORMAT_R32G32_UINT = 17,
+	DXGI_FORMAT_R32G32_SINT = 18,
+	DXGI_FORMAT_R32G8X24_TYPELESS = 19,
+	DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20,
+	DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21,
+	DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22,
+	DXGI_FORMAT_R10G10B10A2_TYPELESS = 23,
+	DXGI_FORMAT_R10G10B10A2_UNORM = 24,
+	DXGI_FORMAT_R10G10B10A2_UINT = 25,
+	DXGI_FORMAT_R11G11B10_FLOAT = 26,
+	DXGI_FORMAT_R8G8B8A8_TYPELESS = 27,
+	DXGI_FORMAT_R8G8B8A8_UNORM = 28,
+	DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29,
+	DXGI_FORMAT_R8G8B8A8_UINT = 30,
+	DXGI_FORMAT_R8G8B8A8_SNORM = 31,
+	DXGI_FORMAT_R8G8B8A8_SINT = 32,
+	DXGI_FORMAT_R16G16_TYPELESS = 33,
+	DXGI_FORMAT_R16G16_FLOAT = 34,
+	DXGI_FORMAT_R16G16_UNORM = 35,
+	DXGI_FORMAT_R16G16_UINT = 36,
+	DXGI_FORMAT_R16G16_SNORM = 37,
+	DXGI_FORMAT_R16G16_SINT = 38,
+	DXGI_FORMAT_R32_TYPELESS = 39,
+	DXGI_FORMAT_D32_FLOAT = 40,
+	DXGI_FORMAT_R32_FLOAT = 41,
+	DXGI_FORMAT_R32_UINT = 42,
+	DXGI_FORMAT_R32_SINT = 43,
+	DXGI_FORMAT_R24G8_TYPELESS = 44,
+	DXGI_FORMAT_D24_UNORM_S8_UINT = 45,
+	DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46,
+	DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47,
+	DXGI_FORMAT_R8G8_TYPELESS = 48,
+	DXGI_FORMAT_R8G8_UNORM = 49,
+	DXGI_FORMAT_R8G8_UINT = 50,
+	DXGI_FORMAT_R8G8_SNORM = 51,
+	DXGI_FORMAT_R8G8_SINT = 52,
+	DXGI_FORMAT_R16_TYPELESS = 53,
+	DXGI_FORMAT_R16_FLOAT = 54,
+	DXGI_FORMAT_D16_UNORM = 55,
+	DXGI_FORMAT_R16_UNORM = 56,
+	DXGI_FORMAT_R16_UINT = 57,
+	DXGI_FORMAT_R16_SNORM = 58,
+	DXGI_FORMAT_R16_SINT = 59,
+	DXGI_FORMAT_R8_TYPELESS = 60,
+	DXGI_FORMAT_R8_UNORM = 61,
+	DXGI_FORMAT_R8_UINT = 62,
+	DXGI_FORMAT_R8_SNORM = 63,
+	DXGI_FORMAT_R8_SINT = 64,
+	DXGI_FORMAT_A8_UNORM = 65,
+	DXGI_FORMAT_R1_UNORM = 66,
+	DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67,
+	DXGI_FORMAT_R8G8_B8G8_UNORM = 68,
+	DXGI_FORMAT_G8R8_G8B8_UNORM = 69,
+	DXGI_FORMAT_BC1_TYPELESS = 70,
+	DXGI_FORMAT_BC1_UNORM = 71,
+	DXGI_FORMAT_BC1_UNORM_SRGB = 72,
+	DXGI_FORMAT_BC2_TYPELESS = 73,
+	DXGI_FORMAT_BC2_UNORM = 74,
+	DXGI_FORMAT_BC2_UNORM_SRGB = 75,
+	DXGI_FORMAT_BC3_TYPELESS = 76,
+	DXGI_FORMAT_BC3_UNORM = 77,
+	DXGI_FORMAT_BC3_UNORM_SRGB = 78,
+	DXGI_FORMAT_BC4_TYPELESS = 79,
+	DXGI_FORMAT_BC4_UNORM = 80,
+	DXGI_FORMAT_BC4_SNORM = 81,
+	DXGI_FORMAT_BC5_TYPELESS = 82,
+	DXGI_FORMAT_BC5_UNORM = 83,
+	DXGI_FORMAT_BC5_SNORM = 84,
+	DXGI_FORMAT_B5G6R5_UNORM = 85,
+	DXGI_FORMAT_B5G5R5A1_UNORM = 86,
+	DXGI_FORMAT_B8G8R8A8_UNORM = 87,
+	DXGI_FORMAT_B8G8R8X8_UNORM = 88,
+	DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89,
+	DXGI_FORMAT_B8G8R8A8_TYPELESS = 90,
+	DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91,
+	DXGI_FORMAT_B8G8R8X8_TYPELESS = 92,
+	DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93,
+	DXGI_FORMAT_BC6H_TYPELESS = 94,
+	DXGI_FORMAT_BC6H_UF16 = 95,
+	DXGI_FORMAT_BC6H_SF16 = 96,
+	DXGI_FORMAT_BC7_TYPELESS = 97,
+	DXGI_FORMAT_BC7_UNORM = 98,
+	DXGI_FORMAT_BC7_UNORM_SRGB = 99,
+	DXGI_FORMAT_AYUV = 100,
+	DXGI_FORMAT_Y410 = 101,
+	DXGI_FORMAT_Y416 = 102,
+	DXGI_FORMAT_NV12 = 103,
+	DXGI_FORMAT_P010 = 104,
+	DXGI_FORMAT_P016 = 105,
+	DXGI_FORMAT_420_OPAQUE = 106,
+	DXGI_FORMAT_YUY2 = 107,
+	DXGI_FORMAT_Y210 = 108,
+	DXGI_FORMAT_Y216 = 109,
+	DXGI_FORMAT_NV11 = 110,
+	DXGI_FORMAT_AI44 = 111,
+	DXGI_FORMAT_IA44 = 112,
+	DXGI_FORMAT_P8 = 113,
+	DXGI_FORMAT_A8P8 = 114,
+	DXGI_FORMAT_B4G4R4A4_UNORM = 115,
+	DXGI_FORMAT_FORCE_UINT = 0xffffffffUL
+} DXGI_FORMAT;
+
+#define EncodeFourCC(x) ((((ui32_t)((x)[0]))      ) | \
+                         (((ui32_t)((x)[1])) << 8 ) | \
+                         (((ui32_t)((x)[2])) << 16) | \
+                         (((ui32_t)((x)[3])) << 24) )
+
+
+void R_LoadDDS ( const char *filename, byte **pic, int *width, int *height, GLenum *picFormat, int *numMips )
+{
+	union {
+		byte *b;
+		void *v;
+	} buffer;
+	int len;
+	ddsHeader_t *ddsHeader = NULL;
+	ddsHeaderDxt10_t *ddsHeaderDxt10 = NULL;
+	byte *data;
+
+	if (!picFormat)
+	{
+		ri.Printf(PRINT_ERROR, "R_LoadDDS() called without picFormat parameter!");
+		return;
+	}
+
+	if (width)
+		*width = 0;
+	if (height)
+		*height = 0;
+	if (picFormat)
+		*picFormat = GL_RGBA8;
+	if (numMips)
+		*numMips = 1;
+
+	*pic = NULL;
+
+	//
+	// load the file
+	//
+	len = ri.FS_ReadFile( ( char * ) filename, &buffer.v);
+	if (!buffer.b || len < 0) {
+		return;
+	}
+
+	//
+	// reject files that are too small to hold even a header
+	//
+	if (len < 4 + sizeof(*ddsHeader))
+	{
+		ri.Printf(PRINT_ALL, "File %s is too small to be a DDS file.\n", filename);
+		ri.FS_FreeFile(buffer.v);
+		return;
+	}
+
+	//
+	// reject files that don't start with "DDS "
+	//
+	if (*((ui32_t *)(buffer.b)) != EncodeFourCC("DDS "))
+	{
+		ri.Printf(PRINT_ALL, "File %s is not a DDS file.\n", filename);
+		ri.FS_FreeFile(buffer.v);
+		return;
+	}
+
+	//
+	// parse header and dx10 header if available
+	//
+	ddsHeader = (ddsHeader_t *)(buffer.b + 4);
+	if ((ddsHeader->pixelFormatFlags & DDSPF_FOURCC) && ddsHeader->fourCC == EncodeFourCC("DX10"))
+	{
+		if (len < 4 + sizeof(*ddsHeader) + sizeof(*ddsHeaderDxt10))
+		{
+			ri.Printf(PRINT_ALL, "File %s indicates a DX10 header it is too small to contain.\n", filename);
+			ri.FS_FreeFile(buffer.v);
+			return;
+		}
+
+		ddsHeaderDxt10 = (ddsHeaderDxt10_t *)(buffer.b + 4 + sizeof(ddsHeader_t));
+		data = buffer.b + 4 + sizeof(*ddsHeader) + sizeof(*ddsHeaderDxt10);
+		len -= 4 + sizeof(*ddsHeader) + sizeof(*ddsHeaderDxt10);
+	}
+	else
+	{
+		data = buffer.b + 4 + sizeof(*ddsHeader);
+		len -= 4 + sizeof(*ddsHeader);
+	}
+
+	if (width)
+		*width = ddsHeader->width;
+	if (height)
+		*height = ddsHeader->height;
+
+	if (numMips)
+	{
+		if (ddsHeader->flags & _DDSFLAGS_MIPMAPCOUNT)
+			*numMips = ddsHeader->numMips;
+		else
+			*numMips = 1;
+	}
+
+	// FIXME: handle cube map
+	//if ((ddsHeader->caps2 & DDSCAPS2_CUBEMAP) == DDSCAPS2_CUBEMAP)
+
+	//
+	// Convert DXGI format/FourCC into OpenGL format
+	//
+	if (ddsHeaderDxt10)
+	{
+		switch (ddsHeaderDxt10->dxgiFormat)
+		{
+			case DXGI_FORMAT_BC1_TYPELESS:
+			case DXGI_FORMAT_BC1_UNORM:
+				// FIXME: check for GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
+				*picFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
+				break;
+
+			case DXGI_FORMAT_BC1_UNORM_SRGB:
+				// FIXME: check for GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT
+				*picFormat = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
+				break;
+
+			case DXGI_FORMAT_BC2_TYPELESS:
+			case DXGI_FORMAT_BC2_UNORM:
+				*picFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+				break;
+
+			case DXGI_FORMAT_BC2_UNORM_SRGB:
+				*picFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
+				break;
+
+			case DXGI_FORMAT_BC3_TYPELESS:
+			case DXGI_FORMAT_BC3_UNORM:
+				*picFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+				break;
+
+			case DXGI_FORMAT_BC3_UNORM_SRGB:
+				*picFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
+				break;
+
+			case DXGI_FORMAT_BC4_TYPELESS:
+			case DXGI_FORMAT_BC4_UNORM:
+				*picFormat = GL_COMPRESSED_RED_RGTC1;
+				break;
+
+			case DXGI_FORMAT_BC4_SNORM:
+				*picFormat = GL_COMPRESSED_SIGNED_RED_RGTC1;
+				break;
+
+			case DXGI_FORMAT_BC5_TYPELESS:
+			case DXGI_FORMAT_BC5_UNORM:
+				*picFormat = GL_COMPRESSED_RG_RGTC2;
+				break;
+
+			case DXGI_FORMAT_BC5_SNORM:
+				*picFormat = GL_COMPRESSED_SIGNED_RG_RGTC2;
+				break;
+
+			case DXGI_FORMAT_BC6H_TYPELESS:
+			case DXGI_FORMAT_BC6H_UF16:
+				*picFormat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB;
+				break;
+
+			case DXGI_FORMAT_BC6H_SF16:
+				*picFormat = GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB;
+				break;
+
+			case DXGI_FORMAT_BC7_TYPELESS:
+			case DXGI_FORMAT_BC7_UNORM:
+				*picFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
+				break;
+
+			case DXGI_FORMAT_BC7_UNORM_SRGB:
+				*picFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB;
+				break;
+
+			case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
+				*picFormat = GL_SRGB8_ALPHA8_EXT;
+				break;
+
+			case DXGI_FORMAT_R8G8B8A8_UNORM:
+			case DXGI_FORMAT_R8G8B8A8_SNORM:
+				*picFormat = GL_RGBA8;
+				break;
+
+			default:
+				ri.Printf(PRINT_ALL, "DDS File %s has unsupported DXGI format %d.", filename, ddsHeaderDxt10->dxgiFormat);
+				ri.FS_FreeFile(buffer.v);
+				return;
+				break;
+		}
+	}
+	else
+	{
+		if (ddsHeader->pixelFormatFlags & DDSPF_FOURCC)
+		{
+			if (ddsHeader->fourCC == EncodeFourCC("DXT1"))
+				*picFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
+			else if (ddsHeader->fourCC == EncodeFourCC("DXT2"))
+				*picFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+			else if (ddsHeader->fourCC == EncodeFourCC("DXT3"))
+				*picFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+			else if (ddsHeader->fourCC == EncodeFourCC("DXT4"))
+				*picFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+			else if (ddsHeader->fourCC == EncodeFourCC("DXT5"))
+				*picFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+			else if (ddsHeader->fourCC == EncodeFourCC("ATI1"))
+				*picFormat = GL_COMPRESSED_RED_RGTC1;
+			else if (ddsHeader->fourCC == EncodeFourCC("BC4U"))
+				*picFormat = GL_COMPRESSED_RED_RGTC1;
+			else if (ddsHeader->fourCC == EncodeFourCC("BC4S"))
+				*picFormat = GL_COMPRESSED_SIGNED_RED_RGTC1;
+			else if (ddsHeader->fourCC == EncodeFourCC("ATI2"))
+				*picFormat = GL_COMPRESSED_RG_RGTC2;
+			else if (ddsHeader->fourCC == EncodeFourCC("BC5U"))
+				*picFormat = GL_COMPRESSED_RG_RGTC2;
+			else if (ddsHeader->fourCC == EncodeFourCC("BC5S"))
+				*picFormat = GL_COMPRESSED_SIGNED_RG_RGTC2;
+			else
+			{
+				ri.Printf(PRINT_ALL, "DDS File %s has unsupported FourCC.", filename);
+				ri.FS_FreeFile(buffer.v);
+				return;
+			}
+		}
+		else if (ddsHeader->pixelFormatFlags == (DDSPF_RGB | DDSPF_ALPHAPIXELS)
+			&& ddsHeader->rgbBitCount == 32
+			&& ddsHeader->rBitMask == 0x000000ff
+			&& ddsHeader->gBitMask == 0x0000ff00
+			&& ddsHeader->bBitMask == 0x00ff0000
+			&& ddsHeader->aBitMask == 0xff000000)
+		{
+			*picFormat = GL_RGBA8;
+		}
+		else
+		{
+			ri.Printf(PRINT_ALL, "DDS File %s has unsupported RGBA format.", filename);
+			ri.FS_FreeFile(buffer.v);
+			return;
+		}
+	}
+
+	*pic = ri.Z_Malloc(len);
+	Com_Memcpy(*pic, data, len);
+
+	ri.FS_FreeFile(buffer.v);
+}
+
+void R_SaveDDS(const char *filename, byte *pic, int width, int height, int depth)
+{
+	byte *data;
+	ddsHeader_t *ddsHeader;
+	int picSize, size;
+
+	if (!depth)
+		depth = 1;
+
+	picSize = width * height * depth * 4;
+	size = 4 + sizeof(*ddsHeader) + picSize;
+	data = ri.Z_Malloc(size);
+
+	data[0] = 'D';
+	data[1] = 'D';
+	data[2] = 'S';
+	data[3] = ' ';
+
+	ddsHeader = (ddsHeader_t *)(data + 4);
+	memset(ddsHeader, 0, sizeof(ddsHeader_t));
+
+	ddsHeader->headerSize = 0x7c;
+	ddsHeader->flags = _DDSFLAGS_REQUIRED;
+	ddsHeader->height = height;
+	ddsHeader->width = width;
+	ddsHeader->always_0x00000020 = 0x00000020;
+	ddsHeader->caps = DDSCAPS_COMPLEX | DDSCAPS_REQUIRED;
+
+	if (depth == 6)
+		ddsHeader->caps2 = DDSCAPS2_CUBEMAP;
+
+	ddsHeader->pixelFormatFlags = DDSPF_RGB | DDSPF_ALPHAPIXELS;
+	ddsHeader->rgbBitCount = 32;
+	ddsHeader->rBitMask = 0x000000ff;
+	ddsHeader->gBitMask = 0x0000ff00;
+	ddsHeader->bBitMask = 0x00ff0000;
+	ddsHeader->aBitMask = 0xff000000;
+
+	Com_Memcpy(data + 4 + sizeof(*ddsHeader), pic, picSize);
+
+	ri.FS_WriteFile(filename, data, size);
+
+	ri.Free(data);
+}
diff --git a/SP/code/rend2/tr_init.c b/SP/code/rend2/tr_init.c
index 6f41ae5..da616a4 100644
--- a/SP/code/rend2/tr_init.c
+++ b/SP/code/rend2/tr_init.c
@@ -166,6 +166,7 @@ cvar_t  *r_specularMapping;
 cvar_t  *r_deluxeMapping;
 cvar_t  *r_parallaxMapping;
 cvar_t  *r_cubeMapping;
+cvar_t  *r_cubemapSize;
 cvar_t  *r_specularIsMetallic;
 cvar_t  *r_glossIsRoughness;
 cvar_t  *r_baseNormalX;
@@ -909,6 +910,33 @@ void R_ScreenShotJPEG_f (void) {
 	}
 }
 
+/*
+==================
+R_ExportCubemaps
+==================
+*/
+void R_ExportCubemaps(void)
+{
+	exportCubemapsCommand_t	*cmd;
+
+	cmd = R_GetCommandBuffer(sizeof(*cmd));
+	if (!cmd) {
+		return;
+	}
+	cmd->commandId = RC_EXPORT_CUBEMAPS;
+}
+
+
+/*
+==================
+R_ExportCubemaps_f
+==================
+*/
+void R_ExportCubemaps_f(void)
+{
+	R_ExportCubemaps();
+}
+
 //============================================================================
 
 /*
@@ -1350,6 +1378,7 @@ void R_Register( void ) {
 	r_deluxeMapping = ri.Cvar_Get( "r_deluxeMapping", "1", CVAR_ARCHIVE | CVAR_LATCH );
 	r_parallaxMapping = ri.Cvar_Get( "r_parallaxMapping", "0", CVAR_ARCHIVE | CVAR_LATCH );
 	r_cubeMapping = ri.Cvar_Get( "r_cubeMapping", "0", CVAR_ARCHIVE | CVAR_LATCH );
+	r_cubemapSize = ri.Cvar_Get( "r_cubemapSize", "128", CVAR_ARCHIVE | CVAR_LATCH );
 	r_specularIsMetallic = ri.Cvar_Get( "r_specularIsMetallic", "0", CVAR_ARCHIVE | CVAR_LATCH );
 	r_glossIsRoughness = ri.Cvar_Get("r_glossIsRoughness", "0", CVAR_ARCHIVE | CVAR_LATCH);
 	r_baseNormalX = ri.Cvar_Get( "r_baseNormalX", "1.0", CVAR_ARCHIVE | CVAR_LATCH );
@@ -1512,6 +1541,7 @@ void R_Register( void ) {
 	ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f );
 	ri.Cmd_AddCommand( "minimize", GLimp_Minimize );
 	ri.Cmd_AddCommand( "gfxmeminfo", GfxMemInfo_f );
+	ri.Cmd_AddCommand( "exportCubemaps", R_ExportCubemaps_f );
 	ri.Cmd_AddCommand( "taginfo", R_TagInfo_f );
 
 	// Ridah
@@ -1664,6 +1694,7 @@ void RE_Shutdown( qboolean destroyWindow ) {
 	ri.Cmd_RemoveCommand( "modelist" );
 	ri.Cmd_RemoveCommand( "shaderstate" );
 	ri.Cmd_RemoveCommand( "gfxmeminfo" );
+	ri.Cmd_RemoveCommand( "exportCubemaps" );
 	ri.Cmd_RemoveCommand( "taginfo" );
 
 	// Ridah
diff --git a/SP/code/rend2/tr_local.h b/SP/code/rend2/tr_local.h
index 469257f..72977eb 100644
--- a/SP/code/rend2/tr_local.h
+++ b/SP/code/rend2/tr_local.h
@@ -61,8 +61,6 @@ typedef unsigned int glIndex_t;
 #define MAX_CALC_PSHADOWS    64
 #define MAX_DRAWN_PSHADOWS    32 // do not increase past 32, because bit flags are used on surfaces
 #define PSHADOW_MAP_SIZE      512
-#define CUBE_MAP_MIPS      7
-#define CUBE_MAP_SIZE      (1 << CUBE_MAP_MIPS)
  
 #define USE_VERT_TANGENT_SPACE
 #define USE_OVERBRIGHT
@@ -2012,6 +2010,7 @@ extern  cvar_t  *r_specularMapping;
 extern  cvar_t  *r_deluxeMapping;
 extern  cvar_t  *r_parallaxMapping;
 extern  cvar_t  *r_cubeMapping;
+extern  cvar_t  *r_cubemapSize;
 extern  cvar_t  *r_specularIsMetallic;
 extern  cvar_t  *r_glossIsRoughness;
 extern  cvar_t  *r_baseNormalX;
@@ -2722,6 +2721,10 @@ typedef struct {
 	viewParms_t	viewParms;
 } postProcessCommand_t;
 
+typedef struct {
+	int commandId;
+} exportCubemapsCommand_t;
+
 typedef enum {
 	RC_END_OF_LIST,
 	RC_SET_COLOR,
@@ -2735,7 +2738,8 @@ typedef enum {
 	RC_COLORMASK,
 	RC_CLEARDEPTH,
 	RC_CAPSHADOWMAP,
-	RC_POSTPROCESS
+	RC_POSTPROCESS,
+	RC_EXPORT_CUBEMAPS
 } renderCommand_t;
 
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/iortcw.git



More information about the Pkg-games-commits mailing list