things

Wed, 01 Jan 2020 17:45:56 +0200

author
Teemu Piippo <teemu@hecknology.net>
date
Wed, 01 Jan 2020 17:45:56 +0200
changeset 21
0133e565e072
parent 20
cef43609a374
child 22
6da867fa5429

things

CMakeLists.txt file | annotate | diff | comparison | revisions
src/basics.h file | annotate | diff | comparison | revisions
src/colors.h file | annotate | diff | comparison | revisions
src/document.cpp file | annotate | diff | comparison | revisions
src/document.h file | annotate | diff | comparison | revisions
src/document.ui file | annotate | diff | comparison | revisions
src/documentmanager.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/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/invert.cpp file | annotate | diff | comparison | revisions
src/invert.h file | annotate | diff | comparison | revisions
src/linetypes/edge.cpp file | annotate | diff | comparison | revisions
src/linetypes/edge.h file | annotate | diff | comparison | revisions
src/linetypes/object.cpp file | annotate | diff | comparison | revisions
src/linetypes/object.h file | annotate | diff | comparison | revisions
src/linetypes/quadrilateral.cpp file | annotate | diff | comparison | revisions
src/linetypes/quadrilateral.h file | annotate | diff | comparison | revisions
src/linetypes/subfilereference.cpp file | annotate | diff | comparison | revisions
src/linetypes/subfilereference.h file | annotate | diff | comparison | revisions
src/linetypes/triangle.cpp file | annotate | diff | comparison | revisions
src/linetypes/triangle.h file | annotate | diff | comparison | revisions
src/main.h file | annotate | diff | comparison | revisions
src/mainwindow.cpp file | annotate | diff | comparison | revisions
src/matrix.cpp file | annotate | diff | comparison | revisions
src/matrix.h file | annotate | diff | comparison | revisions
src/model.cpp file | annotate | diff | comparison | revisions
src/model.h file | annotate | diff | comparison | revisions
src/parser.cpp file | annotate | diff | comparison | revisions
src/vertex.cpp file | annotate | diff | comparison | revisions
src/vertex.h file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Sat Dec 14 23:00:01 2019 +0200
+++ b/CMakeLists.txt	Wed Jan 01 17:45:56 2020 +0200
@@ -2,6 +2,7 @@
 cmake_minimum_required(VERSION 2.8.12)
 set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake")
 include(cotire)
+set(OpenGL_GL_PREFERENCE GLVND)
 find_package(Qt5Widgets REQUIRED)
 if (Qt5Widgets_VERSION VERSION_LESS 5.5.0)
 	message(FATAL_ERROR "Qt5 version 5.5 required")
@@ -51,6 +52,7 @@
 	src/colors.h
 	src/document.h
 	src/documentmanager.h
+	src/header.h
 	src/invert.h
 	src/libraries.h
 	src/main.h
--- a/src/basics.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/basics.h	Wed Jan 01 17:45:56 2020 +0200
@@ -36,9 +36,9 @@
 
 enum Axis
 {
-	X,
-	Y,
-	Z
+	X = 0,
+	Y = 1,
+	Z = 2
 };
 
 enum Winding
@@ -65,3 +65,9 @@
 	one = one ^ other;
 	return one;
 }
+
+template<typename T, int N>
+constexpr int countof(T(&)[N])
+{
+	return N;
+}
--- a/src/colors.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/colors.h	Wed Jan 01 17:45:56 2020 +0200
@@ -6,6 +6,36 @@
 	qint32 index;
 };
 
