- work on improved rendering performance

Thu, 06 Feb 2014 22:11:28 +0200

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Thu, 06 Feb 2014 22:11:28 +0200
changeset 681
c1cc036c6e1f
parent 680
bf6323f2b1be
child 682
c382df477b0c

- work on improved rendering performance

src/Document.cc file | annotate | diff | comparison | revisions
src/Document.h file | annotate | diff | comparison | revisions
src/ExternalPrograms.cc file | annotate | diff | comparison | revisions
src/GLCompiler.cc file | annotate | diff | comparison | revisions
src/GLCompiler.h file | annotate | diff | comparison | revisions
src/GLRenderer.cc file | annotate | diff | comparison | revisions
src/GLRenderer.h file | annotate | diff | comparison | revisions
src/GLShared.h file | annotate | diff | comparison | revisions
src/LDObject.cc file | annotate | diff | comparison | revisions
src/LDObject.h file | annotate | diff | comparison | revisions
src/Types.cc file | annotate | diff | comparison | revisions
src/actions/EditActions.cc file | annotate | diff | comparison | revisions
--- a/src/Document.cc	Wed Feb 05 06:07:05 2014 +0200
+++ b/src/Document.cc	Thu Feb 06 22:11:28 2014 +0200
@@ -19,6 +19,7 @@
 #include <QMessageBox>
 #include <QFileDialog>
 #include <QDir>
+#include <QTime>
 #include <QApplication>
 #include "Main.h"
 #include "Configuration.h"
@@ -145,10 +146,6 @@
 	for (LDObject* obj : getObjects())
 		obj->deleteSelf();
 
-	// Clear the cache as well
-	for (LDObject* obj : getCache())
-		obj->deleteSelf();
-
 	delete m_History;
 	delete m_gldata;
 
@@ -1192,82 +1189,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	Wed Feb 05 06:07:05 2014 +0200
+++ b/src/Document.h	Thu Feb 06 22:11:28 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,	QListWidgetItem*,			ListItem,		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	Wed Feb 05 06:07:05 2014 +0200
+++ b/src/ExternalPrograms.cc	Thu Feb 06 22:11:28 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);
 
--- a/src/GLCompiler.cc	Wed Feb 05 06:07:05 2014 +0200
+++ b/src/GLCompiler.cc	Thu Feb 06 22:11:28 2014 +0200
@@ -9,12 +9,13 @@
 #include "GLRenderer.h"
 #include "Dialogs.h"
 
