Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CUE4Parse
Submodule CUE4Parse updated 32 files
+4 −1 .github/workflows/tests.yml
+24 −8 CUE4Parse-Conversion/ExportSession.cs
+4 −2 CUE4Parse-Conversion/Exporters/ExporterBase.cs
+3 −3 CUE4Parse-Conversion/Exporters/SceneExporter.cs
+1 −1 CUE4Parse-Conversion/Formats/Materials/GltfMaterialFormat.cs
+1 −1 CUE4Parse-Conversion/Formats/Materials/UsdMaterialFormat.cs
+1 −1 CUE4Parse-Conversion/Formats/Meshes/GltfMeshFormat.cs
+1 −1 CUE4Parse-Conversion/Textures/TextureDecoder.cs
+1 −1 CUE4Parse-Conversion/Writers/Gltf/GltfMaterialExporter.cs
+0 −5 CUE4Parse.Tests/Fixtures/Tests/FixtureTestUtilities.cs
+23 −0 CUE4Parse.Tests/Fixtures/Tests/FixtureTextureDecodeTests.cs
+5 −4 CUE4Parse.Tests/Fixtures/UE5_8/README.md
+39 −0 CUE4Parse.Tests/Textures/BCDecoderTests.cs
+105 −0 CUE4Parse.Tests/Textures/BCReferenceDecoder.cs
+11 −5 CUE4Parse/FileProvider/ApkFileProvider.cs
+2 −0 CUE4Parse/FileProvider/Vfs/AbstractVfsFileProvider.cs
+40 −0 CUE4Parse/GameTypes/MonsterHunterOutlanders/Encryption/Aes/MonsterHunterOutlandersAes.cs
+0 −1 CUE4Parse/UE4/Assets/Exports/Animation/USkeleton.cs
+4 −2 CUE4Parse/UE4/Assets/Exports/Component/Landscape/FLandscapeTexture2DMipMap.cs
+48 −1 CUE4Parse/UE4/Assets/Exports/Component/Landscape/ULandscapeTextureStorageProviderFactory.cs
+1 −1 CUE4Parse/UE4/Assets/Exports/Material/FMaterialResourceProxyReader.cs
+5 −5 CUE4Parse/UE4/Assets/Exports/Material/UMaterialInterface.cs
+1 −0 CUE4Parse/UE4/Assets/Exports/SkeletalMesh/FSkelMeshSection.cs
+1 −0 CUE4Parse/UE4/Assets/Exports/SkeletalMesh/FStaticLODModel.cs
+1 −0 CUE4Parse/UE4/Assets/Exports/SkeletalMesh/USkeletalMesh.cs
+1 −1 CUE4Parse/UE4/Assets/Exports/StaticMesh/FStaticMeshLODResources.cs
+1 −0 CUE4Parse/UE4/Assets/Exports/StaticMesh/UStaticMesh.cs
+2 −0 CUE4Parse/UE4/Assets/Exports/Texture/FTexturePlatformData.cs
+0 −1 CUE4Parse/UE4/Assets/Exports/UObject.cs
+27 −0 CUE4Parse/UE4/Objects/Meshes/FPositionVertexBuffer.cs
+1 −1 CUE4Parse/UE4/Readers/FByteArchive.cs
+1 −0 CUE4Parse/UE4/Versions/EGame.cs
2 changes: 1 addition & 1 deletion Editor/ImGuiFontTexture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Editor;

