solvespace/src/render/render.h

386 lines
14 KiB
C++

//-----------------------------------------------------------------------------
// Backend-agnostic rendering interface, and various backends we use.
//
// Copyright 2016 whitequark
//-----------------------------------------------------------------------------
#ifndef SOLVESPACE_RENDER_H
#define SOLVESPACE_RENDER_H
//-----------------------------------------------------------------------------
// Interfaces common for all renderers
//-----------------------------------------------------------------------------
enum class StipplePattern : uint32_t;
// A mapping from 3d sketch coordinates to 2d screen coordinates, using
// an axonometric projection.
class Camera {
public:
double width;
double height;
double pixelRatio;
bool gridFit;
Vector offset;
Vector projRight;
Vector projUp;
double scale;
double tangent;
bool IsPerspective() const { return tangent != 0.0; }
Point2d ProjectPoint(Vector p) const;
Vector ProjectPoint3(Vector p) const;
Vector ProjectPoint4(Vector p, double *w) const;
Vector UnProjectPoint(Point2d p) const;
Vector UnProjectPoint3(Vector p) const;
Vector VectorFromProjs(Vector rightUpForward) const;
Vector AlignToPixelGrid(Vector v) const;
SBezier ProjectBezier(SBezier b) const;
void LoadIdentity();
void NormalizeProjectionVectors();
};
// A description of scene lighting.
class Lighting {
public:
RgbaColor backgroundColor;
double ambientIntensity;
double lightIntensity[2];
Vector lightDirection[2];
};
class BatchCanvas;
// An interface for populating a drawing area with geometry.
class Canvas {
public:
// Stroke and fill styles are addressed with handles to be able to quickly
// group geometry into indexed draw calls.
class hStroke {
public:
uint32_t v;
};
class hFill {
public:
uint32_t v;
};
// The layer of a geometry describes how it occludes other geometry.
// Within a layer, geometry with higher z-index occludes geometry with lower z-index,
// or geometry drawn earlier if z-indexes match.
enum class Layer {
NORMAL, // Occluded by geometry with lower Z coordinate
OCCLUDED, // Only drawn over geometry with lower Z coordinate
DEPTH_ONLY, // Like NORMAL, but only affects future occlusion, not color
BACK, // Always drawn below all other geometry
FRONT, // Always drawn above all other geometry
LAST = FRONT
};
// The outlines are the collection of all edges that may be drawn.
// Outlines can be classified as emphasized or not; emphasized outlines indicate an abrupt
// change in the surface curvature. These are indicated by the SOutline tag.
// Outlines can also be classified as contour or not; contour outlines indicate the boundary
// of the filled mesh. Whether an outline is a part of contour or not depends on point of view.
enum class DrawOutlinesAs {
EMPHASIZED_AND_CONTOUR = 0, // Both emphasized and contour outlines
EMPHASIZED_WITHOUT_CONTOUR = 1, // Emphasized outlines except those also belonging to contour
CONTOUR_ONLY = 2 // Contour outlines only
};
// Stroke widths, etc, can be scale-invariant (in pixels) or scale-dependent (in millimeters).
enum class Unit {
MM,
PX
};
class Stroke {
public:
hStroke h;
Layer layer;
int zIndex;
RgbaColor color;
double width;
Unit unit;
StipplePattern stipplePattern;
double stippleScale;
void Clear() { *this = {}; }
bool Equals(const Stroke &other) const;
double WidthMm(const Camera &camera) const;
double WidthPx(const Camera &camera) const;
double StippleScaleMm(const Camera &camera) const;
double StippleScalePx(const Camera &camera) const;
};
enum class FillPattern {
SOLID, CHECKERED_A, CHECKERED_B
};
class Fill {
public:
hFill h;
Layer layer;
int zIndex;
RgbaColor color;
FillPattern pattern;
std::shared_ptr<const Pixmap> texture;
void Clear() { *this = {}; }
bool Equals(const Fill &other) const;
};
IdList<Stroke, hStroke> strokes = {};
IdList<Fill, hFill> fills = {};
BitmapFont bitmapFont = {};
virtual void Clear();
virtual ~Canvas() = default;
hStroke GetStroke(const Stroke &stroke);
hFill GetFill(const Fill &fill);
BitmapFont *GetBitmapFont();
virtual const Camera &GetCamera() const = 0;
virtual void DrawLine(const Vector &a, const Vector &b, hStroke hcs) = 0;
virtual void DrawEdges(const SEdgeList &el, hStroke hcs) = 0;
virtual bool DrawBeziers(const SBezierList &bl, hStroke hcs) = 0;
virtual void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) = 0;
virtual void DrawVectorText(const std::string &text, double height,
const Vector &o, const Vector &u, const Vector &v,
hStroke hcs) = 0;
virtual void DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
hFill hcf) = 0;
virtual void DrawPoint(const Vector &o, hStroke hcs) = 0;
virtual void DrawPolygon(const SPolygon &p, hFill hcf) = 0;
virtual void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack = {}) = 0;
virtual void DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) = 0;
virtual 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) = 0;
virtual void InvalidatePixmap(std::shared_ptr<const Pixmap> pm) = 0;
virtual std::shared_ptr<BatchCanvas> CreateBatch();
};
template<>
struct IsHandleOracle<Canvas::hStroke> : std::true_type {};
template<>
struct IsHandleOracle<Canvas::hFill> : std::true_type {};
// An interface for view-dependent visualization.
class ViewportCanvas : public Canvas {
public:
virtual void SetCamera(const Camera &camera) = 0;
virtual void SetLighting(const Lighting &lighting) = 0;
virtual void StartFrame() = 0;
virtual void FlushFrame() = 0;
virtual void FinishFrame() = 0;
virtual std::shared_ptr<Pixmap> ReadFrame() = 0;
virtual void GetIdent(const char **vendor, const char **renderer, const char **version) = 0;
};
// An interface for view-independent visualization.
class BatchCanvas : public Canvas {
public:
const Camera &GetCamera() const override;
virtual void Finalize() = 0;
virtual void Draw() = 0;
};
// A wrapper around Canvas that simplifies drawing UI in screen coordinates.
class UiCanvas {
public:
std::shared_ptr<Canvas> canvas;
bool flip = false;
void DrawLine(int x1, int y1, int x2, int y2, RgbaColor color, int width = 1,
int zIndex = 0);
void DrawRect(int l, int r, int t, int b, RgbaColor fillColor, RgbaColor outlineColor,
int zIndex = 0);
void DrawPixmap(std::shared_ptr<const Pixmap> pm, int x, int y,
int zIndex = 0);
void DrawBitmapChar(char32_t codepoint, int x, int y, RgbaColor color,
int zIndex = 0);
void DrawBitmapText(const std::string &str, int x, int y, RgbaColor color,
int zIndex = 0);
int Flip(int y) const { return flip ? (int)canvas->GetCamera().height - y : y; }
};
// A canvas that performs picking against drawn geometry.
class ObjectPicker : public Canvas {
public:
Camera camera = {};
// Configuration.
Point2d point = {};
double selRadius = 0.0;
// Picking state.
double minDistance = 0.0;
double minDepth = 1e10;
int maxZIndex = 0;
uint32_t position = 0;
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) 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 DoCompare(double depth, double distance, int zIndex, int comparePosition = 0);
void DoQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
int zIndex, int comparePosition = 0);
bool Pick(const std::function<void()> &drawFn);
};
// A canvas that renders onto a 2d surface, performing z-index sorting, occlusion testing, etc,
// on the CPU.
class SurfaceRenderer : public ViewportCanvas {
public:
Camera camera = {};
Lighting lighting = {};
// Chord tolerance, for converting beziers to pwl.
double chordTolerance = 0.0;
// Render lists.
handle_map<hStroke, SEdgeList> edges;
handle_map<hStroke, SBezierList> beziers;
SMesh mesh = {};
// State.
BBox bbox = {};
void Clear() override;
// Canvas interface.
const Camera &GetCamera() const override { return camera; }
// ViewportCanvas interface.
void SetCamera(const Camera &cam) override { this->camera = cam; }
void SetLighting(const Lighting &light) override { this->lighting = light; }
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;
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) 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;
// Geometry manipulation.
void CalculateBBox();
void ConvertBeziersToEdges();
void CullOccludedStrokes();
// Renderer operations.
void OutputInPaintOrder();
virtual bool CanOutputCurves() const = 0;
virtual bool CanOutputTriangles() const = 0;
virtual void OutputStart() = 0;
virtual void OutputBezier(const SBezier &b, hStroke hcs) = 0;
virtual void OutputTriangle(const STriangle &tr) = 0;
virtual void OutputEnd() = 0;
void OutputBezierAsNonrationalCubic(const SBezier &b, hStroke hcs);
};
//-----------------------------------------------------------------------------
// 2d renderers
//-----------------------------------------------------------------------------
class CairoRenderer : public SurfaceRenderer {
public:
cairo_t *context = NULL;
// Renderer configuration.
bool antialias = false;
// Renderer state.
struct {
hStroke hcs;
} current = {};
void Clear() override;
void StartFrame() override {}
void FlushFrame() override;
void FinishFrame() override {}
std::shared_ptr<Pixmap> ReadFrame() override;
void GetIdent(const char **vendor, const char **renderer, const char **version) override;
void SelectStroke(hStroke hcs);
void MoveTo(Vector p);
void FinishPath();
bool CanOutputCurves() const override { return true; }
bool CanOutputTriangles() const override { return true; }
void OutputStart() override;
void OutputBezier(const SBezier &b, hStroke hcs) override;
void OutputTriangle(const STriangle &tr) override;
void OutputEnd() override;
};
class CairoPixmapRenderer final : public CairoRenderer {
public:
std::shared_ptr<Pixmap> pixmap;
cairo_surface_t *surface = NULL;
void Init();
void Clear() override;
std::shared_ptr<Pixmap> ReadFrame() override;
};
//-----------------------------------------------------------------------------
// Factories
//-----------------------------------------------------------------------------
std::shared_ptr<ViewportCanvas> CreateRenderer();
#endif