Select Git revision
version.php
-
Chris Sangwin authoredChris Sangwin authored
Pipeline.ts 4.81 KiB
import * as THREE from "three";
import { DragControls } from "three/examples/jsm/controls/DragControls";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { Shape } from "./Shapes";
interface PipelineObserver {
/**
* @breif RendererObserver is an abstract class that is used to observe the renderer.
* The renderer calls in the rendering loop update of this observer.
* @param {number} _deltaTime gives the time in seconds between two frames
*/
update(_deltaTime: number): void;
}
abstract class PipelineData {
observers: Array<PipelineObserver>;
draggables: Array<THREE.Object3D>;
scene: THREE.Scene;
allowOrbit: boolean = true;
constructor() {
this.observers = new Array<PipelineObserver>();
this.draggables = new Array<THREE.Object3D>();
this.scene = new THREE.Scene();
this.data();
}
protected addShape(shape: Shape, observer: boolean = true, draggable: boolean = false) {
this.scene.add(shape.object3d);
if (observer) {
this.observers.push(shape);
}
if (draggable) {
this.draggables.push(shape.object3d);
}
}
/**
* @brief Should define a scenario with observers, draggables and scene.
* It initializes the scene and adds the draggables to the scene.
* Based on that the pipeline can be initialized.
*/
abstract data(): void;
}
class Pipeline {
public readonly renderer: THREE.WebGLRenderer;
public readonly camera: THREE.PerspectiveCamera;
public readonly orbitControls: OrbitControls;
public readonly dragControls: DragControls;
private scene?: THREE.Scene;
private observer: Array<PipelineObserver>;
private startTime: number;
constructor() {
// CREATE CAMERA
this.camera = new THREE.PerspectiveCamera(75,
window.innerWidth / window.innerHeight, 0.1, 1000);
this.camera.position.z = 5;
window.addEventListener('resize', () => {
this.camera.aspect = window.innerWidth / window.innerHeight
this.camera.updateProjectionMatrix()
}, false);
// CREATE RENDERER
this.renderer = new THREE.WebGLRenderer({
antialias: true
});
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setClearColor(1);
document.body.appendChild(this.renderer.domElement);
window.addEventListener('resize', () => {
this.renderer.setSize(window.innerWidth, window.innerHeight);
}, false);
// CREATE CONTROLS
this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
this.dragControls = new DragControls([], this.camera, this.renderer.domElement);
this.dragControls.addEventListener('dragstart', () => {
this.orbitControls.enabled = false;
});
this.dragControls.addEventListener('dragend', () => {
this.orbitControls.enabled = true;
});
// INITIALISE OTHER FIELDS
this.observer = new Array<PipelineObserver>();
this.startTime = performance.now();
}
public init(data: PipelineData): void {
this.setScene(data.scene);
data.draggables.forEach(draggable => { this.addDraggable(draggable); });
data.observers.forEach(observer => { this.addPipelineObserver(observer); });
}
public addPipelineObserver(object: PipelineObserver): void {
this.observer.push(object);
}
public removePipelineObserver(object: PipelineObserver): void {
let index = this.observer.indexOf(object);
if (index > -1) {
this.observer.splice(index, 1);
}
}
public addDraggable(object: THREE.Object3D): void {
if (object instanceof THREE.Line) {
console.warn('Cannot add draggable to line');
return;
}
this.dragControls.getObjects().push(object);
}
public setScene(scene: THREE.Scene): void {
this.scene = scene;
}
private update(deltaTime: number): void {
for (let i = 0; i < this.observer.length; i++) {
this.observer[i].update(deltaTime);
}
}
public loop(): void {
const now = performance.now();
const deltaTime = (now - this.startTime) / 1000;
this.startTime = now;
this.update(deltaTime);
if (this.scene) {
this.renderer.render(this.scene, this.camera);
}
requestAnimationFrame(() => {
this.loop();
});
}
}
export { Pipeline, PipelineData };
export type { PipelineObserver };