浏览代码

We now have a Custom Code Node

/main
bfogerty 8 年前
当前提交
2f5a98bb
共有 5 个文件被更改,包括 405 次插入0 次删除
  1. 1
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Presenters/MaterialGraphPresenter.cs
  2. 99
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Presenters/CustomCodeNodePresenter.cs
  3. 12
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Presenters/CustomCodeNodePresenter.cs.meta
  4. 281
      MaterialGraphProject/Assets/UnityShaderEditor/Runtime/Nodes/Logic/CustomCodeNode.cs
  5. 12
      MaterialGraphProject/Assets/UnityShaderEditor/Runtime/Nodes/Logic/CustomCodeNode.cs.meta

1
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Presenters/MaterialGraphPresenter.cs


typeMapper[typeof(BlendModeNode)] = typeof(BlendModeNodePresenter);
typeMapper[typeof(AddManyNode)] = typeof(AddManyNodePresenter);
typeMapper[typeof(IfNode)] = typeof(IfNodePresenter);
typeMapper[typeof(CustomCodeNode)] = typeof(CustomCodePresenter);
}
}
}

99
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Presenters/CustomCodeNodePresenter.cs


using System;
using System.Linq;
using System.Collections.Generic;
using RMGUI.GraphView;
using UnityEditor.Graphing.Drawing;
using UnityEngine;
using UnityEngine.MaterialGraph;
namespace UnityEditor.MaterialGraph.Drawing
{
class CustomCodeControlPresenter : GraphControlPresenter
{
public class CodeEditorPopup : PopupWindowContent
{
private CustomCodeNode m_customCodeNode = null;
private Vector2 m_scroll;
private bool m_isOpen = false;
public bool IsOpen
{
get { return m_isOpen; }
}
public CodeEditorPopup(CustomCodeNode customCodeNode)
{
m_customCodeNode = customCodeNode;
}
public override Vector2 GetWindowSize()
{
return new Vector2(300, 300);
}
public override void OnGUI(Rect rect)
{
GUILayout.Label("Custom Code Editor", EditorStyles.boldLabel);
m_scroll = EditorGUILayout.BeginScrollView(m_scroll);
m_customCodeNode.Code = EditorGUILayout.TextArea(m_customCodeNode.Code, GUILayout.Height(GetWindowSize().y - 30));
EditorGUILayout.EndScrollView();
}
public override void OnOpen()
{
Debug.Log("Popup opened: " + this);
m_isOpen = true;
}
public override void OnClose()
{
Debug.Log("Popup closed: " + this);
if (m_isOpen)
{
m_customCodeNode.UpdateInputAndOuputSlots();
}
m_isOpen = false;
}
}
private CodeEditorPopup codeEditorPopup = null;
Rect buttonRect;
public override void OnGUIHandler()
{
base.OnGUIHandler();
var tNode = node as UnityEngine.MaterialGraph.CustomCodeNode;
if (tNode == null)
return;
if (codeEditorPopup == null)
{
codeEditorPopup = new CodeEditorPopup(tNode);
tNode.Code = "//Write your function below.\r\nvoid test(float a, float b, out float c)\r\n{\r\n\tc = a + b;\r\n}\r\n";
}
string buttonText = codeEditorPopup.IsOpen ? "Close Code Editor" : "Open Code Editor";
if (GUILayout.Button(buttonText))
{
PopupWindow.Show(buttonRect, codeEditorPopup);
}
if (Event.current.type == EventType.Repaint) buttonRect = GUILayoutUtility.GetLastRect();
}
public override float GetHeight()
{
return (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing) + EditorGUIUtility.standardVerticalSpacing;
}
}
[Serializable]
public class CustomCodePresenter : PropertyNodePresenter
{
protected override IEnumerable<GraphElementPresenter> GetControlData()
{
var instance = CreateInstance<CustomCodeControlPresenter>();
instance.Initialize(node);
return new List<GraphElementPresenter>(base.GetControlData()) { instance };
}
}
}

12
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Presenters/CustomCodeNodePresenter.cs.meta


fileFormatVersion: 2
guid: 2c8cc961345e3fa4591080689cce37f0
timeCreated: 1495617692
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

