Merge ../ldforge into gl

Wed, 05 Feb 2014 06:07:05 +0200

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Wed, 05 Feb 2014 06:07:05 +0200
changeset 680
bf6323f2b1be
parent 679
dd7545e00a8f (diff)
parent 631
93272c0f9a80 (current diff)
child 681
c1cc036c6e1f

Merge ../ldforge into gl

--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/GLCompiler.cc	Wed Feb 05 06:07:05 2014 +0200
@@ -0,0 +1,340 @@
+#define GL_GLEXT_PROTOTYPES
+#include <GL/glu.h>
+#include <GL/glext.h>
+#include "GLCompiler.h"
+#include "LDObject.h"
+#include "Colors.h"
+#include "Document.h"
+#include "Misc.h"
+#include "GLRenderer.h"
+#include "Dialogs.h"
+
+static const struct
+{
+	GLenum	value;
+	QString	text;
+}
+g_GLErrors[] =
+{
+	{ GL_NO_ERROR,						"No error" },
+	{ GL_INVALID_ENUM,					"Unacceptable enumerator passed" },
+	{ GL_INVALID_VALUE,					"Numeric argument out of range" },
+	{ GL_INVALID_OPERATION,				"The operation is not allowed to be done in this state" },
+	{ GL_INVALID_FRAMEBUFFER_OPERATION,	"Framebuffer object is not complete"},
+	{ GL_OUT_OF_MEMORY,					"Out of memory" },
+	{ GL_STACK_UNDERFLOW,				"The operation would have caused an underflow" },
+	{ GL_STACK_OVERFLOW,				"The operation would have caused an overflow" },
+};
+
+#define DEBUG_PRINT(...) fprint (stdout, __VA_ARGS__)
+
+extern_cfg (Bool, gl_blackedges);
+static QList<short> g_warnedColors;
+
+static const QColor g_BFCFrontColor(40, 192, 0);
+static const QColor g_BFCBackColor(224, 0, 0);
+
+// =============================================================================
+//
+void checkGLError_private (const char* file, int line)
+{
+	GLenum errnum = glGetError();
+
+	if (errnum == GL_NO_ERROR)
+		return;
+
+	QString errmsg;
+
+	for (const auto& it : g_GLErrors)
+	{
+		if (it.value == errnum)
+		{
+			errmsg = it.text;
+			break;
+		}
+	}
+
+	log ("GL ERROR: %1:%2: %3", file, line, errmsg);
+}
+
+// =============================================================================
+//
+GLCompiler::GLCompiler() :
+	m_Document (null)
+{
+	needMerge();
+}
+
+// =============================================================================
+//
+void GLCompiler::initialize()
+{
+	glGenBuffers (VBO_NumArrays, &m_mainVBOs[0]);
+	checkGLError();
+}
+
+GLCompiler::~GLCompiler()
+{
+	glDeleteBuffers (VBO_NumArrays, &m_mainVBOs[0]);
+	checkGLError();
+}
+
+// =============================================================================
+//
+uint32 GLCompiler::getColorRGB (const QColor& color)
+{
+	return
+		(color.red()   & 0xFF) << 0x00 |
+		(color.green() & 0xFF) << 0x08 |
+		(color.blue()  & 0xFF) << 0x10 |
+		(color.alpha() & 0xFF) << 0x18;
+}
+
+// =============================================================================
+//
+QColor GLCompiler::getObjectColor (LDObject* obj, GLCompiler::E_ColorType colortype) const
+{
+	QColor qcol;
+
+	if (!obj->isColored())
+		return QColor();
+
+	if (colortype == E_PickColor)
+	{
+		// Make the color by the object's ID if we're picking, so we can make the
+		// ID again from the color we get from the picking results. Be sure to use
+		// the top level parent's index since we want a subfile's children point
+		// to the subfile itself.
+		long i = obj->topLevelParent()->getID();
+
+		// Calculate a color based from this index. This method caters for
+		// 16777216 objects. I don't think that'll be exceeded anytime soon. :)
+		// ATM biggest is 53588.dat with 12600 lines.
+		int r = (i / 0x10000) % 0x100,
+			g = (i / 0x100) % 0x100,
+			b = i % 0x100;
+
+		return QColor (r, g, b);
+	}
+
+	if (obj->getColor() == maincolor)
+		qcol = GLRenderer::getMainColor();
+	else
+	{
+		LDColor* col = getColor (obj->getColor());
+
+		if (col)
+			qcol = col->faceColor;
+	}
+
+	if (obj->getColor() == edgecolor)
+	{
+		qcol = QColor (32, 32, 32); // luma (m_bgcolor) < 40 ? QColor (64, 64, 64) : Qt::black;
+		LDColor* col;
+
+		if (!gl_blackedges && obj->getParent() && (col = getColor (obj->getParent()->getColor())))
+			qcol = col->edgeColor;
+	}
+
+	if (qcol.isValid() == false)
+	{
+		// The color was unknown. Use main color to make the object at least
+		// not appear pitch-black.
+		if (obj->getColor() != edgecolor)
+			qcol = GLRenderer::getMainColor();
+		else
+			qcol = Qt::black;
+
+		// Warn about the unknown color, but only once.
+		for (short i : g_warnedColors)
+			if (obj->getColor() == i)
+				return qcol;
+
+		log ("%1: Unknown color %2!\n", __func__, obj->getColor());
+		g_warnedColors << obj->getColor();
+		return qcol;
+	}
+
+	if (obj->topLevelParent()->isSelected())
+	{
+		// Brighten it up if selected.
+		const int add = 51;
+
+		qcol.setRed (min (qcol.red() + add, 255));
+		qcol.setGreen (min (qcol.green() + add, 255));
+		qcol.setBlue (min (qcol.blue() + add, 255));
+	}
+
+	return qcol;
+}
+
+// =============================================================================
+//
+void GLCompiler::needMerge()
+{
+	// Set all of m_changed to true
+	// memset (m_changed, 0xFF, sizeof m_changed);
+	for (int i = 0; i < VBO_NumArrays; ++i)
+		m_changed[i] = true;
+}
+
+// =============================================================================
+//
+void GLCompiler::stageForCompilation (LDObject* obj)
+{
+	m_staged << obj;
+	removeDuplicates (m_staged);
+}
+
+// =============================================================================
+//
+void GLCompiler::compileDocument()
+{
+	for (LDObject* obj : getDocument()->getObjects())
+		compileObject (obj);
+}
+
+// =============================================================================
+//
+void GLCompiler::compileStaged()
+{
+	for (LDObject* obj : m_staged)
+		compileObject (obj);
+
+	m_staged.clear();
+}
+
+// =============================================================================
+//
+void GLCompiler::prepareVBOArray (E_VBOArray type)
+{
+	// Compile anything that still awaits it
+	compileStaged();
+
+	if (!m_changed[type])
+		return;
+
+	m_mainVBOData[type].clear();
+
+	for (auto it = m_objArrays.begin(); it != m_objArrays.end(); ++it)
+		m_mainVBOData[type] += (*it)[type];
+
+	glBindBuffer (GL_ARRAY_BUFFER, m_mainVBOs[type]);
+	checkGLError();
+	glBufferData (GL_ARRAY_BUFFER, m_mainVBOData[type].size() * sizeof(float),
+		m_mainVBOData[type].constData(), GL_DYNAMIC_DRAW);
+	checkGLError();
+	glBindBuffer (GL_ARRAY_BUFFER, 0);
+	checkGLError();
+	m_changed[type] = false;
+	log ("VBO array %1 prepared: %2 coordinates", (int) type, m_mainVBOData[type].size());
+}
+
+// =============================================================================
+//
+void GLCompiler::forgetObject (LDObject* obj)
+{
+	auto it = m_objArrays.find (obj);
+
+	if (it != m_objArrays.end())
+	{
+		delete *it;
+		m_objArrays.erase (it);
+	}
+}
+
+// =============================================================================
+//
+void GLCompiler::compileObject (LDObject* obj)
+{
+	// Ensure we have valid arrays to write to.
+	if (m_objArrays.find (obj) == m_objArrays.end())
+		m_objArrays[obj] = new QVector<GLfloat>[VBO_NumArrays];
+	else
+	{
+		// Arrays exist already, clear them.
+		for (int i = 0; i < VBO_NumArrays; ++i)
+			m_objArrays[obj][i].clear();
+	}
+
+	compileSubObject (obj, obj);
+	QList<int> data;
+
+	for (int i = 0; i < VBO_NumArrays; ++i)
+		data << m_objArrays[obj][i].size();
+
+	dlog ("Compiled #%1: %2 coordinates", obj->getID(), data);
+	needMerge();
+}
+
+// =============================================================================
+//
+void GLCompiler::compileSubObject (LDObject* obj, LDObject* topobj)
+{
+	switch (obj->getType())
+	{
+		// Note: We cannot split the quad into triangles here, it would
+		// mess up the wireframe view. Quads must go into a separate array.
+		case LDObject::ETriangle:
+		case LDObject::EQuad:
+		case LDObject::ELine:
+		case LDObject::ECondLine:
+		{
+			E_VBOArray arraynum;
+			int verts;
+
+			switch (obj->getType())
+			{
+				case LDObject::ETriangle:	arraynum = VBO_Triangles;	verts = 3; break;
+				case LDObject::EQuad:		arraynum = VBO_Quads;		verts = 4; break;
+				case LDObject::ELine:		arraynum = VBO_Lines;		verts = 2; break;
+				case LDObject::ECondLine:	arraynum = VBO_CondLines;	verts = 2; break;
+				default: break;
+			}
+
+			QVector<GLfloat>* ap = m_objArrays[topobj];
+			QColor normalColor = getObjectColor (obj, E_NormalColor);
+			QColor pickColor = getObjectColor (topobj, E_PickColor);
+
+			for (int i = 0; i < verts; ++i)
+			{
+				// Write coordinates
+				ap[arraynum]
+					<< obj->getVertex (i).x()
+					<< -obj->getVertex (i).y()
+					<< -obj->getVertex (i).z();
+
+				// Colors
+				writeColor (ap[VBO_NormalColors], normalColor);
+				writeColor (ap[VBO_PickColors], pickColor);
+				writeColor (ap[VBO_BFCFrontColors], g_BFCFrontColor);
+				writeColor (ap[VBO_BFCBackColors], g_BFCBackColor);
+			}
+		} break;
+
+		case LDObject::ESubfile:
+		{
+			LDSubfile* ref = static_cast<LDSubfile*> (obj);
+			LDObjectList subobjs = ref->inlineContents (LDSubfile::DeepCacheInline | LDSubfile::RendererInline);
+
+			for (LDObject* subobj : subobjs)
+			{
+				compileSubObject (subobj, topobj);
+				subobj->deleteSelf();
+			}
+		} break;
+
+		default:
+			break;
+	}
+}
+
+// =============================================================================
+//
+void GLCompiler::writeColor (QVector<GLfloat>& array, const QColor& color)
+{
+	array	<< color.red()
+			<< color.green()
+			<< color.blue()
+			<< color.alpha();
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/GLCompiler.h	Wed Feb 05 06:07:05 2014 +0200
@@ -0,0 +1,105 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013, 2014 Santeri 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/>.
+ */
+
+#ifndef LDFORGE_GLCOMPILER_H
+#define LDFORGE_GLCOMPILER_H
+
+#include "Main.h"
+#include "GLRenderer.h"
+#include "GLShared.h"
+#include <QMap>
+
+// =============================================================================
+// GLCompiler
+//
+// This class manages vertex arrays for the GL renderer, compiling vertices into
+// VAO-readable triangles which can be requested with getMergedBuffer.
+//
+// There are 6 main array types:
+// - the normal polygon array, for triangles
+// - edge line array, for lines
+// - conditional line array, for conditional lines. Kept separate so that they
+//       can be drawn as dashed liness
+// - BFC array, this is the same as the normal polygon array except that the
+//       polygons are listed twice, once normally and green and once reversed
+//       and red, this allows BFC red/green view.
+// - Picking array, this is the same as the normal polygon array except the
+//       polygons are compiled with their index color, this way the picking
+//       method is capable of determining which object was selected by pixel
+//       color.
+// - Edge line picking array, the pick array version of the edge line array.
+//       Conditional lines are grouped with normal edgelines here.
+//
+// There are also these same 5 arrays for every LDObject compiled. The main
+// arrays are generated on demand from the ones in the current file's
+// LDObjects and stored in cache for faster renmm dering.
+//
+// The nested Array class contains a vector-like buffer of the Vertex structs,
+// these structs are the VAOs that get passed to the renderer.
+//
+class GLCompiler
+{
+	PROPERTY (public,	LDDocumentPointer,	Document,	NO_OPS,	STOCK_WRITE)
+
+	public:
+		enum E_ColorType
+		{
+			E_NormalColor,
+			E_PickColor,
+		};
+
+		GLCompiler();
+		~GLCompiler();
+		void			compileDocument();
+		void			forgetObject (LDObject* obj);
+		void			initObject (LDObject* obj);
+		QColor			getObjectColor (LDObject* obj, E_ColorType colortype) const;
+		void			needMerge();
+		void			prepareVBOArray (E_VBOArray type);
+		void			stageForCompilation (LDObject* obj);
+
+		static uint32	getColorRGB (const QColor& color);
+
+		inline GLuint	getVBOIndex (E_VBOArray array) const
+		{
+			return m_mainVBOs[array];
+		}
+
+		inline int		getVBOCount (E_VBOArray array) const
+		{
+			return m_mainVBOData[array].size() / 3;
+		}
+		void initialize();
+
+	private:
+		void			compileStaged();
+		void			compileObject (LDObject* obj);
+		void			compileSubObject (LDObject* obj, LDObject* topobj);
+		void			writeColor (QVector< float >& array, const QColor& color);
+
+		QMap<LDObject*, QVector<GLfloat>*>	m_objArrays;
+		QVector<GLfloat>					m_mainVBOData[VBO_NumArrays];
+		GLuint								m_mainVBOs[VBO_NumArrays];
+		bool								m_changed[VBO_NumArrays];
+		LDObjectList						m_staged; // Objects that need to be compiled
+};
+
+#define checkGLError() { checkGLError_private (__FILE__, __LINE__); }
+void checkGLError_private (const char* file, int line);
+
+#endif // LDFORGE_GLCOMPILER_H
--- a/src/GLRenderer.cc	Wed Feb 05 06:06:23 2014 +0200
+++ b/src/GLRenderer.cc	Wed Feb 05 06:07:05 2014 +0200
@@ -16,6 +16,9 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define GL_GLEXT_PROTOTYPES
+#include <GL/glu.h>
+#include <GL/glext.h>
 #include <QGLWidget>
 #include <QWheelEvent>
 #include <QMouseEvent>
@@ -23,8 +26,6 @@
 #include <QInputDialog>
 #include <QToolTip>
 #include <QTimer>
-#include <GL/glu.h>
-
 #include "Main.h"
 #include "Configuration.h"
 #include "Document.h"
@@ -36,6 +37,7 @@
 #include "Dialogs.h"
 #include "AddObjectDialog.h"
 #include "MessageLog.h"
+#include "GLCompiler.h"
 #include "Primitives.h"
 #include "misc/RingFinder.h"
 #include "moc_GLRenderer.cpp"
@@ -97,20 +99,21 @@
 	GL::EFreeCamera
 };
 
