solvespace/src/render/gl2shader.cpp
whitequark c8ff17f4a2 Add OpenGL 2 support on Windows using ANGLE.
This commit performs two main changes:
  * Alters the shaders to use only strictly conformant GLSL 2.0.
  * Alters the Windows UI to use ANGLE via GL ES 2.0 and EGL 1.4.

This commit also drops official support for Windows XP, since ANGLE
requires a non-XP toolset to build. It is still possible to build
SolveSpace for Windows XP using:

  cmake -T v120_xp -DOPENGL=1
2016-11-18 11:38:45 +00:00

1043 lines
35 KiB
C++

//-----------------------------------------------------------------------------
// OpenGL 2 shader interface.
//
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
#include "solvespace.h"
#include "gl2shader.h"
namespace SolveSpace {
//-----------------------------------------------------------------------------
// Floating point data sturctures
//-----------------------------------------------------------------------------
Vector2f Vector2f::From(float x, float y) {
return { x, y };
}
Vector2f Vector2f::From(double x, double y) {
return { (float)x, (float)y };
}
Vector2f Vector2f::FromInt(uint32_t x, uint32_t y) {
return { (float)x, (float)y };
}
Vector3f Vector3f::From(float x, float y, float z) {
return { x, y, z };
}
Vector3f Vector3f::From(const Vector &v) {
return { (float)v.x, (float)v.y, (float)v.z };
}
Vector3f Vector3f::From(const RgbaColor &c) {
return { c.redF(), c.greenF(), c.blueF() };
}
Vector4f Vector4f::From(float x, float y, float z, float w) {
return { x, y, z, w };
}
Vector4f Vector4f::From(const Vector &v, float w) {
return { (float)v.x, (float)v.y, (float)v.z, w };
}
Vector4f Vector4f::FromInt(uint32_t x, uint32_t y, uint32_t z, uint32_t w) {
return { (float)x, (float)y, (float)z, (float)w };
}
Vector4f Vector4f::From(const RgbaColor &c) {
return { c.redF(), c.greenF(), c.blueF(), c.alphaF() };
}
//-----------------------------------------------------------------------------
// Shader manipulation
//-----------------------------------------------------------------------------
static GLuint CompileShader(const std::string &res, GLenum type) {
size_t size;
const char *resData = (const char *)LoadResource(res, &size);
// Sigh, here we go... We want to deploy to four platforms: Linux, Windows, OS X, mobile+web.
// These platforms are basically disjunctive in the OpenGL versions and profiles that they
// support: mobile+web support GLES2, Windows can only be guaranteed to support GL1 without
// vendor's drivers installed but supports D3D9+ natively, Linux supports GL3.2+ and/or
// GLES2+ depending on whether we run on X11 or Wayland, and OS X supports either a legacy
// profile or a GL3.2 core profile or (on 10.9+) a GL4.1 core profile.
// The platforms barely have a common subset of features:
// * Linux Mesa/NVidia accept basically everything thrown at it;
// * mobile+web and Windows (D3D9 through ANGLE) are strictly GLES2/GLSL1.0;
// * OS X legacy compatibility profile has GLSL1.2 only shaders, and GL3.2 core profile
// that has GLSL1.0 shaders compatible with GLES2 makes mandatory the use of vertex array
// objects, which cannot be used in GLES2 at all; similarly GL3.2 core has GL_RED but not
// GL_ALPHA whereas GLES2 has GL_ALPHA but not GL_RED.
// While we're at it, let's remember that GLES2 has *only* glDepthRangef, GL3.2 has *only*
// glDepthRange, and GL4.1+ has both glDepthRangef and glDepthRange. Also, that GLSL1.0
// makes `precision highp float;` mandatory in fragment shaders, and GLSL1.2 removes
// the `precision` keyword entirely, because that's clearly how minor versions work.
// Christ, what a trash fire.
std::string src(resData, size);
#ifdef __APPLE__
src = "#version 120\n" + src;
#else
src = "#version 100\nprecision highp float;\n" + src;
#endif
GLuint shader = glCreateShader(type);
ssassert(shader != 0, "glCreateShader failed");
const GLint glSize[] = { (int)src.length() };
const GLchar* glSource[] = { src.c_str() };
glShaderSource(shader, 1, glSource, glSize);
glCompileShader(shader);
GLint infoLen;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if(infoLen > 1) {
std::string infoStr(infoLen, '\0');
glGetShaderInfoLog(shader, infoLen, NULL, &infoStr[0]);
dbp(infoStr.c_str());
}
GLint compiled;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
ssassert(compiled, "Cannot compile shader");
return shader;
}
void Shader::Init(const std::string &vertexRes, const std::string &fragmentRes,
const std::vector<std::pair<GLuint, std::string> > &locations) {
GLuint vert = CompileShader(vertexRes, GL_VERTEX_SHADER);
GLuint frag = CompileShader(fragmentRes, GL_FRAGMENT_SHADER);
program = glCreateProgram();
ssassert(program != 0, "glCreateProgram failed");
glAttachShader(program, vert);
glAttachShader(program, frag);
for(const auto &l : locations) {
glBindAttribLocation(program, l.first, l.second.c_str());
}
glLinkProgram(program);
GLint infoLen;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
if(infoLen > 1) {
std::string infoStr(infoLen, '\0');
glGetProgramInfoLog(program, infoLen, NULL, &infoStr[0]);
dbp(infoStr.c_str());
}
GLint linked;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
ssassert(linked, "Cannot link shader");
}
void Shader::Clear() {
glDeleteProgram(program);
}
void Shader::SetUniformMatrix(const char *name, double *md) {
Enable();
float mf[16];
for(int i = 0; i < 16; i++) mf[i] = (float)md[i];
glUniformMatrix4fv(glGetUniformLocation(program, name), 1, false, mf);
}
void Shader::SetUniformVector(const char *name, const Vector &v) {
Enable();
glUniform3f(glGetUniformLocation(program, name), (float)v.x, (float)v.y, (float)v.z);
}
void Shader::SetUniformVector(const char *name, const Vector4f &v) {
Enable();
glUniform4f(glGetUniformLocation(program, name), v.x, v.y, v.z, v.w);
}
void Shader::SetUniformColor(const char *name, RgbaColor c) {
Enable();
glUniform4f(glGetUniformLocation(program, name), c.redF(), c.greenF(), c.blueF(), c.alphaF());
}
void Shader::SetUniformFloat(const char *name, float v) {
Enable();
glUniform1f(glGetUniformLocation(program, name), v);
}
void Shader::SetUniformInt(const char *name, GLint v) {
Enable();
glUniform1i(glGetUniformLocation(program, name), v);
}
void Shader::SetUniformTextureUnit(const char *name, GLint index) {
Enable();
glUniform1i(glGetUniformLocation(program, name), index);
}
void Shader::Enable() const {
glUseProgram(program);
}
void Shader::Disable() const {
glUseProgram(0);
}
//-----------------------------------------------------------------------------
// Mesh rendering
//-----------------------------------------------------------------------------
void MeshRenderer::Init() {
lightShader.Init(
"shaders/mesh.vert", "shaders/mesh.frag",
{
{ ATTRIB_POS, "pos" },
{ ATTRIB_NOR, "nor" },
{ ATTRIB_COL, "col" },
}
);
fillShader.Init(
"shaders/mesh_fill.vert", "shaders/mesh_fill.frag",
{
{ ATTRIB_POS, "pos" },
}
);
fillShader.SetUniformTextureUnit("texture", 0);
selectedShader = &lightShader;
}
void MeshRenderer::Clear() {
lightShader.Clear();
fillShader.Clear();
}
MeshRenderer::Handle MeshRenderer::Add(const SMesh &m, bool dynamic) {
Handle handle;
glGenBuffers(1, &handle.vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
MeshVertex *vertices = new MeshVertex[m.l.n * 3];
for(int i = 0; i < m.l.n; i++) {
const STriangle &t = m.l.elem[i];
vertices[i * 3 + 0].pos = Vector3f::From(t.a);
vertices[i * 3 + 1].pos = Vector3f::From(t.b);
vertices[i * 3 + 2].pos = Vector3f::From(t.c);
if(t.an.EqualsExactly(Vector::From(0, 0, 0))) {
Vector3f normal = Vector3f::From(t.Normal());
vertices[i * 3 + 0].nor = normal;
vertices[i * 3 + 1].nor = normal;
vertices[i * 3 + 2].nor = normal;
} else {
vertices[i * 3 + 0].nor = Vector3f::From(t.an);
vertices[i * 3 + 1].nor = Vector3f::From(t.bn);
vertices[i * 3 + 2].nor = Vector3f::From(t.cn);
}
for(int j = 0; j < 3; j++) {
vertices[i * 3 + j].col = Vector4f::From(t.meta.color);
}
}
glBufferData(GL_ARRAY_BUFFER, m.l.n * 3 * sizeof(MeshVertex),
vertices, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
handle.size = m.l.n * 3;
delete []vertices;
return handle;
}
void MeshRenderer::Remove(const MeshRenderer::Handle &handle) {
glDeleteBuffers(1, &handle.vertexBuffer);
}
void MeshRenderer::Draw(const MeshRenderer::Handle &handle,
bool useColors, RgbaColor overrideColor) {
selectedShader->Enable();
glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
glEnableVertexAttribArray(ATTRIB_POS);
glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, sizeof(MeshVertex),
(void *)offsetof(MeshVertex, pos));
if(selectedShader == &lightShader) {
glEnableVertexAttribArray(ATTRIB_NOR);
glVertexAttribPointer(ATTRIB_NOR, 3, GL_FLOAT, GL_FALSE, sizeof(MeshVertex),
(void *)offsetof(MeshVertex, nor));
if(useColors) {
glEnableVertexAttribArray(ATTRIB_COL);
glVertexAttribPointer(ATTRIB_COL, 4, GL_FLOAT, GL_FALSE, sizeof(MeshVertex),
(void *)offsetof(MeshVertex, col));
} else {
glVertexAttrib4f(ATTRIB_COL, overrideColor.redF(), overrideColor.greenF(), overrideColor.blueF(), overrideColor.alphaF());
}
}
glDrawArrays(GL_TRIANGLES, 0, handle.size);
glDisableVertexAttribArray(ATTRIB_POS);
if(selectedShader == &lightShader) {
glDisableVertexAttribArray(ATTRIB_NOR);
if(useColors) glDisableVertexAttribArray(ATTRIB_COL);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
selectedShader->Disable();
}
void MeshRenderer::Draw(const SMesh &mesh, bool useColors, RgbaColor overrideColor) {
Handle handle = Add(mesh, /*dynamic=*/true);
Draw(handle, useColors, overrideColor);
Remove(handle);
}
void MeshRenderer::SetModelview(double *matrix) {
lightShader.SetUniformMatrix("modelview", matrix);
fillShader.SetUniformMatrix("modelview", matrix);
}
void MeshRenderer::SetProjection(double *matrix) {
lightShader.SetUniformMatrix("projection", matrix);
fillShader.SetUniformMatrix("projection", matrix);
}
void MeshRenderer::UseShaded(const Lighting &lighting) {
Vector dir0 = lighting.lightDirection[0];
Vector dir1 = lighting.lightDirection[1];
dir0.z = -dir0.z;
dir1.z = -dir1.z;
lightShader.SetUniformVector("lightDir0", dir0);
lightShader.SetUniformFloat("lightInt0", (float)lighting.lightIntensity[0]);
lightShader.SetUniformVector("lightDir1", dir1);
lightShader.SetUniformFloat("lightInt1", (float)lighting.lightIntensity[1]);
lightShader.SetUniformFloat("ambient", (float)lighting.ambientIntensity);
selectedShader = &lightShader;
}
void MeshRenderer::UseFilled(const Canvas::Fill &fill) {
fillShader.SetUniformColor("color", fill.color);
selectedShader = &fillShader;
}
//-----------------------------------------------------------------------------
// Arrangement of stipple patterns into textures
//-----------------------------------------------------------------------------
static double Frac(double x) {
return x - floor(x);
}
static RgbaColor EncodeLengthAsFloat(double v) {
v = max(0.0, min(1.0, v));
double er = v;
double eg = Frac(255.0 * v);
double eb = Frac(65025.0 * v);
double ea = Frac(160581375.0 * v);
double r = er - eg / 255.0;
double g = eg - eb / 255.0;
double b = eb - ea / 255.0;
return RgbaColor::From((int)floor( r * 255.0 + 0.5),
(int)floor( g * 255.0 + 0.5),
(int)floor( b * 255.0 + 0.5),
(int)floor(ea * 255.0 + 0.5));
}
GLuint Generate(const std::vector<double> &pattern) {
double patternLen = 0.0;
for(double s : pattern) {
patternLen += s;
}
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
GLint size;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
RgbaColor *textureData = new RgbaColor[size];
int mipCount = (int)log2(size) + 1;
for(int mip = 0; mip < mipCount; mip++) {
int dashI = 0;
double dashT = 0.0;
for(int i = 0; i < size; i++) {
if(pattern.size() == 0) {
textureData[i] = EncodeLengthAsFloat(0.0);
continue;
}
double t = (double)i / (double)(size - 1);
while(t - LENGTH_EPS > dashT + pattern[dashI] / patternLen) {
dashT += pattern[dashI] / patternLen;
dashI++;
}
double dashW = pattern[dashI] / patternLen;
if(dashI % 2 == 0) {
textureData[i] = EncodeLengthAsFloat(0.0);
} else {
double value;
if(t - dashT < pattern[dashI] / patternLen / 2.0) {
value = t - dashT;
} else {
value = dashT + dashW - t;
}
value = value * patternLen;
textureData[i] = EncodeLengthAsFloat(value);
}
}
glTexImage2D(GL_TEXTURE_2D, mip, GL_RGBA, size, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
textureData);
size /= 2;
}
delete []textureData;
return texture;
}
void StippleAtlas::Init() {
for(uint32_t i = 0; i <= (uint32_t)StipplePattern::LAST; i++) {
patterns.push_back(Generate(StipplePatternDashes((StipplePattern)i)));
}
}
void StippleAtlas::Clear() {
for(GLuint p : patterns) {
glDeleteTextures(1, &p);
}
}
GLint StippleAtlas::GetTexture(StipplePattern pattern) const {
return patterns[(uint32_t)pattern];
}
double StippleAtlas::GetLength(StipplePattern pattern) const {
if(pattern == StipplePattern::CONTINUOUS) {
return 1.0;
}
return StipplePatternLength(pattern);
}
//-----------------------------------------------------------------------------
// Edge rendering
//-----------------------------------------------------------------------------
void EdgeRenderer::Init(const StippleAtlas *a) {
atlas = a;
shader.Init(
"shaders/edge.vert", "shaders/edge.frag",
{
{ ATTRIB_POS, "pos" },
{ ATTRIB_LOC, "loc" },
{ ATTRIB_TAN, "tan" }
}
);
}
void EdgeRenderer::Clear() {
shader.Clear();
}
EdgeRenderer::Handle EdgeRenderer::Add(const SEdgeList &edges, bool dynamic) {
Handle handle;
glGenBuffers(1, &handle.vertexBuffer);
glGenBuffers(1, &handle.indexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle.indexBuffer);
EdgeVertex *vertices = new EdgeVertex[edges.l.n * 8];
uint32_t *indices = new uint32_t[edges.l.n * 6 * 3];
double phase = 0.0;
uint32_t curVertex = 0;
uint32_t curIndex = 0;
for(int i = 0; i < edges.l.n; i++) {
const SEdge &curr = edges.l.elem[i];
const SEdge &next = edges.l.elem[(i + 1) % edges.l.n];
// 3d positions
Vector3f a = Vector3f::From(curr.a);
Vector3f b = Vector3f::From(curr.b);
// tangent
Vector3f tan = Vector3f::From(curr.b.Minus(curr.a));
// length
double len = curr.b.Minus(curr.a).Magnitude();
// make line start cap
for(int j = 0; j < 2; j++) {
vertices[curVertex + j].pos = a;
vertices[curVertex + j].tan = tan;
}
vertices[curVertex + 0].loc = Vector3f::From(-1.0f, -1.0f, float(phase));
vertices[curVertex + 1].loc = Vector3f::From(-1.0f, +1.0f, float(phase));
indices[curIndex++] = curVertex + 0;
indices[curIndex++] = curVertex + 1;
indices[curIndex++] = curVertex + 2;
indices[curIndex++] = curVertex + 1;
indices[curIndex++] = curVertex + 2;
indices[curIndex++] = curVertex + 3;
curVertex += 2;
// make line body
vertices[curVertex + 0].pos = a;
vertices[curVertex + 1].pos = a;
vertices[curVertex + 2].pos = b;
vertices[curVertex + 3].pos = b;
for(int j = 0; j < 4; j++) {
vertices[curVertex + j].tan = tan;
}
vertices[curVertex + 0].loc = Vector3f::From( 0.0f, -1.0f, float(phase));
vertices[curVertex + 1].loc = Vector3f::From( 0.0f, +1.0f, float(phase));
vertices[curVertex + 2].loc = Vector3f::From( 0.0f, +1.0f, float(phase + len));
vertices[curVertex + 3].loc = Vector3f::From( 0.0f, -1.0f, float(phase + len));
indices[curIndex++] = curVertex + 0;
indices[curIndex++] = curVertex + 1;
indices[curIndex++] = curVertex + 2;
indices[curIndex++] = curVertex + 0;
indices[curIndex++] = curVertex + 2;
indices[curIndex++] = curVertex + 3;
curVertex += 4;
// make line end cap
for(int j = 0; j < 2; j++) {
vertices[curVertex + j].pos = b;
vertices[curVertex + j].tan = tan;
}
vertices[curVertex + 0].loc = Vector3f::From(+1.0, +1.0, float(phase + len));
vertices[curVertex + 1].loc = Vector3f::From(+1.0, -1.0, float(phase + len));
indices[curIndex++] = curVertex - 2;
indices[curIndex++] = curVertex - 1;
indices[curIndex++] = curVertex;
indices[curIndex++] = curVertex - 1;
indices[curIndex++] = curVertex;
indices[curIndex++] = curVertex + 1;
curVertex += 2;
// phase stitching
if(curr.a.EqualsExactly(next.a) ||
curr.a.EqualsExactly(next.b) ||
curr.b.EqualsExactly(next.a) ||
curr.b.EqualsExactly(next.b))
{
phase += len;
} else {
phase = 0.0;
}
}
handle.size = curIndex;
GLenum mode = dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW;
glBufferData(GL_ARRAY_BUFFER, curVertex * sizeof(EdgeVertex), vertices, mode);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, curIndex * sizeof(uint32_t), indices, mode);
delete []vertices;
delete []indices;
return handle;
}
void EdgeRenderer::Remove(const EdgeRenderer::Handle &handle) {
glDeleteBuffers(1, &handle.vertexBuffer);
glDeleteBuffers(1, &handle.indexBuffer);
}
void EdgeRenderer::Draw(const EdgeRenderer::Handle &handle) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, atlas->GetTexture(pattern));
shader.SetUniformTextureUnit("pattern", 1);
shader.SetUniformFloat("patternLen", (float)atlas->GetLength(pattern));
shader.Enable();
glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle.indexBuffer);
glEnableVertexAttribArray(ATTRIB_POS);
glEnableVertexAttribArray(ATTRIB_LOC);
glEnableVertexAttribArray(ATTRIB_TAN);
glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, sizeof(EdgeVertex), (void *)offsetof(EdgeVertex, pos));
glVertexAttribPointer(ATTRIB_LOC, 3, GL_FLOAT, GL_FALSE, sizeof(EdgeVertex), (void *)offsetof(EdgeVertex, loc));
glVertexAttribPointer(ATTRIB_TAN, 3, GL_FLOAT, GL_FALSE, sizeof(EdgeVertex), (void *)offsetof(EdgeVertex, tan));
glDrawElements(GL_TRIANGLES, handle.size, GL_UNSIGNED_INT, NULL);
glDisableVertexAttribArray(ATTRIB_POS);
glDisableVertexAttribArray(ATTRIB_LOC);
glDisableVertexAttribArray(ATTRIB_TAN);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
shader.Disable();
}
void EdgeRenderer::Draw(const SEdgeList &edges) {
Handle handle = Add(edges, /*dynamic=*/true);
Draw(handle);
Remove(handle);
}
void EdgeRenderer::SetModelview(double *matrix) {
shader.SetUniformMatrix("modelview", matrix);
}
void EdgeRenderer::SetProjection(double *matrix) {
shader.SetUniformMatrix("projection", matrix);
}
void EdgeRenderer::SetStroke(const Canvas::Stroke &stroke, double pixel) {
double unitScale = stroke.unit == Canvas::Unit::PX ? pixel : 1.0;
shader.SetUniformFloat("width", float(stroke.width * unitScale / 2.0));
shader.SetUniformColor("color", stroke.color);
shader.SetUniformFloat("patternScale", float(stroke.stippleScale * unitScale * 2.0));
shader.SetUniformFloat("pixel", (float)pixel);
pattern = stroke.stipplePattern;
}
//-----------------------------------------------------------------------------
// Outline rendering
//-----------------------------------------------------------------------------
void OutlineRenderer::Init(const StippleAtlas *a) {
atlas = a;
shader.Init(
"shaders/outline.vert", "shaders/edge.frag",
{
{ ATTRIB_POS, "pos" },
{ ATTRIB_LOC, "loc" },
{ ATTRIB_TAN, "tan" },
{ ATTRIB_NOL, "nol" },
{ ATTRIB_NOR, "nor" }
}
);
}
void OutlineRenderer::Clear() {
shader.Clear();
}
OutlineRenderer::Handle OutlineRenderer::Add(const SOutlineList &outlines, bool dynamic) {
Handle handle;
glGenBuffers(1, &handle.vertexBuffer);
glGenBuffers(1, &handle.indexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle.indexBuffer);
OutlineVertex *vertices = new OutlineVertex[outlines.l.n * 8];
uint32_t *indices = new uint32_t[outlines.l.n * 6 * 3];
double phase = 0.0;
uint32_t curVertex = 0;
uint32_t curIndex = 0;
for(int i = 0; i < outlines.l.n; i++) {
const SOutline &curr = outlines.l.elem[i];
const SOutline &next = outlines.l.elem[(i + 1) % outlines.l.n];
// 3d positions
Vector3f a = Vector3f::From(curr.a);
Vector3f b = Vector3f::From(curr.b);
Vector3f nl = Vector3f::From(curr.nl);
Vector3f nr = Vector3f::From(curr.nr);
// tangent
Vector3f tan = Vector3f::From(curr.b.Minus(curr.a));
// length
double len = curr.b.Minus(curr.a).Magnitude();
float tag = (float)curr.tag;
// make line start cap
for(int j = 0; j < 2; j++) {
vertices[curVertex + j].pos = a;
vertices[curVertex + j].nol = nl;
vertices[curVertex + j].nor = nr;
vertices[curVertex + j].tan = tan;
}
vertices[curVertex + 0].loc = Vector4f::From(-1.0f, -1.0f, float(phase), (float)tag);
vertices[curVertex + 1].loc = Vector4f::From(-1.0f, +1.0f, float(phase), (float)tag);
indices[curIndex++] = curVertex;
indices[curIndex++] = curVertex + 1;
indices[curIndex++] = curVertex + 2;
indices[curIndex++] = curVertex + 1;
indices[curIndex++] = curVertex + 2;
indices[curIndex++] = curVertex + 3;
curVertex += 2;
// make line body
vertices[curVertex + 0].pos = a;
vertices[curVertex + 1].pos = a;
vertices[curVertex + 2].pos = b;
vertices[curVertex + 3].pos = b;
for(int j = 0; j < 4; j++) {
vertices[curVertex + j].nol = nl;
vertices[curVertex + j].nor = nr;
vertices[curVertex + j].tan = tan;
}
vertices[curVertex + 0].loc = Vector4f::From( 0.0f, -1.0f, float(phase), (float)tag);
vertices[curVertex + 1].loc = Vector4f::From( 0.0f, +1.0f, float(phase), (float)tag);
vertices[curVertex + 2].loc = Vector4f::From( 0.0f, +1.0f,
float(phase + len), (float)tag);
vertices[curVertex + 3].loc = Vector4f::From( 0.0f, -1.0f,
float(phase + len), (float)tag);
indices[curIndex++] = curVertex + 0;
indices[curIndex++] = curVertex + 1;
indices[curIndex++] = curVertex + 2;
indices[curIndex++] = curVertex + 0;
indices[curIndex++] = curVertex + 2;
indices[curIndex++] = curVertex + 3;
curVertex += 4;
// make line end cap
for(int j = 0; j < 2; j++) {
vertices[curVertex + j].pos = b;
vertices[curVertex + j].nol = nl;
vertices[curVertex + j].nor = nr;
vertices[curVertex + j].tan = tan;
}
vertices[curVertex + 0].loc = Vector4f::From(+1.0f, +1.0f, float(phase + len), (float)tag);
vertices[curVertex + 1].loc = Vector4f::From(+1.0f, -1.0f, float(phase + len), (float)tag);
indices[curIndex++] = curVertex - 2;
indices[curIndex++] = curVertex - 1;
indices[curIndex++] = curVertex;
indices[curIndex++] = curVertex - 1;
indices[curIndex++] = curVertex;
indices[curIndex++] = curVertex + 1;
curVertex += 2;
// phase stitching
if(curr.a.EqualsExactly(next.a) ||
curr.a.EqualsExactly(next.b) ||
curr.b.EqualsExactly(next.a) ||
curr.b.EqualsExactly(next.b))
{
phase += len;
} else {
phase = 0.0;
}
}
handle.size = curIndex;
GLenum mode = dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW;
glBufferData(GL_ARRAY_BUFFER, curVertex * sizeof(OutlineVertex), vertices, mode);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, curIndex * sizeof(uint32_t), indices, mode);
delete []vertices;
delete []indices;
return handle;
}
void OutlineRenderer::Remove(const OutlineRenderer::Handle &handle) {
glDeleteBuffers(1, &handle.vertexBuffer);
glDeleteBuffers(1, &handle.indexBuffer);
}
void OutlineRenderer::Draw(const OutlineRenderer::Handle &handle, Canvas::DrawOutlinesAs mode) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, atlas->GetTexture(pattern));
shader.SetUniformTextureUnit("pattern", 1);
shader.SetUniformFloat("patternLen", (float)atlas->GetLength(pattern));
shader.SetUniformInt("mode", (GLint)mode);
shader.Enable();
glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle.indexBuffer);
glEnableVertexAttribArray(ATTRIB_POS);
glEnableVertexAttribArray(ATTRIB_LOC);
glEnableVertexAttribArray(ATTRIB_TAN);
glEnableVertexAttribArray(ATTRIB_NOL);
glEnableVertexAttribArray(ATTRIB_NOR);
glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, sizeof(OutlineVertex),
(void *)offsetof(OutlineVertex, pos));
glVertexAttribPointer(ATTRIB_LOC, 4, GL_FLOAT, GL_FALSE, sizeof(OutlineVertex),
(void *)offsetof(OutlineVertex, loc));
glVertexAttribPointer(ATTRIB_TAN, 3, GL_FLOAT, GL_FALSE, sizeof(OutlineVertex),
(void *)offsetof(OutlineVertex, tan));
glVertexAttribPointer(ATTRIB_NOL, 3, GL_FLOAT, GL_FALSE, sizeof(OutlineVertex),
(void *)offsetof(OutlineVertex, nol));
glVertexAttribPointer(ATTRIB_NOR, 3, GL_FLOAT, GL_FALSE, sizeof(OutlineVertex),
(void *)offsetof(OutlineVertex, nor));
glDrawElements(GL_TRIANGLES, handle.size, GL_UNSIGNED_INT, NULL);
glDisableVertexAttribArray(ATTRIB_POS);
glDisableVertexAttribArray(ATTRIB_LOC);
glDisableVertexAttribArray(ATTRIB_TAN);
glDisableVertexAttribArray(ATTRIB_NOL);
glDisableVertexAttribArray(ATTRIB_NOR);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
shader.Disable();
}
void OutlineRenderer::Draw(const SOutlineList &outlines, Canvas::DrawOutlinesAs drawAs) {
Handle handle = Add(outlines, /*dynamic=*/true);
Draw(handle, drawAs);
Remove(handle);
}
void OutlineRenderer::SetModelview(double *matrix) {
shader.SetUniformMatrix("modelview", matrix);
}
void OutlineRenderer::SetProjection(double *matrix) {
shader.SetUniformMatrix("projection", matrix);
}
void OutlineRenderer::SetStroke(const Canvas::Stroke &stroke, double pixel) {
double unitScale = (stroke.unit == Canvas::Unit::PX) ? pixel : 1.0;
shader.SetUniformFloat("width", (float)(stroke.width * unitScale / 2.0));
shader.SetUniformColor("color", stroke.color);
shader.SetUniformFloat("patternScale", (float)(stroke.stippleScale * unitScale * 2.0));
shader.SetUniformFloat("pixel", (float)pixel);
pattern = stroke.stipplePattern;
}
//-----------------------------------------------------------------------------
// Indexed mesh storage
//-----------------------------------------------------------------------------
void SIndexedMesh::AddPoint(const Vector &p) {
uint32_t vstart = vertices.size();
vertices.resize(vertices.size() + 4);
vertices[vstart + 0].pos = Vector3f::From(p);
vertices[vstart + 0].tex = Vector2f::From(-1.0f, -1.0f);
vertices[vstart + 1].pos = Vector3f::From(p);
vertices[vstart + 1].tex = Vector2f::From(+1.0f, -1.0f);
vertices[vstart + 2].pos = Vector3f::From(p);
vertices[vstart + 2].tex = Vector2f::From(+1.0f, +1.0f);
vertices[vstart + 3].pos = Vector3f::From(p);
vertices[vstart + 3].tex = Vector2f::From(-1.0f, +1.0f);
size_t istart = indices.size();
indices.resize(indices.size() + 6);
indices[istart + 0] = vstart + 0;
indices[istart + 1] = vstart + 1;
indices[istart + 2] = vstart + 2;
indices[istart + 3] = vstart + 0;
indices[istart + 4] = vstart + 2;
indices[istart + 5] = vstart + 3;
}
void SIndexedMesh::AddQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d) {
uint32_t vstart = vertices.size();
vertices.resize(vertices.size() + 4);
vertices[vstart + 0].pos = Vector3f::From(a);
vertices[vstart + 1].pos = Vector3f::From(b);
vertices[vstart + 2].pos = Vector3f::From(c);
vertices[vstart + 3].pos = Vector3f::From(d);
size_t istart = indices.size();
indices.resize(indices.size() + 6);
indices[istart + 0] = vstart + 0;
indices[istart + 1] = vstart + 1;
indices[istart + 2] = vstart + 2;
indices[istart + 3] = vstart + 0;
indices[istart + 4] = vstart + 2;
indices[istart + 5] = vstart + 3;
}
void SIndexedMesh::AddPixmap(const Vector &o, const Vector &u, const Vector &v,
const Point2d &ta, const Point2d &tb) {
uint32_t vstart = vertices.size();
vertices.resize(vertices.size() + 4);
vertices[vstart + 0].pos = Vector3f::From(o);
vertices[vstart + 0].tex = Vector2f::From(ta.x, ta.y);
vertices[vstart + 1].pos = Vector3f::From(o.Plus(v));
vertices[vstart + 1].tex = Vector2f::From(ta.x, tb.y);
vertices[vstart + 2].pos = Vector3f::From(o.Plus(u).Plus(v));
vertices[vstart + 2].tex = Vector2f::From(tb.x, tb.y);
vertices[vstart + 3].pos = Vector3f::From(o.Plus(u));
vertices[vstart + 3].tex = Vector2f::From(tb.x, ta.y);
size_t istart = indices.size();
indices.resize(indices.size() + 6);
indices[istart + 0] = vstart + 0;
indices[istart + 1] = vstart + 1;
indices[istart + 2] = vstart + 2;
indices[istart + 3] = vstart + 0;
indices[istart + 4] = vstart + 2;
indices[istart + 5] = vstart + 3;
}
void SIndexedMesh::Clear() {
vertices.clear();
indices.clear();
}
//-----------------------------------------------------------------------------
// Indexed mesh rendering
//-----------------------------------------------------------------------------
void IndexedMeshRenderer::Init() {
colShader.Init(
"shaders/imesh.vert", "shaders/imesh.frag",
{
{ ATTRIB_POS, "pos" },
{ ATTRIB_TEX, "tex" }
}
);
texShader.Init(
"shaders/imesh_tex.vert", "shaders/imesh_tex.frag",
{
{ ATTRIB_POS, "pos" },
{ ATTRIB_TEX, "tex" }
}
);
texaShader.Init(
"shaders/imesh_tex.vert", "shaders/imesh_texa.frag",
{
{ ATTRIB_POS, "pos" },
{ ATTRIB_TEX, "tex" }
}
);
pointShader.Init(
"shaders/imesh_point.vert", "shaders/imesh_point.frag",
{
{ ATTRIB_POS, "pos" },
{ ATTRIB_TEX, "loc" }
}
);
texShader.SetUniformTextureUnit("texture", 0);
texaShader.SetUniformTextureUnit("texture", 0);
selectedShader = &colShader;
}
void IndexedMeshRenderer::Clear() {
texShader.Clear();
texaShader.Clear();
colShader.Clear();
pointShader.Clear();
}
IndexedMeshRenderer::Handle IndexedMeshRenderer::Add(const SIndexedMesh &m, bool dynamic) {
Handle handle;
glGenBuffers(1, &handle.vertexBuffer);
glGenBuffers(1, &handle.indexBuffer);
GLenum mode = dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW;
glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, m.vertices.size() * sizeof(SIndexedMesh::Vertex),
m.vertices.data(), mode);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle.indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m.indices.size() * sizeof(uint32_t),
m.indices.data(), mode);
handle.size = m.indices.size();
return handle;
}
void IndexedMeshRenderer::Remove(const IndexedMeshRenderer::Handle &m) {
glDeleteBuffers(1, &m.vertexBuffer);
glDeleteBuffers(1, &m.indexBuffer);
}
void IndexedMeshRenderer::Draw(const IndexedMeshRenderer::Handle &m) {
selectedShader->Enable();
glBindBuffer(GL_ARRAY_BUFFER, m.vertexBuffer);
glEnableVertexAttribArray(ATTRIB_POS);
glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, sizeof(SIndexedMesh::Vertex),
(void *)offsetof(SIndexedMesh::Vertex, pos));
if(NeedsTexture()) {
glEnableVertexAttribArray(ATTRIB_TEX);
glVertexAttribPointer(ATTRIB_TEX, 2, GL_FLOAT, GL_FALSE, sizeof(SIndexedMesh::Vertex),
(void *)offsetof(SIndexedMesh::Vertex, tex));
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m.indexBuffer);
glDrawElements(GL_TRIANGLES, m.size, GL_UNSIGNED_INT, NULL);
glDisableVertexAttribArray(ATTRIB_POS);
if(NeedsTexture()) glDisableVertexAttribArray(ATTRIB_TEX);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
selectedShader->Disable();
}
void IndexedMeshRenderer::Draw(const SIndexedMesh &mesh) {
Handle handle = Add(mesh, /*dynamic=*/true) ;
Draw(handle);
Remove(handle);
}
bool IndexedMeshRenderer::NeedsTexture() const {
return selectedShader == &texShader ||
selectedShader == &texaShader ||
selectedShader == &pointShader;
}
void IndexedMeshRenderer::SetModelview(double *matrix) {
colShader.SetUniformMatrix("modelview", matrix);
texShader.SetUniformMatrix("modelview", matrix);
texaShader.SetUniformMatrix("modelview", matrix);
pointShader.SetUniformMatrix("modelview", matrix);
}
void IndexedMeshRenderer::SetProjection(double *matrix) {
colShader.SetUniformMatrix("projection", matrix);
texShader.SetUniformMatrix("projection", matrix);
texaShader.SetUniformMatrix("projection", matrix);
pointShader.SetUniformMatrix("projection", matrix);
}
void IndexedMeshRenderer::UseFilled(const Canvas::Fill &fill) {
if(fill.texture) {
selectedShader = (fill.texture->format == Pixmap::Format::A) ? &texaShader : &texShader;
} else {
selectedShader = &colShader;
}
selectedShader->SetUniformColor("color", fill.color);
}
void IndexedMeshRenderer::UsePoint(const Canvas::Stroke &stroke, double pixel) {
pointShader.SetUniformColor("color", stroke.color);
pointShader.SetUniformFloat("width", (float)(stroke.width * pixel / 2.0));
pointShader.SetUniformFloat("pixel", (float)pixel);
selectedShader = &pointShader;
}
}