mirror of
https://github.com/DramaticShape/DramaticShapeVoxelMod.git
synced 2026-08-12 09:20:51 +02:00
116 lines
4.4 KiB
Lua
116 lines
4.4 KiB
Lua
-- Probe: read the .dsm packs back with the mod's OWN reader and print what
|
|
-- it made of them, so the Lua side can be diffed against the packer's
|
|
-- --report output. Headless -- no love.graphics, no meshes.
|
|
--
|
|
-- luajit mods/DramaticShapeVoxelMod/tests/stadium_pack_probe.lua [dex ...]
|
|
--
|
|
-- Run from the repository root.
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local ROOT = "mods/DramaticShapeVoxelMod"
|
|
|
|
-- the mod namespace lib/ modules expect (see main.lua), with just enough of
|
|
-- it for the two files under test
|
|
local V = {}
|
|
local modules = {}
|
|
V.mod = {
|
|
log = {
|
|
warn = function(_, fmt, ...) print("[warn] " .. fmt:format(...)) end,
|
|
info = function(_, fmt, ...) print("[info] " .. fmt:format(...)) end,
|
|
},
|
|
read = function(_, rel)
|
|
local fp = io.open(ROOT .. "/" .. rel, "rb")
|
|
if not fp then return nil end
|
|
local bytes = fp:read("*a")
|
|
fp:close()
|
|
return bytes
|
|
end,
|
|
}
|
|
function V.require(name)
|
|
if modules[name] then return modules[name] end
|
|
local chunk = assert(loadfile(ROOT .. "/lib/" .. name .. ".lua"))
|
|
modules[name] = chunk(V)
|
|
return modules[name]
|
|
end
|
|
|
|
-- StadiumRig pulls in Voxel3D, which needs love; the pose walk itself does
|
|
-- not, so the require is stubbed out to the one field it reads.
|
|
modules.Voxel3D = { FORMAT = {} }
|
|
|
|
local Pack = V.require("StadiumPack")
|
|
local Rig = V.require("StadiumRig")
|
|
|
|
local want = {}
|
|
for i = 1, #arg do want[#want + 1] = tonumber(arg[i]) end
|
|
if #want == 0 then want = { 25, 6, 130, 95, 50, 10, 143, 92, 41, 73 } end
|
|
|
|
print(("%-4s %-6s %6s %6s %6s %8s %8s %8s %8s")
|
|
:format("dex", "bones", "prims", "anims", "tex", "root", "height",
|
|
"floor", "radius"))
|
|
for _, dex in ipairs(want) do
|
|
local m = Pack.load(dex)
|
|
if not m then
|
|
print(("%-4d -- did not load"):format(dex))
|
|
else
|
|
print(("%-4d %-6d %6d %6d %6d %8.4f %8.2f %8.2f %8.2f")
|
|
:format(dex, m.boneCount, m.primCount, m.animCount, m.texCount,
|
|
m.rootScale, m.height, m.floor, m.radius))
|
|
|
|
-- the bind pose, walked by the REAL rig code on a bare instance (no
|
|
-- meshes, so no graphics context needed), measured the same way
|
|
-- tools/stadium_pack.py measures it. Agreement here means the byte
|
|
-- format, the bone tree, the rotation basis and the two-chain scale
|
|
-- split all survived the trip into Lua.
|
|
local rig = setmetatable({
|
|
model = m, pivotM = {}, drawM = {}, accX = {}, accY = {}, accZ = {},
|
|
parts = {},
|
|
}, Rig)
|
|
local function boxAt(anim, frame, wrap)
|
|
rig:pose(anim, frame, wrap)
|
|
local drw = rig.drawM
|
|
local lo, hi = math.huge, -math.huge
|
|
for _, prim in ipairs(m.prims) do
|
|
for k = 1, prim.vertCount do
|
|
local o = (prim.bone[k] - 1) * 12
|
|
local x, y, z = prim.px[k], prim.py[k], prim.pz[k]
|
|
local wy = drw[o + 5] * x + drw[o + 6] * y + drw[o + 7] * z
|
|
+ drw[o + 8]
|
|
wy = wy * m.rootScale
|
|
if wy < lo then lo = wy end
|
|
if wy > hi then hi = wy end
|
|
end
|
|
end
|
|
return hi - lo, lo
|
|
end
|
|
|
|
local h, f = boxAt(nil, 0, false)
|
|
print((" bind pose walked in Lua: height %8.2f floor %8.2f "
|
|
.. "(header says %8.2f / %8.2f)%s")
|
|
:format(h, f, m.height, m.floor,
|
|
(math.abs(h - m.height) < 0.5
|
|
and math.abs(f - m.floor) < 0.5) and " OK" or " MISMATCH"))
|
|
|
|
-- the animation tables, decoded lazily like the game does it
|
|
local idle = m.ctx[Pack.SLOT.idle]
|
|
local names = {}
|
|
for i, a in ipairs(m.anims) do names[i] = a.name end
|
|
print((" anims: %s"):format(table.concat(names, " ")))
|
|
print((" idle slot -> %s, entrance -> %s, faint -> %s")
|
|
:format(tostring(idle), tostring(m.ctx[Pack.SLOT.entrance]),
|
|
tostring(m.ctx[Pack.SLOT.faint])))
|
|
local tracks = Pack.tracks(m, (idle or 0) + 1)
|
|
local animated = 0
|
|
for b = 1, m.boneCount do if tracks and tracks[b] then animated = animated + 1 end end
|
|
print((" idle: %d frames, %d/%d bones animated")
|
|
:format(m.anims[(idle or 0) + 1].frames, animated, m.boneCount))
|
|
-- the POSED extent, which is what actually gets drawn. Compare against
|
|
-- tools/stadium_pack.py's anim_sample, which was checked frame for
|
|
-- frame against the reference glTF export.
|
|
for _, fr in ipairs({ 0, 1, 5 }) do
|
|
local ah, af = boxAt((idle or 0) + 1, fr, true)
|
|
print((" idle frame %d: y %8.2f .. %8.2f"):format(fr, af, af + ah))
|
|
end
|
|
end
|
|
end
|