tile2d
Loading...
Searching...
No Matches
world.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "collide.hpp"
4#include "spatial.hpp"
5
12T2D_NAMESPACE_BEGIN
13
14template<typename Int>
16 static constexpr Int invalid = std::numeric_limits<Int>::max();
17public:
19 : prev(invalid), next(invalid) {}
20
21 virtual ~LinkedListElement() = default;
22
23 Int prev;
24 Int next;
25};
26
27template<typename T, typename Access, typename Int>
29 static constexpr Int invalid = std::numeric_limits<Int>::max();
30public:
31 LinkedListHeader(Access& access)
32 : first(invalid), last(invalid), m_access(access), m_size(0) {}
33
34 bool is_empty() { return first == invalid; }
35
36 void push_front(Int element);
37 void push_back(Int element);
38 void remove_element(Int element);
39 void pop_front();
40 void pop_back();
41
42 size_t size() const {
43 return m_size;
44 }
45
46public:
47 struct Iterator {
48 private:
49 Access& m_access;
50 Int m_idx;
51 public:
52 Iterator(Access& access, Int it)
53 : m_access(access), m_idx(it) {}
54
55 T& operator*() {
56 return m_access[m_idx];
57 }
58
59 T* operator->() {
60 return &m_access[m_idx];
61 }
62
63 Iterator& operator++() {
64 m_idx = m_access[m_idx].next;
65 return *this;
66 }
67
68 Iterator& operator--() {
69 m_idx = m_access[m_idx].prev;
70 return *this;
71 }
72
73 bool operator!=(const Iterator& other) const {
74 return m_idx != other.m_idx;
75 }
76 };
77
78 Iterator begin() { return Iterator(m_access, first); }
79 Iterator end() { return Iterator(m_access, invalid); }
80
81protected:
82 void assign_first(Int element);
83
84 size_t m_size;
85 Int first = invalid;
86 Int last = invalid;
87 Access& m_access;
88};
89
90template<typename T, typename Access, typename Int>
92 first = element;
93 last = element;
94}
95
96template<typename T, typename Access, typename Int>
98 LinkedListElement<Int>* list_element = dynamic_cast<LinkedListElement<Int>*>(&m_access[element]);
99
100 if (is_empty()) {
101 assign_first(element);
102 }
103 else {
104 m_access[first].prev = element;
105 list_element->next = first;
106 first = element;
107 }
108
109 m_size++;
110}
111
112template<typename T, typename Access, typename Int>
114 LinkedListElement<Int>* list_element = dynamic_cast<LinkedListElement<Int>*>(&m_access[element]);
115
116 if (is_empty()) {
117 assign_first(element);
118 }
119 else {
120 m_access[last].next = element;
121 list_element->prev = last;
122 last = element;
123 }
124
125 m_size++;
126}
127
128template<typename T, typename Access, typename Int>
130 LinkedListElement<Int>* list_element = dynamic_cast<LinkedListElement<Int>*>(&m_access[element]);
131
132 if (list_element == first) {
133 first = list_element->next;
134 }
135 if (list_element == last) {
136 last = list_element->prev;
137 }
138
139 if (list_element->prev) {
140 m_access[list_element->prev].next = list_element->next;
141 }
142 if (list_element->next) {
143 m_access[list_element->next].prev = list_element->prev;
144 }
145
146 list_element->prev = invalid;
147 list_element->next = invalid;
148
149 m_size--;
150}
151
152template<typename T, typename Access, typename Int>
154 if (is_empty()) {
155 return;
156 }
157 else {
158 first = access[first].next;
159
160 access[first].prev = invalid;
161 access[first].next = invalid;
162 }
163
164 m_size--;
165}
166
167template<typename T, typename Access, typename Int>
169 if (is_empty()) {
170 return;
171 }
172 else {
173 last = access[last].prev;
174
175 access[last].prev = invalid;
176 access[last].next = invalid;
177 }
178
179 m_size--;
180}
181
187template<class TileData>
188class World {
189protected:
191 uint32_t id = 0;
192
193 bool operator==(const SpatialIndex& other) const {
194 return id == other.id;
195 }
196 };
197
198 struct Cache {
199 Cache() {}
200
201 std::vector<SpatialIndex> results;
202 ::t2d::TileBodyCollisionCache<TileData> detectionCache;
203
204 std::vector<std::pair<uint16_t, AABB<Float>>> shouldMove;
205 std::vector<std::pair<uint32_t, uint32_t>> couldCollide;
206 };
207
208 struct BodyElement : public LinkedListElement<uint32_t> {
209 uint16_t element = std::numeric_limits<uint16_t>::max();
210 WorldBody* body = nullptr;
211 };
212public:
215
217public:
223 World(size_t threadCount)
224 : m_bodyList(m_bodies), m_grid(tileWidth * 20), ioServiceWork(ioService) {
225 threadCount = std::min(threadCount, (size_t)boost::thread::hardware_concurrency());
226
227 std::pair<boost::thread::id, Cache> pair;
228 pair.first = boost::this_thread::get_id();
229 m_threadCache.insert(pair);
230
231 for (size_t i = 0; i < threadCount; i++) {
232 boost::thread* thread = threadPool.create_thread(boost::bind(&boost::asio::io_service::run, &ioService));
233
234 std::pair<boost::thread::id, Cache> pair;
235 pair.first = thread->get_id();
236 m_threadCache.insert(pair);
237 }
238 }
239
240 ~World() {
241 ioService.stop();
242 threadPool.join_all();
243 }
244
249 uint32_t newId = m_bodies.insert();
250
251 BodyElement& bodyElement = m_bodies[newId];
252 bodyElement.body = m_tileBodyPool.template construct<TileMapT&, uint32_t>(tileMap, newId);
253 if (!bodyElement.body)
254 return nullptr;
255
256 m_bodyList.push_back(bodyElement.body->m_id);
257
258 insertIntoTree(bodyElement.body);
259
260 return bodyElement.body;
261 }
262
271 WorldBody* createBulletBody(Float radius, const vec2& initialLinearVelocity) {
272 return nullptr; // to be implemented
273
274 uint32_t newId = m_bodies.insert();
275
276 BodyElement& bodyElement = m_bodies[newId];
277 bodyElement.body = m_bulletBodyPool.template construct<uint32_t, Float>(newId, radius);
278 if (!bodyElement.body)
279 return nullptr;
280
281 m_bodyList.push_back(bodyElement.body->m_id);
282
283 bodyElement.body->addLinearVelocity(initialLinearVelocity);
284
285 insertIntoTree(bodyElement.body);
286
287 return bodyElement.body;
288 }
289
295 void destroyBody(WorldBody* body) {
296 switch (body->m_bodyType) {
297 case BodyType::Tile:
298 m_tileBodyPool.destroy(dynamic_cast<TileBody<TileData>*>(body));
299 break;
300
301 case BodyType::Bullet:
302 m_bulletBodyPool.destroy(dynamic_cast<BulletBody*>(body));
303 break;
304
305 default:
306 assert(false);
307 }
308
309 eraseFromTree(body);
310 m_bodyList.remove_element(body->m_id);
311 m_bodies.erase(body->m_id);
312 }
313
320 void update(const Float dt, size_t steps) {
321 auto start = std::chrono::steady_clock::now();
322
323 Float timePerStep = dt / (Float)steps;
324
325 for (size_t i = 0; i < steps; i++) {
326 /* Update bodies and relocate into grid if necessary*/
327 executeOnBodies(timePerStep, &World::updateBodies);
328
329 m_grid.cleanup();
330
331 /* Perform grid queries */
332 executeOnBodies(timePerStep, &World::batchQuery);
333
334 std::vector<CollisionManifold> manifolds;
335 Cache& mainThreadCache = m_threadCache[boost::this_thread::get_id()];
336 for (const std::pair<uint32_t, uint32_t>& collidingPair : m_possibleCollisions) {
337 WorldBody* body = m_bodies[collidingPair.first].body;
338 WorldBody* otherBody = m_bodies[collidingPair.second].body;
339
340 vec2 body1Offset(0.0);
341 vec2 body2Offset(0.0);
342
343 manifolds.clear();
344
345 switch (body->m_bodyType) {
346 case BodyType::Tile: {
347 switch (otherBody->m_bodyType) {
348 case BodyType::Tile:
349 detectTileBodyCollision<TileData>(dynamic_cast<TileBodyT&>(*body), dynamic_cast<TileBodyT&>(*otherBody), mainThreadCache.detectionCache, manifolds, body1Offset, body2Offset);
350 break;
351
352 default:
353 assert(false);
354 break;
355 }
356 } break;
357
358 default:
359 assert(false);
360 }
361
362 for (CollisionManifold& manifold : manifolds) {
363 ImpulseMethod()(*(Body*)body, *(Body*)otherBody, manifold);
364 }
365
366 body->moveBy(body1Offset);
367 otherBody->moveBy(body2Offset);
368 }
369 m_possibleCollisions.clear();
370
371 }
372
373 auto end = std::chrono::steady_clock::now();
374
375 m_executionTimeAvgTotal += std::chrono::duration_cast<std::chrono::duration<Float, std::milli>>(end - start).count();
376 m_executionTimeAvgCount++;
377 }
378
385 Float avg = m_executionTimeAvgTotal / m_executionTimeAvgCount;
386
387 m_executionTimeAvgTotal = 0;
388 m_executionTimeAvgCount = 0;
389 return avg;
390 }
391
397 vec2 gravity() const {
398 return m_worldForces.gravity;
399 }
400
406 void setGravity(const vec2& gravity) {
407 m_worldForces.gravity = gravity;
408 }
409
416 return m_grid;
417 }
418
419protected:
420 template<class Func>
421 void executeOnBodies(Float timePerStep, Func&& func) {
422 jobsDone = 0;
423 size_t threadPoolSize = threadPool.size() + 1; // +1 includes main thread
424 size_t bodiesPerThread = m_bodyList.size() / threadPoolSize;
425 for (size_t i = 0; i < threadPoolSize; i++) {
426 size_t bodyStart = i * bodiesPerThread;
427 size_t bodyEnd = bodyStart + bodiesPerThread;
428 /* Once all threads have been assigned the task,
429 give the main thread the task as well */
430 if (i + 1 == threadPoolSize) {
431 bodyEnd = m_bodyList.size();
432 (this->*func)(timePerStep, bodyStart, bodyEnd);
433 } else {
434 ioService.post(boost::bind(func, this, timePerStep, bodyStart, bodyEnd));
435 }
436 }
437 while (jobsDone < threadPoolSize) {}
438 }
439
440 void updateBodies(Float timePerStep, size_t start, size_t end) {
441 Cache& cache = m_threadCache.find(boost::this_thread::get_id())->second;
442 cache.shouldMove.clear();
443
444 typename BodyList::Iterator i = m_bodyList.begin();
445 for (size_t index = 0; index < end; index++, ++i) {
446 if (index < start)
447 continue;
448
449 WorldBody* body = i->body;
450
451 body->addLinearVel(m_worldForces.gravity * timePerStep);
452 body->integrate(timePerStep);
453
454 if (body->m_flags[Body::NEEDS_REINSERT]) {
455 cache.shouldMove.emplace_back(std::pair<uint16_t, AABB<Float>>(i->element, body->getAABB()));
456 body->m_flags[Body::NEEDS_REINSERT] = false;
457 }
458 }
459
460 m_gridLock.lock();
461 for (auto& movePair : cache.shouldMove) {
462 m_grid.relocate(movePair.first, movePair.second);
463 }
464 m_gridLock.unlock();
465
466 jobsDone++;
467 }
468
469 void batchQuery(Float timePerStep, size_t start, size_t end) {
470 Cache& cache = m_threadCache.find(boost::this_thread::get_id())->second;
471 cache.couldCollide.clear();
472
473 typename BodyList::Iterator i = m_bodyList.begin();
474 for (size_t index = 0; index < end; index++, ++i) {
475 if (index < start)
476 continue;
477
478 WorldBody* body = i->body;
479 AABB<Float> bodyAABB = body->getAABB();
480 m_grid.queryIntersects(bodyAABB, [&](const SpatialIndex& spatialIndex) {
481 if (spatialIndex.id == body->id())
482 return;
483 if (body->isStatic() && m_bodies[spatialIndex.id].body->isStatic())
484 return;
485
486 cache.couldCollide.push_back(std::pair(body->id(), spatialIndex.id));
487 });
488 }
489
490 m_collisionListLock.lock();
491 m_possibleCollisions.insert(m_possibleCollisions.end(), cache.couldCollide.begin(), cache.couldCollide.end());
492 m_collisionListLock.unlock();
493
494 jobsDone++;
495 }
496
497 void insertIntoTree(WorldBody* body) {
498 BodyElement& bodyElement = m_bodies[body->id()];
499 bodyElement.element = m_grid.insert(body->getAABB(), SpatialIndex{ body->id() });
500 }
501
502 void eraseFromTree(WorldBody* body) {
503 BodyElement& bodyElement = m_bodies[body->id()];
504 m_grid.erase(bodyElement->element);
505 }
506
507private:
508 FlatMap<boost::thread::id, Cache> m_threadCache;
509
510 BodyList m_bodyList;
512 boost::object_pool<TileBodyT> m_tileBodyPool;
513 boost::object_pool<BulletBody> m_bulletBodyPool;
514
515 boost::mutex m_gridLock;
516 Grid<SpatialIndex> m_grid;
517 boost::mutex m_collisionListLock;
518 std::vector<std::pair<uint32_t, uint32_t>> m_possibleCollisions;
519
520 Float m_executionTimeAvgCount = 0;
521 Float m_executionTimeAvgTotal = 0;
522
523 boost::asio::io_service ioService;
524 boost::asio::io_service::work ioServiceWork;
525 boost::thread_group threadPool;
526 std::atomic<size_t> jobsDone;
527
528 struct {
529 vec2 gravity = { 0.0f, 0.0f };
530 } m_worldForces;
531};
532
533T2D_NAMESPACE_END
Extends the functionality of WorldBody to allow for things such as angular velocity.
Definition body.hpp:238
Definition body.hpp:357
Definition spatial.hpp:14
Used for spatial indexing.
Definition spatial.hpp:118
Definition world.hpp:28
For use with a TileMap<>, allows collisions with other rigid bodies inside World<>
Definition tileMap.hpp:271
Definition tileMap.hpp:88
The abstract base class of all Rigid Bodies that defines essential methods and members to be used in ...
Definition body.hpp:30
void addLinearVel(const vec2 &vel)
applies a linear velocity to this body
Definition body.hpp:180
virtual AABB< Float > getAABB() const =0
Returns the AABB of this body.
bool isStatic() const
Returns if the body is static, meaning it cannot be moved by forces or other bodies.
Definition body.hpp:135
virtual void integrate(Float dt)=0
Integerates this body, meaning the linear velocity and angular velocity will be integrated into the p...
void moveBy(const vec2 &amount)
Moves the body by amount.
Definition body.hpp:81
uint32_t id() const
Returns the id assigned to this body the physics world.
Definition body.hpp:48
Allows collisions between different bodies to occur.
Definition world.hpp:188
Grid< SpatialIndex > & grid()
Returns a grid refence, may be used for queries or insertions.
Definition world.hpp:415
void destroyBody(WorldBody *body)
Destroys a body, must be a type of a bullet or a tile body.
Definition world.hpp:295
WorldBody * createBulletBody(Float radius, const vec2 &initialLinearVelocity)
NOT YET IMPLEMENTED. Creates a bullet body.
Definition world.hpp:271
World(size_t threadCount)
Constructs a physics world.
Definition world.hpp:223
vec2 gravity() const
Returns the gravity of the world.
Definition world.hpp:397
void setGravity(const vec2 &gravity)
Set the gravity of the world.
Definition world.hpp:406
WorldBody * createTileBody(TileMapT &tileMap)
Do not use. See createTileMap()
Definition world.hpp:248
void update(const Float dt, size_t steps)
Updates the body by dt.
Definition world.hpp:320
Float getExecutionTimeAvg()
Returns the average speed of update. The stats will reset after call.
Definition world.hpp:384
Contains different types of collision detection and resolve functions.
Definition math.hpp:203
Defines the relevant collision information generated by detectNarrowColllision() to both resolve the ...
Definition collide.hpp:20
Resolves the collision between two bodies using the impulse method.
Definition collide.hpp:397
Definition world.hpp:15
Definition world.hpp:47
Definition world.hpp:208
Definition world.hpp:198
Definition world.hpp:190