123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- #ifndef SLIC3R_OCSG_EXMP_ENGINE_HPP
- #define SLIC3R_OCSG_EXMP_ENGINE_HPP
- #include <vector>
- #include <memory>
- #include <chrono>
- #include <libslic3r/Geometry.hpp>
- #include <libslic3r/Model.hpp>
- #include <libslic3r/TriangleMesh.hpp>
- #include <libslic3r/SLA/Hollowing.hpp>
- #include <opencsg/opencsg.h>
- namespace Slic3r {
- class SLAPrint;
- namespace GL {
- template<class T, class A = std::allocator<T>> using vector = std::vector<T, A>;
- // remove empty weak pointers from a vector
- template<class L> inline void cleanup(vector<std::weak_ptr<L>> &listeners) {
- auto it = std::remove_if(listeners.begin(), listeners.end(),
- [](auto &l) { return !l.lock(); });
- listeners.erase(it, listeners.end());
- }
- // Call a class method on each element of a vector of objects (weak pointers)
- // of the same type.
- template<class F, class L, class...Args>
- inline void call(F &&f, vector<std::weak_ptr<L>> &listeners, Args&&... args) {
- for (auto &l : listeners)
- if (auto p = l.lock()) ((p.get())->*f)(std::forward<Args>(args)...);
- }
- // A representation of a mouse input for the engine.
- class MouseInput
- {
- public:
- enum WheelAxis { waVertical, waHorizontal };
-
- // Interface to implement if an object wants to receive notifications
- // about mouse events.
- class Listener {
- public:
- virtual ~Listener();
-
- 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:
- vector<std::weak_ptr<Listener>> 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(std::shared_ptr<Listener> listener)
- {
- m_listeners.emplace_back(listener);
- cleanup(m_listeners);
- }
- };
- // This is a stripped down version of Slic3r::IndexedVertexArray
- class IndexedVertexArray {
- public:
- ~IndexedVertexArray() { release_geometry(); }
- // Vertices and their normals, interleaved to be used by void
- // glInterleavedArrays(GL_N3F_V3F, 0, x)
- vector<float> vertices_and_normals_interleaved;
- vector<int> triangle_indices;
- vector<int> 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();
- };
- // Try to enable or disable multisampling.
- bool enable_multisampling(bool e = true);
- class Volume {
- IndexedVertexArray m_geom;
- Geometry::Transformation m_trafo;
-
- public:
-
- 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();
- }
- };
- // A primitive that can be used with OpenCSG rendering algorithms.
- // Does a similar job to GLVolume.
- class Primitive : public Volume, public OpenCSG::Primitive
- {
- public:
- using OpenCSG::Primitive::Primitive;
-
- Primitive() : OpenCSG::Primitive(OpenCSG::Intersection, 1) {}
-
- void render() override { Volume::render(); }
- };
- // A simple representation of a camera in a 3D 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; }
- };
- // Reset a camera object
- inline void reset(Camera &cam)
- {
- cam.set_rotation({0., 0.});
- cam.set_zoom(0.);
- cam.set_reference_point({0., 0., 0.});
- cam.set_clip_z(0.);
- }
- // Specialization of a camera which shows in perspective projection
- class PerspectiveCamera: public Camera {
- public:
-
- void set_screen(long width, long height) override;
- };
- // A simple counter of FPS. Subscribed objects will receive updates of the
- // current fps.
- class FpsCounter {
- vector<std::function<void(double)>> m_listeners;
-
- using Clock = std::chrono::high_resolution_clock;
- using Duration = Clock::duration;
- using TimePoint = Clock::time_point;
-
- int m_frames = 0;
- TimePoint m_last = Clock::now(), m_window = m_last;
-
- double m_resolution = 0.1, m_window_size = 1.0;
- double m_fps = 0.;
-
- static double to_sec(Duration d)
- {
- return d.count() * double(Duration::period::num) / Duration::period::den;
- }
-
- public:
-
- void update();
- void add_listener(std::function<void(double)> lst)
- {
- m_listeners.emplace_back(lst);
- }
-
- void clear_listeners() { m_listeners = {}; }
- void set_notification_interval(double seconds);
- void set_measure_window_size(double seconds);
-
- double get_notification_interval() const { return m_resolution; }
- double get_mesure_window_size() const { return m_window_size; }
- };
- // Collection of the used OpenCSG library settings.
- class CSGSettings {
- public:
- static const constexpr unsigned DEFAULT_CONVEXITY = 10;
-
- private:
- OpenCSG::Algorithm m_csgalg = OpenCSG::Algorithm::Automatic;
- OpenCSG::DepthComplexityAlgorithm m_depth_algo = OpenCSG::NoDepthComplexitySampling;
- OpenCSG::Optimization m_optim = OpenCSG::OptimizationDefault;
- bool m_enable = true;
- unsigned int m_convexity = DEFAULT_CONVEXITY;
-
- public:
- int get_algo() const { return int(m_csgalg); }
- void set_algo(int alg)
- {
- if (alg < OpenCSG::Algorithm::AlgorithmUnused)
- m_csgalg = OpenCSG::Algorithm(alg);
- }
-
- int get_depth_algo() const { return int(m_depth_algo); }
- void set_depth_algo(int alg)
- {
- if (alg < OpenCSG::DepthComplexityAlgorithmUnused)
- m_depth_algo = OpenCSG::DepthComplexityAlgorithm(alg);
- }
-
- int get_optimization() const { return int(m_optim); }
- void set_optimization(int o)
- {
- if (o < OpenCSG::Optimization::OptimizationUnused)
- m_optim = OpenCSG::Optimization(o);
- }
-
- void enable_csg(bool en = true) { m_enable = en; }
- bool is_enabled() const { return m_enable; }
-
- unsigned get_convexity() const { return m_convexity; }
- void set_convexity(unsigned c) { m_convexity = c; }
- };
- // The scene is a wrapper around SLAPrint which holds the data to be visualized.
- class Scene
- {
- std::unique_ptr<SLAPrint> m_print;
- public:
-
- // Subscribers will be notified if the model is changed. This might be a
- // display which will have to load the meshes and repaint itself when
- // the scene data changes.
- // eg. We load a new 3mf through the UI, this will notify the controller
- // associated with the scene and all the displays that the controller is
- // connected with.
- class Listener {
- public:
- virtual ~Listener() = default;
- virtual void on_scene_updated(const Scene &scene) = 0;
- };
-
- Scene();
- ~Scene();
-
- void set_print(std::unique_ptr<SLAPrint> &&print);
- const SLAPrint * get_print() const { return m_print.get(); }
-
- BoundingBoxf3 get_bounding_box() const;
-
- void add_listener(std::shared_ptr<Listener> listener)
- {
- m_listeners.emplace_back(listener);
- cleanup(m_listeners);
- }
-
- private:
- vector<std::weak_ptr<Listener>> m_listeners;
- };
- // The basic Display. This is almost just an interface but will do all the
- // initialization and show the fps values. Overriding the render_scene is
- // needed to show the scene content. The specific method of displaying the
- // scene is up the the particular implementation (OpenCSG or other screen space
- // boolean algorithms)
- class Display : public Scene::Listener
- {
- protected:
- Vec2i m_size;
- bool m_initialized = false;
-
- std::shared_ptr<Camera> m_camera;
- FpsCounter m_fps_counter;
-
- public:
-
- explicit Display(std::shared_ptr<Camera> camera = nullptr)
- : m_camera(camera ? camera : std::make_shared<PerspectiveCamera>())
- {}
-
- ~Display() override;
-
- std::shared_ptr<const Camera> get_camera() const { return m_camera; }
- std::shared_ptr<Camera> get_camera() { return m_camera; }
- void set_camera(std::shared_ptr<Camera> cam) { m_camera = cam; }
-
- virtual void swap_buffers() = 0;
- virtual void set_active(long width, long height);
- virtual void set_screen_size(long width, long height);
- Vec2i get_screen_size() const { return m_size; }
-
- virtual void repaint();
-
- bool is_initialized() const { return m_initialized; }
-
- virtual void clear_screen();
- virtual void render_scene() {}
-
- template<class _FpsCounter> void set_fps_counter(_FpsCounter &&fpsc)
- {
- m_fps_counter = std::forward<_FpsCounter>(fpsc);
- }
- const FpsCounter &get_fps_counter() const { return m_fps_counter; }
- FpsCounter &get_fps_counter() { return m_fps_counter; }
- };
- // Special dispaly using OpenCSG for rendering the scene.
- class CSGDisplay : public Display {
- protected:
- CSGSettings m_csgsettings;
-
- // Cache the renderable primitives. These will be fetched when the scene
- // is modified.
- struct SceneCache {
- vector<std::shared_ptr<Primitive>> primitives;
- vector<Primitive *> primitives_free;
- vector<OpenCSG::Primitive *> primitives_csg;
-
- void clear();
-
- std::shared_ptr<Primitive> add_mesh(const TriangleMesh &mesh);
- std::shared_ptr<Primitive> add_mesh(const TriangleMesh &mesh,
- OpenCSG::Operation op,
- unsigned covexity);
- } m_scene_cache;
-
- public:
-
- // Receive or apply the new settings.
- const CSGSettings & get_csgsettings() const { return m_csgsettings; }
- void apply_csgsettings(const CSGSettings &settings);
-
- void render_scene() override;
-
- void on_scene_updated(const Scene &scene) override;
- };
- // The controller is a hub which dispatches mouse events to the connected
- // displays. It keeps track of the mouse wheel position, the states whether
- // the mouse is being held, dragged, etc... All the connected displays will
- // mirror the camera movement (if there is more than one display).
- class Controller : public std::enable_shared_from_this<Controller>,
- public MouseInput::Listener,
- public Scene::Listener
- {
- long m_wheel_pos = 0;
- Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev;
- bool m_left_btn = false, m_right_btn = false;
- std::shared_ptr<Scene> m_scene;
- vector<std::weak_ptr<Display>> m_displays;
-
- // Call a method of Camera on all the cameras of the attached displays
- template<class F, class...Args>
- void call_cameras(F &&f, Args&&... args) {
- for (std::weak_ptr<Display> &l : m_displays)
- if (auto disp = l.lock()) if (auto cam = disp->get_camera())
- (cam.get()->*f)(std::forward<Args>(args)...);
- }
-
- public:
-
- // Set the scene that will be controlled.
- void set_scene(std::shared_ptr<Scene> scene)
- {
- m_scene = scene;
- m_scene->add_listener(shared_from_this());
- }
-
- const Scene * get_scene() const { return m_scene.get(); }
- void add_display(std::shared_ptr<Display> disp)
- {
- m_displays.emplace_back(disp);
- cleanup(m_displays);
- }
-
- void remove_displays() { m_displays = {}; }
-
- void on_scene_updated(const Scene &scene) 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 on_scroll(long v, long d, MouseInput::WheelAxis wa) override;
- void on_moved_to(long x, long y) override;
- void move_clip_plane(double z) { call_cameras(&Camera::set_clip_z, z); }
- };
- }} // namespace Slic3r::GL
- #endif // SLIC3R_OCSG_EXMP_ENGINE_HPP
|