1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQuick module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickspritesequence_p.h"
43 #include "qquicksprite_p.h"
44 #include "qquickspriteengine_p.h"
45 #include <QtQuick/private/qsgcontext_p.h>
46 #include <private/qsgadaptationlayer_p.h>
47 #include <QtQuick/qsgnode.h>
48 #include <QtQuick/qsgtexturematerial.h>
49 #include <QtQuick/qsgtexture.h>
50 #include <QtQuick/qquickcanvas.h>
51 #include <QtQml/qqmlinfo.h>
59 static const char vertexShaderCode[] =
60 "attribute highp vec2 vTex;\n"
61 "uniform highp vec3 animData;// w,h(premultiplied of anim), interpolation progress\n"
62 "uniform highp vec4 animPos;//x,y, x,y (two frames for interpolation)\n"
63 "uniform highp vec2 size;//w,h of element\n"
65 "uniform highp mat4 qt_Matrix;\n"
67 "varying highp vec4 fTexS;\n"
68 "varying lowp float progress;\n"
72 " progress = animData.z;\n"
73 " //Calculate frame location in texture\n"
74 " fTexS.xy = animPos.xy + vTex.xy * animData.xy;\n"
75 " //Next frame is also passed, for interpolation\n"
76 " fTexS.zw = animPos.zw + vTex.xy * animData.xy;\n"
78 " gl_Position = qt_Matrix * vec4(size.x * vTex.x, size.y * vTex.y, 0, 1);\n"
81 static const char fragmentShaderCode[] =
82 "uniform sampler2D texture;\n"
83 "uniform lowp float qt_Opacity;\n"
85 "varying highp vec4 fTexS;\n"
86 "varying lowp float progress;\n"
89 " gl_FragColor = mix(texture2D(texture, fTexS.xy), texture2D(texture, fTexS.zw), progress) * qt_Opacity;\n"
92 class QQuickSpriteSequenceMaterial : public QSGMaterial
95 QQuickSpriteSequenceMaterial();
96 ~QQuickSpriteSequenceMaterial();
97 virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
98 virtual QSGMaterialShader *createShader() const;
99 virtual int compare(const QSGMaterial *other) const
101 return this - static_cast<const QQuickSpriteSequenceMaterial *>(other);
117 QQuickSpriteSequenceMaterial::QQuickSpriteSequenceMaterial()
126 , elementHeight(1.0f)
128 setFlag(Blending, true);
131 QQuickSpriteSequenceMaterial::~QQuickSpriteSequenceMaterial()
136 class SpriteSequenceMaterialData : public QSGMaterialShader
139 SpriteSequenceMaterialData(const char * /* vertexFile */ = 0, const char * /* fragmentFile */ = 0)
144 QSGMaterialShader::deactivate();
146 for (int i=0; i<8; ++i) {
147 program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
151 virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
153 QQuickSpriteSequenceMaterial *m = static_cast<QQuickSpriteSequenceMaterial *>(newEffect);
156 program()->setUniformValue(m_opacity_id, state.opacity());
157 program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT);
158 program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2);
159 program()->setUniformValue(m_size_id, m->elementWidth, m->elementHeight);
161 if (state.isMatrixDirty())
162 program()->setUniformValue(m_matrix_id, state.combinedMatrix());
165 virtual void initialize() {
166 m_matrix_id = program()->uniformLocation("qt_Matrix");
167 m_opacity_id = program()->uniformLocation("qt_Opacity");
168 m_animData_id = program()->uniformLocation("animData");
169 m_animPos_id = program()->uniformLocation("animPos");
170 m_size_id = program()->uniformLocation("size");
173 virtual const char *vertexShader() const { return vertexShaderCode; }
174 virtual const char *fragmentShader() const { return fragmentShaderCode; }
176 virtual char const *const *attributeNames() const {
177 static const char *attr[] = {
190 static float chunkOfBytes[1024];
193 float SpriteSequenceMaterialData::chunkOfBytes[1024];
195 QSGMaterialShader *QQuickSpriteSequenceMaterial::createShader() const
197 return new SpriteSequenceMaterialData;
200 struct SpriteVertex {
205 struct SpriteVertices {
213 \qmlclass SpriteSequence QQuickSpriteSequence
214 \inqmlmodule QtQuick 2
216 \brief The SpriteSequence element draws a sprite animation
218 SpriteSequence renders and controls a list of animations defined
219 by \l Sprite elements.
221 For full details, see the \l{Sprite Animation} overview.
224 \qmlproperty bool QtQuick2::SpriteSequence::running
226 Whether the sprite is animating or not.
231 \qmlproperty bool QtQuick2::SpriteSequence::interpolate
233 If true, interpolation will occur between sprite frames to make the
234 animation appear smoother.
239 \qmlproperty string QtQuick2::SpriteSequence::goalSprite
241 The name of the Sprite which is currently animating.
244 \qmlproperty string QtQuick2::SpriteSequence::goalSprite
246 The name of the Sprite which the animation should move to.
248 Sprite states have defined durations and transitions between them, setting goalState
249 will cause it to disregard any path weightings (including 0) and head down the path
250 which will reach the goalState quickest (fewest animations). It will pass through
251 intermediate states on that path, and animate them for their duration.
253 If it is possible to return to the goalState from the starting point of the goalState
254 it will continue to do so until goalState is set to "" or an unreachable state.
256 /*! \qmlmethod void QtQuick2::SpriteSequence::jumpTo(string sprite)
258 This function causes the sprite to jump to the specified state immediately, intermediate
259 states are not played.
262 \qmlproperty list<Sprite> QtQuick2::SpriteSequence::sprites
264 The sprite or sprites to draw. Sprites will be scaled to the size of this element.
267 //TODO: Implicitly size element to size of first sprite?
268 QQuickSpriteSequence::QQuickSpriteSequence(QQuickItem *parent) :
274 , m_pleaseReset(false)
276 , m_interpolate(true)
279 setFlag(ItemHasContents);
280 connect(this, SIGNAL(runningChanged(bool)),
281 this, SLOT(update()));
284 void QQuickSpriteSequence::jumpTo(const QString &sprite)
288 m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite), 0, true);
291 void QQuickSpriteSequence::setGoalSprite(const QString &sprite)
293 if (m_goalState != sprite){
294 m_goalState = sprite;
295 emit goalSpriteChanged(sprite);
296 m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite));
300 QQmlListProperty<QQuickSprite> QQuickSpriteSequence::sprites()
302 return QQmlListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
305 void QQuickSpriteSequence::createEngine()
307 //TODO: delay until component complete
309 delete m_spriteEngine;
310 if (m_sprites.count())
311 m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
317 static QSGGeometry::Attribute SpriteSequence_Attributes[] = {
318 QSGGeometry::Attribute::create(0, 2, GL_FLOAT), // tex
321 static QSGGeometry::AttributeSet SpriteSequence_AttributeSet =
323 1, // Attribute Count
325 SpriteSequence_Attributes
328 QSGGeometryNode* QQuickSpriteSequence::buildNode()
330 if (!m_spriteEngine) {
331 qmlInfo(this) << "No sprite engine...";
333 } else if (m_spriteEngine->status() == QQuickPixmap::Null) {
334 m_spriteEngine->startAssemblingImage();
335 update();//Schedule another update, where we will check again
337 } else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
338 update();//Schedule another update, where we will check again
342 m_material = new QQuickSpriteSequenceMaterial();
344 QImage image = m_spriteEngine->assembledImage();
347 m_sheetSize = QSizeF(image.size());
348 m_material->texture = canvas()->createTextureFromImage(image);
349 m_material->texture->setFiltering(QSGTexture::Linear);
350 m_spriteEngine->start(0);
351 m_material->animT = 0;
352 m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
353 m_material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
354 m_material->animX2 = m_material->animX1;
355 m_material->animY2 = m_material->animY1;
356 m_material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
357 m_material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
358 m_material->elementWidth = width();
359 m_material->elementHeight = height();
360 m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
361 emit currentSpriteChanged(m_curState);
365 QSGGeometry *g = new QSGGeometry(SpriteSequence_AttributeSet, vCount, iCount);
366 g->setDrawingMode(GL_TRIANGLES);
368 SpriteVertices *p = (SpriteVertices *) g->vertexData();
382 quint16 *indices = g->indexDataAsUShort();
392 m_node = new QSGGeometryNode();
393 m_node->setGeometry(g);
394 m_node->setMaterial(m_material);
395 m_node->setFlag(QSGGeometryNode::OwnsMaterial);
399 void QQuickSpriteSequence::reset()
401 m_pleaseReset = true;
404 QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
411 m_pleaseReset = false;
419 m_node->markDirty(QSGNode::DirtyMaterial);
425 void QQuickSpriteSequence::prepareNextFrame()
428 m_node = buildNode();
429 if (m_node == 0) //error creating node
432 uint timeInt = m_timestamp.elapsed();
433 qreal time = timeInt / 1000.;
434 m_material->elementHeight = height();
435 m_material->elementWidth = width();
438 m_spriteEngine->updateSprites(timeInt);
439 if (m_curStateIdx != m_spriteEngine->curState()) {
440 m_curStateIdx = m_spriteEngine->curState();
441 m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
442 emit currentSpriteChanged(m_curState);
447 qreal animT = m_spriteEngine->spriteStart()/1000.0;
448 qreal frameCount = m_spriteEngine->spriteFrames();
449 qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
452 if (frameDuration > 0) {
453 qreal frame = (time - animT)/(frameDuration / 1000.0);
454 frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
455 progress = modf(frame,&frameAt);
458 if (m_curFrame >= frameCount){
460 m_spriteEngine->advance();
462 frameAt = m_curFrame;
465 if (m_spriteEngine->sprite()->reverse())
466 frameAt = (m_spriteEngine->spriteFrames() - 1) - frameAt;
467 qreal y = m_spriteEngine->spriteY() / m_sheetSize.height();
468 qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
469 qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
470 qreal x1 = m_spriteEngine->spriteX() / m_sheetSize.width();
473 if (frameAt < (frameCount-1))
476 m_material->animX1 = x1;
477 m_material->animY1 = y;
478 m_material->animX2 = x2;
479 m_material->animY2 = y;
480 m_material->animW = w;
481 m_material->animH = h;
482 m_material->animT = m_interpolate ? progress : 0.0;