cleanup, gl::Compiler changed to gl::ModelShaders

Wed, 25 May 2022 12:01:58 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Wed, 25 May 2022 12:01:58 +0300
changeset 189
815fbaae9cb2
parent 188
64ea7282611e
child 190
3dbdc243f053

cleanup, gl::Compiler changed to gl::ModelShaders

src/gl/common.h file | annotate | diff | comparison | revisions
src/gl/compiler.cpp file | annotate | diff | comparison | revisions
src/gl/compiler.h file | annotate | diff | comparison | revisions
src/gl/partrenderer.cpp file | annotate | diff | comparison | revisions
src/gl/partrenderer.h file | annotate | diff | comparison | revisions
src/types/boundingbox.cpp file | annotate | diff | comparison | revisions
src/types/boundingbox.h file | annotate | diff | comparison | revisions
src/ui/canvas.cpp file | annotate | diff | comparison | revisions
--- a/src/gl/common.h	Tue May 24 16:11:10 2022 +0300
+++ b/src/gl/common.h	Wed May 25 12:01:58 2022 +0300
@@ -110,6 +110,7 @@
 };
 
 Q_DECLARE_METATYPE(gl::Polygon)
+extern QOpenGLFunctions glfunc;
 
 namespace gl
 {
--- a/src/gl/compiler.cpp	Tue May 24 16:11:10 2022 +0300
+++ b/src/gl/compiler.cpp	Wed May 25 12:01:58 2022 +0300
@@ -125,17 +125,6 @@
 }
 )";
 
-gl::Compiler::Compiler(Model *model, const ldraw::ColorTable& colorTable, QObject* parent) :
-	QObject{parent},
-	model{model},
-	colorTable{colorTable}
-{
-}
-
-gl::Compiler::~Compiler()
-{
-}
-
 void gl::buildShaders(
 	QOpenGLShaderProgram* shaderProgram,
 	const char* vertexShaderSource,
@@ -174,69 +163,40 @@
 	}
 }
 
-void gl::Compiler::initialize()
+void gl::initializeModelShaders(gl::ModelShaders *modelShaders)
 {
-	if (not this->initialized)
+	if (not modelShaders->initialized)
 	{
-		this->initializeOpenGLFunctions();
-		for (auto& object : this->glObjects)
+		for (auto& shader : modelShaders->shaderObjects)
 		{
-			object.program = new QOpenGLShaderProgram;
-			gl::buildShaders(object.program, ::vertexShaderSource, ::fragmentShaderSource);
-			object.program->bind();
-			object.buffer.create();
-			object.buffer.bind();
-			object.buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
-			object.vertexArray.create();
-			object.vertexArray.bind();
+			shader.program = std::make_unique<QOpenGLShaderProgram>();
+			gl::buildShaders(shader.program.get(), ::vertexShaderSource, ::fragmentShaderSource);
+			shader.program->bind();
+			shader.buffer.create();
+			shader.buffer.bind();
+			shader.buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
+			shader.vertexArray.create();
+			shader.vertexArray.bind();
 			for (int k : {0, 1, 2, 3, 4})
 			{
-				object.program->enableAttributeArray(k);
+				shader.program->enableAttributeArray(k);
 			}
+			using Vertex = ModelShaders::Vertex;
 			constexpr int stride = sizeof(Vertex);
-			object.program->setAttributeBuffer(0, GL_FLOAT, offsetof(Vertex, position), 3, stride);
-			object.program->setAttributeBuffer(1, GL_FLOAT, offsetof(Vertex, color), 4, stride);
-			object.program->setAttributeBuffer(2, GL_FLOAT, offsetof(Vertex, normal), 3, stride);
+			shader.program->setAttributeBuffer(0, GL_FLOAT, offsetof(Vertex, position), 3, stride);
+			shader.program->setAttributeBuffer(1, GL_FLOAT, offsetof(Vertex, color), 4, stride);
+			shader.program->setAttributeBuffer(2, GL_FLOAT, offsetof(Vertex, normal), 3, stride);
 			glVertexAttribIPointer(3, 1, GL_INT, stride, reinterpret_cast<void*>(offsetof(Vertex, id)));
 			glVertexAttribIPointer(4, 1, GL_INT, stride, reinterpret_cast<void*>(offsetof(Vertex, selected)));
-			object.vertexArray.release();
-			object.buffer.release();
-			object.program->release();
+			shader.vertexArray.release();
+			shader.buffer.release();
+			shader.program->release();
 		}
-		this->initialized = true;
+		modelShaders->initialized = true;
 	}
 }
 
