Files
vimix/src/Primitives.cpp
Bruno Herbelin fde6be3f97 BugFix and code cleanup
Fixed rendering of Mesh by using new TextureShader (instead of ImageShader which is dedicated to square Surfaces). Cleanup includes and code layout.
2023-12-26 23:08:09 +01:00

963 lines
28 KiB
C++

/*
* This file is part of vimix - video live mixer
*
* **Copyright** (C) 2019-2023 Bruno Herbelin <bruno.herbelin@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
**/
#include <glad/glad.h>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_access.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/vector_angle.hpp>
#include <glm/gtc/constants.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/rotate_vector.hpp>
#include "Resource.h"
#include "FrameBuffer.h"
#include "Visitor.h"
#include "Primitives.h"
#define MESH_SURFACE_DENSITY 32
Surface::Surface(Shader *s) : Primitive(s), textureindex_(0), mirror_(true)
{
// geometry for a trianglulated simple rectangle surface with UV
// (0,0) B +---+ D (1,0)
// |\ |
// | \ |
// | \|
// (0,1) A +---+ C (1,1)
points_ = std::vector<glm::vec3> { glm::vec3( -1.f, -1.f, 0.f ), glm::vec3( -1.f, 1.f, 0.f ),
glm::vec3( 1.f, -1.f, 0.f ), glm::vec3( 1.f, 1.f, 0.f ) };
colors_ = std::vector<glm::vec4> { glm::vec4( 1.f, 1.f, 1.f , 1.f ), glm::vec4( 1.f, 1.f, 1.f, 1.f ),
glm::vec4( 1.f, 1.f, 1.f, 1.f ), glm::vec4( 1.f, 1.f, 1.f, 1.f ) };
texCoords_ = std::vector<glm::vec2> { glm::vec2( 0.f, 1.f ), glm::vec2( 0.f, 0.f ),
glm::vec2( 1.f, 1.f ), glm::vec2( 1.f, 0.f ) };
indices_ = std::vector<uint> { 0, 1, 2, 3 };
drawMode_ = GL_TRIANGLE_STRIP;
}
Surface::~Surface()
{
// do NOT delete vao_ (unique)
vao_ = 0;
}
void Surface::init()
{
// use static unique vertex array object
static uint unique_vao_ = 0;
static uint unique_drawCount = 0;
if (unique_vao_) {
// 1. only init Node (not the primitive vao)
Node::init();
// 2. use the global vertex array object
vao_ = unique_vao_;
drawCount_ = unique_drawCount;
// compute AxisAlignedBoundingBox
bbox_.extend(points_);
// arrays of vertices are not needed anymore
points_.clear();
colors_.clear();
texCoords_.clear();
indices_.clear();
}
else {
// 1. init the Primitive (only once)
Primitive::init();
// 2. remember global vertex array object
unique_vao_ = vao_;
unique_drawCount = drawCount_;
// 3. unique_vao_ will NOT be deleted
}
}
void Surface::accept(Visitor& v)
{
Primitive::accept(v);
v.visit(*this);
}
void Surface::draw(glm::mat4 modelview, glm::mat4 projection)
{
if ( !initialized() )
init();
glActiveTexture(GL_TEXTURE0);
if ( textureindex_ ) {
glBindTexture(GL_TEXTURE_2D, textureindex_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mirror_ ? GL_MIRRORED_REPEAT : GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mirror_ ? GL_MIRRORED_REPEAT : GL_REPEAT);
}
else
glBindTexture(GL_TEXTURE_2D, Resource::getTextureBlack());
Primitive::draw(modelview, projection);
glBindTexture(GL_TEXTURE_2D, 0);
}
MeshSurface::MeshSurface(Shader *s) : Surface(s)
{
}
void MeshSurface::generate_mesh(size_t rows, size_t columns)
{
// Set up vertices
points_.resize(rows * columns);
texCoords_.resize(rows * columns);
colors_.resize(rows * columns);
for (size_t c = 0; c < columns; ++c) {
for (size_t r = 0; r < rows; ++r) {
size_t index = c * columns + r;
points_[index] = glm::vec3(-1.f, -1.f, 0.f)
+ 2.f * glm::vec3((float) c / (float) (columns - 1),
(float) r / (float) (rows - 1),
0.f);
texCoords_[index] = glm::vec2(0.f, 1.f)
+ glm::vec2((float) c / (float) (columns - 1),
-1.f * (float) r / (float) (rows - 1));
colors_[index] = glm::vec4(1.f, 1.f, 1.f, 1.f);
}
}
// Set up indices
indices_.resize( (rows * 2 + 2) * (columns - 1) - 2 );
size_t i = 0;
size_t height = columns - 1;
for (size_t y = 0; y < height; y++) {
size_t base = y * rows;
//indices[i++] = (uint16)base;
for (size_t x = 0; x < rows; x++) {
indices_[i++] = (base + x);
indices_[i++] = (base + rows + x);
}
// add a degenerate triangle (except in a last row)
if (y < height - 1) {
indices_[i++] = ((y + 1) * rows + (rows - 1));
indices_[i++] = ((y + 1) * rows);
}
}
drawMode_ = GL_TRIANGLE_STRIP;
// g_printerr("generated_mesh %lu x %lu :\n", rows, columns);
// g_printerr("%lu points\n", points_.size());
// for(size_t ind=0; ind < points_.size(); ind++) {
// g_printerr("point[%lu] : %f, %f, %f \n", ind, points_[ind].x, points_[ind].y, points_[ind].z);
// g_printerr(" : %f, %f \n", texCoords_[ind].x, texCoords_[ind].y);
// }
// g_printerr("%lu indices\n", indices_.size());
// for(size_t ind=0; ind < indices_.size(); ind++) {
// g_printerr("index[%lu] = %u \n", ind, indices_[ind]);
// }
}
void MeshSurface::init()
{
// use static unique vertex array object
static uint unique_vao_ = 0;
static uint unique_drawCount = 0;
if (unique_vao_) {
// 1. only init Node (not the primitive vao)
Node::init();
// 2. use the global vertex array object
vao_ = unique_vao_;
drawCount_ = unique_drawCount;
// compute AxisAlignedBoundingBox
bbox_.extend(points_);
// arrays of vertices are not needed anymore
points_.clear();
colors_.clear();
texCoords_.clear();
indices_.clear();
}
else {
// 0. generate mesh
generate_mesh(MESH_SURFACE_DENSITY, MESH_SURFACE_DENSITY);
// 1. init the Primitive (only once)
Primitive::init();
// 2. remember global vertex array object
unique_vao_ = vao_;
unique_drawCount = drawCount_;
// 3. unique_vao_ will NOT be deleted
}
}
ImageSurface::ImageSurface(const std::string& path, Shader *s) : Surface(s), resource_(path)
{
}
void ImageSurface::init()
{
Surface::init();
// load image if specified (should always be the case)
if ( !resource_.empty()) {
textureindex_ = Resource::getTextureImage(resource_);
}
}
void ImageSurface::accept(Visitor& v)
{
Surface::accept(v);
v.visit(*this);
}
FrameBufferSurface::FrameBufferSurface(FrameBuffer *fb, Shader *s) : Surface(s), frame_buffer_(fb)
{
}
void FrameBufferSurface::draw(glm::mat4 modelview, glm::mat4 projection)
{
if ( !initialized() )
init();
glBindTexture(GL_TEXTURE_2D, frame_buffer_->texture());
Primitive::draw(modelview, projection);
glBindTexture(GL_TEXTURE_2D, 0);
}
void FrameBufferSurface::accept(Visitor& v)
{
Surface::accept(v);
v.visit(*this);
}
FrameBufferMeshSurface::FrameBufferMeshSurface(FrameBuffer *fb, Shader *s) : MeshSurface(s), frame_buffer_(fb)
{
}
void FrameBufferMeshSurface::draw(glm::mat4 modelview, glm::mat4 projection)
{
if ( !initialized() )
init();
glBindTexture(GL_TEXTURE_2D, frame_buffer_->texture());
Primitive::draw(modelview, projection);
glBindTexture(GL_TEXTURE_2D, 0);
}
void FrameBufferMeshSurface::accept(Visitor& v)
{
Surface::accept(v);
v.visit(*this);
}
Points::Points(std::vector<glm::vec3> points, glm::vec4 color, uint pointsize) : Primitive(new Shader)
{
for(size_t i = 0; i < points.size(); ++i)
{
points_.push_back( points[i] );
colors_.push_back( color );
indices_.push_back ( i );
}
drawMode_ = GL_POINTS; // TODO implement drawing of points as Mesh
pointsize_ = pointsize;
}
void Points::draw(glm::mat4 modelview, glm::mat4 projection)
{
if ( !initialized() )
init();
glPointSize(pointsize_);
Primitive::draw(modelview, projection);
glPointSize(1);
}
void Points::accept(Visitor& v)
{
Primitive::accept(v);
v.visit(*this);
}
HLine::HLine(float linewidth, Shader *s): Primitive(s), width(linewidth)
{
// 1 3
// +-------+ ^
// / | / | \ |
// +-----+ => 0 + | / | + 5 | linewidth
// -1 1 \ | / | / |
// +-------+ v
// 2 4
//
points_ = std::vector<glm::vec3> { glm::vec3( -1.f, 0.f, 0.f ),
glm::vec3( -0.999f, 0.001f, 0.f ),
glm::vec3( -0.999f, -0.001f, 0.f ),
glm::vec3( 0.999f, 0.001f, 0.f ),
glm::vec3( 0.999f, -0.001f, 0.f ),
glm::vec3( 1.f, 0.f, 0.f ) };
colors_ = std::vector<glm::vec4> { glm::vec4( 1.f, 1.f, 1.f , 1.f ), glm::vec4( 1.f, 1.f, 1.f, 1.f ),
glm::vec4( 1.f, 1.f, 1.f, 1.f ), glm::vec4( 1.f, 1.f, 1.f, 1.f ),
glm::vec4( 1.f, 1.f, 1.f, 1.f ), glm::vec4( 1.f, 1.f, 1.f, 1.f ) };
indices_ = std::vector<uint> { 0, 1, 2, 3, 4, 5 };
drawMode_ = GL_TRIANGLE_STRIP;
// default scale
scale_.y = width;
}
HLine::~HLine()
{
// do NOT delete vao_ (unique)
vao_ = 0;
}
void HLine::init()
{
// use static unique vertex array object
static uint unique_vao_ = 0;
static uint unique_drawCount = 0;
if (unique_vao_) {
// 1. only init Node (not the primitive vao)
Node::init();
// 2. use the global vertex array object
vao_ = unique_vao_;
drawCount_ = unique_drawCount;
// compute AxisAlignedBoundingBox
bbox_.extend(points_);
// arrays of vertices are not needed anymore
points_.clear();
colors_.clear();
texCoords_.clear();
indices_.clear();
}
else {
// 1. init the Primitive (only once)
Primitive::init();
// 2. remember global vertex array object
unique_vao_ = vao_;
unique_drawCount = drawCount_;
// 3. unique_vao_ will NOT be deleted
}
}
void HLine::draw(glm::mat4 modelview, glm::mat4 projection)
{
// extract pure scaling from modelview (without rotation)
glm::mat4 ctm;
glm::vec3 rot(0.f);
glm::vec4 vec = modelview * glm::vec4(1.f, 0.f, 0.f, 0.f);
rot.z = glm::orientedAngle( glm::vec3(1.f, 0.f, 0.f), glm::normalize(glm::vec3(vec)), glm::vec3(0.f, 0.f, 1.f) );
ctm = glm::rotate(glm::identity<glm::mat4>(), -rot.z, glm::vec3(0.f, 0.f, 1.f)) * modelview ;
vec = ctm * glm::vec4(1.f, 1.f, 0.f, 0.f);
// Change transform to use linewidth independently of scale in Y (vertical)
scale_.y = (float) width / vec.y;
update(0);
Primitive::draw(modelview, projection);
}
VLine::VLine(float linewidth, Shader *s): Primitive(s), width(linewidth)
{
points_ = std::vector<glm::vec3> { glm::vec3( 0.f, -1.f, 0.f ),
glm::vec3( 0.001f, -0.999f, 0.f ),
glm::vec3( -0.001f, -0.999f, 0.f ),
glm::vec3( 0.001f, 0.999f, 0.f ),
glm::vec3( -0.001f, 0.999f, 0.f ),
glm::vec3( 0.f, 1.f, 0.f )};
colors_ = std::vector<glm::vec4> { glm::vec4( 1.f, 1.f, 1.f , 1.f ), glm::vec4( 1.f, 1.f, 1.f, 1.f ),
glm::vec4( 1.f, 1.f, 1.f, 1.f ), glm::vec4( 1.f, 1.f, 1.f, 1.f ),
glm::vec4( 1.f, 1.f, 1.f, 1.f ), glm::vec4( 1.f, 1.f, 1.f, 1.f ) };
indices_ = std::vector<uint> { 0, 1, 2, 3, 4, 5 };
drawMode_ = GL_TRIANGLE_STRIP;
// default scale
scale_.x = width;
}
VLine::~VLine()
{
// do NOT delete vao_ (unique)
vao_ = 0;
}
void VLine::init()
{
// use static unique vertex array object
static uint unique_vao_ = 0;
static uint unique_drawCount = 0;
if (unique_vao_) {
// 1. only init Node (not the primitive vao)
Node::init();
// 2. use the global vertex array object
vao_ = unique_vao_;
drawCount_ = unique_drawCount;
// compute AxisAlignedBoundingBox
bbox_.extend(points_);
// arrays of vertices are not needed anymore
points_.clear();
colors_.clear();
texCoords_.clear();
indices_.clear();
}
else {
// 1. init the Primitive (only once)
Primitive::init();
// 2. remember global vertex array object
unique_vao_ = vao_;
unique_drawCount = drawCount_;
// 3. unique_vao_ will NOT be deleted
}
}
void VLine::draw(glm::mat4 modelview, glm::mat4 projection)
{
// extract pure scaling from modelview (without rotation)
glm::mat4 ctm;
glm::vec3 rot(0.f);
glm::vec4 vec = modelview * glm::vec4(1.f, 0.f, 0.f, 0.f);
rot.z = glm::orientedAngle( glm::vec3(1.f, 0.f, 0.f), glm::normalize(glm::vec3(vec)), glm::vec3(0.f, 0.f, 1.f) );
ctm = glm::rotate(glm::identity<glm::mat4>(), -rot.z, glm::vec3(0.f, 0.f, 1.f)) * modelview ;
vec = ctm * glm::vec4(1.f, 1.f, 0.f, 0.f);
// Change transform to use linewidth independently of scale in X (horizontal)
scale_.x = width / vec.x;
update(0);
Primitive::draw(modelview, projection);
}
LineSquare::LineSquare(float linewidth) : Group()
{
shader__ = new Shader;
top_ = new HLine(linewidth, shader__);
top_->translation_ = glm::vec3(0.f, 1.f, 0.f);
attach(top_);
bottom_ = new HLine(linewidth, shader__);
bottom_->translation_ = glm::vec3(0.f, -1.f, 0.f);
attach(bottom_);
left_ = new VLine(linewidth, shader__);
left_->translation_ = glm::vec3(-1.f, 0.f, 0.f);
attach(left_);
right_ = new VLine(linewidth, shader__);
right_->translation_ = glm::vec3(1.f, 0.f, 0.f);
attach(right_);
}
LineSquare::LineSquare(const LineSquare &square)
{
shader__ = new Shader;
shader__->color = square.color();
top_ = new HLine(square.top_->width, shader__);
top_->translation_ = glm::vec3(0.f, 1.f, 0.f);
attach(top_);
bottom_ = new HLine(square.bottom_->width, shader__);
bottom_->translation_ = glm::vec3(0.f, -1.f, 0.f);
attach(bottom_);
left_ = new VLine(square.left_->width, shader__);
left_->translation_ = glm::vec3(-1.f, 0.f, 0.f);
attach(left_);
right_ = new VLine(square.right_->width, shader__);
right_->translation_ = glm::vec3(1.f, 0.f, 0.f);
attach(right_);
}
LineSquare::~LineSquare()
{
top_->replaceShader(nullptr);
bottom_->replaceShader(nullptr);
left_->replaceShader(nullptr);
right_->replaceShader(nullptr);
delete shader__;
}
void LineSquare::setLineWidth(float v)
{
top_->width = v;
bottom_->width = v;
left_->width = v;
right_->width = v;
}
LineGrid::LineGrid(size_t N, float step, float linewidth)
{
shader__ = new Shader;
N = MAX(1, N);
for (size_t n = 0; n < N ; ++n) {
VLine *l = new VLine(linewidth, shader__);
l->translation_.x = (float)n * step;
l->scale_.y = (float)N * step;
attach(l);
}
for (size_t n = 1; n < N ; ++n) {
VLine *l = new VLine(linewidth, shader__);
l->translation_.x = (float)n * -step;
l->scale_.y = (float)N * step;
attach(l);
}
for (size_t n = 0; n < N ; ++n) {
HLine *l = new HLine(linewidth, shader__);
l->translation_.y = (float)n * step;
l->scale_.x = (float)N * step;
attach(l);
}
for (size_t n = 1; n < N ; ++n) {
HLine *l = new HLine(linewidth, shader__);
l->translation_.y = (float)n * -step;
l->scale_.x = (float)N * step;
attach(l);
}
}
LineGrid::~LineGrid()
{
// prevent nodes from deleting the shader
for (NodeSet::iterator node = begin(); node != end(); ++node) {
Primitive *p = dynamic_cast<Primitive *>(*node);
if (p)
p->replaceShader(nullptr);
}
delete shader__;
}
void LineGrid::setLineWidth(float v)
{
for (NodeSet::iterator node = begin(); node != end(); ++node) {
VLine *vl = dynamic_cast<VLine *>(*node);
if (vl) {
vl->width = v;
continue;
}
HLine *hl = dynamic_cast<HLine *>(*node);
if (hl)
hl->width = v;
}
}
float LineGrid::lineWidth() const
{
Node *n = front();
if (n) {
VLine *vl = dynamic_cast<VLine *>(n);
if (vl)
return vl->width;
HLine *hl = dynamic_cast<HLine *>(n);
if (hl)
return hl->width;
}
return 0.f;
}
LineStrip::LineStrip(const std::vector<glm::vec2> &path, float linewidth, Shader *s) : Primitive(s),
arrayBuffer_(0), path_(path)
{
linewidth_ = 0.002f * linewidth;
for(size_t i = 1; i < path_.size(); ++i)
{
glm::vec3 begin = glm::vec3(path_[i-1], 0.f);
glm::vec3 end = glm::vec3(path_[i], 0.f);
glm::vec3 dir = end - begin;
glm::vec3 perp = glm::normalize(glm::cross(dir, glm::vec3(0.f, 0.f, 1.f)));
points_.push_back( begin + perp * linewidth_ );
colors_.push_back( glm::vec4( 1.f, 1.f, 1.f, 1.f ) );
indices_.push_back ( indices_.size() );
points_.push_back( begin - perp * linewidth_ );
colors_.push_back( glm::vec4( 1.f, 1.f, 1.f, 1.f ) );
indices_.push_back ( indices_.size() );
points_.push_back( end + perp * linewidth_ );
colors_.push_back( glm::vec4( 1.f, 1.f, 1.f, 1.f ) );
indices_.push_back ( indices_.size() );
points_.push_back( end - perp * linewidth_ );
colors_.push_back( glm::vec4( 1.f, 1.f, 1.f, 1.f ) );
indices_.push_back ( indices_.size() );
}
drawMode_ = GL_TRIANGLE_STRIP;
}
LineStrip::~LineStrip()
{
// delete buffer
if ( arrayBuffer_ )
glDeleteBuffers ( 1, &arrayBuffer_);
}
void LineStrip::init()
{
if ( vao_ )
glDeleteVertexArrays ( 1, &vao_);
// Vertex Array
glGenVertexArrays( 1, &vao_ );
// Create and initialize buffer objects
if ( arrayBuffer_ )
glDeleteBuffers ( 1, &arrayBuffer_);
glGenBuffers( 1, &arrayBuffer_ );
uint elementBuffer_;
glGenBuffers( 1, &elementBuffer_);
glBindVertexArray( vao_ );
// setup the array buffers for vertices
std::size_t sizeofPoints = sizeof(glm::vec3) * points_.size();
std::size_t sizeofColors = sizeof(glm::vec4) * colors_.size();
glBindBuffer( GL_ARRAY_BUFFER, arrayBuffer_ );
glBufferData( GL_ARRAY_BUFFER, sizeofPoints + sizeofColors, NULL, GL_DYNAMIC_DRAW);
glBufferSubData( GL_ARRAY_BUFFER, 0, sizeofPoints, &points_[0] );
glBufferSubData( GL_ARRAY_BUFFER, sizeofPoints, sizeofColors, &colors_[0] );
// setup the element array for indices
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, elementBuffer_);
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(uint) * indices_.size(), &(indices_[0]), GL_STATIC_DRAW);
// explain how to read attributes 0 and 1
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), (void *)0 );
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (void *)(sizeofPoints) );
glEnableVertexAttribArray(1);
// done
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// drawing indications
drawCount_ = indices_.size();
if ( elementBuffer_ )
glDeleteBuffers ( 1, &elementBuffer_);
indices_.clear();
// compute AxisAlignedBoundingBox
bbox_.extend(points_);
Node::init();
}
void LineStrip::updatePath()
{
if (!vao_)
return;
// redo points_ array
points_.clear();
for(size_t i = 1; i < path_.size(); ++i)
{
glm::vec3 begin = glm::vec3(path_[i-1], 0.f);
glm::vec3 end = glm::vec3(path_[i], 0.f);
glm::vec3 dir = end - begin;
glm::vec3 perp = glm::normalize(glm::cross(dir, glm::vec3(0.f, 0.f, 1.f)));
points_.push_back( begin + perp * linewidth_ );
points_.push_back( begin - perp * linewidth_ );
points_.push_back( end + perp * linewidth_ );
points_.push_back( end - perp * linewidth_ );
}
// bind the vertex array and change the point coordinates
glBindVertexArray( vao_ );
glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer_);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::vec3) * points_.size(), &points_[0] );
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// reset and compute AxisAlignedBoundingBox
GlmToolkit::AxisAlignedBoundingBox b;
bbox_ = b;
bbox_.extend(points_);
}
void LineStrip::editPath(uint index, glm::vec2 position)
{
if (index < path_.size()) {
path_[index] = position;
updatePath();
}
}
void LineStrip::changePath(std::vector<glm::vec2> path)
{
// invalid if not enough points given
size_t N = path_.size();
if (path.size() < N)
return;
// replace path but keep number of points
path_ = path;
path_.resize(N);
updatePath();
}
void LineStrip::setLineWidth(float linewidth) {
linewidth_ = 0.002f * linewidth;
updatePath();
}
void LineStrip::accept(Visitor& v)
{
Primitive::accept(v);
v.visit(*this);
}
LineLoop::LineLoop(const std::vector<glm::vec2> &path, float linewidth, Shader *s) : LineStrip(path, linewidth, s)
{
// close linestrip loop
glm::vec3 begin = glm::vec3(path_[path_.size()-1], 0.f);
glm::vec3 end = glm::vec3(path_[0], 0.f);
glm::vec3 dir = end - begin;
glm::vec3 perp = glm::normalize(glm::cross(dir, glm::vec3(0.f, 0.f, 1.f)));
points_.push_back( begin + perp * linewidth_ );
colors_.push_back( glm::vec4( 1.f, 1.f, 1.f, 1.f ) );
indices_.push_back ( indices_.size() );
points_.push_back( begin - perp * linewidth_ );
colors_.push_back( glm::vec4( 1.f, 1.f, 1.f, 1.f ) );
indices_.push_back ( indices_.size() );
points_.push_back( end + perp * linewidth_ );
colors_.push_back( glm::vec4( 1.f, 1.f, 1.f, 1.f ) );
indices_.push_back ( indices_.size() );
points_.push_back( end - perp * linewidth_ );
colors_.push_back( glm::vec4( 1.f, 1.f, 1.f, 1.f ) );
indices_.push_back ( indices_.size() );
}
void LineLoop::updatePath()
{
if (!vao_)
return;
glm::vec3 begin;
glm::vec3 end;
glm::vec3 dir;
glm::vec3 perp;
// redo points_ array
points_.clear();
size_t i = 1;
for(; i < path_.size(); ++i)
{
begin = glm::vec3(path_[i-1], 0.f);
end = glm::vec3(path_[i], 0.f);
dir = end - begin;
perp = glm::normalize(glm::cross(dir, glm::vec3(0.f, 0.f, 1.f)));
points_.push_back( begin + perp * linewidth_ );
points_.push_back( begin - perp * linewidth_ );
points_.push_back( end + perp * linewidth_ );
points_.push_back( end - perp * linewidth_ );
}
// close linestrip loop
begin = glm::vec3(path_[i-1], 0.f);
end = glm::vec3(path_[0], 0.f);
dir = end - begin;
perp = glm::normalize(glm::cross(dir, glm::vec3(0.f, 0.f, 1.f)));
points_.push_back( begin + perp * linewidth_ );
points_.push_back( begin - perp * linewidth_ );
points_.push_back( end + perp * linewidth_ );
points_.push_back( end - perp * linewidth_ );
// bind the vertex array and change the point coordinates
glBindVertexArray( vao_ );
glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer_);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::vec3) * points_.size(), &points_[0] );
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// re-compute AxisAlignedBoundingBox
bbox_.extend(points_);
}
// statically defined line loop drawing a circle (72 points)
std::vector<glm::vec2> _circle_loop = {
{1.000000, 0.000000},
{0.996087, 0.088380},
{0.984378, 0.176069},
{0.964965, 0.262379},
{0.938000, 0.346636},
{0.903694, 0.428180},
{0.862315, 0.506373},
{0.814187, 0.580603},
{0.759687, 0.650289},
{0.699242, 0.714885},
{0.633324, 0.773887},
{0.562449, 0.826832},
{0.487173, 0.873306},
{0.408084, 0.912945},
{0.325801, 0.945439},
{0.240968, 0.970533},
{0.154249, 0.988032},
{0.066323, 0.997798},
{-0.022122, 0.999756},
{-0.110394, 0.993888},
{-0.197802, 0.980242},
{-0.283662, 0.958925},
{-0.367302, 0.930102},
{-0.448067, 0.894000},
{-0.525325, 0.850902},
{-0.598472, 0.801144},
{-0.666936, 0.745116},
{-0.730179, 0.683256},
{-0.787708, 0.616049},
{-0.839072, 0.544021},
{-0.883869, 0.467734},
{-0.921749, 0.387788},
{-0.952415, 0.304806},
{-0.975627, 0.219439},
{-0.991203, 0.132354},
{-0.999022, 0.044233},
{-0.999022, -0.044233},
{-0.991203, -0.132354},
{-0.975627, -0.219439},
{-0.952415, -0.304806},
{-0.921749, -0.387788},
{-0.883870, -0.467734},
{-0.839072, -0.544021},
{-0.787708, -0.616049},
{-0.730179, -0.683256},
{-0.666936, -0.745116},
{-0.598473, -0.801144},
{-0.525325, -0.850902},
{-0.448067, -0.894001},
{-0.367302, -0.930102},
{-0.283662, -0.958925},
{-0.197802, -0.980243},
{-0.110394, -0.993888},
{-0.022122, -0.999756},
{0.066323, -0.997799},
{0.154249, -0.988033},
{0.240968, -0.970534},
{0.325801, -0.945439},
{0.408084, -0.912945},
{0.487173, -0.873306},
{0.562450, -0.826832},
{0.633324, -0.773887},
{0.699242, -0.714886},
{0.759688, -0.650289},
{0.814188, -0.580603},
{0.862315, -0.506373},
{0.903694, -0.428180},
{0.938001, -0.346636},
{0.964966, -0.262379},
{0.984379, -0.176068},
{0.996088, -0.088380}
};
LineCircle::LineCircle(float linewidth, Shader *s) : LineLoop(_circle_loop, linewidth, s)
{
// static float a = glm::two_pi<float>() / static_cast<float>(LINE_CIRCLE_DENSITY-1);
// // loop to build a circle
// glm::vec3 P(1.f, 0.f, 0.f);
// for (int i = 0; i < LINE_CIRCLE_DENSITY - 1; i++ ){
// path_[i] = glm::vec2(P);
// g_printerr("{%f, %f},\n", path_[i].x, path_[i].y);
// P = glm::rotateZ(P, a);
// }
}
LineCircleGrid::LineCircleGrid(float angle_step, size_t N, float step, float linewidth)
{
shader__ = new Shader;
N = MAX(1, N);
step = MAX(0.01, step);
// Draw N concentric circles
for (size_t n = 1; n < N ; ++n) {
float scale = (float) n * step;
LineCircle *l = new LineCircle(linewidth / scale, shader__);
l->scale_ = glm::vec3( scale, scale, 1.f);
// add cirle to group
attach(l);
}
// Draw radius lines every angle step
glm::vec3 O(0.f, 0.f, 0.f);
glm::vec3 P(1.f, 0.f, 0.f);
std::vector<glm::vec2> points;
for (int a = 0 ; a < (int)( (2.f * M_PI) / angle_step ) + 1 ; ++a ){
points.push_back(O);
P = glm::rotateZ(P, angle_step);
points.push_back(P);
}
// add to group
LineStrip *radius = new LineStrip(points, linewidth * 0.5f, shader__);
radius->scale_ = glm::vec3( glm::vec2( (float) N * step ), 1.f);
attach(radius);
}
LineCircleGrid::~LineCircleGrid()
{
// prevent nodes from deleting the shader
for (NodeSet::iterator node = begin(); node != end(); ++node) {
Primitive *p = dynamic_cast<Primitive *>(*node);
if (p)
p->replaceShader(nullptr);
}
delete shader__;
}
void LineCircleGrid::setLineWidth(float v)
{
for (NodeSet::iterator node = begin(); node != end(); ++node) {
LineStrip *vl = dynamic_cast<LineStrip *>(*node);
if (vl)
vl->setLineWidth(v);
}
}
float LineCircleGrid::lineWidth() const
{
Node *n = front();
if (n) {
LineStrip *vl = dynamic_cast<LineStrip *>(n);
if (vl)
return vl->lineWidth();
}
return 0.f;
}