import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; import './lib/chrome.js'; import { sceneColors } from './lib/theme.js'; import { bindWorld } from './lib/transit.js'; import { bindGlow } from './lib/glow.js'; import { bindCameraMemory } from './lib/cameraMemory.js'; const colors = sceneColors(); // the landing gradient shows through a transparent canvas; all shapes live in // `world`, which page transitions fly in and out const scene = new THREE.Scene(); const world = new THREE.Group(); scene.add( world ); const updateWorld = bindWorld( world, 3 ); const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 500 ); camera.position.z = 12; camera.position.x = 10; camera.position.y = 4; const renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } ); renderer.setPixelRatio( Math.min( window.devicePixelRatio, 2 ) ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1; document.body.appendChild( renderer.domElement ); window.addEventListener( 'resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } ); const controls = new OrbitControls( camera, renderer.domElement ); controls.target.set( -4, 0, 0 ); bindCameraMemory( camera, controls ); const updateGlow = bindGlow( camera ); // const geometry_torus = new THREE.TorusGeometry( 2.5, 2.5, 32, 128 ); // const material_torus = new THREE.MeshBasicMaterial( { color: 0x049ef4, wireframe: true, wireframeLinewidth: 1} ); // const torus = new THREE.Mesh( geometry_torus, material_torus ); // torus.rotation.x = Math.PI / 2; // torus.castShadow = true; // world.add( torus ); // const icoGeometry = new THREE.IcosahedronGeometry( 5 ); // const icoMaterial= new THREE.MeshBasicMaterial( {color: 0x049ef4, wireframe: true, wireframeLinewidth: 8 } ); // const iso = new THREE.Mesh( icoGeometry, icoMaterial ); // iso.castShadow = true; // world.add( iso ); // --------- Mekaraba ---------- // // const tetraGeometry = new THREE.TetrahedronGeometry( 2 ); // const tetraMaterial = new THREE.MeshBasicMaterial( {color: 0xffff00 } ); // const tetra = new THREE.Mesh( tetraGeometry, tetraMaterial ); // // tetra.position.y = - 0.81 // // tetra.rotation.x = Math.PI / 4 ; // tetra.rotation.z = Math.PI / 6 ; // // world.add( tetra ); // const tetraEdges = new THREE.EdgesGeometry( tetraGeometry ); // const tetraEdgesLine = new THREE.LineSegments( tetraEdges, new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 3 } ) ); // // tetraEdgesLine.position.y = -0.81 // tetraEdgesLine.rotation.x = Math.PI / 4; // // tetraEdgesLine.rotation.y = Math.PI ; // // tetraEdgesLine.rotation.y = Math.PI / 6; // tetraEdgesLine.castShadow = true; // world.add( tetraEdgesLine ); // const tetraGeometry2 = new THREE.TetrahedronGeometry( 2 ); // const tetraMaterial2 = new THREE.MeshBasicMaterial( {color: 0xffff00 } ); // const tetra2 = new THREE.Mesh( tetraGeometry2, tetraMaterial2 ); // tetra2.position.y = 0.81 // tetra2.rotation.x = Math.PI / 4 ; // // tetra2.rotation.y = Math.PI / 4 ; // tetra2.rotation.z = Math.PI / 4 ; // // world.add( tetra2 ); // const tetraEdges2 = new THREE.EdgesGeometry( tetraGeometry2 ); // const tetraEdgesLine2 = new THREE.LineSegments( tetraEdges2, new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 3 } ) ); // tetraEdgesLine2.castShadow = true; // tetraEdgesLine2.position.y = 0.81 // tetraEdgesLine2.rotation.x = Math.PI / 4 ; // // tetraEdgesLine2.rotation.y = Math.PI / 4 ; // tetraEdgesLine2.rotation.z = Math.PI / 4 ; // world.add( tetraEdgesLine2 ); // ---------- David ------------ // // const tetraGeometry = new THREE.ConeGeometry( 2, 3, 3 ); // const tetraMaterial = new THREE.MeshBasicMaterial( {color: 0xffff00 } ); // const tetra = new THREE.Mesh( tetraGeometry, tetraMaterial ); // tetra.rotation.y = Math.PI / 6 ; // tetra.position.y = 1 / 2 // world.add( tetra ); // const tetraEdges = new THREE.EdgesGeometry( tetraGeometry ); // const tetraEdgesLine = new THREE.LineSegments( tetraEdges, new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 15 } ) ); // tetraEdgesLine.rotation.y = Math.PI / 6 ; // tetraEdgesLine.position.y = 1 / 2 // tetraEdgesLine.castShadow = true; // world.add( tetraEdgesLine ); // const tetraGeometry2 = new THREE.ConeGeometry( 2, 3, 3 ); // const tetraMaterial2 = new THREE.MeshBasicMaterial( {color: 0xffff00 } ); // const tetra2 = new THREE.Mesh( tetraGeometry2, tetraMaterial2 ); // tetra2.rotation.y = Math.PI + Math.PI / 6 ; // tetra2.rotation.z = Math.PI; // tetra2.position.y = - 1 / 2 // world.add( tetra2 ); // const tetraEdges2 = new THREE.EdgesGeometry( tetraGeometry2 ); // const tetraEdgesLine2 = new THREE.LineSegments( tetraEdges2, new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 15 } ) ); // tetraEdgesLine2.castShadow = true; // tetraEdgesLine2.rotation.y = Math.PI + Math.PI / 6 ; // tetraEdgesLine2.rotation.z = Math.PI; // tetraEdgesLine2.position.y = - 1 / 2 // world.add( tetraEdgesLine2 ); // ---------- Flower of life ------------ // // const circleGeometry = new THREE.CircleGeometry( 3, 64 ); // const circleMaterial = new THREE.MeshBasicMaterial( { color: 0x049ef4, side: THREE.DoubleSide } ); // const circle = new THREE.Mesh( circleGeometry, circleMaterial ); // circle.castShadow = true; // world.add( circle ); // const circleLine = new THREE.LineSegments( circleGeometry, new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 3 } ) ); // circleLine.castShadow = true; // world.add( circleLine ); let rotatePoint = (cx, cy, angle, px, py) => { angle = - angle * (Math.PI / 180) let s = Math.sin(angle); let c = Math.cos(angle); // translate point back to origin: px -= cx; py -= cy; // rotate point let xnew = px * c + py * s; let ynew = px * s - py * c; // translate point back: px = xnew + cx; py = ynew + cy; return [px, py]; } function rotate(px, py, pz, pitch, roll, yaw) { pitch = - pitch * (Math.PI / 180) roll = - roll * (Math.PI / 180) yaw = - yaw * (Math.PI / 180) var cosa = Math.cos(yaw); var sina = Math.sin(yaw); var cosb = Math.cos(pitch); var sinb = Math.sin(pitch); var cosc = Math.cos(roll); var sinc = Math.sin(roll); var Axx = cosa*cosb; var Axy = cosa*sinb*sinc - sina*cosc; var Axz = cosa*sinb*cosc + sina*sinc; var Ayx = sina*cosb; var Ayy = sina*sinb*sinc + cosa*cosc; var Ayz = sina*sinb*cosc - cosa*sinc; var Azx = -sinb; var Azy = cosb*sinc; var Azz = cosb*cosc; let x = Axx*px + Axy*py + Axz*pz; let y = Ayx*px + Ayy*py + Ayz*pz; let z = Azx*px + Azy*py + Azz*pz; return [x, y, z] } const params = { xangle: 45, yangle: 0, zangle: 54.72972973, size: 3, radius: 1 }; let sphere, circleCircum; let spheres = []; let spheresLines = []; let renderFlower = (size = 3, radius = 1) => { spheres = []; spheresLines = []; for (let x of Array.from(Array(size), (_, i) => i * 0.612 * (radius * 2))) { x = x - ((size - 1) * (0.612 * (radius * 2))) / 2 for (let y of Array.from(Array(size), (_, i) => i * 0.612 * (radius * 2))) { y = y - ((size - 1) * (0.612 * (radius * 2))) / 2 for (let z of Array.from(Array(size), (_, i) => i * 0.612 * (radius * 2))) { z = z - ((size - 1) * (0.612 * (radius * 2))) / 2 let xtemp2, ytemp2, ztemp2; // [xtemp, ytemp, ztemp] = rotate(x, y, z, 0, 45, 0) // console.log(xtemp, ytemp, ztemp); [xtemp2, ytemp2, ztemp2] = rotate(x, y, z, params['yangle'], params['xangle'], params['zangle']) let sphereGeometry = new THREE.SphereGeometry( radius, 64, 64 ); let sphereMaterial = new THREE.MeshStandardMaterial( { color: 0x0063e4, metalness: 0.7, roughness: 0.3, opacity: 0.8, transparent: true } ); sphere = new THREE.Mesh( sphereGeometry, sphereMaterial ); sphere.position.x = xtemp2; sphere.position.y = ytemp2; sphere.position.z = ztemp2; world.add( sphere ); let circleGeometry = new THREE.TorusGeometry( radius, 0.02, 64, 64 ); let circleMaterial = new THREE.MeshBasicMaterial( { color: 0x000000, opacity: 0.1, transparent: true } ); circleCircum = new THREE.Mesh( circleGeometry, circleMaterial ); circleCircum.castShadow = true; circleCircum.rotation.y = Math.PI / 2; circleCircum.position.x = xtemp2; circleCircum.position.y = ytemp2; circleCircum.position.z = ztemp2; world.add( circleCircum ); sphere.start_x = x; sphere.start_y = y; sphere.start_z = z; circleCircum.start_x = x; circleCircum.start_y = y; circleCircum.start_z = z; spheres.push(sphere) spheresLines.push(circleCircum) } z_count += 1; } } // Big circle let circleGeometry = new THREE.TorusGeometry( 5 * radius, 0.02, 64, 64); let circleMaterial = new THREE.MeshBasicMaterial( { color: 0x000000, opacity: 0, transparent: true } ); circleCircum = new THREE.Mesh( circleGeometry, circleMaterial ); circleCircum.rotation.y = Math.PI / 2; circleCircum.castShadow = true; world.add( circleCircum ); } // renderFlower() // ---------- Seed of Life ------------ // let z_count = 0 for (let x_angle of Array.from(Array(4), (_, i) => 360 / 4 * i)) { for (let z_angle of Array.from(Array(6), (_, i) => 360 / 6 * i)) { for (let i of Array.from(Array(2), (_, i) => i* 0.5)) { if (z_count > 0 && i === 0) { continue; } let x, y, z; z = rotatePoint(0, 0, z_angle, i, 0)[1] y = rotatePoint(0, 0, z_angle, i, 0)[0] x = 0; [x, y ,z] = rotate(x, y, z, 0, x_angle, 0) console.log(x_angle, z_angle, x, y, z) let sphereGeometry = new THREE.SphereGeometry( 0.5, 64, 64 ); let sphereMaterial = new THREE.MeshBasicMaterial( { color: colors.fills.sphere, metalness: 1, roughness: 1, opacity: 0.5, transparent: true } ); let sphere = new THREE.Mesh( sphereGeometry, sphereMaterial ); sphere.position.x = x; sphere.position.y = y; sphere.position.z = z; world.add( sphere ); let circleGeometry = new THREE.TorusGeometry( 0.5, 0.01, 64, 64); let circleMaterial = new THREE.MeshBasicMaterial( { color: colors.line, opacity: 0.1, transparent: true } ); let circleCircum = new THREE.Mesh( circleGeometry, circleMaterial ); circleCircum.castShadow = true; circleCircum.rotation.y = Math.PI / 2; circleCircum.position.x = x; circleCircum.position.y = y; circleCircum.position.z = z; world.add( circleCircum ); } z_count += 1; } } // dark mode gets a soft warm ambient so unlit faces keep their colour if ( colors.dark ) scene.add( new THREE.AmbientLight( colors.light, 0.9 ) ); // legacy lighting was removed in three r155: intensities need the old value times pi const spotLight = new THREE.DirectionalLight( colors.light, 1.5 * Math.PI ); spotLight.position.set( 10, 0, 0 ); spotLight.castShadow = true; spotLight.shadow.mapSize.width = 2048; spotLight.shadow.mapSize.height = 2048; spotLight.shadow.camera.far = 21; spotLight.shadow.camera.left = -10; spotLight.shadow.camera.right = 10; spotLight.shadow.camera.top = 10; spotLight.shadow.camera.bottom = -10; // spotLight.shadow.camera.fov = 35; scene.add( spotLight ); // the wall itself is invisible: only the shadow "projection" shows, in light // orange, floating on the gradient; front side only so the shapes stay fully // visible when orbiting behind it const planeGeometry = new THREE.PlaneGeometry( 20, 20, 10, 10 ); const planeMaterial = new THREE.ShadowMaterial( { color: colors.shadow, opacity: 0.9 } ) const plane = new THREE.Mesh( planeGeometry, planeMaterial ); plane.rotation.y = Math.PI / 2; plane.position.x = -10 plane.receiveShadow = true; scene.add( plane ); //Create a helper for the shadow camera (optional) const helper = new THREE.CameraHelper( spotLight.shadow.camera ); // world.add( helper ); const axesHelper = new THREE.AxesHelper( 40 ); // scene.add( axesHelper ); // const light = new THREE.HemisphereLight( 0xffffbb, 0x080820, 0.2 ); // world.add( light ); // const gui = new GUI(); // gui.add( params, 'xangle', 0, 360 ).step(0.1).onChange( (value) => { // for (let sphere of spheres) { // [sphere.position.x, sphere.position.y, sphere.position.z] = rotate(sphere.start_x, sphere.start_y, sphere.start_z, params['yangle'], value, params['zangle']) // } // for (let sphereLine of spheresLines) { // [sphereLine.position.x, sphereLine.position.y, sphereLine.position.z] = rotate(sphereLine.start_x, sphereLine.start_y, sphereLine.start_z, params['yangle'], value, params['zangle']) // } // }); // gui.add( params, 'yangle', 0, 360 ).step(0.1).onChange( (value) => { // for (let sphere of spheres) { // [sphere.position.x, sphere.position.y, sphere.position.z] = rotate(sphere.start_x, sphere.start_y, sphere.start_z, value, params['xangle'], params['zangle']) // } // for (let sphereLine of spheresLines) { // [sphereLine.position.x, sphereLine.position.y, sphereLine.position.z] = rotate(sphereLine.start_x, sphereLine.start_y, sphereLine.start_z, value, params['xangle'], params['zangle']) // } // }); // gui.add( params, 'zangle', 0, 360 ).step(0.1).onChange( (value) => { // for (let sphere of spheres) { // [sphere.position.x, sphere.position.y, sphere.position.z] = rotate(sphere.start_x, sphere.start_y, sphere.start_z, params['yangle'], params['xangle'], value) // } // for (let sphereLine of spheresLines) { // [sphereLine.position.x, sphereLine.position.y, sphereLine.position.z] = rotate(sphereLine.start_x, sphereLine.start_y, sphereLine.start_z, params['yangle'], params['xangle'], value) // } // }); // gui.add( params, 'size', 2, 5).step(1).onChange( (value) => { // for (let sphere of spheres) { // world.remove(sphere) // } // for (let sphereLine of spheresLines) { // world.remove(sphereLine) // } // world.remove(circleCircum) // renderFlower (value, params['radius']) // }); // gui.add( params, 'radius', 0.5, 3).onChange( (value) => { // for (let sphere of spheres) { // world.remove(sphere) // } // for (let sphereLine of spheresLines) { // world.remove(sphereLine) // } // world.remove(circleCircum) // renderFlower (params['size'], value) // }); // gui.close(); // var axis = new THREE.Vector3(0, 1, 0); function animate () { requestAnimationFrame( animate ); const time = performance.now() / 1000; // for (let sphere of spheres) { // sphere.position.x = Math.cos( time ) * sphere.start_x; // sphere.position.z = - Math.sin( time ) * sphere.start_z; // } // for (let sphereLine of spheresLines) { // sphereLine.position.x = Math.cos( time ) * sphereLine.start_x; // sphereLine.position.z = - Math.sin( time ) * sphereLine.start_z; // } updateWorld(); updateGlow(); controls.update(); renderer.render( scene, camera ); }; animate();