delay the setup phase of textures and materials

This commit is contained in:
Asval 2025-05-10 23:18:13 +02:00
parent cc2ff71759
commit 8303cc6cf1
35 changed files with 1066 additions and 756 deletions

@ -1 +1 @@
Subproject commit 340b7567fdc2ca050d9042389d09540be1044159
Subproject commit 691b9b93ced53f723119106bc75094da77a4eaf3

View File

@ -59,7 +59,7 @@ public class BaseBundle : UCreator
foreach (var reward in rewards)
{
if (!reward.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition")) continue;
quest.AddCompletionRequest(itemDefinition);
quest.AddCompletionReward(itemDefinition);
}
_quests.Add(quest);
}

View File

@ -44,12 +44,12 @@ public class BaseQuest : BaseIcon
DisplayName = ReformatString(description, completionCount.ToString(), completionCount < 0);
}
public void AddCompletionRequest(FSoftObjectPath itemDefinition)
public void AddCompletionReward(FSoftObjectPath itemDefinition)
{
_rewards.Add(itemDefinition.TryLoad(out UObject uObject) ? new Reward(uObject) : new Reward());
}
public void AddCompletionRequest(int quantity, string reward)
public void AddCompletionReward(int quantity, string reward)
{
_rewards.Add(new Reward(quantity, reward));
}

View File

@ -164,7 +164,7 @@
<PackageReference Include="RestSharp" Version="112.1.0" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.8" />
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.88.9" />
<PackageReference Include="SkiaSharp.Svg" Version="1.60.0" />
<PackageReference Include="Twizzle.ImGui-Bundle.NET" Version="1.91.5.2" />

View File

@ -63,7 +63,6 @@ public partial class MainWindow
await ApplicationViewModel.InitOodle();
await ApplicationViewModel.InitZlib();
await ApplicationViewModel.InitDetex();
await _applicationView.CUE4Parse.Initialize();
await _applicationView.AesManager.InitAes();
await _applicationView.UpdateProvider(true);
@ -74,6 +73,7 @@ public partial class MainWindow
_applicationView.CUE4Parse.VerifyConsoleVariables(),
_applicationView.CUE4Parse.VerifyOnDemandArchives(),
_applicationView.CUE4Parse.InitMappings(),
ApplicationViewModel.InitDetex(),
ApplicationViewModel.InitVgmStream(),
ApplicationViewModel.InitImGuiSettings(newOrUpdated),
Task.Run(() =>
@ -86,7 +86,10 @@ public partial class MainWindow
#if DEBUG
// await _threadWorkerView.Begin(cancellationToken =>
// _applicationView.CUE4Parse.Extract(cancellationToken,
// _applicationView.CUE4Parse.Provider["Marvel/Content/Marvel/Wwise/Assets/Events/Music/music_new/event/Entry.uasset"]));
// _applicationView.CUE4Parse.Provider["FortniteGame/Plugins/GameFeatures/BRCosmetics/Content/Characters/Player/Male/Medium/Bodies/M_MED_Lunch_Box/Meshes/M_MED_Lunch_Box.uasset"]));
await _threadWorkerView.Begin(cancellationToken =>
_applicationView.CUE4Parse.Extract(cancellationToken,
_applicationView.CUE4Parse.Provider["FortniteGame/Plugins/GameFeatures/BlastBerryMap/Content/Maps/BlastBerry_Terrain/_Generated_/D5V3O7ZS9ZLQKFKU6WFZ27OYW.umap"]));
#endif
}

View File

