Merge ../ldforge into gl

Mon, 24 Feb 2014 00:59:57 +0200

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Mon, 24 Feb 2014 00:59:57 +0200
changeset 688
2f4dcc323a85
parent 687
77d7c22ec9f3 (diff)
parent 637
220e79cd6837 (current diff)
child 689
397870c6ed38

Merge ../ldforge into gl

Conflicts:
src/Document.h

src/Document.cc file | annotate | diff | comparison | revisions
src/Document.h file | annotate | diff | comparison | revisions
src/GLCompiler.cc file | annotate | diff | comparison | revisions
src/GLRenderer.cc file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Mon Feb 24 00:25:18 2014 +0200
+++ b/CMakeLists.txt	Mon Feb 24 00:59:57 2014 +0200
@@ -31,6 +31,7 @@
 	src/EditHistory.cc
 	src/ExternalPrograms.cc
 	src/GLRenderer.cc
+	src/GLCompiler.cc
 	src/LDConfig.cc
 	src/LDObject.cc
 	src/Main.cc
--- a/src/Document.cc	Mon Feb 24 00:25:18 2014 +0200
+++ b/src/Document.cc	Mon Feb 24 00:59:57 2014 +0200
@@ -19,6 +19,7 @@
 #include <QMessageBox>
 #include <QFileDialog>
 #include <QDir>
+#include <QTime>
 #include <QApplication>
 #include "Main.h"
 #include "Configuration.h"
@@ -144,10 +145,6 @@
 	for (LDObject* obj : getObjects())
 		obj->deleteSelf();
 
-	// Clear the cache as well
-	for (LDObject* obj : getCache())
-		obj->deleteSelf();
-
 	delete m_History;
 	delete m_gldata;
 
@@ -1191,82 +1188,77 @@
 }
 
 // =============================================================================
+//
+void LDDocument::initializeGLData()
+{
+	log (getDisplayName() + ": Initializing GL data");
+	LDObjectList objs = inlineContents (true, true);
+
+	for (LDObject* obj : objs)
+	{
+		assert (obj->getType() != LDObject::ESubfile);
+		LDPolygon* data = obj->getPolygon();
+
+		if (data != null)
+		{
+			m_PolygonData << *data;
+			delete data;
+		}
+
+		obj->deleteSelf();
+	}
+
+	m_needsGLReInit = false;
+}
+
+// =============================================================================
+//
+QList<LDPolygon> LDDocument::inlinePolygons()
+{
+	if (m_needsGLReInit == true)
+		initializeGLData();
+
+	return m_PolygonData;
+}
+
+// =============================================================================
 // -----------------------------------------------------------------------------
