feat(puppet): v0.3.3 — sprite_rot per-bone render offset

Adds optional 'sprite_rot' field (degrees in JSON, radians stored) to
bone schema. Applied at render: world.rot + sprite_rot. Substrate
convention (puppet.c:295, 2101). Use case: head_001.png has face-right
at rot=0 (asymmetric sprite); without sprite_rot=-90 the look-at result
appears 90° off because math computes rotation assuming sprite-top=
forward but the sprite's natural forward is sprite-right.

Default sprite_rot=0 keeps body, shoulder, arm bones unchanged.
This commit is contained in:
Axel Meyer
2026-05-18 20:42:01 +02:00
parent a64055ca2a
commit 6e90bfa8fe
2 changed files with 10 additions and 2 deletions

View File

@@ -88,6 +88,13 @@ function M.build_rig(rig_table)
local look_at_speed = nil
if b.look_at_speed ~= nil then look_at_speed = math.rad(b.look_at_speed) end
-- A.4: Parse sprite_rot (deg in JSON → radians stored). Render-time offset
-- applied as `world.rot + sprite_rot` to compensate for sprite assets
-- whose natural orientation at rot=0 is not the bone's "forward" (-Y).
-- Substrate convention (puppet.c:295, 2101).
local sprite_rot = 0
if b.sprite_rot ~= nil then sprite_rot = math.rad(b.sprite_rot) end
bones_by_id[b.id] = {
id = b.id,
parent = nil, -- resolved below
@@ -100,6 +107,7 @@ function M.build_rig(rig_table)
rest_rot_min = rest_rot_min,
rest_rot_max = rest_rot_max,
look_at_speed = look_at_speed,
sprite_rot = sprite_rot,
color = b.color or { 200, 200, 200 },
texture = b.texture, -- optional atlas-id (string) or nil
z_order = b.z_order or 0, -- render-sort key; default 0
@@ -987,7 +995,7 @@ function M.render(handle)
local ay = b.anchor and b.anchor[2] or 0
engine.render.draw_sprite_transform(
b.texture_handle,
ws.x, ws.y, ws.rot,
ws.x, ws.y, ws.rot + (b.sprite_rot or 0),
sx, sy, ax, ay,
0xFFFFFFFF
)

View File

@@ -1,6 +1,6 @@
{
"id": "lib-core.puppet",
"version": "0.3.2",
"version": "0.3.3",
"api_min": "0.1",
"deps": []
}