fix(render3d): col_resolve threw a body ~1000x too far from dead centre
The degenerate case - a point exactly on a collider's axis, where there is no direction to push it - set the normal to (1, 0), which is already a unit vector, and then divided it by the clamped d = 0.001 along with the genuine normals. The push came out a thousand times too big: dead centre on a 0.5 m trunk moved a body about 800 m rather than the 0.85 m that clears it. Off-centre the arithmetic was right, and off-centre is how anything arrives at a trunk on foot, so nothing in play ever hit it. A teleport, a spawn, or a world generator dropping something onto an existing collider would have. (ex, ez) / d is a unit vector for every d > 0, because d is its own length - there was never anything to clamp and nothing that could grow. The normal is now built once and explicitly, and the clamp is gone. Verified through a game, which is the only harness this package has: render3d's own float helpers need the Gl runtime spliced, so collide.ludic cannot be compiled standalone for a unit test. Maroon Lake's selftest12 stands a body dead centre on a trunk and asserts the push never exceeds the two radii added together - which is the definition of being pushed clear, and so the tightest honest bound available. It reports 798.88 m before this change and 0.80 m after, with the off-centre case unchanged at 0.66 m.
This commit is contained in:
parent
b16b7aaf0b
commit
219e638316
2 changed files with 32 additions and 5 deletions
16
changes/collide-centre.md
Normal file
16
changes/collide-centre.md
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
bump: patch
|
||||||
|
type: fix
|
||||||
|
**`col_resolve` no longer throws a body across the map when it starts dead centre on a
|
||||||
|
collider.** The degenerate branch — a point exactly on a circle's axis, where there is
|
||||||
|
no direction to push it — chose a unit normal `(1, 0)` and then divided it by the
|
||||||
|
clamped `d = 0.001` anyway, along with the real normals. The push came out a thousand
|
||||||
|
times too large: a body standing exactly on a 0.5 m trunk was moved about 800 m instead
|
||||||
|
of the 0.85 m that clears it.
|
||||||
|
|
||||||
|
Off-centre the arithmetic was correct, and off-centre is how anything arrives at a
|
||||||
|
trunk while walking, so it never showed up in play — only a teleport, a spawn or a
|
||||||
|
world generator placing something on an existing collider could land on the axis.
|
||||||
|
|
||||||
|
`(ex, ez) / d` is always a unit vector for `d > 0`, because `d` is its own length: there
|
||||||
|
was never anything to clamp. The normal is now built once, explicitly, and the clamp is
|
||||||
|
gone.
|
||||||
|
|
@ -72,12 +72,23 @@ function col_resolve(px: int, pz: int, pr: int) -> bool {
|
||||||
let d2 = f_add(f_mul(ex, ex), f_mul(ez, ez))
|
let d2 = f_add(f_mul(ex, ex), f_mul(ez, ez))
|
||||||
let rr = f_add(col_r[i], pr)
|
let rr = f_add(col_r[i], pr)
|
||||||
if f_ls(d2, f_mul(rr, rr)) {
|
if f_ls(d2, f_mul(rr, rr)) {
|
||||||
var d = f_sqrt(d2)
|
let d = f_sqrt(d2)
|
||||||
var nx = ex; var nz = ez
|
# The UNIT normal out of this circle. (ex, ez) / d is always unit for d > 0,
|
||||||
if f_ls(d, fl(0.001)) { d = fl(0.001); nx = F_ONE; nz = F_ZERO }
|
# 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
|
||||||
|
# do and +x is as good as another.
|
||||||
|
#
|
||||||
|
# This used to clamp d to 0.001 and then divide by it, having ALREADY set the
|
||||||
|
# degenerate normal to (1, 0): a unit vector divided by a thousandth, so the
|
||||||
|
# push came out a thousand times too big. A body standing dead centre on a
|
||||||
|
# 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)
|
let push = f_sub(rr, d)
|
||||||
x = f_add(x, f_mul(f_div(nx, d), push))
|
x = f_add(x, f_mul(ux, push))
|
||||||
z = f_add(z, f_mul(f_div(nz, d), push))
|
z = f_add(z, f_mul(uz, push))
|
||||||
moved = true
|
moved = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue