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 | |
8 | QT_BEGIN_NAMESPACE |
9 | |
10 | namespace 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 | |
110 | QVertexBlendAnimationPrivate::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 | |
120 | void 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 | |
144 | void 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 | */ |
201 | QVertexBlendAnimation::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 | |
209 | QList<float> QVertexBlendAnimation::targetPositions() const |
210 | { |
211 | Q_D(const QVertexBlendAnimation); |
212 | return d->m_targetPositions; |
213 | } |
214 | |
215 | float QVertexBlendAnimation::interpolator() const |
216 | { |
217 | Q_D(const QVertexBlendAnimation); |
218 | return d->m_interpolator; |
219 | } |
220 | |
221 | Qt3DRender::QGeometryRenderer *QVertexBlendAnimation::target() const |
222 | { |
223 | Q_D(const QVertexBlendAnimation); |
224 | return d->m_target; |
225 | } |
226 | |
227 | QString 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 | */ |
236 | void 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 | */ |
245 | void 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 | */ |
255 | void QVertexBlendAnimation::removeMorphTarget(Qt3DAnimation::QMorphTarget *target) |
256 | { |
257 | Q_D(QVertexBlendAnimation); |
258 | d->m_morphTargets.removeAll(t: target); |
259 | } |
260 | |
261 | void 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 | |
271 | void 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 | */ |
283 | QList<Qt3DAnimation::QMorphTarget *> QVertexBlendAnimation::morphTargetList() |
284 | { |
285 | Q_D(QVertexBlendAnimation); |
286 | return d->m_morphTargets; |
287 | } |
288 | |
289 | void 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 | |
298 | void QVertexBlendAnimation::updateAnimation(float position) |
299 | { |
300 | Q_D(QVertexBlendAnimation); |
301 | d->updateAnimation(position); |
302 | } |
303 | |
304 | } // Qt3DAnimation |
305 | |
306 | QT_END_NAMESPACE |
307 | |
308 | #include "moc_qvertexblendanimation.cpp" |
309 | |