Ver código fonte

Added texture compression and texture mip map generation

Marko Pintera 11 anos atrás
pai
commit
aef74705f2

+ 9 - 4
BansheeCore/Include/BsPixelData.h

@@ -52,11 +52,11 @@ namespace BansheeEngine
 		PF_BYTE_RGBA = PF_R8G8B8A8,      
         // DXT1/BC1 format containing opaque RGB or 1-bit alpha RGB. 4 bits per pixel.
         PF_BC1 = 13,
-        // DXT2/BC2 format containing RGB with premultiplied alpha. 8 bits per pixel.
-        PF_BC2 = 14,
+		// DXT3/BC2 format containing RGB with premultiplied alpha. 4 bits per pixel.
+		PF_BC1a = 14,
         // DXT3/BC2 format containing RGB with explicit alpha. 8 bits per pixel.
-        PF_BC2a = 15,
-        // DXT5/BC3 format containing RGB with explicit alpha. 8 bits per pixel. Better alpha gradients than BC2.
+        PF_BC2 = 15,
+        // DXT5/BC2 format containing RGB with explicit alpha. 8 bits per pixel. Better alpha gradients than BC2.
         PF_BC3 = 16,
 		// One channel compressed format. 4 bits per pixel.
 		PF_BC4 = 17,
@@ -281,6 +281,11 @@ namespace BansheeEngine
 		 */
       	UINT32 getConsecutiveSize() const;
 
+		/**
+		 * @brief	Return the size (in bytes) of the buffer this image requires.
+		 */
+      	UINT32 getSize() const;
+
 		/**
 		 * @brief	Returns pixel data containing a sub-volume of this object. Returned
 		 *			data will not have its own buffer, but will instead point to this one.

+ 92 - 0
BansheeCore/Include/BsPixelUtil.h

@@ -5,6 +5,85 @@
 
 namespace BansheeEngine 
 {
+	/**
+	 * @brief	Available compression formats.
+	 */
+	enum class CompressedFormat
+	{
+		BC1, /**< PF_BC1 */
+		BC1a, /**< PF_BC1a */
+		BC2, /**< PF_BC2 */
+		BC3, /**< PF_BC3 */
+		BC4, /**< PF_BC4 */
+		BC5, /**< PF_BC5 */
+		BC6H, /**< PF_BC6H */
+		BC7 /**< PF_BC7 */
+	};
+
+	/**
+	 * @brief	Available types of texture compression quality.
+	 */
+	enum class CompressionQuality
+	{
+		Fastest,
+		Normal,
+		Production,
+		Highest
+	};
+
+	/**
+	 * @brief	Specifies what is alpha channel used for in the texture.
+	 */
+	enum class AlphaMode
+	{
+		None,
+		Transparency,
+		Premultiplied
+	};
+
+	/**
+	 * @brief	Wrap mode to use when generating mip maps.
+	 */
+	enum class MipMapWrapMode
+	{
+		Mirror,
+		Repeat,
+		Clamp
+	};
+
+	/**
+	 * @brief	Filter to use when generating mip maps.
+	 */
+	enum class MipMapFilter
+	{
+		Box,
+		Triangle,
+		Kaiser
+	};
+
+	/**
+	 * @brief	Options used to control texture compression.
+	 */
+	struct CompressionOptions
+	{
+		CompressedFormat format = CompressedFormat::BC1;
+		AlphaMode alphaMode = AlphaMode::None;
+		bool isNormalMap = false;
+		bool isSRGB = false;
+		CompressionQuality quality = CompressionQuality::Normal;
+	};
+
+	/**
+	 * @brief	Options used to control texture mip map generation.
+	 */
+	struct MipMapGenOptions
+	{
+		MipMapFilter filter = MipMapFilter::Box;
+		MipMapWrapMode wrapMode = MipMapWrapMode::Mirror;
+		bool isNormalMap = false;
+		bool normalizeMipmaps = false;
+	};
+
 	/**
 	 * @brief	Utility methods for converting and managing pixel data and formats.
 	 */
