1 #include "lc_global.h"
2 #include "minifig.h"
3 #include "lc_colors.h"
4 #include "pieceinf.h"
5 #include "lc_model.h"
6 #include "lc_library.h"
7 #include "lc_application.h"
8 #include "lc_file.h"
9 #include "lc_profile.h"
10
11 const char* MinifigWizard::mSectionNames[LC_MFW_NUMITEMS] =
12 {
13 "HATS", // LC_MFW_HATS
14 "HATS2", // LC_MFW_HATS2
15 "HEAD", // LC_MFW_HEAD
16 "NECK", // LC_MFW_NECK
17 "BODY", // LC_MFW_BODY
18 "BODY2", // LC_MFW_BODY2
19 "BODY3", // LC_MFW_BODY3
20 "RARM", // LC_MFW_RARM
21 "LARM", // LC_MFW_LARM
22 "RHAND", // LC_MFW_RHAND
23 "LHAND", // LC_MFW_LHAND
24 "RHANDA", // LC_MFW_RHANDA
25 "LHANDA", // LC_MFW_LHANDA
26 "RLEG", // LC_MFW_RLEG
27 "LLEG", // LC_MFW_LLEG
28 "RLEGA", // LC_MFW_RLEGA
29 "LLEGA", // LC_MFW_LLEGA
30 };
31
MinifigWizard()32 MinifigWizard::MinifigWizard()
33 : mModel(new lcModel(QString(), nullptr, false))
34 {
35 LoadSettings();
36 LoadTemplates();
37 }
38
~MinifigWizard()39 MinifigWizard::~MinifigWizard()
40 {
41 lcPiecesLibrary* Library = lcGetPiecesLibrary();
42
43 for (int i = 0; i < LC_MFW_NUMITEMS; i++)
44 if (mMinifig.Parts[i])
45 Library->ReleasePieceInfo(mMinifig.Parts[i]); // todo: don't call ReleasePieceInfo here because it may release textures and they need a GL context current
46
47 SaveTemplates();
48 }
49
LoadSettings()50 void MinifigWizard::LoadSettings()
51 {
52 QString CustomSettingsPath = lcGetProfileString(LC_PROFILE_MINIFIG_SETTINGS);
53
54 if (!CustomSettingsPath.isEmpty())
55 {
56 lcDiskFile DiskSettings(CustomSettingsPath);
57
58 if (DiskSettings.Open(QIODevice::ReadOnly))
59 {
60 ParseSettings(DiskSettings);
61 return;
62 }
63 }
64
65 lcDiskFile MinifigFile(":/resources/minifig.ini");
66
67 if (MinifigFile.Open(QIODevice::ReadOnly))
68 ParseSettings(MinifigFile);
69 }
70
LoadDefault()71 void MinifigWizard::LoadDefault()
72 {
73 LC_ARRAY_SIZE_CHECK(MinifigWizard::mSectionNames, LC_MFW_NUMITEMS);
74
75 const int ColorCodes[LC_MFW_NUMITEMS] = { 4, 7, 14, 7, 1, 0, 7, 4, 4, 14, 14, 7, 7, 0, 0, 7, 7 };
76 const char* const Pieces[LC_MFW_NUMITEMS] = { "3624.dat", "", "3626bp01.dat", "", "973.dat", "3815.dat", "", "3819.dat", "3818.dat", "3820.dat", "3820.dat", "", "", "3817.dat", "3816.dat", "", "" };
77 lcPiecesLibrary* Library = lcGetPiecesLibrary();
78
79 for (int i = 0; i < LC_MFW_NUMITEMS; i++)
80 {
81 mMinifig.Colors[i] = lcGetColorIndex(ColorCodes[i]);
82 mMinifig.Angles[i] = 0.0f;
83 mMinifig.Matrices[i] = lcMatrix44Identity();
84
85 PieceInfo* Info = Library->FindPiece(Pieces[i], nullptr, false, false);
86 mMinifig.Parts[i] = Info;
87 if (Info)
88 Library->LoadPieceInfo(Info, false, true);
89 }
90
91 Library->WaitForLoadQueue();
92 Calculate();
93 }
94
ParseSettings(lcFile & Settings)95 void MinifigWizard::ParseSettings(lcFile& Settings)
96 {
97 for (int SectionIndex = 0; SectionIndex < LC_MFW_NUMITEMS; SectionIndex++)
98 {
99 std::vector<lcMinifigPieceInfo>& InfoArray = mSettings[SectionIndex];
100
101 InfoArray.clear();
102 Settings.Seek(0, SEEK_SET);
103
104 char Line[1024];
105 bool FoundSection = false;
106 const char* SectionName = mSectionNames[SectionIndex];
107 const size_t SectionNameLength = strlen(SectionName);
108
109 while (Settings.ReadLine(Line, sizeof(Line)))
110 {
111 if (Line[0] == '[' && !strncmp(Line + 1, SectionName, SectionNameLength) && Line[SectionNameLength + 1] == ']')
112 {
113 FoundSection = true;
114 break;
115 }
116 }
117
118 if (!FoundSection)
119 {
120
121 lcMinifigPieceInfo MinifigInfo;
122 strncpy(MinifigInfo.Description, "None", sizeof(MinifigInfo.Description));
123 MinifigInfo.Description[sizeof(MinifigInfo.Description)-1] = 0;
124 MinifigInfo.Offset = lcMatrix44Identity();
125 MinifigInfo.Info = nullptr;
126
127 InfoArray.emplace_back(std::move(MinifigInfo));
128 continue;
129 }
130
131 while (Settings.ReadLine(Line, sizeof(Line)))
132 {
133 if (Line[0] == '[')
134 break;
135
136 char* DescriptionStart = strchr(Line, '"');
137 if (!DescriptionStart)
138 continue;
139 DescriptionStart++;
140 char* DescriptionEnd = strchr(DescriptionStart, '"');
141 if (!DescriptionEnd)
142 continue;
143 *DescriptionEnd = 0;
144 DescriptionEnd++;
145
146 char* NameStart = strchr(DescriptionEnd, '"');
147 if (!NameStart)
148 continue;
149 NameStart++;
150 char* NameEnd = strchr(NameStart, '"');
151 if (!NameEnd)
152 continue;
153 *NameEnd = 0;
154 NameEnd++;
155
156 PieceInfo* Info = lcGetPiecesLibrary()->FindPiece(NameStart, nullptr, false, false);
157 if (!Info && *NameStart)
158 continue;
159
160 float Mat[12];
161 int Flags;
162
163 if (sscanf(NameEnd, "%d %g %g %g %g %g %g %g %g %g %g %g %g",
164 &Flags, &Mat[0], &Mat[1], &Mat[2], &Mat[3], &Mat[4], &Mat[5], &Mat[6],
165 &Mat[7], &Mat[8], &Mat[9], &Mat[10], &Mat[11]) != 13)
166 continue;
167
168 lcMatrix44 Offset = lcMatrix44Identity();
169 float* OffsetMatrix = &Offset[0][0];
170
171 OffsetMatrix[0] = Mat[0];
172 OffsetMatrix[8] = -Mat[1];
173 OffsetMatrix[4] = Mat[2];
174 OffsetMatrix[2] = -Mat[3];
175 OffsetMatrix[10] = Mat[4];
176 OffsetMatrix[6] = -Mat[5];
177 OffsetMatrix[1] = Mat[6];
178 OffsetMatrix[9] = -Mat[7];
179 OffsetMatrix[5] = Mat[8];
180 OffsetMatrix[12] = Mat[9];
181 OffsetMatrix[14] = -Mat[10];
182 OffsetMatrix[13] = Mat[11];
183
184 lcMinifigPieceInfo MinifigInfo;
185 strncpy(MinifigInfo.Description, DescriptionStart, sizeof(MinifigInfo.Description));
186 MinifigInfo.Description[sizeof(MinifigInfo.Description)-1] = 0;
187 MinifigInfo.Offset = Offset;
188 MinifigInfo.Info = Info;
189
190 InfoArray.emplace_back(std::move(MinifigInfo));
191 }
192 }
193 }
194
SaveTemplate(const QString & TemplateName,const lcMinifigTemplate & Template)195 void MinifigWizard::SaveTemplate(const QString& TemplateName, const lcMinifigTemplate& Template)
196 {
197 mTemplates[TemplateName] = Template;
198 }
199
DeleteTemplate(const QString & TemplateName)200 void MinifigWizard::DeleteTemplate(const QString& TemplateName)
201 {
202 mTemplates.erase(TemplateName);
203 }
204
AddTemplatesJson(const QByteArray & TemplateData)205 void MinifigWizard::AddTemplatesJson(const QByteArray& TemplateData)
206 {
207 QJsonDocument Document = QJsonDocument::fromJson(TemplateData);
208 QJsonObject RootObject = Document.object();
209
210 int Version = RootObject["Version"].toInt(0);
211 QJsonObject TemplatesObject;
212
213 if (Version > 0)
214 TemplatesObject = RootObject["Templates"].toObject();
215 else
216 TemplatesObject = RootObject;
217
218 for (QJsonObject::const_iterator ElementIt = TemplatesObject.constBegin(); ElementIt != TemplatesObject.constEnd(); ElementIt++)
219 {
220 if (!ElementIt.value().isObject())
221 continue;
222
223 QJsonObject TemplateObject = ElementIt.value().toObject();
224 lcMinifigTemplate Template;
225
226 for (int PartIdx = 0; PartIdx < LC_MFW_NUMITEMS; PartIdx++)
227 {
228 QJsonObject PartObject = TemplateObject.value(QLatin1String(mSectionNames[PartIdx])).toObject();
229
230 Template.Parts[PartIdx] = PartObject["Id"].toString();
231 Template.Colors[PartIdx] = PartObject["Color"].toInt();
232 Template.Angles[PartIdx] = PartObject["Angle"].toDouble();
233 }
234
235 mTemplates.emplace(ElementIt.key(), std::move(Template));
236 }
237 }
238
GetTemplatesJson() const239 QByteArray MinifigWizard::GetTemplatesJson() const
240 {
241 QJsonObject TemplatesObject;
242
243 for (const auto& TemplateEntry : mTemplates)
244 {
245 const lcMinifigTemplate& Template = TemplateEntry.second;
246 QJsonObject TemplateObject;
247
248 for (int PartIdx = 0; PartIdx < LC_MFW_NUMITEMS; PartIdx++)
249 {
250 QJsonObject PartObject;
251
252 PartObject["Id"] = Template.Parts[PartIdx];
253 PartObject["Color"] = Template.Colors[PartIdx];
254 PartObject["Angle"] = Template.Angles[PartIdx];
255
256 TemplateObject[QLatin1String(mSectionNames[PartIdx])] = PartObject;
257 }
258
259 TemplatesObject[TemplateEntry.first] = TemplateObject;
260 }
261
262 QJsonObject RootObject;
263 RootObject["Templates"] = TemplatesObject;
264 RootObject["Version"] = 1;
265
266 return QJsonDocument(RootObject).toJson();
267 }
268
LoadTemplates()269 void MinifigWizard::LoadTemplates()
270 {
271 mTemplates.clear();
272
273 QSettings Settings;
274 Settings.beginGroup("Minifig");
275 QByteArray TemplateData = Settings.value("Templates").toByteArray();
276
277 AddTemplatesJson(TemplateData);
278 }
279
SaveTemplates()280 void MinifigWizard::SaveTemplates()
281 {
282 QSettings Settings;
283 Settings.beginGroup("Minifig");
284 Settings.setValue("Templates", GetTemplatesJson());
285 }
286
Calculate()287 void MinifigWizard::Calculate()
288 {
289 float HeadOffset = 0.0f;
290 lcMatrix44 Root, Mat, Mat2;
291
292 PieceInfo** Parts = mMinifig.Parts;
293 const float* Angles = mMinifig.Angles;
294 lcMatrix44* Matrices = mMinifig.Matrices;
295
296 const bool DroidTorso = Parts[LC_MFW_BODY] && !qstricmp(Parts[LC_MFW_BODY]->mFileName, "30375.dat");
297 const bool SkeletonTorso = Parts[LC_MFW_BODY] && !qstricmp(Parts[LC_MFW_BODY]->mFileName, "6260.dat");
298
299 if (Parts[LC_MFW_BODY3])
300 Root = lcMatrix44Translation(lcVector3(0, 0, 74.0f));
301 else
302 Root = lcMatrix44Translation(lcVector3(0, 0, 72.0f));
303 Matrices[LC_MFW_BODY] = lcMul(mSettings[LC_MFW_BODY][GetSelectionIndex(LC_MFW_BODY)].Offset, Root);
304
305 if (Parts[LC_MFW_NECK])
306 {
307 Matrices[LC_MFW_NECK] = lcMul(mSettings[LC_MFW_NECK][GetSelectionIndex(LC_MFW_NECK)].Offset, Root);
308 HeadOffset = 0.08f;
309 }
310
311 if (Parts[LC_MFW_HEAD])
312 {
313 Mat = lcMatrix44RotationZ(-LC_DTOR * Angles[LC_MFW_HEAD]);
314 Mat.SetTranslation(lcVector3(0.0f, 0.0f, 24.0f + HeadOffset));
315 Mat = lcMul(mSettings[LC_MFW_HEAD][GetSelectionIndex(LC_MFW_HEAD)].Offset, Mat);
316 Matrices[LC_MFW_HEAD] = lcMul(Mat, Root);
317 }
318
319 if (Parts[LC_MFW_HATS])
320 {
321 Mat = lcMatrix44RotationZ(-LC_DTOR * Angles[LC_MFW_HATS]);
322 Mat = lcMul(mSettings[LC_MFW_HATS][GetSelectionIndex(LC_MFW_HATS)].Offset, Mat);
323 Matrices[LC_MFW_HATS] = lcMul(Mat, Matrices[LC_MFW_HEAD]);
324 }
325
326 if (Parts[LC_MFW_HATS2])
327 {
328 Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_HATS2]);
329 Mat = lcMul(mSettings[LC_MFW_HATS2][GetSelectionIndex(LC_MFW_HATS2)].Offset, Mat);
330 Matrices[LC_MFW_HATS2] = lcMul(Mat, Matrices[LC_MFW_HATS]);
331 }
332
333 if (Parts[LC_MFW_RARM])
334 {
335 Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_RARM]);
336
337 if (DroidTorso || SkeletonTorso)
338 Mat2 = lcMatrix44Identity();
339 else
340 Mat2 = lcMatrix44RotationY(-LC_DTOR * 9.791f);
341 Mat2.SetTranslation(lcVector3(15.552f, 0, -8.88f));
342
343 Mat = lcMul(mSettings[LC_MFW_RARM][GetSelectionIndex(LC_MFW_RARM)].Offset, Mat);
344 Mat = lcMul(Mat, Mat2);
345 Matrices[LC_MFW_RARM] = lcMul(Mat, Root);
346 }
347
348 if (Parts[LC_MFW_RHAND])
349 {
350 Mat = lcMatrix44RotationY(-LC_DTOR * Angles[LC_MFW_RHAND]);
351 Mat2 = lcMatrix44RotationX(LC_DTOR * 45);
352 Mat = lcMul(mSettings[LC_MFW_RHAND][GetSelectionIndex(LC_MFW_RHAND)].Offset, Mat);
353 Mat = lcMul(Mat, Mat2);
354 Mat.SetTranslation(lcVector3(5.0f, -10.0f, -19.0f));
355 Matrices[LC_MFW_RHAND] = lcMul(Mat, Matrices[LC_MFW_RARM]);
356 }
357
358 if (Parts[LC_MFW_RHANDA])
359 {
360 Mat = lcMatrix44RotationZ(LC_DTOR * Angles[LC_MFW_RHANDA]);
361 Mat.SetTranslation(lcVector3(0, -9.25f, 0));
362 Mat = lcMul(mSettings[LC_MFW_RHANDA][GetSelectionIndex(LC_MFW_RHANDA)].Offset, Mat);
363 Mat = lcMul(Mat, lcMatrix44RotationX(LC_DTOR * 15.0f));
364 Matrices[LC_MFW_RHANDA] = lcMul(Mat, Matrices[LC_MFW_RHAND]);
365 }
366
367 if (Parts[LC_MFW_LARM])
368 {
369 Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_LARM]);
370
371 if (DroidTorso || SkeletonTorso)
372 Mat2 = lcMatrix44Identity();
373 else
374 Mat2 = lcMatrix44RotationY(LC_DTOR * 9.791f);
375 Mat2.SetTranslation(lcVector3(-15.552f, 0.0f, -8.88f));
376
377 Mat = lcMul(mSettings[LC_MFW_LARM][GetSelectionIndex(LC_MFW_LARM)].Offset, Mat);
378 Mat = lcMul(Mat, Mat2);
379 Matrices[LC_MFW_LARM] = lcMul(Mat, Root);
380 }
381
382 if (Parts[LC_MFW_LHAND])
383 {
384 Mat = lcMatrix44RotationY(-LC_DTOR * Angles[LC_MFW_LHAND]);
385 Mat2 = lcMatrix44RotationX(LC_DTOR * 45);
386 Mat = lcMul(mSettings[LC_MFW_LHAND][GetSelectionIndex(LC_MFW_LHAND)].Offset, Mat);
387 Mat = lcMul(Mat, Mat2);
388 Mat.SetTranslation(lcVector3(-5.0f, -10.0f, -19.0f));
389 Matrices[LC_MFW_LHAND] = lcMul(Mat, Matrices[LC_MFW_LARM]);
390 }
391
392 if (Parts[LC_MFW_LHANDA])
393 {
394 Mat = lcMatrix44RotationZ(LC_DTOR * Angles[LC_MFW_LHANDA]);
395 Mat.SetTranslation(lcVector3(0, -9.25f, 0));
396 Mat = lcMul(mSettings[LC_MFW_LHANDA][GetSelectionIndex(LC_MFW_LHANDA)].Offset, Mat);
397 Mat = lcMul(Mat, lcMatrix44RotationX(LC_DTOR * 15.0f));
398 Matrices[LC_MFW_LHANDA] = lcMul(Mat, Matrices[LC_MFW_LHAND]);
399 }
400
401 if (Parts[LC_MFW_BODY2])
402 {
403 Mat = lcMatrix44Identity();
404 Mat.SetTranslation(lcVector3(0, 0, -32.0f));
405 Mat = lcMul(mSettings[LC_MFW_BODY2][GetSelectionIndex(LC_MFW_BODY2)].Offset, Mat);
406 Matrices[LC_MFW_BODY2] = lcMul(Mat, Root);
407 }
408
409 if (Parts[LC_MFW_BODY3])
410 {
411 Mat = lcMatrix44Identity();
412 Mat.SetTranslation(lcVector3(0, 0, -32.0f));
413 Mat = lcMul(mSettings[LC_MFW_BODY3][GetSelectionIndex(LC_MFW_BODY3)].Offset, Mat);
414 Matrices[LC_MFW_BODY3] = lcMul(Mat, Root);
415 }
416
417 if (Parts[LC_MFW_RLEG])
418 {
419 Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_RLEG]);
420 Mat.SetTranslation(lcVector3(0, 0, -44.0f));
421 Mat = lcMul(mSettings[LC_MFW_RLEG][GetSelectionIndex(LC_MFW_RLEG)].Offset, Mat);
422 Matrices[LC_MFW_RLEG] = lcMul(Mat, Root);
423 }
424
425 if (Parts[LC_MFW_RLEGA])
426 {
427 const lcVector3 Center(-10.0f, -1.0f, -28.0f);
428 Mat = lcMatrix44RotationZ(LC_DTOR * Angles[LC_MFW_RLEGA]);
429 Mat2 = mSettings[LC_MFW_RLEGA][GetSelectionIndex(LC_MFW_RLEGA)].Offset;
430 Mat2.SetTranslation(lcMul31(-Center, Mat2));
431 Mat = lcMul(Mat2, Mat);
432 Mat.SetTranslation(lcMul31(Center, Mat2));
433 Matrices[LC_MFW_RLEGA] = lcMul(Mat, Matrices[LC_MFW_RLEG]);
434 }
435
436 if (Parts[LC_MFW_LLEG])
437 {
438 Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_LLEG]);
439 Mat.SetTranslation(lcVector3(0, 0, -44.0f));
440 Mat = lcMul(mSettings[LC_MFW_LLEG][GetSelectionIndex(LC_MFW_LLEG)].Offset, Mat);
441 Matrices[LC_MFW_LLEG] = lcMul(Mat, Root);
442 }
443
444 if (Parts[LC_MFW_LLEGA])
445 {
446 const lcVector3 Center(10.0f, -1.0f, -28.0f);
447 Mat = lcMatrix44RotationZ(LC_DTOR * Angles[LC_MFW_LLEGA]);
448 Mat2 = mSettings[LC_MFW_LLEGA][GetSelectionIndex(LC_MFW_LLEGA)].Offset;
449 Mat2.SetTranslation(lcMul31(-Center, Mat2));
450 Mat = lcMul(Mat2, Mat);
451 Mat.SetTranslation(lcMul31(Center, Mat2));
452 Matrices[LC_MFW_LLEGA] = lcMul(Mat, Matrices[LC_MFW_LLEG]);
453 }
454
455 mModel->SetMinifig(mMinifig);
456 }
457
GetSelectionIndex(int Type) const458 int MinifigWizard::GetSelectionIndex(int Type) const
459 {
460 const std::vector<lcMinifigPieceInfo>& InfoArray = mSettings[Type];
461
462 for (size_t Index = 0; Index < InfoArray.size(); Index++)
463 if (InfoArray[Index].Info == mMinifig.Parts[Type])
464 return (int)Index;
465
466 return 0;
467 }
468
SetSelectionIndex(int Type,int Index)469 void MinifigWizard::SetSelectionIndex(int Type, int Index)
470 {
471 lcPiecesLibrary* Library = lcGetPiecesLibrary();
472
473 if (mMinifig.Parts[Type])
474 Library->ReleasePieceInfo(mMinifig.Parts[Type]);
475
476 mMinifig.Parts[Type] = mSettings[Type][Index].Info;
477
478 if (mMinifig.Parts[Type])
479 Library->LoadPieceInfo(mMinifig.Parts[Type], true, true);
480
481 Calculate();
482 }
483
SetColor(int Type,int Color)484 void MinifigWizard::SetColor(int Type, int Color)
485 {
486 mMinifig.Colors[Type] = Color;
487
488 Calculate();
489 }
490
SetAngle(int Type,float Angle)491 void MinifigWizard::SetAngle(int Type, float Angle)
492 {
493 mMinifig.Angles[Type] = Angle;
494
495 Calculate();
496 }
497