Skip to content

Commit

Permalink
Allow CollisionShape2D/3D nodes to be indirect children of bodies
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronfranke committed Jan 5, 2025
1 parent bdf625b commit 8f614ab
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -335,13 +335,16 @@ CollisionObject3D *_generate_shape_with_body(Ref<GLTFState> p_state, Ref<GLTFNod
#endif // DISABLE_DEPRECATED

CollisionObject3D *_get_ancestor_collision_object(Node *p_scene_parent) {
// Note: Despite the name of the method, at the moment this only checks
// the direct parent. Only check more later if Godot adds support for it.
if (p_scene_parent) {
while (p_scene_parent) {
Node3D *parent_3d = Object::cast_to<Node3D>(p_scene_parent);
if (unlikely(!parent_3d)) {
return nullptr;
}
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_scene_parent);
if (likely(co)) {
return co;
}
p_scene_parent = p_scene_parent->get_parent();
}
return nullptr;
}
Expand Down
90 changes: 72 additions & 18 deletions scene/2d/physics/collision_shape_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CanvasItem>(parent);
if (unlikely(!parent_2d)) {
return nullptr;
}
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(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<CanvasItem>(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<CollisionObject2D>(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);
}
Expand Down Expand Up @@ -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<CollisionObject2D>(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!"));
Expand Down Expand Up @@ -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();
}
9 changes: 8 additions & 1 deletion scene/2d/physics/collision_shape_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
93 changes: 73 additions & 20 deletions scene/3d/physics/collision_shape_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Node3D>(parent);
if (unlikely(!parent_3d)) {
return nullptr;
}
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(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<Node3D>(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<CollisionObject3D>(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);
}
Expand All @@ -122,9 +176,9 @@ void CollisionShape3D::resource_changed(Ref<Resource> res) {
PackedStringArray CollisionShape3D::get_configuration_warnings() const {
PackedStringArray warnings = Node3D::get_configuration_warnings();

CollisionObject3D *col_object = Object::cast_to<CollisionObject3D>(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()) {
Expand Down Expand Up @@ -223,7 +277,7 @@ void CollisionShape3D::set_shape(const Ref<Shape3D> &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();
}
Expand Down Expand Up @@ -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();
}

Expand Down
9 changes: 8 additions & 1 deletion scene/3d/physics/collision_shape_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down

0 comments on commit 8f614ab

Please sign in to comment.