You're breathtaking!
This commit is contained in:
489
Source/Editor/Surface/SurfaceUtils.cs
Normal file
489
Source/Editor/Surface/SurfaceUtils.cs
Normal file
@@ -0,0 +1,489 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Surface
|
||||
{
|
||||
internal static class SurfaceUtils
|
||||
{
|
||||
internal struct GraphParameterData
|
||||
{
|
||||
public GraphParameter Parameter;
|
||||
public bool IsPublic;
|
||||
public Type Type;
|
||||
public object Tag;
|
||||
public Attribute[] Attributes;
|
||||
public EditorOrderAttribute Order;
|
||||
public EditorDisplayAttribute Display;
|
||||
public TooltipAttribute Tooltip;
|
||||
public SpaceAttribute Space;
|
||||
public HeaderAttribute Header;
|
||||
public string DisplayName;
|
||||
|
||||
public GraphParameterData(GraphParameter parameter, object tag = null)
|
||||
: this(parameter, null, parameter.IsPublic, parameter.Type, SurfaceMeta.GetAttributes(parameter), tag)
|
||||
{
|
||||
}
|
||||
|
||||
public GraphParameterData(GraphParameter parameter, string name, bool isPublic, Type type, Attribute[] attributes, object tag)
|
||||
{
|
||||
Parameter = parameter;
|
||||
IsPublic = isPublic;
|
||||
Type = type;
|
||||
Tag = tag;
|
||||
Attributes = attributes;
|
||||
Order = (EditorOrderAttribute)Attributes.FirstOrDefault(x => x is EditorOrderAttribute);
|
||||
Display = (EditorDisplayAttribute)Attributes.FirstOrDefault(x => x is EditorDisplayAttribute);
|
||||
Tooltip = (TooltipAttribute)Attributes.FirstOrDefault(x => x is TooltipAttribute);
|
||||
Space = (SpaceAttribute)Attributes.FirstOrDefault(x => x is SpaceAttribute);
|
||||
Header = (HeaderAttribute)Attributes.FirstOrDefault(x => x is HeaderAttribute);
|
||||
DisplayName = Display?.Name ?? name ?? parameter.Name;
|
||||
}
|
||||
|
||||
public static int Compare(GraphParameterData x, GraphParameterData y)
|
||||
{
|
||||
// By order
|
||||
if (x.Order != null)
|
||||
{
|
||||
if (y.Order != null)
|
||||
return x.Order.Order - y.Order.Order;
|
||||
return -1;
|
||||
}
|
||||
if (y.Order != null)
|
||||
return 1;
|
||||
|
||||
// By group name
|
||||
if (x.Display?.Group != null)
|
||||
{
|
||||
if (y.Display?.Group != null)
|
||||
return string.Compare(x.Display.Group, y.Display.Group, StringComparison.InvariantCulture);
|
||||
}
|
||||
|
||||
// By name
|
||||
return string.Compare(x.DisplayName, y.DisplayName, StringComparison.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
private static Type ToType(MaterialParameterType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MaterialParameterType.Bool: return typeof(bool);
|
||||
case MaterialParameterType.Integer: return typeof(int);
|
||||
case MaterialParameterType.Float: return typeof(float);
|
||||
case MaterialParameterType.Vector2: return typeof(Vector2);
|
||||
case MaterialParameterType.Vector3: return typeof(Vector3);
|
||||
case MaterialParameterType.Vector4: return typeof(Vector4);
|
||||
case MaterialParameterType.Color: return typeof(Color);
|
||||
case MaterialParameterType.Texture: return typeof(Texture);
|
||||
case MaterialParameterType.CubeTexture: return typeof(CubeTexture);
|
||||
case MaterialParameterType.NormalMap: return typeof(Texture);
|
||||
case MaterialParameterType.SceneTexture: return typeof(MaterialSceneTextures);
|
||||
case MaterialParameterType.GPUTexture: return typeof(GPUTexture);
|
||||
case MaterialParameterType.Matrix: return typeof(Matrix);
|
||||
case MaterialParameterType.ChannelMask: return typeof(ChannelMask);
|
||||
default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
}
|
||||
|
||||
internal static GraphParameterData[] InitGraphParameters(IEnumerable<MaterialParameter> parameters, Material material)
|
||||
{
|
||||
int count = parameters.Count();
|
||||
var data = new GraphParameterData[count];
|
||||
int i = 0;
|
||||
|
||||
// Load material surface parameters meta to use it for material instance parameters editing
|
||||
SurfaceParameter[] surfaceParameters = null;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Editor.LogError("Failed to get material parameters metadata.");
|
||||
Editor.LogWarning(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
var surfaceParameter = surfaceParameters?.FirstOrDefault(x => x.ID == parameter.ParameterID);
|
||||
var attributes = surfaceParameter?.Meta.GetAttributes() ?? FlaxEngine.Utils.GetEmptyArray<Attribute>();
|
||||
data[i] = new GraphParameterData(null, parameter.Name, parameter.IsPublic, ToType(parameter.ParameterType), attributes, parameter);
|
||||
i++;
|
||||
}
|
||||
Array.Sort(data, GraphParameterData.Compare);
|
||||
return data;
|
||||
}
|
||||
|
||||
internal static GraphParameterData[] InitGraphParameters(IEnumerable<ParticleEffectParameter> parameters)
|
||||
{
|
||||
int count = parameters.Count();
|
||||
var data = new GraphParameterData[count];
|
||||
int i = 0;
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
data[i] = new GraphParameterData(parameter.EmitterParameter, parameter);
|
||||
i++;
|
||||
}
|
||||
Array.Sort(data, GraphParameterData.Compare);
|
||||
return data;
|
||||
}
|
||||
|
||||
internal static GraphParameterData[] InitGraphParameters(IEnumerable<GraphParameter> parameters)
|
||||
{
|
||||
int count = parameters.Count();
|
||||
var data = new GraphParameterData[count];
|
||||
int i = 0;
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
data[i] = new GraphParameterData(parameter);
|
||||
i++;
|
||||
}
|
||||
Array.Sort(data, GraphParameterData.Compare);
|
||||
return data;
|
||||
}
|
||||
|
||||
internal delegate object GetGraphParameterDelegate(object instance, GraphParameter parameter, object parameterTag);
|
||||
|
||||
internal delegate void SetGraphParameterDelegate(object instance, object value, GraphParameter parameter, object parameterTag);
|
||||
|
||||
internal delegate void CustomPropertySpawnDelegate(LayoutElementsContainer itemLayout, ValueContainer valueContainer, ref GraphParameterData e);
|
||||
|
||||
internal static void DisplayGraphParameters(LayoutElementsContainer layout, GraphParameterData[] data, GetGraphParameterDelegate getter, SetGraphParameterDelegate setter, ValueContainer values, GetGraphParameterDelegate defaultValueGetter = null, CustomPropertySpawnDelegate propertySpawn = null)
|
||||
{
|
||||
GroupElement lastGroup = null;
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
ref var e = ref data[i];
|
||||
if (!e.IsPublic)
|
||||
continue;
|
||||
var tag = e.Tag;
|
||||
var parameter = e.Parameter;
|
||||
LayoutElementsContainer itemLayout;
|
||||
|
||||
// Editor Display
|
||||
if (e.Display?.Group != null)
|
||||
{
|
||||
if (lastGroup == null || lastGroup.Panel.HeaderText != e.Display.Group)
|
||||
{
|
||||
lastGroup = layout.Group(e.Display.Group);
|
||||
lastGroup.Panel.Open(false);
|
||||
}
|
||||
itemLayout = lastGroup;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastGroup = null;
|
||||
itemLayout = layout;
|
||||
}
|
||||
|
||||
// Space
|
||||
if (e.Space != null)
|
||||
itemLayout.Space(e.Space.Height);
|
||||
|
||||
// Header
|
||||
if (e.Header != null)
|
||||
itemLayout.Header(e.Header.Text);
|
||||
|
||||
// Values container
|
||||
var valueType = new ScriptType(e.Type);
|
||||
var valueContainer = new CustomValueContainer(valueType, (instance, index) => getter(values[index], parameter, tag), (instance, index, value) => setter(values[index], value, parameter, tag), e.Attributes);
|
||||
for (int j = 0; j < values.Count; j++)
|
||||
valueContainer.Add(getter(values[j], parameter, tag));
|
||||
|
||||
// Default value
|
||||
if (defaultValueGetter != null)
|
||||
valueContainer.SetDefaultValue(defaultValueGetter(values[0], parameter, tag));
|
||||
|
||||
// Prefab value
|
||||
if (values.HasReferenceValue)
|
||||
{
|
||||
var referenceValue = getter(values.ReferenceValue, parameter, tag);
|
||||
if (referenceValue != null)
|
||||
{
|
||||
valueContainer.SetReferenceValue(referenceValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (propertySpawn == null)
|
||||
itemLayout.Property(e.DisplayName, valueContainer, null, e.Tooltip?.Text);
|
||||
else
|
||||
propertySpawn(itemLayout, valueContainer, ref e);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetMethodDisplayName(string methodName)
|
||||
{
|
||||
if (methodName.StartsWith("get_"))
|
||||
return "Get " + methodName.Substring(4);
|
||||
if (methodName.StartsWith("set_"))
|
||||
return "Set " + methodName.Substring(4);
|
||||
if (methodName.StartsWith("op_"))
|
||||
return "Operator " + methodName.Substring(3);
|
||||
return methodName;
|
||||
}
|
||||
|
||||
internal static bool IsValidVisualScriptInvokeMethod(ScriptMemberInfo member, out ScriptMemberInfo.Parameter[] parameters)
|
||||
{
|
||||
parameters = null;
|
||||
if (member.IsMethod &&
|
||||
IsValidVisualScriptType(member.ValueType) &&
|
||||
!member.HasAttribute(typeof(HideInEditorAttribute), true))
|
||||
{
|
||||
parameters = member.GetParameters();
|
||||
if (parameters.Length <= 27)
|
||||
{
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
if (!IsValidVisualScriptType(parameter.Type))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsValidVisualScriptOverrideMethod(ScriptMemberInfo member, out ScriptMemberInfo.Parameter[] parameters)
|
||||
{
|
||||
if (member.IsMethod &&
|
||||
member.IsVirtual &&
|
||||
IsValidVisualScriptType(member.ValueType) &&
|
||||
member.HasAttribute(typeof(UnmanagedAttribute), true) &&
|
||||
!member.HasAttribute(typeof(HideInEditorAttribute), true))
|
||||
{
|
||||
parameters = member.GetParameters();
|
||||
if (parameters.Length < 31)
|
||||
{
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
if (!IsValidVisualScriptType(parameter.Type) || parameter.IsOut)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
parameters = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsValidVisualScriptField(ScriptMemberInfo member)
|
||||
{
|
||||
return member.IsField && IsValidVisualScriptType(member.ValueType);
|
||||
}
|
||||
|
||||
internal static bool IsValidVisualScriptType(ScriptType scriptType)
|
||||
{
|
||||
if (scriptType.IsGenericType || !scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true))
|
||||
return false;
|
||||
var managedType = TypeUtils.GetType(scriptType);
|
||||
return !TypeUtils.IsDelegate(managedType);
|
||||
}
|
||||
|
||||
internal static bool IsValidVisualScriptFunctionType(ScriptType scriptType)
|
||||
{
|
||||
if (scriptType.IsGenericType || scriptType.IsStatic || !scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true))
|
||||
return false;
|
||||
var managedType = TypeUtils.GetType(scriptType);
|
||||
return !TypeUtils.IsDelegate(managedType);
|
||||
}
|
||||
|
||||
internal static string GetVisualScriptMemberInfoDescription(ScriptMemberInfo member)
|
||||
{
|
||||
var name = member.Name;
|
||||
var declaringType = member.DeclaringType;
|
||||
var valueType = member.ValueType;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
if (member.IsStatic)
|
||||
sb.Append("static ");
|
||||
else if (member.IsVirtual)
|
||||
sb.Append("virtual ");
|
||||
sb.Append(valueType.Name);
|
||||
sb.Append(' ');
|
||||
sb.Append(declaringType.Name);
|
||||
sb.Append('.');
|
||||
sb.Append(name);
|
||||
if (member.IsMethod)
|
||||
{
|
||||
// Getter/setter method of the property
|
||||
if (name.StartsWith("get_") || name.StartsWith("set_"))
|
||||
{
|
||||
var flags = member.IsStatic ? BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly : BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||
var property = declaringType.GetMembers(name.Substring(4), MemberTypes.Property, flags);
|
||||
if (property != null && property.Length != 0)
|
||||
{
|
||||
return GetVisualScriptMemberInfoDescription(property[0]);
|
||||
}
|
||||
}
|
||||
// Method
|
||||
else
|
||||
{
|
||||
sb.Append('(');
|
||||
var parameters = member.GetParameters();
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
sb.Append(", ");
|
||||
ref var param = ref parameters[i];
|
||||
param.ToString(sb);
|
||||
}
|
||||
sb.Append(')');
|
||||
}
|
||||
}
|
||||
else if (member.IsProperty)
|
||||
{
|
||||
sb.Append(' ');
|
||||
sb.Append('{');
|
||||
if (member.HasGet)
|
||||
sb.Append(" get;");
|
||||
if (member.HasSet)
|
||||
sb.Append(" set;");
|
||||
sb.Append(' ');
|
||||
sb.Append('}');
|
||||
}
|
||||
|
||||
// Tooltip
|
||||
var attributes = member.GetAttributes();
|
||||
var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute);
|
||||
if (tooltipAttribute != null)
|
||||
sb.Append("\n").Append(tooltipAttribute.Text);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user