Commit 121e0a36 authored by Leon's avatar Leon

wire Engine, scene and main setup

parent c0b56520
#include <algorithm>
#include "Body.hpp"
#include "Circle.hpp"
#include "ConvexPolygon.hpp"
#include "Engine.hpp"
#include "glut.h"
#include "MathUtils.hpp"
#include "MathUtil.hpp"
#include "Scene.hpp"
#include "Vector2D.hpp"
......@@ -14,12 +15,6 @@ using namespace duodim;
using namespace std;
using namespace chrono;
Engine engine;
Scene scene;
bool frameStepping = false;
bool canStep = false;
void Mouse( int button, int state, int x, int y )
{
x /= 10.f;
......@@ -38,22 +33,13 @@ void Mouse( int button, int state, int x, int y )
{
vertices.push_back(Vector2D<float>(rand(-e, e), rand(-e, e)));
}
ConvexPolygon poly(vertices);
Body *b = scene.Add(&poly, x, y);
b->SetOrient( Random( -PI, PI ) );
b->restitution = 0.2f;
b->dynamicFriction = 0.2f;
b->staticFriction = 0.4f;
delete [] vertices;
Scene::getInstance()->addBody(Body((float) x, (float) y, &Material::wood(), &ConvexPolygon(vertices), &Texture::random()));
}
break;
case GLUT_RIGHT_BUTTON:
{
Circle c(rand(1.f, 3.f));
Body *b = scene.Add(&c, x, y);
Scene::getInstance()->addBody(Body((float) x, (float) y, &Material::bouncyBall(), &Circle(rand(1.f, 3.f)), &Texture::random()));
}
break;
}
......@@ -67,31 +53,11 @@ void Keyboard(unsigned char key, int x, int y)
case ESC_KEY:
exit(0);
break;
case 's':
{
//Circle c( 25.0f );
//scene.Add( &c, 400 + (rand( ) % 250) * ((rand( ) % 2 == 1) ? 1 : -1), 50 );
//OBB obb;
//real e = Random( 10.0f, 35.0f );
//obb.extents.Set( e, e );
//Body *b = scene.Add( &obb, 400 + (rand( ) % 250) * ((rand( ) % 2 == 1) ? 1 : -1), 50 );
//b->SetOrient( PI / 4.0f );
//b->restitution = 0.2f;
//b->dynamicFriction = 0.2f;
//b->staticFriction = 0.4f;
}
break;
case 'd':
{
//Circle c( 25.0f );
//scene.Add( &c, 420, 50 );
}
break;
case 'f':
frameStepping = frameStepping ? false : true;
Engine::getInstance()->toggleFrameStepping();
break;
case ' ':
canStep = true;
Engine::getInstance()->canStep = true;
break;
}
}
......@@ -103,36 +69,7 @@ void PhysicsLoop( void )
RenderString( 1, 2, "Left click to spawn a polygon" );
RenderString( 1, 4, "Right click to spawn a circle" );
static double accumulator = 0;
// Different time mechanisms for Linux and Windows
#ifdef WIN32
accumulator += engine.getElapsedTime();
#else
accumulator += engine.getElapsedTime() / static_cast<double>(duration_cast<nanoseconds>(seconds(1)).count());
#endif
engine.start();
accumulator = clamp(0., .1, accumulator);
while(accumulator >= engine.dt)
{
if(!frameStepping)
{
scene.Step();
}
else
{
if(canStep)
{
scene.Step();
canStep = false;
}
}
accumulator -= engine.dt;
}
engine.stop();
scene.Render();
Engine::getInstance()->iterate();
glutSwapBuffers();
}
......@@ -166,14 +103,8 @@ int main(int argc, char** argv)
glPushMatrix();
glLoadIdentity();
Circle c(5.f);
Body *b = scene.Add(&c, 40, 40);
b->setStatic();
Engine::getInstance(); // Fire up engine
PolygonShape poly;
poly.SetBox(30.f, 1.f);
b = scene.Add(&poly, 40, 55);
b->setStatic();
srand(1);
glutMainLoop();
return 0;
......
......@@ -15,12 +15,13 @@ namespace duodim
const float fps = 75.f;
const float dt = 1 / fps;
static Engine& getInstance();
Engine();
Engine(Engine const&) = delete;
~Engine();
void start();
void stop();
static Engine* getInstance();
bool frameStepping = false;
bool canStep = false;
void iterate();
void toggleFrameStepping();
#ifdef WIN32
float getElapsedTime();
......@@ -31,10 +32,19 @@ namespace duodim
long long getTimeDifference();
long long getCurrentTime();
#endif
void operator =(Engine const&) = delete;
private:
float accumulator = 0;
static Engine* _instance;
Engine() {} // prevent instantiation from outside
Engine(const Engine&); // prevent copy constructor from outside
~Engine() {} // prevent destruction from outside
class EngineInstanceGuard
{
public:
~EngineInstanceGuard();
};
double accumulator = 0;
bool running = false;
#ifdef WIN32
......@@ -47,5 +57,6 @@ namespace duodim
chrono::high_resolution_clock::time_point mCurrent;
#endif
};
Engine* Engine::_instance = 0;
}
#endif
\ No newline at end of file
......@@ -11,21 +11,29 @@ namespace duodim
class Scene
{
public:
static Scene& getInstance();
static Scene* getInstance();
static float gravityConst;
Scene();
Scene(Scene const&) = delete;
optional<Body> getBody(unsigned int id);
bool addBody(Body body);
bool removeBody(Body body);
void update(float deltaTime);
void operator =(Scene const&) = delete;
private:
static optional<Scene> instance;
static Scene* _instance;
Scene() {}
Scene(const Scene&);
~Scene() {}
vector<unsigned int> bodyKeys;
unordered_map<unsigned int, Body> bodies;
void updateKeys();
class SceneInstanceGuard
{
public:
~SceneInstanceGuard();
};
};
Scene* Scene::_instance = 0;
}
#endif
\ No newline at end of file
#ifndef INCLUDE_CONTACT_CONSTRAINT_H
#define INCLUDE_CONTACT_CONSTRAINT_H
#include <vector>
#include "Body.hpp"
#include "Collision.hpp"
using namespace std;
namespace duodim
{
/**
* Not exactly sure what this thing exactly does, yet.
* TODO: find out what exactly this thing does.
*/
class ContactConstraint
{
const float RESTITUTION_VELOCITY_SLOP = 0.5f;
const float PENETRATION_SLOP = 0.005f;
const float BAUMGARTE = 0.1f;
public:
/**
* For a given vector of Contacts, return a vector of ContactConstraints that carry the contacts.
* @param newContacts given vector of Contacts
* @return a vector of ContactConstraints
*/
static vector<ContactConstraint> withContacts(vector<Collision> newContacts);
/**
* For a given vector or existing ContactConstraint and a vector of Contacts, generate a vector of new ContactConstraints
* and try to apply existing ContactConstraint values before returning them.
* @param oldConstraints given vector of existing constraints
* @param newContacts given vector of Contacts
* @return a vector of ContactConstraints
*/
static vector<ContactConstraint> withPersistentContacts(vector<ContactConstraint> oldConstraints, vector<Collision> newContacts);
/**
* ContactConstraint constructor.
* @param contact the associated Collision.
*/
ContactConstraint(Collision contact);
/**
* Return this constraint's contact.
*/
Collision getContact();
/**
* Perform initial velocity calculations for two given bodies.
* @param a first Body
* @param b second Body
*/
void initializeVelocity(Body a, Body b);
/**
* Apply collision impulses for two given bodies.
* @param a first Body
* @param b second Body
*/
void warmStartVelocity(Body a, Body b);
void solveVelocity(Body a, Body b);
void solvePosition(Body a, Body b);
private:
Collision contact;
float normalImpulse = 0.f;
float tangentImpulse = 0.f;
float normalMass = 0.f;
float tangentMass = 0.f;
float restitution = 0.f;
float frictionCoefficient = 0.f;
};
}
#endif
\ No newline at end of file
#ifndef INCLUDE_BODY_H
#define INCLUDE_BODY_H
#include "AxisAlignedBoundingBox.hpp"
#include "Material.hpp"
#include "Shape.hpp"
#include "Texture.hpp"
#include "Transform.hpp"
#include "Vector2D.hpp"
namespace duodim
{
struct BodyPair {
......@@ -16,22 +15,31 @@ namespace duodim
{
public:
Body();
Body(Texture* texture);
Body(Shape* shape);
Body(Material material);
Body(Material material, Shape* shape);
Body(Shape* shape, Texture* texture);
Body(Material* material);
Body(Material* material, Texture* texture);
Body(Material* material, Shape* shape);
Body(Material* material, Shape* shape, Texture* texture);
Body(Vector2D<float> position);
Body(Vector2D<float> position, Shape* shape);
Body(Vector2D<float> position, Material material);
Body(Vector2D<float> position, Material material, Shape* shape);
Body(Vector2D<float> position, Material* material);
Body(Vector2D<float> position, Material* material, Shape* shape);
Body(Vector2D<float> position, Material* material, Shape* shape, Texture* texture);
Body(float posX, float posY);
Body(float posX, float posY, Shape* shape);
Body(float posX, float posY, Material material);
Body(float posX, float posY, Material material, Shape* shape);
Body(float posX, float posY, Material* material);
Body(float posX, float posY, Material* material, Shape* shape);
Body(float posX, float posY, Material* material, Shape* shape, Texture* texture);
~Body();
Material getMaterial();
AxisAlignedBoundingBox* getBounds();
Material* getMaterial();
Shape* getShape();
AxisAlignedBoundingBox getBounds();
Transform getTransform();
Texture* getTexture();
Transform* getTransform();
Vector2D<float> getVelocity();
float getAngularVelocity();
......@@ -45,11 +53,13 @@ namespace duodim
bool isStatic();
bool operator ==(Body o);
void setShape(Shape* s);
void addForce(Vector2D<float> f);
void addForce(Vector2D<float> f, Vector2D<float> pos);
void addImpulse(Vector2D<float> i, Vector2D<float> pos);
void addTorque(float t);
void setShape(Shape* s);
void setDensity(const float d);
void setId(const unsigned int i);
void update();
......@@ -58,14 +68,15 @@ namespace duodim
void setStatic();
void unsetStatic();
private:
Transform transform;
AxisAlignedBoundingBox* bounds;
Texture* texture;
Material* material;
Transform* transform;
Shape* shape;
Vector2D<float> velocity = Vector2D<float>();
Vector2D<float> force = Vector2D<float>();
Shape* shape;
AxisAlignedBoundingBox bounds;
Material material;
float angularVelocity = 0.f;
float torque = 0.f;
......
#ifndef INCLUDE_TEXTURE_H
#define INCLUDE TEXTURE_H
namespace duodim
{
struct ColorCode
{
float r;
float g;
float b;
};
class Texture
{
public:
static Texture random();
Texture(float r, float g, float b);
ColorCode getColor();
private:
Texture() = delete;
float r;
float g;
float b;
};
}
#endif
\ No newline at end of file
#include <algorithm>
#include <optional>
#include "Engine.hpp"
#include "Scene.hpp"
......@@ -5,58 +6,63 @@ using namespace duodim;
using namespace std;
using namespace chrono;
Engine& Engine::getInstance()
Engine* Engine::getInstance()
{
static Engine instance;
return instance;
static EngineInstanceGuard g;
if (!_instance)
{
_instance = new Engine();
#ifdef WIN32
SetThreadAffinityMask(GetCurrentThread(), 1);
_Query_perf_frequency(_instance->mFreq);
_Query_perf_counter(_instance->mStart);
_Query_perf_counter(_instance->mStop);
#else
_instance->mStart = high_resolution_clock::now();
_instance->mStop = high_resolution_clock::now();
#endif
Scene::getInstance(); // Fire up Scene instance along with Engine
}
return _instance;
}
Engine::Engine()
void Engine::iterate()
{
#ifdef WIN32
SetThreadAffinityMask(GetCurrentThread(), 1);
_Query_perf_frequency(&mFreq);
accumulator += getElapsedTime();
_Query_perf_counter(&mStart);
_Query_perf_counter(&mStop);
#else
accumulator += (getElapsedTime() / static_cast<double>(duration_cast<nanoseconds>(seconds(1)).count()));
mStart = high_resolution_clock::now();
mStop = high_resolution_clock::now();
#endif
}
Engine::~Engine() {}
accumulator = clamp(0., .1, accumulator);
void Engine::start()
{
running = true;
Scene& world = Scene::getInstance();
float frameStart = 0.f; // GetCurrentTime();
while (running)
Scene* scene = Scene::getInstance();
while(accumulator >= dt)
{
const float currentTime = 0.f; // GetCurrentTime();
accumulator += currentTime - frameStart;
frameStart = currentTime;
if (accumulator > .2f)
if(!frameStepping)
{
accumulator = .2f;
scene->step();
}
// Avoid spiral of death and clamp dt, thus clamping
// how many times the UpdatePhysics can be called in
// a single game loop.
while (accumulator > dt) {
world.update(dt);
accumulator -= dt;
else
{
if(canStep)
{
scene->step();
canStep = false;
}
}
// Render();
accumulator -= dt;
}
}
void Engine::stop()
{
running = false;
#ifdef WIN32
_Query_perf_counter(&mStop);
#else
mStop = high_resolution_clock::now();
#endif
scene->render();
}
#ifdef WIN32
......@@ -93,4 +99,18 @@ long long Engine::getCurrentTime()
mCurrent = high_resolution_clock::now();
return duration_cast<nanoseconds>(mCurrent.time_since_epoch()).count();
}
#endif
\ No newline at end of file
#endif
void Engine::toggleFrameStepping()
{
frameStepping = !frameStepping;
}
Engine::EngineInstanceGuard::~EngineInstanceGuard()
{
if (NULL != Engine::_instance)
{
delete Engine::_instance;
Engine::_instance = NULL;
}
}
\ No newline at end of file
......@@ -6,14 +6,17 @@
using namespace duodim;
using namespace std;
Scene& Scene::getInstance()
Scene* Scene::getInstance()
{
static Scene instance;
return instance;
static SceneInstanceGuard g;
if (!_instance)
{
_instance = new Scene();
_instance->bodies = {};
}
return _instance;
}
Scene::Scene() : bodies({}) {}
optional<Body> Scene::getBody(unsigned int id)
{
return bodies.find(id) == bodies.end() ? nullopt : optional<Body>{bodies[id]};
......@@ -23,7 +26,7 @@ float Scene::gravityConst = -9.81f;
bool Scene::addBody(Body body)
{
unsigned int startId = body.getId();
unsigned int startId = body.getId(); // new bodies will always have 0u
for (unsigned int i = 0; i < bodyKeys.size(); i++)
{
if (startId >= bodyKeys[i])
......@@ -63,4 +66,13 @@ void Scene::updateKeys()
{
bodyKeys.push_back(kv.first);
}
}
Scene::SceneInstanceGuard::~SceneInstanceGuard()
{
if (NULL != Scene::_instance)
{
delete Scene::_instance;
Scene::_instance = NULL;
}
}
\ No newline at end of file
#include <algorithm>
#include <math.h>
#include <vector>
#include "Body.hpp"
#include "Collision.hpp"
#include "ContactConstraint.hpp"
#include "Vector2D.hpp"
using namespace duodim;
using namespace std;
vector<ContactConstraint> ContactConstraint::withContacts(vector<Collision> newContacts)
{
vector<ContactConstraint> retVal;
for (auto c: newContacts)
{
retVal.push_back(ContactConstraint(c));
}
return retVal;
}
vector<ContactConstraint> ContactConstraint::withPersistentContacts(vector<ContactConstraint> oldConstraints, vector<Collision> newContacts)
{
vector<ContactConstraint> newConstraints = withContacts(newContacts);
const float PERSISTENT_DISTANCE = 0.01f;
for (unsigned int i = 0; i < oldConstraints.size(); i++)
{
for (int j = 0; j < newConstraints.size(); j++)
{
if ((newConstraints[j].contact.getPosition() - oldConstraints[i].contact.getPosition()).squaredMagnitude() <= PERSISTENT_DISTANCE)
{
newConstraints[j].normalImpulse = oldConstraints[i].normalImpulse;
newConstraints[j].tangentImpulse = oldConstraints[i].tangentImpulse;
j = newConstraints.size();
}
}
}
return newConstraints;
}
ContactConstraint::ContactConstraint(Collision contact) : contact(contact) {}
void ContactConstraint::initializeVelocity(Body a, Body b)
{
// Retrieve difference vectors between the contact position and the bodies centroid positions
Vector2D<float> diffContactApos = contact.getPosition() - a.getTransform().position;
Vector2D<float> diffContactBpos = contact.getPosition() - b.getTransform().position;
// Calculate the squared normal scalar from the difference vectors and the contact's normal
float diffContactAposNormal = pow((diffContactApos * contact.getNormal()), 2);
float diffContactBposNormal = pow((diffContactBpos * contact.getNormal()), 2);
// Get sum of inverse masses
float invMassSum = a.getInverseMass() + b.getInverseMass();
// Apply normal and tangent mass from calculcated values
normalMass = 1.f / (invMassSum + (diffContactApos.squaredMagnitude() - diffContactAposNormal) * a.getInverseInertia() + (diffContactBpos.squaredMagnitude() - diffContactBposNormal) * b.getInverseInertia());
tangentMass = 1.f / (invMassSum + diffContactAposNormal * a.getInverseInertia() + diffContactBposNormal * b.getInverseInertia());
// Arithmetic mean
restitution = .5f *(a.getMaterial().getRestitution() + b.getMaterial().getRestitution());
// Geometric mean
frictionCoefficient = sqrt(a.getMaterial().getFriction() * b.getMaterial().getFriction());
}
void ContactConstraint::warmStartVelocity(Body a, Body b)
{
// Retrieve difference vectors between the contact position and the bodies centroid positions
Vector2D<float> diffContactApos = contact.getPosition() - a.getTransform().position;
Vector2D<float> diffContactBpos = contact.getPosition() - b.getTransform().position;
// Calculate an impulse vector that will push the bodies away from each other
Vector2D<float> impulse = (contact.getNormal() * normalImpulse) + (contact.getTangent() * tangentImpulse);
// Apply the impulse. For one body, the impulse must be negated (since it's moving in the opposite direction)
a.addImpulse(impulse * (-1), diffContactApos);
b.addImpulse(impulse, diffContactBpos);
}
void ContactConstraint::solveVelocity(Body a, Body b)
{
// Retrieve difference vectors between the contact position and the bodies centroid positions
Vector2D<float> diffContactApos = contact.getPosition() - a.getTransform().position;
Vector2D<float> diffContactBpos = contact.getPosition() - b.getTransform().position;
// Calculate a relative velocity vector: Sum of difference between velocities and difference between cross products of difference vectors and angular velocities
Vector2D<float> relVel = b.getVelocity() - a.getVelocity() + diffContactBpos.crossMult(b.getAngularVelocity()) - diffContactApos.crossMult(a.getAngularVelocity());
// Apply friction impulses
float relVelTangent = relVel * contact.getTangent();
float jt = relVelTangent * tangentMass;
float maxFriction = frictionCoefficient * normalImpulse;
float oldImpulse = tangentImpulse;
tangentImpulse = clamp((oldImpulse + jt), maxFriction * -1, maxFriction);
jt = tangentImpulse - oldImpulse;