added grid program

Thu, 13 Feb 2020 12:51:27 +0200

author
Teemu Piippo <teemu@hecknology.net>
date
Thu, 13 Feb 2020 12:51:27 +0200
changeset 53
3af627f7a40f
parent 52
eee644f88e93
child 54
a4055f67b9c7

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;

mercurial