Compare commits
2 Commits
master
...
hotfix/tot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
238228e6f2 | ||
|
|
ec40b654d4 |
11
.travis.yml
11
.travis.yml
@@ -7,6 +7,7 @@ node_js:
|
|||||||
- node
|
- node
|
||||||
env:
|
env:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
|
- NPM_TAG=latest
|
||||||
before_install:
|
before_install:
|
||||||
- google-chrome-stable --headless --no-sandbox --remote-debugging-port=9222 &
|
- google-chrome-stable --headless --no-sandbox --remote-debugging-port=9222 &
|
||||||
install:
|
install:
|
||||||
@@ -30,13 +31,17 @@ jobs:
|
|||||||
- VPKG=$($(npm bin)/json -f package.json version)
|
- VPKG=$($(npm bin)/json -f package.json version)
|
||||||
- export VERSION=${VPKG}-prerelease.$(date +%Y%m%d%H%M%S)
|
- export VERSION=${VPKG}-prerelease.$(date +%Y%m%d%H%M%S)
|
||||||
- npm --no-git-tag-version version $VERSION
|
- npm --no-git-tag-version version $VERSION
|
||||||
|
- if [[ "$TRAVIS_BRANCH" == hotfix/* ]]; then export NPM_TAG=hotfix; fi # double brackets are important for matching the wildcard
|
||||||
- git config --global user.email "$(git log --pretty=format:'%ae' -n1)"
|
- git config --global user.email "$(git log --pretty=format:'%ae' -n1)"
|
||||||
- git config --global user.name "$(git log --pretty=format:'%an' -n1)"
|
- git config --global user.name "$(git log --pretty=format:'%an' -n1)"
|
||||||
deploy:
|
deploy:
|
||||||
provider: npm
|
provider: npm
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
"on":
|
on:
|
||||||
all_branches: true
|
branch:
|
||||||
condition: $RELEASE_BRANCHES =~ $TRAVIS_BRANCH
|
- master
|
||||||
|
- develop
|
||||||
|
- hotfix/*
|
||||||
email: $NPM_EMAIL
|
email: $NPM_EMAIL
|
||||||
api_key: $NPM_TOKEN
|
api_key: $NPM_TOKEN
|
||||||
|
tag: $NPM_TAG
|
||||||
|
|||||||
147
src/PenSkin.js
147
src/PenSkin.js
@@ -44,6 +44,11 @@ const __projectionMatrix = twgl.m4.identity();
|
|||||||
*/
|
*/
|
||||||
const __modelTranslationMatrix = twgl.m4.identity();
|
const __modelTranslationMatrix = twgl.m4.identity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reused memory location for rotation matrix for building a model matrix.
|
||||||
|
* @type {FloatArray}
|
||||||
|
*/
|
||||||
|
const __modelRotationMatrix = twgl.m4.identity();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reused memory location for scaling matrix for building a model matrix.
|
* Reused memory location for scaling matrix for building a model matrix.
|
||||||
@@ -104,12 +109,6 @@ class PenSkin extends Skin {
|
|||||||
/** @type {boolean} */
|
/** @type {boolean} */
|
||||||
this._silhouetteDirty = false;
|
this._silhouetteDirty = false;
|
||||||
|
|
||||||
/** @type {Uint8Array} */
|
|
||||||
this._silhouettePixels = null;
|
|
||||||
|
|
||||||
/** @type {ImageData} */
|
|
||||||
this._silhouetteImageData = null;
|
|
||||||
|
|
||||||
/** @type {object} */
|
/** @type {object} */
|
||||||
this._lineOnBufferDrawRegionId = {
|
this._lineOnBufferDrawRegionId = {
|
||||||
enter: () => this._enterDrawLineOnBuffer(),
|
enter: () => this._enterDrawLineOnBuffer(),
|
||||||
@@ -130,7 +129,7 @@ class PenSkin extends Skin {
|
|||||||
this._stampShader = this._renderer._shaderManager.getShader(ShaderManager.DRAW_MODE.default, NO_EFFECTS);
|
this._stampShader = this._renderer._shaderManager.getShader(ShaderManager.DRAW_MODE.default, NO_EFFECTS);
|
||||||
|
|
||||||
/** @type {twgl.ProgramInfo} */
|
/** @type {twgl.ProgramInfo} */
|
||||||
this._lineShader = this._renderer._shaderManager.getShader(ShaderManager.DRAW_MODE.line, NO_EFFECTS);
|
this._lineShader = this._renderer._shaderManager.getShader(ShaderManager.DRAW_MODE.lineSample, NO_EFFECTS);
|
||||||
|
|
||||||
this._createLineGeometry();
|
this._createLineGeometry();
|
||||||
|
|
||||||
@@ -216,15 +215,10 @@ class PenSkin extends Skin {
|
|||||||
* @param {number} y1 - the Y coordinate of the end of the line.
|
* @param {number} y1 - the Y coordinate of the end of the line.
|
||||||
*/
|
*/
|
||||||
drawLine (penAttributes, x0, y0, x1, y1) {
|
drawLine (penAttributes, x0, y0, x1, y1) {
|
||||||
// For compatibility with Scratch 2.0, offset pen lines of width 1 and 3 so they're pixel-aligned.
|
|
||||||
// See https://github.com/LLK/scratch-render/pull/314
|
|
||||||
const diameter = penAttributes.diameter || DefaultPenAttributes.diameter;
|
|
||||||
const offset = (diameter === 1 || diameter === 3) ? 0.5 : 0;
|
|
||||||
|
|
||||||
this._drawLineOnBuffer(
|
this._drawLineOnBuffer(
|
||||||
penAttributes,
|
penAttributes,
|
||||||
x0 + offset, y0 + offset,
|
this._rotationCenter[0] + x0, this._rotationCenter[1] - y0,
|
||||||
x1 + offset, y1 + offset
|
this._rotationCenter[0] + x1, this._rotationCenter[1] - y1
|
||||||
);
|
);
|
||||||
|
|
||||||
this._silhouetteDirty = true;
|
this._silhouetteDirty = true;
|
||||||
@@ -234,16 +228,72 @@ class PenSkin extends Skin {
|
|||||||
* Create 2D geometry for drawing lines to a framebuffer.
|
* Create 2D geometry for drawing lines to a framebuffer.
|
||||||
*/
|
*/
|
||||||
_createLineGeometry () {
|
_createLineGeometry () {
|
||||||
|
// Create a set of triangulated quads that break up a line into 3 parts:
|
||||||
|
// 2 caps and a body. The y component of these position vertices are
|
||||||
|
// divided to bring a value of 1 down to 0.5 to 0. The large y values
|
||||||
|
// are set so they will still be at least 0.5 after division. The
|
||||||
|
// divisor is scaled based on the length of the line and the lines
|
||||||
|
// width.
|
||||||
|
//
|
||||||
|
// Texture coordinates are based on a "generated" texture whose general
|
||||||
|
// shape is a circle. The line caps set their texture values to define
|
||||||
|
// there roundedness with the texture. The body has all of its texture
|
||||||
|
// values set to the center of the texture so it's a solid block.
|
||||||
const quads = {
|
const quads = {
|
||||||
a_position: {
|
a_position: {
|
||||||
numComponents: 2,
|
numComponents: 2,
|
||||||
data: [
|
data: [
|
||||||
|
-0.5, 1,
|
||||||
|
0.5, 1,
|
||||||
|
-0.5, 100000,
|
||||||
|
|
||||||
|
-0.5, 100000,
|
||||||
|
0.5, 1,
|
||||||
|
0.5, 100000,
|
||||||
|
|
||||||
|
-0.5, 1,
|
||||||
|
0.5, 1,
|
||||||
|
-0.5, -1,
|
||||||
|
|
||||||
|
-0.5, -1,
|
||||||
|
0.5, 1,
|
||||||
|
0.5, -1,
|
||||||
|
|
||||||
|
-0.5, -100000,
|
||||||
|
0.5, -100000,
|
||||||
|
-0.5, -1,
|
||||||
|
|
||||||
|
-0.5, -1,
|
||||||
|
0.5, -100000,
|
||||||
|
0.5, -1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
a_texCoord: {
|
||||||
|
numComponents: 2,
|
||||||
|
data: [
|
||||||
|
1, 0.5,
|
||||||
|
0, 0.5,
|
||||||
|
1, 0,
|
||||||
|
|
||||||
|
1, 0,
|
||||||
|
0, 0.5,
|
||||||
|
0, 0,
|
||||||
|
|
||||||
|
0.5, 0,
|
||||||
|
0.5, 1,
|
||||||
|
0.5, 0,
|
||||||
|
|
||||||
|
0.5, 0,
|
||||||
|
0.5, 1,
|
||||||
|
0.5, 1,
|
||||||
|
|
||||||
1, 0,
|
1, 0,
|
||||||
0, 0,
|
0, 0,
|
||||||
1, 1,
|
1, 0.5,
|
||||||
1, 1,
|
|
||||||
|
1, 0.5,
|
||||||
0, 0,
|
0, 0,
|
||||||
0, 1
|
0, 0.5
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -288,8 +338,6 @@ class PenSkin extends Skin {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw a line on the framebuffer.
|
* Draw a line on the framebuffer.
|
||||||
* Note that the point coordinates are in the following coordinate space:
|
|
||||||
* +y is down, (0, 0) is the center, and the coords range from (-width / 2, -height / 2) to (height / 2, width / 2).
|
|
||||||
* @param {PenAttributes} penAttributes - how the line should be drawn.
|
* @param {PenAttributes} penAttributes - how the line should be drawn.
|
||||||
* @param {number} x0 - the X coordinate of the beginning of the line.
|
* @param {number} x0 - the X coordinate of the beginning of the line.
|
||||||
* @param {number} y0 - the Y coordinate of the beginning of the line.
|
* @param {number} y0 - the Y coordinate of the beginning of the line.
|
||||||
@@ -303,6 +351,26 @@ class PenSkin extends Skin {
|
|||||||
|
|
||||||
this._renderer.enterDrawRegion(this._lineOnBufferDrawRegionId);
|
this._renderer.enterDrawRegion(this._lineOnBufferDrawRegionId);
|
||||||
|
|
||||||
|
const diameter = penAttributes.diameter || DefaultPenAttributes.diameter;
|
||||||
|
const length = Math.hypot(Math.abs(x1 - x0) - 0.001, Math.abs(y1 - y0) - 0.001);
|
||||||
|
const avgX = (x0 + x1) / 2;
|
||||||
|
const avgY = (y0 + y1) / 2;
|
||||||
|
const theta = Math.atan2(y0 - y1, x0 - x1);
|
||||||
|
const alias = 1;
|
||||||
|
|
||||||
|
// The line needs a bit of aliasing to look smooth. Add a small offset
|
||||||
|
// and a small size boost to scaling to give a section to alias.
|
||||||
|
const translationVector = __modelTranslationVector;
|
||||||
|
translationVector[0] = avgX - (alias / 2);
|
||||||
|
translationVector[1] = avgY + (alias / 4);
|
||||||
|
|
||||||
|
const scalingVector = __modelScalingVector;
|
||||||
|
scalingVector[0] = diameter + alias;
|
||||||
|
scalingVector[1] = length + diameter - (alias / 2);
|
||||||
|
|
||||||
|
const radius = diameter / 2;
|
||||||
|
const yScalar = (0.50001 - (radius / (length + diameter)));
|
||||||
|
|
||||||
// Premultiply pen color by pen transparency
|
// Premultiply pen color by pen transparency
|
||||||
const penColor = penAttributes.color4f || DefaultPenAttributes.color4f;
|
const penColor = penAttributes.color4f || DefaultPenAttributes.color4f;
|
||||||
__premultipliedColor[0] = penColor[0] * penColor[3];
|
__premultipliedColor[0] = penColor[0] * penColor[3];
|
||||||
@@ -311,10 +379,19 @@ class PenSkin extends Skin {
|
|||||||
__premultipliedColor[3] = penColor[3];
|
__premultipliedColor[3] = penColor[3];
|
||||||
|
|
||||||
const uniforms = {
|
const uniforms = {
|
||||||
u_lineColor: __premultipliedColor,
|
u_positionScalar: yScalar,
|
||||||
u_lineThickness: penAttributes.diameter || DefaultPenAttributes.diameter,
|
u_capScale: diameter,
|
||||||
u_penPoints: [x0, -y0, x1, -y1],
|
u_aliasAmount: alias,
|
||||||
u_stageSize: this.size
|
u_modelMatrix: twgl.m4.multiply(
|
||||||
|
twgl.m4.multiply(
|
||||||
|
twgl.m4.translation(translationVector, __modelTranslationMatrix),
|
||||||
|
twgl.m4.rotationZ(theta - (Math.PI / 2), __modelRotationMatrix),
|
||||||
|
__modelMatrix
|
||||||
|
),
|
||||||
|
twgl.m4.scaling(scalingVector, __modelScalingMatrix),
|
||||||
|
__modelMatrix
|
||||||
|
),
|
||||||
|
u_lineColor: __premultipliedColor
|
||||||
};
|
};
|
||||||
|
|
||||||
twgl.setUniforms(currentShader, uniforms);
|
twgl.setUniforms(currentShader, uniforms);
|
||||||
@@ -520,9 +597,6 @@ class PenSkin extends Skin {
|
|||||||
gl.clearColor(0, 0, 0, 0);
|
gl.clearColor(0, 0, 0, 0);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
this._silhouettePixels = new Uint8Array(Math.floor(width * height * 4));
|
|
||||||
this._silhouetteImageData = this._canvas.getContext('2d').createImageData(width, height);
|
|
||||||
|
|
||||||
this._silhouetteDirty = true;
|
this._silhouetteDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,17 +634,24 @@ class PenSkin extends Skin {
|
|||||||
// Render export texture to another framebuffer
|
// Render export texture to another framebuffer
|
||||||
const gl = this._renderer.gl;
|
const gl = this._renderer.gl;
|
||||||
|
|
||||||
|
const bounds = this._bounds;
|
||||||
|
|
||||||
this._renderer.enterDrawRegion(this._toBufferDrawRegionId);
|
this._renderer.enterDrawRegion(this._toBufferDrawRegionId);
|
||||||
|
|
||||||
// Sample the framebuffer's pixels into the silhouette instance
|
// Sample the framebuffer's pixels into the silhouette instance
|
||||||
gl.readPixels(
|
const skinPixels = new Uint8Array(Math.floor(this._canvas.width * this._canvas.height * 4));
|
||||||
0, 0,
|
gl.readPixels(0, 0, this._canvas.width, this._canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, skinPixels);
|
||||||
this._canvas.width, this._canvas.height,
|
|
||||||
gl.RGBA, gl.UNSIGNED_BYTE, this._silhouettePixels
|
|
||||||
);
|
|
||||||
|
|
||||||
this._silhouetteImageData.data.set(this._silhouettePixels);
|
const skinCanvas = this._canvas;
|
||||||
this._silhouette.update(this._silhouetteImageData, true /* isPremultiplied */);
|
skinCanvas.width = bounds.width;
|
||||||
|
skinCanvas.height = bounds.height;
|
||||||
|
|
||||||
|
const skinContext = skinCanvas.getContext('2d');
|
||||||
|
const skinImageData = skinContext.createImageData(bounds.width, bounds.height);
|
||||||
|
skinImageData.data.set(skinPixels);
|
||||||
|
skinContext.putImageData(skinImageData, 0, 0);
|
||||||
|
|
||||||
|
this._silhouette.update(this._canvas, true /* isPremultiplied */);
|
||||||
|
|
||||||
this._silhouetteDirty = false;
|
this._silhouetteDirty = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,6 +133,13 @@ class RenderWebGL extends EventEmitter {
|
|||||||
throw new Error('Could not get WebGL context: this browser or environment may not support WebGL.');
|
throw new Error('Could not get WebGL context: this browser or environment may not support WebGL.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._mystery = {
|
||||||
|
modeActive: false,
|
||||||
|
mouseMoveListener: null,
|
||||||
|
bufferInfo: null,
|
||||||
|
mouseCoords: [0, 0]
|
||||||
|
};
|
||||||
|
|
||||||
/** @type {RenderWebGL.UseGpuModes} */
|
/** @type {RenderWebGL.UseGpuModes} */
|
||||||
this._useGpuMode = RenderWebGL.UseGpuModes.Automatic;
|
this._useGpuMode = RenderWebGL.UseGpuModes.Automatic;
|
||||||
|
|
||||||
@@ -220,20 +227,15 @@ class RenderWebGL extends EventEmitter {
|
|||||||
* @param {int} pixelsTall The desired height in device-independent pixels.
|
* @param {int} pixelsTall The desired height in device-independent pixels.
|
||||||
*/
|
*/
|
||||||
resize (pixelsWide, pixelsTall) {
|
resize (pixelsWide, pixelsTall) {
|
||||||
const {canvas} = this._gl;
|
|
||||||
const pixelRatio = window.devicePixelRatio || 1;
|
const pixelRatio = window.devicePixelRatio || 1;
|
||||||
const newWidth = pixelsWide * pixelRatio;
|
this._gl.canvas.width = pixelsWide * pixelRatio;
|
||||||
const newHeight = pixelsTall * pixelRatio;
|
this._gl.canvas.height = pixelsTall * pixelRatio;
|
||||||
|
|
||||||
// Certain operations, such as moving the color picker, call `resize` once per frame, even though the canvas
|
if (this._mystery.modeActive && this._mystery.bufferInfo) {
|
||||||
// size doesn't change. To avoid unnecessary canvas updates, check that we *really* need to resize the canvas.
|
twgl.resizeFramebufferInfo(this._gl, this._mystery.bufferInfo, [
|
||||||
if (canvas.width !== newWidth || canvas.height !== newHeight) {
|
{format: this._gl.RGBA}
|
||||||
canvas.width = newWidth;
|
], pixelsWide * pixelRatio, pixelsTall * pixelRatio);
|
||||||
canvas.height = newHeight;
|
|
||||||
// Resizing the canvas causes it to be cleared, so redraw it.
|
|
||||||
this.draw();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -626,7 +628,43 @@ class RenderWebGL extends EventEmitter {
|
|||||||
gl.clearColor.apply(gl, this._backgroundColor);
|
gl.clearColor.apply(gl, this._backgroundColor);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection);
|
let drawList = this._drawList;
|
||||||
|
if (this._mystery.modeActive) {
|
||||||
|
drawList = drawList.slice(1);
|
||||||
|
twgl.bindFramebufferInfo(gl, this._mystery.bufferInfo);
|
||||||
|
gl.clearColor(0, 0, 0, 0);
|
||||||
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
this._drawThese(drawList, ShaderManager.DRAW_MODE.default, this._projection);
|
||||||
|
|
||||||
|
if (this._mystery.modeActive) {
|
||||||
|
// draw all layers except for the bottom layer onto the mystery buffer
|
||||||
|
twgl.bindFramebufferInfo(gl, null);
|
||||||
|
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
||||||
|
this._drawThese([this._drawList[0]], ShaderManager.DRAW_MODE.default, this._projection);
|
||||||
|
|
||||||
|
this._doExitDrawRegion();
|
||||||
|
const newShader = this._shaderManager.getShader(ShaderManager.DRAW_MODE.mystery, 0);
|
||||||
|
this._regionId = newShader;
|
||||||
|
|
||||||
|
// draw mystery buffer to main buffer
|
||||||
|
gl.useProgram(newShader.program);
|
||||||
|
twgl.setBuffersAndAttributes(gl, newShader, this._bufferInfo);
|
||||||
|
const uniforms = {
|
||||||
|
u_projectionMatrix: this._projection,
|
||||||
|
u_modelMatrix: twgl.m4.identity(),
|
||||||
|
u_skin: this._mystery.bufferInfo.attachments[0],
|
||||||
|
u_mousePosition: this._mystery.mouseCoords
|
||||||
|
};
|
||||||
|
|
||||||
|
twgl.setTextureParameters(
|
||||||
|
gl, uniforms.u_skin, {minMag: gl.LINEAR}
|
||||||
|
);
|
||||||
|
|
||||||
|
twgl.setUniforms(newShader, uniforms);
|
||||||
|
twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._snapshotCallbacks.length > 0) {
|
if (this._snapshotCallbacks.length > 0) {
|
||||||
const snapshot = gl.canvas.toDataURL();
|
const snapshot = gl.canvas.toDataURL();
|
||||||
this._snapshotCallbacks.forEach(cb => cb(snapshot));
|
this._snapshotCallbacks.forEach(cb => cb(snapshot));
|
||||||
@@ -1135,8 +1173,7 @@ class RenderWebGL extends EventEmitter {
|
|||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
try {
|
try {
|
||||||
gl.disable(gl.BLEND);
|
gl.disable(gl.BLEND);
|
||||||
// ImageData objects store alpha un-premultiplied, so draw with the `straightAlpha` draw mode.
|
this._drawThese([drawableID], ShaderManager.DRAW_MODE.default, projection,
|
||||||
this._drawThese([drawableID], ShaderManager.DRAW_MODE.straightAlpha, projection,
|
|
||||||
{effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask});
|
{effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask});
|
||||||
} finally {
|
} finally {
|
||||||
gl.enable(gl.BLEND);
|
gl.enable(gl.BLEND);
|
||||||
@@ -1773,10 +1810,6 @@ class RenderWebGL extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
_getConvexHullPointsForDrawable (drawableID) {
|
_getConvexHullPointsForDrawable (drawableID) {
|
||||||
const drawable = this._allDrawables[drawableID];
|
const drawable = this._allDrawables[drawableID];
|
||||||
|
|
||||||
drawable.updateMatrix();
|
|
||||||
drawable.skin.updateSilhouette(this._getDrawableScreenSpaceScale(drawable));
|
|
||||||
|
|
||||||
const [width, height] = drawable.skin.size;
|
const [width, height] = drawable.skin.size;
|
||||||
// No points in the hull if invisible or size is 0.
|
// No points in the hull if invisible or size is 0.
|
||||||
if (!drawable.getVisible() || width === 0 || height === 0) {
|
if (!drawable.getVisible() || width === 0 || height === 0) {
|
||||||
@@ -1925,6 +1958,32 @@ class RenderWebGL extends EventEmitter {
|
|||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setMysteryMode (enableMysteryMode) {
|
||||||
|
this._mystery.modeActive = enableMysteryMode;
|
||||||
|
|
||||||
|
if (enableMysteryMode) {
|
||||||
|
this._mystery.bufferInfo = twgl.createFramebufferInfo(
|
||||||
|
this._gl,
|
||||||
|
[{format: this._gl.RGBA}],
|
||||||
|
this._gl.drawingBufferWidth,
|
||||||
|
this._gl.drawingBufferHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
this._mystery.mouseMoveListener = event => {
|
||||||
|
const rect = this.canvas.getBoundingClientRect();
|
||||||
|
this._mystery.mouseCoords[0] = (event.clientX - rect.left) / rect.width;
|
||||||
|
this._mystery.mouseCoords[1] = (event.clientY - rect.top) / rect.height;
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', this._mystery.mouseMoveListener);
|
||||||
|
} else {
|
||||||
|
if (this._mystery.bufferInfo) {
|
||||||
|
this._gl.deleteFramebuffer(this._mystery.bufferInfo.framebuffer);
|
||||||
|
}
|
||||||
|
document.removeEventListener('mousemove', this._mystery.mouseMoveListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @callback RenderWebGL#snapshotCallback
|
* @callback RenderWebGL#snapshotCallback
|
||||||
* @param {string} dataURI Data URI of the snapshot of the renderer
|
* @param {string} dataURI Data URI of the snapshot of the renderer
|
||||||
|
|||||||
@@ -154,15 +154,10 @@ ShaderManager.EFFECTS = Object.keys(ShaderManager.EFFECT_INFO);
|
|||||||
*/
|
*/
|
||||||
ShaderManager.DRAW_MODE = {
|
ShaderManager.DRAW_MODE = {
|
||||||
/**
|
/**
|
||||||
* Draw normally. Its output will use premultiplied alpha.
|
* Draw normally.
|
||||||
*/
|
*/
|
||||||
default: 'default',
|
default: 'default',
|
||||||
|
|
||||||
/**
|
|
||||||
* Draw with non-premultiplied alpha. Useful for reading pixels from GL into an ImageData object.
|
|
||||||
*/
|
|
||||||
straightAlpha: 'straightAlpha',
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw a silhouette using a solid color.
|
* Draw a silhouette using a solid color.
|
||||||
*/
|
*/
|
||||||
@@ -174,9 +169,11 @@ ShaderManager.DRAW_MODE = {
|
|||||||
colorMask: 'colorMask',
|
colorMask: 'colorMask',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw a line with caps.
|
* Sample a "texture" to draw a line with caps.
|
||||||
*/
|
*/
|
||||||
line: 'line'
|
lineSample: 'lineSample',
|
||||||
|
|
||||||
|
mystery: 'mystery'
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = ShaderManager;
|
module.exports = ShaderManager;
|
||||||
|
|||||||
@@ -33,11 +33,15 @@ uniform float u_mosaic;
|
|||||||
uniform float u_ghost;
|
uniform float u_ghost;
|
||||||
#endif // ENABLE_ghost
|
#endif // ENABLE_ghost
|
||||||
|
|
||||||
#ifdef DRAW_MODE_line
|
#ifdef DRAW_MODE_lineSample
|
||||||
uniform vec4 u_lineColor;
|
uniform vec4 u_lineColor;
|
||||||
uniform float u_lineThickness;
|
uniform float u_capScale;
|
||||||
uniform vec4 u_penPoints;
|
uniform float u_aliasAmount;
|
||||||
#endif // DRAW_MODE_line
|
#endif // DRAW_MODE_lineSample
|
||||||
|
|
||||||
|
#ifdef DRAW_MODE_mystery
|
||||||
|
uniform vec2 u_mousePosition;
|
||||||
|
#endif
|
||||||
|
|
||||||
uniform sampler2D u_skin;
|
uniform sampler2D u_skin;
|
||||||
|
|
||||||
@@ -109,9 +113,18 @@ const vec2 kCenter = vec2(0.5, 0.5);
|
|||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
#ifndef DRAW_MODE_line
|
#ifndef DRAW_MODE_lineSample
|
||||||
vec2 texcoord0 = v_texCoord;
|
vec2 texcoord0 = v_texCoord;
|
||||||
|
|
||||||
|
#ifdef DRAW_MODE_mystery
|
||||||
|
vec2 mysteryCoord = texcoord0;
|
||||||
|
vec2 offset = vec2(u_mousePosition.x, 1.0 - u_mousePosition.y);
|
||||||
|
mysteryCoord -= offset;
|
||||||
|
const float SCALE_FACTOR = 0.85;
|
||||||
|
mysteryCoord *= vec2(SCALE_FACTOR, SCALE_FACTOR);
|
||||||
|
mysteryCoord += offset;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_mosaic
|
#ifdef ENABLE_mosaic
|
||||||
texcoord0 = fract(u_mosaic * texcoord0);
|
texcoord0 = fract(u_mosaic * texcoord0);
|
||||||
#endif // ENABLE_mosaic
|
#endif // ENABLE_mosaic
|
||||||
@@ -155,6 +168,21 @@ void main()
|
|||||||
|
|
||||||
gl_FragColor = texture2D(u_skin, texcoord0);
|
gl_FragColor = texture2D(u_skin, texcoord0);
|
||||||
|
|
||||||
|
#ifdef DRAW_MODE_mystery
|
||||||
|
const vec4 SHADOW_COLOR = vec4(0.0, 0.0, 0.0, 0.5);
|
||||||
|
const float SHADOW_BLUR = 0.0025;
|
||||||
|
|
||||||
|
float shadowSample1 = texture2D(u_skin, mysteryCoord + vec2(SHADOW_BLUR, SHADOW_BLUR)).a;
|
||||||
|
float shadowSample2 = texture2D(u_skin, mysteryCoord + vec2(-SHADOW_BLUR, SHADOW_BLUR)).a;
|
||||||
|
float shadowSample3 = texture2D(u_skin, mysteryCoord + vec2(SHADOW_BLUR, -SHADOW_BLUR)).a;
|
||||||
|
float shadowSample4 = texture2D(u_skin, mysteryCoord + vec2(-SHADOW_BLUR, -SHADOW_BLUR)).a;
|
||||||
|
|
||||||
|
float shadowAlpha = (shadowSample1 + shadowSample2 + shadowSample3 + shadowSample4) * 0.25;
|
||||||
|
|
||||||
|
vec4 shadow = SHADOW_COLOR * shadowAlpha;
|
||||||
|
gl_FragColor = gl_FragColor + (shadow * (1.0 - gl_FragColor.a));
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(ENABLE_color) || defined(ENABLE_brightness)
|
#if defined(ENABLE_color) || defined(ENABLE_brightness)
|
||||||
// Divide premultiplied alpha values for proper color processing
|
// Divide premultiplied alpha values for proper color processing
|
||||||
// Add epsilon to avoid dividing by 0 for fully transparent pixels
|
// Add epsilon to avoid dividing by 0 for fully transparent pixels
|
||||||
@@ -210,31 +238,14 @@ void main()
|
|||||||
#endif // DRAW_MODE_colorMask
|
#endif // DRAW_MODE_colorMask
|
||||||
#endif // DRAW_MODE_silhouette
|
#endif // DRAW_MODE_silhouette
|
||||||
|
|
||||||
#ifdef DRAW_MODE_straightAlpha
|
#else // DRAW_MODE_lineSample
|
||||||
// Un-premultiply alpha.
|
gl_FragColor = u_lineColor * clamp(
|
||||||
gl_FragColor.rgb /= gl_FragColor.a + epsilon;
|
// Scale the capScale a little to have an aliased region.
|
||||||
#endif
|
(u_capScale + u_aliasAmount -
|
||||||
|
u_capScale * 2.0 * distance(v_texCoord, vec2(0.5, 0.5))
|
||||||
#else // DRAW_MODE_line
|
) / (u_aliasAmount + 1.0),
|
||||||
// Maaaaagic antialiased-line-with-round-caps shader.
|
0.0,
|
||||||
// Adapted from Inigo Quilez' 2D distance function cheat sheet
|
1.0
|
||||||
// https://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
|
);
|
||||||
|
#endif // DRAW_MODE_lineSample
|
||||||
// The xy component of u_penPoints is the first point; the zw is the second point.
|
|
||||||
// This is done to minimize the number of gl.uniform calls, which can add up.
|
|
||||||
vec2 pa = v_texCoord - u_penPoints.xy, ba = u_penPoints.zw - u_penPoints.xy;
|
|
||||||
// Magnitude of vector projection of this fragment onto the line (both relative to the line's start point).
|
|
||||||
// This results in a "linear gradient" which goes from 0.0 at the start point to 1.0 at the end point.
|
|
||||||
float projMagnitude = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
|
||||||
|
|
||||||
float lineDistance = length(pa - (ba * projMagnitude));
|
|
||||||
|
|
||||||
// The distance to the line allows us to create lines of any thickness.
|
|
||||||
// Instead of checking whether this fragment's distance < the line thickness,
|
|
||||||
// utilize the distance field to get some antialiasing. Fragments far away from the line are 0,
|
|
||||||
// fragments close to the line are 1, and fragments that are within a 1-pixel border of the line are in between.
|
|
||||||
float cappedLine = clamp((u_lineThickness + 1.0) * 0.5 - lineDistance, 0.0, 1.0);
|
|
||||||
|
|
||||||
gl_FragColor = u_lineColor * cappedLine;
|
|
||||||
#endif // DRAW_MODE_line
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,24 @@
|
|||||||
precision mediump float;
|
|
||||||
|
|
||||||
#ifdef DRAW_MODE_line
|
|
||||||
uniform vec2 u_stageSize;
|
|
||||||
uniform float u_lineThickness;
|
|
||||||
uniform vec4 u_penPoints;
|
|
||||||
|
|
||||||
// Add this to divisors to prevent division by 0, which results in NaNs propagating through calculations.
|
|
||||||
// Smaller values can cause problems on some mobile devices.
|
|
||||||
const float epsilon = 1e-3;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef DRAW_MODE_line
|
|
||||||
uniform mat4 u_projectionMatrix;
|
uniform mat4 u_projectionMatrix;
|
||||||
uniform mat4 u_modelMatrix;
|
uniform mat4 u_modelMatrix;
|
||||||
attribute vec2 a_texCoord;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
attribute vec2 a_position;
|
attribute vec2 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
|
||||||
varying vec2 v_texCoord;
|
varying vec2 v_texCoord;
|
||||||
|
|
||||||
|
#ifdef DRAW_MODE_lineSample
|
||||||
|
uniform float u_positionScalar;
|
||||||
|
#endif
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
#ifdef DRAW_MODE_line
|
#ifdef DRAW_MODE_lineSample
|
||||||
// Calculate a rotated ("tight") bounding box around the two pen points.
|
vec2 position = a_position;
|
||||||
// Yes, we're doing this 6 times (once per vertex), but on actual GPU hardware,
|
position.y = clamp(position.y * u_positionScalar, -0.5, 0.5);
|
||||||
// it's still faster than doing it in JS combined with the cost of uniformMatrix4fv.
|
gl_Position = u_projectionMatrix * u_modelMatrix * vec4(position, 0, 1);
|
||||||
|
#elif defined(DRAW_MODE_mystery)
|
||||||
// Expand line bounds by sqrt(2) / 2 each side-- this ensures that all antialiased pixels
|
gl_Position = vec4(a_position * vec2(-2.0, 2.0), 0.0, 1.0);
|
||||||
// fall within the quad, even at a 45-degree diagonal
|
|
||||||
vec2 position = a_position;
|
|
||||||
float expandedRadius = (u_lineThickness * 0.5) + 1.4142135623730951;
|
|
||||||
|
|
||||||
float lineLength = length(u_penPoints.zw - u_penPoints.xy);
|
|
||||||
|
|
||||||
position.x *= lineLength + (2.0 * expandedRadius);
|
|
||||||
position.y *= 2.0 * expandedRadius;
|
|
||||||
|
|
||||||
// Center around first pen point
|
|
||||||
position -= expandedRadius;
|
|
||||||
|
|
||||||
// Rotate quad to line angle
|
|
||||||
vec2 normalized = (u_penPoints.zw - u_penPoints.xy + epsilon) / (lineLength + epsilon);
|
|
||||||
position = mat2(normalized.x, normalized.y, -normalized.y, normalized.x) * position;
|
|
||||||
// Translate quad
|
|
||||||
position += u_penPoints.xy;
|
|
||||||
|
|
||||||
// Apply view transform
|
|
||||||
position *= 2.0 / u_stageSize;
|
|
||||||
|
|
||||||
gl_Position = vec4(position, 0, 1);
|
|
||||||
v_texCoord = position * 0.5 * u_stageSize;
|
|
||||||
#else
|
#else
|
||||||
gl_Position = u_projectionMatrix * u_modelMatrix * vec4(a_position, 0, 1);
|
gl_Position = u_projectionMatrix * u_modelMatrix * vec4(a_position, 0, 1);
|
||||||
v_texCoord = a_texCoord;
|
#endif
|
||||||
#endif
|
v_texCoord = a_texCoord;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user