1 // -*- c-basic-offset: 4 -*-
2 /** @file GLViewer.cpp
3  *
4  *  @author James Legg
5  *  @author Darko Makreshanski
6  *
7  *  This is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This software is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public
18  *  License along with this software. If not, see
19  *  <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 #include "panoinc_WX.h"
24 #include "base_wx/wxutils.h"
25 
26 #include "panoinc.h"
27 #include "hugin_config.h"
28 #include <GL/glew.h>
29 #include <base_wx/platform.h>
30 #include <wx/settings.h>
31 #include <wx/dcclient.h>
32 #include <wx/event.h>
33 
34 #include "GLViewer.h"
35 #include "GLRenderer.h"
36 #include "TextureManager.h"
37 #include "MeshManager.h"
38 #include "ToolHelper.h"
39 #include "GLPreviewFrame.h"
40 #include "hugin/huginApp.h"
41 
42 bool GLViewer::initialised_glew=false;
43 ViewState * GLViewer::m_view_state = NULL;
44 size_t GLViewer::m_view_state_observer = 0;
45 
BEGIN_EVENT_TABLE(GLViewer,wxGLCanvas)46 BEGIN_EVENT_TABLE(GLViewer, wxGLCanvas)
47     EVT_PAINT (GLViewer::RedrawE)
48     EVT_SIZE  (GLViewer::Resized)
49     EVT_ERASE_BACKGROUND(GLViewer::OnEraseBackground)
50     // mouse motion
51     EVT_MOTION (GLViewer::MouseMotion)
52     // mouse entered or left the preview
53     EVT_ENTER_WINDOW(GLViewer::MouseEnter)
54     EVT_LEAVE_WINDOW(GLViewer::MouseLeave)
55     // mouse buttons
56     EVT_MOUSEWHEEL(GLViewer::MouseWheel)
57     EVT_MOUSE_EVENTS(GLViewer::MouseButtons)
58     // keyboard events
59     EVT_KEY_DOWN(GLViewer::KeyDown)
60     EVT_KEY_UP(GLViewer::KeyUp)
61 END_EVENT_TABLE()
62 
63 
64 GLViewer::GLViewer(
65             wxWindow* parent,
66             HuginBase::Panorama &pano,
67             int args[],
68             GLPreviewFrame *frame_in,
69             wxGLContext * shared_context
70             ) :
71           wxGLCanvas(parent, wxID_ANY, args, wxDefaultPosition, wxDefaultSize,
72                      0, wxT("GLPreviewCanvas"), wxNullPalette)
73 {
74     /* create OpenGL context... */
75     m_glContext = new wxGLContext(this, shared_context);
76 
77     m_renderer = 0;
78     m_visualization_state = 0;
79 
80     m_pano = &pano;
81 
82     frame = frame_in;
83 
84     m_background_color = frame->GetPreviewBackgroundColor();
85 
86     started_creation = false;
87     redrawing = false;
88     m_toolsInitialized = false;
89 
90     active = true;
91 }
92 
~GLViewer()93 GLViewer::~GLViewer()
94 {
95     if (m_renderer)
96     {
97       delete m_tool_helper;
98       delete m_renderer;
99       delete m_visualization_state;
100       // because m_view_state is a static member variable we need to check
101       // if other class has already deleted it
102       --m_view_state_observer;
103       if (m_view_state_observer == 0)
104       {
105         delete m_view_state;
106         m_view_state=NULL;
107       }
108     }
109     delete m_glContext;
110 }
111 
SetUpContext()112 void GLViewer::SetUpContext()
113 {
114     // set the context
115     DEBUG_INFO("Setting rendering context...");
116     Show();
117     m_glContext->SetCurrent(*this);
118     DEBUG_INFO("...got a rendering context.");
119     if (!started_creation)
120     {
121         // It appears we are setting up for the first time.
122         started_creation = true;
123 
124         if (!initialised_glew)
125         {
126             // initialise the glew library, if not done it before.
127             GLenum error_state = glewInit();
128             initialised_glew = true;
129             if (error_state != GLEW_OK)
130             {
131                 // glewInit failed
132                 started_creation=false;
133                 DEBUG_ERROR("Error initialising GLEW: "
134                         << glewGetErrorString(error_state) << ".");
135                 frame->Close();
136                 wxMessageBox(_("Error initializing GLEW\nFast preview window can not be opened."),_("Error"), wxOK | wxICON_ERROR);
137                 return;
138             }
139         }
140         // check the openGL capabilities.
141         if (!(GLEW_VERSION_1_1 && GLEW_ARB_multitexture))
142         {
143             started_creation=false;
144             wxConfigBase::Get()->Write(wxT("DisableOpenGL"), 1l);
145             wxConfigBase::Get()->Flush();
146             DEBUG_ERROR("Sorry, OpenGL 1.1 + GL_ARB_multitexture extension required.");
147             frame->Close();
148             wxMessageBox(_("Sorry, the fast preview window requires a system which supports OpenGL version 1.1 with the GL_ARB_multitexture extension.\nThe fast preview cannot be opened.\n\nHugin has been configured to start without fast preview.\nPlease restart Hugin."),_("Error"), wxOK | wxICON_ERROR);
149             return;
150         }
151 
152         setUp();
153 #ifdef __WXGTK__
154         if(this==frame->getPreview())
155         {
156             frame->getOverview()->setUp();
157         }
158         else
159         {
160             frame->getPreview()->setUp();
161         };
162 #endif
163     }
164 }
165 
setUp()166 void GLPreview::setUp()
167 {
168     DEBUG_DEBUG("Preview Setup");
169     if (m_toolsInitialized)
170     {
171         return;
172     };
173     m_toolsInitialized = true;
174     // we need something to store the state of the view and control updates
175     if (!m_view_state)
176     {
177         GLint countMultiTexture;
178         glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB,&countMultiTexture);
179         m_view_state = new ViewState(m_pano, countMultiTexture>1);
180     }
181     ++m_view_state_observer;
182     m_visualization_state = new VisualizationState(m_pano, m_view_state, this, RefreshWrapper, this, (PreviewMeshManager*) NULL);
183     //Start the tools going:
184     PreviewToolHelper *helper = new PreviewToolHelper(m_pano, m_visualization_state, frame);
185     m_tool_helper = static_cast<ToolHelper*>(helper);
186     frame->MakePreviewTools(helper);
187     // now make a renderer
188     m_renderer =  new GLPreviewRenderer(m_pano, m_view_state->GetTextureManager(),
189                                  m_visualization_state->GetMeshManager(),
190                                  m_visualization_state, helper, m_background_color);
191 }
192 
~GLOverview()193 GLOverview::~GLOverview()
194 {
195     if (m_renderer)
196     {
197         if (m_renderer == plane_m_renderer)
198         {
199             delete panosphere_m_tool_helper;
200             delete panosphere_m_renderer;
201             delete panosphere_m_visualization_state;
202         }
203         else
204         {
205             delete plane_m_tool_helper;
206             delete plane_m_renderer;
207             delete plane_m_visualization_state;
208         };
209     };
210 }
211 
setUp()212 void GLOverview::setUp()
213 {
214     DEBUG_DEBUG("Overview Setup");
215     if (m_toolsInitialized)
216     {
217         return;
218     };
219     m_toolsInitialized = true;
220     // we need something to store the state of the view and control updates
221     if (!m_view_state)
222     {
223         GLint countMultiTexture;
224         glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB,&countMultiTexture);
225         m_view_state = new ViewState(m_pano, countMultiTexture>1);
226     }
227     ++m_view_state_observer;
228 
229     panosphere_m_visualization_state = new PanosphereOverviewVisualizationState(m_pano, m_view_state, this, RefreshWrapper, this);
230     plane_m_visualization_state = new PlaneOverviewVisualizationState(m_pano, m_view_state, this, RefreshWrapper, this);
231 
232     m_visualization_state = panosphere_m_visualization_state;
233 
234     //Start the tools going:
235     panosphere_m_tool_helper = new PanosphereOverviewToolHelper(m_pano, panosphere_m_visualization_state, frame);
236     frame->MakePanosphereOverviewTools(panosphere_m_tool_helper);
237 
238     plane_m_tool_helper = new PlaneOverviewToolHelper(m_pano, plane_m_visualization_state, frame);
239     frame->MakePlaneOverviewTools(plane_m_tool_helper);
240 
241     // now make a renderer
242     panosphere_m_renderer =  new GLPanosphereOverviewRenderer(m_pano, m_view_state->GetTextureManager(),
243                                  panosphere_m_visualization_state->GetMeshManager(),
244                                  panosphere_m_visualization_state, panosphere_m_tool_helper, m_background_color);
245     plane_m_renderer =  new GLPlaneOverviewRenderer(m_pano, m_view_state->GetTextureManager(),
246                                  plane_m_visualization_state->GetMeshManager(),
247                                  plane_m_visualization_state, plane_m_tool_helper, m_background_color);
248 
249     switch(mode) {
250         case PANOSPHERE:
251             m_visualization_state = panosphere_m_visualization_state;
252             m_tool_helper = panosphere_m_tool_helper;
253             m_renderer = panosphere_m_renderer;
254             break;
255         case PLANE:
256             m_visualization_state = plane_m_visualization_state;
257             m_tool_helper = plane_m_tool_helper;
258             m_renderer = plane_m_renderer;
259             break;
260     }
261 }
262 
SetPhotometricCorrect(bool state)263 void GLViewer::SetPhotometricCorrect(bool state)
264 {
265     m_view_state->GetTextureManager()->SetPhotometricCorrect(state);
266     Refresh();
267 }
268 
SetLayoutMode(bool state)269 void GLViewer::SetLayoutMode(bool state)
270 {
271     m_visualization_state->GetMeshManager()->SetLayoutMode(state);
272     Refresh();
273 }
274 
SetLayoutScale(double scale)275 void GLViewer::SetLayoutScale(double scale)
276 {
277     m_visualization_state->GetMeshManager()->SetLayoutScale(scale);
278     Refresh();
279 }
280 
SetLayoutMode(bool state)281 void GLOverview::SetLayoutMode(bool state)
282 {
283     panosphere_m_visualization_state->GetMeshManager()->SetLayoutMode(state);
284     plane_m_visualization_state->GetMeshManager()->SetLayoutMode(state);
285     Refresh();
286 }
287 
SetLayoutScale(double scale)288 void GLOverview::SetLayoutScale(double scale)
289 {
290     panosphere_m_visualization_state->GetMeshManager()->SetLayoutScale(scale*MeshManager::PanosphereOverviewMeshInfo::scale_diff);
291     plane_m_visualization_state->GetMeshManager()->SetLayoutScale(scale);
292     Refresh();
293 }
294 
295 
RedrawE(wxPaintEvent & e)296 void GLViewer::RedrawE(wxPaintEvent& e)
297 {
298     if (!IsActive()) {
299         return;
300     }
301 
302     //TODO: CanResize specific to a viewer?
303     DEBUG_DEBUG("REDRAW_E");
304     if(!IsShown()) return;
305     // don't redraw during a redraw.
306     if (!(frame->CanResize())) {
307         DEBUG_DEBUG("RESIZE IN REDRAW");
308         frame->ContinueResize();
309         return;
310     }
311 
312     if (!redrawing)
313     {
314         DEBUG_DEBUG("REDRAW_E IN");
315         redrawing = true;
316         SetUpContext();
317         wxPaintDC dc(this); // we need this object on the stack to draw.
318         Redraw();
319         redrawing = false;
320     }
321     DEBUG_DEBUG("END OF REDRAW_E");
322 }
323 
RefreshWrapper(void * obj)324 void GLViewer::RefreshWrapper(void * obj)
325 {
326     DEBUG_DEBUG("REFRESH WRAPPER");
327     GLViewer* self = static_cast<GLViewer*>(obj);
328     self->Refresh();
329 }
330 
Resized(wxSizeEvent & e)331 void GLViewer::Resized(wxSizeEvent& e)
332 {
333 
334     if (!IsActive()) {
335         return;
336     }
337 
338     DEBUG_DEBUG("RESIZED_OUT");
339 
340     if (frame->CanResize()) {
341         DEBUG_DEBUG("RESIZED_IN");
342         if(!IsShown()) return;
343         // if we have a render at this point, tell it the new size.
344         DEBUG_DEBUG("RESIZED_IN_SHOWN");
345         if (m_renderer)
346         {
347           DEBUG_DEBUG("RESIZED_IN_RENDERER");
348           wxSize clientSize = GetClientSize();
349 #if defined __WXGTK3__ || defined __WXOSX__
350           m_scale = GetContentScaleFactor();
351           clientSize *= m_scale;
352 #endif
353           SetUpContext();
354           offset = m_renderer->Resize(clientSize.GetWidth(), clientSize.GetHeight());
355           Redraw();
356         };
357     }
358 }
359 
Redraw()360 void GLViewer::Redraw()
361 {
362     // get the renderer to redraw the OpenGL stuff
363     if(!m_renderer) return;
364     // don't redraw if we are in middle of a pending change of the panorama object
365     if(m_pano->hasPendingChanges()) return;
366     DEBUG_INFO("Rendering.");
367 
368     // we should use the window background colour outside the panorama
369     // FIXME shouldn't this work on textured backrounds?
370 #if defined __WXMAC__ && wxCHECK_VERSION(3,1,0)
371     wxColour col(128,128,128);
372 #else
373     wxColour col = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
374 #endif
375     m_renderer->SetBackground(col.Red(), col.Green(), col.Blue());
376     if (m_visualization_state->RequireRecalculateViewport())
377     {
378         // resize the viewport in case the panorama dimensions have changed.
379         wxSize clientSize = GetClientSize();
380 #if defined __WXGTK3__ || defined __WXOSX__
381         m_scale = GetContentScaleFactor();
382         clientSize *= m_scale;
383 #endif
384         offset = m_renderer->Resize(clientSize.GetWidth(), clientSize.GetHeight());
385     }
386     m_visualization_state->DoUpdates();
387     m_renderer->Redraw();
388     glFlush();
389     SwapBuffers();
390     // tell the view state we did all the updates and redrew.
391     m_visualization_state->FinishedDraw();
392     DEBUG_INFO("Finished Rendering.");
393 }
394 
OnEraseBackground(wxEraseEvent & e)395 void GLViewer::OnEraseBackground(wxEraseEvent& e)
396 {
397     // Do nothing, to avoid flashing on MSW
398 }
399 
MouseMotion(wxMouseEvent & e)400 void GLViewer::MouseMotion(wxMouseEvent& e)
401 {
402     if(m_renderer)
403         m_tool_helper->MouseMoved(hugin_utils::roundi(e.m_x * m_scale - offset.x),
404                               hugin_utils::roundi(e.m_y * m_scale - offset.y), e);
405 }
406 
MouseEnter(wxMouseEvent & e)407 void GLViewer::MouseEnter(wxMouseEvent & e)
408 {
409     if(m_renderer)
410         m_tool_helper->MouseEnter(hugin_utils::roundi(e.m_x * m_scale - offset.x),
411                               hugin_utils::roundi(e.m_y * m_scale - offset.y), e);
412 }
413 
MouseLeave(wxMouseEvent & e)414 void GLViewer::MouseLeave(wxMouseEvent & e)
415 {
416     if(m_renderer)
417         m_tool_helper->MouseLeave();
418 }
419 
MouseButtons(wxMouseEvent & e)420 void GLViewer::MouseButtons(wxMouseEvent& e)
421 {
422     if(m_renderer) {
423         //disregard non button events
424         if (e.IsButton()) {
425             m_tool_helper->MouseButtonEvent(e);
426         }
427     }
428 #ifdef __WXMSW__
429     //use normal mouse button processing of GLCanvas
430     //otherwise the mouse wheel is not working
431     e.Skip();
432 #endif
433 }
434 
MouseWheel(wxMouseEvent & e)435 void GLViewer::MouseWheel(wxMouseEvent& e)
436 {
437     if(m_renderer) {
438         m_tool_helper->MouseWheelEvent(e);
439     }
440 }
441 
442 
KeyDown(wxKeyEvent & e)443 void GLViewer::KeyDown(wxKeyEvent& e)
444 {
445     if(m_renderer)
446         m_tool_helper->KeypressEvent(e.GetKeyCode(), e.GetModifiers(), true);
447 }
448 
KeyUp(wxKeyEvent & e)449 void GLViewer::KeyUp(wxKeyEvent& e)
450 {
451     if(m_renderer)
452         m_tool_helper->KeypressEvent(e.GetKeyCode(), e.GetModifiers(), false);
453 }
454 
MarkToolsDirty()455 void GLViewer::MarkToolsDirty()
456 {
457     m_tool_helper->MarkDirty();
458 }
459 
SetViewerBackground(wxColour col)460 void GLViewer::SetViewerBackground(wxColour col)
461 {
462     this->m_background_color = col;
463     if(m_renderer)
464         m_renderer->SetPreviewBackgroundColor(col);
465 }
466 
SetMode(OverviewMode mode)467 void GLOverview::SetMode(OverviewMode mode)
468 {
469     this->mode = mode;
470     if (panosphere_m_renderer != 0 && plane_m_renderer != 0) {
471         switch(mode) {
472             case PANOSPHERE:
473                 m_visualization_state = panosphere_m_visualization_state;
474                 m_tool_helper = panosphere_m_tool_helper;
475                 m_renderer = panosphere_m_renderer;
476                 break;
477             case PLANE:
478                 m_visualization_state = plane_m_visualization_state;
479                 m_tool_helper = plane_m_tool_helper;
480                 m_renderer = plane_m_renderer;
481                 break;
482         }
483         this->Refresh();
484     }
485 }
486 
487 
488