-// Definitions for visual axes, drawn on the screen
-const struct LDGLAxis
+struct LDGLAxis
 {
 	const QColor col;
 	const Vertex vert;
-} g_GLAxes[3] =
+};
+
+// Definitions for visual axes, drawn on the screen
+static const LDGLAxis g_GLAxes[3] =
 {
 	{ QColor (255,   0,   0), Vertex (10000, 0, 0) }, // X
 	{ QColor (80,  192,   0), Vertex (0, 10000, 0) }, // Y
 	{ QColor (0,   160, 192), Vertex (0, 0, 10000) }, // Z
 };
 
-static bool g_glInvert = false;
-static QList<int> g_warnedColors;
+static GLuint g_GLAxes_VBO;
 
 // =============================================================================
 //
@@ -127,6 +130,7 @@
 	setMessageLog (null);
 	m_width = m_height = -1;
 	m_hoverpos = g_origin;
+	m_compiler = new GLCompiler;
 
 	m_toolTipTimer = new QTimer (this);
 	m_toolTipTimer->setSingleShot (true);
@@ -158,6 +162,8 @@
 
 	for (CameraIcon& info : m_cameraIcons)
 		delete info.img;
+
+	delete m_compiler;
 }
 
 // =============================================================================
@@ -245,11 +251,46 @@
 	setBackground();
 
 	glLineWidth (gl_linethickness);
