mirror of
https://github.com/4sval/FModel.git
synced 2026-05-11 06:54:40 -05:00
286 lines
8.1 KiB
C#
286 lines
8.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using CUE4Parse_Conversion.Textures;
|
|
using CUE4Parse.UE4.Assets.Exports.Texture;
|
|
using CUE4Parse.UE4.Objects.Core.Misc;
|
|
using FModel.Settings;
|
|
using FModel.Views.Snooper.Animations;
|
|
using FModel.Views.Snooper.Lights;
|
|
using FModel.Views.Snooper.Models;
|
|
using FModel.Views.Snooper.Shading;
|
|
using SkiaSharp;
|
|
|
|
namespace FModel.Views.Snooper;
|
|
|
|
public class Options
|
|
{
|
|
public FGuid SelectedModel { get; private set; }
|
|
public int SelectedSection { get; private set; }
|
|
public int SelectedMorph { get; private set; }
|
|
public int SelectedAnimation{ get; private set; }
|
|
|
|
public readonly Dictionary<FGuid, UModel> Models;
|
|
public readonly Dictionary<FGuid, Texture> Textures;
|
|
public readonly List<Light> Lights;
|
|
|
|
public readonly TimeTracker Tracker;
|
|
public readonly List<Animation> Animations;
|
|
|
|
public readonly Dictionary<string, Texture> Icons;
|
|
|
|
private readonly string _game;
|
|
|
|
public Options()
|
|
{
|
|
Models = new Dictionary<FGuid, UModel>();
|
|
Textures = new Dictionary<FGuid, Texture>();
|
|
Lights = new List<Light>();
|
|
|
|
Tracker = new TimeTracker();
|
|
Animations = new List<Animation>();
|
|
|
|
Icons = new Dictionary<string, Texture>
|
|
{
|
|
["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"),
|
|
};
|
|
|
|
_game = Services.ApplicationService.ApplicationView.CUE4Parse.Provider.ProjectName.ToUpper();
|
|
|
|
SelectModel(Guid.Empty);
|
|
}
|
|
|
|
public void SetupModelsAndLights()
|
|
{
|
|
foreach (var model in Models.Values)
|
|
{
|
|
if (model.IsSetup) continue;
|
|
model.Setup(this);
|
|
}
|
|
|
|
foreach (var light in Lights)
|
|
{
|
|
if (light.IsSetup) continue;
|
|
light.Setup();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public void SelectAnimation(int animation)
|
|
{
|
|
SelectedAnimation = animation;
|
|
}
|
|
|
|
public void RemoveModel(FGuid guid)
|
|
{
|
|
if (!TryGetModel(guid, out var m) || m is not UModel model)
|
|
return;
|
|
|
|
DetachAndRemoveModels(model, true);
|
|
model.Dispose();
|
|
Models.Remove(guid);
|
|
}
|
|
|
|
private void DetachAndRemoveModels(UModel model, bool detach)
|
|
{
|
|
foreach (var socket in model.Sockets.ToList())
|
|
{
|
|
foreach (var info in socket.AttachedModels)
|
|
{
|
|
if (!TryGetModel(info.Guid, out var m) || m is not UModel attachedModel)
|
|
continue;
|
|
|
|
var t = attachedModel.GetTransform();
|
|
if (attachedModel.IsProp)
|
|
{
|
|
attachedModel.Attachments.SafeDetach(model, t);
|
|
RemoveModel(info.Guid);
|
|
}
|
|
else if (detach) attachedModel.Attachments.SafeDetach(model, t);
|
|
}
|
|
|
|
if (socket.IsVirtual)
|
|
{
|
|
socket.Dispose();
|
|
model.Sockets.Remove(socket);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AddAnimation(Animation animation)
|
|
{
|
|
Animations.Add(animation);
|
|
}
|
|
|
|
public void RemoveAnimations()
|
|
{
|
|
Tracker.Reset();
|
|
SelectedAnimation = 0;
|
|
|
|
foreach (var animation in Animations)
|
|
{
|
|
foreach (var guid in animation.AttachedModels)
|
|
{
|
|
if (!TryGetModel(guid, out var model) || model is not SkeletalModel animatedModel)
|
|
continue;
|
|
|
|
animatedModel.Skeleton.ResetAnimatedData(true);
|
|
DetachAndRemoveModels(animatedModel, false);
|
|
}
|
|
|
|
animation.Dispose();
|
|
}
|
|
|
|
foreach (var kvp in Models)
|
|
if (kvp.Value.IsProp)
|
|
RemoveModel(kvp.Key);
|
|
|
|
Animations.Clear();
|
|
}
|
|
|
|
public void SelectSection(int index)
|
|
{
|
|
SelectedSection = index;
|
|
}
|
|
|
|
public void SelectMorph(int index, SkeletalModel model)
|
|
{
|
|
SelectedMorph = index;
|
|
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);
|
|
|
|
public bool TryGetSection(out Section section) => TryGetSection(SelectedModel, out section);
|
|
public bool TryGetSection(FGuid guid, out Section section)
|
|
{
|
|
if (TryGetModel(guid, out var model))
|
|
{
|
|
return TryGetSection(model, out section);
|
|
}
|
|
|
|
section = null;
|
|
return false;
|
|
}
|
|
public bool TryGetSection(UModel model, out Section section)
|
|
{
|
|
if (SelectedSection >= 0 && SelectedSection < model.Sections.Length)
|
|
section = model.Sections[SelectedSection]; else section = null;
|
|
return section != null;
|
|
}
|
|
|
|
public void SwapMaterial(bool value)
|
|
{
|
|
Services.ApplicationService.ApplicationView.CUE4Parse.ModelIsOverwritingMaterial = value;
|
|
}
|
|
|
|
public void AnimateMesh(bool value)
|
|
{
|
|
Services.ApplicationService.ApplicationView.CUE4Parse.ModelIsWaitingAnimation = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Skip emissive for specific games, cause of excessive use in their materials
|
|
/// </summary>
|
|
public bool SkipEmissive()
|
|
{
|
|
return _game switch
|
|
{
|
|
"LIESOFP" or "CODEVEIN2" or "HIGHONLIFE2" => true,
|
|
_ => false,
|
|
};
|
|
}
|
|
|
|
public void ResetModelsLightsAnimations()
|
|
{
|
|
foreach (var model in Models.Values)
|
|
{
|
|
model.Dispose();
|
|
}
|
|
Models.Clear();
|
|
Lights.Clear();
|
|
Tracker.Reset();
|
|
foreach (var animation in Animations)
|
|
{
|
|
animation.Dispose();
|
|
}
|
|
Animations.Clear();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
ResetModelsLightsAnimations();
|
|
foreach (var texture in Textures.Values)
|
|
{
|
|
texture.Dispose();
|
|
}
|
|
Textures.Clear();
|
|
foreach (var texture in Icons.Values)
|
|
{
|
|
texture.Dispose();
|
|
}
|
|
Icons.Clear();
|
|
}
|
|
}
|