119 lines
3.1 KiB
TypeScript
119 lines
3.1 KiB
TypeScript
import {
|
|
PerspectiveCamera,
|
|
Scene,
|
|
WebGLRenderer,
|
|
AmbientLight,
|
|
DirectionalLight,
|
|
} from "three";
|
|
|
|
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
|
import { getCenter3D, getMaxSize } from "./utils";
|
|
|
|
const DEG2RAD = Math.PI / 180;
|
|
function buildDefaultLights() {
|
|
// Ambient light
|
|
scene.add(new AmbientLight(0xaaaaaa));
|
|
|
|
// Directional lights
|
|
const opt = {
|
|
azimuth: 220,
|
|
altitude: 45,
|
|
};
|
|
|
|
const lambda = (90 - opt.azimuth) * DEG2RAD;
|
|
const phi = opt.altitude * DEG2RAD;
|
|
|
|
const x = Math.cos(phi) * Math.cos(lambda);
|
|
const y = Math.cos(phi) * Math.sin(lambda);
|
|
const z = Math.sin(phi);
|
|
|
|
const light1 = new DirectionalLight(0xffffff, 0.5);
|
|
light1.position.set(x, y, z);
|
|
scene.add(light1);
|
|
|
|
// Thin light from the opposite direction
|
|
const light2 = new DirectionalLight(0xffffff, 0.1);
|
|
light2.position.set(-x, -y, -z);
|
|
return light2;
|
|
}
|
|
|
|
export interface Extent {
|
|
xmin: number;
|
|
ymin: number;
|
|
xmax: number;
|
|
ymax: number;
|
|
zmin: number;
|
|
zmax: number;
|
|
}
|
|
|
|
let controls: OrbitControls;
|
|
let renderer: WebGLRenderer;
|
|
let camera: PerspectiveCamera;
|
|
let scene: Scene;
|
|
export function buildScene(container: HTMLElement, extent: Extent) {
|
|
const maxSize = getMaxSize(extent);
|
|
const center = getCenter3D(extent);
|
|
|
|
const width = container.clientWidth;
|
|
const height = container.clientHeight;
|
|
|
|
camera = new PerspectiveCamera(
|
|
50,
|
|
width / height,
|
|
maxSize * 0.1,
|
|
maxSize * 25
|
|
);
|
|
|
|
camera.position.set(center.x, center.y - 125000, extent.zmax + 100000);
|
|
camera.lookAt(center);
|
|
|
|
// Initialize the renderer
|
|
renderer = new WebGLRenderer({
|
|
alpha: true,
|
|
logarithmicDepthBuffer: true,
|
|
});
|
|
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
renderer.setSize(width, height);
|
|
renderer.localClippingEnabled = true;
|
|
renderer.setAnimationLoop(animate);
|
|
|
|
// Handle window resize event to adapt the aspect ratio
|
|
window.addEventListener("resize", () => onWindowResize(container));
|
|
|
|
container.appendChild(renderer.domElement);
|
|
|
|
controls = new OrbitControls(camera, renderer.domElement);
|
|
controls.target.set(center.x, center.y, center.z); // Focus on the center
|
|
controls.enableDamping = true; // Smooth camera movement
|
|
controls.maxDistance = maxSize * 5;
|
|
controls.update();
|
|
|
|
// Scene will hold all our elements such as objects, cameras and lights
|
|
scene = new Scene();
|
|
|
|
// Add lights to the scene
|
|
const lights = buildDefaultLights();
|
|
lights.name = "lights";
|
|
scene.add(lights);
|
|
|
|
return { renderer, scene, camera, controls };
|
|
}
|
|
|
|
function onWindowResize(container: HTMLElement) {
|
|
// Update the camera's aspect ratio and the renderer's size to reflect
|
|
// the new screen dimensions upon a browser window resize
|
|
camera.aspect = container.clientWidth / container.clientHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(container.clientWidth, container.clientHeight);
|
|
|
|
// required if controls.enableDamping or controls.autoRotate are set to true
|
|
controls.update();
|
|
}
|
|
|
|
function animate() {
|
|
renderer.render(scene, camera);
|
|
|
|
// required if controls.enableDamping or controls.autoRotate are set to true
|
|
controls.update();
|
|
}
|