├── .gitignore ├── LICENSE ├── README.md ├── bundler ├── webpack.common.js ├── webpack.dev.js └── webpack.prod.js ├── package.json ├── src ├── blockchain │ └── PropertySign.js ├── common │ ├── Grid.js │ ├── Helper.js │ ├── Library.js │ └── Parameters.js ├── controls │ ├── Controls.js │ ├── PointerLockControls.js │ └── mobile │ │ ├── movement-pad.js │ │ ├── rotation-pad.js │ │ └── touch-controls.js ├── index.html ├── main.js ├── multiplayer │ ├── Multiplayer.js │ └── Player.js ├── postprocessing │ └── PostProcessor.js ├── procedural │ ├── MultiverseFactory.js │ ├── Workers.js │ ├── galaxy │ │ ├── Galaxy.js │ │ ├── IrregularGalaxyWorker.js │ │ ├── SombreroGalaxyWorker.js │ │ └── SpiralGalaxyWorker.js │ ├── giant │ │ ├── Giant.js │ │ ├── StarGiantWorker.js │ │ ├── SunGiantWorker.js │ │ └── WhiteDwarfGiantWorker.js │ ├── nebula │ │ ├── EmissionNebulaWorker.js │ │ ├── GargantuaNebulaWorker.js │ │ ├── Nebula.js │ │ └── SupernovaRemnantsNebulaWorker.js │ ├── singularity │ │ ├── BlackHoleSingularityWorker.js │ │ └── Singularity.js │ ├── spaceship │ │ ├── Spaceship.js │ │ └── SpaceshipWorker.js │ ├── starfield │ │ ├── GlobularStarfieldWorker.js │ │ ├── OpenStarfieldWorker.js │ │ └── Starfield.js │ └── strangerthings │ │ ├── CyclicWorker.js │ │ ├── SpearWorker.js │ │ └── StrangerThings.js ├── sequencer │ ├── epiphany │ │ └── Epiphany.js │ ├── sequencer.js │ └── wormhole │ │ └── Wormhole.js ├── shaders │ ├── giant │ │ ├── star │ │ │ ├── fragment.glsl │ │ │ └── vertex.glsl │ │ ├── sun │ │ │ ├── fragment.glsl │ │ │ └── vertex.glsl │ │ └── whitedwarf │ │ │ ├── fragment.glsl │ │ │ └── vertex.glsl │ └── singularity │ │ └── blackhole │ │ ├── fragment.glsl │ │ └── vertex.glsl ├── style.css └── universe │ └── Universe.js └── static ├── .gitkeep ├── audio ├── borealis.mp3 ├── celestial.mp3 ├── discovery.mp3 ├── ghosts.mp3 ├── intothenight.mp3 ├── omega.mp3 ├── transcendent.mp3 └── wormhole.mp3 ├── favicon.ico ├── fonts ├── Roboto_Regular.json ├── helvetiker_regular.typeface.json └── optimer_regular.typeface.json ├── models ├── player │ └── sphere.glb └── spaceship │ ├── normandy │ └── normandy.fbx │ └── station │ └── station.glb ├── preview.jpg ├── textures ├── blackhole │ └── disk.jpg ├── nebula │ ├── cloud1.png │ ├── cloud2.png │ ├── cloud3.png │ ├── cloud4.png │ ├── cloud5.png │ ├── cloud7.png │ ├── cloud8.png │ ├── cloud9.png │ └── test.jpeg ├── player │ ├── sphere_AO.jpg │ ├── sphere_albedo.jpg │ ├── sphere_emissive.jpg │ ├── sphere_metallic.jpg │ ├── sphere_normal.png │ ├── sphere_opacity.jpg │ └── sphere_roughness.jpg ├── spaceship │ ├── normandy │ │ ├── decals.png │ │ ├── ingame.png │ │ └── ingamei.png │ └── station │ │ ├── ao.jpg │ │ ├── basecolor.jpg │ │ ├── diffuse.jpg │ │ ├── height.jpg │ │ ├── normal.jpg │ │ ├── opacity.jpg │ │ ├── reflection.jpg │ │ └── specular.jpg ├── starfield │ ├── brightstar1.png │ ├── brightstar2.png │ ├── brightstar3.png │ ├── brightstar4.png │ ├── star1.png │ ├── star10.png │ ├── star11.png │ ├── star2.png │ ├── star3.png │ ├── star4.png │ ├── star5.png │ ├── star6.png │ ├── star7.png │ ├── star8.png │ └── star9.png ├── universe │ └── universe1.png └── wormhole │ ├── galaxy1.jpg │ ├── galaxy2.jpg │ ├── galaxy3.jpg │ ├── galaxy4.jpg │ └── galaxy5.jpeg └── ui ├── credits.png ├── esc.png ├── headphone.svg ├── hide.png ├── logo.png ├── look.png ├── move.png ├── mute.png ├── nav.png └── skip.png /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /test/js 3 | /test/browsertest/js 4 | /test/fixtures/temp-* 5 | /test/temp 6 | /test/ChangesAndRemovals 7 | /benchmark/js 8 | /benchmark/fixtures 9 | /examples/**/dist 10 | /coverage 11 | /.nyc_output 12 | /.jest-cache 13 | .DS_Store 14 | *.log 15 | .idea 16 | .vscode 17 | .cache 18 | .eslintcache 19 | package-lock.json 20 | /dist -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Mehdi ZOGHLAMI (https://twitter.com/jesuisundev) 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Across The Multiverse](https://across-multiverse.com/preview.jpg) 2 | 3 | # Across The Multiverse 4 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://standardjs.com/) 5 | 6 | **An in-browser, freely explorable, 3D game across infinite universes procedurally generated. 7 | Go from universe to universe and discover the origin of everything. 8 | A four chapter story with an epic revelation at the end.** 9 | 10 | https://across-multiverse.com/ 11 | 12 | 13 | A blog post explaining how everything works is available [here](https://www.jesuisundev.com/en/i-built-the-entire-universe-in-javascript/) ! 14 | 15 | # License 16 | 17 | 18 | ## Source Code 19 | 20 | Under [BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause). 21 | 22 | ## Music 23 | 24 | All musics are owned by artists (see list below) and are used here under 25 | [Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0)](https://creativecommons.org/licenses/by-nc-sa/3.0/) license 26 | 27 | 28 | **You can't use the musics for commercial use and/or without mentioning the following artists** 29 | 30 | - Transcendent - Joel Nielsen 31 | - I Walk With Ghosts - Scott Buckley 32 | - Discovery - Scott Buckley 33 | - Celestial - Scott Buckley 34 | - Omega - Scott Buckley 35 | - Into the Night - Melodysheep 36 | 37 | ## Footage 38 | 39 | Under [Attribution 4.0 International (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/) 40 | 41 | **You can use footage of the game for any use but you must mention this project with the following link : https://across-multiverse.com/** 42 | 43 | # Install 44 | 45 | ``` 46 | npm install 47 | ``` 48 | 49 | # Launch 50 | 51 | ``` 52 | npm start 53 | ``` 54 | 55 | # Build 56 | 57 | ``` 58 | npm run-script build 59 | ``` 60 | 61 | # Contributing 62 | 63 | Across The Multiverse is an **Open Source Project**. This means that: 64 | 65 | > Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more a fun experience than a standard guarded open source project. 66 | 67 | Every commit on the main branch automatically deploy a new build on [cloudflare pages](https://pages.cloudflare.com/) which is accesible at https://across-multiverse.com/ 68 | -------------------------------------------------------------------------------- /bundler/webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const copyWebpackPlugin = require('copy-webpack-plugin') 3 | const htmlWebpackPlugin = require('html-webpack-plugin') 4 | const miniCSSExtractPlugin = require('mini-css-extract-plugin') 5 | const webpackObfuscator = require('webpack-obfuscator') 6 | 7 | module.exports = { 8 | entry: path.resolve(__dirname, '../src/main.js'), 9 | output: { 10 | filename: 'bundle.[contenthash].js', 11 | path: path.resolve(__dirname, '../dist') 12 | }, 13 | devtool: 'source-map', 14 | plugins: [ 15 | new copyWebpackPlugin({ 16 | patterns: [ 17 | { from: path.resolve(__dirname, '../static') } 18 | ] 19 | }), 20 | new htmlWebpackPlugin({ 21 | template: path.resolve(__dirname, '../src/index.html'), 22 | minify: true 23 | }), 24 | new miniCSSExtractPlugin(), 25 | // new webpackObfuscator ({ 26 | // rotateStringArray: true 27 | // }, []) 28 | ], 29 | module: { 30 | rules: [{ 31 | test: /\.(html)$/, 32 | use: ['html-loader'] 33 | }, 34 | { 35 | test: /\.js$/, 36 | exclude: /node_modules/, 37 | use: [ 38 | 'babel-loader' 39 | ] 40 | }, 41 | { 42 | test: /\.(glsl|vs|fs|vert|frag)$/, 43 | exclude: /node_modules/, 44 | use: [ 45 | 'raw-loader' 46 | ] 47 | }, 48 | { 49 | test: /\.css$/, 50 | use: [ 51 | miniCSSExtractPlugin.loader, 52 | 'css-loader' 53 | ] 54 | }, 55 | { 56 | test: /\.(jpg|png|gif|svg)$/, 57 | use: [{ 58 | loader: 'file-loader', 59 | options: { 60 | outputPath: 'assets/images/' 61 | } 62 | }] 63 | }, 64 | { 65 | test: /\.(ttf|eot|woff|woff2)$/, 66 | use: [{ 67 | loader: 'file-loader', 68 | options: { 69 | outputPath: 'assets/fonts/' 70 | } 71 | }] 72 | } 73 | ] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /bundler/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge') 2 | const commonConfiguration = require('./webpack.common.js') 3 | const ip = require('internal-ip') 4 | const portFinderSync = require('portfinder-sync') 5 | 6 | const infoColor = (_message) => { 7 | return `\u001b[1m\u001b[34m${_message}\u001b[39m\u001b[22m` 8 | } 9 | 10 | module.exports = merge( 11 | commonConfiguration, { 12 | mode: 'development', 13 | devServer: { 14 | host: '0.0.0.0', 15 | port: portFinderSync.getPort(8080), 16 | contentBase: './dist', 17 | watchContentBase: true, 18 | open: false, 19 | https: false, 20 | useLocalIp: true, 21 | disableHostCheck: true, 22 | overlay: true, 23 | noInfo: true, 24 | after: function (app, server, compiler) { 25 | const port = server.options.port 26 | const https = server.options.https ? 's' : '' 27 | const localIp = ip.v4.sync() 28 | const domain1 = `http${https}://${localIp}:${port}` 29 | const domain2 = `http${https}://localhost:${port}` 30 | 31 | console.log(`server at \n - ${infoColor(domain1)}\n - ${infoColor(domain2)}`) 32 | } 33 | } 34 | } 35 | ) 36 | -------------------------------------------------------------------------------- /bundler/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge') 2 | const commonConfiguration = require('./webpack.common.js') 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') 4 | 5 | module.exports = merge( 6 | commonConfiguration, 7 | { 8 | mode: 'production', 9 | plugins: 10 | [ 11 | new CleanWebpackPlugin() 12 | ] 13 | } 14 | ) 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": "https://github.com/jesuisundev/acrosstheuniverse", 3 | "license": "MIT, CC BY-NC-SA 3.0, CC BY 4.0", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "build": "webpack --config ./bundler/webpack.prod.js", 7 | "start": "webpack serve --config ./bundler/webpack.dev.js" 8 | }, 9 | "dependencies": { 10 | "@babel/core": "^7.14.6", 11 | "@babel/preset-env": "^7.14.7", 12 | "@geckos.io/client": "^2.1.0", 13 | "babel-loader": "^8.2.2", 14 | "clean-webpack-plugin": "^3.0.0", 15 | "copy-webpack-plugin": "^9.0.1", 16 | "css-loader": "^5.2.6", 17 | "dat.gui": "^0.7.7", 18 | "file-loader": "^6.2.0", 19 | "gsap": "^3.7.1", 20 | "howler": "^2.2.3", 21 | "html-loader": "^2.1.2", 22 | "html-webpack-plugin": "^5.3.2", 23 | "mini-css-extract-plugin": "^2.0.0", 24 | "portfinder-sync": "0.0.2", 25 | "postprocessing": "^6.22.1", 26 | "raw-loader": "^4.0.2", 27 | "stats.js": "^0.17.0", 28 | "style-loader": "^3.0.0", 29 | "three": "^0.130.0", 30 | "webpack": "^5.41.1", 31 | "webpack-cli": "^4.7.2", 32 | "webpack-dev-server": "^3.11.2", 33 | "webpack-merge": "^5.8.0" 34 | }, 35 | "devDependencies": { 36 | "javascript-obfuscator": "^3.0.0", 37 | "webpack-obfuscator": "^3.5.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/blockchain/PropertySign.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { FontLoader } from 'three/src/loaders/FontLoader' 3 | 4 | export default class PropertySign { 5 | constructor(camera, scene) { 6 | this.camera = camera 7 | this.scene = scene 8 | 9 | this.text = { 10 | universeNumber: '0001', 11 | owner: 'vooodoo.eth', 12 | metadata: { 13 | type: 'Borealis (60%)', 14 | age: 'Child | ~1T year (40%)', 15 | diversity: 'High (10%)', 16 | singularity: 'Magnetar (5%)', 17 | dominantRace: 'Human (60%)' 18 | } 19 | } 20 | this.fontLoader = new FontLoader() 21 | this.fontLoader.load( 22 | 'fonts/Roboto_Regular.json', 23 | font => { this.font = font} 24 | ) 25 | } 26 | 27 | addPropertySign() { 28 | this.text.metadata = this._getLocalUniverseMetadata() 29 | 30 | const textOwner = ` 31 | Universe : #${window.currentUniverse.universeNumber} 32 | Owner : ${this.text.owner} 33 | ` 34 | 35 | const textDescription = ` 36 | Type : ${this.text.metadata.type} 37 | Age : ${this.text.metadata.age} 38 | Diversity : ${this.text.metadata.diversity} 39 | Singularity : ${this.text.metadata.singularity} 40 | Dominant Race : ${this.text.metadata.dominantRace} 41 | ` 42 | 43 | this.propertySignGeometryOwner = new THREE.TextGeometry(textOwner, { 44 | font: this.font, 45 | size: 1000, 46 | height: 1, 47 | curveSegments: 12, 48 | bevelEnabled: true, 49 | bevelThickness: 0, 50 | bevelSize: 0, 51 | bevelOffset: 0, 52 | bevelSegments: 0 53 | }) 54 | this.propertySignMaterialOwner = new THREE.MeshBasicMaterial({ color: this._getTextColor(), side: THREE.BackSide }) 55 | this.propertySignMeshOwner = new THREE.Mesh(this.propertySignGeometryOwner, this.propertySignMaterialOwner) 56 | 57 | this.propertySignGeometryDescription = new THREE.TextGeometry(textDescription, { 58 | font: this.font, 59 | size: 500, 60 | height: 1, 61 | curveSegments: 12, 62 | bevelEnabled: true, 63 | bevelThickness: 0, 64 | bevelSize: 0, 65 | bevelOffset: 0, 66 | bevelSegments: 0 67 | }) 68 | this.propertySignMaterialDescription = new THREE.MeshBasicMaterial({ color: this._getTextColor(), side: THREE.BackSide }) 69 | this.propertySignMeshDescription = new THREE.Mesh(this.propertySignGeometryDescription, this.propertySignMaterialDescription) 70 | 71 | const z = window.isFirstUniverse ? -11000 : -31000 72 | this.propertySignMeshOwner.position.set(-8000, 5000, z) 73 | this.propertySignMeshDescription.position.set(-5900, 500, z) 74 | this.scene.add(this.propertySignMeshOwner, this.propertySignMeshDescription) 75 | } 76 | 77 | dispose() { 78 | this.propertySignGeometryOwner.dispose() 79 | this.propertySignMaterialOwner.dispose() 80 | 81 | this.propertySignGeometryDescription.dispose() 82 | this.propertySignMaterialDescription.dispose() 83 | this.scene.remove(this.propertySignMeshOwner, this.propertySignMeshDescription) 84 | } 85 | 86 | _getLocalUniverseMetadata() { 87 | if(!window.currentUniverse.isReady) 88 | return {} 89 | 90 | return { 91 | type: window.currentUniverse.universeModifiers.type.displayName, 92 | age: window.currentUniverse.universeModifiers.age.displayName, 93 | diversity: window.currentUniverse.universeModifiers.diversity.displayName, 94 | singularity: window.currentUniverse.universeModifiers.singularity.displayName, 95 | dominantRace: window.currentUniverse.universeModifiers.dominantRace.displayName 96 | } 97 | } 98 | 99 | _getTextColor() { 100 | let color = 0xFFFFFF 101 | 102 | switch (window.currentUniverse.universeModifiers.type.id) { 103 | case 'eternal': 104 | color = 0x000000 105 | } 106 | 107 | return color 108 | } 109 | 110 | _getOwner() { 111 | // todo: from the blockchain 112 | } 113 | 114 | _getUniverseMetadata() { 115 | // todo: from the blockchain (from universe class - from url/hash) 116 | } 117 | } -------------------------------------------------------------------------------- /src/common/Grid.js: -------------------------------------------------------------------------------- 1 | import MultiverseFactory from '../procedural/MultiverseFactory' 2 | import Workers from '../procedural/Workers' 3 | 4 | export default class Grid { 5 | constructor (camera, parameters, scene, library) { 6 | this.camera = camera 7 | this.parameters = parameters 8 | this.scene = scene 9 | this.library = library 10 | this.activeClusters = new Map() 11 | this.queueClusters = new Map() 12 | 13 | this.multiverseFactory = new MultiverseFactory(this.scene, this.library, this.parameters) 14 | this.workers = new Workers(this) 15 | } 16 | 17 | getCurrentClusterPosition () { 18 | const currentCameraPosition = this.getCurrentCameraPosition() 19 | const xCoordinate = Math.trunc(currentCameraPosition.x / this.parameters.grid.clusterSize) 20 | const yCoordinate = Math.trunc(currentCameraPosition.y / this.parameters.grid.clusterSize) 21 | const zCoordinate = Math.trunc(currentCameraPosition.z / this.parameters.grid.clusterSize) 22 | const currentClusterPosition = `${xCoordinate},${yCoordinate},${zCoordinate}` 23 | 24 | return currentClusterPosition 25 | } 26 | 27 | getCurrentCameraPosition () { 28 | this.camera.updateMatrixWorld() 29 | 30 | return this.camera.position 31 | } 32 | 33 | getClustersStatus (currentCluster) { 34 | const clustersNeighbour = this.getNeighbourClusters(currentCluster) 35 | const clustersToPopulate = this._getEmptyClustersToPopulate(clustersNeighbour) 36 | const clustersToDispose = this._getPopulatedClustersToDispose(clustersNeighbour, currentCluster) 37 | 38 | return { 39 | clustersNeighbour, 40 | clustersToPopulate, 41 | clustersToDispose 42 | } 43 | } 44 | 45 | getNeighbourClusters (currentCluster) { 46 | const neighbourClusters = [currentCluster] 47 | const currentClusterArray = currentCluster.split(',') 48 | const x = currentClusterArray[0] 49 | const y = currentClusterArray[1] 50 | const z = currentClusterArray[2] 51 | 52 | // forward 53 | neighbourClusters.push(`${x},${y},${Number(z) - 1}`) 54 | 55 | // backward 56 | neighbourClusters.push(`${x},${y},${Number(z) + 1}`) 57 | 58 | // right 59 | neighbourClusters.push(`${Number(x) + 1},${y},${z}`) 60 | 61 | // left 62 | neighbourClusters.push(`${Number(x) - 1},${y},${z}`) 63 | 64 | // forward right 65 | neighbourClusters.push(`${Number(x) + 1},${y},${Number(z) - 1}`) 66 | 67 | // forward left 68 | neighbourClusters.push(`${Number(x) - 1},${y},${Number(z) - 1}`) 69 | 70 | // backward right 71 | neighbourClusters.push(`${Number(x) + 1},${y},${Number(z) + 1}`) 72 | 73 | // backward left 74 | neighbourClusters.push(`${Number(x) - 1},${y},${Number(z) + 1}`) 75 | 76 | return neighbourClusters 77 | } 78 | 79 | disposeClusters (clustersToDispose) { 80 | for (const clusterToDispose of clustersToDispose) { 81 | let matter = this.activeClusters.get(clusterToDispose) 82 | 83 | matter.dispose() 84 | matter = null 85 | 86 | this.activeClusters.delete(clusterToDispose) 87 | } 88 | } 89 | 90 | addMattersToClustersQueue (matters, type = 'starfield', subtype = null) { 91 | for (const clusterToPopulate of Object.keys(matters)) { 92 | this.queueClusters.set(clusterToPopulate, { 93 | type: type, 94 | subtype: subtype, 95 | data: matters[clusterToPopulate] 96 | }) 97 | } 98 | } 99 | 100 | populateNewUniverse () { 101 | const clusterStatus = this.getClustersStatus('0,0,0') 102 | this.workers = new Workers(this) 103 | this.buildMatters(clusterStatus.clustersToPopulate) 104 | } 105 | 106 | buildMatters (clustersToPopulate) { 107 | for (const clusterToPopulate of clustersToPopulate) { 108 | let randomDistributedWorker = this.workers.getWorkerDistributed(clusterToPopulate) 109 | if (!randomDistributedWorker) { 110 | // TODO - why the fuck this happen, fix it 111 | randomDistributedWorker = this.workers.openStarfieldWorker.source 112 | } 113 | 114 | randomDistributedWorker.postMessage({ 115 | clustersToPopulate: [clusterToPopulate], 116 | parameters: this.parameters, 117 | currentUniverse: window.currentUniverse 118 | }) 119 | } 120 | } 121 | 122 | renderMatters (position, cluster) { 123 | const matter = this.multiverseFactory.createMatter(cluster.type) 124 | 125 | matter.generate(cluster.data, position, cluster.subtype) 126 | matter.show() 127 | 128 | this.queueClusters.delete(position) 129 | this.activeClusters.set(position, matter) 130 | } 131 | 132 | _getEmptyClustersToPopulate (neighbourClusters) { 133 | const emptyClustersToPopulate = [] 134 | 135 | for (const neighbourCluster of neighbourClusters) { 136 | if (!this.activeClusters.has(neighbourCluster)) { 137 | emptyClustersToPopulate.push(neighbourCluster) 138 | } 139 | } 140 | 141 | return emptyClustersToPopulate 142 | } 143 | 144 | _getPopulatedClustersToDispose (neighbourClusters, currentCluster) { 145 | const populatedClustersToDispose = [] 146 | 147 | for (const activeClusterKey of this.activeClusters.keys()) { 148 | if (currentCluster !== activeClusterKey && !neighbourClusters.includes(activeClusterKey)) { 149 | populatedClustersToDispose.push(activeClusterKey) 150 | } 151 | } 152 | 153 | return populatedClustersToDispose 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/common/Helper.js: -------------------------------------------------------------------------------- 1 | export default class Helper { 2 | constructor (parameters) { 3 | this.parameters = parameters 4 | } 5 | 6 | getResizedRenderResolution () { 7 | let renderResolution = { 8 | renderWidth: window.innerWidth, 9 | renderHeight: window.innerHeight, 10 | divider: 1 11 | } 12 | 13 | if (window.innerWidth >= 7600) { 14 | renderResolution.divider = 1.6 15 | } else if (window.innerWidth >= 3800) { 16 | renderResolution.divider = 1.4 17 | } else if (window.innerWidth >= 2500) { 18 | renderResolution.divider = 1.2 19 | } else if (window.innerWidth >= 1900) { 20 | renderResolution.divider = 1.1 21 | } 22 | 23 | renderResolution.renderWidth = Math.floor(window.innerWidth / renderResolution.divider) 24 | renderResolution.renderHeight = Math.floor(window.innerHeight / renderResolution.divider) 25 | 26 | return renderResolution 27 | } 28 | 29 | // i'm lazy, we could refactor some code and avoid globals 30 | setDefaultGlobal () { 31 | // tochange 32 | window.isDebugMode = false 33 | window.isFirstUniverse = true 34 | window.isMetaverse = false 35 | window.highend = false 36 | window.storyCurrentUniverse = 0 37 | window.materialsToUpdate = {} 38 | window.meshesToUpdate = {} 39 | window.spaceshipToUpdate = {} 40 | window.nebulaToUpdate = {} 41 | window.cyclicStrangerThingsToUpdate = {} 42 | window.wormhole = { shape: null, CameraPositionIndex: 0, speed: this.parameters.wormhole.speed, active: false } 43 | window.sequencer = { active: false } 44 | window.isMobileOrTabletFlag = this.isMobileOrTablet() 45 | } 46 | 47 | isMobileOrTablet () { 48 | return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test((navigator.userAgent || navigator.vendor || window.opera)) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test((navigator.userAgent || navigator.vendor || window.opera).substr(0, 4)) 49 | } 50 | 51 | onModeChosen(event, isMetaverse = false) { 52 | event.preventDefault() 53 | 54 | window.isMetaverse = isMetaverse 55 | document.getElementById('mode').style.display = 'none' 56 | document.getElementById('launcher').style.display = 'block' 57 | document.getElementById('warning-pc').style.visibility = 'visible' 58 | } 59 | 60 | /** 61 | * show document element by seting the class fadeIn 62 | * 63 | * @param {String} id id of the element in the document 64 | * @param {Number} fadeInTime time in millisecond before fadein 65 | */ 66 | async showElementById(id, fadeInTime = 1000) { 67 | return await new Promise(resolve => { 68 | setTimeout(() => { 69 | document.getElementById(id).className = 'fadeIn' 70 | resolve() 71 | }, fadeInTime) 72 | }) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/controls/Controls.js: -------------------------------------------------------------------------------- 1 | import { PointerLockControls } from './PointerLockControls.js' 2 | import * as THREE from 'three' 3 | 4 | export default class Controls { 5 | constructor (camera, parameters, sequencer, library) { 6 | this.parameters = parameters 7 | this.camera = camera 8 | this.sequencer = sequencer 9 | this.library = library 10 | 11 | this.pointerLockControls = new PointerLockControls(this.camera, document.body) 12 | 13 | this.velocity = new THREE.Vector3() 14 | this.direction = new THREE.Vector3() 15 | 16 | this.moveForward = false 17 | this.moveBackward = false 18 | this.moveLeft = false 19 | this.moveRight = false 20 | 21 | this.uiVisible = true 22 | this.toggleUiInProgress = false 23 | 24 | this.creditsVisible = false 25 | this.toggleCreditsInProgress = false 26 | 27 | this.soundMuted = false 28 | this.toggleSoundInProgress = false 29 | } 30 | 31 | onKeyDown (event) { 32 | if (this.pointerLockControls.isLocked) { 33 | switch (event.code) { 34 | case 'ArrowUp': 35 | case 'KeyW': 36 | case 'KeyZ': 37 | this.moveForward = true 38 | break 39 | case 'ArrowLeft': 40 | case 'KeyA': 41 | case 'KeyQ': 42 | this.moveLeft = true 43 | break 44 | case 'ArrowDown': 45 | case 'KeyS': 46 | this.moveBackward = true 47 | break 48 | case 'ArrowRight': 49 | case 'KeyD': 50 | this.moveRight = true 51 | break 52 | case 'KeyF': 53 | this.sequencer.wormholeSequence() 54 | break 55 | case 'KeyH': 56 | this.toggleUi() 57 | break 58 | case 'KeyC': 59 | this.toggleCredits() 60 | break 61 | } 62 | } 63 | 64 | // sound control should be free or people will freak out 65 | if(event.code === 'KeyM') this.toggleSound() 66 | } 67 | 68 | onKeyUp (event) { 69 | if (this.pointerLockControls.isLocked) { 70 | switch (event.code) { 71 | case 'ArrowUp': 72 | case 'KeyW': 73 | case 'KeyZ': 74 | this.moveForward = false 75 | break 76 | case 'ArrowLeft': 77 | case 'KeyA': 78 | case 'KeyQ': 79 | this.moveLeft = false 80 | break 81 | case 'ArrowDown': 82 | case 'KeyS': 83 | this.moveBackward = false 84 | break 85 | case 'ArrowRight': 86 | case 'KeyD': 87 | this.moveRight = false 88 | break 89 | } 90 | } 91 | } 92 | 93 | 94 | handleMovements (timePerf, prevTimePerf) { 95 | if (window.sequencer.active) return 96 | 97 | const delta = timePerf - prevTimePerf 98 | 99 | this.direction.z = Number(this.moveForward) - Number(this.moveBackward) 100 | this.direction.x = Number(this.moveRight) - Number(this.moveLeft) 101 | 102 | if (this.moveForward || this.moveBackward) { 103 | if ((this.moveForward && this.parameters.controls.speedLimit.down < this.velocity.z) || (this.moveBackward && this.parameters.controls.speedLimit.up > this.velocity.z)) { 104 | this.velocity.z -= this.direction.z * this.parameters.controls.velocity * delta 105 | } 106 | } 107 | 108 | if (this.moveLeft || this.moveRight) { 109 | if ((this.moveLeft && this.parameters.controls.speedLimit.up > this.velocity.x) || (this.moveRight && this.parameters.controls.speedLimit.down < this.velocity.x)) { 110 | this.velocity.x -= this.direction.x * this.parameters.controls.velocity * delta 111 | } 112 | } 113 | 114 | this.pointerLockControls.moveRight(-this.velocity.x * delta) 115 | this.pointerLockControls.moveForward(-this.velocity.z * delta) 116 | } 117 | 118 | async toggleUi() { 119 | if (this.toggleUiInProgress || window.sequencer.active) return 120 | 121 | this.toggleUiInProgress = true 122 | 123 | if (this.uiVisible) { 124 | await this.sequencer.fadeOutById('#nav', 0.3, 'Power0.easeNone') 125 | this.uiVisible = false 126 | } else { 127 | await this.sequencer.fadeInById('#nav', 0.3, 'Power0.easeNone') 128 | this.uiVisible = true 129 | } 130 | 131 | this.toggleUiInProgress = false 132 | } 133 | 134 | async toggleCredits() { 135 | if (this.toggleCreditsInProgress || window.sequencer.active) return 136 | 137 | this.toggleCreditsInProgress = true 138 | 139 | if (this.creditsVisible) { 140 | await this.sequencer.fadeOutById('#credits', 0.3, 'Power0.easeNone') 141 | this.creditsVisible = false 142 | } else { 143 | await this.sequencer.fadeInById('#credits', 0.3, 'Power0.easeNone') 144 | this.creditsVisible = true 145 | } 146 | 147 | this.toggleCreditsInProgress = false 148 | } 149 | 150 | async toggleSound() { 151 | if (this.toggleSoundInProgress) return 152 | 153 | this.toggleSoundInProgress = true 154 | 155 | if (this.soundMuted) { 156 | Object.keys(this.library.audio).forEach(key => this.library.audio[key].mute(false)) 157 | this.soundMuted = false 158 | } else { 159 | Object.keys(this.library.audio).forEach(key => this.library.audio[key].mute(true)) 160 | this.soundMuted = true 161 | } 162 | 163 | this.toggleSoundInProgress = false 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/controls/PointerLockControls.js: -------------------------------------------------------------------------------- 1 | import { Euler, EventDispatcher, Vector3} from 'three/build/three.module' 2 | 3 | const _euler = new Euler(0, 0, 0, 'YXZ') 4 | const _vector = new Vector3() 5 | 6 | const _changeEvent = { type: 'change' } 7 | const _lockEvent = { type: 'lock' } 8 | const _unlockEvent = { type: 'unlock' } 9 | 10 | const _PI_2 = Math.PI / 2 11 | 12 | class PointerLockControls extends EventDispatcher { 13 | 14 | constructor (camera, domElement) { 15 | super() 16 | 17 | if (domElement === undefined) { 18 | console.warn('THREE.PointerLockControls: The second parameter "domElement" is now mandatory.') 19 | domElement = document.body 20 | } 21 | 22 | this.domElement = domElement 23 | this.isLocked = false 24 | 25 | // Set to constrain the pitch of the camera 26 | // Range is 0 to Math.PI radians 27 | this.minPolarAngle = 0 // radians 28 | this.maxPolarAngle = Math.PI // radians 29 | 30 | const scope = this 31 | 32 | function onMouseMove (event) { 33 | if (scope.isLocked === false || window.sequencer.active) return 34 | 35 | const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0 36 | const movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0 37 | 38 | _euler.setFromQuaternion(camera.quaternion) 39 | 40 | _euler.y -= movementX * 0.002 41 | _euler.x -= movementY * 0.002 42 | 43 | _euler.x = Math.max(_PI_2 - scope.maxPolarAngle, Math.min(_PI_2 - scope.minPolarAngle, _euler.x)) 44 | 45 | camera.quaternion.setFromEuler(_euler) 46 | 47 | scope.dispatchEvent(_changeEvent) 48 | } 49 | 50 | function onPointerlockChange () { 51 | if (scope.domElement.ownerDocument.pointerLockElement === scope.domElement) { 52 | scope.dispatchEvent(_lockEvent) 53 | 54 | scope.isLocked = true 55 | } else { 56 | scope.dispatchEvent(_unlockEvent) 57 | 58 | scope.isLocked = false 59 | } 60 | } 61 | 62 | function onPointerlockError () { 63 | console.error('THREE.PointerLockControls: Unable to use Pointer Lock API') 64 | } 65 | 66 | this.connect = function () { 67 | scope.domElement.ownerDocument.addEventListener('mousemove', onMouseMove) 68 | scope.domElement.ownerDocument.addEventListener('pointerlockchange', onPointerlockChange) 69 | scope.domElement.ownerDocument.addEventListener('pointerlockerror', onPointerlockError) 70 | } 71 | 72 | this.disconnect = function () { 73 | scope.domElement.ownerDocument.removeEventListener('mousemove', onMouseMove) 74 | scope.domElement.ownerDocument.removeEventListener('pointerlockchange', onPointerlockChange) 75 | scope.domElement.ownerDocument.removeEventListener('pointerlockerror', onPointerlockError) 76 | } 77 | 78 | this.dispose = function () { 79 | this.disconnect() 80 | } 81 | 82 | this.getObject = function () { // retaining this method for backward compatibility 83 | 84 | return camera 85 | } 86 | 87 | this.getDirection = function () { 88 | const direction = new Vector3(0, 0, - 1) 89 | 90 | return function (v) { 91 | return v.copy(direction).applyQuaternion(camera.quaternion) 92 | } 93 | }() 94 | 95 | this.moveForward = function (distance) { 96 | 97 | // move forward parallel to the xz-plane 98 | // assumes camera.up is y-up 99 | 100 | _vector.setFromMatrixColumn(camera.matrix, 0) 101 | 102 | _vector.crossVectors(camera.up, _vector) 103 | 104 | camera.position.addScaledVector(_vector, distance) 105 | } 106 | 107 | this.moveRight = function (distance) { 108 | _vector.setFromMatrixColumn(camera.matrix, 0) 109 | 110 | camera.position.addScaledVector(_vector, distance) 111 | } 112 | 113 | this.lock = function () { 114 | this.domElement.requestPointerLock() 115 | } 116 | 117 | this.unlock = function () { 118 | scope.domElement.ownerDocument.exitPointerLock() 119 | } 120 | 121 | this.connect() 122 | } 123 | 124 | } 125 | 126 | export { PointerLockControls } 127 | -------------------------------------------------------------------------------- /src/controls/mobile/movement-pad.js: -------------------------------------------------------------------------------- 1 | /* 2 | just force myself to handle mobile devices. 3 | this is a rewrite of http://mese79.github.io/TouchControls/ 4 | it's working but its shit, make it better before using it 5 | i hate mobile devices with a fierce passion of a millions suns 6 | */ 7 | 8 | export default class MovementPad { 9 | constructor() { 10 | this.mouseDown = false 11 | this.eventRepeatTimeout 12 | this.newLeft 13 | this.newTop 14 | this.distance 15 | this.angle 16 | this.regionData = {} 17 | this.handleData = {} 18 | this.movementPad = document.getElementById('movement-pad') 19 | this.region = document.getElementById('movement-pad-region') 20 | this.handle = document.getElementById('movement-pad-handle') 21 | 22 | this.aligningPad() 23 | this.addEventListener() 24 | } 25 | 26 | aligningPad() { 27 | this.regionData.width = this.movementPad.offsetWidth 28 | this.regionData.height = this.movementPad.offsetHeight 29 | this.regionData.position = { left: 0, bottom: 0} 30 | 31 | this.regionData.offset = { left: this.movementPad.offsetLeft, top: this.movementPad.offsetTop } 32 | this.regionData.radius = this.regionData.width / 2 33 | this.regionData.centerX = this.regionData.position.left + this.regionData.radius 34 | this.regionData.centerY = this.regionData.position.bottom + this.regionData.radius 35 | 36 | 37 | this.handleData.width = this.handle.clientWidth 38 | this.handleData.height = this.handle.clientHeight 39 | this.handleData.radius = this.handleData.width / 2 40 | this.regionData.radius = this.regionData.width / 2 - this.handleData.radius 41 | } 42 | 43 | addEventListener() { 44 | this.region.addEventListener('touchstart', event => { 45 | this.mouseDown = true 46 | this.handle.style.opacity = 1 47 | this.update(event.targetTouches[0].pageX, event.targetTouches[0].pageY) 48 | }, { passive: true}) 49 | 50 | this.region.addEventListener('touchend', () => { 51 | this.handle.style.opacity = 0 52 | }) 53 | 54 | this.region.addEventListener('touchcancel', () => { 55 | this.handle.style.opacity = 0 56 | }) 57 | 58 | this.region.addEventListener('touchmove', event => { 59 | if (!this.mouseDown) return 60 | 61 | // this does not exist in Safari, well then FUCK YOU SAFARI 62 | if(event.changedTouches) { 63 | if(event.changedTouches[0]?.target?.id == 'movement-pad-region') { 64 | this.update(event.changedTouches[0].pageX, event.changedTouches[0].pageY) 65 | } else { 66 | this.update(event.changedTouches[1].pageX, event.changedTouches[1].pageY) 67 | } 68 | } else { 69 | this.update(event.touches[0].pageX, event.touches[0].pageY) 70 | } 71 | }) 72 | } 73 | 74 | update (pageX, pageY) { 75 | if(window.sequencer.active) return 76 | 77 | this.newLeft = (pageX - this.regionData.offset.left) 78 | this.newTop = (pageY - this.regionData.offset.top) 79 | this.distance = Math.pow(this.regionData.centerX - this.newLeft, 2) + Math.pow(this.regionData.centerY - this.newTop, 2) 80 | 81 | if (this.distance > Math.pow(this.regionData.radius, 2)) { 82 | this.angle = Math.atan2((this.newTop - this.regionData.centerY), (this.newLeft - this.regionData.centerX)) 83 | this.newLeft = (Math.cos(this.angle) * this.regionData.radius) + this.regionData.centerX 84 | this.newTop = (Math.sin(this.angle) * this.regionData.radius) + this.regionData.centerY 85 | } 86 | this.newTop = Math.round(this.newTop * 10) / 10 87 | this.newLeft = Math.round(this.newLeft * 10) / 10 88 | 89 | this.handle.style.top = `${this.newTop - this.handleData.radius}px` 90 | this.handle.style.left = `${this.newLeft - this.handleData.radius}px` 91 | let deltaX = this.regionData.centerX - parseInt(this.newLeft) 92 | let deltaY = this.regionData.centerY - parseInt(this.newTop) 93 | 94 | deltaX = -2 + (2 + 2) * (deltaX - (-this.regionData.radius)) / (this.regionData.radius - (-this.regionData.radius)) 95 | deltaY = -2 + (2 + 2) * (deltaY - (-this.regionData.radius)) / (this.regionData.radius - (-this.regionData.radius)) 96 | deltaX = Math.round(deltaX * 10) / 10 97 | deltaY = Math.round(deltaY * 10) / 10 98 | 99 | this.sendEvent(deltaX, deltaY, 0) 100 | } 101 | 102 | sendEvent (dx, dy, middle) { 103 | if (!this.mouseDown) { 104 | clearTimeout(this.eventRepeatTimeout) 105 | this.movementPad.dispatchEvent(new CustomEvent('stopMove', { bubbles: false })) 106 | 107 | return 108 | } 109 | 110 | clearTimeout(this.eventRepeatTimeout) 111 | this.eventRepeatTimeout = setTimeout(() => { 112 | this.sendEvent(dx, dy, middle) 113 | }, 5) 114 | 115 | this.movementPad.dispatchEvent( 116 | new CustomEvent('move', { 117 | detail: { 118 | 'deltaX': dx, 119 | 'deltaY': dy, 120 | 'middle': middle 121 | }, 122 | bubbles: false 123 | }) 124 | ) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/controls/mobile/rotation-pad.js: -------------------------------------------------------------------------------- 1 | /* 2 | just force myself to handle fucking mobile devices. 3 | this is a rewrite of http://mese79.github.io/TouchControls/ 4 | it's working but its shit, make it better before using it 5 | i hate mobile devices with a fierce passion of a millions suns 6 | */ 7 | 8 | export default class RotationPad { 9 | constructor() { 10 | this.mouseDown = false 11 | this.eventRepeatTimeout 12 | this.newLeft 13 | this.newTop 14 | this.distance 15 | this.angle 16 | this.regionData = {} 17 | this.handleData = {} 18 | this.rotationPad = document.getElementById('rotation-pad') 19 | this.region = document.getElementById('rotation-pad-region') 20 | this.handle = document.getElementById('rotation-pad-handle') 21 | 22 | this.aligningPad() 23 | this.addEventListener() 24 | } 25 | 26 | aligningPad() { 27 | this.regionData.width = this.rotationPad.offsetWidth 28 | this.regionData.height = this.rotationPad.offsetHeight 29 | this.regionData.position = { left: 0, bottom: 0} 30 | 31 | this.regionData.offset = { left: this.rotationPad.offsetLeft, top: this.rotationPad.offsetTop } 32 | this.regionData.radius = this.regionData.width / 2 33 | this.regionData.centerX = this.regionData.position.left + this.regionData.radius 34 | this.regionData.centerY = this.regionData.position.bottom + this.regionData.radius 35 | 36 | this.handleData.width = this.handle.clientWidth 37 | this.handleData.height = this.handle.clientHeight 38 | this.handleData.radius = this.handleData.width / 2 39 | this.regionData.radius = this.regionData.width / 2 - this.handleData.radius 40 | } 41 | 42 | addEventListener() { 43 | this.region.addEventListener('touchstart', event => { 44 | this.mouseDown = true 45 | this.handle.style.opacity = 1 46 | this.update(event.targetTouches[0].pageX, event.targetTouches[0].pageY) 47 | }, { passive: true }) 48 | 49 | this.region.addEventListener('touchend', () => { 50 | this.handle.style.opacity = 0 51 | this.mouseDown = false 52 | }) 53 | 54 | this.region.addEventListener('touchcancel', () => { 55 | this.handle.style.opacity = 0 56 | this.mouseDown = false 57 | }) 58 | 59 | this.region.addEventListener('touchmove', event => { 60 | if (!this.mouseDown) return 61 | 62 | // this does not exist in Safari, well then FUCK YOU SAFARI 63 | if(event.changedTouches) { 64 | if(event.changedTouches[0]?.target?.id == 'rotation-pad-region') { 65 | this.update(event.changedTouches[0].pageX, event.changedTouches[0].pageY) 66 | } else { 67 | this.update(event.changedTouches[1].pageX, event.changedTouches[1].pageY) 68 | } 69 | } else { 70 | this.update(event.touches[0].pageX, event.touches[0].pageY) 71 | } 72 | }) 73 | } 74 | 75 | update (pageX, pageY) { 76 | if(window.sequencer.active) return 77 | 78 | this.newLeft = (pageX - this.regionData.offset.left) 79 | this.newTop = (pageY - this.regionData.offset.top) 80 | this.distance = Math.pow(this.regionData.centerX - this.newLeft, 2) + Math.pow(this.regionData.centerY - this.newTop, 2) 81 | 82 | if (this.distance > Math.pow(this.regionData.radius, 2)) { 83 | this.angle = Math.atan2((this.newTop - this.regionData.centerY), (this.newLeft - this.regionData.centerX)) 84 | this.newLeft = (Math.cos(this.angle) * this.regionData.radius) + this.regionData.centerX 85 | this.newTop = (Math.sin(this.angle) * this.regionData.radius) + this.regionData.centerY 86 | } 87 | this.newTop = Math.round(this.newTop * 10) / 10 88 | this.newLeft = Math.round(this.newLeft * 10) / 10 89 | 90 | this.handle.style.top = `${this.newTop - this.handleData.radius}px` 91 | this.handle.style.left = `${this.newLeft - this.handleData.radius}px` 92 | let deltaX = this.regionData.centerX - parseInt(this.newLeft) 93 | let deltaY = this.regionData.centerY - parseInt(this.newTop) 94 | 95 | deltaX = -2 + (2 + 2) * (deltaX - (-this.regionData.radius)) / (this.regionData.radius - (-this.regionData.radius)) 96 | deltaY = -2 + (2 + 2) * (deltaY - (-this.regionData.radius)) / (this.regionData.radius - (-this.regionData.radius)) 97 | deltaX = -1 * Math.round(deltaX * 10) / 10 98 | deltaY = -1 * Math.round(deltaY * 10) / 10 99 | 100 | // intentionaly ignore Y value to block player the possibility to go up or down 101 | // i do that for performance optimisation, memory could explode on some devices 102 | // if you wish the full control back to the player you should use this line of code 103 | // this.sendEvent(deltaX, deltaY) 104 | this.sendEvent(deltaX, 0) 105 | } 106 | 107 | sendEvent (dx, dy) { 108 | if (!this.mouseDown) { 109 | clearTimeout(this.eventRepeatTimeout) 110 | return 111 | } 112 | 113 | clearTimeout(this.eventRepeatTimeout) 114 | this.eventRepeatTimeout = setTimeout(() => { 115 | this.sendEvent(dx, dy) 116 | }, 5) 117 | 118 | this.rotationPad.dispatchEvent( 119 | new CustomEvent('YawPitch', { 120 | detail: { 121 | 'deltaX': dx, 122 | 'deltaY': dy 123 | }, 124 | bubbles: false 125 | }) 126 | ) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/controls/mobile/touch-controls.js: -------------------------------------------------------------------------------- 1 | /* 2 | just force myself to handle fucking mobile devices. 3 | this is a rewrite of http://mese79.github.io/TouchControls/ 4 | it's working but its shit, make it better before using it 5 | i hate mobile devices with a fierce passion of a millions suns 6 | */ 7 | 8 | import * as THREE from 'three' 9 | import RotationPad from './rotation-pad' 10 | import MovementPad from './movement-pad' 11 | 12 | export default class TouchControls { 13 | constructor(camera) { 14 | this.camera = camera 15 | this.config = { 16 | speedFactor: 15, 17 | delta: 1, 18 | rotationFactor: 0.002, 19 | maxPitch: 25 20 | } 21 | 22 | this.moveForward = false 23 | this.moveBackward = false 24 | this.moveLeft = false 25 | this.moveRight = false 26 | this.lockMoveForward = false 27 | this.lockMoveBackward = false 28 | this.lockMoveLeft = false 29 | this.lockMoveRight = false 30 | this.ztouch = 1 31 | this.xtouch = 1 32 | 33 | this.maxPitch = this.config.maxPitch * Math.PI / 180 34 | this.velocity = new THREE.Vector3(0, 0, 0) 35 | this.cameraHolder = new THREE.Object3D() 36 | this.cameraHolder.name = 'cameraHolder' 37 | this.cameraHolder.add(this.camera.clone()) 38 | this.fpsBody = new THREE.Object3D() 39 | this.fpsBody.add(this.cameraHolder) 40 | 41 | 42 | this.rotationPad = new RotationPad() 43 | this.movementPad = new MovementPad() 44 | 45 | this.createRotationPad() 46 | this.createMovementPad() 47 | } 48 | 49 | createRotationPad () { 50 | document.getElementById('rotation-pad').addEventListener('YawPitch', event => { 51 | const rotation = this.calculateCameraRotation(event.detail.deltaX, event.detail.deltaY) 52 | this.setRotation(rotation.rx, rotation.ry) 53 | }) 54 | } 55 | 56 | createMovementPad () { 57 | document.getElementById('movement-pad').addEventListener('move', event => { 58 | this.ztouch = Math.abs(event.detail.deltaY) 59 | this.xtouch = Math.abs(event.detail.deltaX) 60 | 61 | if (event.detail.deltaY == event.detail.middle) { 62 | this.ztouch = 1 63 | this.moveForward = this.moveBackward = false 64 | } else { 65 | if (event.detail.deltaY > event.detail.middle) { 66 | this.moveForward = true 67 | this.moveBackward = false 68 | } 69 | else if (event.detail.deltaY < event.detail.middle) { 70 | this.moveForward = false 71 | this.moveBackward = true 72 | } 73 | } 74 | 75 | if (event.detail.deltaX == event.detail.middle) { 76 | this.xtouch = 1 77 | this.moveRight = this.moveLeft = false 78 | } else { 79 | if (event.detail.deltaX < event.detail.middle) { 80 | this.moveRight = true 81 | this.moveLeft = false 82 | } 83 | else if (event.detail.deltaX > event.detail.middle) { 84 | this.moveRight = false 85 | this.moveLeft = true 86 | } 87 | } 88 | 89 | this.update() 90 | }) 91 | 92 | document.getElementById('movement-pad').addEventListener('stopMove', event => { 93 | this.ztouch = this.xtouch = 1 94 | this.moveForward = this.moveBackward = this.moveLeft = this.moveRight = false 95 | }) 96 | } 97 | 98 | calculateCameraRotation (dx, dy, factor) { 99 | let factorFinal = factor ? factor : this.config.rotationFactor 100 | let ry = this.fpsBody.rotation.y - (dx * factorFinal) 101 | let rx = this.cameraHolder.rotation.x - (dy * factorFinal) 102 | rx = Math.max(-this.maxPitch, Math.min(this.maxPitch, rx)) 103 | 104 | return { 105 | rx: rx, 106 | ry: ry 107 | } 108 | } 109 | 110 | update () { 111 | if(window.sequencer.active) return 112 | 113 | this.velocity.x += (-1 * this.velocity.x) * 0.75 * this.config.delta 114 | this.velocity.z += (-1 * this.velocity.z) * 0.75 * this.config.delta 115 | 116 | if (this.moveForward && !this.lockMoveForward) this.velocity.z -= this.ztouch * this.config.speedFactor * this.config.delta 117 | if (this.moveBackward && !this.lockMoveBackward) this.velocity.z += this.ztouch * this.config.speedFactor * this.config.delta 118 | if (this.moveLeft && !this.lockMoveLeft) this.velocity.x -= this.xtouch * this.config.speedFactor * this.config.delta 119 | if (this.moveRight && !this.lockMoveRight) this.velocity.x += this.xtouch * this.config.speedFactor * this.config.delta 120 | 121 | this.camera.translateX(this.velocity.x) 122 | this.camera.translateZ(this.velocity.z) 123 | } 124 | 125 | setRotation (x, y) { 126 | if(window.sequencer.active) return 127 | 128 | if (x !== null) 129 | this.camera.rotateX(x) 130 | 131 | if (y !== null) 132 | this.camera.rotateY(y) 133 | } 134 | } -------------------------------------------------------------------------------- /src/multiplayer/Multiplayer.js: -------------------------------------------------------------------------------- 1 | import geckos from '@geckos.io/client' 2 | import Player from './Player' 3 | 4 | export default class Multiplayer { 5 | constructor (camera, scene, library, propertySign, enable = false) { 6 | this.camera = camera 7 | this.scene = scene 8 | this.library = library 9 | 10 | // tochange 11 | this.server = { 12 | url: "https://195-154-113-94.rev.poneytelecom.eu", 13 | //url: "http://192.168.2.138", 14 | port: 3000 15 | } 16 | 17 | this.propertySign = propertySign 18 | this.playerBuilder = new Player(camera, scene, library) 19 | this.players = [] 20 | this.channel = {} 21 | this.isConnected = false 22 | this.isEnable = enable 23 | 24 | this.showPlayer = true 25 | this.showOtherPlayers = true 26 | } 27 | 28 | async connect () { 29 | this.channel = geckos({ url: this.server.url, port: this.server.port }) 30 | 31 | this.channel.onConnect(error => { 32 | if (error) { 33 | console.error(error.message) 34 | return 35 | } 36 | 37 | this.isConnected = true 38 | 39 | this.channel.emit('onPlayerConnect', this._getPlayerData()) 40 | 41 | this.channel.on('onPlayerConnect', data => this._onPlayerConnect(data)) 42 | this.channel.on('onPlayerUpdate', data => this._onPlayerUpdate(data)) 43 | this.channel.on('onPlayerDisconnect', id => this._onPlayerDisconnect(id)) 44 | 45 | // TODO: fix bug surimpression 46 | this.propertySign.addPropertySign() 47 | }) 48 | } 49 | 50 | isReady () { 51 | return this.isConnected && this.isEnable 52 | } 53 | 54 | update () { 55 | if (!this.isReady()) 56 | return 57 | 58 | this.channel.emit('onPlayerUpdate', this._getPlayerData()) 59 | } 60 | 61 | getPlayerById (id) { 62 | return this.players.find(player => player.id === id) 63 | } 64 | 65 | hideMultiplayer() { 66 | this.showPlayer = false 67 | this.showOtherPlayers = false 68 | } 69 | 70 | showMultiplayer() { 71 | this.showPlayer = true 72 | this.showOtherPlayers = true 73 | } 74 | 75 | _getPlayerData () { 76 | return { 77 | id: this.channel.id, 78 | xPosition: this.camera.position.x, 79 | yPosition: this.camera.position.y, 80 | zPosition: this.camera.position.z, 81 | xRotation: this.camera.rotation.x, 82 | yRotation: this.camera.rotation.y, 83 | zRotation: this.camera.rotation.z, 84 | showPlayer: this.showPlayer, 85 | universePlayer: window.currentUniverse.universeNumber 86 | } 87 | } 88 | 89 | async _onPlayerConnect (data) { 90 | if(!data || this.channel.id === data.id) 91 | return 92 | 93 | await this._addPlayer(data) 94 | } 95 | 96 | async _addPlayer(data) { 97 | const playerModel = await this.playerBuilder.getNewPlayerModel() 98 | const playerName = await this.playerBuilder.getNewPlayerName(data.id) 99 | 100 | this.scene.add(playerName.playerNameMesh) 101 | this.scene.add(playerModel.playerModelMesh) 102 | 103 | this.players.push({ 104 | id: data.id, 105 | playerModel: playerModel, 106 | playerName: playerName 107 | }) 108 | } 109 | 110 | _onPlayerUpdate(data) { 111 | if(!data || this.channel.id === data.id) 112 | return 113 | 114 | if(!this.showOtherPlayers) { 115 | this._hideAllPlayers() 116 | return 117 | } 118 | 119 | if(!data.showPlayer || window.currentUniverse.universeNumber ==! data.universePlayer) { 120 | playerToUpdate.playerModel.playerModelMesh.visible = false 121 | playerToUpdate.playerName.playerNameMesh.visible = false 122 | return 123 | } 124 | 125 | let playerToUpdate = this.getPlayerById(data.id) 126 | 127 | if (!playerToUpdate) { 128 | this._addPlayer(data) 129 | playerToUpdate = this.getPlayerById(data.id) 130 | } 131 | 132 | if (!playerToUpdate) 133 | return 134 | 135 | playerToUpdate.playerModel.playerModelMesh.position.set(data.xPosition, data.yPosition, data.zPosition) 136 | playerToUpdate.playerModel.playerModelMesh.rotation.set(data.xRotation, data.yRotation, data.zRotation) 137 | 138 | playerToUpdate.playerName.playerNameMesh.lookAt(this.camera.position) 139 | playerToUpdate.playerName.playerNameGeometry.center() 140 | playerToUpdate.playerName.playerNameMesh.position.set( 141 | data.xPosition, 142 | data.yPosition + 650, 143 | data.zPosition 144 | ) 145 | 146 | playerToUpdate.playerModel.playerModelMesh.visible = true 147 | playerToUpdate.playerName.playerNameMesh.visible = true 148 | } 149 | 150 | _onPlayerDisconnect (id) { 151 | const playerDisconnected = this.players.find(player => player.id === id) 152 | 153 | if (!playerDisconnected) 154 | return 155 | 156 | this._disposePlayerModel(playerDisconnected) 157 | 158 | this.players = this.players.filter(player => player.id !== id) 159 | } 160 | 161 | _disposePlayerModel (playerDisconnected) { 162 | if (!playerDisconnected) { 163 | console.log('Can\'t dispose empty player') 164 | return 165 | } 166 | 167 | playerDisconnected.playerModel.playerModelGeometry.dispose() 168 | playerDisconnected.playerModel.playerModelMaterial.dispose() 169 | 170 | playerDisconnected.playerName.playerNameGeometry.dispose() 171 | playerDisconnected.playerName.playerNameMaterial.dispose() 172 | 173 | this.scene.remove(playerDisconnected.playerModel.playerModelMesh) 174 | this.scene.remove(playerDisconnected.playerName.playerNameMesh) 175 | } 176 | 177 | _hideAllPlayers () { 178 | this.players.forEach(player => { 179 | player.playerModel.playerModelMesh.visible = false 180 | player.playerName.playerNameMesh.visible = false 181 | }) 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/multiplayer/Player.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { FontLoader } from 'three/src/loaders/FontLoader' 3 | 4 | export default class Player { 5 | constructor (camera, scene, library) { 6 | this.camera = camera 7 | this.scene = scene 8 | this.library = library 9 | 10 | this.fontLoader = new FontLoader() 11 | this.fontLoader.load( 12 | 'fonts/helvetiker_regular.typeface.json', 13 | font => { this.font = font} 14 | ) 15 | } 16 | 17 | async getNewPlayerModel() { 18 | // TODO: delete uselss sphere and material 19 | const playerModelGeometry = new THREE.SphereGeometry(500, 500, 500) 20 | const playerModelMaterial = new THREE.MeshBasicMaterial({color: 0x7777ff, wireframe: false}) 21 | const playerModelMesh = this.library.player.model.clone() 22 | 23 | return { playerModelGeometry, playerModelMaterial, playerModelMesh } 24 | } 25 | 26 | async getNewPlayerName(name=null) { 27 | const playerNameFont = this.font 28 | const playerNameText = name ? name : (Math.random() + 1).toString(36).substring(2) 29 | 30 | const playerNameGeometry = new THREE.TextGeometry(playerNameText, { 31 | font: playerNameFont, 32 | size: 100, 33 | height: 10, 34 | curveSegments: 10, 35 | bevelEnabled: false 36 | }) 37 | 38 | const playerNameMaterial = new THREE.MeshBasicMaterial({color: 0xffffff}) 39 | const playerNameMesh = new THREE.Mesh(playerNameGeometry, playerNameMaterial) 40 | 41 | return { playerNameGeometry, playerNameMaterial, playerNameMesh } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/postprocessing/PostProcessor.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import * as dat from 'dat.gui' 3 | import * as POSTPROCESSING from 'postprocessing' 4 | 5 | export default class PostProcessor { 6 | constructor (camera, scene, parameters, renderer, isDebug = false) { 7 | this.camera = camera 8 | this.scene = scene 9 | this.parameters = parameters 10 | this.renderer = renderer 11 | this.isDebug = isDebug 12 | 13 | this.composer = new POSTPROCESSING.EffectComposer(this.renderer) 14 | this.composer.addPass(new POSTPROCESSING.RenderPass(this.scene, this.camera)) 15 | this.composer.addPass(this.getEffectPass()) 16 | 17 | if (this.isDebug) { 18 | this.gui = new dat.GUI() 19 | } 20 | } 21 | 22 | updateProcessingRenderer() { 23 | this.composer.reset() 24 | this.renderer.setClearColor(new THREE.Color(window.currentUniverse.matters.global.clearColor)) 25 | this.scene.fog = new THREE.Fog( 26 | window.currentUniverse.matters.global.fogColor, 27 | this.parameters.global.camera.near, 28 | this.parameters.global.camera.far 29 | ) 30 | this.composer = new POSTPROCESSING.EffectComposer(this.renderer) 31 | this.composer.addPass(new POSTPROCESSING.RenderPass(this.scene, this.camera)) 32 | this.composer.addPass(this.getEffectPass()) 33 | } 34 | 35 | getEffectPass () { 36 | this.parameters.postprocessing.bloomEffect.intensity = window.currentUniverse.matters.global.bloomIntensity 37 | const bloomEffect = new POSTPROCESSING.BloomEffect(this.parameters.postprocessing.bloomEffect) 38 | bloomEffect.blendMode.opacity.value = this.parameters.postprocessing.bloomEffect.opacity 39 | 40 | const depthOfFieldEffect = new POSTPROCESSING.DepthOfFieldEffect(this.camera, this.parameters.postprocessing.depthOfFieldEffect) 41 | 42 | if (this.isDebug) { 43 | this._enableDebug(bloomEffect, depthOfFieldEffect) 44 | } 45 | 46 | const effectPass = new POSTPROCESSING.EffectPass(this.camera, bloomEffect, depthOfFieldEffect) 47 | effectPass.renderToScreen = true 48 | 49 | return effectPass 50 | } 51 | 52 | _enableDebug (bloomEffect, depthOfFieldEffect) { 53 | const folderPostprocessingBloom = this.gui.addFolder('Postprocessing - Bloom') 54 | folderPostprocessingBloom.add(this.parameters.postprocessing.bloomEffect, 'opacity').onChange(value => { bloomEffect.blendMode.opacity.value = value }) 55 | 56 | const folderPostprocessingDepth = this.gui.addFolder('Postprocessing - Depth') 57 | folderPostprocessingDepth.add(this.parameters.postprocessing.depthOfFieldEffect, 'focusDistance').onChange(value => { depthOfFieldEffect.circleOfConfusionMaterial.uniforms.focusDistance.value = value }) 58 | folderPostprocessingDepth.add(this.parameters.postprocessing.depthOfFieldEffect, 'focalLength').onChange(value => { depthOfFieldEffect.circleOfConfusionMaterial.uniforms.focalLength.value = value }) 59 | folderPostprocessingDepth.add(this.parameters.postprocessing.depthOfFieldEffect, 'bokehScale').onChange(value => { depthOfFieldEffect.bokehScale = value }) 60 | folderPostprocessingDepth.add(this.parameters.postprocessing.depthOfFieldEffect, 'height').onChange(value => { depthOfFieldEffect.resolution.height = value }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/procedural/MultiverseFactory.js: -------------------------------------------------------------------------------- 1 | import Starfield from './starfield/Starfield' 2 | import Nebula from './nebula/Nebula' 3 | import Galaxy from './galaxy/Galaxy' 4 | import Giant from './giant/Giant' 5 | import Singularity from './singularity/Singularity' 6 | import Spaceship from './spaceship/Spaceship' 7 | import StrangerThings from './strangerthings/StrangerThings' 8 | 9 | export default class MultiverseFactory { 10 | constructor(scene, library, parameters) { 11 | this.scene = scene 12 | this.library = library 13 | this.parameters = parameters 14 | } 15 | 16 | createMatter = type => { 17 | switch(type) { 18 | case "starfield": 19 | return new Starfield(this.scene, this.library, this.parameters) 20 | 21 | case "nebula": 22 | return new Nebula(this.scene, this.library, this.parameters) 23 | 24 | case "galaxy": 25 | return new Galaxy(this.scene, this.library, this.parameters) 26 | 27 | case "giant": 28 | return new Giant(this.scene, this.library, this.parameters) 29 | 30 | case "singularity": 31 | return new Singularity(this.scene, this.library, this.parameters) 32 | 33 | case "spaceship": 34 | return new Spaceship(this.scene, this.library, this.parameters) 35 | 36 | case "strangerthings": 37 | return new StrangerThings(this.scene, this.library, this.parameters) 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/procedural/galaxy/Galaxy.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { gsap } from 'gsap' 3 | 4 | export default class Galaxy { 5 | constructor (scene, library, parameters) { 6 | this.scene = scene 7 | this.library = library 8 | this.parameters = parameters 9 | this.currentCoordinateVector = null 10 | this.textureSeen = [] 11 | this.galaxy = null 12 | this.subtype = null 13 | } 14 | 15 | generate (galaxiesAttributes, position, subtype = null) { 16 | this.currentCoordinateVector = this._getCoordinateVectorByPosition(position) 17 | this.subtype = subtype 18 | 19 | const rotation = THREE.Math.degToRad(THREE.MathUtils.randInt(0, 360)) 20 | 21 | const firstPassStarsGeometry = this._getRandomStarsGeometry(galaxiesAttributes.firstPassStarsRandomAttributes) 22 | const firstPassStarsTexture = this._getRandomStarsTexture() 23 | const firstPassStarsmaterial = this._getRandomStarsMaterial(firstPassStarsTexture, currentUniverse.universeModifiers.type.id === 'filaments' ? 300 : false) 24 | const firstPassStars = new THREE.Points(firstPassStarsGeometry, firstPassStarsmaterial) 25 | 26 | firstPassStars.position.set(this.currentCoordinateVector.x, this.currentCoordinateVector.y, this.currentCoordinateVector.z) 27 | firstPassStarsGeometry.rotateX(rotation) 28 | 29 | const secondPassStarsGeometry = this._getRandomStarsGeometry(galaxiesAttributes.secondPassStarsRandomAttributes) 30 | const secondPassStarsTexture = this._getRandomStarsTextureByType('cloud') 31 | const secondPassStarsmaterial = this._getRandomStarsMaterial(secondPassStarsTexture, 800, 0.04) 32 | const secondPassStars = new THREE.Points(secondPassStarsGeometry, secondPassStarsmaterial) 33 | 34 | secondPassStars.position.set(this.currentCoordinateVector.x, this.currentCoordinateVector.y, this.currentCoordinateVector.z) 35 | secondPassStars.rotateX(rotation) 36 | 37 | const thirdPassStarsGeometry = this._getRandomStarsGeometry(galaxiesAttributes.thirdPassStarsRandomAttributes) 38 | const thirdPassStarsTexture = this._getRandomStarsTexture() 39 | const thirdPassStarsmaterial = this._getRandomStarsMaterial(thirdPassStarsTexture) 40 | const thirdPassStars = new THREE.Points(thirdPassStarsGeometry, thirdPassStarsmaterial) 41 | 42 | if (this.subtype === "sombrero") { 43 | thirdPassStars.rotateX(rotation - 80) 44 | } else { 45 | thirdPassStars.rotateX(rotation) 46 | } 47 | 48 | thirdPassStars.position.set(this.currentCoordinateVector.x, this.currentCoordinateVector.y, this.currentCoordinateVector.z) 49 | 50 | const randomGalaxy = { 51 | firstPass: { 52 | geometry: firstPassStarsGeometry, 53 | texture: firstPassStarsTexture, 54 | material: firstPassStarsmaterial, 55 | points: firstPassStars 56 | }, 57 | secondPass: { 58 | geometry: secondPassStarsGeometry, 59 | texture: secondPassStarsTexture, 60 | material: secondPassStarsmaterial, 61 | points: secondPassStars 62 | }, 63 | thirdPass: { 64 | geometry: thirdPassStarsGeometry, 65 | texture: thirdPassStarsTexture, 66 | material: thirdPassStarsmaterial, 67 | points: thirdPassStars 68 | } 69 | } 70 | 71 | this.galaxy = randomGalaxy 72 | } 73 | 74 | dispose () { 75 | if (!this.galaxy) { 76 | console.log('Can\'t dispose empty galaxy') 77 | return 78 | } 79 | 80 | this.galaxy.firstPass.geometry.dispose() 81 | this.galaxy.secondPass.geometry.dispose() 82 | this.galaxy.thirdPass.geometry.dispose() 83 | 84 | this.galaxy.firstPass.material.dispose() 85 | this.galaxy.secondPass.material.dispose() 86 | this.galaxy.thirdPass.material.dispose() 87 | 88 | this.scene.remove( 89 | this.galaxy.firstPass.points, 90 | this.galaxy.secondPass.points, 91 | this.galaxy.thirdPass.points 92 | ) 93 | 94 | this.galaxy = null 95 | } 96 | 97 | show () { 98 | if (!this.galaxy) { 99 | console.log('Can\'t show empty galaxy') 100 | return 101 | } 102 | 103 | this.scene.add( 104 | this.galaxy.firstPass.points, 105 | this.galaxy.secondPass.points, 106 | this.galaxy.thirdPass.points 107 | ) 108 | 109 | let minCloudOpacity = window.currentUniverse.matters.galaxy.material.opacity.pass.min 110 | let maxCloudOpacity = window.currentUniverse.matters.galaxy.material.opacity.pass.max 111 | 112 | if(this.subtype === 'sombrero') { 113 | minCloudOpacity = window.currentUniverse.matters.galaxy.material.opacity.sombrero.min 114 | maxCloudOpacity = window.currentUniverse.matters.galaxy.material.opacity.sombrero.max 115 | } 116 | 117 | gsap.timeline() 118 | .to(this.galaxy.firstPass.points.material, { duration: 3, opacity: 1 }, 0) 119 | .to(this.galaxy.secondPass.points.material, { 120 | duration: 3, 121 | opacity: THREE.MathUtils.randFloat(minCloudOpacity, maxCloudOpacity) 122 | }, 0) 123 | .to(this.galaxy.thirdPass.points.material, { duration: 3, opacity: 1 }, 0) 124 | } 125 | 126 | _getCoordinateVectorByPosition (position) { 127 | const coordinateVector = new THREE.Vector3(0, 0, 0) 128 | 129 | // we dont need to tweak coordinates on the origin cluster 130 | if (position !== '0,0,0') { 131 | const arrayCurrentCluster = position.split(',') 132 | 133 | // handling x axis (right and left) clusters population 134 | const xCurrentCluster = parseInt(arrayCurrentCluster[0]) 135 | 136 | if (xCurrentCluster !== 0) { 137 | coordinateVector.x = (this.parameters.grid.clusterSize) * xCurrentCluster 138 | } 139 | 140 | // since we're not handling vertical movement at the moment 141 | // we dont need to handle the y axis 142 | 143 | // handling z axis (forward and backward) clusters population 144 | const zCurrentCluster = parseInt(arrayCurrentCluster[2]) 145 | 146 | if (zCurrentCluster !== 0) { 147 | coordinateVector.z = (this.parameters.grid.clusterSize) * zCurrentCluster 148 | } 149 | } 150 | 151 | return coordinateVector 152 | } 153 | 154 | /** 155 | * @param {*} max 156 | * @returns 157 | */ 158 | _getRandomStarsGeometry (randomAttributes) { 159 | const geometry = new THREE.BufferGeometry() 160 | 161 | geometry.setAttribute( 162 | 'position', 163 | new THREE.Float32BufferAttribute(randomAttributes.positions, 3) 164 | ) 165 | 166 | geometry.setAttribute( 167 | 'color', 168 | new THREE.BufferAttribute(randomAttributes.colors, 3) 169 | ) 170 | 171 | return geometry 172 | } 173 | 174 | _getRandomStarsTexture () { 175 | const starsChoosenIndexes = [0, 1, 3, 4] 176 | const currentTexturesPool = this.library.textures.starfield["pass"].filter((texture, index) => starsChoosenIndexes.includes(index)) 177 | const randomTexture = currentTexturesPool[THREE.MathUtils.randInt(0, currentTexturesPool.length - 1)] 178 | 179 | this.textureSeen.push(randomTexture) 180 | 181 | return randomTexture 182 | } 183 | 184 | _getRandomStarsTextureByType (type = 'pass') { 185 | const currentTexturesPool = this.library.textures.nebula[type].filter(texture => !this.textureSeen.includes(texture)) 186 | const randomTexture = currentTexturesPool[THREE.MathUtils.randInt(0, currentTexturesPool.length - 1)] 187 | 188 | this.textureSeen.push(randomTexture) 189 | 190 | return randomTexture 191 | } 192 | 193 | /** 194 | * @param {*} texture 195 | * @param {*} opacity 196 | * @param {*} size 197 | * @returns 198 | */ 199 | _getRandomStarsMaterial (randomMaterialTexture, enforcedSize, enforcedOpacity) { 200 | const randomMaterialSize = enforcedSize || enforcedSize === 0 ? enforcedSize : THREE.MathUtils.randInt(window.currentUniverse.matters.galaxy.material.size.pass.min, window.currentUniverse.matters.galaxy.material.size.pass.max) 201 | const randomMaterialOpacity = enforcedOpacity || enforcedOpacity === 0 ? enforcedOpacity : THREE.MathUtils.randInt(window.currentUniverse.matters.galaxy.material.opacity.pass.min, window.currentUniverse.matters.galaxy.material.opacity.pass.max) 202 | 203 | randomMaterialTexture.magFilter = THREE.NearestFilter 204 | 205 | const material = new THREE.PointsMaterial({ 206 | size: randomMaterialSize, 207 | opacity: randomMaterialOpacity, 208 | map: randomMaterialTexture, 209 | sizeAttenuation: true, 210 | depthWrite: false, 211 | transparent: window.currentUniverse.matters.galaxy.material.transparent, 212 | blending: window.currentUniverse.matters.galaxy.material.blending, 213 | vertexColors: true, 214 | opacity: 0 215 | }) 216 | 217 | return material 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/procedural/galaxy/IrregularGalaxyWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | self.onmessage = messageEvent => { 4 | const clustersToPopulate = messageEvent.data.clustersToPopulate 5 | const galaxyParameters = messageEvent.data.currentUniverse.matters.galaxy 6 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 7 | const galaxyAttributes = {} 8 | const defaultBranchesNumber = THREE.MathUtils.randInt(galaxyParameters.irregular.branches.min, galaxyParameters.irregular.branches.max) 9 | const chosenColors = _getTwoDifferentColors(galaxyParameters.galaxyColors) 10 | 11 | for (const clusterToPopulate of clustersToPopulate) { 12 | // base galaxy shaped 13 | const firstPassStarsRandomAttributes = _getGalaxyAttributesInRandomPosition( 14 | Math.floor( 15 | galaxyParameters.budget * THREE.MathUtils.randFloat( 16 | galaxyParameters.vertices.pass.min, 17 | galaxyParameters.vertices.pass.max 18 | ) 19 | ), 20 | clusterSize, 21 | galaxyParameters, 22 | chosenColors, 23 | defaultBranchesNumber 24 | ) 25 | 26 | // gaz galaxy shaped 27 | const secondPassStarsRandomAttributes = _getGalaxyAttributesInRandomPosition( 28 | Math.floor( 29 | galaxyParameters.budget * THREE.MathUtils.randFloat( 30 | galaxyParameters.vertices.cloud.min * 0.6, 31 | galaxyParameters.vertices.cloud.max * 0.6 32 | ) 33 | ), 34 | clusterSize, 35 | galaxyParameters, 36 | chosenColors, 37 | defaultBranchesNumber 38 | ) 39 | 40 | // low starfield density using color from galaxy 41 | const thirdPassStarsRandomAttributes = _getAttributesInRandomPosition( 42 | Math.floor( 43 | galaxyParameters.budget * THREE.MathUtils.randFloat( 44 | galaxyParameters.vertices.pass.min, 45 | galaxyParameters.vertices.pass.max 46 | ) 47 | ), 48 | clusterSize, 49 | galaxyParameters, 50 | chosenColors 51 | ) 52 | 53 | galaxyAttributes[clusterToPopulate] = { 54 | firstPassStarsRandomAttributes, 55 | secondPassStarsRandomAttributes, 56 | thirdPassStarsRandomAttributes 57 | } 58 | } 59 | 60 | self.postMessage(galaxyAttributes) 61 | } 62 | 63 | function _getGalaxyAttributesInRandomPosition (max, clusterSize, parameters, chosenColors, enforcedBranches) { 64 | const positions = [] 65 | const colors = [] 66 | const radius = clusterSize / 1.8 67 | const branches = enforcedBranches || THREE.MathUtils.randInt(parameters.irregular.branches.min, parameters.irregular.branches.max) 68 | const spin = THREE.MathUtils.randInt(parameters.irregular.spin.min, parameters.irregular.spin.max) 69 | const mixedColor = chosenColors.colorIn.clone() 70 | 71 | for (let i = 0; i < max; i++) { 72 | const i3 = i * 3 73 | const randomRadiusPosition = Math.random() * radius + Math.random() 74 | const spinAngle = (randomRadiusPosition * parameters.irregular.spinAmplitude) * spin 75 | const branchAngle = (i % branches) / branches * Math.PI * parameters.irregular.branchesAmplitude 76 | 77 | const x = Math.pow( 78 | Math.random(), 79 | parameters.irregular.randomnessPower 80 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.irregular.randomness * randomRadiusPosition 81 | 82 | const y = Math.pow( 83 | Math.random(), 84 | parameters.irregular.randomnessPower 85 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.irregular.randomness * randomRadiusPosition 86 | 87 | const z = Math.pow( 88 | Math.random(), 89 | parameters.irregular.randomnessPower 90 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.irregular.randomness * randomRadiusPosition 91 | 92 | positions[i3] = Math.cos(branchAngle + spinAngle) * randomRadiusPosition + x 93 | positions[i3 + 1] = y 94 | positions[i3 + 2] = Math.sin(branchAngle + spinAngle) * randomRadiusPosition + z 95 | 96 | mixedColor.lerpColors( 97 | chosenColors.colorIn, 98 | chosenColors.colorOut, 99 | (randomRadiusPosition / radius) + parameters.irregular.colorInterpolationAmplitude 100 | ) 101 | 102 | colors[i3] = mixedColor.r 103 | colors[i3 + 1] = mixedColor.g 104 | colors[i3 + 2] = mixedColor.b 105 | } 106 | 107 | return { 108 | positions: new Float32Array(positions), 109 | colors: new Float32Array(colors) 110 | } 111 | } 112 | 113 | function _getAttributesInRandomPosition (max, clusterSize, parameters, chosenColors) { 114 | const positions = [] 115 | const colors = [] 116 | 117 | for (let i = 0; i < max; i++) { 118 | // creating coordinate for the particles in random positions but confined in the current square cluster 119 | const x = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 120 | const y = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 121 | const z = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 122 | 123 | positions.push(x, y, z) 124 | 125 | const color = chosenColors.colorOut 126 | 127 | colors.push(color.r, color.g, color.b) 128 | } 129 | 130 | return { 131 | positions: new Float32Array(positions), 132 | colors: new Float32Array(colors) 133 | } 134 | } 135 | 136 | function _getTwoDifferentColors (pool) { 137 | const colorIn = new THREE.Color(pool.in[THREE.MathUtils.randInt(0, pool.in.length - 1)]) 138 | const colorOut = new THREE.Color(pool.out[THREE.MathUtils.randInt(0, pool.out.length - 1)]) 139 | 140 | return { colorIn, colorOut } 141 | } 142 | -------------------------------------------------------------------------------- /src/procedural/galaxy/SombreroGalaxyWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | self.onmessage = messageEvent => { 4 | const clustersToPopulate = messageEvent.data.clustersToPopulate 5 | const currentUniverse = messageEvent.data.currentUniverse 6 | const galaxyParameters = currentUniverse.matters.galaxy 7 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 8 | const galaxyAttributes = {} 9 | const chosenColors = _getTwoDifferentColors(galaxyParameters.galaxyColors) 10 | const innerSize = THREE.MathUtils.randInt(14, 15) 11 | const outerSize = THREE.MathUtils.randInt(3, 4) 12 | 13 | for (const clusterToPopulate of clustersToPopulate) { 14 | // base galaxy shaped 15 | const firstPassStarsRandomAttributes = _getGalaxyAttributesInRandomPosition(innerSize, outerSize, 5000, 1500) 16 | 17 | // gaz galaxy shaped 18 | const secondPassStarsRandomAttributes = _getGalaxyAttributesInRandomPosition(innerSize, outerSize, 1000, 3000) 19 | 20 | // center 21 | const thirdPassStarsRandomAttributes = _getSpiralGalaxyAttributesInRandomPosition( 22 | 300000, 23 | clusterSize, 24 | galaxyParameters, 25 | chosenColors, 26 | 200, 27 | currentUniverse 28 | ) 29 | 30 | galaxyAttributes[clusterToPopulate] = { 31 | firstPassStarsRandomAttributes, 32 | secondPassStarsRandomAttributes, 33 | thirdPassStarsRandomAttributes 34 | } 35 | } 36 | 37 | self.postMessage(galaxyAttributes) 38 | } 39 | 40 | function _getGalaxyAttributesInRandomPosition (innerSize, outerSize, minPositionAmplitude = 1000, maxPositionAmplitude = 6000, density = 300) { 41 | let currentValueX 42 | let currentValueY 43 | let currentValueZ 44 | 45 | const positions = [] 46 | const colors = [] 47 | const randomNess = THREE.MathUtils.randInt(5, 7) 48 | const amplitude = THREE.MathUtils.randInt(4, 6) 49 | const scale = 250 50 | const geometry = new THREE.RingGeometry(innerSize, outerSize, density, density) 51 | 52 | geometry.scale(scale, scale, scale) 53 | 54 | for (let i = 0; i < geometry.attributes.position.array.length - 1; i++) { 55 | if(i > 80000) continue; 56 | 57 | const i3 = i * 3 58 | 59 | if (geometry.attributes.position.array[i3]) { 60 | currentValueX = geometry.attributes.position.array[i3] * (Math.random() * randomNess + amplitude ) 61 | 62 | if(currentValueX && !isNaN(currentValueX)) { 63 | positions[i3] = currentValueX + (THREE.MathUtils.randInt(minPositionAmplitude, maxPositionAmplitude) * Math.random()) + randomNess 64 | colors[i3] = 1 65 | } 66 | } 67 | 68 | if (geometry.attributes.position.array[i3 + 1]) { 69 | currentValueY = geometry.attributes.position.array[i3 + 1] * (Math.random() * randomNess + amplitude ) 70 | 71 | if(currentValueY && !isNaN(currentValueY)) { 72 | positions[i3 + 1] = currentValueY + (THREE.MathUtils.randInt(minPositionAmplitude, maxPositionAmplitude) * Math.random()) + randomNess 73 | colors[i3 + 1] = 1 74 | } 75 | } else { 76 | positions[i3 + 1] = (randomNess + minPositionAmplitude) * Math.random() 77 | colors[i3 + 1] = 1 78 | } 79 | 80 | if (geometry.attributes.position.array[i3 + 2]) { 81 | currentValueZ = geometry.attributes.position.array[i3 + 2] * (Math.random() * randomNess + amplitude ) 82 | 83 | if(currentValueZ && !isNaN(currentValueZ)) { 84 | positions[i3 + 2] = currentValueZ + ((THREE.MathUtils.randInt(minPositionAmplitude, maxPositionAmplitude) * Math.random()) + randomNess) * (Math.random() > 0.5 ? 1 : -1) 85 | colors[i3 + 2] = 1 86 | } 87 | } else { 88 | positions[i3 + 2] = (randomNess + THREE.MathUtils.randInt(minPositionAmplitude, maxPositionAmplitude)) * Math.random() * (Math.random() > 0.5 ? 1 : -1) 89 | colors[i3 + 2] = 1 90 | } 91 | } 92 | 93 | return { 94 | positions: new Float32Array(positions), 95 | colors: new Float32Array(colors) 96 | } 97 | } 98 | 99 | function _getSpiralGalaxyAttributesInRandomPosition (max, clusterSize, parameters, chosenColors, enforcedBranches, currentUniverse) { 100 | const positions = [] 101 | const colors = [] 102 | const radius = currentUniverse.universeModifiers.type.id === 'filaments' ? clusterSize * 4 : clusterSize / 1.8 103 | const branches = enforcedBranches || THREE.MathUtils.randInt(parameters.spiral.branches.min, parameters.spiral.branches.max) 104 | const spin = THREE.MathUtils.randInt(parameters.spiral.spin.min, parameters.spiral.spin.max) 105 | const mixedColor = chosenColors.colorIn.clone() 106 | 107 | for (let i = 0; i < max; i++) { 108 | const i3 = i * 3 109 | const randomRadiusPosition = Math.random() * radius + Math.random() 110 | const spinAngle = (randomRadiusPosition * 100000) * spin 111 | const branchAngle = (i % branches) / branches * Math.PI * parameters.spiral.branchesAmplitude 112 | 113 | const x = Math.pow( 114 | Math.random(), 115 | parameters.spiral.randomnessPower 116 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.spiral.randomness * randomRadiusPosition 117 | 118 | const y = Math.pow( 119 | Math.random(), 120 | parameters.spiral.randomnessPower 121 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.spiral.randomness * randomRadiusPosition 122 | 123 | const z = Math.pow( 124 | Math.random(), 125 | parameters.spiral.randomnessPower 126 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.spiral.randomness * randomRadiusPosition 127 | 128 | positions[i3] = Math.cos(branchAngle + spinAngle) * randomRadiusPosition + x 129 | positions[i3 + 1] = y 130 | positions[i3 + 2] = Math.sin(branchAngle + spinAngle) * randomRadiusPosition + z 131 | 132 | mixedColor.lerpColors( 133 | chosenColors.colorIn, 134 | chosenColors.colorOut, 135 | (randomRadiusPosition / radius) + parameters.spiral.colorInterpolationAmplitude 136 | ) 137 | 138 | colors[i3] = mixedColor.r 139 | colors[i3 + 1] = mixedColor.g 140 | colors[i3 + 2] = mixedColor.b 141 | } 142 | 143 | return { 144 | positions: new Float32Array(positions), 145 | colors: new Float32Array(colors) 146 | } 147 | } 148 | 149 | function _getTwoDifferentColors (pool) { 150 | const colorIn = new THREE.Color(pool.in[THREE.MathUtils.randInt(0, pool.in.length - 1)]) 151 | const colorOut = new THREE.Color(pool.out[THREE.MathUtils.randInt(0, pool.out.length - 1)]) 152 | 153 | return { colorIn, colorOut } 154 | } 155 | -------------------------------------------------------------------------------- /src/procedural/galaxy/SpiralGalaxyWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | self.onmessage = messageEvent => { 4 | const clustersToPopulate = messageEvent.data.clustersToPopulate 5 | const currentUniverse = messageEvent.data.currentUniverse 6 | const galaxyParameters = messageEvent.data.currentUniverse.matters.galaxy 7 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 8 | const galaxyAttributes = {} 9 | const defaultBranchesNumber = THREE.MathUtils.randInt(galaxyParameters.spiral.branches.min, galaxyParameters.spiral.branches.max) 10 | const chosenColors = _getTwoDifferentColors(galaxyParameters.galaxyColors) 11 | 12 | for (const clusterToPopulate of clustersToPopulate) { 13 | // base galaxy shaped 14 | const firstPassStarsRandomAttributes = _getGalaxyAttributesInRandomPosition( 15 | Math.floor( 16 | galaxyParameters.budget * THREE.MathUtils.randFloat( 17 | galaxyParameters.vertices.pass.min, 18 | galaxyParameters.vertices.pass.max 19 | ) 20 | ), 21 | clusterSize, 22 | galaxyParameters, 23 | chosenColors, 24 | defaultBranchesNumber, 25 | currentUniverse 26 | ) 27 | 28 | // gaz galaxy shaped 29 | const secondPassStarsRandomAttributes = _getGalaxyAttributesInRandomPosition( 30 | Math.floor( 31 | galaxyParameters.budget * THREE.MathUtils.randFloat( 32 | galaxyParameters.vertices.cloud.min * 0.6, 33 | galaxyParameters.vertices.cloud.max * 0.6 34 | ) 35 | ), 36 | clusterSize, 37 | galaxyParameters, 38 | chosenColors, 39 | defaultBranchesNumber, 40 | currentUniverse 41 | ) 42 | 43 | // low starfield density using color from galaxy 44 | const thirdPassStarsRandomAttributes = _getAttributesInRandomPosition( 45 | Math.floor( 46 | galaxyParameters.budget * THREE.MathUtils.randFloat( 47 | galaxyParameters.vertices.pass.min, 48 | galaxyParameters.vertices.pass.max 49 | ) 50 | ), 51 | clusterSize, 52 | galaxyParameters, 53 | chosenColors 54 | ) 55 | 56 | galaxyAttributes[clusterToPopulate] = { 57 | firstPassStarsRandomAttributes, 58 | secondPassStarsRandomAttributes, 59 | thirdPassStarsRandomAttributes 60 | } 61 | } 62 | 63 | self.postMessage(galaxyAttributes) 64 | } 65 | 66 | function _getGalaxyAttributesInRandomPosition (max, clusterSize, parameters, chosenColors, enforcedBranches, currentUniverse) { 67 | const positions = [] 68 | const colors = [] 69 | 70 | const radius = currentUniverse.universeModifiers.type.id === 'filaments' ? clusterSize * 5 : clusterSize / 1.8 71 | const branches = enforcedBranches || THREE.MathUtils.randInt(parameters.spiral.branches.min, parameters.spiral.branches.max) 72 | const spin = THREE.MathUtils.randInt(parameters.spiral.spin.min, parameters.spiral.spin.max) 73 | const mixedColor = chosenColors.colorIn.clone() 74 | 75 | for (let i = 0; i < max; i++) { 76 | const i3 = i * 3 77 | const randomRadiusPosition = Math.random() * radius + Math.random() 78 | const spinAngle = (randomRadiusPosition * parameters.spiral.spinAmplitude) * spin 79 | const branchAngle = (i % branches) / branches * Math.PI * parameters.spiral.branchesAmplitude 80 | 81 | const x = Math.pow( 82 | Math.random(), 83 | parameters.spiral.randomnessPower 84 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.spiral.randomness * randomRadiusPosition 85 | 86 | const y = Math.pow( 87 | Math.random(), 88 | parameters.spiral.randomnessPower 89 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.spiral.randomness * randomRadiusPosition 90 | 91 | const z = Math.pow( 92 | Math.random(), 93 | parameters.spiral.randomnessPower 94 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.spiral.randomness * randomRadiusPosition 95 | 96 | positions[i3] = Math.cos(branchAngle + spinAngle) * randomRadiusPosition + x 97 | positions[i3 + 1] = y 98 | positions[i3 + 2] = Math.sin(branchAngle + spinAngle) * randomRadiusPosition + z 99 | 100 | mixedColor.lerpColors( 101 | chosenColors.colorIn, 102 | chosenColors.colorOut, 103 | (randomRadiusPosition / radius) + parameters.spiral.colorInterpolationAmplitude 104 | ) 105 | 106 | colors[i3] = mixedColor.r 107 | colors[i3 + 1] = mixedColor.g 108 | colors[i3 + 2] = mixedColor.b 109 | } 110 | 111 | return { 112 | positions: new Float32Array(positions), 113 | colors: new Float32Array(colors) 114 | } 115 | } 116 | 117 | function _getAttributesInRandomPosition (max, clusterSize, parameters, chosenColors) { 118 | const positions = [] 119 | const colors = [] 120 | 121 | for (let i = 0; i < max; i++) { 122 | // creating coordinate for the particles in random positions but confined in the current square cluster 123 | const x = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 124 | const y = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 125 | const z = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 126 | 127 | positions.push(x, y, z) 128 | 129 | const color = chosenColors.colorOut 130 | 131 | colors.push(color.r, color.g, color.b) 132 | } 133 | 134 | return { 135 | positions: new Float32Array(positions), 136 | colors: new Float32Array(colors) 137 | } 138 | } 139 | 140 | function _getTwoDifferentColors (pool) { 141 | const colorIn = new THREE.Color(pool.in[THREE.MathUtils.randInt(0, pool.in.length - 1)]) 142 | const colorOut = new THREE.Color(pool.out[THREE.MathUtils.randInt(0, pool.out.length - 1)]) 143 | 144 | return { colorIn, colorOut } 145 | } 146 | -------------------------------------------------------------------------------- /src/procedural/giant/StarGiantWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | self.onmessage = messageEvent => { 4 | const clustersToPopulate = messageEvent.data.clustersToPopulate 5 | const giantParameters = messageEvent.data.currentUniverse.matters.giant 6 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 7 | const giantAttributes = {} 8 | 9 | for (const clusterToPopulate of clustersToPopulate) { 10 | const firstPassStarsRandomAttributes = _getAttributesInRandomPosition( 11 | Math.floor( 12 | giantParameters.budget * THREE.MathUtils.randFloat( 13 | giantParameters.vertices.pass.min, 14 | giantParameters.vertices.pass.max 15 | ) 16 | ), 17 | clusterSize, 18 | giantParameters 19 | ) 20 | 21 | giantAttributes[clusterToPopulate] = { 22 | firstPassStarsRandomAttributes 23 | } 24 | } 25 | 26 | self.postMessage(giantAttributes) 27 | } 28 | 29 | function _getAttributesInRandomPosition (max, clusterSize, parameters) { 30 | const positions = [] 31 | const colors = [] 32 | 33 | for (let i = 0; i < max; i++) { 34 | // creating coordinate for the particles in random positions but confined in the current square cluster 35 | const x = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 36 | const y = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 37 | const z = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 38 | 39 | positions.push(x, y, z) 40 | 41 | const color = new THREE.Color( 42 | Math.random() > 0.4 ? '#eeefff' : parameters.colors[THREE.MathUtils.randInt(0, parameters.colors.length - 1)] 43 | ) 44 | 45 | colors.push(color.r, color.g, color.b) 46 | } 47 | 48 | return { 49 | positions: new Float32Array(positions), 50 | colors: new Float32Array(colors) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/procedural/giant/SunGiantWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | self.onmessage = messageEvent => { 4 | const clustersToPopulate = messageEvent.data.clustersToPopulate 5 | const giantParameters = messageEvent.data.currentUniverse.matters.giant 6 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 7 | const giantAttributes = {} 8 | 9 | for (const clusterToPopulate of clustersToPopulate) { 10 | const firstPassStarsRandomAttributes = _getAttributesInRandomPosition( 11 | Math.floor( 12 | giantParameters.budget * THREE.MathUtils.randFloat( 13 | giantParameters.vertices.pass.min, 14 | giantParameters.vertices.pass.max 15 | ) 16 | ), 17 | clusterSize, 18 | giantParameters 19 | ) 20 | 21 | giantAttributes[clusterToPopulate] = { 22 | firstPassStarsRandomAttributes 23 | } 24 | } 25 | 26 | self.postMessage(giantAttributes) 27 | } 28 | 29 | function _getAttributesInRandomPosition (max, clusterSize, parameters) { 30 | const positions = [] 31 | const colors = [] 32 | 33 | for (let i = 0; i < max; i++) { 34 | // creating coordinate for the particles in random positions but confined in the current square cluster 35 | const x = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 36 | const y = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 37 | const z = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 38 | 39 | positions.push(x, y, z) 40 | 41 | const color = new THREE.Color( 42 | Math.random() > 0.4 ? '#eeefff' : parameters.colors[THREE.MathUtils.randInt(0, parameters.colors.length - 1)] 43 | ) 44 | 45 | colors.push(color.r, color.g, color.b) 46 | } 47 | 48 | return { 49 | positions: new Float32Array(positions), 50 | colors: new Float32Array(colors) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/procedural/giant/WhiteDwarfGiantWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | self.onmessage = messageEvent => { 4 | const clustersToPopulate = messageEvent.data.clustersToPopulate 5 | const giantParameters = messageEvent.data.currentUniverse.matters.giant 6 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 7 | const giantAttributes = {} 8 | 9 | for (const clusterToPopulate of clustersToPopulate) { 10 | const firstPassStarsRandomAttributes = _getAttributesInRandomPosition( 11 | Math.floor( 12 | giantParameters.budget * THREE.MathUtils.randFloat( 13 | giantParameters.vertices.pass.min, 14 | giantParameters.vertices.pass.max 15 | ) 16 | ), 17 | clusterSize, 18 | giantParameters 19 | ) 20 | 21 | giantAttributes[clusterToPopulate] = { 22 | firstPassStarsRandomAttributes 23 | } 24 | } 25 | 26 | self.postMessage(giantAttributes) 27 | } 28 | 29 | function _getAttributesInRandomPosition (max, clusterSize, parameters) { 30 | const positions = [] 31 | const colors = [] 32 | 33 | for (let i = 0; i < max; i++) { 34 | // creating coordinate for the particles in random positions but confined in the current square cluster 35 | const x = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 36 | const y = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 37 | const z = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 38 | 39 | positions.push(x, y, z) 40 | 41 | const color = new THREE.Color( 42 | Math.random() > 0.4 ? '#eeefff' : parameters.colors[THREE.MathUtils.randInt(0, parameters.colors.length - 1)] 43 | ) 44 | 45 | colors.push(color.r, color.g, color.b) 46 | } 47 | 48 | return { 49 | positions: new Float32Array(positions), 50 | colors: new Float32Array(colors) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/procedural/nebula/EmissionNebulaWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { Curves } from 'three/examples/jsm/curves/CurveExtras' 3 | 4 | self.onmessage = messageEvent => { 5 | const clustersToPopulate = messageEvent.data.clustersToPopulate 6 | const nebulaParameters = messageEvent.data.currentUniverse.matters.nebula 7 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 8 | const nebulasAttributes = {} 9 | 10 | for (const clusterToPopulate of clustersToPopulate) { 11 | // gaz shaped 12 | const gazRandomAttributes = _getShapeAttributesInRandomPosition(nebulaParameters) 13 | 14 | // random stars to fill emptyness 15 | const firstPassStarsRandomAttributes = _getAttributesInRandomPosition( 16 | nebulaParameters, 17 | Math.floor( 18 | nebulaParameters.budget * THREE.MathUtils.randFloat( 19 | nebulaParameters.vertices.pass.min, 20 | nebulaParameters.vertices.pass.max 21 | ) 22 | ), 23 | clusterSize 24 | ) 25 | 26 | // bright stars 27 | const secondPassStarsRandomAttributes = _getAttributesInRandomPosition( 28 | nebulaParameters, 29 | Math.floor( 30 | nebulaParameters.budget * THREE.MathUtils.randFloat( 31 | nebulaParameters.vertices.bright.min, 32 | nebulaParameters.vertices.bright.max 33 | ) 34 | ), 35 | clusterSize, 36 | false, 37 | gazRandomAttributes.colors 38 | ) 39 | 40 | // random stars shaped not following gaz 41 | const thirdPassStarsRandomAttributes = _getShapeAttributesInRandomPosition(nebulaParameters) 42 | 43 | nebulasAttributes[clusterToPopulate] = { 44 | gazRandomAttributes, 45 | firstPassStarsRandomAttributes, 46 | secondPassStarsRandomAttributes, 47 | thirdPassStarsRandomAttributes 48 | } 49 | } 50 | 51 | self.postMessage(nebulasAttributes) 52 | } 53 | 54 | function _getShapeAttributesInRandomPosition (parameters, enforcedPositions, enforcedColors) { 55 | const positions = enforcedPositions || [] 56 | const colors = enforcedColors || [] 57 | const randomNess = parameters.geometry.emission.randomness 58 | const radius = parameters.geometry.emission.radius 59 | 60 | const geometry = new THREE.TubeGeometry( 61 | new Curves.CinquefoilKnot(), 62 | parameters.vertices.emission.tubularSegments, 63 | parameters.vertices.emission.radius, 64 | parameters.vertices.emission.radiusSegments, 65 | parameters.vertices.emission.closed 66 | ) 67 | 68 | geometry.scale(80, 80, 80) 69 | 70 | if (!positions.length || !colors.length) { 71 | const chosenColors = _getTwoDifferentColors(parameters.colors) 72 | const mixedColor = chosenColors.colorIn.clone() 73 | 74 | for (let i = 0; i < geometry.attributes.position.array.length - 1; i++) { 75 | const i3 = i * 3 76 | 77 | mixedColor.lerpColors(chosenColors.colorIn, chosenColors.colorOut, Math.sin(i)) 78 | 79 | if (geometry.attributes.position.array[i3]) { 80 | positions[i3] = geometry.attributes.position.array[i3] * (Math.random() * randomNess + radius) 81 | colors[i3] = mixedColor.r 82 | } 83 | 84 | if (geometry.attributes.position.array[i3 + 1]) { 85 | positions[i3 + 1] = geometry.attributes.position.array[i3 + 1] * (Math.random() * randomNess + radius) 86 | colors[i3 + 1] = mixedColor.g 87 | } 88 | 89 | if (geometry.attributes.position.array[i3 + 2]) { 90 | positions[i3 + 2] = geometry.attributes.position.array[i3 + 2] * (Math.random() * randomNess + radius) 91 | colors[i3 + 2] = mixedColor.b 92 | } 93 | } 94 | } 95 | 96 | return { 97 | positions: new Float32Array(positions), 98 | colors: new Float32Array(colors) 99 | } 100 | } 101 | 102 | function _getTwoDifferentColors (pool) { 103 | const colorIn = new THREE.Color(pool.in[THREE.MathUtils.randInt(0, pool.in.length - 1)]) 104 | const colorOut = new THREE.Color(pool.out[THREE.MathUtils.randInt(0, pool.out.length - 1)]) 105 | 106 | return { colorIn, colorOut } 107 | } 108 | 109 | function _getAttributesInRandomPosition (parameters, max, clusterSize, enforcedPositions = false, enforcedColors = false) { 110 | const positions = enforcedPositions || [] 111 | const colors = enforcedColors || [] 112 | 113 | for (let i = 0; i < max; i++) { 114 | // creating coordinate for the particles in random positions but confined in the current square cluster 115 | if (!enforcedPositions) { 116 | const x = clusterSize * Math.random() - (clusterSize / 2) 117 | const y = clusterSize * Math.random() - (clusterSize / 2) 118 | const z = clusterSize * Math.random() - (clusterSize / 2) 119 | 120 | positions.push(x, y, z) 121 | } 122 | 123 | if (!enforcedColors) { 124 | const color = new THREE.Color( 125 | Math.random() > 0.2 ? '#eeefff' : parameters.colors[THREE.MathUtils.randInt(0, parameters.colors.length - 1)] 126 | ) 127 | 128 | colors.push(color.r, color.g, color.b) 129 | } 130 | } 131 | 132 | return { 133 | positions: new Float32Array(positions), 134 | colors: new Float32Array(colors) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/procedural/nebula/GargantuaNebulaWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { Curves } from 'three/examples/jsm/curves/CurveExtras' 3 | 4 | self.onmessage = messageEvent => { 5 | const clustersToPopulate = messageEvent.data.clustersToPopulate 6 | const nebulaParameters = messageEvent.data.currentUniverse.matters.nebula 7 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 8 | const nebulasAttributes = {} 9 | 10 | for (const clusterToPopulate of clustersToPopulate) { 11 | // gaz shaped 12 | const gazRandomAttributes = _getShapeAttributesInRandomPosition(nebulaParameters) 13 | 14 | // random stars to fill emptyness 15 | const firstPassStarsRandomAttributes = _getAttributesInRandomPosition( 16 | nebulaParameters, 17 | Math.floor( 18 | nebulaParameters.budget * THREE.MathUtils.randFloat( 19 | nebulaParameters.vertices.pass.min, 20 | nebulaParameters.vertices.pass.max 21 | ) 22 | ), 23 | clusterSize 24 | ) 25 | 26 | // bright stars 27 | const secondPassStarsRandomAttributes = _getAttributesInRandomPosition( 28 | nebulaParameters, 29 | Math.floor( 30 | nebulaParameters.budget * THREE.MathUtils.randFloat( 31 | nebulaParameters.vertices.bright.min, 32 | nebulaParameters.vertices.bright.max 33 | ) 34 | ), 35 | clusterSize, 36 | false, 37 | gazRandomAttributes.colors 38 | ) 39 | 40 | // random stars shaped not following gaz 41 | const thirdPassStarsRandomAttributes = _getShapeAttributesInRandomPosition(nebulaParameters) 42 | 43 | nebulasAttributes[clusterToPopulate] = { 44 | gazRandomAttributes, 45 | firstPassStarsRandomAttributes, 46 | secondPassStarsRandomAttributes, 47 | thirdPassStarsRandomAttributes 48 | } 49 | } 50 | 51 | self.postMessage(nebulasAttributes) 52 | } 53 | 54 | function _getShapeAttributesInRandomPosition (parameters, enforcedPositions, enforcedColors) { 55 | const positions = enforcedPositions || [] 56 | const colors = enforcedColors || [] 57 | const randomNess = 2 58 | const radius = 30 59 | 60 | const geometry = new THREE.TubeGeometry( 61 | new Curves.CinquefoilKnot(), 62 | parameters.vertices.gargantua.tubularSegments, 63 | parameters.vertices.gargantua.radius, 64 | parameters.vertices.gargantua.radiusSegments, 65 | parameters.vertices.gargantua.closed 66 | ) 67 | 68 | geometry.scale(50, 50, 50) 69 | 70 | if (!positions.length || !colors.length) { 71 | const chosenColors = _getTwoDifferentColors(parameters.colors) 72 | const mixedColor = chosenColors.colorIn.clone() 73 | 74 | for (let i = 0; i < geometry.attributes.position.array.length - 1; i++) { 75 | const i3 = i * 3 76 | 77 | mixedColor.lerpColors(chosenColors.colorIn, chosenColors.colorOut, Math.sin(i)) 78 | 79 | if (geometry.attributes.position.array[i3]) { 80 | positions[i3] = geometry.attributes.position.array[i3] * (Math.random() * randomNess + radius) 81 | colors[i3] = mixedColor.r 82 | } 83 | 84 | if (geometry.attributes.position.array[i3 + 1]) { 85 | positions[i3 + 1] = geometry.attributes.position.array[i3 + 1] * (Math.random() * randomNess + radius) 86 | colors[i3 + 1] = mixedColor.g 87 | } 88 | 89 | if (geometry.attributes.position.array[i3 + 2]) { 90 | positions[i3 + 2] = geometry.attributes.position.array[i3 + 2] * (Math.random() * randomNess + radius) 91 | colors[i3 + 2] = mixedColor.b 92 | } 93 | } 94 | } 95 | 96 | return { 97 | positions: new Float32Array(positions), 98 | colors: new Float32Array(colors) 99 | } 100 | } 101 | 102 | function _getTwoDifferentColors (pool) { 103 | const colorIn = new THREE.Color(pool.in[THREE.MathUtils.randInt(0, pool.in.length - 1)]) 104 | const colorOut = new THREE.Color(pool.out[THREE.MathUtils.randInt(0, pool.out.length - 1)]) 105 | 106 | return { colorIn, colorOut } 107 | } 108 | 109 | function _getAttributesInRandomPosition (parameters, max, clusterSize, enforcedPositions = false, enforcedColors = false) { 110 | const positions = enforcedPositions || [] 111 | const colors = enforcedColors || [] 112 | 113 | for (let i = 0; i < max; i++) { 114 | // creating coordinate for the particles in random positions but confined in the current square cluster 115 | if (!enforcedPositions) { 116 | const x = clusterSize * Math.random() - (clusterSize / 2) 117 | const y = clusterSize * Math.random() - (clusterSize / 2) 118 | const z = clusterSize * Math.random() - (clusterSize / 2) 119 | 120 | positions.push(x, y, z) 121 | } 122 | 123 | if (!enforcedColors) { 124 | const color = new THREE.Color( 125 | Math.random() > 0.2 ? '#eeefff' : parameters.colors[THREE.MathUtils.randInt(0, parameters.colors.length - 1)] 126 | ) 127 | 128 | colors.push(color.r, color.g, color.b) 129 | } 130 | } 131 | 132 | return { 133 | positions: new Float32Array(positions), 134 | colors: new Float32Array(colors) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/procedural/nebula/SupernovaRemnantsNebulaWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | self.onmessage = messageEvent => { 4 | const clustersToPopulate = messageEvent.data.clustersToPopulate 5 | const nebulaParameters = messageEvent.data.currentUniverse.matters.nebula 6 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 7 | const nebulasAttributes = {} 8 | 9 | for (const clusterToPopulate of clustersToPopulate) { 10 | // giant gaz sphere shaped 11 | const gazRandomAttributes = _getRoundAttributesInRandomPosition( 12 | nebulaParameters, 13 | Math.floor( 14 | nebulaParameters.budget * THREE.MathUtils.randFloat( 15 | nebulaParameters.vertices.cloud.min * 0.3, 16 | nebulaParameters.vertices.cloud.max * 0.3 17 | ) 18 | ), 19 | clusterSize, 20 | THREE.MathUtils.randInt(3000, 5000) 21 | ) 22 | 23 | // random stars shaped following giant gaz 24 | const firstPassStarsRandomAttributes = _getRoundAttributesInRandomPosition( 25 | nebulaParameters, 26 | Math.floor( 27 | nebulaParameters.budget * THREE.MathUtils.randFloat( 28 | nebulaParameters.vertices.pass.min * 0.5, 29 | nebulaParameters.vertices.pass.max * 0.5 30 | ) 31 | ), 32 | clusterSize 33 | ) 34 | 35 | // small gaz sphere shaped (inside) 36 | const secondPassStarsRandomAttributes = _getRoundAttributesInRandomPosition( 37 | nebulaParameters, 38 | Math.floor( 39 | nebulaParameters.budget * THREE.MathUtils.randFloat( 40 | nebulaParameters.vertices.cloud.min * 0.05, 41 | nebulaParameters.vertices.cloud.max * 0.05 42 | ) 43 | ), 44 | clusterSize * 0.5 45 | ) 46 | 47 | // random stars to fill emptyness 48 | const thirdPassStarsRandomAttributes = _getAttributesInRandomPosition( 49 | nebulaParameters, 50 | Math.floor( 51 | nebulaParameters.budget * THREE.MathUtils.randFloat( 52 | nebulaParameters.vertices.pass.min * 0.5, 53 | nebulaParameters.vertices.pass.max * 0.5 54 | ) 55 | ), 56 | clusterSize 57 | ) 58 | 59 | nebulasAttributes[clusterToPopulate] = { 60 | gazRandomAttributes, 61 | firstPassStarsRandomAttributes, 62 | secondPassStarsRandomAttributes, 63 | thirdPassStarsRandomAttributes 64 | } 65 | } 66 | 67 | self.postMessage(nebulasAttributes) 68 | } 69 | 70 | function _getAttributesInRandomPosition (parameters, max, clusterSize) { 71 | const positions = [] 72 | const colors = [] 73 | 74 | for (let i = 0; i < max; i++) { 75 | // creating coordinate for the particles in random positions but confined in the current square cluster 76 | const x = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 77 | const y = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 78 | const z = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 79 | 80 | positions.push(x, y, z) 81 | 82 | const color = new THREE.Color( 83 | Math.random() > 0.2 ? '#eeefff' : parameters.colors[THREE.MathUtils.randInt(0, parameters.colors.length - 1)] 84 | ) 85 | 86 | colors.push(color.r, color.g, color.b) 87 | } 88 | 89 | return { 90 | positions: new Float32Array(positions), 91 | colors: new Float32Array(colors) 92 | } 93 | } 94 | 95 | function _getRoundAttributesInRandomPosition (parameters, max, clusterSize, distordedAmplitude = 10000) { 96 | const positions = [] 97 | const colors = [] 98 | const radius = clusterSize / 3 99 | const chosenColors = _getTwoDifferentColors(parameters.remnantColors) 100 | const mixedColor = chosenColors.colorIn.clone() 101 | 102 | for (let i = 0; i < max; i++) { 103 | // creating coordinate for the particles in random positions but confined in the current sphere cluster 104 | const alpha = Math.random() * (Math.PI) 105 | const theta = Math.random() * (Math.PI * 2) 106 | const x = radius * (Math.cos(alpha) * Math.sin(theta)) - radius - THREE.MathUtils.randFloat(0, distordedAmplitude) 107 | const y = radius * (Math.sin(alpha) * Math.sin(theta)) - radius 108 | const z = radius * (Math.cos(theta)) - radius - THREE.MathUtils.randFloat(0, distordedAmplitude) 109 | 110 | positions.push(x, y, z) 111 | 112 | mixedColor.lerpColors(chosenColors.colorIn, chosenColors.colorOut, Math.sin(i)) 113 | colors.push(mixedColor.r, mixedColor.g, mixedColor.b) 114 | } 115 | 116 | return { 117 | positions: new Float32Array(positions), 118 | colors: new Float32Array(colors) 119 | } 120 | } 121 | 122 | function _getTwoDifferentColors (pool) { 123 | const colorIn = new THREE.Color(pool.in[THREE.MathUtils.randInt(0, pool.in.length - 1)]) 124 | const colorOut = new THREE.Color(pool.out[THREE.MathUtils.randInt(0, pool.out.length - 1)]) 125 | 126 | return { colorIn, colorOut } 127 | } 128 | -------------------------------------------------------------------------------- /src/procedural/singularity/BlackHoleSingularityWorker.js: -------------------------------------------------------------------------------- 1 | self.onmessage = messageEvent => { 2 | const clustersToPopulate = messageEvent.data.clustersToPopulate 3 | const blackholeAttributes = {} 4 | 5 | // we dont need any particles for this instance 6 | for (const clusterToPopulate of clustersToPopulate) { 7 | blackholeAttributes[clusterToPopulate] = {} 8 | } 9 | 10 | self.postMessage(blackholeAttributes) 11 | } 12 | -------------------------------------------------------------------------------- /src/procedural/singularity/Singularity.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { gsap } from 'gsap' 3 | 4 | import singularityBlackholeVertexShader from '../../shaders/singularity/blackhole/vertex.glsl' 5 | import singularityBlackholeFragmentShader from '../../shaders/singularity/blackhole/fragment.glsl' 6 | 7 | export default class Singularity { 8 | constructor (scene, library, parameters) { 9 | this.scene = scene 10 | this.library = library 11 | this.parameters = parameters 12 | 13 | this.textureSeen = [] 14 | this.giant = null 15 | } 16 | 17 | generate (blackholeAttributes, position) { 18 | const currentCoordinateVector = this._getCoordinateVectorByPosition(position) 19 | 20 | const blackholeDiskGeometry = new THREE.RingGeometry(5.45, 20, 32) 21 | const blackholeDiskMaterial = this._getRandomBlackHoleShaderMaterial() 22 | const blackholeDiskMesh = new THREE.Mesh(blackholeDiskGeometry, blackholeDiskMaterial) 23 | blackholeDiskMesh.scale.set(1000, 1000, 1000) 24 | blackholeDiskMesh.position.set(currentCoordinateVector.x, currentCoordinateVector.y, currentCoordinateVector.z) 25 | 26 | window.materialsToUpdate[blackholeDiskMaterial.uuid] = blackholeDiskMaterial 27 | window.meshesToUpdate[blackholeDiskMesh.uuid] = blackholeDiskMesh 28 | 29 | const blackholeGeometry = new THREE.SphereGeometry(1.5, 32, 16) 30 | const blackholeMaterial = new THREE.MeshBasicMaterial({ 31 | color: 0x000000, 32 | transparent: false, 33 | side: THREE.DoubleSide, 34 | opacity: 0 35 | }) 36 | const blackholeMesh = new THREE.Mesh(blackholeGeometry, blackholeMaterial) 37 | blackholeMesh.scale.set(5000, 5000, 3000) 38 | blackholeMesh.position.set(currentCoordinateVector.x, currentCoordinateVector.y, currentCoordinateVector.z) 39 | 40 | window.meshesToUpdate[blackholeMesh.uuid] = blackholeMesh 41 | 42 | const randomBlackhole = { 43 | disk: { 44 | geometry: blackholeDiskGeometry, 45 | texture: null, 46 | material: blackholeDiskMaterial, 47 | mesh: blackholeDiskMesh 48 | }, 49 | blackhole: { 50 | geometry: blackholeGeometry, 51 | texture: null, 52 | material: blackholeMaterial, 53 | mesh: blackholeMesh 54 | } 55 | } 56 | 57 | this.blackhole = randomBlackhole 58 | } 59 | 60 | dispose () { 61 | if (!this.blackhole) { 62 | console.log('Can\'t dispose empty blackhole') 63 | return 64 | } 65 | 66 | delete window.materialsToUpdate[this.blackhole.disk.material.uuid] 67 | 68 | delete window.meshesToUpdate[this.blackhole.disk.mesh.uuid] 69 | delete window.meshesToUpdate[this.blackhole.blackhole.mesh.uuid] 70 | 71 | this.blackhole.disk.geometry.dispose() 72 | this.blackhole.blackhole.geometry.dispose() 73 | 74 | this.blackhole.disk.material.dispose() 75 | this.blackhole.blackhole.material.dispose() 76 | 77 | this.scene.remove( 78 | this.blackhole.disk.mesh, 79 | this.blackhole.blackhole.mesh 80 | ) 81 | 82 | this.blackhole = null 83 | } 84 | 85 | show () { 86 | if (!this.blackhole) { 87 | console.log('Can\'t show empty blackhole') 88 | return 89 | } 90 | 91 | this.scene.add( 92 | this.blackhole.disk.mesh, 93 | this.blackhole.blackhole.mesh 94 | ) 95 | 96 | gsap.timeline() 97 | .to(this.blackhole.disk.material, { duration: 3, opacity: 1 }, 0) 98 | .to(this.blackhole.blackhole.material, { duration: 3, opacity: 1 }, 0) 99 | } 100 | 101 | _getRandomBlackHoleShaderMaterial () { 102 | return new THREE.ShaderMaterial({ 103 | precision: 'lowp', 104 | vertexShader: singularityBlackholeVertexShader, 105 | fragmentShader: singularityBlackholeFragmentShader, 106 | uniforms: { 107 | uTime: { value: 0 }, 108 | uTexture: { value: this.library.textures.blackhole.disk[0] }, 109 | fogColor: { value: this.scene.fog.color }, 110 | fogNear: { value: this.scene.fog.near }, 111 | fogFar: { value: this.scene.fog.far } 112 | }, 113 | side: THREE.DoubleSide, 114 | fog: true 115 | }) 116 | } 117 | 118 | _getCoordinateVectorByPosition (position) { 119 | const coordinateVector = new THREE.Vector3(0, 0, 0) 120 | 121 | // we dont need to tweak coordinates on the origin cluster 122 | if (position !== '0,0,0') { 123 | const arrayCurrentCluster = position.split(',') 124 | 125 | // handling x axis (right and left) clusters population 126 | const xCurrentCluster = parseInt(arrayCurrentCluster[0]) 127 | 128 | if (xCurrentCluster !== 0) { 129 | coordinateVector.x = (this.parameters.grid.clusterSize) * xCurrentCluster 130 | } 131 | 132 | // since we're not handling vertical movement at the moment 133 | // we dont need to handle the y axis 134 | 135 | // handling z axis (forward and backward) clusters population 136 | const zCurrentCluster = parseInt(arrayCurrentCluster[2]) 137 | 138 | if (zCurrentCluster !== 0) { 139 | coordinateVector.z = (this.parameters.grid.clusterSize) * zCurrentCluster 140 | } 141 | } 142 | 143 | return coordinateVector 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/procedural/spaceship/SpaceshipWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | self.onmessage = messageEvent => { 4 | const clustersToPopulate = messageEvent.data.clustersToPopulate 5 | const currentUniverse = messageEvent.data.currentUniverse 6 | const starfieldParameters = messageEvent.data.currentUniverse.matters.starfield 7 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 8 | const starfieldsAttributes = {} 9 | 10 | for (const clusterToPopulate of clustersToPopulate) { 11 | // first impressions are crucial 12 | const verticesPassmin = clusterToPopulate === '0,0,0' ? starfieldParameters.vertices.pass.max * 1.5 : starfieldParameters.vertices.pass.min 13 | const verticesPassmax = clusterToPopulate === '0,0,0' ? starfieldParameters.vertices.pass.max * 1.5 : starfieldParameters.vertices.pass.max 14 | 15 | const brightStarsRandomAttributes = _getAttributesInRandomPosition( 16 | Math.floor( 17 | starfieldParameters.budget * THREE.MathUtils.randFloat( 18 | starfieldParameters.vertices.bright.min, 19 | starfieldParameters.vertices.bright.max 20 | ) 21 | ), 22 | clusterSize, 23 | starfieldParameters, 24 | currentUniverse 25 | ) 26 | 27 | const firstPassStarsRandomAttributes = _getAttributesInRandomPosition( 28 | Math.floor( 29 | starfieldParameters.budget * THREE.MathUtils.randFloat( 30 | verticesPassmin, 31 | verticesPassmax 32 | ) 33 | ), 34 | clusterSize, 35 | starfieldParameters, 36 | currentUniverse 37 | ) 38 | 39 | const secondPassStarsRandomAttributes = _getAttributesInRandomPosition( 40 | Math.floor( 41 | starfieldParameters.budget * THREE.MathUtils.randFloat( 42 | verticesPassmin, 43 | verticesPassmax 44 | ) 45 | ), 46 | clusterSize, 47 | starfieldParameters, 48 | currentUniverse 49 | ) 50 | 51 | const thirdPassStarsRandomAttributes = _getAttributesInRandomPosition( 52 | Math.floor( 53 | starfieldParameters.budget * THREE.MathUtils.randFloat( 54 | verticesPassmin, 55 | verticesPassmax 56 | ) 57 | ), 58 | clusterSize, 59 | starfieldParameters, 60 | currentUniverse 61 | ) 62 | 63 | starfieldsAttributes[clusterToPopulate] = { 64 | brightStarsRandomAttributes, 65 | firstPassStarsRandomAttributes, 66 | secondPassStarsRandomAttributes, 67 | thirdPassStarsRandomAttributes 68 | } 69 | } 70 | 71 | self.postMessage(starfieldsAttributes) 72 | } 73 | 74 | function _getAttributesInRandomPosition (max, clusterSize, parameters, currentUniverse) { 75 | const positions = [] 76 | const colors = [] 77 | let x, y, z 78 | 79 | for (let i = 0; i < max; i++) { 80 | // random positions but confined in the current square cluster 81 | x = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 82 | y = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 83 | z = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 84 | 85 | positions.push(x, y, z) 86 | const color = new THREE.Color( 87 | Math.random() > 0.4 && currentUniverse.universeModifiers.type.id != 'bloom' ? '#eeefff' : parameters.colors[THREE.MathUtils.randInt(0, parameters.colors.length - 1)] 88 | ) 89 | 90 | colors.push(color.r, color.g, color.b) 91 | } 92 | 93 | return { 94 | positions: new Float32Array(positions), 95 | colors: new Float32Array(colors) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/procedural/starfield/GlobularStarfieldWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | self.onmessage = messageEvent => { 4 | const clustersToPopulate = messageEvent.data.clustersToPopulate 5 | const starfieldParameters = messageEvent.data.currentUniverse.matters.starfield 6 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 7 | const starfieldsAttributes = {} 8 | 9 | for (const clusterToPopulate of clustersToPopulate) { 10 | const brightStarsRandomAttributes = _getAttributesInRandomPosition( 11 | Math.floor( 12 | starfieldParameters.budget * THREE.MathUtils.randFloat( 13 | starfieldParameters.vertices.bright.min, 14 | starfieldParameters.vertices.bright.max 15 | ) 16 | ), 17 | clusterSize, 18 | starfieldParameters 19 | ) 20 | 21 | const firstPassStarsRandomAttributes = _getAttributesInRandomPosition( 22 | Math.floor( 23 | starfieldParameters.budget * THREE.MathUtils.randFloat( 24 | starfieldParameters.vertices.globularPass.min, 25 | starfieldParameters.vertices.globularPass.max 26 | ) 27 | ), 28 | clusterSize, 29 | starfieldParameters 30 | ) 31 | 32 | const secondPassStarsRandomAttributes = _getAttributesInRandomPosition( 33 | Math.floor( 34 | starfieldParameters.budget * THREE.MathUtils.randFloat( 35 | starfieldParameters.vertices.globularPass.min, 36 | starfieldParameters.vertices.globularPass.max 37 | ) 38 | ), 39 | clusterSize, 40 | starfieldParameters 41 | ) 42 | 43 | const thirdPassStarsRandomAttributes = _getAttributesInRandomPosition( 44 | Math.floor( 45 | starfieldParameters.budget * THREE.MathUtils.randFloat( 46 | starfieldParameters.vertices.globularPass.min, 47 | starfieldParameters.vertices.globularPass.max 48 | ) 49 | ), 50 | clusterSize, 51 | starfieldParameters 52 | ) 53 | 54 | starfieldsAttributes[clusterToPopulate] = { 55 | brightStarsRandomAttributes, 56 | firstPassStarsRandomAttributes, 57 | secondPassStarsRandomAttributes, 58 | thirdPassStarsRandomAttributes 59 | } 60 | } 61 | 62 | self.postMessage(starfieldsAttributes) 63 | } 64 | 65 | function _getAttributesInRandomPosition (max, clusterSize, parameters) { 66 | const positions = [] 67 | const colors = [] 68 | const spherical = new THREE.Spherical() 69 | const shapeDice = Math.random() 70 | const colorChosen = parameters.globularColors[THREE.MathUtils.randInt(0, parameters.globularColors.length - 1)] 71 | 72 | for (let i = 0; i < max; i++) { 73 | // creating coordinate for the particles in random positions but confined in the current sphere cluster 74 | spherical.phi = Math.random() * Math.PI 75 | spherical.theta = Math.random() * Math.PI * 2 76 | spherical.radius = Math.random() * ((clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5))) 77 | 78 | const currentVector = new THREE.Vector3().setFromSpherical(spherical) 79 | 80 | // random shapes 81 | if (shapeDice < 0.1) { 82 | currentVector.multiply(new THREE.Vector3().random()) 83 | } else if (shapeDice < 0.9) { 84 | currentVector.cross(new THREE.Vector3().random()) 85 | } 86 | 87 | positions.push(currentVector.x, currentVector.y, currentVector.z) 88 | 89 | const color = new THREE.Color(colorChosen) 90 | 91 | colors.push(color.r, color.g, color.b) 92 | } 93 | 94 | return { 95 | positions: new Float32Array(positions), 96 | colors: new Float32Array(colors) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/procedural/starfield/OpenStarfieldWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | self.onmessage = messageEvent => { 4 | const clustersToPopulate = messageEvent.data.clustersToPopulate 5 | const currentUniverse = messageEvent.data.currentUniverse 6 | const starfieldParameters = messageEvent.data.currentUniverse.matters.starfield 7 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 8 | const starfieldsAttributes = {} 9 | 10 | for (const clusterToPopulate of clustersToPopulate) { 11 | // first impressions are crucial 12 | const verticesPassmin = clusterToPopulate === '0,0,0' ? starfieldParameters.vertices.pass.max * 1.5 : starfieldParameters.vertices.pass.min 13 | const verticesPassmax = clusterToPopulate === '0,0,0' ? starfieldParameters.vertices.pass.max * 1.5 : starfieldParameters.vertices.pass.max 14 | 15 | const brightStarsRandomAttributes = _getAttributesInRandomPosition( 16 | Math.floor( 17 | starfieldParameters.budget * THREE.MathUtils.randFloat( 18 | starfieldParameters.vertices.bright.min, 19 | starfieldParameters.vertices.bright.max 20 | ) 21 | ), 22 | clusterSize, 23 | starfieldParameters, 24 | currentUniverse 25 | ) 26 | 27 | const firstPassStarsRandomAttributes = _getAttributesInRandomPosition( 28 | Math.floor( 29 | starfieldParameters.budget * THREE.MathUtils.randFloat( 30 | verticesPassmin, 31 | verticesPassmax 32 | ) 33 | ), 34 | clusterSize, 35 | starfieldParameters, 36 | currentUniverse 37 | ) 38 | 39 | const secondPassStarsRandomAttributes = _getAttributesInRandomPosition( 40 | Math.floor( 41 | starfieldParameters.budget * THREE.MathUtils.randFloat( 42 | verticesPassmin, 43 | verticesPassmax 44 | ) 45 | ), 46 | clusterSize, 47 | starfieldParameters, 48 | currentUniverse 49 | ) 50 | 51 | const thirdPassStarsRandomAttributes = _getAttributesInRandomPosition( 52 | Math.floor( 53 | starfieldParameters.budget * THREE.MathUtils.randFloat( 54 | verticesPassmin, 55 | verticesPassmax 56 | ) 57 | ), 58 | clusterSize, 59 | starfieldParameters, 60 | currentUniverse 61 | ) 62 | 63 | starfieldsAttributes[clusterToPopulate] = { 64 | brightStarsRandomAttributes, 65 | firstPassStarsRandomAttributes, 66 | secondPassStarsRandomAttributes, 67 | thirdPassStarsRandomAttributes 68 | } 69 | } 70 | 71 | self.postMessage(starfieldsAttributes) 72 | } 73 | 74 | function _getAttributesInRandomPosition (max, clusterSize, parameters, currentUniverse) { 75 | const positions = [] 76 | const colors = [] 77 | let x, y, z, alpha, theta 78 | 79 | for (let i = 0; i < max; i++) { 80 | if(currentUniverse.universeModifiers.type.id === 'ethereum') { 81 | if(Math.random() > 0.5) { 82 | // spheric cluster 83 | alpha = Math.random()*(Math.PI) 84 | theta = Math.random()*(Math.PI*2) 85 | x = clusterSize * (Math.cos(alpha) * Math.sin(theta)) - (clusterSize / 2) 86 | y = clusterSize * (Math.sin(alpha) * Math.sin(theta)) - (clusterSize / 2) 87 | z = clusterSize * (Math.cos(theta)) - (clusterSize / 2) 88 | } else { 89 | // octo cluster 90 | alpha = Math.random()*(Math.PI*2)-(Math.random()*Math.PI*2) 91 | theta = Math.random()*(Math.PI)-(Math.random()*Math.PI*2) 92 | x = clusterSize * (Math.pow(Math.cos(alpha)*Math.cos(theta), 3)) - (clusterSize / 2) 93 | y = clusterSize * (Math.pow(Math.sin(alpha)*Math.cos(theta), 3)) 94 | z = clusterSize * (Math.pow(Math.sin(theta), 3)) - (clusterSize / 2) 95 | } 96 | } else { 97 | // random positions but confined in the current square cluster 98 | x = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 99 | y = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 100 | z = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 101 | } 102 | 103 | positions.push(x, y, z) 104 | const color = new THREE.Color( 105 | Math.random() > 0.4 && currentUniverse != 1 ? '#eeefff' : parameters.colors[THREE.MathUtils.randInt(0, parameters.colors.length - 1)] 106 | ) 107 | 108 | colors.push(color.r, color.g, color.b) 109 | } 110 | 111 | return { 112 | positions: new Float32Array(positions), 113 | colors: new Float32Array(colors) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/procedural/starfield/Starfield.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { gsap } from 'gsap' 3 | 4 | export default class StarField { 5 | constructor (scene, library, parameters) { 6 | this.scene = scene 7 | this.library = library 8 | this.parameters = parameters 9 | 10 | this.textureSeen = [] 11 | this.starfield = null 12 | } 13 | 14 | generate (starfieldsAttributes, position) { 15 | const currentCoordinateVector = this._getCoordinateVectorByPosition(position) 16 | 17 | const brightStarsGeometry = this._getRandomStarsGeometry(starfieldsAttributes.brightStarsRandomAttributes) 18 | const brightStarTexture = this._getRandomStarsTexture('bright') 19 | const brightStarsmaterial = this._getRandomStarsMaterial(brightStarTexture, THREE.MathUtils.randInt( 20 | window.currentUniverse.matters.starfield.material.size.bright.min, 21 | window.currentUniverse.matters.starfield.material.size.bright.max 22 | )) 23 | const brightStars = new THREE.Points(brightStarsGeometry, brightStarsmaterial) 24 | 25 | brightStars.position.set(currentCoordinateVector.x, currentCoordinateVector.y, currentCoordinateVector.z) 26 | brightStars.rotateX(THREE.Math.degToRad(THREE.MathUtils.randInt(0, 360))) 27 | brightStars.rotateZ(THREE.Math.degToRad(THREE.MathUtils.randInt(0, 360))) 28 | 29 | const firstPassStarsGeometry = this._getRandomStarsGeometry(starfieldsAttributes.firstPassStarsRandomAttributes) 30 | const firstPassStarsTexture = this._getRandomStarsTexture() 31 | const firstPassStarsmaterial = this._getRandomStarsMaterial(firstPassStarsTexture) 32 | const firstPassStars = new THREE.Points(firstPassStarsGeometry, firstPassStarsmaterial) 33 | 34 | firstPassStars.position.set(currentCoordinateVector.x, currentCoordinateVector.y, currentCoordinateVector.z) 35 | firstPassStarsGeometry.rotateX(THREE.Math.degToRad(THREE.MathUtils.randInt(0, 360))) 36 | firstPassStarsGeometry.rotateZ(THREE.Math.degToRad(THREE.MathUtils.randInt(0, 360))) 37 | 38 | const secondPassStarsGeometry = this._getRandomStarsGeometry(starfieldsAttributes.secondPassStarsRandomAttributes) 39 | const secondPassStarsTexture = this._getRandomStarsTexture() 40 | const secondPassStarsmaterial = this._getRandomStarsMaterial(secondPassStarsTexture) 41 | const secondPassStars = new THREE.Points(secondPassStarsGeometry, secondPassStarsmaterial) 42 | 43 | secondPassStars.position.set(currentCoordinateVector.x, currentCoordinateVector.y, currentCoordinateVector.z) 44 | secondPassStars.rotateX(THREE.Math.degToRad(THREE.MathUtils.randInt(0, 360))) 45 | secondPassStars.rotateZ(THREE.Math.degToRad(THREE.MathUtils.randInt(0, 360))) 46 | 47 | const thirdPassStarsGeometry = this._getRandomStarsGeometry(starfieldsAttributes.thirdPassStarsRandomAttributes) 48 | const thirdPassStarsTexture = this._getRandomStarsTexture() 49 | const thirdPassStarsmaterial = this._getRandomStarsMaterial(thirdPassStarsTexture) 50 | const thirdPassStars = new THREE.Points(thirdPassStarsGeometry, thirdPassStarsmaterial) 51 | 52 | thirdPassStars.position.set(currentCoordinateVector.x, currentCoordinateVector.y, currentCoordinateVector.z) 53 | thirdPassStars.rotateX(THREE.Math.degToRad(THREE.MathUtils.randInt(0, 360))) 54 | thirdPassStars.rotateZ(THREE.Math.degToRad(THREE.MathUtils.randInt(0, 360))) 55 | 56 | const randomStarfield = { 57 | bright: { 58 | geometry: brightStarsGeometry, 59 | texture: brightStarTexture, 60 | material: brightStarsmaterial, 61 | points: brightStars 62 | }, 63 | firstPass: { 64 | geometry: firstPassStarsGeometry, 65 | texture: firstPassStarsTexture, 66 | material: firstPassStarsmaterial, 67 | points: firstPassStars 68 | }, 69 | secondPass: { 70 | geometry: secondPassStarsGeometry, 71 | texture: secondPassStarsTexture, 72 | material: secondPassStarsmaterial, 73 | points: secondPassStars 74 | }, 75 | thirdPass: { 76 | geometry: thirdPassStarsGeometry, 77 | texture: thirdPassStarsTexture, 78 | material: thirdPassStarsmaterial, 79 | points: thirdPassStars 80 | } 81 | } 82 | 83 | this.starfield = randomStarfield 84 | } 85 | 86 | dispose () { 87 | if (!this.starfield) { 88 | console.log('Can\'t dispose empty starfield') 89 | return 90 | } 91 | 92 | this.starfield.bright.geometry.dispose() 93 | this.starfield.firstPass.geometry.dispose() 94 | this.starfield.secondPass.geometry.dispose() 95 | this.starfield.thirdPass.geometry.dispose() 96 | 97 | this.starfield.bright.material.dispose() 98 | this.starfield.firstPass.material.dispose() 99 | this.starfield.secondPass.material.dispose() 100 | this.starfield.thirdPass.material.dispose() 101 | 102 | this.scene.remove( 103 | this.starfield.bright.points, 104 | this.starfield.firstPass.points, 105 | this.starfield.secondPass.points, 106 | this.starfield.thirdPass.points 107 | ) 108 | 109 | this.starfield = null 110 | } 111 | 112 | show () { 113 | if (!this.starfield) { 114 | console.log('Can\'t show empty starfield') 115 | return 116 | } 117 | 118 | this.scene.add( 119 | this.starfield.bright.points, 120 | this.starfield.firstPass.points, 121 | this.starfield.secondPass.points, 122 | this.starfield.thirdPass.points 123 | ) 124 | 125 | gsap.timeline() 126 | .to(this.starfield.bright.points.material, { duration: 3, opacity: 1 }, 0) 127 | .to(this.starfield.firstPass.points.material, { duration: 3, opacity: 1 }, 0) 128 | .to(this.starfield.secondPass.points.material, { duration: 3, opacity: 1 }, 0) 129 | .to(this.starfield.thirdPass.points.material, { duration: 3, opacity: 1 }, 0) 130 | } 131 | 132 | _getCoordinateVectorByPosition (position) { 133 | const coordinateVector = new THREE.Vector3(0, 0, 0) 134 | 135 | // we dont need to tweak coordinates on the origin cluster 136 | if (position !== '0,0,0') { 137 | const arrayCurrentCluster = position.split(',') 138 | 139 | // handling x axis (right and left) clusters population 140 | const xCurrentCluster = parseInt(arrayCurrentCluster[0]) 141 | 142 | if (xCurrentCluster !== 0) { 143 | coordinateVector.x = (this.parameters.grid.clusterSize) * xCurrentCluster 144 | } 145 | 146 | // since we're not handling vertical movement at the moment 147 | // we dont need to handle the y axis 148 | 149 | // handling z axis (forward and backward) clusters population 150 | const zCurrentCluster = parseInt(arrayCurrentCluster[2]) 151 | 152 | if (zCurrentCluster !== 0) { 153 | coordinateVector.z = (this.parameters.grid.clusterSize) * zCurrentCluster 154 | } 155 | } 156 | 157 | return coordinateVector 158 | } 159 | 160 | /** 161 | * @param {*} max 162 | * @returns 163 | */ 164 | _getRandomStarsGeometry (randomAttributes) { 165 | const geometry = new THREE.BufferGeometry() 166 | 167 | geometry.setAttribute( 168 | 'position', 169 | new THREE.Float32BufferAttribute(randomAttributes.positions, 3) 170 | ) 171 | 172 | geometry.setAttribute( 173 | 'color', 174 | new THREE.BufferAttribute(randomAttributes.colors, 3) 175 | ) 176 | 177 | return geometry 178 | } 179 | 180 | _getRandomStarsTexture (type = window.currentUniverse.matters.starfield.material.defaultType) { 181 | const currentTexturesPool = this.library.textures.starfield[type].filter(texture => !this.textureSeen.includes(texture)) 182 | const randomTexture = currentTexturesPool[THREE.MathUtils.randInt(0, currentTexturesPool.length - 1)] 183 | this.textureSeen.push(randomTexture) 184 | 185 | return randomTexture 186 | } 187 | 188 | /** 189 | * @param {*} texture 190 | * @param {*} opacity 191 | * @param {*} size 192 | * @returns 193 | */ 194 | _getRandomStarsMaterial (randomMaterialTexture, enforcedSize, enforcedOpacity) { 195 | const randomMaterialSize = enforcedSize || enforcedSize === 0 ? enforcedSize : THREE.MathUtils.randInt(window.currentUniverse.matters.starfield.material.size.pass.min, window.currentUniverse.matters.starfield.material.size.pass.max) 196 | const randomMaterialOpacity = enforcedOpacity || enforcedOpacity === 0 ? enforcedOpacity : THREE.MathUtils.randInt(window.currentUniverse.matters.starfield.material.opacity.pass.min, window.currentUniverse.matters.starfield.material.opacity.pass.max) 197 | 198 | randomMaterialTexture.magFilter = THREE.NearestFilter 199 | 200 | const material = new THREE.PointsMaterial({ 201 | size: randomMaterialSize, 202 | opacity: randomMaterialOpacity, 203 | map: randomMaterialTexture, 204 | sizeAttenuation: true, 205 | depthWrite: false, 206 | transparent: window.currentUniverse.matters.starfield.material.transparent, 207 | blending: window.currentUniverse.matters.starfield.material.blending, 208 | vertexColors: true, 209 | opacity: 0 210 | }) 211 | 212 | return material 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/procedural/strangerthings/CyclicWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | self.onmessage = messageEvent => { 4 | const clustersToPopulate = messageEvent.data.clustersToPopulate 5 | const strangerThingsParameters = messageEvent.data.currentUniverse.matters.strangerthings.cyclic 6 | const strangerThingsAttributes = {} 7 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 8 | 9 | for (const clusterToPopulate of clustersToPopulate) { 10 | // base galaxy shaped 11 | const firstPassStarsRandomAttributes = _getCyclicAttributesInRandomPosition( 12 | Math.floor( 13 | strangerThingsParameters.budget * THREE.MathUtils.randFloat( 14 | strangerThingsParameters.vertices.pass.min, 15 | strangerThingsParameters.vertices.pass.max 16 | ) 17 | ), 18 | clusterSize, 19 | strangerThingsParameters 20 | ) 21 | 22 | // gaz galaxy shaped 23 | const secondPassStarsRandomAttributes = _getCyclicAttributesInRandomPosition( 24 | Math.floor( 25 | strangerThingsParameters.budget * THREE.MathUtils.randFloat( 26 | strangerThingsParameters.vertices.cloud.min * 0.6, 27 | strangerThingsParameters.vertices.cloud.max * 0.6 28 | ) 29 | ), 30 | clusterSize, 31 | strangerThingsParameters 32 | ) 33 | 34 | // low starfield density using color from galaxy 35 | const thirdPassStarsRandomAttributes = _getAttributesInRandomPosition( 36 | Math.floor( 37 | strangerThingsParameters.budget * THREE.MathUtils.randFloat( 38 | strangerThingsParameters.vertices.pass.min * 0.6, 39 | strangerThingsParameters.vertices.pass.max * 0.6 40 | ) 41 | ), 42 | clusterSize, 43 | strangerThingsParameters 44 | ) 45 | 46 | strangerThingsAttributes[clusterToPopulate] = { 47 | firstPassStarsRandomAttributes, 48 | secondPassStarsRandomAttributes, 49 | thirdPassStarsRandomAttributes 50 | } 51 | } 52 | 53 | self.postMessage(strangerThingsAttributes) 54 | } 55 | 56 | function _getCyclicAttributesInRandomPosition (max, clusterSize, parameters) { 57 | const chosenColors = _getTwoDifferentColors(parameters.colors) 58 | const positions = [] 59 | const colors = [] 60 | const radius = clusterSize * 20 61 | const mixedColor = chosenColors.colorIn.clone() 62 | 63 | for (let i = 0; i < max; i++) { 64 | const i3 = i * 3 65 | const randomRadiusPosition = Math.random() * radius + Math.random() 66 | 67 | const x = Math.pow( 68 | Math.random(), 69 | parameters.spiral.randomnessPower 70 | ) * parameters.spiral.randomness * randomRadiusPosition 71 | 72 | const y = Math.pow( 73 | Math.random(), 74 | parameters.spiral.randomnessPower 75 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.spiral.randomness * randomRadiusPosition 76 | 77 | const z = Math.pow( 78 | Math.random(), 79 | parameters.spiral.randomnessPower 80 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.spiral.randomness * randomRadiusPosition 81 | 82 | positions[i3] = Math.sin(randomRadiusPosition) + x 83 | positions[i3 + 1] = y 84 | positions[i3 + 2] = Math.cos(randomRadiusPosition) + z 85 | 86 | mixedColor.lerpColors( 87 | chosenColors.colorIn, 88 | chosenColors.colorOut, 89 | (randomRadiusPosition / radius) + parameters.spiral.colorInterpolationAmplitude 90 | ) 91 | 92 | colors[i3] = mixedColor.r 93 | colors[i3 + 1] = mixedColor.g 94 | colors[i3 + 2] = mixedColor.b 95 | } 96 | 97 | return { 98 | positions: new Float32Array(positions), 99 | colors: new Float32Array(colors) 100 | } 101 | } 102 | 103 | function _getAttributesInRandomPosition (max, clusterSize, parameters) { 104 | const positions = [] 105 | const colors = [] 106 | 107 | for (let i = 0; i < max; i++) { 108 | // creating coordinate for the particles in random positions but confined in the current square cluster 109 | const x = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 110 | const y = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 111 | const z = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 112 | 113 | positions.push(x, y, z) 114 | 115 | colors.push(0, 0, 0.545) 116 | } 117 | 118 | return { 119 | positions: new Float32Array(positions), 120 | colors: new Float32Array(colors) 121 | } 122 | } 123 | 124 | function _getTwoDifferentColors (pool) { 125 | const colorIn = new THREE.Color(pool.in[THREE.MathUtils.randInt(0, pool.in.length - 1)]) 126 | const colorOut = new THREE.Color(pool.out[THREE.MathUtils.randInt(0, pool.out.length - 1)]) 127 | 128 | return { colorIn, colorOut } 129 | } 130 | -------------------------------------------------------------------------------- /src/procedural/strangerthings/SpearWorker.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | self.onmessage = messageEvent => { 4 | const clustersToPopulate = messageEvent.data.clustersToPopulate 5 | const strangerThingsParameters = messageEvent.data.currentUniverse.matters.strangerthings.spear 6 | const strangerThingsAttributes = {} 7 | const clusterSize = messageEvent.data.parameters.grid.clusterSize 8 | 9 | for (const clusterToPopulate of clustersToPopulate) { 10 | // base galaxy shaped 11 | const firstPassStarsRandomAttributes = _getCyclicAttributesInRandomPosition( 12 | Math.floor( 13 | strangerThingsParameters.budget * THREE.MathUtils.randFloat( 14 | strangerThingsParameters.vertices.pass.min, 15 | strangerThingsParameters.vertices.pass.max 16 | ) 17 | ), 18 | clusterSize, 19 | strangerThingsParameters 20 | ) 21 | 22 | // gaz galaxy shaped 23 | const secondPassStarsRandomAttributes = _getCyclicAttributesInRandomPosition( 24 | Math.floor( 25 | strangerThingsParameters.budget * THREE.MathUtils.randFloat( 26 | strangerThingsParameters.vertices.cloud.min * 0.6, 27 | strangerThingsParameters.vertices.cloud.max * 0.6 28 | ) 29 | ), 30 | clusterSize, 31 | strangerThingsParameters 32 | ) 33 | 34 | // low starfield density using color from galaxy 35 | const thirdPassStarsRandomAttributes = _getAttributesInRandomPosition( 36 | Math.floor( 37 | strangerThingsParameters.budget * THREE.MathUtils.randFloat( 38 | strangerThingsParameters.vertices.pass.min * 0.01, 39 | strangerThingsParameters.vertices.pass.max * 0.01 40 | ) 41 | ), 42 | clusterSize, 43 | strangerThingsParameters 44 | ) 45 | 46 | strangerThingsAttributes[clusterToPopulate] = { 47 | firstPassStarsRandomAttributes, 48 | secondPassStarsRandomAttributes, 49 | thirdPassStarsRandomAttributes 50 | } 51 | } 52 | 53 | self.postMessage(strangerThingsAttributes) 54 | } 55 | 56 | function _getCyclicAttributesInRandomPosition (max, clusterSize, parameters) { 57 | const chosenColors = _getTwoDifferentColors(parameters.colors) 58 | const positions = [] 59 | const colors = [] 60 | const radius = clusterSize * 20 61 | const mixedColor = chosenColors.colorIn.clone() 62 | 63 | for (let i = 0; i < max; i++) { 64 | const i3 = i * 3 65 | const randomRadiusPosition = Math.random() * radius + Math.random() 66 | 67 | const x = Math.pow( 68 | Math.random(), 69 | parameters.spiral.randomnessPower 70 | ) * parameters.spiral.randomness * randomRadiusPosition 71 | 72 | const y = Math.pow( 73 | Math.random(), 74 | parameters.spiral.randomnessPower 75 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.spiral.randomness * randomRadiusPosition 76 | 77 | const z = Math.pow( 78 | Math.random(), 79 | parameters.spiral.randomnessPower 80 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.spiral.randomness * randomRadiusPosition 81 | 82 | positions[i3] = randomRadiusPosition * 5 + x 83 | positions[i3 + 1] = y *5 84 | positions[i3 + 2] = randomRadiusPosition * 5 + z 85 | 86 | mixedColor.lerpColors( 87 | chosenColors.colorIn, 88 | chosenColors.colorOut, 89 | (randomRadiusPosition / radius) + parameters.spiral.colorInterpolationAmplitude 90 | ) 91 | 92 | colors[i3] = mixedColor.r 93 | colors[i3 + 1] = mixedColor.g 94 | colors[i3 + 2] = mixedColor.b 95 | } 96 | 97 | return { 98 | positions: new Float32Array(positions), 99 | colors: new Float32Array(colors) 100 | } 101 | } 102 | 103 | function _getAttributesInRandomPosition (max, clusterSize, parameters) { 104 | const positions = [] 105 | const colors = [] 106 | 107 | for (let i = 0; i < max; i++) { 108 | // creating coordinate for the particles in random positions but confined in the current square cluster 109 | const x = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 110 | const y = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 111 | const z = clusterSize * Math.random() - (clusterSize / 2) + THREE.MathUtils.randFloat(0, Math.floor(clusterSize / 5)) 112 | 113 | positions.push(x, y, z) 114 | 115 | colors.push(1, 0, 0) 116 | } 117 | 118 | return { 119 | positions: new Float32Array(positions), 120 | colors: new Float32Array(colors) 121 | } 122 | } 123 | 124 | function _getTwoDifferentColors (pool) { 125 | const colorIn = new THREE.Color(pool.in[THREE.MathUtils.randInt(0, pool.in.length - 1)]) 126 | const colorOut = new THREE.Color(pool.out[THREE.MathUtils.randInt(0, pool.out.length - 1)]) 127 | 128 | return { colorIn, colorOut } 129 | } 130 | -------------------------------------------------------------------------------- /src/procedural/strangerthings/StrangerThings.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { gsap } from 'gsap' 3 | 4 | export default class StrangerThings { 5 | constructor (scene, library, parameters) { 6 | this.scene = scene 7 | this.library = library 8 | this.parameters = parameters 9 | this.currentCoordinateVector = null 10 | this.textureSeen = [] 11 | this.galaxy = null 12 | this.subtype = null 13 | } 14 | 15 | generate (strangerThingsAttributes, position, subtype = null) { 16 | this.currentCoordinateVector = this._getCoordinateVectorByPosition(position) 17 | this.subtype = subtype 18 | 19 | const rotation = THREE.Math.degToRad(THREE.MathUtils.randInt(0, 360)) 20 | 21 | const firstPassStarsGeometry = this._getRandomStarsGeometry(strangerThingsAttributes.firstPassStarsRandomAttributes) 22 | const firstPassStarsTexture = this._getRandomStarsTexture() 23 | const firstPassStarsmaterial = this._getRandomStarsMaterial(firstPassStarsTexture, currentUniverse.universeModifiers.type.id === 'filaments' ? 300 : false) 24 | const firstPassStars = new THREE.Points(firstPassStarsGeometry, firstPassStarsmaterial) 25 | 26 | firstPassStars.position.set(this.currentCoordinateVector.x, this.currentCoordinateVector.y, this.currentCoordinateVector.z) 27 | firstPassStarsGeometry.rotateX(rotation) 28 | 29 | const secondPassStarsGeometry = this._getRandomStarsGeometry(strangerThingsAttributes.secondPassStarsRandomAttributes) 30 | const secondPassStarsTexture = this._getRandomStarsTextureByType('cloud') 31 | const secondPassStarsmaterial = this._getRandomStarsMaterial(secondPassStarsTexture, 800, 100) 32 | const secondPassStars = new THREE.Points(secondPassStarsGeometry, secondPassStarsmaterial) 33 | 34 | secondPassStars.position.set(this.currentCoordinateVector.x, this.currentCoordinateVector.y, this.currentCoordinateVector.z) 35 | secondPassStars.rotateX(rotation) 36 | 37 | const thirdPassStarsGeometry = this._getRandomStarsGeometry(strangerThingsAttributes.thirdPassStarsRandomAttributes) 38 | const thirdPassStarsTexture = this._getRandomStarsTexture() 39 | const thirdPassStarsmaterial = this._getRandomStarsMaterial(thirdPassStarsTexture) 40 | const thirdPassStars = new THREE.Points(thirdPassStarsGeometry, thirdPassStarsmaterial) 41 | thirdPassStars.rotateX(rotation) 42 | 43 | thirdPassStars.position.set(this.currentCoordinateVector.x, this.currentCoordinateVector.y, this.currentCoordinateVector.z) 44 | 45 | if(this.subtype === 'cyclic'){ 46 | window.cyclicStrangerThingsToUpdate[firstPassStars.uuid] = firstPassStars 47 | window.cyclicStrangerThingsToUpdate[secondPassStars.uuid] = secondPassStars 48 | window.cyclicStrangerThingsToUpdate[thirdPassStars.uuid] = thirdPassStars 49 | } 50 | 51 | const randomGalaxy = { 52 | firstPass: { 53 | geometry: firstPassStarsGeometry, 54 | texture: firstPassStarsTexture, 55 | material: firstPassStarsmaterial, 56 | points: firstPassStars 57 | }, 58 | secondPass: { 59 | geometry: secondPassStarsGeometry, 60 | texture: secondPassStarsTexture, 61 | material: secondPassStarsmaterial, 62 | points: secondPassStars 63 | }, 64 | thirdPass: { 65 | geometry: thirdPassStarsGeometry, 66 | texture: thirdPassStarsTexture, 67 | material: thirdPassStarsmaterial, 68 | points: thirdPassStars 69 | } 70 | } 71 | 72 | this.galaxy = randomGalaxy 73 | } 74 | 75 | dispose () { 76 | if (!this.galaxy) { 77 | console.log('Can\'t dispose empty galaxy') 78 | return 79 | } 80 | 81 | this.galaxy.firstPass.geometry.dispose() 82 | this.galaxy.secondPass.geometry.dispose() 83 | this.galaxy.thirdPass.geometry.dispose() 84 | 85 | this.galaxy.firstPass.material.dispose() 86 | this.galaxy.secondPass.material.dispose() 87 | this.galaxy.thirdPass.material.dispose() 88 | 89 | if(window.cyclicStrangerThingsToUpdate[this.galaxy.firstPass.uuid]) { 90 | delete window.cyclicStrangerThingsToUpdate[this.galaxy.firstPass.uuid] 91 | delete window.cyclicStrangerThingsToUpdate[this.galaxy.secondPass.uuid] 92 | delete window.cyclicStrangerThingsToUpdate[this.galaxy.thirdPass.uuid] 93 | } 94 | 95 | this.scene.remove( 96 | this.galaxy.firstPass.points, 97 | this.galaxy.secondPass.points, 98 | this.galaxy.thirdPass.points 99 | ) 100 | 101 | this.galaxy = null 102 | } 103 | 104 | show () { 105 | if (!this.galaxy) { 106 | console.log('Can\'t show empty galaxy') 107 | return 108 | } 109 | 110 | this.scene.add( 111 | this.galaxy.firstPass.points, 112 | this.galaxy.secondPass.points, 113 | this.galaxy.thirdPass.points 114 | ) 115 | 116 | let minCloudOpacity = window.currentUniverse.matters.galaxy.material.opacity.pass.min 117 | let maxCloudOpacity = window.currentUniverse.matters.galaxy.material.opacity.pass.max 118 | 119 | gsap.timeline() 120 | .to(this.galaxy.firstPass.points.material, { duration: 3, opacity: 1 }, 0) 121 | .to(this.galaxy.secondPass.points.material, { 122 | duration: 3, 123 | opacity: THREE.MathUtils.randFloat(minCloudOpacity, maxCloudOpacity) 124 | }, 0) 125 | .to(this.galaxy.thirdPass.points.material, { duration: 3, opacity: 1 }, 0) 126 | } 127 | 128 | _getCoordinateVectorByPosition (position) { 129 | const coordinateVector = new THREE.Vector3(0, 0, 0) 130 | 131 | // we dont need to tweak coordinates on the origin cluster 132 | if (position !== '0,0,0') { 133 | const arrayCurrentCluster = position.split(',') 134 | 135 | // handling x axis (right and left) clusters population 136 | const xCurrentCluster = parseInt(arrayCurrentCluster[0]) 137 | 138 | if (xCurrentCluster !== 0) { 139 | coordinateVector.x = (this.parameters.grid.clusterSize) * xCurrentCluster 140 | } 141 | 142 | // since we're not handling vertical movement at the moment 143 | // we dont need to handle the y axis 144 | 145 | // handling z axis (forward and backward) clusters population 146 | const zCurrentCluster = parseInt(arrayCurrentCluster[2]) 147 | 148 | if (zCurrentCluster !== 0) { 149 | coordinateVector.z = (this.parameters.grid.clusterSize) * zCurrentCluster 150 | } 151 | } 152 | 153 | return coordinateVector 154 | } 155 | 156 | /** 157 | * @param {*} max 158 | * @returns 159 | */ 160 | _getRandomStarsGeometry (randomAttributes) { 161 | const geometry = new THREE.BufferGeometry() 162 | 163 | geometry.setAttribute( 164 | 'position', 165 | new THREE.Float32BufferAttribute(randomAttributes.positions, 3) 166 | ) 167 | 168 | geometry.setAttribute( 169 | 'color', 170 | new THREE.BufferAttribute(randomAttributes.colors, 3) 171 | ) 172 | 173 | return geometry 174 | } 175 | 176 | _getRandomStarsTexture () { 177 | const currentTexturesPool = this.library.textures.starfield["pass"] 178 | const randomTexture = currentTexturesPool[THREE.MathUtils.randInt(0, currentTexturesPool.length - 1)] 179 | 180 | this.textureSeen.push(randomTexture) 181 | 182 | return randomTexture 183 | } 184 | 185 | _getRandomStarsTextureByType (type = 'pass') { 186 | const currentTexturesPool = this.library.textures.nebula[type].filter(texture => !this.textureSeen.includes(texture)) 187 | const randomTexture = currentTexturesPool[THREE.MathUtils.randInt(0, currentTexturesPool.length - 1)] 188 | 189 | this.textureSeen.push(randomTexture) 190 | 191 | return randomTexture 192 | } 193 | 194 | /** 195 | * @param {*} texture 196 | * @param {*} opacity 197 | * @param {*} size 198 | * @returns 199 | */ 200 | _getRandomStarsMaterial (randomMaterialTexture, enforcedSize, enforcedOpacity) { 201 | const randomMaterialSize = enforcedSize || enforcedSize === 0 ? enforcedSize : THREE.MathUtils.randInt(window.currentUniverse.matters.strangerthings[this.subtype].material.size.pass.min, window.currentUniverse.matters.strangerthings[this.subtype].material.size.pass.max) 202 | const randomMaterialOpacity = enforcedOpacity || enforcedOpacity === 0 ? enforcedOpacity : THREE.MathUtils.randInt(window.currentUniverse.matters.strangerthings[this.subtype].material.opacity.pass.min, window.currentUniverse.matters.strangerthings[this.subtype].material.opacity.pass.max) 203 | 204 | randomMaterialTexture.magFilter = THREE.NearestFilter 205 | 206 | const material = new THREE.PointsMaterial({ 207 | size: randomMaterialSize, 208 | opacity: randomMaterialOpacity, 209 | map: randomMaterialTexture, 210 | sizeAttenuation: true, 211 | depthWrite: false, 212 | transparent: window.currentUniverse.matters.galaxy.material.transparent, 213 | blending: window.currentUniverse.matters.galaxy.material.blending, 214 | vertexColors: true, 215 | opacity: 0 216 | }) 217 | 218 | return material 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/sequencer/epiphany/Epiphany.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { gsap } from 'gsap' 3 | 4 | export default class Epiphany { 5 | constructor (scene, library, parameters, camera, sequencer) { 6 | this.scene = scene 7 | this.library = library 8 | this.parameters = parameters 9 | this.camera = camera 10 | this.sequencer = sequencer 11 | } 12 | 13 | generate () { 14 | this.epiphanyGeometry = this._getEpiphanyGeometry( 15 | this._getEpiphanyAttributesInRandomPosition( 16 | 300000, 17 | 45000, 18 | this.parameters.defaultMatters.galaxy, 19 | window.currentUniverse 20 | ) 21 | ) 22 | this.epiphanyMaterial = this._getEpiphanyMaterial(this.library.textures.universe.universe[0]) 23 | this.epiphany = new THREE.Points(this.epiphanyGeometry, this.epiphanyMaterial) 24 | 25 | this.epiphanySourceGeometry = new THREE.PlaneGeometry(1000000, 1000000) 26 | this.epiphanySourceMaterial = new THREE.MeshBasicMaterial({color: 0xFFFFFF, side: THREE.DoubleSide}) 27 | this.epiphanySourceMesh = new THREE.Mesh(this.epiphanySourceGeometry, this.epiphanySourceMaterial) 28 | 29 | this.epiphanySourceMesh.rotation.y = -150 30 | this.scene.add(this.epiphany, this.epiphanySourceMesh) 31 | 32 | window.epiphany = this 33 | } 34 | 35 | _getEpiphanyAttributesInRandomPosition (max, clusterSize, parameters) { 36 | const chosenColors = this._getTwoDifferentColors(parameters.galaxyColors) 37 | const positions = [] 38 | const colors = [] 39 | const radius = clusterSize * 10 40 | const mixedColor = chosenColors.colorIn.clone() 41 | 42 | for (let i = 0; i < max; i++) { 43 | const i3 = i * 3 44 | const randomRadiusPosition = Math.random() * radius + Math.random() 45 | 46 | const x = Math.pow( 47 | Math.random(), 48 | parameters.spiral.randomnessPower 49 | ) * parameters.spiral.randomness * randomRadiusPosition 50 | 51 | const y = Math.pow( 52 | Math.random(), 53 | parameters.spiral.randomnessPower 54 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.spiral.randomness * randomRadiusPosition 55 | 56 | const z = Math.pow( 57 | Math.random(), 58 | parameters.spiral.randomnessPower 59 | ) * (Math.random() < 0.5 ? 1 : -1) * parameters.spiral.randomness * randomRadiusPosition 60 | 61 | positions[i3] = randomRadiusPosition + x 62 | positions[i3 + 1] = y 63 | positions[i3 + 2] = randomRadiusPosition + z 64 | 65 | mixedColor.lerpColors( 66 | chosenColors.colorIn, 67 | chosenColors.colorOut, 68 | (randomRadiusPosition / radius) + parameters.spiral.colorInterpolationAmplitude 69 | ) 70 | 71 | colors[i3] = mixedColor.r 72 | colors[i3 + 1] = mixedColor.g 73 | colors[i3 + 2] = mixedColor.b 74 | } 75 | 76 | return { 77 | positions: new Float32Array(positions), 78 | colors: new Float32Array(colors) 79 | } 80 | } 81 | 82 | _getTwoDifferentColors (pool) { 83 | const colorIn = new THREE.Color(pool.in[THREE.MathUtils.randInt(0, pool.in.length - 1)]) 84 | const colorOut = new THREE.Color(pool.out[THREE.MathUtils.randInt(0, pool.out.length - 1)]) 85 | 86 | return { colorIn, colorOut } 87 | } 88 | 89 | _getEpiphanyGeometry (randomAttributes) { 90 | const geometry = new THREE.BufferGeometry() 91 | 92 | geometry.setAttribute( 93 | 'position', 94 | new THREE.Float32BufferAttribute(randomAttributes.positions, 3) 95 | ) 96 | 97 | geometry.setAttribute( 98 | 'color', 99 | new THREE.BufferAttribute(randomAttributes.colors, 3) 100 | ) 101 | 102 | return geometry 103 | } 104 | 105 | _getEpiphanyMaterial (texture) { 106 | texture.magFilter = THREE.NearestFilter 107 | 108 | const material = new THREE.PointsMaterial({ 109 | size: 200, 110 | opacity: 1, 111 | map: texture, 112 | sizeAttenuation: true, 113 | depthWrite: false, 114 | transparent: true, 115 | blending: THREE.AdditiveBlending, 116 | vertexColors: false 117 | }) 118 | 119 | return material 120 | } 121 | 122 | async animate () { 123 | this.epiphanyTimeline = gsap.timeline() 124 | 125 | // initial position 126 | this.camera.position.x = 41933.344083521246 127 | this.camera.position.y = 2601.9473229586356 128 | this.camera.position.z = -41610.88345738852 129 | this.camera.rotation.x = -1.5819946141795853 130 | this.camera.rotation.y = -0.01142823229729023 131 | this.camera.rotation.z = -2.3460639896805997 132 | 133 | // first step, moving to epiphany 134 | this.epiphanyTimeline 135 | .to(this.camera.position, { duration: 70, x: 5073.649103134025 }, 0) 136 | .to(this.camera.position, { duration: 70, y: 2601.9473229586356 }, 0) 137 | .to(this.camera.position, { duration: 70, z: 3463.0872659983047 }, 0) 138 | .to(this.camera.rotation, { duration: 70, x: -2.152754981377802 }, 0) 139 | .to(this.camera.rotation, { duration: 70, y: 0.4933583002859766 }, 0) 140 | .to(this.camera.rotation, { duration: 70, z: 2.517722836064396 }, 0) 141 | 142 | // second step, diving to epiphany 143 | this.epiphanyTimeline 144 | .to(this.camera.position, { duration: 70, x: 43502.35769250616 }, 65) 145 | .to(this.camera.position, { duration: 70, y: 2601.9461793450773 }, 65) 146 | .to(this.camera.position, { duration: 70, z: 40744.89427643778 }, 65) 147 | .to(this.camera.rotation, { duration: 70, x: 3.0245027035506347 }, 65) 148 | .to(this.camera.rotation, { duration: 70, y: -0.8722113376562891 }, 65) 149 | .to(this.camera.rotation, { duration: 70, z: 3.0517609576969265 }, 65) 150 | 151 | // final step, across the multiverse 152 | this.epiphanyTimeline 153 | .to(this.camera.position, { duration: 600, x: 593635.3548790453 }, 120) 154 | .to(this.camera.position, { duration: 600, y: 2601.946179 }, 120) 155 | .to(this.camera.position, { duration: 600, z: 495067.01445157954 }, 120) 156 | 157 | // parallel story telling 158 | this.sequencer.fadeOutById('#blackwall', 10, 'Power0.easeNone') 159 | await this.sequencer.fadeOutById('#whitewall', 10, 'Power0.easeNone') 160 | await this.sequencer.showThenHideStory(this.parameters.story.epiphany[0]) 161 | await this.sequencer.showThenHideStory(this.parameters.story.epiphany[1], 0) 162 | await this.sequencer.showThenHideStory(this.parameters.story.epiphany[2], 0) 163 | await this.sequencer.showThenHideStory(this.parameters.story.epiphany[3], 0) 164 | await this.sequencer.showThenHideStory(this.parameters.story.epiphany[4], 0) 165 | await this.sequencer.showThenHideStory(this.parameters.story.epiphany[5], 0) 166 | await this.sequencer.showThenHideStory(this.parameters.story.epiphany[6], 0) 167 | await this.sequencer.showThenHideStory(this.parameters.story.epiphany[7], 0) 168 | await this.sequencer.showThenHideStory(this.parameters.story.epiphany[8], 0) 169 | await this.sequencer.showThenHideStory(this.parameters.story.epiphany[9], 0) 170 | await this.sequencer.asyncWaitFor(5000) 171 | await this.sequencer.fadeInById('#credits', 2, 'Power0.easeNone') 172 | 173 | window.controls.pointerLockControls.unlock() 174 | } 175 | 176 | dispose () { 177 | this.epiphanyGeometry.dispose() 178 | this.epiphanyMaterial.dispose() 179 | 180 | this.epiphanySourceGeometry.dispose() 181 | this.epiphanySourceMaterial.dispose() 182 | 183 | this.scene.remove(this.epiphany, this.epiphanySourceMesh) 184 | 185 | this.epiphany = null 186 | this.epiphanySourceMesh = null 187 | window.epiphany = null 188 | 189 | this.epiphanyTimeline.kill() 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/sequencer/wormhole/Wormhole.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { Curves } from 'three/examples/jsm/curves/CurveExtras' 3 | import { SceneUtils } from 'three/examples/jsm/utils/SceneUtils.js' 4 | import { gsap } from 'gsap' 5 | 6 | export default class Wormhole { 7 | constructor (scene, library, parameters) { 8 | this.scene = scene 9 | this.library = library 10 | this.parameters = parameters 11 | } 12 | 13 | generate () { 14 | window.wormhole.shape = new Curves.TorusKnot(500) 15 | 16 | this.library.textures.wormhole.galaxy[0].wrapS = THREE.RepeatWrapping 17 | this.library.textures.wormhole.galaxy[0].wrapT = THREE.MirroredRepeatWrapping 18 | this.library.textures.wormhole.galaxy[0].repeat.set(40, 2) 19 | 20 | this.wireframedStarsSpeederMaterial = new THREE.MeshBasicMaterial({ 21 | map: this.library.textures.wormhole.galaxy[0], 22 | transparent: false, 23 | opacity: this.parameters.wormhole.wireframedStarsSpeeder.material.opacity, 24 | blending: THREE.AdditiveBlending, 25 | side: THREE.BackSide, 26 | wireframe: true 27 | }) 28 | 29 | this.library.textures.wormhole.galaxy[1].wrapS = THREE.RepeatWrapping 30 | this.library.textures.wormhole.galaxy[1].wrapT = THREE.MirroredRepeatWrapping 31 | this.library.textures.wormhole.galaxy[1].repeat.set(1, 2) 32 | 33 | this.auraSpeederMaterial = new THREE.MeshBasicMaterial({ 34 | map: this.library.textures.wormhole.galaxy[1], 35 | transparent: false, 36 | opacity: this.parameters.wormhole.auraSpeeder.material.opacity, 37 | blending: THREE.AdditiveBlending, 38 | side: THREE.DoubleSide 39 | }) 40 | 41 | this.library.textures.wormhole.galaxy[2].wrapS = THREE.RepeatWrapping 42 | this.library.textures.wormhole.galaxy[2].wrapT = THREE.MirroredRepeatWrapping 43 | this.library.textures.wormhole.galaxy[2].repeat.set(20, 2) 44 | 45 | this.nebulaSpeederMaterial = new THREE.MeshBasicMaterial({ 46 | map: this.library.textures.wormhole.galaxy[2], 47 | transparent: false, 48 | opacity: this.parameters.wormhole.nebulaSpeeder.material.opacity, 49 | blending: THREE.AdditiveBlending, 50 | side: THREE.BackSide 51 | }) 52 | 53 | this.library.textures.wormhole.galaxy[3].wrapS = THREE.RepeatWrapping 54 | this.library.textures.wormhole.galaxy[3].wrapT = THREE.MirroredRepeatWrapping 55 | this.library.textures.wormhole.galaxy[3].repeat.set(10, 2) 56 | 57 | this.starsSpeederMaterial = new THREE.MeshBasicMaterial({ 58 | map: this.library.textures.wormhole.galaxy[3], 59 | transparent: false, 60 | opacity: this.parameters.wormhole.starsSpeeder.material.opacity, 61 | blending: THREE.AdditiveBlending, 62 | side: THREE.BackSide 63 | }) 64 | 65 | this.library.textures.wormhole.galaxy[4].wrapS = THREE.RepeatWrapping 66 | this.library.textures.wormhole.galaxy[4].wrapT = THREE.MirroredRepeatWrapping 67 | this.library.textures.wormhole.galaxy[4].repeat.set(20, 2) 68 | 69 | this.clusterSpeederMaterial = new THREE.MeshBasicMaterial({ 70 | map: this.library.textures.wormhole.galaxy[4], 71 | transparent: false, 72 | opacity: this.parameters.wormhole.clusterSpeeder.material.opacity, 73 | blending: THREE.AdditiveBlending, 74 | side: THREE.BackSide 75 | }) 76 | 77 | this.wormholeGeometry = new THREE.TubeGeometry(window.wormhole.shape, 800, 5, 12, true) 78 | this.wormholeTubeMesh = SceneUtils.createMultiMaterialObject(this.wormholeGeometry, [ 79 | this.wireframedStarsSpeederMaterial, 80 | this.auraSpeederMaterial, 81 | this.nebulaSpeederMaterial, 82 | this.starsSpeederMaterial, 83 | this.clusterSpeederMaterial 84 | ]) 85 | 86 | this.scene.add(this.wormholeTubeMesh) 87 | } 88 | 89 | async animate () { 90 | this.wormholeTimeline = gsap.timeline() 91 | 92 | // initial massive boost at wormhole enter 93 | this.wormholeTimeline 94 | .to(this.starsSpeederMaterial, { duration: 7, opacity: 1 }, 0) 95 | .to(this.wireframedStarsSpeederMaterial, { duration: 7, ease: 'expo.out', opacity: 1 }, 0) 96 | .to(this.auraSpeederMaterial, { duration: 7, ease: 'expo.out', opacity: 1 }, 0) 97 | .to(window.wormhole, { duration: 7, ease: 'expo.out', speed: 2500 }, 0) 98 | 99 | // adding speed and noises 100 | this.wormholeTimeline 101 | .to(this.clusterSpeederMaterial, { duration: 6, opacity: 1 }, 7) 102 | .to(this.auraSpeederMaterial, { duration: 2, opacity: 0 }, 7) 103 | .to(window.wormhole, { duration: 6, speed: 2000 }, 7) 104 | 105 | // adding speed and nebula distorded 106 | this.wormholeTimeline 107 | .to(this.nebulaSpeederMaterial, { duration: 6, opacity: 1 }, 13) 108 | .to(this.clusterSpeederMaterial, { duration: 6, opacity: 0 }, 13) 109 | .to(this.auraSpeederMaterial, { duration: 6, opacity: 0.7 }, 13) 110 | .to(window.wormhole, { duration: 6, speed: 1800 }, 13) 111 | 112 | if (!window.isMobileOrTabletFlag) { 113 | window.controls.velocity.x = 0 114 | window.controls.velocity.z = 0 115 | } 116 | 117 | return this.wormholeTimeline.then(() => true) 118 | } 119 | 120 | active () { 121 | window.wormhole.active = true 122 | } 123 | 124 | dispose () { 125 | window.wormhole.active = false 126 | 127 | this.wormholeGeometry.dispose() 128 | 129 | this.wireframedStarsSpeederMaterial.dispose() 130 | this.auraSpeederMaterial.dispose() 131 | this.nebulaSpeederMaterial.dispose() 132 | this.starsSpeederMaterial.dispose() 133 | this.clusterSpeederMaterial.dispose() 134 | 135 | this.scene.remove(this.wormholeTubeMesh) 136 | 137 | this.wormholeTimeline = null 138 | this.wormholeTubeMesh = null 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/shaders/giant/star/vertex.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | // 3 | // GLSL textureless classic 4D noise "cnoise", 4 | // with an RSL-style periodic variant "pnoise". 5 | // Author: Stefan Gustavson (stefan.gustavson@liu.se) 6 | // Version: 2011-08-22 7 | // 8 | // Many thanks to Ian McEwan of Ashima Arts for the 9 | // ideas for permutation and gradient selection. 10 | // 11 | // Copyright (c) 2011 Stefan Gustavson. All rights reserved. 12 | // Distributed under the MIT license. See LICENSE file. 13 | // https://github.com/ashima/webgl-noise 14 | // 15 | 16 | vec4 mod289(vec4 x) 17 | { 18 | return x - floor(x * (1.0 / 289.0)) * 289.0; 19 | } 20 | 21 | vec4 permute(vec4 x) 22 | { 23 | return mod289(((x*34.0)+1.0)*x); 24 | } 25 | 26 | vec4 taylorInvSqrt(vec4 r) 27 | { 28 | return 1.79284291400159 - 0.85373472095314 * r; 29 | } 30 | 31 | vec4 fade(vec4 t) { 32 | return t*t*t*(t*(t*6.0-15.0)+10.0); 33 | } 34 | 35 | // Classic Perlin noise 36 | float cnoise(vec4 P) 37 | { 38 | vec4 Pi0 = floor(P); // Integer part for indexing 39 | vec4 Pi1 = Pi0 + 1.0; // Integer part + 1 40 | Pi0 = mod289(Pi0); 41 | Pi1 = mod289(Pi1); 42 | vec4 Pf0 = fract(P); // Fractional part for interpolation 43 | vec4 Pf1 = Pf0 - 1.0; // Fractional part - 1.0 44 | vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); 45 | vec4 iy = vec4(Pi0.yy, Pi1.yy); 46 | vec4 iz0 = vec4(Pi0.zzzz); 47 | vec4 iz1 = vec4(Pi1.zzzz); 48 | vec4 iw0 = vec4(Pi0.wwww); 49 | vec4 iw1 = vec4(Pi1.wwww); 50 | 51 | vec4 ixy = permute(permute(ix) + iy); 52 | vec4 ixy0 = permute(ixy + iz0); 53 | vec4 ixy1 = permute(ixy + iz1); 54 | vec4 ixy00 = permute(ixy0 + iw0); 55 | vec4 ixy01 = permute(ixy0 + iw1); 56 | vec4 ixy10 = permute(ixy1 + iw0); 57 | vec4 ixy11 = permute(ixy1 + iw1); 58 | 59 | vec4 gx00 = ixy00 * (1.0 / 7.0); 60 | vec4 gy00 = floor(gx00) * (1.0 / 7.0); 61 | vec4 gz00 = floor(gy00) * (1.0 / 6.0); 62 | gx00 = fract(gx00) - 0.5; 63 | gy00 = fract(gy00) - 0.5; 64 | gz00 = fract(gz00) - 0.5; 65 | vec4 gw00 = vec4(0.75) - abs(gx00) - abs(gy00) - abs(gz00); 66 | vec4 sw00 = step(gw00, vec4(0.0)); 67 | gx00 -= sw00 * (step(0.0, gx00) - 0.5); 68 | gy00 -= sw00 * (step(0.0, gy00) - 0.5); 69 | 70 | vec4 gx01 = ixy01 * (1.0 / 7.0); 71 | vec4 gy01 = floor(gx01) * (1.0 / 7.0); 72 | vec4 gz01 = floor(gy01) * (1.0 / 6.0); 73 | gx01 = fract(gx01) - 0.5; 74 | gy01 = fract(gy01) - 0.5; 75 | gz01 = fract(gz01) - 0.5; 76 | vec4 gw01 = vec4(0.75) - abs(gx01) - abs(gy01) - abs(gz01); 77 | vec4 sw01 = step(gw01, vec4(0.0)); 78 | gx01 -= sw01 * (step(0.0, gx01) - 0.5); 79 | gy01 -= sw01 * (step(0.0, gy01) - 0.5); 80 | 81 | vec4 gx10 = ixy10 * (1.0 / 7.0); 82 | vec4 gy10 = floor(gx10) * (1.0 / 7.0); 83 | vec4 gz10 = floor(gy10) * (1.0 / 6.0); 84 | gx10 = fract(gx10) - 0.5; 85 | gy10 = fract(gy10) - 0.5; 86 | gz10 = fract(gz10) - 0.5; 87 | vec4 gw10 = vec4(0.75) - abs(gx10) - abs(gy10) - abs(gz10); 88 | vec4 sw10 = step(gw10, vec4(0.0)); 89 | gx10 -= sw10 * (step(0.0, gx10) - 0.5); 90 | gy10 -= sw10 * (step(0.0, gy10) - 0.5); 91 | 92 | vec4 gx11 = ixy11 * (1.0 / 7.0); 93 | vec4 gy11 = floor(gx11) * (1.0 / 7.0); 94 | vec4 gz11 = floor(gy11) * (1.0 / 6.0); 95 | gx11 = fract(gx11) - 0.5; 96 | gy11 = fract(gy11) - 0.5; 97 | gz11 = fract(gz11) - 0.5; 98 | vec4 gw11 = vec4(0.75) - abs(gx11) - abs(gy11) - abs(gz11); 99 | vec4 sw11 = step(gw11, vec4(0.0)); 100 | gx11 -= sw11 * (step(0.0, gx11) - 0.5); 101 | gy11 -= sw11 * (step(0.0, gy11) - 0.5); 102 | 103 | vec4 g0000 = vec4(gx00.x,gy00.x,gz00.x,gw00.x); 104 | vec4 g1000 = vec4(gx00.y,gy00.y,gz00.y,gw00.y); 105 | vec4 g0100 = vec4(gx00.z,gy00.z,gz00.z,gw00.z); 106 | vec4 g1100 = vec4(gx00.w,gy00.w,gz00.w,gw00.w); 107 | vec4 g0010 = vec4(gx10.x,gy10.x,gz10.x,gw10.x); 108 | vec4 g1010 = vec4(gx10.y,gy10.y,gz10.y,gw10.y); 109 | vec4 g0110 = vec4(gx10.z,gy10.z,gz10.z,gw10.z); 110 | vec4 g1110 = vec4(gx10.w,gy10.w,gz10.w,gw10.w); 111 | vec4 g0001 = vec4(gx01.x,gy01.x,gz01.x,gw01.x); 112 | vec4 g1001 = vec4(gx01.y,gy01.y,gz01.y,gw01.y); 113 | vec4 g0101 = vec4(gx01.z,gy01.z,gz01.z,gw01.z); 114 | vec4 g1101 = vec4(gx01.w,gy01.w,gz01.w,gw01.w); 115 | vec4 g0011 = vec4(gx11.x,gy11.x,gz11.x,gw11.x); 116 | vec4 g1011 = vec4(gx11.y,gy11.y,gz11.y,gw11.y); 117 | vec4 g0111 = vec4(gx11.z,gy11.z,gz11.z,gw11.z); 118 | vec4 g1111 = vec4(gx11.w,gy11.w,gz11.w,gw11.w); 119 | 120 | vec4 norm00 = taylorInvSqrt(vec4(dot(g0000, g0000), dot(g0100, g0100), dot(g1000, g1000), dot(g1100, g1100))); 121 | g0000 *= norm00.x; 122 | g0100 *= norm00.y; 123 | g1000 *= norm00.z; 124 | g1100 *= norm00.w; 125 | 126 | vec4 norm01 = taylorInvSqrt(vec4(dot(g0001, g0001), dot(g0101, g0101), dot(g1001, g1001), dot(g1101, g1101))); 127 | g0001 *= norm01.x; 128 | g0101 *= norm01.y; 129 | g1001 *= norm01.z; 130 | g1101 *= norm01.w; 131 | 132 | vec4 norm10 = taylorInvSqrt(vec4(dot(g0010, g0010), dot(g0110, g0110), dot(g1010, g1010), dot(g1110, g1110))); 133 | g0010 *= norm10.x; 134 | g0110 *= norm10.y; 135 | g1010 *= norm10.z; 136 | g1110 *= norm10.w; 137 | 138 | vec4 norm11 = taylorInvSqrt(vec4(dot(g0011, g0011), dot(g0111, g0111), dot(g1011, g1011), dot(g1111, g1111))); 139 | g0011 *= norm11.x; 140 | g0111 *= norm11.y; 141 | g1011 *= norm11.z; 142 | g1111 *= norm11.w; 143 | 144 | float n0000 = dot(g0000, Pf0); 145 | float n1000 = dot(g1000, vec4(Pf1.x, Pf0.yzw)); 146 | float n0100 = dot(g0100, vec4(Pf0.x, Pf1.y, Pf0.zw)); 147 | float n1100 = dot(g1100, vec4(Pf1.xy, Pf0.zw)); 148 | float n0010 = dot(g0010, vec4(Pf0.xy, Pf1.z, Pf0.w)); 149 | float n1010 = dot(g1010, vec4(Pf1.x, Pf0.y, Pf1.z, Pf0.w)); 150 | float n0110 = dot(g0110, vec4(Pf0.x, Pf1.yz, Pf0.w)); 151 | float n1110 = dot(g1110, vec4(Pf1.xyz, Pf0.w)); 152 | float n0001 = dot(g0001, vec4(Pf0.xyz, Pf1.w)); 153 | float n1001 = dot(g1001, vec4(Pf1.x, Pf0.yz, Pf1.w)); 154 | float n0101 = dot(g0101, vec4(Pf0.x, Pf1.y, Pf0.z, Pf1.w)); 155 | float n1101 = dot(g1101, vec4(Pf1.xy, Pf0.z, Pf1.w)); 156 | float n0011 = dot(g0011, vec4(Pf0.xy, Pf1.zw)); 157 | float n1011 = dot(g1011, vec4(Pf1.x, Pf0.y, Pf1.zw)); 158 | float n0111 = dot(g0111, vec4(Pf0.x, Pf1.yzw)); 159 | float n1111 = dot(g1111, Pf1); 160 | 161 | vec4 fade_xyzw = fade(Pf0); 162 | vec4 n_0w = mix(vec4(n0000, n1000, n0100, n1100), vec4(n0001, n1001, n0101, n1101), fade_xyzw.w); 163 | vec4 n_1w = mix(vec4(n0010, n1010, n0110, n1110), vec4(n0011, n1011, n0111, n1111), fade_xyzw.w); 164 | vec4 n_zw = mix(n_0w, n_1w, fade_xyzw.z); 165 | vec2 n_yzw = mix(n_zw.xy, n_zw.zw, fade_xyzw.y); 166 | float n_xyzw = mix(n_yzw.x, n_yzw.y, fade_xyzw.x); 167 | return 2.2 * n_xyzw; 168 | } 169 | 170 | uniform samplerCube texCube; 171 | uniform float uTime; 172 | uniform float time_multiplier; 173 | uniform vec3 color_step_1; 174 | uniform vec3 color_step_2; 175 | uniform vec3 color_step_3; 176 | uniform vec3 color_step_4; 177 | uniform float ratio_step_1; 178 | uniform float ratio_step_2; 179 | uniform float displacement; 180 | varying vec3 v_position; 181 | varying float v_intensity; 182 | 183 | vec3 get_position(vec3 position) 184 | { 185 | vec3 new_position = position; 186 | 187 | // Set up 188 | float value = 0.0; 189 | float frequency_1 = 2.0; 190 | float speed_1 = 0.1; 191 | float frequency_2 = 24.0; 192 | float speed_2 = 0.2; 193 | 194 | // First global perlin (from 0 to 1) 195 | float perlin_1 = (cnoise(vec4(frequency_1 * position,uTime *time_multiplier * 0.6)) + 1.0) / 2.0; 196 | float perlin_2 = (cnoise(vec4(frequency_2 * position,uTime *time_multiplier * 0.8)) + 1.0) / 2.0; 197 | 198 | float value_1 = clamp(perlin_1,0.4,0.5) - 0.35; 199 | float value_2 = perlin_2; 200 | 201 | value = value_1 * value_2 * 0.50; 202 | 203 | new_position += normal * value; 204 | 205 | return new_position; 206 | } 207 | 208 | float get_intensity(vec3 position,float angle) 209 | { 210 | // Perlins 211 | float value = 0.0; 212 | 213 | // | frequency | speed 214 | float perlin_1 = (cnoise(vec4(2.0 * position,uTime * time_multiplier * 0.2)) + 1.0) / 2.0; 215 | float perlin_2 = (cnoise(vec4(6.0 * position,uTime * time_multiplier * 0.3)) + 1.0) / 2.0; 216 | float perlin_3 = (cnoise(vec4(12.0 * position,uTime * time_multiplier * 0.5)) + 1.0) / 2.0; 217 | 218 | float value_1 = clamp(perlin_1,0.4,0.5) - 0.35; 219 | float value_2 = perlin_2; 220 | float value_3 = perlin_3; 221 | 222 | // Final value 223 | value = value_1 * perlin_2 * 8.0 * value_3 * 0.4; 224 | value += clamp(angle - 1.0,0.0,1.0) * 1.2; 225 | 226 | return value; 227 | } 228 | 229 | void main() 230 | { 231 | vec3 new_position = get_position(position); 232 | 233 | v_position = position; 234 | v_intensity = get_intensity(v_position,0.0); 235 | 236 | gl_Position = projectionMatrix * modelViewMatrix * vec4(new_position,1.0); 237 | } -------------------------------------------------------------------------------- /src/shaders/giant/sun/fragment.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform vec3 uColor; 4 | uniform float uTime; 5 | uniform float uBrightnessAmplifier; 6 | uniform float uNoiseIntensity; 7 | uniform float uNoiseSpeed; 8 | uniform vec3 fogColor; 9 | uniform float fogNear; 10 | uniform float fogFar; 11 | uniform float uColorAmplifierPrimary; 12 | uniform float uColorAmplifierSecondary; 13 | uniform float uColorAmplifierTertiary; 14 | 15 | varying vec2 vUv; 16 | varying vec3 vPosition; 17 | varying vec3 vEyeVector; 18 | 19 | // START - Noise traditional formula 20 | //--------------------------------------------------------------------------- 21 | //--------------------------------------------------------------------------- 22 | //--------------------------------------------------------------------------- 23 | // Description : Array and textureless GLSL 2D/3D/4D simplex 24 | // noise functions. 25 | // Author : Ian McEwan, Ashima Arts. 26 | // Maintainer : ijm 27 | // Lastmod : 20110822 (ijm) 28 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 29 | // Distributed under the MIT License. See LICENSE file. 30 | // https://github.com/ashima/webgl-noise 31 | // 32 | vec4 mod289(vec4 x) { 33 | return x - floor(x * (1.0 / 289.0)) * 289.0; 34 | } 35 | 36 | float mod289(float x) { 37 | return x - floor(x * (1.0 / 289.0)) * 289.0; 38 | } 39 | 40 | vec4 permute(vec4 x) { 41 | return mod289(((x*34.0)+1.0)*x); 42 | } 43 | 44 | float permute(float x) { 45 | return mod289(((x*34.0)+1.0)*x); 46 | } 47 | 48 | vec4 taylorInvSqrt(vec4 r) 49 | { 50 | return 1.79284291400159 - 0.85373472095314 * r; 51 | } 52 | 53 | float taylorInvSqrt(float r) 54 | { 55 | return 1.79284291400159 - 0.85373472095314 * r; 56 | } 57 | 58 | vec4 grad4(float j, vec4 ip) 59 | { 60 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0); 61 | vec4 p,s; 62 | 63 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; 64 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz); 65 | s = vec4(lessThan(p, vec4(0.0))); 66 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; 67 | 68 | return p; 69 | } 70 | 71 | // (sqrt(5) - 1)/4 = F4, used once below 72 | #define F4 0.309016994374947451 73 | 74 | float snoise(vec4 v) 75 | { 76 | const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4 77 | 0.276393202250021, // 2 * G4 78 | 0.414589803375032, // 3 * G4 79 | -0.447213595499958); // -1 + 4 * G4 80 | 81 | // First corner 82 | vec4 i = floor(v + dot(v, vec4(F4)) ); 83 | vec4 x0 = v - i + dot(i, C.xxxx); 84 | 85 | // Other corners 86 | 87 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) 88 | vec4 i0; 89 | vec3 isX = step( x0.yzw, x0.xxx ); 90 | vec3 isYZ = step( x0.zww, x0.yyz ); 91 | // i0.x = dot( isX, vec3( 1.0 ) ); 92 | i0.x = isX.x + isX.y + isX.z; 93 | i0.yzw = 1.0 - isX; 94 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); 95 | i0.y += isYZ.x + isYZ.y; 96 | i0.zw += 1.0 - isYZ.xy; 97 | i0.z += isYZ.z; 98 | i0.w += 1.0 - isYZ.z; 99 | 100 | // i0 now contains the unique values 0,1,2,3 in each channel 101 | vec4 i3 = clamp( i0, 0.0, 1.0 ); 102 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 ); 103 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 ); 104 | 105 | // x0 = x0 - 0.0 + 0.0 * C.xxxx 106 | // x1 = x0 - i1 + 1.0 * C.xxxx 107 | // x2 = x0 - i2 + 2.0 * C.xxxx 108 | // x3 = x0 - i3 + 3.0 * C.xxxx 109 | // x4 = x0 - 1.0 + 4.0 * C.xxxx 110 | vec4 x1 = x0 - i1 + C.xxxx; 111 | vec4 x2 = x0 - i2 + C.yyyy; 112 | vec4 x3 = x0 - i3 + C.zzzz; 113 | vec4 x4 = x0 + C.wwww; 114 | 115 | // Permutations 116 | i = mod289(i); 117 | float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); 118 | vec4 j1 = permute( permute( permute( permute ( 119 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) 120 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) 121 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) 122 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); 123 | 124 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope 125 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289. 126 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; 127 | 128 | vec4 p0 = grad4(j0, ip); 129 | vec4 p1 = grad4(j1.x, ip); 130 | vec4 p2 = grad4(j1.y, ip); 131 | vec4 p3 = grad4(j1.z, ip); 132 | vec4 p4 = grad4(j1.w, ip); 133 | 134 | // Normalise gradients 135 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 136 | p0 *= norm.x; 137 | p1 *= norm.y; 138 | p2 *= norm.z; 139 | p3 *= norm.w; 140 | p4 *= taylorInvSqrt(dot(p4,p4)); 141 | 142 | // Mix contributions from the five corners 143 | vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); 144 | vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); 145 | m0 = m0 * m0; 146 | m1 = m1 * m1; 147 | return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) 148 | + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; 149 | } 150 | //--------------------------------------------------------------------------- 151 | //--------------------------------------------------------------------------- 152 | //--------------------------------------------------------------------------- 153 | // END - Noise traditional formula 154 | 155 | // creating octave (layer) of noise 156 | float noiseOctave(vec4 layer) 157 | { 158 | float noiseOctaveOffset = 500.0; 159 | float noiseAmplitude = 1.0; 160 | float noiseScale = 1.0; 161 | 162 | float sum = 0.0; 163 | 164 | for(int i=0; i<6; i++) 165 | { 166 | // adding layers of noise on each other 167 | // with different scale and amplitude 168 | sum += snoise(layer * noiseScale) * noiseAmplitude; 169 | 170 | // tuning down amplitude 171 | noiseAmplitude *= 0.8; 172 | 173 | // tuning up scale 174 | noiseScale *= 2.0; 175 | 176 | // offseting each layer by the time 177 | layer.w += noiseOctaveOffset; 178 | } 179 | 180 | return sum; 181 | } 182 | 183 | // convert perceptible brightness to sun color 184 | vec3 convertBrightToColor(float brightness) 185 | { 186 | float brightnessAmplifier = uBrightnessAmplifier; 187 | 188 | brightness *= brightnessAmplifier; 189 | 190 | // add uniform for color pow here to change color on diff universes 191 | return vec3(pow(brightness, uColorAmplifierPrimary), pow(brightness, uColorAmplifierSecondary), pow(brightness, uColorAmplifierTertiary)) * 0.6; 192 | } 193 | 194 | // calucate the fresnel of the object 195 | float fresnel(vec3 eyeVector, vec3 worldNormal) 196 | { 197 | return pow(1.0 + dot(eyeVector, worldNormal), 3.0); 198 | } 199 | 200 | void main() 201 | { 202 | float noiseIntensity = uNoiseIntensity; 203 | float noiseSpeed = uNoiseSpeed; 204 | float fresnelAmplifier = 1.0; 205 | 206 | // base layer of noise 207 | vec4 baseLayer = vec4(vPosition * noiseIntensity, uTime * noiseSpeed); 208 | 209 | // build octave of noises 210 | float noises = noiseOctave(baseLayer); 211 | 212 | // getting big region of noise by subtracting negative values 213 | float regionNoises = max(snoise(baseLayer), 0.0); 214 | 215 | // interpolate value of region of noises to smooth transition 216 | float smoothRegionNoises = mix(1.0, regionNoises, 0.7); 217 | 218 | // unify layers of noises and smoothed region 219 | float smoothedLayersOfNoises = noises * smoothRegionNoises; 220 | 221 | // add the fresnel to the shape 222 | smoothedLayersOfNoises += fresnel(vEyeVector, vPosition) * fresnelAmplifier; 223 | 224 | // convert region of noises in sun color 225 | vec3 smoothedLayersOfNoisesColored = convertBrightToColor(smoothedLayersOfNoises * 4.0 + 1.0); 226 | 227 | gl_FragColor = vec4(smoothedLayersOfNoisesColored, 1.0); 228 | 229 | // handling the fog effect 230 | float depth = gl_FragCoord.z / gl_FragCoord.w; 231 | float fogFactor = smoothstep( fogNear, fogFar, depth ); 232 | gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); 233 | } -------------------------------------------------------------------------------- /src/shaders/giant/sun/vertex.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vUv; 4 | varying vec3 vPosition; 5 | varying vec3 vEyeVector; 6 | 7 | void main() 8 | { 9 | vec4 modelPosition = modelMatrix * vec4(position, 1.0); 10 | 11 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 12 | 13 | vUv = uv; 14 | vPosition = position; 15 | vEyeVector = normalize(modelPosition.xyz - cameraPosition); 16 | } -------------------------------------------------------------------------------- /src/shaders/giant/whitedwarf/fragment.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform vec3 uColor; 4 | uniform float uTime; 5 | uniform float uBrightnessAmplifier; 6 | uniform float uNoiseIntensity; 7 | uniform float uNoiseSpeed; 8 | uniform vec3 fogColor; 9 | uniform float fogNear; 10 | uniform float fogFar; 11 | 12 | varying vec2 vUv; 13 | varying vec3 vPosition; 14 | varying vec3 vEyeVector; 15 | 16 | // START - Noise traditional formula 17 | //--------------------------------------------------------------------------- 18 | //--------------------------------------------------------------------------- 19 | //--------------------------------------------------------------------------- 20 | // Description : Array and textureless GLSL 2D/3D/4D simplex 21 | // noise functions. 22 | // Author : Ian McEwan, Ashima Arts. 23 | // Maintainer : ijm 24 | // Lastmod : 20110822 (ijm) 25 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 26 | // Distributed under the MIT License. See LICENSE file. 27 | // https://github.com/ashima/webgl-noise 28 | // 29 | vec4 mod289(vec4 x) { 30 | return x - floor(x * (1.0 / 289.0)) * 289.0; 31 | } 32 | 33 | float mod289(float x) { 34 | return x - floor(x * (1.0 / 289.0)) * 289.0; 35 | } 36 | 37 | vec4 permute(vec4 x) { 38 | return mod289(((x*34.0)+1.0)*x); 39 | } 40 | 41 | float permute(float x) { 42 | return mod289(((x*34.0)+1.0)*x); 43 | } 44 | 45 | vec4 taylorInvSqrt(vec4 r) 46 | { 47 | return 1.79284291400159 - 0.85373472095314 * r; 48 | } 49 | 50 | float taylorInvSqrt(float r) 51 | { 52 | return 1.79284291400159 - 0.85373472095314 * r; 53 | } 54 | 55 | vec4 grad4(float j, vec4 ip) 56 | { 57 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0); 58 | vec4 p,s; 59 | 60 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; 61 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz); 62 | s = vec4(lessThan(p, vec4(0.0))); 63 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; 64 | 65 | return p; 66 | } 67 | 68 | // (sqrt(5) - 1)/4 = F4, used once below 69 | #define F4 0.309016994374947451 70 | 71 | float snoise(vec4 v) 72 | { 73 | const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4 74 | 0.276393202250021, // 2 * G4 75 | 0.414589803375032, // 3 * G4 76 | -0.447213595499958); // -1 + 4 * G4 77 | 78 | // First corner 79 | vec4 i = floor(v + dot(v, vec4(F4)) ); 80 | vec4 x0 = v - i + dot(i, C.xxxx); 81 | 82 | // Other corners 83 | 84 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) 85 | vec4 i0; 86 | vec3 isX = step( x0.yzw, x0.xxx ); 87 | vec3 isYZ = step( x0.zww, x0.yyz ); 88 | // i0.x = dot( isX, vec3( 1.0 ) ); 89 | i0.x = isX.x + isX.y + isX.z; 90 | i0.yzw = 1.0 - isX; 91 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); 92 | i0.y += isYZ.x + isYZ.y; 93 | i0.zw += 1.0 - isYZ.xy; 94 | i0.z += isYZ.z; 95 | i0.w += 1.0 - isYZ.z; 96 | 97 | // i0 now contains the unique values 0,1,2,3 in each channel 98 | vec4 i3 = clamp( i0, 0.0, 1.0 ); 99 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 ); 100 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 ); 101 | 102 | // x0 = x0 - 0.0 + 0.0 * C.xxxx 103 | // x1 = x0 - i1 + 1.0 * C.xxxx 104 | // x2 = x0 - i2 + 2.0 * C.xxxx 105 | // x3 = x0 - i3 + 3.0 * C.xxxx 106 | // x4 = x0 - 1.0 + 4.0 * C.xxxx 107 | vec4 x1 = x0 - i1 + C.xxxx; 108 | vec4 x2 = x0 - i2 + C.yyyy; 109 | vec4 x3 = x0 - i3 + C.zzzz; 110 | vec4 x4 = x0 + C.wwww; 111 | 112 | // Permutations 113 | i = mod289(i); 114 | float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); 115 | vec4 j1 = permute( permute( permute( permute ( 116 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) 117 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) 118 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) 119 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); 120 | 121 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope 122 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289. 123 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; 124 | 125 | vec4 p0 = grad4(j0, ip); 126 | vec4 p1 = grad4(j1.x, ip); 127 | vec4 p2 = grad4(j1.y, ip); 128 | vec4 p3 = grad4(j1.z, ip); 129 | vec4 p4 = grad4(j1.w, ip); 130 | 131 | // Normalise gradients 132 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 133 | p0 *= norm.x; 134 | p1 *= norm.y; 135 | p2 *= norm.z; 136 | p3 *= norm.w; 137 | p4 *= taylorInvSqrt(dot(p4,p4)); 138 | 139 | // Mix contributions from the five corners 140 | vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); 141 | vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); 142 | m0 = m0 * m0; 143 | m1 = m1 * m1; 144 | return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) 145 | + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; 146 | } 147 | //--------------------------------------------------------------------------- 148 | //--------------------------------------------------------------------------- 149 | //--------------------------------------------------------------------------- 150 | // END - Noise traditional formula 151 | 152 | // creating octave (layer) of noise 153 | float noiseOctave(vec4 layer) 154 | { 155 | float noiseOctaveOffset = 200.0; 156 | float noiseAmplitude = 6.0; 157 | float noiseScale = 4.0; 158 | 159 | float sum = 0.0; 160 | 161 | for(int i=0; i<6; i++) 162 | { 163 | // adding layers of noise on each other 164 | // with different scale and amplitude 165 | sum += snoise(layer * noiseScale) * noiseAmplitude; 166 | 167 | // tuning down amplitude 168 | noiseAmplitude *= 0.8; 169 | 170 | // tuning up scale 171 | noiseScale *= 2.0; 172 | 173 | // offseting each layer by the time 174 | layer.w += noiseOctaveOffset; 175 | } 176 | 177 | return sum; 178 | } 179 | 180 | // convert to predetermined color 181 | vec3 convertToColor(float brightness) 182 | { 183 | float brightnessAmplifier = 0.15; 184 | 185 | brightness *= brightnessAmplifier; 186 | 187 | // add uniform for color pow here to change color on diff universes 188 | // blue/black and white colors 189 | return vec3( 190 | brightness*brightness*brightness*brightness*brightness*brightness*brightness*brightness*brightness, 191 | brightness*brightness*brightness*brightness*brightness*brightness*brightness*brightness*brightness, 192 | brightness*brightness 193 | ) * 1.2; 194 | } 195 | 196 | // calucate the fresnel of the object 197 | float fresnel(vec3 eyeVector, vec3 worldNormal) 198 | { 199 | return pow(1.0 + dot(eyeVector, worldNormal), 3.0); 200 | } 201 | 202 | void main() 203 | { 204 | float noiseIntensity = uNoiseIntensity; 205 | float noiseSpeed = uNoiseSpeed; 206 | float fresnelAmplifier = 10.0; 207 | 208 | // base layer of noise 209 | vec4 baseLayer = vec4(vPosition * noiseIntensity, uTime * noiseSpeed); 210 | 211 | // build octave of noises 212 | float noises = noiseOctave(baseLayer); 213 | 214 | // add the fresnel to the shape 215 | noises += fresnel(vEyeVector, vPosition) * fresnelAmplifier; 216 | 217 | // convert region of noises in sun color 218 | vec3 noisesColored = convertToColor(noises * 1.0 + 0.2); 219 | 220 | gl_FragColor = vec4(noisesColored, 1.0); 221 | 222 | // handling the fog effect 223 | float depth = gl_FragCoord.z / gl_FragCoord.w; 224 | float fogFactor = smoothstep( fogNear, fogFar, depth ); 225 | gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); 226 | } -------------------------------------------------------------------------------- /src/shaders/giant/whitedwarf/vertex.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vUv; 4 | varying vec3 vPosition; 5 | varying vec3 vEyeVector; 6 | 7 | void main() 8 | { 9 | vec4 modelPosition = modelMatrix * vec4(position, 1.0); 10 | 11 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 12 | 13 | vUv = uv; 14 | vPosition = position; 15 | vEyeVector = normalize(modelPosition.xyz - cameraPosition); 16 | } -------------------------------------------------------------------------------- /src/shaders/singularity/blackhole/fragment.glsl: -------------------------------------------------------------------------------- 1 | uniform vec3 uColor; 2 | uniform float uTime; 3 | uniform sampler2D uTexture; 4 | uniform vec3 fogColor; 5 | uniform float fogNear; 6 | uniform float fogFar; 7 | 8 | varying vec2 vUv; 9 | varying vec3 vPosition; 10 | varying vec3 vEyeVector; 11 | 12 | // START - Noise traditional formula 13 | //--------------------------------------------------------------------------- 14 | //--------------------------------------------------------------------------- 15 | //--------------------------------------------------------------------------- 16 | // Description : Array and textureless GLSL 2D/3D/4D simplex 17 | // noise functions. 18 | // Author : Ian McEwan, Ashima Arts. 19 | // Maintainer : ijm 20 | // Lastmod : 20110822 (ijm) 21 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 22 | // Distributed under the MIT License. See LICENSE file. 23 | // https://github.com/ashima/webgl-noise 24 | // 25 | vec4 mod289(vec4 x) { 26 | return x - floor(x * (1.0 / 289.0)) * 289.0; 27 | } 28 | 29 | float mod289(float x) { 30 | return x - floor(x * (1.0 / 289.0)) * 289.0; 31 | } 32 | 33 | vec4 permute(vec4 x) { 34 | return mod289(((x*34.0)+1.0)*x); 35 | } 36 | 37 | float permute(float x) { 38 | return mod289(((x*34.0)+1.0)*x); 39 | } 40 | 41 | vec4 taylorInvSqrt(vec4 r) 42 | { 43 | return 1.79284291400159 - 0.85373472095314 * r; 44 | } 45 | 46 | float taylorInvSqrt(float r) 47 | { 48 | return 1.79284291400159 - 0.85373472095314 * r; 49 | } 50 | 51 | vec4 grad4(float j, vec4 ip) 52 | { 53 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0); 54 | vec4 p,s; 55 | 56 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; 57 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz); 58 | s = vec4(lessThan(p, vec4(0.0))); 59 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; 60 | 61 | return p; 62 | } 63 | 64 | // (sqrt(5) - 1)/4 = F4, used once below 65 | #define F4 0.309016994374947451 66 | 67 | float snoise(vec4 v) 68 | { 69 | const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4 70 | 0.276393202250021, // 2 * G4 71 | 0.414589803375032, // 3 * G4 72 | -0.447213595499958); // -1 + 4 * G4 73 | 74 | // First corner 75 | vec4 i = floor(v + dot(v, vec4(F4)) ); 76 | vec4 x0 = v - i + dot(i, C.xxxx); 77 | 78 | // Other corners 79 | 80 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) 81 | vec4 i0; 82 | vec3 isX = step( x0.yzw, x0.xxx ); 83 | vec3 isYZ = step( x0.zww, x0.yyz ); 84 | // i0.x = dot( isX, vec3( 1.0 ) ); 85 | i0.x = isX.x + isX.y + isX.z; 86 | i0.yzw = 1.0 - isX; 87 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); 88 | i0.y += isYZ.x + isYZ.y; 89 | i0.zw += 1.0 - isYZ.xy; 90 | i0.z += isYZ.z; 91 | i0.w += 1.0 - isYZ.z; 92 | 93 | // i0 now contains the unique values 0,1,2,3 in each channel 94 | vec4 i3 = clamp( i0, 0.0, 1.0 ); 95 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 ); 96 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 ); 97 | 98 | // x0 = x0 - 0.0 + 0.0 * C.xxxx 99 | // x1 = x0 - i1 + 1.0 * C.xxxx 100 | // x2 = x0 - i2 + 2.0 * C.xxxx 101 | // x3 = x0 - i3 + 3.0 * C.xxxx 102 | // x4 = x0 - 1.0 + 4.0 * C.xxxx 103 | vec4 x1 = x0 - i1 + C.xxxx; 104 | vec4 x2 = x0 - i2 + C.yyyy; 105 | vec4 x3 = x0 - i3 + C.zzzz; 106 | vec4 x4 = x0 + C.wwww; 107 | 108 | // Permutations 109 | i = mod289(i); 110 | float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); 111 | vec4 j1 = permute( permute( permute( permute ( 112 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) 113 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) 114 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) 115 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); 116 | 117 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope 118 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289. 119 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; 120 | 121 | vec4 p0 = grad4(j0, ip); 122 | vec4 p1 = grad4(j1.x, ip); 123 | vec4 p2 = grad4(j1.y, ip); 124 | vec4 p3 = grad4(j1.z, ip); 125 | vec4 p4 = grad4(j1.w, ip); 126 | 127 | // Normalise gradients 128 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 129 | p0 *= norm.x; 130 | p1 *= norm.y; 131 | p2 *= norm.z; 132 | p3 *= norm.w; 133 | p4 *= taylorInvSqrt(dot(p4,p4)); 134 | 135 | // Mix contributions from the five corners 136 | vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); 137 | vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); 138 | m0 = m0 * m0; 139 | m1 = m1 * m1; 140 | return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) 141 | + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; 142 | } 143 | //--------------------------------------------------------------------------- 144 | //--------------------------------------------------------------------------- 145 | //--------------------------------------------------------------------------- 146 | // END - Noise traditional formula 147 | 148 | // creating octave (layer) of noise 149 | float noiseOctave(vec4 layer) 150 | { 151 | float noiseOctaveOffset = abs(sin(uTime) * 0.2) + 4.5; 152 | float noiseAmplitude = 5.0; 153 | float noiseScale = 2.0; 154 | 155 | float sum = 0.0; 156 | 157 | for(int i=0; i<2; i++) 158 | { 159 | // adding layers of noise on each other 160 | // with different scale and amplitude 161 | sum += snoise(layer * noiseScale) * noiseAmplitude; 162 | 163 | // tuning down amplitude 164 | noiseAmplitude *= 0.8; 165 | 166 | // tuning up scale 167 | noiseScale *= 2.0; 168 | 169 | // offseting each layer by the time 170 | layer.w *= noiseOctaveOffset; 171 | } 172 | 173 | return sum; 174 | } 175 | 176 | // convert to predetermined color 177 | vec3 convertToColor(float brightness) 178 | { 179 | float brightnessAmplifier = 0.40; 180 | 181 | brightness *= brightnessAmplifier; 182 | 183 | // add uniform for color pow here to change color on diff universes 184 | // black and white colors 185 | return vec3( 186 | brightness*brightness*brightness*brightness*brightness*brightness, 187 | brightness*brightness*brightness*brightness*brightness*brightness, 188 | brightness*brightness*brightness*brightness*brightness*brightness 189 | ) * 1.2; 190 | } 191 | 192 | // calucate the fresnel of the object 193 | float fresnel(vec3 eyeVector, vec3 worldNormal) 194 | { 195 | return pow(1.0 + dot(eyeVector, worldNormal), 3.0); 196 | } 197 | 198 | void main() 199 | { 200 | // get texture data from texture 201 | vec4 texture = texture2D(uTexture, vUv); 202 | 203 | // noises 204 | float textureNoises = noiseOctave(texture); 205 | float regionNoises = max(snoise(texture), 0.0); 206 | float smoothRegionNoises = mix(1.0, regionNoises, 0.7); 207 | float smoothedLayersOfNoises = textureNoises * smoothRegionNoises; 208 | 209 | // add the fresnel to the shape 210 | smoothedLayersOfNoises += fresnel(vEyeVector, vPosition) * 1.2; 211 | 212 | vec3 textureNoisesColored = convertToColor(smoothedLayersOfNoises); 213 | 214 | gl_FragColor = vec4(textureNoisesColored, 1.0); 215 | 216 | // handling the fog effect 217 | float depth = gl_FragCoord.z / gl_FragCoord.w; 218 | float fogFactor = smoothstep( fogNear, fogFar, depth ); 219 | gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); 220 | } -------------------------------------------------------------------------------- /src/shaders/singularity/blackhole/vertex.glsl: -------------------------------------------------------------------------------- 1 | uniform float uTime; 2 | 3 | varying vec2 vUv; 4 | varying vec3 vPosition; 5 | varying vec3 vEyeVector; 6 | 7 | void main() 8 | { 9 | vec4 modelPosition = modelMatrix * vec4(position, 1.0); 10 | 11 | // clip space coordinates of the camera related vertices 12 | vec4 viewPosition = viewMatrix * modelPosition; 13 | 14 | // clip space coordinates of the final projetction render 15 | vec4 projectedPosition = projectionMatrix * viewPosition; 16 | 17 | gl_Position = projectedPosition; 18 | 19 | vUv = uv; 20 | } -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/.gitkeep -------------------------------------------------------------------------------- /static/audio/borealis.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/audio/borealis.mp3 -------------------------------------------------------------------------------- /static/audio/celestial.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/audio/celestial.mp3 -------------------------------------------------------------------------------- /static/audio/discovery.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/audio/discovery.mp3 -------------------------------------------------------------------------------- /static/audio/ghosts.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/audio/ghosts.mp3 -------------------------------------------------------------------------------- /static/audio/intothenight.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/audio/intothenight.mp3 -------------------------------------------------------------------------------- /static/audio/omega.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/audio/omega.mp3 -------------------------------------------------------------------------------- /static/audio/transcendent.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/audio/transcendent.mp3 -------------------------------------------------------------------------------- /static/audio/wormhole.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/audio/wormhole.mp3 -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/favicon.ico -------------------------------------------------------------------------------- /static/models/player/sphere.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/models/player/sphere.glb -------------------------------------------------------------------------------- /static/models/spaceship/normandy/normandy.fbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/models/spaceship/normandy/normandy.fbx -------------------------------------------------------------------------------- /static/models/spaceship/station/station.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/models/spaceship/station/station.glb -------------------------------------------------------------------------------- /static/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/preview.jpg -------------------------------------------------------------------------------- /static/textures/blackhole/disk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/blackhole/disk.jpg -------------------------------------------------------------------------------- /static/textures/nebula/cloud1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/nebula/cloud1.png -------------------------------------------------------------------------------- /static/textures/nebula/cloud2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/nebula/cloud2.png -------------------------------------------------------------------------------- /static/textures/nebula/cloud3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/nebula/cloud3.png -------------------------------------------------------------------------------- /static/textures/nebula/cloud4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/nebula/cloud4.png -------------------------------------------------------------------------------- /static/textures/nebula/cloud5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/nebula/cloud5.png -------------------------------------------------------------------------------- /static/textures/nebula/cloud7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/nebula/cloud7.png -------------------------------------------------------------------------------- /static/textures/nebula/cloud8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/nebula/cloud8.png -------------------------------------------------------------------------------- /static/textures/nebula/cloud9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/nebula/cloud9.png -------------------------------------------------------------------------------- /static/textures/nebula/test.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/nebula/test.jpeg -------------------------------------------------------------------------------- /static/textures/player/sphere_AO.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/player/sphere_AO.jpg -------------------------------------------------------------------------------- /static/textures/player/sphere_albedo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/player/sphere_albedo.jpg -------------------------------------------------------------------------------- /static/textures/player/sphere_emissive.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/player/sphere_emissive.jpg -------------------------------------------------------------------------------- /static/textures/player/sphere_metallic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/player/sphere_metallic.jpg -------------------------------------------------------------------------------- /static/textures/player/sphere_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/player/sphere_normal.png -------------------------------------------------------------------------------- /static/textures/player/sphere_opacity.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/player/sphere_opacity.jpg -------------------------------------------------------------------------------- /static/textures/player/sphere_roughness.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/player/sphere_roughness.jpg -------------------------------------------------------------------------------- /static/textures/spaceship/normandy/decals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/spaceship/normandy/decals.png -------------------------------------------------------------------------------- /static/textures/spaceship/normandy/ingame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/spaceship/normandy/ingame.png -------------------------------------------------------------------------------- /static/textures/spaceship/normandy/ingamei.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/spaceship/normandy/ingamei.png -------------------------------------------------------------------------------- /static/textures/spaceship/station/ao.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/spaceship/station/ao.jpg -------------------------------------------------------------------------------- /static/textures/spaceship/station/basecolor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/spaceship/station/basecolor.jpg -------------------------------------------------------------------------------- /static/textures/spaceship/station/diffuse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/spaceship/station/diffuse.jpg -------------------------------------------------------------------------------- /static/textures/spaceship/station/height.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/spaceship/station/height.jpg -------------------------------------------------------------------------------- /static/textures/spaceship/station/normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/spaceship/station/normal.jpg -------------------------------------------------------------------------------- /static/textures/spaceship/station/opacity.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/spaceship/station/opacity.jpg -------------------------------------------------------------------------------- /static/textures/spaceship/station/reflection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/spaceship/station/reflection.jpg -------------------------------------------------------------------------------- /static/textures/spaceship/station/specular.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/spaceship/station/specular.jpg -------------------------------------------------------------------------------- /static/textures/starfield/brightstar1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/brightstar1.png -------------------------------------------------------------------------------- /static/textures/starfield/brightstar2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/brightstar2.png -------------------------------------------------------------------------------- /static/textures/starfield/brightstar3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/brightstar3.png -------------------------------------------------------------------------------- /static/textures/starfield/brightstar4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/brightstar4.png -------------------------------------------------------------------------------- /static/textures/starfield/star1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/star1.png -------------------------------------------------------------------------------- /static/textures/starfield/star10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/star10.png -------------------------------------------------------------------------------- /static/textures/starfield/star11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/star11.png -------------------------------------------------------------------------------- /static/textures/starfield/star2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/star2.png -------------------------------------------------------------------------------- /static/textures/starfield/star3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/star3.png -------------------------------------------------------------------------------- /static/textures/starfield/star4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/star4.png -------------------------------------------------------------------------------- /static/textures/starfield/star5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/star5.png -------------------------------------------------------------------------------- /static/textures/starfield/star6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/star6.png -------------------------------------------------------------------------------- /static/textures/starfield/star7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/star7.png -------------------------------------------------------------------------------- /static/textures/starfield/star8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/star8.png -------------------------------------------------------------------------------- /static/textures/starfield/star9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/starfield/star9.png -------------------------------------------------------------------------------- /static/textures/universe/universe1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/universe/universe1.png -------------------------------------------------------------------------------- /static/textures/wormhole/galaxy1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/wormhole/galaxy1.jpg -------------------------------------------------------------------------------- /static/textures/wormhole/galaxy2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/wormhole/galaxy2.jpg -------------------------------------------------------------------------------- /static/textures/wormhole/galaxy3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/wormhole/galaxy3.jpg -------------------------------------------------------------------------------- /static/textures/wormhole/galaxy4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/wormhole/galaxy4.jpg -------------------------------------------------------------------------------- /static/textures/wormhole/galaxy5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/textures/wormhole/galaxy5.jpeg -------------------------------------------------------------------------------- /static/ui/credits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/ui/credits.png -------------------------------------------------------------------------------- /static/ui/esc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/ui/esc.png -------------------------------------------------------------------------------- /static/ui/headphone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | B6CD5A83-5FB5-4F5D-B7E5-47D9233BD158 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /static/ui/hide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/ui/hide.png -------------------------------------------------------------------------------- /static/ui/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/ui/logo.png -------------------------------------------------------------------------------- /static/ui/look.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/ui/look.png -------------------------------------------------------------------------------- /static/ui/move.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/ui/move.png -------------------------------------------------------------------------------- /static/ui/mute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/ui/mute.png -------------------------------------------------------------------------------- /static/ui/nav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/ui/nav.png -------------------------------------------------------------------------------- /static/ui/skip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesuisundev/acrossthemultiverse/3f36118a679ebd1189510109b41dca00dd2c9d08/static/ui/skip.png --------------------------------------------------------------------------------