forked from qt-creator/qt-creator
QmlDesigner: Implement proper preview for pure 3D scenes
Refactored the existing 3D node preview generation to be done synchronously, which enables their use also for creating state and item library previews for 3D nodes with ease. Fixes: QDS-5785 Change-Id: Ib493eccbc239f33bcad3301673a865494616a901 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -32,6 +32,11 @@ View3D {
|
||||
|
||||
property Material previewMaterial
|
||||
|
||||
function fitToViewPort()
|
||||
{
|
||||
// No need to zoom this view, this is here just to avoid runtime warnings
|
||||
}
|
||||
|
||||
SceneEnvironment {
|
||||
id: sceneEnv
|
||||
antialiasingMode: SceneEnvironment.MSAA
|
||||
|
@@ -41,8 +41,6 @@ Item {
|
||||
property var modelViewComponent
|
||||
property var nodeViewComponent
|
||||
|
||||
property bool ready: false
|
||||
|
||||
function destroyView()
|
||||
{
|
||||
previewObject = null;
|
||||
@@ -58,8 +56,6 @@ Item {
|
||||
createViewForModel(obj);
|
||||
else if (obj instanceof Node)
|
||||
createViewForNode(obj);
|
||||
|
||||
previewObject = obj;
|
||||
}
|
||||
|
||||
function createViewForMaterial(material)
|
||||
@@ -70,6 +66,8 @@ Item {
|
||||
// Always recreate the view to ensure material is up to date
|
||||
if (materialViewComponent.status === Component.Ready)
|
||||
view = materialViewComponent.createObject(viewRect, {"previewMaterial": material});
|
||||
|
||||
previewObject = material;
|
||||
}
|
||||
|
||||
function createViewForModel(model)
|
||||
@@ -80,6 +78,8 @@ Item {
|
||||
// Always recreate the view to ensure model is up to date
|
||||
if (modelViewComponent.status === Component.Ready)
|
||||
view = modelViewComponent.createObject(viewRect, {"sourceModel": model});
|
||||
|
||||
previewObject = model;
|
||||
}
|
||||
|
||||
function createViewForNode(node)
|
||||
@@ -90,16 +90,13 @@ Item {
|
||||
// Always recreate the view to ensure node is up to date
|
||||
if (nodeViewComponent.status === Component.Ready)
|
||||
view = nodeViewComponent.createObject(viewRect, {"importScene": node});
|
||||
|
||||
previewObject = node;
|
||||
}
|
||||
|
||||
function afterRender()
|
||||
function fitToViewPort()
|
||||
{
|
||||
if (previewObject instanceof Node) {
|
||||
view.fitToViewPort();
|
||||
ready = view.ready;
|
||||
} else {
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
View3D {
|
||||
|
@@ -32,24 +32,13 @@ View3D {
|
||||
environment: sceneEnv
|
||||
camera: theCamera
|
||||
|
||||
property bool ready: false
|
||||
property real prevZoomFactor: -1
|
||||
property Model sourceModel
|
||||
|
||||
function fitToViewPort()
|
||||
{
|
||||
cameraControl.focusObject(model, theCamera.eulerRotation, true, false);
|
||||
|
||||
if (cameraControl._zoomFactor < 0.1) {
|
||||
model.scale = model.scale.times(10);
|
||||
} else if (cameraControl._zoomFactor > 10) {
|
||||
model.scale = model.scale.times(0.1);
|
||||
} else {
|
||||
// We need one more render after zoom factor change, so only set ready when zoom factor
|
||||
// or scaling hasn't changed from the previous frame
|
||||
ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor);
|
||||
prevZoomFactor = cameraControl._zoomFactor;
|
||||
}
|
||||
// The magic number is the distance from camera default pos to origin
|
||||
_generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root,
|
||||
1040);
|
||||
}
|
||||
|
||||
SceneEnvironment {
|
||||
@@ -58,14 +47,6 @@ View3D {
|
||||
antialiasingQuality: SceneEnvironment.High
|
||||
}
|
||||
|
||||
EditCameraController {
|
||||
id: cameraControl
|
||||
camera: theCamera
|
||||
anchors.fill: parent
|
||||
view3d: root
|
||||
ignoreToolState: true
|
||||
}
|
||||
|
||||
DirectionalLight {
|
||||
eulerRotation.x: -30
|
||||
eulerRotation.y: -30
|
||||
@@ -75,15 +56,15 @@ View3D {
|
||||
id: theCamera
|
||||
z: 600
|
||||
y: 600
|
||||
x: 600
|
||||
eulerRotation.x: -45
|
||||
eulerRotation.y: -45
|
||||
clipFar: 10000
|
||||
clipNear: 1
|
||||
}
|
||||
|
||||
Model {
|
||||
id: model
|
||||
eulerRotation.y: 45
|
||||
|
||||
source: sourceModel.source
|
||||
geometry: sourceModel.geometry
|
||||
|
||||
|
@@ -32,30 +32,11 @@ View3D {
|
||||
environment: sceneEnv
|
||||
camera: theCamera
|
||||
|
||||
property bool ready: false
|
||||
property bool first: true
|
||||
property real prevZoomFactor: -1
|
||||
|
||||
function fitToViewPort()
|
||||
{
|
||||
if (first) {
|
||||
first = false;
|
||||
selectionBox.targetNode = root.importScene;
|
||||
} else {
|
||||
cameraControl.focusObject(selectionBox.model, theCamera.eulerRotation, true, false);
|
||||
|
||||
if (cameraControl._zoomFactor < 0.1) {
|
||||
root.importScene.scale = root.importScene.scale.times(10);
|
||||
} else if (cameraControl._zoomFactor > 10) {
|
||||
root.importScene.scale = root.importScene.scale.times(0.1);
|
||||
} else {
|
||||
// We need one more render after zoom factor change, so only set ready when zoom factor
|
||||
// or scaling hasn't changed from the previous frame
|
||||
ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor);
|
||||
prevZoomFactor = cameraControl._zoomFactor;
|
||||
selectionBox.visible = false;
|
||||
}
|
||||
}
|
||||
// The magic number is the distance from camera default pos to origin
|
||||
_generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root,
|
||||
1040);
|
||||
}
|
||||
|
||||
SceneEnvironment {
|
||||
@@ -64,20 +45,6 @@ View3D {
|
||||
antialiasingQuality: SceneEnvironment.High
|
||||
}
|
||||
|
||||
SelectionBox {
|
||||
id: selectionBox
|
||||
view3D: root
|
||||
geometryName: "NodeNodeViewSB"
|
||||
}
|
||||
|
||||
EditCameraController {
|
||||
id: cameraControl
|
||||
camera: theCamera
|
||||
anchors.fill: parent
|
||||
view3d: root
|
||||
ignoreToolState: true
|
||||
}
|
||||
|
||||
DirectionalLight {
|
||||
eulerRotation.x: -30
|
||||
eulerRotation.y: -30
|
||||
|
@@ -32,6 +32,11 @@ View3D {
|
||||
|
||||
property Material previewMaterial
|
||||
|
||||
function fitToViewPort()
|
||||
{
|
||||
// No need to zoom this view, this is here just to avoid runtime warnings
|
||||
}
|
||||
|
||||
SceneEnvironment {
|
||||
id: sceneEnv
|
||||
antialiasingMode: SceneEnvironment.MSAA
|
||||
|
@@ -41,8 +41,6 @@ Item {
|
||||
property var modelViewComponent
|
||||
property var nodeViewComponent
|
||||
|
||||
property bool ready: false
|
||||
|
||||
function destroyView()
|
||||
{
|
||||
previewObject = null;
|
||||
@@ -58,8 +56,6 @@ Item {
|
||||
createViewForModel(obj);
|
||||
else if (obj instanceof Node)
|
||||
createViewForNode(obj);
|
||||
|
||||
previewObject = obj;
|
||||
}
|
||||
|
||||
function createViewForMaterial(material)
|
||||
@@ -70,6 +66,8 @@ Item {
|
||||
// Always recreate the view to ensure material is up to date
|
||||
if (materialViewComponent.status === Component.Ready)
|
||||
view = materialViewComponent.createObject(viewRect, {"previewMaterial": material});
|
||||
|
||||
previewObject = material;
|
||||
}
|
||||
|
||||
function createViewForModel(model)
|
||||
@@ -80,6 +78,8 @@ Item {
|
||||
// Always recreate the view to ensure model is up to date
|
||||
if (modelViewComponent.status === Component.Ready)
|
||||
view = modelViewComponent.createObject(viewRect, {"sourceModel": model});
|
||||
|
||||
previewObject = model;
|
||||
}
|
||||
|
||||
function createViewForNode(node)
|
||||
@@ -90,16 +90,13 @@ Item {
|
||||
// Always recreate the view to ensure node is up to date
|
||||
if (nodeViewComponent.status === Component.Ready)
|
||||
view = nodeViewComponent.createObject(viewRect, {"importScene": node});
|
||||
|
||||
previewObject = node;
|
||||
}
|
||||
|
||||
function afterRender()
|
||||
function fitToViewPort()
|
||||
{
|
||||
if (previewObject instanceof Node) {
|
||||
view.fitToViewPort();
|
||||
ready = view.ready;
|
||||
} else {
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
|
@@ -32,24 +32,13 @@ View3D {
|
||||
environment: sceneEnv
|
||||
camera: theCamera
|
||||
|
||||
property bool ready: false
|
||||
property real prevZoomFactor: -1
|
||||
property Model sourceModel
|
||||
|
||||
function fitToViewPort()
|
||||
{
|
||||
cameraControl.focusObject(model, theCamera.eulerRotation, true, false);
|
||||
|
||||
if (cameraControl._zoomFactor < 0.1) {
|
||||
model.scale = model.scale.times(10);
|
||||
} else if (cameraControl._zoomFactor > 10) {
|
||||
model.scale = model.scale.times(0.1);
|
||||
} else {
|
||||
// We need one more render after zoom factor change, so only set ready when zoom factor
|
||||
// or scaling hasn't changed from the previous frame
|
||||
ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor);
|
||||
prevZoomFactor = cameraControl._zoomFactor;
|
||||
}
|
||||
// The magic number is the distance from camera default pos to origin
|
||||
_generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root,
|
||||
1040);
|
||||
}
|
||||
|
||||
SceneEnvironment {
|
||||
@@ -58,14 +47,6 @@ View3D {
|
||||
antialiasingQuality: SceneEnvironment.High
|
||||
}
|
||||
|
||||
EditCameraController {
|
||||
id: cameraControl
|
||||
camera: theCamera
|
||||
anchors.fill: parent
|
||||
view3d: root
|
||||
ignoreToolState: true
|
||||
}
|
||||
|
||||
DirectionalLight {
|
||||
eulerRotation.x: -30
|
||||
eulerRotation.y: -30
|
||||
@@ -75,16 +56,15 @@ View3D {
|
||||
id: theCamera
|
||||
z: 600
|
||||
y: 600
|
||||
x: 600
|
||||
eulerRotation.x: -45
|
||||
eulerRotation.y: -45
|
||||
clipFar: 10000
|
||||
clipNear: 1
|
||||
}
|
||||
|
||||
Model {
|
||||
id: model
|
||||
readonly property bool _edit3dLocked: true // Make this non-pickable
|
||||
eulerRotation.y: 45
|
||||
|
||||
source: sourceModel.source
|
||||
geometry: sourceModel.geometry
|
||||
|
||||
|
@@ -32,30 +32,11 @@ View3D {
|
||||
environment: sceneEnv
|
||||
camera: theCamera
|
||||
|
||||
property bool ready: false
|
||||
property bool first: true
|
||||
property real prevZoomFactor: -1
|
||||
|
||||
function fitToViewPort()
|
||||
{
|
||||
if (first) {
|
||||
first = false;
|
||||
selectionBox.targetNode = root.importScene;
|
||||
} else {
|
||||
cameraControl.focusObject(selectionBox.model, theCamera.eulerRotation, true, false);
|
||||
|
||||
if (cameraControl._zoomFactor < 0.1) {
|
||||
root.importScene.scale = root.importScene.scale.times(10);
|
||||
} else if (cameraControl._zoomFactor > 10) {
|
||||
root.importScene.scale = root.importScene.scale.times(0.1);
|
||||
} else {
|
||||
// We need one more render after zoom factor change, so only set ready when zoom factor
|
||||
// or scaling hasn't changed from the previous frame
|
||||
ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor);
|
||||
prevZoomFactor = cameraControl._zoomFactor;
|
||||
selectionBox.visible = false;
|
||||
}
|
||||
}
|
||||
// The magic number is the distance from camera default pos to origin
|
||||
_generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root,
|
||||
1040);
|
||||
}
|
||||
|
||||
SceneEnvironment {
|
||||
@@ -64,20 +45,6 @@ View3D {
|
||||
antialiasingQuality: SceneEnvironment.High
|
||||
}
|
||||
|
||||
SelectionBox {
|
||||
id: selectionBox
|
||||
view3D: root
|
||||
geometryName: "NodeNodeViewSB"
|
||||
}
|
||||
|
||||
EditCameraController {
|
||||
id: cameraControl
|
||||
camera: theCamera
|
||||
anchors.fill: parent
|
||||
view3d: root
|
||||
ignoreToolState: true
|
||||
}
|
||||
|
||||
DirectionalLight {
|
||||
eulerRotation.x: -30
|
||||
eulerRotation.y: -30
|
||||
|
@@ -55,6 +55,11 @@ const QString _globalStateId = QStringLiteral("@GTS"); // global tool state
|
||||
const QString _lastSceneIdKey = QStringLiteral("lastSceneId");
|
||||
const QString _rootSizeKey = QStringLiteral("rootSize");
|
||||
|
||||
static const float floatMin = std::numeric_limits<float>::lowest();
|
||||
static const float floatMax = std::numeric_limits<float>::max();
|
||||
static const QVector3D maxVec = QVector3D(floatMax, floatMax, floatMax);
|
||||
static const QVector3D minVec = QVector3D(floatMin, floatMin, floatMin);
|
||||
|
||||
GeneralHelper::GeneralHelper()
|
||||
: QObject()
|
||||
{
|
||||
@@ -269,6 +274,37 @@ QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaul
|
||||
return QVector4D(lookAt, cameraZoomFactor);
|
||||
}
|
||||
|
||||
// This function can be used to synchronously focus camera on a node, which doesn't have to be
|
||||
// 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)
|
||||
{
|
||||
QVector3D minBounds;
|
||||
QVector3D maxBounds;
|
||||
|
||||
getBounds(viewPort, node, minBounds, maxBounds);
|
||||
|
||||
QVector3D extents = maxBounds - minBounds;
|
||||
QVector3D lookAt = minBounds + (extents / 2.f);
|
||||
float maxExtent = qMax(extents.x(), qMax(extents.y(), extents.z()));
|
||||
|
||||
// Reset camera position to default zoom
|
||||
QMatrix4x4 m = camera->sceneTransform();
|
||||
const float *dataPtr(m.data());
|
||||
QVector3D newLookVector(dataPtr[8], dataPtr[9], dataPtr[10]);
|
||||
newLookVector.normalize();
|
||||
newLookVector *= defaultLookAtDistance;
|
||||
|
||||
camera->setPosition(lookAt + newLookVector);
|
||||
|
||||
float newZoomFactor = maxExtent / 725.f; // Divisor taken from focusNodesToCamera function
|
||||
|
||||
zoomCamera(viewPort, camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false);
|
||||
}
|
||||
|
||||
// Aligns any cameras found in nodes list to a camera.
|
||||
// Only position and rotation are copied, rest of the camera properties stay the same.
|
||||
void GeneralHelper::alignCameras(QQuick3DCamera *camera, const QVariant &nodes)
|
||||
@@ -727,6 +763,129 @@ QVector3D GeneralHelper::pivotScenePosition(QQuick3DNode *node) const
|
||||
return mat44::getPosition(sceneTransform);
|
||||
}
|
||||
|
||||
// Calculate bounds for given node, including all child nodes.
|
||||
// Returns true if the tree contains at least one Model node.
|
||||
bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVector3D &minBounds,
|
||||
QVector3D &maxBounds, bool recursive)
|
||||
{
|
||||
if (!node) {
|
||||
const float halfExtent = 100.f;
|
||||
minBounds = {-halfExtent, -halfExtent, -halfExtent};
|
||||
maxBounds = {halfExtent, halfExtent, halfExtent};
|
||||
return false;
|
||||
}
|
||||
|
||||
QMatrix4x4 localTransform;
|
||||
auto nodePriv = QQuick3DObjectPrivate::get(node);
|
||||
auto renderNode = static_cast<QSSGRenderNode *>(nodePriv->spatialNode);
|
||||
|
||||
if (recursive && renderNode) {
|
||||
if (renderNode->flags.testFlag(QSSGRenderNode::Flag::TransformDirty))
|
||||
renderNode->calculateLocalTransform();
|
||||
localTransform = renderNode->localTransform;
|
||||
}
|
||||
|
||||
QVector3D localMinBounds = maxVec;
|
||||
QVector3D localMaxBounds = minVec;
|
||||
|
||||
// Find bounds for children
|
||||
QVector<QVector3D> minBoundsVec;
|
||||
QVector<QVector3D> maxBoundsVec;
|
||||
const auto children = node->childItems();
|
||||
bool hasModel = false;
|
||||
for (const auto child : children) {
|
||||
if (auto childNode = qobject_cast<QQuick3DNode *>(child)) {
|
||||
QVector3D newMinBounds = minBounds;
|
||||
QVector3D newMaxBounds = maxBounds;
|
||||
hasModel = getBounds(view3D, childNode, newMinBounds, newMaxBounds, true);
|
||||
// Ignore any subtrees that do not have Model in them as we don't need those
|
||||
// for visual bounds calculations
|
||||
if (hasModel) {
|
||||
minBoundsVec << newMinBounds;
|
||||
maxBoundsVec << newMaxBounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto combineMinBounds = [](QVector3D &target, const QVector3D &source) {
|
||||
target.setX(qMin(source.x(), target.x()));
|
||||
target.setY(qMin(source.y(), target.y()));
|
||||
target.setZ(qMin(source.z(), target.z()));
|
||||
};
|
||||
auto combineMaxBounds = [](QVector3D &target, const QVector3D &source) {
|
||||
target.setX(qMax(source.x(), target.x()));
|
||||
target.setY(qMax(source.y(), target.y()));
|
||||
target.setZ(qMax(source.z(), target.z()));
|
||||
};
|
||||
auto transformCorner = [&](const QMatrix4x4 &m, QVector3D &minTarget, QVector3D &maxTarget,
|
||||
const QVector3D &corner) {
|
||||
QVector3D mappedCorner = m.map(corner);
|
||||
combineMinBounds(minTarget, mappedCorner);
|
||||
combineMaxBounds(maxTarget, mappedCorner);
|
||||
};
|
||||
auto transformCorners = [&](const QMatrix4x4 &m, QVector3D &minTarget, QVector3D &maxTarget,
|
||||
const QVector3D &minCorner, const QVector3D &maxCorner) {
|
||||
transformCorner(m, minTarget, maxTarget, minCorner);
|
||||
transformCorner(m, minTarget, maxTarget, maxCorner);
|
||||
transformCorner(m, minTarget, maxTarget, QVector3D(minCorner.x(), minCorner.y(), maxCorner.z()));
|
||||
transformCorner(m, minTarget, maxTarget, QVector3D(minCorner.x(), maxCorner.y(), minCorner.z()));
|
||||
transformCorner(m, minTarget, maxTarget, QVector3D(maxCorner.x(), minCorner.y(), minCorner.z()));
|
||||
transformCorner(m, minTarget, maxTarget, QVector3D(minCorner.x(), maxCorner.y(), maxCorner.z()));
|
||||
transformCorner(m, minTarget, maxTarget, QVector3D(maxCorner.x(), maxCorner.y(), minCorner.z()));
|
||||
transformCorner(m, minTarget, maxTarget, QVector3D(maxCorner.x(), minCorner.y(), maxCorner.z()));
|
||||
};
|
||||
|
||||
// Combine all child bounds
|
||||
for (const auto &newBounds : qAsConst(minBoundsVec))
|
||||
combineMinBounds(localMinBounds, newBounds);
|
||||
for (const auto &newBounds : qAsConst(maxBoundsVec))
|
||||
combineMaxBounds(localMaxBounds, newBounds);
|
||||
|
||||
if (qobject_cast<QQuick3DModel *>(node)) {
|
||||
if (auto renderModel = static_cast<QSSGRenderModel *>(renderNode)) {
|
||||
QWindow *window = static_cast<QWindow *>(view3D->window());
|
||||
if (window) {
|
||||
QSSGRef<QSSGRenderContextInterface> context;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
context = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window));
|
||||
#else
|
||||
context = QQuick3DObjectPrivate::get(node)->sceneManager->rci;
|
||||
#endif
|
||||
if (!context.isNull()) {
|
||||
auto bufferManager = context->bufferManager();
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
|
||||
QSSGBounds3 bounds = renderModel->getModelBounds(bufferManager);
|
||||
#else
|
||||
QSSGBounds3 bounds = bufferManager->getModelBounds(renderModel);
|
||||
#endif
|
||||
QVector3D center = bounds.center();
|
||||
QVector3D extents = bounds.extents();
|
||||
QVector3D localMin = center - extents;
|
||||
QVector3D localMax = center + extents;
|
||||
|
||||
combineMinBounds(localMinBounds, localMin);
|
||||
combineMaxBounds(localMaxBounds, localMax);
|
||||
|
||||
hasModel = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
combineMinBounds(localMinBounds, {});
|
||||
combineMaxBounds(localMaxBounds, {});
|
||||
}
|
||||
|
||||
if (localMaxBounds == minVec) {
|
||||
localMinBounds = {};
|
||||
localMaxBounds = {};
|
||||
}
|
||||
|
||||
// Transform local space bounding box to parent space
|
||||
transformCorners(localTransform, minBounds, maxBounds, localMinBounds, localMaxBounds);
|
||||
|
||||
return hasModel;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -72,6 +72,9 @@ public:
|
||||
const QVariant &nodes, QQuick3DViewport *viewPort,
|
||||
float oldZoom, bool updateZoom = true,
|
||||
bool closeUp = false);
|
||||
Q_INVOKABLE void calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node,
|
||||
QQuick3DViewport *viewPort,
|
||||
float defaultLookAtDistance);
|
||||
Q_INVOKABLE void alignCameras(QQuick3DCamera *camera, const QVariant &nodes);
|
||||
Q_INVOKABLE QVector3D alignView(QQuick3DCamera *camera, const QVariant &nodes,
|
||||
const QVector3D &lookAtPoint);
|
||||
@@ -126,6 +129,8 @@ protected:
|
||||
private:
|
||||
void handlePendingToolStateUpdate();
|
||||
QVector3D pivotScenePosition(QQuick3DNode *node) const;
|
||||
bool getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVector3D &minBounds,
|
||||
QVector3D &maxBounds, bool recursive = false);
|
||||
|
||||
QTimer m_overlayUpdateTimer;
|
||||
QTimer m_toolStateUpdateTimer;
|
||||
|
@@ -1588,6 +1588,11 @@ bool NodeInstanceServer::isInformationServer() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NodeInstanceServer::isPreviewServer() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static QString baseProperty(const QString &property)
|
||||
{
|
||||
int index = property.indexOf('.');
|
||||
|
@@ -229,8 +229,10 @@ public:
|
||||
|
||||
virtual QImage grabWindow() = 0;
|
||||
virtual QImage grabItem(QQuickItem *item) = 0;
|
||||
virtual bool renderWindow() = 0;
|
||||
|
||||
virtual bool isInformationServer() const;
|
||||
virtual bool isPreviewServer() const;
|
||||
void addAnimation(QQuickAbstractAnimation *animation);
|
||||
QVector<QQuickAbstractAnimation *> animations() const;
|
||||
QVariant animationDefaultValue(int index) const;
|
||||
|
@@ -180,6 +180,11 @@ bool ObjectNodeInstance::isLayoutable() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectNodeInstance::isRenderable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectNodeInstance::equalGraphicsItem(QGraphicsItem * /*item*/) const
|
||||
{
|
||||
return false;
|
||||
|
@@ -103,6 +103,7 @@ public:
|
||||
virtual bool isQuickItem() const;
|
||||
virtual bool isQuickWindow() const;
|
||||
virtual bool isLayoutable() const;
|
||||
virtual bool isRenderable() const;
|
||||
|
||||
virtual bool equalGraphicsItem(QGraphicsItem *item) const;
|
||||
|
||||
|
@@ -44,10 +44,10 @@ QImage renderImage(ServerNodeInstance rootNodeInstance)
|
||||
|
||||
QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize();
|
||||
if (previewImageSize.isEmpty())
|
||||
previewImageSize = {300, 300};
|
||||
previewImageSize = {150, 150};
|
||||
|
||||
if (previewImageSize.width() > 300 || previewImageSize.height() > 300)
|
||||
previewImageSize.scale({300, 300}, Qt::KeepAspectRatio);
|
||||
if (previewImageSize.width() > 150 || previewImageSize.height() > 150)
|
||||
previewImageSize.scale({150, 150}, Qt::KeepAspectRatio);
|
||||
|
||||
QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize);
|
||||
|
||||
@@ -68,7 +68,8 @@ void Qt5CaptureImageNodeInstanceServer::collectItemChangesAndSendChangeCommands(
|
||||
inFunction = true;
|
||||
|
||||
auto rooNodeInstance = rootNodeInstance();
|
||||
rooNodeInstance.rootQuickItem()->setClip(true);
|
||||
if (QQuickItem *qitem = rooNodeInstance.rootQuickItem())
|
||||
qitem->setClip(true);
|
||||
|
||||
DesignerSupport::polishItems(quickWindow());
|
||||
|
||||
|
@@ -1007,17 +1007,17 @@ void Qt5InformationNodeInstanceServer::doRenderModelNodeImageView()
|
||||
void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
|
||||
{
|
||||
#ifdef QUICK3D_MODULE
|
||||
m_modelNode3DImageViewAsyncData.cleanup();
|
||||
if (m_modelNode3DImageViewData.rootItem) {
|
||||
QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "destroyView");
|
||||
if (!m_modelNode3DImageViewData.contentItem)
|
||||
m_modelNode3DImageViewData.contentItem = getContentItemForRendering(m_modelNode3DImageViewData.rootItem);
|
||||
|
||||
QImage renderImage;
|
||||
if (m_modelNodePreviewImageCache.contains(m_modelNodePreviewImageCommand.componentPath())) {
|
||||
m_modelNode3DImageViewAsyncData.renderImage
|
||||
= m_modelNodePreviewImageCache[m_modelNodePreviewImageCommand.componentPath()];
|
||||
modelNode3DImageViewSendImageToCreator();
|
||||
renderImage = m_modelNodePreviewImageCache[m_modelNodePreviewImageCommand.componentPath()];
|
||||
} else {
|
||||
bool createdFromComponent = false;
|
||||
QObject *instanceObj = nullptr;
|
||||
ServerNodeInstance instance = instanceForId(m_modelNodePreviewImageCommand.instanceId());
|
||||
if (!m_modelNodePreviewImageCommand.componentPath().isEmpty()
|
||||
&& instance.isSubclassOf("QQuick3DNode")) {
|
||||
@@ -1026,15 +1026,14 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
|
||||
// wouldn't want the children of the Node to appear in the preview.
|
||||
QQmlComponent component(engine());
|
||||
component.loadUrl(QUrl::fromLocalFile(m_modelNodePreviewImageCommand.componentPath()));
|
||||
m_modelNode3DImageViewAsyncData.instanceObj = qobject_cast<QQuick3DObject *>(component.create());
|
||||
if (!m_modelNode3DImageViewAsyncData.instanceObj) {
|
||||
instanceObj = qobject_cast<QQuick3DObject *>(component.create());
|
||||
if (!instanceObj) {
|
||||
qWarning() << "Could not create preview component: " << component.errors();
|
||||
m_modelNode3DImageViewAsyncData.cleanup();
|
||||
return;
|
||||
}
|
||||
m_modelNode3DImageViewAsyncData.createdFromComponent = true;
|
||||
createdFromComponent = true;
|
||||
} else {
|
||||
m_modelNode3DImageViewAsyncData.instanceObj = instance.internalObject();
|
||||
instanceObj = instance.internalObject();
|
||||
}
|
||||
QSize renderSize = m_modelNodePreviewImageCommand.size();
|
||||
if (Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) {
|
||||
@@ -1055,41 +1054,18 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
m_modelNode3DImageViewData.rootItem, "createViewForObject",
|
||||
Q_ARG(QVariant, objectToVariant(m_modelNode3DImageViewAsyncData.instanceObj)));
|
||||
Q_ARG(QVariant, objectToVariant(instanceObj)));
|
||||
|
||||
// Selection box geometry updates have an asynchronous step, so we need to do rendering
|
||||
// in asynchronous steps as well, since we are adjusting the selection box geometry
|
||||
// while finding correct zoom level.
|
||||
m_modelNode3DImageViewAsyncData.timer.start();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::modelNode3DImageViewSendImageToCreator()
|
||||
{
|
||||
if (!m_modelNode3DImageViewAsyncData.renderImage.isNull()) {
|
||||
// Key number is selected so that it is unlikely to conflict other ImageContainer use.
|
||||
ImageContainer imgContainer(m_modelNodePreviewImageCommand.instanceId(), {}, 2100000001);
|
||||
imgContainer.setImage(m_modelNode3DImageViewAsyncData.renderImage);
|
||||
|
||||
// send the rendered image to creator process
|
||||
nodeInstanceClient()->handlePuppetToCreatorCommand(
|
||||
{PuppetToCreatorCommand::RenderModelNodePreviewImage,
|
||||
QVariant::fromValue(imgContainer)});
|
||||
|
||||
m_modelNode3DImageViewAsyncData.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::modelNode3DImageViewRenderStep()
|
||||
{
|
||||
++m_modelNode3DImageViewAsyncData.count;
|
||||
// Need to render twice, first render updates spatial nodes
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (i == 1)
|
||||
QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "fitToViewPort"
|
||||
, Qt::DirectConnection);
|
||||
|
||||
updateNodesRecursive(m_modelNode3DImageViewData.contentItem);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
if (Internal::QuickItemNodeInstance::unifiedRenderPath()) {
|
||||
m_modelNode3DImageViewAsyncData.renderImage = m_modelNode3DImageViewData.window->grabWindow();
|
||||
renderImage = m_modelNode3DImageViewData.window->grabWindow();
|
||||
} else {
|
||||
// Fake render loop signaling to update things like QML items as 3D textures
|
||||
m_modelNode3DImageViewData.window->beforeSynchronizing();
|
||||
@@ -1097,27 +1073,34 @@ void Qt5InformationNodeInstanceServer::modelNode3DImageViewRenderStep()
|
||||
|
||||
QSizeF size = qobject_cast<QQuickItem *>(m_modelNode3DImageViewData.contentItem)->size();
|
||||
QRectF renderRect(QPointF(0., 0.), size);
|
||||
m_modelNode3DImageViewAsyncData.renderImage
|
||||
= designerSupport()->renderImageForItem(m_modelNode3DImageViewData.contentItem,
|
||||
renderImage = designerSupport()->renderImageForItem(m_modelNode3DImageViewData.contentItem,
|
||||
renderRect, size.toSize());
|
||||
m_modelNode3DImageViewData.window->afterRendering();
|
||||
}
|
||||
#else
|
||||
m_modelNode3DImageViewAsyncData.renderImage = grabRenderControl(m_modelNode3DImageViewData);
|
||||
renderImage = grabRenderControl(m_modelNode3DImageViewData);
|
||||
#endif
|
||||
QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "afterRender");
|
||||
const bool ready = QQmlProperty::read(m_modelNode3DImageViewData.rootItem, "ready").value<bool>();
|
||||
if (ready || m_modelNode3DImageViewAsyncData.count >= 10) {
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "destroyView");
|
||||
if (m_modelNode3DImageViewAsyncData.createdFromComponent) {
|
||||
|
||||
if (createdFromComponent) {
|
||||
// If component changes, puppet will need a reset anyway, so we can cache the image
|
||||
m_modelNodePreviewImageCache.insert(m_modelNodePreviewImageCommand.componentPath(),
|
||||
m_modelNode3DImageViewAsyncData.renderImage);
|
||||
renderImage);
|
||||
delete instanceObj;
|
||||
}
|
||||
modelNode3DImageViewSendImageToCreator();
|
||||
} else {
|
||||
m_modelNode3DImageViewAsyncData.timer.start();
|
||||
}
|
||||
// Key number is selected so that it is unlikely to conflict other ImageContainer use.
|
||||
ImageContainer imgContainer(m_modelNodePreviewImageCommand.instanceId(), {}, 2100000001);
|
||||
imgContainer.setImage(renderImage);
|
||||
|
||||
// send the rendered image to creator process
|
||||
nodeInstanceClient()->handlePuppetToCreatorCommand(
|
||||
{PuppetToCreatorCommand::RenderModelNodePreviewImage,
|
||||
QVariant::fromValue(imgContainer)});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static QRectF itemBoundingRect(QQuickItem *item)
|
||||
@@ -1234,7 +1217,6 @@ Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceC
|
||||
m_render3DEditViewTimer.setSingleShot(true);
|
||||
m_inputEventTimer.setSingleShot(true);
|
||||
m_renderModelNodeImageViewTimer.setSingleShot(true);
|
||||
m_modelNode3DImageViewAsyncData.timer.setSingleShot(true);
|
||||
m_dynamicAddObjectTimer.setSingleShot(true);
|
||||
|
||||
#ifdef FPS_COUNTER
|
||||
@@ -1260,7 +1242,6 @@ Qt5InformationNodeInstanceServer::~Qt5InformationNodeInstanceServer()
|
||||
m_render3DEditViewTimer.stop();
|
||||
m_inputEventTimer.stop();
|
||||
m_renderModelNodeImageViewTimer.stop();
|
||||
m_modelNode3DImageViewAsyncData.timer.stop();
|
||||
m_dynamicAddObjectTimer.stop();
|
||||
|
||||
if (m_editView3DData.rootItem)
|
||||
@@ -1677,8 +1658,6 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QList<ServerNodeIns
|
||||
this, &Qt5InformationNodeInstanceServer::doRender3DEditView);
|
||||
QObject::connect(&m_inputEventTimer, &QTimer::timeout,
|
||||
this, &Qt5InformationNodeInstanceServer::handleInputEvents);
|
||||
QObject::connect(&m_modelNode3DImageViewAsyncData.timer, &QTimer::timeout,
|
||||
this, &Qt5InformationNodeInstanceServer::modelNode3DImageViewRenderStep);
|
||||
QObject::connect(&m_dynamicAddObjectTimer, &QTimer::timeout,
|
||||
this, &Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout);
|
||||
|
||||
@@ -2165,7 +2144,6 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
|
||||
|
||||
void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command)
|
||||
{
|
||||
m_modelNode3DImageViewAsyncData.timer.stop();
|
||||
m_modelNodePreviewImageCommand = command;
|
||||
renderModelNodeImageView();
|
||||
}
|
||||
|
@@ -140,8 +140,6 @@ private:
|
||||
void renderModelNodeImageView();
|
||||
void doRenderModelNodeImageView();
|
||||
void doRenderModelNode3DImageView();
|
||||
void modelNode3DImageViewSendImageToCreator();
|
||||
void modelNode3DImageViewRenderStep();
|
||||
void doRenderModelNode2DImageView();
|
||||
void updateLockedAndHiddenStates(const QSet<ServerNodeInstance> &instances);
|
||||
void handleInputEvents();
|
||||
@@ -190,26 +188,6 @@ private:
|
||||
QObject *m_3dHelper = nullptr;
|
||||
int m_need3DEditViewRender = 0;
|
||||
QSet<QObject *> m_dynamicObjectConstructors;
|
||||
|
||||
struct ModelNode3DImageViewAsyncData {
|
||||
QTimer timer;
|
||||
QImage renderImage;
|
||||
int count = 0;
|
||||
bool createdFromComponent = false;
|
||||
QObject *instanceObj = nullptr;
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
timer.stop();
|
||||
count = 0;
|
||||
renderImage = {};
|
||||
if (createdFromComponent)
|
||||
delete instanceObj;
|
||||
instanceObj = nullptr;
|
||||
createdFromComponent = false;
|
||||
}
|
||||
};
|
||||
ModelNode3DImageViewAsyncData m_modelNode3DImageViewAsyncData;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -70,6 +70,7 @@ public:
|
||||
|
||||
QImage grabWindow() override;
|
||||
QImage grabItem(QQuickItem *item) override;
|
||||
bool renderWindow() override;
|
||||
|
||||
static QQuickItem *parentEffectItem(QQuickItem *item);
|
||||
|
||||
@@ -97,7 +98,6 @@ protected:
|
||||
|
||||
virtual bool initRhi(RenderViewData &viewData);
|
||||
virtual QImage grabRenderControl(RenderViewData &viewData);
|
||||
virtual bool renderWindow();
|
||||
|
||||
private:
|
||||
RenderViewData m_viewData;
|
||||
|
@@ -133,4 +133,9 @@ void Qt5PreviewNodeInstanceServer::changePreviewImageSize(
|
||||
collectItemChangesAndSendChangeCommands();
|
||||
}
|
||||
|
||||
bool Qt5PreviewNodeInstanceServer::isPreviewServer() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -39,6 +39,7 @@ public:
|
||||
void changeState(const ChangeStateCommand &command) override;
|
||||
void removeSharedMemory(const RemoveSharedMemoryCommand &command) override;
|
||||
void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) override;
|
||||
bool isPreviewServer() const override;
|
||||
|
||||
QImage renderPreviewImage();
|
||||
|
||||
|
@@ -26,6 +26,8 @@
|
||||
#include "quick3dnodeinstance.h"
|
||||
#include "qt5nodeinstanceserver.h"
|
||||
#include "qt5informationnodeinstanceserver.h"
|
||||
#include "quickitemnodeinstance.h"
|
||||
#include "../editor3d/generalhelper.h"
|
||||
|
||||
#include <qmlprivategate.h>
|
||||
|
||||
@@ -37,6 +39,7 @@
|
||||
#include <cmath>
|
||||
|
||||
#ifdef QUICK3D_MODULE
|
||||
#include <private/qquick3dobject_p.h>
|
||||
#include <private/qquick3dnode_p.h>
|
||||
#include <private/qquick3dmodel_p.h>
|
||||
#include <private/qquick3dnode_p_p.h>
|
||||
@@ -45,8 +48,10 @@
|
||||
#if defined(QUICK3D_ASSET_UTILS_MODULE) && QT_VERSION > QT_VERSION_CHECK(6, 2, 0)
|
||||
#include <private/qquick3druntimeloader_p.h>
|
||||
#endif
|
||||
#include <private/qquickstategroup_p.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace QmlDesigner {
|
||||
namespace Internal {
|
||||
|
||||
@@ -57,6 +62,7 @@ Quick3DNodeInstance::Quick3DNodeInstance(QObject *node)
|
||||
|
||||
Quick3DNodeInstance::~Quick3DNodeInstance()
|
||||
{
|
||||
delete m_dummyRootView;
|
||||
}
|
||||
|
||||
void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNodeInstance,
|
||||
@@ -87,10 +93,123 @@ void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
// In case this is the scene root, we need to create a dummy View3D for the scene
|
||||
// in preview puppets
|
||||
if (instanceId() == 0 && nodeInstanceServer()->isPreviewServer()) {
|
||||
auto helper = new QmlDesigner::Internal::GeneralHelper();
|
||||
engine()->rootContext()->setContextProperty("_generalHelper", helper);
|
||||
|
||||
QQmlComponent component(engine());
|
||||
component.loadUrl(QUrl("qrc:/qtquickplugin/mockfiles/qt6/ModelNode3DImageView.qml"));
|
||||
m_dummyRootView = qobject_cast<QQuickItem *>(component.create());
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
m_dummyRootView, "createViewForNode",
|
||||
Q_ARG(QVariant, QVariant::fromValue(object())));
|
||||
|
||||
nodeInstanceServer()->setRootItem(m_dummyRootView);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
ObjectNodeInstance::initialize(objectNodeInstance, flags);
|
||||
}
|
||||
|
||||
QImage Quick3DNodeInstance::renderImage() const
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
if (!isRootNodeInstance() || !m_dummyRootView)
|
||||
return {};
|
||||
|
||||
QSize size(640, 480);
|
||||
nodeInstanceServer()->quickWindow()->resize(size);
|
||||
m_dummyRootView->setSize(size);
|
||||
|
||||
// Just render the window once to update spatial nodes
|
||||
nodeInstanceServer()->renderWindow();
|
||||
|
||||
QMetaObject::invokeMethod(m_dummyRootView, "fitToViewPort", Qt::DirectConnection);
|
||||
|
||||
QRectF renderBoundingRect = m_dummyRootView->boundingRect();
|
||||
QImage renderImage;
|
||||
|
||||
if (QuickItemNodeInstance::unifiedRenderPath()) {
|
||||
renderImage = nodeInstanceServer()->grabWindow();
|
||||
renderImage = renderImage.copy(renderBoundingRect.toRect());
|
||||
} else {
|
||||
renderImage = nodeInstanceServer()->grabItem(m_dummyRootView);
|
||||
}
|
||||
|
||||
// When grabbing an offscreen window the device pixel ratio is 1
|
||||
renderImage.setDevicePixelRatio(1);
|
||||
|
||||
return renderImage;
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
QImage Quick3DNodeInstance::renderPreviewImage(const QSize &previewImageSize) const
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
if (!isRootNodeInstance() || !m_dummyRootView)
|
||||
return {};
|
||||
|
||||
nodeInstanceServer()->quickWindow()->resize(previewImageSize);
|
||||
m_dummyRootView->setSize(previewImageSize);
|
||||
|
||||
// Just render the window once to update spatial nodes
|
||||
nodeInstanceServer()->renderWindow();
|
||||
|
||||
QMetaObject::invokeMethod(m_dummyRootView, "fitToViewPort", Qt::DirectConnection);
|
||||
|
||||
QRectF previewItemBoundingRect = boundingRect();
|
||||
|
||||
if (previewItemBoundingRect.isValid()) {
|
||||
const QSize size = previewImageSize;
|
||||
if (m_dummyRootView->isVisible()) {
|
||||
QImage image;
|
||||
image = nodeInstanceServer()->grabWindow();
|
||||
image = image.copy(previewItemBoundingRect.toRect());
|
||||
image = image.scaledToWidth(size.width());
|
||||
return image;
|
||||
} else {
|
||||
QImage transparentImage(size, QImage::Format_ARGB32_Premultiplied);
|
||||
transparentImage.fill(Qt::transparent);
|
||||
return transparentImage;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Quick3DNodeInstance::isRenderable() const
|
||||
{
|
||||
return m_dummyRootView;
|
||||
}
|
||||
|
||||
QRectF Quick3DNodeInstance::boundingRect() const
|
||||
{
|
||||
if (m_dummyRootView)
|
||||
return m_dummyRootView->boundingRect();
|
||||
return ObjectNodeInstance::boundingRect();
|
||||
}
|
||||
|
||||
QList<ServerNodeInstance> Quick3DNodeInstance::stateInstances() const
|
||||
{
|
||||
QList<ServerNodeInstance> instanceList;
|
||||
#ifdef QUICK3D_MODULE
|
||||
if (auto obj3D = quick3DNode()) {
|
||||
const QList<QQuickState *> stateList = QQuick3DObjectPrivate::get(obj3D)->_states()->states();
|
||||
for (QQuickState *state : stateList) {
|
||||
if (state && nodeInstanceServer()->hasInstanceForObject(state))
|
||||
instanceList.append(nodeInstanceServer()->instanceForObject(state));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return instanceList;
|
||||
}
|
||||
|
||||
Qt5NodeInstanceServer *Quick3DNodeInstance::qt5NodeInstanceServer() const
|
||||
{
|
||||
return qobject_cast<Qt5NodeInstanceServer *>(nodeInstanceServer());
|
||||
|
@@ -47,12 +47,22 @@ public:
|
||||
void initialize(const ObjectNodeInstance::Pointer &objectNodeInstance,
|
||||
InstanceContainer::NodeFlags flags) override;
|
||||
|
||||
QImage renderImage() const override;
|
||||
QImage renderPreviewImage(const QSize &previewImageSize) const override;
|
||||
|
||||
bool isRenderable() const override;
|
||||
QRectF boundingRect() const override;
|
||||
|
||||
QList<ServerNodeInstance> stateInstances() const override;
|
||||
|
||||
protected:
|
||||
explicit Quick3DNodeInstance(QObject *node);
|
||||
|
||||
private:
|
||||
Qt5NodeInstanceServer *qt5NodeInstanceServer() const;
|
||||
QQuick3DNode *quick3DNode() const;
|
||||
|
||||
QQuickItem *m_dummyRootView = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -571,6 +571,11 @@ bool QuickItemNodeInstance::isQuickItem() const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QuickItemNodeInstance::isRenderable() const
|
||||
{
|
||||
return quickItem() && (!s_unifiedRenderPath || isRootNodeInstance());
|
||||
}
|
||||
|
||||
QList<ServerNodeInstance> QuickItemNodeInstance::stateInstances() const
|
||||
{
|
||||
QList<ServerNodeInstance> instanceList;
|
||||
|
@@ -94,6 +94,7 @@ public:
|
||||
bool isResizable() const override;
|
||||
bool isMovable() const override;
|
||||
bool isQuickItem() const override;
|
||||
bool isRenderable() const override;
|
||||
|
||||
QList<ServerNodeInstance> stateInstances() const override;
|
||||
|
||||
|
@@ -154,7 +154,7 @@ void ServerNodeInstance::setNodeSource(const QString &source)
|
||||
|
||||
bool ServerNodeInstance::holdsGraphical() const
|
||||
{
|
||||
return m_nodeInstance->isQuickItem();
|
||||
return m_nodeInstance->isRenderable();
|
||||
}
|
||||
|
||||
bool ServerNodeInstance::isComponentWrap() const
|
||||
|
@@ -48,7 +48,8 @@ QString ItemLibraryItem::typeName() const
|
||||
|
||||
QString ItemLibraryItem::itemLibraryIconPath() const
|
||||
{
|
||||
if (m_itemLibraryEntry.customComponentSource().isEmpty()) {
|
||||
if (m_itemLibraryEntry.customComponentSource().isEmpty()
|
||||
|| !m_itemLibraryEntry.libraryEntryIconPath().isEmpty()) {
|
||||
return QStringLiteral("image://qmldesigner_itemlibrary/")
|
||||
+ m_itemLibraryEntry.libraryEntryIconPath();
|
||||
} else {
|
||||
|
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>651</width>
|
||||
<height>318</height>
|
||||
<width>517</width>
|
||||
<height>166</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -90,8 +90,8 @@
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>300</height>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
@@ -163,7 +163,7 @@
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
<verstretch>3</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
|
@@ -90,7 +90,8 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
|
||||
|
||||
model->setRewriterView(&rewriterView);
|
||||
|
||||
if (rewriterView.inErrorState() || !rewriterView.rootModelNode().metaInfo().isGraphicalItem()) {
|
||||
if (rewriterView.inErrorState() || (!rewriterView.rootModelNode().metaInfo().isGraphicalItem()
|
||||
&& !rewriterView.rootModelNode().isSubclassOf("Quick3D.Node") )) {
|
||||
if (abortCallback)
|
||||
abortCallback(ImageCache::AbortReason::Failed);
|
||||
return;
|
||||
|
@@ -420,6 +420,7 @@ void SubComponentManager::parseQuick3DAssetsItem(const QString &importUrl, const
|
||||
itemLibraryEntry.setType(type.toUtf8(), 1, 0);
|
||||
itemLibraryEntry.setName(name);
|
||||
itemLibraryEntry.setCategory(::QmlDesigner::SubComponentManager::tr("My 3D Components"));
|
||||
itemLibraryEntry.setCustomComponentSource(qmlIt.fileInfo().absoluteFilePath());
|
||||
itemLibraryEntry.setRequiredImport(importUrl);
|
||||
QString iconPath = qmlIt.fileInfo().absolutePath() + '/'
|
||||
+ Constants::QUICK_3D_ASSET_ICON_DIR + '/' + name
|
||||
|
Reference in New Issue
Block a user