Skip to content

Commit 590c2ca

Browse files
authored
Merge pull request #440 from LLK/revert-419-coordinates-fixups-2
Revert "Adjust CPU `isTouchingColor` to match GPU results (again)"
2 parents e365a90 + 757d7e3 commit 590c2ca

File tree

8 files changed

+104
-127
lines changed

8 files changed

+104
-127
lines changed

src/Drawable.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ const __isTouchingPosition = twgl.v3.create();
2424
* @return {twgl.v3} [x,y] texture space float vector - transformed by effects and matrix
2525
*/
2626
const getLocalPosition = (drawable, vec) => {
27-
// Transform from world coordinates to Drawable coordinates.
27+
// Transfrom from world coordinates to Drawable coordinates.
2828
const localPosition = __isTouchingPosition;
2929
const v0 = vec[0];
3030
const v1 = vec[1];
3131
const m = drawable._inverseMatrix;
32+
// var v2 = v[2];
3233
const d = (v0 * m[3]) + (v1 * m[7]) + m[15];
3334
// The RenderWebGL quad flips the texture's X axis. So rendered bottom
3435
// left is 1, 0 and the top right is 0, 1. Flip the X axis so
@@ -341,7 +342,7 @@ class Drawable {
341342
// Drawable configures a 3D matrix for drawing in WebGL, but most values
342343
// will never be set because the inputs are on the X and Y position axis
343344
// and the Z rotation axis. Drawable can bring the work inside
344-
// _calculateTransform and greatly reduce the amount of math and array
345+
// _calculateTransform and greatly reduce the ammount of math and array
345346
// assignments needed.
346347

347348
const scale0 = this._skinScale[0];
@@ -624,6 +625,11 @@ class Drawable {
624625
*/
625626
static sampleColor4b (vec, drawable, dst) {
626627
const localPosition = getLocalPosition(drawable, vec);
628+
if (localPosition[0] < 0 || localPosition[1] < 0 ||
629+
localPosition[0] > 1 || localPosition[1] > 1) {
630+
dst[3] = 0;
631+
return dst;
632+
}
627633
const textColor =
628634
// commenting out to only use nearest for now
629635
// drawable.useNearest ?

src/RenderWebGL.js

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ class RenderWebGL extends EventEmitter {
267267
this._yBottom = yBottom;
268268
this._yTop = yTop;
269269

270-
this._projection = this._makeOrthoProjection(xLeft, xRight, yBottom, yTop);
270+
// swap yBottom & yTop to fit Scratch convention of +y=up
271+
this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1);
271272

272273
this._setNativeSize(Math.abs(xRight - xLeft), Math.abs(yBottom - yTop));
273274
}
@@ -291,20 +292,6 @@ class RenderWebGL extends EventEmitter {
291292
this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize});
292293
}
293294

294-
/**
295-
* Build a projection matrix for Scratch coordinates. For example, `_makeOrthoProjection(-240,240,-180,180)` will
296-
* mean the lower-left pixel is at (-240,-179) and the upper right pixel is at (239,180), matching Scratch 2.0.
297-
* @param {number} xLeft - the left edge of the projection volume (-240)
298-
* @param {number} xRight - the right edge of the projection volume (240)
299-
* @param {number} yBottom - the bottom edge of the projection volume (-180)
300-
* @param {number} yTop - the top edge of the projection volume (180)
301-
* @returns {module:twgl/m4.Mat4} - a projection matrix containing [xLeft,xRight) and (yBottom,yTop]
302-
*/
303-
_makeOrthoProjection (xLeft, xRight, yBottom, yTop) {
304-
// swap yBottom & yTop to fit Scratch convention of +y=up
305-
return twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1);
306-
}
307-
308295
/**
309296
* Create a new bitmap skin from a snapshot of the provided bitmap data.
310297
* @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin.
@@ -542,7 +529,7 @@ class RenderWebGL extends EventEmitter {
542529
* Returns the position of the given drawableID in the draw list. This is
543530
* the absolute position irrespective of layer group.
544531
* @param {number} drawableID The drawable ID to find.
545-
* @return {number} The position of the given drawable ID.
532+
* @return {number} The postion of the given drawable ID.
546533
*/
547534
getDrawableOrder (drawableID) {
548535
return this._drawList.indexOf(drawableID);
@@ -556,7 +543,7 @@ class RenderWebGL extends EventEmitter {
556543
* "go to back": setDrawableOrder(id, 1); (assuming stage at 0).
557544
* "go to front": setDrawableOrder(id, Infinity);
558545
* @param {int} drawableID ID of Drawable to reorder.
559-
* @param {number} order New absolute order or relative order adjustment.
546+
* @param {number} order New absolute order or relative order adjusment.
560547
* @param {string=} group Name of layer group drawable belongs to.
561548
* Reordering will not take place if drawable cannot be found within the bounds
562549
* of the layer group.
@@ -727,7 +714,7 @@ class RenderWebGL extends EventEmitter {
727714

728715
/**
729716
* Check if a particular Drawable is touching a particular color.
730-
* Unlike touching drawable, if the "tester" is invisible, we will still test.
717+
* Unlike touching drawable, if the "tester" is invisble, we will still test.
731718
* @param {int} drawableID The ID of the Drawable to check.
732719
* @param {Array<int>} color3b Test if the Drawable is touching this color.
733720
* @param {Array<int>} [mask3b] Optionally mask the check to this part of Drawable.
@@ -751,7 +738,7 @@ class RenderWebGL extends EventEmitter {
751738

752739
// if there are just too many pixels to CPU render efficiently, we need to let readPixels happen
753740
if (bounds.width * bounds.height * (candidates.length + 1) >= maxPixelsForCPU) {
754-
this._isTouchingColorGpuStart(drawableID, candidates.map(({id}) => id), bounds, color3b, mask3b);
741+
this._isTouchingColorGpuStart(drawableID, candidates.map(({id}) => id).reverse(), bounds, color3b, mask3b);
755742
}
756743

757744
const drawable = this._allDrawables[drawableID];
@@ -760,24 +747,24 @@ class RenderWebGL extends EventEmitter {
760747
const hasMask = Boolean(mask3b);
761748

762749
// Scratch Space - +y is top
763-
for (let y = 0; y < bounds.height; ++y) {
764-
if (bounds.width * y * (candidates.length + 1) >= maxPixelsForCPU) {
765-
return this._isTouchingColorGpuFin(bounds, color3b, y);
750+
for (let y = bounds.bottom; y <= bounds.top; y++) {
751+
if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= maxPixelsForCPU) {
752+
return this._isTouchingColorGpuFin(bounds, color3b, y - bounds.bottom);
766753
}
767-
for (let x = 0; x < bounds.width; ++x) {
768-
point[0] = bounds.left + x; // bounds.left <= point[0] < bounds.right
769-
point[1] = bounds.top - y; // bounds.bottom < point[1] <= bounds.top ("flipped")
754+
for (let x = bounds.left; x <= bounds.right; x++) {
755+
point[1] = y;
756+
point[0] = x;
770757
// if we use a mask, check our sample color...
771758
if (hasMask ?
772759
maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) :
773760
drawable.isTouching(point)) {
774761
RenderWebGL.sampleColor3b(point, candidates, color);
775762
if (debugCanvasContext) {
776763
debugCanvasContext.fillStyle = `rgb(${color[0]},${color[1]},${color[2]})`;
777-
debugCanvasContext.fillRect(x, y, 1, 1);
764+
debugCanvasContext.fillRect(x - bounds.left, bounds.bottom - y, 1, 1);
778765
}
779766
// ...and the target color is drawn at this pixel
780-
if (colorMatches(color3b, color, 0)) {
767+
if (colorMatches(color, color3b, 0)) {
781768
return true;
782769
}
783770
}
@@ -807,7 +794,7 @@ class RenderWebGL extends EventEmitter {
807794
// Limit size of viewport to the bounds around the target Drawable,
808795
// and create the projection matrix for the draw.
809796
gl.viewport(0, 0, bounds.width, bounds.height);
810-
const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom);
797+
const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1);
811798

812799
let fillBackgroundColor = this._backgroundColor;
813800

@@ -890,7 +877,7 @@ class RenderWebGL extends EventEmitter {
890877
const candidates = this._candidatesTouching(drawableID,
891878
// even if passed an invisible drawable, we will NEVER touch it!
892879
candidateIDs.filter(id => this._allDrawables[id]._visible));
893-
// if we are invisible we don't touch anything.
880+
// if we are invisble we don't touch anything.
894881
if (candidates.length === 0 || !this._allDrawables[drawableID]._visible) {
895882
return false;
896883
}
@@ -923,7 +910,7 @@ class RenderWebGL extends EventEmitter {
923910

924911
/**
925912
* Convert a client based x/y position on the canvas to a Scratch 3 world space
926-
* Rectangle. This creates rectangles with a radius to cover selecting multiple
913+
* Rectangle. This creates recangles with a radius to cover selecting multiple
927914
* scratch pixels with touch / small render areas.
928915
*
929916
* @param {int} centerX The client x coordinate of the picking location.
@@ -1040,7 +1027,7 @@ class RenderWebGL extends EventEmitter {
10401027
for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) {
10411028

10421029
// Check candidates in the reverse order they would have been
1043-
// drawn. This will determine what candidate's silhouette pixel
1030+
// drawn. This will determine what candiate's silhouette pixel
10441031
// would have been drawn at the point.
10451032
for (let d = candidateIDs.length - 1; d >= 0; d--) {
10461033
const id = candidateIDs[d];
@@ -1124,7 +1111,7 @@ class RenderWebGL extends EventEmitter {
11241111
// Limit size of viewport to the bounds around the target Drawable,
11251112
// and create the projection matrix for the draw.
11261113
gl.viewport(0, 0, bounds.width, bounds.height);
1127-
const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom);
1114+
const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1);
11281115

11291116
gl.clearColor(0, 0, 0, 0);
11301117
gl.clear(gl.COLOR_BUFFER_BIT);
@@ -1199,7 +1186,7 @@ class RenderWebGL extends EventEmitter {
11991186
const pickY = bounds.top - scratchY;
12001187

12011188
gl.viewport(0, 0, bounds.width, bounds.height);
1202-
const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom);
1189+
const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1);
12031190

12041191
gl.clearColor.apply(gl, this._backgroundColor);
12051192
gl.clear(gl.COLOR_BUFFER_BIT);
@@ -1268,7 +1255,8 @@ class RenderWebGL extends EventEmitter {
12681255
}
12691256

12701257
/**
1271-
* Filter a list of candidates for a touching query into only those that could possibly intersect the given bounds.
1258+
* Filter a list of candidates for a touching query into only those that
1259+
* could possibly intersect the given bounds.
12721260
* @param {int} drawableID - ID for drawable of query.
12731261
* @param {Array<int>} candidateIDs - Candidates for touching query.
12741262
* @return {?Array< {id, drawable, intersection} >} Filtered candidates with useful data.
@@ -1279,7 +1267,8 @@ class RenderWebGL extends EventEmitter {
12791267
if (bounds === null) {
12801268
return result;
12811269
}
1282-
for (let index = 0; index < candidateIDs.length; ++index) {
1270+
// iterate through the drawables list BACKWARDS - we want the top most item to be the first we check
1271+
for (let index = candidateIDs.length - 1; index >= 0; index--) {
12831272
const id = candidateIDs[index];
12841273
if (id !== drawableID) {
12851274
const drawable = this._allDrawables[id];
@@ -1439,7 +1428,7 @@ class RenderWebGL extends EventEmitter {
14391428

14401429
// Limit size of viewport to the bounds around the stamp Drawable and create the projection matrix for the draw.
14411430
gl.viewport(0, 0, bounds.width, bounds.height);
1442-
const projection = this._makeOrthoProjection(bounds.left, bounds.right, bounds.top, bounds.bottom);
1431+
const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1);
14431432

14441433
gl.clearColor(0, 0, 0, 0);
14451434
gl.clear(gl.COLOR_BUFFER_BIT);
@@ -1528,7 +1517,7 @@ class RenderWebGL extends EventEmitter {
15281517
* can skip superfluous extra state calls when it is already in that
15291518
* region. Since one region may be entered from within another a exit
15301519
* handle can also be registered that is called when a new region is about
1531-
* to be entered to restore a common in-between state.
1520+
* to be entered to restore a common inbetween state.
15321521
*
15331522
* @param {any} regionId - id of the region to enter
15341523
* @param {function} enter - handle to call when first entering a region
@@ -1660,7 +1649,7 @@ class RenderWebGL extends EventEmitter {
16601649
*
16611650
* The determinant is useful in this case to know if AC is counter
16621651
* clockwise from AB. A positive value means the AC is counter
1663-
* clockwise from AC. A negative value means AC is clockwise from AB.
1652+
* clockwise from AC. A negative value menas AC is clockwise from AB.
16641653
*
16651654
* @param {Float32Array} A A 2d vector in space.
16661655
* @param {Float32Array} B A 2d vector in space.
@@ -1765,15 +1754,16 @@ class RenderWebGL extends EventEmitter {
17651754
* Sample a "final" color from an array of drawables at a given scratch space.
17661755
* Will blend any alpha values with the drawables "below" it.
17671756
* @param {twgl.v3} vec Scratch Vector Space to sample
1768-
* @param {Array<Drawables>} drawables A list of drawables with the "bottom most" drawable at index 0
1757+
* @param {Array<Drawables>} drawables A list of drawables with the "top most"
1758+
* drawable at index 0
17691759
* @param {Uint8ClampedArray} dst The color3b space to store the answer in.
17701760
* @return {Uint8ClampedArray} The dst vector with everything blended down.
17711761
*/
17721762
static sampleColor3b (vec, drawables, dst) {
17731763
dst = dst || new Uint8ClampedArray(3);
17741764
dst.fill(0);
17751765
let blendAlpha = 1;
1776-
for (let index = drawables.length - 1; blendAlpha !== 0 && index >= 0; --index) {
1766+
for (let index = 0; blendAlpha !== 0 && index < drawables.length; index++) {
17771767
/*
17781768
if (left > vec[0] || right < vec[0] ||
17791769
bottom > vec[1] || top < vec[0]) {

src/Silhouette.js

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@
1111
let __SilhouetteUpdateCanvas;
1212

1313
/**
14-
* Internal helper function (in hopes that compiler can inline). Get the alpha value for a texel in the silhouette
15-
* data, or 0 if outside it's bounds.
14+
* Internal helper function (in hopes that compiler can inline). Get a pixel
15+
* from silhouette data, or 0 if outside it's bounds.
1616
* @private
17-
* @param {Silhouette} $0 - has data, width, and height
18-
* @param {number} x - X position in texels (0..width).
19-
* @param {number} y - Y position in texels (0..height).
17+
* @param {Silhouette} silhouette - has data width and height
18+
* @param {number} x - x
19+
* @param {number} y - y
2020
* @return {number} Alpha value for x/y position
2121
*/
2222
const getPoint = ({_width: width, _height: height, _colorData: data}, x, y) => {
23-
// 0 if outside bounds, otherwise read from data.
23+
// 0 if outside bouds, otherwise read from data.
2424
if (x >= width || y >= height || x < 0 || y < 0) {
2525
return 0;
2626
}
@@ -39,14 +39,14 @@ const __cornerWork = [
3939

4040
/**
4141
* Get the color from a given silhouette at an x/y local texture position.
42-
* @param {Silhouette} $0 - The silhouette to sample.
43-
* @param {number} x - X position in texels (0..width).
44-
* @param {number} y - Y position in texels (0..height).
45-
* @param {Uint8ClampedArray} dst - A color 4b space.
46-
* @return {Uint8ClampedArray} - The dst vector.
42+
* @param {Silhouette} The silhouette to sample.
43+
* @param {number} x X position of texture (0-1).
44+
* @param {number} y Y position of texture (0-1).
45+
* @param {Uint8ClampedArray} dst A color 4b space.
46+
* @return {Uint8ClampedArray} The dst vector.
4747
*/
4848
const getColor4b = ({_width: width, _height: height, _colorData: data}, x, y, dst) => {
49-
// 0 if outside bounds, otherwise read from data.
49+
// 0 if outside bouds, otherwise read from data.
5050
if (x >= width || y >= height || x < 0 || y < 0) {
5151
return dst.fill(0);
5252
}
@@ -110,7 +110,7 @@ class Silhouette {
110110
}
111111

112112
this._colorData = imageData.data;
113-
// delete our custom overridden "uninitialized" color functions
113+
// delete our custom overriden "uninitalized" color functions
114114
// let the prototype work for itself
115115
delete this.colorAtNearest;
116116
delete this.colorAtLinear;
@@ -124,10 +124,12 @@ class Silhouette {
124124
* @returns {Uint8ClampedArray} dst
125125
*/
126126
colorAtNearest (vec, dst) {
127-
const x = Math.round(vec[0] * this._width);
128-
const y = Math.round(vec[1] * this._height);
129-
const color = getColor4b(this, x, y, dst);
130-
return color;
127+
return getColor4b(
128+
this,
129+
Math.floor(vec[0] * (this._width - 1)),
130+
Math.floor(vec[1] * (this._height - 1)),
131+
dst
132+
);
131133
}
132134

133135
/**

src/shaders/sprite.frag

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ uniform sampler2D u_skin;
4545

4646
varying vec2 v_texCoord;
4747

48-
const float minAlpha = 1.0 / 255.0;
49-
5048
#if !defined(DRAW_MODE_silhouette) && (defined(ENABLE_color))
5149
// Branchless color conversions based on code from:
5250
// http://www.chilliant.com/rgb2hsv.html by Ian Taylor
@@ -157,14 +155,9 @@ void main()
157155

158156
gl_FragColor = texture2D(u_skin, texcoord0);
159157

160-
if (gl_FragColor.a < minAlpha)
161-
{
162-
discard;
163-
}
164-
165-
#ifdef ENABLE_ghost
166-
gl_FragColor.a *= u_ghost;
167-
#endif // ENABLE_ghost
158+
#ifdef ENABLE_ghost
159+
gl_FragColor.a *= u_ghost;
160+
#endif // ENABLE_ghost
168161

169162
#ifdef DRAW_MODE_silhouette
170163
// switch to u_silhouetteColor only AFTER the alpha test

test/integration/index.html

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,7 @@
2929
vm.attachV2BitmapAdapter(new ScratchSVGRenderer.BitmapAdapter());
3030

3131
document.getElementById('file').addEventListener('click', e => {
32-
const loaded = document.getElementById('loaded');
33-
if (loaded) {
34-
document.body.removeChild(loaded);
35-
}
32+
document.body.removeChild(document.getElementById('loaded'));
3633
});
3734

3835
document.getElementById('file').addEventListener('change', e => {

0 commit comments

Comments
 (0)