1
0
mirror of synced 2024-12-03 11:37:21 +01:00
Switch-Toolbox/Switch_Toolbox_Library/Rendering/RenderableTex.cs
KillzXGaming 33456d0295 More fixes
Limit the texture renderer to prevent memory issues.
Prevent textures to be loaded and bound if gl textures cannot load.
Fix gen bntx types (PC) from having inaccuate target set.
2019-08-18 20:10:29 -04:00

409 lines
17 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Threading.Tasks;
using OpenTK.Graphics.OpenGL;
using System.Runtime.InteropServices;
namespace Toolbox.Library.Rendering
{
//Parts roughly based on this helpful gl wrapper https://github.com/ScanMountGoat/SFGraphics/blob/89f96b754e17078153315a259baef3859ef5984d/Projects/SFGraphics/GLObjects/Textures/Texture.cs
public class RenderableTex
{
public int width, height;
public int TexID;
public PixelInternalFormat pixelInternalFormat;
public PixelFormat pixelFormat;
public PixelType pixelType = PixelType.UnsignedByte;
public TextureTarget TextureTarget = TextureTarget.Texture2D;
public bool GLInitialized = false;
public int ImageSize;
public bool IsCubeMap = false;
public TextureWrapMode TextureWrapS
{
get { return textureWrapS; }
set
{
textureWrapS = value;
SetTextureParameter(TextureParameterName.TextureWrapS, (int)value);
}
}
public TextureWrapMode TextureWrapT
{
get { return textureWrapT; }
set
{
textureWrapT = value;
SetTextureParameter(TextureParameterName.TextureWrapT, (int)value);
}
}
public TextureWrapMode TextureWrapR
{
get { return textureWrapR; }
set
{
textureWrapR = value;
SetTextureParameter(TextureParameterName.TextureWrapR, (int)value);
}
}
public TextureMinFilter TextureMinFilter
{
get { return textureMinFilter; }
set
{
textureMinFilter = value;
SetTextureParameter(TextureParameterName.TextureMinFilter, (int)value);
}
}
public TextureMagFilter TextureMagFilter
{
get { return textureMagFilter; }
set
{
textureMagFilter = value;
SetTextureParameter(TextureParameterName.TextureMinFilter, (int)value);
}
}
private TextureWrapMode textureWrapS = TextureWrapMode.Repeat;
private TextureWrapMode textureWrapT = TextureWrapMode.Repeat;
private TextureWrapMode textureWrapR = TextureWrapMode.Clamp;
private TextureMinFilter textureMinFilter = TextureMinFilter.Linear;
private TextureMagFilter textureMagFilter = TextureMagFilter.Linear;
public void Dispose()
{
GL.DeleteTexture(TexID);
}
public void SetTextureParameter(TextureParameterName param, int value)
{
Bind();
GL.TexParameter(TextureTarget, param, value);
}
private bool UseMipmaps = false;
public void LoadOpenGLTexture(STGenericTexture GenericTexture, int ArrayStartIndex = 0, bool LoadArrayLevels = false)
{
if (!Runtime.OpenTKInitialized || GLInitialized || Runtime.UseLegacyGL)
return;
width = (int)GenericTexture.Width;
height = (int)GenericTexture.Height;
if (Runtime.DisableLoadingGLHighResTextures)
{
if (width >= 3000 || height >= 3000)
return;
}
if (GenericTexture.ArrayCount == 0)
GenericTexture.ArrayCount = 1;
List<STGenericTexture.Surface> Surfaces = new List<STGenericTexture.Surface>();
if (UseMipmaps && GenericTexture.ArrayCount <= 1)
{
//Load surfaces with mip maps
Surfaces = GenericTexture.GetSurfaces(ArrayStartIndex, false, 6);
}
else
{
//Only load first mip level. Will be generated after
for (int i = 0; i < GenericTexture.ArrayCount; i++)
{
if (i >= ArrayStartIndex && i <= ArrayStartIndex + 6) //Only load up to 6 faces
{
Surfaces.Add(new STGenericTexture.Surface()
{
mipmaps = new List<byte[]>() { GenericTexture.GetImageData(i, 0) }
});
}
}
}
if (Surfaces.Count == 0 || Surfaces[0].mipmaps[0].Length == 0)
return;
IsCubeMap = Surfaces.Count == 6;
ImageSize = Surfaces[0].mipmaps[0].Length;
if (IsCubeMap)
{
TextureTarget = TextureTarget.TextureCubeMap;
TextureWrapS = TextureWrapMode.Clamp;
TextureWrapT = TextureWrapMode.Clamp;
TextureWrapR = TextureWrapMode.Clamp;
TextureMinFilter = TextureMinFilter.LinearMipmapLinear;
TextureMagFilter = TextureMagFilter.Linear;
}
switch (GenericTexture.Format)
{
case TEX_FORMAT.BC1_UNORM:
pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt1Ext;
break;
case TEX_FORMAT.BC1_UNORM_SRGB:
pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt1Ext;
break;
case TEX_FORMAT.BC2_UNORM:
pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt3Ext;
break;
case TEX_FORMAT.BC2_UNORM_SRGB:
pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt3Ext;
break;
case TEX_FORMAT.BC3_UNORM:
pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt5Ext;
break;
case TEX_FORMAT.BC3_UNORM_SRGB:
pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt5Ext;
break;
case TEX_FORMAT.BC4_UNORM:
case TEX_FORMAT.BC4_SNORM:
//Convert to rgb to prevent red output
//While shaders could prevent this, converting is easier and works fine across all editors
if (Runtime.UseDirectXTexDecoder)
{
pixelInternalFormat = PixelInternalFormat.Rgba;
pixelFormat = OpenTK.Graphics.OpenGL.PixelFormat.Rgba;
}
else
{
pixelInternalFormat = PixelInternalFormat.CompressedRedRgtc1;
pixelInternalFormat = PixelInternalFormat.CompressedSignedRedRgtc1;
}
break;
case TEX_FORMAT.BC5_SNORM:
pixelInternalFormat = PixelInternalFormat.Rgba;
pixelFormat = OpenTK.Graphics.OpenGL.PixelFormat.Rgba;
break;
case TEX_FORMAT.BC5_UNORM:
pixelInternalFormat = PixelInternalFormat.CompressedRgRgtc2;
break;
case TEX_FORMAT.BC6H_UF16:
pixelInternalFormat = PixelInternalFormat.CompressedRgbBptcUnsignedFloat;
break;
case TEX_FORMAT.BC6H_SF16:
pixelInternalFormat = PixelInternalFormat.CompressedRgbBptcSignedFloat;
break;
case TEX_FORMAT.BC7_UNORM:
pixelInternalFormat = PixelInternalFormat.CompressedRgbaBptcUnorm;
break;
case TEX_FORMAT.BC7_UNORM_SRGB:
pixelInternalFormat = PixelInternalFormat.CompressedSrgbAlphaBptcUnorm;
break;
case TEX_FORMAT.R8G8B8A8_UNORM:
pixelInternalFormat = PixelInternalFormat.Rgba;
pixelFormat = OpenTK.Graphics.OpenGL.PixelFormat.Rgba;
break;
case TEX_FORMAT.R8G8B8A8_UNORM_SRGB:
pixelInternalFormat = PixelInternalFormat.Rgba;
pixelFormat = OpenTK.Graphics.OpenGL.PixelFormat.Rgba;
break;
default:
if (Runtime.UseDirectXTexDecoder)
{
pixelInternalFormat = PixelInternalFormat.Rgba;
pixelFormat = OpenTK.Graphics.OpenGL.PixelFormat.Rgba;
}
break;
}
GLInitialized = true;
for (int i = 0; i < Surfaces.Count; i++)
{
for (int MipLevel = 0; MipLevel < Surfaces[i].mipmaps.Count; MipLevel++)
{
uint width = Math.Max(1, GenericTexture.Width >> MipLevel);
uint height = Math.Max(1, GenericTexture.Height >> MipLevel);
Surfaces[i].mipmaps[MipLevel] = DecodeWithoutOpenGLDecoder(Surfaces[i].mipmaps[MipLevel], width, height, GenericTexture);
}
}
TexID = GenerateOpenGLTexture(this, Surfaces);
Surfaces.Clear();
}
private byte[] DecodeWithoutOpenGLDecoder(byte[] ImageData, uint width, uint height, STGenericTexture GenericTexture)
{
switch (GenericTexture.Format)
{
case TEX_FORMAT.BC1_UNORM:
case TEX_FORMAT.BC1_UNORM_SRGB:
case TEX_FORMAT.BC2_UNORM:
case TEX_FORMAT.BC2_UNORM_SRGB:
case TEX_FORMAT.BC3_UNORM:
case TEX_FORMAT.BC3_UNORM_SRGB:
case TEX_FORMAT.BC5_UNORM:
case TEX_FORMAT.BC6H_SF16:
case TEX_FORMAT.BC6H_UF16:
case TEX_FORMAT.BC7_UNORM:
case TEX_FORMAT.BC7_UNORM_SRGB:
case TEX_FORMAT.R8G8B8A8_UNORM:
case TEX_FORMAT.R8G8B8A8_UNORM_SRGB:
return ImageData;
case TEX_FORMAT.BC5_SNORM:
return (DDSCompressor.DecompressBC5(ImageData,
(int)width, (int)height, true, true));
default:
if (Runtime.UseDirectXTexDecoder)
{
return STGenericTexture.DecodeBlock(ImageData,
width,
height,
GenericTexture.Format,
new byte[0],
GenericTexture.Parameters,
PALETTE_FORMAT.None,
GenericTexture.PlatformSwizzle);
}
else
return ImageData;
}
}
public static int GenerateOpenGLTexture(RenderableTex t, List<STGenericTexture.Surface> ImageData)
{
if (!t.GLInitialized)
return -1;
int texID = GL.GenTexture();
GL.BindTexture(t.TextureTarget, texID);
if (t.IsCubeMap)
{
if (t.pixelInternalFormat != PixelInternalFormat.Rgba)
{
for (int mipLevel = 0; mipLevel < ImageData[0].mipmaps.Count; mipLevel++)
{
int width = Math.Max(1, t.width >> mipLevel);
int height = Math.Max(1, t.height >> mipLevel);
t.LoadCompressedMips(TextureTarget.TextureCubeMapPositiveX, ImageData[0], width, height, mipLevel);
t.LoadCompressedMips(TextureTarget.TextureCubeMapNegativeX, ImageData[1], width, height, mipLevel);
t.LoadCompressedMips(TextureTarget.TextureCubeMapPositiveY, ImageData[2], width, height, mipLevel);
t.LoadCompressedMips(TextureTarget.TextureCubeMapNegativeY, ImageData[3], width, height, mipLevel);
t.LoadCompressedMips(TextureTarget.TextureCubeMapPositiveZ, ImageData[4], width, height, mipLevel);
t.LoadCompressedMips(TextureTarget.TextureCubeMapNegativeZ, ImageData[5], width, height, mipLevel);
}
}
else
{
for (int mipLevel = 0; mipLevel < ImageData[0].mipmaps.Count; mipLevel++)
{
int width = Math.Max(1, t.width >> mipLevel);
int height = Math.Max(1, t.height >> mipLevel);
t.LoadUncompressedMips(TextureTarget.TextureCubeMapPositiveX, ImageData[0], width, height, mipLevel);
t.LoadUncompressedMips(TextureTarget.TextureCubeMapNegativeX, ImageData[1], width, height, mipLevel);
t.LoadUncompressedMips(TextureTarget.TextureCubeMapPositiveY, ImageData[2], width, height, mipLevel);
t.LoadUncompressedMips(TextureTarget.TextureCubeMapNegativeY, ImageData[3], width, height, mipLevel);
t.LoadUncompressedMips(TextureTarget.TextureCubeMapPositiveZ, ImageData[4], width, height, mipLevel);
t.LoadUncompressedMips(TextureTarget.TextureCubeMapNegativeZ, ImageData[5], width, height, mipLevel);
}
}
if (ImageData[0].mipmaps.Count == 1)
GL.GenerateMipmap(GenerateMipmapTarget.TextureCubeMap);
}
else
{
if (t.pixelInternalFormat != PixelInternalFormat.Rgba)
{
for (int mipLevel = 0; mipLevel < ImageData[0].mipmaps.Count; mipLevel++)
t.LoadCompressedMips(t.TextureTarget, ImageData[0], t.width, t.height, mipLevel);
}
else
{
for (int mipLevel = 0; mipLevel < ImageData[0].mipmaps.Count; mipLevel++)
t.LoadUncompressedMips(t.TextureTarget, ImageData[0], t.width, t.height, mipLevel);
}
if (ImageData[0].mipmaps.Count == 1)
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
}
return texID;
}
public void LoadUncompressedMips(TextureTarget textureTarget, STGenericTexture.Surface ImageData,int mipwidth, int mipheight, int MipLevel = 0)
{
GL.TexImage2D<byte>(textureTarget, MipLevel, pixelInternalFormat, mipwidth, mipheight, 0,
pixelFormat, PixelType.UnsignedByte, ImageData.mipmaps[MipLevel]);
// GL.GenerateMipmap((GenerateMipmapTarget)textureTarget);
}
public void LoadCompressedMips(TextureTarget textureTarget, STGenericTexture.Surface ImageData, int mipwidth, int mipheight, int MipLevel = 0)
{
GL.CompressedTexImage2D<byte>(textureTarget, MipLevel, (InternalFormat)pixelInternalFormat,
mipwidth, mipheight, 0, getImageSize(this), ImageData.mipmaps[MipLevel]);
// GL.GenerateMipmap((GenerateMipmapTarget)textureTarget);
}
public void Bind()
{
GL.BindTexture(TextureTarget, TexID);
}
private static int getImageSize(RenderableTex t)
{
switch (t.pixelInternalFormat)
{
case PixelInternalFormat.CompressedRgbaS3tcDxt1Ext:
case PixelInternalFormat.CompressedSrgbAlphaS3tcDxt1Ext:
case PixelInternalFormat.CompressedRedRgtc1:
case PixelInternalFormat.CompressedSignedRedRgtc1:
return (t.width * t.height / 2);
case PixelInternalFormat.CompressedRgbaS3tcDxt3Ext:
case PixelInternalFormat.CompressedSrgbAlphaS3tcDxt3Ext:
case PixelInternalFormat.CompressedRgbaS3tcDxt5Ext:
case PixelInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext:
case PixelInternalFormat.CompressedSignedRgRgtc2:
case PixelInternalFormat.CompressedRgRgtc2:
case PixelInternalFormat.CompressedRgbaBptcUnorm:
case PixelInternalFormat.CompressedSrgbAlphaBptcUnorm:
return (t.width * t.height);
case PixelInternalFormat.Rgba:
return t.ImageSize;
default:
return t.ImageSize;
}
}
public unsafe Bitmap ToBitmap()
{
Bitmap bitmap = new Bitmap(width, height);
System.Drawing.Imaging.BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.BindTexture(TextureTarget.Texture2D, TexID);
GL.GetTexImage(TextureTarget.Texture2D, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bitmapData.Scan0);
bitmap.UnlockBits(bitmapData);
return bitmap;
}
public static unsafe Bitmap GLTextureToBitmap(RenderableTex t)
{
Bitmap bitmap = new Bitmap(t.width, t.height);
System.Drawing.Imaging.BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, t.width, t.height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.BindTexture(TextureTarget.Texture2D, t.TexID);
GL.GetTexImage(TextureTarget.Texture2D, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bitmapData.Scan0);
bitmap.UnlockBits(bitmapData);
return bitmap;
}
}
}