+inline bool operator==(const Color& one, const Color& other)
+{
+	return one.index == other.index;
+}
+
+inline bool operator!=(const Color& one, const Color& other)
+{
+	return one.index != other.index;
+}
+
+inline bool operator<(const Color& one, const Color& other)
+{
+	return one.index < other.index;
+}
+
+inline bool operator<=(const Color& one, const Color& other)
+{
+	return one.index <= other.index;
+}
+
+inline bool operator>(const Color& one, const Color& other)
+{
+	return one.index > other.index;
+}
+
+inline bool operator>=(const Color& one, const Color& other)
+{
+	return one.index >= other.index;
+}
+
 namespace colors
 {
 	static constexpr Color black {0};
--- a/src/document.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/document.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -2,13 +2,18 @@
 #include "ui_document.h"
 #include "model.h"
 
-Document::Document(Model* model, QWidget* parent) :
+Document::Document(Model* model, DocumentManager* documents, QWidget* parent) :
 	QWidget{parent},
 	model{model},
+	documents{documents},
+	renderer{new PartRenderer{model, documents, this}},
 	ui{*new Ui::Document}
 {
 	this->ui.setupUi(this);
 	this->ui.listView->setModel(model);
+	QVBoxLayout* layout = new QVBoxLayout;
+	layout->addWidget(this->renderer);
+	this->ui.viewportFrame->setLayout(layout);
 	connect(this->ui.splitter, &QSplitter::splitterMoved, this, &Document::splitterChanged);
 }
 
--- a/src/document.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/document.h	Wed Jan 01 17:45:56 2020 +0200
@@ -1,6 +1,7 @@
 #pragma once
 #include <memory>
 #include <QWidget>
+#include "gl/partrenderer.h"
 
 namespace Ui
 {
@@ -13,7 +14,7 @@
 {
 	Q_OBJECT
 public:
-	explicit Document(Model* model, QWidget *parent = nullptr);
+	explicit Document(Model* model, DocumentManager* documents, QWidget *parent = nullptr);
 	~Document();
 	QByteArray saveSplitterState() const;
 	void restoreSplitterState(const QByteArray& state);
@@ -21,5 +22,7 @@
 	void splitterChanged();
 private:
 	Model* model;
+	DocumentManager* const documents;
+	PartRenderer* renderer;
 	Ui::Document& ui;
 };
--- a/src/document.ui	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/document.ui	Wed Jan 01 17:45:56 2020 +0200
@@ -19,19 +19,19 @@
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
-     <widget class="PartRenderer" name="viewport"/>
      <widget class="QListView" name="listView"/>
+     <widget class="QFrame" name="viewportFrame">
+      <property name="frameShape">
+       <enum>QFrame::StyledPanel</enum>
+      </property>
+      <property name="frameShadow">
+       <enum>QFrame::Raised</enum>
+      </property>
+     </widget>
     </widget>
    </item>
   </layout>
  </widget>
- <customwidgets>
-  <customwidget>
-   <class>PartRenderer</class>
-   <extends>QOpenGLWidget</extends>
-   <header>gl/partrenderer.h</header>
-  </customwidget>
- </customwidgets>
  <resources/>
  <connections/>
 </ui>
--- a/src/documentmanager.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/documentmanager.h	Wed Jan 01 17:45:56 2020 +0200
@@ -7,11 +7,7 @@
 	Q_OBJECT
 	using ModelPointer = std::unique_ptr<Model>;
 public:
-	DocumentManager(QObject* parent = nullptr);
-	DocumentManager(const DocumentManager&) = delete;
-	DocumentManager(DocumentManager&&) = default;
-	DocumentManager& operator=(const DocumentManager&) = delete;
-	DocumentManager& operator=(DocumentManager&&) = default;
+    DocumentManager(QObject* parent = nullptr);
 	QString newModel();
 	Model* findModelByName(const QString& name);
 	QString openModel(const QString& path, QTextStream& errorStream);
--- a/src/gl/common.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/gl/common.h	Wed Jan 01 17:45:56 2020 +0200
@@ -55,6 +55,7 @@
 	Type type;
 	Point3D vertices[4];
 	Color color;
+	linetypes::Id id;
 
 	/**
 	 * @return amount of vertices used for geometry
@@ -91,14 +92,19 @@
 
 namespace gl
 {
-	inline Polygon edgeLine(const Point3D& v_1, const Point3D& v_2, Color color)
+	inline Polygon edgeLine(const Point3D& v_1, const Point3D& v_2, Color color, linetypes::Id id)
 	{
-		return {Polygon::EdgeLine, {v_1, v_2}, color};
+		return {Polygon::EdgeLine, {v_1, v_2}, color, id};
 	}
 
-	inline Polygon triangle(const Point3D& v_1, const Point3D& v_2, const Point3D& v_3, Color color)
+	inline Polygon triangle(
+		const Point3D& v_1,
+		const Point3D& v_2,
+		const Point3D& v_3,
+		Color color,
+		linetypes::Id id)
 	{
-		return {Polygon::Triangle, {v_1, v_2, v_3}, color};
+		return {Polygon::Triangle, {v_1, v_2, v_3}, color, id};
 	}
 
 	inline Polygon quadrilateral(
@@ -106,9 +112,10 @@
 		const Point3D& v_2,
 		const Point3D& v_3,
 		const Point3D& v_4,
-		Color color)
+		Color color,
+		linetypes::Id id)
 	{
-		return {Polygon::Quadrilateral, {v_1, v_2, v_3, v_4}, color};
+		return {Polygon::Quadrilateral, {v_1, v_2, v_3, v_4}, color, id};
 	}
 
 	inline Polygon conditionalEdge(
@@ -116,13 +123,14 @@
 		const Point3D& v_2,
 		const Point3D& control_1,
 		const Point3D& control_2,
-		Color color)
+		Color color,
+		linetypes::Id id)
 	{
-		return {Polygon::ConditionalEdge, {v_1, v_2, control_1, control_2}, color};
+		return {Polygon::ConditionalEdge, {v_1, v_2, control_1, control_2}, color, id};
 	}
 
 	// Vbo names
-	enum class VboClass
+	enum class VboClass : std::uint8_t
 	{
 		Lines,
 		Triangles,
@@ -132,7 +140,7 @@
 	constexpr int numVboClasses = 4;
 
 	// Types of vbo per object
-	enum class VboSubclass
+	enum class VboSubclass : std::uint8_t
 	{
 		Surfaces,
 		RegularColors,
--- a/src/gl/compiler.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/gl/compiler.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -24,7 +24,222 @@
 #include "invert.h"
 #include "ring.h"
 
+gl::Compiler::Compiler(QObject* parent) :
+	QObject{parent}
+{
+}
+
 gl::Compiler::~Compiler()
 {
 
 }
+
+void gl::Compiler::build(Model* model, DocumentManager* context)
+{
+	if (not this->initialized)
+	{
+		this->initializeVbo();
+	}
+	std::vector<GLfloat> vboData[gl::numVbos];
+	const std::vector<gl::Polygon> polygons = model->getPolygons(context);
+	for (const gl::Polygon& polygon : polygons)
+	{
+		this->buildPolygon(polygon, vboData);
+	}
+	for (int i = 0; i < gl::numVbos; i += 1)
+	{
+		this->upload(i, vboData[i]);
+	}
+}
+
+gl::VboClass classifyPolygon(const gl::Polygon& polygon)
+{
+	switch (polygon.type)
+	{
+	case gl::Polygon::EdgeLine:
+		return gl::VboClass::Lines;
+	case gl::Polygon::Triangle:
+		return gl::VboClass::Triangles;
+	case gl::Polygon::Quadrilateral:
+		return gl::VboClass::Quads;
+	case gl::Polygon::ConditionalEdge:
+		return gl::VboClass::ConditionalLines;
+	}
+	return gl::VboClass::Lines;
+}
+
+QColor colorFromId(linetypes::Id id)
+{
+	// Calculate a color based from this index. This method caters for
+	// 16777216 objects. I don't think that will be exceeded anytime soon.
+	const int r = (id.value / 0x10000) % 0x100;
+	const int g = (id.value / 0x100) % 0x100;
+	const int b = id.value % 0x100;
+	return {r, g, b};
+}
+
+void writeVertex(std::vector<GLfloat>& data, const Point3D& point)
+{
+	data.push_back(point.x);
+	data.push_back(point.y);
+	data.push_back(point.z);
+}
+
+void gl::Compiler::buildPolygon(gl::Polygon polygon, std::vector<GLfloat>* vboData)
+{
+	const gl::VboClass vboClass = classifyPolygon(polygon);
+	auto vboBuffer = [&](VboSubclass subclass) -> std::vector<GLfloat>&
+	{
+		return vboData[gl::vboIndex({vboClass, subclass})];
+	};
+	auto vertexRing = iter::ring(polygon.vertices, polygon.numPolygonVertices());
+	for (int i = 0; i < polygon.numPolygonVertices(); i += 1)
+	{
+		const Point3D& v1 = vertexRing[i - 1];
+		const Point3D& v2 = vertexRing[i];
+		const Point3D& v3 = vertexRing[i + 1];
+		const QVector3D normal = QVector3D::crossProduct(v3 - v2, v1 - v2).normalized();
+		std::vector<GLfloat>& data = vboBuffer(VboSubclass::Normals);
+		for (const GLfloat coord : {normal.x(), normal.y(), normal.z()})
+			data.push_back(coord);
+	}
+	vboBuffer(VboSubclass::Surfaces).reserve(vboBuffer(VboSubclass::Surfaces).size() + polygon.numPolygonVertices());
+	// Transform vertices so that they're suitable for GL rendering
+	for (int i = 0; i < polygon.numPolygonVertices(); i += 1)
+	{
+		polygon.vertices[i].y = -polygon.vertices[i].y;
+		polygon.vertices[i].z = -polygon.vertices[i].z;
+#if 0
+		// Add these vertices to the bounding box (unless we're going to do it over
+		// from scratch afterwards anyway)
+		if (not this->needBoundingBoxRebuild)
+		{
+			this->boundingBox.consider(poly.vertices[i]);
+		}
+#endif
+		writeVertex(vboBuffer(VboSubclass::Surfaces), polygon.vertices[i]);
+	}
+	this->writeColor(vboData, polygon, VboSubclass::RegularColors);
+	this->writeColor(vboData, polygon, VboSubclass::PickColors);
+	this->writeColor(vboData, polygon, VboSubclass::BfcFrontColors);
+	this->writeColor(vboData, polygon, VboSubclass::BfcBackColors);
+}
+
+QColor gl::Compiler::getColorForPolygon(const gl::Polygon& polygon, VboSubclass subclass)
+{
+	QColor color;
+
+	switch (subclass)
+	{
+	case VboSubclass::Surfaces:
+	case VboSubclass::Normals:
+	case VboSubclass::InvertedNormals:
+		// Surface and normal VBOs contain vertex data, not colors. So we can't return anything meaningful.
+		return {};
+	case VboSubclass::BfcFrontColors:
+		// Use the constant green color for BFC front colors
+		return {64, 192, 80};
+	case VboSubclass::BfcBackColors:
+		// Use the constant red color for BFC back colors
+		return {208, 64, 64};
+	case VboSubclass::PickColors:
+		// For the picking scene, use unique picking colors provided by the model.
+		return colorFromId(polygon.id);
+	case VboSubclass::RandomColors:
+		// For the random color scene, the owner object has rolled up a random color. Use that.
+		color = {255, 64, 255}; // polygonOwner->randomColor();
+		break;
+	case VboSubclass::RegularColors:
+		// For normal colors, use the polygon's color.
+		if (polygon.color == colors::main)
+		{
+			color = {255, 255, 64}; // mainColorRepresentation();
+		}
+		else if (polygon.color == colors::edge)
+		{
+			// Edge color is black, unless we have a dark background, in which case lines need to be bright.
+			color = Qt::black; //luma(config::backgroundColor()) > 40 ? Qt::black : Qt::white;
+		}
+		else
+		{
+			// Not main or edge color, use the polygon's color as is.
+			color = {255, 255, 64}; //polygon.color.faceColor();
+		}
+		break;
+	}
+
+	if (color.isValid())
+	{
+		// We may wish to apply blending on the color to indicate selection or highlight.
+		/*
+		const double blendAlpha = 0.0;
+		if (blendAlpha != 0.0)
+		{
+			QColor selectedColor = config::selectColorBlend();
+			double denominator = blendAlpha + 1.0;
+			color.setRed((color.red() + (selectedColor.red() * blendAlpha)) / denominator);
+			color.setGreen((color.green() + (selectedColor.green() * blendAlpha)) / denominator);
+			color.setBlue((color.blue() + (selectedColor.blue() * blendAlpha)) / denominator);
+		}
+		*/
+	}
+	else
+	{
+		if (polygon.numPolygonVertices() == 2)
+			color = Qt::black;
+		else
+			color = {255, 255, 64};
+	}
+
+	return color;
+}
+
+void gl::Compiler::writeColor(std::vector<GLfloat>* data, const gl::Polygon& polygon, VboSubclass subclass)
+{
+	std::vector<GLfloat>& buffer = data[gl::vboIndex({classifyPolygon(polygon), subclass})];
+	const QColor color = this->getColorForPolygon(polygon, subclass);
+	buffer.reserve(data->size() + 4 * polygon.numPolygonVertices());
+	for (int i = 0; i < polygon.numPolygonVertices(); i += 1)
+	{
+		buffer.push_back(color.redF() / 255.0f);
+		buffer.push_back(color.greenF() / 255.0f);
+		buffer.push_back(color.blueF() / 255.0f);
+		buffer.push_back(color.alphaF() / 255.0f);
+	}
+}
+
+void gl::Compiler::initializeVbo()
+{
+	this->initializeOpenGLFunctions();
+	glGenBuffers(countof(this->storedVbo), &this->storedVbo[0]);
+	this->initialized = true;
+}
+
+///
+/// \brief Uploads data to a vbo
+/// \param vboAddress Which vbo to upload to
+/// \param data Data to upload to vbo
+///
+void gl::Compiler::upload(const int vboIndex, const std::vector<GLfloat>& data)
+{
+	glBindBuffer(GL_ARRAY_BUFFER, this->storedVbo[vboIndex]);
+	glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof data[0], data.data(), GL_STATIC_DRAW);
+	glBindBuffer(GL_ARRAY_BUFFER, 0);
+	this->storedVboSizes[vboIndex] = data.size();
+}
+
+GLuint gl::Compiler::vbo(const VboAddress vboAddress) const
+{
+	return this->storedVbo[gl::vboIndex(vboAddress)];
+}
+
+int gl::Compiler::vboSize(const VboAddress vboAddress) const
+{
+	return this->storedVboSizes[gl::vboIndex(vboAddress)];
+}
+
+int gl::vboIndex(const VboAddress vboAddress)
+{
+	return static_cast<std::uint8_t>(vboAddress.vboClass) * gl::numVboSubclasses
+		+ static_cast<std::uint8_t>(vboAddress.vboSubclass);
+}
--- a/src/gl/compiler.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/gl/compiler.h	Wed Jan 01 17:45:56 2020 +0200
@@ -18,44 +18,45 @@
 
 #pragma once
 #include "main.h"
