From 64a5d59a7bef8c9d88f11b794d44f140bb680ff1 Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Sun, 28 Jan 2024 00:29:36 -0600 Subject: [PATCH] Allow CollisionShape2D/3D nodes to be indirect children of bodies --- .../gltf_document_extension_physics.cpp | 9 +- scene/2d/physics/collision_shape_2d.cpp | 90 ++++++++++++++---- scene/2d/physics/collision_shape_2d.h | 9 +- scene/3d/physics/collision_shape_3d.cpp | 92 +++++++++++++++---- scene/3d/physics/collision_shape_3d.h | 9 +- 5 files changed, 167 insertions(+), 42 deletions(-) diff --git a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp index 5c26a1686b2e..2c449e82723d 100644 --- a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp +++ b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp @@ -192,13 +192,16 @@ CollisionObject3D *_generate_shape_with_body(Ref p_state, Ref(p_scene_parent); + if (unlikely(!parent_3d)) { + return nullptr; + } CollisionObject3D *co = Object::cast_to(p_scene_parent); if (likely(co)) { return co; } + p_scene_parent = p_scene_parent->get_parent(); } return nullptr; } diff --git a/scene/2d/physics/collision_shape_2d.cpp b/scene/2d/physics/collision_shape_2d.cpp index bdd0d06b5e2a..a5187fb3866e 100644 --- a/scene/2d/physics/collision_shape_2d.cpp +++ b/scene/2d/physics/collision_shape_2d.cpp @@ -39,11 +39,57 @@ void CollisionShape2D::_shape_changed() { queue_redraw(); } -void CollisionShape2D::_update_in_shape_owner(bool p_xform_only) { - collision_object->shape_owner_set_transform(owner_id, get_transform()); - if (p_xform_only) { +CollisionObject2D *CollisionShape2D::_get_ancestor_collision_object() const { + Node *parent = get_parent(); + while (parent) { + CanvasItem *parent_2d = Object::cast_to(parent); + if (unlikely(!parent_2d)) { + return nullptr; + } + CollisionObject2D *co = Object::cast_to(parent); + if (likely(co)) { + return co; + } + parent = parent->get_parent(); + } + return nullptr; +} + +Transform2D CollisionShape2D::_get_transform_to_collision_object() const { + Transform2D transform_to_col_obj = get_transform(); + Node *parent = get_parent(); + while (parent != collision_object) { + CanvasItem *parent_2d = Object::cast_to(parent); + if (unlikely(!parent_2d)) { + break; + } + transform_to_col_obj = parent_2d->get_transform() * transform_to_col_obj; + parent = parent->get_parent(); + } + return transform_to_col_obj; +} + +void CollisionShape2D::_set_transform_notifications() { + if (collision_object == get_parent()) { + set_notify_local_transform(true); + set_notify_transform(false); + } else { + set_notify_local_transform(false); + set_notify_transform(true); + } +} + +void CollisionShape2D::_update_transform_in_shape_owner() { + Transform2D transform_to_col_obj = _get_transform_to_collision_object(); + if (transform_to_col_obj == transform_to_col_obj_cache) { return; } + transform_to_col_obj_cache = transform_to_col_obj; + collision_object->shape_owner_set_transform(owner_id, transform_to_col_obj); +} + +void CollisionShape2D::_update_in_shape_owner() { + _update_transform_in_shape_owner(); collision_object->shape_owner_set_disabled(owner_id, disabled); collision_object->shape_owner_set_one_way_collision(owner_id, one_way_collision); collision_object->shape_owner_set_one_way_collision_margin(owner_id, one_way_collision_margin); @@ -54,32 +100,41 @@ Color CollisionShape2D::_get_default_debug_color() const { return st ? st->get_debug_collisions_color() : Color(); } +void CollisionShape2D::_create_shape_owner_in_collision_object() { + owner_id = collision_object->create_shape_owner(this); + if (shape.is_valid()) { + collision_object->shape_owner_add_shape(owner_id, shape); + } + _set_transform_notifications(); + _update_in_shape_owner(); +} + void CollisionShape2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_PARENTED: { - collision_object = Object::cast_to(get_parent()); + collision_object = _get_ancestor_collision_object(); if (collision_object) { - owner_id = collision_object->create_shape_owner(this); - if (shape.is_valid()) { - collision_object->shape_owner_add_shape(owner_id, shape); - } - _update_in_shape_owner(); + _create_shape_owner_in_collision_object(); } } break; - case NOTIFICATION_ENTER_TREE: { - if (collision_object) { - _update_in_shape_owner(); + CollisionObject2D *ancestor_col_obj = _get_ancestor_collision_object(); + if (ancestor_col_obj != collision_object) { + collision_object = ancestor_col_obj; + if (collision_object) { + _create_shape_owner_in_collision_object(); + } } } break; - case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: + case NOTIFICATION_TRANSFORM_CHANGED: { if (collision_object) { - _update_in_shape_owner(true); + _update_transform_in_shape_owner(); } } break; - case NOTIFICATION_UNPARENTED: { + case NOTIFICATION_EXIT_TREE: { if (collision_object) { collision_object->remove_shape_owner(owner_id); } @@ -176,9 +231,9 @@ bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double PackedStringArray CollisionShape2D::get_configuration_warnings() const { PackedStringArray warnings = Node2D::get_configuration_warnings(); - CollisionObject2D *col_object = Object::cast_to(get_parent()); + CollisionObject2D *col_object = _get_ancestor_collision_object(); if (col_object == nullptr) { - warnings.push_back(RTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node.\nPlease only use it as a child of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape.")); + warnings.push_back(RTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node.\nPlease only use it as a descendant of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape.")); } if (!shape.is_valid()) { warnings.push_back(RTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!")); @@ -288,7 +343,6 @@ void CollisionShape2D::_bind_methods() { } CollisionShape2D::CollisionShape2D() { - set_notify_local_transform(true); set_hide_clip_children(true); debug_color = _get_default_debug_color(); } diff --git a/scene/2d/physics/collision_shape_2d.h b/scene/2d/physics/collision_shape_2d.h index 65436f1539eb..0942774e6212 100644 --- a/scene/2d/physics/collision_shape_2d.h +++ b/scene/2d/physics/collision_shape_2d.h @@ -42,13 +42,20 @@ class CollisionShape2D : public Node2D { Rect2 rect = Rect2(-Point2(10, 10), Point2(20, 20)); uint32_t owner_id = 0; CollisionObject2D *collision_object = nullptr; + Transform2D transform_to_col_obj_cache; bool disabled = false; bool one_way_collision = false; real_t one_way_collision_margin = 1.0; Color debug_color; + CollisionObject2D *_get_ancestor_collision_object() const; + Transform2D _get_transform_to_collision_object() const; + void _set_transform_notifications(); + void _shape_changed(); - void _update_in_shape_owner(bool p_xform_only = false); + void _update_transform_in_shape_owner(); + void _update_in_shape_owner(); + void _create_shape_owner_in_collision_object(); Color _get_default_debug_color() const; protected: diff --git a/scene/3d/physics/collision_shape_3d.cpp b/scene/3d/physics/collision_shape_3d.cpp index 304fa74b06a5..6024c85c1bc4 100644 --- a/scene/3d/physics/collision_shape_3d.cpp +++ b/scene/3d/physics/collision_shape_3d.cpp @@ -70,41 +70,96 @@ void CollisionShape3D::make_convex_from_siblings() { set_shape(shape_new); } -void CollisionShape3D::_update_in_shape_owner(bool p_xform_only) { - collision_object->shape_owner_set_transform(owner_id, get_transform()); - if (p_xform_only) { +CollisionObject3D *CollisionShape3D::_get_ancestor_collision_object() const { + Node *parent = get_parent(); + while (parent) { + Node3D *parent_3d = Object::cast_to(parent); + if (unlikely(!parent_3d)) { + return nullptr; + } + CollisionObject3D *co = Object::cast_to(parent); + if (likely(co)) { + return co; + } + parent = parent->get_parent(); + } + return nullptr; +} + +Transform3D CollisionShape3D::_get_transform_to_collision_object() const { + Transform3D transform_to_col_obj = get_transform(); + Node *parent = get_parent(); + while (parent != collision_object) { + Node3D *parent_3d = Object::cast_to(parent); + if (unlikely(!parent_3d)) { + break; + } + transform_to_col_obj = parent_3d->get_transform() * transform_to_col_obj; + parent = parent->get_parent(); + } + return transform_to_col_obj; +} + +void CollisionShape3D::_set_transform_notifications() { + if (collision_object == get_parent()) { + set_notify_local_transform(true); + set_notify_transform(false); + } else { + set_notify_local_transform(false); + set_notify_transform(true); + } +} + +void CollisionShape3D::_update_transform_in_shape_owner() { + Transform3D transform_to_col_obj = _get_transform_to_collision_object(); + if (transform_to_col_obj == transform_to_col_obj_cache) { return; } + transform_to_col_obj_cache = transform_to_col_obj; + collision_object->shape_owner_set_transform(owner_id, transform_to_col_obj); +} + +void CollisionShape3D::_update_in_shape_owner() { + _update_transform_in_shape_owner(); collision_object->shape_owner_set_disabled(owner_id, disabled); } +void CollisionShape3D::_create_shape_owner_in_collision_object() { + owner_id = collision_object->create_shape_owner(this); + if (shape.is_valid()) { + collision_object->shape_owner_add_shape(owner_id, shape); + } + _set_transform_notifications(); + _update_in_shape_owner(); +} + void CollisionShape3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_PARENTED: { - collision_object = Object::cast_to(get_parent()); + collision_object = _get_ancestor_collision_object(); if (collision_object) { - owner_id = collision_object->create_shape_owner(this); - if (shape.is_valid()) { - collision_object->shape_owner_add_shape(owner_id, shape); - } - _update_in_shape_owner(); + _create_shape_owner_in_collision_object(); } } break; - case NOTIFICATION_ENTER_TREE: { - if (collision_object) { - _update_in_shape_owner(); + CollisionObject3D *ancestor_col_obj = _get_ancestor_collision_object(); + if (ancestor_col_obj != collision_object) { + collision_object = ancestor_col_obj; + if (collision_object) { + _create_shape_owner_in_collision_object(); + } } } break; - case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: + case NOTIFICATION_TRANSFORM_CHANGED: { if (collision_object) { - _update_in_shape_owner(true); + _update_transform_in_shape_owner(); } update_configuration_warnings(); } break; - case NOTIFICATION_UNPARENTED: { + case NOTIFICATION_EXIT_TREE: { if (collision_object) { collision_object->remove_shape_owner(owner_id); } @@ -122,9 +177,9 @@ void CollisionShape3D::resource_changed(Ref res) { PackedStringArray CollisionShape3D::get_configuration_warnings() const { PackedStringArray warnings = Node3D::get_configuration_warnings(); - CollisionObject3D *col_object = Object::cast_to(get_parent()); + CollisionObject3D *col_object = _get_ancestor_collision_object(); if (col_object == nullptr) { - warnings.push_back(RTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node.\nPlease only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape.")); + warnings.push_back(RTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node.\nPlease only use it as a descendant of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape.")); } if (!shape.is_valid()) { @@ -194,7 +249,7 @@ void CollisionShape3D::set_shape(const Ref &p_shape) { if (is_inside_tree() && collision_object) { // If this is a heightfield shape our center may have changed - _update_in_shape_owner(true); + _update_transform_in_shape_owner(); } update_configuration_warnings(); } @@ -217,7 +272,6 @@ bool CollisionShape3D::is_disabled() const { CollisionShape3D::CollisionShape3D() { //indicator = RenderingServer::get_singleton()->mesh_create(); - set_notify_local_transform(true); } CollisionShape3D::~CollisionShape3D() { diff --git a/scene/3d/physics/collision_shape_3d.h b/scene/3d/physics/collision_shape_3d.h index 15f6ef73cbf1..6854c9fb3a38 100644 --- a/scene/3d/physics/collision_shape_3d.h +++ b/scene/3d/physics/collision_shape_3d.h @@ -42,14 +42,21 @@ class CollisionShape3D : public Node3D { uint32_t owner_id = 0; CollisionObject3D *collision_object = nullptr; + Transform3D transform_to_col_obj_cache; #ifndef DISABLE_DEPRECATED void resource_changed(Ref res); #endif bool disabled = false; + CollisionObject3D *_get_ancestor_collision_object() const; + Transform3D _get_transform_to_collision_object() const; + void _set_transform_notifications(); + void _create_shape_owner_in_collision_object(); + protected: - void _update_in_shape_owner(bool p_xform_only = false); + void _update_transform_in_shape_owner(); + void _update_in_shape_owner(); protected: void _notification(int p_what);