-void gl::Compiler::build(DocumentManager* context, const gl::RenderPreferences& preferences)
-{
-	this->boundingBox = {};
-	std::vector<Vertex> vboData[gl::NUM_POLYGON_TYPES];
-	std::optional<ModelId> modelId = context->findIdForModel(this->model);
-	if (modelId.has_value())
-	{
-		PolygonCache* polygonBuilder = context->getPolygonCacheForModel(modelId.value());
-		if (polygonBuilder != nullptr)
-		{
-			const std::vector<gl::Polygon> polygons = polygonBuilder->getPolygons(context);
-			for (const gl::Polygon& polygon : polygons)
-			{
-				this->buildPolygon(polygon, vboData, preferences);
-			}
-			for (int arrayId = 0; arrayId < gl::NUM_POLYGON_TYPES; arrayId += 1)
-			{
-				auto& buffer = this->glObjects[arrayId].buffer;
-				auto& vector = vboData[arrayId];
-				this->storedVertexCounts[arrayId] = vector.size();
-				this->glObjects[arrayId].cachedData = vector; // todo: get rid of this copy
-				buffer.bind();
-				buffer.allocate(vector.data(), static_cast<int>(vector.size() * sizeof vector[0]));
-				buffer.release();
-			}
-		}
-	}
-}
-
-gl::ArrayClass classifyPolygon(const gl::Polygon& polygon)
+static gl::ArrayClass classifyPolygon(const gl::Polygon& polygon)
 {
 	switch (polygon.type)
 	{
@@ -252,36 +212,27 @@
 	return gl::ArrayClass::Lines;
 }
 
-ldraw::id_t gl::Compiler::idFromColor(const std::array<GLubyte, 3>& data)
-{
-	return {data[0] * std::int32_t{0x10000} + data[1] * std::int32_t{0x100} + data[2]};
-}
-
-void gl::Compiler::buildPolygon(
-	gl::Polygon polygon,
-	std::vector<Vertex>* vboData,
-	const gl::RenderPreferences& preferences)
+template<typename Fn>
+void iterateModelPolygons(Model* model, DocumentManager* context, Fn&& fn)
 {
-	const gl::ArrayClass vboClass = classifyPolygon(polygon);
-	std::vector<Vertex>& vertexBuffer = vboData[static_cast<int>(vboClass)];
-	auto vertexRing = iter::ring(polygon.vertices, polygon.numPolygonVertices());
-	reserveMore(vertexBuffer, polygon.numPolygonVertices());
-	const QColor color = this->getColorForPolygon(polygon, preferences);
-	for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1)
+	std::optional<ModelId> modelId = context->findIdForModel(model);
+	if (modelId.has_value())
 	{
-		const glm::vec3& v1 = vertexRing[i - 1];
-		const glm::vec3& v2 = vertexRing[i];
-		const glm::vec3& v3 = vertexRing[i + 1];
-		this->boundingBox.consider(polygon.vertices[i]);
-		Vertex& vertex = vertexBuffer.emplace_back();
-		vertex.position = polygon.vertices[i];
-		vertex.normal = glm::normalize(glm::cross(v1 - v2, v3 - v2));
-		vertex.color = glm::vec4{color.redF(), color.greenF(), color.blueF(), color.alphaF()};
-		vertex.id = polygon.id.value;
+		PolygonCache* polygonCache= context->getPolygonCacheForModel(modelId.value());
+		if (polygonCache != nullptr)
+		{
+			for (const gl::Polygon& polygon : polygonCache->getPolygons(context))
+			{
+				fn(polygon);
+			}
+		}
 	}
 }
 
-QColor gl::Compiler::getColorForPolygon(const gl::Polygon& polygon, const gl::RenderPreferences& preferences)
+static QColor getColorForPolygon(
+	const gl::Polygon& polygon,
+	const gl::RenderPreferences& preferences,
+	const ldraw::ColorTable& colorTable)
 {
 	QColor color;
 	// For normal colors, use the polygon's color.
@@ -297,41 +248,94 @@
 	else
 	{
 		// Not main or edge color, use the polygon's color as is.
-		color = this->colorTable[polygon.color].faceColor;
+		color = colorTable[polygon.color].faceColor;
 	}
 	return color;
 }
 