-#include "gl/partrenderer.h"
 #include "gl/common.h"
 #include "types/boundingbox.h"
 #include <QMap>
 #include <QSet>
 
+class Model;
+class DocumentManager;
+
 namespace gl
 {
 	class Compiler;
 	class Renderer;
+	struct VboAddress
+	{
+		VboClass vboClass;
+		VboSubclass vboSubclass;
+	};
+	int vboIndex(const VboAddress vboAddress);
 }
 
-/*
- * Compiles LDObjects into polygons for the GLRenderer to draw.
- */
 class gl::Compiler : public QObject, protected QOpenGLFunctions
 {
 	Q_OBJECT
 public:
-	Compiler(Renderer* renderer);
+	Compiler(QObject* parent);
 	~Compiler();
-	void initialize();
-	Point3D modelCenter();
-	void prepareVBO (int vbonum);
-	GLuint vbo (int vbonum) const;
-	int vboSize (int vbonum) const;
-	static int vboNumber (VboClass surface, VboSubclass complement);
+	void build(Model* model, DocumentManager* context);
+	void buildPolygon(Polygon polygon, std::vector<GLfloat>* vboData);
+	void upload(const int vboIndex, const std::vector<GLfloat>& data);
+	GLuint vbo(const VboAddress vboAddress) const;
+	int vboSize(const VboAddress vboAddress) const;
+	QColor getColorForPolygon(const gl::Polygon& polygon, VboSubclass subclass);
+	void writeColor(std::vector<GLfloat>* data, const gl::Polygon& polygon, VboSubclass subclass);
 private:
-	struct ObjectVboData
-	{
-		QVector<GLfloat> data[gl::numVbos];
-	};
-	QMap<QPersistentModelIndex, ObjectVboData> m_objectInfo;
-	QSet<QPersistentModelIndex> m_staged; // Objects that need to be compiled
-	GLuint m_vbo[gl::numVbos];
+	void initializeVbo();
+	GLuint storedVbo[gl::numVbos];
 	bool m_vboChanged[gl::numVbos] = {true};
-	bool needBoundingBoxRebuild = true;
-	int m_vboSizes[gl::numVbos] = {0};
+	int storedVboSizes[gl::numVbos] = {0};
+	bool initialized = false;
 };
 
 #define CHECK_GL_ERROR() { checkGLError(__FILE__, __LINE__); }
--- a/src/gl/partrenderer.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/gl/partrenderer.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -2,8 +2,11 @@
 #include <GL/glut.h> // teapot
 #include "partrenderer.h"
 
-PartRenderer::PartRenderer(QWidget* parent) :
-	QOpenGLWidget{parent}
+PartRenderer::PartRenderer(Model* model, DocumentManager* documents, QWidget* parent) :
+	QOpenGLWidget{parent},
+	model{model},
+	documents{documents},
+	compiler{new gl::Compiler{this}}
 {
 	this->setMouseTracking(true);
 }
@@ -19,6 +22,7 @@
 	this->initialized = true;
 	this->rotation = QQuaternion::fromAxisAndAngle({1, 0, 0}, 30);
 	this->rotation *= QQuaternion::fromAxisAndAngle({0, 1, 0}, 330);
+	this->compiler->build(this->model, this->documents);
 }
 
 /*
@@ -62,7 +66,23 @@
 	glMatrixMode(GL_MODELVIEW);
 }
 
+static int getGlTypeForVboClass(const gl::VboClass vboClass)
+{
+	switch (vboClass)
+	{
+	case gl::VboClass::Lines:
+	case gl::VboClass::ConditionalLines:
+		return GL_LINES;
+	case gl::VboClass::Triangles:
+		return GL_TRIANGLES;
+	case gl::VboClass::Quads:
+		return GL_QUADS;
+	}
+	throw std::runtime_error{"Bad vbo class passed to getGlTypeForVboClass"};
+}
+
 // https://www.codemiles.com/c-opengl-examples/drawing-teapot-using-opengl-t9010.html?mobile=on
+#include <QMessageBox>
 void PartRenderer::paintGL()
 {
 	switch (this->renderStyle)
@@ -77,7 +97,10 @@
 	}
 	glMatrixMode(GL_MODELVIEW);
 	// clear the drawing buffer.
-	glClear(GL_COLOR_BUFFER_BIT);
+	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	glEnable(GL_DEPTH_TEST);
+	glEnable(GL_LIGHTING);
 	// clear the identity matrix.
 	glLoadIdentity();
 	// traslate the draw by z = -4.0
@@ -86,8 +109,37 @@
 	// Red color used to draw.
 	glColor3f(0.8, 0.2, 0.1);
 	glMultMatrixf(padMatrix(this->rotation.toRotationMatrix()).constData());
-	glutSolidTeapot(1.0);
-	glFlush();
+	//glutSolidTeapot(1.0);
+	glEnableClientState(GL_VERTEX_ARRAY);
+	glEnableClientState(GL_COLOR_ARRAY);
+	for (const gl::VboClass vboClass : {gl::VboClass::Lines, gl::VboClass::Triangles, gl::VboClass::Quads})
+	{
+		const int vboSurfaces = this->compiler->vbo({vboClass, gl::VboSubclass::Surfaces});
+		const int vboColors = this->compiler->vbo({vboClass, gl::VboSubclass::RegularColors});
+		const int vboNormals = this->compiler->vbo({vboClass, gl::VboSubclass::Normals});
+		const int count = this->compiler->vboSize({vboClass, gl::VboSubclass::Surfaces}) / 3;
+		glBindBuffer(GL_ARRAY_BUFFER, vboSurfaces);
+		glVertexPointer(3, GL_FLOAT, 0, nullptr);
+		glBindBuffer(GL_ARRAY_BUFFER, vboColors);
+		glColorPointer(4, GL_FLOAT, 0, nullptr);
+		//glBindBuffer(GL_ARRAY_BUFFER, vboNormals);
+		//glNormalPointer(GL_FLOAT, 0, nullptr);
+		glDrawArrays(getGlTypeForVboClass(vboClass), 0, count);
+	}
+	glBindBuffer(GL_ARRAY_BUFFER, 0);
+	glDisableClientState(GL_VERTEX_ARRAY);
+	glDisableClientState(GL_COLOR_ARRAY);
+
+	//glFlush();
+	const int glError = this->glGetError();
+	if (glError != GL_NO_ERROR)
+	{
+		const QString glErrorString = QString::fromLatin1(reinterpret_cast<const char*>(::gluErrorString(glError)));
+		QMessageBox::critical(
+			this,
+			tr("Rendering error"),
+			QString{"Failed to render: %1"}.arg(glErrorString));
+	}
 }
 
 static QPointF pointToPointF(const QPoint& point)
@@ -118,6 +170,11 @@
 	this->lastMousePosition = pointToPointF(event->pos());
 }
 
+void PartRenderer::setCompiler(gl::Compiler* compiler)
+{
+	this->compiler = compiler;
+}
+
 void PartRenderer::setRenderStyle(const gl::RenderStyle newStyle)
 {
 	this->renderStyle = newStyle;
--- a/src/gl/partrenderer.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/gl/partrenderer.h	Wed Jan 01 17:45:56 2020 +0200
@@ -4,22 +4,28 @@
 #include <QQuaternion>
 #include "main.h"
 #include "gl/common.h"
+#include "gl/compiler.h"
 
 class PartRenderer : public QOpenGLWidget, protected QOpenGLFunctions
 {
+	Q_OBJECT
 public:
-	PartRenderer(QWidget* parent = nullptr);
+	PartRenderer(Model* model, DocumentManager* documents, QWidget* parent = nullptr);
 protected:
 	void initializeGL() override;
 	void resizeGL(int width, int height) override;
 	void paintGL() override;
 	void mouseMoveEvent(QMouseEvent* event) override;
+	void setCompiler(gl::Compiler* compiler);
 private slots:
 	void setRenderStyle(const gl::RenderStyle newStyle);
 private:
+	Model* const model;
+	DocumentManager* const documents;
 	QPointF lastMousePosition;
 	bool initialized = false;
 	gl::RenderStyle renderStyle = gl::RenderStyle::Normal;
 	QQuaternion rotation;
 	void initializeLighting();
+	gl::Compiler* compiler;
 };
--- a/src/invert.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/invert.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -20,6 +20,7 @@
 #include "model.h"
 #include "matrix.h"
 #include "gl/common.h"
+#include "invert.h"
 
 #if 0
 /*
@@ -72,21 +73,10 @@
 /*
  * Returns a matrix that causes a flip on the given dimension.
  */
-Matrix4x4 flipmatrix(Axis dimension)
+Matrix4x4 math::flipmatrix(const Axis dimension)
 {
 	Matrix4x4 result = identity4x4;
-	switch (dimension)
-	{
-	case X:
-		result(0, 0) = -1;
-		break;
-	case Y:
-		result(1, 1) = -1;
-		break;
-	case Z:
-		result(2, 2) = -1;
-		break;
-	}
+	result(static_cast<int>(dimension), static_cast<int>(dimension)) = -1;
 	return result;
 }
 
@@ -143,16 +133,18 @@
 /*
  * Inverts the winding of a polygon.
  */
-void invertPolygon(gl::Polygon& polygon)
+void gl::invert(gl::Polygon& polygon)
 {
 	switch (polygon.numPolygonVertices())
 	{
 	case 2:
 	case 3:
+		// 0 1 => 1 0
+		// 0 1 2 => 1 0 2
 		std::swap(polygon.vertices[0], polygon.vertices[1]);
 		break;
-
 	case 4:
+		// 0 1 2 3 => 0 3 2 1
 		std::swap(polygon.vertices[1], polygon.vertices[3]);
 		break;
 	}
--- a/src/invert.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/invert.h	Wed Jan 01 17:45:56 2020 +0200
@@ -22,6 +22,14 @@
 #include "gl/common.h"
 
 bool isflat(class Model* model, Axis* axis);
-QMatrix4x4 flipmatrix(Axis dimension);
+
+namespace math
+{
+	Matrix4x4 flipmatrix(Axis dimension);
+}
 //void invert(LDObject* obj, class DocumentManager* context);
-void invertPolygon(gl::Polygon& polygon);
+
+namespace gl
+{
+	void invert(gl::Polygon& polygon);
+}
--- a/src/linetypes/edge.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/linetypes/edge.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -48,3 +48,11 @@
 {
 	return utility::format("%1 %2", vertexToStringParens(point_1), vertexToStringParens(point_2));
 }
+
+void linetypes::Edge::getPolygons(
+	std::vector<gl::Polygon>& polygons,
+	GetPolygonsContext* context) const
+{
+	Q_UNUSED(context)
+	polygons.push_back(gl::edgeLine(this->point_1, this->point_2, this->colorIndex, this->id));
+}
--- a/src/linetypes/edge.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/linetypes/edge.h	Wed Jan 01 17:45:56 2020 +0200
@@ -19,6 +19,7 @@
 		Property property,
 		const QVariant& value) override;
 	QString textRepresentation() const override;
