f31522130f
Build system (5 gaps hardened): - COOKBOOK_OFFLINE defaults to true (fork-mode) - normalize_patch handles diff -ruN format - New 'repo validate-patches' command (25/25 relibc patches) - 14 patched Qt/Wayland/display recipes added to protected list - relibc archive regenerated with current patch chain Boot fixes (fixable): - Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset) - D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped) - redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped) - daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch) - udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async) - relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs - greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait) - greeter-ui: built and linked (header guard unification, sem_compat stubs removed) - mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps - greeter config: removed stale keymapd dependency from display/greeter services - prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified Unfixable (diagnosed, upstream): - i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort - kded6/greeter-ui: page fault 0x8 — Qt library null deref - Thread panics fd != -1 — Rust std library on Redox - DHCP timeout / eth0 MAC — QEMU user-mode networking - hwrngd/thermald — no hardware RNG/thermal in VM - live preload allocation — BIOS memory fragmentation, continues on demand
2034 lines
70 KiB
C++
2034 lines
70 KiB
C++
// Copyright (C) 2021 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
|
|
#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
|
|
|
|
#include <QtQuick/private/qsgcontext_p.h>
|
|
#include <private/qsgadaptationlayer_p.h>
|
|
#include <private/qquickitem_p.h>
|
|
#include <QtQuick/qsgnode.h>
|
|
#include <QtQuick/qsgtexture.h>
|
|
#include <QFile>
|
|
#include <QRandomGenerator>
|
|
#include "qquickimageparticle_p.h"
|
|
#include "qquickparticleemitter_p.h"
|
|
#include <private/qquicksprite_p.h>
|
|
#include <private/qquickspriteengine_p.h>
|
|
#include <QSGRendererInterface>
|
|
#include <QtQuick/private/qsgplaintexture_p.h>
|
|
#include <private/qqmlglobal_p.h>
|
|
#include <QtQml/qqmlinfo.h>
|
|
#include <QtCore/QtMath>
|
|
#include <rhi/qrhi.h>
|
|
|
|
#include <cmath>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
// Must match the shader code
|
|
#define UNIFORM_ARRAY_SIZE 64
|
|
|
|
class ImageMaterialData
|
|
{
|
|
public:
|
|
ImageMaterialData()
|
|
: texture(nullptr), colorTable(nullptr)
|
|
{}
|
|
|
|
~ImageMaterialData(){
|
|
delete texture;
|
|
delete colorTable;
|
|
}
|
|
|
|
QSGTexture *texture;
|
|
QSGTexture *colorTable;
|
|
float sizeTable[UNIFORM_ARRAY_SIZE];
|
|
float opacityTable[UNIFORM_ARRAY_SIZE];
|
|
|
|
qreal dpr;
|
|
qreal timestamp;
|
|
qreal entry;
|
|
QSizeF animSheetSize;
|
|
};
|
|
|
|
class TabledMaterialRhiShader : public QSGMaterialShader
|
|
{
|
|
public:
|
|
TabledMaterialRhiShader(int viewCount)
|
|
{
|
|
setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_tabled.vert.qsb"), viewCount);
|
|
setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_tabled.frag.qsb"), viewCount);
|
|
}
|
|
|
|
bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
|
|
{
|
|
QByteArray *buf = renderState.uniformData();
|
|
Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
|
|
const int shaderMatrixCount = newMaterial->viewCount();
|
|
const int matrixCount = qMin(renderState.projectionMatrixCount(), shaderMatrixCount);
|
|
|
|
for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
|
|
if (renderState.isMatrixDirty()) {
|
|
const QMatrix4x4 m = renderState.combinedMatrix(viewIndex);
|
|
memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
|
|
}
|
|
}
|
|
|
|
if (renderState.isOpacityDirty()) {
|
|
const float opacity = renderState.opacity();
|
|
memcpy(buf->data() + 64 * shaderMatrixCount, &opacity, 4);
|
|
}
|
|
|
|
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
|
|
|
|
float entry = float(state->entry);
|
|
memcpy(buf->data() + 64 * shaderMatrixCount + 4, &entry, 4);
|
|
|
|
float timestamp = float(state->timestamp);
|
|
memcpy(buf->data() + 64 * shaderMatrixCount + 8, ×tamp, 4);
|
|
|
|
float *p = reinterpret_cast<float *>(buf->data() + 64 * shaderMatrixCount + 16);
|
|
for (int i = 0; i < UNIFORM_ARRAY_SIZE; ++i) {
|
|
*p = state->sizeTable[i];
|
|
p += 4;
|
|
}
|
|
p = reinterpret_cast<float *>(buf->data() + 64 * shaderMatrixCount + 16 + (UNIFORM_ARRAY_SIZE * 4 * 4));
|
|
for (int i = 0; i < UNIFORM_ARRAY_SIZE; ++i) {
|
|
*p = state->opacityTable[i];
|
|
p += 4;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void updateSampledImage(RenderState &renderState, int binding, QSGTexture **texture,
|
|
QSGMaterial *newMaterial, QSGMaterial *) override
|
|
{
|
|
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
|
|
if (binding == 2) {
|
|
state->colorTable->commitTextureOperations(renderState.rhi(), renderState.resourceUpdateBatch());
|
|
*texture = state->colorTable;
|
|
} else if (binding == 1) {
|
|
state->texture->commitTextureOperations(renderState.rhi(), renderState.resourceUpdateBatch());
|
|
*texture = state->texture;
|
|
}
|
|
}
|
|
};
|
|
|
|
class TabledMaterial : public ImageMaterial
|
|
{
|
|
public:
|
|
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override {
|
|
Q_UNUSED(renderMode);
|
|
return new TabledMaterialRhiShader(viewCount());
|
|
}
|
|
QSGMaterialType *type() const override { return &m_type; }
|
|
|
|
ImageMaterialData *state() override { return &m_state; }
|
|
|
|
private:
|
|
static QSGMaterialType m_type;
|
|
ImageMaterialData m_state;
|
|
};
|
|
|
|
QSGMaterialType TabledMaterial::m_type;
|
|
|
|
class DeformableMaterialRhiShader : public QSGMaterialShader
|
|
{
|
|
public:
|
|
DeformableMaterialRhiShader(int viewCount)
|
|
{
|
|
setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_deformed.vert.qsb"), viewCount);
|
|
setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_deformed.frag.qsb"), viewCount);
|
|
}
|
|
|
|
bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
|
|
{
|
|
QByteArray *buf = renderState.uniformData();
|
|
Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
|
|
const int shaderMatrixCount = newMaterial->viewCount();
|
|
const int matrixCount = qMin(renderState.projectionMatrixCount(), shaderMatrixCount);
|
|
|
|
for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
|
|
if (renderState.isMatrixDirty()) {
|
|
const QMatrix4x4 m = renderState.combinedMatrix(viewIndex);
|
|
memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
|
|
}
|
|
}
|
|
|
|
if (renderState.isOpacityDirty()) {
|
|
const float opacity = renderState.opacity();
|
|
memcpy(buf->data() + 64 * shaderMatrixCount, &opacity, 4);
|
|
}
|
|
|
|
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
|
|
|
|
float entry = float(state->entry);
|
|
memcpy(buf->data() + 64 * shaderMatrixCount + 4, &entry, 4);
|
|
|
|
float timestamp = float(state->timestamp);
|
|
memcpy(buf->data() + 64 * shaderMatrixCount + 8, ×tamp, 4);
|
|
|
|
return true;
|
|
}
|
|
|
|
void updateSampledImage(RenderState &renderState, int binding, QSGTexture **texture,
|
|
QSGMaterial *newMaterial, QSGMaterial *) override
|
|
{
|
|
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
|
|
if (binding == 1) {
|
|
state->texture->commitTextureOperations(renderState.rhi(), renderState.resourceUpdateBatch());
|
|
*texture = state->texture;
|
|
}
|
|
}
|
|
};
|
|
|
|
class DeformableMaterial : public ImageMaterial
|
|
{
|
|
public:
|
|
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override {
|
|
Q_UNUSED(renderMode);
|
|
return new DeformableMaterialRhiShader(viewCount());
|
|
}
|
|
QSGMaterialType *type() const override { return &m_type; }
|
|
|
|
ImageMaterialData *state() override { return &m_state; }
|
|
|
|
private:
|
|
static QSGMaterialType m_type;
|
|
ImageMaterialData m_state;
|
|
};
|
|
|
|
QSGMaterialType DeformableMaterial::m_type;
|
|
|
|
class ParticleSpriteMaterialRhiShader : public QSGMaterialShader
|
|
{
|
|
public:
|
|
ParticleSpriteMaterialRhiShader(int viewCount)
|
|
{
|
|
setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_sprite.vert.qsb"), viewCount);
|
|
setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_sprite.frag.qsb"), viewCount);
|
|
}
|
|
|
|
bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
|
|
{
|
|
QByteArray *buf = renderState.uniformData();
|
|
Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
|
|
const int shaderMatrixCount = newMaterial->viewCount();
|
|
const int matrixCount = qMin(renderState.projectionMatrixCount(), shaderMatrixCount);
|
|
|
|
for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
|
|
if (renderState.isMatrixDirty()) {
|
|
const QMatrix4x4 m = renderState.combinedMatrix(viewIndex);
|
|
memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
|
|
}
|
|
}
|
|
|
|
if (renderState.isOpacityDirty()) {
|
|
const float opacity = renderState.opacity();
|
|
memcpy(buf->data() + 64 * shaderMatrixCount, &opacity, 4);
|
|
}
|
|
|
|
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
|
|
|
|
float entry = float(state->entry);
|
|
memcpy(buf->data() + 64 * shaderMatrixCount + 4, &entry, 4);
|
|
|
|
float timestamp = float(state->timestamp);
|
|
memcpy(buf->data() + 64 * shaderMatrixCount + 8, ×tamp, 4);
|
|
|
|
float *p = reinterpret_cast<float *>(buf->data() + 64 * shaderMatrixCount + 16);
|
|
for (int i = 0; i < UNIFORM_ARRAY_SIZE; ++i) {
|
|
*p = state->sizeTable[i];
|
|
p += 4;
|
|
}
|
|
p = reinterpret_cast<float *>(buf->data() + 64 * shaderMatrixCount + 16 + (UNIFORM_ARRAY_SIZE * 4 * 4));
|
|
for (int i = 0; i < UNIFORM_ARRAY_SIZE; ++i) {
|
|
*p = state->opacityTable[i];
|
|
p += 4;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void updateSampledImage(RenderState &renderState, int binding, QSGTexture **texture,
|
|
QSGMaterial *newMaterial, QSGMaterial *) override
|
|
{
|
|
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
|
|
if (binding == 2) {
|
|
state->colorTable->commitTextureOperations(renderState.rhi(), renderState.resourceUpdateBatch());
|
|
*texture = state->colorTable;
|
|
} else if (binding == 1) {
|
|
state->texture->commitTextureOperations(renderState.rhi(), renderState.resourceUpdateBatch());
|
|
*texture = state->texture;
|
|
}
|
|
}
|
|
};
|
|
|
|
class SpriteMaterial : public ImageMaterial
|
|
{
|
|
public:
|
|
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override {
|
|
Q_UNUSED(renderMode);
|
|
return new ParticleSpriteMaterialRhiShader(viewCount());
|
|
}
|
|
QSGMaterialType *type() const override { return &m_type; }
|
|
|
|
ImageMaterialData *state() override { return &m_state; }
|
|
|
|
private:
|
|
static QSGMaterialType m_type;
|
|
ImageMaterialData m_state;
|
|
};
|
|
|
|
QSGMaterialType SpriteMaterial::m_type;
|
|
|
|
class ColoredPointMaterialRhiShader : public QSGMaterialShader
|
|
{
|
|
public:
|
|
ColoredPointMaterialRhiShader(int viewCount)
|
|
{
|
|
setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_coloredpoint.vert.qsb"), viewCount);
|
|
setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_coloredpoint.frag.qsb"), viewCount);
|
|
}
|
|
|
|
bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
|
|
{
|
|
QByteArray *buf = renderState.uniformData();
|
|
Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
|
|
const int shaderMatrixCount = newMaterial->viewCount();
|
|
const int matrixCount = qMin(renderState.projectionMatrixCount(), shaderMatrixCount);
|
|
|
|
for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
|
|
if (renderState.isMatrixDirty()) {
|
|
const QMatrix4x4 m = renderState.combinedMatrix(viewIndex);
|
|
memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
|
|
}
|
|
}
|
|
|
|
if (renderState.isOpacityDirty()) {
|
|
const float opacity = renderState.opacity();
|
|
memcpy(buf->data() + 64 * shaderMatrixCount, &opacity, 4);
|
|
}
|
|
|
|
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
|
|
|
|
float entry = float(state->entry);
|
|
memcpy(buf->data() + 64 * shaderMatrixCount + 4, &entry, 4);
|
|
|
|
float timestamp = float(state->timestamp);
|
|
memcpy(buf->data() + 64 * shaderMatrixCount + 8, ×tamp, 4);
|
|
|
|
float dpr = float(state->dpr);
|
|
memcpy(buf->data() + 64 * shaderMatrixCount + 12, &dpr, 4);
|
|
|
|
return true;
|
|
}
|
|
|
|
void updateSampledImage(RenderState &renderState, int binding, QSGTexture **texture,
|
|
QSGMaterial *newMaterial, QSGMaterial *) override
|
|
{
|
|
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
|
|
if (binding == 1) {
|
|
state->texture->commitTextureOperations(renderState.rhi(), renderState.resourceUpdateBatch());
|
|
*texture = state->texture;
|
|
}
|
|
}
|
|
};
|
|
|
|
class ColoredPointMaterial : public ImageMaterial
|
|
{
|
|
public:
|
|
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override {
|
|
Q_UNUSED(renderMode);
|
|
return new ColoredPointMaterialRhiShader(viewCount());
|
|
}
|
|
QSGMaterialType *type() const override { return &m_type; }
|
|
|
|
ImageMaterialData *state() override { return &m_state; }
|
|
|
|
private:
|
|
static QSGMaterialType m_type;
|
|
ImageMaterialData m_state;
|
|
};
|
|
|
|
QSGMaterialType ColoredPointMaterial::m_type;
|
|
|
|
class ColoredMaterialRhiShader : public ColoredPointMaterialRhiShader
|
|
{
|
|
public:
|
|
ColoredMaterialRhiShader(int viewCount)
|
|
: ColoredPointMaterialRhiShader(viewCount)
|
|
{
|
|
setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_colored.vert.qsb"), viewCount);
|
|
setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_colored.frag.qsb"), viewCount);
|
|
}
|
|
};
|
|
|
|
class ColoredMaterial : public ImageMaterial
|
|
{
|
|
public:
|
|
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override {
|
|
Q_UNUSED(renderMode);
|
|
return new ColoredMaterialRhiShader(viewCount());
|
|
}
|
|
QSGMaterialType *type() const override { return &m_type; }
|
|
|
|
ImageMaterialData *state() override { return &m_state; }
|
|
|
|
private:
|
|
static QSGMaterialType m_type;
|
|
ImageMaterialData m_state;
|
|
};
|
|
|
|
QSGMaterialType ColoredMaterial::m_type;
|
|
|
|
class SimplePointMaterialRhiShader : public QSGMaterialShader
|
|
{
|
|
public:
|
|
SimplePointMaterialRhiShader(int viewCount)
|
|
{
|
|
setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_simplepoint.vert.qsb"), viewCount);
|
|
setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_simplepoint.frag.qsb"), viewCount);
|
|
}
|
|
|
|
bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
|
|
{
|
|
QByteArray *buf = renderState.uniformData();
|
|
Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
|
|
const int shaderMatrixCount = newMaterial->viewCount();
|
|
const int matrixCount = qMin(renderState.projectionMatrixCount(), shaderMatrixCount);
|
|
|
|
for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
|
|
if (renderState.isMatrixDirty()) {
|
|
const QMatrix4x4 m = renderState.combinedMatrix(viewIndex);
|
|
memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
|
|
}
|
|
}
|
|
|
|
if (renderState.isOpacityDirty()) {
|
|
const float opacity = renderState.opacity();
|
|
memcpy(buf->data() + 64 * shaderMatrixCount, &opacity, 4);
|
|
}
|
|
|
|
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
|
|
|
|
float entry = float(state->entry);
|
|
memcpy(buf->data() + 64 * shaderMatrixCount + 4, &entry, 4);
|
|
|
|
float timestamp = float(state->timestamp);
|
|
memcpy(buf->data() + 64 * shaderMatrixCount + 8, ×tamp, 4);
|
|
|
|
float dpr = float(state->dpr);
|
|
memcpy(buf->data() + 64 * shaderMatrixCount + 12, &dpr, 4);
|
|
|
|
return true;
|
|
}
|
|
|
|
void updateSampledImage(RenderState &renderState, int binding, QSGTexture **texture,
|
|
QSGMaterial *newMaterial, QSGMaterial *) override
|
|
{
|
|
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
|
|
if (binding == 1) {
|
|
state->texture->commitTextureOperations(renderState.rhi(), renderState.resourceUpdateBatch());
|
|
*texture = state->texture;
|
|
}
|
|
}
|
|
};
|
|
|
|
class SimplePointMaterial : public ImageMaterial
|
|
{
|
|
public:
|
|
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override {
|
|
Q_UNUSED(renderMode);
|
|
return new SimplePointMaterialRhiShader(viewCount());
|
|
}
|
|
QSGMaterialType *type() const override { return &m_type; }
|
|
|
|
ImageMaterialData *state() override { return &m_state; }
|
|
|
|
private:
|
|
static QSGMaterialType m_type;
|
|
ImageMaterialData m_state;
|
|
};
|
|
|
|
QSGMaterialType SimplePointMaterial::m_type;
|
|
|
|
void fillUniformArrayFromImage(float* array, const QImage& img, int size)
|
|
{
|
|
if (img.isNull()){
|
|
for (int i=0; i<size; i++)
|
|
array[i] = 1.0;
|
|
return;
|
|
}
|
|
QImage scaled = img.scaled(size,1);
|
|
for (int i=0; i<size; i++)
|
|
array[i] = qAlpha(scaled.pixel(i,0))/255.0;
|
|
}
|
|
|
|
/*!
|
|
\qmltype ImageParticle
|
|
\nativetype QQuickImageParticle
|
|
\inqmlmodule QtQuick.Particles
|
|
\inherits ParticlePainter
|
|
\brief For visualizing logical particles using an image.
|
|
\ingroup qtquick-particles
|
|
|
|
This element renders a logical particle as an image. The image can be
|
|
\list
|
|
\li colorized
|
|
\li rotated
|
|
\li deformed
|
|
\li a sprite-based animation
|
|
\endlist
|
|
|
|
ImageParticles implictly share data on particles if multiple ImageParticles are painting
|
|
the same logical particle group. This is broken down along the four capabilities listed
|
|
above. So if one ImageParticle defines data for rendering the particles in one of those
|
|
capabilities, and the other does not, then both will draw the particles the same in that
|
|
aspect automatically. This is primarily useful when there is some random variation on
|
|
the particle which is supposed to stay with it when switching painters. If both ImageParticles
|
|
define how they should appear for that aspect, they diverge and each appears as it is defined.
|
|
|
|
This sharing of data happens behind the scenes based off of whether properties were implicitly or explicitly
|
|
set. One drawback of the current implementation is that it is only possible to reset the capabilities as a whole.
|
|
So if you explicitly set an attribute affecting color, such as redVariation, and then reset it (by setting redVariation
|
|
to undefined), all color data will be reset and it will begin to have an implicit value of any shared color from
|
|
other ImageParticles.
|
|
|
|
\note The maximum number of image particles is limited to 16383.
|
|
*/
|
|
/*!
|
|
\qmlproperty url QtQuick.Particles::ImageParticle::source
|
|
|
|
The source image to be used.
|
|
|
|
If the image is a sprite animation, use the sprite property instead.
|
|
|
|
Since Qt 5.2, some default images are provided as resources to aid prototyping:
|
|
\table
|
|
\row
|
|
\li qrc:///particleresources/star.png
|
|
\li \inlineimage particles/star.png
|
|
\row
|
|
\li qrc:///particleresources/glowdot.png
|
|
\li \inlineimage particles/glowdot.png
|
|
\row
|
|
\li qrc:///particleresources/fuzzydot.png
|
|
\li \inlineimage particles/fuzzydot.png
|
|
\endtable
|
|
|
|
Note that the images are white and semi-transparent, to allow colorization
|
|
and alpha levels to have maximum effect.
|
|
*/
|
|
/*!
|
|
\qmlproperty list<Sprite> QtQuick.Particles::ImageParticle::sprites
|
|
|
|
The sprite or sprites used to draw this particle.
|
|
|
|
Note that the sprite image will be scaled to a square based on the size of
|
|
the particle being rendered.
|
|
|
|
For full details, see the \l{Sprite Animations} overview.
|
|
*/
|
|
/*!
|
|
\qmlproperty url QtQuick.Particles::ImageParticle::colorTable
|
|
|
|
An image whose color will be used as a 1D texture to determine color over life. E.g. when
|
|
the particle is halfway through its lifetime, it will have the color specified halfway
|
|
across the image.
|
|
|
|
This color is blended with the color property and the color of the source image.
|
|
*/
|
|
/*!
|
|
\qmlproperty url QtQuick.Particles::ImageParticle::sizeTable
|
|
|
|
An image whose opacity will be used as a 1D texture to determine size over life.
|
|
|
|
This property is expected to be removed shortly, in favor of custom easing curves to determine size over life.
|
|
*/
|
|
/*!
|
|
\qmlproperty url QtQuick.Particles::ImageParticle::opacityTable
|
|
|
|
An image whose opacity will be used as a 1D texture to determine size over life.
|
|
|
|
This property is expected to be removed shortly, in favor of custom easing curves to determine opacity over life.
|
|
*/
|
|
/*!
|
|
\qmlproperty color QtQuick.Particles::ImageParticle::color
|
|
|
|
If a color is specified, the provided image will be colorized with it.
|
|
|
|
Default is white (no change).
|
|
*/
|
|
/*!
|
|
\qmlproperty real QtQuick.Particles::ImageParticle::colorVariation
|
|
|
|
This number represents the color variation applied to individual particles.
|
|
Setting colorVariation is the same as setting redVariation, greenVariation,
|
|
and blueVariation to the same number.
|
|
|
|
Each channel can vary between particle by up to colorVariation from its usual color.
|
|
|
|
Color is measured, per channel, from 0.0 to 1.0.
|
|
|
|
Default is 0.0
|
|
*/
|
|
/*!
|
|
\qmlproperty real QtQuick.Particles::ImageParticle::redVariation
|
|
The variation in the red color channel between particles.
|
|
|
|
Color is measured, per channel, from 0.0 to 1.0.
|
|
|
|
Default is 0.0
|
|
*/
|
|
/*!
|
|
\qmlproperty real QtQuick.Particles::ImageParticle::greenVariation
|
|
The variation in the green color channel between particles.
|
|
|
|
Color is measured, per channel, from 0.0 to 1.0.
|
|
|
|
Default is 0.0
|
|
*/
|
|
/*!
|
|
\qmlproperty real QtQuick.Particles::ImageParticle::blueVariation
|
|
The variation in the blue color channel between particles.
|
|
|
|
Color is measured, per channel, from 0.0 to 1.0.
|
|
|
|
Default is 0.0
|
|
*/
|
|
/*!
|
|
\qmlproperty real QtQuick.Particles::ImageParticle::alpha
|
|
An alpha to be applied to the image. This value is multiplied by the value in
|
|
the image, and the value in the color property.
|
|
|
|
Particles have additive blending, so lower alpha on single particles leads
|
|
to stronger effects when multiple particles overlap.
|
|
|
|
Alpha is measured from 0.0 to 1.0.
|
|
|
|
Default is 1.0
|
|
*/
|
|
/*!
|
|
\qmlproperty real QtQuick.Particles::ImageParticle::alphaVariation
|
|
The variation in the alpha channel between particles.
|
|
|
|
Alpha is measured from 0.0 to 1.0.
|
|
|
|
Default is 0.0
|
|
*/
|
|
/*!
|
|
\qmlproperty real QtQuick.Particles::ImageParticle::rotation
|
|
|
|
If set the image will be rotated by this many degrees before it is drawn.
|
|
|
|
The particle coordinates are not transformed.
|
|
*/
|
|
/*!
|
|
\qmlproperty real QtQuick.Particles::ImageParticle::rotationVariation
|
|
|
|
If set the rotation of individual particles will vary by up to this much
|
|
between particles.
|
|
|
|
*/
|
|
/*!
|
|
\qmlproperty real QtQuick.Particles::ImageParticle::rotationVelocity
|
|
|
|
If set particles will rotate at this velocity in degrees/second.
|
|
*/
|
|
/*!
|
|
\qmlproperty real QtQuick.Particles::ImageParticle::rotationVelocityVariation
|
|
|
|
If set the rotationVelocity of individual particles will vary by up to this much
|
|
between particles.
|
|
|
|
*/
|
|
/*!
|
|
\qmlproperty bool QtQuick.Particles::ImageParticle::autoRotation
|
|
|
|
If set to true then a rotation will be applied on top of the particles rotation, so
|
|
that it faces the direction of travel. So to face away from the direction of travel,
|
|
set autoRotation to true and rotation to 180.
|
|
|
|
Default is false
|
|
*/
|
|
/*!
|
|
\qmlproperty StochasticDirection QtQuick.Particles::ImageParticle::xVector
|
|
|
|
Allows you to deform the particle image when drawn. The rectangular image will
|
|
be deformed so that the horizontal sides are in the shape of this vector instead
|
|
of (1,0).
|
|
*/
|
|
/*!
|
|
\qmlproperty StochasticDirection QtQuick.Particles::ImageParticle::yVector
|
|
|
|
Allows you to deform the particle image when drawn. The rectangular image will
|
|
be deformed so that the vertical sides are in the shape of this vector instead
|
|
of (0,1).
|
|
*/
|
|
/*!
|
|
\qmlproperty EntryEffect QtQuick.Particles::ImageParticle::entryEffect
|
|
|
|
This property provides basic and cheap entrance and exit effects for the particles.
|
|
For fine-grained control, see sizeTable and opacityTable.
|
|
|
|
Acceptable values are
|
|
|
|
\value ImageParticle.None Particles just appear and disappear.
|
|
\value ImageParticle.Fade Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end.
|
|
\value ImageParticle.Scale Particles scale in from 0 size at the start of their life, and scale back to 0 at the end.
|
|
|
|
The default value is \c ImageParticle.Fade.
|
|
*/
|
|
/*!
|
|
\qmlproperty bool QtQuick.Particles::ImageParticle::spritesInterpolate
|
|
|
|
If set to true, sprite particles will interpolate between sprite frames each rendered frame, making
|
|
the sprites look smoother.
|
|
|
|
Default is true.
|
|
*/
|
|
|
|
/*!
|
|
\qmlproperty Status QtQuick.Particles::ImageParticle::status
|
|
|
|
The status of loading the image.
|
|
*/
|
|
|
|
|
|
QQuickImageParticle::QQuickImageParticle(QQuickItem* parent)
|
|
: QQuickParticlePainter(parent)
|
|
, m_color_variation(0.0)
|
|
, m_outgoingNode(nullptr)
|
|
, m_material(nullptr)
|
|
, m_alphaVariation(0.0)
|
|
, m_alpha(1.0)
|
|
, m_redVariation(0.0)
|
|
, m_greenVariation(0.0)
|
|
, m_blueVariation(0.0)
|
|
, m_rotation(0)
|
|
, m_rotationVariation(0)
|
|
, m_rotationVelocity(0)
|
|
, m_rotationVelocityVariation(0)
|
|
, m_autoRotation(false)
|
|
, m_xVector(nullptr)
|
|
, m_yVector(nullptr)
|
|
, m_spriteEngine(nullptr)
|
|
, m_spritesInterpolate(true)
|
|
, m_explicitColor(false)
|
|
, m_explicitRotation(false)
|
|
, m_explicitDeformation(false)
|
|
, m_explicitAnimation(false)
|
|
, m_bypassOptimizations(false)
|
|
, perfLevel(Unknown)
|
|
, m_targetPerfLevel(Unknown)
|
|
, m_debugMode(false)
|
|
, m_entryEffect(Fade)
|
|
, m_startedImageLoading(0)
|
|
, m_rhi(nullptr)
|
|
, m_apiChecked(false)
|
|
, m_dpr(1.0)
|
|
, m_previousActive(false)
|
|
{
|
|
setFlag(ItemHasContents);
|
|
}
|
|
|
|
QQuickImageParticle::~QQuickImageParticle()
|
|
{
|
|
clearShadows();
|
|
}
|
|
|
|
QQmlListProperty<QQuickSprite> QQuickImageParticle::sprites()
|
|
{
|
|
return QQmlListProperty<QQuickSprite>(this, &m_sprites,
|
|
spriteAppend, spriteCount, spriteAt,
|
|
spriteClear, spriteReplace, spriteRemoveLast);
|
|
}
|
|
|
|
void QQuickImageParticle::sceneGraphInvalidated()
|
|
{
|
|
m_nodes.clear();
|
|
m_material = nullptr;
|
|
delete m_outgoingNode;
|
|
m_outgoingNode = nullptr;
|
|
m_apiChecked = false;
|
|
}
|
|
|
|
void QQuickImageParticle::setImage(const QUrl &image)
|
|
{
|
|
if (image.isEmpty()){
|
|
if (m_image) {
|
|
m_image.reset();
|
|
emit imageChanged();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!m_image)
|
|
m_image.reset(new ImageData);
|
|
if (image == m_image->source)
|
|
return;
|
|
m_image->source = image;
|
|
emit imageChanged();
|
|
reset();
|
|
}
|
|
|
|
|
|
void QQuickImageParticle::setColortable(const QUrl &table)
|
|
{
|
|
if (table.isEmpty()){
|
|
if (m_colorTable) {
|
|
m_colorTable.reset();
|
|
emit colortableChanged();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!m_colorTable)
|
|
m_colorTable.reset(new ImageData);
|
|
if (table == m_colorTable->source)
|
|
return;
|
|
m_colorTable->source = table;
|
|
emit colortableChanged();
|
|
reset();
|
|
}
|
|
|
|
void QQuickImageParticle::setSizetable(const QUrl &table)
|
|
{
|
|
if (table.isEmpty()){
|
|
if (m_sizeTable) {
|
|
m_sizeTable.reset();
|
|
emit sizetableChanged();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!m_sizeTable)
|
|
m_sizeTable.reset(new ImageData);
|
|
if (table == m_sizeTable->source)
|
|
return;
|
|
m_sizeTable->source = table;
|
|
emit sizetableChanged();
|
|
reset();
|
|
}
|
|
|
|
void QQuickImageParticle::setOpacitytable(const QUrl &table)
|
|
{
|
|
if (table.isEmpty()){
|
|
if (m_opacityTable) {
|
|
m_opacityTable.reset();
|
|
emit opacitytableChanged();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!m_opacityTable)
|
|
m_opacityTable.reset(new ImageData);
|
|
if (table == m_opacityTable->source)
|
|
return;
|
|
m_opacityTable->source = table;
|
|
emit opacitytableChanged();
|
|
reset();
|
|
}
|
|
|
|
void QQuickImageParticle::setColor(const QColor &color)
|
|
{
|
|
if (color == m_color)
|
|
return;
|
|
m_color = color;
|
|
emit colorChanged();
|
|
m_explicitColor = true;
|
|
checkPerfLevel(ColoredPoint);
|
|
}
|
|
|
|
void QQuickImageParticle::setColorVariation(qreal var)
|
|
{
|
|
if (var == m_color_variation)
|
|
return;
|
|
m_color_variation = var;
|
|
emit colorVariationChanged();
|
|
m_explicitColor = true;
|
|
checkPerfLevel(ColoredPoint);
|
|
}
|
|
|
|
void QQuickImageParticle::setAlphaVariation(qreal arg)
|
|
{
|
|
if (m_alphaVariation != arg) {
|
|
m_alphaVariation = arg;
|
|
emit alphaVariationChanged(arg);
|
|
}
|
|
m_explicitColor = true;
|
|
checkPerfLevel(ColoredPoint);
|
|
}
|
|
|
|
void QQuickImageParticle::setAlpha(qreal arg)
|
|
{
|
|
if (m_alpha != arg) {
|
|
m_alpha = arg;
|
|
emit alphaChanged(arg);
|
|
}
|
|
m_explicitColor = true;
|
|
checkPerfLevel(ColoredPoint);
|
|
}
|
|
|
|
void QQuickImageParticle::setRedVariation(qreal arg)
|
|
{
|
|
if (m_redVariation != arg) {
|
|
m_redVariation = arg;
|
|
emit redVariationChanged(arg);
|
|
}
|
|
m_explicitColor = true;
|
|
checkPerfLevel(ColoredPoint);
|
|
}
|
|
|
|
void QQuickImageParticle::setGreenVariation(qreal arg)
|
|
{
|
|
if (m_greenVariation != arg) {
|
|
m_greenVariation = arg;
|
|
emit greenVariationChanged(arg);
|
|
}
|
|
m_explicitColor = true;
|
|
checkPerfLevel(ColoredPoint);
|
|
}
|
|
|
|
void QQuickImageParticle::setBlueVariation(qreal arg)
|
|
{
|
|
if (m_blueVariation != arg) {
|
|
m_blueVariation = arg;
|
|
emit blueVariationChanged(arg);
|
|
}
|
|
m_explicitColor = true;
|
|
checkPerfLevel(ColoredPoint);
|
|
}
|
|
|
|
void QQuickImageParticle::setRotation(qreal arg)
|
|
{
|
|
if (m_rotation != arg) {
|
|
m_rotation = arg;
|
|
emit rotationChanged(arg);
|
|
}
|
|
m_explicitRotation = true;
|
|
checkPerfLevel(Deformable);
|
|
}
|
|
|
|
void QQuickImageParticle::setRotationVariation(qreal arg)
|
|
{
|
|
if (m_rotationVariation != arg) {
|
|
m_rotationVariation = arg;
|
|
emit rotationVariationChanged(arg);
|
|
}
|
|
m_explicitRotation = true;
|
|
checkPerfLevel(Deformable);
|
|
}
|
|
|
|
void QQuickImageParticle::setRotationVelocity(qreal arg)
|
|
{
|
|
if (m_rotationVelocity != arg) {
|
|
m_rotationVelocity = arg;
|
|
emit rotationVelocityChanged(arg);
|
|
}
|
|
m_explicitRotation = true;
|
|
checkPerfLevel(Deformable);
|
|
}
|
|
|
|
void QQuickImageParticle::setRotationVelocityVariation(qreal arg)
|
|
{
|
|
if (m_rotationVelocityVariation != arg) {
|
|
m_rotationVelocityVariation = arg;
|
|
emit rotationVelocityVariationChanged(arg);
|
|
}
|
|
m_explicitRotation = true;
|
|
checkPerfLevel(Deformable);
|
|
}
|
|
|
|
void QQuickImageParticle::setAutoRotation(bool arg)
|
|
{
|
|
if (m_autoRotation != arg) {
|
|
m_autoRotation = arg;
|
|
emit autoRotationChanged(arg);
|
|
}
|
|
m_explicitRotation = true;
|
|
checkPerfLevel(Deformable);
|
|
}
|
|
|
|
void QQuickImageParticle::setXVector(QQuickDirection* arg)
|
|
{
|
|
if (m_xVector != arg) {
|
|
m_xVector = arg;
|
|
emit xVectorChanged(arg);
|
|
}
|
|
m_explicitDeformation = true;
|
|
checkPerfLevel(Deformable);
|
|
}
|
|
|
|
void QQuickImageParticle::setYVector(QQuickDirection* arg)
|
|
{
|
|
if (m_yVector != arg) {
|
|
m_yVector = arg;
|
|
emit yVectorChanged(arg);
|
|
}
|
|
m_explicitDeformation = true;
|
|
checkPerfLevel(Deformable);
|
|
}
|
|
|
|
void QQuickImageParticle::setSpritesInterpolate(bool arg)
|
|
{
|
|
if (m_spritesInterpolate != arg) {
|
|
m_spritesInterpolate = arg;
|
|
emit spritesInterpolateChanged(arg);
|
|
}
|
|
}
|
|
|
|
void QQuickImageParticle::setBypassOptimizations(bool arg)
|
|
{
|
|
if (m_bypassOptimizations != arg) {
|
|
m_bypassOptimizations = arg;
|
|
emit bypassOptimizationsChanged(arg);
|
|
}
|
|
// Applies regardless of perfLevel
|
|
reset();
|
|
}
|
|
|
|
void QQuickImageParticle::setEntryEffect(EntryEffect arg)
|
|
{
|
|
if (m_entryEffect != arg) {
|
|
m_entryEffect = arg;
|
|
if (m_material)
|
|
getState(m_material)->entry = (qreal) m_entryEffect;
|
|
emit entryEffectChanged(arg);
|
|
}
|
|
}
|
|
|
|
void QQuickImageParticle::resetColor()
|
|
{
|
|
m_explicitColor = false;
|
|
for (auto groupId : groupIds()) {
|
|
for (QQuickParticleData* d : std::as_const(m_system->groupData[groupId]->data)) {
|
|
if (d->colorOwner == this) {
|
|
d->colorOwner = nullptr;
|
|
}
|
|
}
|
|
}
|
|
m_color = QColor();
|
|
m_color_variation = 0.0f;
|
|
m_redVariation = 0.0f;
|
|
m_blueVariation = 0.0f;
|
|
m_greenVariation = 0.0f;
|
|
m_alpha = 1.0f;
|
|
m_alphaVariation = 0.0f;
|
|
}
|
|
|
|
void QQuickImageParticle::resetRotation()
|
|
{
|
|
m_explicitRotation = false;
|
|
for (auto groupId : groupIds()) {
|
|
for (QQuickParticleData* d : std::as_const(m_system->groupData[groupId]->data)) {
|
|
if (d->rotationOwner == this) {
|
|
d->rotationOwner = nullptr;
|
|
}
|
|
}
|
|
}
|
|
m_rotation = 0;
|
|
m_rotationVariation = 0;
|
|
m_rotationVelocity = 0;
|
|
m_rotationVelocityVariation = 0;
|
|
m_autoRotation = false;
|
|
}
|
|
|
|
void QQuickImageParticle::resetDeformation()
|
|
{
|
|
m_explicitDeformation = false;
|
|
for (auto groupId : groupIds()) {
|
|
for (QQuickParticleData* d : std::as_const(m_system->groupData[groupId]->data)) {
|
|
if (d->deformationOwner == this) {
|
|
d->deformationOwner = nullptr;
|
|
}
|
|
}
|
|
}
|
|
if (m_xVector)
|
|
delete m_xVector;
|
|
if (m_yVector)
|
|
delete m_yVector;
|
|
m_xVector = nullptr;
|
|
m_yVector = nullptr;
|
|
}
|
|
|
|
void QQuickImageParticle::reset()
|
|
{
|
|
QQuickParticlePainter::reset();
|
|
m_pleaseReset = true;
|
|
update();
|
|
}
|
|
|
|
|
|
void QQuickImageParticle::invalidateSceneGraph()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
void QQuickImageParticle::createEngine()
|
|
{
|
|
if (m_spriteEngine)
|
|
delete m_spriteEngine;
|
|
if (m_sprites.size()) {
|
|
m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
|
|
connect(m_spriteEngine, &QQuickStochasticEngine::stateChanged,
|
|
this, &QQuickImageParticle::spriteAdvance, Qt::DirectConnection);
|
|
m_explicitAnimation = true;
|
|
} else {
|
|
m_spriteEngine = nullptr;
|
|
m_explicitAnimation = false;
|
|
}
|
|
reset();
|
|
}
|
|
|
|
static QSGGeometry::Attribute SimplePointParticle_Attributes[] = {
|
|
QSGGeometry::Attribute::create(0, 2, QSGGeometry::FloatType, true), // Position
|
|
QSGGeometry::Attribute::create(1, 4, QSGGeometry::FloatType), // Data
|
|
QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType) // Vectors
|
|
};
|
|
|
|
static QSGGeometry::AttributeSet SimplePointParticle_AttributeSet =
|
|
{
|
|
3, // Attribute Count
|
|
( 2 + 4 + 4 ) * sizeof(float),
|
|
SimplePointParticle_Attributes
|
|
};
|
|
|
|
static QSGGeometry::Attribute ColoredPointParticle_Attributes[] = {
|
|
QSGGeometry::Attribute::create(0, 2, QSGGeometry::FloatType, true), // Position
|
|
QSGGeometry::Attribute::create(1, 4, QSGGeometry::FloatType), // Data
|
|
QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType), // Vectors
|
|
QSGGeometry::Attribute::create(3, 4, QSGGeometry::UnsignedByteType), // Colors
|
|
};
|
|
|
|
static QSGGeometry::AttributeSet ColoredPointParticle_AttributeSet =
|
|
{
|
|
4, // Attribute Count
|
|
( 2 + 4 + 4 ) * sizeof(float) + 4 * sizeof(uchar),
|
|
ColoredPointParticle_Attributes
|
|
};
|
|
|
|
static QSGGeometry::Attribute ColoredParticle_Attributes[] = {
|
|
QSGGeometry::Attribute::create(0, 2, QSGGeometry::FloatType, true), // Position
|
|
QSGGeometry::Attribute::create(1, 4, QSGGeometry::FloatType), // Data
|
|
QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType), // Vectors
|
|
QSGGeometry::Attribute::create(3, 4, QSGGeometry::UnsignedByteType), // Colors
|
|
QSGGeometry::Attribute::create(4, 4, QSGGeometry::UnsignedByteType), // TexCoord
|
|
};
|
|
|
|
static QSGGeometry::AttributeSet ColoredParticle_AttributeSet =
|
|
{
|
|
5, // Attribute Count
|
|
( 2 + 4 + 4 ) * sizeof(float) + (4 + 4) * sizeof(uchar),
|
|
ColoredParticle_Attributes
|
|
};
|
|
|
|
static QSGGeometry::Attribute DeformableParticle_Attributes[] = {
|
|
QSGGeometry::Attribute::create(0, 4, QSGGeometry::FloatType), // Position & Rotation
|
|
QSGGeometry::Attribute::create(1, 4, QSGGeometry::FloatType), // Data
|
|
QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType), // Vectors
|
|
QSGGeometry::Attribute::create(3, 4, QSGGeometry::UnsignedByteType), // Colors
|
|
QSGGeometry::Attribute::create(4, 4, QSGGeometry::FloatType), // DeformationVectors
|
|
QSGGeometry::Attribute::create(5, 4, QSGGeometry::UnsignedByteType), // TexCoord & autoRotate
|
|
};
|
|
|
|
static QSGGeometry::AttributeSet DeformableParticle_AttributeSet =
|
|
{
|
|
6, // Attribute Count
|
|
(4 + 4 + 4 + 4) * sizeof(float) + (4 + 4) * sizeof(uchar),
|
|
DeformableParticle_Attributes
|
|
};
|
|
|
|
static QSGGeometry::Attribute SpriteParticle_Attributes[] = {
|
|
QSGGeometry::Attribute::create(0, 4, QSGGeometry::FloatType), // Position & Rotation
|
|
QSGGeometry::Attribute::create(1, 4, QSGGeometry::FloatType), // Data
|
|
QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType), // Vectors
|
|
QSGGeometry::Attribute::create(3, 4, QSGGeometry::UnsignedByteType), // Colors
|
|
QSGGeometry::Attribute::create(4, 4, QSGGeometry::FloatType), // DeformationVectors
|
|
QSGGeometry::Attribute::create(5, 4, QSGGeometry::UnsignedByteType), // TexCoord & autoRotate
|
|
QSGGeometry::Attribute::create(6, 3, QSGGeometry::FloatType), // Anim Data
|
|
QSGGeometry::Attribute::create(7, 3, QSGGeometry::FloatType) // Anim Pos
|
|
};
|
|
|
|
static QSGGeometry::AttributeSet SpriteParticle_AttributeSet =
|
|
{
|
|
8, // Attribute Count
|
|
(4 + 4 + 4 + 4 + 3 + 3) * sizeof(float) + (4 + 4) * sizeof(uchar),
|
|
SpriteParticle_Attributes
|
|
};
|
|
|
|
void QQuickImageParticle::clearShadows()
|
|
{
|
|
foreach (const QList<QQuickParticleData*> data, m_shadowData)
|
|
qDeleteAll(data);
|
|
m_shadowData.clear();
|
|
}
|
|
|
|
//Only call if you need to, may initialize the whole array first time
|
|
QQuickParticleData* QQuickImageParticle::getShadowDatum(QQuickParticleData* datum)
|
|
{
|
|
//Will return datum if the datum is a sentinel or uninitialized, to centralize that one check
|
|
if (datum->systemIndex == -1)
|
|
return datum;
|
|
if (!m_shadowData.contains(datum->groupId)) {
|
|
QQuickParticleGroupData* gd = m_system->groupData[datum->groupId];
|
|
QList<QQuickParticleData*> data;
|
|
const int gdSize = gd->size();
|
|
data.reserve(gdSize);
|
|
for (int i = 0; i < gdSize; i++) {
|
|
QQuickParticleData* datum = new QQuickParticleData;
|
|
*datum = *(gd->data[i]);
|
|
data << datum;
|
|
}
|
|
m_shadowData.insert(datum->groupId, data);
|
|
}
|
|
//### If dynamic resize is added, remember to potentially resize the shadow data on out-of-bounds access request
|
|
|
|
return m_shadowData[datum->groupId][datum->index];
|
|
}
|
|
|
|
void QQuickImageParticle::checkPerfLevel(PerformanceLevel level)
|
|
{
|
|
if (m_targetPerfLevel < level) {
|
|
m_targetPerfLevel = level;
|
|
reset();
|
|
}
|
|
}
|
|
|
|
bool QQuickImageParticle::loadingSomething()
|
|
{
|
|
return (m_image && m_image->pix.isLoading())
|
|
|| (m_colorTable && m_colorTable->pix.isLoading())
|
|
|| (m_sizeTable && m_sizeTable->pix.isLoading())
|
|
|| (m_opacityTable && m_opacityTable->pix.isLoading())
|
|
|| (m_spriteEngine && m_spriteEngine->isLoading());
|
|
}
|
|
|
|
void QQuickImageParticle::mainThreadFetchImageData()
|
|
{
|
|
const QQmlContext *context = nullptr;
|
|
QQmlEngine *engine = nullptr;
|
|
const auto loadPix = [&](ImageData *image) {
|
|
if (!engine) {
|
|
context = qmlContext(this);
|
|
engine = context->engine();
|
|
}
|
|
image->pix.load(engine, context->resolvedUrl(image->source));
|
|
};
|
|
|
|
|
|
if (m_image) {//ImageData created on setSource
|
|
m_image->pix.clear(this);
|
|
loadPix(m_image.get());
|
|
}
|
|
|
|
if (m_spriteEngine)
|
|
m_spriteEngine->startAssemblingImage();
|
|
|
|
if (m_colorTable)
|
|
loadPix(m_colorTable.get());
|
|
|
|
if (m_sizeTable)
|
|
loadPix(m_sizeTable.get());
|
|
|
|
if (m_opacityTable)
|
|
loadPix(m_opacityTable.get());
|
|
|
|
m_startedImageLoading = 2;
|
|
}
|
|
|
|
void QQuickImageParticle::buildParticleNodes(QSGNode** passThrough)
|
|
{
|
|
// Starts async parts, like loading images, on gui thread
|
|
// Not on individual properties, because we delay until system is running
|
|
if (*passThrough || loadingSomething())
|
|
return;
|
|
|
|
if (m_startedImageLoading == 0) {
|
|
m_startedImageLoading = 1;
|
|
//stage 1 is in gui thread
|
|
QQuickImageParticle::staticMetaObject.invokeMethod(this, "mainThreadFetchImageData", Qt::QueuedConnection);
|
|
} else if (m_startedImageLoading == 2) {
|
|
finishBuildParticleNodes(passThrough); //rest happens in render thread
|
|
}
|
|
|
|
//No mutex, because it's slow and a compare that fails due to a race condition means just a dropped frame
|
|
}
|
|
|
|
void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node)
|
|
{
|
|
if (!m_rhi)
|
|
return;
|
|
|
|
if (m_count * 4 > 0xffff) {
|
|
// Index data is ushort.
|
|
qmlInfo(this) << "ImageParticle: Too many particles - maximum 16383 per ImageParticle";
|
|
return;
|
|
}
|
|
|
|
if (m_count <= 0)
|
|
return;
|
|
|
|
m_debugMode = m_system->m_debugMode;
|
|
|
|
if (m_sprites.size() || m_bypassOptimizations) {
|
|
perfLevel = Sprites;
|
|
} else if (m_colorTable || m_sizeTable || m_opacityTable) {
|
|
perfLevel = Tabled;
|
|
} else if (m_autoRotation || m_rotation || m_rotationVariation
|
|
|| m_rotationVelocity || m_rotationVelocityVariation
|
|
|| m_xVector || m_yVector) {
|
|
perfLevel = Deformable;
|
|
} else if (m_alphaVariation || m_alpha != 1.0 || m_color.isValid() || m_color_variation
|
|
|| m_redVariation || m_blueVariation || m_greenVariation) {
|
|
perfLevel = ColoredPoint;
|
|
} else {
|
|
perfLevel = SimplePoint;
|
|
}
|
|
|
|
for (auto groupId : groupIds()) {
|
|
//For sharing higher levels, need to have highest used so it renders
|
|
for (QQuickParticlePainter* p : std::as_const(m_system->groupData[groupId]->painters)) {
|
|
QQuickImageParticle* other = qobject_cast<QQuickImageParticle*>(p);
|
|
if (other){
|
|
if (other->perfLevel > perfLevel) {
|
|
if (other->perfLevel >= Tabled){//Deformable is the highest level needed for this, anything higher isn't shared (or requires your own sprite)
|
|
if (perfLevel < Deformable)
|
|
perfLevel = Deformable;
|
|
} else {
|
|
perfLevel = other->perfLevel;
|
|
}
|
|
} else if (other->perfLevel < perfLevel) {
|
|
other->reset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Points with a size other than 1 are an optional feature with QRhi
|
|
// because some of the underlying APIs have no support for this.
|
|
// Therefore, avoid the point sprite path with APIs like Direct3D.
|
|
if (perfLevel < Colored && !m_rhi->isFeatureSupported(QRhi::VertexShaderPointSize))
|
|
perfLevel = Colored;
|
|
|
|
if (perfLevel >= ColoredPoint && !m_color.isValid())
|
|
m_color = QColor(Qt::white);//Hidden default, but different from unset
|
|
|
|
m_targetPerfLevel = perfLevel;
|
|
|
|
clearShadows();
|
|
if (m_material)
|
|
m_material = nullptr;
|
|
|
|
//Setup material
|
|
QImage colortable;
|
|
QImage sizetable;
|
|
QImage opacitytable;
|
|
QImage image;
|
|
bool imageLoaded = false;
|
|
switch (perfLevel) {//Fallthrough intended
|
|
case Sprites:
|
|
{
|
|
if (!m_spriteEngine) {
|
|
qWarning() << "ImageParticle: No sprite engine...";
|
|
//Sprite performance mode with static image is supported, but not advised
|
|
//Note that in this case it always uses shadow data
|
|
} else {
|
|
image = m_spriteEngine->assembledImage();
|
|
if (image.isNull())//Warning is printed in engine
|
|
return;
|
|
imageLoaded = true;
|
|
}
|
|
m_material = new SpriteMaterial;
|
|
ImageMaterialData *state = getState(m_material);
|
|
if (imageLoaded)
|
|
state->texture = QSGPlainTexture::fromImage(image);
|
|
state->animSheetSize = QSizeF(image.size() / image.devicePixelRatio());
|
|
if (m_spriteEngine)
|
|
m_spriteEngine->setCount(m_count);
|
|
}
|
|
Q_FALLTHROUGH();
|
|
case Tabled:
|
|
{
|
|
if (!m_material)
|
|
m_material = new TabledMaterial;
|
|
|
|
if (m_colorTable) {
|
|
if (m_colorTable->pix.isReady())
|
|
colortable = m_colorTable->pix.image();
|
|
else
|
|
qmlWarning(this) << "Error loading color table: " << m_colorTable->pix.error();
|
|
}
|
|
|
|
if (m_sizeTable) {
|
|
if (m_sizeTable->pix.isReady())
|
|
sizetable = m_sizeTable->pix.image();
|
|
else
|
|
qmlWarning(this) << "Error loading size table: " << m_sizeTable->pix.error();
|
|
}
|
|
|
|
if (m_opacityTable) {
|
|
if (m_opacityTable->pix.isReady())
|
|
opacitytable = m_opacityTable->pix.image();
|
|
else
|
|
qmlWarning(this) << "Error loading opacity table: " << m_opacityTable->pix.error();
|
|
}
|
|
|
|
if (colortable.isNull()){//###Goes through image just for this
|
|
colortable = QImage(1,1,QImage::Format_ARGB32_Premultiplied);
|
|
colortable.fill(Qt::white);
|
|
}
|
|
ImageMaterialData *state = getState(m_material);
|
|
state->colorTable = QSGPlainTexture::fromImage(colortable);
|
|
fillUniformArrayFromImage(state->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
|
|
fillUniformArrayFromImage(state->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
|
|
}
|
|
Q_FALLTHROUGH();
|
|
case Deformable:
|
|
{
|
|
if (!m_material)
|
|
m_material = new DeformableMaterial;
|
|
}
|
|
Q_FALLTHROUGH();
|
|
case Colored:
|
|
{
|
|
if (!m_material)
|
|
m_material = new ColoredMaterial;
|
|
}
|
|
Q_FALLTHROUGH();
|
|
case ColoredPoint:
|
|
{
|
|
if (!m_material)
|
|
m_material = new ColoredPointMaterial;
|
|
}
|
|
Q_FALLTHROUGH();
|
|
default://Also Simple
|
|
{
|
|
if (!m_material)
|
|
m_material = new SimplePointMaterial;
|
|
ImageMaterialData *state = getState(m_material);
|
|
if (!imageLoaded) {
|
|
if (!m_image || !m_image->pix.isReady()) {
|
|
if (m_image)
|
|
qmlWarning(this) << m_image->pix.error();
|
|
delete m_material;
|
|
return;
|
|
}
|
|
//state->texture //TODO: Shouldn't this be better? But not crash?
|
|
// = QQuickItemPrivate::get(this)->sceneGraphContext()->textureForFactory(m_imagePix.textureFactory());
|
|
state->texture = QSGPlainTexture::fromImage(m_image->pix.image());
|
|
}
|
|
state->texture->setFiltering(QSGTexture::Linear);
|
|
state->entry = (qreal) m_entryEffect;
|
|
state->dpr = m_dpr;
|
|
|
|
m_material->setFlag(QSGMaterial::Blending | QSGMaterial::RequiresFullMatrix);
|
|
}
|
|
}
|
|
|
|
m_nodes.clear();
|
|
for (auto groupId : groupIds()) {
|
|
int count = m_system->groupData[groupId]->size();
|
|
QSGGeometryNode* node = new QSGGeometryNode();
|
|
node->setMaterial(m_material);
|
|
node->markDirty(QSGNode::DirtyMaterial);
|
|
|
|
m_nodes.insert(groupId, node);
|
|
m_idxStarts.insert(groupId, m_lastIdxStart);
|
|
m_startsIdx.append(std::make_pair(m_lastIdxStart, groupId));
|
|
m_lastIdxStart += count;
|
|
|
|
//Create Particle Geometry
|
|
int vCount = count * 4;
|
|
int iCount = count * 6;
|
|
|
|
QSGGeometry *g;
|
|
if (perfLevel == Sprites)
|
|
g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount);
|
|
else if (perfLevel == Tabled)
|
|
g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
|
|
else if (perfLevel == Deformable)
|
|
g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
|
|
else if (perfLevel == Colored)
|
|
g = new QSGGeometry(ColoredParticle_AttributeSet, vCount, iCount);
|
|
else if (perfLevel == ColoredPoint)
|
|
g = new QSGGeometry(ColoredPointParticle_AttributeSet, count, 0);
|
|
else //Simple
|
|
g = new QSGGeometry(SimplePointParticle_AttributeSet, count, 0);
|
|
|
|
node->setFlag(QSGNode::OwnsGeometry);
|
|
node->setGeometry(g);
|
|
if (perfLevel <= ColoredPoint){
|
|
g->setDrawingMode(QSGGeometry::DrawPoints);
|
|
if (m_debugMode)
|
|
qDebug("Using point sprites");
|
|
} else {
|
|
g->setDrawingMode(QSGGeometry::DrawTriangles);
|
|
}
|
|
|
|
for (int p=0; p < count; ++p)
|
|
commit(groupId, p);//commit sets geometry for the node, has its own perfLevel switch
|
|
|
|
if (perfLevel == Sprites)
|
|
initTexCoords<SpriteVertex>((SpriteVertex*)g->vertexData(), vCount);
|
|
else if (perfLevel == Tabled)
|
|
initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
|
|
else if (perfLevel == Deformable)
|
|
initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
|
|
else if (perfLevel == Colored)
|
|
initTexCoords<ColoredVertex>((ColoredVertex*)g->vertexData(), vCount);
|
|
|
|
if (perfLevel > ColoredPoint){
|
|
quint16 *indices = g->indexDataAsUShort();
|
|
for (int i=0; i < count; ++i) {
|
|
int o = i * 4;
|
|
indices[0] = o;
|
|
indices[1] = o + 1;
|
|
indices[2] = o + 2;
|
|
indices[3] = o + 1;
|
|
indices[4] = o + 3;
|
|
indices[5] = o + 2;
|
|
indices += 6;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (perfLevel == Sprites)
|
|
spritesUpdate();//Gives all vertexes the initial sprite data, then maintained per frame
|
|
|
|
foreach (QSGGeometryNode* node, m_nodes){
|
|
if (node == *(m_nodes.begin()))
|
|
node->setFlag(QSGGeometryNode::OwnsMaterial);//Root node owns the material for memory management purposes
|
|
else
|
|
(*(m_nodes.begin()))->appendChildNode(node);
|
|
}
|
|
|
|
*node = *(m_nodes.begin());
|
|
update();
|
|
}
|
|
|
|
QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
|
|
{
|
|
if (!m_apiChecked || m_windowChanged) {
|
|
m_apiChecked = true;
|
|
m_windowChanged = false;
|
|
|
|
QSGRenderContext *rc = QQuickItemPrivate::get(this)->sceneGraphRenderContext();
|
|
QSGRendererInterface *rif = rc->sceneGraphContext()->rendererInterface(rc);
|
|
if (!rif)
|
|
return nullptr;
|
|
|
|
QSGRendererInterface::GraphicsApi api = rif->graphicsApi();
|
|
const bool isRhi = QSGRendererInterface::isApiRhiBased(api);
|
|
|
|
if (!node && !isRhi)
|
|
return nullptr;
|
|
|
|
if (isRhi)
|
|
m_rhi = static_cast<QRhi *>(rif->getResource(m_window, QSGRendererInterface::RhiResource));
|
|
else
|
|
m_rhi = nullptr;
|
|
|
|
if (isRhi && !m_rhi) {
|
|
qWarning("Failed to query QRhi, particles disabled");
|
|
return nullptr;
|
|
}
|
|
// Get the pixel ratio of the window, used for pointsize scaling
|
|
m_dpr = m_window ? m_window->devicePixelRatio() : 1.0;
|
|
}
|
|
|
|
if (m_pleaseReset){
|
|
// Cannot just destroy the node and then return null (in case image
|
|
// loading is still in progress). Rather, keep track of the old node
|
|
// until we have a new one.
|
|
delete m_outgoingNode;
|
|
m_outgoingNode = node;
|
|
node = nullptr;
|
|
|
|
m_nodes.clear();
|
|
|
|
m_idxStarts.clear();
|
|
m_startsIdx.clear();
|
|
m_lastIdxStart = 0;
|
|
|
|
m_material = nullptr;
|
|
|
|
m_pleaseReset = false;
|
|
m_startedImageLoading = 0;//Cancel a part-way build (may still have a pending load)
|
|
} else if (!m_material) {
|
|
delete node;
|
|
node = nullptr;
|
|
}
|
|
|
|
if (m_system && m_system->isRunning() && !m_system->isPaused()){
|
|
bool dirty = prepareNextFrame(&node);
|
|
if (node) {
|
|
update();
|
|
if (dirty) {
|
|
foreach (QSGGeometryNode* n, m_nodes)
|
|
n->markDirty(QSGNode::DirtyGeometry);
|
|
}
|
|
} else if (m_startedImageLoading < 2) {
|
|
update();//To call prepareNextFrame() again from the renderThread
|
|
}
|
|
}
|
|
|
|
if (!node) {
|
|
node = m_outgoingNode;
|
|
m_outgoingNode = nullptr;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
bool QQuickImageParticle::prepareNextFrame(QSGNode **node)
|
|
{
|
|
if (*node == nullptr){//TODO: Staggered loading (as emitted)
|
|
buildParticleNodes(node);
|
|
if (m_debugMode) {
|
|
qDebug() << "QQuickImageParticle Feature level: " << perfLevel;
|
|
qDebug() << "QQuickImageParticle Nodes: ";
|
|
int count = 0;
|
|
for (auto it = m_nodes.keyBegin(), end = m_nodes.keyEnd(); it != end; ++it) {
|
|
qDebug() << "Group " << *it << " (" << m_system->groupData[*it]->size()
|
|
<< " particles)";
|
|
count += m_system->groupData[*it]->size();
|
|
}
|
|
qDebug() << "Total count: " << count;
|
|
}
|
|
if (*node == nullptr)
|
|
return false;
|
|
}
|
|
qint64 timeStamp = m_system->systemSync(this);
|
|
|
|
qreal time = timeStamp / 1000.;
|
|
|
|
switch (perfLevel){//Fall-through intended
|
|
case Sprites:
|
|
//Advance State
|
|
if (m_spriteEngine)
|
|
m_spriteEngine->updateSprites(timeStamp);//fires signals if anim changed
|
|
spritesUpdate(time);
|
|
Q_FALLTHROUGH();
|
|
case Tabled:
|
|
case Deformable:
|
|
case Colored:
|
|
case ColoredPoint:
|
|
case SimplePoint:
|
|
default: //Also Simple
|
|
getState(m_material)->timestamp = time;
|
|
break;
|
|
}
|
|
|
|
bool active = false;
|
|
for (auto groupId : groupIds()) {
|
|
if (m_system->groupData[groupId]->isActive()) {
|
|
active = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const bool dirty = active || m_previousActive;
|
|
if (dirty) {
|
|
foreach (QSGGeometryNode* node, m_nodes)
|
|
node->markDirty(QSGNode::DirtyMaterial);
|
|
}
|
|
|
|
m_previousActive = active;
|
|
return dirty;
|
|
}
|
|
|
|
void QQuickImageParticle::spritesUpdate(qreal time)
|
|
{
|
|
ImageMaterialData *state = getState(m_material);
|
|
// Sprite progression handled CPU side, so as to have per-frame control.
|
|
for (auto groupId : groupIds()) {
|
|
for (QQuickParticleData* mainDatum : std::as_const(m_system->groupData[groupId]->data)) {
|
|
QSGGeometryNode *node = m_nodes[groupId];
|
|
if (!node)
|
|
continue;
|
|
//TODO: Interpolate between two different animations if it's going to transition next frame
|
|
// This is particularly important for cut-up sprites.
|
|
QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
|
|
int spriteIdx = 0;
|
|
for (int i = 0; i<m_startsIdx.size(); i++) {
|
|
if (m_startsIdx[i].second == groupId){
|
|
spriteIdx = m_startsIdx[i].first + datum->index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
double frameAt;
|
|
qreal progress = 0;
|
|
|
|
if (datum->frameDuration > 0) {
|
|
qreal frame = (time - datum->animT)/(datum->frameDuration / 1000.0);
|
|
frame = qBound((qreal)0.0, frame, (qreal)((qreal)datum->frameCount - 1.0));//Stop at count-1 frames until we have between anim interpolation
|
|
if (m_spritesInterpolate)
|
|
progress = std::modf(frame,&frameAt);
|
|
else
|
|
std::modf(frame,&frameAt);
|
|
} else {
|
|
datum->frameAt++;
|
|
if (datum->frameAt >= datum->frameCount){
|
|
datum->frameAt = 0;
|
|
m_spriteEngine->advance(spriteIdx);
|
|
}
|
|
frameAt = datum->frameAt;
|
|
}
|
|
if (m_spriteEngine->sprite(spriteIdx)->reverse())//### Store this in datum too?
|
|
frameAt = (datum->frameCount - 1) - frameAt;
|
|
QSizeF sheetSize = state->animSheetSize;
|
|
qreal y = datum->animY / sheetSize.height();
|
|
qreal w = datum->animWidth / sheetSize.width();
|
|
qreal h = datum->animHeight / sheetSize.height();
|
|
qreal x1 = datum->animX / sheetSize.width();
|
|
x1 += frameAt * w;
|
|
qreal x2 = x1;
|
|
if (frameAt < (datum->frameCount-1))
|
|
x2 += w;
|
|
|
|
SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
|
|
spriteVertices += datum->index*4;
|
|
for (int i=0; i<4; i++) {
|
|
spriteVertices[i].animX1 = x1;
|
|
spriteVertices[i].animY1 = y;
|
|
spriteVertices[i].animX2 = x2;
|
|
spriteVertices[i].animW = w;
|
|
spriteVertices[i].animH = h;
|
|
spriteVertices[i].animProgress = progress;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void QQuickImageParticle::spriteAdvance(int spriteIdx)
|
|
{
|
|
if (!m_startsIdx.size())//Probably overly defensive
|
|
return;
|
|
|
|
int gIdx = -1;
|
|
int i;
|
|
for (i = 0; i<m_startsIdx.size(); i++) {
|
|
if (spriteIdx < m_startsIdx[i].first) {
|
|
gIdx = m_startsIdx[i-1].second;
|
|
break;
|
|
}
|
|
}
|
|
if (gIdx == -1)
|
|
gIdx = m_startsIdx[i-1].second;
|
|
int pIdx = spriteIdx - m_startsIdx[i-1].first;
|
|
|
|
QQuickParticleData* mainDatum = m_system->groupData[gIdx]->data[pIdx];
|
|
QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
|
|
|
|
datum->animIdx = m_spriteEngine->spriteState(spriteIdx);
|
|
datum->animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
|
|
datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
|
|
datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / datum->frameCount;
|
|
datum->animX = m_spriteEngine->spriteX(spriteIdx);
|
|
datum->animY = m_spriteEngine->spriteY(spriteIdx);
|
|
datum->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
|
|
datum->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
|
|
}
|
|
|
|
void QQuickImageParticle::initialize(int gIdx, int pIdx)
|
|
{
|
|
Color4ub color;
|
|
QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
|
|
qreal redVariation = m_color_variation + m_redVariation;
|
|
qreal greenVariation = m_color_variation + m_greenVariation;
|
|
qreal blueVariation = m_color_variation + m_blueVariation;
|
|
int spriteIdx = 0;
|
|
if (m_spriteEngine) {
|
|
spriteIdx = m_idxStarts[gIdx] + datum->index;
|
|
if (spriteIdx >= m_spriteEngine->count())
|
|
m_spriteEngine->setCount(spriteIdx+1);
|
|
}
|
|
|
|
float rotation;
|
|
float rotationVelocity;
|
|
uchar autoRotate;
|
|
switch (perfLevel){//Fall-through is intended on all of them
|
|
case Sprites:
|
|
// Initial Sprite State
|
|
if (m_explicitAnimation && m_spriteEngine){
|
|
if (!datum->animationOwner)
|
|
datum->animationOwner = this;
|
|
QQuickParticleData* writeTo = (datum->animationOwner == this ? datum : getShadowDatum(datum));
|
|
writeTo->animT = writeTo->t;
|
|
//writeTo->animInterpolate = m_spritesInterpolate;
|
|
if (m_spriteEngine){
|
|
m_spriteEngine->start(spriteIdx);
|
|
writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
|
|
writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / writeTo->frameCount;
|
|
writeTo->animIdx = 0;//Always starts at 0
|
|
writeTo->frameAt = -1;
|
|
writeTo->animX = m_spriteEngine->spriteX(spriteIdx);
|
|
writeTo->animY = m_spriteEngine->spriteY(spriteIdx);
|
|
writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
|
|
writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
|
|
}
|
|
} else {
|
|
ImageMaterialData *state = getState(m_material);
|
|
QQuickParticleData* writeTo = getShadowDatum(datum);
|
|
writeTo->animT = datum->t;
|
|
writeTo->frameCount = 1;
|
|
writeTo->frameDuration = 60000000.0;
|
|
writeTo->frameAt = -1;
|
|
writeTo->animIdx = 0;
|
|
writeTo->animT = 0;
|
|
writeTo->animX = writeTo->animY = 0;
|
|
writeTo->animWidth = state->animSheetSize.width();
|
|
writeTo->animHeight = state->animSheetSize.height();
|
|
}
|
|
Q_FALLTHROUGH();
|
|
case Tabled:
|
|
case Deformable:
|
|
//Initial Rotation
|
|
if (m_explicitDeformation){
|
|
if (!datum->deformationOwner)
|
|
datum->deformationOwner = this;
|
|
if (m_xVector){
|
|
const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y));
|
|
if (datum->deformationOwner == this) {
|
|
datum->xx = ret.x();
|
|
datum->xy = ret.y();
|
|
} else {
|
|
QQuickParticleData* shadow = getShadowDatum(datum);
|
|
shadow->xx = ret.x();
|
|
shadow->xy = ret.y();
|
|
}
|
|
}
|
|
if (m_yVector){
|
|
const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y));
|
|
if (datum->deformationOwner == this) {
|
|
datum->yx = ret.x();
|
|
datum->yy = ret.y();
|
|
} else {
|
|
QQuickParticleData* shadow = getShadowDatum(datum);
|
|
shadow->yx = ret.x();
|
|
shadow->yy = ret.y();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_explicitRotation){
|
|
if (!datum->rotationOwner)
|
|
datum->rotationOwner = this;
|
|
rotation = qDegreesToRadians(
|
|
m_rotation + (m_rotationVariation
|
|
- 2 * QRandomGenerator::global()->bounded(m_rotationVariation)));
|
|
rotationVelocity = qDegreesToRadians(
|
|
m_rotationVelocity
|
|
+ (m_rotationVelocityVariation
|
|
- 2 * QRandomGenerator::global()->bounded(m_rotationVelocityVariation)));
|
|
autoRotate = m_autoRotation ? 1 : 0;
|
|
if (datum->rotationOwner == this) {
|
|
datum->rotation = rotation;
|
|
datum->rotationVelocity = rotationVelocity;
|
|
datum->autoRotate = autoRotate;
|
|
} else {
|
|
QQuickParticleData* shadow = getShadowDatum(datum);
|
|
shadow->rotation = rotation;
|
|
shadow->rotationVelocity = rotationVelocity;
|
|
shadow->autoRotate = autoRotate;
|
|
}
|
|
}
|
|
Q_FALLTHROUGH();
|
|
case Colored:
|
|
Q_FALLTHROUGH();
|
|
case ColoredPoint:
|
|
//Color initialization
|
|
// Particle color
|
|
if (m_explicitColor) {
|
|
if (!datum->colorOwner)
|
|
datum->colorOwner = this;
|
|
const auto rgbColor = m_color.toRgb();
|
|
color.r = rgbColor.red() * (1 - redVariation) + QRandomGenerator::global()->bounded(256) * redVariation;
|
|
color.g = rgbColor.green() * (1 - greenVariation) + QRandomGenerator::global()->bounded(256) * greenVariation;
|
|
color.b = rgbColor.blue() * (1 - blueVariation) + QRandomGenerator::global()->bounded(256) * blueVariation;
|
|
color.a = m_alpha * rgbColor.alpha() * (1 - m_alphaVariation) + QRandomGenerator::global()->bounded(256) * m_alphaVariation;
|
|
if (datum->colorOwner == this)
|
|
datum->color = color;
|
|
else
|
|
getShadowDatum(datum)->color = color;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QQuickImageParticle::commit(int gIdx, int pIdx)
|
|
{
|
|
if (m_pleaseReset)
|
|
return;
|
|
QSGGeometryNode *node = m_nodes[gIdx];
|
|
if (!node)
|
|
return;
|
|
QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
|
|
SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
|
|
DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData();
|
|
ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData();
|
|
ColoredPointVertex *coloredPointVertices = (ColoredPointVertex *) node->geometry()->vertexData();
|
|
SimplePointVertex *simplePointVertices = (SimplePointVertex *) node->geometry()->vertexData();
|
|
switch (perfLevel){//No automatic fall through intended on this one
|
|
case Sprites:
|
|
spriteVertices += pIdx*4;
|
|
for (int i=0; i<4; i++){
|
|
spriteVertices[i].x = datum->x - m_systemOffset.x();
|
|
spriteVertices[i].y = datum->y - m_systemOffset.y();
|
|
spriteVertices[i].t = datum->t;
|
|
spriteVertices[i].lifeSpan = datum->lifeSpan;
|
|
spriteVertices[i].size = datum->size;
|
|
spriteVertices[i].endSize = datum->endSize;
|
|
spriteVertices[i].vx = datum->vx;
|
|
spriteVertices[i].vy = datum->vy;
|
|
spriteVertices[i].ax = datum->ax;
|
|
spriteVertices[i].ay = datum->ay;
|
|
if (m_explicitDeformation && datum->deformationOwner != this) {
|
|
QQuickParticleData* shadow = getShadowDatum(datum);
|
|
spriteVertices[i].xx = shadow->xx;
|
|
spriteVertices[i].xy = shadow->xy;
|
|
spriteVertices[i].yx = shadow->yx;
|
|
spriteVertices[i].yy = shadow->yy;
|
|
} else {
|
|
spriteVertices[i].xx = datum->xx;
|
|
spriteVertices[i].xy = datum->xy;
|
|
spriteVertices[i].yx = datum->yx;
|
|
spriteVertices[i].yy = datum->yy;
|
|
}
|
|
if (m_explicitRotation && datum->rotationOwner != this) {
|
|
QQuickParticleData* shadow = getShadowDatum(datum);
|
|
spriteVertices[i].rotation = shadow->rotation;
|
|
spriteVertices[i].rotationVelocity = shadow->rotationVelocity;
|
|
spriteVertices[i].autoRotate = shadow->autoRotate;
|
|
} else {
|
|
spriteVertices[i].rotation = datum->rotation;
|
|
spriteVertices[i].rotationVelocity = datum->rotationVelocity;
|
|
spriteVertices[i].autoRotate = datum->autoRotate;
|
|
}
|
|
//Sprite-related vertices updated per-frame in spritesUpdate(), not on demand
|
|
if (m_explicitColor && datum->colorOwner != this) {
|
|
QQuickParticleData* shadow = getShadowDatum(datum);
|
|
spriteVertices[i].color = shadow->color;
|
|
} else {
|
|
spriteVertices[i].color = datum->color;
|
|
}
|
|
}
|
|
break;
|
|
case Tabled: //Fall through until it has its own vertex class
|
|
case Deformable:
|
|
deformableVertices += pIdx*4;
|
|
for (int i=0; i<4; i++){
|
|
deformableVertices[i].x = datum->x - m_systemOffset.x();
|
|
deformableVertices[i].y = datum->y - m_systemOffset.y();
|
|
deformableVertices[i].t = datum->t;
|
|
deformableVertices[i].lifeSpan = datum->lifeSpan;
|
|
deformableVertices[i].size = datum->size;
|
|
deformableVertices[i].endSize = datum->endSize;
|
|
deformableVertices[i].vx = datum->vx;
|
|
deformableVertices[i].vy = datum->vy;
|
|
deformableVertices[i].ax = datum->ax;
|
|
deformableVertices[i].ay = datum->ay;
|
|
if (m_explicitDeformation && datum->deformationOwner != this) {
|
|
QQuickParticleData* shadow = getShadowDatum(datum);
|
|
deformableVertices[i].xx = shadow->xx;
|
|
deformableVertices[i].xy = shadow->xy;
|
|
deformableVertices[i].yx = shadow->yx;
|
|
deformableVertices[i].yy = shadow->yy;
|
|
} else {
|
|
deformableVertices[i].xx = datum->xx;
|
|
deformableVertices[i].xy = datum->xy;
|
|
deformableVertices[i].yx = datum->yx;
|
|
deformableVertices[i].yy = datum->yy;
|
|
}
|
|
if (m_explicitRotation && datum->rotationOwner != this) {
|
|
QQuickParticleData* shadow = getShadowDatum(datum);
|
|
deformableVertices[i].rotation = shadow->rotation;
|
|
deformableVertices[i].rotationVelocity = shadow->rotationVelocity;
|
|
deformableVertices[i].autoRotate = shadow->autoRotate;
|
|
} else {
|
|
deformableVertices[i].rotation = datum->rotation;
|
|
deformableVertices[i].rotationVelocity = datum->rotationVelocity;
|
|
deformableVertices[i].autoRotate = datum->autoRotate;
|
|
}
|
|
if (m_explicitColor && datum->colorOwner != this) {
|
|
QQuickParticleData* shadow = getShadowDatum(datum);
|
|
deformableVertices[i].color = shadow->color;
|
|
} else {
|
|
deformableVertices[i].color = datum->color;
|
|
}
|
|
}
|
|
break;
|
|
case Colored:
|
|
coloredVertices += pIdx*4;
|
|
for (int i=0; i<4; i++){
|
|
coloredVertices[i].x = datum->x - m_systemOffset.x();
|
|
coloredVertices[i].y = datum->y - m_systemOffset.y();
|
|
coloredVertices[i].t = datum->t;
|
|
coloredVertices[i].lifeSpan = datum->lifeSpan;
|
|
coloredVertices[i].size = datum->size;
|
|
coloredVertices[i].endSize = datum->endSize;
|
|
coloredVertices[i].vx = datum->vx;
|
|
coloredVertices[i].vy = datum->vy;
|
|
coloredVertices[i].ax = datum->ax;
|
|
coloredVertices[i].ay = datum->ay;
|
|
if (m_explicitColor && datum->colorOwner != this) {
|
|
QQuickParticleData* shadow = getShadowDatum(datum);
|
|
coloredVertices[i].color = shadow->color;
|
|
} else {
|
|
coloredVertices[i].color = datum->color;
|
|
}
|
|
}
|
|
break;
|
|
case ColoredPoint:
|
|
coloredPointVertices += pIdx*1;
|
|
for (int i=0; i<1; i++){
|
|
coloredPointVertices[i].x = datum->x - m_systemOffset.x();
|
|
coloredPointVertices[i].y = datum->y - m_systemOffset.y();
|
|
coloredPointVertices[i].t = datum->t;
|
|
coloredPointVertices[i].lifeSpan = datum->lifeSpan;
|
|
coloredPointVertices[i].size = datum->size;
|
|
coloredPointVertices[i].endSize = datum->endSize;
|
|
coloredPointVertices[i].vx = datum->vx;
|
|
coloredPointVertices[i].vy = datum->vy;
|
|
coloredPointVertices[i].ax = datum->ax;
|
|
coloredPointVertices[i].ay = datum->ay;
|
|
if (m_explicitColor && datum->colorOwner != this) {
|
|
QQuickParticleData* shadow = getShadowDatum(datum);
|
|
coloredPointVertices[i].color = shadow->color;
|
|
} else {
|
|
coloredPointVertices[i].color = datum->color;
|
|
}
|
|
}
|
|
break;
|
|
case SimplePoint:
|
|
simplePointVertices += pIdx*1;
|
|
for (int i=0; i<1; i++){
|
|
simplePointVertices[i].x = datum->x - m_systemOffset.x();
|
|
simplePointVertices[i].y = datum->y - m_systemOffset.y();
|
|
simplePointVertices[i].t = datum->t;
|
|
simplePointVertices[i].lifeSpan = datum->lifeSpan;
|
|
simplePointVertices[i].size = datum->size;
|
|
simplePointVertices[i].endSize = datum->endSize;
|
|
simplePointVertices[i].vx = datum->vx;
|
|
simplePointVertices[i].vy = datum->vy;
|
|
simplePointVertices[i].ax = datum->ax;
|
|
simplePointVertices[i].ay = datum->ay;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
#include "moc_qquickimageparticle_p.cpp"
|