18#include <GLFW/glfw3.h>
24using namespace helios;
37 if (context !=
nullptr) {
40 std::snprintf(outfile, 100,
"%02d-%02d-%4d_%02d-%02d-%02d_frame%d.jpg", date.
day, date.
month, date.
year, time.
hour, time.
minute, time.
second, frame_counter);
42 std::snprintf(outfile, 100,
"frame%d.jpg", frame_counter);
52 bool gizmo_was_visible = navigation_gizmo_enabled;
53 if (gizmo_was_visible) {
60 std::string outfile_str = outfile;
63 std::string format_lower = image_format;
64 std::transform(format_lower.begin(), format_lower.end(), format_lower.begin(), ::tolower);
66 bool is_png = (format_lower ==
"png");
67 bool is_jpeg = (format_lower ==
"jpeg" || format_lower ==
"jpg");
69 if (!is_png && !is_jpeg) {
70 helios_runtime_error(
"ERROR (Visualizer::printWindow): Invalid image_format '" + image_format +
"'. Must be 'jpeg', 'jpg', or 'png'.");
76 outfile_str += is_png ?
".png" :
".jpeg";
79 std::string ext_lower = ext;
80 std::transform(ext_lower.begin(), ext_lower.end(), ext_lower.begin(), ::tolower);
81 if (is_png && ext_lower !=
".png") {
82 helios_runtime_error(
"ERROR (Visualizer::printWindow): File extension '" + ext +
"' does not match image_format 'png'.");
83 }
else if (is_jpeg && ext_lower !=
".jpg" && ext_lower !=
".jpeg") {
84 helios_runtime_error(
"ERROR (Visualizer::printWindow): File extension '" + ext +
"' does not match image_format 'jpeg'.");
89 if (background_is_transparent && is_jpeg && message_flag) {
90 std::cerr <<
"WARNING (Visualizer::printWindow): Transparent background requested but JPEG format does not support transparency. Output will have opaque background." << std::endl;
94 if (window !=
nullptr && !headless) {
96 int window_attrib = glfwGetWindowAttrib((GLFWwindow *) window, GLFW_ICONIFIED);
97 if (window_attrib == GLFW_TRUE) {
98 std::cerr <<
"WARNING (printWindow): Window is minimized - screenshot may be unreliable" << std::endl;
106 bool had_transparent_background = background_is_transparent && background_rectangle_ID != 0;
107 if (had_transparent_background) {
110 background_rectangle_ID = 0;
113 transferBufferData();
119 if (headless && offscreenFramebufferID != 0) {
120 glBindFramebuffer(GL_FRAMEBUFFER, offscreenFramebufferID);
121 glViewport(0, 0, Wframebuffer, Hframebuffer);
123 glBindFramebuffer(GL_FRAMEBUFFER, 0);
124 glViewport(0, 0, Wframebuffer, Hframebuffer);
126 glDrawBuffer(GL_BACK);
130 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
131 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
135 updatePerspectiveTransformation(
false);
149 glReadBuffer(GL_BACK);
151 buffers_swapped_since_render =
false;
160 if (headless && offscreenFramebufferID != 0) {
162 std::vector<helios::RGBAcolor> pixels = readOffscreenPixelsRGBA(background_is_transparent);
163 if (pixels.empty()) {
164 helios_runtime_error(
"ERROR (Visualizer::printWindow): Failed to read pixels from offscreen framebuffer.");
167 int result =
write_PNG_file(outfile_str.c_str(), Wframebuffer, Hframebuffer, pixels, message_flag);
169 helios_runtime_error(
"ERROR (Visualizer::printWindow): Failed to save screenshot to " + outfile_str);
173 int result =
write_PNG_file(outfile_str.c_str(), Wframebuffer, Hframebuffer, buffers_swapped_since_render, background_is_transparent, message_flag);
175 helios_runtime_error(
"ERROR (Visualizer::printWindow): Failed to save screenshot to " + outfile_str);
180 if (headless && offscreenFramebufferID != 0) {
182 std::vector<helios::RGBcolor> pixels = readOffscreenPixels();
183 if (pixels.empty()) {
184 helios_runtime_error(
"ERROR (Visualizer::printWindow): Failed to read pixels from offscreen framebuffer.");
187 int result =
write_JPEG_file(outfile_str.c_str(), Wframebuffer, Hframebuffer, pixels, message_flag);
189 helios_runtime_error(
"ERROR (Visualizer::printWindow): Failed to save screenshot to " + outfile_str);
193 int result =
write_JPEG_file(outfile_str.c_str(), Wframebuffer, Hframebuffer, buffers_swapped_since_render, message_flag);
195 helios_runtime_error(
"ERROR (Visualizer::printWindow): Failed to save screenshot to " + outfile_str);
201 if (had_transparent_background) {
203 std::vector<helios::vec3> vertices = {
211 float aspect_ratio =
static_cast<float>(Wframebuffer) /
static_cast<float>(Hframebuffer);
212 std::vector<helios::vec2> uvs;
213 if (aspect_ratio > 1.f) {
225 if (gizmo_was_visible) {
231 if (pixel_data.empty()) {
234 if (pixel_data.size() != 4 * width_pixels * height_pixels) {
235 helios_runtime_error(
"ERROR (Visualizer::displayImage): Pixel data size does not match the given width and height. Argument 'pixel_data' must have length of 4*width_pixels*height_pixels.");
246 float data_aspect = float(width_pixels) / float(height_pixels);
247 float window_aspect = float(Wdisplay) / float(Hdisplay);
248 if (data_aspect > window_aspect) {
250 image_size =
make_vec2(1.0f, window_aspect / data_aspect);
253 image_size =
make_vec2(data_aspect / window_aspect, 1.0f);
256 constexpr vec3 center(0.5, 0.5, 0);
257 const std::vector vertices{center +
make_vec3(-0.5f * image_size.
x, -0.5f * image_size.
y, 0.f), center +
make_vec3(+0.5f * image_size.
x, -0.5f * image_size.
y, 0.f), center +
make_vec3(+0.5f * image_size.
x, +0.5f * image_size.
y, 0.f),
258 center +
make_vec3(-0.5f * image_size.
x, +0.5f * image_size.
y, 0.f)};
259 const std::vector<vec2> uvs{{0, 0}, {1, 0}, {1, 1}, {0, 1}};
262 geometry_handler.
addGeometry(UUID, GeometryHandler::GEOMETRY_TYPE_RECTANGLE, vertices, RGBA::black, uvs, textureID,
false,
false,
COORDINATES_WINDOW_NORMALIZED,
true,
false);
267 navigation_gizmo_was_enabled_before_image_display = navigation_gizmo_enabled;
276 helios_runtime_error(
"ERROR (Visualizer::displayImage): File " + file_name +
" does not exist or is not a valid image file.");
279 std::vector<unsigned char> image_data;
280 uint image_width, image_height;
282 if (file_name.substr(file_name.find_last_of(
'.') + 1) ==
"png") {
283 read_png_file(file_name.c_str(), image_data, image_height, image_width);
285 read_JPEG_file(file_name.c_str(), image_data, image_height, image_width);
293 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
297 std::vector<GLubyte> buff;
298 buff.resize(3 * Wframebuffer * Hframebuffer);
301 glPixelStorei(GL_PACK_ALIGNMENT, 1);
304 if (headless && offscreenFramebufferID != 0) {
307 GLint current_framebuffer;
308 glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤t_framebuffer);
310 glBindFramebuffer(GL_FRAMEBUFFER, offscreenFramebufferID);
313 glReadPixels(0, 0, GLsizei(Wframebuffer), GLsizei(Hframebuffer), GL_RGB, GL_UNSIGNED_BYTE, &buff[0]);
314 GLenum error = glGetError();
317 glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer);
319 if (error != GL_NO_ERROR) {
320 helios_runtime_error(
"ERROR (getWindowPixelsRGB): glReadPixels failed in headless mode (error: " + std::to_string(error) +
")");
325 const int num_samples = 9;
326 const uint sample_positions[][2] = {{Wframebuffer / 4, Hframebuffer / 4}, {Wframebuffer / 2, Hframebuffer / 4}, {3 * Wframebuffer / 4, Hframebuffer / 4},
327 {Wframebuffer / 4, Hframebuffer / 2}, {Wframebuffer / 2, Hframebuffer / 2}, {3 * Wframebuffer / 4, Hframebuffer / 2},
328 {Wframebuffer / 4, 3 * Hframebuffer / 4}, {Wframebuffer / 2, 3 * Hframebuffer / 4}, {3 * Wframebuffer / 4, 3 * Hframebuffer / 4}};
329 GLubyte test_pixels[num_samples * 3];
331 auto count_non_black_pixels = [](
const GLubyte *pixels,
int count) ->
int {
333 for (
int i = 0; i < count * 3; i += 3) {
334 if (pixels[i] > 5 || pixels[i + 1] > 5 || pixels[i + 2] > 5) {
341 int back_buffer_content_score = 0;
342 int front_buffer_content_score = 0;
345 glReadBuffer(GL_BACK);
346 GLenum error = glGetError();
347 if (error == GL_NO_ERROR) {
349 for (
int i = 0; i < num_samples; i++) {
350 glReadPixels(sample_positions[i][0], sample_positions[i][1], 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &test_pixels[i * 3]);
352 if (glGetError() == GL_NO_ERROR) {
353 back_buffer_content_score = count_non_black_pixels(test_pixels, num_samples);
358 glReadBuffer(GL_FRONT);
359 error = glGetError();
360 if (error == GL_NO_ERROR) {
362 for (
int i = 0; i < num_samples; i++) {
363 glReadPixels(sample_positions[i][0], sample_positions[i][1], 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &test_pixels[i * 3]);
365 if (glGetError() == GL_NO_ERROR) {
366 front_buffer_content_score = count_non_black_pixels(test_pixels, num_samples);
371 if (back_buffer_content_score >= front_buffer_content_score && back_buffer_content_score > 0) {
372 glReadBuffer(GL_BACK);
373 }
else if (front_buffer_content_score > 0) {
374 glReadBuffer(GL_FRONT);
377 glReadBuffer(GL_BACK);
378 error = glGetError();
379 if (error != GL_NO_ERROR) {
380 glReadBuffer(GL_FRONT);
381 error = glGetError();
382 if (error != GL_NO_ERROR) {
388 glReadPixels(0, 0, GLsizei(Wframebuffer), GLsizei(Hframebuffer), GL_RGB, GL_UNSIGNED_BYTE, &buff[0]);
389 error = glGetError();
390 if (error != GL_NO_ERROR) {
397 for (
int i = 0; i < 3 * Wframebuffer * Hframebuffer; i++) {
398 buffer[i] = (
unsigned int) buff[i];
413 std::vector<float> depth_pixels;
416 for (
size_t i = 0; i < depth_pixels.size(); ++i) {
417 buffer[i] = depth_pixels[i];
422 width_pixels = Wdisplay;
423 height_pixels = Hdisplay;
425 depth_pixels.resize(width_pixels * height_pixels);
460 float depth_min = (std::numeric_limits<float>::max)();
461 float depth_max = (std::numeric_limits<float>::min)();
462 for (
auto depth: depth_buffer_data) {
463 if (depth < depth_min) {
466 if (depth > depth_max) {
470 for (
size_t i = 0; i < depth_pixels.size(); i++) {
471 float value = std::round((depth_buffer_data.at(i) - depth_min) / (depth_max - depth_min) * 255);
472 value =
clamp(value, 0.f, 255.f);
473 depth_pixels.at(i) = 255.f - value;
485 width = Wframebuffer;
486 height = Hframebuffer;
490 glfwHideWindow((GLFWwindow *) window);
496 std::cout <<
"Generating interactive plot..." << std::flush;
501 buildContextGeometry_private();
504 if (camera_lookat_center.
x == 0 && camera_lookat_center.
y == 0 && camera_lookat_center.
z == 0) {
505 if (camera_eye_location.
x < 1e-4 && camera_eye_location.
y < 1e-4 && camera_eye_location.
z == 2.f) {
510 float domain_bounding_radius = radius.
magnitude();
512 vec2 xbounds, ybounds, zbounds;
514 camera_lookat_center =
make_vec3(0.5f * (xbounds.
x + xbounds.
y), 0.5f * (ybounds.
x + ybounds.
y), zbounds.
x);
526 if (navigation_gizmo_enabled) {
527 updateNavigationGizmo();
528 previous_camera_eye_location = camera_eye_location;
529 previous_camera_lookat_center = camera_lookat_center;
532 transferBufferData();
534 assert(checkerrors());
540 assert(checkerrors());
542 std::vector<vec3> camera_output;
544 glfwShowWindow((GLFWwindow *) window);
548 if (navigation_gizmo_enabled && cameraHasChanged()) {
549 updateNavigationGizmo();
550 previous_camera_eye_location = camera_eye_location;
551 previous_camera_lookat_center = camera_lookat_center;
553 transferBufferData();
557 assert(checkerrors());
559 glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
560 glViewport(0, 0, shadow_buffer_size.
x, shadow_buffer_size.
y);
563 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
567 updatePerspectiveTransformation(
true);
570 depthMVP = computeShadowDepthMVP();
574 glActiveTexture(GL_TEXTURE1);
575 glBindTexture(GL_TEXTURE_2D, depthTexture);
576 glActiveTexture(GL_TEXTURE0);
581 assert(checkerrors());
585 depthMVP = glm::mat4(1.0);
589 glBindFramebuffer(GL_FRAMEBUFFER, 0);
590 glViewport(0, 0, Wframebuffer, Hframebuffer);
592 if (background_is_transparent) {
593 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
595 glClearColor(backgroundColor.
r, backgroundColor.
g, backgroundColor.
b, 1.0f);
598 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
602 glm::mat4 DepthBiasMVP = biasMatrix * depthMVP;
606 updatePerspectiveTransformation(
false);
618 glActiveTexture(GL_TEXTURE1);
619 glBindTexture(GL_TEXTURE_2D, depthTexture);
620 glUniform1i(primaryShader.shadowmapUniform, 1);
621 glActiveTexture(GL_TEXTURE0);
623 buffers_swapped_since_render =
false;
626 assert(checkerrors());
629 getViewKeystrokes(camera_eye_location, camera_lookat_center);
631 glfwSwapBuffers((GLFWwindow *) window);
632 buffers_swapped_since_render =
true;
634 glfwWaitEventsTimeout(1.0 / 30.0);
637 glfwGetFramebufferSize((GLFWwindow *) window, &width, &height);
638 Wframebuffer = width;
639 Hframebuffer = height;
640 }
while (glfwGetKey((GLFWwindow *) window, GLFW_KEY_ESCAPE) != GLFW_PRESS && glfwWindowShouldClose((GLFWwindow *) window) == 0);
644 assert(checkerrors());
646 camera_output.push_back(camera_eye_location);
647 camera_output.push_back(camera_lookat_center);
650 std::cout <<
"done." << std::endl;
653 return camera_output;
664 glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
665 glViewport(0, 0, shadow_buffer_size.
x, shadow_buffer_size.
y);
668 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
672 updatePerspectiveTransformation(
true);
675 depthMVP = computeShadowDepthMVP();
679 glActiveTexture(GL_TEXTURE1);
680 glBindTexture(GL_TEXTURE_2D, depthTexture);
681 glActiveTexture(GL_TEXTURE0);
688 depthMVP = glm::mat4(1.0);
691 assert(checkerrors());
694 glBindFramebuffer(GL_FRAMEBUFFER, 0);
695 glViewport(0, 0, Wframebuffer, Hframebuffer);
697 if (background_is_transparent) {
698 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
700 glClearColor(backgroundColor.
r, backgroundColor.
g, backgroundColor.
b, 1.0f);
703 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
707 glm::mat4 DepthBiasMVP = biasMatrix * depthMVP;
711 updatePerspectiveTransformation(
false);
723 glActiveTexture(GL_TEXTURE1);
724 glBindTexture(GL_TEXTURE_2D, depthTexture);
725 glUniform1i(primaryShader.shadowmapUniform, 1);
726 glActiveTexture(GL_TEXTURE0);
729 buffers_swapped_since_render =
false;
734 getViewKeystrokes(camera_eye_location, camera_lookat_center);
737 if (navigation_gizmo_enabled && cameraHasChanged()) {
738 updateNavigationGizmo();
739 previous_camera_eye_location = camera_eye_location;
740 previous_camera_lookat_center = camera_lookat_center;
742 transferBufferData();
747 glfwGetFramebufferSize((GLFWwindow *) window, &width, &height);
748 Wframebuffer = width;
749 Hframebuffer = height;
752void Visualizer::transferBufferData() {
753 assert(checkerrors());
760 auto ensureArrayBuffer = [](GLuint buf, GLenum target, GLsizeiptr size,
const void *data) {
761 glBindBuffer(target, buf);
762 GLint current_size = 0;
763 glGetBufferParameteriv(target, GL_BUFFER_SIZE, ¤t_size);
764 if (current_size != size) {
765 glBufferData(target, size, data, GL_STATIC_DRAW);
769 glBufferSubData(target, 0, size, data);
773 auto ensureTextureBuffer = [](GLuint buf, GLuint tex, GLenum format, GLsizeiptr size,
const void *data) {
774 glBindBuffer(GL_TEXTURE_BUFFER, buf);
775 GLint current_size = 0;
776 glGetBufferParameteriv(GL_TEXTURE_BUFFER, GL_BUFFER_SIZE, ¤t_size);
777 if (current_size != size) {
778 glBufferData(GL_TEXTURE_BUFFER, size, data, GL_STATIC_DRAW);
782 glBufferSubData(GL_TEXTURE_BUFFER, 0, size, data);
784 glBindTexture(GL_TEXTURE_BUFFER, tex);
785 glTexBuffer(GL_TEXTURE_BUFFER, format, buf);
789 for (
size_t gi = 0; gi < GeometryHandler::all_geometry_types.size(); ++gi) {
790 const auto geometry_type = GeometryHandler::all_geometry_types[gi];
792 const auto *uv_data = geometry_handler.
getUVData_ptr(geometry_type);
802 ensureArrayBuffer(vertex_buffer.at(gi), GL_ARRAY_BUFFER, vertex_data->size() *
sizeof(GLfloat), vertex_data->data());
803 ensureArrayBuffer(uv_buffer.at(gi), GL_ARRAY_BUFFER, uv_data->size() *
sizeof(GLfloat), uv_data->data());
804 ensureArrayBuffer(face_index_buffer.at(gi), GL_ARRAY_BUFFER, face_index_data->size() *
sizeof(GLint), face_index_data->data());
805 ensureTextureBuffer(color_buffer.at(gi), color_texture_object.at(gi), GL_RGBA32F, color_data->size() *
sizeof(GLfloat), color_data->data());
806 ensureTextureBuffer(normal_buffer.at(gi), normal_texture_object.at(gi), GL_RGB32F, normal_data->size() *
sizeof(GLfloat), normal_data->data());
807 ensureTextureBuffer(texture_flag_buffer.at(gi), texture_flag_texture_object.at(gi), GL_R32I, texture_flag_data->size() *
sizeof(GLint), texture_flag_data->data());
808 ensureTextureBuffer(texture_ID_buffer.at(gi), texture_ID_texture_object.at(gi), GL_R32I, texture_ID_data->size() *
sizeof(GLint), texture_ID_data->data());
809 ensureTextureBuffer(coordinate_flag_buffer.at(gi), coordinate_flag_texture_object.at(gi), GL_R32I, coordinate_flag_data->size() *
sizeof(GLint), coordinate_flag_data->data());
810 ensureTextureBuffer(sky_geometry_flag_buffer.at(gi), sky_geometry_flag_texture_object.at(gi), GL_R8I, sky_geometry_flag_data->size() *
sizeof(GLbyte), sky_geometry_flag_data->data());
811 ensureTextureBuffer(hidden_flag_buffer.at(gi), hidden_flag_texture_object.at(gi), GL_R8I, visible_flag_data->size() *
sizeof(GLbyte), visible_flag_data->data());
813 glBindBuffer(GL_ARRAY_BUFFER, 0);
814 glBindTexture(GL_TEXTURE_BUFFER, 0);
817 bool rect_dirty =
false;
818 for (
size_t UUID: dirty) {
819 if (!geometry_handler.doesGeometryExist(UUID)) {
823 const auto &index_map = geometry_handler.
getIndexMap(UUID);
824 auto geometry_type = index_map.geometry_type;
825 size_t i = std::find(GeometryHandler::all_geometry_types.begin(), GeometryHandler::all_geometry_types.end(), geometry_type) - GeometryHandler::all_geometry_types.begin();
829 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer.at(i));
830 glBufferSubData(GL_ARRAY_BUFFER, index_map.vertex_index *
sizeof(GLfloat), vcount * 3 *
sizeof(GLfloat), geometry_handler.
getVertexData_ptr(geometry_type)->data() + index_map.vertex_index);
832 glBindBuffer(GL_ARRAY_BUFFER, uv_buffer.at(i));
833 glBufferSubData(GL_ARRAY_BUFFER, index_map.uv_index *
sizeof(GLfloat), vcount * 2 *
sizeof(GLfloat), geometry_handler.
getUVData_ptr(geometry_type)->data() + index_map.uv_index);
835 glBindBuffer(GL_ARRAY_BUFFER, face_index_buffer.at(i));
836 glBufferSubData(GL_ARRAY_BUFFER, index_map.face_index_index *
sizeof(GLint), vcount *
sizeof(GLint), geometry_handler.
getFaceIndexData_ptr(geometry_type)->data() + index_map.face_index_index);
838 glBindBuffer(GL_TEXTURE_BUFFER, color_buffer.at(i));
839 glBufferSubData(GL_TEXTURE_BUFFER, index_map.color_index *
sizeof(GLfloat), 4 *
sizeof(GLfloat), geometry_handler.
getColorData_ptr(geometry_type)->data() + index_map.color_index);
840 glBindTexture(GL_TEXTURE_BUFFER, color_texture_object.at(i));
841 glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, color_buffer.at(i));
843 glBindBuffer(GL_ARRAY_BUFFER, normal_buffer.at(i));
844 glBufferSubData(GL_ARRAY_BUFFER, index_map.normal_index *
sizeof(GLfloat), 3 *
sizeof(GLfloat), geometry_handler.
getNormalData_ptr(geometry_type)->data() + index_map.normal_index);
845 glBindTexture(GL_TEXTURE_BUFFER, normal_texture_object.at(i));
846 glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, normal_buffer.at(i));
848 glBindBuffer(GL_ARRAY_BUFFER, texture_flag_buffer.at(i));
849 glBufferSubData(GL_ARRAY_BUFFER, index_map.texture_flag_index *
sizeof(GLint),
sizeof(GLint), geometry_handler.
getTextureFlagData_ptr(geometry_type)->data() + index_map.texture_flag_index);
850 glBindTexture(GL_TEXTURE_BUFFER, texture_flag_texture_object.at(i));
851 glTexBuffer(GL_TEXTURE_BUFFER, GL_R32I, texture_flag_buffer.at(i));
853 glBindBuffer(GL_ARRAY_BUFFER, texture_ID_buffer.at(i));
854 glBufferSubData(GL_ARRAY_BUFFER, index_map.texture_ID_index *
sizeof(GLint),
sizeof(GLint), geometry_handler.
getTextureIDData_ptr(geometry_type)->data() + index_map.texture_ID_index);
855 glBindTexture(GL_TEXTURE_BUFFER, texture_ID_texture_object.at(i));
856 glTexBuffer(GL_TEXTURE_BUFFER, GL_R32I, texture_ID_buffer.at(i));
858 glBindBuffer(GL_ARRAY_BUFFER, coordinate_flag_buffer.at(i));
859 glBufferSubData(GL_ARRAY_BUFFER, index_map.coordinate_flag_index *
sizeof(GLint),
sizeof(GLint), geometry_handler.
getCoordinateFlagData_ptr(geometry_type)->data() + index_map.coordinate_flag_index);
860 glBindTexture(GL_TEXTURE_BUFFER, coordinate_flag_texture_object.at(i));
861 glTexBuffer(GL_TEXTURE_BUFFER, GL_R32I, coordinate_flag_buffer.at(i));
863 glBindBuffer(GL_ARRAY_BUFFER, sky_geometry_flag_buffer.at(i));
864 glBufferSubData(GL_ARRAY_BUFFER, index_map.sky_geometry_flag_index *
sizeof(GLbyte),
sizeof(GLbyte), geometry_handler.
getSkyGeometryFlagData_ptr(geometry_type)->data() + index_map.sky_geometry_flag_index);
865 glBindTexture(GL_TEXTURE_BUFFER, sky_geometry_flag_texture_object.at(i));
866 glTexBuffer(GL_TEXTURE_BUFFER, GL_R8I, sky_geometry_flag_buffer.at(i));
868 glBindBuffer(GL_ARRAY_BUFFER, hidden_flag_buffer.at(i));
869 glBufferSubData(GL_ARRAY_BUFFER, index_map.visible_index *
sizeof(GLbyte),
sizeof(GLbyte), geometry_handler.
getVisibilityFlagData_ptr(geometry_type)->data() + index_map.visible_index);
870 glBindTexture(GL_TEXTURE_BUFFER, hidden_flag_texture_object.at(i));
871 glTexBuffer(GL_TEXTURE_BUFFER, GL_R8I, hidden_flag_buffer.at(i));
873 glBindBuffer(GL_ARRAY_BUFFER, 0);
874 glBindTexture(GL_TEXTURE_BUFFER, 0);
876 if (geometry_type == GeometryHandler::GEOMETRY_TYPE_RECTANGLE) {
884 rectangle_vertex_group_firsts.resize(rectangle_count);
885 rectangle_vertex_group_counts.resize(rectangle_count, 4);
886 for (
int j = 0; j < rectangle_count; ++j) {
887 rectangle_vertex_group_firsts[j] = j * 4;
891 if (textures_dirty || texArray == 0) {
892 transferTextureData();
893 textures_dirty =
false;
898 assert(checkerrors());
901void Visualizer::transferTextureData() {
903 const size_t layers = std::max<size_t>(1, texture_manager.size());
907 if (texArray == 0 || layers != texture_array_layers) {
910 glDeleteTextures(1, &texArray);
914 glGenTextures(1, &texArray);
915 glBindTexture(GL_TEXTURE_2D_ARRAY, texArray);
916 glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, maximum_texture_size.
x, maximum_texture_size.
y, layers);
917 texture_array_layers = layers;
919 glBindTexture(GL_TEXTURE_2D_ARRAY, texArray);
922 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
923 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
924 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
925 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
926 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
928 std::vector<GLfloat> uv_rescale;
929 uv_rescale.resize(texture_manager.size() * 2);
931 for (
const auto &[textureID, texture]: texture_manager) {
932 GLenum externalFormat = 0;
933 switch (texture.num_channels) {
935 externalFormat = GL_RED;
938 externalFormat = GL_RGB;
941 externalFormat = GL_RGBA;
944 throw std::runtime_error(
"unsupported channel count");
947 glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureID, texture.texture_resolution.x, texture.texture_resolution.y, 1, externalFormat, GL_UNSIGNED_BYTE, texture.texture_data.data());
949 uv_rescale.at(textureID * 2 + 0) = float(texture.texture_resolution.x) / float(maximum_texture_size.
x);
950 uv_rescale.at(textureID * 2 + 1) = float(texture.texture_resolution.y) / float(maximum_texture_size.
y);
953 glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
954 glUniform1i(glGetUniformLocation(primaryShader.shaderID,
"textureSampler"), 0);
956 glBindBuffer(GL_TEXTURE_BUFFER, uv_rescale_buffer);
957 glBufferData(GL_TEXTURE_BUFFER, uv_rescale.size() *
sizeof(GLfloat), uv_rescale.data(), GL_STATIC_DRAW);
958 glBindTexture(GL_TEXTURE_BUFFER, uv_rescale_texture_object);
959 glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, uv_rescale_buffer);
960 glBindBuffer(GL_TEXTURE_BUFFER, 0);
964void Visualizer::render(
bool shadow)
const {
965 size_t rectangle_ind = std::find(GeometryHandler::all_geometry_types.begin(), GeometryHandler::all_geometry_types.end(), GeometryHandler::GEOMETRY_TYPE_RECTANGLE) - GeometryHandler::all_geometry_types.begin();
966 size_t triangle_ind = std::find(GeometryHandler::all_geometry_types.begin(), GeometryHandler::all_geometry_types.end(), GeometryHandler::GEOMETRY_TYPE_TRIANGLE) - GeometryHandler::all_geometry_types.begin();
967 size_t point_ind = std::find(GeometryHandler::all_geometry_types.begin(), GeometryHandler::all_geometry_types.end(), GeometryHandler::GEOMETRY_TYPE_POINT) - GeometryHandler::all_geometry_types.begin();
968 size_t line_ind = std::find(GeometryHandler::all_geometry_types.begin(), GeometryHandler::all_geometry_types.end(), GeometryHandler::GEOMETRY_TYPE_LINE) - GeometryHandler::all_geometry_types.begin();
976 GLint current_shader_program = 0;
977 glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_shader_program);
980 glActiveTexture(GL_TEXTURE0);
981 glBindTexture(GL_TEXTURE_2D_ARRAY, texArray);
983 assert(checkerrors());
985 glActiveTexture(GL_TEXTURE9);
986 assert(checkerrors());
987 glBindTexture(GL_TEXTURE_BUFFER, uv_rescale_texture_object);
988 assert(checkerrors());
989 glUniform1i(glGetUniformLocation(current_shader_program,
"uv_rescale"), 9);
993 assert(checkerrors());
995 if (triangle_count > 0) {
996 glActiveTexture(GL_TEXTURE3);
997 glBindTexture(GL_TEXTURE_BUFFER, color_texture_object.at(triangle_ind));
1000 glActiveTexture(GL_TEXTURE4);
1001 glBindTexture(GL_TEXTURE_BUFFER, normal_texture_object.at(triangle_ind));
1004 glActiveTexture(GL_TEXTURE5);
1005 glBindTexture(GL_TEXTURE_BUFFER, texture_flag_texture_object.at(triangle_ind));
1008 glActiveTexture(GL_TEXTURE6);
1009 glBindTexture(GL_TEXTURE_BUFFER, texture_ID_texture_object.at(triangle_ind));
1012 glActiveTexture(GL_TEXTURE7);
1013 glBindTexture(GL_TEXTURE_BUFFER, coordinate_flag_texture_object.at(triangle_ind));
1016 glActiveTexture(GL_TEXTURE2);
1017 glBindTexture(GL_TEXTURE_BUFFER, sky_geometry_flag_texture_object.at(triangle_ind));
1020 glActiveTexture(GL_TEXTURE8);
1021 glBindTexture(GL_TEXTURE_BUFFER, hidden_flag_texture_object.at(triangle_ind));
1024 glBindVertexArray(primaryShader.vertex_array_IDs.at(triangle_ind));
1025 assert(checkerrors());
1026 glDrawArrays(GL_TRIANGLES, 0, triangle_count * 3);
1029 assert(checkerrors());
1033 if (rectangle_count > 0) {
1034 glActiveTexture(GL_TEXTURE3);
1035 glBindTexture(GL_TEXTURE_BUFFER, color_texture_object.at(rectangle_ind));
1038 glActiveTexture(GL_TEXTURE4);
1039 glBindTexture(GL_TEXTURE_BUFFER, normal_texture_object.at(rectangle_ind));
1042 glActiveTexture(GL_TEXTURE5);
1043 glBindTexture(GL_TEXTURE_BUFFER, texture_flag_texture_object.at(rectangle_ind));
1046 glActiveTexture(GL_TEXTURE6);
1047 glBindTexture(GL_TEXTURE_BUFFER, texture_ID_texture_object.at(rectangle_ind));
1050 glActiveTexture(GL_TEXTURE7);
1051 glBindTexture(GL_TEXTURE_BUFFER, coordinate_flag_texture_object.at(rectangle_ind));
1054 glActiveTexture(GL_TEXTURE2);
1055 glBindTexture(GL_TEXTURE_BUFFER, sky_geometry_flag_texture_object.at(rectangle_ind));
1058 glActiveTexture(GL_TEXTURE8);
1059 glBindTexture(GL_TEXTURE_BUFFER, hidden_flag_texture_object.at(rectangle_ind));
1062 glBindVertexArray(primaryShader.vertex_array_IDs.at(rectangle_ind));
1064 std::vector<GLint> opaque_firsts;
1065 std::vector<GLint> opaque_counts;
1066 struct TransparentRect {
1070 std::vector<TransparentRect> transparent_rects;
1072 const auto &texFlags = *geometry_handler.
getTextureFlagData_ptr(GeometryHandler::GEOMETRY_TYPE_RECTANGLE);
1073 const auto &colors = *geometry_handler.
getColorData_ptr(GeometryHandler::GEOMETRY_TYPE_RECTANGLE);
1074 const auto &verts = *geometry_handler.
getVertexData_ptr(GeometryHandler::GEOMETRY_TYPE_RECTANGLE);
1077 opaque_firsts.reserve(rectangle_count);
1078 opaque_counts.reserve(rectangle_count);
1079 transparent_rects.reserve(rectangle_count);
1081 for (
size_t i = 0; i < rectangle_count; ++i) {
1083 if (!visibilityFlags.at(i)) {
1087 bool isGlyph = texFlags.at(i) == 3;
1088 float alpha = colors.at(i * 4 + 3);
1089 if (!isGlyph && alpha >= 1.f) {
1090 opaque_firsts.push_back(
static_cast<GLint
>(i * 4));
1091 opaque_counts.push_back(4);
1093 glm::vec3 center(0.f);
1094 for (
int j = 0; j < 4; ++j) {
1095 center.x += verts.at(i * 12 + j * 3 + 0);
1096 center.y += verts.at(i * 12 + j * 3 + 1);
1097 center.z += verts.at(i * 12 + j * 3 + 2);
1100 glm::vec4 viewPos = cameraViewMatrix * glm::vec4(center, 1.f);
1101 transparent_rects.push_back({i, viewPos.z});
1105 if (!opaque_firsts.empty()) {
1106 glMultiDrawArrays(GL_TRIANGLE_FAN, opaque_firsts.data(), opaque_counts.data(),
static_cast<GLsizei
>(opaque_firsts.size()));
1109 if (!transparent_rects.empty()) {
1110 std::sort(transparent_rects.begin(), transparent_rects.end(), [](
const TransparentRect &a,
const TransparentRect &b) {
1111 return a.depth > b.depth;
1114 glDepthMask(GL_FALSE);
1115 for (
const auto &tr: transparent_rects) {
1116 glDrawArrays(GL_TRIANGLE_FAN,
static_cast<GLint
>(tr.index * 4), 4);
1118 glDepthMask(GL_TRUE);
1122 assert(checkerrors());
1127 if (line_count > 0) {
1137 GLint viewportSizeLoc = glGetUniformLocation(lineShader.shaderID,
"viewportSize");
1138 glUniform2f(viewportSizeLoc,
static_cast<float>(Wframebuffer),
static_cast<float>(Hframebuffer));
1141 glActiveTexture(GL_TEXTURE0);
1142 glBindTexture(GL_TEXTURE_2D_ARRAY, texArray);
1143 glActiveTexture(GL_TEXTURE9);
1144 glBindTexture(GL_TEXTURE_BUFFER, uv_rescale_texture_object);
1145 glUniform1i(glGetUniformLocation(lineShader.shaderID,
"uv_rescale"), 9);
1147 glActiveTexture(GL_TEXTURE3);
1148 glBindTexture(GL_TEXTURE_BUFFER, color_texture_object.at(line_ind));
1151 glActiveTexture(GL_TEXTURE4);
1152 glBindTexture(GL_TEXTURE_BUFFER, normal_texture_object.at(line_ind));
1155 glActiveTexture(GL_TEXTURE5);
1156 glBindTexture(GL_TEXTURE_BUFFER, texture_flag_texture_object.at(line_ind));
1159 glActiveTexture(GL_TEXTURE6);
1160 glBindTexture(GL_TEXTURE_BUFFER, texture_ID_texture_object.at(line_ind));
1163 glActiveTexture(GL_TEXTURE7);
1164 glBindTexture(GL_TEXTURE_BUFFER, coordinate_flag_texture_object.at(line_ind));
1167 glActiveTexture(GL_TEXTURE2);
1168 glBindTexture(GL_TEXTURE_BUFFER, sky_geometry_flag_texture_object.at(line_ind));
1171 glActiveTexture(GL_TEXTURE8);
1172 glBindTexture(GL_TEXTURE_BUFFER, hidden_flag_texture_object.at(line_ind));
1175 glBindVertexArray(lineShader.vertex_array_IDs.at(line_ind));
1178 const std::vector<float> *size_data = geometry_handler.
getSizeData_ptr(GeometryHandler::GEOMETRY_TYPE_LINE);
1179 if (size_data && !size_data->empty()) {
1181 std::map<float, std::vector<size_t>> width_groups;
1182 for (
size_t i = 0; i < size_data->size(); ++i) {
1183 float width = size_data->at(i);
1186 width_groups[width].push_back(i);
1190 GLint lineWidthLoc = glGetUniformLocation(lineShader.shaderID,
"lineWidth");
1193 for (
const auto &group: width_groups) {
1194 float width = group.first;
1195 const std::vector<size_t> &line_indices = group.second;
1198 glUniform1f(lineWidthLoc, width);
1202 for (
size_t line_idx: line_indices) {
1203 glDrawArrays(GL_LINES,
static_cast<GLint
>(line_idx * 2), 2);
1208 GLint lineWidthLoc = glGetUniformLocation(lineShader.shaderID,
"lineWidth");
1209 glUniform1f(lineWidthLoc, 1.0f);
1210 glDrawArrays(GL_LINES, 0, line_count * 2);
1221 glActiveTexture(GL_TEXTURE0);
1222 glBindTexture(GL_TEXTURE_2D_ARRAY, texArray);
1223 glActiveTexture(GL_TEXTURE9);
1224 glBindTexture(GL_TEXTURE_BUFFER, uv_rescale_texture_object);
1225 glUniform1i(glGetUniformLocation(primaryShader.shaderID,
"uv_rescale"), 9);
1228 assert(checkerrors());
1232 if (point_count > 0) {
1233 glActiveTexture(GL_TEXTURE3);
1234 glBindTexture(GL_TEXTURE_BUFFER, color_texture_object.at(point_ind));
1237 glActiveTexture(GL_TEXTURE4);
1238 glBindTexture(GL_TEXTURE_BUFFER, normal_texture_object.at(point_ind));
1241 glActiveTexture(GL_TEXTURE5);
1242 glBindTexture(GL_TEXTURE_BUFFER, texture_flag_texture_object.at(point_ind));
1245 glActiveTexture(GL_TEXTURE6);
1246 glBindTexture(GL_TEXTURE_BUFFER, texture_ID_texture_object.at(point_ind));
1249 glActiveTexture(GL_TEXTURE7);
1250 glBindTexture(GL_TEXTURE_BUFFER, coordinate_flag_texture_object.at(point_ind));
1253 glActiveTexture(GL_TEXTURE2);
1254 glBindTexture(GL_TEXTURE_BUFFER, sky_geometry_flag_texture_object.at(point_ind));
1257 glActiveTexture(GL_TEXTURE8);
1258 glBindTexture(GL_TEXTURE_BUFFER, hidden_flag_texture_object.at(point_ind));
1261 glBindVertexArray(primaryShader.vertex_array_IDs.at(point_ind));
1264 const std::vector<float> *size_data = geometry_handler.
getSizeData_ptr(GeometryHandler::GEOMETRY_TYPE_POINT);
1265 if (size_data && !size_data->empty()) {
1267 std::map<float, std::vector<size_t>> size_groups;
1268 for (
size_t i = 0; i < size_data->size(); ++i) {
1269 float size = size_data->at(i);
1272 size_groups[size].push_back(i);
1276 for (
const auto &group: size_groups) {
1277 float size = group.first;
1278 const std::vector<size_t> &point_indices = group.second;
1284 for (
size_t point_idx: point_indices) {
1285 glDrawArrays(GL_POINTS, point_idx, 1);
1290 glPointSize(point_width);
1291 glDrawArrays(GL_POINTS, 0, point_count);
1304 glActiveTexture(GL_TEXTURE0);
1306 assert(checkerrors());
1315 if (!headless && window !=
nullptr && glfwWindowShouldClose(scast<GLFWwindow *>(window))) {
1320 std::cout <<
"Updating the plot..." << std::flush;
1324 if (!hide_window && headless) {
1326 std::cout <<
"\nWARNING: plotUpdate(false) called in headless mode - window cannot be displayed. Use plotUpdate(true) for headless rendering." << std::endl;
1330 if (!hide_window && !headless && window !=
nullptr) {
1331 glfwShowWindow(scast<GLFWwindow *>(window));
1335 buildContextGeometry_private();
1338 updatePointCulling();
1341 if (camera_lookat_center.
x == 0 && camera_lookat_center.
y == 0 && camera_lookat_center.
z == 0) {
1342 if (camera_eye_location.
x < 1e-4 && camera_eye_location.
y < 1e-4 && camera_eye_location.
z == 2.f) {
1347 float domain_bounding_radius = radius.
magnitude();
1349 vec2 xbounds, ybounds, zbounds;
1351 camera_lookat_center =
make_vec3(0.5f * (xbounds.
x + xbounds.
y), 0.5f * (ybounds.
x + ybounds.
y), 0.5f * (zbounds.
x + zbounds.
y));
1363 if (navigation_gizmo_enabled) {
1364 if (navigation_gizmo_IDs.empty() || cameraHasChanged()) {
1365 updateNavigationGizmo();
1366 previous_camera_eye_location = camera_eye_location;
1367 previous_camera_lookat_center = camera_lookat_center;
1371 transferBufferData();
1379 glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
1380 glViewport(0, 0, shadow_buffer_size.
x, shadow_buffer_size.
y);
1383 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1387 updatePerspectiveTransformation(
true);
1390 depthMVP = computeShadowDepthMVP();
1394 glActiveTexture(GL_TEXTURE1);
1395 glBindTexture(GL_TEXTURE_2D, depthTexture);
1396 glActiveTexture(GL_TEXTURE0);
1403 depthMVP = glm::mat4(1.0);
1406 assert(checkerrors());
1409 if (headless && offscreenFramebufferID != 0) {
1411 glBindFramebuffer(GL_FRAMEBUFFER, offscreenFramebufferID);
1412 glViewport(0, 0, Wframebuffer, Hframebuffer);
1415 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1416 glViewport(0, 0, Wframebuffer, Hframebuffer);
1419 if (background_is_transparent) {
1420 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
1422 glClearColor(backgroundColor.
r, backgroundColor.
g, backgroundColor.
b, 1.0f);
1425 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1429 glm::mat4 DepthBiasMVP = biasMatrix * depthMVP;
1433 updatePerspectiveTransformation(
false);
1445 glActiveTexture(GL_TEXTURE1);
1446 glBindTexture(GL_TEXTURE_2D, depthTexture);
1447 glUniform1i(primaryShader.shadowmapUniform, 1);
1448 glActiveTexture(GL_TEXTURE0);
1450 buffers_swapped_since_render =
false;
1454 if (!headless && window !=
nullptr) {
1456 getViewKeystrokes(camera_eye_location, camera_lookat_center);
1459 if (navigation_gizmo_enabled && cameraHasChanged()) {
1460 updateNavigationGizmo();
1461 previous_camera_eye_location = camera_eye_location;
1462 previous_camera_lookat_center = camera_lookat_center;
1464 transferBufferData();
1468 glfwGetFramebufferSize((GLFWwindow *) window, &width, &height);
1469 Wframebuffer = width;
1470 Hframebuffer = height;
1472 glfwSwapBuffers((GLFWwindow *) window);
1473 buffers_swapped_since_render =
true;
1477 std::cout <<
"done." << std::endl;
1481void Visualizer::updateDepthBuffer() {
1484 buildContextGeometry_private();
1487 transferBufferData();
1490 glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
1491 glViewport(0, 0, Wframebuffer, Hframebuffer);
1494 glActiveTexture(GL_TEXTURE1);
1495 glBindTexture(GL_TEXTURE_2D, depthTexture);
1496 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, Wframebuffer, Hframebuffer, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
nullptr);
1497 glActiveTexture(GL_TEXTURE0);
1500 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1504 updatePerspectiveTransformation(
false);
1512 assert(checkerrors());
1514 depth_buffer_data.resize(Wframebuffer * Hframebuffer);
1516#if defined(__APPLE__)
1517 constexpr GLenum read_buf = GL_FRONT;
1519 constexpr GLenum read_buf = GL_BACK;
1521 glReadBuffer(read_buf);
1522 glReadPixels(0, 0, Wframebuffer, Hframebuffer, GL_DEPTH_COMPONENT, GL_FLOAT, depth_buffer_data.data());
1525 assert(checkerrors());
1532 std::cout <<
"Rendering depth map..." << std::flush;
1535 updateDepthBuffer();
1538 float depth_min = (std::numeric_limits<float>::max)();
1539 float depth_max = (std::numeric_limits<float>::min)();
1540 for (
auto depth: depth_buffer_data) {
1541 if (depth < depth_min) {
1544 if (depth > depth_max) {
1548 std::vector<unsigned char> depth_uchar(depth_buffer_data.size() * 4);
1549 for (
size_t i = 0; i < depth_buffer_data.size(); i++) {
1550 auto value = scast<unsigned char>(std::round((depth_buffer_data.at(i) - depth_min) / (depth_max - depth_min) * 255));
1551 value =
clamp(value, scast<unsigned char>(0), scast<unsigned char>(255));
1552 size_t row = i / Wframebuffer;
1553 size_t col = i % Wframebuffer;
1554 size_t flipped_i = (Hframebuffer - 1 - row) * Wframebuffer + col;
1555 depth_uchar.at(flipped_i * 4) = 255 - value;
1556 depth_uchar.at(flipped_i * 4 + 1) = 255 - value;
1557 depth_uchar.at(flipped_i * 4 + 2) = 255 - value;
1558 depth_uchar.at(flipped_i * 4 + 3) = 255;
1564 std::cout <<
"done." << std::endl;
1571 assert(checkerrors());
1574 unsigned int VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
1575 unsigned int FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
1576 unsigned int GeometryShaderID = 0;
1579 std::string VertexShaderCode;
1580 std::ifstream VertexShaderStream(vertex_shader_file, std::ios::in);
1581 assert(VertexShaderStream.is_open());
1583 while (getline(VertexShaderStream, Line))
1584 VertexShaderCode +=
"\n" + Line;
1585 VertexShaderStream.close();
1588 std::string FragmentShaderCode;
1589 std::ifstream FragmentShaderStream(fragment_shader_file, std::ios::in);
1590 assert(FragmentShaderStream.is_open());
1592 while (getline(FragmentShaderStream, Line))
1593 FragmentShaderCode +=
"\n" + Line;
1594 FragmentShaderStream.close();
1597 std::string GeometryShaderCode;
1598 bool hasGeometryShader = (geometry_shader_file !=
nullptr);
1599 if (hasGeometryShader) {
1600 GeometryShaderID = glCreateShader(GL_GEOMETRY_SHADER);
1601 std::ifstream GeometryShaderStream(geometry_shader_file, std::ios::in);
1602 assert(GeometryShaderStream.is_open());
1604 while (getline(GeometryShaderStream, Line))
1605 GeometryShaderCode +=
"\n" + Line;
1606 GeometryShaderStream.close();
1610 char const *VertexSourcePointer = VertexShaderCode.c_str();
1611 glShaderSource(VertexShaderID, 1, &VertexSourcePointer,
nullptr);
1612 glCompileShader(VertexShaderID);
1614 assert(checkerrors());
1617 GLint compileOK = GL_FALSE;
1618 glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &compileOK);
1619 if (compileOK != GL_TRUE) {
1621 glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &logLen);
1622 std::vector<char> log(logLen);
1623 glGetShaderInfoLog(VertexShaderID, logLen,
nullptr, log.data());
1624 fprintf(stderr,
"Vertex shader compilation failed:\n%s\n", log.data());
1625 throw std::runtime_error(
"vertex shader compile error");
1629 char const *FragmentSourcePointer = FragmentShaderCode.c_str();
1630 glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer,
nullptr);
1631 glCompileShader(FragmentShaderID);
1633 assert(checkerrors());
1636 compileOK = GL_FALSE;
1637 glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &compileOK);
1638 if (compileOK != GL_TRUE) {
1640 glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &logLen);
1641 std::vector<char> log(logLen);
1642 glGetShaderInfoLog(FragmentShaderID, logLen,
nullptr, log.data());
1643 fprintf(stderr,
"Fragment shader compilation failed:\n%s\n", log.data());
1644 throw std::runtime_error(
"fragment shader compile error");
1648 if (hasGeometryShader) {
1649 char const *GeometrySourcePointer = GeometryShaderCode.c_str();
1650 glShaderSource(GeometryShaderID, 1, &GeometrySourcePointer,
nullptr);
1651 glCompileShader(GeometryShaderID);
1653 assert(checkerrors());
1656 compileOK = GL_FALSE;
1657 glGetShaderiv(GeometryShaderID, GL_COMPILE_STATUS, &compileOK);
1658 if (compileOK != GL_TRUE) {
1660 glGetShaderiv(GeometryShaderID, GL_INFO_LOG_LENGTH, &logLen);
1661 std::vector<char> log(logLen);
1662 glGetShaderInfoLog(GeometryShaderID, logLen,
nullptr, log.data());
1663 fprintf(stderr,
"Geometry shader compilation failed:\n%s\n", log.data());
1664 throw std::runtime_error(
"geometry shader compile error");
1669 shaderID = glCreateProgram();
1670 glAttachShader(shaderID, VertexShaderID);
1671 glAttachShader(shaderID, FragmentShaderID);
1672 if (hasGeometryShader) {
1673 glAttachShader(shaderID, GeometryShaderID);
1675 glLinkProgram(shaderID);
1677 assert(checkerrors());
1679 GLint linkOK = GL_FALSE;
1680 glGetProgramiv(shaderID, GL_LINK_STATUS, &linkOK);
1681 if (linkOK != GL_TRUE) {
1683 glGetProgramiv(shaderID, GL_INFO_LOG_LENGTH, &logLen);
1684 std::vector<char> log(logLen);
1685 glGetProgramInfoLog(shaderID, logLen,
nullptr, log.data());
1686 fprintf(stderr,
"Shader program link failed:\n%s\n", log.data());
1687 throw std::runtime_error(
"program link error");
1690 assert(checkerrors());
1692 glDeleteShader(VertexShaderID);
1693 glDeleteShader(FragmentShaderID);
1694 if (hasGeometryShader) {
1695 glDeleteShader(GeometryShaderID);
1698 assert(checkerrors());
1701 vertex_array_IDs.resize(GeometryHandler::all_geometry_types.size());
1702 glGenVertexArrays(GeometryHandler::all_geometry_types.size(), vertex_array_IDs.data());
1704 assert(checkerrors());
1709 for (
const auto &geometry_type: GeometryHandler::all_geometry_types) {
1710 glBindVertexArray(vertex_array_IDs.at(i));
1713 glBindBuffer(GL_ARRAY_BUFFER, visualizer_ptr->vertex_buffer.at(i));
1714 glEnableVertexAttribArray(0);
1715 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0,
nullptr);
1718 glBindBuffer(GL_ARRAY_BUFFER, visualizer_ptr->uv_buffer.at(i));
1719 glEnableVertexAttribArray(1);
1720 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0,
nullptr);
1723 glBindBuffer(GL_ARRAY_BUFFER, visualizer_ptr->face_index_buffer.at(i));
1724 glEnableVertexAttribArray(2);
1725 glVertexAttribIPointer(2, 1, GL_INT, 0,
nullptr);
1730 glBindBuffer(GL_ARRAY_BUFFER, 0);
1732 assert(checkerrors());
1734 glUseProgram(shaderID);
1736 assert(checkerrors());
1741 transformMatrixUniform = glGetUniformLocation(shaderID,
"MVP");
1744 viewMatrixUniform = glGetUniformLocation(shaderID,
"view");
1745 projectionMatrixUniform = glGetUniformLocation(shaderID,
"projection");
1748 depthBiasUniform = glGetUniformLocation(shaderID,
"DepthBiasMVP");
1751 textureUniform = glGetUniformLocation(shaderID,
"textureSampler");
1754 shadowmapUniform = glGetUniformLocation(shaderID,
"shadowMap");
1755 glUniform1i(shadowmapUniform, 1);
1758 lightDirectionUniform = glGetUniformLocation(shaderID,
"lightDirection");
1759 glUniform3f(lightDirectionUniform, 0, 0, 1);
1762 lightingModelUniform = glGetUniformLocation(shaderID,
"lightingModel");
1763 glUniform1i(lightingModelUniform, 0);
1765 RboundUniform = glGetUniformLocation(shaderID,
"Rbound");
1766 glUniform1i(RboundUniform, 0);
1769 lightIntensityUniform = glGetUniformLocation(shaderID,
"lightIntensity");
1770 glUniform1f(lightIntensityUniform, 1.f);
1773 uvRescaleUniform = glGetUniformLocation(shaderID,
"uv_rescale");
1777 colorTextureObjectUniform = glGetUniformLocation(shaderID,
"color_texture_object");
1778 normalTextureObjectUniform = glGetUniformLocation(shaderID,
"normal_texture_object");
1779 textureFlagTextureObjectUniform = glGetUniformLocation(shaderID,
"texture_flag_texture_object");
1780 textureIDTextureObjectUniform = glGetUniformLocation(shaderID,
"texture_ID_texture_object");
1781 coordinateFlagTextureObjectUniform = glGetUniformLocation(shaderID,
"coordinate_flag_texture_object");
1782 skyGeometryFlagTextureObjectUniform = glGetUniformLocation(shaderID,
"sky_geometry_flag_texture_object");
1783 hiddenFlagTextureObjectUniform = glGetUniformLocation(shaderID,
"hidden_flag_texture_object");
1786 if (colorTextureObjectUniform >= 0)
1787 glUniform1i(colorTextureObjectUniform, 3);
1788 if (normalTextureObjectUniform >= 0)
1789 glUniform1i(normalTextureObjectUniform, 4);
1790 if (textureFlagTextureObjectUniform >= 0)
1791 glUniform1i(textureFlagTextureObjectUniform, 5);
1792 if (textureIDTextureObjectUniform >= 0)
1793 glUniform1i(textureIDTextureObjectUniform, 6);
1794 if (coordinateFlagTextureObjectUniform >= 0)
1795 glUniform1i(coordinateFlagTextureObjectUniform, 7);
1796 if (skyGeometryFlagTextureObjectUniform >= 0)
1797 glUniform1i(skyGeometryFlagTextureObjectUniform, 2);
1798 if (hiddenFlagTextureObjectUniform >= 0)
1799 glUniform1i(hiddenFlagTextureObjectUniform, 8);
1801 assert(checkerrors());
1810 glDeleteVertexArrays(vertex_array_IDs.size(), vertex_array_IDs.data());
1811 glDeleteProgram(shaderID);
1816 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1820 glActiveTexture(GL_TEXTURE0);
1821 glUniform1i(textureUniform, 0);
1824 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1825 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1826 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1827 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1828 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1829 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1831 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1835 glActiveTexture(GL_TEXTURE0);
1836 glUniform1i(textureUniform, 0);
1837 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1838 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1839 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1840 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1841 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1842 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1844 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1848 glUniformMatrix4fv(transformMatrixUniform, 1, GL_FALSE, &matrix[0][0]);
1852 glUniformMatrix4fv(viewMatrixUniform, 1, GL_FALSE, &matrix[0][0]);
1856 glUniformMatrix4fv(projectionMatrixUniform, 1, GL_FALSE, &matrix[0][0]);
1860 glUniformMatrix4fv(depthBiasUniform, 1, GL_FALSE, &matrix[0][0]);
1864 glUniform3f(lightDirectionUniform, direction.
x, direction.
y, direction.
z);
1868 glUniform1i(lightingModelUniform, lightingmodel);
1872 glUniform1f(lightIntensityUniform, lightintensity);
1876 glUseProgram(shaderID);
1879void Visualizer::framebufferResizeCallback(GLFWwindow *window,
int width,
int height) {
1880 if (width <= 0 || height <= 0) {
1883 auto *viz =
static_cast<Visualizer *
>(glfwGetWindowUserPointer(window));
1884 if (viz !=
nullptr) {
1885 viz->Wframebuffer =
static_cast<uint>(width);
1886 viz->Hframebuffer =
static_cast<uint>(height);
1890void Visualizer::windowResizeCallback(GLFWwindow *window,
int width,
int height) {
1891 if (width <= 0 || height <= 0) {
1894 auto *viz =
static_cast<Visualizer *
>(glfwGetWindowUserPointer(window));
1895 if (viz !=
nullptr) {
1897 glfwGetFramebufferSize(window, &fbw, &fbh);
1898 if (fbw != width || fbh != height) {
1899 glfwSetWindowSize(window, width, height);
1903 viz->Wdisplay =
static_cast<uint>(width);
1904 viz->Hdisplay =
static_cast<uint>(height);
1905 viz->Wframebuffer =
static_cast<uint>(fbw);
1906 viz->Hframebuffer =
static_cast<uint>(fbh);
1907 viz->updateWatermark();
1908 viz->updateNavigationGizmo();
1909 viz->transferBufferData();
1914 if (!isWatermarkVisible) {
1915 if (watermark_ID != 0) {
1922 constexpr float texture_aspect = 675.f / 195.f;
1924 float window_aspect = float(Wframebuffer) / float(Hframebuffer);
1925 float width = 0.07f * texture_aspect / window_aspect;
1926 if (watermark_ID != 0) {
1933void Visualizer::updateColorbar() {
1935 if (colorbar_flag != 2) {
1937 if (!colorbar_IDs.empty()) {
1939 colorbar_IDs.clear();
1945 float window_aspect = float(Wframebuffer) / float(Hframebuffer);
1950 float corrected_width = colorbar_size.
y * colorbar_intended_aspect_ratio / window_aspect;
1953 if (!colorbar_IDs.empty()) {
1955 colorbar_IDs.clear();
1959 colorbar_IDs = addColorbarByCenter(colorbar_title.c_str(),
make_vec2(corrected_width, colorbar_size.
y), colorbar_position, colorbar_fontcolor, colormap_current);
1963bool lbutton_down =
false;
1964bool rbutton_down =
false;
1965bool mbutton_down =
false;
1966double startX, startY;
1967double scrollX, scrollY;
1971double click_startX = 0.0, click_startY = 0.0;
1972bool potential_click =
false;
1973const double click_threshold = 5.0;
1977 if (action == GLFW_PRESS) {
1978 glfwGetCursorPos(window, &startX, &startY);
1980 if (button == GLFW_MOUSE_BUTTON_LEFT) {
1981 if (GLFW_PRESS == action) {
1982 lbutton_down =
true;
1984 glfwGetCursorPos(window, &click_startX, &click_startY);
1985 potential_click =
true;
1986 }
else if (GLFW_RELEASE == action) {
1987 lbutton_down =
false;
1989 if (potential_click) {
1990 double releaseX, releaseY;
1991 glfwGetCursorPos(window, &releaseX, &releaseY);
1992 double dx_click = releaseX - click_startX;
1993 double dy_click = releaseY - click_startY;
1994 double movement = std::sqrt(dx_click * dx_click + dy_click * dy_click);
1996 if (movement < click_threshold) {
1998 auto *visualizer =
static_cast<Visualizer *
>(glfwGetWindowUserPointer(window));
1999 if (visualizer !=
nullptr) {
2003 potential_click =
false;
2006 }
else if (button == GLFW_MOUSE_BUTTON_MIDDLE) {
2007 if (GLFW_PRESS == action) {
2008 mbutton_down =
true;
2009 }
else if (GLFW_RELEASE == action) {
2010 mbutton_down =
false;
2012 }
else if (button == GLFW_MOUSE_BUTTON_RIGHT) {
2013 if (GLFW_PRESS == action) {
2014 rbutton_down =
true;
2015 }
else if (GLFW_RELEASE == action) {
2016 rbutton_down =
false;
2026 }
else if (lbutton_down || mbutton_down) {
2027 dphi = scast<float>(xpos - startX);
2028 dtheta = scast<float>(ypos - startY);
2031 if (potential_click && lbutton_down) {
2032 double dx_click = xpos - click_startX;
2033 double dy_click = ypos - click_startY;
2034 double movement = std::sqrt(dx_click * dx_click + dy_click * dy_click);
2035 if (movement >= click_threshold) {
2036 potential_click =
false;
2040 dphi = dtheta = 0.f;
2043 auto *visualizer =
static_cast<Visualizer *
>(glfwGetWindowUserPointer(window));
2044 if (visualizer !=
nullptr) {
2054 dscroll = scast<float>(yoffset);
2056 if (yoffset > 0.0 || yoffset < 0.0) {
2064void Visualizer::getViewKeystrokes(
vec3 &eye,
vec3 ¢er) {
2065 vec3 forward = center - eye;
2075 float radius = Spherical.
radius;
2077 float phi = Spherical.
azimuth;
2079 phi +=
PI_F * (dphi / 160.f);
2080 if (dtheta > 0 && theta +
PI_F / 80.f < 0.49f *
PI_F) {
2081 theta +=
PI_F * (dtheta / 120.f);
2082 }
else if (dtheta < 0 && theta > -0.25 *
PI_F) {
2083 theta +=
PI_F * (dtheta / 120.f);
2087 center -= 0.025f * dx * right;
2090 center += 0.025f * dy * up;
2094 if (dscroll > 0.0f) {
2095 radius = (radius * 0.9f > minimum_view_radius) ? radius * 0.9f : minimum_view_radius;
2102 auto *_window = scast<GLFWwindow *>(window);
2105 if (glfwGetKey(_window, GLFW_KEY_SPACE) == GLFW_PRESS) {
2107 if (glfwGetKey(_window, GLFW_KEY_LEFT) == GLFW_PRESS) {
2108 center.
x += 0.1f * sin(phi);
2109 center.
y += 0.1f * cos(phi);
2112 else if (glfwGetKey(_window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
2113 center.
x -= 0.1f * sin(phi);
2114 center.
y -= 0.1f * cos(phi);
2117 else if (glfwGetKey(_window, GLFW_KEY_UP) == GLFW_PRESS) {
2121 else if (glfwGetKey(_window, GLFW_KEY_DOWN) == GLFW_PRESS) {
2128 if (glfwGetKey(_window, GLFW_KEY_LEFT) == GLFW_PRESS) {
2132 else if (glfwGetKey(_window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
2137 else if (glfwGetKey(_window, GLFW_KEY_UP) == GLFW_PRESS) {
2138 if (theta +
PI_F / 80.f < 0.49f *
PI_F) {
2139 theta +=
PI_F / 80.f;
2143 else if (glfwGetKey(_window, GLFW_KEY_DOWN) == GLFW_PRESS) {
2144 if (theta > -0.25 *
PI_F) {
2145 theta -=
PI_F / 80.f;
2150 if (glfwGetKey(_window, GLFW_KEY_EQUAL) == GLFW_PRESS) {
2151 radius = (radius * 0.9f > minimum_view_radius) ? radius * 0.9f : minimum_view_radius;
2154 else if (glfwGetKey(_window, GLFW_KEY_MINUS) == GLFW_PRESS) {
2159 if (glfwGetKey(_window, GLFW_KEY_P) == GLFW_PRESS) {
2160 std::cout <<
"View is angle: (R,theta,phi)=(" << radius <<
"," << theta <<
"," << phi <<
") at from position (" << camera_eye_location.
x <<
"," << camera_eye_location.
y <<
"," << camera_eye_location.
z <<
") looking at (" << center.
x <<
","
2161 << center.
y <<
"," << center.
z <<
")" << std::endl;
2167void Visualizer::cullPointsByFrustum() {
2168 const std::vector<float> *vertex_data = geometry_handler.
getVertexData_ptr(GeometryHandler::GEOMETRY_TYPE_POINT);
2169 if (!vertex_data || vertex_data->empty()) {
2173 std::vector<glm::vec4> frustum_planes = extractFrustumPlanes();
2176 size_t point_count = vertex_data->size() / 3;
2177 for (
size_t i = 0; i < point_count; ++i) {
2178 glm::vec3 point(vertex_data->at(i * 3), vertex_data->at(i * 3 + 1), vertex_data->at(i * 3 + 2));
2180 bool inside_frustum =
true;
2181 for (
const auto &plane: frustum_planes) {
2184 if (glm::dot(glm::vec3(plane), point) + plane.w < 0) {
2185 inside_frustum =
false;
2191 std::vector<size_t> all_UUIDs = geometry_handler.getAllGeometryIDs();
2192 size_t point_index = 0;
2193 for (
size_t UUID: all_UUIDs) {
2194 if (geometry_handler.
getIndexMap(UUID).geometry_type == GeometryHandler::GEOMETRY_TYPE_POINT) {
2195 if (point_index == i) {
2205void Visualizer::cullPointsByDistance(
float maxDistance,
float lodFactor) {
2206 const std::vector<float> *vertex_data = geometry_handler.
getVertexData_ptr(GeometryHandler::GEOMETRY_TYPE_POINT);
2207 if (!vertex_data || vertex_data->empty()) {
2211 glm::vec3 camera_pos(camera_eye_location.
x, camera_eye_location.
y, camera_eye_location.
z);
2214 size_t point_count = vertex_data->size() / 3;
2215 for (
size_t i = 0; i < point_count; ++i) {
2216 glm::vec3 point(vertex_data->at(i * 3), vertex_data->at(i * 3 + 1), vertex_data->at(i * 3 + 2));
2218 float distance = glm::length(point - camera_pos);
2219 bool should_render =
true;
2220 float adaptive_size = 1.0f;
2223 if (distance > maxDistance) {
2224 should_render =
false;
2227 else if (distance > maxDistance * 0.3f) {
2228 float distance_ratio = distance / maxDistance;
2229 float lod_threshold = distance_ratio * lodFactor;
2232 if ((i %
static_cast<size_t>(std::max(1.0f, lod_threshold))) != 0) {
2233 should_render =
false;
2236 adaptive_size = 1.0f + (distance_ratio * 3.0f);
2241 std::vector<size_t> all_UUIDs = geometry_handler.getAllGeometryIDs();
2242 size_t point_index = 0;
2243 for (
size_t UUID: all_UUIDs) {
2244 if (geometry_handler.
getIndexMap(UUID).geometry_type == GeometryHandler::GEOMETRY_TYPE_POINT) {
2245 if (point_index == i) {
2247 if (should_render) {
2249 float original_size = geometry_handler.
getSize(UUID);
2250 if (original_size <= 0)
2251 original_size = 1.0f;
2252 geometry_handler.
setSize(UUID, original_size * adaptive_size);
2262void Visualizer::updatePointCulling() {
2267 if (!point_culling_enabled || points_total_count < point_culling_threshold) {
2268 points_rendered_count = points_total_count;
2269 last_culling_time_ms = 0;
2274 auto start_time = std::chrono::high_resolution_clock::now();
2277 cullPointsByFrustum();
2280 float max_distance = point_max_render_distance;
2281 if (max_distance <= 0) {
2284 float scene_size = std::max({xbounds.
y - xbounds.
x, ybounds.
y - ybounds.
x, zbounds.
y - zbounds.
x});
2285 max_distance = scene_size * 5.0f;
2289 cullPointsByDistance(max_distance, point_lod_factor);
2292 points_rendered_count = geometry_handler.
getPointCount(
false);
2294 auto end_time = std::chrono::high_resolution_clock::now();
2295 last_culling_time_ms = std::chrono::duration<float, std::milli>(end_time - start_time).count();
2298std::vector<glm::vec4> Visualizer::extractFrustumPlanes()
const {
2299 std::vector<glm::vec4> planes(6);
2302 glm::mat4 mvp = cameraProjectionMatrix * cameraViewMatrix;
2305 planes[0] = glm::vec4(mvp[0][3] + mvp[0][0], mvp[1][3] + mvp[1][0], mvp[2][3] + mvp[2][0], mvp[3][3] + mvp[3][0]);
2307 planes[1] = glm::vec4(mvp[0][3] - mvp[0][0], mvp[1][3] - mvp[1][0], mvp[2][3] - mvp[2][0], mvp[3][3] - mvp[3][0]);
2309 planes[2] = glm::vec4(mvp[0][3] + mvp[0][1], mvp[1][3] + mvp[1][1], mvp[2][3] + mvp[2][1], mvp[3][3] + mvp[3][1]);
2311 planes[3] = glm::vec4(mvp[0][3] - mvp[0][1], mvp[1][3] - mvp[1][1], mvp[2][3] - mvp[2][1], mvp[3][3] - mvp[3][1]);
2313 planes[4] = glm::vec4(mvp[0][3] + mvp[0][2], mvp[1][3] + mvp[1][2], mvp[2][3] + mvp[2][2], mvp[3][3] + mvp[3][2]);
2315 planes[5] = glm::vec4(mvp[0][3] - mvp[0][2], mvp[1][3] - mvp[1][2], mvp[2][3] - mvp[2][2], mvp[3][3] - mvp[3][2]);
2318 for (
auto &plane: planes) {
2319 float length = glm::length(glm::vec3(plane));
2329 point_culling_enabled = enabled;
2333 point_culling_threshold = threshold;
2337 point_max_render_distance = distance;
2341 point_lod_factor = factor;
2345 total_points = points_total_count;
2346 rendered_points = points_rendered_count;
2347 culling_time_ms = last_culling_time_ms;
2350std::string errorString(GLenum err) {
2351 std::string message;
2354 if (err == GL_INVALID_ENUM) {
2355 message.assign(
"GL_INVALID_ENUM - An unacceptable value is specified for an enumerated argument.");
2356 }
else if (err == GL_INVALID_VALUE) {
2357 message.assign(
"GL_INVALID_VALUE - A numeric argument is out of range.");
2358 }
else if (err == GL_INVALID_OPERATION) {
2359 message.assign(
"GL_INVALID_OPERATION - The specified operation is not allowed in the current state.");
2360 }
else if (err == GL_STACK_OVERFLOW) {
2361 message.assign(
"GL_STACK_OVERFLOW - This command would cause a stack overflow.");
2362 }
else if (err == GL_STACK_UNDERFLOW) {
2363 message.assign(
"GL_STACK_UNDERFLOW - This command would cause a stack underflow.");
2364 }
else if (err == GL_OUT_OF_MEMORY) {
2365 message.assign(
"GL_OUT_OF_MEMORY - There is not enough memory left to execute the command.");
2366 }
else if (err == GL_TABLE_TOO_LARGE) {
2367 message.assign(
"GL_TABLE_TOO_LARGE - The specified table exceeds the implementation's maximum supported table size.");
2376 while ((err = glGetError()) != GL_NO_ERROR) {
2377 std::cerr <<
"glError #" << err_count <<
": " << errorString(err) << std::endl;
2380 if (err_count > 0) {
2389 if (!checkerrors()) {
2390 helios_runtime_error(
"ERROR (Visualizer): OpenGL errors detected in " + context +
". Check console output for specific error details.");