Engine.hpp 15 KB

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