├── .gitignore ├── LICENSE ├── README.md ├── assets ├── logo.png └── screen.png └── src ├── glsl ├── default.fp ├── default.vp ├── func │ ├── cubicPulse.glsl │ ├── snoise.glsl │ └── wrap.glsl ├── magicFabric.fp ├── magicFabric.vp ├── magicGlitter.fp ├── magicGlitter.vp ├── magicPrism.fp ├── magicPrism.vp ├── magicRings.fp ├── magicRings.vp ├── magicSphere.fp ├── magicSphere.vp ├── splashLines.fp └── splashLines.vp ├── gulpfile.js ├── html ├── index.html ├── test-audio.html └── test.html ├── js ├── App.js ├── Camera.js ├── CameraControlVR.js ├── GuiHelper.js ├── Main.js ├── MathExt.js ├── Scene │ ├── AudioGen.js │ ├── Background.js │ ├── BounceSpheres.js │ ├── DebugArrows.js │ ├── DebugBlendTest.js │ ├── DebugBox.js │ ├── DebugPlane.js │ ├── DebugRoom.js │ ├── Director.js │ ├── IntroSplash.js │ ├── MagicEye.js │ ├── MagicFabric.js │ ├── MagicGlitter.js │ ├── MagicPrism.js │ ├── MagicRings.js │ ├── MagicSphere.js │ ├── Scene.js │ └── Signal.js ├── Settings.js ├── Shaders.js └── Textures.js ├── lib ├── CubemapToEquirectangular.js ├── NoSleep.min.js ├── StartAudioContext.js ├── Tone.min.js ├── jquery-3.1.1.min.js ├── three │ └── three.min.js └── webvr │ ├── VRControls.js │ ├── VREffect.js │ ├── webvr-polyfill.js │ └── webvr-ui.js ├── package.json ├── res ├── img │ ├── FinalBadge.png │ ├── Web-VR_Lockup-01.png │ ├── btn-360.png │ ├── btn-vr.png │ ├── checker.png │ ├── favicon.png │ ├── ftl-logo-white.png │ ├── funktronic-logo.png │ ├── info-quit.png │ ├── info_btn.png │ ├── logo.png │ ├── meta-promo.png │ ├── perlin.png │ └── prism-mask.png └── svg │ ├── FinalBadge.svg │ ├── FinalBadgeBlack.svg │ └── mlx_badge.svg ├── run.sh ├── scss ├── main.scss └── reset.scss └── setup.sh /.gitignore: -------------------------------------------------------------------------------- 1 | src/node_modules/* 2 | 3 | src/npm-debug.log 4 | public/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Eddie Lee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Chromatic](https://github.com/eddietree/chromatic/blob/master/assets/logo.png) 2 | 3 | [Chromatic VR](https://chromatic.funktroniclabs.com/) is an WebVR audiovisual experiment created using realtime shaders and generative audio. Watch iridescent shards of light unfurl into procedurally generated prismatic realms and meditative soundscapes. 4 | 5 | This project was developed using various web frameworks including [three.js](https://threejs.org/), an open source 3D library, and [Tone.js](https://tonejs.github.io/), a framework for creating interactive audio. 6 | 7 | Requires a VR HMD and [WebVR friendly browser](https://webvr.info/) in order to run properly. 8 | 9 | ![Chromatic](https://github.com/eddietree/chromatic/blob/master/assets/screen.png) 10 | 11 | ## Getting Started 12 | 13 | ### Check out the git repository 14 | 15 | Check out the [git repo](https://github.com/eddietree/chromatic/) 16 | 17 | `git clone https://github.com/eddietree/chromatic` 18 | 19 | ### How to run 20 | 21 | First, make sure you have [nodejs](https://nodejs.org/en/) installed on your machine. 22 | 23 | Then, 24 | * goto folder `src` 25 | * run the file `setup.sh` and it wil install and run the project 26 | 27 | 28 | ## Contributors 29 | - [Eddie Lee](https://www.eddietree.com/) (Design, Code) 30 | - [Tetra Von Orden](https://www.tetramid.co/) (Art Direction) 31 | - [Eric Cheng](https://soundcloud.com/eric_cheng/) (Sound Design) 32 | 33 | ## Technologies Used 34 | three.js, Tone.js, gulp, WebVR UI 35 | 36 | *As of 9/11/2018, the WebVR API is no longer supported so some features may not work as intended.* -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/assets/logo.png -------------------------------------------------------------------------------- /assets/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/assets/screen.png -------------------------------------------------------------------------------- /src/glsl/default.fp: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec3 vNormal; 4 | 5 | void main() { 6 | gl_FragColor = vec4(vNormal, 1.0); 7 | } -------------------------------------------------------------------------------- /src/glsl/default.vp: -------------------------------------------------------------------------------- 1 | //varying vec2 vUv; 2 | 3 | varying vec3 vNormal; 4 | 5 | void main() { 6 | 7 | vNormal = normal; 8 | 9 | //vUv = uv; 10 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 11 | } -------------------------------------------------------------------------------- /src/glsl/func/cubicPulse.glsl: -------------------------------------------------------------------------------- 1 | float cubicPulse( float center, float width, float val ) 2 | { 3 | val = abs(val - center); 4 | if( val>width ) return 0.0; 5 | val /= width; 6 | return 1.0 - val*val*(3.0-2.0*val); 7 | } 8 | #pragma glslify: export(cubicPulse) 9 | -------------------------------------------------------------------------------- /src/glsl/func/snoise.glsl: -------------------------------------------------------------------------------- 1 | vec4 mod289(vec4 x) { 2 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 3 | 4 | float mod289(float x) { 5 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 6 | 7 | vec4 permute(vec4 x) { 8 | return mod289(((x*34.0)+1.0)*x); 9 | } 10 | 11 | float permute(float x) { 12 | return mod289(((x*34.0)+1.0)*x); 13 | } 14 | 15 | vec4 taylorInvSqrt(vec4 r) 16 | { 17 | return 1.79284291400159 - 0.85373472095314 * r; 18 | } 19 | 20 | float taylorInvSqrt(float r) 21 | { 22 | return 1.79284291400159 - 0.85373472095314 * r; 23 | } 24 | 25 | vec4 grad4(float j, vec4 ip) 26 | { 27 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0); 28 | vec4 p,s; 29 | 30 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; 31 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz); 32 | s = vec4(lessThan(p, vec4(0.0))); 33 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; 34 | 35 | return p; 36 | } 37 | 38 | // (sqrt(5) - 1)/4 = F4, used once below 39 | #define F4 0.309016994374947451 40 | 41 | float snoise(vec4 v) 42 | { 43 | const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4 44 | 0.276393202250021, // 2 * G4 45 | 0.414589803375032, // 3 * G4 46 | -0.447213595499958); // -1 + 4 * G4 47 | 48 | // First corner 49 | vec4 i = floor(v + dot(v, vec4(F4)) ); 50 | vec4 x0 = v - i + dot(i, C.xxxx); 51 | 52 | // Other corners 53 | 54 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) 55 | vec4 i0; 56 | vec3 isX = step( x0.yzw, x0.xxx ); 57 | vec3 isYZ = step( x0.zww, x0.yyz ); 58 | // i0.x = dot( isX, vec3( 1.0 ) ); 59 | i0.x = isX.x + isX.y + isX.z; 60 | i0.yzw = 1.0 - isX; 61 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); 62 | i0.y += isYZ.x + isYZ.y; 63 | i0.zw += 1.0 - isYZ.xy; 64 | i0.z += isYZ.z; 65 | i0.w += 1.0 - isYZ.z; 66 | 67 | // i0 now contains the unique values 0,1,2,3 in each channel 68 | vec4 i3 = clamp( i0, 0.0, 1.0 ); 69 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 ); 70 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 ); 71 | 72 | // x0 = x0 - 0.0 + 0.0 * C.xxxx 73 | // x1 = x0 - i1 + 1.0 * C.xxxx 74 | // x2 = x0 - i2 + 2.0 * C.xxxx 75 | // x3 = x0 - i3 + 3.0 * C.xxxx 76 | // x4 = x0 - 1.0 + 4.0 * C.xxxx 77 | vec4 x1 = x0 - i1 + C.xxxx; 78 | vec4 x2 = x0 - i2 + C.yyyy; 79 | vec4 x3 = x0 - i3 + C.zzzz; 80 | vec4 x4 = x0 + C.wwww; 81 | 82 | // Permutations 83 | i = mod289(i); 84 | float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); 85 | vec4 j1 = permute( permute( permute( permute ( 86 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) 87 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) 88 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) 89 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); 90 | 91 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope 92 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289. 93 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; 94 | 95 | vec4 p0 = grad4(j0, ip); 96 | vec4 p1 = grad4(j1.x, ip); 97 | vec4 p2 = grad4(j1.y, ip); 98 | vec4 p3 = grad4(j1.z, ip); 99 | vec4 p4 = grad4(j1.w, ip); 100 | 101 | // Normalise gradients 102 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 103 | p0 *= norm.x; 104 | p1 *= norm.y; 105 | p2 *= norm.z; 106 | p3 *= norm.w; 107 | p4 *= taylorInvSqrt(dot(p4,p4)); 108 | 109 | // Mix contributions from the five corners 110 | vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); 111 | vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); 112 | m0 = m0 * m0; 113 | m1 = m1 * m1; 114 | return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) 115 | + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; 116 | 117 | } 118 | 119 | ///////////////////////////////////// 120 | 121 | 122 | #pragma glslify: export(snoise) 123 | //#pragma glslify: export(taylorInvSqrt) -------------------------------------------------------------------------------- /src/glsl/func/wrap.glsl: -------------------------------------------------------------------------------- 1 | float wrap(float val, float minVal, float maxVal) 2 | { 3 | val -= minVal; 4 | 5 | float delta = maxVal - minVal; 6 | if ( delta < 0.0001 ) return val; 7 | 8 | return val - (delta* floor(val/delta)) + minVal; 9 | } 10 | 11 | #pragma glslify: export(wrap) 12 | -------------------------------------------------------------------------------- /src/glsl/magicFabric.fp: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform sampler2D uTexRainbow; 4 | uniform float uColorize; 5 | uniform float uGlobalAlpha; 6 | 7 | varying vec4 vFreqData0; 8 | varying vec4 vFreqData1; 9 | varying vec4 vFreqData2; 10 | 11 | varying vec3 vNormal; 12 | varying vec3 vColor; 13 | varying vec4 vLightData; 14 | varying float vTime; 15 | varying float vRingPulse; 16 | varying vec4 vSoothingLightData; 17 | 18 | vec3 getRainbowColor(float val) { 19 | vec3 color = texture2D(uTexRainbow, vec2(val, 0.0)).xyz; 20 | return color; 21 | } 22 | 23 | void main() { 24 | 25 | float rainbowSpeed = 0.5; 26 | 27 | vec3 color = vColor; 28 | color += mix( getRainbowColor(vLightData.x * vFreqData0.z + vTime*rainbowSpeed + vLightData.z*0.2), vec3(1.0), vLightData.x) * vLightData.x * vFreqData0.w; // freq0 29 | color += mix( getRainbowColor(vLightData.y * vFreqData1.z + vTime*rainbowSpeed + vLightData.z*0.2), vec3(1.0), vLightData.y) * vLightData.y * vFreqData1.w; // freq0 30 | color += mix( getRainbowColor(vLightData.z * vFreqData2.z + vTime*rainbowSpeed + vLightData.z*0.2), vec3(1.0), vLightData.z) * vLightData.z * vFreqData2.w; // freq0 31 | 32 | //color += (getRainbowColor(vLightData.w * 250.0 + vTime*rainbowSpeed* 2.0)+vec3(vLightData.w)*0.2) * vLightData.w * 0.3; // soothing light 33 | //color += vLightData.w * vec3(0.25, 0.25, 0.55); // soothing light 34 | //color += vLightData.w * vec3(0.2, vNormal.x*0.5+0.5, vNormal.y*0.5+0.5)*0.6; // soothing light 35 | 36 | // pulse 37 | color += getRainbowColor(vRingPulse + vTime* 10.0) * vRingPulse * 0.3; 38 | 39 | // random color 40 | color += vLightData.xyz*uColorize; 41 | 42 | // soothing light 43 | color += vSoothingLightData.xyz * vSoothingLightData.w; 44 | 45 | gl_FragColor = vec4(color * uGlobalAlpha, 1.0) ; 46 | //gl_FragColor = vec4(color, 1.0) ; 47 | } -------------------------------------------------------------------------------- /src/glsl/magicFabric.vp: -------------------------------------------------------------------------------- 1 | uniform vec4 uFreq0; 2 | uniform vec4 uFreq1; 3 | uniform vec4 uFreq2; 4 | uniform vec4 uSoothingLight; 5 | uniform vec4 uSoothingColor; 6 | uniform vec4 uFolding; 7 | uniform float uTime; 8 | uniform float uEyeSharpness; 9 | uniform float uEyeDepth; 10 | uniform float uEyeCurveCoeff; 11 | uniform float uPulsePosZ; 12 | uniform float uBaseRadius; 13 | uniform vec3 uCamPos; 14 | uniform vec3 uCamFwdDir; 15 | uniform vec3 uBaseColor; 16 | 17 | varying vec4 vFreqData0; 18 | varying vec4 vFreqData1; 19 | varying vec4 vFreqData2; 20 | varying vec3 vNormal; 21 | varying vec3 vColor; 22 | varying vec4 vLightData; 23 | varying vec4 vSoothingLightData; 24 | varying float vTime; 25 | varying float vRingPulse; 26 | 27 | //#pragma glslify: snoise = require(./func/snoise.glsl) 28 | #pragma glslify: cubicPulse = require(./func/cubicPulse.glsl) 29 | 30 | 31 | void main() { 32 | 33 | float dotFwd = dot(uCamFwdDir,normal)*0.5+0.5; 34 | 35 | //float noise = snoise(vec4(normal*1.0, uTime*0.0003)); 36 | vec3 pos = position * uBaseRadius; 37 | pos += uCamFwdDir * pow(dotFwd,uEyeCurveCoeff) * uEyeDepth;// uTime *5.0; 38 | 39 | // wiggle 40 | pos += normal * sin(uTime*uFolding.z + pos.y*uFolding.x) * uFolding.y; 41 | 42 | 43 | float fallOff = pow(dotFwd, uEyeSharpness); 44 | //float fallOff = smoothstep(0.7, 1.0, dotFwd); 45 | float freq0 = (sin( position.z* uFreq0.x + uTime*uFreq0.y)*0.5+0.5) * fallOff; 46 | float freq1 = (cos( position.z*uFreq1.x + uTime*uFreq1.y)*0.5+0.5) * fallOff; 47 | float freq2 = (sin( pos.z*uFreq2.x + uTime*uFreq2.y)*0.5+0.5) * fallOff; 48 | 49 | float ringPulse = cubicPulse(uPulsePosZ, 2.0, pos.z); 50 | 51 | // soothing light 52 | float soothingFreq = uSoothingLight.x; 53 | float soothingTime = uSoothingLight.z; 54 | float soothingZFreq = uSoothingLight.w; 55 | float soothingLight = clamp(sin(pos.z*soothingFreq + soothingTime), 0.0, 1.0); 56 | 57 | vSoothingLightData = vec4((sin(vec3(normal.xyz *5000.0 + vec3(normal.z* soothingZFreq + uTime*6.0)))*0.5+0.5)*0.03+uSoothingColor.xyz, soothingLight); 58 | //vSoothingLightData = vec4(vec3(0.5, 0.5, 1.0)*0.6, soothingLight); 59 | vLightData = vec4(freq0, freq1, freq2, fallOff); 60 | vFreqData0 = uFreq0; 61 | vFreqData1 = uFreq1; 62 | vFreqData2 = uFreq2; 63 | 64 | vRingPulse = ringPulse; 65 | vTime = uTime; 66 | vNormal = normal; 67 | vColor = uBaseColor; 68 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 ); 69 | } 70 | -------------------------------------------------------------------------------- /src/glsl/magicGlitter.fp: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform sampler2D uTexRainbow; 4 | 5 | varying vec3 vColor; 6 | varying float vTime; 7 | varying vec3 vRandData; 8 | varying float vPulsateCoeff; 9 | 10 | void main() { 11 | 12 | vec2 uv = gl_PointCoord.st; 13 | vec2 uvNorm = uv*2.0 - vec2(1.0); 14 | 15 | float len = length(uvNorm); 16 | if(len > 1.0) 17 | discard; 18 | 19 | //vec3 color = 20 | float glistenPulsate = sin(vTime*mix(1.0, 3.0, vRandData.y) + vRandData.x * 100.0)*0.5+0.5; 21 | vec3 rainbowColor = texture2D(uTexRainbow, vec2(len * 1.0 - vTime*0.5 + vRandData.x, 0.0)).xyz;; 22 | 23 | vec3 color = mix(rainbowColor, vec3(1.0), pow(glistenPulsate,2.5)); 24 | color += vPulsateCoeff * 10.0; 25 | 26 | float alpha = glistenPulsate * smoothstep(0.1,1.5,1.0-len) * mix( 0.2, 0.8, vRandData.z); 27 | alpha *= smoothstep(0.0,(1.0-glistenPulsate*glistenPulsate),len); 28 | 29 | //alpha = 1.0; 30 | gl_FragColor = vec4(color, alpha); 31 | } -------------------------------------------------------------------------------- /src/glsl/magicGlitter.vp: -------------------------------------------------------------------------------- 1 | //varying vec2 vUv; 2 | 3 | uniform float uTime; 4 | uniform vec3 uCamPos; 5 | uniform float uPulsePosZ; 6 | 7 | varying vec3 vRandData; 8 | varying float vTime; 9 | varying float vPulsateCoeff; 10 | 11 | #pragma glslify: wrap = require(./func/wrap.glsl) 12 | #pragma glslify: cubicPulse = require(./func/cubicPulse.glsl) 13 | 14 | const float PI = 3.14159; 15 | const float TWICE_PI = PI * 2.0; 16 | 17 | void main() { 18 | 19 | //vNormal = normal; 20 | 21 | vec3 randaDatPos = position; 22 | vec3 randData = normal.xyz; 23 | 24 | const float zMin = -30.0; 25 | const float zMax = 3.0; 26 | const float posRadiusMin= 0.6; 27 | const float posRadiusMax = 10.0; 28 | const float ptSizeMin = 5.0; 29 | const float ptSizeMax = 40.0; 30 | 31 | float distCoeff = wrap(randaDatPos.z * 100.0 - uTime * 0.025 * mix(0.5, 1.0, randaDatPos.z), 0.0, 1.0); 32 | 33 | float posZ = mix(zMin, zMax, distCoeff); 34 | float angle = TWICE_PI *randaDatPos.y; 35 | float radius = mix(posRadiusMin, posRadiusMax, randaDatPos.x*randaDatPos.x*randaDatPos.x) * distCoeff; 36 | 37 | vec3 pos = vec3( cos(angle) * radius, sin(angle)* radius, posZ); 38 | 39 | // calc size 40 | float distToEye = length(pos - uCamPos); 41 | float pointSize = mix(ptSizeMin, ptSizeMax, randData.x * randData.x); 42 | float pulsateCoeff = cubicPulse(uPulsePosZ + 3.0, 3.0, pos.z); 43 | pointSize += pulsateCoeff * 70.0; 44 | 45 | gl_PointSize = pointSize / distToEye; 46 | vPulsateCoeff = pulsateCoeff; 47 | vTime = uTime; 48 | vRandData = randData; 49 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 ); 50 | } -------------------------------------------------------------------------------- /src/glsl/magicPrism.fp: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform sampler2D uTexRainbow; 4 | uniform sampler2D uTexPerlin; 5 | uniform sampler2D uTexMask; 6 | uniform float uGlobalAlpha; 7 | uniform vec4 uAlphaFadeDist; 8 | uniform vec4 uPerlinParams; 9 | uniform vec3 uCamPos; 10 | 11 | varying vec2 vUv; 12 | varying float vTime; 13 | varying vec3 vNormal; 14 | varying vec3 vPosBase; 15 | varying vec3 vPosWorld; 16 | varying float vNoise; 17 | varying vec4 vColorTint; 18 | varying vec4 vColorParams; 19 | 20 | vec3 getRainbowColor(float val) { 21 | vec3 color = texture2D(uTexRainbow, vec2(val, 0.0)).xyz; 22 | return color; 23 | } 24 | 25 | void main() { 26 | float rainbowFreq = vColorParams.x; 27 | float rainbowSpeed = vColorParams.y; 28 | float fadeFreq = vColorParams.z; 29 | float fadeSpeed = vColorParams.w; 30 | 31 | float perlin = texture2D(uTexPerlin, vec2(vUv.x*uPerlinParams.z, vTime*1.0)).x; 32 | 33 | vec3 color = getRainbowColor((vPosBase.y+vPosBase.z)*0.1 + vNoise*0.2 + perlin*uPerlinParams.x + vUv.x*rainbowFreq - vTime*rainbowSpeed ); 34 | color *= vColorTint.xyz; 35 | 36 | float distAlphaMin = uAlphaFadeDist.x*uAlphaFadeDist.x; 37 | float distAlphaMax = uAlphaFadeDist.y*uAlphaFadeDist.y; 38 | vec3 dirToCam = vPosWorld-uCamPos; 39 | float distToCamSqr = dot(dirToCam,dirToCam); 40 | 41 | // alpha 42 | float alpha = vColorTint.w * vColorTint.w; 43 | alpha -= perlin*uPerlinParams.y; 44 | alpha *= smoothstep(distAlphaMin, distAlphaMax, distToCamSqr); 45 | alpha *= uGlobalAlpha; 46 | 47 | gl_FragColor = vec4(color, alpha); 48 | } -------------------------------------------------------------------------------- /src/glsl/magicPrism.vp: -------------------------------------------------------------------------------- 1 | attribute vec4 center; 2 | attribute vec4 data; 3 | 4 | uniform float uTime; 5 | uniform float uPulsePosZ; 6 | uniform float uInfiniteScalar; 7 | uniform vec4 uSwayParams; 8 | uniform vec4 uColorTint; 9 | uniform vec4 uColorParams; 10 | uniform vec4 uTubeParams; 11 | 12 | varying vec2 vUv; 13 | varying vec3 vNormal; 14 | varying vec3 vPosBase; 15 | varying vec3 vPosWorld; 16 | varying float vTime; 17 | varying float vNoise; 18 | varying vec4 vColorTint; 19 | varying vec4 vColorParams; 20 | 21 | #pragma glslify: snoise = require(./func/snoise.glsl) 22 | //#pragma glslify: cubicPulse = require(./func/cubicPulse.glsl) 23 | 24 | vec3 rotate( vec3 pos, float angle) { 25 | float sinAngle = sin(angle); 26 | float cosAngle = cos(angle); 27 | 28 | float xNew = pos.x * cosAngle - pos.z * sinAngle; 29 | float zNew = pos.x * sinAngle + pos.z * cosAngle; 30 | 31 | return vec3(xNew, pos.y, zNew); 32 | } 33 | 34 | void main() { 35 | 36 | float freq = uSwayParams.x; 37 | float amp = uSwayParams.z; 38 | float ampHeight = uSwayParams.w; 39 | float tubeRadius = uTubeParams.x; 40 | float tubeSpread = uTubeParams.y; 41 | float tubeYHeight = uTubeParams.z; 42 | float tubeYOffset = uTubeParams.w; 43 | 44 | float noise = snoise(vec4((position)*freq, uTime)); 45 | 46 | // position 47 | vec3 pos = position * vec3(tubeRadius, 1.0, tubeRadius); 48 | pos += (normal+vec3(0.0, ampHeight, 0.0))* (1.0-smoothstep(1.0, 1.5, uv.y)) * noise*amp; 49 | //pos += data.yzw * noise*amp; 50 | pos.y = pos.y * tubeYHeight + tubeYOffset; 51 | pos = rotate(pos, center.w); 52 | pos += center.xyz * tubeSpread; 53 | 54 | // pull to infinity 55 | pos.y *= (1.0 + uInfiniteScalar * step(0.0, pos.y)); 56 | 57 | vec3 posWorld = (modelMatrix * vec4( pos, 1.0 )).xyz; 58 | 59 | // color 60 | vec3 colorTint = uColorTint.xyz; 61 | colorTint.xyz += sin(data.w*100.0 + uTime*20.0); 62 | 63 | // alpha fade 64 | float fadeFreq = uColorParams.z; 65 | float fadeSpeed = uColorParams.w; 66 | float alphaFade = sin(fadeFreq*position.y + fadeSpeed * uTime)*0.5+0.5; 67 | //float ringPulse = cubicPulse(uPulsePosZ, 10.0, posWorld.z); 68 | //colorTint.xyz += ringPulse * 100.0; 69 | 70 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 ); 71 | gl_PointSize = 2.0; 72 | vPosBase = position; 73 | vTime = uTime; 74 | vNormal = normal; 75 | vNoise = noise; 76 | vPosWorld = posWorld; 77 | vUv = uv; 78 | vColorTint = vec4(colorTint,alphaFade); 79 | vColorParams = uColorParams; 80 | } -------------------------------------------------------------------------------- /src/glsl/magicRings.fp: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec3 vColor; 4 | 5 | void main() { 6 | gl_FragColor = vec4(vColor, 0.4); 7 | } -------------------------------------------------------------------------------- /src/glsl/magicRings.vp: -------------------------------------------------------------------------------- 1 | 2 | uniform float uTime; 3 | 4 | varying vec3 vColor; 5 | 6 | const float PI = 3.14159; 7 | const float TWICE_PI = PI * 2.0; 8 | 9 | void main() { 10 | 11 | vec3 ringData = position.xyz; 12 | 13 | float iRing = ringData.x; // ring index, normalized 14 | float iLocal = ringData.y; // local index within ring, 15 | float iGlobal = ringData.z; 16 | 17 | float angle = iGlobal * 30.0 * TWICE_PI;// + sin(iLocal + uTime*0.0002); 18 | float radius = 1.0;// + cos(iGlobal * mix(5000.0,5000.0, sin(uTime*0.00001+1.0)*0.5+0.5) + uTime*0.002) + iGlobal; 19 | 20 | float x = cos(angle) * radius; 21 | float y = sin(angle) * radius; 22 | float z = -10.0 + sin(1.0 + uTime*0.0005 + iGlobal * 6.0) * 10.0; 23 | 24 | vec3 pos = vec3(x,y,z); 25 | 26 | //vColor = vec3(sin(uTime*0.001 + iGlobal*3.0), cos(iGlobal*10.0 + uTime*0.0015), 1.0); 27 | vColor = vec3(1.0); 28 | 29 | //vUv = uv; 30 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 ); 31 | } -------------------------------------------------------------------------------- /src/glsl/magicSphere.fp: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform float uTime; 4 | uniform vec3 uCamPos; 5 | 6 | 7 | float sdPlane( vec3 p ) 8 | { 9 | return p.y; 10 | } 11 | 12 | float sdSphere( vec3 p, float s ) 13 | { 14 | return length(p)-s; 15 | } 16 | 17 | float sdBox( vec3 p, vec3 b ) 18 | { 19 | vec3 d = abs(p) - b; 20 | return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0)); 21 | } 22 | 23 | float sdEllipsoid( in vec3 p, in vec3 r ) 24 | { 25 | return (length( p/r ) - 1.0) * min(min(r.x,r.y),r.z); 26 | } 27 | 28 | float udRoundBox( vec3 p, vec3 b, float r ) 29 | { 30 | return length(max(abs(p)-b,0.0))-r; 31 | } 32 | 33 | float sdTorus( vec3 p, vec2 t ) 34 | { 35 | return length( vec2(length(p.xz)-t.x,p.y) )-t.y; 36 | } 37 | 38 | float sdHexPrism( vec3 p, vec2 h ) 39 | { 40 | vec3 q = abs(p); 41 | #if 0 42 | return max(q.z-h.y,max((q.x*0.866025+q.y*0.5),q.y)-h.x); 43 | #else 44 | float d1 = q.z-h.y; 45 | float d2 = max((q.x*0.866025+q.y*0.5),q.y)-h.x; 46 | return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); 47 | #endif 48 | } 49 | 50 | float sdCapsule( vec3 p, vec3 a, vec3 b, float r ) 51 | { 52 | vec3 pa = p-a, ba = b-a; 53 | float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); 54 | return length( pa - ba*h ) - r; 55 | } 56 | 57 | float sdTriPrism( vec3 p, vec2 h ) 58 | { 59 | vec3 q = abs(p); 60 | #if 0 61 | return max(q.z-h.y,max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5); 62 | #else 63 | float d1 = q.z-h.y; 64 | float d2 = max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5; 65 | return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); 66 | #endif 67 | } 68 | 69 | float sdCylinder( vec3 p, vec2 h ) 70 | { 71 | vec2 d = abs(vec2(length(p.xz),p.y)) - h; 72 | return min(max(d.x,d.y),0.0) + length(max(d,0.0)); 73 | } 74 | 75 | float sdCone( in vec3 p, in vec3 c ) 76 | { 77 | vec2 q = vec2( length(p.xz), p.y ); 78 | float d1 = -q.y-c.z; 79 | float d2 = max( dot(q,c.xy), q.y); 80 | return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); 81 | } 82 | 83 | float sdConeSection( in vec3 p, in float h, in float r1, in float r2 ) 84 | { 85 | float d1 = -p.y - h; 86 | float q = p.y - h; 87 | float si = 0.5*(r1-r2)/h; 88 | float d2 = max( sqrt( dot(p.xz,p.xz)*(1.0-si*si)) + q*si - r2, q ); 89 | return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); 90 | } 91 | 92 | 93 | float length2( vec2 p ) 94 | { 95 | return sqrt( p.x*p.x + p.y*p.y ); 96 | } 97 | 98 | float length6( vec2 p ) 99 | { 100 | p = p*p*p; p = p*p; 101 | return pow( p.x + p.y, 1.0/6.0 ); 102 | } 103 | 104 | float length8( vec2 p ) 105 | { 106 | p = p*p; p = p*p; p = p*p; 107 | return pow( p.x + p.y, 1.0/8.0 ); 108 | } 109 | 110 | float sdTorus82( vec3 p, vec2 t ) 111 | { 112 | vec2 q = vec2(length2(p.xz)-t.x,p.y); 113 | return length8(q)-t.y; 114 | } 115 | 116 | float sdTorus88( vec3 p, vec2 t ) 117 | { 118 | vec2 q = vec2(length8(p.xz)-t.x,p.y); 119 | return length8(q)-t.y; 120 | } 121 | 122 | float sdCylinder6( vec3 p, vec2 h ) 123 | { 124 | return max( length6(p.xz)-h.x, abs(p.y)-h.y ); 125 | } 126 | 127 | //---------------------------------------------------------------------- 128 | 129 | float opS( float d1, float d2 ) 130 | { 131 | return max(-d2,d1); 132 | } 133 | 134 | vec2 opS( vec2 d1, vec2 d2 ) 135 | { 136 | return vec2(opS(d1.x,d2.x), min(d1.y,d2.y)); 137 | } 138 | 139 | vec2 opU( vec2 d1, vec2 d2 ) 140 | { 141 | return (d1.xtmax ) break; 181 | t += res.x; 182 | m = res.y; 183 | } 184 | 185 | if( t>tmax ) m=-1.0; 186 | return vec2( t, m ); 187 | } 188 | 189 | 190 | float softshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax ) 191 | { 192 | float res = 1.0; 193 | float t = mint; 194 | for( int i=0; i<16; i++ ) 195 | { 196 | float h = map( ro + rd*t ).x; 197 | res = min( res, 8.0*h/t ); 198 | t += clamp( h, 0.02, 0.10 ); 199 | if( h<0.001 || t>tmax ) break; 200 | } 201 | return clamp( res, 0.0, 1.0 ); 202 | 203 | } 204 | 205 | vec3 calcNormal( in vec3 pos ) 206 | { 207 | vec3 eps = vec3( 0.001, 0.0, 0.0 ); 208 | vec3 nor = vec3( 209 | map(pos+eps.xyy).x - map(pos-eps.xyy).x, 210 | map(pos+eps.yxy).x - map(pos-eps.yxy).x, 211 | map(pos+eps.yyx).x - map(pos-eps.yyx).x ); 212 | return normalize(nor); 213 | } 214 | 215 | float calcAO( in vec3 pos, in vec3 nor ) 216 | { 217 | float occ = 0.0; 218 | float sca = 1.0; 219 | for( int i=0; i<5; i++ ) 220 | { 221 | float hr = 0.01 + 0.12*float(i)/4.0; 222 | vec3 aopos = nor * hr + pos; 223 | float dd = map( aopos ).x; 224 | occ += -(dd-hr)*sca; 225 | sca *= 0.95; 226 | } 227 | return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ); 228 | } 229 | 230 | 231 | vec3 render( in vec3 ro, in vec3 rd ) 232 | { 233 | vec3 col = vec3(0.6, 0.8, 0.9) + rd.y*0.8; 234 | vec2 res = castRay(ro,rd); 235 | float t = res.x; 236 | float m = res.y; 237 | 238 | if( m>-0.5 ) 239 | { 240 | vec3 pos = ro + t*rd; 241 | vec3 nor = calcNormal( pos ); 242 | //vec3 ref = reflect( rd, nor ); 243 | 244 | col = nor * 0.5 + vec3(0.5); 245 | 246 | float occ = calcAO( pos, nor ); 247 | col *= mix(0.5, 1.0,occ); 248 | 249 | /*// material 250 | col = 0.45 + 0.3*sin( vec3(0.05,0.08,0.10)*(m-1.0) ); 251 | 252 | if( m<1.5 ) 253 | { 254 | 255 | float f = mod( floor(5.0*pos.z) + floor(5.0*pos.x), 2.0); 256 | col = 0.4 + 0.1*f*vec3(1.0); 257 | } 258 | 259 | // lighitng 260 | float occ = calcAO( pos, nor ); 261 | vec3 lig = normalize( vec3(-0.6, 0.7, -0.5) ); 262 | float amb = clamp( 0.5+0.5*nor.y, 0.0, 1.0 ); 263 | float dif = clamp( dot( nor, lig ), 0.0, 1.0 ); 264 | float bac = clamp( dot( nor, normalize(vec3(-lig.x,0.0,-lig.z))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0); 265 | float dom = smoothstep( -0.1, 0.1, ref.y ); 266 | float fre = pow( clamp(1.0+dot(nor,rd),0.0,1.0), 2.0 ); 267 | float spe = pow(clamp( dot( ref, lig ), 0.0, 1.0 ),16.0); 268 | 269 | dif *= softshadow( pos, lig, 0.02, 2.5 ); 270 | dom *= softshadow( pos, ref, 0.02, 2.5 ); 271 | 272 | vec3 lin = vec3(0.0); 273 | lin += 1.20*dif*vec3(1.00,0.85,0.55); 274 | lin += 1.20*spe*vec3(1.00,0.85,0.55)*dif; 275 | lin += 0.20*amb*vec3(0.50,0.70,1.00)*occ; 276 | lin += 0.30*dom*vec3(0.50,0.70,1.00)*occ; 277 | lin += 0.30*bac*vec3(0.25,0.25,0.25)*occ; 278 | lin += 0.40*fre*vec3(1.00,1.00,1.00)*occ; 279 | col = col*lin; 280 | 281 | col = mix( col, vec3(0.8,0.9,1.0), 1.0-exp( -0.002*t*t ) );*/ 282 | 283 | } 284 | 285 | return vec3( clamp(col,0.0,1.0) ); 286 | } 287 | 288 | 289 | varying vec3 vPosWorld; 290 | varying vec3 vNormal; 291 | 292 | void main() { 293 | 294 | vec3 ro = uCamPos; 295 | vec3 rd = normalize(vPosWorld-ro); 296 | 297 | vec3 color = render(ro, rd); 298 | 299 | gl_FragColor = vec4(color, 1.0); 300 | } -------------------------------------------------------------------------------- /src/glsl/magicSphere.vp: -------------------------------------------------------------------------------- 1 | //varying vec2 vUv; 2 | 3 | varying vec3 vNormal; 4 | varying vec3 vPosWorld; 5 | 6 | void main() { 7 | 8 | vNormal = normal; 9 | vPosWorld = (modelMatrix * vec4(position, 1.0)).xyz; 10 | 11 | //vUv = uv; 12 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 13 | } -------------------------------------------------------------------------------- /src/glsl/splashLines.fp: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec3 vNormal; 4 | varying vec4 vColor; 5 | 6 | void main() { 7 | gl_FragColor = vColor; 8 | } -------------------------------------------------------------------------------- /src/glsl/splashLines.vp: -------------------------------------------------------------------------------- 1 | //varying vec2 vUv; 2 | 3 | varying vec3 vNormal; 4 | varying vec4 vColor; 5 | 6 | uniform float uTime; 7 | uniform float uFadeCoeff; 8 | #pragma glslify: cubicPulse = require(./func/cubicPulse.glsl) 9 | 10 | void main() { 11 | 12 | vNormal = normal; 13 | 14 | float r = mix( 0.1, 0.8, cos(uTime*1.0 + (uv.x+uv.y)*7.0)*0.5+0.5); 15 | float g = mix( 0.3, 0.6, sin(uTime*2.0 + uv.x*5.0)*0.5+0.5); 16 | float b = mix( 0.5, 1.0, cos(-uTime*2.0 + (uv.x-uv.y)*6.0)*0.5+0.5); 17 | vec3 color = vec3(r,g,b); 18 | float alpha = pow(1.0-uFadeCoeff,1.5); 19 | 20 | vColor.xyz = color; 21 | vColor.w = alpha; 22 | 23 | //vColor.xyz = vec3(uv, 0.0); 24 | float yNorm = uv.y *2.0 - 1.0; 25 | 26 | vec3 pos = position; 27 | 28 | float waveCoeff = sin(uTime + 20.0*position.x)*0.5+0.5; 29 | 30 | float pulseCenter = 0.5; 31 | float pulseWidth = 1.0-uFadeCoeff; 32 | float pulse = cubicPulse(pulseCenter, pulseWidth, uv.x); 33 | pos.y *= mix(0.9, 1.0, waveCoeff); 34 | pos.y *= mix(1.0, pulse, uFadeCoeff); 35 | pos.z += mix(0.0, sin(uTime*2.0 + 100.0*position.x+90.0*position.y)*0.5 *pow(pulse,20.0), uFadeCoeff); 36 | 37 | pos.xy = mix(pos.xy, vec2(0.0), pow(uFadeCoeff, 1.5 + sin(uTime*3.0 + position.x*300.0 + position.y*2555.0))); 38 | 39 | //vUv = uv; 40 | gl_PointSize = mix(3.0, 6.0, waveCoeff); 41 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 ); 42 | } -------------------------------------------------------------------------------- /src/gulpfile.js: -------------------------------------------------------------------------------- 1 | var env = 'debug'; // 'production' 2 | 3 | var gulp = require('gulp'), 4 | jshint = require('gulp-jshint'), 5 | sass = require('gulp-sass'), 6 | gutil = require('gulp-util'), 7 | //less = require('gulp-less'), 8 | concat = require('gulp-concat'), 9 | uglify = require('gulp-uglify'), 10 | rename = require('gulp-rename'), 11 | browserSync = require('browser-sync'), 12 | runSequence = require('run-sequence'), 13 | clean = require('gulp-clean'), 14 | browserify = require('browserify'), 15 | notify = require("gulp-notify"), 16 | source = require("vinyl-source-stream"), 17 | buffer = require('vinyl-buffer'), 18 | gulpif = require('gulp-if'), 19 | uglify = require('gulp-uglify'), 20 | glslify = require('glslify'), 21 | babelify = require('babelify'); 22 | 23 | var settings = require('./js/Settings.js'); 24 | 25 | var reload = browserSync.reload; 26 | 27 | var pathsSrc = { 28 | html: 'html/*', 29 | lib: 'lib/**/**/*', 30 | js: 'js/', 31 | glsl: 'glsl/*', 32 | scss: 'scss/main.scss', 33 | res: 'res/**/*.*', 34 | }; 35 | 36 | var pathDstRoot = '../public/'; 37 | var pathsDst = { 38 | root: pathDstRoot, 39 | html: pathDstRoot, 40 | lib: pathDstRoot + 'lib/', 41 | js: pathDstRoot + 'js/', 42 | scss: pathDstRoot + 'css/', 43 | res: pathDstRoot + 'res/', 44 | }; 45 | 46 | var notifyMsg = function(msg, title) 47 | { 48 | gulp.src("").pipe(notify({message:msg, title:title || "Zen Shader"})); 49 | } 50 | 51 | var onError = function (err) { 52 | gutil.log(err.toString()); 53 | this.emit("end"); 54 | }; 55 | 56 | var onErrorFunc = function(title) { 57 | return notify.onError({ 58 | message: "Error: <%= error.message %>", 59 | title: "Error: " + title 60 | }); 61 | }; 62 | 63 | var onReloadWebsite = function(){ 64 | reload(); 65 | gutil.log("-------------------------------------------"); 66 | }; 67 | 68 | //////////////////////////////////////////////// 69 | 70 | gulp.task('clean', function() { 71 | return gulp.src(pathsDst.root, { 72 | read: false 73 | }) 74 | .pipe(clean({force: true})); 75 | }); 76 | 77 | gulp.task('build-html', function() { 78 | return gulp.src(pathsSrc.html).pipe(gulp.dest(pathsDst.html)); 79 | }); 80 | 81 | gulp.task('build-scss', function() { 82 | return gulp.src(pathsSrc.scss) 83 | .pipe(sass().on('error', onErrorFunc("SCSS Compile Error"))) 84 | .pipe(gulp.dest(pathsDst.scss)); 85 | }); 86 | 87 | gulp.task('build-lib', function() { 88 | return gulp.src(pathsSrc.lib) 89 | .pipe(gulp.dest(pathsDst.lib)); 90 | }); 91 | 92 | gulp.task('build-res', function() { 93 | return gulp.src(pathsSrc.res) 94 | .pipe(gulp.dest(pathsDst.res)); 95 | }); 96 | 97 | gulp.task('build-js', function() { 98 | 99 | var b = browserify({debug:settings.debug, insertGlobals: true}); 100 | b.transform(babelify, {presets: ["es2015"]} ) 101 | b.transform(glslify) 102 | b.add(pathsSrc.js + "Main.js"); 103 | 104 | return b.bundle() 105 | .on('error', onErrorFunc("Browserify Compile Error")) 106 | .pipe(source("Main.js")) 107 | .pipe(buffer()) 108 | .pipe(gulpif(!settings.debug, uglify())) 109 | .pipe(gulp.dest(pathsDst.js )); 110 | }); 111 | 112 | gulp.task('lint', function() { 113 | return gulp.src(pathsSrc.js + "*.js") 114 | .pipe(jshint()) 115 | .pipe(jshint.reporter('default')); 116 | }); 117 | 118 | //////////////////////////////////////////////// 119 | 120 | gulp.task('serve', function() { 121 | 122 | browserSync({ 123 | server: {baseDir: pathDstRoot}, 124 | //https: true 125 | }); 126 | 127 | gulp.watch(pathsSrc.html, ['build-html', onReloadWebsite]); 128 | gulp.watch(pathsSrc.scss, function() {runSequence('build-scss', 'build-html', onReloadWebsite); }); 129 | gulp.watch(pathsSrc.res, function() {runSequence('build-res', onReloadWebsite); }); 130 | gulp.watch(pathsSrc.lib, function() {runSequence('build-lib', 'build-html', onReloadWebsite); }); 131 | gulp.watch([pathsSrc.js+"*", pathsSrc.js+"*/*", pathsSrc.glsl], function() {runSequence('build-js', onReloadWebsite); }); 132 | }); 133 | 134 | gulp.task('default', function(callback) { 135 | 136 | runSequence( 137 | 'clean', 138 | ['build-res', 'build-lib', 'build-scss', 'build-js'], 139 | 'build-html', 140 | 'serve', 141 | callback 142 | ); 143 | }); -------------------------------------------------------------------------------- /src/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Chromatic (VR) 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 49 | 50 |
51 |
52 | 53 | 54 |
55 | 56 |
57 |
58 | 59 |
60 | 61 | 62 | 63 |
64 | 65 |
66 |
67 | 68 | 69 | 70 |
71 | 72 |
73 |

Chromatic

74 | 75 |

Chromatic VR is an WebVR audiovisual experiment created using realtime shaders and generative audio. Watch iridescent shards of light unfurl into procedurally generated prismatic realms and meditative soundscapes.

76 | 77 |

(Audio optimized for Android and desktop, disabled on iOS)

78 | 79 |

Contributors: Eddie Lee, Tetra Von Orden, Eric Cheng

80 |

Technologies: three.js, Tone.js, webvr-ui 81 | 82 |
83 | Source: Github. See other WebVR Experiments

84 |
85 |
86 | 87 | 88 |
89 | 90 |
91 | 92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
101 | 102 |
103 |

A WebVR experiment created with realtime shaders and generative audio.

104 | 105 |
106 |
107 | 108 |
Try without a headset
109 |
110 |
111 |
112 |
113 | 114 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /src/html/test-audio.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | VR Test 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | 30 |
31 | Start Audio 32 |
33 | 34 | 35 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/html/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | VR Test 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 35 | 36 | 37 | 38 |
39 |
40 | 41 | 42 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/js/App.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require('tween.js'); 2 | var settings = require('./Settings.js'); 3 | var dat = require('dat-gui'); 4 | 5 | console.log("Shader Vibes " + settings.version); 6 | 7 | // Setup three.js WebGL renderer. Note: Antialiasing is a big performance hit. 8 | // Only enable it if you actually need to. 9 | var renderer = new THREE.WebGLRenderer({ 10 | antialias: settings.antialias 11 | }); 12 | renderer.setPixelRatio(Math.floor(window.devicePixelRatio)); 13 | renderer.setClearColor(settings.clearColor); 14 | document.body.appendChild(renderer.domElement); 15 | 16 | // Apply VR stereo rendering to renderer. 17 | var effect = new THREE.VREffect(renderer); 18 | effect.setSize(window.innerWidth, window.innerHeight); 19 | 20 | var equiManaged = new CubemapToEquirectangular( renderer, true ); 21 | 22 | var scene; 23 | var camera; 24 | var controls; 25 | 26 | var gui = null; 27 | var guiParams = {}; 28 | 29 | var fpsClock = new THREE.Clock(); 30 | var fpsFrameTime = 0.0; 31 | 32 | window.oncontextmenu = function(event) { 33 | event.preventDefault(); 34 | event.stopPropagation(); 35 | return false; 36 | }; 37 | 38 | var noSleep = new NoSleep(); 39 | 40 | function enableNoSleep() { 41 | noSleep.enable(); 42 | document.removeEventListener('touchstart', enableNoSleep, false); 43 | } 44 | 45 | document.querySelector('#infobtn').addEventListener('click', function() { 46 | document.getElementById("infowall").style.display = "block"; 47 | }); 48 | 49 | document.querySelector('#infobtn-quit').addEventListener('click', function() { 50 | document.getElementById("infowall").style.display = "none"; 51 | }); 52 | 53 | // Enable wake lock. 54 | // (must be wrapped in a user input event handler e.g. a mouse or touch handler) 55 | document.addEventListener('touchstart', enableNoSleep, false); 56 | 57 | module.exports = { 58 | renderer: renderer, 59 | 60 | init: function() { 61 | 62 | module.exports.initGui(); 63 | 64 | scene = require('./Scene/Scene.js'); 65 | camera = require('./Camera.js'); 66 | controls = require('./CameraControlVR.js'); 67 | 68 | camera.init(); 69 | controls.init(); 70 | require('./Textures.js').init(); 71 | scene.init(); 72 | 73 | module.exports.onResize(); 74 | window.addEventListener('resize', module.exports.onResize); 75 | 76 | var isIOS = module.exports.getMobileOperatingSystem() === "iOS"; 77 | if(isIOS) 78 | { 79 | window.onorientationchange = function() { setTimeout(function(){ 80 | module.exports.onResize(); 81 | 82 | //document.getElementById("infowall").style.display = document.getElementById("infowall").style.display ==="block"? "none": "block"; 83 | }, 300); }; 84 | } 85 | }, 86 | 87 | getMobileOperatingSystem:function() { 88 | 89 | var userAgent = navigator.userAgent || navigator.vendor || window.opera; 90 | 91 | // Windows Phone must come first because its UA also contains "Android" 92 | if (/windows phone/i.test(userAgent)) { 93 | return "Windows Phone"; 94 | } 95 | 96 | if (/android/i.test(userAgent)) { 97 | return "Android"; 98 | } 99 | 100 | // iOS detection from: http://stackoverflow.com/a/9039885/177710 101 | if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) { 102 | return "iOS"; 103 | } 104 | 105 | return "unknown"; 106 | }, 107 | 108 | initGui: function() { 109 | 110 | if (!settings.showGui) { 111 | return; 112 | } 113 | 114 | gui = new dat.GUI(); 115 | //gui.close(); 116 | 117 | var appGuiFolder = gui.addFolder("App"); 118 | appGuiFolder.add(settings, 'debug'); 119 | appGuiFolder.add(settings, 'cameraFOV', 10, 180).onChange((value) => { camera.obj.fov = settings.cameraFOV; camera.obj.updateProjectionMatrix(); }); 120 | appGuiFolder.addColor(settings, 'clearColor'); 121 | }, 122 | 123 | addGuiItem: function(funcName, func, param0, param1) { 124 | if( gui === null ) { 125 | return; 126 | } 127 | 128 | guiParams[funcName] = func; 129 | return gui.add(guiParams, funcName, param0, param1); 130 | }, 131 | 132 | addGuiFolder: function(name) { 133 | if( gui === null ) { 134 | return null; 135 | } 136 | 137 | return gui.addFolder(name); 138 | }, 139 | 140 | start: function() { 141 | scene.start(); 142 | 143 | function animate(timestamp) { 144 | effect.requestAnimationFrame(animate); 145 | 146 | module.exports.update(); 147 | module.exports.draw(); 148 | } 149 | 150 | effect.requestAnimationFrame(animate); 151 | }, 152 | 153 | onResize: function() { 154 | var width = window.innerWidth; 155 | var height = window.innerHeight; 156 | 157 | var isIOS = module.exports.getMobileOperatingSystem() === "iOS"; 158 | if(isIOS) 159 | { 160 | //width = screen.width; 161 | //height = screen.height; 162 | } 163 | 164 | console.log('Resizing to %s x %s.', width, height); 165 | effect.setSize(width, height); 166 | camera.obj.aspect = width / height; 167 | camera.obj.updateProjectionMatrix(); 168 | //camera.obj.lookAt(new THREE.Vector3(0.0, 0.0, -1.0)); 169 | }, 170 | 171 | onFullscreen: function() { 172 | 173 | var el = renderer.domElement; 174 | 175 | if (el.requestFullscreen) { 176 | el.requestFullscreen(); 177 | } else if (el.mozRequestFullScreen) { 178 | el.mozRequestFullScreen(); 179 | } else if (el.webkitRequestFullscreen) { 180 | el.webkitRequestFullscreen(); 181 | } else if (el.msRequestFullscreen) { 182 | el.msRequestFullscreen(); 183 | } 184 | }, 185 | 186 | getFrameTime: function() { 187 | return fpsFrameTime; 188 | }, 189 | 190 | update: function() { 191 | controls.update(); 192 | TWEEN.update(); 193 | 194 | fpsFrameTime = fpsClock.getDelta(); 195 | }, 196 | 197 | draw: function() { 198 | renderer.setClearColor(settings.clearColor); 199 | effect.render(scene.obj, camera.obj); 200 | }, 201 | 202 | captureCubeMap: function() { 203 | equiManaged.update( camera.obj, scene.obj ); 204 | } 205 | } -------------------------------------------------------------------------------- /src/js/Camera.js: -------------------------------------------------------------------------------- 1 | var settings = require('./Settings.js'); 2 | 3 | var camera = new THREE.PerspectiveCamera(settings.cameraFOV, window.innerWidth / window.innerHeight, 0.1, 10000); 4 | 5 | module.exports = { 6 | obj: camera, 7 | 8 | init: function() { 9 | }, 10 | 11 | update: function() { 12 | } 13 | }; -------------------------------------------------------------------------------- /src/js/CameraControlVR.js: -------------------------------------------------------------------------------- 1 | var animitter = require('animitter'); 2 | var camera = require('./Camera.js'); 3 | var settings = require('./Settings.js'); 4 | var guihelper = require('./GuiHelper.js'); 5 | var controls = new THREE.VRControls(camera.obj); 6 | var Noise = require('noisejs'); 7 | 8 | var vrDisplay = null; 9 | var useVRCam = false; 10 | 11 | document.getElementById("buttons").style.display = settings.showGui ? "block" : "none"; 12 | 13 | function onEnterFocus() { 14 | doResetPoseVR(); 15 | var director = require('./Scene/Director.js'); 16 | director.beginUnlockSeq(); 17 | 18 | let sceneObjs = require('./Scene/Scene.js').sceneObjs; 19 | sceneObjs['IntroSplash'].doFadeAwaySeq(); 20 | 21 | useVRCam = true; 22 | }; 23 | 24 | function onExitFocus() { 25 | doResetPoseVR(); 26 | 27 | var director = require('./Scene/Director.js'); 28 | director.restartToBeginning(); 29 | 30 | let sceneObjs = require('./Scene/Scene.js').sceneObjs; 31 | sceneObjs['IntroSplash'].doFadeInSeq(); 32 | 33 | useVRCam = false; 34 | }; 35 | 36 | function onConnectHMD(hmd) { 37 | 38 | function doVRRequestPresent() { 39 | 40 | if (vrDisplay.isPresenting || (vrDisplay.capabilities.canPresent == false)) { 41 | console.log("vrDisplay - Unable to present!"); 42 | return; 43 | } 44 | 45 | // request VR 46 | var renderer = require('./App.js').renderer; 47 | vrDisplay.requestPresent([{ 48 | source: renderer.domElement 49 | }]); 50 | 51 | console.log("doVRRequestPresent"); 52 | } 53 | 54 | function doVRExitPresent() { 55 | 56 | // No sense in exiting presentation if we're not actually presenting. 57 | // (This may happen if we get an event like vrdisplaydeactivate when 58 | // we weren't presenting.) 59 | if (!vrDisplay.isPresenting) 60 | return; 61 | 62 | vrDisplay.exitPresent().then(function() { 63 | console.log("doVRExitPresent"); 64 | }, function() { 65 | console.log("ERROR: doVRExitPresent"); 66 | }); 67 | } 68 | 69 | function doVRTogglePresent() { 70 | var director = require('./Scene/Director.js'); 71 | director.beginUnlockSeq(); 72 | 73 | if (vrDisplay.isPresenting) { 74 | doVRExitPresent(); 75 | } else { 76 | doVRRequestPresent(); 77 | } 78 | } 79 | 80 | console.log(vrDisplay); 81 | 82 | // auto put on? 83 | //window.addEventListener('vrdisplayactivate', doVRRequestPresent, false); 84 | //window.addEventListener('vrdisplaydeactivate', doVRExitPresent, false); 85 | 86 | let app = require('./App.js'); 87 | app.addGuiItem('Toggle VR Mode', doVRTogglePresent); 88 | 89 | window.addEventListener('keypress', function(key) { 90 | if (key.code === "Space" || key.code === "Enter") { 91 | 92 | if (vrDisplay.capabilities.canPresent) { 93 | doVRTogglePresent(); 94 | } else { 95 | require('./App.js').onFullscreen(); 96 | } 97 | } 98 | }, false); 99 | } 100 | 101 | function initStatsVR() { 102 | 103 | let elemStats = document.getElementById("stats"); 104 | let app = require('./App.js'); 105 | 106 | function DebugPrintVectorLine(title, data) { 107 | elemStats.innerHTML += "" + title + ": ("; 108 | 109 | for (let i = 0; i < data.length; i += 1) { 110 | elemStats.innerHTML += data[i].toFixed(3); 111 | 112 | if (i < data.length - 1) { 113 | elemStats.innerHTML += ", "; 114 | } 115 | } 116 | elemStats.innerHTML += ")
"; 117 | } 118 | 119 | let loop = animitter(function(deltaTime, elapsedTime, frameCount) { 120 | 121 | elemStats.innerHTML = ""; 122 | 123 | // display vr stats 124 | if (vrDisplay !== null && typeof VRFrameData !== 'undefined') { 125 | let frameData = new VRFrameData(); 126 | vrDisplay.getFrameData(frameData); 127 | 128 | var pose = frameData.pose; 129 | elemStats.innerHTML += "HMD: " + vrDisplay.displayName + "
"; 130 | //elemStats.innerHTML += "isConnected: " + vrDisplay.isConnected + "
"; 131 | elemStats.innerHTML += "isPresenting: " + vrDisplay.isPresenting + "
"; 132 | elemStats.innerHTML += "timeStamp: " + frameData.timestamp + "
"; 133 | 134 | DebugPrintVectorLine("pos", pose.position); 135 | DebugPrintVectorLine("orientation", pose.orientation); 136 | DebugPrintVectorLine("linearVelocity", pose.linearVelocity); 137 | DebugPrintVectorLine("linearAcceleration", pose.linearAcceleration); 138 | DebugPrintVectorLine("angularVelocity", pose.angularVelocity); 139 | DebugPrintVectorLine("angularAcceleration", pose.angularAcceleration); 140 | elemStats.innerHTML += "
"; 141 | } 142 | 143 | elemStats.innerHTML += "Frame Time: " + app.getFrameTime().toFixed(3) + " (" + (1.0 / app.getFrameTime()).toFixed(3) + ")"; 144 | elemStats.innerHTML += "
"; 145 | elemStats.innerHTML += "ZenVR Version: " + settings.version; 146 | }); 147 | 148 | function toggleStats() { 149 | if (loop.isRunning()) { 150 | elemStats.style.display = "none"; 151 | loop.stop(); 152 | } else { 153 | elemStats.style.display = "block"; 154 | loop.start(); 155 | } 156 | } 157 | 158 | // show stats 159 | window.addEventListener('keypress', function(key) { 160 | if (key.code === "KeyS") { 161 | toggleStats() 162 | } 163 | }, false); 164 | app.addGuiItem('Toggle Stats', toggleStats); 165 | 166 | app.addGuiItem('Capture Cubemap', app.captureCubeMap); 167 | } 168 | 169 | function doResetPoseVR() { 170 | 171 | /*if (vrDisplay !== null) { 172 | vrDisplay.resetPose(); 173 | }*/ 174 | 175 | controls.update(); 176 | camera.obj.lookAt(new THREE.Vector3(0.0, 0.0, -1.0)); 177 | } 178 | 179 | function initButtons() { 180 | 181 | var renderer = require('./App.js').renderer; 182 | 183 | // Create WebVR UI Enter 360 Button 184 | /*var enter360 = new webvrui.Enter360Button(renderer.domElement) 185 | .on("enter", function() { 186 | console.log("enter 360"); 187 | onEnterFocus(); 188 | }) 189 | .on("exit", function() { 190 | console.log("exit 360"); 191 | onExitFocus(); 192 | }); 193 | 194 | document.getElementById("fullscreen").appendChild(enter360.domElement);*/ 195 | 196 | // Button click handlers. 197 | //document.querySelector('button#fullscreen').addEventListener('click', () => require('./App.js').onFullscreen()); 198 | document.querySelector('button#resetVR').addEventListener('click', doResetPoseVR); 199 | window.addEventListener('keypress', function(key) { 200 | if (key.code === "KeyR") { 201 | doResetPoseVR() 202 | } 203 | }, false); 204 | } 205 | 206 | function doHandleHandShakeCamera() { 207 | if (vrDisplay !== null && typeof VRFrameData !== 'undefined') { 208 | return; 209 | } 210 | 211 | let shakeParams = { 212 | posRadiusX: 0.023, 213 | posRadiusY: 0.028, 214 | 215 | posRadiusSpeedX: 1.0, 216 | posRadiusSpeedY: 1.0, 217 | 218 | lookRadiusX: 0.0016, 219 | lookRadiusY: 0.0016, 220 | 221 | lookRadiusSpeedX: 1.0, 222 | lookRadiusSpeedY: 1.0, 223 | 224 | globalSpeed: 0.7, 225 | }; 226 | 227 | let noise = new Noise.Noise(Math.random()); 228 | let guiParams = guihelper.createGuiParamObj("Hand Shake Camera"); 229 | guihelper.addResetButton(guiParams); 230 | 231 | guihelper.addGuiFloat(guiParams, "Pos Radius X", shakeParams, "posRadiusX", 0.0, 0.5); 232 | guihelper.addGuiFloat(guiParams, "Pos Radius Y", shakeParams, "posRadiusY", 0.0, 0.5); 233 | 234 | guihelper.addGuiFloat(guiParams, "Look Radius X", shakeParams, "lookRadiusX", 0.001, 0.01); 235 | guihelper.addGuiFloat(guiParams, "Look Radius Y", shakeParams, "lookRadiusY", 0.001, 0.01); 236 | 237 | guihelper.addGuiFloat(guiParams, "Global Speed", shakeParams, "globalSpeed", 0.0, 5.0); 238 | 239 | let loop = animitter(function(deltaTime, elapsedTime, frameCount) { 240 | 241 | var dist = 0.007; 242 | var speed = 0.0004 * shakeParams.globalSpeed; 243 | var dirX = noise.simplex2(elapsedTime * speed * shakeParams.posRadiusSpeedX, 0.3) * shakeParams.lookRadiusX; 244 | var dirY = noise.simplex2(elapsedTime * speed * shakeParams.posRadiusSpeedY + 100.0, 40.7) * shakeParams.lookRadiusY; 245 | var posX = noise.simplex2(elapsedTime * speed * shakeParams.lookRadiusSpeedX + 800.0, 440.7) * shakeParams.posRadiusX; 246 | var posY = noise.simplex2(elapsedTime * speed * shakeParams.lookRadiusSpeedY + 500.0, 240.7) * shakeParams.posRadiusY; 247 | 248 | var lookDir = new THREE.Vector3(dirX, dirY, -1.0); 249 | camera.obj.position.x = posX; 250 | camera.obj.position.y = posY; 251 | var lookPos = camera.obj.position.clone().add(lookDir); 252 | camera.obj.lookAt(lookPos); 253 | }).start(); 254 | } 255 | 256 | function hideEnterVRButton() { 257 | //document.getElementById("toggleVR").style.display = "none"; 258 | } 259 | 260 | let camPosCached = new THREE.Vector3(0.0, 0.0, 0.0); 261 | let camDirCached = new THREE.Vector3(0.0, 0.0, -1.0); 262 | 263 | function cacheCamPos() { 264 | 265 | if (!module.exports.isCameraReady()) { 266 | return; 267 | } else { 268 | let frameData = new VRFrameData(); 269 | vrDisplay.getFrameData(frameData); 270 | 271 | if (frameData.pose.position == null) { 272 | return; 273 | } 274 | 275 | let pos = frameData.pose.position; 276 | camPosCached.setX(pos[0]); 277 | camPosCached.setY(pos[1]); 278 | camPosCached.setZ(pos[2]); 279 | //return new THREE.Vector3(pos[0], pos[1], pos[2]); 280 | } 281 | } 282 | 283 | function cacheCamDir() { 284 | 285 | if (!module.exports.isCameraReady()) { 286 | 287 | if (vrDisplay !== null && vrDisplay.hasOwnProperty('orientation_')) { 288 | let orientation = vrDisplay.orientation_; 289 | let quat = new THREE.Quaternion(orientation.x, orientation.y, orientation.z, orientation.w); 290 | let result = new THREE.Vector3(0.0, 0.0, -1.0); 291 | result.applyQuaternion(quat); 292 | 293 | camDirCached.copy(result); 294 | return; 295 | } 296 | return; 297 | } else { 298 | let frameData = new VRFrameData(); 299 | vrDisplay.getFrameData(frameData); 300 | 301 | if (frameData.pose.orientation == null) { 302 | return; 303 | } 304 | 305 | let orientation = frameData.pose.orientation; 306 | let quat = new THREE.Quaternion(orientation[0], orientation[1], orientation[2], orientation[3]); 307 | let result = new THREE.Vector3(0.0, 0.0, -1.0); 308 | 309 | result.applyQuaternion(quat); 310 | camDirCached.copy(result); 311 | 312 | return; 313 | } 314 | } 315 | 316 | module.exports = { 317 | obj: controls, 318 | 319 | init: function() { 320 | console.log('CameraControlVR.init()'); 321 | navigator.getVRDisplays().then(function(displays) { 322 | 323 | console.log('Found VR displays, count ' + displays.length); 324 | 325 | if (displays.length > 0) { 326 | vrDisplay = displays[0]; 327 | 328 | console.log("VR display name: " + vrDisplay.displayName); 329 | 330 | ga('send', { 331 | hitType: 'event', 332 | eventCategory: 'Chromatic', 333 | eventAction: 'headset', 334 | eventLabel: vrDisplay.displayName 335 | }); 336 | 337 | onConnectHMD(vrDisplay); 338 | 339 | if (typeof VRFrameData !== 'undefined') { 340 | let frameData = new VRFrameData(); 341 | vrDisplay.getFrameData(frameData); 342 | console.log(frameData); 343 | 344 | if (vrDisplay.capabilities.canPresent == false) { 345 | hideEnterVRButton(); 346 | } 347 | } 348 | } 349 | }).catch( reason => {console.error('getVRDisplays failed: ', reason ); }); 350 | 351 | initButtons(); 352 | initStatsVR(); 353 | 354 | // Create WebVR UI Enter VR Button 355 | var app = require('./App.js'); 356 | var renderer = app.renderer; 357 | 358 | // hack: ios - on "Enter VR" does not hide the screen, so need to hack this in 359 | var isIOS = app.getMobileOperatingSystem() === "iOS"; 360 | 361 | var enterVR = new webvrui.EnterVRButton(renderer.domElement, {}) 362 | .on("enter", function() { 363 | console.log("enter VR!!") 364 | onEnterFocus(); 365 | 366 | console.log("Sending google event"); 367 | ga('send', { 368 | hitType: 'event', 369 | eventCategory: 'Chromatic', 370 | eventAction: 'play', 371 | eventLabel: 'VRmode' 372 | }); 373 | 374 | app.onResize(); 375 | 376 | if (isIOS) 377 | { 378 | document.getElementById("content").style.display = "none"; 379 | } else { 380 | document.getElementById("putOnVR").style.display = "block"; 381 | } 382 | }) 383 | .on("exit", function() { 384 | console.log("exit VR") 385 | onExitFocus(); 386 | 387 | app.onResize(); 388 | 389 | if (isIOS) 390 | { 391 | document.getElementById("content").style.display = "block"; 392 | } else { 393 | document.getElementById("putOnVR").style.display = "none"; 394 | } 395 | }); 396 | 397 | document.getElementById("toggleVR").appendChild(enterVR.domElement); 398 | 399 | document.getElementById("enter360").onclick = function () { 400 | console.log("Entering 360 mode.."); 401 | 402 | console.log("Sending google event"); 403 | ga('send', { 404 | hitType: 'event', 405 | eventCategory: 'Chromatic', 406 | eventAction: 'play', 407 | eventLabel: '360mode' 408 | }); 409 | 410 | enterVR.requestEnterFullscreen(); 411 | } 412 | 413 | //doHandleHandShakeCamera(); 414 | }, 415 | 416 | update: function() { 417 | 418 | if (useVRCam) { 419 | controls.update(); 420 | } 421 | 422 | cacheCamPos(); 423 | cacheCamDir(); 424 | }, 425 | 426 | isCameraReady: function() { 427 | return vrDisplay !== null && typeof VRFrameData !== 'undefined'; 428 | }, 429 | 430 | getCamPos: function() { 431 | return camPosCached; 432 | }, 433 | 434 | getCamForwardDir: function() { 435 | return camDirCached; 436 | } 437 | }; -------------------------------------------------------------------------------- /src/js/GuiHelper.js: -------------------------------------------------------------------------------- 1 | var settings = require('./Settings.js'); 2 | 3 | module.exports = { 4 | 5 | createGuiParamObj:function(folderTitle) { 6 | 7 | var app = require('./App.js'); 8 | 9 | return { 10 | base: {}, 11 | gui: {}, 12 | folder: app.addGuiFolder(folderTitle), 13 | }; 14 | }, 15 | 16 | addGuiButton: function(guiParams, guiTitle, func) { 17 | if (!settings.showGui) { 18 | return; 19 | } 20 | 21 | guiParams.gui[guiTitle] = func; 22 | guiParams.folder.add(guiParams.gui, guiTitle); 23 | }, 24 | 25 | addGuiFloat: function(guiParams, guiTitle, obj, objKey, minVal = 0.0, maxVal = 1.0) { 26 | 27 | if (!settings.showGui) { 28 | return; 29 | } 30 | 31 | Object.defineProperty(guiParams.gui, guiTitle, { 32 | get: function() { 33 | return obj[objKey]; 34 | }, 35 | set: function(val) { 36 | obj[objKey] = val; 37 | }, 38 | }); 39 | 40 | guiParams.base[guiTitle] = guiParams.gui[guiTitle]; 41 | 42 | let guiElem = guiParams.folder.add(guiParams.gui, guiTitle, minVal, maxVal); 43 | guiElem.listen(); 44 | 45 | // remove signal if change manually 46 | guiElem.onChange((value)=>{ 47 | let signal = require("./Scene/Signal.js"); 48 | signal.removeFromMap(obj, objKey); 49 | }); 50 | 51 | return guiElem; 52 | }, 53 | 54 | addGuiColor: function(guiParams, guiTitle, obj, objKey) { 55 | 56 | if (!settings.showGui) { 57 | return; 58 | } 59 | 60 | let startColorVec4 = obj[objKey]; 61 | let color = [startColorVec4.x * 255, startColorVec4.y*255, startColorVec4.z*255, startColorVec4.w]; 62 | let colorNormFactor = 1.0 / 255.0; 63 | 64 | Object.defineProperty(guiParams.gui, guiTitle, { 65 | get: function() { 66 | return color; 67 | }, 68 | set: function(val) { 69 | color = val; 70 | obj[objKey] = new THREE.Vector4(color[0] * colorNormFactor, color[1] * colorNormFactor, color[2]* colorNormFactor, color[3] ); 71 | }, 72 | }); 73 | 74 | guiParams.base[guiTitle] = guiParams.gui[guiTitle]; 75 | 76 | let guiElem = guiParams.folder.addColor(guiParams.gui, guiTitle); 77 | guiElem.listen(); 78 | 79 | return guiElem; 80 | }, 81 | 82 | addGuiBool: function(guiParams, guiTitle, obj, objKey) { 83 | 84 | if (!settings.showGui) { 85 | return; 86 | } 87 | 88 | Object.defineProperty(guiParams.gui, guiTitle, { 89 | get: function() { 90 | return obj[objKey]; 91 | }, 92 | set: function(val) { 93 | obj[objKey] = val; 94 | }, 95 | }); 96 | 97 | guiParams.base[guiTitle] = guiParams.gui[guiTitle]; 98 | 99 | let guiElem = guiParams.folder.add(guiParams.gui, guiTitle); 100 | guiElem.listen(); 101 | 102 | return guiElem; 103 | }, 104 | 105 | addGuiDropDown: function(guiParams, guiTitle, obj, objKey, options) { 106 | 107 | if (!settings.showGui) { 108 | return; 109 | } 110 | 111 | Object.defineProperty(guiParams.gui, guiTitle, { 112 | get: function() { 113 | return obj[objKey]; 114 | }, 115 | set: function(val) { 116 | obj[objKey] = val; 117 | }, 118 | }); 119 | 120 | guiParams.base[guiTitle] = guiParams.gui[guiTitle]; 121 | 122 | let guiElem = guiParams.folder.add(guiParams.gui, guiTitle, options); 123 | guiElem.listen(); 124 | 125 | return guiElem; 126 | }, 127 | 128 | addResetButton: function(guiParams) { 129 | if (!settings.showGui) { 130 | return; 131 | } 132 | 133 | let resetLabel = "Reset"; 134 | 135 | guiParams[resetLabel] = function() { 136 | for (var key in guiParams.base) { 137 | if (guiParams.base.hasOwnProperty(key)) { 138 | guiParams.gui[key] = guiParams.base[key]; 139 | } 140 | } 141 | } 142 | 143 | let guiFolder = guiParams.folder; 144 | guiFolder.add(guiParams, resetLabel); 145 | }, 146 | }; -------------------------------------------------------------------------------- /src/js/Main.js: -------------------------------------------------------------------------------- 1 | var app = require('./App.js'); 2 | app.init(); 3 | app.start(); -------------------------------------------------------------------------------- /src/js/MathExt.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | randBetween: function(minVal, maxVal) { 3 | return minVal + (maxVal - minVal) * Math.random(); 4 | }, 5 | 6 | randBetweenInt: function(minVal, maxVal) { 7 | return Math.floor(minVal + (maxVal - minVal) * Math.random()); 8 | }, 9 | 10 | // radius: 1 11 | randUnitSphere: function(radius = 1) { 12 | let theta = module.exports.randBetween(0.0, 2.0 * Math.PI); 13 | let phi = module.exports.randBetween(0.0, Math.PI); 14 | 15 | let vec = module.exports.sphericalCoord(theta, phi, radius); 16 | return vec; 17 | }, 18 | 19 | sphericalCoord: function(theta, phi, radius = 1) { 20 | let x = radius * Math.cos(theta) * Math.sin(phi); 21 | let y = radius * Math.cos(phi); 22 | let z = radius * Math.sin(theta) * Math.sin(phi); 23 | 24 | return new THREE.Vector3(x, y, z); 25 | }, 26 | 27 | lerp: function(a, b, t) { 28 | return a + (b - a) * t; 29 | }, 30 | 31 | wrap: function(val, minVal, maxVal) { 32 | val -= minVal; 33 | 34 | var delta = maxVal - minVal; 35 | if (delta < 0.0001) return val; 36 | 37 | return val - (delta * Math.floor(val / delta)) + minVal; 38 | }, 39 | 40 | saturate: function(val) { 41 | return Math.max(0.0, Math.min(val, 1.0)); 42 | }, 43 | 44 | clamp: function(val, minVal, maxVal) { 45 | return Math.max(minVal, Math.min(val, maxVal)); 46 | }, 47 | 48 | pulse: function(center, width, val) { 49 | val = Math.abs(val - center); 50 | if (val > width) { 51 | return 0.0; 52 | } 53 | val /= width; 54 | 55 | return 1.0 - val * val * (3.0 - 2.0 * val); 56 | }, 57 | 58 | // http://www.iquilezles.org/www/articles/functions/functions.htm 59 | impulse: function(k, x) { 60 | 61 | let h = k*x; 62 | return h*Math.exp(1.0-h); 63 | }, 64 | }; -------------------------------------------------------------------------------- /src/js/Scene/Background.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require('animitter'); 3 | 4 | var mathext = require("../MathExt.js"); 5 | var settings = require('./../Settings.js'); 6 | 7 | let tween = null; 8 | let baseColor = new THREE.Color(); 9 | baseColor.set(settings.clearColor); 10 | 11 | function killExistingTween() { 12 | if (tween != null) { 13 | tween.stop(); 14 | tween = null; 15 | } 16 | } 17 | 18 | function makeTweenFromTo(colorHexFrom, colorHexTo, timeSecs) { 19 | let colorObj = { 20 | color: new THREE.Color(colorHexFrom), 21 | }; 22 | 23 | let colorGoal = new THREE.Color(colorHexTo); 24 | 25 | return new TWEEN.Tween(colorObj.color).to(colorGoal, timeSecs * 1000.0).onUpdate(() => { 26 | settings.clearColor = colorObj.color.getHex(); 27 | }); 28 | } 29 | 30 | module.exports = { 31 | 32 | fadeTo: function(colorHex, timeSecs) { 33 | killExistingTween(); 34 | 35 | baseColor = new THREE.Color(colorHex); 36 | tween = makeTweenFromTo(settings.clearColor, colorHex, timeSecs); 37 | tween.start(); 38 | }, 39 | 40 | pingColor: function(colorHex, timeSecs) { 41 | killExistingTween(); 42 | 43 | tween = makeTweenFromTo(colorHex, baseColor, timeSecs); 44 | tween.start(); 45 | }, 46 | 47 | setRGB : function(r,g,b,a = 1.0) { 48 | baseColor.setRGB(r,g,b); 49 | settings.clearColor = baseColor.getHex(); 50 | }, 51 | 52 | init: function() {}, 53 | 54 | start: function() {}, 55 | }; -------------------------------------------------------------------------------- /src/js/Scene/BounceSpheres.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require("animitter"); 3 | 4 | var mathext = require("./../MathExt.js"); 5 | 6 | var material, mesh; 7 | 8 | 9 | function createSphere(pos) { 10 | var geometry = new THREE.SphereGeometry(0.05, 8, 8); 11 | 12 | material = new THREE.ShaderMaterial({ 13 | 14 | uniforms: { 15 | time: { 16 | value: 0.1 17 | }, 18 | resolution: { 19 | value: new THREE.Vector2() 20 | } 21 | }, 22 | vertexShader: require('../Shaders.js').get('default.vp'), 23 | fragmentShader: require('../Shaders.js').get('default.fp'), 24 | //visible: true 25 | side: THREE.DoubleSide 26 | }); 27 | 28 | //var material = new THREE.MeshBasicMaterial({color: 0xff00ff, wireframe: false }); 29 | mesh = new THREE.Mesh(geometry, material); 30 | mesh.position.copy(pos); 31 | 32 | 33 | var tweenIn = new TWEEN.Tween(mesh.position).to({ 34 | z: pos.z - 0.5 35 | }, 1000).easing(TWEEN.Easing.Cubic.Out); 36 | 37 | var tweenOut = new TWEEN.Tween(mesh.position).to({ 38 | z: pos.z + 0.0 39 | }, 1000).easing(TWEEN.Easing.Cubic.In); 40 | 41 | tweenIn.chain(tweenOut); 42 | tweenOut.chain(tweenIn); 43 | //tweenIn.repeat(Infinity); 44 | tweenIn.start(); 45 | 46 | return mesh; 47 | } 48 | 49 | 50 | module.exports = { 51 | init: function() { 52 | for (let i = 0; i < 100; i += 1) { 53 | 54 | let radiusMin = 1; 55 | let radiusMax = 5; 56 | let radius = mathext.randBetween(radiusMin, radiusMax); 57 | 58 | let theta = mathext.randBetween(0.0, -Math.PI); 59 | let phi = mathext.randBetween(Math.PI * 0.3, Math.PI * 0.7); 60 | let pos = mathext.sphericalCoord(theta, phi, radius); 61 | 62 | let sphere = createSphere(pos); 63 | 64 | var scene = require('./Scene.js'); 65 | scene.obj.add(sphere); 66 | } 67 | }, 68 | 69 | start: function() { 70 | 71 | }, 72 | }; -------------------------------------------------------------------------------- /src/js/Scene/DebugArrows.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | init : function() { 3 | 4 | let scene = require("./Scene.js"); 5 | 6 | // add arrows 7 | var origin = new THREE.Vector3( 0, 0, 0 ); 8 | var length = 1; 9 | var headLength = 0.2; 10 | var headWidth = 0.1; 11 | scene.obj.add( new THREE.ArrowHelper( new THREE.Vector3( 1, 0, 0 ), origin, length, 0xff0000, headLength, headWidth )); 12 | scene.obj.add( new THREE.ArrowHelper( new THREE.Vector3( 0, 1, 0 ), origin, length, 0x00ff00, headLength, headWidth )); 13 | scene.obj.add( new THREE.ArrowHelper( new THREE.Vector3( 0, 0, 1 ), origin, length, 0x0000ff, headLength, headWidth )); 14 | }, 15 | }; -------------------------------------------------------------------------------- /src/js/Scene/DebugBlendTest.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require("animitter"); 3 | 4 | var geometry, material, mesh; 5 | 6 | module.exports = { 7 | init: function() { 8 | 9 | let scene = require("./Scene.js").obj; 10 | 11 | material = new THREE.MeshBasicMaterial({ 12 | color: 0xff0000, 13 | shading: THREE.SmoothShading, 14 | 15 | transparent: true, 16 | blending: THREE.CustomBlending, 17 | blendEquation: THREE.MaxEquation, 18 | }); 19 | 20 | material.needsUpdate = true; 21 | geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5); 22 | 23 | mesh = new THREE.Mesh(geometry, material); 24 | mesh.position.z = -1.0; 25 | 26 | scene.add(mesh); 27 | }, 28 | }; -------------------------------------------------------------------------------- /src/js/Scene/DebugBox.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require("animitter"); 3 | 4 | var geometry, material, mesh; 5 | 6 | module.exports = { 7 | init : function() { 8 | geometry = new THREE.BoxGeometry( 0.1, 0.1, 0.1 ); 9 | material = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: false } ); 10 | mesh = new THREE.Mesh( geometry, material ); 11 | 12 | var scene = require('./Scene.js'); 13 | scene.obj.add( mesh ); 14 | }, 15 | 16 | start : function() { 17 | var tween = new TWEEN.Tween(mesh.position).to({y:1, z: -1}, 1100).easing(TWEEN.Easing.Elastic.Out).start(); 18 | 19 | var loop = animitter(function(deltaTime, elapsedTime, frameCount){ 20 | //mesh.rotation.x += 0.01; 21 | //mesh.rotation.y += 0.02; 22 | }).start(); 23 | }, 24 | }; -------------------------------------------------------------------------------- /src/js/Scene/DebugPlane.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require("animitter"); 3 | 4 | var geometry, material, mesh; 5 | 6 | module.exports = { 7 | init: function() { 8 | 9 | let scene = require("./Scene.js"); 10 | let renderer = require("./../App.js").renderer; 11 | 12 | let planeWidth = 200; 13 | 14 | material = new THREE.MeshBasicMaterial({ 15 | color: 0xffffff, 16 | shading: THREE.SmoothShading, 17 | //map: texture 18 | }); 19 | 20 | var loader = new THREE.TextureLoader(); 21 | loader.load('res/img/checker.png', function(texture) { 22 | texture.wrapS = THREE.RepeatWrapping; 23 | texture.wrapT = THREE.RepeatWrapping; 24 | texture.repeat.set(planeWidth / 2, planeWidth / 2); 25 | texture.anisotropy = renderer.getMaxAnisotropy(); 26 | 27 | material.map = texture; 28 | material.needsUpdate = true; 29 | }); 30 | 31 | geometry = new THREE.PlaneGeometry(planeWidth, planeWidth); 32 | 33 | mesh = new THREE.Mesh(geometry, material); 34 | mesh.rotation.x = -Math.PI / 2; 35 | mesh.position.y = -1.0; 36 | scene.obj.add(mesh); 37 | }, 38 | }; -------------------------------------------------------------------------------- /src/js/Scene/DebugRoom.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require("animitter"); 3 | 4 | const boxWidth = 15; 5 | 6 | module.exports = { 7 | init: function() { 8 | 9 | var scene = require('./Scene.js'); 10 | 11 | var loader = new THREE.TextureLoader(); 12 | loader.load('res/img/checker.png', onTextureLoaded); 13 | 14 | function onTextureLoaded(texture) { 15 | 16 | texture.wrapS = THREE.RepeatWrapping; 17 | texture.wrapT = THREE.RepeatWrapping; 18 | texture.repeat.set(boxWidth/2, boxWidth/2); 19 | var geometry = new THREE.BoxGeometry(boxWidth, boxWidth, boxWidth); 20 | var material = new THREE.MeshBasicMaterial({ 21 | map: texture, 22 | color: 0xffffff, 23 | side: THREE.BackSide 24 | }); 25 | var skybox = new THREE.Mesh(geometry, material); 26 | scene.obj.add(skybox); 27 | } 28 | }, 29 | }; -------------------------------------------------------------------------------- /src/js/Scene/IntroSplash.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require("animitter"); 3 | 4 | var mathext = require("./../MathExt.js"); 5 | 6 | let obj3d = new THREE.Object3D(); 7 | let objContainer = new THREE.Object3D(); 8 | 9 | let lineData = { 10 | lines: [{ 11 | x0: 223.98, 12 | y0: 1.33, 13 | x1: 185.73, 14 | y1: 1.33 15 | }, { 16 | x0: 185.73, 17 | y0: 1.33, 18 | x1: 186.48, 19 | y1: 309.33 20 | }, { 21 | x0: 756.19, 22 | y0: 1.66, 23 | x1: 716.23, 24 | y1: 1.66 25 | }, { 26 | x0: 716.23, 27 | y0: 1.66, 28 | x1: 715.55, 29 | y1: 308.64 30 | }, { 31 | x0: 715.55, 32 | y0: 308.64, 33 | x1: 753.4, 34 | y1: 308.47 35 | }, { 36 | x0: 436.89, 37 | y0: 1.68, 38 | x1: 365.82, 39 | y1: 1.64 40 | }, { 41 | x0: 365.82, 42 | y0: 1.64, 43 | x1: 366.4, 44 | y1: 309.47 45 | }, { 46 | x0: 530.46, 47 | y0: 1.72, 48 | x1: 492.21, 49 | y1: 1.72 50 | }, { 51 | x0: 492.21, 52 | y0: 1.72, 53 | x1: 491.96, 54 | y1: 309.33 55 | }, { 56 | x0: 41.56, 57 | y0: 1.57, 58 | x1: 1.7, 59 | y1: 1.57 60 | }, { 61 | x0: 1.7, 62 | y0: 1.57, 63 | x1: 2.02, 64 | y1: 309.41 65 | }, { 66 | x0: 2.02, 67 | y0: 309.41, 68 | x1: 39.86, 69 | y1: 309.24 70 | }, { 71 | x0: 314.85, 72 | y0: 1.86, 73 | x1: 274.7, 74 | y1: 1.82 75 | }, { 76 | x0: 274.7, 77 | y0: 1.82, 78 | x1: 274.76, 79 | y1: 309.07 80 | }, { 81 | x0: 274.76, 82 | y0: 309.07, 83 | x1: 314.87, 84 | y1: 308.91 85 | }, { 86 | x0: 314.87, 87 | y0: 308.91, 88 | x1: 314.85, 89 | y1: 1.86 90 | }, { 91 | x0: 187.31, 92 | y0: 154.28, 93 | x1: 224.48, 94 | y1: 309.33 95 | }, { 96 | x0: 223.81, 97 | y0: 1.49, 98 | x1: 224.65, 99 | y1: 154.28 100 | }, { 101 | x0: 187.92, 102 | y0: 154.39, 103 | x1: 223.34, 104 | y1: 154.37 105 | }, { 106 | x0: 595.44, 107 | y0: 309.31, 108 | x1: 595.9, 109 | y1: 1.52 110 | }, { 111 | x0: 530.3, 112 | y0: 1.49, 113 | x1: 529.96, 114 | y1: 309.33 115 | }, { 116 | x0: 493.4, 117 | y0: 154.39, 118 | x1: 528.74, 119 | y1: 154.22 120 | }, { 121 | x0: 659.96, 122 | y0: 1.83, 123 | x1: 659.96, 124 | y1: 309.33 125 | }, { 126 | x0: 94.43, 127 | y0: 1.33, 128 | x1: 94.43, 129 | y1: 309.33 130 | }, { 131 | x0: 132.51, 132 | y0: 1.49, 133 | x1: 132.43, 134 | y1: 309.33 135 | }, { 136 | x0: 95.87, 137 | y0: 154.39, 138 | x1: 131.21, 139 | y1: 154.22 140 | }, { 141 | x0: 400.29, 142 | y0: 309.47, 143 | x1: 400.75, 144 | y1: 1.68 145 | }, { 146 | x0: 436.96, 147 | y0: 309.47, 148 | x1: 437.42, 149 | y1: 1.68 150 | }, { 151 | x0: 611.9, 152 | y0: 1.52, 153 | x1: 577.47, 154 | y1: 1.47 155 | }], 156 | 157 | dots: [{ 158 | x: 186.78, 159 | y: 154.33 160 | }, { 161 | x: 224.61, 162 | y: 154.33 163 | }, { 164 | x: 185.61, 165 | y: 1.66 166 | }, { 167 | x: 223.61, 168 | y: 1.66 169 | }, { 170 | x: 186.53, 171 | y: 309.33 172 | }, { 173 | x: 224.61, 174 | y: 309.33 175 | }, { 176 | x: 577.19, 177 | y: 1.66 178 | }, { 179 | x: 612.36, 180 | y: 1.66 181 | }, { 182 | x: 716.19, 183 | y: 1.66 184 | }, { 185 | x: 756.19, 186 | y: 1.66 187 | }, { 188 | x: 365.72, 189 | y: 1.66 190 | }, { 191 | x: 401.05, 192 | y: 1.66 193 | }, { 194 | x: 437.23, 195 | y: 1.66 196 | }, { 197 | x: 492.09, 198 | y: 154.33 199 | }, { 200 | x: 530.09, 201 | y: 154.33 202 | }, { 203 | x: 492.09, 204 | y: 1.66 205 | }, { 206 | x: 530.34, 207 | y: 1.66 208 | }, { 209 | x: 492.01, 210 | y: 309.33 211 | }, { 212 | x: 530.09, 213 | y: 309.33 214 | }, { 215 | x: 660, 216 | y: 1.66 217 | }, { 218 | x: 660, 219 | y: 309.33 220 | }, { 221 | x: 595.56, 222 | y: 309.33 223 | }, { 224 | x: 715.36, 225 | y: 309.33 226 | }, { 227 | x: 753.69, 228 | y: 309.33 229 | }, { 230 | x: 1.66, 231 | y: 1.66 232 | }, { 233 | x: 41.66, 234 | y: 1.66 235 | }, { 236 | x: 94.56, 237 | y: 154.33 238 | }, { 239 | x: 132.56, 240 | y: 154.33 241 | }, { 242 | x: 94.31, 243 | y: 1.66 244 | }, { 245 | x: 132.31, 246 | y: 1.66 247 | }, { 248 | x: 94.48, 249 | y: 309.33 250 | }, { 251 | x: 132.56, 252 | y: 309.33 253 | }, { 254 | x: 1.83, 255 | y: 309.33 256 | }, { 257 | x: 40.16, 258 | y: 309.33 259 | }, { 260 | x: 274.66, 261 | y: 1.66 262 | }, { 263 | x: 314.87, 264 | y: 1.79 265 | }, { 266 | x: 274.81, 267 | y: 308.91 268 | }, { 269 | x: 314.95, 270 | y: 308.91 271 | }, { 272 | x: 366.38, 273 | y: 309.33 274 | }, { 275 | x: 400.22, 276 | y: 309.33 277 | }, { 278 | x: 437.09, 279 | y: 309.33 280 | }] 281 | }; 282 | 283 | let material; 284 | let meshLine; 285 | let meshPts; 286 | 287 | function createLines() { 288 | 289 | let minX = 100000.0; 290 | let minY = 100000.0; 291 | let maxX = -100000.0; 292 | let maxY = -100000.0; 293 | 294 | for (var i = 0; i < lineData.dots.length; ++i) { 295 | var pt = lineData.dots[i]; 296 | 297 | minX = Math.min(pt.x, minX); 298 | minY = Math.min(pt.y, minY); 299 | maxX = Math.max(pt.x, maxX); 300 | maxY = Math.max(pt.y, maxY); 301 | } 302 | 303 | let centerX = (minX + maxX) * 0.5; 304 | let centerY = (minY + maxY) * 0.5; 305 | let offsetX = minX - centerX; 306 | let offsetY = minY - centerY; 307 | 308 | 309 | let scale = 0.0007; 310 | 311 | let positions = []; 312 | let uv = []; 313 | 314 | for (let i = 0; i < lineData.lines.length; ++i) { 315 | var pair = lineData.lines[i]; 316 | 317 | var x0 = (pair.x0 + offsetX) * scale; 318 | var y0 = (pair.y0 + offsetY) * scale; 319 | var x1 = (pair.x1 + offsetX) * scale; 320 | var y1 = (pair.y1 + offsetY) * scale; 321 | 322 | var uv_x0 = (pair.x0 - minX) / (maxX-minX); 323 | var uv_x1 = (pair.x1 - minX) / (maxX-minX); 324 | var uv_y0 = (pair.y0 - minY) / (maxY-minY); 325 | var uv_y1 = (pair.y1 - minY) / (maxY-minY); 326 | 327 | // flip-y 328 | y0 += -y0*2.0; 329 | y1 += -y1*2.0; 330 | 331 | // pos-0 332 | positions.push(x0); 333 | positions.push(y0); 334 | positions.push(0.0); 335 | uv.push(uv_x0); 336 | uv.push(uv_y0); 337 | 338 | // pos-1 339 | positions.push(x1); 340 | positions.push(y1); 341 | positions.push(0.0); 342 | uv.push(uv_x1); 343 | uv.push(uv_y1); 344 | } 345 | 346 | let geometry = new THREE.BufferGeometry(); 347 | geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3)); 348 | geometry.addAttribute('uv', new THREE.BufferAttribute(new Float32Array(uv), 2)); 349 | 350 | material = new THREE.ShaderMaterial({ 351 | 352 | uniforms: { 353 | uTime: { 354 | value: 0.0 355 | }, 356 | 357 | uFadeCoeff: { 358 | value: 0.0 359 | }, 360 | }, 361 | vertexShader: require('../Shaders.js').get('splashLines.vp'), 362 | fragmentShader: require('../Shaders.js').get('splashLines.fp'), 363 | transparent: true, 364 | depthWrite: false, 365 | depthTest: false, 366 | }); 367 | 368 | // mesh 369 | meshLine = new THREE.LineSegments(geometry, material); 370 | meshLine.frustumCulled = false; 371 | objContainer.add(meshLine); 372 | 373 | let loop = animitter(function(deltaTime, elapsedTime, frameCount) { 374 | let speed = 1.0; 375 | material.uniforms.uTime.value += speed * deltaTime * 0.001; 376 | }).start(); 377 | 378 | meshPts = new THREE.Points(meshLine.geometry, meshLine.material); 379 | meshPts.position.copy(meshLine.position); 380 | objContainer.add(meshPts); 381 | 382 | return meshLine; 383 | } 384 | 385 | let tweens = []; 386 | 387 | function killTweens() { 388 | for( let i = 0; i < tweens.length; i+=1) { 389 | tweens[i].stop(); 390 | } 391 | 392 | tweens.length = 0; 393 | } 394 | 395 | module.exports = { 396 | obj3d: obj3d, 397 | 398 | init: function() { 399 | let mesh = createLines(); 400 | 401 | let globalOffsetZ = -2.0; 402 | obj3d.position.z = globalOffsetZ; 403 | 404 | function resizeSplash() { 405 | 406 | var windowWidth = window.innerWidth; 407 | var windowHeight = window.innerHeight; 408 | 409 | var elemDesc = document.getElementById("desc"); 410 | var elemRect = elemDesc.getBoundingClientRect(); 411 | var pixelsTop = elemRect.top; 412 | 413 | // height offset to fit splash 414 | var goalPercent = 0.58; 415 | var percentDiff = mathext.clamp(goalPercent- pixelsTop / windowHeight, 0.0, 1.0); 416 | objContainer.position.y = percentDiff; 417 | 418 | // scale to asepect 419 | var aspect = windowWidth / windowHeight; 420 | var aspectClamped = mathext.clamp(aspect, 0.0, 1.0); 421 | objContainer.scale.x = aspectClamped; 422 | objContainer.scale.y = aspectClamped; 423 | } 424 | 425 | // handle resizing on portrait mode 426 | window.addEventListener('resize', function(e) { 427 | resizeSplash(); 428 | }); 429 | 430 | // annoying ios orientation change 431 | var app = require('./../App.js'); 432 | var isIOS = app.getMobileOperatingSystem() === "iOS"; 433 | if (isIOS) { 434 | window.addEventListener('orientationchange', function(e) { 435 | setTimeout(function(){ 436 | resizeSplash(); 437 | },200); 438 | }); 439 | } 440 | 441 | resizeSplash(); 442 | }, 443 | 444 | start: function() { 445 | var scene = require('./Scene.js'); 446 | scene.obj.add(obj3d); 447 | obj3d.add(objContainer); 448 | 449 | // fade ins 450 | this.doFadeInSeq(); 451 | }, 452 | 453 | doFadeInSeq: function() { 454 | 455 | obj3d.visible = true; 456 | 457 | // fade in 458 | material.uniforms['uFadeCoeff'].value = 1.0; 459 | 460 | killTweens(); 461 | 462 | let globalOffsetY = 0.06; 463 | let globalOffsetZ = -1.0; 464 | 465 | var tweenFade = new TWEEN.Tween(material.uniforms['uFadeCoeff']).to({value:0.0}, 1500).easing(TWEEN.Easing.Cubic.Out).start(); 466 | var tweenPos = new TWEEN.Tween(obj3d.position).to({y:globalOffsetY, z:globalOffsetZ}, 1000).easing(TWEEN.Easing.Cubic.Out).start(); 467 | 468 | tweens.push(tweenFade); 469 | tweens.push(tweenPos); 470 | }, 471 | 472 | doFadeAwaySeq: function() { 473 | 474 | // fade out 475 | var tweenFade = new TWEEN.Tween(material.uniforms['uFadeCoeff']).to({value:1.0}, 1300).easing(TWEEN.Easing.Cubic.Out).onComplete( ()=>{ 476 | obj3d.visible = false; 477 | }).start(); 478 | 479 | let globalOffsetY = 0.0; 480 | let globalOffsetZ = -2.0; 481 | 482 | var tweenPos = new TWEEN.Tween(obj3d.position).to({y:globalOffsetY, z:globalOffsetZ}, 900).easing(TWEEN.Easing.Cubic.InOut).start(); 483 | 484 | tweens.push(tweenFade); 485 | tweens.push(tweenPos); 486 | } 487 | }; -------------------------------------------------------------------------------- /src/js/Scene/MagicEye.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require("animitter"); 3 | 4 | var mathext = require("./../MathExt.js"); 5 | 6 | var material, mesh; 7 | 8 | module.exports = { 9 | init: function() { 10 | 11 | function makeSphere( radius, pos, color) { 12 | var geometry = new THREE.SphereGeometry(radius, 32, 32); 13 | 14 | material = new THREE.MeshBasicMaterial({ 15 | color: color, 16 | side: THREE.BackSide 17 | }); 18 | 19 | mesh = new THREE.Mesh(geometry, material); 20 | mesh.position.copy(pos); 21 | 22 | var scene = require('./Scene.js'); 23 | scene.obj.add(mesh); 24 | } 25 | 26 | makeSphere(0.15, new THREE.Vector3(0.0,0.0,-20.0), 0xffffff); 27 | makeSphere(0.3, new THREE.Vector3(0.0,0.0,-20.0), 0xff33ff); 28 | makeSphere(0.45, new THREE.Vector3(0.0,0.0,-20.0), 0x333333); 29 | makeSphere(0.6, new THREE.Vector3(0.0,0.0,-20.0), 0xff33ff); 30 | }, 31 | 32 | start: function() { 33 | 34 | }, 35 | }; -------------------------------------------------------------------------------- /src/js/Scene/MagicFabric.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require("animitter"); 3 | 4 | var mathext = require("./../MathExt.js"); 5 | var signal = require("./Signal.js"); 6 | var guihelper = require("../GuiHelper.js"); 7 | 8 | let obj3d = new THREE.Object3D(); 9 | var material, mesh; 10 | var guiParams = guihelper.createGuiParamObj("Rainbow - Base"); 11 | var numLayersFreq = 3; 12 | 13 | module.exports = { 14 | 15 | obj3d : obj3d, 16 | 17 | init: function() { 18 | 19 | var settings = require('./../Settings.js'); 20 | 21 | function initGui() { 22 | var app = require('./../App.js'); 23 | 24 | function addGuiUniformFloat(titleName, uniformName, minVal, maxVal) { 25 | guihelper.addGuiFloat(guiParams, titleName, material.uniforms[uniformName], 'value', minVal, maxVal ); 26 | } 27 | 28 | function addGuiUniformColor(titleName, uniformName) { 29 | guihelper.addGuiColor(guiParams, titleName, material.uniforms[uniformName], 'value'); 30 | } 31 | 32 | function addGuiUniformVectorComponent(titleName, uniformName, vec4Component, minVal, maxVal) { 33 | guihelper.addGuiFloat(guiParams, titleName, material.uniforms[uniformName].value, vec4Component, minVal, maxVal ); 34 | } 35 | 36 | function addGuiFreq(index) { 37 | let uniformName = "uFreq" + index; 38 | let uniformVal = material.uniforms[uniformName].value; 39 | 40 | // freq, movement-speed, rainbow-freq, alpha 41 | let guiFreqParam = guihelper.createGuiParamObj("Rainbow - Layer " + index); 42 | let baseFreqVal = material.uniforms[uniformName].value.clone(); 43 | 44 | function addFreqSlider(titleName, vec4Component, minVal, maxVal) { 45 | guihelper.addGuiFloat(guiFreqParam, titleName, material.uniforms[uniformName].value, vec4Component, minVal, maxVal ); 46 | } 47 | 48 | addFreqSlider("Frequency", 'x', 0.0, 20.0); 49 | addFreqSlider("Speed", 'y', -10.0, 10.0); 50 | addFreqSlider("Rainbow Density", 'z', 0.0, 50.0); 51 | addFreqSlider("Alpha", 'w', 0.0, 1.0); 52 | 53 | // add reset button 54 | guihelper.addResetButton(guiFreqParam); 55 | } 56 | 57 | // core elems 58 | guihelper.addResetButton(guiParams); 59 | addGuiUniformFloat("Global Alpha", 'uGlobalAlpha', 0.0, 2.0); 60 | addGuiUniformFloat("Base Radius", 'uBaseRadius', -10.0, 10.0); 61 | addGuiUniformFloat("Base Speed", 'uBaseSpeed', -5.0, 5.0); 62 | addGuiUniformFloat("Eye Sharpness", 'uEyeSharpness', 0.0, 50.0); 63 | addGuiUniformFloat("Eye Z-Depth", 'uEyeDepth', -50.0, 200.0); 64 | addGuiUniformFloat("Eye Curve Coeff", 'uEyeCurveCoeff', 1.0, 100.0); 65 | addGuiUniformFloat("Pulse Z-Pos", 'uPulsePosZ', -20.0, 80.0); 66 | addGuiUniformFloat("Colorize", 'uColorize', 0.0, 2.0); 67 | 68 | // soothing light 69 | addGuiUniformColor('Soothing Color', 'uSoothingColor' ); 70 | addGuiUniformVectorComponent('Soothing Freq', 'uSoothingLight', 'x', 0.0, 2.0); 71 | addGuiUniformVectorComponent('Soothing Speed', 'uSoothingLight', 'y', -20.0, 20.0); 72 | addGuiUniformVectorComponent('Soothing Z-Freq', 'uSoothingLight', 'w', 1.0, 1000.0); 73 | 74 | // soothing light 75 | addGuiUniformVectorComponent('Folding Freq', 'uFolding', 'x', 0.0, 30.0); 76 | addGuiUniformVectorComponent('Folding Amplitude', 'uFolding', 'y', 0.0, 0.2); 77 | addGuiUniformVectorComponent('Folding Speed', 'uFolding', 'z', -10.0, 10.0); 78 | 79 | // freq 80 | for( let i =0; i < numLayersFreq; i+=1) { 81 | addGuiFreq(i); 82 | } 83 | } 84 | 85 | material = new THREE.ShaderMaterial({ 86 | 87 | uniforms: { 88 | uBaseSpeed: {value: 1.0 }, 89 | uBaseRadius: {value: 4.0 }, 90 | uGlobalAlpha: {value: 1.0 }, 91 | 92 | // Freq Layers: [freq, movement-speed, rainbow-freq, alpha] 93 | uFreq0: {value: new THREE.Vector4(3.0, 2.0, 2.0, 0.9) }, 94 | uFreq1: {value: new THREE.Vector4(9.0, -2.0, 10.0, 0.6) }, 95 | uFreq2: {value: new THREE.Vector4(4.0, 10.0, 25.0, 0.4) }, 96 | 97 | // soothing-light: [freq, speed, time, z-freq] 98 | uSoothingColor: {value: new THREE.Vector4(0.2,0.2,0.4, 1.0) }, 99 | uSoothingLight: {value: new THREE.Vector4(0.5, -1.0, 0.0, 7.0) }, 100 | 101 | // folding: [freq, amplitude, speed, time] 102 | uFolding: {value: new THREE.Vector4(20.0, 0.0, 1.0, 7.0) }, 103 | 104 | // other 105 | uTime: {value: 0.1 }, 106 | uEyeDepth: {value: 75.0 }, 107 | uEyeCurveCoeff: {value: 60.0 }, 108 | uEyeSharpness: {value: 30.0 }, 109 | uPulsePosZ: {value: 0.0 }, 110 | uColorize: {value: 0.15 }, 111 | uCamPos: {value: new THREE.Vector3() }, 112 | uCamFwdDir: {value: new THREE.Vector3(0.0,0.0,-1.0) }, 113 | uBaseColor: {value: new THREE.Color(0.0, 0.0, 0.0) }, 114 | uTexRainbow: {value: require('../Textures.js').get('rainbow') }, 115 | }, 116 | vertexShader: require('../Shaders.js').get('magicFabric.vp'), 117 | fragmentShader: require('../Shaders.js').get('magicFabric.fp'), 118 | side: THREE.DoubleSide, 119 | }); 120 | 121 | var controlVR = require("./../CameraControlVR.js"); 122 | 123 | 124 | var loop = animitter(function(deltaTime, elapsedTime, frameCount) { 125 | material.uniforms.uTime.value += deltaTime * 0.001 * material.uniforms.uBaseSpeed.value; 126 | material.uniforms.uSoothingLight.value.z += deltaTime * 0.001 * material.uniforms.uSoothingLight.value.y; 127 | material.uniforms.uBaseColor.value.setHex(settings.clearColor); 128 | 129 | material.uniforms.uCamPos.value.copy(controlVR.getCamPos()); 130 | //material.uniforms.uCamFwdDir.value.copy(controlVR.getCamForwardDir()); 131 | }).start(); 132 | 133 | 134 | var geometry = new THREE.SphereGeometry(1, 100, 200); 135 | mesh = new THREE.Mesh(geometry, material); 136 | obj3d.add(mesh); 137 | 138 | var scene = require('./Scene.js'); 139 | scene.obj.add(obj3d); 140 | 141 | initGui(); 142 | }, 143 | 144 | start: function() { 145 | 146 | let loopPulse = null; 147 | 148 | function doAutomation() { 149 | 150 | signal.map('eyeDepth', material.uniforms['uEyeSharpness'], 'value', 80.0, 30.0); 151 | signal.map('eyeDepth', material.uniforms['uEyeDepth'], 'value', -1.0, 100.0); 152 | signal.map('eyeDepth', material.uniforms['uBaseRadius'], 'value', 5.0, 0.8); 153 | //signal.map('eyeDepth', material.uniforms['uGlobalAlpha'], 'value', 0.0, 1.0); 154 | signal.map('base', material.uniforms['uBaseSpeed'], 'value', 0.5, 1.5); 155 | signal.map('ringPulse', material.uniforms['uPulsePosZ'], 'value', -20.0, 100.0); 156 | signal.map('soothingLightZFreq', material.uniforms['uSoothingLight'].value, 'w', 500.0, 550.0); 157 | 158 | //signal.map('waveDistort', material.uniforms['uFolding'].value, 'x', 1.0, 20.0); 159 | //signal.map('soothingLightZFreq', material.uniforms['uFolding'].value, 'y', 0.0, 0.2); 160 | } 161 | 162 | doAutomation(); 163 | }, 164 | }; -------------------------------------------------------------------------------- /src/js/Scene/MagicGlitter.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require("animitter"); 3 | 4 | var mathext = require("./../MathExt.js"); 5 | var signal = require("./Signal.js"); 6 | 7 | let obj3d = new THREE.Object3D(); 8 | var material; 9 | var numParticles = 1024; 10 | 11 | module.exports = { 12 | 13 | obj3d : obj3d, 14 | 15 | setPulsePosZ: function(val) { 16 | if ( typeof material === 'undefined' ) { 17 | return; 18 | } 19 | material.uniforms.uPulsePosZ.value = val; 20 | }, 21 | 22 | init: function() { 23 | 24 | material = new THREE.ShaderMaterial({ 25 | 26 | uniforms: { 27 | uTime: {value: 0.1 }, 28 | uPulsePosZ: {value: 0.0 }, 29 | uCamPos: {value: new THREE.Vector3() }, 30 | uTexRainbow: {value: require('../Textures.js').get('rainbow') }, 31 | }, 32 | vertexShader: require('../Shaders.js').get('magicGlitter.vp'), 33 | fragmentShader: require('../Shaders.js').get('magicGlitter.fp'), 34 | transparent: true, 35 | depthWrite: false, 36 | depthTest: false, 37 | 38 | //blending: THREE.CustomBlending, 39 | //blendEquation: THREE.SubtractEquation, 40 | //blendSrc: THREE.ZeroAlphaFactor, 41 | //blendDst: THREE.OneFactor, 42 | }); 43 | 44 | 45 | //material.blending = THREE.CustomBlending; 46 | material.blendEquation = THREE.SubtractEquation; 47 | material.blendSrc = THREE.OneFactor; 48 | material.blendDst = THREE.ZeroFactor; 49 | //console.log(material.blendEquation); 50 | 51 | let positions = []; 52 | let randData = []; 53 | 54 | for (let i = 0; i < numParticles; i += 1) { 55 | 56 | var x = mathext.randBetween(0.0, 1.0); 57 | var y = mathext.randBetween(0.0, 1.0); 58 | var z = mathext.randBetween(0.0, 1.0); 59 | 60 | positions.push(x); 61 | positions.push(y); 62 | positions.push(z); 63 | 64 | randData.push(mathext.randBetween(0.0, 1.0)); 65 | randData.push(mathext.randBetween(0.0, 1.0)); 66 | randData.push(mathext.randBetween(0.0, 1.0)); 67 | } 68 | 69 | let geometry = new THREE.BufferGeometry(); 70 | geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3)); 71 | geometry.addAttribute('normal', new THREE.BufferAttribute(new Float32Array(randData), 3)); 72 | 73 | let mesh = new THREE.Points(geometry, material); 74 | mesh.frustumCulled = false; 75 | obj3d.add(mesh); 76 | 77 | /*let lines = new THREE.LineSegments(geometry, material); 78 | lines.frustumCulled = false; 79 | obj3d.add(lines);*/ 80 | 81 | var scene = require('./Scene.js'); 82 | scene.obj.add(obj3d); 83 | 84 | var controlVR = require("./../CameraControlVR.js"); 85 | 86 | var loop = animitter(function(deltaTime, elapsedTime, frameCount) { 87 | let time = elapsedTime * 0.001; 88 | let speed = signal.get("soothingLight", 1.0, 2.0); 89 | let touchPulsate = signal.get("hit_touchStart"); 90 | 91 | material.uniforms.uTime.value += (speed+touchPulsate*5.0) * deltaTime*0.001; 92 | material.uniforms.uCamPos.value.copy(controlVR.getCamPos()); 93 | }).start(); 94 | }, 95 | 96 | start: function() { 97 | 98 | }, 99 | }; -------------------------------------------------------------------------------- /src/js/Scene/MagicPrism.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require("animitter"); 3 | var mathext = require("./../MathExt.js"); 4 | var guihelper = require("../GuiHelper.js"); 5 | var settings = require("../Settings.js"); 6 | var signal = require("./Signal.js"); 7 | 8 | let prismSolidParams = { 9 | constructor: THREE.Mesh, 10 | numTubes: 2, 11 | tubeRadiusSegs: 8, 12 | tubeHeightSegs: 4, 13 | 14 | matUniforms: { 15 | uGlobalAlpha : 0.6, 16 | uSwayParams: new THREE.Vector4(2.0, 0.15, 1.4, 0.1), // [freq, speed, amplitude, amplitude-height] 17 | uColorParams: new THREE.Vector4(0.1, 1.0, 7.0, -10.0), // [rainbow-freq, rainbow-speed, fade-freq, fade-speed] 18 | uAlphaFadeDist: new THREE.Vector4(1.6, 3.5, 0.0, 0.0), // [alpha z-min,alpha z-max, ?, ?] 19 | uTubeParams: new THREE.Vector4(0.83, 1.4, 30.0, -12.0), // [tube-radius, tube-spread, tube-y-height, tube-y-offset] 20 | uInfiniteScalar : 100.0, 21 | }, 22 | }; 23 | 24 | let prismLineParams = { 25 | constructor: THREE.LineSegments, 26 | numTubes: 2, 27 | tubeRadiusSegs: 8, 28 | tubeHeightSegs: 4, 29 | 30 | matUniforms: { 31 | uGlobalAlpha : 0.1, 32 | uSwayParams: new THREE.Vector4(2.0, 0.15, 1.4, 0.1), // [freq, speed, amplitude, amplitude-height] 33 | uColorParams: new THREE.Vector4(0.3, 1.0, 6.0, -8.0), // [rainbow-freq, rainbow-speed, fade-freq, fade-speed] 34 | uAlphaFadeDist: new THREE.Vector4(2.0, 3.0, 0.0, 0.0), 35 | uTubeParams: new THREE.Vector4(0.83, 1.4, 30.0, -12.0), // [tube-radius, tube-spread, tube-y-height, tube-y-offset] 36 | uInfiniteScalar : 100.0, 37 | }, 38 | }; 39 | 40 | let prismSolidBGParams = { 41 | constructor: THREE.Mesh, 42 | numTubes: 1, 43 | tubeRadiusSegs: 6, 44 | tubeHeightSegs: 5, 45 | 46 | matUniforms: { 47 | uGlobalAlpha : 0.1, 48 | uSwayParams: new THREE.Vector4(10.0, 0.15, 1.5, 0.05), // [freq, speed, amplitude, amplitude-height] 49 | uColorParams: new THREE.Vector4(0.7, 1.0, 7.0, -10.0), // [rainbow-freq, rainbow-speed, fade-freq, fade-speed] 50 | uAlphaFadeDist: new THREE.Vector4(1.5, 6.0, 0.0, 0.0), // [alpha z-min,alpha z-max, ?, ?] 51 | uTubeParams: new THREE.Vector4(7.0, 0.0, 100.0, -50.0), // [tube-radius, tube-spread, tube-y-height, tube-y-offset] 52 | }, 53 | }; 54 | 55 | let prismSolidCoreParams = { 56 | constructor: THREE.Mesh, 57 | numTubes: 3, 58 | tubeRadiusSegs: 3, 59 | tubeHeightSegs: 2, 60 | 61 | matUniforms: { 62 | uGlobalAlpha : 1.0, 63 | uSwayParams: new THREE.Vector4(10.0, 0.15, 4.6, 0.1), // [freq, speed, amplitude, amplitude-height] 64 | uColorParams: new THREE.Vector4(0.5, 1.0, 7.0, -10.0), // [rainbow-freq, rainbow-speed, fade-freq, fade-speed] 65 | uAlphaFadeDist: new THREE.Vector4(1.5, 6.0, 0.0, 0.0), // [alpha z-min,alpha z-max, ?, ?] 66 | uTubeParams: new THREE.Vector4(0.0, 2.0, 0.0, -40.0), // [tube-radius, tube-spread, tube-y-height, tube-y-offset] 67 | }, 68 | }; 69 | 70 | let obj3d = new THREE.Object3D(); 71 | let matPrismSolid = null; 72 | let matPrismLine = null; 73 | let matPrismBG = null; 74 | 75 | function resetMaterialParams(material, params) { 76 | for (var key in params.matUniforms) { 77 | if (params.matUniforms.hasOwnProperty(key)) { 78 | 79 | var val = params.matUniforms[key]; 80 | //material.uniforms[key].value = val; 81 | 82 | if( typeof(val) === 'number') { 83 | material.uniforms[key].value = val; 84 | } else { 85 | material.uniforms[key].value.copy(val); 86 | } 87 | } 88 | } 89 | } 90 | 91 | function makeMaterial(params) { 92 | let material = new THREE.ShaderMaterial({ 93 | 94 | uniforms: { 95 | uTime: { 96 | value: 0.0 97 | }, 98 | 99 | uPulsePosZ: { 100 | value: 0.0 101 | }, 102 | 103 | uGlobalAlpha: { 104 | value: 1.0 105 | }, 106 | 107 | uInfiniteScalar: { 108 | value: 0.0 109 | }, 110 | 111 | // [perlin-colorize, perlin-fade, perlin-freq] 112 | uPerlinParams: { 113 | value: new THREE.Vector4(0.3, 0.2, 0.4, 0.0), 114 | }, 115 | 116 | // [tube-radius, tube-spread, tube-y-height, tube-y-offset] 117 | uTubeParams: { 118 | value: new THREE.Vector4(0.83, 1.4, 30.0, -15.0), 119 | }, 120 | 121 | uAlphaFadeDist: { 122 | value: new THREE.Vector4(1.0, 7.0, 0.0, 0.0) 123 | }, 124 | 125 | uColorTint: { 126 | value: new THREE.Vector4(1.0, 0.9, 1.0, 1.0), 127 | }, 128 | 129 | uCamPos: { 130 | value: new THREE.Vector3() 131 | }, 132 | 133 | uSwayParams: { 134 | // [freq, speed, amplitude, tubeRadius] 135 | value: new THREE.Vector4(), 136 | }, 137 | 138 | uColorParams: { 139 | // [rainbow-freq, rainbow-speed, fade-freq, fade-speed] 140 | value: new THREE.Vector4(), 141 | }, 142 | 143 | uTexRainbow: { 144 | value: require('../Textures.js').get('rainbow') 145 | }, 146 | 147 | uTexMask: { 148 | value: require('../Textures.js').get('white') 149 | }, 150 | uTexPerlin: { 151 | value: require('../Textures.js').get('white') 152 | }, 153 | }, 154 | vertexShader: require('../Shaders.js').get('magicPrism.vp'), 155 | fragmentShader: require('../Shaders.js').get('magicPrism.fp'), 156 | transparent: true, 157 | depthWrite: false, 158 | depthTest: false, 159 | blending : THREE.AdditiveBlending 160 | //side: THREE.DoubleSide 161 | }); 162 | 163 | resetMaterialParams(material, params); 164 | 165 | var loader = new THREE.TextureLoader(); 166 | loader.load('res/img/perlin.png', function(texture) { 167 | texture.wrapS = THREE.RepeatWrapping; 168 | texture.wrapT = THREE.RepeatWrapping; 169 | //texture.anisotropy = renderer.getMaxAnisotropy(); 170 | 171 | material.uniforms.uTexPerlin.value = texture; 172 | material.needsUpdate = true; 173 | }); 174 | 175 | var loader = new THREE.TextureLoader(); 176 | loader.load('res/img/prism-mask.png', function(texture) { 177 | texture.wrapS = THREE.RepeatWrapping; 178 | texture.wrapT = THREE.RepeatWrapping; 179 | //texture.anisotropy = renderer.getMaxAnisotropy(); 180 | 181 | material.uniforms.uTexMask.value = texture; 182 | material.needsUpdate = true; 183 | }); 184 | 185 | return material; 186 | } 187 | 188 | function makeMaterialGui(guiParams, params, material) { 189 | if (!settings.showGui) { 190 | return; 191 | } 192 | 193 | guihelper.addResetButton(guiParams); 194 | 195 | guihelper.addGuiFloat(guiParams, "Alpha", material.uniforms.uGlobalAlpha, 'value', 0.0, 1.0); 196 | guihelper.addGuiColor(guiParams, "Color Tint", material.uniforms.uColorTint, 'value'); 197 | 198 | guihelper.addGuiFloat(guiParams, "Tube Radius", material.uniforms.uTubeParams.value, 'x', 0.0, 20.0); 199 | guihelper.addGuiFloat(guiParams, "Tube Spread", material.uniforms.uTubeParams.value, 'y', 0.0, 2.0); 200 | guihelper.addGuiFloat(guiParams, "Tube Y-Height", material.uniforms.uTubeParams.value, 'z', 0.0, 40.0); 201 | guihelper.addGuiFloat(guiParams, "Tube Y-Offset", material.uniforms.uTubeParams.value, 'w', -40.0, 40.0); 202 | 203 | guihelper.addGuiFloat(guiParams, "Sway Freq", material.uniforms.uSwayParams.value, 'x', 0.0, 10.0); 204 | guihelper.addGuiFloat(guiParams, "Sway Speed", material.uniforms.uSwayParams.value, 'y', 0.0, 1.0); 205 | guihelper.addGuiFloat(guiParams, "Sway Distance", material.uniforms.uSwayParams.value, 'z', 0.0, 20.0); 206 | guihelper.addGuiFloat(guiParams, "Sway Height", material.uniforms.uSwayParams.value, 'w', 0.0, 0.2).step(0.05); 207 | 208 | guihelper.addGuiFloat(guiParams, "Rainbow Freq", material.uniforms.uColorParams.value, 'x', 0.0, 1.0).step(0.1); 209 | guihelper.addGuiFloat(guiParams, "Rainbow Speed", material.uniforms.uColorParams.value, 'y', -10.0, 10.0); 210 | guihelper.addGuiFloat(guiParams, "Fade Freq", material.uniforms.uColorParams.value, 'z', 0.0, 20.0); 211 | guihelper.addGuiFloat(guiParams, "Fade Speed", material.uniforms.uColorParams.value, 'w', -20.0, 20.0); 212 | 213 | guihelper.addGuiFloat(guiParams, "Fade Min-Dist", material.uniforms.uAlphaFadeDist.value, 'x', 0.0, 10.0); 214 | guihelper.addGuiFloat(guiParams, "Fade Max-Dist", material.uniforms.uAlphaFadeDist.value, 'y', 0.0, 10.0); 215 | 216 | guihelper.addGuiFloat(guiParams, "Perlin Colorize", material.uniforms.uPerlinParams.value, 'x', 0.0, 1.0); 217 | guihelper.addGuiFloat(guiParams, "Perlin Darken", material.uniforms.uPerlinParams.value, 'y', 0.0, 1.0); 218 | guihelper.addGuiFloat(guiParams, "Perlin Freq", material.uniforms.uPerlinParams.value, 'z', 0.0, 1.0); 219 | } 220 | 221 | function createGeometry(params) { 222 | // 223 | function getDir(angle) { 224 | return new THREE.Vector3(Math.cos(angle), 0.0, Math.sin(angle)); 225 | } 226 | 227 | function pushFloat2(container, x, y) { 228 | container.push(x); 229 | container.push(y); 230 | } 231 | 232 | function pushFloat3(container, x, y, z) { 233 | container.push(x); 234 | container.push(y); 235 | container.push(z); 236 | } 237 | 238 | function pushFloat4(container, x, y, z, w) { 239 | container.push(x); 240 | container.push(y); 241 | container.push(z); 242 | container.push(w); 243 | } 244 | 245 | function pushVec3(container, val) { 246 | container.push(val.x); 247 | container.push(val.y); 248 | container.push(val.z); 249 | } 250 | 251 | function pushVec4(container, val) { 252 | container.push(val.x); 253 | container.push(val.y); 254 | container.push(val.z); 255 | container.push(val.w); 256 | } 257 | 258 | let positions = []; 259 | let normals = []; 260 | let centers = []; 261 | let uvs = []; 262 | let indices = []; 263 | let segData = []; 264 | 265 | let tubeHeight = 1.0; 266 | let deltaHeight = tubeHeight / params.tubeHeightSegs; 267 | let deltaAngle = (2.0 * Math.PI) / params.tubeRadiusSegs; 268 | 269 | for (let iTube = 0; iTube < params.numTubes; iTube += 1) { 270 | 271 | let tubeAngle = iTube * (2.0 * Math.PI) / params.numTubes + Math.PI*0.5; 272 | let tubeCenterX = Math.cos(tubeAngle); 273 | let tubeCenterY = 0.0; 274 | let tubeCenterZ = Math.sin(tubeAngle); 275 | 276 | for (let iSegment = 0; iSegment < params.tubeHeightSegs; iSegment += 1) { 277 | 278 | for (let iRadiusSeg = 0; iRadiusSeg < params.tubeRadiusSegs; iRadiusSeg += 1) { 279 | 280 | // angles 281 | let angle0 = iRadiusSeg * deltaAngle; 282 | let angle1 = angle0 + deltaAngle; 283 | 284 | // normals 285 | let dir0 = getDir(angle0); 286 | let dir1 = getDir(angle1); 287 | let normal = getDir((angle0 + angle1) * 0.5); 288 | 289 | let uvX0 = 0.0; 290 | let uvX1 = 1.0; 291 | let uvY0 = iSegment / (params.tubeHeightSegs-1); 292 | let uvY1 = (iSegment+1) / (params.tubeHeightSegs-1); 293 | //let uvY0 = 0.0; 294 | //let uvY1 = 1.0; 295 | 296 | // positions 297 | let posY0 = tubeHeight * -0.5 + iSegment * deltaHeight; 298 | let posY1 = posY0 + deltaHeight; 299 | let posX0 = dir0.x; 300 | let posX1 = dir1.x; 301 | let posZ0 = dir0.z; 302 | let posZ1 = dir1.z; 303 | 304 | let indexOffset = positions.length / 3; 305 | 306 | // centers 307 | pushFloat4(centers, tubeCenterX, tubeCenterY, tubeCenterZ, tubeAngle); 308 | pushFloat4(centers, tubeCenterX, tubeCenterY, tubeCenterZ, tubeAngle); 309 | pushFloat4(centers, tubeCenterX, tubeCenterY, tubeCenterZ, tubeAngle); 310 | pushFloat4(centers, tubeCenterX, tubeCenterY, tubeCenterZ, tubeAngle); 311 | 312 | // pos 313 | pushFloat3(positions, posX0, posY0, posZ0); 314 | pushFloat3(positions, posX1, posY0, posZ1); 315 | pushFloat3(positions, posX1, posY1, posZ1); 316 | pushFloat3(positions, posX0, posY1, posZ0); 317 | 318 | // centers 319 | let segData0 = new THREE.Vector4(posY0, angle1, angle0, Math.random()); 320 | let segData1 = new THREE.Vector4(posY0, angle0, angle1, Math.random()); 321 | let segData2 = new THREE.Vector4(posY1, angle1, angle1, Math.random()); 322 | let segData3 = new THREE.Vector4(posY1, angle0, angle0, Math.random()); 323 | pushVec4(segData, segData0); 324 | pushVec4(segData, segData1); 325 | pushVec4(segData, segData2); 326 | pushVec4(segData, segData3); 327 | 328 | // normal 329 | /*pushVec3(normals, normal); 330 | pushVec3(normals, normal); 331 | pushVec3(normals, normal); 332 | pushVec3(normals, normal);*/ 333 | 334 | pushVec3(normals, dir0); 335 | pushVec3(normals, dir1); 336 | pushVec3(normals, dir1); 337 | pushVec3(normals, dir0); 338 | 339 | // uv 340 | pushFloat2(uvs, uvX0, uvY0); 341 | pushFloat2(uvs, uvX1, uvY0); 342 | pushFloat2(uvs, uvX1, uvY1); 343 | pushFloat2(uvs, uvX0, uvY1); 344 | 345 | // indices 346 | indices.push(indexOffset + 0); 347 | indices.push(indexOffset + 2); 348 | indices.push(indexOffset + 1); 349 | indices.push(indexOffset + 0); 350 | indices.push(indexOffset + 3); 351 | indices.push(indexOffset + 2); 352 | } 353 | } 354 | } 355 | 356 | let geometry = new THREE.BufferGeometry(); 357 | geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1)); 358 | geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3)); 359 | geometry.addAttribute('center', new THREE.BufferAttribute(new Float32Array(centers), 4)); 360 | geometry.addAttribute('data', new THREE.BufferAttribute(new Float32Array(segData), 4)); 361 | geometry.addAttribute('normal', new THREE.BufferAttribute(new Float32Array(normals), 3)); 362 | geometry.addAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2)); 363 | 364 | return geometry; 365 | } 366 | 367 | function initPrismMesh(params, material, guiTitle) { 368 | 369 | let geometry = createGeometry(params); 370 | 371 | let mesh = new(params.constructor || THREE.Mesh)(geometry, material); 372 | mesh.frustumCulled = false; 373 | mesh.rotation.set(Math.PI * 0.5, 0.0, 0.0); 374 | 375 | if (settings.showGui) { 376 | let guiParams = guihelper.createGuiParamObj(guiTitle); 377 | makeMaterialGui(guiParams, params, material); 378 | } 379 | 380 | var controlVR = require("./../CameraControlVR.js"); 381 | 382 | let loop = animitter(function(deltaTime, elapsedTime, frameCount) { 383 | let speed = material.uniforms.uSwayParams.value.y; 384 | let touchPulsate = signal.get("hit_touchStart"); 385 | 386 | material.uniforms.uTime.value += (speed+touchPulsate*1.75) * deltaTime * 0.001; 387 | material.uniforms.uCamPos.value.copy(controlVR.getCamPos()); 388 | }).start(); 389 | 390 | mesh.resetParams = function() { 391 | resetMaterialParams(material, params); 392 | }; 393 | 394 | return mesh; 395 | } 396 | 397 | function createOrigamiGeomtries(mesh, params) { 398 | mesh.geometries = []; 399 | mesh.geometries.push(mesh.geometry); 400 | 401 | for( let i = 0; i < 3; i+=1 ) { 402 | params.numTubes += 1; 403 | let geo = createGeometry(params); 404 | mesh.geometries.push(geo); 405 | } 406 | } 407 | 408 | function initPrismOrigami() { 409 | var params = prismSolidParams; 410 | 411 | // material 412 | let material = makeMaterial(params); 413 | matPrismSolid = material; 414 | 415 | let mesh = initPrismMesh(params, material, "PRISM"); 416 | obj3d.add(mesh); 417 | 418 | createOrigamiGeomtries(mesh, params); 419 | 420 | mesh.setGeoIndex = function(index) { 421 | index = index % mesh.geometries.length; 422 | mesh.geometry = mesh.geometries[index]; 423 | } 424 | 425 | return mesh; 426 | } 427 | 428 | function initPrismLines() { 429 | 430 | var scene = require('./Scene.js'); 431 | var params = prismLineParams; 432 | 433 | let material = makeMaterial(params); 434 | matPrismLine = material; 435 | 436 | // lines mesh 437 | let mesh = initPrismMesh(params, material, "PRISM (Lines)"); 438 | obj3d.add(mesh); 439 | 440 | 441 | // pts mesh 442 | let ptsMesh = new THREE.Points(mesh.geometry, mesh.material); 443 | ptsMesh.frustumCulled = false; 444 | mesh.add(ptsMesh); 445 | 446 | createOrigamiGeomtries(mesh, params); 447 | 448 | mesh.setGeoIndex = function(index) { 449 | index = index % mesh.geometries.length; 450 | mesh.geometry = mesh.geometries[index]; 451 | ptsMesh.geometry = mesh.geometries[index]; 452 | } 453 | 454 | return mesh; 455 | } 456 | 457 | function initPrismBG() { 458 | 459 | var scene = require('./Scene.js'); 460 | var params = prismSolidBGParams; 461 | 462 | let material = makeMaterial(params); 463 | material.side = THREE.BackSide; 464 | matPrismBG = material; 465 | 466 | // lines mesh 467 | let mesh = initPrismMesh(params, material, "PRISM (BG)"); 468 | obj3d.add(mesh); 469 | 470 | return mesh; 471 | } 472 | 473 | function initPrismCore() { 474 | 475 | var scene = require('./Scene.js'); 476 | var params = prismSolidCoreParams; 477 | 478 | let material = makeMaterial(params); 479 | material.side = THREE.DoubleSide; 480 | //material.side = THREE.BackSide; 481 | //matPrismLine = material; 482 | 483 | // lines mesh 484 | let mesh = initPrismMesh(params, material, "PRISM (Core)"); 485 | obj3d.add(mesh); 486 | 487 | return mesh; 488 | } 489 | 490 | let layers = {}; 491 | 492 | module.exports = { 493 | 494 | obj3d: obj3d, 495 | layers : layers, 496 | 497 | setPulsePosZ: function(val) { 498 | if (matPrismSolid != null) { 499 | matPrismSolid.uniforms.uPulsePosZ.value = val; 500 | } 501 | 502 | if (matPrismLine != null) { 503 | matPrismLine.uniforms.uPulsePosZ.value = val; 504 | } 505 | 506 | if (matPrismBG != null) { 507 | matPrismBG.uniforms.uPulsePosZ.value = val; 508 | } 509 | }, 510 | 511 | init: function() { 512 | 513 | layers['bg'] = initPrismBG(); 514 | layers['core'] = initPrismCore(); 515 | layers['origami'] = initPrismOrigami(); 516 | layers['lines'] = initPrismLines(); 517 | }, 518 | 519 | start: function() { 520 | var scene = require('./Scene.js'); 521 | scene.obj.add(obj3d); 522 | }, 523 | }; -------------------------------------------------------------------------------- /src/js/Scene/MagicRings.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require("animitter"); 3 | 4 | var geometry, material, mesh; 5 | 6 | module.exports = { 7 | init: function() { 8 | 9 | geometry = new THREE.BufferGeometry(); 10 | 11 | material = new THREE.ShaderMaterial({ 12 | 13 | uniforms: { 14 | uTime: { 15 | value: 0.1 16 | }, 17 | uCamPos: { 18 | value: new THREE.Vector3() 19 | } 20 | }, 21 | 22 | vertexShader: require('../Shaders.js').get('magicRings.vp'), 23 | fragmentShader: require('../Shaders.js').get('magicRings.fp'), 24 | side: THREE.DoubleSide, 25 | 26 | transparent: true, 27 | //blendSrc: THREE.OneFactor, 28 | //blendDst: THREE.OneFactor, 29 | }); 30 | 31 | const numRings = 1; 32 | const numVertsPerRing = 100; 33 | 34 | const numVertsTotal = numRings * numVertsPerRing; 35 | 36 | let positions = []; 37 | let colors = []; 38 | let indices = []; 39 | 40 | for (let iRing = 0; iRing < numRings; iRing += 1) { 41 | for (let iRingVert = 0; iRingVert < numVertsPerRing; iRingVert += 1) { 42 | 43 | var vertIndex = iRing * numVertsPerRing + iRingVert; 44 | 45 | var x = iRing / (numRings - 1.0); 46 | var y = iRingVert / (numVertsPerRing - 1.0); 47 | var z = vertIndex / (numVertsTotal - 1.0); 48 | 49 | positions.push(x); 50 | positions.push(y); 51 | positions.push(z); 52 | 53 | colors.push(1.0); 54 | colors.push(1.0); 55 | colors.push(1.0); 56 | } 57 | } 58 | 59 | // index buffer 60 | for (let i = 0; i < numVertsTotal-1; i += 1) { 61 | indices.push(i); 62 | indices.push(i+1); 63 | } 64 | //geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1)); 65 | geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3)); 66 | geometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3)); 67 | //geometry.computeBoundingSphere(); 68 | 69 | //mesh = new THREE.LineSegments(geometry, material); 70 | mesh = new THREE.Mesh(geometry, material); 71 | mesh.frustumCulled = false; 72 | mesh.drawMode = THREE.TriangleFanDrawMode ; 73 | 74 | var loop = animitter(function(deltaTime, elapsedTime, frameCount) { 75 | material.uniforms.uTime.value = elapsedTime; 76 | //material.uniforms.uCamPos.value.copy(controlVR.getCamPos()); 77 | }).start(); 78 | 79 | let scene = require("./Scene.js"); 80 | scene.obj.add(mesh); 81 | 82 | var app = require("./../App.js"); 83 | }, 84 | }; -------------------------------------------------------------------------------- /src/js/Scene/MagicSphere.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require("animitter"); 3 | 4 | var mathext = require("./../MathExt.js"); 5 | 6 | var material, mesh; 7 | 8 | 9 | function createSphere(pos) { 10 | var geometry = new THREE.SphereGeometry(3, 64, 64); 11 | 12 | material = new THREE.ShaderMaterial({ 13 | 14 | uniforms: { 15 | uTime: { 16 | value: 0.1 17 | }, 18 | uCamPos: { 19 | value: new THREE.Vector3() 20 | } 21 | }, 22 | vertexShader: require('../Shaders.js').get('magicSphere.vp'), 23 | fragmentShader: require('../Shaders.js').get('magicSphere.fp'), 24 | //visible: true 25 | side: THREE.DoubleSide 26 | }); 27 | 28 | var controlVR = require("./../CameraControlVR.js"); 29 | 30 | var loop = animitter(function(deltaTime, elapsedTime, frameCount) { 31 | material.uniforms.uTime.value = elapsedTime; 32 | material.uniforms.uCamPos.value.copy(controlVR.getCamPos()); 33 | }).start(); 34 | 35 | //var material = new THREE.MeshBasicMaterial({color: 0xff00ff, wireframe: false }); 36 | mesh = new THREE.Mesh(geometry, material); 37 | mesh.position.copy(pos); 38 | 39 | return mesh; 40 | } 41 | 42 | 43 | module.exports = { 44 | init: function() { 45 | 46 | let sphere = createSphere(new THREE.Vector3(0.0, 0.0, 0.0)); 47 | 48 | var scene = require('./Scene.js'); 49 | scene.obj.add(sphere); 50 | }, 51 | 52 | start: function() { 53 | 54 | }, 55 | }; -------------------------------------------------------------------------------- /src/js/Scene/Scene.js: -------------------------------------------------------------------------------- 1 | var scene = new THREE.Scene(); 2 | 3 | var sceneObjs = { 4 | Signal : require("./Signal.js"), 5 | AudioGen : require("./AudioGen.js"), 6 | IntroSplash : require("./IntroSplash.js"), 7 | //DebugBox : require("./DebugBox.js"), 8 | //DebugPlane : require("./DebugPlane.js"), 9 | //DebugArrows : require("./DebugArrows.js"), 10 | //DebugRoom : require("./DebugRoom.js"), 11 | //MagicRings : require("./MagicRings.js"), 12 | //MagicSphere : require("./MagicSphere.js"), 13 | MagicPrism : require("./MagicPrism.js"), 14 | //MagicFabric : require("./MagicFabric.js"), 15 | MagicGlitter : require("./MagicGlitter.js"), 16 | //MagicEye : require("./MagicEye.js"), 17 | //BounceSpheres : require("./BounceSpheres.js"), 18 | Background : require("./Background.js"), 19 | Director : require("./Director.js"), 20 | }; 21 | 22 | module.exports = { 23 | obj : scene, 24 | 25 | sceneObjs: sceneObjs, 26 | 27 | init : function() { 28 | for (var key in sceneObjs) { 29 | if(!sceneObjs.hasOwnProperty(key)) 30 | continue; 31 | 32 | var obj = sceneObjs[key]; 33 | if (obj.hasOwnProperty('init')) 34 | obj.init(); 35 | } 36 | }, 37 | 38 | start : function() { 39 | for (var key in sceneObjs) { 40 | if(!sceneObjs.hasOwnProperty(key)) 41 | continue; 42 | 43 | var obj = sceneObjs[key]; 44 | if (obj.hasOwnProperty('start')) 45 | obj.start(); 46 | } 47 | }, 48 | }; -------------------------------------------------------------------------------- /src/js/Scene/Signal.js: -------------------------------------------------------------------------------- 1 | var TWEEN = require("tween.js"); 2 | var animitter = require('animitter'); 3 | 4 | var mathext = require("../MathExt.js"); 5 | var settings = require('./../Settings.js'); 6 | 7 | let TYPE_SINE = 0; 8 | let TYPE_LINEAR = 1; 9 | let TYPE_HIT = 2; 10 | let TYPE_RAW = 3; 11 | 12 | var signals = { 13 | base: { 14 | freq: 0.1, 15 | type: TYPE_SINE, 16 | power: 1.0, 17 | }, 18 | 19 | waveDistort: { 20 | freq: 0.03, 21 | type: TYPE_SINE, 22 | power: 1.0, 23 | }, 24 | 25 | eyeDepth: { 26 | freq: 0.02, 27 | type: TYPE_SINE, 28 | power: 1.0, 29 | }, 30 | 31 | soothingLight: { 32 | freq: 0.4 / Math.PI, 33 | type: TYPE_SINE, 34 | power: 1.0, 35 | }, 36 | 37 | soothingLightZFreq: { 38 | freq: 0.1, 39 | type: TYPE_SINE, 40 | power: 1.0, 41 | }, 42 | 43 | ringPulse: { 44 | freq: 0.075, 45 | type: TYPE_LINEAR 46 | }, 47 | 48 | hit_piano: { 49 | fadeOutLerp: 0.05, 50 | type: TYPE_HIT, 51 | }, 52 | 53 | /*hit_1n: { 54 | fadeOutLerp: 0.05, 55 | type: TYPE_HIT, 56 | }, 57 | 58 | hit_2n: { 59 | fadeOutLerp: 0.05, 60 | type: TYPE_HIT, 61 | }, 62 | 63 | hit_4n: { 64 | fadeOutLerp: 0.05, 65 | type: TYPE_HIT, 66 | }, 67 | 68 | hit_8n: { 69 | fadeOutLerp: 0.05, 70 | type: TYPE_HIT, 71 | }, 72 | 73 | hit_2m: { 74 | fadeOutLerp: 0.04, 75 | type: TYPE_HIT, 76 | }, 77 | 78 | hit_3m: { 79 | fadeOutLerp: 0.04, 80 | type: TYPE_HIT, 81 | },*/ 82 | 83 | hit_4m: { 84 | fadeOutLerp: 0.009, 85 | type: TYPE_HIT, 86 | }, 87 | 88 | hit_touchStart: { 89 | fadeOutLerp: 0.045, 90 | type: TYPE_HIT, 91 | }, 92 | 93 | meter_piano: { 94 | type: TYPE_RAW, 95 | }, 96 | 97 | gazeCharge: { 98 | type: TYPE_RAW, 99 | }, 100 | }; 101 | 102 | let params = { 103 | gui: {}, 104 | layers: {}, 105 | maps: [], 106 | }; 107 | 108 | let mapIndex = 0; 109 | 110 | module.exports = { 111 | init: function() { 112 | 113 | var app = require('./../App.js'); 114 | let guiFolder = app.addGuiFolder("Signals / Events"); 115 | //guiFolder.open(); 116 | 117 | // gui data 118 | for (let key in signals) { 119 | if (signals.hasOwnProperty(key)) { 120 | 121 | let signalData = signals[key]; 122 | 123 | params.layers[key] = { 124 | 'val': 0.0, 125 | 'data': signalData, 126 | }; 127 | 128 | if (settings.showGui) { 129 | 130 | let guiTitleVal = key + ' (' + signalData.type + ')'; 131 | Object.defineProperty(params.layers[key], guiTitleVal, { 132 | get: function() { 133 | return params.layers[key].val; 134 | }, 135 | }); 136 | 137 | //guiFolder.add(signalData, 'type'); 138 | guiFolder.add(params.layers[key], guiTitleVal, 0.0, 1.0).listen(); 139 | 140 | if (signalData.hasOwnProperty('freq')) { 141 | let guiTitleHz = key + ' - Freq'; 142 | Object.defineProperty(params.layers[key], guiTitleHz, { 143 | get: function() { 144 | return params.layers[key].data.freq; 145 | }, 146 | set: function(val) { 147 | params.layers[key].data.freq = val; 148 | }, 149 | }); 150 | guiFolder.add(params.layers[key], guiTitleHz, 0.0, 0.5).listen(); 151 | } 152 | } 153 | } 154 | } 155 | }, 156 | 157 | get: function(signalName, minVal = 0.0, maxVal = 1.0) { 158 | return mathext.lerp(minVal, maxVal, params.layers[signalName].val); 159 | }, 160 | 161 | map: function(signalName, obj, objKey, minVal = 0.0, maxVal = 1.0) { 162 | 163 | var newItem = { 164 | obj: obj, 165 | objKey: objKey, 166 | signal: signalName, 167 | minVal: minVal, 168 | maxVal: maxVal, 169 | id: mapIndex, 170 | } 171 | 172 | params.maps.push(newItem); 173 | mapIndex += 1; 174 | 175 | return newItem.id; 176 | }, 177 | 178 | removeById: function(id) { 179 | for (let i = 0; i < params.maps.length; i += 1) { 180 | let currMap = params.maps[i]; 181 | 182 | if (id === currMap.id) { 183 | params.maps.splice(i, 1); 184 | return true; 185 | } 186 | } 187 | 188 | //console.log("Couldn't find: " + id) 189 | 190 | return false; 191 | }, 192 | 193 | removeByIdArray: function(idArray) { 194 | for (let i = 0; i < idArray.length; i += 1) { 195 | module.exports.removeById(idArray[i]); 196 | } 197 | 198 | idArray.length = 0; 199 | }, 200 | 201 | removeFromMap: function(obj, objKey) { 202 | for (let i = 0; i < params.maps.length; i += 1) { 203 | let currMap = params.maps[i]; 204 | let objCurr = currMap.obj; 205 | let objKeyCurr = currMap.objKey; 206 | 207 | if (objCurr == obj && objKeyCurr == objKey) { 208 | 209 | console.log("Rqemoved: " + objKey); 210 | params.maps.splice(i, 1); 211 | return true; 212 | } 213 | } 214 | 215 | console.log("Couldn't find: " + objKey) 216 | 217 | return false; 218 | }, 219 | 220 | set: function(signalName, val) { 221 | let layer = params.layers[signalName]; 222 | layer.val = val; 223 | }, 224 | 225 | pulsate: function(signalName, value = 1.0) { 226 | let layer = params.layers[signalName]; 227 | 228 | if (layer.data.type === TYPE_HIT) { 229 | layer.val = Math.max(layer.val, value); 230 | } 231 | }, 232 | 233 | start: function() { 234 | 235 | let paramKeys = Object.keys(params.layers); 236 | 237 | // run all data! 238 | let loop = animitter(function(deltaTime, elapsedTime, frameCount) { 239 | 240 | let time = elapsedTime * 0.001; 241 | 242 | // update signals 243 | for (let i = 0; i < paramKeys.length; i += 1) { 244 | let key = paramKeys[i]; 245 | let param = params.layers[key]; 246 | let data = param.data; 247 | let dataType = data.type; 248 | 249 | if (dataType === TYPE_SINE) { 250 | let power = data.power || 1.0; 251 | param.val = Math.pow(Math.sin(data.freq * time * Math.PI * 2.0 - Math.PI * 0.5) * 0.5 + 0.5, power); 252 | } else if (dataType === TYPE_LINEAR) { 253 | let val = data.freq * time; 254 | param.val = val - Math.floor(val); 255 | } else if (dataType === TYPE_HIT) { 256 | param.val = mathext.lerp(param.val, 0.0, data.fadeOutLerp); 257 | } 258 | } 259 | 260 | // update maps 261 | for (let i = 0; i < params.maps.length; i += 1) { 262 | 263 | let map = params.maps[i]; 264 | let obj = map.obj; 265 | let goalVal = module.exports.get(map.signal, map.minVal, map.maxVal); 266 | 267 | obj[map.objKey] = mathext.lerp(obj[map.objKey], goalVal, 0.05); 268 | } 269 | }); 270 | loop.start(); 271 | }, 272 | }; -------------------------------------------------------------------------------- /src/js/Settings.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | debug: false, 3 | showGui: false, 4 | doSplashSeq: true, 5 | version: '0.1', 6 | clearColor: 0x000000, 7 | //clearColor: 0x363141, 8 | cameraFOV: 40.0, 9 | antialias: false, 10 | audioVolume: 0.0, 11 | audioMute: false, 12 | audioBPM: 120, 13 | audioStartNote: "1n", 14 | audioMasteringEnable : true, 15 | disableAudioIOS : true, 16 | }; -------------------------------------------------------------------------------- /src/js/Shaders.js: -------------------------------------------------------------------------------- 1 | var glslify = require('glslify'); 2 | 3 | var shaders = { 4 | 'default.vp' : glslify('./../glsl/default.vp'), 5 | 'default.fp' : glslify('./../glsl/default.fp'), 6 | 'magicSphere.vp' : glslify('./../glsl/magicSphere.vp'), 7 | 'magicSphere.fp' : glslify('./../glsl/magicSphere.fp'), 8 | 'magicRings.vp' : glslify('./../glsl/magicRings.vp'), 9 | 'magicRings.fp' : glslify('./../glsl/magicRings.fp'), 10 | 'magicFabric.vp' : glslify('./../glsl/magicFabric.vp'), 11 | 'magicFabric.fp' : glslify('./../glsl/magicFabric.fp'), 12 | 'magicGlitter.vp' : glslify('./../glsl/magicGlitter.vp'), 13 | 'magicGlitter.fp' : glslify('./../glsl/magicGlitter.fp'), 14 | 'magicPrism.vp' : glslify('./../glsl/magicPrism.vp'), 15 | 'magicPrism.fp' : glslify('./../glsl/magicPrism.fp'), 16 | 'splashLines.vp' : glslify('./../glsl/splashLines.vp'), 17 | 'splashLines.fp' : glslify('./../glsl/splashLines.fp'), 18 | }; 19 | 20 | module.exports = { 21 | get:function(filename) { 22 | return shaders[filename]; 23 | } 24 | }; -------------------------------------------------------------------------------- /src/js/Textures.js: -------------------------------------------------------------------------------- 1 | var textures = {}; 2 | 3 | function genRainbowTex() { 4 | 5 | var texWidth = 8; 6 | var texHeight = 1; 7 | var saturation = 0.6; 8 | var brightness = 0.7; 9 | 10 | //var dataMap = new Float32Array(texWidth * texHeight * 3); 11 | var dataMap = new Uint8Array(texWidth * texHeight * 4); 12 | 13 | var color = new THREE.Color(); 14 | 15 | for (let iWidth = 0; iWidth < texWidth; iWidth += 1) { 16 | for (let iHeight = 0; iHeight < texHeight; iHeight += 1) { 17 | var offset = (iWidth + iHeight * texWidth) * 4; 18 | //var saturation = iHeight / (texHeight-1.0); 19 | 20 | color.setHSL(iWidth / (texWidth - 1.0), saturation, brightness); 21 | let colorHex = color.getHex(); 22 | colorHex = colorHex << 8; 23 | 24 | dataMap[offset + 0] = (0xff000000 & colorHex) >> 24; 25 | dataMap[offset + 1] = (0x00ff0000 & colorHex) >> 16; 26 | dataMap[offset + 2] = (0x0000ff00 & colorHex) >> 8; 27 | dataMap[offset + 3] = 0xff; 28 | } 29 | } 30 | 31 | var texture = new THREE.DataTexture( 32 | dataMap, 33 | texWidth, 34 | texHeight, 35 | THREE.RGBAFormat, 36 | THREE.UnsignedByteType); 37 | 38 | texture.wrapS = THREE.RepeatWrapping; 39 | texture.wrapT = THREE.RepeatWrapping; 40 | texture.minFilter = THREE.LinearFilter; 41 | texture.magFilter = THREE.LinearFilter; 42 | texture.needsUpdate = true; 43 | 44 | return texture; 45 | } 46 | 47 | function genWhiteTex() { 48 | 49 | var texWidth = 1; 50 | var texHeight = 1; 51 | var dataMap = new Uint8Array(texWidth * texHeight * 4); 52 | 53 | for (let iWidth = 0; iWidth < texWidth; iWidth += 1) { 54 | for (let iHeight = 0; iHeight < texHeight; iHeight += 1) { 55 | var offset = (iWidth + iHeight * texWidth) * 4; 56 | 57 | dataMap[offset + 0] = 0xff; 58 | dataMap[offset + 1] = 0xff; 59 | dataMap[offset + 2] = 0xff; 60 | dataMap[offset + 3] = 0xff; 61 | } 62 | } 63 | 64 | var texture = new THREE.DataTexture( 65 | dataMap, 66 | texWidth, 67 | texHeight, 68 | THREE.RGBAFormat, 69 | THREE.UnsignedByteType); 70 | 71 | texture.needsUpdate = true; 72 | 73 | return texture; 74 | } 75 | 76 | function genBlackTex() { 77 | 78 | var texWidth = 1; 79 | var texHeight = 1; 80 | var dataMap = new Uint8Array(texWidth * texHeight * 4); 81 | 82 | for (let iWidth = 0; iWidth < texWidth; iWidth += 1) { 83 | for (let iHeight = 0; iHeight < texHeight; iHeight += 1) { 84 | var offset = (iWidth + iHeight * texWidth) * 4; 85 | 86 | dataMap[offset + 0] = 0x00; 87 | dataMap[offset + 1] = 0x00; 88 | dataMap[offset + 2] = 0x00; 89 | dataMap[offset + 3] = 0xff; 90 | } 91 | } 92 | 93 | var texture = new THREE.DataTexture( 94 | dataMap, 95 | texWidth, 96 | texHeight, 97 | THREE.RGBAFormat, 98 | THREE.UnsignedByteType); 99 | 100 | 101 | texture.needsUpdate = true; 102 | 103 | return texture; 104 | } 105 | 106 | module.exports = { 107 | 108 | init: function() { 109 | textures['rainbow'] = genRainbowTex(); 110 | textures['white'] = genWhiteTex(); 111 | textures['black'] = genBlackTex(); 112 | 113 | // debug texture 114 | if (false) { 115 | 116 | var scene = require('./Scene/Scene.js'); 117 | var material = new THREE.MeshBasicMaterial({ 118 | color: 0xffffff, 119 | shading: THREE.SmoothShading, 120 | map: textures['rainbow'] 121 | }); 122 | 123 | let planeWidth = 3; 124 | var geometry = new THREE.PlaneGeometry(planeWidth, planeWidth); 125 | var mesh = new THREE.Mesh(geometry, material); 126 | mesh.rotation.x = -Math.PI / 2; 127 | mesh.position.y = -1.0; 128 | mesh.position.z = -planeWidth; 129 | scene.obj.add(mesh); 130 | } 131 | 132 | }, 133 | 134 | get: function(name) { 135 | return textures[name]; 136 | } 137 | }; -------------------------------------------------------------------------------- /src/lib/CubemapToEquirectangular.js: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | 3 | "use strict"; 4 | 5 | var root = this 6 | 7 | var has_require = typeof require !== 'undefined' 8 | 9 | var THREE = root.THREE || has_require && require('three') 10 | if( !THREE ) 11 | throw new Error( 'CubemapToEquirectangular requires three.js' ) 12 | 13 | var vertexShader = ` 14 | attribute vec3 position; 15 | attribute vec2 uv; 16 | 17 | uniform mat4 projectionMatrix; 18 | uniform mat4 modelViewMatrix; 19 | 20 | varying vec2 vUv; 21 | 22 | void main() { 23 | 24 | vUv = vec2( 1.- uv.x, uv.y ); 25 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 26 | 27 | } 28 | `; 29 | 30 | var fragmentShader = ` 31 | precision mediump float; 32 | 33 | uniform samplerCube map; 34 | 35 | varying vec2 vUv; 36 | 37 | #define M_PI 3.1415926535897932384626433832795 38 | 39 | void main() { 40 | 41 | vec2 uv = vUv; 42 | 43 | float longitude = uv.x * 2. * M_PI - M_PI + M_PI / 2.; 44 | float latitude = uv.y * M_PI; 45 | 46 | vec3 dir = vec3( 47 | - sin( longitude ) * sin( latitude ), 48 | cos( latitude ), 49 | - cos( longitude ) * sin( latitude ) 50 | ); 51 | normalize( dir ); 52 | 53 | gl_FragColor = vec4( textureCube( map, dir ).rgb, 1. ); 54 | 55 | } 56 | `; 57 | 58 | function CubemapToEquirectangular( renderer, provideCubeCamera ) { 59 | 60 | this.width = 1; 61 | this.height = 1; 62 | 63 | this.renderer = renderer; 64 | 65 | this.material = new THREE.RawShaderMaterial( { 66 | uniforms: { 67 | map: { type: 't', value: null } 68 | }, 69 | vertexShader: vertexShader, 70 | fragmentShader: fragmentShader, 71 | side: THREE.DoubleSide 72 | } ); 73 | 74 | this.scene = new THREE.Scene(); 75 | this.quad = new THREE.Mesh( 76 | new THREE.PlaneBufferGeometry( 1, 1 ), 77 | this.material 78 | ); 79 | this.scene.add( this.quad ); 80 | this.camera = new THREE.OrthographicCamera( 1 / - 2, 1 / 2, 1 / 2, 1 / - 2, -10000, 10000 ); 81 | 82 | this.canvas = document.createElement( 'canvas' ); 83 | this.ctx = this.canvas.getContext( '2d' ); 84 | 85 | this.cubeCamera = null; 86 | this.attachedCamera = null; 87 | 88 | this.setSize( 4096, 2048 ); 89 | 90 | var gl = this.renderer.getContext(); 91 | this.cubeMapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ) 92 | 93 | if( provideCubeCamera ) { 94 | this.getCubeCamera( 2048 ) 95 | } 96 | 97 | } 98 | 99 | CubemapToEquirectangular.prototype.setSize = function( width, height ) { 100 | 101 | this.width = width; 102 | this.height = height; 103 | 104 | this.quad.scale.set( this.width, this.height, 1 ); 105 | 106 | this.camera.left = this.width / - 2; 107 | this.camera.right = this.width / 2; 108 | this.camera.top = this.height / 2; 109 | this.camera.bottom = this.height / - 2; 110 | 111 | this.camera.updateProjectionMatrix(); 112 | 113 | this.output = new THREE.WebGLRenderTarget( this.width, this.height, { 114 | minFilter: THREE.LinearFilter, 115 | magFilter: THREE.LinearFilter, 116 | wrapS: THREE.ClampToEdgeWrapping, 117 | wrapT: THREE.ClampToEdgeWrapping, 118 | format: THREE.RGBAFormat, 119 | type: THREE.UnsignedByteType 120 | }); 121 | 122 | this.canvas.width = this.width; 123 | this.canvas.height = this.height; 124 | 125 | } 126 | 127 | CubemapToEquirectangular.prototype.getCubeCamera = function( size ) { 128 | 129 | this.cubeCamera = new THREE.CubeCamera( .1, 1000, Math.min( this.cubeMapSize, size ) ); 130 | return this.cubeCamera; 131 | 132 | } 133 | 134 | CubemapToEquirectangular.prototype.attachCubeCamera = function( camera ) { 135 | 136 | this.getCubeCamera(); 137 | this.attachedCamera = camera; 138 | 139 | } 140 | 141 | CubemapToEquirectangular.prototype.convert = function( cubeCamera ) { 142 | 143 | this.quad.material.uniforms.map.value = cubeCamera.renderTarget.texture; 144 | this.renderer.render( this.scene, this.camera, this.output, true ); 145 | 146 | var pixels = new Uint8Array( 4 * this.width * this.height ); 147 | this.renderer.readRenderTargetPixels( this.output, 0, 0, this.width, this.height, pixels ); 148 | 149 | var imageData = new ImageData( new Uint8ClampedArray( pixels ), this.width, this.height ); 150 | 151 | this.ctx.putImageData( imageData, 0, 0 ); 152 | 153 | this.canvas.toBlob( function( blob ) { 154 | 155 | var url = URL.createObjectURL(blob); 156 | var fileName = 'pano-' + document.title + '-' + Date.now() + '.png'; 157 | var anchor = document.createElement( 'a' ); 158 | anchor.href = url; 159 | anchor.setAttribute("download", fileName); 160 | anchor.className = "download-js-link"; 161 | anchor.innerHTML = "downloading..."; 162 | anchor.style.display = "none"; 163 | document.body.appendChild(anchor); 164 | setTimeout(function() { 165 | anchor.click(); 166 | document.body.removeChild(anchor); 167 | }, 1 ); 168 | 169 | }, 'image/png' ); 170 | 171 | } 172 | 173 | CubemapToEquirectangular.prototype.update = function( camera, scene ) { 174 | 175 | var autoClear = this.renderer.autoClear; 176 | this.renderer.autoClear = true; 177 | this.cubeCamera.position.copy( camera.position ); 178 | this.cubeCamera.updateCubeMap( this.renderer, scene ); 179 | this.renderer.autoClear = autoClear; 180 | 181 | this.convert( this.cubeCamera ); 182 | 183 | } 184 | 185 | if( typeof exports !== 'undefined' ) { 186 | if( typeof module !== 'undefined' && module.exports ) { 187 | exports = module.exports = CubemapToEquirectangular 188 | } 189 | exports.CubemapToEquirectangular = CubemapToEquirectangular 190 | } 191 | else { 192 | root.CubemapToEquirectangular = CubemapToEquirectangular 193 | } 194 | 195 | }).call(this); 196 | -------------------------------------------------------------------------------- /src/lib/NoSleep.min.js: -------------------------------------------------------------------------------- 1 | // NoSleep.min.js v0.5.0 - git.io/vfn01 - Rich Tibbett - MIT license 2 | !function(A){function e(A,e,o){var t=document.createElement("source");t.src=o,t.type="video/"+e,A.appendChild(t)}var o={Android:/Android/gi.test(navigator.userAgent),iOS:/AppleWebKit/.test(navigator.userAgent)&&/Mobile\/\w+/.test(navigator.userAgent)},t={WebM:"data:video/webm;base64,GkXfo0AgQoaBAUL3gQFC8oEEQvOBCEKCQAR3ZWJtQoeBAkKFgQIYU4BnQI0VSalmQCgq17FAAw9CQE2AQAZ3aGFtbXlXQUAGd2hhbW15RIlACECPQAAAAAAAFlSua0AxrkAu14EBY8WBAZyBACK1nEADdW5khkAFVl9WUDglhohAA1ZQOIOBAeBABrCBCLqBCB9DtnVAIueBAKNAHIEAAIAwAQCdASoIAAgAAUAmJaQAA3AA/vz0AAA=",MP4:"data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAAG21kYXQAAAGzABAHAAABthADAowdbb9/AAAC6W1vb3YAAABsbXZoZAAAAAB8JbCAfCWwgAAAA+gAAAAAAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIVdHJhawAAAFx0a2hkAAAAD3wlsIB8JbCAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAIAAAACAAAAAABsW1kaWEAAAAgbWRoZAAAAAB8JbCAfCWwgAAAA+gAAAAAVcQAAAAAAC1oZGxyAAAAAAAAAAB2aWRlAAAAAAAAAAAAAAAAVmlkZW9IYW5kbGVyAAAAAVxtaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAEcc3RibAAAALhzdHNkAAAAAAAAAAEAAACobXA0dgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAIAAgASAAAAEgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj//wAAAFJlc2RzAAAAAANEAAEABDwgEQAAAAADDUAAAAAABS0AAAGwAQAAAbWJEwAAAQAAAAEgAMSNiB9FAEQBFGMAAAGyTGF2YzUyLjg3LjQGAQIAAAAYc3R0cwAAAAAAAAABAAAAAQAAAAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAEAAAABAAAAFHN0c3oAAAAAAAAAEwAAAAEAAAAUc3RjbwAAAAAAAAABAAAALAAAAGB1ZHRhAAAAWG1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAAK2lsc3QAAAAjqXRvbwAAABtkYXRhAAAAAQAAAABMYXZmNTIuNzguMw=="},i=function(){return o.iOS?this.noSleepTimer=null:o.Android&&(this.noSleepVideo=document.createElement("video"),this.noSleepVideo.setAttribute("loop",""),e(this.noSleepVideo,"webm",t.WebM),e(this.noSleepVideo,"mp4",t.MP4)),this};i.prototype.enable=function(A){o.iOS?(this.disable(),this.noSleepTimer=window.setInterval(function(){window.location.href='/',window.setTimeout(window.stop,0)},A||15e3)):o.Android&&this.noSleepVideo.play()},i.prototype.disable=function(){o.iOS?this.noSleepTimer&&(window.clearInterval(this.noSleepTimer),this.noSleepTimer=null):o.Android&&this.noSleepVideo.pause()},A.NoSleep=i}(this); 3 | -------------------------------------------------------------------------------- /src/lib/StartAudioContext.js: -------------------------------------------------------------------------------- 1 | /** 2 | * StartAudioContext.js 3 | * @author Yotam Mann 4 | * @license http://opensource.org/licenses/MIT MIT License 5 | * @copyright 2016 Yotam Mann 6 | */ 7 | (function (root, factory) { 8 | if (typeof define === "function" && define.amd) { 9 | define([], factory) 10 | } else if (typeof module === "object" && module.exports) { 11 | module.exports = factory() 12 | } else { 13 | root.StartAudioContext = factory() 14 | } 15 | }(this, function () { 16 | 17 | //TAP LISTENER///////////////////////////////////////////////////////////// 18 | 19 | /** 20 | * @class Listens for non-dragging tap ends on the given element 21 | * @param {Element} element 22 | * @internal 23 | */ 24 | var TapListener = function(element, context){ 25 | 26 | this._dragged = false 27 | 28 | this._element = element 29 | 30 | this._bindedMove = this._moved.bind(this) 31 | this._bindedEnd = this._ended.bind(this, context) 32 | 33 | element.addEventListener("touchmove", this._bindedMove) 34 | element.addEventListener("touchend", this._bindedEnd) 35 | element.addEventListener("mouseup", this._bindedEnd) 36 | } 37 | 38 | /** 39 | * drag move event 40 | */ 41 | TapListener.prototype._moved = function(e){ 42 | this._dragged = true 43 | }; 44 | 45 | /** 46 | * tap ended listener 47 | */ 48 | TapListener.prototype._ended = function(context){ 49 | if (!this._dragged){ 50 | startContext(context) 51 | } 52 | this._dragged = false 53 | }; 54 | 55 | /** 56 | * remove all the bound events 57 | */ 58 | TapListener.prototype.dispose = function(){ 59 | this._element.removeEventListener("touchmove", this._bindedMove) 60 | this._element.removeEventListener("touchend", this._bindedEnd) 61 | this._element.removeEventListener("mouseup", this._bindedEnd) 62 | this._bindedMove = null 63 | this._bindedEnd = null 64 | this._element = null 65 | }; 66 | 67 | //END TAP LISTENER///////////////////////////////////////////////////////// 68 | 69 | /** 70 | * Plays a silent sound and also invoke the "resume" method 71 | * @param {AudioContext} context 72 | * @private 73 | */ 74 | function startContext(context){ 75 | // this accomplishes the iOS specific requirement 76 | var buffer = context.createBuffer(1, 1, context.sampleRate) 77 | var source = context.createBufferSource() 78 | source.buffer = buffer 79 | source.connect(context.destination) 80 | source.start(0) 81 | 82 | // resume the audio context 83 | if (context.resume){ 84 | context.resume() 85 | } 86 | } 87 | 88 | /** 89 | * Returns true if the audio context is started 90 | * @param {AudioContext} context 91 | * @return {Boolean} 92 | * @private 93 | */ 94 | function isStarted(context){ 95 | return context.state === "running" 96 | } 97 | 98 | /** 99 | * Invokes the callback as soon as the AudioContext 100 | * is started 101 | * @param {AudioContext} context 102 | * @param {Function} callback 103 | */ 104 | function onStarted(context, callback){ 105 | 106 | function checkLoop(){ 107 | if (isStarted(context)){ 108 | callback() 109 | } else { 110 | requestAnimationFrame(checkLoop) 111 | if (context.resume){ 112 | context.resume() 113 | } 114 | } 115 | } 116 | 117 | if (isStarted(context)){ 118 | callback() 119 | } else { 120 | checkLoop() 121 | } 122 | } 123 | 124 | /** 125 | * Add a tap listener to the audio context 126 | * @param {Array|Element|String|jQuery} element 127 | * @param {Array} tapListeners 128 | */ 129 | function bindTapListener(element, tapListeners, context){ 130 | if (Array.isArray(element) || (NodeList && element instanceof NodeList)){ 131 | for (var i = 0; i < element.length; i++){ 132 | bindTapListener(element[i], tapListeners, context) 133 | } 134 | } else if (typeof element === "string"){ 135 | bindTapListener(document.querySelectorAll(element), tapListeners, context) 136 | } else if (element.jquery && typeof element.toArray === "function"){ 137 | bindTapListener(element.toArray(), tapListeners, context) 138 | } else if (Element && element instanceof Element){ 139 | //if it's an element, create a TapListener 140 | var tap = new TapListener(element, context) 141 | tapListeners.push(tap) 142 | } 143 | } 144 | 145 | /** 146 | * @param {AudioContext} context The AudioContext to start. 147 | * @param {Array|String|Element|jQuery=} elements For iOS, the list of elements 148 | * to bind tap event listeners 149 | * which will start the AudioContext. If 150 | * no elements are given, it will bind 151 | * to the document.body. 152 | * @param {Function=} callback The callback to invoke when the AudioContext is started. 153 | * @return {Promise} The promise is invoked when the AudioContext 154 | * is started. 155 | */ 156 | function StartAudioContext(context, elements, callback){ 157 | 158 | //the promise is invoked when the AudioContext is started 159 | var promise = new Promise(function(success) { 160 | onStarted(context, success) 161 | }) 162 | 163 | // The TapListeners bound to the elements 164 | var tapListeners = [] 165 | 166 | // add all the tap listeners 167 | if (!elements){ 168 | elements = document.body 169 | } 170 | bindTapListener(elements, tapListeners, context) 171 | 172 | //dispose all these tap listeners when the context is started 173 | promise.then(function(){ 174 | for (var i = 0; i < tapListeners.length; i++){ 175 | tapListeners[i].dispose() 176 | } 177 | tapListeners = null 178 | 179 | if (callback){ 180 | callback() 181 | } 182 | }) 183 | 184 | return promise 185 | } 186 | 187 | return StartAudioContext 188 | })) -------------------------------------------------------------------------------- /src/lib/webvr/VRControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author dmarcos / https://github.com/dmarcos 3 | * @author mrdoob / http://mrdoob.com 4 | */ 5 | 6 | THREE.VRControls = function ( object, onError ) { 7 | 8 | var scope = this; 9 | 10 | var vrDisplay, vrDisplays; 11 | 12 | var standingMatrix = new THREE.Matrix4(); 13 | 14 | var frameData = null; 15 | 16 | if ( 'VRFrameData' in window ) { 17 | 18 | frameData = new VRFrameData(); 19 | 20 | } 21 | 22 | function gotVRDisplays( displays ) { 23 | 24 | vrDisplays = displays; 25 | 26 | if ( displays.length > 0 ) { 27 | 28 | vrDisplay = displays[ 0 ]; 29 | 30 | } else { 31 | 32 | if ( onError ) onError( 'VR input not available.' ); 33 | 34 | } 35 | 36 | } 37 | 38 | if ( navigator.getVRDisplays ) { 39 | 40 | navigator.getVRDisplays().then( gotVRDisplays ).catch ( function () { 41 | 42 | console.warn( 'THREE.VRControls: Unable to get VR Displays' ); 43 | 44 | } ); 45 | 46 | } 47 | 48 | // the Rift SDK returns the position in meters 49 | // this scale factor allows the user to define how meters 50 | // are converted to scene units. 51 | 52 | this.scale = 1; 53 | 54 | // If true will use "standing space" coordinate system where y=0 is the 55 | // floor and x=0, z=0 is the center of the room. 56 | this.standing = false; 57 | 58 | // Distance from the users eyes to the floor in meters. Used when 59 | // standing=true but the VRDisplay doesn't provide stageParameters. 60 | this.userHeight = 1.6; 61 | 62 | this.getVRDisplay = function () { 63 | 64 | return vrDisplay; 65 | 66 | }; 67 | 68 | this.setVRDisplay = function ( value ) { 69 | 70 | vrDisplay = value; 71 | 72 | }; 73 | 74 | this.getVRDisplays = function () { 75 | 76 | console.warn( 'THREE.VRControls: getVRDisplays() is being deprecated.' ); 77 | return vrDisplays; 78 | 79 | }; 80 | 81 | this.getStandingMatrix = function () { 82 | 83 | return standingMatrix; 84 | 85 | }; 86 | 87 | this.update = function () { 88 | 89 | if ( vrDisplay ) { 90 | 91 | var pose; 92 | 93 | if ( vrDisplay.getFrameData ) { 94 | 95 | vrDisplay.getFrameData( frameData ); 96 | pose = frameData.pose; 97 | 98 | } else if ( vrDisplay.getPose ) { 99 | 100 | pose = vrDisplay.getPose(); 101 | 102 | } 103 | 104 | if ( pose.orientation !== null ) { 105 | 106 | object.quaternion.fromArray( pose.orientation ); 107 | 108 | } 109 | 110 | if ( pose.position !== null ) { 111 | 112 | object.position.fromArray( pose.position ); 113 | 114 | } else { 115 | 116 | object.position.set( 0, 0, 0 ); 117 | 118 | } 119 | 120 | if ( this.standing ) { 121 | 122 | if ( vrDisplay.stageParameters ) { 123 | 124 | object.updateMatrix(); 125 | 126 | standingMatrix.fromArray( vrDisplay.stageParameters.sittingToStandingTransform ); 127 | object.applyMatrix( standingMatrix ); 128 | 129 | } else { 130 | 131 | object.position.setY( object.position.y + this.userHeight ); 132 | 133 | } 134 | 135 | } 136 | 137 | object.position.multiplyScalar( scope.scale ); 138 | 139 | } 140 | 141 | }; 142 | 143 | this.resetPose = function () { 144 | 145 | if ( vrDisplay ) { 146 | 147 | vrDisplay.resetPose(); 148 | 149 | } 150 | 151 | }; 152 | 153 | this.resetSensor = function () { 154 | 155 | console.warn( 'THREE.VRControls: .resetSensor() is now .resetPose().' ); 156 | this.resetPose(); 157 | 158 | }; 159 | 160 | this.zeroSensor = function () { 161 | 162 | console.warn( 'THREE.VRControls: .zeroSensor() is now .resetPose().' ); 163 | this.resetPose(); 164 | 165 | }; 166 | 167 | this.dispose = function () { 168 | 169 | vrDisplay = null; 170 | 171 | }; 172 | 173 | }; -------------------------------------------------------------------------------- /src/lib/webvr/VREffect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author dmarcos / https://github.com/dmarcos 3 | * @author mrdoob / http://mrdoob.com 4 | * 5 | * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html 6 | * 7 | * Firefox: http://mozvr.com/downloads/ 8 | * Chromium: https://webvr.info/get-chrome 9 | * 10 | */ 11 | 12 | THREE.VREffect = function( renderer, onError ) { 13 | 14 | var vrDisplay, vrDisplays; 15 | var eyeTranslationL = new THREE.Vector3(); 16 | var eyeTranslationR = new THREE.Vector3(); 17 | var renderRectL, renderRectR; 18 | 19 | var frameData = null; 20 | 21 | if ( 'VRFrameData' in window ) { 22 | 23 | frameData = new window.VRFrameData(); 24 | 25 | } 26 | 27 | function gotVRDisplays( displays ) { 28 | 29 | vrDisplays = displays; 30 | 31 | if ( displays.length > 0 ) { 32 | 33 | vrDisplay = displays[ 0 ]; 34 | 35 | } else { 36 | 37 | if ( onError ) onError( 'HMD not available' ); 38 | 39 | } 40 | 41 | } 42 | 43 | if ( navigator.getVRDisplays ) { 44 | 45 | navigator.getVRDisplays().then( gotVRDisplays ).catch( function() { 46 | 47 | console.warn( 'THREE.VREffect: Unable to get VR Displays' ); 48 | 49 | } ); 50 | 51 | } 52 | 53 | // 54 | 55 | this.isPresenting = false; 56 | this.scale = 1; 57 | 58 | var scope = this; 59 | 60 | var rendererSize = renderer.getSize(); 61 | var rendererUpdateStyle = false; 62 | var rendererPixelRatio = renderer.getPixelRatio(); 63 | 64 | this.getVRDisplay = function() { 65 | 66 | return vrDisplay; 67 | 68 | }; 69 | 70 | this.setVRDisplay = function( value ) { 71 | 72 | vrDisplay = value; 73 | 74 | }; 75 | 76 | this.getVRDisplays = function() { 77 | 78 | console.warn( 'THREE.VREffect: getVRDisplays() is being deprecated.' ); 79 | return vrDisplays; 80 | 81 | }; 82 | 83 | this.setSize = function( width, height, updateStyle ) { 84 | 85 | rendererSize = { width: width, height: height }; 86 | rendererUpdateStyle = updateStyle; 87 | 88 | if ( scope.isPresenting ) { 89 | 90 | var eyeParamsL = vrDisplay.getEyeParameters( 'left' ); 91 | renderer.setPixelRatio( 1 ); 92 | renderer.setSize( eyeParamsL.renderWidth * 2, eyeParamsL.renderHeight, false ); 93 | 94 | } else { 95 | 96 | renderer.setPixelRatio( rendererPixelRatio ); 97 | renderer.setSize( width, height, updateStyle ); 98 | 99 | } 100 | 101 | }; 102 | 103 | // VR presentation 104 | 105 | var canvas = renderer.domElement; 106 | var defaultLeftBounds = [ 0.0, 0.0, 0.5, 1.0 ]; 107 | var defaultRightBounds = [ 0.5, 0.0, 0.5, 1.0 ]; 108 | 109 | function onVRDisplayPresentChange() { 110 | 111 | var wasPresenting = scope.isPresenting; 112 | scope.isPresenting = vrDisplay !== undefined && vrDisplay.isPresenting; 113 | 114 | if ( scope.isPresenting ) { 115 | 116 | var eyeParamsL = vrDisplay.getEyeParameters( 'left' ); 117 | var eyeWidth = eyeParamsL.renderWidth; 118 | var eyeHeight = eyeParamsL.renderHeight; 119 | 120 | if ( ! wasPresenting ) { 121 | 122 | rendererPixelRatio = renderer.getPixelRatio(); 123 | rendererSize = renderer.getSize(); 124 | 125 | renderer.setPixelRatio( 1 ); 126 | renderer.setSize( eyeWidth * 2, eyeHeight, false ); 127 | 128 | } 129 | 130 | } else if ( wasPresenting ) { 131 | 132 | renderer.setPixelRatio( rendererPixelRatio ); 133 | renderer.setSize( rendererSize.width, rendererSize.height, rendererUpdateStyle ); 134 | 135 | } 136 | 137 | } 138 | 139 | window.addEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false ); 140 | 141 | this.setFullScreen = function( boolean ) { 142 | 143 | return new Promise( function( resolve, reject ) { 144 | 145 | if ( vrDisplay === undefined ) { 146 | 147 | reject( new Error( 'No VR hardware found.' ) ); 148 | return; 149 | 150 | } 151 | 152 | if ( scope.isPresenting === boolean ) { 153 | 154 | resolve(); 155 | return; 156 | 157 | } 158 | 159 | if ( boolean ) { 160 | 161 | resolve( vrDisplay.requestPresent( [ { source: canvas } ] ) ); 162 | 163 | } else { 164 | 165 | resolve( vrDisplay.exitPresent() ); 166 | 167 | } 168 | 169 | } ); 170 | 171 | }; 172 | 173 | this.requestPresent = function() { 174 | 175 | return this.setFullScreen( true ); 176 | 177 | }; 178 | 179 | this.exitPresent = function() { 180 | 181 | return this.setFullScreen( false ); 182 | 183 | }; 184 | 185 | this.requestAnimationFrame = function( f ) { 186 | 187 | if ( vrDisplay !== undefined ) { 188 | 189 | return vrDisplay.requestAnimationFrame( f ); 190 | 191 | } else { 192 | 193 | return window.requestAnimationFrame( f ); 194 | 195 | } 196 | 197 | }; 198 | 199 | this.cancelAnimationFrame = function( h ) { 200 | 201 | if ( vrDisplay !== undefined ) { 202 | 203 | vrDisplay.cancelAnimationFrame( h ); 204 | 205 | } else { 206 | 207 | window.cancelAnimationFrame( h ); 208 | 209 | } 210 | 211 | }; 212 | 213 | this.submitFrame = function() { 214 | 215 | if ( vrDisplay !== undefined && scope.isPresenting ) { 216 | 217 | vrDisplay.submitFrame(); 218 | 219 | } 220 | 221 | }; 222 | 223 | this.autoSubmitFrame = true; 224 | 225 | // render 226 | 227 | var cameraL = new THREE.PerspectiveCamera(); 228 | cameraL.layers.enable( 1 ); 229 | 230 | var cameraR = new THREE.PerspectiveCamera(); 231 | cameraR.layers.enable( 2 ); 232 | 233 | this.render = function( scene, camera, renderTarget, forceClear ) { 234 | 235 | if ( vrDisplay && scope.isPresenting ) { 236 | 237 | var autoUpdate = scene.autoUpdate; 238 | 239 | if ( autoUpdate ) { 240 | 241 | scene.updateMatrixWorld(); 242 | scene.autoUpdate = false; 243 | 244 | } 245 | 246 | var eyeParamsL = vrDisplay.getEyeParameters( 'left' ); 247 | var eyeParamsR = vrDisplay.getEyeParameters( 'right' ); 248 | 249 | eyeTranslationL.fromArray( eyeParamsL.offset ); 250 | eyeTranslationR.fromArray( eyeParamsR.offset ); 251 | 252 | if ( Array.isArray( scene ) ) { 253 | 254 | console.warn( 'THREE.VREffect.render() no longer supports arrays. Use object.layers instead.' ); 255 | scene = scene[ 0 ]; 256 | 257 | } 258 | 259 | // When rendering we don't care what the recommended size is, only what the actual size 260 | // of the backbuffer is. 261 | var size = renderer.getSize(); 262 | var layers = vrDisplay.getLayers(); 263 | var leftBounds; 264 | var rightBounds; 265 | 266 | if ( layers.length ) { 267 | 268 | var layer = layers[ 0 ]; 269 | 270 | leftBounds = layer.leftBounds !== null && layer.leftBounds.length === 4 ? layer.leftBounds : defaultLeftBounds; 271 | rightBounds = layer.rightBounds !== null && layer.rightBounds.length === 4 ? layer.rightBounds : defaultRightBounds; 272 | 273 | } else { 274 | 275 | leftBounds = defaultLeftBounds; 276 | rightBounds = defaultRightBounds; 277 | 278 | } 279 | 280 | renderRectL = { 281 | x: Math.round( size.width * leftBounds[ 0 ] ), 282 | y: Math.round( size.height * leftBounds[ 1 ] ), 283 | width: Math.round( size.width * leftBounds[ 2 ] ), 284 | height: Math.round( size.height * leftBounds[ 3 ] ) 285 | }; 286 | renderRectR = { 287 | x: Math.round( size.width * rightBounds[ 0 ] ), 288 | y: Math.round( size.height * rightBounds[ 1 ] ), 289 | width: Math.round( size.width * rightBounds[ 2 ] ), 290 | height: Math.round( size.height * rightBounds[ 3 ] ) 291 | }; 292 | 293 | if ( renderTarget ) { 294 | 295 | renderer.setRenderTarget( renderTarget ); 296 | renderTarget.scissorTest = true; 297 | 298 | } else { 299 | 300 | renderer.setRenderTarget( null ); 301 | renderer.setScissorTest( true ); 302 | 303 | } 304 | 305 | if ( renderer.autoClear || forceClear ) renderer.clear(); 306 | 307 | if ( camera.parent === null ) camera.updateMatrixWorld(); 308 | 309 | camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale ); 310 | camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale ); 311 | 312 | var scale = this.scale; 313 | cameraL.translateOnAxis( eyeTranslationL, scale ); 314 | cameraR.translateOnAxis( eyeTranslationR, scale ); 315 | 316 | if ( vrDisplay.getFrameData ) { 317 | 318 | vrDisplay.depthNear = camera.near; 319 | vrDisplay.depthFar = camera.far; 320 | 321 | vrDisplay.getFrameData( frameData ); 322 | 323 | cameraL.projectionMatrix.elements = frameData.leftProjectionMatrix; 324 | cameraR.projectionMatrix.elements = frameData.rightProjectionMatrix; 325 | 326 | } else { 327 | 328 | cameraL.projectionMatrix = fovToProjection( eyeParamsL.fieldOfView, true, camera.near, camera.far ); 329 | cameraR.projectionMatrix = fovToProjection( eyeParamsR.fieldOfView, true, camera.near, camera.far ); 330 | 331 | } 332 | 333 | // render left eye 334 | if ( renderTarget ) { 335 | 336 | renderTarget.viewport.set( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); 337 | renderTarget.scissor.set( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); 338 | 339 | } else { 340 | 341 | renderer.setViewport( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); 342 | renderer.setScissor( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); 343 | 344 | } 345 | renderer.render( scene, cameraL, renderTarget, forceClear ); 346 | 347 | // render right eye 348 | if ( renderTarget ) { 349 | 350 | renderTarget.viewport.set( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); 351 | renderTarget.scissor.set( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); 352 | 353 | } else { 354 | 355 | renderer.setViewport( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); 356 | renderer.setScissor( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); 357 | 358 | } 359 | renderer.render( scene, cameraR, renderTarget, forceClear ); 360 | 361 | if ( renderTarget ) { 362 | 363 | renderTarget.viewport.set( 0, 0, size.width, size.height ); 364 | renderTarget.scissor.set( 0, 0, size.width, size.height ); 365 | renderTarget.scissorTest = false; 366 | renderer.setRenderTarget( null ); 367 | 368 | } else { 369 | 370 | renderer.setViewport( 0, 0, size.width, size.height ); 371 | renderer.setScissorTest( false ); 372 | 373 | } 374 | 375 | if ( autoUpdate ) { 376 | 377 | scene.autoUpdate = true; 378 | 379 | } 380 | 381 | if ( scope.autoSubmitFrame ) { 382 | 383 | scope.submitFrame(); 384 | 385 | } 386 | 387 | return; 388 | 389 | } 390 | 391 | // Regular render mode if not HMD 392 | 393 | renderer.render( scene, camera, renderTarget, forceClear ); 394 | 395 | }; 396 | 397 | this.dispose = function() { 398 | 399 | window.removeEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false ); 400 | 401 | }; 402 | 403 | // 404 | 405 | function fovToNDCScaleOffset( fov ) { 406 | 407 | var pxscale = 2.0 / ( fov.leftTan + fov.rightTan ); 408 | var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5; 409 | var pyscale = 2.0 / ( fov.upTan + fov.downTan ); 410 | var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5; 411 | return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] }; 412 | 413 | } 414 | 415 | function fovPortToProjection( fov, rightHanded, zNear, zFar ) { 416 | 417 | rightHanded = rightHanded === undefined ? true : rightHanded; 418 | zNear = zNear === undefined ? 0.01 : zNear; 419 | zFar = zFar === undefined ? 10000.0 : zFar; 420 | 421 | var handednessScale = rightHanded ? - 1.0 : 1.0; 422 | 423 | // start with an identity matrix 424 | var mobj = new THREE.Matrix4(); 425 | var m = mobj.elements; 426 | 427 | // and with scale/offset info for normalized device coords 428 | var scaleAndOffset = fovToNDCScaleOffset( fov ); 429 | 430 | // X result, map clip edges to [-w,+w] 431 | m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ]; 432 | m[ 0 * 4 + 1 ] = 0.0; 433 | m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale; 434 | m[ 0 * 4 + 3 ] = 0.0; 435 | 436 | // Y result, map clip edges to [-w,+w] 437 | // Y offset is negated because this proj matrix transforms from world coords with Y=up, 438 | // but the NDC scaling has Y=down (thanks D3D?) 439 | m[ 1 * 4 + 0 ] = 0.0; 440 | m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ]; 441 | m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale; 442 | m[ 1 * 4 + 3 ] = 0.0; 443 | 444 | // Z result (up to the app) 445 | m[ 2 * 4 + 0 ] = 0.0; 446 | m[ 2 * 4 + 1 ] = 0.0; 447 | m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale; 448 | m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar ); 449 | 450 | // W result (= Z in) 451 | m[ 3 * 4 + 0 ] = 0.0; 452 | m[ 3 * 4 + 1 ] = 0.0; 453 | m[ 3 * 4 + 2 ] = handednessScale; 454 | m[ 3 * 4 + 3 ] = 0.0; 455 | 456 | mobj.transpose(); 457 | 458 | return mobj; 459 | 460 | } 461 | 462 | function fovToProjection( fov, rightHanded, zNear, zFar ) { 463 | 464 | var DEG2RAD = Math.PI / 180.0; 465 | 466 | var fovPort = { 467 | upTan: Math.tan( fov.upDegrees * DEG2RAD ), 468 | downTan: Math.tan( fov.downDegrees * DEG2RAD ), 469 | leftTan: Math.tan( fov.leftDegrees * DEG2RAD ), 470 | rightTan: Math.tan( fov.rightDegrees * DEG2RAD ) 471 | }; 472 | 473 | return fovPortToProjection( fovPort, rightHanded, zNear, zFar ); 474 | 475 | } 476 | 477 | }; -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zenvr", 3 | "version": "0.0.1", 4 | "description": "ZenVR by Eddie Lee (@eddietree)", 5 | "main": "main.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "eddietree", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "animitter": "latest", 13 | "babel-preset-es2015": "^6.14.0", 14 | "babelify": "^7.3.0", 15 | "browser-sync": "^2.11.2", 16 | "browserify": "^13.1.0", 17 | "dat-gui": "^0.5.0", 18 | "glslify": "^5.1.0", 19 | "gulp": "^3.9.1", 20 | "gulp-clean": "^0.3.2", 21 | "gulp-concat": "^2.6.0", 22 | "gulp-if": "^2.0.1", 23 | "gulp-jshint": "^2.0.0", 24 | "gulp-less": "^3.0.5", 25 | "gulp-notify": "^2.2.0", 26 | "gulp-rename": "^1.2.2", 27 | "gulp-sass": "^2.2.0", 28 | "gulp-uglify": "^1.5.3", 29 | "gulp-util": "^3.0.7", 30 | "jshint": "^2.9.1", 31 | "noisejs": "^2.1.0", 32 | "run-sequence": "^1.1.5", 33 | "startaudiocontext": "^1.2.0", 34 | "tone": "^0.7.1", 35 | "tween.js": "^16.3.5", 36 | "vinyl-buffer": "^1.0.0", 37 | "vinyl-source-stream": "^1.1.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/res/img/FinalBadge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/FinalBadge.png -------------------------------------------------------------------------------- /src/res/img/Web-VR_Lockup-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/Web-VR_Lockup-01.png -------------------------------------------------------------------------------- /src/res/img/btn-360.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/btn-360.png -------------------------------------------------------------------------------- /src/res/img/btn-vr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/btn-vr.png -------------------------------------------------------------------------------- /src/res/img/checker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/checker.png -------------------------------------------------------------------------------- /src/res/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/favicon.png -------------------------------------------------------------------------------- /src/res/img/ftl-logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/ftl-logo-white.png -------------------------------------------------------------------------------- /src/res/img/funktronic-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/funktronic-logo.png -------------------------------------------------------------------------------- /src/res/img/info-quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/info-quit.png -------------------------------------------------------------------------------- /src/res/img/info_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/info_btn.png -------------------------------------------------------------------------------- /src/res/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/logo.png -------------------------------------------------------------------------------- /src/res/img/meta-promo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/meta-promo.png -------------------------------------------------------------------------------- /src/res/img/perlin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/perlin.png -------------------------------------------------------------------------------- /src/res/img/prism-mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddietree/chromatic/0be44d6deb700b688991b52075d50014390179b7/src/res/img/prism-mask.png -------------------------------------------------------------------------------- /src/res/svg/FinalBadge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 12 | 13 | 17 | 21 | 25 | 26 | 29 | 32 | 34 | 37 | 39 | 43 | 45 | 49 | 53 | 56 | 60 | 64 | 68 | 71 | 74 | 79 | 83 | 86 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /src/res/svg/FinalBadgeBlack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | 12 | 16 | 19 | 23 | 24 | 27 | 30 | 32 | 35 | 37 | 41 | 43 | 47 | 50 | 53 | 57 | 60 | 63 | 66 | 69 | 74 | 77 | 80 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /src/res/svg/mlx_badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Hey ${USERNAME}! Now running the project..." 4 | gulp --color -------------------------------------------------------------------------------- /src/scss/main.scss: -------------------------------------------------------------------------------- 1 | @import 'reset'; 2 | 3 | * { 4 | -webkit-user-select: none; 5 | -khtml-user-select: none; 6 | -moz-user-select: -moz-none; 7 | -o-user-select: none; 8 | user-select: none; 9 | } 10 | 11 | 12 | body { 13 | margin: 0px; 14 | overflow: hidden; 15 | background-color: black; 16 | } 17 | 18 | canvas { 19 | -webkit-touch-callout: none; 20 | -webkit-user-select: none; /* Disable selection/copy in UIWebView */ 21 | } 22 | 23 | #putOnVR { 24 | position: fixed; 25 | top: 45%; 26 | left: 50%; 27 | transform: translate(-50%, -50%); 28 | color: white; 29 | font-size: 36px; 30 | display: none; 31 | text-align: center; 32 | } 33 | 34 | #putOnVR img { 35 | width: 50%; 36 | } 37 | 38 | #icons { 39 | background-color: yellow; 40 | display: block; 41 | } 42 | 43 | #iconwebvr img { 44 | position: fixed; 45 | left: 15px; 46 | bottom: 15px; 47 | 48 | width: 20%; 49 | min-width: 150px; 50 | max-width: 250px; 51 | } 52 | 53 | #iconfunk img { 54 | position: fixed; 55 | right: 15px; 56 | bottom: 15px; 57 | 58 | width: 20%; 59 | min-width: 70px; 60 | max-width: 150px; 61 | } 62 | 63 | #desc { 64 | //font-size: 0.9em; 65 | //font-size: 4vmin; 66 | font-size: calc(10px + .4vw); 67 | position: fixed; 68 | top: 70%; 69 | left: 50%; 70 | transform: translate(-50%, -50%); 71 | color: gray; 72 | text-align: center; 73 | line-height: 1.5em; 74 | font-family: 'Roboto Mono', monospace; 75 | 76 | width: 80%; 77 | } 78 | 79 | #desc p { 80 | margin: 0 auto; 81 | } 82 | 83 | a#desc-link { 84 | color: gray; 85 | display: block; 86 | margin-top: 0.5em; 87 | } 88 | 89 | #enter360 { 90 | margin-top: 0.5em; 91 | text-decoration: underline; 92 | color: rgb(10,100,255); 93 | font-size: calc(10px + .4vw); 94 | } 95 | 96 | #enter360:hover { 97 | cursor: pointer; 98 | color: rgb(20,255,255); 99 | } 100 | 101 | #btns { 102 | margin-top: 1.75em; 103 | //display: inline-block; 104 | //position: relative; 105 | } 106 | 107 | .webvr-ui-title { 108 | font-family: 'Roboto Mono', monospace; 109 | font-size: 1em; 110 | } 111 | 112 | /* 113 | .webvr-ui-button-description { 114 | font-family: 'Roboto Mono', monospace; 115 | font-size: 0.8em; 116 | width: 150px; 117 | line-height: 1.5em; 118 | margin-top: 1em; 119 | }*/ 120 | 121 | .btn { 122 | //float: left; 123 | display: inline-flex; 124 | margin: 0 0.5em; 125 | } 126 | 127 | #infobtn { 128 | position: fixed; 129 | right: 10px; 130 | top: 10px; 131 | } 132 | 133 | #infobtn img { 134 | width: 24px; 135 | } 136 | 137 | #infobtn-quit { 138 | position: fixed; 139 | right: 10px; 140 | top: 10px; 141 | } 142 | 143 | #infobtn-quit img { 144 | width: 24px; 145 | } 146 | 147 | $gradient-top: #e078bf; 148 | $gradient-bot: #6cb8fe; 149 | 150 | #infowall { 151 | position: fixed; 152 | 153 | background: -webkit-linear-gradient($gradient-top, $gradient-bot); /* For Safari 5.1 to 6.0 */ 154 | background: -o-linear-gradient($gradient-top, $gradient-bot); /* For Opera 11.1 to 12.0 */ 155 | background: -moz-linear-gradient($gradient-top, $gradient-bot); /* For Firefox 3.6 to 15 */ 156 | background: linear-gradient($gradient-top, $gradient-bot); /* Standard syntax */ 157 | 158 | //background-color: #324161; 159 | height: 100%; 160 | width: 100%; 161 | z-index: 50; 162 | display: none; 163 | 164 | font-family: 'Roboto Mono', monospace; 165 | } 166 | 167 | #infowall-content { 168 | position: fixed; 169 | top: 50%; 170 | left: 50%; 171 | transform: translate(-50%, -50%); 172 | color: white; 173 | text-align: center; 174 | } 175 | 176 | #infowall-content h1 { 177 | font-size: 3em; 178 | margin-bottom: 0.5em; 179 | } 180 | 181 | #infowall-content p { 182 | font-size: 0.7em; 183 | line-height: 1.5em; 184 | margin-top: 1.5em; 185 | } 186 | 187 | #infowall-content a { 188 | color: #00ffe7; 189 | } 190 | 191 | #stats { 192 | padding: 6px; 193 | position: fixed; 194 | top: 5px; 195 | left: 5px; 196 | color: white; 197 | background-color: black; 198 | display: none; 199 | font-family: monospace; 200 | border: 1px solid white; 201 | } 202 | 203 | #stats strong { 204 | font-weight: bolder; 205 | color: #aaaaaa; 206 | } 207 | 208 | #buttons { 209 | display: none; 210 | position: fixed; 211 | top: 5px; 212 | left: 5px; 213 | } 214 | #buttons button { 215 | background-color: black; 216 | color: white; 217 | padding: 5px; 218 | font-family: monospace; 219 | 220 | border-top: 1px solid #aaaaaa; 221 | border-left: 1px solid #aaaaaa; 222 | border-bottom: 1px solid #333333; 223 | border-right: 1px solid #333333; 224 | border-radius: 3px; 225 | } 226 | 227 | #buttons button:hover { 228 | background-color: #00aaaa; 229 | } 230 | 231 | #buttons button:active img { 232 | position: relative; 233 | bottom: -2px; 234 | } -------------------------------------------------------------------------------- /src/scss/reset.scss: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /src/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Hey ${USERNAME}!" 4 | echo "npm installing..." 5 | npm install 6 | 7 | echo "Now running the project..." 8 | gulp --color 9 | --------------------------------------------------------------------------------