Skip to content

Fix chained SaveCachedPose layer resolution and center node on navigation#1

Merged
LoogLong merged 52 commits intodevfrom
copilot/add-animation-blueprint-parsing
Mar 5, 2026
Merged

Fix chained SaveCachedPose layer resolution and center node on navigation#1
LoogLong merged 52 commits intodevfrom
copilot/add-animation-blueprint-parsing

Conversation

Copy link

Copilot AI commented Feb 28, 2026

Chained SaveCachedPose/UseCachedPose dependencies (e.g. UseCachedPose(A) → … → SaveCachedPose(B) → UseCachedPose(B) → … → SaveCachedPose(C)) resolved to the wrong animation blueprint layer. Additionally, navigating from a UseCachedPose to its SaveCachedPose via double-click didn't center the target node in the viewport.

Layer resolution fix (AnimGraphViewModel.cs)

  • Root cause: BuildLayerLookups was called once before the Pass 3 loop, so nodes placed during earlier iterations were invisible to FindOwnerRootLayer in later iterations.
  • Fix: Iterative multi-pass resolution — each pass rebuilds lookups and resolves SaveCachedPose nodes whose consumers are already placed, deferring nodes with unresolved consumers to the next pass. maxPasses guard prevents infinite loops; unresolvable nodes fall back to outermost layer.
bool madeProgress;
var maxPasses = pending.Count;
do
{
    madeProgress = false;
    var lookups = BuildLayerLookups(vm); // Rebuild each pass

    for (var i = pending.Count - 1; i >= 0; i--)
    {
        var saveNode = pending[i];
        var targetLayer = FindOwnerRootLayer(saveNode, vm, lookups);

        if (targetLayer == null)
        {
            // Defer if consumers aren't placed yet
            if (HasDeferredConsumer(saveNode, vm, lookups))
                continue;
            targetLayer = outermostGraphLayer;
        }
        // ... assign node and its upstream chain to targetLayer
        madeProgress = true;
    }
} while (madeProgress && pending.Count > 0 && --maxPasses > 0);

Center-on-navigate (AnimGraphViewer.xaml.cs)

  • Added CenterOnNode — adjusts TranslateTransform to place the target node at viewport center, preserving current zoom.
  • Called in TryNavigateToSaveCachedPose after SelectNode, inside the existing Dispatcher.BeginInvoke callback.

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI and others added 3 commits February 28, 2026 10:05
…and visualization

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
…mprove null handling

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Copilot AI changed the title [WIP] Add animation blueprint parsing and node visualization Add animation blueprint graph viewer with node/connection extraction and visualization Feb 28, 2026
Copilot AI and others added 2 commits March 2, 2026 02:01
…and CDO instead of exports

In cooked UE assets, UEdGraphNode objects are stripped as editor-only data
(PKG_FilterEditorOnly). The actual animation node information is stored in:
- UAnimBlueprintGeneratedClass.ChildProperties (FStructProperty entries describing node types)
- ClassDefaultObject properties (FStructFallback values with node data)
- FPoseLink/FComponentSpacePoseLink structs with LinkID for connections

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
…ion check, fix comment

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Copilot AI changed the title Add animation blueprint graph viewer with node/connection extraction and visualization Fix animation blueprint node extraction to read from ChildProperties/CDO instead of stripped exports Mar 2, 2026
Copilot AI and others added 2 commits March 2, 2026 02:35
- AnimGraphViewModel: Add AnimGraphLayer class and BuildLayers() to group
  nodes into connected subgraphs displayed as separate tabs
- AnimGraphViewModel: Add topological layout (LayoutLayerNodes) so nodes
  flow left-to-right by dependency depth instead of grid
- AnimGraphViewer.xaml: Replace single canvas with TabControl for layers
  and a right-side properties panel with GridSplitter
- AnimGraphViewer.xaml.cs: Per-layer canvas state, node selection with
  property panel population, per-tab zoom/pan

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
…onstants

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Copilot AI changed the title Fix animation blueprint node extraction to read from ChildProperties/CDO instead of stripped exports Fix AnimGraph viewer: extract nodes from class properties, add layer tabs and properties panel Mar 2, 2026
- Fix OnMouseWheel: get mouse position relative to parent Border (stable
  coords) instead of Canvas (transformed coords that shift during zoom);
  compute canvas-local point manually and preserve it after scale change
- Fix OnCanvasMouseDown/Move: use parent Border coords for consistent
  panning behavior
