Added gizmo to rotate train tracks.

This commit is contained in:
Leonardo Zide 2024-11-30 12:29:15 -08:00
parent 41097f8489
commit fd61c1c9f9
9 changed files with 162 additions and 22 deletions

View file

@ -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<lcPiece>& 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<lcMatrix44> 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);

View file

@ -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);

View file

@ -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<PieceInfo*, lcMatrix44> 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<lcTrainTrackConnection>& 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<int>(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<lcMatrix44> 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<lcMatrix44> 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<lcMatrix44> 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;

View file

@ -27,7 +27,8 @@ public:
std::pair<PieceInfo*, lcMatrix44> GetPieceInsertTransform(lcPiece* Piece, quint32 ConnectionIndex, lcTrainTrackType TrainTrackType) const;
static std::optional<lcMatrix44> GetPieceInsertTransform(lcPiece* CurrentPiece, PieceInfo* Info);
static bool ArePiecesConnected(const lcPiece* Piece1, int ConnectionIndex1, const lcPiece* Piece2);
static std::optional<lcMatrix44> 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)
{

View file

@ -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:

View file

@ -70,6 +70,7 @@ enum class lcTrackTool
RotateZ,
RotateXY,
RotateXYZ,
RotateTrainTrack,
ScalePlus,
ScaleMinus,
Eraser,

View file

@ -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<lcTrackTool, quint32> 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;
}
}
}

View file

@ -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<lcTrainTrackConnection>& 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<lcPiece*>(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);
}

View file

@ -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;