juno building preview

This commit is contained in:
Asval 2024-05-25 00:19:47 +02:00
parent eb49b3c853
commit 9781446aef
11 changed files with 208 additions and 54 deletions

@ -1 +1 @@
Subproject commit 52292cb88d4de990fbcf6e07fac883e3a8d13d4f Subproject commit 0c12be3c624d0e75b18b0a03915442610de54672

View File

@ -112,6 +112,7 @@ public class CreatorPackage : IDisposable
case "FortConversionControlItemDefinition": case "FortConversionControlItemDefinition":
case "FortAccountBuffCreditItemDefinition": case "FortAccountBuffCreditItemDefinition":
case "JunoBuildInstructionsItemDefinition": case "JunoBuildInstructionsItemDefinition":
case "JunoBuildingSetAccountItemDefinition":
case "FortEventCurrencyItemDefinitionRedir": case "FortEventCurrencyItemDefinitionRedir":
case "FortPersistentResourceItemDefinition": case "FortPersistentResourceItemDefinition":
case "FortWeaponMeleeOffhandItemDefinition": case "FortWeaponMeleeOffhandItemDefinition":

View File

@ -2,6 +2,7 @@ using System;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ICSharpCode.AvalonEdit.Document;
namespace FModel.Extensions; namespace FModel.Extensions;
@ -94,7 +95,7 @@ public static class StringExtensions
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetLineNumber(this string s, string lineToFind) public static int GetNameLineNumber(this string s, string lineToFind)
{ {
if (int.TryParse(lineToFind, out var index)) if (int.TryParse(lineToFind, out var index))
return s.GetLineNumber(index); return s.GetLineNumber(index);
@ -113,6 +114,24 @@ public static class StringExtensions
return 1; return 1;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string GetParentExportType(this TextDocument doc, int startOffset)
{
var line = doc.GetLineByOffset(startOffset);
var lineNumber = line.LineNumber - 1;
while (doc.GetText(line.Offset, line.Length) is { } content)
{
if (content.StartsWith(" \"Type\": \"", StringComparison.OrdinalIgnoreCase))
return content.Split("\"")[3];
lineNumber--;
line = doc.GetLineByNumber(lineNumber);
}
return string.Empty;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetKismetLineNumber(this string s, string input) public static int GetKismetLineNumber(this string s, string input)
{ {

View File

@ -95,5 +95,5 @@ void main()
fTangent = vec3(transpose(inverse(vInstanceMatrix)) * finalTangent); fTangent = vec3(transpose(inverse(vInstanceMatrix)) * finalTangent);
fTexCoords = vTexCoords; fTexCoords = vTexCoords;
fTexLayer = vTexLayer; fTexLayer = vTexLayer;
fColor = unpackARGB(int(vColor)); fColor = unpackARGB(int(vColor)) / 255.0;
} }

View File

@ -609,23 +609,13 @@ public class CUE4ParseViewModel : ViewModel
var fileName = fullPath.SubstringAfterLast('/'); var fileName = fullPath.SubstringAfterLast('/');
var ext = fullPath.SubstringAfterLast('.').ToLower(); var ext = fullPath.SubstringAfterLast('.').ToLower();
if (addNewTab && TabControl.CanAddTabs) if (addNewTab && TabControl.CanAddTabs) TabControl.AddTab(fileName, directory);
{ else TabControl.SelectedTab.SoftReset(fileName, directory);
TabControl.AddTab(fileName, directory); TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(ext);
}
else
{
TabControl.SelectedTab.Header = fileName;
TabControl.SelectedTab.Directory = directory;
}
var updateUi = !HasFlag(bulk, EBulkType.Auto); var updateUi = !HasFlag(bulk, EBulkType.Auto);
var saveProperties = HasFlag(bulk, EBulkType.Properties); var saveProperties = HasFlag(bulk, EBulkType.Properties);
var saveTextures = HasFlag(bulk, EBulkType.Textures); var saveTextures = HasFlag(bulk, EBulkType.Textures);
TabControl.SelectedTab.ClearImages();
TabControl.SelectedTab.ResetDocumentText();
TabControl.SelectedTab.ScrollTrigger = null;
TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(ext);
switch (ext) switch (ext)
{ {
case "uasset": case "uasset":
@ -801,10 +791,10 @@ public class CUE4ParseViewModel : ViewModel
} }
} }
public void ExtractAndScroll(CancellationToken cancellationToken, string fullPath, string objectName) public void ExtractAndScroll(CancellationToken cancellationToken, string fullPath, string objectName, string parentExportType)
{ {
Log.Information("User CTRL-CLICKED to extract '{FullPath}'", fullPath); Log.Information("User CTRL-CLICKED to extract '{FullPath}'", fullPath);
TabControl.AddTab(fullPath.SubstringAfterLast('/'), fullPath.SubstringBeforeLast('/')); TabControl.AddTab(fullPath.SubstringAfterLast('/'), fullPath.SubstringBeforeLast('/'), parentExportType);
TabControl.SelectedTab.ScrollTrigger = objectName; TabControl.SelectedTab.ScrollTrigger = objectName;
var exports = Provider.LoadAllObjects(fullPath); var exports = Provider.LoadAllObjects(fullPath);
@ -855,6 +845,12 @@ public class CUE4ParseViewModel : ViewModel
return false; return false;
} }
case UWorld when isNone && UserSettings.Default.PreviewWorlds: case UWorld when isNone && UserSettings.Default.PreviewWorlds:
case UBlueprintGeneratedClass when isNone && UserSettings.Default.PreviewWorlds && TabControl.SelectedTab.ParentExportType switch
{
"JunoBuildInstructionsItemDefinition" => true,
"JunoBuildingSetAccountItemDefinition" => true,
_ => false
}:
case UAtomModel when isNone && UserSettings.Default.PreviewStaticMeshes: case UAtomModel when isNone && UserSettings.Default.PreviewStaticMeshes:
case UStaticMesh when isNone && UserSettings.Default.PreviewStaticMeshes: case UStaticMesh when isNone && UserSettings.Default.PreviewStaticMeshes:
case USkeletalMesh when isNone && UserSettings.Default.PreviewSkeletalMeshes: case USkeletalMesh when isNone && UserSettings.Default.PreviewSkeletalMeshes:

View File

@ -86,6 +86,8 @@ public class TabImage : ViewModel
public class TabItem : ViewModel public class TabItem : ViewModel
{ {
public string ParentExportType { get; private set; }
private string _header; private string _header;
public string Header public string Header
{ {
@ -211,20 +213,28 @@ public class TabItem : ViewModel
private GoToCommand _goToCommand; private GoToCommand _goToCommand;
public GoToCommand GoToCommand => _goToCommand ??= new GoToCommand(null); public GoToCommand GoToCommand => _goToCommand ??= new GoToCommand(null);
public TabItem(string header, string directory) public TabItem(string header, string directory, string parentExportType)
{ {
Header = header; Header = header;
Directory = directory; Directory = directory;
ParentExportType = parentExportType;
_images = new ObservableCollection<TabImage>(); _images = new ObservableCollection<TabImage>();
} }
public void ClearImages() public void SoftReset(string header, string directory)
{ {
Header = header;
Directory = directory;
ParentExportType = string.Empty;
ScrollTrigger = null;
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
_images.Clear(); _images.Clear();
SelectedImage = null; SelectedImage = null;
RaisePropertyChanged("HasMultipleImages"); RaisePropertyChanged("HasMultipleImages");
Document ??= new TextDocument();
Document.Text = string.Empty;
}); });
} }
@ -274,15 +284,6 @@ public class TabItem : ViewModel
}); });
} }
public void ResetDocumentText()
{
Application.Current.Dispatcher.Invoke(() =>
{
Document ??= new TextDocument();
Document.Text = string.Empty;
});
}
public void SaveImage() => SaveImage(SelectedImage, true); public void SaveImage() => SaveImage(SelectedImage, true);
private void SaveImage(TabImage image, bool updateUi) private void SaveImage(TabImage image, bool updateUi)
{ {
@ -368,12 +369,13 @@ public class TabControlViewModel : ViewModel
SelectedTab = TabsItems.FirstOrDefault(); SelectedTab = TabsItems.FirstOrDefault();
} }
public void AddTab(string header = null, string directory = null) public void AddTab(string header = null, string directory = null, string parentExportType = null)
{ {
if (!CanAddTabs) return; if (!CanAddTabs) return;
var h = header ?? "New Tab"; var h = header ?? "New Tab";
var d = directory ?? string.Empty; var d = directory ?? string.Empty;
var p = parentExportType ?? string.Empty;
if (SelectedTab is { Header : "New Tab" }) if (SelectedTab is { Header : "New Tab" })
{ {
SelectedTab.Header = h; SelectedTab.Header = h;
@ -383,7 +385,7 @@ public class TabControlViewModel : ViewModel
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
_tabItems.Add(new TabItem(h, d)); _tabItems.Add(new TabItem(h, d, p));
SelectedTab = _tabItems.Last(); SelectedTab = _tabItems.Last();
}); });
} }
@ -445,6 +447,6 @@ public class TabControlViewModel : ViewModel
private static IEnumerable<TabItem> EnumerateTabs() private static IEnumerable<TabItem> EnumerateTabs()
{ {
yield return new TabItem("New Tab", string.Empty); yield return new TabItem("New Tab", string.Empty, string.Empty);
} }
} }