@@ -171,6 +250,19 @@ namespace BansheeEngine
 		 */
         static void bulkPixelConversion(const PixelData& src, const PixelData& dst);
 
+		/**
+		 * @brief	Compresses the provided data using the specified compression options. 
+		 */
+		static PixelDataPtr compress(const PixelData& src, const CompressionOptions& options);
+
+		/**
+		 * @brief	Generates mip-maps from the provided source data using the specified compression options.
+		 *
+		 * @returns	A list of calculated mip-map data. First entry is the largest mip and other follow in
+		 *			order from largest to smallest.
+		 */
+		static Vector<PixelDataPtr> genMipmaps(const PixelData& src, const MipMapGenOptions& options);
+
 		/**
 		 * @brief	Scales pixel data in the source buffer and stores the scaled data in the destination buffer.
 		 *			Provided pixel data objects must have previously allocated buffers of adequate size. You may

+ 6 - 1
BansheeCore/Source/BsPixelData.cpp

@@ -31,6 +31,11 @@ namespace BansheeEngine
 		return PixelUtil::getMemorySize(getWidth(), getHeight(), getDepth(), mFormat);
 	}
 
+	UINT32 PixelData::getSize() const
+	{
+		return PixelUtil::getMemorySize(mRowPitch, mSlicePitch / mRowPitch, getDepth(), getFormat());
+	}
+
 	PixelData PixelData::getSubVolume(const PixelVolume &def) const
 	{
 		if (PixelUtil::isCompressed(mFormat))
@@ -84,7 +89,7 @@ namespace BansheeEngine
 
 	UINT32 PixelData::getInternalBufferSize()
 	{
-		return PixelUtil::getMemorySize(getWidth(), getHeight(), getDepth(), getFormat());
+		return getSize();
 	}
 
 	/************************************************************************/

+ 298 - 6
BansheeCore/Source/BsPixelUtil.cpp

@@ -1,7 +1,9 @@
 #include "BsPixelUtil.h"
 #include "BsBitwise.h"
 #include "BsColor.h"
+#include "BsMath.h"
 #include "BsException.h"
+#include "nvtt/nvtt.h"
 
 namespace BansheeEngine 
 {
@@ -548,20 +550,20 @@ namespace BansheeEngine
         0, 0, 0, 0, 0, 0, 0, 0
         },
 	//-----------------------------------------------------------------------
