I'm trying to get physics working in SFML using Box2D, but for some reason I can't set the position of my sprite in my class.
I created a TestBox for my game, basically just a box that has physics, but it doesn't seem to be working. I am able to set the position of the box in my class constructor, but not in the update function I created.
So onto the code:
TestBox.h:
#pragma once
#include<Box2D/Box2D.h>
#include<SFML/Graphics.hpp>
#include<string>
#include<iostream>
class TestBox : public sf:: Drawable
{
public:
TestBox(b2World *world, std::string texturePath, float x, float y, float w, float h);
void update();
b2Body *getBody() { return body; }
b2Fixture *getFixture() { return fixture; }
private:
virtual void draw(sf::RenderTarget &target, sf::RenderStates states) const;
b2Body *body = nullptr;
b2Fixture *fixture = nullptr;
sf::Texture texture;
sf::Sprite boxSprite;
};
TestBox.cpp:
#include "TestBox.h"
TestBox::TestBox(b2World *world, std::string texturePath, float x, float y, float w, float h) {
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(x, y);
body = world->CreateBody(&bodyDef);
b2PolygonShape boxShape;
boxShape.SetAsBox(w / 2.0f, h / 2.0f);
b2FixtureDef fixtureDef;
fixtureDef.shape = &boxShape;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
fixture = body->CreateFixture(&fixtureDef);
texture.loadFromFile(texturePath.c_str());
boxSprite.setTexture(texture);
boxSprite.setOrigin(16.f, 16.f);
boxSprite.setPosition(30.0f * body->GetPosition().x, 30.0f * body->GetPosition().y);
boxSprite.setRotation(body->GetAngle() * 180 / b2_pi);
}
void TestBox::update() {
std::cout << boxSprite.getPosition().x << ", " << boxSprite.getPosition().y << std::endl;
boxSprite.setPosition(30.0f * body->GetPosition().x, 30.0f * body->GetPosition().y);
boxSprite.setRotation(body->GetAngle() * 180 / b2_pi);
}
void TestBox::draw(sf::RenderTarget &target, sf::RenderStates states) const {
target.draw(boxSprite, states);
}
So, I've tried to narrow down my problem as much as possible, and rule as much as I could out. Here is what I found:
I am able to set the position in the constructor, this works fine.
I have been checking the position of the Box2D body, the position of that changes fine.
I have checked the math:
30.0f * body->GetPosition().x
30.0f * body->GetPosition().y
this is accurate and works fine.
The update function does run, it prints normally.
The result of printing the position is always the start position
Let me know if I left out any information you could need.
Related
I am working on creating a 2d platformer and want to implement moving platforms that move backward and forwards a set distance from their original position.
At the moment, the platform moves in one direction as expected, but does not move back when it reaches the specified range.
What needs to be changed or added to make this work as expected?
KinematicBlock.cpp
/*!
\file kinematicBlock.cpp
*/
#include "kinematicBlock.h"
KinematicBlock::KinematicBlock(b2World * world, sf::Vector2f position, sf::Vector2f size, float orientation, sf::Texture *texture)
{
b2BodyDef l_bodyDef;
b2PolygonShape l_shape;
b2FixtureDef l_fixtureDef;
l_bodyDef.type = b2_kinematicBody;
l_bodyDef.position.Set(position.x, position.y);
l_bodyDef.angle = orientation * DEG2RAD;
m_body = world->CreateBody(&l_bodyDef);
m_body->SetUserData(this); // used by our contact listener
l_fixtureDef.density = mk_fDensity;
l_fixtureDef.friction = mk_fFriction;
l_fixtureDef.restitution = 0.f;
l_fixtureDef.shape = &l_shape;
l_shape.SetAsBox(size.x * 0.5f, size.y * 0.5f);
b2Fixture* bodyFixture = m_body->CreateFixture(&l_fixtureDef);
bodyFixture->SetUserData((void *)PhysicalThing::KINEMATICBLOCK);
setPosition(position);
setSize(size);
setOrigin(size * 0.5f);
setRotation(orientation);
setOutlineThickness(0.f);
setTexture(texture);
setOutlineThickness(0.f);
setTextureRect(sf::IntRect(0, 0, size.x * 24, size.y * 32));
setScale(1, 1);
originPos = position;
m_body->SetLinearVelocity(b2Vec2(0.2f, 0.f));
}
void KinematicBlock::update()
{
if (m_body->GetPosition().x == originPos.x + 0.5)
{
m_body->SetLinearVelocity(b2Vec2(-0.2f, 0.f));
}
if (m_body->GetPosition().x == originPos.x - 0.5)
{
m_body->SetLinearVelocity(b2Vec2(0.2f, 0.f));
}
setPosition(sf::Vector2f(m_body->GetPosition().x, m_body->GetPosition().y));
}
KinematicBlock.h
#pragma once
/*!
\file kinematicBlock.h
*/
#include <Box2D/Box2D.h>
#include <SFML/Graphics.hpp>
#include "physicalThing.h"
/*! \class KinematicBlock
\brief A simple block which can move, rotate and collide with stuff ut is not affected by other dynamic bodies.
*/
class KinematicBlock : public sf::RectangleShape, public PhysicalThing
{
public:
KinematicBlock() {}; //!< Default Contructor
KinematicBlock(b2World * world, sf::Vector2f position, sf::Vector2f size, float orientation, sf::Texture *texture);
void update();//!< Update rendering infomation
sf::Vector2f originPos;
};
Thanks in advance.
I'm trying to get Box2D to work with SFML, although I'm having some issues like this:
For some reason ball just stops before hitting the platform. I tried varying the ball's position across x-axis and it falled out of the window,so I guess the drawing is off. Since SFML uses top-left origin for objects, but Box2D uses it's center I had to offset that in the draw functions for box and the ball:
void Ball::draw(sf::RenderWindow & window)
{
circleS.setPosition(sf::Vector2f(body->GetPosition().x - (circle.m_radius
/2),
body->GetPosition().y - (circle.m_radius 2)));
circleS.setRotation(body->GetAngle() * (180.0f / 3.14159f));
window.draw(circleS);
}
void Box::draw(sf::RenderWindow & window)
{
boxShape.setPosition(sf::Vector2f(body->GetPosition().x - (dimension.x / 2),
body->GetPosition().y - (dimension.y / 2)));
boxShape.setRotation(body->GetAngle() * (180.0f / 3.14159f));
window.draw(boxShape);
}
I don't understand why this isn't working,but if it's any help I can include more code. All help is appreciated.
Edit:
I replaced the ball with a box and it worked fine; then I changed up ball back and bottom box was changed to static ball and it just passed through,also here's a snippet of my main function:
RenderWindow window;
window.create(VideoMode(SCREENWIDTH, SCREENHEIGHT), "Pool game");
Event event;
//Setup for Box2D
b2Vec2 gravity(0.0f, 9.81f);
b2World world(gravity);
float32 timeStep = 1.0f / 60.0f;
int32 velocityIterations = 8;
int32 positionIterations = 3;
Box box(b2Vec2(100.0f, 400.0f), b2Vec2(100.0f, 20.0f), Color::Magenta,
world);
Ball ball(b2Vec2(50.0f, 50.0f), 9.0f, Color::Blue, world, true);
//game loop
while (window.isOpen()) {
//window closure
while (window.pollEvent(event)) {
if (event.type == Event::Closed) {
window.close();
}
}
world.Step(timeStep, velocityIterations, positionIterations);
//////// Drawing ////////
window.clear(Color(0,128,0));
//------------------------------------------
box.draw(window);
ball.draw(window);
//------------------------------------------
window.display();
//------------------------------------
}
Edit 2:
Here a header files for Ball and Box classes:
class Box {
//Constructors
public:
Box(b2Vec2 position, b2Vec2 dimension, sf::Color c, b2World &world, bool
isDynamic = false) : dimension(dimension) {
if (isDynamic) bodyDef.type = b2_dynamicBody; else bodyDef.type =
b2_staticBody;
bodyDef.position.Set(position.x , position.y );
body = world.CreateBody(&bodyDef);
dynamicBox.SetAsBox(dimension.x / 2, dimension.y / 2 );
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
body->CreateFixture(&fixtureDef);
boxShape.setPosition(sf::Vector2f(body->GetPosition().x - (this-
>dimension.x / 2), body->GetPosition().y - (this->dimension.y / 2)));
boxShape.setSize(sf::Vector2f(this->dimension.x, this->dimension.y));
boxShape.setFillColor(c);
}
~Box() {
body = nullptr;
}
//Functions
public:
void draw(sf::RenderWindow &window);
//Variables
public:
//Box2D
b2BodyDef bodyDef;
b2PolygonShape dynamicBox;
b2FixtureDef fixtureDef;
b2Body* body;
//SFML
sf::RectangleShape boxShape;
b2Vec2 dimension;
};
And also ball :
class Ball {
//Constructors
public:
Ball(b2Vec2 position,float radius, sf::Color c, b2World &world, bool
isDynamic = false) {
if (isDynamic) bodyDef.type = b2_dynamicBody; else bodyDef.type =
b2_staticBody;
bodyDef.position.Set(position.x , position.y );
body = world.CreateBody(&bodyDef);
circle.m_p.Set(position.x,position.y);
circle.m_radius = radius + outlineThickness;
fixtureDef.shape = &circle;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
body->CreateFixture(&fixtureDef);
circleS.setPosition(sf::Vector2f(body->GetPosition().x -
(circle.m_radius / 2),body->GetPosition().y - (circle.m_radius / 2)));
circleS.setRadius(radius);
circleS.setOutlineThickness(outlineThickness);
circleS.setOutlineColor(sf::Color::Black);
circleS.setFillColor(c);
}
~Ball() {
body = nullptr;
}
//Functions
public:
void draw(sf::RenderWindow &window);
//Variables
public:
//Box2D
b2BodyDef bodyDef;
b2CircleShape circle;
b2FixtureDef fixtureDef;
b2Body* body;
//SFML
sf::CircleShape circleS;
float outlineThickness = 1.0f;
};
Current implementation, zooms towards the center of View so items present in the top left corner or the current mouse pointer is not visible when we Zoom it.
I want zoom functionality based on the current mouse pointer so items present on the current mouse pointer zoom towards the center of the view.
Code for Zoom base don center of view area
void csGuiView::wheelEvent(QWheelEvent *event)
{
if ((event->modifiers()&Qt::ControlModifier) == Qt::ControlModifier
&& event->angleDelta().x() == 0)
{
QPoint pos = event->pos();
QPointF posf = this->mapToScene(pos);
double angle = event->angleDelta().y();
double scalingFactor;
if(angle > 0)
{
scalingFactor = 1 + ( angle / 360 * 0.1);
}else if (angle < 0)
{
scalingFactor = 1 - ( -angle / 360 * 0.1);
} else
{
scalingFactor = 1;
}
m_pvtData->m_scale = scalingFactor;
this->scale(scalingFactor, scalingFactor);
double w = this->viewport()->width();
double h = this->viewport()->height();
double wf = this->mapToScene(QPoint(w-1, 0)).x()
- this->mapToScene(QPoint(0,0)).x();
double hf = this->mapToScene(QPoint(0, h-1)).y()
- this->mapToScene(QPoint(0,0)).y();
double lf = posf.x() - pos.x() * wf / w;
double tf = posf.y() - pos.y() * hf / h;
/* try to set viewport properly */
this->ensureVisible(lf, tf, wf, hf, 0, 0);
QPointF newPos = this->mapToScene(pos);
this->ensureVisible(QRectF(QPointF(lf, tf) - newPos + posf,
QSizeF(wf, hf)), 0, 0);
}
if ((event->modifiers()&Qt::ControlModifier) != Qt::ControlModifier) {
QGraphicsView::wheelEvent(event);
}
event->accept();
}
To zoom always centered at mouse pointer – the position of mouse pointer just has to become the origin of scaling.
It sounds that simple but I struggled a bit to prepare a demonstration. (I'm not that good in linear algebra, sorry.) However, I finally got it running.
My sample code testQWidget-Zoom.cc:
#include <vector>
#include <QtWidgets>
// class for widget to demonstrate zooming
class Canvas: public QWidget {
// types:
private:
struct Geo {
QRectF rect; QColor color;
Geo(const QRectF &rect, const QColor &color):
rect(rect), color(color)
{ }
};
// variables:
private:
bool _initDone : 1; // flag: true ... sample geo created
std::vector<Geo> _scene; // contents to render
QMatrix _mat; // view matrix
// methods:
public:
// constructor.
Canvas(): QWidget(), _initDone(false) { }
// destructor.
virtual ~Canvas() = default;
// disabled:
Canvas(const Canvas&) = delete;
Canvas& operator=(const Canvas&) = delete;
private:
// initializes sample geo
void init()
{
if (_initDone) return;
_initDone = true;
// build scene (with NDC i.e. view x/y range: [-1, 1])
_scene.emplace_back(Geo(QRectF(-1.0f, -1.0f, 2.0f, 2.0f), QColor(0x000000u)));
_scene.emplace_back(Geo(QRectF(-0.2f, -0.2f, 0.4f, 0.4f), QColor(0x00ff00u)));
_scene.emplace_back(Geo(QRectF(-0.8f, -0.8f, 0.4f, 0.4f), QColor(0xff0000u)));
_scene.emplace_back(Geo(QRectF(-0.8f, 0.4f, 0.4f, 0.4f), QColor(0x0000ffu)));
_scene.emplace_back(Geo(QRectF(0.4f, 0.4f, 0.4f, 0.4f), QColor(0xff00ffu)));
_scene.emplace_back(Geo(QRectF(0.4f, -0.8f, 0.4f, 0.4f), QColor(0xffff00u)));
// get initial scaling
const int wView = width(), hView = height();
_mat.scale(wView / 2, hView / 2);
_mat.translate(1, 1);
}
protected:
virtual void paintEvent(QPaintEvent *pQEvent) override
{
init();
// render
QPainter qPainter(this);
#if 0 // This scales line width as well:
qPainter.setMatrix(_mat);
for (const Geo &geo : _scene) {
qPainter.setPen(geo.color);
qPainter.drawRect(geo.rect);
}
#else // This transforms only coordinates:
for (const Geo &geo : _scene) {
qPainter.setPen(geo.color);
QRectF rect(geo.rect.topLeft() * _mat, geo.rect.bottomRight() * _mat);
qPainter.drawRect(rect);
}
#endif // 0
}
virtual void wheelEvent(QWheelEvent *pQEvent) override
{
//qDebug() << "Wheel Event:"
//qDebug() << "mouse pos:" << pQEvent->pos();
// pos() -> virtual canvas
bool matInvOK = false;
QMatrix matInv = _mat.inverted(&matInvOK);
if (!matInvOK) {
qDebug() << "View matrix not invertible!";
return;
}
QPointF posNDC
= QPointF(pQEvent->pos().x(), pQEvent->pos().y()) * matInv;
//qDebug() << "mouse pos (NDC):" << posNDC;
float delta = 1.0f + pQEvent->angleDelta().y() / 1200.0f;
//qDebug() << "angleDelta:" << pQEvent->angleDelta().y();
//qDebug() << "scale factor:" << delta;
_mat.translate(posNDC.x(), posNDC.y()); // origin to spot
_mat.scale(delta, delta); // scale
_mat.translate(-posNDC.x(), -posNDC.y()); // spot to origin
update();
pQEvent->accept();
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Canvas canvas;
canvas.resize(512, 512);
canvas.show();
// runtime loop
return app.exec();
}
and these three lines are the actual interesting ones (in Canvas::wheelEvent()):
_mat.translate(posNDC.x(), posNDC.y()); // origin to spot
_mat.scale(delta, delta); // scale
_mat.translate(-posNDC.x(), -posNDC.y()); // spot to origin
And this is how it looks:
The first image is a snapshot of the application just after starting it.
Then I pointed into the center of the red rectangle and turned the wheel slightly. The red rectangle grew around the mouse pointer as intended.
1st Update:
And, this is the updated version which uses screen coordinates directly (instead of converting everything to NDCs):
#include <vector>
#include <QtWidgets>
// class for widget to demonstrate zooming
class Canvas: public QWidget {
// types:
private:
struct Geo {
QRectF rect; QColor color;
Geo(const QRectF &rect, const QColor &color):
rect(rect), color(color)
{ }
};
// variables:
private:
bool _initDone : 1; // flag: true ... sample geo created
std::vector<Geo> _scene; // contents to render
QMatrix _mat; // view matrix
// methods:
public:
// constructor.
Canvas(): QWidget(), _initDone(false) { }
// destructor.
virtual ~Canvas() = default;
// disabled:
Canvas(const Canvas&) = delete;
Canvas& operator=(const Canvas&) = delete;
private:
// initializes sample geo
void init()
{
if (_initDone) return;
_initDone = true;
const int wView = width(), hView = height();
// build scene (with NDC i.e. view x/y range: [-1, 1])
_scene.emplace_back(Geo(QRectF(-1.0f, -1.0f, 2.0f, 2.0f), QColor(0x000000u)));
_scene.emplace_back(Geo(QRectF(-0.2f, -0.2f, 0.4f, 0.4f), QColor(0x00ff00u)));
_scene.emplace_back(Geo(QRectF(-0.8f, -0.8f, 0.4f, 0.4f), QColor(0xff0000u)));
_scene.emplace_back(Geo(QRectF(-0.8f, 0.4f, 0.4f, 0.4f), QColor(0x0000ffu)));
_scene.emplace_back(Geo(QRectF(0.4f, 0.4f, 0.4f, 0.4f), QColor(0xff00ffu)));
_scene.emplace_back(Geo(QRectF(0.4f, -0.8f, 0.4f, 0.4f), QColor(0xffff00u)));
// scale geometry to screen coordinates
QMatrix mat;
mat.scale(wView / 2, hView / 2);
mat.translate(1, 1);
for (Geo &geo : _scene) {
geo.rect = QRectF(geo.rect.topLeft() * mat, geo.rect.bottomRight() * mat);
}
}
protected:
virtual void paintEvent(QPaintEvent *pQEvent) override
{
init();
// render
QPainter qPainter(this);
qPainter.setMatrix(_mat);
for (const Geo &geo : _scene) {
qPainter.setPen(geo.color);
qPainter.drawRect(geo.rect);
}
}
virtual void wheelEvent(QWheelEvent *pQEvent) override
{
//qDebug() << "Wheel Event:";
//qDebug() << "mouse pos:" << pQEvent->pos();
float delta = 1.0f + pQEvent->angleDelta().y() / 1200.0f;
//qDebug() << "angleDelta:" << pQEvent->angleDelta().y();
//qDebug() << "scale factor:" << delta;
_mat.translate(pQEvent->pos().x(), pQEvent->pos().y()); // origin to spot
_mat.scale(delta, delta); // scale
_mat.translate(-pQEvent->pos().x(), -pQEvent->pos().y()); // spot to origin
update();
pQEvent->accept();
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Canvas canvas;
canvas.resize(256, 256);
canvas.show();
// runtime loop
return app.exec();
}
The relevant three lines didn't change much – the mouse coordinates are applied directly to transformation.
Btw. I changed the rendering – it now scales line width as well as I used
qPainter.setMatrix(_mat);
in Canvas::paintEvent() instead of transforming all points "manually".
The snapshot shows the application after I pointed into the center of the blue rectangle and turned the mouse wheel:
2nd Update:
The suggested matrix manipulation works in a QGraphicsView as well:
#include <QtWidgets>
// class for widget to demonstrate zooming
class Canvas: public QGraphicsView {
// methods:
public:
// constructor.
Canvas() = default;
// destructor.
virtual ~Canvas() = default;
// disabled:
Canvas(const Canvas&) = delete;
Canvas& operator=(const Canvas&) = delete;
protected:
virtual void wheelEvent(QWheelEvent *pQEvent) override
{
//qDebug() << "Wheel Event:";
// pos() -> virtual canvas
QPointF pos = mapToScene(pQEvent->pos());
//qDebug() << "mouse pos:" << pos;
// scale from wheel angle
float delta = 1.0f + pQEvent->angleDelta().y() / 1200.0f;
//qDebug() << "angleDelta:" << pQEvent->angleDelta().y();
//qDebug() << "scale factor:" << delta;
// modify transform matrix
QTransform xform = transform();
xform.translate(pos.x(), pos.y()); // origin to spot
xform.scale(delta, delta); // scale
xform.translate(-pos.x(), -pos.y()); // spot to origin
setTransform(xform);
//qDebug() << "transform:" << xform;
// force update
update();
pQEvent->accept();
}
};
QRectF toScr(QWidget *pQWidget, float x, float y, float w, float h)
{
const int wView = pQWidget->width(), hView = pQWidget->height();
const int s = wView < hView ? wView : hView;
return QRectF(
(0.5f * x + 0.5f) * s, (0.5f * y + 0.5f) * s,
0.5f * w * s, 0.5f * h * s);
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
// setup GUI
Canvas canvas;
canvas.setTransformationAnchor(QGraphicsView::NoAnchor);
canvas.resize(256, 256);
canvas.show();
// prepare scene
QGraphicsScene qGScene;
qGScene.addRect(toScr(canvas.viewport(), -1.0f, -1.0f, 2.0f, 2.0f), QColor(0x000000u));
qGScene.addRect(toScr(canvas.viewport(), -0.2f, -0.2f, 0.4f, 0.4f), QColor(0x00ff00u));
qGScene.addRect(toScr(canvas.viewport(), -0.8f, -0.8f, 0.4f, 0.4f), QColor(0xff0000u));
qGScene.addRect(toScr(canvas.viewport(), -0.8f, 0.4f, 0.4f, 0.4f), QColor(0x0000ffu));
qGScene.addRect(toScr(canvas.viewport(), 0.4f, 0.4f, 0.4f, 0.4f), QColor(0xff00ffu));
qGScene.addRect(toScr(canvas.viewport(), 0.4f, -0.8f, 0.4f, 0.4f), QColor(0xffff00u));
canvas.setScene(&qGScene);
// runtime loop
return app.exec();
}
Using a QGraphicsView simplifies code as no rendering code is needed – it's already built-in.
As I have not (yet) much experience with QGraphicsView, another issue hit me quite hard: The QGraphicsView is able to fix the view position automati[cg]ally after a transformation has been applied. In my case, this was rather counter-productive as obviously my transformation and the QGraphicsView seemed to "pull" in opposite directions.
Hence, I've learnt my lesson of the day: QGrapicsView::setTransformationAnchor(QGraphicsView::NoAnchor)
is necessary to switch off this (in my case not-intended) auto-centering.
The other detail I find worth to notice is QGraphicsView::mapToScene() which can be used to conveniently convert widget coordinates (e.g. mouse coordinates) to scene space.
When I compile and run application, for example this source code (from tutorial, I had to fix this code, ex. big letters in names of methods):
#include <SFML\Graphics.hpp>
#include <Box2D\Box2D.h>
/** We need this to easily convert between pixel and real-world coordinates*/
static const float SCALE = 30.f;
/** Create the base for the boxes to land */
void CreateGround(b2World& World, float X, float Y);
/** Create the boxes */
void CreateBox(b2World& World, int MouseX, int MouseY);
int main()
{
/** Prepare the window */
sf::RenderWindow Window(sf::VideoMode(800, 600, 32), "Test");
Window.SetFramerateLimit(60);
/** Prepare the world */
b2Vec2 Gravity(0.f, 9.8f);
b2World World(Gravity);
CreateGround(World, 400.f, 500.f);
/** Prepare textures */
sf::Texture GroundTexture;
sf::Texture BoxTexture;
GroundTexture.LoadFromFile("ground.png");
BoxTexture.LoadFromFile("box.png");
while (Window.IsOpened())
{
if (sf::Mouse::IsButtonPressed(sf::Mouse::Left))
{
int MouseX = sf::Mouse::GetPosition(Window).x;
int MouseY = sf::Mouse::GetPosition(Window).y;
CreateBox(World, MouseX, MouseY);
}
World.Step(1/60.f, 8, 3);
Window.Clear(sf::Color::White);
int BodyCount = 0;
for (b2Body* BodyIterator = World.GetBodyList(); BodyIterator != 0; BodyIterator = BodyIterator->GetNext())
{
if (BodyIterator->GetType() == b2_dynamicBody)
{
sf::Sprite Sprite;
Sprite.SetTexture(BoxTexture);
Sprite.SetOrigin(16.f, 16.f);
Sprite.SetPosition(SCALE * BodyIterator->GetPosition().x, SCALE * BodyIterator->GetPosition().y);
Sprite.SetRotation(BodyIterator->GetAngle() * 180/b2_pi);
Window.Draw(Sprite);
++BodyCount;
}
else
{
sf::Sprite GroundSprite;
GroundSprite.SetTexture(GroundTexture);
GroundSprite.SetOrigin(400.f, 8.f);
GroundSprite.SetPosition(BodyIterator->GetPosition().x * SCALE, BodyIterator->GetPosition().y * SCALE);
GroundSprite.SetRotation(180/b2_pi * BodyIterator->GetAngle());
Window.Draw(GroundSprite);
}
}
Window.Display();
}
return 0;
}
void CreateBox(b2World& World, int MouseX, int MouseY)
{
b2BodyDef BodyDef;
BodyDef.position = b2Vec2(MouseX/SCALE, MouseY/SCALE);
BodyDef.type = b2_dynamicBody;
b2Body* Body = World.CreateBody(&BodyDef);
b2PolygonShape Shape;
Shape.SetAsBox((32.f/2)/SCALE, (32.f/2)/SCALE);
b2FixtureDef FixtureDef;
FixtureDef.density = 1.f;
FixtureDef.friction = 0.7f;
FixtureDef.shape = &Shape;
Body->CreateFixture(&FixtureDef);
}
void CreateGround(b2World& World, float X, float Y)
{
b2BodyDef BodyDef;
BodyDef.position = b2Vec2(X/SCALE, Y/SCALE);
BodyDef.type = b2_staticBody;
b2Body* Body = World.CreateBody(&BodyDef);
b2PolygonShape Shape;
Shape.SetAsBox((800.f/2)/SCALE, (16.f/2)/SCALE);
b2FixtureDef FixtureDef;
FixtureDef.density = 0.f;
FixtureDef.shape = &Shape;
Body->CreateFixture(&FixtureDef);
}
My application is "not responding" after few seconds. The window turns to grey but, everything works! Objects are moving and all interactions are working. What is the problem?
I'm trying to port my height map visualization program written on c++, from SFML to Qt, so it can be shown on widget and controlled by the GUI elements.
The problem is that when I start an application, a camera starts to roll around its center very fast(actually, it looks like a terrain mesh flying around a camera, like an Earth around the Sun :), without any actions from my side(e.g moving mouse, pressing buttons).
Camera should move forward, back, left, right when I press w,a,s,d and look around when I move the mouse(Just typical FPS camera behavior).
I think that problem are in the program's main loop, because it's no standard while(true){ //do something// } approach in qt, and it's a little confusing.
Here's my code:
OGLWidget class(here I'm drawing stuff. Problem somewhere here I think) :
class OGLWidget :
public QGLWidget
{
Q_OBJECT
public:
OGLWidget(QWidget *parent = 0);
~OGLWidget(void);
public:
void paintGL();
void initializeGL();
void resizeGL();
public:
void updateCamera();
public slots:
void mainLoop();
protected:
void keyPressEvent(QKeyEvent *e);
void keyReleaseEvent(QKeyEvent *e);
private:
Terrain _terrain;
Camera _camera;
private:
int _keyPressed;
QTimer _timer;
QElapsedTimer _elapsedTimer;
float _simulationTime;
float _fps;
};
OGLWidget::OGLWidget(QWidget *parent) : QGLWidget(parent)
{
_terrain.loadHeightMap("normalHeightMap256_2.png");
_camera.setScreenDimension(this->width(), this->height());
//setting vertical sync
QGLFormat frmt;
frmt.setSwapInterval(1);
setFormat(frmt);
setMouseTracking(true);
setFocus();
_simulationTime = 0;
_fps = 1.f / 60.f;
connect(&_timer, SIGNAL(timeout()), this, SLOT(mainLoop()));
_timer.start();
_elapsedTimer.start();
}
OGLWidget::~OGLWidget(void)
{
}
void OGLWidget::mainLoop()
{
_simulationTime += _elapsedTimer.elapsed();
_elapsedTimer.restart();
while(_simulationTime > _fps)
{
_simulationTime -= _fps;
updateCamera();
}
updateGL();
}
void OGLWidget::updateCamera()
{
QPoint p = mapFromGlobal(QCursor::pos());
_camera.computeMatrices(p.x(), p.y(), _fps, _keyPressed);
glm::mat4 ViewMatrix = _camera.getViewMatrix();
glm::mat4 ProjectionMatrix = _camera.getProjectionMatrix();
glm::mat4 ModelMatrix = glm::mat4(1.0);
_terrain.setMvp(ProjectionMatrix * ViewMatrix * ModelMatrix);
QPoint center = mapToGlobal(QPoint(this->width() / 2, this->height() / 2));
QCursor::setPos(center);
}
void OGLWidget::initializeGL()
{
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
return;
}
glViewport(0, 0, this->width(), this->height());
_terrain.init();
}
void OGLWidget::paintGL()
{
_terrain.draw();
}
void OGLWidget::resizeGL()
{
glViewport(0, 0, this->width(), this->height());
}
void OGLWidget::keyPressEvent(QKeyEvent *e)
{
switch(e->key())
{
case Qt::Key::Key_Escape:
exit(0);
break;
case Qt::Key::Key_W:
_keyPressed = Key::KEY_PRESSED_UP;
break;
case Qt::Key::Key_S:
_keyPressed = Key::KEY_PRESSED_DOWN;
break;
case Qt::Key::Key_A:
_keyPressed = Key::KEY_PRESSED_LEFT;
break;
case Qt::Key::Key_D:
_keyPressed = Key::KEY_PRESSED_RIGHT;
break;
}
}
void OGLWidget::keyReleaseEvent(QKeyEvent *e)
{
if(e->key() == Qt::Key::Key_W ||
e->key() == Qt::Key::Key_S ||
e->key() == Qt::Key::Key_A ||
e->key() == Qt::Key::Key_D)
_keyPressed = KEY_RELEASED;
}
I'm absolutely sure that Terrain and Camera classes are working correct, because I haven't changed code since my SFML project(Except of using QImage instead of sf::Image, but it's working correct too)
*Camera main algorithm: *
void Camera::computeMatrices(int mouseXpos, int mouseYpos, float deltaTime, int keyPressed)
{
_horizontalAngle += _mouseSpeed * deltaTime * float(_screenWidth / 2 - mouseXpos);
_verticalAngle += _mouseSpeed * deltaTime * float(_screenHeight / 2 - mouseYpos);
_direction = glm::vec3
(
cos(_verticalAngle) * sin(_horizontalAngle),
sin(_verticalAngle),
cos(_verticalAngle) * cos(_horizontalAngle)
);
glm::vec3 right = glm::vec3
(
sin(_horizontalAngle - 3.14f/2.0f),
0,
cos(_horizontalAngle - 3.14f/2.0f)
);
glm::vec3 up = glm::cross( right, _direction );
switch(keyPressed)
{
case Key::KEY_PRESSED_UP:
_position += _direction * deltaTime * _speed;
break;
case Key::KEY_PRESSED_DOWN:
_position -= _direction * deltaTime * _speed;
break;
case Key::KEY_PRESSED_LEFT:
_position -= right * deltaTime * _speed;
break;
case Key::KEY_PRESSED_RIGHT:
_position += right * deltaTime * _speed;
break;
case Key::KEY_RELEASED:
break;
}
_projectionMatrix = glm::perspective(_initialFoV, 4.0f / 3.0f, 0.1f, 1000.0f);
_viewMatrix = glm::lookAt
(
_position, // Camera is here
_position+_direction, // and looks here : at the same position, plus "direction"
up // Head is up (set to 0,-1,0 to look upside-down)
);
}
Help me fix this issue.
Ok, I figured out the problem with spinning camera. The cause of it was that I hardcoded an aspect ratio in Camera::computeMatrices, and used a resolution of my widget which doesn't match to it:
_projectionMatrix = glm::perspective
(
_initialFoV,
4.0f / 3.0f, //here it is
0.1f,
1000.0f
);
I changed 4.0f / 3.0f on (float)_screenWidth / (float)_screenHeight but it didn't help too.
So then I just changed a resolution of my widget to 800 x 600 and it helped.
The new problem is that it works only on 4/3 dimensions(e.g 800x600, 1024x768).
The best way to correct
_direction = glm::vec3
(
cos(_verticalAngle) * sin(_horizontalAngle),
sin(_verticalAngle),
cos(_verticalAngle) * cos(_horizontalAngle)
);
_direction.normalize();
...