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