├── .gitignore
├── LICENSE
├── README.md
├── index.css
├── index.html
├── main.bundle.js
├── media
├── LeePerrySmith.js
├── lowpolyhead.js
├── lowpolymap.txt
├── screens
│ ├── galaxy.gif
│ ├── head.gif
│ ├── static.jpg
│ └── target.gif
└── sound
│ ├── bg.mp3
│ ├── breath.mp3
│ ├── galaxies.mp3
│ ├── glitch0.mp3
│ ├── glitch1.mp3
│ ├── glitch2.mp3
│ ├── hit.mp3
│ ├── kluchik.mp3
│ └── morphing.mp3
├── obj.html
├── package.json
├── src
├── controls
│ ├── controls.ts
│ ├── keyboard.ts
│ ├── mobile.ts
│ └── nosleep.ts
├── display
│ ├── core
│ │ ├── animator.ts
│ │ ├── camera.ts
│ │ ├── final.ts
│ │ ├── math.ts
│ │ └── renderer.ts
│ ├── display.ts
│ ├── objects
│ │ ├── dust.ts
│ │ ├── galaxy.ts
│ │ ├── head.ts
│ │ ├── morphing-sphere.ts
│ │ ├── player.ts
│ │ └── sphere.ts
│ └── sfx
│ │ ├── glitch.ts
│ │ └── shaders.ts
├── engine
│ ├── ellermaze.ts
│ ├── msg.ts
│ ├── physics.ts
│ ├── player.ts
│ ├── target.ts
│ ├── world.ts
│ └── worldobject.ts
├── main.ts
├── senses
│ ├── audio.ts
│ ├── ui.ts
│ └── vibro.ts
├── utils
│ └── lowpoly.ts
└── vendor
│ ├── OBJLoader.js
│ ├── SimplifyModifier.js
│ ├── SubdivisionModifier.js
│ ├── audio
│ ├── Audio.js
│ ├── AudioAnalyser.js
│ ├── AudioBuffer.js
│ ├── AudioListener.js
│ └── PositionalAudio.js
│ ├── glitch
│ ├── CopyShader.js
│ ├── DigitalGlitch.js
│ ├── EffectComposer.js
│ ├── GlitchPass.js
│ ├── MaskPass.js
│ ├── RenderPass.js
│ └── ShaderPass.js
│ └── three.js
├── tsconfig.json
├── tsd.json
├── tslint.json
├── typings_custom
├── glitch.d.ts
├── p2.d.ts
├── positionalaudio.d.ts
└── simplifymodifier.d.ts
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | typings
4 | media/sound/orig
5 | main.bundle.js.map
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Crush
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | a-maze
2 | =========
3 |
4 | Play
5 |
6 | It is a maze. It is an experience, based on procedural visual, motion and audio effects, existential message.
7 |
8 | For desktop and mobile.
9 |
10 | 
11 | 
12 | 
13 |
14 |
15 | Thanks to Mary Lomova and Mikhail "MAD" Dolgoborodov for inspiration and support.
16 |
17 | ## Tech
18 | - typescript language;
19 | - webpack bundler;
20 | - three.js 3d engine, glitch effect;
21 | - p2.js physics engine;
22 | - Lee Perry-Smith head;
23 | - FractKali space shader;
24 | - zz85 geometry modifier.
25 |
26 | ## Sound
27 | - 99sounds.org and sonniss.com libraries;
28 | - Z Kluchik by Natalie Beridze Tba (original track by Rybnikov) for final.
29 |
30 | Message from sciencedaily.com.
31 |
32 | Licensed under MIT.
33 |
--------------------------------------------------------------------------------
/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #ffffff;
3 | margin: 0px;
4 | overflow: hidden;
5 | }
6 |
7 | canvas {width: 100%; height: 100%;}
8 |
9 | #msg {
10 | position: absolute;
11 | font-family: 'Open Sans Condensed', sans-serif;
12 | color: #fff;
13 | text-transform: uppercase;
14 | letter-spacing: .3em;
15 | }
16 |
17 | .msg-game {
18 | bottom: 10%;
19 | left: 10%;
20 | padding-right: 10%;
21 | font-size: 4vmin;
22 | }
23 |
24 | .msg-fin {
25 | top: 50%;
26 | transform: translateY(-50%);
27 | text-align: center;
28 | font-size: 10vmin;
29 | width: 100%;
30 | }
31 |
32 | .menu {
33 | position: absolute;
34 | bottom: 0;
35 | right: 0;
36 | transform: rotate(-90deg) translate(100%, -100%);
37 | transform-origin: right;
38 | font-size: 12px;
39 | line-height: normal;
40 | }
41 |
42 | .menu a {
43 | font-family: 'Open Sans', sans-serif;
44 | color: #000;
45 | opacity: .45;
46 | margin: 0 12px;
47 | text-decoration: none;
48 | }
49 |
50 | .menu a:hover { opacity: 1;}
51 |
52 | .menu a.off { text-decoration: line-through; }
53 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | a-maze
17 |
18 |
19 |
20 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/media/lowpolymap.txt:
--------------------------------------------------------------------------------
1 | 0,0,0,0,1,4,4,4,7,3,9,6,5,10,13,14,15,10,14,18,6,5,0,0,14,15,10,9,24,28,4,1,27,22,3,26,27,36,35,35,4,0,10,27,24,24,9,30,16,5,10,1,31,12,8,10,14,14,0,3,41,2,54,21,11,11,34,4,67,34,66,66,66,23,24,20,75,5,7,30,39,9,39,37,26,82,17,11,38,36,83,20,6,6,92,54,13,43,45,93,33,72,69,101,101,103,46,40,28,28,71,5,5,49,75,87,102,116,103,30,27,26,21,31,16,15,39,76,61,2,2,129,129,131,106,134,33,133,133,137,131,43,141,57,40,22,31,21,10,139,138,149,151,152,36,6,1,131,92,8,142,142,142,14,150,128,2,166,166,128,45,45,12,108,118,88,145,29,32,24,137,180,21,17,55,105,27,32,102,71,110,189,189,42,142,150,150,125,83,25,166,60,89,4,182,21,5,113,34,28,162,162,186,29,9,101,101,168,69,218,102,102,102,66,155,19,1,82,87,72,2,168,132,133,41,48,58,165,66,135,57,113,23,69,104,18,18,38,144,232,142,101,162,183,148,253,59,72,118,116,55,28,161,170,45,193,111,189,71,21,65,1,93,272,183,65,171,36,13,23,56,280,164,195,11,128,128,207,238,33,166,44,169,50,222,189,42,48,16,184,184,179,34,108,17,14,199,74,103,206,129,130,130,312,63,72,70,43,25,74,58,60,125,222,138,196,140,312,326,93,187,24,28,0,7,176,144,216,333,24,24,12,54,342,229,229,4,249,53,31,210,161,46,54,204,103,46,182,31,186,136,19,83,9,61,97,43,103,36,170,369,113,302,164,196,150,70,110,110,216,337,380,147,123,8,45,22,85,95,209,144,40,3,44,44,109,32,394,396,88,20,45,98,62,131,132,18,32,81,194,211,309,21,140,16,37,11,257,56,75,262,62,182,122,423,63,382,112,89,428,351,27,36,432,433,39,39,435,165,168,48,188,228,316,443,316,155,32,187,402,283,261,248,415,227,227,227,73,58,265,459,138,461,138,232,122,245,178,84,26,257,169,166,169,471,217,94,245,135,159,66,223,218,306,477,137,181,447,152,165,165,164,164,450,166,164,165,57,143,160,253,220,308,197,85,38,88,506,506,189,323,323,167,121,333,13,13,179,208,198,106,333,207,309,332,399,60,85,28,146,52,285,52,39,17,72,443,378,536,536,233,233,540,324,325,196,142,160,546,220,168,550,173,177,47,315,68,136,93,415,48,199,25,379,35,217,237,458,427,226,154,36,431,27,251,110,251,116,30,4,272,106,120,582,190,190,37,154,153,268,188,223,482,223,259,273,517,596,170,97,16,287,127,69,501,86,14,605,605,70,66,465,611,115,428,154,154,616,229,344,156,27,346,356,264,265,446,412,431,157,1,250,98,402,411,629,186,452,194,370,263,264,264,97,97,250,137,137,176,51,128,334,427,16,414,56,389,213,178,394,32,32,462,119,458,251,116,117,17,257,590,37,561,15,33,19,239,250,109,343,146,146,146,434,240,14,240,224,19,151,151,139,691,426,589,416,96,525,100,101,101,381,244,331,163,320,252,142,376,397,626,8,517,73,242,249,594,598,535,193,193,108,82,348,53,442,633,401,384,158,341,201,331,90,674,74,598,35,105,185,54,495,210,65,314,64,528,375,149,51,578,22,419,419,513,35,257,301,667,18,545,545,524,162,162,499,131,131,51,768,179,179,179,712,44,44,203,694,116,324,545,241,113,241,172,783,785,505,322,353,586,365,197,688,418,470,613,384,78,483,400,695,93,31,31,482,805,93,86,191,809,77,239,366,410,11,616,214,631,208,24,330,821,167,347,274,86,825,400,344,65,145,30,30,7,78,298,601,27,158,733,519,51,768,526,162,772,528,792,8,92,251,380,335,234,41,73,193,719,289,601,32,32,398,444,99,317,141,202,413,662,693,706,385,606,697,691,111,5,5,878,369,705,378,11,593,885,115,82,193,268,372,656,187,827,95,96,439,376,66,17,407,200,133,200,212,260,514,514,18,448,131,152,125,710,552,133,916,136,101,215,244,104,233,923,133,651,427,478,266,88,787,276,47,47,766,140,140,413,196,684,184,72,78,798,944,222,809,222,21,57,76,348,179,114,192,191,412,377,435,627,153,373,492,48,298,238,207,199,204,35,755,35,653,654,653,548,976,61,160,142,499,295,79,525,202,942,201,292,280,3,657,645,714,820,69,71,996,663,85,418
--------------------------------------------------------------------------------
/media/screens/galaxy.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/screens/galaxy.gif
--------------------------------------------------------------------------------
/media/screens/head.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/screens/head.gif
--------------------------------------------------------------------------------
/media/screens/static.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/screens/static.jpg
--------------------------------------------------------------------------------
/media/screens/target.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/screens/target.gif
--------------------------------------------------------------------------------
/media/sound/bg.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/sound/bg.mp3
--------------------------------------------------------------------------------
/media/sound/breath.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/sound/breath.mp3
--------------------------------------------------------------------------------
/media/sound/galaxies.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/sound/galaxies.mp3
--------------------------------------------------------------------------------
/media/sound/glitch0.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/sound/glitch0.mp3
--------------------------------------------------------------------------------
/media/sound/glitch1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/sound/glitch1.mp3
--------------------------------------------------------------------------------
/media/sound/glitch2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/sound/glitch2.mp3
--------------------------------------------------------------------------------
/media/sound/hit.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/sound/hit.mp3
--------------------------------------------------------------------------------
/media/sound/kluchik.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/sound/kluchik.mp3
--------------------------------------------------------------------------------
/media/sound/morphing.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/htdt/amaze/e72b05b47b310afaae49c381d6268ca339b5eb29/media/sound/morphing.mp3
--------------------------------------------------------------------------------
/obj.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Low Poly Object
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "amaze",
3 | "version": "1.0.0",
4 | "description": "interactive 3d experiment",
5 | "main": "src/main.ts",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "serve": "webpack-dev-server --no-info --hot --inline",
9 | "build": "webpack --optimize-minimize --optimize-occurence-order",
10 | "lint": "tslint 'src/**/*.ts'",
11 | "postinstall": "tsd install"
12 | },
13 | "author": "crush",
14 | "license": "MIT",
15 | "devDependencies": {
16 | "json-loader": "^0.5.2",
17 | "raw-loader": "^0.5.1",
18 | "ts-loader": "^0.8.1",
19 | "tsd": "^0.6.5",
20 | "tslint": "^3.5.0",
21 | "typescript": "^1.8.7",
22 | "webpack": "^1.12.14"
23 | },
24 | "dependencies": {
25 | "p2": "^0.7.0",
26 | "whatwg-fetch": "^0.9.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/controls/controls.ts:
--------------------------------------------------------------------------------
1 | export class Controls {
2 | public turn: number = 0;
3 | public up: number = 0;
4 | }
5 |
6 | export function isMobile(): boolean {
7 | return typeof window.orientation !== 'undefined';
8 | }
9 |
10 | export function isIOS(): boolean {
11 | return /iPad|iPhone|iPod/.test(navigator.userAgent) && !(window).MSStream;
12 | }
13 |
--------------------------------------------------------------------------------
/src/controls/keyboard.ts:
--------------------------------------------------------------------------------
1 | import {Controls} from './controls';
2 |
3 | export class KeyboardControls extends Controls {
4 | constructor() {
5 | super();
6 | window.onkeydown = (e) => {
7 | if (e.keyCode == 37) this.turn = -1;
8 | if (e.keyCode == 38) this.up = 1;
9 | if (e.keyCode == 39) this.turn = 1;
10 | };
11 | window.onkeyup = (e) => {
12 | if (e.keyCode == 37 || e.keyCode == 39) this.turn = 0;
13 | if (e.keyCode == 38) this.up = 0;
14 | };
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/controls/mobile.ts:
--------------------------------------------------------------------------------
1 | import {Controls} from './controls';
2 | import {NoSleep} from './nosleep';
3 |
4 | export class MobileControls extends Controls {
5 | private isPortret: boolean;
6 |
7 | constructor() {
8 | super();
9 | this.computeIsPortret();
10 | window.addEventListener('resize', () => this.computeIsPortret());
11 | window.addEventListener('deviceorientation', e => this.computeControls(e));
12 | new NoSleep().enableOnTouch();
13 | }
14 |
15 | private computeControls(e: DeviceOrientationEvent): void {
16 | if (this.isPortret) this.computePortret(e);
17 | else this.computeLandscape(e);
18 | if (Math.abs(this.turn) > 2) this.turn = 0;
19 | }
20 |
21 | private computePortret(e: DeviceOrientationEvent): void {
22 | if (e.beta <= 75) {
23 | this.turn = e.gamma / 45;
24 | this.up = (75 - e.beta) / 60;
25 | }
26 | }
27 |
28 | private computeLandscape(e: DeviceOrientationEvent): void {
29 | if (Math.abs(e.gamma) <= 60) {
30 | let dir = e.gamma > 0 ? -1 : 1;
31 | this.turn = e.beta * dir / 45;
32 | this.up = (e.gamma * dir + 60) / 40;
33 | }
34 | }
35 |
36 | private computeIsPortret(): void {
37 | this.isPortret = window.innerHeight > window.innerWidth;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/controls/nosleep.ts:
--------------------------------------------------------------------------------
1 | // Based on NoSleep.js git.io/vfn01 by Rich Tibbett
2 |
3 | export class NoSleep {
4 | private isAndroid: boolean = /Android/ig.test(navigator.userAgent);
5 | private isIOS: boolean = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
6 | private noSleepTimer: number = null;
7 | private noSleepVideo: HTMLVideoElement;
8 | /* tslint:disable */
9 | private media = {
10 | WebM: "data:video/webm;base64,GkXfo0AgQoaBAUL3gQFC8oEEQvOBCEKCQAR3ZWJtQoeBAkKFgQIYU4BnQI0VSalmQCgq17FAAw9CQE2AQAZ3aGFtbXlXQUAGd2hhbW15RIlACECPQAAAAAAAFlSua0AxrkAu14EBY8WBAZyBACK1nEADdW5khkAFVl9WUDglhohAA1ZQOIOBAeBABrCBCLqBCB9DtnVAIueBAKNAHIEAAIAwAQCdASoIAAgAAUAmJaQAA3AA/vz0AAA=",
11 | MP4: "data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAAG21kYXQAAAGzABAHAAABthADAowdbb9/AAAC6W1vb3YAAABsbXZoZAAAAAB8JbCAfCWwgAAAA+gAAAAAAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIVdHJhawAAAFx0a2hkAAAAD3wlsIB8JbCAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAIAAAACAAAAAABsW1kaWEAAAAgbWRoZAAAAAB8JbCAfCWwgAAAA+gAAAAAVcQAAAAAAC1oZGxyAAAAAAAAAAB2aWRlAAAAAAAAAAAAAAAAVmlkZW9IYW5kbGVyAAAAAVxtaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAEcc3RibAAAALhzdHNkAAAAAAAAAAEAAACobXA0dgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAIAAgASAAAAEgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj//wAAAFJlc2RzAAAAAANEAAEABDwgEQAAAAADDUAAAAAABS0AAAGwAQAAAbWJEwAAAQAAAAEgAMSNiB9FAEQBFGMAAAGyTGF2YzUyLjg3LjQGAQIAAAAYc3R0cwAAAAAAAAABAAAAAQAAAAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAEAAAABAAAAFHN0c3oAAAAAAAAAEwAAAAEAAAAUc3RjbwAAAAAAAAABAAAALAAAAGB1ZHRhAAAAWG1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAAK2lsc3QAAAAjqXRvbwAAABtkYXRhAAAAAQAAAABMYXZmNTIuNzguMw=="
12 | };
13 | /* tslint:enable */
14 |
15 | constructor() {
16 | if (this.isAndroid) {
17 | this.noSleepVideo = document.createElement('video');
18 | this.noSleepVideo.setAttribute('loop', '');
19 | this.addSourceToVideo(this.noSleepVideo, 'webm', this.media.WebM);
20 | this.addSourceToVideo(this.noSleepVideo, 'mp4', this.media.MP4);
21 | }
22 | }
23 |
24 | public enable(duration = 15000): void {
25 | if (this.isIOS) {
26 | this.disable();
27 | this.noSleepTimer = window.setInterval(() => {
28 | window.location.href = '/';
29 | window.setTimeout((window).stop, 0);
30 | }, duration);
31 | } else if (this.isAndroid) {
32 | this.noSleepVideo.play();
33 | }
34 | };
35 |
36 | public disable(): void {
37 | if (this.isIOS) {
38 | if (this.noSleepTimer) {
39 | window.clearInterval(this.noSleepTimer);
40 | this.noSleepTimer = null;
41 | }
42 | } else if (this.isAndroid) {
43 | this.noSleepVideo.pause();
44 | }
45 | }
46 |
47 | public enableOnTouch(): void {
48 | let f = () => {
49 | this.enable();
50 | document.removeEventListener('touchstart', f);
51 | };
52 | document.addEventListener('touchstart', f);
53 | }
54 |
55 | private addSourceToVideo(element, type, dataURI): void {
56 | let source = document.createElement('source');
57 | source.src = dataURI;
58 | source.type = 'video/' + type;
59 | element.appendChild(source);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/display/core/animator.ts:
--------------------------------------------------------------------------------
1 | interface AnimationObject {
2 | start: number;
3 | func: Function;
4 | duration: number;
5 | resolve: Function;
6 | object: any;
7 | loop: boolean;
8 | timer: boolean;
9 | }
10 |
11 | export class Animator {
12 | private animation: AnimationObject[] = [];
13 |
14 | public play({
15 | func = (dt) => null,
16 | duration = 1000,
17 | object = null,
18 | loop = false,
19 | timer = false,
20 | }): Promise {
21 | return new Promise((resolve, reject) => {
22 | if (object && this.animation.filter(o => o.object == object).length)
23 | return reject('object already in use');
24 | this.animation.push({start: Date.now(), func, duration, resolve, object, loop, timer});
25 | }).catch(error => null);
26 | }
27 |
28 | public stop(object: any): void {
29 | let i = this.animation.map(o => o.object).indexOf(object);
30 | if (i >= 0) this.animation.splice(i, 1);
31 | }
32 |
33 | public step(): void {
34 | let t = Date.now();
35 | for (let i = this.animation.length - 1; i >= 0; i--) {
36 | let dt = t - this.animation[i].start;
37 | if (dt <= this.animation[i].duration) {
38 | if (!this.animation[i].timer)
39 | this.animation[i].func(dt / this.animation[i].duration);
40 | } else this.onEnd(i);
41 | }
42 | }
43 |
44 | public delay(n: number): Promise {
45 | return this.play({duration: n});
46 | }
47 |
48 | private onEnd(i: number): void {
49 | if (this.animation[i].loop) {
50 | if (this.animation[i].timer) this.animation[i].func(1);
51 | this.animation[i].start = Date.now(); // + dt % this.animation[i].duration
52 | } else {
53 | this.animation[i].func(1);
54 | this.animation[i].resolve();
55 | this.animation.splice(i, 1);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/display/core/camera.ts:
--------------------------------------------------------------------------------
1 | import {Animator} from '../core/animator';
2 | import {SCALE} from '../display';
3 |
4 | export class Camera {
5 | public camera: THREE.PerspectiveCamera;
6 | public target: THREE.Object3D;
7 | private radius: number = 10;
8 | private angle: number = 0;
9 | private angleZ: number = 0;
10 |
11 | constructor(
12 | resolution: THREE.Vector2,
13 | private animator: Animator
14 | ) {
15 | this.camera = new THREE.PerspectiveCamera(
16 | 75, resolution.x / resolution.y, 1, 1000);
17 | this.camera.position.y = SCALE * 2.5;
18 | window.addEventListener('resize', () => {
19 | this.camera.aspect = window.innerWidth / window.innerHeight;
20 | this.camera.updateProjectionMatrix();
21 | });
22 | }
23 |
24 | public move(angle: number, up: boolean, turn: number): void {
25 | this.angle = angle;
26 | this.updateEasing(up, turn);
27 | this.updateCamera();
28 | }
29 |
30 | public final(duration: number): Promise {
31 | let y = SCALE * (this.radius + .5);
32 | let y2 = SCALE * 1.25;
33 | return this.animator.play({
34 | func: dt => {
35 | dt = -2 * dt * dt * (2 * dt - 3) / 2; // easing
36 | this.radius = dt + 2;
37 | this.camera.position.x = this.target.position.x - Math.cos(this.angle * (1 - dt)) * SCALE * this.radius;
38 | this.camera.position.z = this.target.position.z - Math.sin(this.angle * (1 - dt)) * SCALE * this.radius;
39 | this.camera.position.y = y * (1 - dt);
40 | this.camera.lookAt(new THREE.Vector3(
41 | this.target.position.x, y2 * (1 - dt), this.target.position.z));
42 | },
43 | duration,
44 | });
45 | }
46 |
47 | private updateEasing(up: boolean, turn: number): void {
48 | if (up && this.radius < 3) this.radius += .0025;
49 | if (!up && this.radius > 2) this.radius -= .01;
50 | if (this.radius > 3.01) this.radius -= .05;
51 | if (turn > 0 && this.angleZ < .1) this.angleZ += .0012;
52 | if (turn < 0 && this.angleZ > -.1) this.angleZ -= .0012;
53 | if (turn == 0 && Math.abs(this.angleZ) > 0) this.angleZ *= .9;
54 | }
55 |
56 | private updateCamera(): void {
57 | this.camera.position.x = this.target.position.x - Math.cos(this.angle) * SCALE * this.radius;
58 | this.camera.position.z = this.target.position.z - Math.sin(this.angle) * SCALE * this.radius;
59 | this.camera.position.y = SCALE * (this.radius + .5);
60 | this.camera.lookAt(new THREE.Vector3(
61 | this.target.position.x, SCALE * 1.25, this.target.position.z));
62 | this.camera.rotation.z += this.angleZ;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/display/core/final.ts:
--------------------------------------------------------------------------------
1 | import {Animator} from '../core/animator';
2 | import {Display3D} from '../display';
3 | import {GameMessage} from '../../engine/msg';
4 | import {Player} from '../objects/player';
5 | import {Camera} from './camera';
6 | import {HeadMaterials} from '../objects/head';
7 | import {Audio} from '../../senses/audio';
8 |
9 | export class Final {
10 | private player: Player;
11 | private camera: Camera;
12 |
13 | constructor(
14 | private display: Display3D,
15 | private animator: Animator,
16 | private msg: GameMessage,
17 | private audio: Audio
18 | ) {
19 | this.player = this.display.player;
20 | this.camera = this.display.camera;
21 | }
22 |
23 | public into(): Promise {
24 | this.audio.stopAll();
25 | return this.animator.delay(5000)
26 | .then(() => this.display.glitch.play(200))
27 | .then(() => this.animator.delay(1500))
28 | .then(() => this.display.glitch.play(200))
29 | .then(() => this.animator.delay(1500))
30 | .then(() => this.display.glitch.play(700))
31 | .then(() => this.animator.delay(1500));
32 | }
33 |
34 | public play(): void {
35 | this.display.glitch.play(700).then(() => {
36 | this.display.rmContainer();
37 | this.msg.hide();
38 | this.audio.bgFinal.play();
39 | return this.animator.delay(1000);
40 | }).then(() => {
41 | this.camera.final(5000);
42 | return this.player.final(5000);
43 | })
44 | .then(() => this.fadeinHeadPoly(25000))
45 | .then(() => {
46 | this.display.addFinalLight();
47 | this.msg.final();
48 | this.msg.show();
49 | this.display.glitch.play(100);
50 | this.rndHeadPoly();
51 | this.rndHeadMaterial();
52 | });
53 | }
54 |
55 | private fadeinHeadPoly(duration: number): Promise {
56 | return this.animator.play({
57 | func: d => this.player.head.lowpoly(d * d * 994 + 6),
58 | duration,
59 | });
60 | }
61 |
62 | private rndHeadPoly(): void {
63 | this.animator.play({
64 | func: _ => {
65 | if (Math.random() > .93) {
66 | this.player.head.lowpoly(Math.pow(Math.random(), 2) * 994 + 6);
67 | this.display.glitch.play(200);
68 | }
69 | },
70 | duration: 1000, timer: true, loop: true,
71 | });
72 | }
73 |
74 | private rndHeadMaterial(): void {
75 | this.player.head.setMaterial(HeadMaterials.Solid);
76 | this.animator.play({
77 | func: _ => {
78 | let rnd = Math.random();
79 | let updated = false;
80 | if (rnd < .075) {
81 | this.msg.show();
82 | updated = this.player.head.setMaterial(HeadMaterials.Solid);
83 | }
84 | else if (rnd > .075 && rnd < .15) {
85 | this.msg.hide();
86 | updated = this.player.head.setMaterial(HeadMaterials.Wire);
87 | }
88 | else if (rnd > .15 && rnd < .225) {
89 | this.msg.show();
90 | updated = this.player.head.setMaterial(HeadMaterials.Space);
91 | }
92 | if (updated) this.display.glitch.play(100);
93 | },
94 | duration: 1000, timer: true, loop: true,
95 | });
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/display/core/math.ts:
--------------------------------------------------------------------------------
1 | export function sphericalTo3d(a, b, r): number[] {
2 | return [
3 | r * Math.cos(a) * Math.sin(b),
4 | r * Math.sin(a) * Math.sin(b),
5 | r * Math.cos(b),
6 | ];
7 | }
8 |
--------------------------------------------------------------------------------
/src/display/core/renderer.ts:
--------------------------------------------------------------------------------
1 | export class Renderer {
2 | public renderer: THREE.WebGLRenderer;
3 |
4 | constructor(resolution: THREE.Vector2, pixelRatio: number) {
5 | this.renderer = new THREE.WebGLRenderer();
6 | this.renderer.setPixelRatio(pixelRatio);
7 | this.renderer.setSize(resolution.x, resolution.y);
8 | document.body.appendChild(this.renderer.domElement);
9 | window.addEventListener('resize', () =>
10 | this.renderer.setSize(window.innerWidth, window.innerHeight));
11 | }
12 |
13 | public render(scene: THREE.Scene, camera: THREE.Camera): void {
14 | this.renderer.render(scene, camera);
15 | }
16 |
17 | public setClearColor(c: THREE.Color): void {
18 | this.renderer.setClearColor(c);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/display/display.ts:
--------------------------------------------------------------------------------
1 | // THREE imported globally in webpack.config.js:25
2 |
3 | import {Animator} from './core/animator';
4 | import {Camera} from './core/camera';
5 | import {Renderer} from './core/renderer';
6 | import {MorphingSphere} from './objects/morphing-sphere';
7 | import {Dust} from './objects/dust';
8 | import {Galaxy} from './objects/galaxy';
9 | import {GlitchEffect} from './sfx/glitch';
10 | import {spaceVertexShader, spaceFragmentShader} from './sfx/shaders';
11 | import {Player} from './objects/player';
12 | import {Final} from './core/final';
13 | import {Audio} from '../senses/audio';
14 | import {GameMessage} from '../engine/msg';
15 | import {initUI} from '../senses/ui';
16 |
17 | export const SCALE = 50;
18 |
19 | export class Display3D {
20 | public galaxy: Galaxy;
21 | public player: Player;
22 | public camera: Camera;
23 | public morphingSphere: MorphingSphere;
24 | public container: THREE.Object3D;
25 | public glitch: GlitchEffect;
26 | public final: Final;
27 |
28 | private animator: Animator;
29 | private scene: THREE.Scene;
30 | private renderer: Renderer;
31 | private spaceMaterial: THREE.ShaderMaterial;
32 | private wallMaterial: THREE.MeshLambertMaterial;
33 | private dust: Dust;
34 | private audio: Audio;
35 |
36 | constructor(msg: GameMessage) {
37 | let resolution = new THREE.Vector2(window.innerWidth, window.innerHeight);
38 | let pixelRatio = window.devicePixelRatio;
39 | this.animator = new Animator();
40 | this.camera = new Camera(resolution, this.animator);
41 | this.renderer = new Renderer(resolution, pixelRatio);
42 | this.scene = this.initScene();
43 | this.container = new THREE.Object3D();
44 | this.scene.add(this.container);
45 | this.audio = new Audio(this.animator, this.scene);
46 | this.glitch = new GlitchEffect(this.animator, resolution, this.renderer.renderer,
47 | this.scene, this.camera.camera, this.audio, pixelRatio);
48 | this.spaceMaterial = this.initSpaceMaterial(resolution);
49 | this.player = this.initPlayer(this.spaceMaterial);
50 | this.galaxy = new Galaxy(this.animator, this.spaceMaterial, this.container, this.audio);
51 | this.wallMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff, shading: THREE.FlatShading });
52 | this.morphingSphere = new MorphingSphere(this.animator, this.container, this.audio);
53 | this.dust = new Dust(this.animator, this.container);
54 | this.final = new Final(this, this.animator, msg, this.audio);
55 | this.colorTransitionLoop();
56 | initUI(this.audio);
57 | }
58 |
59 | public render(dt: number): void {
60 | this.spaceMaterial.uniforms.iGlobalTime.value += dt / 1000;
61 | this.player.update(dt);
62 | this.audio.update(this.player, this.camera);
63 | this.animator.step();
64 | if (!this.glitch.render()) this.renderer.render(this.scene, this.camera.camera);
65 | }
66 |
67 | public rmContainer(): void {
68 | this.scene.remove(this.container);
69 | }
70 |
71 | public addFinalLight(): void {
72 | let light = new THREE.PointLight(0xffffff, .25);
73 | light.position.copy(this.camera.camera.position);
74 | this.scene.add(light);
75 | }
76 |
77 | public addEnvironment(w: number, h: number): void {
78 | let plane = new THREE.Mesh(
79 | new THREE.PlaneBufferGeometry(w * SCALE, h * SCALE),
80 | this.wallMaterial
81 | );
82 | plane.rotation.x = Math.PI / 2;
83 | plane.position.x = w * SCALE / 2 - SCALE / 2;
84 | plane.position.z = h * SCALE / 2 - SCALE / 2;
85 | plane.position.y = -SCALE / 2;
86 | this.container.add(plane);
87 | this.dust.init(w, h);
88 | }
89 |
90 | public addWall(x: number, y: number): THREE.Mesh {
91 | let wall = new THREE.Mesh(
92 | new THREE.BoxGeometry(SCALE, SCALE, SCALE),
93 | this.wallMaterial
94 | );
95 | wall.position.x = x * SCALE;
96 | wall.position.z = y * SCALE;
97 | this.container.add(wall);
98 | return wall;
99 | }
100 |
101 | public moveObject(displayObj: THREE.Mesh, physObj: p2.Body): void {
102 | displayObj.position.x = physObj.position[0] * SCALE;
103 | displayObj.position.z = physObj.position[1] * SCALE;
104 | displayObj.rotation.y = -physObj.angle;
105 | }
106 |
107 | public delay(n: number) { return this.animator.delay(n); }
108 |
109 | private initScene(): THREE.Scene {
110 | let scene = new THREE.Scene();
111 | scene.fog = new THREE.FogExp2(0xffffff, 0.004);
112 | scene.add(new THREE.AmbientLight(0x999999));
113 | return scene;
114 | }
115 |
116 | private initPlayer(spaceMaterial: THREE.ShaderMaterial): Player {
117 | let player = new Player(this.animator, spaceMaterial);
118 | this.scene.add(player.container);
119 | this.camera.target = player.container;
120 | return player;
121 | }
122 |
123 | private colorTransitionLoop(): void {
124 | this.animator.play({
125 | func: x => {
126 | let c = new THREE.Color().setHSL(x, .5, .85);
127 | this.wallMaterial.color.copy(c);
128 | this.dust.material.color.copy(c);
129 | this.scene.fog.color.copy(c);
130 | this.renderer.setClearColor(c);
131 | },
132 | duration: 200000,
133 | loop: true,
134 | });
135 | }
136 |
137 | private initSpaceMaterial(resolution: THREE.Vector2): THREE.ShaderMaterial {
138 | return new THREE.ShaderMaterial({
139 | vertexShader: spaceVertexShader,
140 | fragmentShader: spaceFragmentShader,
141 | uniforms: {
142 | iResolution: { type: 'v2', value: resolution },
143 | iGlobalTime: { type: 'f', value: 0 },
144 | fogDensity: { type: 'f', value: 0 },
145 | fogColor: { type: 'c', value: new THREE.Vector3() },
146 | },
147 | fog: true,
148 | });
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/display/objects/dust.ts:
--------------------------------------------------------------------------------
1 | import {Animator} from '../core/animator';
2 | import {SCALE} from '../display';
3 |
4 | export class Dust {
5 | public material: THREE.PointCloudMaterial;
6 | private geometry: THREE.Geometry;
7 |
8 | constructor(
9 | private animator: Animator,
10 | private container: THREE.Object3D
11 | ) {}
12 |
13 | public init(w: number, h: number): void {
14 | this.geometry = new THREE.Geometry();
15 | for (let i = 0; i < w * h * 3; i++)
16 | this.geometry.vertices.push(new THREE.Vector3(
17 | Math.random() * (w + 6) * SCALE - SCALE * 3,
18 | Math.random() * 6 * SCALE - SCALE * 3,
19 | Math.random() * (h + 6) * SCALE - SCALE * 3
20 | ));
21 | this.material = new THREE.PointCloudMaterial({
22 | size: 1, color: 0xaaaaaa, fog: true,
23 | });
24 | this.start();
25 | }
26 |
27 | private start(): void {
28 | let counter = 0;
29 | this.add();
30 | this.animator.play({
31 | func: _ => {
32 | this.add();
33 | if (++counter >= 20) this.animator.stop(this.material);
34 | },
35 | duration: 10000,
36 | loop: true,
37 | timer: true,
38 | object: this.material,
39 | });
40 | }
41 |
42 | private add(): void {
43 | let body = new THREE.PointCloud(this.geometry, this.material);
44 | body.position.set(this.randomInRange(), SCALE * 6, this.randomInRange());
45 | this.container.add(body);
46 | this.move(body);
47 | }
48 |
49 | private move(body: THREE.PointCloud): void {
50 | let v1 = body.position.clone();
51 | let v2 = this.Vector3InRange();
52 | let changeSpeed = 5000 + 30000 * Math.random();
53 | this.animator.play({
54 | func: dt => body.position.copy(this.lerp(dt, v1, v2)),
55 | duration: changeSpeed,
56 | loop: true,
57 | });
58 | this.animator.play({
59 | func: _ => { v1.copy(v2); v2 = this.Vector3InRange(); },
60 | duration: changeSpeed,
61 | loop: true,
62 | timer: true,
63 | });
64 | }
65 |
66 | private lerp(dt: number, v1: THREE.Vector3, v2: THREE.Vector3): THREE.Vector3 {
67 | let v = new THREE.Vector3();
68 | let dtEasing = -2 * dt * dt * (2 * dt - 3) / 2;
69 | return v.subVectors(v2, v1).multiplyScalar(dtEasing).add(v1);
70 | }
71 |
72 | private randomInRange(): number {
73 | return (Math.random() - .5) * SCALE * 6;
74 | }
75 |
76 | private Vector3InRange(): THREE.Vector3 {
77 | return new THREE.Vector3(this.randomInRange(), this.randomInRange(), this.randomInRange());
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/display/objects/galaxy.ts:
--------------------------------------------------------------------------------
1 | import {SCALE} from '../display';
2 | import {Animator} from '../core/animator';
3 | import {sphericalTo3d} from '../core/math';
4 | import {Audio} from '../../senses/audio';
5 |
6 | const ANIMATION_DURATION = 2000;
7 |
8 | interface GalaxyView {
9 | animation: Promise;
10 | view: THREE.Mesh;
11 | }
12 |
13 | export class Galaxy {
14 | private proto: THREE.Mesh;
15 |
16 | constructor(
17 | private animator: Animator,
18 | private spaceMaterial: THREE.ShaderMaterial,
19 | private container: THREE.Object3D,
20 | private audio: Audio
21 | ) {
22 | this.proto = new THREE.Mesh(
23 | new THREE.SphereGeometry(SCALE / 1.25, 16, 16), spaceMaterial);
24 | }
25 |
26 | public add(pos: THREE.Vector3): GalaxyView {
27 | this.explodeLines(pos);
28 | let g = this.proto.clone();
29 | g.position.set(pos.x, pos.y, pos.z);
30 | let s = this.audio.getPositionalAudio('galaxies');
31 | s.setVolume(2.5);
32 | g.add(s);
33 | this.container.add(g);
34 | return {
35 | animation: this.animator.play({
36 | func: dt => g.scale.fromArray(toArray3(dt * dt * dt)),
37 | duration: ANIMATION_DURATION,
38 | }),
39 | view: g,
40 | };
41 | }
42 |
43 | private explodeLines(pos: THREE.Vector3): Promise {
44 | let lines = this.getLines();
45 | lines.position.set(pos.x, pos.y, pos.z);
46 | this.container.add(lines);
47 | return this.animator.play({
48 | func: dt => lines.scale.fromArray(toArray3(dt * 50 + 1)),
49 | duration: ANIMATION_DURATION,
50 | }).then(() => this.container.remove(lines));
51 | }
52 |
53 | private getLines(): THREE.Line {
54 | let material = new THREE.LineBasicMaterial({color: 0x666666, linewidth: 1, fog: true});
55 | let geometry = new THREE.Geometry();
56 | for (let i = 0; i < 20; i++) {
57 | let a = Math.random() * Math.PI;
58 | let b = Math.random() * Math.PI;
59 | let s1 = Math.random() * SCALE / 10;
60 | let s2 = (Math.random() + .5) * SCALE;
61 | geometry.vertices.push(
62 | (new THREE.Vector3()).fromArray(sphericalTo3d(a, b, s1)),
63 | (new THREE.Vector3()).fromArray(sphericalTo3d(a, b, s2))
64 | );
65 | }
66 | return new THREE.Line(geometry, material, THREE.LinePieces);
67 | }
68 | }
69 |
70 | function toArray3(a) { return [a, a, a]; }
71 |
--------------------------------------------------------------------------------
/src/display/objects/head.ts:
--------------------------------------------------------------------------------
1 | import {SCALE} from '../display';
2 | import 'whatwg-fetch';
3 |
4 | export enum HeadMaterials {Wire, Solid, Space}
5 |
6 | export class Head {
7 | private mesh: THREE.Mesh;
8 | private geometry: THREE.Geometry;
9 | private originalGeometry: THREE.Geometry;
10 | private materials: (THREE.MeshBasicMaterial | THREE.MeshLambertMaterial | THREE.ShaderMaterial)[] = [];
11 | private currentMaterial: HeadMaterials = HeadMaterials.Wire;
12 | private reduceMap: number[];
13 |
14 | constructor(materialSpace: THREE.ShaderMaterial) {
15 | this.materials[HeadMaterials.Wire] = new THREE.MeshBasicMaterial({
16 | wireframe: true,
17 | color: 0xffffff,
18 | fog: true,
19 | });
20 | this.materials[HeadMaterials.Solid] = new THREE.MeshLambertMaterial({
21 | shading: THREE.FlatShading,
22 | vertexColors: THREE.FaceColors,
23 | fog: true,
24 | });
25 | this.materials[HeadMaterials.Space] = materialSpace;
26 | }
27 |
28 | public show(container: THREE.Object3D): void {
29 | container.add(this.mesh);
30 | }
31 |
32 | public setMaterial(newMaterial: HeadMaterials): boolean {
33 | if (this.currentMaterial == newMaterial) return false;
34 | this.mesh.material = this.materials[newMaterial];
35 | this.currentMaterial = newMaterial;
36 | return true;
37 | }
38 |
39 | public load(): Promise {
40 | return Promise.all([this.loadMap(), this.loadObject()]).then(() => this.postLoad());
41 | }
42 |
43 | public getMorphTargets(n: number): THREE.Vector3[] {
44 | return this.originalGeometry.vertices.slice(0, n);
45 | }
46 |
47 | public lowpoly(t: number): void {
48 | let face, oldFace;
49 | for (let i = 0; i < this.geometry.faces.length; i++) {
50 | face = this.geometry.faces[i];
51 | oldFace = this.originalGeometry.faces[i];
52 | face.a = oldFace.a;
53 | face.b = oldFace.b;
54 | face.c = oldFace.c;
55 | while (face.a > t) face.a = this.reduceMap[face.a];
56 | while (face.b > t) face.b = this.reduceMap[face.b];
57 | while (face.c > t) face.c = this.reduceMap[face.c];
58 | }
59 | this.geometry.verticesNeedUpdate = true;
60 | this.geometry.elementsNeedUpdate = true;
61 | }
62 |
63 | private loadMap(): Promise {
64 | return window.fetch('media/lowpolymap.txt').then(d => d.text())
65 | .then(d => this.reduceMap = d.split(',').map(x => parseInt(x, 10)));
66 | }
67 |
68 | private loadObject(): Promise {
69 | let loader = new THREE.JSONLoader(true);
70 | return new Promise((resolve, reject) => {
71 | loader.load('media/lowpolyhead.js', (geometry) => {
72 | this.geometry = geometry;
73 | this.originalGeometry = geometry.clone();
74 | this.mesh = new THREE.Mesh(this.geometry, this.materials[HeadMaterials.Wire]);
75 | resolve();
76 | });
77 | });
78 | }
79 |
80 | private postLoad(): void {
81 | this.mesh.scale.set(SCALE / 7, SCALE / 7, SCALE / 7);
82 | this.mesh.position.y = -SCALE * .33;
83 | this.colorize();
84 | this.lowpoly(6);
85 | }
86 |
87 | private colorize(): void {
88 | let len = this.geometry.faces.length;
89 | for (let i = 0; i < len; i++)
90 | this.geometry.faces[i].color.setHSL(0, 0, Math.random() * .2 + .8);
91 | this.geometry.colorsNeedUpdate = true;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/display/objects/morphing-sphere.ts:
--------------------------------------------------------------------------------
1 | import {Animator} from '../core/animator';
2 | import {SCALE} from '../display';
3 | import {generateSphere} from './sphere';
4 | import {Audio} from '../../senses/audio';
5 | import {isIOS} from '../../controls/controls';
6 |
7 | const SECTORS_I = 10;
8 | const SECTORS_K = 10;
9 | const SECTORS_IK = SECTORS_I * SECTORS_K;
10 | const BASE_RADIUS = 12.5; // SCALE / 4
11 |
12 | export class MorphingSphere {
13 | private calm: boolean = true;
14 | private morphing: boolean = false;
15 | private radiuses: number[] = [];
16 | private vertices: Float32Array;
17 | private geometry: THREE.BufferGeometry;
18 | private mesh: THREE.Mesh;
19 | private sound: THREE.PositionalAudio;
20 |
21 | constructor(
22 | private animator: Animator,
23 | private container: THREE.Object3D,
24 | private audio: Audio
25 | ) {
26 | this.init();
27 | this.sound = audio.getPositionalAudio('morphing');
28 | }
29 |
30 | public add(x: number, y: number): THREE.Mesh {
31 | let s = this.mesh.clone();
32 | s.position.x = x * SCALE;
33 | s.position.z = y * SCALE;
34 | s.add(this.sound);
35 | if ((this.sound).sourceType == 'buffer') this.sound.play();
36 | this.container.add(s);
37 | this.rotate(s);
38 | return s;
39 | }
40 |
41 | public onDestroy(view: THREE.Object3D) {
42 | this.animator.stop(view);
43 | if (!isIOS()) this.sound.stop();
44 | this.audio.hit.play();
45 | }
46 |
47 | private init(): void {
48 | this.vertices = new Float32Array(SECTORS_IK * 6 * 3);
49 | for (let i = 0; i < SECTORS_IK; i++) this.radiuses[i] = BASE_RADIUS;
50 | generateSphere(this.vertices, SECTORS_I, SECTORS_K, this.radiuses);
51 | this.geometry = new THREE.BufferGeometry();
52 | this.geometry.addAttribute( 'position', new THREE.BufferAttribute(this.vertices, 3));
53 | this.geometry.computeVertexNormals();
54 | let material = new THREE.MeshPhongMaterial({
55 | specular: 0xffffff,
56 | color: 0,
57 | shininess: 25,
58 | });
59 | this.mesh = new THREE.Mesh(this.geometry, material);
60 | this.playMorphingLoops();
61 | }
62 |
63 | private rotate(s: THREE.Mesh): void {
64 | this.animator.play({
65 | func: (dt) => {
66 | s.rotation.x = dt * Math.PI;
67 | s.rotation.z = dt * Math.PI * 2;
68 | },
69 | duration: 20000,
70 | loop: true,
71 | });
72 | }
73 |
74 | private computeRadiuses(dt: number): void {
75 | for (let i = 0; i < SECTORS_I; i++)
76 | for (let k = 0; k < SECTORS_K; k++) {
77 | let q = this.calm ? dt * dt : 1 - dt * dt;
78 | if (i % 2 == 0 && k % 2 == 0)
79 | this.radiuses[i + k * SECTORS_I] = BASE_RADIUS + Math.random() * BASE_RADIUS * q * 2;
80 | else
81 | this.radiuses[i + k * SECTORS_I] = BASE_RADIUS * (1 - q / 2);
82 | }
83 | }
84 |
85 | private morph(): void {
86 | this.morphing = true;
87 | this.animator.play({
88 | func: (dt) => {
89 | if (Math.random() > .7) {
90 | this.computeRadiuses(dt);
91 | generateSphere(this.vertices, SECTORS_I, SECTORS_K, this.radiuses);
92 | (this.geometry).attributes.position.needsUpdate = true;
93 | this.geometry.computeVertexNormals();
94 | }
95 | },
96 | duration: 2500,
97 | object: this.mesh,
98 | }).then(() => {
99 | this.morphing = false;
100 | this.calm = !this.calm;
101 | });
102 | }
103 |
104 | private playMorphingLoops(): void {
105 | this.animator.play({
106 | func: (dt) => {
107 | if (Math.random() > .8 && !this.morphing) this.morph();
108 | },
109 | duration: 500,
110 | loop: true,
111 | timer: true,
112 | });
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/display/objects/player.ts:
--------------------------------------------------------------------------------
1 | import {SCALE} from '../display';
2 | import {Animator} from '../core/animator';
3 | import {Head} from './head';
4 |
5 | export class Player {
6 | public container: THREE.Object3D;
7 | public head: Head;
8 | public rotationV: number = 0;
9 | private geometry: THREE.OctahedronGeometry;
10 | private wire: THREE.Mesh = null;
11 | private prevY: number = null;
12 |
13 | constructor(private animator: Animator, spaceMaterial: THREE.ShaderMaterial) {
14 | this.container = new THREE.Object3D();
15 | this.container.add(new THREE.Mesh(
16 | new THREE.SphereGeometry(SCALE / 8, 16, 16), spaceMaterial));
17 | this.runAnimation();
18 | this.head = new Head(spaceMaterial);
19 | this.head.load().then(() => this.initWire());
20 | }
21 |
22 | public update(dt: number): void {
23 | if (this.prevY !== null)
24 | this.rotationV = Math.abs(this.container.rotation.y - this.prevY) / dt;
25 | this.prevY = this.container.rotation.y;
26 | }
27 |
28 | public final(n: number): Promise {
29 | this.stopRotation();
30 | this.finalRotation();
31 | return this.morphToHead(n).then(() => {
32 | this.container.remove(this.wire);
33 | this.head.show(this.container);
34 | });
35 | }
36 |
37 | private stopRotation(): void {
38 | this.animator.stop(this.container);
39 | let z = this.wire.rotation.z;
40 | this.animator.play({func: dt => this.wire.rotation.z = z * (1 - dt)});
41 | }
42 |
43 | private finalRotation(): void {
44 | let rotateme = () => {
45 | let startq = this.container.rotation.y;
46 | let duration = Math.pow(Math.random(), 2) * 1500;
47 | let direction = Math.random() > .2 ? 1 : -1;
48 | this.animator.play({
49 | func: dt => this.container.rotation.y = startq + dt / 5 * direction,
50 | duration: duration,
51 | }).then(() => rotateme());
52 | };
53 | rotateme();
54 | }
55 |
56 | private morphToHead(n: number): Promise {
57 | return this.animator.play({
58 | func: dt => {
59 | (this.wire).morphTargetInfluences[0] = dt;
60 | this.wire.position.y = -dt * SCALE * .33;
61 | this.container.position.y = dt * SCALE * .7;
62 | let d = dt * 3.3 + 1;
63 | this.container.scale.set(d, d, d);
64 | },
65 | duration: n,
66 | });
67 | }
68 |
69 | private initWire(): void {
70 | this.geometry = new THREE.OctahedronGeometry(2.33, 0);
71 | this.geometry.morphTargets.push({
72 | name: 'head',
73 | vertices: this.head.getMorphTargets(this.geometry.vertices.length),
74 | });
75 | this.wire = new THREE.Mesh(this.geometry,
76 | new THREE.MeshBasicMaterial({color: 0xffffff, wireframe: true, morphTargets: true}));
77 | this.wire.scale.set(SCALE / 7, SCALE / 7, SCALE / 7);
78 | this.container.add(this.wire);
79 | }
80 |
81 | private runAnimation() {
82 | this.animator.play({
83 | func: dt => {
84 | this.container.position.y = Math.sin(dt * Math.PI * 2) * SCALE / 16;
85 | if (this.wire) this.wire.rotation.z = dt * Math.PI;
86 | },
87 | duration: 2500,
88 | loop: true,
89 | object: this.container,
90 | });
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/display/objects/sphere.ts:
--------------------------------------------------------------------------------
1 | import {sphericalTo3d} from '../core/math';
2 |
3 | export function generateSphere(vertices: Float32Array, ilen: number, klen: number, rr: number[]) {
4 | for (let i = 0; i < ilen; i++)
5 | for (let k = 0; k < klen; k++)
6 | {
7 | let i1 = i == ilen - 1 ? 0 : i + 1;
8 | let k1 = k == klen - 1 ? 0 : k + 1;
9 |
10 | let q1 = i / ilen * Math.PI * 2;
11 | let q2 = k / klen * Math.PI;
12 | let q3 = i1 / ilen * Math.PI * 2;
13 | let q4 = k1 / klen * Math.PI;
14 |
15 | let v1 = sphericalTo3d(q1, q2, rr[i + k * ilen]);
16 | let v2 = sphericalTo3d(q3, q2, rr[i1 + k * ilen]);
17 | let v3 = sphericalTo3d(q1, q4, rr[i + k1 * ilen]);
18 | let v4 = sphericalTo3d(q3, q4, rr[i1 + k1 * ilen]);
19 |
20 | let offset = (k * ilen + i) * 6 * 3;
21 |
22 | copy3array(vertices, offset, v3);
23 | copy3array(vertices, offset + 3, v2);
24 | copy3array(vertices, offset + 6, v1);
25 | copy3array(vertices, offset + 9, v2);
26 | copy3array(vertices, offset + 12, v3);
27 | copy3array(vertices, offset + 15, v4);
28 | }
29 | }
30 |
31 | function copy3array(a, offset, b) {
32 | a[offset] = b[0];
33 | a[offset + 1] = b[1];
34 | a[offset + 2] = b[2];
35 | }
36 |
--------------------------------------------------------------------------------
/src/display/sfx/glitch.ts:
--------------------------------------------------------------------------------
1 | import {Animator} from '../core/animator';
2 | import {Audio} from '../../senses/audio';
3 | import {Vibro} from '../../senses/vibro';
4 |
5 | import '../../vendor/glitch/CopyShader';
6 | import '../../vendor/glitch/DigitalGlitch';
7 | import '../../vendor/glitch/EffectComposer';
8 | import '../../vendor/glitch/RenderPass';
9 | import '../../vendor/glitch/MaskPass';
10 | import '../../vendor/glitch/ShaderPass';
11 | import '../../vendor/glitch/GlitchPass';
12 |
13 | export class GlitchEffect {
14 | private active: boolean;
15 | private composer: THREE.EffectComposer;
16 | private vibro: Vibro;
17 |
18 | constructor(
19 | private animator: Animator,
20 | resolution: THREE.Vector2,
21 | renderer: THREE.Renderer,
22 | scene: THREE.Scene,
23 | camera: THREE.Camera,
24 | private audio: Audio,
25 | pixelRatio: number
26 | ) {
27 | this.vibro = new Vibro();
28 | this.composer = new THREE.EffectComposer(renderer);
29 | this.composer.addPass(new THREE.RenderPass(scene, camera));
30 | let glitchPass = new THREE.GlitchPass();
31 | glitchPass.renderToScreen = true;
32 | glitchPass.goWild = true;
33 | this.composer.addPass(glitchPass);
34 | this.composer.setSize(resolution.x * pixelRatio, resolution.y * pixelRatio);
35 | }
36 |
37 | public play(duration: number): Promise {
38 | this.active = true;
39 | this.audio.glitch(duration);
40 | this.vibro.play(duration);
41 | return this.animator.play({
42 | duration,
43 | func: _ => this.active = false,
44 | timer: true,
45 | });
46 | }
47 |
48 | public render(): boolean {
49 | if (!this.active) return false;
50 | this.composer.render();
51 | return true;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/display/sfx/shaders.ts:
--------------------------------------------------------------------------------
1 | export var spaceVertexShader = `
2 | varying vec2 vUV;
3 | void main(void){
4 | gl_Position = projectionMatrix *modelViewMatrix * vec4(position,1.0);
5 | vUV = gl_Position.xy / gl_Position.w;
6 | }
7 | `;
8 |
9 | export var spaceFragmentShader = `
10 | // https://www.shadertoy.com/view/XlfGRj
11 | // Star Nest by Pablo Román Andrioli
12 | // This content is under the MIT License.
13 |
14 | #define iterations 17
15 | #define formuparam 0.53
16 |
17 | #define volsteps 5
18 | #define stepsize 0.33
19 |
20 | #define zoom 1.000
21 | #define tile 0.850
22 | #define speed 0.004
23 |
24 | #define brightness 0.0015
25 | #define darkmatter 0.300
26 | #define distfading 0.730
27 | #define saturation 0.850
28 |
29 | varying vec2 vUV;
30 | uniform float iGlobalTime;
31 | uniform vec2 iResolution;
32 | uniform float fogDensity;
33 | uniform vec3 fogColor;
34 |
35 | void main(void)
36 | {
37 | vec2 uv=vUV;//fragCoord.xy/iResolution.xy-.5;
38 | uv.y*=iResolution.y/iResolution.x;
39 | vec3 dir=vec3(uv*zoom,1.);
40 | float time=iGlobalTime*speed+.25;
41 |
42 | //mouse rotation
43 | float a1=.5+.1;
44 | float a2=.8+.2;
45 | mat2 rot1=mat2(cos(a1),sin(a1),-sin(a1),cos(a1));
46 | mat2 rot2=mat2(cos(a2),sin(a2),-sin(a2),cos(a2));
47 | dir.xz*=rot1;
48 | dir.xy*=rot2;
49 | vec3 from=vec3(1.,.5,0.5);
50 | from+=vec3(time*2.,time,-2.);
51 | from.xz*=rot1;
52 | from.xy*=rot2;
53 |
54 | //volumetric rendering
55 | float s=0.1,fade=1.;
56 | vec3 v=vec3(0.);
57 | for (int r=0; r6) fade*=1.-dm; // dark matter, don't render near
69 | //v+=vec3(dm,dm*.5,0.);
70 | v+=fade;
71 | v+=vec3(s,s*s,s*s*s*s)*a*brightness*fade; // coloring based on distance
72 | fade*=distfading; // distance fading
73 | s+=stepsize;
74 | }
75 | v=mix(vec3(length(v)),v,saturation); //color adjust
76 | gl_FragColor = vec4(v*.01,1.);
77 |
78 | float depth = gl_FragCoord.z / gl_FragCoord.w;
79 | const float LOG2 = 1.442695;
80 | float fogDensity2 = fogDensity * 0.75;
81 | float fogFactor = exp2( - fogDensity2 * fogDensity2 * depth * depth * LOG2 );
82 | fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );
83 | gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );
84 | }
85 | `;
86 |
--------------------------------------------------------------------------------
/src/engine/ellermaze.ts:
--------------------------------------------------------------------------------
1 | // algorithm http://www.neocomputer.org/projects/eller.html
2 |
3 | export function EllerMaze(width = 16, height = 16, debug = false) {
4 | let curstr = [];
5 | let map = [];
6 | let ls = width * 2 + 1;
7 |
8 | for (let i = 0; i < width; i++) curstr[i] = i;
9 | border();
10 | for (let n = 0; n < height; n++) {
11 | drawline(line1(), false);
12 | drawline(line2(), true);
13 | unnull();
14 | }
15 | drawline(last(), false);
16 | border();
17 | fillHoles();
18 | if (debug) log();
19 | return map;
20 |
21 | function log() {
22 | for (let n = 0; n < map.length; n++)
23 | console.log(map[n].map(el => el ? '#' : '-').join(''));
24 | }
25 |
26 | function fillHoles() {
27 | for (let n = 1; n < map.length - 1; n++)
28 | for (let x = 1; x < map[n].length - 1; x++)
29 | if (map[n][x - 1] && map[n][x + 1] && map[n - 1][x] && map[n + 1][x]) map[n][x] = true;
30 | }
31 |
32 | function line1() {
33 | let result = [], v1, v2;
34 |
35 | for (let i = 0; i < width - 1; i++) {
36 | result[i] = false;
37 |
38 | if (curstr[i] == curstr[i + 1]) result[i] = true;
39 | else {
40 | if (Math.random() > .5) result[i] = true;
41 | else {
42 | v1 = curstr[i];
43 | v2 = curstr[i + 1];
44 | curstr = curstr.map(x => {
45 | if (x == v2) return v1;
46 | else return x;
47 | });
48 | }
49 | }
50 | }
51 | return result;
52 | }
53 |
54 | function line2() {
55 | let result = [];
56 | for (let i = 0; i < width; i++) {
57 | result[i] = false;
58 | let l = curstr.filter(x => x == curstr[i]).length;
59 | if (l > 1 && Math.random() > .5) {
60 | result[i] = true;
61 | curstr[i] = null;
62 | }
63 | }
64 | return result;
65 | }
66 |
67 | function unique() {
68 | for (let i = 0; ; i++)
69 | if (curstr.filter(x => x == i).length == 0) return i;
70 | }
71 |
72 | function unnull() {
73 | for (let i = 0; i < width; i++)
74 | if (curstr[i] == null) curstr[i] = unique();
75 | }
76 |
77 | function last() {
78 | let result = line1();
79 | for (let i = 0; i < width - 1; i++)
80 | if (curstr[i] != curstr[i + 1])
81 | result[i] = false;
82 | return result;
83 | }
84 |
85 | function drawline(l, isline2) {
86 | let outl = [];
87 | outl.push(true);
88 | for (let i = 0; i < width - 1; i++) {
89 | if (isline2) outl.push(l[i], Math.random() > .3);
90 | else outl.push(false, l[i]);
91 | }
92 | outl.push(l[width - 1]);
93 | outl.push(true);
94 | map.push(outl);
95 | }
96 |
97 | function border() {
98 | let l = [];
99 | for (let i = 0; i < ls; i++) l.push(true);
100 | map.push(l);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/engine/msg.ts:
--------------------------------------------------------------------------------
1 | export class GameMessage {
2 | public container: HTMLElement = document.getElementById('msg');
3 | private parts: string[] = ['Reality', ' does not', ' exist', ' until', ' you', ' look', ' at it'];
4 | private counter: number = 0;
5 |
6 | public next(): boolean {
7 | this.container.innerHTML += this.parts[this.counter++];
8 | return this.counter < this.parts.length;
9 | }
10 |
11 | public final(): void {
12 | this.container.classList.remove('msg-game');
13 | this.container.classList.add('msg-fin');
14 | this.container.innerHTML = this.parts.join('
').replace(/ /g, '').replace('u
l', 'u l');
15 | }
16 |
17 | public hide(): void {
18 | this.container.style.display = 'none';
19 | }
20 |
21 | public show(): void {
22 | this.container.style.display = 'block';
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/engine/physics.ts:
--------------------------------------------------------------------------------
1 | import * as p2 from 'p2';
2 |
3 | interface InteractObject {
4 | obj1: p2.Body;
5 | obj2: p2.Body;
6 | func: Function;
7 | once: boolean;
8 | }
9 |
10 | export class Physics {
11 | public world: p2.World;
12 | public player: p2.Body;
13 | private interact: InteractObject[] = [];
14 | private wallMaterial: p2.Material = new p2.Material();
15 | private galaxyMaterial: p2.Material = new p2.Material();
16 | private targetMaterial: p2.Material = new p2.Material();
17 | private playerMaterial: p2.Material = new p2.Material();
18 |
19 | constructor() {
20 | this.world = new p2.World({gravity: [0, 0]});
21 | this.initContactMaterials();
22 | this.initPlayer();
23 | this.initImpact();
24 | }
25 |
26 | public addWall(x, y): p2.Body {
27 | let wall = new p2.Body({mass: 0, position: [x, y]});
28 | let wallShape = new p2.Box({width: 1, height: 1});
29 | wallShape.material = this.wallMaterial;
30 | wall.addShape(wallShape);
31 | this.world.addBody(wall);
32 | return wall;
33 | }
34 |
35 | public addTarget(x, y): p2.Body {
36 | let t = new p2.Body({mass: 50, position: [x, y]});
37 | let tShape = new p2.Circle({radius: .5});
38 | tShape.material = this.targetMaterial;
39 | t.addShape(tShape);
40 | this.world.addBody(t);
41 | return t;
42 | }
43 |
44 | public onHit({obj1, obj2, func, once = false}): number {
45 | return this.interact.push({obj1, obj2, func, once}) - 1;
46 | }
47 |
48 | public createGalaxy(pos: number[]): p2.Body {
49 | let shape = new p2.Circle({radius: 1 / 1.25});
50 | let g = new p2.Body({mass: 25, position: pos});
51 | shape.material = this.galaxyMaterial;
52 | g.addShape(shape);
53 | g.damping = 0;
54 | this.world.addBody(g);
55 | return g;
56 | }
57 |
58 | private initPlayer() {
59 | let playerShape = new p2.Circle({radius: 1 / 3});
60 | playerShape.material = this.playerMaterial;
61 | this.player = new p2.Body({mass: 1, position: [0, 0]});
62 | this.player.addShape(playerShape);
63 | this.player.damping = .5;
64 | this.world.addBody(this.player);
65 | }
66 |
67 | private initContactMaterials(): void {
68 | this.world.addContactMaterial(new p2.ContactMaterial(
69 | this.wallMaterial, this.playerMaterial,
70 | {restitution : 1, stiffness : 500}));
71 | this.world.addContactMaterial(new p2.ContactMaterial(
72 | this.wallMaterial, this.galaxyMaterial,
73 | {restitution : 1, stiffness : 50}));
74 | this.world.addContactMaterial(new p2.ContactMaterial(
75 | this.playerMaterial, this.galaxyMaterial,
76 | {restitution : 1, stiffness : 7}));
77 | this.world.addContactMaterial(new p2.ContactMaterial(
78 | this.targetMaterial, this.playerMaterial,
79 | {restitution : 2, stiffness : Number.MAX_VALUE}));
80 | }
81 |
82 | private initImpact(): void {
83 | this.world.on('impact', (evt) => {
84 | for (let i = this.interact.length - 1; i >= 0; i--)
85 | if ((evt.bodyA.id == this.interact[i].obj1.id && evt.bodyB.id == this.interact[i].obj2.id) ||
86 | (evt.bodyA.id == this.interact[i].obj2.id && evt.bodyB.id == this.interact[i].obj1.id)) {
87 | this.interact[i].func();
88 | if (this.interact[i].once) this.interact.splice(i, 1);
89 | break;
90 | }
91 | });
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/engine/player.ts:
--------------------------------------------------------------------------------
1 | import {WorldObject} from './worldobject';
2 | import {Vector2d} from './worldobject';
3 | import {isMobile, Controls} from '../controls/controls';
4 | import {KeyboardControls} from '../controls/keyboard';
5 | import {MobileControls} from '../controls/mobile';
6 |
7 | export class Player extends WorldObject {
8 | public keyb: Controls;
9 | public angle: number = 0;
10 |
11 | constructor(view: THREE.Object3D, body: p2.Body, pos: Vector2d) {
12 | super(view, body);
13 | this.keyb = isMobile() ? new MobileControls() : new KeyboardControls();
14 | this.body.position = [pos.x, pos.y];
15 | }
16 |
17 | public move(dt: number): void {
18 | this.angle += dt * this.keyb.turn / 500;
19 | this.body.force[0] = Math.cos(this.angle) * 10 * this.keyb.up;
20 | this.body.force[1] = Math.sin(this.angle) * 10 * this.keyb.up;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/engine/target.ts:
--------------------------------------------------------------------------------
1 | import {WorldObject, Vector2d} from './worldobject';
2 | import {Physics} from './physics';
3 | import {Display3D} from '../display/display';
4 |
5 | export class Target extends WorldObject {
6 | constructor(
7 | private phys: Physics,
8 | private display: Display3D,
9 | v: Vector2d
10 | ) {
11 | super(display.morphingSphere.add(v.x, v.y), phys.addTarget(v.x, v.y));
12 | }
13 |
14 | public onHit(): Promise {
15 | return new Promise((resolve, reject) => {
16 | this.phys.onHit({
17 | obj1: this.body,
18 | obj2: this.phys.player,
19 | once: true,
20 | func: () => resolve(this.destroy()),
21 | });
22 | });
23 | }
24 |
25 | private destroy(): Promise {
26 | this.phys.world.removeBody(this.body);
27 | this.display.morphingSphere.onDestroy(this.view);
28 | this.display.container.remove(this.view);
29 | return this.addGalaxy();
30 | }
31 |
32 | private addGalaxy(): Promise {
33 | let pos = this.view.position.clone();
34 | let physPos = [this.body.position[0], this.body.position[1]];
35 | return this.display.glitch.play(100).then(() => {
36 | this.display.delay(1250).then(() => this.display.glitch.play(750));
37 | let {animation, view} = this.display.galaxy.add(pos);
38 | return {
39 | animation,
40 | galaxy: new WorldObject(view, this.phys.createGalaxy(physPos)),
41 | };
42 | });
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/engine/world.ts:
--------------------------------------------------------------------------------
1 | import {Display3D} from '../display/display';
2 | import {Physics} from './physics';
3 | import {EllerMaze} from './ellermaze';
4 | import {GameMessage} from './msg';
5 | import {WorldObject, Vector2d} from './worldobject';
6 | import {Player} from './player';
7 | import {Target} from './target';
8 | import {isMobile} from '../controls/controls';
9 |
10 | export class World {
11 | private maze: boolean[][];
12 | private phys: Physics;
13 | private display: Display3D;
14 | private me: Player;
15 | private prevLoopTS: number;
16 | private worldObjects: WorldObject[];
17 | private msg: GameMessage;
18 | private stopped: boolean = false;
19 |
20 | constructor() {
21 | this.msg = new GameMessage();
22 | this.phys = new Physics();
23 | this.display = new Display3D(this.msg);
24 | this.maze = isMobile() ? EllerMaze(9, 9) : EllerMaze(12, 12);
25 | this.me = new Player(this.display.player.container, this.phys.player, this.getRandomPosition());
26 | this.worldObjects = [this.me];
27 | this.buildWallsAndFloor();
28 | this.addTarget();
29 | this.mainLoop();
30 | }
31 |
32 | private mainLoop(ts = null): void {
33 | let dt = this.prevLoopTS ? ts - this.prevLoopTS : 1000 / 60;
34 | if (dt > 100) dt = 100;
35 | if (!this.stopped) this.gameStep(dt);
36 | this.display.render(dt);
37 | this.prevLoopTS = ts;
38 | requestAnimationFrame(x => this.mainLoop(x));
39 | }
40 |
41 | private gameStep(dt: number): void {
42 | this.phys.world.step(dt / 1000);
43 | this.me.move(dt);
44 | this.worldObjects.forEach(x => x.update(this.display));
45 | this.display.camera.move(
46 | this.me.angle,
47 | this.me.keyb.up > 0,
48 | this.me.keyb.turn);
49 | }
50 |
51 | private addTarget(): void {
52 | let target = new Target(this.phys, this.display, this.getRandomPosition());
53 | target.onHit().then(({animation, galaxy}) => {
54 | this.worldObjects.push(galaxy);
55 | let end = !this.msg.next();
56 | animation.then(() => this.nextLevel(end, target));
57 | });
58 | this.worldObjects.push(target);
59 | }
60 |
61 | private getRandomPosition(): Vector2d {
62 | let w = this.maze[0].length, h = this.maze.length, x, y;
63 | do {
64 | x = Math.floor(Math.random() * w);
65 | y = Math.floor(Math.random() * h);
66 | } while (this.maze[y][x]);
67 | return {x, y};
68 | }
69 |
70 | private buildWallsAndFloor(): void {
71 | let w = this.maze[0].length, h = this.maze.length;
72 | for (let y = 0; y < h; y++)
73 | for (let x = 0; x < w; x++)
74 | if (this.maze[y][x]) {
75 | this.phys.addWall(x, y);
76 | this.display.addWall(x, y);
77 | }
78 | this.display.addEnvironment(w, h);
79 | }
80 |
81 | private nextLevel(isEnd: boolean, oldTarget: Target): void {
82 | if (!isEnd) {
83 | this.rmWorldObject(oldTarget);
84 | this.addTarget();
85 | }
86 | else this.display.final.into().then(() => {
87 | this.stopped = true;
88 | this.display.final.play();
89 | });
90 | }
91 |
92 | private rmWorldObject(obj: WorldObject): void {
93 | let i = this.worldObjects.map(o => o.view.id).indexOf(obj.view.id);
94 | if (i >= 0) this.worldObjects.splice(i, 1);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/engine/worldobject.ts:
--------------------------------------------------------------------------------
1 | import {Display3D} from '../display/display';
2 |
3 | export class WorldObject {
4 | constructor(public view: any, public body: p2.Body) {}
5 | public update(display: Display3D) {
6 | display.moveObject(this.view, this.body);
7 | }
8 | }
9 |
10 | export interface Vector2d {
11 | x: number;
12 | y: number;
13 | }
14 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import {World} from './engine/world';
2 |
3 | (window).amaze = new World();
4 |
--------------------------------------------------------------------------------
/src/senses/audio.ts:
--------------------------------------------------------------------------------
1 | import {Animator} from '../display/core/animator';
2 | import {Camera} from '../display/core/camera';
3 | import {Player} from '../display/objects/player';
4 | import {isIOS} from '../controls/controls';
5 |
6 | import '../vendor/audio/Audio';
7 | import '../vendor/audio/AudioAnalyser';
8 | import '../vendor/audio/AudioBuffer';
9 | import '../vendor/audio/AudioListener';
10 | import '../vendor/audio/PositionalAudio';
11 |
12 | export class Audio {
13 | public bgFinal: THREE.Audio;
14 | public hit: THREE.Audio;
15 | private bgWind: THREE.Audio;
16 | private bgBreath: THREE.Audio;
17 | private glitches: THREE.Audio[] = [];
18 | private sounds: THREE.PositionalAudio[] = [];
19 | private listener: THREE.AudioListener;
20 | private stopped: boolean = false;
21 | private muted: boolean = false;
22 |
23 | constructor(animator: Animator, scene: THREE.Scene) {
24 | this.listener = new THREE.AudioListener();
25 | scene.add(this.listener);
26 | this.init();
27 | }
28 |
29 | public toggleMute(): void {
30 | this.muted = !this.muted;
31 | (this.listener).setMasterVolume(this.muted ? 0 : 1);
32 | }
33 |
34 | public glitch(len: number): void {
35 | let sound = this.glitches[Math.floor(Math.random() * 3)];
36 | sound.startTime = Math.max((sound.source.buffer.duration - len / 1000), 0) * Math.random();
37 | sound.play();
38 | setTimeout(() => sound.stop(), len);
39 | }
40 |
41 | public getPositionalAudio(name: string): THREE.PositionalAudio {
42 | let a = new THREE.PositionalAudio(this.listener);
43 | a.load(`media/sound/${name}.mp3`);
44 | a.setDistanceModel('exponential');
45 | a.setRefDistance(20);
46 | a.setRolloffFactor(1.15);
47 | a.setLoop(true);
48 | a.autoplay = true;
49 | this.sounds.push(a);
50 | return a;
51 | }
52 |
53 | public update(player: Player, camera: Camera) {
54 | if (!this.stopped) {
55 | this.listener.position.copy(player.container.position);
56 | this.listener.rotation.copy(camera.camera.rotation);
57 | let dt = Math.min(player.rotationV / .02, 1);
58 | this.bgWind.setVolume(dt * Math.sin(player.container.rotation.y * 3) * .2 + .4);
59 | (this.bgWind).setPlaybackRate(1 + dt / 1.25);
60 | }
61 | }
62 |
63 | public stopAll(): void {
64 | if (!isIOS()) {
65 | try {
66 | this.sounds.forEach(x => x.stop());
67 | } catch (err) {
68 | console.error(err);
69 | }
70 | this.bgWind.stop();
71 | this.bgBreath.stop();
72 | }
73 | this.stopped = true;
74 | }
75 |
76 | private init(): void {
77 | this.hit = this.getAudio('hit');
78 | for (let i = 0; i < 3; i++) {
79 | this.glitches[i] = this.getAudio(`glitch${i}`);
80 | this.glitches[i].setVolume(.66);
81 | }
82 | this.initBg();
83 | }
84 |
85 | private initBg(): void {
86 | this.bgWind = this.getAudio('bg');
87 | this.bgWind.autoplay = true;
88 | this.bgWind.setLoop(true);
89 | this.bgWind.setVolume(0.4);
90 | this.bgBreath = this.getAudio('breath');
91 | this.bgBreath.autoplay = true;
92 | this.bgBreath.setLoop(true);
93 | this.bgFinal = this.getAudio('kluchik');
94 | this.bgFinal.setLoop(true);
95 | }
96 |
97 | private getAudio(name: string): THREE.Audio {
98 | let s = new THREE.Audio(this.listener);
99 | s.load(`media/sound/${name}.mp3`);
100 | return s;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/senses/ui.ts:
--------------------------------------------------------------------------------
1 | import {Audio} from './audio';
2 |
3 | function fullscreen(el) {
4 | if (el.requestFullscreen) {
5 | el.requestFullscreen();
6 | } else if (el.msRequestFullscreen) {
7 | el.msRequestFullscreen();
8 | } else if (el.mozRequestFullScreen) {
9 | el.mozRequestFullScreen();
10 | } else if (el.webkitRequestFullscreen) {
11 | el.webkitRequestFullscreen();
12 | }
13 | }
14 |
15 | export function initUI(audio: Audio) {
16 | let fslink = document.getElementById('fullscreen');
17 | if (fslink) fslink.addEventListener('click', () => fullscreen(document.body));
18 |
19 | let slink = document.getElementById('sound');
20 | if (slink) slink.addEventListener('click', () => {
21 | audio.toggleMute();
22 | slink.classList.toggle('off');
23 | });
24 | }
25 |
--------------------------------------------------------------------------------
/src/senses/vibro.ts:
--------------------------------------------------------------------------------
1 | export class Vibro {
2 | constructor() {
3 | navigator.vibrate = navigator.vibrate || (navigator).webkitVibrate || (navigator).mozVibrate || (navigator).msVibrate;
4 | }
5 |
6 | public play(duration: number): void {
7 | if (navigator.vibrate) navigator.vibrate(duration);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/utils/lowpoly.ts:
--------------------------------------------------------------------------------
1 | import './lib/SimplifyModifier';
2 |
3 | let out = document.createElement('div');
4 | document.body.appendChild(out);
5 |
6 | let loader = new THREE.JSONLoader(true);
7 | loader.load( 'media/LeePerrySmith.js', (geometry) => {
8 |
9 | geometry.mergeVertices();
10 | let simplify = new THREE.SimplifyModifier(400);
11 | let sortedGeometry = simplify.modify(geometry);
12 | let lp = lowpoly(sortedGeometry, 1000);
13 | cleanup(lp);
14 |
15 | out.innerHTML = tojson(lp);
16 | out.innerHTML += '
';
17 | out.innerText += reduceMap(sortedGeometry.map, 1001).join(',');
18 | });
19 |
20 | function faceHasClone(g, k): boolean {
21 | let f2 = g.faces[k];
22 | for (let i = 0; i < g.faces.length; i++)
23 | if (i != k) {
24 | let f1 = g.faces[i];
25 | if (f1.a == f2.a && f1.b == f2.b && f1.c == f2.c) return true;
26 | }
27 | return false;
28 | }
29 |
30 | function faceIsNull(face) {
31 | return face.a == face.b || face.b == face.c || face.c == face.a;
32 | }
33 |
34 |
35 | function vertexUsed(g, k): boolean {
36 | for (let i = 0; i < g.faces.length; i++) {
37 | let f1 = g.faces[i];
38 | if (f1.a == k || f1.b == k || f1.c == k) return true;
39 | }
40 | return false;
41 | }
42 |
43 | function vertexHasClone(g, k): boolean {
44 | let v = g.vertices[k];
45 | for (let i = 0; i < g.vertices.length; i++)
46 | if (i != k) {
47 | let v1 = g.vertices[i];
48 | if (v1.equals(v)) return true;
49 | }
50 | return false;
51 | }
52 |
53 | function cleanup(g) {
54 | for (let i = g.faces.length - 1; i >= 0 ; i--)
55 | if (faceHasClone(g, i) || faceIsNull(g.faces[i]))
56 | g.faces.splice(i, 1);
57 |
58 | for (let i = g.vertices.length - 1; i >= 0 ; i--)
59 | if (!vertexUsed(g, i) || vertexHasClone(g, i))
60 | g.vertices.splice(i, 1);
61 |
62 | g.computeFaceNormals();
63 | g.computeVertexNormals();
64 | }
65 |
66 | function lowpoly(sortedGeometry, t): void {
67 | let map = sortedGeometry.map;
68 | let face;
69 | let geometry = sortedGeometry.clone();
70 |
71 | for (let i = 0; i < geometry.faces.length; i++) {
72 | face = geometry.faces[i];
73 | let oldFace = sortedGeometry.faces[i];
74 | face.a = oldFace.a;
75 | face.b = oldFace.b;
76 | face.c = oldFace.c;
77 | while (face.a > t) face.a = map[face.a];
78 | while (face.b > t) face.b = map[face.b];
79 | while (face.c > t) face.c = map[face.c];
80 | }
81 |
82 | return geometry;
83 | }
84 |
85 | function reduceMap(map, t) {
86 | for (let i = 0; i < map.length; i++)
87 | while (map[i] > t) map[i] = map[map[i]];
88 | return map.slice(0, t);
89 | }
90 |
91 | function tojson(geometry) {
92 | let i,
93 | json = {
94 | metadata: {
95 | formatVersion: 3,
96 | },
97 | scale: 1.000000,
98 | materials: [],
99 | vertices: [],
100 | morphTargets: [],
101 | morphColors: [],
102 | normals: [],
103 | colors: [],
104 | uvs: [[]],
105 | faces: [],
106 | };
107 |
108 | for (i = 0; i < geometry.vertices.length; i++) {
109 | json.vertices.push(geometry.vertices[i].x);
110 | json.vertices.push(geometry.vertices[i].y);
111 | json.vertices.push(geometry.vertices[i].z);
112 | }
113 |
114 | for (i = 0; i < geometry.faces.length; i++) {
115 | if (geometry.faces[i].d) {
116 | json.faces.push(1);
117 | } else {
118 | json.faces.push(0);
119 | }
120 |
121 | json.faces.push(geometry.faces[i].a);
122 | json.faces.push(geometry.faces[i].b);
123 | json.faces.push(geometry.faces[i].c);
124 |
125 | if (geometry.faces[i].d) {
126 | json.faces.push(geometry.faces[i].d);
127 | }
128 |
129 | json.normals.push(geometry.faces[i].normal.x);
130 | json.normals.push(geometry.faces[i].normal.y);
131 | json.normals.push(geometry.faces[i].normal.z);
132 | }
133 |
134 | return JSON.stringify(json, null, '');
135 | }
136 |
--------------------------------------------------------------------------------
/src/vendor/OBJLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.OBJLoader = function ( manager ) {
6 |
7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
8 |
9 | };
10 |
11 | THREE.OBJLoader.prototype = {
12 |
13 | constructor: THREE.OBJLoader,
14 |
15 | load: function ( url, onLoad, onProgress, onError ) {
16 |
17 | var scope = this;
18 |
19 | var loader = new THREE.XHRLoader( scope.manager );
20 | loader.setCrossOrigin( this.crossOrigin );
21 | loader.load( url, function ( text ) {
22 |
23 | onLoad( scope.parse( text ) );
24 |
25 | }, onProgress, onError );
26 |
27 | },
28 |
29 | parse: function ( text ) {
30 |
31 | console.time( 'OBJLoader' );
32 |
33 | var object, objects = [];
34 | var geometry, material;
35 |
36 | function parseVertexIndex( value ) {
37 |
38 | var index = parseInt( value );
39 |
40 | return ( index >= 0 ? index - 1 : index + vertices.length / 3 ) * 3;
41 |
42 | }
43 |
44 | function parseNormalIndex( value ) {
45 |
46 | var index = parseInt( value );
47 |
48 | return ( index >= 0 ? index - 1 : index + normals.length / 3 ) * 3;
49 |
50 | }
51 |
52 | function parseUVIndex( value ) {
53 |
54 | var index = parseInt( value );
55 |
56 | return ( index >= 0 ? index - 1 : index + uvs.length / 2 ) * 2;
57 |
58 | }
59 |
60 | function addVertex( a, b, c ) {
61 |
62 | geometry.vertices.push(
63 | vertices[ a ], vertices[ a + 1 ], vertices[ a + 2 ],
64 | vertices[ b ], vertices[ b + 1 ], vertices[ b + 2 ],
65 | vertices[ c ], vertices[ c + 1 ], vertices[ c + 2 ]
66 | );
67 |
68 | }
69 |
70 | function addNormal( a, b, c ) {
71 |
72 | geometry.normals.push(
73 | normals[ a ], normals[ a + 1 ], normals[ a + 2 ],
74 | normals[ b ], normals[ b + 1 ], normals[ b + 2 ],
75 | normals[ c ], normals[ c + 1 ], normals[ c + 2 ]
76 | );
77 |
78 | }
79 |
80 | function addUV( a, b, c ) {
81 |
82 | geometry.uvs.push(
83 | uvs[ a ], uvs[ a + 1 ],
84 | uvs[ b ], uvs[ b + 1 ],
85 | uvs[ c ], uvs[ c + 1 ]
86 | );
87 |
88 | }
89 |
90 | function addFace( a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd ) {
91 |
92 | var ia = parseVertexIndex( a );
93 | var ib = parseVertexIndex( b );
94 | var ic = parseVertexIndex( c );
95 | var id;
96 |
97 | if ( d === undefined ) {
98 |
99 | addVertex( ia, ib, ic );
100 |
101 | } else {
102 |
103 | id = parseVertexIndex( d );
104 |
105 | addVertex( ia, ib, id );
106 | addVertex( ib, ic, id );
107 |
108 | }
109 |
110 | if ( ua !== undefined ) {
111 |
112 | ia = parseUVIndex( ua );
113 | ib = parseUVIndex( ub );
114 | ic = parseUVIndex( uc );
115 |
116 | if ( d === undefined ) {
117 |
118 | addUV( ia, ib, ic );
119 |
120 | } else {
121 |
122 | id = parseUVIndex( ud );
123 |
124 | addUV( ia, ib, id );
125 | addUV( ib, ic, id );
126 |
127 | }
128 |
129 | }
130 |
131 | if ( na !== undefined ) {
132 |
133 | ia = parseNormalIndex( na );
134 | ib = parseNormalIndex( nb );
135 | ic = parseNormalIndex( nc );
136 |
137 | if ( d === undefined ) {
138 |
139 | addNormal( ia, ib, ic );
140 |
141 | } else {
142 |
143 | id = parseNormalIndex( nd );
144 |
145 | addNormal( ia, ib, id );
146 | addNormal( ib, ic, id );
147 |
148 | }
149 |
150 | }
151 |
152 | }
153 |
154 | // create mesh if no objects in text
155 |
156 | if ( /^o /gm.test( text ) === false ) {
157 |
158 | geometry = {
159 | vertices: [],
160 | normals: [],
161 | uvs: []
162 | };
163 |
164 | material = {
165 | name: ''
166 | };
167 |
168 | object = {
169 | name: '',
170 | geometry: geometry,
171 | material: material
172 | };
173 |
174 | objects.push( object );
175 |
176 | }
177 |
178 | var vertices = [];
179 | var normals = [];
180 | var uvs = [];
181 |
182 | // v float float float
183 |
184 | var vertex_pattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
185 |
186 | // vn float float float
187 |
188 | var normal_pattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
189 |
190 | // vt float float
191 |
192 | var uv_pattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
193 |
194 | // f vertex vertex vertex ...
195 |
196 | var face_pattern1 = /f( +-?\d+)( +-?\d+)( +-?\d+)( +-?\d+)?/;
197 |
198 | // f vertex/uv vertex/uv vertex/uv ...
199 |
200 | var face_pattern2 = /f( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))?/;
201 |
202 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ...
203 |
204 | var face_pattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/;
205 |
206 | // f vertex//normal vertex//normal vertex//normal ...
207 |
208 | var face_pattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/
209 |
210 | //
211 |
212 | var lines = text.split( '\n' );
213 |
214 | for ( var i = 0; i < lines.length; i ++ ) {
215 |
216 | var line = lines[ i ];
217 | line = line.trim();
218 |
219 | var result;
220 |
221 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
222 |
223 | continue;
224 |
225 | } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) {
226 |
227 | // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
228 |
229 | vertices.push(
230 | parseFloat( result[ 1 ] ),
231 | parseFloat( result[ 2 ] ),
232 | parseFloat( result[ 3 ] )
233 | );
234 |
235 | } else if ( ( result = normal_pattern.exec( line ) ) !== null ) {
236 |
237 | // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
238 |
239 | normals.push(
240 | parseFloat( result[ 1 ] ),
241 | parseFloat( result[ 2 ] ),
242 | parseFloat( result[ 3 ] )
243 | );
244 |
245 | } else if ( ( result = uv_pattern.exec( line ) ) !== null ) {
246 |
247 | // ["vt 0.1 0.2", "0.1", "0.2"]
248 |
249 | uvs.push(
250 | parseFloat( result[ 1 ] ),
251 | parseFloat( result[ 2 ] )
252 | );
253 |
254 | } else if ( ( result = face_pattern1.exec( line ) ) !== null ) {
255 |
256 | // ["f 1 2 3", "1", "2", "3", undefined]
257 |
258 | addFace(
259 | result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ]
260 | );
261 |
262 | } else if ( ( result = face_pattern2.exec( line ) ) !== null ) {
263 |
264 | // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined]
265 |
266 | addFace(
267 | result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ],
268 | result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ]
269 | );
270 |
271 | } else if ( ( result = face_pattern3.exec( line ) ) !== null ) {
272 |
273 | // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined]
274 |
275 | addFace(
276 | result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ],
277 | result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ],
278 | result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ]
279 | );
280 |
281 | } else if ( ( result = face_pattern4.exec( line ) ) !== null ) {
282 |
283 | // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined]
284 |
285 | addFace(
286 | result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ],
287 | undefined, undefined, undefined, undefined,
288 | result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ]
289 | );
290 |
291 | } else if ( /^o /.test( line ) ) {
292 |
293 | geometry = {
294 | vertices: [],
295 | normals: [],
296 | uvs: []
297 | };
298 |
299 | material = {
300 | name: ''
301 | };
302 |
303 | object = {
304 | name: line.substring( 2 ).trim(),
305 | geometry: geometry,
306 | material: material
307 | };
308 |
309 | objects.push( object )
310 |
311 | } else if ( /^g /.test( line ) ) {
312 |
313 | // group
314 |
315 | } else if ( /^usemtl /.test( line ) ) {
316 |
317 | // material
318 |
319 | material.name = line.substring( 7 ).trim();
320 |
321 | } else if ( /^mtllib /.test( line ) ) {
322 |
323 | // mtl file
324 |
325 | } else if ( /^s /.test( line ) ) {
326 |
327 | // smooth shading
328 |
329 | } else {
330 |
331 | // console.log( "THREE.OBJLoader: Unhandled line " + line );
332 |
333 | }
334 |
335 | }
336 |
337 | var container = new THREE.Object3D();
338 |
339 | for ( var i = 0, l = objects.length; i < l; i ++ ) {
340 |
341 | object = objects[ i ];
342 | geometry = object.geometry;
343 |
344 | var buffergeometry = new THREE.BufferGeometry();
345 |
346 | buffergeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geometry.vertices ), 3 ) );
347 |
348 | if ( geometry.normals.length > 0 ) {
349 | buffergeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geometry.normals ), 3 ) );
350 | }
351 |
352 | if ( geometry.uvs.length > 0 ) {
353 | buffergeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) );
354 | }
355 |
356 | material = new THREE.MeshLambertMaterial();
357 | material.name = object.material.name;
358 |
359 | var mesh = new THREE.Mesh( buffergeometry, material );
360 | mesh.name = object.name;
361 |
362 | container.add( mesh );
363 |
364 | }
365 |
366 | console.timeEnd( 'OBJLoader' );
367 |
368 | return container;
369 |
370 | }
371 |
372 | };
373 |
--------------------------------------------------------------------------------
/src/vendor/SimplifyModifier.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog
3 | *
4 | * Simplification Geometry Modifier
5 | * - based on code and technique
6 | * - by Stan Melax in 1998
7 | * - Progressive Mesh type Polygon Reduction Algorithm
8 | * - http://www.melax.com/polychop/
9 | */
10 |
11 | THREE.SimplifyModifier = function() {
12 |
13 | };
14 |
15 | (function() {
16 | var cb = new THREE.Vector3(), ab = new THREE.Vector3();
17 |
18 | function pushIfUnique( array, object ) {
19 |
20 | if ( array.indexOf( object ) === -1 ) array.push( object );
21 |
22 | }
23 |
24 | function computeEdgeCollapseCost( u, v ) {
25 |
26 | // if we collapse edge uv by moving u to v then how
27 | // much different will the model change, i.e. the "error".
28 |
29 | var edgelength = v.position.distanceTo( u.position );
30 |
31 | var curvature = 0;
32 |
33 |
34 | var sideFaces = [];
35 | var i, uFaces = u.faces, il = u.faces.length, face, sideFace;
36 |
37 | // find the "sides" triangles that are on the edge uv
38 | for ( i = 0 ; i < il; i ++ ) {
39 |
40 | face = u.faces[ i ];
41 |
42 | if ( face.hasVertex(v) ) {
43 |
44 | sideFaces.push( face );
45 |
46 | }
47 |
48 | }
49 |
50 | // use the triangle facing most away from the sides
51 | // to determine our curvature term
52 | for ( i = 0 ; i < il; i ++ ) {
53 |
54 | var minCurvature = 1;
55 | face = u.faces[ i ];
56 |
57 | for( var j = 0; j < sideFaces.length; j ++ ) {
58 |
59 | sideFace = sideFaces[ j ];
60 | // use dot product of face normals.
61 | var dotProd = face.normal.dot( sideFace.normal );
62 | minCurvature = Math.min( minCurvature, ( 1 - dotProd ) / 2);
63 | }
64 |
65 | curvature = Math.max( curvature, minCurvature );
66 | }
67 |
68 | if (u.isBorder() && sideFaces.length > 1) {
69 | curvature = 1;
70 | }
71 |
72 | return edgelength * curvature;
73 |
74 | }
75 |
76 | function computeEdgeCostAtVertex( v ) {
77 |
78 | if ( v.neighbors.length === 0 ) {
79 |
80 | // collpase if no neighbors.
81 | v.collapse = null;
82 | v.cost = - 0.01;
83 |
84 | return;
85 |
86 | }
87 |
88 | v.cost = 1000000;
89 | v.collapse = null;
90 |
91 | // search all neighboring edges for “least cost” edge
92 | for( var i = 0; i < v.neighbors.length; i ++ ) {
93 |
94 | var c;
95 | c = computeEdgeCollapseCost( v, v.neighbors[ i ] );
96 |
97 | if ( c < v.cost ) {
98 |
99 | v.collapse = v.neighbors[i];
100 | v.cost = c;
101 |
102 | }
103 |
104 | }
105 |
106 | }
107 |
108 | function removeVertex( v, vertices ) {
109 |
110 | console.assert( v.faces.length === 0 );
111 |
112 | while ( v.neighbors.length ) {
113 |
114 | var n = v.neighbors.pop();
115 | n.neighbors.splice( n.neighbors.indexOf( v ), 1 );
116 |
117 | }
118 |
119 | vertices.splice( vertices.indexOf( v ), 1 );
120 |
121 | }
122 |
123 | function removeFace( f, faces ) {
124 |
125 | faces.splice( faces.indexOf( f ), 1 );
126 |
127 | if ( f.v1 ) f.v1.faces.splice( f.v1.faces.indexOf( f ), 1 );
128 | if ( f.v2 ) f.v2.faces.splice( f.v2.faces.indexOf( f ), 1 );
129 | if ( f.v3 ) f.v3.faces.splice( f.v3.faces.indexOf( f ), 1 );
130 |
131 | var vs = [this.v1, this.v2, this.v3];
132 | var v1, v2;
133 | for(var i=0;i<3;i++) {
134 | v1 = vs[i];
135 | v2 = vs[(i+1)%3]
136 | if(!v1 || !v2) continue;
137 | v1.removeIfNonNeighbor(v2);
138 | v2.removeIfNonNeighbor(v1);
139 | }
140 |
141 | }
142 |
143 | function collapse( vertices, faces, u, v ) { // u and v are pointers to vertices of an edge
144 |
145 | // Collapse the edge uv by moving vertex u onto v
146 |
147 | if ( !v ) {
148 |
149 | // u is a vertex all by itself so just delete it..
150 | removeVertex( u, vertices );
151 | return;
152 |
153 | }
154 |
155 | var i;
156 | var tmpVertices = [];
157 |
158 | for( i = 0 ; i < u.neighbors.length; i ++ ) {
159 |
160 | tmpVertices.push( u.neighbors[ i ] );
161 |
162 | }
163 |
164 |
165 | // delete triangles on edge uv:
166 | for( i = u.faces.length - 1; i >= 0; i -- ) {
167 |
168 | if ( u.faces[ i ].hasVertex( v ) ) {
169 |
170 | removeFace( u.faces[ i ], faces );
171 |
172 | }
173 |
174 | }
175 |
176 | // update remaining triangles to have v instead of u
177 | for( i = u.faces.length -1 ; i >= 0; i -- ) {
178 |
179 | u.faces[i].replaceVertex( u, v );
180 |
181 | }
182 |
183 |
184 | removeVertex( u, vertices );
185 |
186 | // recompute the edge collapse costs in neighborhood
187 | for( i = 0; i < tmpVertices.length; i ++ ) {
188 |
189 | computeEdgeCostAtVertex( tmpVertices[ i ] );
190 |
191 | }
192 |
193 | }
194 |
195 |
196 |
197 | function minimumCostEdge( vertices ) {
198 |
199 | // O(n * n) approach. TODO optimize this
200 |
201 | var least = vertices[ 0 ];
202 |
203 | for (var i = 0; i < vertices.length; i ++ ) {
204 |
205 | if ( vertices[ i ].cost < least.cost ) {
206 |
207 | least = vertices[ i ];
208 |
209 | }
210 | }
211 |
212 | return least;
213 |
214 | }
215 |
216 | // we use a triangle class to represent structure of face slightly differently
217 |
218 | function Triangle( v1, v2, v3 ) {
219 |
220 | this.v1 = v1;
221 | this.v2 = v2;
222 | this.v3 = v3;
223 |
224 | this.normal = new THREE.Vector3();
225 |
226 | this.computeNormal();
227 |
228 | v1.faces.push( this );
229 | v1.addUniqueNeighbor( v2 );
230 | v1.addUniqueNeighbor( v3 );
231 |
232 | v2.faces.push( this );
233 | v2.addUniqueNeighbor( v1 );
234 | v2.addUniqueNeighbor( v3 );
235 |
236 |
237 | v3.faces.push( this );
238 | v3.addUniqueNeighbor( v1 );
239 | v3.addUniqueNeighbor( v2 );
240 |
241 | }
242 |
243 | Triangle.prototype.computeNormal = function() {
244 |
245 | var vA = this.v1.position;
246 | var vB = this.v2.position;
247 | var vC = this.v3.position;
248 |
249 | cb.subVectors( vC, vB );
250 | ab.subVectors( vA, vB );
251 | cb.cross( ab ).normalize();
252 |
253 | this.normal.copy( cb );
254 |
255 | };
256 |
257 | Triangle.prototype.hasVertex = function( v ) {
258 |
259 | return v === this.v1 || v === this.v2 || v === this.v3;
260 |
261 | };
262 |
263 | Triangle.prototype.replaceVertex = function( oldv, newv ) {
264 |
265 | if ( oldv === this.v1 ) this.v1 = newv;
266 | else if ( oldv === this.v2 ) this.v2 = newv;
267 | else if ( oldv === this.v3 ) this.v3 = newv;
268 |
269 | oldv.faces.splice( oldv.faces.indexOf( this ), 1 );
270 |
271 | newv.faces.push( this );
272 |
273 |
274 | oldv.removeIfNonNeighbor( this.v1 );
275 | this.v1.removeIfNonNeighbor( oldv );
276 |
277 | oldv.removeIfNonNeighbor( this.v2 );
278 | this.v2.removeIfNonNeighbor( oldv );
279 |
280 | oldv.removeIfNonNeighbor( this.v3 );
281 | this.v3.removeIfNonNeighbor( oldv );
282 |
283 | this.v1.addUniqueNeighbor( this.v2 );
284 | this.v1.addUniqueNeighbor( this.v3 );
285 |
286 | this.v2.addUniqueNeighbor( this.v1 );
287 | this.v2.addUniqueNeighbor( this.v3 );
288 |
289 | this.v3.addUniqueNeighbor( this.v1 );
290 | this.v3.addUniqueNeighbor( this.v2 );
291 |
292 | this.computeNormal();
293 |
294 | };
295 |
296 | function Vertex( v, id ) {
297 |
298 | this.position = v;
299 |
300 | this.id = id; // old index id
301 |
302 | this.faces = []; // faces vertex is connected
303 | this.neighbors = []; // neighbouring vertices
304 |
305 | // these will be computed in computeEdgeCostAtVertex()
306 | this.cost = 0; // cost of collapsing this vertex, the less the better
307 | this.collapse = null; // best candinate for collapsing
308 |
309 | }
310 |
311 | Vertex.prototype.addUniqueNeighbor = function( vertex ) {
312 | pushIfUnique(this.neighbors, vertex);
313 | }
314 |
315 |
316 | Vertex.prototype.removeIfNonNeighbor = function( n ) {
317 |
318 | var neighbors = this.neighbors;
319 | var faces = this.faces;
320 |
321 | var offset = neighbors.indexOf( n );
322 | if ( offset === -1 ) return;
323 | for ( var i = 0; i < faces.length; i ++ ) {
324 |
325 | if ( faces[ i ].hasVertex( n ) ) return;
326 |
327 | }
328 |
329 | neighbors.splice( offset, 1 );
330 | }
331 |
332 | Vertex.prototype.isBorder = function() {
333 | var len = this.neighbors.length;
334 | for (var i = 0; i < len; i++) {
335 | var count = 0;
336 | var face_len = this.faces.length;
337 | for (var j = 0;j < face_len; j++) {
338 | if (this.faces[j].hasVertex(this.neighbors[i])) {
339 | count++;
340 | }
341 | }
342 | if (count === 1) return true;
343 | }
344 | return false;
345 | };
346 |
347 |
348 | THREE.SimplifyModifier.prototype.modify = function( geometry ) {
349 |
350 | var oldVertices = geometry.vertices;
351 | var oldFaces = geometry.faces;
352 |
353 | var newGeometry = new THREE.Geometry();
354 |
355 | var vertices = new Array( oldVertices.length );
356 | var faces = new Array( oldFaces.length );
357 |
358 | var i, il, face;
359 |
360 | //
361 | // put data of original geometry in different data structures
362 | //
363 |
364 | // add vertices
365 | for ( i = 0, il = oldVertices.length; i < il; i ++ ) {
366 |
367 | vertices[ i ] = new Vertex( oldVertices[ i ], i );
368 |
369 | }
370 |
371 | // add faces
372 | for ( i = 0, il = oldFaces.length; i < il; i ++ ) {
373 |
374 | face = oldFaces[ i ];
375 | faces[ i ] = new Triangle( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] );
376 |
377 | }
378 |
379 | // compute all edge collapse costs
380 | for ( i = 0, il = vertices.length; i < il; i ++ ) {
381 |
382 | computeEdgeCostAtVertex( vertices[ i ] );
383 |
384 | }
385 |
386 | var permutation = new Array( vertices.length );
387 | var map = new Array( vertices.length );
388 |
389 | var nextVertex;
390 |
391 | // reduce the object down to nothing:
392 | var z = 0;
393 | z = vertices.length * 0.25 | 0;
394 | z = 300;
395 |
396 | // while( z-- ) {
397 | // nextVertex = minimumCostEdge( vertices );
398 | // collapse( vertices, faces, nextVertex, nextVertex.collapse );
399 | // }
400 |
401 | while( vertices.length > 0 ) {
402 |
403 | // get the next vertex to collapse
404 | nextVertex = minimumCostEdge( vertices );
405 |
406 | // keep track of this vertex, i.e. the collapse ordering
407 | permutation[ nextVertex.id ] = vertices.length - 1;
408 |
409 | // keep track of vertex to which we collapse to
410 | map[ vertices.length - 1 ] = nextVertex.collapse ? nextVertex.collapse.id : -1;
411 |
412 | // console.log('b4 vertices', vertices.length, 'faces', faces.length);
413 | // console.log( nextVertex.id, '>', nextVertex.collapse.id)
414 |
415 | // Collapse this edge (nextVertex will go into nextVertex.collapse)
416 | collapse( vertices, faces, nextVertex, nextVertex.collapse );
417 |
418 | // console.log('after vertices', vertices.length, 'faces', faces.length);
419 | // console.log('.', map);
420 | // console.log('*', permutation);
421 | // permutation [7, 6, 5, 2, 1, 4, 3, 0, 11, 10, 9, 8]
422 | }
423 |
424 |
425 | var sortedVertices = new Array(vertices.length);
426 |
427 | for (i = 0; i < map.length; i ++ ) {
428 |
429 | map[i] = ( map[i] === - 1 ) ? 0 : permutation[ map[ i ] ];
430 |
431 | sortedVertices[ permutation[ i ] ] = oldVertices[ i ];
432 |
433 | }
434 |
435 | // console.log('after vertices', vertices.length, 'faces', faces.length);
436 |
437 | // console.log('map', map);
438 | // console.log('permutation', permutation);
439 |
440 | var sortedGeometry = new THREE.Geometry();
441 |
442 | for (i=0; i < sortedVertices.length; i++) {
443 |
444 | sortedGeometry.vertices.push( sortedVertices[ i ] );
445 |
446 | }
447 |
448 |
449 | for (i = 0; i < oldFaces.length; i++) {
450 |
451 | face = oldFaces[ i ];
452 |
453 | var a = permutation[ face.a ];
454 | var b = permutation[ face.b ];
455 | var c = permutation[ face.c ];
456 |
457 | sortedGeometry.faces.push( new THREE.Face3( a, b, c ) );
458 |
459 |
460 | }
461 |
462 | // geometry.vertices = [];
463 | // geometry.faces = [];
464 |
465 | geometry.vertices = sortedGeometry.vertices.concat();
466 | sortedGeometry.map = map;
467 | sortedGeometry.permutation = permutation;
468 |
469 | return sortedGeometry;
470 |
471 |
472 | };
473 | })()
--------------------------------------------------------------------------------
/src/vendor/SubdivisionModifier.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog
3 | *
4 | * Subdivision Geometry Modifier
5 | * using Loop Subdivision Scheme
6 | *
7 | * References:
8 | * http://graphics.stanford.edu/~mdfisher/subdivision.html
9 | * http://www.holmes3d.net/graphics/subdivision/
10 | * http://www.cs.rutgers.edu/~decarlo/readings/subdiv-sg00c.pdf
11 | *
12 | * Known Issues:
13 | * - currently doesn't handle UVs
14 | * - currently doesn't handle "Sharp Edges"
15 | *
16 | */
17 |
18 | THREE.SubdivisionModifier = function ( subdivisions ) {
19 |
20 | this.subdivisions = (subdivisions === undefined ) ? 1 : subdivisions;
21 |
22 | };
23 |
24 | // Applies the "modify" pattern
25 | THREE.SubdivisionModifier.prototype.modify = function ( geometry ) {
26 |
27 | var repeats = this.subdivisions;
28 |
29 | while ( repeats-- > 0 ) {
30 | this.smooth( geometry );
31 | }
32 |
33 | delete geometry.__tmpVertices;
34 |
35 | geometry.computeFaceNormals();
36 | geometry.computeVertexNormals();
37 |
38 | };
39 |
40 | (function() {
41 |
42 | // Some constants
43 | var WARNINGS = !true; // Set to true for development
44 | var ABC = [ 'a', 'b', 'c' ];
45 |
46 |
47 | function getEdge( a, b, map ) {
48 |
49 | var vertexIndexA = Math.min( a, b );
50 | var vertexIndexB = Math.max( a, b );
51 |
52 | var key = vertexIndexA + "_" + vertexIndexB;
53 |
54 | return map[ key ];
55 |
56 | }
57 |
58 |
59 | function processEdge( a, b, vertices, map, face, metaVertices ) {
60 |
61 | var vertexIndexA = Math.min( a, b );
62 | var vertexIndexB = Math.max( a, b );
63 |
64 | var key = vertexIndexA + "_" + vertexIndexB;
65 |
66 | var edge;
67 |
68 | if ( key in map ) {
69 |
70 | edge = map[ key ];
71 |
72 | } else {
73 |
74 | var vertexA = vertices[ vertexIndexA ];
75 | var vertexB = vertices[ vertexIndexB ];
76 |
77 | edge = {
78 |
79 | a: vertexA, // pointer reference
80 | b: vertexB,
81 | newEdge: null,
82 | // aIndex: a, // numbered reference
83 | // bIndex: b,
84 | faces: [] // pointers to face
85 |
86 | };
87 |
88 | map[ key ] = edge;
89 |
90 | }
91 |
92 | edge.faces.push( face );
93 |
94 | metaVertices[ a ].edges.push( edge );
95 | metaVertices[ b ].edges.push( edge );
96 |
97 |
98 | }
99 |
100 | function generateLookups( vertices, faces, metaVertices, edges ) {
101 |
102 | var i, il, face, edge;
103 |
104 | for ( i = 0, il = vertices.length; i < il; i++ ) {
105 | metaVertices[ i ] = { edges: [] };
106 | }
107 |
108 | for ( i = 0, il = faces.length; i < il; i++ ) {
109 | face = faces[ i ];
110 |
111 | processEdge( face.a, face.b, vertices, edges, face, metaVertices );
112 | processEdge( face.b, face.c, vertices, edges, face, metaVertices );
113 | processEdge( face.c, face.a, vertices, edges, face, metaVertices );
114 |
115 | }
116 | }
117 |
118 | function newFace( newFaces, a, b, c ) {
119 |
120 | newFaces.push( new THREE.Face3( a, b, c ) );
121 |
122 | }
123 |
124 |
125 | /////////////////////////////
126 |
127 | // Performs one iteration of Subdivision
128 | THREE.SubdivisionModifier.prototype.smooth = function ( geometry ) {
129 |
130 | var tmp = new THREE.Vector3();
131 |
132 | var oldVertices, oldFaces;
133 | var newVertices, newFaces; // newUVs = [];
134 |
135 | var n, l, i, il, j, k;
136 | var metaVertices, sourceEdges;
137 |
138 | // new stuff.
139 | var sourceEdges, newEdgeVertices, newSourceVertices
140 |
141 | oldVertices = geometry.vertices; // { x, y, z}
142 | oldFaces = geometry.faces; // { a: oldVertex1, b: oldVertex2, c: oldVertex3 }
143 |
144 | /******************************************************
145 | *
146 | * Step 0: Preprocess Geometry to Generate edges Lookup
147 | *
148 | *******************************************************/
149 |
150 | metaVertices = new Array( oldVertices.length );
151 | sourceEdges = {}; // Edge => { oldVertex1, oldVertex2, faces[] }
152 |
153 | generateLookups(oldVertices, oldFaces, metaVertices, sourceEdges);
154 |
155 |
156 | /******************************************************
157 | *
158 | * Step 1.
159 | * For each edge, create a new Edge Vertex,
160 | * then position it.
161 | *
162 | *******************************************************/
163 |
164 | newEdgeVertices = [];
165 | var other, currentEdge, newEdge, face;
166 | var edgeVertexWeight, adjacentVertexWeight, connectedFaces;
167 |
168 | for ( i in sourceEdges ) {
169 |
170 | currentEdge = sourceEdges[ i ];
171 | newEdge = new THREE.Vector3();
172 |
173 | edgeVertexWeight = 3 / 8;
174 | adjacentVertexWeight = 1 / 8;
175 |
176 | connectedFaces = currentEdge.faces.length;
177 |
178 | // check how many linked faces. 2 should be correct.
179 | if ( connectedFaces != 2 ) {
180 |
181 | // if length is not 2, handle condition
182 | edgeVertexWeight = 0.5;
183 | adjacentVertexWeight = 0;
184 |
185 | if ( connectedFaces != 1 ) {
186 |
187 | if (WARNINGS) console.warn('Subdivision Modifier: Number of connected faces != 2, is: ', connectedFaces, currentEdge);
188 |
189 | }
190 |
191 | }
192 |
193 | newEdge.addVectors( currentEdge.a, currentEdge.b ).multiplyScalar( edgeVertexWeight );
194 |
195 | tmp.set( 0, 0, 0 );
196 |
197 | for ( j = 0; j < connectedFaces; j++ ) {
198 |
199 | face = currentEdge.faces[ j ];
200 |
201 | for ( k = 0; k < 3; k++ ) {
202 |
203 | other = oldVertices[ face[ ABC[k] ] ];
204 | if (other !== currentEdge.a && other !== currentEdge.b ) break;
205 |
206 | }
207 |
208 | tmp.add( other );
209 |
210 | }
211 |
212 | tmp.multiplyScalar( adjacentVertexWeight );
213 | newEdge.add( tmp );
214 |
215 | currentEdge.newEdge = newEdgeVertices.length;
216 | newEdgeVertices.push(newEdge);
217 |
218 | // console.log(currentEdge, newEdge);
219 | }
220 |
221 | /******************************************************
222 | *
223 | * Step 2.
224 | * Reposition each source vertices.
225 | *
226 | *******************************************************/
227 |
228 | var beta, sourceVertexWeight, connectingVertexWeight;
229 | var connectingEdge, connectingEdges, oldVertex, newSourceVertex;
230 | newSourceVertices = [];
231 |
232 | for ( i = 0, il = oldVertices.length; i < il; i++ ) {
233 |
234 | oldVertex = oldVertices[ i ];
235 |
236 | // find all connecting edges (using lookupTable)
237 | connectingEdges = metaVertices[ i ].edges;
238 | n = connectingEdges.length;
239 | beta;
240 |
241 | if ( n == 3 ) {
242 |
243 | beta = 3 / 16;
244 |
245 | } else if ( n > 3 ) {
246 |
247 | beta = 3 / ( 8 * n ); // Warren's modified formula
248 |
249 | }
250 |
251 | // Loop's original beta formula
252 | // beta = 1 / n * ( 5/8 - Math.pow( 3/8 + 1/4 * Math.cos( 2 * Math. PI / n ), 2) );
253 |
254 | sourceVertexWeight = 1 - n * beta;
255 | connectingVertexWeight = beta;
256 |
257 | if ( n <= 2 ) {
258 |
259 | // crease and boundary rules
260 | // console.warn('crease and boundary rules');
261 |
262 | if ( n == 2 ) {
263 |
264 | if (WARNINGS) console.warn('2 connecting edges', connectingEdges);
265 | sourceVertexWeight = 3 / 4;
266 | connectingVertexWeight = 1 / 8;
267 |
268 | // sourceVertexWeight = 1;
269 | // connectingVertexWeight = 0;
270 |
271 | } else if ( n == 1 ) {
272 |
273 | if (WARNINGS) console.warn('only 1 connecting edge');
274 |
275 | } else if ( n == 0 ) {
276 |
277 | if (WARNINGS) console.warn('0 connecting edges');
278 |
279 | }
280 |
281 | }
282 |
283 | newSourceVertex = oldVertex.clone().multiplyScalar( sourceVertexWeight );
284 |
285 | tmp.set( 0, 0, 0 );
286 |
287 | for ( j=0; j < n; j++ ) {
288 |
289 | connectingEdge = connectingEdges[ j ];
290 | other = connectingEdge.a !== oldVertex ? connectingEdge.a : connectingEdge.b;
291 | tmp.add( other );
292 |
293 | }
294 |
295 | tmp.multiplyScalar( connectingVertexWeight );
296 | newSourceVertex.add( tmp );
297 |
298 | newSourceVertices.push( newSourceVertex );
299 |
300 | }
301 |
302 |
303 | /******************************************************
304 | *
305 | * Step 3.
306 | * Generate Faces between source vertecies
307 | * and edge vertices.
308 | *
309 | *******************************************************/
310 |
311 | newVertices = newSourceVertices.concat( newEdgeVertices );
312 | var sl = newSourceVertices.length, edge1, edge2, edge3;
313 | newFaces = [];
314 |
315 | for ( i = 0, il = oldFaces.length; i < il; i++ ) {
316 |
317 | face = oldFaces[ i ];
318 |
319 | // find the 3 new edges vertex of each old face
320 |
321 | edge1 = getEdge( face.a, face.b, sourceEdges ).newEdge + sl;
322 | edge2 = getEdge( face.b, face.c, sourceEdges ).newEdge + sl;
323 | edge3 = getEdge( face.c, face.a, sourceEdges ).newEdge + sl;
324 |
325 | // create 4 faces.
326 |
327 | newFace( newFaces, edge1, edge2, edge3 );
328 | newFace( newFaces, face.a, edge1, edge3 );
329 | newFace( newFaces, face.b, edge2, edge1 );
330 | newFace( newFaces, face.c, edge3, edge2 );
331 |
332 | }
333 |
334 | // Overwrite old arrays
335 | geometry.vertices = newVertices;
336 | geometry.faces = newFaces;
337 |
338 | // console.log('done');
339 |
340 | };
341 |
342 |
343 | })();
--------------------------------------------------------------------------------
/src/vendor/audio/Audio.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.Audio = function ( listener ) {
6 |
7 | THREE.Object3D.call( this );
8 |
9 | this.type = 'Audio';
10 |
11 | this.context = listener.context;
12 | this.source = this.context.createBufferSource();
13 | this.source.onended = this.onEnded.bind( this );
14 |
15 | this.gain = this.context.createGain();
16 | this.gain.connect( listener.getInput() );
17 |
18 | this.autoplay = false;
19 |
20 | this.startTime = 0;
21 | this.playbackRate = 1;
22 | this.isPlaying = false;
23 | this.hasPlaybackControl = true;
24 | this.sourceType = 'empty';
25 |
26 | this.filter = null;
27 |
28 | };
29 |
30 | THREE.Audio.prototype = Object.create( THREE.Object3D.prototype );
31 | THREE.Audio.prototype.constructor = THREE.Audio;
32 |
33 | THREE.Audio.prototype.getOutput = function () {
34 |
35 | return this.gain;
36 |
37 | };
38 |
39 | THREE.Audio.prototype.load = function ( file ) {
40 |
41 | var buffer = new THREE.AudioBuffer( this.context );
42 | buffer.load( file );
43 |
44 | this.setBuffer( buffer );
45 |
46 | return this;
47 |
48 | };
49 |
50 | THREE.Audio.prototype.setNodeSource = function ( audioNode ) {
51 |
52 | this.hasPlaybackControl = false;
53 | this.sourceType = 'audioNode';
54 | this.source = audioNode;
55 | this.connect();
56 |
57 | return this;
58 |
59 | };
60 |
61 | THREE.Audio.prototype.setBuffer = function ( audioBuffer ) {
62 |
63 | var scope = this;
64 |
65 | audioBuffer.onReady( function( buffer ) {
66 |
67 | scope.source.buffer = buffer;
68 | scope.sourceType = 'buffer';
69 | if ( scope.autoplay ) scope.play();
70 |
71 | } );
72 |
73 | return this;
74 |
75 | };
76 |
77 | THREE.Audio.prototype.play = function () {
78 |
79 | if ( this.isPlaying === true ) {
80 |
81 | console.warn( 'THREE.Audio: Audio is already playing.' );
82 | return;
83 |
84 | }
85 |
86 | if ( this.hasPlaybackControl === false ) {
87 |
88 | console.warn( 'THREE.Audio: this Audio has no playback control.' );
89 | return;
90 |
91 | }
92 |
93 | var source = this.context.createBufferSource();
94 |
95 | source.buffer = this.source.buffer;
96 | source.loop = this.source.loop;
97 | source.onended = this.source.onended;
98 | source.start( 0, this.startTime );
99 | source.playbackRate.value = this.playbackRate;
100 |
101 | this.isPlaying = true;
102 |
103 | this.source = source;
104 |
105 | this.connect();
106 |
107 | };
108 |
109 | THREE.Audio.prototype.pause = function () {
110 |
111 | if ( this.hasPlaybackControl === false ) {
112 |
113 | console.warn( 'THREE.Audio: this Audio has no playback control.' );
114 | return;
115 |
116 | }
117 |
118 | this.source.stop();
119 | this.startTime = this.context.currentTime;
120 |
121 | };
122 |
123 | THREE.Audio.prototype.stop = function () {
124 |
125 | if ( this.hasPlaybackControl === false ) {
126 |
127 | console.warn( 'THREE.Audio: this Audio has no playback control.' );
128 | return;
129 |
130 | }
131 |
132 | this.source.stop();
133 | this.startTime = 0;
134 |
135 | };
136 |
137 | THREE.Audio.prototype.connect = function () {
138 |
139 | if ( this.filter !== null ) {
140 |
141 | this.source.connect( this.filter );
142 | this.filter.connect( this.getOutput() );
143 |
144 | } else {
145 |
146 | this.source.connect( this.getOutput() );
147 |
148 | }
149 |
150 | };
151 |
152 | THREE.Audio.prototype.disconnect = function () {
153 |
154 | if ( this.filter !== null ) {
155 |
156 | this.source.disconnect( this.filter );
157 | this.filter.disconnect( this.getOutput() );
158 |
159 | } else {
160 |
161 | this.source.disconnect( this.getOutput() );
162 |
163 | }
164 |
165 | };
166 |
167 | THREE.Audio.prototype.getFilter = function () {
168 |
169 | return this.filter;
170 |
171 | };
172 |
173 | THREE.Audio.prototype.setFilter = function ( value ) {
174 |
175 | if ( value === undefined ) value = null;
176 |
177 | if ( this.isPlaying === true ) {
178 |
179 | this.disconnect();
180 | this.filter = value;
181 | this.connect();
182 |
183 | } else {
184 |
185 | this.filter = value;
186 |
187 | }
188 |
189 | };
190 |
191 | THREE.Audio.prototype.setPlaybackRate = function ( value ) {
192 |
193 | if ( this.hasPlaybackControl === false ) {
194 |
195 | console.warn( 'THREE.Audio: this Audio has no playback control.' );
196 | return;
197 |
198 | }
199 |
200 | this.playbackRate = value;
201 |
202 | if ( this.isPlaying === true ) {
203 |
204 | this.source.playbackRate.value = this.playbackRate;
205 |
206 | }
207 |
208 | };
209 |
210 | THREE.Audio.prototype.getPlaybackRate = function () {
211 |
212 | return this.playbackRate;
213 |
214 | };
215 |
216 | THREE.Audio.prototype.onEnded = function() {
217 |
218 | this.isPlaying = false;
219 |
220 | };
221 |
222 | THREE.Audio.prototype.setLoop = function ( value ) {
223 |
224 | if ( this.hasPlaybackControl === false ) {
225 |
226 | console.warn( 'THREE.Audio: this Audio has no playback control.' );
227 | return;
228 |
229 | }
230 |
231 | this.source.loop = value;
232 |
233 | };
234 |
235 | THREE.Audio.prototype.getLoop = function () {
236 |
237 | if ( this.hasPlaybackControl === false ) {
238 |
239 | console.warn( 'THREE.Audio: this Audio has no playback control.' );
240 | return false;
241 |
242 | }
243 |
244 | return this.source.loop;
245 |
246 | };
247 |
248 |
249 | THREE.Audio.prototype.setVolume = function ( value ) {
250 |
251 | this.gain.gain.value = value;
252 |
253 | };
254 |
255 | THREE.Audio.prototype.getVolume = function () {
256 |
257 | return this.gain.gain.value;
258 |
259 | };
260 |
--------------------------------------------------------------------------------
/src/vendor/audio/AudioAnalyser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.AudioAnalyser = function ( audio, fftSize ) {
6 |
7 | this.analyser = audio.context.createAnalyser();
8 | this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048;
9 |
10 | this.data = new Uint8Array( this.analyser.frequencyBinCount );
11 |
12 | audio.getOutput().connect( this.analyser );
13 |
14 | };
15 |
16 | THREE.AudioAnalyser.prototype = {
17 |
18 | constructor: THREE.AudioAnalyser,
19 |
20 | getData: function () {
21 |
22 | this.analyser.getByteFrequencyData( this.data );
23 | return this.data;
24 |
25 | }
26 |
27 | };
28 |
--------------------------------------------------------------------------------
/src/vendor/audio/AudioBuffer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.AudioBuffer = function ( context ) {
6 |
7 | this.context = context;
8 | this.ready = false;
9 | this.readyCallbacks = [];
10 |
11 | };
12 |
13 | THREE.AudioBuffer.prototype.load = function ( file ) {
14 |
15 | var scope = this;
16 |
17 | var request = new XMLHttpRequest();
18 | request.open( 'GET', file, true );
19 | request.responseType = 'arraybuffer';
20 | request.onload = function ( e ) {
21 |
22 | scope.context.decodeAudioData( this.response, function ( buffer ) {
23 |
24 | scope.buffer = buffer;
25 | scope.ready = true;
26 |
27 | for ( var i = 0; i < scope.readyCallbacks.length; i ++ ) {
28 |
29 | scope.readyCallbacks[ i ]( scope.buffer );
30 |
31 | }
32 |
33 | scope.readyCallbacks = [];
34 |
35 | } );
36 |
37 | };
38 | request.send();
39 |
40 | return this;
41 |
42 | };
43 |
44 | THREE.AudioBuffer.prototype.onReady = function ( callback ) {
45 |
46 | if ( this.ready ) {
47 |
48 | callback( this.buffer );
49 |
50 | } else {
51 |
52 | this.readyCallbacks.push( callback );
53 |
54 | }
55 |
56 | };
57 |
--------------------------------------------------------------------------------
/src/vendor/audio/AudioListener.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.AudioListener = function () {
6 |
7 | THREE.Object3D.call( this );
8 |
9 | this.type = 'AudioListener';
10 |
11 | this.context = new ( window.AudioContext || window.webkitAudioContext )();
12 |
13 | this.gain = this.context.createGain();
14 | this.gain.connect( this.context.destination );
15 |
16 | this.filter = null;
17 |
18 | };
19 |
20 | THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype );
21 | THREE.AudioListener.prototype.constructor = THREE.AudioListener;
22 |
23 | THREE.AudioListener.prototype.getInput = function () {
24 |
25 | return this.gain;
26 |
27 | };
28 |
29 | THREE.AudioListener.prototype.removeFilter = function ( ) {
30 |
31 | if ( this.filter !== null ) {
32 |
33 | this.gain.disconnect( this.filter );
34 | this.filter.disconnect( this.context.destination );
35 | this.gain.connect( this.context.destination );
36 | this.filter = null;
37 |
38 | }
39 |
40 | };
41 |
42 | THREE.AudioListener.prototype.setFilter = function ( value ) {
43 |
44 | if ( this.filter !== null ) {
45 |
46 | this.gain.disconnect( this.filter );
47 | this.filter.disconnect( this.context.destination );
48 |
49 | } else {
50 |
51 | this.gain.disconnect( this.context.destination );
52 |
53 | }
54 |
55 | this.filter = value;
56 | this.gain.connect( this.filter );
57 | this.filter.connect( this.context.destination );
58 |
59 | };
60 |
61 | THREE.AudioListener.prototype.getFilter = function () {
62 |
63 | return this.filter;
64 |
65 | };
66 |
67 | THREE.AudioListener.prototype.setMasterVolume = function ( value ) {
68 |
69 | this.gain.gain.value = value;
70 |
71 | };
72 |
73 | THREE.AudioListener.prototype.getMasterVolume = function () {
74 |
75 | return this.gain.gain.value;
76 |
77 | };
78 |
79 |
80 | THREE.AudioListener.prototype.updateMatrixWorld = ( function () {
81 |
82 | var position = new THREE.Vector3();
83 | var quaternion = new THREE.Quaternion();
84 | var scale = new THREE.Vector3();
85 |
86 | var orientation = new THREE.Vector3();
87 |
88 | return function updateMatrixWorld( force ) {
89 |
90 | THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
91 |
92 | var listener = this.context.listener;
93 | var up = this.up;
94 |
95 | this.matrixWorld.decompose( position, quaternion, scale );
96 |
97 | orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion );
98 |
99 | listener.setPosition( position.x, position.y, position.z );
100 | listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z );
101 |
102 | };
103 |
104 | } )();
105 |
--------------------------------------------------------------------------------
/src/vendor/audio/PositionalAudio.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.PositionalAudio = function ( listener ) {
6 |
7 | THREE.Audio.call( this, listener );
8 |
9 | this.panner = this.context.createPanner();
10 | this.panner.connect( this.gain );
11 |
12 | };
13 |
14 | THREE.PositionalAudio.prototype = Object.create( THREE.Audio.prototype );
15 | THREE.PositionalAudio.prototype.constructor = THREE.PositionalAudio;
16 |
17 | THREE.PositionalAudio.prototype.getOutput = function () {
18 |
19 | return this.panner;
20 |
21 | };
22 |
23 | THREE.PositionalAudio.prototype.setRefDistance = function ( value ) {
24 |
25 | this.panner.refDistance = value;
26 |
27 | };
28 |
29 | THREE.PositionalAudio.prototype.getRefDistance = function () {
30 |
31 | return this.panner.refDistance;
32 |
33 | };
34 |
35 | THREE.PositionalAudio.prototype.setRolloffFactor = function ( value ) {
36 |
37 | this.panner.rolloffFactor = value;
38 |
39 | };
40 |
41 | THREE.PositionalAudio.prototype.getRolloffFactor = function () {
42 |
43 | return this.panner.rolloffFactor;
44 |
45 | };
46 |
47 | THREE.PositionalAudio.prototype.setDistanceModel = function ( value ) {
48 |
49 | this.panner.distanceModel = value;
50 |
51 | };
52 |
53 | THREE.PositionalAudio.prototype.getDistanceModel = function () {
54 |
55 | return this.panner.distanceModel;
56 |
57 | };
58 |
59 | THREE.PositionalAudio.prototype.setMaxDistance = function ( value ) {
60 |
61 | this.panner.maxDistance = value;
62 |
63 | };
64 |
65 | THREE.PositionalAudio.prototype.getMaxDistance = function () {
66 |
67 | return this.panner.maxDistance;
68 |
69 | };
70 |
71 | THREE.PositionalAudio.prototype.updateMatrixWorld = ( function () {
72 |
73 | var position = new THREE.Vector3();
74 |
75 | return function updateMatrixWorld( force ) {
76 |
77 | THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
78 |
79 | position.setFromMatrixPosition( this.matrixWorld );
80 |
81 | this.panner.setPosition( position.x, position.y, position.z );
82 |
83 | };
84 |
85 | } )();
86 |
--------------------------------------------------------------------------------
/src/vendor/glitch/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 |
--------------------------------------------------------------------------------
/src/vendor/glitch/DigitalGlitch.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author felixturner / http://airtight.cc/
3 | *
4 | * RGB Shift Shader
5 | * Shifts red and blue channels from center in opposite directions
6 | * Ported from http://kriss.cx/tom/2009/05/rgb-shift/
7 | * by Tom Butterworth / http://kriss.cx/tom/
8 | *
9 | * amount: shift distance (1 is width of input)
10 | * angle: shift angle in radians
11 | */
12 |
13 | THREE.DigitalGlitch = {
14 |
15 | uniforms: {
16 |
17 | "tDiffuse": { type: "t", value: null },//diffuse texture
18 | "tDisp": { type: "t", value: null },//displacement texture for digital glitch squares
19 | "byp": { type: "i", value: 0 },//apply the glitch ?
20 | "amount": { type: "f", value: 0.08 },
21 | "angle": { type: "f", value: 0.02 },
22 | "seed": { type: "f", value: 0.02 },
23 | "seed_x": { type: "f", value: 0.02 },//-1,1
24 | "seed_y": { type: "f", value: 0.02 },//-1,1
25 | "distortion_x": { type: "f", value: 0.5 },
26 | "distortion_y": { type: "f", value: 0.6 },
27 | "col_s": { type: "f", value: 0.05 }
28 | },
29 |
30 | vertexShader: [
31 |
32 | "varying vec2 vUv;",
33 | "void main() {",
34 | "vUv = uv;",
35 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
36 | "}"
37 | ].join("\n"),
38 |
39 | fragmentShader: [
40 | "uniform int byp;",//should we apply the glitch ?
41 |
42 | "uniform sampler2D tDiffuse;",
43 | "uniform sampler2D tDisp;",
44 |
45 | "uniform float amount;",
46 | "uniform float angle;",
47 | "uniform float seed;",
48 | "uniform float seed_x;",
49 | "uniform float seed_y;",
50 | "uniform float distortion_x;",
51 | "uniform float distortion_y;",
52 | "uniform float col_s;",
53 |
54 | "varying vec2 vUv;",
55 |
56 |
57 | "float rand(vec2 co){",
58 | "return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);",
59 | "}",
60 |
61 | "void main() {",
62 | "if(byp<1) {",
63 | "vec2 p = vUv;",
64 | "float xs = floor(gl_FragCoord.x / 0.5);",
65 | "float ys = floor(gl_FragCoord.y / 0.5);",
66 | //based on staffantans glitch shader for unity https://github.com/staffantan/unityglitch
67 | "vec4 normal = texture2D (tDisp, p*seed*seed);",
68 | "if(p.ydistortion_x-col_s*seed) {",
69 | "if(seed_x>0.){",
70 | "p.y = 1. - (p.y + distortion_y);",
71 | "}",
72 | "else {",
73 | "p.y = distortion_y;",
74 | "}",
75 | "}",
76 | "if(p.xdistortion_y-col_s*seed) {",
77 | "if(seed_y>0.){",
78 | "p.x=distortion_x;",
79 | "}",
80 | "else {",
81 | "p.x = 1. - (p.x + distortion_x);",
82 | "}",
83 | "}",
84 | "p.x+=normal.x*seed_x*(seed/5.);",
85 | "p.y+=normal.y*seed_y*(seed/5.);",
86 | //base from RGB shift shader
87 | "vec2 offset = amount * vec2( cos(angle), sin(angle));",
88 | "vec4 cr = texture2D(tDiffuse, p + offset);",
89 | "vec4 cga = texture2D(tDiffuse, p);",
90 | "vec4 cb = texture2D(tDiffuse, p - offset);",
91 | "gl_FragColor = vec4(cr.r, cga.g, cb.b, cga.a);",
92 | //add noise
93 | "vec4 snow = 200.*amount*vec4(rand(vec2(xs * seed,ys * seed*50.))*0.2);",
94 | "gl_FragColor = gl_FragColor+ snow;",
95 | "}",
96 | "else {",
97 | "gl_FragColor=texture2D (tDiffuse, vUv);",
98 | "}",
99 | "}"
100 |
101 | ].join("\n")
102 |
103 | };
104 |
--------------------------------------------------------------------------------
/src/vendor/glitch/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 pixelRatio = renderer.getPixelRatio();
12 |
13 | var width = Math.floor( renderer.context.canvas.width / pixelRatio ) || 1;
14 | var height = Math.floor( renderer.context.canvas.height / pixelRatio ) || 1;
15 | var parameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };
16 |
17 | renderTarget = new THREE.WebGLRenderTarget( width, height, parameters );
18 |
19 | }
20 |
21 | this.renderTarget1 = renderTarget;
22 | this.renderTarget2 = renderTarget.clone();
23 |
24 | this.writeBuffer = this.renderTarget1;
25 | this.readBuffer = this.renderTarget2;
26 |
27 | this.passes = [];
28 |
29 | if ( THREE.CopyShader === undefined )
30 | console.error( "THREE.EffectComposer relies on THREE.CopyShader" );
31 |
32 | this.copyPass = new THREE.ShaderPass( THREE.CopyShader );
33 |
34 | };
35 |
36 | THREE.EffectComposer.prototype = {
37 |
38 | swapBuffers: function() {
39 |
40 | var tmp = this.readBuffer;
41 | this.readBuffer = this.writeBuffer;
42 | this.writeBuffer = tmp;
43 |
44 | },
45 |
46 | addPass: function ( pass ) {
47 |
48 | this.passes.push( pass );
49 |
50 | },
51 |
52 | insertPass: function ( pass, index ) {
53 |
54 | this.passes.splice( index, 0, pass );
55 |
56 | },
57 |
58 | render: function ( delta ) {
59 |
60 | this.writeBuffer = this.renderTarget1;
61 | this.readBuffer = this.renderTarget2;
62 |
63 | var maskActive = false;
64 |
65 | var pass, i, il = this.passes.length;
66 |
67 | for ( i = 0; i < il; i ++ ) {
68 |
69 | pass = this.passes[ i ];
70 |
71 | if ( !pass.enabled ) continue;
72 |
73 | pass.render( this.renderer, this.writeBuffer, this.readBuffer, delta, maskActive );
74 |
75 | if ( pass.needsSwap ) {
76 |
77 | if ( maskActive ) {
78 |
79 | var context = this.renderer.context;
80 |
81 | context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff );
82 |
83 | this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, delta );
84 |
85 | context.stencilFunc( context.EQUAL, 1, 0xffffffff );
86 |
87 | }
88 |
89 | this.swapBuffers();
90 |
91 | }
92 |
93 | if ( pass instanceof THREE.MaskPass ) {
94 |
95 | maskActive = true;
96 |
97 | } else if ( pass instanceof THREE.ClearMaskPass ) {
98 |
99 | maskActive = false;
100 |
101 | }
102 |
103 | }
104 |
105 | },
106 |
107 | reset: function ( renderTarget ) {
108 |
109 | if ( renderTarget === undefined ) {
110 |
111 | renderTarget = this.renderTarget1.clone();
112 |
113 | var pixelRatio = this.renderer.getPixelRatio();
114 |
115 | renderTarget.width = Math.floor( this.renderer.context.canvas.width / pixelRatio );
116 | renderTarget.height = Math.floor( this.renderer.context.canvas.height / pixelRatio );
117 |
118 | }
119 |
120 | this.renderTarget1 = renderTarget;
121 | this.renderTarget2 = renderTarget.clone();
122 |
123 | this.writeBuffer = this.renderTarget1;
124 | this.readBuffer = this.renderTarget2;
125 |
126 | },
127 |
128 | setSize: function ( width, height ) {
129 |
130 | var renderTarget = this.renderTarget1.clone();
131 |
132 | renderTarget.width = width;
133 | renderTarget.height = height;
134 |
135 | this.reset( renderTarget );
136 |
137 | }
138 |
139 | };
140 |
--------------------------------------------------------------------------------
/src/vendor/glitch/GlitchPass.js:
--------------------------------------------------------------------------------
1 | /**
2 |
3 | */
4 |
5 | THREE.GlitchPass = function ( dt_size ) {
6 |
7 | if ( THREE.DigitalGlitch === undefined ) console.error( "THREE.GlitchPass relies on THREE.DigitalGlitch" );
8 |
9 | var shader = THREE.DigitalGlitch;
10 | this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
11 |
12 | if (dt_size == undefined) dt_size = 64;
13 |
14 |
15 | this.uniforms[ "tDisp"].value = this.generateHeightmap(dt_size);
16 |
17 |
18 | this.material = new THREE.ShaderMaterial({
19 | uniforms: this.uniforms,
20 | vertexShader: shader.vertexShader,
21 | fragmentShader: shader.fragmentShader
22 | });
23 |
24 | //console.log(this.material);
25 |
26 | this.enabled = true;
27 | this.renderToScreen = false;
28 | this.needsSwap = true;
29 |
30 |
31 | this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
32 | this.scene = new THREE.Scene();
33 |
34 | this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
35 | this.scene.add( this.quad );
36 |
37 | this.goWild = false;
38 | this.curF = 0;
39 | this.generateTrigger();
40 |
41 | };
42 |
43 | THREE.GlitchPass.prototype = {
44 |
45 | render: function ( renderer, writeBuffer, readBuffer, delta )
46 | {
47 | this.uniforms[ "tDiffuse" ].value = readBuffer;
48 | this.uniforms[ 'seed' ].value = Math.random();//default seeding
49 | this.uniforms[ 'byp' ].value = 0;
50 |
51 | if (this.curF % this.randX == 0 || this.goWild == true)
52 | {
53 | this.uniforms[ 'amount' ].value = Math.random() / 30;
54 | this.uniforms[ 'angle' ].value = THREE.Math.randFloat(-Math.PI, Math.PI);
55 | this.uniforms[ 'seed_x' ].value = THREE.Math.randFloat(-1, 1);
56 | this.uniforms[ 'seed_y' ].value = THREE.Math.randFloat(-1, 1);
57 | this.uniforms[ 'distortion_x' ].value = THREE.Math.randFloat(0, 1);
58 | this.uniforms[ 'distortion_y' ].value = THREE.Math.randFloat(0, 1);
59 | this.curF = 0;
60 | this.generateTrigger();
61 | }
62 | else if (this.curF % this.randX < this.randX / 5)
63 | {
64 | this.uniforms[ 'amount' ].value = Math.random() / 90;
65 | this.uniforms[ 'angle' ].value = THREE.Math.randFloat(-Math.PI, Math.PI);
66 | this.uniforms[ 'distortion_x' ].value = THREE.Math.randFloat(0, 1);
67 | this.uniforms[ 'distortion_y' ].value = THREE.Math.randFloat(0, 1);
68 | this.uniforms[ 'seed_x' ].value = THREE.Math.randFloat(-0.3, 0.3);
69 | this.uniforms[ 'seed_y' ].value = THREE.Math.randFloat(-0.3, 0.3);
70 | }
71 | else if (this.goWild == false)
72 | {
73 | this.uniforms[ 'byp' ].value = 1;
74 | }
75 | this.curF ++;
76 |
77 | this.quad.material = this.material;
78 | if ( this.renderToScreen )
79 | {
80 | renderer.render( this.scene, this.camera );
81 | }
82 | else
83 | {
84 | renderer.render( this.scene, this.camera, writeBuffer, false );
85 | }
86 | },
87 | generateTrigger:function()
88 | {
89 | this.randX = THREE.Math.randInt(120, 240);
90 | },
91 | generateHeightmap:function(dt_size)
92 | {
93 | var data_arr = new Float32Array( dt_size * dt_size * 3 );
94 | //console.log(dt_size);
95 | var length = dt_size * dt_size;
96 |
97 | for ( var i = 0; i < length; i ++)
98 | {
99 | var val = THREE.Math.randFloat(0, 1);
100 | data_arr[ i * 3 + 0 ] = val;
101 | data_arr[ i * 3 + 1 ] = val;
102 | data_arr[ i * 3 + 2 ] = val;
103 | }
104 |
105 | var texture = new THREE.DataTexture( data_arr, dt_size, dt_size, THREE.RGBFormat, THREE.FloatType );
106 | //console.log(texture);
107 | //console.log(dt_size);
108 | texture.minFilter = THREE.NearestFilter;
109 | texture.magFilter = THREE.NearestFilter;
110 | texture.needsUpdate = true;
111 | texture.flipY = false;
112 | return texture;
113 | }
114 | };
--------------------------------------------------------------------------------
/src/vendor/glitch/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 |
--------------------------------------------------------------------------------
/src/vendor/glitch/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 |
--------------------------------------------------------------------------------
/src/vendor/glitch/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 | defines: shader.defines || {},
14 | uniforms: this.uniforms,
15 | vertexShader: shader.vertexShader,
16 | fragmentShader: shader.fragmentShader
17 |
18 | } );
19 |
20 | this.renderToScreen = false;
21 |
22 | this.enabled = true;
23 | this.needsSwap = true;
24 | this.clear = false;
25 |
26 |
27 | this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
28 | this.scene = new THREE.Scene();
29 |
30 | this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
31 | this.scene.add( this.quad );
32 |
33 | };
34 |
35 | THREE.ShaderPass.prototype = {
36 |
37 | render: function ( renderer, writeBuffer, readBuffer, delta ) {
38 |
39 | if ( this.uniforms[ this.textureID ] ) {
40 |
41 | this.uniforms[ this.textureID ].value = readBuffer;
42 |
43 | }
44 |
45 | this.quad.material = this.material;
46 |
47 | if ( this.renderToScreen ) {
48 |
49 | renderer.render( this.scene, this.camera );
50 |
51 | } else {
52 |
53 | renderer.render( this.scene, this.camera, writeBuffer, this.clear );
54 |
55 | }
56 |
57 | }
58 |
59 | };
60 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.8.7",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "commonjs",
6 | "declaration": false,
7 | "noImplicitAny": false,
8 | "removeComments": true,
9 | "noLib": false,
10 | "emitDecoratorMetadata": true,
11 | "experimentalDecorators": true,
12 | "sourceMap": true,
13 | "listFiles": true
14 | },
15 | "exclude": [
16 | "node_modules",
17 | "media"
18 | ]
19 | }
--------------------------------------------------------------------------------
/tsd.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "v4",
3 | "repo": "borisyankov/DefinitelyTyped",
4 | "ref": "master",
5 | "path": "typings",
6 | "bundle": "typings/tsd.d.ts",
7 | "installed": {
8 | "threejs/three.d.ts": {
9 | "commit": "977da240f263e88215a27cb44f2e31524ca07135"
10 | },
11 | "webaudioapi/waa.d.ts": {
12 | "commit": "977da240f263e88215a27cb44f2e31524ca07135"
13 | },
14 | "webrtc/MediaStream.d.ts": {
15 | "commit": "977da240f263e88215a27cb44f2e31524ca07135"
16 | },
17 | "whatwg-fetch/whatwg-fetch.d.ts": {
18 | "commit": "c5a2f44bab06cf9f2bcc14530171daac1cebff6c"
19 | },
20 | "es6-promise/es6-promise.d.ts": {
21 | "commit": "c5a2f44bab06cf9f2bcc14530171daac1cebff6c"
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "class-name": true,
4 | "max-line-length": [true, 100],
5 | "comment-format": [true, "check-space"],
6 | "curly": false,
7 | "eofline": true,
8 | "forin": true,
9 | "indent": [true, "spaces"],
10 | "label-position": true,
11 | "label-undefined": true,
12 | "max-line-length": [true, 140],
13 | "member-access": true,
14 | "member-ordering": [true,
15 | "public-before-private",
16 | "static-before-instance",
17 | "variables-before-functions"
18 | ],
19 | "no-arg": true,
20 | "no-bitwise": true,
21 | "no-console": [true,
22 | "debug",
23 | "info",
24 | "time",
25 | "timeEnd",
26 | "trace"
27 | ],
28 | "no-construct": true,
29 | "no-debugger": true,
30 | "no-duplicate-key": true,
31 | "no-duplicate-variable": true,
32 | "no-empty": true,
33 | "no-eval": true,
34 | "no-inferrable-types": true,
35 | "no-shadowed-variable": true,
36 | "no-string-literal": true,
37 | "no-switch-case-fall-through": true,
38 | "no-trailing-whitespace": true,
39 | "no-unused-expression": true,
40 | "no-unused-variable": true,
41 | "no-unreachable": true,
42 | "no-use-before-declare": true,
43 | "no-var-keyword": true,
44 | "object-literal-sort-keys": false,
45 | "one-line": [true,
46 | "check-catch",
47 | "check-finally",
48 | "check-whitespace"
49 | ],
50 | "quotemark": [true, "single", "avoid-escape"],
51 | "radix": true,
52 | "semicolon": true,
53 | "trailing-comma": [true, {
54 | "singleline": "never",
55 | "multiline": "always"
56 | }],
57 | "triple-equals": false,
58 | "typedef-whitespace": [true, {
59 | "call-signature": "nospace",
60 | "index-signature": "nospace",
61 | "parameter": "nospace",
62 | "property-declaration": "nospace",
63 | "variable-declaration": "nospace"
64 | }],
65 | "variable-name": [
66 | true,
67 | "ban-keywords",
68 | "check-format",
69 | "allow-leading-underscore"
70 | ],
71 | "whitespace": [true,
72 | "check-branch",
73 | "check-decl",
74 | "check-operator",
75 | "check-separator",
76 | "check-type"
77 | ]
78 | }
79 | }
--------------------------------------------------------------------------------
/typings_custom/glitch.d.ts:
--------------------------------------------------------------------------------
1 | declare module THREE{
2 | export class RenderPass{
3 | constructor(scene: Scene, camera: Camera);
4 | }
5 |
6 | export class EffectComposer{
7 | constructor(renderer: Renderer);
8 | render(): void;
9 | addPass(pass: RenderPass);
10 | setSize(x:Number, y:Number);
11 | }
12 |
13 | export class GlitchPass{
14 | renderToScreen: boolean;
15 | goWild: boolean;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/typings_custom/p2.d.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for p2.js v0.6.0
2 | // Project: https://github.com/schteppe/p2.js/
3 | // Rev 31/12/2014
4 |
5 | declare module p2 {
6 |
7 | export class AABB {
8 |
9 | constructor(options?: {
10 | upperBound?: number[];
11 | lowerBound?: number[];
12 | });
13 |
14 | setFromPoints(points: number[][], position: number[], angle: number, skinSize: number): void;
15 | copy(aabb: AABB): void;
16 | extend(aabb: AABB): void;
17 | overlaps(aabb: AABB): boolean;
18 |
19 | }
20 |
21 | export class Broadphase {
22 |
23 | static AABB: number;
24 | static BOUNDING_CIRCLE: number;
25 |
26 | static NAIVE: number;
27 | static SAP: number;
28 |
29 | static boundingRadiusCheck(bodyA: Body, bodyB: Body): boolean;
30 | static aabbCheck(bodyA: Body, bodyB: Body): boolean;
31 | static canCollide(bodyA: Body, bodyB: Body): boolean;
32 |
33 | constructor(type: number);
34 |
35 | type: number;
36 | result: Body[];
37 | world: World;
38 | boundingVolumeType: number;
39 |
40 | setWorld(world: World): void;
41 | getCollisionPairs(world: World): Body[];
42 | boundingVolumeCheck(bodyA: Body, bodyB: Body): boolean;
43 |
44 | }
45 |
46 | export class GridBroadphase extends Broadphase {
47 |
48 | constructor(options?: {
49 | xmin?: number;
50 | xmax?: number;
51 | ymin?: number;
52 | ymax?: number;
53 | nx?: number;
54 | ny?: number;
55 | });
56 |
57 | xmin: number;
58 | xmax: number;
59 | ymin: number;
60 | ymax: number;
61 | nx: number;
62 | ny: number;
63 | binsizeX: number;
64 | binsizeY: number;
65 |
66 | }
67 |
68 | export class NativeBroadphase extends Broadphase {
69 |
70 | }
71 |
72 | export class Narrowphase {
73 |
74 | contactEquations: ContactEquation[];
75 | frictionEquations: FrictionEquation[];
76 | enableFriction: boolean;
77 | enableEquations: boolean;
78 | slipForce: number;
79 | frictionCoefficient: number;
80 | surfaceVelocity: number;
81 | reuseObjects: boolean;
82 | resuableContactEquations: any[];
83 | reusableFrictionEquations: any[];
84 | restitution: number;
85 | stiffness: number;
86 | relaxation: number;
87 | frictionStiffness: number;
88 | frictionRelaxation: number;
89 | enableFrictionReduction: boolean;
90 | contactSkinSize: number;
91 |
92 | collidedLastStep(bodyA: Body, bodyB: Body): boolean;
93 | reset(): void;
94 | createContactEquation(bodyA: Body, bodyB: Body, shapeA: Shape, shapeB: Shape): ContactEquation;
95 | createFrictionFromContact(c: ContactEquation): FrictionEquation;
96 |
97 | }
98 |
99 | export class SAPBroadphase extends Broadphase {
100 |
101 | axisList: Body[];
102 | axisIndex: number;
103 |
104 | }
105 |
106 | export class Constraint {
107 |
108 | static DISTANCE: number;
109 | static GEAR: number;
110 | static LOCK: number;
111 | static PRISMATIC: number;
112 | static REVOLUTE: number;
113 |
114 | constructor(bodyA: Body, bodyB: Body, type: number, options?: {
115 | collideConnected?: boolean;
116 | wakeUpBodies?: boolean;
117 | });
118 |
119 | type: number;
120 | equeations: Equation[];
121 | bodyA: Body;
122 | bodyB: Body;
123 | collideConnected: boolean;
124 |
125 | update(): void;
126 | setStiffness(stiffness: number): void;
127 | setRelaxation(relaxation: number): void;
128 |
129 | }
130 |
131 | export class DistanceConstraint extends Constraint {
132 |
133 | constructor(bodyA: Body, bodyB: Body, type: number, options?: {
134 | collideConnected?: boolean;
135 | wakeUpBodies?: boolean;
136 | distance?: number;
137 | localAnchorA?: number[];
138 | localAnchorB?: number[];
139 | maxForce?: number;
140 | });
141 |
142 | localAnchorA: number[];
143 | localAnchorB: number[];
144 | distance: number;
145 | maxForce: number;
146 | upperLimitEnabled: boolean;
147 | upperLimit: number;
148 | lowerLimitEnabled: boolean;
149 | lowerLimit: number;
150 | position: number;
151 |
152 | setMaxForce(f: number): void;
153 | getMaxForce(): number;
154 |
155 | }
156 |
157 | export class GearConstraint extends Constraint {
158 |
159 | constructor(bodyA: Body, bodyB: Body, type: number, options?: {
160 | collideConnected?: boolean;
161 | wakeUpBodies?: boolean;
162 | angle?: number;
163 | ratio?: number;
164 | maxTorque?: number;
165 | });
166 |
167 | ratio: number;
168 | angle: number;
169 |
170 | setMaxTorque(torque: number): void;
171 | getMaxTorque(): number;
172 |
173 | }
174 |
175 | export class LockConstraint extends Constraint {
176 |
177 | constructor(bodyA: Body, bodyB: Body, type: number, options?: {
178 | collideConnected?: boolean;
179 | wakeUpBodies?: boolean;
180 | localOffsetB?: number[];
181 | localAngleB?: number;
182 | maxForce?: number;
183 | });
184 |
185 | setMaxForce(force: number): void;
186 | getMaxForce(): number;
187 |
188 | }
189 |
190 | export class PrismaticConstraint extends Constraint {
191 |
192 | constructor(bodyA: Body, bodyB: Body, type: number, options?: {
193 | collideConnected?: boolean;
194 | wakeUpBodies?: boolean;
195 | maxForce?: number;
196 | localAnchorA?: number[];
197 | localAnchorB?: number[];
198 | localAxisA?: number[];
199 | disableRotationalLock?: boolean;
200 | upperLimit?: number;
201 | lowerLimit?: number;
202 | });
203 |
204 | localAnchorA: number[];
205 | localAnchorB: number[];
206 | localAxisA: number[];
207 | position: number;
208 | velocity: number;
209 | lowerLimitEnabled: boolean;
210 | upperLimitEnabled: boolean;
211 | lowerLimit: number;
212 | upperLimit: number;
213 | upperLimitEquation: ContactEquation;
214 | lowerLimitEquation: ContactEquation;
215 | motorEquation: Equation;
216 | motorEnabled: boolean;
217 | motorSpeed: number;
218 |
219 | enableMotor(): void;
220 | disableMotor(): void;
221 | setLimits(lower: number, upper: number): void;
222 |
223 | }
224 |
225 | export class RevoluteConstraint extends Constraint {
226 |
227 | constructor(bodyA: Body, bodyB: Body, type: number, options?: {
228 | collideConnected?: boolean;
229 | wakeUpBodies?: boolean;
230 | worldPivot?: number[];
231 | localPivotA?: number[];
232 | localPivotB?: number[];
233 | maxForce?: number;
234 | });
235 |
236 | pivotA: number[];
237 | pivotB: number[];
238 | motorEquation: RotationalVelocityEquation;
239 | motorEnabled: boolean;
240 | angle: number;
241 | lowerLimitEnabled: boolean;
242 | upperLimitEnabled: boolean;
243 | lowerLimit: number;
244 | upperLimit: number;
245 | upperLimitEquation: ContactEquation;
246 | lowerLimitEquation: ContactEquation;
247 |
248 | enableMotor(): void;
249 | disableMotor(): void;
250 | motorIsEnabled(): boolean;
251 | setLimits(lower: number, upper: number): void;
252 | setMotorSpeed(speed: number): void;
253 | getMotorSpeed(): number;
254 |
255 | }
256 |
257 | export class AngleLockEquation extends Equation {
258 |
259 | constructor(bodyA: Body, bodyB: Body, options?: {
260 | angle?: number;
261 | ratio?: number;
262 | });
263 |
264 | computeGq(): number;
265 | setRatio(ratio: number): number;
266 | setMaxTorque(torque: number): number;
267 |
268 | }
269 |
270 | export class ContactEquation extends Equation {
271 |
272 | constructor(bodyA: Body, bodyB: Body);
273 |
274 | contactPointA: number[];
275 | penetrationVec: number[];
276 | contactPointB: number[];
277 | normalA: number[];
278 | restitution: number;
279 | firstImpact: boolean;
280 | shapeA: Shape;
281 | shapeB: Shape;
282 |
283 | computeB(a: number, b: number, h: number): number;
284 |
285 | }
286 |
287 | export class Equation {
288 |
289 | static DEFAULT_STIFFNESS: number;
290 | static DEFAULT_RELAXATION: number;
291 |
292 | constructor(bodyA: Body, bodyB: Body, minForce?: number, maxForce?: number);
293 |
294 | minForce: number;
295 | maxForce: number;
296 | bodyA: Body;
297 | bodyB: Body;
298 | stiffness: number;
299 | relaxation: number;
300 | G: number[];
301 | offset: number;
302 | a: number;
303 | b: number;
304 | epsilon: number;
305 | timeStep: number;
306 | needsUpdate: boolean;
307 | multiplier: number;
308 | relativeVelocity: number;
309 | enabled: boolean;
310 |
311 | gmult(G: number[], vi: number[], wi: number[], vj: number[], wj: number[]): number;
312 | computeB(a: number, b: number, h: number): number;
313 | computeGq(): number;
314 | computeGW(): number;
315 | computeGWlambda(): number;
316 | computeGiMf(): number;
317 | computeGiMGt(): number;
318 | addToWlambda(deltalambda: number): number;
319 | computeInvC(eps: number): number;
320 |
321 | }
322 |
323 | export class FrictionEquation extends Equation {
324 |
325 | constructor(bodyA: Body, bodyB: Body, slipForce: number);
326 |
327 | contactPointA: number[];
328 | contactPointB: number[];
329 | t: number[];
330 | shapeA: Shape;
331 | shapeB: Shape;
332 | frictionCoefficient: number;
333 |
334 | setSlipForce(slipForce: number): number;
335 | getSlipForce(): number;
336 | computeB(a: number, b: number, h: number): number;
337 |
338 | }
339 |
340 | export class RotationalLockEquation extends Equation {
341 |
342 | constructor(bodyA: Body, bodyB: Body, options?: {
343 | angle?: number;
344 | });
345 |
346 | angle: number;
347 |
348 | computeGq(): number;
349 |
350 | }
351 |
352 | export class RotationalVelocityEquation extends Equation {
353 |
354 | constructor(bodyA: Body, bodyB: Body);
355 |
356 | computeB(a: number, b: number, h: number): number;
357 |
358 | }
359 |
360 | export class EventEmitter {
361 |
362 | on(type: string, listener: Function, context: any): EventEmitter;
363 | has(type: string, listener: Function): boolean;
364 | off(type: string, listener: Function): EventEmitter;
365 | emit(event: any): EventEmitter;
366 |
367 | }
368 |
369 | export interface ContactMaterialOptions {
370 |
371 | friction?: number;
372 | restitution?: number;
373 | stiffness?: number;
374 | relaxation?: number;
375 | frictionStiffness?: number;
376 | frictionRelaxation?: number;
377 | surfaceVelocity?: number;
378 |
379 | }
380 |
381 | export class ContactMaterial {
382 |
383 | static idCounter: number;
384 |
385 | constructor(materialA: Material, materialB: Material, options?: ContactMaterialOptions);
386 |
387 | id: number;
388 | materialA: Material;
389 | materialB: Material;
390 | friction: number;
391 | restitution: number;
392 | stiffness: number;
393 | relaxation: number;
394 | frictionStuffness: number;
395 | frictionRelaxation: number;
396 | surfaceVelocity: number;
397 | contactSkinSize: number;
398 |
399 | }
400 |
401 | export class Material {
402 |
403 | static idCounter: number;
404 |
405 | constructor(id?: number);
406 |
407 | id: number;
408 |
409 | }
410 |
411 | export class vec2 {
412 |
413 | static crossLength(a: number[], b: number[]): number;
414 | static crossVZ(out: number[], vec: number[], zcomp: number): number;
415 | static crossZV(out: number[], zcomp: number, vec: number[]): number;
416 | static rotate(out: number[], a: number[], angle: number): void;
417 | static rotate90cw(out: number[], a: number[]): number;
418 | static centroid(out: number[], a: number[], b: number[], c: number[]): number[];
419 | static create(): number[];
420 | static clone(a: number[]): number[];
421 | static fromValues(x: number, y: number): number[];
422 | static copy(out: number[], a: number[]): number[];
423 | static set(out: number[], x: number, y: number): number[];
424 | static toLocalFrame(out: number[], worldPoint: number[], framePosition: number[], frameAngle: number): void;
425 | static toGlobalFrame(out: number[], localPoint: number[], framePosition: number[], frameAngle: number): void;
426 | static add(out: number[], a: number[], b: number[]): number[];
427 | static subtract(out: number[], a: number[], b: number[]): number[];
428 | static sub(out: number[], a: number[], b: number[]): number[];
429 | static multiply(out: number[], a: number[], b: number[]): number[];
430 | static mul(out: number[], a: number[], b: number[]): number[];
431 | static divide(out: number[], a: number[], b: number[]): number[];
432 | static div(out: number[], a: number[], b: number[]): number[];
433 | static scale(out: number[], a: number[], b: number): number[];
434 | static distance(a: number[], b: number[]): number;
435 | static dist(a: number[], b: number[]): number;
436 | static squaredDistance(a: number[], b: number[]): number;
437 | static sqrDist(a: number[], b: number[]): number;
438 | static length(a: number[]): number;
439 | static len(a: number[]): number;
440 | static squaredLength(a: number[]): number;
441 | static sqrLen(a: number[]): number;
442 | static negate(out: number[], a: number[]): number[];
443 | static normalize(out: number[], a: number[]): number[];
444 | static dot(a: number[], b: number[]): number;
445 | static str(a: number[]): string;
446 |
447 | }
448 |
449 | export interface BodyOptions {
450 |
451 | mass?: number;
452 | position?: number[];
453 | velocity?: number[];
454 | angle?: number;
455 | angularVelocity?: number;
456 | force?: number[];
457 | angularForce?: number;
458 | fixedRotation?: number;
459 |
460 | }
461 |
462 | export class Body extends EventEmitter {
463 |
464 | sleepyEvent: {
465 | type: string;
466 | };
467 |
468 | sleepEvent: {
469 | type: string;
470 | };
471 |
472 | wakeUpEvent: {
473 | type: string;
474 | };
475 |
476 | static DYNAMIC: number;
477 | static STATIC: number;
478 | static KINEMATIC: number;
479 | static AWAKE: number;
480 | static SLEEPY: number;
481 | static SLEEPING: number;
482 |
483 | constructor(options?: BodyOptions);
484 |
485 | id: number;
486 | world: World;
487 | shapes: Shape[];
488 | shapeOffsets: number[][];
489 | shapeAngles: number[];
490 | mass: number;
491 | invMass: number;
492 | inertia: number;
493 | invInertia: number;
494 | invMassSolve: number;
495 | invInertiaSolve: number;
496 | fixedRotation: number;
497 | position: number[];
498 | interpolatedPosition: number[];
499 | interpolatedAngle: number;
500 | previousPosition: number[];
501 | previousAngle: number;
502 | velocity: number[];
503 | vlambda: number[];
504 | wlambda: number[];
505 | angle: number;
506 | angularVelocity: number;
507 | force: number[];
508 | angularForce: number;
509 | damping: number;
510 | angularDamping: number;
511 | type: number;
512 | boundingRadius: number;
513 | aabb: AABB;
514 | aabbNeedsUpdate: boolean;
515 | allowSleep: boolean;
516 | wantsToSleep: boolean;
517 | sleepState: number;
518 | sleepSpeedLimit: number;
519 | sleepTimeLimit: number;
520 | gravityScale: number;
521 | collisionResponse: boolean;
522 |
523 | updateSolveMassProperties(): void;
524 | setDensity(density: number): void;
525 | getArea(): number;
526 | getAABB(): AABB;
527 | updateAABB(): void;
528 | updateBoundingRadius(): void;
529 | addShape(shape: Shape, offset?: number[], angle?: number): void;
530 | removeShape(shape: Shape): boolean;
531 | updateMassProperties(): void;
532 | applyForce(force: number[], worldPoint: number[]): void;
533 | toLocalFrame(out: number[], worldPoint: number[]): void;
534 | toWorldFrame(out: number[], localPoint: number[]): void;
535 | fromPolygon(path: number[][], options?: {
536 | optimalDecomp?: boolean;
537 | skipSimpleCheck?: boolean;
538 | removeCollinearPoints?: any; //boolean | number
539 | }): boolean;
540 | adjustCenterOfMass(): void;
541 | setZeroForce(): void;
542 | resetConstraintVelocity(): void;
543 | applyDamping(dy: number): void;
544 | wakeUp(): void;
545 | sleep(): void;
546 | sleepTick(time: number, dontSleep: boolean, dt: number): void;
547 | getVelocityFromPosition(story: number[], dt: number): number[];
548 | getAngularVelocityFromPosition(timeStep: number): number;
549 | overlaps(body: Body): boolean;
550 |
551 | }
552 |
553 | export class Spring {
554 |
555 | constructor(bodyA: Body, bodyB: Body, options?: {
556 |
557 | stiffness?: number;
558 | damping?: number;
559 | localAnchorA?: number[];
560 | localAnchorB?: number[];
561 | worldAnchorA?: number[];
562 | worldAnchorB?: number[];
563 |
564 | });
565 |
566 | stiffness: number;
567 | damping: number;
568 | bodyA: Body;
569 | bodyB: Body;
570 |
571 | applyForce(): void;
572 |
573 | }
574 |
575 | export class LinearSpring extends Spring {
576 |
577 | localAnchorA: number[];
578 | localAnchorB: number[];
579 | restLength: number;
580 |
581 | setWorldAnchorA(worldAnchorA: number[]): void;
582 | setWorldAnchorB(worldAnchorB: number[]): void;
583 | getWorldAnchorA(result: number[]): number[];
584 | getWorldAnchorB(result: number[]): number[];
585 | applyForce(): void;
586 |
587 | }
588 |
589 | export class RotationalSpring extends Spring {
590 |
591 | constructor(bodyA: Body, bodyB: Body, options?: {
592 | restAngle?: number;
593 | stiffness?: number;
594 | damping?: number;
595 | });
596 |
597 | restAngle: number;
598 |
599 | }
600 |
601 | export class Capsule extends Shape {
602 |
603 | constructor(length?: number, radius?: number);
604 |
605 | length: number;
606 | radius: number;
607 |
608 | }
609 |
610 | export interface CircleOptions{
611 | radius?: number;
612 | }
613 |
614 | export class Circle extends Shape {
615 |
616 | constructor(p?: CircleOptions);
617 |
618 | radius: number;
619 |
620 | }
621 |
622 | export class Convex extends Shape {
623 |
624 | static triangleArea(a: number[], b: number[], c: number[]): number;
625 |
626 | constructor(vertices: number[][], axes: number[]);
627 |
628 | vertices: number[][];
629 | axes: number[];
630 | centerOfMass: number[];
631 | triangles: number[];
632 | boundingRadius: number;
633 |
634 | projectOntoLocalAxis(localAxis: number[], result: number[]): void;
635 | projectOntoWorldAxis(localAxis: number[], shapeOffset: number[], shapeAngle: number, result: number[]): void;
636 |
637 | updateCenterOfMass(): void;
638 |
639 | }
640 |
641 | export class Heightfield extends Shape {
642 |
643 | constructor(data: number[], options?: {
644 | minValue?: number;
645 | maxValue?: number;
646 | elementWidth: number;
647 | });
648 |
649 | data: number[];
650 | maxValue: number;
651 | minValue: number;
652 | elementWidth: number;
653 |
654 | }
655 |
656 | export class Shape {
657 |
658 | static idCounter: number;
659 | static CIRCLE: number;
660 | static PARTICLE: number;
661 | static PLANE: number;
662 | static CONVEX: number;
663 | static LINE: number;
664 | static RECTANGLE: number;
665 | static CAPSULE: number;
666 | static HEIGHTFIELD: number;
667 |
668 | constructor(type: number);
669 |
670 | type: number;
671 | id: number;
672 | boundingRadius: number;
673 | collisionGroup: number;
674 | collisionResponse: boolean;
675 | collisionMask: number;
676 | material: Material;
677 | area: number;
678 | sensor: boolean;
679 |
680 | computeMomentOfInertia(mass: number): number;
681 | updateBoundingRadius(): number;
682 | updateArea(): void;
683 | computeAABB(out: AABB, position: number[], angle: number): void;
684 |
685 | }
686 |
687 | export class Line extends Shape {
688 |
689 | constructor(length?: number);
690 |
691 | length: number;
692 |
693 | }
694 |
695 | export class Particle extends Shape {
696 |
697 | }
698 |
699 | export class Plane extends Shape {
700 |
701 | }
702 |
703 | export interface BoxParameters{
704 | width?: number,
705 | height?: number
706 | }
707 |
708 | export class Box extends Shape {
709 |
710 | constructor(p: BoxParameters);
711 |
712 | width: number;
713 | height: number;
714 |
715 | }
716 |
717 | export class Solver extends EventEmitter {
718 |
719 | static GS: number;
720 | static ISLAND: number;
721 |
722 | constructor(options?: {}, type?: number);
723 |
724 | type: number;
725 | equations: Equation[];
726 | equationSortFunction: Equation; //Equation | boolean
727 |
728 | solve(dy: number, world: World): void;
729 | solveIsland(dy: number, island: Island): void;
730 | sortEquations(): void;
731 | addEquation(eq: Equation): void;
732 | addEquations(eqs: Equation[]): void;
733 | removeEquation(eq: Equation): void;
734 | removeAllEquations(): void;
735 |
736 | }
737 |
738 | export class GSSolver extends Solver {
739 |
740 | constructor(options?: {
741 | iterations?: number;
742 | tolerance?: number;
743 | });
744 |
745 | iterations: number;
746 | tolerance: number;
747 | useZeroRHS: boolean;
748 | frictionIterations: number;
749 | usedIterations: number;
750 |
751 | solve(h: number, world: World): void;
752 |
753 | }
754 |
755 | export class OverlapKeeper {
756 |
757 | constructor(bodyA: Body, shapeA: Shape, bodyB: Body, shapeB: Shape);
758 |
759 | shapeA: Shape;
760 | shapeB: Shape;
761 | bodyA: Body;
762 | bodyB: Body;
763 |
764 | tick(): void;
765 | setOverlapping(bodyA: Body, shapeA: Shape, bodyB: Body, shapeB: Body): void;
766 | bodiesAreOverlapping(bodyA: Body, bodyB: Body): boolean;
767 | set(bodyA: Body, shapeA: Shape, bodyB: Body, shapeB: Shape): void;
768 |
769 | }
770 |
771 | export class TupleDictionary {
772 |
773 | data: number[];
774 | keys: number[];
775 |
776 | getKey(id1: number, id2: number): string;
777 | getByKey(key: number): number;
778 | get(i: number, j: number): number;
779 | set(i: number, j: number, value: number): number;
780 | reset(): void;
781 | copy(dict: TupleDictionary): void;
782 |
783 | }
784 |
785 | export class Utils {
786 |
787 | static appendArray(a: Array, b: Array): Array;
788 | static splice(array: Array, index: number, howMany: number): void;
789 | static extend(a: any, b: any): void;
790 | static defaults(options: any, defaults: any): any;
791 |
792 | }
793 |
794 | export class Island {
795 |
796 | equations: Equation[];
797 | bodies: Body[];
798 |
799 | reset(): void;
800 | getBodies(result: any): Body[];
801 | wantsToSleep(): boolean;
802 | sleep(): boolean;
803 |
804 | }
805 |
806 | export class IslandManager extends Solver {
807 |
808 | static getUnvisitedNode(nodes: Node[]): IslandNode; // IslandNode | boolean
809 |
810 | equations: Equation[];
811 | islands: Island[];
812 | nodes: IslandNode[];
813 |
814 | visit(node: IslandNode, bds: Body[], eqs: Equation[]): void;
815 | bfs(root: IslandNode, bds: Body[], eqs: Equation[]): void;
816 | split(world: World): Island[];
817 |
818 | }
819 |
820 | export class IslandNode {
821 |
822 | constructor(body: Body);
823 |
824 | body: Body;
825 | neighbors: IslandNode[];
826 | equations: Equation[];
827 | visited: boolean;
828 |
829 | reset(): void;
830 |
831 | }
832 |
833 | export class World extends EventEmitter {
834 |
835 | postStepEvent: {
836 | type: string;
837 | };
838 |
839 | addBodyEvent: {
840 | type: string;
841 | };
842 |
843 | removeBodyEvent: {
844 | type: string;
845 | };
846 |
847 | addSpringEvent: {
848 | type: string;
849 | };
850 |
851 | impactEvent: {
852 | type: string;
853 | bodyA: Body;
854 | bodyB: Body;
855 | shapeA: Shape;
856 | shapeB: Shape;
857 | contactEquation: ContactEquation;
858 | };
859 |
860 | postBroadphaseEvent: {
861 | type: string;
862 | pairs: Body[];
863 | };
864 |
865 | beginContactEvent: {
866 | type: string;
867 | shapeA: Shape;
868 | shapeB: Shape;
869 | bodyA: Body;
870 | bodyB: Body;
871 | contactEquations: ContactEquation[];
872 | };
873 |
874 | endContactEvent: {
875 | type: string;
876 | shapeA: Shape;
877 | shapeB: Shape;
878 | bodyA: Body;
879 | bodyB: Body;
880 | };
881 |
882 | preSolveEvent: {
883 | type: string;
884 | contactEquations: ContactEquation[];
885 | frictionEquations: FrictionEquation[];
886 | };
887 |
888 | static NO_SLEEPING: number;
889 | static BODY_SLEEPING: number;
890 | static ISLAND_SLEEPING: number;
891 |
892 | static integrateBody(body: Body, dy: number): void;
893 |
894 | constructor(options?: {
895 | solver?: Solver;
896 | gravity?: number[];
897 | broadphase?: Broadphase;
898 | islandSplit?: boolean;
899 | doProfiling?: boolean;
900 | });
901 |
902 | springs: Spring[];
903 | bodies: Body[];
904 | solver: Solver;
905 | narrowphase: Narrowphase;
906 | islandManager: IslandManager;
907 | gravity: number[];
908 | frictionGravity: number;
909 | useWorldGravityAsFrictionGravity: boolean;
910 | useFrictionGravityOnZeroGravity: boolean;
911 | doProfiling: boolean;
912 | lastStepTime: number;
913 | broadphase: Broadphase;
914 | constraints: Constraint[];
915 | defaultMaterial: Material;
916 | defaultContactMaterial: ContactMaterial;
917 | lastTimeStep: number;
918 | applySpringForces: boolean;
919 | applyDamping: boolean;
920 | applyGravity: boolean;
921 | solveConstraints: boolean;
922 | contactMaterials: ContactMaterial[];
923 | time: number;
924 | stepping: boolean;
925 | islandSplit: boolean;
926 | emitImpactEvent: boolean;
927 | sleepMode: number;
928 |
929 | addConstraint(c: Constraint): void;
930 | addContactMaterial(contactMaterial: ContactMaterial): void;
931 | removeContactMaterial(cm: ContactMaterial): void;
932 | getContactMaterial(materialA: Material, materialB: Material): ContactMaterial; // ContactMaterial | boolean
933 | removeConstraint(c: Constraint): void;
934 | step(dy: number, timeSinceLastCalled?: number, maxSubSteps?: number): void;
935 | runNarrowphase(np: Narrowphase, bi: Body, si: Shape, xi: any[], ai: number, bj: Body, sj: Shape, xj: any[], aj: number, cm: number, glen: number): void;
936 | addSpring(s: Spring): void;
937 | removeSpring(s: Spring): void;
938 | addBody(body: Body): void;
939 | removeBody(body: Body): void;
940 | getBodyByID(id: number): Body; //Body | boolean
941 | disableBodyCollision(bodyA: Body, bodyB: Body): void;
942 | enableBodyCollision(bodyA: Body, bodyB: Body): void;
943 | clear(): void;
944 | clone(): World;
945 | hitTest(worldPoint: number[], bodies: Body[], precision: number): Body[];
946 | setGlobalEquationParameters(parameters: {
947 | relaxation?: number;
948 | stiffness?: number;
949 | }): void;
950 | setGlobalStiffness(stiffness: number): void;
951 | setGlobalRelaxation(relaxation: number): void;
952 |
953 | on(type:string, listener:Function): EventEmitter;
954 | off(type:string, listener:Function): EventEmitter;
955 | }
956 |
957 | }
958 |
959 | declare module 'p2' {
960 | export=p2;
961 | }
962 |
--------------------------------------------------------------------------------
/typings_custom/positionalaudio.d.ts:
--------------------------------------------------------------------------------
1 | declare module THREE {
2 | export class PositionalAudio extends THREE.Audio {
3 | autoplay: boolean;
4 | constructor(listner: THREE.AudioListener);
5 | load(path: string);
6 | setRefDistance(n: number);
7 | setLoop(x: boolean);
8 | setVolume(x: number);
9 | setDistanceModel(x: string);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/typings_custom/simplifymodifier.d.ts:
--------------------------------------------------------------------------------
1 | declare module THREE{
2 | export class SimplifyModifier{
3 | constructor(_:any);
4 | modify(_:any):any;
5 | }
6 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require("webpack");
2 |
3 | module.exports = {
4 | devtool: 'source-map',
5 |
6 | context: __dirname + '/src',
7 | entry: {
8 | main: './main.ts'
9 | //obj: './lowpoly.ts'
10 | },
11 | resolve: {
12 | extensions: ['', '.ts', '.js']
13 | },
14 | module: {
15 | loaders: [
16 | { test: /\.json$/, loader: 'json' },
17 | { test: /\.css$/, loader: 'raw' },
18 | { test: /\.html$/, loader: 'raw' },
19 | { test: /\.ts$/, loader: 'ts-loader' }
20 | ]
21 | },
22 | output: {
23 | path: __dirname,
24 | filename: "[name].bundle.js"
25 | },
26 | plugins: [
27 | new webpack.ProvidePlugin({
28 | THREE: __dirname + "/src/vendor/three"
29 | })
30 | ],
31 | devServer: {
32 | contentBase: ".",
33 | noInfo: true,
34 | hot: true,
35 | inline: true,
36 | port: 9000,
37 | }
38 | }
39 |
--------------------------------------------------------------------------------