├── src ├── fonts │ └── README.md ├── models │ └── README.md ├── sounds │ └── README.md ├── textures │ ├── corona.jpg │ ├── lenseFlare.jpg │ ├── lenseFlareSun.jpg │ ├── lenseFlareBlur.jpg │ ├── lenseFlareDisk.jpg │ ├── lenseFlareRing.jpg │ ├── lenseFlareSun copy.jpg │ └── lenseFlareSunDesat.jpg ├── js │ ├── shaders │ │ ├── custom.vert │ │ ├── texture.vert │ │ ├── normalMap.vert │ │ ├── glow.frag │ │ ├── custom.frag │ │ ├── noise.vert │ │ ├── glow.vert │ │ ├── noise.frag │ │ ├── planet.vert │ │ ├── roughnessMap.frag │ │ ├── textureMap.frag │ │ ├── atmosRing.frag │ │ ├── stars.frag │ │ ├── planet.frag │ │ ├── atmos.frag │ │ ├── noiseMap.frag │ │ ├── atmosRing.vert │ │ ├── normalMap.frag │ │ ├── texture.frag │ │ ├── flowNoiseMap.frag │ │ ├── cloudMap.frag │ │ └── nebula.frag │ ├── views │ │ ├── LoadingBar.js │ │ ├── RoughnessMap.js │ │ ├── TextureMap.js │ │ ├── NormalMap.js │ │ ├── CloudMap.js │ │ ├── RenderQueue.js │ │ ├── StarMap.js │ │ ├── NebulaMap.js │ │ ├── NoiseMap.js │ │ ├── Map.js │ │ ├── Glow.js │ │ ├── Stars.js │ │ ├── Atmosphere.js │ │ ├── Nebula.js │ │ ├── AbstractVRApplication.js │ │ ├── NebulaeGradient.js │ │ ├── AtmosphereRing.js │ │ ├── Sun.js │ │ ├── Clouds.js │ │ ├── AbstractApplication.js │ │ ├── SunTexture.js │ │ ├── atmosReference.js │ │ ├── Biome.js │ │ └── Planet.js │ ├── main.js │ ├── mainWagner.js │ └── mainVR.js ├── app.js └── css │ └── style.css ├── .gitignore ├── config ├── prod.env.js ├── dev.env.js └── index.js ├── dist.zip ├── .gitattributes ├── README.md ├── package.json └── index.html /src/fonts/README.md: -------------------------------------------------------------------------------- 1 | Your fonts here. -------------------------------------------------------------------------------- /src/models/README.md: -------------------------------------------------------------------------------- 1 | Your models here. -------------------------------------------------------------------------------- /src/sounds/README.md: -------------------------------------------------------------------------------- 1 | Your sounds here. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | *.log 3 | .DS_Store 4 | dist 5 | wwww 6 | .idea/ -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } -------------------------------------------------------------------------------- /dist.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colordodge/ProceduralPlanet/HEAD/dist.zip -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /src/textures/corona.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colordodge/ProceduralPlanet/HEAD/src/textures/corona.jpg -------------------------------------------------------------------------------- /src/textures/lenseFlare.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colordodge/ProceduralPlanet/HEAD/src/textures/lenseFlare.jpg -------------------------------------------------------------------------------- /src/textures/lenseFlareSun.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colordodge/ProceduralPlanet/HEAD/src/textures/lenseFlareSun.jpg -------------------------------------------------------------------------------- /src/js/shaders/custom.vert: -------------------------------------------------------------------------------- 1 | void main() { 2 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); 3 | } 4 | -------------------------------------------------------------------------------- /src/textures/lenseFlareBlur.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colordodge/ProceduralPlanet/HEAD/src/textures/lenseFlareBlur.jpg -------------------------------------------------------------------------------- /src/textures/lenseFlareDisk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colordodge/ProceduralPlanet/HEAD/src/textures/lenseFlareDisk.jpg -------------------------------------------------------------------------------- /src/textures/lenseFlareRing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colordodge/ProceduralPlanet/HEAD/src/textures/lenseFlareRing.jpg -------------------------------------------------------------------------------- /src/textures/lenseFlareSun copy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colordodge/ProceduralPlanet/HEAD/src/textures/lenseFlareSun copy.jpg -------------------------------------------------------------------------------- /src/textures/lenseFlareSunDesat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colordodge/ProceduralPlanet/HEAD/src/textures/lenseFlareSunDesat.jpg -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import 'css/style.css' 2 | //import Main from 'js/main' 3 | //import Main from 'js/mainWagner' 4 | import Main from 'js/main' 5 | 6 | new Main(); 7 | -------------------------------------------------------------------------------- /src/js/shaders/texture.vert: -------------------------------------------------------------------------------- 1 | varying vec2 vUv; 2 | 3 | void main() { 4 | vUv = uv; 5 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 6 | } 7 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) -------------------------------------------------------------------------------- /src/js/shaders/normalMap.vert: -------------------------------------------------------------------------------- 1 | varying vec2 vUv; 2 | 3 | void main() { 4 | vUv = uv; 5 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 6 | } 7 | -------------------------------------------------------------------------------- /src/js/shaders/glow.frag: -------------------------------------------------------------------------------- 1 | uniform vec3 glowColor; 2 | varying float intensity; 3 | 4 | void main() 5 | { 6 | vec3 glow = glowColor * intensity; 7 | gl_FragColor = vec4( glow, 1.0 ); 8 | } 9 | -------------------------------------------------------------------------------- /src/js/shaders/custom.frag: -------------------------------------------------------------------------------- 1 | import noise from 'glsl-noise/simplex/2d' 2 | 3 | void main() { 4 | 5 | float brightness = noise(gl_FragCoord.xx); 6 | 7 | gl_FragColor = vec4(vec3(brightness), 1.0); 8 | //gl_FragColor = vec4(#ffff00, 1.0); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Procedural Planets in WebGL using three.js 2 | 3 | [Live Demo](http://colordodge.com/ProceduralPlanet) 4 | 5 | [Gallery](https://imgur.com/a/OwfIwj4) 6 | 7 | ## How to install 8 | 9 | * Run `npm install` 10 | * Run `npm run dev` 11 | * Open http://localhost:8080 12 | -------------------------------------------------------------------------------- /src/js/shaders/noise.vert: -------------------------------------------------------------------------------- 1 | attribute vec4 position; 2 | attribute vec2 uv; 3 | uniform mat4 projectionMatrix; 4 | uniform mat4 modelViewMatrix; 5 | 6 | varying vec2 vUv; 7 | 8 | void main () { 9 | vUv = uv; 10 | gl_Position = projectionMatrix * modelViewMatrix * position; 11 | } -------------------------------------------------------------------------------- /src/js/shaders/glow.vert: -------------------------------------------------------------------------------- 1 | uniform vec3 viewVector; 2 | uniform float c; 3 | uniform float p; 4 | varying float intensity; 5 | 6 | void main() 7 | { 8 | vec3 vNormal = normalize( normalMatrix * normal ); 9 | vec3 vNormel = normalize( normalMatrix * viewVector ); 10 | intensity = pow( c - dot(vNormal, vNormel), p ); 11 | 12 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 13 | } 14 | -------------------------------------------------------------------------------- /src/js/shaders/noise.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | // glslify fancy imports 4 | import noise from 'glsl-noise/simplex/3d'; 5 | 6 | varying vec2 vUv; 7 | 8 | void main () { 9 | float n = noise(vec3(vUv.xy * 10.0, 1.0)); 10 | n = smoothstep(0.0, 0.1, n); 11 | 12 | // glslify-hex allows for the color strings 13 | vec3 color = mix(vec3(1.0,0.0,1.0), vec3(0.0,1.0,0.0), n); 14 | gl_FragColor = vec4(color, 1.0); 15 | } 16 | -------------------------------------------------------------------------------- /src/js/shaders/planet.vert: -------------------------------------------------------------------------------- 1 | 2 | varying vec3 vNormal; 3 | varying vec3 vPosition; 4 | varying vec3 cameraVector; 5 | 6 | uniform float time; 7 | 8 | void main() { 9 | // gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); 10 | 11 | // vPosition = normalize(position) * scale; 12 | vPosition = position; 13 | vNormal = normal; 14 | cameraVector = cameraPosition - vPosition; 15 | 16 | // vec3 newPosition = position + normal * amplitude * displacement; 17 | vec3 newPosition = position; 18 | gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 ); 19 | } 20 | -------------------------------------------------------------------------------- /src/js/shaders/roughnessMap.frag: -------------------------------------------------------------------------------- 1 | 2 | varying vec2 vUv; 3 | uniform float resolution; 4 | uniform sampler2D heightMap; 5 | uniform float waterLevel; 6 | 7 | float getBrightness(vec4 color) { 8 | return 1.0 - (0.2126*color.r + 0.7152*color.g + 0.0722*color.b); 9 | } 10 | 11 | void main() { 12 | 13 | float x = vUv.x; 14 | float y = vUv.y; 15 | 16 | float pixelSize = 1.0 / resolution; 17 | 18 | float n = texture2D(heightMap, vec2(x, y)).r; 19 | float roughness = 0.0; 20 | 21 | if (n < waterLevel) { 22 | roughness = 0.75; 23 | } else { 24 | roughness = 0.9; 25 | } 26 | 27 | gl_FragColor = vec4(vec3(roughness), 1.0); 28 | } 29 | -------------------------------------------------------------------------------- /src/js/views/LoadingBar.js: -------------------------------------------------------------------------------- 1 | 2 | export default class LoadingBar { 3 | 4 | constructor() { 5 | this.base = document.createElement("div"); 6 | this.base.id = "loadingBarHolder"; 7 | this.base.innerHTML = 'Rendering
'; 8 | } 9 | 10 | update(progress) { 11 | this.bar = document.getElementById("loadingBar"); 12 | this.bar.style.width = "" + progress * 100 + "%"; 13 | } 14 | 15 | show() { 16 | document.body.appendChild(this.base); 17 | this.bar = document.getElementById("loadingBar"); 18 | this.bar.style.width = "0%"; 19 | } 20 | 21 | hide() { 22 | document.body.removeChild(this.base); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/js/shaders/textureMap.frag: -------------------------------------------------------------------------------- 1 | import noise from 'glsl-noise/classic/4d' 2 | 3 | varying vec2 vUv; 4 | 5 | uniform sampler2D biomeMap; 6 | uniform sampler2D heightMap; 7 | uniform sampler2D moistureMap; 8 | 9 | void main() { 10 | float x = vUv.x; 11 | float y = vUv.y; 12 | 13 | float n1 = texture2D(heightMap, vec2(x, y)).r; 14 | float n2 = texture2D(moistureMap, vec2(x, y)).r; 15 | 16 | vec4 color = texture2D(biomeMap, vec2(n2, n1)); 17 | 18 | // if (n1 < 0.5) { 19 | // vec4 water = vec4(0.0, 0.4, 0.9, 1.0); 20 | // // vec4 water = vec4(1.0, 0.2, 0.0, 1.0); 21 | // color = mix(water, color, 0.3); 22 | // } 23 | // 24 | // if (n1 < 0.5 && n1 > 0.4) { 25 | // vec4 coast = vec4(vec3(1.0), 1.0); 26 | // float amount = 1.0 - ((0.5 - n1) * 10.0); 27 | // color = mix(color, coast, amount*0.3); 28 | // } 29 | 30 | // color = vec4(1.0); 31 | 32 | gl_FragColor = color; 33 | } 34 | -------------------------------------------------------------------------------- /src/js/shaders/atmosRing.frag: -------------------------------------------------------------------------------- 1 | 2 | // Source: http://jsfiddle.net/VsWb9/770/ 3 | 4 | // 5 | // Atmospheric scattering fragment shader 6 | // 7 | // Author: Sean O'Neil 8 | // 9 | // Copyright (c) 2004 Sean O'Neil 10 | // 11 | 12 | uniform vec3 v3LightPos; 13 | uniform float g; 14 | uniform float g2; 15 | uniform vec3 atmosphereColor; 16 | 17 | varying vec3 v3Direction; 18 | varying vec3 c0; 19 | varying vec3 c1; 20 | 21 | // Calculates the Mie phase function 22 | float getMiePhase(float fCos, float fCos2, float g, float g2) 23 | { 24 | return 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(1.0 + g2 - 2.0 * g * fCos, 1.5); 25 | } 26 | 27 | // Calculates the Rayleigh phase function 28 | float getRayleighPhase(float fCos2) 29 | { 30 | return 0.75 + 0.75 * fCos2; 31 | } 32 | 33 | void main (void) 34 | { 35 | float fCos = dot(v3LightPos, v3Direction) / length(v3Direction); 36 | float fCos2 = fCos * fCos; 37 | 38 | vec3 color = getRayleighPhase(fCos2) * c0 + getMiePhase(fCos, fCos2, g, g2) * c1; 39 | 40 | gl_FragColor = vec4(color, 1.0); 41 | gl_FragColor.a = gl_FragColor.b; 42 | } 43 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: '/', 11 | productionSourceMap: true, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'] 18 | }, 19 | dev: { 20 | env: require('./dev.env'), 21 | port: 8080, 22 | assetsSubDirectory: 'static', 23 | assetsPublicPath: '/', 24 | proxyTable: {}, 25 | // CSS Sourcemaps off by default because relative paths are "buggy" 26 | // with this option, according to the CSS-Loader README 27 | // (https://github.com/webpack/css-loader#sourcemaps) 28 | // In our experience, they generally work as expected, 29 | // just be aware of this issue when enabling this option. 30 | cssSourceMap: false 31 | } 32 | } -------------------------------------------------------------------------------- /src/js/views/RoughnessMap.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import vertShader from 'shaders/texture.vert' 3 | import fragShader from 'shaders/roughnessMap.frag' 4 | import Map from 'views/Map.js' 5 | 6 | class RoughnessMap extends Map { 7 | 8 | constructor() { 9 | super(); 10 | this.setup(); 11 | super.setup(); 12 | } 13 | 14 | setup() { 15 | this.mats = []; 16 | 17 | for (let i = 0; i < 6; i++) { 18 | this.mats[i] = new THREE.ShaderMaterial({ 19 | uniforms: { 20 | resolution: {type: "f", value: 0}, 21 | waterLevel: {type: "f", value: 0}, 22 | heightMap: {type: "t", value: new THREE.Texture()} 23 | }, 24 | vertexShader: vertShader, 25 | fragmentShader: fragShader, 26 | transparent: true, 27 | depthWrite: false 28 | }); 29 | } 30 | } 31 | 32 | render(props) { 33 | // props.resolution 34 | // props.heightMaps[] 35 | // props.waterLevel 36 | 37 | for (let i = 0; i < 6; i++) { 38 | this.mats[i].uniforms.resolution.value = props.resolution; 39 | this.mats[i].uniforms.waterLevel.value = props.waterLevel; 40 | this.mats[i].uniforms.heightMap.value = props.heightMaps[i]; 41 | this.mats[i].needsUpdate = true; 42 | } 43 | 44 | super.render(props); 45 | } 46 | 47 | } 48 | 49 | export default RoughnessMap; 50 | -------------------------------------------------------------------------------- /src/js/views/TextureMap.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import vertShader from 'shaders/texture.vert' 3 | import fragShader from 'shaders/textureMap.frag' 4 | import Map from 'views/Map.js' 5 | 6 | class TextureMap extends Map { 7 | 8 | constructor() { 9 | super(); 10 | this.setup(); 11 | super.setup(); 12 | } 13 | 14 | setup() { 15 | this.mats = []; 16 | 17 | for (let i = 0; i < 6; i++) { 18 | this.mats[i] = new THREE.ShaderMaterial({ 19 | uniforms: { 20 | biomeMap: {type: "t", value: new THREE.Texture()}, 21 | heightMap: {type: "t", value: new THREE.Texture()}, 22 | moistureMap: {type: "t", value: new THREE.Texture()} 23 | }, 24 | vertexShader: vertShader, 25 | fragmentShader: fragShader, 26 | transparent: true, 27 | depthWrite: false 28 | }); 29 | } 30 | } 31 | 32 | render(props) { 33 | // props.resolution 34 | // props.heightMaps[] 35 | // props.moistureMaps[] 36 | // props.biomeMap 37 | 38 | let resolution = props.resolution; 39 | 40 | for (let i = 0; i < 6; i++) { 41 | 42 | this.mats[i].uniforms.heightMap.value = props.heightMaps[i]; 43 | this.mats[i].uniforms.moistureMap.value = props.moistureMaps[i]; 44 | this.mats[i].uniforms.biomeMap.value = props.biomeMap; 45 | this.mats[i].needsUpdate = true; 46 | } 47 | 48 | super.render(props); 49 | } 50 | 51 | } 52 | 53 | export default TextureMap; 54 | -------------------------------------------------------------------------------- /src/js/views/NormalMap.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import vertShader from 'shaders/normalMap.vert' 3 | import fragShader from 'shaders/normalMap.frag' 4 | import Map from 'views/Map.js' 5 | 6 | class NormalMap extends Map{ 7 | 8 | constructor() { 9 | super(); 10 | this.setup(); 11 | super.setup(); 12 | } 13 | 14 | setup() { 15 | this.mats = []; 16 | 17 | for (let i = 0; i < 6; i++) { 18 | this.mats[i] = new THREE.ShaderMaterial({ 19 | uniforms: { 20 | resolution: {type: "f", value: 0}, 21 | waterLevel: {type: "f", value: 0}, 22 | heightMap: {type: "t", value: new THREE.Texture()}, 23 | textureMap: {type: "t", value: new THREE.Texture()} 24 | }, 25 | vertexShader: vertShader, 26 | fragmentShader: fragShader, 27 | transparent: true, 28 | depthWrite: false 29 | }); 30 | } 31 | } 32 | 33 | render(props) { 34 | // props.resolution 35 | // props.heightMaps[] 36 | // props.textureMaps[] 37 | // props.waterLevel 38 | 39 | for (let i = 0; i < 6; i++) { 40 | this.mats[i].uniforms.resolution.value = props.resolution; 41 | this.mats[i].uniforms.waterLevel.value = props.waterLevel; 42 | this.mats[i].uniforms.heightMap.value = props.heightMaps[i]; 43 | this.mats[i].uniforms.textureMap.value = props.textureMaps[i]; 44 | this.mats[i].needsUpdate = true; 45 | } 46 | 47 | super.render(props); 48 | } 49 | 50 | } 51 | 52 | export default NormalMap; 53 | -------------------------------------------------------------------------------- /src/js/views/CloudMap.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import vertShader from 'shaders/texture.vert' 3 | import fragShader from 'shaders/cloudMap.frag' 4 | import Map from 'views/Map.js' 5 | 6 | class CloudMap extends Map { 7 | 8 | constructor() { 9 | super(); 10 | this.setup(); 11 | super.setup(); 12 | } 13 | 14 | setup() { 15 | this.mats = []; 16 | 17 | for (let i = 0; i < 6; i++) { 18 | this.mats[i] = new THREE.ShaderMaterial({ 19 | uniforms: { 20 | index: {type: "i", value: i}, 21 | seed: {type: "f", value: 0}, 22 | resolution: {type: "f", value: 0}, 23 | res1: {type: "f", value: 0}, 24 | res2: {type: "f", value: 0}, 25 | resMix: {type: "f", value: 0}, 26 | mixScale: {type: "f", value: 0} 27 | }, 28 | vertexShader: vertShader, 29 | fragmentShader: fragShader, 30 | transparent: true, 31 | depthWrite: false 32 | }); 33 | } 34 | } 35 | 36 | render(props) { 37 | // props.seed 38 | // props.resolution 39 | // props.res1 40 | // props.res2 41 | // props.resMix 42 | // props.mixScale 43 | 44 | for (let i = 0; i < 6; i++) { 45 | this.mats[i].uniforms.seed.value = props.seed; 46 | this.mats[i].uniforms.resolution.value = props.resolution; 47 | this.mats[i].uniforms.res1.value = props.res1; 48 | this.mats[i].uniforms.res2.value = props.res2; 49 | this.mats[i].uniforms.resMix.value = props.resMix; 50 | this.mats[i].uniforms.mixScale.value = props.mixScale; 51 | this.mats[i].needsUpdate = true; 52 | } 53 | 54 | super.render(props); 55 | } 56 | 57 | 58 | } 59 | 60 | export default CloudMap; 61 | -------------------------------------------------------------------------------- /src/js/views/RenderQueue.js: -------------------------------------------------------------------------------- 1 | import LoadingBar from 'views/LoadingBar.js' 2 | 3 | class RenderQueue { 4 | 5 | constructor() { 6 | this.actions = []; 7 | this.callbacks = []; 8 | this.skipFrame = false; 9 | this.lastTime = 0; 10 | 11 | this.totalActions = 0; 12 | this.loadingBar = new LoadingBar(); 13 | } 14 | 15 | start() { 16 | this.startFrame = true; 17 | this.lastTime = Date.now(); 18 | this.loadingBar.show(); 19 | } 20 | 21 | update() { 22 | if (this.startFrame == false) { 23 | if (this.actions.length > 0) { 24 | this.doNextAction(); 25 | } else { 26 | this.checkCallbacks(); 27 | } 28 | } else { 29 | this.startFrame = false; 30 | // first frame after actions added 31 | this.totalActions = this.actions.length; 32 | } 33 | } 34 | 35 | doNextAction() { 36 | 37 | let thisTime = Date.now(); 38 | let totalTime = thisTime - this.lastTime; 39 | 40 | this.lastTime = thisTime; 41 | 42 | this.actions[0](); 43 | this.actions.shift(); 44 | 45 | let progress = this.actions.length / this.totalActions; 46 | progress = 1.0 - progress; 47 | this.loadingBar.update(progress); 48 | } 49 | 50 | addAction(action) { 51 | this.actions.push(action); 52 | } 53 | 54 | addCallback(callback) { 55 | this.callbacks.push(callback); 56 | } 57 | 58 | checkCallbacks() { 59 | if (this.callbacks.length > 0) { 60 | this.executeCallbacks(); 61 | } 62 | } 63 | 64 | executeCallbacks() { 65 | for (let i=0; i { 39 | this.textures[i].setSize(resolution, resolution); 40 | this.textures[i].needsUpdate = true; 41 | this.textureCameras[i].left = -resolution/2; 42 | this.textureCameras[i].right = resolution/2; 43 | this.textureCameras[i].top = resolution/2; 44 | this.textureCameras[i].bottom = -resolution/2; 45 | this.textureCameras[i].updateProjectionMatrix(); 46 | this.geos[i] = new THREE.PlaneGeometry(resolution, resolution); 47 | this.planes[i].geometry = this.geos[i]; 48 | window.renderer.render(this.textureScenes[i], this.textureCameras[i], this.textures[i], true); 49 | this.geos[i].dispose(); 50 | 51 | }); 52 | 53 | } 54 | } 55 | 56 | } 57 | 58 | export default Map; 59 | -------------------------------------------------------------------------------- /src/js/views/Glow.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import shaderVert from 'shaders/glow.vert' 3 | import shaderFrag from 'shaders/glow.frag' 4 | 5 | class Glow { 6 | 7 | constructor() { 8 | this.view = new THREE.Object3D(); 9 | 10 | this.randomizeColor(); 11 | 12 | this.size = 1030; 13 | this.glow = 1.0; 14 | 15 | this.c = 1.0; 16 | this.p = 1.4; 17 | 18 | // window.gui.add(this, "glow", 0.0, 1.0); 19 | 20 | let glowFolder = window.gui.addFolder('Glow'); 21 | glowFolder.add(this, "c", 0, 1).step(0.01); 22 | glowFolder.add(this, "p", 0, 6).step(0.01); 23 | 24 | 25 | 26 | this.mat = new THREE.ShaderMaterial({ 27 | vertexShader: shaderVert, 28 | fragmentShader: shaderFrag, 29 | uniforms: { 30 | "c": { type: "f", value: 1.0 }, 31 | "p": { type: "f", value: 1.4 }, 32 | glowColor: { type: "c", value: new THREE.Color(0x00ffff) }, 33 | viewVector: { type: "v3", value: window.camera.position } 34 | } 35 | }); 36 | 37 | this.mat.transparent = true; 38 | this.mat.blending = THREE.AdditiveBlending; 39 | this.mat.side = THREE.BackSide; 40 | 41 | // this.mat = new THREE.MeshStandardMaterial({color: 0xFFFFFF}); 42 | 43 | this.geo = new THREE.IcosahedronBufferGeometry(1, 6); 44 | this.sphere = new THREE.Mesh(this.geo, this.mat); 45 | this.sphere.scale.set(this.size, this.size, this.size); 46 | this.view.add(this.sphere); 47 | } 48 | 49 | update() { 50 | this.mat.uniforms.c.value = this.c; 51 | this.mat.uniforms.p.value = this.p; 52 | this.mat.uniforms.viewVector.value = new THREE.Vector3().subVectors( window.camera.position, this.sphere.position ); 53 | } 54 | 55 | randomize() { 56 | this.randomizeColor(); 57 | this.mat.uniforms.color.value = this.color; 58 | } 59 | 60 | randomizeColor() { 61 | this.color = new THREE.Color(); 62 | this.color.r = window.rng(); 63 | this.color.g = window.rng(); 64 | this.color.b = window.rng(); 65 | } 66 | } 67 | 68 | export default Glow; 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "node build/dev-server.js", 4 | "build": "node build/build.js" 5 | }, 6 | "license": "WTFPL", 7 | "devDependencies": { 8 | "babel-core": "^6.21.0", 9 | "babel-loader": "^6.2.10", 10 | "babel-plugin-glslify": "^2.0.0", 11 | "babel-preset-es2015": "^6.18.0", 12 | "babelify": "^7.3.0", 13 | "browserify": "^14.3.0", 14 | "copy-webpack-plugin": "^4.0.1", 15 | "css-loader": "^0.28.2", 16 | "exports-loader": "^0.6.4", 17 | "extract-text-webpack-plugin": "^2.1.0", 18 | "glslify": "^5.1.0", 19 | "glslify-loader": "^1.0.2", 20 | "html-webpack-plugin": "^2.26.0", 21 | "ify-loader": "^1.0.4", 22 | "json-loader": "^0.5.4", 23 | "ora": "^1.2.0", 24 | "raw-loader": "^0.5.1", 25 | "shelljs": "^0.7.7", 26 | "style-loader": "^0.18.1", 27 | "webpack": "^2.2.0", 28 | "webpack-dev-server": "^2.2.0", 29 | "webpack-hot-middleware": "^2.18.0", 30 | "webpack-merge": "^4.1.0" 31 | }, 32 | "dependencies": { 33 | "@superguigui/wagner": "^0.1.5", 34 | "crypto-random-string": "^1.0.0", 35 | "dat-gui": "^0.5.0", 36 | "dat.gui": "^0.6.1", 37 | "glsl-checker": "^1.0.1", 38 | "glsl-noise": "0.0.0", 39 | "glsl-worley": "^1.0.2", 40 | "glslify": "^5.0.0", 41 | "glslify-fancy-imports": "^1.0.1", 42 | "glslify-hex": "^2.0.1", 43 | "random-lorem": "^1.0.4", 44 | "seedrandom": "^2.4.3", 45 | "serve": "^6.4.8", 46 | "simplex-noise": "^2.3.0", 47 | "stats-js": "^1.0.0-alpha1", 48 | "three": "^0.85.2", 49 | "three.meshline": "^1.0.3", 50 | "tinycolor2": "^1.4.1", 51 | "webvr-polyfill": "^0.9.29", 52 | "webvr-ui": "^0.9.4" 53 | }, 54 | "browserify": { 55 | "transform": [ 56 | [ 57 | "babelify", 58 | { 59 | "presets": [ 60 | "es2015" 61 | ], 62 | "plugins": [ 63 | "glslify" 64 | ] 65 | } 66 | ], 67 | "glslify" 68 | ] 69 | }, 70 | "glslify": { 71 | "transform": [ 72 | "glslify-fancy-imports", 73 | "glslify-hex" 74 | ] 75 | }, 76 | "engines": { 77 | "node": ">= 4.0.0", 78 | "npm": ">= 3.0.0" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/js/main.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import WAGNER from '@superguigui/wagner/' 3 | import MultiPassBloomPass from '@superguigui/wagner/src/passes/bloom/MultiPassBloomPass' 4 | import GodrayPass from '@superguigui/wagner/src/passes/godray/godraypass' 5 | import AbstractApplication from 'views/AbstractApplication' 6 | import shaderVert from 'shaders/custom.vert' 7 | import shaderFrag from 'shaders/custom.frag' 8 | import Planet from 'views/Planet' 9 | import RenderQueue from 'views/RenderQueue' 10 | 11 | 12 | class Main extends AbstractApplication { 13 | 14 | constructor(){ 15 | super(); 16 | 17 | // texture loading example 18 | // var texture = new THREE.TextureLoader().load( 'assets/textures/crate.gif' ); 19 | 20 | // this.initPostprocessing(); 21 | 22 | this.createBrandTag(); 23 | 24 | window.renderQueue = new RenderQueue(); 25 | 26 | this.planet = new Planet(); 27 | this.scene.add(this.planet.view); 28 | 29 | this.animate(); 30 | } 31 | 32 | initPostprocessing() { 33 | this._renderer.autoClearColor = true; 34 | this.composer = new WAGNER.Composer(this._renderer); 35 | this.bloomPass = new MultiPassBloomPass({ 36 | blurAmount: 3, 37 | applyZoomBlur: false 38 | }); 39 | this.godrayPass = new GodrayPass(); 40 | 41 | let folder = window.gui.addFolder("Post Processing"); 42 | this.bloom = true; 43 | folder.add(this, "bloom"); 44 | folder.add(this.bloomPass.params, "blurAmount", 0, 5); 45 | 46 | 47 | } 48 | 49 | createBrandTag() { 50 | let a = document.createElement("a"); 51 | a.href = "http://www.colordodge.com"; 52 | a.innerHTML = "
Colordodge
"; 53 | document.body.appendChild(a); 54 | } 55 | 56 | animate() { 57 | super.animate(); 58 | 59 | window.renderQueue.update(); 60 | this.planet.update(); 61 | 62 | // if (this.bloom) { 63 | // this.composer.reset(); 64 | // this.composer.render(this._scene, this._camera); 65 | // // this.composer.pass(this.bloomPass); 66 | // this.composer.pass(this.godrayPass); 67 | // this.composer.toScreen(); 68 | // } 69 | 70 | 71 | 72 | } 73 | 74 | } 75 | 76 | export default Main; 77 | -------------------------------------------------------------------------------- /src/js/shaders/stars.frag: -------------------------------------------------------------------------------- 1 | import noise from 'glsl-noise/classic/3d' 2 | import worley3D from 'glsl-worley/worley3D' 3 | 4 | varying vec2 vUv; 5 | uniform int index; 6 | uniform float seed; 7 | uniform float resolution; 8 | uniform float res1; 9 | uniform float res2; 10 | uniform float resMix; 11 | uniform float mixScale; 12 | uniform sampler2D nebulaeMap; 13 | 14 | 15 | vec3 getSphericalCoord(int index, float x, float y, float width) { 16 | width /= 2.0; 17 | x -= width; 18 | y -= width; 19 | vec3 coord = vec3(0.0, 0.0, 0.0); 20 | 21 | if (index == 0) {coord.x=width; coord.y=-y; coord.z=-x;} 22 | else if (index == 1) {coord.x=-width; coord.y=-y; coord.z=x;} 23 | else if (index == 2) {coord.x=x; coord.y=width; coord.z=y;} 24 | else if (index == 3) {coord.x=x; coord.y=-width; coord.z=-y;} 25 | else if (index == 4) {coord.x=x; coord.y=-y; coord.z=width;} 26 | else if (index == 5) {coord.x=-x; coord.y=-y; coord.z=-width;} 27 | 28 | return normalize(coord); 29 | } 30 | 31 | 32 | float simplex(vec3 pos, float seed) { 33 | float n = noise(vec3(pos + seed)); 34 | // return (n + 1.0) * 0.5; 35 | n = (n + 1.0) * 0.5; 36 | // n = 2.0 * (0.5 - abs(0.5 - n)); 37 | return n; 38 | } 39 | 40 | float baseNoise(vec3 pos, float frq, float seed ) { 41 | const int octaves = 16; 42 | float amp = 0.5; 43 | 44 | float n = 0.0; 45 | float gain = 1.0; 46 | for(int i=0; i 25 |
26 | Try it without a headset 27 | ` 28 | document.body.insertAdjacentHTML( 'beforeend', uiVR ); 29 | 30 | const uiOptions = { 31 | color: 'black', 32 | background: 'white', 33 | corners: 'square' 34 | }; 35 | 36 | this._vrButton = new webvrui.EnterVRButton(this._renderer.domElement, uiOptions); 37 | this._vrButton.on('exit', () => { 38 | this._camera.quaternion.set(0, 0, 0, 1); 39 | this._camera.position.set(0, this._controls.userHeight, 0); 40 | }); 41 | this._vrButton.on('hide', function() { 42 | document.getElementById('ui').style.display = 'none'; 43 | }); 44 | this._vrButton.on('show', function() { 45 | document.getElementById('ui').style.display = 'inherit'; 46 | }); 47 | document.getElementById('vr-button').appendChild(this._vrButton.domElement); 48 | document.getElementById('magic-window').addEventListener('click', () => { 49 | this._vrButton.requestEnterFullscreen(); 50 | }); 51 | 52 | 53 | this._effect = new THREE.VREffect(this._renderer); 54 | this._effect.setSize(window.innerWidth, window.innerHeight); 55 | 56 | window.addEventListener( 'resize', this.onWindowResize.bind(this), false ); 57 | 58 | } 59 | 60 | get renderer(){ 61 | 62 | return this._renderer; 63 | 64 | } 65 | 66 | get camera(){ 67 | 68 | return this._camera; 69 | 70 | } 71 | 72 | get scene(){ 73 | 74 | return this._scene; 75 | 76 | } 77 | 78 | get effect(){ 79 | 80 | return this._effect; 81 | 82 | } 83 | 84 | get controls(){ 85 | 86 | return this._controls; 87 | 88 | } 89 | 90 | 91 | onWindowResize() { 92 | 93 | this._camera.aspect = window.innerWidth / window.innerHeight; 94 | this._camera.updateProjectionMatrix(); 95 | 96 | this._renderer.setSize( window.innerWidth, window.innerHeight ); 97 | 98 | } 99 | 100 | animate(timestamp) { 101 | this._effect.requestAnimationFrame( this.animate.bind(this) ); 102 | this._controls.update(); 103 | this._effect.render(this._scene, this._camera); 104 | 105 | } 106 | 107 | 108 | } 109 | export default AbstractVRApplication; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Procedural Planet 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/js/views/NebulaeGradient.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | 4 | class NebulaeGradient { 5 | 6 | constructor() { 7 | 8 | this.canvas = document.createElement("canvas"); 9 | this.canvas.id = "nebulaeCanvas"; 10 | this.canvas.width = 512; 11 | this.canvas.height = 512; 12 | this.canvas.style.width = "200px"; 13 | this.canvas.style.height = "200px"; 14 | this.width = this.canvas.width; 15 | this.height = this.canvas.height; 16 | this.ctx = this.canvas.getContext("2d"); 17 | 18 | document.body.appendChild(this.canvas); 19 | this.toggleCanvasDisplay(false); 20 | } 21 | 22 | generateTexture() { 23 | 24 | let h = this.randRange(0.0, 1.0); 25 | let s = this.randRange(0.2, 0.8); 26 | let l = this.randRange(0.2, 0.6); 27 | this.baseColor = new THREE.Color().setHSL(h, s, l); 28 | this.colorAngle = this.randRange(0.0, 0.5); 29 | 30 | this.fillBaseColor(); 31 | this.drawShapes(); 32 | 33 | this.texture = new THREE.CanvasTexture(this.canvas); 34 | 35 | } 36 | 37 | toggleCanvasDisplay(value) { 38 | if (value) { 39 | this.canvas.style.display = "block"; 40 | } else { 41 | this.canvas.style.display = "none"; 42 | } 43 | } 44 | 45 | fillBaseColor() { 46 | this.ctx.fillStyle = this.toCanvasColor(this.baseColor); 47 | this.ctx.fillRect(0, 0, this.width, this.height); 48 | } 49 | 50 | drawShapes() { 51 | let numCircles = Math.round(this.randRange(20, 50)); 52 | for (let i=0; i 0.6) { 11 | // bright *= 0.3; 12 | // } 13 | return bright; 14 | } 15 | 16 | void main() { 17 | 18 | float x = vUv.x; 19 | float y = vUv.y; 20 | // float x = gl_FragCoord.x / 1024.0; 21 | // float y = gl_FragCoord.y / 1024.0; 22 | float pixelSize = 1.0 / resolution; 23 | 24 | 25 | // Value from trial & error. 26 | // Seems to work fine for the scales we are dealing with. 27 | // float strength = scale.Y / 16; 28 | float strength = 0.8; 29 | 30 | 31 | float level = texture2D(heightMap, vec2(x, y)).r; 32 | float water = waterLevel; 33 | // float mod = 1.0; 34 | // if (level < water) { 35 | // mod = 0.1; 36 | // } 37 | 38 | 39 | 40 | float tl = getBrightness(texture2D(textureMap, vec2(x-pixelSize, y-pixelSize))); 41 | float l = getBrightness(texture2D(textureMap, vec2(x-pixelSize, y))); 42 | float bl = getBrightness(texture2D(textureMap, vec2(x-pixelSize, y+pixelSize))); 43 | float b = getBrightness(texture2D(textureMap, vec2(x, y+pixelSize))); 44 | float br = getBrightness(texture2D(textureMap, vec2(x+pixelSize, y+pixelSize))); 45 | float r = getBrightness(texture2D(textureMap, vec2(x+pixelSize, y))); 46 | float tr = getBrightness(texture2D(textureMap, vec2(x+pixelSize, y-pixelSize))); 47 | float t = getBrightness(texture2D(textureMap, vec2(x, y-pixelSize))); 48 | 49 | 50 | float tl2 = getBrightness(texture2D(heightMap, vec2(x-pixelSize, y-pixelSize))); 51 | float l2 = getBrightness(texture2D(heightMap, vec2(x-pixelSize, y))); 52 | float bl2 = getBrightness(texture2D(heightMap, vec2(x-pixelSize, y+pixelSize))); 53 | float b2 = getBrightness(texture2D(heightMap, vec2(x, y+pixelSize))); 54 | float br2 = getBrightness(texture2D(heightMap, vec2(x+pixelSize, y+pixelSize))); 55 | float r2 = getBrightness(texture2D(heightMap, vec2(x+pixelSize, y))); 56 | float tr2 = getBrightness(texture2D(heightMap, vec2(x+pixelSize, y-pixelSize))); 57 | float t2 = getBrightness(texture2D(heightMap, vec2(x, y-pixelSize))); 58 | 59 | float ratio = 1.0; 60 | 61 | if (level > water) { 62 | float diff = water - level; 63 | ratio = 1.0 - (diff*1.0); 64 | } 65 | 66 | tl = mix(tl, tl2, ratio); 67 | l = mix(l, l2, ratio); 68 | bl = mix(bl, bl2, ratio); 69 | b = mix(b, b2, ratio); 70 | br = mix(br, br2, ratio); 71 | r = mix(r, r2, ratio); 72 | tr = mix(tr, tr2, ratio); 73 | t = mix(t, t2, ratio); 74 | 75 | // float level = getBrightness(texture2D(heightMap, vec2(x, y))); 76 | 77 | 78 | // smooth out the normal map for the water 79 | float factor = 0.01; 80 | float depth = 0.5; 81 | if (level < water) { 82 | // strength = 0.00; 83 | tl = mix(depth, tl, factor); 84 | l = mix(depth, l, factor); 85 | bl = mix(depth, bl, factor); 86 | b = mix(depth, b, factor); 87 | br = mix(depth, br, factor); 88 | r = mix(depth, r, factor); 89 | tr = mix(depth, tr, factor); 90 | t = mix(depth, t, factor); 91 | } 92 | 93 | 94 | // Compute dx using Sobel: 95 | // -1 0 1 96 | // -2 0 2 97 | // -1 0 1 98 | float dX = tr + 2.0 * r + br - tl - 2.0 * l - bl; 99 | 100 | // Compute dy using Sobel: 101 | // -1 -2 -1 102 | // 0 0 0 103 | // 1 2 1 104 | float dY = bl + 2.0 * b + br - tl - 2.0 * t - tr; 105 | 106 | 107 | vec3 N = vec3(dX, dY, 1.0 / strength); 108 | 109 | // 110 | // if (level < 0.5) { 111 | // N.x = 0.5; 112 | // N.y = 0.5; 113 | // N.z = -0.5; 114 | // 115 | // } 116 | 117 | normalize(N); 118 | 119 | //convert (-1.0 , 1.0) to (0.0 , 1.0), if necessary 120 | //Vector3 scale = new Vector3(0.5f, 0.5f, 0.5f); 121 | //Vector3.Multiply(ref N, ref scale, out N); 122 | //Vector3.Add(ref N, ref scale, out N); 123 | 124 | N = N * 0.5 + 0.5; 125 | 126 | 127 | gl_FragColor = vec4(N, 1.0); 128 | } 129 | -------------------------------------------------------------------------------- /src/js/views/Clouds.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import CloudMap from 'views/CloudMap.js' 3 | import tinycolor from 'tinycolor2' 4 | 5 | class Clouds { 6 | 7 | constructor() { 8 | this.view = new THREE.Object3D(); 9 | 10 | this.materials = []; 11 | this.roughness = 0.9; 12 | this.metalness = 0.5; 13 | this.normalScale = 5.0; 14 | this.clouds = 1.0; 15 | 16 | 17 | this.resolution = 1024; 18 | this.size = 1001; 19 | 20 | 21 | this.color = new THREE.Color(0xffffff); 22 | 23 | this.cloudColor = [this.color.r*255, this.color.g*255, this.color.b*255]; 24 | 25 | this.cloudMaps = []; 26 | 27 | this.setup(); 28 | 29 | 30 | let cloudControl = window.gui.add(this, "clouds", 0.0, 1.0); 31 | cloudControl.onChange(value => { this.updateMaterial(); }); 32 | 33 | let colorControl = window.gui.addColor(this, "cloudColor"); 34 | colorControl.onChange(value => { 35 | this.color.r = value[0] / 255; 36 | this.color.g = value[1] / 255; 37 | this.color.b = value[2] / 255; 38 | this.updateMaterial(); 39 | }); 40 | 41 | } 42 | 43 | update() { 44 | // 45 | } 46 | 47 | setup() { 48 | 49 | this.cloudMap = new CloudMap(); 50 | this.cloudMaps = this.cloudMap.maps; 51 | 52 | for (let i=0; i<6; i++) { 53 | let material = new THREE.MeshStandardMaterial({ 54 | color: this.color, 55 | transparent: true, 56 | }); 57 | this.materials[i] = material; 58 | } 59 | 60 | let geo = new THREE.BoxGeometry(1, 1, 1, 64, 64, 64); 61 | let radius = this.size; 62 | for (var i in geo.vertices) { 63 | var vertex = geo.vertices[i]; 64 | vertex.normalize().multiplyScalar(radius); 65 | } 66 | this.computeGeometry(geo); 67 | this.sphere = new THREE.Mesh(geo, this.materials); 68 | this.view.add(this.sphere); 69 | } 70 | 71 | render(props) { 72 | this.seed = this.randRange(0, 1000); 73 | let cloudSize = this.randRange(0.5, 1.0); 74 | 75 | let mixScale = this.map_range(props.waterLevel*props.waterLevel, 0, 0.8, 0.0, 3.0); 76 | 77 | 78 | this.cloudMap.render({ 79 | seed: this.seed, 80 | resolution: this.resolution, 81 | res1: this.randRange(0.1, 1.0), 82 | res2: this.randRange(0.1, 1.0), 83 | resMix: this.randRange(0.1, 1.0), 84 | mixScale: this.randRange(0.1, 1.0) 85 | }); 86 | 87 | this.updateMaterial(); 88 | } 89 | 90 | map_range(value, low1, high1, low2, high2) { 91 | return low2 + (high2 - low2) * (value - low1) / (high1 - low1); 92 | } 93 | 94 | updateMaterial() { 95 | for (let i=0; i<6; i++) { 96 | let material = this.materials[i]; 97 | material.roughness = this.roughness; 98 | material.metalness = this.metalness; 99 | material.opacity = this.clouds; 100 | material.map = this.cloudMaps[i], 101 | // material.alphaMap = this.cloudMaps[i], 102 | // material.bumpMap = this.cloudMaps[i], 103 | // material.bumpScale = 1.0, 104 | material.color = this.color 105 | } 106 | } 107 | 108 | randomizeColor() { 109 | 110 | this.color.r = this.randRange(0.7, 1.0); 111 | this.color.g = this.randRange(0.7, 1.0); 112 | this.color.b = this.randRange(0.7, 1.0); 113 | 114 | this.updateMaterial(); 115 | } 116 | 117 | 118 | randRange(low, high) { 119 | let range = high - low; 120 | let n = window.rng() * range; 121 | return low + n; 122 | } 123 | 124 | computeGeometry(geometry) { 125 | // geometry.makeGroups(); 126 | geometry.computeVertexNormals() 127 | geometry.computeFaceNormals(); 128 | geometry.computeMorphNormals(); 129 | geometry.computeBoundingSphere(); 130 | geometry.computeBoundingBox(); 131 | // geometry.computeLineDistances(); 132 | 133 | geometry.verticesNeedUpdate = true; 134 | geometry.elementsNeedUpdate = true; 135 | geometry.uvsNeedUpdate = true; 136 | geometry.normalsNeedUpdate = true; 137 | // geometry.tangentsNeedUpdate = true; 138 | geometry.colorsNeedUpdate = true; 139 | geometry.lineDistancesNeedUpdate = true; 140 | // geometry.buffersNeedUpdate = true; 141 | geometry.groupsNeedUpdate = true; 142 | } 143 | 144 | } 145 | 146 | export default Clouds; 147 | -------------------------------------------------------------------------------- /src/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 100%; 3 | height: 100%; 4 | background-color: #000; 5 | color: #fff; 6 | margin: 0px; 7 | padding: 0; 8 | overflow: hidden; 9 | font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; 10 | } 11 | 12 | #brandTag 13 | { 14 | position: fixed; 15 | display: block; 16 | top: 0px; 17 | left: 30px; 18 | color: white; 19 | background-color: rgba(0, 0, 0, 0.8); 20 | z-index: 1000; 21 | text-align: center; 22 | padding: 12px 12px 12px 12px; 23 | font-size: 18px; 24 | font-family: "Helvetica", sans-serif; 25 | transition-property: color, background-color, padding; 26 | transition-duration: 0.3s; 27 | } 28 | @media (width < 600px) { 29 | 30 | } 31 | 32 | @media screen and (max-width: 600px) { 33 | #brandTag 34 | { 35 | display: none; 36 | } 37 | #infoBoxHolder 38 | { 39 | display: none; 40 | } 41 | } 42 | 43 | @media screen and (min-width: 600px) { 44 | #infoBoxHolderMobile { 45 | display: none; 46 | } 47 | #newPlanetButtonHolder { 48 | display: none; 49 | } 50 | } 51 | 52 | #brandTag:hover 53 | { 54 | color: black; 55 | background-color: white; 56 | padding: 24px 12px 12px 12px; 57 | } 58 | 59 | #infoBoxHolder { 60 | position: absolute; 61 | z-index: 1000; 62 | top: 0px; 63 | right: 20px; 64 | color: #FFFFFF; 65 | background-color: rgba(0, 0, 0, 0.8); 66 | width: 245px; 67 | } 68 | 69 | #infoBox { 70 | padding: 12px 12px 12px 12px; 71 | font-size: 14px; 72 | font-family: "Helvetica-Light", sans-serif; 73 | line-height: 18px; 74 | } 75 | 76 | #planetName { 77 | font-size: 24px; 78 | line-height: 28px; 79 | } 80 | 81 | #instructions { 82 | font-size: 10px; 83 | } 84 | 85 | #infoBoxHolderMobile { 86 | position: absolute; 87 | z-index: 1000; 88 | top: 0px; 89 | width: 100%; 90 | 91 | } 92 | 93 | #infoBoxMobile { 94 | 95 | margin: 0 auto; 96 | 97 | width: 245px; 98 | color: #FFFFFF; 99 | background-color: rgba(0, 0, 0, 0.8); 100 | padding: 12px 12px 12px 12px; 101 | font-size: 14px; 102 | font-family: "Helvetica-Light", sans-serif; 103 | line-height: 18px; 104 | text-align: center; 105 | } 106 | 107 | #planetNameMobile { 108 | font-size: 24px; 109 | line-height: 28px; 110 | } 111 | 112 | 113 | #newPlanetButtonHolder { 114 | position: absolute; 115 | z-index: 1000; 116 | bottom: 0px; 117 | width: 100%; 118 | } 119 | 120 | #newPlanetButton { 121 | margin: 0 auto; 122 | 123 | width: 145px; 124 | color: #FFFFFF; 125 | background-color: rgba(0, 0, 0, 0.8); 126 | padding: 12px 12px 12px 12px; 127 | font-size: 18px; 128 | font-family: "Helvetica-Light", sans-serif; 129 | line-height: 18px; 130 | text-align: center; 131 | } 132 | 133 | #line { 134 | background-color: rgba(255, 255, 255, 0.2); 135 | width: 100%; 136 | height: 1px; 137 | } 138 | 139 | 140 | #biomeCanvas { 141 | position: absolute; 142 | z-index: 1000; 143 | bottom: 0px; 144 | right: 0px; 145 | } 146 | 147 | #nebulaeCanvas { 148 | position: absolute; 149 | z-index: 1000; 150 | bottom: 0px; 151 | left: 0px; 152 | } 153 | 154 | #sunCanvas { 155 | position: absolute; 156 | z-index: 1000; 157 | bottom: 0px; 158 | right: 0px; 159 | } 160 | 161 | /* Position the button VR on the bottom of the page. */ 162 | #ui { 163 | position: absolute; 164 | bottom: 10px; 165 | left: 50%; 166 | transform: translate(-50%, -50%); 167 | text-align: center; 168 | font-family: 'Karla', sans-serif; 169 | z-index: 1; 170 | } 171 | 172 | a#magic-window { 173 | display: block; 174 | color: white; 175 | margin-top: 1em; 176 | } 177 | 178 | #loadingBarHolder { 179 | position: absolute; 180 | 181 | left: 0px; 182 | right: 0px; 183 | top: 0px; 184 | bottom: 0px; 185 | margin: auto; 186 | 187 | width: 245px; 188 | height: 100px; 189 | 190 | text-align: center; 191 | font-size: 18px; 192 | font-family: "Helvetica-Light", sans-serif; 193 | 194 | /* border: 1px solid #222222; */ 195 | padding: 20px; 196 | padding-top: 30px; 197 | background-color: rgba(0, 0, 0, 0.8); 198 | z-index: 2000; 199 | } 200 | 201 | #loadingBarBase { 202 | width: 100%; 203 | height: 50%; 204 | margin-top: 10px; 205 | background-color: #333333; 206 | } 207 | 208 | #loadingBar { 209 | width: 50%; 210 | height: 100%; 211 | background-color: #777777; 212 | } 213 | -------------------------------------------------------------------------------- /src/js/shaders/texture.frag: -------------------------------------------------------------------------------- 1 | import noise from 'glsl-noise/simplex/3d' 2 | 3 | 4 | varying vec2 vUv; 5 | uniform int index; 6 | 7 | int mod(int x, int m) { 8 | return int(mod(float(x), float(m))); 9 | } 10 | 11 | float random5(vec3 co) { 12 | return fract(sin(dot(co.xyz ,vec3(12.9898,78.233,1.23456))) * 43758.5453); 13 | } 14 | 15 | 16 | float random4(float x, float y, float z) { 17 | return random5(vec3(x, y, z)); 18 | } 19 | 20 | float random4(int x, int y, int z) { 21 | return random4(float(x), float(y), float(z)); 22 | } 23 | 24 | float interpolation(float a, float b, float x) { 25 | float ft = x * 3.1415927; 26 | float f = (1.0 - cos(ft)) * 0.5; 27 | return a*(1.0-f) + b*f; 28 | } 29 | 30 | float tricosine(vec3 coordFloat) { 31 | vec3 coord0 = vec3(floor(coordFloat.x), floor(coordFloat.y), floor(coordFloat.z)); 32 | vec3 coord1 = vec3(coord0.x+1.0, coord0.y+1.0, coord0.z+1.0); 33 | float xd = (coordFloat.x - coord0.x)/max(1.0, (coord1.x-coord0.x)); 34 | float yd = (coordFloat.y - coord0.y)/max(1.0, (coord1.y-coord0.y)); 35 | float zd = (coordFloat.z - coord0.z)/max(1.0, (coord1.z-coord0.z)); 36 | float c00 = interpolation(random4(coord0.x, coord0.y, coord0.z), random4(coord1.x, coord0.y, coord0.z), xd); 37 | float c10 = interpolation(random4(coord0.x, coord1.y, coord0.z), random4(coord1.x, coord1.y, coord0.z), xd); 38 | float c01 = interpolation(random4(coord0.x, coord0.y, coord1.z), random4(coord1.x, coord0.y, coord1.z), xd); 39 | float c11 = interpolation(random4(coord0.x, coord1.y, coord1.z), random4(coord1.x, coord1.y, coord1.z), xd); 40 | float c0 = interpolation(c00, c10, yd); 41 | float c1 = interpolation(c01, c11, yd); 42 | float c = interpolation(c0, c1, zd); 43 | 44 | return c; 45 | } 46 | 47 | float nearestNeighbour(vec3 coordFloat) { 48 | return random4(int(floor(coordFloat.x)), int(floor(coordFloat.y)), int(floor(coordFloat.z))); 49 | } 50 | 51 | float helper(float x, float y, float z, float resolution) { 52 | x = (x+1.0)/2.0*resolution; 53 | y = (y+1.0)/2.0*resolution; 54 | z = (z+1.0)/2.0*resolution; 55 | 56 | vec3 coordFloat = vec3(x, y, z); 57 | float interpolated = tricosine(coordFloat); 58 | // float interpolated = noise(coordFloat); 59 | 60 | return interpolated*2.0 - 1.0; 61 | } 62 | 63 | vec3 scalarField(float x, float y, float z) { 64 | float resolution1 = 4.0; 65 | float resolution2 = 16.0; 66 | float resolution3 = 32.0; 67 | float resolution4 = 64.0; 68 | float resolution5 = 128.0; 69 | float resolutionMax = 256.0; 70 | 71 | vec3 coordFloat = vec3(0.0, 0.0, 0.0); 72 | 73 | float level1 = helper(x, y, z, resolution1); 74 | float level2 = helper(x, y, z, resolution2); 75 | float level3 = helper(x, y, z, resolution3); 76 | float level4 = helper(x, y, z, resolution4); 77 | float level5 = helper(x, y, z, resolution5); 78 | float levelMax = helper(x, y, z, resolutionMax); 79 | 80 | float c = 0.5; 81 | c *= 1.0 + level1*0.8; 82 | c *= 1.0 + level2*0.4; 83 | c *= 1.0 + level3*0.2; 84 | c *= 1.0 + level4*0.1; 85 | c *= 1.0 + level5*0.05; 86 | c *= 1.0 + levelMax*(0.025); 87 | 88 | if (c < 0.45) c *= 0.8; 89 | 90 | c = clamp(c, 0.0, 1.0); 91 | vec3 water = vec3(0.0, 0.2, 0.4) * (0.4+c*0.5); 92 | vec3 land = vec3(0.2, 0.4, 0.3) * c*0.8; 93 | vec3 peak = vec3(0.42, 0.4, 0.42) * c; 94 | // vec3 water = vec3(0.6, 0.2, 0.2) * (0.2 + c*0.3); 95 | // vec3 land = vec3(0.5, 0.4, 0.1) * c; 96 | // vec3 peak = vec3(0.2, 0.1, 0.32) * c; 97 | 98 | if (c < 0.5) { 99 | return water; 100 | } 101 | else if (c < 0.8) { 102 | return land; 103 | } 104 | else { 105 | return peak; 106 | } 107 | 108 | // return vec3(c); 109 | } 110 | 111 | vec3 getSphericalCoord(int index, float x, float y, float width) { 112 | width /= 2.0; 113 | x -= width; 114 | y -= width; 115 | vec3 coord = vec3(0.0, 0.0, 0.0); 116 | 117 | if (index == 0) {coord.x=width; coord.y=-y; coord.z=-x;} 118 | else if (index == 1) {coord.x=-width; coord.y=-y; coord.z=x;} 119 | else if (index == 2) {coord.x=x; coord.y=width; coord.z=y;} 120 | else if (index == 3) {coord.x=x; coord.y=-width; coord.z=-y;} 121 | else if (index == 4) {coord.x=x; coord.y=-y; coord.z=width;} 122 | else if (index == 5) {coord.x=-x; coord.y=-y; coord.z=-width;} 123 | 124 | return normalize(coord); 125 | } 126 | 127 | void main() { 128 | float x = vUv.x; 129 | float y = 1.0 - vUv.y; 130 | vec3 sphericalCoord = getSphericalCoord(index, x*1024.0, y*1024.0, 1024.0); 131 | 132 | vec3 color = scalarField(sphericalCoord.x, sphericalCoord.y, sphericalCoord.z); 133 | 134 | gl_FragColor = vec4(color.x, color.y, color.z, 1.0); 135 | } 136 | -------------------------------------------------------------------------------- /src/js/shaders/flowNoiseMap.frag: -------------------------------------------------------------------------------- 1 | import noise from 'glsl-noise/classic/3d' 2 | 3 | varying vec2 vUv; 4 | uniform int index; 5 | uniform float seed; 6 | uniform float resolution; 7 | uniform float res1; 8 | uniform float res2; 9 | uniform float resMix; 10 | uniform float mixScale; 11 | uniform float doesRidged; 12 | const int octaves = 16; 13 | 14 | // #define M_PI 3.1415926535897932384626433832795; 15 | 16 | 17 | vec3 getSphericalCoord(int index, float x, float y, float width) { 18 | width /= 2.0; 19 | x -= width; 20 | y -= width; 21 | vec3 coord = vec3(0.0, 0.0, 0.0); 22 | 23 | if (index == 0) {coord.x=width; coord.y=-y; coord.z=-x;} 24 | else if (index == 1) {coord.x=-width; coord.y=-y; coord.z=x;} 25 | else if (index == 2) {coord.x=x; coord.y=width; coord.z=y;} 26 | else if (index == 3) {coord.x=x; coord.y=-width; coord.z=-y;} 27 | else if (index == 4) {coord.x=x; coord.y=-y; coord.z=width;} 28 | else if (index == 5) {coord.x=-x; coord.y=-y; coord.z=-width;} 29 | 30 | return normalize(coord); 31 | } 32 | 33 | float simplexRidged(vec3 pos, float seed) { 34 | float n = noise(vec3(pos + seed)); 35 | n = (n + 1.0) * 0.5; 36 | n = 2.0 * (0.5 - abs(0.5 - n)); 37 | return n; 38 | } 39 | 40 | float simplex(vec3 pos, float seed) { 41 | float n = noise(vec3(pos + seed)); 42 | return (n + 1.0) * 0.5; 43 | } 44 | 45 | float baseNoise(vec3 pos, float frq, float seed ) { 46 | float amp = 0.5; 47 | 48 | float n = 0.0; 49 | float gain = 1.0; 50 | for(int i=0; i { 52 | this.directionalLight.color.r = this.sunColor.r / 255; 53 | this.directionalLight.color.g = this.sunColor.g / 255; 54 | this.directionalLight.color.b = this.sunColor.b / 255; 55 | }); 56 | 57 | lightFolder.add(this.directionalLight, "intensity", 0.0, 3.0); 58 | // this.dirLightControl.onChange(value => {}); 59 | 60 | this.ambientColor = {r:255, g:255, b:255}; 61 | this.ambientControl = lightFolder.addColor(this, "ambientColor").onChange(value => { 62 | this.ambientLight.color.r = this.ambientColor.r / 255; 63 | this.ambientLight.color.g = this.ambientColor.g / 255; 64 | this.ambientLight.color.b = this.ambientColor.b / 255; 65 | }); 66 | 67 | lightFolder.add(this.ambientLight, "intensity", 0.0, 2.0); 68 | 69 | let cameraFolder = gui.addFolder('Camera'); 70 | 71 | cameraFolder.add(this._controls, "autoRotate"); 72 | 73 | this.fovControl = cameraFolder.add(this._camera, "fov", 20, 120); 74 | this.fovControl.onChange(value => { this._camera.updateProjectionMatrix() }); 75 | 76 | // stats 77 | this.stats = new Stats(); 78 | this.stats.setMode(0); 79 | // document.body.appendChild(this.stats.domElement); 80 | this.stats.domElement.style.position = 'absolute'; 81 | this.stats.domElement.style.left = '10px'; 82 | this.stats.domElement.style.top = '0px'; 83 | 84 | window.addEventListener( 'resize', this.onWindowResize.bind(this), false ); 85 | window.addEventListener( 'keydown', (e) => { this.onKeyDown(e) }, false ); 86 | 87 | 88 | window.gui.close(); 89 | } 90 | 91 | get renderer(){ 92 | 93 | return this._renderer; 94 | 95 | } 96 | 97 | get camera(){ 98 | 99 | return this._camera; 100 | 101 | } 102 | 103 | get scene(){ 104 | 105 | return this._scene; 106 | 107 | } 108 | 109 | onKeyDown(e) { 110 | if (e.keyCode == '72') 111 | { 112 | var brandTag = document.getElementById("brandTag"); 113 | var infoBoxHolder = document.getElementById("infoBoxHolder"); 114 | if (brandTag.style.visibility == "hidden") { 115 | brandTag.style.visibility = "visible"; 116 | infoBoxHolder.style.visibility = "visible"; 117 | } else { 118 | brandTag.style.visibility = "hidden"; 119 | infoBoxHolder.style.visibility = "hidden"; 120 | } 121 | } 122 | } 123 | 124 | onWindowResize() { 125 | 126 | this._camera.aspect = window.innerWidth / window.innerHeight; 127 | this._camera.updateProjectionMatrix(); 128 | 129 | this._renderer.setSize( window.innerWidth, window.innerHeight ); 130 | 131 | } 132 | 133 | animate(timestamp) { 134 | this.stats.begin(); 135 | requestAnimationFrame( this.animate.bind(this) ); 136 | 137 | this._controls.update(); 138 | this._renderer.render( this._scene, this._camera ); 139 | this.stats.end(); 140 | } 141 | 142 | 143 | } 144 | export default AbstractApplication; 145 | -------------------------------------------------------------------------------- /src/js/shaders/cloudMap.frag: -------------------------------------------------------------------------------- 1 | import noise from 'glsl-noise/classic/4d' 2 | 3 | varying vec2 vUv; 4 | uniform int index; 5 | uniform float seed; 6 | uniform float resolution; 7 | uniform float res1; 8 | uniform float res2; 9 | uniform float resMix; 10 | uniform float mixScale; 11 | 12 | vec3 getSphericalCoord(int index, float x, float y, float width) { 13 | width /= 2.0; 14 | x -= width; 15 | y -= width; 16 | vec3 coord = vec3(0.0, 0.0, 0.0); 17 | 18 | if (index == 0) {coord.x=width; coord.y=-y; coord.z=-x;} 19 | else if (index == 1) {coord.x=-width; coord.y=-y; coord.z=x;} 20 | else if (index == 2) {coord.x=x; coord.y=width; coord.z=y;} 21 | else if (index == 3) {coord.x=x; coord.y=-width; coord.z=-y;} 22 | else if (index == 4) {coord.x=x; coord.y=-y; coord.z=width;} 23 | else if (index == 5) {coord.x=-x; coord.y=-y; coord.z=-width;} 24 | 25 | return normalize(coord); 26 | } 27 | 28 | // float simplexRidged(vec3 pos, float seed) { 29 | // float n = noise(vec4(pos, seed)); 30 | // n = (n + 1.0) * 0.5; 31 | // n = 2.0 * (0.5 - abs(0.5 - n)); 32 | // return n; 33 | // } 34 | 35 | float simplexRidged(vec3 pos, float seed) { 36 | float n = noise(vec4(pos, seed)); 37 | // n = (n + 1.0) * 0.5; 38 | // n = 2.0 * (0.5 - abs(0.5 - n)); 39 | n = abs(n); 40 | return n; 41 | } 42 | 43 | float simplex(vec3 pos, float seed) { 44 | float n = noise(vec4(pos, seed)); 45 | // return (n + 1.0) * 0.5; 46 | n = (n + 1.0) * 0.5; 47 | // n = 2.0 * (0.5 - abs(0.5 - n)); 48 | return n; 49 | } 50 | 51 | float baseNoise(vec3 pos, float frq, float seed ) { 52 | const int octaves = 16; 53 | float amp = 0.5; 54 | 55 | float n = 0.0; 56 | float gain = 1.0; 57 | for(int i=0; i 0 ) { 134 | 135 | if ( this.INTERSECTED != intersects[ 0 ].object ) { 136 | 137 | if ( this.INTERSECTED ) this.INTERSECTED.material.emissive.setHex( this.INTERSECTED.currentHex ); 138 | 139 | this.INTERSECTED = intersects[ 0 ].object; 140 | this.INTERSECTED.currentHex = this.INTERSECTED.material.emissive.getHex(); 141 | this.INTERSECTED.material.emissive.setHex( 0xff0000 ); 142 | 143 | } 144 | 145 | } else { 146 | 147 | if ( this.INTERSECTED ) this.INTERSECTED.material.emissive.setHex( this.INTERSECTED.currentHex ); 148 | 149 | this.INTERSECTED = undefined; 150 | 151 | } 152 | 153 | // Keep cubes inside room 154 | 155 | for ( let i = 0; i < this.room.children.length; i ++ ) { 156 | 157 | const cube = this.room.children[ i ]; 158 | 159 | cube.userData.velocity.multiplyScalar( 1 - ( 0.001 * delta ) ); 160 | 161 | cube.position.add( cube.userData.velocity ); 162 | 163 | if ( cube.position.x < - 3 || cube.position.x > 3 ) { 164 | 165 | cube.position.x = THREE.Math.clamp( cube.position.x, - 3, 3 ); 166 | cube.userData.velocity.x = - cube.userData.velocity.x; 167 | 168 | } 169 | 170 | if ( cube.position.y < - 3 || cube.position.y > 3 ) { 171 | 172 | cube.position.y = THREE.Math.clamp( cube.position.y, - 3, 3 ); 173 | cube.userData.velocity.y = - cube.userData.velocity.y; 174 | 175 | } 176 | 177 | if ( cube.position.z < - 3 || cube.position.z > 3 ) { 178 | 179 | cube.position.z = THREE.Math.clamp( cube.position.z, - 3, 3 ); 180 | cube.userData.velocity.z = - cube.userData.velocity.z; 181 | 182 | } 183 | 184 | cube.rotation.x += cube.userData.velocity.x * 2 * delta; 185 | cube.rotation.y += cube.userData.velocity.y * 2 * delta; 186 | cube.rotation.z += cube.userData.velocity.z * 2 * delta; 187 | 188 | } 189 | 190 | this.controls.update(); 191 | this.effect.render(this.scene, this.camera); 192 | 193 | } 194 | 195 | } 196 | export default Main; -------------------------------------------------------------------------------- /src/js/views/atmosReference.js: -------------------------------------------------------------------------------- 1 | // Initialize THREE.js 2 | var scene = new THREE.Scene(); 3 | var width = window.innerWidth - 200; 4 | var camera = new THREE.PerspectiveCamera( 30, width / window.innerHeight, 0.1, 20000 ); 5 | var renderer = new THREE.WebGLRenderer({ 6 | antialias: true 7 | }); 8 | 9 | renderer.setSize( width, window.innerHeight); 10 | document.body.appendChild( renderer.domElement ); 11 | 12 | // Initialize global variables 13 | // Geometries 14 | var radius = 100; 15 | var planetGeometry = new THREE.SphereGeometry( radius, 70, 70); 16 | var sunGeometry = new THREE.SphereGeometry( radius, 40, 40); 17 | radius += 0.1; // Prevents clipping problems between atmosphere and planet 18 | var atmosphereGeometry = new THREE.SphereGeometry(radius*1.025, 100, 100); 19 | var atmosphereGroundGeometry = new THREE.SphereGeometry(radius*1.01, 70, 70); 20 | 21 | var sunColor = new THREE.Vector4(1,0.765,0.302,1); 22 | var clock = new THREE.Clock(); 23 | var sunPos = new THREE.Vector3(0, 0, 4000); 24 | var placeholderColor = new THREE.Vector4(0,0,0,1); 25 | 26 | // Atmsphere variable used in the atmosphere shader 27 | var atmosphere = { 28 | Kr: 0.0025, 29 | Km: 0.0010, 30 | ESun: 20.0, 31 | g: -0.950, 32 | innerRadius: 100, 33 | outerRadius: 102.5, 34 | wavelength: [0.650, 0.570, 0.475], 35 | scaleDepth: 0.25, 36 | mieScaleDepth: 0.1 37 | }; 38 | 39 | // Initialize UI sliders 40 | $("#noiseIntensity").noUiSlider({ range: { 'min': 1, 'max': 100 }, snap: false, start: 50 }); 41 | $("#noiseVariation").noUiSlider({ range: { 'min': 1, 'max': 100 }, snap: false, start: 50 }); 42 | $("#waterLevel").noUiSlider({ range: { 'min': -25, 'max': 25 }, snap: false, start: 2.20 }); 43 | 44 | $("#waterLevel").Link('lower').to($("#waterLevelValue")); 45 | $("#noiseIntensity").Link('lower').to($("#noiseIntensityValue")); 46 | $("#noiseVariation").Link('lower').to($("#noiseVariationValue")); 47 | 48 | // Load shaders 49 | SHADER_LOADER.load( 50 | function (data) 51 | { 52 | // Initialize uniforms to send to the shaders 53 | planetUniforms = { 54 | time: { type: "f", value: 0 }, 55 | level: { type: "f", value: camera.position.length()}, 56 | sunPos: {type: "v3", value: sunPos}, 57 | diffuseLight: {type: "v4", value: sunColor}, 58 | diffuseMaterial: {type: "v4", value: placeholderColor}, 59 | sealevel: {type: "f", value: 0}, 60 | noiseIntensity: {type: "f", value: 0}, 61 | noiseType: {type: "f", value: 1}, 62 | noiseVariation: {type: "f", value: 0}, 63 | waterColor: {type: "v3", value: placeholderColor}, 64 | renderLevels: {type: "fv1", value: [6000.0, 6000.0, 6000.0, 5000.0, 1800.0, 950.0, 850.0, 500.0, 500, 500]}, 65 | shoreColor: {type: "v3", value: placeholderColor}, 66 | cloudColor: {type: "v3", value: placeholderColor}, 67 | renderClouds: {type: "f", value: 1} 68 | }; 69 | 70 | sunUniforms = { 71 | diffuseLight: {type: "v4", value : sunColor} 72 | } 73 | 74 | // Used for the atmosphere shader 75 | atmosphereUniforms = { 76 | v3LightPosition: {type: "v3",value: new THREE.Vector3(0,0,1)}, 77 | v3InvWavelength: {type: "v3",value: new THREE.Vector3(1 / Math.pow(atmosphere.wavelength[0], 4), 1 / Math.pow(atmosphere.wavelength[1], 4), 1 / Math.pow(atmosphere.wavelength[2], 4))}, 78 | fCameraHeight: {type: "f",value: 0}, 79 | fCameraHeight2: {type: "f",value: 0}, 80 | fInnerRadius: {type: "f",value: atmosphere.innerRadius}, 81 | fInnerRadius2: {type: "f",value: atmosphere.innerRadius * atmosphere.innerRadius}, 82 | fOuterRadius: {type: "f",value: atmosphere.outerRadius}, 83 | fOuterRadius2: {type: "f",value: atmosphere.outerRadius * atmosphere.outerRadius}, 84 | fKrESun: {type: "f",value: atmosphere.Kr * atmosphere.ESun}, 85 | fKmESun: {type: "f",value: atmosphere.Km * atmosphere.ESun}, 86 | fKr4PI: {type: "f",value: atmosphere.Kr * 4.0 * Math.PI}, 87 | fKm4PI: {type: "f",value: atmosphere.Km * 4.0 * Math.PI}, 88 | fScale: {type: "f",value: 1 / (atmosphere.outerRadius - atmosphere.innerRadius)}, 89 | fScaleDepth: {type: "f",value: atmosphere.scaleDepth}, 90 | fScaleOverScaleDepth: {type: "f",value: 1 / (atmosphere.outerRadius - atmosphere.innerRadius) / atmosphere.scaleDepth}, 91 | g: {type: "f",value: atmosphere.g}, 92 | g2: {type: "f",value: atmosphere.g * atmosphere.g}, 93 | nSamples: {type: "i",value: 3}, 94 | fSamples: {type: "f",value: 3.0}, 95 | atmosphereColor: {type: "v3",value: placeholderColor}, 96 | tDisplacement: {type: "t",value: 0}, 97 | tSkyboxDiffuse: {type: "t",value: 0}, 98 | fNightScale: {type: "f",value: 1}, 99 | level: { type: "f", value: camera.position.length()} 100 | }; 101 | 102 | // Load shaders using Shader Loader JS 103 | var vShader = data.shader.vertex; 104 | var fShader = data.shader.fragment; 105 | var vShaderSun = data.shaderSun.vertex; 106 | var fShaderSun = data.shaderSun.fragment; 107 | var vShaderAtmosphere = data.shaderAtmosphere.vertex; 108 | var fShaderAtmosphere = data.shaderAtmosphere.fragment; 109 | var vShaderAtmosphereGround = data.shaderAtmosphereGround.vertex; 110 | var fShaderAtmosphereGround = data.shaderAtmosphereGround.fragment; 111 | 112 | // Create shader materials 113 | planetShader = new THREE.ShaderMaterial({ 114 | uniforms: planetUniforms, 115 | vertexShader: vShader, 116 | fragmentShader: fShader 117 | }); 118 | 119 | sunShader = new THREE.ShaderMaterial({ 120 | uniforms: sunUniforms, 121 | vertexShader: vShaderSun, 122 | fragmentShader: fShaderSun 123 | }); 124 | 125 | // The atmosphere outside the planet 126 | atmosphereShader = new THREE.ShaderMaterial({ 127 | uniforms: atmosphereUniforms, 128 | vertexShader: vShaderAtmosphere, 129 | fragmentShader: fShaderAtmosphere, 130 | transparent: true, 131 | side: THREE.BackSide 132 | }); 133 | 134 | // The atmosphere on the planet 135 | atmosphereGroundShader = new THREE.ShaderMaterial({ 136 | uniforms: atmosphereUniforms, 137 | vertexShader: vShaderAtmosphereGround, 138 | fragmentShader: fShaderAtmosphereGround, 139 | transparent: true, 140 | }); 141 | 142 | initialize(); 143 | } 144 | ); 145 | 146 | // Let's get this party started 147 | function initialize() { 148 | // Create meshes 149 | planetMesh = new THREE.Mesh(planetGeometry, planetShader); 150 | sunMesh = new THREE.Mesh(sunGeometry, sunShader); 151 | atmosphereMesh = new THREE.Mesh(atmosphereGeometry, atmosphereShader); 152 | atmosphereGroundMesh = new THREE.Mesh(atmosphereGroundGeometry, atmosphereGroundShader); 153 | 154 | // Add meshes 155 | scene.add( planetMesh ); 156 | scene.add( sunMesh ); 157 | scene.add( atmosphereMesh ); 158 | scene.add( atmosphereGroundMesh ); 159 | 160 | // Alter meshes 161 | sunMesh.scale.set(3,3,3); 162 | sunMesh.position.set(sunPos.x,sunPos.y,sunPos.z); 163 | 164 | // Handle user controls 165 | controls = new THREE.TrackballControls( camera, renderer.domElement); 166 | controls.target.set( 0, 0, 0 ); 167 | controls.minDistance = 200.0; 168 | controls.maxDistance = 5000; 169 | controls.zoomSpeed = 0.1; 170 | controls.rotateSpeed = 0.05; 171 | controls.noPan = true; 172 | 173 | camera.position = new THREE.Vector3(atmosphere.innerRadius*6,atmosphere.innerRadius,atmosphere.innerRadius*2); 174 | 175 | render(); 176 | }; 177 | 178 | // Rendering function, runs (hopefully) many times every frame 179 | function render() { 180 | // Update uniforms 181 | var delta = clock.getDelta(); 182 | updateUniforms(delta); 183 | 184 | // Update world 185 | planetMesh.rotation.y += 0.001; 186 | controls.update(); 187 | 188 | if($('#renderAtmosphere').prop('checked')) { 189 | atmosphereMesh.visible = true; 190 | atmosphereGroundMesh.visible = true; 191 | } else { 192 | atmosphereMesh.visible = false; 193 | atmosphereGroundMesh.visible = false; 194 | } 195 | 196 | if($('#renderClouds').prop('checked')) 197 | planetUniforms.renderClouds.value = 1; 198 | else 199 | planetUniforms.renderClouds.value = 0; 200 | 201 | renderer.render( scene, camera ); 202 | 203 | // Call render again 204 | requestAnimationFrame( render ); 205 | }; 206 | 207 | function updateUniforms(delta) { 208 | var atmosphereColor; 209 | var color = $("#atmosphereColor").val(); 210 | 211 | // Handle atmosphere color 212 | switch(color) { 213 | case "Blue": 214 | atmosphereColor = new THREE.Vector3(0.72,0.77,0.35); 215 | break; 216 | case "Pink": 217 | atmosphereColor = new THREE.Vector3(0.42,0.97,0.375); 218 | break; 219 | case "Green": 220 | atmosphereColor = new THREE.Vector3(0.72,0.37,0.575); 221 | break; 222 | case "Orange": 223 | atmosphereColor = new THREE.Vector3(0.36,0.524,0.79); 224 | break; 225 | case "White": 226 | atmosphereColor = new THREE.Vector3(0.66,0.424,0.34); 227 | break; 228 | } 229 | 230 | // Update planet uniforms 231 | planetUniforms.time.value += delta; 232 | planetUniforms.level.value = camera.position.distanceTo(new THREE.Vector3(0,0,0)); 233 | planetUniforms.sealevel.value = $("#waterLevel").val()/120; 234 | planetUniforms.noiseIntensity.value = $("#noiseIntensity").val()/1000; 235 | planetUniforms.noiseType.value = $("#noiseType").val(); 236 | planetUniforms.noiseVariation.value = $("#noiseVariation").val()/70; 237 | planetUniforms.waterColor.value = new THREE.Vector3(document.getElementById('waterColor').color.rgb[0],document.getElementById('waterColor').color.rgb[1],document.getElementById('waterColor').color.rgb[2]); 238 | planetUniforms.shoreColor.value = new THREE.Vector3(document.getElementById('shoreColor').color.rgb[0],document.getElementById('shoreColor').color.rgb[1],document.getElementById('shoreColor').color.rgb[2]); 239 | planetUniforms.cloudColor.value = new THREE.Vector3(document.getElementById('cloudColor').color.rgb[0],document.getElementById('cloudColor').color.rgb[1],document.getElementById('cloudColor').color.rgb[2]); 240 | planetUniforms.diffuseMaterial.value = new THREE.Vector4(document.getElementById('landColor').color.rgb[0],document.getElementById('landColor').color.rgb[1],document.getElementById('landColor').color.rgb[2],1); 241 | 242 | // Atmosphere 243 | var cameraHeight = camera.position.length(); 244 | atmosphereUniforms.fCameraHeight.value = cameraHeight; 245 | atmosphereUniforms.fCameraHeight2.value = cameraHeight * cameraHeight; 246 | atmosphereUniforms.v3InvWavelength.value = new THREE.Vector3(1 / Math.pow(atmosphereColor.x,4),1 / Math.pow(atmosphereColor.y,4),1 / Math.pow(atmosphereColor.z,4)); 247 | atmosphereUniforms.level.value = camera.position.distanceTo(new THREE.Vector3(0,0,0)); 248 | } 249 | 250 | // Update window if resized 251 | $(window).resize(function( event ) { 252 | width = window.innerWidth - 200; 253 | renderer.setSize( width, window.innerHeight); 254 | camera.aspect = width / window.innerHeight; 255 | camera.updateProjectionMatrix(); 256 | }); 257 | 258 | // Handle user browser notification 259 | // IE can't even render the planet 260 | // FF produces weird noise artefacts that I don't have time to fix ( related to sphere geometry? ) 261 | var isFirefox = typeof InstallTrigger !== 'undefined'; 262 | var isIE = /*@cc_on!@*/false || !!document.documentMode; 263 | if(isIE || isFirefox){ 264 | $("body").before("

This application runs the best in the newest versions of safari or chrome

"); 265 | $("#renderClouds").remove(); 266 | $("#renderCloudsP").remove(); 267 | } 268 | -------------------------------------------------------------------------------- /src/js/views/Biome.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | 4 | class Biome { 5 | 6 | constructor() { 7 | 8 | this.canvas = document.createElement("canvas"); 9 | this.canvas.id = "biomeCanvas"; 10 | this.canvas.width = 512; 11 | this.canvas.height = 512; 12 | this.canvas.style.width = "200px"; 13 | this.canvas.style.height = "200px"; 14 | this.width = this.canvas.width; 15 | this.height = this.canvas.height; 16 | this.ctx = this.canvas.getContext("2d"); 17 | 18 | document.body.appendChild(this.canvas); 19 | this.toggleCanvasDisplay(false); 20 | 21 | } 22 | 23 | generateTexture(props) { 24 | 25 | this.waterLevel = props.waterLevel; 26 | 27 | let h = this.randRange(0.0, 1.0); 28 | let s = this.randRange(0.0, 0.7); 29 | let l = this.randRange(0.0, 0.6); 30 | this.baseColor = new THREE.Color().setHSL(h, s, l); 31 | this.colorAngle = this.randRange(0.2, 0.4) 32 | this.satRange = this.randRange(0.3, 0.5); 33 | this.lightRange = this.randRange(0.3, 0.5); 34 | this.circleSize = this.randRange(30, 250); 35 | // this.circleSize = 100; 36 | 37 | 38 | this.drawBase(); 39 | 40 | // circles 41 | let numCircles = Math.round(this.randRange(50, 100)); 42 | numCircles = 100; 43 | for (let i=0; i { this.updateMaterial(); }); 53 | 54 | this.metalnessControl = matFolder.add(this, "metalness", 0.0, 1.0); 55 | this.metalnessControl.onChange(value => { this.updateMaterial(); }); 56 | 57 | this.normalScaleControl = matFolder.add(this, "normalScale", -3.0, 6.0).listen(); 58 | this.normalScaleControl.onChange(value => { this.updateMaterial(); }); 59 | 60 | // debug options 61 | this.displayMap = "textureMap"; 62 | let debugFolder = gui.addFolder('Debug'); 63 | this.displayMapControl = debugFolder.add(this, "displayMap", ["textureMap", "heightMap", "moistureMap", "normalMap", "roughnessMap"]); 64 | this.displayMapControl.onChange(value => { this.updateMaterial(); }); 65 | 66 | this.showBiomeMap = false; 67 | this.showBiomeMapControl = debugFolder.add(this, "showBiomeMap"); 68 | this.showBiomeMapControl.onChange(value => { 69 | if (this.biome) { 70 | this.biome.toggleCanvasDisplay(value); 71 | } 72 | }); 73 | 74 | this.showNebulaMap = false; 75 | this.showNebulaMapControl = debugFolder.add(this, "showNebulaMap"); 76 | this.showNebulaMapControl.onChange(value => { 77 | if (this.nebulaeGradient) { 78 | this.nebulaeGradient.toggleCanvasDisplay(value); 79 | } 80 | }); 81 | 82 | 83 | 84 | 85 | this.biome = new Biome(); 86 | this.nebulaeGradient = new NebulaeGradient(); 87 | 88 | this.createScene(); 89 | this.createStars(); 90 | this.createNebula(); 91 | this.createSun(); 92 | // this.createClouds(); 93 | // this.createGlow(); 94 | 95 | // this.atmosphereRing = new AtmosphereRing(); 96 | // this.view.add(this.atmosphereRing.view); 97 | 98 | this.createAtmosphere(); 99 | this.loadSeedFromURL(); 100 | 101 | 102 | ////////////////// environment gui 103 | let enviromentFolder = gui.addFolder('Environment'); 104 | enviromentFolder.add(this.atmosphere, "atmosphere", 0.0, 1.0).step(0.01); 105 | enviromentFolder.add(this.nebula, "nebula", 0.0, 1.0).step(0.01).onChange(value => { 106 | this.nebula.updateMaterial(); 107 | }); 108 | 109 | 110 | this.rotate = true; 111 | this.autoGenerate = false; 112 | this.autoGenCountCurrent = 0; 113 | this.autoGenTime = 3*60; 114 | this.autoGenCountMax = this.autoGenTime * 60; 115 | 116 | window.gui.add(this, "rotate"); 117 | 118 | this.resolutionControl = window.gui.add(this, "resolution", [256, 512, 1024, 2048, 4096]); 119 | this.resolutionControl.onChange(value => { this.regenerate(); }); 120 | 121 | debugFolder.add(this, "autoGenerate"); 122 | this.autoGenTimeControl = debugFolder.add(this, "autoGenTime", 30, 300).step(1); 123 | this.autoGenTimeControl.onChange(value => { this.autoGenCountMax = this.autoGenTime * 60 }); 124 | 125 | this.seedStringControl = window.gui.add(this, "seedString").listen(); 126 | this.seedStringControl.onFinishChange(value => { this.loadSeedFromTextfield(); }); 127 | // window.gui.add(this, "regenerate"); 128 | window.gui.add(this, "randomize"); 129 | 130 | document.addEventListener('keydown', (event) => { 131 | if (event.keyCode == 32) { 132 | this.randomize(); 133 | } 134 | }); 135 | 136 | window.onpopstate = (event) => { 137 | this.loadSeedFromURL(); 138 | }; 139 | 140 | this.renderUI(); 141 | 142 | } 143 | 144 | update() { 145 | if (this.rotate) { 146 | this.ground.rotation.y += 0.0005; 147 | this.stars.view.rotation.y += 0.0003; 148 | this.nebula.view.rotation.y += 0.0003; 149 | // this.clouds.view.rotation.y += 0.0007; 150 | } 151 | 152 | this.atmosphere.update(); 153 | 154 | 155 | 156 | // this.glow.update(); 157 | 158 | if (this.autoGenerate) { 159 | this.autoGenCountCurrent++; 160 | if (this.autoGenCountCurrent > this.autoGenCountMax) { 161 | this.randomize(); 162 | } 163 | } 164 | 165 | this.stars.view.position.copy(window.camera.position); 166 | this.nebula.view.position.copy(window.camera.position); 167 | 168 | // this.atmosphereRing.update(); 169 | 170 | } 171 | 172 | renderUI(){ 173 | let infoBoxHolder = document.createElement("div"); 174 | infoBoxHolder.setAttribute("id", "infoBoxHolder"); 175 | document.body.appendChild(infoBoxHolder); 176 | 177 | let infoBox = document.createElement("div"); 178 | infoBox.setAttribute("id", "infoBox"); 179 | infoBox.innerHTML = "Planet

H - Show/Hide UI
SPACEBAR - New Planet
"; 180 | infoBoxHolder.appendChild(infoBox); 181 | 182 | let line = document.createElement("div"); 183 | line.setAttribute("id", "line"); 184 | infoBoxHolder.appendChild(line); 185 | infoBoxHolder.appendChild(window.gui.domElement); 186 | 187 | // mobile info box 188 | let mobileInfoBox = document.createElement("div"); 189 | mobileInfoBox.setAttribute("id", "infoBoxHolderMobile"); 190 | mobileInfoBox.innerHTML = "
Planet
"; 191 | document.body.appendChild(mobileInfoBox); 192 | 193 | this.updatePlanetName(); 194 | 195 | // new planet button 196 | let newPlanetButtonHolder = document.createElement("div"); 197 | newPlanetButtonHolder.setAttribute("id", "newPlanetButtonHolder"); 198 | newPlanetButtonHolder.innerHTML = "
New Planet
"; 199 | document.body.appendChild(newPlanetButtonHolder); 200 | 201 | let newPlanetButton = document.getElementById("newPlanetButton"); 202 | newPlanetButton.addEventListener('click', (e) => {this.randomize()} ); 203 | } 204 | 205 | updatePlanetName() { 206 | let planetName = document.getElementById("planetName"); 207 | if (planetName != null) { 208 | planetName.innerHTML = this.seedString; 209 | } 210 | 211 | let planetNameMobile = document.getElementById("planetNameMobile"); 212 | if (planetNameMobile != null) { 213 | planetNameMobile.innerHTML = this.seedString; 214 | } 215 | } 216 | 217 | initSeed() { 218 | window.rng = seedrandom(this.seedString); 219 | } 220 | 221 | loadSeedFromURL() { 222 | this.seedString = this.getParameterByName("seed"); 223 | if (this.seedString) { 224 | console.log("seed string exists"); 225 | this.regenerate(); 226 | } else { 227 | console.log("no seed string"); 228 | this.randomize(); 229 | } 230 | 231 | } 232 | 233 | loadSeedFromTextfield() { 234 | let url = this.updateQueryString("seed", this.seedString); 235 | window.history.pushState({seed: this.seedString}, this.seedString, url); 236 | this.regenerate(); 237 | } 238 | 239 | regenerate() { 240 | this.autoGenCountCurrent = 0; 241 | this.renderScene(); 242 | } 243 | 244 | randomize() { 245 | // this.seedString = randomString(10); 246 | 247 | let n = Math.random(); 248 | let wordCount = 0; 249 | if (n > 0.8) wordCount = 1; 250 | else if (n > 0.4) wordCount = 2; 251 | else wordCount = 3; 252 | 253 | this.seedString = ""; 254 | for (let i=0; i { 393 | this.updateMaterial(); 394 | }); 395 | } 396 | 397 | updateMaterial() { 398 | for (let i=0; i<6; i++) { 399 | let material = this.materials[i]; 400 | material.roughness = this.roughness; 401 | material.metalness = this.metalness; 402 | 403 | if (this.displayMap == "textureMap") { 404 | material.map = this.textureMaps[i]; 405 | material.normalMap = this.normalMaps[i]; 406 | material.normalScale = new THREE.Vector2(this.normalScale, this.normalScale); 407 | material.roughnessMap = this.roughnessMaps[i]; 408 | // material.metalnessMap = this.roughnessMaps[i]; 409 | } 410 | else if (this.displayMap == "heightMap") { 411 | material.map = this.heightMaps[i]; 412 | material.normalMap = null; 413 | material.roughnessMap = null; 414 | } 415 | else if (this.displayMap == "moistureMap") { 416 | material.map = this.moistureMaps[i]; 417 | material.normalMap = null; 418 | material.roughnessMap = null; 419 | } 420 | else if (this.displayMap == "normalMap") { 421 | material.map = this.normalMaps[i]; 422 | material.normalMap = null; 423 | material.roughnessMap = null; 424 | } 425 | else if (this.displayMap == "roughnessMap") { 426 | material.map = this.roughnessMaps[i]; 427 | material.normalMap = null; 428 | material.roughnessMap = null; 429 | } 430 | 431 | material.needsUpdate = true; 432 | } 433 | } 434 | 435 | renderBiomeTexture() { 436 | this.biome.generateTexture({waterLevel: this.waterLevel}); 437 | } 438 | 439 | renderNebulaeGradient() { 440 | this.nebulaeGradient.generateTexture(); 441 | } 442 | 443 | createAtmosphere() { 444 | this.atmosphere = new Atmosphere(); 445 | // this.atmosphere.color = this.glow.color; 446 | this.view.add(this.atmosphere.view); 447 | } 448 | 449 | createGlow() { 450 | this.glow = new Glow(); 451 | // this.glow.color = this.atmosphere.color; 452 | this.view.add(this.glow.view); 453 | } 454 | 455 | createClouds() { 456 | this.clouds = new Clouds(); 457 | this.view.add(this.clouds.view); 458 | } 459 | 460 | createStars() { 461 | this.stars = new Stars(); 462 | this.view.add(this.stars.view); 463 | } 464 | 465 | createNebula() { 466 | this.nebula = new Nebula(); 467 | this.view.add(this.nebula.view); 468 | } 469 | 470 | createSun() { 471 | this.sun = new Sun(); 472 | this.view.add(this.sun.view); 473 | } 474 | 475 | updateNormalScaleForRes(value) { 476 | if (value == 256) this.normalScale = 0.25; 477 | if (value == 512) this.normalScale = 0.5; 478 | if (value == 1024) this.normalScale = 1.0; 479 | if (value == 2048) this.normalScale = 1.5; 480 | if (value == 4096) this.normalScale = 3.0; 481 | } 482 | 483 | randRange(low, high) { 484 | let range = high - low; 485 | let n = window.rng() * range; 486 | return low + n; 487 | } 488 | 489 | computeGeometry(geometry) { 490 | // geometry.makeGroups(); 491 | geometry.computeVertexNormals() 492 | geometry.computeFaceNormals(); 493 | geometry.computeMorphNormals(); 494 | geometry.computeBoundingSphere(); 495 | geometry.computeBoundingBox(); 496 | // geometry.computeLineDistances(); 497 | 498 | geometry.verticesNeedUpdate = true; 499 | geometry.elementsNeedUpdate = true; 500 | geometry.uvsNeedUpdate = true; 501 | geometry.normalsNeedUpdate = true; 502 | // geometry.tangentsNeedUpdate = true; 503 | geometry.colorsNeedUpdate = true; 504 | geometry.lineDistancesNeedUpdate = true; 505 | // geometry.buffersNeedUpdate = true; 506 | geometry.groupsNeedUpdate = true; 507 | } 508 | 509 | updateQueryString(key, value, url) { 510 | if (!url) url = window.location.href; 511 | var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"), 512 | hash; 513 | 514 | if (re.test(url)) { 515 | if (typeof value !== 'undefined' && value !== null) 516 | return url.replace(re, '$1' + key + "=" + value + '$2$3'); 517 | else { 518 | hash = url.split('#'); 519 | url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, ''); 520 | if (typeof hash[1] !== 'undefined' && hash[1] !== null) 521 | url += '#' + hash[1]; 522 | return url; 523 | } 524 | } 525 | else { 526 | if (typeof value !== 'undefined' && value !== null) { 527 | var separator = url.indexOf('?') !== -1 ? '&' : '?'; 528 | hash = url.split('#'); 529 | url = hash[0] + separator + key + '=' + value; 530 | if (typeof hash[1] !== 'undefined' && hash[1] !== null) 531 | url += '#' + hash[1]; 532 | return url; 533 | } 534 | else 535 | return url; 536 | } 537 | } 538 | 539 | getParameterByName(name, url) { 540 | if (!url) url = window.location.href; 541 | name = name.replace(/[\[\]]/g, "\\$&"); 542 | var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), 543 | results = regex.exec(url); 544 | if (!results) return null; 545 | if (!results[2]) return ''; 546 | return decodeURIComponent(results[2].replace(/\+/g, " ")); 547 | } 548 | 549 | } 550 | 551 | export default Planet; 552 | --------------------------------------------------------------------------------