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 &parameterDescription, 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