Mon, 24 Feb 2014 00:59:57 +0200
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(); }