using System.Text.RegularExpressions ;
using UnityEngine ;
using UnityEditor.Graphing ;
using UnityEditor.Graphing.Util ;
public abstract class AbstractMaterialGraph : SerializableGraph , IGenerateProperties
public abstract class AbstractMaterialGraph : IGraph , ISerializationCallbackReceiver , IGenerateProperties
public IGraphObject owner { get ; set ; }
#region Property data
public IEnumerable < IShaderProperty > properties
{
get { return m_Properties ; }
}
[SerializeField]
List < SerializationHelper . JSONSerializedElement > m_SerializedProperties = new List < SerializationHelper . JSONSerializedElement > ( ) ;
public IEnumerable < IShaderProperty > addedProperties
{
get { return m_AddedProperties ; }
}
public IEnumerable < Guid > removedProperties
{
get { return m_RemovedProperties ; }
}
public InspectorPreviewData previewData = new InspectorPreviewData ( ) ;
List < IShaderProperty > m_MovedProperties = new List < IShaderProperty > ( ) ;
public Mesh previewMesh
public IEnumerable < IShaderProperty > movedProperties
get { return previewData . mesh ; }
set { previewData . mesh = value ; }
get { return m_MovedProperties ; }
public IEnumerable < IShaderProperty > properties
[SerializeField]
SerializableGuid m_GUID = new SerializableGuid ( ) ;
public Guid guid
get { return m_Properties ; }
get { return m_GUID . guid ; }
public IEnumerable < IShaderProperty > addedProperties
#endregion
#region Node data
[NonSerialized]
Stack < Identifier > m_FreeNodeTempIds = new Stack < Identifier > ( ) ;
[NonSerialized]
List < AbstractMaterialNode > m_Nodes = new List < AbstractMaterialNode > ( ) ;
[NonSerialized]
Dictionary < Guid , INode > m_NodeDictionary = new Dictionary < Guid , INode > ( ) ;
public IEnumerable < T > GetNodes < T > ( ) where T : INode
get { return m_AddedProperties ; }
return m_Nodes . Where ( x = > x ! = null ) . OfType < T > ( ) ;
public IEnumerable < Guid > removedProperties
[SerializeField]
List < SerializationHelper . JSONSerializedElement > m_SerializableNodes = new List < SerializationHelper . JSONSerializedElement > ( ) ;
[NonSerialized]
List < INode > m_AddedNodes = new List < INode > ( ) ;
public IEnumerable < INode > addedNodes
get { return m_RemovedProperties ; }
get { return m_AddedNodes ; }
}
[NonSerialized]
List < INode > m_RemovedNodes = new List < INode > ( ) ;
public IEnumerable < INode > removedNodes
{
get { return m_RemovedNodes ; }
}
#endregion
#region Edge data
[NonSerialized]
List < IEdge > m_Edges = new List < IEdge > ( ) ;
public IEnumerable < IEdge > edges
{
get { return m_Edges ; }
}
[SerializeField]
List < SerializationHelper . JSONSerializedElement > m_SerializableEdges = new List < SerializationHelper . JSONSerializedElement > ( ) ;
[NonSerialized]
Dictionary < Guid , List < IEdge > > m_NodeEdges = new Dictionary < Guid , List < IEdge > > ( ) ;
[NonSerialized]
List < IEdge > m_AddedEdges = new List < IEdge > ( ) ;
public IEnumerable < IEdge > addedEdges
{
get { return m_AddedEdges ; }
}
[NonSerialized]
List < IEdge > m_RemovedEdges = new List < IEdge > ( ) ;
public IEnumerable < IEdge > removedEdges
{
get { return m_RemovedEdges ; }
}
#endregion
[SerializeField]
InspectorPreviewData m_PreviewData = new InspectorPreviewData ( ) ;
public InspectorPreviewData previewData
{
get { return m_PreviewData ; }
set { m_PreviewData = value ; }
public override void ClearChanges ( )
public void ClearChanges ( )
base . ClearChanges ( ) ;
m_AddedNodes . Clear ( ) ;
m_RemovedNodes . Clear ( ) ;
m_AddedEdges . Clear ( ) ;
m_RemovedEdges . Clear ( ) ;
m_MovedProperties . Clear ( ) ;
public override void AddNode ( INode node )
public virtual void AddNode ( INode node )
base . AddNode ( node ) ;
AddNodeNoValidate ( node ) ;
ValidateGraph ( ) ;
}
else
{
public virtual void CollectShaderProperties ( PropertyCollector collector , GenerationMode generationMode )
void AddNodeNoValidate ( INode node )
foreach ( var prop in properties )
collector . AddShaderProperty ( prop ) ;
var materialNode = ( AbstractMaterialNode ) node ;
materialNode . owner = this ;
if ( m_FreeNodeTempIds . Any ( ) )
{
var id = m_FreeNodeTempIds . Pop ( ) ;
id . IncrementVersion ( ) ;
materialNode . tempId = id ;
m_Nodes [ id . index ] = materialNode ;
}
else
{
var id = new Identifier ( m_Nodes . Count ) ;
materialNode . tempId = id ;
m_Nodes . Add ( materialNode ) ;
}
m_NodeDictionary . Add ( materialNode . guid , materialNode ) ;
m_AddedNodes . Add ( materialNode ) ;
public virtual void AddShaderProperty ( IShaderProperty property )
public void RemoveNode ( INode node )
if ( property = = null )
if ( ! node . canDeleteNode )
RemoveNodeNoValidate ( node ) ;
ValidateGraph ( ) ;
}
if ( m_Properties . Contains ( property ) )
void RemoveNodeNoValidate ( INode node )
{
var materialNode = ( AbstractMaterialNode ) node ;
if ( ! materialNode . canDeleteNode )
property . displayName = property . displayName . Trim ( ) ;
if ( m_Properties . Any ( p = > p . displayName = = property . displayName ) )
{
var regex = new Regex ( @"^" + Regex . Escape ( property . displayName ) + @" \((\d+)\)$" ) ;
var existingDuplicateNumbers = m_Properties . Select ( p = > regex . Match ( p . displayName ) ) . Where ( m = > m . Success ) . Select ( m = > int . Parse ( m . Groups [ 1 ] . Value ) ) . Where ( n = > n > 0 ) . ToList ( ) ;
var duplicateNumber = 1 ;
existingDuplicateNumbers . Sort ( ) ;
if ( existingDuplicateNumbers . Any ( ) & & existingDuplicateNumbers . First ( ) = = 1 )
{
duplicateNumber = existingDuplicateNumbers . Last ( ) + 1 ;
for ( var i = 1 ; i < existingDuplicateNumbers . Count ; i + + )
{
if ( existingDuplicateNumbers [ i - 1 ] ! = existingDuplicateNumbers [ i ] - 1 )
{
duplicateNumber = existingDuplicateNumbers [ i - 1 ] + 1 ;
break ;
}
}
}
property . displayName = string . Format ( "{0} ({1})" , property . displayName , duplicateNumber ) ;
}
m_Properties . Add ( property ) ;
m_AddedProperties . Add ( property ) ;
m_Nodes [ materialNode . tempId . index ] = null ;
m_FreeNodeTempIds . Push ( materialNode . tempId ) ;
m_NodeDictionary . Remove ( materialNode . guid ) ;
m_RemovedNodes . Add ( materialNode ) ;
public void RemoveShaderProperty ( Guid guid )
void AddEdgeToNodeEdges ( IEdge edge )
RemoveShaderPropertyNoValidate ( guid ) ;
ValidateGraph ( ) ;
List < IEdge > inputEdges ;
if ( ! m_NodeEdges . TryGetValue ( edge . inputSlot . nodeGuid , out inputEdges ) )
m_NodeEdges [ edge . inputSlot . nodeGuid ] = inputEdges = new List < IEdge > ( ) ;
inputEdges . Add ( edge ) ;
List < IEdge > outputEdges ;
if ( ! m_NodeEdges . TryGetValue ( edge . outputSlot . nodeGuid , out outputEdges ) )
m_NodeEdges [ edge . outputSlot . nodeGuid ] = outputEdges = new List < IEdge > ( ) ;
outputEdges . Add ( edge ) ;
void RemoveShaderPropertyNoValidate ( Guid guid )
IEdge ConnectNoValidate ( SlotReference fromSlotRef , SlotReference toSlotRef )
var propertyNodes = GetNodes < PropertyNode > ( ) . Where ( x = > x . propertyGuid = = guid ) . ToList ( ) ;
var fromNode = GetNodeFromGuid ( fromSlotRef . nodeGuid ) ;
var toNode = GetNodeFromGuid ( toSlotRef . nodeGuid ) ;
foreach ( var propNode in propertyNodes )
ReplacePropertyNodeWithConcreteNode ( propNode ) ;
if ( fromNode = = null | | toNode = = null )
return null ;
if ( m_Properties . RemoveAll ( x = > x . guid = = guid ) > 0 )
m_RemovedProperties . Add ( guid ) ;
}
// if fromNode is already connected to toNode
// do now allow a connection as toNode will then
// have an edge to fromNode creating a cycle.
// if this is parsed it will lead to an infinite loop.
var dependentNodes = new List < INode > ( ) ;
NodeUtils . CollectNodesNodeFeedsInto ( dependentNodes , toNode ) ;
if ( dependentNodes . Contains ( fromNode ) )
return null ;
static List < IEdge > s_TempEdges = new List < IEdge > ( ) ;
var fromSlot = fromNode . FindSlot < ISlot > ( fromSlotRef . slotId ) ;
var toSlot = toNode . FindSlot < ISlot > ( toSlotRef . slotId ) ;
public void ReplacePropertyNodeWithConcreteNode ( PropertyNode propertyNode )
{
var property = properties . FirstOrDefault ( x = > x . guid = = propertyNode . propertyGuid ) ;
if ( property ! = null )
if ( fromSlot . isOutputSlot = = toSlot . isOutputSlot )
return null ;
var outputSlot = fromSlot . isOutputSlot ? fromSlotRef : toSlotRef ;
var inputSlot = fromSlot . isInputSlot ? fromSlotRef : toSlotRef ;
s_TempEdges . Clear ( ) ;
GetEdges ( inputSlot , s_TempEdges ) ;
// remove any inputs that exits before adding
foreach ( var edge in s_TempEdges )
AbstractMaterialNode node = null ;
int slotId = - 1 ;
if ( property is FloatShaderProperty )
{
var createdNode = new Vector1Node ( ) ;
createdNode . value = ( ( FloatShaderProperty ) property ) . value ;
slotId = Vector1Node . OutputSlotId ;
node = createdNode ;
}
else if ( property is Vector2ShaderProperty )
{
var createdNode = new Vector2Node ( ) ;
createdNode . value = ( ( Vector2ShaderProperty ) property ) . value ;
slotId = Vector2Node . OutputSlotId ;
node = createdNode ;
}
else if ( property is Vector3ShaderProperty )
{
var createdNode = new Vector3Node ( ) ;
createdNode . value = ( ( Vector3ShaderProperty ) property ) . value ;
slotId = Vector3Node . OutputSlotId ;
node = createdNode ;
}
else if ( property is Vector4ShaderProperty )
{
var createdNode = new Vector4Node ( ) ;
createdNode . value = ( ( Vector4ShaderProperty ) property ) . value ;
slotId = Vector4Node . OutputSlotId ;
node = createdNode ;
}
else if ( property is ColorShaderProperty )
{
var createdNode = new ColorNode ( ) ;
createdNode . color = ( ( ColorShaderProperty ) property ) . value ;
slotId = ColorNode . OutputSlotId ;
node = createdNode ;
}
else if ( property is TextureShaderProperty )
{
var createdNode = new Texture2DAssetNode ( ) ;
createdNode . texture = ( ( TextureShaderProperty ) property ) . value . texture ;
slotId = Texture2DAssetNode . OutputSlotId ;
node = createdNode ;
}
else if ( property is CubemapShaderProperty )
{
var createdNode = new CubemapAssetNode ( ) ;
createdNode . cubemap = ( ( CubemapShaderProperty ) property ) . value . cubemap ;
slotId = CubemapAssetNode . OutputSlotId ;
node = createdNode ;
}
RemoveEdgeNoValidate ( edge ) ;
}
if ( node = = null )
return ;
var newEdge = new Edge ( outputSlot , inputSlot ) ;
m_Edges . Add ( newEdge ) ;
m_AddedEdges . Add ( newEdge ) ;
AddEdgeToNodeEdges ( newEdge ) ;
var slot = propertyNode . FindOutputSlot < MaterialSlot > ( PropertyNode . OutputSlotId ) ;
node . drawState = propertyNode . drawState ;
AddNodeNoValidate ( node ) ;
//Debug.LogFormat("Connected edge: {0} -> {1} ({2} -> {3})\n{4}", newEdge.outputSlot.nodeGuid, newEdge.inputSlot.nodeGuid, fromNode.name, toNode.name, Environment.StackTrace);
return newEdge ;
}
s_TempEdges . Clear ( ) ;
GetEdges ( slot . slotReference , s_TempEdges ) ;
foreach ( var edge in s_TempEdges )
ConnectNoValidate ( node . GetSlotReference ( slotId ) , edge . inputSlot ) ;
public virtual IEdge Connect ( SlotReference fromSlotRef , SlotReference toSlotRef )
{
var newEdge = ConnectNoValidate ( fromSlotRef , toSlotRef ) ;
ValidateGraph ( ) ;
return newEdge ;
}
RemoveNodeNoValidate ( propertyNode ) ;
}
public virtual void RemoveEdge ( IEdge e )
{
RemoveEdgeNoValidate ( e ) ;
ValidateGraph ( ) ;
public override void ValidateGraph ( )
public void RemoveElements ( IEnumerable < INode > nodes , IEnumerable < IEdge > edges )
var propertyNodes = GetNodes < PropertyNode > ( ) . Where ( n = > ! m_Properties . Any ( p = > p . guid = = n . propertyGuid ) ) . ToArray ( ) ;
foreach ( var pNode in propertyNodes )
ReplacePropertyNodeWithConcreteNode ( pNode ) ;
base . ValidateGraph ( ) ;
foreach ( var edge in edges . ToArray ( ) )
RemoveEdgeNoValidate ( edge ) ;
foreach ( var serializableNode in nodes . ToArray ( ) )
RemoveNodeNoValidate ( serializableNode ) ;
ValidateGraph ( ) ;
public override Dictionary < SerializationHelper . TypeSerializationInfo , SerializationHelper . TypeSerializationInfo > GetLegacyTypeRemapping ( )
protected void RemoveEdgeNoValidate ( IEdge e )
var result = base . GetLegacyTypeRemapping ( ) ;
var viewNode = new SerializationHelper . TypeSerializationInfo
{
fullName = "UnityEngine.MaterialGraph.ViewDirectionNode"
} ;
result [ viewNode ] = SerializationHelper . GetTypeSerializableAsString ( typeof ( ViewDirectionNode ) ) ;
e = m_Edges . FirstOrDefault ( x = > x . Equals ( e ) ) ;
if ( e = = null )
throw new ArgumentException ( "Trying to remove an edge that does not exist." , "e" ) ;
m_Edges . Remove ( e ) ;
var normalNode = new SerializationHelper . TypeSerializationInfo
{
fullName = "UnityEngine.MaterialGraph.NormalNode"
} ;
result [ normalNode ] = SerializationHelper . GetTypeSerializableAsString ( typeof ( NormalVectorNode ) ) ;
List < IEdge > inputNodeEdges ;
if ( m_NodeEdges . TryGetValue ( e . inputSlot . nodeGuid , out inputNodeEdges ) )
inputNodeEdges . Remove ( e ) ;
var worldPosNode = new SerializationHelper . TypeSerializationInfo
{
fullName = "UnityEngine.MaterialGraph.WorldPosNode"
} ;
result [ worldPosNode ] = SerializationHelper . GetTypeSerializableAsString ( typeof ( PositionNode ) ) ;
List < IEdge > outputNodeEdges ;
if ( m_NodeEdges . TryGetValue ( e . outputSlot . nodeGuid , out outputNodeEdges ) )
outputNodeEdges . Remove ( e ) ;
return result ;
m_RemovedEdges . Add ( e ) ;
public override void ReplaceWith ( IGraph other )
public INode GetNodeFromGuid ( Guid guid )
var otherMG = other as AbstractMaterialGraph ;
if ( otherMG ! = null )
{
using ( var removedPropertiesPooledObject = ListPool < Guid > . GetDisposable ( ) )
{
var removedPropertyGuids = removedPropertiesPooledObject . value ;
foreach ( var property in m_Properties )
removedPropertyGuids . Add ( property . guid ) ;
foreach ( var propertyGuid in removedPropertyGuids )
RemoveShaderPropertyNoValidate ( propertyGuid ) ;
}
foreach ( var otherProperty in otherMG . properties )
{
if ( ! properties . Any ( p = > p . guid = = otherProperty . guid ) )
AddShaderProperty ( otherProperty ) ;
}
}
base . ReplaceWith ( other ) ;
INode node ;
m_NodeDictionary . TryGetValue ( guid , out node ) ;
return node ;
public override void OnBeforeSerialize ( )
public INode GetNodeFromTempId ( Identifier tempId )
base . OnBeforeSerialize ( ) ;
m_SerializedProperties = SerializationHelper . Serialize < IShaderProperty > ( m_Properties ) ;
var node = m_Nodes [ tempId . index ] ;
if ( node = = null )
throw new Exception ( "Index does not contain a node." ) ;
if ( node . tempId . version ! = tempId . version )
throw new Exception ( "Trying to retrieve a node that was removed from the graph." ) ;
return node ;
public override void OnAfterDeserialize ( )
public bool ContainsNodeGuid ( Guid guid )
// have to deserialize 'globals' before nodes
m_Properties = SerializationHelper . Deserialize < IShaderProperty > ( m_SerializedProperties , null ) ;
base . OnAfterDeserialize ( ) ;
return m_NodeDictionary . ContainsKey ( guid ) ;
internal static ShaderGraphRequirements GetRequirements ( List < INode > nodes )
public T GetNodeFromGuid < T > ( Guid guid ) where T : INode
NeededCoordinateSpace requiresNormal = nodes . OfType < IMayRequireNormal > ( ) . Aggregate ( NeededCoordinateSpace . None , ( mask , node ) = > mask | node . RequiresNormal ( ) ) ;
NeededCoordinateSpace requiresBitangent = nodes . OfType < IMayRequireBitangent > ( ) . Aggregate ( NeededCoordinateSpace . None , ( mask , node ) = > mask | node . RequiresBitangent ( ) ) ;
NeededCoordinateSpace requiresTangent = nodes . OfType < IMayRequireTangent > ( ) . Aggregate ( NeededCoordinateSpace . None , ( mask , node ) = > mask | node . RequiresTangent ( ) ) ;
NeededCoordinateSpace requiresViewDir = nodes . OfType < IMayRequireViewDirection > ( ) . Aggregate ( NeededCoordinateSpace . None , ( mask , node ) = > mask | node . RequiresViewDirection ( ) ) ;
NeededCoordinateSpace requiresPosition = nodes . OfType < IMayRequirePosition > ( ) . Aggregate ( NeededCoordinateSpace . None , ( mask , node ) = > mask | node . RequiresPosition ( ) ) ;
bool requiresScreenPosition = nodes . OfType < IMayRequireScreenPosition > ( ) . Any ( x = > x . RequiresScreenPosition ( ) ) ;
bool requiresVertexColor = nodes . OfType < IMayRequireVertexColor > ( ) . Any ( x = > x . RequiresVertexColor ( ) ) ;
var node = GetNodeFromGuid ( guid ) ;
if ( node is T )
return ( T ) node ;
return default ( T ) ;
}
var meshUV = new List < UVChannel > ( ) ;
for ( int uvIndex = 0 ; uvIndex < ShaderGeneratorNames . UVCount ; + + uvIndex )
public void GetEdges ( SlotReference s , List < IEdge > foundEdges )
{
var node = GetNodeFromGuid ( s . nodeGuid ) ;
if ( node = = null )
var channel = ( UVChannel ) uvIndex ;
if ( nodes . OfType < IMayRequireMeshUV > ( ) . Any ( x = > x . RequiresMeshUV ( channel ) ) )
meshUV . Add ( channel ) ;
Debug . LogWarning ( "Node does not exist" ) ;
return ;
ISlot slot = node . FindSlot < ISlot > ( s . slotId ) ;
// if anything needs tangentspace we have make
// sure to have our othonormal basis!
var compoundSpaces = requiresBitangent | requiresNormal | requiresPosition
| requiresTangent | requiresViewDir | requiresPosition
| requiresNormal ;
List < IEdge > candidateEdges ;
if ( ! m_NodeEdges . TryGetValue ( s . nodeGuid , out candidateEdges ) )
return ;
var needsTangentSpace = ( compoundSpaces & NeededCoordinateSpace . Tangent ) > 0 ;
if ( needsTangentSpace )
foreach ( var edge in candidateEdges )
requiresBitangent | = NeededCoordinateSpace . Object ;
requiresNormal | = NeededCoordinateSpace . Object ;
requiresTangent | = NeededCoordinateSpace . Object ;
var cs = slot . isInputSlot ? edge . inputSlot : edge . outputSlot ;
if ( cs . nodeGuid = = s . nodeGuid & & cs . slotId = = s . slotId )
foundEdges . Add ( edge ) ;
var reqs = new ShaderGraphRequirements ( )
{
requiresNormal = requiresNormal ,
requiresBitangent = requiresBitangent ,
requiresTangent = requiresTangent ,
requiresViewDir = requiresViewDir ,
requiresPosition = requiresPosition ,
requiresScreenPosition = requiresScreenPosition ,
requiresVertexColor = requiresVertexColor ,
requiresMeshUVs = meshUV
} ;
}
return reqs ;
public virtual void CollectShaderProperties ( PropertyCollector collector , GenerationMode generationMode )
{
foreach ( var prop in properties )
collector . AddShaderProperty ( prop ) ;
public string GetPreviewShader ( AbstractMaterialNode node , out PreviewMode previewMode )
public void AddShaderProperty ( IShaderProperty property )
List < PropertyCollector . TextureInfo > configuredTextures ;
FloatShaderProperty outputIdProperty ;
return GetShader ( node , GenerationMode . Preview , string . Format ( "hidden/preview/{0}" , node . GetVariableNameForNode ( ) ) , out configuredTextures , out previewMode , out outputIdProperty ) ;
if ( property = = null )
return ;
if ( m_Properties . Contains ( property ) )
return ;
property . displayName = property . displayName . Trim ( ) ;
if ( m_Properties . Any ( p = > p . displayName = = property . displayName ) )
{
var regex = new Regex ( @"^" + Regex . Escape ( property . displayName ) + @" \((\d+)\)$" ) ;
var existingDuplicateNumbers = m_Properties . Select ( p = > regex . Match ( p . displayName ) ) . Where ( m = > m . Success ) . Select ( m = > int . Parse ( m . Groups [ 1 ] . Value ) ) . Where ( n = > n > 0 ) . ToList ( ) ;
var duplicateNumber = 1 ;
existingDuplicateNumbers . Sort ( ) ;
if ( existingDuplicateNumbers . Any ( ) & & existingDuplicateNumbers . First ( ) = = 1 )
{
duplicateNumber = existingDuplicateNumbers . Last ( ) + 1 ;
for ( var i = 1 ; i < existingDuplicateNumbers . Count ; i + + )
{
if ( existingDuplicateNumbers [ i - 1 ] ! = existingDuplicateNumbers [ i ] - 1 )
{
duplicateNumber = existingDuplicateNumbers [ i - 1 ] + 1 ;
break ;
}
}
}
property . displayName = string . Format ( "{0} ({1})" , property . displayName , duplicateNumber ) ;
}
m_Properties . Add ( property ) ;
m_AddedProperties . Add ( property ) ;
public string GetUberPreviewShader ( Dictionary < Guid , int > ids , out FloatShaderProperty outputIdProperty )
public void RemoveShaderProperty ( Guid guid )
List < PropertyCollector . TextureInfo > configuredTextures ;
PreviewMode previewMode ;
return GetShader ( null , GenerationMode . Preview , "hidden/preview" , out configuredTextures , out previewMode , out outputIdProperty , ids ) ;
var propertyNodes = GetNodes < PropertyNode > ( ) . Where ( x = > x . propertyGuid = = guid ) . ToList ( ) ;
foreach ( var propNode in propertyNodes )
ReplacePropertyNodeWithConcreteNodeNoValidate ( propNode ) ;
RemoveShaderPropertyNoValidate ( guid ) ;
ValidateGraph ( ) ;
internal static void GenerateSurfaceDescriptionStruct ( ShaderGenerator surfaceDescriptionStruct , List < MaterialSlot > slots , bool isMaster )
public void MoveShaderProperty ( IShaderProperty property , int newIndex )
surfaceDescriptionStruct . AddShaderChunk ( "struct SurfaceDescription{" , false ) ;
surfaceDescriptionStruct . Indent ( ) ;
if ( isMaster )
{
foreach ( var slot in slots )
surfaceDescriptionStruct . AddShaderChunk ( string . Format ( "{0} {1};" , AbstractMaterialNode . ConvertConcreteSlotValueTypeToString ( AbstractMaterialNode . OutputPrecision . @float , slot . concreteValueType ) , AbstractMaterialNode . GetHLSLSafeName ( slot . shaderOutputName ) ) , false ) ;
surfaceDescriptionStruct . Deindent ( ) ;
}
if ( newIndex > m_Properties . Count | | newIndex < 0 )
throw new ArgumentException ( "New index is not within properties list." ) ;
var currentIndex = m_Properties . IndexOf ( property ) ;
if ( currentIndex = = - 1 )
throw new ArgumentException ( "Property is not in graph." ) ;
if ( newIndex = = currentIndex )
return ;
m_Properties . RemoveAt ( currentIndex ) ;
if ( newIndex > currentIndex )
newIndex - - ;
var isLast = newIndex = = m_Properties . Count ;
if ( isLast )
m_Properties . Add ( property ) ;
{
surfaceDescriptionStruct . AddShaderChunk ( "float4 PreviewOutput;" , false ) ;
}
surfaceDescriptionStruct . Deindent ( ) ;
surfaceDescriptionStruct . AddShaderChunk ( "};" , false ) ;
m_Properties . Insert ( newIndex , property ) ;
if ( ! m_MovedProperties . Contains ( property ) )
m_MovedProperties . Add ( property ) ;
internal static void GenerateApplicationVertexInputs ( ShaderGraphRequirements graphRequiements , ShaderGenerator vertexInputs , int vertexInputStartIndex , int maxVertexInputs )
public int GetShaderPropertyIndex ( IShaderProperty property )
int vertexInputIndex = vertexInputStartIndex ;
vertexInputs . AddShaderChunk ( "struct GraphVertexInput" , false ) ;
vertexInputs . AddShaderChunk ( "{" , false ) ;
vertexInputs . Indent ( ) ;
vertexInputs . AddShaderChunk ( "float4 vertex : POSITION;" , false ) ;
vertexInputs . AddShaderChunk ( "float3 normal : NORMAL;" , false ) ;
vertexInputs . AddShaderChunk ( "float4 tangent : TANGENT;" , false ) ;
return m_Properties . IndexOf ( property ) ;
}
if ( graphRequiements . requiresVertexColor )
void RemoveShaderPropertyNoValidate ( Guid guid )
{
if ( m_Properties . RemoveAll ( x = > x . guid = = guid ) > 0 )
vertexInputs . AddShaderChunk ( "float4 color : COLOR;" , false ) ;
m_RemovedProperties . Add ( guid ) ;
m_AddedProperties . RemoveAll ( x = > x . guid = = guid ) ;
m_MovedProperties . RemoveAll ( x = > x . guid = = guid ) ;
foreach ( var channel in graphRequiements . requiresMeshUVs . Distinct ( ) )
{
vertexInputs . AddShaderChunk ( string . Format ( "float4 texcoord{0} : TEXCOORD{1};" , ( ( int ) channel ) . ToString ( ) , vertexInputIndex . ToString ( ) ) , false ) ;
vertexInputIndex + + ;
}
}
static List < IEdge > s_TempEdges = new List < IEdge > ( ) ;
vertexInputs . AddShaderChunk ( "UNITY_VERTEX_INPUT_INSTANCE_ID" , false ) ;
vertexInputs . Deindent ( ) ;
vertexInputs . AddShaderChunk ( "};" , false ) ;
public void ReplacePropertyNodeWithConcreteNode ( PropertyNode propertyNode )
{
ReplacePropertyNodeWithConcreteNodeNoValidate ( propertyNode ) ;
ValidateGraph ( ) ;
internal static void GenerateSurfaceDescription (
List < INode > activeNodeList ,
AbstractMaterialNode masterNode ,
AbstractMaterialGraph graph ,
ShaderGenerator surfaceDescriptionFunction ,
ShaderGenerator shaderFunctionVisitor ,
PropertyCollector shaderProperties ,
ShaderGraphRequirements requirements ,
GenerationMode mode ,
string functionName = "PopulateSurfaceData" ,
string surfaceDescriptionName = "SurfaceDescription" ,
FloatShaderProperty outputIdProperty = null ,
Dictionary < Guid , int > ids = null ,
IEnumerable < MaterialSlot > slots = null )
void ReplacePropertyNodeWithConcreteNodeNoValidate ( PropertyNode propertyNode )
if ( graph = = null )
var property = properties . FirstOrDefault ( x = > x . guid = = propertyNode . propertyGuid ) ;
if ( property = = null )
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "{0} {1}(SurfaceInputs IN) {{" , surfaceDescriptionName , functionName ) , false ) ;
surfaceDescriptionFunction . Indent ( ) ;
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "{0} surface = ({0})0;" , surfaceDescriptionName ) , false ) ;
var node = property . ToConcreteNode ( ) ;
if ( ! ( node is AbstractMaterialNode ) )
return ;
foreach ( CoordinateSpace space in Enum . GetValues ( typeof ( CoordinateSpace ) ) )
{
var neededCoordinateSpace = space . ToNeededCoordinateSpace ( ) ;
if ( ( requirements . requiresNormal & neededCoordinateSpace ) > 0 )
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "float3 {0} = IN.{0};" , space . ToVariableName ( InterpolatorType . Normal ) ) , false ) ;
if ( ( requirements . requiresTangent & neededCoordinateSpace ) > 0 )
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "float3 {0} = IN.{0};" , space . ToVariableName ( InterpolatorType . Tangent ) ) , false ) ;
if ( ( requirements . requiresBitangent & neededCoordinateSpace ) > 0 )
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "float3 {0} = IN.{0};" , space . ToVariableName ( InterpolatorType . BiTangent ) ) , false ) ;
if ( ( requirements . requiresViewDir & neededCoordinateSpace ) > 0 )
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "float3 {0} = IN.{0};" , space . ToVariableName ( InterpolatorType . ViewDirection ) ) , false ) ;
if ( ( requirements . requiresPosition & neededCoordinateSpace ) > 0 )
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "float3 {0} = IN.{0};" , space . ToVariableName ( InterpolatorType . Position ) ) , false ) ;
}
var slot = propertyNode . FindOutputSlot < MaterialSlot > ( PropertyNode . OutputSlotId ) ;
var newSlot = node . GetOutputSlots < MaterialSlot > ( ) . FirstOrDefault ( s = > s . valueType = = slot . valueType ) ;
if ( newSlot = = null )
return ;
node . drawState = propertyNode . drawState ;
AddNodeNoValidate ( node ) ;
if ( requirements . requiresScreenPosition )
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "float4 {0} = IN.{0};" , ShaderGeneratorNames . ScreenPosition ) , false ) ;
if ( requirements . requiresVertexColor )
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "float4 {0} = IN.{0};" , ShaderGeneratorNames . VertexColor ) , false ) ;
foreach ( var edge in this . GetEdges ( slot . slotReference ) )
ConnectNoValidate ( newSlot . slotReference , edge . inputSlot ) ;
foreach ( var channel in requirements . requiresMeshUVs . Distinct ( ) )
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "half4 {0} = IN.{0};" , channel . GetUVName ( ) ) , false ) ;
RemoveNodeNoValidate ( propertyNode ) ;
}
graph . CollectShaderProperties ( shaderProperties , mode ) ;
public void ValidateGraph ( )
{
var propertyNodes = GetNodes < PropertyNode > ( ) . Where ( n = > ! m_Properties . Any ( p = > p . guid = = n . propertyGuid ) ) . ToArray ( ) ;
foreach ( var pNode in propertyNodes )
ReplacePropertyNodeWithConcreteNodeNoValidate ( pNode ) ;
var currentId = - 1 ;
foreach ( var activeNode in activeNodeList . OfType < AbstractMaterialNode > ( ) )
//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.
foreach ( var edge in edges . ToArray ( ) )
if ( activeNode is IGeneratesFunction )
( activeNode as IGeneratesFunction ) . GenerateNodeFunction ( shaderFunctionVisitor , mode ) ;
if ( activeNode is IGeneratesBodyCode )
( activeNode as IGeneratesBodyCode ) . GenerateNodeCode ( surfaceDescriptionFunction , mode ) ;
if ( masterNode = = null & & activeNode . hasPreview )
{
var outputSlot = activeNode . GetOutputSlots < MaterialSlot > ( ) . FirstOrDefault ( ) ;
if ( outputSlot ! = null )
{
currentId + + ;
ids [ activeNode . guid ] = currentId ;
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "if ({0} == {1}) {{ surface.PreviewOutput = {2}; return surface; }}" , outputIdProperty . referenceName , currentId , ShaderGenerator . AdaptNodeOutputForPreview ( activeNode , outputSlot . id , activeNode . GetVariableNameForSlot ( outputSlot . id ) ) ) , false ) ;
}
}
activeNode . CollectShaderProperties ( shaderProperties , mode ) ;
}
var outputNode = GetNodeFromGuid ( edge . outputSlot . nodeGuid ) ;
var inputNode = GetNodeFromGuid ( edge . inputSlot . nodeGuid ) ;
if ( masterNode ! = null )
{
if ( masterNode is IMasterNode )
MaterialSlot outputSlot = null ;
MaterialSlot inputSlot = null ;
if ( outputNode ! = null & & inputNode ! = null )
var usedSlots = slots ? ? masterNode . GetInputSlots < MaterialSlot > ( ) ;
foreach ( var input in usedSlots )
{
var foundEdges = graph . GetEdges ( input . slotReference ) . ToArray ( ) ;
if ( foundEdges . Any ( ) )
{
var outputRef = foundEdges [ 0 ] . outputSlot ;
var fromNode = graph . GetNodeFromGuid < AbstractMaterialNode > ( outputRef . nodeGuid ) ;
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "surface.{0} = {1};" , AbstractMaterialNode . GetHLSLSafeName ( input . shaderOutputName ) , fromNode . GetVariableNameForSlot ( outputRef . slotId ) ) , true ) ;
}
else
{
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "surface.{0} = {1};" , AbstractMaterialNode . GetHLSLSafeName ( input . shaderOutputName ) , input . GetDefaultValue ( mode ) ) , true ) ;
}
}
outputSlot = outputNode . FindOutputSlot < MaterialSlot > ( edge . outputSlot . slotId ) ;
inputSlot = inputNode . FindInputSlot < MaterialSlot > ( edge . inputSlot . slotId ) ;
else if ( masterNode . hasPreview )
if ( outputNode = = null
| | inputNode = = null
| | outputSlot = = null
| | inputSlot = = null
| | ! outputSlot . IsCompatibleWith ( inputSlot ) )
foreach ( var slot in masterNode . GetOutputSlots < MaterialSlot > ( ) )
surfaceDescriptionFunction . AddShaderChunk ( string . Format ( "surface.{0} = {1};" , AbstractMaterialNode . GetHLSLSafeName ( slot . shaderOutputName ) , masterNode . GetVariableNameForSlot ( slot . id ) ) , true ) ;
//orphaned edge
RemoveEdgeNoValidate ( edge ) ;
surfaceDescriptionFunction . AddShaderChunk ( "return surface;" , false ) ;
surfaceDescriptionFunction . Deindent ( ) ;
surfaceDescriptionFunction . AddShaderChunk ( "}" , false ) ;
}
foreach ( var node in GetNodes < INode > ( ) )
node . ValidateNode ( ) ;
static void Visit ( List < INode > outputList , Dictionary < Guid , INode > unmarkedNodes , INode node )
{
if ( ! unmarkedNodes . ContainsKey ( node . guid ) )
return ;
foreach ( var slot in node . GetInputSlots < ISlot > ( ) )
foreach ( var edge in m_AddedEdges . ToList ( ) )
foreach ( var edge in node . owner . GetEdges ( slot . slotReference ) )
if ( ! ContainsNodeGuid ( edge . outputSlot . nodeGuid ) | | ! ContainsNodeGuid ( edge . inputSlot . nodeGuid ) )
var inputNode = node . owner . GetNodeFromGuid ( edge . outputSlot . nodeGuid ) ;
Visit ( outputList , unmarkedNodes , inputNode ) ;
Debug . LogWarningFormat ( "Added edge is invalid: {0} -> {1}\n{2}" , edge . outputSlot . nodeGuid , edge . inputSlot . nodeGuid , Environment . StackTrace ) ;
m_AddedEdges . Remove ( edge ) ;
unmarkedNodes . Remove ( node . guid ) ;
outputList . Add ( node ) ;
public string GetShader ( AbstractMaterialNode node , GenerationMode mode , string name , out List < PropertyCollector . TextureInfo > configuredTextures , out PreviewMode previewMode , out FloatShaderProperty outputIdProperty , Dictionary < Guid , int > ids = null )
public void ReplaceWith ( IGraph other )
bool isUber = node = = null ;
var vertexInputs = new ShaderGenerator ( ) ;
var vertexShader = new ShaderGenerator ( ) ;
var surfaceDescriptionFunction = new ShaderGenerator ( ) ;
var surfaceDescriptionStruct = new ShaderGenerator ( ) ;
var shaderFunctionVisitor = new ShaderGenerator ( ) ;
var surfaceInputs = new ShaderGenerator ( ) ;
surfaceInputs . AddShaderChunk ( "struct SurfaceInputs{" , false ) ;
surfaceInputs . Indent ( ) ;
var otherMg = other as AbstractMaterialGraph ;
if ( otherMg = = null )
throw new ArgumentException ( "Can only replace with another AbstractMaterialGraph" , "other" ) ;
var activeNodeList = ListPool < INode > . Get ( ) ;
if ( isUber )
using ( var removedPropertiesPooledObject = ListPool < Guid > . GetDisposable ( ) )
var unmarkedNodes = GetNodes < INode > ( ) . Where ( x = > ! ( x is IMasterNode ) ) . ToDictionary ( x = > x . guid ) ;
while ( unmarkedNodes . Any ( ) )
{
var unmarkedNode = unmarkedNodes . FirstOrDefault ( ) ;
Visit ( activeNodeList , unmarkedNodes , unmarkedNode . Value ) ;
}
var removedPropertyGuids = removedPropertiesPooledObject . value ;
foreach ( var property in m_Properties )
removedPropertyGuids . Add ( property . guid ) ;
foreach ( var propertyGuid in removedPropertyGuids )
RemoveShaderPropertyNoValidate ( propertyGuid ) ;
else
foreach ( var otherProperty in otherMg . properties )
NodeUtils . DepthFirstCollectNodesFromNode ( activeNodeList , node ) ;
if ( ! properties . Any ( p = > p . guid = = otherProperty . guid ) )
AddShaderProperty ( otherProperty ) ;
var requirements = GetRequirements ( activeNodeList ) ;
GenerateApplicationVertexInputs ( requirements , vertexInputs , 0 , 8 ) ;
ShaderGenerator . GenerateSpaceTranslationSurfaceInputs ( requirements . requiresNormal , InterpolatorType . Normal , surfaceInputs ) ;
ShaderGenerator . GenerateSpaceTranslationSurfaceInputs ( requirements . requiresTangent , InterpolatorType . Tangent , surfaceInputs ) ;
ShaderGenerator . GenerateSpaceTranslationSurfaceInputs ( requirements . requiresBitangent , InterpolatorType . BiTangent , surfaceInputs ) ;
ShaderGenerator . GenerateSpaceTranslationSurfaceInputs ( requirements . requiresViewDir , InterpolatorType . ViewDirection , surfaceInputs ) ;
ShaderGenerator . GenerateSpaceTranslationSurfaceInputs ( requirements . requiresPosition , InterpolatorType . Position , surfaceInputs ) ;
other . ValidateGraph ( ) ;
ValidateGraph ( ) ;
if ( requirements . requiresVertexColor )
surfaceInputs . AddShaderChunk ( string . Format ( "float4 {0};" , ShaderGeneratorNames . VertexColor ) , false ) ;
if ( requirements . requiresScreenPosition )
surfaceInputs . AddShaderChunk ( string . Format ( "float4 {0};" , ShaderGeneratorNames . ScreenPosition ) , false ) ;
previewMode = PreviewMode . Preview3D ;
if ( ! isUber )
// Current tactic is to remove all nodes and edges and then re-add them, such that depending systems
// will re-initialize with new references.
using ( var pooledList = ListPool < IEdge > . GetDisposable ( ) )
foreach ( var pNode in activeNodeList . OfType < AbstractMaterialNode > ( ) )
{
if ( pNode . previewMode = = PreviewMode . Preview3D )
{
previewMode = PreviewMode . Preview3D ;
break ;
}
}
var removedNodeEdges = pooledList . value ;
removedNodeEdges . AddRange ( m_Edges ) ;
foreach ( var edge in removedNodeEdges )
RemoveEdgeNoValidate ( edge ) ;
foreach ( var channel in requirements . requiresMeshUVs . Distinct ( ) )
surfaceInputs . AddShaderChunk ( string . Format ( "half4 {0};" , channel . GetUVName ( ) ) , false ) ;
surfaceInputs . Deindent ( ) ;
surfaceInputs . AddShaderChunk ( "};" , false ) ;
vertexShader . AddShaderChunk ( "GraphVertexInput PopulateVertexData(GraphVertexInput v){" , false ) ;
vertexShader . Indent ( ) ;
vertexShader . AddShaderChunk ( "return v;" , false ) ;
vertexShader . Deindent ( ) ;
vertexShader . AddShaderChunk ( "}" , false ) ;
var slots = new List < MaterialSlot > ( ) ;
foreach ( var activeNode in isUber ? activeNodeList . Where ( n = > ( ( AbstractMaterialNode ) n ) . hasPreview ) : ( ( INode ) node ) . ToEnumerable ( ) )
using ( var removedNodesPooledObject = ListPool < Guid > . GetDisposable ( ) )
if ( activeNode is IMasterNode )
slots . AddRange ( activeNode . GetInputSlots < MaterialSlot > ( ) ) ;
else
slots . AddRange ( activeNode . GetOutputSlots < MaterialSlot > ( ) ) ;
var removedNodeGuids = removedNodesPooledObject . value ;
removedNodeGuids . AddRange ( m_Nodes . Where ( n = > n ! = null ) . Select ( n = > n . guid ) ) ;
foreach ( var nodeGuid in removedNodeGuids )
RemoveNodeNoValidate ( m_NodeDictionary [ nodeGuid ] ) ;
GenerateSurfaceDescriptionStruct ( surfaceDescriptionStruct , slots , ! isUber ) ;
var shaderProperties = new PropertyCollector ( ) ;
outputIdProperty = new FloatShaderProperty
{
displayName = "OutputId" ,
generatePropertyBlock = false ,
value = - 1
} ;
if ( isUber )
shaderProperties . AddShaderProperty ( outputIdProperty ) ;
ValidateGraph ( ) ;
foreach ( var node in other . GetNodes < INode > ( ) )
AddNodeNoValidate ( node ) ;
GenerateSurfaceDescription (
activeNodeList ,
node ,
this ,
surfaceDescriptionFunction ,
shaderFunctionVisitor ,
shaderProperties ,
requirements ,
mode ,
outputIdProperty : outputIdProperty ,
ids : ids ) ;
foreach ( var edge in other . edges )
ConnectNoValidate ( edge . outputSlot , edge . inputSlot ) ;
var finalShader = new ShaderGenerator ( ) ;
finalShader . AddShaderChunk ( string . Format ( @"Shader ""{0}""" , name ) , false ) ;
finalShader . AddShaderChunk ( "{" , false ) ;
finalShader . Indent ( ) ;
ValidateGraph ( ) ;
}
finalShader . AddShaderChunk ( "Properties" , false ) ;
finalShader . AddShaderChunk ( "{" , false ) ;
finalShader . Indent ( ) ;
finalShader . AddShaderChunk ( shaderProperties . GetPropertiesBlock ( 2 ) , false ) ;
finalShader . Deindent ( ) ;
finalShader . AddShaderChunk ( "}" , false ) ;
public void OnBeforeSerialize ( )
{
m_SerializableNodes = SerializationHelper . Serialize ( GetNodes < INode > ( ) ) ;
m_SerializableEdges = SerializationHelper . Serialize < IEdge > ( m_Edges ) ;
m_SerializedProperties = SerializationHelper . Serialize < IShaderProperty > ( m_Properties ) ;
}
finalShader . AddShaderChunk ( "CGINCLUDE" , false ) ;
finalShader . AddShaderChunk ( "#include \"UnityCG.cginc\"" , false ) ;
finalShader . AddShaderChunk ( shaderFunctionVisitor . GetShaderString ( 2 ) , false ) ;
finalShader . AddShaderChunk ( vertexInputs . GetShaderString ( 2 ) , false ) ;
finalShader . AddShaderChunk ( surfaceInputs . GetShaderString ( 2 ) , false ) ;
finalShader . AddShaderChunk ( surfaceDescriptionStruct . GetShaderString ( 2 ) , false ) ;
finalShader . AddShaderChunk ( shaderProperties . GetPropertiesDeclaration ( 2 ) , false ) ;
finalShader . AddShaderChunk ( vertexShader . GetShaderString ( 2 ) , false ) ;
finalShader . AddShaderChunk ( surfaceDescriptionFunction . GetShaderString ( 2 ) , false ) ;
finalShader . AddShaderChunk ( "ENDCG" , false ) ;
public virtual void OnAfterDeserialize ( )
{
// have to deserialize 'globals' before nodes
m_Properties = SerializationHelper . Deserialize < IShaderProperty > ( m_SerializedProperties , GraphUtil . GetLegacyTypeRemapping ( ) ) ;
var nodes = SerializationHelper . Deserialize < INode > ( m_SerializableNodes , GraphUtil . GetLegacyTypeRemapping ( ) ) ;
m_Nodes = new List < AbstractMaterialNode > ( nodes . Count ) ;
m_NodeDictionary = new Dictionary < Guid , INode > ( nodes . Count ) ;
foreach ( var node in nodes . OfType < AbstractMaterialNode > ( ) )
{
node . owner = this ;
node . UpdateNodeAfterDeserialization ( ) ;
node . tempId = new Identifier ( m_Nodes . Count ) ;
m_Nodes . Add ( node ) ;
m_NodeDictionary . Add ( node . guid , node ) ;
}
finalShader . AddShaderChunk ( ShaderGenerator . GetPreviewSubShader ( node , requirements ) , false ) ;
m_SerializableNodes = null ;
ListPool < INode > . Release ( activeNodeList ) ;
m_Edges = SerializationHelper . Deserialize < IEdge > ( m_SerializableEdges , GraphUtil . GetLegacyTypeRemapping ( ) ) ;
m_SerializableEdges = null ;
foreach ( var edge in m_Edges )
AddEdgeToNodeEdges ( edge ) ;
}
finalShader . Deindent ( ) ;
finalShader . AddShaderChunk ( "}" , false ) ;
configuredTextures = shaderProperties . GetConfiguredTexutres ( ) ;
return finalShader . GetShaderString ( 0 ) ;
public void OnEnable ( )
{
foreach ( var node in GetNodes < INode > ( ) . OfType < IOnAssetEnabled > ( ) )
{
node . OnEnable ( ) ;
}
[Serializable]
public Mesh mesh ;
public SerializableMesh serializedMesh = new SerializableMesh ( ) ;
[NonSerialized]
public float scale = 1f ;
}
}