18#include <GLFW/glfw3.h>
28using namespace helios;
31 std::vector<helios::RGBcolor> rgb_data;
35 texture.reserve(width * height * 4);
37 for (
const auto &pixel: rgb_data) {
38 texture.push_back(
static_cast<unsigned char>(pixel.r * 255.0f));
39 texture.push_back(
static_cast<unsigned char>(pixel.g * 255.0f));
40 texture.push_back(
static_cast<unsigned char>(pixel.b * 255.0f));
41 texture.push_back(255);
49 std::cout <<
"writing JPEG image: " << filename << std::endl;
52 const size_t bsize = 3 * width * height;
53 std::vector<GLubyte> screen_shot_trans;
54 screen_shot_trans.resize(bsize);
58 constexpr GLenum read_buf = GL_FRONT;
59 glReadBuffer(read_buf);
60 glReadPixels(0, 0, scast<GLsizei>(width), scast<GLsizei>(height), GL_RGB, GL_UNSIGNED_BYTE, &screen_shot_trans[0]);
64 std::vector<helios::RGBcolor> rgb_data;
65 rgb_data.reserve(width * height);
67 for (
size_t i = 0; i < width * height; i++) {
68 size_t byte_idx = i * 3;
69 rgb_data.emplace_back(screen_shot_trans[byte_idx] / 255.0f, screen_shot_trans[byte_idx + 1] / 255.0f, screen_shot_trans[byte_idx + 2] / 255.0f);
76int write_JPEG_file(
const char *filename,
uint width,
uint height,
const std::vector<helios::RGBcolor> &data,
bool print_messages) {
78 std::cout <<
"writing JPEG image: " << filename << std::endl;
86 std::vector<helios::RGBAcolor> rgba_data;
90 texture.reserve(width * height * 4);
92 for (
const auto &pixel: rgba_data) {
93 texture.push_back(
static_cast<unsigned char>(pixel.r * 255.0f));
94 texture.push_back(
static_cast<unsigned char>(pixel.g * 255.0f));
95 texture.push_back(
static_cast<unsigned char>(pixel.b * 255.0f));
96 texture.push_back(
static_cast<unsigned char>(pixel.a * 255.0f));
100Visualizer::Visualizer(
uint Wdisplay) : colormap_current(), colormap_hot(), colormap_cool(), colormap_lava(), colormap_rainbow(), colormap_parula(), colormap_gray() {
101 initialize(Wdisplay,
uint(std::round(Wdisplay * 0.8)), 16,
true,
false);
104Visualizer::Visualizer(
uint Wdisplay,
uint Hdisplay) : colormap_current(), colormap_hot(), colormap_cool(), colormap_lava(), colormap_rainbow(), colormap_parula(), colormap_gray() {
105 initialize(Wdisplay, Hdisplay, 16,
true,
false);
108Visualizer::Visualizer(
uint Wdisplay,
uint Hdisplay,
int aliasing_samples) : colormap_current(), colormap_hot(), colormap_cool(), colormap_lava(), colormap_rainbow(), colormap_parula(), colormap_gray() {
109 initialize(Wdisplay, Hdisplay, aliasing_samples,
true,
false);
112Visualizer::Visualizer(
uint Wdisplay,
uint Hdisplay,
int aliasing_samples,
bool window_decorations,
bool headless) : colormap_current(), colormap_hot(), colormap_cool(), colormap_lava(), colormap_rainbow(), colormap_parula(), colormap_gray() {
113 initialize(Wdisplay, Hdisplay, aliasing_samples, window_decorations, headless);
116void Visualizer::openWindow() {
118 GLFWwindow *_window = glfwCreateWindow(Wdisplay, Hdisplay,
"Helios 3D Simulation",
nullptr,
nullptr);
119 if (_window ==
nullptr) {
120 std::string errorsrtring;
121 errorsrtring.append(
"ERROR(Visualizer): Failed to initialize graphics.\n");
122 errorsrtring.append(
"Common causes for this error:\n");
123 errorsrtring.append(
"-- OSX\n - Is XQuartz installed (xquartz.org) and configured as the default X11 window handler? When running the visualizer, XQuartz should automatically open and appear in the dock, indicating it is working.\n");
124 errorsrtring.append(
"-- Linux\n - Are you running this program remotely via SSH? Remote X11 graphics along with OpenGL are not natively supported. Installing and using VirtualGL is a good solution for this (virtualgl.org).\n");
127 glfwMakeContextCurrent(_window);
131 glfwSetWindowUserPointer(_window,
this);
134 glfwSetInputMode(_window, GLFW_STICKY_KEYS, GL_TRUE);
136 window = (
void *) _window;
138 int window_width, window_height;
139 glfwGetWindowSize(_window, &window_width, &window_height);
141 int framebuffer_width, framebuffer_height;
142 glfwGetFramebufferSize(_window, &framebuffer_width, &framebuffer_height);
144 Wframebuffer =
uint(framebuffer_width);
145 Hframebuffer =
uint(framebuffer_height);
147 if (window_width < Wdisplay || window_height < Hdisplay) {
148 std::cerr <<
"WARNING (Visualizer): requested size of window is larger than the screen area." << std::endl;
149 Wdisplay =
uint(window_width);
150 Hdisplay =
uint(window_height);
153 glfwSetWindowSize(_window, window_width, window_height);
159 glfwSetWindowAspectRatio(_window, GLFW_DONT_CARE, GLFW_DONT_CARE);
163 glfwSetWindowSizeCallback(_window, Visualizer::windowResizeCallback);
164 glfwSetFramebufferSizeCallback(_window, Visualizer::framebufferResizeCallback);
167 glewExperimental = GL_TRUE;
168 if (glewInit() != GLEW_OK) {
176void Visualizer::initialize(
uint window_width_pixels,
uint window_height_pixels,
int aliasing_samples,
bool window_decorations,
bool headless_mode) {
177 Wdisplay = window_width_pixels;
178 Hdisplay = window_height_pixels;
180 headless = headless_mode;
184 maximum_texture_size =
make_uint2(2048, 2048);
187 texture_array_layers = 0;
188 textures_dirty =
false;
196 minimum_view_radius = 0.05f;
199 primitiveColorsNeedUpdate =
false;
201 isWatermarkVisible =
true;
210 colorbar_fontsize = 12;
211 colorbar_fontcolor = RGB::black;
213 colorbar_position =
make_vec3(0.65, 0.1, 0.1);
215 colorbar_IDs.clear();
220 point_culling_enabled =
true;
221 point_culling_threshold = 10000;
222 point_max_render_distance = 0;
223 point_lod_factor = 10.0f;
226 points_total_count = 0;
227 points_rendered_count = 0;
228 last_culling_time_ms = 0;
238 glfwWindowHint(GLFW_SAMPLES, std::max(0, aliasing_samples));
239 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
240 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
242 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
243 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
248 glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
251 glfwWindowHint(GLFW_VISIBLE, 0);
253 if (!window_decorations) {
254 glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
261 glewExperimental = GL_TRUE;
262 if (glewInit() != GLEW_OK) {
270 if (!checkerrors()) {
271 helios_runtime_error(
"ERROR (Visualizer::initialize): OpenGL context initialization failed after GLEW setup. "
272 "This often occurs in headless CI environments without proper GPU drivers or display servers. "
273 "For headless operation, ensure proper virtual display or software rendering is configured.");
278 glEnable(GL_DEPTH_TEST);
279 glDepthFunc(GL_LESS);
282 if (aliasing_samples <= 0) {
283 glDisable(GL_MULTISAMPLE);
284 glDisable(GL_MULTISAMPLE_ARB);
287 if (aliasing_samples <= 1) {
288 glDisable(GL_POLYGON_SMOOTH);
290 glEnable(GL_POLYGON_SMOOTH);
299 if (!checkerrors()) {
300 helios_runtime_error(
"ERROR (Visualizer::initialize): OpenGL context setup failed during basic parameter configuration. "
301 "This typically indicates graphics driver incompatibility or missing OpenGL support in the execution environment.");
305 glEnable(GL_POLYGON_OFFSET_FILL);
306 glPolygonOffset(1.0f, 1.0f);
307 glDisable(GL_CULL_FACE);
310 if (!checkerrors()) {
311 helios_runtime_error(
"ERROR (Visualizer::initialize): OpenGL context setup failed during advanced parameter configuration. "
312 "Verify that the graphics environment supports the required OpenGL version and features.");
316 constexpr size_t Ntypes = GeometryHandler::all_geometry_types.size();
318 face_index_buffer.resize(Ntypes);
319 vertex_buffer.resize(Ntypes);
320 uv_buffer.resize(Ntypes);
321 glGenBuffers((GLsizei) face_index_buffer.size(), face_index_buffer.data());
322 glGenBuffers((GLsizei) vertex_buffer.size(), vertex_buffer.data());
323 glGenBuffers((GLsizei) uv_buffer.size(), uv_buffer.data());
326 color_buffer.resize(Ntypes);
327 color_texture_object.resize(Ntypes);
328 normal_buffer.resize(Ntypes);
329 normal_texture_object.resize(Ntypes);
330 texture_flag_buffer.resize(Ntypes);
331 texture_flag_texture_object.resize(Ntypes);
332 texture_ID_buffer.resize(Ntypes);
333 texture_ID_texture_object.resize(Ntypes);
334 coordinate_flag_buffer.resize(Ntypes);
335 coordinate_flag_texture_object.resize(Ntypes);
336 hidden_flag_buffer.resize(Ntypes);
337 hidden_flag_texture_object.resize(Ntypes);
338 glGenBuffers((GLsizei) color_buffer.size(), color_buffer.data());
339 glGenTextures((GLsizei) color_texture_object.size(), color_texture_object.data());
340 glGenBuffers((GLsizei) normal_buffer.size(), normal_buffer.data());
341 glGenTextures((GLsizei) normal_texture_object.size(), normal_texture_object.data());
342 glGenBuffers((GLsizei) texture_flag_buffer.size(), texture_flag_buffer.data());
343 glGenTextures((GLsizei) texture_flag_texture_object.size(), texture_flag_texture_object.data());
344 glGenBuffers((GLsizei) texture_ID_buffer.size(), texture_ID_buffer.data());
345 glGenTextures((GLsizei) texture_ID_texture_object.size(), texture_ID_texture_object.data());
346 glGenBuffers((GLsizei) coordinate_flag_buffer.size(), coordinate_flag_buffer.data());
347 glGenTextures((GLsizei) coordinate_flag_texture_object.size(), coordinate_flag_texture_object.data());
348 glGenBuffers((GLsizei) hidden_flag_buffer.size(), hidden_flag_buffer.data());
349 glGenTextures((GLsizei) hidden_flag_texture_object.size(), hidden_flag_texture_object.data());
351 glGenBuffers(1, &uv_rescale_buffer);
352 glGenTextures(1, &uv_rescale_texture_object);
355 if (!checkerrors()) {
357 "This indicates insufficient graphics memory or unsupported buffer operations in the current OpenGL context.");
362 primaryShader.
initialize(
"plugins/visualizer/shaders/primaryShader.vert",
"plugins/visualizer/shaders/primaryShader.frag",
this);
363 depthShader.
initialize(
"plugins/visualizer/shaders/shadow.vert",
"plugins/visualizer/shaders/shadow.frag",
this);
366 if (!checkerrors()) {
368 "Verify that shader files are accessible and the OpenGL context supports the required shading language version.");
376 glGenFramebuffers(1, &framebufferID);
377 glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
380 glActiveTexture(GL_TEXTURE1);
381 glGenTextures(1, &depthTexture);
382 glBindTexture(GL_TEXTURE_2D, depthTexture);
383 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, shadow_buffer_size.
x, shadow_buffer_size.
y, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
nullptr);
385 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
386 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
389 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
390 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
391 GLfloat borderColor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
392 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
395 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
397 if (!checkerrors()) {
398 helios_runtime_error(
"ERROR (Visualizer::initialize): OpenGL setup failed during texture configuration. "
399 "This may indicate graphics driver issues or insufficient OpenGL support.");
403 glActiveTexture(GL_TEXTURE0);
405 glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0);
407 glDrawBuffer(GL_NONE);
410 int max_checks = 10000;
412 while (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE && checks < max_checks) {
416 GLenum framebuffer_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
417 if (framebuffer_status != GL_FRAMEBUFFER_COMPLETE) {
418 std::string error_message =
"ERROR (Visualizer::initialize): Framebuffer is incomplete. Status: ";
419 switch (framebuffer_status) {
420 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
421 error_message +=
"GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT - Framebuffer attachment is incomplete";
423 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
424 error_message +=
"GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT - No attachments";
426 case GL_FRAMEBUFFER_UNSUPPORTED:
427 error_message +=
"GL_FRAMEBUFFER_UNSUPPORTED - Unsupported framebuffer format";
430 error_message +=
"Unknown framebuffer error code: " + std::to_string(framebuffer_status);
433 error_message +=
". This typically occurs in CI environments with limited graphics support or missing GPU drivers.";
439 if (!checkerrors()) {
441 "This indicates issues with OpenGL framebuffer operations, often related to graphics driver limitations or insufficient resources.");
445 Wframebuffer = Wdisplay;
446 Hframebuffer = Hdisplay;
451 perspectiveTransformationMatrix = glm::mat4(1.f);
453 customTransformationMatrix = glm::mat4(1.f);
465 camera_lookat_center =
make_vec3(0, 0, 0);
473 std::vector<RGBcolor> ctable_c{{0.f, 0.f, 0.f}, {0.5f, 0.f, 0.5f}, {1.f, 0.f, 0.f}, {1.f, 0.5f, 0.f}, {1.f, 1.f, 0.f}};
475 std::vector<float> clocs_c{0.f, 0.25f, 0.5f, 0.75f, 1.f};
477 colormap_hot.set(ctable_c, clocs_c, 100, 0, 1);
480 ctable_c = {RGB::cyan, RGB::magenta};
482 clocs_c = {0.f, 1.f};
484 colormap_cool.set(ctable_c, clocs_c, 100, 0, 1);
487 ctable_c = {{0.f, 0.05f, 0.05f}, {0.f, 0.6f, 0.6f}, {1.f, 1.f, 1.f}, {1.f, 0.f, 0.f}, {0.5f, 0.f, 0.f}};
489 clocs_c = {0.f, 0.4f, 0.5f, 0.6f, 1.f};
491 colormap_lava.set(ctable_c, clocs_c, 100, 0, 1);
494 ctable_c = {RGB::navy, RGB::cyan, RGB::yellow,
make_RGBcolor(0.75f, 0.f, 0.f)};
496 clocs_c = {0, 0.3f, 0.7f, 1.f};
498 colormap_rainbow.set(ctable_c, clocs_c, 100, 0, 1);
501 ctable_c = {RGB::navy,
make_RGBcolor(0, 0.6, 0.6), RGB::goldenrod, RGB::yellow};
503 clocs_c = {0, 0.4f, 0.7f, 1.f};
505 colormap_parula.set(ctable_c, clocs_c, 100, 0, 1);
508 ctable_c = {RGB::black, RGB::white};
510 clocs_c = {0.f, 1.f};
512 colormap_gray.set(ctable_c, clocs_c, 100, 0, 1);
514 colormap_current = colormap_hot;
517 glfwSetMouseButtonCallback((GLFWwindow *) window,
mouseCallback);
522 if (!checkerrors()) {
523 helios_runtime_error(
"ERROR (Visualizer::initialize): Final OpenGL setup failed during callback configuration. "
524 "The OpenGL context may be in an invalid state or missing required extensions.");
531 glDeleteFramebuffers(1, &framebufferID);
532 glDeleteTextures(1, &depthTexture);
534 glDeleteBuffers((GLsizei) face_index_buffer.size(), face_index_buffer.data());
535 glDeleteBuffers((GLsizei) vertex_buffer.size(), vertex_buffer.data());
536 glDeleteBuffers((GLsizei) uv_buffer.size(), uv_buffer.data());
538 glDeleteBuffers((GLsizei) color_buffer.size(), color_buffer.data());
539 glDeleteTextures((GLsizei) color_texture_object.size(), color_texture_object.data());
540 glDeleteBuffers((GLsizei) normal_buffer.size(), normal_buffer.data());
541 glDeleteTextures((GLsizei) normal_texture_object.size(), normal_texture_object.data());
542 glDeleteBuffers((GLsizei) texture_flag_buffer.size(), texture_flag_buffer.data());
543 glDeleteTextures((GLsizei) texture_flag_texture_object.size(), texture_flag_texture_object.data());
544 glDeleteBuffers((GLsizei) texture_ID_buffer.size(), texture_ID_buffer.data());
545 glDeleteTextures((GLsizei) texture_ID_texture_object.size(), texture_ID_texture_object.data());
546 glDeleteBuffers((GLsizei) coordinate_flag_buffer.size(), coordinate_flag_buffer.data());
547 glDeleteTextures((GLsizei) coordinate_flag_texture_object.size(), coordinate_flag_texture_object.data());
548 glDeleteBuffers((GLsizei) hidden_flag_buffer.size(), hidden_flag_buffer.data());
549 glDeleteTextures((GLsizei) hidden_flag_texture_object.size(), hidden_flag_texture_object.data());
553 glDeleteTextures(1, &texArray);
555 glDeleteBuffers(1, &uv_rescale_buffer);
556 glDeleteTextures(1, &uv_rescale_texture_object);
558 glfwDestroyWindow(scast<GLFWwindow *>(window));
568 message_flag =
false;
572 camera_eye_location = cameraPosition;
573 camera_lookat_center = lookAt;
577 camera_lookat_center = lookAt;
578 camera_eye_location = camera_lookat_center +
sphere2cart(cameraAngle);
582 camera_FOV = angle_FOV;
586 light_direction = direction / direction.
magnitude();
591 for (
auto &i: primaryLightingModel) {
597 lightintensity = lightintensityfactor;
601 backgroundColor = color;
605 isWatermarkVisible =
false;
606 if (watermark_ID != 0) {
613 isWatermarkVisible =
true;
617void Visualizer::updatePerspectiveTransformation(
bool shadow) {
618 float dist = glm::distance(glm_vec3(camera_lookat_center), glm_vec3(camera_eye_location));
619 float nearPlane = std::max(0.1f, 0.05f * dist);
621 float farPlane = std::max(5.f * camera_eye_location.
z, 2.0f * dist);
622 cameraProjectionMatrix = glm::perspective(glm::radians(camera_FOV),
float(Wframebuffer) /
float(Hframebuffer), nearPlane, farPlane);
624 cameraProjectionMatrix = glm::infinitePerspective(glm::radians(camera_FOV),
float(Wframebuffer) /
float(Hframebuffer), nearPlane);
626 cameraViewMatrix = glm::lookAt(glm_vec3(camera_eye_location), glm_vec3(camera_lookat_center), glm::vec3(0, 0, 1));
628 perspectiveTransformationMatrix = cameraProjectionMatrix * cameraViewMatrix;
631void Visualizer::updateCustomTransformation(
const glm::mat4 &matrix) {
632 customTransformationMatrix = matrix;
640 if (!colorbar_IDs.empty()) {
642 colorbar_IDs.clear();
648 if (position.
x < 0 || position.
x > 1 || position.
y < 0 || position.
y > 1 || position.
z < -1 || position.
z > 1) {
649 helios_runtime_error(
"ERROR (Visualizer::setColorbarPosition): position is out of range. Coordinates must be: 0<x<1, 0<y<1, -1<z<1.");
651 colorbar_position = position;
655 if (size.
x < 0 || size.
x > 1 || size.
y < 0 || size.
y > 1) {
656 helios_runtime_error(
"ERROR (Visualizer::setColorbarSize): Size must be greater than 0 and less than the window size (i.e., 1).");
658 colorbar_size = size;
662 if (message_flag && cmin > cmax) {
663 std::cerr <<
"WARNING (Visualizer::setColorbarRange): Maximum colorbar value must be greater than minimum value...Ignoring command." << std::endl;
677 for (
int i = 1; i < ticks.size(); i++) {
678 if (ticks.at(i) <= ticks.at(i - 1)) {
679 helios_runtime_error(
"ERROR (Visualizer::setColorbarTicks): Colorbar ticks must be monotonically increasing.");
684 for (
int i = ticks.size() - 1; i >= 0; i--) {
685 if (ticks.at(i) < colorbar_min) {
686 colorbar_min = ticks.at(i);
689 for (
float tick: ticks) {
690 if (tick > colorbar_max) {
695 colorbar_ticks = ticks;
699 colorbar_title = title;
703 if (font_size <= 0) {
704 helios_runtime_error(
"ERROR (Visualizer::setColorbarFontSize): Font size must be greater than zero.");
706 colorbar_fontsize = font_size;
710 colorbar_fontcolor = fontcolor;
715 colormap_current = colormap_hot;
717 colormap_current = colormap_cool;
719 colormap_current = colormap_lava;
721 colormap_current = colormap_rainbow;
723 colormap_current = colormap_parula;
725 colormap_current = colormap_gray;
727 helios_runtime_error(
"ERROR (Visualizer::setColormap): Setting a custom colormap requires calling setColormap with additional arguments defining the colormap.");
734 if (colors.size() != divisions.size()) {
735 helios_runtime_error(
"ERROR (Visualizer::setColormap): The number of colors must be equal to the number of divisions.");
738 Colormap colormap_custom(colors, divisions, 100, 0, 1);
740 colormap_current = colormap_custom;
744 return colormap_current;
747glm::mat4 Visualizer::computeShadowDepthMVP()
const {
748 glm::vec3 lightDir = -glm::normalize(glm_vec3(light_direction));
750 const float margin = 0.01;
755 static const std::array<glm::vec4, 8> ndcCorners = {glm::vec4(-1, -1, -1, 1), glm::vec4(+1, -1, -1, 1), glm::vec4(+1, +1, -1, 1), glm::vec4(-1, +1, -1, 1),
756 glm::vec4(-1, -1, +1, 1), glm::vec4(+1, -1, +1, 1), glm::vec4(+1, +1, +1, 1), glm::vec4(-1, +1, +1, 1)};
758 glm::mat4 invCam = glm::inverse(this->perspectiveTransformationMatrix);
760 std::array<glm::vec3, 8> frustumWs;
761 for (std::size_t i = 0; i < 8; i++) {
762 glm::vec4 ws = invCam * ndcCorners[i];
763 frustumWs[i] = glm::vec3(ws) / ws.w;
767 glm::vec3 lightUp(0.0f, 1.0f, 0.0f);
768 if (glm::abs(glm::dot(lightUp, lightDir)) > 0.9f)
769 lightUp = glm::vec3(1, 0, 0);
774 glm::vec3 centroid(0);
775 for (
auto &c: frustumWs)
779 glm::vec3 lightPos = centroid - lightDir * 100.0f;
781 glm::mat4 lightView = glm::lookAt(lightPos, centroid, lightUp);
784 glm::vec3 minL(std::numeric_limits<float>::infinity());
785 glm::vec3 maxL(-std::numeric_limits<float>::infinity());
787 for (
auto &c: frustumWs) {
788 glm::vec3 p = glm::vec3(lightView * glm::vec4(c, 1));
789 minL = glm::min(minL, p);
790 maxL = glm::max(maxL, p);
794 glm::vec3 extent = maxL - minL;
795 minL -= extent * margin;
796 maxL += extent * margin;
798 float zNear = -maxL.z;
799 float zFar = -minL.z;
801 glm::mat4 lightProj = glm::ortho(minL.x, maxL.x, minL.y, maxL.y, zNear, zFar);
804 const glm::mat4 bias(0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f);
806 return bias * lightProj * lightView;
809Visualizer::Texture::Texture(
const std::string &texture_file,
uint textureID,
const helios::uint2 &maximum_texture_size,
bool loadalphaonly) : texture_file(texture_file), glyph(), textureID(textureID) {
826 std::vector<unsigned char> image_data;
828 if (texture_file.substr(texture_file.find_last_of(
'.') + 1) ==
"png") {
829 read_png_file(texture_file.c_str(), image_data, texture_resolution.y, texture_resolution.x);
831 read_JPEG_file(texture_file.c_str(), image_data, texture_resolution.y, texture_resolution.x);
834 texture_data = std::move(image_data);
837 if (texture_resolution.x > maximum_texture_size.
x || texture_resolution.y > maximum_texture_size.
y) {
838 const uint2 new_texture_resolution(std::min(texture_resolution.x, maximum_texture_size.
x), std::min(texture_resolution.y, maximum_texture_size.
y));
839 resizeTexture(new_texture_resolution);
843Visualizer::Texture::Texture(
const Glyph *glyph_ptr,
uint textureID,
const helios::uint2 &maximum_texture_size) : textureID(textureID) {
844 assert(glyph_ptr !=
nullptr);
848 texture_resolution = glyph_ptr->size;
851 texture_data.resize(texture_resolution.x * texture_resolution.y);
852 for (
int j = 0; j < texture_resolution.y; j++) {
853 for (
int i = 0; i < texture_resolution.x; i++) {
854 texture_data[i + j * texture_resolution.x] = glyph_ptr->data.at(j).at(i);
859 if (texture_resolution.x > maximum_texture_size.
x || texture_resolution.y > maximum_texture_size.
y) {
860 const uint2 new_texture_resolution(std::min(texture_resolution.x, maximum_texture_size.
x), std::min(texture_resolution.y, maximum_texture_size.
y));
861 resizeTexture(new_texture_resolution);
867Visualizer::Texture::Texture(
const std::vector<unsigned char> &pixel_data,
uint textureID,
const helios::uint2 &image_resolution,
const helios::uint2 &maximum_texture_size) : textureID(textureID) {
869 assert(pixel_data.size() == 4u * image_resolution.
x * image_resolution.
y);
872 texture_data = pixel_data;
873 texture_resolution = image_resolution;
877 if (texture_resolution.x > maximum_texture_size.
x || texture_resolution.y > maximum_texture_size.
y) {
878 const uint2 new_texture_resolution(std::min(texture_resolution.x, maximum_texture_size.
x), std::min(texture_resolution.y, maximum_texture_size.
y));
879 resizeTexture(new_texture_resolution);
883void Visualizer::Texture::resizeTexture(
const helios::uint2 &new_image_resolution) {
884 int old_width = texture_resolution.x;
885 int old_height = texture_resolution.y;
886 int new_width = new_image_resolution.
x;
887 int new_height = new_image_resolution.
y;
889 std::vector<unsigned char> new_data(new_width * new_height * num_channels);
892 float x_ratio = scast<float>(old_width) / scast<float>(new_width);
893 float y_ratio = scast<float>(old_height) / scast<float>(new_height);
895 for (
int y = 0; y < new_height; ++y) {
896 float srcY = y * y_ratio;
897 int y0 = std::min(scast<int>(std::floor(srcY)), old_height - 1);
898 int y1 = std::min(y0 + 1, old_height - 1);
899 float dy = srcY - y0;
901 for (
int x = 0; x < new_width; ++x) {
902 float srcX = x * x_ratio;
903 int x0 = std::min(scast<int>(std::floor(srcX)), old_width - 1);
904 int x1 = std::min(x0 + 1, old_width - 1);
905 float dx = srcX - x0;
908 for (
int c = 0; c < num_channels; ++c) {
909 float p00 = texture_data[(y0 * old_width + x0) * num_channels + c];
910 float p10 = texture_data[(y0 * old_width + x1) * num_channels + c];
911 float p01 = texture_data[(y1 * old_width + x0) * num_channels + c];
912 float p11 = texture_data[(y1 * old_width + x1) * num_channels + c];
914 float top = p00 * (1.f - dx) + p10 * dx;
915 float bottom = p01 * (1.f - dx) + p11 * dx;
916 float value = top * (1.f - dy) + bottom * dy;
918 new_data[(y * new_width + x) * num_channels + c] = scast<unsigned char>(
clamp(std::round(value + 0.5f), 0.f, 255.f));
923 texture_data = std::move(new_data);
924 texture_resolution = new_image_resolution;
928uint Visualizer::registerTextureImage(
const std::string &texture_file) {
933 for (
const auto &[textureID, texture]: texture_manager) {
934 if (texture.texture_file == texture_file) {
940 const uint textureID = texture_manager.size();
942 texture_manager.try_emplace(textureID, texture_file, textureID, this->maximum_texture_size,
false);
943 textures_dirty =
true;
948uint Visualizer::registerTextureImage(
const std::vector<unsigned char> &texture_data,
const helios::uint2 &image_resolution) {
950 assert(!texture_data.empty() && texture_data.size() == 4 * image_resolution.
x * image_resolution.
y);
953 const uint textureID = texture_manager.size();
955 texture_manager.try_emplace(textureID, texture_data, textureID, image_resolution, this->maximum_texture_size);
956 textures_dirty =
true;
961uint Visualizer::registerTextureTransparencyMask(
const std::string &texture_file) {
966 for (
const auto &[textureID, texture]: texture_manager) {
967 if (texture.texture_file == texture_file) {
973 const uint textureID = texture_manager.size();
975 texture_manager.try_emplace(textureID, texture_file, textureID, this->maximum_texture_size,
true);
976 textures_dirty =
true;
981uint Visualizer::registerTextureGlyph(
const Glyph *glyph) {
983 const uint textureID = texture_manager.size();
985 texture_manager.try_emplace(textureID, glyph, textureID, this->maximum_texture_size);
986 textures_dirty =
true;
992 if (texture_manager.find(textureID) == texture_manager.end()) {
994 return texture_manager.at(textureID).texture_resolution;
998 const std::filesystem::path p(texture_file);
1001 if (!std::filesystem::exists(p) || !std::filesystem::is_regular_file(p)) {
1006 std::string ext = p.extension().string();
1007 std::transform(ext.begin(), ext.end(), ext.begin(), [](
const unsigned char c) { return scast<char>(std::tolower(c)); });
1011 if (ext !=
".png") {
1015 if (ext !=
".png" && ext !=
".jpg" && ext !=
".jpeg") {
1027std::vector<uint> Visualizer::getFrameBufferSize()
const {
1028 return {Wframebuffer, Hframebuffer};
1031void Visualizer::setFrameBufferSize(
int width,
int height) {
1032 Wframebuffer = width;
1033 Hframebuffer = height;
1037 return backgroundColor;
1040Shader Visualizer::getPrimaryShader()
const {
1041 return primaryShader;
1045 return {camera_lookat_center, camera_eye_location};
1049 return perspectiveTransformationMatrix;
1052glm::mat4 Visualizer::getViewMatrix()
const {
1053 vec3 forward = camera_lookat_center - camera_eye_location;
1062 glm::vec3 camera_pos{camera_eye_location.
x, camera_eye_location.y, camera_eye_location.z};
1063 glm::vec3 lookat_pos{camera_lookat_center.
x, camera_lookat_center.
y, camera_lookat_center.
z};
1064 glm::vec3 up_vec{up.
x, up.
y, up.
z};
1066 return glm::lookAt(camera_pos, lookat_pos, up_vec);
1069std::vector<Visualizer::LightingModel> Visualizer::getPrimaryLightingModel() {
1070 return primaryLightingModel;
1073uint Visualizer::getDepthTexture()
const {
1074 return depthTexture;