QmlPuppet: Fix multiselection fit object in 3D editor

Multiselection is now properly fit to the view when fit object is used.

Fixes: QDS-4608
Change-Id: Ia80133fc861bd177b9102423ebef37b592c74758
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Miikka Heikkinen
2021-07-01 16:24:02 +03:00
parent 111f1465c8
commit 18f24ffd68
6 changed files with 121 additions and 55 deletions

View File

@@ -88,14 +88,21 @@ Item {
}
function focusObject(targetObject, rotation, updateZoom, closeUp)
function focusObject(targetNodes, rotation, updateZoom, closeUp)
{
if (!camera)
return;
// targetNodes could be a list of nodes or a single node
var nodes = [];
if (targetNodes instanceof Node)
nodes.push(targetNodes);
else
nodes = targetNodes
camera.eulerRotation = rotation;
var newLookAtAndZoom = _generalHelper.focusObjectToCamera(
camera, _defaultCameraLookAtDistance, targetObject, view3d, _zoomFactor,
var newLookAtAndZoom = _generalHelper.focusNodesToCamera(
camera, _defaultCameraLookAtDistance, nodes, view3d, _zoomFactor,
updateZoom, closeUp);
_lookAtPoint = newLookAtAndZoom.toVector3d();
_zoomFactor = newLookAtAndZoom.w;

View File

@@ -176,9 +176,16 @@ Item {
function fitToView()
{
if (editView) {
var targetNode = selectionBoxes.length > 0
? selectionBoxes[0].model : null;
cameraControl.focusObject(targetNode, editView.camera.eulerRotation, true, false);
var boxModels = [];
if (selectedNodes.length > 1) {
for (var i = 0; i < selectedNodes.length; ++i) {
if (selectionBoxes.length > i)
boxModels.push(selectionBoxes[i].model)
}
} else if (selectedNodes.length > 0 && selectionBoxes.length > 0) {
boxModels.push(selectionBoxes[0].model);
}
cameraControl.focusObject(boxModels, editView.camera.eulerRotation, true, false);
}
}
@@ -857,7 +864,8 @@ Item {
width: 100
height: width
editCameraCtrl: cameraControl
selectedNode : viewRoot.selectedNodes.length ? selectionBoxes[0].model : null
selectedNode: viewRoot.selectedNodes.length === 1 ? viewRoot.selectionBoxes[0].model
: viewRoot.selectedNode
}
Text {

View File

@@ -88,14 +88,21 @@ Item {
}
function focusObject(targetObject, rotation, updateZoom, closeUp)
function focusObject(targetNodes, rotation, updateZoom, closeUp)
{
if (!camera)
return;
// targetNodes could be a list of nodes or a single node
var nodes = [];
if (targetNodes instanceof Node)
nodes.push(targetNodes);
else
nodes = targetNodes
camera.eulerRotation = rotation;
var newLookAtAndZoom = _generalHelper.focusObjectToCamera(
camera, _defaultCameraLookAtDistance, targetObject, view3d, _zoomFactor,
var newLookAtAndZoom = _generalHelper.focusNodesToCamera(
camera, _defaultCameraLookAtDistance, nodes, view3d, _zoomFactor,
updateZoom, closeUp);
_lookAtPoint = newLookAtAndZoom.toVector3d();
_zoomFactor = newLookAtAndZoom.w;

View File

@@ -176,9 +176,16 @@ Item {
function fitToView()
{
if (editView) {
var targetNode = selectionBoxes.length > 0
? selectionBoxes[0].model : null;
cameraControl.focusObject(targetNode, editView.camera.eulerRotation, true, false);
var boxModels = [];
if (selectedNodes.length > 1) {
for (var i = 0; i < selectedNodes.length; ++i) {
if (selectionBoxes.length > i)
boxModels.push(selectionBoxes[i].model)
}
} else if (selectedNodes.length > 0 && selectionBoxes.length > 0) {
boxModels.push(selectionBoxes[0].model);
}
cameraControl.focusObject(boxModels, editView.camera.eulerRotation, true, false);
}
}
@@ -857,7 +864,8 @@ Item {
width: 100
height: width
editCameraCtrl: cameraControl
selectedNode : viewRoot.selectedNodes.length ? selectionBoxes[0].model : null
selectedNode: viewRoot.selectedNodes.length === 1 ? viewRoot.selectionBoxes[0].model
: viewRoot.selectedNode
}
Text {

View File

@@ -46,6 +46,8 @@
#include <QtQuick/qquickitem.h>
#include <QtCore/qmath.h>
#include <limits>
namespace QmlDesigner {
namespace Internal {
@@ -151,56 +153,90 @@ float GeneralHelper::zoomCamera(QQuick3DCamera *camera, float distance, float de
}
// Return value contains new lookAt point (xyz) and zoom factor (w)
QVector4D GeneralHelper::focusObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance,
QQuick3DNode *targetObject, QQuick3DViewport *viewPort,
float oldZoom, bool updateZoom, bool closeUp)
QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaultLookAtDistance,
const QVariant &nodes, QQuick3DViewport *viewPort,
float oldZoom, bool updateZoom, bool closeUp)
{
if (!camera)
return QVector4D(0.f, 0.f, 0.f, 1.f);
QVector3D lookAt = targetObject ? targetObject->scenePosition() : QVector3D();
QList<QQuick3DNode *> nodeList;
const QVariantList varNodes = nodes.value<QVariantList>();
for (const auto &varNode : varNodes) {
auto model = varNode.value<QQuick3DNode *>();
if (model)
nodeList.append(model);
}
// Get object bounds
// Get bounds
QVector3D totalMinBound;
QVector3D totalMaxBound;
const qreal defaultExtent = 200.;
qreal maxExtent = defaultExtent;
if (auto modelNode = qobject_cast<QQuick3DModel *>(targetObject)) {
auto targetPriv = QQuick3DObjectPrivate::get(targetObject);
if (auto renderModel = static_cast<QSSGRenderModel *>(targetPriv->spatialNode)) {
QWindow *window = static_cast<QWindow *>(viewPort->window());
if (window) {
QSSGRef<QSSGRenderContextInterface> context;
if (!nodeList.isEmpty()) {
static const float floatMin = std::numeric_limits<float>::lowest();
static const float floatMax = std::numeric_limits<float>::max();
totalMinBound = {floatMax, floatMax, floatMax};
totalMaxBound = {floatMin, floatMin, floatMin};
} else {
const float halfExtent = defaultExtent / 2.f;
totalMinBound = {-halfExtent, -halfExtent, -halfExtent};
totalMaxBound = {halfExtent, halfExtent, halfExtent};
}
for (const auto node : qAsConst(nodeList)) {
auto model = qobject_cast<QQuick3DModel *>(node);
qreal maxExtent = defaultExtent;
QVector3D center = node->scenePosition();
if (model) {
auto targetPriv = QQuick3DObjectPrivate::get(model);
if (auto renderModel = static_cast<QSSGRenderModel *>(targetPriv->spatialNode)) {
QWindow *window = static_cast<QWindow *>(viewPort->window());
if (window) {
QSSGRef<QSSGRenderContextInterface> context;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
context = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window));
context = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window));
#else
context = targetPriv->sceneManager->rci;
context = targetPriv->sceneManager->rci;
#endif
if (!context.isNull()) {
QSSGBounds3 bounds;
auto geometry = qobject_cast<SelectionBoxGeometry *>(modelNode->geometry());
if (geometry) {
bounds = geometry->bounds();
} else {
auto bufferManager = context->bufferManager();
bounds = renderModel->getModelBounds(bufferManager);
if (!context.isNull()) {
QSSGBounds3 bounds;
auto geometry = qobject_cast<SelectionBoxGeometry *>(model->geometry());
if (geometry) {
bounds = geometry->bounds();
} else {
auto bufferManager = context->bufferManager();
bounds = renderModel->getModelBounds(bufferManager);
}
center = renderModel->globalTransform.map(bounds.center());
const QVector3D e = bounds.extents();
const QVector3D s = model->sceneScale();
qreal maxScale = qSqrt(qreal(s.x() * s.x() + s.y() * s.y() + s.z() * s.z()));
maxExtent = qSqrt(qreal(e.x() * e.x() + e.y() * e.y() + e.z() * e.z()));
maxExtent *= maxScale;
if (maxExtent < 0.0001)
maxExtent = defaultExtent;
}
QVector3D center = bounds.center();
const QVector3D e = bounds.extents();
const QVector3D s = targetObject->sceneScale();
qreal maxScale = qSqrt(qreal(s.x() * s.x() + s.y() * s.y() + s.z() * s.z()));
maxExtent = qSqrt(qreal(e.x() * e.x() + e.y() * e.y() + e.z() * e.z()));
maxExtent *= maxScale;
if (maxExtent < 0.0001)
maxExtent = defaultExtent;
// Adjust lookAt to look directly at the center of the object bounds
lookAt = renderModel->globalTransform.map(center);
}
}
}
float halfExtent = float(maxExtent / 2.);
const QVector3D halfExtents {halfExtent, halfExtent, halfExtent};
const QVector3D minBound = center - halfExtents;
const QVector3D maxBound = center + halfExtents;
for (int i = 0; i < 3; ++i) {
totalMinBound[i] = qMin(minBound[i], totalMinBound[i]);
totalMaxBound[i] = qMax(maxBound[i], totalMaxBound[i]);
}
}
QVector3D extents = totalMaxBound - totalMinBound;
QVector3D lookAt = totalMinBound + (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());
@@ -210,9 +246,9 @@ QVector4D GeneralHelper::focusObjectToCamera(QQuick3DCamera *camera, float defau
camera->setPosition(lookAt + newLookVector);
qreal divisor = closeUp ? 900. : 725.;
float divisor = closeUp ? 900.f : 725.f;
float newZoomFactor = updateZoom ? qBound(.01f, float(maxExtent / divisor), 100.f) : oldZoom;
float newZoomFactor = updateZoom ? qBound(.01f, maxExtent / divisor, 100.f) : oldZoom;
float cameraZoomFactor = zoomCamera(camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false);
return QVector4D(lookAt, cameraZoomFactor);

View File

@@ -67,10 +67,10 @@ public:
Q_INVOKABLE float zoomCamera(QQuick3DCamera *camera, float distance,
float defaultLookAtDistance, const QVector3D &lookAt,
float zoomFactor, bool relative);
Q_INVOKABLE QVector4D focusObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance,
QQuick3DNode *targetObject, QQuick3DViewport *viewPort,
float oldZoom, bool updateZoom = true,
bool closeUp = false);
Q_INVOKABLE QVector4D focusNodesToCamera(QQuick3DCamera *camera, float defaultLookAtDistance,
const QVariant &nodes, QQuick3DViewport *viewPort,
float oldZoom, bool updateZoom = true,
bool closeUp = false);
Q_INVOKABLE bool fuzzyCompare(double a, double b);
Q_INVOKABLE void delayedPropertySet(QObject *obj, int delay, const QString &property,
const QVariant& value);