src/glCompiler.cpp

Sun, 04 Oct 2015 16:45:30 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sun, 04 Oct 2015 16:45:30 +0300
changeset 1008
74cb432812d3
parent 1006
a6b462051ae0
child 1011
4fdd74a04790
permissions
-rw-r--r--

Fixed circle, rectangle and line path modes not working anymore. Add blip coordinates to curve and line path modes. Circle mode for now only can show the coordinates of the initial blip

/*
 *  LDForge: LDraw parts authoring CAD
 *  Copyright (C) 2013 - 2015 Teemu 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/>.
 */

#define GL_GLEXT_PROTOTYPES
#include <GL/glu.h>
#include <GL/glext.h>
#include "glCompiler.h"
#include "ldObject.h"
#include "colors.h"
#include "ldDocument.h"
#include "miscallenous.h"
#include "glRenderer.h"
#include "dialogs.h"
#include "guiutilities.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" },
};

ConfigOption (QString SelectColorBlend = "#0080FF")

// static QMap<LDObject*, String> g_objectOrigins;

// =============================================================================
//
void CheckGLErrorImpl (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;
		}
	}

	print ("OpenGL ERROR: at %1:%2: %3", Basename (QString (file)), line, errmsg);
}

// =============================================================================
//
GLCompiler::GLCompiler (GLRenderer* renderer) :
	HierarchyElement (renderer),
	m_renderer (renderer)
{
	needMerge();
	memset (m_vboSizes, 0, sizeof m_vboSizes);
}

// =============================================================================
//
void GLCompiler::initialize()
{
	initializeOpenGLFunctions();
	glGenBuffers (NumVbos, &m_vbo[0]);
	CHECK_GL_ERROR();
}

// =============================================================================
//
GLCompiler::~GLCompiler()
{
	glDeleteBuffers (NumVbos, &m_vbo[0]);
	CHECK_GL_ERROR();
}

