CultMath HLSL: site cells, scores, border, Gabriel margin and segment distance twins (both copies, mirror asserts)

CommittedGlslEqualsLowering fails at this commit until the GLSL is regenerated in the next one.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-16 18:17:40 +02:00
commit e584bfaff2
3 changed files with 222 additions and 1 deletions

View file

@ -600,6 +600,112 @@ CultCellular cultmath_cellular(float3 p)
return result;
}
// Sites: a site is float4(position.xyz, weight). A span is a loop over a per-site step, because a shader
// function cannot take a span in the mirror (design.md, "Sites"). The consumer's loop, over its own buffer:
// CultSiteCells cells = cultmath_site_cells_empty();
// for (int i = 0; i < count; i++)
// cells = cultmath_site_cells_insert(cells, cultmath_power_score(p, sites[i]), i);
// Statement for statement with math.Sites.cs (power_score, multiplicative_score, site_cells_*, gabriel_margin).
struct CultSiteCells
{
float4 nearest;
float4 second;
float nearestSite;
float secondSite;
};
float4 cultmath_power_score(float3 p, float4 site)
{
float3 offset = p - site.xyz;
return float4(offset * 2.0, dot(offset, offset) - site.w * site.w);
}
float4 cultmath_multiplicative_score(float3 p, float4 site)
{
float3 offset = p - site.xyz;
float r = length(offset);
float3 gradient = r > 0.0 ? offset / (r * site.w) : float3(0.0, 0.0, 0.0);
return float4(gradient, r / site.w);
}
CultSiteCells cultmath_site_cells_empty()
{
CultSiteCells result;
result.nearest = float4(0.0, 0.0, 0.0, 1.0e30);
result.second = float4(0.0, 0.0, 0.0, 1.0e30);
result.nearestSite = -1.0;
result.secondSite = -1.0;
return result;
}
CultSiteCells cultmath_site_cells_insert(CultSiteCells cells, float4 score, int index)
{
CultSiteCells result = cells;
if (score.w < cells.nearest.w)
{
result.second = cells.nearest;
result.secondSite = cells.nearestSite;
result.nearest = score;
result.nearestSite = (float)index;
}
else if (score.w < cells.second.w)
{
result.second = score;
result.secondSite = (float)index;
}
return result;
}
float4 cultmath_site_cells_border(CultSiteCells cells)
{
float3 n = cells.second.xyz - cells.nearest.xyz;
float len = length(n);
if (cells.secondSite < 0.0 || len <= 0.0)
return float4(0.0, 0.0, 0.0, 1.0e30);
return float4(n / len, (cells.second.w - cells.nearest.w) / len);
}
float cultmath_gabriel_margin(float3 a, float3 b, float3 other)
{
float r = cultmath_distance(a, b) * 0.5;
if (r <= 0.0)
return -1.0;
return (cultmath_distance(other, (a + b) * 0.5) - r) / r;
}
float2 cultmath_segment_closest(float2 point_value, float2 start, float2 end)
{
float2 segment = end - start;
float segmentLengthSq = cultmath_lengthsq(segment);
if (segmentLengthSq <= 0.0)
return start;
float t = cultmath_saturate(dot(point_value - start, segment) / segmentLengthSq);
return start + segment * t;
}
float3 cultmath_segment_closest(float3 point_value, float3 start, float3 end)
{
float3 segment = end - start;
float segmentLengthSq = cultmath_lengthsq(segment);
if (segmentLengthSq <= 0.0)
return start;
float t = cultmath_saturate(dot(point_value - start, segment) / segmentLengthSq);
return start + segment * t;
}
float cultmath_distance_to_segment(float2 point_value, float2 start, float2 end)
{
return cultmath_distance(point_value, cultmath_segment_closest(point_value, start, end));
}
float cultmath_distance_to_segment(float3 point_value, float3 start, float3 end)
{
return cultmath_distance(point_value, cultmath_segment_closest(point_value, start, end));
}
#include "CultMath.Phacelle.hlsl"
#include "CultMath.Interval.hlsl"
#include "CultMath.Cycles.hlsl"

View file

