You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

204 lines
8.0 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.ShaderGraph.Internal;
using UnityEngine;
using static UnityEditor.ShaderGraph.CategoryDataCollection;
using Object = UnityEngine.Object;
namespace UnityEditor.ShaderGraph
{
[Serializable]
struct GraphInputData
{
public string referenceName;
public bool isKeyword;
public PropertyType propertyType;
public KeywordType keywordType;
public bool isCompoundProperty;
public List<SubPropertyData> subProperties;
public string tooltip;
public override bool Equals(object obj)
{
if (obj is null || obj is not GraphInputData o)
return false;
if (subProperties is null != o.subProperties is null
|| referenceName is null != o.referenceName is null
|| tooltip is null != o.tooltip is null
|| referenceName is not null && !referenceName.Equals(o.referenceName)
|| !isKeyword.Equals(o.isKeyword)
|| !propertyType.Equals(o.propertyType)
|| !keywordType.Equals(o.keywordType)
|| !isCompoundProperty.Equals(o.isCompoundProperty)
// || tooltip is not null && !tooltip.Equals(o.tooltip)
|| subProperties is not null && !subProperties.Count.Equals(o.subProperties.Count))
return false;
if (subProperties is not null)
for (int i = 0; i < subProperties.Count; ++i)
if (!subProperties[i].Equals(o.subProperties[i]))
return false;
return true;
}
public override int GetHashCode()
{
var hash = new HashCode();
hash.Add(referenceName);
hash.Add(isKeyword);
hash.Add(propertyType);
hash.Add(keywordType);
hash.Add(isCompoundProperty);
// hash.Add(tooltip); This is not part of the graph inputs identity.
if (subProperties is not null)
foreach (var subproperty in subProperties)
{
hash.Add(subproperty.referenceName);
hash.Add(subproperty.propertyType);
}
return hash.ToHashCode();
}
}
[Serializable]
struct SubPropertyData
{
public string referenceName;
public PropertyType propertyType;
}
[Serializable]
class MinimalCategoryData
{
// ShaderLab doesn't understand virtual texture inputs, they need to be processed to replace the virtual texture input with the layers that compose it instead
public static GraphInputData ProcessVirtualTextureProperty(VirtualTextureShaderProperty virtualTextureShaderProperty)
{
var layerReferenceNames = new List<string>();
virtualTextureShaderProperty.GetPropertyReferenceNames(layerReferenceNames);
var virtualTextureLayerDataList = new List<SubPropertyData>();
// Skip the first entry in this list as that's the Virtual Texture reference name itself, which won't exist in ShaderLab
foreach (var referenceName in layerReferenceNames.Skip(1))
{
var layerPropertyData = new SubPropertyData() { referenceName = referenceName, propertyType = PropertyType.Texture2D };
virtualTextureLayerDataList.Add(layerPropertyData);
}
var virtualTexturePropertyData = new GraphInputData() { referenceName = virtualTextureShaderProperty.displayName, propertyType = PropertyType.VirtualTexture, isKeyword = false };
virtualTexturePropertyData.isCompoundProperty = true;
virtualTexturePropertyData.subProperties = virtualTextureLayerDataList;
return virtualTexturePropertyData;
}
public static bool TryProcessInput(ShaderInput input, out GraphInputData data)
{
data = default;
if (!input.isExposed)
return false;
string tooltip = "";
if (input.promoteToFinalShader)
tooltip = $"from {input.PromotedAssetPath}";
switch(input)
{
case VirtualTextureShaderProperty vt:
data = ProcessVirtualTextureProperty(vt);
return true;
case AbstractShaderProperty prop:
data = new GraphInputData() { referenceName = prop.referenceName, propertyType = prop.propertyType, isKeyword = false, tooltip = tooltip };
return true;
case ShaderKeyword keyword:
var sanitizedReferenceName = keyword.referenceName;
if (keyword.keywordType == KeywordType.Boolean && keyword.referenceName.Contains("_ON"))
sanitizedReferenceName = sanitizedReferenceName.Replace("_ON", String.Empty);
data = new GraphInputData() { referenceName = sanitizedReferenceName, keywordType = keyword.keywordType, isKeyword = true, tooltip = tooltip };
return true;
default:
return false;
}
}
public string categoryName;
public List<GraphInputData> propertyDatas;
[NonSerialized]
public bool expanded = true;
}
class ShaderGraphMetadata : ScriptableObject
{
public string outputNodeTypeName;
// these asset dependencies are stored here as a way for "Export Package..." to discover them
// and automatically pull them in to the .unitypackage
public List<Object> assetDependencies;
public List<MinimalCategoryData> categoryDatas;
}
internal class CategoryDataCollection
{
HashSet<(GraphInputData input, string category)> lookup = new();
Dictionary<string, int> categoryScores = new();
Dictionary<GraphInputData, int> propertyScores = new();
internal void Set(string categoryName, GraphInputData data, int propertyScore = 0, int categoryScore = 0)
{
if (!categoryScores.TryAdd(categoryName, categoryScore) && categoryScore < categoryScores[categoryName])
categoryScores[categoryName] = categoryScore;
if (!propertyScores.TryAdd(data, propertyScore) && propertyScore < propertyScores[data])
propertyScores[data] = propertyScore;
// append the tooltip if it doesn't already exist.
if (lookup.TryGetValue((data, categoryName), out var currentEntry) && !currentEntry.input.tooltip.Contains(data.tooltip))
{
lookup.Remove((data, categoryName));
currentEntry.input.tooltip += $"\n{data.tooltip}";
lookup.Add(currentEntry);
}
else lookup.Add((data, categoryName));
}
internal List<MinimalCategoryData> GenerateMCD()
{
var result = new List<MinimalCategoryData>();
var buckets = new Dictionary<string, List<GraphInputData>>();
var order = new List<string>(categoryScores.Keys);
order.Sort((a, b) => {
var score = categoryScores[a].CompareTo(categoryScores[b]);
if (score == 0) score = a.CompareTo(b);
return score;
});
foreach(var data in lookup)
{
buckets.TryAdd(data.category, new());
buckets[data.category].Add(data.input);
}
foreach(var name in order)
{
if (!buckets.ContainsKey(name))
continue;
buckets[name].Sort((a, b) => {
var score = propertyScores[a].CompareTo(propertyScores[b]);
if (score == 0) score = a.referenceName.CompareTo(b.referenceName);
return score;
});
result.Add(new() { categoryName = name, propertyDatas = buckets[name] });
}
return result;
}
}
}