-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Start converting collision/physics handling into ECS
- Loading branch information
Showing
3 changed files
with
283 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
#pragma once | ||
|
||
#include <glm/vec2.hpp> | ||
|
||
|
||
class b2Body; | ||
namespace sp { | ||
class CollisionSystem; | ||
|
||
// Position component, to give an entity a position and rotation in the 3D world. | ||
class Position | ||
{ | ||
public: | ||
glm::vec2 getPosition() const { return position; } | ||
float getRotation() const { return rotation; } | ||
|
||
void setPosition(glm::vec2 v) { position = v; position_user_set = true; multiplayer_dirty = true; } | ||
void setRotation(float angle) { rotation = angle; rotation_user_set = true; multiplayer_dirty = true; } | ||
private: | ||
bool position_user_set = false; | ||
bool rotation_user_set = false; | ||
bool multiplayer_dirty = false; | ||
|
||
glm::vec2 position{}; | ||
float rotation = 0.0f; | ||
|
||
friend class sp::CollisionSystem; | ||
}; | ||
|
||
// The physics component will give the entity a physical presents in the physic simulation | ||
// this includes collision feedback. A physics component does nothing on it's own, it needs a Position component as well. | ||
// The position component will be updated by the physics system on each physics step. Updating the position component from another location will force the | ||
// physics to move the object to that position. | ||
class Physics | ||
{ | ||
public: | ||
enum class Type { | ||
Sensor, | ||
Dynamic, | ||
Static, | ||
}; | ||
Type getType() const { return type; } | ||
void setCircle(Type type, float radius) { if (type == this->type && shape == Shape::Circle && size.x == radius) return; this->type = type; shape = Shape::Circle; size.x = radius; physics_dirty = true; multiplayer_dirty = true; } | ||
void setRectangle(Type type, glm::vec2 new_size) { if (type == this->type && shape == Shape::Rectangle && size == new_size) return; this->type = type; shape = Shape::Circle; size = new_size; physics_dirty = true; multiplayer_dirty = true; } | ||
glm::vec2 getSize() const { return size; } | ||
|
||
glm::vec2 getVelocity() const { return linear_velocity; } | ||
float getAngularVelocity() const { return angular_velocity; } | ||
|
||
void setVelocity(glm::vec2 velocity) { linear_velocity_user_set = true; linear_velocity = velocity; } | ||
void setAngularVelocity(float velocity) { angular_velocity_user_set = true; angular_velocity = velocity; } | ||
private: | ||
bool physics_dirty = true; | ||
bool multiplayer_dirty = false; | ||
|
||
Type type = Type::Sensor; | ||
enum class Shape { | ||
Circle, | ||
Rectangle, | ||
} shape = Shape::Circle; | ||
glm::vec2 size{1.0, 1.0}; | ||
|
||
b2Body* body = nullptr; | ||
glm::vec2 linear_velocity{}; | ||
float angular_velocity = 0.0f; | ||
bool linear_velocity_user_set = false; | ||
bool angular_velocity_user_set = false; | ||
|
||
friend class sp::CollisionSystem; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
#include "systems/collision.h" | ||
#include "components/collision.h" | ||
#include "ecs/query.h" | ||
|
||
#include <glm/trigonometric.hpp> | ||
|
||
|
||
#if defined(__GNUC__) && !defined(__clang__) | ||
#pragma GCC diagnostic push | ||
#pragma GCC diagnostic ignored "-Wsuggest-override" | ||
#endif//__GNUC__ | ||
#include "Box2D/Box2D.h" | ||
#if defined(__GNUC__) && !defined(__clang__) | ||
#pragma GCC diagnostic pop | ||
#endif//__GNUC__ | ||
|
||
|
||
#define BOX2D_SCALE 20.0f | ||
static inline glm::vec2 b2v(b2Vec2 v) | ||
{ | ||
return glm::vec2(v.x * BOX2D_SCALE, v.y * BOX2D_SCALE); | ||
} | ||
static inline b2Vec2 v2b(glm::vec2 v) | ||
{ | ||
return b2Vec2(v.x / BOX2D_SCALE, v.y / BOX2D_SCALE); | ||
} | ||
|
||
|
||
static b2World* world; | ||
|
||
|
||
namespace sp { | ||
|
||
std::vector<CollisionHandler*> CollisionSystem::handlers; | ||
|
||
namespace { | ||
struct Collision | ||
{ | ||
sp::ecs::Entity A; | ||
sp::ecs::Entity B; | ||
float collision_force; | ||
}; | ||
} | ||
|
||
void CollisionSystem::update(float delta) | ||
{ | ||
if (!world) | ||
world = new b2World(b2Vec2(0, 0)); | ||
if (delta <= 0.0f) | ||
return; | ||
world->Step(delta, 4, 8); | ||
|
||
// Go over each entity with physics, and create/update bodies if needed. | ||
for(auto [entity, position, physics] : sp::ecs::Query<Position, Physics>()) { | ||
if (physics.physics_dirty) | ||
{ | ||
physics.physics_dirty = false; | ||
sp::ecs::Entity* ptr; | ||
if (physics.body) { | ||
ptr = (sp::ecs::Entity*)physics.body->GetUserData(); | ||
world->DestroyBody(physics.body); | ||
} else { | ||
ptr = new sp::ecs::Entity(); | ||
*ptr = entity; | ||
} | ||
|
||
b2BodyDef bodyDef; | ||
bodyDef.type = physics.type == Physics::Type::Dynamic ? b2_dynamicBody : b2_kinematicBody; | ||
bodyDef.userData = ptr; | ||
bodyDef.allowSleep = false; | ||
bodyDef.position = v2b(position.position); | ||
bodyDef.angle = glm::radians(position.rotation); | ||
physics.body = world->CreateBody(&bodyDef); | ||
|
||
b2FixtureDef shapeDef; | ||
shapeDef.density = 1.f; | ||
shapeDef.friction = 0.f; | ||
shapeDef.isSensor = physics.type == Physics::Type::Sensor; | ||
|
||
if (physics.shape == Physics::Shape::Circle) { | ||
b2CircleShape shape; | ||
shape.m_radius = physics.size.x / BOX2D_SCALE; | ||
shapeDef.shape = &shape; | ||
physics.body->CreateFixture(&shapeDef); | ||
} else { | ||
b2PolygonShape shape; | ||
shape.SetAsBox(physics.size.x / 2.f / BOX2D_SCALE, physics.size.y / 2.f / BOX2D_SCALE, {0, 0}, 0); | ||
shapeDef.shape = &shape; | ||
physics.body->CreateFixture(&shapeDef); | ||
} | ||
} | ||
if (position.position_user_set && physics.body) { | ||
physics.body->SetTransform(v2b(position.position), physics.body->GetAngle()); | ||
position.position_user_set = false; | ||
} | ||
if (position.rotation_user_set && physics.body) { | ||
physics.body->SetTransform(physics.body->GetPosition(), glm::radians(position.rotation)); | ||
position.rotation_user_set = false; | ||
} | ||
if (physics.linear_velocity_user_set && physics.body) { | ||
physics.body->SetLinearVelocity(v2b(physics.linear_velocity)); | ||
physics.linear_velocity_user_set = false; | ||
} | ||
if (physics.angular_velocity_user_set && physics.body) { | ||
physics.body->SetAngularVelocity(glm::radians(physics.angular_velocity)); | ||
physics.angular_velocity_user_set = false; | ||
} | ||
} | ||
|
||
// Go over each body in the physics world, and update the entity, or delete the body if the entity is gone. | ||
std::vector<b2Body*> remove_list; | ||
for(b2Body* body = world->GetBodyList(); body; body = body->GetNext()) { | ||
sp::ecs::Entity* entity_ptr = (sp::ecs::Entity*)body->GetUserData(); | ||
Position* position; | ||
Physics* physics; | ||
if (!*entity_ptr || !(physics = entity_ptr->getComponent<Physics>()) || !(position = entity_ptr->getComponent<Position>())) { | ||
delete entity_ptr; | ||
remove_list.push_back(body); | ||
} else { | ||
position->position = b2v(body->GetPosition()); | ||
position->rotation = glm::degrees(body->GetAngle()); | ||
physics->linear_velocity = b2v(body->GetLinearVelocity()); | ||
physics->angular_velocity = glm::degrees(body->GetAngularVelocity()); | ||
position->multiplayer_dirty = true; //TODO make this condition and like, not sending every tick. | ||
} | ||
} | ||
for(auto body : remove_list) { | ||
world->DestroyBody(body); | ||
} | ||
|
||
// Find all the collisions and process them. | ||
std::vector<Collision> collisions; | ||
for(b2Contact* contact = world->GetContactList(); contact; contact = contact->GetNext()) | ||
{ | ||
if (contact->IsTouching() && contact->IsEnabled()) | ||
{ | ||
float force = 0.0f; | ||
for (int n = 0; n < contact->GetManifold()->pointCount; n++) | ||
{ | ||
force += contact->GetManifold()->points[n].normalImpulse * BOX2D_SCALE; | ||
} | ||
auto a = (sp::ecs::Entity*)contact->GetFixtureA()->GetBody()->GetUserData(); | ||
auto b = (sp::ecs::Entity*)contact->GetFixtureB()->GetBody()->GetUserData(); | ||
|
||
for(auto handler : handlers) | ||
{ | ||
if (!*a || !*b) | ||
break; | ||
handler->collision(*a, *b, force); | ||
} | ||
} | ||
} | ||
} | ||
|
||
class QueryCallback : public b2QueryCallback | ||
{ | ||
public: | ||
std::vector<sp::ecs::Entity> list; | ||
|
||
/// Called for each fixture found in the query AABB. | ||
/// @return false to terminate the query. | ||
virtual bool ReportFixture(b2Fixture* fixture) override | ||
{ | ||
auto ptr = (sp::ecs::Entity*)fixture->GetBody()->GetUserData(); | ||
if (*ptr) | ||
list.push_back(*ptr); | ||
return true; | ||
} | ||
}; | ||
|
||
std::vector<sp::ecs::Entity> CollisionSystem::queryArea(glm::vec2 lowerBound, glm::vec2 upperBound) | ||
{ | ||
QueryCallback callback; | ||
b2AABB aabb; | ||
aabb.lowerBound = v2b(lowerBound); | ||
aabb.upperBound = v2b(upperBound); | ||
if (aabb.lowerBound.x > aabb.upperBound.x) | ||
std::swap(aabb.upperBound.x, aabb.lowerBound.x); | ||
if (aabb.lowerBound.y > aabb.upperBound.y) | ||
std::swap(aabb.upperBound.y, aabb.lowerBound.y); | ||
world->QueryAABB(&callback, aabb); | ||
return callback.list; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#pragma once | ||
|
||
#include "ecs/entity.h" | ||
#include <glm/vec2.hpp> | ||
|
||
|
||
namespace sp { | ||
|
||
class CollisionHandler | ||
{ | ||
public: | ||
virtual void collision(sp::ecs::Entity a, sp::ecs::Entity b, float force) = 0; | ||
}; | ||
|
||
class CollisionSystem | ||
{ | ||
public: | ||
static void update(float delta); | ||
static void addHandler(CollisionHandler* handler) { handlers.push_back(handler); } | ||
|
||
static std::vector<sp::ecs::Entity> queryArea(glm::vec2 lowerBound, glm::vec2 upperBound); | ||
private: | ||
static std::vector<CollisionHandler*> handlers; | ||
}; | ||
|
||
} |
0750acf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TinyCI build failure:
[/home/tinyci/builds/daid/SeriousProton/_build_native:make -j 2] returned [2]: