aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorccolin2020-12-22 13:15:23 +0100
committerccolin2020-12-22 13:15:23 +0100
commit7fbe0814d52ba861a02b0560d4e6872845ef241e (patch)
tree1a8f6fd67b5d1606a58c0df00fbab285f1206d36 /src
initial commit
Diffstat (limited to 'src')
-rw-r--r--src/boid.cc108
-rw-r--r--src/drone_controller.cc102
-rw-r--r--src/drone_controller.hh57
-rw-r--r--src/main.cc11
-rw-r--r--src/main_window.cc73
-rw-r--r--src/main_window.hh36
-rw-r--r--src/opengl_mesh.cc19
-rw-r--r--src/opengl_mesh.hh18
-rw-r--r--src/opengl_widget.cc143
-rw-r--r--src/opengl_widget.hh35
10 files changed, 602 insertions, 0 deletions
diff --git a/src/boid.cc b/src/boid.cc
new file mode 100644
index 0000000..9eeea41
--- /dev/null
+++ b/src/boid.cc
@@ -0,0 +1,108 @@
+#include "main_window.hh"
+
+#include <QVector3D>
+#include <QRandomGenerator>
+
+
+struct Fish {
+ QVector3D pos;
+ QVector3D vel;
+ float size;
+ float dist = 10;
+ float sqdist;
+
+ OpenGLMesh mesh;
+
+ Fish(QVector3D pos, OpenGLMesh mesh)
+ :pos(pos),
+ sqdist(dist * dist),
+ mesh(mesh) {}
+
+ void step() {
+ pos += vel;
+ mesh.mat.setToIdentity();
+ QMatrix4x4 rot;
+ rot.lookAt(pos, vel, {0, 1, 0});
+ mesh.mat.translate(pos);
+ // mesh.mat = rot * mesh.mat;
+ }
+
+ bool in_neighborhood(const QVector3D &p) const {
+ return (p - pos).lengthSquared() < sqdist;
+ }
+};
+
+
+struct Banc {
+ QVector<Fish> fishes;
+
+ Banc() {}
+
+ Banc(size_t nfishes) {
+ OpenGLMesh fish_mesh = OpenGLMesh({
+ 0, .1, 0,
+ -.0866, -.05, 0,
+ .0866, -.05, 0,
+ });
+ QRandomGenerator *rng = QRandomGenerator::global();
+ for (size_t i = 0; i < nfishes; i++) {
+ Fish fish({
+ (float) (rng->generateDouble() * 3 - 1.5),
+ (float) (rng->generateDouble() * 3 - 1.5),
+ (float) (rng->generateDouble() * 3 - 1.5)
+ }, fish_mesh);
+ fishes.append(fish);
+ }
+ }
+
+ void step() {
+ QVector<Fish> neighbors;
+ for (Fish &fish : fishes) {
+ QVector3D neighbors_cog;
+ QVector3D neighbors_vel;
+ size_t nneighbors = 0;
+ for (Fish &fish_b : fishes) {
+ if (fish_b.in_neighborhood(fish.pos)) {
+ nneighbors++;
+ neighbors_cog += fish_b.pos;
+ neighbors_vel += fish_b.vel;
+ }
+ }
+ neighbors_cog /= nneighbors;
+ neighbors_vel /= nneighbors;
+ QVector3D v1 = -(fish.pos - neighbors_cog);
+ QVector3D v2 = fish.vel - neighbors_vel;
+ QVector3D v3 = neighbors_cog - fish.pos;
+ QVector3D v = 1.5 * v1 + v2 + v3;
+ float L = .001;
+ fish.vel = (1-L) * fish.vel + L * v;
+ fish.step();
+ }
+ }
+};
+
+
+Banc banc;
+
+void MainWindow::init() {
+ banc = Banc(10);
+ for (Fish &fish : banc.fishes) {
+ glw.meshes.append(&fish.mesh);
+ }
+}
+
+
+void MainWindow::step() {
+ banc.step();
+ OpenGLWidget::instance->update();
+}
+
+
+void OpenGLWidget::paintGL() {
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ for (const Fish &fish : banc.fishes) {
+ glUniformMatrix4fv(model_attr, 1, GL_FALSE, fish.mesh.mat.data());
+ glBindVertexArray(fish.mesh.vao);
+ glDrawArrays(GL_TRIANGLES, 0, fish.mesh.nverts);
+ }
+}
diff --git a/src/drone_controller.cc b/src/drone_controller.cc
new file mode 100644
index 0000000..46acc7e
--- /dev/null
+++ b/src/drone_controller.cc
@@ -0,0 +1,102 @@
+#include "drone_controller.hh"
+
+#include <QJsonArray>
+#include <QDebug>
+
+
+Waypoint::Waypoint(unsigned frame, QVector3D pos)
+ :frame(frame),
+ pos(pos) {}
+
+
+Waypoint::Waypoint(const QJsonObject &json)
+ :Waypoint(json["frame"].toInt(),
+ QVector3D(json["position"]["lng_X"].toInt(),
+ json["position"]["alt_Y"].toInt(),
+ json["position"]["lat_Z"].toInt())) {}
+
+
+Drone::Drone(const QJsonObject &json) {
+ QJsonArray ja = json["waypoints"].toArray();
+ waypoints.reserve(ja.size());
+ for (const QJsonValue &o : ja) {
+ waypoints.append(Waypoint(o.toObject()));
+ }
+}
+
+
+const QVector<Waypoint> Drone::getWaypoints() const {
+ return waypoints;
+}
+
+
+DroneController::DroneController(const QJsonObject &json)
+ :framerate(json["framerate"].toInt()) {
+
+ // TODO: REMOVE!!
+ framerate = 1;
+
+ QJsonArray ja = json["drones"].toArray();
+ drones.reserve(ja.size());
+ for (const QJsonValue &o : ja) {
+ drones.append(Drone(o.toObject()));
+ }
+ for (const Drone &d : drones) {
+ for (const Waypoint &wp : d.getWaypoints()) {
+ if (wp.frame > duration) duration = wp.frame;
+ }
+ }
+
+ connect(&timer, &QTimer::timeout, this, &DroneController::step);
+ pause();
+}
+
+
+DroneController::~DroneController() {
+}
+
+
+int DroneController::getDuration() const {
+ return duration;
+}
+
+
+void DroneController::step() {
+ qDebug() << "frame " << frame << "/" << duration
+ << " (" << (double) frame / duration * 100 << "%)";
+ emit frameChanged(frame);
+ frame++;
+}
+
+
+void DroneController::play() {
+ paused = false;
+ timer.start(1000. / framerate);
+ qDebug() << "playing";
+}
+
+
+void DroneController::pause() {
+ paused = true;
+ timer.stop();
+ qDebug() << "pausing";
+}
+
+
+void DroneController::suspend() {
+ bool old_paused = paused;
+ pause();
+ paused = old_paused;
+}
+
+
+void DroneController::resume() {
+ if (!paused) play();
+}
+
+
+void DroneController::seek(int frame) {
+ if (this->frame == frame) return;
+ this->frame = frame;
+ step();
+}
diff --git a/src/drone_controller.hh b/src/drone_controller.hh
new file mode 100644
index 0000000..484cd57
--- /dev/null
+++ b/src/drone_controller.hh
@@ -0,0 +1,57 @@
+#ifndef DRONE_CONTROLLER_HH
+#define DRONE_CONTROLLER_HH
+
+#include <QJsonObject>
+#include <QVector3D>
+#include <QTimer>
+
+
+struct Waypoint {
+ int frame;
+ QVector3D pos;
+
+ Waypoint(unsigned frame, QVector3D pos);
+ Waypoint(const QJsonObject &json);
+};
+
+
+class Drone {
+ QVector<Waypoint> waypoints;
+
+public:
+ Drone(const QJsonObject &json);
+ const QVector<Waypoint> getWaypoints() const;
+};
+
+
+class DroneController : public QObject {
+ Q_OBJECT
+
+ int framerate;
+ int frame = 0;
+ int duration = 0;
+ QVector<Drone> drones;
+ QTimer timer;
+ bool paused = true;
+
+public:
+ DroneController(const QJsonObject &json);
+ ~DroneController();
+ int getDuration() const;
+
+signals:
+ void frameChanged(int frame);
+
+private slots:
+ void step();
+
+public slots:
+ void play();
+ void pause();
+ void suspend();
+ void resume();
+ void seek(int frame);
+};
+
+
+#endif
diff --git a/src/main.cc b/src/main.cc
new file mode 100644
index 0000000..c70cfaa
--- /dev/null
+++ b/src/main.cc
@@ -0,0 +1,11 @@
+#include "main_window.hh"
+
+#include <QApplication>
+
+
+int main(int argc, char *argv[]) {
+ QApplication app(argc, argv);
+ MainWindow mw;
+ mw.show();
+ return app.exec();
+}
diff --git a/src/main_window.cc b/src/main_window.cc
new file mode 100644
index 0000000..5e58660
--- /dev/null
+++ b/src/main_window.cc
@@ -0,0 +1,73 @@
+#include "main_window.hh"
+
+#include <QApplication>
+#include <QFile>
+#include <QFileDialog>
+#include <QJsonDocument>
+#include <QStyle>
+
+
+MainWindow::MainWindow(QWidget *parent) {
+ (void) parent;
+ connect(&glw, &OpenGLWidget::initialized,
+ this, &MainWindow::onOpenGLWidgetInitialized);
+ // connect(&timer, &QTimer::timeout, this, &MainWindow::step);
+ setCentralWidget(&glw);
+ addToolBar(Qt::TopToolBarArea, &top_tb);
+ open_action = top_tb.addAction("Ouvrir…", [&]() {
+ open(QFileDialog::getOpenFileName(this, "Ouvrir un fichier JSON"));
+ });
+ open_action->setEnabled(false);
+ addToolBar(Qt::BottomToolBarArea, &bottom_tb);
+ playpause_action = bottom_tb.addAction(style()->standardIcon(QStyle::SP_MediaPlay), "",
+ this, &MainWindow::play);
+ playpause_action->setEnabled(false);
+ slider = new QSlider(Qt::Horizontal);
+ bottom_tb.addWidget(slider);
+ slider->setEnabled(false);
+}
+
+
+void MainWindow::play() {
+ dc->play();
+ playpause_action->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
+ disconnect(playpause_action, &QAction::triggered, nullptr, nullptr);
+ connect(playpause_action, &QAction::triggered, this, &MainWindow::pause);
+}
+
+
+void MainWindow::pause() {
+ dc->pause();
+ playpause_action->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
+ disconnect(playpause_action, &QAction::triggered, nullptr, nullptr);
+ connect(playpause_action, &QAction::triggered, this, &MainWindow::play);
+}
+
+
+void MainWindow::open(const QString &path) {
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning("Impossible d’ouvrir le fichier.");
+ return;
+ }
+ QByteArray data = file.readAll();
+ QJsonDocument json_doc = QJsonDocument::fromJson(data);
+ if (dc) delete dc;
+ dc = new DroneController(json_doc.object());
+ playpause_action->setEnabled(true);
+ slider->setMinimum(0);
+ slider->setMaximum(dc->getDuration());
+ connect(slider, &QSlider::sliderPressed, dc, &DroneController::suspend);
+ connect(slider, &QSlider::sliderReleased, dc, &DroneController::resume);
+ connect(slider, &QSlider::valueChanged, dc, &DroneController::seek);
+ connect(dc, &DroneController::frameChanged, slider, &QSlider::setValue);
+ slider->setEnabled(true);
+}
+
+
+void MainWindow::onOpenGLWidgetInitialized() {
+ if (qApp->arguments().size() == 2) {
+ open(qApp->arguments().at(1));
+ }
+ open_action->setEnabled(true);
+}
diff --git a/src/main_window.hh b/src/main_window.hh
new file mode 100644
index 0000000..1e8868a
--- /dev/null
+++ b/src/main_window.hh
@@ -0,0 +1,36 @@
+#ifndef MAIN_WINDOW_HH
+#define MAIN_WINDOW_HH
+
+#include "opengl_widget.hh"
+#include "drone_controller.hh"
+
+#include <QMainWindow>
+#include <QTimer>
+#include <QToolBar>
+#include <QSlider>
+
+
+class MainWindow : public QMainWindow {
+ Q_OBJECT
+
+ OpenGLWidget glw;
+ QTimer timer;
+ QToolBar top_tb;
+ QToolBar bottom_tb;
+ QAction *open_action;
+ QAction *playpause_action;
+ QSlider *slider;
+ DroneController *dc = nullptr;
+ void play();
+ void pause();
+
+public:
+ MainWindow(QWidget *parent=nullptr);
+ void open(const QString &path);
+
+private slots:
+ void onOpenGLWidgetInitialized();
+};
+
+
+#endif
diff --git a/src/opengl_mesh.cc b/src/opengl_mesh.cc
new file mode 100644
index 0000000..cca91da
--- /dev/null
+++ b/src/opengl_mesh.cc
@@ -0,0 +1,19 @@
+#include "opengl_mesh.hh"
+
+#include "opengl_widget.hh"
+
+
+OpenGLMesh::OpenGLMesh(QVector<float> verts) {
+ OpenGLWidget::instance->makeCurrent();
+ QOpenGLFunctions_4_4_Core *glf = OpenGLWidget::instance;
+ nverts = verts.size() / 3;
+ glf->glGenVertexArrays(1, &vao);
+ glf->glGenBuffers(1, &vbo);
+ glf->glBindVertexArray(vao);
+ glf->glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glf->glBufferData(GL_ARRAY_BUFFER, nverts * 3 * sizeof (GLfloat), verts.data(), GL_STATIC_DRAW);
+ glf->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
+ glf->glEnableVertexAttribArray(0);
+ glf->glBindVertexArray(0);
+ OpenGLWidget::instance->doneCurrent();
+}
diff --git a/src/opengl_mesh.hh b/src/opengl_mesh.hh
new file mode 100644
index 0000000..e3b8ad2
--- /dev/null
+++ b/src/opengl_mesh.hh
@@ -0,0 +1,18 @@
+#ifndef MESH_HH
+#define MESH_HH
+
+#include <QMatrix4x4>
+#include <QVector>
+#include <QOpenGLFunctions>
+
+
+struct OpenGLMesh {
+ GLuint vao, vbo;
+ unsigned nverts;
+ QMatrix4x4 mat;
+
+ OpenGLMesh(QVector<float> verts);
+};
+
+
+#endif
diff --git a/src/opengl_widget.cc b/src/opengl_widget.cc
new file mode 100644
index 0000000..0251491
--- /dev/null
+++ b/src/opengl_widget.cc
@@ -0,0 +1,143 @@
+#include "opengl_widget.hh"
+
+
+static const GLchar *vertex_shader_source = R"glsl(
+#version 330 core
+
+layout(location = 0) in vec3 pos;
+
+uniform mat4 proj;
+uniform mat4 view;
+uniform mat4 model;
+
+void main() {
+ gl_Position = proj * view * model * vec4(pos, 1.0);
+}
+)glsl";
+
+static const GLchar *fragment_shader_source = R"glsl(
+#version 330 core
+
+out vec4 final_col;
+
+void main() {
+ final_col = vec4(0, 0, 0, 1);
+}
+)glsl";
+
+
+static void GLAPIENTRY opengl_debug_cb(GLenum source, GLenum type, GLuint id,
+ GLenum severity, GLsizei length,
+ const GLchar* message,
+ const void* userParam) {
+ (void) source; (void) type; (void) id; (void) severity; (void) length;
+ (void) userParam;
+ qDebug() << "OpenGL debug output:" << message;
+}
+
+
+OpenGLWidget *OpenGLWidget::instance = nullptr;
+
+
+OpenGLWidget::OpenGLWidget(QWidget *parent)
+ :QOpenGLWidget(parent) {
+ OpenGLWidget::instance = this;
+}
+
+
+OpenGLWidget::~OpenGLWidget() {
+ OpenGLWidget::instance = nullptr;
+}
+
+
+void OpenGLWidget::initializeGL() {
+ initializeOpenGLFunctions();
+ GLint major, minor;
+ glGetIntegerv(GL_MAJOR_VERSION, &major);
+ glGetIntegerv(GL_MINOR_VERSION, &minor);
+ qDebug("OpenGL version %d.%d", major, minor);
+
+ glEnable(GL_DEBUG_OUTPUT);
+ glDebugMessageCallback(opengl_debug_cb, 0);
+
+ /* Compile the vertex shader. */
+ GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
+ glCompileShader(vertex_shader);
+ GLint status;
+ glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &status);
+ if (status != GL_TRUE) {
+ char log[1024];
+ glGetShaderInfoLog(vertex_shader, sizeof log, NULL, log);
+ fprintf(stderr, "Failed to compile the vertex shader: %s\n", log);
+ exit(1);
+ }
+
+ /* Compile the fragment shader. */
+ GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
+ glCompileShader(fragment_shader);
+ glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &status);
+ if (status != GL_TRUE) {
+ char log[1024];
+ glGetShaderInfoLog(fragment_shader, sizeof log, NULL, log);
+ fprintf(stderr, "Failed to compile the fragment shader: %s\n", log);
+ exit(1);
+ }
+
+ /* Link the shader program. */
+ GLuint shader_program = glCreateProgram();
+ glAttachShader(shader_program, vertex_shader);
+ glAttachShader(shader_program, fragment_shader);
+ glBindFragDataLocation(shader_program, 0, "out_color");
+ glLinkProgram(shader_program);
+ glGetProgramiv(shader_program, GL_LINK_STATUS, &status);
+ if (status != GL_TRUE) {
+ char log[1024];
+ glGetProgramInfoLog(shader_program, sizeof log, NULL, log);
+ fprintf(stderr, "Failed to link the shader program: %s\n", log);
+ exit(1);
+ }
+
+ /* Use it. */
+ glUseProgram(shader_program);
+
+ /* Get the position attribute. */
+ pos_attr = glGetAttribLocation(shader_program, "pos");
+
+ proj_attr = glGetUniformLocation(shader_program, "proj");
+ view_attr = glGetUniformLocation(shader_program, "view");
+ model_attr = glGetUniformLocation(shader_program, "model");
+
+ QMatrix4x4 view;
+ view.translate(0, 0, 5);
+ glUniformMatrix4fv(view_attr, 1, GL_FALSE, view.data());
+
+ QMatrix4x4 trans;
+ trans.translate(0, 0, -5);
+ glUniformMatrix4fv(view_attr, 1, GL_FALSE, trans.data());
+
+ glClearColor(1, 1, 1, 0);
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_MULTISAMPLE);
+
+ emit initialized();
+}
+
+
+void OpenGLWidget::resizeGL(int w, int h) {
+ QMatrix4x4 projection;
+ projection.perspective(FOV, (float) w/h, .1, 100);
+ glUniformMatrix4fv(proj_attr, 1, GL_FALSE, projection.data());
+}
+
+
+void OpenGLWidget::paintGL() {
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ for (const OpenGLMesh &mesh : meshes) {
+ glUniformMatrix4fv(model_attr, 1, GL_FALSE, mesh.mat.data());
+ glBindVertexArray(mesh.vao);
+ glDrawArrays(GL_TRIANGLES, 0, mesh.nverts);
+ }
+}
diff --git a/src/opengl_widget.hh b/src/opengl_widget.hh
new file mode 100644
index 0000000..48cb276
--- /dev/null
+++ b/src/opengl_widget.hh
@@ -0,0 +1,35 @@
+#ifndef OPENGL_WIDGET_HH
+#define OPENGL_WIDGET_HH
+
+#include "opengl_mesh.hh"
+
+#include <QOpenGLWidget>
+#include <QMatrix4x4>
+#include <QOpenGLFunctions_4_4_Core>
+#include <QOpenGLShaderProgram>
+
+#define FOV 70
+
+
+class OpenGLWidget : public QOpenGLWidget, public QOpenGLFunctions_4_4_Core {
+ Q_OBJECT
+
+ GLuint pos_attr, proj_attr, view_attr, model_attr;
+
+public:
+ static OpenGLWidget *instance;
+
+ QVector<OpenGLMesh> meshes;
+
+ OpenGLWidget(QWidget *parent=nullptr);
+ ~OpenGLWidget();
+ void initializeGL() override;
+ void resizeGL(int w, int h) override;
+ void paintGL() override;
+
+signals:
+ void initialized();
+};
+
+
+#endif