diff --git a/package-lock.json b/package-lock.json index d7aa97d..e8d0a02 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,11 @@ "gl-matrix": "^3.4.3", "luxon": "^3.4.4", "satellite.js": "^5.0.0", - "three": "^0.158.0" + "three": "^0.157.0" }, "devDependencies": { "@types/luxon": "^3.3.4", - "@types/three": "^0.158.2", + "@types/three": "^0.157.0", "@typescript-eslint/eslint-plugin": "^6.11.0", "@yushijinhun/three-minifier-rollup": "^0.4.0", "eslint": "^8.53.0", @@ -303,9 +303,9 @@ "dev": true }, "node_modules/@types/three": { - "version": "0.158.2", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.158.2.tgz", - "integrity": "sha512-KPYbdLI8VPhu7qnHqsayfkuk58Qk+20l1U5HBK7uG50EjtrqeCreurNNpnatMZje29XRuTM1A+pGHGdDBHPyUQ==", + "version": "0.157.2", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.157.2.tgz", + "integrity": "sha512-2kykrMgvO5LTAiahadM6ijoER+GcbEJ61pQVOyGxIJTCASoUnzwJvfhilsLxvEw4+glzhLYUDvvTvNjx+58Vzw==", "dev": true, "dependencies": { "@types/stats.js": "*", @@ -3166,9 +3166,9 @@ "license": "MIT" }, "node_modules/three": { - "version": "0.158.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.158.0.tgz", - "integrity": "sha512-TALj4EOpdDPF1henk2Q+s17K61uEAAWQ7TJB68nr7FKxqwyDr3msOt5IWdbGm4TaWKjrtWS8DJJWe9JnvsWOhQ==" + "version": "0.157.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.157.0.tgz", + "integrity": "sha512-CeAwQrf4x3z0/e+MC4F+nXLW5t0gh3pw+L6CCBqpHvOq3bGYIgRYub7Pv0j/9wR+d++OiEglyZzWyuSYbwWGOA==" }, "node_modules/through2": { "version": "0.6.5", @@ -3719,9 +3719,9 @@ "dev": true }, "@types/three": { - "version": "0.158.2", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.158.2.tgz", - "integrity": "sha512-KPYbdLI8VPhu7qnHqsayfkuk58Qk+20l1U5HBK7uG50EjtrqeCreurNNpnatMZje29XRuTM1A+pGHGdDBHPyUQ==", + "version": "0.157.2", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.157.2.tgz", + "integrity": "sha512-2kykrMgvO5LTAiahadM6ijoER+GcbEJ61pQVOyGxIJTCASoUnzwJvfhilsLxvEw4+glzhLYUDvvTvNjx+58Vzw==", "dev": true, "requires": { "@types/stats.js": "*", @@ -5552,9 +5552,9 @@ "dev": true }, "three": { - "version": "0.158.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.158.0.tgz", - "integrity": "sha512-TALj4EOpdDPF1henk2Q+s17K61uEAAWQ7TJB68nr7FKxqwyDr3msOt5IWdbGm4TaWKjrtWS8DJJWe9JnvsWOhQ==" + "version": "0.157.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.157.0.tgz", + "integrity": "sha512-CeAwQrf4x3z0/e+MC4F+nXLW5t0gh3pw+L6CCBqpHvOq3bGYIgRYub7Pv0j/9wR+d++OiEglyZzWyuSYbwWGOA==" }, "through2": { "version": "0.6.5", diff --git a/package.json b/package.json index 6c6214e..2daec95 100644 --- a/package.json +++ b/package.json @@ -19,11 +19,11 @@ "gl-matrix": "^3.4.3", "luxon": "^3.4.4", "satellite.js": "^5.0.0", - "three": "^0.158.0" + "three": "^0.157.0" }, "devDependencies": { "@types/luxon": "^3.3.4", - "@types/three": "^0.158.2", + "@types/three": "^0.157.0", "@typescript-eslint/eslint-plugin": "^6.11.0", "@yushijinhun/three-minifier-rollup": "^0.4.0", "eslint": "^8.53.0", diff --git a/public/images/Earth_Cloud.jpg b/public/images/Earth_Cloud.jpg new file mode 100644 index 0000000..24ea818 Binary files /dev/null and b/public/images/Earth_Cloud.jpg differ diff --git a/public/shaders/dot2-fragment.glsl b/public/shaders/dot2-fragment.glsl new file mode 100644 index 0000000..4f59f7f --- /dev/null +++ b/public/shaders/dot2-fragment.glsl @@ -0,0 +1,9 @@ +uniform vec3 color; +uniform sampler2D pointTexture; + +varying vec3 vColor; + +void main() { + gl_FragColor = vec4( color * vColor, 1.0 ); + gl_FragColor = gl_FragColor * texture2D( pointTexture, gl_PointCoord ); +} \ No newline at end of file diff --git a/public/shaders/dot2-vertex.glsl b/public/shaders/dot2-vertex.glsl new file mode 100644 index 0000000..9eb55f2 --- /dev/null +++ b/public/shaders/dot2-vertex.glsl @@ -0,0 +1,14 @@ +attribute float size; +attribute vec3 color; + +varying vec3 vColor; + +void main() { + vColor = color; + + vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); + + gl_PointSize = size * ( 300.0 / -mvPosition.z ); + + gl_Position = projectionMatrix * mvPosition; +} diff --git a/src/viewer/Earth.ts b/src/viewer/Earth.ts index e284047..e9e2ae3 100644 --- a/src/viewer/Earth.ts +++ b/src/viewer/Earth.ts @@ -1,21 +1,96 @@ -import { Color, TextureLoader, MeshPhongMaterial, SphereGeometry, Mesh, Group } from '../utils/three'; +import { ShaderMaterial, UniformsUtils } from 'three'; +import { Color, TextureLoader, MeshPhongMaterial, SphereGeometry, Mesh, Group, BackSide, AdditiveBlending } from '../utils/three'; import SceneComponent from './interfaces/SceneComponent'; import SatelliteOrbitScene from './SatelliteOrbitScene'; class Earth implements SceneComponent { + baseUrl = ''; basePath = '/StuffInSpace/images'; radiusInKm = 6371.0; pxToRadius = 3185.5; + addAtmosphere = false; + addClouds = false; sphere: Mesh | undefined = undefined; group: Group | undefined = undefined; + initClouds (scene: SatelliteOrbitScene, group: Group) { + // this isn't a great implementation of the clouds, + // so will leave off by default + const texture = new TextureLoader().load(`${this.basePath}/Earth_Cloud.jpg`); - init (scene: SatelliteOrbitScene) { - const dayTexture = new TextureLoader().load(`${this.basePath}/earth-blue-marble.jpg`); - const nightTexture = new TextureLoader().load(`${this.basePath}/nightearth-4096.png`); - const bumpTexture = new TextureLoader().load(`${this.basePath}/earth-topology.png`); - const earthSpecularMap = new TextureLoader().load(`${this.basePath}/earth-water.png`); + const radius = scene.km2pixels(this.radiusInKm + 0.02); + const geometry = new SphereGeometry(radius, 32, 32); + + const material = new MeshPhongMaterial({ + map: texture, + opacity: 0.3, + transparent: true + }); + + const mesh = new Mesh(geometry, material); + const scale = 1.01; + mesh.scale.set(scale, scale, scale); + + group.add(mesh); + } + + initAtmosphere (scene: SatelliteOrbitScene, group: Group) { + // this isn't a great implementation of the atmospheric effect, + // so will leave off by default + const vertexShader = [ + 'varying vec3 vNormal;', + 'varying vec3 vPosition;', + 'void main() {', + 'vNormal = normalize( normalMatrix * normal );', + 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', + 'vPosition = gl_Position.xyz;', + '}' + ].join('\n'); + + const fragmentShader = [ + 'varying vec3 vNormal;', + 'varying vec3 vPosition;', + 'void main() {', + ' float intensity = pow( 0.8 + dot( vNormal, vec3( 0, 0, 1.0 ) ), 12.0 );', + ' intensity = min(intensity, 1.7);', + ' gl_FragColor = vec4( 0.37, 0.71, 0.93, 0.6 ) * intensity;', + '}' + ].join('\n'); + + const uniforms = UniformsUtils.clone({}); + + const material = new ShaderMaterial({ + uniforms: uniforms, + vertexShader: vertexShader, + fragmentShader: fragmentShader, + side: BackSide, + blending: AdditiveBlending, + transparent: true, + }); + + const radius = scene.km2pixels(this.radiusInKm + 0.1); + const geometry = new SphereGeometry(radius, 32, 32); + + const mesh = new Mesh(geometry, material); + const scale = 1.019; + mesh.scale.set(scale, scale, scale); + + group.add(mesh); + } + + init (scene: SatelliteOrbitScene, context: Record) { + if (context.config) { + this.baseUrl = context.config.baseUrl; + } + + this.group = new Group(); + + const basePath = `${this.baseUrl}/images`; + const dayTexture = new TextureLoader().load(`${basePath}/earth-blue-marble.jpg`); + const nightTexture = new TextureLoader().load(`${basePath}/nightearth-4096.png`); + const bumpTexture = new TextureLoader().load(`${basePath}/8081_earthbump4k.jpg`); + const earthSpecularMap = new TextureLoader().load(`${basePath}/earth-water.png`); const dayMaterial = new MeshPhongMaterial({ map: dayTexture, @@ -32,7 +107,17 @@ class Earth implements SceneComponent { const radius = scene.km2pixels(this.radiusInKm); const geometry = new SphereGeometry(radius, 32, 32); this.sphere = new Mesh( geometry, dayMaterial ); - scene.add(this.sphere); + this.group.add(this.sphere); + + if (this.addClouds) { + this.initClouds(scene, this.group); + } + + if (this.addAtmosphere) { + this.initAtmosphere(scene, this.group); + } + + scene.add(this.group); } update (): void { diff --git a/src/viewer/Satellites.ts b/src/viewer/Satellites.ts index 4e3b92c..68fd13f 100644 --- a/src/viewer/Satellites.ts +++ b/src/viewer/Satellites.ts @@ -1,5 +1,5 @@ -import { Points, PointsMaterial, BufferGeometry, Float32BufferAttribute, AdditiveBlending, Color, TextureLoader } from '../utils/three'; -import { ShaderMaterial } from 'three'; +import { Points, PointsMaterial, BufferGeometry, Float32BufferAttribute, AdditiveBlending, Color, TextureLoader, SubtractiveBlending } from '../utils/three'; +import { CanvasTexture, ShaderMaterial } from 'three'; import SceneComponent from './interfaces/SceneComponent'; import SatelliteStore from './SatelliteStore'; @@ -170,17 +170,51 @@ class Satellites implements SceneComponent, SelectableSatellite { geometry.setAttribute('color', new Float32BufferAttribute( colors, 3 ) ); geometry.setAttribute('size', new Float32BufferAttribute( sizes, 1 ) ); + const canvas = document.createElement( 'CANVAS' ) as HTMLCanvasElement; + canvas.width = 128; + canvas.height = 128; + + const context = canvas.getContext( '2d' ) as CanvasRenderingContext2D; + context.globalAlpha = 0.3; + context.filter = 'blur(16px)'; + context.fillStyle = 'white'; + context.beginPath(); + context.arc( 64, 64, 40, 0, 2*Math.PI ); + context.fill( ); + context.globalAlpha = 1; + context.filter = 'blur(5px)'; + context.fillStyle = 'white'; + context.beginPath(); + context.arc( 64, 64, 16, 0, 2 * Math.PI ); + context.fill( ); + + const texture = new CanvasTexture( canvas ); + + // const texture = new TextureLoader().load(`${this.baseUrl}/images/spark1.png`); + // const material= new PointsMaterial({ + // color: 'grey', + // // map: texture, + // size: 3, + // sizeAttenuation: false, + // vertexColors: true, + // blending: AdditiveBlending, + // depthTest: true + // }); + + const material= new PointsMaterial({ - color: 'grey', - size: 3, - sizeAttenuation: false, + color: 'white', vertexColors: true, + size: 0.1, + sizeAttenuation: true, + map: texture, + transparent: true, blending: AdditiveBlending, + depthTest: true, + depthWrite: false }); - const shader = this.shaderStore.getShader('dot2'); - - console.log('shader', shader, shader.vertex); + // const shader = this.shaderStore.getShader('dot2'); // const material = new ShaderMaterial({ // uniforms: { diff --git a/src/viewer/ShaderStore.ts b/src/viewer/ShaderStore.ts index 2ecf6ac..d8b60a0 100644 --- a/src/viewer/ShaderStore.ts +++ b/src/viewer/ShaderStore.ts @@ -43,6 +43,11 @@ class ShaderStore { } } + addShader (name: string, fragmentCode: string, vertexCode: string) { + this.shaderData[`${name}-fragment`] = fragmentCode; + this.shaderData[`${name}-vertex`] = vertexCode; + } + getFragmentShader (name: string) { return this.shaderData[`${name}-fragment`]; } diff --git a/src/viewer/Sun.ts b/src/viewer/Sun.ts index 7d81ea9..6d23065 100644 --- a/src/viewer/Sun.ts +++ b/src/viewer/Sun.ts @@ -6,7 +6,7 @@ import SatelliteOrbitScene from './SatelliteOrbitScene'; class Sun implements SceneComponent { static deg2RadMult = (Math.PI / 180); - speedy = false; + fastTime = false; showGeometry = false; lightSouce: Object3D | undefined; lightSourceGeometery: Object3D | undefined; @@ -27,14 +27,12 @@ class Sun implements SceneComponent { let hour = this.hour; // if we aren't overriding the hour, then calculate from actual time if (hour === undefined || hour === -1) { - let time = DateTime.utc(); - - time = time.set({ hour: 18 }); + const time = DateTime.utc(); hour = time.hour; } // adjust by 180, since left of texture is at 0 - const angle = ((hour / 24) * 360) + + 180; + const angle = ((hour / 24) * 360) + 180; const point = { x: distance * Math.cos( this.degreesToReadians(angle) ), @@ -45,6 +43,15 @@ class Sun implements SceneComponent { } init (scene: SatelliteOrbitScene) { + // Speedily rotate simulated sun + if (this.fastTime) { + setInterval(() => { + this.hour += 0.05; + }, 100); + } else { + this.hour = -1; + } + this.calculateSunLoc(); const sunLoc = this.calculateSunLoc(); const coords = { x: sunLoc.x, y: 0, z: sunLoc.z}; @@ -63,28 +70,19 @@ class Sun implements SceneComponent { } scene.add(this.objectGroup); - - // Speedily rotate simulated sun - if (this.speedy) { - setInterval(() => { - this.hour += 0.05; - }, 100); - } else { - this.hour = -1; - } } update (): void { - const sunLoc = this.calculateSunLoc(); - const coords = { x: sunLoc.x, y: 0, z: sunLoc.z}; + // const sunLoc = this.calculateSunLoc(); + // const coords = { x: sunLoc.x, y: 0, z: sunLoc.z}; - if (this.lightSouce) { - this.lightSouce.position.set(coords.x, coords.y, coords.z); - } + // if (this.lightSouce) { + // this.lightSouce.position.set(coords.x, coords.y, coords.z); + // } - if (this.lightSourceGeometery) { - this.lightSourceGeometery.position.set(coords.x, coords.y, coords.z); - } + // if (this.lightSourceGeometery) { + // this.lightSourceGeometery.position.set(coords.x, coords.y, coords.z); + // } } setVisible (visible: boolean): void {