//new 3d-map texture private static Texture3D GenerateNewTexture3D(LoadSurfaceFormat loadSurfaceFormat, FourCC compressionFormat, GraphicsDevice device, int width, int height, int depth, bool hasMipMaps, uint pixelFlags, int rgbBitCount) { SurfaceFormat expectedFormat = SurfaceFormatFromLoadFormat(loadSurfaceFormat, compressionFormat, pixelFlags, rgbBitCount); Texture3D texture = new Texture3D(device, width, height, depth, hasMipMaps, expectedFormat); if (texture.Format != expectedFormat) { throw new InvalidDataException($"Can't generate a {expectedFormat} surface."); } return(texture); }
//new cube-map texture private static TextureCube GenerateNewCubeTexture(LoadSurfaceFormat loadSurfaceFormat, FourCC compressionFormat, GraphicsDevice device, int width, uint pixelFlags, int rgbBitCount) { SurfaceFormat expectedFormat = SurfaceFormatFromLoadFormat(loadSurfaceFormat, compressionFormat, pixelFlags, rgbBitCount); TextureCube texture = new TextureCube(device, width, true, expectedFormat); //hasMipMaps if (texture.Format != expectedFormat) { throw new InvalidDataException($"Can't generate a {expectedFormat} surface."); } return(texture); }
//new 2d-map texture private static Texture2D GenerateNewTexture2D(LoadSurfaceFormat loadSurfaceFormat, FourCC compressionFormat, GraphicsDevice device, int width, int height, bool hasMipMaps, uint pixelFlags, int rgbBitCount) { SurfaceFormat expectedFormat = SurfaceFormatFromLoadFormat(loadSurfaceFormat, compressionFormat, pixelFlags, rgbBitCount); Texture2D texture = new Texture2D(device, width, height, hasMipMaps, expectedFormat); texture.Tag = new Formats.Msts.Models.AceInfo() { AlphaBits = XNATextureNumAlphaBits(texture) }; if (texture.Format != expectedFormat) { throw new InvalidDataException($"Can't generate a {expectedFormat} surface."); } return(texture); }
//new 3d-map texture private static Texture3D GenerateNewTexture3D(LoadSurfaceFormat loadSurfaceFormat, FourCC compressionFormat, GraphicsDevice device, int width, int height, int depth, bool hasMipMaps, uint pixelFlags, int rgbBitCount) { SurfaceFormat surfaceFormat = SurfaceFormatFromLoadFormat(loadSurfaceFormat, compressionFormat, pixelFlags, rgbBitCount); Texture3D tx = new Texture3D(device, width, height, depth, hasMipMaps, surfaceFormat); if (tx.Format != surfaceFormat) { throw new Exception("Can't generate a " + surfaceFormat.ToString() + " surface."); } return tx; }
//try to evaluate the xna compatible surface for the present data private static SurfaceFormat SurfaceFormatFromLoadFormat(LoadSurfaceFormat loadSurfaceFormat, FourCC compressionFormat, uint pixelFlags, int rgbBitCount) { if (loadSurfaceFormat == LoadSurfaceFormat.Unknown) { switch (compressionFormat) { case FourCC.D3DFMT_DXT1: return SurfaceFormat.Dxt1; case FourCC.D3DFMT_DXT3: return SurfaceFormat.Dxt3; case FourCC.D3DFMT_DXT5: return SurfaceFormat.Dxt5; case 0: if (rgbBitCount == 8) { return SurfaceFormat.Alpha8; } if (rgbBitCount == 16) { if (HasAlphaTest(pixelFlags)) { return SurfaceFormat.Bgr565; } else { return SurfaceFormat.Bgra4444; } } if (rgbBitCount == 32 || rgbBitCount == 24) { return SurfaceFormat.Color; } break; default: throw new Exception("Unsuported format"); } } else { switch (loadSurfaceFormat) { case LoadSurfaceFormat.Alpha8: return SurfaceFormat.Alpha8; case LoadSurfaceFormat.Bgr565: return SurfaceFormat.Bgr565; case LoadSurfaceFormat.Bgra4444: return SurfaceFormat.Bgra4444; case LoadSurfaceFormat.Bgra5551: return SurfaceFormat.Bgra5551; case LoadSurfaceFormat.A8R8G8B8: return SurfaceFormat.Color; case LoadSurfaceFormat.Dxt1: return SurfaceFormat.Dxt1; case LoadSurfaceFormat.Dxt3: return SurfaceFormat.Dxt3; case LoadSurfaceFormat.Dxt5: return SurfaceFormat.Dxt5; //Updated at load time to X8R8B8B8 case LoadSurfaceFormat.R8G8B8: return SurfaceFormat.Color; case LoadSurfaceFormat.X8B8G8R8: return SurfaceFormat.Color; case LoadSurfaceFormat.X8R8G8B8: return SurfaceFormat.Color; case LoadSurfaceFormat.A8B8G8R8: return SurfaceFormat.Color; case LoadSurfaceFormat.R32F: return SurfaceFormat.Single; case LoadSurfaceFormat.A32B32G32R32F: return SurfaceFormat.Vector4; case LoadSurfaceFormat.G32R32F: return SurfaceFormat.Vector2; case LoadSurfaceFormat.R16F: return SurfaceFormat.HalfSingle; case LoadSurfaceFormat.G16R16F: return SurfaceFormat.HalfVector2; case LoadSurfaceFormat.A16B16G16R16F: return SurfaceFormat.HalfVector4; case LoadSurfaceFormat.CxV8U8: return SurfaceFormat.NormalizedByte2; case LoadSurfaceFormat.Q8W8V8U8: return SurfaceFormat.NormalizedByte4; case LoadSurfaceFormat.G16R16: return SurfaceFormat.Rg32; case LoadSurfaceFormat.A2B10G10R10: return SurfaceFormat.Rgba1010102; case LoadSurfaceFormat.A16B16G16R16: return SurfaceFormat.Rgba64; default: throw new Exception(loadSurfaceFormat.ToString() + " is an unsuported format"); } } throw new Exception("Unsuported format"); }
//Get the byte data from a mip-map level. private static void GetMipMaps(int offsetInStream, int map, bool hasMipMaps, int width, int height, bool isCompressed, FourCC compressionFormat, int rgbBitCount, bool partOfCubeMap, BinaryReader reader, LoadSurfaceFormat loadSurfaceFormat, ref byte[] data, out int numBytes) { int seek = 128 + offsetInStream; for (int i = 0; i < map; i++) { seek += MipMapSizeInBytes(i, width, height, isCompressed, compressionFormat, rgbBitCount); } reader.BaseStream.Seek(seek, SeekOrigin.Begin); numBytes = MipMapSizeInBytes(map, width, height, isCompressed, compressionFormat, rgbBitCount); if (isCompressed == false && rgbBitCount == 24) { numBytes += (numBytes / 3); } if (data == null || data.Length < numBytes) { data = new byte[numBytes]; } if (isCompressed == false && loadSurfaceFormat == LoadSurfaceFormat.R8G8B8) { for (int i = 0; i < numBytes; i += 4) { data[i] = reader.ReadByte(); data[i + 1] = reader.ReadByte(); data[i + 2] = reader.ReadByte(); data[i + 3] = 255; } } else { reader.Read(data, 0, numBytes); } if (loadSurfaceFormat == LoadSurfaceFormat.X8R8G8B8 || loadSurfaceFormat == LoadSurfaceFormat.X8B8G8R8) { for (int i = 0; i < numBytes; i += 4) { data[i + 3] = 255; } } if (loadSurfaceFormat == LoadSurfaceFormat.A8R8G8B8 || loadSurfaceFormat == LoadSurfaceFormat.X8R8G8B8 || loadSurfaceFormat == LoadSurfaceFormat.R8G8B8) { int bytesPerPixel = (rgbBitCount == 32 || rgbBitCount == 24) ? 4 : 3; byte g, b; if (bytesPerPixel == 3) { for (int i = 0; i < numBytes - 2; i += 3) { g = data[i]; b = data[i + 2]; data[i] = b; data[i + 2] = g; } } else { for (int i = 0; i < numBytes - 3; i += 4) { g = data[i]; b = data[i + 2]; data[i] = b; data[i + 2] = g; } } } }
//loads the data from a stream in to a texture object. private static void InternalDDSFromStream(Stream stream, GraphicsDevice device, int streamOffset, bool loadMipMap, out Texture texture) { if (stream == null) { throw new ArgumentNullException(nameof(stream), "Can't read from a null stream"); } using (BinaryReader reader = new BinaryReader(stream)) { if (streamOffset > reader.BaseStream.Length) { throw new Exception("The stream you offered is smaller then the offset you are proposing for it."); } reader.BaseStream.Seek(streamOffset, SeekOrigin.Begin); //First element of a dds file is a "magic-number" a system to identify that the file is a dds if translated as asci chars the first 4 charachters should be 'DDS ' if (!(reader.ReadUInt32() == DDS_MAGIC)) { throw new InvalidDataException("Can't open non DDS data."); } reader.BaseStream.Position += 8; //size in pixels for the texture. int height = reader.ReadInt32(); int width = reader.ReadInt32(); reader.BaseStream.Position += 4; //depth int depth = reader.ReadInt32(); //number of mip-maps. int numMips = reader.ReadInt32(); reader.BaseStream.Position += 4 * 12; //pixel format flags uint pixelFlags = reader.ReadUInt32(); // (FOURCC code) uint pixelFourCC = reader.ReadUInt32(); //color bit depth int rgbBitCount = reader.ReadInt32(); //mask for red. uint rBitMask = reader.ReadUInt32(); //mask for green. uint gBitMask = reader.ReadUInt32(); //mask for blue. uint bBitMask = reader.ReadUInt32(); //mask for alpha. uint aBitMask = reader.ReadUInt32(); //reader.BaseStream.Position += 16; //texture + mip-map flags. int ddsCaps1 = reader.ReadInt32(); //extra info flags. int ddsCaps2 = reader.ReadInt32(); //ddsCaps3 //reader.ReadInt32(); //ddsCaps4 //reader.ReadInt32(); //reader.ReadInt32(); reader.BaseStream.Position += 12; bool isCubeMap = IsCubemapTest(ddsCaps1, ddsCaps2); bool isVolumeTexture = IsVolumeTextureTest(ddsCaps2); FourCC compressionFormat = GetCompressionFormat(pixelFlags, pixelFourCC); if (compressionFormat == FourCC.DX10) { throw new NotImplementedException("The Dxt 10 header reader is not implemented"); } LoadSurfaceFormat loadSurfaceFormat = GetLoadSurfaceFormat(pixelFlags, pixelFourCC, rgbBitCount, rBitMask, gBitMask, bBitMask, aBitMask); bool isCompressed = IsCompressedTest(pixelFlags); bool hasMipMaps = CheckFullMipChain(width, height, numMips); bool hasAnyMipmaps = numMips > 0; hasMipMaps &= loadMipMap; if (isCubeMap) { TextureCube tex = GenerateNewCubeTexture(loadSurfaceFormat, compressionFormat, device, width, pixelFlags, rgbBitCount); int byteAcumulator = 0; if (numMips == 0) { numMips = 1; } if (!hasMipMaps) { for (int j = 0; j < numMips; j++) { byteAcumulator += MipMapSizeInBytes(j, width, height, isCompressed, compressionFormat, rgbBitCount); } } for (int j = 0; j < numMips; j++) { byte[] localMipData = mipData; GetMipMaps(streamOffset, j, width, height, isCompressed, compressionFormat, rgbBitCount, reader, loadSurfaceFormat, ref localMipData, out int numBytes); mipData = localMipData; if (hasMipMaps) { byteAcumulator += numBytes; } if (j == 0 || hasMipMaps) { tex.SetData(CubeMapFace.PositiveX, j, null, localMipData, 0, numBytes); } else { break; } } for (int j = 0; j < numMips; j++) { byte[] localMipData = mipData; GetMipMaps(byteAcumulator + streamOffset, j, width, height, isCompressed, compressionFormat, rgbBitCount, reader, loadSurfaceFormat, ref localMipData, out int numBytes); mipData = localMipData; if (j == 0 || hasMipMaps) { tex.SetData(CubeMapFace.NegativeX, j, null, localMipData, 0, numBytes); } else { break; } } for (int j = 0; j < numMips; j++) { byte[] localMipData = mipData; GetMipMaps((byteAcumulator * 2) + streamOffset, j, width, height, isCompressed, compressionFormat, rgbBitCount, reader, loadSurfaceFormat, ref localMipData, out int numBytes); mipData = localMipData; if (j == 0 || hasMipMaps) { tex.SetData(CubeMapFace.PositiveY, j, null, localMipData, 0, numBytes); } else { break; } } for (int j = 0; j < numMips; j++) { byte[] localMipData = mipData; GetMipMaps((byteAcumulator * 3) + streamOffset, j, width, height, isCompressed, compressionFormat, rgbBitCount, reader, loadSurfaceFormat, ref localMipData, out int numBytes); mipData = localMipData; if (j == 0 || hasMipMaps) { tex.SetData(CubeMapFace.NegativeY, j, null, localMipData, 0, numBytes); } else { break; } } for (int j = 0; j < numMips; j++) { byte[] localMipData = mipData; GetMipMaps((byteAcumulator * 4) + streamOffset, j, width, height, isCompressed, compressionFormat, rgbBitCount, reader, loadSurfaceFormat, ref localMipData, out int numBytes); mipData = localMipData; if (j == 0 || hasMipMaps) { tex.SetData(CubeMapFace.PositiveZ, j, null, localMipData, 0, numBytes); } else { break; } } for (int j = 0; j < numMips; j++) { byte[] localMipData = mipData; GetMipMaps((byteAcumulator * 5) + streamOffset, j, width, height, isCompressed, compressionFormat, rgbBitCount, reader, loadSurfaceFormat, ref localMipData, out int numBytes); mipData = localMipData; if (j == 0 || hasMipMaps) { tex.SetData(CubeMapFace.NegativeZ, j, null, localMipData, 0, numBytes); } else { break; } } texture = tex; } else if (isVolumeTexture) { Texture3D tex = GenerateNewTexture3D(loadSurfaceFormat, compressionFormat, device, width, height, depth, hasMipMaps, pixelFlags, rgbBitCount); int localStreamOffset = streamOffset; for (int i = 0; i < tex.LevelCount; i++) { int localWidth = MipMapSize(i, width); int localHeight = MipMapSize(i, height); int localDepth = MipMapSize(i, depth); for (int j = 0; j < localDepth; j++) { byte[] localMipData = mipData; GetMipMaps(localStreamOffset, 0, localWidth, localHeight, isCompressed, compressionFormat, rgbBitCount, reader, loadSurfaceFormat, ref localMipData, out int numBytes); localStreamOffset += numBytes; mipData = localMipData; tex.SetData(i, 0, 0, localWidth, localHeight, j, j + 1, localMipData, 0, numBytes); } } texture = tex; } else { Texture2D tex = GenerateNewTexture2D(loadSurfaceFormat, compressionFormat, device, width, height, hasMipMaps, pixelFlags, rgbBitCount); for (int i = 0; i < tex.LevelCount; i++) { byte[] localMipData = mipData; GetMipMaps(streamOffset, i, width, height, isCompressed, compressionFormat, rgbBitCount, reader, loadSurfaceFormat, ref localMipData, out int numBytes); mipData = localMipData; tex.SetData(i, null, localMipData, 0, numBytes); } texture = tex; } } }
//try to evaluate the xna compatible surface for the present data private static SurfaceFormat SurfaceFormatFromLoadFormat(LoadSurfaceFormat loadSurfaceFormat, FourCC compressionFormat, uint pixelFlags, int rgbBitCount) { switch (loadSurfaceFormat) { case LoadSurfaceFormat.Alpha8: return(SurfaceFormat.Alpha8); case LoadSurfaceFormat.Bgr565: return(SurfaceFormat.Bgr565); case LoadSurfaceFormat.Bgra4444: return(SurfaceFormat.Bgra4444); case LoadSurfaceFormat.Bgra5551: return(SurfaceFormat.Bgra5551); case LoadSurfaceFormat.A8R8G8B8: return(SurfaceFormat.Color); case LoadSurfaceFormat.Dxt1: return(SurfaceFormat.Dxt1); case LoadSurfaceFormat.Dxt3: return(SurfaceFormat.Dxt3); case LoadSurfaceFormat.Dxt5: return(SurfaceFormat.Dxt5); case LoadSurfaceFormat.R8G8B8: return(SurfaceFormat.Color); //Updated at load time to X8R8B8B8 case LoadSurfaceFormat.X8B8G8R8: return(SurfaceFormat.Color); case LoadSurfaceFormat.X8R8G8B8: return(SurfaceFormat.Color); case LoadSurfaceFormat.A8B8G8R8: return(SurfaceFormat.Color); case LoadSurfaceFormat.R32F: return(SurfaceFormat.Single); case LoadSurfaceFormat.A32B32G32R32F: return(SurfaceFormat.Vector4); case LoadSurfaceFormat.G32R32F: return(SurfaceFormat.Vector2); case LoadSurfaceFormat.R16F: return(SurfaceFormat.HalfSingle); case LoadSurfaceFormat.G16R16F: return(SurfaceFormat.HalfVector2); case LoadSurfaceFormat.A16B16G16R16F: return(SurfaceFormat.HalfVector4); case LoadSurfaceFormat.CxV8U8: return(SurfaceFormat.NormalizedByte2); case LoadSurfaceFormat.Q8W8V8U8: return(SurfaceFormat.NormalizedByte4); case LoadSurfaceFormat.G16R16: return(SurfaceFormat.Rg32); case LoadSurfaceFormat.A2B10G10R10: return(SurfaceFormat.Rgba1010102); case LoadSurfaceFormat.A16B16G16R16: return(SurfaceFormat.Rgba64); case LoadSurfaceFormat.Unknown: switch (compressionFormat) { case FourCC.D3DFMT_DXT1: return(SurfaceFormat.Dxt1); case FourCC.D3DFMT_DXT3: return(SurfaceFormat.Dxt3); case FourCC.D3DFMT_DXT5: return(SurfaceFormat.Dxt5); case 0: switch (rgbBitCount) { case 8: return(SurfaceFormat.Alpha8); case 16: return(HasAlphaTest(pixelFlags) ? SurfaceFormat.Bgr565 : SurfaceFormat.Bgra4444); case 24: case 32: return(SurfaceFormat.Color); } break; default: throw new InvalidDataException("Unsupported format"); } ; break; default: throw new InvalidDataException(loadSurfaceFormat.ToString() + " is an unsuported format"); } throw new InvalidDataException("Unsupported format"); }
//Get the byte data from a mip-map level. private static void GetMipMaps(int offsetInStream, int map, int width, int height, bool isCompressed, FourCC compressionFormat, int rgbBitCount, BinaryReader reader, LoadSurfaceFormat loadSurfaceFormat, ref byte[] data, out int numBytes) { int seek = 128 + offsetInStream; for (int i = 0; i < map; i++) { seek += MipMapSizeInBytes(i, width, height, isCompressed, compressionFormat, rgbBitCount); } reader.BaseStream.Seek(seek, SeekOrigin.Begin); numBytes = MipMapSizeInBytes(map, width, height, isCompressed, compressionFormat, rgbBitCount); if (data == null || data.Length < numBytes) { data = new byte[numBytes]; } if (!isCompressed && rgbBitCount == 24) { numBytes += (numBytes / 3); } if (!isCompressed && loadSurfaceFormat == LoadSurfaceFormat.R8G8B8) { for (int i = 0; i < numBytes; i += 4) { data[i] = reader.ReadByte(); data[i + 1] = reader.ReadByte(); data[i + 2] = reader.ReadByte(); data[i + 3] = 255; } } else { reader.Read(data, 0, numBytes); } if (loadSurfaceFormat == LoadSurfaceFormat.X8R8G8B8 || loadSurfaceFormat == LoadSurfaceFormat.X8B8G8R8) { for (int i = 0; i < numBytes; i += 4) { data[i + 3] = 255; } } if (loadSurfaceFormat == LoadSurfaceFormat.A8R8G8B8 || loadSurfaceFormat == LoadSurfaceFormat.X8R8G8B8 || loadSurfaceFormat == LoadSurfaceFormat.R8G8B8) { int bytesPerPixel = (rgbBitCount == 32 || rgbBitCount == 24) ? 4 : 3; byte g, b; if (bytesPerPixel == 3) { for (int i = 0; i < numBytes - 2; i += 3) { g = data[i]; b = data[i + 2]; data[i] = b; data[i + 2] = g; } } else { for (int i = 0; i < numBytes - 3; i += 4) { g = data[i]; b = data[i + 2]; data[i] = b; data[i + 2] = g; } } } }