New gcode visualization library - Compatibility with OpenGL ES

This commit is contained in:
enricoturri1966 2024-01-17 10:25:07 +01:00 committed by Lukas Matena
parent 9bb31f3468
commit d7d0f3e28c
6 changed files with 376 additions and 26 deletions

View File

@ -31,6 +31,7 @@ add_library(libvgcode STATIC
src/Settings.hpp
src/Settings.cpp
src/Shaders.hpp
src/ShadersES.hpp
src/ToolMarker.hpp
src/ToolMarker.cpp
src/Types.cpp

View File

@ -28,6 +28,11 @@ public:
// Initialize the viewer.
// This method must be called after a valid OpenGL context has been already created
// and before calling any other method of the viewer.
// Throws an std::runtime_error exception if:
// * the method is called before creating an OpenGL context
// * the created OpenGL context does not support for OpenGL 3.2 or greater
// * when using OpenGL ES, the created OpenGL ES context does not support OpenGL ES 2.0 or greater
// * any of the shaders fails to compile
//
void init();
//

View File

@ -7,6 +7,7 @@
#include <iostream>
#include <assert.h>
#include <cctype>
#include <stdio.h>
namespace libvgcode {
@ -32,24 +33,37 @@ void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char
}
#endif // HAS_GLSAFE
bool load_opengl()
{
return gladLoadGL() != 0;
}
static const std::string OPENGL_ES_STR = "OpenGL ES";
bool check_opengl_version()
bool OpenGLWrapper::s_detected = false;
bool OpenGLWrapper::s_valid = false;
bool OpenGLWrapper::s_es = false;
bool OpenGLWrapper::load_opengl()
{
bool ret = false;
const GLubyte* version = glGetString(GL_VERSION);
if (version != nullptr) {
const std::string version_str(reinterpret_cast<const char*>(version));
if (version_str.length() > 4 && isdigit(version_str[0]) && isdigit(version_str[2])) {
const int major = version_str[0] - '0';
const int minor = version_str[2] - '0';
ret = major > 3 || (major == 3 && minor >= 2);
}
if (gladLoadGL() == 0)
return false;
s_detected = true;
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
if (version == nullptr)
return false;
std::string version_str(version);
const size_t pos = version_str.find(OPENGL_ES_STR.c_str());
if (pos == 0) {
s_es = true;
version_str = version_str.substr(OPENGL_ES_STR.length() + 1);
}
return ret;
GLint major = 0;
GLint minor = 0;
const int res = sscanf(version_str.c_str(), "%d.%d", &major, &minor);
if (res != 2)
return false;
s_valid = s_es ? major > 2 || (major == 2 && minor >= 0) : major > 3 || (major == 3 && minor >= 2);
return s_valid;
}
} // namespace libvgcode

View File

@ -24,8 +24,21 @@ inline void glAssertRecentCall() { }
#define glcheck()
#endif // HAS_GLSAFE
extern bool load_opengl();
extern bool check_opengl_version();
class OpenGLWrapper
{
public:
OpenGLWrapper() = delete;
static bool load_opengl();
static bool is_detected() { return s_detected; }
static bool is_valid() { return s_valid; }
static bool is_es() { return s_es; }
private:
static bool s_detected;
static bool s_valid;
static bool s_es;
};
} // namespace libvgcode

View File

