singe/testScripts/scene104.singe
2026-10-13 13:58:00 -05:00

108 lines
3.6 KiB
Text

-- A large heightmap is drawn in chunks, each culled alone, and looks as it did drawn whole.
-- testScripts/ridges.png is 257 samples a side, so meshHeightmap makes 256 by 256 cells in 16
-- chunks of 64; a twin built cell for cell through meshNew from the same formula is one mesh
-- with no chunks. sceneGetStats has to count 16 draws for the chunked one, fewer of them inside
-- the view (the camera stands in the middle, so what is behind it is not drawn), and one for the
-- twin. terrainGetHeight has to read the samples exactly, which a greyscale PNG did not until
-- the loader's grey ramp was fixed. The screenshots chunked and whole should match: the
-- triangles are the same and only the order the normals are summed in differs. Each check prints
-- "CHUNKS ok" or "CHUNKS FAIL". No disc: run with -C 720x480.
dofile("Singe/Framework.singe")
local font = fontLoad("Singe/FreeSansBold.ttf", 14)
local ground = materialNew()
local camera = nodeNew()
local sun = lightNew(LIGHT_DIRECTIONAL)
local chunked = nodeNew()
local whole = nodeNew()
local frames = 0
local failures = 0
local SAMPLES = 257
local SIZE = 256 -- Across, so every vertex lands on a whole number
local HEIGHT = 16
local LAST = 10
local function check(name, ok, detail)
if ok then
debugPrint("CHUNKS ok " .. name)
else
failures = failures + 1
debugPrint("CHUNKS FAIL " .. name .. (detail and (" (" .. detail .. ")") or ""))
end
end
-- The sample at column x, row y, as the PNG holds it: the same formula made ridges.png.
local function sample(x, y)
return math.floor((0.5 + 0.25 * math.sin(x * 0.05) + 0.25 * math.cos(y * 0.07)) * 255 + 0.5) / 255
end
-- The grid meshHeightmap makes, a vertex per sample with row 0 at -Z, wound to face up.
local function twinMesh()
local positions = {}
local indices = {}
local cells = SAMPLES - 1
for y = 0, cells do
for x = 0, cells do
positions[#positions + 1] = -SIZE / 2 + x
positions[#positions + 1] = sample(x, y) * HEIGHT
positions[#positions + 1] = -SIZE / 2 + y
end
end
for y = 0, cells - 1 do
for x = 0, cells - 1 do
local a = y * SAMPLES + x + 1
local b = a + 1
local c = a + SAMPLES
local d = c + 1
for _, index in ipairs({ a, d, b, a, c, d }) do
indices[#indices + 1] = index
end
end
end
return meshNew(positions, nil, nil, indices)
end
fontSelect(font)
overlaySetResolution(720, 480)
sceneEnable(true)
cameraSet(camera)
nodeSetPosition(camera, 0, 30, 0)
nodeLookAt(camera, 60, 6, 80)
nodeLookAt(sun, -1, -2, -0.5)
materialSetColor(ground, 110, 160, 90)
nodeSetMesh(chunked, meshHeightmap("testScripts/ridges.png", SIZE, HEIGHT, SIZE), ground)
nodeSetMesh(whole, twinMesh(), ground)
nodeSetVisible(whole, false)
do
local got = terrainGetHeight(chunked, -SIZE / 2 + 10, -SIZE / 2 + 20)
check("terrainGetHeight reads the samples", (got ~= nil) and (math.abs(got - sample(10, 20) * HEIGHT) < 0.001), tostring(got) .. " against " .. (sample(10, 20) * HEIGHT))
end
function onOverlayUpdate()
local total, drawn = sceneGetStats()
frames = frames + 1
overlayClear()
if frames == 3 then
check("16 chunk draws", total == 16, tostring(total))
check("chunks out of view are culled", (drawn > 0) and (drawn < total), drawn .. " of " .. total)
singeScreenshot("chunked")
nodeSetVisible(chunked, false)
nodeSetVisible(whole, true)
elseif frames == 6 then
check("the twin is one draw", total == 1, tostring(total))
singeScreenshot("whole")
elseif frames == LAST then
debugPrint(string.format("CHUNKS RESULT failures=%d", failures))
singeQuit()
end
return OVERLAY_UPDATED
end