1 // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
2 #include "GLGizmoMove.hpp"
3 #include "slic3r/GUI/GLCanvas3D.hpp"
4
5 #include <GL/glew.h>
6
7 #include <wx/utils.h>
8
9 namespace Slic3r {
10 namespace GUI {
11
12 const double GLGizmoMove3D::Offset = 10.0;
13
GLGizmoMove3D(GLCanvas3D & parent,const std::string & icon_filename,unsigned int sprite_id)14 GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
15 : GLGizmoBase(parent, icon_filename, sprite_id)
16 , m_displacement(Vec3d::Zero())
17 , m_snap_step(1.0)
18 , m_starting_drag_position(Vec3d::Zero())
19 , m_starting_box_center(Vec3d::Zero())
20 , m_starting_box_bottom_center(Vec3d::Zero())
21 , m_quadric(nullptr)
22 {
23 m_quadric = ::gluNewQuadric();
24 if (m_quadric != nullptr)
25 ::gluQuadricDrawStyle(m_quadric, GLU_FILL);
26 }
27
~GLGizmoMove3D()28 GLGizmoMove3D::~GLGizmoMove3D()
29 {
30 if (m_quadric != nullptr)
31 ::gluDeleteQuadric(m_quadric);
32 }
33
get_tooltip() const34 std::string GLGizmoMove3D::get_tooltip() const
35 {
36 const Selection& selection = m_parent.get_selection();
37 bool show_position = selection.is_single_full_instance();
38 const Vec3d& position = selection.get_bounding_box().center();
39
40 if (m_hover_id == 0 || m_grabbers[0].dragging)
41 return "X: " + format(show_position ? position(0) : m_displacement(0), 2);
42 else if (m_hover_id == 1 || m_grabbers[1].dragging)
43 return "Y: " + format(show_position ? position(1) : m_displacement(1), 2);
44 else if (m_hover_id == 2 || m_grabbers[2].dragging)
45 return "Z: " + format(show_position ? position(2) : m_displacement(2), 2);
46 else
47 return "";
48 }
49
on_init()50 bool GLGizmoMove3D::on_init()
51 {
52 for (int i = 0; i < 3; ++i)
53 {
54 m_grabbers.push_back(Grabber());
55 }
56
57 m_shortcut_key = WXK_CONTROL_M;
58
59 return true;
60 }
61
on_get_name() const62 std::string GLGizmoMove3D::on_get_name() const
63 {
64 return (_(L("Move")) + " [M]").ToUTF8().data();
65 }
66
on_is_activable() const67 bool GLGizmoMove3D::on_is_activable() const
68 {
69 return !m_parent.get_selection().is_empty();
70 }
71
on_start_dragging()72 void GLGizmoMove3D::on_start_dragging()
73 {
74 if (m_hover_id != -1)
75 {
76 m_displacement = Vec3d::Zero();
77 const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
78 m_starting_drag_position = m_grabbers[m_hover_id].center;
79 m_starting_box_center = box.center();
80 m_starting_box_bottom_center = box.center();
81 m_starting_box_bottom_center(2) = box.min(2);
82 }
83 }
84
on_stop_dragging()85 void GLGizmoMove3D::on_stop_dragging()
86 {
87 m_displacement = Vec3d::Zero();
88 }
89
on_update(const UpdateData & data)90 void GLGizmoMove3D::on_update(const UpdateData& data)
91 {
92 if (m_hover_id == 0)
93 m_displacement(0) = calc_projection(data);
94 else if (m_hover_id == 1)
95 m_displacement(1) = calc_projection(data);
96 else if (m_hover_id == 2)
97 m_displacement(2) = calc_projection(data);
98 }
99
on_render() const100 void GLGizmoMove3D::on_render() const
101 {
102 const Selection& selection = m_parent.get_selection();
103
104 glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
105 glsafe(::glEnable(GL_DEPTH_TEST));
106
107 const BoundingBoxf3& box = selection.get_bounding_box();
108 const Vec3d& center = box.center();
109
110 // x axis
111 m_grabbers[0].center = Vec3d(box.max(0) + Offset, center(1), center(2));
112 ::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 4 * sizeof(float));
113
114 // y axis
115 m_grabbers[1].center = Vec3d(center(0), box.max(1) + Offset, center(2));
116 ::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[1], 4 * sizeof(float));
117
118 // z axis
119 m_grabbers[2].center = Vec3d(center(0), center(1), box.max(2) + Offset);
120 ::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[2], 4 * sizeof(float));
121
122 glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
123
124 if (m_hover_id == -1)
125 {
126 // draw axes
127 for (unsigned int i = 0; i < 3; ++i)
128 {
129 if (m_grabbers[i].enabled)
130 {
131 glsafe(::glColor4fv(AXES_COLOR[i]));
132 ::glBegin(GL_LINES);
133 ::glVertex3dv(center.data());
134 ::glVertex3dv(m_grabbers[i].center.data());
135 glsafe(::glEnd());
136 }
137 }
138
139 // draw grabbers
140 render_grabbers(box);
141 for (unsigned int i = 0; i < 3; ++i)
142 {
143 if (m_grabbers[i].enabled)
144 render_grabber_extension((Axis)i, box, false);
145 }
146 }
147 else
148 {
149 // draw axis
150 glsafe(::glColor4fv(AXES_COLOR[m_hover_id]));
151 ::glBegin(GL_LINES);
152 ::glVertex3dv(center.data());
153 ::glVertex3dv(m_grabbers[m_hover_id].center.data());
154 glsafe(::glEnd());
155
156 // draw grabber
157 float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
158 m_grabbers[m_hover_id].render(true, mean_size);
159 render_grabber_extension((Axis)m_hover_id, box, false);
160 }
161 }
162
on_render_for_picking() const163 void GLGizmoMove3D::on_render_for_picking() const
164 {
165 glsafe(::glDisable(GL_DEPTH_TEST));
166
167 const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
168 render_grabbers_for_picking(box);
169 render_grabber_extension(X, box, true);
170 render_grabber_extension(Y, box, true);
171 render_grabber_extension(Z, box, true);
172 }
173
calc_projection(const UpdateData & data) const174 double GLGizmoMove3D::calc_projection(const UpdateData& data) const
175 {
176 double projection = 0.0;
177
178 Vec3d starting_vec = m_starting_drag_position - m_starting_box_center;
179 double len_starting_vec = starting_vec.norm();
180 if (len_starting_vec != 0.0)
181 {
182 Vec3d mouse_dir = data.mouse_ray.unit_vector();
183 // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
184 // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
185 // in our case plane normal and ray direction are the same (orthogonal view)
186 // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
187 Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
188 // vector from the starting position to the found intersection
189 Vec3d inters_vec = inters - m_starting_drag_position;
190
191 // finds projection of the vector along the staring direction
192 projection = inters_vec.dot(starting_vec.normalized());
193 }
194
195 if (wxGetKeyState(WXK_SHIFT))
196 projection = m_snap_step * (double)std::round(projection / m_snap_step);
197
198 return projection;
199 }
200
render_grabber_extension(Axis axis,const BoundingBoxf3 & box,bool picking) const201 void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const
202 {
203 if (m_quadric == nullptr)
204 return;
205
206 float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
207 double size = m_dragging ? (double)m_grabbers[axis].get_dragging_half_size(mean_size) : (double)m_grabbers[axis].get_half_size(mean_size);
208
209 float color[4];
210 ::memcpy((void*)color, (const void*)m_grabbers[axis].color, 4 * sizeof(float));
211 if (!picking && (m_hover_id != -1))
212 {
213 color[0] = 1.0f - color[0];
214 color[1] = 1.0f - color[1];
215 color[2] = 1.0f - color[2];
216 color[3] = color[3];
217 }
218
219 if (!picking)
220 glsafe(::glEnable(GL_LIGHTING));
221
222 glsafe(::glColor4fv(color));
223 glsafe(::glPushMatrix());
224 glsafe(::glTranslated(m_grabbers[axis].center(0), m_grabbers[axis].center(1), m_grabbers[axis].center(2)));
225 if (axis == X)
226 glsafe(::glRotated(90.0, 0.0, 1.0, 0.0));
227 else if (axis == Y)
228 glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0));
229
230 glsafe(::glTranslated(0.0, 0.0, 2.0 * size));
231 ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
232 ::gluCylinder(m_quadric, 0.75 * size, 0.0, 3.0 * size, 36, 1);
233 ::gluQuadricOrientation(m_quadric, GLU_INSIDE);
234 ::gluDisk(m_quadric, 0.0, 0.75 * size, 36, 1);
235 glsafe(::glPopMatrix());
236
237 if (!picking)
238 glsafe(::glDisable(GL_LIGHTING));
239 }
240
241
242
243 } // namespace GUI
244 } // namespace Slic3r
245