diff --git a/common/lc_model.cpp b/common/lc_model.cpp index e9bb4b75..1ba7e6a5 100644 --- a/common/lc_model.cpp +++ b/common/lc_model.cpp @@ -2383,7 +2383,7 @@ void lcModel::UpdateTrainTrackConnections(lcPiece* FocusPiece) const continue; for (int ConnectionIndex = 0; ConnectionIndex < ConnectionCount; ConnectionIndex++) - if (!Connections[ConnectionIndex] && lcTrainTrackInfo::ArePiecesConnected(FocusPiece, ConnectionIndex, Piece.get())) + if (!Connections[ConnectionIndex] && lcTrainTrackInfo::GetPieceConnectionIndex(FocusPiece, ConnectionIndex, Piece.get()) != -1) Connections[ConnectionIndex] = true; } @@ -4395,6 +4395,54 @@ void lcModel::UpdateRotateTool(const lcVector3& Angles, bool AlternateButtonDrag UpdateAllViews(); } +void lcModel::RotateTrainTrackToolClicked(quint32 ConnectionIndex) +{ + const lcObject* Focus = GetFocusObject(); + + if (!Focus || !Focus->IsPiece()) + return; + + lcPiece* FocusPiece = (lcPiece*)Focus; + const lcTrainTrackInfo* TrainTrackInfo = FocusPiece->mPieceInfo->GetTrainTrackInfo(); + + if (!TrainTrackInfo) + return; + + lcPiece* ConnectedPiece = nullptr; + int ConnectedPieceConnectionIndex = -1; + + for (const std::unique_ptr& Piece : mPieces) + { + if (Piece.get() == FocusPiece || !Piece->mPieceInfo->GetTrainTrackInfo()) + continue; + + ConnectedPieceConnectionIndex = lcTrainTrackInfo::GetPieceConnectionIndex(FocusPiece, ConnectionIndex, Piece.get()); + + if (ConnectedPieceConnectionIndex != -1) + { + ConnectedPiece = Piece.get(); + break; + } + } + + if (!ConnectedPiece) + return; + + quint32 NewConnectionIndex = (ConnectionIndex + 1) % TrainTrackInfo->GetConnections().size(); + std::optional Transform = lcTrainTrackInfo::GetConnectionTransform(ConnectedPiece, ConnectedPieceConnectionIndex, FocusPiece->mPieceInfo, NewConnectionIndex); + + if (!Transform) + return; + + FocusPiece->SetPosition(Transform.value().GetTranslation(), mCurrentStep, gMainWindow->GetAddKeys()); + FocusPiece->SetRotation(lcMatrix33(Transform.value()), mCurrentStep, gMainWindow->GetAddKeys()); + FocusPiece->UpdatePosition(mCurrentStep); + + gMainWindow->UpdateSelectedObjects(true); + UpdateAllViews(); + SaveCheckpoint(tr("Rotating")); +} + void lcModel::UpdateScaleTool(const float Scale) { ScaleSelectedPieces(Scale, true, false); diff --git a/common/lc_model.h b/common/lc_model.h index 963ae353..1f1e484d 100644 --- a/common/lc_model.h +++ b/common/lc_model.h @@ -334,6 +334,7 @@ public: void UpdateCameraTool(const lcVector3& Position); void UpdateMoveTool(const lcVector3& Distance, bool AllowRelative, bool AlternateButtonDrag); void UpdateRotateTool(const lcVector3& Angles, bool AlternateButtonDrag); + void RotateTrainTrackToolClicked(quint32 ConnectionIndex); void UpdateScaleTool(const float Scale); void EraserToolClicked(lcObject* Object); void PaintToolClicked(lcObject* Object); diff --git a/common/lc_traintrack.cpp b/common/lc_traintrack.cpp index 1c868317..0f39e7f0 100644 --- a/common/lc_traintrack.cpp +++ b/common/lc_traintrack.cpp @@ -13,6 +13,8 @@ // shortcuts for changing active connection // move config to json // add other track types +// crash when focusing a track during in place submodel editing because GetScene doesn't have the focus piece +// macros to encode/decode mTrackToolSection void lcTrainTrackInit(lcPiecesLibrary* Library) { @@ -127,16 +129,18 @@ std::pair lcTrainTrackInfo::GetPieceInsertTransform(lcPi return { Info, Transform }; } -bool lcTrainTrackInfo::ArePiecesConnected(const lcPiece* Piece1, int ConnectionIndex1, const lcPiece* Piece2) +int lcTrainTrackInfo::GetPieceConnectionIndex(const lcPiece* Piece1, int ConnectionIndex1, const lcPiece* Piece2) { const lcTrainTrackInfo* TrainTrackInfo1 = Piece1->mPieceInfo->GetTrainTrackInfo(); const lcTrainTrackInfo* TrainTrackInfo2 = Piece2->mPieceInfo->GetTrainTrackInfo(); + const std::vector& Connections2 = TrainTrackInfo2->GetConnections(); lcMatrix44 Transform1 = lcMul(TrainTrackInfo1->GetConnections()[ConnectionIndex1].Transform, Piece1->mModelWorld); - for (const lcTrainTrackConnection& Connection2 : TrainTrackInfo2->GetConnections()) + for (int ConnectionIndex2 = 0; ConnectionIndex2 < static_cast(Connections2.size()); ConnectionIndex2++) { - lcMatrix44 Transform2 = lcMul(Connection2.Transform, Piece2->mModelWorld); + const lcTrainTrackConnection& Connection2 = Connections2[ConnectionIndex2]; + const lcMatrix44 Transform2 = lcMul(Connection2.Transform, Piece2->mModelWorld); if (lcLengthSquared(Transform1.GetTranslation() - Transform2.GetTranslation()) > 0.1f) continue; @@ -144,17 +148,14 @@ bool lcTrainTrackInfo::ArePiecesConnected(const lcPiece* Piece1, int ConnectionI float Dot = lcDot3(Transform1[0], Transform2[0]); if (Dot < -0.99f && Dot > -1.01f) - return true; + return ConnectionIndex2; } - return false; + return -1; } std::optional lcTrainTrackInfo::GetPieceInsertTransform(lcPiece* CurrentPiece, PieceInfo* Info) { - if (!CurrentPiece || !Info) - return std::nullopt; - const lcTrainTrackInfo* CurrentTrackInfo = CurrentPiece->mPieceInfo->GetTrainTrackInfo(); if (!CurrentTrackInfo || CurrentTrackInfo->GetConnections().empty()) @@ -177,25 +178,38 @@ std::optional lcTrainTrackInfo::GetPieceInsertTransform(lcPiece* Cur ConnectionIndex = FocusSection - LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST; } - if (ConnectionIndex >= CurrentTrackInfo->GetConnections().size()) + return GetConnectionTransform(CurrentPiece, ConnectionIndex, Info, 0); +} + +std::optional lcTrainTrackInfo::GetConnectionTransform(lcPiece* CurrentPiece, quint32 CurrentConnectionIndex, PieceInfo* Info, quint32 NewConnectionIndex) +{ + if (!CurrentPiece || !Info) + return std::nullopt; + + const lcTrainTrackInfo* CurrentTrackInfo = CurrentPiece->mPieceInfo->GetTrainTrackInfo(); + + if (!CurrentTrackInfo || CurrentTrackInfo->GetConnections().empty()) + return std::nullopt; + + if (CurrentConnectionIndex >= CurrentTrackInfo->GetConnections().size()) return std::nullopt; lcTrainTrackInfo* NewTrackInfo = Info->GetTrainTrackInfo(); - if (!NewTrackInfo || NewTrackInfo->mConnections.empty()) + if (!NewTrackInfo || NewConnectionIndex >= NewTrackInfo->mConnections.size()) return std::nullopt; lcMatrix44 Transform; // if (TrainTrackType != lcTrainTrackType::Left) - Transform = NewTrackInfo->mConnections[0].Transform; +// Transform = NewTrackInfo->mConnections[NewConnectionIndex].Transform; // else // { -// Transform = lcMatrix44AffineInverse(TrainTrackInfo->mConnections[0].Transform); -// Transform = lcMul(Transform, lcMatrix44RotationZ(LC_PI)); + Transform = lcMatrix44AffineInverse(NewTrackInfo->mConnections[NewConnectionIndex].Transform); + Transform = lcMul(Transform, lcMatrix44RotationZ(LC_PI)); // } - Transform = lcMul(Transform, CurrentTrackInfo->GetConnections()[ConnectionIndex].Transform); + Transform = lcMul(Transform, CurrentTrackInfo->GetConnections()[CurrentConnectionIndex].Transform); Transform = lcMul(Transform, CurrentPiece->mModelWorld); return Transform; diff --git a/common/lc_traintrack.h b/common/lc_traintrack.h index dcb00b79..2885415b 100644 --- a/common/lc_traintrack.h +++ b/common/lc_traintrack.h @@ -27,7 +27,8 @@ public: std::pair GetPieceInsertTransform(lcPiece* Piece, quint32 ConnectionIndex, lcTrainTrackType TrainTrackType) const; static std::optional GetPieceInsertTransform(lcPiece* CurrentPiece, PieceInfo* Info); - static bool ArePiecesConnected(const lcPiece* Piece1, int ConnectionIndex1, const lcPiece* Piece2); + static std::optional GetConnectionTransform(lcPiece* CurrentPiece, quint32 CurrentConnectionIndex, PieceInfo* Info, quint32 NewConnectionIndex); + static int GetPieceConnectionIndex(const lcPiece* Piece1, int ConnectionIndex1, const lcPiece* Piece2); void AddConnection(const lcTrainTrackConnection& TrainTrackConnection) { diff --git a/common/lc_view.cpp b/common/lc_view.cpp index 81d2cf6a..3801c5c9 100644 --- a/common/lc_view.cpp +++ b/common/lc_view.cpp @@ -1925,6 +1925,7 @@ lcCursor lcView::GetCursor() const lcCursor::Rotate, // lcTrackTool::RotateZ lcCursor::Rotate, // lcTrackTool::RotateXY lcCursor::Rotate, // lcTrackTool::RotateXYZ + lcCursor::Rotate, // lcTrackTool::RotateTrainTrack lcCursor::Move, // lcTrackTool::ScalePlus lcCursor::Move, // lcTrackTool::ScaleMinus lcCursor::Delete, // lcTrackTool::Eraser @@ -2034,6 +2035,7 @@ lcTool lcView::GetCurrentTool() const lcTool::Rotate, // lcTrackTool::RotateZ lcTool::Rotate, // lcTrackTool::RotateXY lcTool::Rotate, // lcTrackTool::RotateXYZ + lcTool::Rotate, // lcTrackTool::RotateTrainTrack lcTool::Move, // lcTrackTool::ScalePlus lcTool::Move, // lcTrackTool::ScaleMinus lcTool::Eraser, // lcTrackTool::Eraser @@ -2560,6 +2562,18 @@ void lcView::OnButtonDown(lcTrackButton TrackButton) StartTracking(TrackButton); break; + case lcTrackTool::RotateTrainTrack: + { + quint32 ConnectionIndex = mTrackToolSection & 0xff; + + ActiveModel->RotateTrainTrackToolClicked(ConnectionIndex); + + mToolClicked = true; + UpdateTrackTool(); + } + break; + + case lcTrackTool::ScalePlus: case lcTrackTool::ScaleMinus: if (ActiveModel->AnyPiecesSelected()) @@ -2992,6 +3006,7 @@ void lcView::OnMouseMove() } break; + case lcTrackTool::RotateTrainTrack: case lcTrackTool::Eraser: case lcTrackTool::Paint: case lcTrackTool::ColorPicker: diff --git a/common/lc_view.h b/common/lc_view.h index a1cee772..d3208181 100644 --- a/common/lc_view.h +++ b/common/lc_view.h @@ -70,6 +70,7 @@ enum class lcTrackTool RotateZ, RotateXY, RotateXYZ, + RotateTrainTrack, ScalePlus, ScaleMinus, Eraser, diff --git a/common/lc_viewmanipulator.cpp b/common/lc_viewmanipulator.cpp index 676d5579..5cee29a7 100644 --- a/common/lc_viewmanipulator.cpp +++ b/common/lc_viewmanipulator.cpp @@ -762,6 +762,9 @@ bool lcViewManipulator::IsTrackToolAllowed(lcTrackTool TrackTool, quint32 Allowe case lcTrackTool::RotateXYZ: return (AllowedTransforms & (LC_OBJECT_TRANSFORM_ROTATE_X | LC_OBJECT_TRANSFORM_ROTATE_Y | LC_OBJECT_TRANSFORM_ROTATE_Z)) == (LC_OBJECT_TRANSFORM_ROTATE_X | LC_OBJECT_TRANSFORM_ROTATE_Y | LC_OBJECT_TRANSFORM_ROTATE_Z); + case lcTrackTool::RotateTrainTrack: + return true; + case lcTrackTool::ScalePlus: case lcTrackTool::ScaleMinus: return AllowedTransforms & (LC_OBJECT_TRANSFORM_SCALE_X | LC_OBJECT_TRANSFORM_SCALE_Y | LC_OBJECT_TRANSFORM_SCALE_Z); @@ -996,6 +999,23 @@ std::pair lcViewManipulator::UpdateSelectMove() } } } + + lcObjectRayTest ObjectRayTest; + + ObjectRayTest.PiecesOnly = true; + ObjectRayTest.IgnoreSelected = false; + ObjectRayTest.ViewCamera = mView->GetCamera(); + ObjectRayTest.Start = StartEnd[0]; + ObjectRayTest.End = StartEnd[1]; + + Piece->RayTestConnectedTrainTracks(ObjectRayTest); + + if (ObjectRayTest.Distance < ClosestIntersectionDistance && ObjectRayTest.ObjectSection.Section >= LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST) + { + NewTrackTool = lcTrackTool::RotateTrainTrack; + ClosestIntersectionDistance = ObjectRayTest.Distance; + NewTrackSection = ObjectRayTest.ObjectSection.Section - LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST; + } } } diff --git a/common/piece.cpp b/common/piece.cpp index 1dc4d425..91186ffd 100644 --- a/common/piece.cpp +++ b/common/piece.cpp @@ -463,6 +463,41 @@ void lcPiece::RemoveTime(lcStep Start, lcStep Time) mRotation.RemoveTime(Start, Time); } +void lcPiece::RayTestConnectedTrainTracks(lcObjectRayTest& ObjectRayTest) const +{ + const lcMatrix44 InverseWorldMatrix = lcMatrix44AffineInverse(mModelWorld); + const lcVector3 Start = lcMul31(ObjectRayTest.Start, InverseWorldMatrix); + const lcVector3 End = lcMul31(ObjectRayTest.End, InverseWorldMatrix); + + if (mPieceInfo->GetTrainTrackInfo() && AreTrainTrackConnectionsVisible()) + { + const lcVector3 Min(-LC_PIECE_CONTROL_POINT_SIZE, -LC_PIECE_CONTROL_POINT_SIZE, -LC_PIECE_CONTROL_POINT_SIZE); + const lcVector3 Max(LC_PIECE_CONTROL_POINT_SIZE, LC_PIECE_CONTROL_POINT_SIZE, LC_PIECE_CONTROL_POINT_SIZE); + const std::vector& Connections = mPieceInfo->GetTrainTrackInfo()->GetConnections(); + + for (quint32 ConnectionIndex = 0; ConnectionIndex < Connections.size(); ConnectionIndex++) + { + if (!mTrainTrackConnections[ConnectionIndex]) + continue; + + const lcMatrix44 InverseTransform = lcMatrix44AffineInverse(Connections[ConnectionIndex].Transform); + const lcVector3 PointStart = lcMul31(Start, InverseTransform); + const lcVector3 PointEnd = lcMul31(End, InverseTransform); + + float Distance; + lcVector3 Plane; + + if (lcBoundingBoxRayIntersectDistance(Min, Max, PointStart, PointEnd, &Distance, nullptr, &Plane)) + { + ObjectRayTest.ObjectSection.Object = const_cast(this); + ObjectRayTest.ObjectSection.Section = LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST + ConnectionIndex; + ObjectRayTest.Distance = Distance; + ObjectRayTest.PieceInfoRayTest.Plane = Plane; + } + } + } +} + void lcPiece::RayTest(lcObjectRayTest& ObjectRayTest) const { const lcMatrix44 InverseWorldMatrix = lcMatrix44AffineInverse(mModelWorld); @@ -731,19 +766,23 @@ void lcPiece::DrawTrainTrackInterface(lcContext* Context, const lcMatrix44& Worl for (quint32 ConnectionIndex = 0; ConnectionIndex < Connections.size(); ConnectionIndex++) { - if (mTrainTrackConnections[ConnectionIndex]) - continue; - Context->SetWorldMatrix(lcMul(Connections[ConnectionIndex].Transform, WorldMatrix)); Context->SetVertexBufferPointer(Verts); Context->SetVertexFormatPosition(3); Context->SetIndexBufferPointer(Indices); - if (IsFocused(LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST + ConnectionIndex)) - Context->SetColor(ConnectionFocusedColor); + if (mTrainTrackConnections[ConnectionIndex]) + { + Context->SetColor(lcVector4(1.0f, 0.0f, 0.0f, 1.0f)); + } else - Context->SetColor(ConnectionColor); + { + if (IsFocused(LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST + ConnectionIndex)) + Context->SetColor(ConnectionFocusedColor); + else + Context->SetColor(ConnectionColor); + } Context->DrawIndexedPrimitives(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0); } diff --git a/common/piece.h b/common/piece.h index cc184e60..29d5ffe1 100644 --- a/common/piece.h +++ b/common/piece.h @@ -107,6 +107,7 @@ public: } void RayTest(lcObjectRayTest& ObjectRayTest) const override; + void RayTestConnectedTrainTracks(lcObjectRayTest& ObjectRayTest) const; void BoxTest(lcObjectBoxTest& ObjectBoxTest) const override; void DrawInterface(lcContext* Context, const lcScene& Scene) const override; QVariant GetPropertyValue(lcObjectPropertyId PropertyId) const override;