File layer_renderer.hpp
File List > gui > layer_renderer.hpp
Go to the documentation of this file
#pragma once
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject>
#include <QPoint>
#include <QVector3D>
#include <cstdint>
#include <optional>
#include <unordered_map>
#include <vector>
#include "gui/camera.hpp"
#include "gui/layer.hpp"
#include "gui/point_cloud_gl.hpp"
#include "gui/point_octree.hpp"
struct PointPickResult {
OctreePoint point;
double world_x = 0.0;
double world_y = 0.0;
double world_z = 0.0;
std::string layer_name;
bool operator==(const PointPickResult& other) const {
return layer_name == other.layer_name && world_x == other.world_x && world_y == other.world_y &&
world_z == other.world_z;
}
bool operator!=(const PointPickResult& other) const { return !(*this == other); }
};
struct RenderContext {
// When false, the FBO is cleared and stream indices reset for a fresh preview frame.
// When true, newly drawn points accumulate on the existing FBO (Inspired by Displaz).
bool incremental_points = false;
// OpenGL viewport height in framebuffer pixels (must match glViewport for point sizing).
float viewport_height = 0.0f;
// Global light direction in world space for mesh/contour shading.
QVector3D light_direction_world{0.0f, 0.0f, 1.0f};
// Global light direction in eye space for point-sphere shading.
QVector3D light_direction_eye{0.0f, 0.0f, 1.0f};
// Ambient floor and direct-light contribution for mesh/contour layers.
float ambient_light = 0.30f;
float diffuse_light = 1.00f;
// Separate ambient floor for point rendering.
float point_ambient_light = 0.55f;
};
class LayerRenderer : public QObject {
Q_OBJECT
protected:
bool m_data_update_required = true;
bool m_visible = true;
signals:
void repaint_required() const;
// Emitted when the visible octree set or view changes enough to invalidate
// incremental point/pick FBO accumulation.
void stream_view_reset() const;
public:
virtual void render(const Camera& camera, const RenderContext& ctx) = 0;
virtual ~LayerRenderer() = default;
void data_update_required() {
m_data_update_required = true;
emit repaint_required();
}
void set_visible(bool visible) { m_visible = visible; }
static std::unique_ptr<LayerRenderer> create(std::shared_ptr<Layer> layer,
const Coordinate3D<double>& offset);
};
class OctreeLASLayerRenderer : public LayerRenderer {
public:
~OctreeLASLayerRenderer() override;
std::weak_ptr<LASLayer> m_layer;
PointCloudGL m_point_gl;
std::vector<OctreePoint> m_draw_batch;
std::unique_ptr<QOpenGLShaderProgram> m_shader;
int m_view_matrix_loc = 0;
int m_proj_matrix_loc = 0;
int m_point_radius_loc = 0;
int m_viewport_height_loc = 0;
int m_fov_rad_loc = 0;
int m_color_mode_loc = 0;
int m_fixed_color_loc = 0;
int m_point_alpha_loc = 0;
int m_point_offset_loc = 0;
int m_shader_layer_slot_loc = -1;
int m_light_direction_eye_loc = -1;
int m_ambient_light_loc = -1;
int m_diffuse_light_loc = -1;
double m_lod_quality = 1.0;
double m_inc_lod_quality = 1.0;
double m_ms_per_vertex = 30.0 / 800'000.0;
bool m_stream_backlog = false;
bool m_prev_incremental_stream = false;
double m_last_point_draw_ms = 0.0;
double m_last_point_gpu_ms = 0.0;
size_t m_last_point_vertices_drawn = 0;
int m_layer_slot = 0; // assigned once in add_layer(), 1-based, 0 = unset
GLuint m_gpu_timer_query = 0;
size_t m_lod_query_vertices = 0;
double m_lod_query_cpu_ms = 0.0;
struct NodeStreamState {
size_t point_count = 0;
size_t streamed_count = 0;
size_t locked_chunk_size = 0;
};
std::unordered_map<const PointOctreeNode*, NodeStreamState> m_node_stream;
bool m_points_uploaded = false;
std::vector<GLint> m_firsts;
std::vector<GLsizei> m_counts;
QVector3D m_stream_camera_pos;
QVector3D m_stream_camera_dir;
size_t m_visible_fingerprint = 0;
void reset_stream_cache();
bool stream_camera_changed(const Camera& camera) const;
bool visible_set_changed(const std::vector<PointOctree::VisibleNode>& visible_nodes) const;
size_t visible_nodes_fingerprint(
const std::vector<PointOctree::VisibleNode>& visible_nodes) const;
static void sort_visible_by_lod(std::vector<PointOctree::VisibleNode>& visible_nodes);
void collect_visible_octree_nodes(const LasRenderSnapshot& snap, const Camera& camera,
double vis_quality, const Coordinate3D<double>& file_origin,
std::vector<PointOctree::VisibleNode>& visible_nodes) const;
void ensure_shader();
size_t estimate_draw_vertices(const std::vector<PointOctree::VisibleNode>& visible_nodes,
double quality, bool incremental) const;
double select_draw_quality(const std::vector<PointOctree::VisibleNode>& visible_nodes,
bool incremental, bool lod_base_from_incremental,
double target_draw_ms) const;
void record_lod_sample(size_t vertices, double ms);
void ensure_gpu_timer(class QOpenGLExtraFunctions* gl);
void consume_gpu_timer_sample(class QOpenGLExtraFunctions* gl);
size_t draw_octree_nodes(QOpenGLFunctions* f, const OctreePointVector& point_storage,
const std::vector<PointOctree::VisibleNode>& visible_nodes,
const Coordinate3D<double>& file_origin,
const Coordinate3D<double>& scene_offset, double quality,
bool incremental);
size_t draw_preview_points(QOpenGLFunctions* f, const OctreePointVector& preview,
const Coordinate3D<double>& file_origin,
const Coordinate3D<double>& scene_offset);
public:
OctreeLASLayerRenderer(std::shared_ptr<LASLayer> layer, const Coordinate3D<double>& offset);
bool has_stream_backlog() const { return m_stream_backlog; }
double last_point_draw_ms() const { return m_last_point_draw_ms; }
double last_point_gpu_ms() const { return m_last_point_gpu_ms; }
size_t last_point_vertices_drawn() const { return m_last_point_vertices_drawn; }
void refresh_after_style_change();
virtual void render(const Camera& camera, const RenderContext& ctx) override;
void set_layer_slot(int slot) { m_layer_slot = slot; }
int layer_slot() const { return m_layer_slot; }
bool can_fbo_pick() const;
std::optional<PointPickResult> point_from_index(uint32_t layer_slot, uint32_t pick_index,
const Coordinate3D<double>& scene_offset) const;
};
class MeshLayerRenderer : public LayerRenderer {
std::weak_ptr<Layer> m_layer;
std::function<const AsyncRasterData*()> m_data_accessor;
bool m_gpu_texture = false;
QOpenGLVertexArrayObject m_vao;
QOpenGLBuffer m_vbo;
QOpenGLBuffer m_ibo{QOpenGLBuffer::IndexBuffer};
std::unique_ptr<QOpenGLShaderProgram> m_shader;
std::unique_ptr<QOpenGLTexture> m_texture;
bool m_mesh_uploaded = false;
bool m_texture_uploaded = false;
size_t m_index_count = 0;
int m_proj_matrix_loc = 0;
int m_light_direction_loc = -1;
int m_camera_position_loc = -1;
int m_ambient_light_loc = -1;
int m_diffuse_light_loc = -1;
int m_texture_sampler_loc = 0;
int m_layer_alpha_loc = -1;
int m_vertical_offset_loc = -1;
void upload_mesh(const DemMeshData& mesh, const Coordinate3D<double>& offset);
void upload_texture(const Geo<MultiBand<FlexGrid>>& texture);
public:
MeshLayerRenderer(std::shared_ptr<Layer> layer,
std::function<const AsyncRasterData*()> data_accessor,
const Coordinate3D<double>& offset, bool gpu_texture = false);
virtual void render(const Camera& camera, const RenderContext& ctx) override;
};
class ContourLayerRenderer : public LayerRenderer {
std::weak_ptr<ContourLayer> m_layer;
QOpenGLVertexArrayObject m_vao;
QOpenGLBuffer m_vbo;
QOpenGLBuffer m_ibo{QOpenGLBuffer::IndexBuffer};
std::unique_ptr<QOpenGLShaderProgram> m_shader;
bool m_uploaded = false;
size_t m_index_count = 0;
int m_proj_matrix_loc = 0;
int m_light_direction_loc = -1;
int m_camera_position_loc = -1;
int m_ambient_light_loc = -1;
int m_diffuse_light_loc = -1;
int m_layer_alpha_loc = -1;
int m_vertical_offset_loc = -1;
void upload_contours(const std::vector<Contour>& contours, const Coordinate3D<double>& offset);
public:
ContourLayerRenderer(std::shared_ptr<ContourLayer> layer, const Coordinate3D<double>& offset);
virtual void render(const Camera& camera, const RenderContext& ctx) override;
};