diff options
Diffstat (limited to 'src/declarative')
102 files changed, 11242 insertions, 284 deletions
diff --git a/src/declarative/declarative.pro b/src/declarative/declarative.pro index bc3dbbb9c4..5cb1d9570c 100644 --- a/src/declarative/declarative.pro +++ b/src/declarative/declarative.pro @@ -19,7 +19,7 @@ exists("qdeclarative_enable_gcov") { LIBS += -lgcov } -include($$QT_SOURCE_TREE/src/qbase.pri) +load(qt_module_config) HEADERS += qtdeclarativeversion.h @@ -33,6 +33,7 @@ include(qml/qml.pri) include(debugger/debugger.pri) include(scenegraph/scenegraph.pri) include(items/items.pri) +include(particles/particles.pri) symbian: { TARGET.UID3=0x2001E623 diff --git a/src/declarative/graphicsitems/qdeclarativeanimatedimage.cpp b/src/declarative/graphicsitems/qdeclarativeanimatedimage.cpp index b7fcbb546e..8787a5ed68 100644 --- a/src/declarative/graphicsitems/qdeclarativeanimatedimage.cpp +++ b/src/declarative/graphicsitems/qdeclarativeanimatedimage.cpp @@ -87,6 +87,32 @@ QT_BEGIN_NAMESPACE */ /*! + \qmlproperty url AnimatedImage::source + + This property holds the URL that refers to the source image. + + AnimatedImage can handle any image format supported by Qt, loaded from any + URL scheme supported by Qt. + + \sa QDeclarativeImageProvider +*/ + +/*! + \qmlproperty bool AnimatedImage::asynchronous + + Specifies that images on the local filesystem should be loaded + asynchronously in a separate thread. The default value is + false, causing the user interface thread to block while the + image is loaded. Setting \a asynchronous to true is useful where + maintaining a responsive user interface is more desirable + than having images immediately visible. + + Note that this property is only valid for images read from the + local filesystem. Images loaded via a network resource (e.g. HTTP) + are always loaded asynchonously. +*/ + +/*! \qmlproperty bool AnimatedImage::cache \since Quick 1.1 diff --git a/src/declarative/items/items.pri b/src/declarative/items/items.pri index d6942973cd..f29a82e77e 100644 --- a/src/declarative/items/items.pri +++ b/src/declarative/items/items.pri @@ -61,6 +61,9 @@ HEADERS += \ $$PWD/qsgcanvasitem_p.h \ $$PWD/qsgcontext2d_p.h \ $$PWD/qsgcontext2d_p_p.h \ + $$PWD/qsgspriteengine_p.h \ + $$PWD/qsgsprite_p.h \ + $$PWD/qsgspriteimage_p.h \ SOURCES += \ $$PWD/qsgevents.cpp \ @@ -100,6 +103,9 @@ SOURCES += \ $$PWD/qsgimplicitsizeitem.cpp \ $$PWD/qsgcanvasitem.cpp \ $$PWD/qsgcontext2d.cpp \ + $$PWD/qsgspriteengine.cpp \ + $$PWD/qsgsprite.cpp \ + $$PWD/qsgspriteimage.cpp \ SOURCES += \ $$PWD/qsgshadereffectitem.cpp \ diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp index 16bd8ce44c..dce5fe0888 100644 --- a/src/declarative/items/qsgcanvas.cpp +++ b/src/declarative/items/qsgcanvas.cpp @@ -64,6 +64,8 @@ QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_NO_THREADED_RENDERER) DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP) +extern Q_OPENGL_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); + /* Focus behavior ============== @@ -79,10 +81,65 @@ a scope focused item that takes precedence over the item being added. Otherwise the focus of the added tree is used. In the case of of a tree of items being added to a canvas for the first time, which may have a conflicted focus state (two or more items in one scope having focus set), the same rule is applied item by item - -thus the first item that has focus will get it (assuming the scope doesn't already +thus the first item that has focus will get it (assuming the scope doesn't already have a scope focused item), and the other items will have their focus cleared. */ +/* + Threaded Rendering + ================== + + The threaded rendering uses a number of different variables to track potential + states used to handle resizing, initial paint, grabbing and driving animations + while ALWAYS keeping the GL context in the rendering thread and keeping the + overhead of normal one-shot paints and vblank driven animations at a minimum. + + Resize, initial show and grab suffer slightly in this model as they are locked + to the rendering in the rendering thread, but this is a necessary evil for + the system to work. + + Variables that are used: + + Private::animationRunning: This is true while the animations are running, and only + written to inside locks. + + RenderThread::isGuiBlocked: This is used to indicate that the GUI thread owns the + lock. This variable is an integer to allow for recursive calls to lockInGui() + without using a recursive mutex. See isGuiBlockPending. + + RenderThread::isPaintComplete: This variable is cleared when rendering starts and + set once rendering is complete. It is monitored in the paintEvent(), + resizeEvent() and grab() functions to force them to wait for rendering to + complete. + + RenderThread::isGuiBlockPending: This variable is set in the render thread just + before the sync event is sent to the GUI thread. It is used to avoid deadlocks + in the case where render thread waits while waiting for GUI to pick up the sync + event and GUI thread gets a resizeEvent, the initial paintEvent or a grab. + When this happens, we use the + exhaustSyncEvent() function to do the sync right there and mark the coming + sync event to be discarded. There can only ever be one sync incoming. + + RenderThread::isRenderBlock: This variable is true when animations are not + running and the render thread has gone to sleep, waiting for more to do. + + RenderThread::isExternalUpdatePending: This variable is set to false during + the sync phase in the GUI thread and to true in maybeUpdate(). It is an + indication to the render thread that another render pass needs to take + place, rather than the render thread going to sleep after completing its swap. + + RenderThread::doGrab: This variable is set by the grab() function and + tells the renderer to do a grab after rendering is complete and before + swapping happens. + + RenderThread::shouldExit: This variable is used to determine if the render + thread should do a nother pass. It is typically set as a result of show() + and unset as a result of hide() or during shutdown() + + RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown + after shouldExit has been set to true. + */ + // #define FOCUS_DEBUG // #define MOUSE_DEBUG // #define TOUCH_DEBUG @@ -106,41 +163,6 @@ QSGRootItem::QSGRootItem() { } -void QSGCanvasPrivate::stopRenderingThread() -{ - if (thread->isRunning()) { - mutex.lock(); - exitThread = true; - wait.wakeOne(); - wait.wait(&mutex); - exitThread = false; - mutex.unlock(); - thread->wait(); - } -} - -void QSGCanvasPrivate::_q_animationStarted() -{ -#ifdef THREAD_DEBUG - qWarning("AnimationDriver: Main Thread: started"); -#endif - mutex.lock(); - animationRunning = true; - if (idle) - wait.wakeOne(); - mutex.unlock(); -} - -void QSGCanvasPrivate::_q_animationStopped() -{ -#ifdef THREAD_DEBUG - qWarning("AnimationDriver: Main Thread: stopped"); -#endif - mutex.lock(); - animationRunning = false; - mutex.unlock(); -} - void QSGCanvas::paintEvent(QPaintEvent *) { Q_D(QSGCanvas); @@ -180,7 +202,9 @@ void QSGCanvas::paintEvent(QPaintEvent *) int syncTime = frameTimer.elapsed(); #endif - d->renderSceneGraph(); + d->renderSceneGraph(d->widgetSize); + + swapBuffers(); #ifdef FRAME_TIMING printf("FrameTimes, last=%d, animations=%d, polish=%d, makeCurrent=%d, sync=%d, sgrender=%d, readback=%d, total=%d\n", @@ -198,6 +222,11 @@ void QSGCanvas::paintEvent(QPaintEvent *) if (d->animationDriver->isRunning()) update(); + } else { + if (isUpdatesEnabled()) { + d->thread->paint(); + setUpdatesEnabled(false); + } } } @@ -205,10 +234,7 @@ void QSGCanvas::resizeEvent(QResizeEvent *e) { Q_D(QSGCanvas); if (d->threadedRendering) { - d->mutex.lock(); - QGLWidget::resizeEvent(e); - d->widgetSize = e->size(); - d->mutex.unlock(); + d->thread->resize(e->size()); } else { d->widgetSize = e->size(); d->viewportSize = d->widgetSize; @@ -222,29 +248,27 @@ void QSGCanvas::showEvent(QShowEvent *e) QGLWidget::showEvent(e); - if (d->threadedRendering) { - d->contextInThread = true; - doneCurrent(); - if (!d->animationDriver) { - d->animationDriver = d->context->createAnimationDriver(this); - connect(d->animationDriver, SIGNAL(started()), this, SLOT(_q_animationStarted()), Qt::DirectConnection); - connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(_q_animationStopped()), Qt::DirectConnection); - } - d->animationDriver->install(); - d->mutex.lock(); - d->thread->start(); - d->wait.wait(&d->mutex); - d->mutex.unlock(); - } else { - makeCurrent(); + if (!d->contextFailed) { + if (d->threadedRendering) { + if (!d->animationDriver) { + d->animationDriver = d->context->createAnimationDriver(this); + connect(d->animationDriver, SIGNAL(started()), d->thread, SLOT(animationStarted()), Qt::DirectConnection); + connect(d->animationDriver, SIGNAL(stopped()), d->thread, SLOT(animationStopped()), Qt::DirectConnection); + } + d->animationDriver->install(); + d->thread->startRenderThread(); + setUpdatesEnabled(true); + } else { + makeCurrent(); - if (!d->context || !d->context->isReady()) { - d->initializeSceneGraph(); - d->animationDriver = d->context->createAnimationDriver(this); - connect(d->animationDriver, SIGNAL(started()), this, SLOT(update())); - } + if (!d->context || !d->context->isReady()) { + d->initializeSceneGraph(); + d->animationDriver = d->context->createAnimationDriver(this); + connect(d->animationDriver, SIGNAL(started()), this, SLOT(update())); + } - d->animationDriver->install(); + d->animationDriver->install(); + } } } @@ -252,10 +276,13 @@ void QSGCanvas::hideEvent(QHideEvent *e) { Q_D(QSGCanvas); - if (d->threadedRendering) - d->stopRenderingThread(); + if (!d->contextFailed) { + if (d->threadedRendering) { + d->thread->stopRenderThread(); + } - d->animationDriver->uninstall(); + d->animationDriver->uninstall(); + } QGLWidget::hideEvent(e); } @@ -312,18 +339,14 @@ void QSGCanvasPrivate::polishItems() void QSGCanvasPrivate::syncSceneGraph() { - inSync = true; updateDirtyNodes(); - inSync = false; } -void QSGCanvasPrivate::renderSceneGraph() +void QSGCanvasPrivate::renderSceneGraph(const QSize &size) { - QGLContext *glctx = const_cast<QGLContext *>(QGLContext::currentContext()); - - context->renderer()->setDeviceRect(QRect(QPoint(0, 0), viewportSize)); - context->renderer()->setViewportRect(QRect(QPoint(0, 0), viewportSize)); + context->renderer()->setDeviceRect(QRect(QPoint(0, 0), size)); + context->renderer()->setViewportRect(QRect(QPoint(0, 0), size)); context->renderer()->setProjectMatrixToDeviceRect(); context->renderNextFrame(); @@ -332,127 +355,22 @@ void QSGCanvasPrivate::renderSceneGraph() sceneGraphRenderTime = frameTimer.elapsed(); #endif - #ifdef FRAME_TIMING // int pixel; // glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); readbackTime = frameTimer.elapsed(); #endif - - glctx->swapBuffers(); } +// ### Do we need this? void QSGCanvas::sceneGraphChanged() { - Q_D(QSGCanvas); - d->needsRepaint = true; +// Q_D(QSGCanvas); +// d->needsRepaint = true; } -void QSGCanvasPrivate::runThread() -{ -#ifdef THREAD_DEBUG - qWarning("QSGRenderer: Render thread running"); -#endif - Q_Q(QSGCanvas); - - printf("QSGCanvas::runThread(), rendering in a thread...\n"); - - q->makeCurrent(); - initializeSceneGraph(); - - QObject::connect(context->renderer(), SIGNAL(sceneGraphChanged()), - q, SLOT(sceneGraphChanged()), - Qt::DirectConnection); - - mutex.lock(); - wait.wakeOne(); // Wake the main thread waiting for us to start - - while (true) { - QSize s; - s = widgetSize; - - if (exitThread) - break; - - if (s != viewportSize) { - glViewport(0, 0, s.width(), s.height()); - viewportSize = s; - } - -#ifdef THREAD_DEBUG - qWarning("QSGRenderer: Render Thread: Waiting for main thread to stop"); -#endif - QCoreApplication::postEvent(q, new QEvent(QEvent::User)); - wait.wait(&mutex); - - if (exitThread) { -#ifdef THREAD_DEBUG - qWarning("QSGRenderer: Render Thread: Shutting down..."); -#endif - break; - } - -#ifdef THREAD_DEBUG - qWarning("QSGRenderer: Render Thread: Main thread has stopped, syncing scene"); -#endif - - // Do processing while main thread is frozen - syncSceneGraph(); - -#ifdef THREAD_DEBUG - qWarning("QSGRenderer: Render Thread: Resuming main thread"); -#endif - - // Read animationRunning while inside the locked section - bool continous = animationRunning; - - wait.wakeOne(); - mutex.unlock(); - - bool enterIdle = false; - if (needsRepaint) { -#ifdef THREAD_DEBUG - qWarning("QSGRenderer: Render Thread: rendering scene"); -#endif - renderSceneGraph(); - needsRepaint = false; - } else if (continous) { -#ifdef THREAD_DEBUG - qWarning("QSGRenderer: Render Thread: waiting a while..."); -#endif - MyThread::doWait(); - } else { - enterIdle = true; - } - - mutex.lock(); - - if (enterIdle) { -#ifdef THREAD_DEBUG - qWarning("QSGRenderer: Render Thread: Nothing has changed, going idle..."); -#endif - idle = true; - wait.wait(&mutex); - idle = false; -#ifdef THREAD_DEBUG - qWarning("QSGRenderer: Render Thread: waking up from idle"); -#endif - } - - } - - -#ifdef THREAD_DEBUG - qWarning("QSGRenderer: Render Thread: shutting down, waking up main thread"); -#endif - wait.wakeOne(); - mutex.unlock(); - - q->doneCurrent(); -} - QSGCanvasPrivate::QSGCanvasPrivate() : rootItem(0) , activeFocusItem(0) @@ -460,15 +378,11 @@ QSGCanvasPrivate::QSGCanvasPrivate() , hoverItem(0) , dirtyItemList(0) , context(0) - , contextInThread(false) + , contextFailed(false) , threadedRendering(false) - , exitThread(false) , animationRunning(false) - , idle(false) - , needsRepaint(true) , renderThreadAwakened(false) - , inSync(false) - , thread(new MyThread(this)) + , thread(0) , animationDriver(0) { threadedRendering = !qmlNoThreadedRenderer(); @@ -482,6 +396,11 @@ void QSGCanvasPrivate::init(QSGCanvas *c) { QUnifiedTimer::instance(true)->setConsistentTiming(qmlFixedAnimationStep()); + if (!c->context() || !c->context()->isValid()) { + contextFailed = true; + qWarning("QSGCanvas: Couldn't acquire a GL context."); + } + q_ptr = c; Q_Q(QSGCanvas); @@ -495,6 +414,13 @@ void QSGCanvasPrivate::init(QSGCanvas *c) rootItemPrivate->flags |= QSGItem::ItemIsFocusScope; context = QSGContext::createDefaultContext(); + + if (threadedRendering) { + thread = new QSGCanvasRenderThread; + thread->renderer = q; + thread->d = this; + } + } void QSGCanvasPrivate::sceneMouseEventForTransform(QGraphicsSceneMouseEvent &sceneEvent, @@ -945,9 +871,10 @@ QSGCanvas::~QSGCanvas() { Q_D(QSGCanvas); - if (d->threadedRendering) { - d->stopRenderingThread(); + if (d->threadedRendering && d->thread->isRunning()) { + d->thread->stopRenderThread(); delete d->thread; + d->thread = 0; } // ### should we change ~QSGItem to handle this better? @@ -960,18 +887,20 @@ QSGCanvas::~QSGCanvas() d->cleanupNodes(); - // We need to remove all references to textures pointing to "our" QSGContext - // from the QDeclarativePixmapCache. Call into the cache to remove the GL / Scene Graph - // part of those cache entries. - // To "play nice" with other GL apps that are potentially running in the GUI thread, - // We get the current context and only temporarily make our own current - QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext()); - makeCurrent(); - extern void qt_declarative_pixmapstore_clean(QSGContext *context); - qt_declarative_pixmapstore_clean(d->context); - delete d->context; - if (currentContext) - currentContext->makeCurrent(); + if (!d->contextFailed) { + // We need to remove all references to textures pointing to "our" QSGContext + // from the QDeclarativePixmapCache. Call into the cache to remove the GL / Scene Graph + // part of those cache entries. + // To "play nice" with other GL apps that are potentially running in the GUI thread, + // We get the current context and only temporarily make our own current + QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext()); + makeCurrent(); + extern void qt_declarative_pixmapstore_clean(QSGContext *context); + qt_declarative_pixmapstore_clean(d->context); + delete d->context; + if (currentContext) + currentContext->makeCurrent(); + } } QSGItem *QSGCanvas::rootItem() const @@ -1022,31 +951,24 @@ bool QSGCanvas::event(QEvent *e) Q_D(QSGCanvas); if (e->type() == QEvent::User) { - Q_ASSERT(d->threadedRendering); + if (!d->thread->syncAlreadyHappened) + d->thread->sync(false); + + d->thread->syncAlreadyHappened = false; - d->mutex.lock(); + if (d->animationRunning) { #ifdef THREAD_DEBUG - qWarning("QSGRenderer: Main Thread: Stopped"); + qDebug("GUI: Advancing animations...\n"); #endif - d->polishItems(); - - d->renderThreadAwakened = false; - - d->wait.wakeOne(); + d->animationDriver->advance(); - // The thread is exited when the widget has been hidden. We then need to - // skip the waiting, otherwise we would be waiting for a wakeup that never - // comes. - if (d->thread->isRunning()) - d->wait.wait(&d->mutex); #ifdef THREAD_DEBUG - qWarning("QSGRenderer: Main Thread: Resumed"); + qDebug("GUI: Animations advanced...\n"); #endif - d->mutex.unlock(); + } - if (d->animationRunning) - d->animationDriver->advance(); + return true; } switch (e->type()) { @@ -1881,24 +1803,17 @@ void QSGCanvas::maybeUpdate() { Q_D(QSGCanvas); - if (d->threadedRendering) { + if (d->threadedRendering && d->thread && d->thread->isRunning()) { if (!d->renderThreadAwakened) { - d->renderThreadAwakened = true; - bool locked = d->mutex.tryLock(); - if (d->idle && locked) { #ifdef THREAD_DEBUG - qWarning("QSGRenderer: now maybe I should update..."); + printf("GUI: doing update...\n"); #endif - d->wait.wakeOne(); - } else if (d->inSync) { - // If we are in sync (on scene graph thread) someone has explicitely asked us - // to redraw, hence we tell the render loop to not go idle. - // The primary usecase for this is updatePaintNode() calling update() without - // changing the scene graph. - d->needsRepaint = true; - } - if (locked) - d->mutex.unlock(); + d->renderThreadAwakened = true; + d->thread->lockInGui(); + d->thread->isExternalUpdatePending = true; + if (d->thread->isRenderBlocked) + d->thread->wake(); + d->thread->unlockInGui(); } } else if (!d->animationDriver || !d->animationDriver->isRunning()) { update(); @@ -1929,6 +1844,373 @@ QSGEngine *QSGCanvas::sceneGraphEngine() const return 0; } + +/*! + Grabs the contents of the framebuffer and returns it as an image. + + This function might not work if the view is not visible. + + \warning Calling this function will cause performance problems. + */ +QImage QSGCanvas::grabFrameBuffer() +{ + Q_D(QSGCanvas); + if (d->threadedRendering) + return d->thread ? d->thread->grab() : QImage(); + else { + // render a fresh copy of the scene graph in the current thread. + d->renderSceneGraph(size()); + return QGLWidget::grabFrameBuffer(false); + } +} + + +void QSGCanvasRenderThread::run() +{ + qDebug("QML Rendering Thread Started"); + + renderer->makeCurrent(); + + if (!d->context->isReady()) + d->initializeSceneGraph(); + + + while (!shouldExit) { + lock(); + + bool sizeChanged = false; + isExternalUpdatePending = false; + + if (renderedSize != windowSize) { +#ifdef THREAD_DEBUG + printf(" RenderThread: window has changed size...\n"); +#endif + glViewport(0, 0, windowSize.width(), windowSize.height()); + sizeChanged = true; + } + +#ifdef THREAD_DEBUG + printf(" RenderThread: preparing to sync...\n"); +#endif + + if (!isGuiBlocked) { + isGuiBlockPending = true; + +#ifdef THREAD_DEBUG + printf(" RenderThread: aquired sync lock...\n"); +#endif + QApplication::postEvent(renderer, new QEvent(QEvent::User)); +#ifdef THREAD_DEBUG + printf(" RenderThread: going to sleep...\n"); +#endif + wait(); + + isGuiBlockPending = false; + } + +#ifdef THREAD_DEBUG + printf(" RenderThread: Doing locked sync\n"); +#endif + d->syncSceneGraph(); + + // Wake GUI after sync to let it continue animating and event processing. + wake(); + unlock(); +#ifdef THREAD_DEBUG + printf(" RenderThread: sync done\n"); +#endif + + + +#ifdef THREAD_DEBUG + printf(" RenderThread: rendering... %d x %d\n", windowSize.width(), windowSize.height()); +#endif + + d->renderSceneGraph(windowSize); + + // The content of the target buffer is undefined after swap() so grab needs + // to happen before swap(); + if (doGrab) { +#ifdef THREAD_DEBUG + printf(" RenderThread: doing a grab...\n"); +#endif + grabContent = qt_gl_read_framebuffer(windowSize, false, false); + doGrab = false; + } + +#ifdef THREAD_DEBUG + printf(" RenderThread: wait for swap...\n"); +#endif + + renderer->swapBuffers(); +#ifdef THREAD_DEBUG + printf(" RenderThread: swap complete...\n"); +#endif + + lock(); + isPaintCompleted = true; + if (sizeChanged) + renderedSize = windowSize; + + // Wake the GUI thread now that rendering is complete, to signal that painting + // is done, resizing is done or grabbing is completed. For grabbing, we're + // signalling this much later than needed (we could have done it before swap) + // but we don't want to lock an extra time. + wake(); + + if (!d->animationRunning && !isExternalUpdatePending) { +#ifdef THREAD_DEBUG + printf(" RenderThread: nothing to do, going to sleep...\n"); +#endif + isRenderBlocked = true; + wait(); + isRenderBlocked = false; + } + + unlock(); + } + +#ifdef THREAD_DEBUG + printf(" RenderThread: exited... Good Night!\n"); +#endif + + renderer->doneCurrent(); + + lock(); + hasExited = true; +#ifdef THREAD_DEBUG + printf(" RenderThread: waking GUI for final sleep..\n"); +#endif + wake(); + unlock(); +} + + + +void QSGCanvasRenderThread::exhaustSyncEvent() +{ + if (isGuiBlockPending) { + sync(true); + syncAlreadyHappened = true; + } +} + + + +void QSGCanvasRenderThread::sync(bool guiAlreadyLocked) +{ +#ifdef THREAD_DEBUG + printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event"); +#endif + Q_ASSERT(d->threadedRendering); + + if (!guiAlreadyLocked) + d->thread->lockInGui(); + + d->renderThreadAwakened = false; + + d->polishItems(); + + d->thread->wake(); + d->thread->wait(); + + if (!guiAlreadyLocked) + d->thread->unlockInGui(); +} + + + + +/*! + Acquires the mutex for the GUI thread. The function uses the isGuiBlocked + variable to keep track of how many recursion levels the gui is locket with. + We only actually acquire the mutex for the first level to avoid deadlocking + ourselves. + */ + +void QSGCanvasRenderThread::lockInGui() +{ + // We must avoid recursive locking in the GUI thread, hence we + // only lock when we are the first one to try to block. + if (!isGuiBlocked) + lock(); + + isGuiBlocked++; + +#ifdef THREAD_DEBUG + printf("GUI: aquired lock... %d\n", isGuiBlocked); +#endif +} + + + +void QSGCanvasRenderThread::unlockInGui() +{ +#ifdef THREAD_DEBUG + printf("GUI: releasing lock... %d\n", isGuiBlocked); +#endif + --isGuiBlocked; + if (!isGuiBlocked) + unlock(); +} + + + + +void QSGCanvasRenderThread::animationStarted() +{ +#ifdef THREAD_DEBUG + printf("GUI: animationStarted()\n"); +#endif + + lockInGui(); + + d->animationRunning = true; + + if (isRenderBlocked) + wake(); + + unlockInGui(); +} + + + +void QSGCanvasRenderThread::animationStopped() +{ +#ifdef THREAD_DEBUG + printf("GUI: animationStopped()...\n"); +#endif + + lockInGui(); + d->animationRunning = false; + unlockInGui(); +} + + +void QSGCanvasRenderThread::paint() +{ +#ifdef THREAD_DEBUG + printf("GUI: paint called..\n"); +#endif + + lockInGui(); + exhaustSyncEvent(); + + isPaintCompleted = false; + while (isRunning() && !isPaintCompleted) { + if (isRenderBlocked) + wake(); + wait(); + } + unlockInGui(); + + // paint is only called for the inital show. After that we will do all + // drawing ourselves, so block future updates.. + renderer->setUpdatesEnabled(false); +} + + + +void QSGCanvasRenderThread::resize(const QSize &size) +{ +#ifdef THREAD_DEBUG + printf("GUI: Resize Event: %dx%d\n", size.width(), size.height()); +#endif + + if (!isRunning()) { + windowSize = size; + return; + } + + lockInGui(); + exhaustSyncEvent(); + + windowSize = size; + + while (isRunning() && renderedSize != windowSize) { + if (isRenderBlocked) + wake(); + wait(); + } + unlockInGui(); +} + + + +void QSGCanvasRenderThread::startRenderThread() +{ +#ifdef THREAD_DEBUG + printf("GUI: Starting Render Thread\n"); +#endif + hasExited = false; + shouldExit = false; + isGuiBlocked = 0; + isGuiBlockPending = false; + + renderer->doneCurrent(); + start(); +} + + + +void QSGCanvasRenderThread::stopRenderThread() +{ +#ifdef THREAD_DEBUG + printf("GUI: stopping render thread\n"); +#endif + + lockInGui(); + exhaustSyncEvent(); + shouldExit = true; + + if (isRenderBlocked) { +#ifdef THREAD_DEBUG + printf("GUI: waking up render thread\n"); +#endif + wake(); + } + + while (!hasExited) { +#ifdef THREAD_DEBUG + printf("GUI: waiting for render thread to have exited..\n"); +#endif + wait(); + } + + unlockInGui(); +} + + + +QImage QSGCanvasRenderThread::grab() +{ + if (!isRunning()) + return QImage(); + +#ifdef THREAD_DEBUG + printf("GUI: doing a pixelwise grab..\n"); +#endif + + lockInGui(); + exhaustSyncEvent(); + + doGrab = true; + isPaintCompleted = false; + while (isRunning() && !isPaintCompleted) { + if (!isRenderBlocked) + wake(); + wait(); + } + + QImage grabbed = grabContent; + grabContent = QImage(); + + unlockInGui(); + + return grabbed; +} + + #include "moc_qsgcanvas.cpp" QT_END_NAMESPACE diff --git a/src/declarative/items/qsgcanvas.h b/src/declarative/items/qsgcanvas.h index 6707d24b30..d0d0c79d5e 100644 --- a/src/declarative/items/qsgcanvas.h +++ b/src/declarative/items/qsgcanvas.h @@ -75,6 +75,8 @@ public: QSGEngine *sceneGraphEngine() const; + QImage grabFrameBuffer(); + Q_SIGNALS: void sceneGraphInitialized(); @@ -109,8 +111,6 @@ private Q_SLOTS: private: Q_DISABLE_COPY(QSGCanvas) - Q_PRIVATE_SLOT(d_func(), void _q_animationStarted()) - Q_PRIVATE_SLOT(d_func(), void _q_animationStopped()) }; QT_END_NAMESPACE diff --git a/src/declarative/items/qsgcanvas_p.h b/src/declarative/items/qsgcanvas_p.h index 6b8034f922..87fae1c31a 100644 --- a/src/declarative/items/qsgcanvas_p.h +++ b/src/declarative/items/qsgcanvas_p.h @@ -78,6 +78,8 @@ public: class QSGCanvasPrivate; class QTouchEvent; +class QSGCanvasRenderThread; + class QSGCanvasPrivate : public QGLWidgetPrivate { public: @@ -115,8 +117,6 @@ public: void sendHoverEvent(QEvent::Type, QSGItem *, QGraphicsSceneHoverEvent *); void clearHover(); - void stopRenderingThread(); - QDeclarativeGuard<QSGItem> hoverItem; enum FocusOption { DontChangeFocusProperty = 0x01, @@ -135,11 +135,7 @@ public: void initializeSceneGraph(); void polishItems(); void syncSceneGraph(); - void renderSceneGraph(); - void runThread(); - - void _q_animationStarted(); - void _q_animationStopped(); + void renderSceneGraph(const QSize &size); QSGItem::UpdatePaintNodeData updatePaintNodeData; @@ -156,24 +152,12 @@ public: QSGContext *context; - uint contextInThread : 1; + uint contextFailed : 1; uint threadedRendering : 1; - uint exitThread : 1; uint animationRunning: 1; - uint idle : 1; // Set to true when render thread sees no change and enters a wait() - uint needsRepaint : 1; // Set by callback from render if scene needs repainting. uint renderThreadAwakened : 1; - uint inSync: 1; - struct MyThread : public QThread { - MyThread(QSGCanvasPrivate *r) : renderer(r) {} - virtual void run() { renderer->runThread(); } - static void doWait() { QThread::msleep(16); } - QSGCanvasPrivate *renderer; - }; - MyThread *thread; - QMutex mutex; - QWaitCondition wait; + QSGCanvasRenderThread *thread; QSize widgetSize; QSize viewportSize; @@ -182,6 +166,73 @@ public: QHash<int, QSGItem *> itemForTouchPointId; }; + + +class QSGCanvasRenderThread : public QThread +{ + Q_OBJECT +public: + QSGCanvasRenderThread() + : mutex(QMutex::NonRecursive) + , isGuiBlocked(0) + , isPaintCompleted(false) + , isGuiBlockPending(false) + , isRenderBlocked(false) + , isExternalUpdatePending(false) + , syncAlreadyHappened(false) + , doGrab(false) + , shouldExit(false) + , hasExited(false) + {} + + inline void lock() { mutex.lock(); } + inline void unlock() { mutex.unlock(); } + inline void wait() { condition.wait(&mutex); } + inline void wake() { condition.wakeOne(); } + + void lockInGui(); + void unlockInGui(); + + void paint(); + void resize(const QSize &size); + void startRenderThread(); + void stopRenderThread(); + void exhaustSyncEvent(); + void sync(bool guiAlreadyLocked); + + QImage grab(); + +public slots: + void animationStarted(); + void animationStopped(); + +public: + QMutex mutex; + QWaitCondition condition; + + QSize windowSize; + QSize renderedSize; + + QSGCanvas *renderer; + QSGCanvasPrivate *d; + + int isGuiBlocked; + uint isPaintCompleted : 1; + uint isGuiBlockPending : 1; + uint isRenderBlocked : 1; + uint isExternalUpdatePending : 1; + uint syncAlreadyHappened : 1; + + uint doGrab : 1; + uint shouldExit : 1; + uint hasExited : 1; + + QImage grabContent; + + void run(); +}; + + Q_DECLARE_OPERATORS_FOR_FLAGS(QSGCanvasPrivate::FocusOptions) QT_END_NAMESPACE diff --git a/src/declarative/items/qsgcanvasitem.cpp b/src/declarative/items/qsgcanvasitem.cpp index 2ceb118465..eddaf955a3 100644 --- a/src/declarative/items/qsgcanvasitem.cpp +++ b/src/declarative/items/qsgcanvasitem.cpp @@ -117,7 +117,6 @@ QDeclarativeV8Handle QSGCanvasItem::getContext(const QString &contextId) void QSGCanvasItem::requestPaint() { - //Q_D(QSGCanvasItem); //TODO:update(d->context->dirtyRect()); update(); } diff --git a/src/declarative/items/qsgitemsmodule.cpp b/src/declarative/items/qsgitemsmodule.cpp index 6ea20bb38b..a29776fc68 100644 --- a/src/declarative/items/qsgitemsmodule.cpp +++ b/src/declarative/items/qsgitemsmodule.cpp @@ -75,6 +75,8 @@ //#include "private/qsgpincharea_p.h" #include "qsgcanvasitem_p.h" #include "qsgcontext2d_p.h" +#include "qsgsprite_p.h" +#include "qsgspriteimage_p.h" static QDeclarativePrivate::AutoParentResult qsgitem_autoParent(QObject *obj, QObject *parent) { @@ -179,6 +181,8 @@ static void qt_sgitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QSGContext2D>(); qmlRegisterType<QSGCanvasGradient>(); + qmlRegisterType<QSGSprite>("QtQuick", 2, 0, "Sprite"); + qmlRegisterType<QSGSpriteImage>("QtQuick", 2, 0, "SpriteImage"); qmlRegisterType<QSGParentChange>(uri, major, minor,"ParentChange"); qmlRegisterType<QSGAnchorChanges>(uri, major, minor,"AnchorChanges"); diff --git a/src/declarative/items/qsgsprite.cpp b/src/declarative/items/qsgsprite.cpp new file mode 100644 index 0000000000..694976a540 --- /dev/null +++ b/src/declarative/items/qsgsprite.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsprite_p.h" + +QT_BEGIN_NAMESPACE + +QSGSprite::QSGSprite(QObject *parent) : + QObject(parent) + , m_generatedCount(0) + , m_framesPerRow(0) + , m_frames(1) + , m_frameHeight(0) + , m_frameWidth(0) + , m_duration(1000) +{ +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qsgsprite_p.h b/src/declarative/items/qsgsprite_p.h new file mode 100644 index 0000000000..652a4cd482 --- /dev/null +++ b/src/declarative/items/qsgsprite_p.h @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SPRITESTATE_H +#define SPRITESTATE_H + +#include <QObject> +#include <QUrl> +#include <QVariantMap> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGSprite : public QObject +{ + Q_OBJECT + 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) + Q_PROPERTY(QVariantMap to READ to WRITE setTo NOTIFY toChanged) + +public: + explicit QSGSprite(QObject *parent = 0); + + QUrl source() const + { + return m_source; + } + + int frames() const + { + return m_frames; + } + + int frameHeight() const + { + return m_frameHeight; + } + + int frameWidth() const + { + return m_frameWidth; + } + + int duration() const + { + return m_duration; + } + + QString name() const + { + return m_name; + } + + QVariantMap to() const + { + return m_to; + } + + qreal speedModifer() const + { + return m_speedModifier; + } + + int durationVariance() const + { + return m_durationVariance; + } + +signals: + + void sourceChanged(QUrl arg); + + void framesChanged(int arg); + + void frameHeightChanged(int arg); + + void frameWidthChanged(int arg); + + void durationChanged(int arg); + + void nameChanged(QString arg); + + void toChanged(QVariantMap arg); + + void speedModifierChanged(qreal arg); + + void durationVarianceChanged(int arg); + +public slots: + + void setSource(QUrl arg) + { + if (m_source != arg) { + m_source = arg; + emit sourceChanged(arg); + } + } + + void setFrames(int arg) + { + if (m_frames != arg) { + m_frames = arg; + emit framesChanged(arg); + } + } + + 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) { + m_duration = arg; + emit durationChanged(arg); + } + } + + void setName(QString arg) + { + if (m_name != arg) { + m_name = arg; + emit nameChanged(arg); + } + } + + void setTo(QVariantMap arg) + { + if (m_to != arg) { + m_to = arg; + emit toChanged(arg); + } + } + + void setSpeedModifier(qreal arg) + { + if (m_speedModifier != arg) { + m_speedModifier = arg; + emit speedModifierChanged(arg); + } + } + + void setDurationVariance(int arg) + { + if (m_durationVariance != arg) { + m_durationVariance = arg; + emit durationVarianceChanged(arg); + } + } + +private: + friend class QSGImageParticle; + friend class QSGSpriteEngine; + 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; + qreal m_speedModifier; + int m_durationVariance; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // SPRITESTATE_H diff --git a/src/declarative/items/qsgspriteengine.cpp b/src/declarative/items/qsgspriteengine.cpp new file mode 100644 index 0000000000..27de0d94f6 --- /dev/null +++ b/src/declarative/items/qsgspriteengine.cpp @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgspriteengine_p.h" +#include "qsgsprite_p.h" +#include <QDebug> +#include <QPainter> +#include <QSet> +#include <QtOpenGL> + +QT_BEGIN_NAMESPACE + +QSGSpriteEngine::QSGSpriteEngine(QObject *parent) : + QObject(parent), m_timeOffset(0) +{ + //Default size 1 + setCount(1); + m_advanceTime.start(); +} + +QSGSpriteEngine::QSGSpriteEngine(QList<QSGSprite*> states, QObject *parent) : + QObject(parent), m_states(states), m_timeOffset(0) +{ + //Default size 1 + setCount(1); + m_advanceTime.start(); +} + +QSGSpriteEngine::~QSGSpriteEngine() +{ +} + +int QSGSpriteEngine::maxFrames() +{ + 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 QSGSpriteEngine::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 QSGSpriteEngine::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 QSGSpriteEngine::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 QSGSpriteEngine::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 QSGSpriteEngine::spriteCount()//TODO: Actually image state count, need to rename these things to make sense together +{ + return m_imageStateCount; +} + +void QSGSpriteEngine::setGoal(int state, int sprite, bool jump) +{ + if(sprite >= m_sprites.count() || state >= m_states.count()) + return; + if(!jump){ + m_goals[sprite] = state; + return; + } + + if(m_sprites[sprite] == state) + return;//Already there + m_sprites[sprite] = state; + m_goals[sprite] = -1; + restartSprite(sprite); + return; +} + +QImage QSGSpriteEngine::assembledImage() +{ + int frameHeight = 0; + int frameWidth = 0; + m_maxFrames = 0; + m_imageStateCount = 0; + + int maxSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize); + + foreach(QSGSprite* state, m_states){ + if(state->frames() > m_maxFrames) + m_maxFrames = state->frames(); + + QImage img(state->source().toLocalFile()); + if (img.isNull()) { + qWarning() << "SpriteEngine: loading image failed..." << state->source().toLocalFile(); + 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(imgWidth != frameWidth){ + qWarning() << "SpriteEngine: Irregular frame width..." << state->source().toLocalFile(); + return QImage(); + } + }else{ + frameWidth = imgWidth; + } + + 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->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++; + } + } + + //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(QSGSprite* state, m_states){ + QImage img(state->source().toLocalFile()); + 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; +} + +void QSGSpriteEngine::setCount(int c) +{ + m_sprites.resize(c); + m_goals.resize(c); + m_startTimes.resize(c); +} + +void QSGSpriteEngine::startSprite(int index) +{ + if(index >= m_sprites.count()) + return; + m_sprites[index] = 0; + m_goals[index] = -1; + restartSprite(index); +} + +void QSGSpriteEngine::restartSprite(int index) +{ + m_startTimes[index] = m_timeOffset + m_advanceTime.elapsed(); + int time = m_states[m_sprites[index]]->duration() * m_states[m_sprites[index]]->frames() + m_startTimes[index]; + for(int i=0; i<m_stateUpdates.count(); i++) + m_stateUpdates[i].second.removeAll(index); + addToUpdateList(time, index); +} + +uint QSGSpriteEngine::updateSprites(uint time) +{ + //Sprite State Update; + while(!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){ + foreach(int idx, m_stateUpdates.first().second){ + if(idx >= m_sprites.count()) + continue;//TODO: Proper fix(because this does happen and I'm just ignoring it) + int stateIdx = m_sprites[idx]; + int nextIdx = -1; + int goalPath = goalSeek(stateIdx, idx); + if(goalPath == -1){//Random + qreal r =(qreal) qrand() / (qreal) RAND_MAX; + qreal total = 0.0; + for(QVariantMap::const_iterator iter=m_states[stateIdx]->m_to.constBegin(); + iter!=m_states[stateIdx]->m_to.constEnd(); iter++) + total += (*iter).toReal(); + r*=total; + for(QVariantMap::const_iterator iter= m_states[stateIdx]->m_to.constBegin(); + iter!=m_states[stateIdx]->m_to.constEnd(); iter++){ + if(r < (*iter).toReal()){ + bool superBreak = false; + for(int i=0; i<m_states.count(); i++){ + if(m_states[i]->name() == iter.key()){ + nextIdx = i; + superBreak = true; + break; + } + } + if(superBreak) + break; + } + r -= (*iter).toReal(); + } + }else{//Random out of shortest paths to goal + nextIdx = goalPath; + } + if(nextIdx == -1)//No to states means stay here + nextIdx = stateIdx; + + m_sprites[idx] = nextIdx; + m_startTimes[idx] = time; + //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(); + } + + m_timeOffset = time; + m_advanceTime.start(); + if(m_stateUpdates.isEmpty()) + return -1; + return m_stateUpdates.first().first; +} + +int QSGSpriteEngine::goalSeek(int curIdx, int spriteIdx, int dist) +{ + QString goalName; + if(m_goals[spriteIdx] != -1) + goalName = m_states[m_goals[spriteIdx]]->name(); + else + goalName = m_globalGoal; + if(goalName.isEmpty()) + return -1; + //TODO: caching instead of excessively redoing iterative deepening (which was chosen arbitarily anyways) + // Paraphrased - implement in an *efficient* manner + for(int i=0; i<m_states.count(); i++) + if(m_states[curIdx]->name() == goalName) + return curIdx; + if(dist < 0) + dist = m_states.count(); + QSGSprite* curState = m_states[curIdx]; + for(QVariantMap::const_iterator iter = curState->m_to.constBegin(); + iter!=curState->m_to.constEnd(); iter++){ + if(iter.key() == goalName) + for(int i=0; i<m_states.count(); i++) + if(m_states[i]->name() == goalName) + return i; + } + QSet<int> options; + for(int i=1; i<dist; i++){ + for(QVariantMap::const_iterator iter = curState->m_to.constBegin(); + iter!=curState->m_to.constEnd(); iter++){ + int option = -1; + for(int j=0; j<m_states.count(); j++)//One place that could be a lot more efficient... + if(m_states[j]->name() == iter.key()) + if(goalSeek(j, spriteIdx, i) != -1) + option = j; + if(option != -1) + options << option; + } + if(!options.isEmpty()){ + if(options.count()==1) + return *(options.begin()); + int option = -1; + qreal r =(qreal) qrand() / (qreal) RAND_MAX; + qreal total; + for(QSet<int>::const_iterator iter=options.constBegin(); + iter!=options.constEnd(); iter++) + total += curState->m_to.value(m_states[(*iter)]->name()).toReal(); + r *= total; + for(QVariantMap::const_iterator iter = curState->m_to.constBegin(); + iter!=curState->m_to.constEnd(); iter++){ + bool superContinue = true; + for(int j=0; j<m_states.count(); j++) + if(m_states[j]->name() == iter.key()) + if(options.contains(j)) + superContinue = false; + if(superContinue) + continue; + if(r < (*iter).toReal()){ + bool superBreak = false; + for(int j=0; j<m_states.count(); j++){ + if(m_states[j]->name() == iter.key()){ + option = j; + superBreak = true; + break; + } + } + if(superBreak) + break; + } + r-=(*iter).toReal(); + } + return option; + } + } + return -1; +} + +void QSGSpriteEngine::addToUpdateList(uint t, int idx) +{ + for(int i=0; i<m_stateUpdates.count(); i++){ + if(m_stateUpdates[i].first==t){ + m_stateUpdates[i].second << idx; + return; + }else if(m_stateUpdates[i].first > t){ + QList<int> tmpList; + tmpList << idx; + m_stateUpdates.insert(i, qMakePair(t, tmpList)); + return; + } + } + QList<int> tmpList; + tmpList << idx; + m_stateUpdates << qMakePair(t, tmpList); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qsgspriteengine_p.h b/src/declarative/items/qsgspriteengine_p.h new file mode 100644 index 0000000000..8ab6e3a30a --- /dev/null +++ b/src/declarative/items/qsgspriteengine_p.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SPRITEENGINE_H +#define SPRITEENGINE_H + +#include <QObject> +#include <QVector> +#include <QTimer> +#include <QTime> +#include <QList> +#include <QDeclarativeListProperty> +#include <QImage> +#include <QPair> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGSprite; + +class QSGSpriteEngine : public QObject +{ + Q_OBJECT + //TODO: Optimize single sprite case + Q_PROPERTY(QDeclarativeListProperty<QSGSprite> sprites READ sprites) + Q_PROPERTY(QString globalGoal READ globalGoal WRITE setGlobalGoal NOTIFY globalGoalChanged) +public: + explicit QSGSpriteEngine(QObject *parent = 0); + QSGSpriteEngine(QList<QSGSprite*> sprites, QObject *parent=0); + ~QSGSpriteEngine(); + + QDeclarativeListProperty<QSGSprite> sprites() + { + return QDeclarativeListProperty<QSGSprite>(this, m_states); + } + QString globalGoal() const + { + return m_globalGoal; + } + + 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 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); + QImage assembledImage(); + + void startSprite(int index=0); + +private://Nothing outside should use this? + friend class QSGSpriteGoalAffector;//XXX: Fix interface + int stateCount() {return m_states.count();} + int stateIndex(QSGSprite* s){return m_states.indexOf(s);}//TODO: Does this need to be hidden? + QSGSprite* state(int idx){return m_states[idx];}//Used by spritegoal affector +signals: + + void globalGoalChanged(QString arg); + +public slots: + void setGlobalGoal(QString arg) + { + if (m_globalGoal != arg) { + m_globalGoal = arg; + emit globalGoalChanged(arg); + } + } + + uint updateSprites(uint time); + +private: + void restartSprite(int sprite); + void addToUpdateList(uint t, int idx); + int goalSeek(int curState, int spriteIdx, int dist=-1); + QList<QSGSprite*> m_states; + QVector<int> m_sprites;//int is the index in m_states of the current state + QVector<int> m_goals; + QVector<int> m_startTimes; + QList<QPair<uint, QList<int> > > m_stateUpdates;//### This could be done faster + + QTime m_advanceTime; + 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 +inline void spriteAppend(QDeclarativeListProperty<QSGSprite> *p, QSGSprite* s) +{ + reinterpret_cast<QList<QSGSprite *> *>(p->data)->append(s); + p->object->metaObject()->invokeMethod(p->object, "createEngine"); +} + +inline QSGSprite* spriteAt(QDeclarativeListProperty<QSGSprite> *p, int idx) +{ + return reinterpret_cast<QList<QSGSprite *> *>(p->data)->at(idx); +} + +inline void spriteClear(QDeclarativeListProperty<QSGSprite> *p) +{ + reinterpret_cast<QList<QSGSprite *> *>(p->data)->clear(); + p->object->metaObject()->invokeMethod(p->object, "createEngine"); +} + +inline int spriteCount(QDeclarativeListProperty<QSGSprite> *p) +{ + return reinterpret_cast<QList<QSGSprite *> *>(p->data)->count(); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // SPRITEENGINE_H diff --git a/src/declarative/items/qsgspriteimage.cpp b/src/declarative/items/qsgspriteimage.cpp new file mode 100644 index 0000000000..8cc0dc5b76 --- /dev/null +++ b/src/declarative/items/qsgspriteimage.cpp @@ -0,0 +1,353 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgspriteimage_p.h" +#include "qsgsprite_p.h" +#include "qsgspriteengine_p.h" +#include <private/qsgcontext_p.h> +#include <private/qsgadaptationlayer_p.h> +#include <qsgnode.h> +#include <qsgengine.h> +#include <qsgtexturematerial.h> +#include <qsgtexture.h> +#include <QFile> +#include <cmath> +#include <qmath.h> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +class QSGSpriteMaterial : public QSGMaterial +{ +public: + QSGSpriteMaterial(); + virtual ~QSGSpriteMaterial(); + virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } + virtual QSGMaterialShader *createShader() const; + virtual int compare(const QSGMaterial *other) const + { + return this - static_cast<const QSGSpriteMaterial *>(other); + } + + QSGTexture *texture; + + qreal timestamp; + qreal timelength; + int framecount; + int animcount; + int width; + int height; +}; + +QSGSpriteMaterial::QSGSpriteMaterial() + : timestamp(0) + , timelength(1) + , framecount(1) + , animcount(1) + , width(0) + , height(0) +{ + setFlag(Blending, true); +} + +QSGSpriteMaterial::~QSGSpriteMaterial() +{ + delete texture; +} + +class SpriteMaterialData : public QSGMaterialShader +{ +public: + SpriteMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) + { + QFile vf(vertexFile ? vertexFile : ":defaultshaders/spriteimagevertex.shader"); + vf.open(QFile::ReadOnly); + m_vertex_code = vf.readAll(); + + QFile ff(fragmentFile ? fragmentFile : ":defaultshaders/spriteimagefragment.shader"); + ff.open(QFile::ReadOnly); + m_fragment_code = ff.readAll(); + + Q_ASSERT(!m_vertex_code.isNull()); + Q_ASSERT(!m_fragment_code.isNull()); + } + + void deactivate() { + QSGMaterialShader::deactivate(); + + for (int i=0; i<8; ++i) { + program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0); + } + } + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) + { + QSGSpriteMaterial *m = static_cast<QSGSpriteMaterial *>(newEffect); + m->texture->bind(); + + program()->setUniformValue(m_opacity_id, state.opacity()); + program()->setUniformValue(m_timestamp_id, (float) m->timestamp); + program()->setUniformValue(m_framecount_id, (float) m->framecount); + program()->setUniformValue(m_animcount_id, (float) m->animcount); + program()->setUniformValue(m_width_id, (float) m->width); + program()->setUniformValue(m_height_id, (float) m->height); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); + } + + virtual void initialize() { + m_matrix_id = program()->uniformLocation("matrix"); + m_opacity_id = program()->uniformLocation("opacity"); + m_timestamp_id = program()->uniformLocation("timestamp"); + m_framecount_id = program()->uniformLocation("framecount"); + m_animcount_id = program()->uniformLocation("animcount"); + m_width_id = program()->uniformLocation("width"); + m_height_id = program()->uniformLocation("height"); + } + + virtual const char *vertexShader() const { return m_vertex_code.constData(); } + virtual const char *fragmentShader() const { return m_fragment_code.constData(); } + + virtual char const *const *attributeNames() const { + static const char *attr[] = { + "vTex", + "vAnimData", + 0 + }; + return attr; + } + + virtual bool isColorTable() const { return false; } + + int m_matrix_id; + int m_opacity_id; + int m_timestamp_id; + int m_framecount_id; + int m_animcount_id; + int m_width_id; + int m_height_id; + + QByteArray m_vertex_code; + QByteArray m_fragment_code; + + static float chunkOfBytes[1024]; +}; +float SpriteMaterialData::chunkOfBytes[1024]; + +QSGMaterialShader *QSGSpriteMaterial::createShader() const +{ + return new SpriteMaterialData; +} + +struct SpriteVertex { + float tx; + float ty; + float animIdx; + float frameDuration; + float frameCount; + float animT; +}; + +struct SpriteVertices { + SpriteVertex v1; + SpriteVertex v2; + SpriteVertex v3; + SpriteVertex v4; +}; + +QSGSpriteImage::QSGSpriteImage(QSGItem *parent) : + QSGItem(parent) + , m_node(0) + , m_material(0) + , m_spriteEngine(0) + , m_pleaseReset(false) + , m_running(true) +{ + setFlag(ItemHasContents); + connect(this, SIGNAL(runningChanged(bool)), + this, SLOT(update())); +} + +QDeclarativeListProperty<QSGSprite> QSGSpriteImage::sprites() +{ + return QDeclarativeListProperty<QSGSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); +} + +void QSGSpriteImage::createEngine() +{ + //TODO: delay until component complete + 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 SpriteImage_Attributes[] = { + { 0, 2, GL_FLOAT }, // tex + { 1, 4, GL_FLOAT } // animData +}; + +static QSGGeometry::AttributeSet SpriteImage_AttributeSet = +{ + 2, // Attribute Count + (4 + 2) * sizeof(float), + SpriteImage_Attributes +}; + +QSGGeometryNode* QSGSpriteImage::buildNode() +{ + if (!m_spriteEngine) { + qWarning() << "SpriteImage: No sprite engine..."; + return 0; + } + + if (m_material) { + delete m_material; + m_material = 0; + } + + m_material = new QSGSpriteMaterial(); + + QImage image = m_spriteEngine->assembledImage(); + if(image.isNull()) + return 0; + m_material->texture = sceneGraphEngine()->createTextureFromImage(image); + m_material->texture->setFiltering(QSGTexture::Linear); + m_material->framecount = m_spriteEngine->maxFrames(); + + int vCount = 4; + int iCount = 6; + QSGGeometry *g = new QSGGeometry(SpriteImage_AttributeSet, vCount, iCount); + 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; + 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; + + p->v2.tx = 1.0; + p->v2.ty = 0; + + p->v3.tx = 0; + p->v3.ty = 1.0; + + p->v4.tx = 1.0; + p->v4.ty = 1.0; + + quint16 *indices = g->indexDataAsUShort(); + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + indices[3] = 1; + indices[4] = 3; + indices[5] = 2; + + + m_timestamp.start(); + m_node = new QSGGeometryNode(); + m_node->setGeometry(g); + m_node->setMaterial(m_material); + return m_node; +} + +void QSGSpriteImage::reset() +{ + m_pleaseReset = true; +} + +QSGNode *QSGSpriteImage::updatePaintNode(QSGNode *, UpdatePaintNodeData *) +{ + if(m_pleaseReset){ + delete m_node; + delete m_material; + + m_node = 0; + m_material = 0; + m_pleaseReset = false; + } + + prepareNextFrame(); + + if(m_running){ + update(); + if (m_node) + m_node->markDirty(QSGNode::DirtyMaterial); + } + + return m_node; +} + +void QSGSpriteImage::prepareNextFrame() +{ + if (m_node == 0) + m_node = buildNode(); + if (m_node == 0) //error creating node + return; + + uint timeInt = m_timestamp.elapsed(); + qreal time = timeInt / 1000.; + m_material->timestamp = time; + m_material->animcount = m_spriteEngine->spriteCount(); + m_material->height = height(); + m_material->width = width(); + + //Advance State + SpriteVertices *p = (SpriteVertices *) m_node->geometry()->vertexData(); + m_spriteEngine->updateSprites(timeInt); + int curIdx = m_spriteEngine->spriteState(); + 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->spriteFrames(); + p->v1.frameDuration = p->v2.frameDuration = p->v3.frameDuration = p->v4.frameDuration = m_spriteEngine->spriteDuration(); + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qsgspriteimage_p.h b/src/declarative/items/qsgspriteimage_p.h new file mode 100644 index 0000000000..f03fd869f0 --- /dev/null +++ b/src/declarative/items/qsgspriteimage_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SPRITEIMAGE_H +#define SPRITEIMAGE_H + +#include <QSGItem> +#include <QTime> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGContext; +class QSGSprite; +class QSGSpriteEngine; +class QSGGeometryNode; +class QSGSpriteMaterial; +class QSGSpriteImage : public QSGItem +{ + Q_OBJECT + Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged) + //###try to share similar spriteEngines for less overhead? + Q_PROPERTY(QDeclarativeListProperty<QSGSprite> sprites READ sprites) + Q_CLASSINFO("DefaultProperty", "sprites") + +public: + explicit QSGSpriteImage(QSGItem *parent = 0); + + QDeclarativeListProperty<QSGSprite> sprites(); + + bool running() const + { + return m_running; + } + +signals: + + + void runningChanged(bool arg); + +public slots: + +void setRunning(bool arg) +{ + if (m_running != arg) { + m_running = arg; + emit runningChanged(arg); + } +} + +private slots: + void createEngine(); +protected: + void reset(); + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); +private: + void prepareNextFrame(); + QSGGeometryNode* buildNode(); + QSGGeometryNode *m_node; + QSGSpriteMaterial *m_material; + QList<QSGSprite*> m_sprites; + QSGSpriteEngine* m_spriteEngine; + QTime m_timestamp; + int m_maxFrames; + bool m_pleaseReset; + bool m_running; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // SPRITEIMAGE_H diff --git a/src/declarative/particles/defaultshaders/ctfragment.shader b/src/declarative/particles/defaultshaders/ctfragment.shader new file mode 100644 index 0000000000..a17f5841ca --- /dev/null +++ b/src/declarative/particles/defaultshaders/ctfragment.shader @@ -0,0 +1,11 @@ +uniform sampler2D texture; +uniform sampler2D colortable; +uniform sampler2D opacitytable; + +varying highp vec2 fTex; +varying lowp vec4 fColor; +varying lowp float tt; + +void main() { + gl_FragColor = (texture2D(texture, fTex).w) * fColor * texture2D(colortable, vec2(tt, 0.5)) *( texture2D(opacitytable, vec2(tt, 0.5)).w); +} diff --git a/src/declarative/particles/defaultshaders/ctvertex.shader b/src/declarative/particles/defaultshaders/ctvertex.shader new file mode 100644 index 0000000000..b20676cc49 --- /dev/null +++ b/src/declarative/particles/defaultshaders/ctvertex.shader @@ -0,0 +1,38 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration +attribute lowp vec4 vColor; + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform sampler2D sizetable; +uniform sampler2D opacitytable; + +varying highp vec2 fTex; +varying lowp vec4 fColor; +varying lowp float tt; + +void main() { + fTex = vTex; + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t) * texture2D(sizetable, vec2(t,0.5)).w; + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos = vPos + - currentSize / 2. + currentSize * vTex // adjust size + + vVec.xy * t * vData.y // apply speed vector.. + + 0.5 * vVec.zw * pow(t * vData.y, 2.); + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + fColor = vColor; + tt = t; + +} diff --git a/src/declarative/particles/defaultshaders/defaultFadeInOut.png b/src/declarative/particles/defaultshaders/defaultFadeInOut.png Binary files differnew file mode 100644 index 0000000000..89c04eaefe --- /dev/null +++ b/src/declarative/particles/defaultshaders/defaultFadeInOut.png diff --git a/src/declarative/particles/defaultshaders/deformablefragment.shader b/src/declarative/particles/defaultshaders/deformablefragment.shader new file mode 100644 index 0000000000..494053e319 --- /dev/null +++ b/src/declarative/particles/defaultshaders/deformablefragment.shader @@ -0,0 +1,8 @@ +uniform sampler2D texture; + +varying highp vec2 fTex; +varying lowp float fFade; + +void main() { + gl_FragColor = (texture2D(texture, fTex)) * fFade; +} diff --git a/src/declarative/particles/defaultshaders/deformablevertex.shader b/src/declarative/particles/defaultshaders/deformablevertex.shader new file mode 100644 index 0000000000..01570950b1 --- /dev/null +++ b/src/declarative/particles/defaultshaders/deformablevertex.shader @@ -0,0 +1,57 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration +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 + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform lowp float opacity; + +varying highp vec2 fTex; +varying lowp float fFade; + +void main() { + fTex = vTex; + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t); + + highp vec2 pos; + if (t < 0. || t > 1.){ + currentSize = 0.; + pos = vPos; + }else{ + highp float rotation = vRotation.x + vRotation.y * t * vData.y; + if(vRotation.z == 1.0){ + highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy; + rotation += atan(curVel.y, curVel.x); + } + highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation)); + highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5); + highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5); + highp vec2 xRotatedDeform; + xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y; + xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y; + highp vec2 yRotatedDeform; + yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y; + yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y; + pos = vPos + + xRotatedDeform + + yRotatedDeform + //- vec2(1,1) * currentSize * 0.5 // 'center' + + vVec.xy * t * vData.y // apply speed + + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration + } + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + highp float fadeIn = min(t * 10., 1.); + highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.)); + + fFade = fadeIn * fadeOut * opacity; +} diff --git a/src/declarative/particles/defaultshaders/identitytable.png b/src/declarative/particles/defaultshaders/identitytable.png Binary files differnew file mode 100644 index 0000000000..2cada1bfad --- /dev/null +++ b/src/declarative/particles/defaultshaders/identitytable.png diff --git a/src/declarative/particles/defaultshaders/simplefragment.shader b/src/declarative/particles/defaultshaders/simplefragment.shader new file mode 100644 index 0000000000..494053e319 --- /dev/null +++ b/src/declarative/particles/defaultshaders/simplefragment.shader @@ -0,0 +1,8 @@ +uniform sampler2D texture; + +varying highp vec2 fTex; +varying lowp float fFade; + +void main() { + gl_FragColor = (texture2D(texture, fTex)) * fFade; +} diff --git a/src/declarative/particles/defaultshaders/simplevertex.shader b/src/declarative/particles/defaultshaders/simplevertex.shader new file mode 100644 index 0000000000..f185ef0700 --- /dev/null +++ b/src/declarative/particles/defaultshaders/simplevertex.shader @@ -0,0 +1,36 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform lowp float opacity; + +varying highp vec2 fTex; +varying lowp float fFade; + +void main() { + fTex = vTex; + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t); + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos = vPos + - currentSize / 2. + currentSize * vTex // adjust size + + vVec.xy * t * vData.y // apply speed vector.. + + 0.5 * vVec.zw * pow(t * vData.y, 2.); + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + highp float fadeIn = min(t * 10., 1.); + highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.)); + + fFade = fadeIn * fadeOut * opacity; +} diff --git a/src/declarative/particles/defaultshaders/spritefragment.shader b/src/declarative/particles/defaultshaders/spritefragment.shader new file mode 100644 index 0000000000..4d89d69c6a --- /dev/null +++ b/src/declarative/particles/defaultshaders/spritefragment.shader @@ -0,0 +1,10 @@ +uniform sampler2D texture; + +varying highp vec2 fTexA; +varying highp vec2 fTexB; +varying lowp float progress; +varying lowp vec4 fColor; + +void main() { + gl_FragColor = mix(texture2D(texture, fTexA), texture2D(texture, fTexB), progress) * fColor.w; +} diff --git a/src/declarative/particles/defaultshaders/spriteimagefragment.shader b/src/declarative/particles/defaultshaders/spriteimagefragment.shader new file mode 100644 index 0000000000..ecd62cf390 --- /dev/null +++ b/src/declarative/particles/defaultshaders/spriteimagefragment.shader @@ -0,0 +1,9 @@ +uniform sampler2D texture; + +varying highp vec2 fTexA; +varying highp vec2 fTexB; +varying lowp float progress; + +void main() { + gl_FragColor = mix(texture2D(texture, fTexA), texture2D(texture, fTexB), progress); +} diff --git a/src/declarative/particles/defaultshaders/spriteimagevertex.shader b/src/declarative/particles/defaultshaders/spriteimagevertex.shader new file mode 100644 index 0000000000..27de2ada6a --- /dev/null +++ b/src/declarative/particles/defaultshaders/spriteimagevertex.shader @@ -0,0 +1,52 @@ +attribute highp vec2 vTex; +attribute highp vec4 vAnimData;// idx, duration, frameCount (this anim), timestamp (this anim) + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform lowp float opacity; +uniform highp float framecount; //maximum of all anims +uniform highp float animcount; +uniform highp float width; +uniform highp float height; + +varying highp vec2 fTexA; +varying highp vec2 fTexB; +varying lowp float progress; + + +void main() { + //Calculate frame location in texture + highp float frameIndex = mod((((timestamp - vAnimData.w)*1000.)/vAnimData.y),vAnimData.z); + progress = mod((timestamp - vAnimData.w)*1000., vAnimData.y) / vAnimData.y; + + frameIndex = floor(frameIndex); + highp vec2 frameTex; + if(vTex.x == 0.) + frameTex.x = (frameIndex/framecount); + else + frameTex.x = 1. * ((frameIndex + 1.)/framecount); + + if(vTex.y == 0.) + frameTex.y = (vAnimData.x/animcount); + else + frameTex.y = 1. * ((vAnimData.x + 1.)/animcount); + + fTexA = frameTex; + //Next frame is also passed, for interpolation + if(frameIndex != vAnimData.z - 1.)//Can't do it for the last frame though, this anim may not loop + frameIndex = mod(frameIndex+1., vAnimData.z); + + if(vTex.x == 0.) + frameTex.x = (frameIndex/framecount); + else + frameTex.x = 1. * ((frameIndex + 1.)/framecount); + + if(vTex.y == 0.) + frameTex.y = (vAnimData.x/animcount); + else + frameTex.y = 1. * ((vAnimData.x + 1.)/animcount); + fTexB = frameTex; + + + gl_Position = matrix * vec4(width * vTex.x, height * vTex.y, 0, 1); +} diff --git a/src/declarative/particles/defaultshaders/spritevertex.shader b/src/declarative/particles/defaultshaders/spritevertex.shader new file mode 100644 index 0000000000..78b8e36b3b --- /dev/null +++ b/src/declarative/particles/defaultshaders/spritevertex.shader @@ -0,0 +1,77 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration +attribute highp vec4 vAnimData;// idx, duration, frameCount (this anim), timestamp (this anim) + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform lowp float opacity; +uniform highp float framecount; //maximum of all anims +uniform highp float animcount; + +varying highp vec2 fTexA; +varying highp vec2 fTexB; +varying lowp float progress; +varying lowp vec4 fColor; + +void main() { + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + //Calculate frame location in texture + highp float frameIndex = mod((((timestamp - vAnimData.w)*1000.)/vAnimData.y),vAnimData.z); + progress = mod((timestamp - vAnimData.w)*1000., vAnimData.y) / vAnimData.y; + + frameIndex = floor(frameIndex); + highp vec2 frameTex = vTex; + if(vTex.x == 0.) + frameTex.x = (frameIndex/framecount); + else + frameTex.x = 1. * ((frameIndex + 1.)/framecount); + + if(vTex.y == 0.) + frameTex.y = (vAnimData.x/animcount); + else + frameTex.y = 1. * ((vAnimData.x + 1.)/animcount); + + fTexA = frameTex; + //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 + frameIndex = mod(frameIndex+1., vAnimData.z); + + if(vTex.x == 0.) + frameTex.x = (frameIndex/framecount); + else + frameTex.x = 1. * ((frameIndex + 1.)/framecount); + + if(vTex.y == 0.) + frameTex.y = (vAnimData.x/animcount); + else + frameTex.y = 1. * ((vAnimData.x + 1.)/animcount); + fTexB = frameTex; + + //Applying Size here seems to screw with RockingAffector? + highp float currentSize = mix(size, endSize, t * t); + + if (t < 0. || t > 1.) + currentSize = 0.; + + //If affector is mananging pos, they don't set speed? + highp vec2 pos = vPos + - currentSize / 2. + currentSize * vTex // adjust size + + vVec.xy * t * vData.y // apply speed vector.. + + 0.5 * vVec.zw * pow(t * vData.y, 2.); + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + // calculate opacity + highp float fadeIn = min(t * 10., 1.); + highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.)); + + lowp vec4 white = vec4(1.); + fColor = white * fadeIn * fadeOut * opacity; +} diff --git a/src/declarative/particles/defaultshaders/superfragment.shader b/src/declarative/particles/defaultshaders/superfragment.shader new file mode 100644 index 0000000000..a17f5841ca --- /dev/null +++ b/src/declarative/particles/defaultshaders/superfragment.shader @@ -0,0 +1,11 @@ +uniform sampler2D texture; +uniform sampler2D colortable; +uniform sampler2D opacitytable; + +varying highp vec2 fTex; +varying lowp vec4 fColor; +varying lowp float tt; + +void main() { + gl_FragColor = (texture2D(texture, fTex).w) * fColor * texture2D(colortable, vec2(tt, 0.5)) *( texture2D(opacitytable, vec2(tt, 0.5)).w); +} diff --git a/src/declarative/particles/defaultshaders/supervertex.shader b/src/declarative/particles/defaultshaders/supervertex.shader new file mode 100644 index 0000000000..432a23ce05 --- /dev/null +++ b/src/declarative/particles/defaultshaders/supervertex.shader @@ -0,0 +1,57 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration +attribute lowp vec4 vColor; +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 + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform sampler2D sizetable; +uniform sampler2D opacitytable; + +varying highp vec2 fTex; +varying lowp vec4 fColor; +varying lowp float tt; + +void main() { + fTex = vTex; + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t) * texture2D(sizetable, vec2(t,0.5)).w; + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos; + highp float rotation = vRotation.x + vRotation.y * t * vData.y; + if(vRotation.z == 1.0){ + highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy; + rotation += atan(curVel.y, curVel.x); + } + highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation)); + highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5); + highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5); + highp vec2 xRotatedDeform; + xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y; + xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y; + highp vec2 yRotatedDeform; + yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y; + yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y; + pos = vPos + + xRotatedDeform + + yRotatedDeform + //- vec2(1,1) * currentSize * 0.5 // 'center' + + vVec.xy * t * vData.y // apply speed + + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + fColor = vColor; + tt = t; + +} diff --git a/src/declarative/particles/defaultshaders/trailsfragment.shader b/src/declarative/particles/defaultshaders/trailsfragment.shader new file mode 100644 index 0000000000..d3db87fa30 --- /dev/null +++ b/src/declarative/particles/defaultshaders/trailsfragment.shader @@ -0,0 +1,8 @@ +uniform sampler2D texture; + +varying highp vec2 fTex; +varying lowp vec4 fColor; + +void main() { + gl_FragColor = (texture2D(texture, fTex).w) * fColor; +} diff --git a/src/declarative/particles/defaultshaders/trailsvertex.shader b/src/declarative/particles/defaultshaders/trailsvertex.shader new file mode 100644 index 0000000000..7bc1d66b71 --- /dev/null +++ b/src/declarative/particles/defaultshaders/trailsvertex.shader @@ -0,0 +1,37 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration +attribute lowp vec4 vColor; + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform lowp float opacity; + +varying highp vec2 fTex; +varying lowp vec4 fColor; + +void main() { + fTex = vTex; + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t); + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos = vPos + - currentSize / 2. + currentSize * vTex // adjust size + + vVec.xy * t * vData.y // apply speed vector.. + + 0.5 * vVec.zw * pow(t * vData.y, 2.); + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + highp float fadeIn = min(t * 10., 1.); + highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.)); + + fColor = vColor * fadeIn * fadeOut * opacity; +} diff --git a/src/declarative/particles/defaultshaders/ultrafragment.shader b/src/declarative/particles/defaultshaders/ultrafragment.shader new file mode 100644 index 0000000000..0627d0f1e8 --- /dev/null +++ b/src/declarative/particles/defaultshaders/ultrafragment.shader @@ -0,0 +1,16 @@ +uniform sampler2D texture; +uniform sampler2D colortable; +uniform sampler2D opacitytable; + +varying highp vec2 fTexA; +varying highp vec2 fTexB; +varying lowp float progress; +varying lowp vec4 fColor; +varying lowp float tt; + +void main() { + gl_FragColor = mix(texture2D(texture, fTexA), texture2D(texture, fTexB), progress) + * fColor + * texture2D(colortable, vec2(tt, 0.5)) + *( texture2D(opacitytable, vec2(tt, 0.5)).w); +} diff --git a/src/declarative/particles/defaultshaders/ultravertex.shader b/src/declarative/particles/defaultshaders/ultravertex.shader new file mode 100644 index 0000000000..65a1a3077a --- /dev/null +++ b/src/declarative/particles/defaultshaders/ultravertex.shader @@ -0,0 +1,94 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration +attribute lowp vec4 vColor; +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 +attribute highp vec4 vAnimData;// idx, duration, frameCount (this anim), timestamp (this anim) + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform highp float framecount; //maximum of all anims +uniform highp float animcount; +uniform sampler2D sizetable; + +varying lowp float tt; +varying highp vec2 fTexA; +varying highp vec2 fTexB; +varying lowp float progress; +varying lowp vec4 fColor; + + +void main() { + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + //Calculate frame location in texture + highp float frameIndex = mod((((timestamp - vAnimData.w)*1000.)/vAnimData.y),vAnimData.z); + progress = mod((timestamp - vAnimData.w)*1000., vAnimData.y) / vAnimData.y; + + frameIndex = floor(frameIndex); + highp vec2 frameTex = vTex; + if(vTex.x == 0.) + frameTex.x = (frameIndex/framecount); + else + frameTex.x = 1. * ((frameIndex + 1.)/framecount); + + if(vTex.y == 0.) + frameTex.y = (vAnimData.x/animcount); + else + frameTex.y = 1. * ((vAnimData.x + 1.)/animcount); + + fTexA = frameTex; + //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 + frameIndex = mod(frameIndex+1., vAnimData.z); + + if(vTex.x == 0.) + frameTex.x = (frameIndex/framecount); + else + frameTex.x = 1. * ((frameIndex + 1.)/framecount); + + if(vTex.y == 0.) + frameTex.y = (vAnimData.x/animcount); + else + frameTex.y = 1. * ((vAnimData.x + 1.)/animcount); + fTexB = frameTex; + + highp float currentSize = mix(size, endSize, t * t) * texture2D(sizetable, vec2(t,0.5)).w; + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos; + highp float rotation = vRotation.x + vRotation.y * t * vData.y; + if(vRotation.z == 1.0){ + highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy; + rotation += atan(curVel.y, curVel.x); + } + highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation)); + highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5); + highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5); + highp vec2 xRotatedDeform; + xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y; + xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y; + highp vec2 yRotatedDeform; + yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y; + yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y; + pos = vPos + + xRotatedDeform + + yRotatedDeform + //- vec2(1,1) * currentSize * 0.5 // 'center' + + vVec.xy * t * vData.y // apply speed + + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + fColor = vColor; + tt = t; + +} diff --git a/src/declarative/particles/particles.pri b/src/declarative/particles/particles.pri new file mode 100644 index 0000000000..04200a380e --- /dev/null +++ b/src/declarative/particles/particles.pri @@ -0,0 +1,60 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qsgangleddirection_p.h \ + $$PWD/qsgcustomparticle_p.h \ + $$PWD/qsgellipseextruder_p.h \ + $$PWD/qsgemitter_p.h \ + $$PWD/qsgfollowemitter_p.h \ + $$PWD/qsgfriction_p.h \ + $$PWD/qsggravity_p.h \ + $$PWD/qsgimageparticle_p.h \ + $$PWD/qsgitemparticle_p.h \ + $$PWD/qsgkill_p.h \ + $$PWD/qsglineextruder_p.h \ + $$PWD/qsgmaskextruder_p.h \ + $$PWD/qsgmodelparticle_p.h \ + $$PWD/qsgparticleaffector_p.h \ + $$PWD/qsgparticleemitter_p.h \ + $$PWD/qsgparticleextruder_p.h \ + $$PWD/qsgparticlepainter_p.h \ + $$PWD/qsgparticlesmodule_p.h \ + $$PWD/qsgparticlesystem_p.h \ + $$PWD/qsgpointattractor_p.h \ + $$PWD/qsgpointdirection_p.h \ + $$PWD/qsgspritegoal_p.h \ + $$PWD/qsgstochasticdirection_p.h \ + $$PWD/qsgtargeteddirection_p.h \ + $$PWD/qsgturbulence_p.h \ + $$PWD/qsgwander_p.h + +SOURCES += \ + $$PWD/qsgangleddirection.cpp \ + $$PWD/qsgcustomparticle.cpp \ + $$PWD/qsgellipseextruder.cpp \ + $$PWD/qsgemitter.cpp \ + $$PWD/qsgfollowemitter.cpp \ + $$PWD/qsgfriction.cpp \ + $$PWD/qsggravity.cpp \ + $$PWD/qsgimageparticle.cpp \ + $$PWD/qsgitemparticle.cpp \ + $$PWD/qsgkill.cpp \ + $$PWD/qsglineextruder.cpp \ + $$PWD/qsgmaskextruder.cpp \ + $$PWD/qsgmodelparticle.cpp \ + $$PWD/qsgparticleaffector.cpp \ + $$PWD/qsgparticleemitter.cpp \ + $$PWD/qsgparticleextruder.cpp \ + $$PWD/qsgparticlepainter.cpp \ + $$PWD/qsgparticlesmodule.cpp \ + $$PWD/qsgparticlesystem.cpp \ + $$PWD/qsgpointattractor.cpp \ + $$PWD/qsgpointdirection.cpp \ + $$PWD/qsgspritegoal.cpp \ + $$PWD/qsgstochasticdirection.cpp \ + $$PWD/qsgtargeteddirection.cpp \ + $$PWD/qsgturbulence.cpp \ + $$PWD/qsgwander.cpp + +RESOURCES += \ + $$PWD/particles.qrc diff --git a/src/declarative/particles/particles.qrc b/src/declarative/particles/particles.qrc new file mode 100644 index 0000000000..85931ec9ce --- /dev/null +++ b/src/declarative/particles/particles.qrc @@ -0,0 +1,22 @@ +<RCC> + <qresource prefix="/"> + <file>defaultshaders/spritefragment.shader</file> + <file>defaultshaders/spritevertex.shader</file> + <file>defaultshaders/ctfragment.shader</file> + <file>defaultshaders/ctvertex.shader</file> + <file>defaultshaders/trailsfragment.shader</file> + <file>defaultshaders/trailsvertex.shader</file> + <file>defaultshaders/spriteimagefragment.shader</file> + <file>defaultshaders/spriteimagevertex.shader</file> + <file>defaultshaders/identitytable.png</file> + <file>defaultshaders/defaultFadeInOut.png</file> + <file>defaultshaders/deformablefragment.shader</file> + <file>defaultshaders/deformablevertex.shader</file> + <file>defaultshaders/ultravertex.shader</file> + <file>defaultshaders/ultrafragment.shader</file> + <file>defaultshaders/supervertex.shader</file> + <file>defaultshaders/superfragment.shader</file> + <file>defaultshaders/simplevertex.shader</file> + <file>defaultshaders/simplefragment.shader</file> + </qresource> +</RCC> diff --git a/src/declarative/particles/qsgangleddirection.cpp b/src/declarative/particles/qsgangleddirection.cpp new file mode 100644 index 0000000000..5c32b53e3e --- /dev/null +++ b/src/declarative/particles/qsgangleddirection.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgangleddirection_p.h" +#include <cmath> +QT_BEGIN_NAMESPACE +const qreal CONV = 0.017453292519943295; +QSGAngledDirection::QSGAngledDirection(QObject *parent) : + QSGStochasticDirection(parent) + , m_angle(0) + , m_magnitude(0) + , m_angleVariation(0) + , m_magnitudeVariation(0) +{ + +} + +const QPointF &QSGAngledDirection::sample(const QPointF &from) +{ + //TODO: Faster + qreal theta = m_angle*CONV - m_angleVariation*CONV + rand()/float(RAND_MAX) * m_angleVariation*CONV * 2; + qreal mag = m_magnitude- m_magnitudeVariation + rand()/float(RAND_MAX) * m_magnitudeVariation * 2; + m_ret.setX(mag * cos(theta)); + m_ret.setY(mag * sin(theta)); + return m_ret; +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgangleddirection_p.h b/src/declarative/particles/qsgangleddirection_p.h new file mode 100644 index 0000000000..76262453a9 --- /dev/null +++ b/src/declarative/particles/qsgangleddirection_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGANGLEDDIRECTION_H +#define QSGANGLEDDIRECTION_H +#include "qsgstochasticdirection_p.h" +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGAngledDirection : public QSGStochasticDirection +{ + Q_OBJECT + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) + Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged) + Q_PROPERTY(qreal angleVariation READ angleVariation WRITE setAngleVariation NOTIFY angleVariationChanged) + Q_PROPERTY(qreal magnitudeVariation READ magnitudeVariation WRITE setMagnitudeVariation NOTIFY magnitudeVariationChanged) +public: + explicit QSGAngledDirection(QObject *parent = 0); + const QPointF &sample(const QPointF &from); + qreal angle() const + { + return m_angle; + } + + qreal magnitude() const + { + return m_magnitude; + } + + qreal angleVariation() const + { + return m_angleVariation; + } + + qreal magnitudeVariation() const + { + return m_magnitudeVariation; + } + +signals: + + void angleChanged(qreal arg); + + void magnitudeChanged(qreal arg); + + void angleVariationChanged(qreal arg); + + void magnitudeVariationChanged(qreal arg); + +public slots: +void setAngle(qreal arg) +{ + if (m_angle != arg) { + m_angle = arg; + emit angleChanged(arg); + } +} + +void setMagnitude(qreal arg) +{ + if (m_magnitude != arg) { + m_magnitude = arg; + emit magnitudeChanged(arg); + } +} + +void setAngleVariation(qreal arg) +{ + if (m_angleVariation != arg) { + m_angleVariation = arg; + emit angleVariationChanged(arg); + } +} + +void setMagnitudeVariation(qreal arg) +{ + if (m_magnitudeVariation != arg) { + m_magnitudeVariation = arg; + emit magnitudeVariationChanged(arg); + } +} + +private: +qreal m_angle; +qreal m_magnitude; +qreal m_angleVariation; +qreal m_magnitudeVariation; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // QSGANGLEDDIRECTION_H diff --git a/src/declarative/particles/qsgcustomparticle.cpp b/src/declarative/particles/qsgcustomparticle.cpp new file mode 100644 index 0000000000..808ff1c427 --- /dev/null +++ b/src/declarative/particles/qsgcustomparticle.cpp @@ -0,0 +1,581 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgcustomparticle_p.h" +#include <private/qsgshadereffectmesh_p.h> +#include <cstdlib> + +QT_BEGIN_NAMESPACE +/* + "uniform highp mat4 qt_ModelViewProjectionMatrix; \n" + "attribute highp vec4 qt_Vertex; \n" + "attribute highp vec2 qt_MultiTexCoord0; \n" + "varying highp vec2 qt_TexCoord0; \n" + "void main() { \n" + " qt_TexCoord0 = qt_MultiTexCoord0; \n" + " gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex; \n" + "}"; +*/ +//Includes comments because the code isn't self explanatory +static const char qt_particles_default_vertex_code[] = + "attribute highp vec2 vPos; \n" + "attribute highp vec2 vTex; \n" + "attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize \n" + "attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration \n" + "uniform highp mat4 qt_ModelViewProjectionMatrix; \n" + "uniform highp float timestamp; \n" + "varying highp vec2 fTex; \n" + "void main() { \n" + " fTex = vTex; \n" + " highp float size = vData.z; \n" + " highp float endSize = vData.w; \n" + " highp float t = (timestamp - vData.x) / vData.y; \n" + " highp float currentSize = mix(size, endSize, t * t); \n" + " if (t < 0. || t > 1.) \n" + " currentSize = 0.; \n" + " highp vec2 pos = vPos \n" + " - currentSize / 2. + currentSize * vTex // adjust size \n" + " + vVec.xy * t * vData.y // apply speed vector.. \n" + " + 0.5 * vVec.zw * pow(t * vData.y, 2.); \n" + " gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1); \n" + "}"; + +static const char qt_particles_default_fragment_code[] =//TODO: Default frag requires source? + "uniform sampler2D source; \n" + "varying highp vec2 fTex; \n" + "uniform lowp float qt_Opacity; \n" + "void main() { \n" + " gl_FragColor = texture2D(source, fTex) * qt_Opacity; \n" + "}"; + +/* +static const char qt_particles_default_vertex_code[] = + "attribute highp vec2 vPos; \n" + "attribute highp vec2 vTex; \n" + "uniform highp mat4 qt_ModelViewProjectionMatrix; \n" + "void main() { \n" + " highp float currentSize = 1000.0; \n" + " highp vec2 pos = vec2(100.0,100.0) \n" + " - currentSize / 2. + currentSize * vTex; // adjust size \n" + " gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1); \n" + "}"; +static const char qt_particles_default_fragment_code[] =//TODO: Default frag requires source? + "void main() { \n" + " gl_FragColor = vec4(0,255,0,255); \n" + "}"; +*/ + +static const char qt_position_attribute_name[] = "qt_Vertex"; +static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0"; + +static QSGGeometry::Attribute PlainParticle_Attributes[] = { + { 0, 2, GL_FLOAT }, // Position + { 1, 2, GL_FLOAT }, // TexCoord + { 2, 4, GL_FLOAT }, // Data + { 3, 4, GL_FLOAT }, // Vectors + { 4, 1, GL_FLOAT } // r +}; + +static QSGGeometry::AttributeSet PlainParticle_AttributeSet = +{ + 5, // Attribute Count + (2 + 2 + 4 + 4 + 1) * sizeof(float), + PlainParticle_Attributes +}; + +struct PlainVertex { + float x; + float y; + float tx; + float ty; + float t; + float lifeSpan; + float size; + float endSize; + float sx; + float sy; + float ax; + float ay; + float r; +}; + +struct PlainVertices { + PlainVertex v1; + PlainVertex v2; + PlainVertex v3; + PlainVertex v4; +}; + +QSGCustomParticle::QSGCustomParticle(QSGItem* parent) + : QSGParticlePainter(parent) + , m_pleaseReset(true) + , m_dirtyData(true) +{ + setFlag(QSGItem::ItemHasContents); +} + +void QSGCustomParticle::componentComplete() +{ + reset(); + QSGParticlePainter::componentComplete(); +} + + +//Trying to keep the shader conventions the same as in qsgshadereffectitem +/*! + \qmlproperty string CustomParticle::fragmentShader + + This property holds the fragment shader's GLSL source code. + The default shader passes the texture coordinate along to the fragment + shader as "varying highp vec2 qt_TexCoord0". +*/ + +void QSGCustomParticle::setFragmentShader(const QByteArray &code) +{ + if (m_source.fragmentCode.constData() == code.constData()) + return; + m_source.fragmentCode = code; + if (isComponentComplete()) { + reset(); + } + emit fragmentShaderChanged(); +} + +/*! + \qmlproperty string CustomParticle::vertexShader + + This property holds the vertex shader's GLSL source code. + The default shader expects the texture coordinate to be passed from the + vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a + sampler2D named "source". +*/ + +void QSGCustomParticle::setVertexShader(const QByteArray &code) +{ + if (m_source.vertexCode.constData() == code.constData()) + return; + m_source.vertexCode = code; + if (isComponentComplete()) { + reset(); + } + emit vertexShaderChanged(); +} + +void QSGCustomParticle::setCount(int c) +{ + QSGParticlePainter::setCount(c); + m_pleaseReset = true; +} + +void QSGCustomParticle::reset() +{ + disconnectPropertySignals(); + + m_source.attributeNames.clear(); + m_source.uniformNames.clear(); + m_source.respectsOpacity = false; + m_source.respectsMatrix = false; + m_source.className = metaObject()->className(); + + for (int i = 0; i < m_sources.size(); ++i) { + const SourceData &source = m_sources.at(i); + delete source.mapper; + if (source.item && source.item->parentItem() == this) + source.item->setParentItem(0); + } + m_sources.clear(); + + QSGParticlePainter::reset(); + m_pleaseReset = true; +} + + +void QSGCustomParticle::changeSource(int index) +{ + Q_ASSERT(index >= 0 && index < m_sources.size()); + QVariant v = property(m_sources.at(index).name.constData()); + setSource(v, index); +} + +void QSGCustomParticle::updateData() +{ + m_dirtyData = true; + update(); +} + +void QSGCustomParticle::setSource(const QVariant &var, int index) +{ + Q_ASSERT(index >= 0 && index < m_sources.size()); + + SourceData &source = m_sources[index]; + + source.item = 0; + if (var.isNull()) { + return; + } else if (!qVariantCanConvert<QObject *>(var)) { + qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData()); + return; + } + + QObject *obj = qVariantValue<QObject *>(var); + + QSGTextureProvider *int3rface = QSGTextureProvider::from(obj); + if (!int3rface) { + qWarning("Could not assign property '%s', did not implement QSGTextureProvider.", source.name.constData()); + } + + source.item = qobject_cast<QSGItem *>(obj); + + // TODO: Copy better solution in QSGShaderEffectItem when they find it. + // 'source.item' needs a canvas to get a scenegraph node. + // The easiest way to make sure it gets a canvas is to + // make it a part of the same item tree as 'this'. + if (source.item && source.item->parentItem() == 0) { + source.item->setParentItem(this); + source.item->setVisible(false); + } +} + +void QSGCustomParticle::disconnectPropertySignals() +{ + disconnect(this, 0, this, SLOT(updateData())); + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + disconnect(this, 0, source.mapper, 0); + disconnect(source.mapper, 0, this, 0); + } +} + +void QSGCustomParticle::connectPropertySignals() +{ + QSet<QByteArray>::const_iterator it; + for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) { + int pi = metaObject()->indexOfProperty(it->constData()); + if (pi >= 0) { + QMetaProperty mp = metaObject()->property(pi); + if (!mp.hasNotifySignal()) + qWarning("QSGShaderEffectItem: property '%s' does not have notification method!", it->constData()); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().signature()); + connect(this, signalName, this, SLOT(updateData())); + } else { + qWarning("QSGShaderEffectItem: '%s' does not have a matching property!", it->constData()); + } + } + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + int pi = metaObject()->indexOfProperty(source.name.constData()); + if (pi >= 0) { + QMetaProperty mp = metaObject()->property(pi); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().signature()); + connect(this, signalName, source.mapper, SLOT(map())); + source.mapper->setMapping(this, i); + connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int))); + } else { + qWarning("QSGShaderEffectItem: '%s' does not have a matching source!", source.name.constData()); + } + } +} + +void QSGCustomParticle::updateProperties() +{ + QByteArray vertexCode = m_source.vertexCode; + QByteArray fragmentCode = m_source.fragmentCode; + if (vertexCode.isEmpty()) + vertexCode = qt_particles_default_vertex_code; + if (fragmentCode.isEmpty()) + fragmentCode = qt_particles_default_fragment_code; + + m_source.attributeNames.clear(); + m_source.attributeNames << "vPos" << "vTex" << "vData" << "vVec" << "r"; + + lookThroughShaderCode(vertexCode); + lookThroughShaderCode(fragmentCode); + + if (!m_source.attributeNames.contains(qt_position_attribute_name)) + qWarning("QSGShaderEffectItem: Missing reference to \'%s\'.", qt_position_attribute_name); + if (!m_source.attributeNames.contains(qt_texcoord_attribute_name)) + qWarning("QSGShaderEffectItem: Missing reference to \'%s\'.", qt_texcoord_attribute_name); + if (!m_source.respectsMatrix) + qWarning("QSGShaderEffectItem: Missing reference to \'qt_ModelViewProjectionMatrix\'."); + if (!m_source.respectsOpacity) + qWarning("QSGShaderEffectItem: Missing reference to \'qt_Opacity\'."); + + for (int i = 0; i < m_sources.size(); ++i) { + QVariant v = property(m_sources.at(i).name); + setSource(v, i); + } + + connectPropertySignals(); +} + +void QSGCustomParticle::lookThroughShaderCode(const QByteArray &code) +{ + // Regexp for matching attributes and uniforms. + // In human readable form: attribute|uniform [lowp|mediump|highp] <type> <name> + static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)")); + Q_ASSERT(re.isValid()); + + int pos = -1; + + QString wideCode = QString::fromLatin1(code.constData(), code.size()); + + while ((pos = re.indexIn(wideCode, pos + 1)) != -1) { + QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute + QByteArray type = re.cap(2).toLatin1(); // type + QByteArray name = re.cap(3).toLatin1(); // variable name + + if (decl == "attribute") { + if(!m_source.attributeNames.contains(name))//TODO: Can they add custom attributes? + qWarning() << "Custom Particle: Unknown attribute " << name; + } else { + Q_ASSERT(decl == "uniform");//TODO: Shouldn't assert + + if (name == "qt_ModelViewProjectionMatrix") { + m_source.respectsMatrix = true; + } else if (name == "qt_Opacity") { + m_source.respectsOpacity = true; + } else if (name == "timestamp") { + //TODO: Copy the whole thing just because I have one more uniform? + } else { + m_source.uniformNames.insert(name); + if (type == "sampler2D") { + SourceData d; + d.mapper = new QSignalMapper; + d.name = name; + d.item = 0; + m_sources.append(d); + } + } + } + } +} + +QSGNode *QSGCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + if(m_pleaseReset){ + if(m_node) + delete m_node; + //delete m_material;//Shader effect item doesn't regen material? + + m_node = 0; + m_pleaseReset = false; + m_dirtyData = false; + } + + if(m_system && m_system->isRunning()) + prepareNextFrame(); + if (m_node){ + if(oldNode) + Q_ASSERT(oldNode == m_node); + update(); + m_node->markDirty(QSGNode::DirtyMaterial); //done in buildData? + } + + return m_node; +} + +void QSGCustomParticle::prepareNextFrame(){ + if(!m_node) + m_node = buildCustomNode(); + if(!m_node) + return; + + m_lastTime = m_system->systemSync(this) / 1000.; + if(m_dirtyData || true)//Currently this is how we update timestamp... potentially over expensive. + buildData(); +} + +QSGShaderEffectNode* QSGCustomParticle::buildCustomNode() +{ + if (m_count * 4 > 0xffff) { + printf("CustomParticle: Too many particles... \n");//####Why is this here? + return 0; + } + + if(m_count <= 0) { + printf("CustomParticle: Too few particles... \n"); + return 0; + } + + + //Create Particle Geometry + int vCount = m_count * 4; + int iCount = m_count * 6; + QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount); + g->setDrawingMode(GL_TRIANGLES); + PlainVertex *vertices = (PlainVertex *) g->vertexData(); + for (int p=0; p<m_count; ++p) { + double r = rand()/(double)RAND_MAX;//TODO: Seed? + for (int i=0; i<4; ++i) { + vertices[i].x = 0; + vertices[i].y = 0; + vertices[i].t = -1; + vertices[i].lifeSpan = 0; + vertices[i].size = 0; + vertices[i].endSize = 0; + vertices[i].sx = 0; + vertices[i].sy = 0; + vertices[i].ax = 0; + vertices[i].ay = 0; + vertices[i].r = r; + } + + vertices[0].tx = 0; + vertices[0].ty = 0; + + vertices[1].tx = 1; + vertices[1].ty = 0; + + vertices[2].tx = 0; + vertices[2].ty = 1; + + vertices[3].tx = 1; + vertices[3].ty = 1; + + vertices += 4; + } + quint16 *indices = g->indexDataAsUShort(); + for (int i=0; i<m_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; + } + + updateProperties(); + QSGShaderEffectNode* node = new QSGShaderEffectNode(); + + + node->setGeometry(g); + node->setMaterial(&m_material); + + /* + //For debugging, just use grid vertices like ShaderEffect + node->setGeometry(0); + QSGShaderEffectMesh* mesh = new QSGGridMesh(); + node->setFlag(QSGNode::OwnsGeometry, false); + + qDebug() << m_source.attributeNames; + QSGGeometry* geometry = node->geometry(); + geometry = mesh->updateGeometry(geometry, m_source.attributeNames, QRectF(0,0,width(),height())); + if(!geometry) + qDebug() << "Should have written the error handling"; + else + qDebug() << "Mesh Loaded"; + node->setGeometry(geometry); + qDebug() << QString("INIT") << geometry << (QObject*)node; + node->setFlag(QSGNode::OwnsGeometry, true); + */ + QSGShaderEffectProgram s = m_source; + if (s.fragmentCode.isEmpty()) + s.fragmentCode = qt_particles_default_fragment_code; + if (s.vertexCode.isEmpty()) + s.vertexCode = qt_particles_default_vertex_code; + m_material.setProgramSource(s); + node->markDirty(QSGNode::DirtyMaterial); + node->markDirty(QSGNode::DirtyAll); + return node; +} + +static const QByteArray timestampName("timestamp"); + +void QSGCustomParticle::buildData() +{ + if(!m_node)//Operates on m_node + return; + QVector<QPair<QByteArray, QVariant> > values; + QVector<QPair<QByteArray, QPointer<QSGItem> > > textures; + const QVector<QPair<QByteArray, QPointer<QSGItem> > > &oldTextures = m_material.textureProviders(); + + for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin(); + it != m_source.uniformNames.end(); ++it) { + values.append(qMakePair(*it, property(*it))); + } + for (int i = 0; i < oldTextures.size(); ++i) { + QSGTextureProvider *oldSource = QSGTextureProvider::from(oldTextures.at(i).second); + if (oldSource && oldSource->textureChangedSignal()) + disconnect(oldTextures.at(i).second, oldSource->textureChangedSignal(), m_node, SLOT(markDirtyTexture())); + } + for (int i = 0; i < m_sources.size(); ++i) { + const SourceData &source = m_sources.at(i); + textures.append(qMakePair(source.name, source.item)); + QSGTextureProvider *t = QSGTextureProvider::from(source.item); + if (t && t->textureChangedSignal()) + connect(source.item, t->textureChangedSignal(), m_node, SLOT(markDirtyTexture()), Qt::DirectConnection); + } + values.append(qMakePair(timestampName, QVariant(m_lastTime))); + m_material.setUniforms(values); + m_material.setTextureProviders(textures); + m_node->markDirty(QSGNode::DirtyMaterial); + m_dirtyData = false; +} + +void QSGCustomParticle::load(QSGParticleData *d) +{ + reload(d);//We don't do anything special in C++ here. +} + +void QSGCustomParticle::reload(QSGParticleData *d) +{ + if (m_node == 0) + return; + + PlainVertices *particles = (PlainVertices *) m_node->geometry()->vertexData(); + + int pos = particleTypeIndex(d); + + PlainVertices &p = particles[pos]; + + //Perhaps we could be more efficient? + vertexCopy(p.v1, d->pv); + vertexCopy(p.v2, d->pv); + vertexCopy(p.v3, d->pv); + vertexCopy(p.v4, d->pv); + +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgcustomparticle_p.h b/src/declarative/particles/qsgcustomparticle_p.h new file mode 100644 index 0000000000..95144ef638 --- /dev/null +++ b/src/declarative/particles/qsgcustomparticle_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CUSTOM_PARTICLE_H +#define CUSTOM_PARTICLE_H +#include "qsgparticlepainter_p.h" +#include <QtDeclarative/private/qsgshadereffectnode_p.h> +#include <QSignalMapper> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGNode; + +//Genealogy: Hybrid of UltraParticle and ShaderEffectItem +class QSGCustomParticle : public QSGParticlePainter +{ + Q_OBJECT + Q_PROPERTY(QByteArray fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged) + Q_PROPERTY(QByteArray vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged) + +public: + explicit QSGCustomParticle(QSGItem* parent=0); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); + virtual void setCount(int c); + + QByteArray fragmentShader() const { return m_source.fragmentCode; } + void setFragmentShader(const QByteArray &code); + + QByteArray vertexShader() const { return m_source.vertexCode; } + void setVertexShader(const QByteArray &code); +public Q_SLOTS: + void updateData(); + void changeSource(int); +Q_SIGNALS: + void fragmentShaderChanged(); + void vertexShaderChanged(); +protected: + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + void prepareNextFrame(); + void setSource(const QVariant &var, int index); + void disconnectPropertySignals(); + void connectPropertySignals(); + void reset(); + void updateProperties(); + void lookThroughShaderCode(const QByteArray &code); + virtual void componentComplete(); + QSGShaderEffectNode *buildCustomNode(); + +private: + void buildData(); + + bool m_pleaseReset; + bool m_dirtyData; + QSGShaderEffectProgram m_source; + struct SourceData + { + QSignalMapper *mapper; + QPointer<QSGItem> item; + QByteArray name; + }; + QVector<SourceData> m_sources; + QSGShaderEffectMaterial m_material; + QSGShaderEffectNode* m_node; + qreal m_lastTime; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //HEADER_GUARD diff --git a/src/declarative/particles/qsgellipseextruder.cpp b/src/declarative/particles/qsgellipseextruder.cpp new file mode 100644 index 0000000000..76925895b9 --- /dev/null +++ b/src/declarative/particles/qsgellipseextruder.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgellipseextruder_p.h" +#include <cmath> +QT_BEGIN_NAMESPACE +QSGEllipseExtruder::QSGEllipseExtruder(QObject *parent) : + QSGParticleExtruder(parent) + , m_fill(true) +{ +} + +QPointF QSGEllipseExtruder::extrude(const QRectF & r) +{ + qreal theta = ((qreal)rand()/RAND_MAX) * 6.2831853071795862; + qreal mag = m_fill ? ((qreal)rand()/RAND_MAX) : 1; + return QPointF(r.x() + r.width()/2 + mag * (r.width()/2) * cos(theta), + r.y() + r.height()/2 + mag * (r.height()/2) * sin(theta)); +} + +bool QSGEllipseExtruder::contains(const QRectF &bounds, const QPointF &point) +{ + return bounds.contains(point);//TODO: Ellipse +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgellipseextruder_p.h b/src/declarative/particles/qsgellipseextruder_p.h new file mode 100644 index 0000000000..9f770a7ee6 --- /dev/null +++ b/src/declarative/particles/qsgellipseextruder_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ELLIPSEEXTRUDER_H +#define ELLIPSEEXTRUDER_H +#include "qsgparticleextruder_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGEllipseExtruder : public QSGParticleExtruder +{ + Q_OBJECT + Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)//###Use base class? If it's still box +public: + explicit QSGEllipseExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + virtual bool contains(const QRectF &bounds, const QPointF &point); + + bool fill() const + { + return m_fill; + } + +signals: + + void fillChanged(bool arg); + +public slots: + + void setFill(bool arg) + { + if (m_fill != arg) { + m_fill = arg; + emit fillChanged(arg); + } + } +private: + bool m_fill; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // ELLIPSEEXTRUDER_H diff --git a/src/declarative/particles/qsgemitter.cpp b/src/declarative/particles/qsgemitter.cpp new file mode 100644 index 0000000000..081dd8dec5 --- /dev/null +++ b/src/declarative/particles/qsgemitter.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgemitter_p.h" +#include "qsgparticlesystem_p.h" +#include "qsgparticlepainter_p.h"//TODO: What was this for again? +QT_BEGIN_NAMESPACE + +QSGBasicEmitter::QSGBasicEmitter(QSGItem* parent) + : QSGParticleEmitter(parent) + , m_speed_from_movement(0) + , m_particle_count(0) + , m_reset_last(true) + , m_last_timestamp(0) + , m_last_emission(0) +{ +// setFlag(ItemHasContents); +} + +void QSGBasicEmitter::setSpeedFromMovement(qreal t) +{ + if (t == m_speed_from_movement) + return; + m_speed_from_movement = t; + emit speedFromMovementChanged(); +} + +void QSGBasicEmitter::reset() +{ + m_reset_last = true; +} + +void QSGBasicEmitter::emitWindow(int timeStamp) +{ + if (m_system == 0) + return; + if((!m_emitting || !m_particlesPerSecond)&& !m_burstLeft && m_burstQueue.isEmpty()){ + m_reset_last = true; + return; + } + + if (m_reset_last) { + m_last_emitter = m_last_last_emitter = QPointF(x(), y()); + m_last_timestamp = timeStamp/1000.; + m_last_emission = m_last_timestamp; + m_reset_last = false; + } + + if(m_burstLeft){ + m_burstLeft -= timeStamp - m_last_timestamp * 1000.; + if(m_burstLeft < 0){ + if(!m_emitting) + timeStamp += m_burstLeft; + m_burstLeft = 0; + } + } + + qreal time = timeStamp / 1000.; + + qreal particleRatio = 1. / m_particlesPerSecond; + qreal pt = m_last_emission; + + qreal opt = pt; // original particle time + qreal dt = time - m_last_timestamp; // timestamp delta... + if(!dt) + dt = 0.000001; + + // emitter difference since last... + qreal dex = (x() - m_last_emitter.x()); + qreal dey = (y() - m_last_emitter.y()); + + qreal ax = (m_last_last_emitter.x() + m_last_emitter.x()) / 2; + qreal bx = m_last_emitter.x(); + qreal cx = (x() + m_last_emitter.x()) / 2; + qreal ay = (m_last_last_emitter.y() + m_last_emitter.y()) / 2; + qreal by = m_last_emitter.y(); + qreal cy = (y() + m_last_emitter.y()) / 2; + + qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize; + qreal emitter_x_offset = m_last_emitter.x() - x(); + qreal emitter_y_offset = m_last_emitter.y() - y(); + if(!m_burstQueue.isEmpty() && !m_burstLeft && !m_emitting)//'outside time' emissions only + pt = time; + while (pt < time || !m_burstQueue.isEmpty()) { + //int pos = m_last_particle % m_particle_count; + QSGParticleData* datum = m_system->newDatum(m_system->m_groupIds[m_particle]); + if(datum){//actually emit(otherwise we've been asked to skip this one) + datum->e = this;//###useful? + ParticleVertex &p = datum->pv; + qreal t = 1 - (pt - opt) / dt; + qreal vx = + - 2 * ax * (1 - t) + + 2 * bx * (1 - 2 * t) + + 2 * cx * t; + qreal vy = + - 2 * ay * (1 - t) + + 2 * by * (1 - 2 * t) + + 2 * cy * t; + + + // Particle timestamp + p.t = pt; + p.lifeSpan = //TODO:Promote to base class? + (m_particleDuration + + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation)) + / 1000.0; + + // Particle position + QRectF boundsRect; + if(!m_burstQueue.isEmpty()){ + boundsRect = QRectF(m_burstQueue.first().second.x() - x(), m_burstQueue.first().second.y() - y(), + width(), height()); + } else { + boundsRect = QRectF(emitter_x_offset + dex * (pt - opt) / dt, emitter_y_offset + dey * (pt - opt) / dt + , width(), height()); + } + QPointF newPos = effectiveExtruder()->extrude(boundsRect); + p.x = newPos.x(); + p.y = newPos.y(); + + // Particle speed + const QPointF &speed = m_speed->sample(newPos); + p.sx = speed.x() + + m_speed_from_movement * vx; + p.sy = speed.y() + + m_speed_from_movement * vy; + + // Particle acceleration + const QPointF &accel = m_acceleration->sample(newPos); + p.ax = accel.x(); + p.ay = accel.y(); + + // Particle size + float sizeVariation = -m_particleSizeVariation + + rand() / float(RAND_MAX) * m_particleSizeVariation * 2; + + float size = qMax((qreal)0.0 , m_particleSize + sizeVariation); + float endSize = qMax((qreal)0.0 , sizeAtEnd + sizeVariation); + + p.size = size;// * float(m_emitting); + p.endSize = endSize;// * float(m_emitting); + + m_system->emitParticle(datum); + } + if(m_burstQueue.isEmpty()){ + pt += particleRatio; + }else{ + m_burstQueue.first().first--; + if(m_burstQueue.first().first <= 0) + m_burstQueue.pop_front(); + } + } + m_last_emission = pt; + + m_last_last_last_emitter = m_last_last_emitter; + m_last_last_emitter = m_last_emitter; + m_last_emitter = QPointF(x(), y()); + m_last_timestamp = time; +} + + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgemitter_p.h b/src/declarative/particles/qsgemitter_p.h new file mode 100644 index 0000000000..3988c71cdf --- /dev/null +++ b/src/declarative/particles/qsgemitter_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TRAILSEMITTER_H +#define TRAILSEMITTER_H + +#include <QtCore> +#include <QtGui> + +#include "qsgparticleemitter_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGGeometryNode; + +class QSGBasicEmitter : public QSGParticleEmitter +{ + Q_OBJECT + + Q_PROPERTY(qreal speedFromMovement READ speedFromMovement WRITE setSpeedFromMovement NOTIFY speedFromMovementChanged) + +public: + explicit QSGBasicEmitter(QSGItem* parent=0); + virtual ~QSGBasicEmitter(){} + virtual void emitWindow(int timeStamp); + + + qreal speedFromMovement() const { return m_speed_from_movement; } + void setSpeedFromMovement(qreal s); + + qreal renderOpacity() const { return m_render_opacity; } + +signals: + + void speedFromMovementChanged(); + +public slots: +public: + virtual void reset(); +protected: + +private: + + qreal m_speed_from_movement; + + // derived values... + int m_particle_count; + bool m_reset_last; + qreal m_last_timestamp; + qreal m_last_emission; + + QPointF m_last_emitter; + QPointF m_last_last_emitter; + QPointF m_last_last_last_emitter; + + qreal m_render_opacity; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // TRAILSEMITTER_H diff --git a/src/declarative/particles/qsgfollowemitter.cpp b/src/declarative/particles/qsgfollowemitter.cpp new file mode 100644 index 0000000000..442cff9ec8 --- /dev/null +++ b/src/declarative/particles/qsgfollowemitter.cpp @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgfollowemitter_p.h" +#include "qsgparticlepainter_p.h"//TODO: What was this for again? +#include <cmath> +QT_BEGIN_NAMESPACE + +QSGFollowEmitter::QSGFollowEmitter(QSGItem *parent) : + QSGParticleEmitter(parent) + , m_particlesPerParticlePerSecond(0) + , m_lastTimeStamp(0) + , m_emitterXVariation(0) + , m_emitterYVariation(0) + , m_followCount(0) + , m_emissionExtruder(0) + , m_defaultEmissionExtruder(new QSGParticleExtruder(this)) +{ + connect(this, SIGNAL(followChanged(QString)), + this, SLOT(recalcParticlesPerSecond())); + connect(this, SIGNAL(particleDurationChanged(int)), + this, SLOT(recalcParticlesPerSecond())); + connect(this, SIGNAL(particlesPerParticlePerSecondChanged(int)), + this, SLOT(recalcParticlesPerSecond())); +} + +void QSGFollowEmitter::recalcParticlesPerSecond(){ + if(!m_system) + return; + m_followCount = m_system->m_groupData[m_system->m_groupIds[m_follow]]->size; + if(!m_followCount){ + setParticlesPerSecond(1000);//XXX: Fix this horrendous hack, needed so they aren't turned off from start (causes crashes - test that when gone you don't crash with 0 PPPS) + }else{ + setParticlesPerSecond(m_particlesPerParticlePerSecond * m_followCount); + m_lastEmission.resize(m_followCount); + m_lastEmission.fill(0); + } +} + +void QSGFollowEmitter::reset() +{ + m_followCount = 0; +} + +void QSGFollowEmitter::emitWindow(int timeStamp) +{ + if (m_system == 0) + return; + if(!m_emitting && !m_burstLeft && m_burstQueue.isEmpty()) + return; + if(m_followCount != m_system->m_groupData[m_system->m_groupIds[m_follow]]->size){ + qreal oldPPS = m_particlesPerSecond; + recalcParticlesPerSecond(); + if(m_particlesPerSecond != oldPPS) + return;//system may need to update + } + + if(m_burstLeft){ + m_burstLeft -= timeStamp - m_lastTimeStamp * 1000.; + if(m_burstLeft < 0){ + timeStamp += m_burstLeft; + m_burstLeft = 0; + } + } + + qreal time = timeStamp / 1000.; + qreal particleRatio = 1. / m_particlesPerParticlePerSecond; + qreal pt; + + //Have to map it into this system, because particlesystem automaps it back + QPointF offset = m_system->mapFromItem(this, QPointF(0, 0)); + qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize; + + int gId = m_system->m_groupIds[m_follow]; + int gId2 = m_system->m_groupIds[m_particle]; + for(int i=0; i<m_system->m_groupData[gId]->size; i++){ + pt = m_lastEmission[i]; + QSGParticleData* d = m_system->m_data[i + m_system->m_groupData[gId]->start]; + if(!d || !d->stillAlive()) + continue; + if(pt < d->pv.t) + pt = d->pv.t; + + if(!effectiveExtruder()->contains(QRectF(offset.x(), offset.y(), width(), height()),QPointF(d->curX(), d->curY()))){ + m_lastEmission[i] = time;//jump over this time period without emitting, because it's outside + continue; + } + while(pt < time || !m_burstQueue.isEmpty()){ + QSGParticleData* datum = m_system->newDatum(gId2); + if(datum){//else, skip this emission + datum->e = this;//###useful? + ParticleVertex &p = datum->pv; + + // Particle timestamp + p.t = pt; + p.lifeSpan = + (m_particleDuration + + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation)) + / 1000.0; + + // Particle position + // Note that burst location doesn't get used for follow emitter + qreal followT = pt - d->pv.t; + qreal followT2 = followT * followT * 0.5; + qreal sizeOffset = d->pv.size/2;//TODO: Current size? As an option + //TODO: Set variations + //Subtract offset, because PS expects this in emitter coordinates + QRectF boundsRect(d->pv.x - offset.x() + d->pv.sx * followT + d->pv.ax * followT2 - m_emitterXVariation/2, + d->pv.y - offset.y() + d->pv.sy * followT + d->pv.ay * followT2 - m_emitterYVariation/2, + m_emitterXVariation, + m_emitterYVariation); + // QRectF boundsRect(d->pv.x + d->pv.sx * followT + d->pv.ax * followT2 + offset.x() - sizeOffset, + // d->pv.y + d->pv.sy * followT + d->pv.ay * followT2 + offset.y() - sizeOffset, + // sizeOffset*2, + // sizeOffset*2); + + QSGParticleExtruder* effectiveEmissionExtruder = m_emissionExtruder ? m_emissionExtruder : m_defaultEmissionExtruder; + const QPointF &newPos = effectiveEmissionExtruder->extrude(boundsRect); + p.x = newPos.x(); + p.y = newPos.y(); + + // Particle speed + const QPointF &speed = m_speed->sample(newPos); + p.sx = speed.x(); + p.sy = speed.y(); + + // Particle acceleration + const QPointF &accel = m_acceleration->sample(newPos); + p.ax = accel.x(); + p.ay = accel.y(); + + // Particle size + float sizeVariation = -m_particleSizeVariation + + rand() / float(RAND_MAX) * m_particleSizeVariation * 2; + + float size = qMax((qreal)0.0, m_particleSize + sizeVariation); + float endSize = qMax((qreal)0.0, sizeAtEnd + sizeVariation); + + p.size = size * float(m_emitting); + p.endSize = endSize * float(m_emitting); + + m_system->emitParticle(datum); + } + if(!m_burstQueue.isEmpty()){ + m_burstQueue.first().first--; + if(m_burstQueue.first().first <= 0) + m_burstQueue.pop_front(); + }else{ + pt += particleRatio; + } + } + m_lastEmission[i] = pt; + } + + m_lastTimeStamp = time; +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgfollowemitter_p.h b/src/declarative/particles/qsgfollowemitter_p.h new file mode 100644 index 0000000000..bf5d2fe8ef --- /dev/null +++ b/src/declarative/particles/qsgfollowemitter_p.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FOLLOWEMITTER_H +#define FOLLOWEMITTER_H +#include "qsgparticleemitter_p.h" +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGFollowEmitter : public QSGParticleEmitter +{ + Q_OBJECT + Q_PROPERTY(QString follow READ follow WRITE setFollow NOTIFY followChanged) + //### Remove, and just document that particles per second is per particle? But has count issues + Q_PROPERTY(int emitRatePerParticle READ particlesPerParticlePerSecond WRITE setParticlesPerParticlePerSecond NOTIFY particlesPerParticlePerSecondChanged) + + //TODO: Document that FollowEmitter's box is where it follows. It emits in a rect centered on the followed particle + //TODO: A set of properties that can involve the particle size of the followed + Q_PROPERTY(QSGParticleExtruder* emitShape READ emissonShape WRITE setEmissionShape NOTIFY emissionShapeChanged) + Q_PROPERTY(qreal emitHeight READ emitterYVariation WRITE setEmitterYVariation NOTIFY emitterYVariationChanged) + Q_PROPERTY(qreal emitWidth READ emitterXVariation WRITE setEmitterXVariation NOTIFY emitterXVariationChanged) + +public: + explicit QSGFollowEmitter(QSGItem *parent = 0); + virtual void emitWindow(int timeStamp); + virtual void reset(); + + int particlesPerParticlePerSecond() const + { + return m_particlesPerParticlePerSecond; + } + + qreal emitterXVariation() const + { + return m_emitterXVariation; + } + + qreal emitterYVariation() const + { + return m_emitterYVariation; + } + + QString follow() const + { + return m_follow; + } + + QSGParticleExtruder* emissonShape() const + { + return m_emissionExtruder; + } + +signals: + + void particlesPerParticlePerSecondChanged(int arg); + + void emitterXVariationChanged(qreal arg); + + void emitterYVariationChanged(qreal arg); + + void followChanged(QString arg); + + void emissionShapeChanged(QSGParticleExtruder* arg); + +public slots: + + void setParticlesPerParticlePerSecond(int arg) + { + if (m_particlesPerParticlePerSecond != arg) { + m_particlesPerParticlePerSecond = arg; + emit particlesPerParticlePerSecondChanged(arg); + } + } + void setEmitterXVariation(qreal arg) + { + if (m_emitterXVariation != arg) { + m_emitterXVariation = arg; + emit emitterXVariationChanged(arg); + } + } + + void setEmitterYVariation(qreal arg) + { + if (m_emitterYVariation != arg) { + m_emitterYVariation = arg; + emit emitterYVariationChanged(arg); + } + } + + void setFollow(QString arg) + { + if (m_follow != arg) { + m_follow = arg; + emit followChanged(arg); + } + } + + void setEmissionShape(QSGParticleExtruder* arg) + { + if (m_emissionExtruder != arg) { + m_emissionExtruder = arg; + emit emissionShapeChanged(arg); + } + } + +private slots: + void recalcParticlesPerSecond(); + +private: + QSet<QSGParticleData*> m_pending; + QVector<qreal> m_lastEmission; + int m_particlesPerParticlePerSecond; + qreal m_lastTimeStamp; + qreal m_emitterXVariation; + qreal m_emitterYVariation; + QString m_follow; + int m_followCount; + QSGParticleExtruder* m_emissionExtruder; + QSGParticleExtruder* m_defaultEmissionExtruder; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // FOLLOWEMITTER_H diff --git a/src/declarative/particles/qsgfriction.cpp b/src/declarative/particles/qsgfriction.cpp new file mode 100644 index 0000000000..828d20556d --- /dev/null +++ b/src/declarative/particles/qsgfriction.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgfriction_p.h" +QT_BEGIN_NAMESPACE +QSGFrictionAffector::QSGFrictionAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_factor(0.0) +{ +} + +bool QSGFrictionAffector::affectParticle(QSGParticleData *d, qreal dt) +{ + if(!m_factor) + return false; + qreal curSX = d->curSX(); + qreal curSY = d->curSY(); + d->setInstantaneousSX(curSX + (curSX * m_factor * -1 * dt)); + d->setInstantaneousSY(curSY + (curSY * m_factor * -1 * dt)); + return true; +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgfriction_p.h b/src/declarative/particles/qsgfriction_p.h new file mode 100644 index 0000000000..6b5a86f3f2 --- /dev/null +++ b/src/declarative/particles/qsgfriction_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FRICTIONAFFECTOR_H +#define FRICTIONAFFECTOR_H +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGFrictionAffector : public QSGParticleAffector +{ + Q_OBJECT + Q_PROPERTY(qreal factor READ factor WRITE setFactor NOTIFY factorChanged) +public: + explicit QSGFrictionAffector(QSGItem *parent = 0); + + qreal factor() const + { + return m_factor; + } +protected: + virtual bool affectParticle(QSGParticleData *d, qreal dt); +signals: + + void factorChanged(qreal arg); + +public slots: + +void setFactor(qreal arg) +{ + if (m_factor != arg) { + m_factor = arg; + emit factorChanged(arg); + } +} + +private: +qreal m_factor; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // FRICTIONAFFECTOR_H diff --git a/src/declarative/particles/qsggravity.cpp b/src/declarative/particles/qsggravity.cpp new file mode 100644 index 0000000000..de735da5ad --- /dev/null +++ b/src/declarative/particles/qsggravity.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsggravity_p.h" +#include <cmath> +QT_BEGIN_NAMESPACE +const qreal CONV = 0.017453292520444443; +QSGGravityAffector::QSGGravityAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_acceleration(-10), m_angle(90), m_xAcc(0), m_yAcc(0) +{ + connect(this, SIGNAL(accelerationChanged(qreal)), + this, SLOT(recalc())); + connect(this, SIGNAL(angleChanged(qreal)), + this, SLOT(recalc())); + recalc(); +} + +void QSGGravityAffector::recalc() +{ + qreal theta = m_angle * CONV; + m_xAcc = m_acceleration * cos(theta); + m_yAcc = m_acceleration * sin(theta); +} + +bool QSGGravityAffector::affectParticle(QSGParticleData *d, qreal dt) +{ + Q_UNUSED(dt); + bool changed = false; + if(d->pv.ax != m_xAcc){ + d->setInstantaneousAX(m_xAcc); + changed = true; + } + if(d->pv.ay != m_yAcc){ + d->setInstantaneousAY(m_yAcc); + changed = true; + } + return changed; +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsggravity_p.h b/src/declarative/particles/qsggravity_p.h new file mode 100644 index 0000000000..57b2511911 --- /dev/null +++ b/src/declarative/particles/qsggravity_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GRAVITYAFFECTOR_H +#define GRAVITYAFFECTOR_H +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGGravityAffector : public QSGParticleAffector +{ + Q_OBJECT + Q_PROPERTY(qreal acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged) + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) +public: + explicit QSGGravityAffector(QSGItem *parent = 0); + qreal acceleration() const + { + return m_acceleration; + } + + qreal angle() const + { + return m_angle; + } +protected: + virtual bool affectParticle(QSGParticleData *d, qreal dt); +signals: + + void accelerationChanged(qreal arg); + + void angleChanged(qreal arg); + +public slots: +void setAcceleration(qreal arg) +{ + if (m_acceleration != arg) { + m_acceleration = arg; + emit accelerationChanged(arg); + } +} + +void setAngle(qreal arg) +{ + if (m_angle != arg) { + m_angle = arg; + emit angleChanged(arg); + } +} + +private slots: + void recalc(); +private: + qreal m_acceleration; + qreal m_angle; + + qreal m_xAcc; + qreal m_yAcc; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // GRAVITYAFFECTOR_H diff --git a/src/declarative/particles/qsgimageparticle.cpp b/src/declarative/particles/qsgimageparticle.cpp new file mode 100644 index 0000000000..c9df5f4dbd --- /dev/null +++ b/src/declarative/particles/qsgimageparticle.cpp @@ -0,0 +1,993 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qsgcontext_p.h> +#include <private/qsgadaptationlayer_p.h> +#include <qsgnode.h> +#include <qsgtexturematerial.h> +#include <qsgtexture.h> +#include <QFile> +#include "qsgimageparticle_p.h" +#include "qsgparticleemitter_p.h" +#include "qsgsprite_p.h" +#include "qsgspriteengine_p.h" +#include <QGLFunctions> +#include <qsgengine.h> + +QT_BEGIN_NAMESPACE + +const float CONV = 0.017453292519943295; +class UltraMaterial : public QSGMaterial +{ +public: + UltraMaterial(bool withSprites=false) + : timestamp(0) + , framecount(1) + , animcount(1) + , usesSprites(withSprites) + { + setFlag(Blending, true); + } + + ~UltraMaterial() + { + delete texture; + delete colortable; + delete sizetable; + delete opacitytable; + } + + virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } + virtual QSGMaterialShader *createShader() const; + virtual int compare(const QSGMaterial *other) const + { + return this - static_cast<const UltraMaterial *>(other); + } + + QSGTexture *texture; + QSGTexture *colortable; + QSGTexture *sizetable; + QSGTexture *opacitytable; + + qreal timestamp; + int framecount; + int animcount; + bool usesSprites; +}; +class UltraMaterialData : public QSGMaterialShader +{ +public: + UltraMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) + { + QFile vf(vertexFile ? vertexFile : ":defaultshaders/ultravertex.shader"); + vf.open(QFile::ReadOnly); + m_vertex_code = vf.readAll(); + + QFile ff(fragmentFile ? fragmentFile : ":defaultshaders/ultrafragment.shader"); + ff.open(QFile::ReadOnly); + m_fragment_code = ff.readAll(); + + Q_ASSERT(!m_vertex_code.isNull()); + Q_ASSERT(!m_fragment_code.isNull()); + } + + void deactivate() { + QSGMaterialShader::deactivate(); + + for (int i=0; i<8; ++i) { + program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0); + } + } + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) + { + UltraMaterial *m = static_cast<UltraMaterial *>(newEffect); + state.context()->functions()->glActiveTexture(GL_TEXTURE1); + m->colortable->bind(); + program()->setUniformValue(m_colortable_id, 1); + + state.context()->functions()->glActiveTexture(GL_TEXTURE2); + m->sizetable->bind(); + program()->setUniformValue(m_sizetable_id, 2); + + state.context()->functions()->glActiveTexture(GL_TEXTURE3); + m->opacitytable->bind(); + program()->setUniformValue(m_opacitytable_id, 3); + + state.context()->functions()->glActiveTexture(GL_TEXTURE0);//Investigate why this screws up Text{} if placed before 1 + m->texture->bind(); + + program()->setUniformValue(m_opacity_id, state.opacity()); + program()->setUniformValue(m_timestamp_id, (float) m->timestamp); + program()->setUniformValue(m_framecount_id, (float) m->framecount); + program()->setUniformValue(m_animcount_id, (float) m->animcount); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); + } + + virtual void initialize() { + m_colortable_id = program()->uniformLocation("colortable"); + m_sizetable_id = program()->uniformLocation("sizetable"); + m_opacitytable_id = program()->uniformLocation("opacitytable"); + m_matrix_id = program()->uniformLocation("matrix"); + m_opacity_id = program()->uniformLocation("opacity"); + m_timestamp_id = program()->uniformLocation("timestamp"); + m_framecount_id = program()->uniformLocation("framecount"); + m_animcount_id = program()->uniformLocation("animcount"); + } + + virtual const char *vertexShader() const { return m_vertex_code.constData(); } + virtual const char *fragmentShader() const { return m_fragment_code.constData(); } + + virtual char const *const *attributeNames() const { + static const char *attr[] = { + "vPos", + "vTex", + "vData", + "vVec", + "vColor", + "vDeformVec", + "vRotation", + "vAnimData", + 0 + }; + return attr; + } + + virtual bool isColorTable() const { return false; } + + int m_matrix_id; + int m_opacity_id; + int m_timestamp_id; + int m_colortable_id; + int m_sizetable_id; + int m_opacitytable_id; + int m_framecount_id; + int m_animcount_id; + + QByteArray m_vertex_code; + QByteArray m_fragment_code; + + static float chunkOfBytes[1024]; +}; +float UltraMaterialData::chunkOfBytes[1024]; + +QSGMaterialShader *UltraMaterial::createShader() const +{ + if(usesSprites)//TODO: Perhaps just swap the shaders, and don't mind the extra vector? + return new UltraMaterialData; + else + return new UltraMaterialData; +} + + +class SimpleMaterial : public UltraMaterial +{ + virtual QSGMaterialShader *createShader() const; + virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } +}; + +class SimpleMaterialData : public QSGMaterialShader +{ +public: + SimpleMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) + { + QFile vf(vertexFile ? vertexFile : ":defaultshaders/simplevertex.shader"); + vf.open(QFile::ReadOnly); + m_vertex_code = vf.readAll(); + + QFile ff(fragmentFile ? fragmentFile : ":defaultshaders/simplefragment.shader"); + ff.open(QFile::ReadOnly); + m_fragment_code = ff.readAll(); + + Q_ASSERT(!m_vertex_code.isNull()); + Q_ASSERT(!m_fragment_code.isNull()); + } + + void deactivate() { + QSGMaterialShader::deactivate(); + + for (int i=0; i<8; ++i) { + program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0); + } + } + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) + { + UltraMaterial *m = static_cast<UltraMaterial *>(newEffect); + state.context()->functions()->glActiveTexture(GL_TEXTURE0); + m->texture->bind(); + + program()->setUniformValue(m_opacity_id, state.opacity()); + program()->setUniformValue(m_timestamp_id, (float) m->timestamp); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); + } + + virtual void initialize() { + m_matrix_id = program()->uniformLocation("matrix"); + m_opacity_id = program()->uniformLocation("opacity"); + m_timestamp_id = program()->uniformLocation("timestamp"); + } + + virtual const char *vertexShader() const { return m_vertex_code.constData(); } + virtual const char *fragmentShader() const { return m_fragment_code.constData(); } + + virtual char const *const *attributeNames() const { + static const char *attr[] = { + "vPos", + "vTex", + "vData", + "vVec", + 0 + }; + return attr; + } + + virtual bool isColorTable() const { return false; } + + int m_matrix_id; + int m_opacity_id; + int m_timestamp_id; + + QByteArray m_vertex_code; + QByteArray m_fragment_code; + + static float chunkOfBytes[1024]; +}; +float SimpleMaterialData::chunkOfBytes[1024]; + +QSGMaterialShader *SimpleMaterial::createShader() const { + return new SimpleMaterialData; +} + +QSGImageParticle::QSGImageParticle(QSGItem* parent) + : QSGParticlePainter(parent) + , m_do_reset(false) + , m_color_variation(0.0) + , m_node(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_xVector(0) + , m_yVector(0) + , m_rotationVariation(0) + , m_rotationSpeed(0) + , m_rotationSpeedVariation(0) + , m_spriteEngine(0) + , m_bloat(false) + , perfLevel(Unknown) + , m_lastLevel(Unknown) +{ + setFlag(ItemHasContents); +} + +QDeclarativeListProperty<QSGSprite> QSGImageParticle::sprites() +{ + return QDeclarativeListProperty<QSGSprite>(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 < Coloured) + reset(); +} + +void QSGImageParticle::setColorVariation(qreal var) +{ + if (var == m_color_variation) + return; + m_color_variation = var; + emit colorVariationChanged(); + if(perfLevel < Coloured) + reset(); +} + +void QSGImageParticle::setAlphaVariation(qreal arg) +{ + if (m_alphaVariation != arg) { + m_alphaVariation = arg; + emit alphaVariationChanged(arg); + } + if(perfLevel < Coloured) + reset(); +} + +void QSGImageParticle::setAlpha(qreal arg) +{ + if (m_alpha != arg) { + m_alpha = arg; + emit alphaChanged(arg); + } + if(perfLevel < Coloured) + reset(); +} + +void QSGImageParticle::setRedVariation(qreal arg) +{ + if (m_redVariation != arg) { + m_redVariation = arg; + emit redVariationChanged(arg); + } + if(perfLevel < Coloured) + reset(); +} + +void QSGImageParticle::setGreenVariation(qreal arg) +{ + if (m_greenVariation != arg) { + m_greenVariation = arg; + emit greenVariationChanged(arg); + } + if(perfLevel < Coloured) + reset(); +} + +void QSGImageParticle::setBlueVariation(qreal arg) +{ + if (m_blueVariation != arg) { + m_blueVariation = arg; + emit blueVariationChanged(arg); + } + if(perfLevel < Coloured) + 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(QSGStochasticDirection* arg) +{ + if (m_xVector != arg) { + m_xVector = arg; + emit xVectorChanged(arg); + } + if(perfLevel < Deformable) + reset(); +} + +void QSGImageParticle::setYVector(QSGStochasticDirection* 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::setCount(int c) +{ + QSGParticlePainter::setCount(c); + m_pleaseReset = true; +} + +void QSGImageParticle::reset() +{ + QSGParticlePainter::reset(); + m_pleaseReset = true; +} + +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, 2, GL_FLOAT }, // TexCoord + { 2, 4, GL_FLOAT }, // Data + { 3, 4, GL_FLOAT } // Vectors +}; + +static QSGGeometry::AttributeSet SimpleParticle_AttributeSet = +{ + 4, // Attribute Count + (2 + 2 + 4 + 4 ) * sizeof(float), + SimpleParticle_Attributes +}; + +static QSGGeometry::Attribute UltraParticle_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 UltraParticle_AttributeSet = +{ + 8, // Attribute Count + (2 + 2 + 4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar), + UltraParticle_Attributes +}; + +QSGGeometryNode* QSGImageParticle::buildSimpleParticleNode() +{ + perfLevel = Simple;//TODO: Intermediate levels + QImage image = QImage(m_image_name.toLocalFile()); + if (image.isNull()) { + printf("UltraParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile())); + return 0; + } + int vCount = m_count * 4; + int iCount = m_count * 6; + qDebug() << "Simple Case"; + + QSGGeometry *g = new QSGGeometry(SimpleParticle_AttributeSet, vCount, iCount); + g->setDrawingMode(GL_TRIANGLES); + + SimpleVertex *vertices = (SimpleVertex *) g->vertexData(); + for (int p=0; p<m_count; ++p) { + for (int i=0; i<4; ++i) { + vertices[i].x = 0; + vertices[i].y = 0; + vertices[i].t = -1; + vertices[i].lifeSpan = 0; + vertices[i].size = 0; + vertices[i].endSize = 0; + vertices[i].sx = 0; + vertices[i].sy = 0; + vertices[i].ax = 0; + vertices[i].ay = 0; + } + + vertices[0].tx = 0; + vertices[0].ty = 0; + + vertices[1].tx = 1; + vertices[1].ty = 0; + + vertices[2].tx = 0; + vertices[2].ty = 1; + + vertices[3].tx = 1; + vertices[3].ty = 1; + + vertices += 4; + } + + quint16 *indices = g->indexDataAsUShort(); + for (int i=0; i<m_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; + } + + if (m_material) { + delete m_material; + m_material = 0; + } + + m_material = new SimpleMaterial(); + m_material->texture = sceneGraphEngine()->createTextureFromImage(image); + m_material->texture->setFiltering(QSGTexture::Linear); + m_material->framecount = 1; + m_node = new QSGGeometryNode(); + m_node->setGeometry(g); + m_node->setMaterial(m_material); + + m_last_particle = 0; + + return m_node; +} + +QSGGeometryNode* QSGImageParticle::buildParticleNode() +{ + if (m_count * 4 > 0xffff) { + printf("UltraParticle: Too many particles... \n");//####Why is this here? + return 0; + } + + if(m_count <= 0) { + printf("UltraParticle: Too few particles... \n"); + return 0; + } + + if(!m_sprites.count() && !m_bloat + && m_colortable_name.isEmpty() + && m_sizetable_name.isEmpty() + && m_opacitytable_name.isEmpty() + && !m_autoRotation + && !m_rotation && !m_rotationVariation + && !m_rotationSpeed && !m_rotationSpeedVariation + && !m_alphaVariation && m_alpha == 1.0 + && !m_redVariation && !m_blueVariation && !m_greenVariation + && !m_color.isValid() + ) + return buildSimpleParticleNode(); + perfLevel = Sprites;//TODO: intermediate levels + if(!m_color.isValid())//But we're in colored level (or higher) + m_color = QColor(Qt::white); + qDebug() << "Complex Case"; + + QImage image; + if(m_sprites.count()){ + if (!m_spriteEngine) { + qWarning() << "UltraParticle: 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("UltraParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile())); + return 0; + } + } + + int vCount = m_count * 4; + int iCount = m_count * 6; + + QSGGeometry *g = new QSGGeometry(UltraParticle_AttributeSet, vCount, iCount); + g->setDrawingMode(GL_TRIANGLES); + + UltraVertex *vertices = (UltraVertex *) g->vertexData(); + SimpleVertex *oldSimple = (SimpleVertex *) m_lastData;//TODO: Other levels + if(m_lastLevel == 1) + qDebug() << "Theta" << m_lastLevel << oldSimple[0].x << oldSimple[0].y << oldSimple[0].t; + for (int p=0; p<m_count; ++p) { + + if (m_lastLevel == 1) {//Transplant/IntermediateVertices? + for (int i=0; i<4; ++i) { + vertices[i].x = oldSimple[i].x; + vertices[i].y = oldSimple[i].y; + vertices[i].t = oldSimple[i].t; + vertices[i].lifeSpan = oldSimple[i].lifeSpan; + vertices[i].size = oldSimple[i].size; + vertices[i].endSize = oldSimple[i].endSize; + vertices[i].sx = oldSimple[i].sx; + vertices[i].sy = oldSimple[i].sy; + vertices[i].ax = oldSimple[i].ax; + vertices[i].ay = oldSimple[i].ay; + vertices[i].xx = 1; + vertices[i].xy = 0; + vertices[i].yx = 0; + vertices[i].yy = 1; + vertices[i].rotation = 0; + vertices[i].rotationSpeed = 0; + vertices[i].autoRotate = 0; + vertices[i].animIdx = 0; + vertices[i].frameDuration = oldSimple[i].lifeSpan; + vertices[i].frameCount = 1; + vertices[i].animT = oldSimple[i].t; + vertices[i].color.r = 255; + vertices[i].color.g = 255; + vertices[i].color.b = 255; + vertices[i].color.a = 255; + } + } else { + for (int i=0; i<4; ++i) { + vertices[i].x = 0; + vertices[i].y = 0; + vertices[i].t = -1; + vertices[i].lifeSpan = 0; + vertices[i].size = 0; + vertices[i].endSize = 0; + vertices[i].sx = 0; + vertices[i].sy = 0; + vertices[i].ax = 0; + vertices[i].ay = 0; + vertices[i].xx = 1; + vertices[i].xy = 0; + vertices[i].yx = 0; + vertices[i].yy = 1; + vertices[i].rotation = 0; + vertices[i].rotationSpeed = 0; + vertices[i].autoRotate = 0; + vertices[i].animIdx = -1; + vertices[i].frameDuration = 1; + vertices[i].frameCount = 0; + vertices[i].animT = -1; + vertices[i].color.r = 0;//TODO:Some things never get used uninitialized. Consider dropping them here? + vertices[i].color.g = 0; + vertices[i].color.b = 0; + vertices[i].color.a = 0; + } + } + + vertices[0].tx = 0; + vertices[0].ty = 0; + + vertices[1].tx = 1; + vertices[1].ty = 0; + + vertices[2].tx = 0; + vertices[2].ty = 1; + + vertices[3].tx = 1; + vertices[3].ty = 1; + + vertices += 4; + oldSimple += 4; + } + + quint16 *indices = g->indexDataAsUShort();//TODO: Speed gains by copying this over if count unchanged? + for (int i=0; i<m_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; + } + + qFree(m_lastData); + if (m_material) { + delete m_material; + m_material = 0; + } + + QImage colortable(m_colortable_name.toLocalFile()); + QImage sizetable(m_sizetable_name.toLocalFile()); + QImage opacitytable(m_opacitytable_name.toLocalFile()); + m_material = new UltraMaterial(); + if(colortable.isNull()) + colortable = QImage(":defaultshaders/identitytable.png"); + if(sizetable.isNull()) + sizetable = QImage(":defaultshaders/identitytable.png"); + if(opacitytable.isNull()) + opacitytable = QImage(":defaultshaders/defaultFadeInOut.png"); + Q_ASSERT(!colortable.isNull()); + Q_ASSERT(!sizetable.isNull()); + Q_ASSERT(!opacitytable.isNull()); + m_material->colortable = sceneGraphEngine()->createTextureFromImage(colortable); + m_material->sizetable = sceneGraphEngine()->createTextureFromImage(sizetable); + m_material->opacitytable = sceneGraphEngine()->createTextureFromImage(opacitytable); + + m_material->texture = sceneGraphEngine()->createTextureFromImage(image); + m_material->texture->setFiltering(QSGTexture::Linear); + + m_material->framecount = 1; + if(m_spriteEngine){ + m_material->framecount = m_spriteEngine->maxFrames(); + m_spriteEngine->setCount(m_count); + } + + m_node = new QSGGeometryNode(); + m_node->setGeometry(g); + m_node->setMaterial(m_material); + + m_last_particle = 0; + + return m_node; +} + +QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) +{ + if(m_pleaseReset){ + if(m_node){ + if(perfLevel == 1){ + qDebug() << "Beta"; + m_lastData = qMalloc(m_count*sizeof(SimpleVertices));//TODO: Account for count_changed possibility + memcpy(m_lastData, m_node->geometry()->vertexData(), m_count * sizeof(SimpleVertices));//TODO: Multiple levels + } + m_lastLevel = perfLevel; + delete m_node; + } + if(m_material) + delete m_material; + + m_node = 0; + m_material = 0; + m_pleaseReset = false; + } + + if(m_system && m_system->isRunning()) + prepareNextFrame(); + if (m_node){ + update(); + m_node->markDirty(QSGNode::DirtyMaterial); + } + + return m_node; +} + +void QSGImageParticle::prepareNextFrame() +{ + if (m_node == 0){ //TODO: Staggered loading (as emitted) + m_node = buildParticleNode(); + if(m_node == 0) + return; + qDebug() << "Feature level: " << perfLevel; + } + qint64 timeStamp = m_system->systemSync(this); + + qreal time = timeStamp / 1000.; + m_material->timestamp = time; + + //Advance State + if(m_spriteEngine){//perfLevel == Sprites? + m_material->animcount = m_spriteEngine->spriteCount(); + UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData(); + m_spriteEngine->updateSprites(timeStamp); + for(int i=0; i<m_count; i++){ + UltraVertices &p = particles[i]; + int curIdx = m_spriteEngine->spriteState(i); + 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->spriteFrames(i); + p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(i); + } + } + }else{ + m_material->animcount = 1; + } +} + +template <typename VT> +IntermediateVertices* transplant(IntermediateVertices* iv, VT &v) +{//Deliberate typemangling cast + iv->v1 = (UltraVertex*)&(v.v1); + iv->v2 = (UltraVertex*)&(v.v2); + iv->v3 = (UltraVertex*)&(v.v3); + iv->v4 = (UltraVertex*)&(v.v4); + return iv; +} + +IntermediateVertices* QSGImageParticle::fetchIntermediateVertices(int pos) +{ + //Note that this class ruins typesafety for you. Maybe even thread safety. + //TODO: Something better, possibly with templates or inheritance + static IntermediateVertices iv; + SimpleVertices *sv; + UltraVertices *uv; + switch(perfLevel){ + case Simple: + sv = (SimpleVertices *) m_node->geometry()->vertexData(); + return transplant(&iv, sv[pos]); + case Coloured: + case Deformable: + case Tabled: + case Sprites: + default: + uv = (UltraVertices *) m_node->geometry()->vertexData(); + return transplant(&iv,uv[pos]); + } +} + +void QSGImageParticle::reloadColor(const Color4ub &c, QSGParticleData* d) +{ + UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData(); + int pos = particleTypeIndex(d); + UltraVertices &p = particles[pos]; + p.v1.color = p.v2.color = p.v3.color = p.v4.color = c; +} + +void QSGImageParticle::reload(QSGParticleData *d) +{ + if (m_node == 0) + return; + + int pos = particleTypeIndex(d); + IntermediateVertices* p = fetchIntermediateVertices(pos); + + //Perhaps we could be more efficient? + vertexCopy(*p->v1, d->pv); + vertexCopy(*p->v2, d->pv); + vertexCopy(*p->v3, d->pv); + vertexCopy(*p->v4, d->pv); +} + +void QSGImageParticle::load(QSGParticleData *d) +{ + if (m_node == 0) + return; + + int pos = particleTypeIndex(d); + IntermediateVertices* p = fetchIntermediateVertices(pos);//Remember this removes typesafety! + Color4ub color; + qreal redVariation = m_color_variation + m_redVariation; + qreal greenVariation = m_color_variation + m_greenVariation; + qreal blueVariation = m_color_variation + m_blueVariation; + switch(perfLevel){//Fall-through is intended on all of them + case Sprites: + // Initial Sprite State + 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){ + 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; + } + case Tabled: + case Deformable: + //Initial Rotation + if(m_xVector){ + const QPointF &ret = m_xVector->sample(QPointF(d->pv.x, d->pv.y)); + p->v1->xx = p->v2->xx = p->v3->xx = p->v4->xx = ret.x(); + p->v1->xy = p->v2->xy = p->v3->xy = p->v4->xy = ret.y(); + } + if(m_yVector){ + const QPointF &ret = m_yVector->sample(QPointF(d->pv.x, d->pv.y)); + p->v1->yx = p->v2->yx = p->v3->yx = p->v4->yx = ret.x(); + p->v1->yy = p->v2->yy = p->v3->yy = p->v4->yy = ret.y(); + } + p->v1->rotation = p->v2->rotation = p->v3->rotation = p->v4->rotation = + (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV; + p->v1->rotationSpeed = p->v2->rotationSpeed = p->v3->rotationSpeed = p->v4->rotationSpeed = + (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV; + p->v1->autoRotate = p->v2->autoRotate = p->v3->autoRotate = p->v4->autoRotate = m_autoRotation?1.0:0.0; + case Coloured: + //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; + p->v1->color = p->v2->color = p->v3->color = p->v4->color = color; + default: + break; + } + + vertexCopy(*p->v1, d->pv); + vertexCopy(*p->v2, d->pv); + vertexCopy(*p->v3, d->pv); + vertexCopy(*p->v4, d->pv); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgimageparticle_p.h b/src/declarative/particles/qsgimageparticle_p.h new file mode 100644 index 0000000000..c6ec4c2c6a --- /dev/null +++ b/src/declarative/particles/qsgimageparticle_p.h @@ -0,0 +1,352 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ULTRAPARTICLE_H +#define ULTRAPARTICLE_H +#include "qsgparticlepainter_p.h" +#include "qsgstochasticdirection_p.h" +#include <QDeclarativeListProperty> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class UltraMaterial; +class QSGGeometryNode; + +class QSGSprite; +class QSGSpriteEngine; + +struct Color4ub { + uchar r; + uchar g; + uchar b; + uchar a; +}; + +struct SimpleVertex { + float x; + float y; + float tx; + float ty; + float t; + float lifeSpan; + float size; + float endSize; + float sx; + float sy; + float ax; + float ay; +}; + +struct SimpleVertices { + SimpleVertex v1; + SimpleVertex v2; + SimpleVertex v3; + SimpleVertex v4; +}; + +struct UltraVertex { + float x; + float y; + float tx; + float ty; + float t; + float lifeSpan; + float size; + float endSize; + float sx; + float sy; + float ax; + float ay; + Color4ub color; + float xx; + float xy; + float yx; + float yy; + float rotation; + float rotationSpeed; + float autoRotate;//Assume that GPUs prefer floats to bools + float animIdx; + float frameDuration; + float frameCount; + float animT; +}; + +struct UltraVertices { + UltraVertex v1; + UltraVertex v2; + UltraVertex v3; + UltraVertex v4; +}; + +struct IntermediateVertices { + UltraVertex* v1; + UltraVertex* v2; + UltraVertex* v3; + UltraVertex* v4; +}; + +class QSGImageParticle : public QSGParticlePainter +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ image WRITE setImage NOTIFY imageChanged) + Q_PROPERTY(QUrl colorTable READ colortable WRITE setColortable NOTIFY colortableChanged) + Q_PROPERTY(QUrl sizeTable READ sizetable WRITE setSizetable NOTIFY sizetableChanged) + Q_PROPERTY(QUrl opacityTable READ opacitytable WRITE setOpacitytable NOTIFY opacitytableChanged) + + //###Now just colorize - add a flag for 'solid' color particles(where the img is just a mask?)? + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + //Stacks (added) with individual colorVariations + Q_PROPERTY(qreal colorVariation READ colorVariation WRITE setColorVariation NOTIFY colorVariationChanged) + Q_PROPERTY(qreal redVariation READ redVariation WRITE setRedVariation NOTIFY redVariationChanged) + Q_PROPERTY(qreal greenVariation READ greenVariation WRITE setGreenVariation NOTIFY greenVariationChanged) + Q_PROPERTY(qreal blueVariation READ blueVariation WRITE setBlueVariation NOTIFY blueVariationChanged) + //Stacks (multiplies) with the Alpha in the color, mostly here so you can use svg color names (which have full alpha) + Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged) + Q_PROPERTY(qreal alphaVariation READ alphaVariation WRITE setAlphaVariation NOTIFY alphaVariationChanged) + + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged) + Q_PROPERTY(qreal rotationVariation READ rotationVariation WRITE setRotationVariation NOTIFY rotationVariationChanged) + Q_PROPERTY(qreal rotationSpeed READ rotationSpeed WRITE setRotationSpeed NOTIFY rotationSpeedChanged) + Q_PROPERTY(qreal rotationSpeedVariation READ rotationSpeedVariation WRITE setRotationSpeedVariation NOTIFY rotationSpeedVariationChanged) + //If true, then will face the direction of motion. Stacks with rotation, e.g. setting rotation + //to 180 will lead to facing away from the direction of motion + Q_PROPERTY(bool autoRotation READ autoRotation WRITE setAutoRotation NOTIFY autoRotationChanged) + + //###Call i/j? Makes more sense to those with vector calculus experience, and I could even add the cirumflex in QML? + //xVector is the vector from the top-left point to the top-right point, and is multiplied by current size + Q_PROPERTY(QSGStochasticDirection* xVector READ xVector WRITE setXVector NOTIFY xVectorChanged) + //yVector is the same, but top-left to bottom-left. The particle is always a parallelogram. + Q_PROPERTY(QSGStochasticDirection* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged) + Q_PROPERTY(QDeclarativeListProperty<QSGSprite> sprites READ sprites) + Q_PROPERTY(bool bloat READ bloat WRITE setBloat NOTIFY bloatChanged)//Just a debugging property to bypass optimizations +public: + explicit QSGImageParticle(QSGItem *parent = 0); + virtual ~QSGImageParticle(){} + + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); + virtual void setCount(int c); + + QDeclarativeListProperty<QSGSprite> sprites(); + QSGSpriteEngine* spriteEngine() {return m_spriteEngine;} + + enum PerformanceLevel{//TODO: Expose? + Unknown = 0, + Simple, + Coloured, + Deformable, + Tabled, + Sprites + }; + + QUrl image() const { return m_image_name; } + void setImage(const QUrl &image); + + QUrl colortable() const { return m_colortable_name; } + void setColortable(const QUrl &table); + + QUrl sizetable() const { return m_sizetable_name; } + void setSizetable (const QUrl &table); + + QUrl opacitytable() const { return m_opacitytable_name; } + void setOpacitytable(const QUrl &table); + + QColor color() const { return m_color; } + void setColor(const QColor &color); + + qreal colorVariation() const { return m_color_variation; } + void setColorVariation(qreal var); + + qreal renderOpacity() const { return m_render_opacity; } + + qreal alphaVariation() const { return m_alphaVariation; } + + qreal alpha() const { return m_alpha; } + + qreal redVariation() const { return m_redVariation; } + + qreal greenVariation() const { return m_greenVariation; } + + qreal blueVariation() const { return m_blueVariation; } + + qreal rotation() const { return m_rotation; } + + qreal rotationVariation() const { return m_rotationVariation; } + + qreal rotationSpeed() const { return m_rotationSpeed; } + + qreal rotationSpeedVariation() const { return m_rotationSpeedVariation; } + + bool autoRotation() const { return m_autoRotation; } + + QSGStochasticDirection* xVector() const { return m_xVector; } + + QSGStochasticDirection* yVector() const { return m_yVector; } + + bool bloat() const { return m_bloat; } + +signals: + + void imageChanged(); + void colortableChanged(); + void sizetableChanged(); + void opacitytableChanged(); + + void colorChanged(); + void colorVariationChanged(); + + void particleDurationChanged(); + void alphaVariationChanged(qreal arg); + + void alphaChanged(qreal arg); + + void redVariationChanged(qreal arg); + + void greenVariationChanged(qreal arg); + + void blueVariationChanged(qreal arg); + + void rotationChanged(qreal arg); + + void rotationVariationChanged(qreal arg); + + void rotationSpeedChanged(qreal arg); + + void rotationSpeedVariationChanged(qreal arg); + + void autoRotationChanged(bool arg); + + void xVectorChanged(QSGStochasticDirection* arg); + + void yVectorChanged(QSGStochasticDirection* arg); + + void bloatChanged(bool arg); + +public slots: + void reloadColor(const Color4ub &c, QSGParticleData* d); + void setAlphaVariation(qreal arg); + + void setAlpha(qreal arg); + + void setRedVariation(qreal arg); + + void setGreenVariation(qreal arg); + + void setBlueVariation(qreal arg); + + void setRotation(qreal arg); + + void setRotationVariation(qreal arg); + + void setRotationSpeed(qreal arg); + + void setRotationSpeedVariation(qreal arg); + + void setAutoRotation(bool arg); + + void setXVector(QSGStochasticDirection* arg); + + void setYVector(QSGStochasticDirection* arg); + + void setBloat(bool arg); + +protected: + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + void reset(); + void prepareNextFrame(); + QSGGeometryNode* buildParticleNode(); + QSGGeometryNode* buildSimpleParticleNode(); + +private slots: + void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty + +private: + //void vertexCopy(UltraVertex &b,const ParticleVertex& a); + IntermediateVertices* fetchIntermediateVertices(int pos); + bool m_do_reset; + + QUrl m_image_name; + QUrl m_colortable_name; + QUrl m_sizetable_name; + QUrl m_opacitytable_name; + + + QColor m_color; + qreal m_color_variation; + qreal m_particleDuration; + + QSGGeometryNode *m_node; + UltraMaterial *m_material; + + // derived values... + int m_last_particle; + + qreal m_render_opacity; + qreal m_alphaVariation; + qreal m_alpha; + qreal m_redVariation; + qreal m_greenVariation; + qreal m_blueVariation; + qreal m_rotation; + qreal m_rotationVariation; + qreal m_rotationSpeed; + qreal m_rotationSpeedVariation; + bool m_autoRotation; + QSGStochasticDirection* m_xVector; + QSGStochasticDirection* m_yVector; + + QList<QSGSprite*> m_sprites; + QSGSpriteEngine* m_spriteEngine; + + bool m_bloat; + PerformanceLevel perfLevel; + + PerformanceLevel m_lastLevel; + void* m_lastData; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // ULTRAPARTICLE_H diff --git a/src/declarative/particles/qsgitemparticle.cpp b/src/declarative/particles/qsgitemparticle.cpp new file mode 100644 index 0000000000..819c823155 --- /dev/null +++ b/src/declarative/particles/qsgitemparticle.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgitemparticle_p.h" +#include <QtDeclarative/private/qsgvisualitemmodel_p.h> +#include <qsgnode.h> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +QSGItemParticle::QSGItemParticle(QSGItem *parent) : + QSGParticlePainter(parent), m_fade(true) +{ + setFlag(QSGItem::ItemHasContents); +} + + +void QSGItemParticle::freeze(QSGItem* item) +{ + m_stasis << item; +} + + +void QSGItemParticle::unfreeze(QSGItem* item) +{ + m_stasis.remove(item); +} + +void QSGItemParticle::take(QSGItem *item, bool prioritize) +{ + if(prioritize) + m_pendingItems.push_front(item); + else + m_pendingItems.push_back(item); +} + +void QSGItemParticle::give(QSGItem *item) +{ + //TODO: This +} + +void QSGItemParticle::load(QSGParticleData* d) +{ + if(m_pendingItems.isEmpty()) + return; + int pos = particleTypeIndex(d); + if(m_items[pos]){ + if(m_stasis.contains(m_items[pos])) + qWarning() << "Current model particles prefers overwrite:false"; + //remove old item from the particle that is dying to make room for this one + m_items[pos]->setOpacity(0.); + QSGItemParticleAttached* mpa; + if((mpa = qobject_cast<QSGItemParticleAttached*>(qmlAttachedPropertiesObject<QSGItemParticle>(m_items[pos], false)))) + mpa->detach();//reparent as well? + m_items[pos] = 0; + m_data[pos] = 0; + m_activeCount--; + } + m_items[pos] = m_pendingItems.front(); + m_pendingItems.pop_front(); + m_items[pos]->setX(d->curX() - m_items[pos]->width()/2); + m_items[pos]->setY(d->curY() - m_items[pos]->height()/2); + QSGItemParticleAttached* mpa = qobject_cast<QSGItemParticleAttached*>(qmlAttachedPropertiesObject<QSGItemParticle>(m_items[pos])); + if(mpa){ + mpa->m_mp = this; + mpa->attach(); + } + m_items[pos]->setParentItem(this); + m_data[pos] = d; + m_activeCount++; +} + +void QSGItemParticle::reload(QSGParticleData* d) +{ + //No-op unless we start copying the data. +} + +void QSGItemParticle::setCount(int c) +{ + QSGParticlePainter::setCount(c);//###Do we need our own? + m_particleCount = c; + reset(); +} + +int QSGItemParticle::count() +{ + return m_particleCount; +} + +void QSGItemParticle::reset() +{ + QSGParticlePainter::reset(); + //TODO: Cleanup items? + m_items.resize(m_particleCount); + m_data.resize(m_particleCount); + m_items.fill(0); + m_data.fill(0); + //m_pendingItems.clear();//TODO: Should this be done? If so, Emit signal? +} + + +QSGNode* QSGItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d) +{ + //Dummy update just to get painting tick + if(m_pleaseReset){ + m_pleaseReset = false; + reset(); + } + prepareNextFrame(); + + update();//Get called again + if(n) + n->markDirty(QSGNode::DirtyMaterial); + return QSGItem::updatePaintNode(n,d); +} + +void QSGItemParticle::prepareNextFrame() +{ + qint64 timeStamp = m_system->systemSync(this); + qreal curT = timeStamp/1000.0; + qreal dt = curT - m_lastT; + m_lastT = curT; + if(!m_activeCount) + return; + + //TODO: Size, better fade? + for(int i=0; i<m_particleCount; i++){ + QSGItem* item = m_items[i]; + QSGParticleData* data = m_data[i]; + if(!item || !data) + continue; + qreal t = ((timeStamp/1000.0) - data->pv.t) / data->pv.lifeSpan; + if(m_stasis.contains(item)) { + m_data[i]->pv.t += dt;//Stasis effect + continue; + } + if(t >= 1.0){//Usually happens from load + item->setOpacity(0.); + QSGItemParticleAttached* mpa; + if((mpa = qobject_cast<QSGItemParticleAttached*>(qmlAttachedPropertiesObject<QSGItemParticle>(m_items[i])))) + mpa->detach();//reparent as well? + m_items[i] = 0; + m_data[i] = 0; + m_activeCount--; + }else{//Fade + if(m_fade){ + qreal o = 1.; + if(t<0.2) + o = t*5; + if(t>0.8) + o = (1-t)*5; + item->setOpacity(o); + }else{ + item->setOpacity(1.);//###Without fade, it's just a binary toggle - if we turn it off we have to turn it back on + } + } + item->setX(data->curX() - item->width()/2); + item->setY(data->curY() - item->height()/2); + } +} + +QSGItemParticleAttached *QSGItemParticle::qmlAttachedProperties(QObject *object) +{ + return new QSGItemParticleAttached(object); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgitemparticle_p.h b/src/declarative/particles/qsgitemparticle_p.h new file mode 100644 index 0000000000..fa3516b631 --- /dev/null +++ b/src/declarative/particles/qsgitemparticle_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ITEMPARTICLE_H +#define ITEMPARTICLE_H +#include "qsgparticlepainter_p.h" +#include <QPointer> +#include <QSet> +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QSGVisualDataModel; +class QSGItemParticleAttached; + +class QSGItemParticle : public QSGParticlePainter +{ + Q_OBJECT + + Q_PROPERTY(bool fade READ fade WRITE setFade NOTIFY fadeChanged) +public: + explicit QSGItemParticle(QSGItem *parent = 0); + + bool fade() const { return m_fade; } + + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); + virtual void setCount(int c); + virtual int count(); + + static QSGItemParticleAttached *qmlAttachedProperties(QObject *object); +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 + void give(QSGItem* item);//give from modelparticle + + void setFade(bool arg){if(arg == m_fade) return; m_fade = arg; emit fadeChanged();} +protected: + virtual void reset(); + void prepareNextFrame(); +private: + QList<QPointer<QSGItem> > m_deletables; + int m_particleCount; + bool m_fade; + + QList<QSGItem*> m_pendingItems; + QVector<QSGItem*> m_items; + QVector<QSGParticleData*> m_data; + QVector<int> m_idx; + QList<int> m_available; + QSet<QSGItem*> m_stasis; + qreal m_lastT; + int m_activeCount; +}; + +class QSGItemParticleAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QSGItemParticle* particle READ particle CONSTANT); +public: + QSGItemParticleAttached(QObject* parent) + : QObject(parent), m_mp(0) + {;} + QSGItemParticle* particle() {return m_mp;} + void detach(){emit detached();} + void attach(){emit attached();} +private: + QSGItemParticle* m_mp; + friend class QSGItemParticle; +Q_SIGNALS: + void detached(); + void attached(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPEINFO(QSGItemParticle, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER +#endif // ITEMPARTICLE_H diff --git a/src/declarative/particles/qsgkill.cpp b/src/declarative/particles/qsgkill.cpp new file mode 100644 index 0000000000..1321898dc9 --- /dev/null +++ b/src/declarative/particles/qsgkill.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgkill_p.h" +#include "qsgparticleemitter_p.h" +QT_BEGIN_NAMESPACE +QSGKillAffector::QSGKillAffector(QSGItem *parent) : + QSGParticleAffector(parent) +{ +} + + +bool QSGKillAffector::affectParticle(QSGParticleData *d, qreal dt) +{ + Q_UNUSED(dt); + if(d->stillAlive()){ + d->pv.t -= d->pv.lifeSpan + 1; + return true; + } +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgkill_p.h b/src/declarative/particles/qsgkill_p.h new file mode 100644 index 0000000000..1b24b2fb40 --- /dev/null +++ b/src/declarative/particles/qsgkill_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef KILLAFFECTOR_H +#define KILLAFFECTOR_H +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGKillAffector : public QSGParticleAffector +{ + Q_OBJECT +public: + explicit QSGKillAffector(QSGItem *parent = 0); +protected: + virtual bool affectParticle(QSGParticleData *d, qreal dt); +signals: + +public slots: + +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // KILLAFFECTOR_H diff --git a/src/declarative/particles/qsglineextruder.cpp b/src/declarative/particles/qsglineextruder.cpp new file mode 100644 index 0000000000..f32b01402a --- /dev/null +++ b/src/declarative/particles/qsglineextruder.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qsglineextruder_p.h" +#include <cmath> + +QSGLineExtruder::QSGLineExtruder(QObject *parent) : + QSGParticleExtruder(parent), m_mirrored(false) +{ +} + +QPointF QSGLineExtruder::extrude(const QRectF &r) +{ + qreal x,y; + if(!r.height()){ + x = r.width() * ((qreal)rand())/RAND_MAX; + y = 0; + }else{ + y = r.height() * ((qreal)rand())/RAND_MAX; + if(!r.width()){ + x = 0; + }else{ + x = r.width()/r.height() * y; + if(m_mirrored) + x = r.width() - x; + } + } + return QPointF(x,y); +} diff --git a/src/declarative/particles/qsglineextruder_p.h b/src/declarative/particles/qsglineextruder_p.h new file mode 100644 index 0000000000..f356ca332a --- /dev/null +++ b/src/declarative/particles/qsglineextruder_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LINEEXTRUDER_H +#define LINEEXTRUDER_H +#include "qsgparticleextruder_p.h" + +class QSGLineExtruder : public QSGParticleExtruder +{ + Q_OBJECT + //Default is topleft to bottom right. Flipped makes it topright to bottom left + Q_PROPERTY(bool mirrored READ mirrored WRITE setmirrored NOTIFY mirroredChanged) + +public: + explicit QSGLineExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + bool mirrored() const + { + return m_mirrored; + } + +signals: + + void mirroredChanged(bool arg); + +public slots: + + void setmirrored(bool arg) + { + if (m_mirrored != arg) { + m_mirrored = arg; + emit mirroredChanged(arg); + } + } +private: + bool m_mirrored; +}; + +#endif // LINEEXTRUDER_H diff --git a/src/declarative/particles/qsgmaskextruder.cpp b/src/declarative/particles/qsgmaskextruder.cpp new file mode 100644 index 0000000000..d9a463910d --- /dev/null +++ b/src/declarative/particles/qsgmaskextruder.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgmaskextruder_p.h" +#include <QImage> +#include <QDebug> +QT_BEGIN_NAMESPACE +QSGMaskExtruder::QSGMaskExtruder(QObject *parent) : + QSGParticleExtruder(parent) + , m_lastWidth(-1) + , m_lastHeight(-1) +{ +} + +QPointF QSGMaskExtruder::extrude(const QRectF &r) +{ + ensureInitialized(r); + if(!m_mask.count() || m_img.isNull()) + return r.topLeft(); + const QPointF p = m_mask[rand() % m_mask.count()]; + //### Should random sub-pixel positioning be added? + return p + r.topLeft(); +} + +bool QSGMaskExtruder::contains(const QRectF &bounds, const QPointF &point) +{ + ensureInitialized(bounds);//###Current usage patterns WILL lead to different bounds/r calls. Separate list? + if(m_img.isNull()) + return false; + QPoint p = point.toPoint() - bounds.topLeft().toPoint(); + return m_img.rect().contains(p) && (bool)m_img.pixelIndex(p); +} + +void QSGMaskExtruder::ensureInitialized(const QRectF &r) +{ + if(m_lastWidth == r.width() && m_lastHeight == r.height()) + return;//Same as before + m_lastWidth = r.width(); + m_lastHeight = r.height(); + + m_img = QImage(); + m_mask.clear(); + if(m_source.isEmpty()) + return; + m_img = QImage(m_source.toLocalFile()); + if(m_img.isNull()){ + qWarning() << "MaskShape: Cannot load" << qPrintable(m_source.toLocalFile()); + return; + } + m_img = m_img.createAlphaMask(); + m_img = m_img.convertToFormat(QImage::Format_Mono);//Else LSB, but I think that's easier + m_img = m_img.scaled(r.size().toSize());//TODO: Do they need aspect ratio stuff? Or tiling? + for(int i=0; i<r.width(); i++){ + for(int j=0; j<r.height(); j++){ + if(m_img.pixelIndex(i,j))//Direct bit manipulation is presumably more efficient + m_mask << QPointF(i,j); + } + } +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgmaskextruder_p.h b/src/declarative/particles/qsgmaskextruder_p.h new file mode 100644 index 0000000000..b564efa6dc --- /dev/null +++ b/src/declarative/particles/qsgmaskextruder_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MASKEXTRUDER_H +#define MASKEXTRUDER_H +#include "qsgparticleextruder_p.h" +#include <QUrl> +#include <QImage> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGMaskExtruder : public QSGParticleExtruder +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) +public: + explicit QSGMaskExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + virtual bool contains(const QRectF &bounds, const QPointF &point); + + QUrl source() const + { + return m_source; + } + +signals: + + void sourceChanged(QUrl arg); + +public slots: + + void setSource(QUrl arg) + { + if (m_source != arg) { + m_source = arg; + m_lastHeight = -1;//Trigger reset + m_lastWidth = -1; + emit sourceChanged(arg); + } + } +private: + QUrl m_source; + + void ensureInitialized(const QRectF &r); + int m_lastWidth; + int m_lastHeight; + QImage m_img; + QList<QPointF> m_mask;//TODO: More memory efficient datastructures +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // MASKEXTRUDER_H diff --git a/src/declarative/particles/qsgmodelparticle.cpp b/src/declarative/particles/qsgmodelparticle.cpp new file mode 100644 index 0000000000..94ce082c9d --- /dev/null +++ b/src/declarative/particles/qsgmodelparticle.cpp @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgmodelparticle_p.h" +#include <QtDeclarative/private/qsgvisualitemmodel_p.h> +#include <qsgnode.h> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +QSGModelParticle::QSGModelParticle(QSGItem *parent) : + QSGParticlePainter(parent), m_ownModel(false), m_comp(0), m_model(0), m_fade(true), m_modelCount(0) +{ + setFlag(QSGItem::ItemHasContents); +} + +QVariant QSGModelParticle::model() const +{ + return m_dataSource; +} + +void QSGModelParticle::setModel(const QVariant &arg) +{ + if(arg == m_dataSource) + return; + m_dataSource = arg; + if(qobject_cast<QSGVisualDataModel*>(arg.value<QObject*>())) { + if(m_ownModel && m_model) + delete m_model; + m_model = qobject_cast<QSGVisualDataModel*>(arg.value<QObject*>()); + m_ownModel = false; + }else{ + if(!m_model || !m_ownModel) + m_model = new QSGVisualDataModel(qmlContext(this)); + m_model->setModel(m_dataSource); + m_ownModel = true; + } + if(m_comp) + m_model->setDelegate(m_comp); + emit modelChanged(); + emit modelCountChanged(); + connect(m_model, SIGNAL(countChanged()), + this, SIGNAL(modelCountChanged())); + connect(m_model, SIGNAL(countChanged()), + this, SLOT(updateCount())); + updateCount(); +} + +void QSGModelParticle::updateCount() +{ + int newCount = 0; + if(m_model) + newCount = m_model->count(); + if(newCount < 0) + return;//WTF? + if(m_modelCount == 0 || newCount == 0){ + m_available.clear(); + for(int i=0; i<newCount; i++) + m_available << i; + }else if(newCount < m_modelCount){ + for(int i=newCount; i<m_modelCount; i++) //existing ones must leave normally, but aren't readded + m_available.removeAll(i); + }else if(newCount > m_modelCount){ + for(int i=m_modelCount; i<newCount; i++) + m_available << i; + } + m_modelCount = newCount; +} + +QDeclarativeComponent *QSGModelParticle::delegate() const +{ + if(m_model) + return m_model->delegate(); + return 0; +} + +void QSGModelParticle::setDelegate(QDeclarativeComponent *comp) +{ + if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(m_model)) + if (comp == dataModel->delegate()) + return; + m_comp = comp; + if(m_model) + m_model->setDelegate(comp); + emit delegateChanged(); +} + +int QSGModelParticle::modelCount() const +{ + if(m_model) + const_cast<QSGModelParticle*>(this)->updateCount();//TODO: Investigate why this doesn't get called properly + return m_modelCount; +} + + +void QSGModelParticle::freeze(QSGItem* item) +{ + m_stasis << item; +} + + +void QSGModelParticle::unfreeze(QSGItem* item) +{ + m_stasis.remove(item); +} + +void QSGModelParticle::load(QSGParticleData* d) +{ + if(!m_model || !m_model->count()) + return; + int pos = particleTypeIndex(d); + if(m_available.isEmpty()) + return; + if(m_items[pos]){ + if(m_stasis.contains(m_items[pos])) + qWarning() << "Current model particles prefers overwrite:false"; + //remove old item from the particle that is dying to make room for this one + m_items[pos]->setOpacity(0.); + m_available << m_idx[pos]; + m_model->release(m_items[pos]); + m_idx[pos] = -1; + m_items[pos] = 0; + m_data[pos] = 0; + m_activeCount--; + } + m_items[pos] = m_model->item(m_available.first()); + m_idx[pos] = m_available.first(); + m_available.pop_front(); + QSGModelParticleAttached* mpa = qobject_cast<QSGModelParticleAttached*>(qmlAttachedPropertiesObject<QSGModelParticle>(m_items[pos])); + if(mpa){ + mpa->m_mp = this; + mpa->attach(); + } + m_items[pos]->setParentItem(this); + m_data[pos] = d; + m_activeCount++; +} + +void QSGModelParticle::reload(QSGParticleData* d) +{ + //No-op unless we start copying the data. +} + +void QSGModelParticle::setCount(int c) +{ + QSGParticlePainter::setCount(c);//###Do we need our own? + m_particleCount = c; + reset(); +} + +int QSGModelParticle::count() +{ + return m_particleCount; +} + +void QSGModelParticle::reset() +{ + QSGParticlePainter::reset(); + //TODO: Cleanup items? + m_items.resize(m_particleCount); + m_data.resize(m_particleCount); + m_idx.resize(m_particleCount); + m_items.fill(0); + m_data.fill(0); + m_idx.fill(-1); + //m_available.clear();//Should this be reset too? + //m_pendingItems.clear();//TODO: Should this be done? If so, Emit signal? +} + + +QSGNode* QSGModelParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d) +{ + //Dummy update just to get painting tick + if(m_pleaseReset){ + m_pleaseReset = false; + reset(); + } + prepareNextFrame(); + + update();//Get called again + if(n) + n->markDirty(QSGNode::DirtyMaterial); + return QSGItem::updatePaintNode(n,d); +} + +void QSGModelParticle::prepareNextFrame() +{ + qint64 timeStamp = m_system->systemSync(this); + qreal curT = timeStamp/1000.0; + qreal dt = curT - m_lastT; + m_lastT = curT; + if(!m_activeCount) + return; + + //TODO: Size, better fade? + for(int i=0; i<m_particleCount; i++){ + QSGItem* item = m_items[i]; + QSGParticleData* data = m_data[i]; + if(!item || !data) + continue; + qreal t = ((timeStamp/1000.0) - data->pv.t) / data->pv.lifeSpan; + if(m_stasis.contains(item)) { + m_data[i]->pv.t += dt;//Stasis effect + continue; + } + if(t >= 1.0){//Usually happens from load + item->setOpacity(0.); + m_available << m_idx[i]; + m_model->release(m_items[i]); + m_idx[i] = -1; + m_items[i] = 0; + m_data[i] = 0; + m_activeCount--; + }else{//Fade + if(m_fade){ + qreal o = 1.; + if(t<0.2) + o = t*5; + if(t>0.8) + o = (1-t)*5; + item->setOpacity(o); + }else{ + item->setOpacity(1.);//###Without fade, it's just a binary toggle - if we turn it off we have to turn it back on + } + } + item->setX(data->curX() - item->width()/2); + item->setY(data->curY() - item->height()/2); + } +} + +QSGModelParticleAttached *QSGModelParticle::qmlAttachedProperties(QObject *object) +{ + return new QSGModelParticleAttached(object); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgmodelparticle_p.h b/src/declarative/particles/qsgmodelparticle_p.h new file mode 100644 index 0000000000..4dd8bfa710 --- /dev/null +++ b/src/declarative/particles/qsgmodelparticle_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DATAPARTICLE_H +#define DATAPARTICLE_H +#include "qsgparticlepainter_p.h" +#include <QPointer> +#include <QSet> +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QSGVisualDataModel; +class QSGModelParticleAttached; + +class QSGModelParticle : public QSGParticlePainter +{ + Q_OBJECT + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(int modelCount READ modelCount NOTIFY modelCountChanged) + Q_PROPERTY(bool fade READ fade WRITE setFade NOTIFY fadeChanged) + Q_CLASSINFO("DefaultProperty", "delegate") +public: + explicit QSGModelParticle(QSGItem *parent = 0); + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int modelCount() const; + + bool fade() const { return m_fade; } + + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); + virtual void setCount(int c); + virtual int count(); + + static QSGModelParticleAttached *qmlAttachedProperties(QObject *object); +signals: + void modelChanged(); + void delegateChanged(); + void modelCountChanged(); + void fadeChanged(); + +public slots: + void freeze(QSGItem* item); + void unfreeze(QSGItem* item); + + void setFade(bool arg){if(arg == m_fade) return; m_fade = arg; emit fadeChanged();} +protected: + virtual void reset(); + void prepareNextFrame(); +private slots: + void updateCount(); +private: + bool m_ownModel; + QDeclarativeComponent* m_comp; + QSGVisualDataModel *m_model; + QVariant m_dataSource; + QList<QPointer<QSGItem> > m_deletables; + int m_particleCount; + bool m_fade; + + QList<QSGItem*> m_pendingItems; + QVector<QSGItem*> m_items; + QVector<QSGParticleData*> m_data; + QVector<int> m_idx; + QList<int> m_available; + QSet<QSGItem*> m_stasis; + qreal m_lastT; + int m_activeCount; + int m_modelCount; +}; + +class QSGModelParticleAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QSGModelParticle* particle READ particle CONSTANT); +public: + QSGModelParticleAttached(QObject* parent) + : QObject(parent), m_mp(0) + {;} + QSGModelParticle* particle() {return m_mp;} + void detach(){emit detached();} + void attach(){emit attached();} +private: + QSGModelParticle* m_mp; + friend class QSGModelParticle; +Q_SIGNALS: + void detached(); + void attached(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPEINFO(QSGModelParticle, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER +#endif // DATAPARTICLE_H diff --git a/src/declarative/particles/qsgparticleaffector.cpp b/src/declarative/particles/qsgparticleaffector.cpp new file mode 100644 index 0000000000..96c5cfb25b --- /dev/null +++ b/src/declarative/particles/qsgparticleaffector.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgparticleaffector_p.h" +#include <QDebug> +QT_BEGIN_NAMESPACE +QSGParticleAffector::QSGParticleAffector(QSGItem *parent) : + QSGItem(parent), m_needsReset(false), m_system(0), m_active(true) + , m_updateIntSet(false), m_shape(new QSGParticleExtruder(this)), m_signal(false) +{ + connect(this, SIGNAL(systemChanged(QSGParticleSystem*)), + this, SLOT(updateOffsets())); + connect(this, SIGNAL(xChanged()), + this, SLOT(updateOffsets())); + connect(this, SIGNAL(yChanged()), + this, SLOT(updateOffsets()));//TODO: in componentComplete and all relevant signals +} + +void QSGParticleAffector::componentComplete() +{ + if(!m_system && qobject_cast<QSGParticleSystem*>(parentItem())) + setSystem(qobject_cast<QSGParticleSystem*>(parentItem())); + if(!m_system) + qWarning() << "Affector created without a particle system specified";//TODO: useful QML warnings, like line number? + QSGItem::componentComplete(); +} + +void QSGParticleAffector::affectSystem(qreal dt) +{ + if(!m_active) + return; + if(!m_system){ + qDebug() << "No system" << this; + return; + } + //If not reimplemented, calls affect particle per particle + //But only on particles in targeted system/area + if(m_updateIntSet){ + m_groups.clear(); + foreach(const QString &p, m_particles) + m_groups << m_system->m_groupIds[p];//###Can this occur before group ids are properly assigned? + m_updateIntSet = false; + } + //foreach(ParticleData* d, m_system->m_data){ + for(int i=0; i<m_system->m_particle_count; i++){ + QSGParticleData* d = m_system->m_data[i]; + if(!d || (m_onceOff && m_onceOffed.contains(d->systemIndex))) + continue; + if(m_groups.isEmpty() || m_groups.contains(d->group)){ + //Need to have previous location for affected. if signal || shape might be faster? + QPointF curPos = QPointF(d->curX(), d->curY()); + if(width() == 0 || height() == 0 + || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()),curPos)){ + if(affectParticle(d, dt)){ + m_system->m_needsReset << d; + if(m_onceOff) + m_onceOffed << d->systemIndex; + if(m_signal) + emit affected(curPos.x(), curPos.y()); + } + } + } + } +} + +bool QSGParticleAffector::affectParticle(QSGParticleData *d, qreal dt) +{ + Q_UNUSED(d); + Q_UNUSED(dt); + return false; +} + +void QSGParticleAffector::reset(int idx) +{//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass + if(m_onceOff) + m_onceOffed.remove(idx); +} + +void QSGParticleAffector::updateOffsets() +{ + if(m_system) + m_offset = m_system->mapFromItem(this, QPointF(0, 0)); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgparticleaffector_p.h b/src/declarative/particles/qsgparticleaffector_p.h new file mode 100644 index 0000000000..b299158069 --- /dev/null +++ b/src/declarative/particles/qsgparticleaffector_p.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PARTICLEAFFECTOR_H +#define PARTICLEAFFECTOR_H + +#include <QObject> +#include "qsgparticlesystem_p.h" +#include "qsgparticleextruder_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGParticleAffector : public QSGItem +{ + Q_OBJECT + Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + Q_PROPERTY(QStringList particles READ particles WRITE setParticles NOTIFY particlesChanged) + Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) + Q_PROPERTY(bool onceOff READ onceOff WRITE setOnceOff NOTIFY onceOffChanged) + Q_PROPERTY(QSGParticleExtruder* shape READ shape WRITE setShape NOTIFY shapeChanged) + Q_PROPERTY(bool signal READ signal WRITE setSignal NOTIFY signalChanged) + +public: + explicit QSGParticleAffector(QSGItem *parent = 0); + virtual void affectSystem(qreal dt); + virtual void reset(int systemIdx);//As some store their own data per idx? + QSGParticleSystem* system() const + { + return m_system; + } + + QStringList particles() const + { + return m_particles; + } + + bool active() const + { + return m_active; + } + + bool onceOff() const + { + return m_onceOff; + } + + QSGParticleExtruder* shape() const + { + return m_shape; + } + + bool signal() const + { + return m_signal; + } + +signals: + + void systemChanged(QSGParticleSystem* arg); + + void particlesChanged(QStringList arg); + + void activeChanged(bool arg); + + void onceOffChanged(bool arg); + + void shapeChanged(QSGParticleExtruder* arg); + + void affected(qreal x, qreal y);//###Idx too? + void signalChanged(bool arg); + +public slots: +void setSystem(QSGParticleSystem* arg) +{ + if (m_system != arg) { + m_system = arg; + m_system->registerParticleAffector(this); + emit systemChanged(arg); + } +} + +void setParticles(QStringList arg) +{ + if (m_particles != arg) { + m_particles = arg; + m_updateIntSet = true; + emit particlesChanged(arg); + } +} + +void setActive(bool arg) +{ + if (m_active != arg) { + m_active = arg; + emit activeChanged(arg); + } +} + +void setOnceOff(bool arg) +{ + if (m_onceOff != arg) { + m_onceOff = arg; + emit onceOffChanged(arg); + } +} + +void setShape(QSGParticleExtruder* arg) +{ + if (m_shape != arg) { + m_shape = arg; + emit shapeChanged(arg); + } +} + +void setSignal(bool arg) +{ + if (m_signal != arg) { + m_signal = arg; + emit signalChanged(arg); + } +} + +protected: + friend class QSGParticleSystem; + virtual bool affectParticle(QSGParticleData *d, qreal dt); + bool m_needsReset;//### What is this really saving? + QSGParticleSystem* m_system; + QStringList m_particles; + bool activeGroup(int g) {return m_groups.isEmpty() || m_groups.contains(g);} + bool m_active; + virtual void componentComplete(); + QPointF m_offset; +private: + QSet<int> m_groups; + QSet<int> m_onceOffed; + bool m_updateIntSet; + + bool m_onceOff; + + QSGParticleExtruder* m_shape; + + bool m_signal; + +private slots: + void updateOffsets(); +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // PARTICLEAFFECTOR_H diff --git a/src/declarative/particles/qsgparticleemitter.cpp b/src/declarative/particles/qsgparticleemitter.cpp new file mode 100644 index 0000000000..20f3c6bb4b --- /dev/null +++ b/src/declarative/particles/qsgparticleemitter.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgparticleemitter_p.h" +QT_BEGIN_NAMESPACE +QSGParticleEmitter::QSGParticleEmitter(QSGItem *parent) : + QSGItem(parent) + , m_particlesPerSecond(10) + , m_particleDuration(1000) + , m_particleDurationVariation(0) + , m_emitting(true) + , m_system(0) + , m_extruder(0) + , m_defaultExtruder(0) + , m_speed(&m_nullVector) + , m_acceleration(&m_nullVector) + , m_particleSize(16) + , m_particleEndSize(-1) + , m_particleSizeVariation(0) + , m_maxParticleCount(-1) + , m_burstLeft(0) + +{ + //TODO: Reset speed/acc back to null vector? Or allow null pointer? + connect(this, SIGNAL(maxParticleCountChanged(int)), + this, SIGNAL(particleCountChanged())); + connect(this, SIGNAL(particlesPerSecondChanged(qreal)), + this, SIGNAL(particleCountChanged())); + connect(this, SIGNAL(particleDurationChanged(int)), + this, SIGNAL(particleCountChanged())); +} + +QSGParticleEmitter::~QSGParticleEmitter() +{ + if(m_defaultExtruder) + delete m_defaultExtruder; +} + +void QSGParticleEmitter::componentComplete() +{ + if(!m_system && qobject_cast<QSGParticleSystem*>(parentItem())) + setSystem(qobject_cast<QSGParticleSystem*>(parentItem())); + if(!m_system) + qWarning() << "Emitter created without a particle system specified";//TODO: useful QML warnings, like line number? + QSGItem::componentComplete(); +} +void QSGParticleEmitter::emitWindow(int timeStamp) +{ + Q_UNUSED(timeStamp); +} + + +void QSGParticleEmitter::setEmitting(bool arg) +{ + if (m_emitting != arg) { + m_emitting = arg; + emit emittingChanged(arg); + } +} + + +QSGParticleExtruder* QSGParticleEmitter::effectiveExtruder() +{ + if(m_extruder) + return m_extruder; + if(!m_defaultExtruder) + m_defaultExtruder = new QSGParticleExtruder; + return m_defaultExtruder; +} + +void QSGParticleEmitter::pulse(qreal seconds) +{ + if(!particleCount()) + qWarning() << "pulse called on an emitter with a particle count of zero"; + if(!m_emitting) + m_burstLeft = seconds*1000.0;//TODO: Change name to match +} + +void QSGParticleEmitter::burst(int num) +{ + if(!particleCount()) + qWarning() << "burst called on an emitter with a particle count of zero"; + m_burstQueue << qMakePair(num, QPointF(x(), y())); +} + +void QSGParticleEmitter::setMaxParticleCount(int arg) +{ + if (m_maxParticleCount != arg) { + if(arg < 0 && m_maxParticleCount >= 0){ + connect(this, SIGNAL(particlesPerSecondChanged(qreal)), + this, SIGNAL(particleCountChanged())); + connect(this, SIGNAL(particleDurationChanged(int)), + this, SIGNAL(particleCountChanged())); + }else if(arg >= 0 && m_maxParticleCount < 0){ + disconnect(this, SIGNAL(particlesPerSecondChanged(qreal)), + this, SIGNAL(particleCountChanged())); + disconnect(this, SIGNAL(particleDurationChanged(int)), + this, SIGNAL(particleCountChanged())); + } + m_maxParticleCount = arg; + emit maxParticleCountChanged(arg); + } +} + +int QSGParticleEmitter::particleCount() const +{ + if(m_maxParticleCount >= 0) + return m_maxParticleCount; + return m_particlesPerSecond*((m_particleDuration+m_particleDurationVariation)/1000.0); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgparticleemitter_p.h b/src/declarative/particles/qsgparticleemitter_p.h new file mode 100644 index 0000000000..9fafd9d43d --- /dev/null +++ b/src/declarative/particles/qsgparticleemitter_p.h @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PARTICLEEMITTER_H +#define PARTICLEEMITTER_H + +#include <QSGItem> +#include <QDebug> +#include "qsgparticlesystem_p.h" +#include "qsgparticleextruder_p.h" +#include "qsgstochasticdirection_p.h" + +#include <QList> +#include <QPair> +#include <QPointF> +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGParticleEmitter : public QSGItem +{ + Q_OBJECT + //###currently goes in emitters OR sets system. Pick one? + Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + Q_PROPERTY(QString particle READ particle WRITE setParticle NOTIFY particleChanged) + Q_PROPERTY(QSGParticleExtruder* shape READ extruder WRITE setExtruder NOTIFY extruderChanged) + Q_PROPERTY(bool emitting READ emitting WRITE setEmitting NOTIFY emittingChanged) + + Q_PROPERTY(qreal emitRate READ particlesPerSecond WRITE setParticlesPerSecond NOTIFY particlesPerSecondChanged) + Q_PROPERTY(int lifeSpan READ particleDuration WRITE setParticleDuration NOTIFY particleDurationChanged) + Q_PROPERTY(int lifeSpanVariation READ particleDurationVariation WRITE setParticleDurationVariation NOTIFY particleDurationVariationChanged) + Q_PROPERTY(int emitCap READ maxParticleCount WRITE setMaxParticleCount NOTIFY maxParticleCountChanged) + + Q_PROPERTY(qreal size READ particleSize WRITE setParticleSize NOTIFY particleSizeChanged) + Q_PROPERTY(qreal endSize READ particleEndSize WRITE setParticleEndSize NOTIFY particleEndSizeChanged) + Q_PROPERTY(qreal sizeVariation READ particleSizeVariation WRITE setParticleSizeVariation NOTIFY particleSizeVariationChanged) + + Q_PROPERTY(QSGStochasticDirection *speed READ speed WRITE setSpeed NOTIFY speedChanged) + Q_PROPERTY(QSGStochasticDirection *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged) +public: + explicit QSGParticleEmitter(QSGItem *parent = 0); + virtual ~QSGParticleEmitter(); + virtual void emitWindow(int timeStamp); + + bool emitting() const + { + return m_emitting; + } + + qreal particlesPerSecond() const + { + return m_particlesPerSecond; + } + + int particleDuration() const + { + return m_particleDuration; + } + + QSGParticleSystem* system() const + { + return m_system; + } + + QString particle() const + { + return m_particle; + } + + int particleDurationVariation() const + { + return m_particleDurationVariation; + } + + virtual void componentComplete(); +signals: + void particlesPerSecondChanged(qreal); + void particleDurationChanged(int); + void emittingChanged(bool); + + void systemChanged(QSGParticleSystem* arg); + + void particleChanged(QString arg); + + void particleDurationVariationChanged(int arg); + + void extruderChanged(QSGParticleExtruder* arg); + + void particleSizeChanged(qreal arg); + + void particleEndSizeChanged(qreal arg); + + void particleSizeVariationChanged(qreal arg); + + void speedChanged(QSGStochasticDirection * arg); + + void accelerationChanged(QSGStochasticDirection * arg); + + void maxParticleCountChanged(int arg); + void particleCountChanged(); + +public slots: + void pulse(qreal seconds); + void burst(int num); + + void setEmitting(bool arg); + + void setParticlesPerSecond(qreal arg) + { + if (m_particlesPerSecond != arg) { + m_particlesPerSecond = arg; + emit particlesPerSecondChanged(arg); + } + } + + void setParticleDuration(int arg) + { + if (m_particleDuration != arg) { + m_particleDuration = arg; + emit particleDurationChanged(arg); + } + } + + void setSystem(QSGParticleSystem* arg) + { + if (m_system != arg) { + m_system = arg; + m_system->registerParticleEmitter(this); + emit systemChanged(arg); + } + } + + void setParticle(QString arg) + { + if (m_particle != arg) { + m_particle = arg; + emit particleChanged(arg); + } + } + + void setParticleDurationVariation(int arg) + { + if (m_particleDurationVariation != arg) { + m_particleDurationVariation = arg; + emit particleDurationVariationChanged(arg); + } + } + void setExtruder(QSGParticleExtruder* arg) + { + if (m_extruder != arg) { + m_extruder = arg; + emit extruderChanged(arg); + } + } + + void setParticleSize(qreal arg) + { + if (m_particleSize != arg) { + m_particleSize = arg; + emit particleSizeChanged(arg); + } + } + + void setParticleEndSize(qreal arg) + { + if (m_particleEndSize != arg) { + m_particleEndSize = arg; + emit particleEndSizeChanged(arg); + } + } + + void setParticleSizeVariation(qreal arg) + { + if (m_particleSizeVariation != arg) { + m_particleSizeVariation = arg; + emit particleSizeVariationChanged(arg); + } + } + + void setSpeed(QSGStochasticDirection * arg) + { + if (m_speed != arg) { + m_speed = arg; + emit speedChanged(arg); + } + } + + void setAcceleration(QSGStochasticDirection * arg) + { + if (m_acceleration != arg) { + m_acceleration = arg; + emit accelerationChanged(arg); + } + } + + void setMaxParticleCount(int arg); + +public: + int particleCount() const; + + virtual void reset(){;} + QSGParticleExtruder* extruder() const + { + return m_extruder; + } + + qreal particleSize() const + { + return m_particleSize; + } + + qreal particleEndSize() const + { + return m_particleEndSize; + } + + qreal particleSizeVariation() const + { + return m_particleSizeVariation; + } + + QSGStochasticDirection * speed() const + { + return m_speed; + } + + QSGStochasticDirection * acceleration() const + { + return m_acceleration; + } + + int maxParticleCount() const + { + return m_maxParticleCount; + } + +protected: + qreal m_particlesPerSecond; + int m_particleDuration; + int m_particleDurationVariation; + bool m_emitting; + QSGParticleSystem* m_system; + QString m_particle; + QSGParticleExtruder* m_extruder; + QSGParticleExtruder* m_defaultExtruder; + QSGParticleExtruder* effectiveExtruder(); + QSGStochasticDirection * m_speed; + QSGStochasticDirection * m_acceleration; + qreal m_particleSize; + qreal m_particleEndSize; + qreal m_particleSizeVariation; + + int m_burstLeft;//TODO: Rename to pulse + QList<QPair<int, QPointF > > m_burstQueue; + int m_maxParticleCount; +private: + QSGStochasticDirection m_nullVector; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // PARTICLEEMITTER_H diff --git a/src/declarative/particles/qsgparticleextruder.cpp b/src/declarative/particles/qsgparticleextruder.cpp new file mode 100644 index 0000000000..91b968c8af --- /dev/null +++ b/src/declarative/particles/qsgparticleextruder.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgparticleextruder_p.h" + +QT_BEGIN_NAMESPACE + +QSGParticleExtruder::QSGParticleExtruder(QObject *parent) : + QObject(parent), m_fill(true) +{ +} + +QPointF QSGParticleExtruder::extrude(const QRectF &rect) +{ + if(m_fill) + return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(), + ((qreal)rand() / RAND_MAX) * rect.height() + rect.y()); + int side = rand() % 4; + switch(side){//TODO: Doesn't this overlap the corners? + case 0: + return QPointF(rect.x(), + ((qreal)rand() / RAND_MAX) * rect.height() + rect.y()); + case 1: + return QPointF(rect.width() + rect.x(), + ((qreal)rand() / RAND_MAX) * rect.height() + rect.y()); + case 2: + return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(), + rect.y()); + default: + return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(), + rect.height() + rect.y()); + } +} + +bool QSGParticleExtruder::contains(const QRectF &bounds, const QPointF &point) +{ + return bounds.contains(point); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgparticleextruder_p.h b/src/declarative/particles/qsgparticleextruder_p.h new file mode 100644 index 0000000000..41e27eb2fa --- /dev/null +++ b/src/declarative/particles/qsgparticleextruder_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PARTICLEEXTRUDER_H +#define PARTICLEEXTRUDER_H + +#include <QObject> +#include <QRectF> +#include <QPointF> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGParticleExtruder : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)//###Should this be base class, or a BoxExtruder? + +public: + explicit QSGParticleExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + virtual bool contains(const QRectF &bounds, const QPointF &point);//###Needed for follow emitter, but does it belong? Only marginally conceptually valid, and that's from user's perspective + bool fill() const + { + return m_fill; + } + +signals: + + void fillChanged(bool arg); + +public slots: + + void setFill(bool arg) + { + if (m_fill != arg) { + m_fill = arg; + emit fillChanged(arg); + } + } +protected: + bool m_fill; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // PARTICLEEXTRUDER_H diff --git a/src/declarative/particles/qsgparticlepainter.cpp b/src/declarative/particles/qsgparticlepainter.cpp new file mode 100644 index 0000000000..a5e8c0a3e6 --- /dev/null +++ b/src/declarative/particles/qsgparticlepainter.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgparticlepainter_p.h" +#include <QDebug> +QT_BEGIN_NAMESPACE +QSGParticlePainter::QSGParticlePainter(QSGItem *parent) : + QSGItem(parent), + m_system(0) +{ + connect(this, SIGNAL(xChanged()), + this, SLOT(calcSystemOffset())); + connect(this, SIGNAL(yChanged()), + this, SLOT(calcSystemOffset())); +} + +void QSGParticlePainter::componentComplete() +{ + if(!m_system && qobject_cast<QSGParticleSystem*>(parentItem())) + setSystem(qobject_cast<QSGParticleSystem*>(parentItem())); + if(!m_system) + qWarning() << "ParticlePainter created without a particle system specified";//TODO: useful QML warnings, like line number? + QSGItem::componentComplete(); +} + + +void QSGParticlePainter::setSystem(QSGParticleSystem *arg) +{ + if (m_system != arg) { + m_system = arg; + if(m_system){ + m_system->registerParticleType(this); + connect(m_system, SIGNAL(xChanged()), + this, SLOT(calcSystemOffset())); + connect(m_system, SIGNAL(yChanged()), + this, SLOT(calcSystemOffset())); + calcSystemOffset(); + } + emit systemChanged(arg); + } +} + +void QSGParticlePainter::load(QSGParticleData*) +{ +} + +void QSGParticlePainter::reload(QSGParticleData*) +{ +} + +void QSGParticlePainter::reset() +{ + //Have to every time because what it's emitting may have changed and that affects particleTypeIndex + m_particleStarts.clear(); + m_lastStart = 0; +} + +void QSGParticlePainter::setCount(int c) +{ + if(c == m_count) + return; + m_count = c; + emit countChanged(); +} + +int QSGParticlePainter::count() +{ + return m_count; +} + + +int QSGParticlePainter::particleTypeIndex(QSGParticleData* d) +{ + if(!m_particleStarts.contains(d->group)){ + m_particleStarts.insert(d->group, m_lastStart); + m_lastStart += m_system->m_groupData[d->group]->size; + } + int ret = m_particleStarts[d->group] + d->particleIndex; + Q_ASSERT(ret >=0 && ret < m_count);//XXX: Possibly shouldn't assert, but bugs here were hard to find in the past + return ret; +} + + +void QSGParticlePainter::calcSystemOffset() +{ + if(!m_system) + return; + QPointF lastOffset = m_systemOffset; + m_systemOffset = -1 * this->mapFromItem(m_system, QPointF()); + if(lastOffset != m_systemOffset){ + //Reload all particles//TODO: Necessary? + foreach(const QString &g, m_particles){ + int gId = m_system->m_groupIds[g]; + for(int i=0; i<m_system->m_groupData[gId]->size; i++) + reload(m_system->m_data[m_system->m_groupData[gId]->start + i]); + } + } +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgparticlepainter_p.h b/src/declarative/particles/qsgparticlepainter_p.h new file mode 100644 index 0000000000..8f1e13b70f --- /dev/null +++ b/src/declarative/particles/qsgparticlepainter_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PARTICLE_H +#define PARTICLE_H + +#include <QObject> +#include <QDebug> +#include "qsgparticlesystem_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGParticlePainter : public QSGItem +{ + Q_OBJECT + Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + Q_PROPERTY(QStringList particles READ particles WRITE setParticles NOTIFY particlesChanged) + +public: + explicit QSGParticlePainter(QSGItem *parent = 0); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); + virtual void setCount(int c); + virtual int count(); + QSGParticleSystem* system() const + { + return m_system; + } + + + QStringList particles() const + { + return m_particles; + } + + int particleTypeIndex(QSGParticleData*); +signals: + void countChanged(); + void systemChanged(QSGParticleSystem* arg); + + void particlesChanged(QStringList arg); + +public slots: +void setSystem(QSGParticleSystem* arg); + +void setParticles(QStringList arg) +{ + if (m_particles != arg) { + m_particles = arg; + emit particlesChanged(arg); + } +} +private slots: + void calcSystemOffset(); +protected: + virtual void reset(); + virtual void componentComplete(); + +// virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *){ +// qDebug() << "Shouldn't be here..." << this; +// return 0; +// } + + QSGParticleSystem* m_system; + friend class QSGParticleSystem; + int m_count; + bool m_pleaseReset; + QStringList m_particles; + QHash<int,int> m_particleStarts; + int m_lastStart; + QPointF m_systemOffset; + + template <typename VertexStruct> + void vertexCopy(VertexStruct &b, const ParticleVertex& a) + { + b.x = a.x - m_systemOffset.x(); + b.y = a.y - m_systemOffset.y(); + b.t = a.t; + b.lifeSpan = a.lifeSpan; + b.size = a.size; + b.endSize = a.endSize; + b.sx = a.sx; + b.sy = a.sy; + b.ax = a.ax; + b.ay = a.ay; + } + +private: +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // PARTICLE_H diff --git a/src/declarative/particles/qsgparticlesmodule.cpp b/src/declarative/particles/qsgparticlesmodule.cpp new file mode 100644 index 0000000000..a7a9a9253f --- /dev/null +++ b/src/declarative/particles/qsgparticlesmodule.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgangleddirection_p.h" +#include "qsgcustomparticle_p.h" +#include "qsgellipseextruder_p.h" +#include "qsgemitter_p.h" +#include "qsgfollowemitter_p.h" +#include "qsgfriction_p.h" +#include "qsggravity_p.h" +#include "qsgimageparticle_p.h" +#include "qsgitemparticle_p.h" +#include "qsgkill_p.h" +#include "qsglineextruder_p.h" +#include "qsgmaskextruder_p.h" +#include "qsgmodelparticle_p.h" +#include "qsgparticleaffector_p.h" +#include "qsgparticleemitter_p.h" +#include "qsgparticleextruder_p.h" +#include "qsgparticlepainter_p.h" +#include "qsgparticlesmodule_p.h" +#include "qsgparticlesystem_p.h" +#include "qsgpointattractor_p.h" +#include "qsgpointdirection_p.h" +#include "qsgspritegoal_p.h" +#include "qsgstochasticdirection_p.h" +#include "qsgtargeteddirection_p.h" +#include "qsgturbulence_p.h" +#include "qsgwander_p.h" + +QT_BEGIN_NAMESPACE + +void QSGParticlesModule::defineModule() +{ + const char* uri = "QtQuick.Particles"; + //Debugging only exposition + qmlRegisterType<QSGParticlePainter>(uri, 2, 0, "ParticlePainter"); + qmlRegisterType<QSGParticleEmitter>(uri, 2, 0, "ParticleEmitter"); + qmlRegisterType<QSGParticleExtruder>(uri, 2, 0, "ParticleExtruder"); + qmlRegisterType<QSGStochasticDirection>(uri, 2, 0, "NullVector"); + //Probably should be nocreate types + + qmlRegisterType<QSGParticleSystem>(uri, 2, 0, "ParticleSystem"); + + qmlRegisterType<QSGImageParticle>(uri, 2, 0, "ImageParticle"); + qmlRegisterType<QSGCustomParticle>(uri, 2, 0, "CustomParticle"); + qmlRegisterType<QSGItemParticle>(uri, 2, 0, "ItemParticle"); + qmlRegisterType<QSGModelParticle>(uri, 2, 0, "ModelParticle"); + + qmlRegisterType<QSGBasicEmitter>(uri, 2, 0, "Emitter"); + qmlRegisterType<QSGFollowEmitter>(uri, 2, 0, "FollowEmitter"); + + qmlRegisterType<QSGEllipseExtruder>(uri, 2, 0, "EllipseShape"); + qmlRegisterType<QSGLineExtruder>(uri, 2, 0, "LineShape"); + qmlRegisterType<QSGMaskExtruder>(uri, 2, 0, "MaskShape"); + + qmlRegisterType<QSGPointDirection>(uri, 2, 0, "PointDirection"); + qmlRegisterType<QSGAngledDirection>(uri, 2, 0, "AngledDirection"); + qmlRegisterType<QSGTargetedDirection>(uri, 2, 0, "TargetedDirection"); + + qmlRegisterType<QSGParticleAffector>(uri, 2, 0, "ParticleAffector");//if it has a triggered signal, it's useful + qmlRegisterType<QSGWanderAffector>(uri, 2, 0, "Wander"); + qmlRegisterType<QSGFrictionAffector>(uri, 2, 0, "Friction"); + qmlRegisterType<QSGPointAttractorAffector>(uri, 2, 0, "PointAttractor"); + qmlRegisterType<QSGGravityAffector>(uri, 2, 0, "Gravity"); + qmlRegisterType<QSGKillAffector>(uri, 2, 0, "Kill"); + qmlRegisterType<QSGSpriteGoalAffector>(uri, 2, 0, "SpriteGoal"); + qmlRegisterType<QSGTurbulenceAffector>(uri, 2, 0 , "Turbulence"); +} + +QT_END_NAMESPACE + +//Q_EXPORT_PLUGIN2(Particles, QT_PREPEND_NAMESPACE(ParticlesModule)) diff --git a/src/declarative/particles/qsgparticlesmodule_p.h b/src/declarative/particles/qsgparticlesmodule_p.h new file mode 100644 index 0000000000..1afe0654f6 --- /dev/null +++ b/src/declarative/particles/qsgparticlesmodule_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGPARTICLESMODULE_H +#define QSGPARTICLESMODULE_H + +#include <qdeclarative.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGParticlesModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGPARTICLESMODULE_H diff --git a/src/declarative/particles/qsgparticlesystem.cpp b/src/declarative/particles/qsgparticlesystem.cpp new file mode 100644 index 0000000000..4a8f75bb04 --- /dev/null +++ b/src/declarative/particles/qsgparticlesystem.cpp @@ -0,0 +1,402 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgparticlesystem_p.h" +#include <qsgnode.h> +#include "qsgparticleemitter_p.h" +#include "qsgparticleaffector_p.h" +#include "qsgparticlepainter_p.h" +#include <cmath> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +QSGParticleData::QSGParticleData() + : group(0) + , e(0) + , particleIndex(0) + , systemIndex(0) +{ + pv.x = 0; + pv.y = 0; + pv.t = -1; + pv.size = 0; + pv.endSize = 0; + pv.sx = 0; + pv.sy = 0; + pv.ax = 0; + pv.ay = 0; +} + +QSGParticleSystem::QSGParticleSystem(QSGItem *parent) : + QSGItem(parent), m_particle_count(0), m_running(true) + , m_startTime(0), m_overwrite(false) + , m_componentComplete(false) +{ + m_groupIds = QHash<QString, int>(); +} + +void QSGParticleSystem::registerParticleType(QSGParticlePainter* p) +{ + m_particles << QPointer<QSGParticlePainter>(p);//###Set or uniqueness checking? + reset(); +} + +void QSGParticleSystem::registerParticleEmitter(QSGParticleEmitter* e) +{ + m_emitters << QPointer<QSGParticleEmitter>(e);//###How to get them out? + connect(e, SIGNAL(particleCountChanged()), + this, SLOT(countChanged())); + connect(e, SIGNAL(particleChanged(QString)), + this, SLOT(countChanged())); + reset(); +} + +void QSGParticleSystem::registerParticleAffector(QSGParticleAffector* a) +{ + m_affectors << QPointer<QSGParticleAffector>(a); + //reset();//TODO: Slim down the huge batch of resets at the start +} + +void QSGParticleSystem::countChanged() +{ + reset();//Need to give Particles new Count +} + +void QSGParticleSystem::setRunning(bool arg) +{ + if (m_running != arg) { + m_running = arg; + emit runningChanged(arg); + reset(); + } +} + +void QSGParticleSystem::componentComplete() +{ + QSGItem::componentComplete(); + m_componentComplete = true; + if(!m_emitters.isEmpty() && !m_particles.isEmpty()) + reset(); +} + +void QSGParticleSystem::initializeSystem() +{ + int oldCount = m_particle_count; + m_particle_count = 0;//TODO: Only when changed? + + //### Reset the data too? + for(int i=0; i<oldCount; i++){ + if(m_data[i]){ + delete m_data[i]; + m_data[i] = 0; + } + } + + for(QHash<int, GroupData*>::iterator iter = m_groupData.begin(); iter != m_groupData.end(); iter++) + delete (*iter); + m_groupData.clear(); + m_groupIds.clear(); + + GroupData* gd = new GroupData;//Default group + gd->size = 0; + gd->start = -1; + gd->nextIdx = 0; + m_groupData.insert(0,gd); + m_groupIds.insert("",0); + m_nextGroupId = 1; + + if(!m_emitters.count() || !m_particles.count()) + return; + + foreach(QSGParticleEmitter* e, m_emitters){ + if(!m_groupIds.contains(e->particle()) + || (!e->particle().isEmpty() && !m_groupIds[e->particle()])){//or it was accidentally inserted by a failed lookup earlier + GroupData* gd = new GroupData; + gd->size = 0; + gd->start = -1; + gd->nextIdx = 0; + int id = m_nextGroupId++; + m_groupIds.insert(e->particle(), id); + m_groupData.insert(id, gd); + } + m_groupData[m_groupIds[e->particle()]]->size += e->particleCount(); + } + + for(QHash<int, GroupData*>::iterator iter = m_groupData.begin(); iter != m_groupData.end(); iter++){ + (*iter)->start = m_particle_count; + m_particle_count += (*iter)->size; + } + m_data.resize(m_particle_count); + for(int i=oldCount; i<m_particle_count; i++) + m_data[i] = 0;//setup new ones + + if(m_particle_count > 16000) + qWarning() << "Particle system contains a vast number of particles (>16000). Expect poor performance"; + + foreach(QSGParticlePainter* particle, m_particles){ + int particleCount = 0; + if(particle->particles().isEmpty()){//Uses default particle + particleCount += m_groupData[0]->size; + m_groupData[0]->types << particle; + }else{ + foreach(const QString &group, particle->particles()){ + particleCount += m_groupData[m_groupIds[group]]->size; + m_groupData[m_groupIds[group]]->types << particle; + } + } + particle->setCount(particleCount); + particle->m_pleaseReset = true; + } + + m_timestamp.start(); + m_initialized = true; + emit systemInitialized(); + qDebug() << "System Initialized. Size:" << m_particle_count; +} + +void QSGParticleSystem::reset() +{ + if(!m_componentComplete) + return;//Batch starting reset()s a little + //Clear guarded pointers which have been deleted + int cleared = 0; + cleared += m_emitters.removeAll(0); + cleared += m_particles.removeAll(0); + cleared += m_affectors.removeAll(0); + //qDebug() << "Reset" << m_emitters.count() << m_particles.count() << "Cleared" << cleared; + foreach(QSGParticlePainter* p, m_particles) + p->reset(); + foreach(QSGParticleEmitter* e, m_emitters) + e->reset(); + if(!m_running) + return; + initializeSystem(); + foreach(QSGParticlePainter* p, m_particles) + p->update(); + foreach(QSGParticleEmitter* e, m_emitters) + e->emitWindow(0);//Start, so that starttime factors appropriately +} + +QSGParticleData* QSGParticleSystem::newDatum(int groupId) +{ + Q_ASSERT(groupId < m_groupData.count());//XXX shouldn't really be an assert + Q_ASSERT(m_groupData[groupId]->size); + int nextIdx = m_groupData[groupId]->start + m_groupData[groupId]->nextIdx++; + if( m_groupData[groupId]->nextIdx >= m_groupData[groupId]->size) + m_groupData[groupId]->nextIdx = 0; + + Q_ASSERT(nextIdx < m_data.size()); + QSGParticleData* ret; + if(m_data[nextIdx]){//Recycle, it's faster. + ret = m_data[nextIdx]; + if(!m_overwrite && ret->stillAlive()){ + return 0;//Artificial longevity (or too fast emission) means this guy hasn't died. To maintain count, don't emit a new one + }//###Reset? + }else{ + ret = new QSGParticleData; + m_data[nextIdx] = ret; + } + + ret->system = this; + ret->systemIndex = nextIdx; + ret->particleIndex = nextIdx - m_groupData[groupId]->start; + ret->group = groupId; + return ret; +} + +void QSGParticleSystem::emitParticle(QSGParticleData* pd) +{// called from prepareNextFrame()->emitWindow - enforce? + //Account for relative emitter position + QPointF offset = this->mapFromItem(pd->e, QPointF(0, 0)); + if(!offset.isNull()){ + pd->pv.x += offset.x(); + pd->pv.y += offset.y(); + } + + foreach(QSGParticleAffector *a, m_affectors) + if(a && a->m_needsReset) + a->reset(pd->systemIndex); + foreach(QSGParticlePainter* p, m_groupData[pd->group]->types) + if(p) + p->load(pd); +} + + + +qint64 QSGParticleSystem::systemSync(QSGParticlePainter* p) +{ + if (!m_running) + return 0; + if (!m_initialized) + return 0;//error in initialization + + if(m_syncList.isEmpty() || m_syncList.contains(p)){//Need to advance the simulation + m_syncList.clear(); + + //### Elapsed time never shrinks - may cause problems if left emitting for weeks at a time. + qreal dt = m_timeInt / 1000.; + m_timeInt = m_timestamp.elapsed() + m_startTime; + qreal time = m_timeInt / 1000.; + dt = time - dt; + m_needsReset.clear(); + foreach(QSGParticleEmitter* emitter, m_emitters) + if(emitter) + emitter->emitWindow(m_timeInt); + foreach(QSGParticleAffector* a, m_affectors) + if(a) + a->affectSystem(dt); + foreach(QSGParticleData* d, m_needsReset) + foreach(QSGParticlePainter* p, m_groupData[d->group]->types) + if(p && d) + p->reload(d); + } + m_syncList << p; + return m_timeInt; +} + +//sets the x accleration without affecting the instantaneous x velocity or position +void QSGParticleData::setInstantaneousAX(qreal ax) +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + qreal sx = (pv.sx + t*pv.ax) - t*ax; + qreal ex = pv.x + pv.sx * t + 0.5 * pv.ax * t * t; + qreal x = ex - t*sx - 0.5 * t*t*ax; + + pv.ax = ax; + pv.sx = sx; + pv.x = x; +} + +//sets the x velocity without affecting the instantaneous x postion +void QSGParticleData::setInstantaneousSX(qreal vx) +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + qreal sx = vx - t*pv.ax; + qreal ex = pv.x + pv.sx * t + 0.5 * pv.ax * t * t; + qreal x = ex - t*sx - 0.5 * t*t*pv.ax; + + pv.sx = sx; + pv.x = x; +} + +//sets the instantaneous x postion +void QSGParticleData::setInstantaneousX(qreal x) +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + pv.x = x - t*pv.sx - 0.5 * t*t*pv.ax; +} + +//sets the y accleration without affecting the instantaneous y velocity or position +void QSGParticleData::setInstantaneousAY(qreal ay) +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + qreal sy = (pv.sy + t*pv.ay) - t*ay; + qreal ey = pv.y + pv.sy * t + 0.5 * pv.ay * t * t; + qreal y = ey - t*sy - 0.5 * t*t*ay; + + pv.ay = ay; + pv.sy = sy; + pv.y = y; +} + +//sets the y velocity without affecting the instantaneous y position +void QSGParticleData::setInstantaneousSY(qreal vy) +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + //qDebug() << t << (system->m_timeInt/1000.0) << pv.x << pv.sx << pv.ax << pv.x + pv.sx * t + 0.5 * pv.ax * t * t; + qreal sy = vy - t*pv.ay; + qreal ey = pv.y + pv.sy * t + 0.5 * pv.ay * t * t; + qreal y = ey - t*sy - 0.5 * t*t*pv.ay; + + pv.sy = sy; + pv.y = y; +} + +//sets the instantaneous Y position +void QSGParticleData::setInstantaneousY(qreal y) +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + pv.y = y - t*pv.sy - 0.5 * t*t*pv.ay; +} + +qreal QSGParticleData::curX() const +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + return pv.x + pv.sx * t + 0.5 * pv.ax * t * t; +} + +qreal QSGParticleData::curSX() const +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + return pv.sx + t*pv.ax; +} + +qreal QSGParticleData::curY() const +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + return pv.y + pv.sy * t + 0.5 * pv.ay * t * t; +} + +qreal QSGParticleData::curSY() const +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + return pv.sy + t*pv.ay; +} + +void QSGParticleData::debugDump() +{ + qDebug() << "Particle" << group + << "Pos: " << pv.x << "," << pv.y + << "Vel: " << pv.sx << "," << pv.sy + << "Acc: " << pv.ax << "," << pv.ay + << "Size: " << pv.size << "," << pv.endSize + << "Time: " << pv.t << "," <<pv.lifeSpan; +} + +bool QSGParticleData::stillAlive() +{ + if(!system) + return false; + return (pv.t + pv.lifeSpan) > (system->m_timeInt/1000.0); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgparticlesystem_p.h b/src/declarative/particles/qsgparticlesystem_p.h new file mode 100644 index 0000000000..9730ff35e8 --- /dev/null +++ b/src/declarative/particles/qsgparticlesystem_p.h @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PARTICLESYSTEM_H +#define PARTICLESYSTEM_H + +#include <QSGItem> +#include <QElapsedTimer> +#include <QVector> +#include <QHash> +#include <QPointer> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGParticleAffector; +class QSGParticleEmitter; +class QSGParticlePainter; +class QSGParticleData; + + +struct GroupData{ + int size; + int start; + int nextIdx; + QList<QSGParticlePainter*> types; +}; + +class QSGParticleSystem : public QSGItem +{ + Q_OBJECT + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged) + Q_PROPERTY(bool overwrite READ overwrite WRITE setOverwrite NOTIFY overwriteChanged)//XXX: Should just be an implementation detail, but I can't decide which way + /* The problem is that it ought to be false (usually) for stasis effects like model particles, + but it ought to be true (usually) for burst effects where you want it to burst, and forget the old stuff + Ideally burst never overflows? But that leads to crappy behaviour from crappy users... + */ + +public: + explicit QSGParticleSystem(QSGItem *parent = 0); + +bool isRunning() const +{ + return m_running; +} + +int startTime() const +{ + return m_startTime; +} + +int count(){ return m_particle_count; } + +signals: + +void systemInitialized(); +void runningChanged(bool arg); + +void startTimeChanged(int arg); + + +void overwriteChanged(bool arg); + +public slots: +void reset(); +void setRunning(bool arg); + + +void setStartTime(int arg) +{ + m_startTime = arg; +} + +void setOverwrite(bool arg) +{ + if (m_overwrite != arg) { + m_overwrite = arg; +emit overwriteChanged(arg); +} +} + +void fastForward(int ms) +{ + m_startTime += ms; +} + +protected: + void componentComplete(); + +private slots: + void countChanged(); +public://but only really for related class usage. Perhaps we should all be friends? + void emitParticle(QSGParticleData* p); + QSGParticleData* newDatum(int groupId); + qint64 systemSync(QSGParticlePainter* p); + QElapsedTimer m_timestamp; + QVector<QSGParticleData*> m_data; + QSet<QSGParticleData*> m_needsReset; + QHash<QString, int> m_groupIds; + QHash<int, GroupData*> m_groupData;//id, size, start + qint64 m_timeInt; + bool m_initialized; + + void registerParticleType(QSGParticlePainter* p); + void registerParticleEmitter(QSGParticleEmitter* e); + void registerParticleAffector(QSGParticleAffector* a); + bool overwrite() const + { + return m_overwrite; + } + + int m_particle_count; +private: + void initializeSystem(); + bool m_running; + QList<QPointer<QSGParticleEmitter> > m_emitters; + QList<QPointer<QSGParticleAffector> > m_affectors; + QList<QPointer<QSGParticlePainter> > m_particles; + QList<QPointer<QSGParticlePainter> > m_syncList; + qint64 m_startTime; + int m_nextGroupId; + bool m_overwrite; + bool m_componentComplete; +}; + +//TODO: Clean up all this into ParticleData + +struct ParticleVertex { + float x; + float y; + float t; + float lifeSpan; + float size; + float endSize; + float sx; + float sy; + float ax; + float ay; + //TODO: Need opacity over life control. More variable size over life? +}; + +class QSGParticleData{ +public: + QSGParticleData(); + + ParticleVertex pv; + + //Convenience functions for working backwards, because parameters are from the start of particle life + //If setting multiple parameters at once, doing the conversion yourself will be faster. + + //sets the x accleration without affecting the instantaneous x velocity or position + void setInstantaneousAX(qreal ax); + //sets the x velocity without affecting the instantaneous x postion + void setInstantaneousSX(qreal vx); + //sets the instantaneous x postion + void setInstantaneousX(qreal x); + //sets the y accleration without affecting the instantaneous y velocity or position + void setInstantaneousAY(qreal ay); + //sets the y velocity without affecting the instantaneous y postion + void setInstantaneousSY(qreal vy); + //sets the instantaneous Y postion + void setInstantaneousY(qreal y); + + //TODO: Slight caching? + qreal curX() const; + qreal curSX() const; + qreal curY() const; + qreal curSY() const; + + int group; + QSGParticleEmitter* e; + QSGParticleSystem* system; + int particleIndex; + int systemIndex; + + void debugDump(); + bool stillAlive(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // PARTICLESYSTEM_H + + diff --git a/src/declarative/particles/qsgpointattractor.cpp b/src/declarative/particles/qsgpointattractor.cpp new file mode 100644 index 0000000000..4c675237ba --- /dev/null +++ b/src/declarative/particles/qsgpointattractor.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgpointattractor_p.h" +#include <cmath> +#include <QDebug> +QT_BEGIN_NAMESPACE +QSGPointAttractorAffector::QSGPointAttractorAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_strength(0.0), m_x(0), m_y(0) + , m_physics(Velocity), m_proportionalToDistance(Linear) +{ +} + +bool QSGPointAttractorAffector::affectParticle(QSGParticleData *d, qreal dt) +{ + if(m_strength == 0.0) + return false; + qreal dx = m_y - d->curX(); + qreal dy = m_x - d->curY(); + qreal r = sqrt((dx*dx) + (dy*dy)); + qreal theta = atan2(dy,dx); + qreal ds = 0; + switch(m_proportionalToDistance){ + case Quadratic: + ds = (m_strength / qMax(1.,r*r)) * dt; + break; + case Linear://also default + default: + ds = (m_strength / qMax(1.,r)) * dt; + } + dx = ds * cos(theta); + dy = ds * sin(theta); + switch(m_physics){ + case Position: + d->pv.x = (d->pv.x + dx); + d->pv.y = (d->pv.y + dy); + break; + case Acceleration: + d->setInstantaneousAX(d->pv.ax + dx); + d->setInstantaneousAY(d->pv.ay + dy); + break; + case Velocity: //also default + default: + d->setInstantaneousSX(d->pv.sx + dx); + d->setInstantaneousSY(d->pv.sy + dy); + } + + return true; +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgpointattractor_p.h b/src/declarative/particles/qsgpointattractor_p.h new file mode 100644 index 0000000000..3ca29dfa96 --- /dev/null +++ b/src/declarative/particles/qsgpointattractor_p.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ATTRACTORAFFECTOR_H +#define ATTRACTORAFFECTOR_H +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGPointAttractorAffector : public QSGParticleAffector +{ + Q_OBJECT + //Like Gravitational singularity, but linear to distance instead of quadratic + //And affects ds/dt, not da/dt + Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged) + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) + Q_PROPERTY(PhysicsAffects physics READ physics WRITE setPhysics NOTIFY physicsChanged) + Q_PROPERTY(Proportion proportionalToDistance READ proportionalToDistance WRITE setProportionalToDistance NOTIFY proportionalToDistanceChanged) + Q_ENUMS(PhysicsAffects) + Q_ENUMS(Proportion) + +public: + enum Proportion{ + Linear, + Quadratic + }; + + enum PhysicsAffects { + Position, + Velocity, + Acceleration + }; + + explicit QSGPointAttractorAffector(QSGItem *parent = 0); + + qreal strength() const + { + return m_strength; + } + + qreal x() const + { + return m_x; + } + + qreal y() const + { + return m_y; + } + + PhysicsAffects physics() const + { + return m_physics; + } + + Proportion proportionalToDistance() const + { + return m_proportionalToDistance; + } + +signals: + + void strengthChanged(qreal arg); + + void xChanged(qreal arg); + + void yChanged(qreal arg); + + void physicsChanged(PhysicsAffects arg); + + void proportionalToDistanceChanged(Proportion arg); + +public slots: +void setStrength(qreal arg) +{ + if (m_strength != arg) { + m_strength = arg; + emit strengthChanged(arg); + } +} + +void setX(qreal arg) +{ + if (m_x != arg) { + m_x = arg; + emit xChanged(arg); + } +} + +void setY(qreal arg) +{ + if (m_y != arg) { + m_y = arg; + emit yChanged(arg); + } +} +void setPhysics(PhysicsAffects arg) +{ + if (m_physics != arg) { + m_physics = arg; + emit physicsChanged(arg); + } +} + +void setProportionalToDistance(Proportion arg) +{ + if (m_proportionalToDistance != arg) { + m_proportionalToDistance = arg; + emit proportionalToDistanceChanged(arg); + } +} + +protected: + virtual bool affectParticle(QSGParticleData *d, qreal dt); +private: +qreal m_strength; +qreal m_x; +qreal m_y; +PhysicsAffects m_physics; +Proportion m_proportionalToDistance; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // ATTRACTORAFFECTOR_H diff --git a/src/declarative/particles/qsgpointdirection.cpp b/src/declarative/particles/qsgpointdirection.cpp new file mode 100644 index 0000000000..c3c4f1c5de --- /dev/null +++ b/src/declarative/particles/qsgpointdirection.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgpointdirection_p.h" + +QT_BEGIN_NAMESPACE + +QSGPointDirection::QSGPointDirection(QObject *parent) : + QSGStochasticDirection(parent) + , m_x(0) + , m_y(0) + , m_xVariation(0) + , m_yVariation(0) +{ +} + +const QPointF &QSGPointDirection::sample(const QPointF &) +{ + m_ret.setX(m_x - m_xVariation + rand() / float(RAND_MAX) * m_xVariation * 2); + m_ret.setY(m_y - m_yVariation + rand() / float(RAND_MAX) * m_yVariation * 2); + return m_ret; +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgpointdirection_p.h b/src/declarative/particles/qsgpointdirection_p.h new file mode 100644 index 0000000000..5e5b052744 --- /dev/null +++ b/src/declarative/particles/qsgpointdirection_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef POINTVECTOR_H +#define POINTVECTOR_H +#include "qsgstochasticdirection_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGPointDirection : public QSGStochasticDirection +{ + Q_OBJECT + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) + Q_PROPERTY(qreal xVariation READ xVariation WRITE setXVariation NOTIFY xVariationChanged) + Q_PROPERTY(qreal yVariation READ yVariation WRITE setYVariation NOTIFY yVariationChanged) +public: + explicit QSGPointDirection(QObject *parent = 0); + virtual const QPointF &sample(const QPointF &from); + qreal x() const + { + return m_x; + } + + qreal y() const + { + return m_y; + } + + qreal xVariation() const + { + return m_xVariation; + } + + qreal yVariation() const + { + return m_yVariation; + } + +signals: + + void xChanged(qreal arg); + + void yChanged(qreal arg); + + void xVariationChanged(qreal arg); + + void yVariationChanged(qreal arg); + +public slots: + void setX(qreal arg) + { + if (m_x != arg) { + m_x = arg; + emit xChanged(arg); + } + } + + void setY(qreal arg) + { + if (m_y != arg) { + m_y = arg; + emit yChanged(arg); + } + } + + void setXVariation(qreal arg) + { + if (m_xVariation != arg) { + m_xVariation = arg; + emit xVariationChanged(arg); + } + } + + void setYVariation(qreal arg) + { + if (m_yVariation != arg) { + m_yVariation = arg; + emit yVariationChanged(arg); + } + } + +private: + + qreal m_x; + qreal m_y; + qreal m_xVariation; + qreal m_yVariation; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // POINTVECTOR_H diff --git a/src/declarative/particles/qsgspritegoal.cpp b/src/declarative/particles/qsgspritegoal.cpp new file mode 100644 index 0000000000..8dc98ae314 --- /dev/null +++ b/src/declarative/particles/qsgspritegoal.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgspritegoal_p.h" +#include "private/qsgspriteengine_p.h" +#include "private/qsgsprite_p.h" +#include "qsgimageparticle_p.h" +#include <QDebug> + +QT_BEGIN_NAMESPACE + +QSGSpriteGoalAffector::QSGSpriteGoalAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_goalIdx(-1), m_jump(false) +{ +} + +void QSGSpriteGoalAffector::updateStateIndex(QSGSpriteEngine* e) +{ + m_lastEngine = e; + for(int i=0; i<e->stateCount(); i++){ + if(e->state(i)->name() == m_goalState){ + m_goalIdx = i; + return; + } + } + m_goalIdx = -1;//Can't find it +} + +void QSGSpriteGoalAffector::setGoalState(QString arg) +{ + if (m_goalState != arg) { + m_goalState = arg; + emit goalStateChanged(arg); + if(m_goalState.isEmpty()) + m_goalIdx = -1; + else + m_goalIdx = -2; + } +} + +bool QSGSpriteGoalAffector::affectParticle(QSGParticleData *d, qreal dt) +{ + Q_UNUSED(dt); + //TODO: Affect all engines + QSGSpriteEngine *engine = 0; + foreach(QSGParticlePainter *p, m_system->m_groupData[d->group]->types) + if(qobject_cast<QSGImageParticle*>(p)) + engine = qobject_cast<QSGImageParticle*>(p)->spriteEngine(); + if(!engine) + return false; + + if(m_goalIdx == -2 || engine != m_lastEngine) + updateStateIndex(engine); + if(engine->spriteState(d->particleIndex) != m_goalIdx){ + engine->setGoal(m_goalIdx, d->particleIndex, m_jump); + emit affected(QPointF(d->curX(), d->curY()));//###Expensive if unconnected? Move to Affector? + return true; //Doesn't affect particle data, but necessary for onceOff + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgspritegoal_p.h b/src/declarative/particles/qsgspritegoal_p.h new file mode 100644 index 0000000000..28fb2939e6 --- /dev/null +++ b/src/declarative/particles/qsgspritegoal_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SPRITEGOALAFFECTOR_H +#define SPRITEGOALAFFECTOR_H +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGSpriteEngine; + +class QSGSpriteGoalAffector : public QSGParticleAffector +{ + Q_OBJECT + Q_PROPERTY(QString goalState READ goalState WRITE setGoalState NOTIFY goalStateChanged) + Q_PROPERTY(bool jump READ jump WRITE setJump NOTIFY jumpChanged) +public: + explicit QSGSpriteGoalAffector(QSGItem *parent = 0); + + QString goalState() const + { + return m_goalState; + } + + bool jump() const + { + return m_jump; + } +protected: + virtual bool affectParticle(QSGParticleData *d, qreal dt); +signals: + + void goalStateChanged(QString arg); + + void jumpChanged(bool arg); + + void affected(const QPointF &pos); +public slots: + +void setGoalState(QString arg); + +void setJump(bool arg) +{ + if (m_jump != arg) { + m_jump = arg; + emit jumpChanged(arg); + } +} + +private: + void updateStateIndex(QSGSpriteEngine* e); + QString m_goalState; + int m_goalIdx; + QSGSpriteEngine* m_lastEngine; + bool m_jump; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // SPRITEGOALAFFECTOR_H diff --git a/src/declarative/particles/qsgstochasticdirection.cpp b/src/declarative/particles/qsgstochasticdirection.cpp new file mode 100644 index 0000000000..3673b9c7a7 --- /dev/null +++ b/src/declarative/particles/qsgstochasticdirection.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgstochasticdirection_p.h" + +QT_BEGIN_NAMESPACE + +QSGStochasticDirection::QSGStochasticDirection(QObject *parent) : + QObject(parent) +{ +} + +const QPointF &QSGStochasticDirection::sample(const QPointF &from) +{ + return m_ret; +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgstochasticdirection_p.h b/src/declarative/particles/qsgstochasticdirection_p.h new file mode 100644 index 0000000000..da3a4302b1 --- /dev/null +++ b/src/declarative/particles/qsgstochasticdirection_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VARYINGVECTOR_H +#define VARYINGVECTOR_H + +#include <QObject> +#include <QPointF> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGStochasticDirection : public QObject +{ + Q_OBJECT +public: + explicit QSGStochasticDirection(QObject *parent = 0); + + virtual const QPointF &sample(const QPointF &from); +signals: + +public slots: + +protected: + QPointF m_ret; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // VARYINGVECTOR_H diff --git a/src/declarative/particles/qsgtargeteddirection.cpp b/src/declarative/particles/qsgtargeteddirection.cpp new file mode 100644 index 0000000000..9f1a868512 --- /dev/null +++ b/src/declarative/particles/qsgtargeteddirection.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgtargeteddirection_p.h" +#include "qsgparticleemitter_p.h" +#include <cmath> +#include <QDebug> + +QT_BEGIN_NAMESPACE +QSGTargetedDirection::QSGTargetedDirection(QObject *parent) : + QSGStochasticDirection(parent) + , m_targetX(0) + , m_targetY(0) + , m_targetVariation(0) + , m_proportionalMagnitude(false) + , m_magnitude(0) + , m_magnitudeVariation(0) + , m_targetItem(0) +{ +} + +const QPointF &QSGTargetedDirection::sample(const QPointF &from) +{ + //###This approach loses interpolating the last position of the target (like we could with the emitter) is it worthwhile? + qreal targetX; + qreal targetY; + if(m_targetItem){ + QSGParticleEmitter* parentEmitter = qobject_cast<QSGParticleEmitter*>(parent()); + targetX = m_targetItem->width()/2; + targetY = m_targetItem->height()/2; + if(!parentEmitter){ + qWarning() << "Directed vector is not a child of the emitter. Mapping of target item coordinates may fail."; + targetX += m_targetItem->x(); + targetY += m_targetItem->y(); + }else{ + m_ret = parentEmitter->mapFromItem(m_targetItem, QPointF(targetX, targetY)); + targetX = m_ret.x(); + targetY = m_ret.y(); + } + }else{ + targetX = m_targetX; + targetY = m_targetY; + } + targetX += 0 - from.x() - m_targetVariation + rand()/(float)RAND_MAX * m_targetVariation*2; + targetY += 0 - from.y() - m_targetVariation + rand()/(float)RAND_MAX * m_targetVariation*2; + qreal theta = atan2(targetY, targetX); + qreal mag = m_magnitude + rand()/(float)RAND_MAX * m_magnitudeVariation * 2 - m_magnitudeVariation; + if(m_proportionalMagnitude) + mag *= sqrt(targetX * targetX + targetY * targetY); + m_ret.setX(mag * cos(theta)); + m_ret.setY(mag * sin(theta)); + return m_ret; +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgtargeteddirection_p.h b/src/declarative/particles/qsgtargeteddirection_p.h new file mode 100644 index 0000000000..4010505858 --- /dev/null +++ b/src/declarative/particles/qsgtargeteddirection_p.h @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTEDVECTOR_H +#define DIRECTEDVECTOR_H +#include "qsgstochasticdirection_p.h" +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGItem; +class QSGTargetedDirection : public QSGStochasticDirection +{ + Q_OBJECT + Q_PROPERTY(qreal targetX READ targetX WRITE setTargetX NOTIFY targetXChanged) + Q_PROPERTY(qreal targetY READ targetY WRITE setTargetY NOTIFY targetYChanged) + //If targetItem is set, X/Y are ignored. Aims at middle of item, use variation for variation + Q_PROPERTY(QSGItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged) + + Q_PROPERTY(qreal targetVariation READ targetVariation WRITE setTargetVariation NOTIFY targetVariationChanged) + + Q_PROPERTY(bool proportionalMagnitude READ proportionalMagnitude WRITE setProportionalMagnitude NOTIFY proprotionalMagnitudeChanged) + Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged) + Q_PROPERTY(qreal magnitudeVariation READ magnitudeVariation WRITE setMagnitudeVariation NOTIFY magnitudeVariationChanged) + +public: + explicit QSGTargetedDirection(QObject *parent = 0); + virtual const QPointF &sample(const QPointF &from); + + qreal targetX() const + { + return m_targetX; + } + + qreal targetY() const + { + return m_targetY; + } + + qreal targetVariation() const + { + return m_targetVariation; + } + + qreal magnitude() const + { + return m_magnitude; + } + + bool proportionalMagnitude() const + { + return m_proportionalMagnitude; + } + + qreal magnitudeVariation() const + { + return m_magnitudeVariation; + } + + QSGItem* targetItem() const + { + return m_targetItem; + } + +signals: + + void targetXChanged(qreal arg); + + void targetYChanged(qreal arg); + + void targetVariationChanged(qreal arg); + + void magnitudeChanged(qreal arg); + + void proprotionalMagnitudeChanged(bool arg); + + void magnitudeVariationChanged(qreal arg); + + void targetItemChanged(QSGItem* arg); + +public slots: + void setTargetX(qreal arg) + { + if (m_targetX != arg) { + m_targetX = arg; + emit targetXChanged(arg); + } + } + + void setTargetY(qreal arg) + { + if (m_targetY != arg) { + m_targetY = arg; + emit targetYChanged(arg); + } + } + + void setTargetVariation(qreal arg) + { + if (m_targetVariation != arg) { + m_targetVariation = arg; + emit targetVariationChanged(arg); + } + } + + void setMagnitude(qreal arg) + { + if (m_magnitude != arg) { + m_magnitude = arg; + emit magnitudeChanged(arg); + } + } + + void setProportionalMagnitude(bool arg) + { + if (m_proportionalMagnitude != arg) { + m_proportionalMagnitude = arg; + emit proprotionalMagnitudeChanged(arg); + } + } + + void setMagnitudeVariation(qreal arg) + { + if (m_magnitudeVariation != arg) { + m_magnitudeVariation = arg; + emit magnitudeVariationChanged(arg); + } + } + + void setTargetItem(QSGItem* arg) + { + if (m_targetItem != arg) { + m_targetItem = arg; + emit targetItemChanged(arg); + } + } + +private: + qreal m_targetX; + qreal m_targetY; + qreal m_targetVariation; + bool m_proportionalMagnitude; + qreal m_magnitude; + qreal m_magnitudeVariation; + QSGItem *m_targetItem; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // DIRECTEDVECTOR_H diff --git a/src/declarative/particles/qsgturbulence.cpp b/src/declarative/particles/qsgturbulence.cpp new file mode 100644 index 0000000000..476db9c4b0 --- /dev/null +++ b/src/declarative/particles/qsgturbulence.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgturbulence_p.h" +#include "qsgparticlepainter_p.h"//TODO: Why was this needed again? +#include <cmath> +#include <cstdlib> +#include <QDebug> +QT_BEGIN_NAMESPACE + +QSGTurbulenceAffector::QSGTurbulenceAffector(QSGItem *parent) : + QSGParticleAffector(parent), + m_strength(10), m_lastT(0), m_frequency(64), m_gridSize(10), m_field(0), m_inited(false) +{ + //TODO: Update grid on size change +} + +QSGTurbulenceAffector::~QSGTurbulenceAffector() +{ + if (m_field) { + for(int i=0; i<m_gridSize; i++) + free(m_field[i]); + free(m_field); + } +} + +static qreal magnitude(qreal x, qreal y) +{ + return sqrt(x*x + y*y); +} + +void QSGTurbulenceAffector::setSize(int arg) +{ + if (m_gridSize != arg) { + if(m_field){ //deallocate and then reallocate grid + for(int i=0; i<m_gridSize; i++) + free(m_field[i]); + free(m_field); + m_system = 0; + } + m_gridSize = arg; + emit sizeChanged(arg); + } +} + +void QSGTurbulenceAffector::ensureInit() +{ + if(m_inited) + return; + m_inited = true; + m_field = (QPointF**)malloc(m_gridSize * sizeof(QPointF*)); + for(int i=0; i<m_gridSize; i++) + m_field[i] = (QPointF*)malloc(m_gridSize * sizeof(QPointF)); + for(int i=0; i<m_gridSize; i++) + for(int j=0; j<m_gridSize; j++) + m_field[i][j] = QPointF(); + m_spacing = QPointF(width()/m_gridSize, height()/m_gridSize); + m_magSum = magnitude(m_spacing.x(), m_spacing.y())*2; +} + +void QSGTurbulenceAffector::mapUpdate() +{ + QPoint pos(rand() % m_gridSize, rand() % m_gridSize); + QPointF vector(m_strength - (((qreal)rand() / RAND_MAX) * m_strength*2), + m_strength - (((qreal)rand() / RAND_MAX) * m_strength*2)); + for(int i = 0; i < m_gridSize; i++){ + for(int j = 0; j < m_gridSize; j++){ + qreal dist = magnitude(i-pos.x(), j-pos.y()); + m_field[i][j] += vector/(dist + 1); + if(magnitude(m_field[i][j].x(), m_field[i][j].y()) > m_strength){ + //Speed limit + qreal theta = atan2(m_field[i][j].y(), m_field[i][j].x()); + m_field[i][j].setX(m_strength * cos(theta)); + m_field[i][j].setY(m_strength * sin(theta)); + } + } + } +} + + +void QSGTurbulenceAffector::affectSystem(qreal dt) +{ + if(!m_system || !m_active) + return; + ensureInit(); + qreal period = 1.0/m_frequency; + qreal time = m_system->m_timeInt / 1000.0; + while( m_lastT < time ){ + mapUpdate(); + m_lastT += period; + } + + foreach(QSGParticleData *d, m_system->m_data){ + if(!d || !activeGroup(d->group)) + return; + qreal fx = 0.0; + qreal fy = 0.0; + QPointF pos = QPointF(d->curX() - x(), d->curY() - y());//TODO: Offset + QPointF nodePos = QPointF(pos.x() / m_spacing.x(), pos.y() / m_spacing.y()); + QSet<QPair<int, int> > nodes; + nodes << qMakePair((int)ceil(nodePos.x()), (int)ceil(nodePos.y())); + nodes << qMakePair((int)ceil(nodePos.x()), (int)floor(nodePos.y())); + nodes << qMakePair((int)floor(nodePos.x()), (int)ceil(nodePos.y())); + nodes << qMakePair((int)floor(nodePos.x()), (int)floor(nodePos.y())); + typedef QPair<int, int> intPair; + foreach(const intPair &p, nodes){ + if(!QRect(0,0,m_gridSize-1,m_gridSize-1).contains(QPoint(p.first, p.second))) + continue; + qreal dist = magnitude(pos.x() - p.first*m_spacing.x(), pos.y() - p.second*m_spacing.y());//TODO: Mathematically valid + fx += m_field[p.first][p.second].x() * ((m_magSum - dist)/m_magSum);//Proportionally weight nodes + fy += m_field[p.first][p.second].y() * ((m_magSum - dist)/m_magSum); + } + if(fx || fy){ + d->setInstantaneousSX(d->curSX()+ fx * dt); + d->setInstantaneousSY(d->curSY()+ fy * dt); + m_system->m_needsReset << d; + } + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgturbulence_p.h b/src/declarative/particles/qsgturbulence_p.h new file mode 100644 index 0000000000..29483fbc70 --- /dev/null +++ b/src/declarative/particles/qsgturbulence_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TURBULENCEAFFECTOR_H +#define TURBULENCEAFFECTOR_H +#include "qsgparticleaffector_p.h" +#include <QDeclarativeListProperty> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGParticlePainter; + +class QSGTurbulenceAffector : public QSGParticleAffector +{ + Q_OBJECT + Q_PROPERTY(int strength READ strength WRITE setStrength NOTIFY strengthChanged) + Q_PROPERTY(int frequency READ frequency WRITE setFrequency NOTIFY frequencyChanged) + Q_PROPERTY(int gridSize READ size WRITE setSize NOTIFY sizeChanged) +public: + explicit QSGTurbulenceAffector(QSGItem *parent = 0); + ~QSGTurbulenceAffector(); + virtual void affectSystem(qreal dt); + + int strength() const + { + return m_strength; + } + + int frequency() const + { + return m_frequency; + } + + int size() const + { + return m_gridSize; + } + +signals: + + void strengthChanged(int arg); + + void frequencyChanged(int arg); + + void sizeChanged(int arg); + +public slots: + +void setStrength(int arg) +{ + if (m_strength != arg) { + m_strength = arg; + emit strengthChanged(arg); + } +} + +void setFrequency(int arg) +{ + if (m_frequency != arg) { + m_frequency = arg; + emit frequencyChanged(arg); + } +} + +void setSize(int arg); + +private: + void ensureInit(); + void mapUpdate(); + int m_strength; + qreal m_lastT; + int m_frequency; + int m_gridSize; + QPointF** m_field; + QPointF m_spacing; + qreal m_magSum; + bool m_inited; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // TURBULENCEAFFECTOR_H diff --git a/src/declarative/particles/qsgwander.cpp b/src/declarative/particles/qsgwander.cpp new file mode 100644 index 0000000000..6e56d6a05b --- /dev/null +++ b/src/declarative/particles/qsgwander.cpp @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgwander_p.h" +#include "qsgparticlesystem_p.h"//for ParticlesVertices +QT_BEGIN_NAMESPACE + +QSGWanderAffector::QSGWanderAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_xVariance(0), m_yVariance(0), m_pace(0) + , m_physics(Velocity) +{ + m_needsReset = true; +} + +QSGWanderAffector::~QSGWanderAffector() +{ + for(QHash<int, WanderData*>::const_iterator iter=m_wanderData.constBegin(); + iter != m_wanderData.constEnd(); iter++) + delete (*iter); +} + +WanderData* QSGWanderAffector::getData(int idx) +{ + if(m_wanderData.contains(idx)) + return m_wanderData[idx]; + WanderData* d = new WanderData; + d->x_vel = 0; + d->y_vel = 0; + d->x_peak = m_xVariance; + d->y_peak = m_yVariance; + d->x_var = m_pace * qreal(qrand()) / RAND_MAX; + d->y_var = m_pace * qreal(qrand()) / RAND_MAX; + + m_wanderData.insert(idx, d); + return d; +} + +void QSGWanderAffector::reset(int systemIdx) +{ + if(m_wanderData.contains(systemIdx)) + delete m_wanderData[systemIdx]; + m_wanderData.remove(systemIdx); +} + +bool QSGWanderAffector::affectParticle(QSGParticleData* data, qreal dt) +{ + /*TODO: Add a mode which does basically this - picking a direction, going in it (random speed) and then going back + WanderData* d = getData(data->systemIndex); + if (m_xVariance != 0.) { + if ((d->x_vel > d->x_peak && d->x_var > 0.0) || (d->x_vel < -d->x_peak && d->x_var < 0.0)) { + d->x_var = -d->x_var; + d->x_peak = m_xVariance + m_xVariance * qreal(qrand()) / RAND_MAX; + } + d->x_vel += d->x_var * dt; + } + qreal dx = dt * d->x_vel; + + if (m_yVariance != 0.) { + if ((d->y_vel > d->y_peak && d->y_var > 0.0) || (d->y_vel < -d->y_peak && d->y_var < 0.0)) { + d->y_var = -d->y_var; + d->y_peak = m_yVariance + m_yVariance * qreal(qrand()) / RAND_MAX; + } + d->y_vel += d->y_var * dt; + } + qreal dy = dt * d->x_vel; + + //### Should we be amending vel instead? + ParticleVertex* p = &(data->pv); + p->x += dx; + + p->y += dy; + return true; + */ + qreal dx = dt * m_pace * (2 * qreal(qrand())/RAND_MAX - 1); + qreal dy = dt * m_pace * (2 * qreal(qrand())/RAND_MAX - 1); + qreal newX, newY; + switch(m_physics){ + case Position: + newX = data->curX() + dx; + if(m_xVariance > qAbs(newX) ) + data->pv.x += dx; + newY = data->curY() + dy; + if(m_yVariance > qAbs(newY) ) + data->pv.y += dy; + break; + default: + case Velocity: + newX = data->curSX() + dx; + if(m_xVariance > qAbs(newX) ) + data->setInstantaneousSX(newX); + newY = data->curSY() + dy; + if(m_yVariance > qAbs(newY) ) + data->setInstantaneousSY(newY); + break; + case Acceleration: + newX = data->pv.ax + dx; + if(m_xVariance > qAbs(newX) ) + data->setInstantaneousAX(newX); + newY = data->pv.ay + dy; + if(m_yVariance > qAbs(newY) ) + data->setInstantaneousAY(newY); + break; + } + return true; +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgwander_p.h b/src/declarative/particles/qsgwander_p.h new file mode 100644 index 0000000000..783efc8636 --- /dev/null +++ b/src/declarative/particles/qsgwander_p.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WANDERAFFECTOR_H +#define WANDERAFFECTOR_H +#include <QHash> +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +struct WanderData{ + qreal x_vel; + qreal y_vel; + qreal x_peak; + qreal x_var; + qreal y_peak; + qreal y_var; +}; + +class QSGWanderAffector : public QSGParticleAffector +{ + Q_OBJECT + Q_PROPERTY(qreal pace READ pace WRITE setPace NOTIFY paceChanged) + Q_PROPERTY(qreal xVariance READ xVariance WRITE setXVariance NOTIFY xVarianceChanged) + Q_PROPERTY(qreal yVariance READ yVariance WRITE setYVariance NOTIFY yVarianceChanged) + Q_PROPERTY(PhysicsAffects physics READ physics WRITE setPhysics NOTIFY physicsChanged) + Q_ENUMS(PhysicsAffects) + +public: + enum PhysicsAffects { + Position, + Velocity, + Acceleration + }; + + explicit QSGWanderAffector(QSGItem *parent = 0); + ~QSGWanderAffector(); + virtual void reset(int systemIdx); + + qreal xVariance() const + { + return m_xVariance; + } + + qreal yVariance() const + { + return m_yVariance; + } + + qreal pace() const + { + return m_pace; + } + + PhysicsAffects physics() const + { + return m_physics; + } + +protected: + virtual bool affectParticle(QSGParticleData *d, qreal dt); +signals: + + void xVarianceChanged(qreal arg); + + void yVarianceChanged(qreal arg); + + void paceChanged(qreal arg); + + + void physicsChanged(PhysicsAffects arg); + +public slots: +void setXVariance(qreal arg) +{ + if (m_xVariance != arg) { + m_xVariance = arg; + emit xVarianceChanged(arg); + } +} + +void setYVariance(qreal arg) +{ + if (m_yVariance != arg) { + m_yVariance = arg; + emit yVarianceChanged(arg); + } +} + +void setPace(qreal arg) +{ + if (m_pace != arg) { + m_pace = arg; + emit paceChanged(arg); + } +} + + +void setPhysics(PhysicsAffects arg) +{ + if (m_physics != arg) { + m_physics = arg; + emit physicsChanged(arg); + } +} + +private: + WanderData* getData(int idx); + QHash<int, WanderData*> m_wanderData; + qreal m_xVariance; + qreal m_yVariance; + qreal m_pace; + PhysicsAffects m_physics; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // WANDERAFFECTOR_H diff --git a/src/declarative/qml/qdeclarativecompiler.cpp b/src/declarative/qml/qdeclarativecompiler.cpp index bd223b317c..712f787549 100644 --- a/src/declarative/qml/qdeclarativecompiler.cpp +++ b/src/declarative/qml/qdeclarativecompiler.cpp @@ -1011,12 +1011,14 @@ void QDeclarativeCompiler::genObjectBody(QDeclarativeParser::Object *obj) { typedef QPair<Property *, int> PropPair; foreach(const PropPair &prop, obj->scriptStringProperties) { + const QString &script = prop.first->values.at(0)->value.asScript(); QDeclarativeInstruction ss; ss.setType(QDeclarativeInstruction::StoreScriptString); ss.storeScriptString.propertyIndex = prop.first->index; - ss.storeScriptString.value = - output->indexForString(prop.first->values.at(0)->value.asScript()); + ss.storeScriptString.value = output->indexForString(script); ss.storeScriptString.scope = prop.second; + ss.storeScriptString.bindingId = rewriteBinding(script, prop.first->name); + ss.storeScriptString.line = prop.first->location.start.line; output->addInstruction(ss); } diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp index c0db9826ab..8764ab9452 100644 --- a/src/declarative/qml/qdeclarativeengine.cpp +++ b/src/declarative/qml/qdeclarativeengine.cpp @@ -99,6 +99,7 @@ #include <private/qdeclarativeitemsmodule_p.h> #include <private/qdeclarativeutilmodule_p.h> #include <private/qsgitemsmodule_p.h> +#include <private/qsgparticlesmodule_p.h> #include <qsgtexture.h> #ifdef Q_OS_WIN // for %APPDATA% @@ -355,6 +356,7 @@ QDeclarativeEnginePrivate::QDeclarativeEnginePrivate(QDeclarativeEngine *e) QDeclarativeUtilModule::defineModule(); QDeclarativeEnginePrivate::defineModule(); QSGItemsModule::defineModule(); + QSGParticlesModule::defineModule(); QDeclarativeValueTypeFactory::registerValueTypes(); } } @@ -651,9 +653,6 @@ QNetworkAccessManager *QDeclarativeEngine::networkAccessManager() const All required image providers should be added to the engine before any QML sources files are loaded. - Note that images loaded from a QDeclarativeImageProvider are cached - by QPixmapCache, similar to any image loaded by QML. - \sa removeImageProvider() */ void QDeclarativeEngine::addImageProvider(const QString &providerId, QDeclarativeImageProvider *provider) diff --git a/src/declarative/qml/qdeclarativeexpression.cpp b/src/declarative/qml/qdeclarativeexpression.cpp index 095d9be335..7d2803f33b 100644 --- a/src/declarative/qml/qdeclarativeexpression.cpp +++ b/src/declarative/qml/qdeclarativeexpression.cpp @@ -45,6 +45,7 @@ #include "private/qdeclarativeengine_p.h" #include "private/qdeclarativecontext_p.h" #include "private/qdeclarativerewrite_p.h" +#include "private/qdeclarativescriptstring_p.h" #include "private/qdeclarativecompiler_p.h" #include <QtCore/qdebug.h> @@ -232,6 +233,58 @@ QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, vo /*! Create a QDeclarativeExpression object that is a child of \a parent. + The \script provides the expression to be evaluated, the context to evaluate it in, + and the scope object to evaluate it with. + + This constructor is functionally equivalent to the following, but in most cases + is more efficient. + \code + QDeclarativeExpression expression(script.context(), script.scopeObject(), script.script(), parent); + \endcode + + \sa QDeclarativeScriptString +*/ +QDeclarativeExpression::QDeclarativeExpression(const QDeclarativeScriptString &script, QObject *parent) +: QObject(*new QDeclarativeExpressionPrivate, parent) +{ + Q_D(QDeclarativeExpression); + bool defaultConstruction = false; + + int id = script.d.data()->bindingId; + if (id < 0) { + defaultConstruction = true; + } else { + QDeclarativeContextData *ctxtdata = QDeclarativeContextData::get(script.context()); + + QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(qmlEngine(script.scopeObject())); + QDeclarativeCompiledData *cdata = 0; + QDeclarativeTypeData *typeData = 0; + if (engine && ctxtdata && !ctxtdata->url.isEmpty()) { + typeData = engine->typeLoader.get(ctxtdata->url); + cdata = typeData->compiledData(); + } + + if (cdata) + d->init(ctxtdata, (void*)cdata->datas.at(id).constData(), cdata, script.scopeObject(), + cdata->name, script.d.data()->lineNumber); + else + defaultConstruction = true; + + if (typeData) + typeData->release(); + } + + if (defaultConstruction) + d->init(QDeclarativeContextData::get(script.context()), script.script(), script.scopeObject()); + + if (QDeclarativeExpression_notifyIdx == -1) + QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()"); + d->setNotifyObject(this, QDeclarativeExpression_notifyIdx); +} + +/*! + Create a QDeclarativeExpression object that is a child of \a parent. + The \a expression JavaScript will be executed in the \a ctxt QDeclarativeContext. If specified, the \a scope object's properties will also be in scope during the expression's execution. diff --git a/src/declarative/qml/qdeclarativeexpression.h b/src/declarative/qml/qdeclarativeexpression.h index de2042e58c..217545e08e 100644 --- a/src/declarative/qml/qdeclarativeexpression.h +++ b/src/declarative/qml/qdeclarativeexpression.h @@ -43,6 +43,7 @@ #define QDECLARATIVEEXPRESSION_H #include <QtDeclarative/qdeclarativeerror.h> +#include <QtDeclarative/qdeclarativescriptstring.h> #include <QtCore/qobject.h> #include <QtCore/qvariant.h> @@ -65,6 +66,7 @@ class Q_DECLARATIVE_EXPORT QDeclarativeExpression : public QObject public: QDeclarativeExpression(); QDeclarativeExpression(QDeclarativeContext *, QObject *, const QString &, QObject * = 0); + explicit QDeclarativeExpression(const QDeclarativeScriptString &, QObject * = 0); virtual ~QDeclarativeExpression(); QDeclarativeEngine *engine() const; diff --git a/src/declarative/qml/qdeclarativeimageprovider.cpp b/src/declarative/qml/qdeclarativeimageprovider.cpp index 50a3aad733..56e04c963f 100644 --- a/src/declarative/qml/qdeclarativeimageprovider.cpp +++ b/src/declarative/qml/qdeclarativeimageprovider.cpp @@ -136,7 +136,8 @@ public: Image providers that support QImage loading automatically include support for asychronous loading of images. To enable asynchronous loading for an - \l Image source, set \l Image::asynchronous to \c true. When this is enabled, + image source, set the \c asynchronous property to \c true for the relevant + \l Image, \l BorderImage or \l AnimatedImage object. When this is enabled, the image request to the provider is run in a low priority thread, allowing image loading to be executed in the background, and reducing the performance impact on the user interface. @@ -147,6 +148,17 @@ public: \c true, the value is ignored and the image is loaded synchronously. + + \section2 Image caching + + Images returned by a QDeclarativeImageProvider are automatically cached, + similar to any image loaded by the QML engine. When an image with a + "image://" prefix is loaded from cache, requestImage() and requestPixmap() + will not be called for the relevant image provider. If an image should always + be fetched from the image provider, and should not be cached at all, set the + \c cache property to \c false for the relevant \l Image, \l BorderImage or + \l AnimatedImage object. + \sa QDeclarativeEngine::addImageProvider() */ diff --git a/src/declarative/qml/qdeclarativeinstruction.cpp b/src/declarative/qml/qdeclarativeinstruction.cpp index fa0d8ea6f4..79a2a1cc34 100644 --- a/src/declarative/qml/qdeclarativeinstruction.cpp +++ b/src/declarative/qml/qdeclarativeinstruction.cpp @@ -160,7 +160,7 @@ void QDeclarativeCompiledData::dump(QDeclarativeInstruction *instr, int idx) qWarning().nospace() << idx << "\t\t" << "STORE_IMPORTED_SCRIPT\t" << instr->storeScript.value; break; case QDeclarativeInstruction::StoreScriptString: - qWarning().nospace() << idx << "\t\t" << "STORE_SCRIPT_STRING\t" << instr->storeScriptString.propertyIndex << "\t" << instr->storeScriptString.value << "\t" << instr->storeScriptString.scope; + qWarning().nospace() << idx << "\t\t" << "STORE_SCRIPT_STRING\t" << instr->storeScriptString.propertyIndex << "\t" << instr->storeScriptString.value << "\t" << instr->storeScriptString.scope << "\t" << instr->storeScriptString.bindingId; break; case QDeclarativeInstruction::AssignSignalObject: qWarning().nospace() << idx << "\t\t" << "ASSIGN_SIGNAL_OBJECT\t" << instr->assignSignalObject.signal << "\t\t\t" << datas.at(instr->assignSignalObject.signal); diff --git a/src/declarative/qml/qdeclarativeinstruction_p.h b/src/declarative/qml/qdeclarativeinstruction_p.h index d040967882..3db55a66d3 100644 --- a/src/declarative/qml/qdeclarativeinstruction_p.h +++ b/src/declarative/qml/qdeclarativeinstruction_p.h @@ -245,6 +245,8 @@ union QDeclarativeInstruction int propertyIndex; int value; int scope; + int bindingId; + ushort line; }; struct instr_storeScript { QML_INSTR_HEADER diff --git a/src/declarative/qml/qdeclarativescriptstring.cpp b/src/declarative/qml/qdeclarativescriptstring.cpp index f544393b9b..02d6e5603e 100644 --- a/src/declarative/qml/qdeclarativescriptstring.cpp +++ b/src/declarative/qml/qdeclarativescriptstring.cpp @@ -40,19 +40,10 @@ ****************************************************************************/ #include "qdeclarativescriptstring.h" +#include "qdeclarativescriptstring_p.h" QT_BEGIN_NAMESPACE -class QDeclarativeScriptStringPrivate : public QSharedData -{ -public: - QDeclarativeScriptStringPrivate() : context(0), scope(0) {} - - QDeclarativeContext *context; - QObject *scope; - QString script; -}; - /*! \class QDeclarativeScriptString \since 4.7 @@ -75,8 +66,8 @@ and the class could choose how to handle it. Typically, the class will evaluate the script at some later time using a QDeclarativeExpression. \code -QDeclarativeExpression expr(scriptString.context(), scriptString.script(), scriptStr.scopeObject()); -expr.value(); +QDeclarativeExpression expr(scriptString); +expr.evaluate(); \endcode \sa QDeclarativeExpression diff --git a/src/declarative/qml/qdeclarativescriptstring.h b/src/declarative/qml/qdeclarativescriptstring.h index 5408dd404d..5f3a2fe73f 100644 --- a/src/declarative/qml/qdeclarativescriptstring.h +++ b/src/declarative/qml/qdeclarativescriptstring.h @@ -75,6 +75,9 @@ public: private: QSharedDataPointer<QDeclarativeScriptStringPrivate> d; + + friend class QDeclarativeVME; + friend class QDeclarativeExpression; }; QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativescriptstring_p.h b/src/declarative/qml/qdeclarativescriptstring_p.h new file mode 100644 index 0000000000..cd0cc43995 --- /dev/null +++ b/src/declarative/qml/qdeclarativescriptstring_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** 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 QtDeclarative 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESCRIPTSTRING_P_H +#define QDECLARATIVESCRIPTSTRING_P_H + +#include <QtDeclarative/qdeclarativecontext.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativeScriptStringPrivate : public QSharedData +{ +public: + QDeclarativeScriptStringPrivate() : context(0), scope(0), bindingId(-1), lineNumber(-1) {} + + QDeclarativeContext *context; + QObject *scope; + QString script; + int bindingId; + int lineNumber; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESCRIPTSTRING_P_H diff --git a/src/declarative/qml/qdeclarativevme.cpp b/src/declarative/qml/qdeclarativevme.cpp index 146c856f8f..9082ef06c1 100644 --- a/src/declarative/qml/qdeclarativevme.cpp +++ b/src/declarative/qml/qdeclarativevme.cpp @@ -60,6 +60,7 @@ #include "private/qdeclarativev4bindings_p.h" #include "private/qdeclarativeglobal_p.h" #include "qdeclarativescriptstring.h" +#include "qdeclarativescriptstring_p.h" #include <QStack> #include <QWidget> @@ -639,6 +640,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, ss.setContext(ctxt->asQDeclarativeContext()); ss.setScopeObject(scope); ss.setScript(primitives.at(instr.value)); + ss.d.data()->bindingId = instr.bindingId; + ss.d.data()->lineNumber = instr.line; void *a[] = { &ss, 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index cc2f362d4a..7325cd8340 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -113,12 +113,10 @@ HEADERS += \ $$PWD/qdeclarativeimport_p.h \ $$PWD/qdeclarativeextensionplugin.h \ $$PWD/qintrusivelist_p.h \ + $$PWD/qdeclarativescriptstring_p.h QT += sql include(parser/parser.pri) include(rewriter/rewriter.pri) include(v4/v4.pri) include(v8/v8.pri) - -# mirrors logic in corelib/kernel/kernel.pri -unix:!symbian: contains(QT_CONFIG, clock-gettime):include($$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri) diff --git a/src/declarative/scenegraph/coreapi/qsgrenderer.cpp b/src/declarative/scenegraph/coreapi/qsgrenderer.cpp index e267e3d8b8..dbf704b64a 100644 --- a/src/declarative/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/declarative/scenegraph/coreapi/qsgrenderer.cpp @@ -206,10 +206,7 @@ void QSGRenderer::renderScene() class B : public Bindable { public: - B() : m_ctx(const_cast<QGLContext *>(QGLContext::currentContext())) { } void bind() const { QGLFramebufferObject::bindDefault(); } - private: - QGLContext *m_ctx; } b; renderScene(b); } diff --git a/src/declarative/scenegraph/qsgcontext.cpp b/src/declarative/scenegraph/qsgcontext.cpp index e36d432e40..55942bebe4 100644 --- a/src/declarative/scenegraph/qsgcontext.cpp +++ b/src/declarative/scenegraph/qsgcontext.cpp @@ -281,10 +281,17 @@ QSGImageNode *QSGContext::createImageNode() */ QSGGlyphNode *QSGContext::createGlyphNode() { + // ### Do something with these before final release... + static bool doSubpixel = qApp->arguments().contains(QLatin1String("--text-subpixel-antialiasing")); + static bool doGray = qApp->arguments().contains(QLatin1String("--text-gray-antialiasing")); + if (QSGDistanceFieldGlyphCache::distanceFieldEnabled()) { QSGGlyphNode *node = new QSGDistanceFieldGlyphNode; - if (qApp->arguments().contains(QLatin1String("--subpixel-antialiasing"))) + + if (doSubpixel) node->setPreferredAntialiasingMode(QSGGlyphNode::SubPixelAntialiasing); + else if (doGray) + node->setPreferredAntialiasingMode(QSGGlyphNode::GrayAntialiasing); return node; } else { return new QSGDefaultGlyphNode; @@ -299,8 +306,10 @@ QSGGlyphNode *QSGContext::createGlyphNode() */ QSGRenderer *QSGContext::createRenderer() { + // ### Do something with this before release... + static bool doFrontToBack = qApp->arguments().contains(QLatin1String("--opaque-front-to-back")); QMLRenderer *renderer = new QMLRenderer(this); - if (qApp->arguments().contains(QLatin1String("--opaque-front-to-back"))) { + if (doFrontToBack) { printf("QSGContext: Sorting opaque nodes front to back...\n"); renderer->setSortFrontToBackEnabled(true); } diff --git a/src/declarative/scenegraph/scenegraph.pri b/src/declarative/scenegraph/scenegraph.pri index 77a93e4811..17370804a3 100644 --- a/src/declarative/scenegraph/scenegraph.pri +++ b/src/declarative/scenegraph/scenegraph.pri @@ -29,6 +29,7 @@ HEADERS += \ $$PWD/util/qsgareaallocator_p.h \ $$PWD/util/qsgengine.h \ $$PWD/util/qsgflatcolormaterial.h \ + $$PWD/util/qsgsimplematerial.h \ $$PWD/util/qsgsimplerectnode.h \ $$PWD/util/qsgsimpletexturenode.h \ $$PWD/util/qsgtexturematerial.h \ diff --git a/src/declarative/scenegraph/util/qsgsimplematerial.h b/src/declarative/scenegraph/util/qsgsimplematerial.h new file mode 100644 index 0000000000..810d89f224 --- /dev/null +++ b/src/declarative/scenegraph/util/qsgsimplematerial.h @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSIMPLEMATERIAL_H +#define QSGSIMPLEMATERIAL_H + +#include <qsgmaterial.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +template <typename State> +class QSGSimpleMaterialShader : public QSGMaterialShader +{ +public: + void initialize() { + QSGMaterialShader::initialize(); + + m_id_matrix = program()->uniformLocation(uniformMatrixName()); + if (m_id_matrix < 0) { + qFatal("QSGSimpleMaterialShader does not implement 'uniform highp mat4 %s;' in its vertex shader", + uniformMatrixName()); + } + + const char *opacity = uniformOpacityName(); + if (opacity) { + m_id_opacity = program()->uniformLocation(uniformOpacityName()); + if (m_id_opacity < 0) { + qFatal("QSGSimpleMaterialShader does not implement 'uniform lowp float %s' in its fragment shader", + uniformOpacityName()); + } + } else { + m_id_opacity = -1; + } + + resolveUniforms(); + } + + const char *uniformMatrixName() const { return "qt_ModelViewProjectionMatrix"; } + const char *uniformOpacityName() const { return "qt_Opacity"; } + + void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial); + + virtual void updateState(const State *newState, const State *oldState) = 0; + + virtual void resolveUniforms() {} + + virtual QList<QByteArray> attributes() const = 0; + + char const *const *attributeNames() const + { + if (m_attribute_pointers.size()) + return m_attribute_pointers.constData(); + + QList<QByteArray> names = attributes(); + + // Calculate the total number of bytes needed, so we don't get rellocs and + // bad pointers while copying over the individual names. + // Add an extra byte pr entry for the '\0' char. + int total = 0; + for (int i=0; i<names.size(); ++i) + total += names.at(i).size() + 1; + m_attribute_name_data.reserve(total); + + // Copy over the names + for (int i=0; i<names.size(); ++i) { + m_attribute_pointers << m_attribute_name_data.constData() + m_attribute_name_data.size(); + m_attribute_name_data.append(names.at(i)); + m_attribute_name_data.append('\0'); + } + + // Append the "null" terminator + m_attribute_pointers << 0; + + return m_attribute_pointers.constData(); + } + +private: + int m_id_matrix; + int m_id_opacity; + + mutable QByteArray m_attribute_name_data; + mutable QVector<const char *> m_attribute_pointers; +}; + +#define QSG_DECLARE_SIMPLE_SHADER(Shader, State) \ +static QSGMaterialShader *createShader() \ +{ \ + return new Shader; \ +} \ +public: \ +static QSGSimpleMaterial<State> *createMaterial() \ +{ \ + return new QSGSimpleMaterial<State>(createShader); \ +} + + +typedef QSGMaterialShader *(*PtrShaderCreateFunc)(); + + +template <typename State> +class QSGSimpleMaterial : public QSGMaterial +{ + +public: + QSGSimpleMaterial(const State &state, PtrShaderCreateFunc func) + : m_state(state) + , m_func(func) + { + } + + QSGSimpleMaterial(PtrShaderCreateFunc func) + : m_func(func) + { + } + + QSGMaterialShader *createShader() const { return m_func(); } + QSGMaterialType *type() const { return &m_type; } + + State *state() { return &m_state; } + const State *state() const { return &m_state; } + +private: + static QSGMaterialType m_type; + State m_state; + PtrShaderCreateFunc m_func; +}; + +#define QSG_DECLARE_SIMPLE_COMPARABLE_SHADER(Shader, State) \ +static QSGMaterialShader *createShader() \ +{ \ + return new Shader; \ +} \ +public: \ +static QSGSimpleMaterialComparableMaterial<State> *createMaterial() \ +{ \ + return new QSGSimpleMaterialComparableMaterial<State>(createShader); \ +} + +template <typename State> +class QSGSimpleMaterialComparableMaterial : public QSGSimpleMaterial<State> +{ + +public: + QSGSimpleMaterialComparableMaterial(const State &state, PtrShaderCreateFunc func) + : QSGSimpleMaterial<State>(state, func) {} + + QSGSimpleMaterialComparableMaterial(PtrShaderCreateFunc func) + : QSGSimpleMaterial<State>(func) {} + + int compare(const QSGMaterial *other) const { + return QSGSimpleMaterialComparableMaterial<State>::state()->compare(static_cast<const QSGSimpleMaterialComparableMaterial<State> *>(other)->state()); + } +}; + + +template <typename State> +QSGMaterialType QSGSimpleMaterial<State>::m_type; + + +template <typename State> +Q_INLINE_TEMPLATE void QSGSimpleMaterialShader<State>::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) +{ + if (state.isMatrixDirty()) + program()->setUniformValue(m_id_matrix, state.combinedMatrix()); + if (state.isOpacityDirty() && m_id_opacity >= 0) + program()->setUniformValue(m_id_opacity, state.opacity()); + + State *ns = static_cast<QSGSimpleMaterial<State> *>(newMaterial)->state(); + State *old = 0; + if (oldMaterial) + old = static_cast<QSGSimpleMaterial<State> *>(oldMaterial)->state(); + updateState(ns, old); +} + +QT_END_NAMESPACE + +QT_END_HEADER + + +#endif |