1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "lookatnode_p.h"
5
6#include <QtMath>
7
8/*!
9 \qmltype LookAtNode
10 \inqmlmodule QtQuick3D.Helpers
11 \inherits Node
12 \brief A helper node that is automatically kept pointing at another node.
13 \since 6.4
14
15 This helper implements a node that automatically rotates so that it is always
16 pointed towards a specified node. The rotation only happens over X and Y axes.
17
18 For example, the following snippet keeps the cylinder pointed at the cube.
19
20 \badcode
21 View3D {
22 anchors.fill: parent
23 camera: camera
24
25 PerspectiveCamera {
26 id: camera
27 }
28
29 DirectionalLight {}
30
31 LookAtNode {
32 target: cube
33
34 Model {
35 id: cylinder
36 source: "#Cone"
37 eulerRotation.x: -90
38 materials: [
39 PrincipledMaterial {}
40 ]
41 }
42 }
43
44 Model {
45 id: cube
46 position: Qt.vector3d(300, 300, 0);
47 source: "#Cube"
48 materials: [ PrincipledMaterial {} ]
49 }
50 }
51 \endcode
52*/
53
54/*! \qmlproperty Node LookAtNode::target
55 Specifies the target node to look at. The default value is \c{null}.
56*/
57
58LookAtNode::LookAtNode()
59 : QQuick3DNode()
60{
61}
62
63LookAtNode::~LookAtNode()
64{
65}
66
67QQuick3DNode *LookAtNode::target() const
68{
69 return m_target;
70}
71
72void LookAtNode::setTarget(QQuick3DNode *node)
73{
74 if (node == m_target)
75 return;
76
77 if (m_target) {
78 disconnect(sender: m_target, signal: &QQuick3DNode::scenePositionChanged, receiver: this, slot: &LookAtNode::updateLookAt);
79 disconnect(sender: this, signal: &QQuick3DNode::scenePositionChanged, receiver: this, slot: &LookAtNode::updateLookAt);
80 }
81
82 m_target = node;
83
84 if (m_target) {
85 connect(sender: m_target, signal: &QQuick3DNode::scenePositionChanged, context: this, slot: &LookAtNode::updateLookAt);
86 connect(sender: this, signal: &QQuick3DNode::scenePositionChanged, context: this, slot: &LookAtNode::updateLookAt);
87 }
88
89 emit targetChanged();
90 updateLookAt();
91}
92
93void LookAtNode::updateLookAt()
94{
95 if (m_target) {
96 // Note: This code was originally copied from QQuick3DCamera::lookAt method.
97
98 // Assumption: we never want the node to roll.
99 // We use Euler angles here to avoid roll to sneak in through numerical instability.
100
101 const QVector3D targetPosition = m_target->scenePosition();
102 auto sourcePosition = scenePosition();
103
104 QVector3D targetVector = sourcePosition - targetPosition;
105
106 float yaw = qRadiansToDegrees(radians: atan2(y: targetVector.x(), x: targetVector.z()));
107
108 QVector2D p(targetVector.x(), targetVector.z()); // yaw vector projected to horizontal plane
109 float pitch = qRadiansToDegrees(radians: atan2(y: p.length(), x: targetVector.y())) - 90;
110
111 const float previousRoll = eulerRotation().z();
112 setEulerRotation(QVector3D(pitch, yaw, previousRoll));
113 }
114}
115

source code of qtquick3d/src/helpers/lookatnode.cpp