aboutsummaryrefslogtreecommitdiffstats
path: root/src/declarative/items
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/items')
-rw-r--r--src/declarative/items/items.pri6
-rw-r--r--src/declarative/items/qsgitemsmodule.cpp4
-rw-r--r--src/declarative/items/qsgsprite.cpp57
-rw-r--r--src/declarative/items/qsgsprite_p.h231
-rw-r--r--src/declarative/items/qsgspriteengine.cpp437
-rw-r--r--src/declarative/items/qsgspriteengine_p.h161
-rw-r--r--src/declarative/items/qsgspriteimage.cpp353
-rw-r--r--src/declarative/items/qsgspriteimage_p.h114
8 files changed, 1363 insertions, 0 deletions
diff --git a/src/declarative/items/items.pri b/src/declarative/items/items.pri
index d6942973cd..f29a82e77e 100644
--- a/src/declarative/items/items.pri
+++ b/src/declarative/items/items.pri
@@ -61,6 +61,9 @@ HEADERS += \
$$PWD/qsgcanvasitem_p.h \
$$PWD/qsgcontext2d_p.h \
$$PWD/qsgcontext2d_p_p.h \
+ $$PWD/qsgspriteengine_p.h \
+ $$PWD/qsgsprite_p.h \
+ $$PWD/qsgspriteimage_p.h \
SOURCES += \
$$PWD/qsgevents.cpp \
@@ -100,6 +103,9 @@ SOURCES += \
$$PWD/qsgimplicitsizeitem.cpp \
$$PWD/qsgcanvasitem.cpp \
$$PWD/qsgcontext2d.cpp \
+ $$PWD/qsgspriteengine.cpp \
+ $$PWD/qsgsprite.cpp \
+ $$PWD/qsgspriteimage.cpp \
SOURCES += \
$$PWD/qsgshadereffectitem.cpp \
diff --git a/src/declarative/items/qsgitemsmodule.cpp b/src/declarative/items/qsgitemsmodule.cpp
index 6ea20bb38b..a29776fc68 100644
--- a/src/declarative/items/qsgitemsmodule.cpp
+++ b/src/declarative/items/qsgitemsmodule.cpp
@@ -75,6 +75,8 @@
//#include "private/qsgpincharea_p.h"
#include "qsgcanvasitem_p.h"
#include "qsgcontext2d_p.h"
+#include "qsgsprite_p.h"
+#include "qsgspriteimage_p.h"
static QDeclarativePrivate::AutoParentResult qsgitem_autoParent(QObject *obj, QObject *parent)
{
@@ -179,6 +181,8 @@ static void qt_sgitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QSGContext2D>();
qmlRegisterType<QSGCanvasGradient>();
+ qmlRegisterType<QSGSprite>("QtQuick", 2, 0, "Sprite");
+ qmlRegisterType<QSGSpriteImage>("QtQuick", 2, 0, "SpriteImage");
qmlRegisterType<QSGParentChange>(uri, major, minor,"ParentChange");
qmlRegisterType<QSGAnchorChanges>(uri, major, minor,"AnchorChanges");
diff --git a/src/declarative/items/qsgsprite.cpp b/src/declarative/items/qsgsprite.cpp
new file mode 100644
index 0000000000..694976a540
--- /dev/null
+++ b/src/declarative/items/qsgsprite.cpp
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsprite_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGSprite::QSGSprite(QObject *parent) :
+ QObject(parent)
+ , m_generatedCount(0)
+ , m_framesPerRow(0)
+ , m_frames(1)
+ , m_frameHeight(0)
+ , m_frameWidth(0)
+ , m_duration(1000)
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgsprite_p.h b/src/declarative/items/qsgsprite_p.h
new file mode 100644
index 0000000000..652a4cd482
--- /dev/null
+++ b/src/declarative/items/qsgsprite_p.h
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SPRITESTATE_H
+#define SPRITESTATE_H
+
+#include <QObject>
+#include <QUrl>
+#include <QVariantMap>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+
+class QSGSprite : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(int frames READ frames WRITE setFrames NOTIFY framesChanged)
+ //If frame height or width is not specified, it is assumed to be a single long row of frames.
+ //Otherwise, it can be multiple contiguous rows, when one row runs out the next will be used.
+ Q_PROPERTY(int frameHeight READ frameHeight WRITE setFrameHeight NOTIFY frameHeightChanged)
+ Q_PROPERTY(int frameWidth READ frameWidth WRITE setFrameWidth NOTIFY frameWidthChanged)
+ Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
+ Q_PROPERTY(int durationVariance READ durationVariance WRITE setDurationVariance NOTIFY durationVarianceChanged)
+ Q_PROPERTY(qreal speedModifiesDuration READ speedModifer WRITE setSpeedModifier NOTIFY speedModifierChanged)
+ Q_PROPERTY(QVariantMap to READ to WRITE setTo NOTIFY toChanged)
+
+public:
+ explicit QSGSprite(QObject *parent = 0);
+
+ QUrl source() const
+ {
+ return m_source;
+ }
+
+ int frames() const
+ {
+ return m_frames;
+ }
+
+ int frameHeight() const
+ {
+ return m_frameHeight;
+ }
+
+ int frameWidth() const
+ {
+ return m_frameWidth;
+ }
+
+ int duration() const
+ {
+ return m_duration;
+ }
+
+ QString name() const
+ {
+ return m_name;
+ }
+
+ QVariantMap to() const
+ {
+ return m_to;
+ }
+
+ qreal speedModifer() const
+ {
+ return m_speedModifier;
+ }
+
+ int durationVariance() const
+ {
+ return m_durationVariance;
+ }
+
+signals:
+
+ void sourceChanged(QUrl arg);
+
+ void framesChanged(int arg);
+
+ void frameHeightChanged(int arg);
+
+ void frameWidthChanged(int arg);
+
+ void durationChanged(int arg);
+
+ void nameChanged(QString arg);
+
+ void toChanged(QVariantMap arg);
+
+ void speedModifierChanged(qreal arg);
+
+ void durationVarianceChanged(int arg);
+
+public slots:
+
+ void setSource(QUrl arg)
+ {
+ if (m_source != arg) {
+ m_source = arg;
+ emit sourceChanged(arg);
+ }
+ }
+
+ void setFrames(int arg)
+ {
+ if (m_frames != arg) {
+ m_frames = arg;
+ emit framesChanged(arg);
+ }
+ }
+
+ void setFrameHeight(int arg)
+ {
+ if (m_frameHeight != arg) {
+ m_frameHeight = arg;
+ emit frameHeightChanged(arg);
+ }
+ }
+
+ void setFrameWidth(int arg)
+ {
+ if (m_frameWidth != arg) {
+ m_frameWidth = arg;
+ emit frameWidthChanged(arg);
+ }
+ }
+
+ void setDuration(int arg)
+ {
+ if (m_duration != arg) {
+ m_duration = arg;
+ emit durationChanged(arg);
+ }
+ }
+
+ void setName(QString arg)
+ {
+ if (m_name != arg) {
+ m_name = arg;
+ emit nameChanged(arg);
+ }
+ }
+
+ void setTo(QVariantMap arg)
+ {
+ if (m_to != arg) {
+ m_to = arg;
+ emit toChanged(arg);
+ }
+ }
+
+ void setSpeedModifier(qreal arg)
+ {
+ if (m_speedModifier != arg) {
+ m_speedModifier = arg;
+ emit speedModifierChanged(arg);
+ }
+ }
+
+ void setDurationVariance(int arg)
+ {
+ if (m_durationVariance != arg) {
+ m_durationVariance = arg;
+ emit durationVarianceChanged(arg);
+ }
+ }
+
+private:
+ friend class QSGImageParticle;
+ friend class QSGSpriteEngine;
+ int m_generatedCount;
+ int m_framesPerRow;
+ QUrl m_source;
+ int m_frames;
+ int m_frameHeight;
+ int m_frameWidth;
+ int m_duration;
+ QString m_name;
+ QVariantMap m_to;
+ qreal m_speedModifier;
+ int m_durationVariance;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // SPRITESTATE_H
diff --git a/src/declarative/items/qsgspriteengine.cpp b/src/declarative/items/qsgspriteengine.cpp
new file mode 100644
index 0000000000..27de0d94f6
--- /dev/null
+++ b/src/declarative/items/qsgspriteengine.cpp
@@ -0,0 +1,437 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgspriteengine_p.h"
+#include "qsgsprite_p.h"
+#include <QDebug>
+#include <QPainter>
+#include <QSet>
+#include <QtOpenGL>
+
+QT_BEGIN_NAMESPACE
+
+QSGSpriteEngine::QSGSpriteEngine(QObject *parent) :
+ QObject(parent), m_timeOffset(0)
+{
+ //Default size 1
+ setCount(1);
+ m_advanceTime.start();
+}
+
+QSGSpriteEngine::QSGSpriteEngine(QList<QSGSprite*> states, QObject *parent) :
+ QObject(parent), m_states(states), m_timeOffset(0)
+{
+ //Default size 1
+ setCount(1);
+ m_advanceTime.start();
+}
+
+QSGSpriteEngine::~QSGSpriteEngine()
+{
+}
+
+int QSGSpriteEngine::maxFrames()
+{
+ return m_maxFrames;
+}
+
+/* States too large to fit in one row are split into multiple rows
+ This is more efficient for the implementation, but should remain an implementation detail (invisible from QML)
+ Therefore the below functions abstract sprite from the viewpoint of classes that pass the details onto shaders
+ But States maintain their listed index for internal structures
+TODO: All these calculations should be pre-calculated and cached during initialization for a significant performance boost
+*/
+int QSGSpriteEngine::spriteState(int sprite)
+{
+ int state = m_sprites[sprite];
+ if(!m_states[state]->m_generatedCount)
+ return state;
+ int rowDuration = m_states[state]->duration() * m_states[state]->m_framesPerRow;
+ int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration;
+ return state + extra;
+}
+
+int QSGSpriteEngine::spriteStart(int sprite)
+{
+ int state = m_sprites[sprite];
+ if(!m_states[state]->m_generatedCount)
+ return m_startTimes[sprite];
+ int rowDuration = m_states[state]->duration() * m_states[state]->m_framesPerRow;
+ int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration;
+ return state + extra*rowDuration;
+}
+
+int QSGSpriteEngine::spriteFrames(int sprite)
+{
+ int state = m_sprites[sprite];
+ if(!m_states[state]->m_generatedCount)
+ return m_states[state]->frames();
+ int rowDuration = m_states[state]->duration() * m_states[state]->m_framesPerRow;
+ int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration;
+ if(extra == m_states[state]->m_generatedCount - 1)//last state
+ return m_states[state]->frames() % m_states[state]->m_framesPerRow;
+ else
+ return m_states[state]->m_framesPerRow;
+}
+
+int QSGSpriteEngine::spriteDuration(int sprite)
+{
+ int state = m_sprites[sprite];
+ if(!m_states[state]->m_generatedCount)
+ return m_states[state]->duration();
+ int rowDuration = m_states[state]->duration() * m_states[state]->m_framesPerRow;
+ int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration;
+ if(extra == m_states[state]->m_generatedCount - 1)//last state
+ return (m_states[state]->duration() * m_states[state]->frames()) % rowDuration;
+ else
+ return rowDuration;
+}
+
+int QSGSpriteEngine::spriteCount()//TODO: Actually image state count, need to rename these things to make sense together
+{
+ return m_imageStateCount;
+}
+
+void QSGSpriteEngine::setGoal(int state, int sprite, bool jump)
+{
+ if(sprite >= m_sprites.count() || state >= m_states.count())
+ return;
+ if(!jump){
+ m_goals[sprite] = state;
+ return;
+ }
+
+ if(m_sprites[sprite] == state)
+ return;//Already there
+ m_sprites[sprite] = state;
+ m_goals[sprite] = -1;
+ restartSprite(sprite);
+ return;
+}
+
+QImage QSGSpriteEngine::assembledImage()
+{
+ int frameHeight = 0;
+ int frameWidth = 0;
+ m_maxFrames = 0;
+ m_imageStateCount = 0;
+
+ int maxSize;
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
+
+ foreach(QSGSprite* state, m_states){
+ if(state->frames() > m_maxFrames)
+ m_maxFrames = state->frames();
+
+ QImage img(state->source().toLocalFile());
+ if (img.isNull()) {
+ qWarning() << "SpriteEngine: loading image failed..." << state->source().toLocalFile();
+ return QImage();
+ }
+
+ //Check that the frame sizes are the same within one engine
+ int imgWidth = state->frameWidth();
+ if(!imgWidth)
+ imgWidth = img.width() / state->frames();
+ if(frameWidth){
+ if(imgWidth != frameWidth){
+ qWarning() << "SpriteEngine: Irregular frame width..." << state->source().toLocalFile();
+ return QImage();
+ }
+ }else{
+ frameWidth = imgWidth;
+ }
+
+ int imgHeight = state->frameHeight();
+ if(!imgHeight)
+ imgHeight = img.height();
+ if(frameHeight){
+ if(imgHeight!=frameHeight){
+ qWarning() << "SpriteEngine: Irregular frame height..." << state->source().toLocalFile();
+ return QImage();
+ }
+ }else{
+ frameHeight = imgHeight;
+ }
+
+ if(state->frames() * frameWidth > maxSize){
+ struct helper{
+ static int divRoundUp(int a, int b){return (a+b-1)/b;}
+ };
+ int rowsNeeded = helper::divRoundUp(state->frames(), helper::divRoundUp(maxSize, frameWidth));
+ if(rowsNeeded * frameHeight > maxSize){
+ qWarning() << "SpriteEngine: Animation too large to fit in one texture..." << state->source().toLocalFile();
+ qWarning() << "SpriteEngine: Your texture max size today is " << maxSize;
+ }
+ state->m_generatedCount = rowsNeeded;
+ m_imageStateCount += rowsNeeded;
+ }else{
+ m_imageStateCount++;
+ }
+ }
+
+ //maxFrames is max number in a line of the texture
+ if(m_maxFrames * frameWidth > maxSize)
+ m_maxFrames = maxSize/frameWidth;
+ QImage image(frameWidth * m_maxFrames, frameHeight * m_imageStateCount, QImage::Format_ARGB32);
+ image.fill(0);
+ QPainter p(&image);
+ int y = 0;
+ foreach(QSGSprite* state, m_states){
+ QImage img(state->source().toLocalFile());
+ if(img.height() == frameHeight && img.width() < maxSize){//Simple case
+ p.drawImage(0,y,img);
+ y += frameHeight;
+ }else{
+ state->m_framesPerRow = image.width()/frameWidth;
+ int x = 0;
+ int curX = 0;
+ int curY = 0;
+ int framesLeft = state->frames();
+ while(framesLeft > 0){
+ if(image.width() - x + curX <= img.width()){//finish a row in image (dest)
+ int copied = image.width() - x;
+ Q_ASSERT(!(copied % frameWidth));//XXX: Just checking
+ framesLeft -= copied/frameWidth;
+ p.drawImage(x,y,img.copy(curX,curY,copied,frameHeight));
+ y += frameHeight;
+ curX += copied;
+ x = 0;
+ if(curX == img.width()){
+ curX = 0;
+ curY += frameHeight;
+ }
+ }else{//finish a row in img (src)
+ int copied = img.width() - curX;
+ Q_ASSERT(!(copied % frameWidth));//XXX: Just checking
+ framesLeft -= copied/frameWidth;
+ p.drawImage(x,y,img.copy(curX,curY,copied,frameHeight));
+ curY += frameHeight;
+ x += copied;
+ curX = 0;
+ }
+ }
+ if(x)
+ y += frameHeight;
+ }
+ }
+
+ if(image.height() > maxSize){
+ qWarning() << "SpriteEngine: Too many animations to fit in one texture...";
+ qWarning() << "SpriteEngine: Your texture max size today is " << maxSize;
+ return QImage();
+ }
+ return image;
+}
+
+void QSGSpriteEngine::setCount(int c)
+{
+ m_sprites.resize(c);
+ m_goals.resize(c);
+ m_startTimes.resize(c);
+}
+
+void QSGSpriteEngine::startSprite(int index)
+{
+ if(index >= m_sprites.count())
+ return;
+ m_sprites[index] = 0;
+ m_goals[index] = -1;
+ restartSprite(index);
+}
+
+void QSGSpriteEngine::restartSprite(int index)
+{
+ m_startTimes[index] = m_timeOffset + m_advanceTime.elapsed();
+ int time = m_states[m_sprites[index]]->duration() * m_states[m_sprites[index]]->frames() + m_startTimes[index];
+ for(int i=0; i<m_stateUpdates.count(); i++)
+ m_stateUpdates[i].second.removeAll(index);
+ addToUpdateList(time, index);
+}
+
+uint QSGSpriteEngine::updateSprites(uint time)
+{
+ //Sprite State Update;
+ while(!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){
+ foreach(int idx, m_stateUpdates.first().second){
+ if(idx >= m_sprites.count())
+ continue;//TODO: Proper fix(because this does happen and I'm just ignoring it)
+ int stateIdx = m_sprites[idx];
+ int nextIdx = -1;
+ int goalPath = goalSeek(stateIdx, idx);
+ if(goalPath == -1){//Random
+ qreal r =(qreal) qrand() / (qreal) RAND_MAX;
+ qreal total = 0.0;
+ for(QVariantMap::const_iterator iter=m_states[stateIdx]->m_to.constBegin();
+ iter!=m_states[stateIdx]->m_to.constEnd(); iter++)
+ total += (*iter).toReal();
+ r*=total;
+ for(QVariantMap::const_iterator iter= m_states[stateIdx]->m_to.constBegin();
+ iter!=m_states[stateIdx]->m_to.constEnd(); iter++){
+ if(r < (*iter).toReal()){
+ bool superBreak = false;
+ for(int i=0; i<m_states.count(); i++){
+ if(m_states[i]->name() == iter.key()){
+ nextIdx = i;
+ superBreak = true;
+ break;
+ }
+ }
+ if(superBreak)
+ break;
+ }
+ r -= (*iter).toReal();
+ }
+ }else{//Random out of shortest paths to goal
+ nextIdx = goalPath;
+ }
+ if(nextIdx == -1)//No to states means stay here
+ nextIdx = stateIdx;
+
+ m_sprites[idx] = nextIdx;
+ m_startTimes[idx] = time;
+ //TODO: emit something? Remember to emit this when a psuedostate changes too
+ addToUpdateList((m_states[nextIdx]->duration() * m_states[nextIdx]->frames()) + time, idx);
+ }
+ m_stateUpdates.pop_front();
+ }
+
+ m_timeOffset = time;
+ m_advanceTime.start();
+ if(m_stateUpdates.isEmpty())
+ return -1;
+ return m_stateUpdates.first().first;
+}
+
+int QSGSpriteEngine::goalSeek(int curIdx, int spriteIdx, int dist)
+{
+ QString goalName;
+ if(m_goals[spriteIdx] != -1)
+ goalName = m_states[m_goals[spriteIdx]]->name();
+ else
+ goalName = m_globalGoal;
+ if(goalName.isEmpty())
+ return -1;
+ //TODO: caching instead of excessively redoing iterative deepening (which was chosen arbitarily anyways)
+ // Paraphrased - implement in an *efficient* manner
+ for(int i=0; i<m_states.count(); i++)
+ if(m_states[curIdx]->name() == goalName)
+ return curIdx;
+ if(dist < 0)
+ dist = m_states.count();
+ QSGSprite* curState = m_states[curIdx];
+ for(QVariantMap::const_iterator iter = curState->m_to.constBegin();
+ iter!=curState->m_to.constEnd(); iter++){
+ if(iter.key() == goalName)
+ for(int i=0; i<m_states.count(); i++)
+ if(m_states[i]->name() == goalName)
+ return i;
+ }
+ QSet<int> options;
+ for(int i=1; i<dist; i++){
+ for(QVariantMap::const_iterator iter = curState->m_to.constBegin();
+ iter!=curState->m_to.constEnd(); iter++){
+ int option = -1;
+ for(int j=0; j<m_states.count(); j++)//One place that could be a lot more efficient...
+ if(m_states[j]->name() == iter.key())
+ if(goalSeek(j, spriteIdx, i) != -1)
+ option = j;
+ if(option != -1)
+ options << option;
+ }
+ if(!options.isEmpty()){
+ if(options.count()==1)
+ return *(options.begin());
+ int option = -1;
+ qreal r =(qreal) qrand() / (qreal) RAND_MAX;
+ qreal total;
+ for(QSet<int>::const_iterator iter=options.constBegin();
+ iter!=options.constEnd(); iter++)
+ total += curState->m_to.value(m_states[(*iter)]->name()).toReal();
+ r *= total;
+ for(QVariantMap::const_iterator iter = curState->m_to.constBegin();
+ iter!=curState->m_to.constEnd(); iter++){
+ bool superContinue = true;
+ for(int j=0; j<m_states.count(); j++)
+ if(m_states[j]->name() == iter.key())
+ if(options.contains(j))
+ superContinue = false;
+ if(superContinue)
+ continue;
+ if(r < (*iter).toReal()){
+ bool superBreak = false;
+ for(int j=0; j<m_states.count(); j++){
+ if(m_states[j]->name() == iter.key()){
+ option = j;
+ superBreak = true;
+ break;
+ }
+ }
+ if(superBreak)
+ break;
+ }
+ r-=(*iter).toReal();
+ }
+ return option;
+ }
+ }
+ return -1;
+}
+
+void QSGSpriteEngine::addToUpdateList(uint t, int idx)
+{
+ for(int i=0; i<m_stateUpdates.count(); i++){
+ if(m_stateUpdates[i].first==t){
+ m_stateUpdates[i].second << idx;
+ return;
+ }else if(m_stateUpdates[i].first > t){
+ QList<int> tmpList;
+ tmpList << idx;
+ m_stateUpdates.insert(i, qMakePair(t, tmpList));
+ return;
+ }
+ }
+ QList<int> tmpList;
+ tmpList << idx;
+ m_stateUpdates << qMakePair(t, tmpList);
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgspriteengine_p.h b/src/declarative/items/qsgspriteengine_p.h
new file mode 100644
index 0000000000..8ab6e3a30a
--- /dev/null
+++ b/src/declarative/items/qsgspriteengine_p.h
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SPRITEENGINE_H
+#define SPRITEENGINE_H
+
+#include <QObject>
+#include <QVector>
+#include <QTimer>
+#include <QTime>
+#include <QList>
+#include <QDeclarativeListProperty>
+#include <QImage>
+#include <QPair>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGSprite;
+
+class QSGSpriteEngine : public QObject
+{
+ Q_OBJECT
+ //TODO: Optimize single sprite case
+ Q_PROPERTY(QDeclarativeListProperty<QSGSprite> sprites READ sprites)
+ Q_PROPERTY(QString globalGoal READ globalGoal WRITE setGlobalGoal NOTIFY globalGoalChanged)
+public:
+ explicit QSGSpriteEngine(QObject *parent = 0);
+ QSGSpriteEngine(QList<QSGSprite*> sprites, QObject *parent=0);
+ ~QSGSpriteEngine();
+
+ QDeclarativeListProperty<QSGSprite> sprites()
+ {
+ return QDeclarativeListProperty<QSGSprite>(this, m_states);
+ }
+ QString globalGoal() const
+ {
+ return m_globalGoal;
+ }
+
+ int count() const {return m_sprites.count();}
+ void setCount(int c);
+
+ int spriteState(int sprite=0);// {return m_sprites[sprite];}
+ int spriteStart(int sprite=0);// {return m_startTimes[sprite];}
+ int spriteFrames(int sprite=0);
+ int spriteDuration(int sprite=0);
+ int spriteCount();//Like state count, but for the image states
+ int maxFrames();
+
+ void setGoal(int state, int sprite=0, bool jump=false);
+ QImage assembledImage();
+
+ void startSprite(int index=0);
+
+private://Nothing outside should use this?
+ friend class QSGSpriteGoalAffector;//XXX: Fix interface
+ int stateCount() {return m_states.count();}
+ int stateIndex(QSGSprite* s){return m_states.indexOf(s);}//TODO: Does this need to be hidden?
+ QSGSprite* state(int idx){return m_states[idx];}//Used by spritegoal affector
+signals:
+
+ void globalGoalChanged(QString arg);
+
+public slots:
+ void setGlobalGoal(QString arg)
+ {
+ if (m_globalGoal != arg) {
+ m_globalGoal = arg;
+ emit globalGoalChanged(arg);
+ }
+ }
+
+ uint updateSprites(uint time);
+
+private:
+ void restartSprite(int sprite);
+ void addToUpdateList(uint t, int idx);
+ int goalSeek(int curState, int spriteIdx, int dist=-1);
+ QList<QSGSprite*> m_states;
+ QVector<int> m_sprites;//int is the index in m_states of the current state
+ QVector<int> m_goals;
+ QVector<int> m_startTimes;
+ QList<QPair<uint, QList<int> > > m_stateUpdates;//### This could be done faster
+
+ QTime m_advanceTime;
+ uint m_timeOffset;
+ QString m_globalGoal;
+ int m_maxFrames;
+ int m_imageStateCount;
+};
+
+//Common use is to have your own list property which is transparently an engine
+inline void spriteAppend(QDeclarativeListProperty<QSGSprite> *p, QSGSprite* s)
+{
+ reinterpret_cast<QList<QSGSprite *> *>(p->data)->append(s);
+ p->object->metaObject()->invokeMethod(p->object, "createEngine");
+}
+
+inline QSGSprite* spriteAt(QDeclarativeListProperty<QSGSprite> *p, int idx)
+{
+ return reinterpret_cast<QList<QSGSprite *> *>(p->data)->at(idx);
+}
+
+inline void spriteClear(QDeclarativeListProperty<QSGSprite> *p)
+{
+ reinterpret_cast<QList<QSGSprite *> *>(p->data)->clear();
+ p->object->metaObject()->invokeMethod(p->object, "createEngine");
+}
+
+inline int spriteCount(QDeclarativeListProperty<QSGSprite> *p)
+{
+ return reinterpret_cast<QList<QSGSprite *> *>(p->data)->count();
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // SPRITEENGINE_H
diff --git a/src/declarative/items/qsgspriteimage.cpp b/src/declarative/items/qsgspriteimage.cpp
new file mode 100644
index 0000000000..8cc0dc5b76
--- /dev/null
+++ b/src/declarative/items/qsgspriteimage.cpp
@@ -0,0 +1,353 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgspriteimage_p.h"
+#include "qsgsprite_p.h"
+#include "qsgspriteengine_p.h"
+#include <private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <qsgnode.h>
+#include <qsgengine.h>
+#include <qsgtexturematerial.h>
+#include <qsgtexture.h>
+#include <QFile>
+#include <cmath>
+#include <qmath.h>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSpriteMaterial : public QSGMaterial
+{
+public:
+ QSGSpriteMaterial();
+ virtual ~QSGSpriteMaterial();
+ virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
+ virtual QSGMaterialShader *createShader() const;
+ virtual int compare(const QSGMaterial *other) const
+ {
+ return this - static_cast<const QSGSpriteMaterial *>(other);
+ }
+
+ QSGTexture *texture;
+
+ qreal timestamp;
+ qreal timelength;
+ int framecount;
+ int animcount;
+ int width;
+ int height;
+};
+
+QSGSpriteMaterial::QSGSpriteMaterial()
+ : timestamp(0)
+ , timelength(1)
+ , framecount(1)
+ , animcount(1)
+ , width(0)
+ , height(0)
+{
+ setFlag(Blending, true);
+}
+
+QSGSpriteMaterial::~QSGSpriteMaterial()
+{
+ delete texture;
+}
+
+class SpriteMaterialData : public QSGMaterialShader
+{
+public:
+ SpriteMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0)
+ {
+ QFile vf(vertexFile ? vertexFile : ":defaultshaders/spriteimagevertex.shader");
+ vf.open(QFile::ReadOnly);
+ m_vertex_code = vf.readAll();
+
+ QFile ff(fragmentFile ? fragmentFile : ":defaultshaders/spriteimagefragment.shader");
+ ff.open(QFile::ReadOnly);
+ m_fragment_code = ff.readAll();
+
+ Q_ASSERT(!m_vertex_code.isNull());
+ Q_ASSERT(!m_fragment_code.isNull());
+ }
+
+ void deactivate() {
+ QSGMaterialShader::deactivate();
+
+ for (int i=0; i<8; ++i) {
+ program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
+ }
+ }
+
+ virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
+ {
+ QSGSpriteMaterial *m = static_cast<QSGSpriteMaterial *>(newEffect);
+ m->texture->bind();
+
+ program()->setUniformValue(m_opacity_id, state.opacity());
+ program()->setUniformValue(m_timestamp_id, (float) m->timestamp);
+ program()->setUniformValue(m_framecount_id, (float) m->framecount);
+ program()->setUniformValue(m_animcount_id, (float) m->animcount);
+ program()->setUniformValue(m_width_id, (float) m->width);
+ program()->setUniformValue(m_height_id, (float) m->height);
+
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_matrix_id, state.combinedMatrix());
+ }
+
+ virtual void initialize() {
+ m_matrix_id = program()->uniformLocation("matrix");
+ m_opacity_id = program()->uniformLocation("opacity");
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_framecount_id = program()->uniformLocation("framecount");
+ m_animcount_id = program()->uniformLocation("animcount");
+ m_width_id = program()->uniformLocation("width");
+ m_height_id = program()->uniformLocation("height");
+ }
+
+ virtual const char *vertexShader() const { return m_vertex_code.constData(); }
+ virtual const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+ virtual char const *const *attributeNames() const {
+ static const char *attr[] = {
+ "vTex",
+ "vAnimData",
+ 0
+ };
+ return attr;
+ }
+
+ virtual bool isColorTable() const { return false; }
+
+ int m_matrix_id;
+ int m_opacity_id;
+ int m_timestamp_id;
+ int m_framecount_id;
+ int m_animcount_id;
+ int m_width_id;
+ int m_height_id;
+
+ QByteArray m_vertex_code;
+ QByteArray m_fragment_code;
+
+ static float chunkOfBytes[1024];
+};
+float SpriteMaterialData::chunkOfBytes[1024];
+
+QSGMaterialShader *QSGSpriteMaterial::createShader() const
+{
+ return new SpriteMaterialData;
+}
+
+struct SpriteVertex {
+ float tx;
+ float ty;
+ float animIdx;
+ float frameDuration;
+ float frameCount;
+ float animT;
+};
+
+struct SpriteVertices {
+ SpriteVertex v1;
+ SpriteVertex v2;
+ SpriteVertex v3;
+ SpriteVertex v4;
+};
+
+QSGSpriteImage::QSGSpriteImage(QSGItem *parent) :
+ QSGItem(parent)
+ , m_node(0)
+ , m_material(0)
+ , m_spriteEngine(0)
+ , m_pleaseReset(false)
+ , m_running(true)
+{
+ setFlag(ItemHasContents);
+ connect(this, SIGNAL(runningChanged(bool)),
+ this, SLOT(update()));
+}
+
+QDeclarativeListProperty<QSGSprite> QSGSpriteImage::sprites()
+{
+ return QDeclarativeListProperty<QSGSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
+}
+
+void QSGSpriteImage::createEngine()
+{
+ //TODO: delay until component complete
+ if(m_spriteEngine)
+ delete m_spriteEngine;
+ if(m_sprites.count())
+ m_spriteEngine = new QSGSpriteEngine(m_sprites, this);
+ else
+ m_spriteEngine = 0;
+ reset();
+}
+
+static QSGGeometry::Attribute SpriteImage_Attributes[] = {
+ { 0, 2, GL_FLOAT }, // tex
+ { 1, 4, GL_FLOAT } // animData
+};
+
+static QSGGeometry::AttributeSet SpriteImage_AttributeSet =
+{
+ 2, // Attribute Count
+ (4 + 2) * sizeof(float),
+ SpriteImage_Attributes
+};
+
+QSGGeometryNode* QSGSpriteImage::buildNode()
+{
+ if (!m_spriteEngine) {
+ qWarning() << "SpriteImage: No sprite engine...";
+ return 0;
+ }
+
+ if (m_material) {
+ delete m_material;
+ m_material = 0;
+ }
+
+ m_material = new QSGSpriteMaterial();
+
+ QImage image = m_spriteEngine->assembledImage();
+ if(image.isNull())
+ return 0;
+ m_material->texture = sceneGraphEngine()->createTextureFromImage(image);
+ m_material->texture->setFiltering(QSGTexture::Linear);
+ m_material->framecount = m_spriteEngine->maxFrames();
+
+ int vCount = 4;
+ int iCount = 6;
+ QSGGeometry *g = new QSGGeometry(SpriteImage_AttributeSet, vCount, iCount);
+ g->setDrawingMode(GL_TRIANGLES);
+
+ SpriteVertices *p = (SpriteVertices *) g->vertexData();
+ m_spriteEngine->startSprite(0);
+ p->v1.animT = p->v2.animT = p->v3.animT = p->v4.animT = 0;
+ p->v1.animIdx = p->v2.animIdx = p->v3.animIdx = p->v4.animIdx = 0;
+ p->v1.frameCount = p->v2.frameCount = p->v3.frameCount = p->v4.frameCount = m_spriteEngine->spriteFrames();
+ p->v1.frameDuration = p->v2.frameDuration = p->v3.frameDuration = p->v4.frameDuration = m_spriteEngine->spriteDuration();
+
+ p->v1.tx = 0;
+ p->v1.ty = 0;
+
+ p->v2.tx = 1.0;
+ p->v2.ty = 0;
+
+ p->v3.tx = 0;
+ p->v3.ty = 1.0;
+
+ p->v4.tx = 1.0;
+ p->v4.ty = 1.0;
+
+ quint16 *indices = g->indexDataAsUShort();
+ indices[0] = 0;
+ indices[1] = 1;
+ indices[2] = 2;
+ indices[3] = 1;
+ indices[4] = 3;
+ indices[5] = 2;
+
+
+ m_timestamp.start();
+ m_node = new QSGGeometryNode();
+ m_node->setGeometry(g);
+ m_node->setMaterial(m_material);
+ return m_node;
+}
+
+void QSGSpriteImage::reset()
+{
+ m_pleaseReset = true;
+}
+
+QSGNode *QSGSpriteImage::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
+{
+ if(m_pleaseReset){
+ delete m_node;
+ delete m_material;
+
+ m_node = 0;
+ m_material = 0;
+ m_pleaseReset = false;
+ }
+
+ prepareNextFrame();
+
+ if(m_running){
+ update();
+ if (m_node)
+ m_node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ return m_node;
+}
+
+void QSGSpriteImage::prepareNextFrame()
+{
+ if (m_node == 0)
+ m_node = buildNode();
+ if (m_node == 0) //error creating node
+ return;
+
+ uint timeInt = m_timestamp.elapsed();
+ qreal time = timeInt / 1000.;
+ m_material->timestamp = time;
+ m_material->animcount = m_spriteEngine->spriteCount();
+ m_material->height = height();
+ m_material->width = width();
+
+ //Advance State
+ SpriteVertices *p = (SpriteVertices *) m_node->geometry()->vertexData();
+ m_spriteEngine->updateSprites(timeInt);
+ int curIdx = m_spriteEngine->spriteState();
+ if(curIdx != p->v1.animIdx){
+ p->v1.animIdx = p->v2.animIdx = p->v3.animIdx = p->v4.animIdx = curIdx;
+ p->v1.animT = p->v2.animT = p->v3.animT = p->v4.animT = m_spriteEngine->spriteStart()/1000.0;
+ p->v1.frameCount = p->v2.frameCount = p->v3.frameCount = p->v4.frameCount = m_spriteEngine->spriteFrames();
+ p->v1.frameDuration = p->v2.frameDuration = p->v3.frameDuration = p->v4.frameDuration = m_spriteEngine->spriteDuration();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgspriteimage_p.h b/src/declarative/items/qsgspriteimage_p.h
new file mode 100644
index 0000000000..f03fd869f0
--- /dev/null
+++ b/src/declarative/items/qsgspriteimage_p.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SPRITEIMAGE_H
+#define SPRITEIMAGE_H
+
+#include <QSGItem>
+#include <QTime>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGContext;
+class QSGSprite;
+class QSGSpriteEngine;
+class QSGGeometryNode;
+class QSGSpriteMaterial;
+class QSGSpriteImage : public QSGItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged)
+ //###try to share similar spriteEngines for less overhead?
+ Q_PROPERTY(QDeclarativeListProperty<QSGSprite> sprites READ sprites)
+ Q_CLASSINFO("DefaultProperty", "sprites")
+
+public:
+ explicit QSGSpriteImage(QSGItem *parent = 0);
+
+ QDeclarativeListProperty<QSGSprite> sprites();
+
+ bool running() const
+ {
+ return m_running;
+ }
+
+signals:
+
+
+ void runningChanged(bool arg);
+
+public slots:
+
+void setRunning(bool arg)
+{
+ if (m_running != arg) {
+ m_running = arg;
+ emit runningChanged(arg);
+ }
+}
+
+private slots:
+ void createEngine();
+protected:
+ void reset();
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+private:
+ void prepareNextFrame();
+ QSGGeometryNode* buildNode();
+ QSGGeometryNode *m_node;
+ QSGSpriteMaterial *m_material;
+ QList<QSGSprite*> m_sprites;
+ QSGSpriteEngine* m_spriteEngine;
+ QTime m_timestamp;
+ int m_maxFrames;
+ bool m_pleaseReset;
+ bool m_running;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // SPRITEIMAGE_H