Files
2026-07-13 13:13:17 +08:00

82 lines
3.3 KiB
C#

using T3.Core.DataTypes.ShaderGraph;
namespace Lib.field.use;
[Guid("48136477-cb34-416c-a968-7c9d82da9d9b")]
internal sealed class SdfToVector : Instance<SdfToVector>
, IGraphNodeOp
{
[Output(Guid = "8055c3e2-0830-4795-ad60-16d8716209fc")]
public readonly Slot<ShaderGraphNode> Result = new();
public SdfToVector()
{
ShaderNode = new ShaderGraphNode(this, null, InputField);
Result.Value = ShaderNode;
Result.UpdateAction += Update;
}
private void Update(EvaluationContext context)
{
ShaderNode.Update(context);
ShaderNode.FlagCodeChanged();
}
public ShaderGraphNode ShaderNode { get; }
/// <remarks>
/// This method is more complex than might be obvious:
/// - We need to sample the field at 4 + 1 positions.
/// - But we _only_ want to include incoming shader code once.
/// - The only solution is to define and wrap the shader code inside a method.
/// - But the incoming shader code must be collected in THIS call through CollectEmbeddedShaderCode()
/// - To separate the sub shader code for our method, we have to introduce a stack of StringBuilders to
/// push and keep the previous StringBuilders and then pop and convert the temporary ones into a string.
/// - This result can then be used to define an instance method with that code...
/// - which can then be called from the actual shader lines.
///
/// This hasn't been extensively tested. But it should be.
/// </remarks>
bool IGraphNodeOp.TryBuildCustomCode(CodeAssembleContext c)
{
var fields = ShaderNode.InputNodes;
if (fields.Count == 0)
return true;
c.PushMethod("GetDistance");
var inputField = fields[0];
inputField?.CollectEmbeddedShaderCode(c);
var nestedCode = c.PopMethod();
c.Definitions.Append($$"""
float {{c}}GetSdfDistance(float3 p3)
{
float4 p{{c}} = float4(p3, 0);
float4 f{{c}} = 1;
{{nestedCode}}
return f{{c}}.w;
}
""");
var offset = $"{ShaderNode}LookUpDistance";
c.AppendCall($"""
f{c}.xyz = normalize(
{c}GetSdfDistance(p{c}.xyz + float3({offset}, -{offset}, -{offset})) * float3(1, -1, -1) +
{c}GetSdfDistance(p{c}.xyz + float3(-{offset}, {offset}, -{offset})) * float3(-1, 1, -1) +
{c}GetSdfDistance(p{c}.xyz + float3(-{offset}, -{offset}, {offset})) * float3(-1, -1, 1) +
{c}GetSdfDistance(p{c}.xyz + float3({offset}, {offset}, {offset})) * float3(1, 1, 1));
f{c}.w = GetSdfDistance(p{c}.xyz);
""");
return true;
}
[Input(Guid = "d6664c35-040a-4021-ac37-aa7eaaae2b65")]
public readonly InputSlot<ShaderGraphNode> InputField = new();
[GraphParam]
[Input(Guid = "2ea69efd-a01b-4a7f-8f87-31c8dfefcad5")]
public readonly InputSlot<float> LookUpDistance = new();
}