diff --git a/common/lc_library.cpp b/common/lc_library.cpp index 1e8f7475..322136dc 100644 --- a/common/lc_library.cpp +++ b/common/lc_library.cpp @@ -1816,7 +1816,7 @@ std::vector lcPiecesLibrary::GetVisibleTrainTrackParts(const lcTrain { lcTrainTrackInfo* TrainTrackInfo = Info->GetTrainTrackInfo(); - if (TrainTrackInfo && TrainTrackInfo->IsVisible() && TrainTrackInfo->CanConnectTo(ConnectionType)) + if (TrainTrackInfo && TrainTrackInfo->IsVisible() && TrainTrackInfo->CanConnectTo(ConnectionType, true)) Parts.emplace_back(Info); } diff --git a/common/lc_math.h b/common/lc_math.h index 4723601d..6c0f2d1e 100644 --- a/common/lc_math.h +++ b/common/lc_math.h @@ -970,6 +970,19 @@ inline lcVector3 lcMatrix33ToEulerAngles(const lcMatrix33& RotMat) return Rot; } +inline bool lcMatrix33Similar(const lcMatrix33& a, const lcMatrix33& b) +{ + for (int Row = 0; Row < 3; Row++) + { + float Dot = lcDot(a.r[Row], b.r[Row]); + + if (Dot < 0.99f || Dot > 1.01f) + return false; + } + + return true; +} + inline float lcMatrix44::Determinant() const { return r[0][0] * r[1][1] * r[2][2] + r[0][1] * r[1][2] * r[2][0] + diff --git a/common/lc_model.cpp b/common/lc_model.cpp index 31dfd059..c8fea138 100644 --- a/common/lc_model.cpp +++ b/common/lc_model.cpp @@ -2325,7 +2325,7 @@ lcPiece* lcModel::AddPiece(PieceInfo* Info, quint32 Section) if (!Last->IsFocused()) UpdateTrainTrackConnections(Last, false); - TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(Last, Info, gMainWindow->mColorIndex, Section, std::nullopt); + TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(Last, Info, nullptr, gMainWindow->mColorIndex, Section, true, std::nullopt); for (const lcInsertPieceInfo& TrainTrack : TrainTracks) CreatePiece(TrainTrack.Info, TrainTrack.Transform, TrainTrack.ColorIndex); @@ -2533,6 +2533,34 @@ void lcModel::UpdateTrainTrackConnections(lcPiece* TrackPiece, bool IgnoreSelect TrackPiece->SetTrainTrackConnections(std::move(Connections)); } +void lcModel::UpdateSelectedPiecesTrainTrackConnections() +{ + std::vector TrackPieces; + + for (const std::unique_ptr& Piece : mPieces) + if (Piece->mPieceInfo->GetTrainTrackInfo() && Piece->IsSelected()) + TrackPieces.push_back(Piece.get()); + + for (lcPiece* TrackPiece : TrackPieces) + { + const lcTrainTrackInfo* TrainTrackInfo = TrackPiece->mPieceInfo->GetTrainTrackInfo(); + const int ConnectionCount = static_cast(TrainTrackInfo->GetConnections().size()); + std::vector Connections(ConnectionCount, false); + + for (const lcPiece* CheckPiece : TrackPieces) + { + if (CheckPiece == TrackPiece) + continue; + + for (int ConnectionIndex = 0; ConnectionIndex < ConnectionCount; ConnectionIndex++) + if (!Connections[ConnectionIndex] && lcTrainTrackInfo::GetPieceConnectionIndex(TrackPiece, ConnectionIndex, CheckPiece) != -1) + Connections[ConnectionIndex] = true; + } + + TrackPiece->SetTrainTrackConnections(std::move(Connections)); + } +} + void lcModel::DeleteAllCameras() { if (mCameras.empty()) @@ -4539,6 +4567,72 @@ void lcModel::UpdateMoveTool(const lcVector3& Distance, bool AllowRelative, bool UpdateAllViews(); } +void lcModel::UpdateFreeMoveTool(lcPiece* MousePiece, const lcMatrix44& StartTransform, const lcMatrix44& NewTransform, bool IsConnection, bool AlternateButtonDrag) +{ + lcMatrix33 NewRotation = IsConnection ? lcMatrix33(NewTransform) : lcMatrix33(StartTransform); + lcMatrix33 CurrentRotation = lcMatrix33(MousePiece->mModelWorld); + + if (!lcMatrix33Similar(CurrentRotation, NewRotation)) + { + const lcVector3 PieceDistance = NewTransform.GetTranslation() - MousePiece->mModelWorld.GetTranslation(); + const lcVector3 ObjectDistance = PieceDistance; + + lcMatrix33 RotationMatrix = lcMul(lcMatrix33AffineInverse(CurrentRotation), NewRotation); + + const lcVector3& Center = NewTransform.GetTranslation(); + lcMatrix33 RelativeRotation = MousePiece->GetRelativeRotation(); + + lcMatrix33 WorldToFocusMatrix = lcMatrix33AffineInverse(RelativeRotation); + RotationMatrix = lcMul(RotationMatrix, RelativeRotation); + + int Flags; + std::vector Selection; + lcObject* Focus; + + GetSelectionInformation(&Flags, Selection, &Focus); + + for (lcObject* Object : Selection) + { + if (Object->IsPiece()) + { + lcPiece* Piece = (lcPiece*)Object; + + Piece->MoveSelected(mCurrentStep, gMainWindow->GetAddKeys(), PieceDistance); + Piece->UpdatePosition(mCurrentStep); + + Piece->Rotate(mCurrentStep, gMainWindow->GetAddKeys(), RotationMatrix, Center, WorldToFocusMatrix); + Piece->UpdatePosition(mCurrentStep); + } + else if (Object->IsCamera()) + { + //move + //rotate + } + else if (Object->IsLight()) + { + lcLight* Light = (lcLight*)Object; + + //move + +// Light->Rotate(mCurrentStep, gMainWindow->GetAddKeys(), RotationMatrix, Center, WorldToFocusMatrix); + Light->UpdatePosition(mCurrentStep); + } + } + + mMouseToolDistance = NewTransform.GetTranslation() - StartTransform.GetTranslation(); + mMouseToolFirstMove = false; + + gMainWindow->UpdateSelectedObjects(false); + UpdateAllViews(); + } + else + { + lcVector3 Distance = NewTransform.GetTranslation() - StartTransform.GetTranslation(); + + UpdateMoveTool(Distance, false, AlternateButtonDrag); + } +} + void lcModel::UpdateRotateTool(const lcVector3& Angles, bool AlternateButtonDrag) { const lcVector3 Delta = SnapRotation(Angles) - SnapRotation(mMouseToolDistance); diff --git a/common/lc_model.h b/common/lc_model.h index 6ce43d0e..02a395f6 100644 --- a/common/lc_model.h +++ b/common/lc_model.h @@ -269,6 +269,7 @@ public: void DuplicateSelectedPieces(); void PaintSelectedPieces(); void UpdateTrainTrackConnections(lcPiece* TrackPiece, bool IgnoreSelected) const; + void UpdateSelectedPiecesTrainTrackConnections(); void GetScene(lcScene* Scene, const lcCamera* ViewCamera, bool AllowHighlight, bool AllowFade) const; void AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMatrix, int DefaultColorIndex, lcRenderMeshState RenderMeshState, bool ParentActive) const; @@ -345,6 +346,7 @@ public: void BeginCameraTool(const lcVector3& Position, const lcVector3& Target); void UpdateCameraTool(const lcVector3& Position); void UpdateMoveTool(const lcVector3& Distance, bool AllowRelative, bool AlternateButtonDrag); + void UpdateFreeMoveTool(lcPiece* MousePiece, const lcMatrix44& StartTransform, const lcMatrix44& NewTransform, bool IsConnection, bool AlternateButtonDrag); void UpdateRotateTool(const lcVector3& Angles, bool AlternateButtonDrag); void UpdateScaleTool(const float Scale); void EraserToolClicked(lcObject* Object); diff --git a/common/lc_traintrack.cpp b/common/lc_traintrack.cpp index c3de2190..684b1da5 100644 --- a/common/lc_traintrack.cpp +++ b/common/lc_traintrack.cpp @@ -7,7 +7,10 @@ #include "lc_application.h" // todo: -// when moving existing pieces, lcView::OnMouseMove calls UpdateMoveTool which only takes a position and can't rotate pieces +// when moving existing pieces: +// need to check for parts intersecting instead of mouse intersection because it's hard to connect tracks +// look into the snapping that happens when there are no available connections +// fix comments in UpdateFreeMoveTool // hide some of the 12v tracks to avoid bloat // auto replace cross when going over a straight section // set focus connection after adding @@ -131,12 +134,12 @@ int lcTrainTrackInfo::GetPieceConnectionIndex(const lcPiece* Piece1, int Connect return -1; } -std::vector lcTrainTrackInfo::GetInsertPieceInfo(lcPiece* CurrentPiece, PieceInfo* Info, int ColorIndex, quint32 PreferredSection, std::optional ClosestPoint) +std::vector lcTrainTrackInfo::GetInsertPieceInfo(lcPiece* CurrentPiece, PieceInfo* Info, lcPiece* MovingPiece, int ColorIndex, quint32 PreferredSection, bool AllowNewPieces, std::optional ClosestPoint) { std::vector Pieces; const lcTrainTrackInfo* CurrentTrackInfo = CurrentPiece->mPieceInfo->GetTrainTrackInfo(); - if (!CurrentTrackInfo || CurrentTrackInfo->GetConnections().empty()) + if (!CurrentTrackInfo || CurrentTrackInfo->GetConnections().empty() || (MovingPiece && Info != MovingPiece->mPieceInfo)) return Pieces; quint32 ConnectionIndex = 0; @@ -152,6 +155,29 @@ std::vector lcTrainTrackInfo::GetInsertPieceInfo(lcPiece* Cur if (CurrentPiece->IsTrainTrackConnected(CheckIndex)) continue; + if (MovingPiece) + { + const std::vector& MovingConnections = Info->GetTrainTrackInfo()->GetConnections(); + bool CanConnect = false; + + for (quint32 MovingConnectionIndex = 0; MovingConnectionIndex < MovingConnections.size(); MovingConnectionIndex++) + { + if (!MovingPiece->IsTrainTrackConnected(MovingConnectionIndex) && AreConnectionsCompatible(CurrentTrackInfo->GetConnections()[CheckIndex].Type, MovingConnections[MovingConnectionIndex].Type, AllowNewPieces)) + { + CanConnect = true; + break; + } + } + + if (!CanConnect) + continue; + } + else + { + if (!Info->GetTrainTrackInfo()->CanConnectTo(CurrentTrackInfo->GetConnections()[CheckIndex].Type, AllowNewPieces)) + continue; + } + float Distance = (CurrentTrackInfo->GetConnections()[CheckIndex].Transform.GetTranslation() - LocalClosestPoint).LengthSquared(); if (Distance < BestDistance) @@ -186,8 +212,23 @@ std::vector lcTrainTrackInfo::GetInsertPieceInfo(lcPiece* Cur quint32 NewConnectionIndex;// = ConnectionIndex ? 0 : 1; for (NewConnectionIndex = 0; NewConnectionIndex < NewConnections.size(); NewConnectionIndex++) - if (AreConnectionsCompatible(CurrentConnectionType, NewConnections[NewConnectionIndex].Type)) - break; + { + if (MovingPiece) + { + if (MovingPiece->IsTrainTrackConnected(NewConnectionIndex)) + continue; + + const std::vector& MovingConnections = Info->GetTrainTrackInfo()->GetConnections(); + + if (AreConnectionsCompatible(CurrentTrackInfo->GetConnections()[ConnectionIndex].Type, MovingConnections[NewConnectionIndex].Type, AllowNewPieces)) + break; + } + else + { + if (AreConnectionsCompatible(CurrentConnectionType, NewConnections[NewConnectionIndex].Type, AllowNewPieces)) + break; + } + } if (NewConnectionIndex == NewConnections.size()) return Pieces; diff --git a/common/lc_traintrack.h b/common/lc_traintrack.h index 2805c3dc..d05616d5 100644 --- a/common/lc_traintrack.h +++ b/common/lc_traintrack.h @@ -45,7 +45,7 @@ public: } static void Initialize(lcPiecesLibrary* Library); - static std::vector GetInsertPieceInfo(lcPiece* CurrentPiece, PieceInfo* Info, int ColorIndex, quint32 PreferredSection, std::optional ClosestPoint); + static std::vector GetInsertPieceInfo(lcPiece* CurrentPiece, PieceInfo* Info, lcPiece* MovingPiece, int ColorIndex, quint32 PreferredSection, bool AllowNewPieces, std::optional ClosestPoint); 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); @@ -55,17 +55,17 @@ public: return mConnections; } - bool CanConnectTo(const lcTrainTrackConnectionType& ConnectionType) const + bool CanConnectTo(const lcTrainTrackConnectionType& ConnectionType, bool AllowNewPieces) const { for (const lcTrainTrackConnection& Connection : mConnections) - if (AreConnectionsCompatible(Connection.Type, ConnectionType)) + if (AreConnectionsCompatible(Connection.Type, ConnectionType, AllowNewPieces)) return true; return false; } protected: - static bool AreConnectionsCompatible(const lcTrainTrackConnectionType& a, const lcTrainTrackConnectionType& b) + static bool AreConnectionsCompatible(const lcTrainTrackConnectionType& a, const lcTrainTrackConnectionType& b, bool AllowNewPieces) { if (a.Group != b.Group) return false; @@ -79,7 +79,7 @@ protected: return b.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper; case lcTrainTrackConnectionSleeper::NeedsSleeper: - return b.Sleeper == lcTrainTrackConnectionSleeper::HasSleeper || b.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper; + return b.Sleeper == lcTrainTrackConnectionSleeper::HasSleeper || (AllowNewPieces && b.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper); } return false; diff --git a/common/lc_view.cpp b/common/lc_view.cpp index 712d1b90..1acf9754 100644 --- a/common/lc_view.cpp +++ b/common/lc_view.cpp @@ -463,10 +463,11 @@ void lcView::UpdatePiecePreview() { PieceInfo* PreviewInfo = gMainWindow->GetCurrentPieceInfo(); std::vector InsertPieceInfo; + bool IsConnection; if (PreviewInfo) { - InsertPieceInfo = GetMouseInsertPieceInfo(false, PreviewInfo); + std::tie(InsertPieceInfo, IsConnection) = GetMouseInsertPieceInfo(false, true, PreviewInfo, nullptr); if (GetActiveModel() != mModel) { @@ -486,7 +487,7 @@ void lcView::UpdatePiecePreview() mPreviewInsertPieceInfo = std::move(InsertPieceInfo); } -std::vector lcView::GetMouseInsertPieceInfo(bool IgnoreSelected, PieceInfo* Info) const +std::pair, bool> lcView::GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, PieceInfo* Info, lcPiece* MovingPiece) const { lcModel* ActiveModel = GetActiveModel(); @@ -505,10 +506,10 @@ std::vector lcView::GetMouseInsertPieceInfo(bool IgnoreSelect lcVector3 ClosestPoint = ObjectRayTest.Start + lcNormalize(ObjectRayTest.End - ObjectRayTest.Start) * ObjectRayTest.Distance; quint32 FocusSection = MousePiece->GetFocusSection(); - std::vector TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(MousePiece, Info, gMainWindow->mColorIndex, FocusSection, ClosestPoint); + std::vector TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(MousePiece, Info, MovingPiece, gMainWindow->mColorIndex, FocusSection, AllowNewPieces, ClosestPoint); if (!TrainTracks.empty()) - return TrainTracks; + return { TrainTracks, true }; } lcVector3 Position = ObjectRayTest.PieceInfoRayTest.Plane; @@ -534,7 +535,7 @@ std::vector lcView::GetMouseInsertPieceInfo(bool IgnoreSelect lcMatrix44 WorldMatrix = ObjectRayTest.PieceInfoRayTest.Transform; WorldMatrix.SetTranslation(Position); - return { lcInsertPieceInfo{ Info, WorldMatrix, gMainWindow->mColorIndex } }; + return { { lcInsertPieceInfo{ Info, WorldMatrix, gMainWindow->mColorIndex } }, false }; } std::array ClickPoints = {{ lcVector3((float)mMouseX, (float)mMouseY, 0.0f), lcVector3((float)mMouseX, (float)mMouseY, 1.0f) }}; @@ -554,7 +555,7 @@ std::vector lcView::GetMouseInsertPieceInfo(bool IgnoreSelect if (lcLineSegmentPlaneIntersection(&Intersection, ClickPoints[0], ClickPoints[1], lcVector4(0, 0, 1, BoundingBox.Min.z))) { Intersection = ActiveModel->SnapPosition(Intersection); - return { lcInsertPieceInfo{ Info, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }; + return { { lcInsertPieceInfo{ Info, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false }; } lcVector3 Position; @@ -567,10 +568,10 @@ std::vector lcView::GetMouseInsertPieceInfo(bool IgnoreSelect if (lcLineSegmentPlaneIntersection(&Intersection, ClickPoints[0], ClickPoints[1], lcVector4(FrontVector, -lcDot(FrontVector, Position)))) { Intersection = ActiveModel->SnapPosition(Intersection); - return { lcInsertPieceInfo{ Info, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }; + return { { lcInsertPieceInfo{ Info, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false }; } - return { lcInsertPieceInfo{ Info, lcMatrix44Translation(UnprojectPoint(lcVector3((float)mMouseX, (float)mMouseY, 0.9f))), gMainWindow->mColorIndex } }; + return { { lcInsertPieceInfo{ Info, lcMatrix44Translation(UnprojectPoint(lcVector3((float)mMouseX, (float)mMouseY, 0.9f))), gMainWindow->mColorIndex } }, false }; } lcVector3 lcView::GetCameraLightInsertPosition() const @@ -2082,6 +2083,7 @@ void lcView::UpdateTrackTool() int x = mMouseX; int y = mMouseY; bool Redraw = false; + mMouseDownPiece = nullptr; mTrackToolFromOverlay = false; lcModel* ActiveModel = GetActiveModel(); @@ -2114,7 +2116,6 @@ void lcView::UpdateTrackTool() case lcTool::Select: case lcTool::Move: { - mMouseDownPiece = nullptr; std::tie(NewTrackTool, NewTrackSection) = mViewManipulator->UpdateSelectMove(mTrackButton); mTrackToolFromOverlay = NewTrackTool != lcTrackTool::MoveXYZ && NewTrackTool != lcTrackTool::Select; Redraw = NewTrackTool != mTrackTool || NewTrackSection != mTrackToolSection; @@ -2127,8 +2128,8 @@ void lcView::UpdateTrackTool() if (Object && Object->IsPiece() && ObjectSection.Section == LC_PIECE_SECTION_POSITION && Object->IsSelected()) { lcPiece* Piece = (lcPiece*)Object; - mMouseDownPosition = Piece->mModelWorld.GetTranslation(); - mMouseDownPiece = Piece->mPieceInfo; + mMouseDownTransform = Piece->mModelWorld; + mMouseDownPiece = Piece; NewTrackTool = lcTrackTool::MoveXYZ; } } @@ -2926,13 +2927,14 @@ void lcView::OnMouseMove() } else if (mTrackTool == lcTrackTool::MoveXYZ && mMouseDownPiece) { - std::vector PieceInfoTransforms = GetMouseInsertPieceInfo(true, mMouseDownPiece); + if (mMouseDownPiece->mPieceInfo->GetTrainTrackInfo()) + ActiveModel->UpdateSelectedPiecesTrainTrackConnections(); + + auto [PieceInfoTransforms, IsConnection] = GetMouseInsertPieceInfo(true, false, mMouseDownPiece->mPieceInfo, mMouseDownPiece); if (!PieceInfoTransforms.empty()) { - lcMatrix44 NewPosition = PieceInfoTransforms.back().Transform; - lcVector3 Distance = NewPosition.GetTranslation() - mMouseDownPosition; - ActiveModel->UpdateMoveTool(Distance, false, mTrackButton != lcTrackButton::Left); + ActiveModel->UpdateFreeMoveTool(mMouseDownPiece, mMouseDownTransform, PieceInfoTransforms.back().Transform, IsConnection, mTrackButton != lcTrackButton::Left); } } else if (mTrackTool == lcTrackTool::ScalePlus || mTrackTool == lcTrackTool::ScaleMinus) diff --git a/common/lc_view.h b/common/lc_view.h index f4de30c2..c915b622 100644 --- a/common/lc_view.h +++ b/common/lc_view.h @@ -266,7 +266,7 @@ public: float GetOverlayScale() const; lcVector3 GetMoveDirection(const lcVector3& Direction) const; void UpdatePiecePreview(); - std::vector GetMouseInsertPieceInfo(bool IgnoreSelected, PieceInfo* Info) const; + std::pair, bool> GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, PieceInfo* Info, lcPiece* MovingPiece) const; lcVector3 GetCameraLightInsertPosition() const; void GetRayUnderPointer(lcVector3& Start, lcVector3& End) const; lcObjectRayTest RayTest(bool PiecesOnly, bool IgnoreSelected) const; @@ -337,8 +337,8 @@ protected: lcDragState mDragState; bool mTrackToolFromOverlay; quint32 mTrackToolSection = ~0U; - lcVector3 mMouseDownPosition; - PieceInfo* mMouseDownPiece; + lcMatrix44 mMouseDownTransform; + lcPiece* mMouseDownPiece = nullptr; int mPanX = 0; int mPanY = 0; diff --git a/resources/traintrack.json b/resources/traintrack.json index 98fb778c..23601ad7 100644 --- a/resources/traintrack.json +++ b/resources/traintrack.json @@ -400,7 +400,7 @@ { "Position": [ -160.0, 0.0, 0.0 ], "Rotation": -180.0, - "Type": "+4.5V" + "Type": "-4.5V" } ] },