Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow CollisionShape nodes to be indirect children of bodies #77937

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
Comment on lines +114 to +115
Copy link
Contributor

@mihe mihe Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't an exact comparison like this still cause this cached transform to be invalidated quite often just by the nature of floating-point precision?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the transforms are exactly the same, then multiplying them will result in the exact same outputs. Within the same machine (not including network stuff), floating-point math is deterministic. If the float is slightly off due to precision issues, it will always be off by the exact same amount when the calculation is repeated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, somehow I thought the transform included that of the collision object, which it obviously doesn't, but even then you'd end up invalidating it just by virtue of the object moving around, so my concern never made much sense anyway.

Nevermind. :)

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
Loading