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