-glm::vec3 gl::Compiler::modelCenter() const
+/**
+ * @brief Computes the minimum bounding box for a model
+ */
+BoundingBox gl::boundingBoxForModel(Model* model, DocumentManager* context)
 {
-	return boxCenter(this->boundingBox);
-}
-
-double gl::Compiler::modelDistance() const
-{
-	return static_cast<double>(longestMeasure(this->boundingBox));
+	BoundingBox result = emptyBoundingBox;
+	iterateModelPolygons(model, context, [&](const gl::Polygon& polygon)
+	{
+		for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1)
+		{
+			addPointToBox(result, polygon.vertices[i]);
+		}
+	});
+	return result;
 }
 
-void gl::Compiler::bindVertexArray(gl::ArrayClass arrayClass)
+/**
+ * @brief gl::build Creates GL vertices for objects in the model and buffers them to shaders.
+ */
+void gl::build(
+	gl::ModelShaders* shaders,
+	Model* model,
+	const ldraw::ColorTable& colorTable,
+	DocumentManager* context,
+	const gl::RenderPreferences& preferences)
 {
-	auto& object = this->glObjects[static_cast<int>(arrayClass)];
-	object.vertexArray.bind();
-	object.program->bind();
+	for (gl::ModelShaders::ShaderObject& shader : shaders->shaderObjects) {
+		shader.cachedData.clear();
+	}
+	iterateModelPolygons(model, context, [&](const Polygon& polygon)
+	{
+		const int index = static_cast<int>(classifyPolygon(polygon));
+		std::vector<gl::ModelShaders::Vertex>& vertexBuffer = shaders->shaderObjects[index].cachedData;
+		auto vertexRing = iter::ring(polygon.vertices, polygon.numPolygonVertices());
+		reserveMore(vertexBuffer, polygon.numPolygonVertices());
+		const QColor color = getColorForPolygon(polygon, preferences, colorTable);
+		for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1)
+		{
+			const glm::vec3& v1 = vertexRing[i - 1];
+			const glm::vec3& v2 = vertexRing[i];
+			const glm::vec3& v3 = vertexRing[i + 1];
+			gl::ModelShaders::Vertex& vertex = vertexBuffer.emplace_back();
+			vertex.position = polygon.vertices[i];
+			vertex.normal = glm::normalize(glm::cross(v1 - v2, v3 - v2));
+			vertex.color = glm::vec4{color.redF(), color.greenF(), color.blueF(), color.alphaF()};
+			vertex.id = polygon.id.value;
+		}
+	});
+	for (gl::ModelShaders::ShaderObject& shader : shaders->shaderObjects)
+	{
+		shader.vertexCount = shader.cachedData.size();
+		shader.buffer.bind();
+		const int bytes = static_cast<int>(shader.cachedData.size() * sizeof shader.cachedData[0]);
+		shader.buffer.allocate(shader.cachedData.data(), bytes);
+		shader.buffer.release();
+	}
 }
 
-void gl::Compiler::releaseVertexArray(gl::ArrayClass arrayClass)
+ldraw::id_t gl::idFromColor(const std::array<GLubyte, 3>& data)
 {
-	auto& object = this->glObjects[static_cast<int>(arrayClass)];
-	object.program->release();
-	object.vertexArray.release();
+	return {data[0] * std::int32_t{0x10000} + data[1] * std::int32_t{0x100} + data[2]};
+}
+
+void gl::bindModelShaderVertexArray(gl::ModelShaders* shaders, gl::ArrayClass arrayClass)
+{
+	ModelShaders::ShaderObject& shaderObject = shaders->shaderObjects[static_cast<int>(arrayClass)];
+	shaderObject.vertexArray.bind();
+	shaderObject.program->bind();
 }
 
-void gl::Compiler::setSelectedObjects(const QSet<ldraw::id_t> ids)
+void gl::releaseModelShaderVertexArray(gl::ModelShaders* shaders, gl::ArrayClass arrayClass)
 {
-	for (auto& object : this->glObjects)
+	ModelShaders::ShaderObject& shaderObject = shaders->shaderObjects[static_cast<int>(arrayClass)];
+	shaderObject.program->release();
+	shaderObject.vertexArray.release();
+}
+
+void gl::setModelShaderSelectedObjects(gl::ModelShaders* shaders, const QSet<ldraw::id_t>& ids)
+{
+	for (ModelShaders::ShaderObject& object : shaders->shaderObjects)
 	{
-		std::vector<Vertex>& vector = object.cachedData;
-		for (Vertex& vertex : vector)
+		std::vector<ModelShaders::Vertex>& vector = object.cachedData;
+		for (ModelShaders::Vertex& vertex : vector)
 		{
 			vertex.selected = (ids.contains({vertex.id})) ? 1 : 0;
 		}
@@ -342,7 +346,7 @@
 	}
 }
 
