tile2d
Loading...
Searching...
No Matches
collide.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "tileMap.hpp"
4
11T2D_NAMESPACE_BEGIN
12
22
24 normal = other.normal;
25 depth = other.depth;
26
27 count = other.count;
28 for (size_t i = 0; i < other.count; i++) {
29 points[i] = other.points[i];
30 }
31
32 staticFriction = other.staticFriction;
33 dynamicFriction = other.dynamicFriction;
34 restitution = other.restitution;
35
36 }
37
38 vec2 normal = { Float(0.0), Float(0.0) };
39 Float depth = std::numeric_limits<Float>::max();
40
41 size_t count = 0;
42 std::array<vec2, 2> points;
43
44 Float staticFriction = Float(1.0);
45 Float dynamicFriction = Float(1.0);
46 Float restitution = Float(0.0);
47
53 bool isValid() const {
54 return !isNaN(normal) &&
55 !isNaN(depth) &&
56 !isNaN(count) &&
57 count <= 2 &&
58 !isNaN(staticFriction) &&
59 !isNaN(dynamicFriction) &&
60 !isNaN(restitution);
61 }
62};
63
64namespace debug {
65 struct CollideInfo {
66 std::vector<CollisionManifold> manifolds;
67 std::vector<std::array<vec2, 4>> tileAABBs;
68 std::vector<std::array<vec2, 4>> chunkAABBs;
69 };
70
71 std::vector<CollideInfo> collideInfos;
72}
73
88template<class BoxVertexContainer, class NormalContainer>
89bool satBoxTest(const BoxVertexContainer& vertices1, const BoxVertexContainer& vertices2, const NormalContainer& normals, CollisionManifold& manifold) {
90 assert(normals.size() == 2);
91 assert(vertices1.size() == 4);
92 assert(vertices2.size() == 4);
93
94 for (size_t i = 0; i < normals.size(); i++) {
95
96 const vec2& normal = normals[i];
97
98 MinMax proj1 = projectBoxOnNormal(vertices1, normal);
99 MinMax proj2 = projectBoxOnNormal(vertices2, normal);
100
101 if (!(proj1.second >= proj2.first && proj2.second >= proj1.first)) {
102 // they are not colliding
103 return false;
104 }
105 else {
106 Float newDepth = std::max(Float(0.0), std::min(proj1.second, proj2.second) - std::max(proj1.first, proj2.first));
107 if (newDepth <= manifold.depth) {
108 manifold.normal = normal;
109 manifold.depth = newDepth;
110
111 // we check to see if this value is on the left or right side of proj1
112 Float direction = proj1.second - proj2.second;
113 if (direction > 0) {
114 manifold.normal *= -1.0f;
115 }
116 }
117 }
118
119 }
120
121 return true;
122};
123
124
138template<typename Container, typename Integer>
139inline void findClosestPoint(vec2& cp1, vec2& cp2, Integer& count, Float& distance1,
140 const Container& points1, const Container& points2) {
141 for (Integer i = 0; i < points1.size(); i++) {
142 vec2 p = points1[i];
143
144 for (Integer j = 0; j < points2.size(); j++) {
145 vec2 cur = points2[j];
146 vec2 next = points2[(j + 1) % points2.size()];
147
148 vec2 cp;
149 Float distance = pointSegmentDistance(p, cur, next, cp);
150
151 if (nearlyEqual(distance, distance1, 0.1f)) {
152 if (!nearlyEqual(cp, cp1, 0.005f)) {
153 count = 2;
154 cp2 = cp;
155 }
156 }
157 else if (distance <= distance1) {
158 count = 1;
159 cp1 = cp;
160 distance1 = distance;
161 }
162 }
163 }
164}
165
176template<typename Container>
177inline void computeBoxManifold(const Container& points1, const Container& points2, CollisionManifold& manifold) {
178 std::array<vec2, 2> edge1;
179 std::array<vec2, 2> edge2;
180
181 // Face 1, finding the edge, whom's normals are most parallel to the manifold normal
182 Float max = -std::numeric_limits<Float>::max();
183 for (size_t i = 0; i < 4; i++) {
184 const vec2 p1 = points1[i];
185 const vec2 p2 = points1[(i + 1) % 4];
186 const vec2 edge = p2 - p1;
187 const vec2 normal = { -edge.y, edge.x };
188
189 const Float dot = glm::dot(normal, -manifold.normal);
190 if (dot > max) {
191 max = dot;
192 edge1[0] = p1;
193 edge1[1] = p2;
194 }
195 }
196
197 // Face 2, finding the edge, whom's normals are most parallel but against the manifold normal
198 max = -std::numeric_limits<Float>::max();
199 for (size_t i = 0; i < 4; i++) {
200 const vec2 p1 = points2[i];
201 const vec2 p2 = points2[(i + 1) % 4];
202 const vec2 edge = p2 - p1;
203 const vec2 normal = { -edge.y, edge.x };
204
205 const Float dot = glm::dot(normal, manifold.normal);
206 if (dot > max) {
207 max = dot;
208 edge2[0] = p1;
209 edge2[1] = p2;
210 }
211 }
212
213 Float minDist = std::numeric_limits<Float>::max();
214 findClosestPoint(manifold.points[0], manifold.points[1], manifold.count, minDist, edge1, edge2);
215
216 assert(manifold.count >= 1);
217}
218
235template<class TileData>
237 const TileBody<TileData>& body1,
238 const glm::i32vec2& tilePos1,
239 const vec2& body1Offset,
240 const TileBody<TileData>& body2,
241 const glm::i32vec2& tilePos2,
242 const vec2& body2Offset,
243 CollisionManifold& manifold) {
244 std::array<vec2, 4> tileObb1 = body1.getTileWorldOBBOffsetVertices(tilePos1, body1Offset, tileObb1);
245 std::array<vec2, 4> tileObb2 = body2.getTileWorldOBBOffsetVertices(tilePos2, body2Offset, tileObb2);
246
247 {
248 auto normals1 = body1.normals();
249 if (!satBoxTest(tileObb1, tileObb2, normals1, manifold))
250 return false;
251
252 auto normals2 = body2.normals();
253 if (!satBoxTest(tileObb1, tileObb2, normals2, manifold))
254 return false;
255 }
256
257 if (manifold.depth == 0.0f)
258 return false;
259
260 computeBoxManifold(tileObb1, tileObb2, manifold);
261
262 assert(manifold.isValid());
263
264 const PhysicsTileProperties& tile1 = body1.tileMap().getPhysicsTileProperties(tilePos1);
265 const PhysicsTileProperties& tile2 = body2.tileMap().getPhysicsTileProperties(tilePos2);
266 manifold.dynamicFriction = convertToFloat(tile1.dynamicFriction) * convertToFloat(tile2.dynamicFriction);
267 manifold.staticFriction = convertToFloat(tile1.staticFriction) * convertToFloat(tile2.staticFriction);
268 manifold.restitution = convertToFloat(std::max(tile1.restitution, tile2.restitution));
269
270 return true;
271}
272
277template<class TileData>
279 using SpatialIndex = typename ::t2d::TileBody<TileData>::SpatialIndex;
280
281 std::vector<SpatialIndex> results1;
282 std::vector<SpatialIndex> results2;
283 std::vector<std::pair<AABB<Float>, AABB<Float>>> approxAreas;
284};
285
298template<class TileData>
300 const TileBody<TileData>& body1,
301 const TileBody<TileData>& body2,
303 std::vector<CollisionManifold>& manifolds,
304 vec2& body1Offset,
305 vec2& body2Offset) {
306 AABB<Float> approxAreaOfBody1Local = computeAABBCollisionArea(body1.getLocalAABB(), body2.getAABB(body1.transform(), body1.com()));
307 if (std::isnan(approxAreaOfBody1Local.min().x))
308 return;
309
310 AABB<Float> approxAreaOfBody2Local = computeAABBCollisionArea(body2.getLocalAABB(), body1.getAABB(body2.transform(), body2.com()));
311 if (std::isnan(approxAreaOfBody2Local.min().x))
312 return;
313
314 cache.results1.clear();
315 cache.results2.clear();
316 cache.approxAreas.clear();
317
318 body1.queryChunks(approxAreaOfBody1Local, std::back_inserter(cache.results1));
319 body2.queryChunks(approxAreaOfBody2Local, std::back_inserter(cache.results2));
320
321#ifndef NDEBUG
322 debug::CollideInfo collideInfo;
323 for (auto& r1 : cache.results1)
324 collideInfo.chunkAABBs.push_back(body1.getLocalAABBWorldVertices(r1.aabb));
325
326 for (auto& r2 : cache.results2)
327 collideInfo.chunkAABBs.push_back(body2.getLocalAABBWorldVertices(r2.aabb));
328#endif
329
330 for (auto& chunk1 : cache.results1) {
331 for (auto& chunk2 : cache.results2) {
332 AABB<Float> approxAreaOfChunk1Local = computeAABBCollisionArea(chunk1.aabb, body2.getAABB(chunk2.aabb, body1.transform(), body1.com()));
333 if (std::isnan(approxAreaOfChunk1Local.min().x))
334 continue;
335
336 AABB<Float> approxAreaOfChunk2Local = computeAABBCollisionArea(chunk2.aabb, body1.getAABB(chunk1.aabb, body2.transform(), body2.com()));
337 if (std::isnan(approxAreaOfChunk2Local.min().x))
338 continue;
339
340 cache.approxAreas.push_back(std::pair(approxAreaOfChunk1Local, approxAreaOfChunk2Local));
341 }
342 }
343
344 for (const auto& approxArea : cache.approxAreas) {
345 cache.results1.clear();
346 cache.results2.clear();
347
348 body1.queryTiles(approxArea.first, std::back_inserter(cache.results1));
349 body2.queryTiles(approxArea.second, std::back_inserter(cache.results2));
350
351#ifndef NDEBUG
352 for (auto& r1 : cache.results1)
353 collideInfo.tileAABBs.push_back(body1.getLocalAABBWorldVertices(body1.getTileLocalAABB(r1.pos)));
354
355 for (auto& r2 : cache.results2)
356 collideInfo.tileAABBs.push_back(body2.getLocalAABBWorldVertices(body2.getTileLocalAABB(r2.pos)));
357#endif
358
359
360 for (auto& tile1 : cache.results1) {
361 for (auto& tile2 : cache.results2) {
362 CollisionManifold manifold;
363
364 if (detectNarrowCollision(body1, tile1.pos, body1Offset, body2, tile2.pos, body2Offset, manifold)) {
365 Float body1Mass = body1.mass() * (Float)!body1.isStatic();
366 Float body2Mass = body2.mass() * (Float)!body2.isStatic();
367 Float totalMass = body1Mass + body2Mass;
368 Float body1Depth = (body1Mass / totalMass) * manifold.depth;
369 Float body2Depth = (body2Mass / totalMass) * manifold.depth;
370
371 body1Offset -= manifold.normal * body1Depth * Float(0.5);
372 body2Offset += manifold.normal * body2Depth * Float(0.5);
373
374 manifolds.emplace_back(manifold);
375#ifndef NDEBUG
376 collideInfo.manifolds.emplace_back(manifold);
377#endif
378 }
379 }
380 }
381 }
382
383#ifndef NDEBUG
384 debug::collideInfos.emplace_back(std::move(collideInfo));
385#endif
386}
387
398 struct Impulse {
399 vec2 r1 = {Float(0.0), Float(0.0)};
400 vec2 r2 = {Float(0.0), Float(0.0)};
401 Float j = 0.0f;
402 vec2 friction_impulse = { 0.0f, 0.0f };
403 };
404
405 void operator()(Body& body1, Body& body2, const CollisionManifold& manifold) {
406 Impulse impulse;
407 vec2 average = manifold.points[0];
408
409 if (manifold.count == 2) {
410 average += manifold.points[1];
411 average /= 2.0f;
412 }
413
414 { // normal calculations
415 impulse.r1 = body1.getWorldPos() - average;
416 impulse.r2 = body2.getWorldPos() - average;
417
418 vec2 r1_perp = { impulse.r1.y, -impulse.r1.x };
419 vec2 r2_perp = { impulse.r2.y, -impulse.r2.x };
420
421 vec2 angular_linear1 = r1_perp * body1.m_angularVelocity;
422 vec2 angular_linear2 = r2_perp * body2.m_angularVelocity;
423
424 vec2 rel_vel =
425 (body2.m_linearVelocity + angular_linear2) -
426 (body1.m_linearVelocity + angular_linear1);
427
428 Float rel_vel_dot_n = glm::dot(rel_vel, manifold.normal);
429 if (rel_vel_dot_n > 0.0f)
430 return;
431
432 Float r1_perp_dot_n = glm::dot(r1_perp, manifold.normal);
433 Float r2_perp_dot_n = glm::dot(r2_perp, manifold.normal);
434
435 Float denom =
436 body1.m_inverseMass + body2.m_inverseMass +
437 (sqaure(r1_perp_dot_n) * body1.m_inverseI) +
438 (sqaure(r2_perp_dot_n) * body2.m_inverseI);
439
440 impulse.j = -(1.0f + manifold.restitution) * rel_vel_dot_n;
441 impulse.j /= denom;
442 impulse.j /= (Float)manifold.count;
443
444 // apply impulse
445 vec2 impulsej = impulse.j * manifold.normal;
446
447 body1.m_linearVelocity -= impulsej * body1.m_inverseMass;
448 body1.m_angularVelocity -= cross(impulsej, impulse.r1) * body1.m_inverseI;
449 body2.m_linearVelocity += impulsej * body2.m_inverseMass;
450 body2.m_angularVelocity += cross(impulsej, impulse.r2) * body2.m_inverseI;
451 }
452
453 { // tangent calculations
454 vec2 r1_perp = { impulse.r1.y, -impulse.r1.x };
455 vec2 r2_perp = { impulse.r2.y, -impulse.r2.x };
456
457 vec2 angular_linear1 = r1_perp * body1.m_angularVelocity;
458 vec2 angular_linear2 = r2_perp * body2.m_angularVelocity;
459
460 vec2 rel_vel =
461 (body2.m_linearVelocity + angular_linear2) - (body1.m_linearVelocity + angular_linear1);
462
463 vec2 tangent = rel_vel - glm::dot(rel_vel, manifold.normal) * manifold.normal;
464 if (nearlyEqual(tangent, { 0.0f, 0.0f }))
465 return;
466 else
467 tangent = glm::normalize(tangent);
468
469 Float r1_perp_dot_t = glm::dot(r1_perp, tangent);
470 Float r2_perp_dot_t = glm::dot(r2_perp, tangent);
471
472 Float denom =
473 body1.m_inverseMass + body2.m_inverseMass +
474 (sqaure(r1_perp_dot_t) * body1.m_inverseI) +
475 (sqaure(r2_perp_dot_t) * body2.m_inverseI);
476
477 Float jt = -glm::dot(rel_vel, tangent);
478 jt /= denom;
479 jt /= manifold.count;
480
481 if (glm::abs(jt) <= impulse.j * manifold.staticFriction) {
482 impulse.friction_impulse = jt * tangent;
483 }
484 else {
485 impulse.friction_impulse = -impulse.j * tangent * manifold.dynamicFriction;
486 }
487
488 body1.m_linearVelocity -= impulse.friction_impulse * body1.m_inverseMass;
489 body1.m_angularVelocity -= cross(impulse.friction_impulse, impulse.r1) * body1.m_inverseI;
490
491 body2.m_linearVelocity += impulse.friction_impulse * body2.m_inverseMass;
492 body2.m_angularVelocity += cross(impulse.friction_impulse, impulse.r2) * body2.m_inverseI;
493 }
494 }
495};
496
497T2D_NAMESPACE_END
Extends the functionality of WorldBody to allow for things such as angular velocity.
Definition body.hpp:238
const vec2 & com() const
Returns the center of mass of this body.
Definition body.hpp:321
virtual vec2 getWorldPos() override
Returns the world position of this body.
Definition body.hpp:286
For use with a TileMap<>, allows collisions with other rigid bodies inside World<>
Definition tileMap.hpp:271
TileMap< TileType > & tileMap() const
Returns the tile map attached to this rigid body.
Definition tileMap.hpp:322
void queryChunks(const AABB< Float > &intersectBox, OutIt chunks) const
Will get the list of chunks that intersect intersectBox, along with their AABBS.
Definition tileMap.hpp:450
virtual AABB< Float > getAABB() const override
Returns the AABB of this body.
Definition tileMap.hpp:313
std::array< vec2, 2 > normals() const
Returns the normals of this body. Can be used for all tiles of this body.
Definition tileMap.hpp:331
void queryTiles(const AABB< Float > &intersectBox, OutIt tiles) const
Will get the list of tiles that intersect intersectBox.
Definition tileMap.hpp:478
std::array< vec2, 4 > getTileWorldOBBOffsetVertices(const glm::i32vec2 &iPos, const vec2 &worldOffset) const
Returns the world vertices of the tile located at iPos. with an added offset.
Definition tileMap.hpp:399
AABB< Float > getTileLocalAABB(const glm::i32vec2 &iPos) const
Returns the local AABB of a tile located at iPos.
Definition tileMap.hpp:434
Transform & transform()
Returns the REFERENCE transform of this body.
Definition body.hpp:64
bool isStatic() const
Returns if the body is static, meaning it cannot be moved by forces or other bodies.
Definition body.hpp:135
Float mass() const
Returns the mass of this body.
Definition body.hpp:189
bool satBoxTest(const BoxVertexContainer &vertices1, const BoxVertexContainer &vertices2, const NormalContainer &normals, CollisionManifold &manifold)
Returns the normal and depth of two polygons defined by the vertices vertices1 and vertices2 by using...
Definition collide.hpp:89
bool detectNarrowCollision(const TileBody< TileData > &body1, const glm::i32vec2 &tilePos1, const vec2 &body1Offset, const TileBody< TileData > &body2, const glm::i32vec2 &tilePos2, const vec2 &body2Offset, CollisionManifold &manifold)
Detects the collision between the tile in tilePos1 of body1 and the tile tilePos2 of body2.
Definition collide.hpp:236
void findClosestPoint(vec2 &cp1, vec2 &cp2, Integer &count, Float &distance1, const Container &points1, const Container &points2)
Finds the closest point between points1 and the faces defined by points2.
Definition collide.hpp:139
void computeBoxManifold(const Container &points1, const Container &points2, CollisionManifold &manifold)
Finds the closest point between points1 and the faces defined by points2.
Definition collide.hpp:177
void detectTileBodyCollision(const TileBody< TileData > &body1, const TileBody< TileData > &body2, TileBodyCollisionCache< TileData > &cache, std::vector< CollisionManifold > &manifolds, vec2 &body1Offset, vec2 &body2Offset)
Detects the collision between the tiles bodies, body1 and body2.
Definition collide.hpp:299
Definition math.hpp:203
Defines the relevant collision information generated by detectNarrowColllision() to both resolve the ...
Definition collide.hpp:20
bool isValid() const
If any members are NaN, normal is not normalized, or the contact count is more then 2,...
Definition collide.hpp:53
Definition collide.hpp:398
Resolves the collision between two bodies using the impulse method.
Definition collide.hpp:397
Contains the physical properties of a tile, such as elasticity.
Definition tileMap.hpp:45
Used to speed up the collision detection between two tile bodies.
Definition collide.hpp:278
Definition collide.hpp:65
Defines TileMap<> and TileBody<>