Use swept Bokusho contact capsules

This commit is contained in:
Meta Vens 2026-06-16 03:39:52 +01:00
commit e9caacd781
3 changed files with 20 additions and 18 deletions

View file

@ -6,10 +6,9 @@ namespace Aquarium.Engine.Render;
public static class BokushoBrushSimulation
{
public const float PageDensityScale = 65535.0f;
public const float PagePatchTransferGain = 0.0032f;
public const float PagePatchTangentRadiusScale = 0.26f;
public const float PagePatchSweepRadiusScale = 0.5f;
public const float PagePatchNormalRadiusScale = 0.20f;
public const float PagePatchTransferGain = 0.0044f;
public const float PagePatchTangentRadiusScale = 0.32f;
public const float PagePatchNormalRadiusScale = 0.32f;
public const float PagePatchMinimumRadius = 0.001f;
public const float PagePatchWorldQuantum = 1.0f / 1024.0f;
public const float PagePatchInkQuantum = 1.0f / 2048.0f;
@ -667,9 +666,10 @@ public static class BokushoBrushSimulation
var normal = new Vector2(-tangent.Y, tangent.X);
var (patchTangentRadius, patchNormalRadius) = PagePatchRadii(tangentRadius, normalRadius, sweepLength);
var center = (previousTip + tip) * 0.5f;
var halfSweepLength = sweepLength * 0.5f;
var extents = new Vector2(
MathF.Abs(tangent.X) * patchTangentRadius + MathF.Abs(normal.X) * patchNormalRadius,
MathF.Abs(tangent.Y) * patchTangentRadius + MathF.Abs(normal.Y) * patchNormalRadius);
MathF.Abs(tangent.X) * (halfSweepLength + patchTangentRadius) + MathF.Abs(normal.X) * patchNormalRadius,
MathF.Abs(tangent.Y) * (halfSweepLength + patchTangentRadius) + MathF.Abs(normal.Y) * patchNormalRadius);
var minWorld = center - extents;
var maxWorld = center + extents;
var minPixel = WorldToPixel(minWorld, width, height, viewCenter, viewRadius);
@ -687,7 +687,8 @@ public static class BokushoBrushSimulation
var uvX = width <= 1 ? 0.0f : x / (float)(width - 1);
var world = viewCenter + (new Vector2(uvX, uvY) * 2.0f - Vector2.One) * viewRadius;
var local = world - center;
var tangentDistance = Vector2.Dot(local, tangent) / patchTangentRadius;
var axialExcess = MathF.Max(MathF.Abs(Vector2.Dot(local, tangent)) - halfSweepLength, 0.0f);
var tangentDistance = axialExcess / patchTangentRadius;
var normalDistance = Vector2.Dot(local, normal) / patchNormalRadius;
var ellipse = MathF.Sqrt(tangentDistance * tangentDistance + normalDistance * normalDistance);
var coverage = QuantizePositive(SmoothStep(1.0f, 0.0f, ellipse), PagePatchCoverageQuantum);
@ -791,7 +792,7 @@ public static class BokushoBrushSimulation
public static (float TangentRadius, float NormalRadius) PagePatchRadii(float tangentRadius, float normalRadius, float sweepLength)
{
return (
MathF.Max(tangentRadius * PagePatchTangentRadiusScale + sweepLength * PagePatchSweepRadiusScale, PagePatchMinimumRadius),
MathF.Max(tangentRadius * PagePatchTangentRadiusScale, PagePatchMinimumRadius),
MathF.Max(normalRadius * PagePatchNormalRadiusScale, PagePatchMinimumRadius));
}

View file

