...
 
Commits (3)
cmake_minimum_required(VERSION 3.6.2)
project(DuodimEngine)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++17")
include_directories("${PROJECT_SOURCE_DIR}")
add_subdirectory(engine)
add_subdirectory(demo)
\ No newline at end of file
cmake_minimum_required(VERSION 3.6.2)
project(DuodimDemo_Demo)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++17")
file(GLOB SRC ${PROJECT_SOURCE_DIR}/src/*.cpp)
link_directories("../engine/build")
include_directories(include)
include_directories("../engine/include")
include_directories("../glut/include")
add_executable(${PROJECT_NAME} ${SRC})
target_link_libraries(${PROJECT_NAME} duodim_engine glut32.dll)
\ No newline at end of file
......@@ -38,7 +38,6 @@ void Mouse( int button, int state, int x, int y )
break;
case GLUT_RIGHT_BUTTON:
{
Circle c(rand(1.f, 3.f));
Scene::getInstance()->addBody(Body((float) x, (float) y, &Material::bouncyBall(), &Circle(rand(1.f, 3.f)), &Texture::random()));
}
break;
......@@ -64,7 +63,7 @@ void Keyboard(unsigned char key, int x, int y)
void PhysicsLoop( void )
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
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" );
......@@ -84,17 +83,20 @@ void RenderString(int x, int y, const char *s)
}
}
int main(int argc, char** argv)
void setupGlut(int* argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInit(argc, argv);
glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE);
glutInitWindowSize(800, 600);
glutCreateWindow("Duodim demo");
glutDisplayFunc(PhysicsLoop);
glutKeyboardFunc(Keyboard);
glutMouseFunc(Mouse);
glutIdleFunc(PhysicsLoop);
}
void setupGlutView()
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
......@@ -102,6 +104,15 @@ int main(int argc, char** argv)
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
}
/**
* Entry point
*/
int main(int argc, char** argv)
{
setupGlut(&argc, argv);
setupGlutView();
Engine::getInstance(); // Fire up engine
......
project(VCB_ENGINE)
cmake_minimum_required(VERSION 3.6.2)
project(DuodimDemo_Engine)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++17")
include_directories(include)
file(GLOB SRC "src/*.cpp")
add_library(duodim_engine SHARED ${SRC})
\ No newline at end of file
......@@ -12,8 +12,8 @@ namespace duodim
class Engine
{
public:
const float fps = 75.f;
const float dt = 1 / fps;
static float fps;
static float dt;
static Engine* getInstance();
......
......@@ -3,6 +3,7 @@
#include <optional>
#include <unordered_map>
#include "Body.hpp"
#include "Collision.hpp"
using namespace std;
......@@ -13,19 +14,20 @@ namespace duodim
public:
static Scene* getInstance();
static float gravityConst;
optional<Body> getBody(unsigned int id);
bool addBody(Body body);
bool removeBody(Body body);
void update(float deltaTime);
bool addBody(Body* body);
bool removeBody(Body* body);
void step();
void render();
private:
static Scene* _instance;
Scene() {}
Scene(const Scene&);
~Scene() {}
vector<Collision> contacts = {};
vector<unsigned int> bodyKeys;
unordered_map<unsigned int, Body> bodies;
unordered_map<unsigned int, Body*> bodies;
void updateKeys();
class SceneInstanceGuard
......
#ifndef INCLUDE_BROAD_PHASE_H
#define INCLUDE_BROAD_PHASE_H
#include <unordered_map>
#include <vector>
#include "AxisAlignedBoundingBox.hpp"
#include "Body.hpp"
......@@ -11,14 +12,7 @@ namespace duodim
class BroadPhase
{
public:
static BroadPhase& getInstance();
BroadPhase();
BroadPhase(BroadPhase const&) = delete;
void operator =(BroadPhase const&) = delete;
void generatePairs(vector<Body> bodies);
private:
vector<CollisionPair> pairs = {};
bool isBoxesCollide(AxisAlignedBoundingBox a, AxisAlignedBoundingBox b);
static vector<CollisionPair> getNarrowPhaseCandidates(vector<unsigned int> mapKeys, unordered_map<unsigned int, Body*> bodies);
};
}
#endif
\ No newline at end of file
#ifndef INCLUDE_COLLISION_H
#define INCLUDE_COLLISION_H
#include <vector>
#include "Body.hpp"
#include "Vector2D.hpp"
namespace duodim
......@@ -10,11 +11,9 @@ namespace duodim
class Collision
{
public:
Collision(Body* a, Body* b);
Collision(Vector2D<float> position, Vector2D<float> normal, float penetration);
/**
* Get the 2D space position of the contact.
*/
Vector2D<float> getPosition();
vector<Vector2D<float>> getContactPoints();
/**
*
*/
......@@ -28,19 +27,28 @@ namespace duodim
*/
float getPenetration();
void solve();
void init();
unsigned int getContacts();
bool solve();
void setContactCount(const unsigned int cnt);
void setPenetration(const float pen);
void setNormal(const Vector2D<float> n);
void addContactPosition(const Vector2D<float> p);
void applyImpulse();
void positionalCorrection();
void infiniteMassCorrection();
void clearContactPoints();
void correctPositions();
private:
Body* a;
Body* b;
Vector2D<float> position;
vector<Vector2D<float>> contactPoints;
Vector2D<float> normal;
Vector2D<float> tangent;
float penetration;
float averageRestitution;
float mixedStaticFriction;
float mixedDynamicFriction;
unsigned int contactCount = 0;
bool initialized = false;
};
}
#endif
\ No newline at end of file
......@@ -15,6 +15,10 @@ namespace duodim
static void circlePolygon(Collision* c, Body* a, Body* b);
static void polygonCircle(Collision* c, Body* a, Body* b);
static void twoPolygons(Collision* c, Body* a, Body* b);
private:
static float findAxisLeastPenetration(unsigned int* faceIdx, Body* a, ConvexPolygon* cpa, Body* b, ConvexPolygon* cpb);
static int clip(Vector2D<float> n, float c, Vector2D<float>* face);
static void findIncidentFace(Vector2D<float>* v, ConvexPolygon* refShape, Body* refBody, ConvexPolygon* incShape, Body* incBody, unsigned int refIdx);
};
}
#endif
\ No newline at end of file
......@@ -14,27 +14,10 @@ namespace duodim
class Body
{
public:
Body();
Body(Texture* texture);
Body(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, 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, Shape* shape, Texture* texture);
~Body();
AxisAlignedBoundingBox* getBounds();
Material* getMaterial();
Shape* getShape();
Texture* getTexture();
......@@ -46,25 +29,21 @@ namespace duodim
float getMass();
float getInverseMass();
float getInverseInertia();
float getDensity();
unsigned int getId();
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 setDensity(const float d);
void setId(const unsigned int i);
void update();
void integrateForce(const float deltaTime);
void integrateVelocity(const float deltaTime);
void integrateForce();
void integrateVelocity();
void nullifyVelocity();
void setStatic();
void unsetStatic();
private:
......@@ -86,8 +65,6 @@ namespace duodim
float inv_mass;
float inv_inertia;
float density;
unsigned int id = 0u;
void initialize();
......
......@@ -12,6 +12,8 @@ namespace duodim
Transform(Vector2D<float> position, float rotation);
~Transform();
Matrix2<float> getRotationMatrix();
Vector2D<float> position;
Vector2D<float> getWorldPosition(Vector2D<float> localPosition);
......@@ -20,6 +22,7 @@ namespace duodim
Vector2D<float> getLocalDirection(Vector2D<float> worldDirection);
float getRotation();
void setRotation(const float r);
void addRotation(const float r);
private:
Matrix2<float> rotMatrix;
float rotation;
......
......@@ -11,11 +11,13 @@ namespace duodim
Circle(float radius);
~Circle();
AxisAlignedBoundingBox getBounds(const Vector2D<float> position);
MassAndInertia getMassAndInertia(const float density);
AxisAlignedBoundingBox getBounds();
MassAndInertia getMassAndInertia();
ShapeType getType();
float getRadius();
void setRadius(float r);
void draw();
protected:
void updateName();
private:
......
#ifndef INCLUDE_CONVEX_POLYGON_HPP
#define INCLUDE_CONVEX_POLYGON_HPP
#include <list>
#include <optional>
#include "AxisAlignedBoundingBox.hpp"
#include "Shape.hpp"
#include "Transform.hpp"
......@@ -14,23 +12,26 @@ namespace duodim
{
public:
ConvexPolygon();
ConvexPolygon(list<Vector2D<float>> vertices);
ConvexPolygon(vector<Vector2D<float>> vertices);
~ConvexPolygon();
AxisAlignedBoundingBox getBounds(const optional<Transform> transformOpt);
MassAndInertia getMassAndInertia(const float density);
ShapeType getType();
vector<Vector2D<float>> getVertices();
vector<Vector2D<float>> getNormals();
list<Vector2D<float>> getVertices();
list<Vector2D<float>> getNormals();
AxisAlignedBoundingBox getBounds();
MassAndInertia getMassAndInertia();
ShapeType getType();
Vector2D<float> getSupport(Vector2D<float>& direction);
void setVertices(list<Vector2D<float>> v);
void setNormals(list<Vector2D<float>> n);
void setVertices(vector<Vector2D<float>> v);
void init();
void draw();
protected:
void calculateNormals();
void updateName();
private:
list<Vector2D<float>> vertices;
list<Vector2D<float>> normals;
vector<Vector2D<float>> vertices;
vector<Vector2D<float>> normals;
};
}
#endif
\ No newline at end of file
......@@ -3,6 +3,7 @@
#include <optional>
#include <string>
#include "AxisAlignedBoundingBox.hpp"
#include "Body.hpp"
#include "Transform.hpp"
using namespace std;
namespace duodim
......@@ -16,16 +17,18 @@ namespace duodim
class Shape
{
public:
virtual AxisAlignedBoundingBox getBounds(optional<Transform> transformOpt);
virtual MassAndInertia getMassAndInertia(const float density);
virtual AxisAlignedBoundingBox getBounds();
virtual MassAndInertia getMassAndInertia();
virtual ShapeType getType();
virtual void draw();
string getName();
void setBody(Body* b);
void setName(string n);
protected:
virtual void updateName();
Body* body;
string name;
virtual void updateName();
};
}
#endif
\ No newline at end of file
......@@ -16,8 +16,8 @@ namespace duodim
Vector2D<float> getExtents();
AxisAlignedBoundingBox expandBy(const float factor);
float getPerimeter();
bool intersects(const AxisAlignedBoundingBox o);
bool contains(const AxisAlignedBoundingBox o);
bool intersects(AxisAlignedBoundingBox o);
bool contains(AxisAlignedBoundingBox o);
private:
Vector2D<float> min;
Vector2D<float> max;
......
#ifndef INCLUDE_MATH_UTIL_H
#define INCLUDE_MATH_UTIL_H
#include <cstdlib>
#define EPSILON 0.0001f
inline float rand(float a, float b)
{
float r = (float) rand();
r /= RAND_MAX;
return (b - a) * r + a;
}
inline bool BiasGreaterThan(float a, float b)
{
const float k_biasRelative = 0.95f;
const float k_biasAbsolute = 0.01f;
return a >= (b * k_biasRelative) + (a * k_biasAbsolute);
}
#endif
\ No newline at end of file
......@@ -24,6 +24,7 @@ namespace duodim
Vector2D<T> operator /(const T a);
Vector2D<T> operator +=(const Vector2D<T> b);
Vector2D<T> operator -=(const Vector2D<T> b);
Vector2D<T> operator *=(const T a);
Vector2D<T> crossMult(const T a);
Vector2D<T> iCrossMult(const T a);
......
......@@ -12,6 +12,9 @@ Engine* Engine::getInstance()
if (!_instance)
{
_instance = new Engine();
_instance->fps = 75.f;
_instance->dt = 1 / fps;
#ifdef WIN32
SetThreadAffinityMask(GetCurrentThread(), 1);
_Query_perf_frequency(_instance->mFreq);
......
#include <optional>
#include <unordered_map>
#include "Scene.hpp"
#include "glut.h"
#include "Body.hpp"
#include "BroadPhase.hpp"
#include "Engine.hpp"
#include "Scene.hpp"
using namespace duodim;
using namespace std;
......@@ -17,16 +20,11 @@ Scene* Scene::getInstance()
return _instance;
}
optional<Body> Scene::getBody(unsigned int id)
{
return bodies.find(id) == bodies.end() ? nullopt : optional<Body>{bodies[id]};
}
float Scene::gravityConst = -9.81f;
bool Scene::addBody(Body body)
bool Scene::addBody(Body* body)
{
unsigned int startId = body.getId(); // new bodies will always have 0u
unsigned int startId = body->getId(); // new bodies will always have 0u
for (unsigned int i = 0; i < bodyKeys.size(); i++)
{
if (startId >= bodyKeys[i])
......@@ -38,25 +36,94 @@ bool Scene::addBody(Body body)
i = bodyKeys.size();
}
}
body.setId(startId);
body->setId(startId);
bodies[startId] = body;
updateKeys();
return true;
}
bool Scene::removeBody(Body body)
bool Scene::removeBody(Body* body)
{
bodies.erase(body.getId());
bodies.erase(body->getId());
updateKeys();
return true;
}
void Scene::update(float deltaTime)
void Scene::step()
{
for(auto b : bodies)
contacts.clear();
// Broad phase:
vector<CollisionPair> narrowPhaseCandidates = BroadPhase::getNarrowPhaseCandidates(bodyKeys, bodies);
if (!narrowPhaseCandidates.empty())
{
for (auto p : narrowPhaseCandidates)
{
Collision c(p.a, p.b);
if (c.solve())
{
contacts.emplace_back(c);
}
}
}
for (auto kv : bodies)
{
kv.second->integrateForce();
}
for (unsigned int i = 0; i < (unsigned int) Engine::fps; i++)
{
for (auto c : contacts)
{
c.applyImpulse();
}
}
for (auto kv : bodies)
{
b.second.update();
kv.second->integrateVelocity();
}
for (auto c: contacts)
{
c.correctPositions();
}
}
void Scene::render()
{
for (auto kv : bodies)
{
kv.second->getShape()->draw();
}
glPointSize(4.f);
glBegin(GL_POINTS);
glColor3f(1.f, 0.f, 0.f);
for (auto c: contacts)
{
for (auto pt : c.getContactPoints())
{
glVertex2f(pt.getX(), pt.getY());
}
}
glEnd();
glPointSize(1.f);
glBegin(GL_LINES);
glColor3f(0.f, 1.f, 0.f);
for (auto c: contacts)
{
Vector2D<float> n = c.getNormal();
for (auto pt : c.getContactPoints())
{
glVertex2f( pt.getX(), pt.getY());
n *= 0.75f;
pt += n;
glVertex2f( pt.getX(), pt.getY());
}
}
glEnd();
}
void Scene::updateKeys()
......
#include <unordered_map>
#include <vector>
#include "AxisAlignedBoundingBox.hpp"
#include "Body.hpp"
......@@ -5,37 +6,26 @@
using namespace duodim;
using namespace std;
BroadPhase& BroadPhase::getInstance()
vector<CollisionPair> BroadPhase::getNarrowPhaseCandidates(vector<unsigned int> mapKeys, unordered_map<unsigned int, Body*> bodies)
{
static BroadPhase instance;
return instance;
}
BroadPhase::BroadPhase() {}
void BroadPhase::generatePairs(vector<Body> bodies)
{
pairs.clear();
AxisAlignedBoundingBox a_aabb;
AxisAlignedBoundingBox b_aabb;
vector<CollisionPair> pairs;
for (auto i = 0; i < bodies.size(); i++)
{
for (auto j = 0; j != bodies.size(); j++)
{
Body a = bodies[i];
Body b = bodies[j];
if (a == b)
if (j == i)
{
continue; // Prevent identical bodies
}
Body* a = bodies[i];
Body* b = bodies[j];
if (a->isStatic() && b->isStatic())
{
continue;
}
a.update();
b.update();
if (a.getBounds().intersects(b.getBounds()))
else if (a->getShape()->getBounds().intersects(b->getShape()->getBounds()))
{
pairs.push_back({&a, &b});
pairs.push_back({a, b});
}
}
}
......
#include <algorithm>
#include "Engine.hpp"
#include "Collision.hpp"
#include "MathUtil.hpp"
#include "NarrowPhase.hpp"
#include "Vector2D.hpp"
using namespace duodim;
Collision::Collision(Vector2D<float> position, Vector2D<float> normal, float penetration) : position(position), normal(normal), penetration(penetration)
{
tangent = normal.crossMult(1.f);
Collision::Collision(Body* a, Body* b) : a(a), b(b) {
Dispatch[a->getShape()->getType()][b->getShape()->getType()](this, a, b);
}
Vector2D<float> Collision::getPosition()
vector<Vector2D<float>> Collision::getContactPoints()
{
return position;
return contactPoints;
}
Vector2D<float> Collision::getNormal()
......@@ -28,7 +30,134 @@ float Collision::getPenetration()
return penetration;
}
void Collision::solve()
bool Collision::solve()
{
Dispatch[a->getShape()->getType()][b->getShape()->getType()](this, a, b);
if (initialized)
{
Material* am = a->getMaterial();
Material* bm = b->getMaterial();
averageRestitution = min(am->getRestitution(), bm->getRestitution());
mixedStaticFriction = sqrt(am->getStaticFriction() * bm->getStaticFriction());
mixedDynamicFriction = sqrt(am->getDynamicFriction() * bm->getDynamicFriction());
for (auto cPoint : contactPoints)
{
Vector2D<float> ra = cPoint - a->getTransform()->position;
Vector2D<float> rb = cPoint - b->getTransform()->position;
Vector2D<float> rv = (
(b->getVelocity() + rb.crossMult(b->getAngularVelocity()))
- (a->getVelocity() - ra.crossMult(a->getAngularVelocity()))
);
// Resting collision: if gravity is the only influence of movement, remove restitution
if (rv.squaredMagnitude() < (Vector2D<float>::gravity() * Engine::dt).squaredMagnitude() + EPSILON)
{
averageRestitution = 0.f;
}
}
}
return initialized;
}
void Collision::setContactCount(const unsigned int cnt)
{
contactCount = cnt;
}
void Collision::setPenetration(const float pen)
{
penetration = pen;
}
void Collision::setNormal(const Vector2D<float> n)
{
normal = n;
}
void Collision::addContactPosition(const Vector2D<float> p)
{
contactPoints.push_back(p);
contactCount++;
}
void Collision::applyImpulse()
{
if (a->getInverseMass() + b->getInverseMass() == 0.f)
{
a->nullifyVelocity();
b->nullifyVelocity();
return;
}
Transform* at = a->getTransform();
Transform* bt = a->getTransform();
for (auto cPoint : contactPoints)
{
// Radii from COM to contact
Vector2D<float> ra = cPoint - at->position;
Vector2D<float> rb = cPoint - bt->position;
// Relative velocity
Vector2D<float> rv = (
(b->getVelocity() + rb.crossMult(b->getAngularVelocity()))
- (a->getVelocity() - ra.crossMult(a->getAngularVelocity()))
);
float contactVelocity = rv * normal;
if (contactVelocity > 0) // velocities must not separate
{
return;
}
float raCrossN = ra.crossMult(normal);
float rbCrossN = rb.crossMult(normal);
float invMassSum = (a->getInverseMass() + b->getInverseMass() + pow(raCrossN, 2)) * (a->getInverseInertia() + pow(rbCrossN, 2)) * b->getInverseInertia();
// Impulse scalar
float j = ((contactVelocity * -(1.f + averageRestitution)) / invMassSum) / (float) contactCount;
// Apply impulse
Vector2D<float> impulse = normal * j;
a->addImpulse(impulse * -1.f, ra);
b->addImpulse(impulse, rb);
//Recalculate relative velocity for friction impulse
rv = (
(b->getVelocity() + rb.crossMult(b->getAngularVelocity()))
- (a->getVelocity() - ra.crossMult(a->getAngularVelocity()))
);
// Friction impulse direction
Vector2D<float> fiDirection = (rv - (normal * (normal * rv))).normalized();
// Friction impulse scalar
float jt = (-(rv * fiDirection) / invMassSum) / (float) contactCount;
if (jt == 0.f) // Friction impuls too tiny, abort
{
return;
}
// Applying Coulumb's law
Vector2D<float> tImpulse = abs(jt) < j * mixedStaticFriction
? fiDirection * jt
: fiDirection * -j * mixedDynamicFriction
;
a->addImpulse(tImpulse * -1.f, ra);
b->addImpulse(tImpulse, rb);
}
}
void Collision::clearContactPoints()
{
contactPoints.clear();
contactCount = 0u;
}
void Collision::correctPositions()
{
const float kSlop = .05f; // Penetration allowance
const float percent = .4f; // Penetration correction percentage
Vector2D<float> correction = normal * percent * (max(penetration - kSlop, 0.f) / (a->getInverseMass + b->getInverseMass()));
a->getTransform()->position -= (correction * a->getInverseMass());
b->getTransform()->position += (correction * a->getInverseMass());
}
\ No newline at end of file
This diff is collapsed.
#include <optional>
#include "Body.hpp"
#include "AxisAlignedBoundingBox.hpp"
#include "Body.hpp"
#include "Circle.hpp"
#include "Engine.hpp"
#include "Shape.hpp"
#include "Transform.hpp"
#include "Vector2D.hpp"
......@@ -9,84 +10,33 @@
using namespace duodim;
Body::Body()
: transform(&Transform()), material(&Material()), shape(&Circle(1.f))
Body::Body(Vector2D<float> position, Material* material, Shape* shape, Texture* texture)
: transform(&Transform(position, 0.f)), material(material), shape(shape), texture(texture)
{
initialize();
}
Body::Body(Shape* shape)
: transform(&Transform()), material(&Material()), shape(shape)
Body::Body(float posX, float posY, Material* material, Shape* shape, Texture* texture)
: transform(&Transform(Vector2D<float>(posX, posY))), material(material), shape(shape), texture(texture)
{
initialize();
}
Body::Body(Material* material)
: transform(&Transform()), material(material), shape(&Circle(1.f))
Body::~Body()
{
initialize();
}
delete material;
delete shape;
delete texture;
delete transform;
Body::Body(Material* material, Shape* shape)
: transform(Transform()), material(material), shape(shape)
{
initialize();
material = NULL;
shape = NULL;
texture = NULL;
transform = NULL;
}
Body::Body(Vector2D<float> position)
: transform(Transform(position)), material(Material()), shape(&Circle(1.f))
{
initialize();
}
Body::Body(Vector2D<float> position, Shape* shape)
: transform(Transform(position, 0.f)), material(Material()), shape(shape)
{
initialize();
}
Body::Body(Vector2D<float> position, Material material)
: transform(Transform(position, 0.f)), material(material), shape(&Circle(1.f))
{
initialize();
}
Body::Body(Vector2D<float> position, Material material, Shape* shape)
: transform(Transform(position, 0.f)), material(material), shape(shape)
{
initialize();
}
Body::Body(float posX, float posY)
: transform(Transform(Vector2D<float>(posX, posY))), material(Material()), shape(&Circle(1.f))
{
initialize();
}
Body::Body(float posX, float posY, Shape* shape)
: transform(Transform(Vector2D<float>(posX, posY))), material(Material()), shape(shape)
{
initialize();
}
Body::Body(float posX, float posY, Material material)
: transform(Transform(Vector2D<float>(posX, posY))), material(material), shape(&Circle(1.f))
{
initialize();
}
Body::Body(float posX, float posY, Material material, Shape* shape)
: transform(Transform(Vector2D<float>(posX, posY))), material(material), shape(shape)
{
initialize();
}
AxisAlignedBoundingBox Body::getBounds()
{
return bounds;
}
Material Body::getMaterial()
Material* Body::getMaterial()
{
return material;
}
......@@ -96,7 +46,7 @@ Shape* Body::getShape()
return shape;
}
Transform Body::getTransform()
Transform* Body::getTransform()
{
return transform;
}
......@@ -126,11 +76,6 @@ float Body::getInverseInertia()
return inv_inertia;
}
float Body::getDensity()
{
return density;
}
unsigned int Body::getId()
{
return id;
......@@ -168,46 +113,39 @@ void Body::addTorque(float t)
torque += t;
}
void Body::setShape(Shape* s)
{
shape = s;
}
void Body::setDensity(const float d)
{
density = d;
unsetStatic();
}
void Body::setId(const unsigned int i)
{
id = i;
}
void Body::update()
{
bounds = shape->getBounds(optional<Transform>{transform});
}
void Body::integrateForce(const float deltaTime)
void Body::integrateForce()
{
if (isStatic())
{
return;
}
auto g = Vector2D<float>::gravity();
velocity += (force * inv_mass + g) * (deltaTime / 2.f);
angularVelocity += torque * inv_inertia * (deltaTime / 2.f);
float dt = Engine::dt;
velocity += (((force * inv_mass) + Vector2D<float>::gravity()) * (dt / 2.f));
angularVelocity += ((torque * inv_inertia) * (dt / 2.f));
}
void Body::integrateVelocity(const float deltaTime)
void Body::integrateVelocity()
{
if (isStatic())
{
return;
}
transform.position += (velocity * deltaTime);
transform.setRotation(transform.getRotation() + angularVelocity * deltaTime);
float dt = Engine::dt;
transform->position += (velocity * dt);
transform->addRotation(angularVelocity * dt);
integrateForce();
force = Vector2D<float>();
torque = 0.f;
}
void Body::nullifyVelocity()
{
velocity = Vector2D<float>();
}
void Body::setStatic()
......@@ -220,7 +158,7 @@ void Body::setStatic()
void Body::unsetStatic()
{
MassAndInertia mai = shape->getMassAndInertia(density);
MassAndInertia mai = shape->getMassAndInertia();
mass = mai.mass;
inertia = mai.inertia;
inv_mass = mass != 0.f ? 1.f / mass : 0.f;
......@@ -229,7 +167,6 @@ void Body::unsetStatic()
void Body::initialize()
{
density = material.getDensity();
update();
shape->setBody(this);
unsetStatic();
}
\ No newline at end of file
......@@ -10,6 +10,11 @@ Transform::Transform(Vector2D<float> position)
Transform::Transform(Vector2D<float> position, float rotation)
: position(position), rotation(rotation), rotMatrix(Matrix2<float>::fromRadians(rotation)) {}
Matrix2<float> Transform::getRotationMatrix()
{
return rotMatrix;
}
Vector2D<float> Transform::getWorldPosition(Vector2D<float> localPosition)
{
return rotMatrix * localPosition + position;
......@@ -39,4 +44,10 @@ void Transform::setRotation(const float r)
{
rotation = r;
rotMatrix = Matrix2<float>::fromRadians(r);
}
void Transform::addRotation(const float r)
{
rotation += r;
rotMatrix = Matrix2<float>::fromRadians(r);
}
\ No newline at end of file
......@@ -3,6 +3,7 @@
#include <string>
#include "Circle.hpp"
#include "AxisAlignedBoundingBox.hpp"
#include "glut.h"
using namespace duodim;
using namespace std;
......@@ -15,15 +16,15 @@ Circle::Circle(float radius) : radius(radius) {
}
Circle::~Circle() {}
AxisAlignedBoundingBox Circle::getBounds(const Vector2D<float> position)
AxisAlignedBoundingBox Circle::getBounds()
{
return AxisAlignedBoundingBox::fromCenterAndExtents(position, (Vector2D<float>::one() * radius));
return AxisAlignedBoundingBox::fromCenterAndExtents(body->getTransform()->position, (Vector2D<float>::one() * radius));
}
MassAndInertia Circle::getMassAndInertia(const float density)
MassAndInertia Circle::getMassAndInertia()
{
float sqRad = pow(radius, 2);
float mass = density * M_PI * sqRad;
float mass = body->getMaterial()->getDensity() * M_PI * sqRad;
float inertia = .5f * mass * sqRad;
return {mass, inertia};
}
......@@ -47,4 +48,39 @@ void Circle::setRadius(float r)
void Circle::updateName()
{
name = "Circle (" + (radius == 0.f ? "empty" : to_string(radius)) + ")";
}
void Circle::draw()
{
const int kSegments = 20;
ColorCode color = body->getTexture()->getColor();
// Render a circle with a bunch of lines
glColor3f(color.r, color.g, color.b);
glBegin(GL_LINE_LOOP);
Vector2D<float> position = body->getTransform()->position;
float rotation = body->getTransform()->getRotation();
float theta = rotation;
float inc = M_PI * 2.0f / (float) kSegments;
for(int i = 0; i < kSegments; ++i)
{
theta += inc;
Vector2D<float> p(cos(theta), sin(theta));
p *= radius;
p += position;
glVertex2f(p.getX(), p.getY());
}
glEnd();
// Render line within circle so orientation is visible
glBegin(GL_LINE_STRIP);
Vector2D<float> r(0.f, 1.0f);
float c = cos(rotation);
float s = sin(rotation);
r.setX(r.getX() * c - r.getY() * s);
r.setY(r.getX() * s + r.getY() * c);
r *= radius;
r = r + position;
glVertex2f(position.getX(), position.getY());
glVertex2f(r.getX(), r.getY());
glEnd();
}
\ No newline at end of file
#include <math.h>
#include <list>
#include <string>
#include <vector>
#include "glut.h"
#include "Transform.hpp"
#include "ConvexPolygon.hpp"
#include "Vector2D.hpp"
......@@ -13,7 +14,7 @@ ConvexPolygon::ConvexPolygon() : vertices({}), normals({})
updateName();
}
ConvexPolygon::ConvexPolygon(list<Vector2D<float>> vertices) : vertices(vertices)
ConvexPolygon::ConvexPolygon(vector<Vector2D<float>> vertices) : vertices(vertices)
{
Vector2D<float> centroid = Vector2D<float>();
float area = 0.f;
......@@ -32,49 +33,39 @@ ConvexPolygon::ConvexPolygon(list<Vector2D<float>> vertices) : vertices(vertices
v -= centroid;
}
auto anormals = list<Vector2D<float>>();
i = 0u;
for (auto v : vertices)
{
auto j = (i + 1) % vertices.size();
auto it = vertices.begin();
advance(it, j);
anormals.push_back((v - *it).normalized().crossMult(1.f));
i++;
}
normals = anormals;
calculateNormals();
updateName();
}
ConvexPolygon::~ConvexPolygon() {}
AxisAlignedBoundingBox ConvexPolygon::getBounds(const optional<Transform> transformOpt)
vector<Vector2D<float>> ConvexPolygon::getVertices()
{
return vertices;
}
vector<Vector2D<float>> ConvexPolygon::getNormals()
{
return normals;
}
AxisAlignedBoundingBox ConvexPolygon::getBounds()
{
Vector2D<float> min = Vector2D<float>::one() * INFINITY;
Vector2D<float> max = Vector2D<float>::one() * (-1.f) * INFINITY;
for (auto v : vertices)
{
Vector2D<float> vertex;
if (transformOpt.has_value())
{
Transform t = transformOpt.value();
vertex = t.getWorldPosition(v);
}
else
{
vertex = v;
}
Vector2D<float> vertex = body->getTransform()->getWorldPosition(v);
min = Vector2D<float>::min(min, vertex);
max = Vector2D<float>::max(max, vertex);
}
return AxisAlignedBoundingBox(min, max);
}
MassAndInertia ConvexPolygon::getMassAndInertia(const float density)
MassAndInertia ConvexPolygon::getMassAndInertia()
{
float area = 0.f, densityInertia = 0.f;
float area = 0.f;
float densityInertia = 0.f;
auto i = 0u;
for (auto v : vertices)
{
......@@ -89,6 +80,7 @@ MassAndInertia ConvexPolygon::getMassAndInertia(const float density)
densityInertia = tri_inertia;
i++;
}
float density = body->getMaterial()->getDensity();
return {(area * density), (densityInertia * density)};
}
......@@ -97,26 +89,60 @@ ShapeType ConvexPolygon::getType()
return CONVEXPOLYGON;
}
list<Vector2D<float>> ConvexPolygon::getVertices()
Vector2D<float> ConvexPolygon::getSupport(Vector2D<float>& direction)
{
return vertices;
}
float bestProjection = -FLT_MAX;
Vector2D<float> bestVertex;
list<Vector2D<float>> ConvexPolygon::getNormals()
{
return normals;
for(auto v : vertices)
{
float projection = v * direction;
if (projection > bestProjection)
{
bestVertex = v;
bestProjection = projection;
}
}
return bestVertex;
}
void ConvexPolygon::setVertices(list<Vector2D<float>> v)
void ConvexPolygon::setVertices(vector<Vector2D<float>> v)
{
vertices = v;
calculateNormals();
updateName();
}
void ConvexPolygon::setNormals(list<Vector2D<float>> n)
void ConvexPolygon::draw()
{
normals = n;
updateName();
ColorCode color = body->getTexture()->getColor();
Transform* t = body->getTransform();
glColor3f( color.r, color.g, color.b );
glBegin(GL_LINE_LOOP);
for (auto v : vertices)
{
Vector2D<float> tgt = t->position + t ->getRotationMatrix() * v;
glVertex2f(tgt.getX(), tgt.getY());
}
glEnd();
}
void ConvexPolygon::calculateNormals()
{
auto anormals = vector<Vector2D<float>>();
unsigned int i = 0u;
for (auto v : vertices)
{
auto j = (i + 1) % vertices.size();
auto it = vertices.begin();
advance(it, j);
anormals.push_back((v - *it).normalized().crossMult(1.f));
i++;
}
normals = anormals;
}
void ConvexPolygon::updateName()
......@@ -126,5 +152,4 @@ void ConvexPolygon::updateName()
name = vempty && nempty
? "ConvexPolygon (empty)"
: "ConvexPolygon (" + (vempty ? "no" : to_string(vertices.size())) + " polygons, " + (nempty ? "no" : to_string(normals.size())) + " normals)";
}
\ No newline at end of file
#include <optional>
#include <string>
#include "AxisAlignedBoundingBox.hpp"
#include "Body.hpp"
#include "Shape.hpp"
#include "Transform.hpp"
using namespace duodim;
using namespace std;
AxisAlignedBoundingBox Shape::getBounds(const optional<Transform> transformOpt)
AxisAlignedBoundingBox Shape::getBounds()
{
return AxisAlignedBoundingBox(Vector2D<float>(), Vector2D<float>());
}
MassAndInertia Shape::getMassAndInertia(const float density)
MassAndInertia Shape::getMassAndInertia()
{
return {1.f, 1.f};
}
......@@ -29,8 +30,12 @@ string Shape::getName()
void Shape::updateName() {}
void Shape::setName(string n)
void Shape::setBody(Body* b)
{
name = n;
body = b;
}
void Shape::setName(string n)
{
name = n;
}
\ No newline at end of file
......@@ -50,10 +50,10 @@ float AxisAlignedBoundingBox::getPerimeter()
bool AxisAlignedBoundingBox::intersects(AxisAlignedBoundingBox o)
{
return !(max.getX() < o.min.getX() || min.getX() > o.min.getX()) && !(max.getY() < o.min.getY() || min.getY() > o.max.getY);
return !(max.getX() < o.min.getX() || min.getX() > o.min.getX()) && !(max.getY() < o.min.getY() || min.getY() > o.max.getY());
}
bool AxisAlignedBoundingBox::contains(const AxisAlignedBoundingBox o)
bool AxisAlignedBoundingBox::contains(AxisAlignedBoundingBox o)
{
return Vector2D<float>::min(min, o.min) == min && Vector2D<float>::max(max, o.max) == max;
}
\ No newline at end of file
......@@ -79,6 +79,12 @@ Vector2D<T> Vector2D<T>::operator -=(const Vector2D<T> b)
return this - b;
}
template <typename T>
Vector2D<T> Vector2D<T>::operator *=(const T a)
{
return this * a;
}
template <typename T>
Vector2D<T> Vector2D<T>::crossMult(const T a)
{
......