Tim Cooper
9 年前
当前提交
418682b1
共有 26 个文件被更改,包括 1444 次插入 和 102 次删除
-
10UnityProject/Assets/UnityShaderEditor/Editor/Source/Drawing/DrawableMaterialNode.cs
-
16UnityProject/Assets/UnityShaderEditor/Editor/Source/Drawing/MaterialGraphDataSource.cs
-
12UnityProject/Assets/UnityShaderEditor/Editor/Source/Drawing/MaterialWindow.cs
-
6UnityProject/Assets/UnityShaderEditor/Editor/Source/Drawing/NodeAnchor.cs
-
6UnityProject/Assets/UnityShaderEditor/Editor/Source/Drawing/NullInputProxy.cs
-
26UnityProject/Assets/UnityShaderEditor/Editor/Source/Light/BaseLightFunction.cs
-
2UnityProject/Assets/UnityShaderEditor/Editor/Source/MaterialGraph.cs
-
2UnityProject/Assets/UnityShaderEditor/Editor/Source/Nodes/AddNode.cs
-
16UnityProject/Assets/UnityShaderEditor/Editor/Source/Nodes/Function2Input.cs
-
20UnityProject/Assets/UnityShaderEditor/Editor/Source/Nodes/PixelShaderNode.cs
-
8UnityProject/Assets/UnityShaderEditor/Editor/Source/Nodes/PropertyNode.cs
-
34UnityProject/Assets/UnityShaderEditor/Editor/Source/Nodes/SlotValue.cs
-
7UnityProject/Assets/UnityShaderEditor/Editor/Source/Nodes/Vector4Node.cs
-
8UnityProject/Assets/UnityShaderEditor/Editor/Source/PixelGraph.cs
-
26UnityProject/Assets/UnityShaderEditor/Editor/Source/Util/NodeUtils.cs
-
8UnityProject/Assets/UnityShaderEditor/Editor/Source/Util/ShaderGenerator.cs
-
56UnityProject/Assets/UnityShaderEditor/Editor/Source/AbstractMaterialGraph.cs
-
8UnityProject/Assets/UnityShaderEditor/Editor/Source/AbstractMaterialGraph.cs.meta
-
709UnityProject/Assets/UnityShaderEditor/Editor/Source/Nodes/AbstractMaterialNode.cs
-
8UnityProject/Assets/UnityShaderEditor/Editor/Source/Nodes/AbstractMaterialNode.cs.meta
-
139UnityProject/Assets/UnityShaderEditor/Editor/Source/Nodes/MaterialSlot.cs
-
12UnityProject/Assets/UnityShaderEditor/Editor/Source/Nodes/MaterialSlot.cs.meta
-
116UnityProject/Assets/UnityShaderEditor/Editor/Source/Nodes/SerializableNode.cs
-
61UnityProject/Assets/UnityShaderEditor/Editor/Source/Nodes/SerializableSlot.cs
-
134UnityProject/Assets/UnityShaderEditor/Editor/Source/SerializableGraph.cs
-
96UnityProject/Assets/UnityShaderEditor/Editor/Source/Util/SerializationHelper.cs
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace UnityEditor.MaterialGraph |
|||
{ |
|||
[Serializable] |
|||
public abstract class AbstractMaterialGraph : SerializableGraph |
|||
{ |
|||
private PreviewRenderUtility m_PreviewUtility; |
|||
private MaterialGraph m_Owner; |
|||
|
|||
protected AbstractMaterialGraph(MaterialGraph owner) |
|||
{ |
|||
m_Owner = owner; |
|||
} |
|||
|
|||
public MaterialGraph owner |
|||
{ |
|||
get { return m_Owner; } |
|||
} |
|||
|
|||
public IEnumerable<AbstractMaterialNode> materialNodes |
|||
{ |
|||
get { return nodes.Where(x => x is AbstractMaterialNode).Cast<AbstractMaterialNode>(); } |
|||
} |
|||
|
|||
public PreviewRenderUtility previewUtility |
|||
{ |
|||
get |
|||
{ |
|||
if (m_PreviewUtility == null) |
|||
{ |
|||
m_PreviewUtility = new PreviewRenderUtility(); |
|||
// EditorUtility.SetCameraAnimateMaterials(m_PreviewUtility.m_Camera, true);
|
|||
} |
|||
|
|||
return m_PreviewUtility; |
|||
} |
|||
} |
|||
|
|||
public bool requiresRepaint |
|||
{ |
|||
get { return nodes.Any(x => x is IRequiresTime); } |
|||
} |
|||
|
|||
|
|||
public override void ValidateGraph() |
|||
{ |
|||
base.ValidateGraph(); |
|||
|
|||
foreach (var node in materialNodes) |
|||
node.ValidateNode(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 6567c9f37c0aaa94e9083ffc63612ecf |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using UnityEditorInternal; |
|||
using UnityEngine; |
|||
using Object = UnityEngine.Object; |
|||
|
|||
namespace UnityEditor.MaterialGraph |
|||
{ |
|||
[Serializable] |
|||
public abstract class AbstractMaterialNode : SerializableNode, IGenerateProperties |
|||
{ |
|||
private static readonly Mesh[] s_Meshes = {null, null, null, null}; |
|||
|
|||
[SerializeField] |
|||
private DrawMode m_DrawMode = DrawMode.Full; |
|||
|
|||
protected PreviewMode m_GeneratedShaderMode = PreviewMode.Preview2D; |
|||
|
|||
[NonSerialized] |
|||
private bool m_HasError; |
|||
|
|||
[SerializeField] |
|||
private string m_LastShader; |
|||
|
|||
[NonSerialized] |
|||
private Material m_PreviewMaterial; |
|||
|
|||
[NonSerialized] |
|||
private Shader m_PreviewShader; |
|||
|
|||
public NeedsRepaint onNeedsRepaint; |
|||
|
|||
protected AbstractMaterialNode(AbstractMaterialGraph theOwner) : base(theOwner) |
|||
{} |
|||
|
|||
public string precision |
|||
{ |
|||
get { return "half"; } |
|||
} |
|||
|
|||
public string[] m_PrecisionNames = { "half" }; |
|||
|
|||
// Nodes that want to have a preview area can override this and return true
|
|||
public virtual bool hasPreview |
|||
{ |
|||
get { return false; } |
|||
} |
|||
|
|||
public virtual PreviewMode previewMode |
|||
{ |
|||
get { return PreviewMode.Preview2D; } |
|||
} |
|||
|
|||
public DrawMode drawMode |
|||
{ |
|||
get { return m_DrawMode; } |
|||
set { m_DrawMode = value; } |
|||
} |
|||
|
|||
protected virtual bool generateDefaultInputs |
|||
{ |
|||
get { return true; } |
|||
} |
|||
|
|||
public Material previewMaterial |
|||
{ |
|||
get |
|||
{ |
|||
if (m_PreviewMaterial == null) |
|||
{ |
|||
m_PreviewMaterial = new Material(m_PreviewShader) {hideFlags = HideFlags.HideInHierarchy}; |
|||
m_PreviewMaterial.hideFlags = HideFlags.HideInHierarchy; |
|||
} |
|||
return m_PreviewMaterial; |
|||
} |
|||
} |
|||
|
|||
public bool hasError |
|||
{ |
|||
get |
|||
{ |
|||
return m_HasError; |
|||
} |
|||
protected set |
|||
{ |
|||
if (m_HasError != value) |
|||
{ |
|||
m_HasError = value; |
|||
ExecuteRepaint(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public virtual void GeneratePropertyBlock(PropertyGenerator visitor, GenerationMode generationMode) |
|||
{} |
|||
|
|||
public virtual void GeneratePropertyUsages(ShaderGenerator visitor, GenerationMode generationMode, ConcreteSlotValueType slotValueType) |
|||
{ |
|||
if (!generateDefaultInputs) |
|||
return; |
|||
|
|||
if (!generationMode.IsPreview()) |
|||
return; |
|||
|
|||
foreach (var inputSlot in inputSlots) |
|||
{ |
|||
var edges = owner.GetEdges(inputSlot); |
|||
if (edges.Any()) |
|||
continue; |
|||
|
|||
inputSlot.GeneratePropertyUsages(visitor, generationMode, inputSlot.concreteValueType, this); |
|||
} |
|||
} |
|||
|
|||
protected virtual void OnPreviewGUI() |
|||
{ |
|||
if (!ShaderUtil.hardwareSupportsRectRenderTexture) |
|||
return; |
|||
|
|||
GUILayout.BeginHorizontal(GUILayout.MinWidth(previewWidth + 10), GUILayout.MinWidth(previewHeight + 10)); |
|||
GUILayout.FlexibleSpace(); |
|||
var rect = GUILayoutUtility.GetRect(previewWidth, previewHeight, GUILayout.ExpandWidth(false)); |
|||
var preview = RenderPreview(rect); |
|||
GUILayout.FlexibleSpace(); |
|||
GUILayout.EndHorizontal(); |
|||
GL.sRGBWrite = QualitySettings.activeColorSpace == ColorSpace.Linear; |
|||
GUI.DrawTexture(rect, preview, ScaleMode.StretchToFill, false); |
|||
GL.sRGBWrite = false; |
|||
} |
|||
|
|||
public MaterialSlot FindInputSlot(string name) |
|||
{ |
|||
var slot = m_Slots.FirstOrDefault(x => x.isInputSlot && x.name == name); |
|||
if (slot == null) |
|||
Debug.LogError("Input MaterialSlot: " + name + " could be found on node " + GetOutputVariableNameForNode()); |
|||
return slot; |
|||
} |
|||
|
|||
public MaterialSlot FindOutputSlot(string name) |
|||
{ |
|||
var slot = m_Slots.FirstOrDefault(x => x.isOutputSlot && x.name == name); |
|||
if (slot == null) |
|||
Debug.LogError("Output MaterialSlot: " + name + " could be found on node " + GetOutputVariableNameForNode()); |
|||
return slot; |
|||
} |
|||
|
|||
protected string GetSlotValue(MaterialSlot inputSlot, GenerationMode generationMode) |
|||
{ |
|||
var edges = owner.GetEdges(inputSlot).ToArray(); |
|||
|
|||
if (edges.Length > 0) |
|||
{ |
|||
var fromSocketRef = edges[0].outputSlot; |
|||
var fromNode = owner.GetNodeFromGuid(fromSocketRef.nodeGuid); |
|||
var slot = fromNode.FindOutputSlot(fromSocketRef.slotName); |
|||
|
|||
return ShaderGenerator.AdaptNodeOutput(this, slot, generationMode, inputSlot.concreteValueType); |
|||
} |
|||
|
|||
return inputSlot.GetDefaultValue(generationMode, inputSlot.concreteValueType, this); |
|||
} |
|||
|
|||
public void RemoveSlotsNameNotMatching(string[] slotNames) |
|||
{ |
|||
var invalidSlots = m_Slots.Select(x => x.name).Except(slotNames); |
|||
|
|||
foreach (var invalidSlot in invalidSlots.ToList()) |
|||
{ |
|||
Debug.LogWarningFormat("Removing Invalid MaterialSlot: {0}", invalidSlot); |
|||
RemoveSlot(invalidSlot); |
|||
} |
|||
} |
|||
|
|||
private ConcreteSlotValueType FindCommonChannelType(ConcreteSlotValueType @from, ConcreteSlotValueType to) |
|||
{ |
|||
if (ImplicitConversionExists(@from, to)) |
|||
return to; |
|||
|
|||
return ConcreteSlotValueType.Error; |
|||
} |
|||
|
|||
private static ConcreteSlotValueType ToConcreteType(SlotValueType svt) |
|||
{ |
|||
switch (svt) |
|||
{ |
|||
case SlotValueType.Vector1: |
|||
return ConcreteSlotValueType.Vector1; |
|||
case SlotValueType.Vector2: |
|||
return ConcreteSlotValueType.Vector2; |
|||
case SlotValueType.Vector3: |
|||
return ConcreteSlotValueType.Vector3; |
|||
case SlotValueType.Vector4: |
|||
return ConcreteSlotValueType.Vector4; |
|||
} |
|||
return ConcreteSlotValueType.Error; |
|||
} |
|||
|
|||
private static bool ImplicitConversionExists(ConcreteSlotValueType from, ConcreteSlotValueType to) |
|||
{ |
|||
return from >= to || from == ConcreteSlotValueType.Vector1; |
|||
} |
|||
|
|||
protected virtual ConcreteSlotValueType ConvertDynamicInputTypeToConcrete(IEnumerable<ConcreteSlotValueType> inputTypes) |
|||
{ |
|||
var concreteSlotValueTypes = inputTypes as IList<ConcreteSlotValueType> ?? inputTypes.ToList(); |
|||
if (concreteSlotValueTypes.Any(x => x == ConcreteSlotValueType.Error)) |
|||
return ConcreteSlotValueType.Error; |
|||
|
|||
var inputTypesDistinct = concreteSlotValueTypes.Distinct().ToList(); |
|||
switch (inputTypesDistinct.Count) |
|||
{ |
|||
case 0: |
|||
return ConcreteSlotValueType.Vector1; |
|||
case 1: |
|||
return inputTypesDistinct.FirstOrDefault(); |
|||
default: |
|||
// find the 'minumum' channel width excluding 1 as it can promote
|
|||
inputTypesDistinct.RemoveAll(x => x == ConcreteSlotValueType.Vector1); |
|||
var ordered = inputTypesDistinct.OrderBy(x => x); |
|||
if (ordered.Any()) |
|||
return ordered.FirstOrDefault(); |
|||
break; |
|||
} |
|||
return ConcreteSlotValueType.Error; |
|||
} |
|||
|
|||
public void ValidateNode() |
|||
{ |
|||
var isInError = false; |
|||
|
|||
// all children nodes needs to be updated first
|
|||
// so do that here
|
|||
foreach (var inputSlot in inputSlots) |
|||
{ |
|||
var edges = owner.GetEdges(inputSlot); |
|||
foreach (var edge in edges) |
|||
{ |
|||
var fromSocketRef = edge.outputSlot; |
|||
var outputNode = owner.GetNodeFromGuid(fromSocketRef.nodeGuid); |
|||
|
|||
outputNode.ValidateNode(); |
|||
if (outputNode.hasError) |
|||
isInError = true; |
|||
} |
|||
} |
|||
|
|||
var dynamicInputSlotsToCompare = new Dictionary<MaterialSlot, ConcreteSlotValueType>(); |
|||
var skippedDynamicSlots = new List<MaterialSlot>(); |
|||
|
|||
// iterate the input slots
|
|||
foreach (var inputSlot in inputSlots) |
|||
{ |
|||
var inputType = inputSlot.valueType; |
|||
// if there is a connection
|
|||
var edges = owner.GetEdges(inputSlot).ToList(); |
|||
if (!edges.Any()) |
|||
{ |
|||
if (inputType != SlotValueType.Dynamic) |
|||
inputSlot.concreteValueType = ToConcreteType(inputType); |
|||
else |
|||
skippedDynamicSlots.Add(inputSlot); |
|||
continue; |
|||
} |
|||
|
|||
// get the output details
|
|||
var outputSlotRef = edges[0].outputSlot; |
|||
var outputNode = owner.GetNodeFromGuid(outputSlotRef.nodeGuid); |
|||
var outputSlot = outputNode.FindOutputSlot(outputSlotRef.slotName); |
|||
var outputConcreteType = outputSlot.concreteValueType; |
|||
|
|||
// if we have a standard connection... just check the types work!
|
|||
if (inputType != SlotValueType.Dynamic) |
|||
{ |
|||
var inputConcreteType = ToConcreteType(inputType); |
|||
inputSlot.concreteValueType = FindCommonChannelType(outputConcreteType, inputConcreteType); |
|||
continue; |
|||
} |
|||
|
|||
// dynamic input... depends on output from other node.
|
|||
// we need to compare ALL dynamic inputs to make sure they
|
|||
// are compatable.
|
|||
dynamicInputSlotsToCompare.Add(inputSlot, outputConcreteType); |
|||
} |
|||
|
|||
// we can now figure out the dynamic slotType
|
|||
// from here set all the
|
|||
var dynamicType = ConvertDynamicInputTypeToConcrete(dynamicInputSlotsToCompare.Values); |
|||
foreach (var dynamicKvP in dynamicInputSlotsToCompare) |
|||
dynamicKvP.Key.concreteValueType= dynamicType; |
|||
foreach (var skippedSlot in skippedDynamicSlots) |
|||
skippedSlot.concreteValueType = dynamicType; |
|||
|
|||
var inputError = inputSlots.Any(x => x.concreteValueType == ConcreteSlotValueType.Error); |
|||
|
|||
// configure the output slots now
|
|||
// their slotType will either be the default output slotType
|
|||
// or the above dynanic slotType for dynamic nodes
|
|||
// or error if there is an input error
|
|||
foreach (var outputSlot in outputSlots) |
|||
{ |
|||
if (inputError) |
|||
{ |
|||
outputSlot.concreteValueType = ConcreteSlotValueType.Error; |
|||
continue; |
|||
} |
|||
|
|||
if (outputSlot.valueType == SlotValueType.Dynamic) |
|||
{ |
|||
outputSlot.concreteValueType = dynamicType; |
|||
continue; |
|||
} |
|||
outputSlot.concreteValueType = ToConcreteType(outputSlot.valueType); |
|||
} |
|||
|
|||
isInError |= inputError; |
|||
isInError |= outputSlots.Any(x => x.concreteValueType == ConcreteSlotValueType.Error); |
|||
isInError |= CalculateNodeHasError(); |
|||
hasError = isInError; |
|||
|
|||
if (!hasError) |
|||
{ |
|||
previewShaderNeedsUpdate = true; |
|||
} |
|||
} |
|||
|
|||
public bool previewShaderNeedsUpdate { get; set; } |
|||
|
|||
//True if error
|
|||
protected virtual bool CalculateNodeHasError() |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
public static string ConvertConcreteSlotValueTypeToString(ConcreteSlotValueType slotValue) |
|||
{ |
|||
switch (slotValue) |
|||
{ |
|||
case ConcreteSlotValueType.Vector1: |
|||
return string.Empty; |
|||
case ConcreteSlotValueType.Vector2: |
|||
return "2"; |
|||
case ConcreteSlotValueType.Vector3: |
|||
return "3"; |
|||
case ConcreteSlotValueType.Vector4: |
|||
return "4"; |
|||
default: |
|||
return "Error"; |
|||
} |
|||
} |
|||
|
|||
public virtual bool OnGUI() |
|||
{ |
|||
GUILayout.Label("MaterialSlot Defaults", EditorStyles.boldLabel); |
|||
var modified = false; |
|||
foreach (var slot in inputSlots) |
|||
{ |
|||
if (!owner.GetEdges(slot).Any()) |
|||
modified |= DoSlotUI(this, slot); |
|||
} |
|||
|
|||
return modified; |
|||
} |
|||
|
|||
public static bool DoSlotUI(AbstractMaterialNode node, MaterialSlot slot) |
|||
{ |
|||
GUILayout.BeginHorizontal( /*EditorStyles.inspectorBig*/); |
|||
GUILayout.BeginVertical(); |
|||
GUILayout.BeginHorizontal(); |
|||
GUILayout.Label("MaterialSlot " + slot.name, EditorStyles.largeLabel); |
|||
GUILayout.FlexibleSpace(); |
|||
GUILayout.EndHorizontal(); |
|||
GUILayout.EndVertical(); |
|||
GUILayout.EndHorizontal(); |
|||
|
|||
return slot.OnGUI(); |
|||
} |
|||
|
|||
public virtual bool DrawSlotDefaultInput(Rect rect, MaterialSlot inputSlot) |
|||
{ |
|||
var inputSlotType = inputSlot.concreteValueType; |
|||
return inputSlot.OnGUI(rect, inputSlotType); |
|||
} |
|||
|
|||
public virtual IEnumerable<MaterialSlot> GetDrawableInputProxies() |
|||
{ |
|||
return inputSlots.Where(x => !owner.GetEdges(x).Any()); |
|||
} |
|||
|
|||
public void ExecuteRepaint() |
|||
{ |
|||
if (onNeedsRepaint != null) |
|||
onNeedsRepaint(); |
|||
} |
|||
|
|||
|
|||
// CollectDependentNodes looks at the current node and calculates
|
|||
// which nodes further up the tree (parents) would be effected if this node was changed
|
|||
// it also includes itself in this list
|
|||
public IEnumerable<AbstractMaterialNode> CollectDependentNodes() |
|||
{ |
|||
var nodeList = new List<AbstractMaterialNode>(); |
|||
NodeUtils.CollectDependentNodes(nodeList, this); |
|||
return nodeList; |
|||
} |
|||
|
|||
// CollectDependentNodes looks at the current node and calculates
|
|||
// which child nodes it depends on for it's calculation.
|
|||
// Results are returned depth first so by processing each node in
|
|||
// order you can generate a valid code block.
|
|||
public List<AbstractMaterialNode> CollectChildNodesByExecutionOrder(List<AbstractMaterialNode> nodeList, MaterialSlot slotToUse = null, bool includeSelf = true) |
|||
{ |
|||
if (slotToUse != null && !m_Slots.Contains(slotToUse)) |
|||
{ |
|||
Debug.LogError("Attempting to collect nodes by execution order with an invalid MaterialSlot on: " + name); |
|||
return nodeList; |
|||
} |
|||
|
|||
NodeUtils.CollectChildNodesByExecutionOrder(nodeList, this, slotToUse); |
|||
|
|||
if (!includeSelf) |
|||
nodeList.Remove(this); |
|||
|
|||
return nodeList; |
|||
} |
|||
|
|||
protected virtual bool UpdatePreviewShader() |
|||
{ |
|||
if (hasError) |
|||
return false; |
|||
|
|||
var resultShader = ShaderGenerator.GeneratePreviewShader(this, out m_GeneratedShaderMode); |
|||
return InternalUpdatePreviewShader(resultShader); |
|||
} |
|||
|
|||
private static bool ShaderHasError(Shader shader) |
|||
{ |
|||
var hasErrorsCall = typeof(ShaderUtil).GetMethod("GetShaderErrorCount", BindingFlags.Static | BindingFlags.NonPublic); |
|||
var result = hasErrorsCall.Invoke(null, new object[] {shader}); |
|||
return (int) result != 0; |
|||
} |
|||
|
|||
protected bool InternalUpdatePreviewShader(string resultShader) |
|||
{ |
|||
MaterialWindow.DebugMaterialGraph("RecreateShaderAndMaterial : " + name + "_" + guid.ToString().Replace("-","_") + "\n" + resultShader); |
|||
|
|||
// workaround for some internal shader compiler weirdness
|
|||
// if we are in error we sometimes to not properly clean
|
|||
// clean out the error flags and will stay in error, even
|
|||
// if we are now valid
|
|||
if (m_PreviewShader && ShaderHasError(m_PreviewShader)) |
|||
{ |
|||
Object.DestroyImmediate(m_PreviewShader, true); |
|||
Object.DestroyImmediate(m_PreviewMaterial, true); |
|||
m_PreviewShader = null; |
|||
m_PreviewMaterial = null; |
|||
} |
|||
|
|||
if (m_PreviewShader == null) |
|||
{ |
|||
m_PreviewShader = ShaderUtil.CreateShaderAsset(resultShader); |
|||
m_PreviewShader.hideFlags = HideFlags.HideInHierarchy; |
|||
m_LastShader = resultShader; |
|||
} |
|||
else |
|||
{ |
|||
if (string.CompareOrdinal(resultShader, m_LastShader) != 0) |
|||
{ |
|||
ShaderUtil.UpdateShaderAsset(m_PreviewShader, resultShader); |
|||
m_LastShader = resultShader; |
|||
} |
|||
} |
|||
|
|||
return !ShaderHasError(m_PreviewShader); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// RenderPreview gets called in OnPreviewGUI. Nodes can override
|
|||
/// RenderPreview and do their own rendering to the render texture
|
|||
/// </summary>
|
|||
public Texture RenderPreview(Rect targetSize) |
|||
{ |
|||
if (hasError) |
|||
return null; |
|||
|
|||
if (previewShaderNeedsUpdate) |
|||
{ |
|||
UpdatePreviewShader(); |
|||
previewShaderNeedsUpdate = false; |
|||
} |
|||
|
|||
UpdatePreviewProperties(); |
|||
|
|||
if (s_Meshes[0] == null) |
|||
{ |
|||
var handleGo = (GameObject) EditorGUIUtility.LoadRequired("Previews/PreviewMaterials.fbx"); |
|||
// @TODO: temp workaround to make it not render in the scene
|
|||
handleGo.SetActive(false); |
|||
foreach (Transform t in handleGo.transform) |
|||
{ |
|||
switch (t.name) |
|||
{ |
|||
case "sphere": |
|||
s_Meshes[0] = ((MeshFilter) t.GetComponent("MeshFilter")).sharedMesh; |
|||
break; |
|||
case "cube": |
|||
s_Meshes[1] = ((MeshFilter) t.GetComponent("MeshFilter")).sharedMesh; |
|||
break; |
|||
case "cylinder": |
|||
s_Meshes[2] = ((MeshFilter) t.GetComponent("MeshFilter")).sharedMesh; |
|||
break; |
|||
case "torus": |
|||
s_Meshes[3] = ((MeshFilter) t.GetComponent("MeshFilter")).sharedMesh; |
|||
break; |
|||
default: |
|||
Debug.Log("Something is wrong, weird object found: " + t.name); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
var previewUtil = owner.previewUtility; |
|||
previewUtil.BeginPreview(targetSize, GUIStyle.none); |
|||
|
|||
if (m_GeneratedShaderMode == PreviewMode.Preview3D) |
|||
{ |
|||
previewUtil.m_Camera.transform.position = -Vector3.forward * 5; |
|||
previewUtil.m_Camera.transform.rotation = Quaternion.identity; |
|||
EditorUtility.SetCameraAnimateMaterialsTime(previewUtil.m_Camera, Time.realtimeSinceStartup); |
|||
var amb = new Color(.2f, .2f, .2f, 0); |
|||
previewUtil.m_Light[0].intensity = 1.0f; |
|||
previewUtil.m_Light[0].transform.rotation = Quaternion.Euler(50f, 50f, 0); |
|||
previewUtil.m_Light[1].intensity = 1.0f; |
|||
|
|||
InternalEditorUtility.SetCustomLighting(previewUtil.m_Light, amb); |
|||
previewUtil.DrawMesh(s_Meshes[0], Vector3.zero, Quaternion.Euler(-20, 0, 0) * Quaternion.Euler(0, 0, 0), previewMaterial, 0); |
|||
var oldFog = RenderSettings.fog; |
|||
Unsupported.SetRenderSettingsUseFogNoDirty(false); |
|||
previewUtil.m_Camera.Render(); |
|||
Unsupported.SetRenderSettingsUseFogNoDirty(oldFog); |
|||
InternalEditorUtility.RemoveCustomLighting(); |
|||
} |
|||
else |
|||
{ |
|||
EditorUtility.UpdateGlobalShaderProperties(Time.realtimeSinceStartup); |
|||
Graphics.Blit(null, previewMaterial); |
|||
} |
|||
return previewUtil.EndPreview(); |
|||
} |
|||
|
|||
private static void SetPreviewMaterialProperty(PreviewProperty previewProperty, Material mat) |
|||
{ |
|||
switch (previewProperty.m_PropType) |
|||
{ |
|||
case PropertyType.Texture2D: |
|||
mat.SetTexture(previewProperty.m_Name, previewProperty.m_Texture); |
|||
break; |
|||
case PropertyType.Color: |
|||
mat.SetColor(previewProperty.m_Name, previewProperty.m_Color); |
|||
break; |
|||
case PropertyType.Vector2: |
|||
mat.SetVector(previewProperty.m_Name, previewProperty.m_Vector4); |
|||
break; |
|||
case PropertyType.Vector3: |
|||
mat.SetVector(previewProperty.m_Name, previewProperty.m_Vector4); |
|||
break; |
|||
case PropertyType.Vector4: |
|||
mat.SetVector(previewProperty.m_Name, previewProperty.m_Vector4); |
|||
break; |
|||
case PropertyType.Float: |
|||
mat.SetFloat(previewProperty.m_Name, previewProperty.m_Float); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
protected virtual void CollectPreviewMaterialProperties(List<PreviewProperty> properties) |
|||
{ |
|||
var validSlots = inputSlots.ToArray(); |
|||
|
|||
for (var index = 0; index < validSlots.Length; index++) |
|||
{ |
|||
var s = validSlots[index]; |
|||
var edges = owner.GetEdges(s); |
|||
if (edges.Any()) |
|||
continue; |
|||
|
|||
var pp = new PreviewProperty |
|||
{ |
|||
m_Name = s.GetInputName(this), |
|||
m_PropType = PropertyType.Vector4, |
|||
m_Vector4 = s.currentValue |
|||
}; |
|||
properties.Add(pp); |
|||
} |
|||
} |
|||
|
|||
public static void UpdateMaterialProperties(AbstractMaterialNode target, Material material) |
|||
{ |
|||
var childNodes = ListPool<AbstractMaterialNode>.Get(); |
|||
target.CollectChildNodesByExecutionOrder(childNodes); |
|||
|
|||
var pList = ListPool<PreviewProperty>.Get(); |
|||
for (var index = 0; index < childNodes.Count; index++) |
|||
{ |
|||
var node = childNodes[index]; |
|||
node.CollectPreviewMaterialProperties(pList); |
|||
} |
|||
|
|||
foreach (var prop in pList) |
|||
SetPreviewMaterialProperty(prop, material); |
|||
|
|||
ListPool<AbstractMaterialNode>.Release(childNodes); |
|||
ListPool<PreviewProperty>.Release(pList); |
|||
} |
|||
|
|||
public void UpdatePreviewProperties() |
|||
{ |
|||
if (!hasPreview) |
|||
return; |
|||
|
|||
UpdateMaterialProperties(this, previewMaterial); |
|||
} |
|||
|
|||
|
|||
public virtual string GetOutputVariableNameForSlot(MaterialSlot s, GenerationMode generationMode) |
|||
{ |
|||
if (s.isInputSlot) Debug.LogError("Attempting to use input MaterialSlot (" + s + ") for output!"); |
|||
if (!m_Slots.Contains(s)) Debug.LogError("Attempting to use MaterialSlot (" + s + ") for output on a node that does not have this MaterialSlot!"); |
|||
|
|||
return GetOutputVariableNameForNode() + "_" + s.name; |
|||
} |
|||
|
|||
public virtual string GetOutputVariableNameForNode() |
|||
{ |
|||
return name + "_" + guid.ToString().Replace("-", "_"); |
|||
} |
|||
|
|||
public virtual Vector4 GetNewSlotDefaultValue(SlotValueType type) |
|||
{ |
|||
return Vector4.one; |
|||
} |
|||
|
|||
public void AddSlot(MaterialSlot slot) |
|||
{ |
|||
if (slot == null) |
|||
return; |
|||
|
|||
// new MaterialSlot, just add it, we cool
|
|||
if (!m_Slots.Contains(slot)) |
|||
{ |
|||
m_Slots.Add(slot); |
|||
return; |
|||
} |
|||
|
|||
// old MaterialSlot found
|
|||
// update the default value, and the slotType!
|
|||
var foundSlots = m_Slots.FindAll(x => x.name == slot.name); |
|||
|
|||
// if we are in a bad state (> 1 MaterialSlot with same name, just reset).
|
|||
if (foundSlots.Count > 1) |
|||
{ |
|||
Debug.LogWarningFormat("Node {0} has more than one MaterialSlot with the same name, removing."); |
|||
foundSlots.ForEach(x => m_Slots.Remove(x)); |
|||
m_Slots.Add(slot); |
|||
return; |
|||
} |
|||
|
|||
var foundSlot = foundSlots[0]; |
|||
|
|||
// if the defualt and current are the same, change the current
|
|||
// to the new default.
|
|||
if (foundSlot.defaultValue == foundSlot.currentValue) |
|||
foundSlot.currentValue = slot.defaultValue; |
|||
|
|||
foundSlot.defaultValue = slot.defaultValue; |
|||
foundSlot.valueType = slot.valueType; |
|||
} |
|||
|
|||
public void RemoveSlot(string name) |
|||
{ |
|||
m_Slots.RemoveAll(x => x.name == name); |
|||
} |
|||
|
|||
public string GenerateSlotName(SerializableSlot.SlotType type) |
|||
{ |
|||
var slotsToCheck = type == SerializableSlot.SlotType.Input ? inputSlots.ToArray() : outputSlots.ToArray(); |
|||
var format = type == SerializableSlot.SlotType.Input ? "I{0:00}" : "O{0:00}"; |
|||
var index = slotsToCheck.Count(); |
|||
var slotName = string.Format(format, index); |
|||
if (slotsToCheck.All(x => x.name != slotName)) |
|||
return slotName; |
|||
index = 0; |
|||
do |
|||
{ |
|||
slotName = string.Format(format, index++); |
|||
} while (slotsToCheck.Any(x => x.name == slotName)); |
|||
|
|||
return slotName; |
|||
} |
|||
} |
|||
|
|||
public enum GUIModificationType |
|||
{ |
|||
None, |
|||
Repaint, |
|||
ModelChanged |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a4da4d7c02dcbdd4f9ef2d3b1106879a |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|
|||
using System; |
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.MaterialGraph |
|||
{ |
|||
[Serializable] |
|||
public class MaterialSlot : SerializableSlot |
|||
{ |
|||
[SerializeField] |
|||
private SlotValueType m_ValueType; |
|||
|
|||
[SerializeField] |
|||
private Vector4 m_DefaultValue; |
|||
|
|||
[SerializeField] |
|||
private Vector4 m_CurrentValue; |
|||
|
|||
[SerializeField] |
|||
private ConcreteSlotValueType m_ConcreteValueType; |
|||
|
|||
public MaterialSlot(AbstractMaterialNode owner, string name, string displayName, SlotType slotType, SlotValueType valueType, Vector4 defaultValue) |
|||
: base(owner, name, displayName, slotType) |
|||
{ |
|||
m_ValueType = valueType; |
|||
m_DefaultValue = defaultValue; |
|||
m_CurrentValue = defaultValue; |
|||
} |
|||
|
|||
internal MaterialSlot (AbstractMaterialNode owner) : base (owner) |
|||
{ } |
|||
|
|||
public Vector4 defaultValue |
|||
{ |
|||
get { return m_DefaultValue; } |
|||
set { m_DefaultValue = value; } |
|||
} |
|||
|
|||
public SlotValueType valueType |
|||
{ |
|||
get { return m_ValueType; } |
|||
set { m_ValueType = value; } |
|||
} |
|||
|
|||
public Vector4 currentValue |
|||
{ |
|||
get { return m_CurrentValue; } |
|||
set { m_CurrentValue = value; } |
|||
} |
|||
|
|||
public ConcreteSlotValueType concreteValueType |
|||
{ |
|||
get { return m_ConcreteValueType; } |
|||
set { m_ConcreteValueType = value; } |
|||
} |
|||
|
|||
public string GetInputName (AbstractMaterialNode node) |
|||
{ |
|||
return string.Format( "{0}_{1}", node.name, name); |
|||
} |
|||
|
|||
public void GeneratePropertyUsages(ShaderGenerator visitor, GenerationMode generationMode, ConcreteSlotValueType slotValueType, AbstractMaterialNode owner) |
|||
{ |
|||
if (!generationMode.IsPreview()) |
|||
return; |
|||
|
|||
visitor.AddShaderChunk("float" + AbstractMaterialNode.ConvertConcreteSlotValueTypeToString(slotValueType) + " " + GetInputName(owner) + ";", true); |
|||
} |
|||
|
|||
public string GetDefaultValue(GenerationMode generationMode, ConcreteSlotValueType slotValueType, AbstractMaterialNode owner) |
|||
{ |
|||
if (generationMode.IsPreview()) |
|||
return GetInputName(owner); |
|||
|
|||
switch (slotValueType) |
|||
{ |
|||
case ConcreteSlotValueType.Vector1: |
|||
return m_CurrentValue.x.ToString(); |
|||
case ConcreteSlotValueType.Vector2: |
|||
return "half2 (" + m_CurrentValue.x + "," + m_CurrentValue.y + ")"; |
|||
case ConcreteSlotValueType.Vector3: |
|||
return "half3 (" + m_CurrentValue.x + "," + m_CurrentValue.y + "," + m_CurrentValue.z + ")"; |
|||
case ConcreteSlotValueType.Vector4: |
|||
return "half4 (" + m_CurrentValue.x + "," + m_CurrentValue.y + "," + m_CurrentValue.z + "," + m_CurrentValue.w + ")"; |
|||
default: |
|||
return "error"; |
|||
} |
|||
} |
|||
|
|||
public bool OnGUI() |
|||
{ |
|||
EditorGUI.BeginChangeCheck(); |
|||
m_CurrentValue = EditorGUILayout.Vector4Field("Value", m_CurrentValue); |
|||
return EditorGUI.EndChangeCheck(); |
|||
} |
|||
|
|||
public bool OnGUI(Rect rect, ConcreteSlotValueType inputSlotType) |
|||
{ |
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
var rectXmax = rect.xMax; |
|||
switch (inputSlotType) |
|||
{ |
|||
case ConcreteSlotValueType.Vector1: |
|||
rect.x = rectXmax - 50; |
|||
rect.width = 50; |
|||
EditorGUIUtility.labelWidth = 15; |
|||
EditorGUI.DrawRect(rect, new Color(0.0f, 0.0f, 0.0f, 0.7f)); |
|||
m_CurrentValue.x = EditorGUI.FloatField(rect, "X", m_CurrentValue.x); |
|||
break; |
|||
case ConcreteSlotValueType.Vector2: |
|||
rect.x = rectXmax - 90; |
|||
rect.width = 90; |
|||
EditorGUI.DrawRect(rect, new Color(0.0f, 0.0f, 0.0f, 0.7f)); |
|||
var result2 = new Vector4(m_CurrentValue.x, m_CurrentValue.y); |
|||
result2 = EditorGUI.Vector2Field(rect, GUIContent.none, result2); |
|||
m_CurrentValue.x = result2.x; |
|||
m_CurrentValue.y = result2.y; |
|||
break; |
|||
case ConcreteSlotValueType.Vector3: |
|||
rect.x = rectXmax - 140; |
|||
rect.width = 140; |
|||
EditorGUI.DrawRect(rect, new Color(0.0f, 0.0f, 0.0f, 0.7f)); |
|||
var result3 = new Vector3(m_CurrentValue.x, m_CurrentValue.y, m_CurrentValue.z); |
|||
result3 = EditorGUI.Vector3Field(rect, GUIContent.none, result3); |
|||
m_CurrentValue.x = result3.x; |
|||
m_CurrentValue.y = result3.y; |
|||
m_CurrentValue.z = result3.z; |
|||
break; |
|||
default: |
|||
rect.x = rectXmax - 190; |
|||
rect.width = 190; |
|||
EditorGUI.DrawRect(rect, new Color(0.0f, 0.0f, 0.0f, 0.7f)); |
|||
m_CurrentValue = EditorGUI.Vector4Field(rect, GUIContent.none, m_CurrentValue); |
|||
break; |
|||
} |
|||
return EditorGUI.EndChangeCheck(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 7d998d7fbd75b97459f63fb4931d14b5 |
|||
timeCreated: 1463560120 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.MaterialGraph |
|||
{ |
|||
[Serializable] |
|||
public class SerializableNode : ISerializationCallbackReceiver |
|||
{ |
|||
public delegate void NeedsRepaint(); |
|||
|
|||
private const int kPreviewWidth = 64; |
|||
private const int kPreviewHeight = 64; |
|||
|
|||
[NonSerialized] |
|||
private Guid m_Guid; |
|||
|
|||
[SerializeField] |
|||
private string m_GuidSerialized; |
|||
|
|||
[SerializeField] |
|||
private string m_Name; |
|||
|
|||
[SerializeField] |
|||
private Rect m_Position; |
|||
|
|||
[NonSerialized] |
|||
private List<SerializableSlot> m_Slots = new List<SerializableSlot>(); |
|||
|
|||
[SerializeField] |
|||
List<SerializationHelper.JSONSerializedElement> m_SerializableSlots = new List<SerializationHelper.JSONSerializedElement>(); |
|||
|
|||
public SerializableGraph owner { get; set; } |
|||
|
|||
public Guid guid |
|||
{ |
|||
get { return m_Guid; } |
|||
} |
|||
|
|||
public string name |
|||
{ |
|||
get { return m_Name; } |
|||
set { m_Name = value; } |
|||
} |
|||
|
|||
public virtual bool canDeleteNode |
|||
{ |
|||
get { return true; } |
|||
} |
|||
|
|||
protected virtual int previewWidth |
|||
{ |
|||
get { return kPreviewWidth; } |
|||
} |
|||
|
|||
protected virtual int previewHeight |
|||
{ |
|||
get { return kPreviewHeight; } |
|||
} |
|||
|
|||
public Rect position |
|||
{ |
|||
get { return m_Position; } |
|||
set { m_Position = value; } |
|||
} |
|||
|
|||
public IEnumerable<SerializableSlot> inputSlots |
|||
{ |
|||
get { return m_Slots.Where(x => x.isInputSlot); } |
|||
} |
|||
|
|||
public IEnumerable<SerializableSlot> outputSlots |
|||
{ |
|||
get { return m_Slots.Where(x => x.isOutputSlot); } |
|||
} |
|||
|
|||
public IEnumerable<SerializableSlot> slots |
|||
{ |
|||
get { return m_Slots; } |
|||
} |
|||
|
|||
public SerializableNode(SerializableGraph theOwner) |
|||
{ |
|||
owner = theOwner; |
|||
m_Guid = Guid.NewGuid(); |
|||
} |
|||
|
|||
public virtual float GetNodeUIHeight(float width) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
public virtual GUIModificationType NodeUI(Rect drawArea) |
|||
{ |
|||
return GUIModificationType.None; |
|||
} |
|||
|
|||
public virtual void OnBeforeSerialize() |
|||
{ |
|||
m_GuidSerialized = m_Guid.ToString(); |
|||
m_SerializableSlots = SerializationHelper.Serialize(m_Slots); |
|||
} |
|||
|
|||
public virtual void OnAfterDeserialize() |
|||
{ |
|||
if (!string.IsNullOrEmpty(m_GuidSerialized)) |
|||
m_Guid = new Guid(m_GuidSerialized); |
|||
else |
|||
m_Guid = Guid.NewGuid(); |
|||
|
|||
m_Slots = SerializationHelper.Deserialize<SerializableSlot>(m_SerializableSlots, new object[] { this }); |
|||
m_SerializableSlots = null; |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.MaterialGraph |
|||
{ |
|||
[Serializable] |
|||
public class SerializableSlot |
|||
{ |
|||
private const string kNotInit = "Not Initilaized"; |
|||
public enum SlotType |
|||
{ |
|||
Input, |
|||
Output |
|||
} |
|||
|
|||
[SerializeField] |
|||
private string m_Name = kNotInit; |
|||
|
|||
[SerializeField] |
|||
private string m_DisplayName = kNotInit; |
|||
|
|||
[SerializeField] |
|||
private SlotType m_SlotType; |
|||
|
|||
public SerializableNode owner { get; set; } |
|||
|
|||
public string name |
|||
{ |
|||
get { return m_Name; } |
|||
} |
|||
|
|||
public string displayName |
|||
{ |
|||
get { return m_DisplayName; } |
|||
} |
|||
|
|||
public bool isInputSlot |
|||
{ |
|||
get { return m_SlotType == SlotType.Input; } |
|||
} |
|||
|
|||
public bool isOutputSlot |
|||
{ |
|||
get { return m_SlotType == SlotType.Output; } |
|||
} |
|||
|
|||
public SerializableSlot(SerializableNode theOwner, string name, string displayName, SlotType slotType) |
|||
{ |
|||
owner = theOwner; |
|||
m_Name = name; |
|||
m_DisplayName = displayName; |
|||
m_SlotType = slotType; |
|||
} |
|||
|
|||
// used via reflection / serialization after deserialize
|
|||
internal SerializableSlot(SerializableNode theOwner) |
|||
{ |
|||
owner = theOwner; |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.MaterialGraph |
|||
{ |
|||
[Serializable] |
|||
public class SerializableGraph : ISerializationCallbackReceiver |
|||
{ |
|||
[SerializeField] |
|||
private List<Edge> m_Edges = new List<Edge>(); |
|||
|
|||
[NonSerialized] |
|||
private List<SerializableNode> m_Nodes = new List<SerializableNode>(); |
|||
|
|||
[SerializeField] |
|||
List<SerializationHelper.JSONSerializedElement> m_SerializableNodes = new List<SerializationHelper.JSONSerializedElement>(); |
|||
|
|||
public IEnumerable<SerializableNode> nodes |
|||
{ |
|||
get { return m_Nodes; } |
|||
} |
|||
|
|||
public IEnumerable<Edge> edges |
|||
{ |
|||
get { return m_Edges; } |
|||
} |
|||
|
|||
public virtual void AddNode(SerializableNode node) |
|||
{ |
|||
m_Nodes.Add(node); |
|||
ValidateGraph(); |
|||
} |
|||
|
|||
public virtual void RemoveNode(SerializableNode node) |
|||
{ |
|||
if (!node.canDeleteNode) |
|||
return; |
|||
|
|||
m_Nodes.Remove(node); |
|||
ValidateGraph(); |
|||
} |
|||
|
|||
public virtual Edge Connect(SerializableSlot fromSlot, SerializableSlot toSlot) |
|||
{ |
|||
SerializableSlot outputSlot = null; |
|||
SerializableSlot inputSlot = null; |
|||
|
|||
// output must connect to input
|
|||
if (fromSlot.isOutputSlot) |
|||
outputSlot = fromSlot; |
|||
else if (fromSlot.isInputSlot) |
|||
inputSlot = fromSlot; |
|||
|
|||
if (toSlot.isOutputSlot) |
|||
outputSlot = toSlot; |
|||
else if (toSlot.isInputSlot) |
|||
inputSlot = toSlot; |
|||
|
|||
if (inputSlot == null || outputSlot == null) |
|||
return null; |
|||
|
|||
var edges = GetEdges(inputSlot).ToList(); |
|||
// remove any inputs that exits before adding
|
|||
foreach (var edge in edges) |
|||
{ |
|||
Debug.Log("Removing existing edge:" + edge); |
|||
// call base here as we DO NOT want to
|
|||
// do expensive shader regeneration
|
|||
RemoveEdge(edge); |
|||
} |
|||
|
|||
var newEdge = new Edge(new SlotReference(outputSlot.owner.guid, outputSlot.name), new SlotReference(inputSlot.owner.guid, inputSlot.name)); |
|||
m_Edges.Add(newEdge); |
|||
|
|||
Debug.Log("Connected edge: " + newEdge); |
|||
return newEdge; |
|||
} |
|||
|
|||
public virtual void RemoveEdge(Edge e) |
|||
{ |
|||
m_Edges.Remove(e); |
|||
ValidateGraph(); |
|||
} |
|||
|
|||
private void RemoveEdgeNoValidate(Edge e) |
|||
{ |
|||
m_Edges.Remove(e); |
|||
} |
|||
|
|||
public SerializableNode GetNodeFromGuid(Guid guid) |
|||
{ |
|||
return m_Nodes.FirstOrDefault(x => x.guid == guid); |
|||
} |
|||
|
|||
public IEnumerable<Edge> GetEdges(SerializableSlot s) |
|||
{ |
|||
return m_Edges.Where(x => |
|||
(x.outputSlot.nodeGuid == s.owner.guid && x.outputSlot.slotName == s.name) |
|||
|| x.inputSlot.nodeGuid == s.owner.guid && x.inputSlot.slotName == s.name); |
|||
} |
|||
|
|||
public virtual void OnBeforeSerialize() |
|||
{ |
|||
m_SerializableNodes = SerializationHelper.Serialize(m_Nodes); |
|||
} |
|||
|
|||
public virtual void OnAfterDeserialize() |
|||
{ |
|||
m_Nodes = SerializationHelper.Deserialize<SerializableNode>(m_SerializableNodes, new object[] {this}); |
|||
m_SerializableNodes = null; |
|||
} |
|||
|
|||
public virtual void ValidateGraph() |
|||
{ |
|||
//First validate edges, remove any
|
|||
//orphans. This can happen if a user
|
|||
//manually modifies serialized data
|
|||
//of if they delete a node in the inspector
|
|||
//debug view.
|
|||
var allNodeGUIDs = nodes.Select(x => x.guid).ToList(); |
|||
|
|||
foreach (var edge in edges.ToArray()) |
|||
{ |
|||
if (allNodeGUIDs.Contains(edge.inputSlot.nodeGuid) && allNodeGUIDs.Contains(edge.outputSlot.nodeGuid)) |
|||
continue; |
|||
|
|||
//orphaned edge
|
|||
RemoveEdgeNoValidate(edge); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.MaterialGraph |
|||
{ |
|||
public static class SerializationHelper |
|||
{ |
|||
[Serializable] |
|||
public struct JSONSerializedElement |
|||
{ |
|||
[SerializeField] |
|||
public string typeName; |
|||
|
|||
[SerializeField] |
|||
public string JSONnodeData; |
|||
} |
|||
|
|||
private static string GetTypeSerializableAsString(Type type) |
|||
{ |
|||
if (type == null) |
|||
return string.Empty; |
|||
|
|||
return string.Format("{0}, {1}", type.FullName, type.Assembly.GetName().Name); |
|||
} |
|||
|
|||
private static Type GetTypeFromSerializedString(string type) |
|||
{ |
|||
if (string.IsNullOrEmpty(type)) |
|||
return null; |
|||
|
|||
return Type.GetType(type); |
|||
} |
|||
|
|||
public static List<JSONSerializedElement> Serialize<T>(List<T> list) |
|||
{ |
|||
var result = new List<JSONSerializedElement>(list.Count); |
|||
|
|||
foreach (var element in list) |
|||
{ |
|||
if (element == null) |
|||
continue; |
|||
|
|||
var typeName = GetTypeSerializableAsString(element.GetType()); |
|||
var data = JsonUtility.ToJson(element, true); |
|||
|
|||
if (string.IsNullOrEmpty(typeName) || string.IsNullOrEmpty(data)) |
|||
continue; |
|||
|
|||
result.Add(new JSONSerializedElement() |
|||
{ |
|||
typeName = typeName, |
|||
JSONnodeData = data |
|||
}); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
public static List<T> Deserialize<T>(List<JSONSerializedElement> list, object[] constructorArgs) |
|||
{ |
|||
var result = new List<T>(); |
|||
|
|||
Type[] types = constructorArgs.Select(x => x.GetType()).ToArray(); |
|||
|
|||
foreach (var element in list) |
|||
{ |
|||
if (string.IsNullOrEmpty(element.typeName) || string.IsNullOrEmpty(element.JSONnodeData)) |
|||
continue; |
|||
|
|||
var type = GetTypeFromSerializedString(element.typeName); |
|||
if (type == null) |
|||
{ |
|||
Debug.LogWarningFormat("Could not find node of type {0} in loaded assemblies", element.typeName); |
|||
continue; |
|||
} |
|||
|
|||
T instance; |
|||
try |
|||
{ |
|||
var constructorInfo = type.GetConstructor(types); |
|||
instance = (T) constructorInfo.Invoke(constructorArgs); |
|||
} |
|||
catch |
|||
{ |
|||
Debug.LogWarningFormat("Could not construct instance of: {0} as there is no single argument constuctor that takes a AbstractMaterialGraph", type); |
|||
continue; |
|||
} |
|||
JsonUtility.FromJsonOverwrite(element.JSONnodeData, instance); |
|||
result.Add(instance); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
} |
|||
} |
撰写
预览
正在加载...
取消
保存
Reference in new issue