diff --git a/common/lc_model.cpp b/common/lc_model.cpp index 2f85958c..4d49b05a 100644 --- a/common/lc_model.cpp +++ b/common/lc_model.cpp @@ -2281,12 +2281,12 @@ lcMatrix33 lcModel::GetRelativeRotation() const return lcMatrix33Identity(); } -lcPiece* lcModel::AddPiece(PieceInfo* PieceInfo, quint32 Section) +lcPiece* lcModel::AddPiece(PieceInfo* Info, quint32 Section) { - if (!PieceInfo) - PieceInfo = gMainWindow->GetCurrentPieceInfo(); + if (!Info) + Info = gMainWindow->GetCurrentPieceInfo(); - if (!PieceInfo) + if (!Info) return nullptr; lcPiece* Last = mPieces.empty() ? nullptr : mPieces.back().get(); @@ -2300,43 +2300,57 @@ lcPiece* lcModel::AddPiece(PieceInfo* PieceInfo, quint32 Section) } } - lcMatrix44 WorldMatrix; - const lcBoundingBox& PieceInfoBoundingBox = PieceInfo->GetBoundingBox(); + const lcBoundingBox& PieceInfoBoundingBox = Info->GetBoundingBox(); + lcPiece* Piece = nullptr; + + auto CreatePiece=[this, &Piece](PieceInfo* Info, const lcMatrix44& WorldMatrix, int ColorIndex) + { + Piece = new lcPiece(Info); + Piece->Initialize(WorldMatrix, mCurrentStep); + Piece->SetColorIndex(ColorIndex); + AddPiece(Piece); + }; if (Last) { - bool TransformValid = false; + std::vector TrainTracks; - if (PieceInfo->GetTrainTrackInfo() && Last->mPieceInfo->GetTrainTrackInfo()) + if (Info->GetTrainTrackInfo() && Last->mPieceInfo->GetTrainTrackInfo()) { - std::optional TrainTrackTransform = lcTrainTrackInfo::GetPieceInsertTransform(Last, PieceInfo, Section); + quint32 FocusSection = Last->GetFocusSection(); - if (TrainTrackTransform) + if (FocusSection != LC_PIECE_SECTION_INVALID && FocusSection >= LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST) + Section = FocusSection; + + TrainTracks = lcTrainTrackInfo::GetPieceInsertTransforms(Last, Info, Section); + + for (const lcTrainTrackInsert& TrainTrack : TrainTracks) { - TransformValid = true; - WorldMatrix = TrainTrackTransform.value(); + int ColorIndex = TrainTrack.ColorCode == 16 ? gMainWindow->mColorIndex : lcGetColorIndex(TrainTrack.ColorCode); + + CreatePiece(TrainTrack.Info, TrainTrack.Transform, ColorIndex); } } - if (!TransformValid) + if (TrainTracks.empty()) { const lcBoundingBox& LastBoundingBox = Last->GetBoundingBox(); lcVector3 Dist(0, 0, LastBoundingBox.Max.z - PieceInfoBoundingBox.Min.z); Dist = SnapPosition(Dist); - WorldMatrix = Last->mModelWorld; + lcMatrix44 WorldMatrix = Last->mModelWorld; WorldMatrix.SetTranslation(lcMul31(Dist, Last->mModelWorld)); + + CreatePiece(Info, WorldMatrix, gMainWindow->mColorIndex); } } else { - WorldMatrix = lcMatrix44Translation(lcVector3(0.0f, 0.0f, -PieceInfoBoundingBox.Min.z)); + lcMatrix44 WorldMatrix = lcMatrix44Translation(lcVector3(0.0f, 0.0f, -PieceInfoBoundingBox.Min.z)); + + CreatePiece(Info, WorldMatrix, gMainWindow->mColorIndex); } - lcPiece* Piece = new lcPiece(PieceInfo); - Piece->Initialize(WorldMatrix, mCurrentStep); - Piece->SetColorIndex(gMainWindow->mColorIndex); - AddPiece(Piece); gMainWindow->UpdateTimeline(false, false); ClearSelectionAndSetFocus(Piece, LC_PIECE_SECTION_POSITION, false); diff --git a/common/lc_traintrack.cpp b/common/lc_traintrack.cpp index 15d9c1fc..1449aee0 100644 --- a/common/lc_traintrack.cpp +++ b/common/lc_traintrack.cpp @@ -7,6 +7,9 @@ // todo: // add the rest of the 9v and 12v tracks, look into 4.5v +// see if we should remove some of the 12v tracks to avoid bloat +// data driven sleepers +// lcView::GetPieceInsertTransform should use track connections when dragging a new track over an existing one // better insert gizmo mouse detection // auto replace cross when going over a straight section // set focus connection after adding @@ -53,20 +56,20 @@ void lcTrainTrackInit(lcPiecesLibrary* Library) float Rotation = JsonConnection["Rotation"].toDouble() * LC_DTOR; QString ConnectionGroup = JsonConnection["Type"].toString(); - int ConnectionDirection = 0; + lcTrainTrackConnectionSleeper ConnectionSleeper = lcTrainTrackConnectionSleeper::None; if (ConnectionGroup.startsWith('+')) { - ConnectionDirection = 1; + ConnectionSleeper = lcTrainTrackConnectionSleeper::NeedsSleeper; ConnectionGroup = ConnectionGroup.mid(1); } else if (ConnectionGroup.startsWith('-')) { - ConnectionDirection = -1; + ConnectionSleeper = lcTrainTrackConnectionSleeper::HasSleeper; ConnectionGroup = ConnectionGroup.mid(1); } - TrainTrackInfo->AddConnection(lcMatrix44(lcMatrix33RotationZ(Rotation), Position), { qHash(ConnectionGroup), ConnectionDirection } ); + TrainTrackInfo->AddConnection(lcMatrix44(lcMatrix33RotationZ(Rotation), Position), { qHash(ConnectionGroup), ConnectionSleeper } ); } } } @@ -97,21 +100,17 @@ int lcTrainTrackInfo::GetPieceConnectionIndex(const lcPiece* Piece1, int Connect return -1; } -std::optional lcTrainTrackInfo::GetPieceInsertTransform(lcPiece* CurrentPiece, PieceInfo* Info, quint32 Section) +std::vector lcTrainTrackInfo::GetPieceInsertTransforms(lcPiece* CurrentPiece, PieceInfo* Info, quint32 Section) { + std::vector Pieces; const lcTrainTrackInfo* CurrentTrackInfo = CurrentPiece->mPieceInfo->GetTrainTrackInfo(); if (!CurrentTrackInfo || CurrentTrackInfo->GetConnections().empty()) - return std::nullopt; + return Pieces; - const quint32 FocusSection = CurrentPiece->GetFocusSection(); quint32 ConnectionIndex = 0; - if (FocusSection != LC_PIECE_SECTION_INVALID && FocusSection >= LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST) - { - ConnectionIndex = FocusSection - LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST; - } - else if (Section != LC_PIECE_SECTION_INVALID && Section >= LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST) + if (Section != LC_PIECE_SECTION_INVALID && Section >= LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST) { ConnectionIndex = Section - LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST; } @@ -122,7 +121,7 @@ std::optional lcTrainTrackInfo::GetPieceInsertTransform(lcPiece* Cur break; if (ConnectionIndex == CurrentTrackInfo->GetConnections().size()) - return std::nullopt; + return Pieces; } const lcTrainTrackConnectionType& CurrentConnectionType = CurrentTrackInfo->GetConnections()[ConnectionIndex].Type; @@ -134,17 +133,45 @@ std::optional lcTrainTrackInfo::GetPieceInsertTransform(lcPiece* Cur break; if (NewConnectionIndex == NewConnections.size()) - return std::nullopt; + return Pieces; - return GetConnectionTransform(CurrentPiece, ConnectionIndex, Info, NewConnectionIndex); + if (CurrentConnectionType.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper && NewConnections[NewConnectionIndex].Type.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper) + { + PieceInfo* SleeperInfo = lcGetPiecesLibrary()->FindPiece("4166a.dat", nullptr, false, false); // todo: data driven + + if (!SleeperInfo) + return Pieces; + + std::optional SleeperTransform = GetConnectionTransform(CurrentPiece->mPieceInfo, CurrentPiece->mModelWorld, ConnectionIndex, SleeperInfo, 0); + + if (!SleeperTransform) + return Pieces; + + std::optional Transform = GetConnectionTransform(SleeperInfo, SleeperTransform.value(), 1, Info, NewConnectionIndex); + + if (Transform) + { + Pieces.emplace_back(lcTrainTrackInsert{ SleeperInfo, SleeperTransform.value(), 8 }); + Pieces.emplace_back(lcTrainTrackInsert{ Info, Transform.value(), 16 }); + } + } + else + { + std::optional Transform = GetConnectionTransform(CurrentPiece->mPieceInfo, CurrentPiece->mModelWorld, ConnectionIndex, Info, NewConnectionIndex); + + if (Transform) + Pieces.emplace_back(lcTrainTrackInsert{ Info, Transform.value(), 16 }); + } + + return Pieces; } -std::optional lcTrainTrackInfo::GetConnectionTransform(lcPiece* CurrentPiece, quint32 CurrentConnectionIndex, PieceInfo* Info, quint32 NewConnectionIndex) +std::optional lcTrainTrackInfo::GetConnectionTransform(PieceInfo* CurrentInfo, const lcMatrix44& CurrentTransform, quint32 CurrentConnectionIndex, PieceInfo* Info, quint32 NewConnectionIndex) { - if (!CurrentPiece || !Info) + if (!CurrentInfo || !Info) return std::nullopt; - const lcTrainTrackInfo* CurrentTrackInfo = CurrentPiece->mPieceInfo->GetTrainTrackInfo(); + const lcTrainTrackInfo* CurrentTrackInfo = CurrentInfo->GetTrainTrackInfo(); if (!CurrentTrackInfo || CurrentTrackInfo->GetConnections().empty()) return std::nullopt; @@ -168,7 +195,7 @@ std::optional lcTrainTrackInfo::GetConnectionTransform(lcPiece* Curr // } Transform = lcMul(Transform, CurrentTrackInfo->GetConnections()[CurrentConnectionIndex].Transform); - Transform = lcMul(Transform, CurrentPiece->mModelWorld); + Transform = lcMul(Transform, CurrentTransform); return Transform; } diff --git a/common/lc_traintrack.h b/common/lc_traintrack.h index 45e86992..06c1f2cb 100644 --- a/common/lc_traintrack.h +++ b/common/lc_traintrack.h @@ -4,11 +4,19 @@ class lcPiece; class lcPiecesLibrary; +class PieceInfo; + +enum class lcTrainTrackConnectionSleeper +{ + None, + HasSleeper, + NeedsSleeper +}; struct lcTrainTrackConnectionType { quint32 Group; - int Format; + lcTrainTrackConnectionSleeper Sleeper; }; struct lcTrainTrackConnection @@ -17,13 +25,20 @@ struct lcTrainTrackConnection lcTrainTrackConnectionType Type; }; +struct lcTrainTrackInsert +{ + PieceInfo* Info; + lcMatrix44 Transform; + int ColorCode; +}; + class lcTrainTrackInfo { public: lcTrainTrackInfo() = default; - static std::optional GetPieceInsertTransform(lcPiece* CurrentPiece, PieceInfo* Info, quint32 Section); - static std::optional GetConnectionTransform(lcPiece* CurrentPiece, quint32 CurrentConnectionIndex, PieceInfo* Info, quint32 NewConnectionIndex); + static std::vector GetPieceInsertTransforms(lcPiece* CurrentPiece, PieceInfo* Info, quint32 Section); + static std::optional GetConnectionTransform(PieceInfo* CurrentInfo, const lcMatrix44& CurrentTransform, quint32 CurrentConnectionIndex, PieceInfo* Info, quint32 NewConnectionIndex); static std::optional CalculateTransformToConnection(const lcMatrix44& ConnectionTransform, PieceInfo* Info, quint32 ConnectionIndex); static int GetPieceConnectionIndex(const lcPiece* Piece1, int ConnectionIndex1, const lcPiece* Piece2); @@ -49,7 +64,22 @@ public: protected: static bool AreConnectionsCompatible(const lcTrainTrackConnectionType& a, const lcTrainTrackConnectionType& b) { - return a.Group == b.Group && a.Format + b.Format == 0; + if (a.Group != b.Group) + return false; + + switch (a.Sleeper) + { + case lcTrainTrackConnectionSleeper::None: + return b.Sleeper == lcTrainTrackConnectionSleeper::None; + + case lcTrainTrackConnectionSleeper::HasSleeper: + return b.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper; + + case lcTrainTrackConnectionSleeper::NeedsSleeper: + return b.Sleeper == lcTrainTrackConnectionSleeper::HasSleeper || b.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper; + } + + return false; } std::vector mConnections; diff --git a/resources/traintrack.json b/resources/traintrack.json index 3672525c..c78d8fb9 100644 --- a/resources/traintrack.json +++ b/resources/traintrack.json @@ -126,7 +126,7 @@ }, "Train Track Sleeper Plate 2 x 8": { - "Parts": [ "4166a.dat" ], + "Parts": [ "4166a.dat", "4166b.dat" ], "Connections": [ { @@ -279,6 +279,23 @@ "Type": "+12V" } ] + }, + "Train Track 4.5V Slotted Straight Without End Sleepers": + { + "Parts": [ "3228bc02.dat" ], + "Connections": + [ + { + "Position": [ 160.0, 0.0, 0.0 ], + "Rotation": 0.0, + "Type": "+4.5V" + }, + { + "Position": [ -160.0, 0.0, 0.0 ], + "Rotation": -180.0, + "Type": "+4.5V" + } + ] } } }