+	glLineStipple (1, 0x6666);
 
 	setAutoFillBackground (false);
 	setMouseTracking (true);
 	setFocusPolicy (Qt::WheelFocus);
-	compileAllObjects();
+
+	m_compiler->initialize();
+	m_compiler->compileDocument();
+
+	initializeAxes();
+}
+
+// =============================================================================
+//
+void GLRenderer::initializeAxes()
+{
+	float axesdata[18];
+	memset (axesdata, 0, sizeof axesdata);
+	float colordata[18];
+
+	for (int i = 0; i < 3; ++i)
+	{
+		for (int j = 0; j < 3; ++j)
+		{
+			axesdata[(i * 6) + j] = g_GLAxes[i].vert.getCoordinate (j);
+			axesdata[(i * 6) + 3 + j] = -g_GLAxes[i].vert.getCoordinate (j);
+		}
+
+		for (int j = 0; j < 2; ++j)
+		{
+			colordata[(i * 6) + (j * 3) + 0] = g_GLAxes[i].col.red();
+			colordata[(i * 6) + (j * 3) + 1] = g_GLAxes[i].col.green();
+			colordata[(i * 6) + (j * 3) + 2] = g_GLAxes[i].col.blue();
+		}
+	}
+
+	glGenBuffers (1, &g_GLAxes_VBO);
+	glBindBuffer (GL_ARRAY_BUFFER, g_GLAxes_VBO);
+	glBufferData (GL_ARRAY_BUFFER, sizeof axesdata, axesdata, GL_STATIC_DRAW);
+	glBindBuffer (GL_ARRAY_BUFFER, 0);
 }
 
 // =============================================================================
