Spaces:
Running
Running
| import { | |
| Vector3, | |
| Vector4, | |
| Clock | |
| } from 'three' | |
| import Ui from './Ui' | |
| import Player from './Player' | |
| import KeyboardControls from './controls/KeyboardControls' | |
| import MobileDeviceControls from './controls/MobileDeviceControls' | |
| import ControlsManager from './controls/ControlsManager' | |
| import SimulationRenderer from './SimulationRenderer' | |
| import Teleporter from './Teleporter' | |
| class Simulation { | |
| init () { | |
| this.config = { | |
| wormhole: { | |
| position: new Vector3(10, 0.0, -32), | |
| radius: 0.8, | |
| gravityRatio: 0.25 | |
| }, | |
| blackhole: { | |
| position: new Vector3(0.0, -250.0, 250.0), | |
| radius: 12.5, | |
| // Ring definition - xyz is normal going through ring. Its magnitude determines inner radius. | |
| // w component determines outer radius | |
| disk: new Vector4(-12, 12, 6, 150.0), | |
| diskTexture: 'assets/accretion_disk.png' | |
| }, | |
| saturn: { | |
| position: new Vector3(-14, 5, -40), | |
| radius: 8.0, | |
| // Ring definition - xyz is normal going through ring. Its magnitude determines inner radius. | |
| // w component determines outer radius | |
| rings: new Vector4(0, 9.22, 0, 17.1), | |
| texture: 'assets/saturn.jpg', | |
| ringsTexture: 'assets/saturnrings.png', | |
| lightDirection: (new Vector3(-4, 2, 3)).normalize() | |
| }, | |
| planet: { | |
| position: new Vector3(7.6, -188.0, 200), | |
| radius: 0.08, | |
| diffuse: new Vector3(0.58, 0.85, 0.96), | |
| specular: new Vector3(0.1, 0.1, 0.1) | |
| }, | |
| galaxy1: { texture: 'assets/galaxy1.png' }, | |
| galaxy2: { texture: 'assets/galaxy2.png' } | |
| } | |
| this.teleportTargets = [ | |
| { position: new Vector3(10, -307, 454), lookAt: this.config.blackhole.position, galaxy: 1 }, | |
| { position: new Vector3(7.2, -188, 199.6), lookAt: this.config.planet.position, galaxy: 1 }, | |
| { position: new Vector3(12.4, 3.3, -35.1), lookAt: this.config.wormhole.position, galaxy: 1 }, | |
| { position: new Vector3(9.8, -4.6, -3.1), lookAt: this.config.wormhole.position, galaxy: 0 } | |
| ] | |
| this.initPlayer() | |
| this.initTeleporter() | |
| this.initRenderer() | |
| this.initControls() | |
| } | |
| initRenderer () { | |
| this.renderer = new SimulationRenderer(this.config, this.player) | |
| this.renderer.onTexturesLoaded = () => { | |
| Ui.removeLoadingScreen() | |
| this.inited = true | |
| } | |
| this.container = document.getElementById('container') | |
| this.container.appendChild(this.renderer.domElement) | |
| Ui.onPixelSizeChange = pixelSize => { | |
| this.renderer.setPixelSize(pixelSize) | |
| } | |
| window.addEventListener( | |
| 'resize', e => { | |
| this.renderer.setSize(window.innerWidth, window.innerHeight) | |
| }, | |
| false | |
| ) | |
| let pixelSize = Ui.getSelectedPixelSize() | |
| if (!pixelSize) { | |
| pixelSize = this.getSuggestedPixelSize() | |
| Ui.setPixelSize(pixelSize) | |
| } | |
| this.renderer.setSize(window.innerWidth, window.innerHeight, pixelSize) | |
| window.addEventListener('wheel', e => { | |
| e.preventDefault() | |
| const delta = e.delta || (e.deltaX + e.deltaY + e.deltaZ) | |
| if (delta < 0) { | |
| this.renderer.setZoom(this.renderer.zoom * 1.06) | |
| } | |
| else { | |
| this.renderer.setZoom(this.renderer.zoom / 1.06) | |
| } | |
| }, false) | |
| } | |
| initPlayer () { | |
| this.player = new Player() | |
| this.player.lookAt(this.config.wormhole.position) | |
| } | |
| initControls () { | |
| // Add keyboard controls to the player | |
| this.keyboardControls = new KeyboardControls(this.player, this.container) | |
| this.keyboardControls.movementSpeed = 1 | |
| this.keyboardControls.rollSpeed = Math.PI / 3 | |
| this.keyboardControls.autoForward = false | |
| this.keyboardControls.dragToLook = false | |
| // Add mobile device controls (touch + accelerometer) to the player | |
| this.mobileDeviceControls = new MobileDeviceControls(this.player, this.container) | |
| this.controlsManager = new ControlsManager(this.keyboardControls, this.mobileDeviceControls) | |
| this.controlsManager.onChange = device => { | |
| if (device !== 'mobile') { | |
| return | |
| } | |
| // The player will probably not be looking with their device in the right direction, so fix that | |
| requestAnimationFrame(() => { | |
| this.player.object.quaternion.multiply(this.player.eyes.quaternion.clone().inverse()) | |
| }) | |
| } | |
| this.controlsManager.start() | |
| this.player.addController(this.keyboardControls) | |
| this.player.addController(this.mobileDeviceControls) | |
| } | |
| initTeleporter () { | |
| this.teleporter = new Teleporter(this.player) | |
| this.teleportTargets.forEach(target => { | |
| this.teleporter.addTarget(target) | |
| }) | |
| Ui.onTeleportClick = () => { | |
| this.teleporter.teleportNext() | |
| } | |
| document.addEventListener('keydown', e => { | |
| if (e.keyCode === 84) { | |
| e.preventDefault() | |
| this.teleporter.teleportNext() | |
| } | |
| }) | |
| } | |
| step () { | |
| if (this.inited) { | |
| this.update() | |
| } | |
| this.render() | |
| } | |
| start () { | |
| this.clock = new Clock() | |
| const animate = () => { | |
| requestAnimationFrame(animate) | |
| this.step() | |
| } | |
| animate() | |
| } | |
| update () { | |
| const delta = Math.max(0.001, this.clock.getDelta()) | |
| this.player.update(delta) | |
| } | |
| render () { | |
| this.renderer.render() | |
| } | |
| getSuggestedPixelSize () { | |
| const pixelCount = window.innerWidth * window.innerHeight | |
| let pixelSize = 4 | |
| while (pixelSize > 1) { | |
| if (pixelCount / (pixelSize * pixelSize) < 512 * 512) { | |
| pixelSize /= 2 | |
| } | |
| else { | |
| break | |
| } | |
| } | |
| return pixelSize | |
| } | |
| } | |
| export default new Simulation() | |