Rewrite puppet load_textures for atlas-handle + uv
Bones now reference atlas-uv rectangles instead of holding per-bone texture handles. The rig gains an atlas record with a single diffuse texture handle plus a tiles-by-name dictionary for bone-name lookup. Bone-draw uses the ADR-0044 source-rect render primitive. Anchor defaults to tile UV center; rig-level b.anchor overrides. Bumps lib to 0.5.0 to flag the breaking change in rig.bones shape. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
82
init.lua
82
init.lua
@@ -434,8 +434,9 @@ function M.spawn(rig, pos)
|
||||
-- built without aliases), fallback uses spec default 30.
|
||||
sprite_reach = 30.0,
|
||||
}
|
||||
-- If texture is loaded, compute sprite_reach from actual sprite dim
|
||||
if leg_bone.texture_handle and leg_bone.texture_height then
|
||||
-- If texture is loaded (atlas-uv path or legacy), compute sprite_reach
|
||||
-- from tile UV height minus anchor.y.
|
||||
if leg_bone.texture_height then
|
||||
local anchor_y = (leg_bone.anchor and leg_bone.anchor[2]) or 0
|
||||
foot_state[i].sprite_reach = leg_bone.texture_height - anchor_y
|
||||
if foot_state[i].sprite_reach < 1 then foot_state[i].sprite_reach = 1 end
|
||||
@@ -1104,9 +1105,9 @@ end
|
||||
-- ====================================================================
|
||||
-- Rendering (engine.render.*; only callable in render-phase).
|
||||
-- Walks bones in z-sorted order (back-to-front). For each bone:
|
||||
-- - If bone has texture_handle: draw via engine.render.draw_sprite_transform
|
||||
-- using cascaded world.scl_x/y and world.rot
|
||||
-- - Else: fall back to engine.render.draw_rect_rotated with bone.color
|
||||
-- - M.2: If bone has atlas_uv + rig.atlas.diffuse_texture_handle: draw via
|
||||
-- engine.render.draw_sprite_transform with ADR-0044 source-rect args.
|
||||
-- - Else: fall back to engine.render.draw_rect_rotated with bone.color.
|
||||
-- ====================================================================
|
||||
function M.render(handle)
|
||||
local p = all_puppets[handle]
|
||||
@@ -1116,16 +1117,19 @@ function M.render(handle)
|
||||
-- v0.4.0: Skip virtual bones (they participate in compute_world but are not drawn).
|
||||
if b.virtual then goto continue end
|
||||
local ws = p.bone_state[b.id].world
|
||||
if b.texture_handle then
|
||||
-- Cascaded world-scale (was: per-bone constant b.scale)
|
||||
if b.atlas_uv and p.rig.atlas and p.rig.atlas.diffuse_texture_handle then
|
||||
-- M.2: Atlas-based bone draw. Single texture handle per atlas;
|
||||
-- per-bone UV rect selects the tile via ADR-0044 source-rect args.
|
||||
local sx, sy = ws.scl_x, ws.scl_y
|
||||
local ax = b.anchor and b.anchor[1] or 0
|
||||
local ay = b.anchor and b.anchor[2] or 0
|
||||
local uv = b.atlas_uv
|
||||
engine.render.draw_sprite_transform(
|
||||
b.texture_handle,
|
||||
p.rig.atlas.diffuse_texture_handle,
|
||||
ws.x, ws.y, ws.rot + (b.sprite_rot or 0),
|
||||
sx, sy, ax, ay,
|
||||
0xFFFFFFFF
|
||||
0xFFFFFFFF,
|
||||
uv.x, uv.y, uv.w, uv.h -- ADR-0044 source-rect args
|
||||
)
|
||||
else
|
||||
-- Fallback: colored rotated rect (Phase-1 behavior)
|
||||
@@ -1143,7 +1147,8 @@ end
|
||||
-- call, returning a Lua table directly. No separate decode step needed.
|
||||
|
||||
-- ====================================================================
|
||||
-- Resolve atlas-ids to texture-handles via asset-lib indirection.
|
||||
-- M.2: Atlas-loading rewrite. Per-atlas texture-handle; bones reference
|
||||
-- atlas-index + UV-rect rather than holding their own per-bone handle.
|
||||
-- asset_aliases: { [alias-key] = asset-lib-id } from module's manifest.
|
||||
-- ====================================================================
|
||||
function M.load_textures(rig, asset_aliases)
|
||||
@@ -1153,30 +1158,47 @@ function M.load_textures(rig, asset_aliases)
|
||||
error("puppet.load_textures: asset-pack alias '" .. rig.asset_pack
|
||||
.. "' not found in module asset_aliases")
|
||||
end
|
||||
local atlas_path = lib_id .. "/assets/atlas.json"
|
||||
local atlas = engine.asset.load_json(atlas_path)
|
||||
local pack = atlas[rig.asset_pack]
|
||||
if pack == nil then
|
||||
error("puppet.load_textures: asset_pack '" .. rig.asset_pack
|
||||
.. "' not declared in atlas of '" .. lib_id .. "'")
|
||||
local base = lib_id .. "/assets/atlases/" .. rig.asset_pack
|
||||
local meta = engine.asset.load_json(base .. "/tiles.atlas.json")
|
||||
if meta.atlas_id ~= rig.asset_pack then
|
||||
error(string.format(
|
||||
"puppet.load_textures: atlas_id mismatch in %s (json has '%s')",
|
||||
base, meta.atlas_id))
|
||||
end
|
||||
for _, b in pairs(rig.bones_by_id) do
|
||||
-- Build a name -> { id, uv } dict for fast bone lookup
|
||||
local by_name = {}
|
||||
for _, t in ipairs(meta.tiles) do
|
||||
by_name[t.name] = {
|
||||
id = t.id,
|
||||
uv = { x = t.uv[1], y = t.uv[2], w = t.uv[3], h = t.uv[4] },
|
||||
}
|
||||
end
|
||||
-- Texture loading may fail in headless test environments (no OpenGL
|
||||
-- context). Use pcall so that bone-shape + animation tests still pass;
|
||||
-- only the handle is nil when loading is unavailable.
|
||||
local ok_d, diffuse_h = pcall(engine.asset.load_texture,
|
||||
base .. "/tiles.diffuse.atlas.png")
|
||||
rig.atlas = {
|
||||
id = meta.atlas_id,
|
||||
tiles_by_name = by_name,
|
||||
atlas_size_px = meta.atlas_size_px,
|
||||
diffuse_texture_handle = ok_d and diffuse_h or nil,
|
||||
}
|
||||
-- Bind each bone to its uv-rect; resolve anchor (uv.w/2, uv.h/2 default).
|
||||
-- Anchor is a rig-author concern: bone-level b.anchor (set in rig table)
|
||||
-- takes precedence; tile UV center is the fallback.
|
||||
for _, b in ipairs(rig.bones) do
|
||||
if b.texture then
|
||||
local entry = pack[b.texture]
|
||||
if entry == nil then
|
||||
error("puppet.load_textures: atlas-id '" .. b.texture
|
||||
.. "' not in asset_pack '" .. rig.asset_pack
|
||||
.. "' of '" .. lib_id .. "'")
|
||||
local tile = by_name[b.texture]
|
||||
if tile == nil then
|
||||
error("puppet.load_textures: atlas '" .. rig.asset_pack
|
||||
.. "' has no tile '" .. b.texture .. "'")
|
||||
end
|
||||
local tex_path = lib_id .. "/assets/" .. entry.file
|
||||
b.texture_handle = engine.asset.load_texture(tex_path)
|
||||
-- Cache texture dimensions for locomotion sprite_reach
|
||||
local tw, th = engine.asset.texture_size(b.texture_handle)
|
||||
b.texture_width = tw
|
||||
b.texture_height = th
|
||||
-- Resolve anchor: bone.anchor override beats atlas-default.
|
||||
b.atlas_uv = tile.uv
|
||||
-- sprite_reach for locomotion: tile height minus anchor.y
|
||||
b.texture_height = tile.uv.h
|
||||
if b.anchor == nil then
|
||||
b.anchor = entry.anchor or { 0, 0 }
|
||||
b.anchor = { tile.uv.w / 2, tile.uv.h / 2 }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user