-std::size_t gl::Compiler::vertexCount(const gl::ArrayClass arrayClass) const
+std::size_t gl::vertexCount(const gl::ModelShaders* shaders, const gl::ArrayClass arrayClass)
 {
-	return this->storedVertexCounts[static_cast<int>(arrayClass)];
+	return shaders->shaderObjects[static_cast<int>(arrayClass)].vertexCount;
 }
--- a/src/gl/compiler.h	Tue May 24 16:11:10 2022 +0300
+++ b/src/gl/compiler.h	Wed May 25 12:01:58 2022 +0300
@@ -33,73 +33,66 @@
 
 namespace gl
 {
-	class Compiler;
-}
+	struct ModelShaders
+	{
+		struct Vertex
+		{
+			glm::vec3 position;
+			glm::vec4 color;
+			glm::vec3 normal;
+			glm::int32 id;
+			glm::int32 selected = 0;
+		};
+		bool initialized = false;
+		struct ShaderObject
+		{
+			std::unique_ptr<QOpenGLShaderProgram> program = nullptr;
+			QOpenGLShaderProgram* pickSceneProgram = nullptr;
+			QOpenGLBuffer buffer{QOpenGLBuffer::VertexBuffer};
+			QOpenGLVertexArrayObject vertexArray;
+			std::vector<Vertex> cachedData;
+			std::size_t vertexCount;
+		} shaderObjects[gl::NUM_POLYGON_TYPES];
+	};
 
-class gl::Compiler : public QObject, protected QOpenGLExtraFunctions
-{
-	Q_OBJECT
-public:
-	Compiler(Model* model, const ldraw::ColorTable& colorTable, QObject* parent);
-	~Compiler();
-	void build(DocumentManager* context, const RenderPreferences& preferences);
-	std::size_t vertexCount(gl::ArrayClass arrayClass) const;
-	QColor getColorForPolygon(const gl::Polygon& polygon, const RenderPreferences& preferences);
-	glm::vec3 modelCenter() const;
-	double modelDistance() const;
-	void initialize();
-	void bindVertexArray(gl::ArrayClass arrayClass);
-	void releaseVertexArray(gl::ArrayClass arrayClass);
-	void buildShaders(int arrayId);
-	void setSelectedObjects(const QSet<ldraw::id_t> ids);
-
-	static ldraw::id_t idFromColor(const std::array<GLubyte, 3>& data);
+	void build(
+		ModelShaders* shaders,
+		Model *model,
+		const ldraw::ColorTable& colorTable,
+		DocumentManager* context,
+		const RenderPreferences& preferences);
+	void initializeModelShaders(ModelShaders* modelShaders);
+	void bindModelShaderVertexArray(gl::ModelShaders* shaders, gl::ArrayClass arrayClass);
+	void releaseModelShaderVertexArray(gl::ModelShaders* shaders, gl::ArrayClass arrayClass);
+	void setModelShaderSelectedObjects(gl::ModelShaders* shaders, const QSet<ldraw::id_t>& ids);
+	std::size_t vertexCount(const ModelShaders *shaders, gl::ArrayClass arrayClass);
+	ldraw::id_t idFromColor(const std::array<GLubyte, 3>& data);
 
 	template<typename T>
-	void setUniform(const char* uniformName, T&& value)
+	void setShaderUniform(gl::ModelShaders* shaders, const char* uniformName, T&& value)
 	{
-		Q_ASSERT(this->initialized);
-		for (auto& object : this->glObjects)
+		for (gl::ModelShaders::ShaderObject& shader : shaders->shaderObjects)
 		{
-			object.program->bind();
-			const int location = glGetUniformLocation(object.program->programId(), uniformName);
+			shader.program->bind();
+			const int location = glfunc.glGetUniformLocation(shader.program->programId(), uniformName);
 			Q_ASSERT(location != -1);
-			object.program->setUniformValue(location, std::forward<T>(value));
-			object.program->release();
+			shader.program->setUniformValue(location, std::forward<T>(value));
+			shader.program->release();
 		}
 	}
 
 	template<typename Float, glm::qualifier Prec>
-	void setUniformMatrix(const char* uniformName, const glm::mat<4, 4, Float, Prec>& value)
+	void setShaderUniformMatrix(
+		gl::ModelShaders* shaders,
+		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->setUniform(uniformName, *array);
+		setShaderUniform(shaders, uniformName, *array);
 	}
-private:
-	struct Vertex
-	{
-		glm::vec3 position;
-		glm::vec4 color;
-		glm::vec3 normal;
-		glm::int32 id;
-		glm::int32 selected = 0;
-	};
-	void buildPolygon(Polygon polygon, std::vector<Vertex>* vboData, const gl::RenderPreferences& preferences);
-	Model* const model;
-	std::size_t storedVertexCounts[gl::NUM_POLYGON_TYPES] = {0};
-	bool initialized = false;
-	BoundingBox boundingBox;
-	const ldraw::ColorTable& colorTable;
-	ldraw::id_t hovered = ldraw::NULL_ID;
-	struct
-	{
-		QOpenGLShaderProgram* program = nullptr;
-		QOpenGLShaderProgram* pickSceneProgram = nullptr;
-		QOpenGLBuffer buffer{QOpenGLBuffer::VertexBuffer};
-		QOpenGLVertexArrayObject vertexArray;
-		std::vector<Vertex> cachedData;
-	} glObjects[gl::NUM_POLYGON_TYPES];
-};
+
+	BoundingBox boundingBoxForModel(Model* model, DocumentManager* context);
+}
 
 #define CHECK_GL_ERROR() { checkGLError(__FILE__, __LINE__); }
 void checkGLError (QString file, int line);
