I want to create a very basic physics engine in SFML. I also want to be able to control objects using Dear ImGui. So I created this basic foundation. My main problem with it is how I handle rendering the GUI, because I couldn't find another way to implement it, either just drawing an object, or drawing object and a GUI. Mainly because the draw function must be const
.
This is supposed to be able to handle both objects that have a GUI (IEntityWithGui
) and those that don't (IEntity
). But in this example there's just objects with a GUI.
main.cpp:
#include <iostream>
#include <SFML/Graphics.hpp>
#include "imgui/imgui.h"
#include "imgui/imgui-SFML.h"
#include "updater.h"
#include "renderer.h"
#include "shapeObj.h"
int main() {
sf::RenderWindow window(sf::VideoMode(200, 200), "PhysicsBox");
ImGui::SFML::Init(window);
sf::Clock clock;
std::atomic<bool> isRunning{ true };
ShapeObj shape{window};
Renderer render{ &window,isRunning };
render.queueSubmit(&shape);
Updater update{ &window,isRunning,std::ref(clock) };
update.queueSubmit(&shape);
window.setActive(false);
window.setKeyRepeatEnabled(false);
update.startThread();
render.startThread();
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
ImGui::SFML::ProcessEvent(event);
if (event.type == sf::Event::Closed) {
isRunning.store(false);
render.joinThread();
update.joinThread();
window.setActive(true);
window.close();
}
if (event.type == sf::Event::Resized)
{
// update the view to the new size of the window
sf::FloatRect visibleArea(0.f, 0.f, event.size.width, event.size.height);
window.setView(sf::View(visibleArea));
}
shape.eventProcess(event, clock.restart());
}
}
ImGui::SFML::Shutdown();
return 0;
}
IEntity.h:
#pragma once
#include <SFML/Graphics.hpp>
class IEntity : public sf::Drawable, public sf::Transformable
{
public:
virtual void update(sf::Time dt)=0;
virtual void eventProcess(sf::Event event,sf::Time dt)=0;
virtual ~IEntity(){}
};
IEntityWithGui:
#pragma once
#include "IEntity.h"
class IEntityWithGui : public IEntity
{
public:
virtual void processGui()=0;
virtual ~IEntityWithGui() {}
};
shapeObj.hpp:
#pragma once
#include "IEntityWithGui.h"
class ShapeObj : public IEntityWithGui
{
public:
ShapeObj(sf::RenderWindow &window);
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
virtual void update(sf::Time deltaTime) override ;
virtual void processGui() override;
virtual void eventProcess(sf::Event event, sf::Time time) override;
virtual ~ShapeObj()=default;
private:
sf::CircleShape m_shape{ 100.f,75 };
sf::RenderWindow& m_window;
bool m_show_gui = false;
bool m_mouse_captured = false;
};
ShapeObj.cpp:
#include "shapeObj.h"
#include "imgui/imgui.h"
#include "imgui/imgui-SFML.h"
ShapeObj::ShapeObj(sf::RenderWindow& window)
: m_window(window) {};
void ShapeObj::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
states.transform *= getTransform();
target.draw(m_shape, states);
}
void ShapeObj::update(sf::Time deltaTime)
{
//It will be used, just not now.
}
void ShapeObj::processGui()
{
if (!m_show_gui) return;
ImGuiIO io = ImGui::GetIO();
ImGui::SFML::Update(m_window, sf::seconds(1.f / 60.f));
ImGui::Begin("Grid");
if (io.WantCaptureMouse) {
m_mouse_captured = true;
}
else {
m_mouse_captured = false;
}
ImGui::Text("Mouse captured=%d\n",
m_mouse_captured);
ImGui::End();
ImGui::SFML::Render(m_window);
}
void ShapeObj::eventProcess(sf::Event event, sf::Time time)
{
auto mousePos= m_window.mapPixelToCoords(sf::Mouse::getPosition(m_window));
if (!m_mouse_captured) {
if(event.type==sf::Event::MouseButtonPressed &&
m_shape.getGlobalBounds().contains(mousePos))
{
m_show_gui = !m_show_gui;
}
//Whatever other events
}
}
threadWithQueue.h:
#pragma once
#pragma once
#include <SFML/Graphics.hpp>
#include <vector>
#include <thread>
template <class T>
class ThreadWithQueue {
public:
ThreadWithQueue<T>(sf::RenderWindow* window, std::atomic<bool>& isRunning);
void startThread();
void joinThread();
virtual void queueSubmit(T object);
protected:
virtual void queueThread()=0;
std::atomic<bool>& isRunning;
sf::RenderWindow* m_window;
std::thread mThreadObj;
std::vector<T> m_queue;
};
template <class T>
ThreadWithQueue<T>::ThreadWithQueue(sf::RenderWindow* window, std::atomic<bool>& isRunning)
: m_window{ window }, isRunning{ isRunning } {}
template <class T>
void ThreadWithQueue<T>::startThread() {
mThreadObj = std::thread{ &ThreadWithQueue::queueThread,this };
}
template <class T>
void ThreadWithQueue<T>::joinThread() {
if (mThreadObj.joinable()) {
mThreadObj.join();
}
}
template <class T>
void ThreadWithQueue<T>::queueSubmit(T object) {
m_queue.push_back(object);
}
updater.h:
#pragma once
#include "threadWithQueue.h"
#include "IEntity.h"
class Updater : public ThreadWithQueue<IEntity*> {
public:
Updater(sf::RenderWindow* window, std::atomic<bool>& isRunning, sf::Clock& global_clock);
~Updater() = default;
private:
virtual void queueThread();
sf::Clock& m_global_clock;
};
updater.cpp
#include "updater.h"
Updater::Updater(sf::RenderWindow* window, std::atomic<bool>& isRunning, sf::Clock& global_clock)
: ThreadWithQueue<IEntity*>(window, isRunning), m_global_clock(global_clock) {}
void Updater::queueThread() {
while (isRunning.load()) {
for (auto& i : m_queue) {
i->update(m_global_clock.restart());
}
}
}
renderer.h:
#include "updater.h"
Updater::Updater(sf::RenderWindow* window, std::atomic<bool>& isRunning, sf::Clock& global_clock)
: ThreadWithQueue<IEntity*>(window, isRunning), m_global_clock(global_clock) {}
void Updater::queueThread() {
while (isRunning.load()) {
for (auto& i : m_queue) {
i->update(m_global_clock.restart());
}
}
}
renderer.cpp:
#include <SFML/Graphics.hpp>
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>
#include "renderer.h"
#include "imgui/imgui.h"
#include "imgui/imgui-SFML.h"
Renderer::Renderer(sf::RenderWindow* window, std::atomic<bool>& isRunning)
: ThreadWithQueue(window,isRunning) {}
void Renderer::queueThread() {
m_window->setActive(true);
while (isRunning.load()) {
m_window->clear();
for (auto& i: m_queue) {
m_window->draw(*i);
}
for (auto& i : m_queue_gui) {
m_window->draw(*i);
i->processGui();
}
m_window->display();
}
}
void Renderer::queueSubmit(IEntityWithGui* object)
{
m_queue_gui.push_back(object);
}