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.
This commit is contained in:
EvilSpirit 2016-06-28 14:48:06 +06:00 committed by whitequark
parent f5e955a015
commit 7b9d730a23
10 changed files with 164 additions and 119 deletions

View File

@ -125,9 +125,11 @@ set(solvespace_cad_HEADERS
set(solvespace_cad_SOURCES set(solvespace_cad_SOURCES
bsp.cpp bsp.cpp
clipboard.cpp clipboard.cpp
confscreen.cpp
constraint.cpp constraint.cpp
constrainteq.cpp constrainteq.cpp
describescreen.cpp describescreen.cpp
draw.cpp
drawconstraint.cpp drawconstraint.cpp
drawentity.cpp drawentity.cpp
entity.cpp entity.cpp
@ -150,6 +152,7 @@ set(solvespace_cad_SOURCES
style.cpp style.cpp
system.cpp system.cpp
textscreens.cpp textscreens.cpp
textwin.cpp
toolbar.cpp toolbar.cpp
ttf.cpp ttf.cpp
undoredo.cpp undoredo.cpp
@ -167,11 +170,8 @@ set(solvespace_cad_SOURCES
srf/triangulate.cpp) srf/triangulate.cpp)
set(solvespace_cad_gl_SOURCES set(solvespace_cad_gl_SOURCES
confscreen.cpp
draw.cpp
export.cpp export.cpp
solvespace.cpp solvespace.cpp)
textwin.cpp)
add_library(solvespace_cad STATIC add_library(solvespace_cad STATIC
${util_SOURCES} ${util_SOURCES}

View File

@ -302,14 +302,14 @@ void TextWindow::ShowConfiguration() {
Printf(false, "%Ba %d %Fl%Ll%f[change]%E", Printf(false, "%Ba %d %Fl%Ll%f[change]%E",
SS.autosaveInterval, &ScreenChangeAutosaveInterval); SS.autosaveInterval, &ScreenChangeAutosaveInterval);
#if !defined(HEADLESS) if(canvas) {
const char *gl_vendor, *gl_renderer, *gl_version; const char *gl_vendor, *gl_renderer, *gl_version;
OpenGl1Renderer::GetIdent(&gl_vendor, &gl_renderer, &gl_version); canvas->GetIdent(&gl_vendor, &gl_renderer, &gl_version);
Printf(false, ""); Printf(false, "");
Printf(false, " %Ftgl vendor %E%s", gl_vendor); Printf(false, " %Ftgl vendor %E%s", gl_vendor);
Printf(false, " %Ft renderer %E%s", gl_renderer); Printf(false, " %Ft renderer %E%s", gl_renderer);
Printf(false, " %Ft version %E%s", gl_version); Printf(false, " %Ft version %E%s", gl_version);
#endif }
} }
bool TextWindow::EditControlDoneForConfiguration(const char *s) { bool TextWindow::EditControlDoneForConfiguration(const char *s) {

View File

@ -727,7 +727,8 @@ void GraphicsWindow::Draw(Canvas *canvas) {
} }
void GraphicsWindow::Paint() { void GraphicsWindow::Paint() {
#if !defined(HEADLESS) if (!canvas) return;
havePainted = true; havePainted = true;
auto renderStartTime = std::chrono::high_resolution_clock::now(); auto renderStartTime = std::chrono::high_resolution_clock::now();
@ -737,11 +738,8 @@ void GraphicsWindow::Paint() {
width = w; width = w;
height = h; height = h;
Camera camera = GetCamera(); Camera camera = GetCamera();
Lighting lighting = GetLighting();
OpenGl1Renderer canvas = {};
canvas.camera = camera;
canvas.lighting = GetLighting();
if(!SS.ActiveGroupsOkay()) { if(!SS.ActiveGroupsOkay()) {
// Draw a different background whenever we're having solve problems. // Draw a different background whenever we're having solve problems.
@ -749,20 +747,22 @@ void GraphicsWindow::Paint() {
bgColor = RgbaColor::FromFloat(0.4f*bgColor.redF(), bgColor = RgbaColor::FromFloat(0.4f*bgColor.redF(),
0.4f*bgColor.greenF(), 0.4f*bgColor.greenF(),
0.4f*bgColor.blueF()); 0.4f*bgColor.blueF());
canvas.lighting.backgroundColor = bgColor; lighting.backgroundColor = bgColor;
// And show the text window, which has info to debug it // And show the text window, which has info to debug it
ForceTextWindowShown(); ForceTextWindowShown();
} }
canvas.BeginFrame();
canvas.UpdateProjection();
Draw(&canvas); canvas->BeginFrame();
canvas->SetCamera(camera);
canvas->SetLighting(lighting);
Draw(canvas.get());
canvas->EndFrame();
canvas.camera.LoadIdentity(); camera.LoadIdentity();
canvas.UpdateProjection(); canvas->SetCamera(camera);
UiCanvas uiCanvas = {}; UiCanvas uiCanvas = {};
uiCanvas.canvas = &canvas; uiCanvas.canvas = canvas;
// If a marquee selection is in progress, then draw the selection // If a marquee selection is in progress, then draw the selection
// rectangle, as an outline and a transparent fill. // rectangle, as an outline and a transparent fill.
@ -776,16 +776,16 @@ void GraphicsWindow::Paint() {
// And finally the toolbar. // And finally the toolbar.
if(SS.showToolbar) { if(SS.showToolbar) {
canvas.camera.offset = {}; camera.offset = {};
canvas.camera.offset.x = -(double)canvas.camera.width / 2.0; camera.offset.x = -(double)camera.width / 2.0;
canvas.camera.offset.y = -(double)canvas.camera.height / 2.0; camera.offset.y = -(double)camera.height / 2.0;
canvas.UpdateProjection(); canvas->SetCamera(camera);
ToolbarDraw(&uiCanvas); ToolbarDraw(&uiCanvas);
} }
// If we display UI elements, also display an fps counter. // If we display UI elements, also display an fps counter.
if(SS.showToolbar) { if(SS.showToolbar) {
canvas.EndFrame(); canvas->EndFrame();
auto renderEndTime = std::chrono::high_resolution_clock::now(); auto renderEndTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> renderTime = renderEndTime - renderStartTime; std::chrono::duration<double, std::milli> renderTime = renderEndTime - renderStartTime;
@ -803,7 +803,6 @@ void GraphicsWindow::Paint() {
5, 5, renderTimeColor); 5, 5, renderTimeColor);
} }
canvas.EndFrame(); canvas->EndFrame();
canvas.Clear(); canvas->Clear();
#endif
} }

View File

@ -1119,35 +1119,29 @@ void SolveSpaceUI::ExportMeshAsThreeJsTo(FILE *f, const std::string &filename,
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SolveSpaceUI::ExportAsPngTo(const std::string &filename) { void SolveSpaceUI::ExportAsPngTo(const std::string &filename) {
#if !defined(HEADLESS) #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<Pixmap> screenshot;
// No guarantee that the back buffer contains anything valid right now, // No guarantee that the back buffer contains anything valid right now,
// so repaint the scene. And hide the toolbar too. // so repaint the scene. And hide the toolbar too.
bool prevShowToolbar = SS.showToolbar; bool prevShowToolbar = SS.showToolbar;
SS.showToolbar = false; SS.showToolbar = false;
// Somewhat hacky way to invoke glReadPixels without dragging in all OpenGL headers.
std::shared_ptr<ViewportCanvas> canvas = CreateRenderer();
canvas->SetCamera(SS.GW.GetCamera());
std::shared_ptr<Pixmap> screenshot;
#if !defined(WIN32) #if !defined(WIN32)
GlOffscreen offscreen; GlOffscreen offscreen;
offscreen.Render((int)SS.GW.width, (int)SS.GW.height, [&] { offscreen.Render((int)SS.GW.width, (int)SS.GW.height, [&] {
SS.GW.Paint(); SS.GW.Paint();
screenshot = canvas.ReadFrame(); screenshot = canvas->ReadFrame();
}); });
#else #else
SS.GW.Paint(); SS.GW.Paint();
screenshot = canvas.ReadFrame(); screenshot = canvas->ReadFrame();
#endif #endif
SS.showToolbar = prevShowToolbar; SS.showToolbar = prevShowToolbar;
#if defined(WIN32) || defined(HAVE_GTK)
bool flip = true;
#else
bool flip = false;
#endif
FILE *f = ssfopen(filename, "wb"); 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()); Error("Couldn't write to '%s'", filename.c_str());
} }
if(f) fclose(f); if(f) fclose(f);

View File

@ -205,6 +205,8 @@ std::string SolveSpace::MakeAcceleratorLabel(int accel) {
} }
void GraphicsWindow::Init() { void GraphicsWindow::Init() {
canvas = CreateRenderer();
scale = 5; scale = 5;
offset = Vector::From(0, 0, 0); offset = Vector::From(0, 0, 0);
projRight = Vector::From(1, 0, 0); projRight = Vector::From(1, 0, 0);

View File

@ -89,6 +89,16 @@ void SetAutosaveTimerFor(int minutes) {
void ScheduleLater() { void ScheduleLater() {
} }
//-----------------------------------------------------------------------------
// Rendering
//-----------------------------------------------------------------------------
const bool FLIP_FRAMEBUFFER = false;
std::shared_ptr<ViewportCanvas> CreateRenderer() {
return NULL;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Graphics window // Graphics window
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -164,13 +164,24 @@ public:
virtual void InvalidatePixmap(std::shared_ptr<const Pixmap> pm) = 0; virtual void InvalidatePixmap(std::shared_ptr<const Pixmap> 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<Pixmap> 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. // A wrapper around Canvas that simplifies drawing UI in screen coordinates.
class UiCanvas { class UiCanvas {
public: public:
Canvas *canvas; std::shared_ptr<Canvas> canvas;
bool flip; bool flip;
UiCanvas() : canvas(), flip() {};
void DrawLine(int x1, int y1, int x2, int y2, RgbaColor color, int width = 1); 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); void DrawRect(int l, int r, int t, int b, RgbaColor fillColor, RgbaColor outlineColor);
@ -329,62 +340,6 @@ public:
void Clear(); void Clear();
}; };
// A canvas that uses the core OpenGL profile, for desktop systems. std::shared_ptr<ViewportCanvas> CreateRenderer();
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<const Pixmap> 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<uint32_t> &faces, hFill hcf) override;
void DrawPixmap(std::shared_ptr<const Pixmap> pm,
const Vector &o, const Vector &u, const Vector &v,
const Point2d &ta, const Point2d &tb, hFill hcf) override;
void InvalidatePixmap(std::shared_ptr<const Pixmap> pm) override;
void SelectPrimitive(unsigned mode);
void UnSelectPrimitive();
Stroke *SelectStroke(hStroke hcs);
Fill *SelectFill(hFill hcf);
void SelectTexture(std::shared_ptr<const Pixmap> 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<Pixmap> ReadFrame();
static void GetIdent(const char **vendor, const char **renderer, const char **version);
};
#endif #endif

View File

@ -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<const Pixmap> 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<uint32_t> &faces, hFill hcf) override;
void DrawPixmap(std::shared_ptr<const Pixmap> pm,
const Vector &o, const Vector &u, const Vector &v,
const Point2d &ta, const Point2d &tb, hFill hcf) override;
void InvalidatePixmap(std::shared_ptr<const Pixmap> pm) override;
void SelectPrimitive(unsigned mode);
void UnSelectPrimitive();
Stroke *SelectStroke(hStroke hcs);
Fill *SelectFill(hFill hcf);
void SelectTexture(std::shared_ptr<const Pixmap> 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<Pixmap> ReadFrame() override;
void GetIdent(const char **vendor, const char **renderer, const char **version) override;
};
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// A simple OpenGL state tracker to group consecutive draw calls. // 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); *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<ViewportCanvas> CreateRenderer() {
return std::shared_ptr<ViewportCanvas>(new OpenGl1Renderer());
}
} }

View File

@ -223,17 +223,25 @@ void TextWindow::MakeColorTable(const Color *in, float *out) {
} }
void TextWindow::Init() { void TextWindow::Init() {
canvas = CreateRenderer();
ClearSuper(); ClearSuper();
} }
void TextWindow::ClearSuper() { void TextWindow::ClearSuper() {
HideEditControl(); HideEditControl();
// Ugly hack, but not so ugly as the next line
std::shared_ptr<ViewportCanvas> oldCanvas = canvas;
// Cannot use *this = {} here because TextWindow instances // Cannot use *this = {} here because TextWindow instances
// are 2.4MB long; this causes stack overflows in prologue // are 2.4MB long; this causes stack overflows in prologue
// when built with MSVC, even with optimizations. // when built with MSVC, even with optimizations.
memset(this, 0, sizeof(*this)); memset(this, 0, sizeof(*this));
// Return old canvas
canvas = oldCanvas;
MakeColorTable(fgColors, fgColorTable); MakeColorTable(fgColors, fgColorTable);
MakeColorTable(bgColors, bgColorTable); MakeColorTable(bgColors, bgColorTable);
@ -843,7 +851,8 @@ bool TextWindow::DrawOrHitTestColorPicker(UiCanvas *uiCanvas, DrawOrHitHow how,
} }
void TextWindow::Paint() { void TextWindow::Paint() {
#if !defined(HEADLESS) if (!canvas) return;
int width, height; int width, height;
GetTextWindowSize(&width, &height); GetTextWindowSize(&width, &height);
@ -854,13 +863,11 @@ void TextWindow::Paint() {
camera.offset.x = -(double)camera.width / 2.0; camera.offset.x = -(double)camera.width / 2.0;
camera.offset.y = -(double)camera.height / 2.0; camera.offset.y = -(double)camera.height / 2.0;
OpenGl1Renderer canvas = {}; canvas->BeginFrame();
canvas.camera = camera; canvas->SetCamera(camera);
canvas.BeginFrame();
canvas.UpdateProjection();
UiCanvas uiCanvas = {}; UiCanvas uiCanvas = {};
uiCanvas.canvas = &canvas; uiCanvas.canvas = canvas;
uiCanvas.flip = true; uiCanvas.flip = true;
halfRows = camera.height / (LINE_HEIGHT/2); halfRows = camera.height / (LINE_HEIGHT/2);
@ -983,9 +990,8 @@ void TextWindow::Paint() {
// And we may show a color picker for certain editable fields // And we may show a color picker for certain editable fields
DrawOrHitTestColorPicker(&uiCanvas, PAINT, false, 0, 0); DrawOrHitTestColorPicker(&uiCanvas, PAINT, false, 0, 0);
canvas.EndFrame(); canvas->EndFrame();
canvas.Clear(); canvas->Clear();
#endif
} }
void TextWindow::MouseEvent(bool leftClick, bool leftDown, double x, double y) { void TextWindow::MouseEvent(bool leftClick, bool leftDown, double x, double y) {

View File

@ -194,11 +194,11 @@ public:
} meta[MAX_ROWS][MAX_COLS]; } meta[MAX_ROWS][MAX_COLS];
int hoveredRow, hoveredCol; int hoveredRow, hoveredCol;
int top[MAX_ROWS]; // in half-line units, or -1 for unused int top[MAX_ROWS]; // in half-line units, or -1 for unused
int rows; int rows;
// The row of icons at the top of the text window, to hide/show things std::shared_ptr<ViewportCanvas> canvas;
void Draw(Canvas *canvas); void Draw(Canvas *canvas);
// These are called by the platform-specific code. // These are called by the platform-specific code.
@ -500,6 +500,8 @@ public:
void PasteClipboard(Vector trans, double theta, double scale); void PasteClipboard(Vector trans, double theta, double scale);
static void MenuClipboard(Command id); static void MenuClipboard(Command id);
std::shared_ptr<ViewportCanvas> canvas;
// The width and height (in pixels) of the window. // The width and height (in pixels) of the window.
double width, height; double width, height;
// These parameters define the map from 2d screen coordinates to the // These parameters define the map from 2d screen coordinates to the