UNPKG

vertecs

Version:

A typescript entity-component-system framework

183 lines (146 loc) 5.81 kB
import { Camera, ColorManagement, InstancedMesh, Matrix4, PCFSoftShadowMap, Quaternion, Scene, SRGBColorSpace, Vector3, WebGLRenderer, } from "three"; import { EcsManager, Entity, System } from "../core"; import ThreeObject3D from "./ThreeObject3D"; import { Transform } from "../math"; import ThreeCameraSystem from "./camera/ThreeCameraSystem"; import ThreeCamera from "./camera/ThreeCamera"; import ThreeLightSystem from "./light/ThreeLightSystem"; import ThreeCss3dSystem from "./css3d/ThreeCss3dSystem"; import ThreeInstancedMesh from "./ThreeInstancedMesh"; export default class ThreeSystem extends System<[Transform, ThreeObject3D]> { #scene: Scene; #renderer: WebGLRenderer; #cameraSystem?: ThreeCameraSystem; #lightSystem?: ThreeLightSystem; #css3dSystem?: ThreeCss3dSystem; public constructor(tps?: number) { super([Transform, ThreeObject3D], tps); this.#scene = new Scene(); this.#renderer = new WebGLRenderer({ antialias: true }); this.#renderer.setPixelRatio(window.devicePixelRatio); this.#renderer.setSize(window.innerWidth, window.innerHeight); this.#renderer.setClearColor(0x323336); this.#renderer.shadowMap.enabled = true; this.#renderer.shadowMap.type = PCFSoftShadowMap; ColorManagement.enabled = true; this.#renderer.outputColorSpace = SRGBColorSpace; document.body.appendChild(this.#renderer.domElement); } public onAddedToEcsManager(ecsManager: EcsManager) { this.#lightSystem = new ThreeLightSystem(this.#scene, this.tps); ecsManager.addSystem(this.#lightSystem); this.#cameraSystem = new ThreeCameraSystem(this.renderer, this.tps); ecsManager.addSystem(this.#cameraSystem); this.$dependencies = [ThreeCameraSystem, ThreeLightSystem]; if (document.getElementById("hud")) { this.#css3dSystem = new ThreeCss3dSystem(this, this.tps); ecsManager.addSystem(this.#css3dSystem); } } public onEntityEligible( entity: Entity, components: [Transform, ThreeObject3D] ) { const threeMesh = entity.getComponent(ThreeObject3D); if (!threeMesh) { throw new Error("ThreeMesh not found on eligible entity"); } if (threeMesh instanceof ThreeInstancedMesh) { const instancedMesh = threeMesh.object3D as InstancedMesh; for (let i = 0; i < instancedMesh.count; i++) { const matrix = new Matrix4(); matrix.compose( new Vector3(0, 0, 0), new Quaternion(0, 0, 0, 1), new Vector3(0, 0, 0) ); instancedMesh.setMatrixAt(i, matrix); instancedMesh.instanceMatrix.needsUpdate = true; } } this.#scene.add(threeMesh?.object3D); } public onEntityNoLongerEligible( entity: Entity, components: [Transform, ThreeObject3D] ) { const [transform, threeComponent] = components; if (threeComponent instanceof ThreeInstancedMesh) { const instancedMesh = threeComponent.object3D as InstancedMesh; const matrix = new Matrix4(); matrix.compose( new Vector3(0, 0, 0), new Quaternion(0, 0, 0, 1), new Vector3(0, 0, 0) ); instancedMesh.setMatrixAt( threeComponent.entities.length + 1, matrix ); instancedMesh.instanceMatrix.needsUpdate = true; } else { this.#scene.remove(threeComponent?.object3D); } } public async onStart(): Promise<void> {} protected onLoop( components: [Transform, ThreeObject3D][], entities: Entity[], deltaTime: number ): void { const positionVector3 = new Vector3(); const quaternion = new Quaternion(); const scaleVector3 = new Vector3(); const matrix4 = new Matrix4(); for (let i = 0; i < components.length; i++) { const [transform, threeMesh] = components[i]; // TODO: Check how to ignore entities that have a camera const [x, y, z] = transform.getWorldPosition(); const [qx, qy, qz, qw] = transform.getWorldRotation(); const [sx, sy, sz] = transform.getWorldScale(); if (threeMesh instanceof ThreeInstancedMesh) { const object3d = threeMesh.object3D as InstancedMesh; const index = threeMesh.getEntityIndex(entities[i].id); object3d.getMatrixAt(index, matrix4); matrix4.makeRotationFromQuaternion( quaternion.set(qx, qy, qz, qw) ); matrix4.scale(scaleVector3.set(sx, sy, sz)); matrix4.setPosition(x, y, z); object3d.setMatrixAt(index, matrix4); object3d.instanceMatrix.needsUpdate = true; } else { const { object3D } = threeMesh; object3D.position.set(x, y, z); object3D.quaternion.set(qx, qy, qz, qw); object3D.scale.set(sx, sy, sz); object3D.updateMatrix(); } } this.#renderer.render( this.#scene, this.#cameraSystem!.cameraEntity!.getComponent(ThreeCamera)!.camera ); } public get camera(): Camera { return this.#cameraSystem!.cameraEntity!.getComponent(ThreeCamera)! .camera; } public get renderer(): WebGLRenderer { return this.#renderer; } public get scene(): Scene { return this.#scene; } }