mirror of
https://github.com/leozide/leocad.git
synced 2026-10-27 06:20:55 +00:00
Large models can extend beyond the default far plane after fitting, leaving the view blank or clipped. Pass an explicit adjustment flag through view, model, and camera fitting, and reuse the fitted bounds to expand clipping with a margin, rounded up to multiples of 10,000. Adjust restored ordinary cameras after applying their saved state. Preserve named cameras and explicit command-line clipping planes, and avoid recalculating clipping during redraws or navigation. Route output fitting through the view and move the bounds policy definition out of the precompiled header.
3390 lines
95 KiB
C++
3390 lines
95 KiB
C++
#include "lc_global.h"
|
|
#include "lc_view.h"
|
|
#include "lc_viewwidget.h"
|
|
#include <stdlib.h>
|
|
#include "lc_mainwindow.h"
|
|
#include "light.h"
|
|
#include "camera.h"
|
|
#include "texfont.h"
|
|
#include "lc_texture.h"
|
|
#include "piece.h"
|
|
#include "pieceinf.h"
|
|
#include "lc_traintrack.h"
|
|
#include "lc_scene.h"
|
|
#include "lc_context.h"
|
|
#include "lc_viewmanipulator.h"
|
|
#include "lc_viewsphere.h"
|
|
#include "lc_findreplacewidget.h"
|
|
#include "lc_library.h"
|
|
#include "lc_qutils.h"
|
|
#include "lc_partselectionpopup.h"
|
|
|
|
lcFindReplaceParams lcView::mFindReplaceParams;
|
|
QPointer<lcFindReplaceWidget> lcView::mFindWidget;
|
|
|
|
lcView* lcView::mLastFocusedView;
|
|
std::vector<lcView*> lcView::mViews;
|
|
|
|
lcView::lcView(lcViewType ViewType, lcModel* Model)
|
|
: mViewType(ViewType), mScene(new lcScene()), mModel(Model)
|
|
{
|
|
mContext = new lcContext();
|
|
mViews.push_back(this);
|
|
|
|
mViewManipulator = std::unique_ptr<lcViewManipulator>(new lcViewManipulator(this));
|
|
mViewSphere = std::unique_ptr<lcViewSphere>(new lcViewSphere(this));
|
|
memset(mGridSettings, 0, sizeof(mGridSettings));
|
|
|
|
mDragState = lcDragState::None;
|
|
mTrackToolFromOverlay = false;
|
|
|
|
lcView* ActiveView = gMainWindow ? gMainWindow->GetActiveView() : nullptr;
|
|
|
|
if (ActiveView)
|
|
SetCamera(ActiveView->mCamera, false);
|
|
else
|
|
{
|
|
mCamera = new lcCamera(true);
|
|
mCamera->SetViewpoint(lcViewpoint::Home);
|
|
}
|
|
}
|
|
|
|
lcView::~lcView()
|
|
{
|
|
mContext->DestroyVertexBuffer(mGridBuffer);
|
|
|
|
if (gMainWindow && mViewType == lcViewType::View)
|
|
gMainWindow->RemoveView(this);
|
|
|
|
if (mCamera && mCamera->IsSimple())
|
|
delete mCamera;
|
|
|
|
mViews.erase(std::find(mViews.begin(), mViews.end(), this));
|
|
|
|
if (mLastFocusedView == this)
|
|
mLastFocusedView = nullptr;
|
|
|
|
delete mContext;
|
|
}
|
|
|
|
std::vector<lcView*> lcView::GetModelViews(const lcModel* Model)
|
|
{
|
|
std::vector<lcView*> Views;
|
|
|
|
for (lcView* View : mViews)
|
|
if (View->GetModel() == Model)
|
|
Views.push_back(View);
|
|
|
|
return Views;
|
|
}
|
|
|
|
void lcView::UpdateProjectViews(const Project* Project)
|
|
{
|
|
for (lcView* View : mViews)
|
|
{
|
|
const lcModel* ViewModel = View->GetActiveModel();
|
|
|
|
if (ViewModel && ViewModel->GetProject() == Project)
|
|
View->Redraw();
|
|
}
|
|
}
|
|
|
|
void lcView::UpdateAllViews()
|
|
{
|
|
for (lcView* View : mViews)
|
|
View->Redraw();
|
|
}
|
|
|
|
void lcView::MakeCurrent()
|
|
{
|
|
mContext->MakeCurrent();
|
|
}
|
|
|
|
void lcView::Redraw()
|
|
{
|
|
if (mWidget)
|
|
mWidget->update();
|
|
}
|
|
|
|
void lcView::SetOffscreenContext()
|
|
{
|
|
mContext->SetOffscreenContext();
|
|
}
|
|
|
|
void lcView::SetFocus(bool Focus)
|
|
{
|
|
if (Focus)
|
|
{
|
|
if (mLastFocusedView != this)
|
|
{
|
|
delete mFindWidget;
|
|
mFindWidget = nullptr;
|
|
|
|
mLastFocusedView = this;
|
|
}
|
|
|
|
emit FocusReceived();
|
|
}
|
|
}
|
|
|
|
void lcView::SetMousePosition(int MouseX, int MouseY)
|
|
{
|
|
mMouseX = MouseX;
|
|
mMouseY = MouseY;
|
|
}
|
|
|
|
void lcView::SetMouseModifiers(Qt::KeyboardModifiers MouseModifiers)
|
|
{
|
|
mMouseModifiers = MouseModifiers;
|
|
}
|
|
|
|
lcModel* lcView::GetActiveModel() const
|
|
{
|
|
return !mActiveSubmodelInstance ? mModel : mActiveSubmodelInstance->mPieceInfo->GetModel();
|
|
}
|
|
|
|
void lcView::SetTopSubmodelActive()
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
if (mActiveSubmodelInstance)
|
|
{
|
|
ActiveModel->SetActive(false);
|
|
mActiveSubmodelInstance = nullptr;
|
|
}
|
|
|
|
GetActiveModel()->UpdateInterface();
|
|
}
|
|
|
|
void lcView::SetSelectedSubmodelActive()
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
lcObject* Object = ActiveModel->GetFocusObject();
|
|
|
|
if (mActiveSubmodelInstance)
|
|
{
|
|
ActiveModel->SetActive(false);
|
|
mActiveSubmodelInstance = nullptr;
|
|
}
|
|
|
|
if (Object && Object->IsPiece())
|
|
{
|
|
lcPiece* Piece = (lcPiece*)Object;
|
|
|
|
if (Piece->mPieceInfo->IsModel())
|
|
{
|
|
mActiveSubmodelTransform = lcMatrix44Identity();
|
|
mModel->GetPieceWorldMatrix(Piece, mActiveSubmodelTransform);
|
|
mActiveSubmodelInstance = Piece;
|
|
ActiveModel = mActiveSubmodelInstance->mPieceInfo->GetModel();
|
|
ActiveModel->SetActive(true);
|
|
RemoveCamera();
|
|
}
|
|
}
|
|
|
|
GetActiveModel()->UpdateInterface();
|
|
}
|
|
|
|
void lcView::CreateResources(lcContext* Context)
|
|
{
|
|
Q_UNUSED(Context);
|
|
|
|
gGridTexture = new lcTexture;
|
|
gGridTexture->CreateGridTexture();
|
|
}
|
|
|
|
void lcView::DestroyResources(lcContext* Context)
|
|
{
|
|
Q_UNUSED(Context);
|
|
|
|
delete gGridTexture;
|
|
gGridTexture = nullptr;
|
|
}
|
|
|
|
void lcView::RemoveCamera()
|
|
{
|
|
if (mCamera && mCamera->IsSimple())
|
|
return;
|
|
|
|
lcCamera* Camera = mCamera;
|
|
mCamera = new lcCamera(true);
|
|
|
|
if (Camera)
|
|
mCamera->CopyPosition(Camera);
|
|
else
|
|
mCamera->SetViewpoint(lcViewpoint::Home);
|
|
|
|
Redraw();
|
|
}
|
|
|
|
lcVector3 lcView::ProjectPoint(const lcVector3& Point) const
|
|
{
|
|
int Viewport[4] = { 0, 0, mWidth, mHeight };
|
|
return lcProjectPoint(Point, mCamera->mWorldView, GetProjectionMatrix(), Viewport);
|
|
}
|
|
|
|
lcVector3 lcView::UnprojectPoint(const lcVector3& Point) const
|
|
{
|
|
int Viewport[4] = { 0, 0, mWidth, mHeight };
|
|
return lcUnprojectPoint(Point, mCamera->mWorldView, GetProjectionMatrix(), Viewport);
|
|
}
|
|
|
|
void lcView::UnprojectPoints(lcVector3* Points, int NumPoints) const
|
|
{
|
|
int Viewport[4] = { 0, 0, mWidth, mHeight };
|
|
lcUnprojectPoints(Points, NumPoints, mCamera->mWorldView, GetProjectionMatrix(), Viewport);
|
|
}
|
|
|
|
lcMatrix44 lcView::GetProjectionMatrix() const
|
|
{
|
|
float AspectRatio = (float)mWidth / (float)mHeight;
|
|
|
|
if (mCamera->GetProjection() == lcCameraProjection::Orthographic)
|
|
{
|
|
float OrthoHeight = mCamera->GetOrthoHeight() / 2.0f;
|
|
float OrthoWidth = OrthoHeight * AspectRatio;
|
|
|
|
return lcMatrix44Ortho(-OrthoWidth, OrthoWidth, -OrthoHeight, OrthoHeight, mCamera->m_zNear, mCamera->m_zFar * 4);
|
|
}
|
|
else
|
|
return lcMatrix44Perspective(mCamera->m_fovy, AspectRatio, mCamera->m_zNear, mCamera->m_zFar);
|
|
}
|
|
|
|
lcMatrix44 lcView::GetTileProjectionMatrix(int CurrentRow, int CurrentColumn, int CurrentTileWidth, int CurrentTileHeight) const
|
|
{
|
|
int ImageWidth = mRenderImage.width();
|
|
int ImageHeight = mRenderImage.height();
|
|
|
|
double ImageLeft, ImageRight, ImageBottom, ImageTop, Near, Far;
|
|
double AspectRatio = (double)ImageWidth / (double)ImageHeight;
|
|
|
|
if (mCamera->GetProjection() == lcCameraProjection::Orthographic)
|
|
{
|
|
float OrthoHeight = mCamera->GetOrthoHeight() / 2.0f;
|
|
float OrthoWidth = OrthoHeight * AspectRatio;
|
|
|
|
ImageLeft = -OrthoWidth;
|
|
ImageRight = OrthoWidth;
|
|
ImageBottom = -OrthoHeight;
|
|
ImageTop = OrthoHeight;
|
|
Near = mCamera->m_zNear;
|
|
Far = mCamera->m_zFar * 4;
|
|
}
|
|
else
|
|
{
|
|
double xmin, xmax, ymin, ymax;
|
|
ymax = mCamera->m_zNear * tan(mCamera->m_fovy * 3.14159265 / 360.0);
|
|
ymin = -ymax;
|
|
xmin = ymin * AspectRatio;
|
|
xmax = ymax * AspectRatio;
|
|
|
|
ImageLeft = xmin;
|
|
ImageRight = xmax;
|
|
ImageBottom = ymin;
|
|
ImageTop = ymax;
|
|
Near = mCamera->m_zNear;
|
|
Far = mCamera->m_zFar;
|
|
}
|
|
|
|
double Left = ImageLeft + (ImageRight - ImageLeft) * (CurrentColumn * mWidth) / ImageWidth;
|
|
double Right = Left + (ImageRight - ImageLeft) * CurrentTileWidth / ImageWidth;
|
|
double Bottom = ImageBottom + (ImageTop - ImageBottom) * (CurrentRow * mHeight) / ImageHeight;
|
|
double Top = Bottom + (ImageTop - ImageBottom) * CurrentTileHeight / ImageHeight;
|
|
|
|
if (mCamera->GetProjection() == lcCameraProjection::Orthographic)
|
|
return lcMatrix44Ortho(Left, Right, Bottom, Top, Near, Far);
|
|
else
|
|
return lcMatrix44Frustum(Left, Right, Bottom, Top, Near, Far);
|
|
}
|
|
|
|
void lcView::ShowTrainTrackPopup()
|
|
{
|
|
if (mViewType != lcViewType::View)
|
|
return;
|
|
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
lcObject* Focus = ActiveModel->GetFocusObject();
|
|
|
|
if (!Focus || !Focus->IsPiece())
|
|
return;
|
|
|
|
lcPiece* Piece = (lcPiece*)Focus;
|
|
const lcTrainTrackInfo* TrainTrackInfo = Piece->mPieceInfo->GetTrainTrackInfo();
|
|
|
|
if (!TrainTrackInfo)
|
|
return;
|
|
|
|
if (mTrackToolSection < LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST)
|
|
return;
|
|
|
|
int ConnectionIndex = mTrackToolSection - LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST;
|
|
const lcTrainTrackConnectionType& ConnectionType = TrainTrackInfo->GetConnections()[ConnectionIndex].Type;
|
|
std::vector<PieceInfo*> TrainTrackParts = lcGetPiecesLibrary()->GetVisibleTrainTrackParts(ConnectionType);
|
|
|
|
auto PartSortFunc = [](const PieceInfo* a, const PieceInfo* b)
|
|
{
|
|
if (!a)
|
|
return true;
|
|
|
|
if (!b)
|
|
return false;
|
|
|
|
return strcmp(a->m_strDescription, b->m_strDescription) < 0;
|
|
};
|
|
|
|
std::sort(TrainTrackParts.begin(), TrainTrackParts.end(), PartSortFunc);
|
|
|
|
std::vector<std::pair<PieceInfo*, std::string>> Parts;
|
|
|
|
Parts.reserve(TrainTrackParts.size());
|
|
|
|
for (PieceInfo* Info : TrainTrackParts)
|
|
Parts.emplace_back(Info, std::string());
|
|
|
|
std::optional<PieceInfo*> Result = lcShowPartSelectionPopup(nullptr, Parts, gMainWindow->mColorIndex, mWidget, QCursor::pos());
|
|
PieceInfo* Info = Result.has_value() ? Result.value() : nullptr;
|
|
|
|
if (Info)
|
|
{
|
|
Piece = ActiveModel->AddPiece(Info, mTrackToolSection);
|
|
|
|
if (Piece)
|
|
ActiveModel->UpdateTrainTrackConnections(Piece, false);
|
|
}
|
|
|
|
mWidget->UpdateMousePosition();
|
|
}
|
|
|
|
void lcView::ShowContextMenu() const
|
|
{
|
|
if (mViewType != lcViewType::View)
|
|
return;
|
|
|
|
QAction** Actions = gMainWindow->mActions;
|
|
|
|
QMenu* Popup = new QMenu();
|
|
|
|
Popup->addAction(Actions[LC_EDIT_CUT]);
|
|
Popup->addAction(Actions[LC_EDIT_COPY]);
|
|
Popup->addAction(Actions[LC_EDIT_PASTE]);
|
|
Popup->addAction(Actions[LC_EDIT_PASTE_STEPS]);
|
|
Popup->addAction(Actions[LC_PIECE_DUPLICATE]);
|
|
|
|
Popup->addSeparator();
|
|
|
|
Popup->addAction(Actions[LC_PIECE_CONTROL_POINT_INSERT]);
|
|
Popup->addAction(Actions[LC_PIECE_CONTROL_POINT_REMOVE]);
|
|
|
|
Popup->addSeparator();
|
|
|
|
Popup->addAction(Actions[LC_PIECE_EDIT_SELECTED_SUBMODEL]);
|
|
Popup->addAction(Actions[LC_PIECE_EDIT_END_SUBMODEL]);
|
|
Popup->addAction(Actions[LC_PIECE_VIEW_SELECTED_MODEL]);
|
|
Popup->addAction(Actions[LC_PIECE_INLINE_SELECTED_MODELS]);
|
|
Popup->addAction(Actions[LC_PIECE_MOVE_SELECTION_TO_MODEL]);
|
|
|
|
Popup->addSeparator();
|
|
|
|
Popup->addMenu(gMainWindow->GetToolsMenu());
|
|
Popup->addMenu(gMainWindow->GetViewpointMenu());
|
|
Popup->addMenu(gMainWindow->GetCameraMenu());
|
|
Popup->addMenu(gMainWindow->GetProjectionMenu());
|
|
Popup->addMenu(gMainWindow->GetShadingMenu());
|
|
|
|
Popup->addSeparator();
|
|
|
|
Popup->addAction(Actions[LC_VIEW_SPLIT_HORIZONTAL]);
|
|
Popup->addAction(Actions[LC_VIEW_SPLIT_VERTICAL]);
|
|
Popup->addAction(Actions[LC_VIEW_REMOVE_VIEW]);
|
|
Popup->addAction(Actions[LC_VIEW_RESET_VIEWS]);
|
|
|
|
Popup->exec(QCursor::pos());
|
|
delete Popup;
|
|
}
|
|
|
|
bool lcView::CloseFindReplaceDialog()
|
|
{
|
|
if (mFindWidget && (mWidget->hasFocus() || mFindWidget->focusWidget()))
|
|
{
|
|
delete mFindWidget;
|
|
mFindWidget = nullptr;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void lcView::ShowFindReplaceWidget(bool Replace)
|
|
{
|
|
delete mFindWidget;
|
|
mFindReplaceParams = lcFindReplaceParams();
|
|
mFindWidget = new lcFindReplaceWidget(mWidget, GetActiveModel(), Replace);
|
|
}
|
|
|
|
lcVector3 lcView::GetMoveDirection(const lcVector3& Direction) const
|
|
{
|
|
if (lcGetPreferences().mFixedAxes)
|
|
return Direction;
|
|
|
|
// TODO: rewrite this
|
|
lcVector3 axis = Direction;
|
|
|
|
lcVector3 Pts[3] = { lcVector3(5.0f, 5.0f, 0.1f), lcVector3(10.0f, 5.0f, 0.1f), lcVector3(5.0f, 10.0f, 0.1f) };
|
|
UnprojectPoints(Pts, 3);
|
|
|
|
float ax, ay;
|
|
lcVector3 vx((Pts[1][0] - Pts[0][0]), (Pts[1][1] - Pts[0][1]), 0);//Pts[1][2] - Pts[0][2] };
|
|
vx.Normalize();
|
|
lcVector3 x(1, 0, 0);
|
|
ax = acosf(lcDot(vx, x));
|
|
|
|
lcVector3 vy((Pts[2][0] - Pts[0][0]), (Pts[2][1] - Pts[0][1]), 0);//Pts[2][2] - Pts[0][2] };
|
|
vy.Normalize();
|
|
lcVector3 y(0, -1, 0);
|
|
ay = acosf(lcDot(vy, y));
|
|
|
|
if (ax > 135)
|
|
axis[0] = -axis[0];
|
|
|
|
if (ay < 45)
|
|
axis[1] = -axis[1];
|
|
|
|
if (ax >= 45 && ax <= 135)
|
|
{
|
|
float tmp = axis[0];
|
|
|
|
ax = acosf(lcDot(vx, y));
|
|
if (ax > 90)
|
|
{
|
|
axis[0] = -axis[1];
|
|
axis[1] = tmp;
|
|
}
|
|
else
|
|
{
|
|
axis[0] = axis[1];
|
|
axis[1] = -tmp;
|
|
}
|
|
}
|
|
|
|
return axis;
|
|
}
|
|
|
|
void lcView::UpdatePiecePreview()
|
|
{
|
|
PieceInfo* PreviewInfo = gMainWindow->GetCurrentPieceInfo();
|
|
std::vector<lcInsertPieceInfo> InsertPieceInfo;
|
|
bool IsConnection;
|
|
|
|
if (PreviewInfo)
|
|
{
|
|
std::tie(InsertPieceInfo, IsConnection) = GetMouseInsertPieceInfo(false, true, PreviewInfo, nullptr);
|
|
}
|
|
|
|
GetActiveModel()->SetPreviewInsertPieceInfo(std::move(InsertPieceInfo));
|
|
}
|
|
|
|
std::pair<std::vector<lcInsertPieceInfo>, bool> lcView::GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, PieceInfo* Info, lcPiece* MovingPiece) const
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
lcGetPiecesLibrary()->EnsurePieceAssetsReady(Info);
|
|
|
|
if (MovingPiece && Info->GetSynthInfo())
|
|
lcGetPiecesLibrary()->EnsureSynthMeshesReady({ MovingPiece });
|
|
|
|
lcObjectRayTest ObjectRayTest = RayTest(true, IgnoreSelected);
|
|
|
|
// Settling a hit's geometry can reveal another pending piece behind it, so pick again until the hit is stable.
|
|
while (ObjectRayTest.ObjectSection.Object)
|
|
{
|
|
lcPiece* MousePiece = reinterpret_cast<lcPiece*>(ObjectRayTest.ObjectSection.Object);
|
|
PieceInfo* MouseInfo = MousePiece->mPieceInfo;
|
|
const bool Retest = MouseInfo->mState != lcPieceInfoState::Loaded || MouseInfo->IsModel() || MouseInfo->IsProject() || MousePiece->IsGeneratedMeshPending();
|
|
|
|
lcGetPiecesLibrary()->EnsurePieceAssetsReady(MouseInfo);
|
|
|
|
if (MouseInfo->GetSynthInfo())
|
|
lcGetPiecesLibrary()->EnsureSynthMeshesReady({ MousePiece });
|
|
|
|
if (!Retest)
|
|
break;
|
|
|
|
lcObjectRayTest ReadyRayTest = RayTest(true, IgnoreSelected);
|
|
const bool SamePiece = ReadyRayTest.ObjectSection.Object == ObjectRayTest.ObjectSection.Object;
|
|
ObjectRayTest = ReadyRayTest;
|
|
|
|
if (SamePiece)
|
|
break;
|
|
}
|
|
|
|
const lcBoundingBox& BoundingBox = MovingPiece ? MovingPiece->GetBoundingBox() : Info->GetBoundingBox();
|
|
|
|
if (ObjectRayTest.ObjectSection.Object)
|
|
{
|
|
lcPiece* MousePiece = reinterpret_cast<lcPiece*>(ObjectRayTest.ObjectSection.Object);
|
|
lcTrainTrackInfo* RayTrackInfo = MousePiece->mPieceInfo->GetTrainTrackInfo();
|
|
lcTrainTrackInfo* InfoTrackInfo = Info->GetTrainTrackInfo();
|
|
|
|
if (RayTrackInfo && InfoTrackInfo)
|
|
{
|
|
ActiveModel->UpdateTrainTrackConnections(MousePiece, IgnoreSelected);
|
|
|
|
lcVector3 ClosestPoint = ObjectRayTest.Start + lcNormalize(ObjectRayTest.End - ObjectRayTest.Start) * ObjectRayTest.Distance;
|
|
quint32 FocusSection = MousePiece->GetFocusSection();
|
|
|
|
std::vector<lcInsertPieceInfo> TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(MousePiece, Info, MovingPiece, gMainWindow->mColorIndex, FocusSection, AllowNewPieces, ClosestPoint);
|
|
|
|
if (!TrainTracks.empty())
|
|
return { TrainTracks, true };
|
|
}
|
|
|
|
lcVector3 Position = ObjectRayTest.PieceInfoRayTest.Plane;
|
|
|
|
if (Position.x > 0.0f)
|
|
Position.x += fabsf(BoundingBox.Min.x);
|
|
else if (Position.x < 0.0f)
|
|
Position.x -= fabsf(BoundingBox.Max.x);
|
|
else if (Position.y > 0.0f)
|
|
Position.y += fabsf(BoundingBox.Min.y);
|
|
else if (Position.y < 0.0f)
|
|
Position.y -= fabsf(BoundingBox.Max.y);
|
|
else if (Position.z > 0.0f)
|
|
Position.z += fabsf(BoundingBox.Min.z);
|
|
else if (Position.z < 0.0f)
|
|
Position.z -= fabsf(BoundingBox.Max.z);
|
|
|
|
if (gMainWindow->GetRelativeTransform())
|
|
Position = lcMul31(ActiveModel->SnapPosition(Position), ObjectRayTest.PieceInfoRayTest.Transform);
|
|
else
|
|
Position = ActiveModel->SnapPosition(lcMul31(Position, ObjectRayTest.PieceInfoRayTest.Transform));
|
|
|
|
lcMatrix44 WorldMatrix = ObjectRayTest.PieceInfoRayTest.Transform;
|
|
WorldMatrix.SetTranslation(Position);
|
|
|
|
return { { lcInsertPieceInfo{ Info, WorldMatrix, gMainWindow->mColorIndex } }, false };
|
|
}
|
|
|
|
std::array<lcVector3, 2> ClickPoints = {{ lcVector3((float)mMouseX, (float)mMouseY, 0.0f), lcVector3((float)mMouseX, (float)mMouseY, 1.0f) }};
|
|
UnprojectPoints(ClickPoints.data(), 2);
|
|
|
|
if (ActiveModel != mModel)
|
|
{
|
|
lcMatrix44 InverseMatrix = lcMatrix44AffineInverse(mActiveSubmodelTransform);
|
|
|
|
for (lcVector3& Point : ClickPoints)
|
|
Point = lcMul31(Point, InverseMatrix);
|
|
}
|
|
|
|
lcVector3 Intersection;
|
|
|
|
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 } }, false };
|
|
}
|
|
|
|
lcVector3 Position;
|
|
|
|
if (!ActiveModel->GetFocusPosition(Position))
|
|
Position = ActiveModel->GetSelectionOrModelCenter();
|
|
|
|
lcVector3 FrontVector(mCamera->mTargetPosition - mCamera->mPosition);
|
|
|
|
if (lcLineSegmentPlaneIntersection(&Intersection, ClickPoints[0], ClickPoints[1], lcVector4(FrontVector, -lcDot(FrontVector, Position))))
|
|
{
|
|
Intersection = ActiveModel->SnapPosition(Intersection);
|
|
return { { lcInsertPieceInfo{ Info, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false };
|
|
}
|
|
|
|
return { { lcInsertPieceInfo{ Info, lcMatrix44Translation(UnprojectPoint(lcVector3((float)mMouseX, (float)mMouseY, 0.9f))), gMainWindow->mColorIndex } }, false };
|
|
}
|
|
|
|
lcVector3 lcView::GetCameraLightInsertPosition() const
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
std::array<lcVector3, 2> ClickPoints = { { lcVector3((float)mMouseX, (float)mMouseY, 0.0f), lcVector3((float)mMouseX, (float)mMouseY, 1.0f) } };
|
|
UnprojectPoints(ClickPoints.data(), 2);
|
|
|
|
if (ActiveModel != mModel)
|
|
{
|
|
lcMatrix44 InverseMatrix = lcMatrix44AffineInverse(mActiveSubmodelTransform);
|
|
|
|
for (lcVector3& Point : ClickPoints)
|
|
Point = lcMul31(Point, InverseMatrix);
|
|
}
|
|
|
|
lcVector3 Min, Max;
|
|
lcVector3 Center;
|
|
|
|
if (ActiveModel->GetVisiblePiecesBoundingBox(Min, Max))
|
|
Center = (Min + Max) / 2.0f;
|
|
else
|
|
Center = lcVector3(0.0f, 0.0f, 0.0f);
|
|
|
|
return lcRayPointClosestPoint(Center, ClickPoints[0], ClickPoints[1]);
|
|
}
|
|
|
|
void lcView::GetRayUnderPointer(lcVector3& Start, lcVector3& End) const
|
|
{
|
|
lcVector3 StartEnd[2] =
|
|
{
|
|
lcVector3((float)mMouseX, (float)mMouseY, 0.0f),
|
|
lcVector3((float)mMouseX, (float)mMouseY, 1.0f)
|
|
};
|
|
|
|
UnprojectPoints(StartEnd, 2);
|
|
|
|
Start = StartEnd[0];
|
|
End = StartEnd[1];
|
|
}
|
|
|
|
lcObjectRayTest lcView::RayTest(bool PiecesOnly, bool IgnoreSelected) const
|
|
{
|
|
lcVector3 StartEnd[2] =
|
|
{
|
|
lcVector3((float)mMouseX, (float)mMouseY, 0.0f),
|
|
lcVector3((float)mMouseX, (float)mMouseY, 1.0f)
|
|
};
|
|
|
|
UnprojectPoints(StartEnd, 2);
|
|
|
|
lcObjectRayTest ObjectRayTest;
|
|
|
|
ObjectRayTest.PiecesOnly = PiecesOnly;
|
|
ObjectRayTest.IgnoreSelected = IgnoreSelected;
|
|
ObjectRayTest.ViewCamera = mCamera;
|
|
ObjectRayTest.Start = StartEnd[0];
|
|
ObjectRayTest.End = StartEnd[1];
|
|
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
if (ActiveModel != mModel)
|
|
{
|
|
lcMatrix44 InverseMatrix = lcMatrix44AffineInverse(mActiveSubmodelTransform);
|
|
|
|
ObjectRayTest.Start = lcMul31(ObjectRayTest.Start, InverseMatrix);
|
|
ObjectRayTest.End = lcMul31(ObjectRayTest.End, InverseMatrix);
|
|
}
|
|
|
|
ActiveModel->RayTest(ObjectRayTest);
|
|
|
|
return ObjectRayTest;
|
|
}
|
|
|
|
lcObjectSection lcView::FindObjectUnderPointer(bool PiecesOnly, bool IgnoreSelected) const
|
|
{
|
|
return RayTest(PiecesOnly, IgnoreSelected).ObjectSection;
|
|
}
|
|
|
|
std::vector<lcObject*> lcView::FindObjectsInBox(float x1, float y1, float x2, float y2) const
|
|
{
|
|
float Left, Top, Bottom, Right;
|
|
|
|
if (x1 < x2)
|
|
{
|
|
Left = x1;
|
|
Right = x2;
|
|
}
|
|
else
|
|
{
|
|
Left = x2;
|
|
Right = x1;
|
|
}
|
|
|
|
if (y1 > y2)
|
|
{
|
|
Top = y1;
|
|
Bottom = y2;
|
|
}
|
|
else
|
|
{
|
|
Top = y2;
|
|
Bottom = y1;
|
|
}
|
|
|
|
std::array<lcVector3, 6> Corners =
|
|
{{
|
|
lcVector3(Left, Top, 0),
|
|
lcVector3(Left, Bottom, 0),
|
|
lcVector3(Right, Bottom, 0),
|
|
lcVector3(Right, Top, 0),
|
|
lcVector3(Left, Top, 1),
|
|
lcVector3(Right, Bottom, 1)
|
|
}};
|
|
|
|
UnprojectPoints(Corners.data(), (int)Corners.size());
|
|
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
if (ActiveModel != mModel)
|
|
{
|
|
lcMatrix44 InverseMatrix = lcMatrix44AffineInverse(mActiveSubmodelTransform);
|
|
|
|
for (lcVector3& Point : Corners)
|
|
Point = lcMul31(Point, InverseMatrix);
|
|
}
|
|
|
|
lcVector3 PlaneNormals[6];
|
|
PlaneNormals[0] = lcNormalize(lcCross(Corners[4] - Corners[0], Corners[1] - Corners[0])); // Left
|
|
PlaneNormals[1] = lcNormalize(lcCross(Corners[5] - Corners[2], Corners[3] - Corners[2])); // Right
|
|
PlaneNormals[2] = lcNormalize(lcCross(Corners[3] - Corners[0], Corners[4] - Corners[0])); // Top
|
|
PlaneNormals[3] = lcNormalize(lcCross(Corners[1] - Corners[2], Corners[5] - Corners[2])); // Bottom
|
|
PlaneNormals[4] = lcNormalize(lcCross(Corners[1] - Corners[0], Corners[3] - Corners[0])); // Front
|
|
PlaneNormals[5] = lcNormalize(lcCross(Corners[1] - Corners[2], Corners[3] - Corners[2])); // Back
|
|
|
|
lcObjectBoxTest ObjectBoxTest;
|
|
ObjectBoxTest.ViewCamera = mCamera;
|
|
ObjectBoxTest.Planes[0] = lcVector4(PlaneNormals[0], -lcDot(PlaneNormals[0], Corners[0]));
|
|
ObjectBoxTest.Planes[1] = lcVector4(PlaneNormals[1], -lcDot(PlaneNormals[1], Corners[5]));
|
|
ObjectBoxTest.Planes[2] = lcVector4(PlaneNormals[2], -lcDot(PlaneNormals[2], Corners[0]));
|
|
ObjectBoxTest.Planes[3] = lcVector4(PlaneNormals[3], -lcDot(PlaneNormals[3], Corners[5]));
|
|
ObjectBoxTest.Planes[4] = lcVector4(PlaneNormals[4], -lcDot(PlaneNormals[4], Corners[0]));
|
|
ObjectBoxTest.Planes[5] = lcVector4(PlaneNormals[5], -lcDot(PlaneNormals[5], Corners[5]));
|
|
|
|
ActiveModel->BoxTest(ObjectBoxTest);
|
|
|
|
return ObjectBoxTest.Objects;
|
|
}
|
|
|
|
std::vector<QImage> lcView::GetStepImages(lcStep Start, lcStep End)
|
|
{
|
|
std::vector<QImage> Images;
|
|
mModel->WaitForAssets();
|
|
|
|
if (!BeginRenderToImage(mWidth, mHeight))
|
|
{
|
|
if (gMainWindow)
|
|
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Error creating images."));
|
|
|
|
return Images;
|
|
}
|
|
|
|
const lcStep CurrentStep = mModel->GetCurrentStep();
|
|
bool RenderFailed = false;
|
|
|
|
for (lcStep Step = Start; Step <= End; Step++)
|
|
{
|
|
mModel->SetTemporaryStep(Step);
|
|
|
|
OnDraw();
|
|
|
|
QImage Image = GetRenderImage();
|
|
|
|
if (Image.isNull())
|
|
{
|
|
RenderFailed = true;
|
|
Images.clear();
|
|
break;
|
|
}
|
|
|
|
Images.emplace_back(std::move(Image));
|
|
}
|
|
|
|
EndRenderToImage();
|
|
|
|
mModel->SetTemporaryStep(CurrentStep);
|
|
|
|
if (!mModel->IsActive())
|
|
mModel->CalculateStep(LC_STEP_MAX);
|
|
|
|
if (RenderFailed && gMainWindow)
|
|
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Error creating images."));
|
|
|
|
return Images;
|
|
}
|
|
|
|
lcResult<void> lcView::SaveStepImages(const QString& BaseName, bool AddStepSuffix, lcStep Start, lcStep End, std::function<void(const QString&)> ProgressCallback)
|
|
{
|
|
std::vector<QImage> Images = GetStepImages(Start, End);
|
|
|
|
if (Images.size() != static_cast<size_t>(End - Start + 1))
|
|
return lcUnexpected(tr("Error creating images."));
|
|
|
|
for (lcStep Step = Start; Step <= End; Step++)
|
|
{
|
|
QString FileName;
|
|
|
|
if (AddStepSuffix)
|
|
FileName = BaseName.arg(Step, 2, 10, QLatin1Char('0'));
|
|
else
|
|
FileName = BaseName;
|
|
|
|
QImageWriter Writer(FileName);
|
|
|
|
if (Writer.format().isEmpty())
|
|
Writer.setFormat("png");
|
|
|
|
if (!Writer.write(Images[Step - Start]))
|
|
return lcUnexpected(tr("Error writing to file '%1':\n%2").arg(FileName, Writer.errorString()));
|
|
|
|
if (ProgressCallback)
|
|
ProgressCallback(FileName);
|
|
}
|
|
|
|
return lcResult<void>();
|
|
}
|
|
|
|
bool lcView::BeginRenderToImage(int Width, int Height)
|
|
{
|
|
lcContext* Context = lcContext::GetGlobalOffscreenContext();
|
|
GLint MaxTexture;
|
|
|
|
Context->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &MaxTexture);
|
|
MaxTexture = qMin(MaxTexture, 2048);
|
|
|
|
const int Samples = QSurfaceFormat::defaultFormat().samples();
|
|
if (Samples > 1)
|
|
MaxTexture /= Samples;
|
|
|
|
int TileWidth = qMin(Width, MaxTexture);
|
|
int TileHeight = qMin(Height, MaxTexture);
|
|
|
|
mWidth = TileWidth;
|
|
mHeight = TileHeight;
|
|
mRenderImage = QImage(Width, Height, QImage::Format_ARGB32);
|
|
|
|
QOpenGLFramebufferObjectFormat Format;
|
|
Format.setAttachment(QOpenGLFramebufferObject::Depth);
|
|
|
|
if (QSurfaceFormat::defaultFormat().samples() > 1)
|
|
Format.setSamples(QSurfaceFormat::defaultFormat().samples());
|
|
|
|
mRenderFramebuffer = std::unique_ptr<QOpenGLFramebufferObject>(new QOpenGLFramebufferObject(QSize(TileWidth, TileHeight), Format));
|
|
|
|
if (!mRenderFramebuffer->bind())
|
|
{
|
|
mRenderFramebuffer.reset();
|
|
mRenderImage = QImage();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void lcView::EndRenderToImage()
|
|
{
|
|
mRenderFramebuffer.reset();
|
|
}
|
|
|
|
QImage lcView::GetRenderImage() const
|
|
{
|
|
return mRenderImage;
|
|
}
|
|
|
|
void lcView::BindRenderFramebuffer()
|
|
{
|
|
mRenderFramebuffer->bind();
|
|
}
|
|
|
|
void lcView::UnbindRenderFramebuffer()
|
|
{
|
|
mRenderFramebuffer->release();
|
|
}
|
|
|
|
QImage lcView::GetRenderFramebufferImage() const
|
|
{
|
|
return mRenderFramebuffer->toImage();
|
|
}
|
|
|
|
void lcView::OnDraw()
|
|
{
|
|
if (!mModel)
|
|
return;
|
|
|
|
//#define LC_PROFILE_DRAW
|
|
#ifdef LC_PROFILE_DRAW
|
|
static std::array<GLuint64, 30> QueryResults;
|
|
static std::array<qint64, 30> TimerResults;
|
|
static int FrameNumber;
|
|
|
|
QElapsedTimer Timer;
|
|
QOpenGLTimerQuery Query;
|
|
|
|
if (!Query.isCreated())
|
|
Query.create();
|
|
|
|
Query.begin();
|
|
Timer.start();
|
|
#endif
|
|
|
|
const lcPreferences& Preferences = lcGetPreferences();
|
|
const bool DrawOverlays = mWidget != nullptr;
|
|
const bool DrawInterface = mWidget != nullptr && mViewType == lcViewType::View;
|
|
|
|
lcShadingMode ShadingMode = Preferences.mShadingMode;
|
|
if (ShadingMode == lcShadingMode::Wireframe && !mWidget)
|
|
ShadingMode = lcShadingMode::Flat;
|
|
|
|
mScene->SetShadingMode(ShadingMode);
|
|
mScene->SetAllowLOD(Preferences.mAllowLOD && mWidget != nullptr);
|
|
mScene->SetRequireCompleteAssets(static_cast<bool>(mRenderFramebuffer));
|
|
mScene->SetRequireGeneratedMeshes(mRenderFramebuffer && mViewType != lcViewType::PartsList);
|
|
mScene->SetLODDistance(Preferences.mMeshLODDistance);
|
|
|
|
mScene->Begin(mCamera->mWorldView);
|
|
|
|
mScene->SetActiveSubmodelInstance(mActiveSubmodelInstance, mActiveSubmodelTransform);
|
|
mScene->SetDrawInterface(DrawInterface);
|
|
|
|
bool DrawInsertPreview = DrawInterface && std::any_of(mViews.begin(), mViews.end(), [](lcView* View){ return View->mTrackTool == lcTrackTool::Insert; });
|
|
mScene->SetDrawInsertPreview(DrawInsertPreview);
|
|
|
|
mModel->GetScene(mScene.get(), mCamera, Preferences.mHighlightNewParts, Preferences.mFadeSteps && mViewType == lcViewType::View);
|
|
|
|
if (DrawInterface)
|
|
mScene->SetPreTranslucentCallback([this]() { DrawGrid(); });
|
|
|
|
mScene->End();
|
|
|
|
int TotalTileRows = 1;
|
|
int TotalTileColumns = 1;
|
|
|
|
if (!mRenderImage.isNull())
|
|
{
|
|
int ImageWidth = mRenderImage.width();
|
|
int ImageHeight = mRenderImage.height();
|
|
|
|
if (ImageWidth > mWidth || ImageHeight > mHeight)
|
|
{
|
|
TotalTileColumns = (mWidth + ImageWidth - 1) / mWidth;
|
|
TotalTileRows = (mHeight + ImageHeight - 1) / mHeight;
|
|
}
|
|
}
|
|
|
|
for (int CurrentTileRow = 0; CurrentTileRow < TotalTileRows; CurrentTileRow++)
|
|
{
|
|
for (int CurrentTileColumn = 0; CurrentTileColumn < TotalTileColumns; CurrentTileColumn++)
|
|
{
|
|
mContext->SetDefaultState();
|
|
mContext->SetViewport(0, 0, mWidth, mHeight);
|
|
|
|
int CurrentTileWidth, CurrentTileHeight;
|
|
|
|
if (!mRenderImage.isNull() && (TotalTileRows > 1 || TotalTileColumns > 1))
|
|
{
|
|
if (CurrentTileRow < TotalTileRows - 1)
|
|
CurrentTileHeight = mHeight;
|
|
else
|
|
CurrentTileHeight = mRenderImage.height() - (TotalTileRows - 1) * (mHeight);
|
|
|
|
if (CurrentTileColumn < TotalTileColumns - 1)
|
|
CurrentTileWidth = mWidth;
|
|
else
|
|
CurrentTileWidth = mRenderImage.width() - (TotalTileColumns - 1) * (mWidth);
|
|
|
|
DrawBackground(CurrentTileRow, TotalTileRows, CurrentTileHeight);
|
|
|
|
mContext->SetViewport(0, 0, CurrentTileWidth, CurrentTileHeight);
|
|
mContext->SetProjectionMatrix(GetTileProjectionMatrix(CurrentTileRow, CurrentTileColumn, CurrentTileWidth, CurrentTileHeight));
|
|
}
|
|
else
|
|
{
|
|
CurrentTileWidth = mWidth;
|
|
CurrentTileHeight = mHeight;
|
|
|
|
DrawBackground(CurrentTileRow, TotalTileRows, CurrentTileHeight);
|
|
|
|
mContext->SetProjectionMatrix(GetProjectionMatrix());
|
|
}
|
|
|
|
mContext->SetLineWidth(Preferences.mLineWidth);
|
|
|
|
mScene->Draw(mContext);
|
|
|
|
if (!mRenderImage.isNull())
|
|
{
|
|
UnbindRenderFramebuffer();
|
|
QImage TileImage = GetRenderFramebufferImage();
|
|
BindRenderFramebuffer();
|
|
|
|
if (TileImage.isNull())
|
|
{
|
|
mRenderImage = QImage();
|
|
return;
|
|
}
|
|
|
|
const quint8* Buffer = TileImage.constBits();
|
|
uchar* ImageBuffer = mRenderImage.bits();
|
|
|
|
if (!ImageBuffer)
|
|
{
|
|
mRenderImage = QImage();
|
|
return;
|
|
}
|
|
|
|
quint32 TileY = 0, SrcY = 0;
|
|
if (CurrentTileRow != TotalTileRows - 1)
|
|
TileY = (TotalTileRows - CurrentTileRow - 1) * mHeight - ((mHeight - mRenderImage.height() % mHeight) % mHeight);
|
|
else if (TotalTileRows > 1)
|
|
SrcY = (mHeight - mRenderImage.height() % mHeight) % mHeight;
|
|
|
|
quint32 TileStart = ((CurrentTileColumn * mWidth) + (TileY * mRenderImage.width())) * 4;
|
|
|
|
for (int y = 0; y < CurrentTileHeight; y++)
|
|
{
|
|
const quint8* src = Buffer + (SrcY + y) * mWidth * 4;
|
|
quint8* dst = ImageBuffer + TileStart + y * mRenderImage.width() * 4;
|
|
|
|
memcpy(dst, src, CurrentTileWidth * 4);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DrawInterface)
|
|
mScene->DrawInterfaceObjects(mContext);
|
|
|
|
if (DrawOverlays)
|
|
DrawAxes();
|
|
|
|
if (DrawInterface && mTrackTool != lcTrackTool::Insert)
|
|
{
|
|
lcTool Tool = gMainWindow->GetTool();
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
if ((Tool == lcTool::Select || Tool == lcTool::Move) && mTrackButton == lcTrackButton::None && ActiveModel->AnyObjectsSelected())
|
|
mViewManipulator->DrawSelectMove(mTrackButton, mTrackTool, mTrackToolSection);
|
|
else if (GetCurrentTool() == lcTool::Move && mTrackButton != lcTrackButton::None)
|
|
mViewManipulator->DrawSelectMove(mTrackButton, mTrackTool, mTrackToolSection);
|
|
else if ((Tool == lcTool::Rotate || (Tool == lcTool::Select && mTrackButton != lcTrackButton::None && mTrackTool >= lcTrackTool::RotateX && mTrackTool <= lcTrackTool::RotateCamera)) && ActiveModel->CanRotateSelection())
|
|
mViewManipulator->DrawRotate(mTrackButton, mTrackTool);
|
|
else if ((mTrackTool == lcTrackTool::Select || mTrackTool == lcTrackTool::ZoomRegion) && mTrackButton != lcTrackButton::None)
|
|
DrawSelectZoomRegionOverlay();
|
|
else if (Tool == lcTool::RotateView && mTrackButton == lcTrackButton::None)
|
|
DrawRotateViewOverlay();
|
|
}
|
|
|
|
if (DrawOverlays)
|
|
{
|
|
mViewSphere->Draw();
|
|
|
|
DrawViewport();
|
|
}
|
|
|
|
#ifdef LC_PROFILE_DRAW
|
|
qint64 TimerElapsed = Timer.nsecsElapsed();
|
|
Query.end();
|
|
|
|
GLuint64 QueryElapsed = Query.waitForResult();
|
|
QueryResults[FrameNumber % QueryResults.size()] = QueryElapsed;
|
|
TimerResults[FrameNumber % TimerResults.size()] = TimerElapsed;
|
|
FrameNumber++;
|
|
|
|
GLuint64 QueryAverage = 0;
|
|
for (GLuint64 Result : QueryResults)
|
|
QueryAverage += Result;
|
|
QueryAverage /= QueryResults.size();
|
|
|
|
GLuint64 TimerAverage = 0;
|
|
for (GLuint64 Result : TimerResults)
|
|
TimerAverage += Result;
|
|
TimerAverage /= TimerResults.size();
|
|
|
|
mContext->SetWorldMatrix(lcMatrix44Identity());
|
|
mContext->SetViewMatrix(lcMatrix44Translation(lcVector3(0.375, 0.375, 0.0)));
|
|
mContext->SetProjectionMatrix(lcMatrix44Ortho(0.0f, mWidth, 0.0f, mHeight, -1.0f, 1.0f));
|
|
|
|
QString Line = QString("GPU: %1 CPU: %2").arg(QString::number(QueryAverage / 1000000.0, 'f', 2), QString::number(TimerAverage / 1000000.0, 'f', 2));
|
|
|
|
mContext->SetMaterial(gTexFont.IsSDF() ? lcMaterialType::TextSDF : lcMaterialType::UnlitTextureModulate);
|
|
mContext->SetColor(lcVector4FromColor(lcGetPreferences().mTextColor));
|
|
mContext->SetTextHaloColor(lcVector4FromColor(lcGetPreferences().mTextHaloColor));
|
|
mContext->BindTexture2D(gTexFont.GetTexture());
|
|
|
|
mContext->EnableDepthTest(false);
|
|
mContext->EnableColorBlend(true);
|
|
|
|
gTexFont.PrintText(mContext, 3.0f, (float)mHeight - 1.0f - 6.0f, 0.0f, Line.toLatin1().constData());
|
|
|
|
mContext->EnableColorBlend(false);
|
|
mContext->EnableDepthTest(true);
|
|
|
|
Redraw();
|
|
#endif
|
|
|
|
mContext->ClearResources();
|
|
}
|
|
|
|
bool lcView::HasMissingAssets() const
|
|
{
|
|
return mScene->HasMissingAssets();
|
|
}
|
|
|
|
void lcView::DrawBackground(int CurrentTileRow, int TotalTileRows, int CurrentTileHeight) const
|
|
{
|
|
if (mOverrideBackgroundColor)
|
|
{
|
|
lcVector4 BackgroundColor(lcVector4FromColor(mBackgroundColor));
|
|
mContext->ClearColorAndDepth(BackgroundColor);
|
|
return;
|
|
}
|
|
|
|
const lcPreferences& Preferences = lcGetPreferences();
|
|
float Alpha = mContext->IsOffscreen() ? 0.0f : 1.0f;
|
|
|
|
if (!Preferences.mBackgroundGradient)
|
|
{
|
|
lcVector4 BackgroundColor(lcVector3FromColor(Preferences.mBackgroundSolidColor), Alpha);
|
|
mContext->ClearColorAndDepth(BackgroundColor);
|
|
return;
|
|
}
|
|
|
|
mContext->ClearDepth();
|
|
|
|
mContext->SetDepthWrite(false);
|
|
mContext->EnableDepthTest(false);
|
|
|
|
float ViewWidth = (float)mWidth;
|
|
float ViewHeight = (float)mHeight;
|
|
|
|
mContext->SetWorldMatrix(lcMatrix44Identity());
|
|
mContext->SetViewMatrix(lcMatrix44Identity());
|
|
mContext->SetProjectionMatrix(lcMatrix44Ortho(0.0f, ViewWidth, 0.0f, ViewHeight, -1.0f, 1.0f));
|
|
|
|
const int TotalHeight = TotalTileRows == 1 ? mHeight : mRenderImage.height();
|
|
const quint32 TopY = CurrentTileRow * mHeight + CurrentTileHeight;
|
|
|
|
const double t1 = 1.0 - (double)TopY / (double)TotalHeight;
|
|
const double t2 = 1.0 - (double)(TopY - CurrentTileHeight) / (double)TotalHeight;
|
|
|
|
const quint32 ColorTop = Preferences.mBackgroundGradientColorTop;
|
|
const quint32 ColorBottom = Preferences.mBackgroundGradientColorBottom;
|
|
|
|
double TopRed = LC_RGBA_RED(ColorTop);
|
|
double TopGreen = LC_RGBA_GREEN(ColorTop);
|
|
double TopBlue = LC_RGBA_BLUE(ColorTop);
|
|
double BottomRed = LC_RGBA_RED(ColorBottom);
|
|
double BottomGreen = LC_RGBA_GREEN(ColorBottom);
|
|
double BottomBlue = LC_RGBA_BLUE(ColorBottom);
|
|
const double DeltaRed = BottomRed - TopRed;
|
|
const double DeltaGreen = BottomGreen - TopGreen;
|
|
const double DeltaBlue = BottomBlue - TopBlue;
|
|
|
|
BottomRed = TopRed + DeltaRed * t2;
|
|
BottomGreen = TopGreen + DeltaGreen * t2;
|
|
BottomBlue = TopBlue + DeltaBlue * t2;
|
|
|
|
TopRed = TopRed + DeltaRed * t1;
|
|
TopGreen = TopGreen + DeltaGreen * t1;
|
|
TopBlue = TopBlue + DeltaBlue * t1;
|
|
|
|
const quint32 Color1 = LC_RGBA(TopRed, TopGreen, TopBlue, Alpha);
|
|
const quint32 Color2 = LC_RGBA(BottomRed, BottomGreen, BottomBlue, Alpha);
|
|
|
|
struct lcBackgroundVertex
|
|
{
|
|
float x, y;
|
|
quint32 Color;
|
|
};
|
|
|
|
const lcBackgroundVertex Verts[4] =
|
|
{
|
|
{ ViewWidth, ViewHeight, Color1 }, { 0.0f, ViewHeight, Color1 }, { 0.0f, 0.0f, Color2 }, { ViewWidth, 0.0f, Color2 }
|
|
};
|
|
|
|
mContext->SetMaterial(lcMaterialType::UnlitVertexColor);
|
|
mContext->SetVertexBufferPointer(Verts);
|
|
mContext->SetVertexFormat(0, 2, 0, 0, 4, false);
|
|
|
|
mContext->DrawPrimitives(GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
mContext->EnableDepthTest(true);
|
|
mContext->SetDepthWrite(true);
|
|
}
|
|
|
|
void lcView::DrawViewport() const
|
|
{
|
|
float Scale = GetUIScale();
|
|
float Width = mWidth / Scale;
|
|
float Height = mHeight / Scale;
|
|
|
|
mContext->SetWorldMatrix(lcMatrix44Identity());
|
|
mContext->SetViewMatrix(lcMatrix44Translation(lcVector3(0.375, 0.375, 0.0)));
|
|
mContext->SetProjectionMatrix(lcMatrix44Ortho(0.0f, Width, 0.0f, Height, -1.0f, 1.0f));
|
|
mContext->SetLineWidth(1.0f);
|
|
|
|
mContext->SetDepthWrite(false);
|
|
mContext->EnableDepthTest(false);
|
|
|
|
mContext->SetMaterial(lcMaterialType::UnlitColor);
|
|
|
|
if (mLastFocusedView == this)
|
|
mContext->SetColor(lcVector4FromColor(lcGetPreferences().mActiveViewColor));
|
|
else
|
|
mContext->SetColor(lcVector4FromColor(lcGetPreferences().mInactiveViewColor));
|
|
|
|
float Verts[8] = { 0.0f, 0.0f, Width - 1.0f, 0.0f, Width - 1.0f, Height - 1.0f, 0.0f, Height - 1.0f };
|
|
|
|
mContext->SetVertexBufferPointer(Verts);
|
|
mContext->SetVertexFormatPosition(2);
|
|
mContext->DrawPrimitives(GL_LINE_LOOP, 0, 4);
|
|
|
|
QString CameraName = mCamera->GetName();
|
|
|
|
if (!CameraName.isEmpty())
|
|
{
|
|
mContext->SetMaterial(gTexFont.IsSDF() ? lcMaterialType::TextSDF : lcMaterialType::UnlitTextureModulate);
|
|
mContext->SetColor(lcVector4FromColor(lcGetPreferences().mTextColor));
|
|
mContext->SetTextHaloColor(lcVector4FromColor(lcGetPreferences().mTextHaloColor));
|
|
mContext->BindTexture2D(gTexFont.GetTexture());
|
|
|
|
mContext->EnableColorBlend(true);
|
|
|
|
gTexFont.PrintText(mContext, 3.0f, (float)Height - 1.0f - 6.0f, 0.0f, CameraName.toLatin1().constData());
|
|
|
|
mContext->EnableColorBlend(false);
|
|
}
|
|
|
|
mContext->SetDepthWrite(true);
|
|
mContext->EnableDepthTest(true);
|
|
}
|
|
|
|
void lcView::DrawAxes() const
|
|
{
|
|
const lcPreferences& Preferences = lcGetPreferences();
|
|
|
|
switch (mViewType)
|
|
{
|
|
case lcViewType::View:
|
|
if (!Preferences.mDrawAxes)
|
|
return;
|
|
break;
|
|
|
|
case lcViewType::Preview:
|
|
if (!Preferences.mDrawPreviewAxis)
|
|
return;
|
|
break;
|
|
|
|
case lcViewType::Minifig:
|
|
case lcViewType::PartsList:
|
|
case lcViewType::Count:
|
|
return;
|
|
}
|
|
|
|
// mContext->ClearDepth();
|
|
|
|
struct lcAxisVertex
|
|
{
|
|
float x, y, z;
|
|
quint32 Color;
|
|
};
|
|
|
|
const quint32 Red = LC_RGBA(204, 0, 0, 255);
|
|
const quint32 Green = LC_RGBA(0, 204, 0, 255);
|
|
const quint32 Blue = LC_RGBA(0, 0, 204, 255);
|
|
|
|
const lcAxisVertex Verts[30] =
|
|
{
|
|
{ 0.00f, 0.00f, 0.00f, Red }, { 20.00f, 0.00f, 0.00f, Red }, { 12.00f, 3.00f, 0.00f, Red }, { 12.00f, 2.12f, 2.12f, Red }, { 12.00f, 0.00f, 3.00f, Red },
|
|
{ 12.00f, -2.12f, 2.12f, Red }, { 12.00f, -3.00f, 0.00f, Red }, { 12.00f, -2.12f, -2.12f, Red }, { 12.00f, 0.00f, -3.00f, Red }, { 12.00f, 2.12f, -2.12f, Red },
|
|
{ 0.00f, 0.00f, 0.00f, Green }, { 0.00f, 20.00f, 0.00f, Green }, { 3.00f, 12.00f, 0.00f, Green }, { 2.12f, 12.00f, 2.12f, Green }, { 0.00f, 12.00f, 3.00f, Green },
|
|
{ -2.12f, 12.00f, 2.12f, Green }, { -3.00f, 12.00f, 0.00f, Green }, { -2.12f, 12.00f, -2.12f, Green }, { 0.00f, 12.00f, -3.00f, Green }, { 2.12f, 12.00f, -2.12f, Green },
|
|
{ 0.00f, 0.00f, 0.00f, Blue }, { 0.00f, 0.00f, 20.00f, Blue }, { 0.00f, 3.00f, 12.00f, Blue }, { 2.12f, 2.12f, 12.00f, Blue }, { 3.00f, 0.00f, 12.00f, Blue },
|
|
{ 2.12f, -2.12f, 12.00f, Blue }, { 0.00f, -3.00f, 12.00f, Blue }, { -2.12f, -2.12f, 12.00f, Blue }, { -3.00f, 0.00f, 12.00f, Blue }, { -2.12f, 2.12f, 12.00f, Blue }
|
|
};
|
|
|
|
const GLushort Indices[78] =
|
|
{
|
|
0, 1, 10, 11, 20, 21,
|
|
1, 2, 3, 1, 3, 4, 1, 4, 5, 1, 5, 6, 1, 6, 7, 1, 7, 8, 1, 8, 9, 1, 9, 2,
|
|
11, 12, 13, 11, 13, 14, 11, 14, 15, 11, 15, 16, 11, 16, 17, 11, 17, 18, 11, 18, 19, 11, 19, 12,
|
|
21, 22, 23, 21, 23, 24, 21, 24, 25, 21, 25, 26, 21, 26, 27, 21, 27, 28, 21, 28, 29, 21, 29, 22
|
|
};
|
|
|
|
lcMatrix44 TranslationMatrix;
|
|
float Scale = GetUIScale();
|
|
|
|
switch (Preferences.mAxisIconLocation)
|
|
{
|
|
default:
|
|
case lcAxisIconLocation::BottomLeft:
|
|
TranslationMatrix = lcMatrix44Translation(lcVector3(32, 32, 0.0f) / Scale);
|
|
break;
|
|
|
|
case lcAxisIconLocation::BottomRight:
|
|
TranslationMatrix = lcMatrix44Translation(lcVector3(mWidth - 36, 32, 0.0f) / Scale);
|
|
break;
|
|
|
|
case lcAxisIconLocation::TopLeft:
|
|
TranslationMatrix = lcMatrix44Translation(lcVector3(32, mHeight - 36, 0.0f) / Scale);
|
|
break;
|
|
|
|
case lcAxisIconLocation::TopRight:
|
|
TranslationMatrix = lcMatrix44Translation(lcVector3(mWidth - 36, mHeight - 36, 0.0f) / Scale);
|
|
break;
|
|
}
|
|
|
|
lcMatrix44 WorldViewMatrix = mCamera->mWorldView;
|
|
WorldViewMatrix.SetTranslation(lcVector3(0, 0, 0));
|
|
|
|
mContext->SetLineWidth(1.0f);
|
|
mContext->SetMaterial(lcMaterialType::UnlitVertexColor);
|
|
mContext->SetWorldMatrix(lcMatrix44Identity());
|
|
mContext->SetViewMatrix(lcMul(WorldViewMatrix, TranslationMatrix));
|
|
mContext->SetProjectionMatrix(lcMatrix44Ortho(0, mWidth / Scale, 0, mHeight / Scale, -50, 50));
|
|
|
|
mContext->SetVertexBufferPointer(Verts);
|
|
mContext->SetVertexFormat(0, 3, 0, 0, 4, false);
|
|
mContext->SetIndexBufferPointer(Indices);
|
|
|
|
mContext->DrawIndexedPrimitives(GL_LINES, 6, GL_UNSIGNED_SHORT, 0);
|
|
mContext->DrawIndexedPrimitives(GL_TRIANGLES, 72, GL_UNSIGNED_SHORT, 6 * 2);
|
|
|
|
mContext->SetMaterial(gTexFont.IsSDF() ? lcMaterialType::TextSDF : lcMaterialType::UnlitTextureModulate);
|
|
mContext->SetViewMatrix(TranslationMatrix);
|
|
mContext->BindTexture2D(gTexFont.GetTexture());
|
|
mContext->EnableColorBlend(true);
|
|
|
|
float TextBuffer[6 * 5 * 3];
|
|
lcVector3 PosX = lcMul30(lcVector3(25.0f, 0.0f, 0.0f), WorldViewMatrix);
|
|
gTexFont.GetGlyphTriangles(PosX.x, PosX.y, PosX.z, 'X', TextBuffer);
|
|
lcVector3 PosY = lcMul30(lcVector3(0.0f, 25.0f, 0.0f), WorldViewMatrix);
|
|
gTexFont.GetGlyphTriangles(PosY.x, PosY.y, PosY.z, 'Y', TextBuffer + 5 * 6);
|
|
lcVector3 PosZ = lcMul30(lcVector3(0.0f, 0.0f, 25.0f), WorldViewMatrix);
|
|
gTexFont.GetGlyphTriangles(PosZ.x, PosZ.y, PosZ.z, 'Z', TextBuffer + 5 * 6 * 2);
|
|
|
|
mContext->SetVertexBufferPointer(TextBuffer);
|
|
mContext->SetVertexFormat(0, 3, 0, 2, 0, false);
|
|
|
|
mContext->SetColor(lcVector4FromColor(lcGetPreferences().mTextColor));
|
|
mContext->SetTextHaloColor(lcVector4FromColor(lcGetPreferences().mTextHaloColor));
|
|
mContext->DrawPrimitives(GL_TRIANGLES, 0, 6 * 3);
|
|
|
|
mContext->EnableColorBlend(false);
|
|
}
|
|
|
|
void lcView::DrawSelectZoomRegionOverlay()
|
|
{
|
|
mContext->SetMaterial(lcMaterialType::UnlitColor);
|
|
mContext->SetWorldMatrix(lcMatrix44Identity());
|
|
mContext->SetViewMatrix(lcMatrix44Translation(lcVector3(0.375, 0.375, 0.0)));
|
|
mContext->SetProjectionMatrix(lcMatrix44Ortho(0.0f, mWidth, 0.0f, mHeight, -1.0f, 1.0f));
|
|
mContext->SetLineWidth(1.0f);
|
|
|
|
mContext->EnableDepthTest(false);
|
|
|
|
float pt1x = (float)mMouseDownX;
|
|
float pt1y = (float)mMouseDownY;
|
|
float pt2x = (float)mMouseX;
|
|
float pt2y = (float)mMouseY;
|
|
|
|
float Left, Right, Bottom, Top;
|
|
|
|
if (pt1x < pt2x)
|
|
{
|
|
Left = pt1x;
|
|
Right = pt2x;
|
|
}
|
|
else
|
|
{
|
|
Left = pt2x;
|
|
Right = pt1x;
|
|
}
|
|
|
|
if (pt1y < pt2y)
|
|
{
|
|
Bottom = pt1y;
|
|
Top = pt2y;
|
|
}
|
|
else
|
|
{
|
|
Bottom = pt2y;
|
|
Top = pt1y;
|
|
}
|
|
|
|
Left = lcMax(Left, 0.0f);
|
|
Right = lcMin(Right, mWidth - 1.0f);
|
|
Bottom = lcMax(Bottom, 0.0f);
|
|
Top = lcMin(Top, mHeight - 1.0f);
|
|
|
|
float BorderX = lcMin(2.0f, Right - Left);
|
|
float BorderY = lcMin(2.0f, Top - Bottom);
|
|
|
|
float Verts[14][2] =
|
|
{
|
|
{ Left, Bottom },
|
|
{ Left + BorderX, Bottom + BorderY },
|
|
{ Right, Bottom },
|
|
{ Right - BorderX, Bottom + BorderY },
|
|
{ Right, Top },
|
|
{ Right - BorderX, Top - BorderY },
|
|
{ Left, Top },
|
|
{ Left + BorderX, Top - BorderY },
|
|
{ Left, Bottom },
|
|
{ Left + BorderX, Bottom + BorderY },
|
|
{ Left + BorderX, Bottom + BorderY },
|
|
{ Right - BorderX, Bottom + BorderY },
|
|
{ Left + BorderX, Top - BorderY },
|
|
{ Right - BorderX, Top - BorderY },
|
|
};
|
|
|
|
mContext->SetVertexBufferPointer(Verts);
|
|
mContext->SetVertexFormatPosition(2);
|
|
|
|
const lcPreferences& Preferences = lcGetPreferences();
|
|
|
|
mContext->SetColor(lcVector4FromColor(Preferences.mMarqueeBorderColor));
|
|
mContext->DrawPrimitives(GL_TRIANGLE_STRIP, 0, 10);
|
|
|
|
if (LC_RGBA_ALPHA(Preferences.mMarqueeFillColor))
|
|
{
|
|
mContext->EnableColorBlend(true);
|
|
mContext->SetColor(lcVector4FromColor(Preferences.mMarqueeFillColor));
|
|
mContext->DrawPrimitives(GL_TRIANGLE_STRIP, 10, 4);
|
|
mContext->EnableColorBlend(false);
|
|
}
|
|
|
|
mContext->EnableDepthTest(true);
|
|
}
|
|
|
|
void lcView::DrawRotateViewOverlay()
|
|
{
|
|
int x, y, w, h;
|
|
|
|
x = 0;
|
|
y = 0;
|
|
w = mWidth;
|
|
h = mHeight;
|
|
|
|
mContext->SetMaterial(lcMaterialType::UnlitColor);
|
|
mContext->SetWorldMatrix(lcMatrix44Identity());
|
|
mContext->SetViewMatrix(lcMatrix44Translation(lcVector3(0.375, 0.375, 0.0)));
|
|
mContext->SetProjectionMatrix(lcMatrix44Ortho(0, w, 0, h, -1, 1));
|
|
mContext->SetLineWidth(1.0f);
|
|
|
|
mContext->EnableDepthTest(false);
|
|
mContext->SetColor(lcVector4FromColor(lcGetPreferences().mOverlayColor));
|
|
|
|
float Verts[32 * 16 * 2];
|
|
float* CurVert = Verts;
|
|
|
|
float r = lcMin(w, h) * 0.35f;
|
|
float cx = x + w / 2.0f;
|
|
float cy = y + h / 2.0f;
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
*CurVert++ = cosf((float)i / 32.0f * (2.0f * LC_PI)) * r + cx;
|
|
*CurVert++ = sinf((float)i / 32.0f * (2.0f * LC_PI)) * r + cy;
|
|
}
|
|
|
|
const float OverlayCameraSquareSize = lcMax(8.0f, (w + h) / 200.0f);
|
|
|
|
*CurVert++ = cx + OverlayCameraSquareSize; *CurVert++ = cy + r + OverlayCameraSquareSize;
|
|
*CurVert++ = cx - OverlayCameraSquareSize; *CurVert++ = cy + r + OverlayCameraSquareSize;
|
|
*CurVert++ = cx - OverlayCameraSquareSize; *CurVert++ = cy + r - OverlayCameraSquareSize;
|
|
*CurVert++ = cx + OverlayCameraSquareSize; *CurVert++ = cy + r - OverlayCameraSquareSize;
|
|
*CurVert++ = cx + OverlayCameraSquareSize; *CurVert++ = cy - r + OverlayCameraSquareSize;
|
|
*CurVert++ = cx - OverlayCameraSquareSize; *CurVert++ = cy - r + OverlayCameraSquareSize;
|
|
*CurVert++ = cx - OverlayCameraSquareSize; *CurVert++ = cy - r - OverlayCameraSquareSize;
|
|
*CurVert++ = cx + OverlayCameraSquareSize; *CurVert++ = cy - r - OverlayCameraSquareSize;
|
|
*CurVert++ = cx + r + OverlayCameraSquareSize; *CurVert++ = cy + OverlayCameraSquareSize;
|
|
*CurVert++ = cx + r - OverlayCameraSquareSize; *CurVert++ = cy + OverlayCameraSquareSize;
|
|
*CurVert++ = cx + r - OverlayCameraSquareSize; *CurVert++ = cy - OverlayCameraSquareSize;
|
|
*CurVert++ = cx + r + OverlayCameraSquareSize; *CurVert++ = cy - OverlayCameraSquareSize;
|
|
*CurVert++ = cx - r + OverlayCameraSquareSize; *CurVert++ = cy + OverlayCameraSquareSize;
|
|
*CurVert++ = cx - r - OverlayCameraSquareSize; *CurVert++ = cy + OverlayCameraSquareSize;
|
|
*CurVert++ = cx - r - OverlayCameraSquareSize; *CurVert++ = cy - OverlayCameraSquareSize;
|
|
*CurVert++ = cx - r + OverlayCameraSquareSize; *CurVert++ = cy - OverlayCameraSquareSize;
|
|
|
|
mContext->SetVertexBufferPointer(Verts);
|
|
mContext->SetVertexFormatPosition(2);
|
|
|
|
GLushort Indices[64 + 32] =
|
|
{
|
|
0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
|
|
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 0,
|
|
32, 33, 33, 34, 34, 35, 35, 32, 36, 37, 37, 38, 38, 39, 39, 36,
|
|
40, 41, 41, 42, 42, 43, 43, 40, 44, 45, 45, 46, 46, 47, 47, 44
|
|
};
|
|
|
|
mContext->SetIndexBufferPointer(Indices);
|
|
mContext->DrawIndexedPrimitives(GL_LINES, 96, GL_UNSIGNED_SHORT, 0);
|
|
|
|
mContext->EnableDepthTest(true);
|
|
}
|
|
|
|
void lcView::DrawGrid()
|
|
{
|
|
const lcPreferences& Preferences = lcGetPreferences();
|
|
|
|
if (!Preferences.mDrawGridStuds && !Preferences.mDrawGridLines && !Preferences.mDrawGridOrigin)
|
|
return;
|
|
|
|
if (!Preferences.mGridEnabled)
|
|
return;
|
|
|
|
const int Spacing = lcMax(Preferences.mGridLineSpacing, 1);
|
|
int MinX, MaxX, MinY, MaxY;
|
|
lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
|
|
|
bool GridSizeValid = mModel->GetVisiblePiecesBoundingBox(Min, Max);
|
|
bool DrawInsertPreview = std::any_of(mViews.begin(), mViews.end(), [](lcView* View){ return View->mTrackTool == lcTrackTool::Insert; });
|
|
|
|
if (DrawInsertPreview)
|
|
{
|
|
const lcModel* PreviewModel = GetActiveModel();
|
|
|
|
for (const lcInsertPieceInfo& PreviewInsertPieceInfo : PreviewModel->GetPreviewInsertPieceInfo())
|
|
{
|
|
lcVector3 Points[8];
|
|
lcGetBoxCorners(PreviewInsertPieceInfo.Info->GetBoundingBox(), Points);
|
|
lcMatrix44 PreviewTransform = PreviewInsertPieceInfo.Transform;
|
|
|
|
if (PreviewModel != mModel)
|
|
PreviewTransform = lcMul(PreviewTransform, mActiveSubmodelTransform);
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
lcVector3 Point = lcMul31(Points[i], PreviewTransform);
|
|
|
|
Min = lcMin(Point, Min);
|
|
Max = lcMax(Point, Max);
|
|
}
|
|
|
|
GridSizeValid = true;
|
|
}
|
|
}
|
|
|
|
if (GridSizeValid)
|
|
{
|
|
MinX = (int)(floorf(Min[0] / (20.0f * Spacing))) - 1;
|
|
MinY = (int)(floorf(Min[1] / (20.0f * Spacing))) - 1;
|
|
MaxX = (int)(ceilf(Max[0] / (20.0f * Spacing))) + 1;
|
|
MaxY = (int)(ceilf(Max[1] / (20.0f * Spacing))) + 1;
|
|
|
|
MinX = lcMin(MinX, -2);
|
|
MinY = lcMin(MinY, -2);
|
|
MaxX = lcMax(MaxX, 2);
|
|
MaxY = lcMax(MaxY, 2);
|
|
}
|
|
else
|
|
{
|
|
MinX = -2;
|
|
MinY = -2;
|
|
MaxX = 2;
|
|
MaxY = 2;
|
|
}
|
|
|
|
if (!mGridBuffer.IsValid() || MinX != mGridSettings[0] || MinY != mGridSettings[1] || MaxX != mGridSettings[2] || MaxY != mGridSettings[3] ||
|
|
Spacing != mGridSettings[4] || (Preferences.mDrawGridStuds ? 1 : 0) != mGridSettings[5] || (Preferences.mDrawGridLines ? 1 : 0) != mGridSettings[6])
|
|
{
|
|
int VertexBufferSize = 0;
|
|
|
|
if (Preferences.mDrawGridStuds)
|
|
VertexBufferSize += 4 * 5 * sizeof(float);
|
|
|
|
if (Preferences.mDrawGridLines)
|
|
VertexBufferSize += 2 * (MaxX - MinX + MaxY - MinY + 2) * 3 * sizeof(float);
|
|
|
|
float* Verts = nullptr;
|
|
|
|
if (VertexBufferSize)
|
|
{
|
|
Verts = static_cast<float*>(malloc(VertexBufferSize));
|
|
|
|
if (!Verts)
|
|
return;
|
|
|
|
float* CurVert = Verts;
|
|
|
|
if (Preferences.mDrawGridStuds)
|
|
{
|
|
float Left = MinX * 20.0f * Spacing;
|
|
float Right = MaxX * 20.0f * Spacing;
|
|
float Top = MinY * 20.0f * Spacing;
|
|
float Bottom = MaxY * 20.0f * Spacing;
|
|
float Z = 0;
|
|
float U = (MaxX - MinX) * Spacing;
|
|
float V = (MaxY - MinY) * Spacing;
|
|
|
|
*CurVert++ = Left;
|
|
*CurVert++ = Top;
|
|
*CurVert++ = Z;
|
|
*CurVert++ = 0.0f;
|
|
*CurVert++ = V;
|
|
|
|
*CurVert++ = Right;
|
|
*CurVert++ = Top;
|
|
*CurVert++ = Z;
|
|
*CurVert++ = U;
|
|
*CurVert++ = V;
|
|
|
|
*CurVert++ = Left;
|
|
*CurVert++ = Bottom;
|
|
*CurVert++ = Z;
|
|
*CurVert++ = 0.0f;
|
|
*CurVert++ = 0.0f;
|
|
|
|
*CurVert++ = Right;
|
|
*CurVert++ = Bottom;
|
|
*CurVert++ = Z;
|
|
*CurVert++ = U;
|
|
*CurVert++ = 0.0f;
|
|
}
|
|
|
|
if (Preferences.mDrawGridLines)
|
|
{
|
|
float LineSpacing = Spacing * 20.0f;
|
|
|
|
for (int Step = MinX; Step < MaxX + 1; Step++)
|
|
{
|
|
*CurVert++ = Step * LineSpacing;
|
|
*CurVert++ = MinY * LineSpacing;
|
|
*CurVert++ = 0.0f;
|
|
*CurVert++ = Step * LineSpacing;
|
|
*CurVert++ = MaxY * LineSpacing;
|
|
*CurVert++ = 0.0f;
|
|
}
|
|
|
|
for (int Step = MinY; Step < MaxY + 1; Step++)
|
|
{
|
|
*CurVert++ = MinX * LineSpacing;
|
|
*CurVert++ = Step * LineSpacing;
|
|
*CurVert++ = 0.0f;
|
|
*CurVert++ = MaxX * LineSpacing;
|
|
*CurVert++ = Step * LineSpacing;
|
|
*CurVert++ = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
mGridSettings[0] = MinX;
|
|
mGridSettings[1] = MinY;
|
|
mGridSettings[2] = MaxX;
|
|
mGridSettings[3] = MaxY;
|
|
mGridSettings[4] = Spacing;
|
|
mGridSettings[5] = (Preferences.mDrawGridStuds ? 1 : 0);
|
|
mGridSettings[6] = (Preferences.mDrawGridLines ? 1 : 0);
|
|
|
|
mContext->DestroyVertexBuffer(mGridBuffer);
|
|
mGridBuffer = mContext->CreateVertexBuffer(VertexBufferSize, Verts);
|
|
free(Verts);
|
|
}
|
|
|
|
int BufferOffset = 0;
|
|
mContext->SetVertexBuffer(mGridBuffer);
|
|
mContext->SetWorldMatrix(lcMatrix44Identity());
|
|
|
|
if (Preferences.mDrawGridStuds)
|
|
{
|
|
mContext->BindTexture2D(gGridTexture);
|
|
mContext->SetDepthWrite(false);
|
|
mContext->EnableColorBlend(true);
|
|
|
|
mContext->SetMaterial(lcMaterialType::UnlitTextureModulate);
|
|
mContext->SetColor(lcVector4FromColor(Preferences.mGridStudColor));
|
|
|
|
mContext->SetVertexFormat(0, 3, 0, 2, 0, false);
|
|
mContext->DrawPrimitives(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
mContext->EnableColorBlend(false);
|
|
mContext->SetDepthWrite(true);
|
|
|
|
BufferOffset = 4 * 5 * sizeof(float);
|
|
}
|
|
|
|
if (Preferences.mDrawGridLines)
|
|
{
|
|
mContext->SetLineWidth(1.0f);
|
|
mContext->SetMaterial(lcMaterialType::UnlitColor);
|
|
mContext->SetColor(lcVector4FromColor(Preferences.mGridLineColor));
|
|
|
|
int NumVerts = 2 * (MaxX - MinX + MaxY - MinY + 2);
|
|
|
|
mContext->SetVertexFormat(BufferOffset, 3, 0, 0, 0, false);
|
|
mContext->DrawPrimitives(GL_LINES, 0, NumVerts);
|
|
}
|
|
|
|
if (Preferences.mDrawGridOrigin)
|
|
{
|
|
struct lcGridVertex
|
|
{
|
|
float x, y;
|
|
quint32 Color;
|
|
};
|
|
|
|
const quint32 Red = LC_RGBA(204, 0, 0, 255);
|
|
const quint32 Green = LC_RGBA(0, 204, 0, 255);
|
|
const float Scale = 20.0f * Spacing;
|
|
|
|
const lcGridVertex Verts[4] =
|
|
{
|
|
{ 0.0f, MinY * Scale, Green }, { 0.0f, MaxY * Scale, Green }, { MinX * Scale, 0.0f, Red }, { MaxX * Scale, 0.0f, Red }
|
|
};
|
|
|
|
mContext->SetMaterial(lcMaterialType::UnlitVertexColor);
|
|
mContext->SetVertexBufferPointer(Verts);
|
|
mContext->SetVertexFormat(0, 2, 0, 0, 4, false);
|
|
|
|
mContext->DrawPrimitives(GL_LINES, 0, 4);
|
|
}
|
|
}
|
|
|
|
lcTrackTool lcView::GetOverrideTrackTool(Qt::MouseButton Button) const
|
|
{
|
|
if (mTrackToolFromOverlay)
|
|
return lcTrackTool::None;
|
|
|
|
lcTool OverrideTool = gMouseShortcuts.GetTool(Button, mMouseModifiers);
|
|
|
|
if (OverrideTool == lcTool::Count)
|
|
return lcTrackTool::None;
|
|
|
|
constexpr lcTrackTool TrackToolFromTool[] =
|
|
{
|
|
lcTrackTool::Insert, // lcTool::Insert
|
|
lcTrackTool::PointLight, // lcTool::PointLight
|
|
lcTrackTool::SpotLight, // lcTool::SpotLight
|
|
lcTrackTool::DirectionalLight, // lcTool::DirectionalLight
|
|
lcTrackTool::AreaLight, // lcTool::AreaLight
|
|
lcTrackTool::Camera, // lcTool::Camera
|
|
lcTrackTool::Select, // lcTool::Select
|
|
lcTrackTool::MoveXYZ, // lcTool::Move
|
|
lcTrackTool::RotateTrackBall, // lcTool::Rotate
|
|
lcTrackTool::Eraser, // lcTool::Eraser
|
|
lcTrackTool::Paint, // lcTool::Paint
|
|
lcTrackTool::ColorPicker, // lcTool::ColorPicker
|
|
lcTrackTool::Zoom, // lcTool::Zoom
|
|
lcTrackTool::Pan, // lcTool::Pan
|
|
lcTrackTool::OrbitXY, // lcTool::RotateView
|
|
lcTrackTool::Roll, // lcTool::Roll
|
|
lcTrackTool::ZoomRegion // lcTool::ZoomRegion
|
|
};
|
|
|
|
LC_ARRAY_SIZE_CHECK(TrackToolFromTool, lcTool::Count);
|
|
|
|
return TrackToolFromTool[static_cast<int>(OverrideTool)];
|
|
}
|
|
|
|
float lcView::GetUIScale() const
|
|
{
|
|
return mWidget ? mWidget->GetDeviceScale() : 1.0f;
|
|
}
|
|
|
|
float lcView::GetOverlayScale() const
|
|
{
|
|
lcVector3 OverlayCenter;
|
|
lcMatrix33 RelativeRotation;
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
ActiveModel->GetMoveRotateTransform(OverlayCenter, RelativeRotation);
|
|
|
|
lcMatrix44 WorldMatrix = lcMatrix44(RelativeRotation, OverlayCenter);
|
|
|
|
if (ActiveModel != mModel)
|
|
WorldMatrix = lcMul(WorldMatrix, mActiveSubmodelTransform);
|
|
|
|
lcVector3 ScreenPos = ProjectPoint(WorldMatrix.GetTranslation());
|
|
ScreenPos[0] += 10.0f;
|
|
lcVector3 Point = UnprojectPoint(ScreenPos);
|
|
|
|
lcVector3 Dist(Point - WorldMatrix.GetTranslation());
|
|
|
|
return Dist.Length() * 5.0f * GetUIScale();
|
|
}
|
|
|
|
void lcView::BeginDrag(lcDragState DragState)
|
|
{
|
|
mDragState = DragState;
|
|
UpdateTrackTool();
|
|
}
|
|
|
|
void lcView::EndDrag(bool Accept)
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
if (Accept)
|
|
{
|
|
switch (mDragState)
|
|
{
|
|
case lcDragState::None:
|
|
break;
|
|
|
|
case lcDragState::Piece:
|
|
ActiveModel->InsertPieceToolClicked();
|
|
break;
|
|
|
|
case lcDragState::Color:
|
|
ActiveModel->PaintToolClicked(FindObjectUnderPointer(true, false).Object);
|
|
break;
|
|
}
|
|
}
|
|
|
|
mDragState = lcDragState::None;
|
|
UpdateTrackTool();
|
|
|
|
ActiveModel->SetPreviewInsertPieceInfo(std::vector<lcInsertPieceInfo>());
|
|
ActiveModel->UpdateAllViews();
|
|
}
|
|
|
|
void lcView::SetViewpoint(lcViewpoint Viewpoint)
|
|
{
|
|
if (!mCamera || !mCamera->IsSimple())
|
|
{
|
|
lcCamera* OldCamera = mCamera;
|
|
|
|
mCamera = new lcCamera(true);
|
|
|
|
if (OldCamera)
|
|
mCamera->CopySettings(OldCamera);
|
|
}
|
|
|
|
mCamera->SetViewpoint(Viewpoint);
|
|
ZoomExtents(lcGeometryBoundsMode::IncludeFallback, false);
|
|
Redraw();
|
|
}
|
|
|
|
void lcView::SetViewpoint(const lcVector3& Position)
|
|
{
|
|
if (!mCamera || !mCamera->IsSimple())
|
|
{
|
|
lcCamera* OldCamera = mCamera;
|
|
|
|
mCamera = new lcCamera(true);
|
|
|
|
if (OldCamera)
|
|
mCamera->CopySettings(OldCamera);
|
|
}
|
|
|
|
mCamera->SetViewpoint(Position);
|
|
ZoomExtents(lcGeometryBoundsMode::IncludeFallback, false);
|
|
Redraw();
|
|
}
|
|
|
|
void lcView::SetViewpoint(const lcVector3& Position, const lcVector3& Target, const lcVector3& Up)
|
|
{
|
|
if (!mCamera || !mCamera->IsSimple())
|
|
{
|
|
lcCamera* OldCamera = mCamera;
|
|
|
|
mCamera = new lcCamera(true);
|
|
|
|
if (OldCamera)
|
|
mCamera->CopySettings(OldCamera);
|
|
}
|
|
|
|
mCamera->SetViewpoint(Position, Target, Up);
|
|
Redraw();
|
|
}
|
|
|
|
void lcView::SetCameraAngles(float Latitude, float Longitude)
|
|
{
|
|
if (!mCamera || !mCamera->IsSimple())
|
|
{
|
|
lcCamera* OldCamera = mCamera;
|
|
|
|
mCamera = new lcCamera(true);
|
|
|
|
if (OldCamera)
|
|
mCamera->CopySettings(OldCamera);
|
|
}
|
|
|
|
mCamera->SetAngles(Latitude, Longitude, 1.0f);
|
|
ZoomExtents(lcGeometryBoundsMode::IncludeFallback, false);
|
|
Redraw();
|
|
}
|
|
|
|
void lcView::SetDefaultCamera()
|
|
{
|
|
if (!mCamera || !mCamera->IsSimple())
|
|
mCamera = new lcCamera(true);
|
|
|
|
mCamera->SetViewpoint(lcViewpoint::Home);
|
|
ZoomExtents(lcGeometryBoundsMode::IncludeFallback, false);
|
|
Redraw();
|
|
}
|
|
|
|
void lcView::SetCamera(lcCamera* Camera, bool ForceCopy)
|
|
{
|
|
if (Camera->IsSimple() || ForceCopy)
|
|
{
|
|
if (!mCamera || !mCamera->IsSimple())
|
|
mCamera = new lcCamera(true);
|
|
|
|
mCamera->CopyPosition(Camera);
|
|
}
|
|
else
|
|
{
|
|
if (mCamera && mCamera->IsSimple())
|
|
delete mCamera;
|
|
|
|
mCamera = Camera;
|
|
}
|
|
}
|
|
|
|
void lcView::SetCamera(const QString& CameraName)
|
|
{
|
|
const std::vector<std::unique_ptr<lcCamera>>& Cameras = mModel->GetCameras();
|
|
|
|
for (size_t CameraIndex = 0; CameraIndex < Cameras.size(); CameraIndex++)
|
|
{
|
|
if (CameraName.compare(Cameras[CameraIndex]->GetName(), Qt::CaseInsensitive) == 0)
|
|
{
|
|
SetCameraIndex(CameraIndex);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void lcView::SetCameraIndex(size_t CameraIndex)
|
|
{
|
|
const std::vector<std::unique_ptr<lcCamera>>& Cameras = mModel->GetCameras();
|
|
|
|
if (CameraIndex >= Cameras.size())
|
|
return;
|
|
|
|
lcCamera* Camera = Cameras[CameraIndex].get();
|
|
SetCamera(Camera, false);
|
|
|
|
Redraw();
|
|
}
|
|
|
|
void lcView::SetCameraProjection(lcCameraProjection CameraProjection)
|
|
{
|
|
if (mCamera->IsSimple())
|
|
{
|
|
mCamera->SetProjection(CameraProjection);
|
|
Redraw();
|
|
}
|
|
else
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
if (ActiveModel)
|
|
ActiveModel->SetCameraProjection(mCamera, CameraProjection);
|
|
}
|
|
}
|
|
|
|
void lcView::LookAt()
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
if (ActiveModel)
|
|
ActiveModel->LookAt(mCamera);
|
|
}
|
|
|
|
void lcView::MoveCamera(const lcVector3& Direction)
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
if (ActiveModel)
|
|
ActiveModel->MoveCamera(mCamera, Direction);
|
|
}
|
|
|
|
void lcView::Zoom(float Amount)
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
if (ActiveModel)
|
|
ActiveModel->Zoom(mCamera, Amount);
|
|
}
|
|
|
|
void lcView::ZoomExtents(lcGeometryBoundsMode Mode, bool AdjustFarPlane)
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
if (ActiveModel)
|
|
{
|
|
const lcMatrix44 WorldMatrix = mActiveSubmodelInstance ? mActiveSubmodelTransform : lcMatrix44Identity();
|
|
|
|
ActiveModel->ZoomExtents(mCamera, (float)mWidth / (float)mHeight, WorldMatrix, Mode, AdjustFarPlane);
|
|
}
|
|
}
|
|
|
|
void lcView::ZoomExtentsAtStep(lcStep Step, lcGeometryBoundsMode Mode, bool AdjustFarPlane)
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
if (!ActiveModel)
|
|
return;
|
|
|
|
const lcStep CurrentStep = ActiveModel->GetCurrentStep();
|
|
|
|
ActiveModel->SetTemporaryStep(Step);
|
|
ZoomExtents(Mode, AdjustFarPlane);
|
|
ActiveModel->SetTemporaryStep(CurrentStep);
|
|
|
|
if (!ActiveModel->IsActive())
|
|
ActiveModel->CalculateStep(LC_STEP_MAX);
|
|
}
|
|
|
|
lcCursor lcView::GetCursor() const
|
|
{
|
|
const lcTool CurrentTool = GetCurrentTool();
|
|
const bool IsMoveTool = mTrackTool >= lcTrackTool::MoveX && mTrackTool <= lcTrackTool::MoveXYZ;
|
|
|
|
if (mTrackButton != lcTrackButton::None && mTrackTool != lcTrackTool::Paint && !IsMoveTool && CurrentTool != lcTool::Rotate)
|
|
return lcCursor::Hidden;
|
|
|
|
if (mTrackTool == lcTrackTool::Select)
|
|
{
|
|
if (mMouseModifiers & Qt::ControlModifier)
|
|
return lcCursor::SelectAdd;
|
|
|
|
if (mMouseModifiers & Qt::ShiftModifier)
|
|
return lcCursor::SelectRemove;
|
|
}
|
|
|
|
constexpr lcCursor CursorFromTrackTool[] =
|
|
{
|
|
lcCursor::Select, // lcTrackTool::None
|
|
lcCursor::Brick, // lcTrackTool::Insert
|
|
lcCursor::PointLight, // lcTrackTool::PointLight
|
|
lcCursor::SpotLight, // lcTrackTool::SpotLight
|
|
lcCursor::DirectionalLight, // lcTrackTool::DirectionalLight
|
|
lcCursor::AreaLight, // lcTrackTool::AreaLight
|
|
lcCursor::Camera, // lcTrackTool::Camera
|
|
lcCursor::Select, // lcTrackTool::Select
|
|
lcCursor::Default, // lcTrackTool::MoveX
|
|
lcCursor::Default, // lcTrackTool::MoveY
|
|
lcCursor::Default, // lcTrackTool::MoveZ
|
|
lcCursor::Default, // lcTrackTool::MoveXY
|
|
lcCursor::Default, // lcTrackTool::MoveXZ
|
|
lcCursor::Default, // lcTrackTool::MoveYZ
|
|
lcCursor::Default, // lcTrackTool::MoveXYZ
|
|
lcCursor::Default, // lcTrackTool::RotateX
|
|
lcCursor::Default, // lcTrackTool::RotateY
|
|
lcCursor::Default, // lcTrackTool::RotateZ
|
|
lcCursor::Default, // lcTrackTool::RotateTrackBall
|
|
lcCursor::Default, // lcTrackTool::RotateCamera
|
|
lcCursor::Select, // lcTrackTool::RotateTrainTrackRight
|
|
lcCursor::Select, // lcTrackTool::RotateTrainTrackLeft
|
|
lcCursor::Select, // lcTrackTool::InsertTrainTrack
|
|
lcCursor::Select, // lcTrackTool::SelectTrainTrack
|
|
lcCursor::Move, // lcTrackTool::ScalePlus
|
|
lcCursor::Move, // lcTrackTool::ScaleMinus
|
|
lcCursor::Delete, // lcTrackTool::Eraser
|
|
lcCursor::Paint, // lcTrackTool::Paint
|
|
lcCursor::ColorPicker, // lcTrackTool::ColorPicker
|
|
lcCursor::Zoom, // lcTrackTool::Zoom
|
|
lcCursor::Pan, // lcTrackTool::Pan
|
|
lcCursor::RotateX, // lcTrackTool::OrbitX
|
|
lcCursor::RotateY, // lcTrackTool::OrbitY
|
|
lcCursor::RotateView, // lcTrackTool::OrbitXY
|
|
lcCursor::Roll, // lcTrackTool::Roll
|
|
lcCursor::ZoomRegion // lcTrackTool::ZoomRegion
|
|
};
|
|
|
|
LC_ARRAY_SIZE_CHECK(CursorFromTrackTool, lcTrackTool::Count);
|
|
|
|
if (mTrackTool >= lcTrackTool::None && mTrackTool < lcTrackTool::Count)
|
|
return CursorFromTrackTool[static_cast<int>(mTrackTool)];
|
|
|
|
return lcCursor::Select;
|
|
}
|
|
|
|
void lcView::SetCursor(lcCursor CursorType)
|
|
{
|
|
if (mCursor == CursorType)
|
|
return;
|
|
|
|
struct lcCursorInfo
|
|
{
|
|
int x, y;
|
|
const char* Name;
|
|
};
|
|
|
|
constexpr lcCursorInfo Cursors[] =
|
|
{
|
|
{ 0, 0, "" }, // lcCursor::Hidden
|
|
{ 0, 0, "" }, // lcCursor::Default
|
|
{ 8, 3, ":/resources/cursor_insert" }, // lcCursor::Brick
|
|
{ 15, 15, ":/resources/cursor_light" }, // lcCursor::PointLight
|
|
{ 7, 10, ":/resources/cursor_spotlight" }, // lcCursor::SpotLight
|
|
{ 15, 15, ":/resources/cursor_sunlight" }, // lcCursor::DirectionalLight
|
|
{ 15, 15, ":/resources/cursor_arealight" }, // lcCursor::AreaLight
|
|
{ 15, 9, ":/resources/cursor_camera" }, // lcCursor::Camera
|
|
{ 0, 2, ":/resources/cursor_select" }, // lcCursor::Select
|
|
{ 0, 2, ":/resources/cursor_select_add" }, // lcCursor::SelectAdd
|
|
{ 0, 2, ":/resources/cursor_select_remove" }, // lcCursor::SelectRemove
|
|
{ 15, 15, ":/resources/cursor_move" }, // lcCursor::Move
|
|
{ 15, 15, ":/resources/cursor_rotate" }, // lcCursor::Rotate
|
|
{ 15, 15, ":/resources/cursor_rotatex" }, // lcCursor::RotateX
|
|
{ 15, 15, ":/resources/cursor_rotatey" }, // lcCursor::RotateY
|
|
{ 0, 10, ":/resources/cursor_delete" }, // lcCursor::Delete
|
|
{ 14, 14, ":/resources/cursor_paint" }, // lcCursor::Paint
|
|
{ 1, 13, ":/resources/cursor_color_picker" }, // lcCursor::ColorPicker
|
|
{ 15, 15, ":/resources/cursor_zoom" }, // lcCursor::Zoom
|
|
{ 9, 9, ":/resources/cursor_zoom_region" }, // lcCursor::ZoomRegion
|
|
{ 15, 15, ":/resources/cursor_pan" }, // lcCursor::Pan
|
|
{ 15, 15, ":/resources/cursor_roll" }, // lcCursor::Roll
|
|
{ 15, 15, ":/resources/cursor_rotate_view" }, // lcCursor::RotateView
|
|
};
|
|
|
|
LC_ARRAY_SIZE_CHECK(Cursors, lcCursor::Count);
|
|
|
|
if (CursorType == lcCursor::Hidden)
|
|
{
|
|
mWidget->setCursor(Qt::BlankCursor);
|
|
mCursor = CursorType;
|
|
}
|
|
else if (CursorType == lcCursor::Default)
|
|
{
|
|
mWidget->unsetCursor();
|
|
mCursor = CursorType;
|
|
}
|
|
else if (CursorType >= lcCursor::First && CursorType < lcCursor::Count)
|
|
{
|
|
const lcCursorInfo& Cursor = Cursors[static_cast<int>(CursorType)];
|
|
mWidget->setCursor(QCursor(QPixmap(Cursor.Name), Cursor.x, Cursor.y));
|
|
mCursor = CursorType;
|
|
}
|
|
else
|
|
{
|
|
mWidget->unsetCursor();
|
|
mCursor = lcCursor::Default;
|
|
}
|
|
}
|
|
|
|
void lcView::UpdateCursor()
|
|
{
|
|
SetCursor(GetCursor());
|
|
}
|
|
|
|
lcTool lcView::GetCurrentTool() const
|
|
{
|
|
constexpr lcTool ToolFromTrackTool[] =
|
|
{
|
|
lcTool::Select, // lcTrackTool::None
|
|
lcTool::Insert, // lcTrackTool::Insert
|
|
lcTool::PointLight, // lcTrackTool::PointLight
|
|
lcTool::SpotLight, // lcTrackTool::SpotLight
|
|
lcTool::DirectionalLight, // lcTrackTool::DirectionalLight
|
|
lcTool::AreaLight, // lcTrackTool::AreaLight
|
|
lcTool::Camera, // lcTrackTool::Camera
|
|
lcTool::Select, // lcTrackTool::Select
|
|
lcTool::Move, // lcTrackTool::MoveX
|
|
lcTool::Move, // lcTrackTool::MoveY
|
|
lcTool::Move, // lcTrackTool::MoveZ
|
|
lcTool::Move, // lcTrackTool::MoveXY
|
|
lcTool::Move, // lcTrackTool::MoveXZ
|
|
lcTool::Move, // lcTrackTool::MoveYZ
|
|
lcTool::Move, // lcTrackTool::MoveXYZ
|
|
lcTool::Rotate, // lcTrackTool::RotateX
|
|
lcTool::Rotate, // lcTrackTool::RotateY
|
|
lcTool::Rotate, // lcTrackTool::RotateZ
|
|
lcTool::Rotate, // lcTrackTool::RotateTrackBall
|
|
lcTool::Rotate, // lcTrackTool::RotateCamera
|
|
lcTool::Rotate, // lcTrackTool::RotateTrainTrackRight
|
|
lcTool::Rotate, // lcTrackTool::RotateTrainTrackLeft
|
|
lcTool::Insert, // lcTrackTool::InsertTrainTrack
|
|
lcTool::Select, // lcTrackTool::SelectTrainTrack
|
|
lcTool::Move, // lcTrackTool::ScalePlus
|
|
lcTool::Move, // lcTrackTool::ScaleMinus
|
|
lcTool::Eraser, // lcTrackTool::Eraser
|
|
lcTool::Paint, // lcTrackTool::Paint
|
|
lcTool::ColorPicker, // lcTrackTool::ColorPicker
|
|
lcTool::Zoom, // lcTrackTool::Zoom
|
|
lcTool::Pan, // lcTrackTool::Pan
|
|
lcTool::RotateView, // lcTrackTool::OrbitX
|
|
lcTool::RotateView, // lcTrackTool::OrbitY
|
|
lcTool::RotateView, // lcTrackTool::OrbitXY
|
|
lcTool::Roll, // lcTrackTool::Roll
|
|
lcTool::ZoomRegion // lcTrackTool::ZoomRegion
|
|
};
|
|
|
|
LC_ARRAY_SIZE_CHECK(ToolFromTrackTool, lcTrackTool::Count);
|
|
|
|
if (mTrackTool >= lcTrackTool::None && mTrackTool < lcTrackTool::Count)
|
|
return ToolFromTrackTool[static_cast<int>(mTrackTool)];
|
|
|
|
return lcTool::Select;
|
|
}
|
|
|
|
void lcView::UpdateTrackTool()
|
|
{
|
|
if (mViewType != lcViewType::View)
|
|
{
|
|
mTrackTool = lcTrackTool::None;
|
|
UpdateCursor();
|
|
return;
|
|
}
|
|
|
|
lcTool CurrentTool = gMainWindow->GetTool();
|
|
lcTrackTool NewTrackTool = mTrackTool;
|
|
quint32 NewTrackSection = ~0U;
|
|
int x = mMouseX;
|
|
int y = mMouseY;
|
|
bool Redraw = false;
|
|
mMouseDownPiece = nullptr;
|
|
mTrackToolFromOverlay = false;
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
switch (CurrentTool)
|
|
{
|
|
case lcTool::Insert:
|
|
NewTrackTool = lcTrackTool::Insert;
|
|
break;
|
|
|
|
case lcTool::PointLight:
|
|
NewTrackTool = lcTrackTool::PointLight;
|
|
break;
|
|
|
|
case lcTool::SpotLight:
|
|
NewTrackTool = lcTrackTool::SpotLight;
|
|
break;
|
|
|
|
case lcTool::DirectionalLight:
|
|
NewTrackTool = lcTrackTool::DirectionalLight;
|
|
break;
|
|
|
|
case lcTool::AreaLight:
|
|
NewTrackTool = lcTrackTool::AreaLight;
|
|
break;
|
|
|
|
case lcTool::Camera:
|
|
NewTrackTool = lcTrackTool::Camera;
|
|
break;
|
|
|
|
case lcTool::Select:
|
|
case lcTool::Move:
|
|
{
|
|
std::tie(NewTrackTool, NewTrackSection) = mViewManipulator->UpdateSelectMove(mTrackButton);
|
|
mTrackToolFromOverlay = NewTrackTool != lcTrackTool::MoveXYZ && NewTrackTool != lcTrackTool::Select;
|
|
Redraw = NewTrackTool != mTrackTool || NewTrackSection != mTrackToolSection;
|
|
|
|
if (CurrentTool == lcTool::Select && NewTrackTool == lcTrackTool::Select && mMouseModifiers == Qt::NoModifier)
|
|
{
|
|
lcObjectSection ObjectSection = FindObjectUnderPointer(false, false);
|
|
lcObject* Object = ObjectSection.Object;
|
|
|
|
if (Object && Object->IsPiece() && ObjectSection.Section == LC_PIECE_SECTION_POSITION && Object->IsSelected())
|
|
{
|
|
lcPiece* Piece = (lcPiece*)Object;
|
|
mMouseDownTransform = Piece->mModelWorld;
|
|
mMouseDownPiece = Piece;
|
|
NewTrackTool = lcTrackTool::MoveXYZ;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case lcTool::Rotate:
|
|
{
|
|
NewTrackTool = mViewManipulator->UpdateRotate();
|
|
mTrackToolFromOverlay = NewTrackTool != lcTrackTool::RotateTrackBall;
|
|
Redraw = NewTrackTool != mTrackTool;
|
|
}
|
|
break;
|
|
|
|
case lcTool::Eraser:
|
|
NewTrackTool = lcTrackTool::Eraser;
|
|
break;
|
|
|
|
case lcTool::Paint:
|
|
NewTrackTool = lcTrackTool::Paint;
|
|
break;
|
|
|
|
case lcTool::ColorPicker:
|
|
NewTrackTool = lcTrackTool::ColorPicker;
|
|
break;
|
|
|
|
case lcTool::Zoom:
|
|
NewTrackTool = lcTrackTool::Zoom;
|
|
break;
|
|
|
|
case lcTool::Pan:
|
|
NewTrackTool = lcTrackTool::Pan;
|
|
break;
|
|
|
|
case lcTool::RotateView:
|
|
{
|
|
int vx, vy, vw, vh;
|
|
|
|
vx = 0;
|
|
vy = 0;
|
|
vw = mWidth;
|
|
vh = mHeight;
|
|
|
|
int cx = vx + vw / 2;
|
|
int cy = vy + vh / 2;
|
|
|
|
float d = sqrtf((float)((cx - x) * (cx - x) + (cy - y) * (cy - y)));
|
|
float r = lcMin(vw, vh) * 0.35f;
|
|
|
|
const float SquareSize = lcMax(8.0f, (vw + vh) / 200.0f);
|
|
|
|
if ((d < r + SquareSize) && (d > r - SquareSize))
|
|
{
|
|
if ((cx - x < SquareSize) && (cx - x > -SquareSize))
|
|
{
|
|
NewTrackTool = lcTrackTool::OrbitY;
|
|
mTrackToolFromOverlay = true;
|
|
}
|
|
|
|
if ((cy - y < SquareSize) && (cy - y > -SquareSize))
|
|
{
|
|
NewTrackTool = lcTrackTool::OrbitX;
|
|
mTrackToolFromOverlay = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (d < r)
|
|
NewTrackTool = lcTrackTool::OrbitXY;
|
|
else
|
|
NewTrackTool = lcTrackTool::Roll;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case lcTool::Roll:
|
|
NewTrackTool = lcTrackTool::Roll;
|
|
break;
|
|
|
|
case lcTool::ZoomRegion:
|
|
NewTrackTool = lcTrackTool::ZoomRegion;
|
|
break;
|
|
|
|
case lcTool::Count:
|
|
break;
|
|
}
|
|
|
|
switch (mDragState)
|
|
{
|
|
case lcDragState::None:
|
|
break;
|
|
|
|
case lcDragState::Piece:
|
|
NewTrackTool = lcTrackTool::Insert;
|
|
Redraw = true;
|
|
break;
|
|
|
|
case lcDragState::Color:
|
|
NewTrackTool = lcTrackTool::Paint;
|
|
break;
|
|
}
|
|
|
|
mTrackTool = NewTrackTool;
|
|
mTrackToolSection = NewTrackSection;
|
|
|
|
UpdateCursor();
|
|
|
|
if (Redraw)
|
|
ActiveModel->UpdateAllViews();
|
|
}
|
|
|
|
void lcView::StartOrbitTracking()
|
|
{
|
|
mTrackTool = lcTrackTool::OrbitXY;
|
|
UpdateCursor();
|
|
OnButtonDown(lcTrackButton::Left);
|
|
}
|
|
|
|
void lcView::StartPanGesture()
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
StartPan(mWidth / 2, mHeight / 2);
|
|
ActiveModel->BeginMouseTool(lcTool::Pan, this);
|
|
}
|
|
|
|
void lcView::UpdatePanGesture(int dx, int dy)
|
|
{
|
|
UpdatePan(mPanX + dx, mPanY + dy);
|
|
}
|
|
|
|
void lcView::StartPan(int x, int y)
|
|
{
|
|
mPanX = x;
|
|
mPanY = y;
|
|
}
|
|
|
|
void lcView::UpdatePan(int x, int y)
|
|
{
|
|
if (x == mPanX && y == mPanY)
|
|
return;
|
|
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
lcVector3 Points[4] =
|
|
{
|
|
lcVector3((float)x, (float)y, 0.0f),
|
|
lcVector3((float)x, (float)y, 1.0f),
|
|
lcVector3(mPanX, mPanY, 0.0f),
|
|
lcVector3(mPanX, mPanY, 1.0f)
|
|
};
|
|
|
|
UnprojectPoints(Points, 4);
|
|
|
|
const lcVector3& CurrentStart = Points[0];
|
|
const lcVector3& CurrentEnd = Points[1];
|
|
const lcVector3& MouseDownStart = Points[2];
|
|
const lcVector3& MouseDownEnd = Points[3];
|
|
lcVector3 Center = ActiveModel->GetSelectionOrModelCenter();
|
|
|
|
lcVector3 PlaneNormal(mCamera->mPosition - mCamera->mTargetPosition);
|
|
lcVector4 Plane(PlaneNormal, -lcDot(PlaneNormal, Center));
|
|
lcVector3 Intersection, MoveStart;
|
|
|
|
if (!lcLineSegmentPlaneIntersection(&Intersection, CurrentStart, CurrentEnd, Plane) || !lcLineSegmentPlaneIntersection(&MoveStart, MouseDownStart, MouseDownEnd, Plane))
|
|
{
|
|
Center = MouseDownStart + lcNormalize(MouseDownEnd - MouseDownStart) * 10.0f;
|
|
Plane = lcVector4(PlaneNormal, -lcDot(PlaneNormal, Center));
|
|
|
|
if (!lcLineSegmentPlaneIntersection(&Intersection, CurrentStart, CurrentEnd, Plane) || !lcLineSegmentPlaneIntersection(&MoveStart, MouseDownStart, MouseDownEnd, Plane))
|
|
return;
|
|
}
|
|
|
|
mPanX = x;
|
|
mPanY = y;
|
|
|
|
ActiveModel->UpdatePanTool(mCamera, MoveStart - Intersection);
|
|
}
|
|
|
|
void lcView::EndPanGesture(bool Accept)
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
ActiveModel->EndMouseTool(lcTool::Pan, this, Accept);
|
|
}
|
|
|
|
lcVector3 lcView::GetTrackballPoint(int x, int y) const
|
|
{
|
|
const float PointX = ((float)x - mTrackballCenter[0]) / mTrackballRadius;
|
|
const float PointY = ((float)y - mTrackballCenter[1]) / mTrackballRadius;
|
|
const float LengthSquared = PointX * PointX + PointY * PointY;
|
|
const float PointZ = LengthSquared <= 0.5f ? sqrtf(1.0f - LengthSquared) : 0.5f / sqrtf(LengthSquared);
|
|
|
|
return lcNormalize(lcVector3(PointX, PointY, PointZ));
|
|
}
|
|
|
|
void lcView::StartTracking(lcTrackButton TrackButton)
|
|
{
|
|
mTrackButton = TrackButton;
|
|
mTrackUpdated = false;
|
|
mMouseDownX = mMouseX;
|
|
mMouseDownY = mMouseY;
|
|
mCameraRotationAngle = 0.0f;
|
|
lcTool Tool = GetCurrentTool();
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
switch (Tool)
|
|
{
|
|
case lcTool::Insert:
|
|
case lcTool::PointLight:
|
|
case lcTool::SpotLight:
|
|
case lcTool::DirectionalLight:
|
|
case lcTool::AreaLight:
|
|
case lcTool::Camera:
|
|
case lcTool::Select:
|
|
break;
|
|
|
|
case lcTool::Move:
|
|
case lcTool::Rotate:
|
|
ActiveModel->BeginMouseTool(Tool, this);
|
|
|
|
if (mTrackTool == lcTrackTool::RotateX || mTrackTool == lcTrackTool::RotateY || mTrackTool == lcTrackTool::RotateZ || mTrackTool == lcTrackTool::RotateCamera)
|
|
{
|
|
lcVector3 OverlayCenter;
|
|
lcMatrix33 RelativeRotation;
|
|
|
|
ActiveModel->GetMoveRotateTransform(OverlayCenter, RelativeRotation);
|
|
|
|
lcMatrix44 WorldMatrix = lcMatrix44(RelativeRotation, OverlayCenter);
|
|
|
|
if (ActiveModel != mModel)
|
|
WorldMatrix = lcMul(WorldMatrix, mActiveSubmodelTransform);
|
|
|
|
const lcVector3 ScreenCenter = ProjectPoint(WorldMatrix.GetTranslation());
|
|
|
|
mCameraRotationMouseAngle = atan2f((float)mMouseY - ScreenCenter[1], (float)mMouseX - ScreenCenter[0]);
|
|
mCameraRotationLastMouseAngle = mCameraRotationMouseAngle;
|
|
}
|
|
|
|
if (mTrackTool == lcTrackTool::RotateTrackBall)
|
|
{
|
|
lcVector3 OverlayCenter;
|
|
lcMatrix33 RelativeRotation;
|
|
ActiveModel->GetMoveRotateTransform(OverlayCenter, RelativeRotation);
|
|
|
|
lcMatrix44 WorldMatrix = lcMatrix44(RelativeRotation, OverlayCenter);
|
|
if (ActiveModel != mModel)
|
|
WorldMatrix = lcMul(WorldMatrix, mActiveSubmodelTransform);
|
|
|
|
mTrackballCenter = ProjectPoint(WorldMatrix.GetTranslation());
|
|
const lcMatrix44 CameraWorldMatrix = lcMatrix44AffineInverse(mCamera->mWorldView);
|
|
const lcVector3 CameraRight = lcNormalize(lcMul30(lcVector3(1.0f, 0.0f, 0.0f), CameraWorldMatrix));
|
|
const lcVector3 RadiusPoint = ProjectPoint(WorldMatrix.GetTranslation() + CameraRight * (2.0f * GetOverlayScale()));
|
|
mTrackballRadius = lcMax(1.0f, lcLength(RadiusPoint - mTrackballCenter));
|
|
mTrackballVector = GetTrackballPoint(mMouseX, mMouseY);
|
|
mTrackballPendingRotation = lcMatrix33Identity();
|
|
mTrackballSnapping = gMainWindow->GetAngleSnap() != 0.0f;
|
|
mTrackballHasPendingRotation = false;
|
|
}
|
|
break;
|
|
|
|
case lcTool::Eraser:
|
|
case lcTool::Paint:
|
|
case lcTool::ColorPicker:
|
|
break;
|
|
|
|
case lcTool::Pan:
|
|
StartPan(mMouseX, mMouseY);
|
|
ActiveModel->BeginMouseTool(Tool, this);
|
|
break;
|
|
|
|
case lcTool::Zoom:
|
|
case lcTool::RotateView:
|
|
case lcTool::Roll:
|
|
ActiveModel->BeginMouseTool(Tool, this);
|
|
break;
|
|
|
|
case lcTool::ZoomRegion:
|
|
break;
|
|
|
|
case lcTool::Count:
|
|
break;
|
|
}
|
|
|
|
UpdateCursor();
|
|
}
|
|
|
|
void lcView::StopTracking(bool Accept)
|
|
{
|
|
if (mTrackButton == lcTrackButton::None)
|
|
return;
|
|
|
|
lcTool Tool = GetCurrentTool();
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
switch (Tool)
|
|
{
|
|
case lcTool::Insert:
|
|
case lcTool::PointLight:
|
|
case lcTool::SpotLight:
|
|
case lcTool::DirectionalLight:
|
|
case lcTool::AreaLight:
|
|
case lcTool::Camera:
|
|
break;
|
|
|
|
case lcTool::Select:
|
|
if (Accept && mMouseDownX != mMouseX && mMouseDownY != mMouseY)
|
|
{
|
|
std::vector<lcObject*> Objects = FindObjectsInBox(mMouseDownX, mMouseDownY, mMouseX, mMouseY);
|
|
|
|
if (mMouseModifiers & Qt::ControlModifier)
|
|
ActiveModel->AddToSelectionAction(Objects);
|
|
else if (mMouseModifiers & Qt::ShiftModifier)
|
|
ActiveModel->RemoveFromSelectionAction(Objects);
|
|
else
|
|
ActiveModel->SetSelectionAndFocusAction(Objects, nullptr, 0, gMainWindow->GetSelectionMode());
|
|
}
|
|
break;
|
|
|
|
case lcTool::Move:
|
|
case lcTool::Rotate:
|
|
ActiveModel->EndMouseTool(Tool, this, Accept);
|
|
break;
|
|
|
|
case lcTool::Eraser:
|
|
case lcTool::Paint:
|
|
case lcTool::ColorPicker:
|
|
break;
|
|
|
|
case lcTool::Zoom:
|
|
case lcTool::Pan:
|
|
case lcTool::RotateView:
|
|
case lcTool::Roll:
|
|
ActiveModel->EndMouseTool(Tool, this, Accept);
|
|
break;
|
|
|
|
case lcTool::ZoomRegion:
|
|
{
|
|
if (mMouseX == mMouseDownX || mMouseY == mMouseDownY)
|
|
break;
|
|
|
|
lcVector3 Points[6] =
|
|
{
|
|
lcVector3((mMouseDownX + lcMin(mMouseX, mWidth - 1)) / 2, (mMouseDownY + lcMin(mMouseY, mHeight - 1)) / 2, 0.0f),
|
|
lcVector3((mMouseDownX + lcMin(mMouseX, mWidth - 1)) / 2, (mMouseDownY + lcMin(mMouseY, mHeight - 1)) / 2, 1.0f),
|
|
lcVector3((float)mMouseX, (float)mMouseY, 0.0f),
|
|
lcVector3((float)mMouseX, (float)mMouseY, 1.0f),
|
|
lcVector3(mMouseDownX, mMouseDownY, 0.0f),
|
|
lcVector3(mMouseDownX, mMouseDownY, 1.0f)
|
|
};
|
|
|
|
UnprojectPoints(Points, 5);
|
|
|
|
lcVector3 Center = ActiveModel->GetSelectionOrModelCenter();
|
|
|
|
lcVector3 PlaneNormal(mCamera->mPosition - mCamera->mTargetPosition);
|
|
lcVector4 Plane(PlaneNormal, -lcDot(PlaneNormal, Center));
|
|
lcVector3 Target, Corners[2];
|
|
|
|
if (lcLineSegmentPlaneIntersection(&Target, Points[0], Points[1], Plane) && lcLineSegmentPlaneIntersection(&Corners[0], Points[2], Points[3], Plane) && lcLineSegmentPlaneIntersection(&Corners[1], Points[3], Points[4], Plane))
|
|
{
|
|
float AspectRatio = (float)mWidth / (float)mHeight;
|
|
ActiveModel->ZoomRegionToolClicked(this, AspectRatio, Points[0], Target, Corners);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case lcTool::Count:
|
|
break;
|
|
}
|
|
|
|
mTrackButton = lcTrackButton::None;
|
|
UpdateTrackTool();
|
|
ActiveModel->UpdateAllViews();
|
|
}
|
|
|
|
void lcView::CancelTrackingOrClearSelectionAction()
|
|
{
|
|
if (mTrackButton != lcTrackButton::None)
|
|
StopTracking(false);
|
|
else
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
if (ActiveModel)
|
|
ActiveModel->ClearSelectionAction();
|
|
}
|
|
}
|
|
|
|
void lcView::OnButtonDown(lcTrackButton TrackButton)
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
mToolClicked = false;
|
|
|
|
auto AddLight = [this, ActiveModel](lcLightType LightType)
|
|
{
|
|
ActiveModel->InsertLightToolClicked(GetCameraLightInsertPosition(), LightType);
|
|
|
|
if ((mMouseModifiers & Qt::ControlModifier) == 0)
|
|
gMainWindow->SetTool(lcTool::Select);
|
|
|
|
mToolClicked = true;
|
|
UpdateTrackTool();
|
|
};
|
|
|
|
switch (mTrackTool)
|
|
{
|
|
case lcTrackTool::None:
|
|
break;
|
|
|
|
case lcTrackTool::Insert:
|
|
{
|
|
UpdatePiecePreview();
|
|
|
|
if (!ActiveModel->InsertPieceToolClicked())
|
|
break;
|
|
|
|
if ((mMouseModifiers & Qt::ControlModifier) == 0)
|
|
gMainWindow->SetTool(lcTool::Select);
|
|
|
|
mToolClicked = true;
|
|
UpdateTrackTool();
|
|
}
|
|
break;
|
|
|
|
case lcTrackTool::PointLight:
|
|
AddLight(lcLightType::Point);
|
|
break;
|
|
|
|
case lcTrackTool::SpotLight:
|
|
AddLight(lcLightType::Spot);
|
|
break;
|
|
|
|
case lcTrackTool::DirectionalLight:
|
|
AddLight(lcLightType::Directional);
|
|
break;
|
|
|
|
case lcTrackTool::AreaLight:
|
|
AddLight(lcLightType::Area);
|
|
break;
|
|
|
|
case lcTrackTool::Camera:
|
|
{
|
|
ActiveModel->InsertCameraToolClicked(GetCameraLightInsertPosition());
|
|
|
|
if ((mMouseModifiers & Qt::ControlModifier) == 0)
|
|
gMainWindow->SetTool(lcTool::Select);
|
|
|
|
mToolClicked = true;
|
|
UpdateTrackTool();
|
|
}
|
|
break;
|
|
|
|
case lcTrackTool::Select:
|
|
{
|
|
lcObjectSection ObjectSection = FindObjectUnderPointer(false, false);
|
|
|
|
if (mMouseModifiers & Qt::ControlModifier)
|
|
ActiveModel->FocusOrDeselectObjectAction(ObjectSection.Object, ObjectSection.Section, gMainWindow->GetSelectionMode());
|
|
else if (mMouseModifiers & Qt::ShiftModifier)
|
|
{
|
|
if (ObjectSection.Object)
|
|
ActiveModel->RemoveFromSelectionAction({ ObjectSection.Object });
|
|
}
|
|
else
|
|
ActiveModel->SetSelectionAndFocusAction(std::vector<lcObject*>(), ObjectSection.Object, ObjectSection.Section, gMainWindow->GetSelectionMode());
|
|
|
|
StartTracking(TrackButton);
|
|
}
|
|
break;
|
|
|
|
case lcTrackTool::MoveX:
|
|
case lcTrackTool::MoveY:
|
|
case lcTrackTool::MoveZ:
|
|
case lcTrackTool::MoveXY:
|
|
case lcTrackTool::MoveXZ:
|
|
case lcTrackTool::MoveYZ:
|
|
case lcTrackTool::MoveXYZ:
|
|
if (ActiveModel->AnyObjectsSelected())
|
|
StartTracking(TrackButton);
|
|
break;
|
|
|
|
case lcTrackTool::RotateX:
|
|
case lcTrackTool::RotateY:
|
|
case lcTrackTool::RotateZ:
|
|
case lcTrackTool::RotateTrackBall:
|
|
case lcTrackTool::RotateCamera:
|
|
if (ActiveModel->CanRotateSelection())
|
|
StartTracking(TrackButton);
|
|
break;
|
|
|
|
case lcTrackTool::RotateTrainTrackRight:
|
|
case lcTrackTool::RotateTrainTrackLeft:
|
|
{
|
|
ActiveModel->RotateFocusedTrainTrack(mTrackTool == lcTrackTool::RotateTrainTrackRight ? 1 : -1);
|
|
|
|
mToolClicked = true;
|
|
UpdateTrackTool();
|
|
}
|
|
break;
|
|
|
|
case lcTrackTool::InsertTrainTrack:
|
|
{
|
|
ShowTrainTrackPopup();
|
|
|
|
mToolClicked = true;
|
|
UpdateTrackTool();
|
|
}
|
|
break;
|
|
|
|
case lcTrackTool::SelectTrainTrack:
|
|
{
|
|
lcObject* Focus = ActiveModel->GetFocusObject();
|
|
|
|
if (!Focus || !Focus->IsPiece())
|
|
break;
|
|
|
|
lcPiece* Piece = (lcPiece*)Focus;
|
|
const lcTrainTrackInfo* TrainTrackInfo = Piece->mPieceInfo->GetTrainTrackInfo();
|
|
|
|
if (!TrainTrackInfo)
|
|
break;
|
|
|
|
if (mTrackToolSection < LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST)
|
|
break;
|
|
|
|
lcObjectSection ObjectSection;
|
|
|
|
ObjectSection.Object = Focus;
|
|
ObjectSection.Section = mTrackToolSection;
|
|
|
|
ActiveModel->SetSelectionAndFocusAction(std::vector<lcObject*>(), ObjectSection.Object, ObjectSection.Section, gMainWindow->GetSelectionMode());
|
|
}
|
|
break;
|
|
|
|
case lcTrackTool::ScalePlus:
|
|
case lcTrackTool::ScaleMinus:
|
|
if (ActiveModel->AnyPiecesSelected())
|
|
StartTracking(TrackButton);
|
|
break;
|
|
|
|
case lcTrackTool::Eraser:
|
|
ActiveModel->EraserToolClicked(FindObjectUnderPointer(false, false).Object);
|
|
mToolClicked = true;
|
|
break;
|
|
|
|
case lcTrackTool::Paint:
|
|
ActiveModel->PaintToolClicked(FindObjectUnderPointer(true, false).Object);
|
|
StartTracking(TrackButton);
|
|
mToolClicked = true;
|
|
break;
|
|
|
|
case lcTrackTool::ColorPicker:
|
|
ActiveModel->ColorPickerToolClicked(FindObjectUnderPointer(true, false).Object);
|
|
mToolClicked = true;
|
|
break;
|
|
|
|
case lcTrackTool::Zoom:
|
|
case lcTrackTool::Pan:
|
|
case lcTrackTool::OrbitX:
|
|
case lcTrackTool::OrbitY:
|
|
case lcTrackTool::OrbitXY:
|
|
case lcTrackTool::Roll:
|
|
case lcTrackTool::ZoomRegion:
|
|
StartTracking(TrackButton);
|
|
break;
|
|
|
|
case lcTrackTool::Count:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void lcView::OnLeftButtonDown()
|
|
{
|
|
if (mTrackButton != lcTrackButton::None)
|
|
{
|
|
StopTracking(false);
|
|
return;
|
|
}
|
|
|
|
if (mViewSphere->OnLeftButtonDown())
|
|
return;
|
|
|
|
lcTrackTool OverrideTool = GetOverrideTrackTool(Qt::LeftButton);
|
|
|
|
if (OverrideTool != lcTrackTool::None)
|
|
{
|
|
mTrackTool = OverrideTool;
|
|
UpdateCursor();
|
|
}
|
|
|
|
OnButtonDown(lcTrackButton::Left);
|
|
}
|
|
|
|
void lcView::OnLeftButtonUp()
|
|
{
|
|
StopTracking(mTrackButton == lcTrackButton::Left);
|
|
|
|
if (mViewSphere->OnLeftButtonUp())
|
|
return;
|
|
}
|
|
|
|
void lcView::OnLeftButtonDoubleClick()
|
|
{
|
|
if (mViewType != lcViewType::View)
|
|
{
|
|
ZoomExtents(lcGeometryBoundsMode::IncludeFallback, false);
|
|
return;
|
|
}
|
|
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
if (mTrackTool == lcTrackTool::RotateTrainTrackRight || mTrackTool == lcTrackTool::RotateTrainTrackLeft)
|
|
{
|
|
ActiveModel->RotateFocusedTrainTrack(mTrackTool == lcTrackTool::RotateTrainTrackRight ? 1 : -1);
|
|
|
|
mToolClicked = true;
|
|
UpdateTrackTool();
|
|
|
|
return;
|
|
}
|
|
|
|
lcObjectSection ObjectSection = FindObjectUnderPointer(false, false);
|
|
|
|
if (mMouseModifiers & Qt::ControlModifier)
|
|
ActiveModel->FocusOrDeselectObjectAction(ObjectSection.Object, ObjectSection.Section, gMainWindow->GetSelectionMode());
|
|
else if (mMouseModifiers & Qt::ShiftModifier)
|
|
{
|
|
if (ObjectSection.Object)
|
|
ActiveModel->RemoveFromSelectionAction({ ObjectSection.Object });
|
|
}
|
|
else
|
|
ActiveModel->SetSelectionAndFocusAction(std::vector<lcObject*>(), ObjectSection.Object, ObjectSection.Section, gMainWindow->GetSelectionMode());
|
|
}
|
|
|
|
void lcView::OnMiddleButtonDown()
|
|
{
|
|
if (mTrackButton != lcTrackButton::None)
|
|
{
|
|
StopTracking(false);
|
|
return;
|
|
}
|
|
|
|
lcTrackTool OverrideTool = GetOverrideTrackTool(Qt::MiddleButton);
|
|
|
|
if (OverrideTool != lcTrackTool::None)
|
|
{
|
|
mTrackTool = OverrideTool;
|
|
UpdateCursor();
|
|
}
|
|
|
|
OnButtonDown(lcTrackButton::Middle);
|
|
}
|
|
|
|
void lcView::OnMiddleButtonUp()
|
|
{
|
|
StopTracking(mTrackButton == lcTrackButton::Middle);
|
|
}
|
|
|
|
void lcView::OnRightButtonDown()
|
|
{
|
|
if (mTrackButton != lcTrackButton::None)
|
|
{
|
|
StopTracking(false);
|
|
return;
|
|
}
|
|
|
|
lcTrackTool OverrideTool = GetOverrideTrackTool(Qt::RightButton);
|
|
|
|
if (OverrideTool != lcTrackTool::None)
|
|
{
|
|
mTrackTool = OverrideTool;
|
|
UpdateCursor();
|
|
}
|
|
|
|
OnButtonDown(lcTrackButton::Right);
|
|
}
|
|
|
|
void lcView::OnRightButtonUp()
|
|
{
|
|
bool ShowMenu = !mToolClicked && (mTrackButton == lcTrackButton::None || !mTrackUpdated);
|
|
|
|
if (mTrackButton != lcTrackButton::None)
|
|
StopTracking(mTrackButton == lcTrackButton::Right);
|
|
|
|
if (ShowMenu)
|
|
ShowContextMenu();
|
|
}
|
|
|
|
void lcView::OnBackButtonDown()
|
|
{
|
|
}
|
|
|
|
void lcView::OnBackButtonUp()
|
|
{
|
|
gMainWindow->HandleCommand(LC_VIEW_TIME_PREVIOUS);
|
|
}
|
|
|
|
void lcView::OnForwardButtonDown()
|
|
{
|
|
}
|
|
|
|
void lcView::OnForwardButtonUp()
|
|
{
|
|
gMainWindow->HandleCommand(LC_VIEW_TIME_NEXT);
|
|
}
|
|
|
|
void lcView::OnMouseMove()
|
|
{
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
if (!ActiveModel)
|
|
return;
|
|
|
|
if (mTrackButton == lcTrackButton::None)
|
|
{
|
|
if (mViewSphere->OnMouseMove())
|
|
{
|
|
lcTrackTool NewTrackTool = mViewSphere->IsDragging() ? lcTrackTool::OrbitXY : lcTrackTool::None;
|
|
|
|
if (NewTrackTool != mTrackTool)
|
|
{
|
|
mTrackTool = NewTrackTool;
|
|
UpdateCursor();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
UpdateTrackTool();
|
|
|
|
if (mTrackTool == lcTrackTool::Insert)
|
|
{
|
|
UpdatePiecePreview();
|
|
ActiveModel->UpdateAllViews();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
mTrackUpdated = true;
|
|
const float MouseSensitivity = 0.5f / (21.0f - lcGetPreferences().mMouseSensitivity);
|
|
|
|
switch (mTrackTool)
|
|
{
|
|
case lcTrackTool::None:
|
|
case lcTrackTool::Insert:
|
|
case lcTrackTool::PointLight:
|
|
case lcTrackTool::SpotLight:
|
|
case lcTrackTool::DirectionalLight:
|
|
case lcTrackTool::AreaLight:
|
|
case lcTrackTool::Camera:
|
|
break;
|
|
|
|
case lcTrackTool::Select:
|
|
Redraw();
|
|
break;
|
|
|
|
case lcTrackTool::MoveX:
|
|
case lcTrackTool::MoveY:
|
|
case lcTrackTool::MoveZ:
|
|
case lcTrackTool::MoveXY:
|
|
case lcTrackTool::MoveXZ:
|
|
case lcTrackTool::MoveYZ:
|
|
case lcTrackTool::MoveXYZ:
|
|
case lcTrackTool::ScalePlus:
|
|
case lcTrackTool::ScaleMinus:
|
|
{
|
|
lcVector3 Points[4] =
|
|
{
|
|
lcVector3((float)mMouseX, (float)mMouseY, 0.0f),
|
|
lcVector3((float)mMouseX, (float)mMouseY, 1.0f),
|
|
lcVector3(mMouseDownX, mMouseDownY, 0.0f),
|
|
lcVector3(mMouseDownX, mMouseDownY, 1.0f)
|
|
};
|
|
|
|
UnprojectPoints(Points, 4);
|
|
|
|
const lcVector3& CurrentStart = Points[0];
|
|
const lcVector3& CurrentEnd = Points[1];
|
|
const lcVector3& MouseDownStart = Points[2];
|
|
const lcVector3& MouseDownEnd = Points[3];
|
|
|
|
lcVector3 OverlayCenter;
|
|
lcMatrix33 RelativeRotation;
|
|
ActiveModel->GetMoveRotateTransform(OverlayCenter, RelativeRotation);
|
|
|
|
lcMatrix44 WorldMatrix = lcMatrix44(RelativeRotation, OverlayCenter);
|
|
|
|
if (ActiveModel != mModel)
|
|
WorldMatrix = lcMul(WorldMatrix, mActiveSubmodelTransform);
|
|
|
|
const lcVector3 Center = WorldMatrix.GetTranslation();
|
|
|
|
if (mTrackTool == lcTrackTool::MoveX || mTrackTool == lcTrackTool::MoveY || mTrackTool == lcTrackTool::MoveZ)
|
|
{
|
|
lcVector3 Direction;
|
|
if (mTrackTool == lcTrackTool::MoveX)
|
|
Direction = lcVector3(1.0f, 0.0f, 0.0f);
|
|
else if (mTrackTool == lcTrackTool::MoveY)
|
|
Direction = lcVector3(0.0f, 1.0f, 0.0f);
|
|
else
|
|
Direction = lcVector3(0.0f, 0.0f, 1.0f);
|
|
|
|
Direction = lcMul30(Direction, WorldMatrix);
|
|
|
|
lcVector3 Intersection;
|
|
lcClosestPointsBetweenLines(Center, Center + Direction, CurrentStart, CurrentEnd, &Intersection, nullptr);
|
|
|
|
lcVector3 MoveStart;
|
|
lcClosestPointsBetweenLines(Center, Center + Direction, MouseDownStart, MouseDownEnd, &MoveStart, nullptr);
|
|
|
|
lcVector3 Distance = Intersection - MoveStart;
|
|
Distance = lcMul(Distance, lcMatrix33AffineInverse(lcMatrix33(WorldMatrix)));
|
|
ActiveModel->UpdateMoveTool(Distance, true, mTrackButton != lcTrackButton::Left);
|
|
}
|
|
else if (mTrackTool == lcTrackTool::MoveXY || mTrackTool == lcTrackTool::MoveXZ || mTrackTool == lcTrackTool::MoveYZ)
|
|
{
|
|
lcVector3 PlaneNormal;
|
|
|
|
if (mTrackTool == lcTrackTool::MoveXY)
|
|
PlaneNormal = lcVector3(0.0f, 0.0f, 1.0f);
|
|
else if (mTrackTool == lcTrackTool::MoveXZ)
|
|
PlaneNormal = lcVector3(0.0f, 1.0f, 0.0f);
|
|
else
|
|
PlaneNormal = lcVector3(1.0f, 0.0f, 0.0f);
|
|
|
|
PlaneNormal = lcMul30(PlaneNormal, WorldMatrix);
|
|
lcVector4 Plane(PlaneNormal, -lcDot(PlaneNormal, Center));
|
|
lcVector3 Intersection;
|
|
|
|
if (lcLineSegmentPlaneIntersection(&Intersection, CurrentStart, CurrentEnd, Plane))
|
|
{
|
|
lcVector3 MoveStart;
|
|
|
|
if (lcLineSegmentPlaneIntersection(&MoveStart, MouseDownStart, MouseDownEnd, Plane))
|
|
{
|
|
lcVector3 Distance = Intersection - MoveStart;
|
|
Distance = lcMul(Distance, lcMatrix33AffineInverse(lcMatrix33(WorldMatrix)));
|
|
ActiveModel->UpdateMoveTool(Distance, true, mTrackButton != lcTrackButton::Left);
|
|
}
|
|
}
|
|
}
|
|
else if (mTrackTool == lcTrackTool::MoveXYZ && mMouseDownPiece)
|
|
{
|
|
if (mMouseDownPiece->mPieceInfo->GetTrainTrackInfo())
|
|
ActiveModel->UpdateSelectedPiecesTrainTrackConnections();
|
|
|
|
auto [PieceInfoTransforms, IsConnection] = GetMouseInsertPieceInfo(true, false, mMouseDownPiece->mPieceInfo, mMouseDownPiece);
|
|
|
|
if (!PieceInfoTransforms.empty())
|
|
{
|
|
ActiveModel->UpdateFreeMoveTool(mMouseDownPiece, mMouseDownTransform, PieceInfoTransforms.back().Transform, IsConnection, mTrackButton != lcTrackButton::Left);
|
|
}
|
|
}
|
|
else if (mTrackTool == lcTrackTool::ScalePlus || mTrackTool == lcTrackTool::ScaleMinus)
|
|
{
|
|
lcVector3 Direction;
|
|
if (mTrackTool == lcTrackTool::ScalePlus)
|
|
Direction = lcVector3(1.0f, 0.0f, 0.0f);
|
|
else
|
|
Direction = lcVector3(-1.0f, 0.0f, 0.0f);
|
|
|
|
Direction = lcMul30(Direction, WorldMatrix);
|
|
|
|
lcVector3 Intersection;
|
|
lcClosestPointsBetweenLines(Center, Center + Direction, CurrentStart, CurrentEnd, &Intersection, nullptr);
|
|
|
|
lcObject* Focus = ActiveModel->GetFocusObject();
|
|
if (Focus && Focus->IsPiece())
|
|
{
|
|
lcPiece* Piece = (lcPiece*)Focus;
|
|
quint32 Section = Piece->GetFocusSection();
|
|
|
|
if (Section >= LC_PIECE_SECTION_CONTROL_POINT_FIRST && Section <= LC_PIECE_SECTION_CONTROL_POINT_LAST)
|
|
{
|
|
const float ScaleMax = 200.0f;
|
|
const float OverlayScale = GetOverlayScale();
|
|
const float ScaleStart = 2.0f * OverlayScale;
|
|
|
|
lcVector3 Position = Piece->GetSectionPosition(Section);
|
|
lcVector3 Start = Position + Direction * ScaleStart;
|
|
|
|
float Distance = lcLength(Intersection - Start);
|
|
if (lcDot(Direction, Intersection - Start) < 0.0f)
|
|
Distance = 0.0f;
|
|
|
|
float Scale = lcClamp(Distance, 0.1f, ScaleMax);
|
|
|
|
ActiveModel->UpdateScaleTool(Scale);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lcVector3 PlaneNormal = lcNormalize(mCamera->mTargetPosition - mCamera->mPosition);
|
|
lcVector4 Plane(PlaneNormal, -lcDot(PlaneNormal, Center));
|
|
lcVector3 Intersection;
|
|
|
|
if (lcLineSegmentPlaneIntersection(&Intersection, CurrentStart, CurrentEnd, Plane))
|
|
{
|
|
lcVector3 MoveStart;
|
|
|
|
if (lcLineSegmentPlaneIntersection(&MoveStart, MouseDownStart, MouseDownEnd, Plane))
|
|
{
|
|
lcVector3 Distance = Intersection - MoveStart;
|
|
ActiveModel->UpdateMoveTool(Distance, true, mTrackButton != lcTrackButton::Left);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case lcTrackTool::RotateX:
|
|
case lcTrackTool::RotateY:
|
|
case lcTrackTool::RotateZ:
|
|
{
|
|
lcVector3 OverlayCenter;
|
|
lcMatrix33 RelativeRotation;
|
|
ActiveModel->GetMoveRotateTransform(OverlayCenter, RelativeRotation);
|
|
|
|
lcMatrix44 WorldMatrix = lcMatrix44(RelativeRotation, OverlayCenter);
|
|
if (ActiveModel != mModel)
|
|
WorldMatrix = lcMul(WorldMatrix, mActiveSubmodelTransform);
|
|
|
|
const lcVector3 ScreenCenter = ProjectPoint(WorldMatrix.GetTranslation());
|
|
const float MouseAngle = atan2f((float)mMouseY - ScreenCenter[1], (float)mMouseX - ScreenCenter[0]);
|
|
float Angle = mCameraRotationLastMouseAngle - MouseAngle;
|
|
|
|
if (Angle > LC_PI)
|
|
Angle -= LC_2PI;
|
|
else if (Angle < -LC_PI)
|
|
Angle += LC_2PI;
|
|
|
|
mCameraRotationAngle += Angle;
|
|
mCameraRotationLastMouseAngle = MouseAngle;
|
|
|
|
lcVector3 Axis;
|
|
|
|
switch (mTrackTool)
|
|
{
|
|
case lcTrackTool::RotateX:
|
|
Axis = lcVector3(1.0f, 0.0f, 0.0f);
|
|
break;
|
|
case lcTrackTool::RotateY:
|
|
Axis = lcVector3(0.0f, 1.0f, 0.0f);
|
|
break;
|
|
case lcTrackTool::RotateZ:
|
|
Axis = lcVector3(0.0f, 0.0f, 1.0f);
|
|
break;
|
|
default:
|
|
Axis = lcVector3(0.0f, 0.0f, 1.0f);
|
|
break;
|
|
}
|
|
|
|
// The rings rotate with the current gizmo frame, so derive the displayed
|
|
// axis from the current transform before determining its screen direction.
|
|
lcVector3 DisplayAxis = lcNormalize(lcMul30(Axis, WorldMatrix));
|
|
if (WorldMatrix.Determinant() < 0.0f)
|
|
DisplayAxis = -DisplayAxis;
|
|
|
|
const lcVector3 CameraAxis = lcNormalize(mCamera->mTargetPosition - mCamera->mPosition);
|
|
const float AxisDirection = lcDot(DisplayAxis, CameraAxis) < 0.0f ? -1.0f : 1.0f;
|
|
ActiveModel->UpdateRotateTool(Axis, AxisDirection * mCameraRotationAngle * LC_RTOD, mTrackButton != lcTrackButton::Left, true);
|
|
}
|
|
break;
|
|
|
|
case lcTrackTool::RotateTrackBall:
|
|
{
|
|
const lcVector3 CurrentVector = GetTrackballPoint(mMouseX, mMouseY);
|
|
const lcVector3 LocalAxis = lcCross(mTrackballVector, CurrentVector);
|
|
const float AxisLength = lcLength(LocalAxis);
|
|
|
|
if (AxisLength > 0.0001f)
|
|
{
|
|
const float Angle = atan2f(AxisLength, lcClamp(lcDot(mTrackballVector, CurrentVector), -1.0f, 1.0f));
|
|
const lcMatrix44 CameraWorldMatrix = lcMatrix44AffineInverse(mCamera->mWorldView);
|
|
lcVector3 RotationAxis = lcNormalize(lcMul30(LocalAxis, CameraWorldMatrix));
|
|
if (ActiveModel != mModel)
|
|
{
|
|
const lcMatrix44 ActiveSubmodelInverse = lcMatrix44AffineInverse(mActiveSubmodelTransform);
|
|
RotationAxis = lcNormalize(lcMul30(RotationAxis, ActiveSubmodelInverse));
|
|
|
|
if (ActiveSubmodelInverse.Determinant() < 0.0f)
|
|
RotationAxis = -RotationAxis;
|
|
}
|
|
|
|
const lcMatrix33 IncrementalRotation = lcMatrix33FromAxisAngle(RotationAxis, Angle);
|
|
|
|
const bool Snapping = gMainWindow->GetAngleSnap() != 0.0f;
|
|
if (Snapping != mTrackballSnapping)
|
|
{
|
|
if (!Snapping && mTrackballHasPendingRotation)
|
|
ActiveModel->UpdateRotateTool(mTrackballPendingRotation, mTrackButton != lcTrackButton::Left, false);
|
|
|
|
mTrackballPendingRotation = lcMatrix33Identity();
|
|
mTrackballSnapping = Snapping;
|
|
mTrackballHasPendingRotation = false;
|
|
}
|
|
|
|
if (!Snapping)
|
|
{
|
|
ActiveModel->UpdateRotateTool(IncrementalRotation, mTrackButton != lcTrackButton::Left, false);
|
|
}
|
|
else
|
|
{
|
|
mTrackballPendingRotation = lcMul(mTrackballPendingRotation, IncrementalRotation);
|
|
mTrackballHasPendingRotation = true;
|
|
const lcVector4 AxisAngle = lcMatrix44ToAxisAngle(lcMatrix44(mTrackballPendingRotation, lcVector3(0.0f, 0.0f, 0.0f)));
|
|
const float SnappedAngle = ActiveModel->SnapRotation(lcVector3(AxisAngle[3] * LC_RTOD, 0.0f, 0.0f))[0];
|
|
|
|
if (SnappedAngle != 0.0f)
|
|
{
|
|
const lcMatrix33 SnappedRotation = lcMatrix33FromAxisAngle(lcVector3(AxisAngle), SnappedAngle * LC_DTOR);
|
|
ActiveModel->UpdateRotateTool(SnappedRotation, mTrackButton != lcTrackButton::Left, false);
|
|
mTrackballPendingRotation = lcMul(lcMatrix33AffineInverse(SnappedRotation), mTrackballPendingRotation);
|
|
mTrackballHasPendingRotation = lcMatrix44ToAxisAngle(lcMatrix44(mTrackballPendingRotation, lcVector3(0.0f, 0.0f, 0.0f)))[3] > 0.000001f;
|
|
}
|
|
}
|
|
}
|
|
|
|
mTrackballVector = CurrentVector;
|
|
}
|
|
break;
|
|
|
|
case lcTrackTool::RotateCamera:
|
|
{
|
|
lcVector3 OverlayCenter;
|
|
lcMatrix33 RelativeRotation;
|
|
ActiveModel->GetMoveRotateTransform(OverlayCenter, RelativeRotation);
|
|
|
|
lcMatrix44 WorldMatrix = lcMatrix44(RelativeRotation, OverlayCenter);
|
|
if (ActiveModel != mModel)
|
|
WorldMatrix = lcMul(WorldMatrix, mActiveSubmodelTransform);
|
|
|
|
const lcVector3 ScreenCenter = ProjectPoint(WorldMatrix.GetTranslation());
|
|
const float MouseAngle = atan2f((float)mMouseY - ScreenCenter[1], (float)mMouseX - ScreenCenter[0]);
|
|
// Unwrap each clockwise-positive pointer delta so full turns do not flip
|
|
// when the pointer crosses the opposite side of the ring.
|
|
float Angle = mCameraRotationLastMouseAngle - MouseAngle;
|
|
|
|
if (Angle > LC_PI)
|
|
Angle -= LC_2PI;
|
|
else if (Angle < -LC_PI)
|
|
Angle += LC_2PI;
|
|
|
|
mCameraRotationAngle += Angle;
|
|
mCameraRotationLastMouseAngle = MouseAngle;
|
|
|
|
lcVector3 CameraAxis = lcNormalize(mCamera->mTargetPosition - mCamera->mPosition);
|
|
if (ActiveModel != mModel)
|
|
{
|
|
const lcMatrix44 ActiveSubmodelInverse = lcMatrix44AffineInverse(mActiveSubmodelTransform);
|
|
CameraAxis = lcNormalize(lcMul30(CameraAxis, ActiveSubmodelInverse));
|
|
|
|
if (ActiveSubmodelInverse.Determinant() < 0.0f)
|
|
CameraAxis = -CameraAxis;
|
|
}
|
|
|
|
ActiveModel->UpdateRotateTool(CameraAxis, mCameraRotationAngle * LC_RTOD, mTrackButton != lcTrackButton::Left, false);
|
|
}
|
|
break;
|
|
|
|
case lcTrackTool::RotateTrainTrackRight:
|
|
case lcTrackTool::RotateTrainTrackLeft:
|
|
case lcTrackTool::InsertTrainTrack:
|
|
case lcTrackTool::SelectTrainTrack:
|
|
case lcTrackTool::Eraser:
|
|
break;
|
|
|
|
case lcTrackTool::Paint:
|
|
ActiveModel->PaintToolClicked(FindObjectUnderPointer(true, false).Object);
|
|
break;
|
|
|
|
case lcTrackTool::ColorPicker:
|
|
break;
|
|
|
|
case lcTrackTool::Zoom:
|
|
ActiveModel->UpdateZoomTool(mCamera, 2.0f * MouseSensitivity * (mMouseY - mMouseDownY));
|
|
break;
|
|
|
|
case lcTrackTool::Pan:
|
|
UpdatePan(mMouseX, mMouseY);
|
|
break;
|
|
|
|
case lcTrackTool::OrbitX:
|
|
ActiveModel->UpdateOrbitTool(mCamera, 0.1f * MouseSensitivity * (mMouseX - mMouseDownX), 0.0f);
|
|
break;
|
|
|
|
case lcTrackTool::OrbitY:
|
|
ActiveModel->UpdateOrbitTool(mCamera, 0.0f, 0.1f * MouseSensitivity * (mMouseY - mMouseDownY));
|
|
break;
|
|
|
|
case lcTrackTool::OrbitXY:
|
|
ActiveModel->UpdateOrbitTool(mCamera, 0.1f * MouseSensitivity * (mMouseX - mMouseDownX), 0.1f * MouseSensitivity * (mMouseY - mMouseDownY));
|
|
break;
|
|
|
|
case lcTrackTool::Roll:
|
|
ActiveModel->UpdateRollTool(mCamera, 2.0f * MouseSensitivity * (mMouseX - mMouseDownX) * LC_DTOR);
|
|
break;
|
|
|
|
case lcTrackTool::ZoomRegion:
|
|
Redraw();
|
|
break;
|
|
|
|
case lcTrackTool::Count:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void lcView::OnMouseLeave()
|
|
{
|
|
if (mTrackTool == lcTrackTool::Insert)
|
|
{
|
|
mTrackTool = lcTrackTool::None;
|
|
|
|
UpdateCursor();
|
|
|
|
lcModel* ActiveModel = GetActiveModel();
|
|
|
|
if (ActiveModel)
|
|
ActiveModel->SetPreviewInsertPieceInfo(std::vector<lcInsertPieceInfo>());
|
|
}
|
|
}
|
|
|
|
void lcView::OnMouseWheel(float Direction)
|
|
{
|
|
float Scale = 10.0f;
|
|
|
|
if (mMouseModifiers & Qt::ControlModifier)
|
|
Scale = 100.0f;
|
|
else if (mMouseModifiers & Qt::ShiftModifier)
|
|
Scale = 1.0f;
|
|
|
|
mModel->Zoom(mCamera, static_cast<int>(Direction * Scale));
|
|
}
|