@ -27,6 +27,15 @@ public sealed class HlslSourceCompatibilityTests
Assert.NotNull(ShaderFunction(shader, "cultmath_value_noise_bicubic", typeof(float2)));
Assert.NotNull(ShaderFunction(shader, "cultmath_smin_grad", typeof(float4), typeof(float4), typeof(float)));
Assert.NotNull(ShaderFunction(shader, "cultmath_cellular", typeof(float3)));
var siteCells = shader.GetNestedType("CultSiteCells", BindingFlags.Public | BindingFlags.NonPublic)!;
Assert.NotNull(ShaderFunction(shader, "cultmath_power_score", typeof(float3), typeof(float4)));
Assert.NotNull(ShaderFunction(shader, "cultmath_multiplicative_score", typeof(float3), typeof(float4)));
Assert.NotNull(ShaderFunction(shader, "cultmath_site_cells_empty"));
Assert.NotNull(ShaderFunction(shader, "cultmath_site_cells_insert", siteCells, typeof(float4), typeof(int)));
Assert.NotNull(ShaderFunction(shader, "cultmath_site_cells_border", siteCells));
Assert.NotNull(ShaderFunction(shader, "cultmath_gabriel_margin", typeof(float3), typeof(float3), typeof(float3)));
Assert.NotNull(ShaderFunction(shader, "cultmath_distance_to_segment", typeof(float2), typeof(float2), typeof(float2)));
Assert.NotNull(ShaderFunction(shader, "cultmath_distance_to_segment", typeof(float3), typeof(float3), typeof(float3)));
Assert.NotNull(ShaderFunction(shader, "cultmath_snoise_grad", typeof(float3)));
Assert.NotNull(ShaderFunction(shader, "cultmath_snoise_hessian", typeof(float3)));
Assert.NotNull(ShaderFunction(shader, "cultmath_integral_clamp_linear", typeof(float), typeof(float), typeof(float), typeof(float), typeof(float), typeof(float)));
@ -178,7 +187,7 @@ public sealed class HlslSourceCompatibilityTests
}
Assert.True(mismatches.Count == 0, $"{mismatches.Count} mismatches:{Environment.NewLine}{string.Join(Environment.NewLine, mismatches.Take(12))}");
Assert.Equal(127, compared.Count);
Assert.Equal(135, compared.Count);
}
/// <summary>

View file

@ -600,6 +600,112 @@ CultCellular cultmath_cellular(float3 p)
return result;
}
// Sites: a site is float4(position.xyz, weight). A span is a loop over a per-site step, because a shader
// function cannot take a span in the mirror (design.md, "Sites"). The consumer's loop, over its own buffer:
// CultSiteCells cells = cultmath_site_cells_empty();
// for (int i = 0; i < count; i++)
// cells = cultmath_site_cells_insert(cells, cultmath_power_score(p, sites[i]), i);
// Statement for statement with math.Sites.cs (power_score, multiplicative_score, site_cells_*, gabriel_margin).
struct CultSiteCells
{
float4 nearest;
float4 second;
float nearestSite;
float secondSite;
};
float4 cultmath_power_score(float3 p, float4 site)
{
float3 offset = p - site.xyz;
return float4(offset * 2.0, dot(offset, offset) - site.w * site.w);
}
float4 cultmath_multiplicative_score(float3 p, float4 site)
{
float3 offset = p - site.xyz;
float r = length(offset);
float3 gradient = r > 0.0 ? offset / (r * site.w) : float3(0.0, 0.0, 0.0);
return float4(gradient, r / site.w);
}
CultSiteCells cultmath_site_cells_empty()
{
CultSiteCells result;
result.nearest = float4(0.0, 0.0, 0.0, 1.0e30);
result.second = float4(0.0, 0.0, 0.0, 1.0e30);
result.nearestSite = -1.0;
result.secondSite = -1.0;
return result;
}
CultSiteCells cultmath_site_cells_insert(CultSiteCells cells, float4 score, int index)
{
CultSiteCells result = cells;
if (score.w < cells.nearest.w)
{
result.second = cells.nearest;
result.secondSite = cells.nearestSite;
result.nearest = score;
result.nearestSite = (float)index;
}
else if (score.w < cells.second.w)
{
result.second = score;
result.secondSite = (float)index;
}
return result;
}
float4 cultmath_site_cells_border(CultSiteCells cells)
{
float3 n = cells.second.xyz - cells.nearest.xyz;
float len = length(n);
if (cells.secondSite < 0.0 || len <= 0.0)
return float4(0.0, 0.0, 0.0, 1.0e30);
return float4(n / len, (cells.second.w - cells.nearest.w) / len);
}
float cultmath_gabriel_margin(float3 a, float3 b, float3 other)
{
float r = cultmath_distance(a, b) * 0.5;
if (r <= 0.0)
return -1.0;
return (cultmath_distance(other, (a + b) * 0.5) - r) / r;
}
float2 cultmath_segment_closest(float2 point_value, float2 start, float2 end)
{
float2 segment = end - start;
float segmentLengthSq = cultmath_lengthsq(segment);
if (segmentLengthSq <= 0.0)
return start;
float t = cultmath_saturate(dot(point_value - start, segment) / segmentLengthSq);
return start + segment * t;
}
float3 cultmath_segment_closest(float3 point_value, float3 start, float3 end)
{
float3 segment = end - start;
float segmentLengthSq = cultmath_lengthsq(segment);
if (segmentLengthSq <= 0.0)
return start;
float t = cultmath_saturate(dot(point_value - start, segment) / segmentLengthSq);
return start + segment * t;
}
float cultmath_distance_to_segment(float2 point_value, float2 start, float2 end)
{
return cultmath_distance(point_value, cultmath_segment_closest(point_value, start, end));
}
float cultmath_distance_to_segment(float3 point_value, float3 start, float3 end)
{
return cultmath_distance(point_value, cultmath_segment_closest(point_value, start, end));
}
#include "CultMath.Phacelle.hlsl"
#include "CultMath.Interval.hlsl"
#include "CultMath.Cycles.hlsl"