--- a/src/gl/partrenderer.cpp	Tue May 24 16:11:10 2022 +0300
+++ b/src/gl/partrenderer.cpp	Wed May 25 12:01:58 2022 +0300
@@ -28,6 +28,7 @@
 
 static constexpr double MIN_ZOOM = -3.0;
 static constexpr double MAX_ZOOM = 3.0;
+QOpenGLFunctions glfunc;
 
 PartRenderer::PartRenderer(
 	Model* model,
@@ -37,8 +38,7 @@
 	QOpenGLWidget{parent},
 	model{model},
 	documents{documents},
-	colorTable{colorTable},
-	compiler{new gl::Compiler{model, this->colorTable, this}}
+	colorTable{colorTable}
 {
 	this->setMouseTracking(true);
 	connect(model, &Model::rowsInserted, [&]{
@@ -51,7 +51,7 @@
 {
 }
 
-static QVector3D vec3FromQColor(const QColor& color)
+static QVector3D calcQVector3DFromQColor(const QColor& color)
 {
 	return {
 		toFloat(color.redF()),
@@ -62,17 +62,16 @@
 
 void PartRenderer::initializeGL()
 {
-	this->initializeOpenGLFunctions();
+	::glfunc.initializeOpenGLFunctions();
 	if (glGetError() != GL_NO_ERROR)
 	{
 		abort();
 	}
-	this->compiler->initialize();
+	gl::initializeModelShaders(&this->shaders);
 	connect(this->model, &Model::dataChanged, this, &PartRenderer::build);
 	this->initialized = true;
 	this->modelQuaternion = glm::angleAxis(glm::radians(30.0f), glm::vec3{-1, 0, 0});
 	this->modelQuaternion *= glm::angleAxis(glm::radians(225.0f), glm::vec3{-0, 1, 0});
-	this->setupBackgroundColor();
 	this->updateModelMatrix();
 	this->updateViewMatrix();
 	this->update();
@@ -87,11 +86,11 @@
 		static_cast<float>(width) / static_cast<float>(height),
 		0.1f,
 		10000.f);
-	this->compiler->setUniformMatrix("projectionMatrix", this->projectionMatrix);
+	gl::setShaderUniformMatrix(&this->shaders, "projectionMatrix", this->projectionMatrix);
 	Q_EMIT projectionMatrixChanged(this->projectionMatrix);
 }
 
-static GLenum getGlTypeForArrayClass(const gl::ArrayClass vboClass)
+static constexpr GLenum getGlTypeForArrayClass(const gl::ArrayClass vboClass)
 {
 	switch (vboClass)
 	{
@@ -103,7 +102,7 @@
 	case gl::ArrayClass::Quads:
 		return GL_QUADS;
 	}
-	throw std::runtime_error{"Bad vbo class passed to getGlTypeForVboClass"};
+	throw std::runtime_error{"bad value for vboClass"};
 }
 
 void PartRenderer::paintGL()
@@ -117,12 +116,15 @@
 {
 	if (this->needBuild)
 	{
-		this->compiler->build(this->documents, this->renderPreferences);
+		gl::build(&this->shaders, this->model, this->colorTable, this->documents, this->renderPreferences);
+		this->boundingBox = gl::boundingBoxForModel(this->model, this->documents);
 		this->needBuild = false;
 	}
 	this->checkForGLErrors();
-	if (this->renderPreferences.lineAntiAliasing && this->renderPreferences.style != gl::RenderStyle::PickScene)
-	{
+	if (true
+		and this->renderPreferences.lineAntiAliasing
+		and this->renderPreferences.style != gl::RenderStyle::PickScene
+	) {
 		glEnable(GL_LINE_SMOOTH);
 		glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
 	}
@@ -137,27 +139,34 @@
 			static_cast<float>(backgroundColor.greenF()),
 			static_cast<float>(backgroundColor.blueF()),
 			1.0f);
-		this->compiler->setUniform("useLighting", GL_TRUE);
+		gl::setShaderUniform(&this->shaders, "useLighting", GL_TRUE);
 	}
 	else
 	{
 		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-		this->compiler->setUniform("useLighting", GL_FALSE);
+		gl::setShaderUniform(&this->shaders, "useLighting", GL_FALSE);
 	}
 	this->checkForGLErrors();
-	this->compiler->setUniform("selectedColor", vec3FromQColor(this->renderPreferences.selectedColor));
-	this->compiler->setUniform("highlighted", this->highlighted.value);
+	const QVector3D color = calcQVector3DFromQColor(this->renderPreferences.selectedColor);
+	gl::setShaderUniform(&this->shaders, "selectedColor", color);
+	gl::setShaderUniform(&this->shaders, "highlighted", this->highlighted.value);
 	this->checkForGLErrors();
 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 	glEnable(GL_DEPTH_TEST);
 	glEnable(GL_POLYGON_OFFSET_FILL);
 	glPolygonOffset(1.0f, 1.0f);
 	glLineWidth(this->renderPreferences.lineThickness);
+	const auto renderAllArrays = [this](){
+		// Lines need to be rendered last so that anti-aliasing does not interfere with polygon rendering.
+		this->renderVao(gl::ArrayClass::Triangles);
+		this->renderVao(gl::ArrayClass::Quads);
+		this->renderVao(gl::ArrayClass::Lines);
+	};
 	switch (this->renderPreferences.style)
 	{
 	case gl::RenderStyle::Normal:
 		this->setFragmentStyle(gl::FragmentStyle::Normal);
-		this->renderAllArrays();
+		renderAllArrays();
 		break;
 	case gl::RenderStyle::BfcRedGreen:
 		glEnable(GL_CULL_FACE);
@@ -172,59 +181,49 @@
 		glDisable(GL_CULL_FACE);
 		this->setFragmentStyle(gl::FragmentStyle::Normal);
 		renderVao(gl::ArrayClass::Lines);
+		break;
 	case gl::RenderStyle::RandomColors:
 		this->setFragmentStyle(gl::FragmentStyle::RandomColors);
-		this->renderAllArrays();
+		renderAllArrays();
 		break;
 	case gl::RenderStyle::PickScene:
 		glLineWidth(3.0f);
 		this->setFragmentStyle(gl::FragmentStyle::Id);
-		this->renderAllArrays();
+		renderAllArrays();
 		break;
 	case gl::RenderStyle::VertexPickScene:
 		glLineWidth(1.0f);
 		this->setFragmentStyle(gl::FragmentStyle::Black);
-		this->renderAllArrays();
+		renderAllArrays();
 		break;
 	case gl::RenderStyle::Wireframe:
 		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 		this->setFragmentStyle(gl::FragmentStyle::Normal);
-		this->renderAllArrays();
+		renderAllArrays();
 		break;
 	}
 	glDisable(GL_POLYGON_OFFSET_FILL);
 }
 
-void PartRenderer::renderAllArrays()
-{
-	// Lines need to be rendered last so that anti-aliasing does not interfere with polygon rendering.
-	renderVao(gl::ArrayClass::Triangles);
-	renderVao(gl::ArrayClass::Quads);
-	renderVao(gl::ArrayClass::Lines);
-}
-
 
 void PartRenderer::updateViewMatrix()
 {
 	// I'm not quite sure why using the exponent function on the zoom factor causes linear zoom behavior
-	const double z  = 2 * std::exp(this->zoom) * (1 + this->compiler->modelDistance());
+	const float modelDistance = longestMeasure(this->boundingBox);
+	const double z  = 2.0 * std::exp(this->zoom) * (1 + static_cast<double>(modelDistance));
 	this->viewMatrix = glm::lookAt(glm::vec3{0, 0, z}, {0, 0, 0}, {0, -1, 0});
-	this->compiler->setUniformMatrix("viewMatrix", this->viewMatrix);
+	gl::setShaderUniformMatrix(&this->shaders, "viewMatrix", this->viewMatrix);
 	Q_EMIT this->viewMatrixChanged(this->viewMatrix);
 }
 
 void PartRenderer::updateModelMatrix()
 {
 	this->modelMatrix = glm::mat4_cast(this->modelQuaternion);
-	this->compiler->setUniformMatrix("modelMatrix", modelMatrix);
+	gl::setShaderUniformMatrix(&this->shaders, "modelMatrix", modelMatrix);
 	Q_EMIT this->modelMatrixChanged(this->modelMatrix);
 	this->update();
 }
 
-void PartRenderer::setupBackgroundColor()
-{
-}
-
 void PartRenderer::build()
 {
 	this->needBuild = true;
@@ -232,12 +231,12 @@
 
 void PartRenderer::renderVao(const gl::ArrayClass arrayClass)
 {
-	this->compiler->bindVertexArray(arrayClass);
-	const std::size_t vertexCount = this->compiler->vertexCount(arrayClass);
+	gl::bindModelShaderVertexArray(&this->shaders, arrayClass);
+	const std::size_t vertexCount = gl::vertexCount(&this->shaders, arrayClass);
 	this->checkForGLErrors();
 	glDrawArrays(getGlTypeForArrayClass(arrayClass), 0, static_cast<GLsizei>(vertexCount));
 	this->checkForGLErrors();
-	this->compiler->releaseVertexArray(arrayClass);
+	gl::releaseModelShaderVertexArray(&this->shaders, arrayClass);
 	this->checkForGLErrors();
 }
 
@@ -365,7 +364,7 @@
 	this->checkForGLErrors();
 	this->renderPreferences.style = oldRenderStyle;
 	this->update();
-	return gl::Compiler::idFromColor(data);
+	return gl::idFromColor(data);
 }
 
 /**
@@ -374,7 +373,7 @@
  */
 void PartRenderer::setFragmentStyle(gl::FragmentStyle newFragmentStyle)
 {
-	this->compiler->setUniform("fragmentStyle", static_cast<int>(newFragmentStyle));
+	gl::setShaderUniform(&this->shaders, "fragmentStyle", static_cast<int>(newFragmentStyle));
 }
 
 /**
@@ -389,7 +388,6 @@
 	if (mainColorChanged or backgroundColorChanged)
 	{
 		this->build();
-		this->setupBackgroundColor();
 	}
 	Q_EMIT this->renderPreferencesChanged();
 	this->update();
--- a/src/gl/partrenderer.h	Tue May 24 16:11:10 2022 +0300
+++ b/src/gl/partrenderer.h	Wed May 25 12:01:58 2022 +0300
@@ -11,7 +11,7 @@
 #include "gl/common.h"
 #include "gl/compiler.h"
 
-class PartRenderer : public QOpenGLWidget, protected QOpenGLFunctions
+class PartRenderer : public QOpenGLWidget
 {
 	Q_OBJECT
 public:
@@ -33,7 +33,8 @@
 	Model* const model;
 	DocumentManager* const documents;
 	const ldraw::ColorTable& colorTable;
-	gl::Compiler* const compiler;
+	BoundingBox boundingBox;
+	gl::ModelShaders shaders;
 	ldraw::id_t highlighted = ldraw::NULL_ID;
 	std::optional<glm::vec3> screenToModelCoordinates(const QPoint& point, const geom::Plane& plane) const;
 	QPointF modelToScreenCoordinates(const glm::vec3& point) const;
@@ -53,11 +54,9 @@
 	void renderPreferencesChanged();
 private:
 	void setFragmentStyle(gl::FragmentStyle fragStyle);
-	void renderAllArrays();
 	void renderScene();
 	void updateViewMatrix();
 	void updateModelMatrix();
-	void setupBackgroundColor();
 	Q_SLOT void build();
 	double zoom = 1.0;
 	bool initialized = false;
--- a/src/types/boundingbox.cpp	Tue May 24 16:11:10 2022 +0300
+++ b/src/types/boundingbox.cpp	Wed May 25 12:01:58 2022 +0300
@@ -18,20 +18,19 @@
 
 #include "boundingbox.h"
 
-BoundingBox& BoundingBox::operator<<(const glm::vec3& vertex)
+/**
+ * @brief Resizes @c box so that @c vertex fits inside
+ * @param box
+ * @param vertex
+ */
+void addPointToBox(BoundingBox &box, const glm::vec3 &vertex)
 {
-	this->consider(vertex);
-	return *this;
-}
-
-void BoundingBox::consider(const glm::vec3& vertex)
-{
-	this->minimum.x = math::min(vertex.x, this->minimum.x);
-	this->minimum.y = math::min(vertex.y, this->minimum.y);
-	this->minimum.z = math::min(vertex.z, this->minimum.z);
-	this->maximum.x = math::max(vertex.x, this->maximum.x);
-	this->maximum.y = math::max(vertex.y, this->maximum.y);
-	this->maximum.z = math::max(vertex.z, this->maximum.z);
+	box.minimum.x = math::min(vertex.x, box.minimum.x);
+	box.minimum.y = math::min(vertex.y, box.minimum.y);
+	box.minimum.z = math::min(vertex.z, box.minimum.z);
+	box.maximum.x = math::max(vertex.x, box.maximum.x);
+	box.maximum.y = math::max(vertex.y, box.maximum.y);
+	box.maximum.z = math::max(vertex.z, box.maximum.z);
 }
 
 /*
--- a/src/types/boundingbox.h	Tue May 24 16:11:10 2022 +0300
+++ b/src/types/boundingbox.h	Wed May 25 12:01:58 2022 +0300
@@ -20,19 +20,16 @@
 #include "basics.h"
 #include "maths.h"
 
-class BoundingBox
+struct BoundingBox
 {
-public:
-	void consider(const glm::vec3& vertex);
 	glm::vec3 minimum {math::infinity, math::infinity, math::infinity};
 	glm::vec3 maximum {-math::infinity, -math::infinity, -math::infinity};
-	BoundingBox& operator<<(const glm::vec3& v);
 };
 
-inline const BoundingBox emptyBoundingBox = {};
+constexpr BoundingBox emptyBoundingBox = {};
 glm::vec3 boxCenter(const BoundingBox& box);
 float longestMeasure(const BoundingBox& box);
 float spaceDiagonal(const BoundingBox& box);
-
+void addPointToBox(BoundingBox& box, const glm::vec3& vertex);
 bool operator==(const BoundingBox& box_1, const BoundingBox& box_2);
 bool operator!=(const BoundingBox& box_1, const BoundingBox& box_2);
--- a/src/ui/canvas.cpp	Tue May 24 16:11:10 2022 +0300
+++ b/src/ui/canvas.cpp	Wed May 25 12:01:58 2022 +0300
@@ -22,7 +22,7 @@
 	Q_ASSERT(not selectedIds.contains(ldraw::NULL_ID));
 	this->selection.subtract(deselectedIds);
 	this->selection.unite(selectedIds);
-	this->compiler->setSelectedObjects(this->selection);
+	gl::setModelShaderSelectedObjects(&this->shaders, this->selection);
 	this->update();
 }
 
@@ -378,7 +378,7 @@
 void Canvas::clearSelection()
 {
 	this->selection.clear();
-	this->compiler->setSelectedObjects(this->selection);
+	gl::setModelShaderSelectedObjects(&this->shaders, this->selection);
 	Q_EMIT selectionChanged(this->selection);
 	this->update();
 }
@@ -390,7 +390,7 @@
 void Canvas::addToSelection(ldraw::id_t id)
 {
 	this->selection.insert(id);
-	this->compiler->setSelectedObjects(this->selection);
+	gl::setModelShaderSelectedObjects(&this->shaders, this->selection);
 	Q_EMIT selectionChanged(this->selection);
 	this->update();
 }

mercurial