1 /* This file is part of the Pangolin Project.
2 * http://github.com/stevenlovegrove/Pangolin
3 *
4 * Copyright (c) 2013 Steven Lovegrove
5 *
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following
13 * conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28 #include <pangolin/handler/handler.h>
29 #include <pangolin/display/display_internal.h>
30 #include <pangolin/display/view.h>
31
32 namespace pangolin
33 {
34
35 // Pointer to context defined in display.cpp
36 extern __thread PangolinGl* context;
37
Keyboard(View & d,unsigned char key,int x,int y,bool pressed)38 void Handler::Keyboard(View& d, unsigned char key, int x, int y, bool pressed)
39 {
40 View* child = d.FindChild(x,y);
41 if( child)
42 {
43 context->activeDisplay = child;
44 if( child->handler)
45 child->handler->Keyboard(*child,key,x,y,pressed);
46 }
47 }
48
Mouse(View & d,MouseButton button,int x,int y,bool pressed,int button_state)49 void Handler::Mouse(View& d, MouseButton button, int x, int y, bool pressed, int button_state)
50 {
51 View* child = d.FindChild(x,y);
52 if( child )
53 {
54 context->activeDisplay = child;
55 if( child->handler)
56 child->handler->Mouse(*child,button,x,y,pressed,button_state);
57 }
58 }
59
MouseMotion(View & d,int x,int y,int button_state)60 void Handler::MouseMotion(View& d, int x, int y, int button_state)
61 {
62 View* child = d.FindChild(x,y);
63 if( child )
64 {
65 context->activeDisplay = child;
66 if( child->handler)
67 child->handler->MouseMotion(*child,x,y,button_state);
68 }
69 }
70
PassiveMouseMotion(View & d,int x,int y,int button_state)71 void Handler::PassiveMouseMotion(View& d, int x, int y, int button_state)
72 {
73 View* child = d.FindChild(x,y);
74 if( child )
75 {
76 if( child->handler)
77 child->handler->PassiveMouseMotion(*child,x,y,button_state);
78 }
79 }
80
Special(View & d,InputSpecial inType,float x,float y,float p1,float p2,float p3,float p4,int button_state)81 void Handler::Special(View& d, InputSpecial inType, float x, float y, float p1, float p2, float p3, float p4, int button_state)
82 {
83 View* child = d.FindChild( (int)x, (int)y);
84 if( child )
85 {
86 context->activeDisplay = child;
87 if( child->handler)
88 child->handler->Special(*child,inType, x,y, p1, p2, p3, p4, button_state);
89 }
90 }
91
Mouse(View & d,MouseButton button,int x,int y,bool pressed,int button_state)92 void HandlerScroll::Mouse(View& d, MouseButton button, int x, int y, bool pressed, int button_state)
93 {
94 if( pressed && (button == MouseWheelUp || button == MouseWheelDown) )
95 {
96 if( button == MouseWheelUp) d.scroll_offset -= 1;
97 if( button == MouseWheelDown) d.scroll_offset += 1;
98 d.scroll_offset = std::max(0, std::min(d.scroll_offset, (int)d.NumVisibleChildren()-1) );
99 d.ResizeChildren();
100 }else{
101 Handler::Mouse(d,button,x,y,pressed,button_state);
102 }
103 }
104
Special(View & d,InputSpecial inType,float x,float y,float p1,float p2,float p3,float p4,int button_state)105 void HandlerScroll::Special(View& d, InputSpecial inType, float x, float y, float p1, float p2, float p3, float p4, int button_state)
106 {
107 if( inType == InputSpecialScroll )
108 {
109 d.scroll_offset -= (int)(p2 / fabs(p2));
110 d.scroll_offset = std::max(0, std::min(d.scroll_offset, (int)d.NumVisibleChildren()-1) );
111 d.ResizeChildren();
112 }else{
113 Handler::Special(d,inType,x,y,p1,p2,p3,p4,button_state);
114 }
115 }
116
Handler3D(OpenGlRenderState & cam_state,AxisDirection enforce_up,float trans_scale,float zoom_fraction)117 Handler3D::Handler3D(OpenGlRenderState& cam_state, AxisDirection enforce_up, float trans_scale, float zoom_fraction)
118 : cam_state(&cam_state), enforce_up(enforce_up), tf(trans_scale), zf(zoom_fraction), cameraspec(CameraSpecOpenGl), last_z(0.8)
119 {
120 SetZero<3,1>(rot_center);
121 }
122
Keyboard(View &,unsigned char,int,int,bool)123 void Handler3D::Keyboard(View&, unsigned char /*key*/, int /*x*/, int /*y*/, bool /*pressed*/)
124 {
125 // TODO: hooks for reset / changing mode (perspective / ortho etc)
126 }
127
ValidWinDepth(GLprecision depth)128 bool Handler3D::ValidWinDepth(GLprecision depth)
129 {
130 return depth != 1;
131 }
132
PixelUnproject(View & view,GLprecision winx,GLprecision winy,GLprecision winz,GLprecision Pc[3])133 void Handler3D::PixelUnproject( View& view, GLprecision winx, GLprecision winy, GLprecision winz, GLprecision Pc[3])
134 {
135 const GLint viewport[4] = {view.v.l,view.v.b,view.v.w,view.v.h};
136 const pangolin::OpenGlMatrix proj = cam_state->GetProjectionMatrix();
137 glUnProject(winx, winy, winz, Identity4d, proj.m, viewport, &Pc[0], &Pc[1], &Pc[2]);
138 }
139
GetPosNormal(pangolin::View & view,int winx,int winy,GLprecision p[3],GLprecision Pw[3],GLprecision Pc[3],GLprecision nw[3],GLprecision default_z)140 void Handler3D::GetPosNormal(pangolin::View& view, int winx, int winy, GLprecision p[3], GLprecision Pw[3], GLprecision Pc[3], GLprecision nw[3], GLprecision default_z)
141 {
142 // TODO: Get to work on android
143
144 const int zl = (hwin*2+1);
145 const int zsize = zl*zl;
146 GLfloat zs[zsize];
147
148 #ifndef HAVE_GLES
149 glReadBuffer(GL_FRONT);
150 glReadPixels(winx-hwin,winy-hwin,zl,zl,GL_DEPTH_COMPONENT,GL_FLOAT,zs);
151 #else
152 std::fill(zs,zs+zsize, 1);
153 #endif
154 GLfloat mindepth = *(std::min_element(zs,zs+zsize));
155
156 if(mindepth == 1) mindepth = (GLfloat)default_z;
157
158 p[0] = winx; p[1] = winy; p[2] = mindepth;
159 PixelUnproject(view, winx, winy, mindepth, Pc);
160
161 const pangolin::OpenGlMatrix mv = cam_state->GetModelViewMatrix();
162
163 GLprecision T_wc[3*4];
164 LieSE3from4x4(T_wc, mv.Inverse().m );
165 LieApplySE3vec(Pw, T_wc, Pc);
166
167 // Neighboring points in camera coordinates
168 GLprecision Pl[3]; GLprecision Pr[3]; GLprecision Pb[3]; GLprecision Pt[3];
169 PixelUnproject(view, winx-hwin, winy, zs[hwin*zl + 0], Pl );
170 PixelUnproject(view, winx+hwin, winy, zs[hwin*zl + zl-1], Pr );
171 PixelUnproject(view, winx, winy-hwin, zs[hwin+1], Pb );
172 PixelUnproject(view, winx, winy+hwin, zs[zsize-(hwin+1)], Pt );
173
174 // n = ((Pr-Pl).cross(Pt-Pb)).normalized();
175 GLprecision PrmPl[3]; GLprecision PtmPb[3];
176 MatSub<3,1>(PrmPl,Pr,Pl);
177 MatSub<3,1>(PtmPb,Pt,Pb);
178
179 GLprecision nc[3];
180 CrossProduct(nc, PrmPl, PtmPb);
181 Normalise<3>(nc);
182
183 // T_wc is col major, so the rotation component is first.
184 LieApplySO3(nw,T_wc,nc);
185 }
186
Mouse(View & display,MouseButton button,int x,int y,bool pressed,int button_state)187 void Handler3D::Mouse(View& display, MouseButton button, int x, int y, bool pressed, int button_state)
188 {
189 // mouse down
190 last_pos[0] = (float)x;
191 last_pos[1] = (float)y;
192
193 GLprecision T_nc[3*4];
194 LieSetIdentity(T_nc);
195
196 funcKeyState = 0;
197 if( pressed )
198 {
199 GetPosNormal(display,x,y,p,Pw,Pc,n,last_z);
200 if( ValidWinDepth(p[2]) )
201 {
202 last_z = p[2];
203 std::copy(Pc,Pc+3,rot_center);
204 }
205
206 if( button == MouseWheelUp || button == MouseWheelDown)
207 {
208 LieSetIdentity(T_nc);
209 const GLprecision t[3] = { 0,0,(button==MouseWheelUp?1:-1)*100*tf};
210 LieSetTranslation<>(T_nc,t);
211 if( !(button_state & MouseButtonRight) && !(rot_center[0]==0 && rot_center[1]==0 && rot_center[2]==0) )
212 {
213 LieSetTranslation<>(T_nc,rot_center);
214 const GLprecision s = (button==MouseWheelUp?-1.0:1.0) * zf;
215 MatMul<3,1>(T_nc+(3*3), s);
216 }
217 OpenGlMatrix& spec = cam_state->GetModelViewMatrix();
218 LieMul4x4bySE3<>(spec.m,T_nc,spec.m);
219 }
220
221 funcKeyState = button_state;
222 }
223 }
224
MouseMotion(View & display,int x,int y,int button_state)225 void Handler3D::MouseMotion(View& display, int x, int y, int button_state)
226 {
227 const GLprecision rf = 0.01;
228 const float delta[2] = { (float)x - last_pos[0], (float)y - last_pos[1] };
229 const float mag = delta[0]*delta[0] + delta[1]*delta[1];
230
231 if((button_state & KeyModifierCtrl) && (button_state & KeyModifierShift))
232 {
233 GLprecision T_nc[3 * 4];
234 LieSetIdentity(T_nc);
235
236 GetPosNormal(display, x, y, p, Pw, Pc, n, last_z);
237 if(ValidWinDepth(p[2]))
238 {
239 last_z = p[2];
240 std::copy(Pc, Pc + 3, rot_center);
241 }
242
243 funcKeyState = button_state;
244 }
245 else
246 {
247 funcKeyState = 0;
248 }
249
250 // TODO: convert delta to degrees based of fov
251 // TODO: make transformation with respect to cam spec
252 if( mag < 50.0f*50.0f )
253 {
254 OpenGlMatrix& mv = cam_state->GetModelViewMatrix();
255 const GLprecision* up = AxisDirectionVector[enforce_up];
256 GLprecision T_nc[3*4];
257 LieSetIdentity(T_nc);
258 bool rotation_changed = false;
259
260 if( button_state == MouseButtonMiddle )
261 {
262 // Middle Drag: Rotate around view
263
264 // Try to correct for different coordinate conventions.
265 GLprecision aboutx = -rf * delta[1];
266 GLprecision abouty = rf * delta[0];
267 OpenGlMatrix& pm = cam_state->GetProjectionMatrix();
268 abouty *= -pm.m[2 * 4 + 3];
269
270 Rotation<>(T_nc, aboutx, abouty, (GLprecision)0.0);
271 }else if( button_state == MouseButtonLeft )
272 {
273 // Left Drag: in plane translate
274 if( ValidWinDepth(last_z) )
275 {
276 GLprecision np[3];
277 PixelUnproject(display, x, y, last_z, np);
278 const GLprecision t[] = { np[0] - rot_center[0], np[1] - rot_center[1], 0};
279 LieSetTranslation<>(T_nc,t);
280 std::copy(np,np+3,rot_center);
281 }else{
282 const GLprecision t[] = { -10*delta[0]*tf, 10*delta[1]*tf, 0};
283 LieSetTranslation<>(T_nc,t);
284 }
285 }else if( button_state == (MouseButtonLeft | MouseButtonRight) )
286 {
287 // Left and Right Drag: in plane rotate about object
288 // Rotation<>(T_nc,0.0,0.0, delta[0]*0.01);
289
290 GLprecision T_2c[3*4];
291 Rotation<>(T_2c, (GLprecision)0.0, (GLprecision)0.0, delta[0]*rf);
292 GLprecision mrotc[3];
293 MatMul<3,1>(mrotc, rot_center, (GLprecision)-1.0);
294 LieApplySO3<>(T_2c+(3*3),T_2c,mrotc);
295 GLprecision T_n2[3*4];
296 LieSetIdentity<>(T_n2);
297 LieSetTranslation<>(T_n2,rot_center);
298 LieMulSE3(T_nc, T_n2, T_2c );
299 rotation_changed = true;
300 }else if( button_state == MouseButtonRight)
301 {
302 GLprecision aboutx = -rf * delta[1];
303 GLprecision abouty = -rf * delta[0];
304
305 // Try to correct for different coordinate conventions.
306 if(cam_state->GetProjectionMatrix().m[2*4+3] <= 0) {
307 abouty *= -1;
308 }
309
310 if(enforce_up) {
311 // Special case if view direction is parallel to up vector
312 const GLprecision updotz = mv.m[2]*up[0] + mv.m[6]*up[1] + mv.m[10]*up[2];
313 if(updotz > 0.98) aboutx = std::min(aboutx, (GLprecision)0.0);
314 if(updotz <-0.98) aboutx = std::max(aboutx, (GLprecision)0.0);
315 // Module rotation around y so we don't spin too fast!
316 abouty *= (1-0.6*fabs(updotz));
317 }
318
319 // Right Drag: object centric rotation
320 GLprecision T_2c[3*4];
321 Rotation<>(T_2c, aboutx, abouty, (GLprecision)0.0);
322 GLprecision mrotc[3];
323 MatMul<3,1>(mrotc, rot_center, (GLprecision)-1.0);
324 LieApplySO3<>(T_2c+(3*3),T_2c,mrotc);
325 GLprecision T_n2[3*4];
326 LieSetIdentity<>(T_n2);
327 LieSetTranslation<>(T_n2,rot_center);
328 LieMulSE3(T_nc, T_n2, T_2c );
329 rotation_changed = true;
330 }
331
332 LieMul4x4bySE3<>(mv.m,T_nc,mv.m);
333
334 if(enforce_up != AxisNone && rotation_changed) {
335 EnforceUpT_cw(mv.m, up);
336 }
337 }
338
339 last_pos[0] = (float)x;
340 last_pos[1] = (float)y;
341 }
342
Special(View & display,InputSpecial inType,float x,float y,float p1,float p2,float,float,int button_state)343 void Handler3D::Special(View& display, InputSpecial inType, float x, float y, float p1, float p2, float /*p3*/, float /*p4*/, int button_state)
344 {
345 if( !(inType == InputSpecialScroll || inType == InputSpecialRotate) )
346 return;
347
348 // mouse down
349 last_pos[0] = x;
350 last_pos[1] = y;
351
352 GLprecision T_nc[3*4];
353 LieSetIdentity(T_nc);
354
355 GetPosNormal(display, (int)x, (int)y, p, Pw, Pc, n, last_z);
356 if(p[2] < 1.0) {
357 last_z = p[2];
358 std::copy(Pc,Pc+3,rot_center);
359 }
360
361 if( inType == InputSpecialScroll ) {
362 if(button_state & KeyModifierCmd) {
363 const GLprecision rx = -p2 / 1000;
364 const GLprecision ry = -p1 / 1000;
365
366 Rotation<>(T_nc,rx, ry, (GLprecision)0.0);
367 OpenGlMatrix& spec = cam_state->GetModelViewMatrix();
368 LieMul4x4bySE3<>(spec.m,T_nc,spec.m);
369 }else{
370 const GLprecision scrolly = p2/10;
371
372 LieSetIdentity(T_nc);
373 const GLprecision t[] = { 0,0, -scrolly*100*tf};
374 LieSetTranslation<>(T_nc,t);
375 if( !(button_state & MouseButtonRight) && !(rot_center[0]==0 && rot_center[1]==0 && rot_center[2]==0) )
376 {
377 LieSetTranslation<>(T_nc,rot_center);
378 MatMul<3,1>(T_nc+(3*3), -scrolly * zf);
379 }
380 OpenGlMatrix& spec = cam_state->GetModelViewMatrix();
381 LieMul4x4bySE3<>(spec.m,T_nc,spec.m);
382 }
383 }else if(inType == InputSpecialRotate) {
384 const GLprecision r = p1 / 20;
385
386 GLprecision T_2c[3*4];
387 Rotation<>(T_2c, (GLprecision)0.0, (GLprecision)0.0, r);
388 GLprecision mrotc[3];
389 MatMul<3,1>(mrotc, rot_center, (GLprecision)-1.0);
390 LieApplySO3<>(T_2c+(3*3),T_2c,mrotc);
391 GLprecision T_n2[3*4];
392 LieSetIdentity<>(T_n2);
393 LieSetTranslation<>(T_n2,rot_center);
394 LieMulSE3(T_nc, T_n2, T_2c );
395 OpenGlMatrix& spec = cam_state->GetModelViewMatrix();
396 LieMul4x4bySE3<>(spec.m,T_nc,spec.m);
397 }
398
399 }
400
401 }
402