@ -0,0 +1,303 @@
///|/ Copyright (c) Prusa Research 2023 Enrico Turri @enricoturri1966, Pavel Mikuš @Godrak
///|/
///|/ libvgcode is released under the terms of the AGPLv3 or higher
///|/
#ifndef VGCODE_SHADERSES_HPP
#define VGCODE_SHADERSES_HPP
// needed for tech VGCODE_ENABLE_COG_AND_TOOL_MARKERS
#include "../include/Types.hpp"
namespace libvgcode {
static const char* Segments_Vertex_Shader_ES =
"#version 300 es\n"
"#define POINTY_CAPS\n"
"#define FIX_TWISTING\n"
"const vec3 light_top_dir = vec3(-0.4574957, 0.4574957, 0.7624929);\n"
"const float light_top_diffuse = 0.6 * 0.8;\n"
"const float light_top_specular = 0.6 * 0.125;\n"
"const float light_top_shininess = 20.0;\n"
"const vec3 light_front_dir = vec3(0.6985074, 0.1397015, 0.6985074);\n"
"const float light_front_diffuse = 0.6 * 0.3;\n"
"const float ambient = 0.3;\n"
"const float emission = 0.15;\n"
"const vec3 UP = vec3(0, 0, 1);\n"
"uniform mat4 view_matrix;\n"
"uniform mat4 projection_matrix;\n"
"uniform vec3 camera_position;\n"
"uniform samplerBuffer positionsTex;\n"
"uniform samplerBuffer heightWidthAngleTex;\n"
"uniform samplerBuffer colorsTex;\n"
"uniform isamplerBuffer segmentIndexTex;\n"
"in int vertex_id;\n"
"out vec3 color;\n"
"vec3 decode_color(float color) {\n"
" int c = int(round(color));\n"
" int r = (c >> 16) & 0xFF;\n"
" int g = (c >> 8) & 0xFF;\n"
" int b = (c >> 0) & 0xFF;\n"
" float f = 1.0 / 255.0f;\n"
" return f * vec3(r, g, b);\n"
"}\n"
"float lighting(vec3 eye_position, vec3 eye_normal) {\n"
" float top_diffuse = light_top_diffuse * max(dot(eye_normal, light_top_dir), 0.0);\n"
" float front_diffuse = light_front_diffuse * max(dot(eye_normal, light_front_dir), 0.0);\n"
" float top_specular = light_top_specular * pow(max(dot(-normalize(eye_position), reflect(-light_top_dir, eye_normal)), 0.0), light_top_shininess);\n"
" return ambient + top_diffuse + front_diffuse + top_specular + emission;\n"
"}\n"
"void main() {\n"
" int id_a = texelFetch(segmentIndexTex, gl_InstanceID).r;\n"
" int id_b = id_a + 1;\n"
" vec3 pos_a = texelFetch(positionsTex, id_a).xyz;\n"
" vec3 pos_b = texelFetch(positionsTex, id_b).xyz;\n"
" vec3 line = pos_b - pos_a;\n"
" // directions of the line box in world space\n"
" float line_len = length(line);\n"
" vec3 line_dir;\n"
" if (line_len < 1e-4)\n"
" line_dir = vec3(1.0, 0.0, 0.0);\n"
" else\n"
" line_dir = line / line_len;\n"
" vec3 line_right_dir;\n"
" if (abs(dot(line_dir, UP)) > 0.9) {\n"
" // For vertical lines, the width and height should be same, there is no concept of up and down.\n"
" // For simplicity, the code will expand width in the x axis, and height in the y axis\n"
" line_right_dir = normalize(cross(vec3(1, 0, 0), line_dir));\n"
" }\n"
" else\n"
" line_right_dir = normalize(cross(line_dir, UP));\n"
" vec3 line_up_dir = normalize(cross(line_right_dir, line_dir));\n"
" const vec2 horizontal_vertical_view_signs_array[16] = vec2[](\n"
" //horizontal view (from right)\n"
" vec2(1.0, 0.0),\n"
" vec2(0.0, 1.0),\n"
" vec2(0.0, 0.0),\n"
" vec2(0.0, -1.0),\n"
" vec2(0.0, -1.0),\n"
" vec2(1.0, 0.0),\n"
" vec2(0.0, 1.0),\n"
" vec2(0.0, 0.0),\n"
" // vertical view (from top)\n"
" vec2(0.0, 1.0),\n"
" vec2(-1.0, 0.0),\n"
" vec2(0.0, 0.0),\n"
" vec2(1.0, 0.0),\n"
" vec2(1.0, 0.0),\n"
" vec2(0.0, 1.0),\n"
" vec2(-1.0, 0.0),\n"
" vec2(0.0, 0.0)\n"
" );\n"
" int id = vertex_id < 4 ? id_a : id_b;\n"
" vec3 endpoint_pos = vertex_id < 4 ? pos_a : pos_b;\n"
" vec3 height_width_angle = texelFetch(heightWidthAngleTex, id).xyz;\n"
"#ifdef FIX_TWISTING\n"
" int closer_id = (dot(camera_position - pos_a, camera_position - pos_a) < dot(camera_position - pos_b, camera_position - pos_b)) ? id_a : id_b;\n"
" vec3 closer_pos = (closer_id == id_a) ? pos_a : pos_b;\n"
" vec3 camera_view_dir = normalize(closer_pos - camera_position);\n"
" vec3 closer_height_width_angle = texelFetch(heightWidthAngleTex, closer_id).xyz;\n"
" vec3 diagonal_dir_border = normalize(closer_height_width_angle.x * line_up_dir + closer_height_width_angle.y * line_right_dir);\n"
"#else\n"
" vec3 camera_view_dir = normalize(endpoint_pos - camera_position);\n"
" vec3 diagonal_dir_border = normalize(height_width_angle.x * line_up_dir + height_width_angle.y * line_right_dir);\n"
"#endif\n"
" bool is_vertical_view = abs(dot(camera_view_dir, line_up_dir)) / abs(dot(diagonal_dir_border, line_up_dir)) >\n"
" abs(dot(camera_view_dir, line_right_dir)) / abs(dot(diagonal_dir_border, line_right_dir));\n"
" vec2 signs = horizontal_vertical_view_signs_array[vertex_id + 8 * int(is_vertical_view)];\n"
"#ifndef POINTY_CAPS\n"
" if (vertex_id == 2 || vertex_id == 7) signs = -horizontal_vertical_view_signs_array[(vertex_id - 2) + 8 * int(is_vertical_view)];\n"
"#endif\n"
" float view_right_sign = sign(dot(-camera_view_dir, line_right_dir));\n"
" float view_top_sign = sign(dot(-camera_view_dir, line_up_dir));\n"
" float half_height = 0.5 * height_width_angle.x;\n"
" float half_width = 0.5 * height_width_angle.y;\n"
" vec3 horizontal_dir = half_width * line_right_dir;\n"
" vec3 vertical_dir = half_height * line_up_dir;\n"
" float horizontal_sign = signs.x * view_right_sign;\n"
" float vertical_sign = signs.y * view_top_sign;\n"
" vec3 pos = endpoint_pos + horizontal_sign * horizontal_dir + vertical_sign * vertical_dir;\n"
" if (vertex_id == 2 || vertex_id == 7) {\n"
" float line_dir_sign = (vertex_id == 2) ? -1.0 : 1.0;\n"
" if (height_width_angle.z == 0.0) {\n"
"#ifdef POINTY_CAPS\n"
" // There I add a cap to lines that do not have a following line\n"
" // (or they have one, but perfectly aligned, so the cap is hidden inside the next line).\n"
" pos += line_dir_sign * line_dir * half_width;\n"
"#endif\n"
" }\n"
" else {\n"
" pos += line_dir_sign * line_dir * half_width * sin(abs(height_width_angle.z) * 0.5);\n"
" pos += sign(height_width_angle.z) * horizontal_dir * cos(abs(height_width_angle.z) * 0.5);\n"
" }\n"
" }\n"
" vec3 eye_position = (view_matrix * vec4(pos, 1.0)).xyz;\n"
" vec3 eye_normal = (view_matrix * vec4(normalize(pos - endpoint_pos), 0.0)).xyz;\n"
" vec3 color_base = decode_color(texelFetch(colorsTex, id).x);\n"
" color = color_base * lighting(eye_position, eye_normal);\n"
" gl_Position = projection_matrix * vec4(eye_position, 1.0);\n"
"}\n";
static const char* Segments_Fragment_Shader_ES =
"#version 300 es\n"
"precision highp float;\n"
"in vec3 color;\n"
"out vec4 fragmentColor;\n"
"void main() {\n"
" fragmentColor = vec4(color, 1.0);\n"
"}\n";
static const char* Options_Vertex_Shader_ES =
"#version 300 es\n"
"const vec3 light_top_dir = vec3(-0.4574957, 0.4574957, 0.7624929);\n"
"const float light_top_diffuse = 0.6 * 0.8;\n"
"const float light_top_specular = 0.6 * 0.125;\n"
"const float light_top_shininess = 20.0;\n"
"const vec3 light_front_dir = vec3(0.6985074, 0.1397015, 0.6985074);\n"
"const float light_front_diffuse = 0.6 * 0.3;\n"
"const float ambient = 0.3;\n"
"const float emission = 0.25;\n"
"const float scaling_factor = 1.5;\n"
"uniform mat4 view_matrix;\n"
"uniform mat4 projection_matrix;\n"
"uniform samplerBuffer positionsTex;\n"
"uniform samplerBuffer heightWidthAngleTex;\n"
"uniform samplerBuffer colorsTex;\n"
"uniform isamplerBuffer segmentIndexTex;\n"
"in vec3 in_position;\n"
"in vec3 in_normal;\n"
"out vec3 color;\n"
"vec3 decode_color(float color) {\n"
" int c = int(round(color));\n"
" int r = (c >> 16) & 0xFF;\n"
" int g = (c >> 8) & 0xFF;\n"
" int b = (c >> 0) & 0xFF;\n"
" float f = 1.0 / 255.0f;\n"
" return f * vec3(r, g, b);\n"
"}\n"
"float lighting(vec3 eye_position, vec3 eye_normal) {\n"
" float top_diffuse = light_top_diffuse * max(dot(eye_normal, light_top_dir), 0.0);\n"
" float front_diffuse = light_front_diffuse * max(dot(eye_normal, light_front_dir), 0.0);\n"
" float top_specular = light_top_specular * pow(max(dot(-normalize(eye_position), reflect(-light_top_dir, eye_normal)), 0.0), light_top_shininess);\n"
" return ambient + top_diffuse + front_diffuse + top_specular + emission;\n"
"}\n"
"void main() {\n"
" int id = texelFetch(segmentIndexTex, gl_InstanceID).r;\n"
" vec2 height_width = texelFetch(heightWidthAngleTex, id).xy;\n"
" vec3 offset = texelFetch(positionsTex, id).xyz - vec3(0.0, 0.0, 0.5 * height_width.x);\n"
" height_width *= scaling_factor;\n"
" mat3 scale_matrix = mat3(\n"
" height_width.y, 0.0, 0.0,\n"
" 0.0, height_width.y, 0.0,\n"
" 0.0, 0.0, height_width.x);\n"
" vec3 eye_position = (view_matrix * vec4(scale_matrix * in_position + offset, 1.0)).xyz;\n"
" vec3 eye_normal = (view_matrix * vec4(in_normal, 0.0)).xyz;\n"
" vec3 color_base = decode_color(texelFetch(colorsTex, id).x);\n"
" color = color_base * lighting(eye_position, eye_normal);\n"
" gl_Position = projection_matrix * vec4(eye_position, 1.0);\n"
"}\n";
static const char* Options_Fragment_Shader_ES =
"#version 300 es\n"
"precision highp float;\n"
"in vec3 color;\n"
"out vec4 fragmentColor;\n"
"void main() {\n"
" fragmentColor = vec4(color, 1.0);\n"
"}\n";
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
static const char* Cog_Marker_Vertex_Shader_ES =
"#version 300 es\n"
"const vec3 light_top_dir = vec3(-0.4574957, 0.4574957, 0.7624929);\n"
"const float light_top_diffuse = 0.6 * 0.8;\n"
"const float light_top_specular = 0.6 * 0.125;\n"
"const float light_top_shininess = 20.0;\n"
"const vec3 light_front_dir = vec3(0.6985074, 0.1397015, 0.6985074);\n"
"const float light_front_diffuse = 0.6 * 0.3;\n"
"const float ambient = 0.3;\n"
"const float emission = 0.25;\n"
"uniform vec3 world_center_position;\n"
"uniform float scale_factor;\n"
"uniform mat4 view_matrix;\n"
"uniform mat4 projection_matrix;\n"
"in vec3 in_position;\n"
"in vec3 in_normal;\n"
"out float intensity;\n"
"out vec3 world_position;\n"
"float lighting(vec3 eye_position, vec3 eye_normal) {\n"
" float top_diffuse = light_top_diffuse * max(dot(eye_normal, light_top_dir), 0.0);\n"
" float front_diffuse = light_front_diffuse * max(dot(eye_normal, light_front_dir), 0.0);\n"
" float top_specular = light_top_specular * pow(max(dot(-normalize(eye_position), reflect(-light_top_dir, eye_normal)), 0.0), light_top_shininess);\n"
" return ambient + top_diffuse + front_diffuse + top_specular + emission;\n"
"}\n"
"void main() {\n"
" world_position = scale_factor * in_position + world_center_position;\n"
" vec3 eye_position = (view_matrix * vec4(world_position, 1.0)).xyz;\n"
" vec3 eye_normal = (view_matrix * vec4(in_normal, 0.0)).xyz;\n"
" intensity = lighting(eye_position, eye_normal);\n"
" gl_Position = projection_matrix * vec4(eye_position, 1.0);\n"
"}\n";
static const char* Cog_Marker_Fragment_Shader_ES =
"#version 300 es\n"
"precision highp float;\n"
"const vec3 BLACK = vec3(0.05);\n"
"const vec3 WHITE = vec3(0.95);\n"
"uniform vec3 world_center_position;\n"
"in float intensity;\n"
"in vec3 world_position;\n"
"out vec4 out_color;\n"
"void main()\n"
"{\n"
" vec3 delta = world_position - world_center_position;\n"
" vec3 color = delta.x * delta.y * delta.z > 0.0 ? BLACK : WHITE;\n"
" out_color = intensity * vec4(color, 1.0);\n"
"}\n";
static const char* Tool_Marker_Vertex_Shader_ES =
"#version 300 es\n"
"const vec3 light_top_dir = vec3(-0.4574957, 0.4574957, 0.7624929);\n"
"const float light_top_diffuse = 0.6 * 0.8;\n"
"const float light_top_specular = 0.6 * 0.125;\n"
"const float light_top_shininess = 20.0;\n"
"const vec3 light_front_dir = vec3(0.6985074, 0.1397015, 0.6985074);\n"
"const float light_front_diffuse = 0.6 * 0.3;\n"
"const float ambient = 0.3;\n"
"const float emission = 0.25;\n"
"uniform vec3 world_origin;\n"
"uniform float scale_factor;\n"
"uniform mat4 view_matrix;\n"
"uniform mat4 projection_matrix;\n"
"uniform vec4 color_base;\n"
"in vec3 in_position;\n"
"in vec3 in_normal;\n"
"out vec4 color;\n"
"float lighting(vec3 eye_position, vec3 eye_normal) {\n"
" float top_diffuse = light_top_diffuse * max(dot(eye_normal, light_top_dir), 0.0);\n"
" float front_diffuse = light_front_diffuse * max(dot(eye_normal, light_front_dir), 0.0);\n"
" float top_specular = light_top_specular * pow(max(dot(-normalize(eye_position), reflect(-light_top_dir, eye_normal)), 0.0), light_top_shininess);\n"
" return ambient + top_diffuse + front_diffuse + top_specular + emission;\n"
"}\n"
"void main() {\n"
" vec3 world_position = scale_factor * in_position + world_origin;\n"
" vec3 eye_position = (view_matrix * vec4(world_position, 1.0)).xyz;\n"
" // no need of normal matrix as the scaling is uniform\n"
" vec3 eye_normal = (view_matrix * vec4(in_normal, 0.0)).xyz;\n"
" color = vec4(color_base.rgb * lighting(eye_position, eye_normal), color_base.a);\n"
" gl_Position = projection_matrix * vec4(eye_position, 1.0);\n"
"}\n";
static const char* Tool_Marker_Fragment_Shader_ES =
"#version 300 es\n"
"precision highp float;\n"
"in vec4 color;\n"
"out vec4 fragment_color;\n"
"void main() {\n"
" fragment_color = color;\n"
"}\n";
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
} // namespace libvgcode
#endif // VGCODE_SHADERSES_HPP

