2026-04-11 16:30:10 +10:00
|
|
|
/********************************************************************************
|
|
|
|
|
* *
|
|
|
|
|
* This file is part of IfcOpenShell. *
|
|
|
|
|
* *
|
|
|
|
|
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
|
|
|
|
* it under the terms of the Lesser GNU General Public License as published by *
|
|
|
|
|
* the Free Software Foundation, either version 3.0 of the License, or *
|
|
|
|
|
* (at your option) any later version. *
|
|
|
|
|
* *
|
|
|
|
|
* IfcOpenShell is distributed in the hope that it will be useful, *
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
|
|
|
* Lesser GNU General Public License for more details. *
|
|
|
|
|
* *
|
|
|
|
|
* You should have received a copy of the Lesser GNU General Public License *
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
|
|
|
|
* *
|
|
|
|
|
********************************************************************************/
|
|
|
|
|
|
|
|
|
|
#ifndef VIEWPORTWINDOW_H
|
|
|
|
|
#define VIEWPORTWINDOW_H
|
|
|
|
|
|
|
|
|
|
#include <QWindow>
|
|
|
|
|
#include <QOpenGLContext>
|
|
|
|
|
#include <QtOpenGL/QOpenGLFunctions_4_5_Core>
|
|
|
|
|
#include <QTimer>
|
|
|
|
|
#include <QElapsedTimer>
|
|
|
|
|
#include <QMatrix4x4>
|
|
|
|
|
#include <QVector3D>
|
|
|
|
|
|
|
|
|
|
#include <vector>
|
2026-04-12 09:09:32 +10:00
|
|
|
#include <unordered_map>
|
2026-04-11 16:30:10 +10:00
|
|
|
#include <cstdint>
|
|
|
|
|
#include <mutex>
|
2026-04-12 09:09:32 +10:00
|
|
|
#include <memory>
|
|
|
|
|
|
2026-04-12 20:23:50 +10:00
|
|
|
#include "BvhAccel.h"
|
2026-04-12 19:53:06 +10:00
|
|
|
#include "InstancedGeometry.h"
|
2026-04-12 09:09:32 +10:00
|
|
|
#include "SidecarCache.h"
|
2026-04-11 16:30:10 +10:00
|
|
|
|
2026-04-12 19:53:06 +10:00
|
|
|
// Per-model GPU state for the instanced render path.
|
|
|
|
|
//
|
|
|
|
|
// VBO: local-coord interleaved verts (pos3 + normal3 + color1_packed) — 28 B.
|
|
|
|
|
// EBO: mesh-local indices (uint32).
|
|
|
|
|
// meshes[]: per-unique-representation metadata; indexed by local_mesh_id.
|
|
|
|
|
// instances[]: CPU-side per-instance records; sorted by mesh_id at finalize.
|
|
|
|
|
// ssbo: InstanceGpu[]; populated at finalize.
|
|
|
|
|
//
|
|
|
|
|
// A model is drawable once `finalized == true`.
|
2026-04-12 09:09:32 +10:00
|
|
|
struct ModelGpuData {
|
|
|
|
|
GLuint vao = 0;
|
|
|
|
|
GLuint vbo = 0;
|
|
|
|
|
GLuint ebo = 0;
|
2026-04-12 19:53:06 +10:00
|
|
|
GLuint ssbo = 0;
|
|
|
|
|
|
2026-04-12 09:09:32 +10:00
|
|
|
size_t vbo_capacity = 0;
|
|
|
|
|
size_t ebo_capacity = 0;
|
2026-04-12 19:53:06 +10:00
|
|
|
size_t vbo_used = 0;
|
|
|
|
|
size_t ebo_used = 0;
|
|
|
|
|
uint32_t vertex_count = 0; // total (across all meshes)
|
2026-04-12 09:09:32 +10:00
|
|
|
uint32_t total_triangles = 0;
|
|
|
|
|
|
2026-04-12 19:53:06 +10:00
|
|
|
std::vector<MeshInfo> meshes;
|
|
|
|
|
std::vector<InstanceCpu> instances; // unsorted until finalize
|
|
|
|
|
uint32_t ssbo_instance_count = 0;
|
|
|
|
|
|
2026-04-12 20:23:50 +10:00
|
|
|
// Per-instance world AABB + BVH (built at finalize). The BVH is the
|
|
|
|
|
// same ordering as `instances`; bvh_items[i] corresponds to instances[i].
|
|
|
|
|
std::vector<BvhItem> bvh_items;
|
|
|
|
|
ModelBvh bvh;
|
|
|
|
|
|
|
|
|
|
// Dynamic visible-instance index buffer (std430, binding = 1).
|
|
|
|
|
// Re-uploaded each frame from frame_visible_scratch_.
|
|
|
|
|
GLuint visible_ssbo = 0;
|
|
|
|
|
size_t visible_ssbo_capacity = 0; // bytes
|
|
|
|
|
|
|
|
|
|
// Per-mesh visible-list offset/count, rebuilt each frame.
|
|
|
|
|
std::vector<uint32_t> mesh_vis_first;
|
|
|
|
|
std::vector<uint32_t> mesh_vis_count;
|
|
|
|
|
|
2026-04-12 19:53:06 +10:00
|
|
|
bool finalized = false;
|
|
|
|
|
bool hidden = false;
|
2026-04-12 09:09:32 +10:00
|
|
|
};
|
|
|
|
|
|
2026-04-11 16:30:10 +10:00
|
|
|
class ViewportWindow : public QWindow {
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
public:
|
|
|
|
|
explicit ViewportWindow(QWindow* parent = nullptr);
|
|
|
|
|
~ViewportWindow();
|
|
|
|
|
|
2026-04-12 19:53:06 +10:00
|
|
|
// Streaming ingress.
|
|
|
|
|
void uploadMeshChunk(const MeshChunk& chunk);
|
|
|
|
|
void uploadInstanceChunk(const InstanceChunk& chunk);
|
2026-04-11 16:30:10 +10:00
|
|
|
|
2026-04-12 19:53:06 +10:00
|
|
|
// Called once all chunks for a model have arrived: sorts instances by
|
|
|
|
|
// mesh_id, assigns each mesh its contiguous range, and uploads the
|
|
|
|
|
// instance SSBO. The model becomes drawable.
|
|
|
|
|
void finalizeModel(uint32_t model_id);
|
|
|
|
|
|
|
|
|
|
void resetScene();
|
2026-04-12 09:09:32 +10:00
|
|
|
|
2026-04-12 20:10:20 +10:00
|
|
|
// Snapshot the finalised model into a SidecarData struct for caching.
|
|
|
|
|
// Vertices + indices are read back from the GPU; meshes/instances come
|
|
|
|
|
// from the CPU-side vectors. Leaves `elements` and `string_table` empty
|
|
|
|
|
// for the caller to fill in.
|
|
|
|
|
bool snapshotModel(uint32_t model_id, SidecarData& out) const;
|
|
|
|
|
|
|
|
|
|
// Restore a finalised model from a cached SidecarData struct. Replaces
|
|
|
|
|
// any existing state for model_id and marks it drawable.
|
|
|
|
|
void applyCachedModel(uint32_t model_id, SidecarData data);
|
|
|
|
|
|
2026-04-11 20:49:39 +10:00
|
|
|
void hideModel(uint32_t model_id);
|
|
|
|
|
void showModel(uint32_t model_id);
|
|
|
|
|
void removeModel(uint32_t model_id);
|
|
|
|
|
|
2026-04-11 16:30:10 +10:00
|
|
|
void setSelectedObjectId(uint32_t id);
|
|
|
|
|
uint32_t pickObjectAt(int x, int y);
|
|
|
|
|
|
2026-04-11 20:05:50 +10:00
|
|
|
struct FrameStats {
|
|
|
|
|
float fps;
|
|
|
|
|
float frame_time_ms;
|
|
|
|
|
uint32_t total_objects;
|
|
|
|
|
uint32_t visible_objects;
|
|
|
|
|
uint32_t total_triangles;
|
|
|
|
|
uint32_t visible_triangles;
|
2026-04-12 19:53:06 +10:00
|
|
|
uint32_t unique_meshes;
|
|
|
|
|
uint32_t instanced_draws;
|
2026-04-11 20:05:50 +10:00
|
|
|
};
|
|
|
|
|
|
2026-04-11 16:30:10 +10:00
|
|
|
signals:
|
|
|
|
|
void objectPicked(uint32_t object_id);
|
|
|
|
|
void initialized();
|
2026-04-11 20:05:50 +10:00
|
|
|
void frameStatsUpdated(const ViewportWindow::FrameStats& stats);
|
2026-04-11 16:30:10 +10:00
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
void exposeEvent(QExposeEvent* event) override;
|
|
|
|
|
void resizeEvent(QResizeEvent* event) override;
|
|
|
|
|
bool event(QEvent* event) override;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void initGL();
|
|
|
|
|
void render();
|
|
|
|
|
void renderPickPass();
|
|
|
|
|
void renderAxisGizmo();
|
|
|
|
|
void updateCamera();
|
|
|
|
|
void buildShaders();
|
|
|
|
|
void buildAxisGizmo();
|
2026-04-12 09:09:32 +10:00
|
|
|
void setupVaoLayout(GLuint vao, GLuint vbo, GLuint ebo);
|
|
|
|
|
bool growModelVbo(ModelGpuData& m, size_t needed_total);
|
|
|
|
|
bool growModelEbo(ModelGpuData& m, size_t needed_total);
|
2026-04-12 19:53:06 +10:00
|
|
|
ModelGpuData& getOrCreateModel(uint32_t model_id);
|
2026-04-11 16:30:10 +10:00
|
|
|
|
2026-04-12 20:23:50 +10:00
|
|
|
// Populate m.mesh_vis_first / mesh_vis_count and upload visible indices
|
|
|
|
|
// to m.visible_ssbo. Uses BVH when available, else linear scan.
|
|
|
|
|
void cullAndUploadVisible(ModelGpuData& m, const float planes[6][4]);
|
|
|
|
|
|
2026-04-11 16:30:10 +10:00
|
|
|
// Mouse interaction
|
|
|
|
|
void handleMousePress(QMouseEvent* event);
|
|
|
|
|
void handleMouseRelease(QMouseEvent* event);
|
|
|
|
|
void handleMouseMove(QMouseEvent* event);
|
|
|
|
|
void handleWheel(QWheelEvent* event);
|
|
|
|
|
|
|
|
|
|
QOpenGLContext* context_ = nullptr;
|
|
|
|
|
QOpenGLFunctions_4_5_Core* gl_ = nullptr;
|
|
|
|
|
QTimer render_timer_;
|
|
|
|
|
QElapsedTimer frame_clock_;
|
|
|
|
|
bool gl_initialized_ = false;
|
|
|
|
|
|
|
|
|
|
// Shaders
|
|
|
|
|
GLuint main_program_ = 0;
|
|
|
|
|
GLuint pick_program_ = 0;
|
|
|
|
|
GLuint axis_program_ = 0;
|
|
|
|
|
|
2026-04-12 19:53:06 +10:00
|
|
|
// Axis gizmo
|
2026-04-11 16:30:10 +10:00
|
|
|
GLuint axis_vao_ = 0;
|
|
|
|
|
GLuint axis_vbo_ = 0;
|
|
|
|
|
|
2026-04-12 09:09:32 +10:00
|
|
|
// Per-model GPU data
|
|
|
|
|
std::unordered_map<uint32_t, ModelGpuData> models_gpu_;
|
2026-04-11 16:30:10 +10:00
|
|
|
|
|
|
|
|
// Pick framebuffer
|
|
|
|
|
GLuint pick_fbo_ = 0;
|
|
|
|
|
GLuint pick_color_tex_ = 0;
|
|
|
|
|
GLuint pick_depth_rbo_ = 0;
|
|
|
|
|
int pick_width_ = 0;
|
|
|
|
|
int pick_height_ = 0;
|
|
|
|
|
|
2026-04-12 19:53:06 +10:00
|
|
|
// Per-frame stats
|
2026-04-12 09:09:32 +10:00
|
|
|
uint32_t visible_triangles_ = 0;
|
2026-04-12 19:17:44 +10:00
|
|
|
uint32_t visible_objects_ = 0;
|
2026-04-12 19:53:06 +10:00
|
|
|
uint32_t instanced_draws_ = 0;
|
2026-04-11 19:50:46 +10:00
|
|
|
|
2026-04-12 20:23:50 +10:00
|
|
|
// Reused scratch: visible-instance index lists per mesh, flattened into
|
|
|
|
|
// `visible_flat_` for upload. Both live in the parent object to avoid
|
|
|
|
|
// per-frame allocation.
|
|
|
|
|
std::vector<std::vector<uint32_t>> visible_by_mesh_;
|
|
|
|
|
std::vector<uint32_t> visible_flat_;
|
|
|
|
|
|
2026-04-11 16:30:10 +10:00
|
|
|
// Camera
|
|
|
|
|
QVector3D camera_target_{0, 0, 0};
|
|
|
|
|
float camera_distance_ = 50.0f;
|
|
|
|
|
float camera_yaw_ = 45.0f;
|
|
|
|
|
float camera_pitch_ = 30.0f;
|
|
|
|
|
QMatrix4x4 view_matrix_;
|
|
|
|
|
QMatrix4x4 proj_matrix_;
|
|
|
|
|
|
2026-04-12 19:53:06 +10:00
|
|
|
// Mouse
|
2026-04-11 16:30:10 +10:00
|
|
|
Qt::MouseButton active_button_ = Qt::NoButton;
|
|
|
|
|
QPoint last_mouse_pos_;
|
|
|
|
|
|
|
|
|
|
// Selection
|
|
|
|
|
uint32_t selected_object_id_ = 0;
|
2026-04-12 09:09:32 +10:00
|
|
|
|
2026-04-12 19:53:06 +10:00
|
|
|
// FPS smoothing
|
2026-04-11 20:05:50 +10:00
|
|
|
int frame_count_ = 0;
|
|
|
|
|
float accumulated_time_ = 0.0f;
|
|
|
|
|
float last_fps_ = 0.0f;
|
2026-04-11 16:30:10 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#endif // VIEWPORTWINDOW_H
|