1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the Qt3D module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL3$ |
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 http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free |
28 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
29 | ** the packaging of this file. Please review the following information to |
30 | ** ensure the GNU General Public License version 2.0 requirements will be |
31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
32 | ** |
33 | ** $QT_END_LICENSE$ |
34 | ** |
35 | ****************************************************************************/ |
36 | |
37 | #include "qchannelmapping.h" |
38 | #include "qchannelmapping_p.h" |
39 | |
40 | #include <Qt3DAnimation/private/qchannelmappingcreatedchange_p.h> |
41 | #include <Qt3DCore/qpropertyupdatedchange.h> |
42 | |
43 | #include <QtCore/qmetaobject.h> |
44 | #include <QtCore/QMetaProperty> |
45 | |
46 | QT_BEGIN_NAMESPACE |
47 | |
48 | namespace Qt3DAnimation { |
49 | |
50 | namespace { |
51 | |
52 | template<typename T> |
53 | int componentCountForValue(const T &) |
54 | { |
55 | return 0; |
56 | } |
57 | |
58 | template<> |
59 | int componentCountForValue<QVector<float>>(const QVector<float> &v) |
60 | { |
61 | return v.size(); |
62 | } |
63 | |
64 | template<> |
65 | int componentCountForValue<QVariantList>(const QVariantList &v) |
66 | { |
67 | return v.size(); |
68 | } |
69 | |
70 | |
71 | int componentCountForType(int type, const QVariant &value) |
72 | { |
73 | const int vectorOfFloatTypeId = qMetaTypeId<QVector<float>>(); |
74 | |
75 | if (type == vectorOfFloatTypeId) |
76 | return componentCountForValue<QVector<float>>(value.value<QVector<float>>()); |
77 | |
78 | switch (type) { |
79 | case QMetaType::Float: |
80 | case QVariant::Double: |
81 | return 1; |
82 | |
83 | case QVariant::Vector2D: |
84 | return 2; |
85 | |
86 | case QVariant::Vector3D: |
87 | case QVariant::Color: |
88 | return 3; |
89 | |
90 | case QVariant::Vector4D: |
91 | case QVariant::Quaternion: |
92 | return 4; |
93 | |
94 | case QVariant::List: |
95 | return componentCountForValue<QVariantList>(value.toList()); |
96 | |
97 | default: |
98 | qWarning() << "Unhandled animation type" ; |
99 | return 0; |
100 | } |
101 | } |
102 | |
103 | } // anonymous |
104 | |
105 | QChannelMappingPrivate::QChannelMappingPrivate() |
106 | : QAbstractChannelMappingPrivate() |
107 | , m_channelName() |
108 | , m_target(nullptr) |
109 | , m_property() |
110 | , m_propertyName(nullptr) |
111 | , m_type(static_cast<int>(QVariant::Invalid)) |
112 | , m_componentCount(0) |
113 | { |
114 | m_mappingType = QChannelMappingCreatedChangeBase::ChannelMapping; |
115 | } |
116 | |
117 | /*! |
118 | \internal |
119 | |
120 | Find the type of the property specified on the target node |
121 | */ |
122 | void QChannelMappingPrivate::updatePropertyNameTypeAndComponentCount() |
123 | { |
124 | int type; |
125 | int componentCount = 0; |
126 | const char *propertyName = nullptr; |
127 | |
128 | if (!m_target || m_property.isNull()) { |
129 | type = QVariant::Invalid; |
130 | } else { |
131 | const QMetaObject *mo = m_target->metaObject(); |
132 | const int propertyIndex = mo->indexOfProperty(m_property.toLocal8Bit()); |
133 | QMetaProperty mp = mo->property(propertyIndex); |
134 | propertyName = mp.name(); |
135 | type = mp.userType(); |
136 | const QVariant currentValue = m_target->property(mp.name()); |
137 | if (type == QMetaType::QVariant) { |
138 | if (currentValue.isValid()) { |
139 | type = currentValue.userType(); |
140 | } else { |
141 | qWarning("QChannelMapping: Attempted to target QVariant property with no value set. " |
142 | "Set a value first in order to be able to determine the type." ); |
143 | } |
144 | } |
145 | componentCount = componentCountForType(type, currentValue); |
146 | } |
147 | |
148 | if (m_type != type) { |
149 | m_type = type; |
150 | |
151 | // Send update to the backend |
152 | Q_Q(QChannelMapping); |
153 | auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(q->id()); |
154 | e->setPropertyName("type" ); |
155 | e->setValue(QVariant(m_type)); |
156 | notifyObservers(e); |
157 | } |
158 | |
159 | if (m_componentCount != componentCount) { |
160 | m_componentCount = componentCount; |
161 | |
162 | // Send update to the backend |
163 | Q_Q(QChannelMapping); |
164 | auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(q->id()); |
165 | e->setPropertyName("componentCount" ); |
166 | e->setValue(QVariant(m_componentCount)); |
167 | notifyObservers(e); |
168 | } |
169 | |
170 | if (qstrcmp(m_propertyName, propertyName) != 0) { |
171 | m_propertyName = propertyName; |
172 | |
173 | // Send update to the backend |
174 | Q_Q(QChannelMapping); |
175 | auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(q->id()); |
176 | e->setPropertyName("propertyName" ); |
177 | e->setValue(QVariant::fromValue(const_cast<void *>(static_cast<const void *>(m_propertyName)))); |
178 | notifyObservers(e); |
179 | } |
180 | } |
181 | /*! |
182 | \class QChannelMapping |
183 | \inherits Qt3DCore::QNode |
184 | \inmodule Qt3DAnimation |
185 | \brief Allows to map the channels within the clip onto properties of |
186 | objects in the application. |
187 | |
188 | */ |
189 | |
190 | QChannelMapping::QChannelMapping(Qt3DCore::QNode *parent) |
191 | : QAbstractChannelMapping(*new QChannelMappingPrivate, parent) |
192 | { |
193 | } |
194 | |
195 | QChannelMapping::QChannelMapping(QChannelMappingPrivate &dd, Qt3DCore::QNode *parent) |
196 | : QAbstractChannelMapping(dd, parent) |
197 | { |
198 | } |
199 | |
200 | QChannelMapping::~QChannelMapping() |
201 | { |
202 | } |
203 | |
204 | QString QChannelMapping::channelName() const |
205 | { |
206 | Q_D(const QChannelMapping); |
207 | return d->m_channelName; |
208 | } |
209 | |
210 | Qt3DCore::QNode *QChannelMapping::target() const |
211 | { |
212 | Q_D(const QChannelMapping); |
213 | return d->m_target; |
214 | } |
215 | |
216 | QString QChannelMapping::property() const |
217 | { |
218 | Q_D(const QChannelMapping); |
219 | return d->m_property; |
220 | } |
221 | |
222 | void QChannelMapping::setChannelName(const QString &channelName) |
223 | { |
224 | Q_D(QChannelMapping); |
225 | if (d->m_channelName == channelName) |
226 | return; |
227 | |
228 | d->m_channelName = channelName; |
229 | emit channelNameChanged(channelName); |
230 | } |
231 | |
232 | void QChannelMapping::setTarget(Qt3DCore::QNode *target) |
233 | { |
234 | Q_D(QChannelMapping); |
235 | if (d->m_target == target) |
236 | return; |
237 | |
238 | if (d->m_target) |
239 | d->unregisterDestructionHelper(d->m_target); |
240 | |
241 | if (target && !target->parent()) |
242 | target->setParent(this); |
243 | d->m_target = target; |
244 | |
245 | // Ensures proper bookkeeping |
246 | if (d->m_target) |
247 | d->registerDestructionHelper(d->m_target, &QChannelMapping::setTarget, d->m_target); |
248 | |
249 | emit targetChanged(target); |
250 | d->updatePropertyNameTypeAndComponentCount(); |
251 | } |
252 | |
253 | void QChannelMapping::setProperty(const QString &property) |
254 | { |
255 | Q_D(QChannelMapping); |
256 | if (d->m_property == property) |
257 | return; |
258 | |
259 | d->m_property = property; |
260 | |
261 | // The backend uses propertyName instead of property |
262 | const bool blocked = blockNotifications(true); |
263 | emit propertyChanged(property); |
264 | blockNotifications(blocked); |
265 | |
266 | d->updatePropertyNameTypeAndComponentCount(); |
267 | } |
268 | |
269 | Qt3DCore::QNodeCreatedChangeBasePtr QChannelMapping::createNodeCreationChange() const |
270 | { |
271 | auto creationChange = QChannelMappingCreatedChangePtr<QChannelMappingData>::create(this); |
272 | auto &data = creationChange->data; |
273 | Q_D(const QChannelMapping); |
274 | data.channelName = d->m_channelName; |
275 | data.targetId = Qt3DCore::qIdForNode(d->m_target); |
276 | data.type = d->m_type; |
277 | data.componentCount = d->m_componentCount; |
278 | data.propertyName = d->m_propertyName; |
279 | return creationChange; |
280 | } |
281 | |
282 | } // namespace Qt3DAnimation |
283 | |
284 | QT_END_NAMESPACE |
285 | |