Skip to content

Instantly share code, notes, and snippets.

@XProger
Last active August 29, 2015 14:05
Show Gist options
  • Save XProger/43cf20386b18aecebbcf to your computer and use it in GitHub Desktop.
Save XProger/43cf20386b18aecebbcf to your computer and use it in GitHub Desktop.
DDS texture loader (2d, cubemap, OpenGL)
constructor TTexture.Create(const FileName: string);
type
TLoadFormat = (lfNULL, lfDXT1c, lfDXT1a, lfDXT3, lfDXT5, lfA8, lfL8, lfAL8,
lfBGRA8, lfBGR8, lfBGR5A1, lfBGR565, lfBGRA4, lfR16F, lfR32F,
lfGR16F, lfGR32F, lfBGRA16F, lfBGRA32F);
TDDS = record
dwMagic : LongWord;
dwSize : LongInt;
dwFlags : LongWord;
dwHeight : LongWord;
dwWidth : LongWord;
dwPOLSize : LongWord;
dwDepth : LongWord;
dwMipMapCount : LongInt;
SomeData1 : array [0..11] of LongWord;
pfFlags : LongWord;
pfFourCC : LongWord;
pfRGBbpp : LongWord;
pfRMask : LongWord;
pfGMask : LongWord;
pfBMask : LongWord;
pfAMask : LongWord;
dwCaps1 : LongWord;
dwCaps2 : LongWord;
SomeData3 : array [0..2] of LongWord;
end;
const
FOURCC_DXT1 = $31545844;
FOURCC_DXT3 = $33545844;
FOURCC_DXT5 = $35545844;
FOURCC_R16F = $0000006F;
FOURCC_G16R16F = $00000070;
FOURCC_A16B16G16R16F = $00000071;
FOURCC_R32F = $00000072;
FOURCC_G32R32F = $00000073;
FOURCC_A32B32G32R32F = $00000074;
DDPF_ALPHAPIXELS = $01;
DDPF_ALPHA = $02;
DDPF_FOURCC = $04;
DDPF_RGB = $40;
DDPF_LUMINANCE = $020000;
DDSD_MIPMAPCOUNT = $020000;
DDSCAPS2_CUBEMAP = $0200;
LoadFormat : array [TLoadFormat] of record
Compressed : Boolean;
DivSize : Byte;
Bytes : Byte;
IFormat : TGLConst;
EFormat : TGLConst;
DataType : TGLConst;
end = (
(Compressed: False; DivSize: 1; Bytes: 1; IFormat: GL_FALSE; EFormat: GL_FALSE; DataType: GL_FALSE),
(Compressed: True; DivSize: 4; Bytes: 8; IFormat: GL_COMPRESSED_RGB_S3TC_DXT1; EFormat: GL_FALSE; DataType: GL_FALSE),
(Compressed: True; DivSize: 4; Bytes: 8; IFormat: GL_COMPRESSED_RGBA_S3TC_DXT1; EFormat: GL_FALSE; DataType: GL_FALSE),
(Compressed: True; DivSize: 4; Bytes: 16; IFormat: GL_COMPRESSED_RGBA_S3TC_DXT3; EFormat: GL_FALSE; DataType: GL_FALSE),
(Compressed: True; DivSize: 4; Bytes: 16; IFormat: GL_COMPRESSED_RGBA_S3TC_DXT5; EFormat: GL_FALSE; DataType: GL_FALSE),
(Compressed: False; DivSize: 1; Bytes: 1; IFormat: GL_ALPHA8; EFormat: GL_ALPHA; DataType: GL_UNSIGNED_BYTE),
(Compressed: False; DivSize: 1; Bytes: 1; IFormat: GL_LUMINANCE8; EFormat: GL_LUMINANCE; DataType: GL_UNSIGNED_BYTE),
(Compressed: False; DivSize: 1; Bytes: 2; IFormat: GL_LUMINANCE8_ALPHA8; EFormat: GL_LUMINANCE_ALPHA; DataType: GL_UNSIGNED_BYTE),
(Compressed: False; DivSize: 1; Bytes: 4; IFormat: GL_RGBA8; EFormat: GL_BGRA; DataType: GL_UNSIGNED_BYTE),
(Compressed: False; DivSize: 1; Bytes: 3; IFormat: GL_RGB8; EFormat: GL_BGR; DataType: GL_UNSIGNED_BYTE),
(Compressed: False; DivSize: 1; Bytes: 2; IFormat: GL_RGB5_A1; EFormat: GL_BGRA; DataType: GL_UNSIGNED_SHORT_1_5_5_5_REV),
(Compressed: False; DivSize: 1; Bytes: 2; IFormat: GL_RGB5; EFormat: GL_RGB; DataType: GL_UNSIGNED_SHORT_5_6_5),
(Compressed: False; DivSize: 1; Bytes: 2; IFormat: GL_RGBA4; EFormat: GL_BGRA; DataType: GL_UNSIGNED_SHORT_4_4_4_4_REV),
(Compressed: False; DivSize: 1; Bytes: 2; IFormat: GL_R16F; EFormat: GL_RED; DataType: GL_HALF_FLOAT),
(Compressed: False; DivSize: 1; Bytes: 4; IFormat: GL_R32F; EFormat: GL_RED; DataType: GL_FLOAT),
(Compressed: False; DivSize: 1; Bytes: 4; IFormat: GL_RG16F; EFormat: GL_RG; DataType: GL_HALF_FLOAT),
(Compressed: False; DivSize: 1; Bytes: 8; IFormat: GL_RG32F; EFormat: GL_RG; DataType: GL_FLOAT),
(Compressed: False; DivSize: 1; Bytes: 8; IFormat: GL_RGBA16F; EFormat: GL_RGBA; DataType: GL_HALF_FLOAT),
(Compressed: False; DivSize: 1; Bytes: 16; IFormat: GL_RGBA32F; EFormat: GL_RGBA; DataType: GL_FLOAT)
);
var
Stream : TStream;
i, w, h : LongInt;
Data : Pointer;
LF : TLoadFormat;
Samples : LongInt;
st : TGLConst;
s : LongInt;
RMips : LongInt;
DDS : TDDS;
function GetLoadFormat(const DDS: TDDS): TLoadFormat;
begin
Result := lfNULL;
with DDS do
if pfFlags and DDPF_FOURCC = DDPF_FOURCC then
begin
case pfFourCC of
// Compressed
FOURCC_DXT1 :
if pfFlags xor DDPF_ALPHAPIXELS > 0 then
Result := lfDXT1a
else
Result := lfDXT1c;
FOURCC_DXT3 : Result := lfDXT3;
FOURCC_DXT5 : Result := lfDXT5;
// Float
FOURCC_R16F : Result := lfR16F;
FOURCC_G16R16F : Result := lfGR16F;
FOURCC_A16B16G16R16F : Result := lfBGRA16F;
FOURCC_R32F : Result := lfR32F;
FOURCC_G32R32F : Result := lfGR32F;
FOURCC_A32B32G32R32F : Result := lfBGRA32F;
end
end else
case pfRGBbpp of
8 :
if (pfFlags and DDPF_LUMINANCE > 0) and (pfRMask xor $FF = 0) then
Result := lfL8
else
if (pfFlags and DDPF_ALPHA > 0) and (pfAMask xor $FF = 0) then
Result := lfA8;
16 :
if pfFlags and DDPF_ALPHAPIXELS > 0 then
begin
if (pfFlags and DDPF_LUMINANCE > 0) and (pfRMask xor $FF + pfAMask xor $FF00 = 0) then
Result := lfAL8
else
if pfFlags and DDPF_RGB > 0 then
if pfRMask xor $0F00 + pfGMask xor $00F0 + pfBMask xor $0F + pfAMask xor $F000 = 0 then
Result := lfBGRA4
else
if pfRMask xor $7C00 + pfGMask xor $03E0 + pfBMask xor $1F + pfAMask xor $8000 = 0 then
Result := lfBGR5A1;
end else
if pfFlags and DDPF_RGB > 0 then
if pfRMask xor $F800 + pfGMask xor $07E0 + pfBMask xor $1F = 0 then
Result := lfBGR565;
24 :
if pfRMask xor $FF0000 + pfGMask xor $FF00 + pfBMask xor $FF = 0 then
Result := lfBGR8;
32 :
if pfRMask xor $FF0000 + pfGMask xor $FF00 + pfBMask xor $FF + pfAMask xor $FF000000 = 0 then
Result := lfBGRA8;
end;
end;
begin
inherited Create(FileName);
Stream := TStream.Init(FileName);
if Stream = nil then
begin
FWidth := 1;
FHeight := 1;
Exit;
end;
Stream.Read(DDS, SizeOf(DDS));
FWidth := DDS.dwWidth;
FHeight := DDS.dwHeight;
// Select OpenGL texture format
LF := GetLoadFormat(DDS);
if LF = lfNULL then
begin
LogError('Not supported format: "' + FileName + '"');
Stream.Free;
Exit;
end;
with DDS, LoadFormat[LF] do
begin
if dwFlags and DDSD_MIPMAPCOUNT = 0 then
dwMipMapCount := 1;
RMips := dwMipMapCount;
for i := 0 to dwMipMapCount - 1 do
if Min(Width shr i, Height shr i) < 4 then
begin
RMips := i;
break;
end;
// 2D image
Sampler := GL_TEXTURE_2D;
Samples := 1;
// CubeMap image
if dwCaps2 and DDSCAPS2_CUBEMAP > 0 then
begin
Sampler := GL_TEXTURE_CUBE_MAP;
Samples := 6;
end;
// 3D image
///...
Data := GetMemory((FWidth div DivSize) * (FHeight div DivSize) * Bytes);
gl.GenTextures(1, @FID);
gl.BindTexture(Sampler, FID);
for s := 0 to Samples - 1 do
begin
case Sampler of
GL_TEXTURE_CUBE_MAP :
st := TGLConst(Ord(GL_TEXTURE_CUBE_MAP_POSITIVE_X) + s)
else
st := Sampler;
end;
for i := 0 to dwMipMapCount - 1 do
begin
w := FWidth shr i;
h := FHeight shr i;
dwSize := ((w div DivSize) * (h div DivSize) * Bytes);
if i >= RMips then
begin
Stream.Pos := Stream.Pos + dwSize;
continue;
end;
Stream.Read(Data^, dwSize);
if Compressed then
gl.CompressedTexImage2D(st, i, IFormat, w, h, 0, dwSize, Data)
else
gl.TexImage2D(st, i, IFormat, w, h, 0, EFormat, DataType, Data);
end;
end;
FreeMemory(Data);
FMipMap := dwMipMapCount > 1;
if FMipMap then
gl.TexParameteri(Sampler, GL_TEXTURE_MAX_LEVEL, TGLConst(RMips - 1));
end;
Stream.Free;
Filter := tfAniso;
end;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment