#ifndef GLSCENE_HPP #define GLSCENE_HPP #include #include #include #include #include #include #include namespace Slic3r { class SLAPrint; namespace GL { template using shptr = std::shared_ptr; template using uqptr = std::unique_ptr; template using wkptr = std::weak_ptr; template> using Collection = std::vector; template void cleanup(Collection> &listeners) { auto it = std::remove_if(listeners.begin(), listeners.end(), [](auto &l) { return !l.lock(); }); listeners.erase(it, listeners.end()); } template void call(F &&f, Collection> &listeners, Args&&... args) { for (auto &l : listeners) if (auto p = l.lock()) ((p.get())->*f)(std::forward(args)...); } class MouseInput { public: enum WheelAxis { waVertical, waHorizontal }; class Listener { public: virtual ~Listener() = default; virtual void on_left_click_down() {} virtual void on_left_click_up() {} virtual void on_right_click_down() {} virtual void on_right_click_up() {} virtual void on_double_click() {} virtual void on_scroll(long /*v*/, long /*delta*/, WheelAxis ) {} virtual void on_moved_to(long /*x*/, long /*y*/) {} }; private: Collection> m_listeners; public: virtual ~MouseInput() = default; virtual void left_click_down() { call(&Listener::on_left_click_down, m_listeners); } virtual void left_click_up() { call(&Listener::on_left_click_up, m_listeners); } virtual void right_click_down() { call(&Listener::on_right_click_down, m_listeners); } virtual void right_click_up() { call(&Listener::on_right_click_up, m_listeners); } virtual void double_click() { call(&Listener::on_double_click, m_listeners); } virtual void scroll(long v, long d, WheelAxis wa) { call(&Listener::on_scroll, m_listeners, v, d, wa); } virtual void move_to(long x, long y) { call(&Listener::on_moved_to, m_listeners, x, y); } void add_listener(shptr listener) { m_listeners.emplace_back(listener); cleanup(m_listeners); } }; class IndexedVertexArray { public: ~IndexedVertexArray() { release_geometry(); } // Vertices and their normals, interleaved to be used by void // glInterleavedArrays(GL_N3F_V3F, 0, x) Collection vertices_and_normals_interleaved; Collection triangle_indices; Collection quad_indices; // When the geometry data is loaded into the graphics card as Vertex // Buffer Objects, the above mentioned std::vectors are cleared and the // following variables keep their original length. size_t vertices_and_normals_interleaved_size{ 0 }; size_t triangle_indices_size{ 0 }; size_t quad_indices_size{ 0 }; // IDs of the Vertex Array Objects, into which the geometry has been loaded. // Zero if the VBOs are not sent to GPU yet. unsigned int vertices_and_normals_interleaved_VBO_id{ 0 }; unsigned int triangle_indices_VBO_id{ 0 }; unsigned int quad_indices_VBO_id{ 0 }; void push_geometry(float x, float y, float z, float nx, float ny, float nz); inline void push_geometry( double x, double y, double z, double nx, double ny, double nz) { push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz)); } inline void push_geometry(const Vec3d &p, const Vec3d &n) { push_geometry(p(0), p(1), p(2), n(0), n(1), n(2)); } void push_triangle(int idx1, int idx2, int idx3); void load_mesh(const TriangleMesh &mesh); inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; } // Finalize the initialization of the geometry & indices, // upload the geometry and indices to OpenGL VBO objects // and shrink the allocated data, possibly relasing it if it has been // loaded into the VBOs. void finalize_geometry(); // Release the geometry data, release OpenGL VBOs. void release_geometry(); void render() const; // Is there any geometry data stored? bool empty() const { return vertices_and_normals_interleaved_size == 0; } void clear(); // Shrink the internal storage to tighly fit the data stored. void shrink_to_fit(); }; bool enable_multisampling(bool e = true); void renderfps(); class Primitive : public OpenCSG::Primitive { IndexedVertexArray m_geom; Geometry::Transformation m_trafo; public: using OpenCSG::Primitive::Primitive; Primitive() : OpenCSG::Primitive(OpenCSG::Intersection, 1) {} void render(); void translation(const Vec3d &offset) { m_trafo.set_offset(offset); } void rotation(const Vec3d &rot) { m_trafo.set_rotation(rot); } void scale(const Vec3d &scaleing) { m_trafo.set_scaling_factor(scaleing); } void scale(double s) { scale({s, s, s}); } inline void load_mesh(const TriangleMesh &mesh) { m_geom.load_mesh(mesh); m_geom.finalize_geometry(); } }; class Scene; class Camera { protected: Vec2f m_rot = {0., 0.}; Vec3d m_referene = {0., 0., 0.}; double m_zoom = 0.; double m_clip_z = 0.; public: virtual ~Camera() = default; virtual void view(); virtual void set_screen(long width, long height) = 0; void set_rotation(const Vec2f &rotation) { m_rot = rotation; } void rotate(const Vec2f &rotation) { m_rot += rotation; } void set_zoom(double z) { m_zoom = z; } void set_reference_point(const Vec3d &p) { m_referene = p; } void set_clip_z(double z) { m_clip_z = z; } }; class PerspectiveCamera: public Camera { public: void set_screen(long width, long height) override; }; class CSGSettings { OpenCSG::Algorithm m_csgalg = OpenCSG::Algorithm::Automatic; OpenCSG::DepthComplexityAlgorithm m_depth_algo = OpenCSG::DepthComplexityAlgorithm::NoDepthComplexitySampling; OpenCSG::Optimization m_optim = OpenCSG::Optimization::OptimizationDefault; public: int get_algo() const { return int(m_csgalg); } void set_algo(OpenCSG::Algorithm alg) { m_csgalg = alg; } int get_depth_algo() const { return int(m_depth_algo); } void set_depth_algo(OpenCSG::DepthComplexityAlgorithm alg) { m_depth_algo = alg; } int get_optimization() const { return int(m_optim); } void set_optimization(OpenCSG::Optimization o) { m_optim = o; } }; class Display : public std::enable_shared_from_this, public MouseInput::Listener { protected: shptr m_scene; long m_wheel_pos = 0; Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev; Vec2i m_size; bool m_initialized = false, m_left_btn = false, m_right_btn = false; CSGSettings m_csgsettings; shptr m_camera; public: Display(shptr scene = nullptr, shptr camera = nullptr) : m_scene(scene) , m_camera(camera ? camera : std::make_shared()) {} virtual void swap_buffers() = 0; virtual void set_active(long width, long height); virtual void repaint(long width, long height); void repaint() { repaint(m_size.x(), m_size.y()); } void set_scene(shptr scene); shptr get_scene() { return m_scene; } bool is_initialized() const { return m_initialized; } void on_scroll(long v, long d, MouseInput::WheelAxis wa) override; void on_moved_to(long x, long y) override; void on_left_click_down() override { m_left_btn = true; } void on_left_click_up() override { m_left_btn = false; } void on_right_click_down() override { m_right_btn = true; } void on_right_click_up() override { m_right_btn = false; } void move_clip_plane(double z) { m_camera->set_clip_z(z); } const CSGSettings & get_csgsettings() const { return m_csgsettings; } void apply_csgsettings(const CSGSettings &settings); virtual void on_scene_updated(); virtual void clear_screen(); virtual void render_scene(); }; class Scene: public MouseInput::Listener { Collection> m_primitives; Collection m_primitives_free; Collection m_primitives_csg; uqptr m_print; public: Scene(); ~Scene(); const Collection& free_primitives() const { return m_primitives_free; } const Collection& csg_primitives() const { return m_primitives_csg; } void add_display(shptr disp) { m_displays.emplace_back(disp); cleanup(m_displays); } void set_print(uqptr &&print); BoundingBoxf3 get_bounding_box() const; protected: shptr add_mesh(const TriangleMesh &mesh); shptr add_mesh(const TriangleMesh &mesh, OpenCSG::Operation op, unsigned covexity); private: Collection> m_displays; }; }} // namespace Slic3r::GL #endif // GLSCENE_HPP