@ -1,10 +1,9 @@
#include "CultMath/CultMath.hlsl"
static const float BOKUSHO_PAGE_DENSITY_SCALE = 65535.0;
static const float BOKUSHO_PAGE_PATCH_TRANSFER_GAIN = 0.0032;
static const float BOKUSHO_PAGE_PATCH_TANGENT_RADIUS_SCALE = 0.26;
static const float BOKUSHO_PAGE_PATCH_SWEEP_RADIUS_SCALE = 0.5;
static const float BOKUSHO_PAGE_PATCH_NORMAL_RADIUS_SCALE = 0.20;
static const float BOKUSHO_PAGE_PATCH_TRANSFER_GAIN = 0.0044;
static const float BOKUSHO_PAGE_PATCH_TANGENT_RADIUS_SCALE = 0.32;
static const float BOKUSHO_PAGE_PATCH_NORMAL_RADIUS_SCALE = 0.32;
static const float BOKUSHO_PAGE_PATCH_MINIMUM_RADIUS = 0.001;
static const float BOKUSHO_PAGE_PATCH_WORLD_QUANTUM = 1.0 / 1024.0;
static const float BOKUSHO_PAGE_PATCH_INK_QUANTUM = 1.0 / 2048.0;
@ -142,12 +141,13 @@ void DepositSweptPatch(float2 previousTip, float2 tip, float normalRadius, float
float sweepLength = length(sweep);
float2 tangent = sweepLength > 0.000001 ? sweep / sweepLength : float2(1.0, 0.0);
float2 normal = float2(-tangent.y, tangent.x);
float patchTangentRadius = max(tangentRadius * BOKUSHO_PAGE_PATCH_TANGENT_RADIUS_SCALE + sweepLength * BOKUSHO_PAGE_PATCH_SWEEP_RADIUS_SCALE, BOKUSHO_PAGE_PATCH_MINIMUM_RADIUS);
float patchTangentRadius = max(tangentRadius * BOKUSHO_PAGE_PATCH_TANGENT_RADIUS_SCALE, BOKUSHO_PAGE_PATCH_MINIMUM_RADIUS);
float patchNormalRadius = max(normalRadius * BOKUSHO_PAGE_PATCH_NORMAL_RADIUS_SCALE, BOKUSHO_PAGE_PATCH_MINIMUM_RADIUS);
float2 center = (previousTip + tip) * 0.5;
float halfSweepLength = sweepLength * 0.5;
float2 extents = float2(
abs(tangent.x) * patchTangentRadius + abs(normal.x) * patchNormalRadius,
abs(tangent.y) * patchTangentRadius + abs(normal.y) * patchNormalRadius);
abs(tangent.x) * (halfSweepLength + patchTangentRadius) + abs(normal.x) * patchNormalRadius,
abs(tangent.y) * (halfSweepLength + patchTangentRadius) + abs(normal.y) * patchNormalRadius);
float pageSize = max(pageView.w, 1.0);
float2 minUv = (center - extents - pageView.xy) / max(pageView.z, 0.001) * 0.5 + 0.5;
float2 maxUv = (center + extents - pageView.xy) / max(pageView.z, 0.001) * 0.5 + 0.5;
@ -166,7 +166,8 @@ void DepositSweptPatch(float2 previousTip, float2 tip, float normalRadius, float
float uvX = pageSize <= 1.0 ? 0.0 : (float)x / (pageSize - 1.0);
float2 world = pageView.xy + (float2(uvX, uvY) * 2.0 - 1.0) * pageView.z;
float2 local = world - center;
float tangentDistance = dot(local, tangent) / patchTangentRadius;
float axialExcess = max(abs(dot(local, tangent)) - halfSweepLength, 0.0);
float tangentDistance = axialExcess / patchTangentRadius;
float normalDistance = dot(local, normal) / patchNormalRadius;
float ellipse = sqrt(tangentDistance * tangentDistance + normalDistance * normalDistance);
float coverage = QuantizePositive(cultmath_smoothstep(1.0, 0.0, ellipse), BOKUSHO_PAGE_PATCH_COVERAGE_QUANTUM);

View file

@ -292,14 +292,14 @@ public sealed class BokushoBrushSimulationTests
}
[Fact]
public void PagePatchRadiiIncludeBristleFootprintAndSweepDistance()
public void PagePatchRadiiKeepSweepLengthOutOfBristleFootprint()
{
var (tangentRadius, normalRadius) = BokushoBrushSimulation.PagePatchRadii(
tangentRadius: 0.42f,
normalRadius: 0.35f,
sweepLength: 0.18f);
Assert.Equal(0.42f * BokushoBrushSimulation.PagePatchTangentRadiusScale + 0.18f * BokushoBrushSimulation.PagePatchSweepRadiusScale, tangentRadius, precision: 6);
Assert.Equal(0.42f * BokushoBrushSimulation.PagePatchTangentRadiusScale, tangentRadius, precision: 6);
Assert.Equal(0.35f * BokushoBrushSimulation.PagePatchNormalRadiusScale, normalRadius, precision: 6);
var tiny = BokushoBrushSimulation.PagePatchRadii(0.0f, 0.0f, 0.0f);