2014-06-16 21:18:15 +02:00
|
|
|
#include "graphics/gl_ext.h"
|
|
|
|
#include "core/fs/file_system.h"
|
|
|
|
#include "core/fs/ifile.h"
|
|
|
|
#include "core/log.h"
|
2014-08-06 21:37:03 +02:00
|
|
|
#include "core/path_utils.h"
|
2014-07-24 23:05:17 +02:00
|
|
|
#include "core/profiler.h"
|
2014-06-16 21:18:15 +02:00
|
|
|
#include "core/resource_manager.h"
|
|
|
|
#include "core/resource_manager_base.h"
|
|
|
|
#include "graphics/texture.h"
|
|
|
|
#include "graphics/texture_manager.h"
|
|
|
|
|
|
|
|
namespace Lumix
|
|
|
|
{
|
|
|
|
|
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
namespace DDS
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2014-08-06 21:37:03 +02:00
|
|
|
static const uint32_t DDS_MAGIC = 0x20534444; // little-endian
|
|
|
|
static const uint32_t DDSD_CAPS = 0x00000001;
|
|
|
|
static const uint32_t DDSD_HEIGHT = 0x00000002;
|
|
|
|
static const uint32_t DDSD_WIDTH = 0x00000004;
|
|
|
|
static const uint32_t DDSD_PITCH = 0x00000008;
|
|
|
|
static const uint32_t DDSD_PIXELFORMAT = 0x00001000;
|
|
|
|
static const uint32_t DDSD_MIPMAPCOUNT = 0x00020000;
|
|
|
|
static const uint32_t DDSD_LINEARSIZE = 0x00080000;
|
|
|
|
static const uint32_t DDSD_DEPTH = 0x00800000;
|
|
|
|
static const uint32_t DDPF_ALPHAPIXELS = 0x00000001;
|
|
|
|
static const uint32_t DDPF_FOURCC = 0x00000004;
|
|
|
|
static const uint32_t DDPF_INDEXED = 0x00000020;
|
|
|
|
static const uint32_t DDPF_RGB = 0x00000040;
|
|
|
|
static const uint32_t DDSCAPS_COMPLEX = 0x00000008;
|
|
|
|
static const uint32_t DDSCAPS_TEXTURE = 0x00001000;
|
|
|
|
static const uint32_t DDSCAPS_MIPMAP = 0x00400000;
|
|
|
|
static const uint32_t DDSCAPS2_CUBEMAP = 0x00000200;
|
|
|
|
static const uint32_t DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400;
|
|
|
|
static const uint32_t DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800;
|
|
|
|
static const uint32_t DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000;
|
|
|
|
static const uint32_t DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000;
|
|
|
|
static const uint32_t DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000;
|
|
|
|
static const uint32_t DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000;
|
|
|
|
static const uint32_t DDSCAPS2_VOLUME = 0x00200000;
|
|
|
|
static const uint32_t D3DFMT_DXT1 = '1TXD';
|
|
|
|
static const uint32_t D3DFMT_DXT2 = '2TXD';
|
|
|
|
static const uint32_t D3DFMT_DXT3 = '3TXD';
|
|
|
|
static const uint32_t D3DFMT_DXT4 = '4TXD';
|
|
|
|
static const uint32_t D3DFMT_DXT5 = '5TXD';
|
|
|
|
|
|
|
|
struct PixelFormat {
|
|
|
|
uint32_t dwSize;
|
|
|
|
uint32_t dwFlags;
|
|
|
|
uint32_t dwFourCC;
|
|
|
|
uint32_t dwRGBBitCount;
|
|
|
|
uint32_t dwRBitMask;
|
|
|
|
uint32_t dwGBitMask;
|
|
|
|
uint32_t dwBBitMask;
|
|
|
|
uint32_t dwAlphaBitMask;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Caps2 {
|
|
|
|
uint32_t dwCaps1;
|
|
|
|
uint32_t dwCaps2;
|
|
|
|
uint32_t dwDDSX;
|
|
|
|
uint32_t dwReserved;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Header {
|
|
|
|
uint32_t dwMagic;
|
|
|
|
uint32_t dwSize;
|
|
|
|
uint32_t dwFlags;
|
|
|
|
uint32_t dwHeight;
|
|
|
|
uint32_t dwWidth;
|
|
|
|
uint32_t dwPitchOrLinearSize;
|
|
|
|
uint32_t dwDepth;
|
|
|
|
uint32_t dwMipMapCount;
|
|
|
|
uint32_t dwReserved1[11];
|
|
|
|
|
|
|
|
PixelFormat pixelFormat;
|
|
|
|
Caps2 caps2;
|
|
|
|
|
|
|
|
uint32_t dwReserved2;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct LoadInfo {
|
|
|
|
bool compressed;
|
|
|
|
bool swap;
|
|
|
|
bool palette;
|
|
|
|
uint32_t divSize;
|
|
|
|
uint32_t blockBytes;
|
|
|
|
GLenum internalFormat;
|
|
|
|
GLenum externalFormat;
|
|
|
|
GLenum type;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool isDXT1(PixelFormat& pf)
|
|
|
|
{
|
|
|
|
return ((pf.dwFlags & DDPF_FOURCC) && (pf.dwFourCC == D3DFMT_DXT1));
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static bool isDXT3(PixelFormat& pf)
|
|
|
|
{
|
|
|
|
return ((pf.dwFlags & DDPF_FOURCC) && (pf.dwFourCC == D3DFMT_DXT3));
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static bool isDXT5(PixelFormat& pf)
|
|
|
|
{
|
|
|
|
return ((pf.dwFlags & DDPF_FOURCC) && (pf.dwFourCC == D3DFMT_DXT5));
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static bool isBGRA8(PixelFormat& pf)
|
|
|
|
{
|
|
|
|
return ((pf.dwFlags & DDPF_RGB)
|
|
|
|
&& (pf.dwFlags & DDPF_ALPHAPIXELS)
|
|
|
|
&& (pf.dwRGBBitCount == 32)
|
|
|
|
&& (pf.dwRBitMask == 0xff0000)
|
|
|
|
&& (pf.dwGBitMask == 0xff00)
|
|
|
|
&& (pf.dwBBitMask == 0xff)
|
|
|
|
&& (pf.dwAlphaBitMask == 0xff000000U));
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static bool isBGR8(PixelFormat& pf)
|
|
|
|
{
|
|
|
|
return ((pf.dwFlags & DDPF_ALPHAPIXELS)
|
|
|
|
&& !(pf.dwFlags & DDPF_ALPHAPIXELS)
|
|
|
|
&& (pf.dwRGBBitCount == 24)
|
|
|
|
&& (pf.dwRBitMask == 0xff0000)
|
|
|
|
&& (pf.dwGBitMask == 0xff00)
|
|
|
|
&& (pf.dwBBitMask == 0xff));
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static bool isBGR5A1(PixelFormat& pf)
|
|
|
|
{
|
|
|
|
return ((pf.dwFlags & DDPF_RGB)
|
|
|
|
&& (pf.dwFlags & DDPF_ALPHAPIXELS)
|
|
|
|
&& (pf.dwRGBBitCount == 16)
|
|
|
|
&& (pf.dwRBitMask == 0x00007c00)
|
|
|
|
&& (pf.dwGBitMask == 0x000003e0)
|
|
|
|
&& (pf.dwBBitMask == 0x0000001f)
|
|
|
|
&& (pf.dwAlphaBitMask == 0x00008000));
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static bool isBGR565(PixelFormat& pf)
|
|
|
|
{
|
|
|
|
return ((pf.dwFlags & DDPF_RGB)
|
|
|
|
&& !(pf.dwFlags & DDPF_ALPHAPIXELS)
|
|
|
|
&& (pf.dwRGBBitCount == 16)
|
|
|
|
&& (pf.dwRBitMask == 0x0000f800)
|
|
|
|
&& (pf.dwGBitMask == 0x000007e0)
|
|
|
|
&& (pf.dwBBitMask == 0x0000001f));
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static bool isINDEX8(PixelFormat& pf)
|
|
|
|
{
|
|
|
|
return ((pf.dwFlags & DDPF_INDEXED) && (pf.dwRGBBitCount == 8));
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static LoadInfo loadInfoDXT1 = {
|
|
|
|
true, false, false, 4, 8, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
|
|
|
|
};
|
|
|
|
static LoadInfo loadInfoDXT3 = {
|
|
|
|
true, false, false, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
|
|
|
|
};
|
|
|
|
static LoadInfo loadInfoDXT5 = {
|
|
|
|
true, false, false, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
|
|
|
|
};
|
|
|
|
static LoadInfo loadInfoBGRA8 = {
|
|
|
|
false, false, false, 1, 4, GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE
|
|
|
|
};
|
|
|
|
static LoadInfo loadInfoBGR8 = {
|
|
|
|
false, false, false, 1, 3, GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE
|
|
|
|
};
|
|
|
|
static LoadInfo loadInfoBGR5A1 = {
|
|
|
|
false, true, false, 1, 2, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV
|
|
|
|
};
|
|
|
|
static LoadInfo loadInfoBGR565 = {
|
|
|
|
false, true, false, 1, 2, GL_RGB5, GL_RGB, GL_UNSIGNED_SHORT_5_6_5
|
|
|
|
};
|
|
|
|
static LoadInfo loadInfoIndex8 = {
|
|
|
|
false, false, true, 1, 1, GL_RGB8, GL_BGRA, GL_UNSIGNED_BYTE
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DXTColBlock
|
|
|
|
{
|
|
|
|
uint16_t col0;
|
|
|
|
uint16_t col1;
|
|
|
|
uint8_t row[4];
|
|
|
|
};
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
struct DXT3AlphaBlock
|
|
|
|
{
|
|
|
|
uint16_t row[4];
|
|
|
|
};
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
struct DXT5AlphaBlock
|
|
|
|
{
|
|
|
|
uint8_t alpha0;
|
|
|
|
uint8_t alpha1;
|
|
|
|
uint8_t row[6];
|
|
|
|
};
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-11-09 14:32:37 +01:00
|
|
|
static LUMIX_FORCE_INLINE void swapMemory(void* mem1, void* mem2, size_t size, IAllocator& allocator)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2014-08-06 21:37:03 +02:00
|
|
|
if (size <= 32768)
|
|
|
|
{
|
|
|
|
uint8_t tmp[32768];
|
|
|
|
memcpy(tmp, mem1, size);
|
|
|
|
memcpy(mem1, mem2, size);
|
|
|
|
memcpy(mem2, tmp, size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-11-09 14:32:37 +01:00
|
|
|
uint8_t* tmp = (uint8_t*)allocator.allocate(sizeof(uint8_t) * size);
|
2014-08-06 21:37:03 +02:00
|
|
|
memcpy(tmp, mem1, size);
|
|
|
|
memcpy(mem1, mem2, size);
|
|
|
|
memcpy(mem2, tmp, size);
|
2014-11-09 14:32:37 +01:00
|
|
|
allocator.deallocate(tmp);
|
2014-08-06 21:37:03 +02:00
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
2014-08-06 21:37:03 +02:00
|
|
|
|
|
|
|
static LUMIX_FORCE_INLINE void swapBytes(uint8_t* LUMIX_RESTRICT mem1, uint8_t* LUMIX_RESTRICT mem2)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2014-08-06 21:37:03 +02:00
|
|
|
uint8_t tmp = *mem1;
|
|
|
|
*mem1 = *mem2;
|
|
|
|
*mem2 = tmp;
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
2014-07-24 23:27:01 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static LUMIX_FORCE_INLINE void swapBytes(uint16_t* LUMIX_RESTRICT mem1, uint16_t* LUMIX_RESTRICT mem2)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2014-08-06 21:37:03 +02:00
|
|
|
uint16_t tmp = *mem1;
|
|
|
|
*mem1 = *mem2;
|
|
|
|
*mem2 = tmp;
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static void flipBlockDXTC1(DXTColBlock *line, int numBlocks)
|
|
|
|
{
|
|
|
|
DXTColBlock *curblock = line;
|
|
|
|
|
|
|
|
for (int i = 0; i < numBlocks; i++)
|
|
|
|
{
|
|
|
|
swapBytes(&curblock->row[0], &curblock->row[3]);
|
|
|
|
swapBytes(&curblock->row[1], &curblock->row[2]);
|
|
|
|
++curblock;
|
|
|
|
}
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static void flipBlockDXTC3(DXTColBlock *line, int numBlocks)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2014-08-06 21:37:03 +02:00
|
|
|
DXTColBlock *curblock = line;
|
|
|
|
DXT3AlphaBlock *alphablock;
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
for (int i = 0; i < numBlocks; i++)
|
|
|
|
{
|
2014-10-29 00:00:23 +01:00
|
|
|
alphablock = reinterpret_cast<DXT3AlphaBlock*>(curblock);
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
swapBytes((uint16_t*)&alphablock->row[0], (uint16_t*)&alphablock->row[3]);
|
|
|
|
swapBytes((uint16_t*)&alphablock->row[1], (uint16_t*)&alphablock->row[2]);
|
|
|
|
++curblock;
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
swapBytes(&curblock->row[0], &curblock->row[3]);
|
|
|
|
swapBytes(&curblock->row[1], &curblock->row[2]);
|
|
|
|
++curblock;
|
|
|
|
}
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static void flipDXT5Alpha(DXT5AlphaBlock *block)
|
|
|
|
{
|
|
|
|
uint8_t tmp_bits[4][4];
|
|
|
|
|
|
|
|
const uint32_t mask = 0x00000007;
|
|
|
|
uint32_t bits = 0;
|
|
|
|
memcpy(&bits, &block->row[0], sizeof(uint8_t)* 3);
|
|
|
|
|
|
|
|
tmp_bits[0][0] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[0][1] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[0][2] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[0][3] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[1][0] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[1][1] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[1][2] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[1][3] = (uint8_t)(bits & mask);
|
|
|
|
|
|
|
|
bits = 0;
|
|
|
|
memcpy(&bits, &block->row[3], sizeof(uint8_t)* 3);
|
|
|
|
|
|
|
|
tmp_bits[2][0] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[2][1] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[2][2] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[2][3] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[3][0] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[3][1] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[3][2] = (uint8_t)(bits & mask);
|
|
|
|
bits >>= 3;
|
|
|
|
tmp_bits[3][3] = (uint8_t)(bits & mask);
|
|
|
|
|
|
|
|
uint32_t *out_bits = (uint32_t*)&block->row[0];
|
|
|
|
|
|
|
|
*out_bits = *out_bits | (tmp_bits[3][0] << 0);
|
|
|
|
*out_bits = *out_bits | (tmp_bits[3][1] << 3);
|
|
|
|
*out_bits = *out_bits | (tmp_bits[3][2] << 6);
|
|
|
|
*out_bits = *out_bits | (tmp_bits[3][3] << 9);
|
|
|
|
|
|
|
|
*out_bits = *out_bits | (tmp_bits[2][0] << 12);
|
|
|
|
*out_bits = *out_bits | (tmp_bits[2][1] << 15);
|
|
|
|
*out_bits = *out_bits | (tmp_bits[2][2] << 18);
|
|
|
|
*out_bits = *out_bits | (tmp_bits[2][3] << 21);
|
|
|
|
|
|
|
|
out_bits = (uint32_t*)&block->row[3];
|
|
|
|
|
|
|
|
*out_bits &= 0xff000000;
|
|
|
|
|
|
|
|
*out_bits = *out_bits | (tmp_bits[1][0] << 0);
|
|
|
|
*out_bits = *out_bits | (tmp_bits[1][1] << 3);
|
|
|
|
*out_bits = *out_bits | (tmp_bits[1][2] << 6);
|
|
|
|
*out_bits = *out_bits | (tmp_bits[1][3] << 9);
|
|
|
|
|
|
|
|
*out_bits = *out_bits | (tmp_bits[0][0] << 12);
|
|
|
|
*out_bits = *out_bits | (tmp_bits[0][1] << 15);
|
|
|
|
*out_bits = *out_bits | (tmp_bits[0][2] << 18);
|
|
|
|
*out_bits = *out_bits | (tmp_bits[0][3] << 21);
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
static void flipBlockDXTC5(DXTColBlock *line, int numBlocks)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2014-10-14 01:58:13 +02:00
|
|
|
DXTColBlock* curblock = line;
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
for (int i = 0; i < numBlocks; i++)
|
|
|
|
{
|
2014-10-29 00:00:23 +01:00
|
|
|
DXT5AlphaBlock* alphablock = reinterpret_cast<DXT5AlphaBlock*>(curblock);
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
flipDXT5Alpha(alphablock);
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
++curblock;
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
swapBytes(&curblock->row[0], &curblock->row[3]);
|
|
|
|
swapBytes(&curblock->row[1], &curblock->row[2]);
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
++curblock;
|
|
|
|
}
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
/// from gpu gems
|
2014-11-09 14:32:37 +01:00
|
|
|
static void flipCompressedTexture(int w, int h, int format, void* surface, IAllocator& allocator)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2014-08-06 21:37:03 +02:00
|
|
|
PROFILE_FUNCTION();
|
|
|
|
void(*flipBlocksFunction)(DXTColBlock*, int);
|
|
|
|
int xblocks = w >> 2;
|
|
|
|
int yblocks = h >> 2;
|
|
|
|
int blocksize;
|
|
|
|
|
|
|
|
switch (format)
|
|
|
|
{
|
2014-06-16 21:18:15 +02:00
|
|
|
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
|
|
|
blocksize = 8;
|
|
|
|
flipBlocksFunction = &flipBlockDXTC1;
|
|
|
|
break;
|
|
|
|
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
|
|
|
blocksize = 16;
|
|
|
|
flipBlocksFunction = &flipBlockDXTC3;
|
|
|
|
break;
|
|
|
|
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
|
|
|
blocksize = 16;
|
|
|
|
flipBlocksFunction = &flipBlockDXTC5;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(false);
|
|
|
|
return;
|
2014-08-06 21:37:03 +02:00
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
int linesize = xblocks * blocksize;
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-10-29 00:00:23 +01:00
|
|
|
DXTColBlock * LUMIX_RESTRICT top = static_cast<DXTColBlock*>(surface);
|
|
|
|
DXTColBlock * LUMIX_RESTRICT bottom = reinterpret_cast<DXTColBlock*>((uint8_t*)surface + ((yblocks - 1) * linesize));
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
while (top < bottom)
|
|
|
|
{
|
|
|
|
(*flipBlocksFunction)(top, xblocks);
|
|
|
|
(*flipBlocksFunction)(bottom, xblocks);
|
2014-11-09 14:32:37 +01:00
|
|
|
swapMemory(bottom, top, linesize, allocator);
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
top = (DXTColBlock*)((uint8_t*)top + linesize);
|
|
|
|
bottom = (DXTColBlock*)((uint8_t*)bottom - linesize);
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma pack(1)
|
2014-08-06 21:37:03 +02:00
|
|
|
struct TGAHeader
|
|
|
|
{
|
|
|
|
char idLength;
|
|
|
|
char colourMapType;
|
|
|
|
char dataType;
|
|
|
|
short int colourMapOrigin;
|
|
|
|
short int colourMapLength;
|
|
|
|
char colourMapDepth;
|
|
|
|
short int xOrigin;
|
|
|
|
short int yOrigin;
|
|
|
|
short int width;
|
|
|
|
short int height;
|
|
|
|
char bitsPerPixel;
|
|
|
|
char imageDescriptor;
|
|
|
|
};
|
2014-06-16 21:18:15 +02:00
|
|
|
#pragma pack()
|
|
|
|
|
|
|
|
|
2014-11-09 14:32:37 +01:00
|
|
|
Texture::Texture(const Path& path, ResourceManager& resource_manager, IAllocator& allocator)
|
2014-11-09 14:58:40 +01:00
|
|
|
: Resource(path, resource_manager, allocator)
|
2014-08-06 21:37:03 +02:00
|
|
|
, m_data_reference(0)
|
|
|
|
, m_is_cubemap(false)
|
2014-11-09 14:32:37 +01:00
|
|
|
, m_allocator(allocator)
|
2014-11-09 17:27:45 +01:00
|
|
|
, m_data(m_allocator)
|
2014-12-12 00:48:06 +01:00
|
|
|
, m_BPP(-1)
|
2014-08-06 21:37:03 +02:00
|
|
|
{
|
|
|
|
glGenTextures(1, &m_id);
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
Texture::~Texture()
|
|
|
|
{
|
|
|
|
ASSERT(isEmpty());
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
bool Texture::create(int w, int h)
|
|
|
|
{
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_id);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Texture::apply(int unit)
|
|
|
|
{
|
|
|
|
glActiveTexture(GL_TEXTURE0 + unit);
|
|
|
|
glBindTexture(m_is_cubemap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, m_id);
|
|
|
|
}
|
|
|
|
|
2014-09-05 22:54:19 +02:00
|
|
|
|
2014-09-05 23:18:29 +02:00
|
|
|
uint32_t Texture::getPixel(float x, float y) const
|
2014-09-05 21:58:21 +02:00
|
|
|
{
|
|
|
|
if(m_data.empty() || x >= m_width || y >= m_height || x < 0 || y < 0)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2014-09-05 22:54:19 +02:00
|
|
|
|
|
|
|
// http://fastcpp.blogspot.sk/2011/06/bilinear-pixel-interpolation-using-sse.html
|
|
|
|
int px = (int)x;
|
|
|
|
int py = (int)y;
|
|
|
|
const uint32_t* p0 = (uint32_t*)&m_data[(px + py * m_width) * 4];
|
|
|
|
|
|
|
|
const uint8_t* p1 = (uint8_t*)p0;
|
|
|
|
const uint8_t* p2 = (uint8_t*)(p0 + 1);
|
|
|
|
const uint8_t* p3 = (uint8_t*)(p0 + m_width);
|
|
|
|
const uint8_t* p4 = (uint8_t*)(p0 + 1 + m_width);
|
|
|
|
|
|
|
|
float fx = x - px;
|
|
|
|
float fy = y - py;
|
|
|
|
float fx1 = 1.0f - fx;
|
|
|
|
float fy1 = 1.0f - fy;
|
|
|
|
|
|
|
|
int w1 = (int)(fx1 * fy1 * 256.0f);
|
|
|
|
int w2 = (int)(fx * fy1 * 256.0f);
|
|
|
|
int w3 = (int)(fx1 * fy * 256.0f);
|
|
|
|
int w4 = (int)(fx * fy * 256.0f);
|
|
|
|
|
|
|
|
uint8_t res[4];
|
2014-09-09 20:55:09 +02:00
|
|
|
res[0] = (uint8_t)((p1[0] * w1 + p2[0] * w2 + p3[0] * w3 + p4[0] * w4) >> 8);
|
|
|
|
res[1] = (uint8_t)((p1[1] * w1 + p2[1] * w2 + p3[1] * w3 + p4[1] * w4) >> 8);
|
|
|
|
res[2] = (uint8_t)((p1[2] * w1 + p2[2] * w2 + p3[2] * w3 + p4[2] * w4) >> 8);
|
|
|
|
res[3] = (uint8_t)((p1[3] * w1 + p2[3] * w2 + p3[3] * w3 + p4[3] * w4) >> 8);
|
2014-09-05 22:54:19 +02:00
|
|
|
|
|
|
|
return *(uint32_t*)res;
|
2014-09-05 21:58:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-29 23:58:30 +01:00
|
|
|
unsigned int Texture::compareTGA(IAllocator& allocator, FS::IFile* file1, FS::IFile* file2, int difference)
|
2015-01-19 23:02:55 +01:00
|
|
|
{
|
|
|
|
TGAHeader header1, header2;
|
|
|
|
file1->read(&header1, sizeof(header1));
|
|
|
|
file2->read(&header2, sizeof(header2));
|
|
|
|
|
|
|
|
if (header1.bitsPerPixel != header2.bitsPerPixel
|
|
|
|
|| header1.width != header2.width
|
|
|
|
|| header1.height != header2.height
|
|
|
|
|| header1.dataType != header2.dataType
|
|
|
|
|| header1.imageDescriptor != header2.imageDescriptor
|
|
|
|
)
|
|
|
|
{
|
|
|
|
g_log_error.log("renderer") << "Trying to compare textures with different formats";
|
2015-01-29 23:58:30 +01:00
|
|
|
return 0;
|
2015-01-19 23:02:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int color_mode = header1.bitsPerPixel / 8;
|
|
|
|
if (header1.dataType != 2)
|
|
|
|
{
|
|
|
|
g_log_error.log("renderer") << "Unsupported texture format";
|
2015-01-29 23:58:30 +01:00
|
|
|
return 0;
|
2015-01-19 23:02:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Targa is BGR, swap to RGB, add alpha and flip Y axis
|
2015-01-29 23:58:30 +01:00
|
|
|
int different_pixel_count = 0;
|
|
|
|
size_t pixel_count = header1.width * header1.height;
|
|
|
|
uint8_t* img1 = (uint8_t*)allocator.allocate(pixel_count * color_mode);
|
|
|
|
uint8_t* img2 = (uint8_t*)allocator.allocate(pixel_count * color_mode);
|
2015-01-19 23:02:55 +01:00
|
|
|
|
2015-01-29 23:58:30 +01:00
|
|
|
file1->read(img1, pixel_count * color_mode);
|
|
|
|
file2->read(img2, pixel_count * color_mode);
|
2015-01-19 23:02:55 +01:00
|
|
|
|
2015-01-29 23:58:30 +01:00
|
|
|
for (size_t i = 0; i < pixel_count * color_mode; i += color_mode)
|
2015-01-19 23:02:55 +01:00
|
|
|
{
|
2015-01-29 23:58:30 +01:00
|
|
|
for (int j = 0; j < color_mode; ++j)
|
|
|
|
{
|
|
|
|
if (Math::abs(img1[i + j] - img2[i + j]) > difference)
|
|
|
|
{
|
|
|
|
++different_pixel_count;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-01-19 23:02:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
allocator.deallocate(img1);
|
|
|
|
allocator.deallocate(img2);
|
|
|
|
|
2015-01-29 23:58:30 +01:00
|
|
|
return different_pixel_count;
|
2015-01-19 23:02:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-03 12:13:13 +01:00
|
|
|
bool Texture::saveTGA(IAllocator& allocator, FS::IFile* file, int width, int height, int bytes_per_pixel, const uint8_t* image_dest, const Path& path)
|
2014-08-06 21:37:03 +02:00
|
|
|
{
|
2015-01-03 12:13:13 +01:00
|
|
|
if (bytes_per_pixel != 4)
|
2014-08-06 21:37:03 +02:00
|
|
|
{
|
2015-01-03 12:13:13 +01:00
|
|
|
g_log_error.log("renderer") << "Texture " << path.c_str() << " could not be saved, unsupported TGA format";
|
|
|
|
return false;
|
2014-08-06 21:37:03 +02:00
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
2015-01-03 12:13:13 +01:00
|
|
|
uint8_t* data = (uint8_t*)allocator.allocate(width * height * 4);
|
2014-08-06 21:37:03 +02:00
|
|
|
|
|
|
|
TGAHeader header;
|
|
|
|
memset(&header, 0, sizeof(header));
|
2015-01-03 12:13:13 +01:00
|
|
|
header.bitsPerPixel = (char)(bytes_per_pixel * 8);
|
|
|
|
header.height = (short)height;
|
|
|
|
header.width = (short)width;
|
2014-08-06 21:37:03 +02:00
|
|
|
header.dataType = 2;
|
|
|
|
|
|
|
|
file->write(&header, sizeof(header));
|
|
|
|
|
|
|
|
for (long y = 0; y < header.height; y++)
|
|
|
|
{
|
|
|
|
long read_index = y * header.width * 4;
|
|
|
|
long write_index = ((header.imageDescriptor & 32) != 0) ? read_index : y * header.width * 4;
|
|
|
|
for (long x = 0; x < header.width; x++)
|
|
|
|
{
|
2015-01-03 12:13:13 +01:00
|
|
|
data[write_index + 0] = image_dest[write_index + 2];
|
|
|
|
data[write_index + 1] = image_dest[write_index + 1];
|
|
|
|
data[write_index + 2] = image_dest[write_index + 0];
|
|
|
|
data[write_index + 3] = image_dest[write_index + 3];
|
2014-08-06 21:37:03 +02:00
|
|
|
write_index += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-03 12:13:13 +01:00
|
|
|
file->write(data, width * height * 4);
|
|
|
|
|
|
|
|
allocator.deallocate(data);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Texture::saveTGA()
|
|
|
|
{
|
|
|
|
if (m_data.empty())
|
|
|
|
{
|
|
|
|
g_log_error.log("renderer") << "Texture " << getPath().c_str() << " could not be saved, no data was loaded";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
FS::FileSystem& fs = m_resource_manager.getFileSystem();
|
|
|
|
FS::IFile* file = fs.open("disk", getPath().c_str(), FS::Mode::OPEN_OR_CREATE | FS::Mode::WRITE);
|
|
|
|
|
|
|
|
saveTGA(m_allocator, file, m_width, m_height, m_BPP, &m_data[0], getPath());
|
|
|
|
|
2014-08-06 21:37:03 +02:00
|
|
|
fs.close(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Texture::save()
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2014-08-06 21:37:03 +02:00
|
|
|
char ext[5];
|
|
|
|
ext[0] = 0;
|
|
|
|
PathUtils::getExtension(ext, 5, getPath().c_str());
|
|
|
|
if (strcmp(ext, "raw") == 0 && m_BPP == 2)
|
|
|
|
{
|
|
|
|
FS::FileSystem& fs = m_resource_manager.getFileSystem();
|
|
|
|
FS::IFile* file = fs.open(fs.getDefaultDevice(), getPath().c_str(), FS::Mode::OPEN_OR_CREATE | FS::Mode::WRITE);
|
|
|
|
file->write(&m_data[0], m_data.size() * sizeof(m_data[0]));
|
|
|
|
fs.close(file);
|
|
|
|
}
|
|
|
|
else if (strcmp(ext, "tga") == 0 && m_BPP == 4)
|
|
|
|
{
|
|
|
|
saveTGA();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_log_error.log("renderer") << "Texture " << getPath() << " can not be saved - unsupported format";
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-02 01:56:37 +02:00
|
|
|
void Texture::onDataUpdated()
|
|
|
|
{
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_id);
|
|
|
|
if (m_BPP == 4)
|
|
|
|
{
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &m_data[0]);
|
|
|
|
}
|
|
|
|
else if (m_BPP == 2)
|
|
|
|
{
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, m_width, m_height, 0, GL_RED, GL_UNSIGNED_SHORT, &m_data[0]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-04 00:39:15 +02:00
|
|
|
bool Texture::loadRaw(FS::IFile& file)
|
|
|
|
{
|
2014-07-27 00:27:01 +02:00
|
|
|
PROFILE_FUNCTION();
|
2014-07-04 00:39:15 +02:00
|
|
|
size_t size = file.size();
|
|
|
|
m_BPP = 2;
|
2014-07-09 19:52:10 +02:00
|
|
|
m_width = (int)sqrt(size / m_BPP);
|
|
|
|
m_height = m_width;
|
2014-07-08 22:10:10 +02:00
|
|
|
|
2014-07-17 22:28:45 +02:00
|
|
|
if (m_data_reference)
|
2014-07-08 22:10:10 +02:00
|
|
|
{
|
|
|
|
m_data.resize(size);
|
|
|
|
file.read(&m_data[0], size);
|
|
|
|
}
|
|
|
|
|
2014-07-17 22:28:45 +02:00
|
|
|
glGenTextures(1, &m_id);
|
|
|
|
if (m_id == 0)
|
|
|
|
{
|
|
|
|
return false;
|
2014-07-08 22:10:10 +02:00
|
|
|
}
|
2014-07-17 22:28:45 +02:00
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_id);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, m_width, m_height, 0, GL_RED, GL_UNSIGNED_SHORT, file.getBuffer());
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
|
2014-07-04 00:39:15 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-16 21:18:15 +02:00
|
|
|
bool Texture::loadTGA(FS::IFile& file)
|
|
|
|
{
|
2014-07-27 00:27:01 +02:00
|
|
|
PROFILE_FUNCTION();
|
2014-06-16 21:18:15 +02:00
|
|
|
TGAHeader header;
|
|
|
|
file.read(&header, sizeof(header));
|
|
|
|
|
|
|
|
int color_mode = header.bitsPerPixel / 8;
|
|
|
|
int image_size = header.width * header.height * 4;
|
|
|
|
if (header.dataType != 2)
|
|
|
|
{
|
2014-06-29 11:45:32 +02:00
|
|
|
g_log_error.log("renderer") << "Unsupported texture format " << m_path.c_str();
|
2014-06-16 21:18:15 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (color_mode < 3)
|
|
|
|
{
|
2014-06-29 11:45:32 +02:00
|
|
|
g_log_error.log("renderer") << "Unsupported color mode " << m_path.c_str();
|
2014-06-16 21:18:15 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_width = header.width;
|
|
|
|
m_height = header.height;
|
|
|
|
TextureManager* manager = static_cast<TextureManager*>(getResourceManager().get(ResourceManager::TEXTURE));
|
2014-07-17 22:28:45 +02:00
|
|
|
if (m_data_reference)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
|
|
|
m_data.resize(image_size);
|
|
|
|
}
|
2014-07-17 22:28:45 +02:00
|
|
|
uint8_t* image_dest = m_data_reference ? &m_data[0] : (uint8_t*)manager->getBuffer(image_size);
|
2014-06-16 21:18:15 +02:00
|
|
|
|
|
|
|
// Targa is BGR, swap to RGB, add alpha and flip Y axis
|
|
|
|
for (long y = 0; y < header.height; y++)
|
|
|
|
{
|
|
|
|
long read_index = y * header.width * color_mode;
|
|
|
|
long write_index = ((header.imageDescriptor & 32) != 0) ? read_index : y * header.width * 4;
|
|
|
|
for (long x = 0; x < header.width; x++)
|
|
|
|
{
|
|
|
|
file.read(&image_dest[write_index + 2], sizeof(uint8_t));
|
|
|
|
file.read(&image_dest[write_index + 1], sizeof(uint8_t));
|
|
|
|
file.read(&image_dest[write_index + 0], sizeof(uint8_t));
|
|
|
|
if (color_mode == 4)
|
|
|
|
file.read(&image_dest[write_index + 3], sizeof(uint8_t));
|
|
|
|
else
|
|
|
|
image_dest[write_index + 3] = 255;
|
|
|
|
write_index += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_BPP = 4;
|
|
|
|
|
|
|
|
glGenTextures(1, &m_id);
|
|
|
|
if (m_id == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_id);
|
|
|
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, header.width, header.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_dest);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
2014-07-20 18:14:37 +02:00
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
2014-06-16 21:18:15 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-17 22:28:45 +02:00
|
|
|
void Texture::addDataReference()
|
2014-07-17 22:08:28 +02:00
|
|
|
{
|
2014-08-09 14:37:23 +02:00
|
|
|
ASSERT(!isReady() || m_data_reference > 0);
|
2014-07-17 22:28:45 +02:00
|
|
|
++m_data_reference;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Texture::removeDataReference()
|
|
|
|
{
|
|
|
|
--m_data_reference;
|
|
|
|
if (m_data_reference == 0)
|
2014-07-17 22:08:28 +02:00
|
|
|
{
|
|
|
|
m_data.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-16 21:18:15 +02:00
|
|
|
bool Texture::loadDDS(FS::IFile& file)
|
|
|
|
{
|
2014-07-24 23:05:17 +02:00
|
|
|
PROFILE_FUNCTION();
|
2014-07-17 22:28:45 +02:00
|
|
|
if (m_data_reference)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2014-07-17 22:08:28 +02:00
|
|
|
g_log_error.log("renderer") << "DDS texture " << m_path.c_str() << " can only be used as renderable texture";
|
2014-06-16 21:18:15 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
DDS::Header hdr;
|
|
|
|
uint32_t width = 0;
|
|
|
|
uint32_t height = 0;
|
|
|
|
uint32_t mipMapCount = 0;
|
|
|
|
|
|
|
|
file.read(&hdr, sizeof(hdr));
|
|
|
|
|
2014-07-24 22:19:04 +02:00
|
|
|
m_is_cubemap = (hdr.caps2.dwCaps2 & DDS::DDSCAPS2_CUBEMAP) != 0;
|
|
|
|
|
2014-06-16 21:18:15 +02:00
|
|
|
if (hdr.dwMagic != DDS::DDS_MAGIC || hdr.dwSize != 124 ||
|
|
|
|
!(hdr.dwFlags & DDS::DDSD_PIXELFORMAT) || !(hdr.dwFlags & DDS::DDSD_CAPS))
|
|
|
|
{
|
2014-06-29 11:45:32 +02:00
|
|
|
g_log_error.log("renderer") << "Wrong dds format or corrupted dds " << m_path.c_str();
|
2014-06-16 21:18:15 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
width = hdr.dwWidth;
|
|
|
|
height = hdr.dwHeight;
|
2014-08-24 15:43:29 +02:00
|
|
|
m_width = width;
|
|
|
|
m_height = height;
|
2014-06-16 21:18:15 +02:00
|
|
|
if ((width & (width - 1)) || (height & (height - 1)))
|
|
|
|
{
|
2014-06-29 11:45:32 +02:00
|
|
|
g_log_error.log("renderer") << "Wrong dds format " << m_path.c_str();
|
2014-06-16 21:18:15 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
DDS::LoadInfo* li;
|
|
|
|
|
|
|
|
if (isDXT1(hdr.pixelFormat))
|
|
|
|
{
|
|
|
|
li = &DDS::loadInfoDXT1;
|
|
|
|
}
|
|
|
|
else if (isDXT3(hdr.pixelFormat))
|
|
|
|
{
|
|
|
|
li = &DDS::loadInfoDXT3;
|
|
|
|
}
|
|
|
|
else if (isDXT5(hdr.pixelFormat))
|
|
|
|
{
|
|
|
|
li = &DDS::loadInfoDXT5;
|
|
|
|
}
|
|
|
|
else if (isBGRA8(hdr.pixelFormat))
|
|
|
|
{
|
|
|
|
li = &DDS::loadInfoBGRA8;
|
|
|
|
}
|
|
|
|
else if (isBGR8(hdr.pixelFormat))
|
|
|
|
{
|
|
|
|
li = &DDS::loadInfoBGR8;
|
|
|
|
}
|
|
|
|
else if (isBGR5A1(hdr.pixelFormat))
|
|
|
|
{
|
|
|
|
li = &DDS::loadInfoBGR5A1;
|
|
|
|
}
|
|
|
|
else if (isBGR565(hdr.pixelFormat))
|
|
|
|
{
|
|
|
|
li = &DDS::loadInfoBGR565;
|
|
|
|
}
|
|
|
|
else if (isINDEX8(hdr.pixelFormat))
|
|
|
|
{
|
|
|
|
li = &DDS::loadInfoIndex8;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-29 11:45:32 +02:00
|
|
|
g_log_error.log("renderer") << "Unsupported DDS format " << m_path.c_str();
|
2014-06-16 21:18:15 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
glGenTextures(1, &m_id);
|
|
|
|
if (m_id == 0)
|
|
|
|
{
|
2014-06-29 11:45:32 +02:00
|
|
|
g_log_error.log("renderer") << "Error generating OpenGL texture " << m_path.c_str();
|
2014-06-16 21:18:15 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TextureManager* manager = static_cast<TextureManager*>(getResourceManager().get(ResourceManager::TEXTURE));
|
2014-07-24 22:19:04 +02:00
|
|
|
if (m_is_cubemap)
|
|
|
|
{
|
|
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, m_id);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_id);
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
|
|
|
|
mipMapCount = (hdr.dwFlags & DDS::DDSD_MIPMAPCOUNT) ? hdr.dwMipMapCount : 1;
|
|
|
|
if (li->compressed)
|
|
|
|
{
|
|
|
|
uint32_t size = Math::max(li->divSize, width) / li->divSize * Math::max(li->divSize, height) / li->divSize * li->blockBytes;
|
|
|
|
uint8_t* data = (uint8_t*)manager->getBuffer(size);
|
|
|
|
ASSERT(data);
|
2014-07-24 22:19:04 +02:00
|
|
|
if (m_is_cubemap)
|
2014-06-16 21:18:15 +02:00
|
|
|
{
|
2014-07-24 22:19:04 +02:00
|
|
|
GLenum sides[] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z };
|
|
|
|
for (int i = 0; i < 6; ++i)
|
|
|
|
{
|
|
|
|
width = hdr.dwWidth;
|
|
|
|
height = hdr.dwHeight;
|
|
|
|
size = Math::max(li->divSize, width) / li->divSize * Math::max(li->divSize, height) / li->divSize * li->blockBytes;
|
|
|
|
for (uint32_t ix = 0; ix < mipMapCount; ++ix)
|
|
|
|
{
|
|
|
|
file.read(data, size);
|
2014-11-09 14:32:37 +01:00
|
|
|
DDS::flipCompressedTexture(width, height, li->internalFormat, data, m_allocator);
|
2014-07-24 22:19:04 +02:00
|
|
|
|
|
|
|
glCompressedTexImage2D(sides[i], ix, li->internalFormat, width, height, 0, size, data);
|
|
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
width = (width + 1) >> 1;
|
|
|
|
height = (height + 1) >> 1;
|
|
|
|
size = Math::max(li->divSize, width) / li->divSize * Math::max(li->divSize, height) / li->divSize * li->blockBytes;
|
|
|
|
}
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
2014-07-24 22:19:04 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
for (uint32_t ix = 0; ix < mipMapCount; ++ix)
|
|
|
|
{
|
|
|
|
file.read(data, size);
|
2014-11-09 14:32:37 +01:00
|
|
|
DDS::flipCompressedTexture(width, height, li->internalFormat, data, m_allocator);
|
2014-07-24 22:19:04 +02:00
|
|
|
glCompressedTexImage2D(GL_TEXTURE_2D, ix, li->internalFormat, width, height, 0, size, data);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
width = (width + 1) >> 1;
|
|
|
|
height = (height + 1) >> 1;
|
|
|
|
size = Math::max(li->divSize, width) / li->divSize * Math::max(li->divSize, height) / li->divSize * li->blockBytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
|
|
|
else if (li->palette)
|
|
|
|
{
|
|
|
|
if ((hdr.dwFlags & DDS::DDSD_PITCH) == 0 || hdr.pixelFormat.dwRGBBitCount != 8)
|
|
|
|
{
|
|
|
|
glDeleteTextures(1, &m_id);
|
2014-06-29 11:45:32 +02:00
|
|
|
g_log_error.log("renderer") << "Unsupported DDS format " << m_path.c_str();
|
2014-06-16 21:18:15 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
uint32_t size = hdr.dwPitchOrLinearSize * height;
|
|
|
|
if (size != width * height * li->blockBytes)
|
|
|
|
{
|
|
|
|
glDeleteTextures(1, &m_id);
|
2014-06-29 11:45:32 +02:00
|
|
|
g_log_error.log("renderer") << "Unsupported DDS format or corrupted DDS " << m_path.c_str();
|
2014-06-16 21:18:15 +02:00
|
|
|
return false;
|
|
|
|
}
|
2014-11-09 14:32:37 +01:00
|
|
|
unsigned char * data = (unsigned char*)m_allocator.allocate(sizeof(unsigned char) * size);
|
2014-06-16 21:18:15 +02:00
|
|
|
uint32_t palette[256];
|
|
|
|
uint32_t* unpacked = (uint32_t*)manager->getBuffer(size * sizeof(uint32_t));
|
|
|
|
file.read(palette, 4 * 256);
|
|
|
|
for (uint32_t ix = 0; ix < mipMapCount; ++ix)
|
|
|
|
{
|
|
|
|
file.read(data, size);
|
|
|
|
for (uint32_t zz = 0; zz < size; ++zz)
|
|
|
|
{
|
|
|
|
unpacked[zz] = palette[data[zz]];
|
|
|
|
}
|
|
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, height);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, ix, li->internalFormat, width, height, 0, li->externalFormat, li->type, unpacked);
|
|
|
|
width = (width + 1) >> 1;
|
|
|
|
height = (height + 1) >> 1;
|
|
|
|
size = width * height * li->blockBytes;
|
|
|
|
}
|
2014-11-09 14:32:37 +01:00
|
|
|
m_allocator.deallocate(data);
|
2014-06-16 21:18:15 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (li->swap)
|
|
|
|
{
|
|
|
|
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE);
|
|
|
|
}
|
|
|
|
uint32_t size = width * height * li->blockBytes;
|
|
|
|
uint8_t* data = (uint8_t*)manager->getBuffer(size);
|
|
|
|
for (uint32_t ix = 0; ix < mipMapCount; ++ix)
|
|
|
|
{
|
|
|
|
file.read(data, size);
|
|
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, height);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, ix, li->internalFormat, width, height, 0, li->externalFormat, li->type, data);
|
|
|
|
width = (width+ 1) >> 1;
|
|
|
|
height = (height + 1) >> 1;
|
|
|
|
size = width * height * li->blockBytes;
|
|
|
|
}
|
|
|
|
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
|
|
|
|
}
|
2014-07-24 22:19:04 +02:00
|
|
|
glTexParameteri(m_is_cubemap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipMapCount - 1);
|
2015-01-23 23:41:12 +01:00
|
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
2014-06-16 21:18:15 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Texture::loaded(FS::IFile* file, bool success, FS::FileSystem& fs)
|
|
|
|
{
|
2014-07-27 00:27:01 +02:00
|
|
|
PROFILE_FUNCTION();
|
2014-06-16 21:18:15 +02:00
|
|
|
ASSERT(file);
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
const char* path = m_path.c_str();
|
|
|
|
size_t len = m_path.length();
|
|
|
|
bool loaded = false;
|
|
|
|
if (len > 3 && strcmp(path + len - 4, ".dds") == 0)
|
|
|
|
{
|
|
|
|
loaded = loadDDS(*file);
|
|
|
|
}
|
2014-07-04 22:14:29 +02:00
|
|
|
else if (len > 3 && strcmp(path + len - 4, ".raw") == 0)
|
2014-07-04 00:39:15 +02:00
|
|
|
{
|
|
|
|
loaded = loadRaw(*file);
|
|
|
|
}
|
2014-06-16 21:18:15 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
loaded = loadTGA(*file);
|
|
|
|
}
|
|
|
|
if(!loaded)
|
|
|
|
{
|
2014-08-23 01:07:48 +02:00
|
|
|
g_log_warning.log("renderer") << "Error loading texture " << m_path.c_str();
|
2014-06-16 21:18:15 +02:00
|
|
|
onFailure();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_size = file->size();
|
|
|
|
decrementDepCount();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-08-23 01:07:48 +02:00
|
|
|
g_log_warning.log("renderer") << "Error loading texture " << m_path.c_str();
|
2014-06-16 21:18:15 +02:00
|
|
|
onFailure();
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.close(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Texture::doUnload(void)
|
|
|
|
{
|
|
|
|
glDeleteTextures(1, &m_id);
|
|
|
|
|
|
|
|
m_size = 0;
|
|
|
|
onEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // ~namespace Lumix
|