Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use crate::node_3d::Node3D;
use perro_ids::NodeID;
use std::ops::{Deref, DerefMut};

pub type BoneIndex = i32;

#[derive(Clone, Debug)]
pub struct BoneAttachment3D {
pub base: Node3D,
pub skeleton: Option<NodeID>,
pub bone_index: BoneIndex,
pub enabled: bool,
}

impl BoneAttachment3D {
pub const fn new() -> Self {
Self {
base: Node3D::new(),
skeleton: None,
bone_index: -1,
enabled: true,
}
}

pub fn set_skeleton(&mut self, skeleton: Option<NodeID>) {
self.skeleton = skeleton;
}

pub fn skeleton(&self) -> Option<NodeID> {
self.skeleton
}

pub fn set_bone_index(&mut self, bone_index: BoneIndex) {
self.bone_index = bone_index;
}

pub fn bone_index(&self) -> BoneIndex {
self.bone_index
}

pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}

pub fn enabled(&self) -> bool {
self.enabled
}
}

impl Default for BoneAttachment3D {
fn default() -> Self {
Self::new()
}
}

impl Deref for BoneAttachment3D {
type Target = Node3D;

fn deref(&self) -> &Self::Target {
&self.base
}
}

impl DerefMut for BoneAttachment3D {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
2 changes: 2 additions & 0 deletions perro_source/core/perro_nodes/src/nodes/node_3d/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#[path = "lights/ambient_light_3d.rs"]
pub mod ambient_light_3d;
pub mod bone_attachment_3d;
pub mod camera_3d;
pub mod mesh_instance_3d;
pub mod multi_mesh_instance_3d;
Expand All @@ -17,6 +18,7 @@ pub mod sky_3d;
pub mod spot_light_3d;

pub use ambient_light_3d::*;
pub use bone_attachment_3d::*;
pub use camera_3d::*;
pub use mesh_instance_3d::*;
pub use multi_mesh_instance_3d::*;
Expand Down
2 changes: 2 additions & 0 deletions perro_source/core/perro_nodes/src/nodes/node_registry.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::ambient_light_3d::AmbientLight3D;
use crate::animation_player::AnimationPlayer;
use crate::bone_attachment_3d::BoneAttachment3D;
use crate::camera_2d::Camera2D;
use crate::camera_3d::Camera3D;
use crate::mesh_instance_3d::MeshInstance3D;
Expand Down Expand Up @@ -822,6 +823,7 @@ define_scene_nodes! {
Area3D => (Node3D, Area3D, Renderable::False, InternalUpdate::False, InternalFixedUpdate::True),
RigidBody3D => (Node3D, RigidBody3D, Renderable::False, InternalUpdate::False, InternalFixedUpdate::True),
Skeleton3D => (Node3D, Skeleton3D, Renderable::False, InternalUpdate::False, InternalFixedUpdate::False),
BoneAttachment3D => (Node3D, BoneAttachment3D, Renderable::False, InternalUpdate::False, InternalFixedUpdate::False),
ParticleEmitter3D => (Node3D, ParticleEmitter3D, Renderable::True, InternalUpdate::True, InternalFixedUpdate::False),
//Lights
AmbientLight3D => (None, AmbientLight3D, Renderable::True, InternalUpdate::False, InternalFixedUpdate::False),
Expand Down
2 changes: 2 additions & 0 deletions perro_source/runtime_project/perro_runtime/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ impl Runtime {
self.schedules.snapshot_update(&self.scripts);
self.run_update_schedule();
self.run_internal_update_schedule();
self.update_bone_attachments();
}

#[inline]
Expand All @@ -255,6 +256,7 @@ impl Runtime {

let internal_start = std::time::Instant::now();
self.run_internal_update_schedule();
self.update_bone_attachments();
let internal_update = internal_start.elapsed();

RuntimeUpdateTiming {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::Runtime;
use perro_ids::NodeID;
use perro_input::InputWindow;
use perro_nodes::{InternalFixedUpdate, InternalUpdate, NodeType};
use perro_nodes::{InternalFixedUpdate, InternalUpdate, NodeType, SceneNodeData};
use perro_resource_context::ResourceWindow;
use perro_runtime_context::RuntimeWindow;

Expand Down Expand Up @@ -290,4 +290,40 @@ impl Runtime {
let mut ctx = RuntimeWindow::new(self);
perro_internal_updates::internal_fixed_update_node(&mut ctx, res, ipt, id);
}

pub(crate) fn update_bone_attachments(&mut self) {
let mut updates = Vec::new();
for (id, node) in self.nodes.iter() {
let SceneNodeData::BoneAttachment3D(attachment) = &node.data else {
continue;
};
if !attachment.enabled {
continue;
}
let Some(skeleton_id) = attachment.skeleton else {
continue;
};
let Some(skeleton_node) = self.nodes.get(skeleton_id) else {
continue;
};
let SceneNodeData::Skeleton3D(skeleton) = &skeleton_node.data else {
continue;
};
let Ok(bone_index) = usize::try_from(attachment.bone_index) else {
continue;
};
let Some(bone) = skeleton.bones.get(bone_index) else {
continue;
};
// TODO: apply local/user offsets once attachment offset support lands.
updates.push((id, bone.rest));
}

for (id, transform) in updates {
let _ = self.with_base_node_mut::<perro_nodes::Node3D, _, _>(id, |node| {
node.transform = transform;
});
self.mark_global_transform_dirty(id);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use perro_nodes::{
ambient_light_3d::AmbientLight3D,
animation_player::AnimationPlayer,
camera_2d::Camera2D,
bone_attachment_3d::BoneAttachment3D,
camera_3d::{Camera3D, CameraProjection},
mesh_instance_3d::{MaterialParamOverride, MaterialParamOverrideValue, MeshInstance3D, MeshSurfaceBinding},
multi_mesh_instance_3d::MultiMeshInstance3D,
Expand All @@ -29,6 +30,7 @@ use perro_scene::{
RayLight3DField, RigidBody2DField, RigidBody3DField, Scene, SceneFieldIterRef,
SceneKey, SceneNodeData as SceneDefNodeData, SceneNodeEntry as SceneDefNodeEntry,
SceneObjectField, SceneValue, Skeleton3DField, Sky3DField, SpotLight3DField, Sprite2DField,
BoneAttachment3DField,
StaticBody2DField, StaticBody3DField, resolve_node_field,
};
use perro_structs::{
Expand Down Expand Up @@ -611,6 +613,7 @@ fn scene_node_data_from(data: &SceneDefNodeData) -> Result<SceneNodeData, String
"Area3D" => Ok(SceneNodeData::Area3D(build_area_3d(data))),
"RigidBody3D" => Ok(SceneNodeData::RigidBody3D(build_rigid_body_3d(data))),
"Skeleton3D" => Ok(SceneNodeData::Skeleton3D(build_skeleton_3d(data))),
"BoneAttachment3D" => Ok(SceneNodeData::BoneAttachment3D(build_bone_attachment_3d(data))),
"Camera3D" => Ok(SceneNodeData::Camera3D(build_camera_3d(data))),
"ParticleEmitter3D" => Ok(SceneNodeData::ParticleEmitter3D(build_particle_emitter_3d(
data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ fn build_multi_mesh_instance_3d(data: &SceneDefNodeData) -> MultiMeshInstance3D
node
}


fn build_bone_attachment_3d(data: &SceneDefNodeData) -> BoneAttachment3D {
let mut node = BoneAttachment3D::new();
if let Some(base) = data.base_ref() {
apply_node_3d_data(&mut node, base);
}
apply_node_3d_fields(&mut node, &data.fields);
apply_bone_attachment_3d_fields(&mut node, &data.fields);
node
}

fn build_skeleton_3d(data: &SceneDefNodeData) -> Skeleton3D {
let mut node = Skeleton3D::new();
if let Some(base) = data.base_ref() {
Expand Down Expand Up @@ -120,6 +131,21 @@ fn apply_multi_mesh_instance_3d_fields(

fn apply_skeleton_3d_fields(_node: &mut Skeleton3D, _fields: &[SceneObjectField]) {}

fn apply_bone_attachment_3d_fields(node: &mut BoneAttachment3D, fields: &[SceneObjectField]) {
SceneFieldIterRef::new(fields).for_each(|name, value| match resolve_node_field("BoneAttachment3D", name) {
Some(NodeField::BoneAttachment3D(BoneAttachment3DField::Skeleton)) => {
node.skeleton = as_node_id(value);
}
Some(NodeField::BoneAttachment3D(BoneAttachment3DField::BoneIndex)) => {
if let Some(v) = as_i32(value) { node.bone_index = v; }
}
Some(NodeField::BoneAttachment3D(BoneAttachment3DField::Enabled)) => {
if let Some(v) = as_bool(value) { node.enabled = v; }
}
_ => {}
});
}

fn extract_mesh_source(data: &SceneDefNodeData) -> Option<String> {
if data.ty != "MeshInstance3D" && data.ty != "MultiMeshInstance3D" {
return None;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use perro_ids::NodeID;
use perro_nodes::{Bone3D, BoneAttachment3D, Node3D, SceneNode, SceneNodeData, Skeleton3D};
use perro_runtime::runtime::Runtime;
use perro_structs::{Transform3D, Vector3};

#[test]
fn bone_attachment_create_and_setters() {
let mut node = BoneAttachment3D::new();
let skeleton = NodeID::from_u64(99);
node.set_skeleton(Some(skeleton));
node.set_bone_index(2);
node.set_enabled(false);
assert_eq!(node.skeleton(), Some(skeleton));
assert_eq!(node.bone_index(), 2);
assert!(!node.enabled());
}

#[test]
fn bone_attachment_runtime_updates_transform() {
let mut runtime = Runtime::new();

let mut skeleton = Skeleton3D::new();
skeleton.bones.push(Bone3D { rest: Transform3D { position: Vector3::new(3.0, 4.0, 5.0), ..Transform3D::IDENTITY }, ..Bone3D::new()});
let skeleton_id = runtime.nodes.insert(SceneNode::new(SceneNodeData::Skeleton3D(skeleton)));

let mut attachment = BoneAttachment3D::new();
attachment.set_skeleton(Some(skeleton_id));
attachment.set_bone_index(0);
let attachment_id = runtime.nodes.insert(SceneNode::new(SceneNodeData::BoneAttachment3D(attachment)));

runtime.update_bone_attachments();

let t = runtime.with_base_node::<Node3D, _, _>(attachment_id, |node| node.transform).unwrap();
assert_eq!(t.position, Vector3::new(3.0, 4.0, 5.0));
}

#[test]
fn bone_attachment_invalid_index_or_disabled_safe() {
let mut runtime = Runtime::new();
let skeleton_id = runtime.nodes.insert(SceneNode::new(SceneNodeData::Skeleton3D(Skeleton3D::new())));
let mut attachment = BoneAttachment3D::new();
attachment.set_skeleton(Some(skeleton_id));
attachment.set_bone_index(100);
let attachment_id = runtime.nodes.insert(SceneNode::new(SceneNodeData::BoneAttachment3D(attachment.clone())));
runtime.update_bone_attachments();

let mut disabled = attachment;
disabled.set_enabled(false);
let _disabled_id = runtime.nodes.insert(SceneNode::new(SceneNodeData::BoneAttachment3D(disabled)));
runtime.update_bone_attachments();

assert!(runtime.nodes.get(attachment_id).is_some());
}
16 changes: 16 additions & 0 deletions perro_source/runtime_project/perro_scene/src/node_fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub enum NodeField {
Area2D(Area2DField),
MeshInstance3D(MeshInstance3DField),
Skeleton3D(Skeleton3DField),
BoneAttachment3D(BoneAttachment3DField),
Camera3D(Camera3DField),
ParticleEmitter3D(ParticleEmitter3DField),
AnimationPlayer(AnimationPlayerField),
Expand Down Expand Up @@ -105,6 +106,13 @@ pub enum Skeleton3DField {
Skeleton,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BoneAttachment3DField {
Skeleton,
BoneIndex,
Enabled,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Camera3DField {
Zoom,
Expand Down Expand Up @@ -309,6 +317,14 @@ pub fn resolve_node_field(node_type_name: &str, field: &str) -> Option<NodeField
"skeleton" => Some(NodeField::Skeleton3D(Skeleton3DField::Skeleton)),
_ => None,
},
NodeType::BoneAttachment3D => match field {
"skeleton" => Some(NodeField::BoneAttachment3D(BoneAttachment3DField::Skeleton)),
"bone_index" | "bone" => Some(NodeField::BoneAttachment3D(
BoneAttachment3DField::BoneIndex,
)),
"enabled" => Some(NodeField::BoneAttachment3D(BoneAttachment3DField::Enabled)),
_ => None,
},
NodeType::Camera3D => match field {
"zoom" => Some(NodeField::Camera3D(Camera3DField::Zoom)),
"projection" => Some(NodeField::Camera3D(Camera3DField::Projection)),
Expand Down
Loading