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 | |
8 | QT_BEGIN_NAMESPACE |
9 | |
10 | struct SpriteVertex { |
11 | float x; |
12 | float y; |
13 | float tx; |
14 | float ty; |
15 | }; |
16 | |
17 | struct SpriteVertices { |
18 | SpriteVertex v1; |
19 | SpriteVertex v2; |
20 | SpriteVertex v3; |
21 | SpriteVertex v4; |
22 | }; |
23 | |
24 | class QQuickSpriteMaterial : public QSGMaterial |
25 | { |
26 | public: |
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 | |
43 | QQuickSpriteMaterial::QQuickSpriteMaterial() |
44 | { |
45 | setFlag(flags: Blending, on: true); |
46 | } |
47 | |
48 | QQuickSpriteMaterial::~QQuickSpriteMaterial() |
49 | { |
50 | delete texture; |
51 | } |
52 | |
53 | class SpriteMaterialRhiShader : public QSGMaterialShader |
54 | { |
55 | public: |
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 | |
64 | SpriteMaterialRhiShader::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 | |
70 | bool 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 | |
103 | void 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 | |
120 | QSGMaterialShader *QQuickSpriteMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const |
121 | { |
122 | Q_UNUSED(renderMode); |
123 | return new SpriteMaterialRhiShader; |
124 | } |
125 | |
126 | static 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 | |
131 | static QSGGeometry::AttributeSet Sprite_AttributeSet = |
132 | { |
133 | .count: 2, // Attribute Count |
134 | .stride: (2+2) * sizeof(float), |
135 | .attributes: Sprite_Attributes |
136 | }; |
137 | |
138 | QSGDefaultSpriteNode::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 | |
160 | void QSGDefaultSpriteNode::setTexture(QSGTexture *texture) |
161 | { |
162 | m_material->texture = texture; |
163 | m_geometryDirty = true; |
164 | markDirty(bits: DirtyMaterial); |
165 | } |
166 | |
167 | void QSGDefaultSpriteNode::setTime(float time) |
168 | { |
169 | m_material->animT = time; |
170 | markDirty(bits: DirtyMaterial); |
171 | } |
172 | |
173 | void 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 | |
183 | void 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 | |
193 | void 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 | |
204 | void 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 | |
220 | void QSGDefaultSpriteNode::setSize(const QSizeF &size) |
221 | { |
222 | if (m_size != size) { |
223 | m_size = size; |
224 | m_geometryDirty = true; |
225 | } |
226 | } |
227 | |
228 | void QSGDefaultSpriteNode::setFiltering(QSGTexture::Filtering filtering) |
229 | { |
230 | m_material->texture->setFiltering(filtering); |
231 | markDirty(bits: DirtyMaterial); |
232 | } |
233 | |
234 | void QSGDefaultSpriteNode::update() |
235 | { |
236 | if (m_geometryDirty) { |
237 | updateGeometry(); |
238 | m_geometryDirty = false; |
239 | } |
240 | } |
241 | |
242 | void 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 | |
277 | QT_END_NAMESPACE |
278 | |