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)
31 *
32 * RenderedImage class - extension for QWidget class. Widget prepared for displaying rendered image
33 * and 3D cursor
34 */
35
36 #include "rendered_image_widget.hpp"
37
38 #include <QApplication>
39 #include <QKeyEvent>
40 #include <QMouseEvent>
41 #include <QPainter>
42 #include <QStaticText>
43 #include <QVariant>
44
45 #include "animation_path_data.hpp"
46 #include "camera_movement_modes.h"
47 #include "cimage.hpp"
48 #include "common_math.h"
49 #include "fractparams.hpp"
50 #include "light.h"
51 #include "nine_fractals.hpp"
52 #include "parameters.hpp"
53 #include "primitives.h"
54 #include "trace_behind.h"
55
56 using namespace Qt;
57
RenderedImage(QWidget * parent)58 RenderedImage::RenderedImage(QWidget *parent) : QWidget(parent)
59 {
60 // makes RenderedImage focusable to catch keyboard events
61 setFocusPolicy(Qt::StrongFocus);
62 setMouseTracking(true);
63
64 image = nullptr;
65 params = nullptr;
66 fractals = nullptr;
67 cursorVisible = true;
68 lightsVisible = false;
69 smoothLastZMouse = 0.0;
70 redrawed = true;
71 isFocus = false;
72 isOnObject = false;
73 lastDepth = 0.0;
74 frontDist = 0.0;
75 flightRotationDirection = 0;
76 clickMode = clickDoNothing;
77 anaglyphMode = false;
78 gridType = gridTypeCrosshair;
79 placeLightBehind = false;
80 clickModesEnables = true;
81 draggingStarted = false;
82 draggingInitStarted = false;
83 buttonsPressed = 0;
84 currentLightIndex = 1;
85
86 QList<QVariant> mode;
87 mode.append(int(RenderedImage::clickDoNothing));
88 clickModeData = mode;
89 cameraMovementMode = cameraMovementEnums::fixedDistance;
90
91 // timer to refresh image
92 timerRefreshImage = new QTimer(this);
93 timerRefreshImage->setInterval(40);
94 connect(timerRefreshImage, SIGNAL(timeout()), this, SLOT(update()));
95 }
96
paintEvent(QPaintEvent * event)97 void RenderedImage::paintEvent(QPaintEvent *event)
98 {
99 (void)event;
100
101 if (image)
102 {
103 CVector2<int> point = lastMousePosition / image->GetPreviewScale();
104 float z;
105 if (point.x >= 0 && point.y >= 0 && point.x < int(image->GetWidth())
106 && point.y < int(image->GetHeight()))
107 z = image->GetPixelZBuffer(point.x, point.y);
108 else
109 z = float(1e20);
110
111 if (params)
112 {
113 if ((cursorVisible && isFocus) || gridType != gridTypeCrosshair)
114 {
115 if (!anaglyphMode) DisplayCrosshair();
116 }
117
118 if (lightsVisible)
119 {
120 DisplayAllLights();
121 }
122
123 if (cursorVisible && isFocus)
124 {
125 if (z < 1e10 || enumClickMode(clickModeData.at(0).toInt()) == clickFlightSpeedControl)
126 {
127 redrawed = false;
128 if (!isOnObject)
129 {
130 QApplication::setOverrideCursor(Qt::BlankCursor);
131 }
132 isOnObject = true;
133
134 Display3DCursor(lastMousePosition, z);
135 }
136 else
137 {
138 if (isOnObject)
139 {
140 QApplication::restoreOverrideCursor();
141 }
142 isOnObject = false;
143 }
144 }
145 }
146
147 if (params && animationPathData.animationPath.length() > 0)
148 {
149 DrawAnimationPath();
150 }
151
152 image->RedrawInWidget();
153
154 if (params)
155 {
156 if (cursorVisible && isFocus && !anaglyphMode
157 && (isOnObject || enumClickMode(clickModeData.at(0).toInt()) == clickFlightSpeedControl))
158 {
159 DisplayCoordinates();
160 }
161 }
162
163 if (params && cursorVisible && clickMode != clickFlightSpeedControl)
164 {
165 CVector3 rotation = params->Get<CVector3>("camera_rotation") / 180.0 * M_PI;
166 Compass(rotation, QPointF(image->GetPreviewWidth() * 0.9, image->GetPreviewHeight() * 0.9),
167 image->GetPreviewHeight() * 0.05);
168 }
169
170 if (params)
171 {
172 if (clickMode == clickFlightSpeedControl)
173 {
174 Compass(flightData.rotation,
175 QPointF(image->GetPreviewWidth() * 0.5, image->GetPreviewHeight() * 0.5),
176 image->GetPreviewHeight() * 0.2);
177 }
178 }
179
180 PaintLastRenderedTilesInfo();
181
182 redrawed = true;
183 }
184 else
185 {
186 qCritical() << "RenderedImage::mouseMoveEvent(QMouseEvent * event): image not assigned";
187 }
188 }
189
DisplayCoordinates()190 void RenderedImage::DisplayCoordinates()
191 {
192 QPainter painter(this);
193 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
194
195 QPen penWhite(Qt::white, 1, Qt::SolidLine);
196 QBrush brushBrown(QColor(100, 50, 0));
197 QBrush brushDarkBlue(QColor(0, 0, 100));
198
199 QString text;
200 enumClickMode clickMode = enumClickMode(clickModeData.at(0).toInt());
201 switch (clickMode)
202 {
203 case clickMoveCamera:
204 {
205 switch (cameraMovementEnums::enumCameraMovementMode(cameraMovementMode))
206 {
207 case cameraMovementEnums::fixedDistance: text = tr("Move camera and target"); break;
208 case cameraMovementEnums::moveCamera: text = tr("Move camera"); break;
209 case cameraMovementEnums::moveTarget: text = tr("Move target"); break;
210 }
211 break;
212 }
213 case clickFogVisibility: text = tr("Change fog visibility"); break;
214 case clickDOFFocus: text = tr("Change DOF focus"); break;
215 case clickPlaceLight:
216 text = tr("Place light #") + QString::number(clickModeData.at(1).toInt());
217 text += tr("\nCtrl + Mouse wheel - light fwd/bkwd ");
218 text += tr("\nAlt + Mouse wheel - placement fwd/bkwd ");
219 break;
220 case clickPlacePrimitive:
221 text = tr("Place ") + PrimitiveNames(fractal::enumObjectType(clickModeData.at(1).toInt()))
222 + QString(" #") + QString::number(clickModeData.at(2).toInt());
223 break;
224 case clickGetJuliaConstant: text = tr("Get Julia constant"); break;
225 case clickFlightSpeedControl:
226 text = tr("LMB - increase speed");
227 text += tr("\nRMB - decrease speed");
228 text += tr("\narrow keys - sidewards");
229 text += tr("\nz, x keys - roll");
230 text += tr("\nspacebar - pause");
231 text += tr("\nhold shift key - orthogonal move");
232 break;
233 case clickDoNothing: text = ""; break;
234 case clickPlaceRandomLightCenter:
235 text = tr("Place center of random light");
236 text += tr("\nalso calculates");
237 text += tr("\ndistribution radius of lights to 50%,");
238 text += tr("\nmax distance from fractal to 10%");
239 text += tr("\nof distance [center to camera position]");
240 break;
241 case clickGetPoint:
242 text = tr("Get coordinates");
243 text += tr("\nand distance");
244 break;
245 case clickWrapLimitsAroundObject: text = tr("Wrap limits\naround object"); break;
246 }
247
248 if (clickMode != clickDoNothing)
249 {
250 QRect textRect = painter.boundingRect(QRect(), Qt::AlignTop | Qt::AlignLeft, text);
251 textRect.setHeight(textRect.height() + 2);
252 textRect.moveBottomLeft(QPoint(lastMousePosition.x + 30, lastMousePosition.y - 3));
253
254 painter.setOpacity(0.8);
255 painter.setPen(penWhite);
256 painter.setBrush(brushBrown);
257 painter.drawRoundedRect(textRect, 3, 3);
258 painter.drawText(textRect, Qt::AlignTop | Qt::AlignLeft, text);
259 }
260
261 QString textCoordinates;
262 if (clickMode != clickFlightSpeedControl)
263 {
264 textCoordinates += "x: " + QString::number(lastCoordinates.x, 'g', 15);
265 textCoordinates += "\ny: " + QString::number(lastCoordinates.y, 'g', 15);
266 textCoordinates += "\nz: " + QString::number(lastCoordinates.z, 'g', 15);
267 textCoordinates += "\ndist: " + QString::number(lastDepth, 'g', 15);
268 }
269 else
270 {
271 textCoordinates += "frame: " + QString::number(flightData.frame);
272 textCoordinates += "\nx: " + QString::number(flightData.camera.x, 'g', 15);
273 textCoordinates += "\ny: " + QString::number(flightData.camera.y, 'g', 15);
274 textCoordinates += "\nz: " + QString::number(flightData.camera.y, 'z', 15);
275 textCoordinates += "\ndist: " + QString::number(flightData.distance);
276 textCoordinates += "\nspeed act: " + QString::number(flightData.speed);
277 textCoordinates += "\nspeed set: " + QString::number(flightData.speedSp);
278 }
279
280 QRect textRect2 = painter.boundingRect(QRect(), Qt::AlignTop | Qt::AlignLeft, textCoordinates);
281 textRect2.setHeight(textRect2.height() + 2);
282 textRect2.moveTopLeft(QPoint(lastMousePosition.x + 30, lastMousePosition.y + 3));
283 painter.setOpacity(0.8);
284 painter.setPen(penWhite);
285 painter.setBrush(brushDarkBlue);
286 painter.drawRoundedRect(textRect2, 3, 3);
287 painter.drawText(textRect2, Qt::AlignTop | Qt::AlignLeft, textCoordinates);
288 }
289
Display3DCursor(CVector2<int> screenPoint,double z)290 void RenderedImage::Display3DCursor(CVector2<int> screenPoint, double z)
291 {
292 clickMode = enumClickMode(clickModeData.at(0).toInt());
293 if (clickMode == clickPlaceLight)
294 {
295 if (!placeLightBehind)
296 {
297 z -= frontDist;
298 }
299 }
300
301 double diff = z - smoothLastZMouse;
302 if (fabs(diff) >= 1.0)
303 {
304 smoothLastZMouse += diff * 0.01;
305 }
306 else
307 {
308 double delta = sqrt(fabs(diff)) * 0.01;
309 smoothLastZMouse += (diff > 0 ? 1.0 : -1.0) * fmin(delta, fabs(diff));
310 }
311
312 if (z > 0 && clickMode != clickFlightSpeedControl)
313 {
314 if (smoothLastZMouse < 0.0) smoothLastZMouse = 0.0;
315
316 bool legacyCoordinateSystem = params->Get<bool>("legacy_coordinate_system");
317 double reverse = legacyCoordinateSystem ? -1.0 : 1.0;
318
319 // preparing rotation matrix
320 CVector3 rotation = params->Get<CVector3>("camera_rotation") / 180.0 * M_PI;
321 double sweetSpotHAngle = params->Get<double>("sweet_spot_horizontal_angle") / 180.0 * M_PI;
322 double sweetSpotVAngle = params->Get<double>("sweet_spot_vertical_angle") / 180.0 * M_PI;
323
324 bool stereoEnabled = params->Get<bool>("stereo_enabled");
325 cStereo::enumStereoMode stereoMode = cStereo::enumStereoMode(params->Get<int>("stereo_mode"));
326 anaglyphMode = stereoMode == cStereo::stereoRedCyan && stereoEnabled;
327 double stereoEyeDistance = params->Get<double>("stereo_eye_distance");
328 double stereoInfiniteCorrection = params->Get<double>("stereo_infinite_correction");
329 double distanceLimit = params->Get<double>("view_distance_max");
330
331 params::enumPerspectiveType perspType =
332 params::enumPerspectiveType(params->Get<int>("perspective_type"));
333 CVector3 camera = params->Get<CVector3>("camera");
334
335 CRotationMatrix mRot;
336 mRot.RotateZ(rotation.x);
337 mRot.RotateX(rotation.y);
338 mRot.RotateY(rotation.z);
339 mRot.RotateZ(-sweetSpotHAngle);
340 mRot.RotateX(sweetSpotVAngle);
341
342 double fov = CalcFOV(params->Get<double>("fov"), perspType);
343
344 double sw = image->GetPreviewWidth();
345 double sh = image->GetPreviewHeight();
346 double aspectRatio = sw / sh;
347
348 if (perspType == params::perspEquirectangular) aspectRatio = 2.0;
349
350 CVector2<double> p;
351 p.x = (screenPoint.x / sw - 0.5) * aspectRatio;
352 p.y = (screenPoint.y / sh - 0.5);
353
354 double scale = smoothLastZMouse / z;
355
356 // calculate 3D point coordinates
357 CVector2<double> pTemp = p;
358 pTemp.y *= -1.0 * reverse;
359 CVector3 viewVector = CalculateViewVector(pTemp, fov, perspType, mRot);
360 CVector3 point = camera + viewVector * z;
361
362 if (clickMode == clickPlaceLight)
363 {
364 if (placeLightBehind)
365 {
366 double distanceBehind = traceBehindFractal(
367 params, fractals, frontDist, viewVector, z, 1.0 / image->GetHeight(), distanceLimit);
368 z += distanceBehind;
369 }
370 }
371
372 lastCoordinates = point;
373
374 if (anaglyphMode)
375 {
376 CVector2<float> p1, p2;
377 p1.x = p.x;
378 p1.y = p.y;
379 Draw3DBox(scale, fov, p1, z, cStereo::eyeLeft);
380 if (perspType == params::perspThreePoint || perspType == params::perspFishEye
381 || perspType == params::perspFishEyeCut)
382 {
383 p2.x = p.x - 2.0 * (stereoEyeDistance / z - stereoInfiniteCorrection / 10.0) / fov;
384 }
385 else
386 {
387 p2.x = p.x - 2.0 * (stereoEyeDistance / z - stereoInfiniteCorrection / 10.0) / fov * 2.0;
388 }
389 p2.y = p.y;
390 Draw3DBox(scale, fov, p2, z, cStereo::eyeRight);
391 }
392 else
393 {
394 Draw3DBox(scale, fov, CVector2<float>(p.x, p.y), z, cStereo::eyeNone);
395 }
396 }
397 else if (clickMode == clickFlightSpeedControl)
398 {
399 // draw small cross
400 image->AntiAliasedLine(screenPoint.x - 20, screenPoint.y - 20, screenPoint.x + 20,
401 screenPoint.y + 20, -1, -1, sRGB8(255, 255, 255), sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
402 image->AntiAliasedLine(screenPoint.x + 20, screenPoint.y - 20, screenPoint.x - 20,
403 screenPoint.y + 20, -1, -1, sRGB8(255, 255, 255), sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
404 }
405 lastDepth = z;
406 }
407
Draw3DBox(float scale,float fov,CVector2<float> p,float z,cStereo::enumEye eye) const408 void RenderedImage::Draw3DBox(
409 float scale, float fov, CVector2<float> p, float z, cStereo::enumEye eye) const
410 {
411 float sw = image->GetPreviewWidth();
412 float sh = image->GetPreviewHeight();
413
414 float aspectRatio = sw / sh;
415
416 float boxWidth = 10.0f / sw * scale;
417 float boxHeight = 10.0f / sw * scale;
418 float boxDepth = 10.0f / sw * scale;
419
420 float boxDepth2 = boxHeight * z * fov;
421
422 float n = 3.0;
423 int in = int(n);
424
425 sRGBFloat opacity;
426 switch (eye)
427 {
428 case cStereo::eyeNone: opacity = sRGBFloat(0.8f, 0.8f, 0.8f); break;
429 case cStereo::eyeLeft: opacity = sRGBFloat(0.8f, 0.0f, 0.0f); break;
430 case cStereo::eyeRight: opacity = sRGBFloat(0.0f, 0.8f, 0.8f); break;
431 }
432
433 unsigned char R, G, B;
434 for (int iz = -in; iz <= in; iz++)
435 {
436 float yy1 = ((p.y + n * boxHeight) / (1.0f - boxDepth * iz * fov) + 0.5f) * sh;
437 float yy2 = ((p.y - n * boxHeight) / (1.0f - boxDepth * iz * fov) + 0.5f) * sh;
438 for (int ix = -in; ix <= in; ix++)
439 {
440 float xx1 = ((p.x + boxWidth * ix) / (1.0f - boxDepth * iz * fov) / aspectRatio + 0.5f) * sw;
441 if (eye == cStereo::eyeNone)
442 {
443 R = uchar(128 + iz * 40);
444 G = uchar(128 - iz * 40);
445 B = 0;
446 }
447 else
448 {
449 R = uchar(128 + iz * 40);
450 G = uchar(128 + iz * 40);
451 B = uchar(128 + iz * 40);
452 }
453 if (iz == 0 && ix == 0)
454 {
455 R = G = B = 255;
456 // opacity = 1.0;
457 }
458 image->AntiAliasedLine(xx1, yy1, xx1, yy2, z - iz * boxDepth2, z - iz * boxDepth2,
459 sRGB8(R, G, B), opacity, 1.0f, 1);
460 }
461
462 float xx1 = ((p.x + n * boxWidth) / (1.0f - boxDepth * iz * fov) / aspectRatio + 0.5f) * sw;
463 float xx2 = ((p.x - n * boxWidth) / (1.0f - boxDepth * iz * fov) / aspectRatio + 0.5f) * sw;
464 for (int iy = -in; iy <= in; iy++)
465 {
466 float yyn1 = ((p.y + boxWidth * iy) / (1.0f - boxDepth * iz * fov) + 0.5f) * sh;
467
468 if (eye == cStereo::eyeNone)
469 {
470 R = uchar(128 + iz * 40);
471 G = uchar(128 - iz * 40);
472 B = 0;
473 }
474 else
475 {
476 R = uchar(128 + iz * 40);
477 G = uchar(128 + iz * 40);
478 B = uchar(128 + iz * 40);
479 }
480
481 if (iz == 0 && iy == 0)
482 {
483 R = G = B = 255;
484 // opacity = 1.0;
485 }
486
487 image->AntiAliasedLine(xx1, yyn1, xx2, yyn1, z - iz * boxDepth2, z - iz * boxDepth2,
488 sRGB8(R, G, B), opacity, 1.0f, 1);
489 }
490
491 if (iz < n)
492 {
493 for (int ix = -in; ix <= in; ix++)
494 {
495 for (int iy = -in; iy <= in; iy++)
496 {
497 float xxn1 =
498 ((p.x + boxWidth * ix) / (1.0f - boxDepth * iz * fov) / aspectRatio + 0.5f) * sw;
499 float yyn1 = ((p.y + boxWidth * iy) / (1.0f - boxDepth * iz * fov) + 0.5f) * sh;
500 float xxn2 =
501 ((p.x + boxWidth * ix) / (1.0f - boxDepth * (iz + 1) * fov) / aspectRatio + 0.5f) * sw;
502 float yyn2 = ((p.y + boxWidth * iy) / (1.0f - boxDepth * (iz + 1) * fov) + 0.5f) * sh;
503
504 if (eye == cStereo::eyeNone)
505 {
506 R = uchar(128 + iz * 40);
507 G = uchar(128 - iz * 40);
508 B = 0;
509 }
510 else
511 {
512 R = uchar(128 + iz * 40);
513 G = uchar(128 + iz * 40);
514 B = uchar(128 + iz * 40);
515 }
516
517 if (ix == 0 && iy == 0)
518 {
519 R = G = B = 255;
520 // opacity = 1.0;
521 }
522
523 image->AntiAliasedLine(xxn1, yyn1, xxn2, yyn2, z - iz * boxDepth2,
524 z - (iz + 1) * boxDepth2, sRGB8(R, G, B), opacity, 1.0f, 1);
525 }
526 }
527 }
528 if (iz == 0)
529 {
530 CVector2<float> sPoint((p.x / aspectRatio + 0.5f) * sw, (p.y + 0.5f) * sh);
531 image->AntiAliasedLine(sPoint.x - sw * 0.3f, sPoint.y, sPoint.x + sw * 0.3f, sPoint.y, z, z,
532 sRGB8(255, 255, 255), opacity, 1.0f, 1);
533 image->AntiAliasedLine(sPoint.x, sPoint.y - sh * 0.3f, sPoint.x, sPoint.y + sh * 0.3f, z, z,
534 sRGB8(255, 255, 255), opacity, 1.0f, 1);
535 if (anaglyphMode)
536 {
537 image->AntiAliasedLine(sPoint.x - sw * 0.05f, sPoint.y - sh * 0.05f, sPoint.x + sw * 0.05f,
538 sPoint.y - sh * 0.05f, z, z, sRGB8(0, 0, 0), opacity, 1.0f, 1);
539 image->AntiAliasedLine(sPoint.x + sw * 0.05f, sPoint.y - sh * 0.05f, sPoint.x + sw * 0.05f,
540 sPoint.y + sh * 0.05f, z, z, sRGB8(0, 0, 0), opacity, 1.0f, 1);
541 image->AntiAliasedLine(sPoint.x + sw * 0.05f, sPoint.y + sh * 0.05f, sPoint.x - sw * 0.05f,
542 sPoint.y + sh * 0.05f, z, z, sRGB8(0, 0, 0), opacity, 1.0f, 1);
543 image->AntiAliasedLine(sPoint.x - sw * 0.05f, sPoint.y + sh * 0.05f, sPoint.x - sw * 0.05f,
544 sPoint.y - sh * 0.05f, z, z, sRGB8(0, 0, 0), opacity, 1.0f, 1);
545 }
546
547 if (clickMode == clickPlaceLight)
548 {
549 float r = 1.5f * (boxWidth * n / aspectRatio);
550 if (r > 1.0f) r = 1.0f;
551 image->CircleBorder(
552 sPoint.x, sPoint.y, z, r * sw, sRGB8(0, 100, 255), r * 0.1f * sw, opacity, 1);
553 }
554 }
555 }
556 }
557
mouseMoveEvent(QMouseEvent * event)558 void RenderedImage::mouseMoveEvent(QMouseEvent *event)
559 {
560 CVector2<int> screenPoint(event->x(), event->y());
561
562 // remember last mouse position
563 lastMousePosition = screenPoint;
564
565 CVector2<double> yawAndPitch;
566 yawAndPitch.x = (double(lastMousePosition.x) / image->GetPreviewWidth() - 0.5) * 2.0;
567 yawAndPitch.y = (double(lastMousePosition.y) / image->GetPreviewHeight() - 0.5) * 2.0;
568 emit YawAndPitchChanged(yawAndPitch);
569
570 if (params)
571 {
572 if (cursorVisible && isFocus && redrawed)
573 {
574 update();
575 }
576 }
577 else
578 {
579 if (cursorVisible)
580 qCritical() << "RenderedImage::mouseMoveEvent(QMouseEvent * event): parameters not assigned";
581 }
582
583 emit mouseMoved(screenPoint.x, screenPoint.y);
584
585 if (draggingInitStarted)
586 {
587 if (abs(screenPoint.x - dragStartPosition.x) > 1
588 || abs(screenPoint.y - dragStartPosition.y) > 1)
589 {
590 draggingInitStarted = false;
591 draggingStarted = true;
592 emit mouseDragStart(dragStartPosition.x, dragStartPosition.y, dragButtons);
593 }
594 }
595
596 if (draggingStarted)
597 {
598 int dx = screenPoint.x - dragStartPosition.x;
599 int dy = screenPoint.y - dragStartPosition.y;
600 emit mouseDragDelta(dx, dy);
601 }
602 }
603
mousePressEvent(QMouseEvent * event)604 void RenderedImage::mousePressEvent(QMouseEvent *event)
605 {
606 if (enumClickMode(clickModeData.at(0).toInt()) == clickFlightSpeedControl)
607 {
608 if (event->button() == Qt::LeftButton)
609 {
610 emit SpeedChanged(1.1);
611 }
612 else if (event->button() == Qt::RightButton)
613 {
614 emit SpeedChanged(0.9);
615 }
616 }
617 else
618 {
619 if (clickModesEnables)
620 {
621 draggingInitStarted = true;
622 dragStartPosition = CVector2<int>(event->x(), event->y());
623 dragButtons = event->buttons();
624 }
625 }
626 buttonsPressed++;
627 }
628
mouseReleaseEvent(QMouseEvent * event)629 void RenderedImage::mouseReleaseEvent(QMouseEvent *event)
630 {
631 if (!draggingStarted && enumClickMode(clickModeData.at(0).toInt()) != clickFlightSpeedControl)
632 {
633 if (clickModesEnables)
634 {
635 emit singleClick(event->x(), event->y(), event->button());
636 }
637 }
638
639 if (buttonsPressed == 1)
640 {
641 draggingStarted = false;
642 draggingInitStarted = false;
643 emit mouseDragFinish();
644 }
645 buttonsPressed--;
646
647 // in case if some release event was missed
648 if (event->buttons() == Qt::NoButton)
649 {
650 buttonsPressed = 0;
651 draggingStarted = false;
652 draggingInitStarted = false;
653 }
654 }
655
enterEvent(QEvent * event)656 void RenderedImage::enterEvent(QEvent *event)
657 {
658 (void)event;
659
660 if (!isFocus)
661 {
662 setFocus();
663 QApplication::setOverrideCursor(Qt::CrossCursor);
664 }
665 isFocus = true;
666 timerRefreshImage->start();
667 }
668
leaveEvent(QEvent * event)669 void RenderedImage::leaveEvent(QEvent *event)
670 {
671 (void)event;
672 isFocus = false;
673 isOnObject = false;
674 update();
675 timerRefreshImage->stop();
676 QApplication::restoreOverrideCursor();
677 QApplication::restoreOverrideCursor();
678 }
679
keyPressEvent(QKeyEvent * event)680 void RenderedImage::keyPressEvent(QKeyEvent *event)
681 {
682 if (event->isAutoRepeat())
683 {
684 event->ignore();
685 }
686 else
687 {
688 if (enumClickMode(clickModeData.at(0).toInt()) == clickFlightSpeedControl)
689 {
690 Qt::Key key = Qt::Key(event->key());
691 if (key == Qt::Key_Up)
692 {
693 keyArrows.y += 1;
694 emit StrafeChanged(keyArrows);
695 }
696 else if (key == Qt::Key_Down)
697 {
698 keyArrows.y -= 1;
699 emit StrafeChanged(keyArrows);
700 }
701 else if (key == Qt::Key_Left)
702 {
703 keyArrows.x -= 1;
704 emit StrafeChanged(keyArrows);
705 }
706 else if (key == Qt::Key_Right)
707 {
708 keyArrows.x += 1;
709 emit StrafeChanged(keyArrows);
710 }
711 else if (key == Qt::Key_Z)
712 {
713 flightRotationDirection = 1;
714 emit RotationChanged(flightRotationDirection);
715 }
716 else if (key == Qt::Key_X)
717 {
718 flightRotationDirection = -1;
719 emit RotationChanged(flightRotationDirection);
720 }
721 else if (key == Qt::Key_Space)
722 {
723 emit Pause();
724 }
725 else if (key == Qt::Key_Shift)
726 {
727 emit ShiftModeChanged(true);
728 }
729 }
730 else
731 {
732 emit keyPress(event);
733 }
734 }
735 event->ignore(); // pass pressed key event to parents
736 }
737
keyReleaseEvent(QKeyEvent * event)738 void RenderedImage::keyReleaseEvent(QKeyEvent *event)
739 {
740 if (event->isAutoRepeat())
741 {
742 event->ignore();
743 emit keyRelease(event);
744 }
745 else
746 {
747 if (enumClickMode(clickModeData.at(0).toInt()) == clickFlightSpeedControl)
748 {
749 Qt::Key key = Qt::Key(event->key());
750 if (key == Qt::Key_Up)
751 {
752 keyArrows.y -= 1;
753 emit StrafeChanged(keyArrows);
754 }
755 else if (key == Qt::Key_Down)
756 {
757 keyArrows.y += 1;
758 emit StrafeChanged(keyArrows);
759 }
760 else if (key == Qt::Key_Left)
761 {
762 keyArrows.x += 1;
763 emit StrafeChanged(keyArrows);
764 }
765 else if (key == Qt::Key_Right)
766 {
767 keyArrows.x -= 1;
768 emit StrafeChanged(keyArrows);
769 }
770 else if (key == Qt::Key_Z)
771 {
772 flightRotationDirection = 0;
773 emit RotationChanged(flightRotationDirection);
774 }
775 else if (key == Qt::Key_X)
776 {
777 flightRotationDirection = 0;
778 emit RotationChanged(flightRotationDirection);
779 }
780 else if (key == Qt::Key_Shift)
781 {
782 emit ShiftModeChanged(false);
783 }
784 }
785 else
786 {
787 emit keyRelease(event);
788 }
789 }
790 event->ignore(); // pass pressed key event to parents
791 }
792
wheelEvent(QWheelEvent * event)793 void RenderedImage::wheelEvent(QWheelEvent *event)
794 {
795 if (clickModesEnables || enumClickMode(clickModeData.at(0).toInt()) == clickFlightSpeedControl)
796 {
797 if ((event->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) != 0)
798 {
799 event->accept(); // do not propagate event to parent widgets - prevents from scrolling
800
801 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
802 emit mouseWheelRotatedWithKey(event->x(), event->y(), event->delta(), event->modifiers());
803 #else
804 emit mouseWheelRotatedWithKey(int(event->position().x()), int(event->position().y()),
805 event->angleDelta().y() + event->angleDelta().x(), event->modifiers());
806 // with alt key there is modified delta.x
807 #endif
808 if (params)
809 {
810 if (cursorVisible && isFocus && redrawed)
811 {
812 update();
813 }
814 }
815 else
816 {
817 if (cursorVisible)
818 qCritical()
819 << "RenderedImage::mouseMoveEvent(QMouseEvent * event): parameters not assigned";
820 }
821 }
822 }
823 }
824
DisplayCrosshair() const825 void RenderedImage::DisplayCrosshair() const
826 {
827 // calculate crosshair center point according to sweet point
828
829 double sweetSpotHAngle = params->Get<double>("sweet_spot_horizontal_angle") / 180.0 * M_PI;
830 double sweetSpotVAngle = params->Get<double>("sweet_spot_vertical_angle") / 180.0 * M_PI;
831 params::enumPerspectiveType perspType =
832 params::enumPerspectiveType(params->Get<int>("perspective_type"));
833
834 double fov = CalcFOV(params->Get<double>("fov"), perspType);
835
836 float sw = image->GetPreviewWidth();
837 float sh = image->GetPreviewHeight();
838
839 double aspectRatio = sw / sh;
840
841 CVector2<float> crossShift;
842
843 switch (perspType)
844 {
845 case params::perspThreePoint:
846 crossShift.y = tan(sweetSpotVAngle) / fov;
847 crossShift.x = tan(-sweetSpotHAngle) / fov / cos(sweetSpotVAngle) / aspectRatio;
848 break;
849
850 case params::perspFishEye:
851 case params::perspFishEyeCut:
852 {
853 CVector3 forward(0.0, 0.0, 1.0);
854 forward = forward.RotateAroundVectorByAngle(CVector3(0.0, 1.0, 0.0), -sweetSpotHAngle);
855 forward = forward.RotateAroundVectorByAngle(CVector3(1.0, 0.0, 0.0), -sweetSpotVAngle);
856 forward.Normalize();
857 double r = sqrt(forward.x * forward.x + forward.y * forward.y);
858 if (r > 0)
859 {
860 double r2 = asin(r) * 2.;
861 crossShift.x = (forward.x / fov) * r2 / r / 2.0 / aspectRatio;
862 crossShift.y = (forward.y / fov) * r2 / r / 2.0;
863 }
864 else
865 {
866 crossShift = CVector2<float>(0, 0);
867 }
868 break;
869 }
870
871 case params::perspEquirectangular:
872 {
873 CVector3 forward(0.0, 0.0, 1.0);
874 forward = forward.RotateAroundVectorByAngle(CVector3(0.0, 1.0, 0.0), -sweetSpotHAngle);
875 forward = forward.RotateAroundVectorByAngle(CVector3(1.0, 0.0, 0.0), -sweetSpotVAngle);
876 crossShift.x = asin(forward.x / cos(asin(forward.y))) / 0.5 / fov / aspectRatio;
877 if (forward.z < 0 && crossShift.x > 0) crossShift.x = fov / aspectRatio - crossShift.x;
878 if (forward.z < 0 && crossShift.x < 0) crossShift.x = -fov / aspectRatio - crossShift.x;
879 crossShift.y = asin(forward.y) / 0.5 / fov;
880 break;
881 }
882 }
883
884 CVector2<float> crossCenter;
885 crossCenter.x = (sw * 0.5f) * (1.0f + 2.0f * crossShift.x);
886 crossCenter.y = (sh * 0.5f) * (1.0f + 2.0f * crossShift.y);
887
888 if (params->Get<bool>("stereo_enabled")
889 && params->Get<bool>("stereo_mode") == cStereo::stereoLeftRight)
890 {
891 image->AntiAliasedLine(crossCenter.x / 2, 0, crossCenter.x / 2, sh, -1, -1,
892 sRGB8(255, 255, 255), sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
893 image->AntiAliasedLine(crossCenter.x * 1.5f, 0, crossCenter.x * 1.5f, sh, -1, -1,
894 sRGB8(255, 255, 255), sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
895 image->AntiAliasedLine(0, crossCenter.y, sw, crossCenter.y, -1, -1, sRGB8(255, 255, 255),
896 sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
897 }
898 else
899 {
900 switch (gridType)
901 {
902 case gridTypeCrosshair:
903 image->AntiAliasedLine(crossCenter.x, 0, crossCenter.x, sh, -1, -1, sRGB8(255, 255, 255),
904 sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
905 image->AntiAliasedLine(0, crossCenter.y, sw, crossCenter.y, -1, -1, sRGB8(255, 255, 255),
906 sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
907 break;
908
909 case gridTypeThirds:
910 image->AntiAliasedLine(sw * 0.3333f, 0, sw * 0.3333f, sh, -1, -1, sRGB8(255, 255, 255),
911 sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
912 image->AntiAliasedLine(sw * 0.6666f, 0, sw * 0.6666f, sh, -1, -1, sRGB8(255, 255, 255),
913 sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
914 image->AntiAliasedLine(0, sh * 0.3333f, sw, sh * 0.3333f, -1, -1, sRGB8(255, 255, 255),
915 sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
916 image->AntiAliasedLine(0, sh * 0.6666f, sw, sh * 0.6666f, -1, -1, sRGB8(255, 255, 255),
917 sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
918 break;
919
920 case gridTypeGolden:
921 float goldenRatio = (1.0f + sqrtf(5.0f)) / 2.0f;
922 float ratio1 = goldenRatio - 1.0f;
923 float ratio2 = 1.0f - ratio1;
924 image->AntiAliasedLine(sw * ratio1, 0, sw * ratio1, sh, -1, -1, sRGB8(255, 255, 255),
925 sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
926 image->AntiAliasedLine(sw * ratio2, 0, sw * ratio2, sh, -1, -1, sRGB8(255, 255, 255),
927 sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
928 image->AntiAliasedLine(0, sh * ratio1, sw, sh * ratio1, -1, -1, sRGB8(255, 255, 255),
929 sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
930 image->AntiAliasedLine(0, sh * ratio2, sw, sh * ratio2, -1, -1, sRGB8(255, 255, 255),
931 sRGBFloat(0.3f, 0.3f, 0.3f), 1.0f, 1);
932 break;
933 }
934 }
935 }
936
Compass(CVector3 rotation,QPointF center,double size)937 void RenderedImage::Compass(CVector3 rotation, QPointF center, double size)
938 {
939 QPainter painter(this);
940 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
941
942 QPen penRed(Qt::red, 1.0, Qt::SolidLine);
943 QPen penGreen(Qt::green, 1.0, Qt::SolidLine);
944 QPen penBlue(Qt::blue, 1.0, Qt::SolidLine);
945 QPen penYellow(Qt::yellow, 2.0, Qt::SolidLine);
946 QPen penMagenta(Qt::magenta, 2.0, Qt::SolidLine);
947 QPen penCyan(Qt::cyan, 2.0, Qt::SolidLine);
948
949 CRotationMatrix mRotInv;
950 mRotInv.RotateY(-rotation.z);
951 mRotInv.RotateX(-rotation.y);
952 mRotInv.RotateZ(-rotation.x);
953
954 // Draw circles
955 const int steps = 40;
956 const double persp = 0.5;
957 CVector3 circlePoint1[steps];
958 CVector3 circlePoint2[steps];
959 CVector3 circlePoint3[steps];
960
961 for (int i = 0; i < steps; i++)
962 {
963 double angle = i * 2.0 * M_PI / steps;
964
965 circlePoint1[i].x = cos(angle);
966 circlePoint1[i].y = sin(angle);
967 circlePoint1[i].z = 0.0;
968 circlePoint2[i].x = 0;
969 circlePoint2[i].y = sin(angle);
970 circlePoint2[i].z = cos(angle);
971 circlePoint3[i].x = cos(angle);
972 circlePoint3[i].y = 0.0;
973 circlePoint3[i].z = sin(angle);
974 }
975
976 QPolygonF polygon1(steps);
977 QPolygonF polygon2(steps);
978 QPolygonF polygon3(steps);
979
980 for (int i = 0; i < steps; i++)
981 {
982 polygon1[i] = CalcPointPersp(circlePoint1[i], mRotInv, persp) * size + center;
983 polygon2[i] = CalcPointPersp(circlePoint2[i], mRotInv, persp) * size + center;
984 polygon3[i] = CalcPointPersp(circlePoint3[i], mRotInv, persp) * size + center;
985 }
986
987 painter.setOpacity(0.5);
988
989 painter.setPen(penRed);
990 painter.drawPolyline(polygon1);
991 painter.setPen(penGreen);
992 painter.drawPolyline(polygon2);
993 painter.setPen(penBlue);
994 painter.drawPolyline(polygon3);
995
996 // Draw arrows
997 QPointF point1, point2;
998 painter.setOpacity(0.5);
999 QFont font = painter.font();
1000 font.setBold(true);
1001 painter.setFont(font);
1002 // X axis
1003 painter.setPen(penMagenta);
1004 point1 = CalcPointPersp(CVector3(1.0, 0.0, 0.0), mRotInv, persp) * size + center;
1005 point2 = CalcPointPersp(CVector3(-1.0, 0.0, 0.0), mRotInv, persp) * size + center;
1006 painter.drawLine(point1, point2);
1007
1008 QStaticText textX("X");
1009 point1 = CalcPointPersp(CVector3(1.2, 0.0, 0.0), mRotInv, persp) * size + center;
1010 point2 =
1011 QPointF(point1.x() - textX.size().width() * 0.5, point1.y() - textX.size().height() * 0.5);
1012 painter.drawStaticText(point2, textX);
1013
1014 point1 = CalcPointPersp(CVector3(0.9, -0.05, 0.0), mRotInv, persp) * size + center;
1015 point2 = CalcPointPersp(CVector3(1.0, 0.0, 0.0), mRotInv, persp) * size + center;
1016 painter.drawLine(point1, point2);
1017 point1 = CalcPointPersp(CVector3(0.9, 0.05, 0.0), mRotInv, persp) * size + center;
1018 point2 = CalcPointPersp(CVector3(1.0, 0.0, 0.0), mRotInv, persp) * size + center;
1019 painter.drawLine(point1, point2);
1020 point1 = CalcPointPersp(CVector3(0.9, 0.0, 0.05), mRotInv, persp) * size + center;
1021 point2 = CalcPointPersp(CVector3(1.0, 0.0, 0.0), mRotInv, persp) * size + center;
1022 painter.drawLine(point1, point2);
1023 point1 = CalcPointPersp(CVector3(0.9, 0.0, -0.05), mRotInv, persp) * size + center;
1024 point2 = CalcPointPersp(CVector3(1.0, 0.0, 0.0), mRotInv, persp) * size + center;
1025 painter.drawLine(point1, point2);
1026
1027 // Z axis
1028 painter.setPen(penCyan);
1029 point1 = CalcPointPersp(CVector3(0.0, 0.0, 1.0), mRotInv, persp) * size + center;
1030 point2 = CalcPointPersp(CVector3(0.0, 0.0, -1.0), mRotInv, persp) * size + center;
1031 painter.drawLine(point1, point2);
1032
1033 QStaticText textZ("Z");
1034 point1 = CalcPointPersp(CVector3(0.0, 0.0, 1.2), mRotInv, persp) * size + center;
1035 point2 =
1036 QPointF(point1.x() - textZ.size().width() * 0.5, point1.y() - textZ.size().height() * 0.5);
1037 painter.drawStaticText(point2, textZ);
1038
1039 point1 = CalcPointPersp(CVector3(0.05, 0.0, 0.9), mRotInv, persp) * size + center;
1040 point2 = CalcPointPersp(CVector3(0.0, 0.0, 1.0), mRotInv, persp) * size + center;
1041 painter.drawLine(point1, point2);
1042 point1 = CalcPointPersp(CVector3(-0.05, 0.0, 0.9), mRotInv, persp) * size + center;
1043 point2 = CalcPointPersp(CVector3(0.0, 0.0, 1.0), mRotInv, persp) * size + center;
1044 painter.drawLine(point1, point2);
1045 point1 = CalcPointPersp(CVector3(0.0, 0.05, 0.9), mRotInv, persp) * size + center;
1046 point2 = CalcPointPersp(CVector3(0.0, 0.0, 1.0), mRotInv, persp) * size + center;
1047 painter.drawLine(point1, point2);
1048 point1 = CalcPointPersp(CVector3(0.0, -0.05, 0.9), mRotInv, persp) * size + center;
1049 point2 = CalcPointPersp(CVector3(0.0, 0.0, 1.0), mRotInv, persp) * size + center;
1050 painter.drawLine(point1, point2);
1051
1052 // Y axis
1053 painter.setPen(penYellow);
1054 point1 = CalcPointPersp(CVector3(0.0, 1.0, 0.0), mRotInv, persp) * size + center;
1055 point2 = CalcPointPersp(CVector3(0.0, -1.0, 0.0), mRotInv, persp) * size + center;
1056 painter.drawLine(point1, point2);
1057
1058 QStaticText textY("Y");
1059 point1 = CalcPointPersp(CVector3(0.0, 1.2, 0.0), mRotInv, persp) * size + center;
1060 point2 =
1061 QPointF(point1.x() - textY.size().width() * 0.5, point1.y() - textY.size().height() * 0.5);
1062 painter.drawStaticText(point2, textY);
1063
1064 point1 = CalcPointPersp(CVector3(0.05, 0.9, 0.0), mRotInv, persp) * size + center;
1065 point2 = CalcPointPersp(CVector3(0.0, 1.0, 0.0), mRotInv, persp) * size + center;
1066 painter.drawLine(point1, point2);
1067 point1 = CalcPointPersp(CVector3(-0.05, 0.9, 0.0), mRotInv, persp) * size + center;
1068 point2 = CalcPointPersp(CVector3(0.0, 1.0, 0.0), mRotInv, persp) * size + center;
1069 painter.drawLine(point1, point2);
1070 point1 = CalcPointPersp(CVector3(0.0, 0.9, 0.05), mRotInv, persp) * size + center;
1071 point2 = CalcPointPersp(CVector3(0.0, 1.0, 0.0), mRotInv, persp) * size + center;
1072 painter.drawLine(point1, point2);
1073 point1 = CalcPointPersp(CVector3(0.0, 0.9, -0.05), mRotInv, persp) * size + center;
1074 point2 = CalcPointPersp(CVector3(0.0, 1.0, 0.0), mRotInv, persp) * size + center;
1075 painter.drawLine(point1, point2);
1076 }
1077
CalcPointPersp(const CVector3 & point,const CRotationMatrix & rot,double persp)1078 QPointF RenderedImage::CalcPointPersp(
1079 const CVector3 &point, const CRotationMatrix &rot, double persp)
1080 {
1081 CVector3 vect;
1082
1083 vect = rot.RotateVector(point);
1084 QPointF out(vect.x / (1.0 + vect.y * persp), -vect.z / (1.0 + vect.y * persp));
1085 return out;
1086 }
1087
setClickMode(QList<QVariant> _clickMode)1088 void RenderedImage::setClickMode(QList<QVariant> _clickMode)
1089 {
1090 if (_clickMode.size() > 0)
1091 clickModeData = _clickMode;
1092 else
1093 qWarning() << "_clickMode cannot be empty!";
1094 }
1095
slotSetMinimumSize(int width,int height)1096 void RenderedImage::slotSetMinimumSize(int width, int height)
1097 {
1098 setMinimumSize(width, height);
1099 }
1100
SetGridType(enumGridType _gridType)1101 void RenderedImage::SetGridType(enumGridType _gridType)
1102 {
1103 gridType = _gridType;
1104 update();
1105 }
1106
SetAnimationPath(const sAnimationPathData & _animationPath)1107 void RenderedImage::SetAnimationPath(const sAnimationPathData &_animationPath)
1108 {
1109 animationPathData = _animationPath;
1110 }
1111
DrawAnimationPath()1112 void RenderedImage::DrawAnimationPath()
1113 {
1114 int numberOfKeyframes = animationPathData.numberOfKeyframes;
1115 int numberOfFrames = animationPathData.numberOfFrames;
1116
1117 CVector3 camera = params->Get<CVector3>("camera");
1118 CVector3 rotation = params->Get<CVector3>("camera_rotation");
1119 params::enumPerspectiveType perspectiveType =
1120 static_cast<params::enumPerspectiveType>(params->Get<int>("perspective_type"));
1121 double fov = CalcFOV(params->Get<double>("fov"), perspectiveType);
1122 int width = image->GetPreviewWidth();
1123 int height = image->GetPreviewHeight();
1124
1125 CRotationMatrix mRotInv;
1126 mRotInv.RotateY(-rotation.z / 180.0 * M_PI);
1127 mRotInv.RotateX(-rotation.y / 180.0 * M_PI);
1128 mRotInv.RotateZ(-rotation.x / 180.0 * M_PI);
1129
1130 int frameIndex = 0;
1131 for (int key = 0; key < numberOfKeyframes; key++)
1132 {
1133 int numberOfSubframes = animationPathData.framesPeyKey.at(key);
1134
1135 for (int subframe = 0; subframe < numberOfSubframes; subframe++)
1136 {
1137 if (frameIndex >= numberOfFrames - 1) break;
1138
1139 CVector3 pointTarget1, pointTarget2, pointCamera1, pointCamera2;
1140
1141 double percent = double(frameIndex) / numberOfFrames;
1142
1143 if (animationPathData.targetPathEnable)
1144 {
1145 CVector3 target1 = animationPathData.animationPath[frameIndex].target;
1146 CVector3 target2 = animationPathData.animationPath[frameIndex + 1].target;
1147
1148 pointTarget1 =
1149 InvProjection3D(target1, camera, mRotInv, perspectiveType, fov, width, height);
1150 pointTarget2 =
1151 InvProjection3D(target2, camera, mRotInv, perspectiveType, fov, width, height);
1152
1153 sRGB8 color(255 - percent * 128, 0, percent * 255);
1154 if (pointTarget1.z > 0)
1155 {
1156 if (subframe == 0)
1157 {
1158 image->CircleBorder(pointTarget1.x, pointTarget1.y, pointTarget1.z, 5.0, color, 2.0,
1159 sRGBFloat(1.0, 1.0, 1.0), 1);
1160 }
1161 }
1162 if (pointTarget1.z > 0 && pointTarget2.z > 0)
1163 {
1164 image->AntiAliasedLine(pointTarget1.x, pointTarget1.y, pointTarget2.x, pointTarget2.y,
1165 pointTarget1.z, pointTarget2.z, color, sRGBFloat(1.0, 1.0, 1.0), 1.0f, 1);
1166 }
1167 }
1168
1169 if (animationPathData.cameraPathEnable)
1170 {
1171 CVector3 camera1 = animationPathData.animationPath[frameIndex].camera;
1172 CVector3 camera2 = animationPathData.animationPath[frameIndex + 1].camera;
1173 pointCamera1 =
1174 InvProjection3D(camera1, camera, mRotInv, perspectiveType, fov, width, height);
1175 pointCamera2 =
1176 InvProjection3D(camera2, camera, mRotInv, perspectiveType, fov, width, height);
1177
1178 sRGB8 color(0, 255 - percent * 128, percent * 255);
1179 if (pointCamera1.z > 0)
1180 {
1181 if (subframe == 0)
1182 {
1183 image->CircleBorder(pointCamera1.x, pointCamera1.y, pointCamera1.z, 5.0, color, 2.0,
1184 sRGBFloat(1.0, 1.0, 1.0), 1);
1185 }
1186 }
1187
1188 if (pointCamera1.z > 0 && pointCamera2.z > 0)
1189 {
1190 image->AntiAliasedLine(pointCamera1.x, pointCamera1.y, pointCamera2.x, pointCamera2.y,
1191 pointCamera1.z, pointCamera2.z, color, sRGBFloat(1.0, 1.0, 1.0), 1.0f, 1);
1192 }
1193 }
1194
1195 if (animationPathData.cameraPathEnable && animationPathData.targetPathEnable)
1196 {
1197 if (pointCamera1.z > 0 && pointTarget1.z > 0)
1198 {
1199 if (subframe % (numberOfSubframes / 4) == 0)
1200 {
1201 image->AntiAliasedLine(pointCamera1.x, pointCamera1.y, pointTarget1.x, pointTarget1.y,
1202 pointCamera1.z, pointTarget1.z, sRGB8(255, 255, 0), sRGBFloat(0.2f, 0.2f, 0.2f), 1.0f,
1203 1);
1204 }
1205 }
1206 }
1207
1208 // lights
1209 for (int i = 0; i < 4; i++)
1210 {
1211 if (animationPathData.lightPathEnable[i])
1212 {
1213 CVector3 target1 = animationPathData.animationPath[frameIndex].lights[i];
1214 CVector3 target2 = animationPathData.animationPath[frameIndex + 1].lights[i];
1215
1216 pointTarget1 =
1217 InvProjection3D(target1, camera, mRotInv, perspectiveType, fov, width, height);
1218 pointTarget2 =
1219 InvProjection3D(target2, camera, mRotInv, perspectiveType, fov, width, height);
1220
1221 sRGB8 color = animationPathData.animationPath[frameIndex].lightColor[i];
1222
1223 if (subframe % 8 >= 4)
1224 {
1225 color.R = 255 - color.R;
1226 color.G = 255 - color.G;
1227 color.B = 255 - color.B;
1228 }
1229 if (pointTarget1.z > 0)
1230 {
1231 if (subframe == 0)
1232 {
1233 image->CircleBorder(pointTarget1.x, pointTarget1.y, pointTarget1.z, 5.0, color, 2.0,
1234 sRGBFloat(1.0, 1.0, 1.0), 1);
1235 }
1236 }
1237 if (pointTarget1.z > 0 && pointTarget2.z > 0)
1238 {
1239 image->AntiAliasedLine(pointTarget1.x, pointTarget1.y, pointTarget2.x, pointTarget2.y,
1240 pointTarget1.z, pointTarget2.z, color, sRGBFloat(1.0, 1.0, 1.0), 1.0f, 1);
1241 }
1242 }
1243 }
1244
1245 frameIndex++;
1246 }
1247 }
1248 }
1249
showRenderedTilesList(QList<sRenderedTileData> listOfRenderedTiles)1250 void RenderedImage::showRenderedTilesList(QList<sRenderedTileData> listOfRenderedTiles)
1251 {
1252 listOfRenderedTilesData.append(listOfRenderedTiles);
1253 }
1254
PaintLastRenderedTilesInfo()1255 void RenderedImage::PaintLastRenderedTilesInfo()
1256 {
1257 QPainter painter(this);
1258 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
1259
1260 QPen penRed(Qt::red, 1.0, Qt::SolidLine);
1261 QPen penGreen(Qt::green, 1.0, Qt::SolidLine);
1262 painter.setOpacity(0.5);
1263
1264 QList<QPair<int, int>> listOfPaintedTiles;
1265
1266 for (sRenderedTileData &tile : listOfRenderedTilesData)
1267 {
1268 if (!listOfPaintedTiles.contains(QPair<int, int>(tile.x, tile.y)))
1269 {
1270 listOfPaintedTiles.append(QPair<int, int>(tile.x, tile.y));
1271
1272 QRect r(tile.x * image->GetPreviewScale(), tile.y * image->GetPreviewScale(),
1273 tile.width * image->GetPreviewScale(), tile.height * image->GetPreviewScale());
1274
1275 painter.setOpacity(0.5);
1276 painter.setPen(penRed);
1277
1278 painter.drawLine(r.x(), r.y(), r.x() + r.width() / 4, r.y());
1279 painter.drawLine(r.x(), r.y(), r.x(), r.y() + r.height() / 4);
1280 painter.drawLine(r.right(), r.bottom(), r.right() - r.width() / 4, r.bottom());
1281 painter.drawLine(r.right(), r.bottom(), r.right(), r.bottom() - r.height() / 4);
1282
1283 painter.setPen(penGreen);
1284
1285 painter.drawLine(r.right(), r.y(), r.right() - r.width() / 4, r.y());
1286 painter.drawLine(r.right(), r.y(), r.right(), r.y() + r.height() / 4);
1287 painter.drawLine(r.left(), r.bottom(), r.left() + r.width() / 4, r.bottom());
1288 painter.drawLine(r.left(), r.bottom(), r.left(), r.bottom() - r.height() / 4);
1289
1290 QPoint center = r.center();
1291
1292 painter.setOpacity(1.0);
1293 if (tile.noiseLevel > 0)
1294 {
1295 QStaticText text(QString("%1").arg(tile.noiseLevel * 100.0, 0, 'f', 1));
1296 QPointF point =
1297 QPointF(center.x() - text.size().width() * 0.5, center.y() - text.size().height() * 0.5);
1298 painter.drawStaticText(point, text);
1299 }
1300 }
1301 }
1302 listOfRenderedTilesData.clear();
1303 }
1304
DisplayAllLights()1305 void RenderedImage::DisplayAllLights()
1306 {
1307 CVector3 camera = params->Get<CVector3>("camera");
1308 CVector3 rotation = params->Get<CVector3>("camera_rotation");
1309 params::enumPerspectiveType perspectiveType =
1310 static_cast<params::enumPerspectiveType>(params->Get<int>("perspective_type"));
1311 double fov = CalcFOV(params->Get<double>("fov"), perspectiveType);
1312 int width = image->GetPreviewWidth();
1313 int height = image->GetPreviewHeight();
1314
1315 CRotationMatrix mRotInv;
1316 mRotInv.RotateY(-rotation.z / 180.0 * M_PI);
1317 mRotInv.RotateX(-rotation.y / 180.0 * M_PI);
1318 mRotInv.RotateZ(-rotation.x / 180.0 * M_PI);
1319
1320 QList<QString> listOfParameters = params->GetListOfParameters();
1321 for (auto ¶meterName : listOfParameters)
1322 {
1323 const int lengthOfPrefix = 5;
1324 if (parameterName.leftRef(lengthOfPrefix) == "light")
1325 {
1326 int positionOfDash = parameterName.indexOf('_');
1327 int lightIndex =
1328 parameterName.midRef(lengthOfPrefix, positionOfDash - lengthOfPrefix).toInt();
1329 if (parameterName.midRef(positionOfDash + 1) == "is_defined")
1330 {
1331 const cLight light(lightIndex, params, false, true, false);
1332
1333 if (light.enabled)
1334 {
1335 bool bold = lightIndex == currentLightIndex;
1336 double thickness = (bold) ? 2.0 : 1.0;
1337
1338 if (light.type != cLight::lightDirectional)
1339 {
1340 CVector3 lightCenter =
1341 InvProjection3D(light.position, camera, mRotInv, perspectiveType, fov, width, height);
1342
1343 sRGB8 color = toRGB8(light.color);
1344
1345 image->CircleBorder(lightCenter.x, lightCenter.y, lightCenter.z, 10.0, color,
1346 thickness * 4.0, sRGBFloat(1.0, 1.0, 1.0), 1);
1347
1348 if (light.type == cLight::lightPoint)
1349 {
1350 double visibleSize =
1351 sqrt(light.intensity) * light.size / lightCenter.z / fov * height;
1352
1353 image->CircleBorder(lightCenter.x, lightCenter.y, lightCenter.z, visibleSize, color,
1354 thickness * 2.0, sRGBFloat(0.5, 0.5, 0.5), 1);
1355
1356 double sizeFactor = sqrt(light.intensity) * light.size * 20.0;
1357
1358 line3D(light.position - CVector3(sizeFactor, 0.0, 0.0),
1359 light.position + CVector3(sizeFactor, 0.0, 0.0), camera, mRotInv, perspectiveType,
1360 fov, width, height, color, thickness, sRGBFloat(0.7, 0.7, 0.7), 20, 1);
1361
1362 line3D(light.position - CVector3(0.0, sizeFactor, 0.0),
1363 light.position + CVector3(0.0, sizeFactor, 0.0), camera, mRotInv, perspectiveType,
1364 fov, width, height, color, thickness, sRGBFloat(0.7, 0.7, 0.7), 20, 1);
1365
1366 line3D(light.position - CVector3(0.0, 0.0, sizeFactor),
1367 light.position + CVector3(0.0, 0.0, sizeFactor), camera, mRotInv, perspectiveType,
1368 fov, width, height, color, thickness, sRGBFloat(0.7, 0.7, 0.7), 20, 1);
1369 }
1370
1371 if (light.type == cLight::lightConical)
1372 {
1373 double sizeFactor = sqrt(light.intensity) * light.size * 2.0;
1374 for (int s = 0; s < 2; s++)
1375 {
1376
1377 double coneRatio =
1378 sin((s == 0) ? light.coneAngle : light.coneAngle + light.coneSoftAngle);
1379
1380 sRGBFloat opacity =
1381 ((s == 0) ? sRGBFloat(0.7, 0.7, 0.7) : sRGBFloat(0.2, 0.2, 0.2));
1382
1383 for (int i = 0; i < 8; i++)
1384 {
1385 double r1 = (i + 1) * light.size * coneRatio * sizeFactor;
1386 double r2 = i * light.size * coneRatio * sizeFactor;
1387
1388 CVector3 previousPoint;
1389
1390 for (int j = 0; j <= 16; j++)
1391 {
1392 double angle = j / 16.0 * 2.0 * M_PI;
1393
1394 CVector3 dx1 = r1 * cos(angle) * light.lightRightVector;
1395 CVector3 dy1 = r1 * sin(angle) * light.lightTopVector;
1396 CVector3 dz1 =
1397 (-1.0) * (light.size * (i + 1) * sizeFactor) * light.lightDirection;
1398
1399 CVector3 point1 = light.position + dx1 + dy1 + dz1;
1400
1401 // draw lines
1402 if (j % 4 == 0)
1403 {
1404 CVector3 dx2 = r2 * cos(angle) * light.lightRightVector;
1405 CVector3 dy2 = r2 * sin(angle) * light.lightTopVector;
1406 CVector3 dz2 = (-1.0) * (light.size * i * sizeFactor) * light.lightDirection;
1407
1408 CVector3 point2 = light.position + dx2 + dy2 + dz2;
1409
1410 line3D(point1, point2, camera, mRotInv, perspectiveType, fov, width, height,
1411 color, thickness, opacity, 10, 1);
1412 }
1413
1414 // draw circles
1415 if (j > 0)
1416 {
1417 line3D(point1, previousPoint, camera, mRotInv, perspectiveType, fov, width,
1418 height, color, thickness, opacity, 10, 1);
1419 }
1420
1421 previousPoint = point1;
1422 } // for j
1423 } // for i
1424 } // for s
1425 } // if conical
1426
1427 if (light.type == cLight::lightProjection)
1428 {
1429 double sizeFactor = sqrt(light.intensity) * light.size * 2.0;
1430
1431 for (int i = 1; i <= 8; i++)
1432 {
1433 double w = i * light.size * light.projectionHorizontalRatio * sizeFactor * 0.5;
1434 double h = i * light.size * light.projectionVerticalRatio * sizeFactor * 0.5;
1435
1436 CVector3 dx = w * light.lightRightVector;
1437 CVector3 dy = h * light.lightTopVector;
1438 CVector3 dz = (-1.0) * light.size * i * sizeFactor * light.lightDirection;
1439
1440 CVector3 point1 = light.position + dx + dy + dz;
1441 CVector3 point2 = light.position - dx + dy + dz;
1442 CVector3 point3 = light.position - dx - dy + dz;
1443 CVector3 point4 = light.position + dx - dy + dz;
1444
1445 line3D(point1, point2, camera, mRotInv, perspectiveType, fov, width, height, color,
1446 thickness, sRGBFloat(0.7, 0.7, 0.7), 10, 1);
1447 line3D(point2, point3, camera, mRotInv, perspectiveType, fov, width, height, color,
1448 thickness, sRGBFloat(0.7, 0.7, 0.7), 10, 1);
1449 line3D(point3, point4, camera, mRotInv, perspectiveType, fov, width, height, color,
1450 thickness, sRGBFloat(0.7, 0.7, 0.7), 10, 1);
1451 line3D(point4, point1, camera, mRotInv, perspectiveType, fov, width, height, color,
1452 thickness, sRGBFloat(0.7, 0.7, 0.7), 10, 1);
1453
1454 if (i == 8)
1455 {
1456 line3D(point1, light.position, camera, mRotInv, perspectiveType, fov, width,
1457 height, color, thickness, sRGBFloat(0.7, 0.7, 0.7), 100, 1);
1458 line3D(point2, light.position, camera, mRotInv, perspectiveType, fov, width,
1459 height, color, thickness, sRGBFloat(0.7, 0.7, 0.7), 100, 1);
1460 line3D(point3, light.position, camera, mRotInv, perspectiveType, fov, width,
1461 height, color, thickness, sRGBFloat(0.7, 0.7, 0.7), 100, 1);
1462 line3D(point4, light.position, camera, mRotInv, perspectiveType, fov, width,
1463 height, color, thickness, sRGBFloat(0.7, 0.7, 0.7), 100, 1);
1464 }
1465
1466 } // for i
1467 } // if projection
1468 } // if not directional
1469 } // if enabled
1470 } // if is defined
1471 } // if parameter is light
1472 } // for parameterName
1473 }
1474
line3D(const CVector3 & p1,const CVector3 & p2,const CVector3 camera,const CRotationMatrix & mRotInv,params::enumPerspectiveType perspectiveType,double fov,double imgWidth,double imgHeight,sRGB8 color,double thickness,sRGBFloat opacity,int numberOfSegments,int layer)1475 void RenderedImage::line3D(const CVector3 &p1, const CVector3 &p2, const CVector3 camera,
1476 const CRotationMatrix &mRotInv, params::enumPerspectiveType perspectiveType, double fov,
1477 double imgWidth, double imgHeight, sRGB8 color, double thickness, sRGBFloat opacity,
1478 int numberOfSegments, int layer)
1479 {
1480 for (int i = 0; i < numberOfSegments; i++)
1481 {
1482 double k1 = double(i) / numberOfSegments;
1483 double kn1 = 1.0 - k1;
1484
1485 double k2 = double(i + 1) / numberOfSegments;
1486 double kn2 = 1.0 - k2;
1487
1488 CVector3 p1Projected = InvProjection3D(
1489 p1 * kn1 + p2 * k1, camera, mRotInv, perspectiveType, fov, imgWidth, imgHeight);
1490
1491 CVector3 p2Projected = InvProjection3D(
1492 p1 * kn2 + p2 * k2, camera, mRotInv, perspectiveType, fov, imgWidth, imgHeight);
1493
1494 image->AntiAliasedLine(p1Projected.x, p1Projected.y, p2Projected.x, p2Projected.y,
1495 p1Projected.z, p2Projected.z, color, opacity, thickness, layer);
1496 }
1497 }
1498