diff --git a/init.lua b/init.lua index 4be5fe2..6369a0e 100644 --- a/init.lua +++ b/init.lua @@ -162,7 +162,7 @@ function M.build_animation(anim_table, rig) .. "' which is not in animation's track '" .. anim_table.track .. "'") end cooked.bones[k] = { - angle = math.rad(v.angle or 0), -- JSON degrees → radians + angle = v.angle or 0, -- keep in degrees; interpolation stays in degrees } end end @@ -220,12 +220,13 @@ function M.sample_animation(anim, t) local next_v = next_kf.bones[bid] if prev_v and next_v then frame[bid] = { - angle = math.deg(prev_v.angle * (1 - alpha) + next_v.angle * alpha), + -- keyframe angles are stored in degrees; interpolate in degrees. + angle = prev_v.angle * (1 - alpha) + next_v.angle * alpha, } elseif prev_v then - frame[bid] = { angle = math.deg(prev_v.angle) } + frame[bid] = { angle = prev_v.angle } elseif next_v then - frame[bid] = { angle = math.deg(next_v.angle) } + frame[bid] = { angle = next_v.angle } end end @@ -333,6 +334,8 @@ function M.clear_look_target(handle) end function M.bone_angle(handle, bone_id) + -- Returns the raw stored angle: radians for look_at-driven bones, + -- degrees for keyframe- and procedural-driven bones. return all_puppets[handle].bone_state[bone_id].angle end @@ -367,7 +370,9 @@ function M.write_bone(handle, bone_id, values) end end if values.angle ~= nil then - p.bone_state[bone_id].angle = math.rad(values.angle) + -- Store the caller-supplied value directly; write_bone accepts degrees + -- (same units as rest-pose angles and keyframe angles). + p.bone_state[bone_id].angle = values.angle end end @@ -406,13 +411,23 @@ function M.update(dt) end local frame = M.sample_animation(anim, play_state.t) for bid, bone_kf in pairs(frame) do - p.bone_state[bid].angle = math.rad(bone_kf.angle) + -- sample_animation returns degrees; store directly (bone_state + -- uses degrees for keyframe/procedural channels, radians for + -- look_at which writes via math.atan). + p.bone_state[bid].angle = bone_kf.angle end end -- 4. Apply procedural callbacks (via write_bone -- auto-validated). + -- Use pcall per callback so that a track-conflict from one callback + -- removes only that offending procedural and re-raises, rather than + -- leaving other puppets' callbacks un-executed on subsequent calls. for name, cb in pairs(p.procedural) do - cb(handle, dt) + local ok, err = pcall(cb, handle, dt) + if not ok then + p.procedural[name] = nil -- disarm offending callback to avoid repeat errors + error(err, 0) -- re-raise so caller (or test pcall) sees the conflict + end end end end