-LDObjectList LDDocument::inlineContents (LDSubfile::InlineFlags flags)
+LDObjectList LDDocument::inlineContents (bool deep, bool renderinline)
 {
 	// Possibly substitute with logoed studs:
 	// stud.dat -> stud-logo.dat
 	// stud2.dat -> stud-logo2.dat
-	if (gl_logostuds && (flags & LDSubfile::RendererInline))
+	if (gl_logostuds && renderinline)
 	{
 		// Ensure logoed studs are loaded first
 		loadLogoedStuds();
 
-		if (getName() == "stud.dat" && g_logoedStud)
-			return g_logoedStud->inlineContents (flags);
-		elif (getName() == "stud2.dat" && g_logoedStud2)
-			return g_logoedStud2->inlineContents (flags);
+		if (getName() == "stud.dat" && g_logoedStud != null)
+			return g_logoedStud->inlineContents (deep, renderinline);
+		elif (getName() == "stud2.dat" && g_logoedStud2 != null)
+			return g_logoedStud2->inlineContents (deep, renderinline);
 	}
 
 	LDObjectList objs, objcache;
 
-	bool deep = flags & LDSubfile::DeepInline,
-		 doCache = flags & LDSubfile::CacheInline;
-
-	if (m_needsCache)
-	{
-		clearCache();
-		doCache = true;
-	}
-
-	// If we have this cached, just create a copy of that
-	if (deep && getCache().isEmpty() == false)
+	for (LDObject* obj : getObjects())
 	{
-		for (LDObject* obj : getCache())
-			objs << obj->createCopy();
-	}
-	else
-	{
-		if (!deep)
-			doCache = false;
-
-		for (LDObject* obj : getObjects())
-		{
-			// Skip those without scemantic meaning
-			if (!obj->isScemantic())
-				continue;
+		// Skip those without scemantic meaning
+		if (!obj->isScemantic())
+			continue;
 
-			// Got another sub-file reference, inline it if we're deep-inlining. If not,
-			// just add it into the objects normally. Also, we only cache immediate
-			// subfiles and this is not one. Yay, recursion!
-			if (deep && obj->getType() == LDObject::ESubfile)
-			{
-				LDSubfile* ref = static_cast<LDSubfile*> (obj);
-
-				// We only want to cache immediate subfiles, so shed the caching
-				// flag when recursing deeper in hierarchy.
-				LDObjectList otherobjs = ref->inlineContents (flags & ~ (LDSubfile::CacheInline));
+		// Got another sub-file reference, inline it if we're deep-inlining. If not,
+		// just add it into the objects normally. Yay, recursion!
+		if (deep == true && obj->getType() == LDObject::ESubfile)
+		{
+			LDSubfile* ref = static_cast<LDSubfile*> (obj);
+			LDObjectList otherobjs = ref->inlineContents (deep, renderinline);
 
-				for (LDObject* otherobj : otherobjs)
-				{
-					// Cache this object, if desired
-					if (doCache)
-						objcache << otherobj->createCopy();
-
-					objs << otherobj;
-				}
-			}
-			else
-			{
-				if (doCache)
-					objcache << obj->createCopy();
-
-				objs << obj->createCopy();
-			}
+			for (LDObject* otherobj : otherobjs)
+				objs << otherobj;
 		}
-
-		if (doCache)
-			setCache (objcache);
+		else
+			objs << obj->createCopy();
 	}
 
 	return objs;
--- a/src/Document.h	Mon Feb 24 00:25:18 2014 +0200
+++ b/src/Document.h	Mon Feb 24 00:59:57 2014 +0200
@@ -23,6 +23,7 @@
 #include "Main.h"
 #include "LDObject.h"
 #include "EditHistory.h"
+#include "GLShared.h"
 
 class History;
 class OpenProgressDialog;
@@ -65,7 +66,7 @@
 		PROPERTY (public,	QString,					FullPath,		STR_OPS,	STOCK_WRITE)
 		PROPERTY (public,	QString,					DefaultName,	STR_OPS,	STOCK_WRITE)
 		PROPERTY (public,	bool,						Implicit,		BOOL_OPS,	STOCK_WRITE)
-		PROPERTY (public,	LDObjectList,				Cache,			LIST_OPS,	STOCK_WRITE)
+		PROPERTY (public,	QList<LDPolygon>,			PolygonData,	NO_OPS,		STOCK_WRITE)
 		PROPERTY (public,	long,						SavePosition,	NUM_OPS,	STOCK_WRITE)
 		PROPERTY (public,	int,						TabIndex,		NO_OPS,		STOCK_WRITE)
 
@@ -80,7 +81,8 @@
 		QString getDisplayName();
 		const LDObjectList& getSelection() const;
 		bool hasUnsavedChanges() const; // Does this Document.have unsaved changes?
-		LDObjectList inlineContents (LDSubfile::InlineFlags flags);
+		void initializeGLData();
+		LDObjectList inlineContents (bool deep, bool renderinline);
 		void insertObj (int pos, LDObject* obj);
 		int getObjectCount() const;
 		LDObject* getObject (int pos) const;
@@ -90,6 +92,7 @@
 		void setObject (int idx, LDObject* obj);
 		void addReference (LDDocumentPointer* ptr);
 		void removeReference (LDDocumentPointer* ptr);
+		QList<LDPolygon> inlinePolygons();
 
 		inline LDDocument& operator<< (LDObject* obj)
 		{
@@ -147,9 +150,9 @@
 		LDObjectList			m_sel;
 		LDGLData*				m_gldata;
 
-		// If set to true, next inline of this document discards the cache and
-		// re-builds it.
-		bool					m_needsCache;
+		// If set to true, next polygon inline of this document discards the
+		// stored polygon data and re-builds it.
+		bool					m_needsGLReInit;
 
 		static LDDocument*		m_curdoc;
 };
--- a/src/ExternalPrograms.cc	Mon Feb 24 00:25:18 2014 +0200
+++ b/src/ExternalPrograms.cc	Mon Feb 24 00:59:57 2014 +0200
@@ -173,7 +173,7 @@
 		if (obj->getType() == LDObject::ESubfile)
 		{
 			LDSubfile* ref = static_cast<LDSubfile*> (obj);
-			LDObjectList objs = ref->inlineContents (LDSubfile::DeepInline);
+			LDObjectList objs = ref->inlineContents (true, false);
 
 			writeObjects (objs, f);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/GLCompiler.cc	Mon Feb 24 00:59:57 2014 +0200
@@ -0,0 +1,387 @@
+#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"
+
+struct GLErrorInfo
+{
+	GLenum	value;
+	QString	text;
+};
+
+static const GLErrorInfo 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" },
+};
+
+#include <QTime>
+
+#define CLOCK_INIT QTime t0;
+
+#define CLOCK_START \
+{ \
+	t0 = QTime::currentTime(); \
+}
+
+#define CLOCK_TIME(A) \
+{ \
+	fprint (stderr, A ": %1ms\n", t0.msecsTo (QTime::currentTime())); \
+}
+
+#define DEBUG_PRINT(...) fprint (stdout, __VA_ARGS__)
+
+extern_cfg (Bool, gl_blackedges);
+static QList<short>		gWarnedColors;
+static const QColor		gBFCFrontColor (40, 192, 40);
+static const QColor		gBFCBackColor (224, 40, 40);
+
+// =============================================================================
+//
+void checkGLError_private (const char* file, int line)
+{
+	QString errmsg;
+	GLenum errnum = glGetError();
+
+	if (errnum == GL_NO_ERROR)
+		return;
+
+	for (const GLErrorInfo& err : g_GLErrors)
+	{
+		if (err.value == errnum)
+		{
+			errmsg = err.text;
+			break;
+		}
+	}
+
+	log ("OpenGL ERROR: at %1:%2: %3", basename (QString (file)), line, errmsg);
+}
+
+// =============================================================================
+//
+GLCompiler::GLCompiler() :
+	m_Document (null)
+{
+	needMerge();
+}
+
+// =============================================================================
+//
+void GLCompiler::initialize()
+{
+	glGenBuffers (gNumVBOs, &mVBOs[0]);
+	checkGLError();
+}
+
+// =============================================================================
+//
+GLCompiler::~GLCompiler()
+{
+	glDeleteBuffers (gNumVBOs, &mVBOs[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::getIndexColor (int id) const
+{
+	// Calculate a color based from this index. This method caters for
+	// 16777216 objects. I don't think that will be exceeded anytime soon. :)
+	int r = (id / 0x10000) % 0x100,
+		g = (id / 0x100) % 0x100,
+		b = id % 0x100;
+
+	return QColor (r, g, b);
+}
+
+// =============================================================================
+//
+QColor GLCompiler::getPolygonColor (LDPolygon& poly, LDObject* topobj) const
+{
+	QColor qcol;
+
+	if (poly.color == maincolor)
+		qcol = GLRenderer::getMainColor();
+	else
+	{
+		LDColor* col = getColor (poly.color);
+
+		if (col)
+			qcol = col->faceColor;
+	}
+
+	if (poly.color == edgecolor)
+	{
+		qcol = QColor (32, 32, 32); // luma (m_bgcolor) < 40 ? QColor (64, 64, 64) : Qt::black;
+
+		/*
+		if (!gl_blackedges && poly.obj->getParent() && (col = getColor (poly.obj->getParent()->getColor())))
+			qcol = col->edgeColor;
+		*/
+	}
+
+	if (qcol.isValid() == false)
+	{
+		// The color was unknown. Use main color to make the poly.object at least
+		// not appear pitch-black.
+		if (poly.color != edgecolor)
+			qcol = GLRenderer::getMainColor();
+		else
+			qcol = Qt::black;
+
+		// Warn about the unknown color, but only once.
+		for (short i : gWarnedColors)
+			if (poly.color == i)
+				return qcol;
+
+		log ("%1: Unknown color %2!\n", __func__, poly.color);
+		gWarnedColors << poly.color;
+		return qcol;
+	}
+
+	if (topobj->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 mChanged to true
+	// memset (mChanged, 0xFF, sizeof mChanged);
+	for (int i = 0; i < ((int) (sizeof mChanged / sizeof *mChanged)); ++i)
+		mChanged[i] = true;
+}
+
+// =============================================================================
+//
+void GLCompiler::stageForCompilation (LDObject* obj)
+{
+	mStaged << obj;
+	removeDuplicates (mStaged);
+}
+
+// =============================================================================
+//
+void GLCompiler::compileDocument()
+{
+	if (getDocument() == null)
+		return;
+
+	for (LDObject* obj : getDocument()->getObjects())
+		compileObject (obj);
+}
+
+// =============================================================================
+//
+void GLCompiler::compileStaged()
+{
+	for (LDObject* obj : mStaged)
+		compileObject (obj);
+
+	mStaged.clear();
+}
+
+// =============================================================================
+//
+void GLCompiler::prepareVBO (int vbonum)
+{
+	// Compile anything that still awaits it
+	compileStaged();
+
+	if (mChanged[vbonum] == false)
+		return;
+
+	mVBOData[vbonum].clear();
+
+	for (auto it = mObjectInfo.begin(); it != mObjectInfo.end(); ++it)
+		mVBOData[vbonum] += it->data[vbonum];
+
+	glBindBuffer (GL_ARRAY_BUFFER, mVBOs[vbonum]);
+	checkGLError();
+	glBufferData (GL_ARRAY_BUFFER, mVBOData[vbonum].size() * sizeof(float),
+		mVBOData[vbonum].constData(), GL_DYNAMIC_DRAW);
+	checkGLError();
+	glBindBuffer (GL_ARRAY_BUFFER, 0);
+	checkGLError();
+	mChanged[vbonum] = false;
+}
+
+// =============================================================================
+//
+void GLCompiler::dropObject (LDObject* obj)
+{
+	auto it = mObjectInfo.find (obj);
+
+	if (it != mObjectInfo.end())
+	{
+		mObjectInfo.erase (it);
+		needMerge();
+	}
+}
+
+// =============================================================================
+//
+void GLCompiler::compileObject (LDObject* obj)
+{
+	log ("compile #%1\n", obj->getID() );
+	ObjectVBOInfo info;
+	dropObject (obj);
+	compileSubObject (obj, obj, &info);
+	mObjectInfo[obj] = info;
+	needMerge();
+}
+
+// =============================================================================
+//
+void GLCompiler::compilePolygon (LDPolygon& poly, LDObject* topobj, ObjectVBOInfo* objinfo)
+{
+	EVBOSurface surface;
+	int numverts;
+
+	switch (poly.num)
+	{
+		case 3:	surface = vboTriangles;	numverts = 3; break;
+		case 4:	surface = vboQuads;		numverts = 4; break;
+		case 2:	surface = vboLines;		numverts = 2; break;
+		case 5:	surface = vboCondLines;	numverts = 2; break;
+
+		default:
+			log ("OMGWTFBBQ weird polygon with number %1 (topobj: #%2, %3), origin: %4",
+				(int) poly.num, topobj->getID(), topobj->getTypeName(), poly.origin);
+			assert (false);
+	}
+
+	for (int complement = 0; complement < vboNumComplements; ++complement)
+	{
+		const int vbonum			= getVBONumber (surface, (EVBOComplement) complement);
+		QVector<GLfloat>& vbodata	= objinfo->data[vbonum];
+		const QColor normalColor	= getPolygonColor (poly, topobj);
+		const QColor pickColor		= getIndexColor (topobj->getID());
+
+		for (int vert = 0; vert < numverts; ++vert)
+		{
+			switch ((EVBOComplement) complement)
+			{
+				case vboSurfaces:
+				{
+					// Write coordinates. Apparently Z must be flipped too?
+					vbodata	<< poly.vertices[vert].x()
+							<< -poly.vertices[vert].y()
+							<< -poly.vertices[vert].z();
+					break;
+				}
+
+				case vboNormalColors:
+				{
+					writeColor (vbodata, normalColor);
+					break;
+				}
+
+				case vboPickColors:
+				{
+					writeColor (vbodata, pickColor);
+					break;
+				}
+
+				case vboBFCFrontColors:
+				{
+					writeColor (vbodata, gBFCFrontColor);
+					break;
+				}
+
+				case vboBFCBackColors:
+				{
+					writeColor (vbodata, gBFCBackColor);
+					break;
+				}
+
+				case vboNumComplements:
+					break;
+			}
+		}
+	}
+}
+
+// =============================================================================
+//
+void GLCompiler::compileSubObject (LDObject* obj, LDObject* topobj, ObjectVBOInfo* objinfo)
+{
+	CLOCK_INIT
+
+	switch (obj->getType())
+	{
+		// Note: We cannot split quads into triangles here, it would mess up the
+		// wireframe view. Quads must go into separate vbos.
+		case LDObject::ETriangle:
+		case LDObject::EQuad:
+		case LDObject::ELine:
+		case LDObject::ECondLine:
+		{
+			LDPolygon* poly = obj->getPolygon();
+			poly->id = topobj->getID();
+			compilePolygon (*poly, topobj, objinfo);
+			delete poly;
+			break;
+		}
+
+		case LDObject::ESubfile:
+		{
+			LDSubfile* ref = static_cast<LDSubfile*> (obj);
+			auto data = ref->inlinePolygons();
+
+			for (LDPolygon& poly : data)
+			{
+				poly.id = topobj->getID();
+				compilePolygon (poly, topobj, objinfo);
+			}
+			break;
+		}
+
+		default:
+			break;
+	}
+}
+
+// =============================================================================
+//
+void GLCompiler::writeColor (QVector<GLfloat>& array, const QColor& color)
+{
+	array	<< ((float) color.red()) / 255.0f
+			<< ((float) color.green()) / 255.0f
+			<< ((float) color.blue()) / 255.0f
+			<< ((float) color.alpha()) / 255.0f;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/GLCompiler.h	Mon Feb 24 00:59:57 2014 +0200
@@ -0,0 +1,84 @@
+/*
+ *  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>
+
+// =============================================================================
+//
+class GLCompiler
+{
+	PROPERTY (public,	LDDocumentPointer,	Document,	NO_OPS,	STOCK_WRITE)
+
+	public:
+		struct ObjectVBOInfo
+		{
+			QVector<GLfloat> data[gNumVBOs];
+		};
+
+		GLCompiler();
+		~GLCompiler();
+		void				compileDocument();
+		void				dropObject (LDObject* obj);
+		void				initialize();
+		QColor				getPolygonColor (LDPolygon& poly, LDObject* topobj) const;
+		QColor				getIndexColor (int id) const;
+		void				needMerge();
+		void				prepareVBO (int vbonum);
+		void				stageForCompilation (LDObject* obj);
+
+		static uint32		getColorRGB (const QColor& color);
+
+		static inline int	getVBONumber (EVBOSurface surface, EVBOComplement complement)
+		{
+			return (surface * vboNumComplements) + complement;
+		}
+
+		inline GLuint		getVBO (int vbonum) const
+		{
+			return mVBOs[vbonum];
+		}
+
+		inline int			getVBOCount (int vbonum) const
+		{
+			return mVBOData[vbonum].size() / 3;
+		}
+
+	private:
+		void			compileStaged();
+		void			compileObject (LDObject* obj);
+		void			compileSubObject (LDObject* obj, LDObject* topobj, GLCompiler::ObjectVBOInfo* objinfo);
+		void			writeColor (QVector<float>& array, const QColor& color);
+		void			compilePolygon (LDPolygon& poly, LDObject* topobj, GLCompiler::ObjectVBOInfo* objinfo);
+
+		QMap<LDObject*, ObjectVBOInfo>		mObjectInfo;
+		QVector<GLfloat>					mVBOData[gNumVBOs];
+		GLuint								mVBOs[gNumVBOs];
+		bool								mChanged[gNumVBOs];
+		LDObjectList						mStaged; // 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	Mon Feb 24 00:25:18 2014 +0200
+++ b/src/GLRenderer.cc	Mon Feb 24 00:59:57 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"
 
@@ -96,20 +98,22 @@
 	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;
+static GLuint g_GLAxes_ColorVBO;
 
 // =============================================================================
 //
@@ -121,6 +125,7 @@
 	m_EditMode = ESelectMode;
 	m_rectdraw = false;
 	m_panning = false;
+	m_compiler = new GLCompiler;
 	setFile (null);
 	setDrawOnly (false);
 	setMessageLog (null);
@@ -157,6 +162,8 @@
 
 	for (CameraIcon& info : m_cameraIcons)
 		delete info.img;
+
+	delete m_compiler;
 }
 
 // =============================================================================
@@ -244,11 +251,49 @@
 	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];
+	float colordata[18];
+	memset (axesdata, 0, sizeof axesdata);
+
+	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);
+	glGenBuffers (1, &g_GLAxes_ColorVBO);
+	glBindBuffer (GL_ARRAY_BUFFER, g_GLAxes_ColorVBO);
+	glBufferData (GL_ARRAY_BUFFER, sizeof colordata, colordata, GL_STATIC_DRAW);
+	glBindBuffer (GL_ARRAY_BUFFER, 0);
 }
 
 // =============================================================================
@@ -281,105 +326,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();
@@ -387,10 +334,10 @@
 }
 
 // =============================================================================
-//
+// -----------------------------------------------------------------------------
 void GLRenderer::hardRefresh()
 {
-	compileAllObjects();
+	m_compiler->compileDocument();
 	refresh();
 
 	glLineWidth (gl_linethickness);
@@ -461,47 +408,86 @@
 		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);
+	glEnableClientState (GL_VERTEX_ARRAY);
+	glEnableClientState (GL_COLOR_ARRAY);
 
-		for (LDObject* obj : getFile()->getObjects())
-		{
-			if (obj->isHidden())
-				continue;
-
-			glCullFace (GL_BACK);
-			glCallList (obj->glLists[BFCFrontList]);
-
-			glCullFace (GL_FRONT);
-			glCallList (obj->glLists[BFCBackList]);
-		}
-
-		glDisable (GL_CULL_FACE);
+	if (isPicking())
+	{
+		drawVBOs (vboTriangles, vboPickColors, GL_TRIANGLES);
+		drawVBOs (vboQuads, vboPickColors, GL_QUADS);
+		drawVBOs (vboLines, vboPickColors, GL_LINES);
+		drawVBOs (vboCondLines, vboPickColors, GL_LINES);
 	}
 	else
 	{
-		for (LDObject* obj : getFile()->getObjects())
+		if (gl_colorbfc)
+		{
+			glEnable (GL_CULL_FACE);
+			glCullFace (GL_BACK);
+			drawVBOs (vboTriangles, vboBFCFrontColors, GL_TRIANGLES);
+			drawVBOs (vboQuads, vboBFCFrontColors, GL_QUADS);
+			glCullFace (GL_FRONT);
+			drawVBOs (vboTriangles, vboBFCBackColors, GL_TRIANGLES);
+			drawVBOs (vboQuads, vboBFCBackColors, GL_QUADS);
+			glDisable (GL_CULL_FACE);
+		}
+		else
 		{
-			if (obj->isHidden())
-				continue;
+			drawVBOs (vboTriangles, vboNormalColors, GL_TRIANGLES);
+			drawVBOs (vboQuads, vboNormalColors, GL_QUADS);
+		}
+
+		drawVBOs (vboLines, vboNormalColors, GL_LINES);
+		drawVBOs (vboCondLines, vboNormalColors, GL_LINES);
 
-			glCallList (obj->glLists[list]);
+		if (gl_axes)
+		{
+			glBindBuffer (GL_ARRAY_BUFFER, g_GLAxes_VBO);
+			glVertexPointer (3, GL_FLOAT, 0, NULL);
+			glBindBuffer (GL_ARRAY_BUFFER, g_GLAxes_VBO);
+			glColorPointer (3, GL_FLOAT, 0, NULL);
+			glDrawArrays (GL_LINES, 0, 6);
+			checkGLError();
 		}
 	}
 
-	if (gl_axes && !isPicking() && !isDrawOnly())
-		glCallList (m_axeslist);
-
 	glPopMatrix();
+	glBindBuffer (GL_ARRAY_BUFFER, 0);
+	glDisableClientState (GL_VERTEX_ARRAY);
+	glDisableClientState (GL_COLOR_ARRAY);
+	checkGLError();
+	glDisable (GL_CULL_FACE);
 	glMatrixMode (GL_MODELVIEW);
 	glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
 }
 
 // =============================================================================
 //
+void GLRenderer::drawVBOs (EVBOSurface surface, EVBOComplement colors, GLenum type)
+{
+	int surfacenum = m_compiler->getVBONumber (surface, vboSurfaces);
+	int colornum = m_compiler->getVBONumber (surface, colors);
+
+	m_compiler->prepareVBO (surfacenum);
+	m_compiler->prepareVBO (colornum);
+	GLuint surfacevbo = m_compiler->getVBO (surfacenum);
+	GLuint colorvbo = m_compiler->getVBO (colornum);
+	GLsizei count = m_compiler->getVBOCount (surfacevbo);
+
+	if (count > 0)
+	{
+		glBindBuffer (GL_ARRAY_BUFFER, surfacevbo);
+		glVertexPointer (3, GL_FLOAT, 0, null);
+		checkGLError();
+		glBindBuffer (GL_ARRAY_BUFFER, colorvbo);
+		glColorPointer (4, GL_FLOAT, 0, null);
+		checkGLError();
+		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.
 //
@@ -896,127 +882,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();
 }
 
 // =============================================================================
@@ -1375,8 +1241,8 @@
 	{
 		qint32 idx =
 			(*(pixelptr + 0) * 0x10000) +
-			(*(pixelptr + 1) * 0x00100) +
-			(*(pixelptr + 2) * 0x00001);
+			(*(pixelptr + 1) * 0x100) +
+			*(pixelptr + 2);
 		pixelptr += 4;
 
 		if (idx == 0xFFFFFF)
@@ -1472,6 +1338,7 @@
 void GLRenderer::setFile (LDDocument* const& a)
 {
 	m_File = a;
+	m_compiler->setDocument (a);
 
 	if (a != null)
 	{
@@ -1726,10 +1593,11 @@
 	{
 		for (int i = 0; i < obj->vertices(); ++i)
 			verts << obj->getVertex (i);
-	} elif (obj->getType() == LDObject::ESubfile)
+	}
+	elif (obj->getType() == LDObject::ESubfile)
 	{
 		LDSubfile* ref = static_cast<LDSubfile*> (obj);
-		LDObjectList objs = ref->inlineContents (LDSubfile::DeepCacheInline);
+		LDObjectList objs = ref->inlineContents (true, false);
 
 		for (LDObject* obj : objs)
 		{
@@ -1745,32 +1613,27 @@
 //
 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);
 	m_knownVerts << verts;
 	removeDuplicates (m_knownVerts);
+	*/
 
 	obj->setGLInit (true);
 }
 
 // =============================================================================
 //
+void GLRenderer::forgetObject (LDObject* obj)
+{
+	m_compiler->dropObject (obj);
+}
+
+// =============================================================================
+//
 uchar* GLRenderer::getScreencap (int& w, int& h)
 {
 	w = m_width;
@@ -1794,7 +1657,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))
 		{
@@ -1808,20 +1671,6 @@
 
 // =============================================================================
 //
-void GLRenderer::deleteLists (LDObject* obj)
-{
-	// Delete the lists but only if they have been initialized
-	if (!obj->isGLInit())
-		return;
-
-	for (const GL::ListType listType : g_glListTypes)
-		glDeleteLists (obj->glLists[listType], 1);
-
-	obj->setGLInit (false);
-}
-
-// =============================================================================
-//
 Axis GLRenderer::getCameraAxis (bool y, GLRenderer::EFixedCamera camid)
 {
 	if (camid == (GL::EFixedCamera) - 1)
@@ -1872,11 +1721,11 @@
 
 	// Set alpha of all pixels to 0.5
 	for (long i = 0; i < img->width(); ++i)
-		for (long j = 0; j < img->height(); ++j)
-		{
-			uint32 pixel = img->pixel (i, j);
-			img->setPixel (i, j, 0x80000000 | (pixel & 0x00FFFFFF));
-		}
+	for (long j = 0; j < img->height(); ++j)
+	{
+		uint32 pixel = img->pixel (i, j);
+		img->setPixel (i, j, 0x80000000 | (pixel & 0x00FFFFFF));
+	}
 
 	updateOverlayObjects();
 	return true;
--- a/src/GLRenderer.h	Mon Feb 24 00:25:18 2014 +0200
+++ b/src/GLRenderer.h	Mon Feb 24 00:59:57 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;
@@ -149,10 +151,10 @@
 		void           compileAllObjects();
 		void           drawGLScene();
 		void           endDraw (bool accept);
+		void           forgetObject (LDObject* obj);
 		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 +173,7 @@
 		void           zoomAllToFit();
 
 		static void    deleteLists (LDObject* obj);
+		static QColor  getMainColor();
 
 	protected:
 		void           contextMenuEvent (QContextMenuEvent* ev);
@@ -216,6 +219,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 +249,12 @@
 		// Convert a 2D point to a 3D point
 		Vertex         coordconv2_3 (const QPoint& pos2d, bool snap) const;
 
+		// Draw a VBO array
+		void           drawVBOs (EVBOSurface surface, EVBOComplement colors, 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 +300,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	Mon Feb 24 00:59:57 2014 +0200
@@ -0,0 +1,70 @@
+/*
+ *  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
+#include <QString>
+
+class LDObject;
+
+struct LDPolygon
+{
+	char		num;
+	Vertex		vertices[4];
+	int			id;
+	int			color;
+	QString		origin;
+
+	inline int numVertices() const
+	{
+		return (num == 5) ? 4 : num;
+	}
+};
+
+enum EVBOSurface
+{
+	vboLines,
+	vboTriangles,
+	vboQuads,
+	vboCondLines,
+	vboNumSurfaces
+};
+
+enum EVBOComplement
+{
+	vboSurfaces,
+	vboNormalColors,
+	vboPickColors,
+	vboBFCFrontColors,
+	vboBFCBackColors,
+	vboNumComplements
+};
+
+// KDevelop doesn't seem to understand some VBO stuff
+#ifdef IN_IDE_PARSER
+using GLenum = unsigned int;
+using GLuint = unsigned int;
+void glBindBuffer (GLenum, GLuint);
+void glGenBuffers (GLuint, GLuint*);
+void glDeleteBuffers (GLuint, GLuint*);
+void glBufferData (GLuint, GLuint, void*, GLuint);
+#endif
+
+static const int gNumVBOs = vboNumSurfaces * vboNumComplements;
+
+#endif // LDFORGE_GLSHARED_H
\ No newline at end of file
--- a/src/LDObject.cc	Mon Feb 24 00:25:18 2014 +0200
+++ b/src/LDObject.cc	Mon Feb 24 00:59:57 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, "");
@@ -269,7 +270,7 @@
 		getFile()->forgetObject (this);
 
 	// Delete the GL lists
-	GL::deleteLists (this);
+	g_win->R()->forgetObject (this);
 
 	// Remove this object from the list of LDObjects
 	g_LDObjects.removeOne (this);
@@ -319,9 +320,9 @@
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-LDObjectList LDSubfile::inlineContents (InlineFlags flags)
+LDObjectList LDSubfile::inlineContents (bool deep, bool render)
 {
-	LDObjectList objs = getFileInfo()->inlineContents (flags);
+	LDObjectList objs = getFileInfo()->inlineContents (deep, render);
 
 	// Transform the objects
 	for (LDObject* obj : objs)
@@ -335,6 +336,45 @@
 }
 
 // =============================================================================
+//
+LDPolygon* LDObject::getPolygon()
+{
+	Type ot = getType();
+	int num =
+		(ot == LDObject::ELine)		?	2 :
+		(ot == LDObject::ETriangle)	?	3 :
+		(ot == LDObject::EQuad)		?	4 :
+		(ot == LDObject::ECondLine)	?	5 :
+										0;
+	if (num == 0)
+		return null;
+
+	LDPolygon* data = new LDPolygon;
+	data->id = getID();
+	data->num = num;
+	data->color = getColor();
+	data->origin = getOrigin();
+
+	for (int i = 0; i < data->numVertices(); ++i)
+		data->vertices[i] = getVertex (i);
+
+	return data;
+}
+
+// =============================================================================
+//
+QList<LDPolygon> LDSubfile::inlinePolygons()
+{
+	QList<LDPolygon> data = getFileInfo()->inlinePolygons();
+
+	for (LDPolygon& entry : data)
+		for (int i = 0; i < entry.numVertices(); ++i)
+			entry.vertices[i].transform (getTransform(), getPosition());
+
+	return data;
+}
+
+// =============================================================================
 // -----------------------------------------------------------------------------
 long LDObject::getIndex() const
 {
@@ -831,5 +871,28 @@
 	*/
 
 	LDObject* copy = parseLine (raw());
+
+	if (getOrigin().isEmpty() == false)
+		copy->setOrigin (getOrigin());
+	elif (getFile() != null)
+		copy->setOrigin (getFile()->getDisplayName() + ":" + QString::number (getIndex()));
+
 	return copy;
-}
\ No newline at end of file
+}
+
+// =============================================================================
+//
+void LDSubfile::setFileInfo (const LDDocumentPointer& a)
+{
+	m_FileInfo = a;
+
+	// If it's an immediate subfile reference (i.e. this subfile belongs in an
+	// explicit file), we need to pre-compile the GL polygons for the document
+	// if they don't exist already.
+	if (a != null &&
+		a->isImplicit() == false &&
+		a->getPolygonData().isEmpty())
+	{
+		a->initializeGLData();
+	}
+};
\ No newline at end of file
--- a/src/LDObject.h	Mon Feb 24 00:25:18 2014 +0200
+++ b/src/LDObject.h	Mon Feb 24 00:59:57 2014 +0200
@@ -22,6 +22,7 @@
 #include "Main.h"
 #include "Types.h"
 #include "misc/DocumentPointer.h"
+#include "GLShared.h"
 
 #define LDOBJ(T)										\
 protected:												\
@@ -74,6 +75,7 @@
 	PROPERTY (private,		int,			ID,				NUM_OPS,	STOCK_WRITE)
 	PROPERTY (public,		int,			Color,			NUM_OPS,	CUSTOM_WRITE)
 	PROPERTY (public,		bool,			GLInit,			BOOL_OPS,	STOCK_WRITE)
+	PROPERTY (public,		QString,		Origin,			NO_OPS,		STOCK_WRITE)
 
 	public:
 		// Object type codes.
@@ -174,6 +176,7 @@
 		// Get a description of a list of LDObjects
 		static QString describeObjects (const LDObjectList& objs);
 		static LDObject* fromID (int id);
+		LDPolygon* getPolygon();
 
 		// TODO: make these private!
 		// OpenGL list for this object
@@ -395,7 +398,7 @@
 	LDOBJ_COLORED
 	LDOBJ_SCEMANTIC
 	LDOBJ_HAS_MATRIX
-	PROPERTY (public,	LDDocumentPointer, FileInfo, NO_OPS,	STOCK_WRITE)
+	PROPERTY (public,	LDDocumentPointer, FileInfo, NO_OPS,	CUSTOM_WRITE)
 
 	public:
 		enum InlineFlag
@@ -416,7 +419,8 @@
 
 		// Inlines this subfile. Note that return type is an array of heap-allocated
 		// LDObject copies, they must be deleted manually.
-		LDObjectList inlineContents (InlineFlags flags);
+		LDObjectList inlineContents (bool deep, bool render);
+		QList<LDPolygon> inlinePolygons();
 
 	protected:
 		~LDSubfile();
--- a/src/Main.cc	Mon Feb 24 00:25:18 2014 +0200
+++ b/src/Main.cc	Mon Feb 24 00:59:57 2014 +0200
@@ -22,6 +22,7 @@
 #include <QFile>
 #include <QTextStream>
 #include <QDir>
+#include <QDate>
 #include "MainWindow.h"
 #include "Document.h"
 #include "Misc.h"
--- a/src/Types.cc	Mon Feb 24 00:25:18 2014 +0200
+++ b/src/Types.cc	Mon Feb 24 00:59:57 2014 +0200
@@ -329,7 +329,7 @@
 		case LDObject::ESubfile:
 		{
 			LDSubfile* ref = static_cast<LDSubfile*> (obj);
-			LDObjectList objs = ref->inlineContents (LDSubfile::DeepCacheInline);
+			LDObjectList objs = ref->inlineContents (true, false);
 
 			for (LDObject * obj : objs)
 			{
--- a/src/Types.h	Mon Feb 24 00:25:18 2014 +0200
+++ b/src/Types.h	Mon Feb 24 00:59:57 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;
--- a/src/actions/EditActions.cc	Mon Feb 24 00:25:18 2014 +0200
+++ b/src/actions/EditActions.cc	Mon Feb 24 00:59:57 2014 +0200
@@ -123,19 +123,10 @@
 		// inlined contents.
 		long idx = obj->getIndex();
 
-		if (idx == -1)
+		if (idx == -1 || obj->getType() != LDObject::ESubfile)
 			continue;
 
-		LDObjectList objs;
-
-		if (obj->getType() == LDObject::ESubfile)
-			objs = static_cast<LDSubfile*> (obj)->inlineContents (
-					   (LDSubfile::InlineFlags)
-					   ( (deep) ? LDSubfile::DeepInline : 0) |
-					   LDSubfile::CacheInline
-				   );
-		else
-			continue;
+		LDObjectList objs = static_cast<LDSubfile*> (obj)->inlineContents (deep, false);
 
 		// Merge in the inlined objects
 		for (LDObject * inlineobj : objs)
@@ -394,11 +385,13 @@
 	vect[Y] *= *currentGrid().confs[Grid::Y];
 	vect[Z] *= *currentGrid().confs[Grid::Z];
 
+	QTime t0 = QTime::currentTime();
 	for (LDObject* obj : selection())
 	{
 		obj->move (vect);
 		g_win->R()->compileObject (obj);
 	}
+	fprint (stderr, "Move: %1ms\n", t0.msecsTo (QTime::currentTime()));
 
 	g_win->refresh();
 }

mercurial