Commit Graph

3 Commits

Author SHA1 Message Date
indigo ddffa5764c Curve editor: fix repeated Apply after bake
Three bugs prevented Apply from working correctly after the first bake:

1. m_needsRefresh spuriously set during bake
   ClearAllTimeSamples/WriteChannelValue fire OnObjectsChanged synchronously.
   Since the channel is dirty at that moment, it was skipped, setting
   anyMatchedChannel=false and triggering m_needsRefresh=true. This caused
   RefreshFromStage() on the next frame, clearing m_selection and rebuilding
   m_channels — dropping the user's edit context. Fixed with m_suppressNotice:
   set true during USD writes so OnObjectsChanged is a no-op during bake.

2. Prim-level metadata notice triggered rebuild
   SaveBezierToMetadata calls SetCustomDataByKey, which fires a notice with
   changed==primPath. No attribute channel matched, so m_needsRefresh=true
   again. Fixed in OnObjectsChanged: skip changed==primPath (metadata-only
   changes), and mark a path as "known" even if the channel is dirty (so
   the refresh gate only fires for genuinely new/unknown attributes).

3. Dangling chPtr in undo/redo closures
   After RefreshFromStage() cleared and rebuilt m_channels, the raw pointer
   captured in closures pointed to freed memory. Replaced with a findCh
   lambda that searches m_channels by attr path + component at call time.
   Falls back to m_needsRefresh=true if the channel no longer exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 09:28:09 +08:00
indigo e8a73674ad Curve editor: fix context menu, persist Bezier metadata, always-visible handles
Context menu fix:
- Move RMB popup and hit-test lambdas before the early-return guard in
  HandleCanvasInput so BeginPopup is called every frame while the popup is
  open, regardless of whether the mouse has left the canvas area.
  Previously the early-return fired (hovered=false) the moment the mouse
  moved onto the menu, silently closing it before any item could register.

Bezier metadata persistence:
- On Apply/bake, write each channel's BezierKey array into
  prim.customData["curveEditor"]["channels"][attrName@component] as flat
  double[]/int[] arrays (times, values, inDt, inDv, outDt, outDv, broken).
- FitCurveFromSamples tries LoadBezierFromMetadata first; falls back to
  Catmull-Rom only when no saved entry exists, so tangent shapes survive
  file save/reload.
- Bake undo command snapshots the entire curveEditor customData value and
  restores it on Ctrl+Z, keeping metadata in sync with sample history.

Always-visible tangent handles:
- Remove keySel guard so in/out handle lines and circles render for every
  keyframe, not just selected ones.
- hitTestHandle now iterates all visible channels/keys so any handle can
  be clicked directly without first selecting its parent keyframe.
- Clicking a handle auto-selects the parent key as well, keeping context
  menu operations (Flatten, Break, Delete) consistent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 09:07:01 +08:00
indigo 425d21db45 Add animation curve editor with Bezier F-curve round-trip
Maya-style Graph Editor panel (View > Curve Editor) that fits Catmull-Rom
Bezier curves through USD time samples, allows interactive editing with
tangent handles, and bakes the result back to dense linear samples on Apply.

Key behaviours:
- Left panel lists all animated attributes of the selected prim, decomposed
  into per-component channels (translate [X/Y/Z], etc.) with colour swatches
  and visibility toggles
- ImDrawList canvas: smooth Bezier polylines, always-visible tangent handle
  lines/circles, diamond keyframe markers
- Pan (MMB/Alt+drag), zoom (scroll / Shift+scroll), Frame All (F)
- LMB drag moves keyframes; tangent handle drag reshapes curve with mirrored
  or broken handles; box-select for multi-selection
- Double-click canvas adds a keyframe; Delete removes selected keyframes
- RMB context menu: Delete, Flatten, Break/Unify Tangents, Auto Tangents
- Simplify toggle (Ramer-Douglas-Peucker) reduces baked sample count
- Time cursor draggable to scrub the timeline
- UsdNotice::ObjectsChanged listener re-fits clean channels whenever the
  stage changes externally (Property Panel edits, Auto-Key, undo/redo),
  while preserving channels with unsaved Bezier edits
- Bake is a single undoable AttributeSetCommand (Ctrl+Z restores original
  sparse samples); Revert discards in-editor edits without touching USD

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 08:25:25 +08:00