From 66759e10e348c627ace37085c225113841f768e6 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 11:02:54 +0100 Subject: [PATCH 1/5] Add opencsg demo sandbox --- sandboxes/CMakeLists.txt | 3 +- sandboxes/opencsg/CMakeLists.txt | 21 ++ sandboxes/opencsg/Canvas.hpp | 112 +++++++ sandboxes/opencsg/GLScene.cpp | 509 +++++++++++++++++++++++++++++++ sandboxes/opencsg/GLScene.hpp | 321 +++++++++++++++++++ sandboxes/opencsg/main.cpp | 195 ++++++++++++ src/libslic3r/SLA/Hollowing.cpp | 14 +- src/libslic3r/SLA/Hollowing.hpp | 3 + 8 files changed, 1176 insertions(+), 2 deletions(-) create mode 100644 sandboxes/opencsg/CMakeLists.txt create mode 100644 sandboxes/opencsg/Canvas.hpp create mode 100644 sandboxes/opencsg/GLScene.cpp create mode 100644 sandboxes/opencsg/GLScene.hpp create mode 100644 sandboxes/opencsg/main.cpp diff --git a/sandboxes/CMakeLists.txt b/sandboxes/CMakeLists.txt index 91d6ca225e..181c70d48e 100644 --- a/sandboxes/CMakeLists.txt +++ b/sandboxes/CMakeLists.txt @@ -1,3 +1,4 @@ #add_subdirectory(slasupporttree) #add_subdirectory(openvdb) -add_subdirectory(meshboolean) +#add_subdirectory(meshboolean) +add_subdirectory(opencsg) diff --git a/sandboxes/opencsg/CMakeLists.txt b/sandboxes/opencsg/CMakeLists.txt new file mode 100644 index 0000000000..9a216a7dc1 --- /dev/null +++ b/sandboxes/opencsg/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.0) + +project(OpenCSG-example) + +add_executable(opencsg_example main.cpp GLScene.hpp GLScene.cpp Canvas.hpp + ${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) + +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) diff --git a/sandboxes/opencsg/Canvas.hpp b/sandboxes/opencsg/Canvas.hpp new file mode 100644 index 0000000000..85d490ddf5 --- /dev/null +++ b/sandboxes/opencsg/Canvas.hpp @@ -0,0 +1,112 @@ +#ifndef CANVAS_HPP +#define CANVAS_HPP + +#include + +// For compilers that support precompilation, includes "wx/wx.h". +#include +#ifndef WX_PRECOMP +#include +#endif + +#include +#include + +#include "GLScene.hpp" + +namespace Slic3r { namespace GL { + +class Canvas: public wxGLCanvas, public Slic3r::GL::Display +{ + std::unique_ptr m_context; +public: + + void set_active(long w, long h) override + { + SetCurrent(*m_context); + Slic3r::GL::Display::set_active(w, h); + } + + void repaint(long width, long height) override + { + Slic3r::GL::Display::repaint(width, height); + } + + using Slic3r::GL::Display::repaint; + + void swap_buffers() override { SwapBuffers(); } + + void on_scroll(long v, long d, Slic3r::GL::MouseInput::WheelAxis wa) override + { + Slic3r::GL::Display::on_scroll(v, d, wa); + } + + 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; + } + + m_context.reset(ctx); + + Bind( + wxEVT_MOUSEWHEEL, + [this](wxMouseEvent &evt) { + on_scroll(evt.GetWheelRotation(), evt.GetWheelDelta(), + evt.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL ? + Slic3r::GL::MouseInput::waVertical : + Slic3r::GL::MouseInput::waHorizontal); + }, + GetId()); + + Bind( + wxEVT_MOTION, + [this](wxMouseEvent &evt) { + on_moved_to(evt.GetPosition().x, evt.GetPosition().y); + }, + GetId()); + + Bind( + wxEVT_RIGHT_DOWN, + [this](wxMouseEvent & /*evt*/) { on_right_click_down(); }, + GetId()); + + Bind( + wxEVT_RIGHT_UP, + [this](wxMouseEvent & /*evt*/) { on_right_click_up(); }, + GetId()); + + Bind( + wxEVT_LEFT_DOWN, + [this](wxMouseEvent & /*evt*/) { on_left_click_down(); }, + GetId()); + + Bind( + wxEVT_LEFT_UP, + [this](wxMouseEvent & /*evt*/) { on_left_click_up(); }, + GetId()); + + 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(); + repaint(ClientSize.x, ClientSize.y); + }, GetId()); + } +}; + +}} // namespace Slic3r::GL + +#endif // CANVAS_HPP diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp new file mode 100644 index 0000000000..5f4a205327 --- /dev/null +++ b/sandboxes/opencsg/GLScene.cpp @@ -0,0 +1,509 @@ +#include "GLScene.hpp" +#include +#include +#include + +#include + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#include + +#ifndef NDEBUG +#define HAS_GLSAFE +#endif + +#ifdef HAS_GLSAFE +extern void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char *function_name); +inline void glAssertRecentCall() { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } +#define glsafe(cmd) do { cmd; glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false) +#define glcheck() do { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false) + +void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char *function_name) +{ + GLenum err = glGetError(); + if (err == GL_NO_ERROR) + return; + const char *sErr = 0; + switch (err) { + case GL_INVALID_ENUM: sErr = "Invalid Enum"; break; + case GL_INVALID_VALUE: sErr = "Invalid Value"; break; + // be aware that GL_INVALID_OPERATION is generated if glGetError is executed between the execution of glBegin and the corresponding execution of glEnd + case GL_INVALID_OPERATION: sErr = "Invalid Operation"; break; + case GL_STACK_OVERFLOW: sErr = "Stack Overflow"; break; + case GL_STACK_UNDERFLOW: sErr = "Stack Underflow"; break; + case GL_OUT_OF_MEMORY: sErr = "Out Of Memory"; break; + default: sErr = "Unknown"; break; + } + BOOST_LOG_TRIVIAL(error) << "OpenGL error in " << file_name << ":" << line << ", function " << function_name << "() : " << (int)err << " - " << sErr; + assert(false); +} + +#else +inline void glAssertRecentCall() { } +#define glsafe(cmd) cmd +#define glcheck() +#endif + +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; icsg_primitives()); + + glDepthFunc(GL_EQUAL); + for (auto& p : m_scene->csg_primitives()) p->render(); + glDepthFunc(GL_LESS); + + for (auto& p : m_scene->free_primitives()) p->render(); + + glFlush(); +} + +template::value_type> +std::vector transform_pts( + It from, It to, Trafo &&tr, GetPt &&point) +{ + auto ret = reserve_vector(to - from); + for(auto it = from; it != to; ++it) { + V v = *it; + v.pos = tr * point(*it); + ret.emplace_back(std::move(v)); + } + return ret; +} + +void Scene::set_print(uqptr &&print) +{ + m_print = std::move(print); + + for (const SLAPrintObject *po : m_print->objects()) { + const ModelObject *mo = po->model_object(); + TriangleMesh msh = mo->raw_mesh(); + + sla::DrainHoles holedata = mo->sla_drain_holes; + + for (const ModelInstance *mi : mo->instances) { + + TriangleMesh mshinst = msh; + auto interior = po->hollowed_interior_mesh(); + interior.transform(po->trafo().inverse()); + + mshinst.merge(interior); + mshinst.require_shared_vertices(); + + mi->transform_mesh(&mshinst); + + auto bb = mshinst.bounding_box(); + auto center = bb.center().cast(); + mshinst.translate(-center); + + mshinst.require_shared_vertices(); + add_mesh(mshinst, OpenCSG::Intersection, 15); + + auto tr = Transform3f::Identity(); + tr.translate(-center); + + transform_pts(holedata.begin(), holedata.end(), tr, + [](const sla::DrainHole &dh) { + return dh.pos; + }); + + transform_pts(holedata.begin(), holedata.end(), tr, + [](const sla::DrainHole &dh) { + return dh.normal; + }); + } + + for (const sla::DrainHole &holept : holedata) { + TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh()); + holemesh.require_shared_vertices(); + add_mesh(holemesh, OpenCSG::Subtraction, 1); + } + } + + // Notify displays + call(&Display::on_scene_updated, m_displays); +} + +BoundingBoxf3 Scene::get_bounding_box() const +{ + return m_print->model().bounding_box(); +} + +shptr Scene::add_mesh(const TriangleMesh &mesh) +{ + auto p = std::make_shared(); + p->load_mesh(mesh); + m_primitives.emplace_back(p); + m_primitives_free.emplace_back(p.get()); + return p; +} + +shptr Scene::add_mesh(const TriangleMesh &mesh, OpenCSG::Operation o, unsigned c) +{ + auto p = std::make_shared(o, c); + p->load_mesh(mesh); + m_primitives.emplace_back(p); + m_primitives_csg.emplace_back(p.get()); + return p; +} + +void IndexedVertexArray::push_geometry(float x, float y, float z, float nx, float ny, float nz) +{ + assert(this->vertices_and_normals_interleaved_VBO_id == 0); + if (this->vertices_and_normals_interleaved_VBO_id != 0) + return; + + if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity()) + this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6)); + this->vertices_and_normals_interleaved.emplace_back(nx); + this->vertices_and_normals_interleaved.emplace_back(ny); + this->vertices_and_normals_interleaved.emplace_back(nz); + this->vertices_and_normals_interleaved.emplace_back(x); + this->vertices_and_normals_interleaved.emplace_back(y); + this->vertices_and_normals_interleaved.emplace_back(z); + + this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size(); +} + +void IndexedVertexArray::push_triangle(int idx1, int idx2, int idx3) { + assert(this->vertices_and_normals_interleaved_VBO_id == 0); + if (this->vertices_and_normals_interleaved_VBO_id != 0) + return; + + if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity()) + this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3)); + this->triangle_indices.emplace_back(idx1); + this->triangle_indices.emplace_back(idx2); + this->triangle_indices.emplace_back(idx3); + this->triangle_indices_size = this->triangle_indices.size(); +} + +void IndexedVertexArray::load_mesh(const TriangleMesh &mesh) +{ + assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0); + assert(quad_indices.empty() && triangle_indices_size == 0); + assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size()); + + this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count()); + + int vertices_count = 0; + for (size_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) { + const stl_facet &facet = mesh.stl.facet_start[i]; + for (int j = 0; j < 3; ++j) + this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); + + this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2); + vertices_count += 3; + } +} + +void IndexedVertexArray::finalize_geometry() +{ + assert(this->vertices_and_normals_interleaved_VBO_id == 0); + assert(this->triangle_indices_VBO_id == 0); + assert(this->quad_indices_VBO_id == 0); + + if (!this->vertices_and_normals_interleaved.empty()) { + glsafe( + ::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, + this->vertices_and_normals_interleaved_VBO_id)); + glsafe( + ::glBufferData(GL_ARRAY_BUFFER, + GLsizeiptr( + this->vertices_and_normals_interleaved.size() * + 4), + this->vertices_and_normals_interleaved.data(), + GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + this->vertices_and_normals_interleaved.clear(); + } + if (!this->triangle_indices.empty()) { + glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + this->triangle_indices_VBO_id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, + GLsizeiptr(this->triangle_indices.size() * 4), + this->triangle_indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + this->triangle_indices.clear(); + } + if (!this->quad_indices.empty()) { + glsafe(::glGenBuffers(1, &this->quad_indices_VBO_id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + this->quad_indices_VBO_id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, + GLsizeiptr(this->quad_indices.size() * 4), + this->quad_indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + this->quad_indices.clear(); + } +} + +void IndexedVertexArray::release_geometry() +{ + if (this->vertices_and_normals_interleaved_VBO_id) { + glsafe( + ::glDeleteBuffers(1, + &this->vertices_and_normals_interleaved_VBO_id)); + this->vertices_and_normals_interleaved_VBO_id = 0; + } + if (this->triangle_indices_VBO_id) { + glsafe(::glDeleteBuffers(1, &this->triangle_indices_VBO_id)); + this->triangle_indices_VBO_id = 0; + } + if (this->quad_indices_VBO_id) { + glsafe(::glDeleteBuffers(1, &this->quad_indices_VBO_id)); + this->quad_indices_VBO_id = 0; + } + this->clear(); +} + +void IndexedVertexArray::render() const +{ + assert(this->vertices_and_normals_interleaved_VBO_id != 0); + assert(this->triangle_indices_VBO_id != 0 || + this->quad_indices_VBO_id != 0); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, + this->vertices_and_normals_interleaved_VBO_id)); + glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), + reinterpret_cast(3 * sizeof(float)))); + glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr)); + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + + // Render using the Vertex Buffer Objects. + if (this->triangle_indices_size > 0) { + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + this->triangle_indices_VBO_id)); + glsafe(::glDrawElements(GL_TRIANGLES, + GLsizei(this->triangle_indices_size), + GL_UNSIGNED_INT, nullptr)); + glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } + if (this->quad_indices_size > 0) { + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + this->quad_indices_VBO_id)); + glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), + GL_UNSIGNED_INT, nullptr)); + glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +} + +void IndexedVertexArray::clear() { + this->vertices_and_normals_interleaved.clear(); + this->triangle_indices.clear(); + this->quad_indices.clear(); + vertices_and_normals_interleaved_size = 0; + triangle_indices_size = 0; + quad_indices_size = 0; +} + +void IndexedVertexArray::shrink_to_fit() { + this->vertices_and_normals_interleaved.shrink_to_fit(); + this->triangle_indices.shrink_to_fit(); + this->quad_indices.shrink_to_fit(); +} + +void Primitive::render() +{ + glsafe(::glPushMatrix()); + glsafe(::glMultMatrixd(m_trafo.get_matrix().data())); + m_geom.render(); + glsafe(::glPopMatrix()); +} + +void Display::clear_screen() +{ + glViewport(0, 0, GLsizei(m_size.x()), GLsizei(m_size.y())); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +void Display::set_active(long width, long height) +{ + static int argc = 0; + + 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 + GLfloat light_position1[] = { 1.0f, 1.0f, 1.0f, 0.0f}; // Infinite light location + + glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); + glLightfv(GL_LIGHT0, GL_POSITION, light_position0); + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse); + glLightfv(GL_LIGHT1, GL_POSITION, light_position1); + glEnable(GL_LIGHT1); + glEnable(GL_LIGHTING); + glEnable(GL_NORMALIZE); + + // Use depth buffering for hidden surface elimination + glEnable(GL_DEPTH_TEST); + glEnable(GL_STENCIL_TEST); + + m_camera->set_screen(width, height); +} + +void Display::repaint(long width, long height) +{ + if (m_size.x() != width || m_size.y() != height) + m_camera->set_screen(width, height); + + m_size = {width, height}; + + clear_screen(); + + m_camera->view(); + render_scene(); + + renderfps(); + + swap_buffers(); +} + +void Display::on_scroll(long v, long d, MouseInput::WheelAxis wa) +{ + m_wheel_pos += v / d; + + m_camera->set_zoom(m_wheel_pos); + + m_scene->on_scroll(v, d, wa); + + repaint(m_size.x(), m_size.y()); +} + +void Display::on_moved_to(long x, long y) +{ + if (m_left_btn) { + m_camera->rotate((Vec2i{x, y} - m_mouse_pos).cast()); + repaint(); + } + m_mouse_pos = {x, y}; +} + +void CSGSettings::set_csg_algo(OpenCSG::Algorithm alg) { m_csgalg = alg; } + +void Display::on_scene_updated() +{ + auto bb = m_scene->get_bounding_box(); + double d = std::max(std::max(bb.size().x(), bb.size().y()), bb.size().z()); + m_wheel_pos = long(2 * d); + m_camera->set_zoom(m_wheel_pos); + repaint(); +} + +void Display::set_scene(shptr scene) +{ + m_scene = scene; + m_scene->add_display(shared_from_this()); +} + +void Camera::view() +{ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt(0.0, m_zoom, 0.0, /* eye is at (0,zoom,0) */ + m_referene.x(), m_referene.y(), m_referene.z(), + 0.0, 0.0, 1.0); /* up is in positive Y direction */ + + // TODO Could have been set in prevoius gluLookAt in first argument + glRotatef(m_rot.y(), 1.0, 0.0, 0.0); + glRotatef(m_rot.x(), 0.0, 0.0, 1.0); + + // glClipPlane() +} + +void PerspectiveCamera::set_screen(long width, long height) +{ + // Setup the view of the CSG shape + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45.0, width / double(height), .1, 200.0); + glMatrixMode(GL_MODELVIEW); +} + +bool enable_multisampling(bool e) +{ + if (!e) { glDisable(GL_MULTISAMPLE); return false; } + + GLint is_ms_context; + glGetIntegerv(GL_SAMPLE_BUFFERS, &is_ms_context); + + if (is_ms_context) { glEnable(GL_MULTISAMPLE); return true; } + else return false; +} + +}} // namespace Slic3r::GL diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/GLScene.hpp new file mode 100644 index 0000000000..411b0db71d --- /dev/null +++ b/sandboxes/opencsg/GLScene.hpp @@ -0,0 +1,321 @@ +#ifndef GLSCENE_HPP +#define GLSCENE_HPP + +#include +#include + +#include +#include +#include +#include +#include + +namespace Slic3r { + +class SLAPrint; + +namespace GL { + +template using shptr = std::shared_ptr; +template using uqptr = std::unique_ptr; +template using wkptr = std::weak_ptr; + +template> +using Collection = std::vector; + +template void cleanup(Collection> &listeners) { + auto it = std::remove_if(listeners.begin(), listeners.end(), + [](auto &l) { return !l.lock(); }); + listeners.erase(it, listeners.end()); +} + +template +void call(F &&f, Collection> &listeners, Args&&... args) { + for (auto &l : listeners) + if (auto p = l.lock()) ((p.get())->*f)(std::forward(args)...); +} + +class MouseInput +{ +public: + + enum WheelAxis { + waVertical, waHorizontal + }; + + class Listener { + public: + + virtual ~Listener() = default; + + 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: + Collection> 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(shptr listener) + { + m_listeners.emplace_back(listener); + cleanup(m_listeners); + } +}; + +class IndexedVertexArray { +public: + ~IndexedVertexArray() { release_geometry(); } + + // Vertices and their normals, interleaved to be used by void + // glInterleavedArrays(GL_N3F_V3F, 0, x) + Collection vertices_and_normals_interleaved; + Collection triangle_indices; + Collection 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(); +}; + +bool enable_multisampling(bool e = true); +void renderfps(); + +class Primitive : public OpenCSG::Primitive +{ + IndexedVertexArray m_geom; + Geometry::Transformation m_trafo; +public: + + using OpenCSG::Primitive::Primitive; + + Primitive() : OpenCSG::Primitive(OpenCSG::Intersection, 1) {} + + 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(); + } +}; + +class 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; } +}; + +class PerspectiveCamera: public Camera { +public: + + void set_screen(long width, long height) override; +}; + +class CSGSettings { + OpenCSG::Algorithm m_csgalg = OpenCSG::Algorithm::Automatic; +public: + void set_csg_algo(OpenCSG::Algorithm alg); +}; + +class Display : public std::enable_shared_from_this, + public MouseInput::Listener +{ +protected: + shptr m_scene; + long m_wheel_pos = 0; + Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev; + Vec2i m_size; + bool m_initialized = false, m_left_btn = false, m_right_btn = false; + + CSGSettings m_csgsettings; + + shptr m_camera; + +public: + Display(shptr scene = nullptr, shptr camera = nullptr) + : m_scene(scene) + , m_camera(camera ? camera : std::make_shared()) + {} + + virtual void swap_buffers() = 0; + + virtual void set_active(long width, long height); + + virtual void repaint(long width, long height); + void repaint() { repaint(m_size.x(), m_size.y()); } + + void set_scene(shptr scene); + shptr get_scene() { return m_scene; } + + bool is_initialized() const { return m_initialized; } + + void on_scroll(long v, long d, MouseInput::WheelAxis wa) override; + void on_moved_to(long x, long y) 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 move_clip_plane(double z) { m_camera->set_clip_z(z); } + + const CSGSettings & csgsettings() const { return m_csgsettings; } + void csgsettings(const CSGSettings &settings) { m_csgsettings = settings; } + + virtual void on_scene_updated(); + virtual void clear_screen(); + virtual void render_scene(); +}; + +class Scene: public MouseInput::Listener +{ + Collection> m_primitives; + Collection m_primitives_free; + Collection m_primitives_csg; + + uqptr m_print; +public: + + Scene(); + ~Scene(); + + const Collection& free_primitives() const + { + return m_primitives_free; + } + + const Collection& csg_primitives() const + { + return m_primitives_csg; + } + + void add_display(shptr disp) + { + m_displays.emplace_back(disp); + cleanup(m_displays); + } + + void set_print(uqptr &&print); + + BoundingBoxf3 get_bounding_box() const; + +protected: + + shptr add_mesh(const TriangleMesh &mesh); + shptr add_mesh(const TriangleMesh &mesh, + OpenCSG::Operation op, + unsigned covexity); + +private: + + Collection> m_displays; +}; + +}} // namespace Slic3r::GL +#endif // GLSCENE_HPP diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp new file mode 100644 index 0000000000..f2e9dc6c1b --- /dev/null +++ b/sandboxes/opencsg/main.cpp @@ -0,0 +1,195 @@ +#include +#include +#include + +#include + +#include +// For compilers that support precompilation, includes "wx/wx.h". +#include +#ifndef WX_PRECOMP + #include +#endif + +#include +#include +#include +#include + +#include "Canvas.hpp" +#include "GLScene.hpp" + +#include "libslic3r/Model.hpp" +#include "libslic3r/Format/3mf.hpp" +#include "libslic3r/SLAPrint.hpp" + +#include "slic3r/GUI/Job.hpp" +#include "slic3r/GUI/ProgressStatusBar.hpp" +//#include "slic3r/GUI/3DEngine.hpp" + +using namespace Slic3r::GL; + +class MyFrame: public wxFrame +{ + std::shared_ptr m_canvas; + std::shared_ptr m_stbar; + std::unique_ptr m_ui_job; + + 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} + , m_parent{frame} + , m_fname{fname} + { + } + + void process() override + { + using Status = Slic3r::PrintBase::SlicingStatus; + + Slic3r::DynamicPrintConfig cfg; + auto model = Slic3r::Model::read_from_file(m_fname, &cfg); + + m_print = std::make_unique(); + m_print->apply(model, cfg); + + m_print->set_status_callback([this](const Status &status) { + update_status(status.percent, status.text); + }); + + m_print->process(); + } + + protected: + + void finalize() override + { + m_parent->m_canvas->get_scene()->set_print(std::move(m_print)); + m_parent->m_stbar->set_status_text( + wxString::Format("Model %s loaded.", m_fname)); + } + }; + +public: + MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size): + wxFrame(nullptr, wxID_ANY, title, pos, size) + { + wxMenu *menuFile = new wxMenu; + menuFile->Append(wxID_OPEN); + menuFile->Append(wxID_EXIT); + wxMenuBar *menuBar = new wxMenuBar; + menuBar->Append( menuFile, "&File" ); + SetMenuBar( menuBar ); + + m_stbar = std::make_shared(this); + m_stbar->embed(this); + + SetStatusText( "Welcome to wxWidgets!" ); + + int attribList[] = + {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, + // RGB channels each should be allocated with 8 bit depth. One + // should almost certainly get these bit depths by default. + WX_GL_MIN_RED, 8, WX_GL_MIN_GREEN, 8, WX_GL_MIN_BLUE, 8, + // Requesting an 8 bit alpha channel. Interestingly, the NVIDIA + // drivers would most likely work with some alpha plane, but + // 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}; + + m_canvas = std::make_shared(this, wxID_ANY, attribList, + wxDefaultPosition, wxDefaultSize, + wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE); + + wxPanel *control_panel = new wxPanel(this); + auto controlsizer = new wxBoxSizer(wxHORIZONTAL); + auto slider_sizer = new wxBoxSizer(wxVERTICAL); + auto console_sizer = new wxBoxSizer(wxVERTICAL); + + auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL); + auto toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); + wxString algorithms [] = {"Default", "Different"}; + auto alg_select = new wxComboBox(control_panel, wxID_ANY, "Default", wxDefaultPosition, wxDefaultSize, 2, algorithms); + + slider_sizer->Add(slider, 1, wxEXPAND); + console_sizer->Add(toggle, 0, wxALL, 5); + console_sizer->Add(alg_select, 0, wxALL, 5); + controlsizer->Add(slider_sizer, 0, wxEXPAND); + controlsizer->Add(console_sizer, 1, wxEXPAND); + control_panel->SetSizer(controlsizer); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(m_canvas.get(), 1, wxEXPAND); + 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()); + Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { + m_canvas->move_clip_plane(double(slider->GetValue())); + }, slider->GetId()); + + Bind(wxEVT_TOGGLEBUTTON, [this, toggle](wxCommandEvent &){ + enable_multisampling(toggle->GetValue()); + m_canvas->repaint(); + }, toggle->GetId()); + + m_canvas->set_scene(std::make_shared()); + } + +private: + + void OnExit(wxCommandEvent& /*event*/) + { + RemoveChild(m_canvas.get()); + m_canvas->Destroy(); + Close( true ); + } + + void OnOpen(wxCommandEvent &/*evt*/) + { + 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(); + } + } + + void OnShown(wxShowEvent&) + { + const wxSize ClientSize = GetClientSize(); + m_canvas->set_active(ClientSize.x, ClientSize.y); + + m_canvas->repaint(ClientSize.x, ClientSize.y); + + // Do the repaint continuously + Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { + m_canvas->repaint(); + evt.RequestMore(); + }); + } +}; + +class App : public wxApp { + MyFrame *m_frame; +public: + bool OnInit() override { + + m_frame = new MyFrame( "PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768) ); + m_frame->Show( true ); + + return true; + } +}; + +wxIMPLEMENT_APP(App); diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 46e60f91cb..2b35722478 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -120,6 +121,17 @@ std::unique_ptr generate_interior(const TriangleMesh & mesh, hc.closing_distance)); } +Contour3D DrainHole::to_mesh() const +{ + auto r = double(radius); + auto h = double(height); + sla::Contour3D hole = sla::cylinder(r, h); + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, normal.cast()); + for(auto& p : hole.points) p = q * p + pos.cast(); + return hole; +} + bool DrainHole::operator==(const DrainHole &sp) const { return (pos == sp.pos) && (normal == sp.normal) && @@ -131,7 +143,7 @@ bool DrainHole::is_inside(const Vec3f& pt) const { Eigen::Hyperplane plane(normal, pos); float dist = plane.signedDistance(pt); - if (dist < EPSILON || dist > height) + if (dist < float(EPSILON) || dist > height) return false; Eigen::ParametrizedLine axis(pos, normal); diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index d5c0d49fc8..ba1eb2d622 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -3,6 +3,7 @@ #include #include +#include #include namespace Slic3r { @@ -42,6 +43,8 @@ struct DrainHole bool get_intersections(const Vec3f& s, const Vec3f& dir, std::array, 2>& out) const; + Contour3D to_mesh() const; + template inline void serialize(Archive &ar) { ar(pos, normal, radius, height); From b1186e339dd7b756d5995f2e74de7fdc6144d696 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 12:36:44 +0100 Subject: [PATCH 2/5] Added opencsg parameter console --- sandboxes/opencsg/GLScene.cpp | 9 +++++- sandboxes/opencsg/GLScene.hpp | 15 +++++++-- sandboxes/opencsg/main.cpp | 59 +++++++++++++++++++++++++++++------ 3 files changed, 70 insertions(+), 13 deletions(-) diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp index 5f4a205327..60eb0ce03b 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/GLScene.cpp @@ -454,7 +454,14 @@ void Display::on_moved_to(long x, long y) m_mouse_pos = {x, y}; } -void CSGSettings::set_csg_algo(OpenCSG::Algorithm alg) { m_csgalg = alg; } +void Display::apply_csgsettings(const CSGSettings &settings) +{ + using namespace OpenCSG; + m_csgsettings = settings; + setOption(AlgorithmSetting, m_csgsettings.get_algo()); + setOption(DepthComplexitySetting, m_csgsettings.get_depth_algo()); + setOption(DepthBoundsOptimization, m_csgsettings.get_optimization()); +} void Display::on_scene_updated() { diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/GLScene.hpp index 411b0db71d..6beecb79a6 100644 --- a/sandboxes/opencsg/GLScene.hpp +++ b/sandboxes/opencsg/GLScene.hpp @@ -220,8 +220,17 @@ public: class CSGSettings { OpenCSG::Algorithm m_csgalg = OpenCSG::Algorithm::Automatic; + OpenCSG::DepthComplexityAlgorithm m_depth_algo = OpenCSG::DepthComplexityAlgorithm::NoDepthComplexitySampling; + OpenCSG::Optimization m_optim = OpenCSG::Optimization::OptimizationDefault; public: - void set_csg_algo(OpenCSG::Algorithm alg); + int get_algo() const { return int(m_csgalg); } + void set_algo(OpenCSG::Algorithm alg) { m_csgalg = alg; } + + int get_depth_algo() const { return int(m_depth_algo); } + void set_depth_algo(OpenCSG::DepthComplexityAlgorithm alg) { m_depth_algo = alg; } + + int get_optimization() const { return int(m_optim); } + void set_optimization(OpenCSG::Optimization o) { m_optim = o; } }; class Display : public std::enable_shared_from_this, @@ -265,8 +274,8 @@ public: void move_clip_plane(double z) { m_camera->set_clip_z(z); } - const CSGSettings & csgsettings() const { return m_csgsettings; } - void csgsettings(const CSGSettings &settings) { m_csgsettings = settings; } + const CSGSettings & get_csgsettings() const { return m_csgsettings; } + void apply_csgsettings(const CSGSettings &settings); virtual void on_scene_updated(); virtual void clear_screen(); diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index f2e9dc6c1b..8c106fa85c 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -100,8 +100,8 @@ public: // drivers would most likely work with some alpha plane, but // 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_MIN_ALPHA, 8, WX_GL_DEPTH_SIZE, 8, WX_GL_STENCIL_SIZE, 8, + WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0}; m_canvas = std::make_shared(this, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, @@ -111,17 +111,33 @@ public: auto controlsizer = new wxBoxSizer(wxHORIZONTAL); auto slider_sizer = new wxBoxSizer(wxVERTICAL); auto console_sizer = new wxBoxSizer(wxVERTICAL); - + auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL); - auto toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); - wxString algorithms [] = {"Default", "Different"}; - auto alg_select = new wxComboBox(control_panel, wxID_ANY, "Default", wxDefaultPosition, wxDefaultSize, 2, algorithms); - slider_sizer->Add(slider, 1, wxEXPAND); - console_sizer->Add(toggle, 0, wxALL, 5); - console_sizer->Add(alg_select, 0, wxALL, 5); + + auto toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); + console_sizer->Add(toggle, 0, wxALL | wxEXPAND, 5); + + auto add_combobox = [control_panel, console_sizer] + (const wxString &label, std::vector &&list) { + auto widget = new wxComboBox(control_panel, wxID_ANY, list[0], + wxDefaultPosition, wxDefaultSize, + int(list.size()), list.data()); + auto sz = new wxBoxSizer(wxHORIZONTAL); + sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, wxALL | wxALIGN_CENTER, 5); + sz->Add(widget, 1, wxALL | wxEXPAND, 5); + console_sizer->Add(sz, 0, wxEXPAND); + return widget; + }; + + auto alg_select = add_combobox("Algorithm", {"Auto", "Goldfeather", "SCS"}); + auto depth_select = add_combobox("Depth Complexity", {"Off", "OcclusionQuery", "On"}); + depth_select->Disable(); + auto optimization_select = add_combobox("Optimization", { "Default", "ForceOn", "On", "Off" }); + controlsizer->Add(slider_sizer, 0, wxEXPAND); controlsizer->Add(console_sizer, 1, wxEXPAND); + control_panel->SetSizer(controlsizer); auto sizer = new wxBoxSizer(wxHORIZONTAL); @@ -141,6 +157,31 @@ public: m_canvas->repaint(); }, toggle->GetId()); + Bind(wxEVT_COMBOBOX, [this, alg_select, depth_select](wxCommandEvent &) + { + int sel = alg_select->GetSelection(); + depth_select->Enable(sel > 0); + CSGSettings settings = m_canvas->get_csgsettings(); + settings.set_algo(OpenCSG::Algorithm(sel)); + m_canvas->apply_csgsettings(settings); + }, alg_select->GetId()); + + Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) + { + int sel = depth_select->GetSelection(); + CSGSettings settings = m_canvas->get_csgsettings(); + settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); + m_canvas->apply_csgsettings(settings); + }, depth_select->GetId()); + + Bind(wxEVT_COMBOBOX, [this, optimization_select](wxCommandEvent &) + { + int sel = optimization_select->GetSelection(); + CSGSettings settings = m_canvas->get_csgsettings(); + settings.set_optimization(OpenCSG::Optimization(sel)); + m_canvas->apply_csgsettings(settings); + }, depth_select->GetId()); + m_canvas->set_scene(std::make_shared()); } From 878f8a8ead6ffa4b53e77b3dd7ff226dc74fe0b1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 14:04:26 +0100 Subject: [PATCH 3/5] Add convexity to csgsettings. Defer all rendering to Display. --- sandboxes/opencsg/GLScene.cpp | 143 ++++++++++++++++++++-------------- sandboxes/opencsg/GLScene.hpp | 51 ++++++------ sandboxes/opencsg/main.cpp | 65 +++++++++++++--- 3 files changed, 169 insertions(+), 90 deletions(-) diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp index 60eb0ce03b..5037ef6a54 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/GLScene.cpp @@ -99,13 +99,15 @@ void Display::render_scene() GLfloat color[] = {1.f, 1.f, 0.f, 0.f}; glsafe(::glColor4fv(color)); - OpenCSG::render(m_scene->csg_primitives()); + if (m_csgsettings.is_enabled()) { + OpenCSG::render(m_scene_cache.primitives_csg); + glDepthFunc(GL_EQUAL); + } - glDepthFunc(GL_EQUAL); - for (auto& p : m_scene->csg_primitives()) p->render(); - glDepthFunc(GL_LESS); + for (auto& p : m_scene_cache.primitives_csg) p->render(); + if (m_csgsettings.is_enabled()) glDepthFunc(GL_LESS); - for (auto& p : m_scene->free_primitives()) p->render(); + for (auto& p : m_scene_cache.primitives_free) p->render(); glFlush(); } @@ -127,53 +129,8 @@ std::vector transform_pts( } void Scene::set_print(uqptr &&print) -{ +{ m_print = std::move(print); - - for (const SLAPrintObject *po : m_print->objects()) { - const ModelObject *mo = po->model_object(); - TriangleMesh msh = mo->raw_mesh(); - - sla::DrainHoles holedata = mo->sla_drain_holes; - - for (const ModelInstance *mi : mo->instances) { - - TriangleMesh mshinst = msh; - auto interior = po->hollowed_interior_mesh(); - interior.transform(po->trafo().inverse()); - - mshinst.merge(interior); - mshinst.require_shared_vertices(); - - mi->transform_mesh(&mshinst); - - auto bb = mshinst.bounding_box(); - auto center = bb.center().cast(); - mshinst.translate(-center); - - mshinst.require_shared_vertices(); - add_mesh(mshinst, OpenCSG::Intersection, 15); - - auto tr = Transform3f::Identity(); - tr.translate(-center); - - transform_pts(holedata.begin(), holedata.end(), tr, - [](const sla::DrainHole &dh) { - return dh.pos; - }); - - transform_pts(holedata.begin(), holedata.end(), tr, - [](const sla::DrainHole &dh) { - return dh.normal; - }); - } - - for (const sla::DrainHole &holept : holedata) { - TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh()); - holemesh.require_shared_vertices(); - add_mesh(holemesh, OpenCSG::Subtraction, 1); - } - } // Notify displays call(&Display::on_scene_updated, m_displays); @@ -184,21 +141,30 @@ BoundingBoxf3 Scene::get_bounding_box() const return m_print->model().bounding_box(); } -shptr Scene::add_mesh(const TriangleMesh &mesh) +void Display::SceneCache::clear() +{ + primitives_csg.clear(); + primitives_free.clear(); + primitives.clear(); +} + +shptr Display::SceneCache::add_mesh(const TriangleMesh &mesh) { auto p = std::make_shared(); p->load_mesh(mesh); - m_primitives.emplace_back(p); - m_primitives_free.emplace_back(p.get()); + primitives.emplace_back(p); + primitives_free.emplace_back(p.get()); return p; } -shptr Scene::add_mesh(const TriangleMesh &mesh, OpenCSG::Operation o, unsigned c) +shptr Display::SceneCache::add_mesh(const TriangleMesh &mesh, + OpenCSG::Operation o, + unsigned c) { auto p = std::make_shared(o, c); p->load_mesh(mesh); - m_primitives.emplace_back(p); - m_primitives_csg.emplace_back(p.get()); + primitives.emplace_back(p); + primitives_csg.emplace_back(p.get()); return p; } @@ -457,18 +423,79 @@ void Display::on_moved_to(long x, long y) void Display::apply_csgsettings(const CSGSettings &settings) { using namespace OpenCSG; + + bool update = m_csgsettings.get_convexity() != settings.get_convexity(); + m_csgsettings = settings; setOption(AlgorithmSetting, m_csgsettings.get_algo()); setOption(DepthComplexitySetting, m_csgsettings.get_depth_algo()); setOption(DepthBoundsOptimization, m_csgsettings.get_optimization()); + + if (update) on_scene_updated(); + + repaint(); } void Display::on_scene_updated() { + const SLAPrint *print = m_scene->get_print(); + if (!print) return; + + { auto bb = m_scene->get_bounding_box(); double d = std::max(std::max(bb.size().x(), bb.size().y()), bb.size().z()); m_wheel_pos = long(2 * d); m_camera->set_zoom(m_wheel_pos); + } + + m_scene_cache.clear(); + + for (const SLAPrintObject *po : print->objects()) { + const ModelObject *mo = po->model_object(); + TriangleMesh msh = mo->raw_mesh(); + + sla::DrainHoles holedata = mo->sla_drain_holes; + + for (const ModelInstance *mi : mo->instances) { + + TriangleMesh mshinst = msh; + auto interior = po->hollowed_interior_mesh(); + interior.transform(po->trafo().inverse()); + + mshinst.merge(interior); + mshinst.require_shared_vertices(); + + mi->transform_mesh(&mshinst); + + auto bb = mshinst.bounding_box(); + auto center = bb.center().cast(); + mshinst.translate(-center); + + mshinst.require_shared_vertices(); + m_scene_cache.add_mesh(mshinst, OpenCSG::Intersection, + m_csgsettings.get_convexity()); + + auto tr = Transform3f::Identity(); + tr.translate(-center); + + transform_pts(holedata.begin(), holedata.end(), tr, + [](const sla::DrainHole &dh) { + return dh.pos; + }); + + transform_pts(holedata.begin(), holedata.end(), tr, + [](const sla::DrainHole &dh) { + return dh.normal; + }); + } + + for (const sla::DrainHole &holept : holedata) { + TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh()); + holemesh.require_shared_vertices(); + m_scene_cache.add_mesh(holemesh, OpenCSG::Subtraction, 1); + } + } + repaint(); } @@ -513,4 +540,6 @@ bool enable_multisampling(bool e) else return false; } +MouseInput::Listener::~Listener() = default; + }} // namespace Slic3r::GL diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/GLScene.hpp index 6beecb79a6..a127ab90be 100644 --- a/sandboxes/opencsg/GLScene.hpp +++ b/sandboxes/opencsg/GLScene.hpp @@ -45,8 +45,7 @@ public: class Listener { public: - - virtual ~Listener() = default; + virtual ~Listener(); virtual void on_left_click_down() {} virtual void on_left_click_up() {} @@ -219,9 +218,16 @@ public: }; class CSGSettings { +public: + static const constexpr unsigned DEFAULT_CONVEXITY = 10; + +private: OpenCSG::Algorithm m_csgalg = OpenCSG::Algorithm::Automatic; OpenCSG::DepthComplexityAlgorithm m_depth_algo = OpenCSG::DepthComplexityAlgorithm::NoDepthComplexitySampling; OpenCSG::Optimization m_optim = OpenCSG::Optimization::OptimizationDefault; + bool m_enable = true; + unsigned int m_convexity = DEFAULT_CONVEXITY; + public: int get_algo() const { return int(m_csgalg); } void set_algo(OpenCSG::Algorithm alg) { m_csgalg = alg; } @@ -231,6 +237,12 @@ public: int get_optimization() const { return int(m_optim); } void set_optimization(OpenCSG::Optimization o) { m_optim = 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; } }; class Display : public std::enable_shared_from_this, @@ -247,6 +259,19 @@ protected: shptr m_camera; + struct SceneCache { + Collection> primitives; + Collection primitives_free; + Collection primitives_csg; + + void clear(); + + shptr add_mesh(const TriangleMesh &mesh); + shptr add_mesh(const TriangleMesh &mesh, + OpenCSG::Operation op, + unsigned covexity); + } m_scene_cache; + public: Display(shptr scene = nullptr, shptr camera = nullptr) : m_scene(scene) @@ -284,26 +309,12 @@ public: class Scene: public MouseInput::Listener { - Collection> m_primitives; - Collection m_primitives_free; - Collection m_primitives_csg; - uqptr m_print; public: Scene(); ~Scene(); - const Collection& free_primitives() const - { - return m_primitives_free; - } - - const Collection& csg_primitives() const - { - return m_primitives_csg; - } - void add_display(shptr disp) { m_displays.emplace_back(disp); @@ -311,16 +322,10 @@ public: } void set_print(uqptr &&print); + const SLAPrint * get_print() const { return m_print.get(); } BoundingBoxf3 get_bounding_box() const; -protected: - - shptr add_mesh(const TriangleMesh &mesh); - shptr add_mesh(const TriangleMesh &mesh, - OpenCSG::Operation op, - unsigned covexity); - private: Collection> m_displays; diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index 8c106fa85c..6274f264a1 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "Canvas.hpp" @@ -111,25 +112,53 @@ public: auto controlsizer = new wxBoxSizer(wxHORIZONTAL); auto slider_sizer = new wxBoxSizer(wxVERTICAL); auto console_sizer = new wxBoxSizer(wxVERTICAL); - - auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL); + + auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100, + wxDefaultPosition, wxDefaultSize, + wxSL_VERTICAL); slider_sizer->Add(slider, 1, wxEXPAND); - auto toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); - console_sizer->Add(toggle, 0, wxALL | wxEXPAND, 5); - + auto ms_toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); + console_sizer->Add(ms_toggle, 0, wxALL | wxEXPAND, 5); + + auto csg_toggle = new wxToggleButton(control_panel, wxID_ANY, "CSG"); + csg_toggle->SetValue(true); + 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, std::vector &&list) + { auto widget = new wxComboBox(control_panel, wxID_ANY, list[0], wxDefaultPosition, wxDefaultSize, int(list.size()), list.data()); + auto sz = new wxBoxSizer(wxHORIZONTAL); - sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, wxALL | wxALIGN_CENTER, 5); + sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, + wxALL | wxALIGN_CENTER, 5); sz->Add(widget, 1, wxALL | wxEXPAND, 5); console_sizer->Add(sz, 0, wxEXPAND); return widget; }; + auto add_spinctl = [control_panel, console_sizer] + (const wxString &label, int initial, int min, int max) + { + auto widget = new wxSpinCtrl( + control_panel, wxID_ANY, + wxString::Format("%d", initial), + wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, + initial); + + auto sz = new wxBoxSizer(wxHORIZONTAL); + sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, + wxALL | wxALIGN_CENTER, 5); + sz->Add(widget, 1, wxALL | wxEXPAND, 5); + console_sizer->Add(sz, 0, wxEXPAND); + return widget; + }; + + 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"}); depth_select->Disable(); @@ -152,10 +181,16 @@ public: m_canvas->move_clip_plane(double(slider->GetValue())); }, slider->GetId()); - Bind(wxEVT_TOGGLEBUTTON, [this, toggle](wxCommandEvent &){ - enable_multisampling(toggle->GetValue()); + Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ + enable_multisampling(ms_toggle->GetValue()); m_canvas->repaint(); - }, toggle->GetId()); + }, ms_toggle->GetId()); + + Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ + CSGSettings settings = m_canvas->get_csgsettings(); + settings.enable_csg(csg_toggle->GetValue()); + m_canvas->apply_csgsettings(settings); + }, csg_toggle->GetId()); Bind(wxEVT_COMBOBOX, [this, alg_select, depth_select](wxCommandEvent &) { @@ -182,6 +217,16 @@ public: m_canvas->apply_csgsettings(settings); }, depth_select->GetId()); + Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { + CSGSettings settings = m_canvas->get_csgsettings(); + int c = convexity_spin->GetValue(); + + if (c > 0) { + settings.set_convexity(unsigned(c)); + m_canvas->apply_csgsettings(settings); + } + }, convexity_spin->GetId()); + m_canvas->set_scene(std::make_shared()); } From 5aaddd82a44475c37f933d476a10351f03ddee01 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 15:36:43 +0100 Subject: [PATCH 4/5] Remove bloat, add some clipping plane functionality. --- sandboxes/opencsg/Canvas.hpp | 14 +- sandboxes/opencsg/GLScene.cpp | 8 +- sandboxes/opencsg/main.cpp | 314 +++++++++++++++++----------------- 3 files changed, 168 insertions(+), 168 deletions(-) diff --git a/sandboxes/opencsg/Canvas.hpp b/sandboxes/opencsg/Canvas.hpp index 85d490ddf5..17fd8f0440 100644 --- a/sandboxes/opencsg/Canvas.hpp +++ b/sandboxes/opencsg/Canvas.hpp @@ -27,20 +27,8 @@ public: Slic3r::GL::Display::set_active(w, h); } - void repaint(long width, long height) override - { - Slic3r::GL::Display::repaint(width, height); - } - - using Slic3r::GL::Display::repaint; - void swap_buffers() override { SwapBuffers(); } - - void on_scroll(long v, long d, Slic3r::GL::MouseInput::WheelAxis wa) override - { - Slic3r::GL::Display::on_scroll(v, d, wa); - } - + template Canvas(Args &&...args): wxGLCanvas(std::forward(args)...) { diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp index 5037ef6a54..02a4b79914 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/GLScene.cpp @@ -517,7 +517,13 @@ void Camera::view() glRotatef(m_rot.y(), 1.0, 0.0, 0.0); glRotatef(m_rot.x(), 0.0, 0.0, 1.0); - // glClipPlane() + if (m_clip_z > 0.) { + GLdouble plane[] = {0., 0., 1., m_clip_z}; + glClipPlane(GL_CLIP_PLANE0, plane); + glEnable(GL_CLIP_PLANE0); + } else { + glDisable(GL_CLIP_PLANE0); + } } void PerspectiveCamera::set_screen(long width, long height) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index 6274f264a1..ac18e177d1 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -26,7 +26,6 @@ #include "slic3r/GUI/Job.hpp" #include "slic3r/GUI/ProgressStatusBar.hpp" -//#include "slic3r/GUI/3DEngine.hpp" using namespace Slic3r::GL; @@ -59,6 +58,10 @@ class MyFrame: public wxFrame m_print = std::make_unique(); m_print->apply(model, cfg); + Slic3r::PrintBase::TaskParams params; + params.to_object_step = Slic3r::slaposHollowing; + m_print->set_task(params); + m_print->set_status_callback([this](const Status &status) { update_status(status.percent, status.text); }); @@ -77,158 +80,7 @@ class MyFrame: public wxFrame }; public: - MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size): - wxFrame(nullptr, wxID_ANY, title, pos, size) - { - wxMenu *menuFile = new wxMenu; - menuFile->Append(wxID_OPEN); - menuFile->Append(wxID_EXIT); - wxMenuBar *menuBar = new wxMenuBar; - menuBar->Append( menuFile, "&File" ); - SetMenuBar( menuBar ); - - m_stbar = std::make_shared(this); - m_stbar->embed(this); - - SetStatusText( "Welcome to wxWidgets!" ); - - int attribList[] = - {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, - // RGB channels each should be allocated with 8 bit depth. One - // should almost certainly get these bit depths by default. - WX_GL_MIN_RED, 8, WX_GL_MIN_GREEN, 8, WX_GL_MIN_BLUE, 8, - // Requesting an 8 bit alpha channel. Interestingly, the NVIDIA - // drivers would most likely work with some alpha plane, but - // 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}; - - m_canvas = std::make_shared(this, wxID_ANY, attribList, - wxDefaultPosition, wxDefaultSize, - wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE); - - wxPanel *control_panel = new wxPanel(this); - auto controlsizer = new wxBoxSizer(wxHORIZONTAL); - auto slider_sizer = new wxBoxSizer(wxVERTICAL); - auto console_sizer = new wxBoxSizer(wxVERTICAL); - - auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100, - wxDefaultPosition, wxDefaultSize, - wxSL_VERTICAL); - slider_sizer->Add(slider, 1, wxEXPAND); - - auto ms_toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); - console_sizer->Add(ms_toggle, 0, wxALL | wxEXPAND, 5); - - auto csg_toggle = new wxToggleButton(control_panel, wxID_ANY, "CSG"); - csg_toggle->SetValue(true); - console_sizer->Add(csg_toggle, 0, wxALL | wxEXPAND, 5); - - auto add_combobox = [control_panel, console_sizer] - (const wxString &label, std::vector &&list) - { - auto widget = new wxComboBox(control_panel, wxID_ANY, list[0], - wxDefaultPosition, wxDefaultSize, - int(list.size()), list.data()); - - auto sz = new wxBoxSizer(wxHORIZONTAL); - sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, - wxALL | wxALIGN_CENTER, 5); - sz->Add(widget, 1, wxALL | wxEXPAND, 5); - console_sizer->Add(sz, 0, wxEXPAND); - return widget; - }; - - auto add_spinctl = [control_panel, console_sizer] - (const wxString &label, int initial, int min, int max) - { - auto widget = new wxSpinCtrl( - control_panel, wxID_ANY, - wxString::Format("%d", initial), - wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, - initial); - - auto sz = new wxBoxSizer(wxHORIZONTAL); - sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, - wxALL | wxALIGN_CENTER, 5); - sz->Add(widget, 1, wxALL | wxEXPAND, 5); - console_sizer->Add(sz, 0, wxEXPAND); - return widget; - }; - - 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"}); - depth_select->Disable(); - auto optimization_select = add_combobox("Optimization", { "Default", "ForceOn", "On", "Off" }); - - controlsizer->Add(slider_sizer, 0, wxEXPAND); - controlsizer->Add(console_sizer, 1, wxEXPAND); - - control_panel->SetSizer(controlsizer); - - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(m_canvas.get(), 1, wxEXPAND); - 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()); - Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { - m_canvas->move_clip_plane(double(slider->GetValue())); - }, slider->GetId()); - - Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ - enable_multisampling(ms_toggle->GetValue()); - m_canvas->repaint(); - }, ms_toggle->GetId()); - - Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ - CSGSettings settings = m_canvas->get_csgsettings(); - settings.enable_csg(csg_toggle->GetValue()); - m_canvas->apply_csgsettings(settings); - }, csg_toggle->GetId()); - - Bind(wxEVT_COMBOBOX, [this, alg_select, depth_select](wxCommandEvent &) - { - int sel = alg_select->GetSelection(); - depth_select->Enable(sel > 0); - CSGSettings settings = m_canvas->get_csgsettings(); - settings.set_algo(OpenCSG::Algorithm(sel)); - m_canvas->apply_csgsettings(settings); - }, alg_select->GetId()); - - Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) - { - int sel = depth_select->GetSelection(); - CSGSettings settings = m_canvas->get_csgsettings(); - settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); - m_canvas->apply_csgsettings(settings); - }, depth_select->GetId()); - - Bind(wxEVT_COMBOBOX, [this, optimization_select](wxCommandEvent &) - { - int sel = optimization_select->GetSelection(); - CSGSettings settings = m_canvas->get_csgsettings(); - settings.set_optimization(OpenCSG::Optimization(sel)); - m_canvas->apply_csgsettings(settings); - }, depth_select->GetId()); - - Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { - CSGSettings settings = m_canvas->get_csgsettings(); - int c = convexity_spin->GetValue(); - - if (c > 0) { - settings.set_convexity(unsigned(c)); - m_canvas->apply_csgsettings(settings); - } - }, convexity_spin->GetId()); - - m_canvas->set_scene(std::make_shared()); - } + MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size); private: @@ -270,8 +122,8 @@ class App : public wxApp { MyFrame *m_frame; public: bool OnInit() override { + m_frame = new MyFrame("PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768)); - m_frame = new MyFrame( "PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768) ); m_frame->Show( true ); return true; @@ -279,3 +131,157 @@ public: }; wxIMPLEMENT_APP(App); + +MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): + wxFrame(nullptr, wxID_ANY, title, pos, size) +{ + wxMenu *menuFile = new wxMenu; + menuFile->Append(wxID_OPEN); + menuFile->Append(wxID_EXIT); + wxMenuBar *menuBar = new wxMenuBar; + menuBar->Append( menuFile, "&File" ); + SetMenuBar( menuBar ); + + m_stbar = std::make_shared(this); + m_stbar->embed(this); + + SetStatusText( "Welcome to wxWidgets!" ); + + int attribList[] = + {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, + // RGB channels each should be allocated with 8 bit depth. One + // should almost certainly get these bit depths by default. + WX_GL_MIN_RED, 8, WX_GL_MIN_GREEN, 8, WX_GL_MIN_BLUE, 8, + // Requesting an 8 bit alpha channel. Interestingly, the NVIDIA + // drivers would most likely work with some alpha plane, but + // 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}; + + m_canvas = std::make_shared(this, wxID_ANY, attribList, + wxDefaultPosition, wxDefaultSize, + wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE); + + wxPanel *control_panel = new wxPanel(this); + auto controlsizer = new wxBoxSizer(wxHORIZONTAL); + auto slider_sizer = new wxBoxSizer(wxVERTICAL); + auto console_sizer = new wxBoxSizer(wxVERTICAL); + + auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100, + wxDefaultPosition, wxDefaultSize, + wxSL_VERTICAL); + slider_sizer->Add(slider, 1, wxEXPAND); + + auto ms_toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); + console_sizer->Add(ms_toggle, 0, wxALL | wxEXPAND, 5); + + auto csg_toggle = new wxToggleButton(control_panel, wxID_ANY, "CSG"); + csg_toggle->SetValue(true); + console_sizer->Add(csg_toggle, 0, wxALL | wxEXPAND, 5); + + auto add_combobox = [control_panel, console_sizer] + (const wxString &label, std::vector &&list) + { + auto widget = new wxComboBox(control_panel, wxID_ANY, list[0], + wxDefaultPosition, wxDefaultSize, + int(list.size()), list.data()); + + auto sz = new wxBoxSizer(wxHORIZONTAL); + sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, + wxALL | wxALIGN_CENTER, 5); + sz->Add(widget, 1, wxALL | wxEXPAND, 5); + console_sizer->Add(sz, 0, wxEXPAND); + return widget; + }; + + auto add_spinctl = [control_panel, console_sizer] + (const wxString &label, int initial, int min, int max) + { + auto widget = new wxSpinCtrl( + control_panel, wxID_ANY, + wxString::Format("%d", initial), + wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, + initial); + + auto sz = new wxBoxSizer(wxHORIZONTAL); + sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, + wxALL | wxALIGN_CENTER, 5); + sz->Add(widget, 1, wxALL | wxEXPAND, 5); + console_sizer->Add(sz, 0, wxEXPAND); + return widget; + }; + + 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" }); + depth_select->Disable(); + + controlsizer->Add(slider_sizer, 0, wxEXPAND); + controlsizer->Add(console_sizer, 1, wxEXPAND); + + control_panel->SetSizer(controlsizer); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(m_canvas.get(), 1, wxEXPAND); + 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()); + + Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { + m_canvas->move_clip_plane(double(slider->GetValue())); + }, slider->GetId()); + + Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ + enable_multisampling(ms_toggle->GetValue()); + m_canvas->repaint(); + }, ms_toggle->GetId()); + + Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ + CSGSettings settings = m_canvas->get_csgsettings(); + settings.enable_csg(csg_toggle->GetValue()); + m_canvas->apply_csgsettings(settings); + }, csg_toggle->GetId()); + + Bind(wxEVT_COMBOBOX, [this, alg_select, depth_select](wxCommandEvent &) + { + int sel = alg_select->GetSelection(); + depth_select->Enable(sel > 0); + CSGSettings settings = m_canvas->get_csgsettings(); + settings.set_algo(OpenCSG::Algorithm(sel)); + m_canvas->apply_csgsettings(settings); + }, alg_select->GetId()); + + Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) + { + int sel = depth_select->GetSelection(); + CSGSettings settings = m_canvas->get_csgsettings(); + settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); + m_canvas->apply_csgsettings(settings); + }, depth_select->GetId()); + + Bind(wxEVT_COMBOBOX, [this, optimization_select](wxCommandEvent &) + { + int sel = optimization_select->GetSelection(); + CSGSettings settings = m_canvas->get_csgsettings(); + settings.set_optimization(OpenCSG::Optimization(sel)); + m_canvas->apply_csgsettings(settings); + }, depth_select->GetId()); + + Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { + CSGSettings settings = m_canvas->get_csgsettings(); + int c = convexity_spin->GetValue(); + + if (c > 0) { + settings.set_convexity(unsigned(c)); + m_canvas->apply_csgsettings(settings); + } + }, convexity_spin->GetId()); + + m_canvas->set_scene(std::make_shared()); +} From 47ec708c3a33beb9c14c5d6c7c730f3eee4a4e94 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 15:55:49 +0100 Subject: [PATCH 5/5] Fix event dispatching to handlers --- sandboxes/opencsg/main.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index ac18e177d1..ddd4bf33ef 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -235,45 +235,45 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { m_canvas->move_clip_plane(double(slider->GetValue())); - }, slider->GetId()); + }); - Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ + ms_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ enable_multisampling(ms_toggle->GetValue()); m_canvas->repaint(); - }, ms_toggle->GetId()); + }); - Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ + csg_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ CSGSettings settings = m_canvas->get_csgsettings(); settings.enable_csg(csg_toggle->GetValue()); m_canvas->apply_csgsettings(settings); - }, csg_toggle->GetId()); + }); - Bind(wxEVT_COMBOBOX, [this, alg_select, depth_select](wxCommandEvent &) + alg_select->Bind(wxEVT_COMBOBOX, + [this, alg_select, depth_select](wxCommandEvent &) { int sel = alg_select->GetSelection(); depth_select->Enable(sel > 0); CSGSettings settings = m_canvas->get_csgsettings(); settings.set_algo(OpenCSG::Algorithm(sel)); m_canvas->apply_csgsettings(settings); - }, alg_select->GetId()); + }); - Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) - { + depth_select->Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) { int sel = depth_select->GetSelection(); CSGSettings settings = m_canvas->get_csgsettings(); settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); m_canvas->apply_csgsettings(settings); - }, depth_select->GetId()); + }); - Bind(wxEVT_COMBOBOX, [this, optimization_select](wxCommandEvent &) - { + optimization_select->Bind(wxEVT_COMBOBOX, + [this, optimization_select](wxCommandEvent &) { int sel = optimization_select->GetSelection(); CSGSettings settings = m_canvas->get_csgsettings(); settings.set_optimization(OpenCSG::Optimization(sel)); m_canvas->apply_csgsettings(settings); - }, depth_select->GetId()); + }); - Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { + convexity_spin->Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { CSGSettings settings = m_canvas->get_csgsettings(); int c = convexity_spin->GetValue(); @@ -281,7 +281,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): settings.set_convexity(unsigned(c)); m_canvas->apply_csgsettings(settings); } - }, convexity_spin->GetId()); + }); m_canvas->set_scene(std::make_shared()); }