1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
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 "qvertexblendanimation.h"
38
39#include <private/qvertexblendanimation_p.h>
40
41QT_BEGIN_NAMESPACE
42
43namespace Qt3DAnimation {
44
45/*!
46 \class Qt3DAnimation::QVertexBlendAnimation
47 \brief A class implementing vertex-blend morphing animation.
48 \inmodule Qt3DAnimation
49 \since 5.9
50 \inherits Qt3DAnimation::QAbstractAnimation
51
52 A Qt3DAnimation::QVertexBlendAnimation class implements vertex-blend morphing animation
53 to a target \l {Qt3DRender::QGeometryRenderer}{QGeometryRenderer}. The QVertexBlendAnimation
54 sets the correct \l {Qt3DRender::QAttribute}{QAttributes} from the
55 \l {Qt3DAnimation::QMorphTarget}{morph targets} to the target
56 \l {Qt3DRender::QGeometryRenderer::geometry} {QGeometryRenderer::geometry} and calculates
57 interpolator for the current position. Unlike with QMorphingAnimation, where the blending is
58 controller with blend weights, the blending occurs between sequential morph targets.
59 The actual blending between the attributes must be implemented in the material.
60 Qt3DAnimation::QMorphPhongMaterial implements material with morphing support for phong
61 lighting model. The blending happens between 2 attributes - 'base' and 'target'.
62 The names for the base and target attributes are taken from the morph target names,
63 where the base attribute retains the name it already has and the target attribute name
64 gets 'Target' appended to the name. The interpolator can be set as
65 a \l {Qt3DRender::QParameter}{QParameter} to the used material.
66 All morph targets in the animation should contain the attributes with same names as those
67 in the base geometry.
68
69*/
70/*!
71 \qmltype VertexBlendAnimation
72 \brief A type implementing vertex-blend morphing animation.
73 \inqmlmodule Qt3D.Animation
74 \since 5.9
75 \inherits AbstractAnimation
76 \instantiates Qt3DAnimation::QVertexBlendAnimation
77
78 A VertexBlendAnimation type implements vertex-blend morphing animation
79 to a target \l GeometryRenderer. The VertexBlendAnimation sets the correct
80 \l {Attribute}{Attributes} from the morph targets to the target
81 \l {Qt3D.Render::GeometryRenderer::geometry}{GeometryRenderer::geometry} and calculates
82 interpolator for the current position. Unlike with MorphingAnimation, where the blending is
83 controller with blend weights, the blending occurs between sequential morph targets.
84 The actual blending between the attributes must be implemented in the material.
85 MorphPhongMaterial implements material with morphing support for phong lighting model.
86 The blending happens between 2 attributes - 'base' and 'target'. The names for the base
87 and target attributes are taken from the morph target names, where the base attribute
88 retains the name it already has and the target attribute name gets 'Target' appended to
89 the name. All morph targets in the animation should contain the attributes with same names
90 as those in the base geometry.
91
92*/
93/*!
94 \property Qt3DAnimation::QVertexBlendAnimation::targetPositions
95 Holds the position values of the morph target. Each position in the list specifies the position
96 of the corresponding morph target with the same index. The values must be in an ascending order.
97 Values can be positive or negative and do not have any predefined unit.
98*/
99/*!
100 \property Qt3DAnimation::QVertexBlendAnimation::interpolator
101 Holds the interpolator between base and target attributes.
102 \readonly
103*/
104/*!
105 \property Qt3DAnimation::QVertexBlendAnimation::target
106 Holds the target QGeometryRenderer the morphing animation is applied to.
107*/
108/*!
109 \property Qt3DAnimation::QVertexBlendAnimation::targetName
110 Holds the name of the target geometry. This is a convenience property making it
111 easier to match the target geometry to the morphing animation. The name
112 is usually same as the name of the parent entity of the target QGeometryRenderer, but
113 does not have to be.
114*/
115
116/*!
117 \qmlproperty list<real> VertexBlendAnimation::targetPositions
118 Holds the position values of the morph target. Each position in the list specifies the position
119 of the corresponding morph target with the same index. The values must be in an ascending order.
120 Values can be positive or negative and do not have any predefined unit.
121*/
122/*!
123 \qmlproperty real VertexBlendAnimation::interpolator
124 Holds the interpolator between base and target attributes.
125 \readonly
126*/
127/*!
128 \qmlproperty GeometryRenderer VertexBlendAnimation::target
129 Holds the target GeometryRenderer the morphing animation is applied to.
130*/
131/*!
132 \qmlproperty string VertexBlendAnimation::targetName
133 Holds the name of the target geometry. This is a convenience property making it
134 easier to match the target geometry to the morphing animation. The name
135 is usually same as the name of the parent entity of the target GeometryRenderer, but
136 does not have to be.
137*/
138/*!
139 \qmlproperty list<MorphTarget> VertexBlendAnimation::morphTargets
140 Holds the list of \l {MorphTarget}{morph targets} added to the animation.
141*/
142
143QVertexBlendAnimationPrivate::QVertexBlendAnimationPrivate()
144 : QAbstractAnimationPrivate(QAbstractAnimation::VertexBlendAnimation)
145 , m_interpolator(0.0f)
146 , m_target(nullptr)
147 , m_currentBase(nullptr)
148 , m_currentTarget(nullptr)
149{
150
151}
152
153void QVertexBlendAnimationPrivate::getAttributesInPosition(float position, int *target0,
154 int *target1, float *interpolator)
155{
156 if (position < m_targetPositions.first()) {
157 *target0 = 0;
158 *target1 = qMin(1, m_targetPositions.size());
159 *interpolator = 0.0f;
160 } else if (position > m_targetPositions.last()) {
161 *target0 = qMax(m_targetPositions.size() - 2, 0);
162 *target1 = qMax(m_targetPositions.size() - 1, 0);
163 *interpolator = 1.0f;
164 } else {
165 for (int i = 0; i < m_targetPositions.size() - 1; i++) {
166 if (position >= m_targetPositions[i] && position < m_targetPositions[i + 1]) {
167 *target0 = i;
168 *target1 = i + 1;
169 float a = (position - m_targetPositions[i])
170 / (m_targetPositions[i + 1] - m_targetPositions[i]);
171 *interpolator = a;
172 }
173 }
174 }
175}
176
177void QVertexBlendAnimationPrivate::updateAnimation(float position)
178{
179 Q_Q(QVertexBlendAnimation);
180 if (!m_target || !m_target->geometry())
181 return;
182
183 Qt3DAnimation::QMorphTarget *base;
184 Qt3DAnimation::QMorphTarget *target;
185 int target0, target1;
186 float interpolator;
187 getAttributesInPosition(position, &target0, &target1, &interpolator);
188
189 base = m_morphTargets.at(target0);
190 target = m_morphTargets.at(target1);
191
192 Qt3DRender::QGeometry *geometry = m_target->geometry();
193
194 // remove attributes from previous frame
195 if ((m_currentBase && (base != m_currentBase))
196 || (m_currentTarget && (target != m_currentTarget))) {
197 const QVector<Qt3DRender::QAttribute *> baseAttributes = m_currentBase->attributeList();
198 const QVector<Qt3DRender::QAttribute *> targetAttributes = m_currentTarget->attributeList();
199 for (int i = 0; i < baseAttributes.size(); ++i) {
200 geometry->removeAttribute(baseAttributes.at(i));
201 geometry->removeAttribute(targetAttributes.at(i));
202 }
203 }
204
205 const QVector<Qt3DRender::QAttribute *> baseAttributes = base->attributeList();
206 const QVector<Qt3DRender::QAttribute *> targetAttributes = target->attributeList();
207 const QStringList attributeNames = base->attributeNames();
208
209 // add attributes from current frame to the geometry
210 if (base != m_currentBase || target != m_currentTarget) {
211 for (int i = 0; i < baseAttributes.size(); ++i) {
212 const QString baseName = attributeNames.at(i);
213 QString targetName = baseName;
214 targetName.append(QLatin1String("Target"));
215
216 baseAttributes[i]->setName(baseName);
217 geometry->addAttribute(baseAttributes.at(i));
218 targetAttributes[i]->setName(targetName);
219 geometry->addAttribute(targetAttributes.at(i));
220 }
221 }
222 m_currentBase = base;
223 m_currentTarget = target;
224
225 if (!qFuzzyCompare(interpolator, m_interpolator)) {
226 m_interpolator = interpolator;
227 emit q->interpolatorChanged(interpolator);
228 }
229}
230
231/*!
232 Construct a new QVertexBlendAnimation with \a parent.
233 */
234QVertexBlendAnimation::QVertexBlendAnimation(QObject *parent)
235 : QAbstractAnimation(*new QVertexBlendAnimationPrivate, parent)
236{
237 Q_D(QVertexBlendAnimation);
238 d->m_positionConnection = QObject::connect(this, &QAbstractAnimation::positionChanged,
239 this, &QVertexBlendAnimation::updateAnimation);
240}
241
242QVector<float> QVertexBlendAnimation::targetPositions() const
243{
244 Q_D(const QVertexBlendAnimation);
245 return d->m_targetPositions;
246}
247
248float QVertexBlendAnimation::interpolator() const
249{
250 Q_D(const QVertexBlendAnimation);
251 return d->m_interpolator;
252}
253
254Qt3DRender::QGeometryRenderer *QVertexBlendAnimation::target() const
255{
256 Q_D(const QVertexBlendAnimation);
257 return d->m_target;
258}
259
260QString QVertexBlendAnimation::targetName() const
261{
262 Q_D(const QVertexBlendAnimation);
263 return d->m_targetName;
264}
265
266/*!
267 Set morph \a targets to animation. Old targets are cleared.
268*/
269void QVertexBlendAnimation::setMorphTargets(const QVector<Qt3DAnimation::QMorphTarget *> &targets)
270{
271 Q_D(QVertexBlendAnimation);
272 d->m_morphTargets = targets;
273}
274
275/*!
276 Add new morph \a target at the end of the animation.
277*/
278void QVertexBlendAnimation::addMorphTarget(Qt3DAnimation::QMorphTarget *target)
279{
280 Q_D(QVertexBlendAnimation);
281 if (!d->m_morphTargets.contains(target))
282 d->m_morphTargets.push_back(target);
283}
284
285/*!
286 Remove morph \a target from the animation.
287*/
288void QVertexBlendAnimation::removeMorphTarget(Qt3DAnimation::QMorphTarget *target)
289{
290 Q_D(QVertexBlendAnimation);
291 d->m_morphTargets.removeAll(target);
292}
293
294void QVertexBlendAnimation::setTargetPositions(const QVector<float> &targetPositions)
295{
296 Q_D(QVertexBlendAnimation);
297 if (d->m_targetPositions == targetPositions)
298 return;
299 d->m_targetPositions = targetPositions;
300 emit targetPositionsChanged(targetPositions);
301 setDuration(d->m_targetPositions.last());
302}
303
304void QVertexBlendAnimation::setTarget(Qt3DRender::QGeometryRenderer *target)
305{
306 Q_D(QVertexBlendAnimation);
307 if (d->m_target != target) {
308 d->m_target = target;
309 emit targetChanged(target);
310 }
311}
312
313/*!
314 Return morph target list.
315*/
316QVector<Qt3DAnimation::QMorphTarget *> QVertexBlendAnimation::morphTargetList()
317{
318 Q_D(QVertexBlendAnimation);
319 return d->m_morphTargets;
320}
321
322void QVertexBlendAnimation::setTargetName(const QString name)
323{
324 Q_D(QVertexBlendAnimation);
325 if (d->m_targetName != name) {
326 d->m_targetName = name;
327 emit targetNameChanged(name);
328 }
329}
330
331void QVertexBlendAnimation::updateAnimation(float position)
332{
333 Q_D(QVertexBlendAnimation);
334 d->updateAnimation(position);
335}
336
337} // Qt3DAnimation
338
339QT_END_NAMESPACE
340