1010#include < glm/gtc/quaternion.hpp>
1111#define GLM_ENABLE_EXPERIMENTAL
1212#include < glm/gtx/quaternion.hpp>
13+ #include < vector>
14+ #include < print>
15+
16+ bool plugins::Physics::checkAABBCollision (const glm::vec3& pos1, const glm::vec3& halfExtents1, const glm::vec3& pos2,
17+ const glm::vec3& halfExtents2, glm::vec3& penetrationDepth) {
18+ // Calculate the distance between centers
19+ glm::vec3 distance = pos1 - pos2;
20+
21+ // Calculate the sum of half extents
22+ glm::vec3 totalHalfExtents = halfExtents1 + halfExtents2;
23+
24+ // Check for overlap on all axes
25+ glm::vec3 overlap = totalHalfExtents - glm::abs (distance);
26+
27+ // If any axis has no overlap, there's no collision
28+ if (overlap.x <= 0 .0f || overlap.y <= 0 .0f || overlap.z <= 0 .0f ) {
29+ return false ;
30+ }
31+
32+ // Find the axis with minimum overlap (this is our separation axis)
33+ if (overlap.x <= overlap.y && overlap.x <= overlap.z ) {
34+ penetrationDepth = glm::vec3 (overlap.x * (distance.x < 0 ? -1 .0f : 1 .0f ), 0 .0f , 0 .0f );
35+ } else if (overlap.y <= overlap.z ) {
36+ penetrationDepth = glm::vec3 (0 .0f , overlap.y * (distance.y < 0 ? -1 .0f : 1 .0f ), 0 .0f );
37+ } else {
38+ penetrationDepth = glm::vec3 (0 .0f , 0 .0f , overlap.z * (distance.z < 0 ? -1 .0f : 1 .0f ));
39+ }
40+
41+ return true ;
42+ }
1343
1444void plugins::Physics::build (Game& game) {
1545 game.addSystem (Schedule::Update, [](std::shared_ptr<entt::registry>& registry, resources::Time& time) {
1646 float deltaTime = time.deltaTime ;
1747
48+ // Update velocities first
1849 registry->view <components::AngularVelocity, components::AngularAcceleration>().each (
1950 [®istry, deltaTime](entt::entity entity, auto & angularVelocity, const auto & angularAcceleration) {
2051 angularVelocity.value += angularAcceleration.value * deltaTime;
@@ -27,6 +58,15 @@ void plugins::Physics::build(Game& game) {
2758 registry->replace <components::Velocity>(entity, velocity);
2859 });
2960
61+ // Store original positions for collision detection
62+ auto colliderView = registry->view <components::Position, components::BoxCollider>();
63+ std::vector<std::pair<entt::entity, glm::vec3>> originalPositions;
64+
65+ for (auto entity : colliderView) {
66+ auto & pos = colliderView.get <components::Position>(entity);
67+ originalPositions.emplace_back (entity, pos.value );
68+ }
69+
3070 // Update rotation based on angular velocity
3171 registry->view <components::Rotation, components::AngularVelocity>().each (
3272 [®istry, deltaTime](entt::entity entity, auto & rotation, const auto & angularVelocity) {
@@ -41,10 +81,117 @@ void plugins::Physics::build(Game& game) {
4181 }
4282 });
4383
84+ // Update positions based on velocity
4485 registry->view <components::Position, components::Velocity>().each (
4586 [®istry, deltaTime](entt::entity entity, auto & position, const auto & velocity) {
4687 position.value += velocity.value * deltaTime;
4788 registry->replace <components::Position>(entity, position);
4889 });
90+
91+ // Collision detection and response
92+ std::vector<plugins::physics::components::CollisionEvent> collisionEvents;
93+
94+ // Check all pairs of colliders
95+ for (auto entity1 : colliderView) {
96+ auto & pos1 = colliderView.get <components::Position>(entity1);
97+ auto & collider1 = colliderView.get <components::BoxCollider>(entity1);
98+
99+ for (auto entity2 : colliderView) {
100+ // Don't check collision with self
101+ if (entity1 >= entity2)
102+ continue ;
103+
104+ auto & pos2 = colliderView.get <components::Position>(entity2);
105+ auto & collider2 = colliderView.get <components::BoxCollider>(entity2);
106+
107+ glm::vec3 penetrationDepth;
108+ if (checkAABBCollision (pos1.value , collider1.halfExtents , pos2.value , collider2.halfExtents , penetrationDepth)) {
109+ // Create collision event
110+ plugins::physics::components::CollisionEvent event;
111+ event.entity1 = entity1;
112+ event.entity2 = entity2;
113+ event.penetrationDepth = penetrationDepth;
114+ collisionEvents.push_back (event);
115+
116+ // Improved collision response
117+ glm::vec3 collisionNormal = glm::normalize (penetrationDepth);
118+ float penetrationMagnitude = glm::length (penetrationDepth);
119+
120+ // Separate objects by full penetration distance plus small margin
121+ glm::vec3 separation = collisionNormal * (penetrationMagnitude + 0 .001f );
122+
123+ // Check if entities have velocity to determine mass-like behavior
124+ auto * vel1 = registry->try_get <components::Velocity>(entity1);
125+ auto * vel2 = registry->try_get <components::Velocity>(entity2);
126+
127+ if (vel1 && vel2) {
128+ // Both objects can move - split separation
129+ pos1.value += separation * 0 .5f ;
130+ pos2.value -= separation * 0 .5f ;
131+
132+ // Apply velocity changes for bouncing/energy loss
133+ float restitution = 0 .3f ; // Energy loss factor
134+ glm::vec3 relativeVelocity = vel1->value - vel2->value ;
135+ float velocityAlongNormal = glm::dot (relativeVelocity, collisionNormal);
136+
137+ if (velocityAlongNormal > 0 ) {
138+ continue ; // Objects separating
139+ }
140+
141+ float impulse = -(1 + restitution) * velocityAlongNormal;
142+ glm::vec3 impulseVector = impulse * collisionNormal;
143+
144+ vel1->value += impulseVector * 0 .5f ;
145+ vel2->value -= impulseVector * 0 .5f ;
146+
147+ registry->replace <components::Velocity>(entity1, *vel1);
148+ registry->replace <components::Velocity>(entity2, *vel2);
149+ } else if (vel1) {
150+ // Only entity1 can move (entity2 is static)
151+ pos1.value += separation;
152+ if (glm::dot (vel1->value , collisionNormal) < 0 ) {
153+ // Remove velocity component in collision direction
154+ vel1->value -= glm::dot (vel1->value , collisionNormal) * collisionNormal * 1 .3f ;
155+ registry->replace <components::Velocity>(entity1, *vel1);
156+ }
157+ } else if (vel2) {
158+ // Only entity2 can move (entity1 is static)
159+ pos2.value -= separation;
160+ if (glm::dot (vel2->value , collisionNormal) > 0 ) {
161+ // Remove velocity component in collision direction
162+ vel2->value -= glm::dot (vel2->value , collisionNormal) * collisionNormal * 1 .3f ;
163+ registry->replace <components::Velocity>(entity2, *vel2);
164+ }
165+ } else {
166+ // Both are static - just separate
167+ pos1.value += separation * 0 .5f ;
168+ pos2.value -= separation * 0 .5f ;
169+ }
170+
171+ // Update positions in registry
172+ registry->replace <components::Position>(entity1, pos1);
173+ registry->replace <components::Position>(entity2, pos2);
174+ }
175+ }
176+ }
177+
178+ // Process collision callbacks
179+ for (const auto & event : collisionEvents) {
180+ // Check if entities have collision callbacks
181+ auto * callback1 = registry->try_get <plugins::physics::components::CollisionCallback>(event.entity1 );
182+ auto * callback2 = registry->try_get <plugins::physics::components::CollisionCallback>(event.entity2 );
183+
184+ if (callback1 && callback1->onCollisionEnter ) {
185+ callback1->onCollisionEnter (event);
186+ }
187+ if (callback2 && callback2->onCollisionEnter ) {
188+ // Create event from entity2's perspective
189+ plugins::physics::components::CollisionEvent reverseEvent = event;
190+ reverseEvent.entity1 = event.entity2 ;
191+ reverseEvent.entity2 = event.entity1 ;
192+ reverseEvent.penetrationDepth = -event.penetrationDepth ;
193+ callback2->onCollisionEnter (reverseEvent);
194+ }
195+ }
49196 });
50197}
0 commit comments