1 /**
2 * Mandelbulber v2, a 3D fractal generator ,=#MKNmMMKmmßMNWy,
3 * ,B" ]L,,p%%%,,,§;, "K
4 * Copyright (C) 2021 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 * TODO: description
33 */
34
35 #include "light_widget.h"
36
37 #include <QDebug>
38 #include <QEvent>
39 #include <QImage>
40 #include <QPaintEvent>
41 #include <QPainter>
42
43 #include "src/common_math.h"
44 #include "src/system_data.hpp"
45
cLightWidget(QWidget * parent)46 cLightWidget::cLightWidget(QWidget *parent) : QWidget(parent)
47 {
48 size = systemData.GetPreferredThumbnailSize() * 0.7;
49 // SetLightAngle(CVector3(0.0, 0.5, 0.5), sRGBFloat(0.9, 0.4, 0.1));
50 SetLightColor(sRGBFloat(0.9, 0.4, 0.1));
51 setMinimumWidth(size);
52 setMinimumHeight(size);
53 updateGeometry();
54 }
55
SetLightAngle(CVector3 _angle)56 void cLightWidget::SetLightAngle(CVector3 _angle)
57 {
58 lightAngle = _angle;
59 Render();
60 }
61
SetLightColor(sRGBFloat _color)62 void cLightWidget::SetLightColor(sRGBFloat _color)
63 {
64 lightColor = _color;
65 Render();
66 }
67
SetCameraTarget(CVector3 camera,CVector3 target,CVector3 top)68 void cLightWidget::SetCameraTarget(CVector3 camera, CVector3 target, CVector3 top)
69 {
70 cameraTarget = cCameraTarget(camera, target, top);
71 Render();
72 }
73
SetRelativeMode(bool _relativeRotationMode)74 void cLightWidget::SetRelativeMode(bool _relativeRotationMode)
75 {
76 relativeRotationMode = _relativeRotationMode;
77 Render();
78 }
79
paintEvent(QPaintEvent * event)80 void cLightWidget::paintEvent(QPaintEvent *event)
81 {
82 if (image8.size() > 0)
83 {
84 event->accept();
85
86 QPainter painter(this);
87 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
88
89 QImage qImage(reinterpret_cast<const quint8 *>(image8.data()), size, size,
90 int(size * sizeof(sRGBA8)), QImage::Format_RGBA8888);
91 painter.drawImage(QRect(0, 0, size, size), qImage, QRect(0, 0, size, size));
92 }
93 }
94
Render()95 void cLightWidget::Render()
96 {
97 image8.resize(size * size);
98
99 double center = double(size) / 2.0;
100 double radius = center * 0.9;
101
102 CRotationMatrix rotMatrix;
103
104 if (!relativeRotationMode)
105 {
106 CVector3 cameraRotation = cameraTarget.GetRotation();
107 // inverse rotation matrix for camera
108 rotMatrix.RotateY(-cameraRotation.z);
109 rotMatrix.RotateX(cameraRotation.y);
110 rotMatrix.RotateZ(cameraRotation.x);
111 }
112
113 rotMatrix.RotateZ(lightAngle.x);
114 rotMatrix.RotateX(lightAngle.y);
115 rotMatrix.RotateY(lightAngle.z);
116
117 CVector3 lightDirection = rotMatrix.RotateVector(CVector3(0.0, 1.0, 0.0));
118
119 for (int y = 0; y < size; y++)
120 {
121 for (int x = 0; x < size; x++)
122 {
123 CVector3 sphereVect;
124 sphereVect.x = double(x) - center;
125 sphereVect.z = -(double(y) - center);
126 double r2D = sqrt(sphereVect.x * sphereVect.x + sphereVect.z * sphereVect.z);
127
128 sRGBFloat color;
129 int opacity = 0;
130 if (r2D < radius)
131 {
132 sphereVect.y = sqrt(radius * radius - r2D * r2D);
133
134 sphereVect.Normalize();
135
136 // shading
137 double shade = sphereVect.Dot(lightDirection);
138 shade = shade + 0.4;
139 if (shade < 0.4)
140 {
141 shade = 0.0;
142 }
143 shade *= 0.7;
144
145 // specular highlight
146 CVector3 halfVector = lightDirection - CVector3(0.0, -1.0, 0.0);
147 halfVector.Normalize();
148 double shade2 = sphereVect.Dot(halfVector);
149 if (shade2 < 0.0f) shade2 = 0.0f;
150 double specular = pow(shade2, 60.0) * 1.5;
151
152 color.R = clamp((shade + specular) * lightColor.R, 0.0, 1.0);
153 color.G = clamp((shade + specular) * lightColor.G, 0.0, 1.0);
154 color.B = clamp((shade + specular) * lightColor.B, 0.0, 1.0);
155
156 // antialiasing
157 opacity = clamp(radius - r2D, 0.0, 1.0) * 255;
158 }
159 else
160 {
161 color = {0.5, 0.5, 0.5};
162 opacity = 0;
163 }
164
165 int address = y * size + x;
166
167 if (!isEnabled())
168 {
169 if (opacity > 30) opacity = 30;
170 }
171 image8[address] = sRGBA8(color.R * 255, color.G * 255, color.B * 255, opacity);
172 }
173 }
174 update();
175 }
176
mousePressEvent(QMouseEvent * event)177 void cLightWidget::mousePressEvent(QMouseEvent *event)
178 {
179 if (event->button() == Qt::LeftButton)
180 {
181 dragStartPosition = CVector2<int>(event->x(), event->y());
182 draggingInitStarted = true;
183 lightAngleBeforeDrag = lightAngle;
184 }
185 }
186
mouseReleaseEvent(QMouseEvent * event)187 void cLightWidget::mouseReleaseEvent(QMouseEvent *event)
188 {
189 Q_UNUSED(event);
190 draggingStarted = false;
191 draggingInitStarted = false;
192 emit angleChanged(lightAngle.x, -lightAngle.y);
193 }
194
mouseMoveEvent(QMouseEvent * event)195 void cLightWidget::mouseMoveEvent(QMouseEvent *event)
196 {
197 CVector2<int> screenPoint(event->x(), event->y());
198
199 if (abs(screenPoint.x - dragStartPosition.x) > 1 || abs(screenPoint.y - dragStartPosition.y) > 1)
200 {
201 draggingInitStarted = false;
202 draggingStarted = true;
203 }
204
205 if (draggingStarted)
206 {
207 double dx = double(screenPoint.x - dragStartPosition.x) / size;
208 double dy = double(screenPoint.y - dragStartPosition.y) / size;
209
210 if (relativeRotationMode)
211 {
212 double alpha = lightAngleBeforeDrag.x - dx * 1.5;
213 double beta = lightAngleBeforeDrag.y - dy * 1.5;
214
215 beta = clamp(beta, -M_PI * 0.5, M_PI * 0.5);
216
217 lightAngle = CVector3(alpha, beta, 0.0);
218 }
219 else
220 {
221 CVector3 cameraRotation = cameraTarget.GetRotation();
222 // inverse rotation matrix for camera
223 CRotationMatrix dragRotMatrix;
224
225 dragRotMatrix.RotateZ(-cameraRotation.x);
226 dragRotMatrix.RotateX(-cameraRotation.y);
227 dragRotMatrix.RotateY(cameraRotation.z);
228
229 dragRotMatrix.RotateZ(-dx * 1.5);
230 dragRotMatrix.RotateX(-dy * 1.5);
231
232 dragRotMatrix.RotateY(-cameraRotation.z);
233 dragRotMatrix.RotateX(cameraRotation.y);
234 dragRotMatrix.RotateZ(cameraRotation.x);
235
236 CRotationMatrix rotMatrix;
237 rotMatrix.RotateZ(lightAngleBeforeDrag.x);
238 rotMatrix.RotateX(lightAngleBeforeDrag.y);
239 rotMatrix.RotateY(lightAngleBeforeDrag.z);
240 CVector3 lightDirection = rotMatrix.RotateVector(CVector3(0.0, 1.0, 0.0));
241
242 lightDirection = dragRotMatrix.RotateVector(lightDirection);
243
244 lightAngle.x = -atan2(lightDirection.x, lightDirection.y);
245 lightAngle.y = atan2(lightDirection.z,
246 sqrt(lightDirection.x * lightDirection.x + lightDirection.y * lightDirection.y));
247
248 // CVector3 lightDirectionTemp = cameraInvRotMatrix.RotateVector(ligh)
249 }
250 Render();
251 }
252 }
253
changeEvent(QEvent * event)254 void cLightWidget::changeEvent(QEvent *event)
255 {
256 if (event->type() == QEvent::EnabledChange)
257 {
258 Render();
259 }
260 QWidget::changeEvent(event);
261 }
262