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
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;
|
|
}
|
|
}
|
|
}
|