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:
parent
f5e955a015
commit
7b9d730a23
@ -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}
|
||||
|
@ -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) {
|
||||
|
41
src/draw.cpp
41
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<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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
6
src/ui.h
6
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<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
|
||||
|
Loading…
Reference in New Issue
Block a user