From d3925abb13aa6e43257eafa0638d94e39b41e3a5 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 19 Dec 2019 16:10:34 +0100 Subject: [PATCH] Add some comments. --- sandboxes/opencsg/Engine.hpp | 54 ++++++++++++++++++++++++++------ sandboxes/opencsg/main.cpp | 60 +++++++++++++++++++++++++----------- 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/sandboxes/opencsg/Engine.hpp b/sandboxes/opencsg/Engine.hpp index 5e7030523..2b58e9b75 100644 --- a/sandboxes/opencsg/Engine.hpp +++ b/sandboxes/opencsg/Engine.hpp @@ -17,33 +17,36 @@ class SLAPrint; namespace GL { +// Simple shorthands for smart pointers template using shptr = std::shared_ptr; template using uqptr = std::unique_ptr; template using wkptr = std::weak_ptr; -template> -using vector = std::vector; +template> using vector = std::vector; +// remove empty weak pointers from a vector template void cleanup(vector> &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 void call(F &&f, vector> &listeners, Args&&... args) { for (auto &l : listeners) if (auto p = l.lock()) ((p.get())->*f)(std::forward(args)...); } +// A representation of a mouse input for the engine. class MouseInput { public: + enum WheelAxis { waVertical, waHorizontal }; - enum WheelAxis { - waVertical, waHorizontal - }; - + // Interface to implement if an object wants to receive notifications + // about mouse events. class Listener { public: virtual ~Listener(); @@ -99,6 +102,7 @@ public: } }; +// This is a stripped down version of Slic3r::IndexedVertexArray class IndexedVertexArray { public: ~IndexedVertexArray() { release_geometry(); } @@ -164,8 +168,11 @@ public: void shrink_to_fit(); }; +// Try to enable or disable multisampling. bool enable_multisampling(bool e = true); +// A primitive that can be used with OpenCSG rendering algorithms. +// Does a similar job to GLVolume. class Primitive : public OpenCSG::Primitive { IndexedVertexArray m_geom; @@ -176,19 +183,21 @@ public: Primitive() : OpenCSG::Primitive(OpenCSG::Intersection, 1) {} - void render(); + void render() override; 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) { + inline void load_mesh(const TriangleMesh &mesh) + { m_geom.load_mesh(mesh); m_geom.finalize_geometry(); } }; +// A simple representation of a camera in a 3D scene class Camera { protected: Vec2f m_rot = {0., 0.}; @@ -209,6 +218,7 @@ public: void set_clip_z(double z) { m_clip_z = z; } }; +// Reset a camera object inline void reset(Camera &cam) { cam.set_rotation({0., 0.}); @@ -217,12 +227,15 @@ inline void reset(Camera &cam) 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> m_listeners; @@ -259,6 +272,7 @@ public: 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; @@ -286,12 +300,19 @@ public: 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 { uqptr 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; @@ -316,6 +337,11 @@ private: vector> 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: @@ -356,10 +382,13 @@ public: 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> primitives; vector primitives_free; @@ -375,6 +404,7 @@ protected: public: + // Receive or apply the new settings. const CSGSettings & get_csgsettings() const { return m_csgsettings; } void apply_csgsettings(const CSGSettings &settings); @@ -383,6 +413,11 @@ public: 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, public MouseInput::Listener, public Scene::Listener @@ -404,6 +439,7 @@ class Controller : public std::enable_shared_from_this, public: + // Set the scene that will be controlled. void set_scene(shptr scene) { m_scene = scene; diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index 95e00b366..b609657c0 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -30,8 +30,11 @@ using namespace Slic3r::GL; +// The opengl rendering facility. Here we implement the rendering objects. class Canvas: public wxGLCanvas { + + // Tell the CSGDisplay how to swap buffers and set the gl context. class OCSGRenderer: public Slic3r::GL::CSGDisplay { Canvas *m_canvas; shptr m_context; @@ -62,8 +65,10 @@ class Canvas: public wxGLCanvas ~OCSGRenderer() override { m_scene_cache.clear(); } }; + // Create the OCSGDisplay for rendering with OpenCSG algorithms shptr m_ocsgdisplay = std::make_shared(this); + // One display is active at a time, the OCSGRenderer by default. shptr m_display = m_ocsgdisplay; public: @@ -94,6 +99,7 @@ public: shptr get_ocsg_display() const { return m_ocsgdisplay; } }; +// Enumerate possible mouse events, we will record them. enum EEvents { LCLK_U, RCLK_U, LCLK_D, RCLK_D, DDCLK, SCRL, MV }; struct Event { @@ -102,6 +108,8 @@ struct Event Event(EEvents t, long x = 0, long y = 0) : type{t}, a{x}, b{y} {} }; +// Create a special mouse input adapter, which can store (record) the received +// mouse signals into a file and play back the stored events later. class RecorderMouseInput: public MouseInput { std::vector m_events; bool m_recording = false, m_playing = false; @@ -181,16 +189,20 @@ public: } }; +// The top level frame of the application. class MyFrame: public wxFrame { + // Instantiate the 3D engine. shptr m_scene; // Model shptr m_canvas; // View shptr m_ctl; // Controller - + + // Add a status bar with progress indication. shptr m_stbar; RecorderMouseInput m_mouse; + // When loading a Model from 3mf and preparing it, we use a separate thread. class SLAJob: public Slic3r::GUI::Job { MyFrame *m_parent; std::unique_ptr m_print; @@ -202,12 +214,15 @@ class MyFrame: public wxFrame , m_parent{frame} , m_fname{fname} {} - + + // Runs in separate thread void process() override; const std::string & get_project_fname() const { return m_fname; } protected: + + // Runs in the UI thread. void finalize() override { m_parent->m_scene->set_print(std::move(m_print)); @@ -218,16 +233,22 @@ class MyFrame: public wxFrame uqptr m_ui_job; + // To keep track of the running average of measured fps values. double m_fps_avg = 0.; public: - MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size, const Slic3r::GL::CSGSettings &settings); + MyFrame(const wxString & title, + const wxPoint & pos, + const wxSize & size, + const Slic3r::GL::CSGSettings &settings); + // Grab a 3mf and load (hollow it out) within the UI job. void load_model(const std::string &fname) { m_ui_job = std::make_unique(this, fname); m_ui_job->start(); } + // Load a previously stored mouse event log and play it back. void play_back_mouse(const std::string &events_fname) { std::fstream stream(events_fname, std::fstream::in); @@ -249,11 +270,14 @@ public: Canvas * canvas() { return m_canvas.get(); } const Canvas * canvas() const { return m_canvas.get(); } + // Bind the canvas mouse events to a class implementing MouseInput interface void bind_canvas_events(MouseInput &msinput); double get_fps_average() const { return m_fps_avg; } }; +// Possible OpenCSG configuration values. Will be used on the command line and +// on the UI widgets. static const std::vector CSG_ALGS = {"Auto", "Goldfeather", "SCS"}; static const std::vector CSG_DEPTH = {"Off", "OcclusionQuery", "On"}; static const std::vector CSG_OPT = { "Default", "ForceOn", "On", "Off" }; @@ -482,9 +506,9 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, }); csg_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ - CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); - settings.enable_csg(csg_toggle->GetValue()); - m_canvas->get_ocsg_display()->apply_csgsettings(settings); + CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); + stt.enable_csg(csg_toggle->GetValue()); + m_canvas->get_ocsg_display()->apply_csgsettings(stt); }); alg_select->Bind(wxEVT_COMBOBOX, @@ -492,33 +516,33 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, { int sel = alg_select->GetSelection(); depth_select->Enable(sel > 0); - CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); - settings.set_algo(OpenCSG::Algorithm(sel)); - m_canvas->get_ocsg_display()->apply_csgsettings(settings); + CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); + stt.set_algo(OpenCSG::Algorithm(sel)); + m_canvas->get_ocsg_display()->apply_csgsettings(stt); }); depth_select->Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) { int sel = depth_select->GetSelection(); - CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); - settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); - m_canvas->get_ocsg_display()->apply_csgsettings(settings); + CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); + stt.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); + m_canvas->get_ocsg_display()->apply_csgsettings(stt); }); optimization_select->Bind(wxEVT_COMBOBOX, [this, optimization_select](wxCommandEvent &) { int sel = optimization_select->GetSelection(); - CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); - settings.set_optimization(OpenCSG::Optimization(sel)); - m_canvas->get_ocsg_display()->apply_csgsettings(settings); + CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); + stt.set_optimization(OpenCSG::Optimization(sel)); + m_canvas->get_ocsg_display()->apply_csgsettings(stt); }); convexity_spin->Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { - CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); + CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); int c = convexity_spin->GetValue(); if (c > 0) { - settings.set_convexity(unsigned(c)); - m_canvas->get_ocsg_display()->apply_csgsettings(settings); + stt.set_convexity(unsigned(c)); + m_canvas->get_ocsg_display()->apply_csgsettings(stt); } });