Skip to content

Emulate silhouette rendering for pick and _getConvexHullPointsForDrawable #196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/BitmapSkin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const twgl = require('twgl.js');

const Skin = require('./Skin');
const Silhouette = require('./Silhouette');

class BitmapSkin extends Skin {
/**
Expand All @@ -23,6 +24,8 @@ class BitmapSkin extends Skin {

/** @type {Array<int>} */
this._textureSize = [0, 0];

this._silhouette = new Silhouette();
}

/**
Expand Down Expand Up @@ -66,6 +69,7 @@ class BitmapSkin extends Skin {
if (this._texture) {
gl.bindTexture(gl.TEXTURE_2D, this._texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmapData);
this._silhouette.update(bitmapData);
} else {
const textureOptions = {
auto: true,
Expand All @@ -77,6 +81,7 @@ class BitmapSkin extends Skin {
};

this._texture = twgl.createTexture(gl, textureOptions);
this._silhouette.update(bitmapData);
}

// Do these last in case any of the above throws an exception
Expand Down Expand Up @@ -106,6 +111,15 @@ class BitmapSkin extends Skin {
// ImageData or HTMLCanvasElement
return [bitmapData.width, bitmapData.height];
}

/**
* Does this point touch an opaque or translucent point on this skin?
* @param {twgl.v3} vec A texture coordinate.
* @return {boolean} Did it touch?
*/
isTouching (vec) {
return this._silhouette.isTouching(vec);
}
}

module.exports = BitmapSkin;
49 changes: 49 additions & 0 deletions src/Drawable.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ const Rectangle = require('./Rectangle');
const RenderConstants = require('./RenderConstants');
const ShaderManager = require('./ShaderManager');
const Skin = require('./Skin');
const EffectTransform = require('./EffectTransform');

const __isTouchingPosition = twgl.v3.create();