@@ -282,105 +323,7 @@
 }
 
 // =============================================================================
-//
-void GLRenderer::setObjectColor (LDObject* obj, const ListType list)
-{
-	QColor qcol;
-
-	if (!obj->isColored())
-		return;
-
-	if (list == GL::PickList)
-	{
-		// Make the color by the object's ID if we're picking, so we can make the
-		// ID again from the color we get from the picking results. Be sure to use
-		// the top level parent's index since we want a subfile's children point
-		// to the subfile itself.
-		long i = obj->topLevelParent()->getID();
-
-		// Calculate a color based from this index. This method caters for
-		// 16777216 objects. I don't think that'll be exceeded anytime soon. :)
-		// ATM biggest is 53588.dat with 12600 lines.
-		double r = (i / 0x10000) % 0x100,
-			   g = (i / 0x100) % 0x100,
-			   b = i % 0x100;
-
-		qglColor (QColor (r, g, b));
-		return;
-	}
-
-	if ((list == BFCFrontList || list == BFCBackList) &&
-		obj->getType() != LDObject::ELine &&
-		obj->getType() != LDObject::ECondLine)
-	{
-		if (list == GL::BFCFrontList)
-			qcol = QColor (40, 192, 0);
-		else
-			qcol = QColor (224, 0, 0);
-	}
-	else
-	{
-		if (obj->getColor() == maincolor)
-			qcol = getMainColor();
-		else
-		{
-			LDColor* col = getColor (obj->getColor());
-
-			if (col)
-				qcol = col->faceColor;
-		}
-
-		if (obj->getColor() == edgecolor)
-		{
-			LDColor* col;
-
-			if (!gl_blackedges && obj->getParent() && (col = getColor (obj->getParent()->getColor())))
-				qcol = col->edgeColor;
-			else
-				qcol = (m_darkbg == false) ? Qt::black : Qt::white;
-		}
-
-		if (qcol.isValid() == false)
-		{
-			// The color was unknown. Use main color to make the object at least
-			// not appear pitch-black.
-			if (obj->getColor() != edgecolor)
-				qcol = getMainColor();
-
-			// Warn about the unknown colors, but only once.
-			for (int i : g_warnedColors)
-				if (obj->getColor() == i)
-					return;
-
-			log ("%1: Unknown color %2!\n", __func__, obj->getColor());
-			g_warnedColors << obj->getColor();
-			return;
-		}
-	}
-
-	int r = qcol.red(),
-		 g = qcol.green(),
-		 b = qcol.blue(),
-		 a = qcol.alpha();
-
-	if (obj->topLevelParent()->isSelected())
-	{
-		// Brighten it up for the select list.
-		QColor selcolor (gl_selectcolor);
-		r = (r + selcolor.red()) / 2;
-		g = (g + selcolor.green()) / 2;
-		b = (b + selcolor.blue()) / 2;
-	}
-
-	glColor4f (
-		((double) r) / 255.0f,
-		((double) g) / 255.0f,
-		((double) b) / 255.0f,
-		((double) a) / 255.0f);
-}
-
-// =============================================================================
-//
+// -----------------------------------------------------------------------------
 void GLRenderer::refresh()
 {
 	update();
@@ -388,10 +331,10 @@
 }
 
 // =============================================================================
