Skip to content

Commit 3e00808

Browse files
committed
Cache sprite-specific data in Drawable class
1 parent 5ae4105 commit 3e00808

File tree

3 files changed

+124
-63
lines changed

3 files changed

+124
-63
lines changed

src/Renderer.js

+28-60
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Matrix from "./renderer/Matrix.js";
2+
import Drawable from "./renderer/Drawable.js";
23
import BitmapSkin from "./renderer/BitmapSkin.js";
34
import PenSkin from "./renderer/PenSkin.js";
45
import SpeechBubbleSkin from "./renderer/SpeechBubbleSkin.js";
@@ -8,7 +9,6 @@ import ShaderManager from "./renderer/ShaderManager.js";
89
import { effectNames, effectBitmasks } from "./renderer/effectInfo.js";
910

1011
import Costume from "./Costume.js";
11-
import { Sprite, Stage } from "./Sprite.js";
1212

1313
export default class Renderer {
1414
constructor(project, renderTarget) {
@@ -25,6 +25,7 @@ export default class Renderer {
2525
}
2626

2727
this._shaderManager = new ShaderManager(this);
28+
this._drawables = new WeakMap();
2829
this._skins = new WeakMap();
2930

3031
this._currentShader = null;
@@ -69,24 +70,33 @@ export default class Renderer {
6970
// Retrieve a given object (e.g. costume or speech bubble)'s skin. If it doesn't exist, make one.
7071
_getSkin(obj) {
7172
if (this._skins.has(obj)) {
72-
const skin = this._skins.get(obj);
73-
return skin;
74-
} else {
75-
let skin;
76-
77-
if (obj instanceof Costume) {
78-
if (obj.isBitmap) {
79-
skin = new BitmapSkin(this, obj.img);
80-
} else {
81-
skin = new VectorSkin(this, obj.img);
82-
}
73+
return this._skins.get(obj);
74+
}
75+
76+
let skin;
77+
78+
if (obj instanceof Costume) {
79+
if (obj.isBitmap) {
80+
skin = new BitmapSkin(this, obj.img);
8381
} else {
84-
// If it's not a costume, assume it's a speech bubble.
85-
skin = new SpeechBubbleSkin(this, obj);
82+
skin = new VectorSkin(this, obj.img);
8683
}
87-
this._skins.set(obj, skin);
88-
return skin;
84+
} else {
85+
// If it's not a costume, assume it's a speech bubble.
86+
skin = new SpeechBubbleSkin(this, obj);
8987
}
88+
this._skins.set(obj, skin);
89+
return skin;
90+
}
91+
92+
// Retrieve the renderer-specific data object for a given sprite or clone. If it doesn't exist, make one.
93+
_getDrawable(sprite) {
94+
if (this._drawables.has(sprite)) {
95+
return this._drawables.get(sprite);
96+
}
97+
const drawable = new Drawable(this, sprite);
98+
this._drawables.set(sprite, drawable);
99+
return drawable;
90100
}
91101

92102
// Create a framebuffer info object, which contains the following:
@@ -337,48 +347,6 @@ export default class Renderer {
337347
return stage;
338348
}
339349

340-
// Calculate the transform matrix for a sprite.
341-
// TODO: store the transform matrix in the sprite itself. That adds some complexity though,
342-
// so it's better off in another PR.
343-
_calculateSpriteMatrix(spr) {
344-
// These transforms are actually in reverse order because lol matrices
345-
const m = Matrix.create();
346-
if (!(spr instanceof Stage)) {
347-
Matrix.translate(m, m, spr.x, spr.y);
348-
switch (spr.rotationStyle) {
349-
case Sprite.RotationStyle.ALL_AROUND: {
350-
Matrix.rotate(m, m, spr.scratchToRad(spr.direction));
351-
break;
352-
}
353-
case Sprite.RotationStyle.LEFT_RIGHT: {
354-
if (spr.direction < 0) Matrix.scale(m, m, -1, 1);
355-
break;
356-
}
357-
}
358-
359-
const spriteScale = spr.size / 100;
360-
Matrix.scale(m, m, spriteScale, spriteScale);
361-
}
362-
363-
const scalingFactor = 1 / spr.costume.resolution;
364-
// Rotation centers are in non-Scratch space (positive y-values = down),
365-
// but these transforms are in Scratch space (negative y-values = down).
366-
Matrix.translate(
367-
m,
368-
m,
369-
-spr.costume.center.x * scalingFactor,
370-
(spr.costume.center.y - spr.costume.height) * scalingFactor
371-
);
372-
Matrix.scale(
373-
m,
374-
m,
375-
spr.costume.width * scalingFactor,
376-
spr.costume.height * scalingFactor
377-
);
378-
379-
return m;
380-
}
381-
382350
// Calculate the transform matrix for a speech bubble attached to a sprite.
383351
_calculateSpeechBubbleMatrix(spr, speechBubbleSkin) {
384352
const sprBounds = this.getBoundingBox(spr);
@@ -448,7 +416,7 @@ export default class Renderer {
448416
this._renderSkin(
449417
this._getSkin(sprite.costume),
450418
options.drawMode,
451-
this._calculateSpriteMatrix(sprite),
419+
this._getDrawable(sprite).getMatrix(),
452420
spriteScale,
453421
sprite.effects,
454422
options.effectMask,
@@ -472,7 +440,7 @@ export default class Renderer {
472440
}
473441

474442
getBoundingBox(sprite) {
475-
return Rectangle.fromMatrix(this._calculateSpriteMatrix(sprite));
443+
return Rectangle.fromMatrix(this._getDrawable(sprite).getMatrix());
476444
}
477445

478446
// Mask drawing in to only areas where this sprite is opaque.

src/renderer/Drawable.js

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import Matrix from "./Matrix.js";
2+
3+
import { Sprite, Stage } from "../Sprite.js";
4+
5+
// Renderer-specific data for an instance (the original or a clone) of a Sprite
6+
export default class Drawable {
7+
constructor(renderer, sprite) {
8+
this._renderer = renderer;
9+
this._sprite = sprite;
10+
11+
// Transformation matrix for the sprite.
12+
this._matrix = Matrix.create();
13+
this._calculateSpriteMatrix();
14+
}
15+
16+
_calculateSpriteMatrix() {
17+
const m = this._matrix;
18+
Matrix.identity(m);
19+
const spr = this._sprite;
20+
if (!(spr instanceof Stage)) {
21+
Matrix.translate(m, m, spr.x, spr.y);
22+
switch (spr.rotationStyle) {
23+
case Sprite.RotationStyle.ALL_AROUND: {
24+
Matrix.rotate(m, m, spr.scratchToRad(spr.direction));
25+
break;
26+
}
27+
case Sprite.RotationStyle.LEFT_RIGHT: {
28+
if (spr.direction < 0) Matrix.scale(m, m, -1, 1);
29+
break;
30+
}
31+
}
32+
33+
const spriteScale = spr.size / 100;
34+
Matrix.scale(m, m, spriteScale, spriteScale);
35+
}
36+
37+
const scalingFactor = 1 / spr.costume.resolution;
38+
// Rotation centers are in non-Scratch space (positive y-values = down),
39+
// but these transforms are in Scratch space (negative y-values = down).
40+
Matrix.translate(
41+
m,
42+
m,
43+
-spr.costume.center.x * scalingFactor,
44+
(spr.costume.center.y - spr.costume.height) * scalingFactor
45+
);
46+
Matrix.scale(
47+
m,
48+
m,
49+
spr.costume.width * scalingFactor,
50+
spr.costume.height * scalingFactor
51+
);
52+
53+
// Store the values we used to compute the matrix so we only recalculate
54+
// the matrix when we really need to.
55+
this._matrixX = this._sprite.x;
56+
this._matrixY = this._sprite.y;
57+
this._matrixRotation = this._sprite.direction;
58+
this._matrixRotationStyle = this._sprite.rotationStyle;
59+
this._matrixScale = this._sprite.scale;
60+
this._matrixCostume = this._sprite.costume;
61+
this._matrixCostumeLoaded = this._sprite.costume.img.complete;
62+
}
63+
64+
getMatrix() {
65+
// If all the values we used to calculate the matrix haven't changed since
66+
// we last calculated the matrix, we can just return the matrix as-is.
67+
if (
68+
this._matrixX !== this._sprite.x ||
69+
this._matrixY !== this._sprite.y ||
70+
this._matrixRotation !== this._sprite.direction ||
71+
this._matrixRotationStyle !== this._sprite.rotationStyle ||
72+
this._matrixScale !== this._sprite.scale ||
73+
this._matrixCostume !== this._sprite.costume ||
74+
this._matrixCostumeLoaded !== this._sprite.costume.img.complete
75+
) {
76+
this._calculateSpriteMatrix();
77+
}
78+
79+
return this._matrix;
80+
}
81+
}

src/renderer/Matrix.js

+15-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,24 @@ export default class Matrix {
77
// Create a new 3x3 transform matrix, initialized to the identity matrix.
88
static create() {
99
const matrix = new Float32Array(9);
10-
matrix[0] = 1;
11-
matrix[4] = 1;
12-
matrix[8] = 1;
10+
Matrix.identity(matrix);
1311
return matrix;
1412
}
1513

14+
// Reset a matrix to the identity matrix
15+
static identity(dst) {
16+
dst[0] = 1;
17+
dst[1] = 0;
18+
dst[2] = 0;
19+
dst[3] = 0;
20+
dst[4] = 1;
21+
dst[5] = 0;
22+
dst[6] = 0;
23+
dst[7] = 0;
24+
dst[8] = 1;
25+
return dst;
26+
}
27+
1628
// Translate a matrix by the given X and Y values
1729
static translate(dst, src, x, y) {
1830
const a00 = src[0],

0 commit comments

Comments
 (0)