Skip to content

Commit 0e8a9fc

Browse files
authored
feat: add MergeEventQueues node (#133)
This PR adds a `MergeEventQueues` node that concatenates two event queues into one. This is needed when you have multiple event sources (e.g. a `FireEventNode` and a `ClipNode`'s event output) that need to feed into a single event queue input like the FSM's driver. ### What it does - **`MergeEventQueues`** (`Merge Events`): Takes two `EventQueue` inputs and concatenates them into a single output queue. ### Discussion: is explicit merging the right approach? In the current architecture, event queues are explicit data pins that users must manually wire through the graph. This means combining events from multiple sources requires an explicit merge node. Esoterica takes a fundamentally different approach: events are appended into a **single shared buffer** during graph evaluation. Each node returns a `SampledEventRange` (start/end index pair) describing its slice. Blend nodes call `BlendEventRanges()` to combine children's ranges and scale weights by the blend factor. Events propagate implicitly alongside poses — users never wire event connections manually. The Esoterica approach is more elegant (no manual wiring, events automatically weighted by blend factors), but would require significant architectural changes to adopt here. This node is a pragmatic stopgap for the current pin-based event architecture. I may be missing prior discussion or design notes on this — apologies if this has already been considered and the current explicit approach was a deliberate choice. But I wanted to flag the question: is there interest in moving toward implicit event propagation (Esoterica-style shared buffer), or is the explicit pin-based approach the intended long-term design? Happy to hear if there's context I'm not aware of. Either way, this node solves an immediate wiring problem I ran into. ### AI Disclosure I used AI (Claude Code) to research how Esoterica handles event propagation and to generate the node implementation.
1 parent 7feaced commit 0e8a9fc

4 files changed

Lines changed: 55 additions & 1 deletion

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use bevy::prelude::*;
2+
use bevy_animation_graph_core::{
3+
animation_node::{NodeLike, ReflectNodeLike},
4+
context::{new_context::NodeContext, spec_context::SpecContext},
5+
edge_data::DataSpec,
6+
errors::GraphError,
7+
};
8+
9+
/// Merges two event queues into one.
10+
#[derive(Reflect, Clone, Debug, Default)]
11+
#[reflect(Default, NodeLike)]
12+
#[type_path = "bevy_animation_graph::builtin_nodes"]
13+
pub struct MergeEventQueues;
14+
15+
impl MergeEventQueues {
16+
pub const IN_A: &'static str = "in_a";
17+
pub const IN_B: &'static str = "in_b";
18+
pub const OUTPUT: &'static str = "out";
19+
20+
pub fn new() -> Self {
21+
Self
22+
}
23+
}
24+
25+
impl NodeLike for MergeEventQueues {
26+
fn display_name(&self) -> String {
27+
"Merge Events".into()
28+
}
29+
30+
fn update(&self, mut ctx: NodeContext) -> Result<(), GraphError> {
31+
let a = ctx.data_back(Self::IN_A)?.into_event_queue()?;
32+
let b = ctx.data_back(Self::IN_B)?.into_event_queue()?;
33+
ctx.set_data_fwd(Self::OUTPUT, a.concat(b));
34+
Ok(())
35+
}
36+
37+
fn spec(&self, mut ctx: SpecContext) -> Result<(), GraphError> {
38+
ctx.add_input_data(Self::IN_A, DataSpec::EventQueue)
39+
.add_input_data(Self::IN_B, DataSpec::EventQueue)
40+
.add_output_data(Self::OUTPUT, DataSpec::EventQueue);
41+
Ok(())
42+
}
43+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pub mod fire_event;
22
pub mod map_events;
3+
pub mod merge_event_queues;

crates/bevy_animation_graph_builtin_nodes/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use crate::{
66
chain_node::ChainNode,
77
clip_node::ClipNode,
88
dummy_node::DummyNode,
9-
event_queue::{fire_event::FireEventNode, map_events::MapEventsNode},
9+
event_queue::{
10+
fire_event::FireEventNode, map_events::MapEventsNode, merge_event_queues::MergeEventQueues,
11+
},
1012
f32::{
1113
abs_f32::AbsF32, add_f32::AddF32, clamp_f32::ClampF32, compare_f32::CompareF32,
1214
div_f32::DivF32, mul_f32::MulF32, sub_f32::SubF32,
@@ -86,6 +88,7 @@ impl BuiltinNodesPlugin {
8688
// event queue
8789
.register_type::<FireEventNode>()
8890
.register_type::<MapEventsNode>()
91+
.register_type::<MergeEventQueues>()
8992
// ragdoll
9093
.register_type::<ConstRagdollConfig>();
9194
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
title: Merge Event Queues Node
3+
authors: ["@baszalmstra"]
4+
pull_requests: [133]
5+
---
6+
7+
A new `MergeEventQueues` node has been added that concatenates two event queues into one. This is useful when multiple event sources (e.g. a `FireEventNode` and a `ClipNode`'s event output) need to feed into a single event queue input like the FSM's driver.

0 commit comments

Comments
 (0)