View File

@ -5,6 +5,7 @@
#include "ViewerImpl.hpp"
#include "../include/GCodeInputData.hpp"
#include "Shaders.hpp"
#include "ShadersES.hpp"
#include "OpenGLUtils.hpp"
#include "Utils.hpp"
@ -322,14 +323,21 @@ void ViewerImpl::init()
if (m_initialized)
return;
if (!load_opengl())
throw std::runtime_error("LibVGCode was unable to initialize OpenGL\n");
if (!check_opengl_version())
throw std::runtime_error("LibVGCode requires an active OpenGL context based on OpenGL 3.2 or higher:\n");
if (!OpenGLWrapper::load_opengl()) {
if (!OpenGLWrapper::is_detected())
throw std::runtime_error("LibVGCode was unable to initialize OpenGL. A valid OpenGL context is missing.\n");
else if (!OpenGLWrapper::is_valid()) {
if (OpenGLWrapper::is_es())
throw std::runtime_error("LibVGCode requires an active OpenGL ES context based on OpenGL ES 2.0 or higher:\n");
else
throw std::runtime_error("LibVGCode requires an active OpenGL context based on OpenGL 3.2 or higher:\n");
}
}
// segments shader
m_segments_shader_id = init_shader("segments", Segments_Vertex_Shader, Segments_Fragment_Shader);
m_segments_shader_id = OpenGLWrapper::is_es() ?
init_shader("segments", Segments_Vertex_Shader_ES, Segments_Fragment_Shader_ES) :
init_shader("segments", Segments_Vertex_Shader, Segments_Fragment_Shader);
m_uni_segments_view_matrix_id = glGetUniformLocation(m_segments_shader_id, "view_matrix");
m_uni_segments_projection_matrix_id = glGetUniformLocation(m_segments_shader_id, "projection_matrix");
@ -350,7 +358,9 @@ void ViewerImpl::init()
m_segment_template.init();
// options shader
m_options_shader_id = init_shader("options", Options_Vertex_Shader, Options_Fragment_Shader);
m_options_shader_id = OpenGLWrapper::is_es() ?
init_shader("options", Options_Vertex_Shader_ES, Options_Fragment_Shader_ES) :
init_shader("options", Options_Vertex_Shader, Options_Fragment_Shader);
m_uni_options_view_matrix_id = glGetUniformLocation(m_options_shader_id, "view_matrix");
m_uni_options_projection_matrix_id = glGetUniformLocation(m_options_shader_id, "projection_matrix");
@ -370,7 +380,9 @@ void ViewerImpl::init()
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
// cog marker shader
m_cog_marker_shader_id = init_shader("cog_marker", Cog_Marker_Vertex_Shader, Cog_Marker_Fragment_Shader);
m_cog_marker_shader_id = OpenGLWrapper::is_es() ?
init_shader("cog_marker", Cog_Marker_Vertex_Shader_ES, Cog_Marker_Fragment_Shader_ES) :
init_shader("cog_marker", Cog_Marker_Vertex_Shader, Cog_Marker_Fragment_Shader);
m_uni_cog_marker_world_center_position = glGetUniformLocation(m_cog_marker_shader_id, "world_center_position");
m_uni_cog_marker_scale_factor = glGetUniformLocation(m_cog_marker_shader_id, "scale_factor");
@ -385,7 +397,9 @@ void ViewerImpl::init()
m_cog_marker.init(32, 1.0f);
// tool marker shader
m_tool_marker_shader_id = init_shader("tool_marker", Tool_Marker_Vertex_Shader, Tool_Marker_Fragment_Shader);
m_tool_marker_shader_id = OpenGLWrapper::is_es() ?
init_shader("tool_marker", Tool_Marker_Vertex_Shader_ES, Tool_Marker_Fragment_Shader_ES) :
init_shader("tool_marker", Tool_Marker_Vertex_Shader, Tool_Marker_Fragment_Shader);
m_uni_tool_marker_world_origin = glGetUniformLocation(m_tool_marker_shader_id, "world_origin");
m_uni_tool_marker_scale_factor = glGetUniformLocation(m_tool_marker_shader_id, "scale_factor");