1 // -*- c-basic-offset: 4 -*-
2 /** @file GLRenderer.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 <wx/wx.h>
24 #include <wx/platform.h>
25
26 #ifdef __WXMAC__
27 #include <OpenGL/gl.h>
28 #include <OpenGL/glu.h>
29 #else
30 #ifdef __WXMSW__
31 #include <vigra/windows.h>
32 #endif
33 #include <GL/gl.h>
34 #include <GL/glu.h>
35
36 #endif
37
38 #include "hugin_config.h"
39
40 #include "panoinc.h"
41
42 #include "TextureManager.h"
43 #include "MeshManager.h"
44 #include "ViewState.h"
45 #include "GLRenderer.h"
46 #include "GLViewer.h"
47 #include "ToolHelper.h"
48 #include <panodata/PanoramaOptions.h>
49
GLRenderer(const wxColour backgroundColour)50 GLRenderer::GLRenderer(const wxColour backgroundColour)
51 {
52 m_background_color = backgroundColour;
53 };
54
SetPreviewBackgroundColor(const wxColour c)55 void GLRenderer::SetPreviewBackgroundColor(const wxColour c)
56 {
57 m_background_color = c;
58 }
59
SetBackground(unsigned char red,unsigned char green,unsigned char blue)60 void GLRenderer::SetBackground(unsigned char red, unsigned char green, unsigned char blue)
61 {
62 glClearColor((float) red / 255.0, (float) green / 255.0, (float) blue / 255.0, 1.0);
63 }
64
~GLRenderer()65 GLRenderer::~GLRenderer()
66 {
67 }
68
GLPreviewRenderer(HuginBase::Panorama * pano,TextureManager * tex_man,MeshManager * mesh_man,VisualizationState * visualization_state,PreviewToolHelper * tool_helper,const wxColour backgroundColour)69 GLPreviewRenderer::GLPreviewRenderer(HuginBase::Panorama *pano, TextureManager *tex_man,
70 MeshManager *mesh_man, VisualizationState *visualization_state,
71 PreviewToolHelper *tool_helper,const wxColour backgroundColour) : GLRenderer(backgroundColour)
72 {
73 m_pano = pano;
74 m_tex_man = tex_man;
75 m_mesh_man = mesh_man;
76 m_visualization_state = visualization_state;
77 m_tool_helper = tool_helper;
78 }
79
GLPanosphereOverviewRenderer(HuginBase::Panorama * pano,TextureManager * tex_man,MeshManager * mesh_man,PanosphereOverviewVisualizationState * visualization_state,PanosphereOverviewToolHelper * tool_helper,const wxColour backgroundColour)80 GLPanosphereOverviewRenderer::GLPanosphereOverviewRenderer(HuginBase::Panorama *pano, TextureManager *tex_man,
81 MeshManager *mesh_man, PanosphereOverviewVisualizationState *visualization_state,
82 PanosphereOverviewToolHelper *tool_helper, const wxColour backgroundColour) : GLRenderer(backgroundColour)
83 {
84 m_pano = pano;
85 m_tex_man = tex_man;
86 m_mesh_man = mesh_man;
87 m_visualization_state = visualization_state;
88 m_tool_helper = tool_helper;
89 }
90
GLPlaneOverviewRenderer(HuginBase::Panorama * pano,TextureManager * tex_man,MeshManager * mesh_man,PlaneOverviewVisualizationState * visualization_state,PlaneOverviewToolHelper * tool_helper,const wxColour backgroundColour)91 GLPlaneOverviewRenderer::GLPlaneOverviewRenderer(HuginBase::Panorama *pano, TextureManager *tex_man,
92 MeshManager *mesh_man, PlaneOverviewVisualizationState *visualization_state,
93 PlaneOverviewToolHelper *tool_helper, const wxColour backgroundColour) : GLRenderer(backgroundColour)
94 {
95
96 m_pano = pano;
97 m_tex_man = tex_man;
98 m_mesh_man = mesh_man;
99 m_visualization_state = visualization_state;
100 m_tool_helper = tool_helper;
101 }
102
Resize(int in_width,int in_height)103 vigra::Diff2D GLPreviewRenderer::Resize(int in_width, int in_height)
104 {
105 width = in_width;
106 height = in_height;
107 glViewport(0, 0, width, height);
108 // notify viewstate about current canvas size,
109 // maybe needed for a changed image center
110 m_visualization_state->SetCanvasSize(vigra::Size2D(width, height));
111 // we use the view_state rather than the panorama to allow interactivity.
112 HuginBase::PanoramaOptions *options = m_visualization_state->getViewState()->GetOptions();
113 width_o = options->getWidth();
114 height_o = options->getHeight();
115 double aspect_screen = double(width) / double (height),
116 aspect_pano = width_o / height_o;
117 glMatrixMode(GL_PROJECTION);
118 glLoadIdentity();
119 double scale;
120 if (aspect_screen < aspect_pano)
121 {
122 // the panorama is wider than the screen
123 scale = width_o / width;
124 } else {
125 // the screen is wider than the panorama
126 scale = height_o / height;
127 }
128 hugin_utils::FDiff2D center(0.5, 0.5);
129 if (!m_visualization_state->GetMeshManager()->GetLayoutMode())
130 {
131 // apply scale and translate only when not in layout mode
132 scale /= m_visualization_state->GetZoomLevel();
133 center = m_visualization_state->GetViewingCenter();
134 };
135 const hugin_utils::FDiff2D lookAt((center.x - 0.5) * width_o, (center.y - 0.5) * height_o);
136
137 double x_offs = (scale * double(width) - width_o) / 2.0,
138 y_offs = (scale * double(height) - height_o) / 2.0;
139 // set up the projection, so we can use panorama coordinates.
140 glOrtho(-x_offs, width * scale - x_offs,
141 height * scale - y_offs, -y_offs,
142 -1.0, 1.0);
143 glTranslatef(-lookAt.x, -lookAt.y, 0);
144 // scissor to the panorama.
145 glScissor((x_offs-lookAt.x) / scale, (y_offs+lookAt.y) / scale,
146 width_o / scale, height_o / scale);
147 glMatrixMode(GL_MODELVIEW);
148 // tell the view state the region we are displaying.
149 // TODO add support for zooming and panning.
150 m_visualization_state->SetVisibleArea(vigra::Rect2D((center.x-0.5) * width_o, (center.y-0.5) * height_o,
151 (0.5 + center.x) * width_o, (0.5 + center.y) * height_o));
152 m_visualization_state->SetScale(1.0 / scale);
153
154 // return the offset from the top left corner of the viewpoer to the top left
155 // corner of the panorama.
156 return vigra::Diff2D(int (x_offs / scale), int (y_offs / scale));
157 }
158
Redraw()159 void GLPreviewRenderer::Redraw()
160 {
161 glClear(GL_COLOR_BUFFER_BIT);
162 glEnable(GL_SCISSOR_TEST);
163 m_tex_man->DisableTexture();
164 // background color of flat pano preview
165 glColor3f((float)m_background_color.Red()/255, (float)m_background_color.Green()/255, (float)m_background_color.Blue()/255);
166 glBegin(GL_QUADS);
167 glVertex2f(0.0, 0.0);
168 glVertex2f(width_o, 0.0);
169 glVertex2f(width_o, height_o);
170 glVertex2f(0.0, height_o);
171 glEnd();
172 glColor3f(1.0, 1.0, 1.0);
173 // draw things under the preview images
174 // draw each active image.
175 int imgs = m_pano->getNrOfImages();
176 // offset by a half a pixel
177 glPushMatrix();
178 glTranslatef(0.5, 0.5, 0.0);
179 glEnable(GL_TEXTURE_2D);
180 m_tex_man->Begin();
181 m_tool_helper->BeforeDrawImages();
182 // The old preview shows the lowest numbered image on top, so do the same:
183 for (int img = imgs - 1; img != -1; img--)
184 {
185 // only draw active images
186 if (m_pano->getImage(img).getActive())
187 {
188 // the tools can cancel drawing of images.
189 if (m_tool_helper->BeforeDrawImageNumber(img))
190 {
191 // the texture manager may need to call the display list
192 // multiple times with blending, so we pass it the display list
193 // rather than switching to the texture and then calling the
194 // list ourselves.
195 m_tex_man->DrawImage(img, m_mesh_man->GetDisplayList(img));
196 m_tool_helper->AfterDrawImageNumber(img);
197 }
198 }
199 }
200 m_tex_man->End();
201 // drawn things after the active image.
202 m_tool_helper->AfterDrawImages();
203 m_tex_man->DisableTexture();
204 glPopMatrix();
205 // darken the cropped out range, except in layout mode
206 if (!m_visualization_state->GetMeshManager()->GetLayoutMode())
207 {
208 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
209 glEnable(GL_BLEND);
210 glColor4f(0.0, 0.0, 0.0, 0.5);
211 // construct a strip of quads, with each pair being one of the corners.
212 const vigra::Rect2D roi = m_visualization_state->getViewState()->GetOptions()->getROI();
213 glBegin(GL_QUAD_STRIP);
214 glVertex2f(0.0, 0.0); glVertex2i(roi.left(), roi.top());
215 glVertex2f(width_o, 0.0); glVertex2i(roi.right(), roi.top());
216 glVertex2f(width_o, height_o); glVertex2i(roi.right(), roi.bottom());
217 glVertex2f(0.0, height_o); glVertex2i(roi.left(), roi.bottom());
218 glVertex2f(0.0, 0.0); glVertex2i(roi.left(), roi.top());
219 glEnd();
220 // draw lines around cropped area.
221 // we want to invert the color to make it stand out.
222 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
223 glColor3f(1.0, 1.0, 1.0);
224 glBegin(GL_LINE_LOOP);
225 glVertex2i(roi.left(), roi.top());
226 glVertex2i(roi.right(), roi.top());
227 glVertex2i(roi.right(), roi.bottom());
228 glVertex2i(roi.left(), roi.bottom());
229 glEnd();
230 glDisable(GL_BLEND);
231 };
232 glEnable(GL_TEXTURE_2D);
233
234 glDisable(GL_SCISSOR_TEST);
235 }
236
237
Redraw()238 void GLPanosphereOverviewRenderer::Redraw()
239 {
240 glClearColor(0,0,0,1);
241 glClear(GL_COLOR_BUFFER_BIT);
242
243 glMatrixMode(GL_MODELVIEW);
244
245 glLoadIdentity();
246
247 double R = m_visualization_state->getR();
248 double angx = m_visualization_state->getAngX();
249 double angy = m_visualization_state->getAngY();
250
251 gluLookAt(R * cos(angy) * cos(angx), R * sin(angy), R * cos(angy) * sin(angx), 0, 0, 0, 0, 1, 0);
252 //for look from inside
253 // gluLookAt(0,0,0,R * cos(angy) * cos(angx), R * sin(angy), R * cos(angy) * sin(angx), 0, 1, 0);
254
255 // draw things under the preview images
256 // draw each active image.
257 int imgs = m_pano->getNrOfImages();
258 // offset by a half a pixel
259 glPushMatrix();
260
261 //draw the rectangle around the sphere
262 glColor3f(0.5, 0.5, 0.5);
263
264 double side = 150;
265 glBegin(GL_LINE_LOOP);
266
267 glVertex3f(-side,side,0);
268 glVertex3f(side,side,0);
269 glVertex3f(side,-side,0);
270 glVertex3f(-side,-side,0);
271
272 glEnd();
273
274 //draw the axes, to give a sense of orientation
275 double axis = 200;
276 glBegin(GL_LINES);
277
278 glColor3f(1,0,0);
279 glVertex3f(-axis,0,0);
280 glVertex3f(axis,0,0);
281
282 glColor3f(0,1,0);
283 glVertex3f(0,0,0);
284 glVertex3f(0,axis,0);
285
286 glColor3f(0,0,1);
287 glVertex3f(0,0,0);
288 glVertex3f(0,0,axis);
289
290 glEnd();
291
292
293 glEnable(GL_TEXTURE_2D);
294
295 //To avoid z-order fight of the images if depth buffer is used, depth buffer is disabled and meshes are drawn twice,
296 //first with back faces culled so that the inner face of the sphere is visible and below the outter face,
297 //and afterwards the meshes are drawn again with the front faces culled
298
299 glEnable(GL_CULL_FACE);
300 glCullFace(GL_BACK);
301
302 //event called only before drawing of the images with front faces culled (the inner face of the panosphere)
303 static_cast<PanosphereOverviewToolHelper*>(m_tool_helper)->BeforeDrawImagesBack();
304 //generic draw before images are drawn (called twice with front and back faces culled)
305 m_tex_man->Begin();
306 m_tool_helper->BeforeDrawImages();
307
308 // The old preview shows the lowest numbered image on top, so do the same:
309 for (int img = imgs - 1; img != -1; img--)
310 {
311 // only draw active images
312 if (m_pano->getImage(img).getActive())
313 {
314 // the tools can cancel drawing of images.
315 if (m_tool_helper->BeforeDrawImageNumber(img))
316 {
317 // the texture manager may need to call the display list
318 // multiple times with blending, so we pass it the display list
319 // rather than switching to the texture and then calling the
320 // list ourselves.
321 m_tex_man->DrawImage(img, m_mesh_man->GetDisplayList(img));
322 m_tool_helper->AfterDrawImageNumber(img);
323 }
324 }
325 }
326
327 m_tex_man->End();
328 m_tool_helper->AfterDrawImages();
329 m_tex_man->DisableTexture();
330 static_cast<PanosphereOverviewToolHelper*>(m_tool_helper)->AfterDrawImagesBack();
331
332 // #ifdef __WXGTK__
333 //// glCullFace(GL_BACK);
334 //// glPushMatrix();
335 //// glRotated(90,1,0,0);
336 ////// if (imgs > 0) {
337 ////// glEnable( GL_TEXTURE_2D );
338 ////// glEnable(GL_BLEND);
339 ////// glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
340 ////// glColor4f(0.5,0.5,0.5,0.5);
341 ////// GLUquadric* grid = gluNewQuadric();
342 ////// gluQuadricTexture(grid, GL_TRUE);
343 ////// m_tex_man->BindTexture(0);
344 ////// gluSphere(grid, 101,40,20);
345 ////// glDisable(GL_BLEND);
346 ////// } else {
347 //// glColor4f(0.5,0.5,0.5,0.5);
348 //// glutWireSphere(101,40,20);
349 ////// }
350 //// glPopMatrix();
351 // #endif
352
353 glMatrixMode(GL_MODELVIEW);
354 glCullFace(GL_FRONT);
355
356 static_cast<PanosphereOverviewToolHelper*>(m_tool_helper)->BeforeDrawImagesFront();
357
358 glEnable(GL_TEXTURE_2D);
359 m_tex_man->Begin();
360 m_tool_helper->BeforeDrawImages();
361
362 // The old preview shows the lowest numbered image on top, so do the same:
363 for (int img = imgs - 1; img != -1; img--)
364 {
365 // only draw active images
366 if (m_pano->getImage(img).getActive())
367 {
368 // the tools can cancel drawing of images.
369 if (m_tool_helper->BeforeDrawImageNumber(img))
370 {
371 // the texture manager may need to call the display list
372 // multiple times with blending, so we pass it the display list
373 // rather than switching to the texture and then calling the
374 // list ourselves.
375 m_tex_man->DrawImage(img, m_mesh_man->GetDisplayList(img));
376 m_tool_helper->AfterDrawImageNumber(img);
377 }
378 }
379 }
380
381 m_tex_man->End();
382 // drawn things after the active image.
383 m_tool_helper->AfterDrawImages();
384 m_tex_man->DisableTexture();
385 static_cast<PanosphereOverviewToolHelper*>(m_tool_helper)->AfterDrawImagesFront();
386
387 m_tex_man->DisableTexture();
388
389 glDisable(GL_CULL_FACE);
390
391 glPopMatrix();
392 }
393
Resize(int w,int h)394 vigra::Diff2D GLPanosphereOverviewRenderer::Resize(int w, int h)
395 {
396
397 width = w;
398 height = h;
399 glViewport(0, 0, width, height);
400 // we use the view_state rather than the panorama to allow interactivity.
401 HuginBase::PanoramaOptions *options = m_visualization_state->GetOptions();
402 width_o = options->getWidth();
403 height_o = options->getHeight();
404
405 //since gluPerspective needs vertical field of view, depending on the aspect ratio we convert from vertical to horizontal FOV if needed
406 double fov = m_visualization_state->getFOV();
407 double fovy;
408 if (h > w) {
409 fovy = 2.0 * atan( tan(fov * M_PI / 360.0) * (float) h / (float) w) / M_PI * 180.0;
410 } else {
411 fovy = fov;
412 }
413
414 float ratio = 1.0* w / h;
415 // aspect = ratio;
416 glMatrixMode(GL_PROJECTION);
417 glLoadIdentity();
418 glViewport(0, 0, w, h);
419 gluPerspective(fovy,ratio,1,1000000);
420
421 m_visualization_state->SetVisibleArea(vigra::Rect2D(0, 0, options->getWidth(),
422 options->getHeight()));
423
424 //calculate the scale depending on the section of the panosphere in the center of the screen
425 double R = m_visualization_state->getR();
426 double radius = m_visualization_state->getSphereRadius();
427 //height of the screen in screen pixels over the length of the panosphere in panorama pixels when spread out
428 double scrscale = (float) h / (2 * tan(fovy / 360.0 * M_PI) * (R - radius) / (2 * radius * M_PI) * (options->getWidth()));
429 m_visualization_state->SetScale(scrscale);
430 m_visualization_state->GetViewer()->MarkToolsDirty();
431 // DEBUG_DEBUG("renderer " << scrscale << " " << h << " " << R << " " << fovy);
432 // DEBUG_DEBUG("renderer scale " << scrscale);
433
434 // return vigra::Diff2D(w / 2, h / 2);
435 return vigra::Diff2D(0,0);
436
437 }
438
Redraw()439 void GLPlaneOverviewRenderer::Redraw()
440 {
441 // background color of mosaic plane
442 glClearColor((float)m_background_color.Red()/255, (float)m_background_color.Green()/255, (float)m_background_color.Blue()/255,1.0);
443
444 glClear(GL_COLOR_BUFFER_BIT);
445
446 glMatrixMode(GL_MODELVIEW);
447
448 glLoadIdentity();
449
450 double R = m_visualization_state->getR();
451
452 double X = m_visualization_state->getX();
453 double Y = m_visualization_state->getY();
454
455 gluLookAt(X,Y,R, X, Y, 0, 0, 1, 0);
456
457 // draw things under the preview images
458 m_tool_helper->BeforeDrawImages();
459 int imgs = m_pano->getNrOfImages();
460 glPushMatrix();
461
462 glColor3f(0.5,0.5,0.5);
463 double side = 150;
464 glBegin(GL_LINE_LOOP);
465
466 glVertex3f(-side,side,0);
467 glVertex3f(side,side,0);
468 glVertex3f(side,-side,0);
469 glVertex3f(-side,-side,0);
470
471 glEnd();
472
473 double axis = 200;
474 glBegin(GL_LINES);
475
476 glColor3f(1,0,0);
477 glVertex3f(-axis,0,0);
478 glVertex3f(axis,0,0);
479
480 glColor3f(0,1,0);
481 glVertex3f(0,0,0);
482 glVertex3f(0,axis,0);
483
484 glColor3f(0,0,1);
485 glVertex3f(0,0,0);
486 glVertex3f(0,0,axis);
487
488 glEnd();
489
490
491 glEnable(GL_TEXTURE_2D);
492
493 m_tex_man->Begin();
494 // The old preview shows the lowest numbered image on top, so do the same:
495 for (int img = imgs - 1; img != -1; img--)
496 {
497 // only draw active images
498 if (m_pano->getImage(img).getActive())
499 {
500 // the tools can cancel drawing of images.
501 if (m_tool_helper->BeforeDrawImageNumber(img))
502 {
503 // the texture manager may need to call the display list
504 // multiple times with blending, so we pass it the display list
505 // rather than switching to the texture and then calling the
506 // list ourselves.
507 m_tex_man->DrawImage(img, m_mesh_man->GetDisplayList(img));
508 m_tool_helper->AfterDrawImageNumber(img);
509 }
510 }
511 }
512
513 m_tex_man->End();
514 m_tool_helper->AfterDrawImages();
515
516 m_tex_man->DisableTexture();
517
518 glMatrixMode(GL_MODELVIEW);
519
520 glEnable(GL_TEXTURE_2D);
521 m_tex_man->Begin();
522
523 m_tool_helper->BeforeDrawImages();
524 // The old preview shows the lowest numbered image on top, so do the same:
525 for (int img = imgs - 1; img != -1; img--)
526 {
527 // only draw active images
528 if (m_pano->getImage(img).getActive())
529 {
530 // the tools can cancel drawing of images.
531 if (m_tool_helper->BeforeDrawImageNumber(img))
532 {
533 // the texture manager may need to call the display list
534 // multiple times with blending, so we pass it the display list
535 // rather than switching to the texture and then calling the
536 // list ourselves.
537 m_tex_man->DrawImage(img, m_mesh_man->GetDisplayList(img));
538 m_tool_helper->AfterDrawImageNumber(img);
539 }
540 }
541 }
542
543 m_tex_man->End();
544 // drawn things after the active image.
545 m_tool_helper->AfterDrawImages();
546 m_tex_man->DisableTexture();
547
548 glPopMatrix();
549 }
550
551
Resize(int w,int h)552 vigra::Diff2D GLPlaneOverviewRenderer::Resize(int w, int h)
553 {
554 width = w;
555 height = h;
556 glViewport(0, 0, width, height);
557 // we use the view_state rather than the panorama to allow interactivity.
558 HuginBase::PanoramaOptions *options = m_visualization_state->getViewState()->GetOptions();
559 width_o = options->getWidth();
560 height_o = options->getHeight();
561
562 glMatrixMode(GL_PROJECTION);
563 glLoadIdentity();
564
565 double fov = m_visualization_state->getFOV();
566 double fovy;
567 if (h > w) {
568 fovy = 2.0 * atan( tan(fov * M_PI / 360.0) * (float) h / (float) w) / M_PI * 180.0;
569 } else {
570 fovy = fov;
571 }
572
573 float ratio = 1.0* w / h;
574 // aspect = ratio;
575 glMatrixMode(GL_PROJECTION);
576 glLoadIdentity();
577 gluPerspective(fovy,ratio,1,1000000);
578
579 m_visualization_state->SetVisibleArea(vigra::Rect2D(0, 0, options->getWidth(),
580 options->getHeight()));
581
582 double R = m_visualization_state->getR();
583 double scrscale = (float) h / (2 * tan(fovy / 360.0 * M_PI) * R * options->getWidth() / MeshManager::PlaneOverviewMeshInfo::scale);
584 m_visualization_state->SetScale(scrscale);
585 // m_visualization_state->SetGLScale(gl_scale);
586 m_visualization_state->GetViewer()->MarkToolsDirty();
587
588 return vigra::Diff2D(0,0);
589 }
590
591