Thu, 13 Feb 2020 12:51:27 +0200
added grid program
CMakeLists.txt | file | annotate | diff | comparison | revisions | |
src/basics.h | file | annotate | diff | comparison | revisions | |
src/gl/common.h | file | annotate | diff | comparison | revisions | |
src/gl/compiler.cpp | file | annotate | diff | comparison | revisions | |
src/gl/gridprogram.cpp | file | annotate | diff | comparison | revisions | |
src/gl/gridprogram.h | file | annotate | diff | comparison | revisions | |
src/gl/partrenderer.cpp | file | annotate | diff | comparison | revisions | |
src/gl/partrenderer.h | file | annotate | diff | comparison | revisions |
--- a/CMakeLists.txt Sat Feb 08 00:08:57 2020 +0200 +++ b/CMakeLists.txt Thu Feb 13 12:51:27 2020 +0200 @@ -35,6 +35,7 @@ src/uiutilities.cpp src/version.cpp src/gl/compiler.cpp + src/gl/gridprogram.cpp src/gl/partrenderer.cpp src/linetypes/comment.cpp src/linetypes/conditionaledge.cpp @@ -72,6 +73,7 @@ src/version.h src/gl/common.h src/gl/compiler.h + src/gl/gridprogram.h src/gl/partrenderer.h src/linetypes/comment.h src/linetypes/conditionaledge.h
--- a/src/basics.h Sat Feb 08 00:08:57 2020 +0200 +++ b/src/basics.h Thu Feb 13 12:51:27 2020 +0200 @@ -22,6 +22,7 @@ #include <cstdlib> #include <cstring> #include <cmath> +#include <optional> #include <QMatrix4x4> #include <QObject> #include <QPointF> @@ -105,5 +106,32 @@ return static_cast<std::make_signed_t<T>>(x); } + +/** +* @brief casts double and long double - and only those types - to float +* @param[in] x double or long double to cast +* @returns float +*/ +template<typename T> +auto doubleToFloat(T x) + -> std::enable_if_t< + std::is_same_v<std::decay_t<T>, double> || + std::is_same_v<std::decay_t<T>, long double + >, float> +{ + return static_cast<float>(x); +} + +/** +* @brief Casts float - and only float - to double +* @param[in] x float to cast +* @returns double +*/ +template<typename T> +auto floatToDouble(T x) + -> std::enable_if_t<std::is_same_v<std::decay_t<T>, float>, double> +{ + return static_cast<double>(x); +} Q_DECLARE_METATYPE(glm::vec3) Q_DECLARE_METATYPE(glm::mat4)
--- a/src/gl/common.h Sat Feb 08 00:08:57 2020 +0200 +++ b/src/gl/common.h Thu Feb 13 12:51:27 2020 +0200 @@ -18,24 +18,46 @@ #pragma once #include <QColor> +#include <QOpenGLBuffer> #include <QOpenGLFunctions> -#include <QGenericMatrix> +#include <QOpenGLShader> +#include <QOpenGLShaderProgram> +#include <QOpenGLVertexArrayObject> +#include <glm/gtc/type_ptr.hpp> #include "basics.h" #include "colors.h" namespace gl { - // Transformation matrices for projection cameras. - static const QMatrix4x4 topCameraMatrix = {}; - static const QMatrix4x4 frontCameraMatrix = {1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1}; - static const QMatrix4x4 leftCameraMatrix = {0, -1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 1}; - static const QMatrix4x4 bottomCameraMatrix = {1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1}; - static const QMatrix4x4 backCameraMatrix = {-1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1}; - static const QMatrix4x4 rightCameraMatrix = {0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1}; - static constexpr QRgb BlackRgb = 0xff000000; struct Polygon; + class ShaderProgram; + + void buildShaders( + QOpenGLShaderProgram* shaderProgram, + const char* vertexShaderSource, + const char* fragmentShaderSource); + void checkForGLErrors(QWidget* parent); } +class gl::ShaderProgram : public QOpenGLShaderProgram +{ +public: + using QOpenGLShaderProgram::QOpenGLShaderProgram; + // for some reason I cannot overload setUniformValue properly with this template thing + template<typename Float, glm::qualifier Prec> + void setUniformMatrix(const char* uniformName, const glm::mat<4, 4, Float, Prec>& value) + { + const float (*array)[4][4] = reinterpret_cast<const float(*)[4][4]>(glm::value_ptr(value)); + this->setUniformValue(uniformName, *array); + } + template<int N, typename Float, glm::qualifier Prec> + void setUniformVector(const char* uniformName, const glm::vec<N, Float, Prec>& value) + { + const Float (*array)[N] = reinterpret_cast<const Float(*)[N]>(glm::value_ptr(value)); + this->setUniformValue(uniformName, array); + } +}; + struct gl::Polygon { enum Type : qint8
--- a/src/gl/compiler.cpp Sat Feb 08 00:08:57 2020 +0200 +++ b/src/gl/compiler.cpp Thu Feb 13 12:51:27 2020 +0200 @@ -130,6 +130,42 @@ { } +void gl::buildShaders( + QOpenGLShaderProgram* shaderProgram, + const char* vertexShaderSource, + const char* fragmentShaderSource) +{ + shaderProgram->create(); + const bool vertexShaderCompiled = shaderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource); + QString log; + if (not vertexShaderCompiled) + { + log += "\n" + QObject::tr("Vertex shader:") + "\n" + shaderProgram->log(); + } + const bool fragmentShaderCompiled = shaderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource); + if (not fragmentShaderCompiled) + { + log += "\n" + QObject::tr("Fragment shader:") + "\n" + shaderProgram->log(); + } + if (not vertexShaderCompiled or not fragmentShaderCompiled) + { + QMessageBox::critical( + nullptr, + QObject::tr("Shader compile error"), + QObject::tr("Could not compile shaders.") + "\n" + log); + std::exit(-1); + } + const bool linkSuccessful = shaderProgram->link(); + if (not linkSuccessful) + { + QMessageBox::critical( + nullptr, + QObject::tr("Shader link error"), + QObject::tr("Could not link shaders: %1").arg(shaderProgram->log()) + ); + } +} + void gl::Compiler::initialize() { if (not this->initialized) @@ -139,24 +175,7 @@ { auto& object = this->glObjects[i]; object.program = new QOpenGLShaderProgram; - object.program->create(); - const bool vertexShaderCompiled = object.program->addShaderFromSourceCode(QOpenGLShader::Vertex, ::vertexShaderSource); - QString log; - if (not vertexShaderCompiled) - { - log += "\n" + tr("Vertex shader:") + "\n" + object.program->log(); - } - const bool fragmentShaderCompiled = object.program->addShaderFromSourceCode(QOpenGLShader::Fragment, ::fragmentShaderSource); - if (not fragmentShaderCompiled) - { - log += "\n" + tr("Fragment shader:") + "\n" + object.program->log(); - } - if (not vertexShaderCompiled or not fragmentShaderCompiled) - { - QMessageBox::critical(nullptr, tr("Shader compile error"), tr("Could not compile shaders.") + "\n" + log); - std::exit(-1); - } - object.program->link(); + gl::buildShaders(object.program, ::vertexShaderSource, ::fragmentShaderSource); object.program->bind(); object.buffer.create(); object.buffer.bind();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gl/gridprogram.cpp Thu Feb 13 12:51:27 2020 +0200 @@ -0,0 +1,136 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2020 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "gridprogram.h" + +// Based on https://stackoverflow.com/q/30842755 +const char vertexShaderSource[] = R"( +#version 330 core + +layout (location = 0) in vec2 in_position; +uniform mat4 view; +uniform mat4 projection; +uniform mat4 model; +smooth out vec2 ex_uv; +const mat4 stretch = mat4(vec4(10000, 0, 0, 0), vec4(0, 10000, 0, 0), vec4(0, 0, 10000, 0), vec4(0, 0, 0, 1)); + +void main() +{ + gl_Position = projection * view * model * stretch * vec4(in_position, 0.0, 1.0); + ex_uv = in_position; +} +)"; + +const char fragmentShaderSource[] = R"( +#version 330 core + +out vec4 color; +smooth in vec2 ex_uv; +const vec4 lineColor = vec4(1.0, 1.0, 1.0, 0.75); + +void main(void) +{ + if(fract(ex_uv.x / 0.001f) < 0.01f || fract(ex_uv.y / 0.001f) < 0.01f) + color = lineColor; + else + color = vec4(0); +} +)"; + +static const glm::vec2 data[] = {{-1, -1}, {-1, 1}, {1, 1}, {1, -1}}; + +GridProgram::GridProgram(QObject* parent) : + QObject{parent}, + buffer{QOpenGLBuffer::VertexBuffer}, + vertexShader{QOpenGLShader::Vertex}, + fragmentShader{QOpenGLShader::Fragment} +{ +} + +void GridProgram::initialize() +{ + if (not isInitialized) + { + this->initializeOpenGLFunctions(); + this->isInitialized = true; + this->program.emplace(this); + gl::buildShaders(&*this->program, ::vertexShaderSource, ::fragmentShaderSource); + this->program->bind(); + this->buffer.create(); + this->buffer.bind(); + this->buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); + this->buffer.allocate(data, countof(data) * sizeof data[0]); + this->vertexArrayObject.create(); + this->vertexArrayObject.bind(); + this->program->enableAttributeArray(0); + this->program->setAttributeBuffer(0, GL_FLOAT, 0, 2, 0); + this->vertexArrayObject.release(); + this->buffer.release(); + this->program->release(); + this->checkForGLErrors(); + } +} + +void GridProgram::setViewMatrix(const glm::mat4& newViewMatrix) +{ + Q_ASSERT(this->isInitialized); + this->program->bind(); + this->program->setUniformMatrix("view", newViewMatrix); + this->program->release(); + this->checkForGLErrors(); +} + +void GridProgram::setProjectionMatrix(const glm::mat4& newProjectionMatrix) +{ + Q_ASSERT(this->isInitialized); + this->program->bind(); + this->program->setUniformMatrix("projection", newProjectionMatrix); + this->program->release(); + this->checkForGLErrors(); +} + +void GridProgram::setModelMatrix(const glm::mat4& newModelMatrix) +{ + Q_ASSERT(this->isInitialized); + this->program->bind(); + this->program->setUniformMatrix("model", newModelMatrix); + this->program->release(); + this->checkForGLErrors(); +} + +void GridProgram::draw() +{ + this->program->bind(); + this->vertexArrayObject.bind(); + glDrawArrays(GL_QUADS, 0, countof(data)); + this->vertexArrayObject.release(); + this->program->release(); + this->checkForGLErrors(); +} + +void GridProgram::teardown() +{ + this->vertexArrayObject.destroy(); + this->buffer.destroy(); + this->program.reset(); +} + +void GridProgram::checkForGLErrors() +{ + gl::checkForGLErrors(qobject_cast<QWidget*>(this->parent())); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gl/gridprogram.h Thu Feb 13 12:51:27 2020 +0200 @@ -0,0 +1,45 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2020 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once +#include "common.h" + +class GridProgram : public QObject, protected QOpenGLFunctions +{ + Q_OBJECT +public: + GridProgram(QObject* parent = nullptr); + GridProgram(const GridProgram&) = delete; + GridProgram(GridProgram&&) = delete; + ~GridProgram() = default; + void initialize(); + void setViewMatrix(const glm::mat4& newViewMatrix); + void setProjectionMatrix(const glm::mat4& newProjectionMatrix); + void setModelMatrix(const glm::mat4& newModelMatrix); + void operator=(GridProgram) = delete; + void draw(); + void teardown(); +private: + void checkForGLErrors(); + bool isInitialized = false; + QOpenGLBuffer buffer; + QOpenGLShader vertexShader; + QOpenGLShader fragmentShader; + std::optional<gl::ShaderProgram> program{std::nullopt}; + QOpenGLVertexArrayObject vertexArrayObject; +};
--- a/src/gl/partrenderer.cpp Sat Feb 08 00:08:57 2020 +0200 +++ b/src/gl/partrenderer.cpp Thu Feb 13 12:51:27 2020 +0200 @@ -33,7 +33,8 @@ model{model}, documents{documents}, colorTable{colorTable}, - compiler{new gl::Compiler{this->colorTable, this}} + compiler{new gl::Compiler{this->colorTable, this}}, + gridProgram{this} { this->setMouseTracking(true); } @@ -42,6 +43,11 @@ { } +static QVector3D vec3FromQColor(const QColor& color) +{ + return {(float)color.redF(), (float)color.greenF(), (float)color.blueF()}; +} + void PartRenderer::initializeGL() { this->initializeOpenGLFunctions(); @@ -49,6 +55,8 @@ { abort(); } + this->gridProgram.emplace(this); + this->gridProgram->initialize(); this->compiler->initialize(); this->compiler->build(this->model, this->documents, this->renderPreferences); this->initialized = true; @@ -67,6 +75,7 @@ 0.1f, 10000.f); this->compiler->setUniformMatrix("projectionMatrix", this->projectionMatrix); + this->gridProgram->setProjectionMatrix(this->projectionMatrix); } static GLenum getGlTypeForArrayClass(const gl::ArrayClass vboClass) @@ -88,17 +97,12 @@ { glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); - glEnable(GL_MULTISAMPLE); this->renderScene(); } -static QVector3D vec3FromQColor(const QColor& color) -{ - return {(float)color.redF(), (float)color.greenF(), (float)color.blueF()}; -} - void PartRenderer::renderScene() { + this->checkForGLErrors(); if (this->renderPreferences.lineAntiAliasing && this->renderPreferences.style != gl::RenderStyle::PickScene) { glEnable(GL_LINE_SMOOTH); @@ -122,6 +126,7 @@ glClearColor(0.0f, 0.0f, 0.0f, 1.0f); this->compiler->setUniform("useLighting", GL_FALSE); } + this->checkForGLErrors(); this->compiler->setUniform("selectedColor", vec3FromQColor(this->renderPreferences.selectedColor)); this->compiler->setUniform("highlighted", this->highlighted.value); this->checkForGLErrors(); @@ -164,6 +169,10 @@ this->renderAllArrays(); break; } + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + this->gridProgram->draw(); + glDisable(GL_BLEND); glDisable(GL_POLYGON_OFFSET_FILL); } @@ -182,6 +191,7 @@ const double z = 2 * std::exp(this->zoom) * (1 + this->compiler->modelDistance()); this->viewMatrix = glm::lookAt(glm::vec3{0, 0, z}, {0, 0, 0}, {0, -1, 0}); this->compiler->setUniformMatrix("viewMatrix", this->viewMatrix); + this->gridProgram->setViewMatrix(this->viewMatrix); } void PartRenderer::renderVao(const gl::ArrayClass arrayClass) @@ -197,6 +207,11 @@ void PartRenderer::checkForGLErrors() { + gl::checkForGLErrors(this); +} + +void gl::checkForGLErrors(QWidget* parent) +{ GLenum glError; QStringList errors; while ((glError = glGetError()) != GL_NO_ERROR) @@ -206,12 +221,12 @@ } if (not errors.isEmpty()) { - QMessageBox box{this}; + QMessageBox box{parent}; box.setIcon(QMessageBox::Critical); - box.setText(tr("Failed to render: %1").arg(errors.join("\n"))); - box.setWindowTitle(tr("Rendering error")); + box.setText(QObject::tr("OpenGL error: %1").arg(errors.join("\n"))); + box.setWindowTitle(QObject::tr("OpenGL error")); box.setStandardButtons(QMessageBox::Close); - box.button(QMessageBox::Close)->setText(tr("Damn it")); + box.button(QMessageBox::Close)->setText(QObject::tr("Damn it")); box.exec(); } } @@ -231,7 +246,9 @@ const glm::quat q_x = glm::angleAxis(scalar * move_x, glm::vec3{0, -1, 0}); const glm::quat q_y = glm::angleAxis(scalar * move_y, glm::vec3{-1, 0, 0}); this->modelQuaternion = q_x * q_y * this->modelQuaternion; - this->compiler->setUniformMatrix("modelMatrix", glm::mat4_cast(this->modelQuaternion)); + const glm::mat4 modelMatrix = glm::mat4_cast(this->modelQuaternion); + this->compiler->setUniformMatrix("modelMatrix", modelMatrix); + this->gridProgram->setModelMatrix(modelMatrix); this->update(); } this->lastMousePosition = pointToPointF(event->pos());
--- a/src/gl/partrenderer.h Sat Feb 08 00:08:57 2020 +0200 +++ b/src/gl/partrenderer.h Thu Feb 13 12:51:27 2020 +0200 @@ -10,6 +10,7 @@ #include "main.h" #include "gl/common.h" #include "gl/compiler.h" +#include "gl/gridprogram.h" class PartRenderer : public QOpenGLWidget, protected QOpenGLFunctions { @@ -44,6 +45,7 @@ glm::mat4 projectionMatrix; glm::mat4 viewMatrix; glm::quat modelQuaternion; + std::optional<GridProgram> gridProgram; static constexpr double MIN_ZOOM = 0.0; static constexpr double MAX_ZOOM = 3.0; double zoom = 1.0;