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 Mar 27, 2024
1 parent 7d151c8 commit 103eed7
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 33 deletions.
Expand Up @@ -187,13 +187,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
78 changes: 63 additions & 15 deletions scene/2d/physics/collision_shape_2d.cpp
Expand Up @@ -39,8 +39,48 @@ void CollisionShape2D::_shape_changed() {
queue_redraw();
}

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_in_shape_owner(bool p_xform_only) {
collision_object->shape_owner_set_transform(owner_id, get_transform());
collision_object->shape_owner_set_transform(owner_id, _get_transform_to_collision_object());
if (p_xform_only) {
return;
}
Expand All @@ -54,32 +94,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<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);
}
} break;

case NOTIFICATION_UNPARENTED: {
case NOTIFICATION_EXIT_TREE: {
if (collision_object) {
collision_object->remove_shape_owner(owner_id);
}
Expand Down Expand Up @@ -176,9 +225,9 @@ bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double
PackedStringArray CollisionShape2D::get_configuration_warnings() const {
PackedStringArray warnings = Node::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_valid()) {
warnings.push_back(RTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!"));
Expand Down Expand Up @@ -288,7 +337,6 @@ void CollisionShape2D::_bind_methods() {
}

CollisionShape2D::CollisionShape2D() {
set_notify_local_transform(true);
set_hide_clip_children(true);
debug_color = _get_default_debug_color();
}
5 changes: 5 additions & 0 deletions scene/2d/physics/collision_shape_2d.h
Expand Up @@ -47,8 +47,13 @@ class CollisionShape2D : public Node2D {
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 _create_shape_owner_in_collision_object();
Color _get_default_debug_color() const;

protected:
Expand Down
78 changes: 63 additions & 15 deletions scene/3d/physics/collision_shape_3d.cpp
Expand Up @@ -70,41 +70,90 @@ void CollisionShape3D::make_convex_from_siblings() {
set_shape(shape_new);
}

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_in_shape_owner(bool p_xform_only) {
collision_object->shape_owner_set_transform(owner_id, get_transform());
collision_object->shape_owner_set_transform(owner_id, _get_transform_to_collision_object());
if (p_xform_only) {
return;
}
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_configuration_warnings();
} break;

case NOTIFICATION_UNPARENTED: {
case NOTIFICATION_EXIT_TREE: {
if (collision_object) {
collision_object->remove_shape_owner(owner_id);
}
Expand All @@ -122,9 +171,9 @@ void CollisionShape3D::resource_changed(Ref<Resource> res) {
PackedStringArray CollisionShape3D::get_configuration_warnings() const {
PackedStringArray warnings = Node::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_valid()) {
Expand Down Expand Up @@ -217,7 +266,6 @@ bool CollisionShape3D::is_disabled() const {

CollisionShape3D::CollisionShape3D() {
//indicator = RenderingServer::get_singleton()->mesh_create();
set_notify_local_transform(true);
}

CollisionShape3D::~CollisionShape3D() {
Expand Down
5 changes: 5 additions & 0 deletions scene/3d/physics/collision_shape_3d.h
Expand Up @@ -48,6 +48,11 @@ 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);

Expand Down

0 comments on commit 103eed7

Please sign in to comment.