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

Add shape data to Bullet Area overlap data. #42711

Merged
merged 1 commit into from
Nov 20, 2021
Merged
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
150 changes: 88 additions & 62 deletions modules/bullet/area_bullet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ AreaBullet::AreaBullet() :

AreaBullet::~AreaBullet() {
// signal are handled by godot, so just clear without notify
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
overlappingObjects[i].object->on_exit_area(this);
for (int i = 0; i < overlapping_shapes.size(); i++) {
overlapping_shapes[i].other_object->on_exit_area(this);
}
}

Expand All @@ -70,20 +70,26 @@ void AreaBullet::dispatch_callbacks() {
}
isScratched = false;

// Reverse order because I've to remove EXIT objects
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
OverlappingObjectData &otherObj = overlappingObjects.write[i];
// Reverse order so items can be removed.
for (int i = overlapping_shapes.size() - 1; i >= 0; i--) {
OverlappingShapeData &overlapping_shape = overlapping_shapes.write[i];

switch (otherObj.state) {
switch (overlapping_shape.state) {
case OVERLAP_STATE_ENTER:
otherObj.state = OVERLAP_STATE_INSIDE;
call_event(otherObj.object, PhysicsServer3D::AREA_BODY_ADDED);
otherObj.object->on_enter_area(this);
overlapping_shape.state = OVERLAP_STATE_INSIDE;
call_event(overlapping_shape, PhysicsServer3D::AREA_BODY_ADDED);
if (_overlapping_shape_count(overlapping_shape.other_object) == 1) {
// This object's first shape being added.
overlapping_shape.other_object->on_enter_area(this);
}
break;
case OVERLAP_STATE_EXIT:
call_event(otherObj.object, PhysicsServer3D::AREA_BODY_REMOVED);
otherObj.object->on_exit_area(this);
overlappingObjects.remove(i); // Remove after callback
call_event(overlapping_shape, PhysicsServer3D::AREA_BODY_REMOVED);
if (_overlapping_shape_count(overlapping_shape.other_object) == 1) {
// This object's last shape being removed.
overlapping_shape.other_object->on_exit_area(this);
}
overlapping_shapes.remove(i); // Remove after callback
break;
case OVERLAP_STATE_INSIDE: {
if (otherObj.object->getType() == TYPE_RIGID_BODY) {
Expand All @@ -98,63 +104,101 @@ void AreaBullet::dispatch_callbacks() {
}
}

void AreaBullet::call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3D::AreaBodyStatus p_status) {
InOutEventCallback &event = eventsCallbacks[static_cast<int>(p_otherObject->getType())];
void AreaBullet::call_event(const OverlappingShapeData &p_overlapping_shape, PhysicsServer3D::AreaBodyStatus p_status) {
InOutEventCallback &event = eventsCallbacks[static_cast<int>(p_overlapping_shape.other_object->getType())];

if (!event.event_callback.is_valid()) {
event.event_callback = Callable();
return;
}

call_event_res[0] = p_status;
call_event_res[1] = p_otherObject->get_self(); // Other body
call_event_res[2] = p_otherObject->get_instance_id(); // instance ID
call_event_res[3] = 0; // other_body_shape ID
call_event_res[4] = 0; // self_shape ID
call_event_res[1] = p_overlapping_shape.other_object->get_self(); // RID
call_event_res[2] = p_overlapping_shape.other_object->get_instance_id(); // Object ID
call_event_res[3] = p_overlapping_shape.other_shape_id; // Other object's shape ID
call_event_res[4] = p_overlapping_shape.our_shape_id; // This area's shape ID

Callable::CallError outResp;
Variant ret;
event.event_callback.call((const Variant **)call_event_res, 5, ret, outResp);
}

void AreaBullet::scratch() {
if (isScratched) {
return;
int AreaBullet::_overlapping_shape_count(CollisionObjectBullet *p_other_object) {
int count = 0;
for (int i = 0; i < overlapping_shapes.size(); i++) {
if (overlapping_shapes[i].other_object == p_other_object) {
count++;
}
}
isScratched = true;
return count;
}

void AreaBullet::clear_overlaps(bool p_notify) {
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
if (p_notify) {
call_event(overlappingObjects[i].object, PhysicsServer3D::AREA_BODY_REMOVED);
int AreaBullet::_find_overlapping_shape(CollisionObjectBullet *p_other_object, uint32_t p_other_shape_id, uint32_t p_our_shape_id) {
for (int i = 0; i < overlapping_shapes.size(); i++) {
const OverlappingShapeData &overlapping_shape = overlapping_shapes[i];
if (overlapping_shape.other_object == p_other_object && overlapping_shape.other_shape_id == p_other_shape_id && overlapping_shape.our_shape_id == p_our_shape_id) {
return i;
}
overlappingObjects[i].object->on_exit_area(this);
}
overlappingObjects.clear();
return -1;
}

void AreaBullet::remove_overlap(CollisionObjectBullet *p_object, bool p_notify) {
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
if (overlappingObjects[i].object == p_object) {
if (p_notify) {
call_event(overlappingObjects[i].object, PhysicsServer3D::AREA_BODY_REMOVED);
}
overlappingObjects[i].object->on_exit_area(this);
overlappingObjects.remove(i);
break;
void AreaBullet::mark_all_overlaps_dirty() {
OverlappingShapeData *overlapping_shapes_w = overlapping_shapes.ptrw();
for (int i = 0; i < overlapping_shapes.size(); i++) {
// Don't overwrite OVERLAP_STATE_ENTER state.
if (overlapping_shapes_w[i].state != OVERLAP_STATE_ENTER) {
overlapping_shapes_w[i].state = OVERLAP_STATE_DIRTY;
}
}
}

int AreaBullet::find_overlapping_object(CollisionObjectBullet *p_colObj) {
const int size = overlappingObjects.size();
for (int i = 0; i < size; ++i) {
if (overlappingObjects[i].object == p_colObj) {
return i;
void AreaBullet::mark_object_overlaps_inside(CollisionObjectBullet *p_other_object) {
OverlappingShapeData *overlapping_shapes_w = overlapping_shapes.ptrw();
for (int i = 0; i < overlapping_shapes.size(); i++) {
if (overlapping_shapes_w[i].other_object == p_other_object && overlapping_shapes_w[i].state == OVERLAP_STATE_DIRTY) {
overlapping_shapes_w[i].state = OVERLAP_STATE_INSIDE;
}
}
}

void AreaBullet::set_overlap(CollisionObjectBullet *p_other_object, uint32_t p_other_shape_id, uint32_t p_our_shape_id) {
int i = _find_overlapping_shape(p_other_object, p_other_shape_id, p_our_shape_id);
if (i == -1) { // Not found, create new one.
OverlappingShapeData overlapping_shape(p_other_object, OVERLAP_STATE_ENTER, p_other_shape_id, p_our_shape_id);
overlapping_shapes.push_back(overlapping_shape);
p_other_object->notify_new_overlap(this);
isScratched = true;
} else {
overlapping_shapes.ptrw()[i].state = OVERLAP_STATE_INSIDE;
}
}

void AreaBullet::mark_all_dirty_overlaps_as_exit() {
OverlappingShapeData *overlapping_shapes_w = overlapping_shapes.ptrw();
for (int i = 0; i < overlapping_shapes.size(); i++) {
if (overlapping_shapes[i].state == OVERLAP_STATE_DIRTY) {
overlapping_shapes_w[i].state = OVERLAP_STATE_EXIT;
isScratched = true;
}
}
}

void AreaBullet::remove_object_overlaps(CollisionObjectBullet *p_object) {
// Reverse order so items can be removed.
for (int i = overlapping_shapes.size() - 1; i >= 0; i--) {
if (overlapping_shapes[i].other_object == p_object) {
overlapping_shapes.remove(i);
}
}
return -1;
}

void AreaBullet::clear_overlaps() {
for (int i = 0; i < overlapping_shapes.size(); i++) {
call_event(overlapping_shapes[i], PhysicsServer3D::AREA_BODY_REMOVED);
overlapping_shapes[i].other_object->on_exit_area(this);
}
overlapping_shapes.clear();
}

void AreaBullet::set_monitorable(bool p_monitorable) {
Expand Down Expand Up @@ -182,7 +226,7 @@ void AreaBullet::reload_body() {
void AreaBullet::set_space(SpaceBullet *p_space) {
// Clear the old space if there is one
if (space) {
clear_overlaps(false);
clear_overlaps();
isScratched = false;

// Remove this object form the physics world
Expand All @@ -203,24 +247,6 @@ void AreaBullet::on_collision_filters_change() {
updated = true;
}

void AreaBullet::add_overlap(CollisionObjectBullet *p_otherObject) {
scratch();
overlappingObjects.push_back(OverlappingObjectData(p_otherObject, OVERLAP_STATE_ENTER));
p_otherObject->notify_new_overlap(this);
}

void AreaBullet::put_overlap_as_exit(int p_index) {
scratch();
overlappingObjects.write[p_index].state = OVERLAP_STATE_EXIT;
}

void AreaBullet::put_overlap_as_inside(int p_index) {
// This check is required to be sure this body was inside
if (OVERLAP_STATE_DIRTY == overlappingObjects[p_index].state) {
overlappingObjects.write[p_index].state = OVERLAP_STATE_INSIDE;
}
}

void AreaBullet::set_param(PhysicsServer3D::AreaParameter p_param, const Variant &p_value) {
switch (p_param) {
case PhysicsServer3D::AREA_PARAM_GRAVITY:
Expand Down
51 changes: 23 additions & 28 deletions modules/bullet/area_bullet.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@
class btGhostObject;

class AreaBullet : public RigidCollisionObjectBullet {
friend void SpaceBullet::check_ghost_overlaps();

public:
struct InOutEventCallback {
Callable event_callback;
Expand All @@ -59,21 +57,19 @@ class AreaBullet : public RigidCollisionObjectBullet {
OVERLAP_STATE_EXIT // Mark ended overlaps
};

struct OverlappingObjectData {
CollisionObjectBullet *object = nullptr;
OverlapState state = OVERLAP_STATE_ENTER;

OverlappingObjectData() {}
OverlappingObjectData(CollisionObjectBullet *p_object, OverlapState p_state) :
object(p_object),
state(p_state) {}
OverlappingObjectData(const OverlappingObjectData &other) {
operator=(other);
}
void operator=(const OverlappingObjectData &other) {
object = other.object;
state = other.state;
}
struct OverlappingShapeData {
CollisionObjectBullet *other_object = nullptr;
OverlapState state = OVERLAP_STATE_DIRTY;
uint32_t other_shape_id = 0;
uint32_t our_shape_id = 0;

OverlappingShapeData() {}

OverlappingShapeData(CollisionObjectBullet *p_other_object, OverlapState p_state, uint32_t p_other_shape_id, uint32_t p_our_shape_id) :
other_object(p_other_object),
state(p_state),
other_shape_id(p_other_shape_id),
our_shape_id(p_our_shape_id) {}
};

private:
Expand All @@ -82,7 +78,9 @@ class AreaBullet : public RigidCollisionObjectBullet {
Variant *call_event_res_ptr[5] = {};

btGhostObject *btGhost = nullptr;
Vector<OverlappingObjectData> overlappingObjects;
Vector<OverlappingShapeData> overlapping_shapes;
int _overlapping_shape_count(CollisionObjectBullet *p_other_object);
int _find_overlapping_shape(CollisionObjectBullet *p_other_object, uint32_t p_other_shape_id, uint32_t p_our_shape_id);
bool monitorable = true;

PhysicsServer3D::AreaSpaceOverrideMode spOv_mode = PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED;
Expand All @@ -104,7 +102,6 @@ class AreaBullet : public RigidCollisionObjectBullet {
~AreaBullet();

_FORCE_INLINE_ btGhostObject *get_bt_ghost() const { return btGhost; }
int find_overlapping_object(CollisionObjectBullet *p_colObj);

void set_monitorable(bool p_monitorable);
_FORCE_INLINE_ bool is_monitorable() const { return monitorable; }
Expand Down Expand Up @@ -143,20 +140,18 @@ class AreaBullet : public RigidCollisionObjectBullet {
virtual void set_space(SpaceBullet *p_space);

virtual void dispatch_callbacks();
void call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3D::AreaBodyStatus p_status);
void scratch();

void clear_overlaps(bool p_notify);
// Dispatch the callbacks and removes from overlapping list
void remove_overlap(CollisionObjectBullet *p_object, bool p_notify);
void call_event(const OverlappingShapeData &p_overlapping_shape, PhysicsServer3D::AreaBodyStatus p_status);

virtual void on_collision_filters_change();
virtual void on_collision_checker_start() {}
virtual void on_collision_checker_end() { updated = false; }

void add_overlap(CollisionObjectBullet *p_otherObject);
void put_overlap_as_exit(int p_index);
void put_overlap_as_inside(int p_index);
void mark_all_overlaps_dirty();
void mark_object_overlaps_inside(CollisionObjectBullet *p_other_object);
void set_overlap(CollisionObjectBullet *p_other_object, uint32_t p_other_shape_id, uint32_t p_our_shape_id);
void mark_all_dirty_overlaps_as_exit();
void remove_object_overlaps(CollisionObjectBullet *p_object);
void clear_overlaps();

void set_param(PhysicsServer3D::AreaParameter p_param, const Variant &p_value);
Variant get_param(PhysicsServer3D::AreaParameter p_param) const;
Expand Down
10 changes: 5 additions & 5 deletions modules/bullet/collision_object_bullet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,9 @@ CollisionObjectBullet::CollisionObjectBullet(Type p_type) :
type(p_type) {}

CollisionObjectBullet::~CollisionObjectBullet() {
// Remove all overlapping, notify is not required since godot take care of it
for (int i = areasOverlapped.size() - 1; 0 <= i; --i) {
areasOverlapped[i]->remove_overlap(this, /*Notify*/ false);
for (int i = 0; i < areasOverlapped.size(); i++) {
areasOverlapped[i]->remove_object_overlaps(this);
}

destroyBulletCollisionObject();
}

Expand Down Expand Up @@ -178,7 +176,9 @@ bool CollisionObjectBullet::is_collisions_response_enabled() {
}

void CollisionObjectBullet::notify_new_overlap(AreaBullet *p_area) {
areasOverlapped.push_back(p_area);
if (areasOverlapped.find(p_area) == -1) {
areasOverlapped.push_back(p_area);
}
}

void CollisionObjectBullet::on_exit_area(AreaBullet *p_area) {
Expand Down
Loading