1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qquick3dparticlespritesequence_p.h" |
5 | #include "qquick3dparticlespriteparticle_p.h" |
6 | |
7 | QT_BEGIN_NAMESPACE |
8 | |
9 | /*! |
10 | \qmltype SpriteSequence3D |
11 | \inherits QtObject |
12 | \inqmlmodule QtQuick3D.Particles3D |
13 | \brief Provides image sequence features for the Sprite particles. |
14 | \since 6.2 |
15 | |
16 | The SpriteSequence3D element provides support for animated images with multiple frames. The frames |
17 | should be aligned horizontally in the image, first frame being on the left and last on the right. |
18 | |
19 | To make a \l SpriteParticle3D an animated sequence, set its \l {SpriteParticle3D::spriteSequence}{spriteSequence} property. |
20 | */ |
21 | |
22 | QQuick3DParticleSpriteSequence::QQuick3DParticleSpriteSequence(QObject *parent) |
23 | : QObject(parent) |
24 | { |
25 | |
26 | } |
27 | |
28 | QQuick3DParticleSpriteSequence::~QQuick3DParticleSpriteSequence() |
29 | { |
30 | if (m_parentParticle) |
31 | m_parentParticle->setSpriteSequence(nullptr); |
32 | } |
33 | |
34 | /*! |
35 | \qmlproperty int SpriteSequence3D::frameCount |
36 | |
37 | This property defines the amount of image frames in \l {SpriteParticle3D::}{sprite}. |
38 | Particle animates through these frames during its \l duration. |
39 | The frames should be laid out horizontally in the same image file. For example, |
40 | \e sprite could be a \c {512x64} image, with \c frameCount of \c 8. This would make |
41 | each particle frame size \c {64x64} pixels. |
42 | |
43 | The default value is \c 1. |
44 | |
45 | \note If your image only has a single sprite frame, don't define the |
46 | \l {SpriteParticle3D::}{spriteSequence} property at all. |
47 | |
48 | \sa interpolate |
49 | */ |
50 | int QQuick3DParticleSpriteSequence::frameCount() const |
51 | { |
52 | return m_frameCount; |
53 | } |
54 | |
55 | /*! |
56 | \qmlproperty int SpriteSequence3D::frameIndex |
57 | |
58 | This property defines the initial index of the frame. This is the position in between frames |
59 | where the animation is started. For example when the \c frameIndex is 5 and the \l animationDirection |
60 | is \c Normal, the first rendered frame is 5. If the \l animationDirection is \c Reverse, the |
61 | first rendered frame is 4. |
62 | |
63 | The value of frameIndex must be between 0 and \l{frameCount} - \c 1. When the \l animationDirection |
64 | is \c SingleFrame and \l randomStart is \c false, all the particles will render sprites with the |
65 | \c frameIndex. |
66 | |
67 | The default value is \c 0. |
68 | |
69 | \sa randomStart, animationDirection |
70 | */ |
71 | int QQuick3DParticleSpriteSequence::frameIndex() const |
72 | { |
73 | return m_frameIndex; |
74 | } |
75 | |
76 | /*! |
77 | \qmlproperty bool SpriteSequence3D::interpolate |
78 | |
79 | This property defines if the sprites are interpolated (blended) between frames |
80 | to make the animation appear smoother. |
81 | |
82 | The default value is \c true. |
83 | |
84 | \sa frameCount |
85 | */ |
86 | bool QQuick3DParticleSpriteSequence::interpolate() const |
87 | { |
88 | return m_interpolate; |
89 | } |
90 | |
91 | /*! |
92 | \qmlproperty int SpriteSequence3D::duration |
93 | |
94 | This property defines the duration in milliseconds how long it takes for the |
95 | sprite sequence to animate. For example, if the \l duration is \c 400 and the |
96 | \l frameCount is 8, each frame will be shown for 50 milliseconds. When the |
97 | value is -1, the particle lifeSpan is used as the duration. |
98 | |
99 | The default value is \c -1. |
100 | */ |
101 | int QQuick3DParticleSpriteSequence::duration() const |
102 | { |
103 | return m_duration; |
104 | } |
105 | |
106 | /*! |
107 | \qmlproperty int SpriteSequence3D::durationVariation |
108 | |
109 | This property defines the duration variation in milliseconds. The actual duration |
110 | of the animation is between \c duration - \c durationVariation and \c duration + |
111 | \c durationVariation. |
112 | |
113 | The default value is \c 0 (no variation). |
114 | */ |
115 | int QQuick3DParticleSpriteSequence::durationVariation() const |
116 | { |
117 | return m_durationVariation; |
118 | } |
119 | |
120 | /*! |
121 | \qmlproperty bool SpriteSequence3D::randomStart |
122 | |
123 | This property defines if the animation should start from a random frame between \c 0 and \l frameCount - \c 1. |
124 | This allows animations to not look like they all just started when the animation begins. |
125 | |
126 | The default value is \c false. |
127 | |
128 | \sa animationDirection |
129 | */ |
130 | bool QQuick3DParticleSpriteSequence::randomStart() const |
131 | { |
132 | return m_randomStart; |
133 | } |
134 | |
135 | /*! |
136 | \qmlproperty AnimationDirection SpriteSequence3D::animationDirection |
137 | |
138 | This property defines the animation direction of the sequence. |
139 | |
140 | The default value is \c SpriteSequence3D.Normal. |
141 | |
142 | \sa randomStart |
143 | */ |
144 | |
145 | /*! |
146 | \qmlproperty enumeration SpriteSequence3D::AnimationDirection |
147 | |
148 | Defines the animation playback direction of the sequence. |
149 | |
150 | \value SpriteSequence3D.Normal |
151 | Animate from the first frame to the last frame. When the last frame is reached, jump back to the first frame. |
152 | \value SpriteSequence3D.Reverse |
153 | Animate from the last frame to the first frame. When the first frame is reached, jump back to the last frame. |
154 | \value SpriteSequence3D.Alternate |
155 | Animate from the first frame to the last frame. When the last or first frame is reached, switch the animation direction. |
156 | This makes the sequence animation smooth even when the first and the last frames don't match. |
157 | \value SpriteSequence3D.AlternateReverse |
158 | Animate from the last frame to the first frame. When the last or first frame is reached, switch the animation direction. |
159 | This makes the sequence animation smooth even when the first and the last frames don't match. |
160 | \value SpriteSequence3D.SingleFrame |
161 | Don't animate the frame. When the \l randomStart is false, \l frameIndex frame is rendered. |
162 | When the \l randomStart is true, each particle renders a random frame. |
163 | */ |
164 | QQuick3DParticleSpriteSequence::AnimationDirection QQuick3DParticleSpriteSequence::animationDirection() const |
165 | { |
166 | return m_animationDirection; |
167 | } |
168 | |
169 | void QQuick3DParticleSpriteSequence::setFrameCount(int frameCount) |
170 | { |
171 | if (m_frameCount == frameCount) |
172 | return; |
173 | m_frameCount = std::max(a: 1, b: frameCount); |
174 | markNodesDirty(); |
175 | Q_EMIT frameCountChanged(); |
176 | } |
177 | |
178 | void QQuick3DParticleSpriteSequence::setFrameIndex(int frameIndex) |
179 | { |
180 | if (m_frameIndex == frameIndex) |
181 | return; |
182 | m_frameIndex = std::max(a: 0, b: frameIndex); |
183 | markNodesDirty(); |
184 | Q_EMIT frameIndexChanged(); |
185 | } |
186 | |
187 | void QQuick3DParticleSpriteSequence::setInterpolate(bool interpolate) |
188 | { |
189 | if (m_interpolate == interpolate) |
190 | return; |
191 | m_interpolate = interpolate; |
192 | markNodesDirty(); |
193 | Q_EMIT interpolateChanged(); |
194 | } |
195 | |
196 | void QQuick3DParticleSpriteSequence::setDuration(int duration) |
197 | { |
198 | if (m_duration == duration) |
199 | return; |
200 | |
201 | m_duration = duration; |
202 | markNodesDirty(); |
203 | Q_EMIT durationChanged(); |
204 | } |
205 | |
206 | void QQuick3DParticleSpriteSequence::setDurationVariation(int durationVariation) |
207 | { |
208 | if (m_durationVariation == durationVariation) |
209 | return; |
210 | |
211 | m_durationVariation = durationVariation; |
212 | markNodesDirty(); |
213 | Q_EMIT durationVariationChanged(); |
214 | } |
215 | |
216 | void QQuick3DParticleSpriteSequence::setRandomStart(bool randomStart) |
217 | { |
218 | if (m_randomStart == randomStart) |
219 | return; |
220 | m_randomStart = randomStart; |
221 | markNodesDirty(); |
222 | Q_EMIT randomStartChanged(); |
223 | } |
224 | |
225 | void QQuick3DParticleSpriteSequence::setAnimationDirection(QQuick3DParticleSpriteSequence::AnimationDirection animationDirection) |
226 | { |
227 | if (m_animationDirection == animationDirection) |
228 | return; |
229 | m_animationDirection = animationDirection; |
230 | markNodesDirty(); |
231 | Q_EMIT animationDirectionChanged(); |
232 | } |
233 | |
234 | void QQuick3DParticleSpriteSequence::componentComplete() |
235 | { |
236 | m_parentParticle = qobject_cast<QQuick3DParticleSpriteParticle *>(object: parent()); |
237 | if (!m_parentParticle) |
238 | qWarning() << "SpriteSequence3D requires parent SpriteParticle3D to function correctly!" ; |
239 | } |
240 | |
241 | void QQuick3DParticleSpriteSequence::markNodesDirty() |
242 | { |
243 | if (m_parentParticle) |
244 | m_parentParticle->markNodesDirty(); |
245 | } |
246 | |
247 | // Returns the first frame of the sequence. |
248 | // Return range [0..1) where 0.0 is the first frame and 0.9999 is the last. |
249 | float QQuick3DParticleSpriteSequence::firstFrame(int index, bool singleFrame) |
250 | { |
251 | float firstFrame = 0.0f; |
252 | if (m_randomStart) { |
253 | if (!m_parentParticle || !m_parentParticle->m_system) |
254 | return firstFrame; |
255 | auto rand = m_parentParticle->m_system->rand(); |
256 | firstFrame = rand->get(particleIndex: index, user: QPRand::SpriteAnimationI); |
257 | } else if (m_frameCount > 1 && m_frameIndex > 0) { |
258 | int frameIndex = std::min(a: m_frameIndex, b: m_frameCount - 1); |
259 | if (singleFrame) |
260 | firstFrame = float(frameIndex) / (float(m_frameCount - 1) + 0.0001f); |
261 | else |
262 | firstFrame = float(frameIndex) / float(m_frameCount); |
263 | } |
264 | return firstFrame; |
265 | } |
266 | |
267 | QT_END_NAMESPACE |
268 | |