From 08c975bd77f8fdbbdb5c4694d898a724db900849 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Wed, 5 Oct 2011 11:26:57 +1000 Subject: [PATCH] Sprites can now have varying width and height Varying between Sprites, or between width and height, not within a single Sprite. For ImageParticle only, SpriteImage changes will be in a later commit. Also adds spriteInterpolation boolean. Change-Id: I80681e44f26985a6f6a6b83bd162f6231c7f28c4 Reviewed-on: http://codereview.qt-project.org/6002 Reviewed-by: Qt Sanity Bot Reviewed-by: Martin Jones --- src/declarative/items/qsgsprite.cpp | 1 + src/declarative/items/qsgsprite_p.h | 1 + src/declarative/items/qsgspriteengine.cpp | 73 +++++++++++-------- src/declarative/items/qsgspriteengine_p.h | 4 + .../particles/defaultshaders/imagevertex.shader | 12 ++-- src/declarative/particles/qsgimageparticle.cpp | 70 +++++++++++++------ src/declarative/particles/qsgimageparticle_p.h | 14 ++++- src/declarative/particles/qsgparticleaffector.cpp | 2 +- src/declarative/particles/qsgparticlesystem_p.h | 4 + 9 files changed, 120 insertions(+), 61 deletions(-) diff --git a/src/declarative/items/qsgsprite.cpp b/src/declarative/items/qsgsprite.cpp index 63d1951..68f2e78 100644 --- a/src/declarative/items/qsgsprite.cpp +++ b/src/declarative/items/qsgsprite.cpp @@ -50,6 +50,7 @@ QSGSprite::QSGSprite(QObject *parent) : , m_framesPerRow(0) , m_frameHeight(0) , m_frameWidth(0) + , m_rowY(0) { } diff --git a/src/declarative/items/qsgsprite_p.h b/src/declarative/items/qsgsprite_p.h index ed7c6c4..58b6c13 100644 --- a/src/declarative/items/qsgsprite_p.h +++ b/src/declarative/items/qsgsprite_p.h @@ -127,6 +127,7 @@ private: QUrl m_source; int m_frameHeight; int m_frameWidth; + int m_rowY; }; diff --git a/src/declarative/items/qsgspriteengine.cpp b/src/declarative/items/qsgspriteengine.cpp index f02d229..2ef36df 100644 --- a/src/declarative/items/qsgspriteengine.cpp +++ b/src/declarative/items/qsgspriteengine.cpp @@ -148,6 +148,28 @@ int QSGSpriteEngine::spriteDuration(int sprite) return rowDuration; } +int QSGSpriteEngine::spriteY(int sprite) +{ + int state = m_things[sprite]; + if (!m_sprites[state]->m_generatedCount) + return m_sprites[state]->m_rowY; + int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow; + int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration; + return m_sprites[state]->m_rowY + m_sprites[state]->m_frameHeight * extra; +} + +int QSGSpriteEngine::spriteWidth(int sprite) +{ + int state = m_things[sprite]; + return m_sprites[state]->m_frameWidth; +} + +int QSGSpriteEngine::spriteHeight(int sprite) +{ + int state = m_things[sprite]; + return m_sprites[state]->m_frameHeight; +} + int QSGSpriteEngine::spriteCount()//TODO: Actually image state count, need to rename these things to make sense together { return m_imageStateCount; @@ -175,12 +197,12 @@ void QSGStochasticEngine::setGoal(int state, int sprite, bool jump) QImage QSGSpriteEngine::assembledImage() { - int frameHeight = 0; - int frameWidth = 0; + int h = 0; + int w = 0; m_maxFrames = 0; m_imageStateCount = 0; + int maxSize = 0; - int maxSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize); foreach (QSGStochasticState* s, m_states){ QSGSprite* sprite = qobject_cast(s); @@ -201,60 +223,49 @@ QImage QSGSpriteEngine::assembledImage() } //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; - } + if (!state->m_frameWidth) + state->m_frameWidth = img.width() / state->frames(); 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->m_frameHeight) + state->m_frameHeight = img.height(); - if (state->frames() * frameWidth > maxSize){ + if (state->frames() * state->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){ + int rowsNeeded = helper::divRoundUp(state->frames(), helper::divRoundUp(maxSize, state->frameWidth())); + if (rowsNeeded * state->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; + h += state->frameHeight() * rowsNeeded; + w = qMax(w, helper::divRoundUp(maxSize, state->frameWidth())); m_imageStateCount += rowsNeeded; }else{ + h += state->frameHeight(); + w = qMax(w, state->frameWidth() * state->frames()); 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); + QImage image(w, h, QImage::Format_ARGB32); image.fill(0); QPainter p(&image); int y = 0; foreach (QSGSprite* state, m_sprites){ QImage img(state->source().toLocalFile()); + int frameWidth = state->m_frameWidth; + int frameHeight = state->m_frameHeight; if (img.height() == frameHeight && img.width() < maxSize){//Simple case p.drawImage(0,y,img); + state->m_rowY = y; y += frameHeight; - }else{ + }else{//Chopping up image case state->m_framesPerRow = image.width()/frameWidth; + state->m_rowY = y; int x = 0; int curX = 0; int curY = 0; diff --git a/src/declarative/items/qsgspriteengine_p.h b/src/declarative/items/qsgspriteengine_p.h index 0561849..30a041a 100644 --- a/src/declarative/items/qsgspriteengine_p.h +++ b/src/declarative/items/qsgspriteengine_p.h @@ -277,6 +277,10 @@ public: int spriteStart(int sprite=0); int spriteFrames(int sprite=0); int spriteDuration(int sprite=0); + int spriteX(int sprite=0) { return 0; }//Currently all rows are 0 aligned, if we get more space efficient we might change this + int spriteY(int sprite=0); + int spriteWidth(int sprite=0); + int spriteHeight(int sprite=0); int spriteCount();//Like state count, but for the image states int maxFrames(); QImage assembledImage(); diff --git a/src/declarative/particles/defaultshaders/imagevertex.shader b/src/declarative/particles/defaultshaders/imagevertex.shader index daf49fd..132771a 100644 --- a/src/declarative/particles/defaultshaders/imagevertex.shader +++ b/src/declarative/particles/defaultshaders/imagevertex.shader @@ -11,9 +11,9 @@ attribute highp vec4 vDeformVec; //x,y x unit vector; z,w = y unit vector attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate #endif #ifdef SPRITE -attribute highp vec4 vAnimData;// idx, duration, frameCount (this anim), timestamp (this anim) -uniform highp float framecount; //maximum of all anims -uniform highp float animcount; +attribute highp vec4 vAnimData;// interpolate(bool), duration, frameCount (this anim), timestamp (this anim) +attribute highp vec4 vAnimPos;//sheet x,y, width/height of this anim +uniform highp vec2 animSheetSize; //width/height of whole sheet #endif uniform highp mat4 qt_Matrix; @@ -54,13 +54,13 @@ void main() { tt.y = mod((timestamp - vAnimData.w)*1000., vAnimData.y) / vAnimData.y; frameIndex = floor(frameIndex); - fTexS.xy = vec2(((frameIndex + vTex.x) / framecount), ((vAnimData.x + vTex.y) / animcount)); + fTexS.xy = vec2(((frameIndex + vTex.x) * vAnimPos.z / animSheetSize.x), ((vAnimPos.y + vTex.y * vAnimPos.w) / animSheetSize.y)); //Next frame is also passed, for interpolation //### Should the next anim be precalculated to allow for interpolation there? - if(frameIndex != vAnimData.z - 1.)//Can't do it for the last frame though, this anim may not loop + if(vAnimData.x == 1.0 && frameIndex != vAnimData.z - 1.)//Can't do it for the last frame though, this anim may not loop frameIndex = mod(frameIndex+1., vAnimData.z); - fTexS.zw = vec2(((frameIndex + vTex.x) / framecount), ((vAnimData.x + vTex.y) / animcount)); + fTexS.zw = vec2(((frameIndex + vTex.x) * vAnimPos.z / animSheetSize.x), ((vAnimPos.y + vTex.y * vAnimPos.w) / animSheetSize.y)); #else #ifdef DEFORM fTex = vTex; diff --git a/src/declarative/particles/qsgimageparticle.cpp b/src/declarative/particles/qsgimageparticle.cpp index c6b5138..f0304b4 100644 --- a/src/declarative/particles/qsgimageparticle.cpp +++ b/src/declarative/particles/qsgimageparticle.cpp @@ -85,8 +85,7 @@ class ImageMaterialData qreal timestamp; qreal entry; - qreal framecount; - qreal animcount; + QSizeF animSheetSize; }; //TODO: Move shaders inline once they've stablilized @@ -142,8 +141,6 @@ public: d->texture->bind(); program()->setUniformValue(m_timestamp_id, (float) d->timestamp); - program()->setUniformValue("framecount", (float) 1); - program()->setUniformValue("animcount", (float) 1); program()->setUniformValue(m_entry_id, (float) d->entry); program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, UNIFORM_ARRAY_SIZE, 1); program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1); @@ -243,7 +240,7 @@ public: QList attributes() const { return QList() << "vPos" << "vTex" << "vData" << "vVec" - << "vColor" << "vDeformVec" << "vRotation" << "vAnimData"; + << "vColor" << "vDeformVec" << "vRotation" << "vAnimData" << "vAnimPos"; }; void initialize() { @@ -253,8 +250,7 @@ public: program()->setUniformValue("colortable", 1); glFuncs = QOpenGLContext::currentContext()->functions(); m_timestamp_id = program()->uniformLocation("timestamp"); - m_framecount_id = program()->uniformLocation("framecount"); - m_animcount_id = program()->uniformLocation("animcount"); + m_animsize_id = program()->uniformLocation("animSheetSize"); m_entry_id = program()->uniformLocation("entry"); m_sizetable_id = program()->uniformLocation("sizetable"); m_opacitytable_id = program()->uniformLocation("opacitytable"); @@ -269,16 +265,14 @@ public: d->texture->bind(); program()->setUniformValue(m_timestamp_id, (float) d->timestamp); - program()->setUniformValue(m_framecount_id, (float) d->framecount); - program()->setUniformValue(m_animcount_id, (float) d->animcount); + 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_framecount_id; - int m_animcount_id; + int m_animsize_id; int m_entry_id; int m_sizetable_id; int m_opacitytable_id; @@ -630,6 +624,14 @@ void fillUniformArrayFromImage(float* array, const QImage& img, int size) Default value is Fade. */ +/*! + \qmlproperty bool QtQuick.Particles2::ImageParticle::spritesInterpolate + + If set to true, sprite particles will interpolate between sprite frames each rendered frame, making + the sprites look smoother. + + Default is true. +*/ QSGImageParticle::QSGImageParticle(QSGItem* parent) @@ -650,6 +652,7 @@ QSGImageParticle::QSGImageParticle(QSGItem* parent) , m_xVector(0) , m_yVector(0) , m_spriteEngine(0) + , m_spritesInterpolate(true) , m_explicitColor(false) , m_explicitRotation(false) , m_explicitDeformation(false) @@ -864,6 +867,14 @@ void QSGImageParticle::setYVector(QSGDirection* arg) reset(); } +void QSGImageParticle::setSpritesInterpolate(bool arg) +{ + if (m_spritesInterpolate != arg) { + m_spritesInterpolate = arg; + emit spritesInterpolateChanged(arg); + } +} + void QSGImageParticle::setBloat(bool arg) { if (m_bloat != arg) { @@ -1000,13 +1011,14 @@ static QSGGeometry::Attribute SpriteParticle_Attributes[] = { QSGGeometry::Attribute::create(4, 4, GL_UNSIGNED_BYTE), // Colors QSGGeometry::Attribute::create(5, 4, GL_FLOAT), // DeformationVectors QSGGeometry::Attribute::create(6, 3, GL_FLOAT), // Rotation - QSGGeometry::Attribute::create(7, 4, GL_FLOAT) // Anim Data + QSGGeometry::Attribute::create(7, 4, GL_FLOAT), // Anim Data + QSGGeometry::Attribute::create(8, 4, GL_FLOAT) // Anim Pos }; static QSGGeometry::AttributeSet SpriteParticle_AttributeSet = { - 8, // Attribute Count - (2 + 2 + 4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar), + 9, // Attribute Count + (2 + 2 + 4 + 4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar), SpriteParticle_Attributes }; @@ -1114,7 +1126,7 @@ QSGGeometryNode* QSGImageParticle::buildParticleNodes() switch (perfLevel) {//Fallthrough intended case Sprites: m_material = SpriteMaterial::createMaterial(); - getState(m_material)->framecount = m_spriteEngine->maxFrames(); + getState(m_material)->animSheetSize = QSizeF(image.size()); m_spriteEngine->setCount(m_count); case Tabled: if (!m_material) @@ -1270,7 +1282,6 @@ void QSGImageParticle::prepareNextFrame() switch (perfLevel){//Fall-through intended case Sprites: //Advance State - getState(m_material)->animcount = m_spriteEngine->spriteCount(); m_spriteEngine->updateSprites(timeStamp); foreach (const QString &str, m_groups){ int gIdx = m_system->groupIds[str]; @@ -1280,12 +1291,15 @@ void QSGImageParticle::prepareNextFrame() for (int i=0; i < count; i++){ int spriteIdx = m_idxStarts[gIdx] + i; Vertices &p = particles[i]; - int curIdx = m_spriteEngine->spriteState(spriteIdx); - if (curIdx != p.v1.animIdx){ - p.v1.animIdx = p.v2.animIdx = p.v3.animIdx = p.v4.animIdx = curIdx; + int curY = m_spriteEngine->spriteY(spriteIdx);//Y is fixed per sprite row, used to distinguish rows here + if (curY != p.v1.animY){ p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0; p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(spriteIdx); p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(spriteIdx); + p.v1.animX = p.v2.animX = p.v3.animX = p.v4.animX = m_spriteEngine->spriteX(spriteIdx); + p.v1.animY = p.v2.animY = p.v3.animY = p.v4.animY = m_spriteEngine->spriteY(spriteIdx); + p.v1.animWidth = p.v2.animWidth = p.v3.animWidth = p.v4.animWidth = m_spriteEngine->spriteWidth(spriteIdx); + p.v1.animHeight = p.v2.animHeight = p.v3.animHeight = p.v4.animHeight = m_spriteEngine->spriteHeight(spriteIdx); } } } @@ -1327,14 +1341,19 @@ void QSGImageParticle::initialize(int gIdx, int pIdx) datum->animationOwner = this; QSGParticleData* writeTo = (datum->animationOwner == this ? datum : getShadowDatum(datum)); writeTo->animT = writeTo->t; - writeTo->animIdx = 0; + //writeTo->animInterpolate = m_spritesInterpolate; if (m_spriteEngine){ m_spriteEngine->start(spriteIdx); writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx); writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx); + writeTo->animX = m_spriteEngine->spriteX(spriteIdx); + writeTo->animY = m_spriteEngine->spriteY(spriteIdx); + writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx); + writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx); }else{ writeTo->frameCount = 1; writeTo->frameDuration = 9999; + writeTo->animX = writeTo->animY = writeTo->animWidth = writeTo->animHeight = 0; } } case Tabled: @@ -1452,17 +1471,24 @@ void QSGImageParticle::commit(int gIdx, int pIdx) spriteVertices[i].rotationSpeed = datum->rotationSpeed; spriteVertices[i].autoRotate = datum->autoRotate; } + spriteVertices[i].animInterpolate = m_spritesInterpolate ? 1.0 : 0.0;//### Shadow? In particleData? Or uniform? if (m_explicitAnimation && datum->animationOwner != this) { QSGParticleData* shadow = getShadowDatum(datum); - spriteVertices[i].animIdx = shadow->animIdx; 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].animIdx = datum->animIdx; 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; } if (m_explicitColor && datum->colorOwner != this) { QSGParticleData* shadow = getShadowDatum(datum); diff --git a/src/declarative/particles/qsgimageparticle_p.h b/src/declarative/particles/qsgimageparticle_p.h index 274ff42..89796da 100644 --- a/src/declarative/particles/qsgimageparticle_p.h +++ b/src/declarative/particles/qsgimageparticle_p.h @@ -130,10 +130,14 @@ struct SpriteVertex { float rotation; float rotationSpeed; float autoRotate;//Assumed that GPUs prefer floats to bools - float animIdx; + float animInterpolate; float frameDuration; float frameCount; float animT; + float animX; + float animY; + float animWidth; + float animHeight; }; template @@ -177,6 +181,7 @@ class QSGImageParticle : public QSGParticlePainter //yVector is the same, but top-left to bottom-left. The particle is always a parallelogram. Q_PROPERTY(QSGDirection* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged RESET resetDeformation) Q_PROPERTY(QDeclarativeListProperty sprites READ sprites) + Q_PROPERTY(bool spritesInterpolate READ spritesInterpolate WRITE setSpritesInterpolate NOTIFY spritesInterpolateChanged) Q_PROPERTY(EntryEffect entryEffect READ entryEffect WRITE setEntryEffect NOTIFY entryEffectChanged) Q_PROPERTY(bool bloat READ bloat WRITE setBloat NOTIFY bloatChanged)//Just a debugging property to bypass optimizations @@ -248,6 +253,8 @@ public: QSGDirection* yVector() const { return m_yVector; } + bool spritesInterpolate() const { return m_spritesInterpolate; } + bool bloat() const { return m_bloat; } EntryEffect entryEffect() const { return m_entryEffect; } @@ -291,6 +298,8 @@ signals: void yVectorChanged(QSGDirection* arg); + void spritesInterpolateChanged(bool arg); + void bloatChanged(bool arg); void entryEffectChanged(EntryEffect arg); @@ -321,6 +330,8 @@ public slots: void setYVector(QSGDirection* arg); + void setSpritesInterpolate(bool arg); + void setBloat(bool arg); void setEntryEffect(EntryEffect arg); @@ -372,6 +383,7 @@ private: QList m_sprites; QSGSpriteEngine* m_spriteEngine; + bool m_spritesInterpolate; bool m_explicitColor; bool m_explicitRotation; diff --git a/src/declarative/particles/qsgparticleaffector.cpp b/src/declarative/particles/qsgparticleaffector.cpp index 62f5ccd..24bddea 100644 --- a/src/declarative/particles/qsgparticleaffector.cpp +++ b/src/declarative/particles/qsgparticleaffector.cpp @@ -208,7 +208,7 @@ bool QSGParticleAffector::affectParticle(QSGParticleData *, qreal ) void QSGParticleAffector::reset(QSGParticleData* pd) {//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass if (m_onceOff) - if (activeGroup(d->group)) + if (activeGroup(pd->group)) m_onceOffed.remove(qMakePair(pd->group, pd->index)); } diff --git a/src/declarative/particles/qsgparticlesystem_p.h b/src/declarative/particles/qsgparticlesystem_p.h index 5dd93d2..e88aa49 100644 --- a/src/declarative/particles/qsgparticlesystem_p.h +++ b/src/declarative/particles/qsgparticlesystem_p.h @@ -203,6 +203,10 @@ public: float frameDuration; float frameCount; float animT; + float animX; + float animY; + float animWidth; + float animHeight; float r; QSGItem* delegate; int modelIndex; -- 1.7.2.5