initial commit
This commit is contained in:
commit
7fbe0814d5
16
projet.pro
Normal file
16
projet.pro
Normal file
@ -0,0 +1,16 @@
|
||||
QT += core gui widgets
|
||||
TARGET = projet
|
||||
TEMPLATE = app
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
CONFIG += qt debug
|
||||
|
||||
SOURCES += src/main.cc
|
||||
SOURCES += src/main_window.cc
|
||||
SOURCES += src/opengl_mesh.cc
|
||||
SOURCES += src/opengl_widget.cc
|
||||
SOURCES += src/drone_controller.cc
|
||||
|
||||
HEADERS += src/main_window.hh
|
||||
HEADERS += src/opengl_mesh.hh
|
||||
HEADERS += src/opengl_widget.hh
|
||||
HEADERS += src/drone_controller.hh
|
108
src/boid.cc
Normal file
108
src/boid.cc
Normal file
@ -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);
|
||||
}
|
||||
}
|
102
src/drone_controller.cc
Normal file
102
src/drone_controller.cc
Normal file
@ -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();
|
||||
}
|
57
src/drone_controller.hh
Normal file
57
src/drone_controller.hh
Normal file
@ -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
|
11
src/main.cc
Normal file
11
src/main.cc
Normal file
@ -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();
|
||||
}
|
73
src/main_window.cc
Normal file
73
src/main_window.cc
Normal file
@ -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);
|
||||
}
|
36
src/main_window.hh
Normal file
36
src/main_window.hh
Normal file
@ -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
|
19
src/opengl_mesh.cc
Normal file
19
src/opengl_mesh.cc
Normal file
@ -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();
|
||||
}
|
18
src/opengl_mesh.hh
Normal file
18
src/opengl_mesh.hh
Normal file
@ -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
|
143
src/opengl_widget.cc
Normal file
143
src/opengl_widget.cc
Normal file
@ -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);
|
||||
}
|
||||
}
|
35
src/opengl_widget.hh
Normal file
35
src/opengl_widget.hh
Normal file
@ -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
|
1
waypoints.json
Normal file
1
waypoints.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user