Revision control
++ // Copyright (C) 2022 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++
++ #include "lookatnode_p.h"
++
++ #include <QtMath>
++
++ /*!
++ \qmltype LookAtNode
++ \inqmlmodule QtQuick3D.Helpers
++ \inherits Node
++ \brief A helper node that is automatically kept pointing at another node.
++ \since 6.4
++
++ This helper implements a node that automatically rotates so that it is always
++ pointed towards a specified node. The rotation only happens over X and Y axes.
++
++ For example, the following snippet keeps the cylinder pointed at the cube.
++
++ \badcode
++ View3D {
++ anchors.fill: parent
++ camera: camera
++
++ PerspectiveCamera {
++ id: camera
++ }
++
++ DirectionalLight {}
++
++ LookAtNode {
++ target: cube
++
++ Model {
++ id: cylinder
++ source: "#Cone"
++ eulerRotation.x: -90
++ materials: [
++ PrincipledMaterial {}
++ ]
++ }
++ }
++
++ Model {
++ id: cube
++ position: Qt.vector3d(300, 300, 0);
++ source: "#Cube"
++ materials: [ PrincipledMaterial {} ]
++ }
++ }
++ \endcode
++ */
++
++ /*! \qmlproperty Node LookAtNode::target
++ Specifies the target node to look at. The default value is \c{null}.
++ */
++
++ LookAtNode::LookAtNode()
++ : QQuick3DNode()
++ {
++ }
++
++ LookAtNode::~LookAtNode()
++ {
++ }
++
++ QQuick3DNode *LookAtNode::target() const
++ {
++ return m_target;
++ }
++
++ void LookAtNode::setTarget(QQuick3DNode *node)
++ {
++ if (node == m_target)
++ return;
++
++ if (m_target) {
++ disconnect(m_target, &QQuick3DNode::scenePositionChanged, this, &LookAtNode::updateLookAt);
++ disconnect(this, &QQuick3DNode::scenePositionChanged, this, &LookAtNode::updateLookAt);
++ }
++
++ m_target = node;
++
++ if (m_target) {
++ connect(m_target, &QQuick3DNode::scenePositionChanged, this, &LookAtNode::updateLookAt);
++ connect(this, &QQuick3DNode::scenePositionChanged, this, &LookAtNode::updateLookAt);
++ }
++
++ emit targetChanged();
++ updateLookAt();
++ }
++
++ void LookAtNode::updateLookAt()
++ {
++ if (m_target) {
++ // Note: This code was originally copied from QQuick3DCamera::lookAt method.
++
++ // Assumption: we never want the node to roll.
++ // We use Euler angles here to avoid roll to sneak in through numerical instability.
++
++ const QVector3D targetPosition = m_target->scenePosition();
++ auto sourcePosition = scenePosition();
++
++ QVector3D targetVector = sourcePosition - targetPosition;
++
++ float yaw = qRadiansToDegrees(atan2(targetVector.x(), targetVector.z()));
++
++ QVector2D p(targetVector.x(), targetVector.z()); // yaw vector projected to horizontal plane
++ float pitch = qRadiansToDegrees(atan2(p.length(), targetVector.y())) - 90;
++
++ const float previousRoll = eulerRotation().z();
++ setEulerRotation(QVector3D(pitch, yaw, previousRoll));
++ }
++ }