Commit 5e267650 authored by Leon's avatar Leon

GET IT TO RUN

parent 6650d62a
......@@ -3,6 +3,6 @@ project(DuodimDemo_Exec VERSION 1.0 DESCRIPTION "2D physics engine" LANGUAGES CX
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
file(GLOB SRC "src/*.cpp")
add_definitions(-DBUILD_VTI -DFREEGLUT_STATIC)
add_executable(exec ${SRC})
target_include_directories(exec PUBLIC "../engine/include" "../freeglut/include/GL")
target_link_libraries(exec PUBLIC engine freeglut_static)
\ No newline at end of file
add_executable(duodim_demo ${SRC})
target_include_directories(duodim_demo PUBLIC "../engine/include" "../freeglut/include/GL")
target_link_libraries(duodim_demo PUBLIC duodim_engine freeglut_static)
\ No newline at end of file
......@@ -3,6 +3,7 @@
#define FREEGLUT_LIB_PRAGMAS 0
#include <algorithm>
#include <cstring>
#include <iostream>
#include <vector>
#include "freeglut.h"
......@@ -17,18 +18,34 @@
constexpr auto ESC_KEY = 27;
constexpr auto MAX_POLY_COUNT = 32;
using namespace duodim;
using namespace std;
using namespace chrono;
duodim::Engine* e = nullptr;
duodim::Scene* s = nullptr;
void RenderString(int x, int y, const char* s)
void checkArgs(int argc, char** argv)
{
if (argc == 1)
{
return;
}
const char* is;
for (int i = 1; i < argc; i++)
{
is = argv[i];
if (strcmp(is, "--skip-collision") == 0)
{
e->toggleSkipCollisions();
}
}
}
void RenderString(int x, int y, const char* string)
{
glColor3f(.5f, .5f, .9f);
glRasterPos2i(x, y);
unsigned int l = (unsigned int)strlen(s);
unsigned int l = (unsigned int)strlen(string);
for (unsigned i = 0; i < l; ++i)
{
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, *(s + i));
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, *(string + i));
}
}
......@@ -45,24 +62,24 @@ void Mouse(int button, int state, int x, int y)
{
RenderString(1, 6, "Spawning Polygon");
unsigned int count = (unsigned int)rand(3.f, (float)MAX_POLY_COUNT);
vector<Vector2D> vertices;
float e = rand(5.f, 10.f);
vector<duodim::Vector2D> vertices;
float random = rand(5.f, 10.f);
for (unsigned int i = 0; i < count; ++i)
{
vertices.push_back(Vector2D(rand(-e, e), rand(-e, e)));
vertices.push_back(duodim::Vector2D(rand(-random, random), rand(-random, random)));
}
ConvexPolygon* cp = new ConvexPolygon(vertices);
Texture tex = Texture::red();
Body* b = new Body((float) x, (float) y, &Material::wood(), cp, &tex);
Scene::getInstance()->bodies.push_back(b);
duodim::ConvexPolygon* cp = new duodim::ConvexPolygon(vertices);
duodim::Texture tex = duodim::Texture::red();
duodim::Body* b = new duodim::Body((float) x, (float) y, &duodim::Material::wood(), cp, &tex);
s->bodies.push_back(b);
}
break;
case GLUT_RIGHT_BUTTON:
{
Circle* cs = new Circle(rand(1.f, 3.f));
Texture tex = Texture::green();
Body* c = new Body((float) x, (float) y, &Material::bouncyBall(), cs, &tex);
Scene::getInstance()->bodies.push_back(c);
duodim::Circle* cs = new duodim::Circle(rand(1.f, 3.f));
duodim::Texture tex = duodim::Texture::green();
duodim::Body* c = new duodim::Body((float) x, (float) y, &duodim::Material::bouncyBall(), cs, &tex);
s->bodies.push_back(c);
}
break;
}
......@@ -77,10 +94,10 @@ void Keyboard(unsigned char key, int x, int y)
exit(0);
break;
case 'f':
Engine::getInstance()->toggleFrameStepping();
e->toggleFrameStepping();
break;
case ' ':
Engine::getInstance()->canStep = true;
e->canStep = true;
break;
}
}
......@@ -90,7 +107,7 @@ void PhysicsLoop(void)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
RenderString(1, 2, "Left click to spawn a polygon");
RenderString(1, 4, "Right click to spawn a circle");
Engine::getInstance()->iterate();
e->iterate();
glutSwapBuffers();
}
......@@ -120,23 +137,28 @@ int initGui(int argc, char** argv)
*/
int main(int argc, char** argv)
{
e = duodim::Engine::getInstance(); // fire up engine
s = duodim::Scene::getInstance();
checkArgs(argc, argv);
if (initGui(argc, argv) == -1)
{
return -1;
}
ConvexPolygon* poly = new ConvexPolygon();
poly->setBox(30.0f, 1.0f);
Texture tex = Texture::blue();
Body* b = new Body(40.f, 55.f, &Material::wood(), poly, &tex);
b->setStatic();
Scene::getInstance()->bodies.push_back(b);
if (!e->skipCollisions)
{
duodim::ConvexPolygon* poly = new duodim::ConvexPolygon();
poly->setBox(30.0f, 1.0f);
duodim::Texture tex = duodim::Texture::blue();
duodim::Body* b = new duodim::Body(40.f, 55.f, &duodim::Material::wood(), poly, &tex);
b->setStatic();
s->bodies.push_back(b);
/* Circle* cs = new Circle(rand(1.f, 3.f));
Texture ctex = Texture::green();
Body* c = new Body(75.f, 20.f, &Material::bouncyBall(), cs, &ctex);
Scene::getInstance()->bodies.push_back(c);*/
/* Circle* cs = new Circle(rand(1.f, 3.f));
Texture ctex = Texture::green();
Body* c = new Body(75.f, 20.f, &Material::bouncyBall(), cs, &ctex);
s->bodies.push_back(c);*/
}
srand(1);
glutMainLoop();
......
......@@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.6.2)
project(DuodimDemo_Engine VERSION 1.0 DESCRIPTION "2D physics engine" LANGUAGES CXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
file(GLOB SRC "src/*.cpp")
add_library(engine ${SRC})
target_include_directories(engine PUBLIC include)
target_include_directories(engine PUBLIC "../freeglut/include/GL")
target_link_libraries(engine PUBLIC freeglut_static)
target_compile_features(engine PUBLIC cxx_std_17)
\ No newline at end of file
add_library(duodim_engine ${SRC})
target_include_directories(duodim_engine PUBLIC include)
target_include_directories(duodim_engine PUBLIC "../freeglut/include/GL")
target_link_libraries(duodim_engine PUBLIC freeglut_static)
target_compile_features(duodim_engine PUBLIC cxx_std_17)
\ No newline at end of file
......@@ -14,10 +14,9 @@ namespace duodim
vector<Vector2D> contactPoints;
Vector2D normal;
Vector2D tangent;
float penetration;
float penetration = 0.f;
unsigned int contactCount = 0u;
Collision(Body* a, Body* b);
Collision(Vector2D position, Vector2D normal, float penetration);
bool solve();
void addContactPosition(const Vector2D p);
void applyImpulse();
......@@ -25,10 +24,11 @@ namespace duodim
private:
Body* a;
Body* b;
float averageRestitution;
float mixedStaticFriction;
float mixedDynamicFriction;
float averageRestitution = 0.f;
float mixedStaticFriction = 0.f;
float mixedDynamicFriction = 0.f;
bool initialized = false;
void performNarrowPhase();
};
}
#endif
\ No newline at end of file
#ifndef INCLUDE_ENGINE_H
#define INCLUDE_ENGINE_H
#include <chrono>
using namespace std;
using namespace chrono;
namespace duodim
{
class Engine
{
public:
static float fps;
static float dt;
const float fps;
const float dt;
static Engine* getInstance();
bool skipCollisions = false;
bool frameStepping = false;
bool canStep = false;
void iterate();
void toggleFrameStepping();
void toggleSkipCollisions();
long long getElapsedTime();
long long getTimeDifference();
long long getCurrentTime();
private:
static Engine* _instance;
Engine() {} // prevent instantiation from outside
Engine() : fps(60.f), dt(1 / fps) {} // prevent instantiation from outside
Engine(const Engine&); // prevent copy constructor from outside
~Engine() {} // prevent destruction from outside
Engine* operator=(Engine* o) = delete;
class EngineInstanceGuard
{
public:
......@@ -38,10 +37,9 @@ namespace duodim
double accumulator = 0;
bool running = false;
high_resolution_clock::time_point mStart;
high_resolution_clock::time_point mStop;
high_resolution_clock::time_point mCurrent;
std::chrono::high_resolution_clock::time_point mStart;
std::chrono::high_resolution_clock::time_point mStop;
std::chrono::high_resolution_clock::time_point mCurrent;
};
Engine* Engine::_instance = 0;
}
#endif
\ No newline at end of file
......@@ -6,9 +6,6 @@
#include "Shape.hpp"
namespace duodim
{
typedef void (*CollisionCallback)(Collision* c, Body* a, Body* b);
extern CollisionCallback Dispatch[ShapeType::ECOUNT][ShapeType::ECOUNT];
class NarrowPhase
{
public:
......
......@@ -3,28 +3,26 @@
#include <vector>
#include "Body.hpp"
#include "Collision.hpp"
using namespace std;
namespace duodim
{
class Scene
{
public:
vector<Collision> contacts = {};
vector<Body*> bodies;
std::vector<Collision> contacts = {};
std::vector<Body*> bodies = {};
static Scene* getInstance();
static float gravityConst;
const float gravityConst;
void addBody(Body* b);
void step();
void render();
private:
static Scene* _sceneInstance;
Scene() {}
Scene() : gravityConst(9.81f) {}
Scene(const Scene&);
~Scene() {}
Scene* operator=(Scene* o) = delete;
class SceneInstanceGuard
{
......@@ -32,6 +30,5 @@ namespace duodim
~SceneInstanceGuard();
};
};
Scene* Scene::_sceneInstance = 0;
}
#endif
\ No newline at end of file
......@@ -13,12 +13,12 @@ namespace duodim
float inertia;
};
enum ShapeType { CIRCLE, CONVEXPOLYGON, ECOUNT };
enum class ShapeType { CIRCLE, CONVEXPOLYGON, ECOUNT };
class Shape
{
public:
Body* body;
Body* body = nullptr;
virtual AxisAlignedBoundingBox getBounds() = 0;
virtual MassAndInertia getMassAndInertia() = 0;
virtual ShapeType getType() = 0;
......
#ifndef INCLUDE_VECTOR2D_HPP
#define INCLUDE_VECTOR2D_HPP
#include <type_traits>
#ifndef INCLUDE_VECTOR2D_H
#define INCLUDE_VECTOR2D_H
namespace duodim
{
class Vector2D
......
......@@ -46,7 +46,7 @@ void Body::draw()
void Body::addForce(Vector2D f)
{
force += f;
force = force + f;
}
void Body::addForce(Vector2D f, Vector2D pos)
......@@ -57,7 +57,7 @@ void Body::addForce(Vector2D f, Vector2D pos)
void Body::addImpulse(Vector2D i, Vector2D pos)
{
velocity += (i * inv_mass);
velocity = velocity + (i * inv_mass);
angularVelocity = pos.crossMult(i) * inv_inertia;
}
......@@ -72,8 +72,9 @@ void Body::integrateForce()
{
return;
}
float dt = Engine::dt;
velocity += (((force * inv_mass) + Vector2D::gravity()) * (dt / 2.f));
float dt = Engine::getInstance()->dt;
Vector2D grav = Vector2D::gravity();
velocity = velocity + (((force * inv_mass) + grav) * (dt / 2.f));
angularVelocity += ((torque * inv_inertia) * (dt / 2.f));
}
......@@ -83,8 +84,8 @@ void Body::integrateVelocity()
{
return;
}
float dt = Engine::dt;
transform->position += (velocity * dt);
float dt = Engine::getInstance()->dt;
transform->position = transform->position + (velocity * dt);
transform->addRotation(angularVelocity * dt);
integrateForce();
force = Vector2D();
......@@ -101,7 +102,7 @@ void Body::setStatic()
mass = 0.f;
inertia = 0.f;
inv_mass = 0.f;
inertia = 0.f;
inv_inertia = 0.f;
}
void Body::unsetStatic()
......
......@@ -36,7 +36,7 @@ MassAndInertia Circle::getMassAndInertia()
ShapeType Circle::getType()
{
return CIRCLE;
return ShapeType::CIRCLE;
}
void Circle::updateName()
......@@ -67,9 +67,7 @@ void Circle::draw()
glBegin(GL_LINE_STRIP);
float c = cos(rotation);
float s = sin(rotation);
Vector2D r(s, c);
r *= radius;
r += body->transform->position;
Vector2D r = (Vector2D(s, c) * radius) + body->transform->position;
glVertex2f(body->transform->position.x, body->transform->position.y);
glVertex2f(r.x, r.y);
glEnd();
......
#include <algorithm>
#include "Engine.hpp"
#include "Collision.hpp"
#include "Engine.hpp"
#include "MathUtil.hpp"
#include "NarrowPhase.hpp"
#include "Vector2D.hpp"
......@@ -10,7 +10,7 @@ Collision::Collision(Body* a, Body* b) : a(a), b(b) {}
bool Collision::solve()
{
Dispatch[a->shape->getType()][b->shape->getType()](this, a, b);
performNarrowPhase();
if (this->initialized)
{
Material* am = a->material;
......@@ -25,7 +25,7 @@ bool Collision::solve()
Vector2D rb = cPoint - b->transform->position;
Vector2D rv = ((b->velocity + rb.crossMult(b->angularVelocity)) - (a->velocity - ra.crossMult(a->angularVelocity)));
// Resting collision: if gravity is the only influence of movement, remove restitution
if (rv.squaredMagnitude() < (Vector2D::gravity() * Engine::dt).squaredMagnitude() + EPSILON)
if (rv.squaredMagnitude() < (Vector2D::gravity() * Engine::getInstance()->dt).squaredMagnitude() + EPSILON)
{
averageRestitution = 0.f;
}
......@@ -105,7 +105,18 @@ void Collision::correctPositions()
{
const float kSlop = .05f; // Penetration allowance
const float percent = .4f; // Penetration correction percentage
Vector2D correction = normal * percent * (max(penetration - kSlop, 0.f) / (a->inv_mass + b->inv_mass));
a->transform->position -= (correction * a->inv_mass);
b->transform->position += (correction * a->inv_mass);
Vector2D correction = (normal * percent * (max((penetration - kSlop), 0.f) / (a->inv_mass + b->inv_mass))) * a->inv_mass;
a->transform->position -= correction;
b->transform->position += correction;
}
void Collision::performNarrowPhase()
{
typedef void (*CollisionCallback)(Collision* c, Body* a, Body* b);
CollisionCallback performNarrowPhaseByType[(unsigned long long) ShapeType::ECOUNT][(unsigned long long) ShapeType::ECOUNT] =
{
{ NarrowPhase::twoCircles, NarrowPhase::circlePolygon },
{ NarrowPhase::polygonCircle, NarrowPhase::twoPolygons }
};
performNarrowPhaseByType[(unsigned long long) a->shape->getType()][(unsigned long long) b->shape->getType()](this, a, b);
}
\ No newline at end of file
......@@ -77,7 +77,7 @@ MassAndInertia ConvexPolygon::getMassAndInertia()
ShapeType ConvexPolygon::getType()
{
return CONVEXPOLYGON;
return ShapeType::CONVEXPOLYGON;
}
Vector2D ConvexPolygon::getSupport(Vector2D& direction)
......
......@@ -2,11 +2,6 @@
#include "MathUtil.hpp"
#include "Scene.hpp"
using namespace duodim;
using namespace std;
using namespace chrono;
float Engine::fps = 75.f;
float Engine::dt = 1 / fps;
Engine* Engine::getInstance()
{
......@@ -14,8 +9,8 @@ Engine* Engine::getInstance()
if (!_instance)
{
_instance = new Engine();
_instance->mStart = high_resolution_clock::now();
_instance->mStop = high_resolution_clock::now();
_instance->mStart = std::chrono::high_resolution_clock::now();
_instance->mStop = std::chrono::high_resolution_clock::now();
Scene::getInstance(); // Fire up Scene instance along with Engine
}
return _instance;
......@@ -23,9 +18,9 @@ Engine* Engine::getInstance()
void Engine::iterate()
{
accumulator = (getElapsedTime() / static_cast<double>(duration_cast<nanoseconds>(seconds(1)).count()));
accumulator = Clamp(0.0, 0.2, accumulator);
mStart = high_resolution_clock::now();
accumulator = (getElapsedTime() / static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()));
accumulator = Clamp(0.0, 0.1, accumulator);
mStart = std::chrono::high_resolution_clock::now();
Scene* scene = Scene::getInstance();
while(accumulator >= dt)
......@@ -44,25 +39,30 @@ void Engine::iterate()
}
accumulator -= dt;
}
mStop = high_resolution_clock::now();
mStop = std::chrono::high_resolution_clock::now();
scene->render();
}
long long Engine::getElapsedTime()
{
mCurrent = high_resolution_clock::now();
return duration_cast<nanoseconds>(mCurrent - mStart).count();
mCurrent = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::nanoseconds>(mCurrent - mStart).count();
}
long long Engine::getTimeDifference()
{
return duration_cast<nanoseconds>(mStop - mStart).count();
return std::chrono::duration_cast<std::chrono::nanoseconds>(mStop - mStart).count();
}
long long Engine::getCurrentTime()
{
mCurrent = high_resolution_clock::now();
return duration_cast<nanoseconds>(mCurrent.time_since_epoch()).count();
mCurrent = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::nanoseconds>(mCurrent.time_since_epoch()).count();
}
void Engine::toggleSkipCollisions()
{
skipCollisions = !skipCollisions;
}
void Engine::toggleFrameStepping()
......@@ -75,6 +75,8 @@ Engine::EngineInstanceGuard::~EngineInstanceGuard()
if (NULL != Engine::_instance)
{
delete Engine::_instance;
Engine::_instance = NULL;
Engine::_instance = nullptr;
}
}
\ No newline at end of file
}
Engine* Engine::_instance = 0;
\ No newline at end of file
#include "NarrowPhase.hpp"
#include "MathUtil.hpp"
#include <cassert>
#include "Circle.hpp"
#include "Collision.hpp"
#include "ConvexPolygon.hpp"
#include "MathUtil.hpp"
#include "NarrowPhase.hpp"
#include "Shape.hpp"
using namespace duodim;
CollisionCallback Dispatch[ShapeType::ECOUNT][ShapeType::ECOUNT] =
{
{ NarrowPhase::twoCircles, NarrowPhase::circlePolygon },
{ NarrowPhase::polygonCircle, NarrowPhase::twoPolygons }
};
void NarrowPhase::twoCircles(Collision* c, Body* a, Body* b)
{
Circle* ca = reinterpret_cast<Circle*>(a->shape);
......@@ -227,11 +223,11 @@ int NarrowPhase::clip(Vector2D n, float c, Vector2D* face)
}
// If the points are on different sides of the plane
if (d1 * d2 < 0.0f) // less than to ignore -0.0f
if ((d1 * d2) < 0.0f) // less than to ignore -0.0f
{
// Push interesection point
float alpha = d1 / (d1 - d2);
out[sp] = face[0] + (face[1] - face[0]) * alpha;
out[sp] = face[0] + ((face[1] - face[0]) * alpha);
++sp;
}
......@@ -239,11 +235,7 @@ int NarrowPhase::clip(Vector2D n, float c, Vector2D* face)
face[0] = out[0];
face[1] = out[1];
if (sp != 3)
{
throw "Error while clipping.";
}
assert(sp != 3);
return sp;
}
......
......@@ -19,8 +19,6 @@ Scene* Scene::getInstance()
return _sceneInstance;
}
float Scene::gravityConst = -9.81f;
void Scene::addBody(Body* b)
{
bodies.push_back(b);
......@@ -29,30 +27,37 @@ void Scene::addBody(Body* b)
void Scene::step()
{
contacts.clear();
// Broad phase:
vector<CollisionPair> narrowPhaseCandidates = BroadPhase::getNarrowPhaseCandidates(bodies);
if (!narrowPhaseCandidates.empty())
if (!Engine::getInstance()->skipCollisions)
{
for (auto p : narrowPhaseCandidates)
// Broad phase:
std::vector<CollisionPair> narrowPhaseCandidates = BroadPhase::getNarrowPhaseCandidates(bodies);
if (!narrowPhaseCandidates.empty())
{
Collision c(p.a, p.b);
if (c.solve())
for (auto p : narrowPhaseCandidates)
{
contacts.emplace_back(c);
Collision c(p.a, p.b);
if (c.solve())
{
contacts.emplace_back(c);
}
}
}
}
bool hasContacts = !contacts.empty();
for (auto body : bodies)
{
body->integrateForce();
}
for (unsigned int i = 0; i < (unsigned int) Engine::fps; i++)
if (hasContacts)
{
for (auto c : contacts)
for (unsigned int i = 0; i < (unsigned int) Engine::getInstance()->fps; i++)
{
c.applyImpulse();
for (auto c : contacts)
{
c.applyImpulse();
}
}
}
......@@ -61,9 +66,12 @@ void Scene::step()
body->integrateVelocity();
}
for (auto c: contacts)
if (hasContacts)
{
c.correctPositions();
for (auto c : contacts)
{
c.correctPositions();
}
}
}
......@@ -100,7 +108,8 @@ void Scene::render()
for (auto pt : c.contactPoints)
{
glVertex2f(pt.x, pt.y);
pt += (n * .75f);
n *= .75f;
pt += n;
glVertex2f(pt.x, pt.y);
}
}
......@@ -115,4 +124,6 @@ Scene::SceneInstanceGuard::~SceneInstanceGuard()
delete Scene::_sceneInstance;
Scene::_sceneInstance = NULL;
}
}
\ No newline at end of file
}
Scene* Scene::_sceneInstance = 0;
\ No newline at end of file
......@@ -21,7 +21,7 @@ Vector2D Vector2D::one()