1 #include "lc_global.h"
2 #include "lc_model.h"
3 #include <locale.h>
4 #include "piece.h"
5 #include "camera.h"
6 #include "light.h"
7 #include "group.h"
8 #include "lc_mainwindow.h"
9 #include "lc_profile.h"
10 #include "lc_library.h"
11 #include "lc_scene.h"
12 #include "lc_texture.h"
13 #include "lc_synth.h"
14 #include "lc_file.h"
15 #include "pieceinf.h"
16 #include "lc_view.h"
17 #include "minifig.h"
18 #include "lc_qarraydialog.h"
19 #include "lc_qselectdialog.h"
20 #include "lc_minifigdialog.h"
21 #include "lc_qgroupdialog.h"
22 #include "lc_qeditgroupsdialog.h"
23 #include "lc_qpropertiesdialog.h"
24 #include "lc_qutils.h"
25 #include "lc_lxf.h"
26 #include "lc_previewwidget.h"
27 #include "lc_findreplacewidget.h"
28
LoadDefaults()29 void lcModelProperties::LoadDefaults()
30 {
31 mAuthor = lcGetProfileString(LC_PROFILE_DEFAULT_AUTHOR_NAME);
32 mAmbientColor = lcVector3FromColor(lcGetProfileInt(LC_PROFILE_DEFAULT_AMBIENT_COLOR));
33 }
34
SaveDefaults()35 void lcModelProperties::SaveDefaults()
36 {
37 lcSetProfileInt(LC_PROFILE_DEFAULT_AMBIENT_COLOR, lcColorFromVector3(mAmbientColor));
38 }
39
SaveLDraw(QTextStream & Stream) const40 void lcModelProperties::SaveLDraw(QTextStream& Stream) const
41 {
42 QLatin1String LineEnding("\r\n");
43
44 Stream << QLatin1String("0 ") << mDescription << LineEnding;
45 Stream << QLatin1String("0 Name: ") << mModelName << LineEnding;
46 Stream << QLatin1String("0 Author: ") << mAuthor << LineEnding;
47
48 if (!mComments.isEmpty())
49 {
50 QStringList Comments = mComments.split('\n');
51 for (const QString& Comment : Comments)
52 Stream << QLatin1String("0 !LEOCAD MODEL COMMENT ") << Comment << LineEnding;
53 }
54
55 // lcVector3 mAmbientColor;
56 }
57
ParseLDrawHeader(QString Line,bool FirstLine)58 bool lcModelProperties::ParseLDrawHeader(QString Line, bool FirstLine)
59 {
60 QTextStream LineStream(&Line, QIODevice::ReadOnly);
61
62 QString Token;
63 LineStream >> Token;
64 int StartPos = LineStream.pos();
65 LineStream >> Token;
66
67 if (Token == QLatin1String("!LEOCAD"))
68 return false;
69
70 if (Token == QLatin1String("Name:"))
71 {
72 mModelName = LineStream.readLine().mid(1);
73 return true;
74 }
75
76 if (Token == QLatin1String("Author:"))
77 {
78 mAuthor = LineStream.readLine().mid(1);
79 return true;
80 }
81
82 if (FirstLine)
83 {
84 LineStream.seek(StartPos);
85 mDescription = LineStream.readLine().mid(1);
86 return true;
87 }
88
89 return false;
90 }
91
ParseLDrawLine(QTextStream & Stream)92 void lcModelProperties::ParseLDrawLine(QTextStream& Stream)
93 {
94 QString Token;
95 Stream >> Token;
96
97 if (Token == QLatin1String("AUTHOR"))
98 mAuthor = Stream.readLine().mid(1);
99 else if (Token == QLatin1String("DESCRIPTION"))
100 mDescription = Stream.readLine().mid(1);
101 else if (Token == QLatin1String("COMMENT"))
102 {
103 QString Comment = Stream.readLine().mid(1);
104 if (!mComments.isEmpty())
105 mComments += '\n';
106 mComments += Comment;
107 }
108 }
109
lcModel(const QString & FileName,Project * Project,bool Preview)110 lcModel::lcModel(const QString& FileName, Project* Project, bool Preview)
111 : mProject(Project), mIsPreview(Preview)
112 {
113 mProperties.mModelName = FileName;
114 mProperties.mFileName = FileName;
115 mProperties.LoadDefaults();
116
117 mActive = false;
118 mCurrentStep = 1;
119 mPieceInfo = nullptr;
120 }
121
~lcModel()122 lcModel::~lcModel()
123 {
124 if (mPieceInfo)
125 {
126 if (!mIsPreview && gMainWindow && gMainWindow->GetCurrentPieceInfo() == mPieceInfo)
127 gMainWindow->SetCurrentPieceInfo(nullptr);
128
129 if (mPieceInfo->GetModel() == this)
130 mPieceInfo->SetPlaceholder();
131
132 lcPiecesLibrary* Library = lcGetPiecesLibrary();
133 Library->ReleasePieceInfo(mPieceInfo);
134 }
135
136 DeleteModel();
137 DeleteHistory();
138 }
139
GetPieceWorldMatrix(lcPiece * Piece,lcMatrix44 & ParentWorldMatrix) const140 bool lcModel::GetPieceWorldMatrix(lcPiece* Piece, lcMatrix44& ParentWorldMatrix) const
141 {
142 for (lcPiece* ModelPiece : mPieces)
143 {
144 if (ModelPiece == Piece)
145 {
146 ParentWorldMatrix = lcMul(ModelPiece->mModelWorld, ParentWorldMatrix);
147 return true;
148 }
149
150 PieceInfo* Info = ModelPiece->mPieceInfo;
151
152 if (Info->IsModel())
153 {
154 lcMatrix44 WorldMatrix = lcMul(ModelPiece->mModelWorld, ParentWorldMatrix);
155
156 if (Info->GetPieceWorldMatrix(Piece, WorldMatrix))
157 {
158 ParentWorldMatrix = WorldMatrix;
159 return true;
160 }
161 }
162 }
163
164 return false;
165 }
166
IncludesModel(const lcModel * Model) const167 bool lcModel::IncludesModel(const lcModel* Model) const
168 {
169 if (Model == this)
170 return true;
171
172 for (lcPiece* Piece : mPieces)
173 if (Piece->mPieceInfo->IncludesModel(Model))
174 return true;
175
176 return false;
177 }
178
DeleteHistory()179 void lcModel::DeleteHistory()
180 {
181 for (lcModelHistoryEntry* Entry : mUndoHistory)
182 delete Entry;
183 mUndoHistory.clear();
184 for (lcModelHistoryEntry* Entry : mRedoHistory)
185 delete Entry;
186 mRedoHistory.clear();
187 }
188
DeleteModel()189 void lcModel::DeleteModel()
190 {
191 if (gMainWindow)
192 {
193 std::vector<lcView*> Views = lcView::GetModelViews(this);
194
195 // TODO: this is only needed to avoid a dangling pointer during undo/redo if a camera is set to a view but we should find a better solution instead
196 for (lcView* View : Views)
197 {
198 lcCamera* Camera = View->GetCamera();
199
200 if (!Camera->IsSimple() && mCameras.FindIndex(Camera) != -1)
201 View->SetCamera(Camera, true);
202 }
203 }
204
205 mPieces.DeleteAll();
206 mCameras.DeleteAll();
207 mLights.DeleteAll();
208 mGroups.DeleteAll();
209 mFileLines.clear();
210 }
211
CreatePieceInfo(Project * Project)212 void lcModel::CreatePieceInfo(Project* Project)
213 {
214 lcPiecesLibrary* Library = lcGetPiecesLibrary();
215 mPieceInfo = Library->FindPiece(mProperties.mFileName.toLatin1().constData(), Project, true, false);
216 mPieceInfo->SetModel(this, true, Project, true);
217 Library->LoadPieceInfo(mPieceInfo, true, true);
218 }
219
UpdateMesh()220 void lcModel::UpdateMesh()
221 {
222 mPieceInfo->SetModel(this, true, nullptr, false);
223 }
224
UpdateAllViews() const225 void lcModel::UpdateAllViews() const
226 {
227 lcView::UpdateProjectViews(mProject);
228 }
229
UpdatePieceInfo(std::vector<lcModel * > & UpdatedModels)230 void lcModel::UpdatePieceInfo(std::vector<lcModel*>& UpdatedModels)
231 {
232 if (std::find(UpdatedModels.begin(), UpdatedModels.end(), this) != UpdatedModels.end())
233 return;
234
235 mPieceInfo->SetModel(this, false, nullptr, false);
236 UpdatedModels.push_back(this);
237
238 lcMesh* Mesh = mPieceInfo->GetMesh();
239
240 if (mPieces.IsEmpty() && !Mesh)
241 {
242 mPieceInfo->SetBoundingBox(lcVector3(0.0f, 0.0f, 0.0f), lcVector3(0.0f, 0.0f, 0.0f));
243 return;
244 }
245
246 lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
247
248 for (lcPiece* Piece : mPieces)
249 {
250 if (Piece->IsVisibleInSubModel())
251 {
252 Piece->mPieceInfo->UpdateBoundingBox(UpdatedModels);
253 Piece->CompareBoundingBox(Min, Max);
254 }
255 }
256
257 if (Mesh)
258 {
259 Min = lcMin(Min, Mesh->mBoundingBox.Min);
260 Max = lcMax(Max, Mesh->mBoundingBox.Max);
261 }
262
263 mPieceInfo->SetBoundingBox(Min, Max);
264 }
265
SaveLDraw(QTextStream & Stream,bool SelectedOnly) const266 void lcModel::SaveLDraw(QTextStream& Stream, bool SelectedOnly) const
267 {
268 QLatin1String LineEnding("\r\n");
269
270 mProperties.SaveLDraw(Stream);
271
272 lcArray<lcGroup*> CurrentGroups;
273 lcStep Step = 1;
274 int CurrentLine = 0;
275 int AddedSteps = 0;
276
277 for (lcPiece* Piece : mPieces)
278 {
279 if (SelectedOnly && !Piece->IsSelected())
280 continue;
281
282 while (Piece->GetFileLine() > CurrentLine && CurrentLine < mFileLines.size())
283 {
284 QString Line = mFileLines[CurrentLine];
285 QTextStream LineStream(&Line, QIODevice::ReadOnly);
286
287 QString Token;
288 LineStream >> Token;
289 bool Skip = false;
290
291 if (Token == QLatin1String("0"))
292 {
293 LineStream >> Token;
294
295 if (Token == QLatin1String("STEP"))
296 {
297 if (Piece->GetStepShow() > Step)
298 Step++;
299 else
300 Skip = true;
301 }
302 }
303
304 if (!Skip)
305 {
306 Stream << mFileLines[CurrentLine];
307 if (AddedSteps > 0)
308 AddedSteps--;
309 }
310 CurrentLine++;
311 }
312
313 while (Piece->GetStepShow() > Step)
314 {
315 Stream << QLatin1String("0 STEP\r\n");
316 AddedSteps++;
317 Step++;
318 }
319
320 lcGroup* PieceGroup = Piece->GetGroup();
321
322 if (PieceGroup)
323 {
324 if (CurrentGroups.IsEmpty() || (!CurrentGroups.IsEmpty() && PieceGroup != CurrentGroups[CurrentGroups.GetSize() - 1]))
325 {
326 lcArray<lcGroup*> PieceParents;
327
328 for (lcGroup* Group = PieceGroup; Group; Group = Group->mGroup)
329 PieceParents.InsertAt(0, Group);
330
331 int FoundParent = -1;
332
333 while (!CurrentGroups.IsEmpty())
334 {
335 lcGroup* Group = CurrentGroups[CurrentGroups.GetSize() - 1];
336 int Index = PieceParents.FindIndex(Group);
337
338 if (Index == -1)
339 {
340 CurrentGroups.RemoveIndex(CurrentGroups.GetSize() - 1);
341 Stream << QLatin1String("0 !LEOCAD GROUP END\r\n");
342 }
343 else
344 {
345 FoundParent = Index;
346 break;
347 }
348 }
349
350 for (int ParentIdx = FoundParent + 1; ParentIdx < PieceParents.GetSize(); ParentIdx++)
351 {
352 lcGroup* Group = PieceParents[ParentIdx];
353 CurrentGroups.Add(Group);
354 Stream << QLatin1String("0 !LEOCAD GROUP BEGIN ") << Group->mName << LineEnding;
355 }
356 }
357 }
358 else
359 {
360 while (CurrentGroups.GetSize())
361 {
362 CurrentGroups.RemoveIndex(CurrentGroups.GetSize() - 1);
363 Stream << QLatin1String("0 !LEOCAD GROUP END\r\n");
364 }
365 }
366
367 if (Piece->mPieceInfo->GetSynthInfo())
368 {
369 Stream << QLatin1String("0 !LEOCAD SYNTH BEGIN\r\n");
370
371 const lcArray<lcPieceControlPoint>& ControlPoints = Piece->GetControlPoints();
372 for (int ControlPointIdx = 0; ControlPointIdx < ControlPoints.GetSize(); ControlPointIdx++)
373 {
374 const lcPieceControlPoint& ControlPoint = ControlPoints[ControlPointIdx];
375
376 Stream << QLatin1String("0 !LEOCAD SYNTH CONTROL_POINT");
377
378 const float* FloatMatrix = ControlPoint.Transform;
379 float Numbers[13] = { FloatMatrix[12], -FloatMatrix[14], FloatMatrix[13], FloatMatrix[0], -FloatMatrix[8], FloatMatrix[4], -FloatMatrix[2], FloatMatrix[10], -FloatMatrix[6], FloatMatrix[1], -FloatMatrix[9], FloatMatrix[5], ControlPoint.Scale };
380
381 for (int NumberIdx = 0; NumberIdx < 13; NumberIdx++)
382 Stream << ' ' << lcFormatValue(Numbers[NumberIdx], NumberIdx < 3 ? 4 : 6);
383
384 Stream << LineEnding;
385 }
386 }
387
388 Piece->SaveLDraw(Stream);
389
390 if (Piece->mPieceInfo->GetSynthInfo())
391 Stream << QLatin1String("0 !LEOCAD SYNTH END\r\n");
392 }
393
394 while (CurrentLine < mFileLines.size())
395 {
396 QString Line = mFileLines[CurrentLine];
397 QTextStream LineStream(&Line, QIODevice::ReadOnly);
398
399 QString Token;
400 LineStream >> Token;
401 bool Skip = false;
402
403 if (Token == QLatin1String("0"))
404 {
405 LineStream >> Token;
406
407 if (Token == QLatin1String("STEP") && AddedSteps-- > 0)
408 Skip = true;
409 }
410
411 if (!Skip)
412 Stream << mFileLines[CurrentLine];
413 CurrentLine++;
414 }
415
416 while (CurrentGroups.GetSize())
417 {
418 CurrentGroups.RemoveIndex(CurrentGroups.GetSize() - 1);
419 Stream << QLatin1String("0 !LEOCAD GROUP END\r\n");
420 }
421
422 for (lcCamera* Camera : mCameras)
423 if (!SelectedOnly || Camera->IsSelected())
424 Camera->SaveLDraw(Stream);
425
426 for (lcLight* Light : mLights)
427 if (!SelectedOnly || Light->IsSelected())
428 Light->SaveLDraw(Stream);
429
430 Stream.flush();
431 }
432
SplitMPD(QIODevice & Device)433 int lcModel::SplitMPD(QIODevice& Device)
434 {
435 qint64 ModelPos = Device.pos();
436
437 while (!Device.atEnd())
438 {
439 qint64 Pos = Device.pos();
440 QString OriginalLine = Device.readLine();
441 QString Line = OriginalLine.trimmed();
442 QTextStream LineStream(&Line, QIODevice::ReadOnly);
443
444 QString Token;
445 LineStream >> Token;
446
447 if (Token == QLatin1String("0"))
448 {
449 LineStream >> Token;
450
451 if (Token == QLatin1String("FILE"))
452 {
453 if (!mProperties.mFileName.isEmpty())
454 {
455 Device.seek(Pos);
456 break;
457 }
458
459 SetFileName(LineStream.readAll().trimmed());
460 ModelPos = Pos;
461 }
462 else if (Token == QLatin1String("NOFILE"))
463 {
464 break;
465 }
466 }
467 }
468
469 return ModelPos;
470 }
471
LoadLDraw(QIODevice & Device,Project * Project)472 void lcModel::LoadLDraw(QIODevice& Device, Project* Project)
473 {
474 lcPiece* Piece = nullptr;
475 lcCamera* Camera = nullptr;
476 lcLight* Light = nullptr;
477 lcArray<lcGroup*> CurrentGroups;
478 lcArray<lcPieceControlPoint> ControlPoints;
479 int CurrentStep = 1;
480 lcPiecesLibrary* Library = lcGetPiecesLibrary();
481
482 mProperties.mAuthor.clear();
483 mProperties.mDescription.clear();
484 mProperties.mComments.clear();
485 bool ReadingHeader = true;
486 bool FirstLine = true;
487
488 while (!Device.atEnd())
489 {
490 qint64 Pos = Device.pos();
491 QString OriginalLine = Device.readLine();
492 QString Line = OriginalLine.trimmed();
493 QTextStream LineStream(&Line, QIODevice::ReadOnly);
494
495 QString Token;
496 LineStream >> Token;
497
498 if (Token == QLatin1String("0"))
499 {
500 LineStream >> Token;
501
502 if (Token == QLatin1String("FILE"))
503 {
504 QString Name = LineStream.readAll().trimmed();
505
506 if (mProperties.mFileName != Name)
507 {
508 Device.seek(Pos);
509 break;
510 }
511
512 continue;
513 }
514 else if (Token == QLatin1String("NOFILE"))
515 {
516 break;
517 }
518
519 if (ReadingHeader)
520 {
521 ReadingHeader = mProperties.ParseLDrawHeader(Line, FirstLine);
522 FirstLine = false;
523
524 if (ReadingHeader)
525 continue;
526 }
527
528 if (Token == QLatin1String("STEP"))
529 {
530 delete Piece;
531 Piece = nullptr;
532 CurrentStep++;
533 mFileLines.append(OriginalLine);
534 continue;
535 }
536
537 if (Token != QLatin1String("!LEOCAD"))
538 {
539 mFileLines.append(OriginalLine);
540 continue;
541 }
542
543 LineStream >> Token;
544
545 if (Token == QLatin1String("MODEL"))
546 {
547 mProperties.ParseLDrawLine(LineStream);
548 }
549 else if (Token == QLatin1String("PIECE"))
550 {
551 if (!Piece)
552 Piece = new lcPiece(nullptr);
553
554 Piece->ParseLDrawLine(LineStream);
555 }
556 else if (Token == QLatin1String("CAMERA"))
557 {
558 if (!Camera)
559 Camera = new lcCamera(false);
560
561 if (Camera->ParseLDrawLine(LineStream))
562 {
563 Camera->CreateName(mCameras);
564 mCameras.Add(Camera);
565 Camera = nullptr;
566 }
567 }
568 else if (Token == QLatin1String("LIGHT"))
569 {
570 }
571 else if (Token == QLatin1String("GROUP"))
572 {
573 LineStream >> Token;
574
575 if (Token == QLatin1String("BEGIN"))
576 {
577 QString Name = LineStream.readAll().trimmed();
578 lcGroup* Group = GetGroup(Name, true);
579 if (!CurrentGroups.IsEmpty())
580 Group->mGroup = CurrentGroups[CurrentGroups.GetSize() - 1];
581 else
582 Group->mGroup = nullptr;
583 CurrentGroups.Add(Group);
584 }
585 else if (Token == QLatin1String("END"))
586 {
587 if (!CurrentGroups.IsEmpty())
588 CurrentGroups.RemoveIndex(CurrentGroups.GetSize() - 1);
589 }
590 }
591 else if (Token == QLatin1String("SYNTH"))
592 {
593 LineStream >> Token;
594
595 if (Token == QLatin1String("BEGIN"))
596 {
597 ControlPoints.RemoveAll();
598 }
599 else if (Token == QLatin1String("END"))
600 {
601 ControlPoints.RemoveAll();
602 }
603 else if (Token == QLatin1String("CONTROL_POINT"))
604 {
605 float Numbers[13];
606 for (int TokenIdx = 0; TokenIdx < 13; TokenIdx++)
607 LineStream >> Numbers[TokenIdx];
608
609 lcPieceControlPoint& PieceControlPoint = ControlPoints.Add();
610 PieceControlPoint.Transform = lcMatrix44(lcVector4(Numbers[3], Numbers[9], -Numbers[6], 0.0f), lcVector4(Numbers[5], Numbers[11], -Numbers[8], 0.0f),
611 lcVector4(-Numbers[4], -Numbers[10], Numbers[7], 0.0f), lcVector4(Numbers[0], Numbers[2], -Numbers[1], 1.0f));
612 PieceControlPoint.Scale = Numbers[12];
613 }
614 }
615
616 continue;
617 }
618 else if (Token == QLatin1String("1"))
619 {
620 ReadingHeader = false;
621 int ColorCode;
622 LineStream >> ColorCode;
623
624 float IncludeMatrix[12];
625 for (int TokenIdx = 0; TokenIdx < 12; TokenIdx++)
626 LineStream >> IncludeMatrix[TokenIdx];
627
628 lcMatrix44 IncludeTransform(lcVector4(IncludeMatrix[3], IncludeMatrix[6], IncludeMatrix[9], 0.0f), lcVector4(IncludeMatrix[4], IncludeMatrix[7], IncludeMatrix[10], 0.0f),
629 lcVector4(IncludeMatrix[5], IncludeMatrix[8], IncludeMatrix[11], 0.0f), lcVector4(IncludeMatrix[0], IncludeMatrix[1], IncludeMatrix[2], 1.0f));
630
631 QString PartId = LineStream.readAll().trimmed();
632
633 if (PartId.isEmpty())
634 continue;
635
636 QByteArray CleanId = PartId.toLatin1().toUpper().replace('\\', '/');
637
638 if (Library->IsPrimitive(CleanId.constData()))
639 {
640 mFileLines.append(OriginalLine);
641 }
642 else
643 {
644 if (!Piece)
645 Piece = new lcPiece(nullptr);
646
647 if (!CurrentGroups.IsEmpty())
648 Piece->SetGroup(CurrentGroups[CurrentGroups.GetSize() - 1]);
649
650 PieceInfo* Info = Library->FindPiece(PartId.toLatin1().constData(), Project, true, true);
651
652 float* Matrix = IncludeTransform;
653 lcMatrix44 Transform(lcVector4(Matrix[0], Matrix[2], -Matrix[1], 0.0f), lcVector4(Matrix[8], Matrix[10], -Matrix[9], 0.0f),
654 lcVector4(-Matrix[4], -Matrix[6], Matrix[5], 0.0f), lcVector4(Matrix[12], Matrix[14], -Matrix[13], 1.0f));
655
656 Piece->SetFileLine(mFileLines.size());
657 Piece->SetPieceInfo(Info, PartId, false);
658 Piece->Initialize(Transform, CurrentStep);
659 Piece->SetColorCode(ColorCode);
660 Piece->VerifyControlPoints(ControlPoints);
661 Piece->SetControlPoints(ControlPoints);
662 ControlPoints.RemoveAll();
663
664 if (Piece->mPieceInfo->IsModel() && Piece->mPieceInfo->GetModel()->IncludesModel(this))
665 {
666 delete Piece;
667 Piece = nullptr;
668 continue;
669 }
670
671 AddPiece(Piece);
672 Piece = nullptr;
673 }
674 }
675 else
676 {
677 ReadingHeader = false;
678 mFileLines.append(OriginalLine);
679 }
680
681 FirstLine = false;
682 }
683
684 mCurrentStep = CurrentStep;
685 CalculateStep(mCurrentStep);
686 Library->WaitForLoadQueue();
687 Library->mBuffersDirty = true;
688 Library->UnloadUnusedParts();
689
690 delete Piece;
691 delete Camera;
692 delete Light;
693 }
694
LoadBinary(lcFile * file)695 bool lcModel::LoadBinary(lcFile* file)
696 {
697 qint32 i, count;
698 char id[32];
699 quint32 rgb;
700 float fv = 0.4f;
701 quint8 ch;
702 quint16 sh;
703
704 file->Seek(0, SEEK_SET);
705 file->ReadBuffer(id, 32);
706 sscanf(&id[7], "%f", &fv);
707
708 if (memcmp(id, "LeoCAD ", 7))
709 return false;
710
711 if (fv == 0.0f)
712 {
713 lconv *loc = localeconv();
714 id[8] = loc->decimal_point[0];
715 sscanf(&id[7], "%f", &fv);
716
717 if (fv == 0.0f)
718 return false;
719 }
720
721 if (fv > 0.4f)
722 file->ReadFloats(&fv, 1);
723
724 file->ReadU32(&rgb, 1);
725
726 if (fv < 0.6f) // old view
727 {
728 double eye[3], target[3];
729 file->ReadDoubles(eye, 3);
730 file->ReadDoubles(target, 3);
731 }
732
733 file->Seek(28, SEEK_CUR);
734 file->ReadS32(&i, 1);
735 mCurrentStep = i;
736
737 if (fv > 0.8f)
738 file->ReadU32();//m_nScene
739
740 file->ReadS32(&count, 1);
741 lcPiecesLibrary* Library = lcGetPiecesLibrary();
742
743 int FirstNewPiece = mPieces.GetSize();
744
745 while (count--)
746 {
747 if (fv > 0.4f)
748 {
749 lcPiece* pPiece = new lcPiece(nullptr);
750 pPiece->FileLoad(*file);
751 AddPiece(pPiece);
752 }
753 else
754 {
755 char name[LC_PIECE_NAME_LEN];
756 lcVector3 pos, rot;
757 quint8 color, step, group;
758
759 file->ReadFloats(pos, 3);
760 file->ReadFloats(rot, 3);
761 file->ReadU8(&color, 1);
762 file->ReadBuffer(name, 9);
763 strcat(name, ".dat");
764 file->ReadU8(&step, 1);
765 file->ReadU8(&group, 1);
766
767 pos *= 25.0f;
768 lcMatrix44 WorldMatrix = lcMul(lcMatrix44RotationZ(rot[2] * LC_DTOR), lcMul(lcMatrix44RotationY(rot[1] * LC_DTOR), lcMatrix44RotationX(rot[0] * LC_DTOR)));
769 WorldMatrix.SetTranslation(pos);
770
771 PieceInfo* pInfo = Library->FindPiece(name, nullptr, true, false);
772 lcPiece* pPiece = new lcPiece(pInfo);
773
774 pPiece->Initialize(WorldMatrix, step);
775 pPiece->SetColorCode(lcGetColorCodeFromOriginalColor(color));
776 AddPiece(pPiece);
777
778 // pPiece->SetGroup((lcGroup*)group);
779 }
780 }
781
782 if (fv >= 0.4f)
783 {
784 file->ReadBuffer(&ch, 1);
785 if (ch == 0xFF) file->ReadU16(&sh, 1); else sh = ch;
786 if (sh > 100)
787 file->Seek(sh, SEEK_CUR);
788 else
789 {
790 QByteArray Author;
791 Author.resize(sh + 1);
792 file->ReadBuffer(Author.data(), sh);
793 Author[sh] = 0;
794 mProperties.mAuthor = QString::fromUtf8(Author);
795 }
796
797 file->ReadBuffer(&ch, 1);
798 if (ch == 0xFF) file->ReadU16(&sh, 1); else sh = ch;
799 if (sh > 100)
800 file->Seek(sh, SEEK_CUR);
801 else
802 {
803 QByteArray Description;
804 Description.resize(sh + 1);
805 file->ReadBuffer(Description.data(), sh);
806 Description[sh] = 0;
807 mProperties.mDescription = QString::fromUtf8(Description);
808 }
809
810 file->ReadBuffer(&ch, 1);
811 if (ch == 0xFF && fv < 1.3f) file->ReadU16(&sh, 1); else sh = ch;
812 if (sh > 255)
813 file->Seek(sh, SEEK_CUR);
814 else
815 {
816 QByteArray Comments;
817 Comments.resize(sh + 1);
818 file->ReadBuffer(Comments.data(), sh);
819 Comments[sh] = 0;
820 mProperties.mComments = QString::fromUtf8(Comments);
821 mProperties.mComments.replace(QLatin1String("\r\n"), QLatin1String("\n"));
822 }
823 }
824
825 if (fv >= 0.5f)
826 {
827 int NumGroups = mGroups.GetSize();
828
829 file->ReadS32(&count, 1);
830 for (i = 0; i < count; i++)
831 mGroups.Add(new lcGroup());
832
833 for (int GroupIdx = NumGroups; GroupIdx < mGroups.GetSize(); GroupIdx++)
834 {
835 lcGroup* Group = mGroups[GroupIdx];
836
837 if (fv < 1.0f)
838 {
839 char Name[LC_MAX_GROUP_NAME + 1];
840 file->ReadBuffer(Name, sizeof(Name));
841 Group->mName = QString::fromUtf8(Name);
842 file->ReadBuffer(&ch, 1);
843 Group->mGroup = (lcGroup*)-1;
844 }
845 else
846 Group->FileLoad(file);
847 }
848
849 for (int GroupIdx = NumGroups; GroupIdx < mGroups.GetSize(); GroupIdx++)
850 {
851 lcGroup* Group = mGroups[GroupIdx];
852
853 i = (qint32)(quintptr)(Group->mGroup);
854 Group->mGroup = nullptr;
855
856 if (i > 0xFFFF || i == -1)
857 continue;
858
859 Group->mGroup = mGroups[NumGroups + i];
860 }
861
862 for (int PieceIdx = FirstNewPiece; PieceIdx < mPieces.GetSize(); PieceIdx++)
863 {
864 lcPiece* Piece = mPieces[PieceIdx];
865
866 i = (qint32)(quintptr)(Piece->GetGroup());
867 Piece->SetGroup(nullptr);
868
869 if (i > 0xFFFF || i == -1)
870 continue;
871
872 Piece->SetGroup(mGroups[NumGroups + i]);
873 }
874
875 RemoveEmptyGroups();
876 }
877
878 if (fv >= 0.6f)
879 {
880 if (fv < 1.0f)
881 file->Seek(4, SEEK_CUR);
882 else
883 file->Seek(2, SEEK_CUR);
884
885 file->ReadS32(&count, 1);
886
887 for (i = 0; i < count; i++)
888 lcCamera::FileLoad(*file);
889 }
890
891 if (fv >= 0.7f)
892 {
893 file->Seek(24, SEEK_CUR);
894
895 if (fv < 1.3f)
896 {
897 file->ReadU8(&ch, 1);
898 if (ch == 0xFF)
899 file->ReadU16(&sh, 1);
900 sh = ch;
901 }
902 else
903 file->ReadU16(&sh, 1);
904
905 file->Seek(sh, SEEK_CUR); // Background
906 }
907
908 if (fv >= 0.8f)
909 {
910 file->ReadBuffer(&ch, 1);
911 file->Seek(ch, SEEK_CUR);
912 file->ReadBuffer(&ch, 1);
913 file->Seek(ch, SEEK_CUR);
914 }
915
916 if (fv > 0.9f)
917 {
918 file->ReadU32(&rgb, 1);
919 mProperties.mAmbientColor[0] = (float)((unsigned char) (rgb))/255;
920 mProperties.mAmbientColor[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
921 mProperties.mAmbientColor[2] = (float)((unsigned char) ((rgb) >> 16))/255;
922
923 if (fv < 1.3f)
924 file->Seek(23, SEEK_CUR);
925 else
926 file->Seek(11, SEEK_CUR);
927 }
928
929 if (fv > 1.0f)
930 {
931 file->ReadU32(&rgb, 1);
932 file->ReadU32(&rgb, 1);
933 }
934
935 CalculateStep(mCurrentStep);
936 lcGetPiecesLibrary()->UnloadUnusedParts();
937
938 return true;
939 }
940
LoadLDD(const QString & FileData)941 bool lcModel::LoadLDD(const QString& FileData)
942 {
943 std::vector<lcPiece*> Pieces;
944 std::vector<std::vector<lcPiece*>> Groups;
945
946 if (!lcImportLXFMLFile(FileData, Pieces, Groups))
947 return false;
948
949 for (lcPiece* Piece : Pieces)
950 AddPiece(Piece);
951
952 for (const std::vector<lcPiece*>& Group : Groups)
953 {
954 lcGroup* NewGroup = AddGroup(tr("Group #"), nullptr);
955 for (lcPiece* Piece : Group)
956 Piece->SetGroup(NewGroup);
957 }
958
959 lcPiecesLibrary* Library = lcGetPiecesLibrary();
960 CalculateStep(mCurrentStep);
961 Library->WaitForLoadQueue();
962 Library->mBuffersDirty = true;
963 Library->UnloadUnusedParts();
964
965 return true;
966 }
967
LoadInventory(const QByteArray & Inventory)968 bool lcModel::LoadInventory(const QByteArray& Inventory)
969 {
970 QJsonDocument Document = QJsonDocument::fromJson(Inventory);
971 QJsonObject Root = Document.object();
972 QJsonArray Parts = Root["results"].toArray();
973 lcPiecesLibrary* Library = lcGetPiecesLibrary();
974
975 for (const QJsonValue& Part : Parts)
976 {
977 QJsonObject PartObject = Part.toObject();
978 QByteArray PartID = PartObject["part"].toObject()["part_num"].toString().toLatin1();
979 QJsonArray PartIDArray = PartObject["part"].toObject()["external_ids"].toObject()["LDraw"].toArray();
980 if (!PartIDArray.isEmpty())
981 PartID = PartIDArray.first().toString().toLatin1();
982 int Quantity = PartObject["quantity"].toInt();
983 int ColorCode = 16;
984 QJsonArray ColorArray = PartObject["color"].toObject()["external_ids"].toObject()["LDraw"].toObject()["ext_ids"].toArray();
985 if (!ColorArray.isEmpty())
986 ColorCode = ColorArray.first().toInt();
987
988 PieceInfo* Info = Library->FindPiece(PartID + ".dat", nullptr, true, false);
989
990 while (Quantity--)
991 {
992 lcPiece* Piece = new lcPiece(nullptr);
993 Piece->SetPieceInfo(Info, QString(), false);
994 Piece->Initialize(lcMatrix44Identity(), 1);
995 Piece->SetColorCode(ColorCode);
996 AddPiece(Piece);
997 }
998 }
999
1000 if (mPieces.IsEmpty())
1001 return false;
1002
1003 Library->WaitForLoadQueue();
1004 Library->mBuffersDirty = true;
1005 Library->UnloadUnusedParts();
1006
1007 auto RoundBounds = [](float& Value)
1008 {
1009 Value = ((Value < 0.0f) ? floor((Value - 5.0f) / 10.0f) : ceil((Value + 5.0f) / 10.0f)) * 10.0f;
1010 };
1011
1012 const float TargetHeight = 800.0f;
1013 float CurrentX = 0.0f;
1014 float CurrentY = 0.0f;
1015 float ColumnWidth = 0.0f;
1016
1017 for (lcPiece* Piece : mPieces)
1018 {
1019 lcBoundingBox BoundingBox = Piece->mPieceInfo->GetBoundingBox();
1020 RoundBounds(BoundingBox.Min.x);
1021 RoundBounds(BoundingBox.Min.y);
1022 RoundBounds(BoundingBox.Max.x);
1023 RoundBounds(BoundingBox.Max.y);
1024
1025 float PieceWidth = BoundingBox.Max.x - BoundingBox.Min.x;
1026 float PieceHeight = BoundingBox.Max.y - BoundingBox.Min.y;
1027
1028 if (CurrentY + PieceHeight > TargetHeight)
1029 {
1030 CurrentY = 0.0f;
1031 CurrentX += ColumnWidth;
1032 ColumnWidth = 0.0f;
1033 }
1034
1035 Piece->SetPosition(lcVector3(CurrentX + PieceWidth / 2.0f, CurrentY + PieceHeight / 2.0f, 0.0f), 1, false);
1036 CurrentY += PieceHeight;
1037 ColumnWidth = qMax(ColumnWidth, PieceWidth);
1038 }
1039
1040 CalculateStep(mCurrentStep);
1041
1042 return true;
1043 }
1044
Merge(lcModel * Other)1045 void lcModel::Merge(lcModel* Other)
1046 {
1047 for (int PieceIdx = 0; PieceIdx < Other->mPieces.GetSize(); PieceIdx++)
1048 {
1049 lcPiece* Piece = Other->mPieces[PieceIdx];
1050 Piece->SetFileLine(-1);
1051 AddPiece(Piece);
1052 }
1053
1054 Other->mPieces.RemoveAll();
1055
1056 for (int CameraIdx = 0; CameraIdx < Other->mCameras.GetSize(); CameraIdx++)
1057 {
1058 lcCamera* Camera = Other->mCameras[CameraIdx];
1059 Camera->CreateName(mCameras);
1060 mCameras.Add(Camera);
1061 }
1062
1063 Other->mCameras.RemoveAll();
1064
1065 for (int LightIdx = 0; LightIdx < Other->mLights.GetSize(); LightIdx++)
1066 {
1067 lcLight* Light = Other->mLights[LightIdx];
1068 Light->CreateName(mLights);
1069 mLights.Add(Light);
1070 }
1071
1072 Other->mLights.RemoveAll();
1073
1074 for (int GroupIdx = 0; GroupIdx < Other->mGroups.GetSize(); GroupIdx++)
1075 {
1076 lcGroup* Group = Other->mGroups[GroupIdx];
1077 Group->CreateName(mGroups);
1078 mGroups.Add(Group);
1079 }
1080
1081 Other->mGroups.RemoveAll();
1082
1083 delete Other;
1084
1085 gMainWindow->UpdateTimeline(false, false);
1086 }
1087
Cut()1088 void lcModel::Cut()
1089 {
1090 Copy();
1091
1092 if (RemoveSelectedObjects())
1093 {
1094 gMainWindow->UpdateTimeline(false, false);
1095 gMainWindow->UpdateSelectedObjects(true);
1096 UpdateAllViews();
1097 SaveCheckpoint(tr("Cutting"));
1098 }
1099 }
1100
Copy()1101 void lcModel::Copy()
1102 {
1103 QByteArray File;
1104 QTextStream Stream(&File, QIODevice::WriteOnly);
1105
1106 SaveLDraw(Stream, true);
1107
1108 gApplication->ExportClipboard(File);
1109 }
1110
Paste(bool PasteToCurrentStep)1111 void lcModel::Paste(bool PasteToCurrentStep)
1112 {
1113 if (gApplication->mClipboard.isEmpty())
1114 return;
1115
1116 lcModel* Model = new lcModel(QString(), nullptr, false);
1117
1118 QBuffer Buffer(&gApplication->mClipboard);
1119 Buffer.open(QIODevice::ReadOnly);
1120 Model->LoadLDraw(Buffer, lcGetActiveProject());
1121
1122 const lcArray<lcPiece*>& PastedPieces = Model->mPieces;
1123 lcArray<lcObject*> SelectedObjects;
1124 SelectedObjects.AllocGrow(PastedPieces.GetSize());
1125
1126 for (lcPiece* Piece : PastedPieces)
1127 {
1128 Piece->SetFileLine(-1);
1129
1130 if (PasteToCurrentStep)
1131 {
1132 Piece->SetStepShow(mCurrentStep);
1133 SelectedObjects.Add(Piece);
1134 }
1135 else
1136 {
1137 if (Piece->GetStepShow() <= mCurrentStep)
1138 SelectedObjects.Add(Piece);
1139 }
1140 }
1141
1142 Merge(Model);
1143 SaveCheckpoint(tr("Pasting"));
1144
1145 if (SelectedObjects.GetSize() == 1)
1146 ClearSelectionAndSetFocus(SelectedObjects[0], LC_PIECE_SECTION_POSITION, false);
1147 else
1148 SetSelectionAndFocus(SelectedObjects, nullptr, 0, false);
1149
1150 CalculateStep(mCurrentStep);
1151 gMainWindow->UpdateTimeline(false, false);
1152 UpdateAllViews();
1153 }
1154
DuplicateSelectedPieces()1155 void lcModel::DuplicateSelectedPieces()
1156 {
1157 lcArray<lcObject*> NewPieces;
1158 lcPiece* Focus = nullptr;
1159 std::map<lcGroup*, lcGroup*> GroupMap;
1160
1161 std::function<lcGroup*(lcGroup*)> GetNewGroup = [this, &GroupMap, &GetNewGroup](lcGroup* Group)
1162 {
1163 const auto GroupIt = GroupMap.find(Group);
1164 if (GroupIt != GroupMap.end())
1165 return GroupIt->second;
1166 else
1167 {
1168 lcGroup* Parent = Group->mGroup ? GetNewGroup(Group->mGroup) : nullptr;
1169 QString GroupName = Group->mName;
1170
1171 while (!GroupName.isEmpty())
1172 {
1173 QCharRef Last = GroupName[GroupName.size() - 1];
1174 if (Last.isDigit())
1175 GroupName.chop(1);
1176 else
1177 break;
1178 }
1179
1180 if (GroupName.isEmpty())
1181 GroupName = Group->mName;
1182
1183 lcGroup* NewGroup = AddGroup(GroupName, Parent);
1184 GroupMap[Group] = NewGroup;
1185 return NewGroup;
1186 }
1187 };
1188
1189 for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
1190 {
1191 lcPiece* Piece = mPieces[PieceIdx];
1192
1193 if (!Piece->IsSelected())
1194 continue;
1195
1196 lcPiece* NewPiece = new lcPiece(*Piece);
1197 NewPiece->UpdatePosition(mCurrentStep);
1198 NewPieces.Add(NewPiece);
1199
1200 if (Piece->IsFocused())
1201 Focus = NewPiece;
1202
1203 PieceIdx++;
1204 InsertPiece(NewPiece, PieceIdx);
1205
1206 lcGroup* Group = Piece->GetGroup();
1207 if (Group)
1208 Piece->SetGroup(GetNewGroup(Group));
1209 }
1210
1211 if (NewPieces.IsEmpty())
1212 return;
1213
1214 gMainWindow->UpdateTimeline(false, false);
1215 SetSelectionAndFocus(NewPieces, Focus, LC_PIECE_SECTION_POSITION, false);
1216 SaveCheckpoint(tr("Duplicating Pieces"));
1217 }
1218
PaintSelectedPieces()1219 void lcModel::PaintSelectedPieces()
1220 {
1221 SetSelectedPiecesColorIndex(gMainWindow->mColorIndex);
1222 }
1223
GetScene(lcScene * Scene,lcCamera * ViewCamera,bool AllowHighlight,bool AllowFade) const1224 void lcModel::GetScene(lcScene* Scene, lcCamera* ViewCamera, bool AllowHighlight, bool AllowFade) const
1225 {
1226 if (mPieceInfo)
1227 mPieceInfo->AddRenderMesh(*Scene);
1228
1229 for (const lcPiece* Piece : mPieces)
1230 {
1231 if (Piece->IsVisible(mCurrentStep))
1232 {
1233 lcStep StepShow = Piece->GetStepShow();
1234 Piece->AddMainModelRenderMeshes(Scene, AllowHighlight && StepShow == mCurrentStep, AllowFade && StepShow < mCurrentStep);
1235 }
1236 }
1237
1238 if (Scene->GetDrawInterface() && !Scene->GetActiveSubmodelInstance())
1239 {
1240 for (lcCamera* Camera : mCameras)
1241 if (Camera != ViewCamera && Camera->IsVisible())
1242 Scene->AddInterfaceObject(Camera);
1243
1244 for (lcLight* Light : mLights)
1245 if (Light->IsVisible())
1246 Scene->AddInterfaceObject(Light);
1247 }
1248 }
1249
AddSubModelRenderMeshes(lcScene * Scene,const lcMatrix44 & WorldMatrix,int DefaultColorIndex,lcRenderMeshState RenderMeshState,bool ParentActive) const1250 void lcModel::AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMatrix, int DefaultColorIndex, lcRenderMeshState RenderMeshState, bool ParentActive) const
1251 {
1252 for (lcPiece* Piece : mPieces)
1253 if (Piece->IsVisibleInSubModel())
1254 Piece->AddSubModelRenderMeshes(Scene, WorldMatrix, DefaultColorIndex, RenderMeshState, ParentActive);
1255 }
1256
GetStepImage(bool Zoom,int Width,int Height,lcStep Step)1257 QImage lcModel::GetStepImage(bool Zoom, int Width, int Height, lcStep Step)
1258 {
1259 lcView* ActiveView = gMainWindow->GetActiveView();
1260 const lcStep CurrentStep = mCurrentStep;
1261 lcCamera* Camera = ActiveView->GetCamera();
1262
1263 lcView View(lcViewType::View, this);
1264 View.SetCamera(Camera, true);
1265 View.SetOffscreenContext();
1266 View.MakeCurrent();
1267
1268 if (!View.BeginRenderToImage(Width, Height))
1269 {
1270 QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Error creating images."));
1271 return QImage();
1272 }
1273
1274 SetTemporaryStep(Step);
1275
1276 if (Zoom)
1277 ZoomExtents(Camera, (float)Width / (float)Height);
1278
1279 View.OnDraw();
1280
1281 QImage Image = View.GetRenderImage();
1282
1283 View.EndRenderToImage();
1284
1285 SetTemporaryStep(CurrentStep);
1286
1287 if (!mActive)
1288 CalculateStep(LC_STEP_MAX);
1289
1290 return Image;
1291 }
1292
GetPartsListImage(int MaxWidth,lcStep Step,quint32 BackgroundColor,QFont Font,QColor TextColor) const1293 QImage lcModel::GetPartsListImage(int MaxWidth, lcStep Step, quint32 BackgroundColor, QFont Font, QColor TextColor) const
1294 {
1295 lcPartsList PartsList;
1296
1297 if (Step == 0)
1298 GetPartsList(gDefaultColor, true, false, PartsList);
1299 else
1300 GetPartsListForStep(Step, gDefaultColor, PartsList);
1301
1302 if (PartsList.empty())
1303 return QImage();
1304
1305 struct lcPartsListImage
1306 {
1307 QImage Thumbnail;
1308 const PieceInfo* Info;
1309 int ColorIndex;
1310 int Count;
1311 QRect Bounds;
1312 QPoint Position;
1313 };
1314
1315 std::vector<lcPartsListImage> Images;
1316
1317 for (const auto& PartIt : PartsList)
1318 {
1319 for (const auto& ColorIt : PartIt.second)
1320 {
1321 Images.push_back(lcPartsListImage());
1322 lcPartsListImage& Image = Images.back();
1323 Image.Info = PartIt.first;
1324 Image.ColorIndex = ColorIt.first;
1325 Image.Count = ColorIt.second;
1326 }
1327 }
1328
1329 auto ImageCompare = [](const lcPartsListImage& Image1, const lcPartsListImage& Image2)
1330 {
1331 if (Image1.ColorIndex != Image2.ColorIndex)
1332 return Image1.ColorIndex < Image2.ColorIndex;
1333
1334 return strcmp(Image1.Info->m_strDescription, Image2.Info->m_strDescription) < 0;
1335 };
1336
1337 std::sort(Images.begin(), Images.end(), ImageCompare);
1338
1339 lcView View(lcViewType::PartsList, nullptr);
1340 View.SetOffscreenContext();
1341 View.MakeCurrent();
1342 lcContext* Context = View.mContext;
1343 const int ThumbnailSize = qMin(MaxWidth, 512);
1344
1345 View.SetSize(ThumbnailSize, ThumbnailSize);
1346
1347 if (!View.BeginRenderToImage(ThumbnailSize, ThumbnailSize))
1348 {
1349 QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Error creating images."));
1350 return QImage();
1351 }
1352
1353 float OrthoSize = 200.0f;
1354
1355 lcMatrix44 ProjectionMatrix = lcMatrix44Ortho(-OrthoSize, OrthoSize, -OrthoSize, OrthoSize, -5000.0f, 5000.0f);
1356 lcMatrix44 ViewMatrix = lcMatrix44LookAt(lcVector3(-100.0f, -100.0f, 75.0f), lcVector3(0.0f, 0.0f, 0.0f), lcVector3(0.0f, 0.0f, 1.0f));
1357 const int Viewport[4] = { 0, 0, ThumbnailSize, ThumbnailSize };
1358
1359 float ExtraPixels = 0.0f;
1360
1361 for (lcPartsListImage& Image : Images)
1362 {
1363 const PieceInfo* Info = Image.Info;
1364 const lcBoundingBox& BoundingBox = Info->GetBoundingBox();
1365
1366 lcVector3 Points[8];
1367 lcGetBoxCorners(BoundingBox.Min, BoundingBox.Max, Points);
1368
1369 for (lcVector3& Point : Points)
1370 {
1371 Point = lcProjectPoint(Point, ViewMatrix, ProjectionMatrix, Viewport);
1372
1373 ExtraPixels = qMax(ExtraPixels, -Point.x);
1374 ExtraPixels = qMax(ExtraPixels, Point.x - ThumbnailSize);
1375 ExtraPixels = qMax(ExtraPixels, -Point.y);
1376 ExtraPixels = qMax(ExtraPixels, Point.y - ThumbnailSize);
1377 }
1378 }
1379
1380 if (ExtraPixels)
1381 {
1382 OrthoSize += ExtraPixels * (2.0f * OrthoSize / ThumbnailSize);
1383 ProjectionMatrix = lcMatrix44Ortho(-OrthoSize, OrthoSize, -OrthoSize, OrthoSize, -5000.0f, 5000.0f);
1384 }
1385
1386 Context->SetViewport(0, 0, ThumbnailSize, ThumbnailSize);
1387 Context->SetDefaultState();
1388 Context->SetProjectionMatrix(ProjectionMatrix);
1389
1390 for (lcPartsListImage& Image : Images)
1391 {
1392 View.BindRenderFramebuffer();
1393 Context->ClearColorAndDepth(lcVector4(lcVector3FromColor(BackgroundColor), 0.0f));
1394
1395 lcScene Scene;
1396
1397 const lcPreferences& Preferences = lcGetPreferences();
1398 lcShadingMode ShadingMode = Preferences.mShadingMode;
1399 if (ShadingMode == lcShadingMode::Wireframe)
1400 ShadingMode = lcShadingMode::Flat;
1401
1402 Scene.SetShadingMode(ShadingMode);
1403 Scene.SetAllowLOD(false);
1404 Scene.Begin(ViewMatrix);
1405
1406 Image.Info->AddRenderMeshes(&Scene, lcMatrix44Identity(), Image.ColorIndex, lcRenderMeshState::Default, true);
1407
1408 Scene.End();
1409
1410 Scene.Draw(Context);
1411
1412 View.UnbindRenderFramebuffer();
1413 Image.Thumbnail = View.GetRenderFramebufferImage().convertToFormat(QImage::Format_ARGB32);
1414 }
1415
1416 View.EndRenderToImage();
1417 Context->ClearResources();
1418
1419 auto CalculateImageBounds = [](lcPartsListImage& Image)
1420 {
1421 QImage& Thumbnail = Image.Thumbnail;
1422 int Width = Thumbnail.width();
1423 int Height = Thumbnail.height();
1424
1425 int MinX = Width;
1426 int MinY = Height;
1427 int MaxX = 0;
1428 int MaxY = 0;
1429
1430 for (int x = 0; x < Width; x++)
1431 {
1432 for (int y = 0; y < Height; y++)
1433 {
1434 if (qAlpha(Thumbnail.pixel(x, y)))
1435 {
1436 MinX = qMin(x, MinX);
1437 MinY = qMin(y, MinY);
1438 MaxX = qMax(x, MaxX);
1439 MaxY = qMax(y, MaxY);
1440 }
1441 }
1442 }
1443
1444 Image.Bounds = QRect(QPoint(MinX, MinY), QPoint(MaxX, MaxY));
1445 };
1446
1447 QtConcurrent::blockingMap(Images, CalculateImageBounds);
1448
1449 QImage DummyImage(16, 16, QImage::Format_ARGB32);
1450 QPainter DummyPainter(&DummyImage);
1451
1452 DummyPainter.setFont(Font);
1453 QFontMetrics FontMetrics = DummyPainter.fontMetrics();
1454 int Ascent = FontMetrics.ascent();
1455
1456 int CurrentHeight = 0;
1457 int ImageWidth = MaxWidth;
1458
1459 for (lcPartsListImage& Image : Images)
1460 CurrentHeight = qMax(Image.Bounds.height() + Ascent, CurrentHeight);
1461
1462 for (;;)
1463 {
1464 int CurrentWidth = 0;
1465 int CurrentX = 0;
1466 int CurrentY = 0;
1467 int ColumnWidth = 0;
1468 int Spacing = 20;
1469 int NextHeightIncrease = INT_MAX;
1470
1471 for (lcPartsListImage& Image : Images)
1472 {
1473 if (CurrentY + Image.Bounds.height() + Ascent > CurrentHeight)
1474 {
1475 int NeededSpace = Image.Bounds.height() + Ascent - (CurrentHeight - CurrentY);
1476 NextHeightIncrease = qMin(NeededSpace, NextHeightIncrease);
1477
1478 CurrentY = 0;
1479 CurrentX += ColumnWidth + Spacing;
1480 ColumnWidth = 0;
1481 }
1482
1483 Image.Position = QPoint(CurrentX, CurrentY);
1484 CurrentY += Image.Bounds.height() + Ascent + Spacing;
1485 CurrentWidth = qMax(CurrentWidth, CurrentX + Image.Bounds.width());
1486 ColumnWidth = qMax(ColumnWidth, Image.Bounds.width());
1487 }
1488
1489 if (CurrentWidth <= MaxWidth)
1490 {
1491 ImageWidth = CurrentWidth;
1492 break;
1493 }
1494
1495 CurrentHeight += NextHeightIncrease;
1496 }
1497
1498 QImage PainterImage(ImageWidth + 40, CurrentHeight + 40, QImage::Format_ARGB32);
1499 PainterImage.fill(lcQColorFromRGBA(BackgroundColor));
1500
1501 QPainter Painter(&PainterImage);
1502 Painter.setFont(Font);
1503 Painter.setPen(TextColor);
1504
1505 for (lcPartsListImage& Image : Images)
1506 {
1507 QPoint Position = Image.Position + QPoint(20, 20);
1508 Painter.drawImage(Position, Image.Thumbnail, Image.Bounds);
1509 Painter.drawText(QPoint(Position.x(), Position.y() + Image.Bounds.height() + Ascent), QString::number(Image.Count) + 'x');
1510 }
1511
1512 Painter.end();
1513
1514 return PainterImage;
1515 }
1516
SaveStepImages(const QString & BaseName,bool AddStepSuffix,bool Zoom,int Width,int Height,lcStep Start,lcStep End)1517 void lcModel::SaveStepImages(const QString& BaseName, bool AddStepSuffix, bool Zoom, int Width, int Height, lcStep Start, lcStep End)
1518 {
1519 for (lcStep Step = Start; Step <= End; Step++)
1520 {
1521 QString FileName;
1522
1523 if (AddStepSuffix)
1524 FileName = BaseName.arg(Step, 2, 10, QLatin1Char('0'));
1525 else
1526 FileName = BaseName;
1527
1528 QImageWriter Writer(FileName);
1529
1530 if (Writer.format().isEmpty())
1531 Writer.setFormat("png");
1532
1533 QImage Image = GetStepImage(Zoom, Width, Height, Step);
1534 if (!Writer.write(Image))
1535 {
1536 QMessageBox::information(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, Writer.errorString()));
1537 break;
1538 }
1539 }
1540 }
1541
RayTest(lcObjectRayTest & ObjectRayTest) const1542 void lcModel::RayTest(lcObjectRayTest& ObjectRayTest) const
1543 {
1544 for (lcPiece* Piece : mPieces)
1545 if (Piece->IsVisible(mCurrentStep) && (!ObjectRayTest.IgnoreSelected || !Piece->IsSelected()))
1546 Piece->RayTest(ObjectRayTest);
1547
1548 if (ObjectRayTest.PiecesOnly)
1549 return;
1550
1551 for (lcCamera* Camera : mCameras)
1552 if (Camera != ObjectRayTest.ViewCamera && Camera->IsVisible() && (!ObjectRayTest.IgnoreSelected || !Camera->IsSelected()))
1553 Camera->RayTest(ObjectRayTest);
1554
1555 for (lcLight* Light : mLights)
1556 if (Light->IsVisible() && (!ObjectRayTest.IgnoreSelected || !Light->IsSelected()))
1557 Light->RayTest(ObjectRayTest);
1558 }
1559
BoxTest(lcObjectBoxTest & ObjectBoxTest) const1560 void lcModel::BoxTest(lcObjectBoxTest& ObjectBoxTest) const
1561 {
1562 for (lcPiece* Piece : mPieces)
1563 if (Piece->IsVisible(mCurrentStep))
1564 Piece->BoxTest(ObjectBoxTest);
1565
1566 for (lcCamera* Camera : mCameras)
1567 if (Camera != ObjectBoxTest.ViewCamera && Camera->IsVisible())
1568 Camera->BoxTest(ObjectBoxTest);
1569
1570 for (lcLight* Light : mLights)
1571 if (Light->IsVisible())
1572 Light->BoxTest(ObjectBoxTest);
1573 }
1574
SubModelMinIntersectDist(const lcVector3 & WorldStart,const lcVector3 & WorldEnd,float & MinDistance) const1575 bool lcModel::SubModelMinIntersectDist(const lcVector3& WorldStart, const lcVector3& WorldEnd, float& MinDistance) const
1576 {
1577 bool MinIntersect = false;
1578
1579 for (lcPiece* Piece : mPieces)
1580 {
1581 lcMatrix44 InverseWorldMatrix = lcMatrix44AffineInverse(Piece->mModelWorld);
1582 lcVector3 Start = lcMul31(WorldStart, InverseWorldMatrix);
1583 lcVector3 End = lcMul31(WorldEnd, InverseWorldMatrix);
1584
1585 if (Piece->IsVisibleInSubModel() && Piece->mPieceInfo->MinIntersectDist(Start, End, MinDistance)) // todo: this should check for piece->mMesh first
1586 MinIntersect = true;
1587 }
1588
1589 return MinIntersect;
1590 }
1591
SubModelBoxTest(const lcVector4 Planes[6]) const1592 bool lcModel::SubModelBoxTest(const lcVector4 Planes[6]) const
1593 {
1594 for (lcPiece* Piece : mPieces)
1595 if (Piece->IsVisibleInSubModel() && Piece->mPieceInfo->BoxTest(Piece->mModelWorld, Planes))
1596 return true;
1597
1598 return false;
1599 }
1600
SubModelCompareBoundingBox(const lcMatrix44 & WorldMatrix,lcVector3 & Min,lcVector3 & Max) const1601 void lcModel::SubModelCompareBoundingBox(const lcMatrix44& WorldMatrix, lcVector3& Min, lcVector3& Max) const
1602 {
1603 for (lcPiece* Piece : mPieces)
1604 if (Piece->IsVisibleInSubModel())
1605 Piece->SubModelCompareBoundingBox(WorldMatrix, Min, Max);
1606 }
1607
SubModelAddBoundingBoxPoints(const lcMatrix44 & WorldMatrix,std::vector<lcVector3> & Points) const1608 void lcModel::SubModelAddBoundingBoxPoints(const lcMatrix44& WorldMatrix, std::vector<lcVector3>& Points) const
1609 {
1610 for (lcPiece* Piece : mPieces)
1611 if (Piece->IsVisibleInSubModel())
1612 Piece->SubModelAddBoundingBoxPoints(WorldMatrix, Points);
1613 }
1614
SaveCheckpoint(const QString & Description)1615 void lcModel::SaveCheckpoint(const QString& Description)
1616 {
1617 lcModelHistoryEntry* ModelHistoryEntry = new lcModelHistoryEntry();
1618
1619 ModelHistoryEntry->Description = Description;
1620
1621 QTextStream Stream(&ModelHistoryEntry->File);
1622 SaveLDraw(Stream, false);
1623
1624 mUndoHistory.insert(mUndoHistory.begin(), ModelHistoryEntry);
1625 for (lcModelHistoryEntry* Entry : mRedoHistory)
1626 delete Entry;
1627 mRedoHistory.clear();
1628
1629 if (!Description.isEmpty())
1630 {
1631 gMainWindow->UpdateModified(IsModified());
1632 gMainWindow->UpdateUndoRedo(mUndoHistory.size() > 1 ? mUndoHistory[0]->Description : QString(), !mRedoHistory.empty() ? mRedoHistory[0]->Description : QString());
1633 }
1634 }
1635
LoadCheckPoint(lcModelHistoryEntry * CheckPoint)1636 void lcModel::LoadCheckPoint(lcModelHistoryEntry* CheckPoint)
1637 {
1638 lcPiecesLibrary* Library = lcGetPiecesLibrary();
1639 std::vector<PieceInfo*> LoadedInfos;
1640
1641 for (lcPiece* Piece : mPieces)
1642 {
1643 PieceInfo* Info = Piece->mPieceInfo;
1644 Library->LoadPieceInfo(Info, true, true);
1645 LoadedInfos.push_back(Info);
1646 }
1647
1648 DeleteModel();
1649
1650 QBuffer Buffer(&CheckPoint->File);
1651 Buffer.open(QIODevice::ReadOnly);
1652 LoadLDraw(Buffer, lcGetActiveProject());
1653
1654 gMainWindow->UpdateTimeline(true, false);
1655 gMainWindow->UpdateCameraMenu();
1656 gMainWindow->UpdateCurrentStep();
1657 gMainWindow->UpdateSelectedObjects(true);
1658 UpdateAllViews();
1659
1660 for (PieceInfo* Info : LoadedInfos)
1661 Library->ReleasePieceInfo(Info);
1662 }
1663
SetActive(bool Active)1664 void lcModel::SetActive(bool Active)
1665 {
1666 CalculateStep(Active ? mCurrentStep : LC_STEP_MAX);
1667 mActive = Active;
1668 }
1669
CalculateStep(lcStep Step)1670 void lcModel::CalculateStep(lcStep Step)
1671 {
1672 for (lcPiece* Piece : mPieces)
1673 {
1674 Piece->UpdatePosition(Step);
1675
1676 if (Piece->IsSelected())
1677 {
1678 if (!Piece->IsVisible(Step))
1679 Piece->SetSelected(false);
1680 else
1681 SelectGroup(Piece->GetTopGroup(), true);
1682 }
1683 }
1684
1685 for (lcCamera* Camera : mCameras)
1686 Camera->UpdatePosition(Step);
1687
1688 for (lcLight* Light : mLights)
1689 Light->UpdatePosition(Step);
1690 }
1691
SetCurrentStep(lcStep Step)1692 void lcModel::SetCurrentStep(lcStep Step)
1693 {
1694 mCurrentStep = Step;
1695 CalculateStep(Step);
1696
1697 gMainWindow->UpdateTimeline(false, false);
1698 gMainWindow->UpdateSelectedObjects(true);
1699 UpdateAllViews();
1700 gMainWindow->UpdateCurrentStep();
1701 }
1702
ShowFirstStep()1703 void lcModel::ShowFirstStep()
1704 {
1705 if (mCurrentStep == 1)
1706 return;
1707
1708 SetCurrentStep(1);
1709 }
1710
ShowLastStep()1711 void lcModel::ShowLastStep()
1712 {
1713 lcStep LastStep = GetLastStep();
1714
1715 if (mCurrentStep == LastStep)
1716 return;
1717
1718 SetCurrentStep(LastStep);
1719 }
1720
ShowPreviousStep()1721 void lcModel::ShowPreviousStep()
1722 {
1723 if (mCurrentStep == 1)
1724 return;
1725
1726 SetCurrentStep(mCurrentStep - 1);
1727 }
1728
ShowNextStep()1729 void lcModel::ShowNextStep()
1730 {
1731 if (mCurrentStep == LC_STEP_MAX)
1732 return;
1733
1734 SetCurrentStep(mCurrentStep + 1);
1735 }
1736
GetLastStep() const1737 lcStep lcModel::GetLastStep() const
1738 {
1739 lcStep Step = 1;
1740
1741 for (lcPiece* Piece : mPieces)
1742 Step = lcMax(Step, Piece->GetStepShow());
1743
1744 return Step;
1745 }
1746
InsertStep(lcStep Step)1747 void lcModel::InsertStep(lcStep Step)
1748 {
1749 for (lcPiece* Piece : mPieces)
1750 {
1751 Piece->InsertTime(Step, 1);
1752 if (Piece->IsSelected() && !Piece->IsVisible(mCurrentStep))
1753 Piece->SetSelected(false);
1754 }
1755
1756 for (lcCamera* Camera : mCameras)
1757 Camera->InsertTime(Step, 1);
1758
1759 for (lcLight* Light : mLights)
1760 Light->InsertTime(Step, 1);
1761
1762 SaveCheckpoint(tr("Inserting Step"));
1763 SetCurrentStep(mCurrentStep);
1764 }
1765
RemoveStep(lcStep Step)1766 void lcModel::RemoveStep(lcStep Step)
1767 {
1768 for (lcPiece* Piece : mPieces)
1769 {
1770 Piece->RemoveTime(Step, 1);
1771 if (Piece->IsSelected() && !Piece->IsVisible(mCurrentStep))
1772 Piece->SetSelected(false);
1773 }
1774
1775 for (lcCamera* Camera : mCameras)
1776 Camera->RemoveTime(Step, 1);
1777
1778 for (lcLight* Light : mLights)
1779 Light->RemoveTime(Step, 1);
1780
1781 SaveCheckpoint(tr("Removing Step"));
1782 SetCurrentStep(mCurrentStep);
1783 }
1784
AddGroup(const QString & Prefix,lcGroup * Parent)1785 lcGroup* lcModel::AddGroup(const QString& Prefix, lcGroup* Parent)
1786 {
1787 lcGroup* Group = new lcGroup();
1788 mGroups.Add(Group);
1789
1790 Group->mName = GetGroupName(Prefix);
1791 Group->mGroup = Parent;
1792
1793 return Group;
1794 }
1795
GetGroup(const QString & Name,bool CreateIfMissing)1796 lcGroup* lcModel::GetGroup(const QString& Name, bool CreateIfMissing)
1797 {
1798 for (lcGroup* Group : mGroups)
1799 if (Group->mName == Name)
1800 return Group;
1801
1802 if (CreateIfMissing)
1803 {
1804 lcGroup* Group = new lcGroup();
1805 Group->mName = Name;
1806 mGroups.Add(Group);
1807
1808 return Group;
1809 }
1810
1811 return nullptr;
1812 }
1813
RemoveGroup(lcGroup * Group)1814 void lcModel::RemoveGroup(lcGroup* Group)
1815 {
1816 mGroups.Remove(Group);
1817 delete Group;
1818 }
1819
GroupSelection()1820 void lcModel::GroupSelection()
1821 {
1822 if (!AnyPiecesSelected())
1823 {
1824 QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("No pieces selected."));
1825 return;
1826 }
1827
1828 lcQGroupDialog Dialog(gMainWindow, GetGroupName(tr("Group #")));
1829 if (Dialog.exec() != QDialog::Accepted)
1830 return;
1831
1832 lcGroup* NewGroup = GetGroup(Dialog.mName, true);
1833
1834 for (lcPiece* Piece : mPieces)
1835 {
1836 if (Piece->IsSelected())
1837 {
1838 lcGroup* Group = Piece->GetTopGroup();
1839
1840 if (!Group)
1841 Piece->SetGroup(NewGroup);
1842 else if (Group != NewGroup)
1843 Group->mGroup = NewGroup;
1844 }
1845 }
1846
1847 SaveCheckpoint(tr("Grouping"));
1848 }
1849
UngroupSelection()1850 void lcModel::UngroupSelection()
1851 {
1852 lcArray<lcGroup*> SelectedGroups;
1853
1854 for (lcPiece* Piece : mPieces)
1855 {
1856 if (Piece->IsSelected())
1857 {
1858 lcGroup* Group = Piece->GetTopGroup();
1859
1860 if (SelectedGroups.FindIndex(Group) == -1)
1861 {
1862 mGroups.Remove(Group);
1863 SelectedGroups.Add(Group);
1864 }
1865 }
1866 }
1867
1868 for (lcPiece* Piece : mPieces)
1869 {
1870 lcGroup* Group = Piece->GetGroup();
1871
1872 if (SelectedGroups.FindIndex(Group) != -1)
1873 Piece->SetGroup(nullptr);
1874 }
1875
1876 for (lcGroup* Group : mGroups)
1877 if (SelectedGroups.FindIndex(Group->mGroup) != -1)
1878 Group->mGroup = nullptr;
1879
1880 SelectedGroups.DeleteAll();
1881
1882 RemoveEmptyGroups();
1883 SaveCheckpoint(tr("Ungrouping"));
1884 }
1885
AddSelectedPiecesToGroup()1886 void lcModel::AddSelectedPiecesToGroup()
1887 {
1888 lcGroup* Group = nullptr;
1889
1890 for (lcPiece* Piece : mPieces)
1891 {
1892 if (Piece->IsSelected())
1893 {
1894 Group = Piece->GetTopGroup();
1895 if (Group)
1896 break;
1897 }
1898 }
1899
1900 if (Group)
1901 {
1902 for (lcPiece* Piece : mPieces)
1903 {
1904 if (Piece->IsFocused())
1905 {
1906 Piece->SetGroup(Group);
1907 break;
1908 }
1909 }
1910 }
1911
1912 RemoveEmptyGroups();
1913 SaveCheckpoint(tr("Grouping"));
1914 }
1915
RemoveFocusPieceFromGroup()1916 void lcModel::RemoveFocusPieceFromGroup()
1917 {
1918 for (lcPiece* Piece : mPieces)
1919 {
1920 if (Piece->IsFocused())
1921 {
1922 Piece->SetGroup(nullptr);
1923 break;
1924 }
1925 }
1926
1927 RemoveEmptyGroups();
1928 SaveCheckpoint(tr("Ungrouping"));
1929 }
1930
ShowEditGroupsDialog()1931 void lcModel::ShowEditGroupsDialog()
1932 {
1933 QMap<lcPiece*, lcGroup*> PieceParents;
1934 QMap<lcGroup*, lcGroup*> GroupParents;
1935
1936 for (lcPiece* Piece : mPieces)
1937 PieceParents[Piece] = Piece->GetGroup();
1938
1939 for (lcGroup* Group : mGroups)
1940 GroupParents[Group] = Group->mGroup;
1941
1942 lcQEditGroupsDialog Dialog(gMainWindow, PieceParents, GroupParents, this);
1943
1944 if (Dialog.exec() != QDialog::Accepted)
1945 return;
1946
1947 bool Modified = Dialog.mNewGroups.isEmpty();
1948
1949 for (lcPiece* Piece : mPieces)
1950 {
1951 lcGroup* ParentGroup = Dialog.mPieceParents.value(Piece);
1952
1953 if (ParentGroup != Piece->GetGroup())
1954 {
1955 Piece->SetGroup(ParentGroup);
1956 Modified = true;
1957 }
1958 }
1959
1960 for (lcGroup* Group : mGroups)
1961 {
1962 lcGroup* ParentGroup = Dialog.mGroupParents.value(Group);
1963
1964 if (ParentGroup != Group->mGroup)
1965 {
1966 Group->mGroup = ParentGroup;
1967 Modified = true;
1968 }
1969 }
1970
1971 if (Modified)
1972 {
1973 ClearSelection(true);
1974 SaveCheckpoint(tr("Editing Groups"));
1975 }
1976 }
1977
GetGroupName(const QString & Prefix)1978 QString lcModel::GetGroupName(const QString& Prefix)
1979 {
1980 int Length = Prefix.length();
1981 int Max = 0;
1982
1983 for (lcGroup* Group : mGroups)
1984 {
1985 const QString& Name = Group->mName;
1986
1987 if (Name.startsWith(Prefix))
1988 {
1989 bool Ok = false;
1990 int GroupNumber = Name.mid(Length).toInt(&Ok);
1991 if (Ok && GroupNumber > Max)
1992 Max = GroupNumber;
1993 }
1994 }
1995
1996 return Prefix + QString::number(Max + 1);
1997 }
1998
RemoveEmptyGroups()1999 void lcModel::RemoveEmptyGroups()
2000 {
2001 bool Removed;
2002
2003 do
2004 {
2005 Removed = false;
2006
2007 for (int GroupIdx = 0; GroupIdx < mGroups.GetSize();)
2008 {
2009 lcGroup* Group = mGroups[GroupIdx];
2010 int Ref = 0;
2011
2012 for (lcPiece* Piece : mPieces)
2013 if (Piece->GetGroup() == Group)
2014 Ref++;
2015
2016 for (int ParentIdx = 0; ParentIdx < mGroups.GetSize(); ParentIdx++)
2017 if (mGroups[ParentIdx]->mGroup == Group)
2018 Ref++;
2019
2020 if (Ref > 1)
2021 {
2022 GroupIdx++;
2023 continue;
2024 }
2025
2026 if (Ref != 0)
2027 {
2028 for (lcPiece* Piece : mPieces)
2029 {
2030 if (Piece->GetGroup() == Group)
2031 {
2032 Piece->SetGroup(Group->mGroup);
2033 break;
2034 }
2035 }
2036
2037 for (int ParentIdx = 0; ParentIdx < mGroups.GetSize(); ParentIdx++)
2038 {
2039 if (mGroups[ParentIdx]->mGroup == Group)
2040 {
2041 mGroups[ParentIdx]->mGroup = Group->mGroup;
2042 break;
2043 }
2044 }
2045 }
2046
2047 mGroups.RemoveIndex(GroupIdx);
2048 delete Group;
2049 Removed = true;
2050 }
2051 }
2052 while (Removed);
2053 }
2054
SnapPosition(const lcVector3 & Distance) const2055 lcVector3 lcModel::SnapPosition(const lcVector3& Distance) const
2056 {
2057 lcVector3 NewDistance(Distance);
2058
2059 float SnapXY = gMainWindow->GetMoveXYSnap();
2060 if (SnapXY != 0.0f)
2061 {
2062 int i = (int)(NewDistance[0] / SnapXY);
2063 float Leftover = NewDistance[0] - (SnapXY * i);
2064
2065 if (Leftover > SnapXY / 2)
2066 i++;
2067 else if (Leftover < -SnapXY / 2)
2068 i--;
2069
2070 NewDistance[0] = SnapXY * i;
2071
2072 i = (int)(NewDistance[1] / SnapXY);
2073 Leftover = NewDistance[1] - (SnapXY * i);
2074
2075 if (Leftover > SnapXY / 2)
2076 i++;
2077 else if (Leftover < -SnapXY / 2)
2078 i--;
2079
2080 NewDistance[1] = SnapXY * i;
2081 }
2082
2083 float SnapZ = gMainWindow->GetMoveZSnap();
2084 if (SnapZ != 0.0f)
2085 {
2086 int i = (int)(NewDistance[2] / SnapZ);
2087 float Leftover = NewDistance[2] - (SnapZ * i);
2088
2089 if (Leftover > SnapZ / 2)
2090 i++;
2091 else if (Leftover < -SnapZ / 2)
2092 i--;
2093
2094 NewDistance[2] = SnapZ * i;
2095 }
2096
2097 return NewDistance;
2098 }
2099
SnapRotation(const lcVector3 & Angles) const2100 lcVector3 lcModel::SnapRotation(const lcVector3& Angles) const
2101 {
2102 float AngleSnap = gMainWindow->GetAngleSnap();
2103 lcVector3 NewAngles(Angles);
2104
2105 if (AngleSnap != 0.0f)
2106 {
2107 int Snap[3];
2108
2109 for (int i = 0; i < 3; i++)
2110 Snap[i] = (int)(Angles[i] / AngleSnap);
2111
2112 NewAngles = lcVector3((float)(AngleSnap * Snap[0]), (float)(AngleSnap * Snap[1]), (float)(AngleSnap * Snap[2]));
2113 }
2114
2115 return NewAngles;
2116 }
2117
GetRelativeRotation() const2118 lcMatrix33 lcModel::GetRelativeRotation() const
2119 {
2120 if (gMainWindow->GetRelativeTransform())
2121 {
2122 lcObject* Focus = GetFocusObject();
2123
2124 if (Focus && Focus->IsPiece())
2125 return ((lcPiece*)Focus)->GetRelativeRotation();
2126 }
2127
2128 return lcMatrix33Identity();
2129 }
2130
AddPiece()2131 void lcModel::AddPiece()
2132 {
2133 PieceInfo* CurPiece = gMainWindow->GetCurrentPieceInfo();
2134
2135 if (!CurPiece)
2136 return;
2137
2138 lcPiece* Last = mPieces.IsEmpty() ? nullptr : mPieces[mPieces.GetSize() - 1];
2139
2140 for (lcPiece* Piece : mPieces)
2141 {
2142 if (Piece->IsFocused())
2143 {
2144 Last = Piece;
2145 break;
2146 }
2147 }
2148
2149 lcMatrix44 WorldMatrix;
2150
2151 if (Last)
2152 {
2153 const lcBoundingBox& BoundingBox = Last->GetBoundingBox();
2154 lcVector3 Dist(0, 0, BoundingBox.Max.z - BoundingBox.Min.z);
2155 Dist = SnapPosition(Dist);
2156
2157 WorldMatrix = Last->mModelWorld;
2158 WorldMatrix.SetTranslation(lcMul31(Dist, Last->mModelWorld));
2159 }
2160 else
2161 {
2162 const lcBoundingBox& BoundingBox = CurPiece->GetBoundingBox();
2163 WorldMatrix = lcMatrix44Translation(lcVector3(0.0f, 0.0f, -BoundingBox.Min.z));
2164 }
2165
2166 lcPiece* Piece = new lcPiece(CurPiece);
2167 Piece->Initialize(WorldMatrix, mCurrentStep);
2168 Piece->SetColorIndex(gMainWindow->mColorIndex);
2169 AddPiece(Piece);
2170 gMainWindow->UpdateTimeline(false, false);
2171 ClearSelectionAndSetFocus(Piece, LC_PIECE_SECTION_POSITION, false);
2172
2173 SaveCheckpoint(tr("Adding Piece"));
2174 }
2175
AddPiece(lcPiece * Piece)2176 void lcModel::AddPiece(lcPiece* Piece)
2177 {
2178 for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
2179 {
2180 if (mPieces[PieceIdx]->GetStepShow() > Piece->GetStepShow())
2181 {
2182 InsertPiece(Piece, PieceIdx);
2183 return;
2184 }
2185 }
2186
2187 InsertPiece(Piece, mPieces.GetSize());
2188 }
2189
InsertPiece(lcPiece * Piece,int Index)2190 void lcModel::InsertPiece(lcPiece* Piece, int Index)
2191 {
2192 PieceInfo* Info = Piece->mPieceInfo;
2193
2194 if (!Info->IsModel())
2195 {
2196 lcMesh* Mesh = Info->GetMesh();
2197
2198 if (Mesh && Mesh->mVertexCacheOffset == -1)
2199 lcGetPiecesLibrary()->mBuffersDirty = true;
2200 }
2201
2202 mPieces.InsertAt(Index, Piece);
2203 }
2204
DeleteAllCameras()2205 void lcModel::DeleteAllCameras()
2206 {
2207 if (mCameras.IsEmpty())
2208 return;
2209
2210 mCameras.DeleteAll();
2211
2212 gMainWindow->UpdateCameraMenu();
2213 gMainWindow->UpdateSelectedObjects(true);
2214 UpdateAllViews();
2215 SaveCheckpoint(tr("Resetting Cameras"));
2216 }
2217
DeleteSelectedObjects()2218 void lcModel::DeleteSelectedObjects()
2219 {
2220 if (RemoveSelectedObjects())
2221 {
2222 if (!mIsPreview) {
2223 gMainWindow->UpdateTimeline(false, false);
2224 gMainWindow->UpdateSelectedObjects(true);
2225 UpdateAllViews();
2226 SaveCheckpoint(tr("Deleting"));
2227 }
2228 }
2229 }
2230
ResetSelectedPiecesPivotPoint()2231 void lcModel::ResetSelectedPiecesPivotPoint()
2232 {
2233 for (lcPiece* Piece : mPieces)
2234 if (Piece->IsSelected())
2235 Piece->ResetPivotPoint();
2236
2237 UpdateAllViews();
2238 }
2239
RemoveSelectedPiecesKeyFrames()2240 void lcModel::RemoveSelectedPiecesKeyFrames()
2241 {
2242 for (lcPiece* Piece : mPieces)
2243 if (Piece->IsSelected())
2244 Piece->RemoveKeyFrames();
2245
2246 for (lcCamera* Camera : mCameras)
2247 if (Camera->IsSelected())
2248 Camera->RemoveKeyFrames();
2249
2250 for (lcLight* Light : mLights)
2251 if (Light->IsSelected())
2252 Light->RemoveKeyFrames();
2253
2254 UpdateAllViews();
2255 SaveCheckpoint(tr("Removing Key Frames"));
2256 }
2257
InsertControlPoint()2258 void lcModel::InsertControlPoint()
2259 {
2260 lcObject* Focus = GetFocusObject();
2261
2262 if (!Focus || !Focus->IsPiece())
2263 return;
2264
2265 lcPiece* Piece = (lcPiece*)Focus;
2266
2267 lcVector3 Start, End;
2268 gMainWindow->GetActiveView()->GetRayUnderPointer(Start, End);
2269
2270 if (Piece->InsertControlPoint(Start, End))
2271 {
2272 SaveCheckpoint(tr("Modifying"));
2273 gMainWindow->UpdateSelectedObjects(true);
2274 UpdateAllViews();
2275 }
2276 }
2277
RemoveFocusedControlPoint()2278 void lcModel::RemoveFocusedControlPoint()
2279 {
2280 lcObject* Focus = GetFocusObject();
2281
2282 if (!Focus || !Focus->IsPiece())
2283 return;
2284
2285 lcPiece* Piece = (lcPiece*)Focus;
2286
2287 if (Piece->RemoveFocusedControlPoint())
2288 {
2289 SaveCheckpoint(tr("Modifying"));
2290 gMainWindow->UpdateSelectedObjects(true);
2291 UpdateAllViews();
2292 }
2293 }
2294
ShowSelectedPiecesEarlier()2295 void lcModel::ShowSelectedPiecesEarlier()
2296 {
2297 lcArray<lcPiece*> MovedPieces;
2298
2299 for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
2300 {
2301 lcPiece* Piece = mPieces[PieceIdx];
2302
2303 if (Piece->IsSelected())
2304 {
2305 lcStep Step = Piece->GetStepShow();
2306
2307 if (Step > 1)
2308 {
2309 Step--;
2310 Piece->SetStepShow(Step);
2311
2312 MovedPieces.Add(Piece);
2313 mPieces.RemoveIndex(PieceIdx);
2314 continue;
2315 }
2316 }
2317
2318 PieceIdx++;
2319 }
2320
2321 if (MovedPieces.IsEmpty())
2322 return;
2323
2324 for (int PieceIdx = 0; PieceIdx < MovedPieces.GetSize(); PieceIdx++)
2325 {
2326 lcPiece* Piece = MovedPieces[PieceIdx];
2327 Piece->SetFileLine(-1);
2328 AddPiece(Piece);
2329 }
2330
2331 SaveCheckpoint(tr("Modifying"));
2332 gMainWindow->UpdateTimeline(false, false);
2333 gMainWindow->UpdateSelectedObjects(true);
2334 UpdateAllViews();
2335 }
2336
ShowSelectedPiecesLater()2337 void lcModel::ShowSelectedPiecesLater()
2338 {
2339 lcArray<lcPiece*> MovedPieces;
2340
2341 for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
2342 {
2343 lcPiece* Piece = mPieces[PieceIdx];
2344
2345 if (Piece->IsSelected())
2346 {
2347 lcStep Step = Piece->GetStepShow();
2348
2349 if (Step < LC_STEP_MAX)
2350 {
2351 Step++;
2352 Piece->SetStepShow(Step);
2353
2354 if (!Piece->IsVisible(mCurrentStep))
2355 Piece->SetSelected(false);
2356
2357 MovedPieces.Add(Piece);
2358 mPieces.RemoveIndex(PieceIdx);
2359 continue;
2360 }
2361 }
2362
2363 PieceIdx++;
2364 }
2365
2366 if (MovedPieces.IsEmpty())
2367 return;
2368
2369 for (int PieceIdx = 0; PieceIdx < MovedPieces.GetSize(); PieceIdx++)
2370 {
2371 lcPiece* Piece = MovedPieces[PieceIdx];
2372 Piece->SetFileLine(-1);
2373 AddPiece(Piece);
2374 }
2375
2376 SaveCheckpoint(tr("Modifying"));
2377 gMainWindow->UpdateTimeline(false, false);
2378 gMainWindow->UpdateSelectedObjects(true);
2379 UpdateAllViews();
2380 }
2381
SetPieceSteps(const QList<QPair<lcPiece *,lcStep>> & PieceSteps)2382 void lcModel::SetPieceSteps(const QList<QPair<lcPiece*, lcStep>>& PieceSteps)
2383 {
2384 if (PieceSteps.size() != mPieces.GetSize())
2385 return;
2386
2387 bool Modified = false;
2388
2389 for (int PieceIdx = 0; PieceIdx < PieceSteps.size(); PieceIdx++)
2390 {
2391 const QPair<lcPiece*, lcStep>& PieceStep = PieceSteps[PieceIdx];
2392 lcPiece* Piece = mPieces[PieceIdx];
2393
2394 if (Piece != PieceStep.first || Piece->GetStepShow() != PieceStep.second)
2395 {
2396 Piece = PieceStep.first;
2397 lcStep Step = PieceStep.second;
2398
2399 mPieces[PieceIdx] = Piece;
2400 Piece->SetStepShow(Step);
2401
2402 if (!Piece->IsVisible(mCurrentStep))
2403 Piece->SetSelected(false);
2404
2405 Modified = true;
2406 }
2407 }
2408
2409 if (Modified)
2410 {
2411 SaveCheckpoint(tr("Modifying"));
2412 UpdateAllViews();
2413 gMainWindow->UpdateTimeline(false, false);
2414 gMainWindow->UpdateSelectedObjects(true);
2415 }
2416 }
2417
RenamePiece(PieceInfo * Info)2418 void lcModel::RenamePiece(PieceInfo* Info)
2419 {
2420 for (lcPiece* Piece : mPieces)
2421 if (Piece->mPieceInfo == Info)
2422 Piece->UpdateID();
2423 }
2424
MoveSelectionToModel(lcModel * Model)2425 void lcModel::MoveSelectionToModel(lcModel* Model)
2426 {
2427 if (!Model)
2428 return;
2429
2430 lcArray<lcPiece*> Pieces;
2431 lcPiece* ModelPiece = nullptr;
2432 lcStep FirstStep = LC_STEP_MAX;
2433 lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
2434
2435 for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
2436 {
2437 lcPiece* Piece = mPieces[PieceIdx];
2438
2439 if (Piece->IsSelected())
2440 {
2441 Piece->CompareBoundingBox(Min, Max);
2442 mPieces.RemoveIndex(PieceIdx);
2443 Piece->SetGroup(nullptr); // todo: copy groups
2444 Pieces.Add(Piece);
2445 FirstStep = qMin(FirstStep, Piece->GetStepShow());
2446
2447 if (!ModelPiece)
2448 {
2449 ModelPiece = new lcPiece(Model->mPieceInfo);
2450 ModelPiece->SetColorIndex(gDefaultColor);
2451 InsertPiece(ModelPiece, PieceIdx);
2452 PieceIdx++;
2453 }
2454 }
2455 else
2456 PieceIdx++;
2457 }
2458
2459 lcVector3 ModelCenter = (Min + Max) / 2.0f;
2460 ModelCenter.z += (Min.z - Max.z) / 2.0f;
2461
2462 for (int PieceIdx = 0; PieceIdx < Pieces.GetSize(); PieceIdx++)
2463 {
2464 lcPiece* Piece = Pieces[PieceIdx];
2465 Piece->SetFileLine(-1);
2466 Piece->SetStepShow(Piece->GetStepShow() - FirstStep + 1);
2467 Piece->MoveSelected(Piece->GetStepShow(), false, -ModelCenter);
2468 Model->AddPiece(Piece);
2469 }
2470
2471 std::vector<lcModel*> UpdatedModels;
2472 Model->UpdatePieceInfo(UpdatedModels);
2473 if (ModelPiece)
2474 {
2475 ModelPiece->Initialize(lcMatrix44Translation(ModelCenter), FirstStep);
2476 ModelPiece->UpdatePosition(mCurrentStep);
2477 }
2478
2479 SaveCheckpoint(tr("New Model"));
2480 gMainWindow->UpdateTimeline(false, false);
2481 ClearSelectionAndSetFocus(ModelPiece, LC_PIECE_SECTION_POSITION, false);
2482 }
2483
InlineSelectedModels()2484 void lcModel::InlineSelectedModels()
2485 {
2486 lcArray<lcObject*> NewPieces;
2487
2488 for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
2489 {
2490 lcPiece* Piece = mPieces[PieceIdx];
2491
2492 if (!Piece->IsSelected() || !Piece->mPieceInfo->IsModel())
2493 {
2494 PieceIdx++;
2495 continue;
2496 }
2497
2498 mPieces.RemoveIndex(PieceIdx);
2499
2500 lcModel* Model = Piece->mPieceInfo->GetModel();
2501
2502 for (const lcPiece* ModelPiece : Model->mPieces)
2503 {
2504 lcPiece* NewPiece = new lcPiece(nullptr);
2505
2506 // todo: recreate in groups in the current model
2507
2508 int ColorIndex = ModelPiece->GetColorIndex();
2509
2510 if (ColorIndex == gDefaultColor)
2511 ColorIndex = Piece->GetColorIndex();
2512
2513 NewPiece->SetPieceInfo(ModelPiece->mPieceInfo, ModelPiece->GetID(), true);
2514 NewPiece->Initialize(lcMul(ModelPiece->mModelWorld, Piece->mModelWorld), Piece->GetStepShow());
2515 NewPiece->SetColorIndex(ColorIndex);
2516 NewPiece->UpdatePosition(mCurrentStep);
2517
2518 NewPieces.Add(NewPiece);
2519 InsertPiece(NewPiece, PieceIdx);
2520 PieceIdx++;
2521 }
2522
2523 delete Piece;
2524 }
2525
2526 if (!NewPieces.GetSize())
2527 {
2528 QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("No models selected."));
2529 return;
2530 }
2531
2532 SaveCheckpoint(tr("Inlining"));
2533 gMainWindow->UpdateTimeline(false, false);
2534 SetSelectionAndFocus(NewPieces, nullptr, 0, false);
2535 }
2536
RemoveSelectedObjects()2537 bool lcModel::RemoveSelectedObjects()
2538 {
2539 bool RemovedPiece = false;
2540 bool RemovedCamera = false;
2541 bool RemovedLight = false;
2542
2543 for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
2544 {
2545 lcPiece* Piece = mPieces[PieceIdx];
2546
2547 if (Piece->IsSelected())
2548 {
2549 RemovedPiece = true;
2550 mPieces.Remove(Piece);
2551 delete Piece;
2552 }
2553 else
2554 PieceIdx++;
2555 }
2556
2557 for (int CameraIdx = 0; CameraIdx < mCameras.GetSize(); )
2558 {
2559 lcCamera* Camera = mCameras[CameraIdx];
2560
2561 if (Camera->IsSelected())
2562 {
2563 std::vector<lcView*> Views = lcView::GetModelViews(this);
2564
2565 for (lcView* View : Views)
2566 if (Camera == View->GetCamera())
2567 View->SetCamera(Camera, true);
2568
2569 RemovedCamera = true;
2570 mCameras.RemoveIndex(CameraIdx);
2571 delete Camera;
2572 }
2573 else
2574 CameraIdx++;
2575 }
2576
2577 if (RemovedCamera)
2578 gMainWindow->UpdateCameraMenu();
2579
2580 for (int LightIdx = 0; LightIdx < mLights.GetSize(); )
2581 {
2582 lcLight* Light = mLights[LightIdx];
2583
2584 if (Light->IsSelected())
2585 {
2586 RemovedLight = true;
2587 mLights.RemoveIndex(LightIdx);
2588 delete Light;
2589 }
2590 else
2591 LightIdx++;
2592 }
2593
2594 RemoveEmptyGroups();
2595
2596 return RemovedPiece || RemovedCamera || RemovedLight;
2597 }
2598
MoveSelectedObjects(const lcVector3 & PieceDistance,const lcVector3 & ObjectDistance,bool AllowRelative,bool AlternateButtonDrag,bool Update,bool Checkpoint)2599 void lcModel::MoveSelectedObjects(const lcVector3& PieceDistance, const lcVector3& ObjectDistance, bool AllowRelative, bool AlternateButtonDrag, bool Update, bool Checkpoint)
2600 {
2601 bool Moved = false;
2602 lcMatrix33 RelativeRotation;
2603
2604 if (AllowRelative)
2605 RelativeRotation = GetRelativeRotation();
2606 else
2607 RelativeRotation = lcMatrix33Identity();
2608
2609 if (PieceDistance.LengthSquared() >= 0.001f)
2610 {
2611 lcVector3 TransformedPieceDistance = lcMul(PieceDistance, RelativeRotation);
2612
2613 if (AlternateButtonDrag)
2614 {
2615 for (lcPiece* Piece : mPieces)
2616 {
2617 if (Piece->IsFocused())
2618 {
2619 Piece->MovePivotPoint(TransformedPieceDistance);
2620 Moved = true;
2621 break;
2622 }
2623 }
2624 }
2625 else
2626 {
2627 for (lcPiece* Piece : mPieces)
2628 {
2629 if (Piece->IsSelected())
2630 {
2631 if (gMainWindow->GetSeparateTransform())
2632 TransformedPieceDistance = lcMul(PieceDistance, Piece->GetRelativeRotation());
2633
2634 Piece->MoveSelected(mCurrentStep, gMainWindow->GetAddKeys(), TransformedPieceDistance);
2635 Piece->UpdatePosition(mCurrentStep);
2636 Moved = true;
2637 }
2638 }
2639 }
2640 }
2641
2642 if (ObjectDistance.LengthSquared() >= 0.001f && !AlternateButtonDrag)
2643 {
2644 lcVector3 TransformedObjectDistance = lcMul(ObjectDistance, RelativeRotation);
2645
2646 for (lcCamera* Camera : mCameras)
2647 {
2648 if (Camera->IsSelected())
2649 {
2650 Camera->MoveSelected(mCurrentStep, gMainWindow->GetAddKeys(), TransformedObjectDistance);
2651 Camera->UpdatePosition(mCurrentStep);
2652 Moved = true;
2653 }
2654 }
2655
2656 for (lcLight* Light : mLights)
2657 {
2658 if (Light->IsSelected())
2659 {
2660 Light->MoveSelected(mCurrentStep, gMainWindow->GetAddKeys(), TransformedObjectDistance);
2661 Light->UpdatePosition(mCurrentStep);
2662 Moved = true;
2663 }
2664 }
2665 }
2666
2667 if (Moved && Update)
2668 {
2669 UpdateAllViews();
2670 if (Checkpoint)
2671 SaveCheckpoint(tr("Moving"));
2672 gMainWindow->UpdateSelectedObjects(false);
2673 }
2674 }
2675
RotateSelectedPieces(const lcVector3 & Angles,bool Relative,bool RotatePivotPoint,bool Update,bool Checkpoint)2676 void lcModel::RotateSelectedPieces(const lcVector3& Angles, bool Relative, bool RotatePivotPoint, bool Update, bool Checkpoint)
2677 {
2678 if (Angles.LengthSquared() < 0.001f)
2679 return;
2680
2681 lcMatrix33 RotationMatrix = lcMatrix33Identity();
2682 bool Rotated = false;
2683
2684 if (Angles[0] != 0.0f)
2685 RotationMatrix = lcMul(lcMatrix33RotationX(Angles[0] * LC_DTOR), RotationMatrix);
2686
2687 if (Angles[1] != 0.0f)
2688 RotationMatrix = lcMul(lcMatrix33RotationY(Angles[1] * LC_DTOR), RotationMatrix);
2689
2690 if (Angles[2] != 0.0f)
2691 RotationMatrix = lcMul(lcMatrix33RotationZ(Angles[2] * LC_DTOR), RotationMatrix);
2692
2693 if (RotatePivotPoint)
2694 {
2695 lcObject* Focus = GetFocusObject();
2696
2697 if (Focus && Focus->IsPiece())
2698 {
2699 ((lcPiece*)Focus)->RotatePivotPoint(RotationMatrix);
2700 Rotated = true;
2701 }
2702 }
2703 else
2704 {
2705 if (!gMainWindow->GetSeparateTransform())
2706 {
2707 lcVector3 Center;
2708 lcMatrix33 RelativeRotation;
2709
2710 GetMoveRotateTransform(Center, RelativeRotation);
2711
2712 lcMatrix33 WorldToFocusMatrix;
2713
2714 if (Relative)
2715 {
2716 WorldToFocusMatrix = lcMatrix33AffineInverse(RelativeRotation);
2717 RotationMatrix = lcMul(RotationMatrix, RelativeRotation);
2718 }
2719 else
2720 WorldToFocusMatrix = lcMatrix33Identity();
2721
2722 for (lcPiece* Piece : mPieces)
2723 {
2724 if (!Piece->IsSelected())
2725 continue;
2726
2727 Piece->Rotate(mCurrentStep, gMainWindow->GetAddKeys(), RotationMatrix, Center, WorldToFocusMatrix);
2728 Piece->UpdatePosition(mCurrentStep);
2729 Rotated = true;
2730 }
2731 }
2732 else
2733 {
2734 for (lcPiece* Piece : mPieces)
2735 {
2736 if (!Piece->IsSelected())
2737 continue;
2738
2739 const lcVector3 Center = Piece->GetRotationCenter();
2740 lcMatrix33 WorldToFocusMatrix;
2741 lcMatrix33 RelativeRotationMatrix;
2742
2743 if (Relative)
2744 {
2745 lcMatrix33 RelativeRotation = Piece->GetRelativeRotation();
2746 WorldToFocusMatrix = lcMatrix33AffineInverse(RelativeRotation);
2747 RelativeRotationMatrix = lcMul(RotationMatrix, RelativeRotation);
2748 }
2749 else
2750 {
2751 WorldToFocusMatrix = lcMatrix33Identity();
2752 RelativeRotationMatrix = RotationMatrix;
2753 }
2754
2755 Piece->Rotate(mCurrentStep, gMainWindow->GetAddKeys(), RelativeRotationMatrix, Center, WorldToFocusMatrix);
2756 Piece->UpdatePosition(mCurrentStep);
2757 Rotated = true;
2758 }
2759 }
2760 }
2761
2762 if (Rotated && Update)
2763 {
2764 UpdateAllViews();
2765 if (Checkpoint)
2766 SaveCheckpoint(tr("Rotating"));
2767 gMainWindow->UpdateSelectedObjects(false);
2768 }
2769 }
2770
ScaleSelectedPieces(const float Scale,bool Update,bool Checkpoint)2771 void lcModel::ScaleSelectedPieces(const float Scale, bool Update, bool Checkpoint)
2772 {
2773 if (Scale < 0.001f)
2774 return;
2775
2776 lcObject* Focus = GetFocusObject();
2777 if (!Focus || !Focus->IsPiece())
2778 return;
2779
2780 lcPiece* Piece = (lcPiece*)Focus;
2781 quint32 Section = Piece->GetFocusSection();
2782
2783 if (Section >= LC_PIECE_SECTION_CONTROL_POINT_FIRST && Section <= LC_PIECE_SECTION_CONTROL_POINT_LAST)
2784 {
2785 int ControlPointIndex = Section - LC_PIECE_SECTION_CONTROL_POINT_FIRST;
2786 Piece->SetControlPointScale(ControlPointIndex, Scale);
2787
2788 if (Update)
2789 {
2790 UpdateAllViews();
2791 if (Checkpoint)
2792 SaveCheckpoint(tr("Scaling"));
2793 gMainWindow->UpdateSelectedObjects(false);
2794 }
2795 }
2796 }
2797
TransformSelectedObjects(lcTransformType TransformType,const lcVector3 & Transform)2798 void lcModel::TransformSelectedObjects(lcTransformType TransformType, const lcVector3& Transform)
2799 {
2800 switch (TransformType)
2801 {
2802 case lcTransformType::AbsoluteTranslation:
2803 MoveSelectedObjects(Transform, false, false, true, true);
2804 break;
2805
2806 case lcTransformType::RelativeTranslation:
2807 MoveSelectedObjects(Transform, true, false, true, true);
2808 break;
2809
2810 case lcTransformType::AbsoluteRotation:
2811 RotateSelectedPieces(Transform, false, false, true, true);
2812 break;
2813
2814 case lcTransformType::RelativeRotation:
2815 RotateSelectedPieces(Transform, true, false, true, true);
2816 break;
2817
2818 case lcTransformType::Count:
2819 break;
2820 }
2821 }
2822
SetSelectedPiecesColorIndex(int ColorIndex)2823 void lcModel::SetSelectedPiecesColorIndex(int ColorIndex)
2824 {
2825 bool Modified = false;
2826
2827 for (lcPiece* Piece : mPieces)
2828 {
2829 if (Piece->IsSelected() && Piece->GetColorIndex() != ColorIndex)
2830 {
2831 Piece->SetColorIndex(ColorIndex);
2832 Modified = true;
2833 }
2834 }
2835
2836 if (Modified)
2837 {
2838 SaveCheckpoint(tr("Painting"));
2839 gMainWindow->UpdateSelectedObjects(false);
2840 UpdateAllViews();
2841 gMainWindow->UpdateTimeline(false, true);
2842 }
2843 }
2844
SetSelectedPiecesPieceInfo(PieceInfo * Info)2845 void lcModel::SetSelectedPiecesPieceInfo(PieceInfo* Info)
2846 {
2847 bool Modified = false;
2848
2849 for (lcPiece* Piece : mPieces)
2850 {
2851 if (Piece->IsSelected() && Piece->mPieceInfo != Info)
2852 {
2853 lcPiecesLibrary* Library = lcGetPiecesLibrary();
2854 Library->ReleasePieceInfo(Piece->mPieceInfo);
2855 Piece->SetPieceInfo(Info, QString(), true);
2856 Modified = true;
2857 }
2858 }
2859
2860 if (Modified)
2861 {
2862 SaveCheckpoint(tr("Setting Part"));
2863 gMainWindow->UpdateSelectedObjects(false);
2864 UpdateAllViews();
2865 gMainWindow->UpdateTimeline(false, true);
2866 }
2867 }
2868
SetSelectedPiecesStepShow(lcStep Step)2869 void lcModel::SetSelectedPiecesStepShow(lcStep Step)
2870 {
2871 lcArray<lcPiece*> MovedPieces;
2872 bool SelectionChanged = false;
2873
2874 for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
2875 {
2876 lcPiece* Piece = mPieces[PieceIdx];
2877
2878 if (Piece->IsSelected() && Piece->GetStepShow() != Step)
2879 {
2880 Piece->SetStepShow(Step);
2881
2882 if (!Piece->IsVisible(mCurrentStep))
2883 {
2884 Piece->SetSelected(false);
2885 SelectionChanged = true;
2886 }
2887
2888 MovedPieces.Add(Piece);
2889 mPieces.RemoveIndex(PieceIdx);
2890 continue;
2891 }
2892
2893 PieceIdx++;
2894 }
2895
2896 if (MovedPieces.IsEmpty())
2897 return;
2898
2899 for (int PieceIdx = 0; PieceIdx < MovedPieces.GetSize(); PieceIdx++)
2900 {
2901 lcPiece* Piece = MovedPieces[PieceIdx];
2902 Piece->SetFileLine(-1);
2903 AddPiece(Piece);
2904 }
2905
2906 SaveCheckpoint(tr("Showing Pieces"));
2907 UpdateAllViews();
2908 gMainWindow->UpdateTimeline(false, false);
2909 gMainWindow->UpdateSelectedObjects(SelectionChanged);
2910 }
2911
SetSelectedPiecesStepHide(lcStep Step)2912 void lcModel::SetSelectedPiecesStepHide(lcStep Step)
2913 {
2914 bool Modified = false;
2915 bool SelectionChanged = false;
2916
2917 for (lcPiece* Piece : mPieces)
2918 {
2919 if (Piece->IsSelected() && Piece->GetStepHide() != Step)
2920 {
2921 Piece->SetStepHide(Step);
2922
2923 if (!Piece->IsVisible(mCurrentStep))
2924 {
2925 Piece->SetSelected(false);
2926 SelectionChanged = true;
2927 }
2928
2929 Modified = true;
2930 }
2931 }
2932
2933 if (Modified)
2934 {
2935 SaveCheckpoint(tr("Hiding Pieces"));
2936 UpdateAllViews();
2937 gMainWindow->UpdateTimeline(false, false);
2938 gMainWindow->UpdateSelectedObjects(SelectionChanged);
2939 }
2940 }
2941
SetCameraOrthographic(lcCamera * Camera,bool Ortho)2942 void lcModel::SetCameraOrthographic(lcCamera* Camera, bool Ortho)
2943 {
2944 if (Camera->IsOrtho() == Ortho)
2945 return;
2946
2947 Camera->SetOrtho(Ortho);
2948 Camera->UpdatePosition(mCurrentStep);
2949
2950 SaveCheckpoint(tr("Editing Camera"));
2951 UpdateAllViews();
2952 gMainWindow->UpdatePerspective();
2953 }
2954
SetCameraFOV(lcCamera * Camera,float FOV)2955 void lcModel::SetCameraFOV(lcCamera* Camera, float FOV)
2956 {
2957 if (Camera->m_fovy == FOV)
2958 return;
2959
2960 Camera->m_fovy = FOV;
2961 Camera->UpdatePosition(mCurrentStep);
2962
2963 SaveCheckpoint(tr("Changing FOV"));
2964 UpdateAllViews();
2965 }
2966
SetCameraZNear(lcCamera * Camera,float ZNear)2967 void lcModel::SetCameraZNear(lcCamera* Camera, float ZNear)
2968 {
2969 if (Camera->m_zNear == ZNear)
2970 return;
2971
2972 Camera->m_zNear = ZNear;
2973 Camera->UpdatePosition(mCurrentStep);
2974
2975 SaveCheckpoint(tr("Editing Camera"));
2976 UpdateAllViews();
2977 }
2978
SetCameraZFar(lcCamera * Camera,float ZFar)2979 void lcModel::SetCameraZFar(lcCamera* Camera, float ZFar)
2980 {
2981 if (Camera->m_zFar == ZFar)
2982 return;
2983
2984 Camera->m_zFar = ZFar;
2985 Camera->UpdatePosition(mCurrentStep);
2986
2987 SaveCheckpoint(tr("Editing Camera"));
2988 UpdateAllViews();
2989 }
2990
SetCameraName(lcCamera * Camera,const QString & Name)2991 void lcModel::SetCameraName(lcCamera* Camera, const QString& Name)
2992 {
2993 if (Camera->GetName() == Name)
2994 return;
2995
2996 Camera->SetName(Name);
2997
2998 SaveCheckpoint(tr("Renaming Camera"));
2999 gMainWindow->UpdateSelectedObjects(false);
3000 UpdateAllViews();
3001 gMainWindow->UpdateCameraMenu();
3002 }
3003
AnyPiecesSelected() const3004 bool lcModel::AnyPiecesSelected() const
3005 {
3006 for (lcPiece* Piece : mPieces)
3007 if (Piece->IsSelected())
3008 return true;
3009
3010 return false;
3011 }
3012
AnyObjectsSelected() const3013 bool lcModel::AnyObjectsSelected() const
3014 {
3015 for (lcPiece* Piece : mPieces)
3016 if (Piece->IsSelected())
3017 return true;
3018
3019 for (lcCamera* Camera : mCameras)
3020 if (Camera->IsSelected())
3021 return true;
3022
3023 for (lcLight* Light : mLights)
3024 if (Light->IsSelected())
3025 return true;
3026
3027 return false;
3028 }
3029
GetFocusObject() const3030 lcObject* lcModel::GetFocusObject() const
3031 {
3032 for (lcPiece* Piece : mPieces)
3033 if (Piece->IsFocused())
3034 return Piece;
3035
3036 for (lcCamera* Camera : mCameras)
3037 if (Camera->IsFocused())
3038 return Camera;
3039
3040 for (lcLight* Light : mLights)
3041 if (Light->IsFocused())
3042 return Light;
3043
3044 return nullptr;
3045 }
3046
GetFirstSelectedSubmodel() const3047 lcModel* lcModel::GetFirstSelectedSubmodel() const
3048 {
3049 for (lcPiece* Piece : mPieces)
3050 if (Piece->IsSelected() && Piece->mPieceInfo->IsModel())
3051 return Piece->mPieceInfo->GetModel();
3052
3053 return nullptr;
3054 }
3055
GetSubModels(lcArray<lcModel * > & SubModels) const3056 void lcModel::GetSubModels(lcArray<lcModel*>& SubModels) const
3057 {
3058 for (lcPiece* Piece : mPieces)
3059 {
3060 if (Piece->mPieceInfo->IsModel())
3061 {
3062 lcModel* SubModel = Piece->mPieceInfo->GetModel();
3063 if (SubModels.FindIndex(SubModel) == -1)
3064 SubModels.Add(SubModel);
3065 }
3066 }
3067 }
3068
GetMoveRotateTransform(lcVector3 & Center,lcMatrix33 & RelativeRotation) const3069 bool lcModel::GetMoveRotateTransform(lcVector3& Center, lcMatrix33& RelativeRotation) const
3070 {
3071 bool Relative = gMainWindow->GetRelativeTransform();
3072 int NumSelected = 0;
3073
3074 Center = lcVector3(0.0f, 0.0f, 0.0f);
3075 RelativeRotation = lcMatrix33Identity();
3076
3077 for (lcPiece* Piece : mPieces)
3078 {
3079 if (!Piece->IsSelected())
3080 continue;
3081
3082 if (Piece->IsFocused() && Relative)
3083 {
3084 Center = Piece->GetRotationCenter();
3085 RelativeRotation = Piece->GetRelativeRotation();
3086 return true;
3087 }
3088
3089 Center += Piece->mModelWorld.GetTranslation();
3090 NumSelected++;
3091 }
3092
3093 for (lcCamera* Camera : mCameras)
3094 {
3095 if (!Camera->IsSelected())
3096 continue;
3097
3098 if (Camera->IsFocused() && Relative)
3099 {
3100 Center = Camera->GetSectionPosition(Camera->GetFocusSection());
3101 // RelativeRotation = Piece->GetRelativeRotation();
3102 return true;
3103 }
3104
3105 Center += Camera->GetSectionPosition(LC_CAMERA_SECTION_POSITION);
3106 Center += Camera->GetSectionPosition(LC_CAMERA_SECTION_TARGET);
3107 Center += Camera->GetSectionPosition(LC_CAMERA_SECTION_UPVECTOR);
3108 NumSelected += 3;
3109 }
3110
3111 for (lcLight* Light : mLights)
3112 {
3113 if (!Light->IsSelected())
3114 continue;
3115
3116 if (Light->IsFocused() && Relative)
3117 {
3118 Center = Light->GetSectionPosition(Light->GetFocusSection());
3119 // RelativeRotation = Piece->GetRelativeRotation();
3120 return true;
3121 }
3122
3123 Center += Light->GetSectionPosition(LC_LIGHT_SECTION_POSITION);
3124 NumSelected++;
3125 if (Light->IsSpotLight() || Light->IsDirectionalLight())
3126 {
3127 Center += Light->GetSectionPosition(LC_LIGHT_SECTION_TARGET);
3128 NumSelected++;
3129 }
3130 }
3131
3132 if (NumSelected)
3133 {
3134 Center /= NumSelected;
3135 return true;
3136 }
3137
3138 return false;
3139 }
3140
GetPieceFocusOrSelectionCenter(lcVector3 & Center) const3141 bool lcModel::GetPieceFocusOrSelectionCenter(lcVector3& Center) const
3142 {
3143 lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
3144 lcPiece* Selected = nullptr;
3145 int NumSelected = 0;
3146
3147 for (lcPiece* Piece : mPieces)
3148 {
3149 if (Piece->IsFocused())
3150 {
3151 Center = Piece->mModelWorld.GetTranslation();
3152 return true;
3153 }
3154
3155 if (Piece->IsSelected())
3156 {
3157 Piece->CompareBoundingBox(Min, Max);
3158 Selected = Piece;
3159 NumSelected++;
3160 }
3161 }
3162
3163 if (NumSelected == 1)
3164 Center = Selected->mModelWorld.GetTranslation();
3165 else if (NumSelected)
3166 Center = (Min + Max) / 2.0f;
3167 else
3168 Center = lcVector3(0.0f, 0.0f, 0.0f);
3169
3170 return NumSelected != 0;
3171 }
3172
GetSelectionOrModelCenter() const3173 lcVector3 lcModel::GetSelectionOrModelCenter() const
3174 {
3175 lcVector3 Center;
3176
3177 if (!GetSelectionCenter(Center))
3178 {
3179 lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
3180
3181 if (GetVisiblePiecesBoundingBox(Min, Max))
3182 Center = (Min + Max) / 2.0f;
3183 else
3184 Center = lcVector3(0.0f, 0.0f, 0.0f);
3185 }
3186
3187 return Center;
3188 }
3189
GetFocusPosition(lcVector3 & Position) const3190 bool lcModel::GetFocusPosition(lcVector3& Position) const
3191 {
3192 lcObject* FocusObject = GetFocusObject();
3193
3194 if (FocusObject)
3195 {
3196 Position = FocusObject->GetSectionPosition(FocusObject->GetFocusSection());
3197 return true;
3198 }
3199 else
3200 {
3201 Position = lcVector3(0.0f, 0.0f, 0.0f);
3202 return false;
3203 }
3204 }
3205
GetSelectionCenter(lcVector3 & Center) const3206 bool lcModel::GetSelectionCenter(lcVector3& Center) const
3207 {
3208 lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
3209 lcPiece* SelectedPiece = nullptr;
3210 bool SinglePieceSelected = true;
3211 bool Selected = false;
3212
3213 for (lcPiece* Piece : mPieces)
3214 {
3215 if (Piece->IsSelected())
3216 {
3217 Piece->CompareBoundingBox(Min, Max);
3218 Selected = true;
3219
3220 if (!SelectedPiece)
3221 SelectedPiece = Piece;
3222 else
3223 SinglePieceSelected = false;
3224 }
3225 }
3226
3227 for (lcCamera* Camera : mCameras)
3228 {
3229 if (Camera->IsSelected())
3230 {
3231 Camera->CompareBoundingBox(Min, Max);
3232 Selected = true;
3233 SinglePieceSelected = false;
3234 }
3235 }
3236
3237 for (lcLight* Light : mLights)
3238 {
3239 if (Light->IsSelected())
3240 {
3241 Light->CompareBoundingBox(Min, Max);
3242 Selected = true;
3243 SinglePieceSelected = false;
3244 }
3245 }
3246
3247 if (SelectedPiece && SinglePieceSelected)
3248 Center = SelectedPiece->GetSectionPosition(LC_PIECE_SECTION_POSITION);
3249 else if (Selected)
3250 Center = (Min + Max) / 2.0f;
3251 else
3252 Center = lcVector3(0.0f, 0.0f, 0.0f);
3253
3254 return Selected;
3255 }
3256
GetAllPiecesBoundingBox() const3257 lcBoundingBox lcModel::GetAllPiecesBoundingBox() const
3258 {
3259 lcBoundingBox Box;
3260
3261 if (!mPieces.IsEmpty())
3262 {
3263 Box.Min = lcVector3(FLT_MAX, FLT_MAX, FLT_MAX);
3264 Box.Max = lcVector3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
3265
3266 for (lcPiece* Piece : mPieces)
3267 Piece->CompareBoundingBox(Box.Min, Box.Max);
3268 }
3269 else
3270 Box.Min = Box.Max = lcVector3(0.0f, 0.0f, 0.0f);
3271
3272 return Box;
3273 }
3274
GetVisiblePiecesBoundingBox(lcVector3 & Min,lcVector3 & Max) const3275 bool lcModel::GetVisiblePiecesBoundingBox(lcVector3& Min, lcVector3& Max) const
3276 {
3277 bool Valid = false;
3278 Min = lcVector3(FLT_MAX, FLT_MAX, FLT_MAX);
3279 Max = lcVector3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
3280
3281 for (lcPiece* Piece : mPieces)
3282 {
3283 if (Piece->IsVisible(mCurrentStep))
3284 {
3285 Piece->CompareBoundingBox(Min, Max);
3286 Valid = true;
3287 }
3288 }
3289
3290 return Valid;
3291 }
3292
GetPiecesBoundingBoxPoints() const3293 std::vector<lcVector3> lcModel::GetPiecesBoundingBoxPoints() const
3294 {
3295 std::vector<lcVector3> Points;
3296
3297 for (lcPiece* Piece : mPieces)
3298 if (Piece->IsVisible(mCurrentStep))
3299 Piece->SubModelAddBoundingBoxPoints(lcMatrix44Identity(), Points);
3300
3301 return Points;
3302 }
3303
GetPartsList(int DefaultColorIndex,bool ScanSubModels,bool AddSubModels,lcPartsList & PartsList) const3304 void lcModel::GetPartsList(int DefaultColorIndex, bool ScanSubModels, bool AddSubModels, lcPartsList& PartsList) const
3305 {
3306 for (lcPiece* Piece : mPieces)
3307 {
3308 if (!Piece->IsVisibleInSubModel())
3309 continue;
3310
3311 int ColorIndex = Piece->GetColorIndex();
3312
3313 if (ColorIndex == gDefaultColor)
3314 ColorIndex = DefaultColorIndex;
3315
3316 Piece->mPieceInfo->GetPartsList(ColorIndex, ScanSubModels, AddSubModels, PartsList);
3317 }
3318 }
3319
GetPartsListForStep(lcStep Step,int DefaultColorIndex,lcPartsList & PartsList) const3320 void lcModel::GetPartsListForStep(lcStep Step, int DefaultColorIndex, lcPartsList& PartsList) const
3321 {
3322 for (lcPiece* Piece : mPieces)
3323 {
3324 if (Piece->GetStepShow() != Step || Piece->IsHidden())
3325 continue;
3326
3327 int ColorIndex = Piece->GetColorIndex();
3328
3329 if (ColorIndex == gDefaultColor)
3330 ColorIndex = DefaultColorIndex;
3331
3332 Piece->mPieceInfo->GetPartsList(ColorIndex, false, true, PartsList);
3333 }
3334 }
3335
GetModelParts(const lcMatrix44 & WorldMatrix,int DefaultColorIndex,std::vector<lcModelPartsEntry> & ModelParts) const3336 void lcModel::GetModelParts(const lcMatrix44& WorldMatrix, int DefaultColorIndex, std::vector<lcModelPartsEntry>& ModelParts) const
3337 {
3338 for (lcPiece* Piece : mPieces)
3339 Piece->GetModelParts(WorldMatrix, DefaultColorIndex, ModelParts);
3340 }
3341
GetSelectionInformation(int * Flags,lcArray<lcObject * > & Selection,lcObject ** Focus) const3342 void lcModel::GetSelectionInformation(int* Flags, lcArray<lcObject*>& Selection, lcObject** Focus) const
3343 {
3344 *Flags = 0;
3345 *Focus = nullptr;
3346
3347 if (mPieces.IsEmpty())
3348 *Flags |= LC_SEL_NO_PIECES;
3349 else
3350 {
3351 lcGroup* Group = nullptr;
3352 bool First = true;
3353
3354 for (lcPiece* Piece : mPieces)
3355 {
3356 if (Piece->IsSelected())
3357 {
3358 Selection.Add(Piece);
3359
3360 if (Piece->IsFocused())
3361 *Focus = Piece;
3362
3363 if (Piece->mPieceInfo->IsModel())
3364 *Flags |= LC_SEL_MODEL_SELECTED;
3365
3366 if (Piece->IsHidden())
3367 *Flags |= LC_SEL_HIDDEN | LC_SEL_HIDDEN_SELECTED;
3368 else
3369 *Flags |= LC_SEL_VISIBLE_SELECTED;
3370
3371 *Flags |= LC_SEL_PIECE | LC_SEL_SELECTED;
3372
3373 if (Piece->CanAddControlPoint())
3374 *Flags |= LC_SEL_CAN_ADD_CONTROL_POINT;
3375
3376 if (Piece->CanRemoveControlPoint())
3377 *Flags |= LC_SEL_CAN_REMOVE_CONTROL_POINT;
3378
3379 if (Piece->GetGroup() != nullptr)
3380 {
3381 *Flags |= LC_SEL_GROUPED;
3382 if (Piece->IsFocused())
3383 *Flags |= LC_SEL_FOCUS_GROUPED;
3384 }
3385
3386 if (First)
3387 {
3388 Group = Piece->GetGroup();
3389 First = false;
3390 }
3391 else
3392 {
3393 if (Group != Piece->GetGroup())
3394 *Flags |= LC_SEL_CAN_GROUP;
3395 else
3396 if (Group == nullptr)
3397 *Flags |= LC_SEL_CAN_GROUP;
3398 }
3399 }
3400 else
3401 {
3402 *Flags |= LC_SEL_UNSELECTED;
3403
3404 if (Piece->IsHidden())
3405 *Flags |= LC_SEL_HIDDEN;
3406 }
3407 }
3408 }
3409
3410 for (lcCamera* Camera : mCameras)
3411 {
3412 if (Camera->IsSelected())
3413 {
3414 Selection.Add(Camera);
3415 *Flags |= LC_SEL_SELECTED;
3416
3417 if (Camera->IsFocused())
3418 *Focus = Camera;
3419 }
3420 }
3421
3422 for (lcLight* Light : mLights)
3423 {
3424 if (Light->IsSelected())
3425 {
3426 Selection.Add(Light);
3427 *Flags |= LC_SEL_SELECTED;
3428
3429 if (Light->IsFocused())
3430 *Focus = Light;
3431 }
3432 }
3433 }
3434
GetSelectionModePieces(lcPiece * SelectedPiece) const3435 lcArray<lcObject*> lcModel::GetSelectionModePieces(lcPiece* SelectedPiece) const
3436 {
3437 PieceInfo* Info = SelectedPiece->mPieceInfo;
3438 int ColorIndex = SelectedPiece->GetColorIndex();
3439 lcArray<lcObject*> Pieces;
3440
3441 switch (gMainWindow->GetSelectionMode())
3442 {
3443 case lcSelectionMode::Single:
3444 break;
3445
3446 case lcSelectionMode::Piece:
3447 for (lcPiece* Piece : mPieces)
3448 if (Piece->IsVisible(mCurrentStep) && Piece->mPieceInfo == Info && Piece != SelectedPiece)
3449 Pieces.Add(Piece);
3450 break;
3451
3452 case lcSelectionMode::Color:
3453 for (lcPiece* Piece : mPieces)
3454 if (Piece->IsVisible(mCurrentStep) && Piece->GetColorIndex() == ColorIndex && Piece != SelectedPiece)
3455 Pieces.Add(Piece);
3456 break;
3457
3458 case lcSelectionMode::PieceColor:
3459 for (lcPiece* Piece : mPieces)
3460 if (Piece->IsVisible(mCurrentStep) && Piece->mPieceInfo == Info && Piece->GetColorIndex() == ColorIndex && Piece != SelectedPiece)
3461 Pieces.Add(Piece);
3462 break;
3463 }
3464
3465 return Pieces;
3466 }
3467
ClearSelection(bool UpdateInterface)3468 void lcModel::ClearSelection(bool UpdateInterface)
3469 {
3470 for (lcPiece* Piece : mPieces)
3471 Piece->SetSelected(false);
3472
3473 for (lcCamera* Camera : mCameras)
3474 Camera->SetSelected(false);
3475
3476 for (lcLight* Light : mLights)
3477 Light->SetSelected(false);
3478
3479 if (UpdateInterface)
3480 {
3481 gMainWindow->UpdateSelectedObjects(true);
3482 UpdateAllViews();
3483 }
3484 }
3485
SelectGroup(lcGroup * TopGroup,bool Select)3486 void lcModel::SelectGroup(lcGroup* TopGroup, bool Select)
3487 {
3488 if (!TopGroup)
3489 return;
3490
3491 for (lcPiece* Piece : mPieces)
3492 if (!Piece->IsSelected() && Piece->IsVisible(mCurrentStep) && (Piece->GetTopGroup() == TopGroup))
3493 Piece->SetSelected(Select);
3494 }
3495
FocusOrDeselectObject(const lcObjectSection & ObjectSection)3496 void lcModel::FocusOrDeselectObject(const lcObjectSection& ObjectSection)
3497 {
3498 lcObject* FocusObject = GetFocusObject();
3499 lcObject* Object = ObjectSection.Object;
3500 quint32 Section = ObjectSection.Section;
3501
3502 if (Object)
3503 {
3504 bool WasSelected = Object->IsSelected();
3505
3506 if (!Object->IsFocused(Section))
3507 {
3508 if (FocusObject)
3509 FocusObject->SetFocused(FocusObject->GetFocusSection(), false);
3510
3511 Object->SetFocused(Section, true);
3512 }
3513 else
3514 Object->SetFocused(Section, false);
3515
3516 bool IsSelected = Object->IsSelected();
3517
3518 if (Object->IsPiece() && (WasSelected != IsSelected))
3519 {
3520 lcPiece* Piece = (lcPiece*)Object;
3521
3522 if (gMainWindow->GetSelectionMode() == lcSelectionMode::Single)
3523 SelectGroup(Piece->GetTopGroup(), IsSelected);
3524 else
3525 {
3526 lcArray<lcObject*> Pieces = GetSelectionModePieces(Piece);
3527 AddToSelection(Pieces, false, false);
3528 }
3529 }
3530 }
3531 else
3532 {
3533 if (FocusObject)
3534 FocusObject->SetFocused(FocusObject->GetFocusSection(), false);
3535 }
3536
3537 gMainWindow->UpdateSelectedObjects(true);
3538 UpdateAllViews();
3539 }
3540
ClearSelectionAndSetFocus(lcObject * Object,quint32 Section,bool EnableSelectionMode)3541 void lcModel::ClearSelectionAndSetFocus(lcObject* Object, quint32 Section, bool EnableSelectionMode)
3542 {
3543 ClearSelection(false);
3544
3545 if (Object)
3546 {
3547 Object->SetFocused(Section, true);
3548
3549 if (Object->IsPiece())
3550 {
3551 SelectGroup(((lcPiece*)Object)->GetTopGroup(), true);
3552
3553 if (EnableSelectionMode)
3554 {
3555 lcArray<lcObject*> Pieces = GetSelectionModePieces((lcPiece*)Object);
3556 AddToSelection(Pieces, false, false);
3557 }
3558 }
3559 }
3560
3561 gMainWindow->UpdateSelectedObjects(true);
3562 UpdateAllViews();
3563 }
3564
ClearSelectionAndSetFocus(const lcObjectSection & ObjectSection,bool EnableSelectionMode)3565 void lcModel::ClearSelectionAndSetFocus(const lcObjectSection& ObjectSection, bool EnableSelectionMode)
3566 {
3567 ClearSelectionAndSetFocus(ObjectSection.Object, ObjectSection.Section, EnableSelectionMode);
3568 }
3569
SetSelectionAndFocus(const lcArray<lcObject * > & Selection,lcObject * Focus,quint32 Section,bool EnableSelectionMode)3570 void lcModel::SetSelectionAndFocus(const lcArray<lcObject*>& Selection, lcObject* Focus, quint32 Section, bool EnableSelectionMode)
3571 {
3572 ClearSelection(false);
3573
3574 if (Focus)
3575 {
3576 Focus->SetFocused(Section, true);
3577
3578 if (Focus->IsPiece())
3579 {
3580 SelectGroup(((lcPiece*)Focus)->GetTopGroup(), true);
3581
3582 if (EnableSelectionMode)
3583 {
3584 lcArray<lcObject*> Pieces = GetSelectionModePieces((lcPiece*)Focus);
3585 AddToSelection(Pieces, false, false);
3586 }
3587 }
3588 }
3589
3590 AddToSelection(Selection, EnableSelectionMode, true);
3591 }
3592
AddToSelection(const lcArray<lcObject * > & Objects,bool EnableSelectionMode,bool UpdateInterface)3593 void lcModel::AddToSelection(const lcArray<lcObject*>& Objects, bool EnableSelectionMode, bool UpdateInterface)
3594 {
3595 for (lcObject* Object : Objects)
3596 {
3597 bool WasSelected = Object->IsSelected();
3598 Object->SetSelected(true);
3599
3600 if (Object->IsPiece())
3601 {
3602 if (!WasSelected)
3603 SelectGroup(((lcPiece*)Object)->GetTopGroup(), true);
3604
3605 if (EnableSelectionMode)
3606 {
3607 lcArray<lcObject*> Pieces = GetSelectionModePieces((lcPiece*)Object);
3608 AddToSelection(Pieces, false, false);
3609 }
3610 }
3611 }
3612
3613 if (UpdateInterface)
3614 {
3615 gMainWindow->UpdateSelectedObjects(true);
3616 UpdateAllViews();
3617 }
3618 }
3619
RemoveFromSelection(const lcArray<lcObject * > & Objects)3620 void lcModel::RemoveFromSelection(const lcArray<lcObject*>& Objects)
3621 {
3622 for (lcObject* SelectedObject : Objects)
3623 {
3624 bool WasSelected = SelectedObject->IsSelected();
3625 SelectedObject->SetSelected(false);
3626
3627 if (WasSelected && SelectedObject->IsPiece())
3628 {
3629 lcPiece* Piece = (lcPiece*)SelectedObject;
3630
3631 if (gMainWindow->GetSelectionMode() == lcSelectionMode::Single)
3632 SelectGroup(Piece->GetTopGroup(), false);
3633 else
3634 {
3635 lcArray<lcObject*> Pieces = GetSelectionModePieces(Piece);
3636
3637 for (lcObject* Object : Pieces)
3638 {
3639 if (Object->IsSelected())
3640 {
3641 Object->SetSelected(false);
3642 SelectGroup(((lcPiece*)Object)->GetTopGroup(), false);
3643 }
3644 }
3645 }
3646 }
3647 }
3648
3649 gMainWindow->UpdateSelectedObjects(true);
3650 UpdateAllViews();
3651 }
3652
RemoveFromSelection(const lcObjectSection & ObjectSection)3653 void lcModel::RemoveFromSelection(const lcObjectSection& ObjectSection)
3654 {
3655 lcObject* SelectedObject = ObjectSection.Object;
3656
3657 if (!SelectedObject)
3658 return;
3659
3660 bool WasSelected = SelectedObject->IsSelected();
3661
3662 if (SelectedObject->IsFocused(ObjectSection.Section))
3663 SelectedObject->SetSelected(ObjectSection.Section, false);
3664 else
3665 SelectedObject->SetSelected(false);
3666
3667
3668 if (SelectedObject->IsPiece() && WasSelected)
3669 {
3670 lcPiece* Piece = (lcPiece*)SelectedObject;
3671
3672 if (gMainWindow->GetSelectionMode() == lcSelectionMode::Single)
3673 SelectGroup(Piece->GetTopGroup(), false);
3674 else
3675 {
3676 lcArray<lcObject*> Pieces = GetSelectionModePieces(Piece);
3677
3678 for (lcObject* Object : Pieces)
3679 {
3680 if (Object->IsSelected())
3681 {
3682 Object->SetSelected(false);
3683 SelectGroup(((lcPiece*)Object)->GetTopGroup(), false);
3684 }
3685 }
3686 }
3687 }
3688
3689 gMainWindow->UpdateSelectedObjects(true);
3690 UpdateAllViews();
3691 }
3692
SelectAllPieces()3693 void lcModel::SelectAllPieces()
3694 {
3695 for (lcPiece* Piece : mPieces)
3696 if (Piece->IsVisible(mCurrentStep))
3697 Piece->SetSelected(true);
3698
3699 if (!mIsPreview)
3700 gMainWindow->UpdateSelectedObjects(true);
3701 UpdateAllViews();
3702 }
3703
InvertSelection()3704 void lcModel::InvertSelection()
3705 {
3706 for (lcPiece* Piece : mPieces)
3707 if (Piece->IsVisible(mCurrentStep))
3708 Piece->SetSelected(!Piece->IsSelected());
3709
3710 gMainWindow->UpdateSelectedObjects(true);
3711 UpdateAllViews();
3712 }
3713
HideSelectedPieces()3714 void lcModel::HideSelectedPieces()
3715 {
3716 bool Modified = false;
3717
3718 for (lcPiece* Piece : mPieces)
3719 {
3720 if (Piece->IsSelected())
3721 {
3722 Piece->SetHidden(true);
3723 Piece->SetSelected(false);
3724 Modified = true;
3725 }
3726 }
3727
3728 if (!Modified)
3729 return;
3730
3731 gMainWindow->UpdateTimeline(false, true);
3732 gMainWindow->UpdateSelectedObjects(true);
3733 UpdateAllViews();
3734
3735 SaveCheckpoint(tr("Hide"));
3736 }
3737
HideUnselectedPieces()3738 void lcModel::HideUnselectedPieces()
3739 {
3740 bool Modified = false;
3741
3742 for (lcPiece* Piece : mPieces)
3743 {
3744 if (!Piece->IsSelected())
3745 {
3746 Piece->SetHidden(true);
3747 Modified = true;
3748 }
3749 }
3750
3751 if (!Modified)
3752 return;
3753
3754 gMainWindow->UpdateTimeline(false, true);
3755 gMainWindow->UpdateSelectedObjects(true);
3756 UpdateAllViews();
3757
3758 SaveCheckpoint(tr("Hide"));
3759 }
3760
UnhideSelectedPieces()3761 void lcModel::UnhideSelectedPieces()
3762 {
3763 bool Modified = false;
3764
3765 for (lcPiece* Piece : mPieces)
3766 {
3767 if (Piece->IsSelected() && Piece->IsHidden())
3768 {
3769 Piece->SetHidden(false);
3770 Modified = true;
3771 }
3772 }
3773
3774 if (!Modified)
3775 return;
3776
3777 gMainWindow->UpdateTimeline(false, true);
3778 gMainWindow->UpdateSelectedObjects(true);
3779 UpdateAllViews();
3780
3781 SaveCheckpoint(tr("Unhide"));
3782 }
3783
UnhideAllPieces()3784 void lcModel::UnhideAllPieces()
3785 {
3786 bool Modified = false;
3787
3788 for (lcPiece* Piece : mPieces)
3789 {
3790 if (Piece->IsHidden())
3791 {
3792 Piece->SetHidden(false);
3793 Modified = true;
3794 }
3795 }
3796
3797 if (!Modified)
3798 return;
3799
3800 gMainWindow->UpdateTimeline(false, true);
3801 gMainWindow->UpdateSelectedObjects(true);
3802 UpdateAllViews();
3803
3804 SaveCheckpoint(tr("Unhide"));
3805 }
3806
FindReplacePiece(bool SearchForward,bool FindAll)3807 void lcModel::FindReplacePiece(bool SearchForward, bool FindAll)
3808 {
3809 if (mPieces.IsEmpty())
3810 return;
3811
3812 auto PieceMatches = [](const lcPiece* Piece)
3813 {
3814 const lcFindReplaceParams& Params = lcView::GetFindReplaceParams();
3815
3816 if (Params.FindInfo && Params.FindInfo != Piece->mPieceInfo)
3817 return false;
3818
3819 if (!Params.FindString.isEmpty() && !strcasestr(Piece->mPieceInfo->m_strDescription, Params.FindString.toLatin1()))
3820 return false;
3821
3822 return (lcGetColorCode(Params.FindColorIndex) == LC_COLOR_NOCOLOR) || (Piece->GetColorIndex() == Params.FindColorIndex);
3823 };
3824
3825 const lcFindReplaceParams& Params = lcView::GetFindReplaceParams();
3826 int StartIdx = mPieces.GetSize() - 1;
3827
3828 for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
3829 {
3830 lcPiece* Piece = mPieces[PieceIdx];
3831
3832 if (Piece->IsFocused() && Piece->IsVisible(mCurrentStep))
3833 {
3834 if (PieceMatches(Piece))
3835 {
3836 const bool ReplaceColor = lcGetColorCode(Params.FindColorIndex) != LC_COLOR_NOCOLOR;
3837
3838 if (ReplaceColor)
3839 Piece->SetColorIndex(Params.ReplaceColorIndex);
3840
3841 if (Params.ReplacePieceInfo)
3842 Piece->SetPieceInfo(Params.ReplacePieceInfo, QString(), true);
3843
3844 if (ReplaceColor || Params.ReplacePieceInfo)
3845 {
3846 SaveCheckpoint(tr("Replacing Part"));
3847 gMainWindow->UpdateSelectedObjects(false);
3848 UpdateAllViews();
3849 gMainWindow->UpdateTimeline(false, true);
3850 }
3851 }
3852
3853 StartIdx = PieceIdx;
3854 break;
3855 }
3856 }
3857
3858 int CurrentIdx = StartIdx;
3859 lcPiece* Focus = nullptr;
3860 lcArray<lcObject*> Selection;
3861
3862 for (;;)
3863 {
3864 if (SearchForward)
3865 CurrentIdx++;
3866 else
3867 CurrentIdx--;
3868
3869 if (CurrentIdx < 0)
3870 CurrentIdx = mPieces.GetSize() - 1;
3871 else if (CurrentIdx >= mPieces.GetSize())
3872 CurrentIdx = 0;
3873
3874 lcPiece* Current = mPieces[CurrentIdx];
3875
3876 if (!Current->IsVisible(mCurrentStep))
3877 continue;
3878
3879 if (PieceMatches(Current))
3880 {
3881 if (FindAll)
3882 Selection.Add(Current);
3883 else
3884 {
3885 Focus = Current;
3886 break;
3887 }
3888 }
3889
3890 if (CurrentIdx == StartIdx)
3891 break;
3892 }
3893
3894 if (FindAll)
3895 SetSelectionAndFocus(Selection, nullptr, 0, false);
3896 else
3897 ClearSelectionAndSetFocus(Focus, LC_PIECE_SECTION_POSITION, false);
3898 }
3899
UndoAction()3900 void lcModel::UndoAction()
3901 {
3902 if (mUndoHistory.size() < 2)
3903 return;
3904
3905 lcModelHistoryEntry* Undo = mUndoHistory.front();
3906 mUndoHistory.erase(mUndoHistory.begin());
3907 mRedoHistory.insert(mRedoHistory.begin(), Undo);
3908
3909 LoadCheckPoint(mUndoHistory[0]);
3910
3911 gMainWindow->UpdateModified(IsModified());
3912 gMainWindow->UpdateUndoRedo(mUndoHistory.size() > 1 ? mUndoHistory[0]->Description : nullptr, !mRedoHistory.empty() ? mRedoHistory[0]->Description : nullptr);
3913 }
3914
RedoAction()3915 void lcModel::RedoAction()
3916 {
3917 if (mRedoHistory.empty())
3918 return;
3919
3920 lcModelHistoryEntry* Redo = mRedoHistory.front();
3921 mRedoHistory.erase(mRedoHistory.begin());
3922 mUndoHistory.insert(mUndoHistory.begin(), Redo);
3923
3924 LoadCheckPoint(Redo);
3925
3926 gMainWindow->UpdateModified(IsModified());
3927 gMainWindow->UpdateUndoRedo(mUndoHistory.size() > 1 ? mUndoHistory[0]->Description : nullptr, !mRedoHistory.empty() ? mRedoHistory[0]->Description : nullptr);
3928 }
3929
BeginMouseTool()3930 void lcModel::BeginMouseTool()
3931 {
3932 mMouseToolDistance = lcVector3(0.0f, 0.0f, 0.0f);
3933 }
3934
EndMouseTool(lcTool Tool,bool Accept)3935 void lcModel::EndMouseTool(lcTool Tool, bool Accept)
3936 {
3937 if (!Accept)
3938 {
3939 if (!mUndoHistory.empty())
3940 LoadCheckPoint(mUndoHistory.front());
3941 return;
3942 }
3943
3944 switch (Tool)
3945 {
3946 case lcTool::Insert:
3947 case lcTool::Light:
3948 break;
3949
3950 case lcTool::SpotLight:
3951 SaveCheckpoint(tr("New SpotLight"));
3952 break;
3953
3954 case lcTool::Camera:
3955 gMainWindow->UpdateCameraMenu();
3956 SaveCheckpoint(tr("New Camera"));
3957 break;
3958
3959 case lcTool::Select:
3960 break;
3961
3962 case lcTool::Move:
3963 SaveCheckpoint(tr("Move"));
3964 break;
3965
3966 case lcTool::Rotate:
3967 SaveCheckpoint(tr("Rotate"));
3968 break;
3969
3970 case lcTool::Eraser:
3971 case lcTool::Paint:
3972 case lcTool::ColorPicker:
3973 break;
3974
3975 case lcTool::Zoom:
3976 if (!gMainWindow->GetActiveView()->GetCamera()->IsSimple())
3977 SaveCheckpoint(tr("Zoom"));
3978 break;
3979
3980 case lcTool::Pan:
3981 if (!gMainWindow->GetActiveView()->GetCamera()->IsSimple())
3982 SaveCheckpoint(tr("Pan"));
3983 break;
3984
3985 case lcTool::RotateView:
3986 if (!gMainWindow->GetActiveView()->GetCamera()->IsSimple())
3987 SaveCheckpoint(tr("Orbit"));
3988 break;
3989
3990 case lcTool::Roll:
3991 if (!gMainWindow->GetActiveView()->GetCamera()->IsSimple())
3992 SaveCheckpoint(tr("Roll"));
3993 break;
3994
3995 case lcTool::ZoomRegion:
3996 break;
3997
3998 case lcTool::Count:
3999 break;
4000 }
4001 }
4002
InsertPieceToolClicked(const lcMatrix44 & WorldMatrix)4003 void lcModel::InsertPieceToolClicked(const lcMatrix44& WorldMatrix)
4004 {
4005 lcPiece* Piece = new lcPiece(gMainWindow->GetCurrentPieceInfo());
4006 Piece->Initialize(WorldMatrix, mCurrentStep);
4007 Piece->SetColorIndex(gMainWindow->mColorIndex);
4008 Piece->UpdatePosition(mCurrentStep);
4009 AddPiece(Piece);
4010
4011 gMainWindow->UpdateTimeline(false, false);
4012 ClearSelectionAndSetFocus(Piece, LC_PIECE_SECTION_POSITION, false);
4013
4014 SaveCheckpoint(tr("Insert"));
4015 }
4016
PointLightToolClicked(const lcVector3 & Position)4017 void lcModel::PointLightToolClicked(const lcVector3& Position)
4018 {
4019 lcLight* Light = new lcLight(Position[0], Position[1], Position[2]);
4020 Light->CreateName(mLights);
4021 mLights.Add(Light);
4022
4023 ClearSelectionAndSetFocus(Light, LC_LIGHT_SECTION_POSITION, false);
4024 SaveCheckpoint(tr("New Light"));
4025 }
4026
BeginSpotLightTool(const lcVector3 & Position,const lcVector3 & Target)4027 void lcModel::BeginSpotLightTool(const lcVector3& Position, const lcVector3& Target)
4028 {
4029 lcLight* Light = new lcLight(Position[0], Position[1], Position[2], Target[0], Target[1], Target[2]);
4030 mLights.Add(Light);
4031
4032 mMouseToolDistance = Target;
4033
4034 ClearSelectionAndSetFocus(Light, LC_LIGHT_SECTION_TARGET, false);
4035 }
4036
UpdateSpotLightTool(const lcVector3 & Position)4037 void lcModel::UpdateSpotLightTool(const lcVector3& Position)
4038 {
4039 lcLight* Light = mLights[mLights.GetSize() - 1];
4040
4041 Light->MoveSelected(1, false, Position - mMouseToolDistance);
4042 Light->UpdatePosition(1);
4043
4044 mMouseToolDistance = Position;
4045
4046 gMainWindow->UpdateSelectedObjects(false);
4047 UpdateAllViews();
4048 }
4049
BeginCameraTool(const lcVector3 & Position,const lcVector3 & Target)4050 void lcModel::BeginCameraTool(const lcVector3& Position, const lcVector3& Target)
4051 {
4052 lcCamera* Camera = new lcCamera(Position[0], Position[1], Position[2], Target[0], Target[1], Target[2]);
4053 Camera->CreateName(mCameras);
4054 mCameras.Add(Camera);
4055
4056 mMouseToolDistance = Position;
4057
4058 ClearSelectionAndSetFocus(Camera, LC_CAMERA_SECTION_TARGET, false);
4059 }
4060
UpdateCameraTool(const lcVector3 & Position)4061 void lcModel::UpdateCameraTool(const lcVector3& Position)
4062 {
4063 lcCamera* Camera = mCameras[mCameras.GetSize() - 1];
4064
4065 Camera->MoveSelected(1, false, Position - mMouseToolDistance);
4066 Camera->UpdatePosition(1);
4067
4068 mMouseToolDistance = Position;
4069
4070 gMainWindow->UpdateSelectedObjects(false);
4071 UpdateAllViews();
4072 }
4073
UpdateMoveTool(const lcVector3 & Distance,bool AllowRelative,bool AlternateButtonDrag)4074 void lcModel::UpdateMoveTool(const lcVector3& Distance, bool AllowRelative, bool AlternateButtonDrag)
4075 {
4076 lcVector3 PieceDistance = SnapPosition(Distance) - SnapPosition(mMouseToolDistance);
4077 lcVector3 ObjectDistance = Distance - mMouseToolDistance;
4078
4079 MoveSelectedObjects(PieceDistance, ObjectDistance, AllowRelative, AlternateButtonDrag, true, false);
4080 mMouseToolDistance = Distance;
4081
4082 gMainWindow->UpdateSelectedObjects(false);
4083 UpdateAllViews();
4084 }
4085
UpdateRotateTool(const lcVector3 & Angles,bool AlternateButtonDrag)4086 void lcModel::UpdateRotateTool(const lcVector3& Angles, bool AlternateButtonDrag)
4087 {
4088 lcVector3 Delta = SnapRotation(Angles) - SnapRotation(mMouseToolDistance);
4089 RotateSelectedPieces(Delta, true, AlternateButtonDrag, false, false);
4090 mMouseToolDistance = Angles;
4091
4092 gMainWindow->UpdateSelectedObjects(false);
4093 UpdateAllViews();
4094 }
4095
UpdateScaleTool(const float Scale)4096 void lcModel::UpdateScaleTool(const float Scale)
4097 {
4098 ScaleSelectedPieces(Scale, true, false);
4099
4100 gMainWindow->UpdateSelectedObjects(false);
4101 UpdateAllViews();
4102 }
4103
EraserToolClicked(lcObject * Object)4104 void lcModel::EraserToolClicked(lcObject* Object)
4105 {
4106 if (!Object)
4107 return;
4108
4109 switch (Object->GetType())
4110 {
4111 case lcObjectType::Piece:
4112 mPieces.Remove((lcPiece*)Object);
4113 RemoveEmptyGroups();
4114 break;
4115
4116 case lcObjectType::Camera:
4117 {
4118 std::vector<lcView*> Views = lcView::GetModelViews(this);
4119
4120 for (lcView* View : Views)
4121 {
4122 lcCamera* Camera = View->GetCamera();
4123
4124 if (Camera == Object)
4125 View->SetCamera(Camera, true);
4126 }
4127
4128 mCameras.Remove((lcCamera*)Object);
4129
4130 gMainWindow->UpdateCameraMenu();
4131 }
4132 break;
4133
4134 case lcObjectType::Light:
4135 mLights.Remove((lcLight*)Object);
4136 break;
4137 }
4138
4139 delete Object;
4140 gMainWindow->UpdateTimeline(false, false);
4141 gMainWindow->UpdateSelectedObjects(true);
4142 UpdateAllViews();
4143 SaveCheckpoint(tr("Deleting"));
4144 }
4145
PaintToolClicked(lcObject * Object)4146 void lcModel::PaintToolClicked(lcObject* Object)
4147 {
4148 if (!Object || !Object->IsPiece())
4149 return;
4150
4151 lcPiece* Piece = (lcPiece*)Object;
4152
4153 if (Piece->GetColorIndex() != gMainWindow->mColorIndex)
4154 {
4155 Piece->SetColorIndex(gMainWindow->mColorIndex);
4156
4157 SaveCheckpoint(tr("Painting"));
4158 gMainWindow->UpdateSelectedObjects(false);
4159 UpdateAllViews();
4160 gMainWindow->UpdateTimeline(false, true);
4161 }
4162 }
4163
ColorPickerToolClicked(lcObject * Object)4164 void lcModel::ColorPickerToolClicked(lcObject* Object)
4165 {
4166 if (!Object || !Object->IsPiece())
4167 return;
4168
4169 lcPiece* Piece = (lcPiece*)Object;
4170
4171 gMainWindow->SetColorIndex(Piece->GetColorIndex());
4172 }
4173
UpdateZoomTool(lcCamera * Camera,float Mouse)4174 void lcModel::UpdateZoomTool(lcCamera* Camera, float Mouse)
4175 {
4176 Camera->Zoom(Mouse - mMouseToolDistance.x, mCurrentStep, gMainWindow->GetAddKeys());
4177 mMouseToolDistance.x = Mouse;
4178
4179 UpdateAllViews();
4180 }
4181
UpdatePanTool(lcCamera * Camera,const lcVector3 & Distance)4182 void lcModel::UpdatePanTool(lcCamera* Camera, const lcVector3& Distance)
4183 {
4184 Camera->Pan(Distance - mMouseToolDistance, mCurrentStep, gMainWindow->GetAddKeys());
4185 mMouseToolDistance = Distance;
4186
4187 UpdateAllViews();
4188 }
4189
UpdateOrbitTool(lcCamera * Camera,float MouseX,float MouseY)4190 void lcModel::UpdateOrbitTool(lcCamera* Camera, float MouseX, float MouseY)
4191 {
4192 lcVector3 Center;
4193 GetSelectionCenter(Center);
4194
4195 Camera->Orbit(MouseX - mMouseToolDistance.x, MouseY - mMouseToolDistance.y, Center, mCurrentStep, gMainWindow->GetAddKeys());
4196 mMouseToolDistance.x = MouseX;
4197 mMouseToolDistance.y = MouseY;
4198
4199 UpdateAllViews();
4200 }
4201
UpdateRollTool(lcCamera * Camera,float Mouse)4202 void lcModel::UpdateRollTool(lcCamera* Camera, float Mouse)
4203 {
4204 Camera->Roll(Mouse - mMouseToolDistance.x, mCurrentStep, gMainWindow->GetAddKeys());
4205 mMouseToolDistance.x = Mouse;
4206
4207 UpdateAllViews();
4208 }
4209
ZoomRegionToolClicked(lcCamera * Camera,float AspectRatio,const lcVector3 & Position,const lcVector3 & TargetPosition,const lcVector3 * Corners)4210 void lcModel::ZoomRegionToolClicked(lcCamera* Camera, float AspectRatio, const lcVector3& Position, const lcVector3& TargetPosition, const lcVector3* Corners)
4211 {
4212 Camera->ZoomRegion(AspectRatio, Position, TargetPosition, Corners, mCurrentStep, gMainWindow->GetAddKeys());
4213
4214 gMainWindow->UpdateSelectedObjects(false);
4215 UpdateAllViews();
4216
4217 if (!Camera->IsSimple())
4218 SaveCheckpoint(tr("Zoom"));
4219 }
4220
LookAt(lcCamera * Camera)4221 void lcModel::LookAt(lcCamera* Camera)
4222 {
4223 lcVector3 Center;
4224
4225 if (!GetSelectionCenter(Center))
4226 {
4227 lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
4228
4229 if (GetVisiblePiecesBoundingBox(Min, Max))
4230 Center = (Min + Max) / 2.0f;
4231 else
4232 Center = lcVector3(0.0f, 0.0f, 0.0f);
4233 }
4234
4235 Camera->Center(Center, mCurrentStep, gMainWindow->GetAddKeys());
4236
4237 gMainWindow->UpdateSelectedObjects(false);
4238 UpdateAllViews();
4239
4240 if (!Camera->IsSimple())
4241 SaveCheckpoint(tr("Look At"));
4242 }
4243
MoveCamera(lcCamera * Camera,const lcVector3 & Direction)4244 void lcModel::MoveCamera(lcCamera* Camera, const lcVector3& Direction)
4245 {
4246 Camera->MoveRelative(Direction, mCurrentStep, gMainWindow->GetAddKeys());
4247 gMainWindow->UpdateSelectedObjects(false);
4248 UpdateAllViews();
4249
4250 if (!Camera->IsSimple())
4251 SaveCheckpoint(tr("Moving Camera"));
4252 }
4253
ZoomExtents(lcCamera * Camera,float Aspect)4254 void lcModel::ZoomExtents(lcCamera* Camera, float Aspect)
4255 {
4256 std::vector<lcVector3> Points = GetPiecesBoundingBoxPoints();
4257
4258 if (Points.empty())
4259 return;
4260
4261 lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
4262
4263 for (const lcVector3& Point : Points)
4264 {
4265 Min = lcMin(Point, Min);
4266 Max = lcMax(Point, Max);
4267 }
4268
4269 lcVector3 Center = (Min + Max) / 2.0f;
4270
4271 Camera->ZoomExtents(Aspect, Center, Points, mCurrentStep, gMainWindow ? gMainWindow->GetAddKeys() : false);
4272
4273 if (!mIsPreview && gMainWindow)
4274 gMainWindow->UpdateSelectedObjects(false);
4275 UpdateAllViews();
4276
4277 if (!Camera->IsSimple())
4278 SaveCheckpoint(tr("Zoom"));
4279 }
4280
Zoom(lcCamera * Camera,float Amount)4281 void lcModel::Zoom(lcCamera* Camera, float Amount)
4282 {
4283 Camera->Zoom(Amount, mCurrentStep, gMainWindow->GetAddKeys());
4284
4285 if (!mIsPreview)
4286 gMainWindow->UpdateSelectedObjects(false);
4287 UpdateAllViews();
4288
4289 if (!Camera->IsSimple())
4290 SaveCheckpoint(tr("Zoom"));
4291 }
4292
ShowPropertiesDialog()4293 void lcModel::ShowPropertiesDialog()
4294 {
4295 lcPropertiesDialogOptions Options;
4296
4297 Options.Properties = mProperties;
4298 Options.BoundingBox = GetAllPiecesBoundingBox();
4299
4300 GetPartsList(gDefaultColor, true, false, Options.PartsList);
4301
4302 lcQPropertiesDialog Dialog(gMainWindow, &Options);
4303 if (Dialog.exec() != QDialog::Accepted)
4304 return;
4305
4306 if (mProperties == Options.Properties)
4307 return;
4308
4309 mProperties = Options.Properties;
4310
4311 gMainWindow->GetPreviewWidget()->UpdatePreview();
4312
4313 SaveCheckpoint(tr("Changing Properties"));
4314 }
4315
ShowSelectByNameDialog()4316 void lcModel::ShowSelectByNameDialog()
4317 {
4318 if (mPieces.IsEmpty() && mCameras.IsEmpty() && mLights.IsEmpty())
4319 {
4320 QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to select."));
4321 return;
4322 }
4323
4324 lcQSelectDialog Dialog(gMainWindow, this);
4325
4326 if (Dialog.exec() != QDialog::Accepted)
4327 return;
4328
4329 SetSelectionAndFocus(Dialog.mObjects, nullptr, 0, false);
4330 }
4331
ShowArrayDialog()4332 void lcModel::ShowArrayDialog()
4333 {
4334 lcVector3 Center;
4335
4336 if (!GetPieceFocusOrSelectionCenter(Center))
4337 {
4338 QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("No pieces selected."));
4339 return;
4340 }
4341
4342 lcQArrayDialog Dialog(gMainWindow);
4343
4344 if (Dialog.exec() != QDialog::Accepted)
4345 return;
4346
4347 if (Dialog.mCounts[0] * Dialog.mCounts[1] * Dialog.mCounts[2] < 2)
4348 {
4349 QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Array only has 1 element or less, no pieces added."));
4350 return;
4351 }
4352
4353 lcArray<lcObject*> NewPieces;
4354
4355 for (int Step1 = 0; Step1 < Dialog.mCounts[0]; Step1++)
4356 {
4357 for (int Step2 = 0; Step2 < Dialog.mCounts[1]; Step2++)
4358 {
4359 for (int Step3 = (Step1 == 0 && Step2 == 0) ? 1 : 0; Step3 < Dialog.mCounts[2]; Step3++)
4360 {
4361 lcMatrix44 ModelWorld;
4362 lcVector3 Position;
4363
4364 lcVector3 RotationAngles = Dialog.mRotations[0] * Step1 + Dialog.mRotations[1] * Step2 + Dialog.mRotations[2] * Step3;
4365 lcVector3 Offset = Dialog.mOffsets[0] * Step1 + Dialog.mOffsets[1] * Step2 + Dialog.mOffsets[2] * Step3;
4366
4367 for (lcPiece* Piece : mPieces)
4368 {
4369 if (!Piece->IsSelected())
4370 continue;
4371
4372 ModelWorld = Piece->mModelWorld;
4373
4374 ModelWorld.r[3] -= lcVector4(Center, 0.0f);
4375 ModelWorld = lcMul(ModelWorld, lcMatrix44RotationX(RotationAngles[0] * LC_DTOR));
4376 ModelWorld = lcMul(ModelWorld, lcMatrix44RotationY(RotationAngles[1] * LC_DTOR));
4377 ModelWorld = lcMul(ModelWorld, lcMatrix44RotationZ(RotationAngles[2] * LC_DTOR));
4378 ModelWorld.r[3] += lcVector4(Center, 0.0f);
4379
4380 Position = lcVector3(ModelWorld.r[3].x, ModelWorld.r[3].y, ModelWorld.r[3].z);
4381 ModelWorld.SetTranslation(Position + Offset);
4382
4383 lcPiece* NewPiece = new lcPiece(nullptr);
4384 NewPiece->SetPieceInfo(Piece->mPieceInfo, Piece->GetID(), true);
4385 NewPiece->Initialize(ModelWorld, mCurrentStep);
4386 NewPiece->SetColorIndex(Piece->GetColorIndex());
4387
4388 NewPieces.Add(NewPiece);
4389 }
4390 }
4391 }
4392 }
4393
4394 for (int PieceIdx = 0; PieceIdx < NewPieces.GetSize(); PieceIdx++)
4395 {
4396 lcPiece* Piece = (lcPiece*)NewPieces[PieceIdx];
4397 Piece->UpdatePosition(mCurrentStep);
4398 AddPiece(Piece);
4399 }
4400
4401 AddToSelection(NewPieces, false, true);
4402 gMainWindow->UpdateTimeline(false, false);
4403 SaveCheckpoint(tr("Array"));
4404 }
4405
ShowMinifigDialog()4406 void lcModel::ShowMinifigDialog()
4407 {
4408 lcMinifigDialog Dialog(gMainWindow);
4409
4410 if (Dialog.exec() != QDialog::Accepted)
4411 return;
4412
4413 gMainWindow->GetActiveView()->MakeCurrent();
4414
4415 lcGroup* Group = AddGroup(tr("Minifig #"), nullptr);
4416 lcArray<lcObject*> Pieces(LC_MFW_NUMITEMS);
4417 lcMinifig& Minifig = Dialog.mMinifigWizard->mMinifig;
4418
4419 for (int PartIdx = 0; PartIdx < LC_MFW_NUMITEMS; PartIdx++)
4420 {
4421 if (Minifig.Parts[PartIdx] == nullptr)
4422 continue;
4423
4424 lcPiece* Piece = new lcPiece(Minifig.Parts[PartIdx]);
4425
4426 Piece->Initialize(Minifig.Matrices[PartIdx], mCurrentStep);
4427 Piece->SetColorIndex(Minifig.Colors[PartIdx]);
4428 Piece->SetGroup(Group);
4429 AddPiece(Piece);
4430 Piece->UpdatePosition(mCurrentStep);
4431
4432 Pieces.Add(Piece);
4433 }
4434
4435 SetSelectionAndFocus(Pieces, nullptr, 0, false);
4436 gMainWindow->UpdateTimeline(false, false);
4437 SaveCheckpoint(tr("Minifig"));
4438 }
4439
SetMinifig(const lcMinifig & Minifig)4440 void lcModel::SetMinifig(const lcMinifig& Minifig)
4441 {
4442 DeleteModel();
4443
4444 lcArray<lcObject*> Pieces(LC_MFW_NUMITEMS);
4445
4446 for (int PartIdx = 0; PartIdx < LC_MFW_NUMITEMS; PartIdx++)
4447 {
4448 if (!Minifig.Parts[PartIdx])
4449 continue;
4450
4451 lcPiece* Piece = new lcPiece(Minifig.Parts[PartIdx]);
4452
4453 Piece->Initialize(Minifig.Matrices[PartIdx], 1);
4454 Piece->SetColorIndex(Minifig.Colors[PartIdx]);
4455 AddPiece(Piece);
4456 Piece->UpdatePosition(1);
4457
4458 Pieces.Add(Piece);
4459 }
4460
4461 SetSelectionAndFocus(Pieces, nullptr, 0, false);
4462 }
4463
SetPreviewPieceInfo(PieceInfo * Info,int ColorIndex)4464 void lcModel::SetPreviewPieceInfo(PieceInfo* Info, int ColorIndex)
4465 {
4466 DeleteModel();
4467
4468 lcPiece* Piece = new lcPiece(Info);
4469
4470 Piece->Initialize(lcMatrix44Identity(), 1);
4471 Piece->SetColorIndex(ColorIndex);
4472 AddPiece(Piece);
4473 Piece->UpdatePosition(1);
4474
4475 SaveCheckpoint(QString());
4476 }
4477
UpdateInterface()4478 void lcModel::UpdateInterface()
4479 {
4480 if (!gMainWindow)
4481 return;
4482
4483 gMainWindow->UpdateTimeline(true, false);
4484 gMainWindow->UpdateUndoRedo(mUndoHistory.size() > 1 ? mUndoHistory[0]->Description : nullptr, !mRedoHistory.empty() ? mRedoHistory[0]->Description : nullptr);
4485 gMainWindow->UpdatePaste(!gApplication->mClipboard.isEmpty());
4486 gMainWindow->UpdateCategories();
4487 gMainWindow->UpdateTitle();
4488 gMainWindow->SetTool(gMainWindow->GetTool());
4489
4490 gMainWindow->UpdateSelectedObjects(true);
4491 gMainWindow->SetTransformType(gMainWindow->GetTransformType());
4492 gMainWindow->UpdateLockSnap();
4493 gMainWindow->UpdateSnap();
4494 gMainWindow->UpdateCameraMenu();
4495 gMainWindow->UpdateModels();
4496 gMainWindow->UpdatePerspective();
4497 gMainWindow->UpdateShadingMode();
4498 gMainWindow->UpdateCurrentStep();
4499 gMainWindow->UpdateSelectionMode();
4500 }
4501