diff --git a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp index 512f25a21684..0d239a45772a 100644 --- a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp +++ b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp @@ -335,13 +335,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 44cde3cee7a5..8874217e176a 100644 --- a/scene/2d/physics/collision_shape_2d.cpp +++ b/scene/2d/physics/collision_shape_2d.cpp @@ -39,42 +39,97 @@ 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); } +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); } @@ -171,9 +226,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_null()) { warnings.push_back(RTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!")); @@ -298,7 +353,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 ab72f4cfee98..80d2269e1f84 100644 --- a/scene/2d/physics/collision_shape_2d.h +++ b/scene/2d/physics/collision_shape_2d.h @@ -42,12 +42,19 @@ 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; + 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(); // Not wrapped in `#ifdef DEBUG_ENABLED` as it is used for rendering. Color debug_color = Color(0.0, 0.0, 0.0, 0.0); diff --git a/scene/3d/physics/collision_shape_3d.cpp b/scene/3d/physics/collision_shape_3d.cpp index 5031e040354c..f797408e17ed 100644 --- a/scene/3d/physics/collision_shape_3d.cpp +++ b/scene/3d/physics/collision_shape_3d.cpp @@ -69,42 +69,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 +176,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_null()) { @@ -223,7 +277,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(); } @@ -321,7 +375,6 @@ void CollisionShape3D::_shape_changed() { CollisionShape3D::CollisionShape3D() { //indicator = RenderingServer::get_singleton()->mesh_create(); - set_notify_local_transform(true); debug_color = _get_default_debug_color(); } diff --git a/scene/3d/physics/collision_shape_3d.h b/scene/3d/physics/collision_shape_3d.h index 55e7e90bd17e..3247f239f17f 100644 --- a/scene/3d/physics/collision_shape_3d.h +++ b/scene/3d/physics/collision_shape_3d.h @@ -42,6 +42,7 @@ class CollisionShape3D : public Node3D { uint32_t owner_id = 0; CollisionObject3D *collision_object = nullptr; + Transform3D transform_to_col_obj_cache; Color debug_color; bool debug_fill = true; @@ -57,8 +58,14 @@ class CollisionShape3D : public Node3D { #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);