1 | // Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qabstractphysicaldevicebackendnode_p.h" |
5 | #include "qabstractphysicaldevicebackendnode_p_p.h" |
6 | |
7 | #include <Qt3DInput/qabstractphysicaldevice.h> |
8 | #include <Qt3DInput/qaxissetting.h> |
9 | #include <Qt3DInput/qinputaspect.h> |
10 | |
11 | #include <cmath> |
12 | #include <algorithm> |
13 | |
14 | #include <Qt3DInput/private/inputhandler_p.h> |
15 | #include <Qt3DInput/private/inputmanagers_p.h> |
16 | #include <Qt3DInput/private/qinputaspect_p.h> |
17 | #include <Qt3DCore/private/qabstractaspect_p.h> |
18 | |
19 | QT_BEGIN_NAMESPACE |
20 | |
21 | namespace { |
22 | |
23 | constexpr int signum(float v) |
24 | { |
25 | return (v > 0.0f) - (v < 0.0f); |
26 | } |
27 | |
28 | } |
29 | |
30 | namespace Qt3DInput { |
31 | |
32 | QAbstractPhysicalDeviceBackendNodePrivate::QAbstractPhysicalDeviceBackendNodePrivate(Qt3DCore::QBackendNode::Mode mode) |
33 | : Qt3DCore::QBackendNodePrivate(mode) |
34 | , m_axisSettings() |
35 | , m_inputAspect(nullptr) |
36 | { |
37 | } |
38 | |
39 | void QAbstractPhysicalDeviceBackendNodePrivate::addAxisSetting(int axisIdentifier, Qt3DCore::QNodeId axisSettingsId) |
40 | { |
41 | Input::AxisIdSetting axisIdSetting; |
42 | axisIdSetting.m_axisIdentifier = axisIdentifier; |
43 | axisIdSetting.m_axisSettingsId = axisSettingsId; |
44 | |
45 | // Replace if already present, otherwise append |
46 | const auto end = m_axisSettings.end(); |
47 | for (auto it = m_axisSettings.begin(); it != end; ++it) { |
48 | if (it->m_axisIdentifier == axisIdentifier) { |
49 | *it = axisIdSetting; |
50 | return; |
51 | } |
52 | } |
53 | m_axisSettings.push_back(t: axisIdSetting); |
54 | } |
55 | |
56 | void QAbstractPhysicalDeviceBackendNodePrivate::removeAxisSetting(Qt3DCore::QNodeId axisSettingsId) |
57 | { |
58 | const auto end = m_axisSettings.end(); |
59 | for (auto it = m_axisSettings.begin(); it != end; ++it) { |
60 | if (it->m_axisSettingsId == axisSettingsId) { |
61 | m_axisSettings.erase(pos: it); |
62 | return; |
63 | } |
64 | } |
65 | } |
66 | |
67 | Input::MovingAverage &QAbstractPhysicalDeviceBackendNodePrivate::getOrCreateFilter(int axisIdentifier) |
68 | { |
69 | const auto end = m_axisFilters.end(); |
70 | for (auto it = m_axisFilters.begin(); it != end; ++it) { |
71 | if (it->m_axisIdentifier == axisIdentifier) |
72 | return it->m_filter; |
73 | } |
74 | |
75 | Input::AxisIdFilter axisIdFilter; |
76 | axisIdFilter.m_axisIdentifier = axisIdentifier; |
77 | m_axisFilters.push_back(t: axisIdFilter); |
78 | return m_axisFilters.last().m_filter; |
79 | } |
80 | |
81 | Input::AxisSetting *QAbstractPhysicalDeviceBackendNodePrivate::getAxisSetting(Qt3DCore::QNodeId axisSettingId) const |
82 | { |
83 | Q_Q(const QAbstractPhysicalDeviceBackendNode); |
84 | QInputAspectPrivate *aspectPrivate = static_cast<QInputAspectPrivate *>(Qt3DCore::QAbstractAspectPrivate::get(aspect: q->inputAspect())); |
85 | Input::InputHandler *handler = aspectPrivate->m_inputHandler.data(); |
86 | Input::AxisSetting *axisSetting = handler->axisSettingManager()->getOrCreateResource(id: axisSettingId); |
87 | return axisSetting; |
88 | } |
89 | |
90 | QAbstractPhysicalDeviceBackendNode::QAbstractPhysicalDeviceBackendNode(QBackendNode::Mode mode) |
91 | : Input::BackendNode(*new QAbstractPhysicalDeviceBackendNodePrivate(mode)) |
92 | { |
93 | } |
94 | |
95 | QAbstractPhysicalDeviceBackendNode::QAbstractPhysicalDeviceBackendNode(QAbstractPhysicalDeviceBackendNodePrivate &dd) |
96 | : Input::BackendNode(dd) |
97 | { |
98 | } |
99 | |
100 | void QAbstractPhysicalDeviceBackendNode::cleanup() |
101 | { |
102 | Q_D(QAbstractPhysicalDeviceBackendNode); |
103 | QBackendNode::setEnabled(false); |
104 | d->m_axisSettings.clear(); |
105 | d->m_axisFilters.clear(); |
106 | d->m_inputAspect = nullptr; |
107 | } |
108 | |
109 | void QAbstractPhysicalDeviceBackendNode::syncFromFrontEnd(const Qt3DCore::QNode *frontEnd, bool firstTime) |
110 | { |
111 | Q_D(QAbstractPhysicalDeviceBackendNode); |
112 | BackendNode::syncFromFrontEnd(frontEnd, firstTime); |
113 | const Qt3DInput::QAbstractPhysicalDevice *node = qobject_cast<const Qt3DInput::QAbstractPhysicalDevice *>(object: frontEnd); |
114 | if (!node) |
115 | return; |
116 | |
117 | auto settings = Qt3DCore::qIdsForNodes(nodes: node->axisSettings()); |
118 | std::sort(first: std::begin(cont&: settings), last: std::end(cont&: settings)); |
119 | Qt3DCore::QNodeIdVector addedSettings; |
120 | Qt3DCore::QNodeIdVector removedSettings; |
121 | std::set_difference(first1: std::begin(cont&: settings), last1: std::end(cont&: settings), |
122 | first2: std::begin(cont&: d->m_currentAxisSettingIds), last2: std::end(cont&: d->m_currentAxisSettingIds), |
123 | result: std::inserter(x&: addedSettings, i: addedSettings.end())); |
124 | std::set_difference(first1: std::begin(cont&: d->m_currentAxisSettingIds), last1: std::end(cont&: d->m_currentAxisSettingIds), |
125 | first2: std::begin(cont&: settings), last2: std::end(cont&: settings), |
126 | result: std::inserter(x&: removedSettings, i: removedSettings.end())); |
127 | d->m_currentAxisSettingIds = settings; |
128 | |
129 | for (const auto &axisSettingId: std::as_const(t&: addedSettings)) { |
130 | Input::AxisSetting *axisSetting = d->getAxisSetting(axisSettingId); |
131 | const auto axisIds = axisSetting->axes(); |
132 | for (int axisId : axisIds) |
133 | d->addAxisSetting(axisIdentifier: axisId, axisSettingsId: axisSettingId); |
134 | } |
135 | for (const auto &axisSettingId: std::as_const(t&: removedSettings)) |
136 | d->removeAxisSetting(axisSettingsId: axisSettingId); |
137 | } |
138 | |
139 | void QAbstractPhysicalDeviceBackendNode::setInputAspect(QInputAspect *aspect) |
140 | { |
141 | Q_D(QAbstractPhysicalDeviceBackendNode); |
142 | d->m_inputAspect = aspect; |
143 | } |
144 | |
145 | QInputAspect *QAbstractPhysicalDeviceBackendNode::inputAspect() const |
146 | { |
147 | Q_D(const QAbstractPhysicalDeviceBackendNode); |
148 | return d->m_inputAspect; |
149 | } |
150 | |
151 | float QAbstractPhysicalDeviceBackendNode::processedAxisValue(int axisIdentifier) |
152 | { |
153 | Q_D(QAbstractPhysicalDeviceBackendNode); |
154 | |
155 | // Find axis settings for this axis (if any) |
156 | Qt3DCore::QNodeId axisSettingId; |
157 | const auto end = d->m_axisSettings.cend(); |
158 | for (auto it = d->m_axisSettings.cbegin(); it != end; ++it) { |
159 | if (it->m_axisIdentifier == axisIdentifier) { |
160 | axisSettingId = it->m_axisSettingsId; |
161 | break; |
162 | } |
163 | } |
164 | |
165 | const float rawAxisValue = axisValue(axisIdentifier); |
166 | if (axisSettingId.isNull()) { |
167 | // No special processing. Just return the raw value |
168 | return rawAxisValue; |
169 | } else { |
170 | // Process the raw value in accordance with the settings |
171 | Input::AxisSetting *axisSetting = d->getAxisSetting(axisSettingId); |
172 | Q_ASSERT(axisSetting); |
173 | float val = rawAxisValue; |
174 | |
175 | // Low pass smoothing |
176 | if (axisSetting->isSmoothEnabled()) { |
177 | // Get the filter corresponding to this axis |
178 | Input::MovingAverage &filter = d->getOrCreateFilter(axisIdentifier); |
179 | filter.addSample(sample: val); |
180 | val = filter.average(); |
181 | } |
182 | |
183 | // Deadzone handling |
184 | const float d = axisSetting->deadZoneRadius(); |
185 | if (!qFuzzyIsNull(f: d)) { |
186 | if (std::abs(x: val) <= d) { |
187 | val = 0.0f; |
188 | } else { |
189 | // Calculate value that goes from 0 to 1 linearly from the boundary of |
190 | // the dead zone up to 1. That is we with a dead zone value of d, we do not |
191 | // want a step change from 0 to d when the axis leaves the deadzone. Instead |
192 | // we want to increase the gradient of the line so that it goes from 0 to 1 |
193 | // over the range d to 1. So instead of having y = x, the equation of the |
194 | // line becomes |
195 | // |
196 | // y = x / (1-d) - d / (1-d) = (x - d) / (1 - d) |
197 | // |
198 | // for positive values, and |
199 | // |
200 | // y = x / (1-d) + d / (1-d) = (x + d) / (1 - d) |
201 | // |
202 | // for negative values. |
203 | val = (val - signum(v: val) * d) / (1.0f - d); |
204 | } |
205 | } |
206 | |
207 | return val; |
208 | } |
209 | |
210 | } |
211 | |
212 | } // Qt3DInput |
213 | |
214 | QT_END_NAMESPACE |
215 | |