Skip to content

Instantly share code, notes, and snippets.

@tilkinsc
Last active August 19, 2022 20:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tilkinsc/a3af3c2d53e4f78bfad49db0db516fc7 to your computer and use it in GitHub Desktop.
Save tilkinsc/a3af3c2d53e4f78bfad49db0db516fc7 to your computer and use it in GitHub Desktop.
C# rudimentary loading DDS for OpenGL using Silk.NET
enum DDSD
{
NONE = 0x00,
CAPS = 0x01,
HEIGHT = 0x02,
WIDTH = 0x04,
PITCH = 0x08,
PIXELFORMAT = 0x1000,
MIPMAPCOUNT = 0x20000,
LINEARSIZE = 0x80000,
DEPTH = 0x800000
}
enum DDPF
{
NONE = 0x00,
ALPHAPIXELS = 0x01,
ALPHA = 0x02,
FOURCC = 0x04,
RGB = 0x40,
YUV = 0x200,
LUMINANCE = 0x20000
}
enum DDSFourCC
{
NONE = 0x00,
DXT1 = 0x01,
DXT2 = 0x02,
DXT3 = 0x04,
DXT4 = 0x08,
DXT5 = 0x10,
DX10 = 0x20
}
enum DDSCaps
{
COMPLEX = 0x8,
MIPMAP = 0x400000,
TEXTURE = 0x1000
}
enum DDSCaps2
{
CUBEMAP = 0x200,
CUBEMAP_POSITIVEX = 0x400,
CUBEMAP_NEGATIVEX = 0x800,
CUBEMAP_POSITIVEY = 0x1000,
CUBEMAP_NEGATIVEY = 0x2000,
CUBEMAP_POSITIVEZ = 0x4000,
CUBEMAP_NEGATIVEZ = 0x8000,
VOLUME = 0x200000
}
struct DDSPixelFormat
{
public uint Size;
public DDPF Flags;
public DDSFourCC FourCC;
public uint RGBBitCount;
public uint RBitMask;
public uint GBitMask;
public uint BBitMask;
public uint ABitMask;
}
enum DDSDXGIFormat
{
FORMAT_UNKNOWN = 0,
FORMAT_R32G32B32A32_TYPELESS = 1,
FORMAT_R32G32B32A32_FLOAT = 2,
FORMAT_R32G32B32A32_UINT = 3,
FORMAT_R32G32B32A32_SINT = 4,
FORMAT_R32G32B32_TYPELESS = 5,
FORMAT_R32G32B32_FLOAT = 6,
FORMAT_R32G32B32_UINT = 7,
FORMAT_R32G32B32_SINT = 8,
FORMAT_R16G16B16A16_TYPELESS = 9,
FORMAT_R16G16B16A16_FLOAT = 10,
FORMAT_R16G16B16A16_UNORM = 11,
FORMAT_R16G16B16A16_UINT = 12,
FORMAT_R16G16B16A16_SNORM = 13,
FORMAT_R16G16B16A16_SINT = 14,
FORMAT_R32G32_TYPELESS = 15,
FORMAT_R32G32_FLOAT = 16,
FORMAT_R32G32_UINT = 17,
FORMAT_R32G32_SINT = 18,
FORMAT_R32G8X24_TYPELESS = 19,
FORMAT_D32_FLOAT_S8X24_UINT = 20,
FORMAT_R32_FLOAT_X8X24_TYPELESS = 21,
FORMAT_X32_TYPELESS_G8X24_UINT = 22,
FORMAT_R10G10B10A2_TYPELESS = 23,
FORMAT_R10G10B10A2_UNORM = 24,
FORMAT_R10G10B10A2_UINT = 25,
FORMAT_R11G11B10_FLOAT = 26,
FORMAT_R8G8B8A8_TYPELESS = 27,
FORMAT_R8G8B8A8_UNORM = 28,
FORMAT_R8G8B8A8_UNORM_SRGB = 29,
FORMAT_R8G8B8A8_UINT = 30,
FORMAT_R8G8B8A8_SNORM = 31,
FORMAT_R8G8B8A8_SINT = 32,
FORMAT_R16G16_TYPELESS = 33,
FORMAT_R16G16_FLOAT = 34,
FORMAT_R16G16_UNORM = 35,
FORMAT_R16G16_UINT = 36,
FORMAT_R16G16_SNORM = 37,
FORMAT_R16G16_SINT = 38,
FORMAT_R32_TYPELESS = 39,
FORMAT_D32_FLOAT = 40,
FORMAT_R32_FLOAT = 41,
FORMAT_R32_UINT = 42,
FORMAT_R32_SINT = 43,
FORMAT_R24G8_TYPELESS = 44,
FORMAT_D24_UNORM_S8_UINT = 45,
FORMAT_R24_UNORM_X8_TYPELESS = 46,
FORMAT_X24_TYPELESS_G8_UINT = 47,
FORMAT_R8G8_TYPELESS = 48,
FORMAT_R8G8_UNORM = 49,
FORMAT_R8G8_UINT = 50,
FORMAT_R8G8_SNORM = 51,
FORMAT_R8G8_SINT = 52,
FORMAT_R16_TYPELESS = 53,
FORMAT_R16_FLOAT = 54,
FORMAT_D16_UNORM = 55,
FORMAT_R16_UNORM = 56,
FORMAT_R16_UINT = 57,
FORMAT_R16_SNORM = 58,
FORMAT_R16_SINT = 59,
FORMAT_R8_TYPELESS = 60,
FORMAT_R8_UNORM = 61,
FORMAT_R8_UINT = 62,
FORMAT_R8_SNORM = 63,
FORMAT_R8_SINT = 64,
FORMAT_A8_UNORM = 65,
FORMAT_R1_UNORM = 66,
FORMAT_R9G9B9E5_SHAREDEXP = 67,
FORMAT_R8G8_B8G8_UNORM = 68,
FORMAT_G8R8_G8B8_UNORM = 69,
FORMAT_BC1_TYPELESS = 70,
FORMAT_BC1_UNORM = 71,
FORMAT_BC1_UNORM_SRGB = 72,
FORMAT_BC2_TYPELESS = 73,
FORMAT_BC2_UNORM = 74,
FORMAT_BC2_UNORM_SRGB = 75,
FORMAT_BC3_TYPELESS = 76,
FORMAT_BC3_UNORM = 77,
FORMAT_BC3_UNORM_SRGB = 78,
FORMAT_BC4_TYPELESS = 79,
FORMAT_BC4_UNORM = 80,
FORMAT_BC4_SNORM = 81,
FORMAT_BC5_TYPELESS = 82,
FORMAT_BC5_UNORM = 83,
FORMAT_BC5_SNORM = 84,
FORMAT_B5G6R5_UNORM = 85,
FORMAT_B5G5R5A1_UNORM = 86,
FORMAT_B8G8R8A8_UNORM = 87,
FORMAT_B8G8R8X8_UNORM = 88,
FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89,
FORMAT_B8G8R8A8_TYPELESS = 90,
FORMAT_B8G8R8A8_UNORM_SRGB = 91,
FORMAT_B8G8R8X8_TYPELESS = 92,
FORMAT_B8G8R8X8_UNORM_SRGB = 93,
FORMAT_BC6H_TYPELESS = 94,
FORMAT_BC6H_UF16 = 95,
FORMAT_BC6H_SF16 = 96,
FORMAT_BC7_TYPELESS = 97,
FORMAT_BC7_UNORM = 98,
FORMAT_BC7_UNORM_SRGB = 99,
FORMAT_AYUV = 100,
FORMAT_Y410 = 101,
FORMAT_Y416 = 102,
FORMAT_NV12 = 103,
FORMAT_P010 = 104,
FORMAT_P016 = 105,
FORMAT_420_OPAQUE = 106,
FORMAT_YUY2 = 107,
FORMAT_Y210 = 108,
FORMAT_Y216 = 109,
FORMAT_NV11 = 110,
FORMAT_AI44 = 111,
FORMAT_IA44 = 112,
FORMAT_P8 = 113,
FORMAT_A8P8 = 114,
FORMAT_B4G4R4A4_UNORM = 115,
FORMAT_P208 = 130,
FORMAT_V208 = 131,
FORMAT_V408 = 132,
FORMAT_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE = 133,
FORMAT_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE = 134,
FORMAT_FORCE_UINT = int.MinValue
}
enum DDSResourceDimension
{
UNKNOWN = 0,
BUFFER = 1,
TEXTURE1D = 2,
TEXTURE2D = 3,
TEXTURE3D = 4
}
enum DDSMisc
{
TEXTURECUBE
}
enum DDSMisc2
{
ALPHA_MODE_UNKNOWN = 0x00,
ALPHA_MODE_STRAIGHT = 0x01,
ALPHA_MODE_PREMULTIPLIED = 0x02,
ALPHA_MODE_OPAQUE = 0x03,
ALPHA_MODE_CUSTOM = 0x04
}
struct DDSDX10Header
{
public DDSDXGIFormat Format;
public DDSResourceDimension ResourceDimension;
public DDSMisc MiscFlags;
public uint ArraySize;
public DDSMisc2 MiscFlags2;
}
class DDSFile
{
public uint HeaderSize { get; set; }
public DDSD Flags { get; set; }
public uint Height { get; set; }
public uint Width { get; set; }
public uint PitchOrLinearSize { get; set; }
public uint Depth { get; set; }
public uint MipMapCount { get; set; }
public DDSPixelFormat PixelFormat { get; set; }
public DDSCaps Caps1 { get; set; }
public DDSCaps2 Caps2 { get; set; }
public bool IsHasDX10 { get; set; }
public DDSDX10Header DX10 { get; set; }
public byte[]? Data;
}
static class DDSLoader
{
public static DDSFile? Load(string path)
{
try {
using (FileStream fs = File.OpenRead(path)) {
DDSFile file = new DDSFile();
byte[] header = new byte[128];
fs.Read(header, 0, 128);
file.HeaderSize = (uint) ((header[4]) | (header[5] << 8) | (header[6] << 16) | (header[7] << 24));
file.Flags = (DDSD) ((header[4]) | (header[5] << 8) | (header[6] << 16) | (header[7] << 24));
file.Height = (uint) ((header[12]) | (header[13] << 8) | (header[14] << 16) | (header[15] << 24));
file.Width = (uint) ((header[16]) | (header[17] << 8) | (header[18] << 16) | (header[19] << 24));
file.PitchOrLinearSize = (uint) ((header[20]) | (header[21] << 8) | (header[22] << 16) | (header[23] << 24));
file.Depth = (uint) ((header[24]) | (header[25] << 8) | (header[26] << 16) | (header[27] << 24));
file.MipMapCount = (uint) ((header[28]) | (header[29] << 8) | (header[30] << 16) | (header[31] << 24));
DDSPixelFormat format = new DDSPixelFormat();
format.Size = (uint) ((header[76]) | (header[77] << 8) | (header[78] << 16) | (header[79] << 24));
format.Flags = (DDPF) ((header[80]) | (header[81] << 8) | (header[82] << 16) | (header[83] << 24));
format.FourCC = (DDSFourCC) ((header[84]) | (header[85] << 8) | (header[86] << 16) | (header[87] << 24));
format.RGBBitCount = (uint) ((header[88]) | (header[89] << 8) | (header[90] << 16) | (header[91] << 24));
format.RBitMask = (uint) ((header[92]) | (header[93] << 8) | (header[94] << 16) | (header[95] << 24));
format.GBitMask = (uint) ((header[96]) | (header[97] << 8) | (header[98] << 16) | (header[99] << 24));
format.BBitMask = (uint) ((header[100]) | (header[101] << 8) | (header[102] << 16) | (header[103] << 24));
format.ABitMask = (uint) ((header[104]) | (header[105] << 8) | (header[106] << 16) | (header[107] << 24));
file.PixelFormat = format;
if (format.FourCC == DDSFourCC.DX10) {
byte[] header2 = new byte[20];
fs.Read(header2, 0, 20);
DDSDX10Header dx10 = new DDSDX10Header();
dx10.Format = (DDSDXGIFormat) ((header[0]) | (header[1] << 8) | (header[2] << 16) | (header[3] << 24));
dx10.ResourceDimension = (DDSResourceDimension) ((header[4]) | (header[5] << 8) | (header[6] << 16) | (header[7] << 24));
dx10.MiscFlags = (DDSMisc) ((header[8]) | (header[9] << 8) | (header[10] << 16) | (header[11] << 24));
dx10.ArraySize = (uint) ((header[12]) | (header[13] << 8) | (header[14] << 16) | (header[15] << 24));
dx10.MiscFlags2 = (DDSMisc2) ((header[16]) | (header[17] << 8) | (header[18] << 16) | (header[19] << 24));
file.DX10 = dx10;
file.IsHasDX10 = true;
}
file.Caps1 = (DDSCaps) ((header[104]) | (header[105] << 8) | (header[106] << 16) | (header[107] << 24));
file.Caps2 = (DDSCaps2) ((header[108]) | (header[109] << 8) | (header[110] << 16) | (header[111] << 24));
// TODO: not sure if fs.Length is the correct option here
byte[] data = new byte[fs.Length];
fs.Read(data, 0, (int) fs.Length);
file.Data = data;
return file;
}
} catch (Exception e) {
Console.Error.WriteLine(e);
}
return null;
}
}
class Material
{
public uint TextureID { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public int AtlasWidth { get; private set; }
public int AtlasHeight { get; private set; }
private Material()
{
}
public static unsafe Material? LoadTextureDDS(DDSFile? dds)
{
if (dds == null || dds.Data == null)
return null;
GL gl = WindowHandler.OpenGL!;
Material tex = new Material();
GLEnum format = 0;
uint blocksize = 0;
switch (dds.PixelFormat.FourCC)
{
case DDSFourCC.DXT1:
blocksize = 8;
format = (GLEnum) GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
break;
case DDSFourCC.DXT4:
format = (GLEnum) GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
blocksize = 16;
break;
case DDSFourCC.DX10:
case DDSFourCC.DXT5:
default:
format = (GLEnum) GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
blocksize = 16;
break;
}
uint id = gl.GenTexture();
gl.BindTexture(TextureTarget.Texture2D, id);
gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBaseLevel, 0);
gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMaxLevel, dds.MipMapCount - 1);
gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int) GLEnum.LinearMipmapLinear);
gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int) GLEnum.Nearest);
gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int) GLEnum.ClampToEdge);
gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int) GLEnum.ClampToEdge);
uint offset = 0;
uint size = 0;
uint w = dds.Width;
uint h = dds.Height;
uint mipmaps = dds.MipMapCount;
for (int i=0; i<dds.MipMapCount; i++)
{
if (w == 0 || h == 0) {
mipmaps--;
continue;
}
size = ((w+3)/4) * ((h+3)/4) * blocksize;
fixed (byte* p = &dds.Data[0])
{
gl.CompressedTexImage2D(TextureTarget.Texture2D, i, format, w, h, 0, size, p + offset);
}
offset += size;
w /= 2;
h /= 2;
}
gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMaxLevel, mipmaps - 1);
gl.BindTexture(TextureTarget.Texture2D, 0);
tex.TextureID = id;
return tex;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment