├── grunt
├── tasks
│ ├── .gitkeep
│ └── shaderChunks.js
└── config
│ ├── clean.js
│ ├── notify.js
│ ├── connect.js
│ ├── jshint.js
│ ├── uglify.js
│ ├── handlebars.js
│ ├── haychtml.js
│ ├── autoprefixer.js
│ ├── neuter.js
│ ├── sass.js
│ ├── copy.js
│ └── watch.js
├── static
├── scss
│ ├── helpers
│ │ ├── _mixins.scss
│ │ ├── _colors.scss
│ │ └── _type.scss
│ ├── style.scss
│ ├── components
│ │ ├── _graph.scss
│ │ ├── _color.scss
│ │ └── _controls.scss
│ ├── apps
│ │ ├── _application.scss
│ │ └── _index.scss
│ └── tests.scss
├── img
│ └── favicon.ico
├── audio
│ ├── bg-loop.mp3
│ ├── bg-loop.ogg
│ ├── bubbles-1.mp3
│ ├── bubbles-1.ogg
│ ├── bubbles-2.mp3
│ ├── bubbles-2.ogg
│ ├── buzz-wave-2.mp3
│ ├── buzz-wave-2.ogg
│ ├── buzz-wave-4.mp3
│ └── buzz-wave-4.ogg
├── glsl
│ ├── shader-chunks
│ │ ├── lerp_pos_pars_vertex.glsl
│ │ └── lerp_pos_vertex.glsl
│ └── shaders
│ │ ├── uvs-frag.glsl
│ │ ├── gel-vert.glsl
│ │ ├── tentacle-vert.glsl
│ │ ├── tentacle-frag.glsl
│ │ ├── alpha-vert.glsl
│ │ ├── gel-frag.glsl
│ │ ├── lerp-vert.glsl
│ │ ├── lerp-point-vert.glsl
│ │ ├── normal-vert.glsl
│ │ ├── dust-frag.glsl
│ │ ├── dust-vert.glsl
│ │ ├── basic-point-frag.glsl
│ │ ├── alpha-frag.glsl
│ │ ├── basic-vert.glsl
│ │ ├── basic-frag.glsl
│ │ ├── tail-frag.glsl
│ │ └── bulb-frag.glsl
├── js
│ ├── controllers
│ │ ├── TestController.js
│ │ ├── IndexController.js
│ │ └── AudioController.js
│ ├── application
│ │ └── App.js
│ ├── utils
│ │ ├── Geometry.js
│ │ ├── Format.js
│ │ ├── KeyDelegator.js
│ │ ├── Dispatcher.js
│ │ ├── Links.js
│ │ ├── Faces.js
│ │ ├── Tweens.js
│ │ ├── Looper.js
│ │ └── Features.js
│ ├── app.js
│ ├── materials
│ │ ├── AlphaMaterial.js
│ │ ├── UVMaterial.js
│ │ ├── GelMaterial.js
│ │ ├── LerpMaterial.js
│ │ ├── DustMaterial.js
│ │ ├── TentacleMaterial.js
│ │ ├── LerpPointMaterial.js
│ │ ├── BulbMaterial.js
│ │ ├── TailMaterial.js
│ │ └── ShaderMaterial.js
│ ├── libs.js
│ ├── components
│ │ ├── ModalComponent.js
│ │ ├── ColorComponent.js
│ │ ├── ToggleComponent.js
│ │ └── GraphComponent.js
│ ├── forces
│ │ └── PointRepulsorForce.js
│ ├── constraints
│ │ └── LocalPlaneConstraint.js
│ ├── items
│ │ └── Dust.js
│ └── post-processing
│ │ ├── LensDirtTexture.js
│ │ └── LensDirtPass.js
├── lib-extras
│ └── three
│ │ ├── .editorconfig
│ │ ├── shaders
│ │ ├── BasicShader.js
│ │ ├── CopyShader.js
│ │ ├── LuminosityShader.js
│ │ ├── NormalMapShader.js
│ │ ├── BleachBypassShader.js
│ │ ├── VignetteShader.js
│ │ ├── HueSaturationShader.js
│ │ ├── VerticalBlurShader.js
│ │ ├── HorizontalBlurShader.js
│ │ ├── HorizontalTiltShiftShader.js
│ │ ├── VerticalTiltShiftShader.js
│ │ ├── ConvolutionShader.js
│ │ ├── FXAAShader.js
│ │ ├── BokehShader.js
│ │ └── SSAOShader.js
│ │ ├── postprocessing
│ │ ├── TexturePass.js
│ │ ├── RenderPass.js
│ │ ├── ShaderPass.js
│ │ ├── SavePass.js
│ │ ├── FilmPass.js
│ │ ├── MaskPass.js
│ │ ├── BokehPass.js
│ │ ├── EffectComposer.js
│ │ └── BloomPass.js
│ │ ├── geometries
│ │ ├── PlaneBufferGeometry.js
│ │ └── SphereBufferGeometry.js
│ │ └── controls
│ │ └── TrackballControls.js
└── tests
│ ├── assert
│ ├── range.js
│ ├── equal-array.js
│ └── close-enough.js
│ ├── post-processing
│ └── LensDirtPass.js
│ ├── .jshintrc
│ ├── tests.js
│ └── constraints
│ └── LocalPlaneConstraint.js
├── .bowerrc
├── pages
├── index.html
└── _base.html
├── .editorconfig
├── postinstall.sh
├── .gitattributes
├── bower.json
├── .jshintrc
├── .gitignore
├── package.json
├── Gruntfile.js
├── index.html
├── README.md
└── LICENSE
/grunt/tasks/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/scss/helpers/_mixins.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "static/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milcktoast/particulate-medusae/HEAD/static/img/favicon.ico
--------------------------------------------------------------------------------
/static/audio/bg-loop.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milcktoast/particulate-medusae/HEAD/static/audio/bg-loop.mp3
--------------------------------------------------------------------------------
/static/audio/bg-loop.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milcktoast/particulate-medusae/HEAD/static/audio/bg-loop.ogg
--------------------------------------------------------------------------------
/static/audio/bubbles-1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milcktoast/particulate-medusae/HEAD/static/audio/bubbles-1.mp3
--------------------------------------------------------------------------------
/static/audio/bubbles-1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milcktoast/particulate-medusae/HEAD/static/audio/bubbles-1.ogg
--------------------------------------------------------------------------------
/static/audio/bubbles-2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milcktoast/particulate-medusae/HEAD/static/audio/bubbles-2.mp3
--------------------------------------------------------------------------------
/static/audio/bubbles-2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milcktoast/particulate-medusae/HEAD/static/audio/bubbles-2.ogg
--------------------------------------------------------------------------------
/static/glsl/shader-chunks/lerp_pos_pars_vertex.glsl:
--------------------------------------------------------------------------------
1 | uniform float stepProgress;
2 | attribute vec3 positionPrev;
3 |
--------------------------------------------------------------------------------
/static/audio/buzz-wave-2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milcktoast/particulate-medusae/HEAD/static/audio/buzz-wave-2.mp3
--------------------------------------------------------------------------------
/static/audio/buzz-wave-2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milcktoast/particulate-medusae/HEAD/static/audio/buzz-wave-2.ogg
--------------------------------------------------------------------------------
/static/audio/buzz-wave-4.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milcktoast/particulate-medusae/HEAD/static/audio/buzz-wave-4.mp3
--------------------------------------------------------------------------------
/static/audio/buzz-wave-4.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milcktoast/particulate-medusae/HEAD/static/audio/buzz-wave-4.ogg
--------------------------------------------------------------------------------
/static/scss/helpers/_colors.scss:
--------------------------------------------------------------------------------
1 | $red : #f00;
2 | $black : #222;
3 | $white : #fefefe;
4 |
5 | $link-color : $red;
--------------------------------------------------------------------------------
/static/js/controllers/TestController.js:
--------------------------------------------------------------------------------
1 | App.register('tests', function tests() {
2 | document.body.className = 'testing';
3 | });
4 |
--------------------------------------------------------------------------------
/static/glsl/shader-chunks/lerp_pos_vertex.glsl:
--------------------------------------------------------------------------------
1 | vec4 mvPosition = modelViewMatrix * vec4(mix(positionPrev, position, stepProgress), 1.0);
2 | gl_Position = projectionMatrix * mvPosition;
3 |
--------------------------------------------------------------------------------
/static/scss/helpers/_type.scss:
--------------------------------------------------------------------------------
1 | body {
2 | color: #fff;
3 | font: 12px/1.2 Monaco, monospace;
4 | -webkit-font-smoothing: antialiased;
5 | }
6 |
7 | h1 {
8 | font-size: 14px;
9 | }
10 |
--------------------------------------------------------------------------------
/static/lib-extras/three/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | [*]
4 | indent_style = tab
5 | end_of_line = lf
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
--------------------------------------------------------------------------------
/pages/index.html:
--------------------------------------------------------------------------------
1 | {% extends "_base.html" %}
2 |
3 | {% block page_name %}index{% endblock page_name %}
4 | {% block title %}Home{% endblock title %}
5 |
6 | {% block content %}
7 |
Home
8 | {% endblock content %}
9 |
--------------------------------------------------------------------------------
/static/tests/assert/range.js:
--------------------------------------------------------------------------------
1 | Test.assert.range = assert_range;
2 | function assert_range(actual, min, max, message) {
3 | var passes = actual >= min && actual <= max;
4 | var expected = [min, max];
5 | QUnit.push(passes, actual, expected, message);
6 | }
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/static/scss/style.scss:
--------------------------------------------------------------------------------
1 | @import "red-sass/red-sass";
2 | @include reset;
3 |
4 | @import
5 | "helpers/colors",
6 | "helpers/type",
7 | "helpers/mixins",
8 |
9 | "components/controls",
10 | "components/color",
11 | "components/graph",
12 |
13 | "apps/application",
14 | "apps/index";
15 |
--------------------------------------------------------------------------------
/grunt/config/clean.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | // https://github.com/gruntjs/grunt-contrib-clean
5 |
6 | // Cleans folders and files.
7 |
8 | module.exports = function (config) {
9 | return {
10 | build: config.deploy + '*',
11 | temp: '.temp'
12 | };
13 | };
14 |
--------------------------------------------------------------------------------
/postinstall.sh:
--------------------------------------------------------------------------------
1 | bower install
2 |
3 | NODE=./node_modules
4 | BOWER=./static/lib
5 | PACKAGES="three"
6 |
7 | for PACKAGE in $PACKAGES
8 | do
9 | if [ ! -e "$NODE/$PACKAGE" ]
10 | then
11 | continue
12 | fi
13 | rm -rf $BOWER/$PACKAGE
14 | mv -v $NODE/$PACKAGE $BOWER/$PACKAGE
15 | done
16 |
--------------------------------------------------------------------------------
/static/glsl/shaders/uvs-frag.glsl:
--------------------------------------------------------------------------------
1 | uniform vec3 diffuse;
2 | uniform float opacity;
3 |
4 | {{{chunks.common}}}
5 | {{{chunks.map_pars_fragment}}}
6 |
7 | void main() {
8 | vec4 diffuseColor = vec4(diffuse, opacity);
9 |
10 | {{{chunks.map_fragment}}}
11 |
12 | gl_FragColor = vec4(vUv.xy, 0.0, opacity);
13 | }
14 |
--------------------------------------------------------------------------------
/static/glsl/shaders/gel-vert.glsl:
--------------------------------------------------------------------------------
1 | {{{chunks.common}}}
2 | {{{chunks.lerp_pos_pars_vertex}}}
3 | {{{chunks.color_pars_vertex}}}
4 |
5 | varying vec3 vNormal;
6 |
7 | void main() {
8 | {{{chunks.color_vertex}}}
9 | {{{chunks.lerp_pos_vertex}}}
10 | {{{chunks.worldpos_vertex}}}
11 |
12 | vNormal = normalize(position);
13 | }
14 |
--------------------------------------------------------------------------------
/grunt/config/notify.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | // https://github.com/dylang/grunt-notify
5 |
6 | // Desktop notifications for grunt errors and warnings.
7 |
8 | module.exports = function () {
9 | return {
10 | build: {
11 | options: {
12 | message: 'Build complete.'
13 | }
14 | }
15 | };
16 | };
17 |
--------------------------------------------------------------------------------
/static/glsl/shaders/tentacle-vert.glsl:
--------------------------------------------------------------------------------
1 | uniform float area;
2 | varying float centerDist;
3 |
4 | {{{chunks.common}}}
5 | {{{chunks.lerp_pos_pars_vertex}}}
6 | {{{chunks.color_pars_vertex}}}
7 |
8 | void main() {
9 | {{{chunks.color_vertex}}}
10 |
11 | centerDist = length(position);
12 |
13 | {{{chunks.lerp_pos_vertex}}}
14 | {{{chunks.worldpos_vertex}}}
15 | }
16 |
--------------------------------------------------------------------------------
/static/scss/components/_graph.scss:
--------------------------------------------------------------------------------
1 | .graph {
2 | position: relative;
3 | margin: 10px 0;
4 |
5 | > .label {
6 | position: absolute;
7 | bottom: 0;
8 | right: 0;
9 | padding: 2px 4px 0;
10 |
11 | color: #fff;
12 | background: rgba(#111, 0.5);
13 | }
14 |
15 | > canvas {
16 | position: relative;
17 | display: block;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto* eol=lf
2 | *.png binary
3 | *.jpg binary
4 | *.gif binary
5 | *.jar binary
6 | *.exe binary
7 | *.eot binary
8 | *.ttf binary
9 | *.otf binary
10 | *.pdf binary
11 | *.woff binary
12 | *.bz2 binary
13 | *.gz binary
14 |
15 | # Flash Binaries
16 | *.swc binary
17 | *.fla binary
18 | *.swf binary
19 | *.as binary
20 |
21 | *.mp3 binary
22 | *.ogg binary
23 | *.wav binary
24 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "particulate-medusae",
3 | "private": true,
4 | "version": "0.0.0",
5 | "dependencies": {
6 | "handlebars": "~4.0.5",
7 | "native-promise-only": "~0.8.0-a",
8 | "noise": "git@github.com:josephg/noisejs.git#3861daee43d62f809126eb9783cf4091d4796ee8",
9 | "particulate": "~0.3.2",
10 | "qunit": "~1.18.0",
11 | "red-sass": "~0.2.0"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/static/glsl/shaders/tentacle-frag.glsl:
--------------------------------------------------------------------------------
1 | uniform vec3 diffuse;
2 | uniform float opacity;
3 | uniform float area;
4 | varying float centerDist;
5 |
6 | void main() {
7 | float illumination = area * 2.0 / (centerDist * centerDist);
8 | gl_FragColor = vec4(
9 | mix(vec3(1.0), diffuse, clamp(illumination, 0.0, 1.25)),
10 | clamp(opacity * illumination * illumination, 0.0, opacity));
11 | }
12 |
--------------------------------------------------------------------------------
/static/tests/assert/equal-array.js:
--------------------------------------------------------------------------------
1 | Test.assert.equalArray = assert_equalArray;
2 | function assert_equalArray(actual, expected, message) {
3 | var isEqual = true;
4 |
5 | for (var i = 0, il = expected.length; i < il; i ++) {
6 | if (actual[i] !== expected[i]) {
7 | isEqual = false;
8 | break;
9 | }
10 | }
11 |
12 | QUnit.push(isEqual, actual, expected, message);
13 | }
14 |
--------------------------------------------------------------------------------
/grunt/config/connect.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | // https://github.com/gruntjs/grunt-contrib-connect
5 |
6 | // Start a connect web server.
7 |
8 | module.exports = function (config) {
9 | return {
10 | options: {
11 | port: 8000,
12 | hostname: '*',
13 | base: './',
14 | livereload: 38000,
15 | open: true
16 | },
17 | develop: {}
18 | };
19 | };
20 |
--------------------------------------------------------------------------------
/static/glsl/shaders/alpha-vert.glsl:
--------------------------------------------------------------------------------
1 | {{{chunks.common}}}
2 | {{{chunks.uv_pars_vertex}}}
3 | {{{chunks.color_pars_vertex}}}
4 | {{{chunks.logdepthbuf_pars_vertex}}}
5 |
6 | attribute float alpha;
7 | varying float vAlpha;
8 |
9 | void main() {
10 | {{{chunks.uv_vertex}}}
11 | {{{chunks.color_vertex}}}
12 |
13 | {{{chunks.begin_vertex}}}
14 | {{{chunks.project_vertex}}}
15 | {{{chunks.logdepthbuf_vertex}}}
16 |
17 | {{{chunks.worldpos_vertex}}}
18 |
19 | vAlpha = alpha;
20 | }
21 |
--------------------------------------------------------------------------------
/static/glsl/shaders/gel-frag.glsl:
--------------------------------------------------------------------------------
1 | uniform vec3 diffuse;
2 | uniform float opacity;
3 | varying vec3 vNormal;
4 |
5 | void main() {
6 | vec3 eye = vec3(0.0, 0.0, 1.0);
7 | vec3 normal = normalize(mat3(viewMatrix) * vNormal);
8 | float rim = 1.0 - max(dot(eye, normal), 0.0);
9 |
10 | float rimLight = 0.25 +
11 | smoothstep(0.25, 1.0, rim) * 0.5 +
12 | smoothstep(0.90, 1.0, rim) * 0.8;
13 |
14 | gl_FragColor.rgb = diffuse * vec3(rimLight);
15 | gl_FragColor.a = opacity;
16 | }
17 |
--------------------------------------------------------------------------------
/grunt/config/jshint.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | // https://github.com/gruntjs/grunt-contrib-jshint
5 |
6 | // Validate javascript files with JSHint.
7 | // The jshint options should be set in the .jshintrc file.
8 |
9 | module.exports = function (config) {
10 | return {
11 | options: {
12 | jshintrc: '.jshintrc'
13 | },
14 | all: [
15 | 'Gruntfile.js',
16 | 'grunt/{,**/}*.js',
17 | config.source + 'js/{,**/}*.js'
18 | ]
19 | };
20 | };
21 |
--------------------------------------------------------------------------------
/static/glsl/shaders/lerp-vert.glsl:
--------------------------------------------------------------------------------
1 | {{{chunks.common}}}
2 | {{{chunks.uv_pars_vertex}}}
3 | {{{chunks.uv2_pars_vertex}}}
4 | {{{chunks.color_pars_vertex}}}
5 | {{{chunks.lerp_pos_pars_vertex}}}
6 | {{{chunks.logdepthbuf_pars_vertex}}}
7 |
8 | void main() {
9 | {{{chunks.uv_vertex}}}
10 | {{{chunks.uv2_vertex}}}
11 | {{{chunks.color_vertex}}}
12 |
13 | {{{chunks.begin_vertex}}}
14 | {{{chunks.lerp_pos_vertex}}}
15 | {{{chunks.logdepthbuf_vertex}}}
16 | {{{chunks.worldpos_vertex}}}
17 | }
18 |
--------------------------------------------------------------------------------
/static/glsl/shaders/lerp-point-vert.glsl:
--------------------------------------------------------------------------------
1 | uniform float size;
2 | uniform float scale;
3 |
4 | {{{chunks.common}}}
5 | {{{chunks.color_pars_vertex}}}
6 | {{{chunks.lerp_pos_pars_vertex}}}
7 | {{{chunks.logdepthbuf_pars_vertex}}}
8 |
9 | void main() {
10 | {{{chunks.color_vertex}}}
11 | {{{chunks.lerp_pos_vertex}}}
12 | {{{chunks.logdepthbuf_vertex}}}
13 |
14 | #ifdef USE_SIZEATTENUATION
15 | gl_PointSize = size * (scale / length(mvPosition.xyz));
16 | #else
17 | gl_PointSize = size;
18 | #endif
19 | }
20 |
--------------------------------------------------------------------------------
/static/js/application/App.js:
--------------------------------------------------------------------------------
1 | window.App = Object.create({
2 | ctor : Particulate.ctor,
3 | log : (window.console && window.console.log.bind &&
4 | window.console.log.bind(window.console)) || function () {},
5 |
6 | shaders : window.App && window.App.shaders,
7 |
8 | _register : {},
9 | register : function (name, fn) {
10 | this._register[name] = fn;
11 | },
12 |
13 | run : function (name) {
14 | if (!this._register[name]) { return; }
15 | this._register[name].call(this);
16 | }
17 | });
18 |
--------------------------------------------------------------------------------
/static/js/utils/Geometry.js:
--------------------------------------------------------------------------------
1 | var Geometry = App.Geometry = {};
2 |
3 | Geometry.point = function (x, y, z, buffer) {
4 | buffer.push(x, y, z);
5 | return buffer;
6 | };
7 |
8 | Geometry.circle = function (segments, radius, y, buffer) {
9 | var step = Math.PI * 2 / segments;
10 | var angle = 0;
11 | var x, z;
12 |
13 | for (var i = 0; i < segments; i ++) {
14 | x = Math.cos(angle) * radius;
15 | z = Math.sin(angle) * radius;
16 |
17 | buffer.push(x, y, z);
18 | angle += step;
19 | }
20 | return buffer;
21 | };
22 |
--------------------------------------------------------------------------------
/grunt/config/uglify.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | // https://github.com/gruntjs/grunt-contrib-uglify
5 |
6 | // Minify files with UglifyJS.
7 |
8 | module.exports = function (config) {
9 | return {
10 | app: {
11 | src: [
12 | config.static + 'js/libs.develop.js',
13 | config.static + 'js/shader-chunks.develop.js',
14 | config.static + 'js/shaders.develop.js',
15 | config.static + 'js/app.develop.js'
16 | ],
17 | dest: config.static + 'js/app.min.js'
18 | }
19 | };
20 | };
21 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/BasicShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://www.mrdoob.com
3 | *
4 | * Simple test shader
5 | */
6 |
7 | THREE.BasicShader = {
8 |
9 | uniforms: {},
10 |
11 | vertexShader: [
12 |
13 | "void main() {",
14 |
15 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
16 |
17 | "}"
18 |
19 | ].join("\n"),
20 |
21 | fragmentShader: [
22 |
23 | "void main() {",
24 |
25 | "gl_FragColor = vec4( 1.0, 0.0, 0.0, 0.5 );",
26 |
27 | "}"
28 |
29 | ].join("\n")
30 |
31 | };
32 |
--------------------------------------------------------------------------------
/static/scss/components/_color.scss:
--------------------------------------------------------------------------------
1 | .color {
2 | @include controls-button;
3 |
4 | > .preview { @include controls-toggle; }
5 | > .label { @include controls-label; }
6 |
7 | > input {
8 | position: absolute;
9 | top: 0;
10 | left: 0;
11 |
12 | display: block;
13 | width: 100%;
14 | height: 100%;
15 | padding: 0;
16 | margin: 0;
17 | border: none;
18 |
19 | opacity: 0;
20 | cursor: pointer;
21 | }
22 |
23 | &:hover > .label,
24 | &.focus > .label {
25 | visibility: visible;
26 | opacity: 1;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/static/glsl/shaders/normal-vert.glsl:
--------------------------------------------------------------------------------
1 | {{{chunks.common}}}
2 | {{{chunks.uv_pars_vertex}}}
3 | {{{chunks.uv2_pars_vertex}}}
4 | {{{chunks.color_pars_vertex}}}
5 | {{{chunks.lerp_pos_pars_vertex}}}
6 | {{{chunks.logdepthbuf_pars_vertex}}}
7 |
8 | varying vec3 vNormal;
9 |
10 | void main() {
11 | {{{chunks.uv_vertex}}}
12 | {{{chunks.uv2_vertex}}}
13 | {{{chunks.color_vertex}}}
14 |
15 | {{{chunks.begin_vertex}}}
16 | {{{chunks.lerp_pos_vertex}}}
17 | {{{chunks.logdepthbuf_vertex}}}
18 | {{{chunks.worldpos_vertex}}}
19 |
20 | vNormal = normalize(position);
21 | }
22 |
--------------------------------------------------------------------------------
/static/js/utils/Format.js:
--------------------------------------------------------------------------------
1 | var Format = App.Format = {};
2 |
3 | Format.number = function (val) {
4 | var chars = ('' + val).split('');
5 | var str = '';
6 | var index;
7 |
8 | for (var i = 0, il = chars.length; i < il; i ++) {
9 | index = il - i - 1;
10 | str += chars[i];
11 | if (index % 3 === 0 && index > 0) {
12 | str += ',';
13 | }
14 | }
15 |
16 | return str;
17 | };
18 |
19 | Format.absoluteLength = function (val, length) {
20 | val = '' + val;
21 | while (val.length < length) {
22 | val = '0' + val;
23 | }
24 | return val;
25 | };
26 |
--------------------------------------------------------------------------------
/static/js/app.js:
--------------------------------------------------------------------------------
1 | require('js/application/App');
2 | require('js/utils/*');
3 | require('js/components/*');
4 | require('js/constraints/*');
5 | require('js/forces/*');
6 | require('js/materials/*');
7 | require('js/post-processing/*');
8 | require('js/items/*');
9 | require('js/controllers/*');
10 | require('js/scenes/*');
11 |
12 | setTimeout(function setup() {
13 | var DEBUG = true;
14 | if (DEBUG && location.search.indexOf('test=true') > -1) {
15 | App.run('tests');
16 | } else {
17 | App.run('index');
18 | App.log('Particulate.js ' + Particulate.VERSION);
19 | }
20 | }, 0);
21 |
--------------------------------------------------------------------------------
/static/js/materials/AlphaMaterial.js:
--------------------------------------------------------------------------------
1 | require('./ShaderMaterial');
2 | var ShaderMaterial = App.ShaderMaterial;
3 | var uniforms = THREE.UniformsLib;
4 |
5 | App.AlphaMaterial = AlphaMaterial;
6 | function AlphaMaterial(parameters) {
7 | parameters = parameters || {};
8 | ShaderMaterial.call(this, parameters);
9 | }
10 |
11 | AlphaMaterial.prototype = Object.create(ShaderMaterial.prototype);
12 |
13 | AlphaMaterial.prototype.shader = {
14 | vertexShader : 'alpha-vert',
15 | fragmentShader : 'alpha-frag',
16 |
17 | uniforms : THREE.UniformsUtils.merge([
18 | uniforms.common
19 | ])
20 | };
21 |
--------------------------------------------------------------------------------
/static/glsl/shaders/dust-frag.glsl:
--------------------------------------------------------------------------------
1 | uniform vec3 psColor;
2 | uniform float opacity;
3 | uniform float area;
4 | varying float centerDist;
5 |
6 | {{{chunks.common}}}
7 | {{{chunks.color_pars_fragment}}}
8 | {{{chunks.map_particle_pars_fragment}}}
9 |
10 | void main() {
11 | vec4 diffuseColor = vec4(psColor, opacity);
12 | float radius = area * 0.5;
13 | float illumination = max(0.0, (radius - centerDist) / radius);
14 |
15 | {{{chunks.map_particle_fragment}}}
16 | {{{chunks.color_fragment}}}
17 |
18 | gl_FragColor = vec4(diffuseColor.rgb,
19 | illumination * illumination * diffuseColor.a);
20 | }
21 |
--------------------------------------------------------------------------------
/static/js/materials/UVMaterial.js:
--------------------------------------------------------------------------------
1 | require('./ShaderMaterial');
2 | var ShaderMaterial = App.ShaderMaterial;
3 | var uniforms = THREE.UniformsLib;
4 |
5 | App.UVMaterial = UVMaterial;
6 | function UVMaterial(parameters) {
7 | parameters = parameters || {};
8 | parameters.map = true;
9 | ShaderMaterial.call(this, parameters);
10 | }
11 |
12 | UVMaterial.prototype = Object.create(ShaderMaterial.prototype);
13 |
14 | UVMaterial.prototype.shader = {
15 | vertexShader : 'basic-vert',
16 | fragmentShader : 'uvs-frag',
17 |
18 | uniforms : THREE.UniformsUtils.merge([
19 | uniforms.common
20 | ])
21 | };
22 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "require" : true,
4 | "THREE" : true,
5 | "Particulate" : true,
6 | "App" : true,
7 | "noise" : true
8 | },
9 |
10 | "browser" : true,
11 | "curly" : true,
12 | "eqeqeq" : true,
13 | "eqnull" : true,
14 | "forin" : true,
15 | "indent" : 2,
16 | "latedef" : "nofunc",
17 | "maxcomplexity" : 10,
18 | "maxdepth" : 3,
19 | "newcap" : true,
20 | "noarg" : true,
21 | "quotmark" : "single",
22 | "undef" : true,
23 | "unused" : "vars"
24 | }
25 |
--------------------------------------------------------------------------------
/static/js/utils/KeyDelegator.js:
--------------------------------------------------------------------------------
1 | App.KeyDelegator = KeyDelegator;
2 | function KeyDelegator() {
3 | this._bindings = {};
4 | document.addEventListener('keyup', this.onDocumentKey.bind(this), false);
5 | }
6 |
7 | KeyDelegator.create = App.ctor(KeyDelegator);
8 |
9 | KeyDelegator.prototype.addBinding = function (key, context, fn) {
10 | this._bindings[key] = {
11 | context : context,
12 | fn : fn
13 | };
14 | };
15 |
16 | KeyDelegator.prototype.onDocumentKey = function (event) {
17 | var binding = this._bindings[event.which];
18 | if (!binding) { return; }
19 |
20 | binding.context[binding.fn].call(binding.context, event);
21 | };
22 |
--------------------------------------------------------------------------------
/grunt/config/handlebars.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | // https://github.com/gruntjs/grunt-contrib-handlebars
5 |
6 | // Precompile Handlebars templates to JST file.
7 |
8 | module.exports = function (config) {
9 | return {
10 | options : {
11 | processName : function (path) {
12 | var name = path.split('/').pop();
13 | return name.split('.')[0];
14 | }
15 | },
16 |
17 | shaders : {
18 | options : {
19 | namespace : 'App.shaders'
20 | },
21 | src : config.source + 'glsl/shaders/{,*/}*',
22 | dest : config.static + 'js/shaders.develop.js'
23 | }
24 | };
25 | };
26 |
--------------------------------------------------------------------------------
/grunt/config/haychtml.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | // https://github.com/timrwood/haychtml
5 |
6 | // Compiles html templates.
7 |
8 | module.exports = function (config) {
9 | return {
10 | develop : {
11 | engine: 'swig',
12 | src: config.pages,
13 | dest: './',
14 | data : {
15 | TEMPLATE_DEBUG : true,
16 | STATIC_URL : config.static
17 | }
18 | },
19 | build : {
20 | engine: 'swig',
21 | src: config.pages,
22 | dest: './',
23 | data : {
24 | TEMPLATE_DEBUG : false,
25 | STATIC_URL : config.static
26 | }
27 | }
28 | };
29 | };
30 |
--------------------------------------------------------------------------------
/static/js/materials/GelMaterial.js:
--------------------------------------------------------------------------------
1 | require('./ShaderMaterial');
2 | var ShaderMaterial = App.ShaderMaterial;
3 | var uniforms = THREE.UniformsLib;
4 |
5 | App.GelMaterial = GelMaterial;
6 | function GelMaterial(parameters) {
7 | parameters = parameters || {};
8 | ShaderMaterial.call(this, parameters);
9 | }
10 |
11 | GelMaterial.prototype = Object.create(ShaderMaterial.prototype);
12 |
13 | GelMaterial.prototype.shader = {
14 | vertexShader : 'gel-vert',
15 | fragmentShader : 'gel-frag',
16 |
17 | uniforms : THREE.UniformsUtils.merge([
18 | uniforms.common,
19 | {
20 | stepProgress : { type : 'f', value : 0 }
21 | }
22 | ])
23 | };
24 |
--------------------------------------------------------------------------------
/static/js/materials/LerpMaterial.js:
--------------------------------------------------------------------------------
1 | require('./ShaderMaterial');
2 | var ShaderMaterial = App.ShaderMaterial;
3 | var uniforms = THREE.UniformsLib;
4 |
5 | App.LerpMaterial = LerpMaterial;
6 | function LerpMaterial(parameters) {
7 | parameters = parameters || {};
8 | ShaderMaterial.call(this, parameters);
9 | }
10 |
11 | LerpMaterial.prototype = Object.create(ShaderMaterial.prototype);
12 |
13 | LerpMaterial.prototype.shader = {
14 | vertexShader : 'lerp-vert',
15 | fragmentShader : 'basic-frag',
16 |
17 | uniforms : THREE.UniformsUtils.merge([
18 | uniforms.common,
19 | {
20 | stepProgress : { type : 'f', value : 0 }
21 | }
22 | ])
23 | };
24 |
--------------------------------------------------------------------------------
/grunt/config/autoprefixer.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | // https://github.com/nDmitry/grunt-autoprefixer
5 |
6 | // Parses CSS and add vendor-prefixed properties using the Can I Use database.
7 |
8 | module.exports = function (config) {
9 | return {
10 | options: {
11 | browsers: ['> 1%', 'last 2 versions', 'ie 9']
12 | },
13 | build: {
14 | files : [{
15 | expand: true,
16 | cwd: '.temp/css/',
17 | src: '*.css',
18 | dest: config.static + 'css/'
19 | }]
20 | },
21 | develop: {
22 | files : '<%= autoprefixer.build.files %>',
23 | options: {
24 | map: true
25 | }
26 | }
27 | };
28 | };
29 |
--------------------------------------------------------------------------------
/static/glsl/shaders/dust-vert.glsl:
--------------------------------------------------------------------------------
1 | uniform float size;
2 | uniform float scale;
3 | uniform float time;
4 | uniform float area;
5 | varying float centerDist;
6 |
7 | void main() {
8 | float offsetY = mod(position.y - 1.0 * time, area) - area * 0.5;
9 | vec3 offsetPosition = vec3(
10 | position.x + sin(cos(offsetY * 0.1) + sin(offsetY * 0.1 + position.x * 0.1) * 2.0),
11 | offsetY,
12 | position.z + sin(cos(offsetY * 0.1) + sin(offsetY * 0.1 + position.z * 0.1) * 2.0));
13 |
14 | centerDist = length(offsetPosition);
15 |
16 | vec4 mvPosition = modelViewMatrix * vec4(offsetPosition, 1.0);
17 |
18 | gl_PointSize = size * (scale / length(mvPosition.xyz));
19 | gl_Position = projectionMatrix * mvPosition;
20 | }
21 |
--------------------------------------------------------------------------------
/static/js/materials/DustMaterial.js:
--------------------------------------------------------------------------------
1 | require('./ShaderMaterial');
2 | var ShaderMaterial = App.ShaderMaterial;
3 | var uniforms = THREE.UniformsLib;
4 |
5 | App.DustMaterial = DustMaterial;
6 | function DustMaterial(parameters) {
7 | parameters = parameters || {};
8 | ShaderMaterial.call(this, parameters);
9 | }
10 |
11 | DustMaterial.prototype = Object.create(ShaderMaterial.prototype);
12 |
13 | DustMaterial.prototype.shader = {
14 | vertexShader : 'dust-vert',
15 | fragmentShader : 'dust-frag',
16 |
17 | uniforms : THREE.UniformsUtils.merge([
18 | uniforms.common,
19 | uniforms.points,
20 | {
21 | time : { type : 'f', value : 0 },
22 | area : { type : 'f', value : 1 }
23 | }
24 | ])
25 | };
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Numerous always-ignore extensions
2 | *.diff
3 | *.err
4 | *.orig
5 | *.rej
6 | *.swo
7 | *.swp
8 | *.vi
9 | *~
10 | *.psd
11 |
12 | # OS or Editor folders
13 | .DS_Store
14 | Thumbs.db
15 | .cache
16 | .project
17 | .pydevproject
18 | .settings
19 | .tmproj
20 | *.esproj
21 | nbproject
22 | *.sublime-project
23 | *.sublime-workspace
24 |
25 | # Translations
26 | *.mo
27 |
28 | # Django
29 | *.log
30 | *.pot
31 | *.pyc
32 | local_settings.py
33 |
34 | # Installer logs
35 | pip-log.txt
36 |
37 | # Unit test / coverage reports
38 | .coverage
39 | .tox
40 |
41 | # Compiled static files
42 | build
43 | index.html
44 |
45 | # npm
46 | node_modules
47 |
48 | # Bower
49 | static/lib
50 |
51 | # Sass
52 | .sass-cache
53 | .temp
54 |
--------------------------------------------------------------------------------
/static/js/materials/TentacleMaterial.js:
--------------------------------------------------------------------------------
1 | require('./ShaderMaterial');
2 | var ShaderMaterial = App.ShaderMaterial;
3 | var uniforms = THREE.UniformsLib;
4 |
5 | App.TentacleMaterial = TentacleMaterial;
6 | function TentacleMaterial(parameters) {
7 | parameters = parameters || {};
8 | ShaderMaterial.call(this, parameters);
9 | }
10 |
11 | TentacleMaterial.prototype = Object.create(ShaderMaterial.prototype);
12 |
13 | TentacleMaterial.prototype.shader = {
14 | vertexShader : 'tentacle-vert',
15 | fragmentShader : 'tentacle-frag',
16 |
17 | uniforms : THREE.UniformsUtils.merge([
18 | uniforms.common,
19 | {
20 | stepProgress : { type : 'f', value : 0 },
21 | area : { type : 'f', value : 1 }
22 | }
23 | ])
24 | };
25 |
--------------------------------------------------------------------------------
/grunt/config/neuter.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | // https://github.com/trek/grunt-neuter
5 |
6 | // Concatenate files in the order you require.
7 |
8 | module.exports = function (config) {
9 | return {
10 | options : {
11 | basePath : config.source
12 | },
13 | app: {
14 | src: config.source + 'js/app.js',
15 | dest: config.static + 'js/app.develop.js'
16 | },
17 | tests: {
18 | src: config.source + 'tests/tests.js',
19 | dest: config.static + 'js/tests.js'
20 | },
21 | libs: {
22 | options: {
23 | template : '{%= src %}'
24 | },
25 | src: config.source + 'js/libs.js',
26 | dest: config.static + 'js/libs.develop.js'
27 | }
28 | };
29 | };
30 |
--------------------------------------------------------------------------------
/static/js/materials/LerpPointMaterial.js:
--------------------------------------------------------------------------------
1 | require('./ShaderMaterial');
2 | var ShaderMaterial = App.ShaderMaterial;
3 | var uniforms = THREE.UniformsLib;
4 |
5 | App.LerpPointMaterial = LerpPointMaterial;
6 | function LerpPointMaterial(parameters) {
7 | parameters = parameters || {};
8 | parameters.sizeAttenuation = true;
9 | ShaderMaterial.call(this, parameters);
10 | }
11 |
12 | LerpPointMaterial.prototype = Object.create(ShaderMaterial.prototype);
13 |
14 | LerpPointMaterial.prototype.shader = {
15 | vertexShader : 'lerp-point-vert',
16 | fragmentShader : 'basic-point-frag',
17 |
18 | uniforms : THREE.UniformsUtils.merge([
19 | uniforms.points,
20 | {
21 | stepProgress : { type : 'f', value : 0 }
22 | }
23 | ])
24 | };
25 |
--------------------------------------------------------------------------------
/static/glsl/shaders/basic-point-frag.glsl:
--------------------------------------------------------------------------------
1 | uniform vec3 psColor;
2 | uniform float opacity;
3 |
4 | {{{chunks.common}}}
5 | {{{chunks.color_pars_fragment}}}
6 | {{{chunks.map_particle_pars_fragment}}}
7 | {{{chunks.fog_pars_fragment}}}
8 | {{{chunks.shadowmap_pars_fragment}}}
9 | {{{chunks.logdepthbuf_pars_fragment}}}
10 |
11 | void main() {
12 | vec3 outgoingLight = vec3(0.0);
13 | vec4 diffuseColor = vec4(psColor, opacity);
14 |
15 | {{{chunks.logdepthbuf_fragment}}}
16 | {{{chunks.map_particle_fragment}}}
17 | {{{chunks.color_fragment}}}
18 | {{{chunks.alphatest_fragment}}}
19 |
20 | outgoingLight = diffuseColor.rgb;
21 |
22 | {{{chunks.shadowmap_fragment}}}
23 | {{{chunks.fog_fragment}}}
24 |
25 | gl_FragColor = vec4(outgoingLight, diffuseColor.a);
26 | }
27 |
--------------------------------------------------------------------------------
/grunt/config/sass.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | // https://github.com/sindresorhus/grunt-sass
5 |
6 | // Compile Sass to CSS using node-sass.
7 |
8 | module.exports = function (config) {
9 | return {
10 | options: {
11 | includePaths: [
12 | config.source + 'lib'
13 | ]
14 | },
15 | build: {
16 | files : [{
17 | expand: true,
18 | cwd : config.source + 'scss/',
19 | src: '*.scss',
20 | dest: '.temp/css/',
21 | ext: '.css'
22 | }],
23 | options : {
24 | outputStyle : 'compressed'
25 | }
26 | },
27 | develop: {
28 | files : '<%= sass.build.files %>',
29 | options : {
30 | sourceComments : 'map'
31 | }
32 | }
33 | };
34 | };
35 |
--------------------------------------------------------------------------------
/static/js/materials/BulbMaterial.js:
--------------------------------------------------------------------------------
1 | require('./ShaderMaterial');
2 | var ShaderMaterial = App.ShaderMaterial;
3 | var uniforms = THREE.UniformsLib;
4 |
5 | App.BulbMaterial = BulbMaterial;
6 | function BulbMaterial(parameters) {
7 | parameters = parameters || {};
8 | parameters.map = true;
9 | ShaderMaterial.call(this, parameters);
10 | }
11 |
12 | BulbMaterial.prototype = Object.create(ShaderMaterial.prototype);
13 |
14 | BulbMaterial.prototype.shader = {
15 | vertexShader : 'normal-vert',
16 | fragmentShader : 'bulb-frag',
17 |
18 | uniforms : THREE.UniformsUtils.merge([
19 | uniforms.common,
20 | {
21 | diffuseB : { type : 'c', value : null },
22 | stepProgress : { type : 'f', value : 0 },
23 | time : { type : 'f', value : 0 }
24 | }
25 | ])
26 | };
27 |
--------------------------------------------------------------------------------
/static/js/materials/TailMaterial.js:
--------------------------------------------------------------------------------
1 | require('./ShaderMaterial');
2 | var ShaderMaterial = App.ShaderMaterial;
3 | var uniforms = THREE.UniformsLib;
4 |
5 | App.TailMaterial = TailMaterial;
6 | function TailMaterial(parameters) {
7 | parameters = parameters || {};
8 | parameters.map = true;
9 | ShaderMaterial.call(this, parameters);
10 | }
11 |
12 | TailMaterial.prototype = Object.create(ShaderMaterial.prototype);
13 |
14 | TailMaterial.prototype.shader = {
15 | vertexShader : 'normal-vert',
16 | fragmentShader : 'tail-frag',
17 |
18 | uniforms : THREE.UniformsUtils.merge([
19 | uniforms.common,
20 | {
21 | diffuseB : { type : 'c', value : null },
22 | scale : { type : 'f', value : 1 },
23 | stepProgress : { type : 'f', value : 0 }
24 | }
25 | ])
26 | };
27 |
--------------------------------------------------------------------------------
/grunt/config/copy.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | // https://github.com/gruntjs/grunt-contrib-copy
5 |
6 | // Copy files and folders.
7 |
8 | module.exports = function (config) {
9 | return {
10 | build: {
11 | expand: true,
12 | src: [
13 | config.source + '.htaccess',
14 | config.source + 'img/{,*/}*.{jpg,jpeg,png,webp,gif,ico}',
15 | config.source + 'audio/{,*/}*.{mp3,ogg,wav}',
16 | config.source + 'fonts/*',
17 | config.source + 'lib/modernizr/modernizr.js'
18 | ],
19 | dest: config.deploy
20 | },
21 | develop: {
22 | expand: true,
23 | src: [
24 | config.source + 'scss/**/*.scss',
25 | config.source + 'lib/**/*.js'
26 | ],
27 | dest: config.deploy
28 | }
29 | };
30 | };
31 |
--------------------------------------------------------------------------------
/static/glsl/shaders/alpha-frag.glsl:
--------------------------------------------------------------------------------
1 | uniform vec3 diffuse;
2 | uniform float opacity;
3 | varying float vAlpha;
4 |
5 | {{{chunks.common}}}
6 | {{{chunks.color_pars_fragment}}}
7 | {{{chunks.uv_pars_fragment}}}
8 | {{{chunks.map_pars_fragment}}}
9 | {{{chunks.logdepthbuf_pars_fragment}}}
10 |
11 | void main() {
12 | vec3 outgoingLight = vec3(0.0);
13 | vec4 diffuseColor = vec4(diffuse, opacity * vAlpha);
14 | vec3 totalAmbientLight = vec3(1.0); // hardwired
15 | vec3 shadowMask = vec3(1.0);
16 |
17 | {{{chunks.logdepthbuf_fragment}}}
18 | {{{chunks.map_fragment}}}
19 | {{{chunks.color_fragment}}}
20 | {{{chunks.alphatest_fragment}}}
21 |
22 | outgoingLight = diffuseColor.rgb * totalAmbientLight;
23 |
24 | {{{chunks.linear_to_gamma_fragment}}}
25 |
26 | gl_FragColor = vec4(outgoingLight, diffuseColor.a);
27 | }
28 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/CopyShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Full-screen textured quad shader
5 | */
6 |
7 | THREE.CopyShader = {
8 |
9 | uniforms: {
10 |
11 | "tDiffuse": { type: "t", value: null },
12 | "opacity": { type: "f", value: 1.0 }
13 |
14 | },
15 |
16 | vertexShader: [
17 |
18 | "varying vec2 vUv;",
19 |
20 | "void main() {",
21 |
22 | "vUv = uv;",
23 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
24 |
25 | "}"
26 |
27 | ].join("\n"),
28 |
29 | fragmentShader: [
30 |
31 | "uniform float opacity;",
32 |
33 | "uniform sampler2D tDiffuse;",
34 |
35 | "varying vec2 vUv;",
36 |
37 | "void main() {",
38 |
39 | "vec4 texel = texture2D( tDiffuse, vUv );",
40 | "gl_FragColor = opacity * texel;",
41 |
42 | "}"
43 |
44 | ].join("\n")
45 |
46 | };
47 |
--------------------------------------------------------------------------------
/static/tests/assert/close-enough.js:
--------------------------------------------------------------------------------
1 | Test.assert.close = assert_close;
2 | function assert_close(actual, expected, maxDifference, message) {
3 | var passes = (actual === expected) || Math.abs(actual - expected) <= maxDifference;
4 | QUnit.push(passes, actual, expected, message);
5 | }
6 |
7 | Test.assert.closeArray = assert_closeMany;
8 | function assert_closeMany(actual, expected, maxDifference, message) {
9 | var passes;
10 | for (var i = 0, il = actual.length; i < il; i ++) {
11 | passes = (actual[i] === expected[i]) || Math.abs(actual[i] - expected[i]) <= maxDifference;
12 | if (!passes) { break; }
13 | }
14 | QUnit.push(passes, actual, expected, message);
15 | }
16 |
17 | Test.assert.notClose = assert_notClose;
18 | function assert_notClose(actual, expected, minDifference, message) {
19 | QUnit.push(Math.abs(actual - expected) > minDifference, actual, expected, message);
20 | }
21 |
--------------------------------------------------------------------------------
/static/scss/apps/_application.scss:
--------------------------------------------------------------------------------
1 | body {
2 | @include absolute(0);
3 | width: 100%;
4 | height: 100%;
5 | background: #100A17;
6 | }
7 |
8 | .modal {
9 | $width: 300px;
10 | $height: 220px;
11 |
12 | position: absolute;
13 | z-index: 2;
14 | top: 50%;
15 | left: 50%;
16 |
17 | width: $width;
18 | padding: 20px;
19 | margin-left: -$width / 2;
20 | margin-top: -$height / 2;
21 |
22 | border-radius: 4px;
23 | background: rgba(#100A17, 0.9);
24 | opacity: 0;
25 | visibility: hidden;
26 | transform: scale(0.95);
27 | transition: all 200ms ease-in;
28 |
29 | &.active,
30 | &.active + .modal-cover {
31 | transform: scale(1);
32 | opacity: 1;
33 | visibility: visible;
34 | }
35 | }
36 |
37 | .modal-cover {
38 | @include absolute(0);
39 | z-index: 1;
40 | background: rgba(#100A17, 0.5);
41 | opacity: 0;
42 | visibility: hidden;
43 | transition: all 200ms ease-in;
44 | }
45 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "particulate-medusae",
3 | "private": true,
4 | "version": "0.0.0",
5 | "dependencies": {
6 | "three": "^0.72.0"
7 | },
8 | "devDependencies": {
9 | "grunt": "~0.4.5",
10 | "grunt-autoprefixer": "~3.0.3",
11 | "grunt-contrib-clean": "~0.7.0",
12 | "grunt-contrib-connect": "~0.11.2",
13 | "grunt-contrib-copy": "~0.8.2",
14 | "grunt-contrib-handlebars": "^0.11.0",
15 | "grunt-contrib-jshint": "~0.11.3",
16 | "grunt-contrib-uglify": "~0.11.0",
17 | "grunt-contrib-watch": "~0.6.1",
18 | "grunt-haychtml": "~0.1.1",
19 | "grunt-neuter": "~0.6.0",
20 | "grunt-newer": "~1.1.1",
21 | "grunt-notify": "~0.4.3",
22 | "grunt-sass": "~1.1.0",
23 | "jit-grunt": "~0.9.1",
24 | "swig": "~1.4.2",
25 | "time-grunt": "~1.2.2"
26 | },
27 | "engines": {
28 | "node": ">=0.8.0"
29 | },
30 | "scripts": {
31 | "postinstall": "sh postinstall.sh"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/LuminosityShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Luminosity
5 | * http://en.wikipedia.org/wiki/Luminosity
6 | */
7 |
8 | THREE.LuminosityShader = {
9 |
10 | uniforms: {
11 |
12 | "tDiffuse": { type: "t", value: null }
13 |
14 | },
15 |
16 | vertexShader: [
17 |
18 | "varying vec2 vUv;",
19 |
20 | "void main() {",
21 |
22 | "vUv = uv;",
23 |
24 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
25 |
26 | "}"
27 |
28 | ].join("\n"),
29 |
30 | fragmentShader: [
31 |
32 | "uniform sampler2D tDiffuse;",
33 |
34 | "varying vec2 vUv;",
35 |
36 | "void main() {",
37 |
38 | "vec4 texel = texture2D( tDiffuse, vUv );",
39 |
40 | "vec3 luma = vec3( 0.299, 0.587, 0.114 );",
41 |
42 | "float v = dot( texel.xyz, luma );",
43 |
44 | "gl_FragColor = vec4( v, v, v, texel.w );",
45 |
46 | "}"
47 |
48 | ].join("\n")
49 |
50 | };
51 |
--------------------------------------------------------------------------------
/static/glsl/shaders/basic-vert.glsl:
--------------------------------------------------------------------------------
1 | {{{chunks.common}}}
2 | {{{chunks.uv_pars_vertex}}}
3 | {{{chunks.uv2_pars_vertex}}}
4 | {{{chunks.envmap_pars_vertex}}}
5 | {{{chunks.color_pars_vertex}}}
6 | {{{chunks.morphtarget_pars_vertex}}}
7 | {{{chunks.skinning_pars_vertex}}}
8 | {{{chunks.shadowmap_pars_vertex}}}
9 | {{{chunks.logdepthbuf_pars_vertex}}}
10 |
11 | void main() {
12 | {{{chunks.uv_vertex}}}
13 | {{{chunks.uv2_vertex}}}
14 | {{{chunks.color_vertex}}}
15 | {{{chunks.skinbase_vertex}}}
16 |
17 | #ifdef USE_ENVMAP
18 | {{{chunks.beginnormal_vertex}}}
19 | {{{chunks.morphnormal_vertex}}}
20 | {{{chunks.skinnormal_vertex}}}
21 | {{{chunks.defaultnormal_vertex}}}
22 | #endif
23 |
24 | {{{chunks.begin_vertex}}}
25 | {{{chunks.morphtarget_vertex}}}
26 | {{{chunks.skinning_vertex}}}
27 | {{{chunks.project_vertex}}}
28 | {{{chunks.logdepthbuf_vertex}}}
29 |
30 | {{{chunks.worldpos_vertex}}}
31 | {{{chunks.envmap_vertex}}}
32 | {{{chunks.shadowmap_vertex}}}
33 | }
34 |
--------------------------------------------------------------------------------
/static/js/libs.js:
--------------------------------------------------------------------------------
1 | require('lib/native-promise-only/lib/npo.src');
2 | require('lib/handlebars/handlebars.runtime');
3 | require('lib/particulate/dist/particulate');
4 | require('lib/noise/perlin');
5 |
6 | require('lib-extras/three/controls/TrackballControls');
7 | require('lib-extras/three/geometries/PlaneBufferGeometry');
8 | require('lib-extras/three/geometries/SphereBufferGeometry');
9 |
10 | require('lib-extras/three/shaders/BasicShader');
11 | require('lib-extras/three/shaders/CopyShader');
12 | require('lib-extras/three/shaders/ConvolutionShader');
13 | require('lib-extras/three/shaders/VignetteShader');
14 |
15 | require('lib-extras/three/postprocessing/EffectComposer');
16 | require('lib-extras/three/postprocessing/RenderPass');
17 | require('lib-extras/three/postprocessing/SavePass');
18 | require('lib-extras/three/postprocessing/ShaderPass');
19 | require('lib-extras/three/postprocessing/TexturePass');
20 | require('lib-extras/three/postprocessing/MaskPass');
21 | require('lib-extras/three/postprocessing/BloomPass');
22 |
--------------------------------------------------------------------------------
/static/tests/post-processing/LensDirtPass.js:
--------------------------------------------------------------------------------
1 | module('Pass.LensDirt');
2 |
3 | var _slice = Array.prototype.slice;
4 | var equalArray = Test.assert.equalArray;
5 | var pass = App.LensDirtPass;
6 |
7 | test('Set quad uvs', function () {
8 | var count = 5;
9 | var cells = 2;
10 | var uvAttr = pass.prototype._quadGeomUv(count, cells);
11 | var uvArray = uvAttr.array;
12 |
13 | var c0 = 0;
14 | var c1 = 1 / cells;
15 | var c2 = c1 * 2;
16 |
17 | equalArray(_slice.call(uvArray, 0, 8),
18 | [c0, c0, c1, c0, c1, c1, c0, c1],
19 | 'first quad uvs');
20 | equalArray(_slice.call(uvArray, 8, 16),
21 | [c1, c0, c2, c0, c2, c1, c1, c1],
22 | 'second quad uvs');
23 | equalArray(_slice.call(uvArray, 16, 24),
24 | [c0, c1, c1, c1, c1, c2, c0, c2],
25 | 'third quad uvs');
26 | equalArray(_slice.call(uvArray, 24, 32),
27 | [c1, c1, c2, c1, c2, c2, c1, c2],
28 | 'fourth quad uvs');
29 | equalArray(_slice.call(uvArray, 32, 40),
30 | [c0, c0, c1, c0, c1, c1, c0, c1],
31 | 'fifth quad uvs');
32 | });
33 |
--------------------------------------------------------------------------------
/static/js/components/ModalComponent.js:
--------------------------------------------------------------------------------
1 | App.ModalComponent = ModalComponent;
2 | function ModalComponent(config) {
3 | var name = config.name;
4 | var cover = document.getElementById('cover-' + name);
5 | var toggle = this.toggle = document.getElementById('toggle-' + name);
6 | var modal = this.modal = document.getElementById(name);
7 |
8 | this.isActive = false;
9 | this._toggleClassName = toggle.className;
10 | this._modalClassName = modal.className;
11 |
12 | toggle.addEventListener('click', this.toggleState.bind(this), false);
13 | cover.addEventListener('click', this.toggleState.bind(this), false);
14 | }
15 |
16 | ModalComponent.create = App.ctor(ModalComponent);
17 |
18 | ModalComponent.prototype.toggleState = function (event) {
19 | if (this.isActive) {
20 | this.modal.className = this._modalClassName;
21 | this.toggle.className = this._toggleClassName;
22 | this.isActive = false;
23 | } else {
24 | this.modal.className += ' active';
25 | this.toggle.className += ' active';
26 | this.isActive = true;
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/static/js/utils/Dispatcher.js:
--------------------------------------------------------------------------------
1 | var Dispatcher = App.Dispatcher = {};
2 |
3 | Dispatcher.extend = function (proto) {
4 | proto.addListener = addListener;
5 | proto.triggerListeners = triggerListeners;
6 | };
7 |
8 | function addListener(type, context, fn) {
9 | var listeners = this._listeners;
10 | if (!listeners) { listeners = this._listeners = {}; }
11 | if (!listeners[type]) { listeners[type] = []; }
12 |
13 | listeners[type].push({
14 | context : context,
15 | fn : fn
16 | });
17 | }
18 |
19 | function triggerListeners(type, event) {
20 | var listeners = this._listeners && this._listeners[type];
21 | if (!listeners) { return; }
22 | var listener, context, fn;
23 |
24 | for (var i = 0, il = listeners.length; i < il; i ++) {
25 | listener = listeners[i];
26 | context = listener.context;
27 | fn = listener.fn;
28 |
29 | if (typeof context === 'function') {
30 | fn = context;
31 | context = null;
32 | } else if (typeof fn === 'string') {
33 | fn = context[fn];
34 | }
35 |
36 | fn.call(context, event);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/static/js/utils/Links.js:
--------------------------------------------------------------------------------
1 | var Links = App.Links = {};
2 |
3 | Links.line = function (index, howMany, buffer) {
4 | var a, b;
5 |
6 | for (var i = 0; i < howMany - 1; i ++) {
7 | a = index + i;
8 | b = index + i + 1;
9 |
10 | buffer.push(a, b);
11 | }
12 |
13 | return buffer;
14 | };
15 |
16 | Links.loop = function (index, howMany, buffer) {
17 | var a, b;
18 |
19 | for (var i = 0; i < howMany - 1; i ++) {
20 | a = index + i;
21 | b = index + i + 1;
22 |
23 | buffer.push(a, b);
24 | }
25 |
26 | a = index;
27 | b = index + howMany - 1;
28 |
29 | buffer.push(a, b);
30 |
31 | return buffer;
32 | };
33 |
34 | Links.rings = function (index0, index1, howMany, buffer) {
35 | var a, b;
36 |
37 | for (var i = 0; i < howMany; i ++) {
38 | a = index0 + i;
39 | b = index1 + i;
40 |
41 | buffer.push(a, b);
42 | }
43 |
44 | return buffer;
45 | };
46 |
47 | Links.radial = function (indexCenter, index, howMany, buffer) {
48 | var b;
49 |
50 | for (var i = 0; i < howMany; i ++) {
51 | b = index + i;
52 |
53 | buffer.push(indexCenter, b);
54 | }
55 |
56 | return buffer;
57 | };
58 |
--------------------------------------------------------------------------------
/grunt/tasks/shaderChunks.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 | 'use strict';
3 |
4 | var path = require('path');
5 |
6 | module.exports = function (grunt) {
7 | grunt.config('shaderChunks', {
8 | develop : {
9 | src : 'static/glsl/shader-chunks/*',
10 | dest : 'build/static/js/shader-chunks.develop.js'
11 | }
12 | });
13 |
14 | grunt.registerMultiTask('shaderChunks', 'Concatenate shader chunks', function () {
15 | function handleFile(file) {
16 | var out = '';
17 | var src = path.resolve(file);
18 | var name = file.split('/').pop().split('.')[0];
19 | var source = grunt.file.read(src);
20 | var sourceLines = source.split('\n');
21 |
22 | out += 'THREE.ShaderChunk[\'' + name + '\'] = [\n';
23 |
24 | sourceLines.forEach(function (line) {
25 | line = line.trim();
26 | if (line.length && line.indexOf('//') !== 0) {
27 | out += '\'' + line.trim() + '\',\n';
28 | }
29 | });
30 |
31 | out += '].join(\'\\n\');';
32 | return out;
33 | }
34 |
35 | this.files.forEach(function (files) {
36 | var contents = files.src.map(handleFile).join('\n');
37 | grunt.file.write(path.resolve(files.dest), contents, { encoding : 'utf8' });
38 | });
39 | });
40 | };
41 |
--------------------------------------------------------------------------------
/static/tests/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "App" : true,
4 | "require" : true,
5 | "Particulate" : true,
6 | "Test" : true,
7 |
8 | "QUnit" : false,
9 | "asyncTest" : false,
10 | "deepEqual" : false,
11 | "equal" : false,
12 | "expect" : false,
13 | "module" : false,
14 | "notDeepEqual" : false,
15 | "notEqual" : false,
16 | "notStrictEqual" : false,
17 | "ok" : false,
18 | "raises" : false,
19 | "start" : false,
20 | "stop" : false,
21 | "strictEqual" : false,
22 | "test" : false,
23 | "throws" : false
24 | },
25 |
26 | "browser" : true,
27 | "curly" : true,
28 | "eqeqeq" : true,
29 | "eqnull" : true,
30 | "forin" : true,
31 | "indent" : 2,
32 | "latedef" : "nofunc",
33 | "maxcomplexity" : 10,
34 | "maxdepth" : 3,
35 | "newcap" : true,
36 | "noarg" : true,
37 | "quotmark" : "single",
38 | "undef" : true,
39 | "unused" : "vars"
40 | }
41 |
--------------------------------------------------------------------------------
/static/glsl/shaders/basic-frag.glsl:
--------------------------------------------------------------------------------
1 | uniform vec3 diffuse;
2 | uniform float opacity;
3 |
4 | {{{chunks.common}}}
5 | {{{chunks.color_pars_fragment}}}
6 | {{{chunks.uv_pars_fragment}}}
7 | {{{chunks.uv2_pars_fragment}}}
8 | {{{chunks.map_pars_fragment}}}
9 | {{{chunks.alphamap_pars_fragment}}}
10 | {{{chunks.aomap_pars_fragment}}}
11 | {{{chunks.envmap_pars_fragment}}}
12 | {{{chunks.fog_pars_fragment}}}
13 | {{{chunks.shadowmap_pars_fragment}}}
14 | {{{chunks.specularmap_pars_fragment}}}
15 | {{{chunks.logdepthbuf_pars_fragment}}}
16 |
17 | void main() {
18 | vec3 outgoingLight = vec3(0.0);
19 | vec4 diffuseColor = vec4(diffuse, opacity);
20 | vec3 totalAmbientLight = vec3(1.0);
21 |
22 | {{{chunks.logdepthbuf_fragment}}}
23 | {{{chunks.map_fragment}}}
24 | {{{chunks.color_fragment}}}
25 | {{{chunks.alphamap_fragment}}}
26 | {{{chunks.alphatest_fragment}}}
27 | {{{chunks.specularmap_fragment}}}
28 | {{{chunks.aomap_fragment}}}
29 |
30 | outgoingLight = diffuseColor.rgb * totalAmbientLight;
31 |
32 | {{{chunks.envmap_fragment}}}
33 | {{{chunks.shadowmap_fragment}}}
34 | {{{chunks.linear_to_gamma_fragment}}}
35 | {{{chunks.fog_fragment}}}
36 |
37 | gl_FragColor = vec4(outgoingLight, diffuseColor.a);
38 | }
39 |
--------------------------------------------------------------------------------
/static/glsl/shaders/tail-frag.glsl:
--------------------------------------------------------------------------------
1 | uniform vec3 diffuse;
2 | uniform vec3 diffuseB;
3 | uniform float opacity;
4 | uniform float scale;
5 | varying vec2 vUv;
6 | varying vec3 vNormal;
7 |
8 | const vec3 eye = vec3(0.0, 0.0, 1.0);
9 |
10 | float accumulate(vec2 uv, float saturation, float scale) {
11 | saturation -= sin(uv.y * 12.0 * scale) * 0.8 + uv.y * 1.5 + sin(uv.x * 20.0 * scale) * 0.1 + 0.85;
12 |
13 | saturation -= sin(uv.y * sin(uv.x * 5.0) * 5.0 * scale) * 0.05;
14 | saturation -= sin(uv.y * sin((1.0 - uv.x) * 5.0) * 5.0 * scale) * 0.05;
15 |
16 | saturation -= sin(uv.y * sin(uv.y + cos(uv.x) * 2.0) * 10.0 * scale) * 0.15;
17 | saturation -= sin(uv.y * sin(uv.y + cos(1.0 - uv.x) * 2.0) * 10.0 * scale) * 0.15;
18 |
19 | return saturation;
20 | }
21 |
22 | void main() {
23 | vec2 uv = vUv;
24 | vec3 normal = normalize(mat3(viewMatrix) * vNormal);
25 | float rim = 1.0 - max(dot(eye, normal), 0.0);
26 | float saturation = 0.0;
27 |
28 | saturation += accumulate(uv, 2.0, scale);
29 | saturation += max(accumulate(vec2(rim), 0.75, scale * 0.25), -0.25);
30 |
31 | gl_FragColor = vec4(
32 | mix(diffuseB, diffuse, saturation) * opacity,
33 | clamp(saturation, 0.2, 1.0) * opacity);
34 | }
35 |
--------------------------------------------------------------------------------
/static/tests/tests.js:
--------------------------------------------------------------------------------
1 | window.Test = { assert : {} };
2 |
3 | require('lib/qunit/qunit/qunit.js');
4 |
5 | require('./assert/*');
6 | require('./constraints/*');
7 | require('./forces/*');
8 | require('./post-processing/*');
9 |
10 | function setFavicon(uri) {
11 | var link = document.getElementById('favicon');
12 | if (link) {
13 | document.head.removeChild(link);
14 | } else {
15 | link = document.createElement('link');
16 | }
17 |
18 | link.setAttribute('id', 'favicon');
19 | link.setAttribute('type', 'image/x-icon');
20 | link.setAttribute('rel', 'icon');
21 | link.setAttribute('href', uri);
22 | document.head.appendChild(link);
23 | }
24 |
25 | QUnit.done(function (results) {
26 | if (results.failed) {
27 | setFavicon('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAANElEQVQ4T2NkoBAwUqifgboG/GdgACLCAGgr3GIUF4waAA7B0UCkNAwIp0FMFdTNC+S4AAAt7hQR+uwkyQAAAABJRU5ErkJggg==');
28 | document.title = results.failed + ' of ' + results.total + ' failed.';
29 | } else {
30 | setFavicon('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAM0lEQVQ4T2NkoBAwUqifgcoG/Gf4T5SLGBEWo7pg1AAGYBCOBiLFYUBUMkRVROW8QIYLACPuFBFvqDn4AAAAAElFTkSuQmCC');
31 | document.title = 'All ' + results.total + ' passed.';
32 | }
33 | });
34 |
--------------------------------------------------------------------------------
/static/js/utils/Faces.js:
--------------------------------------------------------------------------------
1 | var Faces = App.Faces = {};
2 |
3 | Faces.quad = function (a, b, c, d, buffer) {
4 | buffer.push(
5 | a, b, c,
6 | c, d, a);
7 |
8 | return buffer;
9 | };
10 |
11 | Faces.quadDoubleSide = function (a, b, c, d, buffer) {
12 | buffer.push(
13 | a, b, c,
14 | c, d, a,
15 | d, c, b,
16 | b, a, d);
17 |
18 | return buffer;
19 | };
20 |
21 | Faces.radial = function (indexCenter, index, howMany, buffer) {
22 | var b, c;
23 |
24 | for (var i = 0, il = howMany - 1; i < il; i ++) {
25 | b = index + i + 1;
26 | c = index + i;
27 |
28 | buffer.push(indexCenter, b, c);
29 | }
30 |
31 | b = index;
32 | c = index + howMany - 1;
33 |
34 | buffer.push(indexCenter, b, c);
35 |
36 | return buffer;
37 | };
38 |
39 | Faces.rings = function (index0, index1, howMany, buffer) {
40 | var a, b, c, d;
41 |
42 | for (var i = 0, il = howMany - 1; i < il; i ++) {
43 | a = index0 + i;
44 | b = index0 + i + 1;
45 | c = index1 + i + 1;
46 | d = index1 + i;
47 |
48 | buffer.push(
49 | a, b, c,
50 | c, d, a);
51 | }
52 |
53 | a = index0 + howMany - 1;
54 | b = index0;
55 | c = index1;
56 | d = index1 + howMany - 1;
57 |
58 | buffer.push(
59 | a, b, c,
60 | c, d, a);
61 |
62 | return buffer;
63 | };
64 |
--------------------------------------------------------------------------------
/static/lib-extras/three/postprocessing/TexturePass.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | */
4 |
5 | THREE.TexturePass = function ( texture, opacity ) {
6 |
7 | if ( THREE.CopyShader === undefined )
8 | console.error( "THREE.TexturePass relies on THREE.CopyShader" );
9 |
10 | var shader = THREE.CopyShader;
11 |
12 | this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
13 |
14 | this.uniforms[ "opacity" ].value = ( opacity !== undefined ) ? opacity : 1.0;
15 | this.uniforms[ "tDiffuse" ].value = texture;
16 |
17 | this.material = new THREE.ShaderMaterial( {
18 |
19 | uniforms: this.uniforms,
20 | vertexShader: shader.vertexShader,
21 | fragmentShader: shader.fragmentShader
22 |
23 | } );
24 |
25 | this.enabled = true;
26 | this.needsSwap = false;
27 |
28 |
29 | this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
30 | this.scene = new THREE.Scene();
31 |
32 | this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
33 | this.scene.add( this.quad );
34 |
35 | };
36 |
37 | THREE.TexturePass.prototype = {
38 |
39 | render: function ( renderer, writeBuffer, readBuffer, delta ) {
40 |
41 | this.quad.material = this.material;
42 |
43 | renderer.render( this.scene, this.camera, readBuffer );
44 |
45 | }
46 |
47 | };
48 |
--------------------------------------------------------------------------------
/static/js/forces/PointRepulsorForce.js:
--------------------------------------------------------------------------------
1 | var PMath = Particulate.Math;
2 | App.PointRepulsorForce = PointRepulsorForce;
3 |
4 | function PointRepulsorForce(position, opts) {
5 | opts = opts || {};
6 | Particulate.Force.apply(this, arguments);
7 |
8 | this.position = this.vector;
9 | this.intensity = opts.intensity != null ? opts.intensity : 0.05;
10 | this.setRadius(opts.radius || 0);
11 | }
12 |
13 | PointRepulsorForce.create = Particulate.ctor(PointRepulsorForce);
14 | PointRepulsorForce.prototype = Object.create(Particulate.Force.prototype);
15 | PointRepulsorForce.prototype.constructor = PointRepulsorForce;
16 |
17 | PointRepulsorForce.prototype.setRadius = function (r) {
18 | this._radius2 = r * r;
19 | };
20 |
21 | PointRepulsorForce.prototype.applyForce = function (ix, f0, p0, p1) {
22 | var v0 = this.vector;
23 | var iy = ix + 1;
24 | var iz = ix + 2;
25 |
26 | var dx = p0[ix] - v0[0];
27 | var dy = p0[iy] - v0[1];
28 | var dz = p0[iz] - v0[2];
29 |
30 | var dist = dx * dx + dy * dy + dz * dz;
31 | var diff = PMath.clamp(0.001, 100,
32 | dist - this._radius2 * this.intensity);
33 | var diffInv = 1 / diff;
34 | var scale = PMath.clamp(0, 10,
35 | diffInv * diffInv * diffInv);
36 |
37 | f0[ix] += dx * scale;
38 | f0[iy] += dy * scale;
39 | f0[iz] += dz * scale;
40 | };
41 |
--------------------------------------------------------------------------------
/static/lib-extras/three/postprocessing/RenderPass.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | */
4 |
5 | THREE.RenderPass = function ( scene, camera, overrideMaterial, clearColor, clearAlpha ) {
6 |
7 | this.scene = scene;
8 | this.camera = camera;
9 |
10 | this.overrideMaterial = overrideMaterial;
11 |
12 | this.clearColor = clearColor;
13 | this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 1;
14 |
15 | this.oldClearColor = new THREE.Color();
16 | this.oldClearAlpha = 1;
17 |
18 | this.enabled = true;
19 | this.clear = true;
20 | this.needsSwap = false;
21 |
22 | };
23 |
24 | THREE.RenderPass.prototype = {
25 |
26 | render: function ( renderer, writeBuffer, readBuffer, delta ) {
27 |
28 | this.scene.overrideMaterial = this.overrideMaterial;
29 |
30 | if ( this.clearColor ) {
31 |
32 | this.oldClearColor.copy( renderer.getClearColor() );
33 | this.oldClearAlpha = renderer.getClearAlpha();
34 |
35 | renderer.setClearColor( this.clearColor, this.clearAlpha );
36 |
37 | }
38 |
39 | renderer.render( this.scene, this.camera, readBuffer, this.clear );
40 |
41 | if ( this.clearColor ) {
42 |
43 | renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
44 |
45 | }
46 |
47 | this.scene.overrideMaterial = null;
48 |
49 | }
50 |
51 | };
52 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/NormalMapShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Normal map shader
5 | * - compute normals from heightmap
6 | */
7 |
8 | THREE.NormalMapShader = {
9 |
10 | uniforms: {
11 |
12 | "heightMap": { type: "t", value: null },
13 | "resolution": { type: "v2", value: new THREE.Vector2( 512, 512 ) },
14 | "scale": { type: "v2", value: new THREE.Vector2( 1, 1 ) },
15 | "height": { type: "f", value: 0.05 }
16 |
17 | },
18 |
19 | vertexShader: [
20 |
21 | "varying vec2 vUv;",
22 |
23 | "void main() {",
24 |
25 | "vUv = uv;",
26 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
27 |
28 | "}"
29 |
30 | ].join("\n"),
31 |
32 | fragmentShader: [
33 |
34 | "uniform float height;",
35 | "uniform vec2 resolution;",
36 | "uniform sampler2D heightMap;",
37 |
38 | "varying vec2 vUv;",
39 |
40 | "void main() {",
41 |
42 | "float val = texture2D( heightMap, vUv ).x;",
43 |
44 | "float valU = texture2D( heightMap, vUv + vec2( 1.0 / resolution.x, 0.0 ) ).x;",
45 | "float valV = texture2D( heightMap, vUv + vec2( 0.0, 1.0 / resolution.y ) ).x;",
46 |
47 | "gl_FragColor = vec4( ( 0.5 * normalize( vec3( val - valU, val - valV, height ) ) + 0.5 ), 1.0 );",
48 |
49 | "}"
50 |
51 | ].join("\n")
52 |
53 | };
54 |
--------------------------------------------------------------------------------
/static/js/constraints/LocalPlaneConstraint.js:
--------------------------------------------------------------------------------
1 | App.LocalPlaneConstraint = LocalPlaneConstraint;
2 | function LocalPlaneConstraint(planeA, planeB, planeC, a) {
3 | Particulate.PlaneConstraint.apply(this, arguments);
4 | this.distance = 0;
5 | }
6 |
7 | LocalPlaneConstraint.create = Particulate.ctor(LocalPlaneConstraint);
8 | LocalPlaneConstraint.prototype = Object.create(Particulate.PlaneConstraint.prototype);
9 | LocalPlaneConstraint.prototype.constructor = LocalPlaneConstraint;
10 |
11 | LocalPlaneConstraint.prototype.applyConstraint = function (index, p0, p1) {
12 | var b0 = this.bufferVec3;
13 | var ii = this.indices;
14 | var bi = ii[1], pi = ii[index + 3];
15 |
16 | var bix = bi * 3, biy = bix + 1, biz = bix + 2;
17 | var pix = pi * 3, piy = pix + 1, piz = pix + 2;
18 |
19 | if (index === 0) {
20 | this._calculateNormal(index, p0);
21 | }
22 |
23 | if (!this._hasNormal) { return; }
24 |
25 | // N (plane normal vector)
26 | var nX = b0[0];
27 | var nY = b0[1];
28 | var nZ = b0[2];
29 |
30 | // BP (B -> P)
31 | var opX = p0[pix] - p0[bix];
32 | var opY = p0[piy] - p0[biy];
33 | var opZ = p0[piz] - p0[biz];
34 |
35 | // Project BP onto normal vector N
36 | var pt = opX * nX + opY * nY + opZ * nZ;
37 | if (pt > this.distance) { return; }
38 |
39 | p0[pix] -= nX * pt;
40 | p0[piy] -= nY * pt;
41 | p0[piz] -= nZ * pt;
42 | };
43 |
--------------------------------------------------------------------------------
/static/js/utils/Tweens.js:
--------------------------------------------------------------------------------
1 | var Tweens = App.Tweens = {};
2 |
3 | Tweens.mapRange = function (a0, a1, b0, b1) {
4 | if (arguments.length === 2) {
5 | b1 = a1[1];
6 | b0 = a1[0];
7 | a1 = a0[1];
8 | a0 = a0[0];
9 | }
10 |
11 | var rangeAInv = 1 / (a1 - a0);
12 | var rangeB = b1 - b0;
13 |
14 | return function (x) {
15 | var t = (x - a0) * rangeAInv;
16 | return b0 + t * rangeB;
17 | };
18 | };
19 |
20 | // Tween to target by difference factor
21 | Tweens.factorTween = function (context, defaultFactor) {
22 | return function (name, target, instanceFactor) {
23 | var state = context[name];
24 | if (state == null) { state = context[name] = target; }
25 | var factor = instanceFactor || defaultFactor;
26 |
27 | return context[name] += (target - state) * factor;
28 | };
29 | };
30 |
31 | // Tween to target by fixed step
32 | Tweens.stepTween = function (context, defaultStep) {
33 | return function (name, target, instanceStep) {
34 | var state = context[name];
35 | if (state == null) { state = context[name] = target; }
36 | if (state === target) { return state; }
37 | var step = instanceStep || defaultStep;
38 | var dir = state < target ? 1 : -1;
39 |
40 | if ((target - state) * dir < step) {
41 | context[name] = target;
42 | return state;
43 | }
44 |
45 | return context[name] += step * dir;
46 | };
47 | };
48 |
--------------------------------------------------------------------------------
/static/lib-extras/three/postprocessing/ShaderPass.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | */
4 |
5 | THREE.ShaderPass = function ( shader, textureID ) {
6 |
7 | this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse";
8 |
9 | this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
10 |
11 | this.material = new THREE.ShaderMaterial( {
12 |
13 | uniforms: this.uniforms,
14 | vertexShader: shader.vertexShader,
15 | fragmentShader: shader.fragmentShader
16 |
17 | } );
18 |
19 | this.renderToScreen = false;
20 |
21 | this.enabled = true;
22 | this.needsSwap = true;
23 | this.clear = false;
24 |
25 |
26 | this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
27 | this.scene = new THREE.Scene();
28 |
29 | this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
30 | this.scene.add( this.quad );
31 |
32 | };
33 |
34 | THREE.ShaderPass.prototype = {
35 |
36 | render: function ( renderer, writeBuffer, readBuffer, delta ) {
37 |
38 | if ( this.uniforms[ this.textureID ] ) {
39 |
40 | this.uniforms[ this.textureID ].value = readBuffer;
41 |
42 | }
43 |
44 | this.quad.material = this.material;
45 |
46 | if ( this.renderToScreen ) {
47 |
48 | renderer.render( this.scene, this.camera );
49 |
50 | } else {
51 |
52 | renderer.render( this.scene, this.camera, writeBuffer, this.clear );
53 |
54 | }
55 |
56 | }
57 |
58 | };
59 |
--------------------------------------------------------------------------------
/grunt/config/watch.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | // https://github.com/gruntjs/grunt-contrib-watch
5 |
6 | // Run tasks whenever files are changed, added, or deleted.
7 |
8 | module.exports = function (config) {
9 | return {
10 | compass: {
11 | files: [config.source + 'scss/{,**/}{,*.scss,*.sass}'],
12 | tasks: ['sass:develop', 'autoprefixer:develop', 'newer:copy:develop']
13 | },
14 | haychtml: {
15 | files: [config.pages + '{,**/}*.html'],
16 | tasks: ['haychtml:develop']
17 | },
18 | neuter: {
19 | files: [config.source + 'js/{,**/}{,*.js}'],
20 | tasks: ['neuter:app']
21 | },
22 | neuterLibs: {
23 | files: [config.source + 'js/libs.js'],
24 | tasks: ['neuter:libs']
25 | },
26 | neuterTests: {
27 | files: [config.source + 'tests/{,**/}{,*.js}'],
28 | tasks: ['neuter:tests']
29 | },
30 | shaderChunks: {
31 | files: config.source + 'glsl/shader-chunks/*',
32 | tasks: ['shaderChunks']
33 | },
34 | handlebarsShaders: {
35 | files: config.source + 'glsl/shaders/*',
36 | tasks: ['handlebars:shaders']
37 | },
38 | copy : {
39 | files: '<%= copy.build.src %>',
40 | tasks: ['newer:copy:build']
41 | },
42 | livereload: {
43 | options: {
44 | debounceDelay: 250,
45 | livereload: 38000
46 | },
47 | files: config.deploy + '**/*.{html,css,js,png,jpg,jpeg,gif,webp,svg}'
48 | }
49 | };
50 | };
51 |
--------------------------------------------------------------------------------
/static/js/utils/Looper.js:
--------------------------------------------------------------------------------
1 | /*global requestAnimationFrame*/
2 | App.Looper = Looper;
3 | function Looper(context, update, render, delta) {
4 | var _this = this;
5 | var _update = context[update];
6 | var _render = context[render];
7 |
8 | var stepTime = 0;
9 | var targetDelta = delta || (1 / 30 * 1000);
10 | var maxDelta = targetDelta;
11 |
12 | var isLooping = false;
13 | var lastTime;
14 |
15 | function animateStep(delta) {
16 | stepTime += delta;
17 | var steps = Math.floor(stepTime / targetDelta);
18 |
19 | if (steps > 0) {
20 | stepTime -= steps * targetDelta;
21 | _this.didUpdate = true;
22 | }
23 |
24 | while (steps > 0) {
25 | _update.call(context, targetDelta);
26 | steps --;
27 | }
28 |
29 | var stepProgress = stepTime / targetDelta;
30 | _render.call(context, targetDelta, stepProgress);
31 | }
32 |
33 | function animate() {
34 | if (!isLooping) { return; }
35 | var time = Date.now();
36 | var delta = Math.min(maxDelta, time - lastTime);
37 |
38 | _this.didUpdate = false;
39 | animateStep(delta);
40 | requestAnimationFrame(animate);
41 | lastTime = time;
42 | }
43 |
44 | this.stop = function () {
45 | isLooping = false;
46 | };
47 |
48 | this.start = function () {
49 | lastTime = Date.now();
50 | isLooping = true;
51 | animate();
52 | };
53 |
54 | this.toggle = function () {
55 | if (isLooping) { this.stop(); }
56 | else { this.start(); }
57 | };
58 | }
59 |
60 | Looper.create = App.ctor(Looper);
61 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/BleachBypassShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Bleach bypass shader [http://en.wikipedia.org/wiki/Bleach_bypass]
5 | * - based on Nvidia example
6 | * http://developer.download.nvidia.com/shaderlibrary/webpages/shader_library.html#post_bleach_bypass
7 | */
8 |
9 | THREE.BleachBypassShader = {
10 |
11 | uniforms: {
12 |
13 | "tDiffuse": { type: "t", value: null },
14 | "opacity": { type: "f", value: 1.0 }
15 |
16 | },
17 |
18 | vertexShader: [
19 |
20 | "varying vec2 vUv;",
21 |
22 | "void main() {",
23 |
24 | "vUv = uv;",
25 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
26 |
27 | "}"
28 |
29 | ].join("\n"),
30 |
31 | fragmentShader: [
32 |
33 | "uniform float opacity;",
34 |
35 | "uniform sampler2D tDiffuse;",
36 |
37 | "varying vec2 vUv;",
38 |
39 | "void main() {",
40 |
41 | "vec4 base = texture2D( tDiffuse, vUv );",
42 |
43 | "vec3 lumCoeff = vec3( 0.25, 0.65, 0.1 );",
44 | "float lum = dot( lumCoeff, base.rgb );",
45 | "vec3 blend = vec3( lum );",
46 |
47 | "float L = min( 1.0, max( 0.0, 10.0 * ( lum - 0.45 ) ) );",
48 |
49 | "vec3 result1 = 2.0 * base.rgb * blend;",
50 | "vec3 result2 = 1.0 - 2.0 * ( 1.0 - blend ) * ( 1.0 - base.rgb );",
51 |
52 | "vec3 newColor = mix( result1, result2, L );",
53 |
54 | "float A2 = opacity * base.a;",
55 | "vec3 mixRGB = A2 * newColor.rgb;",
56 | "mixRGB += ( ( 1.0 - A2 ) * base.rgb );",
57 |
58 | "gl_FragColor = vec4( mixRGB, base.a );",
59 |
60 | "}"
61 |
62 | ].join("\n")
63 |
64 | };
65 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/VignetteShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Vignette shader
5 | * based on PaintEffect postprocess from ro.me
6 | * http://code.google.com/p/3-dreams-of-black/source/browse/deploy/js/effects/PaintEffect.js
7 | */
8 |
9 | THREE.VignetteShader = {
10 |
11 | uniforms: {
12 |
13 | "tDiffuse": { type: "t", value: null },
14 | "offset": { type: "f", value: 1.0 },
15 | "darkness": { type: "f", value: 1.0 },
16 | "color": { type: "c", value: null }
17 |
18 | },
19 |
20 | vertexShader: [
21 |
22 | "varying vec2 vUv;",
23 |
24 | "void main() {",
25 |
26 | "vUv = uv;",
27 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
28 |
29 | "}"
30 |
31 | ].join("\n"),
32 |
33 | fragmentShader: [
34 |
35 | "uniform float offset;",
36 | "uniform float darkness;",
37 | "uniform vec3 color;",
38 |
39 | "uniform sampler2D tDiffuse;",
40 |
41 | "varying vec2 vUv;",
42 |
43 | "void main() {",
44 |
45 | // Eskil's vignette
46 |
47 | "vec4 texel = texture2D( tDiffuse, vUv );",
48 | "vec2 uv = ( vUv - vec2( 0.5 ) ) * vec2( offset );",
49 | "gl_FragColor = vec4( mix( texel.rgb, vec3( 1.0 - darkness ) * color, dot( uv, uv ) ), texel.a );",
50 |
51 | /*
52 | // alternative version from glfx.js
53 | // this one makes more "dusty" look (as opposed to "burned")
54 |
55 | "vec4 color = texture2D( tDiffuse, vUv );",
56 | "float dist = distance( vUv, vec2( 0.5 ) );",
57 | "color.rgb *= smoothstep( 0.8, offset * 0.799, dist *( darkness + offset ) );",
58 | "gl_FragColor = color;",
59 | */
60 |
61 | "}"
62 |
63 | ].join("\n")
64 |
65 | };
66 |
--------------------------------------------------------------------------------
/static/lib-extras/three/postprocessing/SavePass.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | */
4 |
5 | THREE.SavePass = function ( renderTarget ) {
6 |
7 | if ( THREE.CopyShader === undefined )
8 | console.error( "THREE.SavePass relies on THREE.CopyShader" );
9 |
10 | var shader = THREE.CopyShader;
11 |
12 | this.textureID = "tDiffuse";
13 |
14 | this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
15 |
16 | this.material = new THREE.ShaderMaterial( {
17 |
18 | uniforms: this.uniforms,
19 | vertexShader: shader.vertexShader,
20 | fragmentShader: shader.fragmentShader
21 |
22 | } );
23 |
24 | this.renderTarget = renderTarget;
25 |
26 | if ( this.renderTarget === undefined ) {
27 |
28 | this.renderTargetParameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };
29 | this.renderTarget = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, this.renderTargetParameters );
30 |
31 | }
32 |
33 | this.enabled = true;
34 | this.needsSwap = false;
35 | this.clear = false;
36 |
37 |
38 | this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
39 | this.scene = new THREE.Scene();
40 |
41 | this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
42 | this.scene.add( this.quad );
43 |
44 | };
45 |
46 | THREE.SavePass.prototype = {
47 |
48 | render: function ( renderer, writeBuffer, readBuffer, delta ) {
49 |
50 | if ( this.uniforms[ this.textureID ] ) {
51 |
52 | this.uniforms[ this.textureID ].value = readBuffer;
53 |
54 | }
55 |
56 | this.quad.material = this.material;
57 |
58 | renderer.render( this.scene, this.camera, this.renderTarget, this.clear );
59 |
60 | }
61 |
62 | };
63 |
--------------------------------------------------------------------------------
/static/lib-extras/three/postprocessing/FilmPass.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | */
4 |
5 | THREE.FilmPass = function ( noiseIntensity, scanlinesIntensity, scanlinesCount, grayscale ) {
6 |
7 | if ( THREE.FilmShader === undefined )
8 | console.error( "THREE.FilmPass relies on THREE.FilmShader" );
9 |
10 | var shader = THREE.FilmShader;
11 |
12 | this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
13 |
14 | this.material = new THREE.ShaderMaterial( {
15 |
16 | uniforms: this.uniforms,
17 | vertexShader: shader.vertexShader,
18 | fragmentShader: shader.fragmentShader
19 |
20 | } );
21 |
22 | if ( grayscale !== undefined ) this.uniforms.grayscale.value = grayscale;
23 | if ( noiseIntensity !== undefined ) this.uniforms.nIntensity.value = noiseIntensity;
24 | if ( scanlinesIntensity !== undefined ) this.uniforms.sIntensity.value = scanlinesIntensity;
25 | if ( scanlinesCount !== undefined ) this.uniforms.sCount.value = scanlinesCount;
26 |
27 | this.enabled = true;
28 | this.renderToScreen = false;
29 | this.needsSwap = true;
30 |
31 |
32 | this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
33 | this.scene = new THREE.Scene();
34 |
35 | this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
36 | this.scene.add( this.quad );
37 |
38 | };
39 |
40 | THREE.FilmPass.prototype = {
41 |
42 | render: function ( renderer, writeBuffer, readBuffer, delta ) {
43 |
44 | this.uniforms[ "tDiffuse" ].value = readBuffer;
45 | this.uniforms[ "time" ].value += delta;
46 |
47 | this.quad.material = this.material;
48 |
49 | if ( this.renderToScreen ) {
50 |
51 | renderer.render( this.scene, this.camera );
52 |
53 | } else {
54 |
55 | renderer.render( this.scene, this.camera, writeBuffer, false );
56 |
57 | }
58 |
59 | }
60 |
61 | };
62 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/HueSaturationShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author tapio / http://tapio.github.com/
3 | *
4 | * Hue and saturation adjustment
5 | * https://github.com/evanw/glfx.js
6 | * hue: -1 to 1 (-1 is 180 degrees in the negative direction, 0 is no change, etc.
7 | * saturation: -1 to 1 (-1 is solid gray, 0 is no change, and 1 is maximum contrast)
8 | */
9 |
10 | THREE.HueSaturationShader = {
11 |
12 | uniforms: {
13 |
14 | "tDiffuse": { type: "t", value: null },
15 | "hue": { type: "f", value: 0 },
16 | "saturation": { type: "f", value: 0 }
17 |
18 | },
19 |
20 | vertexShader: [
21 |
22 | "varying vec2 vUv;",
23 |
24 | "void main() {",
25 |
26 | "vUv = uv;",
27 |
28 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
29 |
30 | "}"
31 |
32 | ].join("\n"),
33 |
34 | fragmentShader: [
35 |
36 | "uniform sampler2D tDiffuse;",
37 | "uniform float hue;",
38 | "uniform float saturation;",
39 |
40 | "varying vec2 vUv;",
41 |
42 | "void main() {",
43 |
44 | "gl_FragColor = texture2D( tDiffuse, vUv );",
45 |
46 | // hue
47 | "float angle = hue * 3.14159265;",
48 | "float s = sin(angle), c = cos(angle);",
49 | "vec3 weights = (vec3(2.0 * c, -sqrt(3.0) * s - c, sqrt(3.0) * s - c) + 1.0) / 3.0;",
50 | "float len = length(gl_FragColor.rgb);",
51 | "gl_FragColor.rgb = vec3(",
52 | "dot(gl_FragColor.rgb, weights.xyz),",
53 | "dot(gl_FragColor.rgb, weights.zxy),",
54 | "dot(gl_FragColor.rgb, weights.yzx)",
55 | ");",
56 |
57 | // saturation
58 | "float average = (gl_FragColor.r + gl_FragColor.g + gl_FragColor.b) / 3.0;",
59 | "if (saturation > 0.0) {",
60 | "gl_FragColor.rgb += (average - gl_FragColor.rgb) * (1.0 - 1.0 / (1.001 - saturation));",
61 | "} else {",
62 | "gl_FragColor.rgb += (average - gl_FragColor.rgb) * (-saturation);",
63 | "}",
64 |
65 | "}"
66 |
67 | ].join("\n")
68 |
69 | };
70 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/VerticalBlurShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author zz85 / http://www.lab4games.net/zz85/blog
3 | *
4 | * Two pass Gaussian blur filter (horizontal and vertical blur shaders)
5 | * - described in http://www.gamerendering.com/2008/10/11/gaussian-blur-filter-shader/
6 | * and used in http://www.cake23.de/traveling-wavefronts-lit-up.html
7 | *
8 | * - 9 samples per pass
9 | * - standard deviation 2.7
10 | * - "h" and "v" parameters should be set to "1 / width" and "1 / height"
11 | */
12 |
13 | THREE.VerticalBlurShader = {
14 |
15 | uniforms: {
16 |
17 | "tDiffuse": { type: "t", value: null },
18 | "v": { type: "f", value: 1.0 / 512.0 }
19 |
20 | },
21 |
22 | vertexShader: [
23 |
24 | "varying vec2 vUv;",
25 |
26 | "void main() {",
27 |
28 | "vUv = uv;",
29 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
30 |
31 | "}"
32 |
33 | ].join("\n"),
34 |
35 | fragmentShader: [
36 |
37 | "uniform sampler2D tDiffuse;",
38 | "uniform float v;",
39 |
40 | "varying vec2 vUv;",
41 |
42 | "void main() {",
43 |
44 | "vec4 sum = vec4( 0.0 );",
45 |
46 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 4.0 * v ) ) * 0.051;",
47 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 3.0 * v ) ) * 0.0918;",
48 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 2.0 * v ) ) * 0.12245;",
49 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 1.0 * v ) ) * 0.1531;",
50 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y ) ) * 0.1633;",
51 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 1.0 * v ) ) * 0.1531;",
52 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 2.0 * v ) ) * 0.12245;",
53 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 3.0 * v ) ) * 0.0918;",
54 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 4.0 * v ) ) * 0.051;",
55 |
56 | "gl_FragColor = sum;",
57 |
58 | "}"
59 |
60 | ].join("\n")
61 |
62 | };
63 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/HorizontalBlurShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author zz85 / http://www.lab4games.net/zz85/blog
3 | *
4 | * Two pass Gaussian blur filter (horizontal and vertical blur shaders)
5 | * - described in http://www.gamerendering.com/2008/10/11/gaussian-blur-filter-shader/
6 | * and used in http://www.cake23.de/traveling-wavefronts-lit-up.html
7 | *
8 | * - 9 samples per pass
9 | * - standard deviation 2.7
10 | * - "h" and "v" parameters should be set to "1 / width" and "1 / height"
11 | */
12 |
13 | THREE.HorizontalBlurShader = {
14 |
15 | uniforms: {
16 |
17 | "tDiffuse": { type: "t", value: null },
18 | "h": { type: "f", value: 1.0 / 512.0 }
19 |
20 | },
21 |
22 | vertexShader: [
23 |
24 | "varying vec2 vUv;",
25 |
26 | "void main() {",
27 |
28 | "vUv = uv;",
29 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
30 |
31 | "}"
32 |
33 | ].join("\n"),
34 |
35 | fragmentShader: [
36 |
37 | "uniform sampler2D tDiffuse;",
38 | "uniform float h;",
39 |
40 | "varying vec2 vUv;",
41 |
42 | "void main() {",
43 |
44 | "vec4 sum = vec4( 0.0 );",
45 |
46 | "sum += texture2D( tDiffuse, vec2( vUv.x - 4.0 * h, vUv.y ) ) * 0.051;",
47 | "sum += texture2D( tDiffuse, vec2( vUv.x - 3.0 * h, vUv.y ) ) * 0.0918;",
48 | "sum += texture2D( tDiffuse, vec2( vUv.x - 2.0 * h, vUv.y ) ) * 0.12245;",
49 | "sum += texture2D( tDiffuse, vec2( vUv.x - 1.0 * h, vUv.y ) ) * 0.1531;",
50 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y ) ) * 0.1633;",
51 | "sum += texture2D( tDiffuse, vec2( vUv.x + 1.0 * h, vUv.y ) ) * 0.1531;",
52 | "sum += texture2D( tDiffuse, vec2( vUv.x + 2.0 * h, vUv.y ) ) * 0.12245;",
53 | "sum += texture2D( tDiffuse, vec2( vUv.x + 3.0 * h, vUv.y ) ) * 0.0918;",
54 | "sum += texture2D( tDiffuse, vec2( vUv.x + 4.0 * h, vUv.y ) ) * 0.051;",
55 |
56 | "gl_FragColor = sum;",
57 |
58 | "}"
59 |
60 | ].join("\n")
61 |
62 | };
63 |
--------------------------------------------------------------------------------
/static/js/components/ColorComponent.js:
--------------------------------------------------------------------------------
1 | App.ColorComponent = ColorComponent;
2 | function ColorComponent(opts) {
3 | opts = opts || {};
4 |
5 | var element = this.element = document.createElement('div');
6 | var input = this._input = document.createElement('input');
7 | var preview = this._previewEl = document.createElement('div');
8 | var label = this._labelEl = document.createElement('div');
9 |
10 | this.color = opts.color || new THREE.Color();
11 | this.setLabel(opts.label || '');
12 | this.setValue();
13 | this.syncState();
14 |
15 | element.className = this._className = 'color';
16 | preview.className = 'preview';
17 | label.className = 'label';
18 |
19 | input.setAttribute('type', 'color');
20 | element.appendChild(preview);
21 | element.appendChild(label);
22 | element.appendChild(input);
23 |
24 | input.addEventListener('change', this.syncState.bind(this), false);
25 | input.addEventListener('focus', this.focus.bind(this), false);
26 | input.addEventListener('blur', this.blur.bind(this), false);
27 | }
28 |
29 | ColorComponent.create = App.ctor(ColorComponent);
30 | App.Dispatcher.extend(ColorComponent.prototype);
31 |
32 | ColorComponent.prototype.setLabel = function (label) {
33 | this._labelEl.textContent = label;
34 | };
35 |
36 | ColorComponent.prototype.setValue = function (value) {
37 | value = value || ('#' + this.color.getHexString());
38 | this._input.setAttribute('value', value);
39 | };
40 |
41 | // FIXME: Losing focus when switching to color picker window
42 | ColorComponent.prototype.focus = function (event) {
43 | this.element.className = this._className + ' focus';
44 | };
45 |
46 | ColorComponent.prototype.blur = function (event) {
47 | this.element.className = this._className;
48 | };
49 |
50 | ColorComponent.prototype.syncState = function (event) {
51 | var value = this._input.value;
52 | this._previewEl.style.background = value;
53 | this.color.setStyle(value);
54 | this.triggerListeners('change', value);
55 | };
56 |
--------------------------------------------------------------------------------
/static/js/materials/ShaderMaterial.js:
--------------------------------------------------------------------------------
1 | function compileShader(templateName) {
2 | var template = App.shaders[templateName];
3 | return template({
4 | chunks : THREE.ShaderChunk
5 | });
6 | }
7 |
8 | App.ShaderMaterial = ShaderMaterial;
9 | function ShaderMaterial(parameters) {
10 | if (!this.shader) { return; }
11 |
12 | this.uniforms = THREE.UniformsUtils.clone(this.shader.uniforms);
13 | this.setUniformParameters(parameters);
14 |
15 | THREE.ShaderMaterial.call(this, {
16 | uniforms : this.uniforms,
17 | fragmentShader : compileShader(this.shader.fragmentShader),
18 | vertexShader : compileShader(this.shader.vertexShader)
19 | });
20 |
21 | this.transparent = parameters.transparent || false;
22 | this.blending = parameters.blending || THREE.NormalBlending;
23 | this.side = parameters.side || THREE.FrontSide;
24 | this.linewidth = parameters.linewidth || 1;
25 | this.depthTest = parameters.depthTest != null ? parameters.depthTest : true;
26 | this.depthWrite = parameters.depthWrite != null ? parameters.depthWrite : true;
27 |
28 | this.size = parameters.size || 1;
29 | this.sizeAttenuation = parameters.sizeAttenuation;
30 |
31 | this.fog = !!parameters.fog;
32 | this.map = !!parameters.map;
33 | this.bumpMap = !!parameters.bumpMap;
34 | this.normalMap = !!parameters.normalMap;
35 | this.specularMap = !!parameters.specularMap;
36 | }
37 |
38 | ShaderMaterial.prototype = Object.create(THREE.ShaderMaterial.prototype);
39 |
40 | ShaderMaterial.prototype.setUniformParameters = function (parameters) {
41 | var uniforms = this.uniforms;
42 | Object.keys(parameters).forEach(function (key) {
43 | var uniform = uniforms[key];
44 | if (!uniform) { return; }
45 | switch (uniform.type) {
46 | case 'c':
47 | this[key] = uniforms[key].value = new THREE.Color(parameters[key]);
48 | break;
49 | default:
50 | this[key] = uniforms[key].value = parameters[key];
51 | break;
52 | }
53 | }.bind(this));
54 | };
55 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/HorizontalTiltShiftShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Simple fake tilt-shift effect, modulating two pass Gaussian blur (see above) by vertical position
5 | *
6 | * - 9 samples per pass
7 | * - standard deviation 2.7
8 | * - "h" and "v" parameters should be set to "1 / width" and "1 / height"
9 | * - "r" parameter control where "focused" horizontal line lies
10 | */
11 |
12 | THREE.HorizontalTiltShiftShader = {
13 |
14 | uniforms: {
15 |
16 | "tDiffuse": { type: "t", value: null },
17 | "h": { type: "f", value: 1.0 / 512.0 },
18 | "r": { type: "f", value: 0.35 }
19 |
20 | },
21 |
22 | vertexShader: [
23 |
24 | "varying vec2 vUv;",
25 |
26 | "void main() {",
27 |
28 | "vUv = uv;",
29 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
30 |
31 | "}"
32 |
33 | ].join("\n"),
34 |
35 | fragmentShader: [
36 |
37 | "uniform sampler2D tDiffuse;",
38 | "uniform float h;",
39 | "uniform float r;",
40 |
41 | "varying vec2 vUv;",
42 |
43 | "void main() {",
44 |
45 | "vec4 sum = vec4( 0.0 );",
46 |
47 | "float hh = h * abs( r - vUv.y );",
48 |
49 | "sum += texture2D( tDiffuse, vec2( vUv.x - 4.0 * hh, vUv.y ) ) * 0.051;",
50 | "sum += texture2D( tDiffuse, vec2( vUv.x - 3.0 * hh, vUv.y ) ) * 0.0918;",
51 | "sum += texture2D( tDiffuse, vec2( vUv.x - 2.0 * hh, vUv.y ) ) * 0.12245;",
52 | "sum += texture2D( tDiffuse, vec2( vUv.x - 1.0 * hh, vUv.y ) ) * 0.1531;",
53 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y ) ) * 0.1633;",
54 | "sum += texture2D( tDiffuse, vec2( vUv.x + 1.0 * hh, vUv.y ) ) * 0.1531;",
55 | "sum += texture2D( tDiffuse, vec2( vUv.x + 2.0 * hh, vUv.y ) ) * 0.12245;",
56 | "sum += texture2D( tDiffuse, vec2( vUv.x + 3.0 * hh, vUv.y ) ) * 0.0918;",
57 | "sum += texture2D( tDiffuse, vec2( vUv.x + 4.0 * hh, vUv.y ) ) * 0.051;",
58 |
59 | "gl_FragColor = sum;",
60 |
61 | "}"
62 |
63 | ].join("\n")
64 |
65 | };
66 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/VerticalTiltShiftShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Simple fake tilt-shift effect, modulating two pass Gaussian blur (see above) by vertical position
5 | *
6 | * - 9 samples per pass
7 | * - standard deviation 2.7
8 | * - "h" and "v" parameters should be set to "1 / width" and "1 / height"
9 | * - "r" parameter control where "focused" horizontal line lies
10 | */
11 |
12 | THREE.VerticalTiltShiftShader = {
13 |
14 | uniforms: {
15 |
16 | "tDiffuse": { type: "t", value: null },
17 | "v": { type: "f", value: 1.0 / 512.0 },
18 | "r": { type: "f", value: 0.35 }
19 |
20 | },
21 |
22 | vertexShader: [
23 |
24 | "varying vec2 vUv;",
25 |
26 | "void main() {",
27 |
28 | "vUv = uv;",
29 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
30 |
31 | "}"
32 |
33 | ].join("\n"),
34 |
35 | fragmentShader: [
36 |
37 | "uniform sampler2D tDiffuse;",
38 | "uniform float v;",
39 | "uniform float r;",
40 |
41 | "varying vec2 vUv;",
42 |
43 | "void main() {",
44 |
45 | "vec4 sum = vec4( 0.0 );",
46 |
47 | "float vv = v * abs( r - vUv.y );",
48 |
49 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 4.0 * vv ) ) * 0.051;",
50 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 3.0 * vv ) ) * 0.0918;",
51 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 2.0 * vv ) ) * 0.12245;",
52 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 1.0 * vv ) ) * 0.1531;",
53 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y ) ) * 0.1633;",
54 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 1.0 * vv ) ) * 0.1531;",
55 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 2.0 * vv ) ) * 0.12245;",
56 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 3.0 * vv ) ) * 0.0918;",
57 | "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 4.0 * vv ) ) * 0.051;",
58 |
59 | "gl_FragColor = sum;",
60 |
61 | "}"
62 |
63 | ].join("\n")
64 |
65 | };
66 |
--------------------------------------------------------------------------------
/static/lib-extras/three/postprocessing/MaskPass.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | */
4 |
5 | THREE.MaskPass = function ( scene, camera ) {
6 |
7 | this.scene = scene;
8 | this.camera = camera;
9 |
10 | this.enabled = true;
11 | this.clear = true;
12 | this.needsSwap = false;
13 |
14 | this.inverse = false;
15 |
16 | };
17 |
18 | THREE.MaskPass.prototype = {
19 |
20 | render: function ( renderer, writeBuffer, readBuffer, delta ) {
21 |
22 | var context = renderer.context;
23 |
24 | // don't update color or depth
25 |
26 | context.colorMask( false, false, false, false );
27 | context.depthMask( false );
28 |
29 | // set up stencil
30 |
31 | var writeValue, clearValue;
32 |
33 | if ( this.inverse ) {
34 |
35 | writeValue = 0;
36 | clearValue = 1;
37 |
38 | } else {
39 |
40 | writeValue = 1;
41 | clearValue = 0;
42 |
43 | }
44 |
45 | context.enable( context.STENCIL_TEST );
46 | context.stencilOp( context.REPLACE, context.REPLACE, context.REPLACE );
47 | context.stencilFunc( context.ALWAYS, writeValue, 0xffffffff );
48 | context.clearStencil( clearValue );
49 |
50 | // draw into the stencil buffer
51 |
52 | renderer.render( this.scene, this.camera, readBuffer, this.clear );
53 | renderer.render( this.scene, this.camera, writeBuffer, this.clear );
54 |
55 | // re-enable update of color and depth
56 |
57 | context.colorMask( true, true, true, true );
58 | context.depthMask( true );
59 |
60 | // only render where stencil is set to 1
61 |
62 | context.stencilFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1
63 | context.stencilOp( context.KEEP, context.KEEP, context.KEEP );
64 |
65 | }
66 |
67 | };
68 |
69 |
70 | THREE.ClearMaskPass = function () {
71 |
72 | this.enabled = true;
73 |
74 | };
75 |
76 | THREE.ClearMaskPass.prototype = {
77 |
78 | render: function ( renderer, writeBuffer, readBuffer, delta ) {
79 |
80 | var context = renderer.context;
81 |
82 | context.disable( context.STENCIL_TEST );
83 |
84 | }
85 |
86 | };
87 |
--------------------------------------------------------------------------------
/static/glsl/shaders/bulb-frag.glsl:
--------------------------------------------------------------------------------
1 | uniform vec3 diffuse;
2 | uniform vec3 diffuseB;
3 | uniform float opacity;
4 | uniform float time;
5 | varying vec2 vUv;
6 | varying vec3 vNormal;
7 |
8 | const vec3 eye = vec3(0.0, 0.0, 1.0);
9 |
10 | float oscillate(float vMin, float vMax, float t) {
11 | float halfRange = (vMax - vMin) / vMax * 0.5;
12 | return (sin(t) * halfRange + (1.0 - halfRange)) * vMax;
13 | }
14 |
15 | float accumulate(vec2 uv, float saturation, float scale) {
16 | saturation -= sin(uv.x * 60.0) * 0.25 + sin(uv.x * 50.0 * scale) * 0.25 + 0.75;
17 |
18 | saturation -= sin(uv.y * sin(uv.x * 5.0) * 5.0 * scale) * 0.05;
19 | saturation -= sin(uv.y * sin((1.0 - uv.x) * 5.0) * 5.0 * scale) * 0.05;
20 |
21 | saturation -= sin(uv.y * sin(uv.y + cos(uv.x) * 2.0) * 3.0 * scale) * 0.15;
22 | saturation -= sin(uv.y * sin(uv.y + cos(1.0 - uv.x) * 2.0) * 3.0 * scale) * 0.15;
23 |
24 | saturation -= sin((uv.y - 1.5) * sin(uv.y + cos(uv.x - 1.0) * 2.0) * 4.0 * scale) * 0.15;
25 | saturation -= sin((uv.y - 1.5) * sin(uv.y + cos(uv.x) * 2.0) * 3.0 * scale) * 0.15;
26 |
27 | saturation -= sin(uv.y * 5.0) * 0.15 + sin(uv.y * 2.5) * 1.25;
28 |
29 | return saturation;
30 | }
31 |
32 | void main() {
33 | vec3 normal = normalize(mat3(viewMatrix) * vNormal);
34 | float rim = 1.0 - max(dot(eye, normal), 0.0);
35 | float saturation = 0.0;
36 |
37 | vec2 uv0 = vUv;
38 | vec2 uv1 = uv0 + rim;
39 | vec2 uv2 = vec2(-rim * 0.25);
40 | vec2 uv3 = vec2(rim, uv0.y);
41 |
42 | float scale0 = oscillate( 8.0, 15.0, time * 0.25 + 0.5);
43 | float scale1 = oscillate(12.0, 20.0, time * 0.125);
44 | float scale2 = 1.0;
45 | float scale3 = 1.0;
46 |
47 | saturation += max(accumulate(uv0, 2.0, scale0), -0.5);
48 | saturation += max(accumulate(uv1, 2.0, scale1), 0.25);
49 | saturation += max(accumulate(uv2, 1.0, scale2), -0.25);
50 | saturation += max(accumulate(uv3, 1.0, scale3), -0.25);
51 |
52 | gl_FragColor = vec4(
53 | mix(diffuse, diffuseB, smoothstep(-0.5, 0.5, saturation)),
54 | (1.0 - smoothstep(-0.5, 2.5, saturation)) * opacity);
55 | }
56 |
--------------------------------------------------------------------------------
/static/tests/constraints/LocalPlaneConstraint.js:
--------------------------------------------------------------------------------
1 | module('Constraint.LocalPlane');
2 |
3 | var ParticleSystem = Particulate.ParticleSystem;
4 | var LocalPlaneConstraint = App.LocalPlaneConstraint;
5 | var Vec3 = Particulate.Vec3;
6 |
7 | test('Creation', function () {
8 | var pa = 0, pb = 1, pc = 2;
9 | var a = 3;
10 | var indices = [3, 4, 5];
11 | var fromArgs = LocalPlaneConstraint.create(pa, pb, pc, a);
12 | var fromArray = LocalPlaneConstraint.create(pa, pb, pc, indices);
13 |
14 | Test.assert.equalArray(fromArgs.indices, [pa, pb, pc, a],
15 | 'Should create indices from int arguments.');
16 | Test.assert.equalArray(fromArray.indices, [pa, pb, pc].concat(indices),
17 | 'Should create indices from int array.');
18 | });
19 |
20 | function testPlane(expectedZ, v0, v1, v2) {
21 | var system = ParticleSystem.create(10, 10);
22 | var singleIndex = 3;
23 | var single = LocalPlaneConstraint.create(0, 1, 2, singleIndex);
24 | var manyIndices = [4, 5, 6, 7, 8, 9];
25 | var many = LocalPlaneConstraint.create(0, 1, 2, manyIndices);
26 | var pos = Vec3.create();
27 |
28 | function getZ(index) {
29 | return system.getPosition(index, pos)[2];
30 | }
31 |
32 | function returnExpected() {
33 | return expectedZ;
34 | }
35 |
36 | system.setPosition(0, v0);
37 | system.setPosition(1, v1);
38 | system.setPosition(2, v2);
39 |
40 | system.addConstraint(single);
41 | system.addConstraint(many);
42 | system.tick(20);
43 |
44 | Test.assert.closeArray(many.bufferVec3, [0, 0, 1], 0.1,
45 | 'Should cache plane normal vector.');
46 | Test.assert.close(getZ(singleIndex), expectedZ, 0.1,
47 | 'Should constrain single set of particles to plane.');
48 | Test.assert.closeArray(manyIndices.map(getZ), manyIndices.map(returnExpected), 0.1,
49 | 'Should constrain multiple sets of particles to plane.');
50 | }
51 |
52 | test('Application', function () {
53 | testPlane(10,
54 | [25, 15, 10],
55 | [10, 10, 10],
56 | [50, 30, 10]);
57 | });
58 |
59 | test('Application with inline segments', function () {
60 | testPlane(10,
61 | [ 5, 5, 10],
62 | [10, 10, 10],
63 | [15, 15, 10]);
64 | });
65 |
66 | // Plane behind particles, should have no effect
67 | test('Non-application when behind particles', function () {
68 | testPlane(0,
69 | [10, 10, -2],
70 | [ 0, 0, -2],
71 | [20, 20, -2]);
72 | });
73 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /*jshint node:true*/
2 | 'use strict';
3 |
4 | var CONFIG = {
5 | pages: 'pages/',
6 | source: 'static/',
7 | static: './build/static/',
8 | deploy: './build/'
9 | };
10 |
11 | module.exports = function (grunt) {
12 | require('time-grunt')(grunt);
13 | require('jit-grunt')(grunt)({
14 | loadTasks : 'grunt/tasks'
15 | });
16 |
17 | [
18 | 'autoprefixer',
19 | 'clean',
20 | 'connect',
21 | 'copy',
22 | 'handlebars',
23 | 'haychtml',
24 | 'jshint',
25 | 'neuter',
26 | 'notify',
27 | 'sass',
28 | 'uglify',
29 | 'watch'
30 | ].forEach(function (key) {
31 | grunt.config(key, require('./grunt/config/' + key)(CONFIG));
32 | });
33 |
34 | grunt.registerTask('server', function (port) {
35 | var livereloadPort = Math.round(port) + 30000;
36 | if (port) {
37 | grunt.config('watch.livereload.options.livereload', livereloadPort);
38 | grunt.config('connect.options.livereload', livereloadPort);
39 | grunt.config('connect.options.port', port);
40 | }
41 |
42 | grunt.task.run([
43 | // Run tasks once before starting watchers
44 | 'develop',
45 |
46 | // Start server
47 | 'connect',
48 |
49 | // Watch files for changes
50 | 'watch'
51 | ]);
52 | });
53 |
54 | // Build unminified files during development
55 | grunt.registerTask('develop', [
56 | 'clean',
57 |
58 | // JS
59 | 'handlebars',
60 | 'neuter',
61 | 'shaderChunks',
62 |
63 | // CSS
64 | 'sass:develop',
65 | 'autoprefixer:develop',
66 |
67 | // HTML
68 | 'haychtml:develop',
69 |
70 | // OTHER FILES
71 | 'copy:develop',
72 | 'copy:build'
73 | ]);
74 |
75 | // Build minified files for deployment
76 | grunt.registerTask('build', [
77 | 'clean',
78 |
79 | // JS
80 | 'jshint',
81 | 'handlebars',
82 | 'neuter',
83 | 'shaderChunks',
84 | 'uglify',
85 |
86 | // CSS
87 | 'sass:build',
88 | 'autoprefixer:build',
89 |
90 | // HTML
91 | 'haychtml:build',
92 |
93 | // OTHER FILES
94 | 'copy:build',
95 |
96 | // TEMP FOLDER
97 | 'clean:temp',
98 |
99 | // NOTIFICATION
100 | 'notify:build'
101 | ]);
102 |
103 | grunt.registerTask('default', ['build']);
104 | };
105 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/ConvolutionShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Convolution shader
5 | * ported from o3d sample to WebGL / GLSL
6 | * http://o3d.googlecode.com/svn/trunk/samples/convolution.html
7 | */
8 |
9 | THREE.ConvolutionShader = {
10 |
11 | defines: {
12 |
13 | "KERNEL_SIZE_FLOAT": "25.0",
14 | "KERNEL_SIZE_INT": "25",
15 |
16 | },
17 |
18 | uniforms: {
19 |
20 | "tDiffuse": { type: "t", value: null },
21 | "uImageIncrement": { type: "v2", value: new THREE.Vector2( 0.001953125, 0.0 ) },
22 | "cKernel": { type: "fv1", value: [] }
23 |
24 | },
25 |
26 | vertexShader: [
27 |
28 | "uniform vec2 uImageIncrement;",
29 |
30 | "varying vec2 vUv;",
31 |
32 | "void main() {",
33 |
34 | "vUv = uv - ( ( KERNEL_SIZE_FLOAT - 1.0 ) / 2.0 ) * uImageIncrement;",
35 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
36 |
37 | "}"
38 |
39 | ].join("\n"),
40 |
41 | fragmentShader: [
42 |
43 | "uniform float cKernel[ KERNEL_SIZE_INT ];",
44 |
45 | "uniform sampler2D tDiffuse;",
46 | "uniform vec2 uImageIncrement;",
47 |
48 | "varying vec2 vUv;",
49 |
50 | "void main() {",
51 |
52 | "vec2 imageCoord = vUv;",
53 | "vec4 sum = vec4( 0.0, 0.0, 0.0, 0.0 );",
54 |
55 | "for( int i = 0; i < KERNEL_SIZE_INT; i ++ ) {",
56 |
57 | "sum += texture2D( tDiffuse, imageCoord ) * cKernel[ i ];",
58 | "imageCoord += uImageIncrement;",
59 |
60 | "}",
61 |
62 | "gl_FragColor = sum;",
63 |
64 | "}"
65 |
66 |
67 | ].join("\n"),
68 |
69 | buildKernel: function ( sigma ) {
70 |
71 | // We lop off the sqrt(2 * pi) * sigma term, since we're going to normalize anyway.
72 |
73 | function gauss( x, sigma ) {
74 |
75 | return Math.exp( - ( x * x ) / ( 2.0 * sigma * sigma ) );
76 |
77 | }
78 |
79 | var i, values, sum, halfWidth, kMaxKernelSize = 25, kernelSize = 2 * Math.ceil( sigma * 3.0 ) + 1;
80 |
81 | if ( kernelSize > kMaxKernelSize ) kernelSize = kMaxKernelSize;
82 | halfWidth = ( kernelSize - 1 ) * 0.5;
83 |
84 | values = new Array( kernelSize );
85 | sum = 0.0;
86 | for ( i = 0; i < kernelSize; ++i ) {
87 |
88 | values[ i ] = gauss( i - halfWidth, sigma );
89 | sum += values[ i ];
90 |
91 | }
92 |
93 | // normalize the kernel
94 |
95 | for ( i = 0; i < kernelSize; ++i ) values[ i ] /= sum;
96 |
97 | return values;
98 |
99 | }
100 |
101 | };
102 |
--------------------------------------------------------------------------------
/static/scss/apps/_index.scss:
--------------------------------------------------------------------------------
1 | #container {
2 | @include absolute(0);
3 | width: 100%;
4 | height: 100%;
5 | overflow: hidden;
6 |
7 | > canvas {
8 | opacity: 0;
9 | transition: opacity 500ms 200ms;
10 |
11 | &.active {
12 | opacity: 1;
13 | }
14 | }
15 | }
16 |
17 | #container-controls {
18 | position: absolute;
19 | z-index: 2;
20 | left: 0;
21 | bottom: 10px;
22 | opacity: 0;
23 | transition: opacity 500ms 300ms;
24 |
25 | &.active {
26 | opacity: 1;
27 | }
28 | }
29 |
30 | #container-graphs {
31 | position: absolute;
32 | right: 0;
33 | bottom: 30px;
34 | }
35 |
36 | #container-stats {
37 | position: absolute;
38 | z-index: 1;
39 | right: 20px;
40 | bottom: 20px;
41 | }
42 |
43 | .info-modal {
44 | h1 {
45 | margin: 4px 0 8px;
46 |
47 | &:after {
48 | display: block;
49 | content: "-------";
50 | }
51 | }
52 |
53 | dl {
54 | display: block;
55 | margin: 4px 0;
56 | }
57 |
58 | dt {
59 | color: #e9e9e9;
60 | display: inline-block;
61 | opacity: 0.5;
62 |
63 | &:after {
64 | display: inline-block;
65 | padding: 0 4px;
66 | color: #e30060;
67 | content: "/";
68 | }
69 | }
70 |
71 | dd {
72 | display: inline-block;
73 | color: #fff;
74 | }
75 |
76 | a {
77 | position: relative;
78 | display: inline-block;
79 | color: #fff;
80 | text-decoration: none;
81 |
82 | &:after {
83 | position: absolute;
84 | bottom: -1px;
85 | left: 0;
86 | right: 0;
87 | border-bottom: 1px solid #fff;
88 | opacity: 0.25;
89 | content: "";
90 | }
91 |
92 | &:hover:after {
93 | opacity: 1;
94 | }
95 | }
96 | }
97 |
98 | .info-section {
99 | margin: 12px 0 0;
100 | }
101 |
102 | .stats-panel {
103 | color: #fff;
104 | opacity: 0;
105 | visibility: hidden;
106 | transition: all 200ms;
107 |
108 | dl {
109 | display: inline-block;
110 |
111 | &:not(:last-child):after {
112 | display: inline-block;
113 | padding: 0 4px;
114 | color: #e30060;
115 | content: "/";
116 | }
117 | }
118 |
119 | dt {
120 | display: inline-block;
121 |
122 | &:after {
123 | display: inline-block;
124 | padding: 0 0 0 2px;
125 | content: ":";
126 | }
127 | }
128 |
129 | dd {
130 | display: inline-block;
131 | }
132 |
133 | .show-info & {
134 | opacity: 1;
135 | visibility: visible;
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/static/js/components/ToggleComponent.js:
--------------------------------------------------------------------------------
1 | App.ToggleComponent = ToggleComponent;
2 | function ToggleComponent(config) {
3 | var name = config.name;
4 | var toggle = this.toggle = document.getElementById('toggle-' + name);
5 |
6 | this.setupKey(config.key);
7 | this.setupMenu(config.menu);
8 |
9 | this.isActive = config.isActive != null ? config.isActive : false;
10 | this._toggleClassName = toggle.className;
11 | this.syncState();
12 |
13 | toggle.addEventListener('click', this.toggleState.bind(this), false);
14 | }
15 |
16 | ToggleComponent.create = App.ctor(ToggleComponent);
17 | App.Dispatcher.extend(ToggleComponent.prototype);
18 |
19 | ToggleComponent.prototype.setupKey = function (key) {
20 | if (!key) { return; }
21 | this.keyDelegator.addBinding(key, this, 'toggleState');
22 | };
23 |
24 | ToggleComponent.prototype.setupMenu = function (name) {
25 | if (!name) { return; }
26 |
27 | var menu = this.menu = document.getElementById('menu-' + name);
28 | var inner = this.menuInner = document.createElement('div');
29 |
30 | inner.className = 'inner';
31 | menu.appendChild(inner);
32 |
33 | this._menuClassName = menu.className;
34 | this.toggle.className += ' has-menu';
35 | };
36 |
37 | ToggleComponent.prototype.toggleState = function (event) {
38 | this.isActive = !this.isActive;
39 | this.syncState();
40 | this.triggerListeners('toggle', this.isActive);
41 | };
42 |
43 | ToggleComponent.prototype.syncState = function () {
44 | this.updateElClass(this.toggle, this._toggleClassName);
45 | this.updateElClass(this.menu, this._menuClassName);
46 | this.updateElHeight(this.menu, this.menuInner);
47 | };
48 |
49 | ToggleComponent.prototype.updateElClass = function (element, className) {
50 | if (!element) { return; }
51 | if (this.isActive) {
52 | element.className += ' active';
53 | } else {
54 | element.className = className;
55 | }
56 | };
57 |
58 | ToggleComponent.prototype.updateElHeight = function (element, inner) {
59 | if (!element) { return; }
60 | if (this.isActive) {
61 | element.style.height = inner.offsetHeight + 'px';
62 | this._willBecomeVisible = setTimeout(
63 | this.becomeVisible.bind(null, element), 200);
64 | } else {
65 | element.style.height = '';
66 | }
67 | };
68 |
69 | ToggleComponent.prototype.becomeVisible = function (element) {
70 | element.className += ' visible';
71 | };
72 |
73 | ToggleComponent.prototype.hide = function () {
74 | this.toggle.className += ' hidden';
75 | this.menu.className += ' hidden';
76 | };
77 |
78 | ToggleComponent.prototype.keyDelegator = App.KeyDelegator.create();
79 |
--------------------------------------------------------------------------------
/static/scss/components/_controls.scss:
--------------------------------------------------------------------------------
1 | $c-active: #E30060;
2 |
3 | @mixin controls-button {
4 | position: relative;
5 | display: block;
6 | width: 50px;
7 | height: 30px;
8 | cursor: pointer;
9 | }
10 |
11 | @mixin controls-toggle {
12 | position: absolute;
13 | top: 10px;
14 | left: 20px;
15 | width: 8px;
16 | height: 8px;
17 | box-sizing: content-box;
18 | border: 1px solid #fff;
19 | border-radius: 2px;
20 |
21 | background: rgba(#fff, 0.15);
22 | transition-property: border-color, background;
23 | transition-duration: 200ms;
24 | content: "";
25 | }
26 |
27 | @mixin controls-label {
28 | position: absolute;
29 | top: 50%;
30 | left: 100%;
31 | color: #fff;
32 | white-space: nowrap;
33 | opacity: 0;
34 | visibility: hidden;
35 |
36 | transform: translateY(-50%);
37 | transition: all 150ms;
38 |
39 | &:before {
40 | position: absolute;
41 | top: 50%;
42 | right: 100%;
43 | width: 8px;
44 | border-top: 1px solid #fff;
45 | margin-right: 6px;
46 | content: "";
47 | }
48 | }
49 |
50 | .controls-button {
51 | @include controls-button;
52 |
53 | &:before { @include controls-toggle; }
54 | > .label { @include controls-label; }
55 |
56 | &:hover > .label {
57 | visibility: visible;
58 | opacity: 1;
59 | }
60 |
61 | &.active:before {
62 | background: $c-active;
63 | border-color: $c-active;
64 | }
65 |
66 | &:hover:before { border-color: $c-active; }
67 | &.active:hover:before { border-color: #fff; }
68 |
69 | &.hidden { display: none; }
70 | }
71 |
72 | .controls-menu {
73 | $duration: 200ms;
74 | $delay: 200ms;
75 |
76 | position: relative;
77 | overflow: hidden;
78 | height: 0;
79 |
80 | transition: height $duration;
81 | transition-delay: $delay;
82 |
83 | > .inner {
84 | opacity: 0;
85 | visibility: hidden;
86 | transform: translateX(-8px);
87 | transition: all $duration;
88 | transition-delay: 0ms;
89 | }
90 |
91 | &:after {
92 | position: absolute;
93 | top: 0;
94 | left: 0;
95 | height: 100%;
96 | border-left: 1px solid #fff;
97 | content: "";
98 | }
99 |
100 | &.active {
101 | opacity: 1;
102 | visibility: visible;
103 | transition-delay: 0ms;
104 |
105 | > .inner {
106 | opacity: 1;
107 | visibility: visible;
108 | transform: translateX(0);
109 | transition-delay: $delay;
110 | }
111 | }
112 |
113 | &.visible {
114 | overflow: visible;
115 | }
116 |
117 | &.hidden { display: none; }
118 | }
119 |
--------------------------------------------------------------------------------
/static/lib-extras/three/postprocessing/BokehPass.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Depth-of-field post-process with bokeh shader
3 | */
4 |
5 |
6 | THREE.BokehPass = function ( scene, camera, params ) {
7 |
8 | this.scene = scene;
9 | this.camera = camera;
10 |
11 | var focus = ( params.focus !== undefined ) ? params.focus : 1.0;
12 | var aspect = ( params.aspect !== undefined ) ? params.aspect : camera.aspect;
13 | var aperture = ( params.aperture !== undefined ) ? params.aperture : 0.025;
14 | var maxblur = ( params.maxblur !== undefined ) ? params.maxblur : 1.0;
15 |
16 | // render targets
17 |
18 | var width = params.width || window.innerWidth || 1;
19 | var height = params.height || window.innerHeight || 1;
20 |
21 | this.renderTargetColor = new THREE.WebGLRenderTarget( width, height, {
22 | minFilter: THREE.LinearFilter,
23 | magFilter: THREE.LinearFilter,
24 | format: THREE.RGBFormat
25 | } );
26 |
27 | this.renderTargetDepth = this.renderTargetColor.clone();
28 |
29 | // depth material
30 |
31 | this.materialDepth = new THREE.MeshDepthMaterial();
32 |
33 | // bokeh material
34 |
35 | if ( THREE.BokehShader === undefined ) {
36 | console.error( "THREE.BokehPass relies on THREE.BokehShader" );
37 | }
38 |
39 | var bokehShader = THREE.BokehShader;
40 | var bokehUniforms = THREE.UniformsUtils.clone( bokehShader.uniforms );
41 |
42 | bokehUniforms[ "tDepth" ].value = this.renderTargetDepth;
43 |
44 | bokehUniforms[ "focus" ].value = focus;
45 | bokehUniforms[ "aspect" ].value = aspect;
46 | bokehUniforms[ "aperture" ].value = aperture;
47 | bokehUniforms[ "maxblur" ].value = maxblur;
48 |
49 | this.materialBokeh = new THREE.ShaderMaterial({
50 | uniforms: bokehUniforms,
51 | vertexShader: bokehShader.vertexShader,
52 | fragmentShader: bokehShader.fragmentShader
53 | });
54 |
55 | this.uniforms = bokehUniforms;
56 | this.enabled = true;
57 | this.needsSwap = false;
58 | this.renderToScreen = false;
59 | this.clear = false;
60 |
61 | this.camera2 = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
62 | this.scene2 = new THREE.Scene();
63 |
64 | this.quad2 = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
65 | this.scene2.add( this.quad2 );
66 |
67 | };
68 |
69 | THREE.BokehPass.prototype = {
70 |
71 | render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
72 |
73 | this.quad2.material = this.materialBokeh;
74 |
75 | // Render depth into texture
76 |
77 | this.scene.overrideMaterial = this.materialDepth;
78 |
79 | renderer.render( this.scene, this.camera, this.renderTargetDepth, true );
80 |
81 | // Render bokeh composite
82 |
83 | this.uniforms[ "tColor" ].value = readBuffer;
84 |
85 | if ( this.renderToScreen ) {
86 |
87 | renderer.render( this.scene2, this.camera2 );
88 |
89 | } else {
90 |
91 | renderer.render( this.scene2, this.camera2, writeBuffer, this.clear );
92 |
93 | }
94 |
95 | this.scene.overrideMaterial = null;
96 |
97 | }
98 |
99 | };
100 |
101 |
--------------------------------------------------------------------------------
/static/lib-extras/three/geometries/PlaneBufferGeometry.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
4 | */
5 |
6 | THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) {
7 |
8 | THREE.BufferGeometry.call( this );
9 |
10 | this.type = 'PlaneBufferGeometry';
11 |
12 | this.parameters = {
13 | width: width,
14 | height: height,
15 | widthSegments: widthSegments,
16 | heightSegments: heightSegments
17 | };
18 |
19 | var width_half = width / 2;
20 | var height_half = height / 2;
21 |
22 | var gridX = Math.floor( widthSegments ) || 1;
23 | var gridY = Math.floor( heightSegments ) || 1;
24 |
25 | var gridX1 = gridX + 1;
26 | var gridY1 = gridY + 1;
27 |
28 | var segment_width = width / gridX;
29 | var segment_height = height / gridY;
30 |
31 | var vertices = new Float32Array( gridX1 * gridY1 * 3 );
32 | var normals = new Float32Array( gridX1 * gridY1 * 3 );
33 | var uvs = new Float32Array( gridX1 * gridY1 * 2 );
34 |
35 | var offset = 0;
36 | var offset2 = 0;
37 |
38 | for ( var iy = 0; iy < gridY1; iy ++ ) {
39 |
40 | var y = iy * segment_height - height_half;
41 |
42 | for ( var ix = 0; ix < gridX1; ix ++ ) {
43 |
44 | var x = ix * segment_width - width_half;
45 |
46 | vertices[ offset ] = x;
47 | vertices[ offset + 1 ] = - y;
48 |
49 | normals[ offset + 2 ] = 1;
50 |
51 | uvs[ offset2 ] = ix / gridX;
52 | uvs[ offset2 + 1 ] = 1 - ( iy / gridY );
53 |
54 | offset += 3;
55 | offset2 += 2;
56 |
57 | }
58 |
59 | }
60 |
61 | offset = 0;
62 |
63 | var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 );
64 |
65 | for ( var iy = 0; iy < gridY; iy ++ ) {
66 |
67 | for ( var ix = 0; ix < gridX; ix ++ ) {
68 |
69 | var a = ix + gridX1 * iy;
70 | var b = ix + gridX1 * ( iy + 1 );
71 | var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
72 | var d = ( ix + 1 ) + gridX1 * iy;
73 |
74 | indices[ offset ] = a;
75 | indices[ offset + 1 ] = b;
76 | indices[ offset + 2 ] = d;
77 |
78 | indices[ offset + 3 ] = b;
79 | indices[ offset + 4 ] = c;
80 | indices[ offset + 5 ] = d;
81 |
82 | offset += 6;
83 |
84 | }
85 |
86 | }
87 |
88 | this.setIndex( new THREE.BufferAttribute( indices, 1 ) );
89 | this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
90 | this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
91 | this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
92 |
93 | };
94 |
95 | THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
96 | THREE.PlaneBufferGeometry.prototype.constructor = THREE.PlaneBufferGeometry;
97 |
98 | THREE.PlaneBufferGeometry.prototype.clone = function () {
99 |
100 | var geometry = new THREE.PlaneBufferGeometry(
101 | this.parameters.width,
102 | this.parameters.height,
103 | this.parameters.widthSegments,
104 | this.parameters.heightSegments
105 | );
106 |
107 | geometry.copy( this );
108 |
109 | return geometry;
110 |
111 | };
112 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/FXAAShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | * @author davidedc / http://www.sketchpatch.net/
4 | *
5 | * NVIDIA FXAA by Timothy Lottes
6 | * http://timothylottes.blogspot.com/2011/06/fxaa3-source-released.html
7 | * - WebGL port by @supereggbert
8 | * http://www.glge.org/demos/fxaa/
9 | */
10 |
11 | THREE.FXAAShader = {
12 |
13 | uniforms: {
14 |
15 | "tDiffuse": { type: "t", value: null },
16 | "resolution": { type: "v2", value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
17 |
18 | },
19 |
20 | vertexShader: [
21 |
22 | "void main() {",
23 |
24 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
25 |
26 | "}"
27 |
28 | ].join("\n"),
29 |
30 | fragmentShader: [
31 |
32 | "uniform sampler2D tDiffuse;",
33 | "uniform vec2 resolution;",
34 |
35 | "#define FXAA_REDUCE_MIN (1.0/128.0)",
36 | "#define FXAA_REDUCE_MUL (1.0/8.0)",
37 | "#define FXAA_SPAN_MAX 8.0",
38 |
39 | "void main() {",
40 |
41 | "vec3 rgbNW = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ).xyz;",
42 | "vec3 rgbNE = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ).xyz;",
43 | "vec3 rgbSW = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ).xyz;",
44 | "vec3 rgbSE = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ).xyz;",
45 | "vec4 rgbaM = texture2D( tDiffuse, gl_FragCoord.xy * resolution );",
46 | "vec3 rgbM = rgbaM.xyz;",
47 | "vec3 luma = vec3( 0.299, 0.587, 0.114 );",
48 |
49 | "float lumaNW = dot( rgbNW, luma );",
50 | "float lumaNE = dot( rgbNE, luma );",
51 | "float lumaSW = dot( rgbSW, luma );",
52 | "float lumaSE = dot( rgbSE, luma );",
53 | "float lumaM = dot( rgbM, luma );",
54 | "float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );",
55 | "float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );",
56 |
57 | "vec2 dir;",
58 | "dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));",
59 | "dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));",
60 |
61 | "float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );",
62 |
63 | "float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );",
64 | "dir = min( vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX),",
65 | "max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),",
66 | "dir * rcpDirMin)) * resolution;",
67 | "vec4 rgbA = (1.0/2.0) * (",
68 | "texture2D(tDiffuse, gl_FragCoord.xy * resolution + dir * (1.0/3.0 - 0.5)) +",
69 | "texture2D(tDiffuse, gl_FragCoord.xy * resolution + dir * (2.0/3.0 - 0.5)));",
70 | "vec4 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (",
71 | "texture2D(tDiffuse, gl_FragCoord.xy * resolution + dir * (0.0/3.0 - 0.5)) +",
72 | "texture2D(tDiffuse, gl_FragCoord.xy * resolution + dir * (3.0/3.0 - 0.5)));",
73 | "float lumaB = dot(rgbB, vec4(luma, 0.0));",
74 |
75 | "if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) ) {",
76 |
77 | "gl_FragColor = rgbA;",
78 |
79 | "} else {",
80 | "gl_FragColor = rgbB;",
81 |
82 | "}",
83 |
84 | "}"
85 |
86 | ].join("\n")
87 |
88 | };
89 |
--------------------------------------------------------------------------------
/static/js/items/Dust.js:
--------------------------------------------------------------------------------
1 | var DEBUG_TEXTURE = false;
2 | var mapLinear = THREE.Math.mapLinear;
3 |
4 | App.Dust = Dust;
5 | function Dust(opts) {
6 | this.pxRatio = opts.pxRatio || 1;
7 | this.particleSize = 32 * this.pxRatio;
8 | this.particleCount = 8000;
9 | this.area = 300;
10 | this.createParticles();
11 | this.createMaterials();
12 | this.createItem();
13 | }
14 |
15 | Dust.create = App.ctor(Dust);
16 |
17 | Dust.prototype.createParticles = function () {
18 | var count = this.particleCount;
19 | var geom = this.geometry = new THREE.BufferGeometry();
20 | var verts = new Float32Array(count * 3);
21 |
22 | var area = this.area;
23 | var areaHalf = area * 0.5;
24 | var ix;
25 |
26 | for (var i = 0, il = verts.length / 3; i < il; i ++) {
27 | ix = i * 3;
28 | verts[ix] = Math.random() * area - areaHalf;
29 | verts[ix + 1] = Math.random() * area - areaHalf;
30 | verts[ix + 2] = Math.random() * area - areaHalf;
31 | }
32 |
33 | geom.addAttribute('position',
34 | new THREE.BufferAttribute(verts, 3));
35 | };
36 |
37 | Dust.prototype.createTexture = function () {
38 | var canvas = document.createElement('canvas');
39 | var texture = new THREE.Texture(canvas);
40 | var ctx = canvas.getContext('2d');
41 |
42 | var size = Math.pow(2, 6);
43 | var sizeHalf = size * 0.5;
44 | var rings = 2;
45 | var t, radius, alpha;
46 |
47 | canvas.width = canvas.height = size;
48 | ctx.fillStyle = '#fff';
49 |
50 | for (var i = 0; i < rings; i ++) {
51 | t = i / (rings - 1);
52 | radius = mapLinear(t * t, 0, 1, 4, sizeHalf);
53 | alpha = mapLinear(t, 0, 1, 1, 0.05);
54 |
55 | ctx.beginPath();
56 | ctx.arc(sizeHalf, sizeHalf, radius, 0, Math.PI * 2);
57 | ctx.globalAlpha = alpha;
58 | ctx.fill();
59 | }
60 |
61 | texture.needsUpdate = true;
62 |
63 | if (DEBUG_TEXTURE) {
64 | document.body.appendChild(canvas);
65 | canvas.style.position = 'absolute';
66 | }
67 |
68 | return texture;
69 | };
70 |
71 | Dust.prototype.createMaterials = function () {
72 | var params = {
73 | psColor : 0xffffff,
74 | opacity : 0.95,
75 | size : this.particleSize,
76 | map : this.createTexture(),
77 | scale : 150,
78 | area : this.area,
79 | blending: THREE.AdditiveBlending,
80 | transparent : true,
81 | depthTest : false,
82 | depthWrite : false
83 | };
84 |
85 | this.materialFore = new App.DustMaterial(params);
86 |
87 | // params.depthTest = false;
88 | // params.opacity = 0.25;
89 | // this.materialFaint = new App.DustMaterial(params);
90 |
91 | // this.timeAttrFaint = this.materialFaint.uniforms.time;
92 | this.timeAttrFore = this.materialFore.uniforms.time;
93 | };
94 |
95 | Dust.prototype.createItem = function () {
96 | // this.itemFaint = new THREE.PointCloud(this.geometry, this.materialFaint);
97 | this.itemFore = new THREE.Points(this.geometry, this.materialFore);
98 | };
99 |
100 | Dust.prototype.addTo = function (scene) {
101 | // scene.add(this.itemFaint);
102 | scene.add(this.itemFore);
103 | };
104 |
105 | Dust.prototype.updateGraphics = function (delta) {
106 | // this.timeAttrFaint.value += delta * 0.005;
107 | this.timeAttrFore.value += delta * 0.005;
108 | };
109 |
--------------------------------------------------------------------------------
/static/lib-extras/three/postprocessing/EffectComposer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | */
4 |
5 | THREE.EffectComposer = function ( renderer, renderTarget ) {
6 |
7 | this.renderer = renderer;
8 |
9 | if ( renderTarget === undefined ) {
10 |
11 | var width = window.innerWidth || 1;
12 | var height = window.innerHeight || 1;
13 | var parameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };
14 |
15 | renderTarget = new THREE.WebGLRenderTarget( width, height, parameters );
16 |
17 | }
18 |
19 | this.renderTarget1 = renderTarget;
20 | this.renderTarget2 = renderTarget.clone();
21 |
22 | this.writeBuffer = this.renderTarget1;
23 | this.readBuffer = this.renderTarget2;
24 |
25 | this.passes = [];
26 |
27 | if ( THREE.CopyShader === undefined )
28 | console.error( "THREE.EffectComposer relies on THREE.CopyShader" );
29 |
30 | this.copyPass = new THREE.ShaderPass( THREE.CopyShader );
31 |
32 | };
33 |
34 | THREE.EffectComposer.prototype = {
35 |
36 | swapBuffers: function() {
37 |
38 | var tmp = this.readBuffer;
39 | this.readBuffer = this.writeBuffer;
40 | this.writeBuffer = tmp;
41 |
42 | },
43 |
44 | addPass: function ( pass ) {
45 |
46 | this.passes.push( pass );
47 |
48 | },
49 |
50 | insertPass: function ( pass, index ) {
51 |
52 | this.passes.splice( index, 0, pass );
53 |
54 | },
55 |
56 | render: function ( delta ) {
57 |
58 | this.writeBuffer = this.renderTarget1;
59 | this.readBuffer = this.renderTarget2;
60 |
61 | var maskActive = false;
62 |
63 | var pass, i, il = this.passes.length;
64 |
65 | for ( i = 0; i < il; i ++ ) {
66 |
67 | pass = this.passes[ i ];
68 |
69 | if ( !pass.enabled ) continue;
70 |
71 | pass.render( this.renderer, this.writeBuffer, this.readBuffer, delta, maskActive );
72 |
73 | if ( pass.needsSwap ) {
74 |
75 | if ( maskActive ) {
76 |
77 | var context = this.renderer.context;
78 |
79 | context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff );
80 |
81 | this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, delta );
82 |
83 | context.stencilFunc( context.EQUAL, 1, 0xffffffff );
84 |
85 | }
86 |
87 | this.swapBuffers();
88 |
89 | }
90 |
91 | if ( pass instanceof THREE.MaskPass ) {
92 |
93 | maskActive = true;
94 |
95 | } else if ( pass instanceof THREE.ClearMaskPass ) {
96 |
97 | maskActive = false;
98 |
99 | }
100 |
101 | }
102 |
103 | },
104 |
105 | reset: function ( renderTarget ) {
106 |
107 | if ( renderTarget === undefined ) {
108 |
109 | renderTarget = this.renderTarget1.clone();
110 |
111 | renderTarget.width = window.innerWidth;
112 | renderTarget.height = window.innerHeight;
113 |
114 | }
115 |
116 | this.renderTarget1 = renderTarget;
117 | this.renderTarget2 = renderTarget.clone();
118 |
119 | this.writeBuffer = this.renderTarget1;
120 | this.readBuffer = this.renderTarget2;
121 |
122 | },
123 |
124 | setSize: function ( width, height ) {
125 |
126 | var renderTarget = this.renderTarget1.clone();
127 |
128 | renderTarget.width = width;
129 | renderTarget.height = height;
130 |
131 | this.reset( renderTarget );
132 |
133 | }
134 |
135 | };
136 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Particulate Medusae
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Medusae
21 |
22 |
23 | - Concept, Design & Code
24 | - Ash Weeks
25 |
26 |
27 | - Audio Design
28 | - JP Arsenault
29 |
30 |
31 |
41 |
42 |
43 | - Source
44 | - Github
45 |
46 |
47 | - Process
48 | - Flickr
49 |
50 |
51 |
52 |
53 |
54 |
55 |
58 |
61 |
62 |
65 |
68 |
71 |
74 |
75 |
76 |
77 |
78 |
79 | - Particles
80 |
81 |
82 |
83 | - Constraints
84 |
85 |
86 |
87 | - Forces
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/static/js/controllers/IndexController.js:
--------------------------------------------------------------------------------
1 | var ToggleComponent = App.ToggleComponent;
2 | var ModalComponent = App.ModalComponent;
3 | var ColorComponent = App.ColorComponent;
4 | var Features = App.Features;
5 | var keysTop = [85, 73, 79, 80];
6 |
7 | App.register('index', function index() {
8 | var scene = App.MainScene.create();
9 | var controls = document.getElementById('container-controls');
10 |
11 | var dotsToggle = ToggleComponent.create({
12 | name : 'dots',
13 | key : keysTop[0]
14 | });
15 |
16 | var postFxToggle = ToggleComponent.create({
17 | name : 'postfx',
18 | key : keysTop[3],
19 | isActive : scene.usePostFx
20 | });
21 |
22 | var simToggle = ToggleComponent.create({
23 | name : 'sim',
24 | key : 32,
25 | isActive : scene.shouldAnimate
26 | });
27 |
28 | ModalComponent.create({
29 | name : 'info'
30 | });
31 |
32 | scene.initItems();
33 | scene.initForces();
34 | scene.appendRenderer();
35 |
36 | postFxToggle.addListener('toggle', scene, 'togglePostFx');
37 | dotsToggle.addListener('toggle', scene, 'toggleDots');
38 | dotsToggle.addListener('toggle', scene, 'toggleStats');
39 | simToggle.addListener('toggle', scene, 'toggleAnimate');
40 |
41 | setupAudio(scene);
42 | setupColors(scene);
43 | setupSystemUI(scene);
44 |
45 | setTimeout(function () {
46 | scene.loop.start();
47 | controls.className = 'active';
48 | }, 0);
49 | });
50 |
51 | function setupAudio(scene) {
52 | /*global Promise*/
53 | var tests = [
54 | Features.detectWebAudio(),
55 | Features.detectAudioCodecs(['audio/ogg; codecs=vorbis', 'audio/mpeg;']),
56 | Features.detectAudioAutoplay()
57 | ];
58 |
59 | var audioToggle = ToggleComponent.create({
60 | name : 'audio',
61 | key : keysTop[2]
62 | });
63 |
64 | Promise.all(tests).then(function () {
65 | scene.initAudio();
66 | scene.addListener('load:audio', function () {
67 | audioToggle.addListener('toggle', scene, 'toggleAudio');
68 | audioToggle.toggleState();
69 | });
70 | }, function (err) {
71 | audioToggle.hide();
72 | App.log('Audio features not supported');
73 | });
74 | }
75 |
76 | function setupColors(scene) {
77 | var colorsToggle = ToggleComponent.create({
78 | name : 'colors',
79 | menu : 'colors',
80 | key : keysTop[1]
81 | });
82 |
83 | Features.detectInputType('color').then(function () {
84 | scene.medusae.colors.forEach(function (color) {
85 | var controller = ColorComponent.create({
86 | label : color.label,
87 | color : color.uniform.value
88 | });
89 |
90 | controller.addListener('change', scene, 'makeDirty');
91 | colorsToggle.menuInner.appendChild(controller.element);
92 | });
93 | }, function (err) {
94 | colorsToggle.hide();
95 | App.log('Color input not supported');
96 | });
97 | }
98 |
99 | function setupSystemUI(scene) {
100 | var Format = App.Format;
101 | var system = scene.medusae.system;
102 | var particleEl = document.getElementById('particle-count');
103 | var constraintEl = document.getElementById('constraint-count');
104 | var forceEl = document.getElementById('force-count');
105 |
106 | var constraintCount = system._localConstraints.reduce(function (prev, current) {
107 | return prev + current._count;
108 | }, 0);
109 |
110 | particleEl.textContent = Format.number(system._count);
111 | constraintEl.textContent = Format.number(constraintCount);
112 | forceEl.textContent = Format.number(system._forces.length);
113 | }
114 |
--------------------------------------------------------------------------------
/static/lib-extras/three/geometries/SphereBufferGeometry.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author benaadams / https://twitter.com/ben_a_adams
3 | * based on THREE.SphereGeometry
4 | */
5 |
6 | THREE.SphereBufferGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
7 |
8 | THREE.BufferGeometry.call( this );
9 |
10 | this.type = 'SphereBufferGeometry';
11 |
12 | this.parameters = {
13 | radius: radius,
14 | widthSegments: widthSegments,
15 | heightSegments: heightSegments,
16 | phiStart: phiStart,
17 | phiLength: phiLength,
18 | thetaStart: thetaStart,
19 | thetaLength: thetaLength
20 | };
21 |
22 | radius = radius || 50;
23 |
24 | widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );
25 | heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );
26 |
27 | phiStart = phiStart !== undefined ? phiStart : 0;
28 | phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;
29 |
30 | thetaStart = thetaStart !== undefined ? thetaStart : 0;
31 | thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;
32 |
33 | var thetaEnd = thetaStart + thetaLength;
34 |
35 | var vertexCount = ( ( widthSegments + 1 ) * ( heightSegments + 1 ) );
36 |
37 | var positions = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );
38 | var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );
39 | var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 );
40 |
41 | var index = 0, vertices = [], normal = new THREE.Vector3();
42 |
43 | for ( var y = 0; y <= heightSegments; y ++ ) {
44 |
45 | var verticesRow = [];
46 |
47 | var v = y / heightSegments;
48 |
49 | for ( var x = 0; x <= widthSegments; x ++ ) {
50 |
51 | var u = x / widthSegments;
52 |
53 | var px = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
54 | var py = radius * Math.cos( thetaStart + v * thetaLength );
55 | var pz = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
56 |
57 | normal.set( px, py, pz ).normalize();
58 |
59 | positions.setXYZ( index, px, py, pz );
60 | normals.setXYZ( index, normal.x, normal.y, normal.z );
61 | uvs.setXY( index, u, 1 - v );
62 |
63 | verticesRow.push( index );
64 |
65 | index ++;
66 |
67 | }
68 |
69 | vertices.push( verticesRow );
70 |
71 | }
72 |
73 | var indices = [];
74 |
75 | for ( var y = 0; y < heightSegments; y ++ ) {
76 |
77 | for ( var x = 0; x < widthSegments; x ++ ) {
78 |
79 | var v1 = vertices[ y ][ x + 1 ];
80 | var v2 = vertices[ y ][ x ];
81 | var v3 = vertices[ y + 1 ][ x ];
82 | var v4 = vertices[ y + 1 ][ x + 1 ];
83 |
84 | if ( y !== 0 || thetaStart > 0 ) indices.push( v1, v2, v4 );
85 | if ( y !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( v2, v3, v4 );
86 |
87 | }
88 |
89 | }
90 |
91 | this.setIndex( new THREE.BufferAttribute( new Uint16Array( indices ), 1 ) );
92 | this.addAttribute( 'position', positions );
93 | this.addAttribute( 'normal', normals );
94 | this.addAttribute( 'uv', uvs );
95 |
96 | this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
97 |
98 | };
99 |
100 | THREE.SphereBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
101 | THREE.SphereBufferGeometry.prototype.constructor = THREE.SphereBufferGeometry;
102 |
103 | THREE.SphereBufferGeometry.prototype.clone = function () {
104 |
105 | var geometry = new THREE.SphereBufferGeometry(
106 | this.parameters.radius,
107 | this.parameters.widthSegments,
108 | this.parameters.heightSegments,
109 | this.parameters.phiStart,
110 | this.parameters.phiLength,
111 | this.parameters.thetaStart,
112 | this.parameters.thetaLength
113 | );
114 |
115 | geometry.copy( this );
116 |
117 | return geometry;
118 |
119 | };
120 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [Medusae][medusae-url]
2 |
3 | [![Medusae][medusae-image-url]][medusae-url]
4 |
5 | > Soft body jellyfish simulation.
6 |
7 | ### Source
8 |
9 | - Jellyfish physics and graphics: [Medusae.js][medusae-source-url]
10 | - Jellyfish hood shader: [bulb-frag.glsl][hood-glsl-source-url]
11 | - Point repulsor: [PointRepulsorForce.js][point-force-source-url]
12 | - Ambient dust animation and graphics: [Dust.js][dust-source-url] and [dust-vert.glsl][dust-glsl-source-url]
13 | - Interpolated physics rendering: [Looper.js][looper-source-url] and [lerp_pos_vertex.glsl][lerp-vert-source-url]
14 | - WebAudio player: [AudioController.js][audio-source-url]
15 | - Canvas graph: [GraphComponent.js][graph-source-url]
16 | - Lens dirt post effect: [LensDirtPass.js][lens-dirt-source-url]
17 |
18 | ### Process
19 |
20 | - Screenshot and video [album][flickr-album-url]
21 | - Progress release [code and builds][source-releases-url]
22 | - Procedural GLSL hood texture [sketch][hood-glsl-url]
23 | - Procedural Canvas2D water drop texture [sketch][water-canvas-url]
24 | - Canvas graph [sketch][canvas-graph-url]
25 |
26 | ### Resources
27 |
28 | - Photography of [Alexander Semenov][semenov-url]
29 | - Paper on [advanced character physics][character-physics-url] by Thomas Jakobsen
30 | - Article on [interpolated physics rendering][interpolated-physics-url] by Andrew Petersen
31 |
32 | ### Credits
33 |
34 | - Concept, Design & Code: [Ash Weeks][portfolio-url]
35 | - Audio Design: JP Arsenault
36 | - Physics: [Particulate][particulate-url]
37 | - Graphics: [Three][three-url]
38 |
39 | ### License
40 |
41 | The Artistic License 2.0, see [LICENSE][license-source-url] for details.
42 |
43 | ### Development
44 |
45 | [Grunt][grunt-url] is used for building and developing the project.
46 |
47 | ```sh
48 | npm install
49 | grunt server
50 | ```
51 |
52 | [medusae-url]: https://milcktoast.com/medusae/
53 | [medusae-image-url]: https://farm2.staticflickr.com/1628/23884999242_457d932c7a_h.jpg
54 | [medusae-source-url]: https://github.com/jpweeks/particulate-medusae/blob/master/static/js/items/Medusae.js
55 | [hood-glsl-source-url]: https://github.com/jpweeks/particulate-medusae/blob/master/static/glsl/shaders/bulb-frag.glsl
56 | [point-force-source-url]: https://github.com/jpweeks/particulate-medusae/blob/master/static/js/forces/PointRepulsorForce.js
57 | [dust-source-url]: https://github.com/jpweeks/particulate-medusae/blob/master/static/js/items/Dust.js
58 | [dust-glsl-source-url]: https://github.com/jpweeks/particulate-medusae/blob/master/static/glsl/shaders/dust-vert.glsl
59 | [looper-source-url]: https://github.com/jpweeks/particulate-medusae/blob/master/static/js/utils/Looper.js
60 | [lerp-vert-source-url]: https://github.com/jpweeks/particulate-medusae/blob/master/static/glsl/shader-chunks/lerp_pos_vertex.glsl
61 | [audio-source-url]: https://github.com/jpweeks/particulate-medusae/blob/master/static/js/controllers/AudioController.js
62 | [graph-source-url]: https://github.com/jpweeks/particulate-medusae/blob/master/static/js/components/GraphComponent.js
63 | [lens-dirt-source-url]: https://github.com/jpweeks/particulate-medusae/blob/master/static/js/post-processing/LensDirtPass.js
64 | [flickr-album-url]: https://www.flickr.com/photos/jpweeks/sets/72157646887502644/
65 | [source-releases-url]: https://github.com/jpweeks/particulate-medusae/releases
66 | [hood-glsl-url]: http://glslsandbox.com/e#20575.0
67 | [water-canvas-url]: https://jsbin.com/guqodi/11/edit?js,output
68 | [canvas-graph-url]: https://jsbin.com/yoteko/10/edit?js,output
69 | [semenov-url]: https://www.flickr.com/photos/a_semenov/7570746886/
70 | [character-physics-url]: http://web.archive.org/web/20080410171619/http://www.teknikus.dk/tj/gdc2001.htm
71 | [interpolated-physics-url]: http://kirbysayshi.com/2013/09/24/interpolated-physics-rendering.html
72 | [portfolio-url]: https://milcktoast.com
73 | [source-url]: https://github.com/jpweeks/particulate-medusae
74 | [three-url]: http://threejs.org
75 | [particulate-url]: http://particulatejs.org
76 | [license-source-url]: https://github.com/jpweeks/particulate-medusae/blob/master/LICENSE
77 | [grunt-url]: http://gruntjs.com
78 |
--------------------------------------------------------------------------------
/static/js/post-processing/LensDirtTexture.js:
--------------------------------------------------------------------------------
1 | App.LensDirtTexture = LensDirtTexture;
2 | function LensDirtTexture(size, cells, opts) {
3 | this.canvas = document.createElement('canvas');
4 | this.ctx = this.canvas.getContext('2d');
5 | this.texture = new THREE.Texture(this.canvas);
6 | this.drawTexture(size, cells, opts);
7 | }
8 |
9 | LensDirtTexture.prototype.grayscaleColor = function (start, range, alpha) {
10 | var c = Math.floor(Math.random() * range) + start;
11 | return 'rgba(' + [c, c, c, alpha].join(',') + ')';
12 | };
13 |
14 | LensDirtTexture.prototype.createGradients = function (ctx, count, radius) {
15 | var step = Math.PI * 2 / (count + 1);
16 | var angle = 0;
17 |
18 | var gradients = [];
19 | var colorA, colorB, alphaA, alphaB;
20 | var gradient, gx0, gy0, gx1, gy1;
21 |
22 | for (var i = 0; i < count; i ++) {
23 | gx0 = Math.cos(angle) * radius;
24 | gy0 = Math.sin(angle) * radius;
25 | gx1 = Math.cos(angle + Math.PI) * radius;
26 | gy1 = Math.sin(angle + Math.PI) * radius;
27 |
28 | alphaA = Math.random() * 0.1;
29 | alphaB = Math.random() * 0.5;
30 | colorA = this.grayscaleColor(100, 100, alphaA);
31 | colorB = this.grayscaleColor(100, 100, alphaB);
32 |
33 | gradient = ctx.createLinearGradient(gx0, gy0, gx1, gy1);
34 | gradient.addColorStop(0.2, colorA);
35 | gradient.addColorStop(0.8, colorB);
36 | gradients.push(gradient);
37 | gradient._alpha = alphaB;
38 |
39 | angle += step;
40 | }
41 |
42 | return gradients;
43 | };
44 |
45 | LensDirtTexture.prototype.drawBlob = function (ctx, rx, ry, segments) {
46 | var step = Math.PI * 2 / segments;
47 | var angle = 0;
48 | var sx = Math.random() * 100;
49 | var sy = Math.random() * 100;
50 | var x, y, nx, ny;
51 |
52 | ctx.beginPath();
53 |
54 | for (var i = 0, il = segments - 1; i < il; i ++) {
55 | x = Math.cos(angle) * rx;
56 | y = Math.sin(angle) * ry;
57 | nx = (sx + x) * 0.01;
58 | ny = (sy + y) * 0.01;
59 | x += noise.simplex2(nx, ny) * 5;
60 | y += noise.simplex2(nx, ny) * 5;
61 |
62 | angle += step;
63 |
64 | if (i === 0) {
65 | ctx.moveTo(x, y);
66 | } else {
67 | ctx.lineTo(x, y);
68 | }
69 | }
70 |
71 | ctx.closePath();
72 | ctx.fill();
73 | };
74 |
75 | LensDirtTexture.prototype.drawShadow = function (ctx, iterations) {
76 | ctx.save();
77 | ctx.shadowBlur = 10;
78 | ctx.shadowOffsetX = 0;
79 | ctx.shadowOffsetY = 0;
80 | ctx.shadowColor = this.grayscaleColor(200, 10, 1);
81 |
82 | for (var i = 0; i < iterations; i ++) {
83 | ctx.stroke();
84 | }
85 |
86 | ctx.restore();
87 | };
88 |
89 | LensDirtTexture.prototype.drawTexture = function (size, cells, opts) {
90 | opts = opts || {};
91 |
92 | var canvas = this.canvas;
93 | var ctx = this.ctx;
94 |
95 | var detail = opts.detail || 10;
96 | var cellPad = opts.cellPad || 10;
97 |
98 | var cellSize = size / cells;
99 | var cellSizeHalf = cellSize * 0.5;
100 | var blobRad = (cellSize - cellPad) * 0.5;
101 | var blobRadHalf = blobRad * 0.5;
102 |
103 | var gradients = this.createGradients(ctx, cells, cellSize);
104 | var gradient, gi, rx, ry;
105 |
106 | canvas.width = canvas.height = size;
107 | ctx.lineWidth = 1;
108 |
109 | for (var i = 0; i < cells; i ++) {
110 | ctx.setTransform(1, 0, 0, 1, 0, 0);
111 | ctx.translate(0, cellSize * i + cellSizeHalf);
112 |
113 | for (var j = 0; j < cells; j ++) {
114 | ctx.translate(j === 0 ? cellSizeHalf : cellSize, 0);
115 | rx = Math.random() * blobRadHalf + blobRadHalf;
116 | ry = Math.random() * blobRadHalf + blobRadHalf;
117 | gi = Math.floor(Math.random() * gradients.length);
118 | gradient = gradients[gi];
119 |
120 | ctx.fillStyle = gradient;
121 | ctx.strokeStyle = this.grayscaleColor(
122 | 60, 30, gradient._alpha * 0.5);
123 |
124 | this.drawBlob(ctx, rx, ry, detail);
125 | this.drawShadow(ctx, 2);
126 | }
127 | }
128 |
129 | this.texture.needsUpdate = true;
130 | };
131 |
--------------------------------------------------------------------------------
/static/lib-extras/three/postprocessing/BloomPass.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | */
4 |
5 | THREE.BloomPass = function ( strength, kernelSize, sigma, resolution ) {
6 |
7 | strength = ( strength !== undefined ) ? strength : 1;
8 | kernelSize = ( kernelSize !== undefined ) ? kernelSize : 25;
9 | sigma = ( sigma !== undefined ) ? sigma : 4.0;
10 | resolution = ( resolution !== undefined ) ? resolution : 256;
11 |
12 | // render targets
13 |
14 | var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat };
15 |
16 | this.renderTargetX = new THREE.WebGLRenderTarget( resolution, resolution, pars );
17 | this.renderTargetY = new THREE.WebGLRenderTarget( resolution, resolution, pars );
18 |
19 | // copy material
20 |
21 | if ( THREE.CopyShader === undefined )
22 | console.error( "THREE.BloomPass relies on THREE.CopyShader" );
23 |
24 | var copyShader = THREE.CopyShader;
25 |
26 | this.copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms );
27 |
28 | this.copyUniforms[ "opacity" ].value = strength;
29 |
30 | this.materialCopy = new THREE.ShaderMaterial( {
31 |
32 | uniforms: this.copyUniforms,
33 | vertexShader: copyShader.vertexShader,
34 | fragmentShader: copyShader.fragmentShader,
35 | blending: THREE.AdditiveBlending,
36 | transparent: true
37 |
38 | } );
39 |
40 | // convolution material
41 |
42 | if ( THREE.ConvolutionShader === undefined )
43 | console.error( "THREE.BloomPass relies on THREE.ConvolutionShader" );
44 |
45 | var convolutionShader = THREE.ConvolutionShader;
46 |
47 | this.convolutionUniforms = THREE.UniformsUtils.clone( convolutionShader.uniforms );
48 |
49 | this.convolutionUniforms[ "uImageIncrement" ].value = THREE.BloomPass.blurx;
50 | this.convolutionUniforms[ "cKernel" ].value = THREE.ConvolutionShader.buildKernel( sigma );
51 |
52 | this.materialConvolution = new THREE.ShaderMaterial( {
53 |
54 | uniforms: this.convolutionUniforms,
55 | vertexShader: convolutionShader.vertexShader,
56 | fragmentShader: convolutionShader.fragmentShader,
57 | defines: {
58 | "KERNEL_SIZE_FLOAT": kernelSize.toFixed( 1 ),
59 | "KERNEL_SIZE_INT": kernelSize.toFixed( 0 )
60 | }
61 |
62 | } );
63 |
64 | this.enabled = true;
65 | this.needsSwap = false;
66 | this.clear = false;
67 |
68 |
69 | this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
70 | this.scene = new THREE.Scene();
71 |
72 | this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
73 | this.scene.add( this.quad );
74 |
75 | };
76 |
77 | THREE.BloomPass.prototype = {
78 |
79 | render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
80 |
81 | if ( maskActive ) renderer.context.disable( renderer.context.STENCIL_TEST );
82 |
83 | // Render quad with blured scene into texture (convolution pass 1)
84 |
85 | this.quad.material = this.materialConvolution;
86 |
87 | this.convolutionUniforms[ "tDiffuse" ].value = readBuffer;
88 | this.convolutionUniforms[ "uImageIncrement" ].value = THREE.BloomPass.blurX;
89 |
90 | renderer.render( this.scene, this.camera, this.renderTargetX, true );
91 |
92 |
93 | // Render quad with blured scene into texture (convolution pass 2)
94 |
95 | this.convolutionUniforms[ "tDiffuse" ].value = this.renderTargetX;
96 | this.convolutionUniforms[ "uImageIncrement" ].value = THREE.BloomPass.blurY;
97 |
98 | renderer.render( this.scene, this.camera, this.renderTargetY, true );
99 |
100 | // Render original scene with superimposed blur to texture
101 |
102 | this.quad.material = this.materialCopy;
103 |
104 | this.copyUniforms[ "tDiffuse" ].value = this.renderTargetY;
105 |
106 | if ( maskActive ) renderer.context.enable( renderer.context.STENCIL_TEST );
107 |
108 | renderer.render( this.scene, this.camera, readBuffer, this.clear );
109 |
110 | }
111 |
112 | };
113 |
114 | THREE.BloomPass.blurX = new THREE.Vector2( 0.001953125, 0.0 );
115 | THREE.BloomPass.blurY = new THREE.Vector2( 0.0, 0.001953125 );
116 |
--------------------------------------------------------------------------------
/pages/_base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Particulate Medusae
10 |
11 |
12 |
13 |
14 |
15 |
16 | {% if TEMPLATE_DEBUG %}
17 |
18 | {% endif %}
19 |
20 |
21 |
22 | Medusae
23 |
24 |
25 | - Concept, Design & Code
26 | - Ash Weeks
27 |
28 |
29 | - Audio Design
30 | - JP Arsenault
31 |
32 |
33 |
43 |
44 |
45 | - Source
46 | - Github
47 |
48 |
49 | - Process
50 | - Flickr
51 |
52 |
53 |
54 |
55 |
56 |
57 |
60 |
63 |
64 |
67 |
70 |
73 |
76 |
77 |
78 |
79 |
80 |
81 | - Particles
82 |
83 |
84 |
85 | - Constraints
86 |
87 |
88 |
89 | - Forces
90 |
91 |
92 |
93 |
94 |
95 |
96 | {% if TEMPLATE_DEBUG %}
97 |
98 |
99 |
100 |
101 |
102 | {# Run tests by appending ?test=true to the url #}
103 |
109 | {% else %}
110 |
111 |
112 | {% endif %}
113 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/static/js/components/GraphComponent.js:
--------------------------------------------------------------------------------
1 | App.GraphComponent = GraphComponent;
2 | function GraphComponent(opts) {
3 | opts = opts || {};
4 |
5 | this.max = 0;
6 | this.current = 0;
7 |
8 | this.updateFactor = opts.updateFactor || 0.1;
9 | this.drawOffset = opts.drawOffset || 2;
10 | this.pxRatio = opts.pixelRatio || window.devicePixelRatio;
11 |
12 | this.createBuffers();
13 | this.setSize(opts.width || 380, opts.height || 24);
14 | this.createLineBuffer();
15 | this.createElement();
16 | this.setLabel(opts.label || '');
17 | }
18 |
19 | GraphComponent.create = App.ctor(GraphComponent);
20 |
21 | GraphComponent.prototype.setSize = function (width, height) {
22 | var pxRatio = this.pxRatio;
23 |
24 | this.width = width;
25 | this.height = height;
26 | this.fullWidth = width * pxRatio;
27 | this.fullHeight = height * pxRatio;
28 |
29 | this.canvas.width = this.buffer.width = this.fullWidth;
30 | this.canvas.height = this.buffer.height = this.fullHeight;
31 |
32 | this.canvas.style.width = width + 'px';
33 | this.canvas.style.height = height + 'px';
34 | };
35 |
36 | GraphComponent.prototype.appendTo = function (parent) {
37 | parent.appendChild(this.element);
38 | };
39 |
40 | GraphComponent.prototype.createElement = function () {
41 | var el = this.element = document.createElement('div');
42 | var labelContainer = document.createElement('div');
43 | var label = this._labelEl = document.createTextNode('');
44 | var value = this._valueEl = document.createTextNode('');
45 | var canvas = this.canvas;
46 |
47 | el.className = 'graph';
48 | labelContainer.className = 'label';
49 |
50 | el.appendChild(canvas);
51 | el.appendChild(labelContainer);
52 |
53 | labelContainer.appendChild(label);
54 | labelContainer.appendChild(value);
55 | };
56 |
57 | GraphComponent.prototype.setLabel = function (label) {
58 | this._labelEl.textContent = label + ' ';
59 | };
60 |
61 | GraphComponent.prototype.createBuffers = function () {
62 | this.canvas = document.createElement('canvas');
63 | this.ctx = this.canvas.getContext('2d');
64 | this.buffer = document.createElement('canvas');
65 | this.btx = this.buffer.getContext('2d');
66 | };
67 |
68 | GraphComponent.prototype.grayscale = function (v, a) {
69 | return 'rgba(' + [v, v, v, a].join(',') + ')';
70 | };
71 |
72 | GraphComponent.prototype.createLineBuffer = function () {
73 | var height = this.fullHeight;
74 | var pxRatio = this.pxRatio;
75 |
76 | var line = document.createElement('canvas');
77 | var ltx = line.getContext('2d');
78 |
79 | line.width = pxRatio;
80 | line.height = height * 2 + 10;
81 |
82 | ltx.fillStyle = this.grayscale(255, 0.7);
83 | ltx.fillRect(0, height, pxRatio, pxRatio);
84 | ltx.fillStyle = this.grayscale(255, 0.1);
85 | ltx.fillRect(0, height + 2 * pxRatio, pxRatio, height + 10);
86 |
87 | this.line = line;
88 | };
89 |
90 | GraphComponent.prototype.start = function () {
91 | this._startTime = Date.now();
92 | };
93 |
94 | GraphComponent.prototype.end = function () {
95 | this._endTime = Date.now();
96 | };
97 |
98 | GraphComponent.prototype.reset = function () {
99 | this._startTime = this._endTime = 0;
100 | };
101 |
102 | GraphComponent.prototype._startTime = 0;
103 | GraphComponent.prototype._endTime = 0;
104 | GraphComponent.prototype._textTick = 0;
105 |
106 | GraphComponent.prototype.update = function (value, skipLabel) {
107 | var width = this.fullWidth;
108 | var height = this.fullHeight;
109 | var pxRatio = this.pxRatio;
110 | var drawOffset = this.drawOffset;
111 |
112 | var buffer = this.buffer;
113 | var canvas = this.canvas;
114 | var btx = this.btx;
115 | var ctx = this.ctx;
116 |
117 | var current, max;
118 |
119 | if (value == null) {
120 | value = this._endTime - this._startTime;
121 | current = this.current += (value - this.current) * this.updateFactor;
122 | max = this.max *= 0.99;
123 | } else {
124 | current = max = value;
125 | }
126 |
127 | if (current > max) {
128 | max = this.max = current;
129 | }
130 |
131 | btx.clearRect(0, 0, width, height);
132 | btx.drawImage(canvas, -drawOffset * pxRatio, 0, width, height);
133 |
134 | ctx.clearRect(0, 0, width, height);
135 | ctx.drawImage(buffer, 0, 0, width, height);
136 | ctx.drawImage(this.line,
137 | 0, current * height / (max || 1), pxRatio, height,
138 | width - pxRatio, 0, pxRatio, height);
139 |
140 | if (!skipLabel && ++ this._textTick > 6) {
141 | this._textTick = 0;
142 | this._valueEl.textContent = current.toFixed(3);
143 | }
144 |
145 | this._startTime = this._endTime = 0;
146 | };
147 |
--------------------------------------------------------------------------------
/static/js/utils/Features.js:
--------------------------------------------------------------------------------
1 | /*global Promise*/
2 | var Features = App.Features = {};
3 |
4 | Features.detectWebAudio = function () {
5 | return new Promise(function (resolve, reject) {
6 | var prefixed = 'webkitAudioContext' in window;
7 | var unprefixed = 'AudioContext' in window;
8 |
9 | if (prefixed || unprefixed) {
10 | resolve();
11 | } else {
12 | reject();
13 | }
14 | });
15 | };
16 |
17 | Features.detectAudioCodecs = function (codecs) {
18 | return new Promise(function (resolve, reject) {
19 | var audio = new Audio();
20 | var canPlay = codecs.find(function (codec) {
21 | return !!audio.canPlayType(codec).replace(/^no$/, '');
22 | });
23 |
24 | if (canPlay) {
25 | resolve();
26 | } else {
27 | reject();
28 | }
29 | });
30 | };
31 |
32 | Features.detectAudioAutoplay = function () {
33 | return new Promise(function (resolve, reject) {
34 | var mp3 = 'data:audio/mpeg;base64,/+MYxAAAAANIAUAAAASEEB/jwOFM/0MM/90b/+RhST//w4NFwOjf///PZu////9lns5GFDv//l9GlUIEEIAAAgIg8Ir/JGq3/+MYxDsLIj5QMYcoAP0dv9HIjUcH//yYSg+CIbkGP//8w0bLVjUP///3Z0x5QCAv/yLjwtGKTEFNRTMuOTeqqqqqqqqqqqqq/+MYxEkNmdJkUYc4AKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq';
35 | var ogg = 'data:audio/ogg;base64,T2dnUwACAAAAAAAAAADqnjMlAAAAAOyyzPIBHgF2b3JiaXMAAAAAAUAfAABAHwAAQB8AAEAfAACZAU9nZ1MAAAAAAAAAAAAA6p4zJQEAAAANJGeqCj3//////////5ADdm9yYmlzLQAAAFhpcGguT3JnIGxpYlZvcmJpcyBJIDIwMTAxMTAxIChTY2hhdWZlbnVnZ2V0KQAAAAABBXZvcmJpcw9CQ1YBAAABAAxSFCElGVNKYwiVUlIpBR1jUFtHHWPUOUYhZBBTiEkZpXtPKpVYSsgRUlgpRR1TTFNJlVKWKUUdYxRTSCFT1jFloXMUS4ZJCSVsTa50FkvomWOWMUYdY85aSp1j1jFFHWNSUkmhcxg6ZiVkFDpGxehifDA6laJCKL7H3lLpLYWKW4q91xpT6y2EGEtpwQhhc+211dxKasUYY4wxxsXiUyiC0JBVAAABAABABAFCQ1YBAAoAAMJQDEVRgNCQVQBABgCAABRFcRTHcRxHkiTLAkJDVgEAQAAAAgAAKI7hKJIjSZJkWZZlWZameZaouaov+64u667t6roOhIasBACAAAAYRqF1TCqDEEPKQ4QUY9AzoxBDDEzGHGNONKQMMogzxZAyiFssLqgQBKEhKwKAKAAAwBjEGGIMOeekZFIi55iUTkoDnaPUUcoolRRLjBmlEluJMYLOUeooZZRCjKXFjFKJscRUAABAgAMAQICFUGjIigAgCgCAMAYphZRCjCnmFHOIMeUcgwwxxiBkzinoGJNOSuWck85JiRhjzjEHlXNOSuekctBJyaQTAAAQ4AAAEGAhFBqyIgCIEwAwSJKmWZomipamiaJniqrqiaKqWp5nmp5pqqpnmqpqqqrrmqrqypbnmaZnmqrqmaaqiqbquqaquq6nqrZsuqoum65q267s+rZru77uqapsm6or66bqyrrqyrbuurbtS56nqqKquq5nqq6ruq5uq65r25pqyq6purJtuq4tu7Js664s67pmqq5suqotm64s667s2rYqy7ovuq5uq7Ks+6os+75s67ru2rrwi65r66os674qy74x27bwy7ouHJMnqqqnqq7rmarrqq5r26rr2rqmmq5suq4tm6or26os67Yry7aumaosm64r26bryrIqy77vyrJui67r66Ys67oqy8Lu6roxzLat+6Lr6roqy7qvyrKuu7ru+7JuC7umqrpuyrKvm7Ks+7auC8us27oxuq7vq7It/KosC7+u+8Iy6z5jdF1fV21ZGFbZ9n3d95Vj1nVhWW1b+V1bZ7y+bgy7bvzKrQvLstq2scy6rSyvrxvDLux8W/iVmqratum6um7Ksq/Lui60dd1XRtf1fdW2fV+VZd+3hV9pG8OwjK6r+6os68Jry8ov67qw7MIvLKttK7+r68ow27qw3L6wLL/uC8uq277v6rrStXVluX2fsSu38QsAABhwAAAIMKEMFBqyIgCIEwBAEHIOKQahYgpCCKGkEEIqFWNSMuakZM5JKaWUFEpJrWJMSuaclMwxKaGUlkopqYRSWiqlxBRKaS2l1mJKqcVQSmulpNZKSa2llGJMrcUYMSYlc05K5pyUklJrJZXWMucoZQ5K6iCklEoqraTUYuacpA46Kx2E1EoqMZWUYgupxFZKaq2kFGMrMdXUWo4hpRhLSrGVlFptMdXWWqs1YkxK5pyUzDkqJaXWSiqtZc5J6iC01DkoqaTUYiopxco5SR2ElDLIqJSUWiupxBJSia20FGMpqcXUYq4pxRZDSS2WlFosqcTWYoy1tVRTJ6XFklKMJZUYW6y5ttZqDKXEVkqLsaSUW2sx1xZjjqGkFksrsZWUWmy15dhayzW1VGNKrdYWY40x5ZRrrT2n1mJNMdXaWqy51ZZbzLXnTkprpZQWS0oxttZijTHmHEppraQUWykpxtZara3FXEMpsZXSWiypxNhirLXFVmNqrcYWW62ltVprrb3GVlsurdXcYqw9tZRrrLXmWFNtBQAADDgAAASYUAYKDVkJAEQBAADGMMYYhEYpx5yT0ijlnHNSKucghJBS5hyEEFLKnINQSkuZcxBKSSmUklJqrYVSUmqttQIAAAocAAACbNCUWByg0JCVAEAqAIDBcTRNFFXVdX1fsSxRVFXXlW3jVyxNFFVVdm1b+DVRVFXXtW3bFn5NFFVVdmXZtoWiqrqybduybgvDqKqua9uybeuorqvbuq3bui9UXVmWbVu3dR3XtnXd9nVd+Bmzbeu2buu+8CMMR9/4IeTj+3RCCAAAT3AAACqwYXWEk6KxwEJDVgIAGQAAgDFKGYUYM0gxphhjTDHGmAAAgAEHAIAAE8pAoSErAoAoAADAOeecc84555xzzjnnnHPOOeecc44xxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY0wAwE6EA8BOhIVQaMhKACAcAABACCEpKaWUUkoRU85BSSmllFKqFIOMSkoppZRSpBR1lFJKKaWUIqWgpJJSSimllElJKaWUUkoppYw6SimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaVUSimllFJKKaWUUkoppRQAYPLgAACVYOMMK0lnhaPBhYasBAByAwAAhRiDEEJpraRUUkolVc5BKCWUlEpKKZWUUqqYgxBKKqmlklJKKbXSQSihlFBKKSWUUkooJYQQSgmhlFRCK6mEUkoHoYQSQimhhFRKKSWUzkEoIYUOQkmllNRCSB10VFIpIZVSSiklpZQ6CKGUklJLLZVSWkqpdBJSKamV1FJqqbWSUgmhpFZKSSWl0lpJJbUSSkklpZRSSymFVFJJJYSSUioltZZaSqm11lJIqZWUUkqppdRSSiWlkEpKqZSSUmollZRSaiGVlEpJKaTUSimlpFRCSamlUlpKLbWUSkmptFRSSaWUlEpJKaVSSksppRJKSqmllFpJKYWSUkoplZJSSyW1VEoKJaWUUkmptJRSSymVklIBAEAHDgAAAUZUWoidZlx5BI4oZJiAAgAAQABAgAkgMEBQMApBgDACAQAAAADAAAAfAABHARAR0ZzBAUKCwgJDg8MDAAAAAAAAAAAAAACAT2dnUwAEAAAAAAAAAADqnjMlAgAAADzQPmcBAQA=';
36 | var audio = new Audio();
37 | var src = audio.canPlayType('audio/ogg') ? ogg : mp3;
38 |
39 | audio.autoplay = true;
40 | audio.volume = 0;
41 |
42 | var _reject = setTimeout(reject, 20);
43 | audio.addEventListener('play', function() {
44 | clearTimeout(_reject);
45 | resolve();
46 | }, false);
47 |
48 | audio.src = src;
49 | audio.play();
50 | });
51 | };
52 |
53 | Features.detectInputType = function (type) {
54 | return new Promise(function (resolve, reject) {
55 | var el = document.createElement('input');
56 | el.setAttribute('type', type);
57 |
58 | if (el.type === type) {
59 | resolve();
60 | } else {
61 | reject();
62 | }
63 | });
64 | };
65 |
--------------------------------------------------------------------------------
/static/scss/tests.scss:
--------------------------------------------------------------------------------
1 | $green : #5c4;
2 | $red : #d33;
3 |
4 | .testing {
5 | .controls,
6 | #container {
7 | display: none;
8 | }
9 | }
10 |
11 | #qunit {
12 | background: #222;
13 | color: #ccc;
14 | font: 14px/20px "Source Code Pro", "Menlo", monospace;
15 | padding: 30px 20px;
16 | border-bottom: 5px solid #111;
17 | position: relative;
18 |
19 | &.qunit-is-hidden {
20 | display: none;
21 | }
22 |
23 | * {
24 | margin: 0;
25 | padding: 0;
26 | border: 0;
27 | }
28 |
29 | #qunit-header {
30 | font-size: 24px;
31 | line-height: 30px;
32 | margin: 0 520px 10px 0;
33 |
34 | a {
35 | color: #ccc;
36 | text-decoration: none;
37 |
38 | &:hover {
39 | color: #ccc;
40 | text-decoration: underline;
41 | }
42 | }
43 | }
44 |
45 | #qunit-testresult {
46 | margin: 0 -20px 20px;
47 | padding: 10px 20px;
48 | background: #111;
49 |
50 | .module-name {
51 | font-weight: bold;
52 | }
53 |
54 | br {
55 | content: '';
56 | }
57 |
58 | span:before {
59 | content: ' ';
60 | }
61 |
62 | span.failed {
63 | color: $red;
64 | }
65 |
66 | span.passed {
67 | color: $green;
68 | }
69 | }
70 |
71 | #qunit-testrunner-toolbar {
72 | position: absolute;
73 | right: 20px;
74 | top: 30px;
75 |
76 | input {
77 | font: inherit;
78 | margin: 0 6px 0 24px;
79 | }
80 |
81 | label {
82 | display: inline-block;
83 | }
84 |
85 | .qunit-filter {
86 | display: none;
87 | }
88 | }
89 |
90 | #qunit-modulefilter-container {
91 | position: absolute;
92 | right: 0;
93 | top: 30px;
94 |
95 | label {
96 | margin-right: 10px;
97 | }
98 | }
99 |
100 | #qunit-banner {
101 | position: absolute;
102 | top: 0;
103 | left: 0;
104 | right: 0;
105 | height: 10px;
106 |
107 | &.qunit-fail {
108 | background-color: $red;
109 | }
110 |
111 | &.qunit-pass {
112 | background-color: $green;
113 | }
114 | }
115 |
116 | #qunit-tests {
117 | color: #666;
118 | list-style: outside decimal;
119 | padding-left: 30px;
120 | line-height: 30px;
121 |
122 | b.counts {
123 | font-weight: normal;
124 | b {
125 | font-weight: normal;
126 | }
127 | }
128 |
129 | &.hidepass {
130 | li.pass,
131 | li.running {
132 | display: none;
133 | }
134 | }
135 |
136 | li {
137 | a {
138 | color: #666;
139 | margin: 0 6px;
140 | text-decoration: underline;
141 |
142 | &:hover {
143 | color: #ccc;
144 | }
145 | }
146 |
147 | b.counts {
148 | color: #666;
149 | }
150 |
151 | &.fail {
152 | span.test-message {
153 | color: $red;
154 | }
155 |
156 | strong {
157 | color: $red;
158 |
159 | b.counts b.failed {
160 | color: $red;
161 | }
162 | }
163 | }
164 |
165 | &.pass {
166 | b.passed {
167 | color: $green;
168 | }
169 |
170 | span.test-message {
171 | color: $green;
172 | }
173 |
174 | strong {
175 | color: #ccc;
176 | }
177 | }
178 |
179 | strong {
180 | font-weight: normal;
181 | cursor: pointer;
182 | }
183 | }
184 |
185 | ol {
186 | list-style: inside lower-alpha;
187 |
188 | li table {
189 | border-collapse: collapse;
190 |
191 | td pre {
192 | background-color: #181818;
193 | border-radius: 4px;
194 | color: #ccc;
195 | font: inherit;
196 | margin: 0 0 5px 10px;
197 | padding: 5px 10px;
198 | white-space: pre-wrap;
199 | word-wrap: break-word;
200 | line-height: 20px;
201 | float: left;
202 | }
203 |
204 | th {
205 | font-weight: normal;
206 | line-height: 30px;
207 | text-align: right;
208 | }
209 |
210 | th,
211 | td {
212 | padding: 0;
213 | vertical-align: top;
214 | }
215 |
216 | tr.test-actual {
217 | color: $red;
218 | }
219 |
220 | tr.test-diff {
221 | del {
222 | color: $green;
223 | }
224 |
225 | ins {
226 | color: $red;
227 | }
228 |
229 | del,
230 | ins {
231 | text-decoration: none;
232 | }
233 | }
234 |
235 | tr.test-expected {
236 | color: $green;
237 | }
238 | }
239 | }
240 |
241 | .qunit-assert-list.qunit-collapsed {
242 | display: none;
243 | }
244 | }
245 |
246 | #qunit-userAgent {
247 | color: #666;
248 | font-size: 10px;
249 | margin-bottom: 10px;
250 | }
251 | }
252 |
253 | #qunit-fixture {
254 | position: absolute;
255 | height: 768px;
256 | left: -1024px;
257 | top: -768px;
258 | width: 1024px;
259 | }
260 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/BokehShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Depth-of-field shader with bokeh
5 | * ported from GLSL shader by Martins Upitis
6 | * http://artmartinsh.blogspot.com/2010/02/glsl-lens-blur-filter-with-bokeh.html
7 | */
8 |
9 | THREE.BokehShader = {
10 |
11 | uniforms: {
12 |
13 | "tColor": { type: "t", value: null },
14 | "tDepth": { type: "t", value: null },
15 | "focus": { type: "f", value: 1.0 },
16 | "aspect": { type: "f", value: 1.0 },
17 | "aperture": { type: "f", value: 0.025 },
18 | "maxblur": { type: "f", value: 1.0 }
19 |
20 | },
21 |
22 | vertexShader: [
23 |
24 | "varying vec2 vUv;",
25 |
26 | "void main() {",
27 |
28 | "vUv = uv;",
29 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
30 |
31 | "}"
32 |
33 | ].join("\n"),
34 |
35 | fragmentShader: [
36 |
37 | "varying vec2 vUv;",
38 |
39 | "uniform sampler2D tColor;",
40 | "uniform sampler2D tDepth;",
41 |
42 | "uniform float maxblur;", // max blur amount
43 | "uniform float aperture;", // aperture - bigger values for shallower depth of field
44 |
45 | "uniform float focus;",
46 | "uniform float aspect;",
47 |
48 | "void main() {",
49 |
50 | "vec2 aspectcorrect = vec2( 1.0, aspect );",
51 |
52 | "vec4 depth1 = texture2D( tDepth, vUv );",
53 |
54 | "float factor = depth1.x - focus;",
55 |
56 | "vec2 dofblur = vec2 ( clamp( factor * aperture, -maxblur, maxblur ) );",
57 |
58 | "vec2 dofblur9 = dofblur * 0.9;",
59 | "vec2 dofblur7 = dofblur * 0.7;",
60 | "vec2 dofblur4 = dofblur * 0.4;",
61 |
62 | "vec4 col = vec4( 0.0 );",
63 |
64 | "col += texture2D( tColor, vUv.xy );",
65 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.0, 0.4 ) * aspectcorrect ) * dofblur );",
66 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.15, 0.37 ) * aspectcorrect ) * dofblur );",
67 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.29, 0.29 ) * aspectcorrect ) * dofblur );",
68 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.37, 0.15 ) * aspectcorrect ) * dofblur );",
69 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.40, 0.0 ) * aspectcorrect ) * dofblur );",
70 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.37, -0.15 ) * aspectcorrect ) * dofblur );",
71 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.29, -0.29 ) * aspectcorrect ) * dofblur );",
72 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.15, -0.37 ) * aspectcorrect ) * dofblur );",
73 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.0, -0.4 ) * aspectcorrect ) * dofblur );",
74 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.15, 0.37 ) * aspectcorrect ) * dofblur );",
75 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.29, 0.29 ) * aspectcorrect ) * dofblur );",
76 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.37, 0.15 ) * aspectcorrect ) * dofblur );",
77 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.4, 0.0 ) * aspectcorrect ) * dofblur );",
78 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.37, -0.15 ) * aspectcorrect ) * dofblur );",
79 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.29, -0.29 ) * aspectcorrect ) * dofblur );",
80 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.15, -0.37 ) * aspectcorrect ) * dofblur );",
81 |
82 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.15, 0.37 ) * aspectcorrect ) * dofblur9 );",
83 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.37, 0.15 ) * aspectcorrect ) * dofblur9 );",
84 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.37, -0.15 ) * aspectcorrect ) * dofblur9 );",
85 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.15, -0.37 ) * aspectcorrect ) * dofblur9 );",
86 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.15, 0.37 ) * aspectcorrect ) * dofblur9 );",
87 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.37, 0.15 ) * aspectcorrect ) * dofblur9 );",
88 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.37, -0.15 ) * aspectcorrect ) * dofblur9 );",
89 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.15, -0.37 ) * aspectcorrect ) * dofblur9 );",
90 |
91 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.29, 0.29 ) * aspectcorrect ) * dofblur7 );",
92 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.40, 0.0 ) * aspectcorrect ) * dofblur7 );",
93 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.29, -0.29 ) * aspectcorrect ) * dofblur7 );",
94 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.0, -0.4 ) * aspectcorrect ) * dofblur7 );",
95 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.29, 0.29 ) * aspectcorrect ) * dofblur7 );",
96 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.4, 0.0 ) * aspectcorrect ) * dofblur7 );",
97 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.29, -0.29 ) * aspectcorrect ) * dofblur7 );",
98 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.0, 0.4 ) * aspectcorrect ) * dofblur7 );",
99 |
100 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.29, 0.29 ) * aspectcorrect ) * dofblur4 );",
101 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.4, 0.0 ) * aspectcorrect ) * dofblur4 );",
102 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.29, -0.29 ) * aspectcorrect ) * dofblur4 );",
103 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.0, -0.4 ) * aspectcorrect ) * dofblur4 );",
104 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.29, 0.29 ) * aspectcorrect ) * dofblur4 );",
105 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.4, 0.0 ) * aspectcorrect ) * dofblur4 );",
106 | "col += texture2D( tColor, vUv.xy + ( vec2( -0.29, -0.29 ) * aspectcorrect ) * dofblur4 );",
107 | "col += texture2D( tColor, vUv.xy + ( vec2( 0.0, 0.4 ) * aspectcorrect ) * dofblur4 );",
108 |
109 | "gl_FragColor = col / 41.0;",
110 | "gl_FragColor.a = 1.0;",
111 |
112 | "}"
113 |
114 | ].join("\n")
115 |
116 | };
117 |
--------------------------------------------------------------------------------
/static/lib-extras/three/shaders/SSAOShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | *
4 | * Screen-space ambient occlusion shader
5 | * - ported from
6 | * SSAO GLSL shader v1.2
7 | * assembled by Martins Upitis (martinsh) (http://devlog-martinsh.blogspot.com)
8 | * original technique is made by ArKano22 (http://www.gamedev.net/topic/550699-ssao-no-halo-artifacts/)
9 | * - modifications
10 | * - modified to use RGBA packed depth texture (use clear color 1,1,1,1 for depth pass)
11 | * - refactoring and optimizations
12 | */
13 |
14 | THREE.SSAOShader = {
15 |
16 | uniforms: {
17 |
18 | "tDiffuse": { type: "t", value: null },
19 | "tDepth": { type: "t", value: null },
20 | "size": { type: "v2", value: new THREE.Vector2( 512, 512 ) },
21 | "cameraNear": { type: "f", value: 1 },
22 | "cameraFar": { type: "f", value: 100 },
23 | "onlyAO": { type: "i", value: 0 },
24 | "aoClamp": { type: "f", value: 0.5 },
25 | "lumInfluence": { type: "f", value: 0.5 }
26 |
27 | },
28 |
29 | vertexShader: [
30 |
31 | "varying vec2 vUv;",
32 |
33 | "void main() {",
34 |
35 | "vUv = uv;",
36 |
37 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
38 |
39 | "}"
40 |
41 | ].join("\n"),
42 |
43 | fragmentShader: [
44 |
45 | "uniform float cameraNear;",
46 | "uniform float cameraFar;",
47 |
48 | "uniform bool onlyAO;", // use only ambient occlusion pass?
49 |
50 | "uniform vec2 size;", // texture width, height
51 | "uniform float aoClamp;", // depth clamp - reduces haloing at screen edges
52 |
53 | "uniform float lumInfluence;", // how much luminance affects occlusion
54 |
55 | "uniform sampler2D tDiffuse;",
56 | "uniform sampler2D tDepth;",
57 |
58 | "varying vec2 vUv;",
59 |
60 | // "#define PI 3.14159265",
61 | "#define DL 2.399963229728653", // PI * ( 3.0 - sqrt( 5.0 ) )
62 | "#define EULER 2.718281828459045",
63 |
64 | // helpers
65 |
66 | "float width = size.x;", // texture width
67 | "float height = size.y;", // texture height
68 |
69 | "float cameraFarPlusNear = cameraFar + cameraNear;",
70 | "float cameraFarMinusNear = cameraFar - cameraNear;",
71 | "float cameraCoef = 2.0 * cameraNear;",
72 |
73 | // user variables
74 |
75 | "const int samples = 8;", // ao sample count
76 | "const float radius = 5.0;", // ao radius
77 |
78 | "const bool useNoise = false;", // use noise instead of pattern for sample dithering
79 | "const float noiseAmount = 0.0003;", // dithering amount
80 |
81 | "const float diffArea = 0.4;", // self-shadowing reduction
82 | "const float gDisplace = 0.4;", // gauss bell center
83 |
84 |
85 | // RGBA depth
86 |
87 | "float unpackDepth( const in vec4 rgba_depth ) {",
88 |
89 | "const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );",
90 | "float depth = dot( rgba_depth, bit_shift );",
91 | "return depth;",
92 |
93 | "}",
94 |
95 | // generating noise / pattern texture for dithering
96 |
97 | "vec2 rand( const vec2 coord ) {",
98 |
99 | "vec2 noise;",
100 |
101 | "if ( useNoise ) {",
102 |
103 | "float nx = dot ( coord, vec2( 12.9898, 78.233 ) );",
104 | "float ny = dot ( coord, vec2( 12.9898, 78.233 ) * 2.0 );",
105 |
106 | "noise = clamp( fract ( 43758.5453 * sin( vec2( nx, ny ) ) ), 0.0, 1.0 );",
107 |
108 | "} else {",
109 |
110 | "float ff = fract( 1.0 - coord.s * ( width / 2.0 ) );",
111 | "float gg = fract( coord.t * ( height / 2.0 ) );",
112 |
113 | "noise = vec2( 0.25, 0.75 ) * vec2( ff ) + vec2( 0.75, 0.25 ) * gg;",
114 |
115 | "}",
116 |
117 | "return ( noise * 2.0 - 1.0 ) * noiseAmount;",
118 |
119 | "}",
120 |
121 | "float readDepth( const in vec2 coord ) {",
122 |
123 | // "return ( 2.0 * cameraNear ) / ( cameraFar + cameraNear - unpackDepth( texture2D( tDepth, coord ) ) * ( cameraFar - cameraNear ) );",
124 | "return cameraCoef / ( cameraFarPlusNear - unpackDepth( texture2D( tDepth, coord ) ) * cameraFarMinusNear );",
125 |
126 |
127 | "}",
128 |
129 | "float compareDepths( const in float depth1, const in float depth2, inout int far ) {",
130 |
131 | "float garea = 2.0;", // gauss bell width
132 | "float diff = ( depth1 - depth2 ) * 100.0;", // depth difference (0-100)
133 |
134 | // reduce left bell width to avoid self-shadowing
135 |
136 | "if ( diff < gDisplace ) {",
137 |
138 | "garea = diffArea;",
139 |
140 | "} else {",
141 |
142 | "far = 1;",
143 |
144 | "}",
145 |
146 | "float dd = diff - gDisplace;",
147 | "float gauss = pow( EULER, -2.0 * dd * dd / ( garea * garea ) );",
148 | "return gauss;",
149 |
150 | "}",
151 |
152 | "float calcAO( float depth, float dw, float dh ) {",
153 |
154 | "float dd = radius - depth * radius;",
155 | "vec2 vv = vec2( dw, dh );",
156 |
157 | "vec2 coord1 = vUv + dd * vv;",
158 | "vec2 coord2 = vUv - dd * vv;",
159 |
160 | "float temp1 = 0.0;",
161 | "float temp2 = 0.0;",
162 |
163 | "int far = 0;",
164 | "temp1 = compareDepths( depth, readDepth( coord1 ), far );",
165 |
166 | // DEPTH EXTRAPOLATION
167 |
168 | "if ( far > 0 ) {",
169 |
170 | "temp2 = compareDepths( readDepth( coord2 ), depth, far );",
171 | "temp1 += ( 1.0 - temp1 ) * temp2;",
172 |
173 | "}",
174 |
175 | "return temp1;",
176 |
177 | "}",
178 |
179 | "void main() {",
180 |
181 | "vec2 noise = rand( vUv );",
182 | "float depth = readDepth( vUv );",
183 |
184 | "float tt = clamp( depth, aoClamp, 1.0 );",
185 |
186 | "float w = ( 1.0 / width ) / tt + ( noise.x * ( 1.0 - noise.x ) );",
187 | "float h = ( 1.0 / height ) / tt + ( noise.y * ( 1.0 - noise.y ) );",
188 |
189 | "float ao = 0.0;",
190 |
191 | "float dz = 1.0 / float( samples );",
192 | "float z = 1.0 - dz / 2.0;",
193 | "float l = 0.0;",
194 |
195 | "for ( int i = 0; i <= samples; i ++ ) {",
196 |
197 | "float r = sqrt( 1.0 - z );",
198 |
199 | "float pw = cos( l ) * r;",
200 | "float ph = sin( l ) * r;",
201 | "ao += calcAO( depth, pw * w, ph * h );",
202 | "z = z - dz;",
203 | "l = l + DL;",
204 |
205 | "}",
206 |
207 | "ao /= float( samples );",
208 | "ao = 1.0 - ao;",
209 |
210 | "vec3 color = texture2D( tDiffuse, vUv ).rgb;",
211 |
212 | "vec3 lumcoeff = vec3( 0.299, 0.587, 0.114 );",
213 | "float lum = dot( color.rgb, lumcoeff );",
214 | "vec3 luminance = vec3( lum );",
215 |
216 | "vec3 final = vec3( color * mix( vec3( ao ), vec3( 1.0 ), luminance * lumInfluence ) );", // mix( color * ao, white, luminance )
217 |
218 | "if ( onlyAO ) {",
219 |
220 | "final = vec3( mix( vec3( ao ), vec3( 1.0 ), luminance * lumInfluence ) );", // ambient occlusion only
221 |
222 | "}",
223 |
224 | "gl_FragColor = vec4( final, 1.0 );",
225 |
226 | "}"
227 |
228 | ].join("\n")
229 |
230 | };
231 |
--------------------------------------------------------------------------------
/static/js/post-processing/LensDirtPass.js:
--------------------------------------------------------------------------------
1 | require('./LensDirtTexture');
2 |
3 | App.LensDirtPass = LensDirtPass;
4 | function LensDirtPass(opts) {
5 | opts = opts || {};
6 |
7 | var quads = opts.quads || 100;
8 | var textureSize = opts.textureSize || 1024;
9 | var textureCells = opts.textureCells || 10;
10 | var textureCellPad = opts.textureCellPad || 20;
11 | var textureDetail = opts.textureDetail || 50;
12 |
13 | this.renderToScreen = false;
14 | this.enabled = true;
15 | this.needsSwap = false;
16 | this.clear = false;
17 |
18 | this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
19 | this.scene = new THREE.Scene();
20 | this.scale = 1;
21 |
22 | this.textureMap = new App.LensDirtTexture(textureSize, textureCells, {
23 | detail : textureDetail,
24 | cellPad : textureCellPad
25 | });
26 |
27 | this.geom = this.createQuadGeom(quads, textureCells);
28 | this.mesh = new THREE.Mesh(this.geom, new App.AlphaMaterial({
29 | color : 0xffffff,
30 | opacity : 0.5,
31 | map : this.textureMap.texture,
32 | blending : THREE.AdditiveBlending,
33 | transparent : true
34 | }));
35 |
36 | this.scene.add(this.mesh);
37 |
38 | this._quadIndex = 0;
39 | this._quadCount = quads;
40 | }
41 |
42 | LensDirtPass.prototype.setSize = function (width, height) {
43 | var camera = this.camera;
44 | var w, h, s;
45 |
46 | if (width > height) {
47 | w = 1;
48 | h = s = height / width;
49 | } else {
50 | w = s = width / height;
51 | h = 1;
52 | }
53 |
54 | camera.left = -w;
55 | camera.right = w;
56 | camera.top = h;
57 | camera.bottom = -h;
58 | this.scale = s;
59 |
60 | camera.updateProjectionMatrix();
61 | };
62 |
63 | // ..................................................
64 | // Quad geometry
65 | //
66 |
67 | LensDirtPass.prototype._quadGeomPosition = function (count) {
68 | var verts = new Float32Array(count * 4 * 3);
69 | var positionAttr = new THREE.BufferAttribute(verts, 3);
70 |
71 | return positionAttr;
72 | };
73 |
74 | LensDirtPass.prototype._quadGeomIndex = function (count) {
75 | var indices = new Uint16Array(count * 6);
76 | var indexAttr = new THREE.BufferAttribute(indices, 1);
77 | var qi = 0, qj = 0;
78 | var a, b, c, d;
79 |
80 | for (var i = 0; i < count; i ++) {
81 | a = qi;
82 | b = qi + 1;
83 | c = qi + 2;
84 | d = qi + 3;
85 |
86 | indices[qj] = a;
87 | indices[qj + 1] = b;
88 | indices[qj + 2] = c;
89 | indices[qj + 3] = c;
90 | indices[qj + 4] = d;
91 | indices[qj + 5] = a;
92 |
93 | qi += 4;
94 | qj += 6;
95 | }
96 |
97 | return indexAttr;
98 | };
99 |
100 | LensDirtPass.prototype._quadGeomUv = function (count, cells) {
101 | var uvs = new Float32Array(count * 4 * 2);
102 | var uvAttr = new THREE.BufferAttribute(uvs, 2);
103 | var step = 1 / cells;
104 | var qi = 0, row = 0, col = 0;
105 |
106 | for (var i = 0; i < count; i ++) {
107 | uvs[qi] = uvs[qi + 6] = step * col; // au, du
108 | uvs[qi + 1] = uvs[qi + 3] = step * row; // av, bv
109 | uvs[qi + 2] = uvs[qi + 4] = step * (col + 1); // bu, cu
110 | uvs[qi + 5] = uvs[qi + 7] = step * (row + 1); // cv, dv
111 |
112 | qi += 8;
113 |
114 | if (++ col === cells) {
115 | col = 0;
116 | if (++ row === cells) {
117 | row = 0;
118 | }
119 | }
120 | }
121 |
122 | return uvAttr;
123 | };
124 |
125 | LensDirtPass.prototype._quadGeomAlpha = function (count) {
126 | var alpha = new Float32Array(count * 4);
127 | var alphaAttr = new THREE.BufferAttribute(alpha, 1);
128 |
129 | return alphaAttr;
130 | };
131 |
132 | LensDirtPass.prototype.createQuadGeom = function (count, cells) {
133 | var geom = new THREE.BufferGeometry();
134 |
135 | geom.addAttribute('position', this._quadGeomPosition(count));
136 | geom.addAttribute('uv', this._quadGeomUv(count, cells));
137 | geom.addAttribute('alpha', this._quadGeomAlpha(count));
138 | geom.setIndex(this._quadGeomIndex(count));
139 |
140 | return geom;
141 | };
142 |
143 | // ..................................................
144 | // Geometry updates
145 | //
146 |
147 | LensDirtPass.prototype._quadIndex = 0;
148 |
149 | LensDirtPass.prototype.setQuadPosition = (function () {
150 | var pos = new THREE.Matrix4();
151 | var rot = new THREE.Matrix4();
152 | var scale = new THREE.Matrix4();
153 | var transform = new THREE.Matrix4();
154 |
155 | var a = new THREE.Vector3();
156 | var b = new THREE.Vector3();
157 | var c = new THREE.Vector3();
158 | var d = new THREE.Vector3();
159 |
160 | return function (index, x, y, r, s) {
161 | var position = this.geom.attributes.position;
162 | var ai = index * 4, bi = ai + 1, ci = ai + 2, di = ai + 3;
163 |
164 | scale.makeScale(s * this.scale, s * this.scale, 1);
165 | rot.makeRotationZ(r);
166 | pos.makeTranslation(x, y, 0);
167 |
168 | transform.identity();
169 | transform.multiply(pos);
170 | transform.multiply(rot);
171 | transform.multiply(scale);
172 |
173 | a.set(-1, -1, 0);
174 | b.set( 1, -1, 0);
175 | c.set( 1, 1, 0);
176 | d.set(-1, 1, 0);
177 |
178 | a.applyMatrix4(transform);
179 | b.applyMatrix4(transform);
180 | c.applyMatrix4(transform);
181 | d.applyMatrix4(transform);
182 |
183 | position.setXY(ai, a.x, a.y);
184 | position.setXY(bi, b.x, b.y);
185 | position.setXY(ci, c.x, c.y);
186 | position.setXY(di, d.x, d.y);
187 |
188 | position.needsUpdate = true;
189 | };
190 | }());
191 |
192 | LensDirtPass.prototype.setQuadAlpha = function (index, alpha) {
193 | var attr = this.geom.attributes.alpha;
194 | var array = attr.array;
195 | var ai = index * 4;
196 |
197 | array[ai] = alpha;
198 | array[ai + 1] = alpha;
199 | array[ai + 2] = alpha;
200 | array[ai + 3] = alpha;
201 |
202 | attr.needsUpdate = true;
203 | };
204 |
205 | LensDirtPass.prototype.setGroup = function (count, x, y, spread) {
206 | var total = this._quadCount;
207 | var index = this._quadIndex;
208 | var qi = index;
209 | var xi, yi, rot, scale;
210 |
211 | for (var i = 0; i < count; i ++) {
212 | xi = x + (Math.random() - 0.5) * spread;
213 | yi = y + (Math.random() - 0.5) * spread;
214 | rot = Math.random() * Math.PI * 2;
215 | scale = Math.random() * 0.15;
216 |
217 | this.setQuadPosition(qi, xi, yi, rot, scale);
218 | this.setQuadAlpha(qi, 1);
219 |
220 | qi = index + i;
221 | if (qi >= total) {
222 | index = this._quadIndex = 0;
223 | }
224 | }
225 |
226 | this._quadIndex = qi;
227 | };
228 |
229 | LensDirtPass.prototype.update = function (delta) {
230 | var alphaAttr = this.geom.attributes.alpha;
231 | var alphaArray = alphaAttr.array;
232 |
233 | for (var i = 0, il = alphaArray.length; i < il; i ++) {
234 | alphaArray[i] *= 0.995;
235 | }
236 |
237 | alphaAttr.needsUpdate = true;
238 | };
239 |
240 | LensDirtPass.prototype.render = function (renderer, writeBuffer, readBuffer, delta) {
241 | if (this.renderToScreen) {
242 | renderer.render(this.scene, this.camera);
243 | } else {
244 | renderer.render(this.scene, this.camera, readBuffer, this.clear);
245 | }
246 | };
247 |
--------------------------------------------------------------------------------
/static/js/controllers/AudioController.js:
--------------------------------------------------------------------------------
1 | /*global Promise*/
2 | var Tweens = App.Tweens;
3 |
4 | App.AudioController = AudioController;
5 | function AudioController(params) {
6 | params = params || {};
7 |
8 | this.ctx = this.createAudioContext();
9 | this.baseUrl = params.baseUrl;
10 | this.volume = 0;
11 | this.distance = 0;
12 | this.tween = Tweens.factorTween({ volume : 0 }, 0.1);
13 |
14 | this._bufferCache = {};
15 | this._activeRequests = {};
16 | this._activeSounds = [];
17 | }
18 |
19 | AudioController.create = App.ctor(AudioController);
20 | App.Dispatcher.extend(AudioController.prototype);
21 | AudioController.prototype.VOLUME_ZERO = 0.001;
22 |
23 | AudioController.prototype.AUDIO_TYPES = [
24 | {
25 | ext : 'ogg',
26 | type : 'audio/ogg; codecs=vorbis'
27 | }, {
28 | ext : 'mp3',
29 | type : 'audio/mpeg;'
30 | }
31 | ];
32 |
33 | AudioController.prototype.createAudioContext = function () {
34 | var AudioContext = window.AudioContext || window.webkitAudioContext;
35 | return new AudioContext();
36 | };
37 |
38 | AudioController.prototype.canCopyBuffers = window.AudioBuffer &&
39 | window.AudioBuffer.prototype.copyFromChannel;
40 |
41 | AudioController.prototype.getAudioType = function () {
42 | if (this._audioType) { return this._audioType; }
43 |
44 | var audio = new Audio();
45 | var type = this.AUDIO_TYPES.find(function (codec) {
46 | return !!audio.canPlayType(codec.type).replace(/^no$/, '');
47 | });
48 |
49 | this._audioType = type;
50 | return type;
51 | };
52 |
53 | AudioController.prototype._findOrLoadBuffer = function (path) {
54 | var cached = this._bufferCache[path];
55 | if (!cached) {
56 | return this._loadBuffer(path);
57 | }
58 |
59 | return new Promise(function (resolve) {
60 | resolve(cached);
61 | });
62 | };
63 |
64 | AudioController.prototype._loadBuffer = function (path) {
65 | var activeRequests = this._activeRequests;
66 | var request = activeRequests[path];
67 | if (request) { return request; }
68 |
69 | var cache = this._bufferCache;
70 | var ctx = this.ctx;
71 | var audioType = this.getAudioType();
72 | var fullUrl = this.baseUrl + path + '.' + audioType.ext;
73 | var xhr = new XMLHttpRequest();
74 |
75 | xhr.open('GET', fullUrl, true);
76 | xhr.responseType = 'arraybuffer';
77 |
78 | request = activeRequests[path] = new Promise(function (resolve, reject) {
79 | xhr.addEventListener('load', function() {
80 | ctx.decodeAudioData(xhr.response, resolve);
81 | });
82 |
83 | xhr.send();
84 | }).then(function (buffer) {
85 | cache[path] = buffer;
86 | delete activeRequests[path];
87 | return buffer;
88 | });
89 |
90 | return request;
91 | };
92 |
93 | AudioController.prototype._addActiveSound = function (sound, sounds) {
94 | sounds = sounds || this._activeSounds;
95 | sound.sourceNode.onended = this._removeActiveSound.bind(this, sound, sounds);
96 | return sounds.push(sound);
97 | };
98 |
99 | AudioController.prototype._removeActiveSound = function (sound, sounds) {
100 | sounds = sounds || this._activeSounds;
101 | var index = sounds.indexOf(sound);
102 |
103 | if (index !== -1) {
104 | sounds.splice(index, 1);
105 | }
106 |
107 | return sounds.length;
108 | };
109 |
110 | AudioController.prototype.loadBuffer = function (params) {
111 | var path = params.path;
112 | return this._findOrLoadBuffer(path);
113 | };
114 |
115 | AudioController.prototype.sliceBuffer = function (buffer, begin, end) {
116 | var ctx = this.ctx;
117 | var channels = buffer.numberOfChannels;
118 | var rate = buffer.sampleRate;
119 |
120 | var startOffset = rate * begin;
121 | var endOffset = rate * end;
122 | var frameCount = endOffset - startOffset;
123 |
124 | var slicedBuffer = ctx.createBuffer(channels, frameCount * 2, rate);
125 | var copyBuffer = new Float32Array(frameCount);
126 | var channel;
127 |
128 | for (channel = 0; channel < channels; channel ++) {
129 | buffer.copyFromChannel(copyBuffer, channel, startOffset);
130 | slicedBuffer.copyToChannel(copyBuffer, channel, 0);
131 | copyBuffer.reverse();
132 | slicedBuffer.copyToChannel(copyBuffer, channel, frameCount);
133 | }
134 |
135 | return slicedBuffer;
136 | };
137 |
138 | AudioController.prototype.createSound = function (buffer, params) {
139 | var ctx = this.ctx;
140 | var sourceNode = ctx.createBufferSource();
141 | var gainNode = ctx.createGain();
142 | var filterNode = ctx.createBiquadFilter();
143 |
144 | var globalVolume = this.volume;
145 | var volume = params.volume != null ? params.volume : 1;
146 | var offsetTime = params.offsetTime;
147 |
148 | var sound = {
149 | volume : volume,
150 | buffer : buffer,
151 | startTime : ctx.currentTime,
152 | offsetTime : offsetTime,
153 | sourceNode : sourceNode,
154 | gainNode : gainNode,
155 | filterNode : filterNode
156 | };
157 |
158 | filterNode.type = 'lowpass';
159 | filterNode.frequency.value = 320;
160 | sourceNode.buffer = buffer;
161 | sourceNode.loop = !!params.loop;
162 | gainNode.gain.value = globalVolume * volume;
163 |
164 | sourceNode.connect(gainNode);
165 | gainNode.connect(filterNode);
166 | filterNode.connect(ctx.destination);
167 |
168 | if (offsetTime != null) {
169 | sourceNode.start(0, offsetTime);
170 | }
171 |
172 | return sound;
173 | };
174 |
175 | AudioController.prototype.createSoundSlice = function (duration, sound) {
176 | var ctx = this.ctx;
177 | var buffer = sound.buffer;
178 | var soundStart = sound.startTime;
179 | var soundOffset = sound.offsetTime || 0;
180 | var offsetTime = (ctx.currentTime - soundStart + soundOffset) % buffer.duration;
181 | var bufferSlice = this.sliceBuffer(buffer, offsetTime, offsetTime + duration);
182 |
183 | sound.offsetTime = offsetTime;
184 |
185 | return this.createSound(bufferSlice, {
186 | volume : sound.volume * 0.8,
187 | offsetTime : 0,
188 | loop : true
189 | });
190 | };
191 |
192 | AudioController.prototype.playSound = function (params) {
193 | var path = params.path;
194 |
195 | return this._findOrLoadBuffer(path).then(function (buffer) {
196 | var sound = this.createSound(buffer, {
197 | volume : params.volume,
198 | loop : params.loop,
199 | offsetTime : 0
200 | });
201 |
202 | this._addActiveSound(sound);
203 | return sound;
204 | }.bind(this));
205 | };
206 |
207 | AudioController.prototype.updateVolume = function (volume) {
208 | this._activeSounds.forEach(function (sound) {
209 | sound.gainNode.gain.value = volume * sound.volume;
210 | });
211 | };
212 |
213 | AudioController.prototype.pause = function () {
214 | if (!this.canCopyBuffers) { return; }
215 |
216 | var sounds = this._activeSounds.slice();
217 | var soundSlices = sounds.map(
218 | this.createSoundSlice.bind(this, 0.5));
219 |
220 | sounds.forEach(function (sound) {
221 | sound.sourceNode.stop();
222 | });
223 |
224 | this._pausedSounds = sounds;
225 | this._activeSounds = soundSlices;
226 | this.updateVolume(this.volume);
227 | };
228 |
229 | AudioController.prototype.resume = function () {
230 | if (!this.canCopyBuffers) { return; }
231 |
232 | var prevSounds = this._activeSounds;
233 | var pausedSounds = this._pausedSounds;
234 | if (!pausedSounds) { return; }
235 |
236 | var activeSounds = [];
237 |
238 | pausedSounds.forEach(function (sound) {
239 | var newSound = this.createSound(sound.buffer, {
240 | offsetTime : sound.offsetTime,
241 | volume : sound.volume,
242 | loop : sound.sourceNode.loop
243 | });
244 |
245 | this._addActiveSound(newSound, activeSounds);
246 | }.bind(this));
247 |
248 | prevSounds.forEach(function (sound) {
249 | sound.sourceNode.stop();
250 | });
251 |
252 | this._pausedSounds = null;
253 | this._activeSounds = activeSounds;
254 | this.updateVolume(this.volume);
255 | };
256 |
257 | AudioController.prototype.update = function () {
258 | var volume = this.tween('volume', this.volume) * (1 - this.distance);
259 |
260 | if (volume !== this.volume) {
261 | this.updateVolume(volume);
262 | }
263 |
264 | if (this.isMuted && volume > this.VOLUME_ZERO) {
265 | this.triggerListeners('unmute');
266 | this.isMuted = false;
267 | }
268 |
269 | if (!this.isMuted && volume <= this.VOLUME_ZERO) {
270 | this.triggerListeners('mute');
271 | this.isMuted = true;
272 | }
273 | };
274 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The Artistic License 2.0
2 |
3 | Copyright (c) 2014-2016 Ash Weeks
4 |
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | This license establishes the terms under which a given free software
11 | Package may be copied, modified, distributed, and/or redistributed.
12 | The intent is that the Copyright Holder maintains some artistic
13 | control over the development of that Package while still keeping the
14 | Package available as open source and free software.
15 |
16 | You are always permitted to make arrangements wholly outside of this
17 | license directly with the Copyright Holder of a given Package. If the
18 | terms of this license do not permit the full use that you propose to
19 | make of the Package, you should contact the Copyright Holder and seek
20 | a different licensing arrangement.
21 |
22 | Definitions
23 |
24 | "Copyright Holder" means the individual(s) or organization(s)
25 | named in the copyright notice for the entire Package.
26 |
27 | "Contributor" means any party that has contributed code or other
28 | material to the Package, in accordance with the Copyright Holder's
29 | procedures.
30 |
31 | "You" and "your" means any person who would like to copy,
32 | distribute, or modify the Package.
33 |
34 | "Package" means the collection of files distributed by the
35 | Copyright Holder, and derivatives of that collection and/or of
36 | those files. A given Package may consist of either the Standard
37 | Version, or a Modified Version.
38 |
39 | "Distribute" means providing a copy of the Package or making it
40 | accessible to anyone else, or in the case of a company or
41 | organization, to others outside of your company or organization.
42 |
43 | "Distributor Fee" means any fee that you charge for Distributing
44 | this Package or providing support for this Package to another
45 | party. It does not mean licensing fees.
46 |
47 | "Standard Version" refers to the Package if it has not been
48 | modified, or has been modified only in ways explicitly requested
49 | by the Copyright Holder.
50 |
51 | "Modified Version" means the Package, if it has been changed, and
52 | such changes were not explicitly requested by the Copyright
53 | Holder.
54 |
55 | "Original License" means this Artistic License as Distributed with
56 | the Standard Version of the Package, in its current version or as
57 | it may be modified by The Perl Foundation in the future.
58 |
59 | "Source" form means the source code, documentation source, and
60 | configuration files for the Package.
61 |
62 | "Compiled" form means the compiled bytecode, object code, binary,
63 | or any other form resulting from mechanical transformation or
64 | translation of the Source form.
65 |
66 |
67 | Permission for Use and Modification Without Distribution
68 |
69 | (1) You are permitted to use the Standard Version and create and use
70 | Modified Versions for any purpose without restriction, provided that
71 | you do not Distribute the Modified Version.
72 |
73 |
74 | Permissions for Redistribution of the Standard Version
75 |
76 | (2) You may Distribute verbatim copies of the Source form of the
77 | Standard Version of this Package in any medium without restriction,
78 | either gratis or for a Distributor Fee, provided that you duplicate
79 | all of the original copyright notices and associated disclaimers. At
80 | your discretion, such verbatim copies may or may not include a
81 | Compiled form of the Package.
82 |
83 | (3) You may apply any bug fixes, portability changes, and other
84 | modifications made available from the Copyright Holder. The resulting
85 | Package will still be considered the Standard Version, and as such
86 | will be subject to the Original License.
87 |
88 |
89 | Distribution of Modified Versions of the Package as Source
90 |
91 | (4) You may Distribute your Modified Version as Source (either gratis
92 | or for a Distributor Fee, and with or without a Compiled form of the
93 | Modified Version) provided that you clearly document how it differs
94 | from the Standard Version, including, but not limited to, documenting
95 | any non-standard features, executables, or modules, and provided that
96 | you do at least ONE of the following:
97 |
98 | (a) make the Modified Version available to the Copyright Holder
99 | of the Standard Version, under the Original License, so that the
100 | Copyright Holder may include your modifications in the Standard
101 | Version.
102 |
103 | (b) ensure that installation of your Modified Version does not
104 | prevent the user installing or running the Standard Version. In
105 | addition, the Modified Version must bear a name that is different
106 | from the name of the Standard Version.
107 |
108 | (c) allow anyone who receives a copy of the Modified Version to
109 | make the Source form of the Modified Version available to others
110 | under
111 |
112 | (i) the Original License or
113 |
114 | (ii) a license that permits the licensee to freely copy,
115 | modify and redistribute the Modified Version using the same
116 | licensing terms that apply to the copy that the licensee
117 | received, and requires that the Source form of the Modified
118 | Version, and of any works derived from it, be made freely
119 | available in that license fees are prohibited but Distributor
120 | Fees are allowed.
121 |
122 |
123 | Distribution of Compiled Forms of the Standard Version
124 | or Modified Versions without the Source
125 |
126 | (5) You may Distribute Compiled forms of the Standard Version without
127 | the Source, provided that you include complete instructions on how to
128 | get the Source of the Standard Version. Such instructions must be
129 | valid at the time of your distribution. If these instructions, at any
130 | time while you are carrying out such distribution, become invalid, you
131 | must provide new instructions on demand or cease further distribution.
132 | If you provide valid instructions or cease distribution within thirty
133 | days after you become aware that the instructions are invalid, then
134 | you do not forfeit any of your rights under this license.
135 |
136 | (6) You may Distribute a Modified Version in Compiled form without
137 | the Source, provided that you comply with Section 4 with respect to
138 | the Source of the Modified Version.
139 |
140 |
141 | Aggregating or Linking the Package
142 |
143 | (7) You may aggregate the Package (either the Standard Version or
144 | Modified Version) with other packages and Distribute the resulting
145 | aggregation provided that you do not charge a licensing fee for the
146 | Package. Distributor Fees are permitted, and licensing fees for other
147 | components in the aggregation are permitted. The terms of this license
148 | apply to the use and Distribution of the Standard or Modified Versions
149 | as included in the aggregation.
150 |
151 | (8) You are permitted to link Modified and Standard Versions with
152 | other works, to embed the Package in a larger work of your own, or to
153 | build stand-alone binary or bytecode versions of applications that
154 | include the Package, and Distribute the result without restriction,
155 | provided the result does not expose a direct interface to the Package.
156 |
157 |
158 | Items That are Not Considered Part of a Modified Version
159 |
160 | (9) Works (including, but not limited to, modules and scripts) that
161 | merely extend or make use of the Package, do not, by themselves, cause
162 | the Package to be a Modified Version. In addition, such works are not
163 | considered parts of the Package itself, and are not subject to the
164 | terms of this license.
165 |
166 |
167 | General Provisions
168 |
169 | (10) Any use, modification, and distribution of the Standard or
170 | Modified Versions is governed by this Artistic License. By using,
171 | modifying or distributing the Package, you accept this license. Do not
172 | use, modify, or distribute the Package, if you do not accept this
173 | license.
174 |
175 | (11) If your Modified Version has been derived from a Modified
176 | Version made by someone other than you, you are nevertheless required
177 | to ensure that your Modified Version complies with the requirements of
178 | this license.
179 |
180 | (12) This license does not grant you the right to use any trademark,
181 | service mark, tradename, or logo of the Copyright Holder.
182 |
183 | (13) This license includes the non-exclusive, worldwide,
184 | free-of-charge patent license to make, have made, use, offer to sell,
185 | sell, import and otherwise transfer the Package with respect to any
186 | patent claims licensable by the Copyright Holder that are necessarily
187 | infringed by the Package. If you institute patent litigation
188 | (including a cross-claim or counterclaim) against any party alleging
189 | that the Package constitutes direct or contributory patent
190 | infringement, then this Artistic License to you shall terminate on the
191 | date that such litigation is filed.
192 |
193 | (14) Disclaimer of Warranty:
194 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
195 | IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
196 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
197 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
198 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
199 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
200 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
201 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
202 |
--------------------------------------------------------------------------------
/static/lib-extras/three/controls/TrackballControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Eberhard Graether / http://egraether.com/
3 | * @author Mark Lundin / http://mark-lundin.com
4 | */
5 |
6 | THREE.TrackballControls = function ( object, domElement ) {
7 |
8 | var _this = this;
9 | var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM: 4, TOUCH_PAN: 5 };
10 |
11 | this.object = object;
12 | this.domElement = ( domElement !== undefined ) ? domElement : document;
13 |
14 | // API
15 |
16 | this.enabled = true;
17 |
18 | this.screen = { left: 0, top: 0, width: 0, height: 0 };
19 |
20 | this.rotateSpeed = 1.0;
21 | this.zoomSpeed = 1.2;
22 | this.panSpeed = 0.3;
23 |
24 | this.noRotate = false;
25 | this.noZoom = false;
26 | this.noPan = false;
27 | this.noRoll = false;
28 |
29 | this.staticMoving = false;
30 | this.dynamicDampingFactor = 0.2;
31 |
32 | this.minDistance = 0;
33 | this.maxDistance = Infinity;
34 |
35 | this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
36 |
37 | // internals
38 |
39 | this.target = new THREE.Vector3();
40 |
41 | var lastPosition = new THREE.Vector3();
42 |
43 | var _state = STATE.NONE,
44 | _prevState = STATE.NONE,
45 |
46 | _eye = new THREE.Vector3(),
47 |
48 | _rotateStart = new THREE.Vector3(),
49 | _rotateEnd = new THREE.Vector3(),
50 |
51 | _zoomStart = new THREE.Vector2(),
52 | _zoomEnd = new THREE.Vector2(),
53 |
54 | _touchZoomDistanceStart = 0,
55 | _touchZoomDistanceEnd = 0,
56 |
57 | _panStart = new THREE.Vector2(),
58 | _panEnd = new THREE.Vector2();
59 |
60 | // for reset
61 |
62 | this.target0 = this.target.clone();
63 | this.position0 = this.object.position.clone();
64 | this.up0 = this.object.up.clone();
65 |
66 | // events
67 |
68 | var changeEvent = { type: 'change' };
69 | var startEvent = { type: 'start'};
70 | var endEvent = { type: 'end'};
71 |
72 |
73 | // methods
74 |
75 | this.handleResize = function () {
76 |
77 | if ( this.domElement === document ) {
78 |
79 | this.screen.left = 0;
80 | this.screen.top = 0;
81 | this.screen.width = window.innerWidth;
82 | this.screen.height = window.innerHeight;
83 |
84 | } else {
85 |
86 | this.screen = this.domElement.getBoundingClientRect();
87 | // adjustments come from similar code in the jquery offset() function
88 | var d = this.domElement.ownerDocument.documentElement
89 | this.screen.left += window.pageXOffset - d.clientLeft
90 | this.screen.top += window.pageYOffset - d.clientTop
91 |
92 | }
93 |
94 | };
95 |
96 | this.handleEvent = function ( event ) {
97 |
98 | if ( typeof this[ event.type ] == 'function' ) {
99 |
100 | this[ event.type ]( event );
101 |
102 | }
103 |
104 | };
105 |
106 | this.getMouseOnScreen = function ( pageX, pageY, vector ) {
107 |
108 | return vector.set(
109 | ( pageX - _this.screen.left ) / _this.screen.width,
110 | ( pageY - _this.screen.top ) / _this.screen.height
111 | );
112 |
113 | };
114 |
115 | this.getMouseProjectionOnBall = (function(){
116 |
117 | var objectUp = new THREE.Vector3(),
118 | mouseOnBall = new THREE.Vector3();
119 |
120 |
121 | return function ( pageX, pageY, projection ) {
122 |
123 | mouseOnBall.set(
124 | ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5),
125 | ( _this.screen.height * 0.5 + _this.screen.top - pageY ) / (_this.screen.height*.5),
126 | 0.0
127 | );
128 |
129 | var length = mouseOnBall.length();
130 |
131 | if ( _this.noRoll ) {
132 |
133 | if ( length < Math.SQRT1_2 ) {
134 |
135 | mouseOnBall.z = Math.sqrt( 1.0 - length*length );
136 |
137 | } else {
138 |
139 | mouseOnBall.z = .5 / length;
140 |
141 | }
142 |
143 | } else if ( length > 1.0 ) {
144 |
145 | mouseOnBall.normalize();
146 |
147 | } else {
148 |
149 | mouseOnBall.z = Math.sqrt( 1.0 - length * length );
150 |
151 | }
152 |
153 | _eye.copy( _this.object.position ).sub( _this.target );
154 |
155 | projection.copy( _this.object.up ).setLength( mouseOnBall.y )
156 | projection.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) );
157 | projection.add( _eye.setLength( mouseOnBall.z ) );
158 |
159 | return projection;
160 | }
161 |
162 | }());
163 |
164 | this.rotateCamera = (function(){
165 |
166 | var axis = new THREE.Vector3(),
167 | quaternion = new THREE.Quaternion();
168 |
169 |
170 | return function () {
171 |
172 | var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
173 |
174 | if ( angle ) {
175 |
176 | axis.crossVectors( _rotateStart, _rotateEnd ).normalize();
177 |
178 | angle *= _this.rotateSpeed;
179 |
180 | quaternion.setFromAxisAngle( axis, -angle );
181 |
182 | _eye.applyQuaternion( quaternion );
183 | _this.object.up.applyQuaternion( quaternion );
184 |
185 | _rotateEnd.applyQuaternion( quaternion );
186 |
187 | if ( _this.staticMoving ) {
188 |
189 | _rotateStart.copy( _rotateEnd );
190 |
191 | } else {
192 |
193 | quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
194 | _rotateStart.applyQuaternion( quaternion );
195 |
196 | }
197 |
198 | }
199 | }
200 |
201 | }());
202 |
203 | this.zoomCamera = function () {
204 |
205 | if ( _state === STATE.TOUCH_ZOOM ) {
206 |
207 | var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
208 | _touchZoomDistanceStart = _touchZoomDistanceEnd;
209 | _eye.multiplyScalar( factor );
210 |
211 | } else {
212 |
213 | var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
214 |
215 | if ( factor !== 1.0 && factor > 0.0 ) {
216 |
217 | _eye.multiplyScalar( factor );
218 |
219 | if ( _this.staticMoving ) {
220 |
221 | _zoomStart.copy( _zoomEnd );
222 |
223 | } else {
224 |
225 | _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
226 |
227 | }
228 |
229 | }
230 |
231 | }
232 |
233 | };
234 |
235 | this.panCamera = (function(){
236 |
237 | var mouseChange = new THREE.Vector2(),
238 | objectUp = new THREE.Vector3(),
239 | pan = new THREE.Vector3();
240 |
241 | return function () {
242 |
243 | mouseChange.copy( _panEnd ).sub( _panStart );
244 |
245 | if ( mouseChange.lengthSq() ) {
246 |
247 | mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
248 |
249 | pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x );
250 | pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) );
251 |
252 | _this.object.position.add( pan );
253 | _this.target.add( pan );
254 |
255 | if ( _this.staticMoving ) {
256 |
257 | _panStart.copy( _panEnd );
258 |
259 | } else {
260 |
261 | _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
262 |
263 | }
264 |
265 | }
266 | }
267 |
268 | }());
269 |
270 | this.checkDistances = function () {
271 |
272 | if ( !_this.noZoom || !_this.noPan ) {
273 |
274 | if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) {
275 |
276 | _this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) );
277 |
278 | }
279 |
280 | if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) {
281 |
282 | _this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) );
283 |
284 | }
285 |
286 | }
287 |
288 | };
289 |
290 | this.update = function () {
291 |
292 | _eye.subVectors( _this.object.position, _this.target );
293 |
294 | if ( !_this.noRotate ) {
295 |
296 | _this.rotateCamera();
297 |
298 | }
299 |
300 | if ( !_this.noZoom ) {
301 |
302 | _this.zoomCamera();
303 |
304 | }
305 |
306 | if ( !_this.noPan ) {
307 |
308 | _this.panCamera();
309 |
310 | }
311 |
312 | _this.object.position.addVectors( _this.target, _eye );
313 |
314 | _this.checkDistances();
315 |
316 | _this.object.lookAt( _this.target );
317 |
318 | if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) {
319 |
320 | _this.dispatchEvent( changeEvent );
321 |
322 | lastPosition.copy( _this.object.position );
323 |
324 | }
325 |
326 | };
327 |
328 | this.reset = function () {
329 |
330 | _state = STATE.NONE;
331 | _prevState = STATE.NONE;
332 |
333 | _this.target.copy( _this.target0 );
334 | _this.object.position.copy( _this.position0 );
335 | _this.object.up.copy( _this.up0 );
336 |
337 | _eye.subVectors( _this.object.position, _this.target );
338 |
339 | _this.object.lookAt( _this.target );
340 |
341 | _this.dispatchEvent( changeEvent );
342 |
343 | lastPosition.copy( _this.object.position );
344 |
345 | };
346 |
347 | // listeners
348 |
349 | function keydown( event ) {
350 |
351 | if ( _this.enabled === false ) return;
352 |
353 | window.removeEventListener( 'keydown', keydown );
354 |
355 | _prevState = _state;
356 |
357 | if ( _state !== STATE.NONE ) {
358 |
359 | return;
360 |
361 | } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) {
362 |
363 | _state = STATE.ROTATE;
364 |
365 | } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) {
366 |
367 | _state = STATE.ZOOM;
368 |
369 | } else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) {
370 |
371 | _state = STATE.PAN;
372 |
373 | }
374 |
375 | }
376 |
377 | function keyup( event ) {
378 |
379 | if ( _this.enabled === false ) return;
380 |
381 | _state = _prevState;
382 |
383 | window.addEventListener( 'keydown', keydown, false );
384 |
385 | }
386 |
387 | function mousedown( event ) {
388 |
389 | if ( _this.enabled === false ) return;
390 |
391 | // event.preventDefault();
392 | // event.stopPropagation();
393 |
394 | if ( _state === STATE.NONE ) {
395 |
396 | _state = event.button;
397 |
398 | }
399 |
400 | if ( _state === STATE.ROTATE && !_this.noRotate ) {
401 |
402 | _this.getMouseProjectionOnBall( event.pageX, event.pageY, _rotateStart );
403 | _rotateEnd.copy(_rotateStart)
404 |
405 | } else if ( _state === STATE.ZOOM && !_this.noZoom ) {
406 |
407 | _this.getMouseOnScreen( event.pageX, event.pageY, _zoomStart );
408 | _zoomEnd.copy(_zoomStart);
409 |
410 | } else if ( _state === STATE.PAN && !_this.noPan ) {
411 |
412 | _this.getMouseOnScreen( event.pageX, event.pageY, _panStart );
413 | _panEnd.copy(_panStart)
414 |
415 | }
416 |
417 | document.addEventListener( 'mousemove', mousemove, false );
418 | document.addEventListener( 'mouseup', mouseup, false );
419 | _this.dispatchEvent( startEvent );
420 |
421 |
422 | }
423 |
424 | function mousemove( event ) {
425 |
426 | if ( _this.enabled === false ) return;
427 |
428 | event.preventDefault();
429 | event.stopPropagation();
430 |
431 | if ( _state === STATE.ROTATE && !_this.noRotate ) {
432 |
433 | _this.getMouseProjectionOnBall( event.pageX, event.pageY, _rotateEnd );
434 |
435 | } else if ( _state === STATE.ZOOM && !_this.noZoom ) {
436 |
437 | _this.getMouseOnScreen( event.pageX, event.pageY, _zoomEnd );
438 |
439 | } else if ( _state === STATE.PAN && !_this.noPan ) {
440 |
441 | _this.getMouseOnScreen( event.pageX, event.pageY, _panEnd );
442 |
443 | }
444 |
445 | }
446 |
447 | function mouseup( event ) {
448 |
449 | if ( _this.enabled === false ) return;
450 |
451 | event.preventDefault();
452 | event.stopPropagation();
453 |
454 | _state = STATE.NONE;
455 |
456 | document.removeEventListener( 'mousemove', mousemove );
457 | document.removeEventListener( 'mouseup', mouseup );
458 | _this.dispatchEvent( endEvent );
459 |
460 | }
461 |
462 | function mousewheel( event ) {
463 |
464 | if ( _this.enabled === false ) return;
465 |
466 | event.preventDefault();
467 | event.stopPropagation();
468 |
469 | var delta = 0;
470 |
471 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
472 |
473 | delta = event.wheelDelta / 40;
474 |
475 | } else if ( event.detail ) { // Firefox
476 |
477 | delta = - event.detail / 3;
478 |
479 | }
480 |
481 | _zoomStart.y += delta * 0.01;
482 | _this.dispatchEvent( startEvent );
483 | _this.dispatchEvent( endEvent );
484 |
485 | }
486 |
487 | function touchstart( event ) {
488 |
489 | if ( _this.enabled === false ) return;
490 |
491 | switch ( event.touches.length ) {
492 |
493 | case 1:
494 | _state = STATE.TOUCH_ROTATE;
495 | _rotateEnd.copy( _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateStart ));
496 | break;
497 |
498 | case 2:
499 | _state = STATE.TOUCH_ZOOM;
500 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
501 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
502 | _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
503 | break;
504 |
505 | case 3:
506 | _state = STATE.TOUCH_PAN;
507 | _panEnd.copy( _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panStart ));
508 | break;
509 |
510 | default:
511 | _state = STATE.NONE;
512 |
513 | }
514 | _this.dispatchEvent( startEvent );
515 |
516 |
517 | }
518 |
519 | function touchmove( event ) {
520 |
521 | if ( _this.enabled === false ) return;
522 |
523 | event.preventDefault();
524 | event.stopPropagation();
525 |
526 | switch ( event.touches.length ) {
527 |
528 | case 1:
529 | _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateEnd );
530 | break;
531 |
532 | case 2:
533 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
534 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
535 | _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy )
536 | break;
537 |
538 | case 3:
539 | _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panEnd );
540 | break;
541 |
542 | default:
543 | _state = STATE.NONE;
544 |
545 | }
546 |
547 | }
548 |
549 | function touchend( event ) {
550 |
551 | if ( _this.enabled === false ) return;
552 |
553 | switch ( event.touches.length ) {
554 |
555 | case 1:
556 | _rotateStart.copy( _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateEnd ));
557 | break;
558 |
559 | case 2:
560 | _touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
561 | break;
562 |
563 | case 3:
564 | _panStart.copy( _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panEnd ));
565 | break;
566 |
567 | }
568 |
569 | _state = STATE.NONE;
570 | _this.dispatchEvent( endEvent );
571 |
572 | }
573 |
574 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
575 |
576 | this.domElement.addEventListener( 'mousedown', mousedown, false );
577 |
578 | this.domElement.addEventListener( 'mousewheel', mousewheel, false );
579 | this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox
580 |
581 | this.domElement.addEventListener( 'touchstart', touchstart, false );
582 | this.domElement.addEventListener( 'touchend', touchend, false );
583 | this.domElement.addEventListener( 'touchmove', touchmove, false );
584 |
585 | window.addEventListener( 'keydown', keydown, false );
586 | window.addEventListener( 'keyup', keyup, false );
587 |
588 | this.handleResize();
589 |
590 | };
591 |
592 | THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
593 |
--------------------------------------------------------------------------------