View File

@ -1,4 +1,5 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using FModel.Extensions;
using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Rendering;
namespace FModel.Views.Resources.Controls; namespace FModel.Views.Resources.Controls;
@ -29,8 +30,10 @@ public class GamePathElementGenerator : VisualLineElementGenerator
public override VisualLineElement ConstructElement(int offset) public override VisualLineElement ConstructElement(int offset)
{ {
var m = FindMatch(offset); var m = FindMatch(offset);
if (!m.Success || m.Index != 0) return null; if (!m.Success || m.Index != 0 ||
!m.Groups.TryGetValue("target", out var g)) return null;
return m.Groups.TryGetValue("target", out var g) ? new GamePathVisualLineText(g.Value, CurrentContext.VisualLine, g.Length + g.Index + 1) : null; var parentExportType = CurrentContext.Document.GetParentExportType(offset);
return new GamePathVisualLineText(g.Value, parentExportType, CurrentContext.VisualLine, g.Length + g.Index + 1);
} }
} }

View File

@ -16,14 +16,16 @@ public class GamePathVisualLineText : VisualLineText
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView; private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
private ApplicationViewModel _applicationView => ApplicationService.ApplicationView; private ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
public delegate void GamePathOnClick(string gamePath); public delegate void GamePathOnClick(string gamePath, string parentExportType);
public event GamePathOnClick OnGamePathClicked; public event GamePathOnClick OnGamePathClicked;
private readonly string _gamePath; private readonly string _gamePath;
private readonly string _parentExportType;
public GamePathVisualLineText(string gamePath, VisualLine parentVisualLine, int length) : base(parentVisualLine, length) public GamePathVisualLineText(string gamePath, string parentExportType, VisualLine parentVisualLine, int length) : base(parentVisualLine, length)
{ {
_gamePath = gamePath; _gamePath = gamePath;
_parentExportType = parentExportType;
} }
public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context) public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
@ -56,14 +58,14 @@ public class GamePathVisualLineText : VisualLineText
if (e.Handled || OnGamePathClicked == null) if (e.Handled || OnGamePathClicked == null)
return; return;
OnGamePathClicked(_gamePath); OnGamePathClicked(_gamePath, _parentExportType);
e.Handled = true; e.Handled = true;
} }
protected override VisualLineText CreateInstance(int length) protected override VisualLineText CreateInstance(int length)
{ {
var a = new GamePathVisualLineText(_gamePath, ParentVisualLine, length); var a = new GamePathVisualLineText(_gamePath, _parentExportType, ParentVisualLine, length);
a.OnGamePathClicked += async gamePath => a.OnGamePathClicked += async (gamePath, parentExportType) =>
{ {
var obj = gamePath.SubstringAfterLast('.'); var obj = gamePath.SubstringAfterLast('.');
var package = gamePath.SubstringBeforeLast('.'); var package = gamePath.SubstringBeforeLast('.');
@ -80,17 +82,17 @@ public class GamePathVisualLineText : VisualLineText
} }
else else
{ {
lineNumber = a.ParentVisualLine.Document.Text.GetLineNumber(obj); lineNumber = a.ParentVisualLine.Document.Text.GetNameLineNumber(obj);
line = a.ParentVisualLine.Document.GetLineByNumber(lineNumber); line = a.ParentVisualLine.Document.GetLineByNumber(lineNumber);
} }
AvalonEditor.YesWeEditor.Select(line.Offset, line.Length); AvalonEditor.YesWeEditor.Select(line.Offset, line.Length);
AvalonEditor.YesWeEditor.ScrollToLine(lineNumber); AvalonEditor.YesWeEditor.ScrollToLine(lineNumber);
} }
else else
{ {
await _threadWorkerView.Begin(cancellationToken => await _threadWorkerView.Begin(cancellationToken =>
_applicationView.CUE4Parse.ExtractAndScroll(cancellationToken, fullPath, obj)); _applicationView.CUE4Parse.ExtractAndScroll(cancellationToken, fullPath, obj, parentExportType));
} }
}; };
return a; return a;

