1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt3D module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qabstractphysicaldevicebackendnode_p.h" |
41 | #include "qabstractphysicaldevicebackendnode_p_p.h" |
42 | |
43 | #include <Qt3DInput/qabstractphysicaldevice.h> |
44 | #include <Qt3DInput/qaxissetting.h> |
45 | #include <Qt3DInput/qinputaspect.h> |
46 | #include <Qt3DInput/qphysicaldevicecreatedchange.h> |
47 | |
48 | #include <cmath> |
49 | #include <algorithm> |
50 | |
51 | #include <Qt3DInput/private/inputhandler_p.h> |
52 | #include <Qt3DInput/private/inputmanagers_p.h> |
53 | #include <Qt3DInput/private/qinputaspect_p.h> |
54 | #include <Qt3DCore/private/qabstractaspect_p.h> |
55 | |
56 | QT_BEGIN_NAMESPACE |
57 | |
58 | namespace { |
59 | |
60 | Q_DECL_CONSTEXPR int signum(float v) |
61 | { |
62 | return (v > 0.0f) - (v < 0.0f); |
63 | } |
64 | |
65 | } |
66 | |
67 | namespace Qt3DInput { |
68 | |
69 | QAbstractPhysicalDeviceBackendNodePrivate::QAbstractPhysicalDeviceBackendNodePrivate(Qt3DCore::QBackendNode::Mode mode) |
70 | : Qt3DCore::QBackendNodePrivate(mode) |
71 | , m_axisSettings() |
72 | , m_inputAspect(nullptr) |
73 | { |
74 | } |
75 | |
76 | void QAbstractPhysicalDeviceBackendNodePrivate::addAxisSetting(int axisIdentifier, Qt3DCore::QNodeId axisSettingsId) |
77 | { |
78 | Input::AxisIdSetting axisIdSetting; |
79 | axisIdSetting.m_axisIdentifier = axisIdentifier; |
80 | axisIdSetting.m_axisSettingsId = axisSettingsId; |
81 | |
82 | // Replace if already present, otherwise append |
83 | bool replaced = false; |
84 | QVector<Input::AxisIdSetting>::iterator it; |
85 | QVector<Input::AxisIdSetting>::iterator end = m_axisSettings.end(); |
86 | for (it = m_axisSettings.begin(); it != end; ++it) { |
87 | if (it->m_axisIdentifier == axisIdentifier) { |
88 | *it = axisIdSetting; |
89 | replaced = true; |
90 | break; |
91 | } |
92 | } |
93 | |
94 | if (!replaced) |
95 | m_axisSettings.push_back(t: axisIdSetting); |
96 | } |
97 | |
98 | void QAbstractPhysicalDeviceBackendNodePrivate::removeAxisSetting(Qt3DCore::QNodeId axisSettingsId) |
99 | { |
100 | QVector<Input::AxisIdSetting>::iterator it; |
101 | for (it = m_axisSettings.begin(); it != m_axisSettings.end(); ++it) { |
102 | if (it->m_axisSettingsId == axisSettingsId) { |
103 | m_axisSettings.erase(pos: it); |
104 | break; |
105 | } |
106 | } |
107 | } |
108 | |
109 | Input::MovingAverage &QAbstractPhysicalDeviceBackendNodePrivate::getOrCreateFilter(int axisIdentifier) |
110 | { |
111 | QVector<Input::AxisIdFilter>::iterator it; |
112 | QVector<Input::AxisIdFilter>::iterator end = m_axisFilters.end(); |
113 | for (it = m_axisFilters.begin(); it != end; ++it) { |
114 | if (it->m_axisIdentifier == axisIdentifier) |
115 | return it->m_filter; |
116 | } |
117 | |
118 | Input::AxisIdFilter axisIdFilter; |
119 | axisIdFilter.m_axisIdentifier = axisIdentifier; |
120 | m_axisFilters.push_back(t: axisIdFilter); |
121 | return m_axisFilters.last().m_filter; |
122 | } |
123 | |
124 | Input::AxisSetting *QAbstractPhysicalDeviceBackendNodePrivate::getAxisSetting(Qt3DCore::QNodeId axisSettingId) const |
125 | { |
126 | Q_Q(const QAbstractPhysicalDeviceBackendNode); |
127 | QInputAspectPrivate *aspectPrivate = static_cast<QInputAspectPrivate *>(Qt3DCore::QAbstractAspectPrivate::get(aspect: q->inputAspect())); |
128 | Input::InputHandler *handler = aspectPrivate->m_inputHandler.data(); |
129 | Input::AxisSetting *axisSetting = handler->axisSettingManager()->getOrCreateResource(id: axisSettingId); |
130 | return axisSetting; |
131 | } |
132 | |
133 | QAbstractPhysicalDeviceBackendNode::QAbstractPhysicalDeviceBackendNode(QBackendNode::Mode mode) |
134 | : Input::BackendNode(*new QAbstractPhysicalDeviceBackendNodePrivate(mode)) |
135 | { |
136 | } |
137 | |
138 | QAbstractPhysicalDeviceBackendNode::QAbstractPhysicalDeviceBackendNode(QAbstractPhysicalDeviceBackendNodePrivate &dd) |
139 | : Input::BackendNode(dd) |
140 | { |
141 | } |
142 | |
143 | void QAbstractPhysicalDeviceBackendNode::cleanup() |
144 | { |
145 | Q_D(QAbstractPhysicalDeviceBackendNode); |
146 | QBackendNode::setEnabled(false); |
147 | d->m_axisSettings.clear(); |
148 | d->m_axisFilters.clear(); |
149 | d->m_inputAspect = nullptr; |
150 | } |
151 | |
152 | void QAbstractPhysicalDeviceBackendNode::syncFromFrontEnd(const Qt3DCore::QNode *frontEnd, bool firstTime) |
153 | { |
154 | Q_D(QAbstractPhysicalDeviceBackendNode); |
155 | BackendNode::syncFromFrontEnd(frontEnd, firstTime); |
156 | const Qt3DInput::QAbstractPhysicalDevice *node = qobject_cast<const Qt3DInput::QAbstractPhysicalDevice *>(object: frontEnd); |
157 | if (!node) |
158 | return; |
159 | |
160 | auto settings = Qt3DCore::qIdsForNodes(nodes: node->axisSettings()); |
161 | std::sort(first: std::begin(cont&: settings), last: std::end(cont&: settings)); |
162 | Qt3DCore::QNodeIdVector addedSettings; |
163 | Qt3DCore::QNodeIdVector removedSettings; |
164 | std::set_difference(first1: std::begin(cont&: settings), last1: std::end(cont&: settings), |
165 | first2: std::begin(cont&: d->m_currentAxisSettingIds), last2: std::end(cont&: d->m_currentAxisSettingIds), |
166 | result: std::inserter(x&: addedSettings, i: addedSettings.end())); |
167 | std::set_difference(first1: std::begin(cont&: d->m_currentAxisSettingIds), last1: std::end(cont&: d->m_currentAxisSettingIds), |
168 | first2: std::begin(cont&: settings), last2: std::end(cont&: settings), |
169 | result: std::inserter(x&: removedSettings, i: removedSettings.end())); |
170 | d->m_currentAxisSettingIds = settings; |
171 | |
172 | for (const auto &axisSettingId: qAsConst(t&: addedSettings)) { |
173 | Input::AxisSetting *axisSetting = d->getAxisSetting(axisSettingId); |
174 | const auto axisIds = axisSetting->axes(); |
175 | for (int axisId : axisIds) |
176 | d->addAxisSetting(axisIdentifier: axisId, axisSettingsId: axisSettingId); |
177 | } |
178 | for (const auto &axisSettingId: qAsConst(t&: removedSettings)) |
179 | d->removeAxisSetting(axisSettingsId: axisSettingId); |
180 | } |
181 | |
182 | void QAbstractPhysicalDeviceBackendNode::setInputAspect(QInputAspect *aspect) |
183 | { |
184 | Q_D(QAbstractPhysicalDeviceBackendNode); |
185 | d->m_inputAspect = aspect; |
186 | } |
187 | |
188 | QInputAspect *QAbstractPhysicalDeviceBackendNode::inputAspect() const |
189 | { |
190 | Q_D(const QAbstractPhysicalDeviceBackendNode); |
191 | return d->m_inputAspect; |
192 | } |
193 | |
194 | float QAbstractPhysicalDeviceBackendNode::processedAxisValue(int axisIdentifier) |
195 | { |
196 | Q_D(QAbstractPhysicalDeviceBackendNode); |
197 | |
198 | // Find axis settings for this axis (if any) |
199 | Qt3DCore::QNodeId axisSettingId; |
200 | QVector<Input::AxisIdSetting>::const_iterator it; |
201 | QVector<Input::AxisIdSetting>::const_iterator end = d->m_axisSettings.cend(); |
202 | for (it = d->m_axisSettings.cbegin(); it != end; ++it) { |
203 | if (it->m_axisIdentifier == axisIdentifier) { |
204 | axisSettingId = it->m_axisSettingsId; |
205 | break; |
206 | } |
207 | } |
208 | |
209 | const float rawAxisValue = axisValue(axisIdentifier); |
210 | if (axisSettingId.isNull()) { |
211 | // No special processing. Just return the raw value |
212 | return rawAxisValue; |
213 | } else { |
214 | // Process the raw value in accordance with the settings |
215 | Input::AxisSetting *axisSetting = d->getAxisSetting(axisSettingId); |
216 | Q_ASSERT(axisSetting); |
217 | float val = rawAxisValue; |
218 | |
219 | // Low pass smoothing |
220 | if (axisSetting->isSmoothEnabled()) { |
221 | // Get the filter corresponding to this axis |
222 | Input::MovingAverage &filter = d->getOrCreateFilter(axisIdentifier); |
223 | filter.addSample(sample: val); |
224 | val = filter.average(); |
225 | } |
226 | |
227 | // Deadzone handling |
228 | const float d = axisSetting->deadZoneRadius(); |
229 | if (!qFuzzyIsNull(f: d)) { |
230 | if (std::abs(x: val) <= d) { |
231 | val = 0.0f; |
232 | } else { |
233 | // Calculate value that goes from 0 to 1 linearly from the boundary of |
234 | // the dead zone up to 1. That is we with a dead zone value of d, we do not |
235 | // want a step change from 0 to d when the axis leaves the deadzone. Instead |
236 | // we want to increase the gradient of the line so that it goes from 0 to 1 |
237 | // over the range d to 1. So instead of having y = x, the equation of the |
238 | // line becomes |
239 | // |
240 | // y = x / (1-d) - d / (1-d) = (x - d) / (1 - d) |
241 | // |
242 | // for positive values, and |
243 | // |
244 | // y = x / (1-d) + d / (1-d) = (x + d) / (1 - d) |
245 | // |
246 | // for negative values. |
247 | val = (val - signum(v: val) * d) / (1.0f - d); |
248 | } |
249 | } |
250 | |
251 | return val; |
252 | } |
253 | |
254 | } |
255 | |
256 | } // Qt3DInput |
257 | |
258 | QT_END_NAMESPACE |
259 | |