├── .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 | ![Target](http://htdt.github.io/amaze/media/screens/target.gif) 11 | ![Galaxy](http://htdt.github.io/amaze/media/screens/galaxy.gif) 12 | ![Head](http://htdt.github.io/amaze/media/screens/head.gif) 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 | --------------------------------------------------------------------------------