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