1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the Declarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include <private/qsgcontext_p.h>
43 #include <private/qsgadaptationlayer_p.h>
45 #include <qsgtexturematerial.h>
46 #include <qsgtexture.h>
48 #include "ultraparticle.h"
49 #include "particleemitter.h"
50 #include "spritestate.h"
51 #include "spriteengine.h"
52 #include <QGLFunctions>
53 #include <qsgengine.h>
57 const float CONV = 0.017453292519943295;
58 class UltraMaterial : public QSGMaterial
61 UltraMaterial(bool withSprites=false)
65 , usesSprites(withSprites)
67 setFlag(Blending, true);
78 virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
79 virtual QSGMaterialShader *createShader() const;
80 virtual int compare(const QSGMaterial *other) const
82 return this - static_cast<const UltraMaterial *>(other);
86 QSGTexture *colortable;
87 QSGTexture *sizetable;
88 QSGTexture *opacitytable;
95 class UltraMaterialData : public QSGMaterialShader
98 UltraMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0)
100 QFile vf(vertexFile ? vertexFile : ":resources/ultravertex.shader");
101 vf.open(QFile::ReadOnly);
102 m_vertex_code = vf.readAll();
104 QFile ff(fragmentFile ? fragmentFile : ":resources/ultrafragment.shader");
105 ff.open(QFile::ReadOnly);
106 m_fragment_code = ff.readAll();
108 Q_ASSERT(!m_vertex_code.isNull());
109 Q_ASSERT(!m_fragment_code.isNull());
113 QSGMaterialShader::deactivate();
115 for (int i=0; i<8; ++i) {
116 program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
120 virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
122 UltraMaterial *m = static_cast<UltraMaterial *>(newEffect);
123 state.context()->functions()->glActiveTexture(GL_TEXTURE1);
124 m->colortable->bind();
125 program()->setUniformValue(m_colortable_id, 1);
127 state.context()->functions()->glActiveTexture(GL_TEXTURE2);
128 m->sizetable->bind();
129 program()->setUniformValue(m_sizetable_id, 2);
131 state.context()->functions()->glActiveTexture(GL_TEXTURE3);
132 m->opacitytable->bind();
133 program()->setUniformValue(m_opacitytable_id, 3);
135 state.context()->functions()->glActiveTexture(GL_TEXTURE0);//Investigate why this screws up Text{} if placed before 1
138 program()->setUniformValue(m_opacity_id, state.opacity());
139 program()->setUniformValue(m_timestamp_id, (float) m->timestamp);
140 program()->setUniformValue(m_framecount_id, (float) m->framecount);
141 program()->setUniformValue(m_animcount_id, (float) m->animcount);
143 if (state.isMatrixDirty())
144 program()->setUniformValue(m_matrix_id, state.combinedMatrix());
147 virtual void initialize() {
148 m_colortable_id = program()->uniformLocation("colortable");
149 m_sizetable_id = program()->uniformLocation("sizetable");
150 m_opacitytable_id = program()->uniformLocation("opacitytable");
151 m_matrix_id = program()->uniformLocation("matrix");
152 m_opacity_id = program()->uniformLocation("opacity");
153 m_timestamp_id = program()->uniformLocation("timestamp");
154 m_framecount_id = program()->uniformLocation("framecount");
155 m_animcount_id = program()->uniformLocation("animcount");
158 virtual const char *vertexShader() const { return m_vertex_code.constData(); }
159 virtual const char *fragmentShader() const { return m_fragment_code.constData(); }
161 virtual char const *const *attributeNames() const {
162 static const char *attr[] = {
176 virtual bool isColorTable() const { return false; }
183 int m_opacitytable_id;
187 QByteArray m_vertex_code;
188 QByteArray m_fragment_code;
190 static float chunkOfBytes[1024];
192 float UltraMaterialData::chunkOfBytes[1024];
194 QSGMaterialShader *UltraMaterial::createShader() const
196 if(usesSprites)//TODO: Perhaps just swap the shaders, and don't mind the extra vector?
197 return new UltraMaterialData;
199 return new UltraMaterialData;
203 class SimpleMaterial : public UltraMaterial
205 virtual QSGMaterialShader *createShader() const;
208 class SimpleMaterialData : public QSGMaterialShader
211 SimpleMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0)
213 QFile vf(vertexFile ? vertexFile : ":resources/simplevertex.shader");
214 vf.open(QFile::ReadOnly);
215 m_vertex_code = vf.readAll();
217 QFile ff(fragmentFile ? fragmentFile : ":resources/simplefragment.shader");
218 ff.open(QFile::ReadOnly);
219 m_fragment_code = ff.readAll();
221 Q_ASSERT(!m_vertex_code.isNull());
222 Q_ASSERT(!m_fragment_code.isNull());
226 QSGMaterialShader::deactivate();
228 for (int i=0; i<8; ++i) {
229 program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
233 virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
235 UltraMaterial *m = static_cast<UltraMaterial *>(newEffect);
236 state.context()->functions()->glActiveTexture(GL_TEXTURE0);
239 program()->setUniformValue(m_opacity_id, state.opacity());
240 program()->setUniformValue(m_timestamp_id, (float) m->timestamp);
242 if (state.isMatrixDirty())
243 program()->setUniformValue(m_matrix_id, state.combinedMatrix());
246 virtual void initialize() {
247 m_matrix_id = program()->uniformLocation("matrix");
248 m_opacity_id = program()->uniformLocation("opacity");
249 m_timestamp_id = program()->uniformLocation("timestamp");
252 virtual const char *vertexShader() const { return m_vertex_code.constData(); }
253 virtual const char *fragmentShader() const { return m_fragment_code.constData(); }
255 virtual char const *const *attributeNames() const {
256 static const char *attr[] = {
266 virtual bool isColorTable() const { return false; }
272 QByteArray m_vertex_code;
273 QByteArray m_fragment_code;
275 static float chunkOfBytes[1024];
277 float SimpleMaterialData::chunkOfBytes[1024];
279 QSGMaterialShader *SimpleMaterial::createShader() const {
280 return new SimpleMaterialData;
283 UltraParticle::UltraParticle(QSGItem* parent)
284 : ParticleType(parent)
286 , m_color_variation(0.0)
289 , m_alphaVariation(0.0)
291 , m_redVariation(0.0)
292 , m_greenVariation(0.0)
293 , m_blueVariation(0.0)
295 , m_autoRotation(false)
298 , m_rotationVariation(0)
300 , m_rotationSpeedVariation(0)
305 setFlag(ItemHasContents);
308 QDeclarativeListProperty<SpriteState> UltraParticle::sprites()
310 return QDeclarativeListProperty<SpriteState>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
313 void UltraParticle::setImage(const QUrl &image)
315 if (image == m_image_name)
317 m_image_name = image;
323 void UltraParticle::setColortable(const QUrl &table)
325 if (table == m_colortable_name)
327 m_colortable_name = table;
328 emit colortableChanged();
332 void UltraParticle::setSizetable(const QUrl &table)
334 if (table == m_sizetable_name)
336 m_sizetable_name = table;
337 emit sizetableChanged();
341 void UltraParticle::setOpacitytable(const QUrl &table)
343 if (table == m_opacitytable_name)
345 m_opacitytable_name = table;
346 emit opacitytableChanged();
350 void UltraParticle::setColor(const QColor &color)
352 if (color == m_color)
358 void UltraParticle::setColorVariation(qreal var)
360 if (var == m_color_variation)
362 m_color_variation = var;
363 emit colorVariationChanged();
366 void UltraParticle::setCount(int c)
368 ParticleType::setCount(c);
369 m_pleaseReset = true;
372 void UltraParticle::reset()
374 ParticleType::reset();
375 m_pleaseReset = true;
378 void UltraParticle::createEngine()
381 delete m_spriteEngine;
382 if(m_sprites.count())
383 m_spriteEngine = new SpriteEngine(m_sprites, this);
389 static QSGGeometry::Attribute SimpleParticle_Attributes[] = {
390 { 0, 2, GL_FLOAT }, // Position
391 { 1, 2, GL_FLOAT }, // TexCoord
392 { 2, 4, GL_FLOAT }, // Data
393 { 3, 4, GL_FLOAT } // Vectors
396 static QSGGeometry::AttributeSet SimpleParticle_AttributeSet =
398 4, // Attribute Count
399 (2 + 2 + 4 + 4 ) * sizeof(float),
400 SimpleParticle_Attributes
403 static QSGGeometry::Attribute UltraParticle_Attributes[] = {
404 { 0, 2, GL_FLOAT }, // Position
405 { 1, 2, GL_FLOAT }, // TexCoord
406 { 2, 4, GL_FLOAT }, // Data
407 { 3, 4, GL_FLOAT }, // Vectors
408 { 4, 4, GL_UNSIGNED_BYTE }, // Colors
409 { 5, 4, GL_FLOAT }, // DeformationVectors
410 { 6, 3, GL_FLOAT }, // Rotation
411 { 7, 4, GL_FLOAT } // Anim Data
414 static QSGGeometry::AttributeSet UltraParticle_AttributeSet =
416 8, // Attribute Count
417 (2 + 2 + 4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
418 UltraParticle_Attributes
421 QSGGeometryNode* UltraParticle::buildSimpleParticleNode()
423 perfLevel = Simple;//TODO: Intermediate levels
424 QImage image = QImage(m_image_name.toLocalFile());
425 if (image.isNull()) {
426 printf("UltraParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile()));
429 int vCount = m_count * 4;
430 int iCount = m_count * 6;
431 qDebug() << "Simple Case";
433 QSGGeometry *g = new QSGGeometry(SimpleParticle_AttributeSet, vCount, iCount);
434 g->setDrawingMode(GL_TRIANGLES);
436 SimpleVertex *vertices = (SimpleVertex *) g->vertexData();
437 for (int p=0; p<m_count; ++p) {
438 for (int i=0; i<4; ++i) {
442 vertices[i].lifeSpan = 0;
443 vertices[i].size = 0;
444 vertices[i].endSize = 0;
466 quint16 *indices = g->indexDataAsUShort();
467 for (int i=0; i<m_count; ++i) {
483 m_material = new SimpleMaterial();
484 m_material->texture = sceneGraphEngine()->createTextureFromImage(image);
485 m_material->texture->setFiltering(QSGTexture::Linear);
486 m_material->framecount = 1;
487 m_node = new QSGGeometryNode();
488 m_node->setGeometry(g);
489 m_node->setMaterial(m_material);
496 QSGGeometryNode* UltraParticle::buildParticleNode()
498 if (m_count * 4 > 0xffff) {
499 printf("UltraParticle: Too many particles... \n");//####Why is this here?
504 printf("UltraParticle: Too few particles... \n");
508 qDebug() << m_colortable_name.isEmpty() << !m_color.isValid();
509 if(!m_sprites.count() && !m_bloat
510 && m_colortable_name.isEmpty()
511 && m_sizetable_name.isEmpty()
512 && m_opacitytable_name.isEmpty()
513 && !m_rotation && !m_rotationVariation
514 && !m_rotationSpeed && !m_rotationSpeedVariation
515 && !m_alphaVariation && m_alpha == 1.0
516 && !m_redVariation && !m_blueVariation && !m_greenVariation
517 && !m_color.isValid()
519 return buildSimpleParticleNode();
520 perfLevel = Sprites;//TODO: intermediate levels
521 qDebug() << "Complex Case";
524 if(m_sprites.count()){
525 if (!m_spriteEngine) {
526 qWarning() << "UltraParticle: No sprite engine...";
529 image = m_spriteEngine->assembledImage();
530 if(image.isNull())//Warning is printed in engine
533 image = QImage(m_image_name.toLocalFile());
534 if (image.isNull()) {
535 printf("UltraParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile()));
540 int vCount = m_count * 4;
541 int iCount = m_count * 6;
543 QSGGeometry *g = new QSGGeometry(UltraParticle_AttributeSet, vCount, iCount);
544 g->setDrawingMode(GL_TRIANGLES);
546 UltraVertex *vertices = (UltraVertex *) g->vertexData();
547 for (int p=0; p<m_count; ++p) {
549 for (int i=0; i<4; ++i) {
553 vertices[i].lifeSpan = 0;
554 vertices[i].size = 0;
555 vertices[i].endSize = 0;
564 vertices[i].rotation = 0;
565 vertices[i].rotationSpeed = 0;
566 vertices[i].autoRotate = 0;
567 vertices[i].animIdx = 0;
568 vertices[i].frameDuration = 1;
569 vertices[i].frameCount = 1;
570 vertices[i].animT = -1;
588 quint16 *indices = g->indexDataAsUShort();
589 for (int i=0; i<m_count; ++i) {
605 QImage colortable(m_colortable_name.toLocalFile());
606 QImage sizetable(m_sizetable_name.toLocalFile());
607 QImage opacitytable(m_opacitytable_name.toLocalFile());
608 m_material = new UltraMaterial();
609 if(colortable.isNull())
610 colortable = QImage(":resources/identitytable.png");
611 if(sizetable.isNull())
612 sizetable = QImage(":resources/identitytable.png");
613 if(opacitytable.isNull())
614 opacitytable = QImage(":resources/defaultFadeInOut.png");
615 Q_ASSERT(!colortable.isNull());
616 Q_ASSERT(!sizetable.isNull());
617 Q_ASSERT(!opacitytable.isNull());
618 m_material->colortable = sceneGraphEngine()->createTextureFromImage(colortable);
619 m_material->sizetable = sceneGraphEngine()->createTextureFromImage(sizetable);
620 m_material->opacitytable = sceneGraphEngine()->createTextureFromImage(opacitytable);
622 m_material->texture = sceneGraphEngine()->createTextureFromImage(image);
623 m_material->texture->setFiltering(QSGTexture::Linear);
625 m_material->framecount = 1;
627 m_material->framecount = m_spriteEngine->maxFrames();
628 m_spriteEngine->setCount(m_count);
631 m_node = new QSGGeometryNode();
632 m_node->setGeometry(g);
633 m_node->setMaterial(m_material);
640 QSGNode *UltraParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
650 m_pleaseReset = false;
653 if(m_system && m_system->isRunning())
657 m_node->markDirty(QSGNode::DirtyMaterial);
663 void UltraParticle::prepareNextFrame()
665 if (m_node == 0){ //TODO: Staggered loading (as emitted)
666 m_node = buildParticleNode();
669 qDebug() << "Feature level: " << perfLevel;
671 qint64 timeStamp = m_system->systemSync(this);
673 qreal time = timeStamp / 1000.;
674 m_material->timestamp = time;
677 if(m_spriteEngine){//perfLevel == Sprites?
678 m_material->animcount = m_spriteEngine->stateCount();
679 UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData();
680 m_spriteEngine->updateSprites(timeStamp);
681 for(int i=0; i<m_count; i++){
682 UltraVertices &p = particles[i];
683 int curIdx = m_spriteEngine->spriteState(i);
684 if(curIdx != p.v1.animIdx){
685 p.v1.animIdx = p.v2.animIdx = p.v3.animIdx = p.v4.animIdx = curIdx;
686 p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = m_spriteEngine->spriteStart(i)/1000.0;
687 p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->state(curIdx)->frames();
688 p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->state(curIdx)->duration();
692 m_material->animcount = 1;
696 template <typename VT>
697 IntermediateVertices* transplant(IntermediateVertices* iv, VT &v)
698 {//Deliberate typemangling cast
699 iv->v1 = (UltraVertex*)&(v.v1);
700 iv->v2 = (UltraVertex*)&(v.v2);
701 iv->v3 = (UltraVertex*)&(v.v3);
702 iv->v4 = (UltraVertex*)&(v.v4);
706 IntermediateVertices* UltraParticle::fetchIntermediateVertices(int pos)
708 //Note that this class ruins typesafety for you. Maybe even thread safety.
709 //TODO: Something better, possibly with templates or inheritance
710 static IntermediateVertices iv;
715 sv = (SimpleVertices *) m_node->geometry()->vertexData();
716 return transplant(&iv, sv[pos]);
722 uv = (UltraVertices *) m_node->geometry()->vertexData();
723 return transplant(&iv,uv[pos]);
727 void UltraParticle::reloadColor(const Color4ub &c, ParticleData* d)
729 UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData();
730 int pos = particleTypeIndex(d);
731 UltraVertices &p = particles[pos];
732 p.v1.color = p.v2.color = p.v3.color = p.v4.color = c;
735 /*Repalced by superclass templated function
736 void UltraParticle::vertexCopy(UltraVertex &b,const ParticleVertex& a)
738 b.x = a.x - m_systemOffset.x();
739 b.y = a.y - m_systemOffset.y();
741 b.lifeSpan = a.lifeSpan;
743 b.endSize = a.endSize;
752 void UltraParticle::reload(ParticleData *d)
757 int pos = particleTypeIndex(d);
758 IntermediateVertices* p = fetchIntermediateVertices(pos);
760 //Perhaps we could be more efficient?
761 vertexCopy(*p->v1, d->pv);
762 vertexCopy(*p->v2, d->pv);
763 vertexCopy(*p->v3, d->pv);
764 vertexCopy(*p->v4, d->pv);
767 void UltraParticle::load(ParticleData *d)
772 int pos = particleTypeIndex(d);
773 IntermediateVertices* p = fetchIntermediateVertices(pos);//Remember this removes typesafety!
775 qreal redVariation = m_color_variation + m_redVariation;
776 qreal greenVariation = m_color_variation + m_greenVariation;
777 qreal blueVariation = m_color_variation + m_blueVariation;
778 switch(perfLevel){//Fall-through is intended on all of them
780 // Initial Sprite State
781 p->v1->animT = p->v2->animT = p->v3->animT = p->v4->animT = p->v1->t;
782 p->v1->animIdx = p->v2->animIdx = p->v3->animIdx = p->v4->animIdx = 0;
784 SpriteState* state = m_spriteEngine->state(0);
785 p->v1->frameCount = p->v2->frameCount = p->v3->frameCount = p->v4->frameCount = state->frames();
786 p->v1->frameDuration = p->v2->frameDuration = p->v3->frameDuration = p->v4->frameDuration = state->duration();
787 m_spriteEngine->startSprite(pos);
789 p->v1->frameCount = p->v2->frameCount = p->v3->frameCount = p->v4->frameCount = 1;
790 p->v1->frameDuration = p->v2->frameDuration = p->v3->frameDuration = p->v4->frameDuration = 9999;
796 const QPointF &ret = m_xVector->sample(QPointF(d->pv.x, d->pv.y));
797 p->v1->xx = p->v2->xx = p->v3->xx = p->v4->xx = ret.x();
798 p->v1->xy = p->v2->xy = p->v3->xy = p->v4->xy = ret.y();
801 const QPointF &ret = m_yVector->sample(QPointF(d->pv.x, d->pv.y));
802 p->v1->yx = p->v2->yx = p->v3->yx = p->v4->yx = ret.x();
803 p->v1->yy = p->v2->yy = p->v3->yy = p->v4->yy = ret.y();
805 p->v1->rotation = p->v2->rotation = p->v3->rotation = p->v4->rotation =
806 (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
807 p->v1->rotationSpeed = p->v2->rotationSpeed = p->v3->rotationSpeed = p->v4->rotationSpeed =
808 (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV;
809 p->v1->autoRotate = p->v2->autoRotate = p->v3->autoRotate = p->v4->autoRotate = m_autoRotation?1.0:0.0;
811 //Color initialization
813 color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation;
814 color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation;
815 color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation;
816 color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation;
817 p->v1->color = p->v2->color = p->v3->color = p->v4->color = color;
822 vertexCopy(*p->v1, d->pv);
823 vertexCopy(*p->v2, d->pv);
824 vertexCopy(*p->v3, d->pv);
825 vertexCopy(*p->v4, d->pv);