+	void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const override;
 private:
 	Point3D point_1 = {};
 	Point3D point_2 = {};
--- a/src/linetypes/object.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/linetypes/object.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -52,6 +52,12 @@
 	return {};
 }
 
+void linetypes::Object::getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const
+{
+	Q_UNUSED(polygons)
+	Q_UNUSED(context)
+}
+
 linetypes::ColoredObject::ColoredObject(const Color color_index) :
 	colorIndex{color_index}
 {
--- a/src/linetypes/object.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/linetypes/object.h	Wed Jan 01 17:45:56 2020 +0200
@@ -5,16 +5,24 @@
 #include "main.h"
 #include "colors.h"
 #include "vertex.h"
+#include "gl/common.h"
 
 namespace linetypes
 {
-	struct Id { unsigned int value; };
+	struct GetPolygonsContext;
 	enum class Property;
 	class Object;
 	class ColoredObject;
 	class Empty;
 }
 
+class DocumentManager;
+
+struct linetypes::GetPolygonsContext
+{
+	::DocumentManager* documents;
+};
+
 /**
  * @brief Different properties that can be queried with getProperty
  */
@@ -28,8 +36,7 @@
 	Point4, // Fourth vertex in a quadrilateral
 	ControlPoint1, // First control point in a conditional edge line
 	ControlPoint2, // Second control point in a conditional edge line
-	Position, // Position of a subfile reference
-	Transformation, // Transformation matrix of a subfile reference
+	Transformation, // 4x4 transformation matrix of a subfile reference
 	ReferenceName, // Subfile reference name
 	IsInverted, // Whether or not the object has been inverted with BFC INVERTNEXT
 	ErrorMessage // For error lines, why parsing failed
@@ -56,6 +63,7 @@
 	virtual QBrush textRepresentationForeground() const;
 	virtual QBrush textRepresentationBackground() const;
 	virtual QFont textRepresentationFont() const;
+	virtual void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const;
 };
 
 class linetypes::ColoredObject : public Object
@@ -65,7 +73,7 @@
 	bool hasColor() const override final;
 	QVariant getProperty(Property id) const override;
 	SetPropertyResult setProperty(Property id, const QVariant& value) override;
-private:
+protected:
 	Color colorIndex = colors::main;
 };
 
--- a/src/linetypes/quadrilateral.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/linetypes/quadrilateral.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -64,5 +64,19 @@
 		vertexToStringParens(points[0]),
 		vertexToStringParens(points[1]),
 		vertexToStringParens(points[2]),
-		vertexToStringParens(points[3]));
+			vertexToStringParens(points[3]));
 }
+
+void linetypes::Quadrilateral::getPolygons(
+	std::vector<gl::Polygon>& polygons,
+	GetPolygonsContext* context) const
+{
+	Q_UNUSED(context)
+	polygons.push_back(gl::quadrilateral(
+		this->points[0],
+		this->points[1],
+		this->points[2],
+		this->points[3],
+		this->colorIndex,
+		this->id));
+}
--- a/src/linetypes/quadrilateral.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/linetypes/quadrilateral.h	Wed Jan 01 17:45:56 2020 +0200
@@ -20,6 +20,7 @@
 	QVariant getProperty(Property id) const override;
 	SetPropertyResult setProperty(Property id, const QVariant& value) override;
 	QString textRepresentation() const override;
+	void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const override;
 private:
 	Point3D points[4] = {{}};
 };
--- a/src/linetypes/subfilereference.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/linetypes/subfilereference.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -1,12 +1,10 @@
 #include "subfilereference.h"
