1 // -*- c-basic-offset: 4 -*-
2 
3 /** @file ViewState.cpp
4  *
5  *  @author James Legg
6  *  @author Darko Makreshanski
7  *
8  *  This program is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This software is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public
19  *  License along with this software. If not, see
20  *  <http://www.gnu.org/licenses/>.
21  *
22  */
23 
24 #ifdef __WXMAC__
25 #include "panoinc_WX.h"
26 #include "panoinc.h"
27 #endif
28 
29 #include "ViewState.h"
30 #include "MeshManager.h"
31 
ViewState(HuginBase::Panorama * pano,bool supportMultiTexture)32 ViewState::ViewState(HuginBase::Panorama *pano, bool supportMultiTexture)
33 {
34 
35 
36     m_pano = pano;
37     m_multiTexture=supportMultiTexture;
38     m_pano->addObserver(this);
39     // we will need to update everything for this panorama.
40     dirty_image_sizes = true;
41     images_removed = true;
42     dirty_photometrics = true;
43     number_of_images = m_pano->getNrOfImages();
44     for (unsigned int img = 0; img < number_of_images; img++)
45     {
46         img_states[img] = m_pano->getSrcImage(img);
47         dirty_mask[img].val = false;
48     }
49     opts = m_pano->getOptions();
50     projection_info = new OutputProjectionInfo(&opts);
51     // now set the texture manager up.
52     m_tex_manager = new TextureManager(m_pano, this);
53 }
54 
~ViewState()55 ViewState::~ViewState()
56 {
57     m_pano->removeObserver(this);
58     delete projection_info;
59     delete m_tex_manager;
60 }
61 
62 
panoramaChanged(HuginBase::Panorama & pano)63 void ViewState::panoramaChanged(HuginBase::Panorama &pano)
64 {
65 
66     bool require_draw=false;
67 
68     // anything could have happened, check everything.
69     HuginBase::PanoramaOptions new_opts  = m_pano->getOptions();
70     SetOptions(&new_opts);
71     unsigned int imgs = m_pano->getNrOfImages();
72     for (unsigned int img = 0; img < imgs; img++)
73     {
74         HuginBase::SrcPanoImage new_image = m_pano->getSrcImage(img);
75         SetSrcImage(img, &new_image);
76         // has the enabled state changed in the preview?
77         bool new_active = m_pano->getImage(img).getActive();
78         if (new_active != active[img])
79         {
80             require_draw = true;
81             active[img] = new_active;
82         }
83     }
84     // has the number of images changed?
85     if (imgs < number_of_images)
86     {
87         // we've lost some
88         dirty_image_sizes = true;
89         require_draw = true;
90         images_removed = true;
91     } else if (imgs > number_of_images)
92     {
93         // added images. Assume it doesn't affect the rest.
94         dirty_image_sizes = true;
95         require_draw = true;
96         // FIXME more might need to be done, if the new images are not the last
97         // ones in order of image number.
98     }
99     number_of_images = imgs;
100 
101     if (require_draw) {
102         //refresh function is called in the respective VisualizationState callback
103         for (std::map<VisualizationState*,bool>::iterator it = vis_states.begin() ; it != vis_states.end() ; ++it) {
104             DEBUG_DEBUG("PanoChanged - iterator before");
105             if (it->second) {
106                 DEBUG_DEBUG("PanoChanged - iterator after");
107                 it->first->Redraw();
108             }
109         }
110     }
111 
112 }
113 
panoramaImagesChanged(HuginBase::Panorama &,const HuginBase::UIntSet &)114 void ViewState::panoramaImagesChanged(HuginBase::Panorama&,
115                                       const HuginBase::UIntSet&)
116 {
117     // actually this stuff is handled by panoramaChanged.
118 }
119 
SetOptions(const HuginBase::PanoramaOptions * new_opts)120 void ViewState::SetOptions(const HuginBase::PanoramaOptions *new_opts)
121 {
122 
123     bool dirty_projection = false;
124 
125     for (std::map<VisualizationState*,bool>::iterator it = vis_states.begin() ; it != vis_states.end() ; ++it) {
126 
127         if (!(it->second)) continue;
128 
129         // compare the options
130         if (   new_opts->getSize() != opts.getSize()
131             || new_opts->getProjection() != opts.getProjection()
132             || new_opts->getProjectionParameters() != opts.getProjectionParameters()
133             || new_opts->getHFOV() != opts.getHFOV()
134             || new_opts->getVFOV() != opts.getVFOV()
135            )
136         {
137             // output projection changed. All images' meshes need recalculating.
138             unsigned int imgs = m_pano->getNrOfImages();
139             for (unsigned int img = 0; img < imgs; img++)
140             {
141                 it->first->SetDirtyMesh(img);
142             }
143             // we should also change the viewport to fit new the dimensions.
144             dirty_projection = true;
145             it->first->SetDirtyViewport();
146             it->first->ForceRequireRedraw();
147         }
148         if ((new_opts->outputExposureValue != opts.outputExposureValue) ||
149             (new_opts->outputRangeCompression != opts.outputRangeCompression))
150         {
151             // output exposure changed. All image photometrics are now different.
152             it->first->SetDirtyViewport();
153             dirty_photometrics = true;
154         }
155         if (   new_opts->getROI() != opts.getROI()
156            )
157         {
158             // this is all done every frame anyway.
159             it->first->ForceRequireRedraw();
160         }
161 
162         it->first->SetOptions(new_opts);
163 
164     }
165     // store the new options
166     opts = *new_opts;
167     if (dirty_projection)
168     {
169         // we need to update the projection info as well.
170         delete projection_info;
171         projection_info = 0;
172         projection_info = new OutputProjectionInfo(&opts);
173     }
174 }
175 
SetSrcImage(unsigned int image_nr,HuginBase::SrcPanoImage * new_img)176 void ViewState::SetSrcImage(unsigned int image_nr, HuginBase::SrcPanoImage *new_img)
177 {
178     bool dirty_mesh = false;
179     bool dirty_draw = false;
180 
181     if (number_of_images <= image_nr)
182     {
183         // this must be an addition, since we didn't have this many images.
184         dirty_mesh = true;
185         dirty_image_sizes = true;
186         dirty_draw = true;
187     } else {
188         // compare the options
189         HuginBase::SrcPanoImage *img = &img_states[image_nr];
190         // if the filename has changed, something has probably been deleted
191         if (new_img->getFilename() != img->getFilename())
192         {
193             images_removed = true;
194             // since we use image numbers to identify meshes and images,
195             // we can't really tell what happened.
196         }
197         // has the projection changed?
198         if (   new_img->getRoll() != img->getRoll()
199             || new_img->getPitch() != img->getPitch()
200             || new_img->getYaw() != img->getYaw()
201             || new_img->getX() != img->getX()
202             || new_img->getY() != img->getY()
203             || new_img->getZ() != img->getZ()
204             || new_img->getHFOV() != img->getHFOV()
205             || new_img->getProjection() != img->getProjection()
206             || new_img->getShear() != img->getShear()
207             || new_img->getRadialDistortionCenterShift()
208                                        == img->getRadialDistortionCenterShift()
209             || new_img->getRadialDistortion() != img->getRadialDistortion()
210             || new_img->getCropRect() != img->getCropRect()
211            )
212         {
213             dirty_mesh = true;
214             dirty_draw = true;
215 //            dirty_mesh[image_nr].val = true;
216             // the field of view affects the image size calculations.
217             if (new_img->getHFOV() != img->getHFOV())
218             {
219                 dirty_image_sizes = true;
220             }
221         }
222         // photometric adjustments
223         if (   new_img->getVigCorrMode() != img->getVigCorrMode()
224             || new_img->getRadialVigCorrCoeff() != img->getRadialVigCorrCoeff()
225             || new_img->getRadialVigCorrCenterShift() !=
226                                               img->getRadialVigCorrCenterShift()
227             || new_img->getExposureValue() != img->getExposureValue()
228             || new_img->getGamma() != img->getGamma()
229             || new_img->getWhiteBalanceRed() != img->getWhiteBalanceRed()
230             || new_img->getWhiteBalanceBlue() != img->getWhiteBalanceBlue()
231             || new_img->getResponseType() != img->getResponseType()
232            )
233         {
234             // real time photometric correction just needs a redraw.
235             /* full photometric correction will be redone automatically by the
236                TextureManager next redraw.                                    */
237             /* FIXME only white balance and exposure are actually used for
238                real-time photometric correction. */
239             dirty_draw = true;
240         }
241         // mask stuff
242         if(new_img->getActiveMasks() != img->getActiveMasks())
243         {
244             dirty_mask[image_nr].val = true;
245             dirty_draw=true;
246         };
247     }
248     // store the new options
249     img_states[image_nr] = *new_img;
250 
251 
252     for (std::map<VisualizationState*,bool>::iterator it = vis_states.begin() ; it != vis_states.end() ; ++it) {
253         if (!(it->second)) continue;
254         if (dirty_draw) it->first->ForceRequireRedraw();
255         if (dirty_mesh) it->first->SetDirtyMesh(image_nr);
256         it->first->SetSrcImage(image_nr, new_img);
257     }
258 
259 }
260 
ForceRequireRedraw()261 void ViewState::ForceRequireRedraw()
262 {
263     // this is generally called by preview tools. We let them manage themselves.
264     // often they give some user interface thing that doesn't reflect a change
265     // in the panorama at all, so we let them force a redraw.
266     for (std::map<VisualizationState*,bool>::iterator it = vis_states.begin() ; it != vis_states.end() ; ++it) {
267         if (!(it->second)) continue;
268         it->first->ForceRequireRedraw();
269     }
270 }
271 
272 
GetOptions()273 HuginBase::PanoramaOptions *ViewState::GetOptions()
274 {
275     return &opts;
276 }
277 
GetProjectionInfo()278 OutputProjectionInfo *ViewState::GetProjectionInfo()
279 {
280     return projection_info;
281 }
282 
GetSrcImage(unsigned int image_nr)283 HuginBase::SrcPanoImage *ViewState::GetSrcImage(unsigned int image_nr)
284 {
285     return &img_states[image_nr];
286 }
287 
288 
RequireRecalculateImageSizes()289 bool ViewState::RequireRecalculateImageSizes()
290 {
291     return dirty_image_sizes;
292 }
293 
RequireRecalculatePhotometric()294 bool ViewState::RequireRecalculatePhotometric()
295 {
296     return dirty_photometrics;
297 }
298 
ImagesRemoved()299 bool ViewState::ImagesRemoved()
300 {
301     return images_removed;
302 }
303 
RequireRecalculateMasks(unsigned int image_nr)304 bool ViewState::RequireRecalculateMasks(unsigned int image_nr)
305 {
306     if (number_of_images > image_nr)
307     {
308         return dirty_mask[image_nr].val;
309     }
310     return false;
311 }
312 
313 
FinishedDraw()314 void ViewState::FinishedDraw()
315 {
316     // update our copy of the state and clear all the dirty flags.
317     number_of_images = m_pano->getNrOfImages();
318     img_states.clear();
319     active.clear();
320     for (unsigned int img = 0; img < number_of_images; img++)
321     {
322         img_states[img] = m_pano->getSrcImage(img);
323         active[img] = m_pano->getImage(img).getActive();
324     }
325     opts = m_pano->getOptions();
326 
327     Clean();
328 }
329 
Clean()330 void ViewState::Clean()
331 {
332     dirty_image_sizes = false;
333     images_removed = false;
334     dirty_photometrics = false;
335     dirty_mask.clear();
336 }
337 
DoUpdates()338 void ViewState::DoUpdates()
339 {
340     DEBUG_DEBUG("VIEW STATE DO UPDATES");
341     m_tex_manager->CheckUpdate();
342     DEBUG_DEBUG("VIEW STATE END DO UPDATES");
343 }
344 
Redraw()345 void ViewState::Redraw()
346 {
347 
348     for (std::map<VisualizationState*,bool>::iterator it = vis_states.begin() ; it != vis_states.end() ; ++it) {
349 
350         if (!(it->second)) continue;
351 
352         it->first->Redraw();
353 
354     }
355 
356 
357 }
358 
359 
360 
361 
362 
363 
364 
365 
~VisualizationState()366 VisualizationState::~VisualizationState()
367 {
368     m_view_state->vis_states[this] = false;
369     delete m_mesh_manager;
370 }
371 
372 
GetScale()373 float VisualizationState::GetScale()
374 {
375     return scale;
376 }
377 
SetScale(float scale_in)378 void VisualizationState::SetScale(float scale_in)
379 {
380     scale = scale_in;
381     // When resizing the window this can make the level detail of existing
382     // meshes be too high or low, but we don't want to do to much calculation
383     // so limit the forced recalculation of meshes to significant changes.
384     if (genscale == 0.0)
385     {
386         // should only happen the first time it is used. In which case we will
387         // regenerate the meshes anyways.
388         genscale = scale;
389     } else {
390         double difference = scale > genscale ?
391                scale / genscale : genscale / scale;
392         if (difference > 1.25)
393         {
394             genscale = scale;
395             unsigned int number_of_images = m_pano->getNrOfImages();
396             for (unsigned int img = 0; img < number_of_images; img++)
397             {
398                 dirty_mesh[img].val = true;
399             }
400         }
401     }
402 }
403 
404 
Redraw()405 void VisualizationState::Redraw()
406 {
407     DEBUG_DEBUG("REDRAW OUT");
408     if (RequireDraw())
409     {
410         DEBUG_DEBUG("REDRAW IN");
411         RefreshFunc(refreshArg);
412     }
413 }
414 
RequireRecalculateViewport()415 bool VisualizationState::RequireRecalculateViewport()
416 {
417     return dirty_viewport;
418 }
419 
RequireRecalculateMesh(unsigned int image_nr)420 bool VisualizationState::RequireRecalculateMesh (unsigned int image_nr)
421 {
422 
423     unsigned int number_of_images = m_pano->getNrOfImages();
424     if (number_of_images > image_nr)
425     {
426         return dirty_mesh[image_nr].val;
427     }
428     // if we didn't think there were enough images, create the mesh for the
429     //   first time
430     return true;
431 }
432 
433 
RequireDraw()434 bool VisualizationState::RequireDraw()
435 {
436     return (dirty_draw);
437 }
438 
ForceRequireRedraw()439 void VisualizationState::ForceRequireRedraw()
440 {
441     dirty_draw = true;
442 }
443 
FinishedDraw()444 void VisualizationState::FinishedDraw()
445 {
446     DEBUG_DEBUG("VIS State Finished draw");
447     dirty_mesh.clear();
448     dirty_viewport = false;
449     dirty_draw = false;
450     m_view_state->FinishedDraw();
451 }
452 
DoUpdates()453 void VisualizationState::DoUpdates()
454 {
455     DEBUG_DEBUG("BEGIN UPDATES");
456     m_view_state->DoUpdates();
457     DEBUG_DEBUG("END UPDATES");
458     m_mesh_manager->CheckUpdate();
459     DEBUG_DEBUG("END UPDATES");
460 }
461 
GetMeshDisplayList(unsigned int image_number)462 unsigned int VisualizationState::GetMeshDisplayList(unsigned int image_number)
463 {
464     return m_mesh_manager->GetDisplayList(image_number);
465 }
466 
467 
GetOptions()468 HuginBase::PanoramaOptions * VisualizationState::GetOptions()
469 {
470     return m_view_state->GetOptions();
471 }
472 
GetProjectionInfo()473  OutputProjectionInfo * VisualizationState::GetProjectionInfo()
474 {
475     return m_view_state->GetProjectionInfo();
476 }
477 
GetSrcImage(unsigned int image_nr)478 HuginBase::SrcPanoImage * VisualizationState::GetSrcImage(unsigned int image_nr)
479 {
480     return m_view_state->GetSrcImage(image_nr);
481 }
482 
SetZoomLevel(const float new_zoom)483 void VisualizationState::SetZoomLevel(const float new_zoom)
484 {
485     // remember new zoom
486     // make sure that zoom remains in valid range
487     m_zoom = std::min(std::max(new_zoom, 1.0f), 50.0f);
488     // update center
489     SetViewingCenter(m_lookAt);
490 }
491 
SetViewingCenter(const hugin_utils::FDiff2D & center)492 void VisualizationState::SetViewingCenter(const hugin_utils::FDiff2D& center)
493 {
494     // set new center
495     // range check so that there no unnecessary black border remains
496     // we need the actual zoom factor, the width/height of the panorama
497     // and the width/height of the window
498     const double offset = 0.5 / m_zoom;
499     vigra::Size2D panoSize=m_view_state->GetOptions()->getSize();
500     double offsetx = offset;
501     double offsety = offset;
502     if (panoSize.width()*m_canvasSize.height() < m_canvasSize.width() * panoSize.height())
503     {
504         offsetx += ((double)m_canvasSize.width() / (double)m_canvasSize.height() * panoSize.height() / panoSize.width() - 1.0) / (2.0 * m_zoom);
505         offsetx = std::min(offsetx, 0.5);
506     }
507     else
508     {
509         offsety += ((double)m_canvasSize.height() / (double)m_canvasSize.width() * panoSize.width() / panoSize.height() - 1.0) / (2.0 * m_zoom);
510         offsety = std::min(offsety, 0.5);
511     };
512     m_lookAt = hugin_utils::simpleClipPoint(center, hugin_utils::FDiff2D(offsetx, offsety), hugin_utils::FDiff2D(1 - offsetx, 1 - offsety));
513 }
514 
SetCanvasSize(const vigra::Size2D & canvasSize)515 void VisualizationState::SetCanvasSize(const vigra::Size2D& canvasSize)
516 {
517     m_canvasSize = canvasSize;
518     // update center
519     SetViewingCenter(m_lookAt);
520 }
521 
PanosphereOverviewVisualizationState(HuginBase::Panorama * pano,ViewState * view_state,GLViewer * viewer,void (* RefreshFunction)(void *),void * arg)522 PanosphereOverviewVisualizationState::PanosphereOverviewVisualizationState(HuginBase::Panorama* pano, ViewState* view_state, GLViewer * viewer, void (*RefreshFunction)(void*), void *arg)
523         : OverviewVisualizationState(pano, view_state, viewer, RefreshFunction, arg, (PanosphereOverviewMeshManager*) NULL)
524 {
525     scale = 1;
526 
527     angx = M_PI / 2.0;
528     angy = 0;
529     fov = 40;
530     R = 500;
531     sphere_radius = 100;
532 
533     opts = (*(m_view_state->GetOptions()));
534     opts.setProjection(HuginBase::PanoramaOptions::EQUIRECTANGULAR);
535     opts.setHFOV(360.0);
536     opts.setVFOV(180.0);
537     projection_info = new OutputProjectionInfo(&opts);
538 }
539 
~PanosphereOverviewVisualizationState()540 PanosphereOverviewVisualizationState::~PanosphereOverviewVisualizationState()
541 {
542     delete projection_info;
543 }
544 
GetOptions()545 HuginBase::PanoramaOptions * PanosphereOverviewVisualizationState::GetOptions()
546 {
547     return &opts;
548 }
549 
GetProjectionInfo()550 OutputProjectionInfo *PanosphereOverviewVisualizationState::GetProjectionInfo()
551 {
552     return projection_info;
553 }
554 
SetOptions(const HuginBase::PanoramaOptions * new_opts)555 void PanosphereOverviewVisualizationState::SetOptions(const HuginBase::PanoramaOptions * new_opts)
556 {
557     opts = *new_opts;
558     opts.setProjection(HuginBase::PanoramaOptions::EQUIRECTANGULAR);
559     opts.setHFOV(360.0);
560     opts.setVFOV(180.0);
561     delete projection_info;
562     projection_info = new OutputProjectionInfo(&opts);
563 }
564 
setAngX(double angx_in)565 void PanosphereOverviewVisualizationState::setAngX(double angx_in)
566 {
567     angx = angx_in;
568     dirty_draw = true;
569 }
570 
setAngY(double angy_in)571 void PanosphereOverviewVisualizationState::setAngY(double angy_in)
572 {
573     angy = angy_in;
574     dirty_draw = true;
575 }
576 
577 
PlaneOverviewVisualizationState(HuginBase::Panorama * pano,ViewState * view_state,GLViewer * viewer,void (* RefreshFunction)(void *),void * arg)578 PlaneOverviewVisualizationState::PlaneOverviewVisualizationState(HuginBase::Panorama* pano, ViewState* view_state, GLViewer * viewer, void(*RefreshFunction)(void*), void *arg)
579         : OverviewVisualizationState(pano, view_state, viewer, RefreshFunction, arg, (PlaneOverviewMeshManager*) NULL)
580 {
581 
582     scale = 1;
583 
584     fov = 60;
585     R = 500;
586     X = 0;
587     Y = 0;
588 
589     opts = (*(m_view_state->GetOptions()));
590     opts.setProjection(HuginBase::PanoramaOptions::RECTILINEAR);
591     //TODO: hfov and vfov need to be divided into values for the output and values for the visualization
592     opts.setHFOV(90.0);
593     opts.setVFOV(90.0);
594     projection_info = new OutputProjectionInfo(&opts);
595 }
596 
~PlaneOverviewVisualizationState()597 PlaneOverviewVisualizationState::~PlaneOverviewVisualizationState()
598 {
599     delete projection_info;
600 }
601 
GetOptions()602 HuginBase::PanoramaOptions * PlaneOverviewVisualizationState::GetOptions()
603 {
604     return &opts;
605 }
606 
GetProjectionInfo()607 OutputProjectionInfo *PlaneOverviewVisualizationState::GetProjectionInfo()
608 {
609     return projection_info;
610 }
611 
SetOptions(const HuginBase::PanoramaOptions * new_opts)612 void PlaneOverviewVisualizationState::SetOptions(const HuginBase::PanoramaOptions * new_opts)
613 {
614     opts = *new_opts;
615     opts.setProjection(HuginBase::PanoramaOptions::RECTILINEAR);
616     opts.setHFOV(90.0);
617     opts.setVFOV(90.0);
618     delete projection_info;
619     projection_info = new OutputProjectionInfo(&opts);
620 }
621 
622 
623