265 lines
9.3 KiB
C++
265 lines
9.3 KiB
C++
#include "lc_global.h"
|
|
#include "lc_traintrack.h"
|
|
#include "lc_library.h"
|
|
#include "pieceinf.h"
|
|
#include "piece.h"
|
|
#include "lc_model.h"
|
|
#include "lc_application.h"
|
|
|
|
// todo:
|
|
// when moving existing pieces, lcView::OnMouseMove calls UpdateMoveTool which only takes a position and can't rotate pieces
|
|
// detect the closest connection when dragging over pieces
|
|
// hide some of the 12v tracks to avoid bloat
|
|
// better insert gizmo mouse detection
|
|
// auto replace cross when going over a straight section
|
|
// set focus connection after adding
|
|
|
|
std::map<quint32, PieceInfo*> lcTrainTrackInfo::mSleepers;
|
|
|
|
void lcTrainTrackInfo::Initialize(lcPiecesLibrary* Library)
|
|
{
|
|
QFile ConfigFile(QLatin1String(":/resources/traintrack.json"));
|
|
|
|
if (!ConfigFile.open(QIODevice::ReadOnly))
|
|
return;
|
|
|
|
QJsonParseError Error;
|
|
QJsonDocument Document = QJsonDocument::fromJson(ConfigFile.readAll(), &Error);
|
|
|
|
if (Error.error != QJsonParseError::NoError)
|
|
{
|
|
qDebug() << Error.errorString();
|
|
}
|
|
|
|
QJsonObject Root = Document.object();
|
|
|
|
if (Root["Version"].toInt() != 1)
|
|
return;
|
|
|
|
QJsonArray JsonPieces = Root["Pieces"].toArray();
|
|
|
|
for (QJsonArray::const_iterator PiecesIt = JsonPieces.constBegin(); PiecesIt != JsonPieces.constEnd(); ++PiecesIt)
|
|
{
|
|
QJsonObject JsonPiece = PiecesIt->toObject();
|
|
|
|
QJsonArray JsonConnections = JsonPiece["Connections"].toArray();
|
|
std::vector<lcTrainTrackConnection> Connections;
|
|
|
|
for (QJsonArray::const_iterator ConnectionIt = JsonConnections.constBegin(); ConnectionIt != JsonConnections.constEnd(); ++ConnectionIt)
|
|
{
|
|
QJsonObject JsonConnection = ConnectionIt->toObject();
|
|
|
|
QJsonArray JsonPosition = JsonConnection["Position"].toArray();
|
|
lcVector3 Position(JsonPosition[0].toDouble(), JsonPosition[1].toDouble(), JsonPosition[2].toDouble());
|
|
|
|
float Rotation = JsonConnection["Rotation"].toDouble() * LC_DTOR;
|
|
QString ConnectionGroup = JsonConnection["Type"].toString();
|
|
lcTrainTrackConnectionSleeper ConnectionSleeper = lcTrainTrackConnectionSleeper::None;
|
|
|
|
if (ConnectionGroup.startsWith('+'))
|
|
{
|
|
ConnectionSleeper = lcTrainTrackConnectionSleeper::NeedsSleeper;
|
|
ConnectionGroup = ConnectionGroup.mid(1);
|
|
}
|
|
else if (ConnectionGroup.startsWith('-'))
|
|
{
|
|
ConnectionSleeper = lcTrainTrackConnectionSleeper::HasSleeper;
|
|
ConnectionGroup = ConnectionGroup.mid(1);
|
|
}
|
|
|
|
Connections.emplace_back(lcTrainTrackConnection{ lcMatrix44(lcMatrix33RotationZ(Rotation), Position), { qHash(ConnectionGroup), ConnectionSleeper } });
|
|
}
|
|
|
|
int Color = JsonPiece["Color"].toInt(16);
|
|
QJsonArray JsonIDs = JsonPiece["IDs"].toArray();
|
|
|
|
for (QJsonArray::const_iterator IDIt = JsonIDs.constBegin(); IDIt != JsonIDs.constEnd(); ++IDIt)
|
|
{
|
|
PieceInfo* Info = Library->FindPiece(IDIt->toString().toLatin1(), nullptr, false, false);
|
|
|
|
if (Info)
|
|
Info->SetTrainTrackInfo(new lcTrainTrackInfo(Connections, Color, true));
|
|
}
|
|
|
|
JsonIDs = JsonPiece["HiddenIDs"].toArray();
|
|
|
|
for (QJsonArray::const_iterator IDIt = JsonIDs.constBegin(); IDIt != JsonIDs.constEnd(); ++IDIt)
|
|
{
|
|
PieceInfo* Info = Library->FindPiece(IDIt->toString().toLatin1(), nullptr, false, false);
|
|
|
|
if (Info)
|
|
Info->SetTrainTrackInfo(new lcTrainTrackInfo(Connections, Color, false));
|
|
}
|
|
}
|
|
|
|
QJsonObject JsonSleepers = Root["Sleepers"].toObject();
|
|
|
|
mSleepers.clear();
|
|
|
|
for (QJsonObject::const_iterator SleepersIt = JsonSleepers.constBegin(); SleepersIt != JsonSleepers.constEnd(); ++SleepersIt)
|
|
{
|
|
quint32 Group = qHash(SleepersIt.key());
|
|
QString PartId = SleepersIt->toString();
|
|
|
|
PieceInfo* SleeperInfo = Library->FindPiece(PartId.toLatin1(), nullptr, false, false);
|
|
|
|
if (SleeperInfo)
|
|
mSleepers[Group] = SleeperInfo;
|
|
}
|
|
}
|
|
|
|
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 (int ConnectionIndex2 = 0; ConnectionIndex2 < static_cast<int>(Connections2.size()); ConnectionIndex2++)
|
|
{
|
|
const lcTrainTrackConnection& Connection2 = Connections2[ConnectionIndex2];
|
|
const lcMatrix44 Transform2 = lcMul(Connection2.Transform, Piece2->mModelWorld);
|
|
|
|
if (lcLengthSquared(Transform1.GetTranslation() - Transform2.GetTranslation()) > 0.1f)
|
|
continue;
|
|
|
|
float Dot = lcDot3(Transform1[0], Transform2[0]);
|
|
|
|
if (Dot < -0.99f && Dot > -1.01f)
|
|
return ConnectionIndex2;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
std::vector<lcPieceInfoTransform> lcTrainTrackInfo::GetPieceInsertTransforms(lcPiece* CurrentPiece, PieceInfo* Info, quint32 PreferredSection)
|
|
{
|
|
std::vector<lcPieceInfoTransform> Pieces;
|
|
const lcTrainTrackInfo* CurrentTrackInfo = CurrentPiece->mPieceInfo->GetTrainTrackInfo();
|
|
|
|
if (!CurrentTrackInfo || CurrentTrackInfo->GetConnections().empty())
|
|
return Pieces;
|
|
|
|
quint32 ConnectionIndex = 0;
|
|
|
|
if (PreferredSection != LC_PIECE_SECTION_INVALID && PreferredSection >= LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST)
|
|
ConnectionIndex = PreferredSection - LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST;
|
|
|
|
quint32 CheckIndex;
|
|
|
|
for (CheckIndex = 0; CheckIndex < CurrentTrackInfo->GetConnections().size(); CheckIndex++)
|
|
if (!CurrentPiece->IsTrainTrackConnected((ConnectionIndex + CheckIndex) % CurrentTrackInfo->GetConnections().size()))
|
|
break;
|
|
|
|
if (CheckIndex == CurrentTrackInfo->GetConnections().size())
|
|
return Pieces;
|
|
|
|
ConnectionIndex = (ConnectionIndex + CheckIndex) % CurrentTrackInfo->GetConnections().size();
|
|
|
|
const lcTrainTrackConnectionType& CurrentConnectionType = CurrentTrackInfo->GetConnections()[ConnectionIndex].Type;
|
|
const std::vector<lcTrainTrackConnection>& NewConnections = Info->GetTrainTrackInfo()->GetConnections();
|
|
quint32 NewConnectionIndex;// = ConnectionIndex ? 0 : 1;
|
|
|
|
for (NewConnectionIndex = 0; NewConnectionIndex < NewConnections.size(); NewConnectionIndex++)
|
|
if (AreConnectionsCompatible(CurrentConnectionType, NewConnections[NewConnectionIndex].Type))
|
|
break;
|
|
|
|
if (NewConnectionIndex == NewConnections.size())
|
|
return Pieces;
|
|
|
|
if (CurrentConnectionType.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper && NewConnections[NewConnectionIndex].Type.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper)
|
|
{
|
|
std::map<quint32, PieceInfo*>::const_iterator SleeperIt = mSleepers.find(CurrentConnectionType.Group);
|
|
|
|
if (SleeperIt == mSleepers.end())
|
|
return Pieces;
|
|
|
|
PieceInfo* SleeperInfo = SleeperIt->second;
|
|
|
|
if (!SleeperInfo)
|
|
return Pieces;
|
|
|
|
std::optional<lcMatrix44> SleeperTransform = GetConnectionTransform(CurrentPiece->mPieceInfo, CurrentPiece->mModelWorld, ConnectionIndex, SleeperInfo, 0);
|
|
|
|
if (!SleeperTransform)
|
|
return Pieces;
|
|
|
|
std::optional<lcMatrix44> Transform = GetConnectionTransform(SleeperInfo, SleeperTransform.value(), 1, Info, NewConnectionIndex);
|
|
|
|
if (Transform)
|
|
{
|
|
Pieces.emplace_back(lcPieceInfoTransform{ SleeperInfo, SleeperTransform.value() });
|
|
Pieces.emplace_back(lcPieceInfoTransform{ Info, Transform.value() });
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::optional<lcMatrix44> Transform = GetConnectionTransform(CurrentPiece->mPieceInfo, CurrentPiece->mModelWorld, ConnectionIndex, Info, NewConnectionIndex);
|
|
|
|
if (Transform)
|
|
Pieces.emplace_back(lcPieceInfoTransform{ Info, Transform.value() });
|
|
}
|
|
|
|
return Pieces;
|
|
}
|
|
|
|
std::optional<lcMatrix44> lcTrainTrackInfo::GetConnectionTransform(PieceInfo* CurrentInfo, const lcMatrix44& CurrentTransform, quint32 CurrentConnectionIndex, PieceInfo* Info, quint32 NewConnectionIndex)
|
|
{
|
|
if (!CurrentInfo || !Info)
|
|
return std::nullopt;
|
|
|
|
const lcTrainTrackInfo* CurrentTrackInfo = CurrentInfo->GetTrainTrackInfo();
|
|
|
|
if (!CurrentTrackInfo || CurrentTrackInfo->GetConnections().empty())
|
|
return std::nullopt;
|
|
|
|
if (CurrentConnectionIndex >= CurrentTrackInfo->GetConnections().size())
|
|
return std::nullopt;
|
|
|
|
lcTrainTrackInfo* NewTrackInfo = Info->GetTrainTrackInfo();
|
|
|
|
if (!NewTrackInfo || NewConnectionIndex >= NewTrackInfo->mConnections.size())
|
|
return std::nullopt;
|
|
|
|
lcMatrix44 Transform;
|
|
|
|
// if (TrainTrackType != lcTrainTrackType::Left)
|
|
// Transform = NewTrackInfo->mConnections[NewConnectionIndex].Transform;
|
|
// else
|
|
// {
|
|
Transform = lcMatrix44AffineInverse(NewTrackInfo->mConnections[NewConnectionIndex].Transform);
|
|
Transform = lcMul(Transform, lcMatrix44RotationZ(LC_PI));
|
|
// }
|
|
|
|
Transform = lcMul(Transform, CurrentTrackInfo->GetConnections()[CurrentConnectionIndex].Transform);
|
|
Transform = lcMul(Transform, CurrentTransform);
|
|
|
|
return Transform;
|
|
}
|
|
|
|
std::optional<lcMatrix44> lcTrainTrackInfo::CalculateTransformToConnection(const lcMatrix44& ConnectionTransform, PieceInfo* Info, quint32 ConnectionIndex)
|
|
{
|
|
lcTrainTrackInfo* TrackInfo = Info->GetTrainTrackInfo();
|
|
|
|
if (!TrackInfo || ConnectionIndex >= TrackInfo->mConnections.size())
|
|
return std::nullopt;
|
|
|
|
lcMatrix44 Transform;
|
|
|
|
// if (TrainTrackType != lcTrainTrackType::Left)
|
|
// Transform = NewTrackInfo->mConnections[NewConnectionIndex].Transform;
|
|
// else
|
|
// {
|
|
Transform = lcMatrix44AffineInverse(TrackInfo->mConnections[ConnectionIndex].Transform);
|
|
// Transform = lcMul(Transform, lcMatrix44RotationZ(LC_PI));
|
|
// }
|
|
|
|
Transform = lcMul(Transform, ConnectionTransform);
|
|
|
|
// Transform = lcMul(Transform, CurrentTrackInfo->GetConnections()[CurrentConnectionIndex].Transform);
|
|
// Transform = lcMul(Transform, CurrentPiece->mModelWorld);
|
|
|
|
return Transform;
|
|
}
|