Implement an OpenGL 2 renderer.
There are two main reasons to desire an OpenGL 2 renderer: 1. Compatibility. The compatibility profile, ironically, does not offer a lot of compatibility, and our OpenGL 1 renderer will not run on Android, iOS, or WebGL. 2. Performance. The immediate mode does not scale, and in fact becomes very slow with only a moderate amount of lines on screen, and only a somewhat large amount of triangles. This commit implements a basic OpenGL 2 renderer that uses only features from the (OpenGL 3.2) core profile. It is not yet faster than the OpenGL 1 renderer, primarily because it uses a lot of small draw calls. This commit uses OpenGL 2 on Linux and Mac OS X directly (i.e. links to the GL symbols from version 2+); on Windows this is impossible with the default drivers, so for now OpenGL 1 is still used there.single-window
parent
f8824e1fb2
commit
6d2c2aecff
|
@ -196,6 +196,20 @@ add_resources(
|
|||
fonts/private/6-stipple-dash.png
|
||||
fonts/private/7-stipple-zigzag.png
|
||||
fonts/unicode.lff.gz
|
||||
shaders/imesh.frag
|
||||
shaders/imesh.vert
|
||||
shaders/imesh_point.frag
|
||||
shaders/imesh_point.vert
|
||||
shaders/imesh_tex.frag
|
||||
shaders/imesh_texa.frag
|
||||
shaders/imesh_tex.vert
|
||||
shaders/mesh.frag
|
||||
shaders/mesh.vert
|
||||
shaders/mesh_fill.frag
|
||||
shaders/mesh_fill.vert
|
||||
shaders/edge.frag
|
||||
shaders/edge.vert
|
||||
shaders/outline.vert
|
||||
threejs/three-r76.js.gz
|
||||
threejs/hammer-2.0.8.js.gz
|
||||
threejs/SolveSpaceControls.js)
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Edge rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
const float feather = 0.5;
|
||||
|
||||
uniform vec4 color;
|
||||
uniform float pixel;
|
||||
uniform float width;
|
||||
uniform float patternLen;
|
||||
uniform float patternScale;
|
||||
uniform sampler2D pattern;
|
||||
|
||||
varying vec3 fragLoc;
|
||||
|
||||
void main() {
|
||||
// lookup distance texture
|
||||
vec4 v = texture2D(pattern, vec2(fragLoc.z / patternScale, 0.0));
|
||||
|
||||
// decode distance value
|
||||
float val = dot(v, vec4(1.0, 1.0 / 255.0, 1.0 / 65025.0, 1.0 / 160581375.0));
|
||||
|
||||
// calculate cap
|
||||
float dist = length(vec2(val * patternScale / (patternLen * width) + abs(fragLoc.x), fragLoc.y));
|
||||
|
||||
// perform antialising
|
||||
float k = smoothstep(1.0 - 2.0 * feather * pixel / (width + feather * pixel), 1.0, abs(dist));
|
||||
|
||||
// perfrom alpha-test
|
||||
if(k == 1.0) discard;
|
||||
|
||||
// write resulting color
|
||||
gl_FragColor = vec4(color.rgb, color.a * (1.0 - k));
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Edge rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
const float feather = 0.5;
|
||||
|
||||
attribute vec3 pos;
|
||||
attribute vec3 loc;
|
||||
attribute vec3 tan;
|
||||
|
||||
uniform mat4 modelview;
|
||||
uniform mat4 projection;
|
||||
uniform float width;
|
||||
uniform float pixel;
|
||||
|
||||
varying vec3 fragLoc;
|
||||
|
||||
void main() {
|
||||
// get camera direction from modelview matrix
|
||||
vec3 dir = vec3(modelview[0].z, modelview[1].z, modelview[2].z);
|
||||
|
||||
// calculate line contour extension basis for constant width and caps
|
||||
vec3 norm = normalize(cross(tan, dir));
|
||||
norm = normalize(norm - dir * dot(dir, norm));
|
||||
vec3 perp = normalize(cross(dir, norm));
|
||||
|
||||
// calculate line extension width considering antialiasing
|
||||
float ext = width + feather * pixel;
|
||||
|
||||
// extend line contour
|
||||
vec3 vertex = pos;
|
||||
vertex += ext * loc.x * normalize(perp);
|
||||
vertex += ext * loc.y * normalize(norm);
|
||||
|
||||
// write fragment location for calculating caps and antialiasing
|
||||
fragLoc = loc;
|
||||
|
||||
// transform resulting vertex with modelview and projection matrices
|
||||
gl_Position = projection * modelview * vec4(vertex, 1.0);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Indexed Mesh rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
uniform vec4 color;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = color;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Indexed Mesh rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
attribute vec3 pos;
|
||||
|
||||
uniform mat4 modelview;
|
||||
uniform mat4 projection;
|
||||
|
||||
void main() {
|
||||
gl_Position = projection * modelview * vec4(pos, 1.0);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Point rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
const float feather = 0.5;
|
||||
|
||||
uniform vec4 color;
|
||||
uniform float pixel;
|
||||
uniform float width;
|
||||
|
||||
varying vec2 fragLoc;
|
||||
|
||||
void main() {
|
||||
// Rectangular points
|
||||
gl_FragColor = color;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Point rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
const float feather = 0.5;
|
||||
|
||||
attribute vec3 pos;
|
||||
attribute vec2 loc;
|
||||
|
||||
uniform mat4 modelview;
|
||||
uniform mat4 projection;
|
||||
uniform float width;
|
||||
uniform float pixel;
|
||||
|
||||
varying vec2 fragLoc;
|
||||
|
||||
void main() {
|
||||
// get camera vectors from modelview matrix
|
||||
vec3 u = vec3(modelview[0].x, modelview[1].x, modelview[2].x);
|
||||
vec3 v = vec3(modelview[0].y, modelview[1].y, modelview[2].y);
|
||||
|
||||
// calculate point contour extension basis for constant width and caps
|
||||
|
||||
// calculate point extension width considering antialiasing
|
||||
float ext = width + feather * pixel;
|
||||
|
||||
// extend point contour
|
||||
vec3 vertex = pos;
|
||||
vertex += ext * loc.x * normalize(u);
|
||||
vertex += ext * loc.y * normalize(v);
|
||||
|
||||
// write fragment location for calculating caps and antialiasing
|
||||
fragLoc = loc;
|
||||
|
||||
// transform resulting vertex with modelview and projection matrices
|
||||
gl_Position = projection * modelview * vec4(vertex, 1.0);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Indexed Mesh rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
uniform vec4 color;
|
||||
uniform sampler2D texture;
|
||||
|
||||
varying vec2 fragTex;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture2D(texture, fragTex) * color;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Indexed Mesh rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
attribute vec3 pos;
|
||||
attribute vec2 tex;
|
||||
|
||||
uniform mat4 modelview;
|
||||
uniform mat4 projection;
|
||||
|
||||
varying vec2 fragTex;
|
||||
|
||||
void main() {
|
||||
fragTex = tex;
|
||||
gl_Position = projection * modelview * vec4(pos, 1.0);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Indexed Mesh rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
uniform vec4 color;
|
||||
uniform sampler2D texture;
|
||||
|
||||
varying vec2 fragTex;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(color.rgb, color.a * texture2D(texture, fragTex).a);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Mesh rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
uniform vec3 lightDir0;
|
||||
uniform vec3 lightDir1;
|
||||
uniform float lightInt0;
|
||||
uniform float lightInt1;
|
||||
uniform float ambient;
|
||||
|
||||
varying vec3 fragNormal;
|
||||
varying vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec3 result = fragColor.xyz * ambient;
|
||||
vec3 normal = normalize(fragNormal);
|
||||
|
||||
float light0 = clamp(dot(lightDir0, normal), 0.0, 1.0) * lightInt0 * (1.0 - ambient);
|
||||
result += fragColor.rgb * light0;
|
||||
|
||||
float light1 = clamp(dot(lightDir1, normal), 0.0, 1.0) * lightInt1 * (1.0 - ambient);
|
||||
result += fragColor.rgb * light1;
|
||||
|
||||
gl_FragColor = vec4(result, fragColor.a);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Mesh rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
attribute vec3 pos;
|
||||
attribute vec3 nor;
|
||||
attribute vec4 col;
|
||||
|
||||
uniform mat4 modelview;
|
||||
uniform mat4 projection;
|
||||
|
||||
varying vec3 fragNormal;
|
||||
varying vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
fragNormal = vec3(modelview * vec4(nor, 0.0));
|
||||
fragColor = col;
|
||||
|
||||
gl_Position = projection * modelview * vec4(pos, 1.0);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Mesh rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
uniform vec4 color;
|
||||
uniform sampler2D texture;
|
||||
|
||||
void main() {
|
||||
if(texture2D(texture, gl_FragCoord.xy / 32.0f).a < 0.5) discard;
|
||||
gl_FragColor = color;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Mesh rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
attribute vec3 pos;
|
||||
|
||||
uniform mat4 modelview;
|
||||
uniform mat4 projection;
|
||||
|
||||
void main() {
|
||||
gl_Position = projection * modelview * vec4(pos, 1.0);
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// SolveSpace Outline rendering shader
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#version 120
|
||||
|
||||
const int EMPHASIZED_AND_CONTOUR = 0;
|
||||
const int EMPHASIZED_WITHOUT_CONTOUR = 1;
|
||||
const int CONTOUR_ONLY = 2;
|
||||
|
||||
const float feather = 0.5;
|
||||
|
||||
attribute vec3 pos;
|
||||
attribute vec4 loc;
|
||||
attribute vec3 tan;
|
||||
attribute vec3 nol;
|
||||
attribute vec3 nor;
|
||||
|
||||
uniform mat4 modelview;
|
||||
uniform mat4 projection;
|
||||
uniform float width;
|
||||
uniform float pixel;
|
||||
uniform int mode;
|
||||
|
||||
varying vec3 fragLoc;
|
||||
|
||||
void main() {
|
||||
// get camera direction from modelview matrix
|
||||
vec3 dir = vec3(modelview[0].z, modelview[1].z, modelview[2].z);
|
||||
|
||||
// perform outline visibility test
|
||||
float ldot = dot(nol, dir);
|
||||
float rdot = dot(nor, dir);
|
||||
|
||||
bool isOutline = (ldot > -1e-6) == (rdot < 1e-6) ||
|
||||
(rdot > -1e-6) == (ldot < 1e-6);
|
||||
bool isTagged = loc.w > 0.5;
|
||||
|
||||
float visible = float((mode == CONTOUR_ONLY && isOutline) ||
|
||||
(mode == EMPHASIZED_AND_CONTOUR && (isOutline || isTagged)) ||
|
||||
(mode == EMPHASIZED_WITHOUT_CONTOUR && isTagged && !isOutline));
|
||||
|
||||
// calculate line contour extension basis for constant width and caps
|
||||
vec3 norm = normalize(cross(tan, dir));
|
||||
norm = normalize(norm - dir * dot(dir, norm));
|
||||
vec3 perp = normalize(cross(dir, norm));
|
||||
|
||||
// calculate line extension width considering antialiasing
|
||||
float ext = (width + feather * pixel) * visible;
|
||||
|
||||
// extend line contour
|
||||
vec3 vertex = pos;
|
||||
vertex += ext * loc.x * normalize(perp);
|
||||
vertex += ext * loc.y * normalize(norm);
|
||||
|
||||
// write fragment location for calculating caps and antialiasing
|
||||
fragLoc = vec3(loc);
|
||||
|
||||
// transform resulting vertex with modelview and projection matrices
|
||||
gl_Position = projection * modelview * vec4(vertex, 1.0);
|
||||
}
|
|
@ -70,10 +70,19 @@ if(SPACEWARE_FOUND)
|
|||
${SPACEWARE_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(gl_SOURCES
|
||||
render/rendergl1.cpp)
|
||||
else()
|
||||
set(gl_SOURCES
|
||||
render/gl2shader.cpp
|
||||
render/rendergl2.cpp)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(platform_SOURCES
|
||||
platform/w32main.cpp
|
||||
render/rendergl1.cpp)
|
||||
${gl_SOURCES})
|
||||
|
||||
set(platform_LIBRARIES
|
||||
comctl32
|
||||
|
@ -85,7 +94,7 @@ elseif(APPLE)
|
|||
set(platform_SOURCES
|
||||
platform/cocoamain.mm
|
||||
render/rendergl.cpp
|
||||
render/rendergl1.cpp)
|
||||
${gl_SOURCES})
|
||||
|
||||
set(platform_BUNDLED_LIBS
|
||||
${PNG_LIBRARIES}
|
||||
|
@ -97,7 +106,7 @@ elseif(HAVE_GTK)
|
|||
set(platform_SOURCES
|
||||
platform/gtkmain.cpp
|
||||
render/rendergl.cpp
|
||||
render/rendergl1.cpp)
|
||||
${gl_SOURCES})
|
||||
|
||||
set(platform_LIBRARIES
|
||||
${SPACEWARE_LIBRARIES})
|
||||
|
@ -120,6 +129,7 @@ set(solvespace_cad_HEADERS
|
|||
solvespace.h
|
||||
ui.h
|
||||
render/render.h
|
||||
render/gl2shader.h
|
||||
srf/surface.h)
|
||||
|
||||
set(solvespace_cad_SOURCES
|
||||
|
|
|
@ -772,6 +772,7 @@ void GraphicsWindow::Paint() {
|
|||
(int)orig.mouse.y, (int)begin.y,
|
||||
/*fillColor=*/Style::Color(Style::HOVERED).WithAlpha(25),
|
||||
/*outlineColor=*/Style::Color(Style::HOVERED));
|
||||
canvas->EndFrame();
|
||||
}
|
||||
|
||||
// And finally the toolbar.
|
||||
|
|
|
@ -554,8 +554,11 @@ void Group::Draw(Canvas *canvas) {
|
|||
? Canvas::DrawOutlinesAs::EMPHASIZED_WITHOUT_CONTOUR
|
||||
: Canvas::DrawOutlinesAs::EMPHASIZED_AND_CONTOUR);
|
||||
|
||||
if(SS.GW.drawOccludedAs == GraphicsWindow::DrawOccludedAs::STIPPLED) {
|
||||
if(SS.GW.drawOccludedAs != GraphicsWindow::DrawOccludedAs::INVISIBLE) {
|
||||
Canvas::Stroke strokeHidden = Style::Stroke(Style::HIDDEN_EDGE);
|
||||
if(SS.GW.drawOccludedAs == GraphicsWindow::DrawOccludedAs::VISIBLE) {
|
||||
strokeHidden.stipplePattern = StipplePattern::CONTINUOUS;
|
||||
}
|
||||
strokeHidden.layer = Canvas::Layer::OCCLUDED;
|
||||
Canvas::hStroke hcsHidden = canvas->GetStroke(strokeHidden);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,248 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// OpenGL 2 shader interface.
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef SOLVESPACE_GL2UTILS_H
|
||||
#define SOLVESPACE_GL2UTILS_H
|
||||
|
||||
#ifdef WIN32
|
||||
# include <windows.h> // required by GL headers
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
# include <OpenGL/gl.h>
|
||||
#else
|
||||
# define GL_GLEXT_PROTOTYPES
|
||||
# include <GL/gl.h>
|
||||
# include <GL/glext.h>
|
||||
#endif
|
||||
|
||||
namespace SolveSpace {
|
||||
|
||||
class Vector2f {
|
||||
public:
|
||||
float x, y;
|
||||
|
||||
static Vector2f From(float x, float y);
|
||||
static Vector2f From(double x, double y);
|
||||
static Vector2f FromInt(uint32_t x, uint32_t y);
|
||||
};
|
||||
|
||||
class Vector3f {
|
||||
public:
|
||||
float x, y, z;
|
||||
|
||||
static Vector3f From(float x, float y, float z);
|
||||
static Vector3f From(const Vector &v);
|
||||
static Vector3f From(const RgbaColor &c);
|
||||
};
|
||||
|
||||
class Vector4f {
|
||||
public:
|
||||
float w, x, y, z;
|
||||
|
||||
static Vector4f From(float x, float y, float z, float w);
|
||||
static Vector4f From(const Vector &v, float w);
|
||||
static Vector4f FromInt(uint32_t x, uint32_t y, uint32_t z, uint32_t w);
|
||||
static Vector4f From(const RgbaColor &c);
|
||||
};
|
||||
|
||||
class Shader {
|
||||
public:
|
||||
GLuint program;
|
||||
|
||||
void Init(const std::string &vertexRes,
|
||||
const std::string &fragmentRes,
|
||||
const std::vector<std::pair<GLuint, std::string>> &locations = {});
|
||||
void Clear();
|
||||
|
||||
void SetUniformMatrix(const char *name, double *md);
|
||||
void SetUniformVector(const char *name, const Vector &v);
|
||||
void SetUniformVector(const char *name, const Vector4f &v);
|
||||
void SetUniformColor(const char *name, RgbaColor c);
|
||||
void SetUniformFloat(const char *name, float v);
|
||||
void SetUniformInt(const char *name, GLint v);
|
||||
void SetUniformTextureUnit(const char *name, GLint index);
|
||||
void Enable() const;
|
||||
void Disable() const;
|
||||
};
|
||||
|
||||
class MeshRenderer {
|
||||
public:
|
||||
const GLint ATTRIB_POS = 0;
|
||||
const GLint ATTRIB_NOR = 1;
|
||||
const GLint ATTRIB_COL = 2;
|
||||
|
||||
struct MeshVertex {
|
||||
Vector3f pos;
|
||||
Vector3f nor;
|
||||
Vector4f col;
|
||||
};
|
||||
|
||||
struct Handle {
|
||||
GLuint vertexBuffer;
|
||||
GLsizei size;
|
||||
};
|
||||
|
||||
Shader lightShader;
|
||||
Shader fillShader;
|
||||
Shader *selectedShader;
|
||||
|
||||
void Init();
|
||||
void Clear();
|
||||
|
||||
Handle Add(const SMesh &m, bool dynamic = false);
|
||||
void Remove(const Handle &handle);
|
||||
void Draw(const Handle &handle, bool useColors = true, RgbaColor overrideColor = {});
|
||||
void Draw(const SMesh &mesh, bool useColors = true, RgbaColor overrideColor = {});
|
||||
|
||||
void SetModelview(double *matrix);
|
||||
void SetProjection(double *matrix);
|
||||
|
||||
void UseShaded(const Lighting &lighting);
|
||||
void UseFilled(const Canvas::Fill &fill);
|
||||
};
|
||||
|
||||
class StippleAtlas {
|
||||
public:
|
||||
std::vector<GLint> patterns;
|
||||
|
||||
void Init();
|
||||
void Clear();
|
||||
|
||||
GLint GetTexture(StipplePattern pattern) const;
|
||||
double GetLength(StipplePattern pattern) const;
|
||||
};
|
||||
|
||||
class EdgeRenderer {
|
||||
public:
|
||||
const GLint ATTRIB_POS = 0;
|
||||
const GLint ATTRIB_LOC = 1;
|
||||
const GLint ATTRIB_TAN = 2;
|
||||
|
||||
struct EdgeVertex {
|
||||
Vector3f pos;
|
||||
Vector3f loc;
|
||||
Vector3f tan;
|
||||
};
|
||||
|
||||
struct Handle {
|
||||
GLuint vertexBuffer;
|
||||
GLuint indexBuffer;
|
||||
GLsizei size;
|
||||
};
|
||||
|
||||
Shader shader;
|
||||
|
||||
const StippleAtlas *atlas;
|
||||
StipplePattern pattern;
|
||||
|
||||
void Init(const StippleAtlas *atlas);
|
||||
void Clear();
|
||||
|
||||
Handle Add(const SEdgeList &edges, bool dynamic = false);
|
||||
void Remove(const Handle &handle);
|
||||
void Draw(const Handle &handle);
|
||||
void Draw(const SEdgeList &edges);
|
||||
|
||||
void SetModelview(double *matrix);
|
||||
void SetProjection(double *matrix);
|
||||
void SetStroke(const Canvas::Stroke &stroke, double pixel);
|
||||
};
|
||||
|
||||
class OutlineRenderer {
|
||||
public:
|
||||
const GLint ATTRIB_POS = 0;
|
||||
const GLint ATTRIB_LOC = 1;
|
||||
const GLint ATTRIB_TAN = 2;
|
||||
const GLint ATTRIB_NOL = 3;
|
||||
const GLint ATTRIB_NOR = 4;
|
||||
|
||||
struct OutlineVertex {
|
||||
Vector3f pos;
|
||||
Vector4f loc;
|
||||
Vector3f tan;
|
||||
Vector3f nol;
|
||||
Vector3f nor;
|
||||
};
|
||||
|
||||
struct Handle {
|
||||
GLuint vertexBuffer;
|
||||
GLuint indexBuffer;
|
||||
GLsizei size;
|
||||
};
|
||||
|
||||
Shader shader;
|
||||
|
||||
const StippleAtlas *atlas;
|
||||
StipplePattern pattern;
|
||||
|
||||
void Init(const StippleAtlas *atlas);
|
||||
void Clear();
|
||||
|
||||
Handle Add(const SOutlineList &outlines, bool dynamic = false);
|
||||
void Remove(const Handle &handle);
|
||||
void Draw(const Handle &handle, Canvas::DrawOutlinesAs mode);
|
||||
void Draw(const SOutlineList &outlines, Canvas::DrawOutlinesAs mode);
|
||||
|
||||
void SetModelview(double *matrix);
|
||||
void SetProjection(double *matrix);
|
||||
void SetStroke(const Canvas::Stroke &stroke, double pixel);
|
||||
};
|
||||
|
||||
class SIndexedMesh {
|
||||
public:
|
||||
struct Vertex {
|
||||
Vector3f pos;
|
||||
Vector2f tex;
|
||||
};
|
||||
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<uint32_t> indices;
|
||||
|
||||
void AddPoint(const Vector &p);
|
||||
void AddQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d);
|
||||
void AddPixmap(const Vector &o, const Vector &u, const Vector &v,
|
||||
const Point2d &ta, const Point2d &tb);
|
||||
|
||||
void Clear();
|
||||
};
|
||||
|
||||
class IndexedMeshRenderer {
|
||||
public:
|
||||
const GLint ATTRIB_POS = 0;
|
||||
const GLint ATTRIB_TEX = 1;
|
||||
|
||||
struct Handle {
|
||||
GLuint vertexBuffer;
|
||||
GLuint indexBuffer;
|
||||
GLsizei size;
|
||||
};
|
||||
|
||||
Shader texShader;
|
||||
Shader texaShader;
|
||||
Shader colShader;
|
||||
Shader pointShader;
|
||||
|
||||
Shader *selectedShader;
|
||||
|
||||
void Init();
|
||||
void Clear();
|
||||
|
||||
Handle Add(const SIndexedMesh &m, bool dynamic = false);
|
||||
void Remove(const Handle &handle);
|
||||
void Draw(const Handle &handle);
|
||||
void Draw(const SIndexedMesh &mesh);
|
||||
|
||||
void SetModelview(double *matrix);
|
||||
void SetProjection(double *matrix);
|
||||
|
||||
bool NeedsTexture() const;
|
||||
|
||||
void UseFilled(const Canvas::Fill &fill);
|
||||
void UsePoint(const Canvas::Stroke &stroke, double pixel);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -186,7 +186,8 @@ bool Canvas::Fill::Equals(const Fill &other) const {
|
|||
return (layer == other.layer &&
|
||||
zIndex == other.zIndex &&
|
||||
color.Equals(other.color) &&
|
||||
pattern == other.pattern);
|
||||
pattern == other.pattern &&
|
||||
texture == other.texture);
|
||||
}
|
||||
|
||||
void Canvas::Clear() {
|
||||
|
|
|
@ -82,9 +82,9 @@ public:
|
|||
// 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, // Both emphasized and contour outlines
|
||||
EMPHASIZED_WITHOUT_CONTOUR, // Emphasized outlines except those also belonging to contour
|
||||
CONTOUR_ONLY // Contour outlines only
|
||||
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).
|
||||
|
@ -126,6 +126,7 @@ public:
|
|||
int zIndex;
|
||||
RgbaColor color;
|
||||
FillPattern pattern;
|
||||
std::shared_ptr<const Pixmap> texture;
|
||||
|
||||
void Clear() { *this = {}; }
|
||||
bool Equals(const Fill &other) const;
|
||||
|
|
|
@ -0,0 +1,694 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// OpenGL 2 based rendering interface.
|
||||
//
|
||||
// Copyright 2016 Aleksey Egorov
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
#include "gl2shader.h"
|
||||
|
||||
namespace SolveSpace {
|
||||
|
||||
class TextureCache {
|
||||
public:
|
||||
std::map<std::weak_ptr<const Pixmap>, GLuint,
|
||||
std::owner_less<std::weak_ptr<const Pixmap>>> items;
|
||||
|
||||
bool Lookup(std::shared_ptr<const Pixmap> ptr, GLuint *result) {
|
||||
auto it = items.find(ptr);
|
||||
if(it == items.end()) {
|
||||
GLuint id;
|
||||
glGenTextures(1, &id);
|
||||
items[ptr] = id;
|
||||
*result = id;
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CleanupUnused() {
|
||||
for(auto it = items.begin(); it != items.end();) {
|
||||
if(it->first.expired()) {
|
||||
glDeleteTextures(1, &it->second);
|
||||
it = items.erase(it);
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// A canvas that uses the core OpenGL 2 profile, for desktop systems.
|
||||
class OpenGl2Renderer : public ViewportCanvas {
|
||||
public:
|
||||
struct SEdgeListItem {
|
||||
hStroke h;
|
||||
SEdgeList lines;
|
||||
|
||||
void Clear() { lines.Clear(); }
|
||||
};
|
||||
|
||||
struct SMeshListItem {
|
||||
hFill h;
|
||||
SIndexedMesh mesh;
|
||||
|
||||
void Clear() { mesh.Clear(); }
|
||||
};
|
||||
|
||||
struct SPointListItem {
|
||||
hStroke h;
|
||||
SIndexedMesh points;
|
||||
|
||||
void Clear() { points.Clear(); }
|
||||
};
|
||||
|
||||
IdList<SEdgeListItem, hStroke> lines;
|
||||
IdList<SMeshListItem, hFill> meshes;
|
||||
IdList<SPointListItem, hStroke> points;
|
||||
|
||||
TextureCache pixmapCache;
|
||||
std::shared_ptr<Pixmap> masks[3];
|
||||
|
||||
bool initialized;
|
||||
StippleAtlas atlas;
|
||||
MeshRenderer meshRenderer;
|
||||
IndexedMeshRenderer imeshRenderer;
|
||||
EdgeRenderer edgeRenderer;
|
||||
OutlineRenderer outlineRenderer;
|
||||
|
||||
Camera camera;
|
||||
Lighting lighting;
|
||||
// Cached OpenGL state.
|
||||
struct {
|
||||
hStroke hcs;
|
||||
Stroke *stroke;
|
||||
hFill hcf;
|
||||
Fill *fill;
|
||||
std::weak_ptr<const Pixmap> texture;
|
||||
} current;
|
||||
|
||||
OpenGl2Renderer() :
|
||||
lines(), meshes(), points(), pixmapCache(), masks(),
|
||||
initialized(), atlas(), meshRenderer(), imeshRenderer(),
|
||||
edgeRenderer(), outlineRenderer(), camera(), lighting(),
|
||||
current() {}
|
||||
|
||||
void Init();
|
||||
|
||||
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;
|
||||
void DrawEdgesInternal(const SEdgeList &el, hStroke hcs);
|
||||
bool DrawBeziers(const SBezierList &bl, hStroke hcs) override { return false; }
|
||||
void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs mode) 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 hs) 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;
|
||||
|
||||
Stroke *SelectStroke(hStroke hcs);
|
||||
Fill *SelectFill(hFill hcf);
|
||||
void SelectMask(FillPattern pattern);
|
||||
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, hStroke hs);
|
||||
void DoStippledLine(const Vector &a, const Vector &b, hStroke hcs);
|
||||
|
||||
void UpdateProjection(bool flip = FLIP_FRAMEBUFFER);
|
||||
void SetCamera(const Camera &c, bool flip) override;
|
||||
void SetLighting(const Lighting &l) 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;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Thin wrappers around OpenGL functions to fix bugs, adapt them to our
|
||||
// data structures, etc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void ssglDepthRange(Canvas::Layer layer, int zIndex) {
|
||||
switch(layer) {
|
||||
case Canvas::Layer::NORMAL:
|
||||
case Canvas::Layer::FRONT:
|
||||
case Canvas::Layer::BACK:
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glDepthMask(GL_TRUE);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
break;
|
||||
|
||||
case Canvas::Layer::DEPTH_ONLY:
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glDepthMask(GL_TRUE);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
break;
|
||||
|
||||
case Canvas::Layer::OCCLUDED:
|
||||
glDepthFunc(GL_GREATER);
|
||||
glDepthMask(GL_FALSE);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
break;
|
||||
}
|
||||
|
||||
switch(layer) {
|
||||
case Canvas::Layer::FRONT:
|
||||
glDepthRange(0.0, 0.0);
|
||||
break;
|
||||
|
||||
case Canvas::Layer::BACK:
|
||||
glDepthRange(1.0, 1.0);
|
||||
break;
|
||||
|
||||
case Canvas::Layer::NORMAL:
|
||||
case Canvas::Layer::DEPTH_ONLY:
|
||||
case Canvas::Layer::OCCLUDED:
|
||||
// The size of this step depends on the resolution of the Z buffer; for
|
||||
// a 16-bit buffer, this should be fine.
|
||||
double offset = 1.0 / (65535 * 0.8) * zIndex;
|
||||
glDepthRange(0.1 - offset, 1.0 - offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A simple OpenGL state tracker to group consecutive draw calls.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Canvas::Stroke *OpenGl2Renderer::SelectStroke(hStroke hcs) {
|
||||
if(current.hcs.v == hcs.v) return current.stroke;
|
||||
|
||||
Stroke *stroke = strokes.FindById(hcs);
|
||||
ssglDepthRange(stroke->layer, stroke->zIndex);
|
||||
|
||||
current.hcs = hcs;
|
||||
current.stroke = stroke;
|
||||
current.hcf = {};
|
||||
current.fill = NULL;
|
||||
current.texture.reset();
|
||||
return stroke;
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::SelectMask(FillPattern pattern) {
|
||||
if(!masks[0]) {
|
||||
masks[0] = Pixmap::Create(Pixmap::Format::A, 32, 32);
|
||||
masks[1] = Pixmap::Create(Pixmap::Format::A, 32, 32);
|
||||
masks[2] = Pixmap::Create(Pixmap::Format::A, 32, 32);
|
||||
|
||||
for(int x = 0; x < 32; x++) {
|
||||
for(int y = 0; y < 32; y++) {
|
||||
masks[0]->data[y * 32 + x] = ((x / 2) % 2 == 0 && (y / 2) % 2 == 0) ? 0xFF : 0x00;
|
||||
masks[1]->data[y * 32 + x] = ((x / 2) % 2 == 1 && (y / 2) % 2 == 1) ? 0xFF : 0x00;
|
||||
masks[2]->data[y * 32 + x] = 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(pattern) {
|
||||
case Canvas::FillPattern::SOLID:
|
||||
SelectTexture(masks[2]);
|
||||
break;
|
||||
|
||||
case Canvas::FillPattern::CHECKERED_A:
|
||||
SelectTexture(masks[0]);
|
||||
break;
|
||||
|
||||
case Canvas::FillPattern::CHECKERED_B:
|
||||
SelectTexture(masks[1]);
|
||||
break;
|
||||
|
||||
default: ssassert(false, "Unexpected fill pattern");
|
||||
}
|
||||
}
|
||||
|
||||
Canvas::Fill *OpenGl2Renderer::SelectFill(hFill hcf) {
|
||||
if(current.hcf.v == hcf.v) return current.fill;
|
||||
|
||||
Fill *fill = fills.FindById(hcf);
|
||||
ssglDepthRange(fill->layer, fill->zIndex);
|
||||
|
||||
current.hcs = {};
|
||||
current.stroke = NULL;
|
||||
current.hcf = hcf;
|
||||
current.fill = fill;
|
||||
if(fill->pattern != FillPattern::SOLID) {
|
||||
SelectMask(fill->pattern);
|
||||
} else if(fill->texture) {
|
||||
SelectTexture(fill->texture);
|
||||
} else {
|
||||
SelectMask(FillPattern::SOLID);
|
||||
}
|
||||
return fill;
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::InvalidatePixmap(std::shared_ptr<const Pixmap> pm) {
|
||||
GLuint id;
|
||||
pixmapCache.Lookup(pm, &id);
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
GLenum format;
|
||||
switch(pm->format) {
|
||||
case Pixmap::Format::RGBA: format = GL_RGBA; break;
|
||||
case Pixmap::Format::RGB: format = GL_RGB; break;
|
||||
case Pixmap::Format::A: format = GL_ALPHA; break;
|
||||
case Pixmap::Format::BGRA:
|
||||
case Pixmap::Format::BGR:
|
||||
ssassert(false, "Unexpected pixmap format");
|
||||
}
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, pm->width, pm->height, 0,
|
||||
format, GL_UNSIGNED_BYTE, &pm->data[0]);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::SelectTexture(std::shared_ptr<const Pixmap> pm) {
|
||||
if(current.texture.lock() == pm) return;
|
||||
|
||||
GLuint id;
|
||||
if(!pixmapCache.Lookup(pm, &id)) {
|
||||
InvalidatePixmap(pm);
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
current.texture = pm;
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DoLine(const Vector &a, const Vector &b, hStroke hcs) {
|
||||
SEdgeListItem *eli = lines.FindByIdNoOops(hcs);
|
||||
if(eli == NULL) {
|
||||
SEdgeListItem item = {};
|
||||
item.h = hcs;
|
||||
lines.Add(&item);
|
||||
eli = lines.FindByIdNoOops(hcs);
|
||||
}
|
||||
|
||||
eli->lines.AddEdge(a, b);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DoPoint(Vector p, hStroke hs) {
|
||||
SPointListItem *pli = points.FindByIdNoOops(hs);
|
||||
if(pli == NULL) {
|
||||
SPointListItem item = {};
|
||||
item.h = hs;
|
||||
points.Add(&item);
|
||||
pli = points.FindByIdNoOops(hs);
|
||||
}
|
||||
|
||||
pli->points.AddPoint(p);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DoStippledLine(const Vector &a, const Vector &b, hStroke hcs) {
|
||||
Stroke *stroke = strokes.FindById(hcs);
|
||||
if(stroke->stipplePattern != StipplePattern::FREEHAND &&
|
||||
stroke->stipplePattern != StipplePattern::ZIGZAG)
|
||||
{
|
||||
DoLine(a, b, hcs);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *patternSeq;
|
||||
Stroke s = *stroke;
|
||||
s.stipplePattern = StipplePattern::CONTINUOUS;
|
||||
hcs = GetStroke(s);
|
||||
switch(stroke->stipplePattern) {
|
||||
case StipplePattern::CONTINUOUS: DoLine(a, b, hcs); return;
|
||||
case StipplePattern::SHORT_DASH: patternSeq = "- "; break;
|
||||
case StipplePattern::DASH: patternSeq = "- "; break;
|
||||
case StipplePattern::LONG_DASH: patternSeq = "_ "; break;
|
||||
case StipplePattern::DASH_DOT: patternSeq = "-."; break;
|
||||
case StipplePattern::DASH_DOT_DOT: patternSeq = "-.."; break;
|
||||
case StipplePattern::DOT: patternSeq = "."; break;
|
||||
case StipplePattern::FREEHAND: patternSeq = "~"; break;
|
||||
case StipplePattern::ZIGZAG: patternSeq = "~__"; break;
|
||||
}
|
||||
|
||||
Vector dir = b.Minus(a);
|
||||
double len = dir.Magnitude();
|
||||
dir = dir.WithMagnitude(1.0);
|
||||
|
||||
const char *si = patternSeq;
|
||||
double end = len;
|
||||
double ss = stroke->stippleScale / 2.0;
|
||||
do {
|
||||
double start = end;
|
||||
switch(*si) {
|
||||
case ' ':
|
||||
end -= 1.0 * ss;
|
||||
break;
|
||||
|
||||
case '-':
|
||||
start = max(start - 0.5 * ss, 0.0);
|
||||
end = max(start - 2.0 * ss, 0.0);
|
||||
if(start == end) break;
|
||||
DoLine(a.Plus(dir.ScaledBy(start)), a.Plus(dir.ScaledBy(end)), hcs);
|
||||
end = max(end - 0.5 * ss, 0.0);
|
||||
break;
|
||||
|
||||
case '_':
|
||||
end = max(end - 4.0 * ss, 0.0);
|
||||
DoLine(a.Plus(dir.ScaledBy(start)), a.Plus(dir.ScaledBy(end)), hcs);
|
||||
break;
|
||||
|
||||
case '.':
|
||||
end = max(end - 0.5 * ss, 0.0);
|
||||
if(end == 0.0) break;
|
||||
DoPoint(a.Plus(dir.ScaledBy(end)), hcs);
|
||||
end = max(end - 0.5 * ss, 0.0);
|
||||
break;
|
||||
|
||||
case '~': {
|
||||
Vector ab = b.Minus(a);
|
||||
Vector gn = (camera.projRight).Cross(camera.projUp);
|
||||
Vector abn = (ab.Cross(gn)).WithMagnitude(1);
|
||||
abn = abn.Minus(gn.ScaledBy(gn.Dot(abn)));
|
||||
double pws = 2.0 * stroke->width / camera.scale;
|
||||
|
||||
end = max(end - 0.5 * ss, 0.0);
|
||||
Vector aa = a.Plus(dir.ScaledBy(start));
|
||||
Vector bb = a.Plus(dir.ScaledBy(end))
|
||||
.Plus(abn.ScaledBy(pws * (start - end) / (0.5 * ss)));
|
||||
DoLine(aa, bb, hcs);
|
||||
if(end == 0.0) break;
|
||||
|
||||
start = end;
|
||||
end = max(end - 1.0 * ss, 0.0);
|
||||
aa = a.Plus(dir.ScaledBy(end))
|
||||
.Plus(abn.ScaledBy(pws))
|
||||
.Minus(abn.ScaledBy(2.0 * pws * (start - end) / ss));
|
||||
DoLine(bb, aa, hcs);
|
||||
if(end == 0.0) break;
|
||||
|
||||
start = end;
|
||||
end = max(end - 0.5 * ss, 0.0);
|
||||
bb = a.Plus(dir.ScaledBy(end))
|
||||
.Minus(abn.ScaledBy(pws))
|
||||
.Plus(abn.ScaledBy(pws * (start - end) / (0.5 * ss)));
|
||||
DoLine(aa, bb, hcs);
|
||||
break;
|
||||
}
|
||||
|
||||
default: ssassert(false, "Unexpected stipple pattern element");
|
||||
}
|
||||
if(*(++si) == 0) si = patternSeq;
|
||||
} while(end > 0.0);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A canvas implemented using OpenGL 2 vertex buffer objects.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void OpenGl2Renderer::Init() {
|
||||
atlas.Init();
|
||||
edgeRenderer.Init(&atlas);
|
||||
outlineRenderer.Init(&atlas);
|
||||
meshRenderer.Init();
|
||||
imeshRenderer.Init();
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DrawLine(const Vector &a, const Vector &b, hStroke hcs) {
|
||||
DoStippledLine(a, b, hcs);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DrawEdges(const SEdgeList &el, hStroke hcs) {
|
||||
for(const SEdge &e : el.l) {
|
||||
DoStippledLine(e.a, e.b, hcs);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DrawEdgesInternal(const SEdgeList &el, hStroke hcs) {
|
||||
if(el.l.n == 0) return;
|
||||
|
||||
Stroke *stroke = SelectStroke(hcs);
|
||||
if(stroke->stipplePattern == StipplePattern::ZIGZAG ||
|
||||
stroke->stipplePattern == StipplePattern::FREEHAND)
|
||||
{
|
||||
for(const SEdge *e = el.l.First(); e; e = el.l.NextAfter(e)) {
|
||||
DoStippledLine(e->a, e->b, hcs);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
edgeRenderer.SetStroke(*stroke, 1.0 / camera.scale);
|
||||
edgeRenderer.Draw(el);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs mode) {
|
||||
if(ol.l.n == 0) return;
|
||||
|
||||
Stroke *stroke = SelectStroke(hcs);
|
||||
ssassert(stroke->stipplePattern != StipplePattern::ZIGZAG &&
|
||||
stroke->stipplePattern != StipplePattern::FREEHAND,
|
||||
"ZIGZAG and FREEHAND not supported for outlines");
|
||||
|
||||
outlineRenderer.SetStroke(*stroke, 1.0 / camera.scale);
|
||||
outlineRenderer.Draw(ol, mode);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DrawVectorText(const std::string &text, double height,
|
||||
const Vector &o, const Vector &u, const Vector &v,
|
||||
hStroke hcs) {
|
||||
SEdgeList el = {};
|
||||
auto traceEdge = [&](Vector a, Vector b) { el.AddEdge(a, b); };
|
||||
VectorFont::Builtin()->Trace(height, o, u, v, text, traceEdge, camera);
|
||||
DrawEdgesInternal(el, hcs);
|
||||
el.Clear();
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
|
||||
hFill hcf) {
|
||||
SMeshListItem *li = meshes.FindByIdNoOops(hcf);
|
||||
if(li == NULL) {
|
||||
SMeshListItem item = {};
|
||||
item.h = hcf;
|
||||
meshes.Add(&item);
|
||||
li = meshes.FindByIdNoOops(hcf);
|
||||
}
|
||||
li->mesh.AddQuad(a, b, c, d);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DrawPoint(const Vector &o, hStroke hs) {
|
||||
DoPoint(o, hs);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DrawPolygon(const SPolygon &p, hFill hcf) {
|
||||
Fill *fill = SelectFill(hcf);
|
||||
|
||||
SMesh m = {};
|
||||
p.TriangulateInto(&m);
|
||||
meshRenderer.UseFilled(*fill);
|
||||
meshRenderer.Draw(m);
|
||||
m.Clear();
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack) {
|
||||
SelectFill(hcfFront);
|
||||
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
meshRenderer.UseShaded(lighting);
|
||||
meshRenderer.Draw(m);
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) {
|
||||
if(faces.empty()) return;
|
||||
|
||||
Fill *fill = SelectFill(hcf);
|
||||
|
||||
SMesh facesMesh = {};
|
||||
for(uint32_t f : faces) {
|
||||
for(const STriangle &t : m.l) {
|
||||
if(f != t.meta.face) continue;
|
||||
facesMesh.l.Add(&t);
|
||||
}
|
||||
}
|
||||
|
||||
meshRenderer.UseFilled(*fill);
|
||||
meshRenderer.Draw(facesMesh);
|
||||
facesMesh.Clear();
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::DrawPixmap(std::shared_ptr<const Pixmap> pm,
|
||||
const Vector &o, const Vector &u, const Vector &v,
|
||||
const Point2d &ta, const Point2d &tb, hFill hcf) {
|
||||
Fill fill = *fills.FindById(hcf);
|
||||
fill.texture = pm;
|
||||
hcf = GetFill(fill);
|
||||
|
||||
SMeshListItem *mli = meshes.FindByIdNoOops(hcf);
|
||||
if(mli == NULL) {
|
||||
SMeshListItem item = {};
|
||||
item.h = hcf;
|
||||
meshes.Add(&item);
|
||||
mli = meshes.FindByIdNoOops(hcf);
|
||||
}
|
||||
|
||||
mli->mesh.AddPixmap(o, u, v, ta, tb);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::UpdateProjection(bool flip) {
|
||||
glViewport(0, 0, camera.width, camera.height);
|
||||
|
||||
double mat1[16];
|
||||
double mat2[16];
|
||||
|
||||
double sx = camera.scale * 2.0 / camera.width;
|
||||
double sy = camera.scale * 2.0 / camera.height;
|
||||
double sz = camera.scale * 1.0 / 30000;
|
||||
|
||||
MakeMatrix(mat1,
|
||||
sx, 0, 0, 0,
|
||||
0, sy, 0, 0,
|
||||
0, 0, sz, 0,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
|
||||
// Last thing before display is to apply the perspective
|
||||
double clp = camera.tangent * camera.scale;
|
||||
double fy = flip ? -1.0 : 1.0;
|
||||
MakeMatrix(mat2,
|
||||
1, 0, 0, 0,
|
||||
0, fy, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, clp, 1
|
||||
);
|
||||
|
||||
// If we flip the framebuffer, then we also flip the handedness
|
||||
// of the coordinate system, and so the face winding order.
|
||||
glFrontFace(flip ? GL_CW : GL_CCW);
|
||||
|
||||
double projection[16];
|
||||
MultMatrix(mat1, mat2, projection);
|
||||
|
||||
// Before that, we apply the rotation
|
||||
Vector u = camera.projRight,
|
||||
v = camera.projUp,
|
||||
n = camera.projUp.Cross(camera.projRight);
|
||||
MakeMatrix(mat1,
|
||||
u.x, u.y, u.z, 0,
|
||||
v.x, v.y, v.z, 0,
|
||||
n.x, n.y, n.z, 0,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
|
||||
// And before that, the translation
|
||||
Vector o = camera.offset;
|
||||
MakeMatrix(mat2,
|
||||
1, 0, 0, o.x,
|
||||
0, 1, 0, o.y,
|
||||
0, 0, 1, o.z,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
|
||||
double modelview[16];
|
||||
MultMatrix(mat1, mat2, modelview);
|
||||
|
||||
imeshRenderer.SetProjection(projection);
|
||||
imeshRenderer.SetModelview(modelview);
|
||||
meshRenderer.SetProjection(projection);
|
||||
meshRenderer.SetModelview(modelview);
|
||||
edgeRenderer.SetProjection(projection);
|
||||
edgeRenderer.SetModelview(modelview);
|
||||
outlineRenderer.SetProjection(projection);
|
||||
outlineRenderer.SetModelview(modelview);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::BeginFrame() {
|
||||
if(!initialized) {
|
||||
Init();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
RgbaColor backgroundColor = lighting.backgroundColor;
|
||||
glClearColor(backgroundColor.redF(), backgroundColor.greenF(),
|
||||
backgroundColor.blueF(), backgroundColor.alphaF());
|
||||
glClearDepth(1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glPolygonOffset(2.0, 1.0);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::EndFrame() {
|
||||
for(SMeshListItem &li : meshes) {
|
||||
Fill *fill = SelectFill(li.h);
|
||||
|
||||
imeshRenderer.UseFilled(*fill);
|
||||
imeshRenderer.Draw(li.mesh);
|
||||
li.mesh.Clear();
|
||||
}
|
||||
meshes.Clear();
|
||||
|
||||
for(SEdgeListItem &eli : lines) {
|
||||
DrawEdgesInternal(eli.lines, eli.h);
|
||||
eli.lines.Clear();
|
||||
}
|
||||
lines.Clear();
|
||||
|
||||
for(SPointListItem &li : points) {
|
||||
Stroke *stroke = SelectStroke(li.h);
|
||||
|
||||
imeshRenderer.UsePoint(*stroke, 1.0 / camera.scale);
|
||||
imeshRenderer.Draw(li.points);
|
||||
li.points.Clear();
|
||||
}
|
||||
points.Clear();
|
||||
|
||||
glFinish();
|
||||
|
||||
GLenum error = glGetError();
|
||||
if(error != GL_NO_ERROR) {
|
||||
dbp("glGetError() == 0x%X", error);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Pixmap> OpenGl2Renderer::ReadFrame() {
|
||||
std::shared_ptr<Pixmap> pixmap =
|
||||
Pixmap::Create(Pixmap::Format::RGB, (size_t)camera.width, (size_t)camera.height);
|
||||
glReadPixels(0, 0, camera.width, camera.height, GL_RGB, GL_UNSIGNED_BYTE, &pixmap->data[0]);
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::GetIdent(const char **vendor, const char **renderer, const char **version) {
|
||||
*vendor = (const char *)glGetString(GL_VENDOR);
|
||||
*renderer = (const char *)glGetString(GL_RENDERER);
|
||||
*version = (const char *)glGetString(GL_VERSION);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::SetCamera(const Camera &c, bool flip) {
|
||||
camera = c;
|
||||
UpdateProjection(flip);
|
||||
}
|
||||
|
||||
void OpenGl2Renderer::SetLighting(const Lighting &l) {
|
||||
lighting = l;
|
||||
}
|
||||
|
||||
std::shared_ptr<ViewportCanvas> CreateRenderer() {
|
||||
return std::shared_ptr<ViewportCanvas>(new OpenGl2Renderer());
|
||||
}
|
||||
|
||||
}
|
|
@ -365,6 +365,8 @@ void MakeMatrix(double *mat, double a11, double a12, double a13, double a14,
|
|||
double a21, double a22, double a23, double a24,
|
||||
double a31, double a32, double a33, double a34,
|
||||
double a41, double a42, double a43, double a44);
|
||||
void MultMatrix(double *mata, double *matb, double *matr);
|
||||
|
||||
std::string MakeAcceleratorLabel(int accel);
|
||||
bool FilenameHasExtension(const std::string &str, const char *ext);
|
||||
std::string Extension(const std::string &filename);
|
||||
|
|
12
src/util.cpp
12
src/util.cpp
|
@ -155,6 +155,18 @@ void SolveSpace::MakeMatrix(double *mat,
|
|||
mat[15] = a44;
|
||||
}
|
||||
|
||||
void SolveSpace::MultMatrix(double *mata, double *matb, double *matr) {
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int j = 0; j < 4; j++) {
|
||||
double s = 0.0;
|
||||
for(int k = 0; k < 4; k++) {
|
||||
s += mata[k * 4 + j] * matb[i * 4 + k];
|
||||
}
|
||||
matr[i * 4 + j] = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Word-wrap the string for our message box appropriately, and then display
|
||||
// that string.
|
||||
|
|
Loading…
Reference in New Issue