11
11
12
12
using namespace scratchcpprender ;
13
13
14
+ static const double pi = std::acos(-1 ); // TODO: Use std::numbers::pi in C++20
15
+
14
16
std::unordered_map<libscratchcpp::IEngine *, IPenLayer *> PenLayer::m_projectPenLayers;
15
17
18
+ // TODO: Move this to a separate class
19
+ template <typename T>
20
+ short sgn (T x)
21
+ {
22
+ return (T (0 ) < x) - (x < T (0 ));
23
+ }
24
+
16
25
PenLayer::PenLayer (QNanoQuickItem *parent) :
17
26
IPenLayer(parent)
18
27
{
@@ -24,10 +33,13 @@ PenLayer::~PenLayer()
24
33
if (m_engine)
25
34
m_projectPenLayers.erase (m_engine);
26
35
27
- if (m_blitter. isCreated () ) {
36
+ if (m_vao != 0 ) {
28
37
// Delete vertex array and buffer
29
38
m_glF->glDeleteVertexArrays (1 , &m_vao);
30
39
m_glF->glDeleteBuffers (1 , &m_vbo);
40
+
41
+ // Delete stamp FBO
42
+ m_glF->glDeleteFramebuffers (1 , &m_stampFbo);
31
43
}
32
44
}
33
45
@@ -68,13 +80,9 @@ void PenLayer::setEngine(libscratchcpp::IEngine *newEngine)
68
80
m_glF->initializeOpenGLFunctions ();
69
81
}
70
82
71
- if (!m_blitter.isCreated ()) {
72
- m_blitter.create ();
73
-
83
+ if (m_vao == 0 ) {
74
84
// Set up VBO and VAO
75
- float vertices[] = {
76
- -1 .0f , -1 .0f , 0 .0f , 0 .0f , 1 .0f , -1 .0f , 1 .0f , 0 .0f , -1 .0f , 1 .0f , 0 .0f , 1 .0f , 1 .0f , 1 .0f , 1 .0f , 1 .0f ,
77
- };
85
+ float vertices[] = { -1 .0f , -1 .0f , 0 .0f , 0 .0f , 1 .0f , -1 .0f , 1 .0f , 0 .0f , -1 .0f , 1 .0f , 0 .0f , 1 .0f , 1 .0f , -1 .0f , 1 .0f , 0 .0f , 1 .0f , 1 .0f , 1 .0f , 1 .0f , -1 .0f , 1 .0f , 0 .0f , 1 .0f };
78
86
79
87
m_glF->glGenVertexArrays (1 , &m_vao);
80
88
m_glF->glGenBuffers (1 , &m_vbo);
@@ -94,6 +102,9 @@ void PenLayer::setEngine(libscratchcpp::IEngine *newEngine)
94
102
95
103
m_glF->glBindVertexArray (0 );
96
104
m_glF->glBindBuffer (GL_ARRAY_BUFFER, 0 );
105
+
106
+ // Create stamp FBO
107
+ m_glF->glGenFramebuffers (1 , &m_stampFbo);
97
108
}
98
109
99
110
clear ();
@@ -195,98 +206,81 @@ void scratchcpprender::PenLayer::drawLine(const PenAttributes &penAttributes, do
195
206
update ();
196
207
}
197
208
198
- /*
199
- * A brief description of how stamping is implemented:
200
- * 1. Get rotation, size and coordinates and translate them.
201
- * 2. Draw the texture onto a temporary texture using shaders.
202
- * 3. Blit the resulting texture to a FBO with a square texture (required for rotation).
203
- * 4. Blit the resulting texture to the pen layer using QOpenGLTextureBlitter with transform.
204
- *
205
- * If you think this is too complicated, contributions are welcome!
206
- */
207
209
void PenLayer::stamp (IRenderedTarget *target)
208
210
{
209
- if (!target || !m_fbo || !m_texture.isValid () || !m_blitter. isCreated () )
211
+ if (!target || !m_fbo || !m_texture.isValid () || m_vao == 0 || m_vbo == 0 )
210
212
return ;
211
213
212
- double x = 0 ;
213
- double y = 0 ;
214
- double angle = 0 ;
215
- double scale = 1 ;
216
- bool mirror = false ;
217
- std::shared_ptr<libscratchcpp::Costume> costume;
214
+ const float stageWidth = m_engine->stageWidth () * m_scale;
215
+ const float stageHeight = m_engine->stageHeight () * m_scale;
216
+ float x = 0 ;
217
+ float y = 0 ;
218
+ float angle = 180 ;
219
+ float scaleX = 1 ;
220
+ float scaleY = 1 ;
218
221
219
222
SpriteModel *spriteModel = target->spriteModel ();
220
223
221
224
if (spriteModel) {
222
225
libscratchcpp::Sprite *sprite = spriteModel->sprite ();
223
- x = sprite->x ();
224
- y = sprite->y ();
225
226
226
227
switch (sprite->rotationStyle ()) {
227
228
case libscratchcpp::Sprite::RotationStyle::AllAround:
228
- angle = 90 - sprite->direction ();
229
+ angle = 270 - sprite->direction ();
229
230
break ;
230
231
231
232
case libscratchcpp::Sprite::RotationStyle::LeftRight:
232
- mirror = (sprite->direction () < 0 );
233
+ scaleX = sgn (sprite->direction ());
233
234
break ;
234
235
235
236
default :
236
237
break ;
237
238
}
238
239
239
- scale = sprite->size () / 100 ;
240
- costume = sprite->currentCostume ();
241
- } else
242
- costume = target->stageModel ()->stage ()->currentCostume ();
243
-
244
- // Apply scale (HQ pen)
245
- scale *= m_scale;
240
+ scaleY = sprite->size () / 100 ;
241
+ scaleX *= scaleY;
242
+ }
246
243
247
- const double bitmapRes = costume->bitmapResolution ();
248
- const double centerX = costume->rotationCenterX () / bitmapRes;
249
- const double centerY = costume->rotationCenterY () / bitmapRes;
244
+ scaleX *= m_scale;
245
+ scaleY *= m_scale;
250
246
247
+ libscratchcpp::Rect bounds = target->getFastBounds ();
251
248
const Texture &texture = target->cpuTexture ();
252
249
253
250
if (!texture.isValid ())
254
251
return ;
255
252
256
- const double textureScale = texture.width () / static_cast <double >(target->costumeWidth ());
257
-
258
- // Apply scale (HQ pen)
259
- x *= m_scale;
260
- y *= m_scale;
261
-
262
- // Translate the coordinates
263
- x = std::floor (x + m_texture.width () / 2.0 );
264
- y = std::floor (-y + m_texture.height () / 2.0 );
265
-
253
+ const float textureScale = texture.width () / static_cast <float >(target->costumeWidth ());
254
+ const float skinWidth = texture.width ();
255
+ const float skinHeight = texture.height ();
256
+
257
+ // Projection matrix
258
+ QMatrix4x4 projectionMatrix;
259
+ const float aspectRatio = skinHeight / skinWidth;
260
+ projectionMatrix.ortho (1 .0f , -1 .0f , aspectRatio, -aspectRatio, 0 .1f , 0 .0f );
261
+ projectionMatrix.scale (skinWidth / bounds.width () / m_scale, skinHeight / bounds.height () / m_scale);
262
+
263
+ // Model matrix
264
+ // TODO: This should be calculated and cached by targets
265
+ QMatrix4x4 modelMatrix;
266
+ modelMatrix.rotate (angle, 0 , 0 , 1 );
267
+ modelMatrix.scale (scaleX / textureScale, aspectRatio * scaleY / textureScale);
266
268
m_glF->glDisable (GL_SCISSOR_TEST);
267
-
268
- // For some reason nothing is rendered without this
269
- // TODO: Find out why this is happening
270
- m_painter->beginFrame (m_fbo->width (), m_fbo->height ());
271
- m_painter->stroke ();
272
- m_painter->endFrame ();
273
-
274
- // Create a temporary FBO for graphic effects
275
- QOpenGLFramebufferObject tmpFbo (texture.size ());
276
- m_painter->beginFrame (tmpFbo.width (), tmpFbo.height ());
269
+ m_glF->glDisable (GL_DEPTH_TEST);
277
270
278
271
// Create a FBO for the current texture
279
- unsigned int fbo;
280
- m_glF->glGenFramebuffers (1 , &fbo);
281
- m_glF->glBindFramebuffer (GL_FRAMEBUFFER, fbo);
272
+ m_glF->glBindFramebuffer (GL_FRAMEBUFFER, m_stampFbo);
282
273
m_glF->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.handle (), 0 );
283
274
284
275
if (m_glF->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
285
276
qWarning () << " error: framebuffer incomplete (stamp " + target->scratchTarget ()->name () + " )" ;
286
- m_glF->glDeleteFramebuffers ( 1 , &fbo );
277
+ m_glF->glBindFramebuffer (GL_FRAMEBUFFER, 0 );
287
278
return ;
288
279
}
289
280
281
+ // Set viewport
282
+ m_glF->glViewport ((stageWidth / 2 ) + bounds.left () * m_scale, (stageHeight / 2 ) + bounds.bottom () * m_scale, bounds.width () * m_scale, bounds.height () * m_scale);
283
+
290
284
// Get the shader program for the current set of effects
291
285
ShaderManager *shaderManager = ShaderManager::instance ();
292
286
@@ -298,62 +292,24 @@ void PenLayer::stamp(IRenderedTarget *target)
298
292
m_glF->glBindBuffer (GL_ARRAY_BUFFER, m_vbo);
299
293
300
294
// Render to the target framebuffer
301
- m_glF->glBindFramebuffer (GL_FRAMEBUFFER, tmpFbo. handle ());
295
+ m_glF->glBindFramebuffer (GL_FRAMEBUFFER, m_fbo-> handle ());
302
296
shaderProgram->bind ();
303
297
m_glF->glBindVertexArray (m_vao);
304
298
m_glF->glActiveTexture (GL_TEXTURE0);
305
299
m_glF->glBindTexture (GL_TEXTURE_2D, texture.handle ());
306
300
shaderManager->setUniforms (shaderProgram, 0 , texture.size (), effects); // set texture and effect uniforms
307
- m_glF->glDrawArrays (GL_TRIANGLE_STRIP, 0 , 4 );
308
-
309
- m_painter->endFrame ();
310
-
311
- // Resize to square (for rotation)
312
- const double dim = std::max (tmpFbo.width (), tmpFbo.height ());
313
- QOpenGLFramebufferObject resizeFbo (dim, dim);
314
- resizeFbo.bind ();
315
- m_painter->beginFrame (dim, dim);
316
-
317
- const QRect resizeRect (QPoint (0 , 0 ), tmpFbo.size ());
318
- const QMatrix4x4 matrix = QOpenGLTextureBlitter::targetTransform (resizeRect, QRect (QPoint (0 , 0 ), resizeFbo.size ()));
319
- m_glF->glClearColor (0 .0f , 0 .0f , 0 .0f , 0 .0f );
320
- m_glF->glClear (GL_COLOR_BUFFER_BIT);
321
- m_blitter.bind ();
322
- m_blitter.blit (tmpFbo.texture (), matrix, QOpenGLTextureBlitter::OriginBottomLeft);
323
- m_blitter.release ();
324
-
325
- m_painter->endFrame ();
326
- resizeFbo.release ();
301
+ shaderProgram->setUniformValue (" u_projectionMatrix" , projectionMatrix);
302
+ shaderProgram->setUniformValue (" u_modelMatrix" , modelMatrix);
303
+ m_glF->glDrawArrays (GL_TRIANGLES, 0 , 6 );
327
304
328
305
// Cleanup
329
306
shaderProgram->release ();
330
307
m_glF->glBindVertexArray (0 );
331
308
m_glF->glBindBuffer (GL_ARRAY_BUFFER, 0 );
332
309
m_glF->glBindFramebuffer (GL_FRAMEBUFFER, 0 );
333
- m_glF->glDeleteFramebuffers (1 , &fbo);
334
-
335
- // Transform
336
- const double width = resizeFbo.width () / textureScale;
337
- const double height = resizeFbo.height () / textureScale;
338
- QRectF targetRect (QPoint (x, y), QSizeF (width, height));
339
- QTransform transform = QOpenGLTextureBlitter::targetTransform (targetRect, QRect (QPoint (centerX, centerY), m_fbo->size ())).toTransform ();
340
- const double dx = 2 * (centerX - width / 2.0 ) / width;
341
- const double dy = -2 * (centerY - height / 2.0 ) / height;
342
- transform.translate (dx, dy);
343
- transform.rotate (angle);
344
- transform.scale (scale * (mirror ? -1 : 1 ), scale);
345
- transform.translate (-dx, -dy);
346
-
347
- // Blit
348
- m_fbo->bind ();
349
- m_painter->beginFrame (m_fbo->width (), m_fbo->height ());
350
- m_blitter.bind ();
351
- m_blitter.blit (resizeFbo.texture (), transform, QOpenGLTextureBlitter::OriginBottomLeft);
352
- m_blitter.release ();
353
- m_painter->endFrame ();
354
- m_fbo->release ();
355
310
356
311
m_glF->glEnable (GL_SCISSOR_TEST);
312
+ m_glF->glEnable (GL_DEPTH_TEST);
357
313
358
314
m_textureDirty = true ;
359
315
m_boundsDirty = true ;
0 commit comments