wip(standalone): GPU field step 2 -- far-field shapes/strata/relief in shared C++/HLSL, GPU field+mesh

Seven LOD2-8 tiles: RX 7900 XTX field+meshing 0.335 ms vs CPU generateTile 15.81 ms; repeat runs
bit-identical; CPU W1/W8 44/44 hashes unchanged. Not yet a real gain: rock/air signs still come from
CPU masks for every sample. Viewer does not draw GPU tiles yet; no whole-view timing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-06 08:02:02 +02:00
1 parent d4b4377d30
commit b61ccff549
11 files changed
+2361 -9

No files matched your search

+178 -3
View File
@@ -7,22 +7,182 @@
#define VF_FIELD_INLINE
typedef int VF_FieldI32;
typedef uint VF_FieldU32;
typedef float VF_FieldReal;
#define VF_FIELD_CAST_I32(v) int(v)
#define VF_FIELD_CAST_U32(v) uint(v)
#define VF_FIELD_CAST_F32(v) float(v)
#define VF_FIELD_FLOOR(v) floor(v)
#define VF_FIELD_ABS(v) abs(v)
#define VF_FIELD_SQRT(v) sqrt(v)
#define VF_FIELD_MIN(a,b) min(a,b)
#define VF_FIELD_MAX(a,b) max(a,b)
#define VF_FIELD_CLAMP(v,a,b) clamp(v,a,b)
#define VF_FIELD_LERP(a,b,t) lerp(a,b,t)
#else
#include <algorithm>
#include <cmath>
#include <cstdint>
#define VF_FIELD_INLINE inline
typedef std::int32_t VF_FieldI32;
typedef std::uint32_t VF_FieldU32;
typedef double VF_FieldReal;
#define VF_FIELD_CAST_I32(v) static_cast<VF_FieldI32>(v)
#define VF_FIELD_CAST_U32(v) static_cast<VF_FieldU32>(v)
#define VF_FIELD_CAST_F32(v) static_cast<float>(v)
#define VF_FIELD_FLOOR(v) std::floor(v)
#define VF_FIELD_ABS(v) std::abs(v)
#define VF_FIELD_SQRT(v) std::sqrt(v)
#define VF_FIELD_MIN(a,b) std::min(a,b)
#define VF_FIELD_MAX(a,b) std::max(a,b)
#define VF_FIELD_CLAMP(v,a,b) std::clamp(v,a,b)
#define VF_FIELD_LERP(a,b,t) std::lerp(a,b,t)
#endif
// High-level far-field primitive data. The CPU instance uses double intermediates to retain the
// reference path's precision; HLSL instantiates the same fields and routines as float. The packed
// upload record is a separate float-only API type with the same semantic members.
struct VF_FieldPrimitive
{
VF_FieldI32 flags;
VF_FieldI32 minVoxel[3], maxVoxel[3];
VF_FieldReal center[3], q[9], minRadius;
VF_FieldReal capsuleStart[3], capsuleDelta[3];
VF_FieldReal capsuleAxisLengthSquared, capsuleAxisLength;
VF_FieldReal capsuleStartRadius, capsuleEndRadius;
VF_FieldReal profileStartT, profileEndT, profileStartRadius, profileEndRadius;
VF_FieldReal radiusSlopeStart, radiusSlopeEnd;
VF_FieldReal roundedHalfExtent[3], rounding;
VF_FieldReal blendRadius, floorBlendRadius;
VF_FieldReal floorCenterZ, floorSlopeX, floorSlopeY;
VF_FieldReal floorCurveOriginX, floorCurveOriginY;
VF_FieldReal floorCurveDeltaX, floorCurveDeltaY, floorCurveLength;
VF_FieldReal floorCurveStartZ, floorCurveEndZ;
VF_FieldReal floorCurveTangentStart, floorCurveTangentEnd;
VF_FieldReal floorCurveStartBlend, floorCurveEndBlend;
};
static const VF_FieldI32 VF_FIELD_PRIMITIVE_CAPSULE = 1;
static const VF_FieldI32 VF_FIELD_PRIMITIVE_SMOOTH_CAPSULE = 2;
static const VF_FieldI32 VF_FIELD_PRIMITIVE_ROUNDED_BOX = 4;
static const VF_FieldI32 VF_FIELD_PRIMITIVE_HAS_FLOOR = 8;
static const VF_FieldI32 VF_FIELD_PRIMITIVE_CURVED_FLOOR = 16;
static const VF_FieldI32 VF_FIELD_PRIMITIVE_CARVE = 32;
static const VF_FieldI32 VF_FIELD_PRIMITIVE_CRITICAL = 64;
VF_FIELD_INLINE VF_FieldReal VF_FieldEvaluateFloorProfile(VF_FieldReal a, VF_FieldReal b,
VF_FieldReal tangentA, VF_FieldReal tangentB, VF_FieldReal length,
VF_FieldReal startBlend, VF_FieldReal endBlend, VF_FieldReal s)
{
const VF_FieldReal secant = (b - a) / length;
if (s < 0.0) return a + tangentA * s;
if (s > length) return b + tangentB * (s - length);
VF_FieldReal result = VF_FIELD_LERP(a, b, s / length);
if (startBlend > 0.0 && s < startBlend)
{
const VF_FieldReal t = s / startBlend;
const VF_FieldReal correction = (tangentA - secant) * startBlend
* (t - 2.0 * t * t + t * t * t);
result += correction;
}
if (endBlend > 0.0 && length - s < endBlend)
{
const VF_FieldReal t = (length - s) / endBlend;
const VF_FieldReal correction = (tangentB - secant) * endBlend
* (t - 2.0 * t * t + t * t * t);
result -= correction;
}
return result;
}
VF_FIELD_INLINE VF_FieldReal VF_FieldPrimitiveFloorAt(VF_FieldPrimitive p,
VF_FieldReal x, VF_FieldReal y)
{
if ((p.flags & VF_FIELD_PRIMITIVE_CURVED_FLOOR) != 0)
{
const VF_FieldReal t = ((x - p.floorCurveOriginX) * p.floorCurveDeltaX
+ (y - p.floorCurveOriginY) * p.floorCurveDeltaY)
/ (p.floorCurveLength * p.floorCurveLength);
return VF_FieldEvaluateFloorProfile(p.floorCurveStartZ, p.floorCurveEndZ,
p.floorCurveTangentStart, p.floorCurveTangentEnd, p.floorCurveLength,
p.floorCurveStartBlend, p.floorCurveEndBlend, t * p.floorCurveLength);
}
return p.floorCenterZ + p.floorSlopeX * (x - p.center[0])
+ p.floorSlopeY * (y - p.center[1]);
}
VF_FIELD_INLINE float VF_FieldCapsuleRadiusAtT(VF_FieldPrimitive p,
VF_FieldReal t, VF_FieldReal axisLength)
{
t = VF_FIELD_CLAMP(t, 0.0, 1.0);
if ((p.flags & VF_FIELD_PRIMITIVE_SMOOTH_CAPSULE) == 0)
return VF_FIELD_CAST_F32(p.capsuleStartRadius
+ (p.capsuleEndRadius - p.capsuleStartRadius) * t);
const VF_FieldReal t0 = p.profileStartT, t1 = p.profileEndT;
if (t < t0)
return VF_FIELD_CAST_F32(p.profileStartRadius
+ p.radiusSlopeStart * axisLength * (t - t0));
if (t > t1)
return VF_FIELD_CAST_F32(p.profileEndRadius
+ p.radiusSlopeEnd * axisLength * (t - t1));
const VF_FieldReal spanT = VF_FIELD_MAX(1.0e-9, t1 - t0);
const VF_FieldReal u = VF_FIELD_CLAMP((t - t0) / spanT, 0.0, 1.0);
const VF_FieldReal u2 = u * u, u3 = u2 * u;
const VF_FieldReal spanDistance = axisLength * spanT;
const VF_FieldReal h00 = 2.0 * u3 - 3.0 * u2 + 1.0;
const VF_FieldReal h10 = u3 - 2.0 * u2 + u;
const VF_FieldReal h01 = -2.0 * u3 + 3.0 * u2;
const VF_FieldReal h11 = u3 - u2;
return VF_FIELD_CAST_F32(h00 * p.profileStartRadius
+ h10 * spanDistance * p.radiusSlopeStart
+ h01 * p.profileEndRadius
+ h11 * spanDistance * p.radiusSlopeEnd);
}
VF_FIELD_INLINE float VF_FieldPrimitiveDistance(VF_FieldPrimitive p,
VF_FieldReal x, VF_FieldReal y, VF_FieldReal z)
{
if ((p.flags & VF_FIELD_PRIMITIVE_CAPSULE) != 0)
{
const VF_FieldReal position[3] = {x, y, z};
VF_FieldReal v[3], d[3], vd = 0.0;
for (VF_FieldI32 axis = 0; axis < 3; ++axis)
{
v[axis] = position[axis] - p.capsuleStart[axis];
d[axis] = p.capsuleDelta[axis];
vd += v[axis] * d[axis];
}
const VF_FieldReal d2 = p.capsuleAxisLengthSquared;
const VF_FieldReal t = d2 > 1.0e-12 ? VF_FIELD_CLAMP(vd / d2, 0.0, 1.0) : 0.0;
VF_FieldReal distanceSquared = 0.0;
for (VF_FieldI32 axis = 0; axis < 3; ++axis)
{
const VF_FieldReal e = v[axis] - t * d[axis];
distanceSquared += e * e;
}
const float radius = VF_FieldCapsuleRadiusAtT(p, t, p.capsuleAxisLength);
return VF_FIELD_CAST_F32(VF_FIELD_SQRT(distanceSquared) - radius);
}
if ((p.flags & VF_FIELD_PRIMITIVE_ROUNDED_BOX) != 0)
{
const VF_FieldReal d[3] = {
VF_FIELD_ABS(x - p.center[0]) - p.roundedHalfExtent[0],
VF_FIELD_ABS(y - p.center[1]) - p.roundedHalfExtent[1],
VF_FIELD_ABS(z - p.center[2]) - p.roundedHalfExtent[2]};
const VF_FieldReal ox = VF_FIELD_MAX(d[0], 0.0);
const VF_FieldReal oy = VF_FIELD_MAX(d[1], 0.0);
const VF_FieldReal oz = VF_FIELD_MAX(d[2], 0.0);
const VF_FieldReal outside = VF_FIELD_SQRT(ox * ox + oy * oy + oz * oz);
const VF_FieldReal inside = VF_FIELD_MIN(VF_FIELD_MAX(d[0], VF_FIELD_MAX(d[1], d[2])), 0.0);
return VF_FIELD_CAST_F32(outside + inside - p.rounding);
}
const VF_FieldReal d[3] = {x - p.center[0], y - p.center[1], z - p.center[2]};
VF_FieldReal q = 0.0;
for (VF_FieldI32 i = 0; i < 3; ++i)
for (VF_FieldI32 j = 0; j < 3; ++j)
q += p.q[i * 3 + j] * d[i] * d[j] * 256.0;
return VF_FIELD_CAST_F32((VF_FIELD_SQRT(VF_FIELD_MAX(0.0, q)) - 1.0) * p.minRadius);
}
VF_FIELD_INLINE VF_FieldU32 VF_FieldMix(VF_FieldU32 x)
{
x ^= x >> 16;
@@ -94,7 +254,7 @@ VF_FIELD_INLINE float VF_FieldSmoothUnit(float value)
#if defined(VF_FIELD_HLSL)
value = clamp(value, 0.0f, 1.0f);
#else
value = value < 0.0f ? 0.0f : (value > 1.0f ? 1.0f : value);
value = std::clamp(value, 0.0f, 1.0f);
#endif
return value * value * (3.0f - 2.0f * value);
}
@@ -102,7 +262,11 @@ VF_FIELD_INLINE float VF_FieldSmoothUnit(float value)
VF_FIELD_INLINE float VF_FieldSmoothMin(float a, float b, float width)
{
if (width <= 0.0f)
#if defined(VF_FIELD_HLSL)
return a < b ? a : b;
#else
return std::min(a, b);
#endif
const float delta = a - b;
#if defined(VF_FIELD_HLSL)
const float absolute = abs(delta);
@@ -110,12 +274,17 @@ VF_FIELD_INLINE float VF_FieldSmoothMin(float a, float b, float width)
const float h = max(width - absolute, 0.0f) / width;
#else
const float absolute = std::abs(delta);
const float minimum = a < b ? a : b;
const float h = (width - absolute) > 0.0f ? (width - absolute) / width : 0.0f;
const float minimum = std::min(a, b);
const float h = std::max(width - absolute, 0.0f) / width;
#endif
return minimum - (h * h * h) * (width / 6.0f);
}
VF_FIELD_INLINE float VF_FieldSmoothMax(float a, float b, float width)
{
return -VF_FieldSmoothMin(-a, -b, width);
}
// CPU and GPU use the same edge-root rule. The optional midpoint is the exact analytic
// field evaluation used by the reference LOD0 mesher for roots close to an edge midpoint.
VF_FIELD_INLINE float VF_FieldRefineRoot(float d1, float d2, float midpoint,
@@ -171,3 +340,9 @@ VF_FIELD_INLINE float VF_FieldRefineRoot(float d1, float d2, float midpoint,
#undef VF_FIELD_CAST_U32
#undef VF_FIELD_CAST_F32
#undef VF_FIELD_FLOOR
#undef VF_FIELD_ABS
#undef VF_FIELD_SQRT
#undef VF_FIELD_MIN
#undef VF_FIELD_MAX
#undef VF_FIELD_CLAMP
#undef VF_FIELD_LERP