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 Oct 4, 2024
1 parent db66bd3 commit 64a5d59
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,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,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<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);
Expand All @@ -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<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 @@ -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<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 +343,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,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:
Expand Down
92 changes: 73 additions & 19 deletions scene/3d/physics/collision_shape_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<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 +177,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_valid()) {
Expand Down Expand Up @@ -194,7 +249,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 All @@ -217,7 +272,6 @@ bool CollisionShape3D::is_disabled() const {

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

CollisionShape3D::~CollisionShape3D() {
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,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<Resource> 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);
Expand Down

0 comments on commit 64a5d59

Please sign in to comment.