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(int viewCount);
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(int viewCount)
65{
66 setShaderFileName(stage: VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/sprite.vert.qsb"), viewCount);
67 setShaderFileName(stage: FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/sprite.frag.qsb"), viewCount);
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 const int shaderMatrixCount = newMaterial->viewCount();
84 const int matrixCount = qMin(a: state.projectionMatrixCount(), b: shaderMatrixCount);
85 for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
86 if (state.isMatrixDirty()) {
87 const QMatrix4x4 m = state.combinedMatrix(index: viewIndex);
88 memcpy(dest: buf->data() + 64 * viewIndex, src: m.constData(), n: 64);
89 changed = true;
90 }
91 }
92
93 float animPosAndData[7] = { mat->animX1, mat->animY1, mat->animX2, mat->animY2,
94 mat->animW, mat->animH, mat->animT };
95 memcpy(dest: buf->data() + 64 * shaderMatrixCount, src: animPosAndData, n: 28);
96 changed = true;
97
98 if (state.isOpacityDirty()) {
99 const float opacity = state.opacity();
100 memcpy(dest: buf->data() + 64 * shaderMatrixCount + 16 + 12, src: &opacity, n: 4);
101 changed = true;
102 }
103
104 return changed;
105}
106
107void SpriteMaterialRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
108 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
109{
110 if (binding != 1)
111 return;
112
113#ifdef QT_NO_DEBUG
114 Q_UNUSED(oldMaterial);
115#endif
116 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
117 QQuickSpriteMaterial *mat = static_cast<QQuickSpriteMaterial *>(newMaterial);
118
119 QSGTexture *t = mat->texture;
120 t->commitTextureOperations(rhi: state.rhi(), resourceUpdates: state.resourceUpdateBatch());
121 *texture = t;
122}
123
124QSGMaterialShader *QQuickSpriteMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
125{
126 Q_UNUSED(renderMode);
127 return new SpriteMaterialRhiShader(viewCount());
128}
129
130static QSGGeometry::Attribute Sprite_Attributes[] = {
131 QSGGeometry::Attribute::create(pos: 0, tupleSize: 2, primitiveType: QSGGeometry::FloatType, isPosition: true), // pos
132 QSGGeometry::Attribute::create(pos: 1, tupleSize: 2, primitiveType: QSGGeometry::FloatType), // tex
133};
134
135static QSGGeometry::AttributeSet Sprite_AttributeSet =
136{
137 .count: 2, // Attribute Count
138 .stride: (2+2) * sizeof(float),
139 .attributes: Sprite_Attributes
140};
141
142QSGDefaultSpriteNode::QSGDefaultSpriteNode()
143 : m_material(new QQuickSpriteMaterial)
144 , m_geometryDirty(true)
145 , m_sheetSize(QSize(64, 64))
146{
147 // Setup geometry data
148 m_geometry = new QSGGeometry(Sprite_AttributeSet, 4, 6);
149 m_geometry->setDrawingMode(QSGGeometry::DrawTriangles);
150 quint16 *indices = m_geometry->indexDataAsUShort();
151 indices[0] = 0;
152 indices[1] = 1;
153 indices[2] = 2;
154 indices[3] = 1;
155 indices[4] = 3;
156 indices[5] = 2;
157
158 setGeometry(m_geometry);
159 setMaterial(m_material);
160 setFlag(OwnsGeometry, true);
161 setFlag(OwnsMaterial, true);
162}
163
164void QSGDefaultSpriteNode::setTexture(QSGTexture *texture)
165{
166 m_material->texture = texture;
167 m_geometryDirty = true;
168 markDirty(bits: DirtyMaterial);
169}
170
171void QSGDefaultSpriteNode::setTime(float time)
172{
173 m_material->animT = time;
174 markDirty(bits: DirtyMaterial);
175}
176
177void QSGDefaultSpriteNode::setSourceA(const QPoint &source)
178{
179 if (m_sourceA != source) {
180 m_sourceA = source;
181 m_material->animX1 = static_cast<float>(source.x()) / m_sheetSize.width();
182 m_material->animY1 = static_cast<float>(source.y()) / m_sheetSize.height();
183 markDirty(bits: DirtyMaterial);
184 }
185}
186
187void QSGDefaultSpriteNode::setSourceB(const QPoint &source)
188{
189 if (m_sourceB != source) {
190 m_sourceB = source;
191 m_material->animX2 = static_cast<float>(source.x()) / m_sheetSize.width();
192 m_material->animY2 = static_cast<float>(source.y()) / m_sheetSize.height();
193 markDirty(bits: DirtyMaterial);
194 }
195}
196
197void QSGDefaultSpriteNode::setSpriteSize(const QSize &size)
198{
199 if (m_spriteSize != size) {
200 m_spriteSize = size;
201 m_material->animW = static_cast<float>(size.width()) / m_sheetSize.width();
202 m_material->animH = static_cast<float>(size.height()) / m_sheetSize.height();
203 markDirty(bits: DirtyMaterial);
204 }
205
206}
207
208void QSGDefaultSpriteNode::setSheetSize(const QSize &size)
209{
210 if (m_sheetSize != size) {
211 m_sheetSize = size;
212
213 // Update all dependent properties
214 m_material->animX1 = static_cast<float>(m_sourceA.x()) / m_sheetSize.width();
215 m_material->animY1 = static_cast<float>(m_sourceA.y()) / m_sheetSize.height();
216 m_material->animX2 = static_cast<float>(m_sourceB.x()) / m_sheetSize.width();
217 m_material->animY2 = static_cast<float>(m_sourceB.y()) / m_sheetSize.height();
218 m_material->animW = static_cast<float>(m_spriteSize.width()) / m_sheetSize.width();
219 m_material->animH = static_cast<float>(m_spriteSize.height()) / m_sheetSize.height();
220 markDirty(bits: DirtyMaterial);
221 }
222}
223
224void QSGDefaultSpriteNode::setSize(const QSizeF &size)
225{
226 if (m_size != size) {
227 m_size = size;
228 m_geometryDirty = true;
229 }
230}
231
232void QSGDefaultSpriteNode::setFiltering(QSGTexture::Filtering filtering)
233{
234 m_material->texture->setFiltering(filtering);
235 markDirty(bits: DirtyMaterial);
236}
237
238void QSGDefaultSpriteNode::update()
239{
240 if (m_geometryDirty) {
241 updateGeometry();
242 m_geometryDirty = false;
243 }
244}
245
246void QSGDefaultSpriteNode::updateGeometry()
247{
248 if (!m_material->texture)
249 return;
250
251 SpriteVertices *p = (SpriteVertices *) m_geometry->vertexData();
252
253 QRectF texRect = m_material->texture->normalizedTextureSubRect();
254
255 p->v1.tx = texRect.topLeft().x();
256 p->v1.ty = texRect.topLeft().y();
257
258 p->v2.tx = texRect.topRight().x();
259 p->v2.ty = texRect.topRight().y();
260
261 p->v3.tx = texRect.bottomLeft().x();
262 p->v3.ty = texRect.bottomLeft().y();
263
264 p->v4.tx = texRect.bottomRight().x();
265 p->v4.ty = texRect.bottomRight().y();
266
267 p->v1.x = 0;
268 p->v1.y = 0;
269
270 p->v2.x = m_size.width();
271 p->v2.y = 0;
272
273 p->v3.x = 0;
274 p->v3.y = m_size.height();
275
276 p->v4.x = m_size.width();
277 p->v4.y = m_size.height();
278 markDirty(bits: DirtyGeometry);
279}
280
281QT_END_NAMESPACE
282

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