@ -106,7 +106,11 @@ public class FModelApiEndpoint : AbstractApiProvider
public void CheckForUpdates(bool launch = false)
{
if (DateTime.Now < UserSettings.Default.NextUpdateCheck) return;
if (DateTime.Now < UserSettings.Default.NextUpdateCheck)
{
Log.Warning("Updates have been silenced until {DateTime}", UserSettings.Default.NextUpdateCheck);
return;
}
if (launch)
{
@ -140,7 +144,8 @@ public class FModelApiEndpoint : AbstractApiProvider
{
UserSettings.Default.LastUpdateCheck = DateTime.Now;
if (((CustomMandatory)args.Mandatory).CommitHash == Constants.APP_COMMIT_ID)
var targetHash = ((CustomMandatory) args.Mandatory).CommitHash;
if (targetHash == Constants.APP_COMMIT_ID)
{
if (UserSettings.Default.ShowChangelog)
ShowChangelog(args);
@ -152,6 +157,7 @@ public class FModelApiEndpoint : AbstractApiProvider
UserSettings.Default.ShowChangelog = currentVersion != args.InstalledVersion;
const string message = "A new update is available!";
Log.Warning("{message} Version {CurrentVersion} ({Hash})", message, currentVersion, targetHash);
Helper.OpenWindow<AdonisWindow>(message, () => new UpdateView { Title = message, ResizeMode = ResizeMode.NoResize }.ShowDialog());
}
else

View File

@ -260,7 +260,7 @@ public class ApplicationViewModel : ViewModel
ZlibHelper.Initialize(zlibPath);
}
public static async ValueTask InitDetex()
public static async Task InitDetex()
{
var detexPath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", DetexHelper.DLL_NAME);
if (File.Exists(DetexHelper.DLL_NAME))

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Numerics;
using FModel.Views.Snooper.Shading;
using FModel.Views.Snooper.Textures;
using ImGuiNET;
namespace FModel.Views.Snooper.Animations;
@ -61,7 +62,7 @@ public class TimeTracker : IDisposable
}
private readonly string[] _icons = { "tl_forward", "tl_pause", "tl_rewind" };
public void ImGuiTimeline(Snooper s, Save saver, Dictionary<string, Texture> icons, List<Animation> animations, Vector2 outliner, ImFontPtr fontPtr)
public void ImGuiTimeline(Snooper s, Save saver, Dictionary<string, ITexture> icons, List<Animation> animations, Vector2 outliner, ImFontPtr fontPtr)
{
var dpiScale = ImGui.GetWindowDpiScale();
var thickness = 2.0f * dpiScale;

View File

@ -0,0 +1,266 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CUE4Parse_Conversion.Meshes;
using CUE4Parse_Conversion.Textures;
using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Assets.Exports.Animation;
using CUE4Parse.UE4.Assets.Exports.Component.SplineMesh;
using CUE4Parse.UE4.Assets.Exports.Material;
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.UE4.Objects.Core.Misc;
using CUE4Parse.UE4.Objects.UObject;
using FModel.Settings;
using FModel.Views.Snooper.Models;
using FModel.Views.Snooper.Shading;
using FModel.Views.Snooper.Textures;
namespace FModel.Views.Snooper;
public class AssetPool
{
public static AssetPool Get() => _singleton ??= new AssetPool();
private static AssetPool _singleton;
public FGuid SelectedModel { get; private set; }
public readonly ConcurrentBag<IDelayedSetup> Queue;
public readonly Dictionary<FGuid, UModel> Models;
public readonly Dictionary<FGuid, ITexture> Textures;
private readonly string _project;
private AssetPool()
{
Queue = [];
Models = [];
Textures = [];
_project = Services.ApplicationService.ApplicationView.CUE4Parse.Provider.ProjectName.ToUpper();
}
public void OnTick()
{
var current = 0;
while (Queue.TryTake(out var asset))
{
asset.Setup();
switch (asset)
{
case UModel uModel:
Models[asset.Guid] = uModel;
break;
case ITexture texture:
Textures[asset.Guid] = texture;
break;
}
current++;
if (current >= 1) break;
}
foreach (var model in Models.Values)
{
model.Update();
}
}
public void AddModel(UStaticMesh staticMesh)
{
ThreadPool.QueueUserWorkItem(_ =>
{
var guid = staticMesh.LightingGuid;
if (TryGetModel(guid, out var model))
{
model.AddInstance(Transform.Identity);
}
else if (staticMesh.TryConvert(out var mesh))
{
Queue.Add(new StaticModel(staticMesh, mesh) { Guid = guid });
}
});
}
public void AddModel(USkeletalMesh skeletalMesh)
{
ThreadPool.QueueUserWorkItem(_ =>
{
var guid = new FGuid((uint) skeletalMesh.GetFullName().GetHashCode());
if (!Models.ContainsKey(guid) && skeletalMesh.TryConvert(out var mesh))
{
Queue.Add(new SkeletalModel(skeletalMesh, mesh) { Guid = guid });
}
});
}
public void AddModel(USkeleton skeleton)
{
ThreadPool.QueueUserWorkItem(_ =>
{
var guid = skeleton.Guid;
if (!Models.ContainsKey(guid) && skeleton.TryConvert(out var _, out var box))
{
Queue.Add(new SkeletalModel(skeleton, box) { Guid = guid });
}
});
}
public void AddTexture(UTexture texture, bool fix)
{
ThreadPool.QueueUserWorkItem(_ =>
{
var guid = texture.LightingGuid;
if (TryGet<BitmapTexture>(guid, out var _))
{
// do something here??
}
else if (texture.Format != EPixelFormat.PF_BC6H) // BC6H is not supported by Decode thus randomly crashes the app
{
var bitmap = texture switch
{
UTexture2D texture2D => texture2D.Decode(UserSettings.Default.PreviewMaxTextureSize, UserSettings.Default.CurrentDir.TexturePlatform),
UTexture2DArray texture2DArray => texture2DArray.DecodeTextureArray(UserSettings.Default.CurrentDir.TexturePlatform)?.FirstOrDefault(),
_ => texture.Decode(UserSettings.Default.CurrentDir.TexturePlatform)
};
if (bitmap is not null)
{
var t = new BitmapTexture(bitmap.ToSkBitmap(), texture);
if (fix) t.FixChannels(_project);
Queue.Add(t);
}
}
});
}
public void AddModel(IPropertyHolder actor, UObject staticMeshComponent, UStaticMesh staticMesh, Transform transform)
{
ThreadPool.QueueUserWorkItem(_ =>
{
var bSpline = staticMeshComponent is USplineMeshComponent;
var guid = staticMesh.LightingGuid;
if (TryGetModel(guid, out var model))
{
model.AddInstance(transform);
if (bSpline && model is SplineModel splineModel)
splineModel.AddComponent((USplineMeshComponent)staticMeshComponent);
}
else if (staticMesh.TryConvert(out var mesh))
{
model = bSpline ? new SplineModel(staticMesh, mesh, (USplineMeshComponent)staticMeshComponent, transform) : new StaticModel(staticMesh, mesh, transform);
model.Guid = guid;
if (actor.TryGetAllValues(out FPackageIndex[] textureData, "TextureData"))
{
var material = model.Materials.FirstOrDefault();
if (material is { IsUsed: true })
{
for (int j = 0; j < textureData.Length; j++)
{
if (textureData[j]?.Load() is not { } textureDataIdx)
continue;
if (textureDataIdx.TryGetValue(out FPackageIndex overrideMaterial, "OverrideMaterial") &&
overrideMaterial.TryLoad(out var oMaterial) && oMaterial is UMaterialInterface oUnrealMaterial)
material.SwapMaterial(oUnrealMaterial);
WorldTextureData(material, textureDataIdx, "Diffuse", j switch
{
0 => "Diffuse",
> 0 => $"Diffuse_Texture_{j + 1}",
_ => CMaterialParams2.FallbackDiffuse
});
WorldTextureData(material, textureDataIdx, "Normal", j switch
{
0 => "Normals",
> 0 => $"Normals_Texture_{j + 1}",
_ => CMaterialParams2.FallbackNormals
});
WorldTextureData(material, textureDataIdx, "Specular", j switch
{
0 => "SpecularMasks",
> 0 => $"SpecularMasks_{j + 1}",
_ => CMaterialParams2.FallbackNormals
});
}
}
}
if (staticMeshComponent.TryGetValue(out FPackageIndex[] overrideMaterials, "OverrideMaterials"))
{
for (var j = 0; j < overrideMaterials.Length && j < model.Sections.Length; j++)
{
var matIndex = model.Sections[j].MaterialIndex;
if (matIndex < 0 || matIndex >= model.Materials.Length || matIndex >= overrideMaterials.Length ||
overrideMaterials[matIndex].Load() is not UMaterialInterface unrealMaterial) continue;
model.Materials[matIndex].SwapMaterial(unrealMaterial);
}
}
Queue.Add(model);
}
});
void WorldTextureData(Material material, UObject textureData, string name, string key)
{
if (textureData.TryGetValue(out FPackageIndex package, name) && package.Load() is UTexture2D texture)
material.Parameters.Textures[key] = texture;
}
}
public bool TryGet<T>(FGuid guid, [MaybeNullWhen(true)] out T asset)
{
if (Models.TryGetValue(guid, out var m) && m is T model)
{
asset = model;
return true;
}
if (Textures.TryGetValue(guid, out var t) && t is T texture)
{
asset = texture;
return true;
}
foreach (var a in Queue)
{
if (a.Guid == guid && a is T value)
{
asset = value;
return true;
}
}
asset = default;
return false;
}
public bool TryGetModel([MaybeNullWhen(false)] out UModel model) => TryGet(SelectedModel, out model);
public bool TryGetModel(FGuid guid, [MaybeNullWhen(false)] out UModel model) => TryGet(guid, out model);
public bool TryGetTexture(FGuid guid, [MaybeNullWhen(false)] out ITexture texture) => TryGet(guid, out texture);
public void SelectModel(FGuid guid)
{
// unselect old
if (TryGetModel(out var model))
model.IsSelected = false;
// select new
if (!TryGetModel(guid, out model))
SelectedModel = Guid.Empty;
else
{
model.IsSelected = true;
SelectedModel = guid;
}
// SelectedSection = 0;
// SelectedMorph = 0;
}
}

View File

@ -1,5 +1,6 @@
using System;
using FModel.Views.Snooper.Shading;
using FModel.Views.Snooper.Textures;
using OpenTK.Graphics.OpenGL4;
namespace FModel.Views.Snooper.Buffers;
@ -11,15 +12,15 @@ public class FramebufferObject : IDisposable
private int _width;
private int _height;
private readonly MsaaTexture _framebufferTexture;
private readonly RenderbufferObject _renderbuffer;
private readonly FramebufferTexture _postProcessingTexture;
private BufferObject<uint> _ebo;
private BufferObject<float> _vbo;
private VertexArrayObject<float, uint> _vao;
private Shader _shader;
private Texture _framebufferTexture;
private Texture _postProcessingTexture;
public readonly uint[] Indices = { 0, 1, 2, 3, 4, 5 };
public readonly float[] Vertices = {
@ -37,7 +38,9 @@ public class FramebufferObject : IDisposable
{
_width = size.X;
_height = size.Y;
_framebufferTexture = new MsaaTexture((uint) _width, (uint) _height);
_renderbuffer = new RenderbufferObject(_width, _height);
_postProcessingTexture = new FramebufferTexture(_width, _height);
}
public void Setup()
@ -45,8 +48,7 @@ public class FramebufferObject : IDisposable
_framebufferHandle = GL.GenFramebuffer();
Bind();
_framebufferTexture = new Texture((uint) _width, (uint) _height);
_framebufferTexture.Setup();
_renderbuffer.Setup();
_shader = new Shader("framebuffer");
@ -69,7 +71,7 @@ public class FramebufferObject : IDisposable
_postProcessingHandle = GL.GenFramebuffer();
Bind(_postProcessingHandle);
_postProcessingTexture = new Texture(_width, _height);
_postProcessingTexture.Setup();
status = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer);
if (status != FramebufferErrorCode.FramebufferComplete)

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CUE4Parse.UE4.Objects.Core.Misc;
using OpenTK.Graphics.OpenGL4;
using System.Numerics;
@ -54,20 +55,18 @@ public class PickingTexture : IDisposable
Bind(0);
}
public void Render(Matrix4x4 viewMatrix, Matrix4x4 projMatrix, IDictionary<FGuid, UModel> models)
public void Render(Matrix4x4 viewMatrix, Matrix4x4 projMatrix)
{
Bind();
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
_shader.Render(viewMatrix, projMatrix);
foreach ((var guid, var model) in models)
foreach (var model in AssetPool.Get().Models.Values.Where(model => model.IsVisible))
{
_shader.SetUniform("uA", guid.A);
_shader.SetUniform("uB", guid.B);
_shader.SetUniform("uC", guid.C);
_shader.SetUniform("uD", guid.D);
if (!model.IsVisible) continue;
_shader.SetUniform("uA", model.Guid.A);
_shader.SetUniform("uB", model.Guid.B);
_shader.SetUniform("uC", model.Guid.C);
_shader.SetUniform("uD", model.Guid.D);
model.PickingRender(_shader);
}

View File

@ -0,0 +1,11 @@
using System;
using CUE4Parse.UE4.Objects.Core.Misc;
namespace FModel.Views.Snooper;
public interface IDelayedSetup : IDisposable
{
public FGuid Guid { get; }
public void Setup();
}

View File

@ -5,6 +5,7 @@ using CUE4Parse.UE4.Objects.Core.Math;
using CUE4Parse.UE4.Objects.Core.Misc;
using FModel.Views.Snooper.Buffers;
using FModel.Views.Snooper.Shading;
using FModel.Views.Snooper.Textures;
using ImGuiNET;
using OpenTK.Graphics.OpenGL4;
@ -29,14 +30,14 @@ public abstract class Light : IDisposable
1f, -1f, 0
};
public readonly FGuid Model;
public readonly Texture Icon;
public readonly ITexture Icon;
public Transform Transform;
public Vector4 Color;
public float Intensity;
public bool IsSetup;
public Light(Texture icon, UObject light)
public Light(ITexture icon, UObject light)
{
Transform = new Transform
{
@ -52,7 +53,7 @@ public abstract class Light : IDisposable
Intensity = light.GetOrDefault("Intensity", 1.0f);
}
public Light(FGuid model, Texture icon, UObject parent, UObject light, Transform transform)
public Light(FGuid model, ITexture icon, UObject parent, UObject light, Transform transform)
{
Transform = new Transform
{

View File

@ -2,6 +2,7 @@
using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Objects.Core.Misc;
using FModel.Views.Snooper.Shading;
using FModel.Views.Snooper.Textures;
using ImGuiNET;
namespace FModel.Views.Snooper.Lights;
@ -11,7 +12,7 @@ public class PointLight : Light
public float Linear;
public float Quadratic;
public PointLight(Texture icon, UObject point) : base(icon, point)
public PointLight(ITexture icon, UObject point) : base(icon, point)
{
if (!point.TryGetValue(out float radius, "SourceRadius", "AttenuationRadius"))
radius = 1.0f;
@ -21,7 +22,7 @@ public class PointLight : Light
Quadratic = 75.0f / MathF.Pow(radius, 2.0f);
}
public PointLight(FGuid model, Texture icon, UObject parent, UObject point, Transform transform) : base(model, icon, parent, point, transform)
public PointLight(FGuid model, ITexture icon, UObject parent, UObject point, Transform transform) : base(model, icon, parent, point, transform)
{
if (!point.TryGetValue(out float radius, "AttenuationRadius", "SourceRadius"))
radius = 1.0f;

View File

@ -1,6 +1,7 @@
using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Objects.Core.Misc;
using FModel.Views.Snooper.Shading;
using FModel.Views.Snooper.Textures;
using ImGuiNET;
namespace FModel.Views.Snooper.Lights;
@ -11,7 +12,7 @@ public class SpotLight : Light
public float InnerConeAngle;
public float OuterConeAngle;
public SpotLight(Texture icon, UObject spot) : base(icon, spot)
public SpotLight(ITexture icon, UObject spot) : base(icon, spot)
{
if (!spot.TryGetValue(out Attenuation, "SourceRadius", "AttenuationRadius"))
Attenuation = 1.0f;
@ -23,7 +24,7 @@ public class SpotLight : Light
InnerConeAngle = OuterConeAngle - 10;
}
public SpotLight(FGuid model, Texture icon, UObject parent, UObject spot, Transform transform) : base(model, icon, parent, spot, transform)
public SpotLight(FGuid model, ITexture icon, UObject parent, UObject spot, Transform transform) : base(model, icon, parent, spot, transform)
{
if (!spot.TryGetValue(out Attenuation, "AttenuationRadius", "SourceRadius"))
Attenuation = 1.0f;

View File

@ -1,12 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Numerics;
using FModel.Views.Snooper.Buffers;
using FModel.Views.Snooper.Shading;
using FModel.Views.Snooper.Textures;
namespace FModel.Views.Snooper.Models;
public interface IRenderableModel : IDisposable
public interface IRenderableModel : IDelayedSetup
{
protected int Handle { get; set; }
protected BufferObject<uint> Ebo { get; set; }
@ -29,10 +29,7 @@ public interface IRenderableModel : IDisposable
public bool IsSelected { get; set; }
public bool ShowWireframe { get; set; }
public void Setup(Options options);
public void SetupInstances();
public void Render(Shader shader, Texture checker = null, bool outline = false);
public void Render(Shader shader, ITexture checker = null, bool outline = false);
public void PickingRender(Shader shader);
public void Update(Options options);
public void AddInstance(Transform transform);
}

View File

@ -23,9 +23,11 @@ public class Section
Color = Constants.COLOR_PALETTE[MaterialIndex % Constants.PALETTE_LENGTH];
}
public void SetupMaterial(Material material)
public void ValidateMaterial(Material material, int uvCount)
{
material.IsUsed = true;
Show = !material.Parameters.IsNull && !material.Parameters.IsTranslucent;
material.Validate(uvCount);
}
}

View File

@ -131,9 +131,9 @@ public class SkeletalModel : UModel
IsVisible = true;
}
public override void Setup(Options options)
public override void Setup()
{
base.Setup(options);
base.Setup();
Skeleton.Setup();
if (!HasMorphTargets) return;

View File

@ -2,6 +2,7 @@
using System.Numerics;
using FModel.Views.Snooper.Buffers;
using FModel.Views.Snooper.Shading;
using FModel.Views.Snooper.Textures;
using OpenTK.Graphics.OpenGL4;
namespace FModel.Views.Snooper.Models;
@ -16,7 +17,7 @@ public class Skybox : IDisposable
private string[] _textures = { "px", "nx", "py", "ny", "pz", "nz" };
private Texture _cubeMap;
private ITexture _cubeMap;
private Shader _shader;
public readonly uint[] Indices = { 0, 1, 3, 1, 2, 3 };
@ -75,7 +76,7 @@ public class Skybox : IDisposable
_vbo = new BufferObject<float>(Vertices, BufferTarget.ArrayBuffer);
_vao = new VertexArrayObject<float, uint>(_vbo, _ebo);
_cubeMap = new Texture(_textures);
_cubeMap = new ApplicationTexture(_textures);
_shader = new Shader("skybox");
_vao.VertexAttributePointer(0, 3, VertexAttribPointerType.Float, 3, 0); // position

View File

@ -88,9 +88,9 @@ public class SplineModel : StaticModel
_splineParams.Add(new GpuParams(splineMesh));
}
public override void Setup(Options options)
public override void Setup()
{
base.Setup(options);
base.Setup();
_ssbo = new BufferObject<GpuParams>(_splineParams.ToArray(), BufferTarget.ShaderStorageBuffer);
}

View File

@ -9,10 +9,12 @@ using CUE4Parse.UE4.Assets;
using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Assets.Exports.Material;
using CUE4Parse.UE4.Objects.Core.Math;
using CUE4Parse.UE4.Objects.Core.Misc;
using CUE4Parse.Utils;
using FModel.Settings;
using FModel.Views.Snooper.Buffers;
using FModel.Views.Snooper.Shading;
using FModel.Views.Snooper.Textures;
using OpenTK.Graphics.OpenGL4;
namespace FModel.Views.Snooper.Models;
@ -31,15 +33,15 @@ public abstract class UModel : IRenderableModel
private readonly UObject _export;
private readonly List<VertexAttribute> _vertexAttributes =
[
new VertexAttribute { Size = 1, Type = VertexAttribPointerType.Int, Enabled = false }, // VertexIndex
new VertexAttribute { Size = 3, Type = VertexAttribPointerType.Float, Enabled = true }, // Position
new VertexAttribute { Size = 3, Type = VertexAttribPointerType.Float, Enabled = false }, // Normal
new VertexAttribute { Size = 3, Type = VertexAttribPointerType.Float, Enabled = false }, // Tangent
new VertexAttribute { Size = 2, Type = VertexAttribPointerType.Float, Enabled = false }, // UV
new VertexAttribute { Size = 1, Type = VertexAttribPointerType.Float, Enabled = false }, // TextureLayer
new VertexAttribute { Size = 1, Type = VertexAttribPointerType.Float, Enabled = false }, // Colors
new VertexAttribute { Size = 4, Type = VertexAttribPointerType.Float, Enabled = false }, // BoneIds
new VertexAttribute { Size = 4, Type = VertexAttribPointerType.Float, Enabled = false } // BoneWeights
new() { Size = 1, Type = VertexAttribPointerType.Int, Enabled = false }, // VertexIndex
new() { Size = 3, Type = VertexAttribPointerType.Float, Enabled = true }, // Position
new() { Size = 3, Type = VertexAttribPointerType.Float, Enabled = false }, // Normal
new() { Size = 3, Type = VertexAttribPointerType.Float, Enabled = false }, // Tangent
new() { Size = 2, Type = VertexAttribPointerType.Float, Enabled = false }, // UV
new() { Size = 1, Type = VertexAttribPointerType.Float, Enabled = false }, // TextureLayer
new() { Size = 1, Type = VertexAttribPointerType.Float, Enabled = false }, // Colors
new() { Size = 4, Type = VertexAttribPointerType.Float, Enabled = false }, // BoneIds
new() { Size = 4, Type = VertexAttribPointerType.Float, Enabled = false } // BoneWeights
];
public int Handle { get; set; }
@ -48,6 +50,7 @@ public abstract class UModel : IRenderableModel
public BufferObject<Matrix4x4> MatrixVbo { get; set; }
public VertexArrayObject<float, uint> Vao { get; set; }
public FGuid Guid { get; set; }
public string Path { get; }
public string Name { get; }
public string Type { get; protected set; }
@ -177,13 +180,13 @@ public abstract class UModel : IRenderableModel
{
var section = lod.Sections.Value[s];
Sections[s] = new Section(section.MaterialIndex, section.NumFaces * 3, section.FirstIndex);
if (section.IsValid) Sections[s].SetupMaterial(Materials[section.MaterialIndex]);
if (section.IsValid) Sections[s].ValidateMaterial(Materials[section.MaterialIndex], UvCount);
}
AddInstance(transform ?? Transform.Identity);
}
public virtual void Setup(Options options)
public virtual void Setup()
{
Handle = GL.CreateProgram();
Ebo = new BufferObject<uint>(Indices, BufferTarget.ElementArrayBuffer);
@ -211,7 +214,7 @@ public abstract class UModel : IRenderableModel
for (var i = 0; i < Materials.Length; i++)
{
if (!Materials[i].IsUsed) continue;
Materials[i].Setup(options, broken ? 1 : UvCount);
Materials[i].Setup(broken);
}
foreach (var collision in Collisions)
@ -219,7 +222,7 @@ public abstract class UModel : IRenderableModel
collision.Setup();
}
if (options.Models.Count == 1 && Sections.All(x => !x.Show)) // visible if alone and invisible
// if (options.Models.Count == 1 && Sections.All(x => !x.Show)) // visible if alone and invisible
{
IsVisible = true;
foreach (var section in Sections)
@ -227,26 +230,26 @@ public abstract class UModel : IRenderableModel
section.Show = true;
}
}
else if (!IsVisible) // default: visible if one section is visible
{
foreach (var section in Sections)
{
if (section.Show)
{
IsVisible = true;
break;
}
}
}
else foreach (var section in Sections) // force visibility
{
section.Show = true;
}
// else if (!IsVisible) // default: visible if one section is visible
// {
// foreach (var section in Sections)
// {
// if (section.Show)
// {
// IsVisible = true;
// break;
// }
// }
// }
// else foreach (var section in Sections) // force visibility
// {
// section.Show = true;
// }
IsSetup = true;
}
public virtual void Render(Shader shader, Texture checker = null, bool outline = false)
public virtual void Render(Shader shader, ITexture checker = null, bool outline = false)
{
if (outline) GL.Disable(EnableCap.DepthTest);
if (IsTwoSided) GL.Disable(EnableCap.CullFace);
@ -332,7 +335,7 @@ public abstract class UModel : IRenderableModel
shader.SetUniform("uScaleDown", Constants.SCALE_DOWN_RATIO);
}
public void Update(Options options)
public void Update()
{
MatrixVbo.Bind();
for (int instance = 0; instance < TransformsCount; instance++)
@ -353,11 +356,11 @@ public abstract class UModel : IRenderableModel
var socketRelation = boneMatrix * worldMatrix;
foreach (var info in socket.AttachedModels)
{
if (!options.TryGetModel(info.Guid, out var attachedModel))
if (!AssetPool.Get().TryGetModel(info.Guid, out var attachedModel))
continue;
attachedModel.Transforms[info.Instance].Relation = socket.Transform.LocalMatrix * socketRelation;
attachedModel.Update(options);
attachedModel.Update();
}
}
}