- Improve connection lines: thicker stroke (2px), higher opacity (0.8),
  minimum bezier tangent length (50px) for smoother short-distance curves

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Copilot AI changed the title Fix AnimGraph viewer: extract nodes from class properties, add layer tabs and properties panel Fix AnimGraph viewer zoom jumping and add layer tabs with properties panel Mar 2, 2026
In WPF, MouseLeftButtonDown is a Direct routed event that fires
independently on each UIElement with a fresh Handled=false. Setting
e.Handled=true in the node's click handler does NOT prevent
OnCanvasMouseDown on the parent canvasBorder from firing. This caused
clicking a node to both select it AND start panning, making the graph
jump when the mouse moved.

Fix: check e.OriginalSource != sender in OnCanvasMouseDown so panning
only starts when clicking directly on the canvas background.

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Copilot AI changed the title Fix AnimGraph viewer zoom jumping and add layer tabs with properties panel Fix animation graph node click jumping and zoom stability Mar 2, 2026
…ld panning

The previous fix (e.OriginalSource != sender) blocked panning entirely
when clicking on nodes, making them feel like buttons. Connection Path
elements also received mouse events, potentially interfering.

Fix:
- Use a drag-threshold mechanism: clicking selects a node without
  jumping, dragging from anywhere (including nodes) starts panning
  after a 5px threshold is exceeded.
- Register MouseLeftButtonDown with AddHandler(handledEventsToo: true)
  so the panning handler fires even when nodes set e.Handled = true.
- Set IsHitTestVisible = false on connection Path elements so they
  don't interfere with mouse events.
- Check e.LeftButton state in OnCanvasMouseMove to prevent stale pan
  state when the button is released unexpectedly.

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Copilot AI changed the title Fix animation graph node click jumping and zoom stability Fix animation node click-to-pan interference using drag threshold Mar 2, 2026
Copilot AI changed the title Fix StateRootNodeIndex reverse mapping and separate state sub-graphs from graph layers Fix StateRootNodeIndex mapping and add edge-to-edge transition arrows with selection Mar 4, 2026
@LoogLong
Copy link

LoogLong commented Mar 4, 2026

@copilot 从A状态到B状态的transition可能会有多个,参考途中的方法,每个转换都添加一个小圆方便点击选中

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Copy link
Author

Copilot AI commented Mar 4, 2026

@copilot 从A状态到B状态的transition可能会有多个,参考途中的方法,每个转换都添加一个小圆方便点击选中

已在 8dd66a1 中实现。每个 transition 箭头的中点处添加了一个小圆形图标(包含方向箭头),作为主要点击目标:

  • 小圆内绘制了方向箭头图标,表示 transition 方向
  • 鼠标悬停显示 tooltip("源状态 → 目标状态")
  • 点击圆圈或箭头线条均可选中 transition 并显示属性
  • 当同一对状态间有多个 transition 时,线条沿垂直方向偏移避免重叠,每条 transition 都有独立的小圆可单独选中

@LoogLong
Copy link

LoogLong commented Mar 4, 2026

@copilot 认真看图!不同方向的transition需要排布在连线的不同边,相同方向的transition小圆圈按照中心偏移开,不能重叠

…t circles for same direction

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Copy link
Author

Copilot AI commented Mar 4, 2026

@copilot 认真看图!不同方向的transition需要排布在连线的不同边,相同方向的transition小圆圈按照中心偏移开,不能重叠

已在 30bef7f 中修复。Transition 布局逻辑现在:

  1. 不同方向分两侧:A→B 和 B→A 的 transition 分别排布在连线的正负垂直偏移侧(perpSide: +1 / -1),使用 TransitionMultiOffset 控制偏移量
  2. 相同方向圆圈沿线偏移:多个同方向 transition 的小圆圈沿连线方向按 TransitionCircleRadius * TransitionCircleSpacing 间距排列,中心对齐避免重叠
  3. 分组逻辑改用无序节点对string.Compare(a.Name, b.Name)),确保 A→B 和 B→A 在同一组内分别处理

@copilot 从A状态到B状态的transition可能会有多个,参考途中的方法,每个转换都添加一个小圆方便点击选中

已在 30bef7f 中重新实现。Transition 布局现在按照 UE 参考方式工作:

  • A→B 和 B→A transition 排布在连线的不同侧(perpendicular offset ±12px)
  • 相同方向的多个 transition 小圆圈沿连线方向偏移,不会重叠

Copilot AI and others added 5 commits March 4, 2026 09:22
…irection circles spread perpendicular

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
…ne line with circles offset along it

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
…ular vector

