import * as THREE from 'three' import * as dat from 'dat.gui' import Sizes from './Utils/Sizes.js' import Time from './Utils/Time.js' import World from './World/index.js' import Resources from './Resources.js' import Camera from './Camera.js' import ThreejsJourney from './ThreejsJourney.js' import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js' import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js' import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js' import BlurPass from './Passes/Blur.js' import GlowsPass from './Passes/Glows.js' export default class Application { /** * Constructor */ constructor(_options) { // Options this.$canvas = _options.$canvas // Set up this.time = new Time() this.sizes = new Sizes() this.resources = new Resources() this.setConfig() this.setDebug() this.setRenderer() this.setCamera() this.setPasses() this.setWorld() this.setTitle() this.setThreejsJourney() } /** * Set config */ setConfig() { this.config = {} this.config.debug = window.location.hash === '#debug' this.config.cyberTruck = window.location.hash === '#cybertruck' this.config.touch = false window.addEventListener('touchstart', () => { this.config.touch = true this.world.controls.setTouch() this.passes.horizontalBlurPass.strength = 1 this.passes.horizontalBlurPass.material.uniforms.uStrength.value = new THREE.Vector2(this.passes.horizontalBlurPass.strength, 0) this.passes.verticalBlurPass.strength = 1 this.passes.verticalBlurPass.material.uniforms.uStrength.value = new THREE.Vector2(0, this.passes.verticalBlurPass.strength) }, { once: true }) } /** * Set debug */ setDebug() { if(this.config.debug) { this.debug = new dat.GUI({ width: 420 }) } } /** * Set renderer */ setRenderer() { // Scene this.scene = new THREE.Scene() // Renderer this.renderer = new THREE.WebGLRenderer({ canvas: this.$canvas, alpha: true }) // this.renderer.setClearColor(0x414141, 1) this.renderer.setClearColor(0x000000, 1) // this.renderer.setPixelRatio(Math.min(Math.max(window.devicePixelRatio, 1.5), 2)) this.renderer.setPixelRatio(2) this.renderer.setSize(this.sizes.viewport.width, this.sizes.viewport.height) this.renderer.physicallyCorrectLights = true this.renderer.gammaFactor = 2.2 this.renderer.gammaOutPut = true this.renderer.autoClear = false // Resize event this.sizes.on('resize', () => { this.renderer.setSize(this.sizes.viewport.width, this.sizes.viewport.height) }) } /** * Set camera */ setCamera() { this.camera = new Camera({ time: this.time, sizes: this.sizes, renderer: this.renderer, debug: this.debug, config: this.config }) this.scene.add(this.camera.container) this.time.on('tick', () => { if(this.world && this.world.car) { this.camera.target.x = this.world.car.chassis.object.position.x this.camera.target.y = this.world.car.chassis.object.position.y } }) } setPasses() { this.passes = {} // Debug if(this.debug) { this.passes.debugFolder = this.debug.addFolder('postprocess') // this.passes.debugFolder.open() } this.passes.composer = new EffectComposer(this.renderer) // Create passes this.passes.renderPass = new RenderPass(this.scene, this.camera.instance) this.passes.horizontalBlurPass = new ShaderPass(BlurPass) this.passes.horizontalBlurPass.strength = this.config.touch ? 0 : 1 this.passes.horizontalBlurPass.material.uniforms.uResolution.value = new THREE.Vector2(this.sizes.viewport.width, this.sizes.viewport.height) this.passes.horizontalBlurPass.material.uniforms.uStrength.value = new THREE.Vector2(this.passes.horizontalBlurPass.strength, 0) this.passes.verticalBlurPass = new ShaderPass(BlurPass) this.passes.verticalBlurPass.strength = this.config.touch ? 0 : 1 this.passes.verticalBlurPass.material.uniforms.uResolution.value = new THREE.Vector2(this.sizes.viewport.width, this.sizes.viewport.height) this.passes.verticalBlurPass.material.uniforms.uStrength.value = new THREE.Vector2(0, this.passes.verticalBlurPass.strength) // Debug if(this.debug) { const folder = this.passes.debugFolder.addFolder('blur') folder.open() folder.add(this.passes.horizontalBlurPass.material.uniforms.uStrength.value, 'x').step(0.001).min(0).max(10) folder.add(this.passes.verticalBlurPass.material.uniforms.uStrength.value, 'y').step(0.001).min(0).max(10) } this.passes.glowsPass = new ShaderPass(GlowsPass) this.passes.glowsPass.color = '#ffcfe0' this.passes.glowsPass.material.uniforms.uPosition.value = new THREE.Vector2(0, 0.25) this.passes.glowsPass.material.uniforms.uRadius.value = 0.7 this.passes.glowsPass.material.uniforms.uColor.value = new THREE.Color(this.passes.glowsPass.color) this.passes.glowsPass.material.uniforms.uAlpha.value = 0.55 // Debug if(this.debug) { const folder = this.passes.debugFolder.addFolder('glows') folder.open() folder.add(this.passes.glowsPass.material.uniforms.uPosition.value, 'x').step(0.001).min(- 1).max(2).name('positionX') folder.add(this.passes.glowsPass.material.uniforms.uPosition.value, 'y').step(0.001).min(- 1).max(2).name('positionY') folder.add(this.passes.glowsPass.material.uniforms.uRadius, 'value').step(0.001).min(0).max(2).name('radius') folder.addColor(this.passes.glowsPass, 'color').name('color').onChange(() => { this.passes.glowsPass.material.uniforms.uColor.value = new THREE.Color(this.passes.glowsPass.color) }) folder.add(this.passes.glowsPass.material.uniforms.uAlpha, 'value').step(0.001).min(0).max(1).name('alpha') } // Add passes this.passes.composer.addPass(this.passes.renderPass) this.passes.composer.addPass(this.passes.horizontalBlurPass) this.passes.composer.addPass(this.passes.verticalBlurPass) this.passes.composer.addPass(this.passes.glowsPass) // Time tick this.time.on('tick', () => { this.passes.horizontalBlurPass.enabled = this.passes.horizontalBlurPass.material.uniforms.uStrength.value.x > 0 this.passes.verticalBlurPass.enabled = this.passes.verticalBlurPass.material.uniforms.uStrength.value.y > 0 // Renderer this.passes.composer.render() // this.renderer.domElement.style.background = 'black' // this.renderer.render(this.scene, this.camera.instance) }) // Resize event this.sizes.on('resize', () => { this.renderer.setSize(this.sizes.viewport.width, this.sizes.viewport.height) this.passes.composer.setSize(this.sizes.viewport.width, this.sizes.viewport.height) this.passes.horizontalBlurPass.material.uniforms.uResolution.value.x = this.sizes.viewport.width this.passes.horizontalBlurPass.material.uniforms.uResolution.value.y = this.sizes.viewport.height this.passes.verticalBlurPass.material.uniforms.uResolution.value.x = this.sizes.viewport.width this.passes.verticalBlurPass.material.uniforms.uResolution.value.y = this.sizes.viewport.height }) } /** * Set world */ setWorld() { this.world = new World({ config: this.config, debug: this.debug, resources: this.resources, time: this.time, sizes: this.sizes, camera: this.camera, renderer: this.renderer, passes: this.passes }) this.scene.add(this.world.container) } /** * Set title */ setTitle() { this.title = {} this.title.frequency = 300 this.title.width = 20 this.title.position = 0 this.title.$element = document.querySelector('title') this.title.absolutePosition = Math.round(this.title.width * 0.25) this.time.on('tick', () => { if(this.world.physics) { this.title.absolutePosition += this.world.physics.car.forwardSpeed if(this.title.absolutePosition < 0) { this.title.absolutePosition = 0 } } }) window.setInterval(() => { this.title.position = Math.round(this.title.absolutePosition % this.title.width) document.title = `${'_'.repeat(this.title.width - this.title.position)}🚗${'_'.repeat(this.title.position)}` }, this.title.frequency) } /** * Set Three.js Journey */ setThreejsJourney() { this.threejsJourney = new ThreejsJourney({ config: this.config, time: this.time, world: this.world }) } /** * Destructor */ destructor() { this.time.off('tick') this.sizes.off('resize') this.camera.orbitControls.dispose() this.renderer.dispose() this.debug.destroy() } }