#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <box2d/box2d.h>
#include <vector>
#include <sstream>
#include "DebugDraw.hpp"
class QueryCallback : public b2QueryCallback
{
public:
QueryCallback(const b2Vec2& point)
{
m_point = point;
m_fixture = NULL;
}
bool ReportFixture(b2Fixture* fixture)
{
b2Body* body = fixture->GetBody();
if (body->GetType() == b2_dynamicBody)
{
bool inside = fixture->TestPoint(m_point);
if (inside)
{
m_fixture = fixture;
return false;
}
}
return true;
}
b2Vec2 m_point;
b2Fixture* m_fixture;
};
//Converts SFML's vector to Box2D's vector and downscales it so it fits Box2D's MKS units
template<typename T >
b2Vec2 sfVecToB2Vec(sf::Vector2<T> vector)
{
return b2Vec2(vector.x / sfdd::SCALE, vector.y / sfdd::SCALE);
}
//Creates a box body and returns a reference to it
b2Body* createSquare(b2World &world, sf::RenderWindow &window)
{
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position = sfVecToB2Vec(sf::Mouse::getPosition(window));
b2Body* body = world.CreateBody(&bodyDef);
b2PolygonShape boxShape;
boxShape.SetAsBox(0.5f, 0.5f, b2Vec2(0.f, 0.f), 0);
body->CreateFixture(&boxShape, 1.f);
return body;
}
//Creates a circle body and returns a reference to it
b2Body* createCircle(b2World &world, sf::RenderWindow &window)
{
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position = sfVecToB2Vec(sf::Mouse::getPosition(window));
b2Body* body = world.CreateBody(&bodyDef);
b2CircleShape circleShape;
circleShape.m_radius = 0.5f;
circleShape.m_p.SetZero();
body->CreateFixture(&circleShape, 1.f);
return body;
}
int main()
{
/* Create window and limit the framerate */
sf::RenderWindow window(sf::VideoMode(1024,768, 32), "Box2D - SFML Debug Draw Test");
window.setFramerateLimit(60);
/* Initialize font */
sf::Font mainFont;
if(!mainFont.loadFromFile("assets/Ubuntu-Regular.ttf")) // Set path to your font
{
return 1;
}
/* Initialize debug text */
sf::Text fpsCounter;
fpsCounter.setFont(mainFont);
fpsCounter.setFillColor(sf::Color::White);
sf::Text helpText;
helpText.setFont(mainFont);
helpText.setFillColor(sf::Color::White);
helpText.setCharacterSize(20);
helpText.setPosition(static_cast<float>(window.getSize().x) - static_cast<float>(window.getSize().x/ 2.f), 0.f );
std::string helpTextString = std::string("Press 1 to spawn a box\n") +
std::string("Press 2 to spawn a ball\n") +
std::string("Press backspace to remove the most recent body\n") +
std::string("Press F1 to enable/disable drawing of shapes\n") +
std::string("Press F2 to enable/disable drawing of AABB's\n") +
std::string("Press F3 to enable/disable drawing of center of gravity for objects\n") +
std::string("Press Tab to show/hide this text");
helpText.setString(helpTextString);
/* Create a stringstream for conversion purposes */
std::stringstream sstream;
/* Create physical world and set it's gravity */
b2World world(b2Vec2(0.f, 10.f));
world.SetAllowSleeping(true);
/* Initialize SFML Debug Draw */
SFMLDebugDraw debugDraw(window);
world.SetDebugDraw(&debugDraw);
/* Set initial flags for what to draw */
debugDraw.SetFlags(b2Draw::e_shapeBit); //Only draw shapes
/* constants for time step and physics accuracy */
const float timeStep = 1.0f / 60.0f;
const int velocityIterations = 6;
const int positionIterations = 2;
/* Create the bounding box */
b2BodyDef boundingBoxDef;
boundingBoxDef.type = b2_staticBody;
float xPos = (window.getSize().x / 2.f) / sfdd::SCALE;
float yPos = 0.5f;
boundingBoxDef.position.Set(xPos, yPos);
b2Body* boundingBoxBody = world.CreateBody(&boundingBoxDef);
b2PolygonShape boxShape;
boxShape.SetAsBox((window.getSize().x) / sfdd::SCALE, 0.5f, b2Vec2(0.f, 0.f), 0.f);
boundingBoxBody->CreateFixture(&boxShape, 1.0); //Top
yPos = (window.getSize().y) / sfdd::SCALE - 1.f;
boxShape.SetAsBox((window.getSize().x) / sfdd::SCALE, 0.5f, b2Vec2(0.f, yPos), 0.f);
boundingBoxBody->CreateFixture(&boxShape, 1.f); //Bottom
xPos -= 0.5f;
boxShape.SetAsBox(0.5f, (window.getSize().y) / sfdd::SCALE, b2Vec2(-xPos, 0.f), 0.f);
boundingBoxBody->CreateFixture(&boxShape, 1.f);//Left
boxShape.SetAsBox(0.5f, (window.getSize().y) / sfdd::SCALE, b2Vec2(xPos, 0.f), 0.f);
boundingBoxBody->CreateFixture(&boxShape, 1.f);//Right
/* Mouse Joint */
b2MouseJoint* mouseJoint = nullptr;
b2BodyDef bodyDef;
b2Body* ground = world.CreateBody(&bodyDef); //This is not the body of the bounding box
//This body exists to serve as an anchor point for the mouse joint
sf::Clock deltaClock; //Clock used to measure FPS
bool helpTextEnabled = true;
std::vector<b2Body*> bodies;
while(window.isOpen())
{
sf::Time deltaTime = deltaClock.restart();
sf::Event event;
while(window.pollEvent(event))
{
if(event.type == sf::Event::Closed || (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape))
window.close();
else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::F1)
{
if(debugDraw.GetFlags() & b2Draw::e_shapeBit) debugDraw.ClearFlags(b2Draw::e_shapeBit);
else debugDraw.AppendFlags(b2Draw::e_shapeBit);
}
else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::F2)
{
if(debugDraw.GetFlags() & b2Draw::e_aabbBit) debugDraw.ClearFlags(b2Draw::e_aabbBit);
else debugDraw.AppendFlags(b2Draw::e_aabbBit);
}
else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::F3)
{
if(debugDraw.GetFlags() & b2Draw::e_centerOfMassBit) debugDraw.ClearFlags(b2Draw::e_centerOfMassBit);
else debugDraw.AppendFlags(b2Draw::e_centerOfMassBit);
}
else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Tab)
{
if(helpTextEnabled) helpTextEnabled = false;
else helpTextEnabled = true;
}
else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Num1)
{
bodies.push_back(createSquare(world, window));
}
else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Num2)
{
bodies.push_back(createCircle(world, window));
}
else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::BackSpace)
{
if(!bodies.empty())
{
world.DestroyBody(bodies.back());
bodies.pop_back();
}
}
// Following three events are copied almost completely from http://code.google.com/p/box2d/source/browse/trunk/Box2D/Testbed/Framework/Test.cpp
// Copyright (c) 2011 Erin Catto http://box2d.org
else if(event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left && mouseJoint == nullptr)
{
b2Vec2 mousePos = sfVecToB2Vec(sf::Mouse::getPosition(window));
// Make a small box.
b2AABB aabb;
b2Vec2 d;
d.Set(0.001f, 0.001f);
aabb.lowerBound = mousePos + d;
aabb.upperBound = mousePos - d;
// Query the world for overlapping shapes.
QueryCallback callback(mousePos);
world.QueryAABB(&callback, aabb);
if (callback.m_fixture)
{
b2Body* body = callback.m_fixture->GetBody();
b2MouseJointDef md;
md.bodyA = ground; //If bounding box body is used instead, the dynamic bodes can be dragged over the bounding box and we don't want that
md.bodyB = body;
md.target = mousePos;
md.maxForce = 1000.0f * body->GetMass();
mouseJoint = (b2MouseJoint*)world.CreateJoint(&md);
body->SetAwake(true);
}
}
else if(event.type == sf::Event::MouseMoved && mouseJoint != nullptr)
{
b2Vec2 mousePos = sfVecToB2Vec(sf::Mouse::getPosition(window));
mouseJoint->SetTarget(mousePos);
}
else if(event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left && mouseJoint != nullptr)
{
world.DestroyJoint(mouseJoint);
mouseJoint = nullptr;
}
}
window.clear();
world.Step(timeStep,velocityIterations,positionIterations);
{//Display FPS
sstream.precision(0);
sstream << std::fixed << "FPS: " << 1.f / deltaTime.asSeconds();
fpsCounter.setString(sstream.str());
window.draw(fpsCounter);
sstream.str("");
}
if (helpTextEnabled)
{
window.draw(helpText);
}
world.DebugDraw();
window.display();
}
return 0;
}