From 58ee5595a6d4f367699597d1b16212b71ebf2f44 Mon Sep 17 00:00:00 2001 From: Paul Demeulenaere Date: Wed, 8 Dec 2021 17:21:30 +0100 Subject: [PATCH 1/2] [VFX/SG] Fix missing ObjectToWorld using Transform (#6475) * Fix TransformNode usage in VFX Missing World To Object matrix requirement * *Update changelog Only in VFX, I don't see other cases where this fix is applicable, maybe DOTS ? * Better filtering of needed transform Fix issue https://github.com/Unity-Technologies/Graphics/pull/6475#discussion_r764483354 Fix issue https://github.com/Unity-Technologies/Graphics/pull/6475#discussion_r764647186 # Conflicts: # com.unity.shadergraph/Editor/Data/Util/SpaceTransformUtil.cs # com.unity.visualeffectgraph/CHANGELOG.md --- .../Data/Nodes/Math/Vector/TransformNode.cs | 6 +- .../Editor/Data/Util/SpaceTransformUtil.cs | 398 ++++++++++++++++++ com.unity.visualeffectgraph/CHANGELOG.md | 1 + 3 files changed, 401 insertions(+), 4 deletions(-) create mode 100644 com.unity.shadergraph/Editor/Data/Util/SpaceTransformUtil.cs diff --git a/com.unity.shadergraph/Editor/Data/Nodes/Math/Vector/TransformNode.cs b/com.unity.shadergraph/Editor/Data/Nodes/Math/Vector/TransformNode.cs index f038fc041b2..1af9a6d8e62 100644 --- a/com.unity.shadergraph/Editor/Data/Nodes/Math/Vector/TransformNode.cs +++ b/com.unity.shadergraph/Editor/Data/Nodes/Math/Vector/TransformNode.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using UnityEditor.Graphing; using UnityEditor.ShaderGraph.Drawing.Controls; using UnityEditor.ShaderGraph.Internal; @@ -296,10 +297,7 @@ public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapabilit public NeededTransform[] RequiresTransform(ShaderStageCapability stageCapability) { - return new[] - { - new NeededTransform(conversion.from.ToNeededCoordinateSpace(), conversion.to.ToNeededCoordinateSpace()) - }; + return spaceTransform.RequiresTransform.ToArray(); } } } diff --git a/com.unity.shadergraph/Editor/Data/Util/SpaceTransformUtil.cs b/com.unity.shadergraph/Editor/Data/Util/SpaceTransformUtil.cs new file mode 100644 index 00000000000..67a8c23493c --- /dev/null +++ b/com.unity.shadergraph/Editor/Data/Util/SpaceTransformUtil.cs @@ -0,0 +1,398 @@ +// 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 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; + } + } + + static readonly TransformFunction[,] k_TransformFunctions = new TransformFunction[5, 5] // [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 + }, + { // from CoordinateSpace.View + ViaWorld, // to CoordinateSpace.Object + Identity, // to CoordinateSpace.View + ViewToWorld, // to CoordinateSpace.World + ViaWorld, // to CoordinateSpace.Tangent + ViaWorld, // to CoordinateSpace.AbsoluteWorld + }, + { // from CoordinateSpace.World + WorldToObject, // to CoordinateSpace.Object + WorldToView, // to CoordinateSpace.View + Identity, // to CoordinateSpace.World + WorldToTangent, // to CoordinateSpace.Tangent + WorldToAbsoluteWorld, // to CoordinateSpace.AbsoluteWorld + }, + { // from CoordinateSpace.Tangent + ViaWorld, // to CoordinateSpace.Object + ViaWorld, // to CoordinateSpace.View + TangentToWorld, // to CoordinateSpace.World + Identity, // to CoordinateSpace.Tangent + ViaWorld, // to CoordinateSpace.AbsoluteWorld + }, + { // from CoordinateSpace.AbsoluteWorld + ViaWorld, // to CoordinateSpace.Object + ViaWorld, // to CoordinateSpace.View + AbsoluteWorldToWorld, // to CoordinateSpace.World + ViaWorld, // to CoordinateSpace.Tangent + Identity, // to CoordinateSpace.AbsoluteWorld + } + }; + + internal static IEnumerable 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); + } + } +} diff --git a/com.unity.visualeffectgraph/CHANGELOG.md b/com.unity.visualeffectgraph/CHANGELOG.md index b37e157ec05..a8364f6fec2 100644 --- a/com.unity.visualeffectgraph/CHANGELOG.md +++ b/com.unity.visualeffectgraph/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed - Automatically offset contexts when a new node is inserted to avoid overlapping - Fixed null reference exception when opening another VFX and a debug mode is enabled [Case 1347420](https://issuetracker.unity3d.com/product/unity/issues/guid/1347420/) +- Incorrect behavior of Tangent Space in ShaderGraph [Case 1363279](https://issuetracker.unity3d.com/product/unity/issues/guid/1363279/) ## [12.1.2] - 2021-10-22 ### Fixed From eb3626cc62240a50cc3e2cbb39ef4ad993422083 Mon Sep 17 00:00:00 2001 From: Paul Demeulenaere Date: Mon, 13 Dec 2021 08:59:07 +0100 Subject: [PATCH 2/2] Special backport on 21.2 The refactor from https://github.com/Unity-Technologies/Graphics/pull/5817 hasn't been backported --- .../Data/Nodes/Math/Vector/TransformNode.cs | 56 ++- .../Editor/Data/Util/SpaceTransformUtil.cs | 398 ------------------ 2 files changed, 55 insertions(+), 399 deletions(-) delete mode 100644 com.unity.shadergraph/Editor/Data/Util/SpaceTransformUtil.cs diff --git a/com.unity.shadergraph/Editor/Data/Nodes/Math/Vector/TransformNode.cs b/com.unity.shadergraph/Editor/Data/Nodes/Math/Vector/TransformNode.cs index 1af9a6d8e62..930b798fb9e 100644 --- a/com.unity.shadergraph/Editor/Data/Nodes/Math/Vector/TransformNode.cs +++ b/com.unity.shadergraph/Editor/Data/Nodes/Math/Vector/TransformNode.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Collections.Generic; using UnityEditor.Graphing; using UnityEditor.ShaderGraph.Drawing.Controls; using UnityEditor.ShaderGraph.Internal; @@ -295,9 +296,62 @@ public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapabilit return conversion.from.ToNeededCoordinateSpace(); } + static readonly bool[,] k_ViaWorldFunction = new bool[5, 5] // [from, to] + { + { // from CoordinateSpace.Object + false, // to CoordinateSpace.Object + true, // to CoordinateSpace.View + false, // to CoordinateSpace.World + true, // to CoordinateSpace.Tangent + true, // to CoordinateSpace.AbsoluteWorld + }, + { // from CoordinateSpace.View + true, // to CoordinateSpace.Object + false, // to CoordinateSpace.View + false, // to CoordinateSpace.World + true, // to CoordinateSpace.Tangent + true, // to CoordinateSpace.AbsoluteWorld + }, + { // from CoordinateSpace.World + false, // to CoordinateSpace.Object + false, // to CoordinateSpace.View + false, // to CoordinateSpace.World + false, // to CoordinateSpace.Tangent + false, // to CoordinateSpace.AbsoluteWorld + }, + { // from CoordinateSpace.Tangent + true, // to CoordinateSpace.Object + true, // to CoordinateSpace.View + false, // to CoordinateSpace.World + false, // to CoordinateSpace.Tangent + true, // to CoordinateSpace.AbsoluteWorld + }, + { // from CoordinateSpace.AbsoluteWorld + true, // to CoordinateSpace.Object + true, // to CoordinateSpace.View + false, // to CoordinateSpace.World + true, // to CoordinateSpace.Tangent + false, // to CoordinateSpace.AbsoluteWorld + } + }; + + internal static IEnumerable ComputeTransformRequirement(CoordinateSpaceConversion xform) + { + var viaWorld = k_ViaWorldFunction[(int)xform.from, (int)xform.to]; + if (viaWorld) + { + 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 NeededTransform[] RequiresTransform(ShaderStageCapability stageCapability) { - return spaceTransform.RequiresTransform.ToArray(); + return ComputeTransformRequirement(conversion).ToArray(); } } } diff --git a/com.unity.shadergraph/Editor/Data/Util/SpaceTransformUtil.cs b/com.unity.shadergraph/Editor/Data/Util/SpaceTransformUtil.cs deleted file mode 100644 index 67a8c23493c..00000000000 --- a/com.unity.shadergraph/Editor/Data/Util/SpaceTransformUtil.cs +++ /dev/null @@ -1,398 +0,0 @@ -// 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 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; - } - } - - static readonly TransformFunction[,] k_TransformFunctions = new TransformFunction[5, 5] // [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 - }, - { // from CoordinateSpace.View - ViaWorld, // to CoordinateSpace.Object - Identity, // to CoordinateSpace.View - ViewToWorld, // to CoordinateSpace.World - ViaWorld, // to CoordinateSpace.Tangent - ViaWorld, // to CoordinateSpace.AbsoluteWorld - }, - { // from CoordinateSpace.World - WorldToObject, // to CoordinateSpace.Object - WorldToView, // to CoordinateSpace.View - Identity, // to CoordinateSpace.World - WorldToTangent, // to CoordinateSpace.Tangent - WorldToAbsoluteWorld, // to CoordinateSpace.AbsoluteWorld - }, - { // from CoordinateSpace.Tangent - ViaWorld, // to CoordinateSpace.Object - ViaWorld, // to CoordinateSpace.View - TangentToWorld, // to CoordinateSpace.World - Identity, // to CoordinateSpace.Tangent - ViaWorld, // to CoordinateSpace.AbsoluteWorld - }, - { // from CoordinateSpace.AbsoluteWorld - ViaWorld, // to CoordinateSpace.Object - ViaWorld, // to CoordinateSpace.View - AbsoluteWorldToWorld, // to CoordinateSpace.World - ViaWorld, // to CoordinateSpace.Tangent - Identity, // to CoordinateSpace.AbsoluteWorld - } - }; - - internal static IEnumerable 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); - } - } -}