-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCollider.cpp
99 lines (85 loc) · 2.79 KB
/
Collider.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include "pch.h"
#include "Collider.h"
#include "Renderables.h"
#include "Math.h"
std::pair<int, int> CollissionSolver::GetSectorIndices(Position pos) const
{
int x = std::clamp((int)((pos.x - bounds.min.x) / bounds.GetSize().x * divisions), 0, divisions - 1);
int y = std::clamp((int)((pos.y - bounds.min.y) / bounds.GetSize().y * divisions), 0, divisions - 1);
return { x, y };
}
CollissionSolver::Sector& CollissionSolver::GetSector(Position pos)
{
auto i = GetSectorIndices(pos);
return sectors[i.first + i.second * divisions];
}
std::basic_string<CollissionSolver::Sector*> CollissionSolver::GetSectors(Boundary bounds)
{
auto result = std::basic_string<CollissionSolver::Sector*>();
auto min = GetSectorIndices(bounds.min);
auto max = GetSectorIndices(bounds.max);
for(int i = min.first; i <= max.first; ++i)
for (int j = min.second; j <= max.second; ++j)
{
result.push_back(§ors[i + j * divisions]);
}
return result;
}
void CollissionSolver::Clear()
{
for( auto& sector : sectors)
{
sector.objects.clear();
}
}
void CollissionSolver::Populate(Entity& renderable)
{
if (!renderable.GetCollidable())
return;
for (Sector* sector : GetSectors(renderable.GetBoundary()))
{
sector->objects.push_back(&renderable);
}
}
void CollissionSolver::Solve()
{
// Use to filter out same collisions happening in multiple sectors. Pointer equality should suffice.
auto collisions = std::set<std::pair<Entity*, Entity*>>() ;
for (auto& sector : sectors)
{
// O(n*n) for objects inside. High enough `divisions` should limit them to minimum.
for(size_t i = 0; i < sector.objects.size(); ++i)
for (size_t j = i + 1; j < sector.objects.size(); ++j)
{
if (sector.objects[i]->GetBoundary().Overlaps(sector.objects[j]->GetBoundary()))
{
/* Houston, we have a collision! */
collisions.insert({ sector.objects[i], sector.objects[j] });
}
}
}
for (auto pair : collisions)
{
pair.first->Collide(*pair.second);
pair.second->Collide(*pair.first);
}
}
CollisionDir CollissionSolver::Direction(Boundary from, Boundary to)
{
// Compute direction of rectangular collision (spherical surely coming in v2.0 !)
// distance needed to touch
float width = 0.5f * (from.GetSize().x + to.GetSize().x);
float height = 0.5f * (from.GetSize().y + to.GetSize().y);
// actual distance
float dx = from.GetCentre().x - to.GetCentre().x;
float dy = from.GetCentre().y - to.GetCentre().y;
assert(abs(dx) <= width + dx * math::epsilon && abs(dy) <= height + dy * math::epsilon);
// Touching axis will have width == dx or height == dy. Non-touching axis has width > dx or height > dy
float wy = width * dy;
float hx = height * dx;
if (wy > hx) {
return (wy > -hx) ? CollisionDir::Top : CollisionDir::Right;
} else {
return (wy > -hx) ? CollisionDir::Left : CollisionDir::Bottom;
}
}