View File

@ -126,7 +126,7 @@ public partial class AvalonEditor
if (!tabItem.ShouldScroll) return; if (!tabItem.ShouldScroll) return;
var lineNumber = avalonEditor.Document.Text.GetLineNumber(tabItem.ScrollTrigger); var lineNumber = avalonEditor.Document.Text.GetNameLineNumber(tabItem.ScrollTrigger);
var line = avalonEditor.Document.GetLineByNumber(lineNumber); var line = avalonEditor.Document.GetLineByNumber(lineNumber);
avalonEditor.Select(line.Offset, line.Length); avalonEditor.Select(line.Offset, line.Length);
avalonEditor.ScrollToLine(lineNumber); avalonEditor.ScrollToLine(lineNumber);

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Threading; using System.Threading;
@ -9,6 +10,7 @@ using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Assets.Exports.Animation; using CUE4Parse.UE4.Assets.Exports.Animation;
using CUE4Parse.UE4.Assets.Exports.Atom; using CUE4Parse.UE4.Assets.Exports.Atom;
using CUE4Parse.UE4.Assets.Exports.Component.StaticMesh; using CUE4Parse.UE4.Assets.Exports.Component.StaticMesh;
using CUE4Parse.UE4.Assets.Exports.GeometryCollection;
using CUE4Parse.UE4.Assets.Exports.Material; using CUE4Parse.UE4.Assets.Exports.Material;
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh; using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
using CUE4Parse.UE4.Assets.Exports.StaticMesh; using CUE4Parse.UE4.Assets.Exports.StaticMesh;
@ -79,8 +81,8 @@ public class Renderer : IDisposable
public void Load(CancellationToken cancellationToken, UObject export) public void Load(CancellationToken cancellationToken, UObject export)
{ {
ShowLights = false; ShowLights = false;
_saveCameraMode = export is not UWorld; Color = VertexColor.Default;
CameraOp.Mode = _saveCameraMode ? UserSettings.Default.CameraMode : Camera.WorldMode.FlyCam; _saveCameraMode = export is not UWorld and not UBlueprintGeneratedClass;
switch (export) switch (export)
{ {
case UStaticMesh st: case UStaticMesh st:
@ -98,10 +100,15 @@ public class Renderer : IDisposable
case UWorld wd: case UWorld wd:
LoadWorld(cancellationToken, wd, Transform.Identity); LoadWorld(cancellationToken, wd, Transform.Identity);
break; break;
case UBlueprintGeneratedClass bp:
LoadJunoWorld(cancellationToken, bp, Transform.Identity);
Color = VertexColor.Colors;
break;
case UAtomModel at: case UAtomModel at:
LoadAtom(cancellationToken, at); LoadAtom(cancellationToken, at);
break; break;
} }
CameraOp.Mode = _saveCameraMode ? UserSettings.Default.CameraMode : Camera.WorldMode.FlyCam;
SetupCamera(); SetupCamera();
} }
@ -433,6 +440,49 @@ public class Renderer : IDisposable
Services.ApplicationService.ApplicationView.Status.UpdateStatusLabel($"{original.Name} ... {length}/{length}"); Services.ApplicationService.ApplicationView.Status.UpdateStatusLabel($"{original.Name} ... {length}/{length}");
} }
private void LoadJunoWorld(CancellationToken cancellationToken, UBlueprintGeneratedClass original, Transform transform)
{
CameraOp.Setup(new FBox(FVector.ZeroVector, new FVector(0, 10, 10)));
var length = 0;
FPackageIndex[] allNodes = [];
IPropertyHolder[] records = [];
if (original.TryGetValue(out FPackageIndex simpleConstructionScript, "SimpleConstructionScript") &&
simpleConstructionScript.TryLoad(out var scs) && scs.TryGetValue(out allNodes, "AllNodes"))
length = allNodes.Length;
else if (original.TryGetValue(out FPackageIndex inheritableComponentHandler, "InheritableComponentHandler") &&
inheritableComponentHandler.TryLoad(out var ich) && ich.TryGetValue(out records, "Records"))
length = records.Length;
for (var i = 0; i < length; i++)
{
cancellationToken.ThrowIfCancellationRequested();
IPropertyHolder actor;
if (allNodes is {Length: > 0} && allNodes[i].TryLoad(out UObject node))
{
actor = node;
}
else if (records is {Length: > 0})
{
actor = records[i];
}
else continue;
Services.ApplicationService.ApplicationView.Status.UpdateStatusLabel($"{original.Name} ... {i}/{length}");
WorldMesh(actor, transform, true);
}
Services.ApplicationService.ApplicationView.Status.UpdateStatusLabel($"{original.Name} ... {length}/{length}");
if (Options.Models.Count == 1)
{
var (guid, model) = Options.Models.First();
Options.SelectModel(guid);
CameraOp.Setup(model.Box);
_saveCameraMode = true;
}
}
private void WorldCamera(UObject actor) private void WorldCamera(UObject actor)
{ {
if (actor.ExportType != "LevelBounds" || !actor.TryGetValue(out FPackageIndex boxComponent, "BoxComponent") || if (actor.ExportType != "LevelBounds" || !actor.TryGetValue(out FPackageIndex boxComponent, "BoxComponent") ||
@ -463,7 +513,7 @@ public class Renderer : IDisposable
} }
} }
private void WorldMesh(UObject actor, Transform transform) private void WorldMesh(IPropertyHolder actor, Transform transform, bool forceShow = false)
{ {
if (actor.TryGetValue(out FPackageIndex[] instanceComponents, "InstanceComponents")) if (actor.TryGetValue(out FPackageIndex[] instanceComponents, "InstanceComponents"))
{ {
@ -491,7 +541,24 @@ public class Renderer : IDisposable
else ProcessMesh(actor, staticMeshComp, m, CalculateTransform(staticMeshComp, transform)); else ProcessMesh(actor, staticMeshComp, m, CalculateTransform(staticMeshComp, transform));
} }
} }
else if (actor.TryGetValue(out FPackageIndex staticMeshComponent, "StaticMeshComponent", "StaticMesh", "Mesh", "LightMesh") && else if (actor.TryGetValue(out FPackageIndex componentTemplate, "ComponentTemplate") &&
componentTemplate.TryLoad(out UObject compTemplate))
{
UGeometryCollection geometryCollection = null;
if (!compTemplate.TryGetValue(out UStaticMesh m, "StaticMesh") &&
compTemplate.TryGetValue(out FPackageIndex restCollection, "RestCollection") &&
restCollection.TryLoad(out geometryCollection) && geometryCollection.RootProxyData is { ProxyMeshes.Length: > 0 } rootProxyData)
{
rootProxyData.ProxyMeshes[0].TryLoad(out m);
}
if (m is { Materials.Length: > 0 })
{
OverrideJunoVertexColors(m, geometryCollection);
ProcessMesh(actor, compTemplate, m, CalculateTransform(compTemplate, transform), forceShow);
}
}
else if (actor.TryGetValue(out FPackageIndex staticMeshComponent, "StaticMeshComponent", "ComponentTemplate", "StaticMesh", "Mesh", "LightMesh") &&
staticMeshComponent.TryLoad(out UStaticMeshComponent staticMeshComp) && staticMeshComponent.TryLoad(out UStaticMeshComponent staticMeshComp) &&
staticMeshComp.GetStaticMesh().TryLoad(out UStaticMesh m) && m.Materials.Length > 0) staticMeshComp.GetStaticMesh().TryLoad(out UStaticMesh m) && m.Materials.Length > 0)
{ {
@ -499,11 +566,14 @@ public class Renderer : IDisposable
} }
} }
private void ProcessMesh(UObject actor, UStaticMeshComponent staticMeshComp, UStaticMesh m, Transform transform) private void ProcessMesh(IPropertyHolder actor, UStaticMeshComponent staticMeshComp, UStaticMesh m, Transform transform)
{
OverrideVertexColors(staticMeshComp, m);
ProcessMesh(actor, staticMeshComp, m, transform, false);
}
private void ProcessMesh(IPropertyHolder actor, UObject staticMeshComp, UStaticMesh m, Transform transform, bool forceShow)
{ {
var guid = m.LightingGuid; var guid = m.LightingGuid;
OverrideVertexColors(staticMeshComp, m);
if (Options.TryGetModel(guid, out var model)) if (Options.TryGetModel(guid, out var model))
{ {
model.AddInstance(transform); model.AddInstance(transform);
@ -561,6 +631,13 @@ public class Renderer : IDisposable
} }
} }
if (forceShow)
{
foreach (var section in model.Sections)
{
section.Show = true;
}
}
Options.Models[guid] = model; Options.Models[guid] = model;
} }
@ -576,7 +653,7 @@ public class Renderer : IDisposable
} }
} }
private Transform CalculateTransform(UStaticMeshComponent staticMeshComp, Transform relation) private Transform CalculateTransform(IPropertyHolder staticMeshComp, Transform relation)
{ {
return new Transform return new Transform
{ {
@ -587,6 +664,60 @@ public class Renderer : IDisposable
}; };
} }
private void OverrideJunoVertexColors(UStaticMesh staticMesh, UGeometryCollection geometryCollection = null)
{
if (staticMesh.RenderData is not { LODs.Length: > 0 } || staticMesh.RenderData.LODs[0].ColorVertexBuffer == null)
return;
var dico = new Dictionary<byte, FColor>();
if (geometryCollection?.Materials is not { Length: > 0 })
{
var distinctReds = new HashSet<byte>();
for (int i = 0; i < staticMesh.RenderData.LODs[0].ColorVertexBuffer.Data.Length; i++)
{
ref var vertexColor = ref staticMesh.RenderData.LODs[0].ColorVertexBuffer.Data[i];
var indexAsByte = vertexColor.R;
if (vertexColor.R == 255) indexAsByte = vertexColor.A;
distinctReds.Add(indexAsByte);
}
foreach (var indexAsByte in distinctReds)
{
var path = string.Concat("/JunoAtomAssets/Materials/MI_LegoStandard_", indexAsByte, ".MI_LegoStandard_", indexAsByte);
if (!Utils.TryLoadObject(path, out UMaterialInterface unrealMaterial))
continue;
var parameters = new CMaterialParams2();
unrealMaterial.GetParams(parameters, EMaterialFormat.FirstLayer);
if (!parameters.TryGetLinearColor(out var color, "Color"))
color = FLinearColor.Gray;
dico[indexAsByte] = color.ToFColor(true);
}
}
else foreach (var material in geometryCollection.Materials)
{
if (!material.TryLoad(out UMaterialInterface unrealMaterial)) continue;
var parameters = new CMaterialParams2();
unrealMaterial.GetParams(parameters, EMaterialFormat.FirstLayer);
if (!byte.TryParse(material.Name.SubstringAfterLast("_"), out var indexAsByte))
indexAsByte = byte.MaxValue;
if (!parameters.TryGetLinearColor(out var color, "Color"))
color = FLinearColor.Gray;
dico[indexAsByte] = color.ToFColor(true);
}
for (int i = 0; i < staticMesh.RenderData.LODs[0].ColorVertexBuffer.Data.Length; i++)
{
ref var vertexColor = ref staticMesh.RenderData.LODs[0].ColorVertexBuffer.Data[i];
vertexColor = dico.TryGetValue(vertexColor.R, out var color) ? color : FColor.Gray;
}
}
private void OverrideVertexColors(UStaticMeshComponent staticMeshComp, UStaticMesh staticMesh) private void OverrideVertexColors(UStaticMeshComponent staticMeshComp, UStaticMesh staticMesh)
{ {
if (staticMeshComp.LODData is not { Length: > 0 } || staticMesh.RenderData is not { LODs.Length: > 0 }) if (staticMeshComp.LODData is not { Length: > 0 } || staticMesh.RenderData is not { LODs.Length: > 0 })

View File

@ -76,8 +76,8 @@ public class Material : IDisposable
if (uvCount < 1 || Parameters.IsNull) if (uvCount < 1 || Parameters.IsNull)
{ {
Diffuse = [new Texture(new FLinearColor(.6f, .6f, .6f, 1f))]; Diffuse = [new Texture(FLinearColor.Gray)];
Normals = [new Texture(new FLinearColor(0.498f, 0.498f, 0.996f, 1f))]; Normals = [new Texture(new FLinearColor(0.5f, 0.5f, 1f, 1f))];
SpecularMasks = [new Texture(new FLinearColor(1f, 0.5f, 0.5f, 1f))]; SpecularMasks = [new Texture(new FLinearColor(1f, 0.5f, 0.5f, 1f))];
Emissive = new Texture[1]; Emissive = new Texture[1];
DiffuseColor = [Vector4.One]; DiffuseColor = [Vector4.One];