design: world assembler -- Luna D2 + Claude response
Design-only. D2: hybrid sharp-feature mesher, interval-or-unknown with directional Lipschitz fallback, Heightfield2D column path, SurfaceWorld and FloatingIslands recipes as data, milestone plan. Claude: plan shipped in season manifest?, surface first, every milestone in game. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
61ce0dee4b
commit
1cf25eff30
3 files changed
+623
No files matched your search
@@ -0,0 +1,176 @@
|
||||
"""Small analytic D2 measurements; no engine/project build required.
|
||||
|
||||
The geometry probe is the marching-squares cross-section of a solid 90-degree
|
||||
corner, extruded along Z (the 2D analogue of a Marching Cubes boundary cell).
|
||||
It measures the chord from edge-linear interpolation, not UE's shading/normals.
|
||||
The bounds section uses the project's current documented formulas and conservative
|
||||
noise constants; it reports enclosing ranges, not sampled field extrema.
|
||||
"""
|
||||
import math
|
||||
|
||||
H_M = 0.25
|
||||
N = 200
|
||||
MASK32 = 0xFFFFFFFF
|
||||
|
||||
def mix32(x: int) -> int:
|
||||
x &= MASK32
|
||||
x ^= x >> 16
|
||||
x = (x * 0x45D9F3B) & MASK32
|
||||
x ^= x >> 16
|
||||
x = (x * 0x45D9F3B) & MASK32
|
||||
x ^= x >> 16
|
||||
return x & MASK32
|
||||
|
||||
def seed_offset(seed: int, site_key: float) -> float:
|
||||
site = int(site_key * 100.0 + 0.5) & MASK32
|
||||
return float(mix32((seed ^ ((site * 2654435761) & MASK32)) & MASK32) & 0x3FFF)
|
||||
|
||||
def corner_hash(ix: int, iy: int, iz: int) -> int:
|
||||
h = (((ix & MASK32) * 0x9E3779B1) ^ ((iy & MASK32) * 0x85EBCA77)
|
||||
^ ((iz & MASK32) * 0xC2B2AE3D)) & MASK32
|
||||
h ^= h >> 15
|
||||
h = (h * 0x2C1B3C6D) & MASK32
|
||||
h ^= h >> 12
|
||||
h = (h * 0x297A2D39) & MASK32
|
||||
h ^= h >> 15
|
||||
return h & MASK32
|
||||
|
||||
def fade(t: float) -> float:
|
||||
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
|
||||
|
||||
def grad_dot(h: int, x: float, y: float, z: float) -> float:
|
||||
h &= 15
|
||||
u = x if (h & 8) == 0 else y
|
||||
if (h & 12) == 0:
|
||||
v = y
|
||||
elif (h & 13) == 12:
|
||||
v = x
|
||||
else:
|
||||
v = z
|
||||
return (u if (h & 1) == 0 else -u) + (v if (h & 2) == 0 else -v)
|
||||
|
||||
def perlin3(x: float, y: float, z: float) -> float:
|
||||
ix, iy, iz = math.floor(x), math.floor(y), math.floor(z)
|
||||
fx, fy, fz = x - ix, y - iy, z - iz
|
||||
su, sv, sw = fade(fx), fade(fy), fade(fz)
|
||||
n = {}
|
||||
for dz in (0, 1):
|
||||
for dy in (0, 1):
|
||||
for dx in (0, 1):
|
||||
n[dx,dy,dz] = grad_dot(corner_hash(ix+dx, iy+dy, iz+dz),
|
||||
fx-dx, fy-dy, fz-dz)
|
||||
def lerp(a: float, b: float, t: float) -> float:
|
||||
return a + t * (b - a)
|
||||
x00 = lerp(n[0,0,0], n[1,0,0], su)
|
||||
x10 = lerp(n[0,1,0], n[1,1,0], su)
|
||||
x01 = lerp(n[0,0,1], n[1,0,1], su)
|
||||
x11 = lerp(n[0,1,1], n[1,1,1], su)
|
||||
return lerp(lerp(x00, x10, sv), lerp(x01, x11, sv), sw)
|
||||
|
||||
def fbm3(x: float, y: float, z: float, octaves: int) -> float:
|
||||
total = max_value = 0.0
|
||||
freq, amp = 1.0, 1.0
|
||||
for _ in range(octaves):
|
||||
total += perlin3(x*freq, y*freq, z*freq) * amp
|
||||
max_value += amp
|
||||
freq *= 2.0
|
||||
amp *= 0.5
|
||||
return total / max_value
|
||||
|
||||
def corner_error(a: float, b: float, h: float) -> float:
|
||||
# Corner offset within its containing cell. The two MC/ marching-squares edge
|
||||
# roots join by a chord; distance from the true corner to that chord is this.
|
||||
return a * b / math.hypot(a, b) if a or b else 0.0
|
||||
|
||||
samples = [corner_error((i + 0.5) * H_M / N,
|
||||
(j + 0.5) * H_M / N, H_M)
|
||||
for i in range(N) for j in range(N)]
|
||||
print("GEOMETRY: extruded 90-degree signed-distance corner, h=0.25 m")
|
||||
print(f"mid-cell phase (a=b=h/2): {corner_error(H_M/2,H_M/2,H_M)*100:.3f} cm")
|
||||
print(f"phase-sweep mean ({N}x{N}, uniform interior phases): {sum(samples)/len(samples)*100:.3f} cm")
|
||||
print(f"phase-sweep max: {max(samples)*100:.3f} cm")
|
||||
print(f"analytic worst phase limit (a,b -> h): {H_M/math.sqrt(2)*100:.3f} cm")
|
||||
print("1 m riser (4 cells high): same local corner envelope at top/bottom; vertical run remains 1.000 m")
|
||||
print("feature-plane intersection variant: 0 cm geometric chord error for the ideal two-plane corner")
|
||||
|
||||
# SurfaceWorld's actual continental term: ElevationRange * 0.5 * ContScale * FBM4.
|
||||
# Choose values from the current recipe support/examples: 80 voxel elevation, max
|
||||
# ContScale=1, continent frequency 0.001 per voxel; 25 cm voxels.
|
||||
vox_m = 0.25
|
||||
perlin_grad = (15.0 / 4.0) * math.sqrt(3.0) # VoxelNoise.h proof
|
||||
persistence, lacunarity, octaves = 0.5, 2.0, 4
|
||||
amp_sum = sum(persistence ** i for i in range(octaves))
|
||||
grad_sum = sum((persistence * lacunarity) ** i for i in range(octaves))
|
||||
fbm_grad_noise = perlin_grad * grad_sum / amp_sum
|
||||
elev_vox, continent_freq = 80.0, 0.001
|
||||
continent_amp_m = elev_vox * 0.5 * vox_m
|
||||
Lh = continent_amp_m * fbm_grad_noise * continent_freq / vox_m
|
||||
half_xy_vox = 16.0 # 8 m cube => 4 m half-width = 16 voxels
|
||||
half_z_vox = 16.0
|
||||
lip_radius_vox = half_z_vox + Lh * math.sqrt(2.0) * half_xy_vox
|
||||
interval_radius_vox = half_z_vox + elev_vox * 0.5 * 1.5 # proved global FBM [-1.5,1.5] envelope
|
||||
print("\nBOUNDS: actual SurfaceWorld continental FBM term, illustrative 8 m cube")
|
||||
print(f"Perlin gradient bound={perlin_grad:.4f}; FBM4 gradient/coordinate={fbm_grad_noise:.4f}")
|
||||
print(f"height amplitude={continent_amp_m:.3f} m; height Lipschitz bound={Lh:.4f} m/m")
|
||||
print(f"center Lipschitz (axis-aware z + XY) radius={lip_radius_vox*vox_m:.3f} m; range width={2*lip_radius_vox*vox_m:.3f} m")
|
||||
print(f"global amplitude interval radius={interval_radius_vox*vox_m:.3f} m; range width={2*interval_radius_vox*vox_m:.3f} m")
|
||||
print("Note: interval uses the proved |FBM|<=1.5 envelope, not the tempting but unsound 1.0 assumption; a local interval/noise-cell proof can tighten it.")
|
||||
seed = 9320
|
||||
x0, y0 = 1000.0, -700.0
|
||||
zoff = seed_offset(seed, 0.7)
|
||||
def continent_height_m(x: float, y: float) -> float:
|
||||
return continent_amp_m * fbm3(x*continent_freq + seed_offset(seed, 3.1),
|
||||
y*continent_freq + seed_offset(seed, 5.7), zoff, 4)
|
||||
center_h = continent_height_m(x0 + 16.0, y0 + 16.0)
|
||||
surface_samples = [continent_height_m(x0 + 32.0*ix/64.0,
|
||||
y0 + 32.0*iy/64.0)
|
||||
for ix in range(65) for iy in range(65)]
|
||||
sampled_dmin = -4.0 - (max(surface_samples) - center_h)
|
||||
sampled_dmax = 4.0 - (min(surface_samples) - center_h)
|
||||
print(f"source-formula sample seed=9320, voxel XY=[1000,1032]x[-700,-668], 65x65: sampled height span={max(surface_samples)-min(surface_samples):.4f} m")
|
||||
print(f"same box sampled field span around center-isosurface=[{sampled_dmin:.4f},{sampled_dmax:.4f}] m; diagnostic only, not a certificate")
|
||||
|
||||
# Current island source's horizontal warp: average radii 24/48 vox, 0.35 gain,
|
||||
# FBM3 absolute bound 1.5, VOXEL_NOISE_SCALE 1.25. Query cube side is 4 m (16 vox).
|
||||
rmin, rmax, voxel_scale, fbm_abs = 24.0, 48.0, 1.25, 1.5
|
||||
warp_amp = (rmin + rmax) * 0.5 * 0.35
|
||||
warp_axis = warp_amp * voxel_scale * fbm_abs
|
||||
warp_vec = math.sqrt(2.0) * warp_axis
|
||||
half_patch = 8.0
|
||||
query_center_dx = 48.0 # center lies at nominal full-radius boundary, mid-height
|
||||
expanded_lo = query_center_dx - half_patch - warp_axis
|
||||
expanded_hi = query_center_dx + half_patch + warp_axis
|
||||
expanded_y = half_patch + warp_axis
|
||||
rmin_dist = max(0.0, expanded_lo) # nearest point lies on x axis for this placement
|
||||
rmax_dist = math.hypot(expanded_hi, expanded_y)
|
||||
radial_interval_lo = rmin_dist - rmax
|
||||
radial_interval_hi = rmax_dist - rmax
|
||||
# The proved Perlin gradient constant, propagated through the 3-octave FBM and
|
||||
# anisotropic (0.04,0.04,0.012)/voxel domain warp; use Frobenius as a safe Jacobian bound.
|
||||
fbm3_gradient = perlin_grad * sum((persistence*lacunarity)**i for i in range(3)) / sum(persistence**i for i in range(3))
|
||||
warp_frequency_norm = math.sqrt(0.04**2 + 0.04**2 + 0.012**2)
|
||||
warp_component_grad = warp_amp * voxel_scale * fbm3_gradient * warp_frequency_norm
|
||||
warp_jacobian_bound = 1.0 + math.sqrt(2.0) * warp_component_grad
|
||||
cube_halfdiag = math.sqrt(3.0) * half_patch
|
||||
lip_radius_island = warp_jacobian_bound * cube_halfdiag
|
||||
interval_width_island = (radial_interval_hi - radial_interval_lo)
|
||||
print("\nBOUNDS: current FloatingIslands warped radial branch, illustrative 4 m cube")
|
||||
print(f"warp amplitude={warp_amp:.3f} vox; per-axis displacement<= {warp_axis:.3f} vox; XY vector displacement<= {warp_vec:.3f} vox")
|
||||
print(f"expanded query side={16.0 + 2*warp_axis:.3f} vox ({(16.0 + 2*warp_axis)*vox_m:.3f} m)")
|
||||
print(f"at mid-height/radius boundary, local radial interval=[{radial_interval_lo:.3f},{radial_interval_hi:.3f}] vox; width={interval_width_island:.3f} vox ({interval_width_island*vox_m:.3f} m)")
|
||||
print(f"proved Perlin FBM3 gradient={fbm3_gradient:.3f}; warp Jacobian factor<={warp_jacobian_bound:.3f}")
|
||||
print(f"center Lipschitz radial-branch radius<= {lip_radius_island:.3f} vox ({lip_radius_island*vox_m:.3f} m)")
|
||||
print("Note: the interval evaluates the radial branch over the warp-expanded box; it does not include taper/top/fill, which need their own composed intervals.")
|
||||
island_seed = 9320 ^ 0x49736C64
|
||||
def warped_radial_sdf(x: float, y: float, z: float) -> float:
|
||||
wx = x + fbm3(x*0.04 + seed_offset(island_seed, 0.0007), y*0.04,
|
||||
z*0.012, 3) * voxel_scale * warp_amp
|
||||
wy = y + fbm3(x*0.04 + 31.0, y*0.04 + 7.0,
|
||||
z*0.012, 3) * voxel_scale * warp_amp
|
||||
return math.hypot(wx, wy) - rmax
|
||||
island_samples = [warped_radial_sdf(40.0 + 16.0*ix/32.0,
|
||||
-8.0 + 16.0*iy/32.0,
|
||||
67.0 + 16.0*iz/8.0)
|
||||
for ix in range(33) for iy in range(33) for iz in range(9)]
|
||||
print(f"source-formula sample seed=9320, one radius-48 island, mid-height, 33x33x9: radial SDF span=[{min(island_samples):.3f},{max(island_samples):.3f}] vox, width={(max(island_samples)-min(island_samples))*vox_m:.3f} m")
|
||||
print("sample uses VoxelNoise.h scalar hash-gradient Perlin/FBM formulas in Python doubles; not a cross-platform hash test")
|
||||
Reference in new issue
Block a user