1.3.49
 
Loading...
Searching...
No Matches
VisualizerRendering.cpp
Go to the documentation of this file.
1
16// OpenGL Includes
17#include <GL/glew.h>
18#include <GLFW/glfw3.h>
19
20// #include <chrono>
21
22#include "Visualizer.h"
23
24using namespace helios;
25
26float dphi = 0.0;
27float dtheta = 0.0;
28float dx = 0.0;
29float dy = 0.0;
30float dz = 0.0;
31float dx_m = 0.0;
32float dy_m = 0.0;
33float dscroll = 0.0;
34
36 char outfile[100];
37 if (context != nullptr) { // context has been given to visualizer via buildContextGeometry()
38 Date date = context->getDate();
39 Time time = context->getTime();
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);
41 } else {
42 std::snprintf(outfile, 100, "frame%d.jpg", frame_counter);
43 }
44 frame_counter++;
45
46 printWindow(outfile);
47}
48
49void Visualizer::printWindow(const char *outfile) const {
50 std::string outfile_str = outfile;
51
52 std::string ext = getFileExtension(outfile_str);
53 if (ext.empty()) {
54 outfile_str += ".jpeg";
55 }
56
57 if (!validateOutputPath(outfile_str, {".jpeg", ".jpg", ".JPEG", ".JPG"})) {
58 helios_runtime_error("ERROR (Visualizer::printWindow): Output path is not valid or does not have a valid image extension (.jpeg, .jpg).");
59 }
60
61 // Don't swap buffers again - content is already displayed by plotUpdate()
62 // Just ensure rendering is complete and read from the front buffer
63 if (window != nullptr && !headless) {
64 glfwPollEvents();
65 }
66
67 // Ensure rendering is complete
68 glFinish();
69
70 // Read pixels from front buffer (where the displayed content is)
71 write_JPEG_file(outfile_str.c_str(), Wframebuffer, Hframebuffer, message_flag);
72}
73
74void Visualizer::displayImage(const std::vector<unsigned char> &pixel_data, uint width_pixels, uint height_pixels) {
75 if (pixel_data.empty()) {
76 helios_runtime_error("ERROR (Visualizer::displayImage): Pixel data was empty.");
77 }
78 if (pixel_data.size() != 4 * width_pixels * height_pixels) {
79 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.");
80 }
81
82 // Clear out any existing geometry
83 geometry_handler.clearAllGeometry();
84
85 // Register the data as a texture
86 uint textureID = registerTextureImage(pixel_data, helios::make_uint2(width_pixels, height_pixels));
87
88 // Figure out size to render
89 vec2 image_size;
90 float data_aspect = float(width_pixels) / float(height_pixels);
91 float window_aspect = float(Wdisplay) / float(Hdisplay);
92 if (data_aspect > window_aspect) {
93 // fill width, shrink height
94 image_size = make_vec2(1.0f, window_aspect / data_aspect);
95 } else {
96 // fill height, shrink width
97 image_size = make_vec2(data_aspect / window_aspect, 1.0f);
98 }
99
100 constexpr vec3 center(0.5, 0.5, 0);
101 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),
102 center + make_vec3(-0.5f * image_size.x, +0.5f * image_size.y, 0.f)};
103 const std::vector<vec2> uvs{{0, 0}, {1, 0}, {1, 1}, {0, 1}};
104
105 size_t UUID = geometry_handler.sampleUUID();
106 geometry_handler.addGeometry(UUID, GeometryHandler::GEOMETRY_TYPE_RECTANGLE, vertices, RGBA::black, uvs, textureID, false, false, COORDINATES_WINDOW_NORMALIZED, true, false);
107
110}
111
112
113void Visualizer::displayImage(const std::string &file_name) {
114 if (!validateTextureFile(file_name)) {
115 helios_runtime_error("ERROR (Visualizer::displayImage): File " + file_name + " does not exist or is not a valid image file.");
116 }
117
118 std::vector<unsigned char> image_data;
119 uint image_width, image_height;
120
121 if (file_name.substr(file_name.find_last_of('.') + 1) == "png") {
122 read_png_file(file_name.c_str(), image_data, image_height, image_width);
123 } else { // JPEG
124 read_JPEG_file(file_name.c_str(), image_data, image_height, image_width);
125 }
126
127 displayImage(image_data, image_width, image_height);
128}
129
131 std::vector<GLubyte> buff;
132 buff.resize(3 * Wframebuffer * Hframebuffer);
133
134#if defined(__APPLE__)
135 constexpr GLenum read_buf = GL_FRONT;
136#else
137 constexpr GLenum read_buf = GL_BACK;
138#endif
139 glReadBuffer(read_buf);
140 glReadPixels(0, 0, GLsizei(Wframebuffer), GLsizei(Hframebuffer), GL_RGB, GL_UNSIGNED_BYTE, &buff[0]);
141 glFinish();
142
143 // assert( checkerrors() );
144
145 for (int i = 0; i < 3 * Wframebuffer * Hframebuffer; i++) {
146 buffer[i] = (unsigned int) buff[i];
147 }
148}
149
150void Visualizer::getDepthMap(float *buffer) {
151 // if (depth_buffer_data.empty()) {
152 // helios_runtime_error("ERROR (Visualizer::getDepthMap): No depth map data available. You must run 'plotDepthMap' before depth map can be retrieved.");
153 // }
154 //
155 // updatePerspectiveTransformation(camera_lookat_center, camera_eye_location, true);
156 //
157 // for (int i = 0; i < depth_buffer_data.size(); i++) {
158 // buffer[i] = -perspectiveTransformationMatrix[3].z / (depth_buffer_data.at(i) * -2.0f + 1.0f - perspectiveTransformationMatrix[2].z);
159 // }
160
161 std::vector<float> depth_pixels;
162 uint width, height;
163 getDepthMap(depth_pixels, width, height);
164 for (size_t i = 0; i < depth_pixels.size(); ++i) {
165 buffer[i] = depth_pixels[i];
166 }
167}
168
169void Visualizer::getDepthMap(std::vector<float> &depth_pixels, uint &width_pixels, uint &height_pixels) {
170 width_pixels = Wdisplay;
171 height_pixels = Hdisplay;
172
173 depth_pixels.resize(width_pixels * height_pixels);
174
175 updateDepthBuffer();
176 // updatePerspectiveTransformation( false );
177
178 // un-project depth values to give physical depth
179
180 // build a viewport vector for unProject
181 // const glm::vec4 viewport(0, 0, width_pixels, height_pixels);
182 //
183 // for (size_t i = 0; i < width_pixels*height_pixels; ++i) {
184 // // compute pixel coords from linear index
185 // int x = int(i % width_pixels);
186 // int y = int(i / height_pixels);
187 //
188 // // center of the pixel
189 // float winx = float(x) + 0.5f;
190 // float winy = float(y) + 0.5f;
191 // float depth = depth_buffer_data.at(i); // in [0..1]
192 //
193 // // build the window‐space coordinate
194 // glm::vec3 winCoord(winx, winy, depth);
195 //
196 // // unProject to get world‐space position
197 // glm::vec3 worldPos = glm::unProject( winCoord, cameraViewMatrix, cameraProjectionMatrix, viewport );
198 //
199 // // transform into camera‐space
200 // const glm::vec4 camPos = cameraViewMatrix * glm::vec4(worldPos, 1.0f);
201 //
202 // // camPos.z is negative in front of the eye; flip sign for a positive distance
203 // // depth_pixels[i] = -camPos.z;
204 // depth_pixels[i] = depth*255.f;
205 // }
206
207 // normalize data and invert the color space so white=closest, black = furthest
208 float depth_min = (std::numeric_limits<float>::max)();
209 float depth_max = (std::numeric_limits<float>::min)();
210 for (auto depth: depth_buffer_data) {
211 if (depth < depth_min) {
212 depth_min = depth;
213 }
214 if (depth > depth_max) {
215 depth_max = depth;
216 }
217 }
218 for (size_t i = 0; i < depth_pixels.size(); i++) {
219 float value = std::round((depth_buffer_data.at(i) - depth_min) / (depth_max - depth_min) * 255);
220 value = clamp(value, 0.f, 255.f);
221 depth_pixels.at(i) = 255.f - value;
222 }
223
224 //\todo This is not working. Basically the same code works in the plotDepthMap() method, but for some reason doesn't seem to yield the correct float values.
225}
226
227void Visualizer::getWindowSize(uint &width, uint &height) const {
228 width = Wdisplay;
229 height = Hdisplay;
230}
231
232void Visualizer::getFramebufferSize(uint &width, uint &height) const {
233 width = Wframebuffer;
234 height = Hframebuffer;
235}
236
238 glfwHideWindow((GLFWwindow *) window);
239 glfwPollEvents();
240}
241
242std::vector<helios::vec3> Visualizer::plotInteractive() {
243 if (message_flag) {
244 std::cout << "Generating interactive plot..." << std::flush;
245 }
246
247
248 // Update the Context geometry
249 buildContextGeometry_private();
250
251 // Set the view to fit window
252 if (camera_lookat_center.x == 0 && camera_lookat_center.y == 0 && camera_lookat_center.z == 0) { // default center
253 if (camera_eye_location.x < 1e-4 && camera_eye_location.y < 1e-4 && camera_eye_location.z == 2.f) { // default eye position
254
255 vec3 center_sph;
256 vec3 radius;
257 geometry_handler.getDomainBoundingSphere(center_sph, radius);
258 float domain_bounding_radius = radius.magnitude();
259
260 vec2 xbounds, ybounds, zbounds;
261 geometry_handler.getDomainBoundingBox(xbounds, ybounds, zbounds);
262 camera_lookat_center = make_vec3(0.5f * (xbounds.x + xbounds.y), 0.5f * (ybounds.x + ybounds.y), zbounds.x);
263 camera_eye_location = camera_lookat_center + sphere2cart(make_SphericalCoord(2.f * domain_bounding_radius, 20.f * PI_F / 180.f, 0));
264 }
265 }
266
267 // Update
268 if (colorbar_flag == 2) {
269 if (!colorbar_IDs.empty()) {
270 geometry_handler.deleteGeometry(colorbar_IDs);
271 colorbar_IDs.clear();
272 }
273 colorbar_IDs = addColorbarByCenter(colorbar_title.c_str(), colorbar_size, colorbar_position, colorbar_fontcolor, colormap_current);
274 }
275
276
277 // Watermark
279
280 transferBufferData();
281
282 assert(checkerrors());
283
284 bool shadow_flag = false;
285 for (const auto &model: primaryLightingModel) {
287 shadow_flag = true;
288 break;
289 }
290 }
291
292 glm::mat4 depthMVP;
293
294 assert(checkerrors());
295
296 std::vector<vec3> camera_output;
297
298 glfwShowWindow((GLFWwindow *) window);
299
300 do {
301 if (shadow_flag) {
302 assert(checkerrors());
303 // Depth buffer for shadows
304 glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
305 glViewport(0, 0, shadow_buffer_size.x, shadow_buffer_size.y); // Render on the whole framebuffer, complete from the lower left corner to the upper right
306
307 // Clear the screen
308 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
309
310 depthShader.useShader();
311
312 updatePerspectiveTransformation(true);
313
314 // Compute the MVP matrix from the light's point of view
315 depthMVP = computeShadowDepthMVP();
316 depthShader.setTransformationMatrix(depthMVP);
317
318 // bind depth texture
319 glActiveTexture(GL_TEXTURE1);
320 glBindTexture(GL_TEXTURE_2D, depthTexture);
321 glActiveTexture(GL_TEXTURE0);
322
323 depthShader.enableTextureMaps();
324 depthShader.enableTextureMasks();
325
326 assert(checkerrors());
327
328 render(true);
329 } else {
330 depthMVP = glm::mat4(1.0);
331 }
332
333 // Render to the screen
334 glBindFramebuffer(GL_FRAMEBUFFER, 0);
335 glViewport(0, 0, Wframebuffer, Hframebuffer);
336
337 glClearColor(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0.0f);
338
339 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
340
341 primaryShader.useShader();
342
343 glm::mat4 DepthBiasMVP = biasMatrix * depthMVP;
344
345 primaryShader.setDepthBiasMatrix(DepthBiasMVP);
346
347 updatePerspectiveTransformation(false);
348
349 primaryShader.setTransformationMatrix(perspectiveTransformationMatrix);
350
351 primaryShader.enableTextureMaps();
352 primaryShader.enableTextureMasks();
353
354 primaryShader.setLightingModel(primaryLightingModel.at(0));
355 primaryShader.setLightIntensity(lightintensity);
356
357 glActiveTexture(GL_TEXTURE1);
358 glBindTexture(GL_TEXTURE_2D, depthTexture);
359 glUniform1i(primaryShader.shadowmapUniform, 1);
360 glActiveTexture(GL_TEXTURE0);
361
362 render(false);
363
364 glfwPollEvents();
365 getViewKeystrokes(camera_eye_location, camera_lookat_center);
366
367 glfwSwapBuffers((GLFWwindow *) window);
368
369 glfwWaitEventsTimeout(1.0 / 30.0);
370
371 int width, height;
372 glfwGetFramebufferSize((GLFWwindow *) window, &width, &height);
373 Wframebuffer = width;
374 Hframebuffer = height;
375 } while (glfwGetKey((GLFWwindow *) window, GLFW_KEY_ESCAPE) != GLFW_PRESS && glfwWindowShouldClose((GLFWwindow *) window) == 0);
376
377 glfwPollEvents();
378
379 assert(checkerrors());
380
381 camera_output.push_back(camera_eye_location);
382 camera_output.push_back(camera_lookat_center);
383
384 if (message_flag) {
385 std::cout << "done." << std::endl;
386 }
387
388 return camera_output;
389}
390
391void Visualizer::plotOnce(bool getKeystrokes) {
392
393 bool shadow_flag = false;
394 for (const auto &model: primaryLightingModel) {
396 shadow_flag = true;
397 break;
398 }
399 }
400
401 glm::mat4 depthMVP;
402
403 if (shadow_flag) {
404 // Depth buffer for shadows
405 glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
406 glViewport(0, 0, shadow_buffer_size.x, shadow_buffer_size.y); // Render on the whole framebuffer, complete from the lower left corner to the upper right
407
408 // Clear the screen
409 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
410
411 depthShader.useShader();
412
413 updatePerspectiveTransformation(true);
414
415 // Compute the MVP matrix from the light's point of view
416 depthMVP = computeShadowDepthMVP();
417 depthShader.setTransformationMatrix(depthMVP);
418
419 // bind depth texture
420 glActiveTexture(GL_TEXTURE1);
421 glBindTexture(GL_TEXTURE_2D, depthTexture);
422 glActiveTexture(GL_TEXTURE0);
423
424 depthShader.enableTextureMaps();
425 depthShader.enableTextureMasks();
426
427 render(true);
428 } else {
429 depthMVP = glm::mat4(1.0);
430 }
431
432 assert(checkerrors());
433
434 // Render to the screen
435 glBindFramebuffer(GL_FRAMEBUFFER, 0);
436 glViewport(0, 0, Wframebuffer, Hframebuffer);
437
438 glClearColor(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0.0f);
439
440 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
441
442 primaryShader.useShader();
443
444 glm::mat4 DepthBiasMVP = biasMatrix * depthMVP;
445
446 primaryShader.setDepthBiasMatrix(DepthBiasMVP);
447
448 updatePerspectiveTransformation(false);
449
450 primaryShader.setTransformationMatrix(perspectiveTransformationMatrix);
451
452 primaryShader.enableTextureMaps();
453 primaryShader.enableTextureMasks();
454
455 primaryShader.setLightingModel(primaryLightingModel.at(0));
456 primaryShader.setLightIntensity(lightintensity);
457
458 glActiveTexture(GL_TEXTURE1);
459 glBindTexture(GL_TEXTURE_2D, depthTexture);
460 glUniform1i(primaryShader.shadowmapUniform, 1);
461 glActiveTexture(GL_TEXTURE0);
462
463 render(false);
464
465 // glfwPollEvents();
466 if (getKeystrokes) {
467 getViewKeystrokes(camera_eye_location, camera_lookat_center);
468 }
469
470 int width, height;
471 glfwGetFramebufferSize((GLFWwindow *) window, &width, &height);
472 Wframebuffer = width;
473 Hframebuffer = height;
474}
475
476void Visualizer::transferBufferData() {
477 assert(checkerrors());
478
479 const auto &dirty = geometry_handler.getDirtyUUIDs();
480 if (dirty.empty()) {
481 return;
482 }
483
484 auto ensureArrayBuffer = [](GLuint buf, GLenum target, GLsizeiptr size, const void *data) {
485 glBindBuffer(target, buf);
486 GLint current_size = 0;
487 glGetBufferParameteriv(target, GL_BUFFER_SIZE, &current_size);
488 if (current_size != size) {
489 glBufferData(target, size, data, GL_STATIC_DRAW);
490 }
491 };
492
493 auto ensureTextureBuffer = [](GLuint buf, GLuint tex, GLenum format, GLsizeiptr size, const void *data) {
494 glBindBuffer(GL_TEXTURE_BUFFER, buf);
495 GLint current_size = 0;
496 glGetBufferParameteriv(GL_TEXTURE_BUFFER, GL_BUFFER_SIZE, &current_size);
497 if (current_size != size) {
498 glBufferData(GL_TEXTURE_BUFFER, size, data, GL_STATIC_DRAW);
499 }
500 glBindTexture(GL_TEXTURE_BUFFER, tex);
501 glTexBuffer(GL_TEXTURE_BUFFER, format, buf);
502 };
503
504 // Ensure buffers are allocated to the correct size
505 for (size_t gi = 0; gi < GeometryHandler::all_geometry_types.size(); ++gi) {
506 const auto geometry_type = GeometryHandler::all_geometry_types[gi];
507 const auto *vertex_data = geometry_handler.getVertexData_ptr(geometry_type);
508 const auto *uv_data = geometry_handler.getUVData_ptr(geometry_type);
509 const auto *face_index_data = geometry_handler.getFaceIndexData_ptr(geometry_type);
510 const auto *color_data = geometry_handler.getColorData_ptr(geometry_type);
511 const auto *normal_data = geometry_handler.getNormalData_ptr(geometry_type);
512 const auto *texture_flag_data = geometry_handler.getTextureFlagData_ptr(geometry_type);
513 const auto *texture_ID_data = geometry_handler.getTextureIDData_ptr(geometry_type);
514 const auto *coordinate_flag_data = geometry_handler.getCoordinateFlagData_ptr(geometry_type);
515 const auto *visible_flag_data = geometry_handler.getVisibilityFlagData_ptr(geometry_type);
516
517 ensureArrayBuffer(vertex_buffer.at(gi), GL_ARRAY_BUFFER, vertex_data->size() * sizeof(GLfloat), vertex_data->data());
518 ensureArrayBuffer(uv_buffer.at(gi), GL_ARRAY_BUFFER, uv_data->size() * sizeof(GLfloat), uv_data->data());
519 ensureArrayBuffer(face_index_buffer.at(gi), GL_ARRAY_BUFFER, face_index_data->size() * sizeof(GLint), face_index_data->data());
520 ensureTextureBuffer(color_buffer.at(gi), color_texture_object.at(gi), GL_RGBA32F, color_data->size() * sizeof(GLfloat), color_data->data());
521 ensureTextureBuffer(normal_buffer.at(gi), normal_texture_object.at(gi), GL_RGB32F, normal_data->size() * sizeof(GLfloat), normal_data->data());
522 ensureTextureBuffer(texture_flag_buffer.at(gi), texture_flag_texture_object.at(gi), GL_R32I, texture_flag_data->size() * sizeof(GLint), texture_flag_data->data());
523 ensureTextureBuffer(texture_ID_buffer.at(gi), texture_ID_texture_object.at(gi), GL_R32I, texture_ID_data->size() * sizeof(GLint), texture_ID_data->data());
524 ensureTextureBuffer(coordinate_flag_buffer.at(gi), coordinate_flag_texture_object.at(gi), GL_R32I, coordinate_flag_data->size() * sizeof(GLint), coordinate_flag_data->data());
525 ensureTextureBuffer(hidden_flag_buffer.at(gi), hidden_flag_texture_object.at(gi), GL_R8I, visible_flag_data->size() * sizeof(GLbyte), visible_flag_data->data());
526
527 glBindBuffer(GL_ARRAY_BUFFER, 0);
528 glBindTexture(GL_TEXTURE_BUFFER, 0);
529 }
530
531 bool rect_dirty = false;
532 for (size_t UUID: dirty) {
533 if (!geometry_handler.doesGeometryExist(UUID)) {
534 continue;
535 }
536
537 const auto &index_map = geometry_handler.getIndexMap(UUID);
538 auto geometry_type = index_map.geometry_type;
539 size_t i = std::find(GeometryHandler::all_geometry_types.begin(), GeometryHandler::all_geometry_types.end(), geometry_type) - GeometryHandler::all_geometry_types.begin();
540
541 const char vcount = GeometryHandler::getVertexCount(geometry_type);
542
543 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer.at(i));
544 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);
545
546 glBindBuffer(GL_ARRAY_BUFFER, uv_buffer.at(i));
547 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);
548
549 glBindBuffer(GL_ARRAY_BUFFER, face_index_buffer.at(i));
550 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);
551
552 glBindBuffer(GL_TEXTURE_BUFFER, color_buffer.at(i));
553 glBufferSubData(GL_TEXTURE_BUFFER, index_map.color_index * sizeof(GLfloat), 4 * sizeof(GLfloat), geometry_handler.getColorData_ptr(geometry_type)->data() + index_map.color_index);
554 glBindTexture(GL_TEXTURE_BUFFER, color_texture_object.at(i));
555 glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, color_buffer.at(i));
556
557 glBindBuffer(GL_ARRAY_BUFFER, normal_buffer.at(i));
558 glBufferSubData(GL_ARRAY_BUFFER, index_map.normal_index * sizeof(GLfloat), 3 * sizeof(GLfloat), geometry_handler.getNormalData_ptr(geometry_type)->data() + index_map.normal_index);
559 glBindTexture(GL_TEXTURE_BUFFER, normal_texture_object.at(i));
560 glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, normal_buffer.at(i));
561
562 glBindBuffer(GL_ARRAY_BUFFER, texture_flag_buffer.at(i));
563 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);
564 glBindTexture(GL_TEXTURE_BUFFER, texture_flag_texture_object.at(i));
565 glTexBuffer(GL_TEXTURE_BUFFER, GL_R32I, texture_flag_buffer.at(i));
566
567 glBindBuffer(GL_ARRAY_BUFFER, texture_ID_buffer.at(i));
568 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);
569 glBindTexture(GL_TEXTURE_BUFFER, texture_ID_texture_object.at(i));
570 glTexBuffer(GL_TEXTURE_BUFFER, GL_R32I, texture_ID_buffer.at(i));
571
572 glBindBuffer(GL_ARRAY_BUFFER, coordinate_flag_buffer.at(i));
573 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);
574 glBindTexture(GL_TEXTURE_BUFFER, coordinate_flag_texture_object.at(i));
575 glTexBuffer(GL_TEXTURE_BUFFER, GL_R32I, coordinate_flag_buffer.at(i));
576
577 glBindBuffer(GL_ARRAY_BUFFER, hidden_flag_buffer.at(i));
578 glBufferSubData(GL_ARRAY_BUFFER, index_map.visible_index * sizeof(GLbyte), sizeof(GLbyte), geometry_handler.getVisibilityFlagData_ptr(geometry_type)->data() + index_map.visible_index);
579 glBindTexture(GL_TEXTURE_BUFFER, hidden_flag_texture_object.at(i));
580 glTexBuffer(GL_TEXTURE_BUFFER, GL_R8I, hidden_flag_buffer.at(i));
581
582 glBindBuffer(GL_ARRAY_BUFFER, 0);
583 glBindTexture(GL_TEXTURE_BUFFER, 0);
584
585 if (geometry_type == GeometryHandler::GEOMETRY_TYPE_RECTANGLE) {
586 rect_dirty = true;
587 }
588 }
589
590 if (rect_dirty) {
591 size_t rectangle_count = geometry_handler.getRectangleCount();
592
593 rectangle_vertex_group_firsts.resize(rectangle_count);
594 rectangle_vertex_group_counts.resize(rectangle_count, 4);
595 for (int j = 0; j < rectangle_count; ++j) {
596 rectangle_vertex_group_firsts[j] = j * 4;
597 }
598 }
599
600 if (textures_dirty || texArray == 0) {
601 transferTextureData();
602 textures_dirty = false;
603 }
604
605 geometry_handler.clearDirtyUUIDs();
606
607 assert(checkerrors());
608}
609
610void Visualizer::transferTextureData() {
611 if (texArray == 0) {
612 glGenTextures(1, &texArray);
613 }
614
615 glBindTexture(GL_TEXTURE_2D_ARRAY, texArray);
616
617 const size_t layers = std::max<size_t>(1, texture_manager.size());
618 if (layers != texture_array_layers) {
619 glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, maximum_texture_size.x, maximum_texture_size.y, layers);
620 texture_array_layers = layers;
621 }
622
623 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
624 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
625 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
626 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
627 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
628
629 std::vector<GLfloat> uv_rescale;
630 uv_rescale.resize(texture_manager.size() * 2);
631
632 for (const auto &[textureID, texture]: texture_manager) {
633 GLenum externalFormat = 0;
634 switch (texture.num_channels) {
635 case 1:
636 externalFormat = GL_RED;
637 break;
638 case 3:
639 externalFormat = GL_RGB;
640 break;
641 case 4:
642 externalFormat = GL_RGBA;
643 break;
644 default:
645 throw std::runtime_error("unsupported channel count");
646 }
647
648 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());
649
650 uv_rescale.at(textureID * 2 + 0) = float(texture.texture_resolution.x) / float(maximum_texture_size.x);
651 uv_rescale.at(textureID * 2 + 1) = float(texture.texture_resolution.y) / float(maximum_texture_size.y);
652 }
653
654 glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
655 glUniform1i(glGetUniformLocation(primaryShader.shaderID, "textureSampler"), 0);
656
657 glBindBuffer(GL_TEXTURE_BUFFER, uv_rescale_buffer);
658 glBufferData(GL_TEXTURE_BUFFER, uv_rescale.size() * sizeof(GLfloat), uv_rescale.data(), GL_STATIC_DRAW);
659 glBindTexture(GL_TEXTURE_BUFFER, uv_rescale_texture_object);
660 glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, uv_rescale_buffer);
661 glBindBuffer(GL_TEXTURE_BUFFER, 0);
662}
663
664
665void Visualizer::render(bool shadow) const {
666 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();
667 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();
668 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();
669 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();
670
671 size_t triangle_count = geometry_handler.getTriangleCount();
672 size_t rectangle_count = geometry_handler.getRectangleCount();
673 size_t line_count = geometry_handler.getLineCount();
674 size_t point_count = geometry_handler.getPointCount();
675
676 // Look up the currently loaded shader
677 GLint current_shader_program = 0;
678 glGetIntegerv(GL_CURRENT_PROGRAM, &current_shader_program);
679
680 // Bind our texture array
681 glActiveTexture(GL_TEXTURE0);
682 glBindTexture(GL_TEXTURE_2D_ARRAY, texArray);
683
684 assert(checkerrors());
685
686 glActiveTexture(GL_TEXTURE9);
687 assert(checkerrors());
688 glBindTexture(GL_TEXTURE_BUFFER, uv_rescale_texture_object);
689 assert(checkerrors());
690 glUniform1i(glGetUniformLocation(current_shader_program, "uv_rescale"), 9);
691
692 //--- Triangles---//
693
694 assert(checkerrors());
695
696 if (triangle_count > 0) {
697 glActiveTexture(GL_TEXTURE3);
698 glBindTexture(GL_TEXTURE_BUFFER, color_texture_object.at(triangle_ind));
699 glUniform1i(glGetUniformLocation(current_shader_program, "color_texture_object"), 3);
700
701 glActiveTexture(GL_TEXTURE4);
702 glBindTexture(GL_TEXTURE_BUFFER, normal_texture_object.at(triangle_ind));
703 glUniform1i(glGetUniformLocation(current_shader_program, "normal_texture_object"), 4);
704
705 glActiveTexture(GL_TEXTURE5);
706 glBindTexture(GL_TEXTURE_BUFFER, texture_flag_texture_object.at(triangle_ind));
707 glUniform1i(glGetUniformLocation(current_shader_program, "texture_flag_texture_object"), 5);
708
709 glActiveTexture(GL_TEXTURE6);
710 glBindTexture(GL_TEXTURE_BUFFER, texture_ID_texture_object.at(triangle_ind));
711 glUniform1i(glGetUniformLocation(current_shader_program, "texture_ID_texture_object"), 6);
712
713 glActiveTexture(GL_TEXTURE7);
714 glBindTexture(GL_TEXTURE_BUFFER, coordinate_flag_texture_object.at(triangle_ind));
715 glUniform1i(glGetUniformLocation(current_shader_program, "coordinate_flag_texture_object"), 7);
716
717 glActiveTexture(GL_TEXTURE8);
718 glBindTexture(GL_TEXTURE_BUFFER, hidden_flag_texture_object.at(triangle_ind));
719 glUniform1i(glGetUniformLocation(current_shader_program, "hidden_flag_texture_object"), 8);
720
721 glBindVertexArray(primaryShader.vertex_array_IDs.at(triangle_ind));
722 assert(checkerrors());
723 glDrawArrays(GL_TRIANGLES, 0, triangle_count * 3);
724 }
725
726 assert(checkerrors());
727
728 //--- Rectangles---//
729
730 if (rectangle_count > 0) {
731 glActiveTexture(GL_TEXTURE3);
732 glBindTexture(GL_TEXTURE_BUFFER, color_texture_object.at(rectangle_ind));
733 glUniform1i(glGetUniformLocation(current_shader_program, "color_texture_object"), 3);
734
735 glActiveTexture(GL_TEXTURE4);
736 glBindTexture(GL_TEXTURE_BUFFER, normal_texture_object.at(rectangle_ind));
737 glUniform1i(glGetUniformLocation(current_shader_program, "normal_texture_object"), 4);
738
739 glActiveTexture(GL_TEXTURE5);
740 glBindTexture(GL_TEXTURE_BUFFER, texture_flag_texture_object.at(rectangle_ind));
741 glUniform1i(glGetUniformLocation(current_shader_program, "texture_flag_texture_object"), 5);
742
743 glActiveTexture(GL_TEXTURE6);
744 glBindTexture(GL_TEXTURE_BUFFER, texture_ID_texture_object.at(rectangle_ind));
745 glUniform1i(glGetUniformLocation(current_shader_program, "texture_ID_texture_object"), 6);
746
747 glActiveTexture(GL_TEXTURE7);
748 glBindTexture(GL_TEXTURE_BUFFER, coordinate_flag_texture_object.at(rectangle_ind));
749 glUniform1i(glGetUniformLocation(current_shader_program, "coordinate_flag_texture_object"), 7);
750
751 glActiveTexture(GL_TEXTURE8);
752 glBindTexture(GL_TEXTURE_BUFFER, hidden_flag_texture_object.at(rectangle_ind));
753 glUniform1i(glGetUniformLocation(current_shader_program, "hidden_flag_texture_object"), 8);
754
755 glBindVertexArray(primaryShader.vertex_array_IDs.at(rectangle_ind));
756
757 std::vector<GLint> opaque_firsts;
758 std::vector<GLint> opaque_counts;
759 struct TransparentRect {
760 size_t index;
761 float depth;
762 };
763 std::vector<TransparentRect> transparent_rects;
764
765 const auto &texFlags = *geometry_handler.getTextureFlagData_ptr(GeometryHandler::GEOMETRY_TYPE_RECTANGLE);
766 const auto &colors = *geometry_handler.getColorData_ptr(GeometryHandler::GEOMETRY_TYPE_RECTANGLE);
767 const auto &verts = *geometry_handler.getVertexData_ptr(GeometryHandler::GEOMETRY_TYPE_RECTANGLE);
768
769 opaque_firsts.reserve(rectangle_count);
770 opaque_counts.reserve(rectangle_count);
771 transparent_rects.reserve(rectangle_count);
772
773 for (size_t i = 0; i < rectangle_count; ++i) {
774 bool isGlyph = texFlags.at(i) == 3;
775 float alpha = colors.at(i * 4 + 3);
776 if (!isGlyph && alpha >= 1.f) {
777 opaque_firsts.push_back(static_cast<GLint>(i * 4));
778 opaque_counts.push_back(4);
779 } else {
780 glm::vec3 center(0.f);
781 for (int j = 0; j < 4; ++j) {
782 center.x += verts.at(i * 12 + j * 3 + 0);
783 center.y += verts.at(i * 12 + j * 3 + 1);
784 center.z += verts.at(i * 12 + j * 3 + 2);
785 }
786 center /= 4.f;
787 glm::vec4 viewPos = cameraViewMatrix * glm::vec4(center, 1.f);
788 transparent_rects.push_back({i, viewPos.z});
789 }
790 }
791
792 if (!opaque_firsts.empty()) {
793 glMultiDrawArrays(GL_TRIANGLE_FAN, opaque_firsts.data(), opaque_counts.data(), static_cast<GLsizei>(opaque_firsts.size()));
794 }
795
796 if (!transparent_rects.empty()) {
797 std::sort(transparent_rects.begin(), transparent_rects.end(), [](const TransparentRect &a, const TransparentRect &b) {
798 return a.depth > b.depth; // farthest first
799 });
800
801 glDepthMask(GL_FALSE);
802 for (const auto &tr: transparent_rects) {
803 glDrawArrays(GL_TRIANGLE_FAN, static_cast<GLint>(tr.index * 4), 4);
804 }
805 glDepthMask(GL_TRUE);
806 }
807 }
808
809 assert(checkerrors());
810
811 if (!shadow) {
812 //--- Lines ---//
813
814 if (line_count > 0) {
815 glActiveTexture(GL_TEXTURE3);
816 glBindTexture(GL_TEXTURE_BUFFER, color_texture_object.at(line_ind));
817 glUniform1i(glGetUniformLocation(current_shader_program, "color_texture_object"), 3);
818
819 glActiveTexture(GL_TEXTURE4);
820 glBindTexture(GL_TEXTURE_BUFFER, normal_texture_object.at(line_ind));
821 glUniform1i(glGetUniformLocation(current_shader_program, "normal_texture_object"), 4);
822
823 glActiveTexture(GL_TEXTURE5);
824 glBindTexture(GL_TEXTURE_BUFFER, texture_flag_texture_object.at(line_ind));
825 glUniform1i(glGetUniformLocation(current_shader_program, "texture_flag_texture_object"), 5);
826
827 glActiveTexture(GL_TEXTURE6);
828 glBindTexture(GL_TEXTURE_BUFFER, texture_ID_texture_object.at(line_ind));
829 glUniform1i(glGetUniformLocation(current_shader_program, "texture_ID_texture_object"), 6);
830
831 glActiveTexture(GL_TEXTURE7);
832 glBindTexture(GL_TEXTURE_BUFFER, coordinate_flag_texture_object.at(line_ind));
833 glUniform1i(glGetUniformLocation(current_shader_program, "coordinate_flag_texture_object"), 7);
834
835 glActiveTexture(GL_TEXTURE8);
836 glBindTexture(GL_TEXTURE_BUFFER, hidden_flag_texture_object.at(line_ind));
837 glUniform1i(glGetUniformLocation(current_shader_program, "hidden_flag_texture_object"), 8);
838
839 glBindVertexArray(primaryShader.vertex_array_IDs.at(line_ind));
840
841 // Group lines by width and render each group separately
842 const std::vector<float> *size_data = geometry_handler.getSizeData_ptr(GeometryHandler::GEOMETRY_TYPE_LINE);
843 if (size_data && !size_data->empty()) {
844 // Create map of line width -> line indices for grouped rendering
845 std::map<float, std::vector<size_t>> width_groups;
846 for (size_t i = 0; i < size_data->size(); ++i) {
847 float width = size_data->at(i);
848 if (width <= 0)
849 width = 1.0f; // Default width for invalid values
850 width_groups[width].push_back(i);
851 }
852
853 // Render each width group separately
854 for (const auto &group: width_groups) {
855 float width = group.first;
856 const std::vector<size_t> &line_indices = group.second;
857
858 glLineWidth(width);
859
860 // For simplicity, render each line individually
861 // In a more optimized implementation, we would batch lines with same width
862 for (size_t line_idx: line_indices) {
863 glDrawArrays(GL_LINES, line_idx * 2, 2);
864 }
865 }
866 } else {
867 // Fallback to default behavior if no size data available
868 glLineWidth(1);
869 glDrawArrays(GL_LINES, 0, line_count * 2);
870 }
871 }
872
873 //--- Points ---//
874
875 if (point_count > 0) {
876 glActiveTexture(GL_TEXTURE3);
877 glBindTexture(GL_TEXTURE_BUFFER, color_texture_object.at(point_ind));
878 glUniform1i(glGetUniformLocation(current_shader_program, "color_texture_object"), 3);
879
880 glActiveTexture(GL_TEXTURE4);
881 glBindTexture(GL_TEXTURE_BUFFER, normal_texture_object.at(point_ind));
882 glUniform1i(glGetUniformLocation(current_shader_program, "normal_texture_object"), 4);
883
884 glActiveTexture(GL_TEXTURE5);
885 glBindTexture(GL_TEXTURE_BUFFER, texture_flag_texture_object.at(point_ind));
886 glUniform1i(glGetUniformLocation(current_shader_program, "texture_flag_texture_object"), 5);
887
888 glActiveTexture(GL_TEXTURE6);
889 glBindTexture(GL_TEXTURE_BUFFER, texture_ID_texture_object.at(point_ind));
890 glUniform1i(glGetUniformLocation(current_shader_program, "texture_ID_texture_object"), 6);
891
892 glActiveTexture(GL_TEXTURE7);
893 glBindTexture(GL_TEXTURE_BUFFER, coordinate_flag_texture_object.at(point_ind));
894 glUniform1i(glGetUniformLocation(current_shader_program, "coordinate_flag_texture_object"), 7);
895
896 glActiveTexture(GL_TEXTURE8);
897 glBindTexture(GL_TEXTURE_BUFFER, hidden_flag_texture_object.at(point_ind));
898 glUniform1i(glGetUniformLocation(current_shader_program, "hidden_flag_texture_object"), 8);
899
900 glBindVertexArray(primaryShader.vertex_array_IDs.at(point_ind));
901
902 // Group points by size and render each group separately
903 const std::vector<float> *size_data = geometry_handler.getSizeData_ptr(GeometryHandler::GEOMETRY_TYPE_POINT);
904 if (size_data && !size_data->empty()) {
905 // Create map of point size -> point indices for grouped rendering
906 std::map<float, std::vector<size_t>> size_groups;
907 for (size_t i = 0; i < size_data->size(); ++i) {
908 float size = size_data->at(i);
909 if (size <= 0)
910 size = 1.0f; // Default size for invalid values
911 size_groups[size].push_back(i);
912 }
913
914 // Render each size group separately
915 for (const auto &group: size_groups) {
916 float size = group.first;
917 const std::vector<size_t> &point_indices = group.second;
918
919 glPointSize(size);
920
921 // For simplicity, render each point individually
922 // In a more optimized implementation, we would batch points with same size
923 for (size_t point_idx: point_indices) {
924 glDrawArrays(GL_POINTS, point_idx, 1);
925 }
926 }
927 } else {
928 // Fallback to default behavior if no size data available
929 glPointSize(point_width);
930 glDrawArrays(GL_POINTS, 0, point_count);
931 }
932 }
933
934 // if( !positionData["sky"].empty() ){
935 // primaryShader.setLightingModel( LIGHTING_NONE );
936 // glBindTexture(GL_TEXTURE_RECTANGLE,textureIDData["sky"].at(0));
937 // glDrawArrays(GL_TRIANGLES, triangle_size+line_size+point_size, positionData["sky"].size()/3 );
938 // }
939 }
940}
941
943 plotUpdate(false);
944}
945
946void Visualizer::plotUpdate(bool hide_window) {
947 // Check if window is marked for closure to prevent hanging on glfwSwapBuffers()
948 if (!headless && window != nullptr && glfwWindowShouldClose(scast<GLFWwindow *>(window))) {
949 return; // Don't render to a window that should be closed
950 }
951
952 if (message_flag) {
953 std::cout << "Updating the plot..." << std::flush;
954 }
955
956 if (!hide_window && !headless && window != nullptr) {
957 glfwShowWindow(scast<GLFWwindow *>(window));
958 }
959
960 // Update the Context geometry
961 buildContextGeometry_private();
962
963 // Apply point cloud culling for performance optimization
964 updatePointCulling();
965
966 // Set the view to fit window
967 if (camera_lookat_center.x == 0 && camera_lookat_center.y == 0 && camera_lookat_center.z == 0) { // default center
968 if (camera_eye_location.x < 1e-4 && camera_eye_location.y < 1e-4 && camera_eye_location.z == 2.f) { // default eye position
969
970 vec3 center_sph;
971 vec3 radius;
972 geometry_handler.getDomainBoundingSphere(center_sph, radius);
973 float domain_bounding_radius = radius.magnitude();
974
975 vec2 xbounds, ybounds, zbounds;
976 geometry_handler.getDomainBoundingBox(xbounds, ybounds, zbounds);
977 camera_lookat_center = make_vec3(0.5f * (xbounds.x + xbounds.y), 0.5f * (ybounds.x + ybounds.y), 0.5f * (zbounds.x + zbounds.y));
978 camera_eye_location = camera_lookat_center + sphere2cart(make_SphericalCoord(2.f * domain_bounding_radius, 20.f * PI_F / 180.f, 0));
979 }
980 }
981
982 // Update
983 if (colorbar_flag == 2) {
984 if (!colorbar_IDs.empty()) {
985 geometry_handler.deleteGeometry(colorbar_IDs);
986 colorbar_IDs.clear();
987 }
988 colorbar_IDs = addColorbarByCenter(colorbar_title.c_str(), colorbar_size, colorbar_position, colorbar_fontcolor, colormap_current);
989 }
990
991 // Watermark
993
994 transferBufferData();
995
996 bool shadow_flag = false;
997 for (const auto &model: primaryLightingModel) {
999 shadow_flag = true;
1000 break;
1001 }
1002 }
1003
1004 glm::mat4 depthMVP;
1005
1006 if (shadow_flag) {
1007 // Depth buffer for shadows
1008 glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
1009 glViewport(0, 0, shadow_buffer_size.x, shadow_buffer_size.y); // Render on the whole framebuffer, complete from the lower left corner to the upper right
1010
1011 // Clear the screen
1012 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1013
1014 depthShader.useShader();
1015
1016 updatePerspectiveTransformation(true);
1017
1018 // Compute the MVP matrix from the light's point of view
1019 depthMVP = computeShadowDepthMVP();
1020 depthShader.setTransformationMatrix(depthMVP);
1021
1022 // bind depth texture
1023 glActiveTexture(GL_TEXTURE1);
1024 glBindTexture(GL_TEXTURE_2D, depthTexture);
1025 glActiveTexture(GL_TEXTURE0);
1026
1027 depthShader.enableTextureMaps();
1028 depthShader.enableTextureMasks();
1029
1030 render(true);
1031 } else {
1032 depthMVP = glm::mat4(1.0);
1033 }
1034
1035 assert(checkerrors());
1036
1037 // Render to the screen
1038 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1039 glViewport(0, 0, Wframebuffer, Hframebuffer);
1040
1041 glClearColor(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0.0f);
1042
1043 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1044
1045 primaryShader.useShader();
1046
1047 glm::mat4 DepthBiasMVP = biasMatrix * depthMVP;
1048
1049 primaryShader.setDepthBiasMatrix(DepthBiasMVP);
1050
1051 updatePerspectiveTransformation(false);
1052
1053 primaryShader.setTransformationMatrix(perspectiveTransformationMatrix);
1054
1055 primaryShader.enableTextureMaps();
1056 primaryShader.enableTextureMasks();
1057
1058 primaryShader.setLightingModel(primaryLightingModel.at(0));
1059 primaryShader.setLightIntensity(lightintensity);
1060
1061 glActiveTexture(GL_TEXTURE1);
1062 glBindTexture(GL_TEXTURE_2D, depthTexture);
1063 glUniform1i(primaryShader.shadowmapUniform, 1);
1064 glActiveTexture(GL_TEXTURE0);
1065
1066 render(false);
1067
1068 glfwPollEvents();
1069 getViewKeystrokes(camera_eye_location, camera_lookat_center);
1070
1071 int width, height;
1072 glfwGetFramebufferSize((GLFWwindow *) window, &width, &height);
1073 Wframebuffer = width;
1074 Hframebuffer = height;
1075
1076 glfwSwapBuffers((GLFWwindow *) window);
1077
1078 if (message_flag) {
1079 std::cout << "done." << std::endl;
1080 }
1081}
1082
1083void Visualizer::updateDepthBuffer() {
1084 // Update the Context geometry (if needed)
1085 if (true) {
1086 buildContextGeometry_private();
1087 }
1088
1089 transferBufferData();
1090
1091 // Depth buffer for shadows
1092 glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
1093 glViewport(0, 0, Wframebuffer, Hframebuffer);
1094
1095 // bind depth texture
1096 glActiveTexture(GL_TEXTURE1);
1097 glBindTexture(GL_TEXTURE_2D, depthTexture);
1098 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, Wframebuffer, Hframebuffer, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
1099 glActiveTexture(GL_TEXTURE0);
1100
1101 // Clear the screen
1102 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1103
1104 depthShader.useShader();
1105
1106 updatePerspectiveTransformation(false);
1107 depthShader.setTransformationMatrix(perspectiveTransformationMatrix);
1108
1109 depthShader.enableTextureMaps();
1110 depthShader.enableTextureMasks();
1111
1112 render(true);
1113
1114 assert(checkerrors());
1115
1116 depth_buffer_data.resize(Wframebuffer * Hframebuffer);
1117
1118#if defined(__APPLE__)
1119 constexpr GLenum read_buf = GL_FRONT;
1120#else
1121 constexpr GLenum read_buf = GL_BACK;
1122#endif
1123 glReadBuffer(read_buf);
1124 glReadPixels(0, 0, Wframebuffer, Hframebuffer, GL_DEPTH_COMPONENT, GL_FLOAT, depth_buffer_data.data());
1125 glFinish();
1126
1127 assert(checkerrors());
1128
1129 // Updates this->depth_buffer_data()
1130}
1131
1133 if (message_flag) {
1134 std::cout << "Rendering depth map..." << std::flush;
1135 }
1136
1137 updateDepthBuffer();
1138
1139 // normalize data, flip in y direction, and invert the color space so white=closest, black = furthest
1140 float depth_min = (std::numeric_limits<float>::max)();
1141 float depth_max = (std::numeric_limits<float>::min)();
1142 for (auto depth: depth_buffer_data) {
1143 if (depth < depth_min) {
1144 depth_min = depth;
1145 }
1146 if (depth > depth_max) {
1147 depth_max = depth;
1148 }
1149 }
1150 std::vector<unsigned char> depth_uchar(depth_buffer_data.size() * 4);
1151 for (size_t i = 0; i < depth_buffer_data.size(); i++) {
1152 auto value = scast<unsigned char>(std::round((depth_buffer_data.at(i) - depth_min) / (depth_max - depth_min) * 255));
1153 value = clamp(value, scast<unsigned char>(0), scast<unsigned char>(255));
1154 size_t row = i / Wframebuffer;
1155 size_t col = i % Wframebuffer;
1156 size_t flipped_i = (Hframebuffer - 1 - row) * Wframebuffer + col; // flipping
1157 depth_uchar.at(flipped_i * 4) = 255 - value; // R
1158 depth_uchar.at(flipped_i * 4 + 1) = 255 - value; // G
1159 depth_uchar.at(flipped_i * 4 + 2) = 255 - value; // B
1160 depth_uchar.at(flipped_i * 4 + 3) = 255; // A
1161 }
1162
1163 displayImage(depth_uchar, Wframebuffer, Hframebuffer);
1164
1165 if (message_flag) {
1166 std::cout << "done." << std::endl;
1167 }
1168}
1169
1170void Shader::initialize(const char *vertex_shader_file, const char *fragment_shader_file, Visualizer *visualizer_ptr) {
1171 // ~~~~~~~~~~~~~~~ COMPILE SHADERS ~~~~~~~~~~~~~~~~~~~~~~~~~//
1172
1173 assert(checkerrors());
1174
1175 // Create the shaders
1176 unsigned int VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
1177 unsigned int FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
1178
1179 // Read the Vertex Shader code from the file
1180 std::string VertexShaderCode;
1181 std::ifstream VertexShaderStream(vertex_shader_file, std::ios::in);
1182 assert(VertexShaderStream.is_open());
1183 std::string Line;
1184 while (getline(VertexShaderStream, Line))
1185 VertexShaderCode += "\n" + Line;
1186 VertexShaderStream.close();
1187
1188 // Read the Fragment Shader code from the file
1189 std::string FragmentShaderCode;
1190 std::ifstream FragmentShaderStream(fragment_shader_file, std::ios::in);
1191 assert(FragmentShaderStream.is_open());
1192 Line = "";
1193 while (getline(FragmentShaderStream, Line))
1194 FragmentShaderCode += "\n" + Line;
1195 FragmentShaderStream.close();
1196
1197 // Compile Vertex Shader
1198 char const *VertexSourcePointer = VertexShaderCode.c_str();
1199 glShaderSource(VertexShaderID, 1, &VertexSourcePointer, nullptr);
1200 glCompileShader(VertexShaderID);
1201
1202 assert(checkerrors());
1203
1204 // check vertex‐shader compile status
1205 GLint compileOK = GL_FALSE;
1206 glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &compileOK);
1207 if (compileOK != GL_TRUE) {
1208 GLint logLen = 0;
1209 glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &logLen);
1210 std::vector<char> log(logLen);
1211 glGetShaderInfoLog(VertexShaderID, logLen, nullptr, log.data());
1212 fprintf(stderr, "Vertex shader compilation failed:\n%s\n", log.data());
1213 throw std::runtime_error("vertex shader compile error");
1214 }
1215
1216 // Compile Fragment Shader
1217 char const *FragmentSourcePointer = FragmentShaderCode.c_str();
1218 glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, nullptr);
1219 glCompileShader(FragmentShaderID);
1220
1221 assert(checkerrors());
1222
1223 // check fragment‐shader compile status
1224 compileOK = GL_FALSE;
1225 glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &compileOK);
1226 if (compileOK != GL_TRUE) {
1227 GLint logLen = 0;
1228 glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &logLen);
1229 std::vector<char> log(logLen);
1230 glGetShaderInfoLog(FragmentShaderID, logLen, nullptr, log.data());
1231 fprintf(stderr, "Fragment shader compilation failed:\n%s\n", log.data());
1232 throw std::runtime_error("fragment shader compile error");
1233 }
1234
1235 // Link the program
1236 shaderID = glCreateProgram();
1237 glAttachShader(shaderID, VertexShaderID);
1238 glAttachShader(shaderID, FragmentShaderID);
1239 glLinkProgram(shaderID);
1240
1241 assert(checkerrors());
1242
1243 GLint linkOK = GL_FALSE;
1244 glGetProgramiv(shaderID, GL_LINK_STATUS, &linkOK);
1245 if (linkOK != GL_TRUE) {
1246 GLint logLen = 0;
1247 glGetProgramiv(shaderID, GL_INFO_LOG_LENGTH, &logLen);
1248 std::vector<char> log(logLen);
1249 glGetProgramInfoLog(shaderID, logLen, nullptr, log.data());
1250 fprintf(stderr, "Shader program link failed:\n%s\n", log.data());
1251 throw std::runtime_error("program link error");
1252 }
1253
1254 assert(checkerrors());
1255
1256 glDeleteShader(VertexShaderID);
1257 glDeleteShader(FragmentShaderID);
1258
1259 assert(checkerrors());
1260
1261 // ~~~~~~~~~~~ Create a Vertex Array Object (VAO) ~~~~~~~~~~//
1262 vertex_array_IDs.resize(GeometryHandler::all_geometry_types.size());
1263 glGenVertexArrays(GeometryHandler::all_geometry_types.size(), vertex_array_IDs.data());
1264
1265 assert(checkerrors());
1266
1267 // set up vertex buffers
1268
1269 int i = 0;
1270 for (const auto &geometry_type: GeometryHandler::all_geometry_types) {
1271 glBindVertexArray(vertex_array_IDs.at(i));
1272
1273 // 1st attribute buffer : vertex positions
1274 glBindBuffer(GL_ARRAY_BUFFER, visualizer_ptr->vertex_buffer.at(i));
1275 glEnableVertexAttribArray(0); // vertices
1276 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
1277
1278 // 2nd attribute buffer : vertex uv
1279 glBindBuffer(GL_ARRAY_BUFFER, visualizer_ptr->uv_buffer.at(i));
1280 glEnableVertexAttribArray(1); // uv
1281 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
1282
1283 // 3rd attribute buffer : face index
1284 glBindBuffer(GL_ARRAY_BUFFER, visualizer_ptr->face_index_buffer.at(i));
1285 glEnableVertexAttribArray(2); // face index
1286 glVertexAttribIPointer(2, 1, GL_INT, 0, nullptr);
1287
1288 i++;
1289 }
1290
1291 glBindBuffer(GL_ARRAY_BUFFER, 0);
1292
1293 assert(checkerrors());
1294
1295 glUseProgram(shaderID);
1296
1297 assert(checkerrors());
1298
1299 // ~~~~~~~~~~~ Primary Shader Uniforms ~~~~~~~~~~//
1300
1301 // Transformation Matrix
1302 transformMatrixUniform = glGetUniformLocation(shaderID, "MVP");
1303
1304 // Depth Bias Matrix (for shadows)
1305 depthBiasUniform = glGetUniformLocation(shaderID, "DepthBiasMVP");
1306
1307 // Texture Sampler
1308 textureUniform = glGetUniformLocation(shaderID, "textureSampler");
1309
1310 // Shadow Map Sampler
1311 shadowmapUniform = glGetUniformLocation(shaderID, "shadowMap");
1312 glUniform1i(shadowmapUniform, 1);
1313
1314 // Unit vector in the direction of the light (sun)
1315 lightDirectionUniform = glGetUniformLocation(shaderID, "lightDirection");
1316 glUniform3f(lightDirectionUniform, 0, 0, 1); // Default is directly above
1317
1318 // Lighting model used for shading primitives
1319 lightingModelUniform = glGetUniformLocation(shaderID, "lightingModel");
1320 glUniform1i(lightingModelUniform, 0); // Default is none
1321
1322 RboundUniform = glGetUniformLocation(shaderID, "Rbound");
1323 glUniform1i(RboundUniform, 0);
1324
1325 // Lighting intensity factor
1326 lightIntensityUniform = glGetUniformLocation(shaderID, "lightIntensity");
1327 glUniform1f(lightIntensityUniform, 1.f);
1328
1329 // Texture (u,v) rescaling factor
1330 uvRescaleUniform = glGetUniformLocation(shaderID, "uv_rescale");
1331
1332 assert(checkerrors());
1333
1334 initialized = true;
1335}
1336
1337Shader::~Shader() {
1338 if (!initialized) {
1339 return;
1340 }
1341 glDeleteVertexArrays(vertex_array_IDs.size(), vertex_array_IDs.data());
1342 glDeleteProgram(shaderID);
1343}
1344
1346 glEnable(GL_BLEND);
1347 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1348}
1349
1351 glActiveTexture(GL_TEXTURE0);
1352 glUniform1i(textureUniform, 0);
1353 // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1354 // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1355 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1356 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1357 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1358 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1359 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1360 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1361 glEnable(GL_BLEND);
1362 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1363}
1364
1366 glActiveTexture(GL_TEXTURE0);
1367 glUniform1i(textureUniform, 0);
1368 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1369 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1370 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1371 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1372 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1373 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1374 glEnable(GL_BLEND);
1375 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1376}
1377
1378void Shader::setTransformationMatrix(const glm::mat4 &matrix) const {
1379 glUniformMatrix4fv(transformMatrixUniform, 1, GL_FALSE, &matrix[0][0]);
1380}
1381
1382void Shader::setDepthBiasMatrix(const glm::mat4 &matrix) const {
1383 glUniformMatrix4fv(depthBiasUniform, 1, GL_FALSE, &matrix[0][0]);
1384}
1385
1386void Shader::setLightDirection(const helios::vec3 &direction) const {
1387 glUniform3f(lightDirectionUniform, direction.x, direction.y, direction.z);
1388}
1389
1390void Shader::setLightingModel(uint lightingmodel) const {
1391 glUniform1i(lightingModelUniform, lightingmodel);
1392}
1393
1394void Shader::setLightIntensity(float lightintensity) const {
1395 glUniform1f(lightIntensityUniform, lightintensity);
1396}
1397
1398void Shader::useShader() const {
1399 glUseProgram(shaderID);
1400}
1401
1402void Visualizer::framebufferResizeCallback(GLFWwindow *window, int width, int height) {
1403 if (width <= 0 || height <= 0) {
1404 return;
1405 }
1406 auto *viz = static_cast<Visualizer *>(glfwGetWindowUserPointer(window));
1407 if (viz != nullptr) {
1408 viz->Wframebuffer = static_cast<uint>(width);
1409 viz->Hframebuffer = static_cast<uint>(height);
1410 }
1411}
1412
1413void Visualizer::windowResizeCallback(GLFWwindow *window, int width, int height) {
1414 if (width <= 0 || height <= 0) {
1415 return;
1416 }
1417 auto *viz = static_cast<Visualizer *>(glfwGetWindowUserPointer(window));
1418 if (viz != nullptr) {
1419 int fbw, fbh;
1420 glfwGetFramebufferSize(window, &fbw, &fbh);
1421 if (fbw != width || fbh != height) {
1422 glfwSetWindowSize(window, width, height);
1423 fbw = width;
1424 fbh = height;
1425 }
1426 viz->Wdisplay = static_cast<uint>(width);
1427 viz->Hdisplay = static_cast<uint>(height);
1428 viz->Wframebuffer = static_cast<uint>(fbw);
1429 viz->Hframebuffer = static_cast<uint>(fbh);
1430 viz->updateWatermark();
1431 viz->transferBufferData();
1432 }
1433}
1434
1436 if (!isWatermarkVisible) {
1437 if (watermark_ID != 0) {
1438 geometry_handler.deleteGeometry(watermark_ID);
1439 watermark_ID = 0;
1440 }
1441 return;
1442 }
1443
1444 constexpr float texture_aspect = 675.f / 195.f; // image width / height
1445
1446 float window_aspect = float(Wframebuffer) / float(Hframebuffer);
1447 float width = 0.07f * texture_aspect / window_aspect;
1448 if (watermark_ID != 0) {
1449 geometry_handler.deleteGeometry(watermark_ID);
1450 }
1451 watermark_ID = addRectangleByCenter(make_vec3(0.75f * width, 0.95f, 0), make_vec2(width, 0.07), make_SphericalCoord(0, 0), "plugins/visualizer/textures/Helios_watermark.png", COORDINATES_WINDOW_NORMALIZED);
1452}
1453
1454
1455bool lbutton_down = false;
1456bool rbutton_down = false;
1457bool mbutton_down = false;
1458double startX, startY;
1459double scrollX, scrollY;
1460bool scroll = false;
1461
1462
1463void mouseCallback(GLFWwindow *window, int button, int action, int mods) {
1464 if (action == GLFW_PRESS) {
1465 glfwGetCursorPos(window, &startX, &startY);
1466 }
1467 if (button == GLFW_MOUSE_BUTTON_LEFT) {
1468 if (GLFW_PRESS == action) {
1469 lbutton_down = true;
1470 } else if (GLFW_RELEASE == action) {
1471 lbutton_down = false;
1472 }
1473 } else if (button == GLFW_MOUSE_BUTTON_MIDDLE) {
1474 if (GLFW_PRESS == action) {
1475 mbutton_down = true;
1476 } else if (GLFW_RELEASE == action) {
1477 mbutton_down = false;
1478 }
1479 } else if (button == GLFW_MOUSE_BUTTON_RIGHT) {
1480 if (GLFW_PRESS == action) {
1481 rbutton_down = true;
1482 } else if (GLFW_RELEASE == action) {
1483 rbutton_down = false;
1484 }
1485 }
1486}
1487
1488
1489void cursorCallback(GLFWwindow *window, double xpos, double ypos) {
1490 if (rbutton_down) {
1491 dx = xpos - startX;
1492 dy = ypos - startY;
1493 } else if (lbutton_down || mbutton_down) {
1494 dphi = scast<float>(xpos - startX);
1495 dtheta = scast<float>(ypos - startY);
1496 } else {
1497 dphi = dtheta = 0.f;
1498 }
1499 startX = xpos;
1500 startY = ypos;
1501}
1502
1503
1504void scrollCallback(GLFWwindow *window, double xoffset, double yoffset) {
1505 dscroll = scast<float>(yoffset);
1506 scrollY = yoffset;
1507 if (yoffset > 0.0 || yoffset < 0.0) {
1508 scroll = true;
1509 } else {
1510 scroll = false;
1511 }
1512}
1513
1514
1515void Visualizer::getViewKeystrokes(vec3 &eye, vec3 &center) {
1516 vec3 forward = center - eye;
1517 forward = forward.normalize();
1518
1519 vec3 right = cross(forward, vec3(0, 0, 1));
1520 right = right.normalize();
1521
1522 vec3 up = cross(right, forward);
1523 up = up.normalize();
1524
1525 SphericalCoord Spherical = cart2sphere(eye - center);
1526 float radius = Spherical.radius;
1527 float theta = Spherical.elevation;
1528 float phi = Spherical.azimuth;
1529
1530 phi += PI_F * (dphi / 160.f);
1531 if (dtheta > 0 && theta + PI_F / 80.f < 0.49f * PI_F) {
1532 theta += PI_F * (dtheta / 120.f);
1533 } else if (dtheta < 0 && theta > -0.25 * PI_F) {
1534 theta += PI_F * (dtheta / 120.f);
1535 }
1536 dtheta = dphi = 0;
1537 if (dx != 0.f) {
1538 center -= 0.025f * dx * right;
1539 }
1540 if (dy != 0.f) {
1541 center += 0.025f * dy * up;
1542 }
1543 dx = dy = 0.f;
1544 if (scroll) {
1545 if (dscroll > 0.0f) {
1546 radius = (radius * 0.9f > minimum_view_radius) ? radius * 0.9f : minimum_view_radius;
1547 } else {
1548 radius *= 1.1f;
1549 }
1550 }
1551 scroll = false;
1552
1553 auto *_window = scast<GLFWwindow *>(window);
1554
1555 //----- Holding SPACEBAR -----//
1556 if (glfwGetKey(_window, GLFW_KEY_SPACE) == GLFW_PRESS) {
1557 // Move center to the left - SPACE + LEFT KEY
1558 if (glfwGetKey(_window, GLFW_KEY_LEFT) == GLFW_PRESS) {
1559 center.x += 0.1f * sin(phi);
1560 center.y += 0.1f * cos(phi);
1561 }
1562 // Move center to the right - SPACE + RIGHT KEY
1563 else if (glfwGetKey(_window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
1564 center.x -= 0.1f * sin(phi);
1565 center.y -= 0.1f * cos(phi);
1566 }
1567 // Move center upward - SPACE + UP KEY
1568 else if (glfwGetKey(_window, GLFW_KEY_UP) == GLFW_PRESS) {
1569 center.z += 0.2f;
1570 }
1571 // Move center downward - SPACE + DOWN KEY
1572 else if (glfwGetKey(_window, GLFW_KEY_DOWN) == GLFW_PRESS) {
1573 center.z -= 0.2f;
1574 }
1575
1576 //----- Not Holding SPACEBAR -----//
1577 } else {
1578 // Orbit left - LEFT ARROW KEY
1579 if (glfwGetKey(_window, GLFW_KEY_LEFT) == GLFW_PRESS) {
1580 phi += PI_F / 40.f;
1581 }
1582 // Orbit right - RIGHT ARROW KEY
1583 else if (glfwGetKey(_window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
1584 phi -= PI_F / 40.f;
1585 }
1586
1587 // Increase Elevation - UP ARROW KEY
1588 else if (glfwGetKey(_window, GLFW_KEY_UP) == GLFW_PRESS) {
1589 if (theta + PI_F / 80.f < 0.49f * PI_F) {
1590 theta += PI_F / 80.f;
1591 }
1592 }
1593 // Decrease Elevation - DOWN ARROW KEY
1594 else if (glfwGetKey(_window, GLFW_KEY_DOWN) == GLFW_PRESS) {
1595 if (theta > -0.25 * PI_F) {
1596 theta -= PI_F / 80.f;
1597 }
1598 }
1599
1600 // Zoom in - "+" KEY
1601 if (glfwGetKey(_window, GLFW_KEY_EQUAL) == GLFW_PRESS) {
1602 radius = (radius * 0.9f > minimum_view_radius) ? radius * 0.9f : minimum_view_radius;
1603 }
1604 // Zoom out - "-" KEY
1605 else if (glfwGetKey(_window, GLFW_KEY_MINUS) == GLFW_PRESS) {
1606 radius *= 1.1;
1607 }
1608 }
1609
1610 if (glfwGetKey(_window, GLFW_KEY_P) == GLFW_PRESS) {
1611 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 << ","
1612 << center.y << "," << center.z << ")" << std::endl;
1613 }
1614
1615 camera_eye_location = sphere2cart(make_SphericalCoord(radius, theta, phi)) + center;
1616}
1617
1618void Visualizer::cullPointsByFrustum() {
1619 const std::vector<float> *vertex_data = geometry_handler.getVertexData_ptr(GeometryHandler::GEOMETRY_TYPE_POINT);
1620 if (!vertex_data || vertex_data->empty()) {
1621 return;
1622 }
1623
1624 std::vector<glm::vec4> frustum_planes = extractFrustumPlanes();
1625
1626 // Check each point against all 6 frustum planes
1627 size_t point_count = vertex_data->size() / 3;
1628 for (size_t i = 0; i < point_count; ++i) {
1629 glm::vec3 point(vertex_data->at(i * 3), vertex_data->at(i * 3 + 1), vertex_data->at(i * 3 + 2));
1630
1631 bool inside_frustum = true;
1632 for (const auto &plane: frustum_planes) {
1633 // Plane equation: ax + by + cz + d = 0
1634 // Point is outside if dot(plane.xyz, point) + plane.w < 0
1635 if (glm::dot(glm::vec3(plane), point) + plane.w < 0) {
1636 inside_frustum = false;
1637 break;
1638 }
1639 }
1640
1641 // Find the UUID for this point index and update visibility
1642 std::vector<size_t> all_UUIDs = geometry_handler.getAllGeometryIDs();
1643 size_t point_index = 0;
1644 for (size_t UUID: all_UUIDs) {
1645 if (geometry_handler.getIndexMap(UUID).geometry_type == GeometryHandler::GEOMETRY_TYPE_POINT) {
1646 if (point_index == i) {
1647 geometry_handler.setVisibility(UUID, inside_frustum);
1648 break;
1649 }
1650 point_index++;
1651 }
1652 }
1653 }
1654}
1655
1656void Visualizer::cullPointsByDistance(float maxDistance, float lodFactor) {
1657 const std::vector<float> *vertex_data = geometry_handler.getVertexData_ptr(GeometryHandler::GEOMETRY_TYPE_POINT);
1658 if (!vertex_data || vertex_data->empty()) {
1659 return;
1660 }
1661
1662 glm::vec3 camera_pos(camera_eye_location.x, camera_eye_location.y, camera_eye_location.z);
1663
1664 // Apply distance-based culling with level-of-detail and adaptive sizing
1665 size_t point_count = vertex_data->size() / 3;
1666 for (size_t i = 0; i < point_count; ++i) {
1667 glm::vec3 point(vertex_data->at(i * 3), vertex_data->at(i * 3 + 1), vertex_data->at(i * 3 + 2));
1668
1669 float distance = glm::length(point - camera_pos);
1670 bool should_render = true;
1671 float adaptive_size = 1.0f; // Default point size
1672
1673 // Cull points beyond max distance
1674 if (distance > maxDistance) {
1675 should_render = false;
1676 }
1677 // Apply level-of-detail culling and adaptive sizing
1678 else if (distance > maxDistance * 0.3f) { // Start LOD at 30% of max distance
1679 float distance_ratio = distance / maxDistance;
1680 float lod_threshold = distance_ratio * lodFactor;
1681
1682 // Cull every Nth point based on distance
1683 if ((i % static_cast<size_t>(std::max(1.0f, lod_threshold))) != 0) {
1684 should_render = false;
1685 } else {
1686 // For distant points that we keep, increase their size to maintain visual coverage
1687 adaptive_size = 1.0f + (distance_ratio * 3.0f); // Scale up to 4x size for far points
1688 }
1689 }
1690
1691 // Find the UUID for this point index and update visibility and size
1692 std::vector<size_t> all_UUIDs = geometry_handler.getAllGeometryIDs();
1693 size_t point_index = 0;
1694 for (size_t UUID: all_UUIDs) {
1695 if (geometry_handler.getIndexMap(UUID).geometry_type == GeometryHandler::GEOMETRY_TYPE_POINT) {
1696 if (point_index == i) {
1697 geometry_handler.setVisibility(UUID, should_render);
1698 if (should_render) {
1699 // Apply adaptive sizing to maintain visual quality
1700 float original_size = geometry_handler.getSize(UUID);
1701 if (original_size <= 0)
1702 original_size = 1.0f;
1703 geometry_handler.setSize(UUID, original_size * adaptive_size);
1704 }
1705 break;
1706 }
1707 point_index++;
1708 }
1709 }
1710 }
1711}
1712
1713void Visualizer::updatePointCulling() {
1714 // Update total point count
1715 points_total_count = geometry_handler.getPointCount();
1716
1717 // Only perform culling if enabled and we have enough points
1718 if (!point_culling_enabled || points_total_count < point_culling_threshold) {
1719 points_rendered_count = points_total_count;
1720 last_culling_time_ms = 0;
1721 return;
1722 }
1723
1724 // Measure culling performance
1725 auto start_time = std::chrono::high_resolution_clock::now();
1726
1727 // Apply frustum culling first
1728 cullPointsByFrustum();
1729
1730 // Calculate max distance if not set
1731 float max_distance = point_max_render_distance;
1732 if (max_distance <= 0) {
1733 helios::vec2 xbounds, ybounds, zbounds;
1734 geometry_handler.getDomainBoundingBox(xbounds, ybounds, zbounds);
1735 float scene_size = std::max({xbounds.y - xbounds.x, ybounds.y - ybounds.x, zbounds.y - zbounds.x});
1736 max_distance = scene_size * 5.0f; // Render points up to 5x scene size
1737 }
1738
1739 // Apply distance-based culling with configurable parameters
1740 cullPointsByDistance(max_distance, point_lod_factor);
1741
1742 // Update metrics
1743 points_rendered_count = geometry_handler.getPointCount(false); // Count only visible points
1744
1745 auto end_time = std::chrono::high_resolution_clock::now();
1746 last_culling_time_ms = std::chrono::duration<float, std::milli>(end_time - start_time).count();
1747}
1748
1749std::vector<glm::vec4> Visualizer::extractFrustumPlanes() const {
1750 std::vector<glm::vec4> planes(6);
1751
1752 // Extract frustum planes from the MVP matrix
1753 glm::mat4 mvp = cameraProjectionMatrix * cameraViewMatrix;
1754
1755 // Left plane
1756 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]);
1757 // Right plane
1758 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]);
1759 // Bottom plane
1760 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]);
1761 // Top plane
1762 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]);
1763 // Near plane
1764 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]);
1765 // Far plane
1766 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]);
1767
1768 // Normalize the planes
1769 for (auto &plane: planes) {
1770 float length = glm::length(glm::vec3(plane));
1771 if (length > 0) {
1772 plane /= length;
1773 }
1774 }
1775
1776 return planes;
1777}
1778
1780 point_culling_enabled = enabled;
1781}
1782
1784 point_culling_threshold = threshold;
1785}
1786
1788 point_max_render_distance = distance;
1789}
1790
1792 point_lod_factor = factor;
1793}
1794
1795void Visualizer::getPointRenderingMetrics(size_t &total_points, size_t &rendered_points, float &culling_time_ms) const {
1796 total_points = points_total_count;
1797 rendered_points = points_rendered_count;
1798 culling_time_ms = last_culling_time_ms;
1799}
1800
1801std::string errorString(GLenum err) {
1802 std::string message;
1803 message.assign("");
1804
1805 if (err == GL_INVALID_ENUM) {
1806 message.assign("GL_INVALID_ENUM - An unacceptable value is specified for an enumerated argument.");
1807 } else if (err == GL_INVALID_VALUE) {
1808 message.assign("GL_INVALID_VALUE - A numeric argument is out of range.");
1809 } else if (err == GL_INVALID_OPERATION) {
1810 message.assign("GL_INVALID_OPERATION - The specified operation is not allowed in the current state.");
1811 } else if (err == GL_STACK_OVERFLOW) {
1812 message.assign("GL_STACK_OVERFLOW - This command would cause a stack overflow.");
1813 } else if (err == GL_STACK_UNDERFLOW) {
1814 message.assign("GL_STACK_UNDERFLOW - This command would cause a stack underflow.");
1815 } else if (err == GL_OUT_OF_MEMORY) {
1816 message.assign("GL_OUT_OF_MEMORY - There is not enough memory left to execute the command.");
1817 } else if (err == GL_TABLE_TOO_LARGE) {
1818 message.assign("GL_TABLE_TOO_LARGE - The specified table exceeds the implementation's maximum supported table size.");
1819 }
1820
1821 return message;
1822}
1823
1824int checkerrors() {
1825 GLenum err;
1826 int err_count = 0;
1827 while ((err = glGetError()) != GL_NO_ERROR) {
1828 std::cerr << "glError #" << err_count << ": " << errorString(err) << std::endl;
1829 err_count++;
1830 }
1831 if (err_count > 0) {
1832 return 0;
1833 } else {
1834 return 1;
1835 }
1836}
1837
1838// Safe error checking that throws exceptions instead of using assert
1839void check_opengl_errors_safe(const std::string &context) {
1840 if (!checkerrors()) {
1841 helios_runtime_error("ERROR (Visualizer): OpenGL errors detected in " + context + ". Check console output for specific error details.");
1842 }
1843}