The perpendicular vector was being computed per-connection from its own
direction. For B→A connections the perp flipped vs A→B, and when multiplied
by the negative perpSide the offsets cancelled out — both lines landed on
the same position.

Now a stable perpendicular is computed once from the canonical pair direction
(nodeA→nodeB centers) and passed to DrawConnectionLine, so perpSide=+1/-1
correctly separates them to opposite sides.

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
PrefixStateMachineLayerNames and BuildStateMachineOverviewLayers only
scanned vm.Layers for StateMachine nodes, missing nested SM nodes in
vm.StateSubGraphs. This caused overview layers for nested SMs to be
named incorrectly (e.g. "AnimGraph > NestedSM" instead of
"AnimGraph > OuterSM > StateName > NestedSM"), so double-click from
a state sub-graph could not find the matching overview layer.

Fix: iteratively discover nested SM nodes in state sub-graphs and
also scan StateSubGraphs in BuildStateMachineOverviewLayers.

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
@LoogLong
Copy link

LoogLong commented Mar 5, 2026

@copilot 1. tab页面现在开了就无法关闭,需要可以关闭。2. tab页面标签压缩到一行内显示

- Tab headers for dynamically-opened sub-graphs now include a close
  button (×). The initial AnimGraph tab is not closable.
- TabControl uses a custom template with horizontal ScrollViewer to
  keep all tabs in a single line instead of wrapping.
- Long tab names are truncated with ellipsis (MaxWidth=200).

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Copy link
Author

Copilot AI commented Mar 5, 2026

@copilot 1. tab页面现在开了就无法关闭,需要可以关闭。2. tab页面标签压缩到一行内显示

已在 5998953 中实现:

  1. 可关闭的 tab:动态打开的 tab(状态机概览、状态子图、LinkedAnimLayer)头部添加了 "×" 关闭按钮,关闭时清理状态并自动选择前一个 tab。初始 AnimGraph tab 不可关闭。
  2. 单行 tab 标签:TabControl 使用自定义模板,TabPanel 包裹在水平 ScrollViewer 中,tab 不再换行。长名称通过 TextTrimming 省略号截断(MaxWidth=200)。

Copilot AI and others added 13 commits March 5, 2026 02:34
…e nodes

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
…ph layer

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
- Revert Pass 1 exclusion so SaveCachedPose is naturally collected into
  the correct _Root layer when reachable via BFS
- Add EnforceSaveCachedPoseInAnimBlueprintLayer as a final post-processing
  step that scans all non-_Root layers (state sub-graphs and fallback
  layers) and moves any stray SaveCachedPose nodes to the primary _Root
  layer
- Extract RebuildLayerConnections helper for DRY connection rebuilding

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Instead of always placing SaveCachedPose in the primary (first) _Root
layer, trace downstream UseCachedPose consumers through the state
machine hierarchy (BelongsToStateMachine → StateMachineName) to find
the correct ancestor animation blueprint layer.

For example, a SaveCachedPose used by UseCachedPose in
AnimGraph > BaseLayer > LocomotionStates > IdleState is now correctly
placed in BaseLayer (the _Root layer containing LocomotionStates)
instead of AnimGraph.

- Add FindOwnerRootLayer: traces SaveCachedPose downstream consumers
  to find the correct _Root layer
- Add GetAncestorRootLayer: walks up the layer hierarchy via
  BelongsToStateMachine → StateMachineName chain
- Add BuildLayerLookups/LayerLookups: pre-computed lookup maps for
  efficient multi-node queries
- Update EnforceSaveCachedPoseInRootLayers to use smart layer detection

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
…am collection in Pass 3

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
…cases and outermost AnimGraph layer for fallback

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
…cies

When sequential Save/Use dependencies exist (e.g., UseCachedPose(A)->
SaveCachedPose(B)->UseCachedPose(B)->SaveCachedPose(C)), the stale
BuildLayerLookups built once before the loop caused FindOwnerRootLayer
to miss consumers assigned during earlier iterations.

The fix iteratively processes SaveCachedPose nodes: each pass rebuilds
lookups and resolves nodes whose consumers are already placed, deferring
nodes with unresolved consumers. A maxPasses guard prevents infinite loops.

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
When double-clicking a UseCachedPose node to jump to the SaveCachedPose
node's tab, the view now centers on the target node. Added CenterOnNode
helper that adjusts TranslateTransform to place the node at the viewport
center while preserving the current zoom level.

Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants