feat(lang): strict numbers in float files; render3d on float
A numbers float file adapts decimal literals to a fixed operand or slot, and refuses to promote a computed int to a float implicitly: there it is almost always float bits. Explicit float(x) is always allowed. render3d's numbers are float, converted by tools/migrate/floatbits.py - a whole-program inference of which ints carried IEEE bits (union-find over flows, calls, returns, buffers, nested buffers and lexical scopes) and a rewriter to operators, Math.* and float literals, with float_bits / float_from_bits left only where bits really cross (runtime scratch buffers, mixed buffers). Seed regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
3ac0d8d5cb
commit
cc89fc37a4
35 changed files with 57282 additions and 54382 deletions
|
|
@ -9,31 +9,31 @@
|
|||
const COL_CELL: int = 16
|
||||
const COL_CAP: int = 120000
|
||||
|
||||
var col_x: words = null
|
||||
var col_z: words = null
|
||||
var col_r: words = null
|
||||
var col_x: floats = null
|
||||
var col_z: floats = null
|
||||
var col_r: floats = null
|
||||
# What a collider occupies VERTICALLY: y0 its base, y1 its top, metres, float bits. A circle used
|
||||
# to be an infinite pillar - you could not climb a boulder, and a knee-high rock stopped you dead,
|
||||
# because there was no height to compare against. col_add keeps that shape (a span from far below
|
||||
# to far above) so every existing caller behaves exactly as it did; col_add_h gives a real one.
|
||||
var col_y0: words = null
|
||||
var col_y1: words = null
|
||||
var col_y0: floats = null
|
||||
var col_y1: floats = null
|
||||
var col_n: int = 0
|
||||
var col_side: int = 0 # cells per side
|
||||
var col_start: words = null # per cell: first index into col_sorted (side*side + 1)
|
||||
var col_sorted: words = null
|
||||
var col_built: bool = false
|
||||
var col_out: words = null # the resolved position (x, z)
|
||||
var col_out: floats = null # the resolved position (x, z)
|
||||
|
||||
const COL_LOW: int = 0xCB800000 # -16777216.0: below any ground
|
||||
const COL_HIGH: int = 0x4B800000 # 16777216.0: above any sky
|
||||
function col_add(x: int, z: int, r: int) -> void { col_add_h(x, z, r, COL_LOW, COL_HIGH) }
|
||||
const COL_LOW: float = -16777216.0 # -16777216.0: below any ground
|
||||
const COL_HIGH: float = 16777216.0 # 16777216.0: above any sky
|
||||
function col_add(x: float, z: float, r: float) -> void { col_add_h(x, z, r, COL_LOW, COL_HIGH) }
|
||||
# a collider that occupies only y0 .. y1: a body above its top walks over it, a body below its base
|
||||
# passes under, and col_top_at reports it as something to stand on
|
||||
function col_add_h(x: int, z: int, r: int, y0: int, y1: int) -> void {
|
||||
function col_add_h(x: float, z: float, r: float, y0: float, y1: float) -> void {
|
||||
if col_x == null {
|
||||
col_x = words(COL_CAP); col_z = words(COL_CAP); col_r = words(COL_CAP)
|
||||
col_y0 = words(COL_CAP); col_y1 = words(COL_CAP); col_out = words(2)
|
||||
col_x = floats(COL_CAP); col_z = floats(COL_CAP); col_r = floats(COL_CAP)
|
||||
col_y0 = floats(COL_CAP); col_y1 = floats(COL_CAP); col_out = floats(2)
|
||||
}
|
||||
if col_n >= COL_CAP { return }
|
||||
col_x[col_n] = x; col_z[col_n] = z; col_r[col_n] = r
|
||||
|
|
@ -41,8 +41,8 @@ function col_add_h(x: int, z: int, r: int, y0: int, y1: int) -> void {
|
|||
col_n += 1
|
||||
col_built = false
|
||||
}
|
||||
function col_cell_of(v: int, origin: int) -> int {
|
||||
var c = f_to_int(f_floor(f_div(f_add(f_sub(v, origin), fi(TERRAIN_HALF)), fi(COL_CELL))))
|
||||
function col_cell_of(v: float, origin: float) -> int {
|
||||
var c = int(Math.floor((v - origin + float(TERRAIN_HALF)) / float(COL_CELL)))
|
||||
if c < 0 { c = 0 }
|
||||
if c > col_side - 1 { c = col_side - 1 }
|
||||
return c
|
||||
|
|
@ -67,10 +67,10 @@ function col_build() -> void {
|
|||
}
|
||||
|
||||
# push (px, pz) with radius pr out of every circle it overlaps; the result is in col_out
|
||||
function col_resolve(px: int, pz: int, pr: int) -> bool { return col_resolve_at(px, pz, pr, COL_LOW, COL_HIGH) }
|
||||
function col_resolve(px: float, pz: float, pr: float) -> bool { return col_resolve_at(px, pz, pr, COL_LOW, COL_HIGH) }
|
||||
# the same, for a body that occupies feet .. head: a collider whose span misses that is not in the
|
||||
# way at all. This is what lets a hiker stand on top of a boulder rather than inside it.
|
||||
function col_resolve_at(px: int, pz: int, pr: int, feet: int, head: int) -> bool {
|
||||
function col_resolve_at(px: float, pz: float, pr: float, feet: float, head: float) -> bool {
|
||||
col_out[0] = px; col_out[1] = pz
|
||||
if not col_built or col_n == 0 { return false }
|
||||
var x = px; var z = pz
|
||||
|
|
@ -86,14 +86,14 @@ function col_resolve_at(px: int, pz: int, pr: int, feet: int, head: int) -> bool
|
|||
let c = zc * col_side + xc
|
||||
for k in col_start[c] .. col_start[c + 1] {
|
||||
let i = col_sorted[k]
|
||||
let ex = f_sub(x, col_x[i]); let ez = f_sub(z, col_z[i])
|
||||
let d2 = f_add(f_mul(ex, ex), f_mul(ez, ez))
|
||||
let rr = f_add(col_r[i], pr)
|
||||
let ex = x - col_x[i]; let ez = z - col_z[i]
|
||||
let d2 = ex * ex + ez * ez
|
||||
let rr = col_r[i] + pr
|
||||
# nothing to push out of if the body is wholly above its top or below its base
|
||||
if not f_ls(feet, col_y1[i]) { continue }
|
||||
if not f_gt(head, col_y0[i]) { continue }
|
||||
if f_ls(d2, f_mul(rr, rr)) {
|
||||
let d = f_sqrt(d2)
|
||||
if not (feet < col_y1[i]) { continue }
|
||||
if not (head > col_y0[i]) { continue }
|
||||
if d2 < rr * rr {
|
||||
let d = Math.sqrt(d2)
|
||||
# The UNIT normal out of this circle. (ex, ez) / d is always unit for d > 0,
|
||||
# because d is its own length - there is nothing to clamp and nothing that can
|
||||
# grow. Exactly at the centre there is no direction to be had, so any one will
|
||||
|
|
@ -105,11 +105,11 @@ function col_resolve_at(px: int, pz: int, pr: int, feet: int, head: int) -> bool
|
|||
# 0.5 m trunk was thrown roughly 800 m across the map instead of 0.85 m clear
|
||||
# of it. Off-centre - which is how anything actually arrives at a trunk - the
|
||||
# arithmetic was right, so it never showed up in play.
|
||||
var ux = F_ONE; var uz = F_ZERO
|
||||
if f_gt(d, F_ZERO) { ux = f_div(ex, d); uz = f_div(ez, d) }
|
||||
let push = f_sub(rr, d)
|
||||
x = f_add(x, f_mul(ux, push))
|
||||
z = f_add(z, f_mul(uz, push))
|
||||
var ux = 1.0; var uz = 0.0
|
||||
if d > 0.0 { ux = ex / d; uz = ez / d }
|
||||
let push = rr - d
|
||||
x = x + ux * push
|
||||
z = z + uz * push
|
||||
moved = true
|
||||
}
|
||||
}
|
||||
|
|
@ -122,11 +122,11 @@ function col_resolve_at(px: int, pz: int, pr: int, feet: int, head: int) -> bool
|
|||
# The highest collider top under (px, pz) that a body at `feet` could be standing on or step up to:
|
||||
# tops above `reach` are a wall, not a step. F_ZERO-safe: returns `floor` when there is nothing, so
|
||||
# a caller can pass the terrain height and use the answer directly as the ground.
|
||||
function col_top_at(px: int, pz: int, pr: int, feet: int, reach: int, floor: int) -> int {
|
||||
function col_top_at(px: float, pz: float, pr: float, feet: float, reach: float, floor: float) -> float {
|
||||
var top = floor
|
||||
if not col_built or col_n == 0 { return top }
|
||||
let cx = col_cell_of(px, ter_ox); let cz = col_cell_of(pz, ter_oz)
|
||||
let limit = f_add(feet, reach)
|
||||
let limit = feet + reach
|
||||
for dz in 0 .. 3 {
|
||||
let zc = cz + dz - 1
|
||||
if zc < 0 or zc >= col_side { continue }
|
||||
|
|
@ -136,24 +136,24 @@ function col_top_at(px: int, pz: int, pr: int, feet: int, reach: int, floor: int
|
|||
let c = zc * col_side + xc
|
||||
for k in col_start[c] .. col_start[c + 1] {
|
||||
let i = col_sorted[k]
|
||||
let ex = f_sub(px, col_x[i]); let ez = f_sub(pz, col_z[i])
|
||||
let d2 = f_add(f_mul(ex, ex), f_mul(ez, ez))
|
||||
let rr = f_add(col_r[i], pr)
|
||||
if not f_ls(d2, f_mul(rr, rr)) { continue }
|
||||
let ex = px - col_x[i]; let ez = pz - col_z[i]
|
||||
let d2 = ex * ex + ez * ez
|
||||
let rr = col_r[i] + pr
|
||||
if not (d2 < rr * rr) { continue }
|
||||
let t = col_y1[i]
|
||||
if f_gt(t, limit) { continue } # too tall to step onto: it is a wall
|
||||
if f_gt(t, top) { top = t }
|
||||
if t > limit { continue } # too tall to step onto: it is a wall
|
||||
if t > top { top = t }
|
||||
}
|
||||
}
|
||||
}
|
||||
return top
|
||||
}
|
||||
# is the segment from (x0,z0) to (x1,z1) clear of every circle (a camera line of sight)?
|
||||
function col_clear(x0: int, z0: int, x1: int, z1: int, r: int) -> bool {
|
||||
function col_clear(x0: float, z0: float, x1: float, z1: float, r: float) -> bool {
|
||||
let steps = 6
|
||||
for s in 0 .. steps + 1 {
|
||||
let t = fr(s, steps)
|
||||
let x = f_lerp(x0, x1, t); let z = f_lerp(z0, z1, t)
|
||||
let t = float(s) / float(steps)
|
||||
let x = Math.lerp(x0, x1, t); let z = Math.lerp(z0, z1, t)
|
||||
if col_resolve(x, z, r) { return false }
|
||||
}
|
||||
return true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue