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
46QT_BEGIN_NAMESPACE
47
48namespace Qt3DAnimation {
49
50namespace {
51
52template<typename T>
53int componentCountForValue(const T &)
54{
55 return 0;
56}
57
58template<>
59int componentCountForValue<QVector<float>>(const QVector<float> &v)
60{
61 return v.size();
62}
63
64template<>
65int componentCountForValue<QVariantList>(const QVariantList &v)
66{
67 return v.size();
68}
69
70
71int 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
105QChannelMappingPrivate::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 */
122void 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
190QChannelMapping::QChannelMapping(Qt3DCore::QNode *parent)
191 : QAbstractChannelMapping(*new QChannelMappingPrivate, parent)
192{
193}
194
195QChannelMapping::QChannelMapping(QChannelMappingPrivate &dd, Qt3DCore::QNode *parent)
196 : QAbstractChannelMapping(dd, parent)
197{
198}
199
200QChannelMapping::~QChannelMapping()
201{
202}
203
204QString QChannelMapping::channelName() const
205{
206 Q_D(const QChannelMapping);
207 return d->m_channelName;
208}
209
210Qt3DCore::QNode *QChannelMapping::target() const
211{
212 Q_D(const QChannelMapping);
213 return d->m_target;
214}
215
216QString QChannelMapping::property() const
217{
218 Q_D(const QChannelMapping);
219 return d->m_property;
220}
221
222void 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
232void 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
253void 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
269Qt3DCore::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
284QT_END_NAMESPACE
285