-        {"PF_BC2",
+        {"PF_BC1a",
         /* Bytes per element */
         0,
         /* Flags */
-        PFF_COMPRESSED | PFF_HASALPHA,
+        PFF_COMPRESSED,
         /* Component type and count */
-        PCT_BYTE, 4,
+        PCT_BYTE, 3,
         /* rbits, gbits, bbits, abits */
         0, 0, 0, 0,
         /* Masks and shifts */
         0, 0, 0, 0, 0, 0, 0, 0
         },
 	//-----------------------------------------------------------------------
-        {"PF_BC2a",
+        {"PF_BC2",
         /* Bytes per element */
         0,
         /* Flags */
@@ -804,6 +806,167 @@ namespace BansheeEngine
         return _pixelFormats[ord];
     }
 
+	/**
+	 * @brief	Handles compression output from NVTT library for a single image.
+	 */
+	struct NVTTCompressOutputHandler : public nvtt::OutputHandler
+	{
+		NVTTCompressOutputHandler(UINT8* buffer, UINT32 sizeBytes)
+			:buffer(buffer), bufferWritePos(buffer), bufferEnd(buffer + sizeBytes)
+		{ }
+
+		virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel)
+		{ }
+
+		virtual bool writeData(const void* data, int size)
+		{
+			assert((bufferWritePos + size) <= bufferEnd);
+			memcpy(bufferWritePos, data, size);
+			bufferWritePos += size;
+
+			return true;
+		}
+
+		UINT8* buffer;
+		UINT8* bufferWritePos;
+		UINT8* bufferEnd;
+	};
+
+	/**
+	 * @brief	Handles output from NVTT library for a mip-map chain.
+	 */
+	struct NVTTMipmapOutputHandler : public nvtt::OutputHandler
+	{
+		NVTTMipmapOutputHandler(const Vector<PixelDataPtr>& buffers)
+			:buffers(buffers), bufferWritePos(nullptr), bufferEnd(nullptr)
+		{ }
+
+		virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel)
+		{ 
+			assert(miplevel >= 0 && miplevel < buffers.size());
+			assert(size == buffers[miplevel]->getConsecutiveSize());
+
+			activeBuffer = buffers[miplevel];
+
+			bufferWritePos = activeBuffer->getData();
+			bufferEnd = bufferWritePos + activeBuffer->getConsecutiveSize();
+		}
+
+		virtual bool writeData(const void* data, int size)
+		{
+			assert((bufferWritePos + size) <= bufferEnd);
+			memcpy(bufferWritePos, data, size);
+			bufferWritePos += size;
+
+			return true;
+		}
+
+		Vector<PixelDataPtr> buffers;
+		PixelDataPtr activeBuffer;
+
+		UINT8* bufferWritePos;
+		UINT8* bufferEnd;
+	};
+
+	nvtt::Format toNVTTFormat(CompressedFormat format)
+	{
+		switch (format)
+		{
+		case CompressedFormat::BC1:
+			return nvtt::Format_BC1;
+		case CompressedFormat::BC1a:
+			return nvtt::Format_BC1a;
+		case CompressedFormat::BC2:
+			return nvtt::Format_BC2;
+		case CompressedFormat::BC3:
+			return nvtt::Format_BC3;
+		case CompressedFormat::BC4:
+			return nvtt::Format_BC4;
+		case CompressedFormat::BC5:
+			return nvtt::Format_BC5;
+		}
+
+		// Unsupported format
+		return nvtt::Format_BC3;
+	}
+
+	nvtt::Quality toNVTTQuality(CompressionQuality quality)
+	{
+		switch (quality)
+		{
+		case CompressionQuality::Fastest:
+			return nvtt::Quality_Fastest;
+		case CompressionQuality::Highest:
+			return nvtt::Quality_Highest;
+		case CompressionQuality::Normal:
+			return nvtt::Quality_Normal;
+		case CompressionQuality::Production:
+			return nvtt::Quality_Normal;
+		}
+
+		// Unknown quality level
+		return nvtt::Quality_Normal;
+	}
+
+	nvtt::AlphaMode toNVTTAlphaMode(AlphaMode alphaMode)
+	{
+		switch (alphaMode)
+		{
+		case AlphaMode::None:
+			return nvtt::AlphaMode_None;
+		case AlphaMode::Premultiplied:
+			return nvtt::AlphaMode_Premultiplied;
+		case AlphaMode::Transparency:
+			return nvtt::AlphaMode_Transparency;
+		}
+
+		// Unknown alpha mode
+		return nvtt::AlphaMode_None;
+	}
+
+	nvtt::WrapMode toNVTTWrapMode(MipMapWrapMode wrapMode)
+	{
+		switch (wrapMode)
+		{
+		case MipMapWrapMode::Clamp:
+			return nvtt::WrapMode_Clamp;
+		case MipMapWrapMode::Mirror:
+			return nvtt::WrapMode_Mirror;
+		case MipMapWrapMode::Repeat:
+			return nvtt::WrapMode_Repeat;
+		}
+
+		// Unknown alpha mode
+		return nvtt::WrapMode_Mirror;
+	}
+
+
+	PixelFormat toPixelFormat(CompressedFormat format)
+	{
+		switch (format)
+		{
+		case CompressedFormat::BC1:
+			return PF_BC1;
+		case CompressedFormat::BC1a:
+			return PF_BC1a;
+		case CompressedFormat::BC2:
+			return PF_BC2;
+		case CompressedFormat::BC3:
+			return PF_BC3;
+		case CompressedFormat::BC4:
+			return PF_BC4;
+		case CompressedFormat::BC5:
+			return PF_BC5;
+		case CompressedFormat::BC6H:
+			return PF_BC6H;
+		case CompressedFormat::BC7:
+			return PF_BC7;
+		}
+
+		// Unknown format
+		return PF_BC3;
+	}
+
     UINT32 PixelUtil::getNumElemBytes(PixelFormat format)
     {
         return getDescriptionFor(format).elemBytes;
@@ -818,10 +981,10 @@ namespace BansheeEngine
 				// BC formats work by dividing the image into 4x4 blocks, then encoding each
 				// 4x4 block with a certain number of bytes. 
 				case PF_BC1:
+				case PF_BC1a:
 				case PF_BC4:
 					return ((width+3)/4)*((height+3)/4)*8 * depth;
 				case PF_BC2:
-				case PF_BC2a:
 				case PF_BC3:
 				case PF_BC5:
 				case PF_BC6H:
@@ -881,7 +1044,7 @@ namespace BansheeEngine
 			{
 				case PF_BC1:
 				case PF_BC2:
-				case PF_BC2a:
+				case PF_BC1a:
 				case PF_BC3:
 				case PF_BC4:
 				case PF_BC5:
@@ -1458,4 +1621,133 @@ namespace BansheeEngine
 			buffer[2] = (UINT8)b;
 		}
 	}
