cgal/Arrangement_on_surface_2/demo/earth/mainwidget.cpp

458 lines
12 KiB
C++

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mainwidget.h"
#include <QMouseEvent>
#include <cmath>
#include <iostream>
#include <string>
namespace {
// vertex shader
const char* vertex_shader_code = R"vs(
#version 330
layout (location = 0) in vec3 pos;
layout (location = 1) in vec3 normal;
//out vec4 vCol;
//out vec3 vpos;
out vec3 vNormal;
uniform mat4 MVP;
void main()
{
//vpos = pos;
vNormal = normal;
gl_Position = MVP * vec4(pos.xyz, 1);
//gl_Position = vec4(pos.xyz, 1);
}
)vs";
// GEOMETRY SHADER
// * I am using the geometry shader to compute the face-normals in the GPU on the fly
const char* geometry_shader_code = R"gs(
#version 330
in vec3 vpos[];
out vec4 vCol;
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
void main()
{
const vec3 lightDir = normalize(vec3(1,.5,.5));
// compute the normal for the current triangle
vec3 triNormal = normalize(cross(vpos[1]-vpos[0], vpos[2]-vpos[0]));
//float c = clamp(dot(lightDir,triNormal), 0, 1);
float c = abs(dot(lightDir,triNormal));
vCol = vec4(.2, .2,0,1) + vec4(c,c,0,1);
gl_Position = gl_in[0].gl_Position; EmitVertex();
gl_Position = gl_in[1].gl_Position; EmitVertex();
gl_Position = gl_in[2].gl_Position; EmitVertex();
EndPrimitive();
}
)gs";
// FRAGMENT SHADER
static const char* fragment_shader_code = R"fs(
#version 330
//in vec4 vCol;
in vec3 vNormal;
out vec4 color;
void main()
{
const vec3 lightDir = normalize(vec3(1,.5,.5));
//float c = clamp(dot(lightDir,triNormal), 0, 1);
vec3 n = normalize(vNormal);
float c = abs( dot(lightDir, n) );
color = vec4(.2, .2,0,1) + vec4(c,c,0,1);
//color = vec4(1,1,0,1);
//color = vCol;
}
)fs";
}
MainWidget::~MainWidget()
{
// Make sure the context is current when deleting the texture
// and the buffers.
makeCurrent();
doneCurrent();
}
void MainWidget::mousePressEvent(QMouseEvent *e)
{
// Save mouse press position
m_mouse_press_position = QVector2D(e->position());
}
void MainWidget::mouseReleaseEvent(QMouseEvent *e)
{
// Mouse release position - mouse press position
QVector2D diff = QVector2D(e->position()) - m_mouse_press_position;
// Rotation axis is perpendicular to the mouse position difference
// vector
QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized();
// Accelerate angular speed relative to the length of the mouse sweep
qreal acc = diff.length() / 100.0;
// Calculate new rotation axis as weighted sum
m_rotation_axis = (m_rotation_axis * m_angular_speed + n * acc).normalized();
// Increase angular speed
m_angular_speed += acc;
}
void MainWidget::timerEvent(QTimerEvent *)
{
// Decrease angular speed (friction)
m_angular_speed *= 0.99;
// Stop rotation when speed goes below threshold
if (m_angular_speed < 0.01) {
m_angular_speed = 0.0;
} else {
// Update rotation
m_rotation = QQuaternion::fromAxisAndAngle(m_rotation_axis, m_angular_speed) *
m_rotation;
// Request an update
update();
}
// redraw every 12 miliseconds
update();
}
void MainWidget::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0, 0, 0, 1);
init_geometry();
init_shader_program();
// Enable depth buffer
glEnable(GL_DEPTH_TEST);
// Enable back face culling
//glEnable(GL_CULL_FACE);
// Use QBasicTimer because its faster than QTimer
m_timer.start(12, this);
}
void MainWidget::add_shader(GLuint the_program, const char* shader_code,
GLenum shader_type)
{
GLuint the_shader = glCreateShader(shader_type);
const GLchar* the_code[] = { shader_code };
GLint code_length[] = { strlen(shader_code) };
glShaderSource(the_shader, 1, the_code, code_length);
glCompileShader(the_shader);
GLint result = 0;
GLchar elog[1024] = { 0 };
glGetShaderiv(the_shader, GL_COMPILE_STATUS, &result);
if (!result)
{
std::string shader_type_name;
switch (shader_type)
{
case GL_VERTEX_SHADER: shader_type_name = "VERTEX"; break;
case GL_GEOMETRY_SHADER: shader_type_name = "GEOMETRY"; break;
case GL_FRAGMENT_SHADER: shader_type_name = "FRAGMENT"; break;
}
glGetShaderInfoLog(the_shader, sizeof(elog), NULL, elog);
std::cout << "! error compiling the " << shader_type_name <<
" shader:\n" << elog << std::endl;
return;
}
glAttachShader(the_program, the_shader);
}
void MainWidget::init_shader_program()
{
shader = glCreateProgram();
if (!shader)
{
std::cout << "error creating shader program!\n";
return;
}
add_shader(shader, vertex_shader_code, GL_VERTEX_SHADER);
//add_shader(shader, geometry_shader_code, GL_GEOMETRY_SHADER);
add_shader(shader, fragment_shader_code, GL_FRAGMENT_SHADER);
GLint result = 0;
GLchar elog[1024] = { 0 };
glLinkProgram(shader);
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(elog), NULL, elog);
std::cout << "! error linking program:\n" << elog << std::endl;
return;
}
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if (!result)
{
glGetProgramInfoLog(shader, sizeof(elog), NULL, elog);
std::cout << "! error validating program:\n" << elog << std::endl;
return;
}
m_uniform_mvp = glGetUniformLocation(shader, "MVP");
std::cout << "uniform loc = " << m_uniform_mvp << std::endl;
}
void MainWidget::init_geometry()
{
int num_slices, num_stacks;
num_slices = num_stacks = 64;
float r = 3;
create_sphere(num_slices, num_stacks, r);
}
void MainWidget::create_sphere(int num_slices, int num_stacks, float r)
{
num_stacks = std::max<int>(2, num_stacks);
std::vector<QVector3D> vertices, normals;
// NORTH POLE
vertices.push_back(QVector3D(0, 0, r));
normals.push_back(QVector3D(0, 0, 1));
// SOUTH POLE
vertices.push_back(QVector3D(0, 0, -r));
normals.push_back(QVector3D(0, 0, -1));
int starting_index_of_middle_vertices = vertices.size();
for (int j = 1; j < num_stacks; ++j)
{
// Calculate the latitude (vertical angle) for the current stack
float lat = M_PI * j / num_stacks;
float rxy = r * std::sin(lat);
float z = r * std::cos(lat);
for (int i = 0; i < num_slices; ++i)
{
// Calculate the longitude (horizontal angle) for the current slice
float lon = 2 * M_PI * i / num_slices;
// Convert spherical coordinates to Cartesian coordinates
float x = rxy * std::cos(lon);
float y = rxy * std::sin(lon);
auto p = QVector3D(x, y, z);
auto n = p / p.length();
vertices.push_back(p);
normals.push_back(n);
}
}
// strided vertex-data
std::vector<QVector3D> vertex_data;
for (int i = 0; i < vertices.size(); ++i)
{
vertex_data.push_back(vertices[i]);
vertex_data.push_back(normals[i]);
}
// add the indices for all triangles
std::vector<GLuint> indices;
// NORTH CAP
const int north_vertex_index = 0;
const int north_cap_vertex_index_start = starting_index_of_middle_vertices;
for (int i = 0; i < num_slices; i++)
{
indices.push_back(north_vertex_index);
indices.push_back(north_cap_vertex_index_start + i);
indices.push_back(north_cap_vertex_index_start + (i + 1) % num_slices);
}
// 0 = NORTH VERTEX
// 1 = SOUTH VERTEX
// [2, 2 + (numSlices-1)] = bottom vertices of the stack #1
// [2+numSlices, 2 + (2*numSlices - 1)] = bottom vertices of the stack #2
// ...
// [2+(k-1)*numSlices, 2 + (k*numSlices -1)] = bottom vertices of the stack #k
// ..
// [2+(numStacks-1)*numSlices, 2+(numStacks*numSlices-1)] = bottom vertices of
// the last stack (# numStacks)
// SOUTH CAP
const int south_vertex_index = 1;
const int south_cap_index_start = starting_index_of_middle_vertices +
(num_stacks - 2) * num_slices;
for (int i = 0; i < num_slices; ++i)
{
const auto vi0 = south_vertex_index;
const auto vi1 = south_cap_index_start + i;
const auto vi2 = south_cap_index_start + (i + 1) % num_slices;
indices.push_back(vi2);
indices.push_back(vi1);
indices.push_back(vi0);
}
// MIDDLE TRIANGLES
for (int k = 0; k < num_stacks - 2; ++k)
{
const int stack_start_index = starting_index_of_middle_vertices +
k * num_slices;
const int next_stack_start_index = stack_start_index + num_slices;
for (int i = 0; i < num_slices; ++i)
{
// check why the following code snippet does not work (winding order?)
//int vi0 = stackStartIndex + i;
//int vi1 = nextStackStartIndex + i;
//int vi2 = nextStackStartIndex + (i + 1) % numSlices;
//int vi3 = stackStartIndex + (i + 1) % numSlices;
int vi0 = stack_start_index + i;
int vi1 = stack_start_index + (i + 1) % num_slices;
int vi2 = next_stack_start_index + i;
int vi3 = next_stack_start_index + (i + 1) % num_slices;
indices.push_back(vi0);
indices.push_back(vi2);
indices.push_back(vi1);
//
indices.push_back(vi2);
indices.push_back(vi3);
indices.push_back(vi1);
}
}
m_num_indices = indices.size();
// DEFINE OPENGL BUFFERS
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
// Index buffer
glGenBuffers(1, &m_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
auto indices_size = sizeof(GLuint) * indices.size();
auto indices_data = reinterpret_cast<const void*>(indices.data());
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
indices_size,
indices_data,
GL_STATIC_DRAW);
// Vertex Buffer
glGenBuffers(1, &m_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
auto vertex_buffer_size = sizeof(QVector3D) * vertex_data.size();
auto vertex_buffer_data = reinterpret_cast<const void*>(vertex_data.data());
glBufferData(GL_ARRAY_BUFFER,
vertex_buffer_size,
vertex_buffer_data,
GL_STATIC_DRAW);
// Position Vertex-Attribute
GLint position_attrib_index = 0;
const void* position_offset = 0;
GLsizei stride = 6 * sizeof(float);
glVertexAttribPointer(position_attrib_index,
3,
GL_FLOAT, GL_FALSE,
stride,
position_offset);
glEnableVertexAttribArray(position_attrib_index);
// Normal Vertex-Attribute
GLint normal_attrib_index = 1;
auto* normal_offset = reinterpret_cast<const void*>(3 * sizeof(float));
glVertexAttribPointer(normal_attrib_index,
3,
GL_FLOAT,
GL_FALSE,
stride,
normal_offset);
glEnableVertexAttribArray(normal_attrib_index);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// Note: calling this before glBindVertexArray(0) results in no output!
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void MainWidget::resizeGL(int w, int h)
{
// Calculate aspect ratio
qreal aspect = qreal(w) / qreal(h ? h : 1);
// near and far plane locations and vertical field-of-view angle in degrees
const qreal z_near = 1.0, z_far = 100.0, fov = 45.0;
// Reset projection
m_projection.setToIdentity();
m_projection.perspective(fov, aspect, z_near, z_far);
}
void MainWidget::paintGL()
{
QMatrix4x4 view;
const QVector3D eye(0, 10, 10), center(0, 0, 0), up(0, 1, 0);
view.lookAt(eye, center, up);
QMatrix4x4 model;
static float angle = 0;
angle += 1;
model.rotate(angle, up);
// Clear color and depth buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
{
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shader);
auto mvp = m_projection * view * model;
glUniformMatrix4fv(m_uniform_mvp, 1, GL_FALSE, mvp.data());
{
// DRAW TRIANGLE
glBindVertexArray(m_vao);
{
//glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
glDrawElements(GL_TRIANGLES, m_num_indices, GL_UNSIGNED_INT, 0);
}
glBindVertexArray(0);
}
glUseProgram(0);
}
}