UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

171 lines (129 loc) 4.55 kB
import Vector3 from "../../../../../core/geom/Vector3.js"; import { obtainTerrain } from "../../../../ecs/terrain/util/obtainTerrain.js"; import { SurfacePoint3 } from "../../../../../core/geom/3d/SurfacePoint3.js"; import { make_ray_from_viewport_position } from "../../../make_ray_from_viewport_position.js"; import { CameraSystem } from "../CameraSystem.js"; import Vector2 from "../../../../../core/geom/Vector2.js"; import { Transform } from "../../../../ecs/transform/Transform.js"; const NEAR_PLANE_INDEX = 5; const FAR_PLANE_INDEX = 4; export class PerfectPanner { /** * Point in world space that was under our cursor when we started panning * @type {Vector3} */ #grab_world_reference = new Vector3(); #grab_near_projection = new Vector3(); #grab_far_projection = new Vector3(); /** * Camera position at the start * @type {Vector3} */ #grab_eye_position = new Vector3(); /** * * @type {Engine|null} */ #engine = null; /** * * @type {Camera|null} */ #camera = null; /** * * @type {Transform|null} */ #camera_transform = null; /** * * Translation relative to starting point * @type {Vector3} */ #translation = new Vector3(); get engine() { return this.#engine; } /** * * @param {Engine} v */ set engine(v) { this.#engine = v; } /** * @returns {Vector3} */ get translation() { return this.#translation; } /** * * @param {Vector3} out * @param {number} viewport_x * @param {number} viewport_y * @param {number} plane */ #projection_viewport_onto_plane(out, viewport_x, viewport_y, plane) { const engine = this.#engine; const ray = make_ray_from_viewport_position(engine, new Vector2(viewport_x, viewport_y)); // console.log(`Ray origin:${ray.origin}, direction:${ray.direction}`); this.#camera.rayPlaneIntersection( out, ray.origin.x, ray.origin.y, ray.origin.z, ray.direction.x, ray.direction.y, ray.direction.z, plane ); } /** * * @param {Vector2} screen_position */ update(screen_position) { // console.log(`PerfectPanner/update: ${screen_position}`) const C = this.#grab_world_reference; const A = this.#grab_eye_position; const E = this.#grab_far_projection; // const AC = new Vector3(); AC.subVectors(A, C); const EC = new Vector3(); EC.subVectors(E, C); const D = new Vector3(); this.#projection_viewport_onto_plane(D, screen_position.x, screen_position.y, FAR_PLANE_INDEX); const translation = new Vector3(); translation.subVectors(E, D); translation.multiplyScalar(AC.length() / EC.length()); this.#translation.copy(translation); // console.log(`translation: ${translation}`); } start(screen_position) { // console.log(`PerfectPanner/start: ${screen_position}`) // obtain reference point const engine = this.#engine; const ecd = engine.entityManager.dataset; if (ecd === null) { throw new Error('No dataset'); } // bind camera const camera = CameraSystem.getFirstActiveCamera(ecd); this.#camera = camera.component; this.#camera_transform = ecd.getComponent(camera.entity, Transform); this.#projection_viewport_onto_plane(this.#grab_near_projection, screen_position.x, screen_position.y, NEAR_PLANE_INDEX); this.#projection_viewport_onto_plane(this.#grab_far_projection, screen_position.x, screen_position.y, FAR_PLANE_INDEX); const ray = make_ray_from_viewport_position(engine, screen_position); const terrain = obtainTerrain(ecd); const hit = new SurfacePoint3(); if (terrain.raycastFirstSync(hit, ray.origin.x, ray.origin.y, ray.origin.z, ray.direction.x, ray.direction.y, ray.direction.z )) { // got terrain hit this.#grab_world_reference.copy(hit.position); } this.#grab_eye_position.copy(this.#camera_transform.position); this.#translation.set(0, 0, 0); } end() { } }