Cold first use: near base 4.2 ms, near detail 1,595 ms (the floor), connector rings 630 -> 409, skirt classify+emit ~1,409 -> 366. Production warm-up is still ~2.03-2.06 s; whole view median CPU 1.356 s vs hybrid 3.044 s. Near 960 + far 1,114 tiles: 0 topology/protected/thin diffs vs CPU and WARP; hashes, walk 5,642/5,642, survival pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
541 lines
22 KiB
C++
541 lines
22 KiB
C++
#pragma once
|
|
|
|
// Restricted scalar field math shared by the standalone C++ GPU adapter and its HLSL.
|
|
// Define VF_FIELD_HLSL before including this file from a shader. Keep this header free of
|
|
// containers, engine types, and vector intrinsics so it remains usable by both compilers.
|
|
#if defined(VF_FIELD_HLSL)
|
|
#define VF_FIELD_INLINE
|
|
#define VF_FIELD_LOOP [loop]
|
|
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
|
|
#define VF_FIELD_LOOP
|
|
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;
|
|
static const VF_FieldI32 VF_FIELD_PRIMITIVE_PASSAGE = 128;
|
|
|
|
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;
|
|
x *= 0x7FEB352Du;
|
|
x ^= x >> 15;
|
|
x *= 0x846CA68Bu;
|
|
x ^= x >> 16;
|
|
return x;
|
|
}
|
|
|
|
VF_FIELD_INLINE VF_FieldU32 VF_FieldNoiseHash(VF_FieldI32 x, VF_FieldI32 y,
|
|
VF_FieldI32 z, VF_FieldU32 seed)
|
|
{
|
|
VF_FieldU32 h = VF_FieldMix(seed ^ 0xB5297A4Du);
|
|
h = VF_FieldMix(h ^ VF_FIELD_CAST_U32(x) * 0x68E31DA4u);
|
|
h = VF_FieldMix(h ^ VF_FIELD_CAST_U32(y) * 0x1B56C4E9u);
|
|
return VF_FieldMix(h ^ VF_FIELD_CAST_U32(z) * 0x9E3779B9u);
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldNoiseValue(VF_FieldI32 x, VF_FieldI32 y,
|
|
VF_FieldI32 z, VF_FieldU32 seed)
|
|
{
|
|
return VF_FIELD_CAST_F32(VF_FieldNoiseHash(x, y, z, seed) >> 8)
|
|
* (2.0f / 16777215.0f) - 1.0f;
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldFade(float t)
|
|
{
|
|
return t * t * (3.0f - 2.0f * t);
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldLerp(float a, float b, float t)
|
|
{
|
|
return a + (b - a) * t;
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldValueNoise(float x, float y, float z,
|
|
VF_FieldI32 wavelength, VF_FieldU32 seed)
|
|
{
|
|
const float inv = 1.0f / VF_FIELD_CAST_F32(wavelength);
|
|
const float fx = x * inv, fy = y * inv, fz = z * inv;
|
|
const VF_FieldI32 x0 = VF_FIELD_CAST_I32(VF_FIELD_FLOOR(fx));
|
|
const VF_FieldI32 y0 = VF_FIELD_CAST_I32(VF_FIELD_FLOOR(fy));
|
|
const VF_FieldI32 z0 = VF_FIELD_CAST_I32(VF_FIELD_FLOOR(fz));
|
|
const float tx = VF_FieldFade(fx - VF_FIELD_CAST_F32(x0));
|
|
const float ty = VF_FieldFade(fy - VF_FIELD_CAST_F32(y0));
|
|
const float tz = VF_FieldFade(fz - VF_FIELD_CAST_F32(z0));
|
|
const float z00 = VF_FieldLerp(VF_FieldNoiseValue(x0, y0, z0, seed),
|
|
VF_FieldNoiseValue(x0 + 1, y0, z0, seed), tx);
|
|
const float z10 = VF_FieldLerp(VF_FieldNoiseValue(x0, y0 + 1, z0, seed),
|
|
VF_FieldNoiseValue(x0 + 1, y0 + 1, z0, seed), tx);
|
|
const float z01 = VF_FieldLerp(VF_FieldNoiseValue(x0, y0, z0 + 1, seed),
|
|
VF_FieldNoiseValue(x0 + 1, y0, z0 + 1, seed), tx);
|
|
const float z11 = VF_FieldLerp(VF_FieldNoiseValue(x0, y0 + 1, z0 + 1, seed),
|
|
VF_FieldNoiseValue(x0 + 1, y0 + 1, z0 + 1, seed), tx);
|
|
return VF_FieldLerp(VF_FieldLerp(z00, z10, ty), VF_FieldLerp(z01, z11, ty), tz);
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldSurfaceRelief(float x, float y, float z, VF_FieldU32 seed)
|
|
{
|
|
const float macro = VF_FieldValueNoise(x, y, z, 32, seed ^ 0xB5297A4Du);
|
|
const float mid = VF_FieldValueNoise(x, y, z, 16, seed ^ 0x63D83595u);
|
|
const float fine = VF_FieldValueNoise(x, y, z, 4, seed ^ 0xA511E9B3u);
|
|
return 1.20f * macro + (0.80f + 0.25f * macro) * (0.40f * mid + 0.60f * fine);
|
|
}
|
|
|
|
// Scalar version of VoxelNoise::Perlin3D and its owner-world roughness variant.
|
|
// The integer hash and operation order are shared so the standalone field and HLSL
|
|
// evaluate the same authored detail from the same world parameters.
|
|
VF_FIELD_INLINE VF_FieldU32 VF_FieldVoxelHashMix(VF_FieldU32 x)
|
|
{
|
|
x ^= x >> 16; x *= 0x45d9f3bu;
|
|
x ^= x >> 16; x *= 0x45d9f3bu;
|
|
x ^= x >> 16;
|
|
return x;
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldVoxelSeedOffset(VF_FieldU32 seed,float siteKey)
|
|
{
|
|
const VF_FieldU32 site=VF_FIELD_CAST_U32(siteKey*100.0f+0.5f);
|
|
return VF_FIELD_CAST_F32(VF_FieldVoxelHashMix(seed^(site*2654435761u))&0x3FFFu);
|
|
}
|
|
|
|
VF_FIELD_INLINE VF_FieldU32 VF_FieldPerlinHash(VF_FieldI32 x,VF_FieldI32 y,VF_FieldI32 z)
|
|
{
|
|
VF_FieldU32 h=VF_FIELD_CAST_U32(x)*0x9E3779B1u
|
|
^VF_FIELD_CAST_U32(y)*0x85EBCA77u
|
|
^VF_FIELD_CAST_U32(z)*0xC2B2AE3Du;
|
|
h^=h>>15;h*=0x2C1B3C6Du;
|
|
h^=h>>12;h*=0x297A2D39u;
|
|
h^=h>>15;
|
|
return h;
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldPerlinFade(float t)
|
|
{
|
|
const float inner=t*(t*6.0f-15.0f)+10.0f;
|
|
const float t3=t*t*t;
|
|
return t3*inner;
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldPerlinGrad(VF_FieldU32 hash,float x,float y,float z)
|
|
{
|
|
const VF_FieldU32 h=hash&15u;
|
|
const float u=(h&8u)==0u?x:y;
|
|
float v;
|
|
if((h&12u)==0u)v=y;
|
|
else if((h&13u)==12u)v=x;
|
|
else v=z;
|
|
const float ru=(h&1u)==0u?u:-u;
|
|
const float rv=(h&2u)==0u?v:-v;
|
|
return ru+rv;
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldPerlinLerp(float a,float b,float t)
|
|
{ return a+t*(b-a); }
|
|
|
|
VF_FIELD_INLINE float VF_FieldPerlin3D(float x,float y,float z)
|
|
{
|
|
const float xf=VF_FIELD_FLOOR(x),yf=VF_FIELD_FLOOR(y),zf=VF_FIELD_FLOOR(z);
|
|
const VF_FieldI32 X=VF_FIELD_CAST_I32(xf),Y=VF_FIELD_CAST_I32(yf),Z=VF_FIELD_CAST_I32(zf);
|
|
const float fx=x-xf,fy=y-yf,fz=z-zf;
|
|
const float su=VF_FieldPerlinFade(fx),sv=VF_FieldPerlinFade(fy),sw=VF_FieldPerlinFade(fz);
|
|
const VF_FieldU32 h000=VF_FieldPerlinHash(X,Y,Z),h100=VF_FieldPerlinHash(X+1,Y,Z);
|
|
const VF_FieldU32 h010=VF_FieldPerlinHash(X,Y+1,Z),h110=VF_FieldPerlinHash(X+1,Y+1,Z);
|
|
const VF_FieldU32 h001=VF_FieldPerlinHash(X,Y,Z+1),h101=VF_FieldPerlinHash(X+1,Y,Z+1);
|
|
const VF_FieldU32 h011=VF_FieldPerlinHash(X,Y+1,Z+1),h111=VF_FieldPerlinHash(X+1,Y+1,Z+1);
|
|
const float fx1=fx-1.0f,fy1=fy-1.0f,fz1=fz-1.0f;
|
|
const float n000=VF_FieldPerlinGrad(h000,fx,fy,fz);
|
|
const float n100=VF_FieldPerlinGrad(h100,fx1,fy,fz);
|
|
const float n010=VF_FieldPerlinGrad(h010,fx,fy1,fz);
|
|
const float n110=VF_FieldPerlinGrad(h110,fx1,fy1,fz);
|
|
const float n001=VF_FieldPerlinGrad(h001,fx,fy,fz1);
|
|
const float n101=VF_FieldPerlinGrad(h101,fx1,fy,fz1);
|
|
const float n011=VF_FieldPerlinGrad(h011,fx,fy1,fz1);
|
|
const float n111=VF_FieldPerlinGrad(h111,fx1,fy1,fz1);
|
|
const float x00=VF_FieldPerlinLerp(n000,n100,su);
|
|
const float x10=VF_FieldPerlinLerp(n010,n110,su);
|
|
const float x01=VF_FieldPerlinLerp(n001,n101,su);
|
|
const float x11=VF_FieldPerlinLerp(n011,n111,su);
|
|
const float y0=VF_FieldPerlinLerp(x00,x10,sv);
|
|
const float y1=VF_FieldPerlinLerp(x01,x11,sv);
|
|
return VF_FieldPerlinLerp(y0,y1,sw);
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldOwnerFractal(float x,float y,float z,VF_FieldI32 octaves,
|
|
VF_FieldI32 mode)
|
|
{
|
|
float total=0.0f,maxValue=0.0f,frequency=1.0f,amplitude=1.0f,weight=1.0f;
|
|
VF_FIELD_LOOP
|
|
for(VF_FieldI32 i=0;i<octaves;++i)
|
|
{
|
|
const float noise=VF_FieldPerlin3D(x*frequency,y*frequency,z*frequency);
|
|
if(mode==0)total+=noise*amplitude;
|
|
else
|
|
{
|
|
float ridge=noise*1.25f;
|
|
ridge=1.0f-VF_FIELD_ABS(ridge);
|
|
ridge=ridge*ridge;
|
|
ridge*=weight;
|
|
weight=VF_FIELD_CLAMP(ridge*2.0f,0.0f,1.0f);
|
|
total+=ridge*amplitude;
|
|
}
|
|
maxValue+=amplitude;
|
|
amplitude*=0.5f;
|
|
frequency*=2.0f;
|
|
}
|
|
const float result=total/maxValue;
|
|
return mode==0?result:result*2.0f-1.0f;
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldOwnerCellular(float x,float y,float z)
|
|
{
|
|
const VF_FieldI32 cellX=VF_FIELD_CAST_I32(VF_FIELD_FLOOR(x));
|
|
const VF_FieldI32 cellY=VF_FIELD_CAST_I32(VF_FIELD_FLOOR(y));
|
|
const VF_FieldI32 cellZ=VF_FIELD_CAST_I32(VF_FIELD_FLOOR(z));
|
|
const float fracX=x-VF_FIELD_CAST_F32(cellX),fracY=y-VF_FIELD_CAST_F32(cellY),
|
|
fracZ=z-VF_FIELD_CAST_F32(cellZ);
|
|
float first=3.402823466e+38f,second=3.402823466e+38f;
|
|
VF_FIELD_LOOP
|
|
for(VF_FieldI32 dz=-1;dz<=1;++dz)
|
|
for(VF_FieldI32 dy=-1;dy<=1;++dy)
|
|
for(VF_FieldI32 dx=-1;dx<=1;++dx)
|
|
{
|
|
const VF_FieldI32 nx=cellX+dx,ny=cellY+dy,nz=cellZ+dz;
|
|
const VF_FieldU32 h=VF_FieldVoxelHashMix(
|
|
VF_FIELD_CAST_U32(nx+0x7FFFFFFF)
|
|
^VF_FieldVoxelHashMix(VF_FIELD_CAST_U32(ny+0x7FFFFFFF)*2654435761u)
|
|
^VF_FieldVoxelHashMix(VF_FIELD_CAST_U32(nz+0x7FFFFFFF)*374761393u));
|
|
const float fpx=VF_FIELD_CAST_F32(dx)
|
|
+VF_FIELD_CAST_F32(h&0xFFFFFFu)*(1.0f/16777216.0f)-fracX;
|
|
const VF_FieldU32 hy=VF_FieldVoxelHashMix(h^0x12345678u);
|
|
const VF_FieldU32 hz=VF_FieldVoxelHashMix(h^0x9ABCDEF0u);
|
|
const float fpy=VF_FIELD_CAST_F32(dy)
|
|
+VF_FIELD_CAST_F32(hy&0xFFFFFFu)*(1.0f/16777216.0f)-fracY;
|
|
const float fpz=VF_FIELD_CAST_F32(dz)
|
|
+VF_FIELD_CAST_F32(hz&0xFFFFFFu)*(1.0f/16777216.0f)-fracZ;
|
|
const float distanceSquared=fpx*fpx+fpy*fpy+fpz*fpz;
|
|
if(distanceSquared<first){second=first;first=distanceSquared;}
|
|
else if(distanceSquared<second)second=distanceSquared;
|
|
}
|
|
return (VF_FIELD_SQRT(second)-VF_FIELD_SQRT(first))*2.0f-1.0f;
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldOwnerRoughnessCore(float x,float y,float z,
|
|
VF_FieldU32 seed,float surfaceRoughness,float roughnessFrequency,float verticalScale,
|
|
float domainWarpStrength,float domainWarpFrequency,VF_FieldU32 noiseType)
|
|
{
|
|
if(!(roughnessFrequency>0.0f))return 0.0f;
|
|
const float effectiveZ=verticalScale>0.0f&&verticalScale!=1.0f?z/verticalScale:z;
|
|
const float mainX=x*roughnessFrequency+VF_FieldVoxelSeedOffset(seed,11.3f);
|
|
const float mainY=y*roughnessFrequency+VF_FieldVoxelSeedOffset(seed,13.7f);
|
|
const float mainZ=effectiveZ*roughnessFrequency+VF_FieldVoxelSeedOffset(seed,17.1f);
|
|
float fineX=x*roughnessFrequency*3.0f+VF_FieldVoxelSeedOffset(seed,19.1f)+2000.0f;
|
|
float fineY=y*roughnessFrequency*3.0f+VF_FieldVoxelSeedOffset(seed,23.7f)+2500.0f;
|
|
float fineZ=effectiveZ*roughnessFrequency*3.0f+VF_FieldVoxelSeedOffset(seed,29.3f)+3000.0f;
|
|
float mx=mainX,my=mainY,mz=mainZ;
|
|
if(domainWarpStrength>0.0f)
|
|
{
|
|
const float wf=domainWarpFrequency,ws=domainWarpStrength;
|
|
float wx=VF_FieldPerlin3D(x*wf+VF_FieldVoxelSeedOffset(seed,5.2f),
|
|
y*wf+VF_FieldVoxelSeedOffset(seed,1.3f),
|
|
effectiveZ*wf+VF_FieldVoxelSeedOffset(seed,9.7f));
|
|
float wy=VF_FieldPerlin3D(x*wf+100.0f+VF_FieldVoxelSeedOffset(seed,7.7f),
|
|
y*wf+200.0f+VF_FieldVoxelSeedOffset(seed,3.1f),effectiveZ*wf+300.0f);
|
|
float wz=VF_FieldPerlin3D(x*wf+400.0f,y*wf+500.0f+VF_FieldVoxelSeedOffset(seed,11.9f),
|
|
effectiveZ*wf+600.0f+VF_FieldVoxelSeedOffset(seed,13.3f));
|
|
wx*=1.25f*ws;wy*=1.25f*ws;wz*=1.25f*ws;
|
|
mx+=wx;my+=wy;mz+=wz;
|
|
fineX+=wx;fineY+=wy;fineZ+=wz;
|
|
}
|
|
float rough=0.0f,fine=0.0f;
|
|
if(noiseType==1u)
|
|
{
|
|
rough=VF_FieldOwnerFractal(mx,my,mz,3,1);
|
|
fine=VF_FieldOwnerFractal(fineX,fineY,fineZ,2,1);
|
|
}
|
|
else if(noiseType==2u)
|
|
{
|
|
rough=(VF_FieldOwnerFractal(mx,my,mz,3,0)+VF_FieldOwnerFractal(mx,my,mz,3,1))*0.5f;
|
|
fine=(VF_FieldOwnerFractal(fineX,fineY,fineZ,2,0)+VF_FieldOwnerFractal(fineX,fineY,fineZ,2,1))*0.5f;
|
|
}
|
|
else if(noiseType==3u)
|
|
{
|
|
rough=VF_FieldOwnerCellular(mx,my,mz);
|
|
fine=VF_FieldOwnerCellular(fineX,fineY,fineZ);
|
|
}
|
|
else
|
|
{
|
|
rough=VF_FieldOwnerFractal(mx,my,mz,3,0);
|
|
fine=VF_FieldOwnerFractal(fineX,fineY,fineZ,2,0);
|
|
}
|
|
return (rough+fine*0.4f)*1.25f*surfaceRoughness;
|
|
}
|
|
|
|
VF_FIELD_INLINE float VF_FieldSmoothUnit(float value)
|
|
{
|
|
#if defined(VF_FIELD_HLSL)
|
|
value = clamp(value, 0.0f, 1.0f);
|
|
#else
|
|
value = std::clamp(value, 0.0f, 1.0f);
|
|
#endif
|
|
return value * value * (3.0f - 2.0f * 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);
|
|
const float minimum = min(a, b);
|
|
const float h = max(width - absolute, 0.0f) / width;
|
|
#else
|
|
const float absolute = std::abs(delta);
|
|
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,
|
|
VF_FieldU32 hasMidpoint)
|
|
{
|
|
float t = (d2 - d1 < 1.0e-4f && d1 - d2 < 1.0e-4f)
|
|
? 0.5f : (-d1) / (d2 - d1);
|
|
#if defined(VF_FIELD_HLSL)
|
|
t = clamp(t, 0.0f, 1.0f);
|
|
#else
|
|
t = t < 0.0f ? 0.0f : (t > 1.0f ? 1.0f : t);
|
|
#endif
|
|
// hasMidpoint is an integer CPU decision made from the reference edge parameter. Do not
|
|
// repeat its threshold test in floating point on different GPU implementations.
|
|
if (hasMidpoint != 0u)
|
|
{
|
|
if (midpoint < 1.0e-6f && midpoint > -1.0e-6f)
|
|
return 0.5f;
|
|
if ((midpoint < 0.0f) == (d1 < 0.0f))
|
|
{
|
|
const float denominator = d2 - midpoint;
|
|
if (denominator < -1.0e-4f || denominator > 1.0e-4f)
|
|
{
|
|
float half = (-midpoint) / denominator;
|
|
#if defined(VF_FIELD_HLSL)
|
|
half = clamp(half, 0.0f, 1.0f);
|
|
#else
|
|
half = half < 0.0f ? 0.0f : (half > 1.0f ? 1.0f : half);
|
|
#endif
|
|
return 0.5f + 0.5f * half;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const float denominator = midpoint - d1;
|
|
if (denominator < -1.0e-4f || denominator > 1.0e-4f)
|
|
{
|
|
float half = (-d1) / denominator;
|
|
#if defined(VF_FIELD_HLSL)
|
|
half = clamp(half, 0.0f, 1.0f);
|
|
#else
|
|
half = half < 0.0f ? 0.0f : (half > 1.0f ? 1.0f : half);
|
|
#endif
|
|
return 0.5f * half;
|
|
}
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
|
|
#undef VF_FIELD_INLINE
|
|
#undef VF_FIELD_CAST_I32
|
|
#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
|