mirror of
https://github.com/4sval/FModel.git
synced 2026-03-21 17:24:26 -05:00
delay the setup phase of textures and materials
This commit is contained in:
parent
cc2ff71759
commit
8303cc6cf1
|
|
@ -1 +1 @@
|
|||
Subproject commit 340b7567fdc2ca050d9042389d09540be1044159
|
||||
Subproject commit 691b9b93ced53f723119106bc75094da77a4eaf3
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
266
FModel/Views/Snooper/AssetPool.cs
Normal file
266
FModel/Views/Snooper/AssetPool.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
11
FModel/Views/Snooper/IDelayedSetup.cs
Normal file
11
FModel/Views/Snooper/IDelayedSetup.cs
Normal 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();
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
141
FModel/Views/Snooper/Textures/AbstractTexture.cs
Normal file
141
FModel/Views/Snooper/Textures/AbstractTexture.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
74
FModel/Views/Snooper/Textures/ApplicationTexture.cs
Normal file
74
FModel/Views/Snooper/Textures/ApplicationTexture.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
161
FModel/Views/Snooper/Textures/BitmapTexture.cs
Normal file
161
FModel/Views/Snooper/Textures/BitmapTexture.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
FModel/Views/Snooper/Textures/FramebufferTexture.cs
Normal file
27
FModel/Views/Snooper/Textures/FramebufferTexture.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
31
FModel/Views/Snooper/Textures/ITexture.cs
Normal file
31
FModel/Views/Snooper/Textures/ITexture.cs
Normal 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
|
||||
}
|
||||
33
FModel/Views/Snooper/Textures/LinearColorTexture.cs
Normal file
33
FModel/Views/Snooper/Textures/LinearColorTexture.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
20
FModel/Views/Snooper/Textures/MsaaTexture.cs
Normal file
20
FModel/Views/Snooper/Textures/MsaaTexture.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user