533 lines
22 KiB
C#
533 lines
22 KiB
C#
// Copyright (c) 2012-2022 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);
|
|
case MaterialParameterType.GameplayGlobal: return typeof(GameplayGlobals);
|
|
case MaterialParameterType.TextureGroupSampler: return typeof(int);
|
|
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)
|
|
{
|
|
CustomEditors.Editors.GenericEditor.OnGroupUsage();
|
|
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;
|
|
|
|
// Editor Display
|
|
var itemLayout = CustomEditors.Editors.GenericEditor.OnGroup(layout, e.Display);
|
|
if (itemLayout is GroupElement groupElement)
|
|
groupElement.Panel.Open(false);
|
|
|
|
// Space
|
|
if (e.Space != null)
|
|
itemLayout.Space(e.Space.Height);
|
|
|
|
// Header
|
|
if (e.Header != null)
|
|
itemLayout.Header(e.Header);
|
|
|
|
// 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);
|
|
}
|
|
CustomEditors.Editors.GenericEditor.OnGroupUsage();
|
|
}
|
|
|
|
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 IsValidVisualScriptEvent(ScriptMemberInfo member)
|
|
{
|
|
return member.IsEvent && member.HasAttribute(typeof(UnmanagedAttribute));
|
|
}
|
|
|
|
internal static bool IsValidVisualScriptType(ScriptType scriptType)
|
|
{
|
|
if (!scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true))
|
|
return false;
|
|
if (scriptType.IsGenericType)
|
|
{
|
|
// Only Dictionary generic type is valid
|
|
return scriptType.GetGenericTypeDefinition() == typeof(Dictionary<,>);
|
|
}
|
|
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 GetVisualScriptTypeDescription(ScriptType type)
|
|
{
|
|
if (type == ScriptType.Null)
|
|
return "null";
|
|
var sb = new StringBuilder();
|
|
if (type.IsStatic)
|
|
sb.Append("static ");
|
|
else if (type.IsAbstract)
|
|
sb.Append("abstract ");
|
|
sb.Append(Editor.Instance.CodeDocs.GetTooltip(type));
|
|
return sb.ToString();
|
|
}
|
|
|
|
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 tooltip = Editor.Instance.CodeDocs.GetTooltip(member);
|
|
if (!string.IsNullOrEmpty(tooltip))
|
|
sb.Append("\n").Append(tooltip);
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
internal static Double4 GetDouble4(object v, float w = 0.0f)
|
|
{
|
|
var value = new Double4(0, 0, 0, w);
|
|
if (v is Vector2 vec2)
|
|
value = new Double4(vec2, 0.0f, w);
|
|
else if (v is Vector3 vec3)
|
|
value = new Double4(vec3, w);
|
|
else if (v is Vector4 vec4)
|
|
value = vec4;
|
|
else if (v is Float2 float2)
|
|
value = new Double4(float2, 0.0, w);
|
|
else if (v is Float3 float3)
|
|
value = new Double4(float3, w);
|
|
else if (v is Float4 float4)
|
|
value = float4;
|
|
else if (v is Double2 double2)
|
|
value = new Double4(double2, 0.0, w);
|
|
else if (v is Double3 double3)
|
|
value = new Double4(double3, w);
|
|
else if (v is Double4 double4)
|
|
value = double4;
|
|
else if (v is Color col)
|
|
value = new Double4(col.R, col.G, col.B, col.A);
|
|
else if (v is float f)
|
|
value = new Double4(f);
|
|
else if (v is int i)
|
|
value = new Double4(i);
|
|
return value;
|
|
}
|
|
}
|
|
}
|