You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

147 lines
5.3 KiB

import { EventEmitter } from '@angular/core';
import { Scene, Camera, OrthographicCamera, PerspectiveCamera, WebGLRenderer, AmbientLight, PointLight, Raycaster, Intersection, Vector3, Object3D } from 'three';
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import * as Stats from 'stats.js';
export class Three {
private _element: HTMLElement;
private _render: FrameRequestCallback;
constructor(element: HTMLElement) {
this._element = element;
this.onrender = new EventEmitter<any>();
this._render = () => {
requestAnimationFrame(this._render);
this.renderer.render(this.scene, this.camera);
this.css2dRenderer?.render(this.scene, this.camera);
this.controls?.update();
this.stats?.update();
this.onrender.emit();
};
}
public scene: Scene;
public camera: Camera;
public renderer: WebGLRenderer;
public css2dRenderer: CSS2DRenderer;
public ambientLight: AmbientLight;
public pointLight: PointLight;
public controls: OrbitControls;
public stats: Stats;
public raycaster: Raycaster;
public onrender: EventEmitter<any>;
public render(): void {
this._render(0);
}
public initScene(): void {
this.scene = new Scene();
}
public initCamera(): void {
this.camera = new PerspectiveCamera(60, this._element.clientWidth / this._element.clientHeight, 0.1, 10000);
this.camera.position.set(-100, 100, 100);
this.camera.lookAt(new Vector3(0, 0, 0));
}
public initRenderer(): void {
this.renderer = new WebGLRenderer({
alpha: true,
antialias: true
});
this.renderer.setSize(this._element.clientWidth, this._element.clientHeight);
this.renderer.setClearColor(0xffffff, 0);
this._element.appendChild(this.renderer.domElement);
}
public initCSS2DRenderer(): void {
this.css2dRenderer = new CSS2DRenderer();
this.css2dRenderer.setSize(this._element.clientWidth, this._element.clientHeight);
this.css2dRenderer.domElement.style.position = 'absolute';
this.css2dRenderer.domElement.style.top = '0';
this._element.appendChild(this.css2dRenderer.domElement);
}
public initAmbientLight(): void {
this.ambientLight = new AmbientLight(0xffffff);
this.scene.add(this.ambientLight);
}
public initPointLight(): void {
this.pointLight = new PointLight(0xffffff);
this.pointLight.position.set(-100, 100, -100);
this.scene.add(this.pointLight);
}
public initControls(): void {
this.controls = new OrbitControls(this.camera, this.css2dRenderer?.domElement || this.renderer.domElement);
}
public initStats(): void {
this.stats = new Stats();
this.stats.dom.style.position = 'absolute';
this.stats.dom.style.left = '16px';
this.stats.dom.style.top = '16px';
this._element.appendChild(this.stats.dom);
}
public initRaycaster(): void {
this.raycaster = new Raycaster();
}
public getIntersects(event: MouseEvent | TouchEvent, objects?: Object3D[]): Intersection[] {
event.preventDefault();
const domElement = this.css2dRenderer?.domElement || this.renderer.domElement;
let mouse: { x: number, y: number };
if (event instanceof MouseEvent) {
mouse = {
x: event.offsetX,
y: event.offsetY
};
}
else {
const rect = domElement.getBoundingClientRect();
mouse = {
x: event.touches[0].clientX - rect.x,
y: event.touches[0].clientY - rect.y
};
}
this.raycaster.setFromCamera({
x: (mouse.x / domElement.offsetWidth) * 2 - 1,
y: -(mouse.y / domElement.offsetHeight) * 2 + 1
}, this.camera);
return this.raycaster.intersectObjects(objects || this.scene.children);
}
public changeCamera(type: 'orthographic' | 'perspective'): void {
if (type == 'orthographic') {
if (this.camera instanceof PerspectiveCamera) {
const camera = this.camera as PerspectiveCamera;
const depth = camera.position.sub(this.scene.position).length();
const height = depth * 2 * Math.atan(camera.fov * (Math.PI / 180) / 2);
const width = height * camera.aspect;
this.camera = new OrthographicCamera(width / -2, width / 2, height / 2, height / -2, camera.near, camera.far);
this.camera.position.copy(camera.position);
this.camera.quaternion.copy(camera.quaternion);
this.camera.lookAt(camera.getWorldDirection(new Vector3(0, 0, - 1).applyQuaternion(camera.quaternion)));
this.controls && (this.controls.object = this.camera);
}
}
else {
if (this.camera instanceof OrthographicCamera) {
const camera = this.camera as OrthographicCamera;
const depth = camera.position.sub(this.scene.position).length();
const width = camera.right - camera.left;
const height = camera.top - camera.bottom;
const fov = 2 * Math.tan(height / depth / 2) / (Math.PI / 180);
this.camera = new PerspectiveCamera(fov, width / height, camera.near, camera.far);
this.camera.position.copy(camera.position);
this.camera.quaternion.copy(camera.quaternion);
this.camera.lookAt(camera.getWorldDirection(new Vector3(0, 0, - 1).applyQuaternion(camera.quaternion)));
this.controls && (this.controls.object = this.camera);
}
}
}
}