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

153 lines
4.3 KiB
HLSL

#include "shared/hash-functions.hlsl"
#include "shared/noise-functions.hlsl"
#include "shared/point.hlsl"
#include "shared/quat-functions.hlsl"
cbuffer Params : register(b0)
{
float3 Center;
float Amount;
float3 UpVector;
float UseWAsWeight;
float Flip;
}
StructuredBuffer<Point> SourcePoints : t0;
RWStructuredBuffer<Point> ResultPoints : u0;
// Aligns orientation quaternion q so that its +Z forward
// points towards newForward
float4 qAlignForward(float4 q, float3 newForward)
{
newForward = normalize(-newForward);
// old up from current orientation (+Y rotated by q)
float3 oldUp = qRotateVec3(float3(0, 1, 0), q);
// project old up onto plane perpendicular to newForward
float3 projUp = oldUp - newForward * dot(oldUp, newForward);
// handle degeneracy: if oldUp ~ parallel to newForward
if (length(projUp) < 1e-5)
{
projUp = abs(newForward.x) < 0.9 ? float3(1, 0, 0) : float3(0, 1, 0);
projUp = projUp - newForward * dot(projUp, newForward);
}
projUp = normalize(projUp);
// Right-handed orthonormal basis:
float3 ez = normalize(newForward); // forward (+Z)
float3 ex = normalize(cross(projUp, ez)); // right (+X)
float3 ey = normalize(cross(ez, ex)); // up (+Y), fixed orthogonal
// Orientation matrix (columns = basis vectors)
float3x3 m = float3x3(ex, ey, ez);
// Convert to quaternion
return normalize(qFromMatrix3Precise(m));
}
// Aligns orientation quaternion q so that its +Z forward
// points towards newForward
float4 qAlignForward2(float4 q, float3 newForward)
{
newForward = normalize(newForward);
// old up from current orientation (+Y rotated by q)
float3 oldUp = qRotateVec3(float3(0, 1, 0), q);
// project old up onto plane perpendicular to newForward
float3 projUp = oldUp - newForward * dot(oldUp, newForward);
// handle degenerate case: oldUp nearly parallel to newForward
if (length(projUp) < 1e-5)
{
projUp = normalize(abs(newForward.x) < 0.9 ? float3(1, 0, 0) : float3(0, 1, 0));
projUp = normalize(projUp - newForward * dot(projUp, newForward));
}
else
{
projUp = normalize(projUp);
}
// rebuild quaternion with forward = newForward, up ≈ projected up
return qLookAt(newForward, -projUp);
}
float4 qAlignForward3(float4 q, float3 newForward)
{
newForward = normalize(newForward);
// old up from current orientation (+Y rotated by q)
float3 oldUp = qRotateVec3(float3(0, 1, 0), q);
// project old up onto plane perpendicular to newForward
float3 projUp = oldUp - newForward * dot(oldUp, newForward);
// handle degeneracy
if (length(projUp) < 1e-5)
{
projUp = abs(newForward.x) < 0.9 ? float3(1, 0, 0) : float3(0, 1, 0);
projUp = projUp - newForward * dot(projUp, newForward);
}
projUp = normalize(projUp);
// Build explicit right-handed basis
float3 ez = newForward; // +Z = forward
float3 ex = normalize(cross(projUp, ez)); // +X = right
float3 ey = normalize(cross(ez, ex)); // +Y = up
float3x3 m = float3x3(ex, ey, ez);
return normalize(qFromMatrix3Precise(m));
}
[numthreads(64, 1, 1)] void main(uint3 i : SV_DispatchThreadID)
{
uint index = i.x;
uint numStructs, stride;
SourcePoints.GetDimensions(numStructs, stride);
if (index >= numStructs)
return;
if (isnan(SourcePoints[index].Scale.x))
return;
// Find neighbours
uint prevIndex = index;
uint nextIndex = index;
if (index > 0 && !isnan(SourcePoints[index - 1].Scale.x))
{
prevIndex--;
}
if (index <= numStructs - 1 && !isnan(SourcePoints[index + 1].Scale.x))
{
nextIndex++;
}
// Nothing to align
if (prevIndex == nextIndex)
return;
float3 v = SourcePoints[nextIndex].Position - SourcePoints[prevIndex].Position;
// Points fall together
float l = length(v);
if (l < 0.0001)
return;
float3 dir = v / l;
Point p = SourcePoints[index];
float4 r = p.Rotation;
// Attempt to smooth orientaion with neighbours didn't yield significant improvements
// float4 r = qSlerp(p.Rotation, qSlerp(SourcePoints[prevIndex - 1].Rotation, SourcePoints[nextIndex + 1].Rotation, 0.5), 1.0);
p.Rotation = qSlerp(p.Rotation, qAlignForward2(r, dir), Amount);
ResultPoints[i.x] = p;
}