From 9ed8d9a6732e1eea0265ff84917278a82d81e2dd Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Sun, 11 Dec 2011 23:45:14 +0100 Subject: [PATCH] Some small refactoring of color handling to support using the color() module to change only alpha --- openscad.pro | 3 + src/OpenCSGRenderer.cc | 35 ++++---- src/Preferences.cc | 76 +++++++++--------- src/Preferences.h | 16 +--- src/ThrownTogetherRenderer.cc | 57 +++++++------ src/glview.cc | 34 +++++++- src/polyset.cc | 57 +------------ src/polyset.h | 12 +-- src/renderer.cc | 66 +++++++++++++++ src/renderer.h | 17 ++++ src/rendersettings.cc | 35 ++++++++ src/rendersettings.h | 35 ++++++++ tests/CMakeLists.txt | 6 +- .../opencsgtest/color-tests-expected.png | Bin 11055 -> 10950 bytes .../color-tests-expected.png | Bin 9139 -> 10215 bytes 15 files changed, 280 insertions(+), 169 deletions(-) create mode 100644 src/renderer.cc create mode 100644 src/rendersettings.cc create mode 100644 src/rendersettings.h diff --git a/openscad.pro b/openscad.pro index 31c1e15e94..80dd7d989c 100644 --- a/openscad.pro +++ b/openscad.pro @@ -163,6 +163,7 @@ FORMS += src/MainWindow.ui \ src/OpenCSGWarningDialog.ui HEADERS += src/renderer.h \ + src/rendersettings.h \ src/ThrownTogetherRenderer.h \ src/CGAL_renderer.h \ src/OGL_helper.h \ @@ -217,6 +218,8 @@ HEADERS += src/renderer.h \ SOURCES += src/openscad.cc \ src/mainwin.cc \ src/handle_dep.cc \ + src/renderer.cc \ + src/rendersettings.cc \ src/ThrownTogetherRenderer.cc \ src/glview.cc \ src/export.cc \ diff --git a/src/OpenCSGRenderer.cc b/src/OpenCSGRenderer.cc index a1aafc5b6f..233124b94e 100644 --- a/src/OpenCSGRenderer.cc +++ b/src/OpenCSGRenderer.cc @@ -40,11 +40,11 @@ class OpenCSGPrim : public OpenCSG::Primitive OpenCSG::Primitive(operation, convexity) { } shared_ptr ps; Transform3d m; - int csgmode; + PolySet::csgmode_e csgmode; virtual void render() { glPushMatrix(); glMultMatrixd(m.data()); - ps->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); + ps->render_surface(csgmode, m); glPopMatrix(); } }; @@ -89,24 +89,23 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, double *c = chain->colors[j]; glPushMatrix(); glMultMatrixd(m.data()); - int csgmode = chain->types[j] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; + PolySet::csgmode_e csgmode = chain->types[j] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; if (highlight) { - chain->polysets[j]->render_surface(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20), m, shaderinfo); - } else if (background) { - chain->polysets[j]->render_surface(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10), m, shaderinfo); - } else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0) { - // User-defined color from source - glColor4dv(c); - if (shaderinfo) { - glUniform4f(shaderinfo[1], c[0], c[1], c[2], c[3]); - glUniform4f(shaderinfo[2], (c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0); - } - chain->polysets[j]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m, shaderinfo); + setColor(COLORMODE_HIGHLIGHT, shaderinfo); + csgmode = PolySet::csgmode_e(csgmode + 20); + } + else if (background) { + setColor(COLORMODE_BACKGROUND, shaderinfo); + csgmode = PolySet::csgmode_e(csgmode + 10); + } else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0 || c[3] >= 0) { + // User-defined color or alpha from source + setColor(c, shaderinfo); } else if (chain->types[j] == CSGTerm::TYPE_DIFFERENCE) { - chain->polysets[j]->render_surface(PolySet::COLORMODE_CUTOUT, PolySet::csgmode_e(csgmode), m, shaderinfo); + setColor(COLORMODE_CUTOUT, shaderinfo); } else { - chain->polysets[j]->render_surface(PolySet::COLORMODE_MATERIAL, PolySet::csgmode_e(csgmode), m, shaderinfo); + setColor(COLORMODE_MATERIAL, shaderinfo); } + chain->polysets[j]->render_surface(csgmode, m, shaderinfo); glPopMatrix(); } if (shaderinfo) glUseProgram(0); @@ -124,8 +123,8 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, prim->ps = chain->polysets[i]; prim->m = chain->matrices[i]; prim->csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; - if (highlight) prim->csgmode += 20; - else if (background) prim->csgmode += 10; + if (highlight) prim->csgmode = PolySet::csgmode_e(prim->csgmode + 20); + else if (background) prim->csgmode = PolySet::csgmode_e(prim->csgmode + 10); primitives.push_back(prim); } std::for_each(primitives.begin(), primitives.end(), del_fun()); diff --git a/src/Preferences.cc b/src/Preferences.cc index 577ed4aac9..aefbd121d7 100644 --- a/src/Preferences.cc +++ b/src/Preferences.cc @@ -54,38 +54,38 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent) this->actionTriggered(this->prefsAction3DView); // 3D View pane - this->colorschemes["Cornfield"][BACKGROUND_COLOR] = QColor(0xff, 0xff, 0xe5); - this->colorschemes["Cornfield"][OPENCSG_FACE_FRONT_COLOR] = QColor(0xf9, 0xd7, 0x2c); - this->colorschemes["Cornfield"][OPENCSG_FACE_BACK_COLOR] = QColor(0x9d, 0xcb, 0x51); - this->colorschemes["Cornfield"][CGAL_FACE_FRONT_COLOR] = QColor(0xf9, 0xd7, 0x2c); - this->colorschemes["Cornfield"][CGAL_FACE_BACK_COLOR] = QColor(0x9d, 0xcb, 0x51); - this->colorschemes["Cornfield"][CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); - this->colorschemes["Cornfield"][CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Cornfield"][CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Cornfield"][CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Cornfield"][CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); - - this->colorschemes["Metallic"][BACKGROUND_COLOR] = QColor(0xaa, 0xaa, 0xff); - this->colorschemes["Metallic"][OPENCSG_FACE_FRONT_COLOR] = QColor(0xdd, 0xdd, 0xff); - this->colorschemes["Metallic"][OPENCSG_FACE_BACK_COLOR] = QColor(0xdd, 0x22, 0xdd); - this->colorschemes["Metallic"][CGAL_FACE_FRONT_COLOR] = QColor(0xdd, 0xdd, 0xff); - this->colorschemes["Metallic"][CGAL_FACE_BACK_COLOR] = QColor(0xdd, 0x22, 0xdd); - this->colorschemes["Metallic"][CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); - this->colorschemes["Metallic"][CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Metallic"][CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Metallic"][CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Metallic"][CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); - - this->colorschemes["Sunset"][BACKGROUND_COLOR] = QColor(0xaa, 0x44, 0x44); - this->colorschemes["Sunset"][OPENCSG_FACE_FRONT_COLOR] = QColor(0xff, 0xaa, 0xaa); - this->colorschemes["Sunset"][OPENCSG_FACE_BACK_COLOR] = QColor(0x88, 0x22, 0x33); - this->colorschemes["Sunset"][CGAL_FACE_FRONT_COLOR] = QColor(0xff, 0xaa, 0xaa); - this->colorschemes["Sunset"][CGAL_FACE_BACK_COLOR] = QColor(0x88, 0x22, 0x33); - this->colorschemes["Sunset"][CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); - this->colorschemes["Sunset"][CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Sunset"][CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Sunset"][CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); - this->colorschemes["Sunset"][CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); + this->colorschemes["Cornfield"][RenderSettings::BACKGROUND_COLOR] = QColor(0xff, 0xff, 0xe5); + this->colorschemes["Cornfield"][RenderSettings::OPENCSG_FACE_FRONT_COLOR] = QColor(0xf9, 0xd7, 0x2c); + this->colorschemes["Cornfield"][RenderSettings::OPENCSG_FACE_BACK_COLOR] = QColor(0x9d, 0xcb, 0x51); + this->colorschemes["Cornfield"][RenderSettings::CGAL_FACE_FRONT_COLOR] = QColor(0xf9, 0xd7, 0x2c); + this->colorschemes["Cornfield"][RenderSettings::CGAL_FACE_BACK_COLOR] = QColor(0x9d, 0xcb, 0x51); + this->colorschemes["Cornfield"][RenderSettings::CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); + this->colorschemes["Cornfield"][RenderSettings::CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Cornfield"][RenderSettings::CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Cornfield"][RenderSettings::CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Cornfield"][RenderSettings::CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); + + this->colorschemes["Metallic"][RenderSettings::BACKGROUND_COLOR] = QColor(0xaa, 0xaa, 0xff); + this->colorschemes["Metallic"][RenderSettings::OPENCSG_FACE_FRONT_COLOR] = QColor(0xdd, 0xdd, 0xff); + this->colorschemes["Metallic"][RenderSettings::OPENCSG_FACE_BACK_COLOR] = QColor(0xdd, 0x22, 0xdd); + this->colorschemes["Metallic"][RenderSettings::CGAL_FACE_FRONT_COLOR] = QColor(0xdd, 0xdd, 0xff); + this->colorschemes["Metallic"][RenderSettings::CGAL_FACE_BACK_COLOR] = QColor(0xdd, 0x22, 0xdd); + this->colorschemes["Metallic"][RenderSettings::CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); + this->colorschemes["Metallic"][RenderSettings::CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Metallic"][RenderSettings::CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Metallic"][RenderSettings::CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Metallic"][RenderSettings::CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); + + this->colorschemes["Sunset"][RenderSettings::BACKGROUND_COLOR] = QColor(0xaa, 0x44, 0x44); + this->colorschemes["Sunset"][RenderSettings::OPENCSG_FACE_FRONT_COLOR] = QColor(0xff, 0xaa, 0xaa); + this->colorschemes["Sunset"][RenderSettings::OPENCSG_FACE_BACK_COLOR] = QColor(0x88, 0x22, 0x33); + this->colorschemes["Sunset"][RenderSettings::CGAL_FACE_FRONT_COLOR] = QColor(0xff, 0xaa, 0xaa); + this->colorschemes["Sunset"][RenderSettings::CGAL_FACE_BACK_COLOR] = QColor(0x88, 0x22, 0x33); + this->colorschemes["Sunset"][RenderSettings::CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); + this->colorschemes["Sunset"][RenderSettings::CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Sunset"][RenderSettings::CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Sunset"][RenderSettings::CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); + this->colorschemes["Sunset"][RenderSettings::CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); // Editor pane QFontDatabase db; @@ -102,6 +102,8 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent) connect(this->openCSGWarningBox, SIGNAL(toggled(bool)), this, SLOT(openCSGWarningChanged(bool))); updateGUI(); + + RenderSettings::inst()->setColors(this->colorschemes[getValue("3dview/colorscheme").toString()]); } Preferences::~Preferences() @@ -125,15 +127,13 @@ Preferences::actionTriggered(QAction *action) void Preferences::colorSchemeChanged() { + QString scheme = this->colorSchemeChooser->currentItem()->text(); QSettings settings; - settings.setValue("3dview/colorscheme", this->colorSchemeChooser->currentItem()->text()); + settings.setValue("3dview/colorscheme", scheme); - emit requestRedraw(); -} + RenderSettings::inst()->setColors(this->colorschemes[scheme]); -const QColor &Preferences::color(RenderColor idx) -{ - return this->colorschemes[getValue("3dview/colorscheme").toString()][idx]; + emit requestRedraw(); } void Preferences::fontFamilyChanged(const QString &family) diff --git a/src/Preferences.h b/src/Preferences.h index add1a11b1b..7e22e63d24 100644 --- a/src/Preferences.h +++ b/src/Preferences.h @@ -4,6 +4,7 @@ #include #include #include "ui_Preferences.h" +#include "rendersettings.h" class Preferences : public QMainWindow, public Ui::Preferences { @@ -13,19 +14,6 @@ class Preferences : public QMainWindow, public Ui::Preferences ~Preferences(); static Preferences *inst() { if (!instance) instance = new Preferences(); return instance; } - enum RenderColor { - BACKGROUND_COLOR, - OPENCSG_FACE_FRONT_COLOR, - OPENCSG_FACE_BACK_COLOR, - CGAL_FACE_FRONT_COLOR, - CGAL_FACE_2D_COLOR, - CGAL_FACE_BACK_COLOR, - CGAL_EDGE_FRONT_COLOR, - CGAL_EDGE_BACK_COLOR, - CGAL_EDGE_2D_COLOR, - CROSSHAIR_COLOR - }; - const QColor &color(RenderColor idx); QVariant getValue(const QString &key) const; void apply() const; @@ -48,7 +36,7 @@ public slots: void removeDefaultSettings(); QSettings::SettingsMap defaultmap; - QHash > colorschemes; + QHash > colorschemes; static Preferences *instance; }; diff --git a/src/ThrownTogetherRenderer.cc b/src/ThrownTogetherRenderer.cc index 3ab13ea8f7..36f7b951de 100644 --- a/src/ThrownTogetherRenderer.cc +++ b/src/ThrownTogetherRenderer.cc @@ -70,51 +70,48 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, double *c = chain->colors[i]; glPushMatrix(); glMultMatrixd(m.data()); - int csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; + PolySet::csgmode_e csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; if (highlight) { - chain->polysets[i]->render_surface(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20), m); + csgmode = PolySet::csgmode_e(csgmode + 20); + setColor(COLORMODE_HIGHLIGHT); + chain->polysets[i]->render_surface(csgmode, m); if (showedges) { - glDisable(GL_LIGHTING); - chain->polysets[i]->render_edges(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20)); - glEnable(GL_LIGHTING); + setColor(COLORMODE_HIGHLIGHT_EDGES); + chain->polysets[i]->render_edges(csgmode); } } else if (background) { - chain->polysets[i]->render_surface(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10), m); + csgmode = PolySet::csgmode_e(csgmode + 10); + setColor(COLORMODE_BACKGROUND); + chain->polysets[i]->render_surface(csgmode, m); if (showedges) { - glDisable(GL_LIGHTING); - chain->polysets[i]->render_edges(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10)); - glEnable(GL_LIGHTING); + setColor(COLORMODE_BACKGROUND_EDGES); + chain->polysets[i]->render_edges(csgmode); } } else if (fberror) { - if (highlight) { - chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode + 20), m); - } else if (background) { - chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode + 10), m); - } else { - chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); - } - } else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0) { - glColor4dv(c); - chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); + if (highlight) csgmode = PolySet::csgmode_e(csgmode + 20); + else if (background) csgmode = PolySet::csgmode_e(csgmode + 10); + else csgmode = PolySet::csgmode_e(csgmode); + chain->polysets[i]->render_surface(csgmode, m); + } else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0 || c[3] >= 0) { + setColor(c); + chain->polysets[i]->render_surface(csgmode, m); if (showedges) { - glDisable(GL_LIGHTING); glColor4d((c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0); - chain->polysets[i]->render_edges(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode)); - glEnable(GL_LIGHTING); + chain->polysets[i]->render_edges(csgmode); } } else if (chain->types[i] == CSGTerm::TYPE_DIFFERENCE) { - chain->polysets[i]->render_surface(PolySet::COLORMODE_CUTOUT, PolySet::csgmode_e(csgmode), m); + setColor(COLORMODE_CUTOUT); + chain->polysets[i]->render_surface(csgmode, m); if (showedges) { - glDisable(GL_LIGHTING); - chain->polysets[i]->render_edges(PolySet::COLORMODE_CUTOUT, PolySet::csgmode_e(csgmode)); - glEnable(GL_LIGHTING); + setColor(COLORMODE_CUTOUT_EDGES); + chain->polysets[i]->render_edges(csgmode); } } else { - chain->polysets[i]->render_surface(PolySet::COLORMODE_MATERIAL, PolySet::csgmode_e(csgmode), m); + setColor(COLORMODE_MATERIAL); + chain->polysets[i]->render_surface(csgmode, m); if (showedges) { - glDisable(GL_LIGHTING); - chain->polysets[i]->render_edges(PolySet::COLORMODE_MATERIAL, PolySet::csgmode_e(csgmode)); - glEnable(GL_LIGHTING); + setColor(COLORMODE_MATERIAL_EDGES); + chain->polysets[i]->render_edges(csgmode); } } glPopMatrix(); diff --git a/src/glview.cc b/src/glview.cc index d0b3517a53..6836e3bc72 100644 --- a/src/glview.cc +++ b/src/glview.cc @@ -27,6 +27,7 @@ #include "GLView.h" #include "Preferences.h" #include "renderer.h" +#include "rendersettings.h" #include #include @@ -190,6 +191,28 @@ void GLView::initializeGL() } } if (opencsg_support && this->has_shaders) { + /* + Uniforms: + 1 color1 - face color + 2 color2 - edge color + 7 xscale + 8 yscale + + Attributes: + 3 trig + 4 pos_b + 5 pos_c + 6 mask + + Other: + 9 width + 10 height + + Outputs: + tp + tr + shading + */ const char *vs_source = "uniform float xscale, yscale;\n" "attribute vec3 pos_b, pos_c;\n" @@ -215,6 +238,11 @@ void GLView::initializeGL() " shading = abs(dot(normal, lightDir));\n" "}\n"; + /* + Inputs: + tp && tr - if any components of tp < tr, use color2 (edge color) + shading - multiplied by color1. color2 is is without lighting + */ const char *fs_source = "uniform vec4 color1, color2;\n" "varying vec3 tp, tr, tmp;\n" @@ -351,7 +379,7 @@ void GLView::paintGL() glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - const QColor &bgcol = Preferences::inst()->color(Preferences::BACKGROUND_COLOR); + const QColor &bgcol = RenderSettings::inst()->color(RenderSettings::BACKGROUND_COLOR); glClearColor(bgcol.redF(), bgcol.greenF(), bgcol.blueF(), 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); @@ -369,7 +397,7 @@ void GLView::paintGL() if (showcrosshairs) { glLineWidth(3); - const QColor &col = Preferences::inst()->color(Preferences::CROSSHAIR_COLOR); + const QColor &col = RenderSettings::inst()->color(RenderSettings::CROSSHAIR_COLOR); glColor3f(col.redF(), col.greenF(), col.blueF()); glBegin(GL_LINES); for (double xf = -1; xf <= +1; xf += 2) @@ -470,7 +498,7 @@ void GLView::paintGL() // FIXME: This was an attempt to keep contrast with background, but is suboptimal // (e.g. nearly invisible against a gray background). int r,g,b; - bgcol.getRgb(&r, &g, &b); +// bgcol.getRgb(&r, &g, &b); glColor3d((255.0-r)/255.0, (255.0-g)/255.0, (255.0-b)/255.0); glBegin(GL_LINES); // X Label diff --git a/src/polyset.cc b/src/polyset.cc index 742e425aab..481cbeca19 100644 --- a/src/polyset.cc +++ b/src/polyset.cc @@ -108,52 +108,9 @@ static void gl_draw_triangle(GLint *shaderinfo, const Vector3d &p0, const Vector } } -void PolySet::render_surface(colormode_e colormode, csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo) const +void PolySet::render_surface(csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo) const { bool mirrored = m.matrix().determinant() < 0; - - if (colormode == COLORMODE_MATERIAL) { -// FIXME: Reenable/rewrite - don't be dependant on GUI -// const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_FRONT_COLOR); - const QColor &col = QColor(0xf9, 0xd7, 0x2c); - glColor3f(col.redF(), col.greenF(), col.blueF()); -#ifdef ENABLE_OPENCSG - if (shaderinfo) { - glUniform4f(shaderinfo[1], col.redF(), col.greenF(), col.blueF(), 1.0f); - glUniform4f(shaderinfo[2], 255 / 255.0f, 236 / 255.0f, 94 / 255.0f, 1.0f); - } -#endif /* ENABLE_OPENCSG */ - } - if (colormode == COLORMODE_CUTOUT) { -// FIXME: Reenable/rewrite - don't be dependant on GUI -// const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_BACK_COLOR); - const QColor &col = QColor(0x9d, 0xcb, 0x51); - glColor3f(col.redF(), col.greenF(), col.blueF()); -#ifdef ENABLE_OPENCSG - if (shaderinfo) { - glUniform4f(shaderinfo[1], 157 / 255.0f, 203 / 255.0f, 81 / 255.0f, 1.0f); - glUniform4f(shaderinfo[2], 171 / 255.0f, 216 / 255.0f, 86 / 255.0f, 1.0f); - } -#endif /* ENABLE_OPENCSG */ - } - if (colormode == COLORMODE_HIGHLIGHT) { - glColor4ub(255, 157, 81, 128); -#ifdef ENABLE_OPENCSG - if (shaderinfo) { - glUniform4f(shaderinfo[1], 255 / 255.0f, 157 / 255.0f, 81 / 255.0f, 0.5f); - glUniform4f(shaderinfo[2], 255 / 255.0f, 171 / 255.0f, 86 / 255.0f, 0.5f); - } -#endif /* ENABLE_OPENCSG */ - } - if (colormode == COLORMODE_BACKGROUND) { - glColor4ub(180, 180, 180, 128); -#ifdef ENABLE_OPENCSG - if (shaderinfo) { - glUniform4f(shaderinfo[1], 180 / 255.0f, 180 / 255.0f, 180 / 255.0f, 0.5f); - glUniform4f(shaderinfo[2], 150 / 255.0f, 150 / 255.0f, 150 / 255.0f, 0.5f); - } -#endif /* ENABLE_OPENCSG */ - } #ifdef ENABLE_OPENCSG if (shaderinfo) { glUniform1f(shaderinfo[7], shaderinfo[9]); @@ -248,16 +205,9 @@ void PolySet::render_surface(colormode_e colormode, csgmode_e csgmode, const Tra } } -void PolySet::render_edges(colormode_e colormode, csgmode_e csgmode) const +void PolySet::render_edges(csgmode_e csgmode) const { - if (colormode == COLORMODE_MATERIAL) - glColor3ub(255, 236, 94); - if (colormode == COLORMODE_CUTOUT) - glColor3ub(171, 216, 86); - if (colormode == COLORMODE_HIGHLIGHT) - glColor4ub(255, 171, 86, 128); - if (colormode == COLORMODE_BACKGROUND) - glColor4ub(150, 150, 150, 128); + glDisable(GL_LIGHTING); if (this->is2d) { double zbase = csgmode; for (double z = -zbase/2; z < zbase; z += zbase) @@ -293,6 +243,7 @@ void PolySet::render_edges(colormode_e colormode, csgmode_e csgmode) const glEnd(); } } + glEnable(GL_LIGHTING); } BoundingBox PolySet::getBoundingBox() const diff --git a/src/polyset.h b/src/polyset.h index 57f5057213..56986215ce 100644 --- a/src/polyset.h +++ b/src/polyset.h @@ -27,14 +27,6 @@ class PolySet BoundingBox getBoundingBox() const; - enum colormode_e { - COLORMODE_NONE, - COLORMODE_MATERIAL, - COLORMODE_CUTOUT, - COLORMODE_HIGHLIGHT, - COLORMODE_BACKGROUND - }; - enum csgmode_e { CSGMODE_NONE, CSGMODE_NORMAL = 1, @@ -45,8 +37,8 @@ class PolySet CSGMODE_HIGHLIGHT_DIFFERENCE = 22 }; - void render_surface(colormode_e colormode, csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo = NULL) const; - void render_edges(colormode_e colormode, csgmode_e csgmode) const; + void render_surface(csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo = NULL) const; + void render_edges(csgmode_e csgmode) const; }; #endif diff --git a/src/renderer.cc b/src/renderer.cc new file mode 100644 index 0000000000..b791673f62 --- /dev/null +++ b/src/renderer.cc @@ -0,0 +1,66 @@ +#include "renderer.h" +#include "rendersettings.h" +#include + +void Renderer::setColor(const double color[4], GLint *shaderinfo) const +{ + QColor col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_FRONT_COLOR); + double c[4] = {color[0], color[1], color[2], color[3]}; + if (c[0] < 0) c[0] = col.redF(); + if (c[1] < 0) c[1] = col.greenF(); + if (c[2] < 0) c[2] = col.blueF(); + if (c[3] < 0) c[3] = col.alphaF(); + glColor4dv(c); + if (shaderinfo) { + glUniform4f(shaderinfo[1], c[0], c[1], c[2], c[3]); + glUniform4f(shaderinfo[2], (c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0); + } +} + +void Renderer::setColor(ColorMode colormode, GLint *shaderinfo) const +{ + QColor col; + switch (colormode) { + case COLORMODE_NONE: + return; + break; + case COLORMODE_MATERIAL: + col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_FRONT_COLOR); + break; + case COLORMODE_CUTOUT: + col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_BACK_COLOR); + break; + case COLORMODE_HIGHLIGHT: + col.setRgb(255, 157, 81, 128); + break; + case COLORMODE_BACKGROUND: + col.setRgb(180, 180, 180, 128); + break; + case COLORMODE_MATERIAL_EDGES: + col.setRgb(255, 236, 94); + break; + case COLORMODE_CUTOUT_EDGES: + col.setRgb(171, 216, 86); + break; + case COLORMODE_HIGHLIGHT_EDGES: + col.setRgb(255, 171, 86, 128); + break; + case COLORMODE_BACKGROUND_EDGES: + col.setRgb(150, 150, 150, 128); + break; + default: + break; + } + float rgba[4]; + rgba[0] = col.redF(); + rgba[1] = col.greenF(); + rgba[2] = col.blueF(); + rgba[3] = col.alphaF(); + glColor4fv(rgba); +#ifdef ENABLE_OPENCSG + if (shaderinfo) { + glUniform4f(shaderinfo[1], col.redF(), col.greenF(), col.blueF(), 1.0f); + glUniform4f(shaderinfo[2], (col.redF()+1)/2, (col.greenF()+1)/2, (col.blueF()+1)/2, 1.0f); + } +#endif +} diff --git a/src/renderer.h b/src/renderer.h index 3c25e98799..e978080d1f 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -1,11 +1,28 @@ #ifndef RENDERER_H_ #define RENDERER_H_ +#include "system-gl.h" + class Renderer { public: virtual ~Renderer() {} virtual void draw(bool showfaces, bool showedges) const = 0; + + enum ColorMode { + COLORMODE_NONE, + COLORMODE_MATERIAL, + COLORMODE_CUTOUT, + COLORMODE_HIGHLIGHT, + COLORMODE_BACKGROUND, + COLORMODE_MATERIAL_EDGES, + COLORMODE_CUTOUT_EDGES, + COLORMODE_HIGHLIGHT_EDGES, + COLORMODE_BACKGROUND_EDGES + }; + + virtual void setColor(const double color[4], GLint *shaderinfo = NULL) const; + virtual void setColor(ColorMode colormode, GLint *shaderinfo = NULL) const; }; #endif // RENDERER_H diff --git a/src/rendersettings.cc b/src/rendersettings.cc new file mode 100644 index 0000000000..ee57c34f01 --- /dev/null +++ b/src/rendersettings.cc @@ -0,0 +1,35 @@ +#include "rendersettings.h" + +RenderSettings *RenderSettings::inst(bool erase) +{ + static RenderSettings *instance = new RenderSettings; + if (erase) { + delete instance; + instance = NULL; + } + return instance; +} + +RenderSettings::RenderSettings() +{ + this->colors[BACKGROUND_COLOR] = QColor(0xff, 0xff, 0xe5); + this->colors[OPENCSG_FACE_FRONT_COLOR] = QColor(0xf9, 0xd7, 0x2c); + this->colors[OPENCSG_FACE_BACK_COLOR] = QColor(0x9d, 0xcb, 0x51); + this->colors[CGAL_FACE_FRONT_COLOR] = QColor(0xf9, 0xd7, 0x2c); + this->colors[CGAL_FACE_BACK_COLOR] = QColor(0x9d, 0xcb, 0x51); + this->colors[CGAL_FACE_2D_COLOR] = QColor(0x00, 0xbf, 0x99); + this->colors[CGAL_EDGE_FRONT_COLOR] = QColor(0xff, 0x00, 0x00); + this->colors[CGAL_EDGE_BACK_COLOR] = QColor(0xff, 0x00, 0x00); + this->colors[CGAL_EDGE_2D_COLOR] = QColor(0xff, 0x00, 0x00); + this->colors[CROSSHAIR_COLOR] = QColor(0x80, 0x00, 0x00); +} + +QColor RenderSettings::color(RenderColor idx) const +{ + return this->colors[idx]; +} + +void RenderSettings::setColors(const QMap &colors) +{ + this->colors = colors; +} diff --git a/src/rendersettings.h b/src/rendersettings.h new file mode 100644 index 0000000000..32e56f1848 --- /dev/null +++ b/src/rendersettings.h @@ -0,0 +1,35 @@ +#ifndef RENDERSETTINGS_H_ +#define RENDERSETTINGS_H_ + +#include +#include + +class RenderSettings +{ +public: + static RenderSettings *inst(bool erase = false); + + enum RenderColor { + BACKGROUND_COLOR, + OPENCSG_FACE_FRONT_COLOR, + OPENCSG_FACE_BACK_COLOR, + CGAL_FACE_FRONT_COLOR, + CGAL_FACE_2D_COLOR, + CGAL_FACE_BACK_COLOR, + CGAL_EDGE_FRONT_COLOR, + CGAL_EDGE_BACK_COLOR, + CGAL_EDGE_2D_COLOR, + CROSSHAIR_COLOR + }; + + void setColors(const QMap &colors); + QColor color(RenderColor idx) const; + +private: + RenderSettings(); + ~RenderSettings() {} + + QMap colors; +}; + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 585c3b1cbc..61e0ef0ceb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -351,7 +351,7 @@ target_link_libraries(cgaltest tests-cgal ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRA # # cgalpngtest # -add_executable(cgalpngtest cgalpngtest.cc bboxhelp.cc ../src/CGALRenderer.cc) +add_executable(cgalpngtest cgalpngtest.cc bboxhelp.cc ../src/CGALRenderer.cc ../src/renderer.cc ../src/rendersettings.cc) set_target_properties(cgalpngtest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}") target_link_libraries(cgalpngtest tests-offscreen tests-cgal ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES} ${QT_LIBRARIES} ${GLEW_LIBRARY} ${COCOA_LIBRARY} ${OPENGL_LIBRARY} ${Boost_LIBRARIES}) @@ -359,7 +359,7 @@ target_link_libraries(cgalpngtest tests-offscreen tests-cgal ${CGAL_LIBRARY} ${C # opencsgtest # -add_executable(opencsgtest opencsgtest.cc csgtestcore.cc ../src/OpenCSGRenderer.cc ../src/ThrownTogetherRenderer.cc) +add_executable(opencsgtest opencsgtest.cc csgtestcore.cc ../src/OpenCSGRenderer.cc ../src/ThrownTogetherRenderer.cc ../src/renderer.cc ../src/rendersettings.cc) set_target_properties(opencsgtest PROPERTIES COMPILE_FLAGS "-DENABLE_OPENCSG -DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}") target_link_libraries(opencsgtest tests-offscreen tests-cgal ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES} ${QT_LIBRARIES} ${OPENCSG_LIBRARY} ${GLEW_LIBRARY} ${COCOA_LIBRARY} ${OPENGL_LIBRARY} ${Boost_LIBRARIES}) @@ -367,7 +367,7 @@ target_link_libraries(opencsgtest tests-offscreen tests-cgal ${CGAL_LIBRARY} ${C # throwntogethertest # -add_executable(throwntogethertest throwntogethertest.cc csgtestcore.cc ../src/OpenCSGRenderer.cc ../src/ThrownTogetherRenderer.cc) +add_executable(throwntogethertest throwntogethertest.cc csgtestcore.cc ../src/OpenCSGRenderer.cc ../src/ThrownTogetherRenderer.cc ../src/renderer.cc ../src/rendersettings.cc) set_target_properties(throwntogethertest PROPERTIES COMPILE_FLAGS "-DENABLE_OPENCSG -DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}") target_link_libraries(throwntogethertest tests-offscreen tests-cgal ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES} ${QT_LIBRARIES} ${OPENCSG_LIBRARY} ${GLEW_LIBRARY} ${COCOA_LIBRARY} ${OPENGL_LIBRARY} ${Boost_LIBRARIES}) diff --git a/tests/regression/opencsgtest/color-tests-expected.png b/tests/regression/opencsgtest/color-tests-expected.png index b2ef8dd47062d506f317e4157daf9370911ba83b..82ba36a8e908f0d1bae0be8a2096347a0282eeaa 100644 GIT binary patch literal 10950 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 literal 11055 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 literal 9139 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