From c62575612b32e85f131878327e7f5893833e52e9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 9 Sep 2024 12:34:18 +0200 Subject: [PATCH] Fix missing parameters metadata on nested material layers #731 --- Source/Editor/Surface/MaterialSurface.cs | 2 +- Source/Editor/Surface/SurfaceUtils.cs | 218 ++++++++---------- .../MaterialGenerator.Layer.cpp | 11 +- 3 files changed, 100 insertions(+), 131 deletions(-) diff --git a/Source/Editor/Surface/MaterialSurface.cs b/Source/Editor/Surface/MaterialSurface.cs index 0d6e437b8..077e59fc0 100644 --- a/Source/Editor/Surface/MaterialSurface.cs +++ b/Source/Editor/Surface/MaterialSurface.cs @@ -17,7 +17,7 @@ namespace FlaxEditor.Surface public class MaterialSurface : VisjectSurface { /// - public MaterialSurface(IVisjectSurfaceOwner owner, Action onSave, FlaxEditor.Undo undo) + public MaterialSurface(IVisjectSurfaceOwner owner, Action onSave = null, FlaxEditor.Undo undo = null) : base(owner, onSave, undo) { } diff --git a/Source/Editor/Surface/SurfaceUtils.cs b/Source/Editor/Surface/SurfaceUtils.cs index 86acf7b26..fe79ec3af 100644 --- a/Source/Editor/Surface/SurfaceUtils.cs +++ b/Source/Editor/Surface/SurfaceUtils.cs @@ -73,9 +73,8 @@ namespace FlaxEditor.Surface // By name if (Editor.Instance.Options.Options.General.ScriptMembersOrder == GeneralOptions.MembersOrder.Alphabetical) - { return string.Compare(x.DisplayName, y.DisplayName, StringComparison.InvariantCulture); - } + // Keep same order return 0; } @@ -106,6 +105,79 @@ namespace FlaxEditor.Surface } } + private sealed class DummyMaterialSurfaceOwner : IVisjectSurfaceOwner + { + public Asset SurfaceAsset => null; + public string SurfaceName => null; + public FlaxEditor.Undo Undo => null; + public byte[] SurfaceData { get; set; } + public VisjectSurfaceContext ParentContext => null; + + public void OnContextCreated(VisjectSurfaceContext context) + { + } + + public void OnSurfaceEditedChanged() + { + } + + public void OnSurfaceGraphEdited() + { + } + + public void OnSurfaceClose() + { + } + } + + private static void FindGraphParameters(Material material, List surfaceParameters) + { + if (material == null || material.WaitForLoaded()) + return; + var surfaceData = material.LoadSurface(false); + if (surfaceData != null && surfaceData.Length > 0) + { + var surfaceOwner = new DummyMaterialSurfaceOwner { SurfaceData = surfaceData }; + var surface = new MaterialSurface(surfaceOwner); + if (!surface.Load()) + { + surfaceParameters.AddRange(surface.Parameters); + + // Search for any nested parameters (eg. via Sample Layer) + foreach (var node in surface.Nodes) + { + if (node.GroupArchetype.GroupID == 8 && node.Archetype.TypeID == 1) // Sample Layer + { + if (node.Values != null && node.Values.Length > 0 && node.Values[0] is Guid layerId) + { + var layer = FlaxEngine.Content.Load(layerId); + if (layer) + { + FindGraphParameters(layer, surfaceParameters); + } + } + } + } + } + } + } + + private static void FindGraphParameters(MaterialBase materialBase, List surfaceParameters) + { + while (materialBase != null && !materialBase.WaitForLoaded()) + { + if (materialBase is MaterialInstance materialInstance) + { + materialBase = materialInstance.BaseMaterial; + } + else if (materialBase is Material material) + { + FindGraphParameters(material, surfaceParameters); + break; + } + } + } + internal static GraphParameterData[] InitGraphParameters(IEnumerable parameters, Material material) { int count = parameters.Count(); @@ -113,128 +185,11 @@ namespace FlaxEditor.Surface int i = 0; // Load material surface parameters meta to use it for material instance parameters editing - SurfaceParameter[] surfaceParameters = null; + var surfaceParameters = new List(); try { Profiler.BeginEvent("Init Material Parameters UI Data"); - - if (material != null && !material.WaitForLoaded()) - { - var surfaceData = material.LoadSurface(false); - if (surfaceData != null && surfaceData.Length > 0) - { - using (var memoryStream = new MemoryStream(surfaceData)) - using (var stream = new BinaryReader(memoryStream)) - { - // IMPORTANT! This must match C++ Graph format - - // Magic Code - int tmp = stream.ReadInt32(); - if (tmp != 1963542358) - { - // Error - throw new Exception("Invalid Graph format version"); - } - - // Version - var version = stream.ReadUInt32(); - var guidBytes = new byte[16]; - if (version < 7000) - { - // Time saved (not used anymore to prevent binary diffs after saving unmodified surface) - stream.ReadInt64(); - - // Nodes count - int nodesCount = stream.ReadInt32(); - - // Parameters count - int parametersCount = stream.ReadInt32(); - - // For each node - for (int j = 0; j < nodesCount; j++) - { - // ID - stream.ReadUInt32(); - - // Type - stream.ReadUInt16(); - stream.ReadUInt16(); - } - - // For each param - surfaceParameters = new SurfaceParameter[parametersCount]; - for (int j = 0; j < parametersCount; j++) - { - // Create param - var param = new SurfaceParameter(); - surfaceParameters[j] = param; - - // Properties - param.Type = new ScriptType(VisjectSurfaceContext.GetGraphParameterValueType((VisjectSurfaceContext.GraphParamType_Deprecated)stream.ReadByte())); - stream.Read(guidBytes, 0, 16); - param.ID = new Guid(guidBytes); - param.Name = stream.ReadStr(97); - param.IsPublic = stream.ReadByte() != 0; - var isStatic = stream.ReadByte() != 0; - var isUIVisible = stream.ReadByte() != 0; - var isUIEditable = stream.ReadByte() != 0; - - // References [Deprecated] - int refsCount = stream.ReadInt32(); - for (int k = 0; k < refsCount; k++) - stream.ReadUInt32(); - - // Value - stream.ReadCommonValue(ref param.Value); - - // Meta - param.Meta.Load(stream); - } - } - else if (version == 7000) - { - // Nodes count - int nodesCount = stream.ReadInt32(); - - // Parameters count - int parametersCount = stream.ReadInt32(); - - // For each node - for (int j = 0; j < nodesCount; j++) - { - // ID - stream.ReadUInt32(); - - // Type - stream.ReadUInt16(); - stream.ReadUInt16(); - } - - // For each param - surfaceParameters = new SurfaceParameter[parametersCount]; - for (int j = 0; j < parametersCount; j++) - { - // Create param - var param = new SurfaceParameter(); - surfaceParameters[j] = param; - - // Properties - param.Type = stream.ReadVariantScriptType(); - stream.Read(guidBytes, 0, 16); - param.ID = new Guid(guidBytes); - param.Name = stream.ReadStr(97); - param.IsPublic = stream.ReadByte() != 0; - - // Value - param.Value = stream.ReadVariant(); - - // Meta - param.Meta.Load(stream); - } - } - } - } - } + FindGraphParameters(material, surfaceParameters); } catch (Exception ex) { @@ -248,7 +203,26 @@ namespace FlaxEditor.Surface foreach (var parameter in parameters) { - var surfaceParameter = surfaceParameters?.FirstOrDefault(x => x.ID == parameter.ParameterID); + var parameterId = parameter.ParameterID; + var surfaceParameter = surfaceParameters.FirstOrDefault(x => x.ID == parameterId); + if (surfaceParameter == null) + { + // Permutate original parameter ID to reflect logic in MaterialGenerator::prepareLayer used for nested layers + unsafe + { + var raw = parameterId; + var interop = *(FlaxEngine.Json.JsonSerializer.GuidInterop*)&raw; + interop.A -= (uint)(i * 17 + 13); + parameterId = *(Guid*)&interop; + } + surfaceParameter = surfaceParameters.FirstOrDefault(x => x.ID == parameterId); + } + if (surfaceParameter != null) + { + // Reorder so it won't be picked by other parameter that uses the same ID (eg. params from duplicated materials used as layers in other material) + surfaceParameters.Remove(surfaceParameter); + surfaceParameters.Add(surfaceParameter); + } var attributes = surfaceParameter?.Meta.GetAttributes() ?? FlaxEngine.Utils.GetEmptyArray(); data[i] = new GraphParameterData(null, parameter.Name, parameter.IsPublic, ToType(parameter.ParameterType), attributes, parameter); i++; diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp index 4b3654865..7f316dfd5 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp @@ -192,15 +192,10 @@ void MaterialGenerator::prepareLayer(MaterialLayer* layer, bool allowVisiblePara // For all not root layers (sub-layers) we won't to change theirs ID in order to prevent duplicated ID) m.SrcId = param->Identifier; - if (isRooLayer) + m.DstId = param->Identifier; + if (!isRooLayer) { - // Use the same ID (so we can edit it) - m.DstId = param->Identifier; - } - else - { - // Generate new ID - m.DstId = param->Identifier; + // Generate new ID (stable permutation based on the original ID) m.DstId.A += _parameters.Count() * 17 + 13; } layer->ParamIdsMappings.Add(m);