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.
|
-- built without aliases), fallback uses spec default 30.
|
||||||
sprite_reach = 30.0,
|
sprite_reach = 30.0,
|
||||||
}
|
}
|
||||||
-- If texture is loaded, compute sprite_reach from actual sprite dim
|
-- If texture is loaded (atlas-uv path or legacy), compute sprite_reach
|
||||||
if leg_bone.texture_handle and leg_bone.texture_height then
|
-- 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
|
local anchor_y = (leg_bone.anchor and leg_bone.anchor[2]) or 0
|
||||||
foot_state[i].sprite_reach = leg_bone.texture_height - anchor_y
|
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
|
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).
|
-- Rendering (engine.render.*; only callable in render-phase).
|
||||||
-- Walks bones in z-sorted order (back-to-front). For each bone:
|
-- Walks bones in z-sorted order (back-to-front). For each bone:
|
||||||
-- - If bone has texture_handle: draw via engine.render.draw_sprite_transform
|
-- - M.2: If bone has atlas_uv + rig.atlas.diffuse_texture_handle: draw via
|
||||||
-- using cascaded world.scl_x/y and world.rot
|
-- engine.render.draw_sprite_transform with ADR-0044 source-rect args.
|
||||||
-- - Else: fall back to engine.render.draw_rect_rotated with bone.color
|
-- - Else: fall back to engine.render.draw_rect_rotated with bone.color.
|
||||||
-- ====================================================================
|
-- ====================================================================
|
||||||
function M.render(handle)
|
function M.render(handle)
|
||||||
local p = all_puppets[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).
|
-- v0.4.0: Skip virtual bones (they participate in compute_world but are not drawn).
|
||||||
if b.virtual then goto continue end
|
if b.virtual then goto continue end
|
||||||
local ws = p.bone_state[b.id].world
|
local ws = p.bone_state[b.id].world
|
||||||
if b.texture_handle then
|
if b.atlas_uv and p.rig.atlas and p.rig.atlas.diffuse_texture_handle then
|
||||||
-- Cascaded world-scale (was: per-bone constant b.scale)
|
-- 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 sx, sy = ws.scl_x, ws.scl_y
|
||||||
local ax = b.anchor and b.anchor[1] or 0
|
local ax = b.anchor and b.anchor[1] or 0
|
||||||
local ay = b.anchor and b.anchor[2] or 0
|
local ay = b.anchor and b.anchor[2] or 0
|
||||||
|
local uv = b.atlas_uv
|
||||||
engine.render.draw_sprite_transform(
|
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),
|
ws.x, ws.y, ws.rot + (b.sprite_rot or 0),
|
||||||
sx, sy, ax, ay,
|
sx, sy, ax, ay,
|
||||||
0xFFFFFFFF
|
0xFFFFFFFF,
|
||||||
|
uv.x, uv.y, uv.w, uv.h -- ADR-0044 source-rect args
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
-- Fallback: colored rotated rect (Phase-1 behavior)
|
-- Fallback: colored rotated rect (Phase-1 behavior)
|
||||||
@@ -1143,7 +1147,8 @@ end
|
|||||||
-- call, returning a Lua table directly. No separate decode step needed.
|
-- 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.
|
-- asset_aliases: { [alias-key] = asset-lib-id } from module's manifest.
|
||||||
-- ====================================================================
|
-- ====================================================================
|
||||||
function M.load_textures(rig, asset_aliases)
|
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
|
error("puppet.load_textures: asset-pack alias '" .. rig.asset_pack
|
||||||
.. "' not found in module asset_aliases")
|
.. "' not found in module asset_aliases")
|
||||||
end
|
end
|
||||||
local atlas_path = lib_id .. "/assets/atlas.json"
|
local base = lib_id .. "/assets/atlases/" .. rig.asset_pack
|
||||||
local atlas = engine.asset.load_json(atlas_path)
|
local meta = engine.asset.load_json(base .. "/tiles.atlas.json")
|
||||||
local pack = atlas[rig.asset_pack]
|
if meta.atlas_id ~= rig.asset_pack then
|
||||||
if pack == nil then
|
error(string.format(
|
||||||
error("puppet.load_textures: asset_pack '" .. rig.asset_pack
|
"puppet.load_textures: atlas_id mismatch in %s (json has '%s')",
|
||||||
.. "' not declared in atlas of '" .. lib_id .. "'")
|
base, meta.atlas_id))
|
||||||
end
|
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
|
if b.texture then
|
||||||
local entry = pack[b.texture]
|
local tile = by_name[b.texture]
|
||||||
if entry == nil then
|
if tile == nil then
|
||||||
error("puppet.load_textures: atlas-id '" .. b.texture
|
error("puppet.load_textures: atlas '" .. rig.asset_pack
|
||||||
.. "' not in asset_pack '" .. rig.asset_pack
|
.. "' has no tile '" .. b.texture .. "'")
|
||||||
.. "' of '" .. lib_id .. "'")
|
|
||||||
end
|
end
|
||||||
local tex_path = lib_id .. "/assets/" .. entry.file
|
b.atlas_uv = tile.uv
|
||||||
b.texture_handle = engine.asset.load_texture(tex_path)
|
-- sprite_reach for locomotion: tile height minus anchor.y
|
||||||
-- Cache texture dimensions for locomotion sprite_reach
|
b.texture_height = tile.uv.h
|
||||||
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.
|
|
||||||
if b.anchor == nil then
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "lib-core.puppet",
|
"id": "lib-core.puppet",
|
||||||
"version": "0.4.4",
|
"version": "0.5.0",
|
||||||
"api_min": "0.1",
|
"api_min": "0.1",
|
||||||
"deps": []
|
"deps": []
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user