Establish initial structure, create demo.html

So far this does nothing but clear the screen to magenta, but it's a
start.
This commit is contained in:
Christopher Willis-Ford 2016-05-13 10:48:45 -07:00
commit 51e17c57a9
25 changed files with 598 additions and 0 deletions

17
.eslintrc Normal file
View File

@ -0,0 +1,17 @@
{
"rules": {
"curly": [2, "multi-line"],
"eol-last": [2],
"indent": [2, 4],
"quotes": [2, "single"],
"linebreak-style": [2, "unix"],
"max-len": [2, 80, 4],
"semi": [2, "always"],
"strict": [2, "never"]
},
"env": {
"node": true,
"browser": true
},
"extends": "eslint:recommended"
}

31
.gitattributes vendored Normal file
View File

@ -0,0 +1,31 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly specify line endings for as many files as possible.
# People who (for example) rsync between Windows and Linux need this.
# File types which we know are binary
# Prefer LF for most file types
*.frag text eol=lf
*.htm text eol=lf
*.html text eol=lf
*.iml text eol=lf
*.js text eol=lf
*.js.map text eol=lf
*.json text eol=lf
*.md text eol=lf
*.vert text eol=lf
*.xml text eol=lf
# Prefer LF for these files
.eslintrc text eol=lf
.gitattributes text eol=lf
.gitignore text eol=lf
.gitmodules text eol=lf
LICENSE text eol=lf
Makefile text eol=lf
README text eol=lf
TRADEMARK text eol=lf
# Use CRLF for Windows-specific file types

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.idea/workspace.xml
.idea/dictionaries
/node_modules
/render-webgl.js.map
/render-webgl.min.js
/render-webgl.js
/render-webgl.min.js.map

9
.idea/codeStyleSettings.xml generated Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value />
</option>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
</component>
</project>

22
.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

3
.idea/copyright/profiles_settings.xml generated Normal file
View File

@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="" />
</component>

6
.idea/encodings.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

7
.idea/eslintPlugin.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ESLintProjectComponent">
<option name="nodeInterpreter" value="" />
<option name="pluginEnabled" value="true" />
</component>
</project>

View File

@ -0,0 +1,10 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false">
<option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" />
<option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
</inspection_tool>
</profile>
</component>

View File

@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>

6
.idea/jsLibraryMappings.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="WebGL" />
</component>
</project>

16
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="true" assert-keyword="true" jdk-15="true">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/scratch-render-webgl.iml" filepath="$PROJECT_DIR$/scratch-render-webgl.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

12
LICENSE Normal file
View File

@ -0,0 +1,12 @@
Copyright (c) 2016, Massachusetts Institute of Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

30
Makefile Normal file
View File