View File

@ -9,6 +9,7 @@ using FModel.Views.Snooper.Animations;
using FModel.Views.Snooper.Lights;
using FModel.Views.Snooper.Models;
using FModel.Views.Snooper.Shading;
using FModel.Views.Snooper.Textures;
using SkiaSharp;
namespace FModel.Views.Snooper;
@ -22,49 +23,54 @@ public class Options
public int SelectedAnimation{ get; private set; }
public readonly Dictionary<FGuid, UModel> Models;
public readonly Dictionary<FGuid, Texture> Textures;
public readonly Dictionary<FGuid, ITexture> Textures;
public readonly List<Light> Lights;
public readonly TimeTracker Tracker;
public readonly List<Animation> Animations;
public readonly Dictionary<string, Texture> Icons;
public readonly Dictionary<string, ITexture> Icons;
private readonly string _game;
public Options()
{
Models = new Dictionary<FGuid, UModel>();
Textures = new Dictionary<FGuid, Texture>();
Textures = new Dictionary<FGuid, ITexture>();
Lights = new List<Light>();
Tracker = new TimeTracker();
Animations = new List<Animation>();
Icons = new Dictionary<string, Texture>
Icons = new Dictionary<string, ITexture>
{
["material"] = new ("materialicon"),
["square"] = new ("square"),
["square_off"] = new ("square_off"),
["cube"] = new ("cube"),
["cube_off"] = new ("cube_off"),
["light"] = new ("light"),
["light_off"] = new ("light_off"),
["noimage"] = new ("T_Placeholder_Item_Image"),
["checker"] = new ("checker"),
["pointlight"] = new ("pointlight"),
["spotlight"] = new ("spotlight"),
["link_on"] = new ("link_on"),
["link_off"] = new ("link_off"),
["link_has"] = new ("link_has"),
["tl_play"] = new ("tl_play"),
["tl_pause"] = new ("tl_pause"),
["tl_rewind"] = new ("tl_rewind"),
["tl_forward"] = new ("tl_forward"),
["tl_previous"] = new ("tl_previous"),
["tl_next"] = new ("tl_next"),
["material"] = new ApplicationTexture("materialicon"),
["square"] = new ApplicationTexture("square"),
["square_off"] = new ApplicationTexture("square_off"),
["cube"] = new ApplicationTexture("cube"),
["cube_off"] = new ApplicationTexture("cube_off"),
["light"] = new ApplicationTexture("light"),
["light_off"] = new ApplicationTexture("light_off"),
["noimage"] = new ApplicationTexture("T_Placeholder_Item_Image"),
["checker"] = new ApplicationTexture("checker"),
["pointlight"] = new ApplicationTexture("pointlight"),
["spotlight"] = new ApplicationTexture("spotlight"),
["link_on"] = new ApplicationTexture("link_on"),
["link_off"] = new ApplicationTexture("link_off"),
["link_has"] = new ApplicationTexture("link_has"),
["tl_play"] = new ApplicationTexture("tl_play"),
["tl_pause"] = new ApplicationTexture("tl_pause"),
["tl_rewind"] = new ApplicationTexture("tl_rewind"),
["tl_forward"] = new ApplicationTexture("tl_forward"),
["tl_previous"] = new ApplicationTexture("tl_previous"),
["tl_next"] = new ApplicationTexture("tl_next"),
};
foreach (var icon in Icons.Values)
{
icon.Setup();
}
_game = Services.ApplicationService.ApplicationView.CUE4Parse.Provider.ProjectName.ToUpper();
SelectModel(Guid.Empty);
@ -72,11 +78,11 @@ public class Options
public void SetupModelsAndLights()
{
foreach (var model in Models.Values)
{
if (model.IsSetup) continue;
model.Setup(this);
}
// foreach (var model in Models.Values)
// {
// if (model.IsSetup) continue;
// model.Setup(this);
// }
foreach (var light in Lights)
{
@ -187,29 +193,6 @@ public class Options
model.UpdateMorph(SelectedMorph);
}
public bool TryGetTexture(UTexture o, bool fix, out Texture texture)
{
var guid = o.LightingGuid;
if (Textures.TryGetValue(guid, out texture)) return texture != null;
if (o.Format == EPixelFormat.PF_BC6H) return false; // BC6H is not supported by Decode thus randomly crashes the app
var bitmap = o switch
{
UTexture2D texture2D => texture2D.Decode(UserSettings.Default.PreviewMaxTextureSize, UserSettings.Default.CurrentDir.TexturePlatform),
UTexture2DArray texture2DArray => texture2DArray.DecodeTextureArray(UserSettings.Default.CurrentDir.TexturePlatform)?.FirstOrDefault(),
_ => o.Decode(UserSettings.Default.CurrentDir.TexturePlatform)
};
if (bitmap is not null)
{
texture = new Texture(bitmap.ToSkBitmap(), o);
if (fix) TextureHelper.FixChannels(_game, texture);
Textures[guid] = texture;
}
return texture != null;
}
public bool TryGetModel(out UModel model) => Models.TryGetValue(SelectedModel, out model);
public bool TryGetModel(FGuid guid, out UModel model) => Models.TryGetValue(guid, out model);

View File

@ -86,13 +86,13 @@ public class Renderer : IDisposable
switch (dummy)
{
case UStaticMesh when export.Value is UStaticMesh st:
LoadStaticMesh(st);
AssetPool.Get().AddModel(st);
break;
case USkeletalMesh when export.Value is USkeletalMesh sk:
LoadSkeletalMesh(sk);
AssetPool.Get().AddModel(sk);
break;
case USkeleton when export.Value is USkeleton skel:
LoadSkeleton(skel);
AssetPool.Get().AddModel(skel);
break;
case UMaterialInstance when export.Value is UMaterialInstance mi:
LoadMaterialInstance(mi);
@ -117,7 +117,7 @@ public class Renderer : IDisposable
if (!Options.TryGetModel(out var model) || !Options.TryGetSection(model, out var section)) return;
model.Materials[section.MaterialIndex].SwapMaterial(unrealMaterial);
Application.Current.Dispatcher.Invoke(() => model.Materials[section.MaterialIndex].Setup(Options, model.UvCount));
// Application.Current.Dispatcher.Invoke(() => model.Materials[section.MaterialIndex].Setup(model.UvCount));
}
public void Animate(UObject anim)
@ -242,7 +242,7 @@ public class Renderer : IDisposable
_collision = new Shader("collision", "bone");
Picking.Setup();
Options.SetupModelsAndLights();
// Options.SetupModelsAndLights();
}
public void Render()
@ -258,9 +258,8 @@ public class Renderer : IDisposable
_shader.SetUniform($"bVertexColors[{i}]", i == (int) Color);
// render model pass
foreach (var model in Options.Models.Values)
foreach (var model in AssetPool.Get().Models.Values.Where(model => model.IsVisible))
{
if (!model.IsVisible) continue;
model.Render(_shader, Color == VertexColor.TextureCoordinates ? Options.Icons["checker"] : null);
}
@ -278,7 +277,7 @@ public class Renderer : IDisposable
}
// debug + outline pass
if (Options.TryGetModel(out var selected) && selected.IsVisible)
if (AssetPool.Get().TryGetModel(out var selected) && selected.IsVisible)
{
if (IsSkeletonTreeOpen && selected is SkeletalModel skeletalModel)
{
@ -296,7 +295,7 @@ public class Renderer : IDisposable
}
// picking pass (dedicated FBO, binding to 0 afterward)
Picking.Render(viewMatrix, projMatrix, Options.Models);
Picking.Render(viewMatrix, projMatrix);
}
public void Update(Snooper wnd, float deltaSeconds)
@ -313,14 +312,15 @@ public class Renderer : IDisposable
}
{
foreach (var model in Options.Models.Values)
{
model.Update(Options);
}
if (IsSkeletonTreeOpen && Options.TryGetModel(out var selected) && selected is SkeletalModel { IsVisible: true } skeletalModel)
{
skeletalModel.Skeleton.UpdateVertices();
}
AssetPool.Get().OnTick();
// foreach (var model in Options.Models.Values)
// {
// model.Update(Options);
// }
// if (IsSkeletonTreeOpen && Options.TryGetModel(out var selected) && selected is SkeletalModel { IsVisible: true } skeletalModel)
// {
// skeletalModel.Skeleton.UpdateVertices();
// }
}
CameraOp.Modify(wnd.KeyboardState, deltaSeconds);
@ -343,33 +343,6 @@ public class Renderer : IDisposable
wnd.WindowShouldClose(true, true);
}
private void LoadStaticMesh(UStaticMesh original)
{
var guid = original.LightingGuid;
if (Options.TryGetModel(guid, out var model))
{
model.AddInstance(Transform.Identity);
Application.Current.Dispatcher.Invoke(() => model.SetupInstances());
return;
}
if (!original.TryConvert(out var mesh))
return;
Options.Models[guid] = new StaticModel(original, mesh);
Options.SelectModel(guid);
}
private void LoadSkeletalMesh(USkeletalMesh original)
{
var guid = new FGuid((uint) original.GetFullName().GetHashCode());
if (Options.Models.ContainsKey(guid) || !original.TryConvert(out var mesh)) return;
var skeletalModel = new SkeletalModel(original, mesh);
Options.Models[guid] = skeletalModel;
Options.SelectModel(guid);
}
private void LoadSkeleton(USkeleton original)
{
var guid = original.Guid;
@ -390,7 +363,7 @@ public class Renderer : IDisposable
if (Options.TryGetModel(guid, out var model))
{
model.Materials[0].SwapMaterial(original);
Application.Current.Dispatcher.Invoke(() => model.Materials[0].Setup(Options, model.UvCount));
// Application.Current.Dispatcher.Invoke(() => model.Materials[0].Setup(Options, model.UvCount));
return;
}
@ -410,7 +383,7 @@ public class Renderer : IDisposable
if (Options.TryGetModel(guid, out var model))
{
model.AddInstance(Transform.Identity);
Application.Current.Dispatcher.Invoke(() => model.SetupInstances());
// Application.Current.Dispatcher.Invoke(() => model.SetupInstances());
return;
}
@ -589,87 +562,18 @@ public class Renderer : IDisposable
}
private void ProcessMesh(IPropertyHolder actor, UObject staticMeshComp, UStaticMesh m, Transform transform, bool forceShow)
{
var bSpline = staticMeshComp is USplineMeshComponent;
var guid = m.LightingGuid;
if (Options.TryGetModel(guid, out var model))
{
model.AddInstance(transform);
if (bSpline && model is SplineModel splineModel)
splineModel.AddComponent((USplineMeshComponent)staticMeshComp);
}
else if (m.TryConvert(out var mesh))
{
model = bSpline ? new SplineModel(m, mesh, (USplineMeshComponent)staticMeshComp, transform) : new StaticModel(m, mesh, transform);
model.IsTwoSided = actor.GetOrDefault("bMirrored", staticMeshComp.GetOrDefault("bDisallowMeshPaintPerInstance", model.IsTwoSided));
AssetPool.Get().AddModel(actor, staticMeshComp, m, transform);
if (actor.TryGetAllValues(out FPackageIndex[] textureData, "TextureData"))
{
var material = model.Materials.FirstOrDefault();
if (material is { IsUsed: true })
{
for (int j = 0; j < textureData.Length; j++)
{
if (textureData[j]?.Load() is not { } textureDataIdx)
continue;
if (textureDataIdx.TryGetValue(out FPackageIndex overrideMaterial, "OverrideMaterial") &&
overrideMaterial.TryLoad(out var oMaterial) && oMaterial is UMaterialInterface oUnrealMaterial)
material.SwapMaterial(oUnrealMaterial);
WorldTextureData(material, textureDataIdx, "Diffuse", j switch
{
0 => "Diffuse",
> 0 => $"Diffuse_Texture_{j + 1}",
_ => CMaterialParams2.FallbackDiffuse
});
WorldTextureData(material, textureDataIdx, "Normal", j switch
{
0 => "Normals",
> 0 => $"Normals_Texture_{j + 1}",
_ => CMaterialParams2.FallbackNormals
});
WorldTextureData(material, textureDataIdx, "Specular", j switch
{
0 => "SpecularMasks",
> 0 => $"SpecularMasks_{j + 1}",
_ => CMaterialParams2.FallbackNormals
});
}
}
}
if (staticMeshComp.TryGetValue(out FPackageIndex[] overrideMaterials, "OverrideMaterials"))
{
for (var j = 0; j < overrideMaterials.Length && j < model.Sections.Length; j++)
{
var matIndex = model.Sections[j].MaterialIndex;
if (matIndex < 0 || matIndex >= model.Materials.Length || matIndex >= overrideMaterials.Length ||
overrideMaterials[matIndex].Load() is not UMaterialInterface unrealMaterial) continue;
model.Materials[matIndex].SwapMaterial(unrealMaterial);
}
}
if (forceShow)
{
foreach (var section in model.Sections)
{
section.Show = true;
}
}
Options.Models[guid] = model;
}
if (actor.TryGetValue(out FPackageIndex treasureLight, "PointLight", "TreasureLight") &&
treasureLight.TryLoad(out var pl1) && pl1.Template.TryLoad(out var pl2))
{
Options.Lights.Add(new PointLight(guid, Options.Icons["pointlight"], pl1, pl2, transform));
}
if (actor.TryGetValue(out FPackageIndex spotLight, "SpotLight") &&
spotLight.TryLoad(out var sl1) && sl1.Template.TryLoad(out var sl2))
{
Options.Lights.Add(new SpotLight(guid, Options.Icons["spotlight"], sl1, sl2, transform));
}
// if (actor.TryGetValue(out FPackageIndex treasureLight, "PointLight", "TreasureLight") &&
// treasureLight.TryLoad(out var pl1) && pl1.Template.TryLoad(out var pl2))
// {
// Options.Lights.Add(new PointLight(guid, Options.Icons["pointlight"], pl1, pl2, transform));
// }
// if (actor.TryGetValue(out FPackageIndex spotLight, "SpotLight") &&
// spotLight.TryLoad(out var sl1) && sl1.Template.TryLoad(out var sl2))
// {
// Options.Lights.Add(new SpotLight(guid, Options.Icons["spotlight"], sl1, sl2, transform));
// }
}
private Transform CalculateTransform(IPropertyHolder staticMeshComp, Transform relation)

