diff options
21 files changed, 832 insertions, 72 deletions
diff --git a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h index 1e8d82e632b..b0012f3eb24 100644 --- a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h +++ b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h @@ -18,6 +18,7 @@ public: ActiveSceneChanged, ActiveSplitChanged, RenderModelNodePreviewImage, + Import3DPreviewImage, Import3DSupport, NodeAtPos, BakeLightsProgress, diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index 3fdd7bf6cc5..06df0f79754 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -55,7 +55,8 @@ enum class View3DActionType { EditCameraRotation, EditCameraMove, EditCameraStopAllMoves, - SetLastSceneEnvData + SetLastSceneEnvData, + Import3dUpdatePreviewImage }; constexpr bool isNanotraceEnabled() diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 2d67fa360f7..039918f91c2 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -741,6 +741,8 @@ extend_qtc_plugin(QmlDesigner assetimportupdatetreeitemdelegate.cpp assetimportupdatetreeitemdelegate.h assetimportupdatetreemodel.cpp assetimportupdatetreemodel.h assetimportupdatetreeview.cpp assetimportupdatetreeview.h + import3dcanvas.cpp import3dcanvas.h + import3dconnectionmanager.cpp import3dconnectionmanager.h itemlibrary.qrc itemlibraryconstants.h itemlibraryimageprovider.cpp itemlibraryimageprovider.h diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp new file mode 100644 index 00000000000..87014cbe603 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "import3dcanvas.h" + +#include <QImage> +#include <QLinearGradient> +#include <QPainter> + +namespace QmlDesigner { + +static QImage createGradientImage(int width, int height) { + QImage image(width, height, QImage::Format_ARGB32_Premultiplied); + + QLinearGradient gradient(0, 0, 0, height); + gradient.setColorAt(0, QColor(0x999999)); + gradient.setColorAt(1, QColor(0x222222)); + + QPainter painter(&image); + painter.fillRect(0, 0, width, height, gradient); + + return image; +} + +Import3dCanvas::Import3dCanvas(QWidget *parent) + : QWidget(parent) +{ +} + +void Import3dCanvas::updateRenderImage(const QImage &img) +{ + m_image = img; + update(); +} + +void Import3dCanvas::paintEvent([[maybe_unused]] QPaintEvent *e) +{ + QWidget::paintEvent(e); + + QPainter painter(this); + + if (m_image.isNull()) { + QImage image = createGradientImage(width(), height()); + painter.drawImage(rect(), image, QRect(0, 0, image.width(), image.height())); + } else { + painter.drawImage(rect(), m_image, QRect(0, 0, m_image.width(), m_image.height())); + } +} + +void Import3dCanvas::resizeEvent(QResizeEvent *) +{ + emit requestImageUpdate(); +} + + +} diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h new file mode 100644 index 00000000000..22bcc04d70f --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h @@ -0,0 +1,32 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#pragma once + +#include <QEvent> +#include <QImage> +#include <QPointer> +#include <QWidget> + +namespace QmlDesigner { + +class Import3dCanvas : public QWidget +{ + Q_OBJECT + +public: + Import3dCanvas(QWidget *parent); + + void updateRenderImage(const QImage &img); + +signals: + void requestImageUpdate(); + +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + +private: + QImage m_image; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp new file mode 100644 index 00000000000..4c455e3c6d2 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "import3dconnectionmanager.h" + +#include <imagecontainer.h> +#include <puppettocreatorcommand.h> + +#include <QImage> + +namespace QmlDesigner { + +Import3dConnectionManager::Import3dConnectionManager() +{ + connections().clear(); // Remove default interactive puppets + connections().emplace_back("Import 3D", "import3dmode"); +} + +void Import3dConnectionManager::setPreviewImageCallback(ImageCallback callback) +{ + m_previewImageCallback = std::move(callback); +} + +void Import3dConnectionManager::dispatchCommand(const QVariant &command, + ConnectionManagerInterface::Connection &connection) +{ + static const int commandType = QMetaType::type("PuppetToCreatorCommand"); + + if (command.typeId() == commandType) { + auto cmd = command.value<PuppetToCreatorCommand>(); + switch (cmd.type()) { + case PuppetToCreatorCommand::Import3DPreviewImage: { + ImageContainer container = qvariant_cast<ImageContainer>(cmd.data()); + QImage image = container.image(); + if (!image.isNull()) + m_previewImageCallback(image); + break; + } + default: + break; + } + } else { + InteractiveConnectionManager::dispatchCommand(command, connection); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h new file mode 100644 index 00000000000..458d612ad2d --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h @@ -0,0 +1,28 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "interactiveconnectionmanager.h" + +QT_FORWARD_DECLARE_CLASS(QImage) + +namespace QmlDesigner { + +class Import3dConnectionManager : public InteractiveConnectionManager +{ +public: + using ImageCallback = std::function<void(const QImage &)>; + + Import3dConnectionManager(); + + void setPreviewImageCallback(ImageCallback callback); + +protected: + void dispatchCommand(const QVariant &command, Connection &connection) override; + +private: + ImageCallback m_previewImageCallback; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index 2c690726029..4e49cd2b807 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -4,15 +4,19 @@ #include "itemlibraryassetimportdialog.h" #include "ui_itemlibraryassetimportdialog.h" +#include "import3dcanvas.h" +#include "import3dconnectionmanager.h" + #include <model.h> #include <model/modelutils.h> +#include <nodeinstanceview.h> #include <nodemetainfo.h> #include <qmldesignerconstants.h> #include <qmldesignerplugin.h> +#include <rewriterview.h> #include <variantproperty.h> #include <theme.h> -#include <utils/filepath.h> #include <utils/outputformatter.h> #include <projectexplorer/project.h> @@ -74,9 +78,10 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( const QStringList &importFiles, const QString &defaulTargetDirectory, const QVariantMap &supportedExts, const QVariantMap &supportedOpts, const QJsonObject &defaultOpts, const QSet<QString> &preselectedFilesForOverwrite, - QWidget *parent) + AbstractView *view, QWidget *parent) : QDialog(parent) , ui(new Ui::ItemLibraryAssetImportDialog) + , m_view(view) , m_importer(this) , m_preselectedFilesForOverwrite(preselectedFilesForOverwrite) { @@ -107,11 +112,9 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( if (m_quick3DFiles.size() != importFiles.size()) addWarning("Cannot import 3D and other assets simultaneously. Skipping non-3D assets."); - ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Import")); - connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, - this, &ItemLibraryAssetImportDialog::onImport); + connect(ui->importButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onImport); - ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + ui->importButton->setDefault(true); ui->advancedSettingsButton->setStyleSheet( "QPushButton#advancedSettingsButton {background-color: transparent}"); @@ -210,10 +213,14 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( ui->tabWidget->setCurrentIndex(0); } - connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, + connect(ui->closeButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onClose); + connect(ui->acceptButton, &QPushButton::clicked, + this, &ItemLibraryAssetImportDialog::onAccept); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &ItemLibraryAssetImportDialog::updateUi); + connect(canvas(), &Import3dCanvas::requestImageUpdate, + this, &ItemLibraryAssetImportDialog::onRequestImageUpdate); connect(&m_importer, &ItemLibraryAssetImporter::errorReported, this, &ItemLibraryAssetImportDialog::addError); @@ -227,6 +234,8 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( this, &ItemLibraryAssetImportDialog::onImportFinished); connect(&m_importer, &ItemLibraryAssetImporter::progressChanged, this, &ItemLibraryAssetImportDialog::setImportProgress); + connect(&m_importer, &ItemLibraryAssetImporter::importReadyForPreview, + this, &ItemLibraryAssetImportDialog::onImportReadyForPreview); addInfo(tr("Select import options and press \"Import\" to import the following files:")); for (const auto &file : std::as_const(m_quick3DFiles)) @@ -240,10 +249,12 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( ItemLibraryAssetImportDialog::~ItemLibraryAssetImportDialog() { + cleanupPreviewPuppet(); delete ui; } -void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode, +void ItemLibraryAssetImportDialog::updateImport(AbstractView *view, + const ModelNode &updateNode, const QVariantMap &supportedExts, const QVariantMap &supportedOpts) { @@ -332,7 +343,8 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode, {sourceInfo.absoluteFilePath()}, node.model()->fileUrl().toLocalFile(), supportedExts, supportedOpts, options, - preselectedFiles, Core::ICore::dialogParent()); + preselectedFiles, view, + Core::ICore::dialogParent()); importDlg->show(); } else { @@ -829,6 +841,140 @@ bool ItemLibraryAssetImportDialog::isHiddenOption(const QString &id) return hiddenOptions.contains(id); } +void ItemLibraryAssetImportDialog::startPreview() +{ + cleanupPreviewPuppet(); + + // Preview is done via custom QML file added into the temporary folder of the preview + QString previewQml = +R"( +import QtQuick +import QtQuick3D + +Rectangle { + id: root + width: %1 + height: %2 + + property string sceneModelName: "%3" + property alias view3d: view3d + property string extents + + gradient: Gradient { + GradientStop { position: 1.0; color: "#222222" } + GradientStop { position: 0.0; color: "#999999" } + } + + View3D { + id: view3d + anchors.fill: parent + camera: viewCamera + + environment: SceneEnvironment { + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.VeryHigh + } + + PerspectiveCamera { + id: viewCamera + z: 600 + y: 600 + x: 600 + eulerRotation.x: -45 + eulerRotation.y: -45 + clipFar: 100000 + clipNear: 10 + } + + DirectionalLight { + rotation: viewCamera.rotation + } + } + + Text { + anchors.bottom: parent.bottom + anchors.left: parent.left + color: "white" + text: root.extents + font.pixelSize: 14 + } +} +)"; + + QSize size = canvas()->size(); + previewQml = previewQml.arg(size.width()).arg(size.height()).arg(m_previewCompName); + + m_previewFile.writeFileContents(previewQml.toUtf8()); + + if (!m_previewFile.exists()) { + addWarning("Failed to write preview file."); + return; + } + + m_connectionManager = new Import3dConnectionManager; + m_rewriterView = new RewriterView{m_view->externalDependencies(), RewriterView::Amend}; + m_nodeInstanceView = new NodeInstanceView{*m_connectionManager, m_view->externalDependencies()}; + +#ifdef QDS_USE_PROJECTSTORAGE + m_model = m_view->model()->createModel("Item"); +#else + m_model = QmlDesigner::Model::create("QtQuick/Item", 2, 1); + m_model->setFileUrl(m_previewFile.toUrl()); +#endif + + auto textDocument = std::make_unique<QTextDocument>(previewQml); + auto modifier = std::make_unique<NotIndentingTextEditModifier>(textDocument.get(), + QTextCursor{textDocument.get()}); + m_rewriterView->setTextModifier(modifier.get()); + m_model->setRewriterView(m_rewriterView); + + if (!m_rewriterView->errors().isEmpty()) { + addWarning("Preview scene creation failed."); + cleanupPreviewPuppet(); + return; + } + + m_nodeInstanceView->setTarget(m_view->nodeInstanceView()->target()); + + auto previewImageCallback = [this](const QImage &image) { + canvas()->updateRenderImage(image); + }; + + auto crashCallback = [&] { + addWarning("Preview process crashed."); + cleanupPreviewPuppet(); + }; + + m_connectionManager->setPreviewImageCallback(std::move(previewImageCallback)); + m_nodeInstanceView->setCrashCallback(std::move(crashCallback)); + + m_model->setNodeInstanceView(m_nodeInstanceView); +} + +void ItemLibraryAssetImportDialog::cleanupPreviewPuppet() +{ + if (m_model) { + m_model->setNodeInstanceView({}); + m_model->setRewriterView({}); + m_model.reset(); + } + + if (m_nodeInstanceView) + m_nodeInstanceView->setCrashCallback({}); + + if (m_connectionManager) + m_connectionManager->setPreviewImageCallback({}); + + delete m_rewriterView; + delete m_nodeInstanceView; + delete m_connectionManager; +} + +Import3dCanvas *ItemLibraryAssetImportDialog::canvas() +{ + return ui->import3dcanvas; +} + void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event) { m_dialogHeight = event->size().height(); @@ -837,8 +983,8 @@ void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event) void ItemLibraryAssetImportDialog::setCloseButtonState(bool importing) { - ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(true); - ui->buttonBox->button(QDialogButtonBox::Close)->setText(importing ? tr("Cancel") : tr("Close")); + ui->closeButton->setEnabled(true); + ui->closeButton->setText(importing ? tr("Cancel") : tr("Close")); } void ItemLibraryAssetImportDialog::addError(const QString &error, const QString &srcPath) @@ -860,14 +1006,19 @@ void ItemLibraryAssetImportDialog::addInfo(const QString &info, const QString &s void ItemLibraryAssetImportDialog::onImport() { - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + ui->acceptButton->setEnabled(false); + ui->importButton->setEnabled(false); setCloseButtonState(true); ui->progressBar->setValue(0); if (!m_quick3DFiles.isEmpty()) { - m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath, - m_importOptions, m_extToImportOptionsMap, - m_preselectedFilesForOverwrite); + if (!m_previewCompName.isEmpty()) { + m_importer.reImportQuick3D(m_previewCompName, m_importOptions); + } else { + m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath, + m_importOptions, m_extToImportOptionsMap, + m_preselectedFilesForOverwrite); + } } } @@ -881,10 +1032,28 @@ void ItemLibraryAssetImportDialog::setImportProgress(int value, const QString &t ui->progressBar->setValue(value); } +void ItemLibraryAssetImportDialog::onImportReadyForPreview(const QString &path, const QString &compName) +{ + m_previewFile = Utils::FilePath::fromString(path).pathAppended(m_importer.previewFileName()); + m_previewCompName = compName; + QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::startPreview); + + ui->acceptButton->setEnabled(true); + ui->importButton->setEnabled(true); + + addInfo(tr("Generating import preview for %1.").arg(compName)); +} + +void ItemLibraryAssetImportDialog::onRequestImageUpdate() +{ + if (m_nodeInstanceView) + m_nodeInstanceView->view3DAction(View3DActionType::Import3dUpdatePreviewImage, canvas()->size()); +} + void ItemLibraryAssetImportDialog::onImportNearlyFinished() { // Canceling import is no longer doable - ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(false); + ui->closeButton->setEnabled(false); } void ItemLibraryAssetImportDialog::onImportFinished() @@ -920,6 +1089,16 @@ void ItemLibraryAssetImportDialog::onClose() } } +void ItemLibraryAssetImportDialog::onAccept() +{ + cleanupPreviewPuppet(); + + ui->importButton->setEnabled(false); + ui->acceptButton->setEnabled(false); + + m_importer.finalizeQuick3DImport(); +} + void ItemLibraryAssetImportDialog::toggleAdvanced() { m_advancedMode = !m_advancedMode; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h index c5da4782325..efbd189c2fd 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h @@ -3,14 +3,19 @@ #pragma once #include "itemlibraryassetimporter.h" -#include "modelnode.h" + +#include <modelnode.h> + +#include <utils/filepath.h> #include <QDialog> #include <QJsonObject> +#include <QPointer> #include <QSet> QT_BEGIN_NAMESPACE class QGridLayout; +class QPushButton; QT_END_NAMESPACE namespace Utils { @@ -19,6 +24,10 @@ class OutputFormatter; namespace QmlDesigner { class ItemLibraryAssetImporter; +class Import3dCanvas; +class Import3dConnectionManager; +class NodeInstanceView; +class RewriterView; namespace Ui { class ItemLibraryAssetImportDialog; @@ -35,10 +44,12 @@ public: const QVariantMap &supportedOpts, const QJsonObject &defaultOpts, const QSet<QString> &preselectedFilesForOverwrite, + AbstractView *view, QWidget *parent = nullptr); ~ItemLibraryAssetImportDialog(); - static void updateImport(const ModelNode &updateNode, + static void updateImport(AbstractView *view, + const ModelNode &updateNode, const QVariantMap &supportedExts, const QVariantMap &supportedOpts); @@ -55,9 +66,12 @@ private: void onImport(); void setImportProgress(int value, const QString &text); + void onImportReadyForPreview(const QString &path, const QString &compName); + void onRequestImageUpdate(); void onImportNearlyFinished(); void onImportFinished(); void onClose(); + void onAccept(); void toggleAdvanced(); void createTab(const QString &tabLabel, int optionsIndex, const QJsonObject &groups); @@ -69,8 +83,19 @@ private: bool isSimpleOption(const QString &id); bool isHiddenOption(const QString &id); + void startPreview(); + void cleanupPreviewPuppet(); + Import3dCanvas *canvas(); + Ui::ItemLibraryAssetImportDialog *ui = nullptr; Utils::OutputFormatter *m_outputFormatter = nullptr; + QPointer<Import3dConnectionManager> m_connectionManager; + QPointer<NodeInstanceView> m_nodeInstanceView; + QPointer<RewriterView> m_rewriterView; + QPointer<AbstractView> m_view; + ModelPointer m_model; + Utils::FilePath m_previewFile; + QString m_previewCompName; struct OptionsData { diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui index 081bc36a3d3..a35875a5eb7 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui @@ -6,15 +6,15 @@ <rect> <x>0</x> <y>0</y> - <width>630</width> + <width>1100</width> <height>350</height> </rect> </property> <property name="windowTitle"> <string>Asset Import</string> </property> - <layout class="QFormLayout" name="formLayout"> - <item row="0" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QTabWidget" name="tabWidget"> @@ -24,6 +24,12 @@ <verstretch>2</verstretch> </sizepolicy> </property> + <property name="minimumSize"> + <size> + <width>550</width> + <height>0</height> + </size> + </property> <property name="currentIndex"> <number>0</number> </property> @@ -98,6 +104,12 @@ <verstretch>0</verstretch> </sizepolicy> </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> <property name="text"> <string/> </property> @@ -111,16 +123,74 @@ </widget> </item> <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="standardButtons"> - <set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set> - </property> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="closeButton"> + <property name="text"> + <string>Close</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="importButton"> + <property name="text"> + <string>Import</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="acceptButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Accept</string> + </property> + </widget> + </item> + </layout> </item> </layout> </item> + <item> + <widget class="Import3dCanvas" name="import3dcanvas" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>300</width> + <height>300</height> + </size> + </property> + </widget> + </item> </layout> </widget> + <customwidgets> + <customwidget> + <class>Import3dCanvas</class> + <extends>QWidget</extends> + <header>import3dcanvas.h</header> + <container>1</container> + </customwidget> + </customwidgets> <resources/> <connections/> </ui> diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index ed1f8041e9c..010d00a970a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -21,6 +21,7 @@ #include <utils/algorithm.h> #include <utils/async.h> +#include <utils/filepath.h> #include <utils/qtcassert.h> #include <QApplication> @@ -58,8 +59,6 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles, const QHash<QString, int> &extToImportOptionsMap, const QSet<QString> &preselectedFilesForOverwrite) { - if (m_isImporting) - cancelImport(); reset(); m_isImporting = true; @@ -92,6 +91,53 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles, } } +void ItemLibraryAssetImporter::reImportQuick3D(const QString &assetName, + const QVector<QJsonObject> &options) +{ + if (!assetName.isEmpty() && !m_parseData.contains(assetName)) { + addError(tr("Attempted to reimport non-existing asset: %1").arg(assetName)); + return; + } + + ParseData &pd = m_parseData[assetName]; + // Change outDir just in case reimport generates different files + QDir oldDir = pd.outDir; + QString assetFolder = generateAssetFolderName(pd.assetName); + pd.outDir.cdUp(); + pd.outDir.mkpath(assetFolder); + + if (!pd.outDir.cd(assetFolder)) { + addError(tr("Could not access temporary asset directory: \"%1\".") + .arg(pd.outDir.filePath(assetFolder))); + return; + } + + if (oldDir.absolutePath().contains(tempDirNameBase())) + oldDir.removeRecursively(); + + m_isImporting = false; + m_cancelled = false; + + m_puppetProcess.reset(); + m_requiredImports.clear(); + m_currentImportId = 0; + m_puppetQueue.clear(); + + for (ParseData &pd : m_parseData) + pd.importId = -1; + + pd.options = options[pd.optionsIndex]; + pd.importId = 1; + + m_importFiles.remove(assetName); + + m_importIdToAssetNameMap.clear(); + m_importIdToAssetNameMap[pd.importId] = assetName; + + m_puppetQueue.append(pd.importId); + startNextImportProcess(); +} + bool ItemLibraryAssetImporter::isImporting() const { return m_isImporting; @@ -104,19 +150,19 @@ void ItemLibraryAssetImporter::cancelImport() notifyFinished(); } -void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath) const +void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath) { qCDebug(importerLog) << "Error: "<< errMsg << srcPath; emit errorReported(errMsg, srcPath); } -void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath) const +void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath) { qCDebug(importerLog) << "Warning: " << warningMsg << srcPath; emit warningReported(warningMsg, srcPath); } -void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath) const +void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath) { qCDebug(importerLog) << "Info: " << infoMsg << srcPath; emit infoReported(infoMsg, srcPath); @@ -127,8 +173,8 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo { m_puppetProcess.reset(); - if (m_parseData.contains(m_currentImportId)) { - const ParseData &pd = m_parseData[m_currentImportId]; + if (m_importIdToAssetNameMap.contains(m_currentImportId)) { + const ParseData &pd = m_parseData[m_importIdToAssetNameMap[m_currentImportId]]; QString errStr; if (exitStatus == QProcess::ExitStatus::CrashExit) { errStr = tr("Import process crashed."); @@ -151,11 +197,12 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo addError(tr("Asset import process failed: \"%1\".") .arg(pd.sourceInfo.absoluteFilePath())); addError(errStr); - m_parseData.remove(m_currentImportId); + m_parseData.remove(m_importIdToAssetNameMap[m_currentImportId]); + m_importIdToAssetNameMap.remove(m_currentImportId); } } - int finishedCount = m_parseData.size() - m_puppetQueue.size(); + int finishedCount = m_importIdToAssetNameMap.size() - m_puppetQueue.size(); if (!m_puppetQueue.isEmpty()) startNextImportProcess(); @@ -163,7 +210,7 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo notifyProgress(100); QTimer::singleShot(0, this, &ItemLibraryAssetImporter::postImport); } else { - notifyProgress(int(100. * (double(finishedCount) / double(m_parseData.size())))); + notifyProgress(int(100. * (double(finishedCount) / double(m_importIdToAssetNameMap.size())))); } } @@ -179,7 +226,7 @@ void ItemLibraryAssetImporter::reset() m_cancelled = false; delete m_tempDir; - m_tempDir = new QTemporaryDir; + m_tempDir = new QTemporaryDir(QDir::tempPath() + tempDirNameBase()); m_importFiles.clear(); m_overwrittenImports.clear(); m_puppetProcess.reset(); @@ -187,6 +234,7 @@ void ItemLibraryAssetImporter::reset() m_requiredImports.clear(); m_currentImportId = 0; m_puppetQueue.clear(); + m_importIdToAssetNameMap.clear(); } void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths, @@ -208,9 +256,11 @@ void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths, int index = extToImportOptionsMap.value(QFileInfo(file).suffix()); ParseData pd; pd.options = options[index]; + pd.optionsIndex = index; if (preParseQuick3DAsset(file, pd, preselectedFilesForOverwrite)) { pd.importId = ++m_importIdCounter; - m_parseData.insert(pd.importId, pd); + m_importIdToAssetNameMap[pd.importId] = pd.assetName; + m_parseData.insert(pd.assetName, pd); } notifyProgress(qRound(++count * quota), progressTitle); } @@ -239,7 +289,7 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa pd.targetDirPath = pd.targetDir.filePath(pd.assetName); - if (pd.outDir.exists(pd.assetName)) { + if (m_parseData.contains(pd.assetName)) { addWarning(tr("Skipped import of duplicate asset: \"%1\".").arg(pd.assetName)); return false; } @@ -288,11 +338,12 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa } } - pd.outDir.mkpath(pd.assetName); + QString assetFolder = generateAssetFolderName(pd.assetName); + pd.outDir.mkpath(assetFolder); - if (!pd.outDir.cd(pd.assetName)) { + if (!pd.outDir.cd(assetFolder)) { addError(tr("Could not access temporary asset directory: \"%1\".") - .arg(pd.outDir.filePath(pd.assetName))); + .arg(pd.outDir.filePath(assetFolder))); return false; } return true; @@ -425,7 +476,7 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) // Copy the original asset into a subdirectory assetFiles.insert(sourcePath, sourceSceneTargetFilePath(pd)); - m_importFiles.insert(assetFiles); + m_importFiles.insert(pd.assetName, assetFiles); } void ItemLibraryAssetImporter::copyImportedFiles() @@ -500,6 +551,12 @@ void ItemLibraryAssetImporter::keepUiAlive() const QApplication::processEvents(); } +QString ItemLibraryAssetImporter::generateAssetFolderName(const QString &assetName) const +{ + static int counter = 0; + return assetName + "_QDS_" + QString::number(counter++); +} + ItemLibraryAssetImporter::OverwriteResult ItemLibraryAssetImporter::confirmAssetOverwrite(const QString &assetName) { const QString title = tr("Overwrite Existing Asset?"); @@ -534,7 +591,8 @@ void ItemLibraryAssetImporter::startNextImportProcess() if (model && view) { bool done = false; while (!m_puppetQueue.isEmpty() && !done) { - const ParseData pd = m_parseData.value(m_puppetQueue.takeLast()); + const ParseData pd = m_parseData.value( + m_importIdToAssetNameMap.value(m_puppetQueue.takeLast())); QStringList puppetArgs; QJsonDocument optDoc(pd.options); @@ -557,7 +615,8 @@ void ItemLibraryAssetImporter::startNextImportProcess() } else { addError(tr("Failed to start import 3D asset process."), pd.sourceInfo.absoluteFilePath()); - m_parseData.remove(pd.importId); + const QString assetName = m_importIdToAssetNameMap.take(pd.importId); + m_parseData.remove(assetName); m_puppetProcess.reset(); } } @@ -573,8 +632,16 @@ void ItemLibraryAssetImporter::postImport() postParseQuick3DAsset(pd); } - if (!isCancelled()) - finalizeQuick3DImport(); + if (!isCancelled()) { + // TODO: Currently we only support import preview for single imports + if (m_parseData.size() != 1) { + finalizeQuick3DImport(); + } else { + const ParseData &pd = m_parseData[m_parseData.keys().first()]; + const QString importedComponentName = pd.assetName; + emit importReadyForPreview(pd.outDir.absolutePath(), importedComponentName); + } + } } void ItemLibraryAssetImporter::finalizeQuick3DImport() diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h index 8ad7b5a2dec..abe96909514 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once -#include "import.h" - #include <qprocessuniqueptr.h> #include <QSet> @@ -35,20 +33,28 @@ public: const QHash<QString, int> &extToImportOptionsMap, const QSet<QString> &preselectedFilesForOverwrite); + void reImportQuick3D(const QString &assetName, const QVector<QJsonObject> &options); + bool isImporting() const; void cancelImport(); bool isCancelled() const; - void addError(const QString &errMsg, const QString &srcPath = {}) const; - void addWarning(const QString &warningMsg, const QString &srcPath = {}) const; - void addInfo(const QString &infoMsg, const QString &srcPath = {}) const; + void addError(const QString &errMsg, const QString &srcPath = {}); + void addWarning(const QString &warningMsg, const QString &srcPath = {}); + void addInfo(const QString &infoMsg, const QString &srcPath = {}); + + QString previewFileName() const { return "QDSImport3dPreviewScene.qml"; } + QString tempDirNameBase() const { return "/qds3dimport"; } + + void finalizeQuick3DImport(); signals: - void errorReported(const QString &, const QString &) const; - void warningReported(const QString &, const QString &) const; - void infoReported(const QString &, const QString &) const; - void progressChanged(int value, const QString &text) const; - void importNearlyFinished() const; + void errorReported(const QString &, const QString &); + void warningReported(const QString &, const QString &); + void infoReported(const QString &, const QString &); + void progressChanged(int value, const QString &text); + void importReadyForPreview(const QString &path, const QString &compName); + void importNearlyFinished(); void importFinished(); private slots: @@ -63,7 +69,8 @@ private: QFileInfo sourceInfo; QString assetName; QString originalAssetName; - int importId; + int importId = -1; + int optionsIndex = -1; }; void notifyFinished(); @@ -79,6 +86,7 @@ private: void notifyProgress(int value, const QString &text); void notifyProgress(int value); void keepUiAlive() const; + QString generateAssetFolderName(const QString &assetName) const; enum class OverwriteResult { Skip, @@ -89,10 +97,9 @@ private: OverwriteResult confirmAssetOverwrite(const QString &assetName); void startNextImportProcess(); void postImport(); - void finalizeQuick3DImport(); QString sourceSceneTargetFilePath(const ParseData &pd); - QSet<QHash<QString, QString>> m_importFiles; + QHash<QString, QHash<QString, QString>> m_importFiles; // Key: asset name QHash<QString, QStringList> m_overwrittenImports; bool m_isImporting = false; bool m_cancelled = false; @@ -101,7 +108,8 @@ private: QProcessUniquePointer m_puppetProcess; int m_importIdCounter = 0; int m_currentImportId = 0; - QHash<int, ParseData> m_parseData; + QHash<int, QString> m_importIdToAssetNameMap; + QHash<QString, ParseData> m_parseData; // Key: asset name QString m_progressTitle; QStringList m_requiredImports; QList<int> m_puppetQueue; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index feff523b9c6..e4f4ffcd9c6 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -164,7 +164,7 @@ void ItemLibraryView::updateImport3DSupport(const QVariantMap &supportMap) auto importDlg = new ItemLibraryAssetImportDialog(fileNames, defaultDir, m_importableExtensions3DMap, m_importOptions3DMap, {}, {}, - Core::ICore::dialogParent()); + this, Core::ICore::dialogParent()); int result = importDlg->exec(); return result == QDialog::Accepted ? AddFilesResult::succeeded() : AddFilesResult::cancelled(); @@ -198,7 +198,7 @@ void ItemLibraryView::customNotification(const AbstractView *view, const QString const QList<ModelNode> &nodeList, const QList<QVariant> &data) { if (identifier == "UpdateImported3DAsset" && nodeList.size() > 0) { - ItemLibraryAssetImportDialog::updateImport(nodeList[0], + ItemLibraryAssetImportDialog::updateImport(this, nodeList[0], m_importableExtensions3DMap, m_importOptions3DMap); diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 17c75a02853..df17f005a21 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -39,7 +39,8 @@ namespace Internal { static QStringList puppetModes() { - static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode", "bakelightsmode"}; + static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode", + "bakelightsmode", "import3dmode"}; return puppetModeList; } diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 36ca6293d43..e77887adcca 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -140,6 +140,7 @@ extend_qtc_executable(qml2puppet qmltransitionnodeinstance.cpp qmltransitionnodeinstance.h qt3dpresentationnodeinstance.cpp qt3dpresentationnodeinstance.h qt5bakelightsnodeinstanceserver.cpp qt5bakelightsnodeinstanceserver.h + qt5import3dnodeinstanceserver.cpp qt5import3dnodeinstanceserver.h qt5informationnodeinstanceserver.cpp qt5informationnodeinstanceserver.h qt5nodeinstanceclientproxy.cpp qt5nodeinstanceclientproxy.h qt5nodeinstanceserver.cpp qt5nodeinstanceserver.h diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index d4f79fdabdd..1ccea6049c8 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -462,9 +462,9 @@ QVector4D GeneralHelper::approachNode( // a selection box for bound calculations to work. This is used to focus the view for // various preview image generations, where doing things asynchronously is not good // and recalculating bounds for every frame is not a problem. -void GeneralHelper::calculateNodeBoundsAndFocusCamera( - QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, - float defaultLookAtDistance, bool closeUp) +QVector3D GeneralHelper::calculateNodeBoundsAndFocusCamera( + QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, + float defaultLookAtDistance, bool closeUp) { QVector3D minBounds; QVector3D maxBounds; @@ -505,8 +505,9 @@ void GeneralHelper::calculateNodeBoundsAndFocusCamera( perspectiveCamera->setClipNear(minDist * 0.99); perspectiveCamera->setClipFar(maxDist * 1.01); } - } + + return extents; } // Aligns any cameras found in nodes list to a camera. diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 50eaba68ebb..996a06488f4 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -72,9 +72,11 @@ public: bool closeUp = false); Q_INVOKABLE QVector4D approachNode(QQuick3DCamera *camera, float defaultLookAtDistance, QObject *node, QQuick3DViewport *viewPort); - Q_INVOKABLE void calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, - QQuick3DViewport *viewPort, - float defaultLookAtDistance, bool closeUp); + Q_INVOKABLE QVector3D calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, + QQuick3DNode *node, + QQuick3DViewport *viewPort, + float defaultLookAtDistance, + bool closeUp); Q_INVOKABLE void alignCameras(QQuick3DCamera *camera, const QVariant &nodes); Q_INVOKABLE QVector4D alignView(QQuick3DCamera *camera, const QVariant &nodes, const QVector3D &lookAtPoint, float defaultLookAtDistance); diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp index 9815d21ac47..9e3cdac151e 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp @@ -6,6 +6,7 @@ #include "qt5bakelightsnodeinstanceserver.h" #include "qt5captureimagenodeinstanceserver.h" #include "qt5capturepreviewnodeinstanceserver.h" +#include "qt5import3dnodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" #include "qt5rendernodeinstanceserver.h" @@ -173,6 +174,8 @@ std::unique_ptr<NodeInstanceServer> createNodeInstanceServer( return std::make_unique<Qt5PreviewNodeInstanceServer>(nodeInstanceClient); else if (serverName == "bakelightsmode") return std::make_unique<Qt5BakeLightsNodeInstanceServer>(nodeInstanceClient); + else if (serverName == "import3dmode") + return std::make_unique<Qt5Import3dNodeInstanceServer>(nodeInstanceClient); return {}; } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp new file mode 100644 index 00000000000..b5afa949ec2 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp @@ -0,0 +1,186 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "qt5import3dnodeinstanceserver.h" + +#include "createscenecommand.h" +#include "view3dactioncommand.h" + +#include "imagecontainer.h" +#include "nodeinstanceclientinterface.h" +#include "puppettocreatorcommand.h" + +#include <QFileInfo> +#include <QLocale> +#include <QQmlComponent> +#include <QQmlEngine> + +#include <QQmlProperty> + +#include <private/qquick3dnode_p.h> +#include <private/qquick3dviewport_p.h> +#include <private/qquickdesignersupport_p.h> + +namespace QmlDesigner { + +Qt5Import3dNodeInstanceServer::Qt5Import3dNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) : + Qt5NodeInstanceServer(nodeInstanceClient) +{ + setSlowRenderTimerInterval(100000000); + setRenderTimerInterval(20); + +#ifdef QUICK3D_MODULE + m_generalHelper = new Internal::GeneralHelper(); + QObject::connect(m_generalHelper, &Internal::GeneralHelper::requestRender, this, [this]() { + startRenderTimer(); + }); +#endif +} + +Qt5Import3dNodeInstanceServer::~Qt5Import3dNodeInstanceServer() +{ + cleanup(); +} + +void Qt5Import3dNodeInstanceServer::createScene(const CreateSceneCommand &command) +{ + initializeView(); + registerFonts(command.resourceUrl); + setTranslationLanguage(command.language); + setupScene(command); + startRenderTimer(); +} + +void Qt5Import3dNodeInstanceServer::view3DAction([[maybe_unused]] const View3DActionCommand &command) +{ + switch (command.type()) { + case View3DActionType::Import3dUpdatePreviewImage: { + QObject *obj = rootItem(); + if (obj) { + QSize size = command.value().toSize(); + QQmlProperty wProp(obj, "width", context()); + QQmlProperty hProp(obj, "height", context()); + wProp.write(size.width()); + hProp.write(size.height()); + resizeCanvasToRootItem(); + + startRenderTimer(); + } + break; + } + default: + break; + } +} + +void Qt5Import3dNodeInstanceServer::startRenderTimer() +{ + if (timerId() != 0) + killTimer(timerId()); + + int timerId = startTimer(renderTimerInterval()); + + setTimerId(timerId); +} + +void Qt5Import3dNodeInstanceServer::cleanup() +{ +#ifdef QUICK3D_MODULE + delete m_previewNode; + delete m_generalHelper; +#endif +} + +void Qt5Import3dNodeInstanceServer::finish() +{ + cleanup(); +} + +void Qt5Import3dNodeInstanceServer::collectItemChangesAndSendChangeCommands() +{ + static bool inFunction = false; + + if (!rootNodeInstance().holdsGraphical()) + return; + + if (!inFunction) { + inFunction = true; + + QQuickDesignerSupport::polishItems(quickWindow()); + + render(); + + inFunction = false; + } +} + +void Qt5Import3dNodeInstanceServer::render() +{ +#ifdef QUICK3D_MODULE + ++m_renderCount; + + if (m_renderCount == 1) { + QObject *obj = rootItem(); + QQmlProperty viewProp(obj, "view3d", context()); + QObject *viewObj = viewProp.read().value<QObject *>(); + m_view3D = qobject_cast<QQuick3DViewport *>(viewObj); + if (m_view3D) { + QQmlProperty sceneModelNameProp(obj, "sceneModelName", context()); + QString sceneModelName = sceneModelNameProp.read().toString(); + QFileInfo fi(fileUrl().toLocalFile()); + QString compPath = fi.absolutePath() + '/' + sceneModelName + ".qml"; + QQmlComponent comp(engine(), compPath, QQmlComponent::PreferSynchronous); + m_previewNode = qobject_cast<QQuick3DNode *>(comp.create(context())); + if (m_previewNode) { + engine()->setObjectOwnership(m_previewNode, QJSEngine::CppOwnership); + m_previewNode->setParent(m_view3D); + m_view3D->setImportScene(m_previewNode); + } + } + } + + // Render scene once to ensure geometries are intialized so bounds calculations work correctly + if (m_renderCount == 2 && m_view3D) { + QVector3D extents = + m_generalHelper->calculateNodeBoundsAndFocusCamera(m_view3D->camera(), m_previewNode, + m_view3D, 1040, false); + auto getExtentStr = [&extents](int idx) -> QString { + int prec = 0; + float val = extents[idx]; + while (val < 100.f) { + ++prec; + val *= 10.f; + } + // Strip unnecessary zeroes after decimal separator + if (prec > 0) { + QString checkStr = QString::number(extents[idx], 'f', prec); + while (prec > 0 && (checkStr.last(1) == "0" || checkStr.last(1) == ".")) { + --prec; + checkStr.chop(1); + } + } + QString retval = QLocale().toString(extents[idx], 'f', prec); + return retval; + }; + QQmlProperty extentsProp(rootItem(), "extents", context()); + extentsProp.write(tr("Dimensions: %1 x %2 x %3").arg(getExtentStr(0)) + .arg(getExtentStr(1)) + .arg(getExtentStr(2))); + } + + rootNodeInstance().updateDirtyNodeRecursive(); + QImage renderImage = grabWindow(); + + if (m_renderCount >= 2) { + ImageContainer imgContainer(0, renderImage, m_renderCount); + nodeInstanceClient()->handlePuppetToCreatorCommand( + {PuppetToCreatorCommand::Import3DPreviewImage, + QVariant::fromValue(imgContainer)}); + slowDownRenderTimer(); // No more renders needed for now + } +#else + slowDownRenderTimer(); +#endif +} + +} // namespace QmlDesigner diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h new file mode 100644 index 00000000000..bc3506ea3c0 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h @@ -0,0 +1,46 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "generalhelper.h" +#include "qt5nodeinstanceserver.h" + +QT_BEGIN_NAMESPACE +class QQuick3DNode; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class Qt5Import3dNodeInstanceServer : public Qt5NodeInstanceServer +{ + Q_OBJECT +public: + explicit Qt5Import3dNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient); + +public: + virtual ~Qt5Import3dNodeInstanceServer(); + + void createScene(const CreateSceneCommand &command) override; + void view3DAction(const View3DActionCommand &command) override; + + void render(); + +protected: + void collectItemChangesAndSendChangeCommands() override; + void startRenderTimer() override; + +private: + void finish(); + void cleanup(); + + int m_renderCount = 0; + +#ifdef QUICK3D_MODULE + QQuick3DViewport *m_view3D = nullptr; + Internal::GeneralHelper *m_generalHelper = nullptr; + QQuick3DNode *m_previewNode = nullptr; +#endif +}; + +} // namespace QmlDesigner diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp index f4d01d28db4..c11899e0ec9 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp @@ -10,6 +10,7 @@ #include "qt5captureimagenodeinstanceserver.h" #include "qt5capturepreviewnodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" +#include "qt5import3dnodeinstanceserver.h" #include "qt5previewnodeinstanceserver.h" #include "qt5rendernodeinstanceserver.h" #include "qt5testnodeinstanceserver.h" @@ -70,6 +71,9 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) : } else if (QCoreApplication::arguments().at(2) == QLatin1String("bakelightsmode")) { setNodeInstanceServer(std::make_unique<Qt5BakeLightsNodeInstanceServer>(this)); initializeSocket(); + } else if (QCoreApplication::arguments().at(2) == QLatin1String("import3dmode")) { + setNodeInstanceServer(std::make_unique<Qt5Import3dNodeInstanceServer>(this)); + initializeSocket(); } } |