+
+	PixelDataPtr PixelUtil::compress(const PixelData& src, const CompressionOptions& options)
+	{
+		// Note: NVTT site has implementations for these two formats for when I decide to add them
+		if (options.format == CompressedFormat::BC6H || options.format == CompressedFormat::BC7)
+			BS_EXCEPT(InvalidParametersException, "Specified formats are not yet supported.");
+
+		if (src.getDepth() != 1)
+			BS_EXCEPT(InvalidParametersException, "3D textures are not supported.");
+
+		PixelFormat pf = toPixelFormat(options.format);
+
+		// TODO - Get rid of this limitation? Maybe it works without it with no additional changes.
+		if (!isValidExtent(src.getWidth(), src.getHeight(), 1, pf))
+			BS_EXCEPT(InvalidParametersException, "Source texture dimensions must be divisible by 4.");
+
+		if (isCompressed(src.getFormat()))
+			BS_EXCEPT(InvalidParametersException, "Source data cannot be compressed.");
+
+		PixelData argbData(src.getWidth(), src.getHeight(), 1, PF_A8R8G8B8);
+		argbData.allocateInternalBuffer();
+		bulkPixelConversion(src, argbData);
+
+		nvtt::InputOptions io;
+		io.setTextureLayout(nvtt::TextureType_2D, src.getWidth(), src.getHeight());
+		io.setMipmapData(argbData.getData(), src.getWidth(), src.getHeight());
+		io.setMipmapGeneration(false);
+		io.setAlphaMode(toNVTTAlphaMode(options.alphaMode));
+		io.setNormalMap(options.isNormalMap);
+
+		if (options.isSRGB)
+			io.setGamma(2.2f, 2.2f);
+		else
+			io.setGamma(1.0f, 1.0f);
+
+		nvtt::CompressionOptions co;
+		co.setFormat(toNVTTFormat(options.format));
+		co.setQuality(toNVTTQuality(options.quality));
+		
+		PixelDataPtr outputPixelData = bs_shared_ptr<PixelData>(src.getWidth(), src.getHeight(), 1, pf);
+		outputPixelData->allocateInternalBuffer();
+
+		NVTTCompressOutputHandler outputHandler(outputPixelData->getData(), outputPixelData->getConsecutiveSize());
+
+		nvtt::OutputOptions oo;
+		oo.setOutputHeader(false);
+		oo.setOutputHandler(&outputHandler);
+		
+		nvtt::Compressor compressor;
+		if (!compressor.process(io, co, oo))
+			BS_EXCEPT(InternalErrorException, "Compressing failed.");
+
+		return outputPixelData;
+	}
+
+	Vector<PixelDataPtr> PixelUtil::genMipmaps(const PixelData& src, const MipMapGenOptions& options)
+	{
+		if (src.getDepth() != 1)
+			BS_EXCEPT(InvalidParametersException, "3D textures are not supported.");
+
+		// Note: Add support for floating point mips, no reason they shouldn't be supported other than
+		// nvtt doesn't support them natively
+		if (isCompressed(src.getFormat()) || isFloatingPoint(src.getFormat()))
+			BS_EXCEPT(InvalidParametersException, "Source data cannot be compressed or in floating point format.");
+
+		if (!Math::isPow2(src.getWidth()) || !Math::isPow2(src.getHeight()))
+			BS_EXCEPT(InvalidParametersException, "Texture width & height must be powers of 2.");
+
+		PixelData argbData(src.getWidth(), src.getHeight(), 1, PF_A8R8G8B8);
+		argbData.allocateInternalBuffer();
+		bulkPixelConversion(src, argbData);
+
+		nvtt::InputOptions io;
+		io.setTextureLayout(nvtt::TextureType_2D, src.getWidth(), src.getHeight());
+		io.setMipmapData(argbData.getData(), src.getWidth(), src.getHeight());
+		io.setMipmapGeneration(true);
+		io.setNormalMap(options.isNormalMap);
+		io.setNormalizeMipmaps(options.normalizeMipmaps);
+		io.setWrapMode(toNVTTWrapMode(options.wrapMode));
+
+		nvtt::CompressionOptions co;
+		co.setFormat(nvtt::Format_RGBA);
+
+		UINT32 numMips = getMaxMipmaps(src.getWidth(), src.getHeight(), 1, src.getFormat());
+
+		Vector<PixelDataPtr> argbMipBuffers;
+
+		// Note: This can be done more effectively without creating so many temp buffers
+		// and working with the original formats directly, but it would complicate the code
+		// too much at the moment.
+		UINT32 curWidth = src.getWidth();
+		UINT32 curHeight = src.getHeight();
+		for (UINT32 i = 0; i < numMips; i++)
+		{
+			if (curWidth > 1) 
+				curWidth = curWidth / 2;
+
+			if (curHeight > 1)
+				curHeight = curHeight / 2;
+
+			argbMipBuffers.push_back(bs_shared_ptr<PixelData>(curWidth, curHeight, 1, PF_A8R8G8B8));
+			argbMipBuffers.back()->allocateInternalBuffer();
+		}
+
+		NVTTMipmapOutputHandler outputHandler(argbMipBuffers);
+
+		nvtt::OutputOptions oo;
+		oo.setOutputHeader(false);
+		oo.setOutputHandler(&outputHandler);
+
+		nvtt::Compressor compressor;
+		if (!compressor.process(io, co, oo))
+			BS_EXCEPT(InternalErrorException, "Mipmap generation failed.");
+
+		argbData.freeInternalBuffer();
+
+		Vector<PixelDataPtr> outputMipBuffers;
+		for (UINT32 i = 0; i < (UINT32)argbMipBuffers.size(); i++)
+		{
+			PixelDataPtr argbBuffer = argbMipBuffers[i];
+			PixelDataPtr outputBuffer = bs_shared_ptr<PixelData>(argbBuffer->getWidth(), argbBuffer->getHeight(), 1, src.getFormat());
+			outputBuffer->allocateInternalBuffer();
+
+			bulkPixelConversion(*argbBuffer, *outputBuffer);
+			argbBuffer->freeInternalBuffer();
+		}
+
+		return outputMipBuffers;
+	}
 }

