您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

486 行
14 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditorInternal;
using UnityEngine;
namespace UnityEditor.Graphs.Material
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue)]
public class TitleAttribute : Attribute
{
public string m_Title;
public TitleAttribute(string title) { this.m_Title = title; }
}
public enum Precision
{
Default = 0, // half
Full = 1,
Fixed = 2,
}
public class PreviewProperty
{
public string m_Name;
public PropertyType m_PropType;
public Color m_Color;
public Texture2D m_Texture;
public Vector4 m_Vector4;
}
public enum PreviewMode
{
Preview2D,
Preview3D
}
public abstract class BaseMaterialNode : Node, IGenerateProperties
{
#region Fields
private const int kPreviewWidth = 64;
private const int kPreviewHeight = 64;
private UnityEngine.Material m_Material;
[SerializeField]
private List<SlotDefaultValue> m_SlotDefaultValues;
#endregion
#region Properties
internal PixelGraph pixelGraph { get { return graph as PixelGraph; } }
public bool generated { get; set; }
// 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 bool isSelected { get; set; }
// lookup custom slot properties
public void SetSlotDefaultValue (string slotName, SlotDefaultValue defaultValue)
{
var existingValue = m_SlotDefaultValues.FirstOrDefault (x => x.slotName == slotName);
if (existingValue != null)
m_SlotDefaultValues.Remove (existingValue);
if (defaultValue == null)
return;
m_SlotDefaultValues.Add (defaultValue);
}
public string precision
{
get { return "half"; }
}
public string[] m_PrecisionNames = {"half"};
public SlotDefaultValue GetSlotDefaultValue (string slotName)
{
return m_SlotDefaultValues.FirstOrDefault (x => x.slotName == slotName);
}
private UnityEngine.Material previewMaterial
{
get
{
if (m_Material == null)
m_Material = new UnityEngine.Material(Shader.Find("Diffuse")) { hideFlags = HideFlags.DontSave };
return m_Material;
}
}
private PreviewMode m_GeneratedShaderMode = PreviewMode.Preview2D;
private bool needsUpdate
{
get { return true; }
}
#endregion
public virtual void Init ()
{
hideFlags = HideFlags.HideInHierarchy;
}
void OnEnable ()
{
if (m_SlotDefaultValues == null) {
m_SlotDefaultValues = new List<SlotDefaultValue>();
}
}
public override void NodeUI (GraphGUI host)
{
base.NodeUI(host);
if (hasPreview)
OnPreviewGUI();
}
protected virtual void OnPreviewGUI ()
{
if (!ShaderUtil.hardwareSupportsRectRenderTexture)
return;
GUILayout.BeginHorizontal(GUILayout.MinWidth (kPreviewWidth + 10), GUILayout.MinWidth (kPreviewHeight + 10));
GUILayout.FlexibleSpace();
var rect = GUILayoutUtility.GetRect(kPreviewWidth, kPreviewHeight, GUILayout.ExpandWidth(false));
var preview = RenderPreview (rect);
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUI.DrawTexture (rect, preview, ScaleMode.StretchToFill, false);
}
#region Nodes
// 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<BaseMaterialNode> CollectDependentNodes()
{
var nodeList = new List<BaseMaterialNode>();
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 IEnumerable<BaseMaterialNode> CollectChildNodesByExecutionOrder(Slot slotToUse = null, bool includeSelf = true)
{
var nodeList = new List<BaseMaterialNode>();
CollectChildNodesByExecutionOrder (nodeList, slotToUse, includeSelf);
return nodeList;
}
public IEnumerable<BaseMaterialNode> CollectChildNodesByExecutionOrder (List<BaseMaterialNode> nodeList, Slot slotToUse = null, bool includeSelf = true)
{
if (slotToUse != null && !slots.Contains(slotToUse))
{
Debug.LogError("Attempting to collect nodes by execution order with an invalid slot on: " + name);
return nodeList;
}
NodeUtils.CollectChildNodesByExecutionOrder (nodeList, this, slotToUse);
if (!includeSelf)
nodeList.Remove (this);
return nodeList;
}
#endregion
#region Previews
protected virtual void SetEditorPreviewMaterialValues ()
{
if (!needsUpdate)
return;
if (previewMaterial.HasProperty ("EDITOR_TIME"))
{
var time = (float)EditorApplication.timeSinceStartup;
previewMaterial.SetVector("EDITOR_TIME", new Vector4(time / 20.0f, time, time * 2.0f, time*3));
}
if (previewMaterial.HasProperty("EDITOR_SIN_TIME"))
{
var time = (float)EditorApplication.timeSinceStartup;
previewMaterial.SetVector("EDITOR_SIN_TIME",
new Vector4(
Mathf.Sin(time / 8.0f),
Mathf.Sin(time / 4.0f),
Mathf.Sin(time / 2.0f),
Mathf.Sin(time)));
}
}
public virtual bool UpdatePreviewMaterial ()
{
MaterialWindow.DebugMaterialGraph("RecreateShaderAndMaterial : " + name + "_" + GetInstanceID());
var resultShader = ShaderGenerator.GeneratePreviewShader (this, out m_GeneratedShaderMode);
MaterialWindow.DebugMaterialGraph (resultShader);
if (previewMaterial.shader.name != "Diffuse")
DestroyImmediate(previewMaterial.shader, true);
previewMaterial.shader = UnityEditorInternal.InternalEditorUtility.CreateShaderAsset (resultShader);
previewMaterial.shader.hideFlags = HideFlags.DontSave;
return true;
}
// this function looks at all the nodes that have a
// dependency on this node. They will then have their
// preview regenerated.
public void RegeneratePreviewShaders()
{
CollectDependentNodes()
.Where(x => x.hasPreview)
.All(s => s.UpdatePreviewMaterial ());
}
private static Mesh[] s_Meshes = { null, null, null, null };
/// <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 (s_Meshes[0] == null)
{
GameObject 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 bmg = (graph as BaseMaterialGraph);
if (bmg == null)
return null;
var previewUtil = bmg.previewUtility;
previewUtil.BeginPreview (targetSize, GUIStyle.none);
// update the time in the preview material
SetEditorPreviewMaterialValues ();
if (m_GeneratedShaderMode == PreviewMode.Preview3D)
{
previewUtil.m_Camera.transform.position = -Vector3.forward * 5;
previewUtil.m_Camera.transform.rotation = Quaternion.identity;
var amb = new Color(.2f, .2f, .2f, 0);
previewUtil.m_Light[0].intensity = .5f;
previewUtil.m_Light[0].transform.rotation = Quaternion.Euler(50f, 50f, 0);
previewUtil.m_Light[1].intensity = .5f;
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);
bool oldFog = RenderSettings.fog;
Unsupported.SetRenderSettingsUseFogNoDirty (false);
previewUtil.m_Camera.Render();
Unsupported.SetRenderSettingsUseFogNoDirty (oldFog);
InternalEditorUtility.RemoveCustomLighting ();
}
else
{
Graphics.Blit(null, previewMaterial);
}
return previewUtil.EndPreview ();
}
private void SetPreviewMaterialProperty(PreviewProperty previewProperty)
{
switch (previewProperty.m_PropType)
{
case PropertyType.Texture2D:
previewMaterial.SetTexture(previewProperty.m_Name, previewProperty.m_Texture);
break;
case PropertyType.Color:
previewMaterial.SetColor(previewProperty.m_Name, previewProperty.m_Color);
break;
case PropertyType.Vector4:
previewMaterial.SetVector(previewProperty.m_Name, previewProperty.m_Vector4);
break;
}
}
protected void SetDependentPreviewMaterialProperty (PreviewProperty previewProperty)
{
var dependentNodes = CollectDependentNodes ();
foreach (var node in dependentNodes)
{
if (node.hasPreview)
node.SetPreviewMaterialProperty (previewProperty);
}
}
public virtual void UpdatePreviewProperties()
{
foreach (var s in inputSlots)
{
if (s.edges.Count > 0)
continue;
var defaultInput = GetSlotDefaultValue (s.name);
if (defaultInput == null)
continue;
var pp = new PreviewProperty
{
m_Name = defaultInput.inputName,
m_PropType = PropertyType.Vector4,
m_Vector4 = defaultInput.defaultValue
};
SetDependentPreviewMaterialProperty (pp);
}
}
#endregion
#region Slots
public virtual IEnumerable<Slot> GetValidInputSlots ()
{
return inputSlots;
}
public virtual string GetOutputVariableNameForSlot (Slot s, GenerationMode generationMode)
{
if (s.isInputSlot) Debug.LogError ("Attempting to use input slot (" + s + ") for output!");
if (!slots.Contains(s)) Debug.LogError("Attempting to use slot (" + s + ") for output on a node that does not have this slot!");
return GetOutputVariableNameForNode () + "_" + s.name;
}
public virtual string GetOutputVariableNameForNode ()
{
return name + "_" + Math.Abs(GetInstanceID());
}
public virtual Vector4 GetNewSlotDefaultValue ()
{
return Vector4.one;
}
public new void AddSlot(Slot slot)
{
AddSlot (slot, GetNewSlotDefaultValue ());
}
public void AddSlot(Slot slot, Vector4 defaultValue)
{
base.AddSlot(slot);
// slots are not serialzied but the default values are
// because of this we need to see if the default has
// already been set
// if it has... do nothing.
MaterialWindow.DebugMaterialGraph ("Node ID: " + GetInstanceID ());
MaterialWindow.DebugMaterialGraph ("Node Name: " + GetOutputVariableNameForNode ());
if (GetSlotDefaultValue(slot.name) == null)
SetSlotDefaultValue(slot.name, new SlotDefaultValue(defaultValue, this, slot.name, true));
var slotthing = GetSlotDefaultValue (slot.name);
MaterialWindow.DebugMaterialGraph ("Slot Thing: " + slotthing.inputName);
}
public override void RemoveSlot (Slot slot)
{
SetSlotDefaultValue(slot.name, null);
base.RemoveSlot(slot);
}
public string GenerateSlotName (SlotType type)
{
var slotsToCheck = type == SlotType.InputSlot ? inputSlots.ToArray() : outputSlots.ToArray();
string format = type == SlotType.InputSlot ? "I{0:00}" : "O{0:00}";
int index = slotsToCheck.Length;
var name = string.Format (format, index);
if (slotsToCheck.All (x => x.name != name))
return name;
index = 0;
do {
name = string.Format (format, index++);
} while (slotsToCheck.Any(x => x.name == name));
return name;
}
#endregion
public virtual void GeneratePropertyBlock(PropertyGenerator visitor, GenerationMode generationMode)
{
if (!generationMode.IsPreview ())
return;
foreach (var inputSlot in inputSlots)
{
if (inputSlot.edges.Count > 0)
continue;
var defaultForSlot = GetSlotDefaultValue(inputSlot.name);
defaultForSlot.GeneratePropertyBlock(visitor, generationMode);
}
}
public virtual void GeneratePropertyUsages(ShaderGenerator visitor, GenerationMode generationMode)
{
if (!generationMode.IsPreview ())
return;
foreach (var inputSlot in inputSlots)
{
if (inputSlot.edges.Count > 0)
continue;
var defaultForSlot = GetSlotDefaultValue(inputSlot.name);
defaultForSlot.GeneratePropertyUsages(visitor, generationMode);
}
}
public Slot FindInputSlot (string name)
{
var slot = inputSlots.FirstOrDefault(x => x.name == name);
if (slot == null)
Debug.LogError("Input slot: " + name + " could be found on node " + GetOutputVariableNameForNode() );
return slot;
}
public Slot FindOutputSlot(string name)
{
var slot = outputSlots.FirstOrDefault(x => x.name == name);
if (slot == null)
Debug.LogError("Output slot: " + name + " could be found on node " + GetOutputVariableNameForNode());
return slot;
}
protected string GetSlotValue (Slot inputSlot, GenerationMode generationMode)
{
bool pointInputConnected = inputSlot.edges.Count > 0;
string inputValue;
if (pointInputConnected)
{
var dataProvider = inputSlot.edges[0].fromSlot.node as BaseMaterialNode;
inputValue = dataProvider.GetOutputVariableNameForSlot(inputSlot.edges[0].fromSlot, generationMode);
}
else
{
var defaultValue = GetSlotDefaultValue(inputSlot.name);
inputValue = defaultValue.GetDefaultValue(generationMode);
}
return inputValue;
}
}
}