View File

@ -9,6 +9,7 @@ using CUE4Parse.UE4.Objects.Core.Misc;
using FModel.Extensions;
using FModel.Settings;
using FModel.Views.Snooper.Models;
using FModel.Views.Snooper.Textures;
using ImGuiNET;
using OpenTK.Graphics.OpenGL4;
@ -25,10 +26,10 @@ public class Material : IDisposable
public int SelectedTexture;
public bool IsUsed;
public Texture[] Diffuse;
public Texture[] Normals;
public Texture[] SpecularMasks;
public Texture[] Emissive;
public FGuid[] Diffuse;
public FGuid[] Normals;
public FGuid[] SpecularMasks;
public FGuid[] Emissive;
public Vector4[] DiffuseColor;
public Vector4[] EmissiveColor;
@ -48,13 +49,13 @@ public class Material : IDisposable
Path = "None";
IsUsed = false;
Diffuse = Array.Empty<Texture>();
Normals = Array.Empty<Texture>();
SpecularMasks = Array.Empty<Texture>();
Emissive = Array.Empty<Texture>();
Diffuse = [];
Normals = [];
SpecularMasks = [];
Emissive = [];
DiffuseColor = Array.Empty<Vector4>();
EmissiveColor = Array.Empty<Vector4>();
DiffuseColor = [];
EmissiveColor = [];
EmissiveRegion = new Vector4(0, 0, 1, 1);
}
@ -70,39 +71,36 @@ public class Material : IDisposable
unrealMaterial.GetParams(Parameters, UserSettings.Default.MaterialExportFormat);
}
public void Setup(Options options, int uvCount)
public void Validate(int uvCount)
{
_handle = GL.CreateProgram();
if (uvCount < 1 || Parameters.IsNull)
{
Diffuse = [new Texture(FLinearColor.Gray)];
Normals = [new Texture(new FLinearColor(0.5f, 0.5f, 1f, 1f))];
SpecularMasks = [new Texture(new FLinearColor(1f, 0.5f, 0.5f, 1f))];
Emissive = new Texture[1];
DiffuseColor = FillColors(1, Diffuse, CMaterialParams2.DiffuseColors, Vector4.One);
Diffuse = [new LinearColorTexture(FLinearColor.Gray).Guid];
Normals = [new LinearColorTexture(new FLinearColor(0.5f, 0.5f, 1f, 1f)).Guid];
SpecularMasks = [];
Emissive = new FGuid[1];
DiffuseColor = FillColors(1, CMaterialParams2.DiffuseColors, Vector4.One);
EmissiveColor = [Vector4.One];
}
else
{
{ // textures
Diffuse = FillTextures(options, uvCount, Parameters.HasTopDiffuse, CMaterialParams2.Diffuse, CMaterialParams2.FallbackDiffuse, true);
Normals = FillTextures(options, uvCount, Parameters.HasTopNormals, CMaterialParams2.Normals, CMaterialParams2.FallbackNormals);
SpecularMasks = FillTextures(options, uvCount, Parameters.HasTopSpecularMasks, CMaterialParams2.SpecularMasks, CMaterialParams2.FallbackSpecularMasks);
Emissive = FillTextures(options, uvCount, true, CMaterialParams2.Emissive, CMaterialParams2.FallbackEmissive);
}
// textures
Diffuse = FillTextures(uvCount, Parameters.HasTopDiffuse, CMaterialParams2.Diffuse, CMaterialParams2.FallbackDiffuse, true);
Normals = FillTextures(uvCount, Parameters.HasTopNormals, CMaterialParams2.Normals, CMaterialParams2.FallbackNormals);
SpecularMasks = FillTextures(uvCount, Parameters.HasTopSpecularMasks, CMaterialParams2.SpecularMasks, CMaterialParams2.FallbackSpecularMasks);
Emissive = FillTextures(uvCount, true, CMaterialParams2.Emissive, CMaterialParams2.FallbackEmissive);
{ // colors
DiffuseColor = FillColors(uvCount, Diffuse, CMaterialParams2.DiffuseColors, Vector4.One);
EmissiveColor = FillColors(uvCount, Emissive, CMaterialParams2.EmissiveColors, Vector4.One);
}
// colors
DiffuseColor = FillColors(uvCount, CMaterialParams2.DiffuseColors, Vector4.One);
EmissiveColor = FillColors(uvCount, CMaterialParams2.EmissiveColors, Vector4.One);
{ // ambient occlusion + color boost
if (Parameters.TryGetTexture2d(out var original, "M", "AEM", "AO") &&
!original.Name.Equals("T_BlackMask") && options.TryGetTexture(original, false, out var transformed))
if (Parameters.TryGetTexture2d(out var original, "M", "AEM", "AO") && !original.Name.Equals("T_BlackMask"))
{
AssetPool.Get().AddTexture(original, false);
HasAo = true;
Ao = new AoParams { Texture = transformed };
Ao = new AoParams { Texture = original.LightingGuid };
if (Parameters.TryGetLinearColor(out var l, "Skin Boost Color And Exponent"))
{
Ao.HasColorBoost = true;
@ -133,72 +131,94 @@ public class Material : IDisposable
"EmissiveUVPositioning (RG)UpperLeft (BA)LowerRight"))
EmissiveRegion = new Vector4(EmissiveUVs.R, EmissiveUVs.G, EmissiveUVs.B, EmissiveUVs.A);
if ((Parameters.TryGetSwitch(out var swizzleRoughnessToGreen, "SwizzleRoughnessToGreen") && swizzleRoughnessToGreen) ||
Parameters.Textures.ContainsKey("SRM"))
if ((Parameters.TryGetSwitch(out var swizzleRoughnessToGreen, "SwizzleRoughnessToGreen") && swizzleRoughnessToGreen) || Parameters.Textures.ContainsKey("SRM"))
{
foreach (var specMask in SpecularMasks)
{
specMask.SwizzleMask = new []
if (AssetPool.Get().TryGet<BitmapTexture>(specMask, out var bitmap))
{
(int) PixelFormat.Red,
(int) PixelFormat.Blue,
(int) PixelFormat.Green,
(int) PixelFormat.Alpha
};
specMask.Swizzle();
bitmap.SwizzleMask =
[
(int) PixelFormat.Red,
(int) PixelFormat.Blue,
(int) PixelFormat.Green,
(int) PixelFormat.Alpha
];
}
}
}
}
}
}
/// <param name="options">just the cache object</param>
public void Setup(bool broken)
{
_handle = GL.CreateProgram();
// if (broken)
// {
// for (var i = 0; i < 1; i++)
// {
// Diffuse[i].Setup();
// Normals[i].Setup();
// }
// }
// else
// {
// foreach (var diffuse in Diffuse) diffuse.Setup();
// foreach (var normal in Normals) normal.Setup();
// foreach (var specMask in SpecularMasks) specMask.Setup();
// foreach (var emissive in Emissive) emissive.Setup();
// }
}
/// <param name="uvCount">number of item in the array</param>
/// <param name="top">has at least 1 clearly defined texture, else will go straight to fallback</param>
/// <param name="triggers">list of texture parameter names by uv channel</param>
/// <param name="fallback">fallback texture name to use if no top texture found</param>
/// <param name="first">if no top texture, no fallback texture, then use the first texture found</param>
private Texture[] FillTextures(Options options, int uvCount, bool top, string[][] triggers, string fallback, bool first = false)
private FGuid[] FillTextures(int uvCount, bool top, string[][] triggers, string fallback, bool first = false)
{
UTexture original;
Texture transformed;
var fix = fallback == CMaterialParams2.FallbackSpecularMasks;
var textures = new Texture[uvCount];
var textures = new FGuid[uvCount];
if (top)
{
for (int i = 0; i < textures.Length; i++)
{
if (Parameters.TryGetTexture2d(out original, triggers[i]) && options.TryGetTexture(original, fix, out transformed))
textures[i] = transformed;
else if (i > 0 && textures[i - 1] != null)
if (Parameters.TryGetTexture2d(out original, triggers[i]))
{
AssetPool.Get().AddTexture(original, fix);
textures[i] = original.LightingGuid;
}
else if (i > 0)
textures[i] = textures[i - 1];
}
}
else if (Parameters.TryGetTexture2d(out original, fallback) && options.TryGetTexture(original, fix, out transformed))
else if (Parameters.TryGetTexture2d(out original, fallback))
{
AssetPool.Get().AddTexture(original, fix);
for (int i = 0; i < textures.Length; i++)
textures[i] = transformed;
textures[i] = original.LightingGuid;
}
else if (first && Parameters.TryGetFirstTexture2d(out original) && options.TryGetTexture(original, fix, out transformed))
else if (first && Parameters.TryGetFirstTexture2d(out original))
{
AssetPool.Get().AddTexture(original, fix);
for (int i = 0; i < textures.Length; i++)
textures[i] = transformed;
textures[i] = original.LightingGuid;
}
return textures;
}
/// <param name="uvCount">number of item in the array</param>
/// <param name="textures">reference array</param>
/// <param name="triggers">list of color parameter names by uv channel</param>
/// <param name="fallback">fallback color to use if no trigger was found</param>
private Vector4[] FillColors(int uvCount, Texture[] textures, string[][] triggers, Vector4 fallback)
private Vector4[] FillColors(int uvCount, string[][] triggers, Vector4 fallback)
{
var colors = new Vector4[uvCount];
for (int i = 0; i < colors.Length; i++)
{
if (textures[i] == null) continue;
if (Parameters.TryGetLinearColor(out var color, triggers[i]) && color is { A: > 0 })
{
colors[i] = new Vector4(color.R, color.G, color.B, color.A);
@ -211,38 +231,55 @@ public class Material : IDisposable
public void Render(Shader shader)
{
var unit = 0;
ITexture bitmap;
for (var i = 0; i < Diffuse.Length; i++)
{
shader.SetUniform($"uParameters.Diffuse[{i}].Sampler", unit);
shader.SetUniform($"uParameters.Diffuse[{i}].Color", DiffuseColor[i]);
Diffuse[i]?.Bind(TextureUnit.Texture0 + unit++);
if (AssetPool.Get().TryGetTexture(Diffuse[i], out bitmap))
{
shader.SetUniform($"uParameters.Diffuse[{i}].Sampler", unit);
bitmap?.Bind(TextureUnit.Texture0 + unit++);
}
}
for (var i = 0; i < Normals.Length; i++)
{
shader.SetUniform($"uParameters.Normals[{i}].Sampler", unit);
Normals[i]?.Bind(TextureUnit.Texture0 + unit++);
if (AssetPool.Get().TryGetTexture(Normals[i], out bitmap))
{
shader.SetUniform($"uParameters.Normals[{i}].Sampler", unit);
bitmap?.Bind(TextureUnit.Texture0 + unit++);
}
}
for (var i = 0; i < SpecularMasks.Length; i++)
{
shader.SetUniform($"uParameters.SpecularMasks[{i}].Sampler", unit);
SpecularMasks[i]?.Bind(TextureUnit.Texture0 + unit++);
if (AssetPool.Get().TryGetTexture(SpecularMasks[i], out bitmap))
{
shader.SetUniform($"uParameters.SpecularMasks[{i}].Sampler", unit);
bitmap?.Bind(TextureUnit.Texture0 + unit++);
}
}
for (var i = 0; i < Emissive.Length; i++)
{
shader.SetUniform($"uParameters.Emissive[{i}].Sampler", unit);
shader.SetUniform($"uParameters.Emissive[{i}].Color", EmissiveColor[i]);
Emissive[i]?.Bind(TextureUnit.Texture0 + unit++);
if (AssetPool.Get().TryGetTexture(Emissive[i], out bitmap))
{
shader.SetUniform($"uParameters.Emissive[{i}].Color", EmissiveColor[i]);
shader.SetUniform($"uParameters.Emissive[{i}].Sampler", unit);
bitmap?.Bind(TextureUnit.Texture0 + unit++);
}
}
Ao.Texture?.Bind(TextureUnit.Texture31);
shader.SetUniform("uParameters.Ao.Sampler", 31);
shader.SetUniform("uParameters.Ao.HasColorBoost", Ao.HasColorBoost);
shader.SetUniform("uParameters.Ao.ColorBoost.Color", Ao.ColorBoost.Color);
shader.SetUniform("uParameters.Ao.ColorBoost.Exponent", Ao.ColorBoost.Exponent);
shader.SetUniform("uParameters.HasAo", HasAo);
if (HasAo && AssetPool.Get().TryGetTexture(Ao.Texture, out bitmap))
{
bitmap?.Bind(TextureUnit.Texture31);
shader.SetUniform("uParameters.Ao.Sampler", 31);
shader.SetUniform("uParameters.Ao.HasColorBoost", Ao.HasColorBoost);
shader.SetUniform("uParameters.Ao.ColorBoost.Color", Ao.ColorBoost.Color);
shader.SetUniform("uParameters.Ao.ColorBoost.Exponent", Ao.ColorBoost.Exponent);
shader.SetUniform("uParameters.HasAo", HasAo);
}
shader.SetUniform("uParameters.EmissiveRegion", EmissiveRegion);
shader.SetUniform("uParameters.RoughnessMin", RoughnessMin);
@ -311,7 +348,7 @@ public class Material : IDisposable
}
}
public bool ImGuiTextures(Dictionary<string, Texture> icons, UModel model)
public bool ImGuiTextures(Dictionary<string, ITexture> icons, UModel model)
{
if (ImGui.BeginTable("material_textures", 2))
{
@ -340,21 +377,24 @@ public class Material : IDisposable
ImGui.EndTable();
}
var texture = GetSelectedTexture() ?? icons["noimage"];
if (GetSelectedTexture() is not { } g || !AssetPool.Get().TryGetTexture(g, out var texture))
{
texture = icons["noimage"];
}
ImGui.Image(texture.GetPointer(),
new Vector2(ImGui.GetContentRegionAvail().X - ImGui.GetScrollX()),
Vector2.Zero, Vector2.One);
return ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(ImGuiMouseButton.Left);
}
public Texture GetSelectedTexture()
public FGuid? GetSelectedTexture()
{
return SelectedTexture switch
{
0 when Diffuse.Length > 0 => Diffuse[SelectedChannel],
1 when Normals.Length > 0 => Normals[SelectedChannel],
2 when SpecularMasks.Length > 0 => SpecularMasks[SelectedChannel],
3 => Ao.Texture,
3 when HasAo => Ao.Texture,
4 when Emissive.Length > 0 => Emissive[SelectedChannel],
_ => null
};
@ -371,30 +411,13 @@ public class Material : IDisposable
public void Dispose()
{
for (int i = 0; i < Diffuse.Length; i++)
{
Diffuse[i]?.Dispose();
}
for (int i = 0; i < Normals.Length; i++)
{
Normals[i]?.Dispose();
}
for (int i = 0; i < SpecularMasks.Length; i++)
{
SpecularMasks[i]?.Dispose();
}
for (int i = 0; i < Emissive.Length; i++)
{
Emissive[i]?.Dispose();
}
Ao.Texture?.Dispose();
GL.DeleteProgram(_handle);
}
}
public struct AoParams
{
public Texture Texture;
public FGuid Texture;
public Boost ColorBoost;
public bool HasColorBoost;

View File

@ -1,316 +0,0 @@
using System;
using System.Numerics;
using System.Windows;
using CUE4Parse_Conversion.Textures;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.UE4.Objects.Core.Math;
using CUE4Parse.UE4.Objects.Core.Misc;
using ImGuiNET;
using OpenTK.Graphics.OpenGL4;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SkiaSharp;
namespace FModel.Views.Snooper.Shading;
public class Texture : IDisposable
{
private readonly int _handle;
private readonly TextureType _type;
private readonly TextureTarget _target;
public readonly string Type;
public readonly FGuid Guid;
public readonly string Name;
public readonly string Path;
public readonly EPixelFormat Format;
public readonly uint ImportedWidth;
public readonly uint ImportedHeight;
public int Width;
public int Height;
private const int DisabledChannel = (int)BlendingFactor.Zero;
private readonly bool[] _values = [true, true, true, true];
private readonly string[] _labels = ["R", "G", "B", "A"];
public int[] SwizzleMask =
[
(int) PixelFormat.Red,
(int) PixelFormat.Green,
(int) PixelFormat.Blue,
(int) PixelFormat.Alpha
];
private Texture(TextureType type)
{
_handle = GL.GenTexture();
_type = type;
_target = _type switch
{
TextureType.Cubemap => TextureTarget.TextureCubeMap,
TextureType.MsaaFramebuffer => TextureTarget.Texture2DMultisample,
_ => TextureTarget.Texture2D
};
Guid = new FGuid();
}
public Texture(uint width, uint height) : this(TextureType.MsaaFramebuffer)
{
Width = (int) width;
Height = (int) height;
Bind(TextureUnit.Texture0);
GL.TexImage2DMultisample(TextureTargetMultisample.Texture2DMultisample, Constants.SAMPLES_COUNT, PixelInternalFormat.Rgb, Width, Height, true);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, _target, _handle, 0);
}
public Texture(int width, int height) : this(TextureType.Framebuffer)
{
Width = width;
Height = height;
Bind(TextureUnit.Texture0);
GL.TexImage2D(_target, 0, PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, IntPtr.Zero);
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear);
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
GL.TexParameter(_target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(_target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, _target, _handle, 0);
}
public Texture(SKBitmap bitmap, UTexture texture2D) : this(TextureType.Normal)
{
Type = texture2D.ExportType;
Guid = texture2D.LightingGuid;
Name = texture2D.Name;
Path = texture2D.GetPathName();
Format = texture2D.Format;
Width = bitmap.Width;
Height = bitmap.Height;
Bind(TextureUnit.Texture0);
var internalFormat = bitmap.ColorType switch
{
SKColorType.Gray8 => PixelInternalFormat.R8,
_ => texture2D.SRGB ? PixelInternalFormat.Srgb : PixelInternalFormat.Rgb
};
var pixelFormat = bitmap.ColorType switch
{
SKColorType.Gray8 => PixelFormat.Red,
SKColorType.Bgra8888 => PixelFormat.Bgra,
_ => PixelFormat.Rgba
};
GL.TexImage2D(_target, 0, internalFormat, Width, Height, 0, pixelFormat, PixelType.UnsignedByte, bitmap.Bytes);
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear);
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
GL.TexParameter(_target, TextureParameterName.TextureBaseLevel, 0);
GL.TexParameter(_target, TextureParameterName.TextureMaxLevel, 8);
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
bitmap.Dispose();
}
public Texture(FLinearColor color) : this(TextureType.Normal)
{
Type = "LinearColor";
Name = color.Hex;
Width = 1;
Height = 1;
Bind(TextureUnit.Texture0);
GL.TexImage2D(_target, 0, PixelInternalFormat.Rgba, Width, Height, 0, PixelFormat.Rgba, PixelType.Float, ref color);
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear);
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
GL.TexParameter(_target, TextureParameterName.TextureBaseLevel, 0);
GL.TexParameter(_target, TextureParameterName.TextureMaxLevel, 8);
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
}
public Texture(string[] textures) : this(TextureType.Cubemap)
{
Bind(TextureUnit.Texture0);
for (int t = 0; t < textures.Length; t++)
{
ProcessPixels(textures[t], TextureTarget.TextureCubeMapPositiveX + t);
}
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear);
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
GL.TexParameter(_target, TextureParameterName.TextureWrapR, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(_target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(_target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
GL.GenerateMipmap(GenerateMipmapTarget.TextureCubeMap);
}
public Texture(string texture) : this(TextureType.Normal)
{
Bind(TextureUnit.Texture0);
ProcessPixels(texture, _target);
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear);
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
GL.TexParameter(_target, TextureParameterName.TextureWrapR, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(_target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(_target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
}
private void ProcessPixels(string texture, TextureTarget target)
{
var info = Application.GetResourceStream(new Uri($"/FModel;component/Resources/{texture}.png", UriKind.Relative));
using var img = Image.Load<Rgba32>(info.Stream);
Width = img.Width;
Height = img.Height;
GL.TexImage2D(target, 0, PixelInternalFormat.Rgba8, Width, Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
img.ProcessPixelRows(accessor =>
{
for (int y = 0; y < accessor.Height; y++)
{
GL.TexSubImage2D(target, 0, 0, y, accessor.Width, 1, PixelFormat.Rgba, PixelType.UnsignedByte, accessor.GetRowSpan(y).ToArray());
}
});
}
public void Bind(TextureUnit textureSlot)
{
GL.ActiveTexture(textureSlot);
Bind(_target);
}
public void Bind(TextureTarget target)
{
GL.BindTexture(target, _handle);
}
public void Bind()
{
GL.BindTexture(_target, _handle);
}
public void Swizzle()
{
Bind();
GL.TexParameter(_target, TextureParameterName.TextureSwizzleRgba, SwizzleMask);
}
public IntPtr GetPointer() => (IntPtr) _handle;
public void WindowResized(int width, int height)
{
Width = width;
Height = height;
Bind();
switch (_type)
{
case TextureType.MsaaFramebuffer:
GL.TexImage2DMultisample(TextureTargetMultisample.Texture2DMultisample, Constants.SAMPLES_COUNT, PixelInternalFormat.Rgb, Width, Height, true);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, _target, _handle, 0);
break;
case TextureType.Framebuffer:
GL.TexImage2D(_target, 0, PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, IntPtr.Zero);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, _target, _handle, 0);
break;
default:
throw new NotSupportedException();
}
}
public void Dispose()
{
GL.DeleteTexture(_handle);
}
private Vector3 _scrolling = new (0.0f, 0.0f, 1.0f);
public void ImGuiTextureInspector()
{
if (ImGui.BeginTable("texture_inspector", 2, ImGuiTableFlags.SizingStretchProp))
{
SnimGui.NoFramePaddingOnY(() =>
{
SnimGui.Layout("Type");ImGui.Text($" : ({Format}) {Name}");
SnimGui.TooltipCopy("(?) Click to Copy Path", Path);
SnimGui.Layout("Guid");ImGui.Text($" : {Guid.ToString(EGuidFormats.UniqueObjectGuid)}");
SnimGui.Layout("Size");
ImGui.Text($" : {Width}x{Height}");
SnimGui.Layout("Swizzle");
for (int c = 0; c < SwizzleMask.Length; c++)
{
if (ImGui.Checkbox(_labels[c], ref _values[c]))
{
Bind();
GL.TexParameter(_target, TextureParameterName.TextureSwizzleR + c, _values[c] ? SwizzleMask[c] : DisabledChannel);
}
ImGui.SameLine();
}
ImGui.EndTable();
});
}
var io = ImGui.GetIO();
var canvasP0 = ImGui.GetCursorScreenPos();
var canvasSize = ImGui.GetContentRegionAvail();
if (canvasSize.X < 50.0f) canvasSize.X = 50.0f;
if (canvasSize.Y < 50.0f) canvasSize.Y = 50.0f;
var canvasP1 = canvasP0 + canvasSize;
var origin = new Vector2(canvasP0.X + _scrolling.X, canvasP0.Y + _scrolling.Y);
var absoluteMiddle = canvasSize / 2.0f;
ImGui.InvisibleButton("texture_inspector_canvas", canvasSize, ImGuiButtonFlags.MouseButtonLeft);
if (ImGui.IsItemActive() && ImGui.IsMouseDragging(ImGuiMouseButton.Left))
{
_scrolling.X += io.MouseDelta.X;
_scrolling.Y += io.MouseDelta.Y;
}
else if (ImGui.IsItemHovered() && io.MouseWheel != 0.0f)
{
var zoomFactor = 1.0f + io.MouseWheel * 0.1f;
var mousePosCanvas = io.MousePos - origin;
_scrolling.X -= (mousePosCanvas.X - absoluteMiddle.X) * (zoomFactor - 1);
_scrolling.Y -= (mousePosCanvas.Y - absoluteMiddle.Y) * (zoomFactor - 1);
_scrolling.Z *= zoomFactor;
origin = new Vector2(canvasP0.X + _scrolling.X, canvasP0.Y + _scrolling.Y);
}
var drawList = ImGui.GetWindowDrawList();
drawList.AddRectFilled(canvasP0, canvasP1, 0xFF242424);
drawList.PushClipRect(canvasP0, canvasP1, true);
{
var sensitivity = _scrolling.Z * 25.0f;
for (float x = _scrolling.X % sensitivity; x < canvasSize.X; x += sensitivity)
drawList.AddLine(canvasP0 with { X = canvasP0.X + x }, canvasP1 with { X = canvasP0.X + x }, 0x28C8C8C8);
for (float y = _scrolling.Y % sensitivity; y < canvasSize.Y; y += sensitivity)
drawList.AddLine(canvasP0 with { Y = canvasP0.Y + y }, canvasP1 with { Y = canvasP0.Y + y }, 0x28C8C8C8);
}
drawList.PopClipRect();
drawList.PushClipRect(canvasP0, canvasP1, true);
{
var relativeMiddle = origin + absoluteMiddle;
var ratio = Math.Min(canvasSize.X / Width, canvasSize.Y / Height) * 0.95f * _scrolling.Z;
var size = new Vector2(Width, Height) * ratio / 2f;
drawList.AddImage(GetPointer(), relativeMiddle - size, relativeMiddle + size);
drawList.AddRect(relativeMiddle - size, relativeMiddle + size, 0xFFFFFFFF);
}
drawList.PopClipRect();
}
}
public enum TextureType
{
Normal,
Cubemap,
Framebuffer,
MsaaFramebuffer
}

View File

@ -1,70 +0,0 @@
using OpenTK.Graphics.OpenGL4;
namespace FModel.Views.Snooper.Shading;
public static class TextureHelper
{
/// <summary>
/// Red : Specular (not used anymore)
/// Green : Metallic
/// Blue : Roughness
/// </summary>
public static void FixChannels(string game, Texture texture)
{
switch (game)
{
// R: Whatever (AO / S / E / ...)
// G: Roughness
// B: Metallic
case "GAMEFACE":
case "HK_PROJECT":
case "COSMICSHAKE":
case "PHOENIX":
case "ATOMICHEART":
case "MULTIVERSUS":
case "BODYCAM":
{
texture.SwizzleMask =
[
(int) PixelFormat.Red,
(int) PixelFormat.Blue,
(int) PixelFormat.Green,
(int) PixelFormat.Alpha
];
break;
}
// R: Metallic
// G: Roughness
// B: Whatever (AO / S / E / ...)
case "SHOOTERGAME":
case "DIVINEKNOCKOUT":
case "MOONMAN":
{
texture.SwizzleMask =
[
(int) PixelFormat.Blue,
(int) PixelFormat.Red,
(int) PixelFormat.Green,
(int) PixelFormat.Alpha
];
break;
}
// R: Roughness
// G: Metallic
// B: Whatever (AO / S / E / ...)
case "CCFF7R":
case "PJ033":
{
texture.SwizzleMask =
[
(int) PixelFormat.Blue,
(int) PixelFormat.Green,
(int) PixelFormat.Red,
(int) PixelFormat.Alpha
];
break;
}
}
texture.Swizzle();
}
}

View File

@ -11,6 +11,7 @@ using FModel.Settings;
using FModel.Views.Snooper.Animations;
using FModel.Views.Snooper.Models;
using FModel.Views.Snooper.Shading;
using FModel.Views.Snooper.Textures;
using ImGuizmoNET;
using OpenTK.Graphics.OpenGL4;
@ -174,7 +175,7 @@ public class SnimGui
if (ImGui.BeginTable("world_details", 2, ImGuiTableFlags.SizingStretchProp))
{
var b = false;
var length = s.Renderer.Options.Models.Count;
var length = AssetPool.Get().Models.Count;
NoFramePaddingOnY(() =>
{
@ -184,7 +185,7 @@ public class SnimGui
if (ImGui.SmallButton("Save All"))
{
foreach (var model in s.Renderer.Options.Models.Values)
foreach (var model in AssetPool.Get().Models.Values)
{
b |= model.Save(out _, out _);
}
@ -239,12 +240,12 @@ public class SnimGui
for (int i = 0; i < s.Renderer.Options.Lights.Count; i++)
{
var light = s.Renderer.Options.Lights[i];
var id = s.Renderer.Options.TryGetModel(light.Model, out var lightModel) ? lightModel.Name : "None";
var id = AssetPool.Get().TryGetModel(light.Model, out var lightModel) ? lightModel.Name : "None";
id += $"##{i}";
if (ImGui.TreeNode(id) && ImGui.BeginTable(id, 2))
{
s.Renderer.Options.SelectModel(light.Model);
AssetPool.Get().SelectModel(light.Model);
light.ImGuiLight();
ImGui.EndTable();
ImGui.TreePop();
@ -380,7 +381,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
ImGui.TableHeadersRow();
var i = 0;
foreach ((var guid, var model) in s.Renderer.Options.Models)
foreach ((var guid, var model) in AssetPool.Get().Models)
{
ImGui.PushID(i);
ImGui.TableNextRow();
@ -397,14 +398,14 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
ImGui.Text(model.UvCount.ToString("D"));
ImGui.TableNextColumn();
var doubleClick = false;
if (ImGui.Selectable(model.Name, s.Renderer.Options.SelectedModel == guid, ImGuiSelectableFlags.SpanAllColumns | ImGuiSelectableFlags.AllowDoubleClick))
if (ImGui.Selectable(model.Name, AssetPool.Get().SelectedModel == guid, ImGuiSelectableFlags.SpanAllColumns | ImGuiSelectableFlags.AllowDoubleClick))
{
s.Renderer.Options.SelectModel(guid);
AssetPool.Get().SelectModel(guid);
doubleClick = ImGui.IsMouseDoubleClicked(ImGuiMouseButton.Left);
}
Popup(() =>
{
s.Renderer.Options.SelectModel(guid);
AssetPool.Get().SelectModel(guid);
if (ImGui.MenuItem("Show", null, model.IsVisible)) model.IsVisible = !model.IsVisible;
if (ImGui.MenuItem("Wireframe", null, model.ShowWireframe)) model.ShowWireframe = !model.ShowWireframe;
if (ImGui.MenuItem("Collisions", null, model.ShowCollisions, model.HasCollisions)) model.ShowCollisions = !model.ShowCollisions;
@ -443,7 +444,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
doubleClick = ImGui.MenuItem("Teleport To");
if (ImGui.MenuItem("Delete")) s.Renderer.Options.RemoveModel(guid);
if (ImGui.MenuItem("Deselect")) s.Renderer.Options.SelectModel(Guid.Empty);
if (ImGui.MenuItem("Deselect")) AssetPool.Get().SelectModel(Guid.Empty);
ImGui.Separator();
if (ImGui.MenuItem("Copy Path to Clipboard")) ImGui.SetClipboardText(model.Path);
});
@ -470,8 +471,8 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
{
MeshWindow("Sockets", s.Renderer, (icons, selectedModel) =>
{
var info = new SocketAttachementInfo { Guid = s.Renderer.Options.SelectedModel, Instance = selectedModel.SelectedInstance };
foreach (var model in s.Renderer.Options.Models.Values)
var info = new SocketAttachementInfo { Guid = AssetPool.Get().SelectedModel, Instance = selectedModel.SelectedInstance };
foreach (var model in AssetPool.Get().Models.Values)
{
if (!model.HasSockets || model.IsSelected) continue;
if (ImGui.TreeNode($"{model.Name} [{model.Sockets.Count}]"))
@ -511,7 +512,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
NoFramePaddingOnY(() =>
{
Layout("Entity");ImGui.Text($" : ({model.Type}) {model.Name}");
Layout("Guid");ImGui.Text($" : {s.Renderer.Options.SelectedModel.ToString(EGuidFormats.UniqueObjectGuid)}");
Layout("Guid");ImGui.Text($" : {AssetPool.Get().SelectedModel.ToString(EGuidFormats.UniqueObjectGuid)}");
if (model is SkeletalModel skeletalModel)
{
Layout("Skeleton");ImGui.Text($" : {skeletalModel.Skeleton.Name}");
@ -666,7 +667,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
ImGui.PopStyleVar();
}
private void DrawMaterialInspector(Dictionary<string, Texture> icons, UModel model, Section section)
private void DrawMaterialInspector(Dictionary<string, ITexture> icons, UModel model, Section section)
{
var material = model.Materials[section.MaterialIndex];
@ -726,9 +727,13 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
if (!_tiOpen) return;
if (ImGui.Begin("Texture Inspector", ref _tiOpen, ImGuiWindowFlags.NoScrollbar))
{
if (s.Renderer.Options.TryGetModel(out var model) && s.Renderer.Options.TryGetSection(model, out var section))
if (AssetPool.Get().TryGetModel(out var model) && s.Renderer.Options.TryGetSection(model, out var section))
{
(model.Materials[section.MaterialIndex].GetSelectedTexture() ?? s.Renderer.Options.Icons["noimage"]).ImGuiTextureInspector();
if (model.Materials[section.MaterialIndex].GetSelectedTexture() is not { } g || !AssetPool.Get().TryGetTexture(g, out var texture))
{
texture = s.Renderer.Options.Icons["noimage"];
}
texture.ImGuiInspector();
}
}
ImGui.End();
@ -741,7 +746,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero);
if (ImGui.Begin("Skeleton Tree", ref s.Renderer.IsSkeletonTreeOpen, ImGuiWindowFlags.NoScrollbar))
{
if (s.Renderer.Options.TryGetModel(out var model) && model is SkeletalModel skeletalModel)
if (AssetPool.Get().TryGetModel(out var model) && model is SkeletalModel skeletalModel)
{
skeletalModel.Skeleton.ImGuiBoneBreadcrumb();
if (ImGui.BeginTable("skeleton_tree", 2, ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.RowBg, ImGui.GetContentRegionAvail(), ImGui.GetWindowWidth()))
@ -793,7 +798,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
if (ImGui.IsMouseClicked(ImGuiMouseButton.Right))
{
var guid = s.Renderer.Picking.ReadPixel(ImGui.GetMousePos(), ImGui.GetCursorScreenPos(), size);
s.Renderer.Options.SelectModel(guid);
AssetPool.Get().SelectModel(guid);
ImGui.SetWindowFocus("Outliner");
ImGui.SetWindowFocus("Details");
}
@ -847,7 +852,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
private void DrawGuizmo(Snooper s)
{
var enableGuizmo = s.Renderer.Options.TryGetModel(out var selected) && selected.IsVisible;
var enableGuizmo = AssetPool.Get().TryGetModel(out var selected) && selected.IsVisible;
if (enableGuizmo)
{
var view = s.Renderer.CameraOp.GetViewMatrix();
@ -903,16 +908,16 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
ImGui.End();
}
private void MeshWindow(string name, Renderer renderer, Action<Dictionary<string, Texture>, UModel> content, bool styled = true)
private void MeshWindow(string name, Renderer renderer, Action<Dictionary<string, ITexture>, UModel> content, bool styled = true)
{
Window(name, () =>
{
if (renderer.Options.TryGetModel(out var model)) content(renderer.Options.Icons, model);
if (AssetPool.Get().TryGetModel(out var model)) content(renderer.Options.Icons, model);
else NoMeshSelected();
}, styled);
}
private void SectionWindow(string name, Renderer renderer, Action<Dictionary<string, Texture>, UModel, Section> content, bool styled = true)
private void SectionWindow(string name, Renderer renderer, Action<Dictionary<string, ITexture>, UModel, Section> content, bool styled = true)
{
MeshWindow(name, renderer, (icons, model) =>
{
@ -921,7 +926,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
}, styled);
}
private void AnimationWindow(string name, Renderer renderer, Action<Dictionary<string, Texture>, TimeTracker, List<Animation>> content, bool styled = true)
private void AnimationWindow(string name, Renderer renderer, Action<Dictionary<string, ITexture>, TimeTracker, List<Animation>> content, bool styled = true)
{
ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero);
Window(name, () => content(renderer.Options.Icons, renderer.Options.Tracker, renderer.Options.Animations), styled);

View File

@ -37,7 +37,7 @@ public class Snooper : GameWindow
public bool TryLoadExport(CancellationToken cancellationToken, UObject dummy, Lazy<UObject> export)
{
Renderer.Load(cancellationToken, dummy, export);
return Renderer.Options.Models.Count > 0;
return ThreadPool.PendingWorkItemCount > 0 || !AssetPool.Get().Queue.IsEmpty;
}
public unsafe void WindowShouldClose(bool value, bool clear)

View File

@ -0,0 +1,141 @@
using System;
using System.Numerics;
using CUE4Parse.UE4.Objects.Core.Misc;
using ImGuiNET;
using OpenTK.Graphics.OpenGL4;
namespace FModel.Views.Snooper.Textures;
public abstract class AbstractTexture : ITexture
{
public int Handle { get; private set; }
public TextureType Type { get; }
public TextureTarget Target { get; }
public FGuid Guid { get; protected init; }
public string Name { get; protected init; }
public int Width { get; protected set; }
public int Height { get; protected set; }
public AbstractTexture(TextureType type)
{
Type = type;
Target = Type switch
{
TextureType.Cubemap => TextureTarget.TextureCubeMap,
TextureType.MsaaFramebuffer => TextureTarget.Texture2DMultisample,
_ => TextureTarget.Texture2D
};
Guid = new FGuid();
}
public virtual void Setup()
{
Handle = GL.GenTexture();
Bind(TextureUnit.Texture0);
}
protected virtual void ImGuiInspectorHeader()
{
}
public void Bind(TextureUnit unit)
{
GL.ActiveTexture(unit);
Bind(Target);
}
public void Bind(TextureTarget target)
{
GL.BindTexture(target, Handle);
}
public void Bind()
{
GL.BindTexture(Target, Handle);
}
public IntPtr GetPointer() => Handle;
public void WindowResized(int width, int height)
{
Width = width;
Height = height;
Bind();
switch (Type)
{
case TextureType.MsaaFramebuffer:
GL.TexImage2DMultisample(TextureTargetMultisample.Texture2DMultisample, Constants.SAMPLES_COUNT, PixelInternalFormat.Rgb, Width, Height, true);
break;
case TextureType.Framebuffer:
GL.TexImage2D(Target, 0, PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, IntPtr.Zero);
break;
default:
throw new NotSupportedException();
}
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Target, Handle, 0);
}
public void Dispose()
{
GL.DeleteTexture(Handle);
}
private Vector3 _scrolling = new (0.0f, 0.0f, 1.0f);
public void ImGuiInspector()
{
ImGuiInspectorHeader();
var io = ImGui.GetIO();
var canvasP0 = ImGui.GetCursorScreenPos();
var canvasSize = ImGui.GetContentRegionAvail();
if (canvasSize.X < 50.0f) canvasSize.X = 50.0f;
if (canvasSize.Y < 50.0f) canvasSize.Y = 50.0f;
var canvasP1 = canvasP0 + canvasSize;
var origin = new Vector2(canvasP0.X + _scrolling.X, canvasP0.Y + _scrolling.Y);
var absoluteMiddle = canvasSize / 2.0f;
ImGui.InvisibleButton("texture_inspector_canvas", canvasSize, ImGuiButtonFlags.MouseButtonLeft);
if (ImGui.IsItemActive() && ImGui.IsMouseDragging(ImGuiMouseButton.Left))
{
_scrolling.X += io.MouseDelta.X;
_scrolling.Y += io.MouseDelta.Y;
}
else if (ImGui.IsItemHovered() && io.MouseWheel != 0.0f)
{
var zoomFactor = 1.0f + io.MouseWheel * 0.1f;
var mousePosCanvas = io.MousePos - origin;
_scrolling.X -= (mousePosCanvas.X - absoluteMiddle.X) * (zoomFactor - 1);
_scrolling.Y -= (mousePosCanvas.Y - absoluteMiddle.Y) * (zoomFactor - 1);
_scrolling.Z *= zoomFactor;
origin = new Vector2(canvasP0.X + _scrolling.X, canvasP0.Y + _scrolling.Y);
}
var drawList = ImGui.GetWindowDrawList();
drawList.AddRectFilled(canvasP0, canvasP1, 0xFF242424);
drawList.PushClipRect(canvasP0, canvasP1, true);
{
var sensitivity = _scrolling.Z * 25.0f;
for (float x = _scrolling.X % sensitivity; x < canvasSize.X; x += sensitivity)
drawList.AddLine(canvasP0 with { X = canvasP0.X + x }, canvasP1 with { X = canvasP0.X + x }, 0x28C8C8C8);
for (float y = _scrolling.Y % sensitivity; y < canvasSize.Y; y += sensitivity)
drawList.AddLine(canvasP0 with { Y = canvasP0.Y + y }, canvasP1 with { Y = canvasP0.Y + y }, 0x28C8C8C8);
}
drawList.PopClipRect();
drawList.PushClipRect(canvasP0, canvasP1, true);
{
var relativeMiddle = origin + absoluteMiddle;
var ratio = Math.Min(canvasSize.X / Width, canvasSize.Y / Height) * 0.95f * _scrolling.Z;
var size = new Vector2(Width, Height) * ratio / 2f;
drawList.AddImage(GetPointer(), relativeMiddle - size, relativeMiddle + size);
drawList.AddRect(relativeMiddle - size, relativeMiddle + size, 0xFFFFFFFF);
}
drawList.PopClipRect();
}
}

View File

@ -0,0 +1,74 @@
using System;
using System.Windows;
using OpenTK.Graphics.OpenGL4;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace FModel.Views.Snooper.Textures;
public class ApplicationTexture : AbstractTexture
{
private readonly Image<Rgba32>[] _images;
public ApplicationTexture(string texture) : base(TextureType.Normal)
{
_images = [LoadImage(texture)];
Width = _images[0].Width;
Height = _images[0].Height;
}
public ApplicationTexture(string[] textures) : base(TextureType.Cubemap)
{
_images = new Image<Rgba32>[textures.Length];
for (var i = 0; i < _images.Length; i++)
{
_images[i] = LoadImage(textures[i]);
}
Width = _images[0].Width;
Height = _images[0].Height;
}
public override void Setup()
{
base.Setup();
var target = Type == TextureType.Cubemap ? TextureTarget.TextureCubeMapPositiveX : Target;
GL.TexImage2D(target, 0, PixelInternalFormat.Rgba8, Width, Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
for (var i = 0; i < _images.Length; i++)
{
_images[i].ProcessPixelRows(accessor =>
{
for (int y = 0; y < accessor.Height; y++)
{
GL.TexSubImage2D(target + i, 0, 0, y, accessor.Width, 1, PixelFormat.Rgba, PixelType.UnsignedByte, accessor.GetRowSpan(y).ToArray());
}
});
}
if (Type == TextureType.Cubemap)
{
GL.TexParameter(Target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear);
}
else
{
GL.TexParameter(Target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear);
}
GL.TexParameter(Target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
GL.TexParameter(Target, TextureParameterName.TextureWrapR, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(Target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(Target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
if (Type == TextureType.Cubemap)
{
GL.GenerateMipmap(GenerateMipmapTarget.TextureCubeMap);
}
}
private Image<Rgba32> LoadImage(string texture)
{
var info = Application.GetResourceStream(new Uri($"/FModel;component/Resources/{texture}.png", UriKind.Relative));
return Image.Load<Rgba32>(info.Stream);
}
}

View File

@ -0,0 +1,161 @@
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.UE4.Objects.Core.Misc;
using ImGuiNET;
using OpenTK.Graphics.OpenGL4;
using SkiaSharp;
namespace FModel.Views.Snooper.Textures;
public class BitmapTexture : AbstractTexture
{
private readonly byte[] _bytes;
private readonly SKColorType _type;
private readonly bool _isSrgb;
private const int DisabledChannel = (int)BlendingFactor.Zero;
private readonly bool[] _values = [true, true, true, true];
private readonly string[] _labels = ["R", "G", "B", "A"];
public int[] SwizzleMask =
[
(int) PixelFormat.Red,
(int) PixelFormat.Green,
(int) PixelFormat.Blue,
(int) PixelFormat.Alpha
];
public readonly string Path;
public readonly EPixelFormat Format;
public BitmapTexture(SKBitmap bitmap, UTexture texture) : base(TextureType.Normal)
{
Guid = texture.LightingGuid;
Name = texture.Name;
Width = bitmap.Width;
Height = bitmap.Height;
Path = texture.GetPathName();
Format = texture.Format;
_bytes = bitmap.Bytes;
_type = bitmap.ColorType;
_isSrgb = texture.SRGB;
bitmap.Dispose();
}
public override void Setup()
{
base.Setup();
var internalFormat = _type switch
{
SKColorType.Gray8 => PixelInternalFormat.R8,
_ => _isSrgb ? PixelInternalFormat.Srgb : PixelInternalFormat.Rgb
};
var pixelFormat = _type switch
{
SKColorType.Gray8 => PixelFormat.Red,
SKColorType.Bgra8888 => PixelFormat.Bgra,
_ => PixelFormat.Rgba
};
GL.TexImage2D(Target, 0, internalFormat, Width, Height, 0, pixelFormat, PixelType.UnsignedByte, _bytes);
GL.TexParameter(Target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear);
GL.TexParameter(Target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
GL.TexParameter(Target, TextureParameterName.TextureBaseLevel, 0);
GL.TexParameter(Target, TextureParameterName.TextureMaxLevel, 8);
GL.TexParameter(Target, TextureParameterName.TextureSwizzleRgba, SwizzleMask);
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
}
protected override void ImGuiInspectorHeader()
{
base.ImGuiInspectorHeader();
if (ImGui.BeginTable("texture_inspector", 2, ImGuiTableFlags.SizingStretchProp))
{
SnimGui.NoFramePaddingOnY(() =>
{
SnimGui.Layout("Type");ImGui.Text($" : ({Format}) {Name}");
SnimGui.TooltipCopy("(?) Click to Copy Path", Path);
SnimGui.Layout("Guid");ImGui.Text($" : {Guid.ToString(EGuidFormats.UniqueObjectGuid)}");
SnimGui.Layout("Size");
ImGui.Text($" : {Width}x{Height}");
SnimGui.Layout("Swizzle");
for (int c = 0; c < SwizzleMask.Length; c++)
{
if (ImGui.Checkbox(_labels[c], ref _values[c]))
{
Bind();
GL.TexParameter(Target, TextureParameterName.TextureSwizzleR + c, _values[c] ? SwizzleMask[c] : DisabledChannel);
}
ImGui.SameLine();
}
ImGui.EndTable();
});
}
}
public void FixChannels(string project)
{
switch (project)
{
// R: Whatever (AO / S / E / ...)
// G: Roughness
// B: Metallic
case "GAMEFACE":
case "HK_PROJECT":
case "COSMICSHAKE":
case "PHOENIX":
case "ATOMICHEART":
case "MULTIVERSUS":
case "BODYCAM":
{
SwizzleMask =
[
(int) PixelFormat.Red,
(int) PixelFormat.Blue,
(int) PixelFormat.Green,
(int) PixelFormat.Alpha
];
break;
}
// R: Metallic
// G: Roughness
// B: Whatever (AO / S / E / ...)
case "SHOOTERGAME":
case "DIVINEKNOCKOUT":
case "MOONMAN":
{
SwizzleMask =
[
(int) PixelFormat.Blue,
(int) PixelFormat.Red,
(int) PixelFormat.Green,
(int) PixelFormat.Alpha
];
break;
}
// R: Roughness
// G: Metallic
// B: Whatever (AO / S / E / ...)
case "CCFF7R":
case "PJ033":
{
SwizzleMask =
[
(int) PixelFormat.Blue,
(int) PixelFormat.Green,
(int) PixelFormat.Red,
(int) PixelFormat.Alpha
];
break;
}
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using OpenTK.Graphics.OpenGL4;
namespace FModel.Views.Snooper.Textures;
public class FramebufferTexture : AbstractTexture
{
public FramebufferTexture(int width, int height) : base(TextureType.Framebuffer)
{
Width = width;
Height = height;
}
public override void Setup()
{
base.Setup();
GL.TexImage2D(Target, 0, PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, IntPtr.Zero);
GL.TexParameter(Target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear);
GL.TexParameter(Target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
GL.TexParameter(Target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(Target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Target, Handle, 0);
}
}

View File

@ -0,0 +1,31 @@
using System;
using CUE4Parse.UE4.Objects.Core.Misc;
using FModel.Views.Snooper.Models;
using OpenTK.Graphics.OpenGL4;
namespace FModel.Views.Snooper.Textures;
public interface ITexture : IDelayedSetup
{
public int Handle { get; }
public TextureType Type { get; }
public TextureTarget Target { get; }
public string Name { get; }
public int Width { get; }
public int Height { get; }
public void Bind(TextureUnit unit);
public void Bind(TextureTarget target);
public IntPtr GetPointer();
public void ImGuiInspector();
}
public enum TextureType
{
Normal,
Cubemap,
Framebuffer,
MsaaFramebuffer
}

View File

@ -0,0 +1,33 @@
using CUE4Parse.UE4.Objects.Core.Math;
using CUE4Parse.UE4.Objects.Core.Misc;
using OpenTK.Graphics.OpenGL4;
namespace FModel.Views.Snooper.Textures;
public class LinearColorTexture : AbstractTexture
{
private FLinearColor _color;
public LinearColorTexture(FLinearColor color) : base(TextureType.Normal)
{
_color = color;
// Guid = new FGuid(_color.Hex);
Name = _color.Hex;
Width = 1;
Height = 1;
}
public override void Setup()
{
base.Setup();
GL.TexImage2D(Target, 0, PixelInternalFormat.Rgba, Width, Height, 0, PixelFormat.Rgba, PixelType.Float, ref _color);
GL.TexParameter(Target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear);
GL.TexParameter(Target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
GL.TexParameter(Target, TextureParameterName.TextureBaseLevel, 0);
GL.TexParameter(Target, TextureParameterName.TextureMaxLevel, 8);
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
}
}

View File

@ -0,0 +1,20 @@
using OpenTK.Graphics.OpenGL4;
namespace FModel.Views.Snooper.Textures;
public class MsaaTexture : AbstractTexture
{
public MsaaTexture(uint width, uint height) : base(TextureType.MsaaFramebuffer)
{
Width = (int) width;
Height = (int) height;
}
public override void Setup()
{
base.Setup();
GL.TexImage2DMultisample(TextureTargetMultisample.Texture2DMultisample, Constants.SAMPLES_COUNT, PixelInternalFormat.Rgb, Width, Height, true);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Target, Handle, 0);
}
}