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) if (!camera)
return; 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; camera.eulerRotation = rotation;
var newLookAtAndZoom = _generalHelper.focusObjectToCamera( var newLookAtAndZoom = _generalHelper.focusNodesToCamera(
camera, _defaultCameraLookAtDistance, targetObject, view3d, _zoomFactor, camera, _defaultCameraLookAtDistance, nodes, view3d, _zoomFactor,
updateZoom, closeUp); updateZoom, closeUp);
_lookAtPoint = newLookAtAndZoom.toVector3d(); _lookAtPoint = newLookAtAndZoom.toVector3d();
_zoomFactor = newLookAtAndZoom.w; _zoomFactor = newLookAtAndZoom.w;

View File

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

View File

@@ -88,14 +88,21 @@ Item {
} }
function focusObject(targetObject, rotation, updateZoom, closeUp) function focusObject(targetNodes, rotation, updateZoom, closeUp)
{ {
if (!camera) if (!camera)
return; 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; camera.eulerRotation = rotation;
var newLookAtAndZoom = _generalHelper.focusObjectToCamera( var newLookAtAndZoom = _generalHelper.focusNodesToCamera(
camera, _defaultCameraLookAtDistance, targetObject, view3d, _zoomFactor, camera, _defaultCameraLookAtDistance, nodes, view3d, _zoomFactor,
updateZoom, closeUp); updateZoom, closeUp);
_lookAtPoint = newLookAtAndZoom.toVector3d(); _lookAtPoint = newLookAtAndZoom.toVector3d();
_zoomFactor = newLookAtAndZoom.w; _zoomFactor = newLookAtAndZoom.w;

View File

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

View File

@@ -46,6 +46,8 @@
#include <QtQuick/qquickitem.h> #include <QtQuick/qquickitem.h>
#include <QtCore/qmath.h> #include <QtCore/qmath.h>
#include <limits>
namespace QmlDesigner { namespace QmlDesigner {
namespace Internal { namespace Internal {
@@ -151,20 +153,42 @@ float GeneralHelper::zoomCamera(QQuick3DCamera *camera, float distance, float de
} }
// Return value contains new lookAt point (xyz) and zoom factor (w) // Return value contains new lookAt point (xyz) and zoom factor (w)
QVector4D GeneralHelper::focusObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance, QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaultLookAtDistance,
QQuick3DNode *targetObject, QQuick3DViewport *viewPort, const QVariant &nodes, QQuick3DViewport *viewPort,
float oldZoom, bool updateZoom, bool closeUp) float oldZoom, bool updateZoom, bool closeUp)
{ {
if (!camera) if (!camera)
return QVector4D(0.f, 0.f, 0.f, 1.f); 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.; const qreal defaultExtent = 200.;
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; qreal maxExtent = defaultExtent;
if (auto modelNode = qobject_cast<QQuick3DModel *>(targetObject)) { QVector3D center = node->scenePosition();
auto targetPriv = QQuick3DObjectPrivate::get(targetObject); if (model) {
auto targetPriv = QQuick3DObjectPrivate::get(model);
if (auto renderModel = static_cast<QSSGRenderModel *>(targetPriv->spatialNode)) { if (auto renderModel = static_cast<QSSGRenderModel *>(targetPriv->spatialNode)) {
QWindow *window = static_cast<QWindow *>(viewPort->window()); QWindow *window = static_cast<QWindow *>(viewPort->window());
if (window) { if (window) {
@@ -176,7 +200,7 @@ QVector4D GeneralHelper::focusObjectToCamera(QQuick3DCamera *camera, float defau
#endif #endif
if (!context.isNull()) { if (!context.isNull()) {
QSSGBounds3 bounds; QSSGBounds3 bounds;
auto geometry = qobject_cast<SelectionBoxGeometry *>(modelNode->geometry()); auto geometry = qobject_cast<SelectionBoxGeometry *>(model->geometry());
if (geometry) { if (geometry) {
bounds = geometry->bounds(); bounds = geometry->bounds();
} else { } else {
@@ -184,23 +208,35 @@ QVector4D GeneralHelper::focusObjectToCamera(QQuick3DCamera *camera, float defau
bounds = renderModel->getModelBounds(bufferManager); bounds = renderModel->getModelBounds(bufferManager);
} }
QVector3D center = bounds.center(); center = renderModel->globalTransform.map(bounds.center());
const QVector3D e = bounds.extents(); const QVector3D e = bounds.extents();
const QVector3D s = targetObject->sceneScale(); const QVector3D s = model->sceneScale();
qreal maxScale = qSqrt(qreal(s.x() * s.x() + s.y() * s.y() + s.z() * s.z())); 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 = qSqrt(qreal(e.x() * e.x() + e.y() * e.y() + e.z() * e.z()));
maxExtent *= maxScale; maxExtent *= maxScale;
if (maxExtent < 0.0001) if (maxExtent < 0.0001)
maxExtent = defaultExtent; maxExtent = defaultExtent;
}
}
}
}
float halfExtent = float(maxExtent / 2.);
const QVector3D halfExtents {halfExtent, halfExtent, halfExtent};
// Adjust lookAt to look directly at the center of the object bounds const QVector3D minBound = center - halfExtents;
lookAt = renderModel->globalTransform.map(center); 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 // Reset camera position to default zoom
QMatrix4x4 m = camera->sceneTransform(); QMatrix4x4 m = camera->sceneTransform();
const float *dataPtr(m.data()); const float *dataPtr(m.data());
@@ -210,9 +246,9 @@ QVector4D GeneralHelper::focusObjectToCamera(QQuick3DCamera *camera, float defau
camera->setPosition(lookAt + newLookVector); 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); float cameraZoomFactor = zoomCamera(camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false);
return QVector4D(lookAt, cameraZoomFactor); return QVector4D(lookAt, cameraZoomFactor);

View File

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