+#include "documentmanager.h"
 
-linetypes::SubfileReference::SubfileReference(
-	const Point3D& position,
-	const Matrix3x3& transformation,
+linetypes::SubfileReference::SubfileReference(const Matrix4x4& transformation,
 	const QString& referenceName,
 	const Color color) :
 	ColoredObject{color},
-	position{position},
 	transformation{transformation},
 	referenceName{referenceName}
 {
@@ -16,8 +14,6 @@
 {
 	switch (property)
 	{
-	case Property::Position:
-		return this->position;
 	case Property::Transformation:
 		return QVariant::fromValue(this->transformation);
 	case Property::ReferenceName:
@@ -34,11 +30,8 @@
 {
 	switch (property)
 	{
-	case Property::Position:
-		this->position = value.value<Point3D>();
-		return SetPropertyResult::Success;
 	case Property::Transformation:
-		this->transformation = value.value<Matrix3x3>();
+		this->transformation = value.value<Matrix4x4>();
 		return SetPropertyResult::Success;
 	case Property::ReferenceName:
 		this->referenceName = value.toString();
@@ -50,5 +43,40 @@
 
 QString linetypes::SubfileReference::textRepresentation() const
 {
-	return referenceName + " " + vertexToStringParens(this->position);
+	return referenceName + " " + vertexToStringParens(this->position());
 }
+
+void linetypes::SubfileReference::getPolygons(
+	std::vector<gl::Polygon>& polygons,
+	GetPolygonsContext* context) const
+{
+	Model* model = this->resolve(context->documents);
+	if (model != nullptr)
+	{
+		const std::vector<gl::Polygon> modelPolygons = model->getPolygons(context->documents);
+		polygons.reserve(polygons.size() + modelPolygons.size());
+		for (gl::Polygon polygon : modelPolygons)
+		{
+			for (int i = 0; i < polygon.numPolygonVertices(); i += 1)
+			{
+				polygon.vertices[i] = math::transform(polygon.vertices[1], this->transformation);
+			}
+			if (polygon.color == colors::main)
+			{
+				polygon.color = this->colorIndex;
+			}
+			polygon.id = this->id;
+			polygons.push_back(polygon);
+		}
+	}
+}
+
+Point3D linetypes::SubfileReference::position() const
+{
+	return {this->transformation(0, 3), this->transformation(1, 3), this->transformation(2, 3)};
+}
+
+Model* linetypes::SubfileReference::resolve(DocumentManager* documents) const
+{
+	return documents->findModelByName(this->referenceName);
+}
--- a/src/linetypes/subfilereference.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/linetypes/subfilereference.h	Wed Jan 01 17:45:56 2020 +0200
@@ -2,6 +2,8 @@
 #include "object.h"
 #include "matrix.h"
 
+class Model;
+
 namespace linetypes
 {
 	class SubfileReference;
@@ -12,15 +14,16 @@
 public:
 	SubfileReference() = default;
 	SubfileReference(
-		const Point3D& position,
-		const Matrix3x3& transformation,
+		const Matrix4x4& transformation,
 		const QString &referenceName,
 		const Color color = colors::main);
 	QVariant getProperty(Property property) const override;
 	SetPropertyResult setProperty(Property property, const QVariant& value) override;
 	QString textRepresentation() const override;
+	void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const override;
+	Point3D position() const;
 private:
-	Point3D position;
-	Matrix3x3 transformation;
+	Model* resolve(DocumentManager* documents) const;
+	Matrix4x4 transformation;
 	QString referenceName;
 };
--- a/src/linetypes/triangle.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/linetypes/triangle.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -57,3 +57,16 @@
 		vertexToStringParens(points[1]),
 		vertexToStringParens(points[2]));
 }
+
+void linetypes::Triangle::getPolygons(
+	std::vector<gl::Polygon>& polygons,
+	GetPolygonsContext* context) const
+{
+	Q_UNUSED(context)
+	polygons.push_back(gl::triangle(
+		this->points[0],
+		this->points[1],
+		this->points[2],
+		this->colorIndex,
+		this->id));
+}
--- a/src/linetypes/triangle.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/linetypes/triangle.h	Wed Jan 01 17:45:56 2020 +0200
@@ -18,6 +18,7 @@
 	QVariant getProperty(Property id) const override;
 	SetPropertyResult setProperty(Property id, const QVariant& value) override;
 	QString textRepresentation() const override;
+	void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const override;
 private:
 	Point3D points[3] = {{}};
 };
--- a/src/main.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/main.h	Wed Jan 01 17:45:56 2020 +0200
@@ -12,3 +12,12 @@
 	// List of setting groups
 	constexpr char mainwindow[] = "mainwindow";
 }
+
+namespace linetypes
+{
+	// Uniquely identifies a model body object
+	struct Id
+	{
+		unsigned int value;
+	};
+}
--- a/src/mainwindow.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/mainwindow.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -117,7 +117,7 @@
 
 void MainWindow::openModelForEditing(const QString& modelName)
 {
-	Document* document = new Document{this->documents.findModelByName(modelName)};
+	Document* document = new Document{this->documents.findModelByName(modelName), &this->documents};
 	this->ui->tabs->addTab(document, modelName);
 	this->ui->tabs->setCurrentWidget(document);
 	document->restoreSplitterState(this->documentSplitterState);
--- a/src/matrix.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/matrix.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -1,1 +1,12 @@
 #include "matrix.h"
+#include "vertex.h"
+
+Matrix4x4 combine(const Matrix3x3& topLeft, const Point3D& translation)
+{
+	return {{
+		{topLeft(0, 0), topLeft(0, 1), topLeft(0, 2), translation.x},
+		{topLeft(1, 0), topLeft(1, 1), topLeft(1, 2), translation.y},
+		{topLeft(2, 0), topLeft(2, 1), topLeft(2, 2), translation.z},
+		{0, 0, 0, 1}
+	}};
+}
--- a/src/matrix.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/matrix.h	Wed Jan 01 17:45:56 2020 +0200
@@ -146,5 +146,7 @@
 	return stream;
 }
 
+Matrix4x4 combine(const Matrix3x3& topLeft, const struct Point3D& translation);
+
 static constexpr Matrix3x3 identity3x3 {{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}};
 static constexpr Matrix4x4 identity4x4 {{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}};
--- a/src/model.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/model.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -3,10 +3,8 @@
 #include "model.h"
 #include "modeleditcontext.h"
 
-Model::Model()
-{
-
-}
+Model::Model(QObject* parent) :
+	QAbstractListModel{parent} {}
 
 int Model::size() const
 {
@@ -59,6 +57,30 @@
 	return object->getProperty(property);
 }
 
+std::vector<gl::Polygon> Model::getPolygons(DocumentManager* documents) const
+{
+	if (this->needRecache)
+	{
+		this->cachedPolygons.clear();
+		linetypes::GetPolygonsContext context{documents};
+		for (int i = 0; i < this->size(); i += 1)
+		{
+			this->getObjectPolygons(i, this->cachedPolygons, &context);
+		}
+		this->needRecache = false;
+	}
+	return this->cachedPolygons;
+}
+
+void Model::getObjectPolygons(
+	const int index,
+	std::vector<gl::Polygon>& polygons_out,
+	linetypes::GetPolygonsContext* context) const
+{
+	const linetypes::Object* object = this->body[index].get();
+	object->getPolygons(polygons_out, context);
+}
+
 void Model::append(ModelObjectPointer&& object)
 {
 	this->body.push_back(std::move(object));
--- a/src/model.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/model.h	Wed Jan 01 17:45:56 2020 +0200
@@ -4,6 +4,7 @@
 #include "main.h"
 #include "header.h"
 #include "linetypes/object.h"
+#include "gl/common.h"
 
 enum class HeaderProperty
 {
@@ -15,7 +16,7 @@
 	Q_OBJECT
 public:
 	class EditContext;
-	Model();
+	Model(QObject* parent = nullptr);
 	Model(const Model&) = delete;
 	int size() const;
 	EditContext edit();
@@ -24,6 +25,8 @@
 	QVariant getHeaderProperty(const HeaderProperty property);
 	const QString& getName() const;
 	QVariant getObjectProperty(const int index, const linetypes::Property property) const;
+	std::vector<gl::Polygon> getPolygons(class DocumentManager* documents) const;
+	void getObjectPolygons(const int index, std::vector<gl::Polygon>& polygons_out, linetypes::GetPolygonsContext* context) const;
 signals:
 	void objectAdded(linetypes::Id id, int position);
 private:
@@ -38,6 +41,8 @@
 	LDHeader header;
 	std::vector<ModelObjectPointer> body;
 	std::map<linetypes::Id, linetypes::Object*> objectsById;
+	mutable std::vector<gl::Polygon> cachedPolygons;
+	mutable bool needRecache = true;
 };
 
 template<typename T, typename... Args>
--- a/src/parser.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/parser.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -350,7 +350,7 @@
 	const Point3D position = vertexFromStrings(tokens, positionPosition);
 	const Matrix3x3 transform = matrixFromStrings(tokens, transformPosition);
 	const QString& name = tokens[namePosition];
-	return std::make_unique<linetypes::SubfileReference>(position, transform, name, color);
+	return std::make_unique<linetypes::SubfileReference>(combine(transform, position), name, color);
 }
 
 template<typename T, int NumVertices>
--- a/src/vertex.cpp	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/vertex.cpp	Wed Jan 01 17:45:56 2020 +0200
@@ -157,20 +157,23 @@
 }
 
 /*
- * Transforms this vertex with a tranformation matrix and returns the result.
+ * Transforms the specified vertex with a transformation matrix
  */
-Point3D math::transform(const Point3D point, const GLRotationMatrix& matrix)
+Point3D math::transform(const Point3D& point, const Matrix4x4& matrix)
 {
 	return {
 		matrix(0, 0) * point.x
 			+ matrix(0, 1) * point.y
-			+ matrix(0, 2) * point.z,
+			+ matrix(0, 2) * point.z
+			+ matrix(0, 3),
 		matrix(1, 0) * point.x
 			+ matrix(1, 1) * point.y
-			+ matrix(1, 2) * point.z,
+			+ matrix(1, 2) * point.z
+			+ matrix(1, 3),
 		matrix(2, 0) * point.x
 			+ matrix(2, 1) * point.y
-			+ matrix(2, 2) * point.z,
+			+ matrix(2, 2) * point.z
+			+ matrix(2, 3),
 	};
 }
 
--- a/src/vertex.h	Sat Dec 14 23:00:01 2019 +0200
+++ b/src/vertex.h	Wed Jan 01 17:45:56 2020 +0200
@@ -21,6 +21,7 @@
 #include <QVector3D>
 #include "basics.h"
 #include "maths.h"
+#include "matrix.h"
 
 struct Point3D
 {
@@ -34,7 +35,7 @@
 
 namespace math
 {
-	Point3D transform(const Point3D point, const GLRotationMatrix& matrix);
+	Point3D transform(const Point3D& point, const Matrix4x4& matrix);
 	qreal distance(const Point3D& one, const Point3D& other);
 }
 

mercurial