mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-05 06:41:10 +00:00
The segments come in print order, bottom layer first, which seen from above is back to front: every hidden fragment is shaded before the one that covers it, and on an integrated GPU that overdraw is most of the frame. Drawing the instances last to first whenever the camera looks down lets the depth test reject the hidden fragments instead. Side views and views from below keep the print order, and the shadow-caster pass is unchanged.
2344 lines
94 KiB
C++
2344 lines
94 KiB
C++
///|/ Copyright (c) Prusa Research 2023 Enrico Turri @enricoturri1966, Pavel Mikuš @Godrak, Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka
|
|
///|/
|
|
///|/ libvgcode is released under the terms of the AGPLv3 or higher
|
|
///|/
|
|
#include "ViewerImpl.hpp"
|
|
#include "../include/GCodeInputData.hpp"
|
|
#include "Shaders.hpp"
|
|
#include "ShadersES.hpp"
|
|
#include "OpenGLUtils.hpp"
|
|
#include "Utils.hpp"
|
|
|
|
#include <map>
|
|
#include <assert.h>
|
|
#include <stdexcept>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <numeric>
|
|
|
|
namespace libvgcode {
|
|
|
|
template<class T, class O = T>
|
|
using IntegerOnly = std::enable_if_t<std::is_integral<T>::value, O>;
|
|
|
|
// Rounding up.
|
|
// 1.5 is rounded to 2
|
|
// 1.49 is rounded to 1
|
|
// 0.5 is rounded to 1,
|
|
// 0.49 is rounded to 0
|
|
// -0.5 is rounded to 0,
|
|
// -0.51 is rounded to -1,
|
|
// -1.5 is rounded to -1.
|
|
// -1.51 is rounded to -2.
|
|
// If input is not a valid float (it is infinity NaN or if it does not fit)
|
|
// the float to int conversion produces a max int on Intel and +-max int on ARM.
|
|
template<typename I>
|
|
inline IntegerOnly<I, I> fast_round_up(double a)
|
|
{
|
|
// Why does Java Math.round(0.49999999999999994) return 1?
|
|
// https://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1
|
|
return a == 0.49999999999999994 ? I(0) : I(floor(a + 0.5));
|
|
}
|
|
|
|
// Round to a bin with minimum two digits resolution.
|
|
// Equivalent to conversion to string with sprintf(buf, "%.2g", value) and conversion back to float, but faster.
|
|
static float round_to_bin(const float value)
|
|
{
|
|
// assert(value >= 0);
|
|
constexpr float const scale[5] = { 100.f, 1000.f, 10000.f, 100000.f, 1000000.f };
|
|
constexpr float const invscale[5] = { 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f };
|
|
constexpr float const threshold[5] = { 0.095f, 0.0095f, 0.00095f, 0.000095f, 0.0000095f };
|
|
// Scaling factor, pointer to the tables above.
|
|
int i = 0;
|
|
// While the scaling factor is not yet large enough to get two integer digits after scaling and rounding:
|
|
for (; value < threshold[i] && i < 4; ++i);
|
|
// At least on MSVC std::round() calls a complex function, which is pretty expensive.
|
|
// our fast_round_up is much cheaper and it could be inlined.
|
|
// return std::round(value * scale[i]) * invscale[i];
|
|
double a = value * scale[i];
|
|
assert(std::abs(a) < double(std::numeric_limits<int64_t>::max()));
|
|
return fast_round_up<int64_t>(a) * invscale[i];
|
|
}
|
|
|
|
static Mat4x4 inverse(const Mat4x4& m)
|
|
{
|
|
// ref: https://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix
|
|
|
|
Mat4x4 inv;
|
|
|
|
inv[0] = m[5] * m[10] * m[15] -
|
|
m[5] * m[11] * m[14] -
|
|
m[9] * m[6] * m[15] +
|
|
m[9] * m[7] * m[14] +
|
|
m[13] * m[6] * m[11] -
|
|
m[13] * m[7] * m[10];
|
|
|
|
inv[4] = -m[4] * m[10] * m[15] +
|
|
m[4] * m[11] * m[14] +
|
|
m[8] * m[6] * m[15] -
|
|
m[8] * m[7] * m[14] -
|
|
m[12] * m[6] * m[11] +
|
|
m[12] * m[7] * m[10];
|
|
|
|
inv[8] = m[4] * m[9] * m[15] -
|
|
m[4] * m[11] * m[13] -
|
|
m[8] * m[5] * m[15] +
|
|
m[8] * m[7] * m[13] +
|
|
m[12] * m[5] * m[11] -
|
|
m[12] * m[7] * m[9];
|
|
|
|
inv[12] = -m[4] * m[9] * m[14] +
|
|
m[4] * m[10] * m[13] +
|
|
m[8] * m[5] * m[14] -
|
|
m[8] * m[6] * m[13] -
|
|
m[12] * m[5] * m[10] +
|
|
m[12] * m[6] * m[9];
|
|
|
|
inv[1] = -m[1] * m[10] * m[15] +
|
|
m[1] * m[11] * m[14] +
|
|
m[9] * m[2] * m[15] -
|
|
m[9] * m[3] * m[14] -
|
|
m[13] * m[2] * m[11] +
|
|
m[13] * m[3] * m[10];
|
|
|
|
inv[5] = m[0] * m[10] * m[15] -
|
|
m[0] * m[11] * m[14] -
|
|
m[8] * m[2] * m[15] +
|
|
m[8] * m[3] * m[14] +
|
|
m[12] * m[2] * m[11] -
|
|
m[12] * m[3] * m[10];
|
|
|
|
inv[9] = -m[0] * m[9] * m[15] +
|
|
m[0] * m[11] * m[13] +
|
|
m[8] * m[1] * m[15] -
|
|
m[8] * m[3] * m[13] -
|
|
m[12] * m[1] * m[11] +
|
|
m[12] * m[3] * m[9];
|
|
|
|
inv[13] = m[0] * m[9] * m[14] -
|
|
m[0] * m[10] * m[13] -
|
|
m[8] * m[1] * m[14] +
|
|
m[8] * m[2] * m[13] +
|
|
m[12] * m[1] * m[10] -
|
|
m[12] * m[2] * m[9];
|
|
|
|
inv[2] = m[1] * m[6] * m[15] -
|
|
m[1] * m[7] * m[14] -
|
|
m[5] * m[2] * m[15] +
|
|
m[5] * m[3] * m[14] +
|
|
m[13] * m[2] * m[7] -
|
|
m[13] * m[3] * m[6];
|
|
|
|
inv[6] = -m[0] * m[6] * m[15] +
|
|
m[0] * m[7] * m[14] +
|
|
m[4] * m[2] * m[15] -
|
|
m[4] * m[3] * m[14] -
|
|
m[12] * m[2] * m[7] +
|
|
m[12] * m[3] * m[6];
|
|
|
|
inv[10] = m[0] * m[5] * m[15] -
|
|
m[0] * m[7] * m[13] -
|
|
m[4] * m[1] * m[15] +
|
|
m[4] * m[3] * m[13] +
|
|
m[12] * m[1] * m[7] -
|
|
m[12] * m[3] * m[5];
|
|
|
|
inv[14] = -m[0] * m[5] * m[14] +
|
|
m[0] * m[6] * m[13] +
|
|
m[4] * m[1] * m[14] -
|
|
m[4] * m[2] * m[13] -
|
|
m[12] * m[1] * m[6] +
|
|
m[12] * m[2] * m[5];
|
|
|
|
inv[3] = -m[1] * m[6] * m[11] +
|
|
m[1] * m[7] * m[10] +
|
|
m[5] * m[2] * m[11] -
|
|
m[5] * m[3] * m[10] -
|
|
m[9] * m[2] * m[7] +
|
|
m[9] * m[3] * m[6];
|
|
|
|
inv[7] = m[0] * m[6] * m[11] -
|
|
m[0] * m[7] * m[10] -
|
|
m[4] * m[2] * m[11] +
|
|
m[4] * m[3] * m[10] +
|
|
m[8] * m[2] * m[7] -
|
|
m[8] * m[3] * m[6];
|
|
|
|
inv[11] = -m[0] * m[5] * m[11] +
|
|
m[0] * m[7] * m[9] +
|
|
m[4] * m[1] * m[11] -
|
|
m[4] * m[3] * m[9] -
|
|
m[8] * m[1] * m[7] +
|
|
m[8] * m[3] * m[5];
|
|
|
|
inv[15] = m[0] * m[5] * m[10] -
|
|
m[0] * m[6] * m[9] -
|
|
m[4] * m[1] * m[10] +
|
|
m[4] * m[2] * m[9] +
|
|
m[8] * m[1] * m[6] -
|
|
m[8] * m[2] * m[5];
|
|
|
|
float det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
|
|
assert(det != 0.0f);
|
|
|
|
det = 1.0f / det;
|
|
|
|
std::array<float, 16> ret = {};
|
|
for (int i = 0; i < 16; ++i) {
|
|
ret[i] = inv[i] * det;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::string check_shader(GLuint handle)
|
|
{
|
|
std::string ret;
|
|
GLint params;
|
|
glsafe(glGetShaderiv(handle, GL_COMPILE_STATUS, ¶ms));
|
|
if (params == GL_FALSE) {
|
|
glsafe(glGetShaderiv(handle, GL_INFO_LOG_LENGTH, ¶ms));
|
|
ret.resize(params);
|
|
glsafe(glGetShaderInfoLog(handle, params, ¶ms, ret.data()));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::string check_program(GLuint handle)
|
|
{
|
|
std::string ret;
|
|
GLint params;
|
|
glsafe(glGetProgramiv(handle, GL_LINK_STATUS, ¶ms));
|
|
if (params == GL_FALSE) {
|
|
glsafe(glGetProgramiv(handle, GL_INFO_LOG_LENGTH, ¶ms));
|
|
ret.resize(params);
|
|
glsafe(glGetProgramInfoLog(handle, params, ¶ms, ret.data()));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
unsigned int init_shader(const std::string& shader_name, const char* vertex_shader, const char* fragment_shader)
|
|
{
|
|
const GLuint vs_id = glCreateShader(GL_VERTEX_SHADER);
|
|
glcheck();
|
|
glsafe(glShaderSource(vs_id, 1, &vertex_shader, nullptr));
|
|
glsafe(glCompileShader(vs_id));
|
|
std::string res = check_shader(vs_id);
|
|
if (!res.empty()) {
|
|
glsafe(glDeleteShader(vs_id));
|
|
throw std::runtime_error("LibVGCode: Unable to compile vertex shader:\n" + shader_name + "\n" + res + "\n");
|
|
}
|
|
|
|
const GLuint fs_id = glCreateShader(GL_FRAGMENT_SHADER);
|
|
glcheck();
|
|
glsafe(glShaderSource(fs_id, 1, &fragment_shader, nullptr));
|
|
glsafe(glCompileShader(fs_id));
|
|
res = check_shader(fs_id);
|
|
if (!res.empty()) {
|
|
glsafe(glDeleteShader(vs_id));
|
|
glsafe(glDeleteShader(fs_id));
|
|
throw std::runtime_error("LibVGCode: Unable to compile fragment shader:\n" + shader_name + "\n" + res + "\n");
|
|
}
|
|
|
|
const GLuint shader_id = glCreateProgram();
|
|
glcheck();
|
|
glsafe(glAttachShader(shader_id, vs_id));
|
|
glsafe(glAttachShader(shader_id, fs_id));
|
|
glsafe(glLinkProgram(shader_id));
|
|
res = check_program(shader_id);
|
|
if (!res.empty()) {
|
|
glsafe(glDetachShader(shader_id, vs_id));
|
|
glsafe(glDetachShader(shader_id, fs_id));
|
|
glsafe(glDeleteShader(vs_id));
|
|
glsafe(glDeleteShader(fs_id));
|
|
glsafe(glDeleteProgram(shader_id));
|
|
throw std::runtime_error("LibVGCode: Unable to link shader program:\n" + shader_name + "\n" + res + "\n");
|
|
}
|
|
|
|
glsafe(glDetachShader(shader_id, vs_id));
|
|
glsafe(glDetachShader(shader_id, fs_id));
|
|
glsafe(glDeleteShader(vs_id));
|
|
glsafe(glDeleteShader(fs_id));
|
|
return shader_id;
|
|
}
|
|
|
|
static void delete_textures(unsigned int& id)
|
|
{
|
|
if (id != 0) {
|
|
glsafe(glDeleteTextures(1, &id));
|
|
id = 0;
|
|
}
|
|
}
|
|
|
|
static void delete_buffers(unsigned int& id)
|
|
{
|
|
if (id != 0) {
|
|
glsafe(glDeleteBuffers(1, &id));
|
|
id = 0;
|
|
}
|
|
}
|
|
|
|
static const std::array<Color, size_t(EGCodeExtrusionRole::COUNT)> DEFAULT_EXTRUSION_ROLES_COLORS = { {
|
|
{ 230, 179, 179 }, // None
|
|
{ 255, 230, 77 }, // Perimeter
|
|
{ 255, 125, 56 }, // ExternalPerimeter
|
|
{ 31, 31, 255 }, // OverhangPerimeter
|
|
{ 176, 48, 41 }, // InternalInfill
|
|
{ 150, 84, 204 }, // SolidInfill
|
|
{ 240, 64, 64 }, // TopSolidInfill
|
|
{ 255, 140, 105 }, // Ironing
|
|
{ 77, 128, 186 }, // BridgeInfill
|
|
{ 255, 255, 255 }, // GapFill
|
|
{ 0, 135, 110 }, // Skirt
|
|
{ 0, 255, 0 }, // SupportMaterial
|
|
{ 0, 128, 0 }, // SupportMaterialInterface
|
|
{ 179, 227, 171 }, // WipeTower
|
|
{ 94, 209, 148 }, // Custom
|
|
// ORCA
|
|
{ 102, 92, 199 }, // BottomSurface
|
|
{ 77, 128, 186 }, // InternalBridgeInfill
|
|
{ 0, 59, 110 }, // Brim
|
|
{ 0, 64, 0 }, // SupportTransition
|
|
{ 128, 128, 128 }, // Mixed
|
|
} };
|
|
|
|
static const std::array<Color, size_t(EOptionType::COUNT)> DEFAULT_OPTIONS_COLORS{ {
|
|
{ 56, 72, 155 }, // Travels
|
|
{ 255, 255, 0 }, // Wipes
|
|
{ 205, 34, 214 }, // Retractions
|
|
{ 73, 173, 207 }, // Unretractions
|
|
{ 230, 230, 230 }, // Seams
|
|
{ 193, 190, 99 }, // ToolChanges
|
|
{ 218, 148, 139 }, // ColorChanges
|
|
{ 82, 240, 131 }, // PausePrints
|
|
{ 226, 210, 67 } // CustomGCodes
|
|
} };
|
|
|
|
#ifdef ENABLE_OPENGL_ES
|
|
static std::pair<size_t, size_t> width_height(size_t count)
|
|
{
|
|
std::pair<size_t, size_t> ret;
|
|
ret.first = std::min(count, OpenGLWrapper::max_texture_size());
|
|
size_t rows_count = count / ret.first;
|
|
if (count > rows_count * ret.first)
|
|
++rows_count;
|
|
ret.second = std::min(rows_count, OpenGLWrapper::max_texture_size());
|
|
return ret;
|
|
}
|
|
|
|
void ViewerImpl::TextureData::init(size_t vertices_count)
|
|
{
|
|
if (vertices_count == 0)
|
|
return;
|
|
|
|
m_width = std::min(vertices_count, OpenGLWrapper::max_texture_size());
|
|
size_t rows_count = vertices_count / m_width;
|
|
if (vertices_count > rows_count * m_width)
|
|
++rows_count;
|
|
m_height = std::min(rows_count, OpenGLWrapper::max_texture_size());
|
|
m_count = rows_count / m_height;
|
|
if (rows_count > m_count * m_height)
|
|
++m_count;
|
|
|
|
const std::pair<size_t, size_t> test = width_height(vertices_count);
|
|
assert(test.first == m_width);
|
|
assert(test.second == m_height);
|
|
|
|
m_tex_ids = std::vector<TexIds>(m_count);
|
|
}
|
|
|
|
void ViewerImpl::TextureData::set_positions(const std::vector<Vec3>& positions)
|
|
{
|
|
if (m_count == 0)
|
|
return;
|
|
|
|
for (TexIds& ids : m_tex_ids) {
|
|
delete_textures(ids.positions.first);
|
|
ids.positions.second = 0;
|
|
}
|
|
|
|
m_positions_size = 0;
|
|
|
|
if (positions.empty())
|
|
return;
|
|
|
|
int curr_bound_texture = 0;
|
|
glsafe(glGetIntegerv(GL_TEXTURE_BINDING_2D, &curr_bound_texture));
|
|
int curr_unpack_alignment = 0;
|
|
glsafe(glGetIntegerv(GL_UNPACK_ALIGNMENT, &curr_unpack_alignment));
|
|
|
|
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
|
|
|
const size_t tex_capacity = max_texture_capacity();
|
|
size_t remaining = positions.size();
|
|
for (size_t i = 0; i < m_count; ++i) {
|
|
const auto [w, h] = width_height(std::min(remaining, tex_capacity));
|
|
const size_t offset = i * tex_capacity;
|
|
|
|
glsafe(glGenTextures(1, &m_tex_ids[i].positions.first));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, m_tex_ids[i].positions.first));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
|
if (remaining >= tex_capacity) {
|
|
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, static_cast<GLsizei>(w), static_cast<GLsizei>(h), 0, GL_RGB, GL_FLOAT, &positions[offset]));
|
|
m_tex_ids[i].positions.second = w * h;
|
|
}
|
|
else {
|
|
// the last row is only partially fitted with data, send it separately
|
|
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, static_cast<GLsizei>(w), static_cast<GLsizei>(h), 0, GL_RGB, GL_FLOAT, nullptr));
|
|
glsafe(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, static_cast<GLsizei>(w), static_cast<GLsizei>(h - 1), GL_RGB, GL_FLOAT, &positions[offset]));
|
|
glsafe(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, static_cast<GLsizei>(h - 1), static_cast<GLsizei>(remaining % w), 1, GL_RGB, GL_FLOAT, &positions[offset + w * (h - 1)]));
|
|
m_tex_ids[i].positions.second = w * (h - 1) + remaining % w;
|
|
}
|
|
m_positions_size += m_tex_ids[i].positions.second * sizeof(Vec3);
|
|
|
|
remaining = (remaining > tex_capacity) ? remaining - tex_capacity: 0;
|
|
}
|
|
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, curr_bound_texture));
|
|
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, curr_unpack_alignment));
|
|
}
|
|
|
|
void ViewerImpl::TextureData::set_heights_widths_angles(const std::vector<Vec3>& heights_widths_angles)
|
|
{
|
|
if (m_count == 0)
|
|
return;
|
|
|
|
for (TexIds& ids : m_tex_ids) {
|
|
delete_textures(ids.heights_widths_angles.first);
|
|
ids.heights_widths_angles.second = 0;
|
|
}
|
|
|
|
m_height_width_angle_size = 0;
|
|
|
|
if (heights_widths_angles.empty())
|
|
return;
|
|
|
|
int curr_bound_texture = 0;
|
|
glsafe(glGetIntegerv(GL_TEXTURE_BINDING_2D, &curr_bound_texture));
|
|
int curr_unpack_alignment = 0;
|
|
glsafe(glGetIntegerv(GL_UNPACK_ALIGNMENT, &curr_unpack_alignment));
|
|
|
|
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
|
|
|
const size_t tex_capacity = max_texture_capacity();
|
|
size_t remaining = heights_widths_angles.size();
|
|
for (size_t i = 0; i < m_count; ++i) {
|
|
const auto [w, h] = width_height(std::min(remaining, tex_capacity));
|
|
const size_t offset = i * tex_capacity;
|
|
|
|
glsafe(glGenTextures(1, &m_tex_ids[i].heights_widths_angles.first));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, m_tex_ids[i].heights_widths_angles.first));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
|
if (remaining >= tex_capacity) {
|
|
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, static_cast<GLsizei>(w), static_cast<GLsizei>(h), 0, GL_RGB, GL_FLOAT, &heights_widths_angles[offset]));
|
|
m_tex_ids[i].heights_widths_angles.second = w * h;
|
|
}
|
|
else {
|
|
// the last row is only partially fitted with data, send it separately
|
|
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, static_cast<GLsizei>(w), static_cast<GLsizei>(h), 0, GL_RGB, GL_FLOAT, nullptr));
|
|
glsafe(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, static_cast<GLsizei>(w), static_cast<GLsizei>(h - 1), GL_RGB, GL_FLOAT, &heights_widths_angles[offset]));
|
|
glsafe(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, static_cast<GLsizei>(h - 1), static_cast<GLsizei>(remaining % w), 1, GL_RGB, GL_FLOAT, &heights_widths_angles[offset + w * (h - 1)]));
|
|
m_tex_ids[i].heights_widths_angles.second = w * (h - 1) + remaining % w;
|
|
}
|
|
m_height_width_angle_size += m_tex_ids[i].heights_widths_angles.second * sizeof(Vec3);
|
|
|
|
remaining = (remaining > tex_capacity) ? remaining - tex_capacity : 0;
|
|
}
|
|
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, curr_bound_texture));
|
|
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, curr_unpack_alignment));
|
|
}
|
|
|
|
void ViewerImpl::TextureData::set_colors(const std::vector<float>& colors)
|
|
{
|
|
if (m_count == 0)
|
|
return;
|
|
|
|
for (TexIds& ids : m_tex_ids) {
|
|
delete_textures(ids.colors.first);
|
|
ids.colors.second = 0;
|
|
}
|
|
|
|
m_colors_size = 0;
|
|
|
|
if (colors.empty())
|
|
return;
|
|
|
|
int curr_bound_texture = 0;
|
|
glsafe(glGetIntegerv(GL_TEXTURE_BINDING_2D, &curr_bound_texture));
|
|
int curr_unpack_alignment = 0;
|
|
glsafe(glGetIntegerv(GL_UNPACK_ALIGNMENT, &curr_unpack_alignment));
|
|
|
|
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
|
|
|
const size_t tex_capacity = max_texture_capacity();
|
|
size_t remaining = colors.size();
|
|
for (size_t i = 0; i < m_count; ++i) {
|
|
const auto [w, h] = width_height(std::min(remaining, tex_capacity));
|
|
const size_t offset = i * tex_capacity;
|
|
|
|
glsafe(glGenTextures(1, &m_tex_ids[i].colors.first));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, m_tex_ids[i].colors.first));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
|
if (remaining >= tex_capacity) {
|
|
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, static_cast<GLsizei>(w), static_cast<GLsizei>(h), 0, GL_RED, GL_FLOAT, &colors[offset]));
|
|
m_tex_ids[i].colors.second = w * h;
|
|
}
|
|
else {
|
|
// the last row is only partially fitted with data, send it separately
|
|
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, static_cast<GLsizei>(w), static_cast<GLsizei>(h), 0, GL_RED, GL_FLOAT, nullptr));
|
|
glsafe(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, static_cast<GLsizei>(w), static_cast<GLsizei>(h - 1), GL_RED, GL_FLOAT, &colors[offset]));
|
|
glsafe(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, static_cast<GLsizei>(h - 1), static_cast<GLsizei>(remaining % w), 1, GL_RED, GL_FLOAT, &colors[offset + w * (h - 1)]));
|
|
m_tex_ids[i].colors.second = w * (h - 1) + remaining % w;
|
|
}
|
|
m_colors_size += m_tex_ids[i].colors.second * sizeof(float);
|
|
|
|
remaining = (remaining > tex_capacity) ? remaining - tex_capacity : 0;
|
|
}
|
|
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, curr_bound_texture));
|
|
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, curr_unpack_alignment));
|
|
}
|
|
|
|
void ViewerImpl::TextureData::set_enabled_segments(const std::vector<uint32_t>& enabled_segments)
|
|
{
|
|
if (m_count == 0)
|
|
return;
|
|
|
|
for (TexIds& ids : m_tex_ids) {
|
|
delete_textures(ids.enabled_segments.first);
|
|
ids.enabled_segments.second = 0;
|
|
}
|
|
|
|
m_enabled_segments_size = 0;
|
|
|
|
if (enabled_segments.empty())
|
|
return;
|
|
|
|
int curr_bound_texture = 0;
|
|
glsafe(glGetIntegerv(GL_TEXTURE_BINDING_2D, &curr_bound_texture));
|
|
int curr_unpack_alignment = 0;
|
|
glsafe(glGetIntegerv(GL_UNPACK_ALIGNMENT, &curr_unpack_alignment));
|
|
|
|
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
|
|
|
const size_t tex_capacity = max_texture_capacity();
|
|
size_t curr_tex_id = 0;
|
|
std::vector<uint32_t> curr_segments;
|
|
for (size_t i = 0; i < enabled_segments.size(); ++i) {
|
|
uint32_t seg = enabled_segments[i];
|
|
const bool new_tex = static_cast<size_t>(seg) > (curr_tex_id + 1) * tex_capacity;
|
|
if (!new_tex)
|
|
curr_segments.push_back(seg - static_cast<uint32_t>(curr_tex_id * tex_capacity));
|
|
if (i + 1 == enabled_segments.size() || new_tex) {
|
|
const auto [w, h] = width_height(curr_segments.size());
|
|
|
|
glsafe(glGenTextures(1, &m_tex_ids[curr_tex_id].enabled_segments.first));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, m_tex_ids[curr_tex_id].enabled_segments.first));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
|
if (curr_segments.size() == tex_capacity) {
|
|
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, static_cast<GLsizei>(w), static_cast<GLsizei>(h), 0, GL_RED_INTEGER, GL_UNSIGNED_INT, curr_segments.data()));
|
|
m_tex_ids[curr_tex_id].enabled_segments.second = w * h;
|
|
}
|
|
else {
|
|
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, static_cast<GLsizei>(w), static_cast<GLsizei>(h), 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr));
|
|
if (h == 1) {
|
|
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, static_cast<GLsizei>(w), 1, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, curr_segments.data()));
|
|
m_tex_ids[curr_tex_id].enabled_segments.second = w;
|
|
}
|
|
else {
|
|
// the last row is only partially fitted with data, send it separately
|
|
glsafe(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, static_cast<GLsizei>(w), static_cast<GLsizei>(h - 1), GL_RED_INTEGER, GL_UNSIGNED_INT, curr_segments.data()));
|
|
glsafe(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, static_cast<GLsizei>(h - 1), static_cast<GLsizei>(curr_segments.size() % w), 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &curr_segments[w * (h - 1)]));
|
|
m_tex_ids[curr_tex_id].enabled_segments.second = w * (h - 1) + curr_segments.size() % w;
|
|
}
|
|
}
|
|
m_enabled_segments_size += m_tex_ids[curr_tex_id].enabled_segments.second * sizeof(uint32_t);
|
|
if (new_tex) {
|
|
curr_segments.clear();
|
|
++curr_tex_id;
|
|
curr_segments.push_back(seg - static_cast<uint32_t>(curr_tex_id * tex_capacity));
|
|
}
|
|
}
|
|
}
|
|
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, curr_bound_texture));
|
|
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, curr_unpack_alignment));
|
|
}
|
|
|
|
void ViewerImpl::TextureData::set_enabled_options(const std::vector<uint32_t>& enabled_options)
|
|
{
|
|
if (m_count == 0)
|
|
return;
|
|
|
|
for (TexIds& ids : m_tex_ids) {
|
|
delete_textures(ids.enabled_options.first);
|
|
ids.enabled_options.second = 0;
|
|
}
|
|
|
|
m_enabled_options_size = 0;
|
|
|
|
if (enabled_options.empty())
|
|
return;
|
|
|
|
int curr_bound_texture = 0;
|
|
glsafe(glGetIntegerv(GL_TEXTURE_BINDING_2D, &curr_bound_texture));
|
|
int curr_unpack_alignment = 0;
|
|
glsafe(glGetIntegerv(GL_UNPACK_ALIGNMENT, &curr_unpack_alignment));
|
|
|
|
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
|
|
|
const size_t tex_capacity = max_texture_capacity();
|
|
size_t curr_tex_id = 0;
|
|
std::vector<uint32_t> curr_options;
|
|
for (size_t i = 0; i < enabled_options.size(); ++i) {
|
|
uint32_t opt = enabled_options[i];
|
|
const bool new_tex = static_cast<size_t>(opt) > (curr_tex_id + 1) * tex_capacity;
|
|
if (!new_tex)
|
|
curr_options.push_back(opt - static_cast<uint32_t>(curr_tex_id * tex_capacity));
|
|
if (i + 1 == enabled_options.size() || new_tex) {
|
|
const auto [w, h] = width_height(curr_options.size());
|
|
|
|
glsafe(glGenTextures(1, &m_tex_ids[curr_tex_id].enabled_options.first));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, m_tex_ids[curr_tex_id].enabled_options.first));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
|
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
|
if (curr_options.size() == tex_capacity) {
|
|
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, static_cast<GLsizei>(w), static_cast<GLsizei>(h), 0, GL_RED_INTEGER, GL_UNSIGNED_INT, curr_options.data()));
|
|
m_tex_ids[curr_tex_id].enabled_options.second = w * h;
|
|
}
|
|
else {
|
|
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, static_cast<GLsizei>(w), static_cast<GLsizei>(h), 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr));
|
|
if (h == 1) {
|
|
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, static_cast<GLsizei>(w), 1, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, curr_options.data()));
|
|
m_tex_ids[curr_tex_id].enabled_options.second = w;
|
|
}
|
|
else {
|
|
// the last row is only partially fitted with data, send it separately
|
|
glsafe(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, static_cast<GLsizei>(w), static_cast<GLsizei>(h - 1), GL_RED_INTEGER, GL_UNSIGNED_INT, curr_options.data()));
|
|
glsafe(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, static_cast<GLsizei>(h - 1), static_cast<GLsizei>(curr_options.size() % w), 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &curr_options[w * (h - 1)]));
|
|
m_tex_ids[curr_tex_id].enabled_options.second = w * (h - 1) + curr_options.size() % w;
|
|
}
|
|
}
|
|
m_enabled_options_size += m_tex_ids[curr_tex_id].enabled_options.second * sizeof(uint32_t);
|
|
if (new_tex) {
|
|
curr_options.clear();
|
|
++curr_tex_id;
|
|
curr_options.push_back(opt - static_cast<uint32_t>(curr_tex_id * tex_capacity));
|
|
}
|
|
}
|
|
}
|
|
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, curr_bound_texture));
|
|
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, curr_unpack_alignment));
|
|
}
|
|
|
|
void ViewerImpl::TextureData::reset()
|
|
{
|
|
for (TexIds& ids : m_tex_ids) {
|
|
delete_textures(ids.enabled_options.first);
|
|
delete_textures(ids.enabled_segments.first);
|
|
delete_textures(ids.colors.first);
|
|
delete_textures(ids.heights_widths_angles.first);
|
|
delete_textures(ids.positions.first);
|
|
}
|
|
m_tex_ids.clear();
|
|
|
|
m_width = 0;
|
|
m_height = 0;
|
|
m_count = 0;
|
|
|
|
m_positions_size = 0;
|
|
m_height_width_angle_size = 0;
|
|
m_colors_size = 0;
|
|
m_enabled_segments_size = 0;
|
|
m_enabled_options_size = 0;
|
|
}
|
|
|
|
std::pair<unsigned int, size_t> ViewerImpl::TextureData::get_positions_tex_id(size_t id) const
|
|
{
|
|
assert(id < m_tex_ids.size());
|
|
return m_tex_ids[id].positions;
|
|
}
|
|
|
|
std::pair<unsigned int, size_t> ViewerImpl::TextureData::get_heights_widths_angles_tex_id(size_t id) const
|
|
{
|
|
assert(id < m_tex_ids.size());
|
|
return m_tex_ids[id].heights_widths_angles;
|
|
}
|
|
|
|
std::pair<unsigned int, size_t> ViewerImpl::TextureData::get_colors_tex_id(size_t id) const
|
|
{
|
|
assert(id < m_tex_ids.size());
|
|
return m_tex_ids[id].colors;
|
|
}
|
|
|
|
std::pair<unsigned int, size_t> ViewerImpl::TextureData::get_enabled_segments_tex_id(size_t id) const
|
|
{
|
|
assert(id < m_tex_ids.size());
|
|
return m_tex_ids[id].enabled_segments;
|
|
}
|
|
|
|
std::pair<unsigned int, size_t> ViewerImpl::TextureData::get_enabled_options_tex_id(size_t id) const
|
|
{
|
|
assert(id < m_tex_ids.size());
|
|
return m_tex_ids[id].enabled_options;
|
|
}
|
|
|
|
size_t ViewerImpl::TextureData::get_enabled_segments_count() const
|
|
{
|
|
size_t ret = 0;
|
|
for (size_t i = 0; i < m_count; ++i) {
|
|
ret += m_tex_ids[i].enabled_segments.second;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
size_t ViewerImpl::TextureData::get_enabled_options_count() const
|
|
{
|
|
size_t ret = 0;
|
|
for (size_t i = 0; i < m_count; ++i) {
|
|
ret += m_tex_ids[i].enabled_options.second;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
size_t ViewerImpl::TextureData::get_used_gpu_memory() const
|
|
{
|
|
size_t ret = 0;
|
|
ret += m_positions_size;
|
|
ret += m_height_width_angle_size;
|
|
ret += m_colors_size;
|
|
ret += m_enabled_segments_size;
|
|
ret += m_enabled_options_size;
|
|
return ret;
|
|
}
|
|
#endif // ENABLE_OPENGL_ES
|
|
|
|
ViewerImpl::ViewerImpl()
|
|
{
|
|
reset_default_extrusion_roles_colors();
|
|
reset_default_options_colors();
|
|
}
|
|
|
|
void ViewerImpl::init(const std::string& opengl_context_version)
|
|
{
|
|
if (m_initialized)
|
|
return;
|
|
|
|
if (!OpenGLWrapper::load_opengl(opengl_context_version)) {
|
|
if (OpenGLWrapper::is_valid_context())
|
|
throw std::runtime_error("LibVGCode was unable to initialize the GLAD library.\n");
|
|
else {
|
|
#ifdef ENABLE_OPENGL_ES
|
|
throw std::runtime_error("LibVGCode requires an OpenGL ES context based on OpenGL ES 3.0 or higher.\n");
|
|
#else
|
|
throw std::runtime_error("LibVGCode requires an OpenGL context based on OpenGL 3.2 or higher.\n");
|
|
#endif // ENABLE_OPENGL_ES
|
|
}
|
|
}
|
|
|
|
// segments shader
|
|
#ifdef ENABLE_OPENGL_ES
|
|
m_segments_shader_id = init_shader("segments", Segments_Vertex_Shader_ES, Segments_Fragment_Shader_ES);
|
|
#else
|
|
m_segments_shader_id = init_shader("segments", Segments_Vertex_Shader, Segments_Fragment_Shader);
|
|
#endif // ENABLE_OPENGL_ES
|
|
|
|
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");
|
|
m_uni_segments_camera_position_id = glGetUniformLocation(m_segments_shader_id, "camera_position");
|
|
m_uni_segments_positions_tex_id = glGetUniformLocation(m_segments_shader_id, "position_tex");
|
|
m_uni_segments_height_width_angle_tex_id = glGetUniformLocation(m_segments_shader_id, "height_width_angle_tex");
|
|
m_uni_segments_colors_tex_id = glGetUniformLocation(m_segments_shader_id, "color_tex");
|
|
m_uni_segments_segment_index_tex_id = glGetUniformLocation(m_segments_shader_id, "segment_index_tex");
|
|
m_uni_segments_reverse_order_id = glGetUniformLocation(m_segments_shader_id, "reverse_order");
|
|
m_uni_segments_instance_count_id = glGetUniformLocation(m_segments_shader_id, "instance_count");
|
|
// ORCA: realistic view
|
|
m_uni_segments_shadow_map_id = glGetUniformLocation(m_segments_shader_id, "shadow_map");
|
|
m_uni_segments_shadow_light_vp_id = glGetUniformLocation(m_segments_shader_id, "shadow_light_vp");
|
|
m_uni_segments_shadow_intensity_id = glGetUniformLocation(m_segments_shader_id, "shadow_intensity");
|
|
m_uni_segments_shadow_map_texel_id = glGetUniformLocation(m_segments_shader_id, "shadow_map_texel");
|
|
m_uni_segments_exposure_id = glGetUniformLocation(m_segments_shader_id, "exposure");
|
|
m_uni_segments_saturation_id = glGetUniformLocation(m_segments_shader_id, "saturation");
|
|
m_uni_segments_bias_scale_id = glGetUniformLocation(m_segments_shader_id, "bias_scale");
|
|
glcheck();
|
|
assert(m_uni_segments_view_matrix_id != -1 &&
|
|
m_uni_segments_projection_matrix_id != -1 &&
|
|
m_uni_segments_camera_position_id != -1 &&
|
|
m_uni_segments_positions_tex_id != -1 &&
|
|
m_uni_segments_height_width_angle_tex_id != -1 &&
|
|
m_uni_segments_colors_tex_id != -1 &&
|
|
m_uni_segments_segment_index_tex_id != -1);
|
|
|
|
m_segment_template.init();
|
|
|
|
// options shader
|
|
#ifdef ENABLE_OPENGL_ES
|
|
m_options_shader_id = init_shader("options", Options_Vertex_Shader_ES, Options_Fragment_Shader_ES);
|
|
#else
|
|
m_options_shader_id = init_shader("options", Options_Vertex_Shader, Options_Fragment_Shader);
|
|
#endif // ENABLE_OPENGL_ES
|
|
|
|
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");
|
|
m_uni_options_positions_tex_id = glGetUniformLocation(m_options_shader_id, "position_tex");
|
|
m_uni_options_height_width_angle_tex_id = glGetUniformLocation(m_options_shader_id, "height_width_angle_tex");
|
|
m_uni_options_colors_tex_id = glGetUniformLocation(m_options_shader_id, "color_tex");
|
|
m_uni_options_segment_index_tex_id = glGetUniformLocation(m_options_shader_id, "segment_index_tex");
|
|
glcheck();
|
|
assert(m_uni_options_view_matrix_id != -1 &&
|
|
m_uni_options_projection_matrix_id != -1 &&
|
|
m_uni_options_positions_tex_id != -1 &&
|
|
m_uni_options_height_width_angle_tex_id != -1 &&
|
|
m_uni_options_colors_tex_id != -1 &&
|
|
m_uni_options_segment_index_tex_id != -1);
|
|
|
|
m_option_template.init(16);
|
|
|
|
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
// cog marker shader
|
|
#ifdef ENABLE_OPENGL_ES
|
|
m_cog_marker_shader_id = init_shader("cog_marker", Cog_Marker_Vertex_Shader_ES, Cog_Marker_Fragment_Shader_ES);
|
|
#else
|
|
m_cog_marker_shader_id = init_shader("cog_marker", Cog_Marker_Vertex_Shader, Cog_Marker_Fragment_Shader);
|
|
#endif // ENABLE_OPENGL_ES
|
|
|
|
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");
|
|
m_uni_cog_marker_view_matrix = glGetUniformLocation(m_cog_marker_shader_id, "view_matrix");
|
|
m_uni_cog_marker_projection_matrix = glGetUniformLocation(m_cog_marker_shader_id, "projection_matrix");
|
|
glcheck();
|
|
assert(m_uni_cog_marker_world_center_position != -1 &&
|
|
m_uni_cog_marker_scale_factor != -1 &&
|
|
m_uni_cog_marker_view_matrix != -1 &&
|
|
m_uni_cog_marker_projection_matrix != -1);
|
|
|
|
m_cog_marker.init(32, 1.0f);
|
|
|
|
// tool marker shader
|
|
#ifdef ENABLE_OPENGL_ES
|
|
m_tool_marker_shader_id = init_shader("tool_marker", Tool_Marker_Vertex_Shader_ES, Tool_Marker_Fragment_Shader_ES);
|
|
#else
|
|
m_tool_marker_shader_id = init_shader("tool_marker", Tool_Marker_Vertex_Shader, Tool_Marker_Fragment_Shader);
|
|
#endif // ENABLE_OPENGL_ES
|
|
|
|
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");
|
|
m_uni_tool_marker_view_matrix = glGetUniformLocation(m_tool_marker_shader_id, "view_matrix");
|
|
m_uni_tool_marker_projection_matrix = glGetUniformLocation(m_tool_marker_shader_id, "projection_matrix");
|
|
m_uni_tool_marker_color_base = glGetUniformLocation(m_tool_marker_shader_id, "color_base");
|
|
|
|
glcheck();
|
|
assert(m_uni_tool_marker_world_origin != -1 &&
|
|
m_uni_tool_marker_scale_factor != -1 &&
|
|
m_uni_tool_marker_view_matrix != -1 &&
|
|
m_uni_tool_marker_projection_matrix != -1 &&
|
|
m_uni_tool_marker_color_base != -1);
|
|
|
|
m_tool_marker.init(32, 2.0f, 4.0f, 1.0f, 8.0f);
|
|
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
|
|
m_initialized = true;
|
|
}
|
|
|
|
void ViewerImpl::shutdown()
|
|
{
|
|
reset();
|
|
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
m_tool_marker.shutdown();
|
|
m_cog_marker.shutdown();
|
|
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
m_option_template.shutdown();
|
|
m_segment_template.shutdown();
|
|
if (m_options_shader_id != 0) {
|
|
glsafe(glDeleteProgram(m_options_shader_id));
|
|
m_options_shader_id = 0;
|
|
}
|
|
if (m_segments_shader_id != 0) {
|
|
glsafe(glDeleteProgram(m_segments_shader_id));
|
|
m_segments_shader_id = 0;
|
|
}
|
|
m_initialized = false;
|
|
OpenGLWrapper::unload_opengl();
|
|
}
|
|
|
|
void ViewerImpl::reset()
|
|
{
|
|
m_layers.reset();
|
|
m_view_range.reset();
|
|
m_extrusion_roles.reset();
|
|
m_options.clear();
|
|
m_used_extruders.clear();
|
|
m_total_time = { 0.0f, 0.0f };
|
|
m_travels_time = { 0.0f, 0.0f };
|
|
m_vertices.clear();
|
|
m_vertices_colors.clear();
|
|
// swap rather than clear: these are sized by the print, and a reset means the memory
|
|
// should go back, not sit reserved until the next load
|
|
for (std::vector<float>& times : m_layer_start_times)
|
|
std::vector<float>().swap(times);
|
|
std::vector<uint32_t>().swap(m_layer_first_vertex);
|
|
std::vector<float>().swap(m_colors_scratch);
|
|
m_valid_lines_bitset.clear();
|
|
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
m_cog_marker.reset();
|
|
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
|
|
#ifdef ENABLE_OPENGL_ES
|
|
m_texture_data.reset();
|
|
#else
|
|
m_enabled_segments_count = 0;
|
|
m_enabled_options_count = 0;
|
|
|
|
m_settings_used_for_ranges = std::nullopt;
|
|
|
|
delete_textures(m_enabled_options_tex_id);
|
|
delete_buffers(m_enabled_options_buf_id);
|
|
delete_textures(m_enabled_segments_tex_id);
|
|
delete_buffers(m_enabled_segments_buf_id);
|
|
delete_textures(m_colors_tex_id);
|
|
delete_buffers(m_colors_buf_id);
|
|
delete_textures(m_heights_widths_angles_tex_id);
|
|
delete_buffers(m_heights_widths_angles_buf_id);
|
|
delete_textures(m_positions_tex_id);
|
|
delete_buffers(m_positions_buf_id);
|
|
#endif // ENABLE_OPENGL_ES
|
|
}
|
|
|
|
// On some graphic cards texture buffers using GL_RGB32F format do not work, see:
|
|
// https://dev.prusa3d.com/browse/SPE-2411
|
|
// https://github.com/prusa3d/PrusaSlicer/issues/12908
|
|
// To let all drivers be happy, we use GL_RGBA32F format, so we need to add an extra (currently unused) float
|
|
// to position and heights_widths_angles vectors
|
|
using Vec4 = std::array<float, 4>;
|
|
|
|
static void extract_pos_and_or_hwa(const std::vector<PathVertex>& vertices, float travels_radius, float wipes_radius, BitSet<>& valid_lines_bitset,
|
|
std::vector<Vec4>* positions = nullptr, std::vector<Vec4>* heights_widths_angles = nullptr, bool update_bitset = false) {
|
|
static constexpr const Vec3 ZERO = { 0.0f, 0.0f, 0.0f };
|
|
if (positions == nullptr && heights_widths_angles == nullptr)
|
|
return;
|
|
if (vertices.empty())
|
|
return;
|
|
if (travels_radius <= 0.0f || wipes_radius <= 0.0f)
|
|
return;
|
|
|
|
if (positions != nullptr)
|
|
positions->reserve(vertices.size());
|
|
if (heights_widths_angles != nullptr)
|
|
heights_widths_angles->reserve(vertices.size());
|
|
for (size_t i = 0; i < vertices.size(); ++i) {
|
|
const PathVertex& v = vertices[i];
|
|
const EMoveType move_type = v.type;
|
|
const bool prev_line_valid = i > 0 && valid_lines_bitset[i - 1];
|
|
const Vec3 prev_line = prev_line_valid ? v.position - vertices[i - 1].position : ZERO;
|
|
const bool this_line_valid = i + 1 < vertices.size() &&
|
|
vertices[i + 1].position != v.position &&
|
|
vertices[i + 1].type == move_type &&
|
|
move_type != EMoveType::Seam;
|
|
const Vec3 this_line = this_line_valid ? vertices[i + 1].position - v.position : ZERO;
|
|
|
|
if (this_line_valid) {
|
|
// there is a valid path between point i and i+1.
|
|
}
|
|
else {
|
|
// the connection is invalid, there should be no line rendered, ever
|
|
if (update_bitset)
|
|
valid_lines_bitset.reset(i);
|
|
}
|
|
|
|
if (positions != nullptr) {
|
|
// the last component is a dummy float to comply with GL_RGBA32F format
|
|
Vec4 position = { v.position[0], v.position[1], v.position[2], 0.0f };
|
|
if (move_type == EMoveType::Extrude)
|
|
// push down extrusion vertices by half height to render them at the right z
|
|
position[2] -= 0.5f * v.height;
|
|
positions->emplace_back(position);
|
|
}
|
|
|
|
if (heights_widths_angles != nullptr) {
|
|
float height = 0.0f;
|
|
float width = 0.0f;
|
|
if (v.is_travel()) {
|
|
height = travels_radius;
|
|
width = travels_radius;
|
|
}
|
|
else if (v.is_wipe()) {
|
|
height = wipes_radius;
|
|
width = wipes_radius;
|
|
}
|
|
else {
|
|
height = v.height;
|
|
width = v.width;
|
|
}
|
|
|
|
// ORCA: Set bias for wipes and options to avoid z-fighting
|
|
float bias = 0.0f;
|
|
if (v.is_wipe())
|
|
bias = 0.05f;
|
|
else if (v.is_option())
|
|
bias = 0.1f;
|
|
|
|
// the last component is a dummy float to comply with GL_RGBA32F format
|
|
// ORCA: Pass bias to shader
|
|
heights_widths_angles->push_back({ height, width,
|
|
std::atan2(prev_line[0] * this_line[1] - prev_line[1] * this_line[0], dot(prev_line, this_line)), bias });
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewerImpl::load(GCodeInputData&& gcode_data)
|
|
{
|
|
if (!m_initialized)
|
|
return;
|
|
|
|
if (gcode_data.vertices.empty())
|
|
return;
|
|
|
|
reset();
|
|
|
|
m_vertices = std::move(gcode_data.vertices);
|
|
m_tool_colors = std::move(gcode_data.tools_colors);
|
|
m_color_print_colors = std::move(gcode_data.color_print_colors);
|
|
m_vertices_colors.resize(m_vertices.size());
|
|
|
|
m_settings.spiral_vase_mode = gcode_data.spiral_vase_mode;
|
|
|
|
for (size_t i = 0; i < m_vertices.size(); ++i) {
|
|
const PathVertex& v = m_vertices[i];
|
|
|
|
m_layers.update(v, static_cast<uint32_t>(i));
|
|
|
|
for (size_t j = 0; j < TIME_MODES_COUNT; ++j) {
|
|
m_total_time[j] += v.times[j];
|
|
if (v.type == EMoveType::Travel)
|
|
m_travels_time[j] += v.times[j];
|
|
}
|
|
|
|
const EOptionType option_type = move_type_to_option(v.type);
|
|
if (option_type != EOptionType::COUNT)
|
|
m_options.emplace_back(option_type);
|
|
|
|
if (v.type == EMoveType::Extrude) {
|
|
m_extrusion_roles.add(v.role, v.times);
|
|
|
|
auto estruder_it = m_used_extruders.find(v.extruder_id);
|
|
if (estruder_it == m_used_extruders.end())
|
|
estruder_it = m_used_extruders.insert({ v.extruder_id, std::vector<ColorPrint>() }).first;
|
|
if (estruder_it->second.empty() || estruder_it->second.back().color_id != v.color_id) {
|
|
const ColorPrint cp = { v.extruder_id, v.color_id, v.layer_id, m_total_time };
|
|
estruder_it->second.emplace_back(cp);
|
|
}
|
|
}
|
|
|
|
if (i > 0) {
|
|
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
// updates calculation for center of gravity
|
|
if (v.type == EMoveType::Extrude &&
|
|
v.role != EGCodeExtrusionRole::Skirt &&
|
|
v.role != EGCodeExtrusionRole::SupportMaterial &&
|
|
v.role != EGCodeExtrusionRole::SupportMaterialInterface &&
|
|
v.role != EGCodeExtrusionRole::WipeTower &&
|
|
v.role != EGCodeExtrusionRole::Custom &&
|
|
v.role != EGCodeExtrusionRole::Brim &&
|
|
v.role != EGCodeExtrusionRole::SupportTransition) {
|
|
m_cog_marker.update(0.5f * (v.position + m_vertices[i - 1].position), v.weight);
|
|
}
|
|
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
}
|
|
}
|
|
|
|
// Populate layer_duration for each vertex from the accumulated layer times
|
|
for (PathVertex& v : m_vertices) {
|
|
v.layer_duration = m_layers.get_layer_time(m_settings.time_mode, static_cast<size_t>(v.layer_id));
|
|
}
|
|
|
|
// Index of the first vertex of each layer, walked back to front so that a layer with no
|
|
// vertex of its own inherits the next layer's index and the array stays non-decreasing.
|
|
if (!m_layers.empty()) {
|
|
const uint32_t vertices_count = static_cast<uint32_t>(m_vertices.size());
|
|
m_layer_first_vertex.assign(m_layers.count(), vertices_count);
|
|
for (uint32_t i = vertices_count; i > 0; --i) {
|
|
const uint32_t layer_id = m_vertices[i - 1].layer_id;
|
|
if (layer_id < m_layer_first_vertex.size())
|
|
m_layer_first_vertex[layer_id] = i - 1;
|
|
}
|
|
for (size_t i = m_layer_first_vertex.size() - 1; i > 0; --i)
|
|
m_layer_first_vertex[i - 1] = std::min(m_layer_first_vertex[i - 1], m_layer_first_vertex[i]);
|
|
|
|
// the running time at each layer's first vertex, summed in vertex order so that
|
|
// get_estimated_time_at() matches a full accumulation exactly
|
|
std::array<float, TIME_MODES_COUNT> running{};
|
|
for (std::vector<float>& times : m_layer_start_times)
|
|
times.assign(m_layer_first_vertex.size(), 0.0f);
|
|
size_t layer = 0;
|
|
for (size_t i = 0; i <= m_vertices.size(); ++i) {
|
|
for (; layer < m_layer_first_vertex.size() && m_layer_first_vertex[layer] == i; ++layer) {
|
|
for (size_t j = 0; j < TIME_MODES_COUNT; ++j)
|
|
m_layer_start_times[j][layer] = running[j];
|
|
}
|
|
if (i < m_vertices.size()) {
|
|
for (size_t j = 0; j < TIME_MODES_COUNT; ++j)
|
|
running[j] += m_vertices[i].times[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!m_layers.empty())
|
|
m_layers.set_view_range(0, static_cast<uint32_t>(m_layers.count()) - 1);
|
|
|
|
std::sort(m_options.begin(), m_options.end());
|
|
m_options.erase(std::unique(m_options.begin(), m_options.end()), m_options.end());
|
|
m_options.shrink_to_fit();
|
|
|
|
// reset segments visibility bitset
|
|
m_valid_lines_bitset = BitSet<>(m_vertices.size());
|
|
m_valid_lines_bitset.setAll();
|
|
|
|
if (m_settings.time_mode != ETimeMode::Normal && m_total_time[static_cast<size_t>(m_settings.time_mode)] == 0.0f)
|
|
m_settings.time_mode = ETimeMode::Normal;
|
|
|
|
// buffers to send to gpu
|
|
// the last component is a dummy float to comply with GL_RGBA32F format
|
|
std::vector<Vec4> positions;
|
|
std::vector<Vec4> heights_widths_angles;
|
|
positions.reserve(m_vertices.size());
|
|
heights_widths_angles.reserve(m_vertices.size());
|
|
extract_pos_and_or_hwa(m_vertices, m_travels_radius, m_wipes_radius, m_valid_lines_bitset, &positions, &heights_widths_angles, true);
|
|
|
|
if (!positions.empty()) {
|
|
#ifdef ENABLE_OPENGL_ES
|
|
m_texture_data.init(positions.size());
|
|
// create and fill position textures
|
|
m_texture_data.set_positions(positions);
|
|
// create and fill height, width and angle textures
|
|
m_texture_data.set_heights_widths_angles(heights_widths_angles);
|
|
#else
|
|
m_positions_tex_size = positions.size() * sizeof(Vec3);
|
|
m_height_width_angle_tex_size = heights_widths_angles.size() * sizeof(Vec3);
|
|
|
|
int old_bound_texture = 0;
|
|
glsafe(glGetIntegerv(GL_TEXTURE_BINDING_BUFFER, &old_bound_texture));
|
|
|
|
// create and fill positions buffer
|
|
glsafe(glGenBuffers(1, &m_positions_buf_id));
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_positions_buf_id));
|
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, positions.size() * sizeof(Vec4), positions.data(), GL_STATIC_DRAW));
|
|
glsafe(glGenTextures(1, &m_positions_tex_id));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_positions_tex_id));
|
|
|
|
// create and fill height, width and angles buffer
|
|
glsafe(glGenBuffers(1, &m_heights_widths_angles_buf_id));
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_heights_widths_angles_buf_id));
|
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, heights_widths_angles.size() * sizeof(Vec4), heights_widths_angles.data(), GL_DYNAMIC_DRAW));
|
|
glsafe(glGenTextures(1, &m_heights_widths_angles_tex_id));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_heights_widths_angles_tex_id));
|
|
|
|
// create (but do not fill) colors buffer (data is set in update_colors())
|
|
glsafe(glGenBuffers(1, &m_colors_buf_id));
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_colors_buf_id));
|
|
glsafe(glGenTextures(1, &m_colors_tex_id));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
|
|
|
// create (but do not fill) enabled segments buffer (data is set in update_enabled_entities())
|
|
glsafe(glGenBuffers(1, &m_enabled_segments_buf_id));
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_buf_id));
|
|
glsafe(glGenTextures(1, &m_enabled_segments_tex_id));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_segments_tex_id));
|
|
|
|
// create (but do not fill) enabled options buffer (data is set in update_enabled_entities())
|
|
glsafe(glGenBuffers(1, &m_enabled_options_buf_id));
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_options_buf_id));
|
|
glsafe(glGenTextures(1, &m_enabled_options_tex_id));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
|
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, old_bound_texture));
|
|
#endif // ENABLE_OPENGL_ES
|
|
}
|
|
|
|
update_view_full_range();
|
|
m_view_range.set_visible(m_view_range.get_enabled());
|
|
update_enabled_entities();
|
|
update_colors();
|
|
}
|
|
|
|
void ViewerImpl::update_enabled_entities()
|
|
{
|
|
if (m_vertices.empty())
|
|
return;
|
|
|
|
std::vector<uint32_t> enabled_segments;
|
|
std::vector<uint32_t> enabled_options;
|
|
Interval range = m_view_range.get_visible();
|
|
|
|
// when top layer only visualization is enabled, we need to render
|
|
// all the toolpaths in the other layers as grayed, so extend the range
|
|
// to contain them
|
|
if (m_settings.top_layer_only_view_range)
|
|
range[0] = m_view_range.get_full()[0];
|
|
|
|
// to show the options at the current tool marker position we need to extend the range by one extra step
|
|
if (m_vertices[range[1]].is_option() && range[1] < static_cast<uint32_t>(m_vertices.size()) - 1)
|
|
++range[1];
|
|
|
|
if (m_settings.spiral_vase_mode) {
|
|
// when spiral vase mode is enabled and only one layer is shown, extend the range by one step
|
|
const Interval& layers_range = m_layers.get_view_range();
|
|
if (layers_range[0] > 0 && layers_range[0] == layers_range[1])
|
|
--range[0];
|
|
}
|
|
|
|
for (size_t i = range[0]; i < range[1]; ++i) {
|
|
const PathVertex& v = m_vertices[i];
|
|
|
|
if (!m_valid_lines_bitset[i] && !v.is_option())
|
|
continue;
|
|
if (v.is_travel()) {
|
|
if (!m_settings.options_visibility[size_t(EOptionType::Travels)])
|
|
continue;
|
|
}
|
|
else if (v.is_wipe()) {
|
|
if (!m_settings.options_visibility[size_t(EOptionType::Wipes)])
|
|
continue;
|
|
}
|
|
else if (v.is_option()) {
|
|
if (!m_settings.options_visibility[size_t(move_type_to_option(v.type))])
|
|
continue;
|
|
}
|
|
else if (v.is_extrusion()) {
|
|
if (!m_settings.extrusion_roles_visibility[size_t(v.role)])
|
|
continue;
|
|
}
|
|
else
|
|
continue;
|
|
|
|
if (v.is_option())
|
|
enabled_options.push_back(static_cast<uint32_t>(i));
|
|
else
|
|
enabled_segments.push_back(static_cast<uint32_t>(i));
|
|
}
|
|
|
|
#ifdef ENABLE_OPENGL_ES
|
|
m_texture_data.set_enabled_segments(enabled_segments);
|
|
m_texture_data.set_enabled_options(enabled_options);
|
|
#else
|
|
m_enabled_segments_count = enabled_segments.size();
|
|
m_enabled_options_count = enabled_options.size();
|
|
|
|
m_enabled_segments_tex_size = enabled_segments.size() * sizeof(uint32_t);
|
|
m_enabled_options_tex_size = enabled_options.size() * sizeof(uint32_t);
|
|
|
|
// update gpu buffer for enabled segments
|
|
assert(m_enabled_segments_buf_id > 0);
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_buf_id));
|
|
if (!enabled_segments.empty())
|
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, enabled_segments.size() * sizeof(uint32_t), enabled_segments.data(), GL_STATIC_DRAW));
|
|
else
|
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, 0, nullptr, GL_STATIC_DRAW));
|
|
|
|
// update gpu buffer for enabled options
|
|
assert(m_enabled_options_buf_id > 0);
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_options_buf_id));
|
|
if (!enabled_options.empty())
|
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, enabled_options.size() * sizeof(uint32_t), enabled_options.data(), GL_STATIC_DRAW));
|
|
else
|
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, 0, nullptr, GL_STATIC_DRAW));
|
|
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
|
#endif // ENABLE_OPENGL_ES
|
|
|
|
m_settings.update_enabled_entities = false;
|
|
}
|
|
|
|
static float encode_color(const Color& color) {
|
|
const int r = static_cast<int>(color[0]);
|
|
const int g = static_cast<int>(color[1]);
|
|
const int b = static_cast<int>(color[2]);
|
|
const int i_color = r << 16 | g << 8 | b;
|
|
return static_cast<float>(i_color);
|
|
}
|
|
|
|
// ORCA: returns the encoded color scaled towards black by 'brightness', preserving its hue.
|
|
// 1.0 = no change, 0.0 = black.
|
|
static float encode_color_dimmed(const Color& color, float brightness) {
|
|
const int r = static_cast<int>(color[0] * brightness);
|
|
const int g = static_cast<int>(color[1] * brightness);
|
|
const int b = static_cast<int>(color[2] * brightness);
|
|
const int i_color = r << 16 | g << 8 | b;
|
|
return static_cast<float>(i_color);
|
|
}
|
|
|
|
|
|
void ViewerImpl::update_colors_texture()
|
|
{
|
|
#if !defined(ENABLE_OPENGL_ES)
|
|
if (m_colors_buf_id == 0)
|
|
return;
|
|
#endif // ENABLE_OPENGL_ES
|
|
|
|
const size_t top_layer_id = m_settings.top_layer_only_view_range ? m_layers.get_view_range()[1] : 0;
|
|
const bool color_top_layer_only = m_view_range.get_full()[1] != m_view_range.get_visible()[1];
|
|
|
|
// ORCA: when dim_previous_layers is enabled, darken every layer (keeping its color) except the
|
|
// one(s) the layer slider is being scrubbed to, so that only those are shown at full brightness.
|
|
// A slider thumb marks a layer as inspected only once it is moved away from
|
|
// its end of the print: the upper one while it is below the last layer (or while the moves
|
|
// slider is not at the end of the layer), the lower one while it is above the first layer, so
|
|
// trimming the print from the bottom lights up the lowest visible layer and using the slider as
|
|
// a range lights up both ends. When neither thumb is moved the whole print is rendered normally.
|
|
// Gated on top-layer-only mode, which the greying path below also keys off of, so that the moves
|
|
// slider still animates normally across all layers when that mode is disabled.
|
|
const Interval& layers_range = m_layers.get_view_range();
|
|
const bool inspecting_top_layer = layers_range[1] + 1 < m_layers.count() || color_top_layer_only;
|
|
const bool inspecting_bottom_layer = layers_range[0] > 0;
|
|
const bool dim_previous_layers = m_settings.dim_previous_layers && m_settings.top_layer_only_view_range &&
|
|
!m_layers.empty() && (inspecting_top_layer || inspecting_bottom_layer);
|
|
|
|
// Based on current settings and slider position, we might want to render some
|
|
// vertices as dark grey (or darkened, see above). Use either that or the normal color (from the cache).
|
|
// Reused across calls: this runs on every slider tick, and the allocation alone is
|
|
// 4 bytes per vertex of the whole print each time.
|
|
std::vector<float>& colors = m_colors_scratch;
|
|
colors.resize(m_vertices_colors.size());
|
|
assert(colors.size() == m_vertices.size() && m_vertices_colors.size() == m_vertices.size());
|
|
for (size_t i=0; i<m_vertices.size(); ++i) {
|
|
const PathVertex& v = m_vertices[i];
|
|
const bool keep_spiral_seam = m_settings.spiral_vase_mode && i == m_view_range.get_enabled()[0];
|
|
// ORCA: layers kept at full brightness by the dimming above are excluded from the greying below too
|
|
const bool inspected_layer = dim_previous_layers &&
|
|
((inspecting_top_layer && v.layer_id == layers_range[1]) ||
|
|
(inspecting_bottom_layer && v.layer_id == layers_range[0]));
|
|
if (dim_previous_layers && !inspected_layer && !keep_spiral_seam)
|
|
colors[i] = encode_color_dimmed(get_vertex_color(v), m_settings.dim_previous_layers_brightness);
|
|
else if (!inspected_layer && color_top_layer_only && v.layer_id < top_layer_id && !keep_spiral_seam)
|
|
colors[i] = encode_color(DUMMY_COLOR);
|
|
else
|
|
colors[i] = m_vertices_colors[i];
|
|
}
|
|
|
|
#ifdef ENABLE_OPENGL_ES
|
|
if (!colors.empty())
|
|
// update gpu buffer for colors
|
|
m_texture_data.set_colors(colors);
|
|
#else
|
|
m_colors_tex_size = colors.size() * sizeof(float);
|
|
|
|
// update gpu buffer for colors
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_colors_buf_id));
|
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, colors.size() * sizeof(float), colors.data(), GL_STATIC_DRAW));
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
|
#endif // ENABLE_OPENGL_ES
|
|
}
|
|
|
|
|
|
void ViewerImpl::update_colors()
|
|
{
|
|
|
|
|
|
if (!m_used_extruders.empty()) {
|
|
// ensure that the number of defined tool colors matches the max id of the used extruders
|
|
const size_t max_used_extruder_id = 1 + static_cast<size_t>(m_used_extruders.rbegin()->first);
|
|
const size_t tool_colors_size = m_tool_colors.size();
|
|
if (m_tool_colors.size() < max_used_extruder_id) {
|
|
for (size_t i = 0; i < max_used_extruder_id - tool_colors_size; ++i) {
|
|
m_tool_colors.emplace_back(DUMMY_COLOR);
|
|
}
|
|
}
|
|
}
|
|
|
|
update_color_ranges();
|
|
|
|
// Recalculate "normal" colors of all the vertices for current view settings.
|
|
// If some part of the preview should be rendered in dark grey, it is taken
|
|
// care of in update_colors_texture. That is to avoid the need to recalculate
|
|
// the "normal" color on every slider move.
|
|
for (size_t i = 0; i < m_vertices.size(); ++i)
|
|
m_vertices_colors[i] = encode_color(get_vertex_color(m_vertices[i]));
|
|
|
|
update_colors_texture();
|
|
m_settings.update_colors = false;
|
|
}
|
|
|
|
void ViewerImpl::apply_pending_updates()
|
|
{
|
|
if (m_settings.update_view_full_range)
|
|
update_view_full_range();
|
|
|
|
if (m_settings.update_enabled_entities)
|
|
update_enabled_entities();
|
|
|
|
if (m_settings.update_colors)
|
|
update_colors();
|
|
}
|
|
|
|
void ViewerImpl::render(const Mat4x4& view_matrix, const Mat4x4& projection_matrix)
|
|
{
|
|
apply_pending_updates();
|
|
|
|
const Mat4x4 inv_view_matrix = inverse(view_matrix);
|
|
const Vec3 camera_position = { inv_view_matrix[12], inv_view_matrix[13], inv_view_matrix[14] };
|
|
render_segments(view_matrix, projection_matrix, camera_position);
|
|
render_options(view_matrix, projection_matrix);
|
|
|
|
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
if (m_settings.options_visibility[size_t(EOptionType::ToolMarker)])
|
|
render_tool_marker(view_matrix, projection_matrix);
|
|
if (m_settings.options_visibility[size_t(EOptionType::CenterOfGravity)])
|
|
render_cog_marker(view_matrix, projection_matrix);
|
|
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
}
|
|
|
|
void ViewerImpl::render_shadow_casters(const Mat4x4& view_matrix, const Mat4x4& projection_matrix, const Vec3& light_position)
|
|
{
|
|
apply_pending_updates();
|
|
|
|
// Only the extrusions and travels cast: the option markers are indicators, not material.
|
|
m_rendering_shadow_casters = true;
|
|
render_segments(view_matrix, projection_matrix, light_position);
|
|
m_rendering_shadow_casters = false;
|
|
}
|
|
|
|
void ViewerImpl::set_shadow_map(int texture_unit, const Mat4x4& light_view_projection, float intensity, float texel_size)
|
|
{
|
|
m_shadow_map_texture_unit = texture_unit;
|
|
m_shadow_light_vp = light_view_projection;
|
|
m_shadow_intensity = intensity;
|
|
m_shadow_map_texel = texel_size;
|
|
}
|
|
|
|
void ViewerImpl::set_tone(float exposure, float saturation)
|
|
{
|
|
m_exposure = exposure;
|
|
m_saturation = saturation;
|
|
}
|
|
|
|
void ViewerImpl::set_view_type(EViewType type)
|
|
{
|
|
m_settings.view_type = type;
|
|
m_settings.update_colors = true;
|
|
}
|
|
|
|
void ViewerImpl::set_time_mode(ETimeMode mode)
|
|
{
|
|
m_settings.time_mode = mode;
|
|
m_settings.update_colors = true;
|
|
// Update layer_duration for all vertices based on the new time mode
|
|
for (PathVertex& v : m_vertices) {
|
|
v.layer_duration = m_layers.get_layer_time(mode, static_cast<size_t>(v.layer_id));
|
|
}
|
|
}
|
|
|
|
void ViewerImpl::set_layers_view_range(Interval::value_type min, Interval::value_type max)
|
|
{
|
|
min = std::clamp<Interval::value_type>(min, 0, m_layers.count() - 1);
|
|
max = std::clamp<Interval::value_type>(max, 0, m_layers.count() - 1);
|
|
m_layers.set_view_range(min, max);
|
|
// force immediate update of the full range
|
|
update_view_full_range();
|
|
m_view_range.set_visible(m_view_range.get_enabled());
|
|
m_settings.update_enabled_entities = true;
|
|
//m_settings.update_colors = true;
|
|
update_colors_texture();
|
|
}
|
|
|
|
void ViewerImpl::toggle_top_layer_only_view_range()
|
|
{
|
|
m_settings.top_layer_only_view_range = !m_settings.top_layer_only_view_range;
|
|
update_view_full_range();
|
|
m_view_range.set_visible(m_view_range.get_enabled());
|
|
m_settings.update_enabled_entities = true;
|
|
//m_settings.update_colors = true;
|
|
update_colors_texture();
|
|
}
|
|
|
|
// ORCA: enable/disable darkening of the layers the layer slider is not scrubbed to
|
|
void ViewerImpl::set_dim_previous_layers(bool value)
|
|
{
|
|
if (m_settings.dim_previous_layers == value)
|
|
return;
|
|
m_settings.dim_previous_layers = value;
|
|
// defer the actual color/texture rebuild to the next render(), when the GL context is current
|
|
// (this may be toggled from the Preferences dialog, outside the canvas context)
|
|
m_settings.update_colors = true;
|
|
}
|
|
|
|
// ORCA: set how bright the darkened layers are rendered, 1.0 = unchanged, 0.0 = black
|
|
void ViewerImpl::set_dim_previous_layers_brightness(float value)
|
|
{
|
|
value = std::clamp(value, 0.0f, 1.0f);
|
|
if (m_settings.dim_previous_layers_brightness == value)
|
|
return;
|
|
m_settings.dim_previous_layers_brightness = value;
|
|
m_settings.update_colors = true;
|
|
}
|
|
|
|
std::vector<ETimeMode> ViewerImpl::get_time_modes() const
|
|
{
|
|
std::vector<ETimeMode> ret;
|
|
for (size_t i = 0; i < TIME_MODES_COUNT; ++i) {
|
|
if (std::accumulate(m_vertices.begin(), m_vertices.end(), 0.0f,
|
|
[i](float a, const PathVertex& v) { return a + v.times[i]; }) > 0.0f)
|
|
ret.push_back(static_cast<ETimeMode>(i));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::vector<uint8_t> ViewerImpl::get_used_extruders_ids() const
|
|
{
|
|
std::vector<uint8_t> ret;
|
|
ret.reserve(m_used_extruders.size());
|
|
for (const auto& [id, colors] : m_used_extruders) {
|
|
ret.emplace_back(id);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
size_t ViewerImpl::get_color_prints_count(uint8_t extruder_id) const
|
|
{
|
|
const auto it = m_used_extruders.find(extruder_id);
|
|
return (it == m_used_extruders.end()) ? 0 : it->second.size();
|
|
}
|
|
|
|
std::vector<ColorPrint> ViewerImpl::get_color_prints(uint8_t extruder_id) const
|
|
{
|
|
const auto it = m_used_extruders.find(extruder_id);
|
|
return (it == m_used_extruders.end()) ? std::vector<ColorPrint>() : it->second;
|
|
}
|
|
|
|
AABox ViewerImpl::get_bounding_box(const std::vector<EMoveType>& types) const
|
|
{
|
|
Vec3 min = { FLT_MAX, FLT_MAX, FLT_MAX };
|
|
Vec3 max = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
|
|
for (const PathVertex& v : m_vertices) {
|
|
if (std::find(types.begin(), types.end(), v.type) != types.end()) {
|
|
for (int j = 0; j < 3; ++j) {
|
|
min[j] = std::min(min[j], v.position[j]);
|
|
max[j] = std::max(max[j], v.position[j]);
|
|
}
|
|
}
|
|
}
|
|
return { min, max };
|
|
}
|
|
|
|
AABox ViewerImpl::get_extrusion_bounding_box(const std::vector<EGCodeExtrusionRole>& roles) const
|
|
{
|
|
Vec3 min = { FLT_MAX, FLT_MAX, FLT_MAX };
|
|
Vec3 max = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
|
|
for (const PathVertex& v : m_vertices) {
|
|
if (v.is_extrusion() && std::find(roles.begin(), roles.end(), v.role) != roles.end()) {
|
|
for (int j = 0; j < 3; ++j) {
|
|
min[j] = std::min(min[j], v.position[j]);
|
|
max[j] = std::max(max[j], v.position[j]);
|
|
}
|
|
}
|
|
}
|
|
return { min, max };
|
|
}
|
|
|
|
bool ViewerImpl::is_option_visible(EOptionType type) const
|
|
{
|
|
return m_settings.options_visibility[size_t(type)];
|
|
}
|
|
|
|
void ViewerImpl::toggle_option_visibility(EOptionType type)
|
|
{
|
|
m_settings.options_visibility[size_t(type)] = ! m_settings.options_visibility[size_t(type)];
|
|
const Interval old_enabled_range = m_view_range.get_enabled();
|
|
update_view_full_range();
|
|
const Interval& new_enabled_range = m_view_range.get_enabled();
|
|
if (old_enabled_range != new_enabled_range) {
|
|
const Interval& visible_range = m_view_range.get_visible();
|
|
if (old_enabled_range == visible_range)
|
|
m_view_range.set_visible(new_enabled_range);
|
|
else if (m_settings.top_layer_only_view_range && new_enabled_range[0] < visible_range[0])
|
|
m_view_range.set_visible(new_enabled_range[0], visible_range[1]);
|
|
}
|
|
m_settings.update_enabled_entities = true;
|
|
m_settings.update_colors = true;
|
|
}
|
|
|
|
bool ViewerImpl::is_extrusion_role_visible(EGCodeExtrusionRole role) const
|
|
{
|
|
return m_settings.extrusion_roles_visibility[size_t(role)];
|
|
}
|
|
|
|
void ViewerImpl::toggle_extrusion_role_visibility(EGCodeExtrusionRole role)
|
|
{
|
|
m_settings.extrusion_roles_visibility[size_t(role)] = ! m_settings.extrusion_roles_visibility[size_t(role)];
|
|
update_view_full_range();
|
|
m_settings.update_enabled_entities = true;
|
|
m_settings.update_colors = true;
|
|
}
|
|
|
|
void ViewerImpl::set_view_visible_range(Interval::value_type min, Interval::value_type max)
|
|
{
|
|
// force update of the full range, to avoid clamping the visible range with full old values
|
|
// when calling m_view_range.set_visible()
|
|
update_view_full_range();
|
|
m_view_range.set_visible(min, max);
|
|
update_enabled_entities();
|
|
//m_settings.update_colors = true;
|
|
update_colors_texture();
|
|
}
|
|
|
|
float ViewerImpl::get_estimated_time_at(size_t id) const
|
|
{
|
|
const size_t mode = static_cast<size_t>(m_settings.time_mode);
|
|
if (mode >= TIME_MODES_COUNT || id >= m_vertices.size())
|
|
return 0.0f;
|
|
size_t first = 0;
|
|
float time = 0.0f;
|
|
const size_t layer = static_cast<size_t>(m_vertices[id].layer_id);
|
|
if (layer < m_layer_first_vertex.size() && m_layer_first_vertex[layer] <= id) {
|
|
first = m_layer_first_vertex[layer];
|
|
time = m_layer_start_times[mode][layer];
|
|
}
|
|
for (size_t i = first; i <= id; ++i)
|
|
time += m_vertices[i].times[mode];
|
|
return time;
|
|
}
|
|
|
|
Color ViewerImpl::get_vertex_color(const PathVertex& v) const
|
|
{
|
|
if (v.type == EMoveType::Noop)
|
|
return DUMMY_COLOR;
|
|
|
|
if ((v.is_wipe() && (m_settings.view_type != EViewType::Speed && m_settings.view_type != EViewType::ActualSpeed && m_settings.view_type != EViewType::Acceleration && m_settings.view_type != EViewType::Jerk)) || v.is_option())
|
|
return get_option_color(move_type_to_option(v.type));
|
|
|
|
switch (m_settings.view_type)
|
|
{
|
|
case EViewType::FeatureType:
|
|
{
|
|
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : get_extrusion_role_color(v.role);
|
|
}
|
|
case EViewType::Height:
|
|
{
|
|
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : m_height_range.get_color_at(v.height);
|
|
}
|
|
case EViewType::Width:
|
|
{
|
|
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : m_width_range.get_color_at(v.width);
|
|
}
|
|
case EViewType::Speed:
|
|
{
|
|
return m_speed_range.get_color_at(v.feedrate);
|
|
}
|
|
case EViewType::ActualSpeed:
|
|
{
|
|
return m_actual_speed_range.get_color_at(v.actual_feedrate);
|
|
}
|
|
case EViewType::FanSpeed:
|
|
{
|
|
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : m_fan_speed_range.get_color_at(v.fan_speed);
|
|
}
|
|
case EViewType::Temperature:
|
|
{
|
|
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : m_temperature_range.get_color_at(v.temperature);
|
|
}
|
|
// ORCA: Add Pressure Advance visualization support
|
|
case EViewType::PressureAdvance:
|
|
{
|
|
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : m_pressure_advance_range.get_color_at(v.pressure_advance);
|
|
}
|
|
// ORCA: Add Acceleration visualization support
|
|
case EViewType::Acceleration:
|
|
{
|
|
return m_acceleration_range.get_color_at(v.acceleration);
|
|
}
|
|
// ORCA: Add Jerk visualization support
|
|
case EViewType::Jerk:
|
|
{
|
|
return m_jerk_range.get_color_at(v.jerk);
|
|
}
|
|
case EViewType::VolumetricFlowRate:
|
|
{
|
|
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : m_volumetric_rate_range.get_color_at(v.volumetric_rate());
|
|
}
|
|
case EViewType::ActualVolumetricFlowRate:
|
|
{
|
|
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : m_actual_volumetric_rate_range.get_color_at(v.actual_volumetric_rate());
|
|
}
|
|
case EViewType::LayerTimeLinear:
|
|
{
|
|
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) :
|
|
m_layer_time_range[0].get_color_at(m_layers.get_layer_time(m_settings.time_mode, static_cast<size_t>(v.layer_id)));
|
|
}
|
|
case EViewType::LayerTimeLogarithmic:
|
|
{
|
|
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) :
|
|
m_layer_time_range[1].get_color_at(m_layers.get_layer_time(m_settings.time_mode, static_cast<size_t>(v.layer_id)));
|
|
}
|
|
case EViewType::Tool:
|
|
{
|
|
assert(static_cast<size_t>(v.extruder_id) < m_tool_colors.size());
|
|
return m_tool_colors[v.extruder_id];
|
|
}
|
|
case EViewType::Summary: // ORCA
|
|
case EViewType::ColorPrint:
|
|
{
|
|
return m_layers.layer_contains_colorprint_options(static_cast<size_t>(v.layer_id)) ? DUMMY_COLOR :
|
|
m_color_print_colors[static_cast<size_t>(v.color_id) % m_color_print_colors.size()];
|
|
}
|
|
default: { break; }
|
|
}
|
|
|
|
return DUMMY_COLOR;
|
|
}
|
|
|
|
void ViewerImpl::set_tool_colors(const Palette& colors)
|
|
{
|
|
m_tool_colors = colors;
|
|
m_settings.update_colors = true;
|
|
}
|
|
|
|
void ViewerImpl::set_color_print_colors(const Palette& colors)
|
|
{
|
|
m_color_print_colors = colors;
|
|
m_settings.update_colors = true;
|
|
}
|
|
|
|
const Color& ViewerImpl::get_extrusion_role_color(EGCodeExtrusionRole role) const
|
|
{
|
|
return m_extrusion_roles_colors[size_t(role)];
|
|
}
|
|
|
|
void ViewerImpl::set_extrusion_role_color(EGCodeExtrusionRole role, const Color& color)
|
|
{
|
|
m_extrusion_roles_colors[size_t(role)] = color;
|
|
m_settings.update_colors = true;
|
|
}
|
|
|
|
void ViewerImpl::reset_default_extrusion_roles_colors()
|
|
{
|
|
m_extrusion_roles_colors = DEFAULT_EXTRUSION_ROLES_COLORS;
|
|
}
|
|
|
|
const Color& ViewerImpl::get_option_color(EOptionType type) const
|
|
{
|
|
return m_options_colors[size_t(type)];
|
|
}
|
|
|
|
void ViewerImpl::set_option_color(EOptionType type, const Color& color)
|
|
{
|
|
m_options_colors[size_t(type)] = color;
|
|
m_settings.update_colors = true;
|
|
}
|
|
|
|
void ViewerImpl::reset_default_options_colors()
|
|
{
|
|
m_options_colors = DEFAULT_OPTIONS_COLORS;
|
|
}
|
|
|
|
const ColorRange& ViewerImpl::get_color_range(EViewType type) const
|
|
{
|
|
switch (type)
|
|
{
|
|
case EViewType::Height: { return m_height_range; }
|
|
case EViewType::Width: { return m_width_range; }
|
|
case EViewType::Speed: { return m_speed_range; }
|
|
case EViewType::ActualSpeed: { return m_actual_speed_range; }
|
|
case EViewType::FanSpeed: { return m_fan_speed_range; }
|
|
case EViewType::Temperature: { return m_temperature_range; }
|
|
// ORCA: Add Pressure Advance visualization support
|
|
case EViewType::PressureAdvance: { return m_pressure_advance_range; }
|
|
// ORCA: Add Acceleration visualization support
|
|
case EViewType::Acceleration: { return m_acceleration_range; }
|
|
// ORCA: Add Jerk visualization support
|
|
case EViewType::Jerk: { return m_jerk_range; }
|
|
case EViewType::VolumetricFlowRate: { return m_volumetric_rate_range; }
|
|
case EViewType::ActualVolumetricFlowRate: { return m_actual_volumetric_rate_range; }
|
|
case EViewType::LayerTimeLinear: { return m_layer_time_range[0]; }
|
|
case EViewType::LayerTimeLogarithmic: { return m_layer_time_range[1]; }
|
|
default: { return ColorRange::DUMMY_COLOR_RANGE; }
|
|
}
|
|
}
|
|
|
|
void ViewerImpl::set_color_range_palette(EViewType type, const Palette& palette)
|
|
{
|
|
switch (type)
|
|
{
|
|
case EViewType::Height: { m_height_range.set_palette(palette); break; }
|
|
case EViewType::Width: { m_width_range.set_palette(palette); break; }
|
|
case EViewType::Speed: { m_speed_range.set_palette(palette); break; }
|
|
case EViewType::ActualSpeed: { m_actual_speed_range.set_palette(palette); break; }
|
|
case EViewType::FanSpeed: { m_fan_speed_range.set_palette(palette); break; }
|
|
case EViewType::Temperature: { m_temperature_range.set_palette(palette); break; }
|
|
// ORCA: Add Pressure Advance visualization support
|
|
case EViewType::PressureAdvance: { m_pressure_advance_range.set_palette(palette); break; }
|
|
// ORCA: Add Acceleration visualization support
|
|
case EViewType::Acceleration: { m_acceleration_range.set_palette(palette); break; }
|
|
// ORCA: Add Jerk visualization support
|
|
case EViewType::Jerk: { m_jerk_range.set_palette(palette); break; }
|
|
case EViewType::VolumetricFlowRate: { m_volumetric_rate_range.set_palette(palette); break; }
|
|
case EViewType::ActualVolumetricFlowRate: { m_actual_volumetric_rate_range.set_palette(palette); break; }
|
|
case EViewType::LayerTimeLinear: { m_layer_time_range[0].set_palette(palette); break; }
|
|
case EViewType::LayerTimeLogarithmic: { m_layer_time_range[1].set_palette(palette); break; }
|
|
default: { break; }
|
|
}
|
|
m_settings.update_colors = true;
|
|
}
|
|
|
|
void ViewerImpl::set_travels_radius(float radius)
|
|
{
|
|
m_travels_radius = std::clamp(radius, MIN_TRAVELS_RADIUS_MM, MAX_TRAVELS_RADIUS_MM);
|
|
update_heights_widths();
|
|
}
|
|
|
|
void ViewerImpl::set_wipes_radius(float radius)
|
|
{
|
|
m_wipes_radius = std::clamp(radius, MIN_WIPES_RADIUS_MM, MAX_WIPES_RADIUS_MM);
|
|
update_heights_widths();
|
|
}
|
|
|
|
size_t ViewerImpl::get_used_cpu_memory() const
|
|
{
|
|
size_t ret = sizeof(*this);
|
|
ret += m_layers.size_in_bytes_cpu();
|
|
ret += STDVEC_MEMSIZE(m_options, EOptionType);
|
|
ret += m_used_extruders.size() * sizeof(std::map<uint8_t, ColorPrint>::value_type);
|
|
ret += sizeof(m_extrusion_roles_colors);
|
|
ret += sizeof(m_options_colors);
|
|
ret += STDVEC_MEMSIZE(m_vertices, PathVertex);
|
|
for (const std::vector<float>& times : m_layer_start_times)
|
|
ret += STDVEC_MEMSIZE(times, float);
|
|
ret += STDVEC_MEMSIZE(m_layer_first_vertex, uint32_t);
|
|
ret += STDVEC_MEMSIZE(m_colors_scratch, float);
|
|
ret += m_valid_lines_bitset.size_in_bytes_cpu();
|
|
ret += m_height_range.size_in_bytes_cpu();
|
|
ret += m_width_range.size_in_bytes_cpu();
|
|
ret += m_speed_range.size_in_bytes_cpu();
|
|
ret += m_actual_speed_range.size_in_bytes_cpu();
|
|
ret += m_fan_speed_range.size_in_bytes_cpu();
|
|
ret += m_temperature_range.size_in_bytes_cpu();
|
|
// ORCA: Add Pressure Advance visualization support
|
|
ret += m_pressure_advance_range.size_in_bytes_cpu();
|
|
// ORCA: Add Acceleration visualization support
|
|
ret += m_acceleration_range.size_in_bytes_cpu();
|
|
// ORCA: Add Jerk visualization support
|
|
ret += m_jerk_range.size_in_bytes_cpu();
|
|
ret += m_volumetric_rate_range.size_in_bytes_cpu();
|
|
ret += m_actual_volumetric_rate_range.size_in_bytes_cpu();
|
|
for (size_t i = 0; i < COLOR_RANGE_TYPES_COUNT; ++i) {
|
|
ret += m_layer_time_range[i].size_in_bytes_cpu();
|
|
}
|
|
ret += STDVEC_MEMSIZE(m_tool_colors, Color);
|
|
ret += STDVEC_MEMSIZE(m_color_print_colors, Color);
|
|
return ret;
|
|
}
|
|
|
|
size_t ViewerImpl::get_used_gpu_memory() const
|
|
{
|
|
size_t ret = 0;
|
|
ret += m_segment_template.size_in_bytes_gpu();
|
|
ret += m_option_template.size_in_bytes_gpu();
|
|
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
ret += m_tool_marker.size_in_bytes_gpu();
|
|
ret += m_cog_marker.size_in_bytes_gpu();
|
|
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
#ifdef ENABLE_OPENGL_ES
|
|
ret += m_texture_data.get_used_gpu_memory();
|
|
#else
|
|
ret += m_positions_tex_size;
|
|
ret += m_height_width_angle_tex_size;
|
|
ret += m_colors_tex_size;
|
|
ret += m_enabled_segments_tex_size;
|
|
ret += m_enabled_options_tex_size;
|
|
#endif // ENABLE_OPENGL_ES
|
|
return ret;
|
|
}
|
|
|
|
static bool is_visible(const PathVertex& v, const Settings& settings)
|
|
{
|
|
const EOptionType option_type = move_type_to_option(v.type);
|
|
try
|
|
{
|
|
return (option_type == EOptionType::COUNT) ?
|
|
(v.type == EMoveType::Extrude) ? settings.extrusion_roles_visibility[size_t(v.role)] : false :
|
|
settings.options_visibility[size_t(option_type)];
|
|
}
|
|
catch (...)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void ViewerImpl::update_view_full_range()
|
|
{
|
|
const Interval& layers_range = m_layers.get_view_range();
|
|
const bool travels_visible = m_settings.options_visibility[size_t(EOptionType::Travels)];
|
|
const bool wipes_visible = m_settings.options_visibility[size_t(EOptionType::Wipes)];
|
|
|
|
// every vertex before m_layer_first_vertex[layers_range[0]] has a smaller layer_id, so the loop
|
|
// below would skip all of them anyway
|
|
auto first_it = m_vertices.begin();
|
|
if (layers_range[0] < m_layer_first_vertex.size())
|
|
first_it += m_layer_first_vertex[layers_range[0]];
|
|
while (first_it != m_vertices.end() &&
|
|
(first_it->layer_id < layers_range[0] || !is_visible(*first_it, m_settings))) {
|
|
++first_it;
|
|
}
|
|
|
|
// If the first vertex is an extrusion, add an extra step to properly detect the first segment
|
|
if (first_it != m_vertices.begin() && first_it != m_vertices.end() && first_it->type == EMoveType::Extrude)
|
|
--first_it;
|
|
|
|
if (first_it == m_vertices.end())
|
|
m_view_range.set_full(Range());
|
|
else {
|
|
if (travels_visible || wipes_visible) {
|
|
// if the global range starts with a travel/wipe move, extend it to the travel/wipe start
|
|
while (first_it != m_vertices.begin() &&
|
|
((travels_visible && first_it->is_travel()) ||
|
|
(wipes_visible && first_it->is_wipe()))) {
|
|
--first_it;
|
|
}
|
|
}
|
|
|
|
auto last_it = first_it;
|
|
while (last_it != m_vertices.end() && last_it->layer_id <= layers_range[1]) {
|
|
++last_it;
|
|
}
|
|
if (last_it != first_it)
|
|
--last_it;
|
|
|
|
// remove disabled trailing options, if any
|
|
auto rev_first_it = std::make_reverse_iterator(first_it);
|
|
if (rev_first_it != m_vertices.rbegin())
|
|
--rev_first_it;
|
|
auto rev_last_it = std::make_reverse_iterator(last_it);
|
|
if (rev_last_it != m_vertices.rbegin())
|
|
--rev_last_it;
|
|
|
|
bool reduced = false;
|
|
while (rev_last_it != rev_first_it && !is_visible(*rev_last_it, m_settings)) {
|
|
++rev_last_it;
|
|
reduced = true;
|
|
}
|
|
|
|
if (reduced && rev_last_it != m_vertices.rend())
|
|
last_it = rev_last_it.base() - 1;
|
|
|
|
if (travels_visible || wipes_visible) {
|
|
// if the global range ends with a travel/wipe move, extend it to the travel/wipe end
|
|
while (last_it != m_vertices.end() && last_it + 1 != m_vertices.end() &&
|
|
((travels_visible && last_it->is_travel() && (last_it + 1)->is_travel()) ||
|
|
(wipes_visible && last_it->is_wipe() && (last_it + 1)->is_wipe()))) {
|
|
++last_it;
|
|
}
|
|
}
|
|
|
|
if (first_it != last_it)
|
|
m_view_range.set_full(std::distance(m_vertices.begin(), first_it), std::distance(m_vertices.begin(), last_it));
|
|
else
|
|
m_view_range.set_full(Range());
|
|
|
|
if (m_settings.top_layer_only_view_range) {
|
|
const Interval& full_range = m_view_range.get_full();
|
|
auto top_first_it = m_vertices.begin() + full_range[0];
|
|
bool shortened = false;
|
|
while (top_first_it != m_vertices.end() && (top_first_it->layer_id < layers_range[1] || !is_visible(*top_first_it, m_settings))) {
|
|
++top_first_it;
|
|
shortened = true;
|
|
}
|
|
if (shortened)
|
|
--top_first_it;
|
|
|
|
// when spiral vase mode is enabled and only one layer is shown, extend the range by one step
|
|
if (m_settings.spiral_vase_mode && layers_range[0] > 0 && layers_range[0] == layers_range[1])
|
|
--top_first_it;
|
|
m_view_range.set_enabled(std::distance(m_vertices.begin(), top_first_it), full_range[1]);
|
|
}
|
|
else
|
|
m_view_range.set_enabled(m_view_range.get_full());
|
|
}
|
|
|
|
m_settings.update_view_full_range = false;
|
|
}
|
|
|
|
void ViewerImpl::update_color_ranges()
|
|
{
|
|
// Color ranges do not need to be recalculated that often. If the following settings are the same
|
|
// as last time, the current ranges are still valid. The recalculation is quite expensive.
|
|
if (m_settings_used_for_ranges.has_value() &&
|
|
m_settings.extrusion_roles_visibility == m_settings_used_for_ranges->extrusion_roles_visibility &&
|
|
m_settings.options_visibility == m_settings_used_for_ranges->options_visibility)
|
|
return;
|
|
|
|
m_width_range.reset();
|
|
m_height_range.reset();
|
|
m_speed_range.reset();
|
|
m_actual_speed_range.reset();
|
|
m_fan_speed_range.reset();
|
|
m_temperature_range.reset();
|
|
// ORCA: Add Pressure Advance visualization support
|
|
m_pressure_advance_range.reset();
|
|
// ORCA: Add Acceleration visualization support
|
|
m_acceleration_range.reset();
|
|
// ORCA: Add Jerk visualization support
|
|
m_jerk_range.reset();
|
|
m_volumetric_rate_range.reset();
|
|
m_actual_volumetric_rate_range.reset();
|
|
m_layer_time_range[0].reset(); // ColorRange::EType::Linear
|
|
m_layer_time_range[1].reset(); // ColorRange::EType::Logarithmic
|
|
|
|
for (size_t i = 0; i < m_vertices.size(); i++) {
|
|
const PathVertex& v = m_vertices[i];
|
|
if (v.is_extrusion()) {
|
|
m_height_range.update(round_to_bin(v.height));
|
|
if (!v.is_custom_gcode() || m_settings.extrusion_roles_visibility[size_t(EGCodeExtrusionRole::Custom)]) {
|
|
m_width_range.update(round_to_bin(v.width));
|
|
m_volumetric_rate_range.update(round_to_bin(v.volumetric_rate()));
|
|
m_actual_volumetric_rate_range.update(round_to_bin(v.actual_volumetric_rate()));
|
|
}
|
|
m_fan_speed_range.update(round_to_bin(v.fan_speed));
|
|
m_temperature_range.update(round_to_bin(v.temperature));
|
|
// ORCA: Add Pressure Advance visualization support
|
|
if (v.pressure_advance >= 0.0f)
|
|
m_pressure_advance_range.update(v.pressure_advance);
|
|
}
|
|
if ((v.is_travel() && m_settings.options_visibility[size_t(EOptionType::Travels)]) ||
|
|
(v.is_wipe() && m_settings.options_visibility[size_t(EOptionType::Wipes)]) ||
|
|
v.is_extrusion()) {
|
|
m_speed_range.update(v.feedrate);
|
|
m_actual_speed_range.update(v.actual_feedrate);
|
|
// ORCA: Add Acceleration visualization support
|
|
m_acceleration_range.update(v.acceleration);
|
|
// ORCA: Add Jerk visualization support
|
|
m_jerk_range.update(v.jerk);
|
|
}
|
|
}
|
|
|
|
const std::vector<float> times = m_layers.get_times(m_settings.time_mode);
|
|
for (size_t i = 0; i < m_layer_time_range.size(); ++i) {
|
|
for (float t : times) {
|
|
m_layer_time_range[i].update(t);
|
|
}
|
|
}
|
|
|
|
m_settings_used_for_ranges = m_settings;
|
|
}
|
|
|
|
void ViewerImpl::update_heights_widths()
|
|
{
|
|
#ifdef ENABLE_OPENGL_ES
|
|
std::vector<Vec3> heights_widths_angles;
|
|
heights_widths_angles.reserve(m_vertices.size());
|
|
extract_pos_and_or_hwa(m_vertices, m_travels_radius, m_wipes_radius, m_valid_lines_bitset, nullptr, &heights_widths_angles);
|
|
m_texture_data.set_heights_widths_angles(heights_widths_angles);
|
|
#else
|
|
if (m_heights_widths_angles_buf_id == 0)
|
|
return;
|
|
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_heights_widths_angles_buf_id));
|
|
|
|
Vec3* buffer = static_cast<Vec3*>(glMapBuffer(GL_TEXTURE_BUFFER, GL_WRITE_ONLY));
|
|
glcheck();
|
|
|
|
for (size_t i = 0; i < m_vertices.size(); ++i) {
|
|
const PathVertex& v = m_vertices[i];
|
|
if (v.is_travel()) {
|
|
buffer[i][0] = m_travels_radius;
|
|
buffer[i][1] = m_travels_radius;
|
|
}
|
|
else if (v.is_wipe()) {
|
|
buffer[i][0] = m_wipes_radius;
|
|
buffer[i][1] = m_wipes_radius;
|
|
}
|
|
}
|
|
|
|
glsafe(glUnmapBuffer(GL_TEXTURE_BUFFER));
|
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
|
#endif // ENABLE_OPENGL_ES
|
|
}
|
|
|
|
void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projection_matrix, const Vec3& camera_position)
|
|
{
|
|
if (m_segments_shader_id == 0)
|
|
return;
|
|
|
|
#ifdef ENABLE_OPENGL_ES
|
|
if (m_texture_data.get_enabled_segments_count() == 0)
|
|
#else
|
|
if (m_enabled_segments_count == 0)
|
|
#endif // ENABLE_OPENGL_ES
|
|
return;
|
|
|
|
int curr_active_texture = 0;
|
|
glsafe(glGetIntegerv(GL_ACTIVE_TEXTURE, &curr_active_texture));
|
|
int curr_shader;
|
|
glsafe(glGetIntegerv(GL_CURRENT_PROGRAM, &curr_shader));
|
|
const bool curr_cull_face = glIsEnabled(GL_CULL_FACE);
|
|
glcheck();
|
|
|
|
glsafe(glUseProgram(m_segments_shader_id));
|
|
|
|
glsafe(glUniform1i(m_uni_segments_positions_tex_id, 0));
|
|
glsafe(glUniform1i(m_uni_segments_height_width_angle_tex_id, 1));
|
|
glsafe(glUniform1i(m_uni_segments_colors_tex_id, 2));
|
|
glsafe(glUniform1i(m_uni_segments_segment_index_tex_id, 3));
|
|
glsafe(glUniformMatrix4fv(m_uni_segments_view_matrix_id, 1, GL_FALSE, view_matrix.data()));
|
|
glsafe(glUniformMatrix4fv(m_uni_segments_projection_matrix_id, 1, GL_FALSE, projection_matrix.data()));
|
|
glsafe(glUniform3fv(m_uni_segments_camera_position_id, 1, camera_position.data()));
|
|
// The segments come in print order, bottom layer first. Seen from above, that is back to front,
|
|
// and every hidden fragment is shaded before the one that covers it. Drawing them last to first
|
|
// lets the depth test reject the hidden ones instead. The camera looks down when the world's
|
|
// up axis points towards it, which is the view matrix's (2, 2) entry being positive.
|
|
const bool top_down = !m_rendering_shadow_casters && view_matrix[10] > 0.0f;
|
|
glsafe(glUniform1i(m_uni_segments_reverse_order_id, top_down ? 1 : 0));
|
|
#ifndef ENABLE_OPENGL_ES
|
|
glsafe(glUniform1i(m_uni_segments_instance_count_id, static_cast<int>(m_enabled_segments_count)));
|
|
#endif // ENABLE_OPENGL_ES
|
|
// ORCA: realistic view. The depth pass writes the map it would otherwise read, so it shades
|
|
// with the lookup off.
|
|
glsafe(glUniform1i(m_uni_segments_shadow_map_id, m_shadow_map_texture_unit));
|
|
glsafe(glUniformMatrix4fv(m_uni_segments_shadow_light_vp_id, 1, GL_FALSE, m_shadow_light_vp.data()));
|
|
glsafe(glUniform1f(m_uni_segments_shadow_intensity_id, m_rendering_shadow_casters ? 0.0f : m_shadow_intensity));
|
|
glsafe(glUniform1f(m_uni_segments_shadow_map_texel_id, m_shadow_map_texel));
|
|
glsafe(glUniform1f(m_uni_segments_exposure_id, m_exposure));
|
|
glsafe(glUniform1f(m_uni_segments_saturation_id, m_saturation));
|
|
glsafe(glUniform1f(m_uni_segments_bias_scale_id, m_rendering_shadow_casters ? 0.0f : 1.0f));
|
|
|
|
glsafe(glDisable(GL_CULL_FACE));
|
|
|
|
#ifdef ENABLE_OPENGL_ES
|
|
int curr_bound_texture = 0;
|
|
glsafe(glGetIntegerv(GL_TEXTURE_BINDING_2D, &curr_bound_texture));
|
|
|
|
for (size_t i = 0; i < m_texture_data.get_count(); ++i) {
|
|
const auto [id, count] = m_texture_data.get_enabled_segments_tex_id(i);
|
|
if (count == 0)
|
|
continue;
|
|
glsafe(glActiveTexture(GL_TEXTURE0));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, m_texture_data.get_positions_tex_id(i).first));
|
|
glsafe(glActiveTexture(GL_TEXTURE1));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, m_texture_data.get_heights_widths_angles_tex_id(i).first));
|
|
glsafe(glActiveTexture(GL_TEXTURE2));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, m_texture_data.get_colors_tex_id(i).first));
|
|
glsafe(glActiveTexture(GL_TEXTURE3));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, id));
|
|
m_segment_template.render(count);
|
|
}
|
|
#else
|
|
std::array<int, 4> curr_bound_texture = { 0, 0, 0, 0 };
|
|
for (int i = 0; i < curr_bound_texture.size(); ++i) {
|
|
glsafe(glActiveTexture(GL_TEXTURE0 + i));
|
|
glsafe(glGetIntegerv(GL_TEXTURE_BINDING_BUFFER, &curr_bound_texture[i]));
|
|
//assert(curr_bound_texture[i] == 0);
|
|
}
|
|
|
|
glsafe(glActiveTexture(GL_TEXTURE0));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_positions_tex_id));
|
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_positions_buf_id));
|
|
glsafe(glActiveTexture(GL_TEXTURE1));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_heights_widths_angles_tex_id));
|
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_heights_widths_angles_buf_id));
|
|
glsafe(glActiveTexture(GL_TEXTURE2));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
|
glsafe(glActiveTexture(GL_TEXTURE3));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_segments_tex_id));
|
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, m_enabled_segments_buf_id));
|
|
|
|
m_segment_template.render(m_enabled_segments_count);
|
|
#endif // ENABLE_OPENGL_ES
|
|
|
|
if (curr_cull_face)
|
|
glsafe(glEnable(GL_CULL_FACE));
|
|
|
|
glsafe(glUseProgram(curr_shader));
|
|
#ifdef ENABLE_OPENGL_ES
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, curr_bound_texture));
|
|
#else
|
|
for (int i = 0; i < curr_bound_texture.size(); ++i) {
|
|
glsafe(glActiveTexture(GL_TEXTURE0 + i));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, curr_bound_texture[i]));
|
|
}
|
|
#endif // ENABLE_OPENGL_ES
|
|
glsafe(glActiveTexture(curr_active_texture));
|
|
}
|
|
|
|
void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& projection_matrix)
|
|
{
|
|
if (m_options_shader_id == 0)
|
|
return;
|
|
|
|
#ifdef ENABLE_OPENGL_ES
|
|
if (m_texture_data.get_enabled_options_count() == 0)
|
|
#else
|
|
if (m_enabled_options_count == 0)
|
|
#endif // ENABLE_OPENGL_ES
|
|
return;
|
|
|
|
int curr_active_texture = 0;
|
|
glsafe(glGetIntegerv(GL_ACTIVE_TEXTURE, &curr_active_texture));
|
|
int curr_shader;
|
|
glsafe(glGetIntegerv(GL_CURRENT_PROGRAM, &curr_shader));
|
|
const bool curr_cull_face = glIsEnabled(GL_CULL_FACE);
|
|
glcheck();
|
|
|
|
glsafe(glUseProgram(m_options_shader_id));
|
|
|
|
glsafe(glUniform1i(m_uni_options_positions_tex_id, 0));
|
|
glsafe(glUniform1i(m_uni_options_height_width_angle_tex_id, 1));
|
|
glsafe(glUniform1i(m_uni_options_colors_tex_id, 2));
|
|
glsafe(glUniform1i(m_uni_options_segment_index_tex_id, 3));
|
|
glsafe(glUniformMatrix4fv(m_uni_options_view_matrix_id, 1, GL_FALSE, view_matrix.data()));
|
|
glsafe(glUniformMatrix4fv(m_uni_options_projection_matrix_id, 1, GL_FALSE, projection_matrix.data()));
|
|
|
|
glsafe(glEnable(GL_CULL_FACE));
|
|
|
|
#ifdef ENABLE_OPENGL_ES
|
|
int curr_bound_texture = 0;
|
|
glsafe(glGetIntegerv(GL_TEXTURE_BINDING_2D, &curr_bound_texture));
|
|
|
|
for (size_t i = 0; i < m_texture_data.get_count(); ++i) {
|
|
const auto [id, count] = m_texture_data.get_enabled_options_tex_id(i);
|
|
if (count == 0)
|
|
continue;
|
|
glsafe(glActiveTexture(GL_TEXTURE0));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, m_texture_data.get_positions_tex_id(i).first));
|
|
glsafe(glActiveTexture(GL_TEXTURE1));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, m_texture_data.get_heights_widths_angles_tex_id(i).first));
|
|
glsafe(glActiveTexture(GL_TEXTURE2));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, m_texture_data.get_colors_tex_id(i).first));
|
|
glsafe(glActiveTexture(GL_TEXTURE3));
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, id));
|
|
m_option_template.render(count);
|
|
}
|
|
#else
|
|
std::array<int, 4> curr_bound_texture = { 0, 0, 0, 0 };
|
|
for (int i = 0; i < curr_bound_texture.size(); ++i) {
|
|
glsafe(glActiveTexture(GL_TEXTURE0 + i));
|
|
glsafe(glGetIntegerv(GL_TEXTURE_BINDING_BUFFER, &curr_bound_texture[i]));
|
|
//assert(curr_bound_texture[i] == 0);
|
|
}
|
|
|
|
glsafe(glActiveTexture(GL_TEXTURE0));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_positions_tex_id));
|
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_positions_buf_id));
|
|
glsafe(glActiveTexture(GL_TEXTURE1));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_heights_widths_angles_tex_id));
|
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_heights_widths_angles_buf_id));
|
|
glsafe(glActiveTexture(GL_TEXTURE2));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
|
glsafe(glActiveTexture(GL_TEXTURE3));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, m_enabled_options_buf_id));
|
|
|
|
m_option_template.render(m_enabled_options_count);
|
|
#endif // ENABLE_OPENGL_ES
|
|
|
|
if (!curr_cull_face)
|
|
glsafe(glDisable(GL_CULL_FACE));
|
|
|
|
glsafe(glUseProgram(curr_shader));
|
|
#ifdef ENABLE_OPENGL_ES
|
|
glsafe(glBindTexture(GL_TEXTURE_2D, curr_bound_texture));
|
|
#else
|
|
for (int i = 0; i < curr_bound_texture.size(); ++i) {
|
|
glsafe(glActiveTexture(GL_TEXTURE0 + i));
|
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, curr_bound_texture[i]));
|
|
}
|
|
#endif // ENABLE_OPENGL_ES
|
|
glsafe(glActiveTexture(curr_active_texture));
|
|
}
|
|
|
|
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
void ViewerImpl::render_cog_marker(const Mat4x4& view_matrix, const Mat4x4& projection_matrix)
|
|
{
|
|
if (m_cog_marker_shader_id == 0)
|
|
return;
|
|
|
|
int curr_shader;
|
|
glsafe(glGetIntegerv(GL_CURRENT_PROGRAM, &curr_shader));
|
|
const bool curr_cull_face = glIsEnabled(GL_CULL_FACE);
|
|
const bool curr_depth_test = glIsEnabled(GL_DEPTH_TEST);
|
|
glcheck();
|
|
|
|
glsafe(glEnable(GL_CULL_FACE));
|
|
glsafe(glDisable(GL_DEPTH_TEST));
|
|
|
|
glsafe(glUseProgram(m_cog_marker_shader_id));
|
|
|
|
glsafe(glUniform3fv(m_uni_cog_marker_world_center_position, 1, m_cog_marker.get_position().data()));
|
|
glsafe(glUniform1f(m_uni_cog_marker_scale_factor, m_cog_marker_scale_factor));
|
|
glsafe(glUniformMatrix4fv(m_uni_cog_marker_view_matrix, 1, GL_FALSE, view_matrix.data()));
|
|
glsafe(glUniformMatrix4fv(m_uni_cog_marker_projection_matrix, 1, GL_FALSE, projection_matrix.data()));
|
|
|
|
m_cog_marker.render();
|
|
|
|
if (curr_depth_test)
|
|
glsafe(glEnable(GL_DEPTH_TEST));
|
|
if (!curr_cull_face)
|
|
glsafe(glDisable(GL_CULL_FACE));
|
|
|
|
glsafe(glUseProgram(curr_shader));
|
|
}
|
|
|
|
void ViewerImpl::render_tool_marker(const Mat4x4& view_matrix, const Mat4x4& projection_matrix)
|
|
{
|
|
if (m_tool_marker_shader_id == 0)
|
|
return;
|
|
|
|
if (m_view_range.get_visible()[1] == m_view_range.get_enabled()[1])
|
|
return;
|
|
|
|
m_tool_marker.set_position(get_current_vertex().position);
|
|
|
|
int curr_shader;
|
|
glsafe(glGetIntegerv(GL_CURRENT_PROGRAM, &curr_shader));
|
|
const bool curr_cull_face = glIsEnabled(GL_CULL_FACE);
|
|
GLboolean curr_depth_mask;
|
|
glsafe(glGetBooleanv(GL_DEPTH_WRITEMASK, &curr_depth_mask));
|
|
const bool curr_blend = glIsEnabled(GL_BLEND);
|
|
glcheck();
|
|
int curr_blend_func;
|
|
glsafe(glGetIntegerv(GL_BLEND_SRC_ALPHA, &curr_blend_func));
|
|
|
|
glsafe(glDisable(GL_CULL_FACE));
|
|
glsafe(glDepthMask(GL_FALSE));
|
|
glsafe(glEnable(GL_BLEND));
|
|
glsafe(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
|
|
|
glsafe(glUseProgram(m_tool_marker_shader_id));
|
|
|
|
const Vec3& origin = m_tool_marker.get_position();
|
|
const Vec3 offset = { 0.0f, 0.0f, m_tool_marker.get_offset_z() };
|
|
const Vec3 position = origin + offset;
|
|
glsafe(glUniform3fv(m_uni_tool_marker_world_origin, 1, position.data()));
|
|
glsafe(glUniform1f(m_uni_tool_marker_scale_factor, m_tool_marker_scale_factor));
|
|
glsafe(glUniformMatrix4fv(m_uni_tool_marker_view_matrix, 1, GL_FALSE, view_matrix.data()));
|
|
glsafe(glUniformMatrix4fv(m_uni_tool_marker_projection_matrix, 1, GL_FALSE, projection_matrix.data()));
|
|
const Color& color = m_tool_marker.get_color();
|
|
glsafe(glUniform4f(m_uni_tool_marker_color_base, color[0], color[1], color[2], m_tool_marker.get_alpha()));
|
|
|
|
m_tool_marker.render();
|
|
|
|
glsafe(glBlendFunc(GL_SRC_ALPHA, curr_blend_func));
|
|
if (!curr_blend)
|
|
glsafe(glDisable(GL_BLEND));
|
|
if (curr_depth_mask == GL_TRUE)
|
|
glsafe(glDepthMask(GL_TRUE));
|
|
if (curr_cull_face)
|
|
glsafe(glEnable(GL_CULL_FACE));
|
|
|
|
glsafe(glUseProgram(curr_shader));
|
|
}
|
|
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
|
|
} // namespace libvgcode
|