ludic/packages/ludic.nav/tests/tiles_test.ludic
Orkuncakilkaya e807fd556c ludic.nav (23.5b): a baked mesh resident by chunk
nav_load_index indexes a saved mesh's tiles (cell, offset, size, ref) from their headers and keeps the file open, with no tile in. nav_tiles_keep reads in the tiles within rin of any player and removes those past rout of all; the file is read through Ludic's pack-aware file_open. The file format is unchanged. tiles_test: tiles come and go with the point, a path works across the seams once they are in, and a reset closes all. Measured on the baked maps (the 900 / 1100 m ring round the start): Maroon 617 of 1807 tiles in, 8.9 MB instead of about 27 MB for a person and 5.4 MB of about 14 for a large walker; Lamar 391 of 1147. Both libraries are rebuilt; nav 17/17 on the Mac and the PC, DLL KERNEL32 only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:07:27 +03:00

97 lines
4.2 KiB
Text

# tiles_test.ludic - ludic.nav's map in tiles: a 128 m meadow as four 64 m tiles, one of them under
# water. A path crosses the tiles' seams, the water's tile has no polygons and nothing inside it is
# a way's end, and the set saved and loaded answers as the built one did.
import "ludic.nav"
import "ludic.base"
import "fakes/meadow.ludic"
program TilesTest {
numbers float
# four tiles over ground(128), the far corner's squares (x and z past 64) under water when wet
function map(nav_st: mut NavState, wet: bool) -> int {
let g = ground(128, 0)
if wet {
for j in 64 .. 128 { for i in 64 .. 128 { g.area[(j * 128 + i) * 2] = 0 } }
for j in 64 .. 128 { for i in 64 .. 128 { g.area[(j * 128 + i) * 2 + 1] = 0 } }
}
g.cyl = post_at(96.0, 20.0, 2.0)
g.nc = 1
expect(nav_tiled(nav_st, NAV_PERSON, 0.0, 0.0, 64.0, 16, 4096))
var empty = 0
for tz in 0 .. 2 {
for tx in 0 .. 2 {
let p = nav_tile_build(nav_st, NAV_PERSON, tx, tz, g, new NavConfig)
expect(p >= 0)
if p == 0 { empty += 1 }
}
}
return empty
}
function last_x(nav_st: NavState) -> float { return nav_corner_x(nav_st, nav_corners(nav_st) - 1) }
function last_z(nav_st: NavState) -> float { return nav_corner_z(nav_st, nav_corners(nav_st) - 1) }
test "a path crosses the tiles' seams and goes round the post in the far tile" (nav_st: mut NavState) {
expect_eq(map(nav_st, false), 0)
expect(nav_polygons(nav_st, NAV_PERSON) > 0)
let n = nav_path(nav_st, NAV_PERSON, 0, 10.0, 0.0, 20.0, 120.0, 0.0, 20.0)
expect(n >= 3)
expect(not nav_partial(nav_st, NAV_PERSON))
expect(Math.abs(last_x(nav_st) - 120.0) < 0.1)
let d = nav_path(nav_st, NAV_PERSON, 0, 10.0, 0.0, 10.0, 120.0, 0.0, 120.0)
expect(d >= 2)
expect(not nav_partial(nav_st, NAV_PERSON))
}
test "a tile under water has no polygons: a way into it is none, the shore beside it is reached" (nav_st: mut NavState) {
expect_eq(map(nav_st, true), 1)
expect(not nav_nearest(nav_st, NAV_PERSON, 100.0, 0.0, 100.0))
expect_eq(nav_path(nav_st, NAV_PERSON, 0, 10.0, 0.0, 10.0, 70.0, 0.0, 70.0), -1)
expect(nav_path(nav_st, NAV_PERSON, 0, 10.0, 0.0, 10.0, 62.0, 0.0, 70.0) >= 2)
expect(not nav_partial(nav_st, NAV_PERSON))
expect(Math.abs(last_z(nav_st) - 70.0) < 0.1)
}
test "the tiles saved and loaded answer as the built ones did" (nav_st: mut NavState) {
map(nav_st, true)
let path = Os.temp_dir() + "/ludic_nav_tiles.navmesh"
expect(nav_save_file(nav_st, NAV_PERSON, path))
expect(nav_load_file(nav_st, NAV_LARGE, path))
expect_eq(nav_polygons(nav_st, NAV_LARGE), nav_polygons(nav_st, NAV_PERSON))
let a = nav_path(nav_st, NAV_PERSON, 0, 10.0, 0.0, 20.0, 120.0, 0.0, 20.0)
let ax = last_x(nav_st)
let b = nav_path(nav_st, NAV_LARGE, 0, 10.0, 0.0, 20.0, 120.0, 0.0, 20.0)
expect_eq(a, b)
expect_near(last_x(nav_st), ax, 0.0001)
}
test "a saved mesh as an index: only the tiles near a player are in, and they come and go" (nav_st: mut NavState) {
expect_eq(map(nav_st, false), 0)
let path = Os.temp_dir() + "/ludic_nav_resident.navmesh"
expect(nav_save_file(nav_st, NAV_PERSON, path))
nav_reset(nav_st)
expect(nav_load_index(nav_st, NAV_PERSON, path))
expect_eq(nav_tiles_in(nav_st, NAV_PERSON), 0)
expect(not nav_nearest(nav_st, NAV_PERSON, 20.0, 0.0, 20.0))
let xs = floats(1)
let zs = floats(1)
xs[0] = 20.0
zs[0] = 20.0
expect_eq(nav_tiles_keep(nav_st, NAV_PERSON, xs, zs, 1, 60.0, 90.0), 1)
expect_eq(nav_tiles_in(nav_st, NAV_PERSON), 1)
expect(nav_nearest(nav_st, NAV_PERSON, 20.0, 0.0, 20.0))
expect(not nav_nearest(nav_st, NAV_PERSON, 100.0, 0.0, 100.0))
xs[0] = 64.0
zs[0] = 64.0
expect_eq(nav_tiles_keep(nav_st, NAV_PERSON, xs, zs, 1, 60.0, 90.0), 3)
expect_eq(nav_tiles_in(nav_st, NAV_PERSON), 4)
expect(nav_path(nav_st, NAV_PERSON, 0, 10.0, 0.0, 20.0, 120.0, 0.0, 20.0) >= 3)
expect(not nav_partial(nav_st, NAV_PERSON))
xs[0] = 110.0
zs[0] = 110.0
expect_eq(nav_tiles_keep(nav_st, NAV_PERSON, xs, zs, 1, 60.0, 90.0), 1)
expect_eq(nav_tiles_in(nav_st, NAV_PERSON), 3)
expect(not nav_nearest(nav_st, NAV_PERSON, 20.0, 0.0, 20.0))
nav_reset(nav_st)
expect_eq(nav_tiles_in(nav_st, NAV_PERSON), 0)
}
}