Files
OrcaSlicer-bambulab/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
Lukas Matena 4c22023762 SLA gizmo clipping plane resetting
Clipping plane direction is now initialized when the plane is first moved, not when the gizmo is opened. This is how previous versions worked.
This is a minimal-effort solution before the clipping plane is refactored properly.
2020-02-21 09:46:40 +01:00

419 lines
12 KiB
C++

#include "GLGizmoBase.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include <GL/glew.h>
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "slic3r/GUI/MeshUtils.hpp"
// TODO: Display tooltips quicker on Linux
namespace Slic3r {
namespace GUI {
const float GLGizmoBase::Grabber::SizeFactor = 0.05f;
const float GLGizmoBase::Grabber::MinHalfSize = 1.5f;
const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f;
GLGizmoBase::Grabber::Grabber()
: center(Vec3d::Zero())
, angles(Vec3d::Zero())
, dragging(false)
, enabled(true)
{
color[0] = 1.0f;
color[1] = 1.0f;
color[2] = 1.0f;
color[3] = 1.0f;
}
void GLGizmoBase::Grabber::render(bool hover, float size) const
{
float render_color[4];
if (hover)
{
render_color[0] = 1.0f - color[0];
render_color[1] = 1.0f - color[1];
render_color[2] = 1.0f - color[2];
render_color[3] = color[3];
}
else
::memcpy((void*)render_color, (const void*)color, 4 * sizeof(float));
render(size, render_color, true);
}
float GLGizmoBase::Grabber::get_half_size(float size) const
{
return std::max(size * SizeFactor, MinHalfSize);
}
float GLGizmoBase::Grabber::get_dragging_half_size(float size) const
{
return get_half_size(size) * DraggingScaleFactor;
}
void GLGizmoBase::Grabber::render(float size, const float* render_color, bool use_lighting) const
{
float half_size = dragging ? get_dragging_half_size(size) : get_half_size(size);
if (use_lighting)
glsafe(::glEnable(GL_LIGHTING));
glsafe(::glColor4fv(render_color));
glsafe(::glPushMatrix());
glsafe(::glTranslated(center(0), center(1), center(2)));
glsafe(::glRotated(Geometry::rad2deg(angles(2)), 0.0, 0.0, 1.0));
glsafe(::glRotated(Geometry::rad2deg(angles(1)), 0.0, 1.0, 0.0));
glsafe(::glRotated(Geometry::rad2deg(angles(0)), 1.0, 0.0, 0.0));
// face min x
glsafe(::glPushMatrix());
glsafe(::glTranslatef(-(GLfloat)half_size, 0.0f, 0.0f));
glsafe(::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f));
render_face(half_size);
glsafe(::glPopMatrix());
// face max x
glsafe(::glPushMatrix());
glsafe(::glTranslatef((GLfloat)half_size, 0.0f, 0.0f));
glsafe(::glRotatef(90.0f, 0.0f, 1.0f, 0.0f));
render_face(half_size);
glsafe(::glPopMatrix());
// face min y
glsafe(::glPushMatrix());
glsafe(::glTranslatef(0.0f, -(GLfloat)half_size, 0.0f));
glsafe(::glRotatef(90.0f, 1.0f, 0.0f, 0.0f));
render_face(half_size);
glsafe(::glPopMatrix());
// face max y
glsafe(::glPushMatrix());
glsafe(::glTranslatef(0.0f, (GLfloat)half_size, 0.0f));
glsafe(::glRotatef(-90.0f, 1.0f, 0.0f, 0.0f));
render_face(half_size);
glsafe(::glPopMatrix());
// face min z
glsafe(::glPushMatrix());
glsafe(::glTranslatef(0.0f, 0.0f, -(GLfloat)half_size));
glsafe(::glRotatef(180.0f, 1.0f, 0.0f, 0.0f));
render_face(half_size);
glsafe(::glPopMatrix());
// face max z
glsafe(::glPushMatrix());
glsafe(::glTranslatef(0.0f, 0.0f, (GLfloat)half_size));
render_face(half_size);
glsafe(::glPopMatrix());
glsafe(::glPopMatrix());
if (use_lighting)
glsafe(::glDisable(GL_LIGHTING));
}
void GLGizmoBase::Grabber::render_face(float half_size) const
{
::glBegin(GL_TRIANGLES);
::glNormal3f(0.0f, 0.0f, 1.0f);
::glVertex3f(-(GLfloat)half_size, -(GLfloat)half_size, 0.0f);
::glVertex3f((GLfloat)half_size, -(GLfloat)half_size, 0.0f);
::glVertex3f((GLfloat)half_size, (GLfloat)half_size, 0.0f);
::glVertex3f((GLfloat)half_size, (GLfloat)half_size, 0.0f);
::glVertex3f(-(GLfloat)half_size, (GLfloat)half_size, 0.0f);
::glVertex3f(-(GLfloat)half_size, -(GLfloat)half_size, 0.0f);
glsafe(::glEnd());
}
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, CommonGizmosData* common_data_ptr)
: m_parent(parent)
, m_group_id(-1)
, m_state(Off)
, m_shortcut_key(0)
, m_icon_filename(icon_filename)
, m_sprite_id(sprite_id)
, m_hover_id(-1)
, m_dragging(false)
, m_imgui(wxGetApp().imgui())
, m_first_input_window_render(true)
, m_c(common_data_ptr)
{
::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 4 * sizeof(float));
::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 4 * sizeof(float));
::memcpy((void*)m_highlight_color, (const void*)DEFAULT_HIGHLIGHT_COLOR, 4 * sizeof(float));
}
void GLGizmoBase::set_hover_id(int id)
{
if (m_grabbers.empty() || (id < (int)m_grabbers.size()))
{
m_hover_id = id;
on_set_hover_id();
}
}
void GLGizmoBase::set_highlight_color(const float* color)
{
if (color != nullptr)
::memcpy((void*)m_highlight_color, (const void*)color, 4 * sizeof(float));
}
void GLGizmoBase::enable_grabber(unsigned int id)
{
if (id < m_grabbers.size())
m_grabbers[id].enabled = true;
on_enable_grabber(id);
}
void GLGizmoBase::disable_grabber(unsigned int id)
{
if (id < m_grabbers.size())
m_grabbers[id].enabled = false;
on_disable_grabber(id);
}
void GLGizmoBase::start_dragging()
{
m_dragging = true;
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
m_grabbers[i].dragging = (m_hover_id == i);
}
on_start_dragging();
}
void GLGizmoBase::stop_dragging()
{
m_dragging = false;
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
m_grabbers[i].dragging = false;
}
on_stop_dragging();
}
void GLGizmoBase::update(const UpdateData& data)
{
if (m_hover_id != -1)
on_update(data);
}
std::array<float, 4> GLGizmoBase::picking_color_component(unsigned int id) const
{
static const float INV_255 = 1.0f / 255.0f;
id = BASE_ID - id;
if (m_group_id > -1)
id -= m_group_id;
// color components are encoded to match the calculation of volume_id made into GLCanvas3D::_picking_pass()
return std::array<float, 4> {
float((id >> 0) & 0xff) * INV_255, // red
float((id >> 8) & 0xff) * INV_255, // green
float((id >> 16) & 0xff) * INV_255, // blue
float(picking_checksum_alpha_channel(id & 0xff, (id >> 8) & 0xff, (id >> 16) & 0xff))* INV_255 // checksum for validating against unwanted alpha blending and multi sampling
};
}
void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
{
render_grabbers((float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0));
}
void GLGizmoBase::render_grabbers(float size) const
{
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
if (m_grabbers[i].enabled)
m_grabbers[i].render((m_hover_id == i), size);
}
}
void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
{
float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
{
if (m_grabbers[i].enabled)
{
std::array<float, 4> color = picking_color_component(i);
m_grabbers[i].color[0] = color[0];
m_grabbers[i].color[1] = color[1];
m_grabbers[i].color[2] = color[2];
m_grabbers[i].color[3] = color[3];
m_grabbers[i].render_for_picking(mean_size);
}
}
}
void GLGizmoBase::set_tooltip(const std::string& tooltip) const
{
m_parent.set_tooltip(tooltip);
}
std::string GLGizmoBase::format(float value, unsigned int decimals) const
{
return Slic3r::string_printf("%.*f", decimals, value);
}
void GLGizmoBase::render_input_window(float x, float y, float bottom_limit)
{
on_render_input_window(x, y, bottom_limit);
if (m_first_input_window_render)
{
// for some reason, the imgui dialogs are not shown on screen in the 1st frame where they are rendered, but show up only with the 2nd rendered frame
// so, we forces another frame rendering the first time the imgui window is shown
m_parent.set_as_dirty();
m_first_input_window_render = false;
}
}
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
// were not interpolated by alpha blending or multi sampling.
unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue)
{
// 8 bit hash for the color
unsigned char b = ((((37 * red) + green) & 0x0ff) * 37 + blue) & 0x0ff;
// Increase enthropy by a bit reversal
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
// Flip every second bit to increase the enthropy even more.
b ^= 0x55;
return b;
}
bool CommonGizmosData::update_from_backend(GLCanvas3D& canvas, ModelObject* model_object)
{
recent_update = false;
bool object_changed = false;
if (m_model_object != model_object
|| (model_object && m_model_object_id != model_object->id())) {
m_model_object = model_object;
m_print_object_idx = -1;
m_mesh_raycaster.reset();
m_object_clipper.reset();
m_supports_clipper.reset();
m_old_mesh = nullptr;
m_mesh = nullptr;
m_backend_mesh_transformed.clear();
if (m_model_object) {
m_active_instance = canvas.get_selection().get_instance_idx();
m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius();
}
object_changed = true;
recent_update = true;
}
if (! m_model_object || ! canvas.get_selection().is_from_single_instance())
return false;
int old_po_idx = m_print_object_idx;
// First we need a pointer to the respective SLAPrintObject. The index into objects vector is
// cached so we don't have todo it on each render. We only search for the po if needed:
if (m_print_object_idx < 0 || (int)canvas.sla_print()->objects().size() != m_print_objects_count) {
m_print_objects_count = canvas.sla_print()->objects().size();
m_print_object_idx = -1;
for (const SLAPrintObject* po : canvas.sla_print()->objects()) {
++m_print_object_idx;
if (po->model_object()->id() == m_model_object->id())
break;
}
}
bool mesh_exchanged = false;
m_mesh = nullptr;
// Load either the model_object mesh, or one provided by the backend
// This mesh does not account for the possible Z up SLA offset.
// The backend mesh needs to be transformed and because a pointer to it is
// saved, a copy is stored as a member (FIXME)
if (m_print_object_idx >=0) {
const SLAPrintObject* po = canvas.sla_print()->objects()[m_print_object_idx];
if (po->is_step_done(slaposDrillHoles)) {
m_backend_mesh_transformed = po->get_mesh_to_print();
m_backend_mesh_transformed.transform(canvas.sla_print()->sla_trafo(*m_model_object).inverse());
m_mesh = &m_backend_mesh_transformed;
m_has_drilled_mesh = true;
mesh_exchanged = true;
}
}
if (! m_mesh) {
m_mesh = &m_model_object->volumes.front()->mesh();
m_backend_mesh_transformed.clear();
m_has_drilled_mesh = false;
}
m_model_object_id = m_model_object->id();
if (m_mesh != m_old_mesh) {
// Update clipping plane position.
float new_clp_pos = m_clipping_plane_distance;
if (object_changed) {
new_clp_pos = 0.f;
m_clipping_plane_was_moved = false;
} else {
// After we got a drilled mesh, move the cp to 25% (if not used already)
if (m_clipping_plane_distance == 0.f && mesh_exchanged && m_has_drilled_mesh) {
new_clp_pos = 0.25f;
m_clipping_plane_was_moved = false; // so it uses current camera direction
}
}
m_clipping_plane_distance = new_clp_pos;
m_clipping_plane_distance_stash = new_clp_pos;
m_schedule_aabb_calculation = true;
recent_update = true;
return true;
}
if (! recent_update)
recent_update = m_print_object_idx < 0 && old_po_idx >= 0;
return recent_update;
}
void CommonGizmosData::build_AABB_if_needed()
{
if (! m_schedule_aabb_calculation)
return;
wxBusyCursor wait;
m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh));
m_object_clipper.reset();
m_supports_clipper.reset();
m_old_mesh = m_mesh;
m_schedule_aabb_calculation = false;
}
} // namespace GUI
} // namespace Slic3r