Skip to content

Commit

Permalink
Implement more arithmetic nodes (#60)
Browse files Browse the repository at this point in the history
Adds:
- `DecomposeVec3`
- `LerpVec3`
- `SlerpQuat`
- `FromEuler`
- `IntoEuler`
- `MulQuat`
- `InvertQuat`

One issue that's present is `FromEuler` and `IntoEuler` store a
`glam::EulerRot` to store which euler rotation mode to use for
conversions. This is treated as an opaque value by `bevy_reflect`, so
egui can't give you a dropdown box to select which variant you want.
Tracking PR: bevyengine/bevy#15349.

The default rot mode is YXZ, which is what most people will want.
  • Loading branch information
aecsocket authored Sep 22, 2024
1 parent 8eac6c7 commit adb0735
Show file tree
Hide file tree
Showing 14 changed files with 464 additions and 9 deletions.
25 changes: 22 additions & 3 deletions crates/bevy_animation_graph/src/core/animation_graph/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ use crate::{
core::{animation_clip::GraphClip, errors::AssetLoaderError},
nodes::{
AbsF32, AddF32, BlendNode, BuildVec3Node, ChainNode, ClampF32, ClipNode, CompareF32,
ConstBool, DivF32, FSMNode, FireEventNode, FlipLRNode, GraphNode, LoopNode, MulF32,
PaddingNode, RotationArcNode, RotationNode, SpeedNode, SubF32, TwoBoneIKNode,
ConstBool, DecomposeVec3Node, DivF32, FSMNode, FireEventNode, FlipLRNode, FromEulerNode,
GraphNode, IntoEulerNode, InvertQuatNode, LerpVec3Node, LoopNode, MulF32, MulQuatNode,
PaddingNode, RotationArcNode, RotationNode, SlerpQuatNode, SpeedNode, SubF32,
TwoBoneIKNode,
},
prelude::DummyNode,
};
Expand Down Expand Up @@ -192,9 +194,26 @@ impl AssetLoader for AnimationGraphLoader {
AnimationNodeTypeSerial::Padding {
interpolation_period,
} => PaddingNode::new(*interpolation_period).wrapped(&serial_node.name),
AnimationNodeTypeSerial::BuildVec3() => {
AnimationNodeTypeSerial::BuildVec3 => {
BuildVec3Node::new().wrapped(&serial_node.name)
}
AnimationNodeTypeSerial::DecomposeVec3 => {
DecomposeVec3Node::new().wrapped(&serial_node.name)
}
AnimationNodeTypeSerial::LerpVec3 => LerpVec3Node::new().wrapped(&serial_node.name),
AnimationNodeTypeSerial::SlerpQuat => {
SlerpQuatNode::new().wrapped(&serial_node.name)
}
AnimationNodeTypeSerial::FromEuler(mode) => {
FromEulerNode::new(*mode).wrapped(&serial_node.name)
}
AnimationNodeTypeSerial::IntoEuler(mode) => {
IntoEulerNode::new(*mode).wrapped(&serial_node.name)
}
AnimationNodeTypeSerial::MulQuat => MulQuatNode::new().wrapped(&serial_node.name),
AnimationNodeTypeSerial::InvertQuat => {
InvertQuatNode::new().wrapped(&serial_node.name)
}
};
graph.add_node(node);
}
Expand Down
20 changes: 17 additions & 3 deletions crates/bevy_animation_graph/src/core/animation_graph/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
prelude::{AnimationNode, AnimationNodeType, DataSpec, DataValue},
utils::ordered_map::OrderedMap,
};
use bevy::utils::HashMap;
use bevy::{math::EulerRot, utils::HashMap};
use serde::{Deserialize, Serialize};
// pub nodes: HashMap<String, AnimationNode>,
// /// Inverted, indexed by output node name.
Expand Down Expand Up @@ -90,7 +90,14 @@ pub enum AnimationNodeTypeSerial {
CompareF32(CompareOp),
AbsF32,
ConstBool(bool),
BuildVec3(),
BuildVec3,
DecomposeVec3,
LerpVec3,
SlerpQuat,
FromEuler(EulerRot),
IntoEuler(EulerRot),
MulQuat,
InvertQuat,
RotationArc,
FireEvent(AnimationEvent),
// IntoBoneSpace,
Expand Down Expand Up @@ -183,7 +190,14 @@ impl From<&AnimationNodeType> for AnimationNodeTypeSerial {
AnimationNodeType::CompareF32(n) => AnimationNodeTypeSerial::CompareF32(n.op),
AnimationNodeType::AbsF32(_) => AnimationNodeTypeSerial::AbsF32,
AnimationNodeType::ConstBool(n) => AnimationNodeTypeSerial::ConstBool(n.constant),
AnimationNodeType::BuildVec3(_) => AnimationNodeTypeSerial::BuildVec3(),
AnimationNodeType::BuildVec3(_) => AnimationNodeTypeSerial::BuildVec3,
AnimationNodeType::DecomposeVec3(_) => AnimationNodeTypeSerial::DecomposeVec3,
AnimationNodeType::LerpVec3(_) => AnimationNodeTypeSerial::LerpVec3,
AnimationNodeType::SlerpQuat(_) => AnimationNodeTypeSerial::SlerpQuat,
AnimationNodeType::FromEuler(n) => AnimationNodeTypeSerial::FromEuler(n.mode),
AnimationNodeType::IntoEuler(n) => AnimationNodeTypeSerial::IntoEuler(n.mode),
AnimationNodeType::MulQuat(_) => AnimationNodeTypeSerial::MulQuat,
AnimationNodeType::InvertQuat(_) => AnimationNodeTypeSerial::InvertQuat,
AnimationNodeType::RotationArc(_) => AnimationNodeTypeSerial::RotationArc,
AnimationNodeType::Fsm(n) => {
AnimationNodeTypeSerial::Fsm(n.fsm.path().unwrap().to_string())
Expand Down
38 changes: 36 additions & 2 deletions crates/bevy_animation_graph/src/core/animation_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ use super::{
use crate::{
nodes::{
AbsF32, AddF32, BlendNode, BuildVec3Node, ChainNode, ClampF32, ClipNode, CompareF32,
ConstBool, DivF32, DummyNode, FSMNode, FireEventNode, FlipLRNode, GraphNode, LoopNode,
MulF32, PaddingNode, RotationArcNode, RotationNode, SpeedNode, SubF32, TwoBoneIKNode,
ConstBool, DecomposeVec3Node, DivF32, DummyNode, FSMNode, FireEventNode, FlipLRNode,
FromEulerNode, GraphNode, IntoEulerNode, InvertQuatNode, LerpVec3Node, LoopNode, MulF32,
MulQuatNode, PaddingNode, RotationArcNode, RotationNode, SlerpQuatNode, SpeedNode, SubF32,
TwoBoneIKNode,
},
prelude::{PassContext, SpecContext},
};
Expand Down Expand Up @@ -205,6 +207,17 @@ pub enum AnimationNodeType {
// ------------------------------------------------
RotationArc(RotationArcNode),
BuildVec3(BuildVec3Node),
DecomposeVec3(DecomposeVec3Node),
LerpVec3(LerpVec3Node),
// ------------------------------------------------

// --- Quat arithmetic nodes
// ------------------------------------------------
SlerpQuat(SlerpQuatNode),
FromEuler(FromEulerNode),
IntoEuler(IntoEulerNode),
MulQuat(MulQuatNode),
InvertQuat(InvertQuatNode),
// ------------------------------------------------
Fsm(#[reflect(ignore)] FSMNode),
// HACK: needs to be ignored for now due to:
Expand Down Expand Up @@ -243,6 +256,13 @@ impl AnimationNodeType {
AnimationNodeType::AbsF32(n) => f(n),
AnimationNodeType::ConstBool(n) => f(n),
AnimationNodeType::BuildVec3(n) => f(n),
AnimationNodeType::DecomposeVec3(n) => f(n),
AnimationNodeType::LerpVec3(n) => f(n),
AnimationNodeType::SlerpQuat(n) => f(n),
AnimationNodeType::FromEuler(n) => f(n),
AnimationNodeType::IntoEuler(n) => f(n),
AnimationNodeType::MulQuat(n) => f(n),
AnimationNodeType::InvertQuat(n) => f(n),
AnimationNodeType::RotationArc(n) => f(n),
AnimationNodeType::Fsm(n) => f(n),
AnimationNodeType::Graph(n) => f(n),
Expand Down Expand Up @@ -279,6 +299,13 @@ impl AnimationNodeType {
AnimationNodeType::AbsF32(n) => f(n),
AnimationNodeType::ConstBool(n) => f(n),
AnimationNodeType::BuildVec3(n) => f(n),
AnimationNodeType::DecomposeVec3(n) => f(n),
AnimationNodeType::LerpVec3(n) => f(n),
AnimationNodeType::SlerpQuat(n) => f(n),
AnimationNodeType::FromEuler(n) => f(n),
AnimationNodeType::IntoEuler(n) => f(n),
AnimationNodeType::MulQuat(n) => f(n),
AnimationNodeType::InvertQuat(n) => f(n),
AnimationNodeType::RotationArc(n) => f(n),
AnimationNodeType::Fsm(n) => f(n),
AnimationNodeType::Graph(n) => f(n),
Expand Down Expand Up @@ -321,6 +348,13 @@ impl AnimationNodeType {
AnimationNodeType::AbsF32(n) => n,
AnimationNodeType::ConstBool(n) => n,
AnimationNodeType::BuildVec3(n) => n,
AnimationNodeType::DecomposeVec3(n) => n,
AnimationNodeType::LerpVec3(n) => n,
AnimationNodeType::SlerpQuat(n) => n,
AnimationNodeType::FromEuler(n) => n,
AnimationNodeType::IntoEuler(n) => n,
AnimationNodeType::MulQuat(n) => n,
AnimationNodeType::InvertQuat(n) => n,
AnimationNodeType::RotationArc(n) => n,
AnimationNodeType::Fsm(n) => n,
AnimationNodeType::Graph(n) => n,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl MulF32 {

impl NodeLike for MulF32 {
fn display_name(&self) -> String {
"× Multiply".into()
"× Multiply F32".into()
}

fn duration(&self, _ctx: PassContext) -> Result<(), GraphError> {
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_animation_graph/src/nodes/arithmetic/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
mod bool;
mod event_queue;
mod f32;
mod quat;
mod vec3;

pub use bool::*;
pub use event_queue::*;
pub use f32::*;
pub use quat::*;
pub use vec3::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use crate::core::animation_graph::PinMap;
use crate::core::animation_node::{AnimationNode, AnimationNodeType, NodeLike};
use crate::core::errors::GraphError;
use crate::core::prelude::DataSpec;
use crate::prelude::{PassContext, SpecContext};
use crate::utils::unwrap::UnwrapVal;
use bevy::prelude::*;

#[derive(Reflect, Clone, Debug, Default)]
#[reflect(Default)]
pub struct FromEulerNode {
pub mode: EulerRot,
}

impl FromEulerNode {
pub const INPUT: &'static str = "euler";
pub const OUTPUT: &'static str = "quat";

pub fn new(mode: EulerRot) -> Self {
Self { mode }
}

pub fn wrapped(self, name: impl Into<String>) -> AnimationNode {
AnimationNode::new_from_nodetype(name.into(), AnimationNodeType::FromEuler(self))
}
}

impl NodeLike for FromEulerNode {
fn update(&self, mut ctx: PassContext) -> Result<(), GraphError> {
let Vec3 { x, y, z } = ctx.data_back(Self::INPUT)?.val();

let output = Quat::from_euler(self.mode, x, y, z);

ctx.set_data_fwd(Self::OUTPUT, output);

Ok(())
}

fn data_input_spec(&self, _: SpecContext) -> PinMap<DataSpec> {
[(Self::INPUT.into(), DataSpec::Vec3)].into()
}

fn data_output_spec(&self, _: SpecContext) -> PinMap<DataSpec> {
[(Self::OUTPUT.into(), DataSpec::Quat)].into()
}

fn display_name(&self) -> String {
"Quat from Euler".into()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use crate::core::animation_graph::PinMap;
use crate::core::animation_node::{AnimationNode, AnimationNodeType, NodeLike};
use crate::core::errors::GraphError;
use crate::core::prelude::DataSpec;
use crate::prelude::{PassContext, SpecContext};
use crate::utils::unwrap::UnwrapVal;
use bevy::prelude::*;

#[derive(Reflect, Clone, Debug, Default)]
#[reflect(Default)]
pub struct IntoEulerNode {
pub mode: EulerRot,
}

impl IntoEulerNode {
pub const INPUT: &'static str = "quat";
pub const OUTPUT: &'static str = "euler";

pub fn new(mode: EulerRot) -> Self {
Self { mode }
}

pub fn wrapped(self, name: impl Into<String>) -> AnimationNode {
AnimationNode::new_from_nodetype(name.into(), AnimationNodeType::IntoEuler(self))
}
}

impl NodeLike for IntoEulerNode {
fn update(&self, mut ctx: PassContext) -> Result<(), GraphError> {
let quat: Quat = ctx.data_back(Self::INPUT)?.val();

let (x, y, z) = quat.to_euler(self.mode);
let output = Vec3::new(x, y, z);

ctx.set_data_fwd(Self::OUTPUT, output);

Ok(())
}

fn data_input_spec(&self, _: SpecContext) -> PinMap<DataSpec> {
[(Self::INPUT.into(), DataSpec::Quat)].into()
}

fn data_output_spec(&self, _: SpecContext) -> PinMap<DataSpec> {
[(Self::OUTPUT.into(), DataSpec::Vec3)].into()
}

fn display_name(&self) -> String {
"Quat into Euler".into()
}
}
47 changes: 47 additions & 0 deletions crates/bevy_animation_graph/src/nodes/arithmetic/quat/inverse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::core::animation_graph::PinMap;
use crate::core::animation_node::{AnimationNode, AnimationNodeType, NodeLike};
use crate::core::errors::GraphError;
use crate::core::prelude::DataSpec;
use crate::prelude::{PassContext, SpecContext};
use crate::utils::unwrap::UnwrapVal;
use bevy::prelude::*;

#[derive(Reflect, Clone, Debug, Default)]
#[reflect(Default)]
pub struct InvertQuatNode {}

impl InvertQuatNode {
pub const INPUT: &'static str = "quat";
pub const OUTPUT: &'static str = "inverse";

pub fn new() -> Self {
Self {}
}

pub fn wrapped(self, name: impl Into<String>) -> AnimationNode {
AnimationNode::new_from_nodetype(name.into(), AnimationNodeType::InvertQuat(self))
}
}

impl NodeLike for InvertQuatNode {
fn update(&self, mut ctx: PassContext) -> Result<(), GraphError> {
let input: Quat = ctx.data_back(Self::INPUT)?.val();
let output: Quat = input.inverse();

ctx.set_data_fwd(Self::OUTPUT, output);

Ok(())
}

fn data_input_spec(&self, _: SpecContext) -> PinMap<DataSpec> {
[(Self::INPUT.into(), DataSpec::Quat)].into()
}

fn data_output_spec(&self, _: SpecContext) -> PinMap<DataSpec> {
[(Self::OUTPUT.into(), DataSpec::Quat)].into()
}

fn display_name(&self) -> String {
"Invert Quat".into()
}
}
11 changes: 11 additions & 0 deletions crates/bevy_animation_graph/src/nodes/arithmetic/quat/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
mod from_euler;
mod into_euler;
mod inverse;
mod mul;
mod slerp;

pub use from_euler::*;
pub use into_euler::*;
pub use inverse::*;
pub use mul::*;
pub use slerp::*;
54 changes: 54 additions & 0 deletions crates/bevy_animation_graph/src/nodes/arithmetic/quat/mul.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use crate::core::animation_graph::PinMap;
use crate::core::animation_node::{AnimationNode, AnimationNodeType, NodeLike};
use crate::core::errors::GraphError;
use crate::core::prelude::DataSpec;
use crate::prelude::{PassContext, SpecContext};
use crate::utils::unwrap::UnwrapVal;
use bevy::prelude::*;

#[derive(Reflect, Clone, Debug, Default)]
#[reflect(Default)]
pub struct MulQuatNode {}

impl MulQuatNode {
pub const INPUT_A: &'static str = "a";
pub const INPUT_B: &'static str = "b";
pub const OUTPUT: &'static str = "out";

pub fn new() -> Self {
Self {}
}

pub fn wrapped(self, name: impl Into<String>) -> AnimationNode {
AnimationNode::new_from_nodetype(name.into(), AnimationNodeType::MulQuat(self))
}
}

impl NodeLike for MulQuatNode {
fn update(&self, mut ctx: PassContext) -> Result<(), GraphError> {
let a: Quat = ctx.data_back(Self::INPUT_A)?.val();
let b: Quat = ctx.data_back(Self::INPUT_B)?.val();

let output = a * b;

ctx.set_data_fwd(Self::OUTPUT, output);

Ok(())
}

fn data_input_spec(&self, _: SpecContext) -> PinMap<DataSpec> {
[
(Self::INPUT_A.into(), DataSpec::Quat),
(Self::INPUT_B.into(), DataSpec::Quat),
]
.into()
}

fn data_output_spec(&self, _: SpecContext) -> PinMap<DataSpec> {
[(Self::OUTPUT.into(), DataSpec::Quat)].into()
}

fn display_name(&self) -> String {
"× Multiply Quat".into()
}
}
Loading

0 comments on commit adb0735

Please sign in to comment.