Commit cc90231c authored by Simon Boyé's avatar Simon Boyé

Vitelotte: Started editor example + Renderer fixes and refactoring.

parent ca6dd287
......@@ -65,12 +65,10 @@ public:
explicit VGMesh(unsigned attributes = 0);
virtual ~VGMesh() {}
template < typename OtherScalar >
VGMesh(const VGMesh<OtherScalar, _Dim, _Chan>& other)
VGMesh(const Self& other)
{ operator=(other); }
template < typename OtherScalar >
VGMesh& operator=(const VGMesh<OtherScalar, _Dim, _Chan>& rhs);
VGMesh& operator=(const Self& rhs);
unsigned getAttributes() const { return m_attributes; }
void setAttributes(unsigned attributes);
......
......@@ -23,10 +23,8 @@ VGMesh<_Scalar, _Dim, _Chan>::VGMesh(unsigned attributes)
template < typename _Scalar, int _Dim, int _Chan >
template < typename OtherScalar >
VGMesh<_Scalar, _Dim, _Chan>&
VGMesh<_Scalar, _Dim, _Chan>::operator=(
const VGMesh<OtherScalar, _Dim, _Chan>& rhs)
VGMesh<_Scalar, _Dim, _Chan>::operator=(const Self& rhs)
{
if(&rhs != this)
{
......@@ -35,12 +33,16 @@ VGMesh<_Scalar, _Dim, _Chan>::operator=(
m_nodes = rhs.m_nodes;
// TODO: Working copy constructor
// m_vPos = vertexProperty<Vector>("v:position");
m_positions = vertexProperty<Vector>("v:position");
// m_hFromNode = halfedgeProperty<Node>("h:fromNode");
// m_hToNode = halfedgeProperty<Node>("h:toNode");
// m_hMidNode = halfedgeProperty<Node>("h:midNode");
m_attributes = rhs.m_attributes;
m_vertexValueNodes = getHalfedgeProperty<Node>("h:vertexValueNode");
m_vertexFromValueNodes = getHalfedgeProperty<Node>("h:vertexFromValueNode");
m_edgeValueNodes = getHalfedgeProperty<Node>("h:edgeValueNode");
m_edgeGradientNodes = getHalfedgeProperty<Node>("h:edgeGradientNode");
m_edgeConstraintFlag = getEdgeProperty<bool>("e:constraintFlag");
}
return *this;
}
......@@ -98,7 +100,7 @@ template < typename _Scalar, int _Dim, int _Chan >
void
VGMesh<_Scalar, _Dim, _Chan>::compactNodes()
{
std::vector<Node> buf(nNodes(), 0);
std::vector<int> buf(nNodes(), 0);
// Find used node ids
HalfedgeIterator hBegin = halfedgesBegin(),
......@@ -107,22 +109,22 @@ VGMesh<_Scalar, _Dim, _Chan>::compactNodes()
{
if(!isBoundary(*hit))
{
if(hasVertexValue())
buf[vertexValueNode(*hit)] = 1;
if(hasVertexFromValue())
buf[vertexFromValueNode(*hit)] = 1;
if(hasEdgeValue())
buf[edgeValueNode(*hit)] = 1;
if(hasVertexValue() && vertexValueNode(*hit).isValid())
buf[vertexValueNode(*hit).idx()] = 1;
if(hasVertexFromValue() && vertexFromValueNode(*hit).isValid())
buf[vertexFromValueNode(*hit).idx()] = 1;
if(hasEdgeValue() && edgeValueNode(*hit).isValid())
buf[edgeValueNode(*hit).idx()] = 1;
// if(hasVertexGradient())
// marked[vertexGradientNode(*hit)] = 1;
if(hasEdgeGradient())
buf[edgeGradientNode(*hit)] = 1;
if(hasEdgeGradient() && edgeGradientNode(*hit).isValid())
buf[edgeGradientNode(*hit).idx()] = 1;
}
}
// Compute remapping
Node size=0;
for(Node i = 0; i < buf.size(); ++i)
int size=0;
for(int i = 0; i < buf.size(); ++i)
{
if(buf[i])
{
......@@ -132,11 +134,11 @@ VGMesh<_Scalar, _Dim, _Chan>::compactNodes()
}
// Update node vector and fill remapping vector
std::vector<Node> map(nNodes(), Node());
std::vector<int> map(nNodes(), -1);
NodeVector reord(size);
for(Node i = 0; i < size; ++i)
for(int i = 0; i < size; ++i)
{
reord[i] = nodeValue(buf[i]);
reord[i] = nodeValue(Node(buf[i]));
map[buf[i]] = i;
}
m_nodes.swap(reord);
......@@ -146,16 +148,16 @@ VGMesh<_Scalar, _Dim, _Chan>::compactNodes()
{
if(!isBoundary(*hit))
{
if(hasVertexValue())
vertexValueNode(*hit) = map[vertexValueNode(*hit)];
if(hasVertexFromValue())
vertexFromValueNode(*hit) = map[vertexFromValueNode(*hit)];
if(hasEdgeValue())
(edgeValueNode(*hit)) = map[edgeValueNode(*hit)];
if(hasVertexValue() && vertexValueNode(*hit).isValid())
vertexValueNode(*hit) = Node(map[vertexValueNode(*hit).idx()]);
if(hasVertexFromValue() && vertexFromValueNode(*hit).isValid())
vertexFromValueNode(*hit) = Node(map[vertexFromValueNode(*hit).idx()]);
if(hasEdgeValue() && edgeValueNode(*hit).isValid())
(edgeValueNode(*hit)) = Node(map[edgeValueNode(*hit).idx()]);
// if(hasVertexGradient())
// vertexGradientNode(*hit) = map[vertexGradientNode(*hit)];
if(hasEdgeGradient())
edgeGradientNode(*hit) = map[edgeGradientNode(*hit)];
if(hasEdgeGradient() && edgeGradientNode(*hit).isValid())
edgeGradientNode(*hit) = Node(map[edgeGradientNode(*hit).idx()]);
}
}
}
......@@ -304,13 +306,10 @@ VGMesh<_Scalar, _Dim, _Chan>::finalize()
}
consEdges.push_back(consEdges.front());
while(*hit != consEdges.front()) ++hit;
std::vector<Halfedge>::iterator cit = consEdges.begin();
Halfedge prev = *cit;
for(++cit; cit != consEdges.end(); ++cit)
{
assert(prev == *hit);
Halfedge next = oppositeHalfedge(*cit);
Node n0 = hasVertexFromValue()?
vertexFromValueNode(prev):
......
......@@ -4,6 +4,8 @@ uniform float zoom;
uniform float pointRadius;
uniform float halfLineWidth;
uniform bool showWireframe;
uniform vec4 wireframeColor;
uniform vec4 pointColor;
in vec3 linearBasis;
in vec2 position;
......@@ -70,14 +72,14 @@ vec4 colorWithBordersAndPoints(in vec4 colorNodes[6])
if(showWireframe)
{
color = mix(color, vec4(0., 0., 0., 1.),
interpFactor(edgeDist[closestEdge], halfLineWidth+.5));
color = mix(color, colorNodes[closestEdge + 3]*.5,
interpFactor(edgeDist[closestEdge], halfLineWidth));
color = mix(color, vec4(0., 0., 0., 1.),
interpFactor(sqrt(vertexSqrDist[closestVx]), pointRadius+.5));
color = mix(color, colorNodes[closestVx],
interpFactor(sqrt(vertexSqrDist[closestVx]), pointRadius));
color = mix(color, wireframeColor,
interpFactor(edgeDist[closestEdge], halfLineWidth));
// color = mix(color, colorNodes[closestEdge + 3]*.5,
// interpFactor(edgeDist[closestEdge], halfLineWidth));
color = mix(color, pointColor,
interpFactor(sqrt(vertexSqrDist[closestVx]), pointRadius));
// color = mix(color, colorNodes[closestVx],
// interpFactor(sqrt(vertexSqrDist[closestVx]), pointRadius));
}
return color;
......
......@@ -115,14 +115,17 @@ MVGReader<_Mesh>::parseDefinition(const std::string& spec,
{
def >> m_tmp;
parseIndiceList(m_tmp, m_faceIndices);
if(m_faceIndices.size() < 2 || m_faceIndices.size() > 3)
if(m_faceIndices.size() < 1 || m_faceIndices.size() > 3)
error("Invalid number of indices");
m_fVertices.push_back(
typename Mesh::Vertex(m_faceIndices[0] - iOffset));
nodes[2*i + 0] = m_faceIndices[1];
nodes[2*i + 1] = m_faceIndices.back();
if(m_faceIndices.size() > 1)
{
nodes[2*i + 0] = m_faceIndices[1];
nodes[2*i + 1] = m_faceIndices.back();
}
}
// mid nodes
......
// Generated by shader2cpp.
// 2014-10-27T10:59:05.027036
// 2014-11-06T10:09:37.198800
namespace Vitelotte
{
namespace shader
{
static const char* frag_common_glsl =
"#version 410 core\n"
......@@ -8,6 +14,8 @@ static const char* frag_common_glsl =
"uniform float pointRadius;\n"
"uniform float halfLineWidth;\n"
"uniform bool showWireframe;\n"
"uniform vec4 wireframeColor;\n"
"uniform vec4 pointColor;\n"
"\n"
"in vec3 linearBasis;\n"
"in vec2 position;\n"
......@@ -74,14 +82,14 @@ static const char* frag_common_glsl =
"\n"
" if(showWireframe)\n"
" {\n"
" color = mix(color, vec4(0., 0., 0., 1.),\n"
" interpFactor(edgeDist[closestEdge], halfLineWidth+.5));\n"
" color = mix(color, colorNodes[closestEdge + 3]*.5,\n"
" interpFactor(edgeDist[closestEdge], halfLineWidth));\n"
" color = mix(color, vec4(0., 0., 0., 1.),\n"
" interpFactor(sqrt(vertexSqrDist[closestVx]), pointRadius+.5));\n"
" color = mix(color, colorNodes[closestVx],\n"
" interpFactor(sqrt(vertexSqrDist[closestVx]), pointRadius));\n"
" color = mix(color, wireframeColor,\n"
" interpFactor(edgeDist[closestEdge], halfLineWidth));\n"
"// color = mix(color, colorNodes[closestEdge + 3]*.5,\n"
"// interpFactor(edgeDist[closestEdge], halfLineWidth));\n"
" color = mix(color, pointColor,\n"
" interpFactor(sqrt(vertexSqrDist[closestVx]), pointRadius));\n"
"// color = mix(color, colorNodes[closestVx],\n"
"// interpFactor(sqrt(vertexSqrDist[closestVx]), pointRadius));\n"
" }\n"
"\n"
" return color;\n"
......@@ -227,3 +235,6 @@ static const char* vert_common_glsl =
"}\n"
"";
}
}
......@@ -2,12 +2,95 @@
#define _VG_MESH_RENDERER_BASE_H_
#include <Eigen/Dense>
#include "../../common/gl_utils/shader.h"
#include "shaders.hpp"
namespace Vitelotte {
#include "shaders.hpp"
inline Eigen::Vector4f linearToSrgb(const Eigen::Vector4f& linear);
inline Eigen::Vector4f srgbToLinear(const Eigen::Vector4f& srgb);
class VGMeshRendererShaders
{
public:
inline VGMeshRendererShaders() {}
virtual GLuint triangleShader() = 0;
virtual GLuint singularShader() = 0;
private:
VGMeshRendererShaders(const VGMeshRendererShaders&);
};
class VGMeshRendererDefaultShaders : public VGMeshRendererShaders
{
public:
inline VGMeshRendererDefaultShaders();
virtual inline ~VGMeshRendererDefaultShaders() {}
virtual inline GLuint triangleShader();
virtual inline GLuint singularShader();
inline Eigen::Matrix4f& viewMatrix() { return m_viewMatrix; }
inline const Eigen::Matrix4f& viewMatrix() const { return m_viewMatrix; }
inline bool& showWireframe() { return m_showWireframe; }
inline const bool& showWireframe() const { return m_showWireframe; }
inline float& zoom() { return m_zoom; }
inline const float& zoom() const { return m_zoom; }
inline float& pointRadius() { return m_pointRadius; }
inline const float& pointRadius() const { return m_pointRadius; }
inline float& lineWidth() { return m_lineWidth; }
inline const float& lineWidth() const { return m_lineWidth; }
inline Eigen::Vector4f& wireframeColor() { return m_wireframeColor; }
inline const Eigen::Vector4f& wireframeColor() const { return m_wireframeColor; }
inline Eigen::Vector4f& pointColor() { return m_pointColor; }
inline const Eigen::Vector4f& pointColor() const { return m_pointColor; }
private:
struct Uniforms
{
GLint viewMatrixLoc;
GLint showWireframeLoc;
GLint zoomLoc;
GLint pointRadiusLoc;
GLint halfLineWidthLoc;
GLint wireframeColorLoc;
GLint pointColorLoc;
};
inline void getUniforms(Patate::Shader& shader, Uniforms& uniforms);
inline void setupUniforms(const Uniforms& uniforms);
private:
Patate::Shader m_triangleShader;
Patate::Shader m_singularShader;
Uniforms m_triangleUniforms;
Uniforms m_singularUniforms;
Eigen::Matrix4f m_viewMatrix;
bool m_showWireframe;
float m_zoom;
float m_pointRadius;
float m_lineWidth;
Eigen::Vector4f m_wireframeColor;
Eigen::Vector4f m_pointColor;
};
template < class _Mesh >
class VGMeshRenderer
......@@ -24,20 +107,13 @@ public:
m_verticesBuffer(0), m_triangleIndicesBuffer(0),
m_singularIndicesBuffer(0), m_triangleNodesBuffer(0),
m_singularNodesBuffer(0), m_triangleNodesTexture(0),
m_singularNodesTexture(0), m_pTriangleProgram(0),
m_pSingularProgram(0), m_vao(0), m_pMesh(0)
m_singularNodesTexture(0), m_vao(0), m_pMesh(0)
{}
~VGMeshRenderer()
{
PATATE_SAFE_DELETE(m_pSingularProgram);
PATATE_SAFE_DELETE(m_pTriangleProgram);
}
~VGMeshRenderer() {}
void render(Eigen::Matrix4f& _viewMatrix, float _zoom = 1.f,
float _pointRadius = 2.f, float _lineWidth = 1.f,
bool _showShaderWireframe = false);
bool init(Mesh* _mesh);
void initialize(Mesh* _mesh=0);
void render(VGMeshRendererShaders& shaders);
inline void setMesh(Mesh* _mesh)
{
......@@ -53,8 +129,6 @@ private:
typedef std::vector<NodeValue> NodesVector;
private:
bool loadShaders();
bool initGl();
void renderTriangles(GLuint _shader, bool _singular = false);
NodeValue nodeValue(Node node) const;
......@@ -77,9 +151,6 @@ private:
Mesh* m_pMesh;
Patate::Shader* m_pTriangleProgram;
Patate::Shader* m_pSingularProgram;
VectorsVector m_vertices;
IndicesVector m_triangleIndices;
......
......@@ -5,26 +5,165 @@ namespace Vitelotte
{
template < class _Mesh >
inline bool VGMeshRenderer<_Mesh>::init(Mesh* _mesh)
Eigen::Vector4f linearToSrgb(const Eigen::Vector4f& linear)
{
Eigen::Vector4f srgb = linear;
for(int i=0; i<3; ++i)
srgb(i) = linear(i) > 0.0031308?
1.055 * std::pow(linear(i), 1/2.4):
12.92 * linear(i);
return srgb;
}
Eigen::Vector4f srgbToLinear(const Eigen::Vector4f& srgb)
{
Eigen::Vector4f linear = srgb;
for(int i=0; i<3; ++i)
linear(i) = linear(i) > 0.04045?
std::pow((linear(i)+0.055) / 1.055, 2.4):
linear(i) / 12.92;
return linear;
}
VGMeshRendererDefaultShaders::VGMeshRendererDefaultShaders()
: m_viewMatrix(Eigen::Matrix4f::Identity()),
m_showWireframe(false),
m_zoom(1),
m_pointRadius(0),
m_lineWidth(1)
{
if(!loadShaders())
}
GLuint VGMeshRendererDefaultShaders::triangleShader()
{
if(m_triangleShader.status() == Patate::Shader::Uninitialized)
{
return false;
m_triangleShader.create();
bool bRes = true;
bRes &= bRes && m_triangleShader.addShader(GL_VERTEX_SHADER,
shader::vert_common_glsl);
bRes &= bRes && m_triangleShader.addShader(GL_GEOMETRY_SHADER,
shader::geom_common_glsl);
bRes &= bRes && m_triangleShader.addShader(GL_FRAGMENT_SHADER,
shader::frag_common_glsl);
bRes &= bRes && m_triangleShader.addShader(GL_FRAGMENT_SHADER,
shader::frag_triangle_glsl);
bRes &= bRes && m_triangleShader.finalize();
if(bRes)
getUniforms(m_triangleShader, m_triangleUniforms);
}
if(!initGl())
return false;
bool ok = m_triangleShader.status() == Patate::Shader::CompilationSuccessful;
if(ok)
{
m_triangleShader.use();
setupUniforms(m_triangleUniforms);
}
setMesh(_mesh);
return ok? m_triangleShader.getShaderId(): 0;
}
GLuint VGMeshRendererDefaultShaders::singularShader()
{
if(m_singularShader.status() == Patate::Shader::Uninitialized)
{
m_singularShader.create();
bool bRes = true;
bRes &= bRes && m_singularShader.addShader(GL_VERTEX_SHADER,
shader::vert_common_glsl);
bRes &= bRes && m_singularShader.addShader(GL_GEOMETRY_SHADER,
shader::geom_common_glsl);
bRes &= bRes && m_singularShader.addShader(GL_FRAGMENT_SHADER,
shader::frag_common_glsl);
bRes &= bRes && m_singularShader.addShader(GL_FRAGMENT_SHADER,
shader::frag_singular_glsl);
bRes &= bRes && m_singularShader.finalize();
if(bRes)
getUniforms(m_singularShader, m_triangleUniforms);
}
bool ok = m_singularShader.status() == Patate::Shader::CompilationSuccessful;
if(ok)
{
m_singularShader.use();
setupUniforms(m_triangleUniforms);
}
return ok? m_singularShader.getShaderId(): 0;
}
void
VGMeshRendererDefaultShaders::
getUniforms(Patate::Shader& shader, Uniforms& uniforms)
{
uniforms.viewMatrixLoc =
shader.getUniformLocation("viewMatrix");
uniforms.showWireframeLoc =
shader.getUniformLocation("showWireframe");
uniforms.zoomLoc =
shader.getUniformLocation("zoom");
uniforms.pointRadiusLoc =
shader.getUniformLocation("pointRadius");
uniforms.halfLineWidthLoc =
shader.getUniformLocation("halfLineWidth");
uniforms.wireframeColorLoc =
shader.getUniformLocation("wireframeColor");
uniforms.pointColorLoc =
shader.getUniformLocation("pointColor");
}
void
VGMeshRendererDefaultShaders::
setupUniforms(const Uniforms& uniforms)
{
if(uniforms.viewMatrixLoc >= 0)
glUniformMatrix4fv(uniforms.viewMatrixLoc, 1, false, m_viewMatrix.data());
if(uniforms.showWireframeLoc >= 0)
glUniform1i(uniforms.showWireframeLoc, m_showWireframe);
if(uniforms.zoomLoc >= 0)
glUniform1f(uniforms.zoomLoc, m_zoom);
if(uniforms.pointRadiusLoc >= 0)
glUniform1f(uniforms.pointRadiusLoc, m_pointRadius);
if(uniforms.halfLineWidthLoc >= 0)
glUniform1f(uniforms.halfLineWidthLoc, m_lineWidth / 2.f);
if(uniforms.wireframeColorLoc >= 0)
glUniform4fv(uniforms.wireframeColorLoc, 1, m_wireframeColor.data());
if(uniforms.pointColorLoc >= 0)
glUniform4fv(uniforms.pointColorLoc, 1, m_pointColor.data());
}
template < class _Mesh >
inline void VGMeshRenderer<_Mesh>::initialize(Mesh* _mesh)
{
glGenVertexArrays(1, &m_vao);
return true;
setMesh(_mesh);
}
template < class _Mesh >
inline void VGMeshRenderer<_Mesh>::updateMesh()
{
assert(m_pMesh->getAttributes() & Mesh::Quadratic == Mesh::Quadratic);
m_vertices.clear();
m_triangleIndices.clear();
......@@ -33,6 +172,7 @@ inline void VGMeshRenderer<_Mesh>::updateMesh()
m_singularNodes.clear();
if(!m_pMesh) return;
assert(m_pMesh->getAttributes() & Mesh::Quadratic == Mesh::Quadratic);
// Compute number of singular and normal triangles
unsigned nSingulars = m_pMesh->nSingularFaces();
......@@ -132,55 +272,6 @@ inline void VGMeshRenderer<_Mesh>::updateMesh()
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_singularNodesBuffer);
}
template < class _Mesh >
inline bool VGMeshRenderer<_Mesh>::loadShaders()
{
PATATE_GLCheckError();
m_pTriangleProgram = new Patate::Shader();
if(!m_pTriangleProgram->Init())
{
return false;
}
bool bRes = true;
bRes &= m_pTriangleProgram->AddShader(GL_VERTEX_SHADER, vert_common_glsl);
bRes &= m_pTriangleProgram->AddShader(GL_GEOMETRY_SHADER, geom_common_glsl);
bRes &= m_pTriangleProgram->AddShader(GL_FRAGMENT_SHADER, frag_common_glsl);
bRes &= m_pTriangleProgram->AddShader(GL_FRAGMENT_SHADER, frag_triangle_glsl);
assert(bRes);
bRes &= m_pTriangleProgram->Finalize();
assert(bRes);
m_pSingularProgram = new Patate::Shader();
if(!m_pSingularProgram->Init())
{
return false;
}
bRes &= m_pSingularProgram->AddShader(GL_VERTEX_SHADER, vert_common_glsl);
bRes &= m_pSingularProgram->AddShader(GL_GEOMETRY_SHADER, geom_common_glsl);
bRes &= m_pSingularProgram->AddShader(GL_FRAGMENT_SHADER, frag_common_glsl);
bRes &= m_pSingularProgram->AddShader(GL_FRAGMENT_SHADER, frag_singular_glsl);
assert(bRes);
bRes &= m_pSingularProgram->Finalize();
assert(bRes);
return PATATE_GLCheckError();;
}
template < class _Mesh >
inline bool VGMeshRenderer<_Mesh>::initGl()
{
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
return true;
}
template < class _Mesh >
inline void VGMeshRenderer<_Mesh>::renderTriangles(GLuint _shader, bool _singular)
{
......@@ -192,6 +283,8 @@ inline void VGMeshRenderer<_Mesh>::renderTriangles(GLuint _shader, bool _singula
return;
}
glBindVertexArray(m_vao);
glUseProgram(_shader);
GLint verticesLoc = glGetAttribLocation(_shader, "vx_position");
......@@ -224,61 +317,23 @@ inline typename VGMeshRenderer<_Mesh>::NodeValue
VGMeshRenderer<_Mesh>::nodeValue(Node node) const
{
if(m_pMesh->isValid(node) && m_pMesh->isConstraint(node))
return m_pMesh->nodeValue(node);
return srgbToLinear(m_pMesh->nodeValue(node));
return NodeValue(0, 0, 0, 1); // FIXME: Make this class work for Chan != 4
}
template < class _Mesh >
inline void VGMeshRenderer<_Mesh>::render(Eigen::Matrix4f& _viewMatrix, float _zoom, float _pointRadius, float _lineWidth, bool _showShaderWireframe)
inline void VGMeshRenderer<_Mesh>::render(VGMeshRendererShaders &shaders)
{
for(int pass = 0; pass < 2; ++pass)
GLuint shaderIds[2] =
{
Patate::Shader* program = (pass == 0) ? m_pTriangleProgram : m_pSingularProgram;
if(program)
{
program->Enable();
GLuint viewMatrixLoc = program->GetUniformLocation("viewMatrix");
if(viewMatrixLoc >= 0)
{
glUniformMatrix4fv(viewMatrixLoc, 1, false, _viewMatrix.data());
}
shaders.triangleShader(),
shaders.singularShader()
};
GLint wireLoc = program->GetUniformLocation("showWireframe");
if(wireLoc >=0 )
{
glUniform1i(wireLoc, _showShaderWireframe);
}
GLuint zoomLoc = program->GetUniformLocation("zoom");
if(zoomLoc >= 0)
{
glUniform1f(zoomLoc, _zoom);
}
GLuint pointRadiutLoc = program->GetUniformLocation("pointRadius");
if(pointRadiutLoc >= 0)
{
glUniform1f(pointRadiutLoc, _pointRadius);
}
GLuint halfLineWidthLoc = program->GetUniformLocation("halfLineWidth");
if(halfLineWidthLoc >= 0)
{
glUniform1f(halfLineWidthLoc, _lineWidth / 2.f);
}
if(pass == 0)
{
renderTriangles(program->GetShaderId());
}
else
{
renderTriangles(program->GetShaderId(), true);
}
}
for(int pass = 0; pass < 2; ++pass)
{