From: Alan Alpert Date: Thu, 26 May 2011 05:30:20 +0000 (+1000) Subject: Multi-line sprite files will now work X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=8d2c87d357dea140c607a755c8a5c3fff5697721;p=konrad%2Fqtdeclarative.git Multi-line sprite files will now work --- diff --git a/src/imports/particles/itemparticle.h b/src/imports/particles/itemparticle.h index 40dab74..50414c7 100644 --- a/src/imports/particles/itemparticle.h +++ b/src/imports/particles/itemparticle.h @@ -73,6 +73,7 @@ signals: void fadeChanged(); public slots: + //TODO: Add a follow mode, where moving the delegate causes the logical particle to go with it? void freeze(QSGItem* item); void unfreeze(QSGItem* item); void take(QSGItem* item,bool prioritize=false);//take by modelparticle diff --git a/src/imports/particles/spriteengine.cpp b/src/imports/particles/spriteengine.cpp index b324f7a..7676d9e 100644 --- a/src/imports/particles/spriteengine.cpp +++ b/src/imports/particles/spriteengine.cpp @@ -70,11 +70,64 @@ SpriteEngine::~SpriteEngine() int SpriteEngine::maxFrames() { - int max = 0; - foreach(SpriteState* s, m_states) - if(s->frames() > max) - max = s->frames(); - return max; + 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 SpriteEngine::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 SpriteEngine::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 SpriteEngine::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 SpriteEngine::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 SpriteEngine::spriteCount()//TODO: Actually image state count, need to rename these things to make sense together +{ + return m_imageStateCount; } void SpriteEngine::setGoal(int state, int sprite, bool jump) @@ -99,6 +152,7 @@ QImage SpriteEngine::assembledImage() int frameHeight = 0; int frameWidth = 0; m_maxFrames = 0; + m_imageStateCount = 0; int maxSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize); @@ -113,46 +167,96 @@ QImage SpriteEngine::assembledImage() 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(img.width() / state->frames() != frameWidth){ + if(imgWidth != frameWidth){ qWarning() << "SpriteEngine: Irregular frame width..." << state->source().toLocalFile(); return QImage(); } }else{ - frameWidth = img.width() / state->frames(); - } - if(img.width() > maxSize){ - qWarning() << "SpriteEngine: Animation too wide..." << state->source().toLocalFile(); - return QImage(); + frameWidth = imgWidth; } + int imgHeight = state->frameHeight(); + if(!imgHeight) + imgHeight = img.height(); if(frameHeight){ - if(img.height()!=frameHeight){ + if(imgHeight!=frameHeight){ qWarning() << "SpriteEngine: Irregular frame height..." << state->source().toLocalFile(); return QImage(); } }else{ - frameHeight = img.height(); + frameHeight = imgHeight; } - if(img.height() > maxSize){ - qWarning() << "SpriteEngine: Animation too tall..." << state->source().toLocalFile(); - return QImage(); + 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++; } } - QImage image(frameWidth * m_maxFrames, frameHeight * m_states.count(), QImage::Format_ARGB32); + //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(SpriteState* state, m_states){ QImage img(state->source().toLocalFile()); - p.drawImage(0,y,img); - y += frameHeight; + 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; @@ -224,7 +328,7 @@ uint SpriteEngine::updateSprites(uint time) m_sprites[idx] = nextIdx; m_startTimes[idx] = time; - //TODO: emit something? + //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(); diff --git a/src/imports/particles/spriteengine.h b/src/imports/particles/spriteengine.h index 76a2e29..0180245 100644 --- a/src/imports/particles/spriteengine.h +++ b/src/imports/particles/spriteengine.h @@ -82,11 +82,11 @@ public: 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 stateIndex(SpriteState* s){return m_states.indexOf(s);} - SpriteState* state(int idx){return m_states[idx];} - int stateCount() {return m_states.count();} + 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); @@ -94,6 +94,11 @@ public: void startSprite(int index=0); +private://Nothing outside should use this? + friend class SpriteGoalAffector;//XXX: Fix interface + int stateCount() {return m_states.count();} + int stateIndex(SpriteState* s){return m_states.indexOf(s);}//TODO: Does this need to be hidden? + SpriteState* state(int idx){return m_states[idx];}//Used by spritegoal affector signals: void globalGoalChanged(QString arg); @@ -123,6 +128,7 @@ private: 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 diff --git a/src/imports/particles/spriteimage.cpp b/src/imports/particles/spriteimage.cpp index 0ce3461..ea08ae4 100644 --- a/src/imports/particles/spriteimage.cpp +++ b/src/imports/particles/spriteimage.cpp @@ -263,12 +263,11 @@ QSGGeometryNode* SpriteImage::buildNode() 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; - SpriteState* state = m_spriteEngine->state(0); - p->v1.frameCount = p->v2.frameCount = p->v3.frameCount = p->v4.frameCount = state->frames(); - p->v1.frameDuration = p->v2.frameDuration = p->v3.frameDuration = p->v4.frameDuration = state->duration(); - m_spriteEngine->startSprite(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; @@ -335,7 +334,7 @@ void SpriteImage::prepareNextFrame() uint timeInt = m_timestamp.elapsed(); qreal time = timeInt / 1000.; m_material->timestamp = time; - m_material->animcount = m_spriteEngine->stateCount(); + m_material->animcount = m_spriteEngine->spriteCount(); m_material->height = height(); m_material->width = width(); @@ -346,8 +345,8 @@ void SpriteImage::prepareNextFrame() 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->state(curIdx)->frames(); - p->v1.frameDuration = p->v2.frameDuration = p->v3.frameDuration = p->v4.frameDuration = m_spriteEngine->state(curIdx)->duration(); + 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(); } } diff --git a/src/imports/particles/spriteparticle.cpp b/src/imports/particles/spriteparticle.cpp index 1b62765..6039d28 100644 --- a/src/imports/particles/spriteparticle.cpp +++ b/src/imports/particles/spriteparticle.cpp @@ -360,12 +360,11 @@ void SpriteParticle::load(ParticleData *d) SpriteParticleVertices &p = particles[pos]; // Initial Sprite State + m_spriteEngine->startSprite(pos); p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = p.v1.t; p.v1.animIdx = p.v2.animIdx = p.v3.animIdx = p.v4.animIdx = 0; - SpriteState* state = m_spriteEngine->state(0); - p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = state->frames(); - p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = state->duration(); - m_spriteEngine->startSprite(pos); + p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(pos); + p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(pos); vertexCopy(p.v1, d->pv); vertexCopy(p.v2, d->pv); @@ -424,7 +423,7 @@ void SpriteParticle::prepareNextFrame() qreal time = timeStamp / 1000.; m_material->timestamp = time; - m_material->animcount = m_spriteEngine->stateCount(); + m_material->animcount = m_spriteEngine->spriteCount(); //Advance State SpriteParticleVertices *particles = (SpriteParticleVertices *) m_node->geometry()->vertexData(); @@ -435,8 +434,8 @@ void SpriteParticle::prepareNextFrame() 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(i)/1000.0; - p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->state(curIdx)->frames(); - p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->state(curIdx)->duration(); + p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(i); + p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(i); } } } diff --git a/src/imports/particles/spritestate.cpp b/src/imports/particles/spritestate.cpp index c3cc249..72535c0 100644 --- a/src/imports/particles/spritestate.cpp +++ b/src/imports/particles/spritestate.cpp @@ -45,7 +45,11 @@ QT_BEGIN_NAMESPACE SpriteState::SpriteState(QObject *parent) : QObject(parent) + , m_generatedCount(0) + , m_framesPerRow(0) , m_frames(1) + , m_frameHeight(0) + , m_frameWidth(0) , m_duration(1000) { } diff --git a/src/imports/particles/spritestate.h b/src/imports/particles/spritestate.h index 1dbc747..5157a8b 100644 --- a/src/imports/particles/spritestate.h +++ b/src/imports/particles/spritestate.h @@ -59,6 +59,10 @@ class SpriteState : public QObject 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) @@ -77,6 +81,16 @@ public: return m_frames; } + int frameHeight() const + { + return m_frameHeight; + } + + int frameWidth() const + { + return m_frameWidth; + } + int duration() const { return m_duration; @@ -108,6 +122,10 @@ signals: void framesChanged(int arg); + void frameHeightChanged(int arg); + + void frameWidthChanged(int arg); + void durationChanged(int arg); void nameChanged(QString arg); @@ -136,6 +154,22 @@ public slots: } } + 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) { @@ -179,8 +213,12 @@ public slots: private: friend class SpriteParticle; friend class SpriteEngine; + 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; diff --git a/src/imports/particles/ultraparticle.cpp b/src/imports/particles/ultraparticle.cpp index 5616c7f..fd49523 100644 --- a/src/imports/particles/ultraparticle.cpp +++ b/src/imports/particles/ultraparticle.cpp @@ -858,7 +858,7 @@ void UltraParticle::prepareNextFrame() //Advance State if(m_spriteEngine){//perfLevel == Sprites? - m_material->animcount = m_spriteEngine->stateCount(); + m_material->animcount = m_spriteEngine->spriteCount(); UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData(); m_spriteEngine->updateSprites(timeStamp); for(int i=0; ispriteStart(i)/1000.0; - p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->state(curIdx)->frames(); - p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->state(curIdx)->duration(); + p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(i); + p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(i); } } }else{ @@ -947,10 +947,9 @@ void UltraParticle::load(ParticleData *d) p->v1->animT = p->v2->animT = p->v3->animT = p->v4->animT = p->v1->t; p->v1->animIdx = p->v2->animIdx = p->v3->animIdx = p->v4->animIdx = 0; if(m_spriteEngine){ - SpriteState* state = m_spriteEngine->state(0); - p->v1->frameCount = p->v2->frameCount = p->v3->frameCount = p->v4->frameCount = state->frames(); - p->v1->frameDuration = p->v2->frameDuration = p->v3->frameDuration = p->v4->frameDuration = state->duration(); m_spriteEngine->startSprite(pos); + p->v1->frameCount = p->v2->frameCount = p->v3->frameCount = p->v4->frameCount = m_spriteEngine->spriteFrames(pos); + p->v1->frameDuration = p->v2->frameDuration = p->v3->frameDuration = p->v4->frameDuration = m_spriteEngine->spriteDuration(pos); }else{ p->v1->frameCount = p->v2->frameCount = p->v3->frameCount = p->v4->frameCount = 1; p->v1->frameDuration = p->v2->frameDuration = p->v3->frameDuration = p->v4->frameDuration = 9999;