/**************************************************************************** ** ** 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$ ** GNU Lesser General Public License Usage ** 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. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include "qsgimageparticle_p.h" #include "qsgparticleemitter_p.h" #include "qsgsprite_p.h" #include "qsgspriteengine_p.h" #include #include QT_BEGIN_NAMESPACE //###Switch to define later, for now user-friendly (no compilation) debugging is worth it DEFINE_BOOL_CONFIG_OPTION(qmlParticlesDebug, QML_PARTICLES_DEBUG) #ifndef QT_OPENGL_ES_2 #define SHADER_DEFINES "#version 120\n" #else #define SHADER_DEFINES "" #endif //TODO: Make it larger on desktop? Requires fixing up shader code with the same define #define UNIFORM_ARRAY_SIZE 64 const float CONV = 0.017453292519943295; class ImageMaterialData { public: ImageMaterialData() : texture(0), colorTable(0) {} ~ImageMaterialData(){ delete texture; delete colorTable; } QSGTexture *texture; QSGTexture *colorTable; float sizeTable[UNIFORM_ARRAY_SIZE]; float opacityTable[UNIFORM_ARRAY_SIZE]; qreal timestamp; qreal entry; qreal framecount; qreal animcount; }; //TODO: Move shaders inline once they've stablilized class TabledMaterialData : public ImageMaterialData {}; class TabledMaterial : public QSGSimpleMaterialShader { QSG_DECLARE_SIMPLE_SHADER(TabledMaterial, TabledMaterialData) public: TabledMaterial() { QFile vf(":defaultshaders/imagevertex.shader"); vf.open(QFile::ReadOnly); m_vertex_code = QByteArray(SHADER_DEFINES) + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n") + vf.readAll(); QFile ff(":defaultshaders/imagefragment.shader"); ff.open(QFile::ReadOnly); m_fragment_code = QByteArray(SHADER_DEFINES) + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n") + ff.readAll(); Q_ASSERT(!m_vertex_code.isNull()); Q_ASSERT(!m_fragment_code.isNull()); } const char *vertexShader() const { return m_vertex_code.constData(); } const char *fragmentShader() const { return m_fragment_code.constData(); } QList attributes() const { return QList() << "vPos" << "vTex" << "vData" << "vVec" << "vColor" << "vDeformVec" << "vRotation"; }; void initialize() { QSGSimpleMaterialShader::initialize(); program()->bind(); program()->setUniformValue("texture", 0); program()->setUniformValue("colortable", 1); glFuncs = QGLContext::currentContext()->functions(); m_timestamp_id = program()->uniformLocation("timestamp"); m_entry_id = program()->uniformLocation("entry"); m_sizetable_id = program()->uniformLocation("sizetable"); m_opacitytable_id = program()->uniformLocation("opacitytable"); } void updateState(const TabledMaterialData* d, const TabledMaterialData*) { glFuncs->glActiveTexture(GL_TEXTURE1); d->colorTable->bind(); glFuncs->glActiveTexture(GL_TEXTURE0); 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); } int m_entry_id; int m_timestamp_id; int m_sizetable_id; int m_opacitytable_id; QByteArray m_vertex_code; QByteArray m_fragment_code; QGLFunctions* glFuncs; }; class DeformableMaterialData : public ImageMaterialData {}; class DeformableMaterial : public QSGSimpleMaterialShader { QSG_DECLARE_SIMPLE_SHADER(DeformableMaterial, DeformableMaterialData) public: DeformableMaterial() { QFile vf(":defaultshaders/imagevertex.shader"); vf.open(QFile::ReadOnly); m_vertex_code = QByteArray(SHADER_DEFINES) + QByteArray("#define DEFORM\n#define COLOR\n") + vf.readAll(); QFile ff(":defaultshaders/imagefragment.shader"); ff.open(QFile::ReadOnly); m_fragment_code = QByteArray(SHADER_DEFINES) + QByteArray("#define DEFORM\n#define COLOR\n") + ff.readAll(); Q_ASSERT(!m_vertex_code.isNull()); Q_ASSERT(!m_fragment_code.isNull()); } const char *vertexShader() const { return m_vertex_code.constData(); } const char *fragmentShader() const { return m_fragment_code.constData(); } QList attributes() const { return QList() << "vPos" << "vTex" << "vData" << "vVec" << "vColor" << "vDeformVec" << "vRotation"; }; void initialize() { QSGSimpleMaterialShader::initialize(); program()->bind(); program()->setUniformValue("texture", 0); glFuncs = QGLContext::currentContext()->functions(); m_timestamp_id = program()->uniformLocation("timestamp"); m_entry_id = program()->uniformLocation("entry"); } void updateState(const DeformableMaterialData* d, const DeformableMaterialData*) { glFuncs->glActiveTexture(GL_TEXTURE0); d->texture->bind(); program()->setUniformValue(m_timestamp_id, (float) d->timestamp); program()->setUniformValue(m_entry_id, (float) d->entry); } int m_entry_id; int m_timestamp_id; QByteArray m_vertex_code; QByteArray m_fragment_code; QGLFunctions* glFuncs; }; class SpriteMaterialData : public ImageMaterialData {}; class SpriteMaterial : public QSGSimpleMaterialShader { QSG_DECLARE_SIMPLE_SHADER(SpriteMaterial, SpriteMaterialData) public: SpriteMaterial() { QFile vf(":defaultshaders/imagevertex.shader"); vf.open(QFile::ReadOnly); m_vertex_code = QByteArray(SHADER_DEFINES) + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n") + vf.readAll(); QFile ff(":defaultshaders/imagefragment.shader"); ff.open(QFile::ReadOnly); m_fragment_code = QByteArray(SHADER_DEFINES) + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n") + ff.readAll(); Q_ASSERT(!m_vertex_code.isNull()); Q_ASSERT(!m_fragment_code.isNull()); } const char *vertexShader() const { return m_vertex_code.constData(); } const char *fragmentShader() const { return m_fragment_code.constData(); } QList attributes() const { return QList() << "vPos" << "vTex" << "vData" << "vVec" << "vColor" << "vDeformVec" << "vRotation" << "vAnimData"; }; void initialize() { QSGSimpleMaterialShader::initialize(); program()->bind(); program()->setUniformValue("texture", 0); program()->setUniformValue("colortable", 1); glFuncs = QGLContext::currentContext()->functions(); m_timestamp_id = program()->uniformLocation("timestamp"); m_framecount_id = program()->uniformLocation("framecount"); m_animcount_id = program()->uniformLocation("animcount"); m_entry_id = program()->uniformLocation("entry"); m_sizetable_id = program()->uniformLocation("sizetable"); m_opacitytable_id = program()->uniformLocation("opacitytable"); } void updateState(const SpriteMaterialData* d, const SpriteMaterialData*) { glFuncs->glActiveTexture(GL_TEXTURE1); d->colorTable->bind(); // make sure we end by setting GL_TEXTURE0 as active texture glFuncs->glActiveTexture(GL_TEXTURE0); 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_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_entry_id; int m_sizetable_id; int m_opacitytable_id; QByteArray m_vertex_code; QByteArray m_fragment_code; QGLFunctions* glFuncs; }; class ColoredMaterialData : public ImageMaterialData {}; class ColoredMaterial : public QSGSimpleMaterialShader { QSG_DECLARE_SIMPLE_SHADER(ColoredMaterial, ColoredMaterialData) public: ColoredMaterial() { QFile vf(":defaultshaders/imagevertex.shader"); vf.open(QFile::ReadOnly); m_vertex_code = QByteArray(SHADER_DEFINES) + QByteArray("#define COLOR\n") + vf.readAll(); QFile ff(":defaultshaders/imagefragment.shader"); ff.open(QFile::ReadOnly); m_fragment_code = QByteArray(SHADER_DEFINES) + QByteArray("#define COLOR\n") + ff.readAll(); Q_ASSERT(!m_vertex_code.isNull()); Q_ASSERT(!m_fragment_code.isNull()); } const char *vertexShader() const { return m_vertex_code.constData(); } const char *fragmentShader() const { return m_fragment_code.constData(); } void activate() { QSGSimpleMaterialShader::activate(); #ifndef QT_OPENGL_ES_2 glEnable(GL_POINT_SPRITE); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); #endif } void deactivate() { QSGSimpleMaterialShader::deactivate(); #ifndef QT_OPENGL_ES_2 glDisable(GL_POINT_SPRITE); glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); #endif } QList attributes() const { return QList() << "vPos" << "vData" << "vVec" << "vColor"; } void initialize() { QSGSimpleMaterialShader::initialize(); program()->bind(); program()->setUniformValue("texture", 0); glFuncs = QGLContext::currentContext()->functions(); m_timestamp_id = program()->uniformLocation("timestamp"); m_entry_id = program()->uniformLocation("entry"); } void updateState(const ColoredMaterialData* d, const ColoredMaterialData*) { glFuncs->glActiveTexture(GL_TEXTURE0); d->texture->bind(); program()->setUniformValue(m_timestamp_id, (float) d->timestamp); program()->setUniformValue(m_entry_id, (float) d->entry); } int m_timestamp_id; int m_entry_id; QByteArray m_vertex_code; QByteArray m_fragment_code; QGLFunctions* glFuncs; }; class SimpleMaterialData : public ImageMaterialData {}; class SimpleMaterial : public QSGSimpleMaterialShader { QSG_DECLARE_SIMPLE_SHADER(SimpleMaterial, SimpleMaterialData) public: SimpleMaterial() { QFile vf(":defaultshaders/imagevertex.shader"); vf.open(QFile::ReadOnly); m_vertex_code = QByteArray(SHADER_DEFINES) + vf.readAll(); QFile ff(":defaultshaders/imagefragment.shader"); ff.open(QFile::ReadOnly); m_fragment_code = QByteArray(SHADER_DEFINES) + ff.readAll(); Q_ASSERT(!m_vertex_code.isNull()); Q_ASSERT(!m_fragment_code.isNull()); } const char *vertexShader() const { return m_vertex_code.constData(); } const char *fragmentShader() const { return m_fragment_code.constData(); } void activate() { QSGSimpleMaterialShader::activate(); #ifndef QT_OPENGL_ES_2 glEnable(GL_POINT_SPRITE); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); #endif } void deactivate() { QSGSimpleMaterialShader::deactivate(); #ifndef QT_OPENGL_ES_2 glDisable(GL_POINT_SPRITE); glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); #endif } QList attributes() const { return QList() << "vPos" << "vData" << "vVec"; } void initialize() { QSGSimpleMaterialShader::initialize(); program()->bind(); program()->setUniformValue("texture", 0); glFuncs = QGLContext::currentContext()->functions(); m_timestamp_id = program()->uniformLocation("timestamp"); m_entry_id = program()->uniformLocation("entry"); } void updateState(const SimpleMaterialData* d, const SimpleMaterialData*) { glFuncs->glActiveTexture(GL_TEXTURE0); d->texture->bind(); program()->setUniformValue(m_timestamp_id, (float) d->timestamp); program()->setUniformValue(m_entry_id, (float) d->entry); } int m_timestamp_id; int m_entry_id; QByteArray m_vertex_code; QByteArray m_fragment_code; QGLFunctions* glFuncs; }; void fillUniformArrayFromImage(float* array, const QImage& img, int size) { if (img.isNull()){ for (int i=0; i QtQuick.Particles2::ImageParticle::sprites The sprite or sprites used to draw this particle. */ /*! \qmlproperty url QtQuick.Particles2::ImageParticle::colorTable An image whose color will be used as a 1D texture to determine color over life. E.g. when the particle is halfway through its lifetime, it will have the color specified halfway across the image. This color is blended with the color property and the color of the source image. */ /*! \qmlproperty url QtQuick.Particles2::ImageParticle::sizeTable An image whose opacity will be used as a 1D texture to determine size over life. This property is expected to be removed shortly, in favor of custom easing curves to determine size over life. */ /*! \qmlproperty url QtQuick.Particles2::ImageParticle::opacityTable An image whose opacity will be used as a 1D texture to determine size over life. This property is expected to be removed shortly, in favor of custom easing curves to determine opacity over life. */ /*! \qmlproperty color QtQuick.Particles2::ImageParticle::color If a color is specified, the provided image will be colorized with it. Default is white (no change). */ /*! \qmlproperty real QtQuick.Particles2::ImageParticle::colorVariation This number represents the color variation applied to individual particles. Setting colorVariation is the same as setting redVariation, greenVariation, and blueVariation to the same number. Each channel can vary between particle by up to colorVariation from its usual color. Color is measured, per channel, from 0.0 to 1.0. Default is 0.0 */ /*! \qmlproperty real QtQuick.Particles2::ImageParticle::redVariation The variation in the red color channel between particles. Color is measured, per channel, from 0.0 to 1.0. Default is 0.0 */ /*! \qmlproperty real QtQuick.Particles2::ImageParticle::greenVariation The variation in the green color channel between particles. Color is measured, per channel, from 0.0 to 1.0. Default is 0.0 */ /*! \qmlproperty real QtQuick.Particles2::ImageParticle::blueVariation The variation in the blue color channel between particles. Color is measured, per channel, from 0.0 to 1.0. Default is 0.0 */ /*! \qmlproperty real QtQuick.Particles2::ImageParticle::alpha An alpha to be applied to the image. This value is multiplied by the value in the image, and the value in the color property. Particles have additive blending, so lower alpha on single particles leads to stronger effects when multiple particles overlap. Alpha is measured from 0.0 to 1.0. Default is 1.0 */ /*! \qmlproperty real QtQuick.Particles2::ImageParticle::alphaVariation The variation in the alpha channel between particles. Alpha is measured from 0.0 to 1.0. Default is 0.0 */ /*! \qmlproperty real QtQuick.Particles2::ImageParticle::rotation If set the image will be rotated by this many degrees before it is drawn. The particle coordinates are not transformed. */ /*! \qmlproperty real QtQuick.Particles2::ImageParticle::rotationVariation If set the rotation of individual particles will vary by up to this much between particles. */ /*! \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeed If set particles will rotate at this speed in degrees/second. */ /*! \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeedVariation If set the rotationSpeed of individual particles will vary by up to this much between particles. */ /*! \qmlproperty bool QtQuick.Particles2::ImageParticle::autoRotation If set to true then a rotation will be applied on top of the particles rotation, so that it faces the direction of travel. So to face away from the direction of travel, set autoRotation to true and rotation to 180. Default is false */ /*! \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::xVector Allows you to deform the particle image when drawn. The rectangular image will be deformed so that the horizontal sides are in the shape of this vector instead of (1,0). */ /*! \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::yVector Allows you to deform the particle image when drawn. The rectangular image will be deformed so that the vertical sides are in the shape of this vector instead of (0,1). */ /*! \qmlproperty EntryEffect QtQuick.Particles2::ImageParticle::entryEffect This property provides basic and cheap entrance and exit effects for the particles. For fine-grained control, see sizeTable and opacityTable. Acceptable values are \list \o None: Particles just appear and disappear. \o Fade: Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end. \o Scale: Particles scale in from 0 size at the start of their life, and scale back to 0 at the end. \endlist Default value is Fade. */ QSGImageParticle::QSGImageParticle(QSGItem* parent) : QSGParticlePainter(parent) , m_do_reset(false) , m_color_variation(0.0) , m_rootNode(0) , m_material(0) , m_alphaVariation(0.0) , m_alpha(1.0) , m_redVariation(0.0) , m_greenVariation(0.0) , m_blueVariation(0.0) , m_rotation(0) , m_autoRotation(false) , m_rotationVariation(0) , m_rotationSpeed(0) , m_rotationSpeedVariation(0) , m_xVector(0) , m_yVector(0) , m_spriteEngine(0) , m_bloat(false) , perfLevel(Unknown) , m_lastLevel(Unknown) , m_debugMode(false) , m_entryEffect(Fade) { setFlag(ItemHasContents); m_debugMode = qmlParticlesDebug(); } QSGImageParticle::~QSGImageParticle() { delete m_material; } QDeclarativeListProperty QSGImageParticle::sprites() { return QDeclarativeListProperty(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); } void QSGImageParticle::setImage(const QUrl &image) { if (image == m_image_name) return; m_image_name = image; emit imageChanged(); reset(); } void QSGImageParticle::setColortable(const QUrl &table) { if (table == m_colortable_name) return; m_colortable_name = table; emit colortableChanged(); reset(); } void QSGImageParticle::setSizetable(const QUrl &table) { if (table == m_sizetable_name) return; m_sizetable_name = table; emit sizetableChanged(); reset(); } void QSGImageParticle::setOpacitytable(const QUrl &table) { if (table == m_opacitytable_name) return; m_opacitytable_name = table; emit opacitytableChanged(); reset(); } void QSGImageParticle::setColor(const QColor &color) { if (color == m_color) return; m_color = color; emit colorChanged(); if (perfLevel < Colored) reset(); } void QSGImageParticle::setColorVariation(qreal var) { if (var == m_color_variation) return; m_color_variation = var; emit colorVariationChanged(); if (perfLevel < Colored) reset(); } void QSGImageParticle::setAlphaVariation(qreal arg) { if (m_alphaVariation != arg) { m_alphaVariation = arg; emit alphaVariationChanged(arg); } if (perfLevel < Colored) reset(); } void QSGImageParticle::setAlpha(qreal arg) { if (m_alpha != arg) { m_alpha = arg; emit alphaChanged(arg); } if (perfLevel < Colored) reset(); } void QSGImageParticle::setRedVariation(qreal arg) { if (m_redVariation != arg) { m_redVariation = arg; emit redVariationChanged(arg); } if (perfLevel < Colored) reset(); } void QSGImageParticle::setGreenVariation(qreal arg) { if (m_greenVariation != arg) { m_greenVariation = arg; emit greenVariationChanged(arg); } if (perfLevel < Colored) reset(); } void QSGImageParticle::setBlueVariation(qreal arg) { if (m_blueVariation != arg) { m_blueVariation = arg; emit blueVariationChanged(arg); } if (perfLevel < Colored) reset(); } void QSGImageParticle::setRotation(qreal arg) { if (m_rotation != arg) { m_rotation = arg; emit rotationChanged(arg); } if (perfLevel < Deformable) reset(); } void QSGImageParticle::setRotationVariation(qreal arg) { if (m_rotationVariation != arg) { m_rotationVariation = arg; emit rotationVariationChanged(arg); } if (perfLevel < Deformable) reset(); } void QSGImageParticle::setRotationSpeed(qreal arg) { if (m_rotationSpeed != arg) { m_rotationSpeed = arg; emit rotationSpeedChanged(arg); } if (perfLevel < Deformable) reset(); } void QSGImageParticle::setRotationSpeedVariation(qreal arg) { if (m_rotationSpeedVariation != arg) { m_rotationSpeedVariation = arg; emit rotationSpeedVariationChanged(arg); } if (perfLevel < Deformable) reset(); } void QSGImageParticle::setAutoRotation(bool arg) { if (m_autoRotation != arg) { m_autoRotation = arg; emit autoRotationChanged(arg); } if (perfLevel < Deformable) reset(); } void QSGImageParticle::setXVector(QSGDirection* arg) { if (m_xVector != arg) { m_xVector = arg; emit xVectorChanged(arg); } if (perfLevel < Deformable) reset(); } void QSGImageParticle::setYVector(QSGDirection* arg) { if (m_yVector != arg) { m_yVector = arg; emit yVectorChanged(arg); } if (perfLevel < Deformable) reset(); } void QSGImageParticle::setBloat(bool arg) { if (m_bloat != arg) { m_bloat = arg; emit bloatChanged(arg); } if (perfLevel < 9999) reset(); } void QSGImageParticle::setEntryEffect(EntryEffect arg) { if (m_entryEffect != arg) { m_entryEffect = arg; if (m_material) getState(m_material)->entry = (qreal) m_entryEffect; emit entryEffectChanged(arg); } } void QSGImageParticle::reset() { QSGParticlePainter::reset(); m_pleaseReset = true; update(); } void QSGImageParticle::createEngine() { 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 SimpleParticle_Attributes[] = { { 0, 2, GL_FLOAT }, // Position { 1, 4, GL_FLOAT }, // Data { 2, 4, GL_FLOAT } // Vectors }; static QSGGeometry::AttributeSet SimpleParticle_AttributeSet = { 3, // Attribute Count ( 2 + 4 + 4 ) * sizeof(float), SimpleParticle_Attributes }; static QSGGeometry::Attribute ColoredParticle_Attributes[] = { { 0, 2, GL_FLOAT }, // Position { 1, 4, GL_FLOAT }, // Data { 2, 4, GL_FLOAT }, // Vectors { 3, 4, GL_UNSIGNED_BYTE }, // Colors }; static QSGGeometry::AttributeSet ColoredParticle_AttributeSet = { 4, // Attribute Count ( 2 + 4 + 4 ) * sizeof(float) + 4 * sizeof(uchar), ColoredParticle_Attributes }; static QSGGeometry::Attribute DeformableParticle_Attributes[] = { { 0, 2, GL_FLOAT }, // Position { 1, 2, GL_FLOAT }, // TexCoord { 2, 4, GL_FLOAT }, // Data { 3, 4, GL_FLOAT }, // Vectors { 4, 4, GL_UNSIGNED_BYTE }, // Colors { 5, 4, GL_FLOAT }, // DeformationVectors { 6, 3, GL_FLOAT }, // Rotation }; static QSGGeometry::AttributeSet DeformableParticle_AttributeSet = { 7, // Attribute Count (2 + 2 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar), DeformableParticle_Attributes }; static QSGGeometry::Attribute SpriteParticle_Attributes[] = { { 0, 2, GL_FLOAT }, // Position { 1, 2, GL_FLOAT }, // TexCoord { 2, 4, GL_FLOAT }, // Data { 3, 4, GL_FLOAT }, // Vectors { 4, 4, GL_UNSIGNED_BYTE }, // Colors { 5, 4, GL_FLOAT }, // DeformationVectors { 6, 3, GL_FLOAT }, // Rotation { 7, 4, GL_FLOAT } // Anim Data }; static QSGGeometry::AttributeSet SpriteParticle_AttributeSet = { 8, // Attribute Count (2 + 2 + 4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar), SpriteParticle_Attributes }; QSGGeometryNode* QSGImageParticle::buildParticleNodes() { #ifdef QT_OPENGL_ES_2 if (m_count * 4 > 0xffff) { printf("ImageParticle: Too many particles - maximum 16,000 per ImageParticle.\n");//ES 2 vertex count limit is ushort return 0; } #endif if (count() <= 0) return 0; if (m_sprites.count() || m_bloat) { perfLevel = Sprites; } else if (!m_colortable_name.isEmpty() || !m_sizetable_name.isEmpty() || !m_opacitytable_name.isEmpty()) { perfLevel = Tabled; } else if (m_autoRotation || m_rotation || m_rotationVariation || m_rotationSpeed || m_rotationSpeedVariation) { perfLevel = Deformable; } else if (m_alphaVariation || m_alpha != 1.0 || m_color.isValid() || m_color_variation || m_redVariation || m_blueVariation || m_greenVariation) { perfLevel = Colored; } else { perfLevel = Simple; } if (perfLevel >= Colored && !m_color.isValid()) m_color = QColor(Qt::white);//Hidden default, but different from unset QImage image; if (perfLevel >= Sprites){ if (!m_spriteEngine) { qWarning() << "ImageParticle: No sprite engine..."; return 0; } image = m_spriteEngine->assembledImage(); if (image.isNull())//Warning is printed in engine return 0; } else { image = QImage(m_image_name.toLocalFile()); if (image.isNull()) { printf("ImageParticle: loading image failed '%s'\n", qPrintable(m_image_name.toLocalFile())); return 0; } } if (m_material) { delete m_material; m_material = 0; } //Setup material QImage colortable; QImage sizetable; QImage opacitytable; switch (perfLevel) {//Fallthrough intended case Sprites: m_material = SpriteMaterial::createMaterial(); getState(m_material)->framecount = m_spriteEngine->maxFrames(); m_spriteEngine->setCount(m_count); case Tabled: if (!m_material) m_material = TabledMaterial::createMaterial(); colortable = QImage(m_colortable_name.toLocalFile()); sizetable = QImage(m_sizetable_name.toLocalFile()); opacitytable = QImage(m_opacitytable_name.toLocalFile()); if (colortable.isNull()) colortable = QImage(":defaultshaders/identitytable.png"); Q_ASSERT(!colortable.isNull()); getState(m_material)->colorTable = sceneGraphEngine()->createTextureFromImage(colortable); fillUniformArrayFromImage(getState(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE); fillUniformArrayFromImage(getState(m_material)->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE); case Deformable: if (!m_material) m_material = DeformableMaterial::createMaterial(); case Colored: if (!m_material) m_material = ColoredMaterial::createMaterial(); default://Also Simple if (!m_material) m_material = SimpleMaterial::createMaterial(); getState(m_material)->texture = sceneGraphEngine()->createTextureFromImage(image); getState(m_material)->texture->setFiltering(QSGTexture::Linear); getState(m_material)->entry = (qreal) m_entryEffect; m_material->setFlag(QSGMaterial::Blending); } foreach (const QString &str, m_groups){ int gIdx = m_system->m_groupIds[str]; int count = m_system->m_groupData[gIdx]->size(); QSGGeometryNode* node = new QSGGeometryNode(); node->setMaterial(m_material); m_nodes.insert(gIdx, node); m_idxStarts.insert(gIdx, m_lastIdxStart); m_lastIdxStart += count; //Create Particle Geometry int vCount = count * 4; int iCount = count * 6; QSGGeometry *g; if (perfLevel == Sprites) g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount); else if (perfLevel == Tabled) g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount); else if (perfLevel == Deformable) g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount); else if (perfLevel == Colored) g = new QSGGeometry(ColoredParticle_AttributeSet, count, 0); else //Simple g = new QSGGeometry(SimpleParticle_AttributeSet, count, 0); node->setGeometry(g); if (perfLevel <= Colored){ g->setDrawingMode(GL_POINTS); if (m_debugMode){ GLfloat pointSizeRange[2]; glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange); qDebug() << "Using point sprites, GL_ALIASED_POINT_SIZE_RANGE " <setDrawingMode(GL_TRIANGLES); for (int p=0; p < count; ++p) commit(gIdx, p);//commit sets geometry for the node, has its own perfLevel switch if (perfLevel == Sprites) initTexCoords((SpriteVertex*)g->vertexData(), vCount); else if (perfLevel == Tabled) initTexCoords((DeformableVertex*)g->vertexData(), vCount); else if (perfLevel == Deformable) initTexCoords((DeformableVertex*)g->vertexData(), vCount); if (perfLevel > Colored){ quint16 *indices = g->indexDataAsUShort(); for (int i=0; i < count; ++i) { int o = i * 4; indices[0] = o; indices[1] = o + 1; indices[2] = o + 2; indices[3] = o + 1; indices[4] = o + 3; indices[5] = o + 2; indices += 6; } } } foreach (QSGGeometryNode* node, m_nodes){ if (node == *(m_nodes.begin())) continue; (*(m_nodes.begin()))->appendChildNode(node); } return *(m_nodes.begin()); } QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) { if (m_pleaseReset){ m_lastLevel = perfLevel; delete m_rootNode;//Automatically deletes children m_rootNode = 0; m_nodes.clear(); m_idxStarts.clear(); m_lastIdxStart = 0; if (m_material) delete m_material; m_material = 0; m_pleaseReset = false; } if (m_system && m_system->isRunning() && !m_system->isPaused()){ prepareNextFrame(); if (m_rootNode) { update(); //### Should I be using dirty geometry too/instead? foreach (QSGGeometryNode* node, m_nodes) node->markDirty(QSGNode::DirtyMaterial); } } return m_rootNode; } void QSGImageParticle::prepareNextFrame() { if (m_rootNode == 0){//TODO: Staggered loading (as emitted) m_rootNode = buildParticleNodes(); if (m_rootNode == 0) return; if(m_debugMode){ qDebug() << "QSGImageParticle Feature level: " << perfLevel; qDebug() << "QSGImageParticle Nodes: "; int count = 0; foreach(int i, m_nodes.keys()){ qDebug() << "Group " << i << " (" << m_system->m_groupData[i]->size() << " particles)"; count += m_system->m_groupData[i]->size(); } qDebug() << "Total count: " << count; } } qint64 timeStamp = m_system->systemSync(this); qreal time = timeStamp / 1000.; 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->m_groupIds[str]; int count = m_system->m_groupData[gIdx]->size(); Vertices* particles = (Vertices *) m_nodes[gIdx]->geometry()->vertexData(); 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; 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); } } } case Tabled: case Deformable: case Colored: case Simple: default: //Also Simple getState(m_material)->timestamp = time; break; } } void QSGImageParticle::reloadColor(const Color4ub &c, QSGParticleData* d) { d->color = c; //TODO: get index for reload - or make function take an index } void QSGImageParticle::initialize(int gIdx, int pIdx) { Color4ub color; QSGParticleData* datum = m_system->m_groupData[gIdx]->data[pIdx]; qreal redVariation = m_color_variation + m_redVariation; qreal greenVariation = m_color_variation + m_greenVariation; qreal blueVariation = m_color_variation + m_blueVariation; int spriteIdx = m_idxStarts[gIdx] + datum->index; switch (perfLevel){//Fall-through is intended on all of them case Sprites: // Initial Sprite State datum->animT = datum->t; datum->animIdx = 0; if (m_spriteEngine){ m_spriteEngine->start(spriteIdx); datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx); datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx); }else{ datum->frameCount = 1; datum->frameDuration = 9999; } case Tabled: case Deformable: //Initial Rotation if (m_xVector){ const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y)); datum->xx = ret.x(); datum->xy = ret.y(); } if (m_yVector){ const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y)); datum->yx = ret.x(); datum->yy = ret.y(); } datum->rotation = (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV; datum->rotationSpeed = (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV; datum->autoRotate = m_autoRotation?1.0:0.0; case Colored: //Color initialization // Particle color color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation; color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation; color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation; color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation; datum->color = color; default: break; } } void QSGImageParticle::commit(int gIdx, int pIdx) { if (m_pleaseReset) return; QSGGeometryNode *node = m_nodes[gIdx]; if (!node) return; QSGParticleData* datum = m_system->m_groupData[gIdx]->data[pIdx]; node->setFlag(QSGNode::OwnsGeometry, false); SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData(); DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData(); ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData(); SimpleVertex *simpleVertices = (SimpleVertex *) node->geometry()->vertexData(); switch (perfLevel){//No automatic fall through intended on this one case Sprites: spriteVertices += pIdx*4; for (int i=0; i<4; i++){ spriteVertices[i].x = datum->x - m_systemOffset.x(); spriteVertices[i].y = datum->y - m_systemOffset.y(); spriteVertices[i].t = datum->t; spriteVertices[i].lifeSpan = datum->lifeSpan; spriteVertices[i].size = datum->size; spriteVertices[i].endSize = datum->endSize; spriteVertices[i].vx = datum->vx; spriteVertices[i].vy = datum->vy; spriteVertices[i].ax = datum->ax; spriteVertices[i].ay = datum->ay; spriteVertices[i].xx = datum->xx; spriteVertices[i].xy = datum->xy; spriteVertices[i].yx = datum->yx; spriteVertices[i].yy = datum->yy; spriteVertices[i].rotation = datum->rotation; spriteVertices[i].rotationSpeed = datum->rotationSpeed; spriteVertices[i].autoRotate = datum->autoRotate; spriteVertices[i].animIdx = datum->animIdx; spriteVertices[i].frameDuration = datum->frameDuration; spriteVertices[i].frameCount = datum->frameCount; spriteVertices[i].animT = datum->animT; spriteVertices[i].color.r = datum->color.r; spriteVertices[i].color.g = datum->color.g; spriteVertices[i].color.b = datum->color.b; spriteVertices[i].color.a = datum->color.a; } break; case Tabled: //Fall through until it has its own vertex class case Deformable: deformableVertices += pIdx*4; for (int i=0; i<4; i++){ deformableVertices[i].x = datum->x - m_systemOffset.x(); deformableVertices[i].y = datum->y - m_systemOffset.y(); deformableVertices[i].t = datum->t; deformableVertices[i].lifeSpan = datum->lifeSpan; deformableVertices[i].size = datum->size; deformableVertices[i].endSize = datum->endSize; deformableVertices[i].vx = datum->vx; deformableVertices[i].vy = datum->vy; deformableVertices[i].ax = datum->ax; deformableVertices[i].ay = datum->ay; deformableVertices[i].xx = datum->xx; deformableVertices[i].xy = datum->xy; deformableVertices[i].yx = datum->yx; deformableVertices[i].yy = datum->yy; deformableVertices[i].rotation = datum->rotation; deformableVertices[i].rotationSpeed = datum->rotationSpeed; deformableVertices[i].autoRotate = datum->autoRotate; deformableVertices[i].color.r = datum->color.r; deformableVertices[i].color.g = datum->color.g; deformableVertices[i].color.b = datum->color.b; deformableVertices[i].color.a = datum->color.a; } break; case Colored: coloredVertices += pIdx*1; for (int i=0; i<1; i++){ coloredVertices[i].x = datum->x - m_systemOffset.x(); coloredVertices[i].y = datum->y - m_systemOffset.y(); coloredVertices[i].t = datum->t; coloredVertices[i].lifeSpan = datum->lifeSpan; coloredVertices[i].size = datum->size; coloredVertices[i].endSize = datum->endSize; coloredVertices[i].vx = datum->vx; coloredVertices[i].vy = datum->vy; coloredVertices[i].ax = datum->ax; coloredVertices[i].ay = datum->ay; coloredVertices[i].color.r = datum->color.r; coloredVertices[i].color.g = datum->color.g; coloredVertices[i].color.b = datum->color.b; coloredVertices[i].color.a = datum->color.a; } break; case Simple: simpleVertices += pIdx*1; for (int i=0; i<1; i++){ simpleVertices[i].x = datum->x - m_systemOffset.x(); simpleVertices[i].y = datum->y - m_systemOffset.y(); simpleVertices[i].t = datum->t; simpleVertices[i].lifeSpan = datum->lifeSpan; simpleVertices[i].size = datum->size; simpleVertices[i].endSize = datum->endSize; simpleVertices[i].vx = datum->vx; simpleVertices[i].vy = datum->vy; simpleVertices[i].ax = datum->ax; simpleVertices[i].ay = datum->ay; } break; default: break; } node->setFlag(QSGNode::OwnsGeometry, true); } QT_END_NAMESPACE