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