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.
 
 
 
 

329 lines
14 KiB

using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor.ShaderGraph;
using UnityEditor.ShaderGraph.Internal;
using UnityEngine;
using UnityEngine.Rendering;
namespace UnityEditor.VFX
{
interface IVFXShaderGraphOutput
{
ShaderGraphVfxAsset GetShaderGraph();
}
static class VFXShaderGraphHelpers
{
public static string GetMissingShaderGraphErrorMessage(ShaderGraphVfxAsset shader)
{
var missingShaderPath = AssetDatabase.GetAssetPath(shader.GetEntityId());
if (!string.IsNullOrEmpty(missingShaderPath))
{
return $" cannot be compiled because a Shader Graph asset located here '{missingShaderPath}' is missing.";
}
AssetDatabase.TryGetGUIDAndLocalFileIdentifier(shader, out var guid, out var localID);
return $" cannot be compiled because a Shader Graph with GUID '{guid}' is missing.\nYou might find the missing file by searching on your disk this guid in .meta files.";
}
private static Type GetPropertyType(AbstractShaderProperty property)
{
switch (property.propertyType)
{
case PropertyType.Color:
return typeof(Color);
case PropertyType.Texture2D:
return typeof(Texture2D);
case PropertyType.Texture2DArray:
return typeof(Texture2DArray);
case PropertyType.Texture3D:
return typeof(Texture3D);
case PropertyType.Cubemap:
return typeof(Cubemap);
case PropertyType.Gradient:
return null;
case PropertyType.Boolean:
return typeof(bool);
case PropertyType.Float:
return typeof(float);
case PropertyType.Vector2:
return typeof(Vector2);
case PropertyType.Vector3:
return typeof(Vector3);
case PropertyType.Vector4:
return typeof(Vector4);
case PropertyType.Matrix2:
return null;
case PropertyType.Matrix3:
return null;
case PropertyType.Matrix4:
return typeof(Matrix4x4);
case PropertyType.SamplerState:
default:
return null;
}
}
private static object GetPropertyValue(AbstractShaderProperty property)
{
switch (property.propertyType)
{
case PropertyType.Texture2D:
return ((Texture2DShaderProperty) property).value.texture;
case PropertyType.Texture3D:
return ((Texture3DShaderProperty) property).value.texture;
case PropertyType.Cubemap:
return ((CubemapShaderProperty) property).value.cubemap;
case PropertyType.Texture2DArray:
return ((Texture2DArrayShaderProperty) property).value.textureArray;
default:
{
var type = GetPropertyType(property);
PropertyInfo info = property.GetType().GetProperty("value",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
return VFXConverter.ConvertTo(info?.GetValue(property), type);
}
}
}
static VFXExpression ConstantFromValue(object value)
{
if (value == null)
throw new NullReferenceException("ConstantFromValue NRE");
var type = value.GetType();
if (type == typeof(Color)) return VFXValue.Constant<Vector4>((Color)(value));
if (type == typeof(bool)) return VFXValue.Constant((bool)value);
if (type == typeof(float)) return VFXValue.Constant((float)value);
if (type == typeof(Vector2)) return VFXValue.Constant((Vector2)value);
if (type == typeof(Vector3)) return VFXValue.Constant((Vector3)value);
if (type == typeof(Vector4)) return VFXValue.Constant((Vector4)value);
//This function is only used for constant which can be exposed and aren't textures (see VFXSGInputs usage)
throw new InvalidOperationException("ConstantFromValue missing support for: " + type);
}
public struct Property
{
public VFXPropertyWithValue property;
public bool multiCompile;
public string[] keywordsMapping;
}
static bool IsExcludedFromSlot(AbstractShaderProperty shaderProperty)
{
return shaderProperty.hidden || !shaderProperty.isExposed;
}
static bool IsExcludedFromSlot(ShaderGraph.ShaderKeyword shaderKeyword)
{
return !shaderKeyword.isExposed;
}
public static IEnumerable<Property> GetProperties(ShaderGraphVfxAsset shaderGraph)
{
if (shaderGraph == null)
yield break;
foreach (var property in shaderGraph.properties)
{
if (property is AbstractShaderProperty shaderProperty)
{
if (IsExcludedFromSlot(shaderProperty))
continue;
var type = GetPropertyType(shaderProperty);
if (type == null)
continue;
var current = new Property()
{
keywordsMapping = null
};
if (shaderProperty.propertyType == PropertyType.Float)
{
if (property is Vector1ShaderProperty prop)
{
if (prop.floatType == FloatType.Slider)
current.property = new VFXPropertyWithValue(
new VFXProperty(type, property.referenceName,
new RangeAttribute(prop.rangeValues.x, prop.rangeValues.y)),
GetPropertyValue(shaderProperty));
else if (prop.floatType == FloatType.Integer)
current.property = new VFXPropertyWithValue(
new VFXProperty(typeof(int), property.referenceName),
VFXConverter.ConvertTo(GetPropertyValue(shaderProperty), typeof(int)));
else
current.property = new VFXPropertyWithValue(new VFXProperty(type, property.referenceName),
GetPropertyValue(shaderProperty));
}
else
{
//it could be a diffusion profile in HDRP.
continue;
}
}
else
current.property = new VFXPropertyWithValue(new VFXProperty(type, property.referenceName),
GetPropertyValue(shaderProperty));
yield return current;
}
else if (property is ShaderGraph.ShaderKeyword shaderKeyword)
{
if (IsExcludedFromSlot(shaderKeyword))
continue;
if (shaderKeyword.keywordType == KeywordType.Boolean)
{
yield return new Property()
{
property = new VFXPropertyWithValue(
new VFXProperty(typeof(bool), shaderKeyword.displayName), shaderKeyword.value != 0),
multiCompile = shaderKeyword.keywordDefinition == KeywordDefinition.MultiCompile,
keywordsMapping = new[] { shaderKeyword.referenceName }
};
}
else if (shaderKeyword.keywordType == KeywordType.Enum)
{
var keywordsMapping = new string[shaderKeyword.entries.Count];
var enumNames = new string[shaderKeyword.entries.Count];
for (int index = 0; index < shaderKeyword.entries.Count; ++index)
{
keywordsMapping[index] = shaderKeyword.referenceName + "_" + shaderKeyword.entries[index].referenceName;
enumNames[index] = shaderKeyword.entries[index].displayName;
}
yield return new Property
{
property = new VFXPropertyWithValue(
new VFXProperty(typeof(uint), shaderKeyword.displayName, new VFXPropertyAttributes(new EnumAttribute(enumNames))), (uint)shaderKeyword.value),
multiCompile = shaderKeyword.keywordDefinition == KeywordDefinition.MultiCompile,
keywordsMapping = keywordsMapping
};
}
else
{
throw new InvalidOperationException("Unsupported keyword type: " + shaderKeyword.keywordType);
}
}
else
{
throw new InvalidOperationException("Unsupported property type: " + property);
}
}
}
public static IEnumerable<string> GetTextureOnlyUsedInternally(ShaderGraphVfxAsset shaderGraph)
{
foreach (var tex in shaderGraph.textureInfos)
yield return tex.name;
foreach (var property in shaderGraph.properties)
{
if (property is AbstractShaderProperty shaderProperty)
{
if (IsExcludedFromSlot(shaderProperty))
{
var type = GetPropertyType(shaderProperty);
if (type == null)
continue;
if (typeof(Texture).IsAssignableFrom(type))
yield return property.referenceName;
}
}
}
}
static VFXExpression GetConstantTextureExpression(Texture texture, TextureDimension dimension)
{
var entityID = texture != null ? texture.GetEntityId() : EntityId.None;
switch (dimension)
{
case TextureDimension.Tex2D: return new VFXTexture2DValue(entityID, VFXValue.Mode.Variable);
case TextureDimension.Tex3D: return new VFXTexture3DValue(entityID, VFXValue.Mode.Variable);
case TextureDimension.Cube: return new VFXTextureCubeValue(entityID, VFXValue.Mode.Variable);
case TextureDimension.Tex2DArray: return new VFXTexture2DArrayValue(entityID, VFXValue.Mode.Variable);
case TextureDimension.CubeArray: return new VFXTextureCubeArrayValue(entityID, VFXValue.Mode.Variable);
}
throw new NotImplementedException("Unexpected dimension: " + dimension);
}
public static IEnumerable<VFXNamedExpression> GetTextureConstant(ShaderGraphVfxAsset shaderGraph)
{
foreach (var tex in shaderGraph.textureInfos)
yield return new VFXNamedExpression(GetConstantTextureExpression(tex.texture, tex.dimension), tex.name);
foreach (var property in shaderGraph.properties)
{
if (property is AbstractShaderProperty shaderProperty)
{
if (IsExcludedFromSlot(shaderProperty))
{
var dimension = GetDimension(shaderProperty.propertyType);
if (dimension == TextureDimension.Unknown)
continue;
var texture = (Texture)GetPropertyValue(shaderProperty); //Can be null
yield return new VFXNamedExpression(GetConstantTextureExpression(texture, dimension), shaderProperty.referenceName);
}
}
}
}
static TextureDimension GetDimension(PropertyType type)
{
switch (type)
{
case PropertyType.Texture2D: return TextureDimension.Tex2D;
case PropertyType.Texture2DArray: return TextureDimension.Tex2DArray;
case PropertyType.Texture3D: return TextureDimension.Tex3D;
case PropertyType.Cubemap: return TextureDimension.Cube;
}
return TextureDimension.Unknown;
}
public static bool IsTexture(PropertyType type)
{
return GetDimension(type) != TextureDimension.Unknown;
}
public static ShaderGraphVfxAsset GetShaderGraph(VFXContext context)
{
if (context is IVFXShaderGraphOutput shaderGraphOutput)
return shaderGraphOutput.GetShaderGraph();
return null;
}
public static void GetShaderGraphParameters(ShaderGraphVfxAsset shaderGraph, out List<(string name, ShaderStageCapability shaderStage, bool exposed, VFXExpression defaultValue)> parameters)
{
parameters = new();
var properties = shaderGraph.properties;
for (var propertyIndex = 0; propertyIndex < properties.Count; ++propertyIndex)
{
var param = properties[propertyIndex];
if (param is AbstractShaderProperty property
&& !IsTexture(property.propertyType))
{
var propertyStage = shaderGraph.GetPropertyStage(propertyIndex);
if (propertyStage != ShaderStageCapability.None)
{
VFXExpression exp = null;
if (!param.isExposed)
{
var value = GetPropertyValue(property);
exp = ConstantFromValue(value);
} //else, expression will be provided by VFXSlot
parameters.Add((param.referenceName, propertyStage, param.isExposed, exp));
}
}
}
}
}
}