Engine.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. #ifndef SLIC3R_OCSG_EXMP_ENGINE_HPP
  2. #define SLIC3R_OCSG_EXMP_ENGINE_HPP
  3. #include <vector>
  4. #include <memory>
  5. #include <chrono>
  6. #include <libslic3r/Geometry.hpp>
  7. #include <libslic3r/Model.hpp>
  8. #include <libslic3r/TriangleMesh.hpp>
  9. #include <libslic3r/SLA/Hollowing.hpp>
  10. #include <opencsg/opencsg.h>
  11. namespace Slic3r {
  12. class SLAPrint;
  13. namespace GL {
  14. template<class T, class A = std::allocator<T>> using vector = std::vector<T, A>;
  15. // remove empty weak pointers from a vector
  16. template<class L> inline void cleanup(vector<std::weak_ptr<L>> &listeners) {
  17. auto it = std::remove_if(listeners.begin(), listeners.end(),
  18. [](auto &l) { return !l.lock(); });
  19. listeners.erase(it, listeners.end());
  20. }
  21. // Call a class method on each element of a vector of objects (weak pointers)
  22. // of the same type.
  23. template<class F, class L, class...Args>
  24. inline void call(F &&f, vector<std::weak_ptr<L>> &listeners, Args&&... args) {
  25. for (auto &l : listeners)
  26. if (auto p = l.lock()) ((p.get())->*f)(std::forward<Args>(args)...);
  27. }
  28. // A representation of a mouse input for the engine.
  29. class MouseInput
  30. {
  31. public:
  32. enum WheelAxis { waVertical, waHorizontal };
  33. // Interface to implement if an object wants to receive notifications
  34. // about mouse events.
  35. class Listener {
  36. public:
  37. virtual ~Listener();
  38. virtual void on_left_click_down() {}
  39. virtual void on_left_click_up() {}
  40. virtual void on_right_click_down() {}
  41. virtual void on_right_click_up() {}
  42. virtual void on_double_click() {}
  43. virtual void on_scroll(long /*v*/, long /*delta*/, WheelAxis ) {}
  44. virtual void on_moved_to(long /*x*/, long /*y*/) {}
  45. };
  46. private:
  47. vector<std::weak_ptr<Listener>> m_listeners;
  48. public:
  49. virtual ~MouseInput() = default;
  50. virtual void left_click_down()
  51. {
  52. call(&Listener::on_left_click_down, m_listeners);
  53. }
  54. virtual void left_click_up()
  55. {
  56. call(&Listener::on_left_click_up, m_listeners);
  57. }
  58. virtual void right_click_down()
  59. {
  60. call(&Listener::on_right_click_down, m_listeners);
  61. }
  62. virtual void right_click_up()
  63. {
  64. call(&Listener::on_right_click_up, m_listeners);
  65. }
  66. virtual void double_click()
  67. {
  68. call(&Listener::on_double_click, m_listeners);
  69. }
  70. virtual void scroll(long v, long d, WheelAxis wa)
  71. {
  72. call(&Listener::on_scroll, m_listeners, v, d, wa);
  73. }
  74. virtual void move_to(long x, long y)
  75. {
  76. call(&Listener::on_moved_to, m_listeners, x, y);
  77. }
  78. void add_listener(std::shared_ptr<Listener> listener)
  79. {
  80. m_listeners.emplace_back(listener);
  81. cleanup(m_listeners);
  82. }
  83. };
  84. // This is a stripped down version of Slic3r::IndexedVertexArray
  85. class IndexedVertexArray {
  86. public:
  87. ~IndexedVertexArray() { release_geometry(); }
  88. // Vertices and their normals, interleaved to be used by void
  89. // glInterleavedArrays(GL_N3F_V3F, 0, x)
  90. vector<float> vertices_and_normals_interleaved;
  91. vector<int> triangle_indices;
  92. vector<int> quad_indices;
  93. // When the geometry data is loaded into the graphics card as Vertex
  94. // Buffer Objects, the above mentioned std::vectors are cleared and the
  95. // following variables keep their original length.
  96. size_t vertices_and_normals_interleaved_size{ 0 };
  97. size_t triangle_indices_size{ 0 };
  98. size_t quad_indices_size{ 0 };
  99. // IDs of the Vertex Array Objects, into which the geometry has been loaded.
  100. // Zero if the VBOs are not sent to GPU yet.
  101. unsigned int vertices_and_normals_interleaved_VBO_id{ 0 };
  102. unsigned int triangle_indices_VBO_id{ 0 };
  103. unsigned int quad_indices_VBO_id{ 0 };
  104. void push_geometry(float x, float y, float z, float nx, float ny, float nz);
  105. inline void push_geometry(
  106. double x, double y, double z, double nx, double ny, double nz)
  107. {
  108. push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz));
  109. }
  110. inline void push_geometry(const Vec3d &p, const Vec3d &n)
  111. {
  112. push_geometry(p(0), p(1), p(2), n(0), n(1), n(2));
  113. }
  114. void push_triangle(int idx1, int idx2, int idx3);
  115. void load_mesh(const TriangleMesh &mesh);
  116. inline bool has_VBOs() const
  117. {
  118. return vertices_and_normals_interleaved_VBO_id != 0;
  119. }
  120. // Finalize the initialization of the geometry & indices,
  121. // upload the geometry and indices to OpenGL VBO objects
  122. // and shrink the allocated data, possibly relasing it if it has been
  123. // loaded into the VBOs.
  124. void finalize_geometry();
  125. // Release the geometry data, release OpenGL VBOs.
  126. void release_geometry();
  127. void render() const;
  128. // Is there any geometry data stored?
  129. bool empty() const { return vertices_and_normals_interleaved_size == 0; }
  130. void clear();
  131. // Shrink the internal storage to tighly fit the data stored.
  132. void shrink_to_fit();
  133. };
  134. // Try to enable or disable multisampling.
  135. bool enable_multisampling(bool e = true);
  136. class Volume {
  137. IndexedVertexArray m_geom;
  138. Geometry::Transformation m_trafo;
  139. public:
  140. void render();
  141. void translation(const Vec3d &offset) { m_trafo.set_offset(offset); }
  142. void rotation(const Vec3d &rot) { m_trafo.set_rotation(rot); }
  143. void scale(const Vec3d &scaleing) { m_trafo.set_scaling_factor(scaleing); }
  144. void scale(double s) { scale({s, s, s}); }
  145. inline void load_mesh(const TriangleMesh &mesh)
  146. {
  147. m_geom.load_mesh(mesh);
  148. m_geom.finalize_geometry();
  149. }
  150. };
  151. // A primitive that can be used with OpenCSG rendering algorithms.
  152. // Does a similar job to GLVolume.
  153. class Primitive : public Volume, public OpenCSG::Primitive
  154. {
  155. public:
  156. using OpenCSG::Primitive::Primitive;
  157. Primitive() : OpenCSG::Primitive(OpenCSG::Intersection, 1) {}
  158. void render() override { Volume::render(); }
  159. };
  160. // A simple representation of a camera in a 3D scene
  161. class Camera {
  162. protected:
  163. Vec2f m_rot = {0., 0.};
  164. Vec3d m_referene = {0., 0., 0.};
  165. double m_zoom = 0.;
  166. double m_clip_z = 0.;
  167. public:
  168. virtual ~Camera() = default;
  169. virtual void view();
  170. virtual void set_screen(long width, long height) = 0;
  171. void set_rotation(const Vec2f &rotation) { m_rot = rotation; }
  172. void rotate(const Vec2f &rotation) { m_rot += rotation; }
  173. void set_zoom(double z) { m_zoom = z; }
  174. void set_reference_point(const Vec3d &p) { m_referene = p; }
  175. void set_clip_z(double z) { m_clip_z = z; }
  176. };
  177. // Reset a camera object
  178. inline void reset(Camera &cam)
  179. {
  180. cam.set_rotation({0., 0.});
  181. cam.set_zoom(0.);
  182. cam.set_reference_point({0., 0., 0.});
  183. cam.set_clip_z(0.);
  184. }
  185. // Specialization of a camera which shows in perspective projection
  186. class PerspectiveCamera: public Camera {
  187. public:
  188. void set_screen(long width, long height) override;
  189. };
  190. // A simple counter of FPS. Subscribed objects will receive updates of the
  191. // current fps.
  192. class FpsCounter {
  193. vector<std::function<void(double)>> m_listeners;
  194. using Clock = std::chrono::high_resolution_clock;
  195. using Duration = Clock::duration;
  196. using TimePoint = Clock::time_point;
  197. int m_frames = 0;
  198. TimePoint m_last = Clock::now(), m_window = m_last;
  199. double m_resolution = 0.1, m_window_size = 1.0;
  200. double m_fps = 0.;
  201. static double to_sec(Duration d)
  202. {
  203. return d.count() * double(Duration::period::num) / Duration::period::den;
  204. }
  205. public:
  206. void update();
  207. void add_listener(std::function<void(double)> lst)
  208. {
  209. m_listeners.emplace_back(lst);
  210. }
  211. void clear_listeners() { m_listeners = {}; }
  212. void set_notification_interval(double seconds);
  213. void set_measure_window_size(double seconds);
  214. double get_notification_interval() const { return m_resolution; }
  215. double get_mesure_window_size() const { return m_window_size; }
  216. };
  217. // Collection of the used OpenCSG library settings.
  218. class CSGSettings {
  219. public:
  220. static const constexpr unsigned DEFAULT_CONVEXITY = 10;
  221. private:
  222. OpenCSG::Algorithm m_csgalg = OpenCSG::Algorithm::Automatic;
  223. OpenCSG::DepthComplexityAlgorithm m_depth_algo = OpenCSG::NoDepthComplexitySampling;
  224. OpenCSG::Optimization m_optim = OpenCSG::OptimizationDefault;
  225. bool m_enable = true;
  226. unsigned int m_convexity = DEFAULT_CONVEXITY;
  227. public:
  228. int get_algo() const { return int(m_csgalg); }
  229. void set_algo(int alg)
  230. {
  231. if (alg < OpenCSG::Algorithm::AlgorithmUnused)
  232. m_csgalg = OpenCSG::Algorithm(alg);
  233. }
  234. int get_depth_algo() const { return int(m_depth_algo); }
  235. void set_depth_algo(int alg)
  236. {
  237. if (alg < OpenCSG::DepthComplexityAlgorithmUnused)
  238. m_depth_algo = OpenCSG::DepthComplexityAlgorithm(alg);
  239. }
  240. int get_optimization() const { return int(m_optim); }
  241. void set_optimization(int o)
  242. {
  243. if (o < OpenCSG::Optimization::OptimizationUnused)
  244. m_optim = OpenCSG::Optimization(o);
  245. }
  246. void enable_csg(bool en = true) { m_enable = en; }
  247. bool is_enabled() const { return m_enable; }
  248. unsigned get_convexity() const { return m_convexity; }
  249. void set_convexity(unsigned c) { m_convexity = c; }
  250. };
  251. // The scene is a wrapper around SLAPrint which holds the data to be visualized.
  252. class Scene
  253. {
  254. std::unique_ptr<SLAPrint> m_print;
  255. public:
  256. // Subscribers will be notified if the model is changed. This might be a
  257. // display which will have to load the meshes and repaint itself when
  258. // the scene data changes.
  259. // eg. We load a new 3mf through the UI, this will notify the controller
  260. // associated with the scene and all the displays that the controller is
  261. // connected with.
  262. class Listener {
  263. public:
  264. virtual ~Listener() = default;
  265. virtual void on_scene_updated(const Scene &scene) = 0;
  266. };
  267. Scene();
  268. ~Scene();
  269. void set_print(std::unique_ptr<SLAPrint> &&print);
  270. const SLAPrint * get_print() const { return m_print.get(); }
  271. BoundingBoxf3 get_bounding_box() const;
  272. void add_listener(std::shared_ptr<Listener> listener)
  273. {
  274. m_listeners.emplace_back(listener);
  275. cleanup(m_listeners);
  276. }
  277. private:
  278. vector<std::weak_ptr<Listener>> m_listeners;
  279. };
  280. // The basic Display. This is almost just an interface but will do all the
  281. // initialization and show the fps values. Overriding the render_scene is
  282. // needed to show the scene content. The specific method of displaying the
  283. // scene is up the the particular implementation (OpenCSG or other screen space
  284. // boolean algorithms)
  285. class Display : public Scene::Listener
  286. {
  287. protected:
  288. Vec2i m_size;
  289. bool m_initialized = false;
  290. std::shared_ptr<Camera> m_camera;
  291. FpsCounter m_fps_counter;
  292. public:
  293. explicit Display(std::shared_ptr<Camera> camera = nullptr)
  294. : m_camera(camera ? camera : std::make_shared<PerspectiveCamera>())
  295. {}
  296. ~Display() override;
  297. std::shared_ptr<const Camera> get_camera() const { return m_camera; }
  298. std::shared_ptr<Camera> get_camera() { return m_camera; }
  299. void set_camera(std::shared_ptr<Camera> cam) { m_camera = cam; }
  300. virtual void swap_buffers() = 0;
  301. virtual void set_active(long width, long height);
  302. virtual void set_screen_size(long width, long height);
  303. Vec2i get_screen_size() const { return m_size; }
  304. virtual void repaint();
  305. bool is_initialized() const { return m_initialized; }
  306. virtual void clear_screen();
  307. virtual void render_scene() {}
  308. template<class _FpsCounter> void set_fps_counter(_FpsCounter &&fpsc)
  309. {
  310. m_fps_counter = std::forward<_FpsCounter>(fpsc);
  311. }
  312. const FpsCounter &get_fps_counter() const { return m_fps_counter; }
  313. FpsCounter &get_fps_counter() { return m_fps_counter; }
  314. };
  315. // Special dispaly using OpenCSG for rendering the scene.
  316. class CSGDisplay : public Display {
  317. protected:
  318. CSGSettings m_csgsettings;
  319. // Cache the renderable primitives. These will be fetched when the scene
  320. // is modified.
  321. struct SceneCache {
  322. vector<std::shared_ptr<Primitive>> primitives;
  323. vector<Primitive *> primitives_free;
  324. vector<OpenCSG::Primitive *> primitives_csg;
  325. void clear();
  326. std::shared_ptr<Primitive> add_mesh(const TriangleMesh &mesh);
  327. std::shared_ptr<Primitive> add_mesh(const TriangleMesh &mesh,
  328. OpenCSG::Operation op,
  329. unsigned covexity);
  330. } m_scene_cache;
  331. public:
  332. // Receive or apply the new settings.
  333. const CSGSettings & get_csgsettings() const { return m_csgsettings; }
  334. void apply_csgsettings(const CSGSettings &settings);
  335. void render_scene() override;
  336. void on_scene_updated(const Scene &scene) override;
  337. };
  338. // The controller is a hub which dispatches mouse events to the connected
  339. // displays. It keeps track of the mouse wheel position, the states whether
  340. // the mouse is being held, dragged, etc... All the connected displays will
  341. // mirror the camera movement (if there is more than one display).
  342. class Controller : public std::enable_shared_from_this<Controller>,
  343. public MouseInput::Listener,
  344. public Scene::Listener
  345. {
  346. long m_wheel_pos = 0;
  347. Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev;
  348. bool m_left_btn = false, m_right_btn = false;
  349. std::shared_ptr<Scene> m_scene;
  350. vector<std::weak_ptr<Display>> m_displays;
  351. // Call a method of Camera on all the cameras of the attached displays
  352. template<class F, class...Args>
  353. void call_cameras(F &&f, Args&&... args) {
  354. for (std::weak_ptr<Display> &l : m_displays)
  355. if (auto disp = l.lock()) if (auto cam = disp->get_camera())
  356. (cam.get()->*f)(std::forward<Args>(args)...);
  357. }
  358. public:
  359. // Set the scene that will be controlled.
  360. void set_scene(std::shared_ptr<Scene> scene)
  361. {
  362. m_scene = scene;
  363. m_scene->add_listener(shared_from_this());
  364. }
  365. const Scene * get_scene() const { return m_scene.get(); }
  366. void add_display(std::shared_ptr<Display> disp)
  367. {
  368. m_displays.emplace_back(disp);
  369. cleanup(m_displays);
  370. }
  371. void remove_displays() { m_displays = {}; }
  372. void on_scene_updated(const Scene &scene) override;
  373. void on_left_click_down() override { m_left_btn = true; }
  374. void on_left_click_up() override { m_left_btn = false; }
  375. void on_right_click_down() override { m_right_btn = true; }
  376. void on_right_click_up() override { m_right_btn = false; }
  377. void on_scroll(long v, long d, MouseInput::WheelAxis wa) override;
  378. void on_moved_to(long x, long y) override;
  379. void move_clip_plane(double z) { call_cameras(&Camera::set_clip_z, z); }
  380. };
  381. }} // namespace Slic3r::GL
  382. #endif // SLIC3R_OCSG_EXMP_ENGINE_HPP