281
MaterialGraphProject/Assets/UnityShaderEditor/Runtime/Nodes/Logic/CustomCodeNode.cs


using System;
using UnityEngine.Graphing;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace UnityEngine.MaterialGraph
{
[Title("Logic/CustomCode")]
public class CustomCodeNode : FunctionNInNOut, IGeneratesFunction
{
[SerializeField] private string m_code;
public class FunctionArgument
{
public string TypeName;
public string Name;
public FunctionArgument(string typeName, string name)
{
this.TypeName = typeName;
this.Name = name;
}
}
List<int> inputSlotIds = new List<int>();
List<int> outputSlotIds = new List<int>();
public string Code
{
get { return m_code; }
set
{
if (string.Equals(m_code, value))
return;
m_code = value;
}
}
public CustomCodeNode()
{
name = "CustomCode";
}
protected override string GetFunctionName()
{
string guid = System.Guid.NewGuid().ToString();
guid = guid.Replace("-", "_");
return "unity_CustomCode_" + GetFunctionName(GetFunctionSignature()); // + "_" + guid;
}
public void GenerateNodeFunction(ShaderGenerator visitor, GenerationMode generationMode)
{
var outputString = new ShaderGenerator();
outputString.AddShaderChunk(GetFunctionPrototype(), false);
outputString.AddShaderChunk(GetFunctionBody(), false);
visitor.AddShaderChunk(outputString.GetShaderString(0), true);
}
public void UpdateInputAndOuputSlots()
{
if (string.IsNullOrEmpty(m_code))
{
return;
}
onModified(this, ModificationScope.Graph);
string functionSignature = GetFunctionSignature();
List<FunctionArgument> inputArguments = GetInputArguments(functionSignature);
List<FunctionArgument> outputArguments = GetOutputArguments(functionSignature);
UpdateInputSlots(inputArguments);
UpdateOutputSlots(outputArguments);
}
void UpdateInputSlots(List<FunctionArgument> inputArguments)
{
if (inputArguments == null || inputArguments.Count <= 0)
{
return;
}
int slotIndex = 0;
if (inputSlotIds.Count > 0)
{
for (slotIndex = 0; slotIndex < inputSlotIds.Count; ++slotIndex)
{
RemoveSlot(inputSlotIds[slotIndex]);
}
inputSlotIds.Clear();
}
List<int> slotIds = new List<int>();
foreach (ISlot inSlot in GetSlots<ISlot>())
{
slotIds.Add(inSlot.id);
}
// TODO: This is a bit of a hack. We need to find a better way
// to handle the copying of this node.
for (slotIndex = 0; slotIndex < slotIds.Count; ++slotIndex)
{
RemoveSlot(slotIds[slotIndex]);
}
for (slotIndex = 0; slotIndex < inputArguments.Count; ++slotIndex)
{
string nativeType = inputArguments[slotIndex].TypeName;
SlotValueType valueType = SlotValueType.Vector1;
if (string.Equals(nativeType, "float2"))
{
valueType = SlotValueType.Vector2;
}
else if (string.Equals(nativeType, "float3"))
{
valueType = SlotValueType.Vector3;
}
else if (string.Equals(nativeType, "float4"))
{
valueType = SlotValueType.Vector4;
}
inputSlotIds.Add(AddSlot(inputArguments[slotIndex].Name, inputArguments[slotIndex].Name, SlotType.Input, valueType, Vector4.zero));
}
}
void UpdateOutputSlots(List<FunctionArgument> outputArguments)
{
if (outputArguments == null || outputArguments.Count <= 0)
{
return;
}
int slotIndex = 0;
if (outputSlotIds.Count > 0)
{
for (slotIndex = 0; slotIndex < outputSlotIds.Count; ++slotIndex)
{
RemoveSlot(outputSlotIds[slotIndex]);
}
outputSlotIds.Clear();
}
for (slotIndex = 0; slotIndex < outputArguments.Count; ++slotIndex)
{
string nativeType = outputArguments[slotIndex].TypeName;
SlotValueType valueType = SlotValueType.Vector1;
if (string.Equals(nativeType, "float2"))
{
valueType = SlotValueType.Vector2;
}
else if (string.Equals(nativeType, "float3"))
{
valueType = SlotValueType.Vector3;
}
else if (string.Equals(nativeType, "float4"))
{
valueType = SlotValueType.Vector4;
}
inputSlotIds.Add(AddSlot(outputArguments[slotIndex].Name, outputArguments[slotIndex].Name, SlotType.Output, valueType, Vector4.zero));
}
}
string GetFunctionWithoutComments()
{
List<string> codeWithoutComments = new List<string>();
string[] lines = m_code.Split(new string[] {System.Environment.NewLine}, StringSplitOptions.None);
for (int i = 0; i < lines.Length; ++i)
{
string line = lines[i].Trim();
if (!line.StartsWith("//"))
{
codeWithoutComments.Add(lines[i]);
continue;
}
}
return string.Join(System.Environment.NewLine, codeWithoutComments.ToArray());
}
string GetFunctionSignature()
{
// Find First { character so we can get the entire function signature;
string codeWithoutComments = GetFunctionWithoutComments();
int braceIndex = codeWithoutComments.IndexOf('{');
string functionSignature = codeWithoutComments.Substring(0, braceIndex);
functionSignature = functionSignature.Trim();
functionSignature = Regex.Replace(functionSignature, @"\s+", " ");
return functionSignature;
}
string GetFunctionBody()
{
// Find First { character so we can get the entire function signature;
string codeWithoutComments = GetFunctionWithoutComments();
int firstBraceIndex = codeWithoutComments.IndexOf('{');
int lastBraceIndex = codeWithoutComments.LastIndexOf('}')+1;
string functionBody = codeWithoutComments.Substring(firstBraceIndex, lastBraceIndex - firstBraceIndex);
return functionBody;
}
string GetFunctionName(string functionSignature)
{
string codeWithoutComments = GetFunctionWithoutComments();
int firstSpace = functionSignature.IndexOf(' ') + 1;
int firstParenthesee = codeWithoutComments.IndexOf('(');
string functionName = codeWithoutComments.Substring(firstSpace, firstParenthesee - firstSpace);
return functionName;
}
List<FunctionArgument> GetInputArguments(string functionSignature)
{
string codeWithoutComments = GetFunctionWithoutComments();
int firstCharacter = codeWithoutComments.IndexOf('(') + 1;
int lastParenthesee = codeWithoutComments.IndexOf(')');
string argumentString = codeWithoutComments.Substring(firstCharacter, lastParenthesee - firstCharacter);
string[] arguments = argumentString.Split(new char[] {','});
if (arguments == null || arguments.Length <= 0)
{
return null;
}
List<FunctionArgument> inputArguments = new List<FunctionArgument>();
foreach (string arg in arguments)
{
string trimmedArg = arg.Trim();
string[] components = trimmedArg.Split(new char[] {' '});
if (string.Equals(components[0].ToLower(), "out"))
{
break;
}
inputArguments.Add(new FunctionArgument(components[0], components[1]));
}
return inputArguments;
}
List<FunctionArgument> GetOutputArguments(string functionSignature)
{
string codeWithoutComments = GetFunctionWithoutComments();
int firstCharacter = codeWithoutComments.IndexOf('(') + 1;
int lastParenthesee = codeWithoutComments.IndexOf(')');
string argumentString = codeWithoutComments.Substring(firstCharacter, lastParenthesee - firstCharacter);
string[] arguments = argumentString.Split(new char[] { ',' });
if (arguments == null || arguments.Length <= 0)
{
return null;
}
List<FunctionArgument> outputArguments = new List<FunctionArgument>();
foreach (string arg in arguments)
{
string trimmedArg = arg.Trim();
string[] components = trimmedArg.Split(new char[] { ' ' });
// Skip non output nodes
if (!string.Equals(components[0].ToLower(), "out"))
{
continue;
}
outputArguments.Add(new FunctionArgument(components[1], components[2]));
}
return outputArguments;
}
}
}

12
MaterialGraphProject/Assets/UnityShaderEditor/Runtime/Nodes/Logic/CustomCodeNode.cs.meta


fileFormatVersion: 2
guid: a47fbd7891fadf6438af4abc1d2baab4
timeCreated: 1495617270
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存