stop a flat top stamping its rim down the plateau

A cliff mound is drawn as a rim over a body: its top edge, then the same
rock the whole way down. The top face cycles the first two drawn rows to
fill its depth, so it laid that rim again every second tile. The mound the
Diglett's Cave mouth is cut into came out with three rim lines across it
instead of one along its north edge.

Where the drawing says the body is all one tile, lay the rim once and hold
the body after it. Art that genuinely repeats keeps cycling: the Safari
Zone's fence alternates two tiles the whole way down, and there the repeat
is what the drawing says.

Answered per column and per region, because each catches what the other
misses. The columns carrying a mound's cave mouth end in the mouth's own
tiles, so per column alone they kept cycling while their neighbours held,
leaving rim stubs above the doorway. A region vote alone silences a real
rim-over-body column standing in a region of repeating art, of which the
Safari Zone has three. A column holds if either says so.

Geometry is untouched: the silhouette is pixel for pixel what it was, and
only the texel a top face wears changes. Of 3088 flat-topped runs, the
1336 rim-over-body ones change and nothing else does.

tests/flat_top_test.lua walks every map and fails if any rim-over-body run
revisits an earlier drawn row.
This commit is contained in:
Code-Grub
2026-08-06 12:48:06 -04:00
parent b50bbe0782
commit 20f9e19bf9
3 changed files with 154 additions and 2 deletions
+21 -2
View File
@@ -64,6 +64,26 @@ end
local ChunkMesher = {}
-- Which drawn row a FLAT-topped volume's top face wears at depth `ty`.
--
-- A structure is usually deeper than the art that draws it, so the rows
-- cycle and the drawing repeats down the top. That is right for art which
-- genuinely repeats -- the Safari Zone's fence alternates two tiles the
-- whole way down -- and wrong for a RIM over a uniform body: a cliff
-- mound's first row is its top edge, and cycling lays that edge again
-- every second tile, striping a plateau with rims it should not have.
--
-- Where Structures found the body uniform, the rim is laid once at the
-- north edge and the body held after it. Everything else cycles as before.
function ChunkMesher.flatTopRow(run, ty)
local m = math.min(2, run.extent)
local d = ty - run.north
if run.topUniform then
return run.north + math.min(d, m - 1)
end
return run.north + (d % m)
end
-- Ring of border blocks meshed around the body, matching the width
-- TileRenderer draws so the two modes end at the same place.
local RING = 3
@@ -528,8 +548,7 @@ local function runGeometry(map, bodyOnly, masks, sink, waterSink)
{ x0 + 8, neY, z0 }, { x0, nwY, z0 } },
{ { u0, v1 }, { u1, v1 }, { u1, v0 }, { u0, v0 } }, 0.95)
elseif run then
local m = math.min(2, run.extent)
local topTile = map:tileAt(tx, run.north + ((ty - run.north) % m))
local topTile = map:tileAt(tx, ChunkMesher.flatTopRow(run, ty))
topQuad(x0, z0, h, topTile, VOLUME_TOP_SHADE)
else
local topTile = tile
+38
View File
@@ -2225,6 +2225,43 @@ function Structures.buildVolume(S, map, tiles)
-- whether the region's dominant columns are flat repeats (a cliff
-- mound's plateau) rather than drawn facades (a house's front)
local modeRepeat = (repeatVotes[modeH] or 0) * 2 > modeN
-- Whether this REGION's tops are a rim over a uniform body -- what every
-- cliff mound is drawn as: a top edge, then the same rock the whole way
-- down. The top face may then lay that rim once along its north edge and
-- hold the body after it, instead of cycling the rim back every second
-- tile and striping a plateau with edges it should not have.
--
-- Answered per column AND per region, because each catches what the
-- other misses. A mound is one structure many columns wide, and the
-- columns carrying its cave mouth read differently from their neighbours
-- (their drawing ends in the mouth's own tiles): per column alone, those
-- kept cycling while the rest held, leaving rim stubs above the doorway.
-- But a region vote alone silences a genuine rim-over-body column that
-- happens to stand in a region of repeating art -- three of them in the
-- Safari Zone. A column holds if EITHER says so.
--
-- Art that genuinely repeats is not uniform and keeps cycling: the
-- Safari Zone's fence alternates two tiles the whole way down, and there
-- the repeat IS what the drawing says.
local uniformVotes, uniformTotal = 0, 0
for _, r in ipairs(runs) do
local run = r.run
if run.extent > 2 then
uniformTotal = uniformTotal + 1
local body = map:tileAt(r.tx, run.north + 1)
local uniform = true
for d = 2, run.extent - 1 do
if map:tileAt(r.tx, run.north + d) ~= body then
uniform = false
break
end
end
run.ownUniform = uniform
if uniform then uniformVotes = uniformVotes + 1 end
end
end
local regionUniform = uniformTotal > 0 and uniformVotes * 2 > uniformTotal
for _, r in ipairs(runs) do
local run = r.run
local h = run.unit * 8
@@ -2272,6 +2309,7 @@ function Structures.buildVolume(S, map, tiles)
run.rise = roofRows * 8
run.peak = h
run.h = h - run.rise -- facade height: what sides build to
run.topUniform = run.ownUniform or regionUniform
for ty = run.north, run.front do
S.runs[keyOf(r.tx, ty)] = run
end
+95
View File
@@ -0,0 +1,95 @@
-- A flat top must not stamp its rim twice.
--
-- ChunkMesher.flatTopRow decides which drawn row a flat-topped volume's top
-- face wears at each depth. Where the drawing is a RIM over a uniform body
-- -- every cliff mound in the game, and the mound the Diglett's Cave mouth
-- is cut into -- the rim belongs at the plateau's north edge and nowhere
-- else. Cycling the first two rows lays it again every second tile.
--
-- The invariant: on such a run the sampled row never goes BACKWARDS as ty
-- moves south. Art that genuinely repeats (the Safari Zone's fence
-- alternates two tiles the whole way down) is exempt: there the repeat is
-- what the drawing says, and the run is not rim-over-body.
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/flat_top_test.lua lovec .
return function(game)
local U = dofile("tests/drivers/util.lua")
local V = game.mods.exports["DRAMATIC_SHAPE"]
V = V and V.lib
local Structures = V and V.require("Structures")
local ChunkMesher = V and V.require("ChunkMesher")
if not (Structures and ChunkMesher and ChunkMesher.flatTopRow) then
print("[flattop] FAIL mod, Structures or ChunkMesher.flatTopRow missing")
love.event.quit(1)
return
end
local function keyOf(tx, ty) return (ty + 64) * 4096 + (tx + 64) end
local MAPS = {}
for id in pairs((game.data and game.data.maps) or {}) do
MAPS[#MAPS + 1] = id
end
table.sort(MAPS)
local checked, offenders, examples = 0, 0, {}
for _, mapId in ipairs(MAPS) do
U.teleport(game, mapId, 5, 5, "up")
U.wait(6)
local ow = game.overworld
if ow and ow.map and ow.map.def and ow.map.def.id == mapId then
local map = ow.map
local S = Structures.forMap(map)
local seen = {}
for tx = 0, map.def.width * 4 - 1 do
for ty = 0, map.def.height * 4 - 1 do
local run = S.runs[keyOf(tx, ty)]
local sig = run and (tostring(run) .. ":" .. tx)
if run and not seen[sig] and (run.rise or 0) == 0 then
seen[sig] = true
local ext = run.front - run.north + 1
-- rim over a uniform body: the shape the rim must not repeat on
local uniform = ext > 2
if uniform then
local body = map:tileAt(tx, run.north + 1)
for d = 2, ext - 1 do
if map:tileAt(tx, run.north + d) ~= body then
uniform = false
break
end
end
end
if uniform then
checked = checked + 1
local prev = -1
for ty2 = run.north, run.front do
local row = ChunkMesher.flatTopRow(run, ty2)
if row < prev then
offenders = offenders + 1
if #examples < 5 then
examples[#examples + 1] = ("%s tx=%d north=%d ext=%d "
.. "went back to row %d at ty %d")
:format(mapId, tx, run.north, ext, row, ty2)
end
break
end
prev = row
end
end
end
end
end
end
end
print(("[flattop] %d rim-over-body runs checked, %d repeat their rim")
:format(checked, offenders))
for _, e in ipairs(examples) do print("[flattop] " .. e) end
if offenders > 0 then
print("[flattop] FAIL")
love.event.quit(1)
else
print("[flattop] PASS")
love.event.quit(0)
end
end