m2-ar-projet/src/opengl_widget.cc

234 lines
7.5 KiB
C++

#include "opengl_widget.hh"
#include "load_obj.hh"
#include <QMouseEvent>
#include <QOpenGLBuffer>
#include <QSurfaceFormat>
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;
// Those are a bit too verbose
if (!QString((const char *) message).startsWith("Shader Stats:")) {
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::loadSkybox() {
// Shader program
if (!skybox_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/skybox.vert")) {
qFatal("Error compiling skybox.vert: %s", skybox_program.log().toLocal8Bit().constData());
}
if (!skybox_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/skybox.frag")) {
qFatal("Error compiling skybox.frag: %s", skybox_program.log().toLocal8Bit().constData());
}
skybox_program.bindAttributeLocation("in_pos", 0);
if (!skybox_program.link()) {
qFatal("Error linking the skybox shader program: %s", skybox_program.log().toLocal8Bit().constData());
}
skybox_program.bind();
skybox_program.setUniformValue("skybox", 0);
QVector<GLfloat> skybox_verts {
-1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0,
-1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0,
1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0,
-1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
-1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0,
-1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0
};
// QVector<GLfloat> skybox_verts = load_obj(":/mdl/cube.obj", 0);
// VAO
glGenVertexArrays(1, &skybox_vao);
glBindVertexArray(skybox_vao);
glGenBuffers(1, &skybox_vbo);
glBindBuffer(GL_ARRAY_BUFFER, skybox_vbo);
glBufferData(GL_ARRAY_BUFFER, skybox_verts.size() * sizeof (GLfloat), skybox_verts.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
// Skybox texture images
QVector<QImage> skybox_img {
QImage(":/img/clouds1_west.jpg").convertToFormat(QImage::Format_RGB888),
QImage(":/img/clouds1_east.jpg").convertToFormat(QImage::Format_RGB888),
QImage(":/img/clouds1_up.jpg").convertToFormat(QImage::Format_RGB888),
QImage(":/img/clouds1_down.jpg").convertToFormat(QImage::Format_RGB888),
QImage(":/img/clouds1_south.jpg").convertToFormat(QImage::Format_RGB888),
QImage(":/img/clouds1_north.jpg").convertToFormat(QImage::Format_RGB888)
};
size_t width = skybox_img[0].width();
size_t height = skybox_img[0].height();
glGenTextures(1, &skybox_tex);
glBindTexture(GL_TEXTURE_CUBE_MAP, skybox_tex);
for (int i = 0; i < 6; i++) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE,
(const GLvoid *) skybox_img[i].constBits());
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
skybox_program.release();
}
void OpenGLWidget::loadGround() {
QOpenGLTexture *ground_tex = new QOpenGLTexture(QImage(":/img/ground.jpg").mirrored());
ground_tex->generateMipMaps();
ground_tex->setMagnificationFilter(QOpenGLTexture::Linear);
ground_tex->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
ground_tex->setWrapMode(QOpenGLTexture::MirroredRepeat);
ground = new OpenGLMesh({
-1000, 0, 1000, 0, 1000, 0, 0, 1000,
1000, 0, -1000, 0, 1000, 0, 1000, 0,
-1000, 0, -1000, 0, 1000, 0, 0, 0,
1000, 0, -1000, 0, 1000, 0, 1000, 0,
-1000, 0, 1000, 0, 1000, 0, 0, 1000,
1000, 0, 1000, 0, 1000, 0, 1000, 1000,
}, ground_tex, &main_program);
}
void OpenGLWidget::initializeGL() {
QSurfaceFormat format;
format.setProfile(QSurfaceFormat::CoreProfile);
format.setDepthBufferSize(24);
setFormat(format);
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);
if (!main_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/main.vert")) {
qFatal("Error compiling main.vert: %s", main_program.log().toLocal8Bit().constData());
}
if (!main_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/main.frag")) {
qFatal("Error compiling main.frag: %s", main_program.log().toLocal8Bit().constData());
}
main_program.bindAttributeLocation("in_pos", 0);
main_program.bindAttributeLocation("in_norm", 1);
main_program.bindAttributeLocation("in_uv", 2);
if (!main_program.link()) {
qFatal("Error linking the main shader program: %s", main_program.log().toLocal8Bit().constData());
}
main_program.bind();
main_program.setUniformValue("tex", 0);
main_program.release();
loadSkybox();
loadGround();
glClearColor(1, 1, 1, 0);
glCullFace(GL_BACK);
emit initialized();
}
void OpenGLWidget::resizeGL(int w, int h) {
proj.setToIdentity();
proj.perspective(FOV, (float) w/h, .01, 1000);
}
void OpenGLWidget::paintGL() {
glEnable(GL_CULL_FACE); // shouldn't have to do that each frame, weird
glEnable(GL_DEPTH_TEST); //
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 trans;
trans.translate(0, 0, -cam_dist);
QMatrix4x4 view = trans * rot;
glDepthMask(GL_FALSE);
glDepthFunc(GL_LEQUAL);
skybox_program.bind();
skybox_program.setUniformValue("proj", proj);
skybox_program.setUniformValue("view", view);
glBindVertexArray(skybox_vao);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, skybox_tex);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
skybox_program.release();
glDepthMask(GL_TRUE);
glDepthFunc(GL_LESS);
main_program.bind();
main_program.setUniformValue("proj", proj);
main_program.setUniformValue("view", view);
glActiveTexture(GL_TEXTURE0);
ground->draw(this, QMatrix4x4());
if (painter) painter->draw(this);
main_program.release();
}
void OpenGLWidget::mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
mouse_pos = e->pos();
}
}
void OpenGLWidget::mouseReleaseEvent(QMouseEvent *e) {
(void) e;
rot_start = rot;
}
void OpenGLWidget::mouseMoveEvent(QMouseEvent *e) {
if (e->buttons() & Qt::LeftButton) {
QPoint delta = e->pos() - mouse_pos;
rot = rot_start;
rot.rotate(delta.x() / 5., 0, 1, 0);
rot.rotate(delta.y() / 5., QVector3D(1, 0, 0) * rot);
update();
}
}
void OpenGLWidget::wheelEvent(QWheelEvent *e) {
cam_dist -= e->angleDelta().y() / 1000. * cam_dist;
update();
}
void OpenGLWidget::setPainter(const Painter *p) {
painter = p;
}
QOpenGLShaderProgram *OpenGLWidget::getMainProgram() {
return &main_program;
}