--- /dev/null
+#include <config.h>
+
+#include <iostream>
+#include "collision_grid.h"
+#include "special/collision.h"
+#include "sector.h"
+
+static const float DELTA = .001;
+
+CollisionGrid::CollisionGrid(float newwidth, float newheight)
+ : width(newwidth), height(newheight), cell_width(128), cell_height(128)
+{
+ cells_x = size_t(width / cell_width) + 1;
+ cells_y = size_t(height / cell_height) + 1;
+ grid.resize(cells_x * cells_y);
+}
+
+CollisionGrid::~CollisionGrid()
+{
+ for(GridEntries::iterator i = grid.begin(); i != grid.end(); ++i) {
+ GridEntry* entry = *i;
+ while(entry) {
+ GridEntry* nextentry = entry->next;
+ delete entry;
+ entry = nextentry;
+ }
+ }
+}
+
+void
+CollisionGrid::add_object(MovingObject* object)
+{
+#ifdef DEBUG
+ // make sure the object isn't already in the grid
+ for(Objects::iterator i = objects.begin(); i != objects.end(); ++i) {
+ ObjectWrapper* wrapper = *i;
+ if(wrapper->object == object)
+ assert(false);
+ }
+ assert(object != 0);
+#endif
+
+ ObjectWrapper* wrapper = new ObjectWrapper;
+ wrapper->object = object;
+ wrapper->timestamp = 0;
+ wrapper->dest = object->bbox;
+ objects.push_back(wrapper);
+ wrapper->id = objects.size()-1;
+
+ const Rectangle& bbox = object->bbox;
+ for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
+ for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
+ int gridx = int(x / cell_width);
+ int gridy = int(y / cell_height);
+ if(gridx < 0 || gridy < 0
+ || gridx >= int(cells_x) || gridy >= int(cells_y)) {
+ std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n";
+ continue;
+ }
+ GridEntry* entry = new GridEntry;
+ entry->object_wrapper = wrapper;
+ entry->next = grid[gridy*cells_x + gridx];
+ grid[gridy*cells_x + gridx] = entry;
+ }
+ }
+}
+
+void
+CollisionGrid::remove_object(MovingObject* object)
+{
+ ObjectWrapper* wrapper = 0;
+ for(Objects::iterator i = objects.begin(); i != objects.end(); ++i) {
+ if((*i)->object == object) {
+ wrapper = *i;
+ objects.erase(i);
+ break;
+ }
+ }
+ assert(wrapper != 0);
+
+ const Rectangle& bbox = wrapper->dest;
+ for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
+ for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
+ int gridx = int(x / cell_width);
+ int gridy = int(y / cell_height);
+ if(gridx < 0 || gridy < 0
+ || gridx >= int(cells_x) || gridy >= int(cells_y)) {
+ std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n";
+ continue;
+ }
+ remove_object_from_gridcell(gridy*cells_x + gridx, object);
+ }
+ }
+
+ delete wrapper;
+}
+
+void
+CollisionGrid::move_object(MovingObject* object)
+{
+ const Rectangle& bbox = object->bbox;
+ for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
+ for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
+ int gridx = int(x / cell_width);
+ int gridy = int(y / cell_height);
+ if(gridx < 0 || gridy < 0
+ || gridx >= int(cells_x) || gridy >= int(cells_y)) {
+ std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n";
+ continue;
+ }
+ // TODO
+ }
+ }
+}
+
+void
+CollisionGrid::check_collisions()
+{
+ for(Objects::iterator i = objects.begin(); i != objects.end(); ++i) {
+ ObjectWrapper* wrapper = *i;
+ MovingObject* object = wrapper->object;
+ if(!object->is_valid())
+ continue;
+ if(object->get_flags() & GameObject::FLAG_NO_COLLDET) {
+ object->bbox.move(object->movement);
+ object->movement = Vector(0, 0);
+ continue;
+ }
+
+ // hack for now...
+ Sector::current()->collision_tilemap(object, 0);
+
+ collide_object(wrapper);
+
+ object->bbox.move(object->get_movement());
+ object->movement = Vector(0, 0);
+ }
+}
+
+void
+CollisionGrid::collide_object(ObjectWrapper* wrapper)
+{
+ static int timestamp = 0;
+ timestamp++;
+
+ const Rectangle& bbox = wrapper->object->bbox;
+ for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
+ for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
+ int gridx = int(x / cell_width);
+ int gridy = int(y / cell_height);
+ if(gridx < 0 || gridy < 0
+ || gridx >= int(cells_x) || gridy >= int(cells_y)) {
+ std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n";
+ continue;
+ }
+
+ for(GridEntry* entry = grid[gridy*cells_x + gridx]; entry;
+ entry = entry->next) {
+ ObjectWrapper* wrapper2 = entry->object_wrapper;
+ // only check each object once (even if it is in multiple cells)
+ if(wrapper2->timestamp == timestamp)
+ continue;
+ // don't collide with objects we already collided with
+ if(wrapper2->id <= wrapper->id)
+ continue;
+
+ wrapper->timestamp = timestamp;
+ collide_object_object(wrapper, wrapper2);
+ }
+ }
+ }
+}
+
+void
+CollisionGrid::collide_object_object(ObjectWrapper* wrapper,
+ ObjectWrapper* wrapper2)
+{
+ CollisionHit hit;
+ MovingObject* object1 = wrapper->object;
+ MovingObject* object2 = wrapper2->object;
+
+ Rectangle dest1 = object1->get_bbox();
+ dest1.move(object1->get_movement());
+ Rectangle dest2 = object2->get_bbox();
+ dest2.move(object2->get_movement());
+
+ Vector movement = object1->get_movement() - object2->get_movement();
+ if(Collision::rectangle_rectangle(hit, dest1, movement, dest2)) {
+ HitResponse response1 = object1->collision(*object2, hit);
+ hit.normal *= -1;
+ HitResponse response2 = object2->collision(*object1, hit);
+
+ if(response1 != CONTINUE) {
+ if(response1 == ABORT_MOVE)
+ object1->movement = Vector(0, 0);
+ if(response2 == CONTINUE)
+ object2->movement += hit.normal * (hit.depth + DELTA);
+ } else if(response2 != CONTINUE) {
+ if(response2 == ABORT_MOVE)
+ object2->movement = Vector(0, 0);
+ if(response1 == CONTINUE)
+ object1->movement += -hit.normal * (hit.depth + DELTA);
+ } else {
+ object1->movement += -hit.normal * (hit.depth/2 + DELTA);
+ object2->movement += hit.normal * (hit.depth/2 + DELTA);
+ }
+ }
+}
+
+void
+CollisionGrid::remove_object_from_gridcell(int gridcell, MovingObject* object)
+{
+ GridEntry* lastentry = 0;
+ GridEntry* entry = grid[gridcell];
+
+ while(entry) {
+ if(entry->object_wrapper->object == object) {
+ if(lastentry == 0) {
+ grid[gridcell] = entry->next;
+ } else {
+ lastentry->next = entry->next;
+ }
+ delete entry;
+ return;
+ }
+
+ lastentry = entry;
+ entry = entry->next;
+ };
+
+ std::cerr << "Couldn't find object in cell.\n";
+}
+
--- /dev/null
+#ifndef __COLLISION_GRID_H__
+#define __COLLISION_GRID_H__
+
+#include <vector>
+#include "special/moving_object.h"
+
+using namespace SuperTux;
+
+/**
+ * A rectangular grid to keep track of all moving game objects. It allows fast
+ * queries for all objects in a rectangular area.
+ */
+class CollisionGrid
+{
+public:
+ CollisionGrid(float width, float height);
+ ~CollisionGrid();
+
+ void add_object(MovingObject* object);
+ void remove_object(MovingObject* object);
+ void move_object(MovingObject* object);
+
+ void check_collisions();
+
+private:
+ struct ObjectWrapper
+ {
+ MovingObject* object;
+ Rectangle dest;
+ /** (pseudo) timestamp. When reading from the grid the timestamp is
+ * changed so that you can easily avoid reading an object multiple times
+ * when it is in several cells that you check.
+ */
+ int timestamp;
+ /// index in the objects vector
+ int id;
+ };
+
+ /** Element for the single linked list in each grid cell */
+ struct GridEntry
+ {
+ GridEntry* next;
+ ObjectWrapper* object_wrapper;
+ };
+
+ void remove_object_from_gridcell(int gridcell, MovingObject* object);
+ void collide_object(ObjectWrapper* wrapper);
+ void collide_object_object(ObjectWrapper* wrapper, ObjectWrapper* wrapper2);
+
+ typedef std::vector<GridEntry*> GridEntries;
+ GridEntries grid;
+ typedef std::vector<ObjectWrapper*> Objects;
+ Objects objects;
+ size_t cells_x, cells_y;
+ float width;
+ float height;
+ float cell_width;
+ float cell_height;
+};
+
+extern CollisionGrid* bla;
+
+#endif
+
#include "gameloop.h"
#include "resources.h"
#include "statistics.h"
+#include "collision_grid.h"
#include "special/collision.h"
#include "math/rectangle.h"
#include "math/aatriangle.h"
song_title = "Mortimers_chipdisko.mod";
player = new Player();
add_object(player);
+
+ grid = new CollisionGrid(32000, 32000);
}
Sector::~Sector()
update_game_objects();
assert(gameobjects_new.size() == 0);
+ delete grid;
+
for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
++i) {
delete *i;
/** cleanup marked objects */
for(std::vector<GameObject*>::iterator i = gameobjects.begin();
i != gameobjects.end(); /* nothing */) {
- if((*i)->is_valid() == false) {
- Bullet* bullet = dynamic_cast<Bullet*> (*i);
- if(bullet) {
- bullets.erase(
- std::remove(bullets.begin(), bullets.end(), bullet),
- bullets.end());
- }
- delete *i;
- i = gameobjects.erase(i);
- } else {
+ GameObject* object = *i;
+
+ if(object->is_valid()) {
++i;
+ continue;
+ }
+
+ Bullet* bullet = dynamic_cast<Bullet*> (object);
+ if(bullet) {
+ bullets.erase(
+ std::remove(bullets.begin(), bullets.end(), bullet),
+ bullets.end());
+ }
+ MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
+ if(movingobject) {
+ grid->remove_object(movingobject);
}
+ delete *i;
+ i = gameobjects.erase(i);
}
/* add newly created objects */
for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
i != gameobjects_new.end(); ++i)
{
- Bullet* bullet = dynamic_cast<Bullet*> (*i);
+ GameObject* object = *i;
+
+ Bullet* bullet = dynamic_cast<Bullet*> (object);
if(bullet)
bullets.push_back(bullet);
- TileMap* tilemap = dynamic_cast<TileMap*> (*i);
+ MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
+ if(movingobject)
+ grid->add_object(movingobject);
+
+ TileMap* tilemap = dynamic_cast<TileMap*> (object);
if(tilemap && tilemap->is_solid()) {
if(solids == 0) {
solids = tilemap;
}
}
- Camera* camera = dynamic_cast<Camera*> (*i);
+ Camera* camera = dynamic_cast<Camera*> (object);
if(camera) {
if(this->camera != 0) {
std::cerr << "Warning: Multiple cameras added. Ignoring.";
this->camera = camera;
}
- gameobjects.push_back(*i);
+ gameobjects.push_back(object);
}
gameobjects_new.clear();
}
void
Sector::collision_handler()
{
+#if 0
+ grid->check_collisions();
+#else
for(std::vector<GameObject*>::iterator i = gameobjects.begin();
i != gameobjects.end(); ++i) {
GameObject* gameobject = *i;
movingobject->bbox.move(movingobject->get_movement());
movingobject->movement = Vector(0, 0);
}
+#endif
}
bool