diff --git a/CMakeLists.txt b/CMakeLists.txt index f489e46c4..26619e459 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -409,8 +409,7 @@ if(SLIC3R_STATIC) set(USE_BLOSC TRUE) endif() - -find_package(OpenVDB 5.0 COMPONENTS openvdb) +find_package(OpenVDB 5.0 REQUIRED COMPONENTS openvdb) if(OpenVDB_FOUND) slic3r_remap_configs(IlmBase::Half RelWithDebInfo Release) endif() diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index 3400fa786..7bc44bebb 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -215,20 +215,50 @@ if(UNIX AND OPENVDB_USE_STATIC_LIBS) endif() set(OpenVDB_LIB_COMPONENTS "") +set(OpenVDB_DEBUG_SUFFIX "d" CACHE STRING "Suffix for the debug libraries") + +get_property(_is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) set(LIB_NAME ${COMPONENT}) - find_library(OpenVDB_${COMPONENT}_LIBRARY ${LIB_NAME} lib${LIB_NAME} + + find_library(OpenVDB_${COMPONENT}_LIBRARY_RELEASE ${LIB_NAME} lib${LIB_NAME} PATHS ${_OPENVDB_LIBRARYDIR_SEARCH_DIRS} PATH_SUFFIXES ${OPENVDB_PATH_SUFFIXES} ) - list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY}) - if(OpenVDB_${COMPONENT}_LIBRARY) - set(OpenVDB_${COMPONENT}_FOUND TRUE) - else() - set(OpenVDB_${COMPONENT}_FOUND FALSE) - endif() + find_library(OpenVDB_${COMPONENT}_LIBRARY_DEBUG ${LIB_NAME}${OpenVDB_DEBUG_SUFFIX} lib${LIB_NAME}${OpenVDB_DEBUG_SUFFIX} + PATHS ${_OPENVDB_LIBRARYDIR_SEARCH_DIRS} + PATH_SUFFIXES ${OPENVDB_PATH_SUFFIXES} + ) + + if (_is_multi) + list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE} ${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}) + + list(FIND CMAKE_CONFIGURATION_TYPES "Debug" _has_debug) + + if(OpenVDB_${COMPONENT}_LIBRARY_RELEASE AND (_has_debug LESS 0 OR OpenVDB_${COMPONENT}_LIBRARY_DEBUG)) + set(OpenVDB_${COMPONENT}_FOUND TRUE) + else() + set(OpenVDB_${COMPONENT}_FOUND FALSE) + endif() + else () + string(TOUPPER "${CMAKE_BUILD_TYPE}" _BUILD_TYPE) + + set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_${_BUILD_TYPE}}) + + if (NOT MSVC AND NOT OpenVDB_${COMPONENT}_LIBRARY) + set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}) + endif () + + list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY}) + + if(OpenVDB_${COMPONENT}_LIBRARY) + set(OpenVDB_${COMPONENT}_FOUND TRUE) + else() + set(OpenVDB_${COMPONENT}_FOUND FALSE) + endif() + endif () endforeach() if(UNIX AND OPENVDB_USE_STATIC_LIBS) @@ -487,7 +517,6 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) if(NOT TARGET OpenVDB::${COMPONENT}) add_library(OpenVDB::${COMPONENT} UNKNOWN IMPORTED) set_target_properties(OpenVDB::${COMPONENT} PROPERTIES - IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}" INTERFACE_COMPILE_OPTIONS "${OpenVDB_DEFINITIONS}" INTERFACE_INCLUDE_DIRECTORIES "${OpenVDB_INCLUDE_DIR}" IMPORTED_LINK_DEPENDENT_LIBRARIES "${_OPENVDB_HIDDEN_DEPENDENCIES}" # non visible deps @@ -495,6 +524,17 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) INTERFACE_COMPILE_FEATURES cxx_std_11 ) + if (_is_multi) + set_target_properties(OpenVDB::${COMPONENT} PROPERTIES + IMPORTED_LOCATION_RELEASE "${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}" + IMPORTED_LOCATION_DEBUG "${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}" + ) + else () + set_target_properties(OpenVDB::${COMPONENT} PROPERTIES + IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}" + ) + endif () + if (OPENVDB_USE_STATIC_LIBS) set_target_properties(OpenVDB::${COMPONENT} PROPERTIES INTERFACE_COMPILE_DEFINITIONS "OPENVDB_STATICLIB;OPENVDB_OPENEXR_STATICLIB" diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 5e6d28b58..3e6f70e42 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -47,6 +47,7 @@ message(STATUS "PrusaSlicer deps debug build: ${DEP_DEBUG}") find_package(Git REQUIRED) +get_property(_is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) function(prusaslicer_add_cmake_project projectname) cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN}) diff --git a/sandboxes/opencsg/CMakeLists.txt b/sandboxes/opencsg/CMakeLists.txt index 651fbe82f..600ef7884 100644 --- a/sandboxes/opencsg/CMakeLists.txt +++ b/sandboxes/opencsg/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0) project(OpenCSG-example) -add_executable(opencsg_example main.cpp GLScene.hpp GLScene.cpp +add_executable(opencsg_example WIN32 main.cpp Engine.hpp Engine.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/ProgressStatusBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.cpp) @@ -11,11 +11,15 @@ find_package(wxWidgets 3.1 REQUIRED COMPONENTS core base gl html) find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) find_package(OpenCSG REQUIRED) -find_package(GLUT REQUIRED) include(${wxWidgets_USE_FILE}) - target_link_libraries(opencsg_example libslic3r) target_include_directories(opencsg_example PRIVATE ${wxWidgets_INCLUDE_DIRS}) target_compile_definitions(opencsg_example PRIVATE ${wxWidgets_DEFINITIONS}) -target_link_libraries(opencsg_example ${wxWidgets_LIBRARIES} GLEW::GLEW OpenCSG::opencsg GLUT::GLUT OpenGL::OpenGL -lXrandr -lXext -lX11) + +target_link_libraries(opencsg_example ${wxWidgets_LIBRARIES} + OpenCSG::opencsg + GLEW::GLEW + OpenGL::GL + #-lXrandr -lXext -lX11 + ) diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/Engine.cpp similarity index 90% rename from sandboxes/opencsg/GLScene.cpp rename to sandboxes/opencsg/Engine.cpp index 1cfccb3b1..590faa296 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/Engine.cpp @@ -1,16 +1,10 @@ -#include "GLScene.hpp" +#include "Engine.hpp" #include #include #include #include -#ifdef __APPLE__ -#include -#else -#include -#endif - #include #ifndef NDEBUG @@ -52,49 +46,9 @@ inline void glAssertRecentCall() { } namespace Slic3r { namespace GL { Scene::Scene() = default; - Scene::~Scene() = default; -void renderfps () { - static std::ostringstream fpsStream; - static int fps = 0; - static int ancient = 0; - static int last = 0; - static int msec = 0; - - last = msec; - msec = glutGet(GLUT_ELAPSED_TIME); - if (last / 1000 != msec / 1000) { - - float correctedFps = fps * 1000.0f / float(msec - ancient); - fpsStream.str(""); - fpsStream << "fps: " << correctedFps << std::ends; - - ancient = msec; - fps = 0; - } - glDisable(GL_DEPTH_TEST); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glColor3f(0.0f, 0.0f, 0.0f); - glRasterPos2f(-1.0f, -1.0f); - glDisable(GL_LIGHTING); - std::string s = fpsStream.str(); - for (unsigned int i=0; imodel().bounding_box(); } -void Display::SceneCache::clear() +void CSGDisplay::SceneCache::clear() { primitives_csg.clear(); primitives_free.clear(); primitives.clear(); } -shptr Display::SceneCache::add_mesh(const TriangleMesh &mesh) +shptr CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh) { auto p = std::make_shared(); p->load_mesh(mesh); @@ -157,9 +111,9 @@ shptr Display::SceneCache::add_mesh(const TriangleMesh &mesh) return p; } -shptr Display::SceneCache::add_mesh(const TriangleMesh &mesh, - OpenCSG::Operation o, - unsigned c) +shptr CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh, + OpenCSG::Operation o, + unsigned c) { auto p = std::make_shared(o, c); p->load_mesh(mesh); @@ -347,21 +301,21 @@ void Display::clear_screen() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } -void Display::set_active(long width, long height) +Display::~Display() { - static int argc = 0; - + OpenCSG::freeResources(); +} + +void Display::set_active(long width, long height) +{ if (!m_initialized) { glewInit(); - glutInit(&argc, nullptr); m_initialized = true; } - - m_size = {width, height}; - + // gray background glClearColor(0.9f, 0.9f, 0.9f, 1.0f); - + // Enable two OpenGL lights GLfloat light_diffuse[] = { 1.0f, 1.0f, 0.0f, 1.0f}; // White diffuse light GLfloat light_position0[] = {-1.0f, -1.0f, -1.0f, 0.0f}; // Infinite light location @@ -380,7 +334,7 @@ void Display::set_active(long width, long height) glEnable(GL_DEPTH_TEST); glEnable(GL_STENCIL_TEST); - m_camera->set_screen(width, height); + set_screen_size(width, height); } void Display::set_screen_size(long width, long height) @@ -389,8 +343,6 @@ void Display::set_screen_size(long width, long height) m_camera->set_screen(width, height); m_size = {width, height}; - - repaint(); } void Display::repaint() @@ -400,7 +352,7 @@ void Display::repaint() m_camera->view(); render_scene(); - renderfps(); + m_fps_counter.update(); swap_buffers(); } @@ -436,7 +388,7 @@ void Controller::on_moved_to(long x, long y) m_mouse_pos = {x, y}; } -void Display::apply_csgsettings(const CSGSettings &settings) +void CSGDisplay::apply_csgsettings(const CSGSettings &settings) { using namespace OpenCSG; @@ -452,11 +404,9 @@ void Display::apply_csgsettings(const CSGSettings &settings) if (p->getConvexity() > 1) p->setConvexity(m_csgsettings.get_convexity()); } - - repaint(); } -void Display::on_scene_updated(const Scene &scene) +void CSGDisplay::on_scene_updated(const Scene &scene) { const SLAPrint *print = scene.get_print(); if (!print) return; @@ -555,4 +505,24 @@ bool enable_multisampling(bool e) MouseInput::Listener::~Listener() = default; +void FpsCounter::update() +{ + ++m_frames; + + TimePoint msec = Clock::now(); + + double seconds_window = to_sec(msec - m_window); + m_fps = 0.5 * m_fps + 0.5 * (m_frames / seconds_window); + + if (to_sec(msec - m_last) >= m_resolution) { + m_last = msec; + for (auto &l : m_listeners) l(m_fps); + } + + if (seconds_window >= m_window_size) { + m_frames = 0; + m_window = msec; + } +} + }} // namespace Slic3r::GL diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/Engine.hpp similarity index 85% rename from sandboxes/opencsg/GLScene.hpp rename to sandboxes/opencsg/Engine.hpp index 68cc59b01..5e7030523 100644 --- a/sandboxes/opencsg/GLScene.hpp +++ b/sandboxes/opencsg/Engine.hpp @@ -1,8 +1,9 @@ -#ifndef GLSCENE_HPP -#define GLSCENE_HPP +#ifndef SLIC3R_OCSG_EXMP_ENGINE_HPP +#define SLIC3R_OCSG_EXMP_ENGINE_HPP_HPP #include #include +#include #include #include @@ -164,7 +165,6 @@ public: }; bool enable_multisampling(bool e = true); -void renderfps(); class Primitive : public OpenCSG::Primitive { @@ -189,8 +189,6 @@ public: } }; -class Scene; - class Camera { protected: Vec2f m_rot = {0., 0.}; @@ -211,12 +209,56 @@ public: void set_clip_z(double z) { m_clip_z = z; } }; +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.); +} + class PerspectiveCamera: public Camera { public: void set_screen(long width, long height) override; }; +class FpsCounter { + vector> 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 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; } +}; + class CSGSettings { public: static const constexpr unsigned DEFAULT_CONVEXITY = 10; @@ -280,6 +322,42 @@ protected: Vec2i m_size; bool m_initialized = false; + shptr m_camera; + FpsCounter m_fps_counter; + +public: + + explicit Display(shptr camera = nullptr) + : m_camera(camera ? camera : std::make_shared()) + {} + + ~Display() override; + + Camera * camera() { return m_camera.get(); } + + 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 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; } +}; + +class CSGDisplay : public Display { +protected: CSGSettings m_csgsettings; struct SceneCache { @@ -295,32 +373,14 @@ protected: unsigned covexity); } m_scene_cache; - shptr m_camera; - public: - explicit Display(shptr camera = nullptr) - : m_camera(camera ? camera : std::make_shared()) - {} - - Camera * camera() { return m_camera.get(); } - - 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; } - const CSGSettings & get_csgsettings() const { return m_csgsettings; } void apply_csgsettings(const CSGSettings &settings); - void on_scene_updated(const Scene &scene) override; + void render_scene() override; - virtual void clear_screen(); - virtual void render_scene(); + void on_scene_updated(const Scene &scene) override; }; class Controller : public std::enable_shared_from_this, @@ -331,7 +391,7 @@ class Controller : public std::enable_shared_from_this, Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev; bool m_left_btn = false, m_right_btn = false; - shptr m_scene; + shptr m_scene; vector> m_displays; // Call a method of Camera on all the cameras of the attached displays @@ -370,5 +430,6 @@ public: void move_clip_plane(double z) { call_cameras(&Camera::set_clip_z, z); } }; + }} // namespace Slic3r::GL -#endif // GLSCENE_HPP +#endif // SLIC3R_OCSG_EXMP_ENGINE_HPP diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index c2f8a74aa..95e00b366 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -2,13 +2,15 @@ #include #include +#include "Engine.hpp" + #include #include // For compilers that support precompilation, includes "wx/wx.h". #include #ifndef WX_PRECOMP - #include +#include #endif #include @@ -17,8 +19,7 @@ #include #include #include - -#include "GLScene.hpp" +#include #include "libslic3r/Model.hpp" #include "libslic3r/Format/3mf.hpp" @@ -29,30 +30,154 @@ using namespace Slic3r::GL; -class Canvas: public wxGLCanvas, public Slic3r::GL::Display +class Canvas: public wxGLCanvas { - std::unique_ptr m_context; + class OCSGRenderer: public Slic3r::GL::CSGDisplay { + Canvas *m_canvas; + shptr m_context; + public: + + OCSGRenderer(Canvas *c): m_canvas{c} { + auto ctx = new wxGLContext(m_canvas); + if (!ctx || !ctx->IsOK()) { + wxMessageBox("Could not create OpenGL context.", "Error", + wxOK | wxICON_ERROR); + return; + } + + m_context.reset(ctx); + } + + void set_active(long w, long h) override + { + m_canvas->SetCurrent(*m_context); + Slic3r::GL::Display::set_active(w, h); + } + + wxGLContext * context() { return m_context.get(); } + const wxGLContext * context() const { return m_context.get(); } + + void swap_buffers() override { m_canvas->SwapBuffers(); } + + ~OCSGRenderer() override { m_scene_cache.clear(); } + }; + + shptr m_ocsgdisplay = std::make_shared(this); + + shptr m_display = m_ocsgdisplay; + public: - void set_active(long w, long h) override - { - SetCurrent(*m_context); - Slic3r::GL::Display::set_active(w, h); - } - - void swap_buffers() override { SwapBuffers(); } - template Canvas(Args &&...args): wxGLCanvas(std::forward(args)...) { - auto ctx = new wxGLContext(this); - if (!ctx || !ctx->IsOK()) { - wxMessageBox("Could not create OpenGL context.", "Error", - wxOK | wxICON_ERROR); - return; + Bind(wxEVT_PAINT, [this](wxPaintEvent &) { + // This is required even though dc is not used otherwise. + wxPaintDC dc(this); + + // Set the OpenGL viewport according to the client size of this + // canvas. This is done here rather than in a wxSizeEvent handler + // because our OpenGL rendering context (and thus viewport + // setting) is used with multiple canvases: If we updated the + // viewport in the wxSizeEvent handler, changing the size of one + // canvas causes a viewport setting that is wrong when next + // another canvas is repainted. + const wxSize ClientSize = GetClientSize(); + + m_display->set_screen_size(ClientSize.x, ClientSize.y); + }); + } + + shptr get_display() const { return m_display; } + void set_display(shptr d) { m_display = d; } + + shptr get_ocsg_display() const { return m_ocsgdisplay; } +}; + +enum EEvents { LCLK_U, RCLK_U, LCLK_D, RCLK_D, DDCLK, SCRL, MV }; +struct Event +{ + EEvents type; + long a, b; + Event(EEvents t, long x = 0, long y = 0) : type{t}, a{x}, b{y} {} +}; + +class RecorderMouseInput: public MouseInput { + std::vector m_events; + bool m_recording = false, m_playing = false; + +public: + void left_click_down() override + { + if (m_recording) m_events.emplace_back(LCLK_D); + if (!m_playing) MouseInput::left_click_down(); + } + void left_click_up() override + { + if (m_recording) m_events.emplace_back(LCLK_U); + if (!m_playing) MouseInput::left_click_up(); + } + void right_click_down() override + { + if (m_recording) m_events.emplace_back(RCLK_D); + if (!m_playing) MouseInput::right_click_down(); + } + void right_click_up() override + { + if (m_recording) m_events.emplace_back(RCLK_U); + if (!m_playing) MouseInput::right_click_up(); + } + void double_click() override + { + if (m_recording) m_events.emplace_back(DDCLK); + if (!m_playing) MouseInput::double_click(); + } + void scroll(long v, long d, WheelAxis wa) override + { + if (m_recording) m_events.emplace_back(SCRL, v, d); + if (!m_playing) MouseInput::scroll(v, d, wa); + } + void move_to(long x, long y) override + { + if (m_recording) m_events.emplace_back(MV, x, y); + if (!m_playing) MouseInput::move_to(x, y); + } + + void save(std::ostream &stream) + { + for (const Event &evt : m_events) + stream << evt.type << " " << evt.a << " " << evt.b << std::endl; + } + + void load(std::istream &stream) + { + m_events.clear(); + while (stream.good()) { + int type; long a, b; + stream >> type >> a >> b; + m_events.emplace_back(EEvents(type), a, b); } - - m_context.reset(ctx); + } + + void record(bool r) { m_recording = r; if (r) m_events.clear(); } + + void play() + { + m_playing = true; + for (const Event &evt : m_events) { + switch (evt.type) { + case LCLK_U: MouseInput::left_click_up(); break; + case LCLK_D: MouseInput::left_click_down(); break; + case RCLK_U: MouseInput::right_click_up(); break; + case RCLK_D: MouseInput::right_click_down(); break; + case DDCLK: MouseInput::double_click(); break; + case SCRL: MouseInput::scroll(evt.a, evt.b, WheelAxis::waVertical); break; + case MV: MouseInput::move_to(evt.a, evt.b); break; + } + + wxSafeYield(); + } + m_playing = false; } }; @@ -63,12 +188,14 @@ class MyFrame: public wxFrame shptr m_ctl; // Controller shptr m_stbar; - uqptr m_ui_job; + + RecorderMouseInput m_mouse; class SLAJob: public Slic3r::GUI::Job { MyFrame *m_parent; std::unique_ptr m_print; std::string m_fname; + public: SLAJob(MyFrame *frame, const std::string &fname) : Slic3r::GUI::Job{frame->m_stbar} @@ -78,8 +205,9 @@ class MyFrame: public wxFrame void process() override; - protected: + const std::string & get_project_fname() const { return m_fname; } + protected: void finalize() override { m_parent->m_scene->set_print(std::move(m_print)); @@ -88,55 +216,110 @@ class MyFrame: public wxFrame } }; + uqptr m_ui_job; + + double m_fps_avg = 0.; + public: - MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size); + MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size, const Slic3r::GL::CSGSettings &settings); -private: - - void bind_canvas_events_to_controller(); - - void OnExit(wxCommandEvent& /*event*/) - { - RemoveChild(m_canvas.get()); - m_canvas->Destroy(); - Close( true ); + void load_model(const std::string &fname) { + m_ui_job = std::make_unique(this, fname); + m_ui_job->start(); } - void OnOpen(wxCommandEvent &/*evt*/) + void play_back_mouse(const std::string &events_fname) { - wxFileDialog dlg(this, "Select project file", - wxEmptyString, wxEmptyString, "*.3mf"); - - if (dlg.ShowModal() == wxID_OK) - { - m_ui_job = std::make_unique(this, dlg.GetPath().ToStdString()); - m_ui_job->start(); + std::fstream stream(events_fname, std::fstream::in); + + if (stream.good()) { + std::string model_name; + std::getline(stream, model_name); + load_model(model_name); + + int w, h; + stream >> w >> h; + SetSize(w, h); + + m_mouse.load(stream); + m_mouse.play(); } } - void OnShown(wxShowEvent&) - { - const wxSize ClientSize = GetClientSize(); - m_canvas->set_active(ClientSize.x, ClientSize.y); - - m_canvas->set_screen_size(ClientSize.x, ClientSize.y); - m_canvas->repaint(); - - // Do the repaint continuously - Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { - m_canvas->repaint(); - evt.RequestMore(); - }); - } + Canvas * canvas() { return m_canvas.get(); } + const Canvas * canvas() const { return m_canvas.get(); } + + void bind_canvas_events(MouseInput &msinput); + + double get_fps_average() const { return m_fps_avg; } }; +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" }; + class App : public wxApp { - MyFrame *m_frame; + MyFrame *m_frame = nullptr; + public: bool OnInit() override { - m_frame = new MyFrame("PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768)); - m_frame->Show( true ); + wxCmdLineParser parser(argc, argv); + + parser.AddOption("p", "play", "play back file", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption("a", "algorithm", "OpenCSG algorithm [Auto|Goldfeather|SCS]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption("d", "depth", "OpenCSG depth strategy [Off|OcclusionQuery|On]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption("o", "optimization", "OpenCSG optimization strategy [Default|ForceOn|On|Off]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption("c", "convexity", "OpenCSG convexity parameter for generic meshes", wxCMD_LINE_VAL_NUMBER, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddSwitch("", "disable-csg", "Disable csg rendering", wxCMD_LINE_PARAM_OPTIONAL); + + parser.Parse(); + + wxString fname; + bool is_play = parser.Found("play", &fname); + + wxString alg; + parser.Found("algorithm", &alg); + + wxString depth; + parser.Found("depth", &depth); + + wxString opt; + parser.Found("optimization", &opt); + + long convexity = 1; + parser.Found("convexity", &convexity); + + bool csg_off = parser.Found("disable-csg"); + + auto get_idx = [](const wxString &a, const std::vector &v) { + auto it = std::find(v.begin(), v.end(), a.ToStdString()); + return it - v.begin(); + }; + + Slic3r::GL::CSGSettings settings; + + if (auto a = get_idx(alg, CSG_ALGS) < OpenCSG::AlgorithmUnused) + settings.set_algo(OpenCSG::Algorithm(a)); + + if (auto a = get_idx(depth, CSG_DEPTH) < OpenCSG::DepthComplexityAlgorithmUnused) + settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(a)); + + if (auto a = get_idx(opt, CSG_OPT) < OpenCSG::OptimizationUnused) + settings.set_optimization(OpenCSG::Optimization(a)); + + settings.set_convexity(unsigned(convexity)); + settings.enable_csg(!csg_off); + + m_frame = new MyFrame("PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768), settings); + + if (is_play) { + m_frame->Show( true ); + m_frame->play_back_mouse(fname.ToStdString()); + m_frame->Close( true ); + std::cout << m_frame->get_fps_average() << std::endl; + + } else m_frame->Show( true ); return true; } @@ -144,7 +327,8 @@ public: wxIMPLEMENT_APP(App); -MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): +MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, + const Slic3r::GL::CSGSettings &settings): wxFrame(nullptr, wxID_ANY, title, pos, size) { wxMenu *menuFile = new wxMenu; @@ -169,7 +353,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): // glReadPixels would not return the alpha channel on NVIDIA if // not requested when the GL context is created. WX_GL_MIN_ALPHA, 8, WX_GL_DEPTH_SIZE, 8, WX_GL_STENCIL_SIZE, 8, - WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0}; + /*WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4,*/ 0}; m_scene = std::make_shared(); m_ctl = std::make_shared(); @@ -178,9 +362,13 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): m_canvas = std::make_shared(this, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE); - m_ctl->add_display(m_canvas); + m_canvas->get_ocsg_display()->apply_csgsettings(settings); + + m_ctl->add_display(m_canvas->get_display()); + wxPanel *control_panel = new wxPanel(this); + auto controlsizer = new wxBoxSizer(wxHORIZONTAL); auto slider_sizer = new wxBoxSizer(wxVERTICAL); auto console_sizer = new wxBoxSizer(wxVERTICAL); @@ -198,7 +386,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): console_sizer->Add(csg_toggle, 0, wxALL | wxEXPAND, 5); auto add_combobox = [control_panel, console_sizer] - (const wxString &label, std::vector &&list) + (const wxString &label, const std::vector &list) { auto widget = new wxComboBox(control_panel, wxID_ANY, list[0], wxDefaultPosition, wxDefaultSize, @@ -231,11 +419,21 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): auto convexity_spin = add_spinctl("Convexity", CSGSettings::DEFAULT_CONVEXITY, 0, 100); - auto alg_select = add_combobox("Algorithm", {"Auto", "Goldfeather", "SCS"}); - auto depth_select = add_combobox("Depth Complexity", {"Off", "OcclusionQuery", "On"}); - auto optimization_select = add_combobox("Optimization", { "Default", "ForceOn", "On", "Off" }); + auto alg_select = add_combobox("Algorithm", CSG_ALGS); + auto depth_select = add_combobox("Depth Complexity", CSG_DEPTH); + auto optimization_select = add_combobox("Optimization", CSG_OPT); depth_select->Disable(); + auto fpstext = new wxStaticText(control_panel, wxID_ANY, ""); + console_sizer->Add(fpstext, 0, wxALL, 5); + m_canvas->get_ocsg_display()->get_fps_counter().add_listener([this, fpstext](double fps) { + fpstext->SetLabel(wxString::Format("fps: %.2f", fps) ); + m_fps_avg = 0.9 * m_fps_avg + 0.1 * fps; + }); + + auto record_btn = new wxToggleButton(control_panel, wxID_ANY, "Record"); + console_sizer->Add(record_btn, 0, wxALL | wxEXPAND, 5); + controlsizer->Add(slider_sizer, 0, wxEXPAND); controlsizer->Add(console_sizer, 1, wxEXPAND); @@ -246,9 +444,33 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): sizer->Add(control_panel, 0, wxEXPAND); SetSizer(sizer); - Bind(wxEVT_MENU, &MyFrame::OnOpen, this, wxID_OPEN); - Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT); - Bind(wxEVT_SHOW, &MyFrame::OnShown, this, GetId()); + if (settings.get_algo() > 0) depth_select->Enable(true); + alg_select->SetSelection(settings.get_algo()); + depth_select->SetSelection(settings.get_depth_algo()); + optimization_select->SetSelection(settings.get_optimization()); + convexity_spin->SetValue(int(settings.get_convexity())); + csg_toggle->SetValue(settings.is_enabled()); + + Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &){ + RemoveChild(m_canvas.get()); + m_canvas.reset(); + Destroy(); + }); + + Bind(wxEVT_MENU, [this](wxCommandEvent &) { + wxFileDialog dlg(this, "Select project file", wxEmptyString, + wxEmptyString, "*.3mf", wxFD_OPEN|wxFD_FILE_MUST_EXIST); + + if (dlg.ShowModal() == wxID_OK) load_model(dlg.GetPath().ToStdString()); + }, wxID_OPEN); + + Bind(wxEVT_MENU, [this](wxCommandEvent &) { Close(true); }, wxID_EXIT); + + Bind(wxEVT_SHOW, [this, ms_toggle](wxShowEvent &) { + const wxSize ClientSize = GetClientSize(); + m_canvas->get_display()->set_active(ClientSize.x, ClientSize.y); + enable_multisampling(ms_toggle->GetValue()); + }); Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { m_ctl->move_clip_plane(double(slider->GetValue())); @@ -256,13 +478,13 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): ms_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ enable_multisampling(ms_toggle->GetValue()); - m_canvas->repaint(); + m_canvas->get_display()->repaint(); }); csg_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ - CSGSettings settings = m_canvas->get_csgsettings(); + CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); settings.enable_csg(csg_toggle->GetValue()); - m_canvas->apply_csgsettings(settings); + m_canvas->get_ocsg_display()->apply_csgsettings(settings); }); alg_select->Bind(wxEVT_COMBOBOX, @@ -270,89 +492,110 @@ 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_csgsettings(); + CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); settings.set_algo(OpenCSG::Algorithm(sel)); - m_canvas->apply_csgsettings(settings); + m_canvas->get_ocsg_display()->apply_csgsettings(settings); }); depth_select->Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) { int sel = depth_select->GetSelection(); - CSGSettings settings = m_canvas->get_csgsettings(); + CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); - m_canvas->apply_csgsettings(settings); + m_canvas->get_ocsg_display()->apply_csgsettings(settings); }); optimization_select->Bind(wxEVT_COMBOBOX, [this, optimization_select](wxCommandEvent &) { int sel = optimization_select->GetSelection(); - CSGSettings settings = m_canvas->get_csgsettings(); + CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); settings.set_optimization(OpenCSG::Optimization(sel)); - m_canvas->apply_csgsettings(settings); + m_canvas->get_ocsg_display()->apply_csgsettings(settings); }); convexity_spin->Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { - CSGSettings settings = m_canvas->get_csgsettings(); + CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); int c = convexity_spin->GetValue(); if (c > 0) { settings.set_convexity(unsigned(c)); - m_canvas->apply_csgsettings(settings); + m_canvas->get_ocsg_display()->apply_csgsettings(settings); } }); - bind_canvas_events_to_controller(); + record_btn->Bind(wxEVT_TOGGLEBUTTON, [this, record_btn](wxCommandEvent &) { + if (!m_ui_job) { + m_stbar->set_status_text("No project loaded!"); + return; + } + + if (record_btn->GetValue()) { + if (auto c = m_canvas->get_display()->camera()) reset(*c); + m_ctl->on_scene_updated(*m_scene); + m_mouse.record(true); + } else { + m_mouse.record(false); + wxFileDialog dlg(this, "Select output file", + wxEmptyString, wxEmptyString, "*.events", + wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + + if (dlg.ShowModal() == wxID_OK) { + std::fstream stream(dlg.GetPath().ToStdString(), + std::fstream::out); + + if (stream.good()) { + stream << m_ui_job->get_project_fname() << "\n"; + wxSize winsize = GetSize(); + stream << winsize.x << " " << winsize.y << "\n"; + m_mouse.save(stream); + } + } + } + }); + + // Do the repaint continuously + m_canvas->Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { + if (m_canvas->IsShown()) m_canvas->get_display()->repaint(); + evt.RequestMore(); + }); + + bind_canvas_events(m_mouse); } -void MyFrame::bind_canvas_events_to_controller() +void MyFrame::bind_canvas_events(MouseInput &ms) { - m_canvas->Bind(wxEVT_MOUSEWHEEL, [this](wxMouseEvent &evt) { - m_ctl->on_scroll(evt.GetWheelRotation(), evt.GetWheelDelta(), - evt.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL ? - Slic3r::GL::MouseInput::waVertical : - Slic3r::GL::MouseInput::waHorizontal); + m_canvas->Bind(wxEVT_MOUSEWHEEL, [&ms](wxMouseEvent &evt) { + ms.scroll(evt.GetWheelRotation(), evt.GetWheelDelta(), + evt.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL ? + Slic3r::GL::MouseInput::waVertical : + Slic3r::GL::MouseInput::waHorizontal); }); - m_canvas->Bind(wxEVT_MOTION, [this](wxMouseEvent &evt) { - m_ctl->on_moved_to(evt.GetPosition().x, evt.GetPosition().y); + m_canvas->Bind(wxEVT_MOTION, [&ms](wxMouseEvent &evt) { + ms.move_to(evt.GetPosition().x, evt.GetPosition().y); }); - m_canvas->Bind(wxEVT_RIGHT_DOWN, [this](wxMouseEvent & /*evt*/) { - m_ctl->on_right_click_down(); + m_canvas->Bind(wxEVT_RIGHT_DOWN, [&ms](wxMouseEvent & /*evt*/) { + ms.right_click_down(); }); - m_canvas->Bind(wxEVT_RIGHT_UP, [this](wxMouseEvent & /*evt*/) { - m_ctl->on_right_click_up(); + m_canvas->Bind(wxEVT_RIGHT_UP, [&ms](wxMouseEvent & /*evt*/) { + ms.right_click_up(); }); - m_canvas->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent & /*evt*/) { - m_ctl->on_left_click_down(); + m_canvas->Bind(wxEVT_LEFT_DOWN, [&ms](wxMouseEvent & /*evt*/) { + ms.left_click_down(); }); - m_canvas->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent & /*evt*/) { - m_ctl->on_left_click_up(); - }); - - m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent &) { - // This is required even though dc is not used otherwise. - wxPaintDC dc(this); - - // Set the OpenGL viewport according to the client size of this - // canvas. This is done here rather than in a wxSizeEvent handler - // because our OpenGL rendering context (and thus viewport setting) is - // used with multiple canvases: If we updated the viewport in the - // wxSizeEvent handler, changing the size of one canvas causes a - // viewport setting that is wrong when next another canvas is - // repainted. - const wxSize ClientSize = m_canvas->GetClientSize(); - - m_canvas->set_screen_size(ClientSize.x, ClientSize.y); - m_canvas->repaint(); + m_canvas->Bind(wxEVT_LEFT_UP, [&ms](wxMouseEvent & /*evt*/) { + ms.left_click_up(); }); + + ms.add_listener(m_ctl); } void MyFrame::SLAJob::process() { - using Status = Slic3r::PrintBase::SlicingStatus; + using SlStatus = Slic3r::PrintBase::SlicingStatus; Slic3r::DynamicPrintConfig cfg; auto model = Slic3r::Model::read_from_file(m_fname, &cfg); @@ -364,9 +607,15 @@ void MyFrame::SLAJob::process() params.to_object_step = Slic3r::slaposHollowing; m_print->set_task(params); - m_print->set_status_callback([this](const Status &status) { + m_print->set_status_callback([this](const SlStatus &status) { update_status(status.percent, status.text); }); - m_print->process(); + try { + m_print->process(); + } catch(std::exception &e) { + update_status(0, wxString("Exception during processing: ") + e.what()); + } } + +//int main() {}