1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qsgdefaultspritenode_p.h"
5
6#include <QtQuick/QSGMaterial>
7
8QT_BEGIN_NAMESPACE
9
10struct SpriteVertex {
11 float x;
12 float y;
13 float tx;
14 float ty;
15};
16
17struct SpriteVertices {
18 SpriteVertex v1;
19 SpriteVertex v2;
20 SpriteVertex v3;
21 SpriteVertex v4;
22};
23
24class QQuickSpriteMaterial : public QSGMaterial
25{
26public:
27 QQuickSpriteMaterial();
28 ~QQuickSpriteMaterial();
29 QSGMaterialType *type() const override { static QSGMaterialType type; return &type; }
30 QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override;
31
32 QSGTexture *texture = nullptr;
33
34 float animT = 0.0f;
35 float animX1 = 0.0f;
36 float animY1 = 0.0f;
37 float animX2 = 0.0f;
38 float animY2 = 0.0f;
39 float animW = 1.0f;
40 float animH = 1.0f;
41};
42
43QQuickSpriteMaterial::QQuickSpriteMaterial()
44{
45 setFlag(flags: Blending, on: true);
46}
47
48QQuickSpriteMaterial::~QQuickSpriteMaterial()
49{
50 delete texture;
51}
52
53class SpriteMaterialRhiShader : public QSGMaterialShader
54{
55public:
56 SpriteMaterialRhiShader();
57
58 bool updateUniformData(RenderState &state,
59 QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
60 void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
61 QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
62};
63
64SpriteMaterialRhiShader::SpriteMaterialRhiShader()
65{
66 setShaderFileName(stage: VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/sprite.vert.qsb"));
67 setShaderFileName(stage: FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/sprite.frag.qsb"));
68}
69
70bool SpriteMaterialRhiShader::updateUniformData(RenderState &state,
71 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
72{
73#ifdef QT_NO_DEBUG
74 Q_UNUSED(oldMaterial);
75#endif
76 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
77 QQuickSpriteMaterial *mat = static_cast<QQuickSpriteMaterial *>(newMaterial);
78
79 bool changed = false;
80 QByteArray *buf = state.uniformData();
81 Q_ASSERT(buf->size() >= 96);
82
83 if (state.isMatrixDirty()) {
84 const QMatrix4x4 m = state.combinedMatrix();
85 memcpy(dest: buf->data(), src: m.constData(), n: 64);
86 changed = true;
87 }
88
89 float animPosAndData[7] = { mat->animX1, mat->animY1, mat->animX2, mat->animY2,
90 mat->animW, mat->animH, mat->animT };
91 memcpy(dest: buf->data() + 64, src: animPosAndData, n: 28);
92 changed = true;
93
94 if (state.isOpacityDirty()) {
95 const float opacity = state.opacity();
96 memcpy(dest: buf->data() + 92, src: &opacity, n: 4);
97 changed = true;
98 }
99
100 return changed;
101}
102
103void SpriteMaterialRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
104 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
105{
106 if (binding != 1)
107 return;
108
109#ifdef QT_NO_DEBUG
110 Q_UNUSED(oldMaterial);
111#endif
112 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
113 QQuickSpriteMaterial *mat = static_cast<QQuickSpriteMaterial *>(newMaterial);
114
115 QSGTexture *t = mat->texture;
116 t->commitTextureOperations(rhi: state.rhi(), resourceUpdates: state.resourceUpdateBatch());
117 *texture = t;
118}
119
120QSGMaterialShader *QQuickSpriteMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
121{
122 Q_UNUSED(renderMode);
123 return new SpriteMaterialRhiShader;
124}
125
126static QSGGeometry::Attribute Sprite_Attributes[] = {
127 QSGGeometry::Attribute::create(pos: 0, tupleSize: 2, primitiveType: QSGGeometry::FloatType, isPosition: true), // pos
128 QSGGeometry::Attribute::create(pos: 1, tupleSize: 2, primitiveType: QSGGeometry::FloatType), // tex
129};
130
131static QSGGeometry::AttributeSet Sprite_AttributeSet =
132{
133 .count: 2, // Attribute Count
134 .stride: (2+2) * sizeof(float),
135 .attributes: Sprite_Attributes
136};
137
138QSGDefaultSpriteNode::QSGDefaultSpriteNode()
139 : m_material(new QQuickSpriteMaterial)
140 , m_geometryDirty(true)
141 , m_sheetSize(QSize(64, 64))
142{
143 // Setup geometry data
144 m_geometry = new QSGGeometry(Sprite_AttributeSet, 4, 6);
145 m_geometry->setDrawingMode(QSGGeometry::DrawTriangles);
146 quint16 *indices = m_geometry->indexDataAsUShort();
147 indices[0] = 0;
148 indices[1] = 1;
149 indices[2] = 2;
150 indices[3] = 1;
151 indices[4] = 3;
152 indices[5] = 2;
153
154 setGeometry(m_geometry);
155 setMaterial(m_material);
156 setFlag(OwnsGeometry, true);
157 setFlag(OwnsMaterial, true);
158}
159
160void QSGDefaultSpriteNode::setTexture(QSGTexture *texture)
161{
162 m_material->texture = texture;
163 m_geometryDirty = true;
164 markDirty(bits: DirtyMaterial);
165}
166
167void QSGDefaultSpriteNode::setTime(float time)
168{
169 m_material->animT = time;
170 markDirty(bits: DirtyMaterial);
171}
172
173void QSGDefaultSpriteNode::setSourceA(const QPoint &source)
174{
175 if (m_sourceA != source) {
176 m_sourceA = source;
177 m_material->animX1 = static_cast<float>(source.x()) / m_sheetSize.width();
178 m_material->animY1 = static_cast<float>(source.y()) / m_sheetSize.height();
179 markDirty(bits: DirtyMaterial);
180 }
181}
182
183void QSGDefaultSpriteNode::setSourceB(const QPoint &source)
184{
185 if (m_sourceB != source) {
186 m_sourceB = source;
187 m_material->animX2 = static_cast<float>(source.x()) / m_sheetSize.width();
188 m_material->animY2 = static_cast<float>(source.y()) / m_sheetSize.height();
189 markDirty(bits: DirtyMaterial);
190 }
191}
192
193void QSGDefaultSpriteNode::setSpriteSize(const QSize &size)
194{
195 if (m_spriteSize != size) {
196 m_spriteSize = size;
197 m_material->animW = static_cast<float>(size.width()) / m_sheetSize.width();
198 m_material->animH = static_cast<float>(size.height()) / m_sheetSize.height();
199 markDirty(bits: DirtyMaterial);
200 }
201
202}
203
204void QSGDefaultSpriteNode::setSheetSize(const QSize &size)
205{
206 if (m_sheetSize != size) {
207 m_sheetSize = size;
208
209 // Update all dependent properties
210 m_material->animX1 = static_cast<float>(m_sourceA.x()) / m_sheetSize.width();
211 m_material->animY1 = static_cast<float>(m_sourceA.y()) / m_sheetSize.height();
212 m_material->animX2 = static_cast<float>(m_sourceB.x()) / m_sheetSize.width();
213 m_material->animY2 = static_cast<float>(m_sourceB.y()) / m_sheetSize.height();
214 m_material->animW = static_cast<float>(m_spriteSize.width()) / m_sheetSize.width();
215 m_material->animH = static_cast<float>(m_spriteSize.height()) / m_sheetSize.height();
216 markDirty(bits: DirtyMaterial);
217 }
218}
219
220void QSGDefaultSpriteNode::setSize(const QSizeF &size)
221{
222 if (m_size != size) {
223 m_size = size;
224 m_geometryDirty = true;
225 }
226}
227
228void QSGDefaultSpriteNode::setFiltering(QSGTexture::Filtering filtering)
229{
230 m_material->texture->setFiltering(filtering);
231 markDirty(bits: DirtyMaterial);
232}
233
234void QSGDefaultSpriteNode::update()
235{
236 if (m_geometryDirty) {
237 updateGeometry();
238 m_geometryDirty = false;
239 }
240}
241
242void QSGDefaultSpriteNode::updateGeometry()
243{
244 if (!m_material->texture)
245 return;
246
247 SpriteVertices *p = (SpriteVertices *) m_geometry->vertexData();
248
249 QRectF texRect = m_material->texture->normalizedTextureSubRect();
250
251 p->v1.tx = texRect.topLeft().x();
252 p->v1.ty = texRect.topLeft().y();
253
254 p->v2.tx = texRect.topRight().x();
255 p->v2.ty = texRect.topRight().y();
256
257 p->v3.tx = texRect.bottomLeft().x();
258 p->v3.ty = texRect.bottomLeft().y();
259
260 p->v4.tx = texRect.bottomRight().x();
261 p->v4.ty = texRect.bottomRight().y();
262
263 p->v1.x = 0;
264 p->v1.y = 0;
265
266 p->v2.x = m_size.width();
267 p->v2.y = 0;
268
269 p->v3.x = 0;
270 p->v3.y = m_size.height();
271
272 p->v4.x = m_size.width();
273 p->v4.y = m_size.height();
274 markDirty(bits: DirtyGeometry);
275}
276
277QT_END_NAMESPACE
278

source code of qtdeclarative/src/quick/scenegraph/qsgdefaultspritenode.cpp