public class ImGuiFontTexture() : Texture2D(0, 0, SizedInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.UnsignedByte)
public class ImGuiFontTexture() : Texture2D(0, 0, SizedInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte)
{
protected override bool CanBeViewed => false; // a preview is opaque, the glyphs need their alpha

Expand Down
15 changes: 5 additions & 10 deletions Editor/Managers/ImGuiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Snooper.Core.Containers;
using Snooper.Core.Containers.Buffers;
using Snooper.Core.Containers.Programs;
using Snooper.Core.Containers.Resources;
using Snooper.UI;
using ErrorCode = OpenTK.Graphics.OpenGL4.ErrorCode;

Expand All @@ -37,17 +38,11 @@ public void Load()
_ebo.Allocate(45000);
_vbo.Allocate(30000);

GL.VertexArrayVertexBuffer(_vao, 0, _vbo, 0, _vbo.Stride);
GL.VertexArrayElementBuffer(_vao, _ebo);
GL.VertexArrayAttribFormat(_vao, 0, 2, VertexAttribType.Float, false, 0);
GL.VertexArrayAttribFormat(_vao, 1, 2, VertexAttribType.Float, false, 8);
GL.VertexArrayAttribFormat(_vao, 2, 4, VertexAttribType.UnsignedByte, true, 16);
GL.EnableVertexArrayAttrib(_vao, 0);
GL.EnableVertexArrayAttrib(_vao, 1);
GL.EnableVertexArrayAttrib(_vao, 2);
GL.VertexArrayAttribBinding(_vao, 0, 0);
GL.VertexArrayAttribBinding(_vao, 1, 0);
GL.VertexArrayAttribBinding(_vao, 2, 0);
new VertexArrayLayout(_vao, _vbo, _vbo.Stride)
.Float(0, 2)
.Float(1, 2, offset: 8)
.Float(2, 4, VertexAttribType.UnsignedByte, true, 16);

_fontTexture.Generate();

Expand Down
182 changes: 24 additions & 158 deletions Snooper/Core/Containers/Textures/ITextureFormatInfo.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
using OpenTK.Graphics.OpenGL4;
using Snooper.Extensions;
using CUE4Parse.UE4.Assets.Exports.Texture;
using OpenTK.Graphics.OpenGL4;

namespace Snooper.Core.Containers.Textures;

public interface ITextureFormatInfo
{
public SizedInternalFormat InternalFormat { get; }
public SizedInternalFormat LinearFormat { get; }

public long GetMemorySize(int width, int height, int depth = 1);

public bool IsSrgb => InternalFormat.IsSrgb();
public bool IsSrgb => InternalFormat != LinearFormat;
}

public readonly struct TextureFormatInfo : ITextureFormatInfo
{
public SizedInternalFormat InternalFormat { get; }
public SizedInternalFormat LinearFormat { get; }
public readonly PixelFormat Format;
public readonly PixelType Type;

private readonly int _bytesPerPixel;
private readonly string? _name;

public TextureFormatInfo(SizedInternalFormat internalFormat, PixelFormat format, PixelType type)
public TextureFormatInfo(SizedInternalFormat internalFormat, PixelFormat format, PixelType type, SizedInternalFormat? linearFormat = null, string? name = null)
{
InternalFormat = internalFormat;
LinearFormat = linearFormat ?? internalFormat;
Format = format;
Type = type;
_name = name;

_bytesPerPixel = GetBytesPerPixel();
}
Expand All @@ -36,186 +41,47 @@ public long GetMemorySize(int width, int height, int depth = 1)

private int GetBytesPerPixel()
{
return InternalFormat switch
return LinearFormat switch
{
// 8-bit formats
SizedInternalFormat.R8 => 1,
SizedInternalFormat.Rg8 => 2,
SizedInternalFormat.Rg8 or SizedInternalFormat.Rg8Snorm => 2,
SizedInternalFormat.Rgb8 or SizedInternalFormat.Srgb8 => 3,
SizedInternalFormat.Rgba8 or SizedInternalFormat.Srgb8Alpha8 => 4,

// 16-bit formats
SizedInternalFormat.R16 => 2,
SizedInternalFormat.R16f => 2,
SizedInternalFormat.Rg16 => 4,
SizedInternalFormat.Rg16f => 4,
SizedInternalFormat.R16 or SizedInternalFormat.R16f => 2,
SizedInternalFormat.Rg16 or SizedInternalFormat.Rg16f => 4,
SizedInternalFormat.Rgb16 or SizedInternalFormat.Rgb16f => 6,
SizedInternalFormat.Rgba16 or SizedInternalFormat.Rgba16f => 8,

// 32-bit formats
SizedInternalFormat.R32f => 4,
SizedInternalFormat.R32f or SizedInternalFormat.R32ui => 4,
SizedInternalFormat.Rg32f => 8,
SizedInternalFormat.Rgb32f => 12,
SizedInternalFormat.Rgba32f => 16,

// Depth and stencil formats
SizedInternalFormat.DepthComponent16 => 2,
SizedInternalFormat.DepthComponent24 => 3,
SizedInternalFormat.DepthComponent32f => 4,
SizedInternalFormat.Depth24Stencil8 => 4,
SizedInternalFormat.DepthComponent32f or SizedInternalFormat.Depth24Stencil8 => 4,
SizedInternalFormat.Depth32fStencil8 => 5,

_ => Type switch
{
PixelType.UnsignedByte => 4, // Default RGBA8
PixelType.Float => 16, // Default RGBA32F
PixelType.HalfFloat => 8, // Default RGBA16F
PixelType.UnsignedShort => 8, // Default RGBA16
_ => 4
}
_ => 0
};
}

public override string ToString()
{
var name = InternalFormat switch
{
SizedInternalFormat.R8 => "R8",
SizedInternalFormat.Rg8 => "RG8",
SizedInternalFormat.Rgb8 or SizedInternalFormat.Srgb8 => "RGB8",
SizedInternalFormat.Rgba8 or SizedInternalFormat.Srgb8Alpha8 => "RGBA8",

SizedInternalFormat.R16 => "R16",
SizedInternalFormat.R16f => "R16F",
SizedInternalFormat.Rg16 => "RG16",
SizedInternalFormat.Rg16f => "RG16F",
SizedInternalFormat.Rgb16 => "RGB16",
SizedInternalFormat.Rgb16f => "RGB16F",
SizedInternalFormat.Rgba16 => "RGBA16",
SizedInternalFormat.Rgba16f => "RGBA16F",

SizedInternalFormat.R32f => "R32F",
SizedInternalFormat.R32ui => "R32UI",
SizedInternalFormat.Rg32f => "RG32F",
SizedInternalFormat.Rgb32f => "RGB32F",
SizedInternalFormat.Rgba32f => "RGBA32F",

SizedInternalFormat.DepthComponent16 => "D16",
SizedInternalFormat.DepthComponent24 => "D24",
SizedInternalFormat.DepthComponent32f => "D32F",
SizedInternalFormat.Depth24Stencil8 => "D24S8",
SizedInternalFormat.Depth32fStencil8 => "D32FS8",

_ => InternalFormat.ToString()
};

return Format switch
{
PixelFormat.Bgra => $"BGRA -> {name}",
PixelFormat.Bgr => $"BGR -> {name}",
_ => name
};
}
public override string ToString() => _name ?? InternalFormat.ToString();
}

public readonly struct CompressedTextureFormatInfo : ITextureFormatInfo
public readonly struct CompressedTextureFormatInfo(SizedInternalFormat internalFormat, SizedInternalFormat linearFormat, FPixelFormatInfo blocks) : ITextureFormatInfo
{
public SizedInternalFormat InternalFormat { get; }

private readonly int _blockSize;
private readonly int _blockWidth;
private readonly int _blockHeight;

public CompressedTextureFormatInfo(SizedInternalFormat internalFormat)
{
InternalFormat = internalFormat;

_blockSize = GetBlockSize();
(_blockWidth, _blockHeight) = GetBlockDimensions();
}
public SizedInternalFormat InternalFormat { get; } = internalFormat;
public SizedInternalFormat LinearFormat { get; } = linearFormat;

public long GetMemorySize(int width, int height, int depth = 1)
{
var blocksWide = (width + _blockWidth - 1) / _blockWidth;
var blocksHigh = (height + _blockHeight - 1) / _blockHeight;

return (long)blocksWide * blocksHigh * depth * _blockSize;
}

private int GetBlockSize()
{
return InternalFormat switch
{
SizedInternalFormat.CompressedRgbaS3tcDxt1Ext or
SizedInternalFormat.CompressedSrgbAlphaS3tcDxt1Ext or
SizedInternalFormat.CompressedRedRgtc1 => 8,

SizedInternalFormat.CompressedRgbaS3tcDxt3Ext or
SizedInternalFormat.CompressedSrgbAlphaS3tcDxt3Ext or
SizedInternalFormat.CompressedRgbaS3tcDxt5Ext or
SizedInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext or
SizedInternalFormat.CompressedRgRgtc2 or
SizedInternalFormat.CompressedRgbBptcUnsignedFloat or
SizedInternalFormat.CompressedRgbaBptcUnorm => 16,

_ when IsAstcFormat() => 16,

_ when IsEtc2RgbFormat() => 8,
_ when IsEtc2RgbaFormat() => 16,

_ => 16
};
}

private (int width, int height) GetBlockDimensions()
{
return InternalFormat switch
{
SizedInternalFormat.CompressedRgbaAstc4X4 or SizedInternalFormat.CompressedSrgb8Alpha8Astc4X4 => (4, 4),
SizedInternalFormat.CompressedRgbaAstc6X6 or SizedInternalFormat.CompressedSrgb8Alpha8Astc6X6 => (6, 6),
SizedInternalFormat.CompressedRgbaAstc8X8 or SizedInternalFormat.CompressedSrgb8Alpha8Astc8X8 => (8, 8),
SizedInternalFormat.CompressedRgbaAstc10X10 or SizedInternalFormat.CompressedSrgb8Alpha8Astc10X10 => (10, 10),
SizedInternalFormat.CompressedRgbaAstc12X12 or SizedInternalFormat.CompressedSrgb8Alpha8Astc12X12 => (12, 12),
var blocksWide = (width + blocks.BlockSizeX - 1) / blocks.BlockSizeX;
var blocksHigh = (height + blocks.BlockSizeY - 1) / blocks.BlockSizeY;

_ => (4, 4)
};
}

private bool IsAstcFormat()
{
return InternalFormat is
SizedInternalFormat.CompressedRgbaAstc4X4 or SizedInternalFormat.CompressedSrgb8Alpha8Astc4X4 or
SizedInternalFormat.CompressedRgbaAstc6X6 or SizedInternalFormat.CompressedSrgb8Alpha8Astc6X6 or
SizedInternalFormat.CompressedRgbaAstc8X8 or SizedInternalFormat.CompressedSrgb8Alpha8Astc8X8 or
SizedInternalFormat.CompressedRgbaAstc10X10 or SizedInternalFormat.CompressedSrgb8Alpha8Astc10X10 or
SizedInternalFormat.CompressedRgbaAstc12X12 or SizedInternalFormat.CompressedSrgb8Alpha8Astc12X12;
return (long)blocksWide * blocksHigh * depth * blocks.BlockBytes;
}

private bool IsEtc2RgbFormat()
{
return InternalFormat is SizedInternalFormat.CompressedRgb8Etc2 or SizedInternalFormat.CompressedSrgb8Etc2;
}

private bool IsEtc2RgbaFormat()
{
return InternalFormat is SizedInternalFormat.CompressedRgba8Etc2Eac or SizedInternalFormat.CompressedSrgb8Alpha8Etc2Eac;
}

public override string ToString() => InternalFormat switch
{
SizedInternalFormat.CompressedRgbaS3tcDxt1Ext or SizedInternalFormat.CompressedSrgbAlphaS3tcDxt1Ext => "BC1 RGB",
SizedInternalFormat.CompressedRgbaS3tcDxt3Ext or SizedInternalFormat.CompressedSrgbAlphaS3tcDxt3Ext => "BC2 RGBA",
SizedInternalFormat.CompressedRgbaS3tcDxt5Ext or SizedInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext => "BC3 RGBA",
SizedInternalFormat.CompressedRedRgtc1 => "BC4 R",
SizedInternalFormat.CompressedRgRgtc2 => "BC5 RG",
SizedInternalFormat.CompressedRgbBptcUnsignedFloat => "BC6H RGB",
SizedInternalFormat.CompressedRgbaBptcUnorm => "BC7 RGBA",

SizedInternalFormat.CompressedRgb8Etc2 or SizedInternalFormat.CompressedSrgb8Etc2 => "ETC2 RGB",
SizedInternalFormat.CompressedRgba8Etc2Eac or SizedInternalFormat.CompressedSrgb8Alpha8Etc2Eac => "ETC2 RGBA",

_ when IsAstcFormat() => $"ASTC {_blockWidth}x{_blockHeight} RGBA",

_ => InternalFormat.ToString()
};
public override string ToString() => blocks.Name;
}
1 change: 1 addition & 0 deletions Snooper/Core/Containers/Textures/Texture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ protected ITextureFormatInfo FormatInfo
}

