1 /**
2 * Mandelbulber v2, a 3D fractal generator ,=#MKNmMMKmmßMNWy,
3 * ,B" ]L,,p%%%,,,§;, "K
4 * Copyright (C) 2014-21 Mandelbulber Team §R-==%w["'~5]m%=L.=~5N
5 * ,=mm=§M ]=4 yJKA"/-Nsaj "Bw,==,,
6 * This file is part of Mandelbulber. §R.r= jw",M Km .mM FW ",§=ß., ,TN
7 * ,4R =%["w[N=7]J '"5=],""]]M,w,-; T=]M
8 * Mandelbulber is free software: §R.ß~-Q/M=,=5"v"]=Qf,'§"M= =,M.§ Rz]M"Kw
9 * you can redistribute it and/or §w "xDY.J ' -"m=====WeC=\ ""%""y=%"]"" §
10 * modify it under the terms of the "§M=M =D=4"N #"%==A%p M§ M6 R' #"=~.4M
11 * GNU General Public License as §W =, ][T"]C § § '§ e===~ U !§[Z ]N
12 * published by the 4M",,Jm=,"=e~ § § j]]""N BmM"py=ßM
13 * Free Software Foundation, ]§ T,M=& 'YmMMpM9MMM%=w=,,=MT]M m§;'§,
14 * either version 3 of the License, TWw [.j"5=~N[=§%=%W,T ]R,"=="Y[LFT ]N
15 * or (at your option) TW=,-#"%=;[ =Q:["V"" ],,M.m == ]N
16 * any later version. J§"mr"] ,=,," =="""J]= M"M"]==ß"
17 * §= "=C=4 §"eM "=B:m|4"]#F,§~
18 * Mandelbulber is distributed in "9w=,,]w em%wJ '"~" ,=,,ß"
19 * the hope that it will be useful, . "K= ,=RMMMßM"""
20 * but WITHOUT ANY WARRANTY; .'''
21 * without even the implied warranty
22 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23 *
24 * See the GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with Mandelbulber. If not, see <http://www.gnu.org/licenses/>.
27 *
28 * ###########################################################################
29 *
30 * Authors: Krzysztof Marczak (buddhi1980@gmail.com), Sebastian Jennen (jenzebas@gmail.com)
31 *
32 * Functions for flight animation.
33 *
34 * cFlightAnimation contains all functionality for capturing, editing
35 * and rendering of a flight path. The class holds a cAnimationFrames to store
36 * the parameters of the frames and a table to display the frames in a widget.
37 * The class keeps track of current velocity and acceleration and exposes
38 * slots to change the camera acceleration.
39 */
40
41 #include "animation_flight.hpp"
42
43 #include <memory>
44
45 #include <QWidget>
46
47 #include "ui_dock_animation.h"
48
49 #include "animation_frames.hpp"
50 #include "cimage.hpp"
51 #include "common_math.h"
52 #include "files.h"
53 #include "global_data.hpp"
54 #include "headless.h"
55 #include "initparameters.hpp"
56 #include "interface.hpp"
57 #include "netrender.hpp"
58 #include "opencl_engine_render_fractal.h"
59 #include "opencl_global.h"
60 #include "render_job.hpp"
61 #include "render_window.hpp"
62 #include "rendered_image_widget.hpp"
63 #include "rendering_configuration.hpp"
64 #include "settings.hpp"
65 #include "system_data.hpp"
66 #include "system_directories.hpp"
67 #include "undo.h"
68 #include "wait.hpp"
69 #include "write_log.hpp"
70
71 #include "qt/dock_animation.h"
72 #include "qt/dock_navigation.h"
73 #include "qt/dock_statistics.h"
74 #include "qt/my_progress_bar.h"
75 #include "qt/my_table_widget_anim.hpp"
76 #include "qt/player_widget.hpp"
77 #include "qt/system_tray.hpp"
78 #include "qt/thumbnail_widget.h"
79
80 cFlightAnimation *gFlightAnimation = nullptr;
81
cFlightAnimation(cInterface * _interface,std::shared_ptr<cAnimationFrames> _frames,std::shared_ptr<cImage> _image,QWidget * _imageWidget,std::shared_ptr<cParameterContainer> _params,std::shared_ptr<cFractalContainer> _fractal,QObject * parent)82 cFlightAnimation::cFlightAnimation(cInterface *_interface,
83 std::shared_ptr<cAnimationFrames> _frames, std::shared_ptr<cImage> _image, QWidget *_imageWidget,
84 std::shared_ptr<cParameterContainer> _params, std::shared_ptr<cFractalContainer> _fractal,
85 QObject *parent)
86 : QObject(parent), mainInterface(_interface), frames(_frames)
87 {
88 if (mainInterface->mainWindow)
89 {
90 ui = mainInterface->mainWindow->GetWidgetDockAnimation()->GetUi();
91
92 previewSize.setWidth(systemData.GetPreferredThumbnailSize());
93 previewSize.setHeight(systemData.GetPreferredThumbnailSize() * 3 / 4);
94
95 // connect flight control buttons
96 connect(ui->pushButton_record_flight, SIGNAL(clicked()), this, SLOT(slotRecordFlight()));
97 connect(
98 ui->pushButton_continue_recording, SIGNAL(clicked()), this, SLOT(slotContinueRecording()));
99 connect(ui->pushButton_render_flight, SIGNAL(clicked()), this, SLOT(slotRenderFlight()));
100 connect(ui->pushButton_delete_all_images, SIGNAL(clicked()), this, SLOT(slotDeleteAllImages()));
101 connect(ui->pushButton_show_animation, SIGNAL(clicked()), this, SLOT(slotShowAnimation()));
102 connect(ui->pushButton_flight_refresh_table, SIGNAL(clicked()), this, SLOT(slotRefreshTable()));
103 connect(ui->pushButton_flight_to_keyframe_export, SIGNAL(clicked()), this,
104 SLOT(slotExportFlightToKeyframes()));
105 connect(ui->button_selectAnimFlightImageDir, SIGNAL(clicked()), this,
106 SLOT(slotSelectAnimFlightImageDir()));
107 connect(ui->tableWidget_flightAnimation, SIGNAL(cellChanged(int, int)), this,
108 SLOT(slotTableCellChanged(int, int)));
109 connect(ui->spinboxInt_flight_first_to_render, SIGNAL(valueChanged(int)), this,
110 SLOT(slotMovedSliderFirstFrame(int)));
111 connect(ui->spinboxInt_flight_last_to_render, SIGNAL(valueChanged(int)), this,
112 SLOT(slotMovedSliderLastFrame(int)));
113 connect(ui->tableWidget_flightAnimation, SIGNAL(cellDoubleClicked(int, int)), this,
114 SLOT(slotCellDoubleClicked(int, int)));
115 connect(mainInterface->renderedImage, SIGNAL(ShiftModeChanged(bool)), this,
116 SLOT(slotOrthogonalStrafe(bool)));
117
118 // connect renderedImage signals
119 connect(mainInterface->renderedImage, SIGNAL(StrafeChanged(CVector2<double>)), this,
120 SLOT(slotFlightStrafe(CVector2<double>)));
121 connect(mainInterface->renderedImage, SIGNAL(YawAndPitchChanged(CVector2<double>)), this,
122 SLOT(slotFlightYawAndPitch(CVector2<double>)));
123 connect(mainInterface->renderedImage, SIGNAL(SpeedChanged(double)), this,
124 SLOT(slotFlightChangeSpeed(double)));
125 connect(mainInterface->renderedImage, SIGNAL(SpeedSet(double)), this,
126 SLOT(slotFlightSetSpeed(double)));
127 connect(mainInterface->renderedImage, SIGNAL(RotationChanged(double)), this,
128 SLOT(slotFlightRotation(double)));
129 connect(mainInterface->renderedImage, SIGNAL(Pause()), this, SLOT(slotRecordPause()));
130
131 // connect system tray
132 connect(
133 mainInterface->systemTray, SIGNAL(notifyRenderFlight()), this, SLOT(slotRenderFlight()));
134 connect(this, SIGNAL(notifyRenderFlightRenderStatus(QString, QString)),
135 mainInterface->systemTray, SLOT(showMessage(QString, QString)));
136
137 // connect QuestionMessage signal
138 connect(this,
139 SIGNAL(QuestionMessage(
140 const QString, const QString, QMessageBox::StandardButtons, QMessageBox::StandardButton *)),
141 mainInterface->mainWindow,
142 SLOT(slotQuestionMessage(const QString, const QString, QMessageBox::StandardButtons,
143 QMessageBox::StandardButton *)));
144
145 table = ui->tableWidget_flightAnimation;
146 }
147 else
148 {
149 ui = nullptr;
150 table = nullptr;
151 }
152
153 // NetRender for animation
154 // signals to NetRender
155 connect(this, &cFlightAnimation::SendNetRenderSetup, gNetRender, &cNetRender::SendSetup);
156 connect(this, &cFlightAnimation::NetRenderCurrentAnimation, gNetRender,
157 &cNetRender::SetCurrentAnimation);
158 connect(this, &cFlightAnimation::NetRenderConfirmRendered, gNetRender,
159 &cNetRender::ConfirmRenderedFrame);
160 connect(
161 this, &cFlightAnimation::NetRenderAddFileToSender, gNetRender, &cNetRender::AddFileToSender);
162 connect(
163 this, &cFlightAnimation::NetRenderNotifyClientStatus, gNetRender, &cNetRender::NotifyStatus);
164
165 // signals from NetRender
166 connect(
167 gNetRender, &cNetRender::FinishedFrame, this, &cFlightAnimation::slotNetRenderFinishedFrame);
168 connect(this, &cFlightAnimation::NetRenderSendFramesToDoList, gNetRender,
169 &cNetRender::SendFramesToDoList);
170 connect(gNetRender, &cNetRender::UpdateFramesToDo, this,
171 &cFlightAnimation::slotNetRenderUpdateFramesToDo);
172 connect(
173 this, &cFlightAnimation::NetRenderStopAllClients, gNetRender, &cNetRender::StopAllClients);
174 connect(gNetRender, &cNetRender::animationStopRequest, this,
175 &cFlightAnimation::slotAnimationStopRequest);
176
177 connect(this, SIGNAL(showErrorMessage(QString, cErrorMessage::enumMessageType, QWidget *)),
178 gErrorMessage, SLOT(slotShowMessage(QString, cErrorMessage::enumMessageType, QWidget *)));
179
180 image = _image;
181 imageWidget = dynamic_cast<RenderedImage *>(_imageWidget);
182 params = _params;
183 fractalParams = _fractal;
184 linearSpeedSp = 0.0;
185 rotationDirection = 0;
186 recordPause = false;
187 orthogonalStrafe = false;
188 negativeFlightSpeed = false;
189 }
190
slotRecordFlight()191 void cFlightAnimation::slotRecordFlight()
192 {
193 if (frames)
194 {
195 RecordFlight(false);
196 }
197 else
198 {
199 qCritical() << "gAnimFrames not allocated";
200 }
201 }
202
slotContinueRecording()203 void cFlightAnimation::slotContinueRecording()
204 {
205 if (frames)
206 {
207 RecordFlight(true);
208 }
209 else
210 {
211 qCritical() << "gAnimFrames not allocated";
212 }
213 }
214
slotRenderFlight()215 bool cFlightAnimation::slotRenderFlight()
216 {
217 // get latest values of all parameters
218 if (mainInterface->mainWindow)
219 {
220 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
221 }
222
223 if (frames)
224 {
225 if (frames->GetNumberOfFrames() == 0)
226 {
227 emit showErrorMessage(QObject::tr("No frames to render"), cErrorMessage::errorMessage,
228 mainInterface->mainWindow->GetCentralWidget());
229 }
230 else if (!QDir(params->Get<QString>("anim_flight_dir")).exists())
231 {
232 emit showErrorMessage(
233 QObject::tr("The folder %1 does not exist. Please specify a valid location.")
234 .arg(params->Get<QString>("anim_flight_dir")),
235 cErrorMessage::errorMessage, mainInterface->mainWindow->GetCentralWidget());
236 }
237 else
238 {
239 return RenderFlight(&gMainInterface->stopRequest);
240 }
241 }
242 else
243 {
244 qCritical() << "gAnimFrames not allocated";
245 }
246 return false;
247 }
248
RecordFlight(bool continueRecording)249 void cFlightAnimation::RecordFlight(bool continueRecording)
250 {
251 mainInterface->DisablePeriodicRefresh();
252
253 // get latest values of all parameters
254 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
255 gUndo->Store(params, fractalParams, frames, nullptr);
256
257 if (!continueRecording)
258 {
259 const QMessageBox::StandardButton reply =
260 QMessageBox::question(mainInterface->mainWindow->GetCentralWidget(),
261 QObject::tr("Are you sure to start recording of new animation?"),
262 QObject::tr("This will delete all images in the image folder.\nProceed?"),
263 QMessageBox::Yes | QMessageBox::No);
264
265 if (reply == QMessageBox::Yes)
266 {
267 cAnimationFrames::WipeFramesFromFolder(params->Get<QString>("anim_flight_dir"));
268 }
269 else
270 {
271 return;
272 }
273 }
274 else
275 {
276 if (frames->GetNumberOfFrames() == 0)
277 {
278 cErrorMessage::showMessage(
279 QObject::tr("No frames recorded before. Unable to continue."), cErrorMessage::errorMessage);
280 return;
281 }
282 }
283
284 // check if main image is not used by other rendering process
285 if (image->IsUsed())
286 {
287 cErrorMessage::showMessage(
288 QObject::tr("Rendering engine is busy. Stop unfinished rendering before starting new one"),
289 cErrorMessage::errorMessage);
290 return;
291 }
292
293 mainInterface->stopRequest = false;
294 for (int i = 0; i < 30; i++)
295 {
296 if (mainInterface->stopRequest || systemData.globalStopRequest)
297 {
298 emit updateProgressHide();
299 return;
300 }
301 emit updateProgressAndStatus(QObject::tr("Recording flight path"),
302 tr("waiting %1 seconds").arg(QString::number(3.0 - 0.1 * i)), 0.0,
303 cProgressText::progress_ANIMATION);
304 gApplication->processEvents();
305 Wait(100);
306 }
307
308 if (!continueRecording)
309 {
310 frames->Clear();
311
312 const bool addSpeeds = params->Get<bool>("flight_add_speeds");
313
314 // add default parameters for animation
315 if (frames->GetListOfUsedParameters().size() == 0)
316 {
317 gAnimFrames->AddAnimatedParameter("camera", params->GetAsOneParameter("camera"), params);
318 gAnimFrames->AddAnimatedParameter("target", params->GetAsOneParameter("target"), params);
319 gAnimFrames->AddAnimatedParameter(
320 "camera_top", params->GetAsOneParameter("camera_top"), params);
321 if (addSpeeds)
322 {
323 {
324 gAnimFrames->AddAnimatedParameter("flight_movement_speed_vector",
325 params->GetAsOneParameter("flight_movement_speed_vector"), params);
326 gAnimFrames->AddAnimatedParameter("flight_rotation_speed_vector",
327 params->GetAsOneParameter("flight_rotation_speed_vector"), params);
328 }
329 }
330 }
331
332 PrepareTable();
333 }
334
335 // setup cursor mode for renderedImage widget
336 QList<QVariant> clickMode;
337 clickMode.append(int(RenderedImage::clickFlightSpeedControl));
338 mainInterface->renderedImage->setClickMode(clickMode);
339
340 // setup of rendering engine
341 std::unique_ptr<cRenderJob> renderJob(new cRenderJob(params, fractalParams,
342 mainInterface->mainImage, &mainInterface->stopRequest, mainInterface->renderedImage));
343 connect(renderJob.get(),
344 SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), this,
345 SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)));
346 connect(renderJob.get(), SIGNAL(updateStatistics(cStatistics)), this,
347 SIGNAL(updateStatistics(cStatistics)));
348 connect(renderJob.get(), SIGNAL(updateImage()), mainInterface->renderedImage, SLOT(update()));
349
350 cRenderingConfiguration config;
351 config.DisableRefresh();
352 config.SetMaxRenderTime(params->Get<double>("flight_sec_per_frame"));
353
354 renderJob->Init(cRenderJob::flightAnimRecord, config);
355 mainInterface->stopRequest = false;
356
357 image->SetFastPreview(true);
358
359 // vector for speed and rotation control
360 CVector3 cameraSpeed;
361 CVector3 cameraAcceleration;
362 CVector3 cameraAngularSpeed;
363 CVector3 cameraAngularAcceleration;
364
365 int index = 0;
366 if (continueRecording)
367 {
368 frames->GetFrameAndConsolidate(frames->GetNumberOfFrames() - 1, params, fractalParams);
369 cameraSpeed = params->Get<CVector3>("flight_movement_speed_vector");
370 cameraAngularSpeed = params->Get<CVector3>("flight_rotation_speed_vector");
371 index = frames->GetNumberOfFrames() - 1;
372 }
373
374 CVector3 cameraPosition = params->Get<CVector3>("camera");
375 CVector3 target = params->Get<CVector3>("target");
376 CVector3 top = params->Get<CVector3>("camera_top");
377
378 cCameraTarget cameraTarget(cameraPosition, target, top);
379
380 linearSpeedSp = params->Get<double>("flight_speed");
381 const enumSpeedMode speedMode = enumSpeedMode(params->Get<int>("flight_speed_control"));
382 double rotationSpeedSp = params->Get<double>("flight_rotation_speed") / 100.0;
383 double rollSpeedSp = params->Get<double>("flight_roll_speed") / 100.0;
384 double inertia = params->Get<double>("flight_inertia");
385
386 recordPause = false;
387
388 mainInterface->mainWindow->GetWidgetDockNavigation()->LockAllFunctions();
389 imageWidget->SetEnableClickModes(false);
390
391 mainInterface->progressBarAnimation->show();
392
393 while (!mainInterface->stopRequest)
394 {
395 emit updateProgressAndStatus(QObject::tr("Recording flight animation"),
396 tr("Recording flight animation. Frame: ") + QString::number(index), 0.0,
397 cProgressText::progress_ANIMATION);
398
399 bool wasPaused = false;
400 while (recordPause)
401 {
402 wasPaused = true;
403 emit updateProgressAndStatus(QObject::tr("Recording flight animation"),
404 tr("Paused. Frame: ") + QString::number(index), 0.0, cProgressText::progress_ANIMATION);
405 gApplication->processEvents();
406 if (mainInterface->stopRequest) break;
407 }
408
409 if (wasPaused)
410 {
411 // parameter refresh after pause
412 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
413 renderJob->UpdateParameters(params, fractalParams);
414 rotationSpeedSp = params->Get<double>("flight_rotation_speed") / 100.0;
415 rollSpeedSp = params->Get<double>("flight_roll_speed") / 100.0;
416 inertia = params->Get<double>("flight_inertia");
417
418 config.SetMaxRenderTime(params->Get<double>("flight_sec_per_frame"));
419 renderJob->UpdateConfig(config);
420
421 if (mainInterface->stopRequest) break;
422 }
423
424 const double distanceToSurface =
425 gMainInterface->GetDistanceForPoint(cameraPosition, params, fractalParams);
426
427 // speed
428 double linearSpeed = 0;
429 switch (speedMode)
430 {
431 case speedRelative: linearSpeed = distanceToSurface * linearSpeedSp; break;
432 case speedConstant: linearSpeed = linearSpeedSp; break;
433 }
434
435 // integrator for position
436 if (qIsNull(strafe.Length()))
437 {
438 double goForward = (negativeFlightSpeed) ? -1.0 : 1.0;
439 cameraAcceleration =
440 (cameraTarget.GetForwardVector() * goForward * linearSpeed - cameraSpeed) / (inertia + 1.0);
441 }
442 else
443 {
444 CVector3 direction;
445 if (!orthogonalStrafe) direction = cameraTarget.GetForwardVector();
446
447 if (!qIsNull(strafe.x))
448 {
449 direction += cameraTarget.GetRightVector() * strafe.x;
450 }
451 if (!qIsNull(strafe.y))
452 {
453 direction += cameraTarget.GetTopVector() * strafe.y;
454 }
455 cameraAcceleration = (direction * linearSpeed - cameraSpeed) / (inertia + 1.0);
456 }
457 cameraSpeed += cameraAcceleration;
458 cameraPosition += cameraSpeed;
459
460 // rotation
461 cameraAngularAcceleration.x =
462 (yawAndPitch.x * rotationSpeedSp - cameraAngularSpeed.x) / (inertia + 1.0);
463 cameraAngularAcceleration.y =
464 (yawAndPitch.y * rotationSpeedSp - cameraAngularSpeed.y) / (inertia + 1.0);
465 cameraAngularAcceleration.z =
466 (rotationDirection * rollSpeedSp - cameraAngularSpeed.z) / (inertia + 1.0);
467 cameraAngularSpeed += cameraAngularAcceleration;
468
469 CVector3 forwardVector = cameraTarget.GetForwardVector();
470 forwardVector =
471 forwardVector.RotateAroundVectorByAngle(cameraTarget.GetTopVector(), -cameraAngularSpeed.x);
472 forwardVector =
473 forwardVector.RotateAroundVectorByAngle(cameraTarget.GetRightVector(), -cameraAngularSpeed.y);
474
475 top = cameraTarget.GetTopVector();
476 top = top.RotateAroundVectorByAngle(cameraTarget.GetRightVector(), -cameraAngularSpeed.y);
477 top = top.RotateAroundVectorByAngle(cameraTarget.GetForwardVector(), -cameraAngularSpeed.z);
478
479 // update position and rotation
480 target = cameraPosition + forwardVector * cameraSpeed.Length();
481 cameraTarget.SetCameraTargetTop(cameraPosition, target, top);
482
483 // update parameters
484 params->Set("camera", cameraPosition);
485 params->Set("target", target);
486 params->Set("camera_top", top);
487 params->Set("camera_rotation", cameraTarget.GetRotation() * 180.0 / M_PI);
488 params->Set("camera_distance_to_target", cameraTarget.GetDistance());
489 params->Set("flight_movement_speed_vector", cameraSpeed);
490 params->Set("flight_rotation_speed_vector", cameraAngularSpeed);
491 params->Set("frame_no", index);
492
493 SynchronizeInterfaceWindow(
494 mainInterface->mainWindow->GetWidgetDockNavigation(), params, qInterface::write);
495 renderJob->ChangeCameraTargetPosition(cameraTarget);
496
497 // add new frame to container
498 frames->AddFrame(params, fractalParams, 0);
499
500 // add column to table
501 const int newColumn = AddColumn(frames->GetFrame(frames->GetNumberOfFrames() - 1));
502
503 // update HUD
504 RenderedImage::sFlightData flightData;
505 flightData.frame = frames->GetNumberOfFrames();
506 flightData.camera = cameraPosition;
507 flightData.speed = cameraSpeed.Length();
508 flightData.speedSp = linearSpeed;
509 flightData.distance = distanceToSurface;
510 flightData.rotation = cameraTarget.GetRotation();
511 flightData.speedVector = cameraSpeed;
512 flightData.forwardVector = forwardVector;
513 flightData.topVector = top;
514
515 mainInterface->renderedImage->SetFlightData(flightData);
516
517 // render frame
518 const bool result = renderJob->Execute();
519 if (!result) break;
520
521 // create thumbnail
522 if (ui->checkBox_flight_show_thumbnails->isChecked())
523 {
524 UpdateThumbnailFromImage(newColumn);
525 }
526
527 const QString filename = GetFlightFilename(index, false);
528 const ImageFileSave::enumImageFileType fileType =
529 ImageFileSave::enumImageFileType(params->Get<int>("flight_animation_image_type"));
530 SaveImage(filename, fileType, image, gMainInterface->mainWindow);
531
532 gApplication->processEvents();
533
534 index++;
535 }
536
537 if (!systemData.noGui && image->IsMainImage())
538 {
539 mainInterface->mainWindow->GetWidgetDockNavigation()->UnlockAllFunctions();
540 imageWidget->SetEnableClickModes(true);
541 }
542
543 // retrieve original click mode
544 const QList<QVariant> item =
545 mainInterface->mainWindow->GetComboBoxMouseClickFunction()
546 ->itemData(mainInterface->mainWindow->GetComboBoxMouseClickFunction()->currentIndex())
547 .toList();
548 gMainInterface->renderedImage->setClickMode(item);
549
550 image->SetFastPreview(false);
551
552 UpdateLimitsForFrameRange();
553 ui->spinboxInt_flight_last_to_render->setValue(frames->GetNumberOfFrames());
554 }
555
UpdateThumbnailFromImage(int index) const556 void cFlightAnimation::UpdateThumbnailFromImage(int index) const
557 {
558 table->blockSignals(true);
559 const QImage qImage(static_cast<const uchar *>(image->ConvertTo8bitChar()),
560 int(image->GetWidth()), int(image->GetHeight()), int(image->GetWidth() * sizeof(sRGB8)),
561 QImage::Format_RGB888);
562 QPixmap pixmap;
563 pixmap.convertFromImage(qImage);
564 const QIcon icon(
565 pixmap.scaled(previewSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
566 table->setItem(0, index, new QTableWidgetItem(icon, QString()));
567 table->blockSignals(false);
568 }
569
PrepareTable()570 void cFlightAnimation::PrepareTable()
571 {
572 table->setRowCount(0);
573 table->setColumnCount(0);
574 table->clear();
575 tableRowNames.clear();
576 QFontMetrics fm(mainInterface->mainWindow->font());
577 table->verticalHeader()->setDefaultSectionSize(fm.height() + 6);
578 CreateRowsInTable();
579 }
580
CreateRowsInTable()581 void cFlightAnimation::CreateRowsInTable()
582 {
583 QList<cAnimationFrames::sParameterDescription> parList = frames->GetListOfUsedParameters();
584 table->setIconSize(previewSize);
585 table->insertRow(0);
586 table->setVerticalHeaderItem(0, new QTableWidgetItem(tr("preview")));
587 table->setRowHeight(0, previewSize.height());
588 tableRowNames.append(tr("preview"));
589
590 rowParameter.clear();
591 rowParameter.append(-1);
592
593 parameterRows.clear();
594 for (int i = 0; i < parList.size(); ++i)
595 {
596 int row = AddVariableToTable(parList[i], i);
597 parameterRows.append(row);
598 }
599 }
600
AddVariableToTable(const cAnimationFrames::sParameterDescription & parameterDescription,int index)601 int cFlightAnimation::AddVariableToTable(
602 const cAnimationFrames::sParameterDescription ¶meterDescription, int index)
603 {
604 using namespace parameterContainer;
605 const enumVarType type = parameterDescription.varType;
606 const int row = table->rowCount();
607 if (type == typeVector3)
608 {
609 QString varName =
610 parameterDescription.containerName + "_" + parameterDescription.parameterName + "_x";
611 tableRowNames.append(varName);
612 table->insertRow(row);
613 table->setVerticalHeaderItem(row, new QTableWidgetItem(varName));
614 rowParameter.append(index);
615
616 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_y";
617 tableRowNames.append(varName);
618 table->insertRow(row + 1);
619 table->setVerticalHeaderItem(row + 1, new QTableWidgetItem(varName));
620 rowParameter.append(index);
621
622 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_z";
623 tableRowNames.append(varName);
624 table->insertRow(row + 2);
625 table->setVerticalHeaderItem(row + 2, new QTableWidgetItem(varName));
626 rowParameter.append(index);
627 }
628 else if (type == typeVector4)
629 {
630 QString varName =
631 parameterDescription.containerName + "_" + parameterDescription.parameterName + "_x";
632 tableRowNames.append(varName);
633 table->insertRow(row);
634 table->setVerticalHeaderItem(row, new QTableWidgetItem(varName));
635 rowParameter.append(index);
636
637 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_y";
638 tableRowNames.append(varName);
639 table->insertRow(row + 1);
640 table->setVerticalHeaderItem(row + 1, new QTableWidgetItem(varName));
641 rowParameter.append(index);
642
643 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_z";
644 tableRowNames.append(varName);
645 table->insertRow(row + 2);
646 table->setVerticalHeaderItem(row + 2, new QTableWidgetItem(varName));
647 rowParameter.append(index);
648
649 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_w";
650 tableRowNames.append(varName);
651 table->insertRow(row + 3);
652 table->setVerticalHeaderItem(row + 3, new QTableWidgetItem(varName));
653 rowParameter.append(index);
654 }
655 else if (type == typeRgb)
656 {
657 QString varName =
658 parameterDescription.containerName + "_" + parameterDescription.parameterName + "_R";
659 tableRowNames.append(varName);
660 table->insertRow(row);
661 table->setVerticalHeaderItem(row, new QTableWidgetItem(varName));
662 rowParameter.append(index);
663
664 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_G";
665 tableRowNames.append(varName);
666 table->insertRow(row + 1);
667 table->setVerticalHeaderItem(row + 1, new QTableWidgetItem(varName));
668 rowParameter.append(index);
669
670 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_B";
671 tableRowNames.append(varName);
672 table->insertRow(row + 2);
673 table->setVerticalHeaderItem(row + 2, new QTableWidgetItem(varName));
674 rowParameter.append(index);
675 }
676 else
677 {
678 const QString varName =
679 parameterDescription.containerName + "_" + parameterDescription.parameterName;
680 tableRowNames.append(varName);
681 table->insertRow(table->rowCount());
682 table->setVerticalHeaderItem(table->rowCount() - 1, new QTableWidgetItem(varName));
683 rowParameter.append(index);
684 }
685 return row;
686 }
687
AddColumn(const cAnimationFrames::sAnimationFrame & frame,int indexOfExistingColumn)688 int cFlightAnimation::AddColumn(
689 const cAnimationFrames::sAnimationFrame &frame, int indexOfExistingColumn)
690 {
691 table->blockSignals(true);
692
693 int newColumn;
694 if (indexOfExistingColumn >= 0)
695 {
696 newColumn = indexOfExistingColumn;
697 }
698 else
699 {
700 newColumn = table->columnCount();
701 table->insertColumn(newColumn);
702 }
703
704 table->setColumnWidth(newColumn, previewSize.width());
705
706 QList<cAnimationFrames::sParameterDescription> parList = frames->GetListOfUsedParameters();
707
708 using namespace parameterContainer;
709 for (int i = 0; i < parList.size(); ++i)
710 {
711 const QString parameterName = parList[i].containerName + "_" + parList[i].parameterName;
712 const enumVarType type = parList[i].varType;
713 const int row = parameterRows[i];
714
715 if (type == typeVector3)
716 {
717 const CVector3 val = frame.parameters.Get<CVector3>(parameterName);
718 table->setItem(row, newColumn, new QTableWidgetItem(QString("%L1").arg(val.x, 0, 'g', 15)));
719 table->setItem(
720 row + 1, newColumn, new QTableWidgetItem(QString("%L1").arg(val.y, 0, 'g', 15)));
721 table->setItem(
722 row + 2, newColumn, new QTableWidgetItem(QString("%L1").arg(val.z, 0, 'g', 15)));
723 }
724 else if (type == typeVector4)
725 {
726 const CVector4 val = frame.parameters.Get<CVector4>(parameterName);
727 table->setItem(row, newColumn, new QTableWidgetItem(QString("%L1").arg(val.x, 0, 'g', 15)));
728 table->setItem(
729 row + 1, newColumn, new QTableWidgetItem(QString("%L1").arg(val.y, 0, 'g', 15)));
730 table->setItem(
731 row + 2, newColumn, new QTableWidgetItem(QString("%L1").arg(val.z, 0, 'g', 15)));
732 table->setItem(
733 row + 3, newColumn, new QTableWidgetItem(QString("%L1").arg(val.w, 0, 'g', 15)));
734 }
735 else if (type == typeRgb)
736 {
737 const sRGB val = frame.parameters.Get<sRGB>(parameterName);
738 table->setItem(row, newColumn, new QTableWidgetItem(QString::number(val.R)));
739 table->setItem(row + 1, newColumn, new QTableWidgetItem(QString::number(val.G)));
740 table->setItem(row + 2, newColumn, new QTableWidgetItem(QString::number(val.B)));
741 }
742 else
743 {
744 const QString val = frame.parameters.Get<QString>(parameterName);
745 table->setItem(row, newColumn, new QTableWidgetItem(val));
746 }
747 }
748 table->blockSignals(false);
749 return newColumn;
750 }
751
PrepareRenderJob(bool * stopRequest)752 std::shared_ptr<cRenderJob> cFlightAnimation::PrepareRenderJob(bool *stopRequest)
753 {
754 std::shared_ptr<cRenderJob> renderJob(
755 new cRenderJob(params, fractalParams, image, stopRequest, imageWidget));
756 connect(renderJob.get(),
757 SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), this,
758 SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)));
759 connect(renderJob.get(), SIGNAL(updateStatistics(cStatistics)), this,
760 SIGNAL(updateStatistics(cStatistics)));
761 if (!systemData.noGui)
762 {
763 connect(renderJob.get(), SIGNAL(updateImage()), mainInterface->renderedImage, SLOT(update()));
764 connect(renderJob.get(), SIGNAL(sendRenderedTilesList(QList<sRenderedTileData>)),
765 mainInterface->renderedImage, SLOT(showRenderedTilesList(QList<sRenderedTileData>)));
766 }
767 return renderJob;
768 }
769
InitFrameRanges(sFrameRanges * frameRanges)770 bool cFlightAnimation::InitFrameRanges(sFrameRanges *frameRanges)
771 {
772 // range of frames to render
773 frameRanges->startFrame = params->Get<int>("flight_first_to_render");
774 frameRanges->endFrame = params->Get<int>("flight_last_to_render");
775 frameRanges->totalFrames = frames->GetNumberOfFrames();
776 if (frameRanges->totalFrames < 1) frameRanges->totalFrames = 1;
777
778 if (frameRanges->endFrame == 0) frameRanges->endFrame = frameRanges->totalFrames;
779
780 if (frameRanges->startFrame == frameRanges->endFrame)
781 {
782 emit showErrorMessage(
783 QObject::tr(
784 "There is no frame to render: first frame to render and last frame to render are equals."),
785 cErrorMessage::warningMessage);
786 return false;
787 }
788 return true;
789 }
790
InitFrameMarkers(const sFrameRanges & frameRanges)791 void cFlightAnimation::InitFrameMarkers(const sFrameRanges &frameRanges)
792 {
793 alreadyRenderedFrames.resize(frameRanges.totalFrames);
794 alreadyRenderedFrames.fill(false);
795 reservedFrames.clear();
796 reservedFrames.resize(frameRanges.totalFrames);
797 reservedFrames.fill(false);
798 }
799
CheckWhichFramesAreAlreadyRendered(const sFrameRanges & frameRanges)800 void cFlightAnimation::CheckWhichFramesAreAlreadyRendered(const sFrameRanges &frameRanges)
801 {
802 // Check if frames have already been rendered
803 for (int index = 0; index < frames->GetNumberOfFrames(); ++index)
804 {
805 const QString filename = GetFlightFilename(index, false);
806 alreadyRenderedFrames[index] =
807 QFile(filename).exists() || index < frameRanges.startFrame || index >= frameRanges.endFrame;
808
809 if (gNetRender->IsClient())
810 {
811 if (netRenderListOfFramesToRender.size() > 0)
812 {
813 if (index < netRenderListOfFramesToRender[0]) alreadyRenderedFrames[index] = true;
814 }
815 }
816 reservedFrames[index] = alreadyRenderedFrames[index];
817 }
818 }
819
AllFramesAlreadyRendered(const sFrameRanges & frameRanges,bool * startRenderKeyframesAgain)820 bool cFlightAnimation::AllFramesAlreadyRendered(
821 const sFrameRanges &frameRanges, bool *startRenderKeyframesAgain)
822 {
823 bool result = true;
824
825 if (!gNetRender->IsClient())
826 {
827 if (frames->GetNumberOfFrames() > 0 && frameRanges.unrenderedTotalBeforeRender == 0)
828 {
829 bool deletePreviousRender;
830 const QString questionTitle = QObject::tr("Truncate Image Folder");
831 const QString questionText =
832 QObject::tr(
833 "The animation has already been rendered completely.\n Do you want to purge "
834 "the output "
835 "folder?\n")
836 + QObject::tr("This will delete all images in the image folder.\nProceed?");
837
838 if (!systemData.noGui)
839 {
840 QMessageBox::StandardButton reply = QMessageBox::NoButton;
841 emit QuestionMessage(
842 questionTitle, questionText, QMessageBox::Yes | QMessageBox::No, &reply);
843 while (reply == QMessageBox::NoButton)
844 {
845 gApplication->processEvents();
846 }
847 deletePreviousRender = (reply == QMessageBox::Yes);
848 }
849 else
850 {
851 // Exit if silent mode
852 if (systemData.silent)
853 {
854 exit(0);
855 }
856
857 deletePreviousRender = cHeadless::ConfirmMessage(questionTitle + "\n" + questionText);
858 }
859
860 if (deletePreviousRender)
861 {
862 cAnimationFrames::WipeFramesFromFolder(params->Get<QString>("anim_flight_dir"));
863 if (!systemData.noGui && image->IsMainImage())
864 {
865 mainInterface->mainWindow->GetWidgetDockNavigation()->UnlockAllFunctions();
866 imageWidget->SetEnableClickModes(true);
867 }
868
869 *startRenderKeyframesAgain = true;
870 }
871 else
872 {
873 result = false;
874 }
875 }
876 }
877
878 return result;
879 }
880
InitJobsForClients(const sFrameRanges & frameRanges)881 void cFlightAnimation::InitJobsForClients(const sFrameRanges &frameRanges)
882 {
883 int numberOfFramesForNetRender;
884 if (gNetRender->GetClientCount() == 0)
885 {
886 numberOfFramesForNetRender = 0;
887 }
888 else
889 {
890 numberOfFramesForNetRender =
891 frameRanges.unrenderedTotalBeforeRender / gNetRender->GetClientCount() / 2 + 1;
892 if (numberOfFramesForNetRender < minFramesForNetRender)
893 numberOfFramesForNetRender = minFramesForNetRender;
894 }
895 if (numberOfFramesForNetRender > maxFramesForNetRender)
896 numberOfFramesForNetRender = maxFramesForNetRender;
897
898 qint32 renderId = rand();
899 gNetRender->SetCurrentRenderId(renderId);
900 gNetRender->SetAnimation(true);
901 int frameIndex = 0;
902 for (int i = 0; i < gNetRender->GetClientCount(); i++)
903 {
904 QList<int> startingFrames;
905 for (int i = 0; i < numberOfFramesForNetRender; i++)
906 {
907 // looking for next unrendered frame
908 bool notFound = false;
909 while (alreadyRenderedFrames[frameIndex])
910 {
911 frameIndex++;
912 if (frameIndex >= frameRanges.totalFrames)
913 {
914 notFound = true;
915 break;
916 }
917 }
918 if (notFound)
919 {
920 break;
921 }
922 else
923 {
924 startingFrames.append(frameIndex);
925 reservedFrames[frameIndex] = true;
926 frameIndex++;
927 }
928 }
929 if (startingFrames.size() > 0)
930 {
931 emit SendNetRenderSetup(i, startingFrames);
932 }
933 }
934 emit NetRenderCurrentAnimation(params, fractalParams, true);
935 }
936
UpadeProgressInformation(const sFrameRanges & frameRanges,cProgressText * progressText,int index)937 void cFlightAnimation::UpadeProgressInformation(
938 const sFrameRanges &frameRanges, cProgressText *progressText, int index)
939 {
940 double percentDoneFrame;
941
942 if (frameRanges.unrenderedTotalBeforeRender > 0)
943 percentDoneFrame = (double(renderedFramesCount)) / frameRanges.unrenderedTotalBeforeRender;
944 else
945 percentDoneFrame = 1.0;
946
947 const QString progressTxt = progressText->getText(percentDoneFrame);
948
949 emit updateProgressAndStatus(QObject::tr("Animation start"),
950 QObject::tr("Frame %1 of %2").arg((index + 1)).arg(frames->GetNumberOfFrames()) + " "
951 + progressTxt,
952 percentDoneFrame, cProgressText::progress_ANIMATION);
953 }
954
UpdateCameraAndTarget()955 void cFlightAnimation::UpdateCameraAndTarget()
956 {
957 // recalculation of camera rotation and distance (just for display purposes)
958 const CVector3 camera = params->Get<CVector3>("camera");
959 const CVector3 target = params->Get<CVector3>("target");
960 const CVector3 top = params->Get<CVector3>("camera_top");
961 cCameraTarget cameraTarget(camera, target, top);
962 params->Set("camera_rotation", cameraTarget.GetRotation() * 180.0 / M_PI);
963 params->Set("camera_distance_to_target", cameraTarget.GetDistance());
964
965 if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
966 {
967 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::write);
968
969 // show distance in statistics table
970 const double distance =
971 mainInterface->GetDistanceForPoint(params->Get<CVector3>("camera"), params, fractalParams);
972 mainInterface->mainWindow->GetWidgetDockStatistics()->UpdateDistanceToFractal(distance);
973 }
974 }
975
ConfirmAndSendRenderedFrames(const int frameIndex,const QStringList & listOfSavedFiles)976 void cFlightAnimation::ConfirmAndSendRenderedFrames(
977 const int frameIndex, const QStringList &listOfSavedFiles)
978 {
979 emit NetRenderConfirmRendered(frameIndex, netRenderListOfFramesToRender.size());
980 netRenderListOfFramesToRender.removeAll(frameIndex);
981 emit NetRenderNotifyClientStatus();
982 for (QString channelFileName : listOfSavedFiles)
983 {
984 emit NetRenderAddFileToSender(channelFileName);
985 }
986 }
987
RenderFlight(bool * stopRequest)988 bool cFlightAnimation::RenderFlight(bool *stopRequest)
989 {
990 mainInterface->DisablePeriodicRefresh();
991
992 if (image->IsUsed())
993 {
994 emit showErrorMessage(
995 QObject::tr("Rendering engine is busy. Stop unfinished rendering before starting new one"),
996 cErrorMessage::errorMessage);
997 return false;
998 }
999
1000 if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
1001 {
1002 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1003 gUndo->Store(params, fractalParams, frames, nullptr);
1004 }
1005
1006 *stopRequest = false;
1007 animationStopRequest = false;
1008
1009 std::shared_ptr<cRenderJob> renderJob = PrepareRenderJob(stopRequest);
1010
1011 cRenderingConfiguration config;
1012
1013 if (systemData.noGui)
1014 {
1015 config.DisableRefresh();
1016 }
1017 config.DisableProgressiveRender();
1018 config.EnableNetRender();
1019
1020 renderJob->Init(cRenderJob::flightAnim, config);
1021
1022 cProgressText progressText;
1023 progressText.ResetTimer();
1024
1025 // range of keyframes to render
1026 sFrameRanges frameRanges;
1027 if (!InitFrameRanges(&frameRanges)) return false;
1028
1029 if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
1030 {
1031 mainInterface->mainWindow->GetWidgetDockNavigation()->LockAllFunctions();
1032 imageWidget->SetEnableClickModes(false);
1033 }
1034
1035 InitFrameMarkers(frameRanges);
1036
1037 try
1038 {
1039 // updating parameters
1040 if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
1041 {
1042 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1043 gUndo->Store(params, fractalParams, frames, nullptr);
1044 }
1045
1046 CheckWhichFramesAreAlreadyRendered(frameRanges);
1047
1048 animationIsRendered = true;
1049
1050 // count number of unrendered frames
1051 frameRanges.unrenderedTotalBeforeRender = 0;
1052 for (int i = 0; i < frames->GetNumberOfFrames(); i++)
1053 {
1054 if (!alreadyRenderedFrames[i]) frameRanges.unrenderedTotalBeforeRender++;
1055 }
1056
1057 // message if all frames are already rendered
1058 bool startRenderKeyframesAgain = false;
1059 bool result = AllFramesAlreadyRendered(frameRanges, &startRenderKeyframesAgain);
1060
1061 if (startRenderKeyframesAgain) return RenderFlight(stopRequest);
1062 if (!result) throw false;
1063
1064 renderedFramesCount = 0;
1065
1066 if (gNetRender->IsServer())
1067 {
1068 InitJobsForClients(frameRanges);
1069 }
1070
1071 for (int index = 0; index < frames->GetNumberOfFrames(); ++index)
1072 {
1073 // skip already rendered frame
1074 if (alreadyRenderedFrames[index]) continue;
1075 if (reservedFrames[index]) continue;
1076
1077 if (gNetRender->IsClient())
1078 {
1079 bool frameFound = false;
1080 for (int f = 0; f < netRenderListOfFramesToRender.size(); f++)
1081 {
1082 if (netRenderListOfFramesToRender[f] == index)
1083 {
1084 frameFound = true;
1085 break;
1086 }
1087 }
1088 if (!frameFound) continue;
1089 }
1090
1091 if (gNetRender->IsServer())
1092 {
1093 reservedFrames[index] = true;
1094 }
1095
1096 UpadeProgressInformation(frameRanges, &progressText, index);
1097
1098 if (*stopRequest || systemData.globalStopRequest || animationStopRequest) throw false;
1099
1100 frames->GetFrameAndConsolidate(index, params, fractalParams);
1101
1102 // recalculation of camera rotation and distance (just for display purposes)
1103 UpdateCameraAndTarget();
1104
1105 params->Set("frame_no", index);
1106
1107 // render frame
1108 renderJob->UpdateParameters(params, fractalParams);
1109 const int result = renderJob->Execute();
1110 if (!result) throw false;
1111
1112 // save frame
1113 QStringList listOfSavedFiles;
1114 const QString filename = GetFlightFilename(index, gNetRender->IsClient());
1115 const ImageFileSave::enumImageFileType fileType =
1116 ImageFileSave::enumImageFileType(params->Get<int>("flight_animation_image_type"));
1117 listOfSavedFiles = SaveImage(filename, fileType, image, gMainInterface->mainWindow);
1118
1119 renderedFramesCount++;
1120 alreadyRenderedFrames[index] = true;
1121
1122 // qDebug() << "Finished rendering frame" << index;
1123
1124 if (gNetRender->IsClient())
1125 {
1126 ConfirmAndSendRenderedFrames(index, listOfSavedFiles);
1127 }
1128
1129 gApplication->processEvents();
1130 }
1131
1132 emit updateProgressAndStatus(QObject::tr("Animation finished"), progressText.getText(1.0), 1.0,
1133 cProgressText::progress_IMAGE);
1134 emit notifyRenderFlightRenderStatus(
1135 QObject::tr("Animation finished"), progressText.getText(1.0));
1136 emit updateProgressHide();
1137 }
1138 catch (bool ex)
1139 {
1140 if (gNetRender->IsServer())
1141 {
1142 emit NetRenderStopAllClients();
1143 }
1144
1145 if (gNetRender->IsClient())
1146 {
1147 gNetRender->SetStatus(netRenderSts_READY);
1148 emit NetRenderNotifyClientStatus();
1149 }
1150
1151 QString resultStatus = QObject::tr("Rendering terminated");
1152 if (ex) resultStatus += " - " + QObject::tr("Error occured, see log output");
1153 emit updateProgressAndStatus(
1154 resultStatus, progressText.getText(1.0), cProgressText::progress_ANIMATION);
1155 emit updateProgressHide();
1156
1157 if (!systemData.noGui && image->IsMainImage())
1158 {
1159 mainInterface->mainWindow->GetWidgetDockNavigation()->UnlockAllFunctions();
1160 imageWidget->SetEnableClickModes(true);
1161 }
1162
1163 animationIsRendered = false;
1164 return false;
1165 }
1166
1167 if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
1168 {
1169 mainInterface->mainWindow->GetWidgetDockNavigation()->UnlockAllFunctions();
1170 imageWidget->SetEnableClickModes(true);
1171 }
1172
1173 if (gNetRender->IsClient())
1174 {
1175 gNetRender->SetStatus(netRenderSts_READY);
1176 emit NetRenderNotifyClientStatus();
1177 }
1178
1179 animationIsRendered = false;
1180 return true;
1181 }
1182
RefreshTable()1183 void cFlightAnimation::RefreshTable()
1184 {
1185 PrepareTable();
1186 gApplication->processEvents();
1187
1188 const int noOfFrames = frames->GetNumberOfFrames();
1189
1190 // it is needed to do it also here, because limits must be set just after loading of settings
1191 UpdateLimitsForFrameRange();
1192
1193 SynchronizeInterfaceWindow(ui->tab_flight_animation, params, qInterface::read);
1194
1195 auto tempPar = std::make_shared<cParameterContainer>();
1196 *tempPar = *params;
1197 auto tempFract = std::make_shared<cFractalContainer>();
1198 *tempFract = *fractalParams;
1199
1200 table->setColumnCount(noOfFrames);
1201
1202 for (int i = 0; i < noOfFrames; i++)
1203 {
1204 const int newColumn = AddColumn(frames->GetFrame(i), i);
1205
1206 if (ui->checkBox_flight_show_thumbnails->isChecked())
1207 {
1208 cThumbnailWidget *thumbWidget =
1209 new cThumbnailWidget(previewSize.width(), previewSize.height(), 1, table);
1210 thumbWidget->UseOneCPUCore(true);
1211 frames->GetFrameAndConsolidate(i, tempPar, tempFract);
1212 thumbWidget->AssignParameters(tempPar, tempFract);
1213 table->setCellWidget(0, newColumn, thumbWidget);
1214 }
1215 if (i % 100 == 0)
1216 {
1217 emit updateProgressAndStatus(QObject::tr("Refreshing animation"),
1218 tr("Refreshing animation frames"), double(i) / noOfFrames,
1219 cProgressText::progress_ANIMATION);
1220 gApplication->processEvents();
1221 }
1222
1223 if (systemData.globalStopRequest) break;
1224 }
1225 UpdateLimitsForFrameRange();
1226 emit updateProgressHide();
1227 }
1228
GetParameterName(int rowNumber)1229 QString cFlightAnimation::GetParameterName(int rowNumber)
1230 {
1231 const int parameterNumber = rowParameter[rowNumber];
1232
1233 QString fullParameterName;
1234 QList<cAnimationFrames::sParameterDescription> list = frames->GetListOfUsedParameters();
1235 if (parameterNumber >= 0)
1236 {
1237 fullParameterName =
1238 list[parameterNumber].containerName + "_" + list[parameterNumber].parameterName;
1239 }
1240 else
1241 {
1242 qCritical() << "cFlightAnimation::GetParameterNumber(int rowNumber): row not found";
1243 }
1244 return fullParameterName;
1245 }
1246
RenderFrame(int index) const1247 void cFlightAnimation::RenderFrame(int index) const
1248 {
1249 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1250 frames->GetFrameAndConsolidate(index, params, fractalParams);
1251 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::write);
1252
1253 mainInterface->StartRender();
1254 }
1255
DeleteFramesFrom(int index) const1256 void cFlightAnimation::DeleteFramesFrom(int index) const
1257 {
1258 gUndo->Store(params, fractalParams, frames, nullptr);
1259 for (int i = frames->GetNumberOfFrames() - 1; i >= index; i--)
1260 table->removeColumn(index);
1261 frames->DeleteFrames(index, frames->GetNumberOfFrames() - 1);
1262 UpdateLimitsForFrameRange();
1263 }
1264
DeleteFramesTo(int index) const1265 void cFlightAnimation::DeleteFramesTo(int index) const
1266 {
1267 gUndo->Store(params, fractalParams, frames, nullptr);
1268 for (int i = 0; i <= index; i++)
1269 table->removeColumn(0);
1270 frames->DeleteFrames(0, index);
1271 UpdateLimitsForFrameRange();
1272 }
1273
slotFlightStrafe(CVector2<double> _strafe)1274 void cFlightAnimation::slotFlightStrafe(CVector2<double> _strafe)
1275 {
1276 strafe = _strafe;
1277 }
1278
slotFlightYawAndPitch(CVector2<double> _yawAndPitch)1279 void cFlightAnimation::slotFlightYawAndPitch(CVector2<double> _yawAndPitch)
1280 {
1281 yawAndPitch = _yawAndPitch;
1282 }
1283
slotFlightChangeSpeed(double amount)1284 void cFlightAnimation::slotFlightChangeSpeed(double amount)
1285 {
1286 SynchronizeInterfaceWindow(
1287 ui->scrollAreaWidgetContents_flightAnimationParameters, params, qInterface::read);
1288 linearSpeedSp = params->Get<double>("flight_speed") * amount;
1289 params->Set("flight_speed", linearSpeedSp);
1290 SynchronizeInterfaceWindow(
1291 ui->scrollAreaWidgetContents_flightAnimationParameters, params, qInterface::write);
1292 }
1293
slotFlightSetSpeed(double amount)1294 void cFlightAnimation::slotFlightSetSpeed(double amount)
1295 {
1296 SynchronizeInterfaceWindow(
1297 ui->scrollAreaWidgetContents_flightAnimationParameters, params, qInterface::read);
1298 negativeFlightSpeed = (amount < 0) ? true : false;
1299 linearSpeedSp = fabs(amount);
1300 params->Set("flight_speed", linearSpeedSp);
1301 SynchronizeInterfaceWindow(
1302 ui->scrollAreaWidgetContents_flightAnimationParameters, params, qInterface::write);
1303 }
1304
slotFlightRotation(double direction)1305 void cFlightAnimation::slotFlightRotation(double direction)
1306 {
1307 rotationDirection = direction;
1308 }
1309
slotOrthogonalStrafe(bool _orthogonalStrafe)1310 void cFlightAnimation::slotOrthogonalStrafe(bool _orthogonalStrafe)
1311 {
1312 orthogonalStrafe = _orthogonalStrafe;
1313 }
1314
slotSelectAnimFlightImageDir() const1315 void cFlightAnimation::slotSelectAnimFlightImageDir() const
1316 {
1317 QFileDialog dialog;
1318 dialog.setFileMode(QFileDialog::DirectoryOnly);
1319 dialog.setNameFilter(QObject::tr("Animation Image Folder"));
1320 dialog.setDirectory(QDir::toNativeSeparators(params->Get<QString>("anim_flight_dir")));
1321 dialog.setAcceptMode(QFileDialog::AcceptOpen);
1322 dialog.setWindowTitle(QObject::tr("Choose Animation Image Folder"));
1323 dialog.setOption(QFileDialog::ShowDirsOnly);
1324
1325 if (dialog.exec())
1326 {
1327 QStringList fileNames = dialog.selectedFiles();
1328 const QString filename = QDir::toNativeSeparators(fileNames.first() + QDir::separator());
1329 ui->text_anim_flight_dir->setText(filename);
1330 params->Set("anim_flight_dir", filename);
1331 }
1332 }
1333
slotTableCellChanged(int row,int column)1334 void cFlightAnimation::slotTableCellChanged(int row, int column)
1335 {
1336 if (row > 0)
1337 {
1338 table->blockSignals(true);
1339 QTableWidgetItem *cell = table->item(row, column);
1340 QString cellText = cell->text();
1341
1342 cAnimationFrames::sAnimationFrame frame = frames->GetFrame(column);
1343
1344 const QString parameterName = GetParameterName(row);
1345 const int parameterFirstRow = parameterRows[rowParameter[row]];
1346 const int vectIndex = row - parameterFirstRow;
1347
1348 using namespace parameterContainer;
1349 const enumVarType type = frame.parameters.GetVarType(parameterName);
1350
1351 if (type == typeVector3)
1352 {
1353 CVector3 vect = frame.parameters.Get<CVector3>(parameterName);
1354 if (vectIndex == 0) vect.x = systemData.locale.toDouble(cellText);
1355 if (vectIndex == 1) vect.y = systemData.locale.toDouble(cellText);
1356 if (vectIndex == 2) vect.z = systemData.locale.toDouble(cellText);
1357 frame.parameters.Set(parameterName, vect);
1358 }
1359 else if (type == typeVector4)
1360 {
1361 CVector4 vect = frame.parameters.Get<CVector4>(parameterName);
1362 if (vectIndex == 0) vect.x = systemData.locale.toDouble(cellText);
1363 if (vectIndex == 1) vect.y = systemData.locale.toDouble(cellText);
1364 if (vectIndex == 2) vect.z = systemData.locale.toDouble(cellText);
1365 if (vectIndex == 3) vect.w = systemData.locale.toDouble(cellText);
1366 frame.parameters.Set(parameterName, vect);
1367 }
1368 else if (type == typeRgb)
1369 {
1370 sRGB col = frame.parameters.Get<sRGB>(parameterName);
1371 if (vectIndex == 0) col.R = cellText.toInt();
1372 if (vectIndex == 1) col.G = cellText.toInt();
1373 if (vectIndex == 2) col.B = cellText.toInt();
1374 frame.parameters.Set(parameterName, col);
1375 }
1376 else
1377 {
1378 frame.parameters.Set(parameterName, cellText);
1379 }
1380
1381 frames->ModifyFrame(column, frame);
1382
1383 // update thumbnail
1384 if (ui->checkBox_flight_show_thumbnails->isChecked())
1385 {
1386 auto tempPar = std::make_shared<cParameterContainer>();
1387 *tempPar = *params;
1388 auto tempFract = std::make_shared<cFractalContainer>();
1389 *tempFract = *fractalParams;
1390
1391 frames->GetFrameAndConsolidate(column, tempPar, tempFract);
1392 cThumbnailWidget *thumbWidget = static_cast<cThumbnailWidget *>(table->cellWidget(0, column));
1393
1394 if (!thumbWidget)
1395 {
1396 thumbWidget = new cThumbnailWidget(previewSize.width(), previewSize.height(), 1, table);
1397 thumbWidget->UseOneCPUCore(true);
1398 thumbWidget->AssignParameters(tempPar, tempFract);
1399 table->setCellWidget(0, column, thumbWidget);
1400 }
1401 else
1402 {
1403 thumbWidget->AssignParameters(tempPar, tempFract);
1404 }
1405 }
1406
1407 table->blockSignals(false);
1408 }
1409 }
1410
slotDeleteAllImages() const1411 void cFlightAnimation::slotDeleteAllImages() const
1412 {
1413 SynchronizeInterfaceWindow(
1414 ui->scrollAreaWidgetContents_flightAnimationParameters, params, qInterface::read);
1415
1416 const QMessageBox::StandardButton reply = QMessageBox::question(
1417 mainInterface->mainWindow->GetCentralWidget(), QObject::tr("Truncate Image Folder"),
1418 QObject::tr("This will delete all images in the image folder.\nProceed?"),
1419 QMessageBox::Yes | QMessageBox::No);
1420
1421 if (reply == QMessageBox::Yes)
1422 {
1423 cAnimationFrames::WipeFramesFromFolder(params->Get<QString>("anim_flight_dir"));
1424 }
1425 }
1426
slotShowAnimation() const1427 void cFlightAnimation::slotShowAnimation() const
1428 {
1429 WriteLog("Prepare PlayerWidget class", 2);
1430
1431 SynchronizeInterfaceWindow(
1432 ui->scrollAreaWidgetContents_keyframeAnimationParameters, params, qInterface::read);
1433
1434 if (!mainInterface->imageSequencePlayer)
1435 {
1436 mainInterface->imageSequencePlayer = new PlayerWidget;
1437 }
1438
1439 mainInterface->imageSequencePlayer->SetFilePath(params->Get<QString>("anim_flight_dir"));
1440 mainInterface->imageSequencePlayer->show();
1441 mainInterface->imageSequencePlayer->raise();
1442 mainInterface->imageSequencePlayer->activateWindow();
1443 }
1444
slotRecordPause()1445 void cFlightAnimation::slotRecordPause()
1446 {
1447 recordPause = !recordPause;
1448 // qDebug() << recordPause;
1449 }
1450
InterpolateForward(int row,int column)1451 void cFlightAnimation::InterpolateForward(int row, int column)
1452 {
1453 gUndo->Store(params, fractalParams, frames, nullptr);
1454
1455 QTableWidgetItem *cell = ui->tableWidget_flightAnimation->item(row, column);
1456 QString cellText = cell->text();
1457
1458 const QString parameterName = GetParameterName(row);
1459
1460 cAnimationFrames::sAnimationFrame frame = frames->GetFrame(column);
1461
1462 using namespace parameterContainer;
1463 const enumVarType type = frame.parameters.GetVarType(parameterName);
1464
1465 bool valueIsInteger = false;
1466 bool valueIsDouble = false;
1467 bool valueIsText = false;
1468 int valueInteger = 0;
1469 double valueDouble = 0.0;
1470 QString valueText;
1471
1472 bool ok;
1473 const int lastFrame = QInputDialog::getInt(mainInterface->mainWindow, "Parameter interpolation",
1474 "Enter last frame number", column + 1, column + 2, frames->GetNumberOfFrames(), 1, &ok);
1475 if (!ok) return;
1476
1477 const int numberOfFrames = (lastFrame - column - 1);
1478
1479 switch (type)
1480 {
1481 case typeBool:
1482 case typeInt:
1483 case typeRgb:
1484 {
1485 valueIsInteger = true;
1486 valueInteger = cellText.toInt();
1487 // qDebug() << valueInteger;
1488 break;
1489 }
1490 case typeDouble:
1491 case typeVector3:
1492 case typeVector4:
1493 {
1494 valueIsDouble = true;
1495 valueDouble = systemData.locale.toDouble(cellText);
1496 // qDebug() << valueDouble;
1497 break;
1498 }
1499 default:
1500 {
1501 valueIsText = true;
1502 valueText = cellText;
1503 break;
1504 }
1505 }
1506
1507 double integerStep = 0.0;
1508 double doubleStep = 0.0;
1509
1510 if (valueIsInteger)
1511 {
1512 const int finalInteger = QInputDialog::getInt(mainInterface->mainWindow,
1513 "Parameter interpolation", "Enter value for last frame", valueInteger, 0, 2147483647, 1, &ok);
1514 integerStep = double(finalInteger - valueInteger) / numberOfFrames;
1515 }
1516 else if (valueIsDouble)
1517 {
1518 const double finalDouble = systemData.locale.toDouble(QInputDialog::getText(
1519 mainInterface->mainWindow, "Parameter interpolation", "Enter value for last frame",
1520 QLineEdit::Normal, QString("%L1").arg(valueDouble, 0, 'g', 15), &ok));
1521 doubleStep = (finalDouble - valueDouble) / numberOfFrames;
1522 }
1523
1524 if (!ok) return;
1525
1526 for (int i = column; i < lastFrame; i++)
1527 {
1528 QString newCellText;
1529 if (valueIsInteger)
1530 {
1531 const int newValue = int(integerStep * (i - column) + valueInteger);
1532 newCellText = QString::number(newValue);
1533 }
1534 else if (valueIsDouble)
1535 {
1536 const double newValue = doubleStep * (i - column) + valueDouble;
1537 newCellText = QString("%L1").arg(newValue, 0, 'g', 15);
1538 }
1539 else if (valueIsText)
1540 {
1541 newCellText = valueText;
1542 }
1543 QTableWidgetItem *newCell = ui->tableWidget_flightAnimation->item(row, i);
1544 newCell->setText(newCellText);
1545 }
1546 }
1547
slotRefreshTable()1548 void cFlightAnimation::slotRefreshTable()
1549 {
1550 RefreshTable();
1551 }
1552
GetFlightFilename(int index,bool netRenderCache) const1553 QString cFlightAnimation::GetFlightFilename(int index, bool netRenderCache) const
1554 {
1555 QString dir;
1556 if (netRenderCache)
1557 {
1558 dir = systemDirectories.GetNetrenderFolder() + QDir::separator()
1559 + QString("pid%1_").arg(QCoreApplication::applicationPid());
1560 }
1561 else
1562 {
1563 dir = params->Get<QString>("anim_flight_dir");
1564 }
1565
1566 QString filename = dir + "frame_" + QString("%1").arg(index, 7, 10, QChar('0'));
1567 filename += "."
1568 + ImageFileSave::ImageFileExtension(
1569 ImageFileSave::enumImageFileType(params->Get<int>("flight_animation_image_type")));
1570 return filename;
1571 }
1572
slotExportFlightToKeyframes() const1573 void cFlightAnimation::slotExportFlightToKeyframes() const
1574 {
1575 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1576 gUndo->Store(params, fractalParams, gAnimFrames, gKeyframes);
1577
1578 if (gKeyframes->GetFrames().size() > 0)
1579 {
1580 const QMessageBox::StandardButton reply = QMessageBox::question(
1581 mainInterface->mainWindow->GetCentralWidget(), QObject::tr("Export flight to keyframes"),
1582 QObject::tr("There are already captured keyframes present.\nDiscard current keyframes?"),
1583 QMessageBox::Yes | QMessageBox::No);
1584
1585 if (reply == QMessageBox::No) return;
1586 }
1587
1588 gKeyframes->ClearAll();
1589 gKeyframes->ClearMorphCache();
1590 gKeyframes->SetListOfParametersAndClear(gAnimFrames->GetListOfParameters(), params);
1591
1592 const int step = params->Get<int>("frames_per_keyframe");
1593
1594 for (int i = 0; i < frames->GetNumberOfFrames(); i += step)
1595 {
1596 gKeyframes->AddFrame(frames->GetFrame(i));
1597 }
1598
1599 ui->tabWidgetFlightKeyframe->setCurrentIndex(1);
1600 ui->pushButton_refresh_keyframe_table->animateClick();
1601 }
1602
UpdateLimitsForFrameRange() const1603 void cFlightAnimation::UpdateLimitsForFrameRange() const
1604 {
1605 const int noOfFrames = frames->GetNumberOfFrames();
1606
1607 ui->spinboxInt_flight_first_to_render->setMaximum(noOfFrames);
1608
1609 ui->spinboxInt_flight_last_to_render->setMaximum(noOfFrames);
1610
1611 SynchronizeInterfaceWindow(ui->tab_flight_animation, gPar, qInterface::write);
1612 }
1613
slotMovedSliderFirstFrame(int value) const1614 void cFlightAnimation::slotMovedSliderFirstFrame(int value) const
1615 {
1616 if (value > ui->spinboxInt_flight_last_to_render->value())
1617 ui->spinboxInt_flight_last_to_render->setValue(value);
1618 }
1619
slotMovedSliderLastFrame(int value) const1620 void cFlightAnimation::slotMovedSliderLastFrame(int value) const
1621 {
1622 if (value < ui->spinboxInt_flight_first_to_render->value())
1623 ui->spinboxInt_flight_first_to_render->setValue(value);
1624 }
1625
slotCellDoubleClicked(int row,int column) const1626 void cFlightAnimation::slotCellDoubleClicked(int row, int column) const
1627 {
1628 if (row == 0)
1629 {
1630 RenderFrame(column);
1631 }
1632 }
1633
SetNetRenderStartingFrames(const QVector<int> & startingFrames)1634 void cFlightAnimation::SetNetRenderStartingFrames(const QVector<int> &startingFrames)
1635 {
1636 netRenderListOfFramesToRender.clear();
1637 netRenderListOfFramesToRender.append(startingFrames.toList());
1638 }
1639
slotNetRenderFinishedFrame(int clientIndex,int frameIndex,int sizeOfToDoList)1640 void cFlightAnimation::slotNetRenderFinishedFrame(
1641 int clientIndex, int frameIndex, int sizeOfToDoList)
1642 {
1643 Q_UNUSED(frameIndex);
1644
1645 renderedFramesCount++;
1646 if (frameIndex < alreadyRenderedFrames.size())
1647 {
1648 alreadyRenderedFrames[frameIndex] = true;
1649 }
1650
1651 if (!animationStopRequest && animationIsRendered)
1652 {
1653 // qDebug() << "Server: got information about finished frame" << frameIndex << sizeOfToDoList;
1654
1655 // counting left frames
1656 int countLeft = reservedFrames.count(false);
1657
1658 // calculate maximum list size
1659 int numberOfFramesForNetRender = countLeft / gNetRender->GetClientCount() / 2;
1660 if (numberOfFramesForNetRender < minFramesForNetRender)
1661 numberOfFramesForNetRender = minFramesForNetRender;
1662 if (numberOfFramesForNetRender > maxFramesForNetRender)
1663 numberOfFramesForNetRender = maxFramesForNetRender;
1664
1665 // calculate number for frames to supplement
1666 int numberOfNewFrames = numberOfFramesForNetRender - sizeOfToDoList;
1667
1668 if (numberOfNewFrames > 0)
1669 {
1670 QList<int> toDoList;
1671
1672 // looking for not reserved frame
1673 for (int f = 0; f < reservedFrames.size(); f++)
1674 {
1675 if (!reservedFrames[f])
1676 {
1677 toDoList.append(f);
1678 reservedFrames[f] = true;
1679 }
1680 if (toDoList.size() >= numberOfNewFrames) break;
1681 }
1682 NetRenderSendFramesToDoList(clientIndex, toDoList);
1683 // qDebug() << "Server: new toDo list" << toDoList;
1684 }
1685 }
1686 }
1687
slotNetRenderUpdateFramesToDo(QList<int> listOfFrames)1688 void cFlightAnimation::slotNetRenderUpdateFramesToDo(QList<int> listOfFrames)
1689 {
1690 if (animationIsRendered)
1691 {
1692 netRenderListOfFramesToRender.append(listOfFrames);
1693 // qDebug() << "Client: got frames toDo:" << listOfFrames;
1694 }
1695 }
1696
slotAnimationStopRequest()1697 void cFlightAnimation::slotAnimationStopRequest()
1698 {
1699 animationStopRequest = true;
1700 }
1701
cFligtAnimRenderThread(QString & _settingsText)1702 cFligtAnimRenderThread::cFligtAnimRenderThread(QString &_settingsText) : QThread()
1703 {
1704 settingsText = _settingsText;
1705 }
1706
startAnimationRender()1707 void cFligtAnimRenderThread::startAnimationRender()
1708 {
1709 cSettings parSettings(cSettings::formatFullText);
1710 parSettings.BeQuiet(true);
1711 parSettings.LoadFromString(settingsText);
1712 parSettings.Decode(gPar, gParFractal, gAnimFrames, nullptr);
1713 gNetRender->SetAnimation(true);
1714 gFlightAnimation->RenderFlight(&gMainInterface->stopRequest);
1715 emit renderingFinished();
1716 }
1717