Compare commits
56 Commits
greenkeepe
...
greenkeepe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6e47f7173 | ||
|
|
c9f86ef53b | ||
|
|
4bf233ef36 | ||
|
|
253cbd019d | ||
|
|
1f0f89920a | ||
|
|
25df9f1ab7 | ||
|
|
7680270f40 | ||
|
|
c7b22b58c2 | ||
|
|
f2d457a827 | ||
|
|
e3c68e7122 | ||
|
|
e64d8727ec | ||
|
|
c390124df4 | ||
|
|
8bd6241160 | ||
|
|
c8b9516219 | ||
|
|
1db67a474e | ||
|
|
028b4eba3f | ||
|
|
59cef02fdb | ||
|
|
99d6e46f7e | ||
|
|
992977d6c6 | ||
|
|
a358c8f916 | ||
|
|
e8d71277e2 | ||
|
|
b4f9f28417 | ||
|
|
fba2d90fda | ||
|
|
31db3d8596 | ||
|
|
898d5d7885 | ||
|
|
9b11ac894d | ||
|
|
fc6fcd0543 | ||
|
|
b77f4c663a | ||
|
|
5e5a423d39 | ||
|
|
402cfbf99f | ||
|
|
a0dd716c23 | ||
|
|
183919a20a | ||
|
|
3cfafebb2e | ||
|
|
931ff270dd | ||
|
|
cc448951f9 | ||
|
|
cfa0194ab8 | ||
|
|
2b224eb9da | ||
|
|
735c7caaae | ||
|
|
ad1b7111c8 | ||
|
|
e54b590d56 | ||
|
|
355a8c5395 | ||
|
|
f1a7aab5a6 | ||
|
|
bb84abab87 | ||
|
|
75772989ea | ||
|
|
cf5aafc12f | ||
|
|
a5f852fcc2 | ||
|
|
07544595fd | ||
|
|
e616ab5d35 | ||
|
|
6e072ea026 | ||
|
|
230a68d564 | ||
|
|
a0ce9e3dca | ||
|
|
0676e0f54a | ||
|
|
ba93f21795 | ||
|
|
5be561133d | ||
|
|
bfa90a07cc | ||
|
|
5f38cd1ee1 |
@@ -37,10 +37,10 @@
|
||||
"gh-pages": "^1.0.0",
|
||||
"jsdoc": "^3.5.5",
|
||||
"json": "^9.0.4",
|
||||
"scratch-vm": "0.2.0-prerelease.20181024204838",
|
||||
"scratch-vm": "0.2.0-prerelease.20190207224121",
|
||||
"tap": "^11.0.0",
|
||||
"travis-after-all": "^1.4.4",
|
||||
"uglifyjs-webpack-plugin": "^2.1.1",
|
||||
"uglifyjs-webpack-plugin": "^2.1.2",
|
||||
"webpack": "^4.8.0",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"webpack-dev-server": "^3.1.4"
|
||||
@@ -53,7 +53,7 @@
|
||||
"minilog": "3.1.0",
|
||||
"raw-loader": "^0.5.1",
|
||||
"scratch-storage": "^1.0.0",
|
||||
"scratch-svg-renderer": "0.2.0-prerelease.20181220183040",
|
||||
"scratch-svg-renderer": "0.2.0-prerelease.20190125192231",
|
||||
"twgl.js": "4.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,15 @@ class BitmapSkin extends Skin {
|
||||
return this._texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bounds of the drawable for determining its fenced position.
|
||||
* @param {Array<number>} drawable - The Drawable instance this skin is using.
|
||||
* @return {!Rectangle} The drawable's bounds. For compatibility with Scratch 2, we always use getAABB for bitmaps.
|
||||
*/
|
||||
getFenceBounds (drawable) {
|
||||
return drawable.getAABB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the contents of this skin to a snapshot of the provided bitmap data.
|
||||
* @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin.
|
||||
|
||||
@@ -426,16 +426,16 @@ class Drawable {
|
||||
* Should the drawable use NEAREST NEIGHBOR or LINEAR INTERPOLATION mode
|
||||
*/
|
||||
get useNearest () {
|
||||
// We can't use nearest neighbor unless we are a multiple of 90 rotation
|
||||
if (this._direction % 90 !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Raster skins (bitmaps) should always prefer nearest neighbor
|
||||
if (this.skin.isRaster) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We can't use nearest neighbor unless we are a multiple of 90 rotation
|
||||
if (this._direction % 90 !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the scale of the skin is very close to 100 (0.99999 variance is okay I guess)
|
||||
if (Math.abs(this.scale[0]) > 99 && Math.abs(this.scale[0]) < 101 &&
|
||||
Math.abs(this.scale[1]) > 99 && Math.abs(this.scale[1]) < 101) {
|
||||
|
||||
@@ -132,6 +132,9 @@ class RenderWebGL extends EventEmitter {
|
||||
throw new Error('Could not get WebGL context: this browser or environment may not support WebGL.');
|
||||
}
|
||||
|
||||
/** @type {RenderWebGL.UseGpuModes} */
|
||||
this._useGpuMode = RenderWebGL.UseGpuModes.Automatic;
|
||||
|
||||
/** @type {Drawable[]} */
|
||||
this._allDrawables = [];
|
||||
|
||||
@@ -243,6 +246,14 @@ class RenderWebGL extends EventEmitter {
|
||||
this._debugCanvas = canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Control the use of the GPU or CPU paths in `isTouchingColor`.
|
||||
* @param {RenderWebGL.UseGpuModes} useGpuMode - automatically decide, force CPU, or force GPU.
|
||||
*/
|
||||
setUseGpuMode (useGpuMode) {
|
||||
this._useGpuMode = useGpuMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set logical size of the stage in Scratch units.
|
||||
* @param {int} xLeft The left edge's x-coordinate. Scratch 2 uses -240.
|
||||
@@ -717,9 +728,16 @@ class RenderWebGL extends EventEmitter {
|
||||
|
||||
const bounds = this._candidatesBounds(candidates);
|
||||
|
||||
// if there are just too many pixels to CPU render efficently, we
|
||||
// need to let readPixels happen
|
||||
if (bounds.width * bounds.height * (candidates.length + 1) >= __cpuTouchingColorPixelCount) {
|
||||
const maxPixelsForCPU = this._getMaxPixelsForCPU();
|
||||
|
||||
const debugCanvasContext = this._debugCanvas && this._debugCanvas.getContext('2d');
|
||||
if (debugCanvasContext) {
|
||||
this._debugCanvas.width = bounds.width;
|
||||
this._debugCanvas.height = bounds.height;
|
||||
}
|
||||
|
||||
// if there are just too many pixels to CPU render efficiently, we need to let readPixels happen
|
||||
if (bounds.width * bounds.height * (candidates.length + 1) >= maxPixelsForCPU) {
|
||||
this._isTouchingColorGpuStart(drawableID, candidates.map(({id}) => id).reverse(), bounds, color3b, mask3b);
|
||||
}
|
||||
|
||||
@@ -728,29 +746,45 @@ class RenderWebGL extends EventEmitter {
|
||||
const color = __touchingColor;
|
||||
const hasMask = Boolean(mask3b);
|
||||
|
||||
// Scratch Space - +y is top
|
||||
for (let y = bounds.bottom; y <= bounds.top; y++) {
|
||||
if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= __cpuTouchingColorPixelCount) {
|
||||
if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= maxPixelsForCPU) {
|
||||
return this._isTouchingColorGpuFin(bounds, color3b, y - bounds.bottom);
|
||||
}
|
||||
// Scratch Space - +y is top
|
||||
for (let x = bounds.left; x <= bounds.right; x++) {
|
||||
point[1] = y;
|
||||
point[0] = x;
|
||||
if (
|
||||
// if we use a mask, check our sample color
|
||||
(hasMask ?
|
||||
maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) :
|
||||
drawable.isTouching(point)) &&
|
||||
// and the target color is drawn at this pixel
|
||||
colorMatches(RenderWebGL.sampleColor3b(point, candidates, color), color3b, 0)
|
||||
) {
|
||||
return true;
|
||||
// if we use a mask, check our sample color...
|
||||
if (hasMask ?
|
||||
maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) :
|
||||
drawable.isTouching(point)) {
|
||||
RenderWebGL.sampleColor3b(point, candidates, color);
|
||||
if (debugCanvasContext) {
|
||||
debugCanvasContext.fillStyle = `rgb(${color[0]},${color[1]},${color[2]})`;
|
||||
debugCanvasContext.fillRect(x - bounds.left, bounds.bottom - y, 1, 1);
|
||||
}
|
||||
// ...and the target color is drawn at this pixel
|
||||
if (colorMatches(color, color3b, 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_getMaxPixelsForCPU () {
|
||||
switch (this._useGpuMode) {
|
||||
case RenderWebGL.UseGpuModes.ForceCPU:
|
||||
return Infinity;
|
||||
case RenderWebGL.UseGpuModes.ForceGPU:
|
||||
return 0;
|
||||
case RenderWebGL.UseGpuModes.Automatic:
|
||||
default:
|
||||
return __cpuTouchingColorPixelCount;
|
||||
}
|
||||
}
|
||||
|
||||
_isTouchingColorGpuStart (drawableID, candidateIDs, bounds, color3b, mask3b) {
|
||||
this._doExitDrawRegion();
|
||||
|
||||
@@ -931,7 +965,11 @@ class RenderWebGL extends EventEmitter {
|
||||
const worldPos = twgl.v3.create();
|
||||
|
||||
drawable.updateMatrix();
|
||||
drawable.skin.updateSilhouette();
|
||||
if (drawable.skin) {
|
||||
drawable.skin.updateSilhouette();
|
||||
} else {
|
||||
log.warn(`Could not find skin for drawable with id: ${drawableID}`);
|
||||
}
|
||||
|
||||
for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) {
|
||||
for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) {
|
||||
@@ -963,7 +1001,11 @@ class RenderWebGL extends EventEmitter {
|
||||
// default pick list ignores visible and ghosted sprites.
|
||||
if (drawable.getVisible() && drawable.getUniforms().u_ghost !== 0) {
|
||||
drawable.updateMatrix();
|
||||
drawable.skin.updateSilhouette();
|
||||
if (drawable.skin) {
|
||||
drawable.skin.updateSilhouette();
|
||||
} else {
|
||||
log.warn(`Could not find skin for drawable with id: ${id}`);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1040,11 +1082,28 @@ class RenderWebGL extends EventEmitter {
|
||||
const scratchY = this._nativeSize[1] * ((y / this._gl.canvas.clientHeight) - 0.5);
|
||||
|
||||
const gl = this._gl;
|
||||
twgl.bindFramebufferInfo(gl, this._queryBufferInfo);
|
||||
|
||||
const bounds = drawable.getFastBounds();
|
||||
bounds.snapToInt();
|
||||
|
||||
// Set a reasonable max limit width and height for the bufferInfo bounds
|
||||
const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
|
||||
const clampedWidth = Math.min(2048, bounds.width, maxTextureSize);
|
||||
const clampedHeight = Math.min(2048, bounds.height, maxTextureSize);
|
||||
|
||||
// Make a new bufferInfo since this._queryBufferInfo is limited to 480x360
|
||||
const attachments = [
|
||||
{format: gl.RGBA},
|
||||
{format: gl.DEPTH_STENCIL}
|
||||
];
|
||||
const bufferInfo = twgl.createFramebufferInfo(gl, attachments, clampedWidth, clampedHeight);
|
||||
|
||||
// If the new bufferInfo is invalid, fall back to using the smaller _queryBufferInfo
|
||||
twgl.bindFramebufferInfo(gl, bufferInfo);
|
||||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
|
||||
twgl.bindFramebufferInfo(gl, this._queryBufferInfo);
|
||||
}
|
||||
|
||||
// Translate to scratch units relative to the drawable
|
||||
const pickX = scratchX - bounds.left;
|
||||
const pickY = scratchY + bounds.top;
|
||||
@@ -1291,20 +1350,20 @@ class RenderWebGL extends EventEmitter {
|
||||
|
||||
const dx = x - drawable._position[0];
|
||||
const dy = y - drawable._position[1];
|
||||
const aabb = drawable._skin.getFenceBounds(drawable);
|
||||
const inset = Math.floor(Math.min(aabb.width, aabb.height) / 2);
|
||||
|
||||
const aabb = drawable.getFastBounds();
|
||||
|
||||
const sx = this._xRight - Math.min(FENCE_WIDTH, Math.floor((aabb.right - aabb.left) / 2));
|
||||
const sx = this._xRight - Math.min(FENCE_WIDTH, inset);
|
||||
if (aabb.right + dx < -sx) {
|
||||
x = drawable._position[0] - (sx + aabb.right);
|
||||
x = Math.ceil(drawable._position[0] - (sx + aabb.right));
|
||||
} else if (aabb.left + dx > sx) {
|
||||
x = drawable._position[0] + (sx - aabb.left);
|
||||
x = Math.floor(drawable._position[0] + (sx - aabb.left));
|
||||
}
|
||||
const sy = this._yTop - Math.min(FENCE_WIDTH, Math.floor((aabb.top - aabb.bottom) / 2));
|
||||
const sy = this._yTop - Math.min(FENCE_WIDTH, inset);
|
||||
if (aabb.top + dy < -sy) {
|
||||
y = drawable._position[1] - (sy + aabb.top);
|
||||
y = Math.ceil(drawable._position[1] - (sy + aabb.top));
|
||||
} else if (aabb.bottom + dy > sy) {
|
||||
y = drawable._position[1] + (sy - aabb.bottom);
|
||||
y = Math.floor(drawable._position[1] + (sy - aabb.bottom));
|
||||
}
|
||||
return [x, y];
|
||||
}
|
||||
@@ -1744,4 +1803,25 @@ class RenderWebGL extends EventEmitter {
|
||||
// :3
|
||||
RenderWebGL.prototype.canHazPixels = RenderWebGL.prototype.extractDrawable;
|
||||
|
||||
/**
|
||||
* Values for setUseGPU()
|
||||
* @enum {string}
|
||||
*/
|
||||
RenderWebGL.UseGpuModes = {
|
||||
/**
|
||||
* Heuristically decide whether to use the GPU path, the CPU path, or a dynamic mixture of the two.
|
||||
*/
|
||||
Automatic: 'Automatic',
|
||||
|
||||
/**
|
||||
* Always use the GPU path.
|
||||
*/
|
||||
ForceGPU: 'ForceGPU',
|
||||
|
||||
/**
|
||||
* Always use the CPU path.
|
||||
*/
|
||||
ForceCPU: 'ForceCPU'
|
||||
};
|
||||
|
||||
module.exports = RenderWebGL;
|
||||
|
||||
@@ -80,6 +80,7 @@ class SVGSkin extends Skin {
|
||||
const gl = this._renderer.gl;
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas);
|
||||
this._silhouette.update(this._svgRenderer.canvas);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -136,6 +136,15 @@ class Skin extends EventEmitter {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bounds of the drawable for determining its fenced position.
|
||||
* @param {Array<number>} drawable - The Drawable instance this skin is using.
|
||||
* @return {!Rectangle} The drawable's bounds.
|
||||
*/
|
||||
getFenceBounds (drawable) {
|
||||
return drawable.getFastBounds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update and returns the uniforms for this skin.
|
||||
* @param {Array<number>} scale - The scaling factors to be used.
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['scratch'],
|
||||
env: {
|
||||
browser: true
|
||||
},
|
||||
rules: {
|
||||
'no-console': 'off'
|
||||
}
|
||||
};
|
||||
|
||||
37
src/playground/getMousePosition.js
Normal file
37
src/playground/getMousePosition.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// Adapted from code by Simon Sarris: http://stackoverflow.com/a/10450761
|
||||
const getMousePos = function (event, element) {
|
||||
const stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(element, null).paddingLeft, 10) || 0;
|
||||
const stylePaddingTop = parseInt(document.defaultView.getComputedStyle(element, null).paddingTop, 10) || 0;
|
||||
const styleBorderLeft = parseInt(document.defaultView.getComputedStyle(element, null).borderLeftWidth, 10) || 0;
|
||||
const styleBorderTop = parseInt(document.defaultView.getComputedStyle(element, null).borderTopWidth, 10) || 0;
|
||||
|
||||
// Some pages have fixed-position bars at the top or left of the page
|
||||
// They will mess up mouse coordinates and this fixes that
|
||||
const html = document.body.parentNode;
|
||||
const htmlTop = html.offsetTop;
|
||||
const htmlLeft = html.offsetLeft;
|
||||
|
||||
// Compute the total offset. It's possible to cache this if you want
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
if (typeof element.offsetParent !== 'undefined') {
|
||||
do {
|
||||
offsetX += element.offsetLeft;
|
||||
offsetY += element.offsetTop;
|
||||
} while ((element = element.offsetParent));
|
||||
}
|
||||
|
||||
// Add padding and border style widths to offset
|
||||
// Also add the <html> offsets in case there's a position:fixed bar
|
||||
// This part is not strictly necessary, it depends on your styling
|
||||
offsetX += stylePaddingLeft + styleBorderLeft + htmlLeft;
|
||||
offsetY += stylePaddingTop + styleBorderTop + htmlTop;
|
||||
|
||||
// We return a simple javascript object with x and y defined
|
||||
return {
|
||||
x: event.pageX - offsetX,
|
||||
y: event.pageY - offsetY
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = getMousePos;
|
||||
@@ -12,7 +12,7 @@
|
||||
<canvas id="debug-canvas" width="10" height="10" style="border:3px dashed red"></canvas>
|
||||
<p>
|
||||
<label for="fudgeproperty">Property to tweak:</label>
|
||||
<select id="fudgeproperty" onchange="onFudgePropertyChanged(this.value)">
|
||||
<select id="fudgeproperty">
|
||||
<option value="posx">Position X</option>
|
||||
<option value="posy">Position Y</option>
|
||||
<option value="direction">Direction</option>
|
||||
@@ -27,149 +27,12 @@
|
||||
<option value="ghost">Ghost</option>
|
||||
</select>
|
||||
<label for="fudge">Property Value:</label>
|
||||
<input type="range" id="fudge" style="width:50%" value="90" min="-90" max="270" step="any" oninput="onFudgeChanged(this.value)" onchange="onFudgeChanged(this.value)">
|
||||
<input type="range" id="fudge" style="width:50%" value="90" min="-90" max="270" step="any">
|
||||
</p>
|
||||
<p>
|
||||
<label for="fudgeMin">Min:</label><input id="fudgeMin" type="number" onchange="onFudgeMinChanged(this.value)">
|
||||
<label for="fudgeMax">Max:</label><input id="fudgeMax" type="number" onchange="onFudgeMaxChanged(this.value)">
|
||||
<label for="fudgeMin">Min:</label><input id="fudgeMin" type="number">
|
||||
<label for="fudgeMax">Max:</label><input id="fudgeMax" type="number">
|
||||
</p>
|
||||
<script src="scratch-render.js"></script>
|
||||
<script>
|
||||
var canvas = document.getElementById('scratch-stage');
|
||||
var fudge = 90;
|
||||
var renderer = new ScratchRender(canvas);
|
||||
renderer.setLayerGroupOrdering(['group1']);
|
||||
|
||||
var drawableID = renderer.createDrawable('group1');
|
||||
renderer.updateDrawableProperties(drawableID, {
|
||||
position: [0, 0],
|
||||
scale: [100, 100],
|
||||
direction: 90
|
||||
});
|
||||
|
||||
var drawableID2 = renderer.createDrawable('group1');
|
||||
var wantBitmapSkin = false;
|
||||
|
||||
// Bitmap (squirrel)
|
||||
var image = new Image();
|
||||
image.onload = function () {
|
||||
var bitmapSkinId = renderer.createBitmapSkin(image);
|
||||
if (wantBitmapSkin) {
|
||||
renderer.updateDrawableProperties(drawableID2, {
|
||||
skinId: bitmapSkinId
|
||||
});
|
||||
}
|
||||
};
|
||||
image.crossOrigin = 'anonymous';
|
||||
image.src = 'https://cdn.assets.scratch.mit.edu/internalapi/asset/7e24c99c1b853e52f8e7f9004416fa34.png/get/';
|
||||
|
||||
// SVG (cat 1-a)
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.addEventListener("load", function () {
|
||||
var skinId = renderer.createSVGSkin(xhr.responseText);
|
||||
if (!wantBitmapSkin) {
|
||||
renderer.updateDrawableProperties(drawableID2, {
|
||||
skinId: skinId
|
||||
});
|
||||
}
|
||||
});
|
||||
xhr.open('GET', 'https://cdn.assets.scratch.mit.edu/internalapi/asset/f88bf1935daea28f8ca098462a31dbb0.svg/get/');
|
||||
xhr.send();
|
||||
|
||||
var posX = 0;
|
||||
var posY = 0;
|
||||
var scaleX = 100;
|
||||
var scaleY = 100;
|
||||
var fudgeProperty = 'posx';
|
||||
function onFudgePropertyChanged(newValue) {
|
||||
fudgeProperty = newValue;
|
||||
}
|
||||
var fudgeInput = document.getElementById('fudge');
|
||||
function onFudgeMinChanged(newValue) {
|
||||
fudgeInput.min = newValue;
|
||||
}
|
||||
function onFudgeMaxChanged(newValue) {
|
||||
fudgeInput.max = newValue;
|
||||
}
|
||||
function onFudgeChanged(newValue) {
|
||||
fudge = newValue;
|
||||
var props = {};
|
||||
switch (fudgeProperty) {
|
||||
case 'posx': props.position = [fudge, posY]; posX = fudge; break;
|
||||
case 'posy': props.position = [posX, fudge]; posY = fudge; break;
|
||||
case 'direction': props.direction = fudge; break;
|
||||
case 'scalex': props.scale = [fudge, scaleY]; scaleX = fudge; break;
|
||||
case 'scaley': props.scale = [scaleX, fudge]; scaleY = fudge; break;
|
||||
case 'color': props.color = fudge; break;
|
||||
case 'whirl': props.whirl = fudge; break;
|
||||
case 'fisheye': props.fisheye = fudge; break;
|
||||
case 'pixelate': props.pixelate = fudge; break;
|
||||
case 'mosaic': props.mosaic = fudge; break;
|
||||
case 'brightness': props.brightness = fudge; break;
|
||||
case 'ghost': props.ghost = fudge; break;
|
||||
}
|
||||
renderer.updateDrawableProperties(drawableID2, props);
|
||||
}
|
||||
|
||||
// Adapted from code by Simon Sarris: http://stackoverflow.com/a/10450761
|
||||
function getMousePos(event, element) {
|
||||
var stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(element, null)['paddingLeft'], 10) || 0;
|
||||
var stylePaddingTop = parseInt(document.defaultView.getComputedStyle(element, null)['paddingTop'], 10) || 0;
|
||||
var styleBorderLeft = parseInt(document.defaultView.getComputedStyle(element, null)['borderLeftWidth'], 10) || 0;
|
||||
var styleBorderTop = parseInt(document.defaultView.getComputedStyle(element, null)['borderTopWidth'], 10) || 0;
|
||||
|
||||
// Some pages have fixed-position bars at the top or left of the page
|
||||
// They will mess up mouse coordinates and this fixes that
|
||||
var html = document.body.parentNode;
|
||||
var htmlTop = html.offsetTop;
|
||||
var htmlLeft = html.offsetLeft;
|
||||
|
||||
// Compute the total offset. It's possible to cache this if you want
|
||||
var offsetX = 0, offsetY = 0;
|
||||
if (element.offsetParent !== undefined) {
|
||||
do {
|
||||
offsetX += element.offsetLeft;
|
||||
offsetY += element.offsetTop;
|
||||
} while ((element = element.offsetParent));
|
||||
}
|
||||
|
||||
// Add padding and border style widths to offset
|
||||
// Also add the <html> offsets in case there's a position:fixed bar
|
||||
// This part is not strictly necessary, it depends on your styling
|
||||
offsetX += stylePaddingLeft + styleBorderLeft + htmlLeft;
|
||||
offsetY += stylePaddingTop + styleBorderTop + htmlTop;
|
||||
|
||||
// We return a simple javascript object with x and y defined
|
||||
return {
|
||||
x: event.pageX - offsetX,
|
||||
y: event.pageY - offsetY
|
||||
};
|
||||
}
|
||||
|
||||
canvas.onmousemove = function(event) {
|
||||
var mousePos = getMousePos(event, canvas);
|
||||
renderer.extractColor(mousePos.x, mousePos.y, 30);
|
||||
};
|
||||
|
||||
canvas.onclick = function(event) {
|
||||
var mousePos = getMousePos(event, canvas);
|
||||
var pickID = renderer.pick(mousePos.x, mousePos.y);
|
||||
console.log('You clicked on ' + (pickID < 0 ? 'nothing' : 'ID# ' + pickID));
|
||||
if (pickID >= 0) {
|
||||
console.dir(renderer.extractDrawable(pickID, mousePos.x, mousePos.y));
|
||||
}
|
||||
};
|
||||
|
||||
function drawStep() {
|
||||
renderer.draw();
|
||||
// renderer.getBounds(drawableID2);
|
||||
// renderer.isTouchingColor(drawableID2, [255,255,255]);
|
||||
requestAnimationFrame(drawStep);
|
||||
}
|
||||
drawStep();
|
||||
|
||||
var debugCanvas = /** @type {canvas} */ document.getElementById('debug-canvas');
|
||||
renderer.setDebugCanvas(debugCanvas);
|
||||
</script>
|
||||
<script src="playground.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
141
src/playground/playground.js
Normal file
141
src/playground/playground.js
Normal file
@@ -0,0 +1,141 @@
|
||||
const ScratchRender = require('../RenderWebGL');
|
||||
const getMousePosition = require('./getMousePosition');
|
||||
|
||||
var canvas = document.getElementById('scratch-stage');
|
||||
var fudge = 90;
|
||||
var renderer = new ScratchRender(canvas);
|
||||
renderer.setLayerGroupOrdering(['group1']);
|
||||
|
||||
var drawableID = renderer.createDrawable('group1');
|
||||
renderer.updateDrawableProperties(drawableID, {
|
||||
position: [0, 0],
|
||||
scale: [100, 100],
|
||||
direction: 90
|
||||
});
|
||||
|
||||
var drawableID2 = renderer.createDrawable('group1');
|
||||
var wantBitmapSkin = false;
|
||||
|
||||
// Bitmap (squirrel)
|
||||
var image = new Image();
|
||||
image.addEventListener('load', () => {
|
||||
var bitmapSkinId = renderer.createBitmapSkin(image);
|
||||
if (wantBitmapSkin) {
|
||||
renderer.updateDrawableProperties(drawableID2, {
|
||||
skinId: bitmapSkinId
|
||||
});
|
||||
}
|
||||
});
|
||||
image.crossOrigin = 'anonymous';
|
||||
image.src = 'https://cdn.assets.scratch.mit.edu/internalapi/asset/7e24c99c1b853e52f8e7f9004416fa34.png/get/';
|
||||
|
||||
// SVG (cat 1-a)
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.addEventListener('load', function () {
|
||||
var skinId = renderer.createSVGSkin(xhr.responseText);
|
||||
if (!wantBitmapSkin) {
|
||||
renderer.updateDrawableProperties(drawableID2, {
|
||||
skinId: skinId
|
||||
});
|
||||
}
|
||||
});
|
||||
xhr.open('GET', 'https://cdn.assets.scratch.mit.edu/internalapi/asset/f88bf1935daea28f8ca098462a31dbb0.svg/get/');
|
||||
xhr.send();
|
||||
|
||||
var posX = 0;
|
||||
var posY = 0;
|
||||
var scaleX = 100;
|
||||
var scaleY = 100;
|
||||
var fudgeProperty = 'posx';
|
||||
|
||||
const fudgePropertyInput = document.getElementById('fudgeproperty');
|
||||
fudgePropertyInput.addEventListener('change', event => {
|
||||
fudgeProperty = event.target.value;
|
||||
});
|
||||
|
||||
const fudgeInput = document.getElementById('fudge');
|
||||
|
||||
const fudgeMinInput = document.getElementById('fudgeMin');
|
||||
fudgeMinInput.addEventListener('change', event => {
|
||||
fudgeInput.min = event.target.valueAsNumber;
|
||||
});
|
||||
|
||||
const fudgeMaxInput = document.getElementById('fudgeMax');
|
||||
fudgeMaxInput.addEventListener('change', event => {
|
||||
fudgeInput.max = event.target.valueAsNumber;
|
||||
});
|
||||
|
||||
const handleFudgeChanged = function (event) {
|
||||
fudge = event.target.valueAsNumber;
|
||||
var props = {};
|
||||
switch (fudgeProperty) {
|
||||
case 'posx':
|
||||
props.position = [fudge, posY];
|
||||
posX = fudge;
|
||||
break;
|
||||
case 'posy':
|
||||
props.position = [posX, fudge];
|
||||
posY = fudge;
|
||||
break;
|
||||
case 'direction':
|
||||
props.direction = fudge;
|
||||
break;
|
||||
case 'scalex':
|
||||
props.scale = [fudge, scaleY];
|
||||
scaleX = fudge;
|
||||
break;
|
||||
case 'scaley':
|
||||
props.scale = [scaleX, fudge];
|
||||
scaleY = fudge;
|
||||
break;
|
||||
case 'color':
|
||||
props.color = fudge;
|
||||
break;
|
||||
case 'whirl':
|
||||
props.whirl = fudge;
|
||||
break;
|
||||
case 'fisheye':
|
||||
props.fisheye = fudge;
|
||||
break;
|
||||
case 'pixelate':
|
||||
props.pixelate = fudge;
|
||||
break;
|
||||
case 'mosaic':
|
||||
props.mosaic = fudge;
|
||||
break;
|
||||
case 'brightness':
|
||||
props.brightness = fudge;
|
||||
break;
|
||||
case 'ghost':
|
||||
props.ghost = fudge;
|
||||
break;
|
||||
}
|
||||
renderer.updateDrawableProperties(drawableID2, props);
|
||||
};
|
||||
fudgeInput.addEventListener('input', handleFudgeChanged);
|
||||
fudgeInput.addEventListener('change', handleFudgeChanged);
|
||||
|
||||
canvas.addEventListener('mousemove', event => {
|
||||
var mousePos = getMousePosition(event, canvas);
|
||||
renderer.extractColor(mousePos.x, mousePos.y, 30);
|
||||
});
|
||||
|
||||
canvas.addEventListener('click', event => {
|
||||
var mousePos = getMousePosition(event, canvas);
|
||||
var pickID = renderer.pick(mousePos.x, mousePos.y);
|
||||
console.log('You clicked on ' + (pickID < 0 ? 'nothing' : 'ID# ' + pickID));
|
||||
if (pickID >= 0) {
|
||||
console.dir(renderer.extractDrawable(pickID, mousePos.x, mousePos.y));
|
||||
}
|
||||
});
|
||||
|
||||
const drawStep = function () {
|
||||
renderer.draw();
|
||||
// renderer.getBounds(drawableID2);
|
||||
// renderer.isTouchingColor(drawableID2, [255,255,255]);
|
||||
requestAnimationFrame(drawStep);
|
||||
};
|
||||
drawStep();
|
||||
|
||||
var debugCanvas = /** @type {canvas} */ document.getElementById('debug-canvas');
|
||||
renderer.setDebugCanvas(debugCanvas);
|
||||
74
src/playground/queryPlayground.html
Normal file
74
src/playground/queryPlayground.html
Normal file
@@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Scratch WebGL Query Playground</title>
|
||||
<style>
|
||||
input[type=range][orient=vertical] {
|
||||
writing-mode: bt-lr; /* IE */
|
||||
-webkit-appearance: slider-vertical;
|
||||
width: 1rem;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
canvas {
|
||||
border: 3px dashed black;
|
||||
|
||||
/* https://stackoverflow.com/a/7665647 */
|
||||
image-rendering: optimizeSpeed; /* Older versions of FF */
|
||||
image-rendering: -moz-crisp-edges; /* FF 6.0+ */
|
||||
image-rendering: -webkit-optimize-contrast; /* Safari */
|
||||
image-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */
|
||||
image-rendering: pixelated; /* Awesome future-browsers */
|
||||
-ms-interpolation-mode: nearest-neighbor; /* IE */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="background: lightsteelblue">
|
||||
<div>
|
||||
<fieldset>
|
||||
<legend>Query Canvases</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<fieldset>
|
||||
<legend>GPU</legend>
|
||||
<div>Touching color A? <span id="gpuTouchingA">maybe</span></div>
|
||||
<div>Touching color B? <span id="gpuTouchingB">maybe</span></div>
|
||||
<canvas id="gpuQueryCanvas" width="480" height="360" style="height: 20rem"></canvas>
|
||||
</fieldset>
|
||||
</td>
|
||||
<td>
|
||||
<fieldset>
|
||||
<legend>CPU</legend>
|
||||
<div>Touching color A? <span id="cpuTouchingA">maybe</span></div>
|
||||
<div>Touching color B? <span id="cpuTouchingB">maybe</span></div>
|
||||
<canvas id="cpuQueryCanvas" width="480" height="360" style="height: 20rem"></canvas>
|
||||
</fieldset>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Render Canvas</legend>
|
||||
<div>Cursor Position: <span id="cursorPosition">somewhere</span></div>
|
||||
<table>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<input id="cursorX" type="range" step="0.25" value="0" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<input id="cursorY" type="range" orient="vertical" step="0.25" value="0" />
|
||||
</td>
|
||||
<td>
|
||||
<canvas id="renderCanvas" width="480" height="360" style="border:3px dashed black"></canvas>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
</div>
|
||||
</body>
|
||||
<script src="queryPlayground.js"></script>
|
||||
</html>
|
||||
196
src/playground/queryPlayground.js
Normal file
196
src/playground/queryPlayground.js
Normal file
@@ -0,0 +1,196 @@
|
||||
const ScratchRender = require('../RenderWebGL');
|
||||
const getMousePosition = require('./getMousePosition');
|
||||
|
||||
const renderCanvas = document.getElementById('renderCanvas');
|
||||
const gpuQueryCanvas = document.getElementById('gpuQueryCanvas');
|
||||
const cpuQueryCanvas = document.getElementById('cpuQueryCanvas');
|
||||
const inputCursorX = document.getElementById('cursorX');
|
||||
const inputCursorY = document.getElementById('cursorY');
|
||||
const labelCursorPosition = document.getElementById('cursorPosition');
|
||||
const labelGpuTouchingA = document.getElementById('gpuTouchingA');
|
||||
const labelGpuTouchingB = document.getElementById('gpuTouchingB');
|
||||
const labelCpuTouchingA = document.getElementById('cpuTouchingA');
|
||||
const labelCpuTouchingB = document.getElementById('cpuTouchingB');
|
||||
|
||||
const drawables = {
|
||||
testPattern: -1,
|
||||
cursor: -1
|
||||
};
|
||||
|
||||
const colors = {
|
||||
cursor: [255, 0, 0],
|
||||
patternA: [0, 255, 0],
|
||||
patternB: [0, 0, 255]
|
||||
};
|
||||
|
||||
const renderer = new ScratchRender(renderCanvas);
|
||||
|
||||
const handleResizeRenderCanvas = () => {
|
||||
const halfWidth = renderCanvas.clientWidth / 2;
|
||||
const halfHeight = renderCanvas.clientHeight / 2;
|
||||
|
||||
inputCursorX.style.width = `${renderCanvas.clientWidth}px`;
|
||||
inputCursorY.style.height = `${renderCanvas.clientHeight}px`;
|
||||
inputCursorX.min = -halfWidth;
|
||||
inputCursorX.max = halfWidth;
|
||||
inputCursorY.min = -halfHeight;
|
||||
inputCursorY.max = halfHeight;
|
||||
};
|
||||
renderCanvas.addEventListener('resize', handleResizeRenderCanvas);
|
||||
handleResizeRenderCanvas();
|
||||
|
||||
const handleCursorPositionChanged = () => {
|
||||
const devicePixelRatio = window.devicePixelRatio || 1;
|
||||
const cursorX = inputCursorX.valueAsNumber / devicePixelRatio;
|
||||
const cursorY = inputCursorY.valueAsNumber / devicePixelRatio;
|
||||
const positionHTML = `${cursorX}, ${cursorY}`;
|
||||
labelCursorPosition.innerHTML = positionHTML;
|
||||
if (drawables.cursor >= 0) {
|
||||
renderer.draw();
|
||||
renderer.updateDrawableProperties(drawables.cursor, {
|
||||
position: [cursorX, cursorY]
|
||||
});
|
||||
|
||||
renderer.setUseGpuMode(ScratchRender.UseGpuModes.ForceGPU);
|
||||
renderer.setDebugCanvas(gpuQueryCanvas);
|
||||
const isGpuTouchingA = renderer.isTouchingColor(drawables.cursor, colors.patternA);
|
||||
const isGpuTouchingB = renderer.isTouchingColor(drawables.cursor, colors.patternB);
|
||||
labelGpuTouchingA.innerHTML = isGpuTouchingA ? 'yes' : 'no';
|
||||
labelGpuTouchingB.innerHTML = isGpuTouchingB ? 'yes' : 'no';
|
||||
|
||||
renderer.setUseGpuMode(ScratchRender.UseGpuModes.ForceCPU);
|
||||
renderer.setDebugCanvas(cpuQueryCanvas);
|
||||
const isCpuTouchingA = renderer.isTouchingColor(drawables.cursor, colors.patternA);
|
||||
const isCpuTouchingB = renderer.isTouchingColor(drawables.cursor, colors.patternB);
|
||||
labelCpuTouchingA.innerHTML = isCpuTouchingA ? 'yes' : 'no';
|
||||
labelCpuTouchingB.innerHTML = isCpuTouchingB ? 'yes' : 'no';
|
||||
|
||||
renderer.setUseGpuMode(ScratchRender.UseGpuModes.Automatic);
|
||||
}
|
||||
};
|
||||
inputCursorX.addEventListener('change', handleCursorPositionChanged);
|
||||
inputCursorY.addEventListener('change', handleCursorPositionChanged);
|
||||
inputCursorX.addEventListener('input', handleCursorPositionChanged);
|
||||
inputCursorY.addEventListener('input', handleCursorPositionChanged);
|
||||
handleCursorPositionChanged();
|
||||
|
||||
let trackingMouse = true;
|
||||
const handleMouseMove = event => {
|
||||
if (trackingMouse) {
|
||||
const mousePosition = getMousePosition(event, renderCanvas);
|
||||
inputCursorX.value = mousePosition.x - (renderCanvas.clientWidth / 2);
|
||||
inputCursorY.value = (renderCanvas.clientHeight / 2) - mousePosition.y;
|
||||
handleCursorPositionChanged();
|
||||
}
|
||||
};
|
||||
renderCanvas.addEventListener('mousemove', handleMouseMove);
|
||||
|
||||
renderCanvas.addEventListener('click', event => {
|
||||
trackingMouse = !trackingMouse;
|
||||
if (trackingMouse) {
|
||||
handleMouseMove(event);
|
||||
}
|
||||
});
|
||||
|
||||
const rgb2fillStyle = (rgb) => {
|
||||
return `rgb(${rgb[0]},${rgb[1]},${rgb[2]})`;
|
||||
};
|
||||
|
||||
const makeCursorImage = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = canvas.height = 1;
|
||||
|
||||
const context = canvas.getContext('2d');
|
||||
context.fillStyle = rgb2fillStyle(colors.cursor);
|
||||
context.fillRect(0, 0, 1, 1);
|
||||
|
||||
return canvas;
|
||||
};
|
||||
|
||||
const makeTestPatternImage = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 480;
|
||||
canvas.height = 360;
|
||||
|
||||
const patternA = rgb2fillStyle(colors.patternA);
|
||||
const patternB = rgb2fillStyle(colors.patternB);
|
||||
|
||||
const context = canvas.getContext('2d');
|
||||
context.fillStyle = patternA;
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
context.fillStyle = patternB;
|
||||
const xSplit1 = Math.floor(canvas.width * 0.25);
|
||||
const xSplit2 = Math.floor(canvas.width * 0.5);
|
||||
const xSplit3 = Math.floor(canvas.width * 0.75);
|
||||
const ySplit = Math.floor(canvas.height * 0.5);
|
||||
for (let y = 0; y < ySplit; y += 2) {
|
||||
context.fillRect(0, y, xSplit2, 1);
|
||||
}
|
||||
for (let x = xSplit2; x < canvas.width; x += 2) {
|
||||
context.fillRect(x, 0, 1, ySplit);
|
||||
}
|
||||
for (let x = 0; x < xSplit1; x += 2) {
|
||||
for (let y = ySplit; y < canvas.height; y += 2) {
|
||||
context.fillRect(x, y, 1, 1);
|
||||
}
|
||||
}
|
||||
for (let x = xSplit1; x < xSplit2; x += 3) {
|
||||
for (let y = ySplit; y < canvas.height; y += 3) {
|
||||
context.fillRect(x, y, 2, 2);
|
||||
}
|
||||
}
|
||||
for (let x = xSplit2; x < xSplit3; ++x) {
|
||||
for (let y = ySplit; y < canvas.height; ++y) {
|
||||
context.fillStyle = (x + y) % 2 ? patternB : patternA;
|
||||
context.fillRect(x, y, 1, 1);
|
||||
}
|
||||
}
|
||||
for (let x = xSplit3; x < canvas.width; x += 2) {
|
||||
for (let y = ySplit; y < canvas.height; y += 2) {
|
||||
context.fillStyle = (x + y) % 4 ? patternB : patternA;
|
||||
context.fillRect(x, y, 2, 2);
|
||||
}
|
||||
}
|
||||
|
||||
return canvas;
|
||||
};
|
||||
|
||||
const makeTestPatternDrawable = function (group) {
|
||||
const image = makeTestPatternImage();
|
||||
const skinId = renderer.createBitmapSkin(image, 1);
|
||||
const drawableId = renderer.createDrawable(group);
|
||||
renderer.updateDrawableProperties(drawableId, {skinId});
|
||||
return drawableId;
|
||||
};
|
||||
|
||||
const makeCursorDrawable = function (group) {
|
||||
const image = makeCursorImage();
|
||||
const skinId = renderer.createBitmapSkin(image, 1, [0, 0]);
|
||||
const drawableId = renderer.createDrawable(group);
|
||||
renderer.updateDrawableProperties(drawableId, {skinId});
|
||||
return drawableId;
|
||||
};
|
||||
|
||||
const initRendering = () => {
|
||||
const layerGroup = {
|
||||
testPattern: 'testPattern',
|
||||
cursor: 'cursor'
|
||||
};
|
||||
renderer.setLayerGroupOrdering([layerGroup.testPattern, layerGroup.cursor]);
|
||||
drawables.testPattern = makeTestPatternDrawable(layerGroup.testPattern);
|
||||
drawables.cursor = makeCursorDrawable(layerGroup.cursor);
|
||||
|
||||
const corner00 = makeCursorDrawable(layerGroup.cursor);
|
||||
const corner01 = makeCursorDrawable(layerGroup.cursor);
|
||||
const corner10 = makeCursorDrawable(layerGroup.cursor);
|
||||
const corner11 = makeCursorDrawable(layerGroup.cursor);
|
||||
|
||||
renderer.updateDrawableProperties(corner00, {position: [-240, -179]});
|
||||
renderer.updateDrawableProperties(corner01, {position: [-240, 180]});
|
||||
renderer.updateDrawableProperties(corner10, {position: [239, -179]});
|
||||
renderer.updateDrawableProperties(corner11, {position: [239, 180]});
|
||||
};
|
||||
|
||||
initRendering();
|
||||
renderer.draw();
|
||||
@@ -45,13 +45,13 @@ uniform sampler2D u_skin;
|
||||
|
||||
varying vec2 v_texCoord;
|
||||
|
||||
#if !defined(DRAW_MODE_silhouette) && (defined(ENABLE_color) || defined(ENABLE_brightness))
|
||||
#if !defined(DRAW_MODE_silhouette) && (defined(ENABLE_color))
|
||||
// Branchless color conversions based on code from:
|
||||
// http://www.chilliant.com/rgb2hsv.html by Ian Taylor
|
||||
// Based in part on work by Sam Hocevar and Emil Persson
|
||||
// See also: https://en.wikipedia.org/wiki/HSL_and_HSV#Formal_derivation
|
||||
|
||||
// Smaller values can cause problems with "color" and "brightness" effects on some mobile devices
|
||||
// Smaller values can cause problems on some mobile devices
|
||||
const float epsilon = 1e-3;
|
||||
|
||||
// Convert an RGB color to Hue, Saturation, and Value.
|
||||
@@ -103,7 +103,7 @@ vec3 convertHSV2RGB(vec3 hsv)
|
||||
float c = hsv.z * hsv.y;
|
||||
return rgb * c + hsv.z - c;
|
||||
}
|
||||
#endif // !defined(DRAW_MODE_silhouette) && (defined(ENABLE_color) || defined(ENABLE_brightness))
|
||||
#endif // !defined(DRAW_MODE_silhouette) && (defined(ENABLE_color))
|
||||
|
||||
const vec2 kCenter = vec2(0.5, 0.5);
|
||||
|
||||
@@ -164,31 +164,27 @@ void main()
|
||||
gl_FragColor = u_silhouetteColor;
|
||||
#else // DRAW_MODE_silhouette
|
||||
|
||||
#if defined(ENABLE_color) || defined(ENABLE_brightness)
|
||||
#if defined(ENABLE_color)
|
||||
{
|
||||
vec3 hsv = convertRGB2HSV(gl_FragColor.xyz);
|
||||
|
||||
#ifdef ENABLE_color
|
||||
{
|
||||
// this code forces grayscale values to be slightly saturated
|
||||
// so that some slight change of hue will be visible
|
||||
const float minLightness = 0.11 / 2.0;
|
||||
const float minSaturation = 0.09;
|
||||
if (hsv.z < minLightness) hsv = vec3(0.0, 1.0, minLightness);
|
||||
else if (hsv.y < minSaturation) hsv = vec3(0.0, minSaturation, hsv.z);
|
||||
// this code forces grayscale values to be slightly saturated
|
||||
// so that some slight change of hue will be visible
|
||||
const float minLightness = 0.11 / 2.0;
|
||||
const float minSaturation = 0.09;
|
||||
if (hsv.z < minLightness) hsv = vec3(0.0, 1.0, minLightness);
|
||||
else if (hsv.y < minSaturation) hsv = vec3(0.0, minSaturation, hsv.z);
|
||||
|
||||
hsv.x = mod(hsv.x + u_color, 1.0);
|
||||
if (hsv.x < 0.0) hsv.x += 1.0;
|
||||
}
|
||||
#endif // ENABLE_color
|
||||
|
||||
#ifdef ENABLE_brightness
|
||||
hsv.z = clamp(hsv.z + u_brightness, 0.0, 1.0);
|
||||
#endif // ENABLE_brightness
|
||||
hsv.x = mod(hsv.x + u_color, 1.0);
|
||||
if (hsv.x < 0.0) hsv.x += 1.0;
|
||||
|
||||
gl_FragColor.rgb = convertHSV2RGB(hsv);
|
||||
}
|
||||
#endif // defined(ENABLE_color) || defined(ENABLE_brightness)
|
||||
#endif // defined(ENABLE_color)
|
||||
|
||||
#if defined(ENABLE_brightness)
|
||||
gl_FragColor.rgb = clamp(gl_FragColor.rgb + vec3(u_brightness), vec3(0), vec3(1));
|
||||
#endif // defined(ENABLE_brightness)
|
||||
|
||||
#ifdef DRAW_MODE_colorMask
|
||||
vec3 maskDistance = abs(gl_FragColor.rgb - u_colorMask);
|
||||
|
||||
@@ -114,7 +114,7 @@ const testBubbles = () => test('bubble snapshot', async t => {
|
||||
// immediately invoked async function to let us wait for each test to finish before starting the next.
|
||||
(async () => {
|
||||
const files = fs.readdirSync(testDir())
|
||||
.filter(uri => uri.endsWith('.sb2') || uri.endsWidth('.sb3'));
|
||||
.filter(uri => uri.endsWith('.sb2') || uri.endsWith('.sb3'));
|
||||
|
||||
for (const file of files) {
|
||||
await testFile(file);
|
||||
|
||||
BIN
test/integration/scratch-tests/sprite-goes-off-stage.sb2
Normal file
BIN
test/integration/scratch-tests/sprite-goes-off-stage.sb2
Normal file
Binary file not shown.
@@ -39,10 +39,10 @@ module.exports = [
|
||||
Object.assign({}, base, {
|
||||
target: 'web',
|
||||
entry: {
|
||||
'scratch-render': './src/index.js'
|
||||
playground: './src/playground/playground.js',
|
||||
queryPlayground: './src/playground/queryPlayground.js'
|
||||
},
|
||||
output: {
|
||||
library: 'ScratchRender',
|
||||
libraryTarget: 'umd',
|
||||
path: path.resolve('playground'),
|
||||
filename: '[name].js'
|
||||
@@ -50,7 +50,8 @@ module.exports = [
|
||||
plugins: base.plugins.concat([
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: 'src/playground'
|
||||
context: 'src/playground',
|
||||
from: '*.html'
|
||||
}
|
||||
])
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user