aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Alpert <alan.alpert@nokia.com>2012-01-13 13:54:29 +1000
committerQt by Nokia <qt-info@nokia.com>2012-01-23 07:38:34 +0100
commit379e388568f5d5408b4be13ce6a1faba0b74be6b (patch)
tree55d6684817c566dad5939ff5e8ad652215fef7a2
parent69920f4ddeaa5dbdee555e0a607fd21eb42e2bbc (diff)
Per-frame Sprites patch one
To allow for sprites to be advanced by the rendering framerate, two minor redesigns were needed. A) Sprite texture location is now calculated on the CPU and passed to the GPU per frame. B) Stochastic State engine now supports states that do not advance on a timer, and states can be advanced manually. This patch implements B and A for ImageParticle. A for SpriteImage will be done in a separate patch. Task-number: QTBUG-22236 Change-Id: If1c54a6a03fa48b95bb1e672283292859656457b Reviewed-by: Alan Alpert <alan.alpert@nokia.com>
-rw-r--r--src/quick/items/qquicksprite.cpp16
-rw-r--r--src/quick/items/qquickspriteengine.cpp106
-rw-r--r--src/quick/items/qquickspriteengine_p.h6
-rw-r--r--src/quick/particles/qquickimageparticle.cpp140
-rw-r--r--src/quick/particles/qquickimageparticle_p.h16
-rw-r--r--src/quick/particles/qquickparticlesystem.cpp1
-rw-r--r--src/quick/particles/qquickparticlesystem_p.h1
-rw-r--r--src/quick/particles/qquickv8particledata.cpp2
8 files changed, 163 insertions, 125 deletions
diff --git a/src/quick/items/qquicksprite.cpp b/src/quick/items/qquicksprite.cpp
index 61e443db84..4c7dafafbb 100644
--- a/src/quick/items/qquicksprite.cpp
+++ b/src/quick/items/qquicksprite.cpp
@@ -53,12 +53,13 @@ QT_BEGIN_NAMESPACE
/*!
\qmlproperty int QtQuick2::Sprite::duration
- Time between frames.
+ Time between frames. Use -1 to indicate one sprite frame per rendered frame.
*/
/*!
\qmlproperty int QtQuick2::Sprite::durationVariation
- The time between frames can vary by up to this amount.
+ The time between frames can vary by up to this amount. Variation will never decrease the time
+ between frames to less than 0.
Default is 0.
*/
@@ -105,17 +106,6 @@ QT_BEGIN_NAMESPACE
If frameHeight and frameWidth are not specified, it is assumed to be a single long row of square frames.
Otherwise, it can be multiple contiguous rows or rectangluar frames, when one row runs out the next will be used.
*/
- Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
- Q_PROPERTY(int durationVariation READ durationVariance WRITE setDurationVariance NOTIFY durationVarianceChanged)
- Q_PROPERTY(QVariantMap to READ to WRITE setTo NOTIFY toChanged)
- Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
- Q_PROPERTY(qreal speedModifiesDuration READ speedModifer WRITE setSpeedModifier NOTIFY speedModifierChanged)
- Q_PROPERTY(int frames READ frames WRITE setFrames NOTIFY framesChanged)
- Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
- //If frame height or width is not specified, it is assumed to be a single long row of square 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)
QQuickSprite::QQuickSprite(QObject *parent) :
QQuickStochasticState(parent)
diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp
index 5a2d831f92..b48fc71767 100644
--- a/src/quick/items/qquickspriteengine.cpp
+++ b/src/quick/items/qquickspriteengine.cpp
@@ -339,67 +339,73 @@ void QQuickStochasticEngine::restart(int index)
int time = m_duration[index] * m_states[m_things[index]]->frames() + m_startTimes[index];
for (int i=0; i<m_stateUpdates.count(); i++)
m_stateUpdates[i].second.removeAll(index);
- addToUpdateList(time, index);
+ if (m_duration[index] > 0)
+ addToUpdateList(time, index);
}
-uint QQuickStochasticEngine::updateSprites(uint time)//### would returning a list of changed idxs be faster than signals?
+// Doesn't remove from update list. If you want to cancel pending timed updates, call restart() first.
+// Usually sprites are either manually advanced or in the list, and mixing is not recommended anyways.
+void QQuickStochasticEngine::advance(int idx)
{
- //Sprite State Update;
- QSet<int> changedIndexes;
- while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){
- foreach (int idx, m_stateUpdates.first().second){
- if (idx >= m_things.count())
- continue;//TODO: Proper fix(because this does happen and I'm just ignoring it)
- int stateIdx = m_things[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;
+ if (idx >= m_things.count())
+ return;//TODO: Proper fix(because this has happened and I just ignored it)
+ int stateIdx = m_things[idx];
+ int nextIdx = nextState(stateIdx,idx);
+ m_things[idx] = nextIdx;
+ m_duration[idx] = m_states[nextIdx]->variedDuration();
+ m_startTimes[idx] = m_timeOffset;//This will be the last time updateSprites was called
+ emit m_states[nextIdx]->entered();
+ emit stateChanged(idx); //TODO: emit this when a psuedostate changes too(but manually in SpriteEngine)
+ if (m_duration[idx] >= 0)
+ addToUpdateList((m_duration[idx] * m_states[nextIdx]->frames()) + m_startTimes[idx], idx);
+}
+
+int QQuickStochasticEngine::nextState(int curState, int curThing)
+{
+ int nextIdx = -1;
+ int goalPath = goalSeek(curState, curThing);
+ if (goalPath == -1){//Random
+ qreal r =(qreal) qrand() / (qreal) RAND_MAX;
+ qreal total = 0.0;
+ for (QVariantMap::const_iterator iter=m_states[curState]->m_to.constBegin();
+ iter!=m_states[curState]->m_to.constEnd(); iter++)
+ total += (*iter).toReal();
+ r*=total;
+ for (QVariantMap::const_iterator iter= m_states[curState]->m_to.constBegin();
+ iter!=m_states[curState]->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;
}
- 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_things[idx] = nextIdx;
- m_duration[idx] = m_states[nextIdx]->variedDuration();
- m_startTimes[idx] = time;
- if (nextIdx != stateIdx){
- changedIndexes << idx;
- emit m_states[nextIdx]->entered();
+ if (superBreak)
+ break;
}
- addToUpdateList((m_duration[idx] * m_states[nextIdx]->frames()) + time, idx);
+ r -= (*iter).toReal();
}
- m_stateUpdates.pop_front();
+ }else{//Random out of shortest paths to goal
+ nextIdx = goalPath;
}
+ if (nextIdx == -1)//No 'to' states means stay here
+ nextIdx = curState;
+ return nextIdx;
+}
+uint QQuickStochasticEngine::updateSprites(uint time)//### would returning a list of changed idxs be faster than signals?
+{
+ //Sprite State Update;
m_timeOffset = time;
- m_advanceTime.start();
- //TODO: emit this when a psuedostate changes too
- foreach (int idx, changedIndexes){//Batched so that update list doesn't change midway
- emit stateChanged(idx);
+ while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){
+ foreach (int idx, m_stateUpdates.first().second)
+ advance(idx);
+ m_stateUpdates.pop_front();
}
+
+ m_advanceTime.start();
if (m_stateUpdates.isEmpty())
return -1;
return m_stateUpdates.first().first;
diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h
index 050e0c284e..926bb68435 100644
--- a/src/quick/items/qquickspriteengine_p.h
+++ b/src/quick/items/qquickspriteengine_p.h
@@ -102,9 +102,9 @@ public:
int variedDuration() const
{
- return m_duration
+ return qMax(qreal(0.0) , m_duration
+ (m_durationVariance * ((qreal)qrand()/RAND_MAX) * 2)
- - m_durationVariance;
+ - m_durationVariance);
}
int frames() const
@@ -212,6 +212,7 @@ public:
void setGoal(int state, int sprite=0, bool jump=false);
void start(int index=0, int state=0);
+ void advance(int index=0);//Sends state to the next chosen state, unlike goal.
void stop(int index=0);
int curState(int index=0) {return m_things[index];}
@@ -246,6 +247,7 @@ protected:
friend class QQuickParticleSystem;
void restart(int index);
void addToUpdateList(uint t, int idx);
+ int nextState(int curState, int idx=0);
int goalSeek(int curState, int idx, int dist=-1);
QList<QQuickStochasticState*> m_states;
//### Consider struct or class for the four data variables?
diff --git a/src/quick/particles/qquickimageparticle.cpp b/src/quick/particles/qquickimageparticle.cpp
index 9aec234121..e8aa0367b4 100644
--- a/src/quick/particles/qquickimageparticle.cpp
+++ b/src/quick/particles/qquickimageparticle.cpp
@@ -53,6 +53,7 @@
#include <QtQuick/qsgengine.h>
#include <QtQuick/private/qsgtexture_p.h>
#include <private/qdeclarativeglobal_p.h>
+#include <cmath>
QT_BEGIN_NAMESPACE
@@ -82,9 +83,8 @@ static const char vertexShaderCode[] =
"attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate\n"
"#endif\n"
"#if defined(SPRITE)\n"
- "attribute highp vec4 vAnimData;// interpolate(bool), duration, frameCount (this anim), timestamp (this anim)\n"
- "attribute highp vec4 vAnimPos;//sheet x,y, width/height of this anim\n"
- "uniform highp vec2 animSheetSize; //width/height of whole sheet\n"
+ "attribute highp vec3 vAnimData;// w,h(premultiplied of anim), interpolation progress\n"
+ "attribute highp vec4 vAnimPos;//x,y, x,y (two frames for interpolation)\n"
"#endif\n"
"\n"
"uniform highp mat4 qt_Matrix;\n"
@@ -117,18 +117,12 @@ static const char vertexShaderCode[] =
"#endif\n"
" } else {\n"
"#if defined(SPRITE)\n"
+ " tt.y = vAnimData.z;\n"
" //Calculate frame location in texture\n"
- " highp float frameIndex = mod((((timestamp - vAnimData.w)*1000.)/vAnimData.y),vAnimData.z);\n"
- " tt.y = mod((timestamp - vAnimData.w)*1000., vAnimData.y) / vAnimData.y;\n"
- "\n"
- " frameIndex = floor(frameIndex);\n"
- " fTexS.xy = vec2(((frameIndex + vPosTex.z) * vAnimPos.z / animSheetSize.x), ((vAnimPos.y + vPosTex.w * vAnimPos.w) / animSheetSize.y));\n"
- "\n"
+ " fTexS.xy = vAnimPos.xy + vPosTex.zw * vAnimData.xy;\n"
" //Next frame is also passed, for interpolation\n"
- " //### Should the next anim be precalculated to allow for interpolation there?\n"
- " if (vAnimData.x == 1.0 && frameIndex != vAnimData.z - 1.)//Can't do it for the last frame though, this anim may not loop\n"
- " frameIndex = mod(frameIndex+1., vAnimData.z);\n"
- " fTexS.zw = vec2(((frameIndex + vPosTex.z) * vAnimPos.z / animSheetSize.x), ((vAnimPos.y + vPosTex.w * vAnimPos.w) / animSheetSize.y));\n"
+ " fTexS.zw = vAnimPos.zw + vPosTex.zw * vAnimData.xy;\n"
+ "\n"
"#elif defined(DEFORM)\n"
" fTex = vPosTex.zw;\n"
"#endif\n"
@@ -413,8 +407,8 @@ public:
program()->setUniformValue("texture", 0);
program()->setUniformValue("colortable", 1);
glFuncs = QOpenGLContext::currentContext()->functions();
+ //Don't actually expose the animSheetSize in the shader, it's currently only used for CPU calculations.
m_timestamp_id = program()->uniformLocation("timestamp");
- m_animsize_id = program()->uniformLocation("animSheetSize");
m_entry_id = program()->uniformLocation("entry");
m_sizetable_id = program()->uniformLocation("sizetable");
m_opacitytable_id = program()->uniformLocation("opacitytable");
@@ -429,14 +423,12 @@ public:
d->texture->bind();
program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
- program()->setUniformValue(m_animsize_id, d->animSheetSize);
program()->setUniformValue(m_entry_id, (float) d->entry);
program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, 64, 1);
program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
}
int m_timestamp_id;
- int m_animsize_id;
int m_entry_id;
int m_sizetable_id;
int m_opacitytable_id;
@@ -1171,14 +1163,14 @@ static QSGGeometry::Attribute SpriteParticle_Attributes[] = {
QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors
QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation
- QSGGeometry::Attribute::create(6, 4, GL_FLOAT), // Anim Data
+ QSGGeometry::Attribute::create(6, 3, GL_FLOAT), // Anim Data
QSGGeometry::Attribute::create(7, 4, GL_FLOAT) // Anim Pos
};
static QSGGeometry::AttributeSet SpriteParticle_AttributeSet =
{
8, // Attribute Count
- (4 + 4 + 4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
+ (4 + 4 + 4 + 4 + 3 + 3 + 4) * sizeof(float) + 4 * sizeof(uchar),
SpriteParticle_Attributes
};
@@ -1386,9 +1378,11 @@ QSGGeometryNode* QQuickImageParticle::buildParticleNodes()
indices += 6;
}
}
-
}
+ if (perfLevel == Sprites)
+ spritesUpdate();//Gives all vertexes the initial sprite data, then maintained per frame
+
foreach (QSGGeometryNode* node, m_nodes){
if (node == *(m_nodes.begin()))
node->setFlag(QSGGeometryNode::OwnsMaterial);//Root node owns the material for memory management purposes
@@ -1454,7 +1448,8 @@ void QQuickImageParticle::prepareNextFrame()
case Sprites:
//Advance State
if (m_spriteEngine)
- m_spriteEngine->updateSprites(timeStamp);
+ m_spriteEngine->updateSprites(timeStamp);//fires signals if anim changed
+ spritesUpdate(time);
case Tabled:
case Deformable:
case Colored:
@@ -1467,6 +1462,65 @@ void QQuickImageParticle::prepareNextFrame()
node->markDirty(QSGNode::DirtyMaterial);
}
+void QQuickImageParticle::spritesUpdate(qreal time)
+{
+ // Sprite progression handled CPU side, so as to have per-frame control.
+ foreach (const QString &str, m_groups) {
+ int gIdx = m_system->groupIds[str];
+ foreach (QQuickParticleData* mainDatum, m_system->groupData[gIdx]->data) {
+ QSGGeometryNode *node = m_nodes[gIdx];
+ if (!node)
+ continue;
+ //TODO: Interpolate between two different animations if it's going to transition next frame
+ // This is particularly important for cut-up sprites.
+ QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
+ double frameAt;
+ qreal progress;
+ if (datum->frameDuration > 0) {
+ qreal frame = (time - datum->animT)/(datum->frameDuration / 1000.0);
+ frame = qBound(0.0, frame, (qreal)datum->frameCount - 1.0);//Stop at count-1 frames until we have between anim interpolation
+ progress = modf(frame,&frameAt);
+ } else {
+ datum->frameAt++;
+ if (datum->frameAt >= datum->frameCount){
+ datum->frameAt = 0;
+ for (int i = 0; i<m_startsIdx.count(); i++) {
+ if (m_startsIdx[i].second == gIdx){
+ m_spriteEngine->advance(m_startsIdx[i].first + datum->index);
+ break;
+ }
+ }
+ }
+ frameAt = datum->frameAt;
+ progress = 0;
+ }
+ QSizeF sheetSize = getState<ImageMaterialData>(m_material)->animSheetSize;
+ qreal y = datum->animY / sheetSize.height();
+ qreal w = datum->animWidth / sheetSize.width();
+ qreal h = datum->animHeight / sheetSize.height();
+ qreal x1 = datum->animX / sheetSize.width();
+ x1 += frameAt * w;
+ qreal x2 = x1;
+ if (frameAt < (datum->frameCount-1))
+ x2 += w;
+
+ node->setFlag(QSGNode::OwnsGeometry, false);
+ SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
+ spriteVertices += datum->index*4;
+ for (int i=0; i<4; i++) {
+ spriteVertices[i].animX1 = x1;
+ spriteVertices[i].animY1 = y;
+ spriteVertices[i].animX2 = x2;
+ spriteVertices[i].animY2 = y;
+ spriteVertices[i].animW = w;
+ spriteVertices[i].animH = h;
+ spriteVertices[i].animProgress = progress;
+ }
+ node->setFlag(QSGNode::OwnsGeometry, true);
+ }
+ }
+}
+
void QQuickImageParticle::spriteAdvance(int spriteIdx)
{
if (!m_startsIdx.count())//Probably overly defensive
@@ -1484,19 +1538,17 @@ void QQuickImageParticle::spriteAdvance(int spriteIdx)
gIdx = m_startsIdx[i-1].second;
int pIdx = spriteIdx - m_startsIdx[i-1].first;
- QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
- QQuickParticleData* d = (datum->animationOwner == this ? datum : getShadowDatum(datum));
-
- d->animIdx = m_spriteEngine->spriteState(spriteIdx);
- Vertices<SpriteVertex>* particles = (Vertices<SpriteVertex> *) m_nodes[gIdx]->geometry()->vertexData();
- Vertices<SpriteVertex> &p = particles[pIdx];
- d->animT = p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
- d->frameCount = p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(spriteIdx);
- d->frameDuration = p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
- d->animX = p.v1.animX = p.v2.animX = p.v3.animX = p.v4.animX = m_spriteEngine->spriteX(spriteIdx);
- d->animY = p.v1.animY = p.v2.animY = p.v3.animY = p.v4.animY = m_spriteEngine->spriteY(spriteIdx);
- d->animWidth = p.v1.animWidth = p.v2.animWidth = p.v3.animWidth = p.v4.animWidth = m_spriteEngine->spriteWidth(spriteIdx);
- d->animHeight = p.v1.animHeight = p.v2.animHeight = p.v3.animHeight = p.v4.animHeight = m_spriteEngine->spriteHeight(spriteIdx);
+ QQuickParticleData* mainDatum = m_system->groupData[gIdx]->data[pIdx];
+ QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
+
+ datum->animIdx = m_spriteEngine->spriteState(spriteIdx);
+ datum->animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
+ datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
+ datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
+ datum->animX = m_spriteEngine->spriteX(spriteIdx);
+ datum->animY = m_spriteEngine->spriteY(spriteIdx);
+ datum->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
+ datum->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
}
void QQuickImageParticle::reloadColor(const Color4ub &c, QQuickParticleData* d)
@@ -1536,6 +1588,7 @@ void QQuickImageParticle::initialize(int gIdx, int pIdx)
writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
writeTo->animIdx = 0;//Always starts at 0
+ writeTo->frameAt = -1;
writeTo->animX = m_spriteEngine->spriteX(spriteIdx);
writeTo->animY = m_spriteEngine->spriteY(spriteIdx);
writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
@@ -1546,6 +1599,7 @@ void QQuickImageParticle::initialize(int gIdx, int pIdx)
writeTo->animT = datum->t;
writeTo->frameCount = 1;
writeTo->frameDuration = 60000000.0;
+ writeTo->frameAt = -1;
writeTo->animIdx = 0;
writeTo->animT = 0;
writeTo->animX = writeTo->animY = 0;
@@ -1667,25 +1721,7 @@ void QQuickImageParticle::commit(int gIdx, int pIdx)
spriteVertices[i].rotationSpeed = datum->rotationSpeed;
spriteVertices[i].autoRotate = datum->autoRotate;
}
- spriteVertices[i].animInterpolate = m_spriteEngine ? (m_spritesInterpolate ? 1.0 : 0.0) : 0.0;//### Shadow? In particleData? Or uniform?
- if (!m_spriteEngine || (m_explicitAnimation && datum->animationOwner != this)) {
- QQuickParticleData* shadow = getShadowDatum(datum);
- spriteVertices[i].frameDuration = shadow->frameDuration;
- spriteVertices[i].frameCount = shadow->frameCount;
- spriteVertices[i].animT = shadow->animT;
- spriteVertices[i].animX = shadow->animX;
- spriteVertices[i].animY = shadow->animY;
- spriteVertices[i].animWidth = shadow->animWidth;
- spriteVertices[i].animHeight = shadow->animHeight;
- } else {
- spriteVertices[i].frameDuration = datum->frameDuration;
- spriteVertices[i].frameCount = datum->frameCount;
- spriteVertices[i].animT = datum->animT;
- spriteVertices[i].animX = datum->animX;
- spriteVertices[i].animY = datum->animY;
- spriteVertices[i].animWidth = datum->animWidth;
- spriteVertices[i].animHeight = datum->animHeight;
- }
+ //Sprite-related vertices updated per-frame in spritesUpdate(), not on demand
if (m_explicitColor && datum->colorOwner != this) {
QQuickParticleData* shadow = getShadowDatum(datum);
spriteVertices[i].color.r = shadow->color.r;
diff --git a/src/quick/particles/qquickimageparticle_p.h b/src/quick/particles/qquickimageparticle_p.h
index a7cf00dafe..3e50a5fefe 100644
--- a/src/quick/particles/qquickimageparticle_p.h
+++ b/src/quick/particles/qquickimageparticle_p.h
@@ -128,14 +128,13 @@ struct SpriteVertex {
float rotation;
float rotationSpeed;
float autoRotate;//Assumed that GPUs prefer floats to bools
- float animInterpolate;
- float frameDuration;
- float frameCount;
- float animT;
- float animX;
- float animY;
- float animWidth;
- float animHeight;
+ float animW;
+ float animH;
+ float animProgress;
+ float animX1;
+ float animY1;
+ float animX2;
+ float animY2;
};
template <typename Vertex>
@@ -343,6 +342,7 @@ private slots:
void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty
void spriteAdvance(int spriteIndex);
+ void spritesUpdate(qreal time = 0 );
private:
QUrl m_image_name;
QUrl m_colortable_name;
diff --git a/src/quick/particles/qquickparticlesystem.cpp b/src/quick/particles/qquickparticlesystem.cpp
index a701039385..47aa4bef41 100644
--- a/src/quick/particles/qquickparticlesystem.cpp
+++ b/src/quick/particles/qquickparticlesystem.cpp
@@ -420,6 +420,7 @@ QQuickParticleData::QQuickParticleData(QQuickParticleSystem* sys)
autoRotate = 0;
animIdx = 0;
frameDuration = 1;
+ frameAt = -1;
frameCount = 1;
animT = -1;
animX = 0;
diff --git a/src/quick/particles/qquickparticlesystem_p.h b/src/quick/particles/qquickparticlesystem_p.h
index 343d48b654..277dda1771 100644
--- a/src/quick/particles/qquickparticlesystem_p.h
+++ b/src/quick/particles/qquickparticlesystem_p.h
@@ -200,6 +200,7 @@ public:
float autoRotate;//Assume that GPUs prefer floats to bools
float animIdx;
float frameDuration;
+ float frameAt;//Used for duration -1
float frameCount;
float animT;
float animX;
diff --git a/src/quick/particles/qquickv8particledata.cpp b/src/quick/particles/qquickv8particledata.cpp
index fab9b8a095..caf32b6fbc 100644
--- a/src/quick/particles/qquickv8particledata.cpp
+++ b/src/quick/particles/qquickv8particledata.cpp
@@ -411,6 +411,7 @@ FLOAT_GETTER_AND_SETTER(rotation)
FLOAT_GETTER_AND_SETTER(rotationSpeed)
FLOAT_GETTER_AND_SETTER(animIdx)
FLOAT_GETTER_AND_SETTER(frameDuration)
+FLOAT_GETTER_AND_SETTER(frameAt)
FLOAT_GETTER_AND_SETTER(frameCount)
FLOAT_GETTER_AND_SETTER(animT)
FLOAT_GETTER_AND_SETTER(r)
@@ -450,6 +451,7 @@ QV8ParticleDataDeletable::QV8ParticleDataDeletable(QV8Engine *engine)
REGISTER_ACCESSOR(ft, engine, autoRotate, autoRotate);
REGISTER_ACCESSOR(ft, engine, animIdx, animationIndex);
REGISTER_ACCESSOR(ft, engine, frameDuration, frameDuration);
+ REGISTER_ACCESSOR(ft, engine, frameAt, frameAt);
REGISTER_ACCESSOR(ft, engine, frameCount, frameCount);
REGISTER_ACCESSOR(ft, engine, animT, animationT);
REGISTER_ACCESSOR(ft, engine, r, r);