1 #include "lc_global.h"
2 #include "lc_viewsphere.h"
3 #include "lc_view.h"
4 #include "camera.h"
5 #include "lc_context.h"
6 #include "lc_stringcache.h"
7 #include "lc_application.h"
8 #include "image.h"
9 #include "lc_texture.h"
10 
11 lcTexture* lcViewSphere::mTexture;
12 lcVertexBuffer lcViewSphere::mVertexBuffer;
13 lcIndexBuffer lcViewSphere::mIndexBuffer;
14 const float lcViewSphere::mRadius = 1.0f;
15 const float lcViewSphere::mHighlightRadius = 0.35f;
16 const int lcViewSphere::mSubdivisions = 7;
17 
lcViewSphere(lcView * View)18 lcViewSphere::lcViewSphere(lcView* View)
19 	: mView(View)
20 {
21 	UpdateSettings();
22 }
23 
UpdateSettings()24 void lcViewSphere::UpdateSettings()
25 {
26 	const lcPreferences& Preferences = lcGetPreferences();
27 
28 	switch (mView->GetViewType())
29 	{
30 	case lcViewType::View:
31 		mSize = Preferences.mViewSphereSize;
32 		mEnabled = Preferences.mViewSphereEnabled;
33 		mLocation = Preferences.mViewSphereLocation;
34 		break;
35 
36 	case lcViewType::Preview:
37 		mSize = Preferences.mPreviewViewSphereSize;
38 		mEnabled = Preferences.mPreviewViewSphereEnabled;
39 		mLocation = Preferences.mPreviewViewSphereLocation;
40 		break;
41 
42 	case lcViewType::Minifig:
43 	case lcViewType::PartsList:
44 	case lcViewType::Count:
45 		break;
46 	}
47 }
48 
GetViewMatrix() const49 lcMatrix44 lcViewSphere::GetViewMatrix() const
50 {
51 	lcMatrix44 ViewMatrix = mView->GetCamera()->mWorldView;
52 	ViewMatrix.SetTranslation(lcVector3(0, 0, 0));
53 	return ViewMatrix;
54 }
55 
GetProjectionMatrix() const56 lcMatrix44 lcViewSphere::GetProjectionMatrix() const
57 {
58 	return lcMatrix44Ortho(-mRadius * 1.25f, mRadius * 1.25f, -mRadius * 1.25f, mRadius * 1.25f, -mRadius * 1.25f, mRadius * 1.25f);
59 }
60 
CreateResources(lcContext * Context)61 void lcViewSphere::CreateResources(lcContext* Context)
62 {
63 	const int ImageSize = 128;
64 	mTexture = new lcTexture();
65 
66 	const QString ViewNames[6] =
67 	{
68 		QCoreApplication::translate("ViewName", "Left"), QCoreApplication::translate("ViewName", "Right"), QCoreApplication::translate("ViewName", "Back"),
69 		QCoreApplication::translate("ViewName", "Front"), QCoreApplication::translate("ViewName", "Top"), QCoreApplication::translate("ViewName", "Bottom")
70 	};
71 
72 	const QTransform ViewTransforms[6] =
73 	{
74 		QTransform(0, 1, 1, 0, 0, 0), QTransform(0, -1, -1, 0, ImageSize, ImageSize), QTransform(-1, 0, 0, 1, ImageSize, 0),
75 		QTransform(1, 0, 0, -1, 0, ImageSize), QTransform(1, 0, 0, -1, 0, ImageSize), QTransform(-1, 0, 0, 1, ImageSize, 0)
76 	};
77 
78 	QImage PainterImage(ImageSize, ImageSize, QImage::Format_ARGB32);
79 	QPainter Painter;
80 	QFont Font("Helvetica", 20);
81 	std::vector<Image> Images;
82 
83 	for (int ViewIdx = 0; ViewIdx < 6; ViewIdx++)
84 	{
85 		Image TextureImage;
86 		TextureImage.Allocate(ImageSize, ImageSize, LC_PIXEL_FORMAT_A8);
87 
88 		Painter.begin(&PainterImage);
89 		Painter.fillRect(0, 0, PainterImage.width(), PainterImage.height(), QColor(0, 0, 0));
90 		Painter.setBrush(QColor(255, 255, 255));
91 		Painter.setPen(QColor(255, 255, 255));
92 		Painter.setFont(Font);
93 		Painter.setTransform(ViewTransforms[ViewIdx]);
94 		Painter.drawText(0, 0, PainterImage.width(), PainterImage.height(), Qt::AlignCenter, ViewNames[ViewIdx]);
95 		Painter.end();
96 
97 		for (int y = 0; y < ImageSize; y++)
98 		{
99 			unsigned char* Dest = TextureImage.mData + (ImageSize - y - 1) * TextureImage.mWidth;
100 
101 			for (int x = 0; x < ImageSize; x++)
102 				*Dest++ = qRed(PainterImage.pixel(x, y));
103 		}
104 
105 		Images.emplace_back(std::move(TextureImage));
106 	}
107 
108 	mTexture->SetImage(std::move(Images), LC_TEXTURE_CUBEMAP | LC_TEXTURE_LINEAR);
109 
110 	lcVector3 Verts[(mSubdivisions + 1) * (mSubdivisions + 1) * 6];
111 	GLushort Indices[mSubdivisions * mSubdivisions * 6 * 6];
112 
113 	lcMatrix44 Transforms[6] =
114 	{
115 		lcMatrix44(lcVector4(0.0f,  1.0f, 0.0f, 0.0f), lcVector4(0.0f,  0.0f, 1.0f, 0.0f), lcVector4(1.0f, 0.0f, 0.0f, 0.0f), lcVector4(1.0f,  0.0f,  0.0f, 1.0f)),
116 		lcMatrix44(lcVector4(0.0f, -1.0f, 0.0f, 0.0f), lcVector4(0.0f,  0.0f, 1.0f, 0.0f), lcVector4(1.0f, 0.0f, 0.0f, 0.0f), lcVector4(-1.0f,  0.0f,  0.0f, 1.0f)),
117 		lcMatrix44(lcVector4(-1.0f,  0.0f, 0.0f, 0.0f), lcVector4(0.0f,  0.0f, 1.0f, 0.0f), lcVector4(0.0f, 1.0f, 0.0f, 0.0f), lcVector4(0.0f,  1.0f,  0.0f, 1.0f)),
118 		lcMatrix44(lcVector4(1.0f,  0.0f, 0.0f, 0.0f), lcVector4(0.0f,  0.0f, 1.0f, 0.0f), lcVector4(0.0f, 1.0f, 0.0f, 0.0f), lcVector4(0.0f, -1.0f,  0.0f, 1.0f)),
119 		lcMatrix44(lcVector4(1.0f,  0.0f, 0.0f, 0.0f), lcVector4(0.0f,  1.0f, 0.0f, 0.0f), lcVector4(0.0f, 0.0f, 1.0f, 0.0f), lcVector4(0.0f,  0.0f,  1.0f, 1.0f)),
120 		lcMatrix44(lcVector4(1.0f,  0.0f, 0.0f, 0.0f), lcVector4(0.0f, -1.0f, 0.0f, 0.0f), lcVector4(0.0f, 0.0f, 1.0f, 0.0f), lcVector4(0.0f,  0.0f, -1.0f, 1.0f)),
121 	};
122 
123 	const float Step = 2.0f / mSubdivisions;
124 	lcVector3* CurVert = Verts;
125 
126 	for (int FaceIdx = 0; FaceIdx < 6; FaceIdx++)
127 	{
128 		for (int y = 0; y <= mSubdivisions; y++)
129 		{
130 			for (int x = 0; x <= mSubdivisions; x++)
131 			{
132 				lcVector3 Vert = lcMul31(lcVector3(Step * x - 1.0f, Step * y - 1.0f, 0.0f), Transforms[FaceIdx]);
133 				lcVector3 Vert2 = Vert * Vert;
134 
135 				*CurVert++ = lcVector3(Vert.x * sqrt(1.0 - 0.5 * (Vert2.y + Vert2.z) + Vert2.y * Vert2.z / 3.0),
136 									   Vert.y * sqrt(1.0 - 0.5 * (Vert2.z + Vert2.x) + Vert2.z * Vert2.x / 3.0),
137 									   Vert.z * sqrt(1.0 - 0.5 * (Vert2.x + Vert2.y) + Vert2.x * Vert2.y / 3.0)
138 				) * mRadius;
139 			}
140 		}
141 	}
142 
143 	GLushort* CurIndex = Indices;
144 
145 	for (int FaceIdx = 0; FaceIdx < 6; FaceIdx++)
146 	{
147 		const int FaceBase = FaceIdx * (mSubdivisions + 1) * (mSubdivisions + 1);
148 
149 		for (int y = 0; y < mSubdivisions; y++)
150 		{
151 			int RowBase = FaceBase + (mSubdivisions + 1) * y;
152 
153 			for (int x = 0; x < mSubdivisions; x++)
154 			{
155 				*CurIndex++ = RowBase + x;
156 				*CurIndex++ = RowBase + x + 1;
157 				*CurIndex++ = RowBase + x + (mSubdivisions + 1);
158 
159 				*CurIndex++ = RowBase + x + 1;
160 				*CurIndex++ = RowBase + x + 1 + (mSubdivisions + 1);
161 				*CurIndex++ = RowBase + x + (mSubdivisions + 1);
162 			}
163 		}
164 	}
165 
166 	mVertexBuffer = Context->CreateVertexBuffer(sizeof(Verts), Verts);
167 	mIndexBuffer = Context->CreateIndexBuffer(sizeof(Indices), Indices);
168 }
169 
DestroyResources(lcContext * Context)170 void lcViewSphere::DestroyResources(lcContext* Context)
171 {
172 	delete mTexture;
173 	mTexture = nullptr;
174 	Context->DestroyVertexBuffer(mVertexBuffer);
175 	Context->DestroyIndexBuffer(mIndexBuffer);
176 }
177 
Draw()178 void lcViewSphere::Draw()
179 {
180 	UpdateSettings();
181 
182 	if (!mSize || !mEnabled)
183 		return;
184 
185 	lcContext* Context = mView->mContext;
186 	const int Width = mView->GetWidth();
187 	const int Height = mView->GetHeight();
188 	const int ViewportSize = mSize;
189 	const int Left = (mLocation == lcViewSphereLocation::BottomLeft || mLocation == lcViewSphereLocation::TopLeft) ? 0 : Width - ViewportSize;
190 	const int Bottom = (mLocation == lcViewSphereLocation::BottomLeft || mLocation == lcViewSphereLocation::BottomRight) ? 0 : Height - ViewportSize;
191 	Context->SetViewport(Left, Bottom, ViewportSize, ViewportSize);
192 
193 	Context->SetDepthFunction(lcDepthFunction::Always);
194 	Context->EnableCullFace(true);
195 
196 	Context->SetVertexBuffer(mVertexBuffer);
197 	Context->SetVertexFormatPosition(3);
198 	Context->SetIndexBuffer(mIndexBuffer);
199 
200 	Context->SetMaterial(lcMaterialType::UnlitColor);
201 	Context->SetColor(lcVector4(0.0f, 0.0f, 0.0f, 1.0f));
202 
203 	float Scale = 1.005f + 2.0f / (float)ViewportSize;
204 	Context->SetWorldMatrix(lcMatrix44Scale(lcVector3(Scale, Scale, Scale)));
205 	Context->SetViewMatrix(GetViewMatrix());
206 	Context->SetProjectionMatrix(GetProjectionMatrix());
207 
208 	Context->DrawIndexedPrimitives(GL_TRIANGLES, mSubdivisions * mSubdivisions * 6 * 6, GL_UNSIGNED_SHORT, 0);
209 
210 	Context->SetMaterial(lcMaterialType::UnlitViewSphere);
211 	Context->BindTextureCubeMap(mTexture->mTexture);
212 
213 	Context->SetWorldMatrix(lcMatrix44Identity());
214 	Context->SetViewMatrix(GetViewMatrix());
215 	Context->SetProjectionMatrix(GetProjectionMatrix());
216 
217 	lcVector4 HighlightPosition(0.0f, 0.0f, 0.0f, 0.0f);
218 
219 	if (mIntersectionFlags.any())
220 	{
221 		for (int AxisIdx = 0; AxisIdx < 3; AxisIdx++)
222 		{
223 			if (mIntersectionFlags.test(2 * AxisIdx))
224 				HighlightPosition[AxisIdx] = 1.0f;
225 			else if (mIntersectionFlags.test(2 * AxisIdx + 1))
226 				HighlightPosition[AxisIdx] = -1.0f;
227 		}
228 
229 		HighlightPosition = lcVector4(lcNormalize(lcVector3(HighlightPosition)), mHighlightRadius);
230 	}
231 
232 	const lcPreferences& Preferences = lcGetPreferences();
233 	const lcVector4 TextColor = lcVector4FromColor(Preferences.mViewSphereTextColor);
234 	const lcVector4 BackgroundColor = lcVector4FromColor(Preferences.mViewSphereColor);
235 	const lcVector4 HighlightColor = lcVector4FromColor(Preferences.mViewSphereHighlightColor);
236 
237 	Context->SetHighlightParams(HighlightPosition, TextColor, BackgroundColor, HighlightColor);
238 	Context->DrawIndexedPrimitives(GL_TRIANGLES, mSubdivisions * mSubdivisions * 6 * 6, GL_UNSIGNED_SHORT, 0);
239 
240 	Context->EnableCullFace(false);
241 	Context->SetDepthFunction(lcDepthFunction::LessEqual);
242 
243 	Context->SetViewport(0, 0, Width, Height);
244 }
245 
OnLeftButtonDown()246 bool lcViewSphere::OnLeftButtonDown()
247 {
248 	if (!mSize || !mEnabled)
249 		return false;
250 
251 	mIntersectionFlags = GetIntersectionFlags(mIntersection);
252 
253 	if (!mIntersectionFlags.any())
254 		return false;
255 
256 	mMouseDownX = mView->GetMouseX();
257 	mMouseDownY = mView->GetMouseY();
258 	mMouseDown = true;
259 
260 	return true;
261 }
262 
OnLeftButtonUp()263 bool lcViewSphere::OnLeftButtonUp()
264 {
265 	if (!mSize || !mEnabled)
266 		return false;
267 
268 	if (!mMouseDown)
269 		return false;
270 
271 	mMouseDown = false;
272 
273 	if (!mIntersectionFlags.any())
274 		return false;
275 
276 	lcVector3 Position(0.0f, 0.0f, 0.0f);
277 
278 	for (int AxisIdx = 0; AxisIdx < 3; AxisIdx++)
279 	{
280 		if (mIntersectionFlags.test(AxisIdx * 2))
281 			Position[AxisIdx] = 1250.0f;
282 		else if (mIntersectionFlags.test(AxisIdx * 2 + 1))
283 			Position[AxisIdx] = -1250.0f;
284 	}
285 
286 	mView->SetViewpoint(Position);
287 
288 	return true;
289 }
290 
OnMouseMove()291 bool lcViewSphere::OnMouseMove()
292 {
293 	if (!mSize || !mEnabled)
294 		return false;
295 
296 	if (IsDragging())
297 	{
298 		mIntersectionFlags.reset();
299 		mView->StartOrbitTracking();
300 		return true;
301 	}
302 
303 	if (mView->IsTracking())
304 		return false;
305 
306 	std::bitset<6> IntersectionFlags = GetIntersectionFlags(mIntersection);
307 
308 	if (IntersectionFlags != mIntersectionFlags)
309 	{
310 		mIntersectionFlags = IntersectionFlags;
311 		mView->Redraw();
312 	}
313 
314 	return mIntersectionFlags.any();
315 }
316 
IsDragging() const317 bool lcViewSphere::IsDragging() const
318 {
319 	int InputStateX = mView->GetMouseX();
320 	int InputStateY = mView->GetMouseY();
321 	return mMouseDown && (qAbs(mMouseDownX - InputStateX) > 3 || qAbs(mMouseDownY - InputStateY) > 3);
322 }
323 
GetIntersectionFlags(lcVector3 & Intersection) const324 std::bitset<6> lcViewSphere::GetIntersectionFlags(lcVector3& Intersection) const
325 {
326 	const int Width = mView->GetWidth();
327 	const int Height = mView->GetHeight();
328 	const int ViewportSize = mSize;
329 	const int Left = (mLocation == lcViewSphereLocation::BottomLeft || mLocation == lcViewSphereLocation::TopLeft) ? 0 : Width - ViewportSize;
330 	const int Bottom = (mLocation == lcViewSphereLocation::BottomLeft || mLocation == lcViewSphereLocation::BottomRight) ? 0 : Height - ViewportSize;
331 	const int x = mView->GetMouseX() - Left;
332 	const int y = mView->GetMouseY() - Bottom;
333 	std::bitset<6> IntersectionFlags;
334 
335 	if (x < 0 || x > Width || y < 0 || y > Height)
336 		return IntersectionFlags;
337 
338 	lcVector3 StartEnd[2] = { lcVector3(x, y, 0), lcVector3(x, y, 1) };
339 	const int Viewport[4] = { 0, 0, ViewportSize, ViewportSize };
340 
341 	lcUnprojectPoints(StartEnd, 2, GetViewMatrix(), GetProjectionMatrix(), Viewport);
342 
343 	float Distance;
344 	if (lcSphereRayMinIntersectDistance(lcVector3(0.0f, 0.0f, 0.0f), mRadius, StartEnd[0], StartEnd[1], &Distance))
345 	{
346 		Intersection = (StartEnd[0] + (StartEnd[1] - StartEnd[0]) * Distance) / mRadius;
347 
348 		auto CheckIntersection = [&]()
349 		{
350 			for (int Axis1Idx = 0; Axis1Idx < 6; Axis1Idx++)
351 			{
352 				lcVector3 Point1(0.0f, 0.0f, 0.0f);
353 
354 				Point1[Axis1Idx / 2] = Axis1Idx % 2 ? -1.0f : 1.0f;
355 
356 				if (lcLengthSquared(Point1 - Intersection) < mHighlightRadius * mHighlightRadius)
357 				{
358 					IntersectionFlags.set(Axis1Idx);
359 					return;
360 				}
361 
362 				for (int Axis2Idx = 0; Axis2Idx < 6; Axis2Idx++)
363 				{
364 					if (Axis1Idx / 2 == Axis2Idx / 2)
365 						continue;
366 
367 					lcVector3 Point2(0.0f, 0.0f, 0.0f);
368 					Point2[Axis1Idx / 2] = Axis1Idx % 2 ? -0.70710678118f : 0.70710678118f;
369 					Point2[Axis2Idx / 2] = Axis2Idx % 2 ? -0.70710678118f : 0.70710678118f;
370 
371 					if (lcLengthSquared(Point2 - Intersection) < mHighlightRadius * mHighlightRadius)
372 					{
373 						IntersectionFlags.set(Axis1Idx);
374 						IntersectionFlags.set(Axis2Idx);
375 						return;
376 					}
377 
378 					for (int Axis3Idx = 0; Axis3Idx < 6; Axis3Idx++)
379 					{
380 						if (Axis1Idx / 2 == Axis3Idx / 2 || Axis2Idx / 2 == Axis3Idx / 2)
381 							continue;
382 
383 						lcVector3 Point3(0.0f, 0.0f, 0.0f);
384 						Point3[Axis1Idx / 2] = Axis1Idx % 2 ? -0.57735026919f : 0.57735026919f;
385 						Point3[Axis2Idx / 2] = Axis2Idx % 2 ? -0.57735026919f : 0.57735026919f;
386 						Point3[Axis3Idx / 2] = Axis3Idx % 2 ? -0.57735026919f : 0.57735026919f;
387 
388 						if (lcLengthSquared(Point3 - Intersection) < mHighlightRadius * mHighlightRadius)
389 						{
390 							IntersectionFlags.set(Axis1Idx);
391 							IntersectionFlags.set(Axis2Idx);
392 							IntersectionFlags.set(Axis3Idx);
393 							return;
394 						}
395 					}
396 				}
397 			}
398 		};
399 
400 		CheckIntersection();
401 	}
402 
403 	return IntersectionFlags;
404 }
405