public bool IsSrgb => FormatInfo.IsSrgb;
public SizedInternalFormat LinearFormat => FormatInfo.LinearFormat;

public string FormatName { get; private set; } = string.Empty;

Expand Down
26 changes: 23 additions & 3 deletions Snooper/Core/Containers/Textures/Texture2D.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CUE4Parse_Conversion.Textures;
using System.Collections.Concurrent;
using CUE4Parse_Conversion.Textures;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.UE4.Objects.Core.Misc;
using OpenTK.Graphics.OpenGL4;
Expand All @@ -23,6 +24,8 @@ public class Texture2D(int width, int height,
private (TextureMinFilter Min, TextureMagFilter Mag, TextureWrapMode WrapS, TextureWrapMode WrapT) _sampling;
private byte[][]? _levels;

private static readonly ConcurrentDictionary<EPixelFormat, byte> _decodedFormats = new();

public Texture2D(UTexture texture) : this(texture.PlatformData.SizeX, texture.PlatformData.SizeY, guid: texture.LightingGuid, name: texture.Name)
{
_owner = texture;
Expand Down Expand Up @@ -98,7 +101,7 @@ public override void Prepare()

Width = textureData.Width;
Height = textureData.Height;
FormatInfo = textureData.PixelFormat.GetTextureFormat(owner.SRGB);
FormatInfo = textureData.PixelFormat.GetTextureFormat(owner.SRGB) ?? throw new NotSupportedException($"{textureData.PixelFormat} can't be uploaded.");
_levels = [textureData.Data];
}
else
Expand All @@ -121,7 +124,6 @@ public override void Prepare()

Width = mips[first].SizeX;
Height = mips[first].SizeY;
FormatInfo = owner.Format.GetTextureFormat(owner.SRGB);

for (var i = first + 1; i < mips.Length && !_terrain; i++)
{
Expand All @@ -131,6 +133,24 @@ public override void Prepare()
levels.Add(data);
}

var format = owner.Format;
if (format.GetTextureFormat(owner.SRGB) is not { } info)
{
var source = PixelFormatUtils.PixelFormats[format];
for (var i = 0; i < levels.Count; i++)
{
TextureDecoder.DecodeBytes(levels[i], Math.Max(1, Width >> i), Math.Max(1, Height >> i), 1, source, owner.IsNormalMap, out var data, out format);
levels[i] = data;
}
info = format.GetTextureFormat(owner.SRGB) ?? throw new NotSupportedException($"{owner.Format} can't be decoded.");

if (_decodedFormats.TryAdd(owner.Format, 0))
{
Log.Information("{Format} can't be uploaded as is, decoding it to {Decoded} on the CPU", owner.Format, format);
}
}
FormatInfo = info;

_levels = [.. levels];

byte[]? Read(int index) => mips[index].BulkData?.ReadDataOnce() is { Length: > 0 } data ? data
Expand Down
3 changes: 1 addition & 2 deletions Snooper/Core/Containers/Textures/TextureView.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using OpenTK.Graphics.OpenGL4;
using Snooper.Extensions;

namespace Snooper.Core.Containers.Textures;

Expand All @@ -20,7 +19,7 @@ public override void Generate()

uint original = source;
GL.GetTextureLevelParameter(original, 0, GetTextureParameter.TextureInternalFormat, out int format);
if (linear) format = (int) ((SizedInternalFormat) format).ToLinear();
if (linear) format = (int) source.LinearFormat;

var handle = GL.GenTexture();
GL.TextureView(handle, source.Target, (int) original, (PixelInternalFormat) format, 0, source.MipCount, 0, 1);
Expand Down
2 changes: 2 additions & 0 deletions Snooper/Core/Hardware/DeviceInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class DeviceInfo

public static bool IsIntel { get; private set; }
public static bool HasFragmentBarycentric { get; private set; }
public static bool HasAstc { get; private set; }
public static string GlslDefines { get; private set; } = string.Empty;
public static string FragmentGlslDefines { get; private set; } = string.Empty;

Expand All @@ -33,6 +34,7 @@ public void Load()

IsIntel = Vendor.Contains("Intel", StringComparison.OrdinalIgnoreCase);
HasFragmentBarycentric = ExtensionSupport.SupportFragmentBarycentric;
HasAstc = ExtensionSupport.SupportAstcLdr;
GlslDefines = IsIntel ? "#define BINDLESS_RAW_HANDLES\n" : string.Empty;
FragmentGlslDefines = HasFragmentBarycentric ? "#extension GL_NV_fragment_shader_barycentric : require\n#define WIREFRAME\n" : string.Empty;
}
Expand Down
Loading
Loading