#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) { Preview = new lcPreviewWidget(); ViewWidget = new lcQGLWidget(nullptr, Preview, true/*IsView*/, true/*IsPreview*/); setCentralWidget(ViewWidget); setMinimumSize(200, 200); ToolBar = addToolBar(tr("PreviewDescription")); ToolBar->setObjectName("PreviewDescription"); ToolBar->setMovable(false); Label = new QLabel(""); ToolBar->addWidget(Label); } bool lcPreviewDockWidget::SetCurrentPiece(const QString &PartType, int ColorCode) { Label->setText("Loading..."); if (Preview->SetCurrentPiece(PartType, ColorCode)) { Label->setText(Preview->GetDescription()); return true; } return false; } void lcPreviewDockWidget::ClearPreview() { Preview->ClearPreview(); Label->setText(""); } lcPreviewWidget::lcPreviewWidget() : mLoader(new Project(true/*IsPreview*/)), mViewSphere(this/*Preview*/), mIsPart(false) { 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; if (mIsPart) { lcPiecesLibrary* Library = lcGetPiecesLibrary(); for (lcPiece* Piece : mModel->GetPieces()) { PieceInfo *Info = Piece->mPieceInfo; Library->ReleasePieceInfo(Info); } } } bool lcPreviewWidget::SetCurrentPiece(const QString &PartType, int ColorCode) { lcPiecesLibrary *Library = lcGetPiecesLibrary(); PieceInfo* Info = Library->FindPiece(PartType.toLatin1().constData(), nullptr, false, false); if (Info) { mIsPart = true; mDescription = Info->m_strDescription; lcModel* ActiveModel = GetActiveModel(); ActiveModel->SelectAllPieces(); ActiveModel->DeleteSelectedObjects(); Library->LoadPieceInfo(Info, false, true); Library->WaitForLoadQueue(); float* Matrix = lcMatrix44Identity();; lcMatrix44 Transform(lcVector4( Matrix[0], Matrix[2], -Matrix[1], 0.0f), lcVector4(Matrix[8], Matrix[10], -Matrix[9], 0.0f), lcVector4(-Matrix[4], -Matrix[6], Matrix[5], 0.0f), lcVector4(Matrix[12], Matrix[14], -Matrix[13], 1.0f)); int CurrentStep = 1; lcPiece* Piece = new lcPiece(nullptr); Piece->SetPieceInfo(Info, PartType, false); Piece->Initialize(Transform, CurrentStep); Piece->SetColorCode(ColorCode); ActiveModel->SetPreviewPiece(Piece); Piece = nullptr; } else { QString ModelPath = QString("%1/%2") .arg(QDir::currentPath()) .arg(PartType); if (!mLoader->Load(ModelPath)) { QMessageBox::critical(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; } 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]; /*** Native viewer camera globe mod, switch Y and Z axis with -Y(LC -Z) in the up direction ***/ 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, 'Z', TextBuffer + 5 * 6); lcVector3 PosZ = lcMul30(lcVector3(0.0f, 0.0f, 25.0f), WorldViewMatrix); gTexFont.GetGlyphTriangles(PosZ.x, PosZ.y, PosZ.z, 'Y', TextBuffer + 5 * 6 * 2); /*** Camera globe mod end ***/ 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(); }