class Drawable {
/**
Expand Down Expand Up @@ -49,6 +51,8 @@ class Drawable {
this._scale = twgl.v3.create(100, 100);
this._direction = 90;
this._transformDirty = true;
this._inverseMatrix = twgl.m4.create();
this._inverseTransformDirty = true;
this._visible = true;
this._effectBits = 0;

Expand All @@ -73,6 +77,7 @@ class Drawable {
*/
setTransformDirty () {
this._transformDirty = true;
this._inverseTransformDirty = true;
}

/**
Expand Down Expand Up @@ -241,6 +246,50 @@ class Drawable {
this._convexHullDirty = false;
}

/**
* Check if the world position touches the skin.
* @param {twgl.v3} vec World coordinate vector.
* @return {boolean} True if the world position touches the skin.
*/
isTouching (vec) {
if (!this.skin) {
return false;
}

if (this._transformDirty) {
this._calculateTransform();
}

// Get the inverse of the model matrix or update it.
const inverse = this._inverseMatrix;
if (this._inverseTransformDirty) {
const model = twgl.m4.copy(this._uniforms.u_modelMatrix, inverse);
// The normal matrix uses a z scaling of 0 causing model[10] to be
// 0. Getting a 4x4 inverse is impossible without a scaling in x, y,
// and z.
model[10] = 1;
twgl.m4.inverse(model, model);
this._inverseTransformDirty = false;
}

// Transfrom from world coordinates to Drawable coordinates.
const localPosition = twgl.m4.transformPoint(inverse, vec, __isTouchingPosition);

// Transform into texture coordinates. 0, 0 is the bottom left. 1, 1 is
// the top right.
localPosition[0] += 0.5;
localPosition[1] += 0.5;
// The RenderWebGL quad flips the texture's X axis. So rendered bottom
// left is 1, 0 and the top right is 0, 1. Flip the X axis so
// localPosition matches that transformation.
localPosition[0] = 1 - localPosition[0];

// Apply texture effect transform.
EffectTransform.transformPoint(this, localPosition, localPosition);

return this.skin.isTouching(localPosition);
}

/**
* Get the precise bounds for a Drawable.
* This function applies the transform matrix to the known convex hull,
Expand Down
102 changes: 102 additions & 0 deletions src/EffectTransform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* @fileoverview
* A utility to transform a texture coordinate to another texture coordinate
* representing how the shaders apply effects.
*/

const twgl = require('twgl.js');

const ShaderManager = require('./ShaderManager');

/**
* A texture coordinate is between 0 and 1. 0.5 is the center position.
* @const {number}
*/
const CENTER_X = 0.5;

/**
* A texture coordinate is between 0 and 1. 0.5 is the center position.
* @const {number}
*/
const CENTER_Y = 0.5;

class EffectTransform {
/**
* Transform a texture coordinate to one that would be select after applying shader effects.
* @param {Drawable} drawable The drawable whose effects to emulate.
* @param {twgl.v3} vec The texture coordinate to transform.
* @param {?twgl.v3} dst A place to store the output coordinate.
* @return {twgl.v3} The coordinate after being transform by effects.
*/
static transformPoint (drawable, vec, dst) {

This comment was marked as abuse.

This comment was marked as abuse.

dst = dst || twgl.v3.create();
twgl.v3.copy(vec, dst);

const uniforms = drawable.getUniforms();
const effects = drawable.getEnabledEffects();

if ((effects & ShaderManager.EFFECT_INFO.mosaic.mask) !== 0) {
// texcoord0 = fract(u_mosaic * texcoord0);
dst[0] = uniforms.u_mosaic * dst[0] % 1;
dst[1] = uniforms.u_mosaic * dst[1] % 1;
}
if ((effects & ShaderManager.EFFECT_INFO.pixelate.mask) !== 0) {
const skinUniforms = drawable.skin.getUniforms();
// vec2 pixelTexelSize = u_skinSize / u_pixelate;
const texelX = skinUniforms.u_skinSize[0] * uniforms.u_pixelate;
const texelY = skinUniforms.u_skinSize[1] * uniforms.u_pixelate;
// texcoord0 = (floor(texcoord0 * pixelTexelSize) + kCenter) /
// pixelTexelSize;
dst[0] = (Math.floor(dst[0] * texelX) + CENTER_X) / texelX;
dst[1] = (Math.floor(dst[1] * texelY) + CENTER_Y) / texelY;
}
if ((effects & ShaderManager.EFFECT_INFO.whirl.mask) !== 0) {
// const float kRadius = 0.5;
const RADIUS = 0.5;
// vec2 offset = texcoord0 - kCenter;
const offsetX = dst[0] - CENTER_X;
const offsetY = dst[1] - CENTER_Y;
// float offsetMagnitude = length(offset);
const offsetMagnitude = twgl.v3.length(dst);
// float whirlFactor = max(1.0 - (offsetMagnitude / kRadius), 0.0);
const whirlFactor = Math.max(1.0 - (offsetMagnitude / RADIUS), 0.0);
// float whirlActual = u_whirl * whirlFactor * whirlFactor;
const whirlActual = uniforms.u_whirl * whirlFactor * whirlFactor;
// float sinWhirl = sin(whirlActual);
const sinWhirl = Math.sin(whirlActual);
// float cosWhirl = cos(whirlActual);
const cosWhirl = Math.cos(whirlActual);
// mat2 rotationMatrix = mat2(
// cosWhirl, -sinWhirl,
// sinWhirl, cosWhirl
// );
const rot00 = cosWhirl;
const rot10 = -sinWhirl;
const rot01 = sinWhirl;
const rot11 = cosWhirl;

// texcoord0 = rotationMatrix * offset + kCenter;
dst[0] = (rot00 * offsetX) + (rot10 * offsetY) + CENTER_X;
dst[1] = (rot01 * offsetX) + (rot11 * offsetY) + CENTER_Y;
}
if ((effects & ShaderManager.EFFECT_INFO.fisheye.mask) !== 0) {
// vec2 vec = (texcoord0 - kCenter) / kCenter;
const vX = (dst[0] - CENTER_X) / CENTER_X;
const vY = (dst[1] - CENTER_Y) / CENTER_Y;
// float vecLength = length(vec);
const vLength = Math.sqrt((vX * vX) + (vY * vY));
// float r = pow(min(vecLength, 1.0), u_fisheye) * max(1.0, vecLength);
const r = Math.pow(Math.min(vLength, 1), uniforms.u_fisheye) * Math.max(1, vLength);
// vec2 unit = vec / vecLength;
const unitX = vX / vLength;
const unitY = vY / vLength;
// texcoord0 = kCenter + r * unit * kCenter;
dst[0] = CENTER_X + (r * unitX * CENTER_X);
dst[1] = CENTER_Y + (r * unitY * CENTER_Y);
}

return dst;
}
}

module.exports = EffectTransform;
27 changes: 26 additions & 1 deletion src/PenSkin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const twgl = require('twgl.js');

const RenderConstants = require('./RenderConstants');
const Skin = require('./Skin');

const Silhouette = require('./Silhouette');

/**
* Attributes to use when drawing with the pen
Expand Down Expand Up @@ -50,6 +50,12 @@ class PenSkin extends Skin {
/** @type {WebGLTexture} */
this._texture = null;

/** @type {Silhouette} */
this._silhouette = new Silhouette();

/** @type {boolean} */
this._silhouetteDirty = false;

this.onNativeSizeChanged = this.onNativeSizeChanged.bind(this);
this._renderer.on(RenderConstants.Events.NativeSizeChanged, this.onNativeSizeChanged);

Expand Down Expand Up @@ -98,6 +104,7 @@ class PenSkin extends Skin {
const ctx = this._canvas.getContext('2d');
ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
this._canvasDirty = true;
this._silhouetteDirty = true;
}

/**
Expand Down Expand Up @@ -127,6 +134,7 @@ class PenSkin extends Skin {
ctx.lineTo(this._rotationCenter[0] + x1, this._rotationCenter[1] - y1);
ctx.stroke();
this._canvasDirty = true;
this._silhouetteDirty = true;
}

/**
Expand All @@ -139,6 +147,7 @@ class PenSkin extends Skin {
const ctx = this._canvas.getContext('2d');
ctx.drawImage(stampElement, this._rotationCenter[0] + x, this._rotationCenter[1] - y);
this._canvasDirty = true;
this._silhouetteDirty = true;
}

/**
Expand Down Expand Up @@ -173,6 +182,7 @@ class PenSkin extends Skin {
}
);
this._canvasDirty = true;
this._silhouetteDirty = true;
}

/**
Expand All @@ -195,6 +205,21 @@ class PenSkin extends Skin {
context.lineCap = 'round';
context.lineWidth = diameter;
}

/**
* Does this point touch an opaque or translucent point on this skin?
* @param {twgl.v3} vec A texture coordinate.
* @return {boolean} Did it touch?
*/
isTouching (vec) {
if (this._silhouetteDirty) {
if (this._canvasDirty) {
this.getTexture();
}
this._silhouette.update(this._canvas);
}
return this._silhouette.isTouching(vec);
}
}

module.exports = PenSkin;
Loading