leocad/common/lc_previewwidget.cpp

659 lines
16 KiB
C++
Raw Normal View History

#include "QMessageBox"
#include "lc_global.h"
#include "lc_previewwidget.h"
#include "pieceinf.h"
#include "piece.h"
#include "project.h"
#include "lc_model.h"
#include "camera.h"
#include "texfont.h"
#include "lc_library.h"
#include "lc_qglwidget.h"
lcPreviewWidget* gPreviewWidget;
lcPreviewDockWidget::lcPreviewDockWidget(QMainWindow* Parent)
: QMainWindow(Parent)
{
mPreview = new lcPreviewWidget();
mViewWidget = new lcQGLWidget(nullptr, mPreview, true/*IsView*/, true/*IsPreview*/);
setCentralWidget(mViewWidget);
setMinimumSize(200, 200);
2020-10-07 13:42:43 +02:00
mLockAction = new QAction(QIcon(":/resources/action_preview_unlocked.png"),tr("Lock Preview"), this);
mLockAction->setCheckable(true);
mLockAction->setChecked(false);
mLockAction->setShortcut(tr("Ctrl+L"));
connect(mLockAction, SIGNAL(triggered()), this, SLOT(SetPreviewLock()));
SetPreviewLock();
mLabel = new QLabel(QString());
mToolBar = addToolBar(tr("PreviewDescription"));
mToolBar->setObjectName("PreviewDescription");
mToolBar->setMovable(false);
2020-10-07 13:42:43 +02:00
mToolBar->addAction(mLockAction);
mToolBar->addSeparator();
mToolBar->addWidget(mLabel);
}
bool lcPreviewDockWidget::SetCurrentPiece(const QString& PartType, int ColorCode)
{
2020-10-07 13:42:43 +02:00
if (mLockAction->isChecked())
return true;
mLabel->setText(tr("Loading..."));
if (mPreview->SetCurrentPiece(PartType, ColorCode))
{
mLabel->setText(mPreview->GetDescription());
return true;
}
return false;
}
void lcPreviewDockWidget::ClearPreview()
{
mPreview->ClearPreview();
mLabel->setText(QString());
}
2020-10-07 13:42:43 +02:00
void lcPreviewDockWidget::SetPreviewLock()
{
bool Locked = mLockAction->isChecked();
if (Locked && mPreview->GetActiveModel()->GetPieces().IsEmpty())
{
mLockAction->setChecked(false);
return;
}
QIcon LockIcon(Locked ? ":/resources/action_preview_locked.png" : ":/resources/action_preview_unlocked.png");
QString State(Locked ? tr("Unlock") : tr("Lock"));
QString StatusTip(tr("%1 the preview display to %2 updates").arg(State).arg(Locked ? "enable" : "disable"));
mLockAction->setToolTip(tr("%1 Preview").arg(State));
mLockAction->setIcon(LockIcon);
mLockAction->setStatusTip(StatusTip);
}
lcPreviewWidget::lcPreviewWidget()
: mLoader(new Project(true/*IsPreview*/)),
mViewSphere(this/*Preview*/)
{
mTool = LC_TOOL_SELECT;
mTrackTool = LC_TRACKTOOL_NONE;
mTrackButton = lcTrackButton::None;
mLoader->SetActiveModel(0);
mModel = mLoader->GetActiveModel();
mCamera = nullptr;
SetDefaultCamera();
}
lcPreviewWidget::~lcPreviewWidget()
{
if (mCamera && mCamera->IsSimple())
delete mCamera;
delete mLoader;
}
bool lcPreviewWidget::SetCurrentPiece(const QString& PartType, int ColorCode)
{
lcPiecesLibrary* Library = lcGetPiecesLibrary();
PieceInfo* Info = Library->FindPiece(PartType.toLatin1().constData(), nullptr, false, false);
if (Info)
{
mIsModel = Info->IsModel();
mDescription = Info->m_strDescription;
lcModel* ActiveModel = GetActiveModel();
ActiveModel->SelectAllPieces();
ActiveModel->DeleteSelectedObjects();
Library->LoadPieceInfo(Info, false, true);
Library->WaitForLoadQueue();
lcStep CurrentStep = 1;
lcPiece* Piece = new lcPiece(nullptr);
Piece->SetPieceInfo(Info, PartType, false);
Piece->Initialize(lcMatrix44Identity(), CurrentStep);
Piece->SetColorCode(ColorCode);
ActiveModel->SetPreviewPiece(Piece);
}
else
{
QString ModelPath = QString("%1/%2").arg(QDir::currentPath()).arg(PartType);
if (!mLoader->Load(ModelPath))
{
QMessageBox::warning(nullptr, QMessageBox::tr("Error"), QMessageBox::tr("Failed to load '%1'.").arg(ModelPath));
return false;
}
mLoader->SetActiveModel(0);
lcGetPiecesLibrary()->RemoveTemporaryPieces();
mModel = mLoader->GetActiveModel();
if (!mModel->GetProperties().mDescription.isEmpty())
mDescription = mModel->GetProperties().mDescription;
else
mDescription = PartType;
mIsModel = true;
}
ZoomExtents();
return true;
}
void lcPreviewWidget::ClearPreview()
{
mLoader = new Project(true/*IsPreview*/);
mLoader->SetActiveModel(0);
mModel = mLoader->GetActiveModel();
lcGetPiecesLibrary()->UnloadUnusedParts();
Redraw();
}
void lcPreviewWidget::SetDefaultCamera()
{
if (!mCamera || !mCamera->IsSimple())
mCamera = new lcCamera(true);
mCamera->SetViewpoint(LC_VIEWPOINT_HOME);
}
void lcPreviewWidget::SetCamera(lcCamera* Camera) // called by lcModel::DeleteModel()
{
if (!mCamera || !mCamera->IsSimple())
mCamera = new lcCamera(true);
mCamera->CopyPosition(Camera);
}
lcModel* lcPreviewWidget::GetActiveModel() const
{
return mModel;
}
void lcPreviewWidget::ZoomExtents()
{
lcModel* ActiveModel = GetActiveModel();
if (ActiveModel)
{
ActiveModel->ZoomExtents(mCamera, float(mWidth) / float(mHeight));
Redraw();
}
}
lcMatrix44 lcPreviewWidget::GetProjectionMatrix() const
{
float AspectRatio = (float)mWidth / (float)mHeight;
if (mCamera->IsOrtho())
{
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);
}
void lcPreviewWidget::StartOrbitTracking() // called by viewSphere
{
mTrackTool = LC_TRACKTOOL_ORBIT_XY;
OnUpdateCursor();
OnButtonDown(lcTrackButton::Left);
}
void lcPreviewWidget::SetViewpoint(const lcVector3& Position)
{
if (!mCamera || !mCamera->IsSimple())
{
lcCamera* OldCamera = mCamera;
mCamera = new lcCamera(true);
if (OldCamera)
mCamera->CopySettings(OldCamera);
}
mCamera->SetViewpoint(Position);
Redraw();
}
void lcPreviewWidget::DrawViewport()
{
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->SetDepthWrite(false);
glDisable(GL_DEPTH_TEST);
if (true/*we have an active view*/)
{
mContext->SetMaterial(lcMaterialType::UnlitColor);
mContext->SetColor(lcVector4FromColor(lcGetPreferences().mPreviewActiveColor));
float Verts[8] = { 0.0f, 0.0f, mWidth - 1.0f, 0.0f, mWidth - 1.0f, mHeight - 1.0f, 0.0f, mHeight - 1.0f };
mContext->SetVertexBufferPointer(Verts);
mContext->SetVertexFormatPosition(2);
mContext->DrawPrimitives(GL_LINE_LOOP, 0, 4);
}
mContext->SetDepthWrite(true);
glEnable(GL_DEPTH_TEST);
}
void lcPreviewWidget::DrawAxes()
{
// glClear(GL_DEPTH_BUFFER_BIT);
const float Verts[28 * 3] =
{
0.00f, 0.00f, 0.00f, 20.00f, 0.00f, 0.00f, 12.00f, 3.00f, 0.00f, 12.00f, 2.12f, 2.12f,
12.00f, 0.00f, 3.00f, 12.00f, -2.12f, 2.12f, 12.00f, -3.00f, 0.00f, 12.00f, -2.12f, -2.12f,
12.00f, 0.00f, -3.00f, 12.00f, 2.12f, -2.12f, 0.00f, 20.00f, 0.00f, 3.00f, 12.00f, 0.00f,
2.12f, 12.00f, 2.12f, 0.00f, 12.00f, 3.00f, -2.12f, 12.00f, 2.12f, -3.00f, 12.00f, 0.00f,
-2.12f, 12.00f, -2.12f, 0.00f, 12.00f, -3.00f, 2.12f, 12.00f, -2.12f, 0.00f, 0.00f, 20.00f,
0.00f, 3.00f, 12.00f, 2.12f, 2.12f, 12.00f, 3.00f, 0.00f, 12.00f, 2.12f, -2.12f, 12.00f,
0.00f, -3.00f, 12.00f, -2.12f, -2.12f, 12.00f, -3.00f, 0.00f, 12.00f, -2.12f, 2.12f, 12.00f,
};
const GLushort Indices[78] =
{
0, 1, 0, 10, 0, 19,
1, 2, 3, 1, 3, 4, 1, 4, 5, 1, 5, 6, 1, 6, 7, 1, 7, 8, 1, 8, 9, 1, 9, 2,
10, 11, 12, 10, 12, 13, 10, 13, 14, 10, 14, 15, 10, 15, 16, 10, 16, 17, 10, 17, 18, 10, 18, 11,
19, 20, 21, 19, 21, 22, 19, 22, 23, 19, 23, 24, 19, 24, 25, 19, 25, 26, 19, 26, 27, 19, 27, 20
};
lcMatrix44 TranslationMatrix = lcMatrix44Translation(lcVector3(30.375f, 30.375f, 0.0f));
lcMatrix44 WorldViewMatrix = mCamera->mWorldView;
WorldViewMatrix.SetTranslation(lcVector3(0, 0, 0));
mContext->SetMaterial(lcMaterialType::UnlitColor);
mContext->SetWorldMatrix(lcMatrix44Identity());
mContext->SetViewMatrix(lcMul(WorldViewMatrix, TranslationMatrix));
mContext->SetProjectionMatrix(lcMatrix44Ortho(0, mWidth, 0, mHeight, -50, 50));
mContext->SetVertexBufferPointer(Verts);
mContext->SetVertexFormatPosition(3);
mContext->SetIndexBufferPointer(Indices);
mContext->SetColor(0.0f, 0.0f, 0.0f, 1.0f);
mContext->DrawIndexedPrimitives(GL_LINES, 6, GL_UNSIGNED_SHORT, 0);
mContext->SetColor(0.8f, 0.0f, 0.0f, 1.0f);
mContext->DrawIndexedPrimitives(GL_TRIANGLES, 24, GL_UNSIGNED_SHORT, 6 * 2);
mContext->SetColor(0.0f, 0.8f, 0.0f, 1.0f);
mContext->DrawIndexedPrimitives(GL_TRIANGLES, 24, GL_UNSIGNED_SHORT, (6 + 24) * 2);
mContext->SetColor(0.0f, 0.0f, 0.8f, 1.0f);
mContext->DrawIndexedPrimitives(GL_TRIANGLES, 24, GL_UNSIGNED_SHORT, (6 + 48) * 2);
mContext->SetMaterial(lcMaterialType::UnlitTextureModulate);
mContext->SetViewMatrix(TranslationMatrix);
mContext->BindTexture2D(gTexFont.GetTexture());
glEnable(GL_BLEND);
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().mAxesColor));
mContext->DrawPrimitives(GL_TRIANGLES, 0, 6 * 3);
glDisable(GL_BLEND);
}
lcTool lcPreviewWidget::GetCurrentTool() const
{
const lcTool ToolFromTrackTool[] =
{
LC_TOOL_SELECT, // LC_TRACKTOOL_NONE
LC_TOOL_PAN, // LC_TRACKTOOL_PAN
LC_TOOL_ROTATE_VIEW, // LC_TRACKTOOL_ORBIT_XY
};
return ToolFromTrackTool[mTrackTool];
}
void lcPreviewWidget::StartTracking(lcTrackButton TrackButton)
{
mTrackButton = TrackButton;
mTrackUpdated = false;
mMouseDownX = mInputState.x;
mMouseDownY = mInputState.y;
lcTool Tool = GetCurrentTool(); // Either LC_TRACKTOOL_NONE (LC_TOOL_SELECT) or LC_TRACKTOOL_ORBIT_XY (LC_TOOL_ROTATE_VIEW)
lcModel* ActiveModel = GetActiveModel();
switch (Tool)
{
case LC_TOOL_SELECT:
break;
case LC_TOOL_PAN:
case LC_TOOL_ROTATE_VIEW:
ActiveModel->BeginMouseTool();
break;
case LC_NUM_TOOLS:
default:
break;
}
OnUpdateCursor();
}
void lcPreviewWidget::StopTracking(bool Accept)
{
if (mTrackButton == lcTrackButton::None)
return;
lcTool Tool = GetCurrentTool(); // Either LC_TRACKTOOL_NONE (LC_TOOL_SELECT) or LC_TRACKTOOL_ORBIT_XY (LC_TOOL_ROTATE_VIEW)
lcModel* ActiveModel = GetActiveModel();
switch (Tool)
{
case LC_TOOL_SELECT:
break;
case LC_TOOL_PAN:
case LC_TOOL_ROTATE_VIEW:
ActiveModel->EndMouseTool(Tool, Accept);
break;
case LC_NUM_TOOLS:
default:
break;
}
mTrackButton = lcTrackButton::None;
mTrackTool = LC_TRACKTOOL_NONE;
OnUpdateCursor();
}
void lcPreviewWidget::OnButtonDown(lcTrackButton TrackButton)
{
switch (mTrackTool)
{
case LC_TRACKTOOL_NONE:
break;
case LC_TRACKTOOL_PAN:
StartTracking(TrackButton);
break;
case LC_TRACKTOOL_ORBIT_XY:
StartTracking(TrackButton);
break;
case LC_TRACKTOOL_COUNT:
break;
}
}
lcCursor lcPreviewWidget::GetCursor() const
{
const lcCursor CursorFromTrackTool[] =
{
lcCursor::Select, // LC_TRACKTOOL_NONE
lcCursor::Pan, // LC_TRACKTOOL_PAN
lcCursor::RotateView, // LC_TRACKTOOL_ORBIT_XY
};
static_assert(LC_ARRAY_COUNT(CursorFromTrackTool) == LC_TRACKTOOL_COUNT, "Tracktool array size mismatch.");
if (mTrackTool >= 0 && mTrackTool < LC_ARRAY_COUNT(CursorFromTrackTool))
return CursorFromTrackTool[mTrackTool];
return lcCursor::Select;
}
void lcPreviewWidget::OnInitialUpdate()
{
MakeCurrent();
mContext->SetDefaultState();
}
void lcPreviewWidget::OnDraw()
{
if (!mModel)
return;
lcPreferences& Preferences = lcGetPreferences();
const bool DrawInterface = mWidget != nullptr;
mScene.SetAllowLOD(Preferences.mAllowLOD && mWidget != nullptr);
mScene.SetLODDistance(Preferences.mMeshLODDistance);
mScene.Begin(mCamera->mWorldView);
mScene.SetDrawInterface(DrawInterface);
mModel->GetScene(mScene, mCamera, false /*HighlightNewParts*/, false/*mFadeSteps*/);
mScene.End();
mContext->SetDefaultState();
mContext->SetViewport(0, 0, mWidth, mHeight);
mModel->DrawBackground(this);
mContext->SetProjectionMatrix(GetProjectionMatrix());
mContext->SetLineWidth(Preferences.mLineWidth);
mScene.Draw(mContext);
if (DrawInterface)
{
mContext->SetLineWidth(1.0f);
if (Preferences.mDrawPreviewAxis)
DrawAxes();
if (Preferences.mDrawPreviewViewSphere)
mViewSphere.Draw();
DrawViewport();
}
mContext->ClearResources();
}
void lcPreviewWidget::OnUpdateCursor()
{
SetCursor(GetCursor());
}
void lcPreviewWidget::OnLeftButtonDown()
{
if (mTrackButton != lcTrackButton::None)
{
StopTracking(false);
return;
}
if (mViewSphere.OnLeftButtonDown())
return;
lcTrackTool OverrideTool = LC_TRACKTOOL_ORBIT_XY;
if (OverrideTool != LC_TRACKTOOL_NONE)
{
mTrackTool = OverrideTool;
OnUpdateCursor();
}
OnButtonDown(lcTrackButton::Left);
}
void lcPreviewWidget::OnLeftButtonUp()
{
StopTracking(mTrackButton == lcTrackButton::Left);
if (mViewSphere.OnLeftButtonUp()) {
ZoomExtents();
return;
}
}
void lcPreviewWidget::OnMiddleButtonDown()
{
if (mTrackButton != lcTrackButton::None)
{
StopTracking(false);
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0))
lcTrackTool OverrideTool = LC_TRACKTOOL_NONE;
if (OverrideTool != LC_TRACKTOOL_NONE)
{
mTrackTool = OverrideTool;
OnUpdateCursor();
}
#endif
OnButtonDown(lcTrackButton::Middle);
}
void lcPreviewWidget::OnMiddleButtonUp()
{
StopTracking(mTrackButton == lcTrackButton::Middle);
}
void lcPreviewWidget::OnLeftButtonDoubleClick()
{
ZoomExtents();
Redraw();
}
void lcPreviewWidget::OnRightButtonDown()
{
if (mTrackButton != lcTrackButton::None)
{
StopTracking(false);
return;
}
lcTrackTool OverrideTool = LC_TRACKTOOL_PAN;
if (OverrideTool != LC_TRACKTOOL_NONE)
{
mTrackTool = OverrideTool;
OnUpdateCursor();
}
OnButtonDown(lcTrackButton::Middle);
}
void lcPreviewWidget::OnRightButtonUp()
{
if (mTrackButton != lcTrackButton::None)
StopTracking(mTrackButton == lcTrackButton::Right);
}
void lcPreviewWidget::OnMouseMove()
{
lcModel* ActiveModel = GetActiveModel();
if (!ActiveModel)
return;
if (mTrackButton == lcTrackButton::None)
{
if (mViewSphere.OnMouseMove())
{
lcTrackTool NewTrackTool = mViewSphere.IsDragging() ? LC_TRACKTOOL_ORBIT_XY : LC_TRACKTOOL_NONE;
if (NewTrackTool != mTrackTool)
{
mTrackTool = NewTrackTool;
OnUpdateCursor();
}
return;
}
return;
}
mTrackUpdated = true;
const float MouseSensitivity = 0.5f / (21.0f - lcGetPreferences().mMouseSensitivity);
switch (mTrackTool)
{
case LC_TRACKTOOL_NONE:
break;
case LC_TRACKTOOL_PAN:
{
lcVector3 Points[4] =
{
lcVector3((float)mInputState.x, (float)mInputState.y, 0.0f),
lcVector3((float)mInputState.x, (float)mInputState.y, 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 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))
break;
}
ActiveModel->UpdatePanTool(mCamera, MoveStart - Intersection);
Redraw();
}
break;
case LC_TRACKTOOL_ORBIT_XY:
ActiveModel->UpdateOrbitTool(mCamera, 0.1f * MouseSensitivity * (mInputState.x - mMouseDownX), 0.1f * MouseSensitivity * (mInputState.y - mMouseDownY));
Redraw();
break;
case LC_TRACKTOOL_COUNT:
break;
}
}
void lcPreviewWidget::OnMouseWheel(float Direction)
{
mModel->Zoom(mCamera, (int)(((mInputState.Modifiers & Qt::ControlModifier) ? 100 : 10) * Direction));
Redraw();
}