ATLAS Offline Software
Loading...
Searching...
No Matches
VP1CameraHelper.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
6// //
7// Implementation of class VP1CameraHelper //
8// //
9// Author: Thomas Kittelmann <Thomas.Kittelmann@cern.ch> //
10// //
11// Initial version: July 2007 //
12// //
14
17#include "VP1Base/VP1Msg.h"
19
20#include <Inventor/SbSphere.h>
21
22#include <Inventor/nodes/SoNode.h>
23#include <Inventor/nodes/SoCamera.h>
24#include <Inventor/nodes/SoPerspectiveCamera.h>
25#include <Inventor/nodes/SoOrthographicCamera.h>
26#include <Inventor/nodes/SoGroup.h>
27
28#include <Inventor/actions/SoSearchAction.h>
29#include <Inventor/actions/SoGetBoundingBoxAction.h>
30#include <Inventor/actions/SoGetMatrixAction.h>
31
32#include <Inventor/sensors/SoTimerSensor.h>
33
34#include <QDir>
35#include <QFile>
36#include <QPixmap>
37
38#include <map>
39#include <iostream>
40
41//____________________________________________________________________
43public:
45 SoCamera * camera;
46 SoGroup * sceneroot;
47 QByteArray targetCamState;
49 static void seeksensorCB(void * data, SoSensor *);
50 void getCameraCoordinateSystem(SoCamera * cameraarg,SoNode * root,
51 SbMatrix & matrix,SbMatrix & inverse);
52
53 // Seek functionality
54 SoTimerSensor * seeksensor;
56 SbBool inseekmode;
60 //
63 //
69
72
73 //Movie making mode:
74// SoQtRenderArea * renderArea; // original
76 QString outputdir;
77 QString prefix;
78 double fps;
80 int iframe;
81 int width;
82 int height;
83
84 //Variables to detect if we should abort, if e.g. the user started
85 //changing the view by hand:
90 void updateLastVars();
91 bool lastParsChanged() const;
92
93 void startSeekTimer( double duration_in_secs );
94
95 //
96 SoSearchAction * searchaction;
97 SoGetMatrixAction * matrixaction;
98
99 //For keeping track of current seeks and whether the camera is invalidated while seeking:
100 int32_t camera_ref;
101 bool instance_invalid;//This will be set if the camera/sceneroot ref count changes during the lifetime of the VP1CameraHelper.
102 bool instanceInvalid();
103
104 bool getCameraParametersForBBox( const SbBox3f & box,
105 SoSFVec3f & position, SoSFFloat & nearDistance,
106 SoSFFloat & farDistance, SoSFFloat & focalDistance,
107 bool&isPerspective,
108 SoSFFloat & height /*Target height in case of orthographic camera*/);
109
110 //Convenience method for construction camera orientation from
111 //vectors giving view and "up" directions:
112 static SbRotation cameraOrientation( SbVec3f dir, SbVec3f up);
113
114 static std::map<SoCamera*,VP1CameraHelper*> helpers;//All helpers register themselves here.
115 //This allows new instances working on the same camera to abort the old ones.
116
117 //zoom to persistified state:
118 void actual_animatedZoomToCameraState( const QByteArray& camstate,
119 double duration_in_secs );
120
121 //Imitate the zoom towards a point (similar to the ones initiated by the user in an examiner viewer):
122 void actual_animatedZoomToPoint(SbVec3f,double duration_in_secs = 1.0 );
123
124 //For the next three methods, slack < 1.0 gives tighter zoom and slack > 1.0 gives looser zoom.
125 //The first two are more reliable:
126 void actual_animatedZoomToPath(SoPath * path,double duration_in_secs = 1.0, double slack = 1.0,
127 const SbVec3f& lookat = SbVec3f(999,999,999), const SbVec3f& upvec = SbVec3f(999,999,999) );
128 void actual_animatedZoomToBBox(const SbBox3f& box,double duration_in_secs = 1.0, double slack = 1.0,
129 const SbVec3f& lookat = SbVec3f(999,999,999), const SbVec3f& upvec = SbVec3f(999,999,999) );
130
131 //Dont use the following if subtreeroot appears multiple times in your scenegraph:
132 void actual_animatedZoomToSubTree(SoNode*subtreeroot,double duration_in_secs = 1.0, double slack = 1.0,
133 const SbVec3f& lookat = SbVec3f(999,999,999), const SbVec3f& upvec = SbVec3f(999,999,999) );
134
135 void die(bool abnormal = true)
136 {
137 VP1Msg::messageVerbose("VP1CameraHelper::Imp::die(abnormal="+VP1Msg::str(abnormal)+")");
138 if (seeksensor&&seeksensor->isScheduled())
139 seeksensor->unschedule();
140 if (abnormal)
141 theclass->animationFinishedAbnormally();
142 else
143 theclass->animationFinished();
144 theclass->deleteLater();
145 }
146
147};
148
149//____________________________________________________________________
151{
152 std::map<SoCamera*,VP1CameraHelper*>::iterator it = Imp::helpers.find(m_d->camera);
153 if (it!=Imp::helpers.end())
154 Imp::helpers.erase(it);
155 m_d->instance_invalid = true;
156 m_d->camera->unrefNoDelete();
157 m_d->camera = 0;
158}
159
160std::map<SoCamera*,VP1CameraHelper*> VP1CameraHelper::Imp::helpers;
161
162//____________________________________________________________________
163//void VP1CameraHelper::setOutputImagesMode( SoQtRenderArea * ra,
165 const QString& outputdir,
166 int width,
167 int height,
168 double fps,
169 const QString& prefix )
170{
171 if (!ra) {
172 VP1Msg::messageDebug("VP1CameraHelper::setOutputImagesMode ERROR: Bad input. Null render area.");
173 return;
174 }
175 if (fps<1) {
176 VP1Msg::messageDebug("VP1CameraHelper::setOutputImagesMode ERROR: Bad input. Bad FPS = "+QString::number(fps));
177 return;
178 }
179 if (!QDir(outputdir).exists()) {
180 VP1Msg::messageDebug("VP1CameraHelper::setOutputImagesMode ERROR: Bad input. Output dir "+outputdir+" does not exist.");
181 return;
182 }
183 if (width<1||width>4000||height<1||height>4000) {
184 VP1Msg::messageDebug("VP1CameraHelper::setOutputImagesMode ERROR: Bad input. Bad dimensions: "
185 +QString::number(width)+"x"+QString::number(height));
186 return;
187 }
188 if (prefix.isEmpty()) {
189 VP1Msg::messageDebug("VP1CameraHelper::setOutputImagesMode ERROR: Bad input. Empty prefix.");
190 return;
191 }
192 m_d->fps = fps;
193 m_d->outputdir = outputdir;
194 m_d->width = width;
195 m_d->height = height;
196 m_d->prefix = prefix;
197 m_d->renderArea = ra;
198}
199
200
201//____________________________________________________________________
202VP1CameraHelper::VP1CameraHelper(SoCamera * camera, SoGroup * sceneroot)
203 : QObject(0), m_d(new Imp)
204{
205 m_d->theclass=this;
206 m_d->camera=camera;
207 m_d->forceCircular = false;
208 if (camera) {
209 std::map<SoCamera*,VP1CameraHelper*>::iterator it = Imp::helpers.find(camera);
210 if (it!=Imp::helpers.end())
211 it->second->forceAbort();
212 Imp::helpers[camera] = this;
213 }
214 m_d->sceneroot=sceneroot;
215
216 m_d->fps=-1;
217 m_d->ntotframes = -1;
218 m_d->width = 100;
219 m_d->height = 100;
220 m_d->iframe = -1;
221 m_d->renderArea = 0;
222
223 m_d->seekdistance = 50.0f;
224 m_d->seekdistanceabs = FALSE;
225 m_d->seektopoint = TRUE;
226 m_d->seekperiod = 2.0f;
227 m_d->inseekmode = FALSE;
228 m_d->seeksensor = new SoTimerSensor(Imp::seeksensorCB, m_d);
229
230 m_d->searchaction = new SoSearchAction;
231 m_d->matrixaction = new SoGetMatrixAction(SbViewportRegion(100,100));
232
233 m_d->camera->ref();
234 m_d->sceneroot->ref();
235 m_d->camera_ref=m_d->camera->getRefCount();
236 m_d->instance_invalid = false;
237
238 if (!camera||!sceneroot)
239 m_d->instance_invalid=true;
240
241}
242
243
244//____________________________________________________________________
246{
247 if (m_d->seeksensor->isScheduled())
248 m_d->seeksensor->unschedule();
249 delete m_d->seeksensor;
250 m_d->searchaction->reset(); delete m_d->searchaction;
251 delete m_d->matrixaction;
252 SoCamera * cam = m_d->camera;
253 if (m_d->sceneroot)
254 m_d->sceneroot->unref();
255 delete m_d; m_d=0;
256 if (cam) {
257 std::map<SoCamera*,VP1CameraHelper*>::iterator it = Imp::helpers.find(cam);
258 if (it!=Imp::helpers.end())
259 Imp::helpers.erase(it);
260 cam->unref();
261 }
262}
263
264//____________________________________________________________________
266{
267 std::map<SoCamera*,VP1CameraHelper*>::iterator it = Imp::helpers.find(camera);
268 VP1CameraHelper* helper(0);
269 if (it!=Imp::helpers.end()) {
270 helper = it->second;
271 }
272 if (helper)
273 helper->forceAbort();
274}
275
276
277//____________________________________________________________________
279{
280 last_cameraposition=camera->position.getValue();
281 last_cameraorient = camera->orientation.getValue();
283 last_camera_ortho_height=static_cast<SoOrthographicCamera*>(camera)->height;
285}
286
287//____________________________________________________________________
289{
290 if (camera->position.getValue()!=last_cameraposition
291 ||camera->orientation.getValue()!=last_cameraorient)
292 return true;
294 &&static_cast<SoOrthographicCamera*>(camera)->height!=last_camera_ortho_height)
295 return true;
296 return false;
297}
298
299//____________________________________________________________________
300//STATIC:
301VP1CameraHelper * VP1CameraHelper::animatedZoomToCameraState( SoCamera * camera, SoGroup * sceneroot,
302 const QByteArray& camstate,
303 double duration_in_secs, double clipVolPercent, double lastClipVolPercent, bool varySpeed,
304 bool forceCircular )
305{
306 // std::cout<<"VP1CameraHelper::animatedZoomToCameraState - clipVol%="<<clipVolPercent<<" lastClipVolPercent"<<lastClipVolPercent<<std::endl;
307 // std::cout<<"clipVol_lastPercentage%="<<d->clipVol_lastPercentage<<", clipVol_percentage%="<<d->clipVol_percentage
308 // <<", clipVol_startPercentage%="<<d->clipVol_startPercentage<<", clipVol_endPercentage%="<<d->clipVol_endPercentage<<std::endl;
309 VP1CameraHelper * helper = new VP1CameraHelper(camera,sceneroot);
310 helper->m_d->actual_animatedZoomToCameraState( camstate,duration_in_secs );
311 helper->m_d->varySpeed=varySpeed;
312 helper->m_d->clipVol_startPercentage=lastClipVolPercent;
313 helper->m_d->clipVol_percentage=lastClipVolPercent;
314 helper->m_d->clipVol_endPercentage=clipVolPercent;
315 helper->m_d->forceCircular = forceCircular;
316 return helper;
317}
318
319//____________________________________________________________________
321 double duration_in_secs )
322{
323 if (!camera||camstate==QByteArray()||duration_in_secs<0.0) {
324 die();
325 return;
326 }
327
328 if (instanceInvalid()) {
329 if (seeksensor->isScheduled())
330 seeksensor->unschedule();
331 die();
332 return;
333 }
334
335 targetCamState = camstate;
336
337 const bool perspective(camera->getTypeId().isDerivedFrom(SoPerspectiveCamera::getClassTypeId()));
338 SoCamera * targetcam = perspective ? static_cast<SoCamera*>(new SoPerspectiveCamera)
339 : static_cast<SoCamera*>(new SoOrthographicCamera);
340 targetcam->ref();
341 QByteArray ba = camstate;
343 targetcam->unref();
344 die();
345 return;
346 }
347
348 camera_isperspective = perspective;
349 camerastartposition = camera->position.getValue();
350 camerastartorient = camera->orientation.getValue();
351 camerastart_nearDistance = camera->nearDistance.getValue();
352 camerastart_farDistance = camera->farDistance.getValue();
353 camerastart_focalDistance = camera->focalDistance.getValue();
354 cameraendposition = targetcam->position.getValue();
355 cameraendorient = targetcam->orientation.getValue();
356 cameraend_nearDistance = targetcam->nearDistance.getValue();
357 cameraend_farDistance = targetcam->farDistance.getValue();
358 cameraend_focalDistance = targetcam->focalDistance.getValue();
359 if (!perspective) {
360 camerastart_ortho_height = static_cast<SoOrthographicCamera*>(camera)->height.getValue();
361 cameraend_ortho_height = static_cast<SoOrthographicCamera*>(targetcam)->height.getValue();
362 }
363 targetcam->unref();
364
365 startSeekTimer( duration_in_secs );
366}
367
368
369//____________________________________________________________________
370//STATIC:
371VP1CameraHelper* VP1CameraHelper::animatedZoomToPath( SoCamera * camera, SoGroup * sceneroot,
372 SoPath * path,double duration_in_secs, double /*clipVolPercent*/, double slack,
373 const SbVec3f& lookat, const SbVec3f& upvec, bool varySpeed,
374 bool forceCircular )
375{
376 VP1CameraHelper * helper = new VP1CameraHelper(camera,sceneroot);
377 helper->m_d->actual_animatedZoomToPath( path,duration_in_secs, slack, lookat, upvec );
378 helper->m_d->varySpeed=varySpeed;
379 helper->m_d->forceCircular = forceCircular;
380 return helper;
381}
382
383//____________________________________________________________________
384void VP1CameraHelper::Imp::actual_animatedZoomToPath( SoPath * path,double duration_in_secs, double slack,
385 const SbVec3f& lookat, const SbVec3f& upvec )
386{
387 if (!path||instanceInvalid()) {
388 die();
389 return;
390 }
391
392 if (slack<=0.0||duration_in_secs<0.0) {
393 die();
394 return;
395 }
396
397 if (instanceInvalid()) {
398 if (seeksensor->isScheduled())
399 seeksensor->unschedule();
400 die();
401 return;
402 }
403 SbViewportRegion dummyvp;
404 SoGetBoundingBoxAction mybboxaction(dummyvp);
405 mybboxaction.apply(path);
406 SbBox3f box = mybboxaction.getBoundingBox();
407
408 actual_animatedZoomToBBox(box, duration_in_secs, slack, lookat,upvec);
409}
410
411//____________________________________________________________________
412//STATIC:
413VP1CameraHelper * VP1CameraHelper::animatedZoomToSubTree(SoCamera * camera, SoGroup * sceneroot,
414 SoNode*subtreeroot,double duration_in_secs, double /* clipVolPercent*/, double /*lastClipVolPercent*/, double slack,
415 const SbVec3f& lookat, const SbVec3f& upvec, bool varySpeed,
416 bool forceCircular )
417{
418 VP1CameraHelper * helper = new VP1CameraHelper(camera,sceneroot);
419 helper->m_d->actual_animatedZoomToSubTree( subtreeroot,duration_in_secs,slack,lookat,upvec );
420 helper->m_d->varySpeed=varySpeed;
421 helper->m_d->forceCircular = forceCircular;
422 return helper;
423}
424
425//____________________________________________________________________
426void VP1CameraHelper::Imp::actual_animatedZoomToSubTree( SoNode*subtreeroot,double duration_in_secs, double slack,
427 const SbVec3f& lookat, const SbVec3f& upvec)
428{
429 if (!subtreeroot||instanceInvalid()) {
430 die();
431 return;
432 }
433
434 if (slack<=0.0||duration_in_secs<0.0) {
435 die();
436 return;
437 }
438
439 if (instanceInvalid()) {
440 die();
441 return;
442 }
443
444 SoSearchAction mysearchaction;
445 mysearchaction.setNode(subtreeroot);
446 mysearchaction.apply(sceneroot);
447 SoPath * mypath = mysearchaction.getPath();
448 if (!mypath) {
449 VP1Msg::messageDebug("VP1CameraHelper WARNING: Cancelled operation due to failure to locate path to node.");
450 return;
451 }
452 mypath->ref();
453 actual_animatedZoomToPath( mypath, duration_in_secs, slack, lookat,upvec );
454 mypath->unref();
455 mysearchaction.reset();
456}
457
458//____________________________________________________________________
459//STATIC:
460VP1CameraHelper * VP1CameraHelper::animatedZoomToBBox(SoCamera * camera, SoGroup * sceneroot,
461 const SbBox3f& box,double duration_in_secs, double /*clipVolPercent*/, double slack,
462 const SbVec3f& lookat, const SbVec3f& upvec , bool varySpeed,
463 bool forceCircular )
464{
465
466 VP1CameraHelper * helper = new VP1CameraHelper(camera,sceneroot);
467 helper->m_d->actual_animatedZoomToBBox( box,duration_in_secs,slack,lookat,upvec );
468 helper->m_d->varySpeed=varySpeed;
469 helper->m_d->forceCircular = forceCircular;
470 return helper;
471}
472
473//____________________________________________________________________
474void VP1CameraHelper::Imp::actual_animatedZoomToBBox( const SbBox3f& box,double duration_in_secs, double slack,
475 const SbVec3f& lookat, const SbVec3f& upvec )
476{
477 if (instanceInvalid()) {
478 die();
479 return;
480 }
481
482 if (slack<=0.0||duration_in_secs<0.0) {
483 die();
484 return;
485 }
486
487 if (instanceInvalid()) {
488 die();
489 return;
490 }
491
492 if (box.isEmpty()) {
493 die();
494 return;
495 }
496 SbBox3f actualbbox = box;
497 if (slack!=1.0) {
498 float minx, miny, minz, maxx, maxy, maxz;
499 actualbbox.getBounds (minx, miny, minz, maxx, maxy, maxz);
500 float dx(maxx-minx), cx(0.5*(maxx+minx));
501 float dy(maxy-miny), cy(0.5*(maxy+miny));
502 float dz(maxz-minz), cz(0.5*(maxz+minz));
503 actualbbox.setBounds (cx-0.5*slack*dx, cy-0.5*slack*dy, cz-0.5*slack*dz,
504 cx+0.5*slack*dx, cy+0.5*slack*dy, cz+0.5*slack*dz);
505 }
506
507
508
509 SoSFVec3f target_position;
510 SoSFFloat target_nearDistance;
511 SoSFFloat target_farDistance;
512 SoSFFloat target_focalDistance;
513 bool currentcamera_isperspective;
514 SoSFFloat target_ortho_height;
515
516 camerastartorient = camera->orientation.getValue();
517
518 bool changeorient(false);
519 if (lookat==SbVec3f(999,999,999)&&upvec==SbVec3f(999,999,999)) {
520 cameraendorient = camerastartorient;//Same orientation
521 } else {
522 changeorient=true;
523 //At least one of view direction and upwards direction is set by user
524 SbVec3f actual_lookat(lookat);
525 if (lookat==SbVec3f(999,999,999)) {
526 //Use present view direction of camera
527 SbRotation camrot = camera->orientation.getValue();
528 actual_lookat = SbVec3f(0.0f, 0.0f, -1.0f); // init to default view direction vector
529 camrot.multVec(actual_lookat, actual_lookat);
530 }
531 SbVec3f actual_upvec(upvec);
532 if (upvec==SbVec3f(999,999,999)) {
533 //Use present upwards direction of camera
534 SbRotation camrot = camera->orientation.getValue();
535 actual_upvec = SbVec3f(0.0f, 1.0f, 0.0f); // init to default up vector
536 camrot.multVec(actual_upvec, actual_upvec);
537 // { float x,y,z; actual_upvec.getValue(x,y,z);
538 // //std::cout<<"USE PRESENT CAMERA UPVEC ( "<<x<<", "<<y<<", "<<z<<" )"<<std::endl;
539 // }
540 }
541 //Use actual_lookat and actual_upvec to construct an orientation
542 //of the camera (refer to the SoCamera doc. for how it should be
543 //defined):
544 cameraendorient = Imp::cameraOrientation(actual_lookat,actual_upvec);
545 }
546
547
548 if (changeorient)
549 camera->orientation.setValue(cameraendorient);
550 if (!getCameraParametersForBBox( actualbbox,
551 target_position, target_nearDistance,
552 target_farDistance, target_focalDistance,
553 currentcamera_isperspective,target_ortho_height)) {
554 die();
555 return;
556 }
557 camerastartposition = camera->position.getValue();
558 cameraendposition = target_position.getValue();
559
560 camerastart_nearDistance = camera->nearDistance.getValue();
561 cameraend_nearDistance = target_nearDistance;
562 camerastart_farDistance = camera->farDistance.getValue();
563 cameraend_farDistance = target_farDistance;
564 camerastart_focalDistance = camera->focalDistance.getValue();
565 cameraend_focalDistance = target_focalDistance;
566 camera_isperspective = currentcamera_isperspective;
568 camerastart_ortho_height = 1.0f;//Irrelevant value since not needed (we could cast to orthographic camera and get the real value).
570 } else {
571 SoOrthographicCamera * orthocam =static_cast<SoOrthographicCamera*>(camera);
572 if (!orthocam) {
573 if (changeorient)
574 camera->orientation.setValue(camerastartorient);
575 die();
576 return;
577 }
578 camerastart_ortho_height = orthocam->height.getValue();
579 cameraend_ortho_height = target_ortho_height;
580 }
581 if (changeorient)
582 camera->orientation.setValue(camerastartorient);
583
584 //Start seek:
585
586 startSeekTimer(duration_in_secs);
587}
588
589
590//____________________________________________________________________
591//STATIC:
592VP1CameraHelper * VP1CameraHelper::animatedZoomToPoint(SoCamera * camera, SoGroup * sceneroot,
593 SbVec3f targetpoint,double duration_in_secs, double /*clipVolPercent*/, bool varySpeed,
594 bool forceCircular )
595{
596 VP1CameraHelper * helper = new VP1CameraHelper(camera,sceneroot);
597 helper->m_d->actual_animatedZoomToPoint( targetpoint,duration_in_secs );
598 helper->m_d->varySpeed=varySpeed;
599 helper->m_d->forceCircular = forceCircular;
600 return helper;
601}
602
603//____________________________________________________________________
604void VP1CameraHelper::Imp::startSeekTimer( double duration_in_secs )
605{
606 seekperiod = static_cast<float>(duration_in_secs);
607
608 if (seeksensor->isScheduled())
609 seeksensor->unschedule();
610
611 seeksensor->setBaseTime(SbTime::getTimeOfDay());
612 seeksensor->schedule();
613
615
616}
617
618//____________________________________________________________________
619void VP1CameraHelper::Imp::actual_animatedZoomToPoint( SbVec3f targetpoint,double duration_in_secs )
620{
621 if (instanceInvalid()) {
622 die();
623 return;
624 }
625
626 if (duration_in_secs<0.0) {
627 die();
628 return;
629 }
630
631 if (instanceInvalid()) {
632 die();
633 return;
634 }
635
636 camerastartposition = camera->position.getValue();
637 camerastartorient = camera->orientation.getValue();
638
639 //The following variables are not needed in seekToPoint mode, so
640 //give them values indicating they should not be updated:
641
642 camerastart_nearDistance = camera->nearDistance.getValue();
644 camerastart_farDistance = camera->farDistance.getValue();
646 camerastart_focalDistance = camera->focalDistance.getValue();
648 //It actually doesnt matter if we set this one:
649 camera_isperspective=!(camera->getTypeId() == SoOrthographicCamera::getClassTypeId());
650 camerastart_ortho_height = 1.0f;//Irrelevant value since not needed (we could cast to orthographic camera and get the real value).
652
653 // move point to the camera coordinate system, consider
654 // transformations before camera in the scene graph
655 SbMatrix cameramatrix, camerainverse;
657 sceneroot,
658 cameramatrix,
659 camerainverse);
660 camerainverse.multVecMatrix(targetpoint, targetpoint);
661 float fd = seekdistance;
662 if (!seekdistanceabs)
663 fd *= (targetpoint - camera->position.getValue()).length()/100.0f;
664 camera->focalDistance = fd;
665
666 SbVec3f dir = targetpoint - camerastartposition;
667 dir.normalize();
668
669 // find a rotation that rotates current camera direction into new
670 // camera direction.
671 SbVec3f olddir;
672 camera->orientation.getValue().multVec(SbVec3f(0, 0, -1), olddir);
673 SbRotation diffrot(olddir, dir);
674 cameraendposition = targetpoint - fd * dir;
675 cameraendorient = camera->orientation.getValue() * diffrot;
676
677 startSeekTimer(duration_in_secs);
678}
679
680
681//____________________________________________________________________
682void VP1CameraHelper::Imp::seeksensorCB(void * data, SoSensor * s)
683{
684 VP1Msg::messageVerbose("VP1CameraHelper::Imp::seeksensorCB()");
685
687 if (!d||!s)
688 return;
689 if (d->instanceInvalid() || d->lastParsChanged()) {
690 d->die();
691 return;
692 }
693
694 SoTimerSensor * sensor = static_cast<SoTimerSensor *>(s);
695
696 float t(0);
697
698 if (d->fps>0) {
699 //fixed number of frames and output
700 if (d->ntotframes<0) {
701 //first time: figure out total number of frames
702 d->ntotframes = std::max(1,static_cast<int>((d->fps)*(d->seekperiod)+0.5));
703 d->iframe = 0;
704 }
705 //Example: 5 total frames should give times 0.2, 0.4, 0.6, 0.8,
706 //1.0. We should _not_ include 0.0 here. Otherwise chained zooms
707 //will get a frame at both the old 1.0 and the new 0.0.
708 t = ( (d->iframe+1==d->ntotframes) ? 1.0f : static_cast<float>((d->iframe+1)*1.0/(d->ntotframes*1.0)) );
709 ++(d->iframe);
710 } else {
711 //normal animation:
712 SbTime currenttime = SbTime::getTimeOfDay();
713 t = float((currenttime - sensor->getBaseTime()).getValue()) / d->seekperiod;
714 }
715
716 if ((t > 1.0f) || (d->fps<=0&&(t + sensor->getInterval().getValue()/d->seekperiod > 1.0f))) t = 1.0f;
717
718 d->clipVol_percentage = d->clipVol_startPercentage+(d->clipVol_endPercentage - d->clipVol_startPercentage)*t;
719 emit d->theclass->clipVolumePercentageOfATLAS(d->clipVol_percentage);
720
721 bool end = (t == 1.0f);
722
723 if (d->varySpeed)
724 t = (float) ((1.0 - cos(M_PI*t)) * 0.5);//Map t to slow down motion in the beginning and end of the animation.
725
726 if (d->camera->position.getValue()!=d->cameraendposition) {
727 double rBegin=d->camerastartposition.length();
728 double rEnd=d->cameraendposition.length();
729 double lengthRatio = rEnd/rBegin;
730 SbVec3f u = d->camerastartposition; u.normalize();
731 SbVec3f v = d->cameraendposition; v.normalize();
732 double angle = acos(u.dot(v));
733 if (d->forceCircular||(lengthRatio>0.99 && lengthRatio<1.01 && angle<0.95*M_PI && angle>0.05*M_PI)) {
734 double r = rBegin + (rEnd-rBegin)*t;
735 SbVec3f w = u.cross(v);
736 SbRotation R(w,t*angle);
737 SbVec3f z;
738 R.multVec(u,z);
739 z*=r;
740 d->camera->position=z;
741 } else {
742 d->camera->position = d->camerastartposition + (d->cameraendposition - d->camerastartposition) * t;
743 }
744 }
745 if (d->camera->orientation.getValue()!=d->cameraendorient)
746 d->camera->orientation = SbRotation::slerp(d->camerastartorient,d->cameraendorient,t);
747 //TKTEST: Comment out the next lines to fix camera??>
748 //Other thing to try: Something about disable notify while setting multiple pars??
749
750 //TEST if (d->camera->nearDistance!=d->cameraend_nearDistance)
751 //TEST d->camera->nearDistance.setValue(d->camerastart_nearDistance.getValue()*(1-t) + d->cameraend_nearDistance.getValue()*t);
752 //TEST if (d->camera->farDistance!=d->cameraend_farDistance)
753 //TEST d->camera->farDistance.setValue(d->camerastart_farDistance.getValue()*(1-t) + d->cameraend_farDistance.getValue()*t);
754 if (d->camera->focalDistance!=d->cameraend_focalDistance)
755 d->camera->focalDistance.setValue(d->camerastart_focalDistance.getValue()*(1-t) + d->cameraend_focalDistance.getValue()*t);
756 if (!d->camera_isperspective) {
757 SoOrthographicCamera * orthocam =static_cast<SoOrthographicCamera*>(d->camera);
758 if (orthocam&&orthocam->height!=d->cameraend_ortho_height)
759 orthocam->height.setValue(d->camerastart_ortho_height.getValue()*(1-t) + d->cameraend_ortho_height.getValue()*t);
760 }
761
762 d->updateLastVars();
763
764 if (d->ntotframes>0) {
765 QString dummy, filename;
766 VP1CameraHelper::getLastAndNextFrameFileNames( d->outputdir,d->prefix,dummy, filename );
767 if (d->seekperiod>0.0) {
768 VP1Msg::messageVerbose("VP1CameraHelper: Attempting to create image "+filename);
769 QPixmap pm = VP1QtInventorUtils::renderToPixmap(d->renderArea,
770 d->width,d->height,false);
771 if (pm.isNull()) {
772 VP1Msg::messageDebug("VP1CameraHelper ERROR: Could not create image");
773 } else {
774 if (!pm.save(filename))
775 VP1Msg::messageDebug("VP1CameraHelper ERROR: Could not save image file "+filename);
776 }
777 }
778 }
779
780 if (end) {
781 if (d->targetCamState!=QByteArray()) {
782 if (!VP1QtInventorUtils::deserializeSoCameraParameters( d->targetCamState, *(d->camera) )) {
783 d->die();
784 return;
785 }
786 }
787 d->die(false/*non-abnormal end!*/);
788 }
789
790}
791
792//____________________________________________________________________
793void VP1CameraHelper::getLastAndNextFrameFileNames( const QString& outputdir,const QString& prefix,
794 QString& lastOfExistingFiles,
795 QString& nextAvailableFile )
796{
797 lastOfExistingFiles="";
798 QString filename;
799 int i(0);
800 while (true) {
801 filename=outputdir+"/"+prefix+"_"+QString::number(i++).rightJustified(6,'0')+".png";
802 if (!QFile::exists(filename)) {
803 nextAvailableFile=filename;
804 return;
805 }
806 lastOfExistingFiles = filename;
807 }
808}
809
810//____________________________________________________________________
812{
814 return true;
815 if (!camera||camera_ref!=camera->getRefCount()) {
816 instance_invalid=true;
817 VP1Msg::messageDebug("VP1CameraHelper WARNING: Cancelled operation due to invalid camera.");
818 return true;
819 }
820 if (sceneroot->getRefCount()<=1) {
821 instance_invalid=true;
822 VP1Msg::messageDebug("VP1CameraHelper WARNING: Cancelled operation due to sceneroot being unref'ed.");
823 return true;
824 }
825
826 return false;
827}
828
829//____________________________________________________________________
831 SoNode * root,
832 SbMatrix & matrix,
833 SbMatrix & inverse)
834{
835 searchaction->reset();
836 searchaction->setSearchingAll(TRUE);
837 searchaction->setInterest(SoSearchAction::FIRST);
838 searchaction->setNode(cameraarg);
839 searchaction->apply(root);
840
841 matrix = inverse = SbMatrix::identity();
842 if (searchaction->getPath()) {
843 matrixaction->apply(searchaction->getPath());
844 matrix = matrixaction->getMatrix();
845 inverse = matrixaction->getInverse();
846 }
847 searchaction->reset();
848}
849
850//____________________________________________________________________
852 SoSFVec3f & position, SoSFFloat & nearDistance,
853 SoSFFloat & farDistance, SoSFFloat & focalDistance,
854 bool&isPerspective,
855 SoSFFloat & height /*Target height in case of orthographic camera*/ )
856{
857 float aspect = camera->aspectRatio.getValue();
858
859 if (box.isEmpty())
860 return false;
861 if (camera->getTypeId() == SoPerspectiveCamera::getClassTypeId()) {
862 isPerspective=true;
863 } else if (camera->getTypeId() == SoOrthographicCamera::getClassTypeId()) {
864 isPerspective=false;
865 } else {
866 VP1Msg::messageDebug("VP1CameraHelper WARNING: Unknown camera type. Animation aborted.");
867 return false;
868 }
869
870 SbVec3f cameradirection;
871 camera->orientation.getValue().multVec(SbVec3f(0, 0, -1), cameradirection);
872 position.setValue(box.getCenter() + -cameradirection);
873
874 // Get the radius of the bounding sphere.
875 SbSphere bs;
876 bs.circumscribe(box);
877 float radius = bs.getRadius();
878
879 if (isPerspective) {
880 // Make sure that everything will still be inside the viewing volume
881 // even if the aspect ratio "favorizes" width over height.
882 float aspectradius = radius / (aspect < 1.0f ? aspect : 1.0f);
883
884 SoPerspectiveCamera * perspcam =static_cast<SoPerspectiveCamera*>(camera);
885 if (!perspcam)
886 return false;
887 // Move the camera to the edge of the bounding sphere, while still
888 // pointing at the scene.
889 SbVec3f direction = position.getValue() - box.getCenter();
890 (void) direction.normalize(); // we know this is not a null vector
891 float movelength =
892 aspectradius + (aspectradius/float(atan(perspcam->heightAngle.getValue())));
893 position.setValue(box.getCenter() + direction * movelength);
894 } else {
895 // Make sure that everything will still be inside the viewing volume
896 // even if the aspect ratio "favorizes" width over height.
897 if (aspect < 1.0f)
898 height = 2 * radius / aspect;
899 else
900 height = 2 * radius;
901
902 // Move the camera to the edge of the bounding sphere, while still
903 // pointing at the scene.
904 SbVec3f direction = position.getValue() - box.getCenter();
905 (void) direction.normalize(); // we know this is not a null vector
906 position.setValue(box.getCenter() + direction * radius);
907 }
908
909 // Set up the clipping planes according to the slack value (a value
910 // of 1.0 will yield clipping planes that are tangent to the
911 // bounding sphere of the scene).
912 float distance_to_midpoint = (position.getValue() - box.getCenter()).length();
913 nearDistance = distance_to_midpoint - radius;
914 farDistance = distance_to_midpoint + radius;
915
916
917 // The focal distance is simply the distance from the camera to the
918 // scene midpoint. This field is not used in rendering, its just
919 // provided to make it easier for the user to do calculations based
920 // on the distance between the camera and the scene.
921 focalDistance = distance_to_midpoint;
922
923 return true;
924}
925
926//____________________________________________________________________
927SbRotation VP1CameraHelper::Imp::cameraOrientation( SbVec3f dir, SbVec3f up)
928{
929 const float epsilon = 0.00001f;
930
931 //Input vectors must be non-zero:
932 if (dir.sqrLength()==0.0f||up.sqrLength()==0.0f) {
933 VP1Msg::messageDebug("VP1CameraHelper Warning: Asked to determine camera orientation from "
934 "view and up direction vectors that are not both non-zero");
935 return SbRotation(SbVec3f(0.0f, 0.0f, 1.0f), 0.0f);//Default rotation (as in SoCamera.cpp)
936 }
937
938 //Normalize input:
939 dir.normalize();
940 up.normalize();
941
942 //Direction and upvector corresponding to identity rotation:
943 SbVec3f dir0(0.0f, 0.0f, -1.0f), up0(0.0f, 1.0f, 0.0f);
944
945 //First we construct the rotation which takes dir0 into dir:
946 SbRotation rot(dir0,dir);
947
948 //dir and up should not be parallel:
949 if (fabs(dir.dot(up))>1.0f-epsilon) {
950 VP1Msg::messageDebug("VP1CameraHelper Warning: Asked to determine camera orientation from "
951 "view and up direction vectors that are essentially parallel."
952 " Choosing new arbitrary up direction.");
953 up = dir.cross(SbVec3f(1.0f,1.0f,0.0f));
954 up.normalize();
955 if (fabs(dir.dot(up))>1.0f-epsilon) {
956 up = dir.cross(SbVec3f(1.0f,0.0f,1.0f));
957 up.normalize();
958 if (fabs(dir.dot(up))>1.0f-epsilon) {
959 VP1Msg::messageDebug("VP1CameraHelper Warning: Unable to find useful up-vector.");
960 return rot;
961 }
962 }
963 }
964
965 //The second rotation should rotate around dir (to leave it
966 //unchanged), and should take, in the plane orthogonal to dir, (the
967 //projection of) rot*up0 into (the projection of) up. This is achieved
968 //by first putting the two up vectors into the plane orthogonal to
969 //dir, and then simply finding the rotation which brings up0 into
970 //up:
971
972 //Put up vectors into plane orthogonal to dir.
973 up = up - dir*(dir.dot(up));
974 if (up.length()<epsilon) {
975 //up and dir are parallel?
976 VP1Msg::messageDebug("VP1CameraHelper Warning: Can't determine correct up direction (1).");
977 return rot;
978 }
979 up.normalize();
980
981 rot.multVec(up0,up0);
982 up0 = up0 - dir*(dir.dot(up0));
983 if (up0.length()<epsilon) {
984 //up0 and dir are parallel?
985 VP1Msg::messageDebug("VP1CameraHelper Warning: Can't determine correct up direction (2).");
986 return rot;
987 }
988 up0.normalize();
989
990 //Done:
991 return rot*SbRotation(up0,up);
992}
#define M_PI
char data[hepevt_bytes_allocation_ATLAS]
Definition HepEvt.cxx:11
double angle(const GeoTrf::Vector2D &a, const GeoTrf::Vector2D &b)
const double width
#define z
SoSFFloat camerastart_focalDistance
SoSearchAction * searchaction
void actual_animatedZoomToBBox(const SbBox3f &box, double duration_in_secs=1.0, double slack=1.0, const SbVec3f &lookat=SbVec3f(999, 999, 999), const SbVec3f &upvec=SbVec3f(999, 999, 999))
bool getCameraParametersForBBox(const SbBox3f &box, SoSFVec3f &position, SoSFFloat &nearDistance, SoSFFloat &farDistance, SoSFFloat &focalDistance, bool &isPerspective, SoSFFloat &height)
void actual_animatedZoomToPath(SoPath *path, double duration_in_secs=1.0, double slack=1.0, const SbVec3f &lookat=SbVec3f(999, 999, 999), const SbVec3f &upvec=SbVec3f(999, 999, 999))
VP1CameraHelper * theclass
static SbRotation cameraOrientation(SbVec3f dir, SbVec3f up)
void actual_animatedZoomToPoint(SbVec3f, double duration_in_secs=1.0)
VP1ExaminerViewer * renderArea
SoTimerSensor * seeksensor
void actual_animatedZoomToCameraState(const QByteArray &camstate, double duration_in_secs)
static void seeksensorCB(void *data, SoSensor *)
void die(bool abnormal=true)
static std::map< SoCamera *, VP1CameraHelper * > helpers
void actual_animatedZoomToSubTree(SoNode *subtreeroot, double duration_in_secs=1.0, double slack=1.0, const SbVec3f &lookat=SbVec3f(999, 999, 999), const SbVec3f &upvec=SbVec3f(999, 999, 999))
void getCameraCoordinateSystem(SoCamera *cameraarg, SoNode *root, SbMatrix &matrix, SbMatrix &inverse)
void startSeekTimer(double duration_in_secs)
SoGetMatrixAction * matrixaction
static VP1CameraHelper * animatedZoomToPath(SoCamera *camera, SoGroup *sceneroot, SoPath *path, double duration_in_secs=1.0, double clipVolPercent=100.0, double slack=1.0, const SbVec3f &lookat=SbVec3f(999, 999, 999), const SbVec3f &upvec=SbVec3f(999, 999, 999), bool varySpeed=true, bool forceCircular=false)
static VP1CameraHelper * animatedZoomToSubTree(SoCamera *camera, SoGroup *sceneroot, SoNode *subtreeroot, double duration_in_secs=1.0, double clipVolPercent=100.0, double lastClipVolPercent=100.0, double slack=1.0, const SbVec3f &lookat=SbVec3f(999, 999, 999), const SbVec3f &upvec=SbVec3f(999, 999, 999), bool varySpeed=true, bool forceCircular=false)
void setOutputImagesMode(VP1ExaminerViewer *ra, const QString &outputdir, int width=1024, int height=768, double fps=24, const QString &prefix="vp1_frame")
static void getLastAndNextFrameFileNames(const QString &outputdir, const QString &prefix, QString &lastOfExistingFiles, QString &nextAvailableFile)
static VP1CameraHelper * animatedZoomToCameraState(SoCamera *camera, SoGroup *sceneroot, const QByteArray &camstate, double duration_in_secs=1.0, double clipVolPercent=100.0, double lastClipVolPercent=100.0, bool varySpeed=true, bool forceCircular=false)
static VP1CameraHelper * animatedZoomToBBox(SoCamera *camera, SoGroup *sceneroot, const SbBox3f &box, double duration_in_secs=1.0, double clipVolPercent=100.0, double slack=1.0, const SbVec3f &lookat=SbVec3f(999, 999, 999), const SbVec3f &upvec=SbVec3f(999, 999, 999), bool varySpeed=true, bool forceCircular=false)
static VP1CameraHelper * animatedZoomToPoint(SoCamera *camera, SoGroup *sceneroot, SbVec3f, double duration_in_secs=1.0, double clipVolPercent=100.0, bool varySpeed=true, bool forceCircular=false)
static void abortAnyCurrentZoom(SoCamera *camera)
VP1CameraHelper(SoCamera *, SoGroup *)
static void messageVerbose(const QString &)
Definition VP1Msg.cxx:84
static void messageDebug(const QString &)
Definition VP1Msg.cxx:39
static QPixmap renderToPixmap(VP1ExaminerViewer *ra, int pixels_x, int pixels_y, bool transparent_background=false, double actualRenderedSizeFact=1.0)
static bool deserializeSoCameraParameters(QByteArray &, SoCamera &)
static QString str(const QString &s)
Definition VP1String.h:49
bool exists(const std::string &filename)
does a file exist
int r
Definition globals.cxx:22