QmlDesigner: Add support for previewing multiple 3D imports

Imported items are shown on a list in import dialog and a preview is
generated for each. Options are also specified per-import rather
than applying to all imports.

Fixes: QDS-10806
Change-Id: I6be09880afc0f8886585c4e768da1197b46bc71a
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Miikka Heikkinen
2024-05-28 17:48:47 +03:00
parent d3a0a497b0
commit b3a5d38a68
8 changed files with 356 additions and 129 deletions

View File

@@ -17,9 +17,12 @@
#include <QQmlProperty>
#include <private/qquickdesignersupport_p.h>
#ifdef QUICK3D_MODULE
#include <private/qquick3dnode_p.h>
#include <private/qquick3dviewport_p.h>
#include <private/qquickdesignersupport_p.h>
#endif
namespace QmlDesigner {
@@ -48,11 +51,24 @@ void Qt5Import3dNodeInstanceServer::createScene(const CreateSceneCommand &comman
registerFonts(command.resourceUrl);
setTranslationLanguage(command.language);
setupScene(command);
#ifdef QUICK3D_MODULE
QObject *obj = rootItem();
QQmlProperty viewProp(obj, "view3d", context());
QObject *viewObj = viewProp.read().value<QObject *>();
m_view3D = qobject_cast<QQuick3DViewport *>(viewObj);
if (m_view3D) {
QQmlProperty sceneNodeProp(obj, "sceneNode", context());
m_sceneNode = sceneNodeProp.read().value<QQuick3DNode *>();
}
#endif
startRenderTimer();
}
void Qt5Import3dNodeInstanceServer::view3DAction([[maybe_unused]] const View3DActionCommand &command)
{
#ifdef QUICK3D_MODULE
switch (command.type()) {
case View3DActionType::Import3dUpdatePreviewImage: {
QObject *obj = rootItem();
@@ -63,7 +79,6 @@ void Qt5Import3dNodeInstanceServer::view3DAction([[maybe_unused]] const View3DAc
wProp.write(size.width());
hProp.write(size.height());
resizeCanvasToRootItem();
startRenderTimer();
}
break;
@@ -72,18 +87,72 @@ void Qt5Import3dNodeInstanceServer::view3DAction([[maybe_unused]] const View3DAc
QObject *obj = rootItem();
QQmlProperty sceneNodeProp(obj, "sceneNode", context());
auto sceneNode = sceneNodeProp.read().value<QQuick3DNode *>();
if (sceneNode) {
if (sceneNode && m_previewData.contains(m_currentNode)) {
const PreviewData &data = m_previewData[m_currentNode];
QPointF delta = command.value().toPointF();
m_generalHelper->orbitCamera(m_view3D->camera(), m_view3D->camera()->eulerRotation(),
m_lookAt, {}, {float(delta.x()), float(delta.y()), 0.f});
data.lookAt, {}, {float(delta.x()), float(delta.y()), 0.f});
m_keepRendering = true;
startRenderTimer();
}
break;
}
case View3DActionType::Import3dAddPreviewModel: {
const QVariantHash cmd = command.value().toHash();
const QString name = cmd["name"].toString();
const QString folder = cmd["folder"].toString();
if (m_previewData.contains(name)) {
QQuick3DNode *node = m_previewData[name].node;
if (node) {
node->setParentItem({});
node->setParent({});
node->deleteLater();
}
}
PreviewData &data = m_previewData[name];
data.name = name;
data.lookAt = {};
QFileInfo fi(fileUrl().toLocalFile());
QString compPath = fi.absolutePath() + '/' + folder + '/' + name + ".qml";
QQmlComponent comp(engine(), compPath, QQmlComponent::PreferSynchronous);
data.node = qobject_cast<QQuick3DNode *>(comp.create(context()));
if (data.node) {
engine()->setObjectOwnership(data.node, QJSEngine::CppOwnership);
data.node->setParentItem(m_sceneNode);
data.node->setParent(m_sceneNode);
}
if (m_currentNode == data.name) {
m_renderCount = 0;
startRenderTimer();
} else if (data.node) {
data.node->setVisible(false);
}
break;
}
case View3DActionType::Import3dSetCurrentPreviewModel: {
QString newName = command.value().toString();
if (m_previewData.contains(newName) && m_currentNode != newName) {
const PreviewData &newData = m_previewData[newName];
const PreviewData oldData = m_previewData.value(m_currentNode);
if (oldData.node)
oldData.node->setVisible(false);
if (newData.node)
newData.node->setVisible(true);
m_renderCount = 0;
m_currentNode = newName;
startRenderTimer();
}
break;
}
default:
break;
}
#endif
}
void Qt5Import3dNodeInstanceServer::startRenderTimer()
@@ -97,16 +166,13 @@ void Qt5Import3dNodeInstanceServer::startRenderTimer()
void Qt5Import3dNodeInstanceServer::cleanup()
{
#ifdef QUICK3D_MODULE
delete m_previewNode;
for (const PreviewData &data : std::as_const(m_previewData))
delete data.node;
m_previewData.clear();
delete m_generalHelper;
#endif
}
void Qt5Import3dNodeInstanceServer::finish()
{
cleanup();
}
void Qt5Import3dNodeInstanceServer::collectItemChangesAndSendChangeCommands()
{
static bool inFunction = false;
@@ -130,53 +196,32 @@ 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());
QQmlProperty sceneNodeProp(obj, "sceneNode", context());
auto sceneNode = sceneNodeProp.read().value<QQuick3DNode *>();
if (sceneNode) {
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->setParentItem(sceneNode);
m_previewNode->setParent(sceneNode);
}
}
}
}
// Render scene at least once before calculating bounds to ensure geometries are intialized
if (m_renderCount == 2 && m_view3D && m_previewData.contains(m_currentNode)) {
PreviewData &data = m_previewData[m_currentNode];
m_generalHelper->calculateBoundsAndFocusCamera(m_view3D->camera(), data.node,
m_view3D, 1050, false, data.lookAt,
data.extents);
// Render scene once to ensure geometries are intialized so bounds calculations work correctly
if (m_renderCount == 2 && m_view3D) {
QVector3D extents;
m_generalHelper->calculateBoundsAndFocusCamera(m_view3D->camera(), m_previewNode,
m_view3D, 1050, false, m_lookAt, extents);
auto getExtentStr = [&extents](int idx) -> QString {
auto getExtentStr = [&data](int idx) -> QString {
int prec = 0;
float val = extents[idx];
float val = data.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);
QString checkStr = QString::number(data.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);
QString retval = QLocale().toString(data.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))

View File

@@ -3,12 +3,14 @@
#pragma once
#include "generalhelper.h"
#include "qt5nodeinstanceserver.h"
#ifdef QUICK3D_MODULE
#include "generalhelper.h"
QT_BEGIN_NAMESPACE
class QQuick3DNode;
QT_END_NAMESPACE
#endif
namespace QmlDesigner {
@@ -31,7 +33,6 @@ protected:
void startRenderTimer() override;
private:
void finish();
void cleanup();
int m_renderCount = 0;
@@ -40,8 +41,17 @@ private:
#ifdef QUICK3D_MODULE
QQuick3DViewport *m_view3D = nullptr;
Internal::GeneralHelper *m_generalHelper = nullptr;
QQuick3DNode *m_previewNode = nullptr;
QVector3D m_lookAt;
struct PreviewData
{
QString name;
QVector3D lookAt;
QVector3D extents;
QQuick3DNode *node = {};
};
QHash<QString, PreviewData> m_previewData;
QString m_currentNode;
QQuick3DNode *m_sceneNode = {};
#endif
};