Bladeren bron

Fix Area3D signal emissions when using Jolt Physics

Mikael Hermansson 3 maanden geleden
bovenliggende
commit
b3ddb88035

+ 19 - 9
modules/jolt_physics/objects/jolt_area_3d.cpp

@@ -113,12 +113,14 @@ void JoltArea3D::_add_shape_pair(Overlap &p_overlap, const JPH::BodyID &p_body_i
 	p_overlap.rid = other_object->get_rid();
 	p_overlap.rid = other_object->get_rid();
 	p_overlap.instance_id = other_object->get_instance_id();
 	p_overlap.instance_id = other_object->get_instance_id();
 
 
-	ShapeIndexPair &shape_indices = p_overlap.shape_pairs[{ p_other_shape_id, p_self_shape_id }];
-
-	shape_indices.other = other_object->find_shape_index(p_other_shape_id);
-	shape_indices.self = find_shape_index(p_self_shape_id);
+	HashMap<ShapeIDPair, ShapeIndexPair, ShapeIDPair>::Iterator shape_pair = p_overlap.shape_pairs.find(ShapeIDPair(p_other_shape_id, p_self_shape_id));
+	if (shape_pair == p_overlap.shape_pairs.end()) {
+		const int other_shape_index = other_object->find_shape_index(p_other_shape_id);
+		const int self_shape_index = find_shape_index(p_self_shape_id);
+		shape_pair = p_overlap.shape_pairs.insert(ShapeIDPair(p_other_shape_id, p_self_shape_id), ShapeIndexPair(other_shape_index, self_shape_index));
+	}
 
 
-	p_overlap.pending_added.push_back(shape_indices);
+	p_overlap.pending_added.push_back(shape_pair->value);
 
 
 	_events_changed();
 	_events_changed();
 }
 }
