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