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.
287 lines
12 KiB
287 lines
12 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Serialization;
|
|
using UnityEngine.VFX;
|
|
|
|
namespace UnityEditor.VFX
|
|
{
|
|
[VFXInfo(name = "Output Mesh", category = "Output")]
|
|
class VFXStaticMeshOutput : VFXContext, IVFXSubRenderer
|
|
{
|
|
[VFXSetting, Tooltip("Specifies the shader with which the mesh output is rendered.")]
|
|
private Shader shader; // not serialized here but in VFXDataMesh
|
|
|
|
[VFXSetting(VFXSettingAttribute.VisibleFlags.None), FormerlySerializedAs("sortPriority"), SerializeField, Header("Rendering Options")]
|
|
protected int vfxSystemSortPriority = 0;
|
|
|
|
[VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector), SerializeField, Tooltip("When enabled, the mesh output will cast shadows.")]
|
|
protected bool castShadows = false;
|
|
|
|
private bool m_IsShaderGraphMissing;
|
|
|
|
// IVFXSubRenderer interface
|
|
// TODO Could we derive this directly by looking at the shader to know if a shadow pass is present?
|
|
public virtual bool hasShadowCasting { get { return castShadows; } }
|
|
|
|
public virtual bool hasMotionVector { get { return false; } } //TODO
|
|
|
|
public virtual bool isRayTraced { get { return false; } } //TODO : Handle static mesh for raytracing
|
|
|
|
int IVFXSubRenderer.vfxSystemSortPriority
|
|
{
|
|
get
|
|
{
|
|
return vfxSystemSortPriority;
|
|
}
|
|
set
|
|
{
|
|
if (vfxSystemSortPriority != value)
|
|
{
|
|
vfxSystemSortPriority = value;
|
|
Invalidate(InvalidationCause.kSettingChanged);
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual void SetupMaterial(Material material)
|
|
{
|
|
VFXLibrary.currentSRPBinder.SetupMaterial(material, false, hasShadowCasting);
|
|
}
|
|
|
|
protected VFXStaticMeshOutput() : base(VFXContextType.Output, VFXDataType.Mesh, VFXDataType.None) { }
|
|
|
|
public override VFXSpace GetOutputSpaceFromSlot(VFXSlot slot)
|
|
{
|
|
return VFXSpace.Local;
|
|
}
|
|
|
|
private Shader GetOrRefreshShaderGraphObject(bool refreshErrors = true)
|
|
{
|
|
var wasShaderGraphMissing = m_IsShaderGraphMissing;
|
|
var meshShader = ((VFXDataMesh)GetData()).shader;
|
|
//This is the only place where shader property is updated or read
|
|
if (meshShader == null && !object.ReferenceEquals(meshShader, null) && meshShader.GetInstanceID() != 0)
|
|
{
|
|
var assetPath = AssetDatabase.GetAssetPath(meshShader.GetInstanceID());
|
|
|
|
var newShader = AssetDatabase.LoadAssetAtPath<Shader>(assetPath);
|
|
m_IsShaderGraphMissing = newShader == null;
|
|
|
|
shader = !m_IsShaderGraphMissing ? newShader : meshShader;
|
|
}
|
|
else
|
|
{
|
|
m_IsShaderGraphMissing = false;
|
|
shader = meshShader;
|
|
}
|
|
|
|
if (refreshErrors && wasShaderGraphMissing != m_IsShaderGraphMissing)
|
|
{
|
|
RefreshErrors();
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
public override bool CanBeCompiled()
|
|
{
|
|
GetOrRefreshShaderGraphObject();
|
|
return !m_IsShaderGraphMissing && base.CanBeCompiled();
|
|
}
|
|
|
|
protected override void OnInvalidate(VFXModel model, VFXModel.InvalidationCause cause)
|
|
{
|
|
if (model == this && cause == VFXModel.InvalidationCause.kSettingChanged)
|
|
{
|
|
var data = (VFXDataMesh)GetData();
|
|
data.shader = shader;
|
|
}
|
|
|
|
base.OnInvalidate(model, cause);
|
|
}
|
|
|
|
public override void GetImportDependentAssets(HashSet<int> dependencies)
|
|
{
|
|
base.GetImportDependentAssets(dependencies);
|
|
if (!object.ReferenceEquals(shader, null))
|
|
{
|
|
dependencies.Add(shader.GetInstanceID());
|
|
}
|
|
}
|
|
|
|
protected override IEnumerable<VFXPropertyWithValue> inputProperties
|
|
{
|
|
get
|
|
{
|
|
yield return new VFXPropertyWithValue(new VFXProperty(typeof(Mesh), "mesh"), VFXResources.defaultResources.mesh);
|
|
yield return new VFXPropertyWithValue(new VFXProperty(typeof(Transform), "transform"), Transform.defaultValue);
|
|
yield return new VFXPropertyWithValue(new VFXProperty(typeof(uint), "subMeshMask", new BitFieldAttribute()), uint.MaxValue);
|
|
|
|
if (GetData() != null)
|
|
{
|
|
Shader copyShader = GetOrRefreshShaderGraphObject();
|
|
|
|
if (copyShader != null)
|
|
{
|
|
var mat = ((VFXDataMesh)GetData()).GetOrCreateMaterial();
|
|
var propertyAttribs = new List<object>(1);
|
|
for (int i = 0; i < ShaderUtil.GetPropertyCount(copyShader); ++i)
|
|
{
|
|
if (ShaderUtil.IsShaderPropertyHidden(copyShader, i) || ShaderUtil.IsShaderPropertyNonModifiableTexureProperty(copyShader, i))
|
|
continue;
|
|
|
|
Type propertyType = null;
|
|
object propertyValue = null;
|
|
|
|
var propertyName = ShaderUtil.GetPropertyName(copyShader, i);
|
|
var propertyNameId = Shader.PropertyToID(propertyName);
|
|
|
|
propertyAttribs.Clear();
|
|
|
|
switch (ShaderUtil.GetPropertyType(copyShader, i))
|
|
{
|
|
case ShaderUtil.ShaderPropertyType.Color:
|
|
propertyType = typeof(Color);
|
|
propertyValue = mat.GetColor(propertyNameId);
|
|
break;
|
|
case ShaderUtil.ShaderPropertyType.Vector:
|
|
propertyType = typeof(Vector4);
|
|
propertyValue = mat.GetVector(propertyNameId);
|
|
break;
|
|
case ShaderUtil.ShaderPropertyType.Float:
|
|
propertyType = typeof(float);
|
|
propertyValue = mat.GetFloat(propertyNameId);
|
|
break;
|
|
case ShaderUtil.ShaderPropertyType.Range:
|
|
propertyType = typeof(float);
|
|
float minRange = ShaderUtil.GetRangeLimits(copyShader, i, 1);
|
|
float maxRange = ShaderUtil.GetRangeLimits(copyShader, i, 2);
|
|
propertyAttribs.Add(new RangeAttribute(minRange, maxRange));
|
|
propertyValue = mat.GetFloat(propertyNameId);
|
|
break;
|
|
case ShaderUtil.ShaderPropertyType.TexEnv:
|
|
{
|
|
switch (ShaderUtil.GetTexDim(copyShader, i))
|
|
{
|
|
case TextureDimension.Tex2D:
|
|
propertyType = typeof(Texture2D);
|
|
break;
|
|
case TextureDimension.Tex3D:
|
|
propertyType = typeof(Texture3D);
|
|
break;
|
|
case TextureDimension.Cube:
|
|
propertyType = typeof(Cubemap);
|
|
break;
|
|
case TextureDimension.Tex2DArray:
|
|
propertyType = typeof(Texture2DArray);
|
|
break;
|
|
case TextureDimension.CubeArray:
|
|
propertyType = typeof(CubemapArray);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
propertyValue = mat.GetTexture(propertyNameId);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (propertyType != null)
|
|
{
|
|
propertyAttribs.Add(new TooltipAttribute(ShaderUtil.GetPropertyDescription(copyShader, i)));
|
|
yield return new VFXPropertyWithValue(new VFXProperty(propertyType, propertyName, propertyAttribs.ToArray()), propertyValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override string name { get { return "Output Mesh"; } }
|
|
public override string codeGeneratorTemplate { get { return null; } }
|
|
public override VFXTaskType taskType { get { return VFXTaskType.Output; } }
|
|
|
|
public override VFXExpressionMapper GetExpressionMapper(VFXDeviceTarget target)
|
|
{
|
|
var meshData = (VFXDataMesh)GetData();
|
|
|
|
switch (target)
|
|
{
|
|
case VFXDeviceTarget.GPU:
|
|
{
|
|
var mapper = new VFXExpressionMapper();
|
|
for (int i = 2; i < GetNbInputSlots(); ++i)
|
|
{
|
|
VFXExpression exp = GetInputSlot(i).GetExpression();
|
|
VFXProperty prop = GetInputSlot(i).property;
|
|
|
|
// As there's not shader generation here, we need expressions that can be evaluated on CPU
|
|
if (exp.IsAny(VFXExpression.Flags.NotCompilableOnCPU))
|
|
throw new InvalidOperationException(string.Format("Expression for slot {0} must be evaluable on CPU: {1}", prop.name, exp));
|
|
|
|
// needs to convert to srgb as color are linear in vfx graph
|
|
// This should not be performed for colors with the attribute [HDR] and be performed for vector4 with the attribute [Gamma]
|
|
// But property attributes cannot seem to be accessible from C# :(
|
|
if (prop.type == typeof(Color))
|
|
exp = VFXOperatorUtility.LinearToGamma(exp);
|
|
|
|
mapper.AddExpression(exp, prop.name, -1);
|
|
}
|
|
return mapper;
|
|
}
|
|
|
|
case VFXDeviceTarget.CPU:
|
|
{
|
|
var mapper = new VFXExpressionMapper();
|
|
mapper.AddExpression(GetInputSlot(0).GetExpression(), "mesh", -1);
|
|
mapper.AddExpression(GetInputSlot(1).GetExpression(), "transform", -1);
|
|
mapper.AddExpression(GetInputSlot(2).GetExpression(), "subMeshMask", -1);
|
|
|
|
return mapper;
|
|
}
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public override IEnumerable<VFXMapping> additionalMappings
|
|
{
|
|
get
|
|
{
|
|
yield return new VFXMapping("sortPriority", vfxSystemSortPriority);
|
|
yield return new VFXMapping("castShadows", castShadows ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
// Do not resync slots when shader graph is missing to keep potential links to the shader properties
|
|
public override bool ResyncSlots(bool notify) => !m_IsShaderGraphMissing && base.ResyncSlots(notify);
|
|
|
|
public override void CheckGraphBeforeImport()
|
|
{
|
|
base.CheckGraphBeforeImport();
|
|
// If the graph is reimported it can be because one of its dependency such as the shadergraphs, has been changed.
|
|
if (!VFXGraph.explicitCompile)
|
|
ResyncSlots(true);
|
|
|
|
Invalidate(InvalidationCause.kUIChangedTransient);
|
|
}
|
|
|
|
internal override void GenerateErrors(VFXErrorReporter report)
|
|
{
|
|
base.GenerateErrors(report);
|
|
|
|
GetOrRefreshShaderGraphObject(false);
|
|
if (m_IsShaderGraphMissing)
|
|
{
|
|
report.RegisterError("ErrorMissingShaderGraph", VFXErrorType.Error, "The VFX Graph cannot be compiled because the Shader Graph asset is missing.", this);
|
|
}
|
|
}
|
|
}
|
|
}
|