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