+ 1 - 2
BansheeD3D11RenderSystem/Source/BsD3D11Mappings.cpp

@@ -665,11 +665,10 @@ namespace BansheeEngine
 		case PF_FLOAT32_RGBA:
 			return DXGI_FORMAT_R32G32B32A32_FLOAT;
 		case PF_BC1:
+		case PF_BC1a:
 			return DXGI_FORMAT_BC1_UNORM;
 		case PF_BC2:
 			return DXGI_FORMAT_BC2_UNORM;
-		case PF_BC2a:
-			return DXGI_FORMAT_BC2_UNORM;
 		case PF_BC3:
 			return DXGI_FORMAT_BC3_UNORM;
 		case PF_BC4:

+ 3 - 3
BansheeD3D9RenderSystem/Include/BsD3D9HLSLParamParser.h

@@ -127,9 +127,9 @@ namespace BansheeEngine
 			// Recursively descend through the structure levels
 			auto findIter = nonGlobalBlocks.find(paramName);
 			if (findIter == nonGlobalBlocks.end())
-				processParameter(globalBlockDesc, paramName, constantHandle, "", i);
+				processParameter(globalBlockDesc, paramName, constantHandle, "");
 			else
-				processParameter(mParamDesc->paramBlocks[findIter->second], paramName, constantHandle, "", i);
+				processParameter(mParamDesc->paramBlocks[findIter->second], paramName, constantHandle, "");
 		}
 
 		return mParamDesc;
