1 /**
2 * Mandelbulber v2, a 3D fractal generator ,=#MKNmMMKmmßMNWy,
3 * ,B" ]L,,p%%%,,,§;, "K
4 * Copyright (C) 2015-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 * cKeyframeAnimation contains all functionality for editing and rendering of
35 * a keyframe animation. The class holds a cKeyframes to store
36 * the parameters of the keyframes and a table to display the frames in a widget.
37 * The class can render interpolated sub-frames and validate their path (check
38 * for collision). It exposes slots to manipulate the keyframes.
39 */
40
41 #include "animation_keyframes.hpp"
42
43 #include <memory>
44
45 #include "ui_dock_animation.h"
46
47 #include "animation_path_data.hpp"
48 #include "cimage.hpp"
49 #include "common_math.h"
50 #include "files.h"
51 #include "global_data.hpp"
52 #include "headless.h"
53 #include "initparameters.hpp"
54 #include "interface.hpp"
55 #include "light.h"
56 #include "netrender.hpp"
57 #include "render_job.hpp"
58 #include "render_window.hpp"
59 #include "rendered_image_widget.hpp"
60 #include "rendering_configuration.hpp"
61 #include "settings.hpp"
62 #include "system_data.hpp"
63 #include "system_directories.hpp"
64 #include "undo.h"
65 #include "write_log.hpp"
66
67 #include "qt/dock_animation.h"
68 #include "qt/dock_navigation.h"
69 #include "qt/dock_statistics.h"
70 #include "qt/my_progress_bar.h"
71 #include "qt/my_table_widget_keyframes.hpp"
72 #include "qt/player_widget.hpp"
73 #include "qt/pushbutton_anim_sound.h"
74 #include "qt/randomizer_dialog.h"
75 #include "qt/system_tray.hpp"
76 #include "qt/thumbnail_widget.h"
77
78 cKeyframeAnimation *gKeyframeAnimation = nullptr;
79
cKeyframeAnimation(cInterface * _interface,std::shared_ptr<cKeyframes> _frames,std::shared_ptr<cImage> _image,QWidget * _imageWidget,std::shared_ptr<cParameterContainer> _params,std::shared_ptr<cFractalContainer> _fractal,QObject * parent)80 cKeyframeAnimation::cKeyframeAnimation(cInterface *_interface, std::shared_ptr<cKeyframes> _frames,
81 std::shared_ptr<cImage> _image, QWidget *_imageWidget,
82 std::shared_ptr<cParameterContainer> _params, std::shared_ptr<cFractalContainer> _fractal,
83 QObject *parent)
84 : QObject(parent), mainInterface(_interface), keyframes(_frames)
85 {
86 image = _image;
87 imageWidget = dynamic_cast<RenderedImage *>(_imageWidget);
88 params = _params;
89 fractalParams = _fractal;
90
91 if (mainInterface->mainWindow)
92 {
93 ui = mainInterface->mainWindow->GetWidgetDockAnimation()->GetUi();
94
95 previewSize.setWidth(systemData.GetPreferredThumbnailSize() * 5 / 4);
96 previewSize.setHeight(previewSize.width() * 3 / 4);
97
98 // connect keyframe control buttons
99 connect(ui->pushButton_add_keyframe, &QPushButton::clicked, this,
100 &cKeyframeAnimation::slotAddKeyframe);
101 connect(ui->pushButton_insert_keyframe, &QPushButton::clicked, this,
102 &cKeyframeAnimation::slotInsertKeyframe);
103 connect(ui->pushButton_delete_keyframe, &QPushButton::clicked, this,
104 &cKeyframeAnimation::slotDeleteKeyframe);
105 connect(ui->pushButton_modify_keyframe, &QPushButton::clicked, this,
106 &cKeyframeAnimation::slotModifyKeyframe);
107 connect(ui->pushButton_render_keyframe_animation, &QPushButton::clicked, this,
108 &cKeyframeAnimation::slotRenderKeyframes);
109 connect(ui->pushButton_delete_all_keyframe_images, &QPushButton::clicked, this,
110 &cKeyframeAnimation::slotDeleteAllImages);
111 connect(ui->pushButton_show_keyframe_animation, &QPushButton::clicked, this,
112 &cKeyframeAnimation::slotShowAnimation);
113 connect(ui->pushButton_refresh_keyframe_table, &QPushButton::clicked, this,
114 &cKeyframeAnimation::slotRefreshTable);
115 connect(ui->pushButton_keyframe_to_flight_export, &QPushButton::clicked, this,
116 &cKeyframeAnimation::slotExportKeyframesToFlight);
117 connect(ui->pushButton_check_for_collisions, &QPushButton::clicked, this,
118 &cKeyframeAnimation::slotValidate);
119 connect(ui->pushButton_set_constant_target_distance, &QPushButton::clicked, this,
120 &cKeyframeAnimation::slotSetConstantTargetDistance);
121 connect(ui->button_selectAnimKeyframeImageDir, &QPushButton::clicked, this,
122 &cKeyframeAnimation::slotSelectKeyframeAnimImageDir);
123 connect(
124 ui->pushButton_randomize, &QPushButton::clicked, this, &cKeyframeAnimation::slotRandomize);
125 connect(ui->pushButton_add_all_parameters, &QPushButton::clicked, this,
126 &cKeyframeAnimation::slotAddAllParameters);
127 connect(ui->pushButton_set_frames_per_keyframe_to_all, &QPushButton::clicked, this,
128 &cKeyframeAnimation::slotSetFramesPerKeyframeToAllKeyframes);
129
130 connect(ui->tableWidget_keyframe_animation, &QTableWidget::cellChanged, this,
131 &cKeyframeAnimation::slotTableCellChanged);
132 connect(ui->spinboxInt_keyframe_first_to_render, &QSpinBox::editingFinished, this,
133 &cKeyframeAnimation::slotMovedSliderFirstFrame);
134 connect(ui->spinboxInt_keyframe_last_to_render, &QSpinBox::editingFinished, this,
135 &cKeyframeAnimation::slotMovedSliderLastFrame);
136 connect(ui->spinboxInt_frames_per_keyframe, &QSpinBox::editingFinished, this,
137 &cKeyframeAnimation::UpdateLimitsForFrameRange);
138 connect(ui->tableWidget_keyframe_animation, &QTableWidget::cellDoubleClicked, this,
139 &cKeyframeAnimation::slotCellDoubleClicked);
140 connect(ui->tableWidget_keyframe_animation, &QTableWidget::cellClicked, this,
141 &cKeyframeAnimation::slotCellClicked);
142
143 // connect system tray
144 connect(mainInterface->systemTray, &cSystemTray::notifyRenderKeyframes, this,
145 &cKeyframeAnimation::slotRenderKeyframes);
146 connect(this, &cKeyframeAnimation::notifyRenderKeyframeRenderStatus, mainInterface->systemTray,
147 &cSystemTray::showMessage);
148
149 connect(this, &cKeyframeAnimation::QuestionMessage, mainInterface->mainWindow,
150 &RenderWindow::slotQuestionMessage);
151
152 connect(ui->checkBox_show_camera_path, &QCheckBox::stateChanged, this,
153 &cKeyframeAnimation::slotUpdateAnimationPathSelection);
154 connect(ui->checkBox_show_target_path, &QCheckBox::stateChanged, this,
155 &cKeyframeAnimation::slotUpdateAnimationPathSelection);
156 connect(ui->checkBox_show_light_path_1, &QCheckBox::stateChanged, this,
157 &cKeyframeAnimation::slotUpdateAnimationPathSelection);
158 connect(ui->checkBox_show_light_path_2, &QCheckBox::stateChanged, this,
159 &cKeyframeAnimation::slotUpdateAnimationPathSelection);
160 connect(ui->checkBox_show_light_path_3, &QCheckBox::stateChanged, this,
161 &cKeyframeAnimation::slotUpdateAnimationPathSelection);
162 connect(ui->checkBox_show_light_path_4, &QCheckBox::stateChanged, this,
163 &cKeyframeAnimation::slotUpdateAnimationPathSelection);
164
165 table = ui->tableWidget_keyframe_animation;
166
167 // add default parameters for animation
168 if (keyframes->GetListOfUsedParameters().size() == 0)
169 {
170 keyframes->AddAnimatedParameter("camera", params->GetAsOneParameter("camera"), params);
171 keyframes->AddAnimatedParameter("target", params->GetAsOneParameter("target"), params);
172 keyframes->AddAnimatedParameter(
173 "camera_top", params->GetAsOneParameter("camera_top"), params);
174 if (mainInterface->mainWindow) PrepareTable();
175 }
176 }
177 else
178 {
179 ui = nullptr;
180 table = nullptr;
181 }
182
183 // NetRender for animation
184 // signals to NetRender
185 connect(this, &cKeyframeAnimation::SendNetRenderSetup, gNetRender, &cNetRender::SendSetup);
186 connect(this, &cKeyframeAnimation::NetRenderCurrentAnimation, gNetRender,
187 &cNetRender::SetCurrentAnimation);
188 connect(this, &cKeyframeAnimation::NetRenderConfirmRendered, gNetRender,
189 &cNetRender::ConfirmRenderedFrame);
190 connect(
191 this, &cKeyframeAnimation::NetRenderAddFileToSender, gNetRender, &cNetRender::AddFileToSender);
192 connect(
193 this, &cKeyframeAnimation::NetRenderNotifyClientStatus, gNetRender, &cNetRender::NotifyStatus);
194
195 // signals from NetRender
196 connect(
197 gNetRender, &cNetRender::FinishedFrame, this, &cKeyframeAnimation::slotNetRenderFinishedFrame);
198 connect(this, &cKeyframeAnimation::NetRenderSendFramesToDoList, gNetRender,
199 &cNetRender::SendFramesToDoList);
200 connect(gNetRender, &cNetRender::UpdateFramesToDo, this,
201 &cKeyframeAnimation::slotNetRenderUpdateFramesToDo);
202 connect(
203 this, &cKeyframeAnimation::NetRenderStopAllClients, gNetRender, &cNetRender::StopAllClients);
204 connect(gNetRender, &cNetRender::animationStopRequest, this,
205 &cKeyframeAnimation::slotAnimationStopRequest);
206
207 connect(this, SIGNAL(showErrorMessage(QString, cErrorMessage::enumMessageType, QWidget *)),
208 gErrorMessage, SLOT(slotShowMessage(QString, cErrorMessage::enumMessageType, QWidget *)));
209 }
210
slotAddKeyframe()211 void cKeyframeAnimation::slotAddKeyframe()
212 {
213 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
214 gUndo->Store(params, fractalParams, nullptr, keyframes);
215
216 NewKeyframe(keyframes->GetNumberOfFrames());
217
218 keyframes->UpdateFramesIndexesTable();
219 }
220
slotInsertKeyframe()221 void cKeyframeAnimation::slotInsertKeyframe()
222 {
223 int index = table->currentColumn() - reservedColumns;
224 if (index < 0) index = 0;
225
226 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
227 gUndo->Store(params, fractalParams, nullptr, keyframes);
228
229 NewKeyframe(index);
230 }
231
NewKeyframe(int index)232 void cKeyframeAnimation::NewKeyframe(int index)
233 {
234 if (keyframes)
235 {
236 // add new frame to container
237 keyframes->AddFrame(params, fractalParams, params->Get<int>("frames_per_keyframe"), index);
238 keyframes->UpdateFramesIndexesTable();
239
240 params->Set("frame_no", keyframes->GetFrameIndexForKeyframe(index));
241
242 // add column to table
243 const int newColumn = AddColumn(keyframes->GetFrame(index), index);
244 table->selectColumn(newColumn);
245
246 if (ui->checkBox_show_keyframe_thumbnails->isChecked())
247 {
248 cThumbnailWidget *thumbWidget =
249 new cThumbnailWidget(previewSize.width(), previewSize.height(), 1, table);
250 thumbWidget->UseOneCPUCore(false);
251 thumbWidget->AssignParameters(params, fractalParams);
252 table->setCellWidget(0, newColumn, thumbWidget);
253 }
254
255 UpdateLimitsForFrameRange();
256
257 UpdateAnimationPath();
258 }
259 else
260 {
261 qCritical() << "gAnimFrames not allocated";
262 }
263 }
264
DeleteKeyframe(int index) const265 void cKeyframeAnimation::DeleteKeyframe(int index) const
266 {
267 if (index < 0)
268 {
269 cErrorMessage::showMessage(QObject::tr("No keyframe selected"), cErrorMessage::errorMessage,
270 mainInterface->mainWindow->GetCentralWidget());
271 }
272 else
273 {
274 gUndo->Store(params, fractalParams, nullptr, keyframes);
275 keyframes->DeleteFrames(index, index);
276 table->removeColumn(index + reservedColumns);
277 keyframes->UpdateFramesIndexesTable();
278 UpdateLimitsForFrameRange();
279 UpdateAnimationPath();
280 }
281 }
282
slotModifyKeyframe()283 void cKeyframeAnimation::slotModifyKeyframe()
284 {
285 const int index = table->currentColumn() - reservedColumns;
286 if (index < 0)
287 {
288 cErrorMessage::showMessage(QObject::tr("No keyframe selected"), cErrorMessage::errorMessage,
289 mainInterface->mainWindow->GetCentralWidget());
290 }
291 else
292 {
293 if (keyframes)
294 {
295 // get latest values of all parameters
296 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
297 gUndo->Store(params, fractalParams, nullptr, keyframes);
298
299 int framesPerKeyframe = keyframes->GetFrame(index).numberOfSubFrames;
300
301 // add new frame to container
302 keyframes->DeleteFrames(index, index);
303 keyframes->AddFrame(params, fractalParams, framesPerKeyframe, index);
304
305 keyframes->UpdateFramesIndexesTable();
306
307 // add column to table
308 table->removeColumn(index + reservedColumns);
309 const int newColumn = AddColumn(keyframes->GetFrame(index), index);
310 table->selectColumn(newColumn);
311
312 if (ui->checkBox_show_keyframe_thumbnails->isChecked())
313 {
314 cThumbnailWidget *thumbWidget =
315 new cThumbnailWidget(previewSize.width(), previewSize.height(), 1, table);
316 thumbWidget->UseOneCPUCore(false);
317 thumbWidget->AssignParameters(params, fractalParams);
318 table->setCellWidget(0, newColumn, thumbWidget);
319 }
320
321 UpdateAnimationPath();
322 }
323 else
324 {
325 qCritical() << "gAnimFrames not allocated";
326 }
327 }
328 }
329
slotDeleteKeyframe() const330 void cKeyframeAnimation::slotDeleteKeyframe() const
331 {
332 const int index = table->currentColumn() - reservedColumns;
333 DeleteKeyframe(index);
334
335 keyframes->UpdateFramesIndexesTable();
336 }
337
slotRenderKeyframes()338 bool cKeyframeAnimation::slotRenderKeyframes()
339 {
340 // get latest values of all parameters
341 if (mainInterface->mainWindow)
342 {
343 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
344 }
345
346 if (keyframes)
347 {
348 if (keyframes->GetNumberOfFrames() == 0)
349 {
350 emit showErrorMessage(QObject::tr("No frames to render"), cErrorMessage::errorMessage,
351 mainInterface->mainWindow->GetCentralWidget());
352 }
353 else if (!QDir(params->Get<QString>("anim_keyframe_dir")).exists())
354 {
355 emit showErrorMessage(
356 QObject::tr("The folder %1 does not exist. Please specify a valid location.")
357 .arg(params->Get<QString>("anim_keyframe_dir")),
358 cErrorMessage::errorMessage, mainInterface->mainWindow->GetCentralWidget());
359 }
360 else
361 {
362 return RenderKeyframes(&gMainInterface->stopRequest);
363 }
364 }
365 else
366 {
367 qCritical() << "gAnimFrames not allocated";
368 }
369 return false;
370 }
371
slotIncreaseCurrentTableIndex()372 void cKeyframeAnimation::slotIncreaseCurrentTableIndex()
373 {
374 int nextColumn = (table->currentColumn() + 1) % table->columnCount();
375 table->setCurrentCell(table->currentRow(), nextColumn);
376 }
377
slotDecreaseCurrentTableIndex()378 void cKeyframeAnimation::slotDecreaseCurrentTableIndex()
379 {
380 int nextColumn = (table->columnCount() + table->currentColumn() - 1) % table->columnCount();
381 table->setCurrentCell(table->currentRow(), nextColumn);
382 }
383
PrepareTable()384 void cKeyframeAnimation::PrepareTable()
385 {
386 table->setRowCount(0);
387 table->setColumnCount(0);
388 table->clear();
389 tableRowNames.clear();
390 QFontMetrics fm(mainInterface->mainWindow->font());
391 table->verticalHeader()->setDefaultSectionSize(fm.height() + 6);
392 AddAnimSoundColumn();
393 CreateRowsInTable();
394 }
395
CreateRowsInTable()396 void cKeyframeAnimation::CreateRowsInTable()
397 {
398 QList<cAnimationFrames::sParameterDescription> parList = keyframes->GetListOfUsedParameters();
399 table->setIconSize(previewSize);
400 table->insertRow(0);
401 table->setVerticalHeaderItem(0, new QTableWidgetItem(tr("Keyframe\npreviews")));
402 table->setRowHeight(0, previewSize.height());
403 tableRowNames.append(tr("Keyframe\npreviews"));
404
405 table->insertRow(framesPerKeyframeRow);
406 table->setVerticalHeaderItem(framesPerKeyframeRow, new QTableWidgetItem(tr("Frames/keyframe")));
407 tableRowNames.append(tr("Frames/keyframe"));
408
409 table->insertRow(cameraSpeedRow);
410 table->setVerticalHeaderItem(
411 cameraSpeedRow, new QTableWidgetItem(tr("Camera speed [units/frame]")));
412 tableRowNames.append(tr("Camera speed [units/frame]"));
413
414 rowParameter.clear();
415 rowParameter.append(-1); // prevuew
416 rowParameter.append(-1); // frames per keyframe
417 rowParameter.append(-1); // camera speed
418
419 parameterRows.clear();
420 for (int i = 0; i < parList.size(); ++i)
421 {
422 int row = AddVariableToTable(parList[i], i);
423 parameterRows.append(row);
424 }
425 }
426
AddVariableToTable(const cAnimationFrames::sParameterDescription & parameterDescription,int index)427 int cKeyframeAnimation::AddVariableToTable(
428 const cAnimationFrames::sParameterDescription ¶meterDescription, int index)
429 {
430 using namespace parameterContainer;
431 const enumVarType type = parameterDescription.varType;
432 const int row = table->rowCount();
433 if (type == typeVector3)
434 {
435 QString varName =
436 parameterDescription.containerName + "_" + parameterDescription.parameterName + "_x";
437 AddRow(row, varName, index);
438
439 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_y";
440 AddRow(row + 1, varName, index);
441
442 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_z";
443 AddRow(row + 2, varName, index);
444 }
445 else if (type == typeVector4)
446 {
447 QString varName =
448 parameterDescription.containerName + "_" + parameterDescription.parameterName + "_x";
449 AddRow(row, varName, index);
450
451 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_y";
452 AddRow(row + 1, varName, index);
453
454 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_z";
455 AddRow(row + 2, varName, index);
456
457 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_w";
458 AddRow(row + 3, varName, index);
459 }
460 else if (type == typeRgb)
461 {
462 QString varName =
463 parameterDescription.containerName + "_" + parameterDescription.parameterName + "_R";
464 AddRow(row, varName, index);
465
466 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_G";
467 AddRow(row + 1, varName, index);
468
469 varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_B";
470 AddRow(row + 2, varName, index);
471 }
472 else
473 {
474 const QString varName =
475 parameterDescription.containerName + "_" + parameterDescription.parameterName;
476 AddRow(row, varName, index);
477 }
478 return row;
479 }
480
AddRow(int row,const QString & fullParameterName,int index)481 void cKeyframeAnimation::AddRow(int row, const QString &fullParameterName, int index)
482 {
483 tableRowNames.append(fullParameterName);
484 table->insertRow(row);
485 table->setVerticalHeaderItem(row, new QTableWidgetItem(fullParameterName));
486 rowParameter.append(index);
487
488 cPushButtonAnimSound *audioSelectorButton = new cPushButtonAnimSound(table);
489 table->setCellWidget(row, animSoundColumn, audioSelectorButton);
490 connect(audioSelectorButton, &cPushButtonAnimSound::signalAudioSelectorClosed, this,
491 &cKeyframeAnimation::slotRefreshTable);
492
493 static_cast<cPushButtonAnimSound *>(table->cellWidget(row, animSoundColumn))
494 ->AssignParameterName(fullParameterName);
495 static_cast<cPushButtonAnimSound *>(table->cellWidget(row, animSoundColumn))
496 ->AssignAnimation(keyframes);
497 }
498
AddColumn(const cAnimationFrames::sAnimationFrame & frame,int index)499 int cKeyframeAnimation::AddColumn(const cAnimationFrames::sAnimationFrame &frame, int index)
500 {
501 table->blockSignals(true);
502 int newColumn = index + reservedColumns;
503 if (index == -1)
504 {
505 newColumn = table->columnCount();
506 index = newColumn - reservedColumns;
507 }
508 table->insertColumn(newColumn);
509 table->setColumnWidth(newColumn, previewSize.width());
510
511 const int frameNo = keyframes->GetFrameIndexForKeyframe(newColumn - reservedColumns);
512 const double time = double(frameNo) / params->Get<double>("keyframe_frames_per_second");
513 const int minutes = int(time / 60);
514 const int seconds = int(time) % 60;
515 const int milliseconds = int(time * 1000.0) % 1000;
516
517 const QString columnHeader = QString("%1 (%2)\n(%3:%4.%5)")
518 .arg(newColumn - reservedColumns)
519 .arg(frameNo)
520 .arg(minutes)
521 .arg(seconds, 2, 10, QChar('0'))
522 .arg(milliseconds, 3, 10, QChar('0'));
523 table->setHorizontalHeaderItem(newColumn, new QTableWidgetItem(columnHeader));
524
525 table->setItem(framesPerKeyframeRow, newColumn,
526 new QTableWidgetItem(QString::number(frame.numberOfSubFrames)));
527
528 int nextIndex = min(index + 1, keyframes->GetNumberOfFrames() - 1);
529 const cAnimationFrames::sAnimationFrame &nextFrame = keyframes->GetFrame(nextIndex);
530 double cameraSpeed = GetCameraSpeed(frame, nextFrame);
531 table->setItem(cameraSpeedRow, newColumn, new QTableWidgetItem(QString::number(cameraSpeed)));
532 table->item(cameraSpeedRow, newColumn)->setFlags(Qt::NoItemFlags);
533
534 QList<cAnimationFrames::sParameterDescription> parList = keyframes->GetListOfUsedParameters();
535
536 using namespace parameterContainer;
537 for (int i = 0; i < parList.size(); ++i)
538 {
539 const QString parameterName = parList[i].containerName + "_" + parList[i].parameterName;
540 const enumVarType type = parList[i].varType;
541 const int row = parameterRows[i];
542
543 cOneParameter parameter = frame.parameters.GetAsOneParameter(parameterName);
544 const parameterContainer::enumMorphType morphType = parameter.GetMorphType();
545
546 if (type == typeVector3)
547 {
548 const CVector3 val = parameter.Get<CVector3>(valueActual);
549 table->setItem(row, newColumn, new QTableWidgetItem(QString("%L1").arg(val.x, 0, 'g', 15)));
550 table->setItem(
551 row + 1, newColumn, new QTableWidgetItem(QString("%L1").arg(val.y, 0, 'g', 15)));
552 table->setItem(
553 row + 2, newColumn, new QTableWidgetItem(QString("%L1").arg(val.z, 0, 'g', 15)));
554 table->item(row, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
555 table->item(row + 1, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
556 table->item(row + 2, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
557 table->item(row, newColumn)->setForeground(QBrush(Qt::black));
558 table->item(row + 1, newColumn)->setForeground(QBrush(Qt::black));
559 table->item(row + 2, newColumn)->setForeground(QBrush(Qt::black));
560 }
561 else if (type == typeVector4)
562 {
563 const CVector4 val = parameter.Get<CVector4>(valueActual);
564 table->setItem(row, newColumn, new QTableWidgetItem(QString("%L1").arg(val.x, 0, 'g', 15)));
565 table->setItem(
566 row + 1, newColumn, new QTableWidgetItem(QString("%L1").arg(val.y, 0, 'g', 15)));
567 table->setItem(
568 row + 2, newColumn, new QTableWidgetItem(QString("%L1").arg(val.z, 0, 'g', 15)));
569 table->setItem(
570 row + 3, newColumn, new QTableWidgetItem(QString("%L1").arg(val.w, 0, 'g', 15)));
571 table->item(row, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
572 table->item(row + 1, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
573 table->item(row + 2, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
574 table->item(row + 3, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
575 table->item(row, newColumn)->setForeground(QBrush(Qt::black));
576 table->item(row + 1, newColumn)->setForeground(QBrush(Qt::black));
577 table->item(row + 2, newColumn)->setForeground(QBrush(Qt::black));
578 table->item(row + 3, newColumn)->setForeground(QBrush(Qt::black));
579 }
580 else if (type == typeRgb)
581 {
582 const sRGB val = parameter.Get<sRGB>(valueActual);
583 table->setItem(row, newColumn, new QTableWidgetItem(QString::number(val.R)));
584 table->setItem(row + 1, newColumn, new QTableWidgetItem(QString::number(val.G)));
585 table->setItem(row + 2, newColumn, new QTableWidgetItem(QString::number(val.B)));
586 table->item(row, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
587 table->item(row + 1, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
588 table->item(row + 2, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
589 table->item(row, newColumn)->setForeground(QBrush(Qt::black));
590 table->item(row + 1, newColumn)->setForeground(QBrush(Qt::black));
591 table->item(row + 2, newColumn)->setForeground(QBrush(Qt::black));
592 }
593 else
594 {
595 const QString val = parameter.Get<QString>(valueActual);
596 table->setItem(row, newColumn, new QTableWidgetItem(val));
597 table->item(row, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
598 table->item(row, newColumn)->setForeground(QBrush(Qt::black));
599 }
600 }
601 table->blockSignals(false);
602 return newColumn;
603 }
604
MorphType2Color(parameterContainer::enumMorphType morphType)605 QColor cKeyframeAnimation::MorphType2Color(parameterContainer::enumMorphType morphType)
606 {
607 using namespace parameterContainer;
608 QColor color;
609 switch (morphType)
610 {
611 case morphNone: color = QColor(255, 255, 255); break; // white
612 case morphLinear: color = QColor(200, 200, 200); break; // gray
613 case morphLinearAngle: color = QColor(200, 200, 255); break; // gray/blue
614 case morphCatMullRom: color = QColor(200, 255, 200); break; // green
615 case morphCatMullRomAngle: color = QColor(200, 255, 255); break; // green/blue
616 case morphAkima: color = QColor(255, 200, 200); break; // red
617 case morphAkimaAngle: color = QColor(255, 200, 255); break; // red/blue
618 case morphCubic: color = QColor(255, 255, 200); break; // yellow
619 case morphCubicAngle: color = QColor(255, 200, 255); break; // yellow/blue
620 case morphSteffen: color = QColor(150, 150, 200); break; // blue
621 case morphSteffenAngle: color = QColor(150, 150, 255); break; // blue
622 }
623 return color;
624 }
625
PrepareRenderJob(bool * stopRequest)626 std::shared_ptr<cRenderJob> cKeyframeAnimation::PrepareRenderJob(bool *stopRequest)
627 {
628 // preparing Render Job
629 std::shared_ptr<cRenderJob> renderJob(
630 new cRenderJob(params, fractalParams, image, stopRequest, imageWidget));
631 connect(renderJob.get(),
632 SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), this,
633 SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)));
634 connect(renderJob.get(), SIGNAL(updateStatistics(cStatistics)), this,
635 SIGNAL(updateStatistics(cStatistics)));
636 if (!systemData.noGui)
637 {
638 connect(renderJob.get(), SIGNAL(updateImage()), mainInterface->renderedImage, SLOT(update()));
639 connect(renderJob.get(), SIGNAL(sendRenderedTilesList(QList<sRenderedTileData>)),
640 mainInterface->renderedImage, SLOT(showRenderedTilesList(QList<sRenderedTileData>)));
641 }
642 return renderJob;
643 }
644
InitFrameRanges(sFrameRanges * frameRanges)645 bool cKeyframeAnimation::InitFrameRanges(sFrameRanges *frameRanges)
646 {
647 // range of keyframes to render
648 frameRanges->startFrame = params->Get<int>("keyframe_first_to_render");
649 frameRanges->endFrame = params->Get<int>("keyframe_last_to_render");
650 frameRanges->totalFrames = keyframes->GetTotalNumberOfFrames();
651 if (frameRanges->totalFrames < 1) frameRanges->totalFrames = 1;
652
653 if (frameRanges->endFrame == 0) frameRanges->endFrame = frameRanges->totalFrames;
654
655 if (frameRanges->startFrame == frameRanges->endFrame)
656 {
657 emit showErrorMessage(
658 QObject::tr(
659 "There is no frame to render: first frame to render and last frame to render are equals."),
660 cErrorMessage::warningMessage);
661 return false;
662 }
663 return true;
664 }
665
InitFrameMarkers(const sFrameRanges & frameRanges)666 void cKeyframeAnimation::InitFrameMarkers(const sFrameRanges &frameRanges)
667 {
668 alreadyRenderedFrames.resize(frameRanges.totalFrames);
669 alreadyRenderedFrames.fill(false);
670 reservedFrames.clear();
671 reservedFrames.resize(frameRanges.totalFrames);
672 reservedFrames.fill(false);
673 }
674
VerifyAnimation(bool * stopRequest)675 void cKeyframeAnimation::VerifyAnimation(bool *stopRequest)
676 {
677 // checking for collisions
678 if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
679 {
680 if (params->Get<bool>("keyframe_auto_validate"))
681 {
682 keyframes->ClearMorphCache();
683 QList<int> listOfCollisions =
684 CheckForCollisions(params->Get<double>("keyframe_collision_thresh"), stopRequest);
685 if (*stopRequest) throw false;
686
687 if (listOfCollisions.size() > 0)
688 {
689 QString collisionText;
690 for (int i = 0; i < listOfCollisions.size(); i++)
691 {
692 collisionText += QString("%1").arg(listOfCollisions.at(i));
693 if (i < listOfCollisions.size() - 1) collisionText += QString(", ");
694 }
695 emit showErrorMessage(
696 QObject::tr("Camera collides with fractal at following frames:\n") + collisionText,
697 cErrorMessage::warningMessage);
698 }
699 }
700 }
701 }
702
CheckWhichFramesAreAlreadyRendered(const sFrameRanges & frameRanges)703 void cKeyframeAnimation::CheckWhichFramesAreAlreadyRendered(const sFrameRanges &frameRanges)
704 {
705 // Check if frames have already been rendered
706
707 for (int frameNo = 0; frameNo < keyframes->GetTotalNumberOfFrames(); frameNo++)
708 {
709 int index = keyframes->GetKeyframeIndex(frameNo);
710 int subIndex = keyframes->GetSubIndex(frameNo);
711
712 const QString filename = GetKeyframeFilename(index, subIndex, false);
713
714 alreadyRenderedFrames[frameNo] = (QFile(filename).exists() || frameNo < frameRanges.startFrame
715 || frameNo >= frameRanges.endFrame);
716 if (gNetRender->IsClient())
717 {
718 if (netRenderListOfFramesToRender.size() > 0)
719 {
720 if (frameNo < netRenderListOfFramesToRender[0]) alreadyRenderedFrames[frameNo] = true;
721 }
722 }
723 reservedFrames[frameNo] = alreadyRenderedFrames[frameNo];
724 }
725 }
726
AllFramesAlreadyRendered(const sFrameRanges & frameRanges,bool * startRenderKeyframesAgain)727 bool cKeyframeAnimation::AllFramesAlreadyRendered(
728 const sFrameRanges &frameRanges, bool *startRenderKeyframesAgain)
729 {
730 bool result = true;
731
732 if (!gNetRender->IsClient())
733 {
734 if (keyframes->GetNumberOfFrames() - 1 > 0 && frameRanges.unrenderedTotalBeforeRender == 0)
735 {
736 bool deletePreviousRender;
737 const QString questionTitle = QObject::tr("Truncate Image Folder");
738 const QString questionText =
739 QObject::tr(
740 "The animation has already been rendered completely.\n Do you want to purge the output "
741 "folder?\n")
742 + QObject::tr("This will delete all images in the image folder.\nProceed?");
743 if (!systemData.noGui)
744 {
745 QMessageBox::StandardButton reply = QMessageBox::NoButton;
746 emit QuestionMessage(
747 questionTitle, questionText, QMessageBox::Yes | QMessageBox::No, &reply);
748 while (reply == QMessageBox::NoButton)
749 {
750 gApplication->processEvents();
751 }
752 deletePreviousRender = (reply == QMessageBox::Yes);
753 }
754 else
755 {
756 // Exit if silent mode
757 if (systemData.silent)
758 {
759 exit(0);
760 }
761 deletePreviousRender = cHeadless::ConfirmMessage(questionTitle + "\n" + questionText);
762 }
763 if (deletePreviousRender)
764 {
765 cAnimationFrames::WipeFramesFromFolder(params->Get<QString>("anim_keyframe_dir"));
766 if (!systemData.noGui && image->IsMainImage())
767 {
768 mainInterface->mainWindow->GetWidgetDockNavigation()->UnlockAllFunctions();
769 imageWidget->SetEnableClickModes(true);
770 }
771 *startRenderKeyframesAgain = true;
772 }
773 else
774 {
775 result = false;
776 }
777 }
778 }
779
780 return result;
781 }
782
InitJobsForClients(const sFrameRanges & frameRanges)783 void cKeyframeAnimation::InitJobsForClients(const sFrameRanges &frameRanges)
784 {
785 int numberOfFramesForNetRender;
786 if (gNetRender->GetClientCount() == 0)
787 {
788 numberOfFramesForNetRender = 0;
789 }
790 else
791 {
792 numberOfFramesForNetRender =
793 frameRanges.unrenderedTotalBeforeRender / gNetRender->GetClientCount() / 2 + 1;
794 if (numberOfFramesForNetRender < minFramesForNetRender)
795 numberOfFramesForNetRender = minFramesForNetRender;
796 }
797 if (numberOfFramesForNetRender > maxFramesForNetRender)
798 numberOfFramesForNetRender = maxFramesForNetRender;
799
800 qint32 renderId = rand();
801 gNetRender->SetCurrentRenderId(renderId);
802 gNetRender->SetAnimation(true);
803 int frameIndex = 0;
804 for (int i = 0; i < gNetRender->GetClientCount(); i++)
805 {
806 QList<int> startingFrames;
807 for (int j = 0; j < numberOfFramesForNetRender; j++)
808 {
809 // looking for next unrendered frame
810 bool notFound = false;
811 while (alreadyRenderedFrames[frameIndex])
812 {
813 frameIndex++;
814 if (frameIndex >= frameRanges.totalFrames)
815 {
816 notFound = true;
817 break;
818 }
819 }
820 if (notFound)
821 {
822 break;
823 }
824 else
825 {
826 startingFrames.append(frameIndex);
827 reservedFrames[frameIndex] = true;
828 frameIndex++;
829 }
830 }
831 if (startingFrames.size() > 0)
832 {
833 emit SendNetRenderSetup(i, startingFrames);
834 }
835 }
836 emit NetRenderCurrentAnimation(params, fractalParams, false);
837 }
838
UpdateCameraAndTarget()839 void cKeyframeAnimation::UpdateCameraAndTarget()
840 {
841 // recalculation of camera rotation and distance (just for display purposes)
842 const CVector3 camera = params->Get<CVector3>("camera");
843 const CVector3 target = params->Get<CVector3>("target");
844 const CVector3 top = params->Get<CVector3>("camera_top");
845 cCameraTarget cameraTarget(camera, target, top);
846 params->Set("camera_rotation", cameraTarget.GetRotation() * 180.0 / M_PI);
847 params->Set("camera_distance_to_target", cameraTarget.GetDistance());
848
849 if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
850 {
851 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::write);
852
853 // show distance in statistics table
854 const double distance =
855 mainInterface->GetDistanceForPoint(params->Get<CVector3>("camera"), params, fractalParams);
856 mainInterface->mainWindow->GetWidgetDockStatistics()->UpdateDistanceToFractal(distance);
857 }
858 }
859
ConfirmAndSendRenderedFrames(const int frameIndex,const QStringList & listOfSavedFiles)860 void cKeyframeAnimation::ConfirmAndSendRenderedFrames(
861 const int frameIndex, const QStringList &listOfSavedFiles)
862 {
863 emit NetRenderConfirmRendered(frameIndex, netRenderListOfFramesToRender.size());
864 netRenderListOfFramesToRender.removeAll(frameIndex);
865 emit NetRenderNotifyClientStatus();
866 for (QString channelFileName : listOfSavedFiles)
867 {
868 emit NetRenderAddFileToSender(channelFileName);
869 }
870 }
871
UpadeProgressInformation(const sFrameRanges & frameRanges,cProgressText * progressText,const int frameIndex,int index)872 void cKeyframeAnimation::UpadeProgressInformation(
873 const sFrameRanges &frameRanges, cProgressText *progressText, const int frameIndex, int index)
874 {
875 double percentDoneFrame;
876 if (frameRanges.unrenderedTotalBeforeRender > 0)
877 percentDoneFrame = double(renderedFramesCount) / frameRanges.unrenderedTotalBeforeRender;
878 else
879 percentDoneFrame = 1.0;
880
881 const QString progressTxt = progressText->getText(percentDoneFrame);
882 emit updateProgressAndStatus(QObject::tr("Rendering animation"),
883 QObject::tr("Frame %1 of %2 (key %3)").arg(frameIndex).arg(frameRanges.totalFrames).arg(index)
884 + " " + progressTxt,
885 percentDoneFrame, cProgressText::progress_ANIMATION);
886 }
887
RenderKeyframes(bool * stopRequest)888 bool cKeyframeAnimation::RenderKeyframes(bool *stopRequest)
889 {
890 mainInterface->DisablePeriodicRefresh();
891
892 if (image->IsUsed())
893 {
894 emit showErrorMessage(
895 QObject::tr(
896 "Rendering engine is busy. Stop unfinished rendering before starting rendering animation"),
897 cErrorMessage::errorMessage);
898 return false;
899 }
900
901 *stopRequest = false;
902 animationStopRequest = false;
903
904 // preparing Render Job
905 std::shared_ptr<cRenderJob> renderJob = PrepareRenderJob(stopRequest);
906
907 cRenderingConfiguration config;
908
909 if (systemData.noGui)
910 {
911 config.DisableRefresh();
912 }
913 config.EnableNetRender();
914 config.DisableProgressiveRender();
915
916 renderJob->Init(cRenderJob::keyframeAnim, config);
917
918 cProgressText progressText;
919 progressText.ResetTimer();
920
921 keyframes->UpdateFramesIndexesTable();
922
923 // range of keyframes to render
924 sFrameRanges frameRanges;
925 if (!InitFrameRanges(&frameRanges)) return false;
926
927 if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
928 {
929 mainInterface->mainWindow->GetWidgetDockNavigation()->LockAllFunctions();
930 imageWidget->SetEnableClickModes(false);
931 }
932
933 InitFrameMarkers(frameRanges);
934
935 try
936 {
937 // updating parameters
938 if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
939 {
940 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
941 gUndo->Store(params, fractalParams, nullptr, keyframes);
942 }
943
944 keyframes->RefreshAllAudioTracks(params);
945
946 // checking for collisions
947 VerifyAnimation(stopRequest);
948
949 // Check if frames have already been rendered
950 CheckWhichFramesAreAlreadyRendered(frameRanges);
951
952 // count number of unrendered frames
953 frameRanges.unrenderedTotalBeforeRender = 0;
954 for (int i = 0; i < frameRanges.totalFrames; i++)
955 {
956 if (!alreadyRenderedFrames[i]) frameRanges.unrenderedTotalBeforeRender++;
957 }
958
959 // message if all frames are already rendered
960 bool startRenderKeyframesAgain = false;
961 bool result = AllFramesAlreadyRendered(frameRanges, &startRenderKeyframesAgain);
962
963 if (startRenderKeyframesAgain) return RenderKeyframes(stopRequest);
964 if (!result) throw false;
965
966 animationIsRendered = true;
967
968 if (gNetRender->IsServer())
969 {
970 InitJobsForClients(frameRanges);
971 }
972
973 keyframes->ClearMorphCache();
974
975 // main loop for rendering of frames
976 renderedFramesCount = 0;
977 for (int frameIndex = 0; frameIndex < keyframes->GetTotalNumberOfFrames(); frameIndex++)
978 {
979 int index = keyframes->GetKeyframeIndex(frameIndex);
980 int subIndex = keyframes->GetSubIndex(frameIndex);
981
982 // skip already rendered frame
983 if (alreadyRenderedFrames[frameIndex]) continue;
984 if (reservedFrames[frameIndex]) continue;
985
986 if (gNetRender->IsClient())
987 {
988 // //waiting for new jobs when list is empty
989 // while (gNetRender->IsClient() && !animationStopRequest &&
990 // netRenderListOfFramesToRender.size() == 0)
991 // {
992 // gApplication->processEvents();
993 // }
994
995 bool frameFound = false;
996 for (int f = 0; f < netRenderListOfFramesToRender.size(); f++)
997 {
998 if (netRenderListOfFramesToRender[f] == frameIndex)
999 {
1000 frameFound = true;
1001 break;
1002 }
1003 }
1004 if (!frameFound) continue;
1005 }
1006
1007 if (gNetRender->IsServer())
1008 {
1009 reservedFrames[frameIndex] = true;
1010 }
1011
1012 UpadeProgressInformation(frameRanges, &progressText, frameIndex, index);
1013
1014 if (*stopRequest || systemData.globalStopRequest || animationStopRequest) throw false;
1015
1016 keyframes->GetInterpolatedFrameAndConsolidate(frameIndex, params, fractalParams);
1017
1018 // recalculation of camera rotation and distance (just for display purposes)
1019 UpdateCameraAndTarget();
1020
1021 // render frame
1022 params->Set("frame_no", frameIndex);
1023 renderJob->UpdateParameters(params, fractalParams);
1024 result = renderJob->Execute();
1025 if (!result) throw false;
1026
1027 // save frame
1028 QStringList listOfSavedFiles;
1029 const QString filename = GetKeyframeFilename(index, subIndex, gNetRender->IsClient());
1030
1031 if (gNetRender->IsClient())
1032 {
1033 const ImageFileSave::enumImageFileType fileType =
1034 ImageFileSave::enumImageFileType(params->Get<int>("keyframe_animation_image_type"));
1035 listOfSavedFiles = SaveImage(filename, fileType, image, gMainInterface->mainWindow);
1036 }
1037 else
1038 {
1039 std::shared_ptr<cImage> imageCopy(new cImage(*image.get()));
1040
1041 const ImageFileSave::enumImageFileType fileType =
1042 ImageFileSave::enumImageFileType(params->Get<int>("keyframe_animation_image_type"));
1043
1044 cKeyframeSaveImageThread *savingThread =
1045 new cKeyframeSaveImageThread(imageCopy, filename, fileType);
1046
1047 QThread *thread = new QThread();
1048 thread->setObjectName("Image saving thread");
1049
1050 connect(thread, &QThread::started, savingThread, &cKeyframeSaveImageThread::startSaving);
1051
1052 connect(savingThread, &cKeyframeSaveImageThread::savingFinished, savingThread,
1053 &cKeyframeSaveImageThread::deleteLater);
1054 connect(savingThread, &cKeyframeSaveImageThread::savingFinished, thread, &QThread::quit);
1055 connect(thread, &QThread::finished, thread, &QThread::deleteLater);
1056
1057 savingThread->moveToThread(thread);
1058 thread->start();
1059 }
1060
1061 renderedFramesCount++;
1062 alreadyRenderedFrames[frameIndex] = true;
1063
1064 // qDebug() << "Finished rendering frame" << frameIndex;
1065
1066 if (gNetRender->IsClient())
1067 {
1068 ConfirmAndSendRenderedFrames(frameIndex, listOfSavedFiles);
1069 }
1070
1071 gApplication->processEvents();
1072 }
1073 //--------------------------------------------------------------------
1074
1075 emit updateProgressAndStatus(QObject::tr("Animation finished"), progressText.getText(1.0), 1.0,
1076 cProgressText::progress_IMAGE);
1077 emit updateProgressHide();
1078 emit notifyRenderKeyframeRenderStatus(
1079 QObject::tr("Animation finished"), progressText.getText(1.0));
1080 }
1081 catch (bool ex)
1082 {
1083 if (gNetRender->IsServer())
1084 {
1085 emit NetRenderStopAllClients();
1086 }
1087
1088 if (gNetRender->IsClient())
1089 {
1090 gNetRender->SetStatus(netRenderSts_READY);
1091 emit NetRenderNotifyClientStatus();
1092 }
1093
1094 QString resultStatus = QObject::tr("Rendering terminated");
1095 if (ex) resultStatus += " - " + QObject::tr("Error occured, see log output");
1096 emit updateProgressAndStatus(
1097 resultStatus, progressText.getText(1.0), cProgressText::progress_ANIMATION);
1098 emit updateProgressHide();
1099
1100 if (!systemData.noGui && image->IsMainImage())
1101 {
1102 mainInterface->mainWindow->GetWidgetDockNavigation()->UnlockAllFunctions();
1103 imageWidget->SetEnableClickModes(true);
1104 }
1105 animationIsRendered = false;
1106 return false;
1107 }
1108
1109 if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
1110 {
1111 mainInterface->mainWindow->GetWidgetDockNavigation()->UnlockAllFunctions();
1112 imageWidget->SetEnableClickModes(true);
1113 }
1114
1115 if (gNetRender->IsClient())
1116 {
1117 gNetRender->SetStatus(netRenderSts_READY);
1118 emit NetRenderNotifyClientStatus();
1119 }
1120
1121 animationIsRendered = false;
1122 return true;
1123 }
1124
RefreshTable()1125 void cKeyframeAnimation::RefreshTable()
1126 {
1127 keyframes->UpdateFramesIndexesTable();
1128 UpdateLimitsForFrameRange(); // it is needed to do it also here, because limits must be set just
1129 // after loading of settings
1130 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1131
1132 keyframes->UpdateFramesIndexesTable();
1133
1134 keyframes->RefreshAllAudioTracks(params);
1135
1136 int lastSelectedColumn = table->currentColumn();
1137
1138 PrepareTable();
1139 gApplication->processEvents();
1140
1141 const int noOfKeyframes = keyframes->GetNumberOfFrames();
1142
1143 auto tempPar = std::make_shared<cParameterContainer>();
1144 *tempPar = *params;
1145 auto tempFract = std::make_shared<cFractalContainer>();
1146 *tempFract = *fractalParams;
1147
1148 for (int i = 0; i < noOfKeyframes; i++)
1149 {
1150 const int newColumn = AddColumn(keyframes->GetFrame(i));
1151
1152 if (ui->checkBox_show_keyframe_thumbnails->isChecked())
1153 {
1154 cThumbnailWidget *thumbWidget =
1155 new cThumbnailWidget(previewSize.width(), previewSize.height(), 1, table);
1156 thumbWidget->UseOneCPUCore(true);
1157 keyframes->GetFrameAndConsolidate(i, tempPar, tempFract);
1158 tempPar->Set("frame_no", keyframes->GetFrameIndexForKeyframe(i));
1159 thumbWidget->AssignParameters(tempPar, tempFract);
1160 table->setCellWidget(0, newColumn, thumbWidget);
1161 }
1162 if (i % 100 == 0)
1163 {
1164 emit updateProgressAndStatus(QObject::tr("Refreshing animation"),
1165 tr("Refreshing animation frames"), double(i) / noOfKeyframes,
1166 cProgressText::progress_ANIMATION);
1167 gApplication->processEvents();
1168 }
1169
1170 if (systemData.globalStopRequest) break;
1171 }
1172
1173 table->selectColumn(lastSelectedColumn);
1174
1175 UpdateLimitsForFrameRange();
1176
1177 UpdateAnimationPath();
1178
1179 mainInterface->progressBarAnimation->hide();
1180 }
1181
GetParameterName(int rowNumber)1182 QString cKeyframeAnimation::GetParameterName(int rowNumber)
1183 {
1184 const int parameterNumber = rowParameter[rowNumber];
1185
1186 QString fullParameterName;
1187 QList<cAnimationFrames::sParameterDescription> list = keyframes->GetListOfUsedParameters();
1188 if (parameterNumber >= 0)
1189 {
1190 fullParameterName =
1191 list[parameterNumber].containerName + "_" + list[parameterNumber].parameterName;
1192 }
1193 else
1194 {
1195 qCritical() << "cFlightAnimation::GetParameterNumber(int rowNumber): row not found";
1196 }
1197 return fullParameterName;
1198 }
1199
RenderFrame(int index) const1200 void cKeyframeAnimation::RenderFrame(int index) const
1201 {
1202 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1203 keyframes->GetFrameAndConsolidate(index, params, fractalParams);
1204
1205 // recalculation of camera rotation and distance (just for display purposes)
1206 const CVector3 camera = params->Get<CVector3>("camera");
1207 const CVector3 target = params->Get<CVector3>("target");
1208 const CVector3 top = params->Get<CVector3>("camera_top");
1209 cCameraTarget cameraTarget(camera, target, top);
1210 params->Set("camera_rotation", cameraTarget.GetRotation() * 180.0 / M_PI);
1211 params->Set("camera_distance_to_target", cameraTarget.GetDistance());
1212
1213 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::write);
1214
1215 params->Set("frame_no", keyframes->GetFrameIndexForKeyframe(index));
1216
1217 mainInterface->StartRender();
1218 }
1219
DeleteFramesFrom(int index) const1220 void cKeyframeAnimation::DeleteFramesFrom(int index) const
1221 {
1222 gUndo->Store(params, fractalParams, nullptr, keyframes);
1223 for (int i = keyframes->GetNumberOfFrames() - 1; i >= index; i--)
1224 table->removeColumn(index + reservedColumns);
1225 keyframes->DeleteFrames(index, keyframes->GetNumberOfFrames() - 1);
1226
1227 keyframes->UpdateFramesIndexesTable();
1228
1229 UpdateLimitsForFrameRange();
1230 }
1231
DeleteFramesTo(int index) const1232 void cKeyframeAnimation::DeleteFramesTo(int index) const
1233 {
1234 gUndo->Store(params, fractalParams, nullptr, keyframes);
1235 for (int i = 0; i <= index; i++)
1236 table->removeColumn(reservedColumns);
1237 keyframes->DeleteFrames(0, index);
1238
1239 keyframes->UpdateFramesIndexesTable();
1240
1241 UpdateLimitsForFrameRange();
1242 }
1243
slotSelectKeyframeAnimImageDir() const1244 void cKeyframeAnimation::slotSelectKeyframeAnimImageDir() const
1245 {
1246 QFileDialog *dialog = new QFileDialog();
1247 dialog->setFileMode(QFileDialog::DirectoryOnly);
1248 dialog->setNameFilter(QObject::tr("Animation Image Folder"));
1249 dialog->setDirectory(QDir::toNativeSeparators(params->Get<QString>("anim_keyframe_dir")));
1250 dialog->setAcceptMode(QFileDialog::AcceptOpen);
1251 dialog->setWindowTitle(QObject::tr("Choose Animation Image Folder"));
1252 dialog->setOption(QFileDialog::ShowDirsOnly);
1253
1254 if (dialog->exec())
1255 {
1256 QStringList fileNames = dialog->selectedFiles();
1257 const QString filename = QDir::toNativeSeparators(fileNames.first() + QDir::separator());
1258 ui->text_anim_keyframe_dir->setText(filename);
1259 params->Set("anim_keyframe_dir", filename);
1260 }
1261 }
1262
slotTableCellChanged(int row,int column)1263 void cKeyframeAnimation::slotTableCellChanged(int row, int column)
1264 {
1265 table->blockSignals(true);
1266
1267 if (row >= reservedRows)
1268 {
1269 QTableWidgetItem *cell = table->item(row, column);
1270 QString cellText = cell->text();
1271
1272 const int index = column - reservedColumns;
1273 cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(index);
1274
1275 const QString parameterName = GetParameterName(row);
1276 const int parameterFirstRow = parameterRows[rowParameter[row]];
1277 const int vectIndex = row - parameterFirstRow;
1278
1279 using namespace parameterContainer;
1280 const enumVarType type = frame.parameters.GetVarType(parameterName);
1281
1282 if (type == typeVector3)
1283 {
1284 CVector3 vect = frame.parameters.Get<CVector3>(parameterName);
1285 if (vectIndex == 0) vect.x = systemData.locale.toDouble(cellText);
1286 if (vectIndex == 1) vect.y = systemData.locale.toDouble(cellText);
1287 if (vectIndex == 2) vect.z = systemData.locale.toDouble(cellText);
1288 frame.parameters.Set(parameterName, vect);
1289 }
1290 else if (type == typeVector4)
1291 {
1292 CVector4 vect = frame.parameters.Get<CVector4>(parameterName);
1293 if (vectIndex == 0) vect.x = systemData.locale.toDouble(cellText);
1294 if (vectIndex == 1) vect.y = systemData.locale.toDouble(cellText);
1295 if (vectIndex == 2) vect.z = systemData.locale.toDouble(cellText);
1296 if (vectIndex == 3) vect.w = systemData.locale.toDouble(cellText);
1297 frame.parameters.Set(parameterName, vect);
1298 }
1299 else if (type == typeRgb)
1300 {
1301 sRGB col = frame.parameters.Get<sRGB>(parameterName);
1302 if (vectIndex == 0) col.R = cellText.toInt();
1303 if (vectIndex == 1) col.G = cellText.toInt();
1304 if (vectIndex == 2) col.B = cellText.toInt();
1305 frame.parameters.Set(parameterName, col);
1306 }
1307 else
1308 {
1309 frame.parameters.Set(parameterName, cellText);
1310 }
1311
1312 keyframes->ModifyFrame(index, frame);
1313
1314 // update thumbnail
1315 if (ui->checkBox_show_keyframe_thumbnails->isChecked())
1316 {
1317 auto tempPar = std::make_shared<cParameterContainer>();
1318 *tempPar = *params;
1319 auto tempFract = std::make_shared<cFractalContainer>();
1320 *tempFract = *fractalParams;
1321
1322 keyframes->GetFrameAndConsolidate(index, tempPar, tempFract);
1323 cThumbnailWidget *thumbWidget = static_cast<cThumbnailWidget *>(table->cellWidget(0, column));
1324
1325 if (!thumbWidget)
1326 {
1327 thumbWidget = new cThumbnailWidget(previewSize.width(), previewSize.height(), 1, table);
1328 thumbWidget->UseOneCPUCore(true);
1329 thumbWidget->AssignParameters(tempPar, tempFract);
1330 table->setCellWidget(0, column, thumbWidget);
1331 }
1332 else
1333 {
1334 thumbWidget->AssignParameters(tempPar, tempFract);
1335 }
1336 }
1337 UpdateAnimationPath();
1338 }
1339 else
1340 {
1341 if (row == framesPerKeyframeRow)
1342 {
1343 QTableWidgetItem *cell = table->item(row, column);
1344 QString cellText = cell->text();
1345 int newFramesPerKey = cellText.toInt();
1346 if (newFramesPerKey < 1) newFramesPerKey = 1;
1347
1348 const int index = column - reservedColumns;
1349 cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(index);
1350 frame.numberOfSubFrames = newFramesPerKey;
1351 keyframes->ModifyFrame(index, frame);
1352
1353 keyframes->UpdateFramesIndexesTable();
1354 }
1355 }
1356
1357 table->blockSignals(false);
1358
1359 const int index = min(column - reservedColumns, keyframes->GetNumberOfFrames() - 1);
1360 int nextIndex = min(index + 1, keyframes->GetNumberOfFrames() - 1);
1361 double speed = GetCameraSpeed(keyframes->GetFrame(index), keyframes->GetFrame(nextIndex));
1362 QTableWidgetItem *speedCell = table->item(cameraSpeedRow, index + reservedColumns);
1363 speedCell->setText(QString::number(speed));
1364 table->update();
1365 }
1366
slotDeleteAllImages() const1367 void cKeyframeAnimation::slotDeleteAllImages() const
1368 {
1369 SynchronizeInterfaceWindow(
1370 ui->scrollAreaWidgetContents_keyframeAnimationParameters, params, qInterface::read);
1371
1372 const QMessageBox::StandardButton reply = QMessageBox::question(
1373 mainInterface->mainWindow->GetCentralWidget(), QObject::tr("Truncate Image Folder"),
1374 QObject::tr("This will delete all images in the image folder.\nProceed?"),
1375 QMessageBox::Yes | QMessageBox::No);
1376
1377 if (reply == QMessageBox::Yes)
1378 {
1379 cAnimationFrames::WipeFramesFromFolder(params->Get<QString>("anim_keyframe_dir"));
1380 }
1381 }
1382
slotShowAnimation() const1383 void cKeyframeAnimation::slotShowAnimation() const
1384 {
1385 WriteLog("Prepare PlayerWidget class", 2);
1386
1387 SynchronizeInterfaceWindow(
1388 ui->scrollAreaWidgetContents_keyframeAnimationParameters, params, qInterface::read);
1389
1390 if (!mainInterface->imageSequencePlayer)
1391 {
1392 mainInterface->imageSequencePlayer = new PlayerWidget;
1393 }
1394
1395 mainInterface->imageSequencePlayer->SetFilePath(params->Get<QString>("anim_keyframe_dir"));
1396 mainInterface->imageSequencePlayer->show();
1397 mainInterface->imageSequencePlayer->raise();
1398 mainInterface->imageSequencePlayer->activateWindow();
1399 }
1400
InterpolateForward(int row,int column)1401 void cKeyframeAnimation::InterpolateForward(int row, int column)
1402 {
1403 gUndo->Store(params, fractalParams, nullptr, keyframes);
1404
1405 QTableWidgetItem *cell = table->item(row, column);
1406 QString cellText = cell->text();
1407
1408 const QString parameterName = GetParameterName(row);
1409
1410 const int index = column - reservedColumns;
1411 cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(index);
1412
1413 using namespace parameterContainer;
1414 const enumVarType type = frame.parameters.GetVarType(parameterName);
1415
1416 bool valueIsInteger = false;
1417 bool valueIsDouble = false;
1418 bool valueIsText = false;
1419 int valueInteger = 0;
1420 double valueDouble = 0.0;
1421 QString valueText;
1422
1423 bool ok;
1424 const int lastFrame = QInputDialog::getInt(mainInterface->mainWindow, "Parameter interpolation",
1425 "Enter last keyframe number", index, index + 1, keyframes->GetNumberOfFrames() - 1, 1, &ok);
1426 if (!ok) return;
1427
1428 const int numberOfFrames = (lastFrame - index);
1429
1430 switch (type)
1431 {
1432 case typeBool:
1433 case typeInt:
1434 case typeRgb:
1435 {
1436 valueIsInteger = true;
1437 valueInteger = cellText.toInt();
1438 // qDebug() << valueInteger;
1439 break;
1440 }
1441 case typeDouble:
1442 case typeVector3:
1443 case typeVector4:
1444 {
1445 valueIsDouble = true;
1446 valueDouble = systemData.locale.toDouble(cellText);
1447 // qDebug() << valueDouble;
1448 break;
1449 }
1450 default:
1451 {
1452 valueIsText = true;
1453 valueText = cellText;
1454 break;
1455 }
1456 }
1457
1458 double integerStep = 0.0;
1459 double doubleStep = 0.0;
1460
1461 if (valueIsInteger)
1462 {
1463 const int finalInteger =
1464 QInputDialog::getInt(mainInterface->mainWindow, "Parameter interpolation",
1465 "Enter value for last keyframe", valueInteger, 0, 2147483647, 1, &ok);
1466 integerStep = double(finalInteger - valueInteger) / numberOfFrames;
1467 }
1468 else if (valueIsDouble)
1469 {
1470 const double finalDouble = systemData.locale.toDouble(QInputDialog::getText(
1471 mainInterface->mainWindow, "Parameter interpolation", "Enter value for last keyframe",
1472 QLineEdit::Normal, QString("%L1").arg(valueDouble, 0, 'g', 15), &ok));
1473 doubleStep = (finalDouble - valueDouble) / numberOfFrames;
1474 }
1475
1476 if (!ok) return;
1477
1478 for (int i = index + 1; i <= lastFrame; i++)
1479 {
1480 QString newCellText;
1481 if (valueIsInteger)
1482 {
1483 const int newValue = int(integerStep * (i - index) + valueInteger);
1484 newCellText = QString::number(newValue);
1485 }
1486 else if (valueIsDouble)
1487 {
1488 const double newValue = doubleStep * (i - index) + valueDouble;
1489 newCellText = QString("%L1").arg(newValue, 0, 'g', 15);
1490 }
1491 else if (valueIsText)
1492 {
1493 newCellText = valueText;
1494 }
1495 QTableWidgetItem *newCell = table->item(row, i + reservedColumns);
1496 newCell->setText(newCellText);
1497 newCell->setForeground(QBrush(Qt::black));
1498 }
1499 }
1500
CopyToAllKeyframes(int row,int column)1501 void cKeyframeAnimation::CopyToAllKeyframes(int row, int column)
1502 {
1503 QTableWidgetItem *cell = table->item(row, column);
1504 QString cellText = cell->text();
1505
1506 for (int i = reservedColumns; i < table->columnCount(); i++)
1507 {
1508 table->item(row, i)->setText(cellText);
1509 }
1510 }
1511
slotRefreshTable()1512 void cKeyframeAnimation::slotRefreshTable()
1513 {
1514 RefreshTable();
1515 }
1516
GetKeyframeFilename(int index,int subIndex,bool netRenderCache) const1517 QString cKeyframeAnimation::GetKeyframeFilename(int index, int subIndex, bool netRenderCache) const
1518 {
1519 const int frameIndex = keyframes->GetFrameIndexForKeyframe(index) + subIndex;
1520
1521 QString dir;
1522 if (netRenderCache)
1523 {
1524 dir = systemDirectories.GetNetrenderFolder() + QDir::separator()
1525 + QString("pid%1_").arg(QCoreApplication::applicationPid());
1526 }
1527 else
1528 {
1529 dir = params->Get<QString>("anim_keyframe_dir");
1530 }
1531
1532 QString filename = dir + "frame_" + QString("%1").arg(frameIndex, 7, 10, QChar('0'));
1533 filename += "."
1534 + ImageFileSave::ImageFileExtension(ImageFileSave::enumImageFileType(
1535 params->Get<int>("keyframe_animation_image_type")));
1536 return filename;
1537 }
1538
GetMorphType(int row) const1539 parameterContainer::enumMorphType cKeyframeAnimation::GetMorphType(int row) const
1540 {
1541 const int parameterIndex = rowParameter.at(row);
1542 const parameterContainer::enumMorphType morphType =
1543 keyframes->GetListOfParameters().at(parameterIndex).morphType;
1544 return morphType;
1545 }
1546
ChangeMorphType(int row,parameterContainer::enumMorphType morphType)1547 void cKeyframeAnimation::ChangeMorphType(int row, parameterContainer::enumMorphType morphType)
1548 {
1549 gUndo->Store(params, fractalParams, nullptr, keyframes);
1550 const int parameterIndex = rowParameter.at(row);
1551 keyframes->ChangeMorphType(parameterIndex, morphType);
1552 RefreshTable();
1553 }
1554
slotExportKeyframesToFlight()1555 void cKeyframeAnimation::slotExportKeyframesToFlight()
1556 {
1557 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1558 gUndo->Store(params, fractalParams, gAnimFrames, keyframes);
1559
1560 keyframes->UpdateFramesIndexesTable();
1561
1562 if (gAnimFrames->GetFrames().size() > 0)
1563 {
1564 const QMessageBox::StandardButton reply = QMessageBox::question(
1565 mainInterface->mainWindow->GetCentralWidget(), QObject::tr("Export keyframes to flight"),
1566 QObject::tr(
1567 "There are already captured flight frames present.\nDiscard current flight frames ?"),
1568 QMessageBox::Yes | QMessageBox::No);
1569
1570 if (reply == QMessageBox::No) return;
1571 }
1572
1573 gAnimFrames->ClearAll();
1574 gAnimFrames->SetListOfParametersAndClear(keyframes->GetListOfParameters(), params);
1575
1576 keyframes->ClearMorphCache();
1577
1578 keyframes->RefreshAllAudioTracks(params);
1579
1580 for (int frameIndex = 0; frameIndex < keyframes->GetTotalNumberOfFrames(); frameIndex++)
1581 {
1582 int index = keyframes->GetKeyframeIndex(frameIndex);
1583 gAnimFrames->AddFrame(keyframes->GetInterpolatedFrame(frameIndex, params, fractalParams));
1584
1585 if (frameIndex % 100 == 0)
1586 {
1587 emit updateProgressAndStatus(QObject::tr("Exporting"), tr("Exporting keyframes to flight"),
1588 double(index) / keyframes->GetNumberOfFrames(), cProgressText::progress_ANIMATION);
1589 gApplication->processEvents();
1590 }
1591 }
1592 mainInterface->progressBarAnimation->hide();
1593 ui->tabWidgetFlightKeyframe->setCurrentIndex(0);
1594 ui->pushButton_flight_refresh_table->animateClick();
1595 }
1596
UpdateLimitsForFrameRange() const1597 void cKeyframeAnimation::UpdateLimitsForFrameRange() const
1598 {
1599 int noOfFrames = (keyframes->GetTotalNumberOfFrames() - 1);
1600 if (noOfFrames < 0) noOfFrames = 0;
1601
1602 ui->spinboxInt_keyframe_first_to_render->setMaximum(noOfFrames);
1603
1604 ui->spinboxInt_keyframe_last_to_render->setMaximum(noOfFrames);
1605
1606 if (lastToRenderMax)
1607 {
1608 ui->spinboxInt_keyframe_last_to_render->setValue(noOfFrames);
1609 }
1610 }
1611
slotMovedSliderFirstFrame() const1612 void cKeyframeAnimation::slotMovedSliderFirstFrame() const
1613 {
1614 int value = ui->spinboxInt_keyframe_first_to_render->value();
1615 if (value > ui->spinboxInt_keyframe_last_to_render->value())
1616 ui->spinboxInt_keyframe_last_to_render->setValue(value);
1617 }
slotMovedSliderLastFrame()1618 void cKeyframeAnimation::slotMovedSliderLastFrame()
1619 {
1620 int value = ui->spinboxInt_keyframe_last_to_render->value();
1621 lastToRenderMax = (value == ui->spinboxInt_keyframe_last_to_render->maximum());
1622 if (value < ui->spinboxInt_keyframe_first_to_render->value())
1623 ui->spinboxInt_keyframe_first_to_render->setValue(value);
1624 }
1625
CheckForCollisions(double minDist,bool * stopRequest)1626 QList<int> cKeyframeAnimation::CheckForCollisions(double minDist, bool *stopRequest)
1627 {
1628 QList<int> listOfCollisions;
1629
1630 auto tempPar = std::make_shared<cParameterContainer>();
1631 *tempPar = *params;
1632 auto tempFractPar = std::make_shared<cFractalContainer>();
1633 *tempFractPar = *fractalParams;
1634
1635 *stopRequest = false;
1636
1637 keyframes->UpdateFramesIndexesTable();
1638
1639 keyframes->ClearMorphCache();
1640
1641 for (int frameIndex = 0; frameIndex < keyframes->GetTotalNumberOfFrames(); frameIndex++)
1642 {
1643 int index = keyframes->GetKeyframeIndex(frameIndex);
1644
1645 gApplication->processEvents();
1646 if (*stopRequest || systemData.globalStopRequest) return listOfCollisions;
1647
1648 keyframes->GetInterpolatedFrameAndConsolidate(frameIndex, tempPar, tempFractPar);
1649 tempPar->Set("frame_no", frameIndex);
1650 const CVector3 point = tempPar->Get<CVector3>("camera");
1651 const double dist = mainInterface->GetDistanceForPoint(point, tempPar, tempFractPar);
1652 if (dist < minDist)
1653 {
1654 listOfCollisions.append(frameIndex);
1655 }
1656
1657 if (frameIndex % 100 == 0)
1658 {
1659 emit updateProgressAndStatus(QObject::tr("Checking for collisions"),
1660 QObject::tr("Checking for collisions on keyframe # %1").arg(index),
1661 double(frameIndex) / keyframes->GetTotalNumberOfFrames(),
1662 cProgressText::progress_ANIMATION);
1663 }
1664 }
1665
1666 emit updateProgressAndStatus(QObject::tr("Checking for collisions"),
1667 QObject::tr("Checking for collisions finished"), 1.0, cProgressText::progress_ANIMATION);
1668
1669 return listOfCollisions;
1670 }
1671
slotValidate()1672 void cKeyframeAnimation::slotValidate()
1673 {
1674 // updating parameters
1675 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1676 gUndo->Store(params, fractalParams, nullptr, keyframes);
1677
1678 // checking for collisions
1679 QList<int> listOfCollisions = CheckForCollisions(
1680 params->Get<double>("keyframe_collision_thresh"), &mainInterface->stopRequest);
1681 if (listOfCollisions.size() > 0)
1682 {
1683 QString collisionText;
1684 for (int i = 0; i < listOfCollisions.size(); i++)
1685 {
1686 collisionText += QString("%1").arg(listOfCollisions.at(i));
1687 if (i < listOfCollisions.size() - 1) collisionText += QString(", ");
1688 }
1689 emit showErrorMessage(
1690 QObject::tr("Camera collides with fractal at following frames:\n") + collisionText,
1691 cErrorMessage::warningMessage);
1692 }
1693 else
1694 {
1695 emit showErrorMessage(QObject::tr("No collisions detected\n"), cErrorMessage::infoMessage);
1696 }
1697 }
1698
slotCellDoubleClicked(int row,int column) const1699 void cKeyframeAnimation::slotCellDoubleClicked(int row, int column) const
1700 {
1701 if (row == 0)
1702 {
1703 RenderFrame(column - reservedColumns);
1704 }
1705 }
1706
slotCellClicked(int row,int column) const1707 void cKeyframeAnimation::slotCellClicked(int row, int column) const
1708 {
1709 Q_UNUSED(row);
1710 Q_UNUSED(column);
1711 UpdateCameraDistanceInformation();
1712 }
1713
slotSetConstantTargetDistance()1714 void cKeyframeAnimation::slotSetConstantTargetDistance()
1715 {
1716 // updating parameters
1717 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1718 gUndo->Store(params, fractalParams, nullptr, keyframes);
1719
1720 const double constDist = params->Get<double>("keyframe_constant_target_distance");
1721
1722 for (int key = 0; key < keyframes->GetNumberOfFrames() - 1; key++)
1723 {
1724 cAnimationFrames::sAnimationFrame keyframe = keyframes->GetFrame(key);
1725
1726 if (keyframe.parameters.IfExists("main_camera") && keyframe.parameters.IfExists("main_target")
1727 && keyframe.parameters.IfExists("main_camera_top"))
1728 {
1729 const CVector3 camera = keyframe.parameters.Get<CVector3>("main_camera");
1730 const CVector3 target = keyframe.parameters.Get<CVector3>("main_target");
1731 const CVector3 top = keyframe.parameters.Get<CVector3>("main_camera_top");
1732
1733 cCameraTarget cameraTarget(camera, target, top);
1734 const CVector3 forwardVector = cameraTarget.GetForwardVector();
1735
1736 const CVector3 newTarget = camera + forwardVector * constDist;
1737
1738 keyframe.parameters.Set("main_target", newTarget);
1739 if (keyframe.parameters.IfExists("camera_distance_to_target"))
1740 {
1741 keyframe.parameters.Set("camera_distance_to_target", constDist);
1742 }
1743
1744 keyframes->ModifyFrame(key, keyframe);
1745 }
1746 else
1747 {
1748 cErrorMessage::showMessage(
1749 QObject::tr("Cannot change target distance. Missing camera parameters in keyframes"),
1750 cErrorMessage::errorMessage, mainInterface->mainWindow->GetCentralWidget());
1751 return;
1752 }
1753 }
1754 RefreshTable();
1755 }
1756
AddAnimSoundColumn() const1757 void cKeyframeAnimation::AddAnimSoundColumn() const
1758 {
1759 const int newColumn = table->columnCount();
1760 table->insertColumn(newColumn);
1761 table->setHorizontalHeaderItem(newColumn, new QTableWidgetItem(tr("Audio")));
1762 }
1763
UpdateAnimationPath() const1764 void cKeyframeAnimation::UpdateAnimationPath() const
1765 {
1766 int numberOfKeyframes = keyframes->GetNumberOfFrames();
1767
1768 auto tempPar = std::make_shared<cParameterContainer>();
1769 *tempPar = *params;
1770 auto tempFractPar = std::make_shared<cFractalContainer>();
1771 *tempFractPar = *fractalParams;
1772
1773 sAnimationPathData animationPathData;
1774 animationPathData.numberOfKeyframes = numberOfKeyframes;
1775 animationPathData.numberOfFrames = keyframes->GetTotalNumberOfFrames();
1776 animationPathData.actualSelectedFrameNo = table->currentColumn() - reservedColumns;
1777 if (animationPathData.actualSelectedFrameNo < 0) animationPathData.actualSelectedFrameNo = 0;
1778 animationPathData.cameraPathEnable = params->Get<bool>("show_camera_path");
1779 animationPathData.targetPathEnable = params->Get<bool>("show_target_path");
1780
1781 for (int i = 1; i <= 4; i++)
1782 {
1783 if (params->IfExists(cLight::Name("is_defined", i)))
1784 {
1785 animationPathData.lightPathEnable[i - 1] =
1786 params->Get<bool>("show_light_path", i) && params->Get<bool>(cLight::Name("enabled", i));
1787 }
1788 else
1789 {
1790 animationPathData.lightPathEnable[i - 1] = false;
1791 }
1792 }
1793
1794 for (int key = 0; key < keyframes->GetNumberOfFrames(); key++)
1795 {
1796 animationPathData.framesPeyKey.append(keyframes->GetFrame(key).numberOfSubFrames);
1797 }
1798
1799 keyframes->ClearMorphCache();
1800
1801 for (int frameIndex = 0; frameIndex < keyframes->GetTotalNumberOfFrames(); frameIndex++)
1802 {
1803 keyframes->GetInterpolatedFrameAndConsolidate(frameIndex, tempPar, tempFractPar);
1804 sAnimationPathPoint point;
1805 point.camera = tempPar->Get<CVector3>("camera");
1806 point.target = tempPar->Get<CVector3>("target");
1807 CVector3 top = tempPar->Get<CVector3>("camera_top");
1808 cCameraTarget cameraTarget(point.camera, point.target, top);
1809
1810 for (int l = 0; l < 4; l++)
1811 {
1812 if (params->IfExists(cLight::Name("is_defined", l + 1)))
1813 {
1814 if (params->Get<bool>(cLight::Name("relative_position", l + 1)))
1815 {
1816 CVector3 deltaPosition = tempPar->Get<CVector3>(cLight::Name("position", l + 1));
1817 CVector3 deltaPositionRotated = cameraTarget.GetForwardVector() * deltaPosition.z
1818 + cameraTarget.GetTopVector() * deltaPosition.y
1819 + cameraTarget.GetRightVector() * deltaPosition.x;
1820 point.lights[l] = point.camera + deltaPositionRotated;
1821 }
1822 else
1823 {
1824 point.lights[l] = tempPar->Get<CVector3>(cLight::Name("position", l + 1));
1825 }
1826 sRGB color16 = tempPar->Get<sRGB>(cLight::Name("color", l + 1));
1827 sRGB8 color8(quint8(color16.R / 256), quint8(color16.G / 256), quint8(color16.B / 256));
1828 point.lightColor[l] = color8;
1829 }
1830 }
1831 animationPathData.animationPath.append(point);
1832 }
1833 imageWidget->SetAnimationPath(animationPathData);
1834 imageWidget->update();
1835 }
1836
slotUpdateAnimationPathSelection()1837 void cKeyframeAnimation::slotUpdateAnimationPathSelection()
1838 {
1839 SynchronizeInterfaceWindow(ui->tab_keyframe_animation, params, qInterface::read);
1840 UpdateAnimationPath();
1841 }
1842
UpdateActualCameraPosition(const CVector3 & cameraPosition)1843 void cKeyframeAnimation::UpdateActualCameraPosition(const CVector3 &cameraPosition)
1844 {
1845 actualCameraPosition = cameraPosition;
1846 UpdateCameraDistanceInformation();
1847 }
1848
UpdateCameraDistanceInformation() const1849 void cKeyframeAnimation::UpdateCameraDistanceInformation() const
1850 {
1851 if (keyframes->GetNumberOfFrames() > 0)
1852 {
1853 int selectedKeyframe = table->currentColumn() - reservedColumns;
1854 if (selectedKeyframe < 0) selectedKeyframe = 0;
1855
1856 cKeyframes::sAnimationFrame keyframe = keyframes->GetFrame(selectedKeyframe);
1857
1858 if (keyframe.parameters.IfExists("main_camera"))
1859 {
1860 CVector3 camera = keyframe.parameters.Get<CVector3>("main_camera");
1861 double distance = (camera - actualCameraPosition).Length();
1862 ui->label_camera_distance_from_keyframe->setText(
1863 tr("Camera distance from selected keyframe: %1").arg(distance));
1864 }
1865 }
1866 }
1867
SetNetRenderStartingFrames(const QVector<int> & startingFrames)1868 void cKeyframeAnimation::SetNetRenderStartingFrames(const QVector<int> &startingFrames)
1869 {
1870 netRenderListOfFramesToRender.clear();
1871 netRenderListOfFramesToRender.append(startingFrames.toList());
1872 }
1873
slotNetRenderFinishedFrame(int clientIndex,int frameIndex,int sizeOfToDoList)1874 void cKeyframeAnimation::slotNetRenderFinishedFrame(
1875 int clientIndex, int frameIndex, int sizeOfToDoList)
1876 {
1877 Q_UNUSED(frameIndex);
1878
1879 renderedFramesCount++;
1880 if (frameIndex < alreadyRenderedFrames.size())
1881 {
1882 alreadyRenderedFrames[frameIndex] = true;
1883 }
1884
1885 if (!animationStopRequest && animationIsRendered)
1886 {
1887 // qDebug() << "Server: got information about finished frame" << frameIndex << sizeOfToDoList;
1888
1889 // counting left frames
1890 int countLeft = reservedFrames.count(false);
1891
1892 // calculate maximum list size
1893 int numberOfFramesForNetRender = countLeft / gNetRender->GetClientCount() / 2;
1894 if (numberOfFramesForNetRender < minFramesForNetRender)
1895 numberOfFramesForNetRender = minFramesForNetRender;
1896 if (numberOfFramesForNetRender > maxFramesForNetRender)
1897 numberOfFramesForNetRender = maxFramesForNetRender;
1898
1899 // calculate number for frames to supplement
1900 int numberOfNewFrames = numberOfFramesForNetRender - sizeOfToDoList;
1901
1902 if (numberOfNewFrames > 0)
1903 {
1904 QList<int> toDoList;
1905
1906 // looking for not reserved frame
1907 for (int f = 0; f < reservedFrames.size(); f++)
1908 {
1909 if (!reservedFrames[f])
1910 {
1911 toDoList.append(f);
1912 reservedFrames[f] = true;
1913 }
1914 if (toDoList.size() >= numberOfNewFrames) break;
1915 }
1916 if (toDoList.size() > 0)
1917 {
1918 emit NetRenderSendFramesToDoList(clientIndex, toDoList);
1919 }
1920 // qDebug() << "Server: new toDo list" << toDoList;
1921 }
1922 }
1923 }
1924
slotNetRenderUpdateFramesToDo(QList<int> listOfFrames)1925 void cKeyframeAnimation::slotNetRenderUpdateFramesToDo(QList<int> listOfFrames)
1926 {
1927 if (animationIsRendered)
1928 {
1929 netRenderListOfFramesToRender.append(listOfFrames);
1930 // qDebug() << "Client: got frames toDo:" << listOfFrames;
1931 }
1932 }
1933
slotRandomize()1934 void cKeyframeAnimation::slotRandomize()
1935 {
1936 cRandomizerDialog *randomizer = new cRandomizerDialog(); // deleted by WA_DeleteOnClose
1937 randomizer->setAttribute(Qt::WA_DeleteOnClose);
1938 QStringList listOfParameterNames;
1939 QList<cKeyframes::sParameterDescription> list = keyframes->GetListOfParameters();
1940 for (const cKeyframes::sParameterDescription ¶meter : list)
1941 {
1942 QString fullParameterName = parameter.containerName + "_" + parameter.parameterName;
1943 listOfParameterNames.append(fullParameterName);
1944 }
1945
1946 randomizer->AssignParameters(listOfParameterNames);
1947 randomizer->show();
1948 }
1949
slotAnimationStopRequest()1950 void cKeyframeAnimation::slotAnimationStopRequest()
1951 {
1952 animationStopRequest = true;
1953 }
1954
InsertKeyframeInBetween(int index)1955 void cKeyframeAnimation::InsertKeyframeInBetween(int index)
1956 {
1957 if (keyframes)
1958 {
1959 auto tempPar = std::make_shared<cParameterContainer>();
1960 *tempPar = *params;
1961 auto tempFractPar = std::make_shared<cFractalContainer>();
1962 *tempFractPar = *fractalParams;
1963
1964 keyframes->UpdateFramesIndexesTable();
1965 keyframes->ClearMorphCache();
1966
1967 int frameIndex = keyframes->GetFrameIndexForKeyframe(index - 1)
1968 + keyframes->GetFramesPerKeyframe(index - 1) / 2;
1969
1970 keyframes->GetInterpolatedFrameAndConsolidate(frameIndex, tempPar, tempFractPar);
1971
1972 // add new frame to container
1973 keyframes->AddFrame(
1974 tempPar, tempFractPar, keyframes->GetFramesPerKeyframe(index - 1) / 2, index);
1975 keyframes->UpdateFramesIndexesTable();
1976
1977 params->Set("frame_no", keyframes->GetFrameIndexForKeyframe(index));
1978
1979 // add column to table
1980 const int newColumn = AddColumn(keyframes->GetFrame(index), index);
1981 table->selectColumn(newColumn);
1982
1983 if (ui->checkBox_show_keyframe_thumbnails->isChecked())
1984 {
1985 cThumbnailWidget *thumbWidget =
1986 new cThumbnailWidget(previewSize.width(), previewSize.height(), 1, table);
1987 thumbWidget->UseOneCPUCore(false);
1988 thumbWidget->AssignParameters(tempPar, tempFractPar);
1989 table->setCellWidget(0, newColumn, thumbWidget);
1990 }
1991
1992 // change frames per keyframe in previous keyframe
1993 cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(index - 1);
1994 frame.numberOfSubFrames = frame.numberOfSubFrames / 2;
1995 keyframes->ModifyFrame(index - 1, frame);
1996
1997 keyframes->UpdateFramesIndexesTable();
1998
1999 UpdateLimitsForFrameRange();
2000
2001 RefreshTable();
2002
2003 UpdateAnimationPath();
2004 }
2005 else
2006 {
2007 qCritical() << "gAnimFrames not allocated";
2008 }
2009 }
2010
DeleteRenderedFramesForKeyframe(int keyframeIndex)2011 void cKeyframeAnimation::DeleteRenderedFramesForKeyframe(int keyframeIndex)
2012 {
2013 if (keyframeIndex < keyframes->GetNumberOfFrames() - 1)
2014 {
2015 int startFrame = keyframes->GetFrameIndexForKeyframe(keyframeIndex);
2016 int endFrame = keyframes->GetFrameIndexForKeyframe(keyframeIndex + 1) - 1;
2017
2018 SynchronizeInterfaceWindow(
2019 ui->scrollAreaWidgetContents_keyframeAnimationParameters, params, qInterface::read);
2020
2021 QString folder = params->Get<QString>("anim_keyframe_dir");
2022
2023 const QMessageBox::StandardButton reply = QMessageBox::question(
2024 mainInterface->mainWindow->GetCentralWidget(), QObject::tr("Deleting rendered frames"),
2025 QObject::tr("This will delete rendered frames from %1 to %2\n"
2026 "in the image folder.\n"
2027 "%3\n"
2028 "Proceed?")
2029 .arg(startFrame)
2030 .arg(endFrame)
2031 .arg(folder),
2032 QMessageBox::Yes | QMessageBox::No);
2033
2034 if (reply == QMessageBox::Yes)
2035 {
2036 cAnimationFrames::WipeFramesFromFolder(folder, startFrame, endFrame);
2037 }
2038 }
2039 }
2040
GetCameraSpeed(const cAnimationFrames::sAnimationFrame & frame,const cAnimationFrames::sAnimationFrame & nextFrame)2041 double cKeyframeAnimation::GetCameraSpeed(const cAnimationFrames::sAnimationFrame &frame,
2042 const cAnimationFrames::sAnimationFrame &nextFrame)
2043 {
2044 CVector3 camera = frame.parameters.Get<CVector3>("main_camera");
2045 CVector3 previousCamera = nextFrame.parameters.Get<CVector3>("main_camera");
2046 double distance = (camera - previousCamera).Length();
2047 double speed = distance / frame.numberOfSubFrames;
2048 return speed;
2049 }
2050
slotAddAllParameters()2051 void cKeyframeAnimation::slotAddAllParameters()
2052 {
2053
2054 struct sParameterInfo
2055 {
2056 QString parameterName;
2057 std::shared_ptr<cParameterContainer> container;
2058 std::shared_ptr<cParameterContainer> containerBefore;
2059 };
2060
2061 std::shared_ptr<cParameterContainer> parBefore(new cParameterContainer);
2062 *parBefore = *params;
2063 std::shared_ptr<cFractalContainer> parFractBefore(new cFractalContainer);
2064 *parFractBefore = *fractalParams;
2065
2066 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
2067
2068 QList<sParameterInfo> listOfAllModifiedParameters;
2069
2070 QList<QString> listOfMainParameters = params->GetListOfParameters();
2071 for (QString parameterName : listOfMainParameters)
2072 {
2073 if (!params->isDefaultValue(parameterName)
2074 && params->GetParameterType(parameterName) == paramStandard
2075 && params->GetAsOneParameter(parameterName).GetMorphType() != morphNone)
2076 {
2077 // exceptions
2078 if (parameterName == "image_width") continue;
2079 if (parameterName == "image_height") continue;
2080 if (parameterName == "camera_distance_to_target") continue;
2081 if (parameterName == "camera_rotation") continue;
2082
2083 sParameterInfo par;
2084 par.parameterName = parameterName;
2085 par.container = params;
2086 par.containerBefore = parBefore;
2087 listOfAllModifiedParameters.append(par);
2088 }
2089 }
2090
2091 for (int i = 0; i < NUMBER_OF_FRACTALS; i++)
2092 {
2093 QList<QString> listOfFractalParameters = fractalParams->at(i)->GetListOfParameters();
2094 for (QString parameterName : listOfFractalParameters)
2095 {
2096 if (!fractalParams->at(i)->isDefaultValue(parameterName)
2097 && fractalParams->at(i)->GetParameterType(parameterName) == paramStandard)
2098 {
2099 sParameterInfo par;
2100 par.parameterName = parameterName;
2101 par.container = fractalParams->at(i);
2102 par.containerBefore = parFractBefore->at(i);
2103 listOfAllModifiedParameters.append(par);
2104 }
2105 }
2106 }
2107
2108 for (const sParameterInfo ¶meterInfo : listOfAllModifiedParameters)
2109 {
2110 QString parameterName = parameterInfo.parameterName;
2111 QString containerName = parameterInfo.container->GetContainerName();
2112 if (keyframes->IndexOnList(parameterName, containerName) == -1)
2113 {
2114 cOneParameter parameter = parameterInfo.containerBefore->GetAsOneParameter(parameterName);
2115 keyframes->AddAnimatedParameter(parameterName, parameter, params);
2116 }
2117 }
2118
2119 RefreshTable();
2120 }
2121
ModifyValueInCells(const QList<QTableWidgetItem * > & selectedItemsList,enumModifyMode mode)2122 void cKeyframeAnimation::ModifyValueInCells(
2123 const QList<QTableWidgetItem *> &selectedItemsList, enumModifyMode mode)
2124 {
2125 bool ok;
2126 const double modifier = QInputDialog::getDouble(
2127 mainInterface->mainWindow, "Modify values", "Modifier:", 1.0, -1e100, 1e100, 5, &ok);
2128 if (!ok) return;
2129
2130 for (auto cell : selectedItemsList)
2131 {
2132 int row = cell->row();
2133 int column = cell->column();
2134
2135 if (row == framesPerKeyframeRow && column >= reservedColumns)
2136 {
2137 const int index = column - reservedColumns;
2138 cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(index);
2139
2140 if (mode == modifyModeMultiply)
2141 frame.numberOfSubFrames *= modifier;
2142 else if (mode == modifyModeIncrease)
2143 frame.numberOfSubFrames += modifier;
2144
2145 keyframes->ModifyFrame(index, frame);
2146 }
2147 else if (row >= reservedRows && column >= reservedColumns)
2148 {
2149 QString cellText = cell->text();
2150
2151 const int index = column - reservedColumns;
2152 cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(index);
2153
2154 const QString parameterName = GetParameterName(row);
2155 const int parameterFirstRow = parameterRows[rowParameter[row]];
2156 const int vectIndex = row - parameterFirstRow;
2157
2158 using namespace parameterContainer;
2159 const enumVarType type = frame.parameters.GetVarType(parameterName);
2160
2161 if (type == typeVector3)
2162 {
2163 CVector3 vect = frame.parameters.Get<CVector3>(parameterName);
2164 if (mode == modifyModeMultiply)
2165 {
2166 if (vectIndex == 0) vect.x *= modifier;
2167 if (vectIndex == 1) vect.y *= modifier;
2168 if (vectIndex == 2) vect.z *= modifier;
2169 }
2170 else if (mode == modifyModeIncrease)
2171 {
2172 if (vectIndex == 0) vect.x += modifier;
2173 if (vectIndex == 1) vect.y += modifier;
2174 if (vectIndex == 2) vect.z += modifier;
2175 }
2176 frame.parameters.Set(parameterName, vect);
2177 }
2178 else if (type == typeVector4)
2179 {
2180 CVector4 vect = frame.parameters.Get<CVector4>(parameterName);
2181
2182 if (mode == modifyModeMultiply)
2183 {
2184 if (vectIndex == 0) vect.x *= modifier;
2185 if (vectIndex == 1) vect.y *= modifier;
2186 if (vectIndex == 2) vect.z *= modifier;
2187 if (vectIndex == 3) vect.w *= modifier;
2188 }
2189 else if (mode == modifyModeIncrease)
2190 {
2191 if (vectIndex == 0) vect.x += modifier;
2192 if (vectIndex == 1) vect.y += modifier;
2193 if (vectIndex == 2) vect.z += modifier;
2194 if (vectIndex == 3) vect.w += modifier;
2195 }
2196 frame.parameters.Set(parameterName, vect);
2197 }
2198 else
2199 {
2200 if (mode == modifyModeMultiply)
2201 {
2202 frame.parameters.Set(
2203 parameterName, frame.parameters.Get<double>(parameterName) * modifier);
2204 }
2205 else if (mode == modifyModeIncrease)
2206 {
2207 frame.parameters.Set(
2208 parameterName, frame.parameters.Get<double>(parameterName) + modifier);
2209 }
2210 }
2211
2212 keyframes->ModifyFrame(index, frame);
2213 }
2214 }
2215 RefreshTable();
2216 }
2217
slotSetFramesPerKeyframeToAllKeyframes(void)2218 void cKeyframeAnimation::slotSetFramesPerKeyframeToAllKeyframes(void)
2219 {
2220 SynchronizeInterfaceWindow(
2221 ui->scrollAreaWidgetContents_keyframeAnimationParameters, params, qInterface::read);
2222 int framesPerKeyframe = params->Get<int>("frames_per_keyframe");
2223
2224 for (int i = 0; i < keyframes->GetNumberOfFrames(); i++)
2225 {
2226 cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(i);
2227 frame.numberOfSubFrames = framesPerKeyframe;
2228 keyframes->ModifyFrame(i, frame);
2229 }
2230 RefreshTable();
2231 }
2232
AddAnimatedParameter(const QString & parameterName,std::shared_ptr<cParameterContainer> parameterContainer)2233 void cKeyframeAnimation::AddAnimatedParameter(
2234 const QString ¶meterName, std::shared_ptr<cParameterContainer> parameterContainer)
2235 {
2236 mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
2237 keyframes->AddAnimatedParameter(
2238 parameterName, parameterContainer->GetAsOneParameter(parameterName));
2239 }
2240
cKeyframeRenderThread(QString & _settingsText)2241 cKeyframeRenderThread::cKeyframeRenderThread(QString &_settingsText) : QThread()
2242 {
2243 settingsText = _settingsText;
2244 }
2245
startAnimationRender()2246 void cKeyframeRenderThread::startAnimationRender()
2247 {
2248 cSettings parSettings(cSettings::formatFullText);
2249 parSettings.BeQuiet(true);
2250 parSettings.LoadFromString(settingsText);
2251 parSettings.Decode(gPar, gParFractal, nullptr, gKeyframes);
2252 gNetRender->SetAnimation(true);
2253 gKeyframeAnimation->RenderKeyframes(&gMainInterface->stopRequest);
2254 emit renderingFinished();
2255 }
2256
cKeyframeSaveImageThread(std::shared_ptr<cImage> _image,const QString & _filename,ImageFileSave::enumImageFileType _fileType)2257 cKeyframeSaveImageThread::cKeyframeSaveImageThread(std::shared_ptr<cImage> _image,
2258 const QString &_filename, ImageFileSave::enumImageFileType _fileType)
2259 {
2260 image = _image;
2261 filename = _filename;
2262 fileType = _fileType;
2263 }
2264
startSaving()2265 void cKeyframeSaveImageThread::startSaving()
2266 {
2267 SaveImage(filename, fileType, image, gMainInterface->mainWindow);
2268 emit savingFinished();
2269 }
2270