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

161 lines
5.9 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using T3.Core.DataTypes.ShaderGraph;
using T3.Core.Utils;
namespace Lib.field.generate.vec3;
[Guid("5623d6b3-b8a7-4fd1-9cb4-952dc7818a83")]
internal sealed class ToroidalVortexField : Instance<ToroidalVortexField>
,IGraphNodeOp,ITransformable
{
[Output(Guid = "fc9119f1-32ae-4b3a-93d2-f9980d9b345f")]
public readonly Slot<ShaderGraphNode> Result = new();
// ITransformable interface implementation (Gizmo support)
IInputSlot ITransformable.TranslationInput => Center;
IInputSlot ITransformable.RotationInput => null;
IInputSlot ITransformable.ScaleInput => null;
public Action<Instance, EvaluationContext> TransformCallback { get; set; }
public ToroidalVortexField()
{
ShaderNode = new ShaderGraphNode(this);
Result.Value = ShaderNode;
Result.UpdateAction += Update;
}
private void Update(EvaluationContext context)
{
TransformCallback?.Invoke(this, context); // Needed for Gizmo support
var axis = Axis.GetEnumValue<AxisTypes>(context);
var templateChanged = axis != _axis;
if (templateChanged)
{
_axis = axis;
ShaderNode.FlagCodeChanged();
}
ShaderNode.Update(context);
}
public ShaderGraphNode ShaderNode { get; }
void IGraphNodeOp.AddDefinitions(CodeAssembleContext c)
{
c.Globals["fDecay"] = """
float fDecay(float dist, float falloffRadius, float rate)
{
//float x = saturate(dist / max(falloffRadius, 1e-6));
return 1.0 / (falloffRadius + pow(dist, rate));
}
""";
// Particles spin around the torus *centerline* (major ring) and
// can be attracted to (+) or repelled from () that ring.
// Returns .xyz = velocity contribution, .w = distance to centerline (rho).
//
// Params
// radius : major radius of the torus centerline
// fallOffRadius : range for both swirl + radial influence
// swirlGain : magnitude of the around-the-ring swirl (units: vel)
// set 0 to disable swirl
// radialGain : >0 attracts toward the ring, <0 repels away from the ring
// decayK : falloff exponent (2 = quadratic, 4 = quartic, ...)
c.Globals["fToroidalVectorField"]
= """
float4 fToroidalVectorField(
float3 p,
float radius,
float range,
float swirlGain,
float radialGain,
float decayK)
{
const float eps = 1e-6;
// Angle around Z and basis on the centerline
float phi = atan2(p.y, p.x);
float c = cos(phi), s = sin(phi);
float3 e_r = float3(c, s, 0); // radial in XY
float3 e_phi = float3(-s, c, 0); // tangent of centerline (around Z)
// Closest point on centerline and offset from it (minor-plane vector)
float3 C = radius * e_r;
float3 r = p - C; // lies in span{e_r, e_z}
float rho = length(r);
if (rho < eps) return float4(0,0,0,0);
// Decay in [0,1]: 1 at rho=0, ->0 at rho >= range
float x = rho / max(range, eps);
float decay = saturate(1.0 - pow(x, decayK));
//decay = 1;
// --- Swirl around the minor circle (tangent to cross-section) ---
// Use cross(e_phi, r) so swirl -> 0 on centerline and grows ~ rho.
float3 vSwirl = cross(e_phi, r);
// Normalize then scale by gain and decay (keeps units stable):
vSwirl = normalize(vSwirl) * (swirlGain * decay);
// --- Radial attraction/repulsion toward/from the centerline ---
// +radialGain attracts toward the ring, -radialGain repels.
float3 dirToRing = -r / rho; // toward centerline
float3 vRadial = dirToRing * (radialGain * decay);
float3 v = vSwirl + vRadial;
return float4(v, decay);
}
""";
}
public void GetPreShaderCode(CodeAssembleContext c, int inputIndex)
{
var n = ShaderNode;
var a = _axisCodes0[(int)_axis];
c.AppendCall($"f{c} = fToroidalVectorField(p{c}.{a} - {n}Center.{a}, {n}Radius, {n}Range, {n}SwirlGain, {n}RadialGain, {n}FallOffRate).{a}w;");
}
private readonly string[] _axisCodes0 =
[
"zyx",
"xzy",
"xyz",
];
private AxisTypes _axis;
private enum AxisTypes
{
X,
Y,
Z,
}
[GraphParam]
[Input(Guid = "1efeddb4-58b1-4877-bc45-efe430f147db")]
public readonly InputSlot<Vector3> Center = new();
[GraphParam]
[Input(Guid = "f2c26298-fb95-4b74-848e-28ea9ea7b724")]
public readonly InputSlot<float> Radius = new();
[GraphParam]
[Input(Guid = "CDC51BA3-2989-449F-BF51-D8A642FD4647")]
public readonly InputSlot<float> Range = new();
[GraphParam]
[Input(Guid = "634933DF-5F75-4071-A76A-6440BB3F02EF")]
public readonly InputSlot<float> SwirlGain = new();
[GraphParam]
[Input(Guid = "EB2E2E6D-AF78-4B9A-A925-24CCE2164053")]
public readonly InputSlot<float> RadialGain = new();
[GraphParam]
[Input(Guid = "f6d274ae-8502-459e-b7a9-59755e3cb21e")]
public readonly InputSlot<float> FallOffRate = new();
[Input(Guid = "2ed51b8f-af21-4b93-9428-45e08b9eec62", MappedType = typeof(AxisTypes))]
public readonly InputSlot<int> Axis = new();
}