forked from MoonsideGames/MoonWorks
add DDS and BC7 support
parent
0d93207ae9
commit
b22d3bed30
|
@ -1 +1 @@
|
||||||
Subproject commit 610698fcd63f4c135fc0f60f38ccc8707ce568eb
|
Subproject commit 98c590ae77c3b6a64a370bac439f20728959a8b6
|
|
@ -67,6 +67,7 @@ namespace MoonWorks.Graphics
|
||||||
BC1,
|
BC1,
|
||||||
BC2,
|
BC2,
|
||||||
BC3,
|
BC3,
|
||||||
|
BC7,
|
||||||
R8G8_SNORM,
|
R8G8_SNORM,
|
||||||
R8G8B8A8_SNORM,
|
R8G8B8A8_SNORM,
|
||||||
A2R10G10B10,
|
A2R10G10B10,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using RefreshCS;
|
using RefreshCS;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics
|
namespace MoonWorks.Graphics
|
||||||
|
@ -56,6 +57,9 @@ namespace MoonWorks.Graphics
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves RGBA or BGRA pixel data to a file in PNG format.
|
||||||
|
/// </summary>
|
||||||
public unsafe static void SavePNG(string path, int width, int height, TextureFormat format, byte[] pixels)
|
public unsafe static void SavePNG(string path, int width, int height, TextureFormat format, byte[] pixels)
|
||||||
{
|
{
|
||||||
if (format != TextureFormat.R8G8B8A8 && format != TextureFormat.B8G8R8A8)
|
if (format != TextureFormat.R8G8B8A8 && format != TextureFormat.B8G8R8A8)
|
||||||
|
@ -69,6 +73,34 @@ namespace MoonWorks.Graphics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Texture LoadDDS(GraphicsDevice graphicsDevice, CommandBuffer commandBuffer, System.IO.Stream stream)
|
||||||
|
{
|
||||||
|
using (var reader = new BinaryReader(stream))
|
||||||
|
{
|
||||||
|
ParseDDS(reader, out var format, out var width, out var height, out var levels);
|
||||||
|
Texture texture = CreateTexture2D(graphicsDevice, (uint) width, (uint) height, format, TextureUsageFlags.Sampler, SampleCount.One, (uint) levels);
|
||||||
|
|
||||||
|
for (int i = 0; i < levels; i += 1)
|
||||||
|
{
|
||||||
|
var levelWidth = width >> i;
|
||||||
|
var levelHeight = height >> i;
|
||||||
|
|
||||||
|
var pixels = reader.ReadBytes(
|
||||||
|
Texture.CalculateDDSLevelSize(
|
||||||
|
levelWidth,
|
||||||
|
levelHeight,
|
||||||
|
format
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
var textureSlice = new TextureSlice(texture, new Rect(0, 0, levelWidth, levelHeight), 0, 0, (uint) i);
|
||||||
|
commandBuffer.SetTextureData(textureSlice, pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a 2D texture.
|
/// Creates a 2D texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -222,5 +254,202 @@ namespace MoonWorks.Graphics
|
||||||
LevelCount = 1;
|
LevelCount = 1;
|
||||||
UsageFlags = TextureUsageFlags.ColorTarget;
|
UsageFlags = TextureUsageFlags.ColorTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DDS loading extension, based on MojoDDS
|
||||||
|
// Taken from https://github.com/FNA-XNA/FNA/blob/1e49f868f595f62bc6385db45949a03186a7cd7f/src/Graphics/Texture.cs#L194
|
||||||
|
private static void ParseDDS(
|
||||||
|
BinaryReader reader,
|
||||||
|
out TextureFormat format,
|
||||||
|
out int width,
|
||||||
|
out int height,
|
||||||
|
out int levels
|
||||||
|
) {
|
||||||
|
// A whole bunch of magic numbers, yay DDS!
|
||||||
|
const uint DDS_MAGIC = 0x20534444;
|
||||||
|
const uint DDS_HEADERSIZE = 124;
|
||||||
|
const uint DDS_PIXFMTSIZE = 32;
|
||||||
|
const uint DDSD_CAPS = 0x1;
|
||||||
|
const uint DDSD_HEIGHT = 0x2;
|
||||||
|
const uint DDSD_WIDTH = 0x4;
|
||||||
|
const uint DDSD_PITCH = 0x8;
|
||||||
|
const uint DDSD_FMT = 0x1000;
|
||||||
|
const uint DDSD_LINEARSIZE = 0x80000;
|
||||||
|
const uint DDSD_REQ = (
|
||||||
|
DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_FMT
|
||||||
|
);
|
||||||
|
const uint DDSCAPS_MIPMAP = 0x400000;
|
||||||
|
const uint DDSCAPS_TEXTURE = 0x1000;
|
||||||
|
const uint DDSCAPS2_CUBEMAP = 0x200;
|
||||||
|
const uint DDPF_FOURCC = 0x4;
|
||||||
|
const uint DDPF_RGB = 0x40;
|
||||||
|
const uint FOURCC_DXT1 = 0x31545844;
|
||||||
|
const uint FOURCC_DXT3 = 0x33545844;
|
||||||
|
const uint FOURCC_DXT5 = 0x35545844;
|
||||||
|
const uint FOURCC_BPTC = 0x30315844;
|
||||||
|
// const uint FOURCC_DX10 = 0x30315844;
|
||||||
|
const uint pitchAndLinear = (
|
||||||
|
DDSD_PITCH | DDSD_LINEARSIZE
|
||||||
|
);
|
||||||
|
|
||||||
|
// File should start with 'DDS '
|
||||||
|
if (reader.ReadUInt32() != DDS_MAGIC)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Not a DDS!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Texture info
|
||||||
|
uint size = reader.ReadUInt32();
|
||||||
|
if (size != DDS_HEADERSIZE)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Invalid DDS header!");
|
||||||
|
}
|
||||||
|
uint flags = reader.ReadUInt32();
|
||||||
|
if ((flags & DDSD_REQ) != DDSD_REQ)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Invalid DDS flags!");
|
||||||
|
}
|
||||||
|
if ((flags & pitchAndLinear) == pitchAndLinear)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Invalid DDS flags!");
|
||||||
|
}
|
||||||
|
height = reader.ReadInt32();
|
||||||
|
width = reader.ReadInt32();
|
||||||
|
reader.ReadUInt32(); // dwPitchOrLinearSize, unused
|
||||||
|
reader.ReadUInt32(); // dwDepth, unused
|
||||||
|
levels = reader.ReadInt32();
|
||||||
|
|
||||||
|
// "Reserved"
|
||||||
|
reader.ReadBytes(4 * 11);
|
||||||
|
|
||||||
|
// Format info
|
||||||
|
uint formatSize = reader.ReadUInt32();
|
||||||
|
if (formatSize != DDS_PIXFMTSIZE)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Bogus PIXFMTSIZE!");
|
||||||
|
}
|
||||||
|
uint formatFlags = reader.ReadUInt32();
|
||||||
|
uint formatFourCC = reader.ReadUInt32();
|
||||||
|
uint formatRGBBitCount = reader.ReadUInt32();
|
||||||
|
uint formatRBitMask = reader.ReadUInt32();
|
||||||
|
uint formatGBitMask = reader.ReadUInt32();
|
||||||
|
uint formatBBitMask = reader.ReadUInt32();
|
||||||
|
uint formatABitMask = reader.ReadUInt32();
|
||||||
|
|
||||||
|
// dwCaps "stuff"
|
||||||
|
uint caps = reader.ReadUInt32();
|
||||||
|
if ((caps & DDSCAPS_TEXTURE) == 0)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Not a texture!");
|
||||||
|
}
|
||||||
|
uint caps2 = reader.ReadUInt32();
|
||||||
|
if ( caps2 != 0 &&
|
||||||
|
(caps2 & DDSCAPS2_CUBEMAP) != DDSCAPS2_CUBEMAP )
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Invalid caps2!");
|
||||||
|
}
|
||||||
|
reader.ReadUInt32(); // dwCaps3, unused
|
||||||
|
reader.ReadUInt32(); // dwCaps4, unused
|
||||||
|
|
||||||
|
// "Reserved"
|
||||||
|
reader.ReadUInt32();
|
||||||
|
|
||||||
|
// Mipmap sanity check
|
||||||
|
if ((caps & DDSCAPS_MIPMAP) != DDSCAPS_MIPMAP)
|
||||||
|
{
|
||||||
|
levels = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine texture format
|
||||||
|
if ((formatFlags & DDPF_FOURCC) == DDPF_FOURCC)
|
||||||
|
{
|
||||||
|
switch (formatFourCC)
|
||||||
|
{
|
||||||
|
case 0x71:
|
||||||
|
format = TextureFormat.R16G16B16A16_SFLOAT;
|
||||||
|
break;
|
||||||
|
case 0x74:
|
||||||
|
format = TextureFormat.R32G32B32A32_SFLOAT;
|
||||||
|
break;
|
||||||
|
case FOURCC_DXT1:
|
||||||
|
format = TextureFormat.BC1;
|
||||||
|
break;
|
||||||
|
case FOURCC_DXT3:
|
||||||
|
format = TextureFormat.BC2;
|
||||||
|
break;
|
||||||
|
case FOURCC_DXT5:
|
||||||
|
format = TextureFormat.BC3;
|
||||||
|
break;
|
||||||
|
case FOURCC_BPTC:
|
||||||
|
format = TextureFormat.BC7;
|
||||||
|
// These next 5 uints are part of the DX10 DDS header.
|
||||||
|
// They contain a little extra information but aren't that important.
|
||||||
|
uint dxgiFormat = reader.ReadUInt32();
|
||||||
|
uint resourceDimension = reader.ReadUInt32();
|
||||||
|
uint miscFlag = reader.ReadUInt32();
|
||||||
|
uint arraySize = reader.ReadUInt32();
|
||||||
|
reader.ReadUInt32(); // reserved
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Unsupported DDS texture format"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((formatFlags & DDPF_RGB) == DDPF_RGB)
|
||||||
|
{
|
||||||
|
if ( formatRGBBitCount != 32 ||
|
||||||
|
formatRBitMask != 0x00FF0000 ||
|
||||||
|
formatGBitMask != 0x0000FF00 ||
|
||||||
|
formatBBitMask != 0x000000FF ||
|
||||||
|
formatABitMask != 0xFF000000 )
|
||||||
|
{
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Unsupported DDS texture format"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
format = TextureFormat.B8G8R8A8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Unsupported DDS texture format"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculateDDSLevelSize(
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
TextureFormat format
|
||||||
|
) {
|
||||||
|
if (format == TextureFormat.R8G8B8A8)
|
||||||
|
{
|
||||||
|
return (((width * 32) + 7) / 8) * height;
|
||||||
|
}
|
||||||
|
else if (format == TextureFormat.R16G16B16A16_SFLOAT)
|
||||||
|
{
|
||||||
|
return (((width * 64) + 7) / 8) * height;
|
||||||
|
}
|
||||||
|
else if (format == TextureFormat.R32G32B32A32_SFLOAT)
|
||||||
|
{
|
||||||
|
return (((width * 128) + 7) / 8) * height;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int blockSize = 16;
|
||||||
|
if (format == TextureFormat.BC1)
|
||||||
|
{
|
||||||
|
blockSize = 8;
|
||||||
|
}
|
||||||
|
width = System.Math.Max(width, 1);
|
||||||
|
height = System.Math.Max(height, 1);
|
||||||
|
return (
|
||||||
|
((width + 3) / 4) *
|
||||||
|
((height + 3) / 4) *
|
||||||
|
blockSize
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue