494 lines
24 KiB
C#
494 lines
24 KiB
C#
// using System;
|
|
using System.Collections.Generic;
|
|
using UnityEditor.ShaderGraph.Internal;
|
|
|
|
namespace UnityEditor.ShaderGraph
|
|
{
|
|
enum ConversionType
|
|
{
|
|
Position,
|
|
Direction,
|
|
Normal
|
|
}
|
|
|
|
internal struct SpaceTransform
|
|
{
|
|
public CoordinateSpace from;
|
|
public CoordinateSpace to;
|
|
public ConversionType type;
|
|
public bool normalize;
|
|
public int version;
|
|
|
|
public const int kLatestVersion = 2;
|
|
|
|
public SpaceTransform(CoordinateSpace from, CoordinateSpace to, ConversionType type, bool normalize = false, int version = kLatestVersion)
|
|
{
|
|
this.from = from;
|
|
this.to = to;
|
|
this.type = type;
|
|
this.normalize = normalize;
|
|
this.version = version;
|
|
}
|
|
|
|
internal string NormalizeString()
|
|
{
|
|
return normalize ? "true" : "false";
|
|
}
|
|
|
|
// non-identity transforms involving tangent space require the full tangent frame
|
|
public NeededCoordinateSpace RequiresNormal =>
|
|
(((from == CoordinateSpace.Tangent) || (to == CoordinateSpace.Tangent)) && (from != to)) ?
|
|
NeededCoordinateSpace.World : NeededCoordinateSpace.None;
|
|
public NeededCoordinateSpace RequiresTangent =>
|
|
(((from == CoordinateSpace.Tangent) || (to == CoordinateSpace.Tangent)) && (from != to)) ?
|
|
NeededCoordinateSpace.World : NeededCoordinateSpace.None;
|
|
public NeededCoordinateSpace RequiresBitangent =>
|
|
(((from == CoordinateSpace.Tangent) || (to == CoordinateSpace.Tangent)) && (from != to)) ?
|
|
NeededCoordinateSpace.World : NeededCoordinateSpace.None;
|
|
|
|
// non-identity position transforms involving tangent space require the world position
|
|
public NeededCoordinateSpace RequiresPosition =>
|
|
((type == ConversionType.Position) && ((from == CoordinateSpace.Tangent) || (to == CoordinateSpace.Tangent)) && (from != to)) ?
|
|
NeededCoordinateSpace.World : NeededCoordinateSpace.None;
|
|
|
|
public IEnumerable<NeededTransform> RequiresTransform =>
|
|
SpaceTransformUtil.ComputeTransformRequirement(this);
|
|
};
|
|
|
|
static class SpaceTransformUtil
|
|
{
|
|
delegate void TransformFunction(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb);
|
|
|
|
public static string GenerateTangentTransform(ShaderStringBuilder sb, CoordinateSpace tangentTransformSpace)
|
|
{
|
|
sb.AddLine("$precision3x3 tangentTransform = $precision3x3(IN.",
|
|
tangentTransformSpace.ToString(), "SpaceTangent, IN.",
|
|
tangentTransformSpace.ToString(), "SpaceBiTangent, IN.",
|
|
tangentTransformSpace.ToString(), "SpaceNormal);");
|
|
return "tangentTransform";
|
|
}
|
|
|
|
public static void Identity(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
// identity didn't normalize before version 2
|
|
if ((xform.version > 1) && xform.normalize && (xform.type != ConversionType.Position))
|
|
sb.AddLine(outputVariable, " = SafeNormalize(", inputValue, ");");
|
|
else
|
|
sb.AddLine(outputVariable, " = ", inputValue, ";");
|
|
}
|
|
|
|
private static void ViaWorld(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
// should never be calling this if one of the spaces is already world space (silly, and could lead to infinite recursions)
|
|
if ((xform.from == CoordinateSpace.World) || (xform.to == CoordinateSpace.World))
|
|
return;
|
|
|
|
// this breaks the transform into two parts: (from->world) and (world->to)
|
|
var toWorld = new SpaceTransform()
|
|
{
|
|
from = xform.from,
|
|
to = CoordinateSpace.World,
|
|
type = xform.type,
|
|
normalize = false,
|
|
version = xform.version
|
|
};
|
|
|
|
var fromWorld = new SpaceTransform()
|
|
{
|
|
from = CoordinateSpace.World,
|
|
to = xform.to,
|
|
type = xform.type,
|
|
normalize = xform.normalize,
|
|
version = xform.version
|
|
};
|
|
|
|
// Apply Versioning Hacks to match old (incorrect) versions
|
|
if (xform.version <= 1)
|
|
{
|
|
if (xform.type == ConversionType.Direction)
|
|
{
|
|
switch (xform.from)
|
|
{
|
|
case CoordinateSpace.AbsoluteWorld:
|
|
if ((xform.to == CoordinateSpace.Object) || (xform.to == CoordinateSpace.View))
|
|
{
|
|
// these transforms were wrong in v0, but correct in v1, so here we
|
|
// pretend it is a later version to disable the v1 versioning in the AbsWorldToWorld transform
|
|
if (xform.version == 1)
|
|
toWorld.version = 2;
|
|
}
|
|
break;
|
|
case CoordinateSpace.View:
|
|
if ((xform.to == CoordinateSpace.Tangent) || (xform.to == CoordinateSpace.AbsoluteWorld))
|
|
{
|
|
// these transforms erroneously used the position view-to-world transform
|
|
toWorld.type = ConversionType.Position;
|
|
}
|
|
break;
|
|
case CoordinateSpace.Tangent:
|
|
if ((xform.to == CoordinateSpace.Object) || (xform.to == CoordinateSpace.View) || (xform.to == CoordinateSpace.AbsoluteWorld))
|
|
{
|
|
// manually version to 2, to remove normalization (while keeping Normal type)
|
|
toWorld.type = ConversionType.Normal;
|
|
toWorld.version = 2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
using (sb.BlockScope())
|
|
{
|
|
sb.AddLine("// Converting ", xform.type.ToString(), " from ", xform.from.ToString(), " to ", xform.to.ToString(), " via world space");
|
|
sb.AddLine("float3 world;");
|
|
GenerateTransformCodeStatement(toWorld, inputValue, "world", sb);
|
|
GenerateTransformCodeStatement(fromWorld, "world", outputVariable, sb);
|
|
}
|
|
}
|
|
|
|
public static void WorldToObject(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
sb.AddLine(outputVariable, " = TransformWorldToObject(", inputValue, ");");
|
|
break;
|
|
case ConversionType.Direction:
|
|
if (xform.version <= 1)
|
|
xform.normalize = true;
|
|
sb.AddLine(outputVariable, " = TransformWorldToObjectDir(", inputValue, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
case ConversionType.Normal:
|
|
sb.AddLine(outputVariable, " = TransformWorldToObjectNormal(", inputValue, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static void WorldToTangent(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
if (xform.version <= 1)
|
|
{
|
|
// prior to version 2, all transform were normalized, and all transforms were Normal transforms
|
|
xform.normalize = true;
|
|
xform.type = ConversionType.Normal;
|
|
}
|
|
|
|
using (sb.BlockScope())
|
|
{
|
|
string tangentTransform = GenerateTangentTransform(sb, xform.from);
|
|
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
sb.AddLine(outputVariable, " = TransformWorldToTangentDir(", inputValue, " - IN.WorldSpacePosition, ", tangentTransform, ", false);");
|
|
break;
|
|
case ConversionType.Direction:
|
|
sb.AddLine(outputVariable, " = TransformWorldToTangentDir(", inputValue, ", ", tangentTransform, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
case ConversionType.Normal:
|
|
sb.AddLine(outputVariable, " = TransformWorldToTangent(", inputValue, ", ", tangentTransform, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void WorldToView(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
sb.AddLine(outputVariable, " = TransformWorldToView(", inputValue, ");");
|
|
break;
|
|
case ConversionType.Direction:
|
|
if (xform.version <= 1)
|
|
xform.normalize = false;
|
|
sb.AddLine(outputVariable, " = TransformWorldToViewDir(", inputValue, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
case ConversionType.Normal:
|
|
sb.AddLine(outputVariable, " = TransformWorldToViewNormal(", inputValue, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static void WorldToAbsoluteWorld(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
// prior to version 2 always used Position transform
|
|
if (xform.version <= 1)
|
|
xform.type = ConversionType.Position;
|
|
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
sb.AddLine(outputVariable, " = GetAbsolutePositionWS(", inputValue, ");");
|
|
break;
|
|
case ConversionType.Direction:
|
|
case ConversionType.Normal:
|
|
// both normal and direction are unchanged
|
|
if (xform.normalize)
|
|
sb.AddLine(outputVariable, " = SafeNormalize(", inputValue, ");");
|
|
else
|
|
sb.AddLine(outputVariable, " = ", inputValue, ";");
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static void ObjectToWorld(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
sb.AddLine(outputVariable, " = TransformObjectToWorld(", inputValue, ");");
|
|
break;
|
|
case ConversionType.Direction:
|
|
if (xform.version <= 1)
|
|
xform.normalize = true;
|
|
sb.AddLine(outputVariable, " = TransformObjectToWorldDir(", inputValue, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
case ConversionType.Normal:
|
|
sb.AddLine(outputVariable, " = TransformObjectToWorldNormal(", inputValue, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static void ObjectToAbsoluteWorld(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
ViaWorld(xform, inputValue, outputVariable, sb);
|
|
break;
|
|
case ConversionType.Direction:
|
|
if (xform.version <= 1)
|
|
xform.normalize = true;
|
|
sb.AddLine(outputVariable, " = TransformObjectToWorldDir(", inputValue, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
case ConversionType.Normal:
|
|
sb.AddLine(outputVariable, " = TransformObjectToWorldNormal(", inputValue, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static void TangentToWorld(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
// prior to version 2 all transforms were Normal, and directional transforms were normalized
|
|
if (xform.version <= 1)
|
|
{
|
|
if (xform.type != ConversionType.Position)
|
|
xform.normalize = true;
|
|
xform.type = ConversionType.Normal;
|
|
}
|
|
|
|
using (sb.BlockScope())
|
|
{
|
|
string tangentTransform = GenerateTangentTransform(sb, CoordinateSpace.World);
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
sb.AddLine(outputVariable, " = TransformTangentToWorldDir(", inputValue, ", ", tangentTransform, ", false).xyz + IN.WorldSpacePosition;");
|
|
break;
|
|
case ConversionType.Direction:
|
|
sb.AddLine(outputVariable, " = TransformTangentToWorldDir(", inputValue, ", ", tangentTransform, ", ", xform.NormalizeString(), ").xyz;");
|
|
break;
|
|
case ConversionType.Normal:
|
|
sb.AddLine(outputVariable, " = TransformTangentToWorld(", inputValue, ", ", tangentTransform, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void ViewToWorld(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
sb.AddLine(outputVariable, " = TransformViewToWorld(", inputValue, ");");
|
|
break;
|
|
case ConversionType.Direction:
|
|
if (xform.version <= 1)
|
|
xform.normalize = false;
|
|
sb.AddLine(outputVariable, " = TransformViewToWorldDir(", inputValue, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
case ConversionType.Normal:
|
|
sb.AddLine(outputVariable, " = TransformViewToWorldNormal(", inputValue, ", ", xform.NormalizeString(), ");");
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static void AbsoluteWorldToWorld(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
// prior to version 2, always used position transform
|
|
if (xform.version <= 1)
|
|
xform.type = ConversionType.Position;
|
|
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
sb.AddLine(outputVariable, " = GetCameraRelativePositionWS(", inputValue, ");");
|
|
break;
|
|
case ConversionType.Direction:
|
|
case ConversionType.Normal:
|
|
// both normal and direction are unchanged
|
|
if (xform.normalize)
|
|
sb.AddLine(outputVariable, " = SafeNormalize(", inputValue, ");");
|
|
else
|
|
sb.AddLine(outputVariable, " = ", inputValue, ";");
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static void ObjectToHScreen(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
using (sb.BlockScope())
|
|
{
|
|
sb.AddLine("$precision4 ", outputVariable, "_value = TransformObjectToHClip(", inputValue, ");");
|
|
sb.AddLine("$precision3 ", outputVariable, "_uv = ", outputVariable, "_value.xyz / ", outputVariable, "_value.w;");
|
|
sb.AddLine("#if UNITY_UV_STARTS_AT_TOP");
|
|
sb.AddLine(outputVariable, "_uv.y = -", outputVariable, "_uv.y;");
|
|
sb.AddLine("#endif");
|
|
sb.AddLine(outputVariable, "_uv.xy = ", outputVariable, "_uv.xy * 0.5 + 0.5;");
|
|
sb.AddLine(outputVariable, " = ", outputVariable, "_uv;");
|
|
}
|
|
break;
|
|
case ConversionType.Direction:
|
|
case ConversionType.Normal:
|
|
ViaWorld(xform, inputValue, outputVariable, sb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static void ViewToHScreen(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
sb.AddLine("$precision4 ", outputVariable, "_value = TransformWViewToHClip(", inputValue, ");");
|
|
sb.AddLine("$precision3 ", outputVariable, "_uv = ", outputVariable, "_value.xyz / ", outputVariable, "_value.w;");
|
|
sb.AddLine("#if UNITY_UV_STARTS_AT_TOP");
|
|
sb.AddLine(outputVariable, "_uv.y = -", outputVariable, "_uv.y;");
|
|
sb.AddLine("#endif");
|
|
sb.AddLine(outputVariable, "_uv.xy = ", outputVariable, "_uv.xy * 0.5 + 0.5;");
|
|
sb.AddLine(outputVariable, " = ", outputVariable, "_uv;");
|
|
break;
|
|
case ConversionType.Direction:
|
|
case ConversionType.Normal:
|
|
ViaWorld(xform, inputValue, outputVariable, sb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static void WorldToHScreen(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
sb.AddLine("$precision4 ", outputVariable, "_value = TransformWorldToHClip(", inputValue, ");");
|
|
break;
|
|
case ConversionType.Direction:
|
|
if (xform.version <= 1)
|
|
xform.normalize = true;
|
|
sb.AddLine("$precision4 ", outputVariable, "_value = $precision4(TransformWorldToHClipDir(", inputValue, ", ", xform.NormalizeString(), "), 1.0f);");
|
|
break;
|
|
case ConversionType.Normal:
|
|
sb.AddLine("$precision4 ", outputVariable, "_value = $precision4(TransformWorldToHClipDir(", inputValue, ", ", xform.NormalizeString(), "), 1.0f);");
|
|
break;
|
|
}
|
|
sb.AddLine("$precision3 ", outputVariable, "_uv = ", outputVariable, "_value.xyz / ", outputVariable, "_value.w;");
|
|
sb.AddLine("#if UNITY_UV_STARTS_AT_TOP");
|
|
sb.AddLine(outputVariable, "_uv.y = -", outputVariable, "_uv.y;");
|
|
sb.AddLine("#endif");
|
|
sb.AddLine(outputVariable, "_uv.xy = ", outputVariable, "_uv.xy * 0.5 + 0.5;");
|
|
sb.AddLine(outputVariable, " = ", outputVariable, "_uv;");
|
|
}
|
|
|
|
public static void HScreenToWorld(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
switch (xform.type)
|
|
{
|
|
case ConversionType.Position:
|
|
sb.AddLine(outputVariable, " = ComputeWorldSpacePosition(", inputValue, ".xy, ", inputValue, ".z, UNITY_MATRIX_I_VP);");
|
|
break;
|
|
case ConversionType.Direction:
|
|
case ConversionType.Normal:
|
|
sb.AddLine("$precision4 ", outputVariable, "_CS = ComputeClipSpacePosition(", inputValue, ".xy, ", inputValue, ".z);");
|
|
sb.AddLine(outputVariable, " = mul(UNITY_MATRIX_I_VP, $precision4(", outputVariable, "_CS.xyz, 0.0f)).xyz;");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static readonly TransformFunction[,] k_TransformFunctions = new TransformFunction[6, 6] // [from, to]
|
|
{
|
|
{ // from CoordinateSpace.Object
|
|
Identity, // to CoordinateSpace.Object
|
|
ViaWorld, // to CoordinateSpace.View
|
|
ObjectToWorld, // to CoordinateSpace.World
|
|
ViaWorld, // to CoordinateSpace.Tangent
|
|
ObjectToAbsoluteWorld, // to CoordinateSpace.AbsoluteWorld
|
|
ObjectToHScreen // to CoordinateSpace.Screen
|
|
},
|
|
{ // from CoordinateSpace.View
|
|
ViaWorld, // to CoordinateSpace.Object
|
|
Identity, // to CoordinateSpace.View
|
|
ViewToWorld, // to CoordinateSpace.World
|
|
ViaWorld, // to CoordinateSpace.Tangent
|
|
ViaWorld, // to CoordinateSpace.AbsoluteWorld
|
|
ViewToHScreen // to CoordinateSpace.Screen
|
|
},
|
|
{ // from CoordinateSpace.World
|
|
WorldToObject, // to CoordinateSpace.Object
|
|
WorldToView, // to CoordinateSpace.View
|
|
Identity, // to CoordinateSpace.World
|
|
WorldToTangent, // to CoordinateSpace.Tangent
|
|
WorldToAbsoluteWorld, // to CoordinateSpace.AbsoluteWorld
|
|
WorldToHScreen // to CoordinateSpace.Screen
|
|
},
|
|
{ // from CoordinateSpace.Tangent
|
|
ViaWorld, // to CoordinateSpace.Object
|
|
ViaWorld, // to CoordinateSpace.View
|
|
TangentToWorld, // to CoordinateSpace.World
|
|
Identity, // to CoordinateSpace.Tangent
|
|
ViaWorld, // to CoordinateSpace.AbsoluteWorld
|
|
ViaWorld // to CoordinateSpace.Screen
|
|
},
|
|
{ // from CoordinateSpace.AbsoluteWorld
|
|
ViaWorld, // to CoordinateSpace.Object
|
|
ViaWorld, // to CoordinateSpace.View
|
|
AbsoluteWorldToWorld, // to CoordinateSpace.World
|
|
ViaWorld, // to CoordinateSpace.Tangent
|
|
Identity, // to CoordinateSpace.AbsoluteWorld
|
|
ViaWorld // to CoordinateSpace.Screen
|
|
},
|
|
{ // from CoordinateSpace.Screen
|
|
ViaWorld, // to CoordinateSpace.Object
|
|
ViaWorld, // to CoordinateSpace.View
|
|
HScreenToWorld, // to CoordinateSpace.World
|
|
ViaWorld, // to CoordinateSpace.Tangent
|
|
ViaWorld, // to CoordinateSpace.AbsoluteWorld
|
|
Identity, // to CoordinateSpace.Screen
|
|
}
|
|
};
|
|
|
|
internal static IEnumerable<NeededTransform> ComputeTransformRequirement(SpaceTransform xform)
|
|
{
|
|
var func = k_TransformFunctions[(int)xform.from, (int)xform.to];
|
|
if (func == ViaWorld || func == ObjectToAbsoluteWorld)
|
|
{
|
|
yield return new NeededTransform(xform.from.ToNeededCoordinateSpace(), NeededCoordinateSpace.World);
|
|
yield return new NeededTransform(NeededCoordinateSpace.World, xform.to.ToNeededCoordinateSpace());
|
|
}
|
|
else
|
|
{
|
|
yield return new NeededTransform(xform.from.ToNeededCoordinateSpace(), xform.to.ToNeededCoordinateSpace());
|
|
}
|
|
}
|
|
|
|
public static void GenerateTransformCodeStatement(SpaceTransform xform, string inputValue, string outputVariable, ShaderStringBuilder sb)
|
|
{
|
|
var func = k_TransformFunctions[(int)xform.from, (int)xform.to];
|
|
func(xform, inputValue, outputVariable, sb);
|
|
}
|
|
}
|
|
}
|