-//
+// -----------------------------------------------------------------------------
 void GLRenderer::hardRefresh()
 {
-	compileAllObjects();
+	m_compiler->compileDocument();
 	refresh();
 
 	glLineWidth (gl_linethickness);
@@ -462,47 +405,54 @@
 		glRotatef (rot (Z), 0.0f, 0.0f, 1.0f);
 	}
 
-	const GL::ListType list = (!isDrawOnly() && isPicking()) ? PickList : NormalList;
-
-	if (gl_colorbfc && !isPicking() && !isDrawOnly())
-	{
-		glEnable (GL_CULL_FACE);
-
-		for (LDObject* obj : getFile()->getObjects())
-		{
-			if (obj->isHidden())
-				continue;
-
-			glCullFace (GL_BACK);
-			glCallList (obj->glLists[BFCFrontList]);
+	glEnableClientState (GL_VERTEX_ARRAY);
 
-			glCullFace (GL_FRONT);
-			glCallList (obj->glLists[BFCBackList]);
-		}
-
-		glDisable (GL_CULL_FACE);
-	}
-	else
+	if (gl_axes)
 	{
-		for (LDObject* obj : getFile()->getObjects())
-		{
-			if (obj->isHidden())
-				continue;
-
-			glCallList (obj->glLists[list]);
-		}
+		glBindBuffer (GL_ARRAY_BUFFER, g_GLAxes_VBO);
+		checkGLError();
+		glVertexPointer (3, GL_FLOAT, 0, NULL);
+		checkGLError();
+		glDrawArrays (GL_LINES, 0, 6);
+		checkGLError();
 	}
 
