From 7b9d730a23fda251c0bb1e6499dd75ad922d76d1 Mon Sep 17 00:00:00 2001 From: EvilSpirit Date: Tue, 28 Jun 2016 14:48:06 +0600 Subject: [PATCH] Hide OpenGL implementation details. Abstract the exact details of the OpenGL renderer in the render.h header; this allows us to use GL-specific types in the renderer class and functions without including OpenGL (and Windows, where applicable) headers in every source file. --- src/CMakeLists.txt | 8 ++-- src/confscreen.cpp | 16 ++++---- src/draw.cpp | 41 ++++++++++----------- src/export.cpp | 22 ++++------- src/graphicswin.cpp | 2 + src/platform/headless.cpp | 10 +++++ src/render/render.h | 77 ++++++++------------------------------- src/render/rendergl1.cpp | 77 +++++++++++++++++++++++++++++++++++++++ src/textwin.cpp | 24 +++++++----- src/ui.h | 6 ++- 10 files changed, 164 insertions(+), 119 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3ac6259..346d525 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -125,9 +125,11 @@ set(solvespace_cad_HEADERS set(solvespace_cad_SOURCES bsp.cpp clipboard.cpp + confscreen.cpp constraint.cpp constrainteq.cpp describescreen.cpp + draw.cpp drawconstraint.cpp drawentity.cpp entity.cpp @@ -150,6 +152,7 @@ set(solvespace_cad_SOURCES style.cpp system.cpp textscreens.cpp + textwin.cpp toolbar.cpp ttf.cpp undoredo.cpp @@ -167,11 +170,8 @@ set(solvespace_cad_SOURCES srf/triangulate.cpp) set(solvespace_cad_gl_SOURCES - confscreen.cpp - draw.cpp export.cpp - solvespace.cpp - textwin.cpp) + solvespace.cpp) add_library(solvespace_cad STATIC ${util_SOURCES} diff --git a/src/confscreen.cpp b/src/confscreen.cpp index 0856856..e70e921 100644 --- a/src/confscreen.cpp +++ b/src/confscreen.cpp @@ -302,14 +302,14 @@ void TextWindow::ShowConfiguration() { Printf(false, "%Ba %d %Fl%Ll%f[change]%E", SS.autosaveInterval, &ScreenChangeAutosaveInterval); -#if !defined(HEADLESS) - const char *gl_vendor, *gl_renderer, *gl_version; - OpenGl1Renderer::GetIdent(&gl_vendor, &gl_renderer, &gl_version); - Printf(false, ""); - Printf(false, " %Ftgl vendor %E%s", gl_vendor); - Printf(false, " %Ft renderer %E%s", gl_renderer); - Printf(false, " %Ft version %E%s", gl_version); -#endif + if(canvas) { + const char *gl_vendor, *gl_renderer, *gl_version; + canvas->GetIdent(&gl_vendor, &gl_renderer, &gl_version); + Printf(false, ""); + Printf(false, " %Ftgl vendor %E%s", gl_vendor); + Printf(false, " %Ft renderer %E%s", gl_renderer); + Printf(false, " %Ft version %E%s", gl_version); + } } bool TextWindow::EditControlDoneForConfiguration(const char *s) { diff --git a/src/draw.cpp b/src/draw.cpp index 539e4a9..dc41f31 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -727,7 +727,8 @@ void GraphicsWindow::Draw(Canvas *canvas) { } void GraphicsWindow::Paint() { -#if !defined(HEADLESS) + if (!canvas) return; + havePainted = true; auto renderStartTime = std::chrono::high_resolution_clock::now(); @@ -737,11 +738,8 @@ void GraphicsWindow::Paint() { width = w; height = h; - Camera camera = GetCamera(); - - OpenGl1Renderer canvas = {}; - canvas.camera = camera; - canvas.lighting = GetLighting(); + Camera camera = GetCamera(); + Lighting lighting = GetLighting(); if(!SS.ActiveGroupsOkay()) { // Draw a different background whenever we're having solve problems. @@ -749,20 +747,22 @@ void GraphicsWindow::Paint() { bgColor = RgbaColor::FromFloat(0.4f*bgColor.redF(), 0.4f*bgColor.greenF(), 0.4f*bgColor.blueF()); - canvas.lighting.backgroundColor = bgColor; + lighting.backgroundColor = bgColor; // And show the text window, which has info to debug it ForceTextWindowShown(); } - canvas.BeginFrame(); - canvas.UpdateProjection(); - Draw(&canvas); + canvas->BeginFrame(); + canvas->SetCamera(camera); + canvas->SetLighting(lighting); + Draw(canvas.get()); + canvas->EndFrame(); - canvas.camera.LoadIdentity(); - canvas.UpdateProjection(); + camera.LoadIdentity(); + canvas->SetCamera(camera); UiCanvas uiCanvas = {}; - uiCanvas.canvas = &canvas; + uiCanvas.canvas = canvas; // If a marquee selection is in progress, then draw the selection // rectangle, as an outline and a transparent fill. @@ -776,16 +776,16 @@ void GraphicsWindow::Paint() { // And finally the toolbar. if(SS.showToolbar) { - canvas.camera.offset = {}; - canvas.camera.offset.x = -(double)canvas.camera.width / 2.0; - canvas.camera.offset.y = -(double)canvas.camera.height / 2.0; - canvas.UpdateProjection(); + camera.offset = {}; + camera.offset.x = -(double)camera.width / 2.0; + camera.offset.y = -(double)camera.height / 2.0; + canvas->SetCamera(camera); ToolbarDraw(&uiCanvas); } // If we display UI elements, also display an fps counter. if(SS.showToolbar) { - canvas.EndFrame(); + canvas->EndFrame(); auto renderEndTime = std::chrono::high_resolution_clock::now(); std::chrono::duration renderTime = renderEndTime - renderStartTime; @@ -803,7 +803,6 @@ void GraphicsWindow::Paint() { 5, 5, renderTimeColor); } - canvas.EndFrame(); - canvas.Clear(); -#endif + canvas->EndFrame(); + canvas->Clear(); } diff --git a/src/export.cpp b/src/export.cpp index c7306d0..d060d15 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -1119,35 +1119,29 @@ void SolveSpaceUI::ExportMeshAsThreeJsTo(FILE *f, const std::string &filename, //----------------------------------------------------------------------------- void SolveSpaceUI::ExportAsPngTo(const std::string &filename) { #if !defined(HEADLESS) - // Somewhat hacky way to invoke glReadPixels without dragging in all OpenGL headers. - OpenGl1Renderer canvas = {}; - canvas.camera = SS.GW.GetCamera(); - std::shared_ptr screenshot; - // No guarantee that the back buffer contains anything valid right now, // so repaint the scene. And hide the toolbar too. bool prevShowToolbar = SS.showToolbar; SS.showToolbar = false; + + // Somewhat hacky way to invoke glReadPixels without dragging in all OpenGL headers. + std::shared_ptr canvas = CreateRenderer(); + canvas->SetCamera(SS.GW.GetCamera()); + std::shared_ptr screenshot; #if !defined(WIN32) GlOffscreen offscreen; offscreen.Render((int)SS.GW.width, (int)SS.GW.height, [&] { SS.GW.Paint(); - screenshot = canvas.ReadFrame(); + screenshot = canvas->ReadFrame(); }); #else SS.GW.Paint(); - screenshot = canvas.ReadFrame(); + screenshot = canvas->ReadFrame(); #endif SS.showToolbar = prevShowToolbar; -#if defined(WIN32) || defined(HAVE_GTK) - bool flip = true; -#else - bool flip = false; -#endif - FILE *f = ssfopen(filename, "wb"); - if(!f || !screenshot->WritePng(f, flip)) { + if(!f || !screenshot->WritePng(f, /*flip=*/true)) { Error("Couldn't write to '%s'", filename.c_str()); } if(f) fclose(f); diff --git a/src/graphicswin.cpp b/src/graphicswin.cpp index 4878e60..81f502a 100644 --- a/src/graphicswin.cpp +++ b/src/graphicswin.cpp @@ -205,6 +205,8 @@ std::string SolveSpace::MakeAcceleratorLabel(int accel) { } void GraphicsWindow::Init() { + canvas = CreateRenderer(); + scale = 5; offset = Vector::From(0, 0, 0); projRight = Vector::From(1, 0, 0); diff --git a/src/platform/headless.cpp b/src/platform/headless.cpp index 8b949d6..7eefa5d 100644 --- a/src/platform/headless.cpp +++ b/src/platform/headless.cpp @@ -89,6 +89,16 @@ void SetAutosaveTimerFor(int minutes) { void ScheduleLater() { } +//----------------------------------------------------------------------------- +// Rendering +//----------------------------------------------------------------------------- + +const bool FLIP_FRAMEBUFFER = false; + +std::shared_ptr CreateRenderer() { + return NULL; +} + //----------------------------------------------------------------------------- // Graphics window //----------------------------------------------------------------------------- diff --git a/src/render/render.h b/src/render/render.h index 60e1719..d8a4af9 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -164,13 +164,24 @@ public: virtual void InvalidatePixmap(std::shared_ptr pm) = 0; }; +// An interface for view-dependent visualization +class ViewportCanvas : public Canvas { +public: + virtual void SetCamera(const Camera &camera, bool filp = FLIP_FRAMEBUFFER) = 0; + virtual void SetLighting(const Lighting &lighting) = 0; + + virtual void BeginFrame() = 0; + virtual void EndFrame() = 0; + virtual std::shared_ptr ReadFrame() = 0; + + virtual void GetIdent(const char **vendor, const char **renderer, const char **version) = 0; +}; + // A wrapper around Canvas that simplifies drawing UI in screen coordinates. class UiCanvas { public: - Canvas *canvas; - bool flip; - - UiCanvas() : canvas(), flip() {}; + std::shared_ptr canvas; + bool flip; void DrawLine(int x1, int y1, int x2, int y2, RgbaColor color, int width = 1); void DrawRect(int l, int r, int t, int b, RgbaColor fillColor, RgbaColor outlineColor); @@ -329,62 +340,6 @@ public: void Clear(); }; -// A canvas that uses the core OpenGL profile, for desktop systems. -class OpenGl1Renderer : public Canvas { -public: - Camera camera; - Lighting lighting; - // Cached OpenGL state. - struct { - bool drawing; - unsigned mode; // GLenum, but we don't include GL.h globally - hStroke hcs; - Stroke *stroke; - hFill hcf; - Fill *fill; - std::weak_ptr texture; - } current; - - OpenGl1Renderer() : camera(), lighting(), current() {} - - const Camera &GetCamera() const override { return camera; } - - void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override; - void DrawEdges(const SEdgeList &el, hStroke hcs) override; - bool DrawBeziers(const SBezierList &bl, hStroke hcs) override { return false; } - void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) override; - void DrawVectorText(const std::string &text, double height, - const Vector &o, const Vector &u, const Vector &v, - hStroke hcs) override; - - void DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d, - hFill hcf) override; - void DrawPoint(const Vector &o, hStroke hcs) override; - void DrawPolygon(const SPolygon &p, hFill hcf) override; - void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack, hStroke hcsTriangles) override; - void DrawFaces(const SMesh &m, const std::vector &faces, hFill hcf) override; - void DrawPixmap(std::shared_ptr pm, - const Vector &o, const Vector &u, const Vector &v, - const Point2d &ta, const Point2d &tb, hFill hcf) override; - void InvalidatePixmap(std::shared_ptr pm) override; - - void SelectPrimitive(unsigned mode); - void UnSelectPrimitive(); - Stroke *SelectStroke(hStroke hcs); - Fill *SelectFill(hFill hcf); - void SelectTexture(std::shared_ptr pm); - void DoFatLineEndcap(const Vector &p, const Vector &u, const Vector &v); - void DoFatLine(const Vector &a, const Vector &b, double width); - void DoLine(const Vector &a, const Vector &b, hStroke hcs); - void DoPoint(Vector p, double radius); - void DoStippledLine(const Vector &a, const Vector &b, hStroke hcs, double phase = 0.0); - - void UpdateProjection(bool flip = FLIP_FRAMEBUFFER); - void BeginFrame(); - void EndFrame(); - std::shared_ptr ReadFrame(); - - static void GetIdent(const char **vendor, const char **renderer, const char **version); -}; +std::shared_ptr CreateRenderer(); #endif diff --git a/src/render/rendergl1.cpp b/src/render/rendergl1.cpp index f341d19..a84cb9d 100644 --- a/src/render/rendergl1.cpp +++ b/src/render/rendergl1.cpp @@ -163,6 +163,70 @@ static void ssglFillPattern(Canvas::FillPattern pattern) { } } +//----------------------------------------------------------------------------- +// OpenGL 1 / compatibility profile based renderer +//----------------------------------------------------------------------------- + +class OpenGl1Renderer : public ViewportCanvas { +public: + Camera camera; + Lighting lighting; + // Cached OpenGL state. + struct { + bool drawing; + GLenum mode; + hStroke hcs; + Stroke *stroke; + hFill hcf; + Fill *fill; + std::weak_ptr texture; + } current; + + OpenGl1Renderer() : camera(), lighting(), current() {} + + const Camera &GetCamera() const override { return camera; } + + void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override; + void DrawEdges(const SEdgeList &el, hStroke hcs) override; + bool DrawBeziers(const SBezierList &bl, hStroke hcs) override { return false; } + void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) override; + void DrawVectorText(const std::string &text, double height, + const Vector &o, const Vector &u, const Vector &v, + hStroke hcs) override; + + void DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d, + hFill hcf) override; + void DrawPoint(const Vector &o, hStroke hcs) override; + void DrawPolygon(const SPolygon &p, hFill hcf) override; + void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack, hStroke hcsTriangles) override; + void DrawFaces(const SMesh &m, const std::vector &faces, hFill hcf) override; + void DrawPixmap(std::shared_ptr pm, + const Vector &o, const Vector &u, const Vector &v, + const Point2d &ta, const Point2d &tb, hFill hcf) override; + void InvalidatePixmap(std::shared_ptr pm) override; + + void SelectPrimitive(unsigned mode); + void UnSelectPrimitive(); + Stroke *SelectStroke(hStroke hcs); + Fill *SelectFill(hFill hcf); + void SelectTexture(std::shared_ptr pm); + void DoFatLineEndcap(const Vector &p, const Vector &u, const Vector &v); + void DoFatLine(const Vector &a, const Vector &b, double width); + void DoLine(const Vector &a, const Vector &b, hStroke hcs); + void DoPoint(Vector p, double radius); + void DoStippledLine(const Vector &a, const Vector &b, hStroke hcs, double phase = 0.0); + + void UpdateProjection(bool flip = FLIP_FRAMEBUFFER); + void SetCamera(const Camera &camera, bool filp = FLIP_FRAMEBUFFER) override; + void SetLighting(const Lighting &lighting) override; + + void BeginFrame() override; + void EndFrame() override; + std::shared_ptr ReadFrame() override; + + void GetIdent(const char **vendor, const char **renderer, const char **version) override; +}; + //----------------------------------------------------------------------------- // A simple OpenGL state tracker to group consecutive draw calls. //----------------------------------------------------------------------------- @@ -766,4 +830,17 @@ void OpenGl1Renderer::GetIdent(const char **vendor, const char **renderer, const *version = (const char *)glGetString(GL_VERSION); } +void OpenGl1Renderer::SetCamera(const Camera &c, bool flip) { + camera = c; + UpdateProjection(flip); +} + +void OpenGl1Renderer::SetLighting(const Lighting &l) { + lighting = l; +} + +std::shared_ptr CreateRenderer() { + return std::shared_ptr(new OpenGl1Renderer()); +} + } diff --git a/src/textwin.cpp b/src/textwin.cpp index 1e67a15..a86a617 100644 --- a/src/textwin.cpp +++ b/src/textwin.cpp @@ -223,17 +223,25 @@ void TextWindow::MakeColorTable(const Color *in, float *out) { } void TextWindow::Init() { + canvas = CreateRenderer(); + ClearSuper(); } void TextWindow::ClearSuper() { HideEditControl(); + // Ugly hack, but not so ugly as the next line + std::shared_ptr oldCanvas = canvas; + // Cannot use *this = {} here because TextWindow instances // are 2.4MB long; this causes stack overflows in prologue // when built with MSVC, even with optimizations. memset(this, 0, sizeof(*this)); + // Return old canvas + canvas = oldCanvas; + MakeColorTable(fgColors, fgColorTable); MakeColorTable(bgColors, bgColorTable); @@ -843,7 +851,8 @@ bool TextWindow::DrawOrHitTestColorPicker(UiCanvas *uiCanvas, DrawOrHitHow how, } void TextWindow::Paint() { -#if !defined(HEADLESS) + if (!canvas) return; + int width, height; GetTextWindowSize(&width, &height); @@ -854,13 +863,11 @@ void TextWindow::Paint() { camera.offset.x = -(double)camera.width / 2.0; camera.offset.y = -(double)camera.height / 2.0; - OpenGl1Renderer canvas = {}; - canvas.camera = camera; - canvas.BeginFrame(); - canvas.UpdateProjection(); + canvas->BeginFrame(); + canvas->SetCamera(camera); UiCanvas uiCanvas = {}; - uiCanvas.canvas = &canvas; + uiCanvas.canvas = canvas; uiCanvas.flip = true; halfRows = camera.height / (LINE_HEIGHT/2); @@ -983,9 +990,8 @@ void TextWindow::Paint() { // And we may show a color picker for certain editable fields DrawOrHitTestColorPicker(&uiCanvas, PAINT, false, 0, 0); - canvas.EndFrame(); - canvas.Clear(); -#endif + canvas->EndFrame(); + canvas->Clear(); } void TextWindow::MouseEvent(bool leftClick, bool leftDown, double x, double y) { diff --git a/src/ui.h b/src/ui.h index 1aace05..08bc178 100644 --- a/src/ui.h +++ b/src/ui.h @@ -194,11 +194,11 @@ public: } meta[MAX_ROWS][MAX_COLS]; int hoveredRow, hoveredCol; - int top[MAX_ROWS]; // in half-line units, or -1 for unused int rows; - // The row of icons at the top of the text window, to hide/show things + std::shared_ptr canvas; + void Draw(Canvas *canvas); // These are called by the platform-specific code. @@ -500,6 +500,8 @@ public: void PasteClipboard(Vector trans, double theta, double scale); static void MenuClipboard(Command id); + std::shared_ptr canvas; + // The width and height (in pixels) of the window. double width, height; // These parameters define the map from 2d screen coordinates to the