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

source code of qt3d/src/animation/frontend/qvertexblendanimation.cpp