-	if (gl_axes && !isPicking() && !isDrawOnly())
-		glCallList (m_axeslist);
+	drawVBOs (VBO_Triangles, GL_TRIANGLES);
+	drawVBOs (VBO_Quads, GL_QUADS);
+	drawVBOs (VBO_Lines, GL_LINES);
+	drawVBOs (VBO_CondLines, GL_LINES);
 
 	glPopMatrix();
+	glBindBuffer (GL_ARRAY_BUFFER, 0);
+	glDisableClientState (GL_VERTEX_ARRAY);
+	checkGLError();
+	glDisable (GL_CULL_FACE);
 	glMatrixMode (GL_MODELVIEW);
 	glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
 }
 
 // =============================================================================
 //
+void GLRenderer::drawVBOs (E_VBOArray arrayType, GLenum type)
+{
+	m_compiler->prepareVBOArray (arrayType);
+	GLuint idx = m_compiler->getVBOIndex (arrayType);
+	GLsizei count = m_compiler->getVBOCount (arrayType);
+
+	if (count > 0)
+	{
+		glBindBuffer (GL_ARRAY_BUFFER, idx);
+		checkGLError();
+		glVertexPointer (3, GL_FLOAT, 0, null);
+		checkGLError();
+		// glColorPointer (4, GL_UNSIGNED_BYTE, sizeof (GLCompiler::VAO), &array->data()[0].color);
+		// glVertexAttribPointer (idx, 3, GL_FLOAT, GL_FALSE, 0, null);
+		glDrawArrays (type, 0, count);
+		checkGLError();
+	}
+}
+
+// =============================================================================
 // This converts a 2D point on the screen to a 3D point in the model. If 'snap'
 // is true, the 3D point will snap to the current grid.
 //
@@ -897,127 +847,7 @@
 //
 void GLRenderer::compileAllObjects()
 {
-	if (!getFile())
-		return;
-
-	// Compiling all is a big job, use a busy cursor
-	setCursor (Qt::BusyCursor);
-
-	m_knownVerts.clear();
-
-	for (LDObject* obj : getFile()->getObjects())
-		compileObject (obj);
-
-	// Compile axes
-	glDeleteLists (m_axeslist, 1);
-	m_axeslist = glGenLists (1);
-	glNewList (m_axeslist, GL_COMPILE);
-	glBegin (GL_LINES);
-
-	for (const LDGLAxis& ax : g_GLAxes)
-	{
-		qglColor (ax.col);
-		compileVertex (ax.vert);
-		compileVertex (-ax.vert);
-	}
-
-	glEnd();
-	glEndList();
-
-	setCursor (Qt::ArrowCursor);
-}
-
-// =============================================================================
-//
-void GLRenderer::compileSubObject (LDObject* obj, const GLenum gltype)
-{
-	glBegin (gltype);
-
-	const int numverts = (obj->getType() != LDObject::ECondLine) ? obj->vertices() : 2;
-
-	if (g_glInvert == false)
-		for (int i = 0; i < numverts; ++i)
-			compileVertex (obj->getVertex (i));
-	else
-		for (int i = numverts - 1; i >= 0; --i)
-			compileVertex (obj->getVertex (i));
-
-	glEnd();
-}
-
-// =============================================================================
-//
-void GLRenderer::compileList (LDObject* obj, const GLRenderer::ListType list)
-{
-	setObjectColor (obj, list);
-
-	switch (obj->getType())
-	{
-		case LDObject::ELine:
-		{
-			compileSubObject (obj, GL_LINES);
-		} break;
-
-		case LDObject::ECondLine:
-		{
-			// Draw conditional lines with a dash pattern - however, use a full
-			// line when drawing a pick list to make selecting them easier.
-			if (list != GL::PickList)
-			{
-				glLineStipple (1, 0x6666);
-				glEnable (GL_LINE_STIPPLE);
-			}
-
-			compileSubObject (obj, GL_LINES);
-
-			glDisable (GL_LINE_STIPPLE);
-		} break;
-
-		case LDObject::ETriangle:
-		{
-			compileSubObject (obj, GL_TRIANGLES);
-		} break;
-
-		case LDObject::EQuad:
-		{
-			compileSubObject (obj, GL_QUADS);
-		} break;
-
-		case LDObject::ESubfile:
-		{
-			LDSubfile* ref = static_cast<LDSubfile*> (obj);
-			LDObjectList objs;
-
-			objs = ref->inlineContents (LDSubfile::DeepCacheInline | LDSubfile::RendererInline);
-			bool oldinvert = g_glInvert;
-
-			if (ref->getTransform().getDeterminant() < 0)
-				g_glInvert = !g_glInvert;
-
-			LDObject* prev = ref->prev();
-
-			if (prev && prev->getType() == LDObject::EBFC && static_cast<LDBFC*> (prev)->type == LDBFC::InvertNext)
-				g_glInvert = !g_glInvert;
-
-			for (LDObject* obj : objs)
-			{
-				compileList (obj, list);
-				obj->deleteSelf();
-			}
-
-			g_glInvert = oldinvert;
-		} break;
-
-		default:
-			break;
-	}
-}
-
-// =============================================================================
-//
-void GLRenderer::compileVertex (const Vertex& vrt)
-{
-	glVertex3d (vrt[X], -vrt[Y], -vrt[Z]);
+	m_compiler->compileDocument();
 }
 
 // =============================================================================