@ -0,0 +1,30 @@
ESLINT=./node_modules/.bin/eslint
NODE=node
TAP=./node_modules/.bin/tap
WEBPACK=./node_modules/.bin/webpack --progress --colors
# ------------------------------------------------------------------------------
build:
$(WEBPACK)
watch:
$(WEBPACK) --watch --watch-poll
# ------------------------------------------------------------------------------
lint:
$(ESLINT) ./src/*.js
$(ESLINT) ./src/**/*.js
$(ESLINT) ./test/**/*.js
test:
@make lint
$(TAP) ./test/{unit,integration}/*.js
coverage:
$(TAP) ./test/{unit,integration}/*.js --coverage --coverage-report=lcov
# ------------------------------------------------------------------------------
.PHONY: build lint test coverage benchmark

36
README.md Normal file
View File

@ -0,0 +1,36 @@
## scratch-render-webgl
[![Build Status](https://travis-ci.com/LLK/scratch-render-webgl.svg?token=zzz&branch=develop)](https://travis-ci.com/LLK/scratch-render-webgl)
## Installation
```bash
npm install scratch-render-webgl
```
## Setup
## Standalone Build
```bash
make build
```
```html
<script src="/path/to/render-webgl.js"></script>
<script>
var renderer = new window.RenderWebGL();
// do things
</script>
```
## Testing
```bash
make test
```
```bash
make coverage
```
```bash
make benchmark
```

7
TRADEMARK Normal file
View File

@ -0,0 +1,7 @@
The Scratch trademarks, including the Scratch name, logo, the Scratch Cat, Gobo, Pico, Nano, Tera and Giga graphics (the "Marks"), are property of the Massachusetts Institute of Technology (MIT), and the use of the Marks is governed by this policy.
You may use the Marks to refer to Scratch in Substantially Unmodified form.
"Substantially Unmodified" means the source code provided by MIT, possibly with minor modifications including but not limited to: bug fixes (including security), changing the locations of files for better integration with the host operating system, adding documentation, and changes to the dynamic linking of libraries.
A version is not "Substantially Unmodified" if it incorporates features not present in a release of Scratch by MIT. If you do make a substantial modification, to avoid confusion with versions of Scratch produced by MIT you must remove all Marks from your version of the software and refrain from using any of the Marks to refer to your version.

21
demo.html Normal file
View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Scratch WebGL rendering demo</title>
</head>
<body>
<canvas id="scratch-stage" width="10" height="10"></canvas>
</body>
<script src="render-webgl.js"></script>
<script>
var canvas = document.getElementById('scratch-stage');
var renderer = new RenderWebGL(canvas);
function step() {
renderer.draw();
requestAnimationFrame(step);
}
step();
</script>
</html>

23
package.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "scratch-render-webgl",
"version": "1.0.0",
"description": "WebGL Renderer for Scratch 3.0",
"author": "Massachusetts Institute of Technology",
"license": "BSD-3-Clause",
"homepage": "https://github.com/LLK/scratch-render-webgl#readme",
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/LLK/scratch-render-webgl.git"
},
"main": "./src/index.js",
"scripts": {
"test": "make test"
},
"devDependencies": {
"eslint": "2.7.0",
"json-loader": "0.5.4",
"tap": "5.7.1",
"twgl.js": "1.5.2",
"webpack": "1.13.0"
}
}

8
scratch-render-webgl.iml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

131
src/index.js Normal file
View File

@ -0,0 +1,131 @@
var EventEmitter = require('events');
var twgl = require('twgl.js');
var util = require('util');
/**
* Create a renderer for drawing Scratch sprites to a canvas using WebGL.
* Optionally, specify the logical and/or physical size of the Scratch stage.
* Logical coordinates will default to Scratch 2.0 values if unspecified.
* Unspecified physical size will be calculated from the logical size.
* @see setStageSize
* @see resize
* @param {canvas} canvas The canvas to draw onto.
* @param {number} [xLeft=-240] The x-coordinate of the left edge.
* @param {number} [xRight=240] The x-coordinate of the right edge.
* @param {number} [yBottom=-180] The y-coordinate of the bottom edge.
* @param {number} [yTop=180] The y-coordinate of the top edge.
* @param {int} [pixelsWide] The desired width in device-independent pixels.
* @param {int} [pixelsTall] The desired height in device-independent pixels.
* @constructor
*/
function RenderWebGL(
canvas, xLeft, xRight, yBottom, yTop, pixelsWide, pixelsTall) {
// Bind event emitter and runtime to VM instance
EventEmitter.call(this);
this._gl = twgl.getWebGLContext(canvas);
this._drawables = {};
this._uniforms = {};
this._createPrograms();
this._createGeometry();
this.setStageSize(
xLeft || -240, xRight || 240, yBottom || -180, yTop || 180);
this.resize(
pixelsWide || Math.abs(this._xRight - this._xLeft),
pixelsTall || Math.abs(this._yTop - this._yBottom));
}
/**
* Inherit from EventEmitter
*/
util.inherits(RenderWebGL, EventEmitter);
/**
* Export and bind to `window`
*/
module.exports = RenderWebGL;
if (typeof window !== 'undefined') window.RenderWebGL = module.exports;
/**
* Set logical size of the stage in Scratch units.
* @param {number} xLeft The left edge's x-coordinate. Scratch 2 uses -240.
* @param {number} xRight The right edge's x-coordinate. Scratch 2 uses 240.
* @param {number} yBottom The bottom edge's y-coordinate. Scratch 2 uses -180.
* @param {number} yTop The top edge's y-coordinate. Scratch 2 uses 180.
*/
RenderWebGL.prototype.setStageSize = function (xLeft, xRight, yBottom, yTop) {
this._xLeft = xLeft;
this._xRight = xRight;
this._yBottom = yBottom;
this._yTop = yTop;
this._uniforms.u_projection =
twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1);
};
/**
* Set the physical size of the stage in device-independent pixels.
* This will be multiplied by the device's pixel ratio on high-DPI displays.
* @param {int} pixelsWide The desired width in device-independent pixels.
* @param {int} pixelsTall The desired height in device-independent pixels.
*/
RenderWebGL.prototype.resize = function (pixelsWide, pixelsTall) {
var pixelRatio = window.devicePixelRatio || 1;
this._gl.canvas.width = pixelsWide * pixelRatio;
this._gl.canvas.height = pixelsTall * pixelRatio;
};
/**
* Draw all current drawables and present the frame on the canvas.
*/
RenderWebGL.prototype.draw = function () {
var gl = this._gl;
gl.viewport(0, 0, gl.canvas.clientWidth, gl.canvas.clientHeight);
gl.clearColor(1, 0, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(this._programInfo.program);
twgl.setBuffersAndAttributes(gl, this._programInfo, this._bufferInfo);
twgl.setUniforms(this._programInfo, this._uniforms);
for (var id in this._drawables) {
if (this._drawables.hasOwnProperty(id)) {
var drawable = this._drawables[id];
twgl.setUniforms(this._programInfo, drawable);
twgl.drawBufferInfo(gl, gl.TRIANGLES, this._bufferInfo);
}
}
};
/**
* Build shaders.
* @private
*/
RenderWebGL.prototype._createPrograms = function () {
var vsText = require('./shaders/sprite.vert');
var fsText = require('./shaders/sprite.frag');
this._programInfo = twgl.createProgramInfo(this._gl, [vsText, fsText]);
};
/**
* Build geometry (vertex and index) buffers.
* @private
*/
RenderWebGL.prototype._createGeometry = function () {
var quad = {
position: [
-0.5, -0.5, 0,
0.5, -0.5, 0,
-0.5, 0.5, 0,
-0.5, 0.5, 0,
0.5, -0.5, 0,
0.5, 0.5, 0
]
};
this._bufferInfo = twgl.createBufferInfoFromArrays(this._gl, quad);
};

128
src/shaders/sprite.frag Normal file
View File

@ -0,0 +1,128 @@
precision mediump float;
uniform sampler2D u_image;
varying vec2 v_texCoord;
uniform float u_hue_shift;
uniform float u_brightness_shift;
uniform float u_whirl_radians;
vec3 convertRGB2HSV(vec3 rgb)
{
float maxRGB = max(max(rgb.r, rgb.g), rgb.b);
float minRGB = min(min(rgb.r, rgb.g), rgb.b);
float span = maxRGB - minRGB;
float h, s;
if (span == 0.0)
{
h = s = 0.0;
}
else
{
if (maxRGB == rgb.r) h = 60.0 * ((rgb.g - rgb.b) / span);
else if (maxRGB == rgb.g) h = 120.0 + 60.0 * ((rgb.b - rgb.r) / span);
else h = 240.0 + 60.0 * ((rgb.r - rgb.g) / span);
s = span / maxRGB;
}
return vec3(h, s, maxRGB);
}
vec3 convertHSV2RGB(vec3 hsv)
{
float h = hsv.r;
float s = hsv.g;
float v = hsv.b;
float f = h / 60.0;
int i = int(f);
f -= float(i);
float p = v * (1.0 - s);
float q = v * (1.0 - (s * f));
float t = v * (1.0 - (s * (1.0 - f)));
vec3 rgb;
if (i == 1)
{
rgb = vec3(q, v, p);
}
else if (i == 2)
{
rgb = vec3(p, v, t);
}
else if (i == 3)
{
rgb = vec3(p, q, v);
}
else if (i == 4)
{
rgb = vec3(t, p, v);
}
else if (i == 5)
{
rgb = vec3(v, p, q);
}
else // i == 0, i == 6, or h was out of range
{
rgb = vec3(v, t, p);
}
return rgb;
}
void main()
{
float hueShift = u_hue_shift;
float whirlRadians = u_whirl_radians;
vec2 texcoord0 = v_texCoord;
if (whirlRadians != 0.0)
{
const vec2 kCenter = vec2(0.5, 0.5);
const float kRadius = 0.5;
vec2 offset = texcoord0 - kCenter;
float offsetMagnitude = length(offset);
float whirlFactor = 1.0 - (offsetMagnitude / kRadius);
float whirlActual = whirlRadians * whirlFactor * whirlFactor;
float sinWhirl = sin(whirlActual);
float cosWhirl = cos(whirlActual);
mat2 rotationMatrix = mat2(
cosWhirl, -sinWhirl,
sinWhirl, cosWhirl
);
if (offsetMagnitude <= kRadius)
{
texcoord0 = rotationMatrix * offset + kCenter;
}
}
gl_FragColor = texture2D(u_image, texcoord0);
// TODO: See if we can/should use actual alpha test.
// Does bgfx offer a way to set u_alphaRef? Would that help?
if (gl_FragColor.a == 0.0)
{
discard;
}
const bool needHSV = true;
if (needHSV)
{
vec3 hsv = convertRGB2HSV(gl_FragColor.rgb);
if (hueShift != 0.0)
{
// this code forces grayscale values to be slightly saturated
// so that some slight change of hue will be visible
if (hsv.b < 0.11) hsv = vec3(0.0, 1.0, 0.11); // force black to dark gray, fully-saturated
if (hsv.g < 0.09) hsv = vec3(0.0, 0.09, hsv.b); // make saturation at least 0.09
hsv.r = mod(hsv.r + hueShift, 360.0);
if (hsv.r < 0.0) hsv.r += 360.0;
}
gl_FragColor.rgb = convertHSV2RGB(hsv);
}
}

12
src/shaders/sprite.vert Normal file
View File

@ -0,0 +1,12 @@
attribute vec4 position;
varying vec2 v_texCoord;
uniform mat4 u_transform;
uniform mat4 u_projection;
void main() {
gl_Position = u_projection * u_transform * position;
// Map clipspace coordinates to texture coordinates
v_texCoord = (position.xy * vec2(1.0, -1.0)) + vec2(0.5);
}

35
webpack.config.js Normal file
View File

@ -0,0 +1,35 @@
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: {
'render-webgl': './src/index.js',
'render-webgl.min': './src/index.js'
},
devtool: 'source-map',
output: {
path: __dirname,
filename: '[name].js'
},
module: {
loaders: [
{
test: /\.json$/,
loader: 'json-loader'
},
{
test: /\.(glsl|vs|fs|frag|vert)$/,
loader: 'raw-loader' // we might want a GLSL loader if we use includes
}
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
include: /\.min\.js$/,
minimize: true,
compress: {
warnings: false
}
})
]
};