-static const struct
+struct GLErrorInfo
 {
 	GLenum	value;
 	QString	text;
-}
-g_GLErrors[] =
+};
+
+static const GLErrorInfo g_GLErrors[] =
 {
 	{ GL_NO_ERROR,						"No error" },
 	{ GL_INVALID_ENUM,					"Unacceptable enumerator passed" },
@@ -26,35 +27,47 @@
 	{ 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> g_warnedColors;
-
-static const QColor g_BFCFrontColor(40, 192, 0);
-static const QColor g_BFCBackColor(224, 0, 0);
+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;
 
-	QString errmsg;
-
-	for (const auto& it : g_GLErrors)
+	for (const GLErrorInfo& err : g_GLErrors)
 	{
-		if (it.value == errnum)
+		if (err.value == errnum)
 		{
-			errmsg = it.text;
+			errmsg = err.text;
 			break;
 		}
 	}
 
-	log ("GL ERROR: %1:%2: %3", file, line, errmsg);
+	log ("OpenGL ERROR: at %1:%2: %3", basename (QString (file)), line, errmsg);
 }
 
 // =============================================================================
@@ -69,13 +82,15 @@
 //
 void GLCompiler::initialize()
 {
-	glGenBuffers (VBO_NumArrays, &m_mainVBOs[0]);
+	glGenBuffers (gNumVBOs, &mVBOs[0]);
 	checkGLError();
 }
 
+// =============================================================================
+//
 GLCompiler::~GLCompiler()
 {
-	glDeleteBuffers (VBO_NumArrays, &m_mainVBOs[0]);
+	glDeleteBuffers (gNumVBOs, &mVBOs[0]);
 	checkGLError();
 }
 
@@ -92,70 +107,64 @@
 
 // =============================================================================
 //
-QColor GLCompiler::getObjectColor (LDObject* obj, GLCompiler::E_ColorType colortype) const
+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 (!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)
+	if (poly.color == maincolor)
 		qcol = GLRenderer::getMainColor();
 	else
 	{
-		LDColor* col = getColor (obj->getColor());
+		LDColor* col = getColor (poly.color);
 
 		if (col)
 			qcol = col->faceColor;
 	}
 
-	if (obj->getColor() == edgecolor)
+	if (poly.color == 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())))
+		/*
+		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 object at least
+		// The color was unknown. Use main color to make the poly.object at least
 		// not appear pitch-black.
-		if (obj->getColor() != edgecolor)
+		if (poly.color != 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)
+		for (short i : gWarnedColors)
+			if (poly.color == i)
 				return qcol;
 
-		log ("%1: Unknown color %2!\n", __func__, obj->getColor());
-		g_warnedColors << obj->getColor();
+		log ("%1: Unknown color %2!\n", __func__, poly.color);
+		gWarnedColors << poly.color;
 		return qcol;
 	}
 
-	if (obj->topLevelParent()->isSelected())
+	if (topobj->isSelected())
 	{
 		// Brighten it up if selected.
 		const int add = 51;
@@ -172,24 +181,27 @@
 //
 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;
+	// 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)
 {
-	m_staged << obj;
-	removeDuplicates (m_staged);
+	mStaged << obj;
+	removeDuplicates (mStaged);
 }
 
 // =============================================================================
 //
 void GLCompiler::compileDocument()
 {
+	if (getDocument() == null)
+		return;
+
 	for (LDObject* obj : getDocument()->getObjects())
 		compileObject (obj);
 }
@@ -198,131 +210,184 @@
 //
 void GLCompiler::compileStaged()
 {
-	for (LDObject* obj : m_staged)
+	for (LDObject* obj : mStaged)
 		compileObject (obj);
 
-	m_staged.clear();
+	mStaged.clear();
 }
 
 // =============================================================================
 //
-void GLCompiler::prepareVBOArray (E_VBOArray type)
+void GLCompiler::prepareVBO (int vbonum)
 {
 	// Compile anything that still awaits it
 	compileStaged();
 
-	if (!m_changed[type])
+	if (mChanged[vbonum] == false)
 		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]);
+	glBindBuffer (GL_ARRAY_BUFFER, mVBOs[vbonum]);
 	checkGLError();
-	glBufferData (GL_ARRAY_BUFFER, m_mainVBOData[type].size() * sizeof(float),
-		m_mainVBOData[type].constData(), GL_DYNAMIC_DRAW);
+	glBufferData (GL_ARRAY_BUFFER, mVBOData[vbonum].size() * sizeof(float),
+		mVBOData[vbonum].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());
+	mChanged[vbonum] = false;
 }
 
 // =============================================================================
 //
-void GLCompiler::forgetObject (LDObject* obj)
+void GLCompiler::uncompileObject (LDObject* obj)
 {
-	auto it = m_objArrays.find (obj);
+	auto it = mObjectInfo.find (obj);
+
+	if (it == mObjectInfo.end())
+		return;
+
+	ObjectVBOInfo* info = &(*it);
 
-	if (it != m_objArrays.end())
+	for (int i = 0; i < gNumVBOs; ++i)
 	{
-		delete *it;
-		m_objArrays.erase (it);
+		if (info->size[i] == 0)
+			continue;
+
+		mVBOData[i].remove (info->offset[i], info->size[i]);
+		mChanged[i] = true;
 	}
+
+	mObjectInfo.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();
-	}
+	ObjectVBOInfo info;
+	uncompileObject (obj);
 
-	compileSubObject (obj, obj);
-	QList<int> data;
+	for (int i = 0; i < gNumVBOs; ++i)
+		info.offset[i] = mVBOData[i].size();
 
-	for (int i = 0; i < VBO_NumArrays; ++i)
-		data << m_objArrays[obj][i].size();
-
-	dlog ("Compiled #%1: %2 coordinates", obj->getID(), data);
+	memset (info.size, 0, sizeof info.size);
+	compileSubObject (obj, obj, &info);
+	mObjectInfo[obj] = info;
 	needMerge();
 }
 
 // =============================================================================
 //
-void GLCompiler::compileSubObject (LDObject* obj, LDObject* topobj)
+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	= mVBOData[vbonum];
+		const QColor normalColor	= getPolygonColor (poly, topobj);
+		const QColor pickColor		= getIndexColor (topobj->getID());
+
+		for (int vert = 0; vert < numverts; ++vert)
+		{
+			objinfo->size[vbonum] += (complement == vboSurfaces) ? 3 : 4;
+
+			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 the quad into triangles here, it would
-		// mess up the wireframe view. Quads must go into a separate array.
+		// 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:
 		{
-			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;
+			LDPolygon* poly = obj->getPolygon();
+			poly->id = topobj->getID();
+			compilePolygon (*poly, topobj, objinfo);
+			delete poly;
+			break;
+		}
 
 		case LDObject::ESubfile:
 		{
+			CLOCK_INIT
+			CLOCK_START
 			LDSubfile* ref = static_cast<LDSubfile*> (obj);
-			LDObjectList subobjs = ref->inlineContents (LDSubfile::DeepCacheInline | LDSubfile::RendererInline);
+			auto data = ref->inlinePolygons();
+			CLOCK_TIME ("Inline")
+			CLOCK_START
 
-			for (LDObject* subobj : subobjs)
+			for (LDPolygon& poly : data)
 			{
-				compileSubObject (subobj, topobj);
-				subobj->deleteSelf();
+				poly.id = topobj->getID();
+				compilePolygon (poly, topobj, objinfo);
 			}
-		} break;
+			CLOCK_TIME ("Compile")
+
+			break;
+		}
 
 		default:
 			break;
@@ -333,8 +398,8 @@
 //
 void GLCompiler::writeColor (QVector<GLfloat>& array, const QColor& color)
 {
-	array	<< color.red()
-			<< color.green()
-			<< color.blue()
-			<< color.alpha();
+	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
--- a/src/GLCompiler.h	Wed Feb 05 06:07:05 2014 +0200
+++ b/src/GLCompiler.h	Thu Feb 06 22:11:28 2014 +0200
@@ -25,78 +25,58 @@
 #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
+		struct ObjectVBOInfo
 		{
-			E_NormalColor,
-			E_PickColor,
+			int offset[gNumVBOs];
+			int size[gNumVBOs];
 		};
 
 		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);
+		void				compileDocument();
+		void				uncompileObject (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 uint32		getColorRGB (const QColor& color);
 
-		inline GLuint	getVBOIndex (E_VBOArray array) const
+		static inline int	getVBONumber (EVBOSurface surface, EVBOComplement complement)
 		{
-			return m_mainVBOs[array];
+			return (surface * vboNumComplements) + complement;
 		}
 
-		inline int		getVBOCount (E_VBOArray array) const
+		inline GLuint		getVBO (int vbonum) const
 		{
-			return m_mainVBOData[array].size() / 3;
+			return mVBOs[vbonum];
 		}
-		void initialize();
+
+		inline int			getVBOCount (int vbonum) const
+		{
+			return mVBOData[vbonum].size() / 3;
+		}
 
 	private:
 		void			compileStaged();
 		void			compileObject (LDObject* obj);
-		void			compileSubObject (LDObject* obj, LDObject* topobj);
-		void			writeColor (QVector< float >& array, const QColor& color);
+		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*, 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
+		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__); }
--- a/src/GLRenderer.cc	Wed Feb 05 06:07:05 2014 +0200
+++ b/src/GLRenderer.cc	Thu Feb 06 22:11:28 2014 +0200
@@ -125,12 +125,12 @@
 	m_EditMode = ESelectMode;
 	m_rectdraw = false;
 	m_panning = false;
+	m_compiler = new GLCompiler;
 	setFile (null);
 	setDrawOnly (false);
 	setMessageLog (null);
 	m_width = m_height = -1;
 	m_hoverpos = g_origin;
-	m_compiler = new GLCompiler;
 
 	m_toolTipTimer = new QTimer (this);
 	m_toolTipTimer->setSingleShot (true);
@@ -417,10 +417,10 @@
 		checkGLError();
 	}
 
-	drawVBOs (VBO_Triangles, GL_TRIANGLES);
-	drawVBOs (VBO_Quads, GL_QUADS);
-	drawVBOs (VBO_Lines, GL_LINES);
-	drawVBOs (VBO_CondLines, GL_LINES);
+	drawVBOs (vboTriangles, GL_TRIANGLES);
+	drawVBOs (vboQuads, GL_QUADS);
+	drawVBOs (vboLines, GL_LINES);
+	drawVBOs (vboCondLines, GL_LINES);
 
 	glPopMatrix();
 	glBindBuffer (GL_ARRAY_BUFFER, 0);
@@ -433,20 +433,25 @@
 
 // =============================================================================
 //
-void GLRenderer::drawVBOs (E_VBOArray arrayType, GLenum type)
+void GLRenderer::drawVBOs (EVBOSurface surface, GLenum type)
 {
-	m_compiler->prepareVBOArray (arrayType);
-	GLuint idx = m_compiler->getVBOIndex (arrayType);
-	GLsizei count = m_compiler->getVBOCount (arrayType);
+	int surfacenum = m_compiler->getVBONumber (surface, vboSurfaces);
+	int colornum = m_compiler->getVBONumber (surface, vboNormalColors);
+
+	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, idx);
-		checkGLError();
+		glBindBuffer (GL_ARRAY_BUFFER, surfacevbo);
 		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);
+		glBindBuffer (GL_ARRAY_BUFFER, colorvbo);
+		glColorPointer (4, GL_FLOAT, 0, null);
+		checkGLError();
 		glDrawArrays (type, 0, count);
 		checkGLError();
 	}
@@ -1558,10 +1563,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)
 		{
@@ -1580,15 +1586,24 @@
 	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->uncompileObject (obj);
+}
+
+// =============================================================================
+//
 uchar* GLRenderer::getScreencap (int& w, int& h)
 {
 	w = m_width;
@@ -1626,20 +1641,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)
--- a/src/GLRenderer.h	Wed Feb 05 06:07:05 2014 +0200
+++ b/src/GLRenderer.h	Thu Feb 06 22:11:28 2014 +0200
@@ -151,6 +151,7 @@
 		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;
@@ -249,7 +250,7 @@
 		Vertex         coordconv2_3 (const QPoint& pos2d, bool snap) const;
 
 		// Draw a VBO array
-		void           drawVBOs (E_VBOArray arrayType, GLenum type);
+		void           drawVBOs (EVBOSurface surface, GLenum type);
 
 		// Determine which color to draw text with
 		QColor         getTextPen() const;
--- a/src/GLShared.h	Wed Feb 05 06:07:05 2014 +0200
+++ b/src/GLShared.h	Thu Feb 06 22:11:28 2014 +0200
@@ -18,18 +18,53 @@
 
 #ifndef LDFORGE_GLSHARED_H
 #define LDFORGE_GLSHARED_H
+#include <QString>
 
-enum E_VBOArray
+class LDObject;
+
+struct LDPolygon
 {
-	VBO_Quads,
-	VBO_Triangles,
-	VBO_Lines,
-	VBO_CondLines,
-	VBO_NormalColors,
-	VBO_BFCFrontColors,
-	VBO_BFCBackColors,
-	VBO_PickColors,
-	VBO_NumArrays
+	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	Wed Feb 05 06:07:05 2014 +0200
+++ b/src/LDObject.cc	Thu Feb 06 22:11:28 2014 +0200
@@ -270,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);
@@ -320,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)
@@ -336,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 (ot == 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
 {
@@ -832,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	Wed Feb 05 06:07:05 2014 +0200
+++ b/src/LDObject.h	Thu Feb 06 22:11:28 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/Types.cc	Wed Feb 05 06:07:05 2014 +0200
+++ b/src/Types.cc	Thu Feb 06 22:11:28 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/actions/EditActions.cc	Wed Feb 05 06:07:05 2014 +0200
+++ b/src/actions/EditActions.cc	Thu Feb 06 22:11:28 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