@@ -143,12 +145,20 @@ void JoltArea3D::_flush_events(OverlapsById &p_objects, const Callable &p_callba
 		Overlap &overlap = E->value;
 		Overlap &overlap = E->value;
 
 
 		if (p_callback.is_valid()) {
 		if (p_callback.is_valid()) {
-			for (ShapeIndexPair &shape_indices : overlap.pending_removed) {
-				_report_event(p_callback, PhysicsServer3D::AREA_BODY_REMOVED, overlap.rid, overlap.instance_id, shape_indices.other, shape_indices.self);
+			for (const ShapeIndexPair &shape_indices : overlap.pending_added) {
+				int &ref_count = overlap.ref_counts[shape_indices];
+				if (ref_count++ == 0) {
+					_report_event(p_callback, PhysicsServer3D::AREA_BODY_ADDED, overlap.rid, overlap.instance_id, shape_indices.other, shape_indices.self);
+				}
 			}
 			}
 
 
-			for (ShapeIndexPair &shape_indices : overlap.pending_added) {
-				_report_event(p_callback, PhysicsServer3D::AREA_BODY_ADDED, overlap.rid, overlap.instance_id, shape_indices.other, shape_indices.self);
+			for (const ShapeIndexPair &shape_indices : overlap.pending_removed) {
+				int &ref_count = overlap.ref_counts[shape_indices];
+				ERR_CONTINUE(ref_count <= 0);
+				if (--ref_count == 0) {
+					_report_event(p_callback, PhysicsServer3D::AREA_BODY_REMOVED, overlap.rid, overlap.instance_id, shape_indices.other, shape_indices.self);
+					overlap.ref_counts.erase(shape_indices);
+				}
 			}
 			}
 		}
 		}
 
 

+ 7 - 0
modules/jolt_physics/objects/jolt_area_3d.h

@@ -73,6 +73,12 @@ private:
 		ShapeIndexPair(int p_other, int p_self) :
 		ShapeIndexPair(int p_other, int p_self) :
 				other(p_other), self(p_self) {}
 				other(p_other), self(p_self) {}
 
 
+		static uint32_t hash(const ShapeIndexPair &p_pair) {
+			uint32_t hash = hash_murmur3_one_32(p_pair.other);
+			hash = hash_murmur3_one_32(p_pair.self, hash);
+			return hash_fmix32(hash);
+		}
+
 		friend bool operator==(const ShapeIndexPair &p_lhs, const ShapeIndexPair &p_rhs) {
 		friend bool operator==(const ShapeIndexPair &p_lhs, const ShapeIndexPair &p_rhs) {
 			return (p_lhs.other == p_rhs.other) && (p_lhs.self == p_rhs.self);
 			return (p_lhs.other == p_rhs.other) && (p_lhs.self == p_rhs.self);
 		}
 		}
@@ -80,6 +86,7 @@ private:
 
 
 	struct Overlap {
 	struct Overlap {
 		HashMap<ShapeIDPair, ShapeIndexPair, ShapeIDPair> shape_pairs;
 		HashMap<ShapeIDPair, ShapeIndexPair, ShapeIDPair> shape_pairs;
+		HashMap<ShapeIndexPair, int, ShapeIndexPair> ref_counts;
 		LocalVector<ShapeIndexPair> pending_added;
 		LocalVector<ShapeIndexPair> pending_added;
 		LocalVector<ShapeIndexPair> pending_removed;
 		LocalVector<ShapeIndexPair> pending_removed;
 		RID rid;
 		RID rid;

+ 4 - 1
modules/jolt_physics/objects/jolt_shaped_object_3d.cpp

@@ -304,7 +304,10 @@ void JoltShapedObject3D::commit_shapes(bool p_optimize_compound) {
 		return;
 		return;
 	}
 	}
 
 
-	previous_jolt_shape = jolt_shape;
+	if (previous_jolt_shape == nullptr) {
+		previous_jolt_shape = jolt_shape;
+	}
+
 	jolt_shape = new_shape;
 	jolt_shape = new_shape;
 
 
 	space->get_body_iface().SetShape(jolt_body->GetID(), jolt_shape, false, JPH::EActivation::DontActivate);
 	space->get_body_iface().SetShape(jolt_body->GetID(), jolt_shape, false, JPH::EActivation::DontActivate);

+ 10 - 28
modules/jolt_physics/spaces/jolt_contact_listener_3d.cpp

@@ -246,13 +246,22 @@ bool JoltContactListener3D::_try_evaluate_area_overlap(const JPH::Body &p_body1,
 		return false;
 		return false;
 	}
 	}
 
 
-	auto evaluate = [&](const auto &p_area, const auto &p_object, const JPH::SubShapeIDPair &p_shape_pair) {
+	auto has_shifted = [](const JoltShapedObject3D &p_object, const JPH::SubShapeID &p_sub_shape_id) {
+		return p_object.get_previous_jolt_shape() != nullptr && p_object.get_jolt_shape()->GetSubShapeUserData(p_sub_shape_id) != p_object.get_previous_jolt_shape()->GetSubShapeUserData(p_sub_shape_id);
+	};
+
+	auto evaluate = [&](const JoltArea3D &p_area, const auto &p_object, const JPH::SubShapeIDPair &p_shape_pair) {
 		const MutexLock write_lock(write_mutex);
 		const MutexLock write_lock(write_mutex);
 
 
 		if (p_area.can_monitor(p_object)) {
 		if (p_area.can_monitor(p_object)) {
 			if (!area_overlaps.has(p_shape_pair)) {
 			if (!area_overlaps.has(p_shape_pair)) {
 				area_overlaps.insert(p_shape_pair);
 				area_overlaps.insert(p_shape_pair);
 				area_enters.insert(p_shape_pair);
 				area_enters.insert(p_shape_pair);
+			} else if (has_shifted(p_area, p_shape_pair.GetSubShapeID1()) || has_shifted(p_object, p_shape_pair.GetSubShapeID2())) {
+				// A shape has taken on the `JPH::SubShapeID` value of another shape, likely because of the other shape having been replaced or moved
+				// in some way, so we force the area to refresh its internal mappings by exiting and entering this shape pair.
+				area_exits.insert(p_shape_pair);
+				area_enters.insert(p_shape_pair);
 			}
 			}
 		} else {
 		} else {
 			if (area_overlaps.erase(p_shape_pair)) {
 			if (area_overlaps.erase(p_shape_pair)) {
@@ -456,32 +465,6 @@ void JoltContactListener3D::_flush_area_enters() {
 	area_enters.clear();
 	area_enters.clear();
 }
 }
 
 
-void JoltContactListener3D::_flush_area_shifts() {
-	for (const JPH::SubShapeIDPair &shape_pair : area_overlaps) {
-		auto is_shifted = [&](const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_sub_shape_id) {
-			const JoltShapedObject3D *object = space->try_get_shaped(p_body_id);
-			ERR_FAIL_NULL_V(object, false);
-
-			if (object->get_previous_jolt_shape() == nullptr) {
-				return false;
-			}
-
-			const JPH::Shape &current_shape = *object->get_jolt_shape();
-			const JPH::Shape &previous_shape = *object->get_previous_jolt_shape();
-
-			const uint32_t current_id = (uint32_t)current_shape.GetSubShapeUserData(p_sub_shape_id);
-			const uint32_t previous_id = (uint32_t)previous_shape.GetSubShapeUserData(p_sub_shape_id);
-
-			return current_id != previous_id;
-		};
-
-		if (is_shifted(shape_pair.GetBody1ID(), shape_pair.GetSubShapeID1()) || is_shifted(shape_pair.GetBody2ID(), shape_pair.GetSubShapeID2())) {
-			area_enters.insert(shape_pair);
-			area_exits.insert(shape_pair);
-		}
-	}
-}
-
 void JoltContactListener3D::_flush_area_exits() {
 void JoltContactListener3D::_flush_area_exits() {
 	for (const JPH::SubShapeIDPair &shape_pair : area_exits) {
 	for (const JPH::SubShapeIDPair &shape_pair : area_exits) {
 		const JPH::BodyID &body_id1 = shape_pair.GetBody1ID();
 		const JPH::BodyID &body_id1 = shape_pair.GetBody1ID();
@@ -523,7 +506,6 @@ void JoltContactListener3D::pre_step() {
 
 
 void JoltContactListener3D::post_step() {
 void JoltContactListener3D::post_step() {
 	_flush_contacts();
 	_flush_contacts();
-	_flush_area_shifts();
 	_flush_area_exits();
 	_flush_area_exits();
 	_flush_area_enters();
 	_flush_area_enters();
 }
 }

+ 0 - 1
modules/jolt_physics/spaces/jolt_contact_listener_3d.h

@@ -120,7 +120,6 @@ class JoltContactListener3D final
 
 
 	void _flush_contacts();
 	void _flush_contacts();
 	void _flush_area_enters();
 	void _flush_area_enters();
-	void _flush_area_shifts();
 	void _flush_area_exits();
 	void _flush_area_exits();
 
 
 public:
 public: