Compare commits

...

4 Commits

  1. 2
      CMakeLists.txt
  2. 9
      shaders/fragment.glsl
  3. 6
      shaders/shaders.qrc
  4. 13
      shaders/vertex.glsl
  5. 140
      src/GLWidget.cpp
  6. 27
      src/GLWidget.h
  7. 7
      src/MainWindow.cpp
  8. 5
      src/Pendulum.h
  9. 33
      src/Simulation.cpp
  10. 9
      src/Simulation.h

@ -10,6 +10,6 @@ find_package(Qt6 COMPONENTS OpenGLWidgets REQUIRED)
find_package(OpenMP REQUIRED) find_package(OpenMP REQUIRED)
file(GLOB_RECURSE SOURCE_FILES src/**.cpp) file(GLOB_RECURSE SOURCE_FILES src/**.cpp)
add_executable(Pendulum WIN32 ${SOURCE_FILES} icons/icons.qrc) add_executable(Pendulum WIN32 ${SOURCE_FILES} icons/icons.qrc shaders/shaders.qrc)
target_link_libraries(Pendulum Qt6::OpenGLWidgets OpenMP::OpenMP_CXX) target_link_libraries(Pendulum Qt6::OpenGLWidgets OpenMP::OpenMP_CXX)

@ -0,0 +1,9 @@
#version 330 core
out vec4 FragColor;
in vec3 color;
void main() {
FragColor = vec4(color, 1);
}

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/shaders/">
<file>vertex.glsl</file>
<file>fragment.glsl</file>
</qresource>
</RCC>

@ -0,0 +1,13 @@
#version 330 core
layout (location = 0) in vec2 vPos;
layout (location = 1) in vec3 vColor;
uniform mat2 VP;
out vec3 color;
void main() {
gl_Position = vec4(VP * vPos, 0.0, 1.0);
color = vColor;
}

@ -7,29 +7,59 @@
#include <QApplication> #include <QApplication>
#include <QDialog> #include <QDialog>
#include "FPS.h" #include "FPS.h"
#include <QOpenGLShaderProgram>
GLWidget::GLWidget(Simulation * simulation) : simulation(simulation) { GLWidget::GLWidget(Simulation * simulation) : simulation(simulation) {
startTimer(1000 / 144); startTimer(1000 / 144);
fps = new FPS; fps = new FPS;
fps->setUpdateInterval(500); fps->setUpdateInterval(500);
connect(simulation, &Simulation::layoutChanged, this, &GLWidget::initGPUMemory);
connect(simulation, &Simulation::positionChanged, this, &GLWidget::changePosition);
} }
void GLWidget::paintEvent(QPaintEvent *e) { void GLWidget::initializeGL() {
fps->newFrame(); initializeOpenGLFunctions();
QString fpsString = "FPS: " + QString::fromStdString(std::to_string(fps->current));
program = new QOpenGLShaderProgram;
program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertex.glsl");
program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragment.glsl");
program->link();
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &positionVBO);
glGenBuffers(1, &colorVBO);
glGenBuffers(1, &EBO);
}
void GLWidget::paintGL() {
auto p = new QPainter(this); auto p = new QPainter(this);
p->setRenderHint(QPainter::Antialiasing); p->setRenderHint(QPainter::Antialiasing);
p->fillRect(e->rect(), QColor(30, 30, 30)); p->beginNativePainting();
glEnable(GL_PRIMITIVE_RESTART);
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glClearColor(.15, .15, .15, 1);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, QWidget::width(), QWidget::height());
program->bind();
program->setUniformValue("VP", VP);
glBindVertexArray(VAO);
glDrawElements(GL_LINE_STRIP, indexCount, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
p->endNativePainting();
p->setPen(Qt::white); p->setPen(Qt::white);
auto font = p->font(); auto font = p->font();
font.setPixelSize(20); font.setPixelSize(20);
p->setFont(font); p->setFont(font);
p->drawText(0, 0, 400, 400, Qt::AlignTop | Qt::AlignLeft, fpsString);
p->translate(e->rect().center());
simulation->draw(p, std::min(width(), height())); fps->newFrame();
QString fpsString = "FPS: " + QString::fromStdString(std::to_string(fps->current));
p->drawText(0, 0, 100, 100, Qt::AlignTop | Qt::AlignLeft, fpsString);
p->end(); p->end();
} }
@ -46,4 +76,100 @@ bool GLWidget::AnyDialogOpen() {
return false; return false;
} }
void GLWidget::initGPUMemory(const std::vector<Pendulum *> *pendula) {
std::vector<float> positions;
std::vector<float> colors;
std::vector<float> massRadii;
std::vector<GLuint> indices;
GLuint index = 0;
for (const auto p : *pendula){
// Origin point
positions.push_back(0);
positions.push_back(0);
colors.resize(colors.size() + 3);
float * red = &colors.back() - 2;
p->color.getRgbF(red, red + 1, red + 2);
massRadii.push_back(0);
indices.push_back(index++);
// All other points
for (int segment = 0; segment < p->M.size(); segment++){
Vector pos = p->X[segment];
positions.push_back(float(pos.x));
positions.push_back(float(pos.y));
colors.resize(colors.size() + 3);
red = &colors.back() - 2;
p->color.getRgbF(red, red + 1, red + 2);
massRadii.push_back(float(p->M[segment]));
indices.push_back(index++);
}
// Primitive Restart
indices.push_back(0xFFFFFFFF);
}
indexCount = GLsizei(indices.size());
positionCount = GLsizei(positions.size());
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
glBufferData(GL_ARRAY_BUFFER, GLsizeiptr(positions.size() * sizeof(float)), positions.data(), GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
glBufferData(GL_ARRAY_BUFFER, GLsizeiptr(colors.size() * sizeof(float)), colors.data(), GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, GLsizeiptr(indices.size() * sizeof(GLuint)), indices.data(), GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
simulation->pendulaMutex.unlock();
}
void GLWidget::changePosition(const std::vector<Pendulum *> *pendula) {
glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
auto positions = (GLfloat*) glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(positionCount * sizeof(float)), GL_MAP_WRITE_BIT);
if (positions){
size_t index = 0;
for (const auto p : *pendula){
index += 2;
for (auto x : p->X){
positions[index++] = float(x.x);
positions[index++] = float(x.y);
}
}
}
simulation->pendulaMutex.unlock();
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void GLWidget::resizeGL(int w, int h) {
float m = std::min(float(w), float(h));
float scale = 1 / (float(simulation->size) / 2) * 0.9f;
VP.setToIdentity();
VP(0, 0) = 1 / float(w);
VP(1, 1) = -1 / float(h);
VP *= m * scale;
}

@ -1,18 +1,35 @@
#include <QOpenGLWidget> #pragma once
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include <QOpenGLExtraFunctions>
#include <QMatrix2x2>
class Pendulum;
class Simulation; class Simulation;
class FPS; class FPS;
class QOpenGLShaderProgram;
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions { class GLWidget : public QOpenGLWidget, protected QOpenGLExtraFunctions {
public: public:
explicit GLWidget(Simulation *); explicit GLWidget(Simulation *);
protected: protected:
void paintEvent(QPaintEvent* e) override;
void timerEvent(QTimerEvent* e) override; void timerEvent(QTimerEvent* e) override;
void initializeGL() override;
void paintGL() override;
void resizeGL(int w, int h) override;
private slots:
void initGPUMemory(const std::vector<Pendulum *> *pendula);
void changePosition(const std::vector<Pendulum *> *pendula);
private: private:
QOpenGLShaderProgram * program;
GLuint VAO;
GLuint positionVBO;
GLuint colorVBO;
GLuint EBO;
GLsizei indexCount = 0;
GLsizei positionCount = 0;
QMatrix2x2 VP;
Simulation * simulation; Simulation * simulation;
FPS * fps; FPS * fps;
static bool AnyDialogOpen(); static bool AnyDialogOpen();

@ -30,6 +30,9 @@ MainWindow::MainWindow() {
resetLengths(); resetLengths();
buildUI(); buildUI();
normalizeLengths();
add();
} }
void MainWindow::buildUI() { void MainWindow::buildUI() {
@ -330,7 +333,7 @@ QWidget * MainWindow::buildSimulationUI() {
timescaleSlider->setMinimum(1); timescaleSlider->setMinimum(1);
timescaleSlider->setMaximum(500); timescaleSlider->setMaximum(500);
substepsSlider->setMinimum(1); substepsSlider->setMinimum(1);
substepsSlider->setMaximum(1000); substepsSlider->setMaximum(100);
resetSimulationControl(); resetSimulationControl();
@ -430,7 +433,7 @@ void MainWindow::add() {
void MainWindow::resetSimulationControl() { void MainWindow::resetSimulationControl() {
gravitySlider->setValue(981); gravitySlider->setValue(981);
timescaleSlider->setValue(100); timescaleSlider->setValue(100);
substepsSlider->setValue(30); substepsSlider->setValue(5);
} }
void MainWindow::toggleSimulation() { void MainWindow::toggleSimulation() {

@ -1,7 +1,10 @@
#pragma once
#include <vector> #include <vector>
#include <QVector2D> #include <QVector2D>
#include <QColor> #include <QColor>
#include "Vector.h" #include "Vector.h"
#include "GLWidget.h"
class QPainter; class QPainter;
@ -13,8 +16,8 @@ public:
void draw(QPainter*, double) const; void draw(QPainter*, double) const;
void update(double, double); void update(double, double);
private: private:
friend class GLWidget;
std::vector<Vector> X, V; std::vector<Vector> X, V;
std::vector<double> M, L; std::vector<double> M, L;
QColor color; QColor color;

@ -1,5 +1,4 @@
#include "Simulation.h" #include "Simulation.h"
#include "Pendulum.h"
#include <QPainter> #include <QPainter>
#include <QTimer> #include <QTimer>
#include <QThread> #include <QThread>
@ -14,16 +13,10 @@ Simulation::Simulation() {
lastUpdate = system_clock::now(); lastUpdate = system_clock::now();
}; };
void Simulation::draw(QPainter *p, int screenSize) {
double scale = screenSize * 0.95 / size;
for (const auto pendulum : pendula)
pendulum->draw(p, scale);
}
void Simulation::update() { void Simulation::update() {
auto thisUpdate = system_clock::now(); auto thisUpdate = system_clock::now();
auto ms = (int)duration_cast<milliseconds>(thisUpdate - lastUpdate).count(); auto ms = (int)duration_cast<milliseconds>(thisUpdate - lastUpdate).count();
std::cout << ms << std::endl;
lastUpdate = thisUpdate; lastUpdate = thisUpdate;
if (!isPlaying) if (!isPlaying)
@ -33,23 +26,37 @@ void Simulation::update() {
h /= substeps; h /= substeps;
pendulaMutex.lock();
#pragma omp parallel for #pragma omp parallel for
for (int i = 0; i < pendula.size(); i++) // NOLINT(*-loop-convert) // no ranged based for msvc for (int i = 0; i < pendula.size(); i++){ // NOLINT(*-loop-convert) // not ranged based for msvc
for (int k = 0; k < substeps; k++) for (int k = 0; k < substeps; k++)
pendula[i]->update(h, gravity); pendula[i]->update(h, gravity);
} }
emit positionChanged(&pendula);
}
void Simulation::clearPendula() { void Simulation::clearPendula() {
auto deleteLater = pendula; pendulaMutex.lock();
for (Pendulum* &p : pendula){
delete p;
p = nullptr;
}
pendula.clear(); pendula.clear();
pendula.shrink_to_fit(); pendula.shrink_to_fit();
QThread::usleep(500000);
for (auto p : deleteLater) emit layoutChanged(&pendula);
delete p;
} }
void Simulation::addPendula(const std::vector<Pendulum *> &add) { void Simulation::addPendula(const std::vector<Pendulum *> &add) {
pendulaMutex.lock();
for (auto p : add) for (auto p : add)
pendula.push_back(p); pendula.push_back(p);
emit layoutChanged(&pendula);
} }

@ -2,14 +2,16 @@
#include <cstdint> #include <cstdint>
#include <QObject> #include <QObject>
#include <chrono> #include <chrono>
#include <mutex>
#include "Pendulum.h"
using namespace std::chrono; using namespace std::chrono;
class Pendulum;
class QPainter; class QPainter;
class QTimer; class QTimer;
class Simulation : public QObject { class Simulation : public QObject {
Q_OBJECT
public: public:
explicit Simulation(); explicit Simulation();
@ -21,7 +23,10 @@ public:
bool isPlaying = false; bool isPlaying = false;
void draw(QPainter*, int); std::mutex pendulaMutex;
signals:
void layoutChanged(const std::vector<Pendulum *> *newPendula);
void positionChanged(const std::vector<Pendulum *> *changedPendula);
public slots: public slots:
void clearPendula(); void clearPendula();
void addPendula(const std::vector<Pendulum *> &add); void addPendula(const std::vector<Pendulum *> &add);

Loading…
Cancel
Save