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
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}

View File

@ -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) {

View File

@ -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<double, std::milli> renderTime = renderEndTime - renderStartTime;
@ -803,7 +803,6 @@ void GraphicsWindow::Paint() {
5, 5, renderTimeColor);
}
canvas.EndFrame();
canvas.Clear();
#endif
canvas->EndFrame();
canvas->Clear();
}

View File

@ -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<Pixmap> 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<ViewportCanvas> canvas = CreateRenderer();
canvas->SetCamera(SS.GW.GetCamera());
std::shared_ptr<Pixmap> 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);

View File

@ -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);

View File

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

View File

@ -164,13 +164,24 @@ public:
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.
class UiCanvas {
public:
Canvas *canvas;
bool flip;
UiCanvas() : canvas(), flip() {};
std::shared_ptr<Canvas> 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<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);
};
std::shared_ptr<ViewportCanvas> CreateRenderer();
#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.
//-----------------------------------------------------------------------------
@ -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<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() {
canvas = CreateRenderer();
ClearSuper();
}
void TextWindow::ClearSuper() {
HideEditControl();
// Ugly hack, but not so ugly as the next line
std::shared_ptr<ViewportCanvas> 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) {

View File

@ -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<ViewportCanvas> 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<ViewportCanvas> canvas;
// The width and height (in pixels) of the window.
double width, height;
// These parameters define the map from 2d screen coordinates to the