@@ -1376,8 +1206,8 @@
 	{
 		qint32 idx =
 			(*(pixelptr + 0) * 0x10000) +
-			(*(pixelptr + 1) * 0x00100) +
-			(*(pixelptr + 2) * 0x00001);
+			(*(pixelptr + 1) * 0x100) +
+			*(pixelptr + 2);
 		pixelptr += 4;
 
 		if (idx == 0xFFFFFF)
@@ -1473,6 +1303,7 @@
 void GLRenderer::setFile (LDDocument* const& a)
 {
 	m_File = a;
+	m_compiler->setDocument (a);
 
 	if (a != null)
 	{
@@ -1746,21 +1577,7 @@
 //
 void GLRenderer::compileObject (LDObject* obj)
 {
-	deleteLists (obj);
-
-	for (const GL::ListType listType : g_glListTypes)
-	{
-		if (isDrawOnly() && listType != GL::NormalList)
-			continue;
-
-		GLuint list = glGenLists (1);
-		glNewList (list, GL_COMPILE);
-
-		obj->glLists[listType] = list;
-		compileList (obj, listType);
-
-		glEndList();
-	}
+	m_compiler->stageForCompilation (obj);
 
 	// Mark in known vertices of this object
 	QList<Vertex> verts = getVertices (obj);
@@ -1795,7 +1612,7 @@
 	// We come here if the cursor has stayed in one place for longer than a
 	// a second. Check if we're holding it over a camera icon - if so, draw
 	// a tooltip.
-for (CameraIcon & icon : m_cameraIcons)
+	for (CameraIcon & icon : m_cameraIcons)
 	{
 		if (icon.destRect.contains (m_pos))
 		{
--- a/src/GLRenderer.h	Wed Feb 05 06:06:23 2014 +0200
+++ b/src/GLRenderer.h	Wed Feb 05 06:07:05 2014 +0200
@@ -23,7 +23,9 @@
 #include "Main.h"
 #include "LDObject.h"
 #include "Document.h"
+#include "GLShared.h"
 
+class GLCompiler;
 class MessageManager;
 class QDialogButtonBox;
 class RadioGroup;
@@ -152,7 +154,6 @@
 		Axis           getCameraAxis (bool y, EFixedCamera camid = (EFixedCamera) - 1);
 		const char*    getCameraName() const;
 		double         getDepthValue() const;
-		QColor         getMainColor();
 		LDGLOverlay&   getOverlay (int newcam);
 		uchar*         getScreencap (int& w, int& h);
 		void           hardRefresh();
@@ -171,6 +172,7 @@
 		void           zoomAllToFit();
 
 		static void    deleteLists (LDObject* obj);
+		static QColor  getMainColor();
 
 	protected:
 		void           contextMenuEvent (QContextMenuEvent* ev);
@@ -216,6 +218,7 @@
 		Vertex						m_rectverts[4];
 		QColor						m_bgcolor;
 		QList<Vertex>				m_knownVerts;
+		GLCompiler*					m_compiler;
 
 		void           addDrawnVertex (Vertex m_hoverpos);
 		LDOverlay*     findOverlayObject (EFixedCamera cam);
@@ -245,6 +248,12 @@
 		// Convert a 2D point to a 3D point
 		Vertex         coordconv2_3 (const QPoint& pos2d, bool snap) const;
 
+		// Draw a VBO array
+		void           drawVBOs (E_VBOArray arrayType, GLenum type);
+
+		// Determine which color to draw text with
+		QColor         getTextPen() const;
+
 		// Convert a 3D point to a 2D point
 		QPoint         coordconv3_2 (const Vertex& pos3d) const;
 
@@ -290,6 +299,7 @@
 
 	private slots:
 		void           slot_toolTipTimer();
+		void initializeAxes();
 };
 
 // Alias for short namespaces
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/GLShared.h	Wed Feb 05 06:07:05 2014 +0200
@@ -0,0 +1,35 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013, 2014 Santeri 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/>.
+ */
+
+#ifndef LDFORGE_GLSHARED_H
+#define LDFORGE_GLSHARED_H
+
+enum E_VBOArray
+{
+	VBO_Quads,
+	VBO_Triangles,
+	VBO_Lines,
+	VBO_CondLines,
+	VBO_NormalColors,
+	VBO_BFCFrontColors,
+	VBO_BFCBackColors,
+	VBO_PickColors,
+	VBO_NumArrays
+};
+
+#endif // LDFORGE_GLSHARED_H
\ No newline at end of file
--- a/src/LDObject.cc	Wed Feb 05 06:06:23 2014 +0200
+++ b/src/LDObject.cc	Wed Feb 05 06:07:05 2014 +0200
@@ -24,6 +24,7 @@
 #include "EditHistory.h"
 #include "GLRenderer.h"
 #include "Colors.h"
+#include "GLCompiler.h"
 
 cfg (String, ld_defaultname, "");
 cfg (String, ld_defaultuser, "");
--- a/src/Main.cc	Wed Feb 05 06:06:23 2014 +0200
+++ b/src/Main.cc	Wed Feb 05 06:07:05 2014 +0200
@@ -22,6 +22,7 @@
 #include <QFile>
 #include <QTextStream>
 #include <QDir>
+#include <QDate>
 #include "MainWindow.h"
 #include "Document.h"
 #include "Misc.h"
@@ -70,6 +71,30 @@
 	newFile();
 	win->show();
 
+	/*
+	LDDocument* box = getDocument ("box.dat");
+	LDSubfile* ref = new LDSubfile;
+	ref->setFileInfo (box);
+	ref->setPosition (g_origin);
+	ref->setTransform (g_identity);
+	ref->setColor (maincolor);
+	assert (box != null);
+
+	for (int i = 0; i < 500; ++i)
+	{
+		QTime t0 = QTime::currentTime();
+		LDObjectList objs = ref->inlineContents (LDSubfile::DeepCacheInline | LDSubfile::RendererInline);
+		LDObject::Type type = static_cast<LDObject*> (ref)->getType();
+		dlog ("%1: %2ms (%3 objs)\n", i, t0.msecsTo (QTime::currentTime()), objs.size());
+
+		for (LDObject* obj : objs)
+			obj->deleteSelf();
+	}
+
+	ref->deleteSelf();
+	delete box;
+	*/
+
 	// If this is the first start, get the user to configuration. Especially point
 	// them to the profile tab, it's the most important form to fill in.
 	if (firststart)
--- a/src/Types.h	Wed Feb 05 06:06:23 2014 +0200
+++ b/src/Types.h	Wed Feb 05 06:07:05 2014 +0200
@@ -23,6 +23,7 @@
 #include <QObject>
 #include <QStringList>
 #include <QMetaType>
+#include <QVector>
 #include "PropertyMacro.h"
 
 class LDObject;
@@ -231,11 +232,11 @@
 			m_val.sprintf ("%p", a);
 		}
 
-		template<class T> StringFormatArg (const QList<T>& a)
+		template<class T, class R> void initFromList (const T& a)
 		{
 			m_val = "{ ";
 
-			for (const T& it : a)
+			for (const R& it : a)
 			{
 				if (&it != &a.first())
 					m_val += ", ";
@@ -250,6 +251,16 @@
 			m_val += "}";
 		}
 
+		template<class T> StringFormatArg (const QList<T>& a)
+		{
+			initFromList<QList<T>, T> (a);
+		}
+
+		template<class T> StringFormatArg (const QVector<T>& a)
+		{
+			initFromList<QVector<T>, T> (a);
+		}
+
 		inline QString value() const
 		{
 			return m_val;

mercurial