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;
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<QSGSprite*>(s);
}
//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;
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;
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;
qreal timestamp;
qreal entry;
- qreal framecount;
- qreal animcount;
+ QSizeF animSheetSize;
};
//TODO: Move shaders inline once they've stablilized
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);
QList<QByteArray> attributes() const {
return QList<QByteArray>() << "vPos" << "vTex" << "vData" << "vVec"
- << "vColor" << "vDeformVec" << "vRotation" << "vAnimData";
+ << "vColor" << "vDeformVec" << "vRotation" << "vAnimData" << "vAnimPos";
};
void initialize() {
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");
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;
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)
, m_xVector(0)
, m_yVector(0)
, m_spriteEngine(0)
+ , m_spritesInterpolate(true)
, m_explicitColor(false)
, m_explicitRotation(false)
, m_explicitDeformation(false)
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) {
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
};
switch (perfLevel) {//Fallthrough intended
case Sprites:
m_material = SpriteMaterial::createMaterial();
- getState<ImageMaterialData>(m_material)->framecount = m_spriteEngine->maxFrames();
+ getState<ImageMaterialData>(m_material)->animSheetSize = QSizeF(image.size());
m_spriteEngine->setCount(m_count);
case Tabled:
if (!m_material)
switch (perfLevel){//Fall-through intended
case Sprites:
//Advance State
- getState<ImageMaterialData>(m_material)->animcount = m_spriteEngine->spriteCount();
m_spriteEngine->updateSprites(timeStamp);
foreach (const QString &str, m_groups){
int gIdx = m_system->groupIds[str];
for (int i=0; i < count; i++){
int spriteIdx = m_idxStarts[gIdx] + i;
Vertices<SpriteVertex> &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);
}
}
}
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:
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);