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