@@ -158,7 +158,7 @@ namespace BansheeEngine
 				D3DXHANDLE childHandle = mpConstTable->GetConstant(constant, i);
 				String childParamName = getParamName(childHandle);
 
-				processParameter(blockDesc, childParamName, childHandle, prefix, i);
+				processParameter(blockDesc, childParamName, childHandle, prefix);
 			}
 		}
 		else

+ 4 - 16
BansheeD3D9RenderSystem/Source/BsD3D9GpuProgram.cpp

@@ -287,10 +287,7 @@ namespace BansheeEngine
 
 	void D3D9GpuVertexProgram::notifyOnDeviceDestroy(IDirect3DDevice9* d3d9Device)
 	{
-		auto it;
-
-		// Find the shader of this device.
-		it = mMapDeviceToVertexShader.find(d3d9Device);
+		auto it = mMapDeviceToVertexShader.find(d3d9Device);
 
 		// Case shader found -> release it and erase from map.
 		if (it != mMapDeviceToVertexShader.end())
@@ -306,11 +303,8 @@ namespace BansheeEngine
 			return nullptr;
 
 		IDirect3DDevice9* d3d9Device = D3D9RenderSystem::getActiveD3D9Device();
-		auto it;
+		auto it = mMapDeviceToVertexShader.find(d3d9Device);
 
-		// Find the shader of this device.
-		it = mMapDeviceToVertexShader.find(d3d9Device);
-		
 		// Shader was not found -> load it.
 		if (it == mMapDeviceToVertexShader.end())		
 		{
@@ -385,10 +379,7 @@ namespace BansheeEngine
 
 	void D3D9GpuFragmentProgram::notifyOnDeviceDestroy(IDirect3DDevice9* d3d9Device)
 	{
-		auto it;
-
-		// Find the shader of this device.
-		it = mMapDeviceToPixelShader.find(d3d9Device);
+		auto it = mMapDeviceToPixelShader.find(d3d9Device);
 
 		// Case shader found -> release it and erase from map.
 		if (it != mMapDeviceToPixelShader.end())
@@ -404,10 +395,7 @@ namespace BansheeEngine
 			return nullptr;
 
 		IDirect3DDevice9* d3d9Device = D3D9RenderSystem::getActiveD3D9Device();
-		auto it;
-
-		// Find the shader of this device.
-		it = mMapDeviceToPixelShader.find(d3d9Device);
+		auto it = mMapDeviceToPixelShader.find(d3d9Device);
 
 		// Shader was not found -> load it.
 		if (it == mMapDeviceToPixelShader.end())		

+ 2 - 5
BansheeD3D9RenderSystem/Source/BsD3D9Mappings.cpp

@@ -446,10 +446,8 @@ namespace BansheeEngine
 			return PF_FLOAT16_RG;
 		case D3DFMT_DXT1:
 			return PF_BC1;
-		case D3DFMT_DXT2:
-			return PF_BC2;
 		case D3DFMT_DXT3:
-			return PF_BC2a;
+			return PF_BC2;
 		case D3DFMT_DXT5:
 			return PF_BC3;
 		case D3DFMT_D24S8:
@@ -490,10 +488,9 @@ namespace BansheeEngine
 		case PF_FLOAT32_RGBA:
 			return D3DFMT_A32B32G32R32F;
 		case PF_BC1:
+		case PF_BC1a:
 			return D3DFMT_DXT1;
 		case PF_BC2:
-			return D3DFMT_DXT2;
-		case PF_BC2a:
 			return D3DFMT_DXT3;
 		case PF_BC3:
 			return D3DFMT_DXT5;

+ 5 - 4
BansheeGLRenderSystem/Source/BsGLPixelFormat.cpp

@@ -44,7 +44,7 @@ namespace BansheeEngine
             case PF_FLOAT32_RGBA:
                 return GL_RGBA;
             case PF_BC1:
-            case PF_BC2a:
+            case PF_BC1a:
             case PF_BC3:
 			case PF_BC7:
 				return GL_RGBA;
@@ -159,12 +159,13 @@ namespace BansheeEngine
                 return GL_RGB32F;
             case PF_FLOAT32_RGBA:
                 return GL_RGBA32F;
+			case PF_BC1a:
 			case PF_BC1:
 				if (hwGamma)
 					return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
 				else
 					return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
-            case PF_BC2a:
+            case PF_BC2:
 				if (hwGamma)
 					return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
 				else
@@ -329,8 +330,8 @@ namespace BansheeEngine
 			return PF_FLOAT32_RGBA;
 		case PF_BC1:
 			return PF_BC1;
-		case PF_BC2a:
-			return PF_BC2a;
+		case PF_BC1a:
+			return PF_BC1;
 		case PF_BC3:
 			return PF_BC3;
 		case PF_BC4:

+ 9 - 0
BansheeUtility/Include/BsMath.h

@@ -76,6 +76,15 @@ namespace BansheeEngine
 			return std::max(std::min(val, (T)1), (T)0);
 		}
 
+		/**
+		 * @brief	Checks is the specified value a power of two. Only works on integer values.
+		 */
+		template <typename T>
+		static bool isPow2(T val)
+		{
+			return (val & (val - 1)) == 0;
+		}
+
 		static bool isNaN(float f)
 		{
 			return f != f;

+ 1 - 0
Notes.txt

@@ -61,6 +61,7 @@ Reminders:
 	- When displaying an error with a callstack, make each line of the callstack clickable where it opens the external editor
   - std::function allocates memory but I have no got way of using custom allocators as I'd have to wrap std::bind and that seems non-trivial
   - Add a TaskScheduler profiler that neatly shows time slices of each task and on which thread they are run on
+  - Add support for BC6H and BC7 file format compression
 
 Potential optimizations:
  - bulkPixelConversion is EXTREMELY poorly unoptimized. Each pixel it calls a separate method that does redudant operations every pixel.

+ 2 - 3
Renderer.txt

@@ -23,10 +23,9 @@ Just notes for later potentially:
  -------------------------
 
  Texture compression/mipmaps:
+  - Test it out. Especially pontential problems with textures not divisible by 4 for compression, or non-pow2 textures for mipmaps
 
-Remove DXT2, DXT4 formats. Add BC4, 5, 6, 7 formats, including their conversion formats.
-Compile nvtt as a library. Add include files. Add library to Dependencies repo, and info about it to Dependencies.txt. Add it to list of licences.
-Add compress(format) functionality to PixelUtil. Optionally also uncompress(). 
+Add compress(format) functionality to PixelUtil.
 Add calculateMips functionality to PixelUtil. It takes PixelData as parameter, and returns a set of PixelData mips.
 Add Texture import options that allow you to choose format of the texture when loading. Not just compressed/uncompressed but all formats.
  - PixelUtil can convert all normal formats. compress can deal with others.