// =============================================================================
//
QColor GLCompiler::indexColorForID (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::getColorForPolygon (LDPolygon& poly, LDObject* topobj, ComplementVboType complement) const
{
	QColor qcol;
	static const QColor bfcFrontColor (64, 192, 80);
	static const QColor bfcBackColor (208, 64, 64);

	switch (complement)
	{
	case SurfacesVboComplement:
	case NumVboComplements:
		return QColor();

	case BfcFrontColorsVboComplement:
		qcol = bfcFrontColor;
		break;

	case BfcBackColorsVboComplement:
		qcol = bfcBackColor;
		break;

	case PickColorsVboComplement:
		return indexColorForID (topobj->id());

	case RandomColorsVboComplement:
		qcol = topobj->randomColor();
		break;

	case NormalColorsVboComplement:
		if (poly.color == MainColor)
		{
			if (topobj->color() == MainColor)
				qcol = guiUtilities()->mainColorRepresentation();
			else
				qcol = topobj->color().faceColor();
		}
		else if (poly.color == EdgeColor)
		{
			qcol = luma (QColor (m_config->backgroundColor())) > 40 ? Qt::black : Qt::white;
		}
		else
		{
			LDColor col = poly.color;

			if (col.isValid())
				qcol = col.faceColor();
		}
		break;
	}

	if (not qcol.isValid())
	{
		// The color was unknown. Use main color to make the polygon at least
		// not appear pitch-black.
		if (poly.num != 2 and poly.num != 5)
			qcol = guiUtilities()->mainColorRepresentation();
		else
			qcol = Qt::black;

		// Warn about the unknown color, but only once.
		static QList<int> warnedColors;
		if (not warnedColors.contains (poly.color))
		{
			print ("Unknown color %1!\n", poly.color);
			warnedColors << poly.color;
		}

		return qcol;
	}

	double blendAlpha = 0.0;

	if (topobj->isSelected())
		blendAlpha = 1.0;
	else if (topobj == m_renderer->objectAtCursor())
		blendAlpha = 0.5;

	if (blendAlpha != 0.0)
	{
		QColor selcolor (m_config->selectColorBlend());
		double denom = blendAlpha + 1.0;
		qcol.setRed ((qcol.red() + (selcolor.red() * blendAlpha)) / denom);
		qcol.setGreen ((qcol.green() + (selcolor.green() * blendAlpha)) / denom);
		qcol.setBlue ((qcol.blue() + (selcolor.blue() * blendAlpha)) / denom);
	}

	return qcol;
}

// =============================================================================
//
void GLCompiler::needMerge()
{
	for (int i = 0; i < countof (m_vboChanged); ++i)
		m_vboChanged[i] = true;
}

// =============================================================================
//
void GLCompiler::stageForCompilation (LDObject* obj)
{
	/*
	g_objectOrigins[obj] = format ("%1:%2 (%3)",
		obj->document()->getDisplayName(), obj->lineNumber(), obj->typeName());
	*/

	m_staged << obj;
}

// =============================================================================
//
void GLCompiler::unstage (LDObject* obj)
{
	m_staged.remove (obj);
}

// =============================================================================
//
void GLCompiler::compileDocument (LDDocument* doc)
{
	if (doc)
	{
		for (LDObject* obj : doc->objects())
			compileObject (obj);
	}
}

// =============================================================================
//
void GLCompiler::compileStaged()
{
	for (QSetIterator<LDObject*> it (m_staged); it.hasNext();)
		compileObject (it.next());

	m_staged.clear();
}

// =============================================================================
//
void GLCompiler::prepareVBO (int vbonum)
{
	// Compile anything that still awaits it
	compileStaged();

	if (not m_vboChanged[vbonum])
		return;

	QVector<GLfloat> vbodata;

	for (auto it = m_objectInfo.begin(); it != m_objectInfo.end();)
	{
		if (it.key() == nullptr)
		{
			it = m_objectInfo.erase (it);
			continue;
		}

		if (it.key()->document() == currentDocument() and not it.key()->isHidden())
			vbodata += it->data[vbonum];

		++it;
	}

	glBindBuffer (GL_ARRAY_BUFFER, m_vbo[vbonum]);
	glBufferData (GL_ARRAY_BUFFER, vbodata.size() * sizeof(GLfloat), vbodata.constData(), GL_STATIC_DRAW);
	glBindBuffer (GL_ARRAY_BUFFER, 0);
	CHECK_GL_ERROR();
	m_vboChanged[vbonum] = false;
	m_vboSizes[vbonum] = vbodata.size();
}

// =============================================================================
//
void GLCompiler::dropObjectInfo (LDObject* obj)
{
	if (m_objectInfo.contains (obj))
	{
		m_objectInfo.remove (obj);
		needMerge();
	}
}

// =============================================================================
//
void GLCompiler::compileObject (LDObject* obj)
{
//	print ("Compile %1\n", g_objectOrigins[obj]);

	if (obj == nullptr or obj->document() == nullptr or obj->document()->isCache())
		return;

	ObjectVBOInfo info;
	info.isChanged = true;
	dropObjectInfo (obj);

	switch (obj->type())
	{
		// Note: We cannot split quads into triangles here, it would mess up the
		// wireframe view. Quads must go into separate vbos.
		case OBJ_Triangle:
		case OBJ_Quad:
		case OBJ_Line:
		case OBJ_CondLine:
		{
			LDPolygon* poly = obj->getPolygon();
			poly->id = obj->id();
			compilePolygon (*poly, obj, &info);
			delete poly;
			break;
		}

		case OBJ_Subfile:
		{
			LDSubfile* ref = static_cast<LDSubfile*> (obj);
			auto data = ref->inlinePolygons();

			for (LDPolygon& poly : data)
			{
				poly.id = obj->id();
				compilePolygon (poly, obj, &info);
			}
			break;
		}

	case OBJ_BezierCurve:
		{
			LDBezierCurve* curve = static_cast<LDBezierCurve*> (obj);
			for (LDPolygon& poly : curve->rasterizePolygons())
			{
				poly.id = obj->id();
				compilePolygon (poly, obj, &info);
			}
		}
		break;

		default:
			break;
	}

	m_objectInfo[obj] = info;
	needMerge();
}

// =============================================================================
//
void GLCompiler::compilePolygon (LDPolygon& poly, LDObject* topobj, ObjectVBOInfo* objinfo)
{
	SurfaceVboType surface;
	int numverts;

	switch (poly.num)
	{
		case 2:	surface = LinesVbo;		numverts = 2; break;
		case 3:	surface = TrianglesVbo;	numverts = 3; break;
		case 4:	surface = QuadsVbo;		numverts = 4; break;
		case 5:	surface = ConditionalLinesVbo;	numverts = 2; break;
		default: return;
	}

	for (ComplementVboType complement = FirstVboComplement; complement < NumVboComplements; ++complement)
	{
		const int vbonum			= vboNumber (surface, complement);
		QVector<GLfloat>& vbodata	= objinfo->data[vbonum];
		const QColor color			= getColorForPolygon (poly, topobj, complement);

		for (int vert = 0; vert < numverts; ++vert)
		{
			if (complement == SurfacesVboComplement)
			{
				// Write coordinates. Apparently Z must be flipped too?
				vbodata	<< poly.vertices[vert].x()
						<< -poly.vertices[vert].y()
						<< -poly.vertices[vert].z();
			}
			else
			{
				vbodata	<< ((GLfloat) color.red()) / 255.0f
						<< ((GLfloat) color.green()) / 255.0f
						<< ((GLfloat) color.blue()) / 255.0f
						<< ((GLfloat) color.alpha()) / 255.0f;
			}
		}
	}
}

void GLCompiler::setRenderer (GLRenderer* renderer)
{
	m_renderer = renderer;
}

int GLCompiler::vboNumber (SurfaceVboType surface, ComplementVboType complement)
{
	return (surface * NumVboComplements) + complement;
}

GLuint GLCompiler::vbo (int vbonum) const
{
	return m_vbo[vbonum];
}

int GLCompiler::vboSize (int vbonum) const
{
	return m_vboSizes[vbonum];
}

mercurial