@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
171 lines (129 loc) • 4.55 kB
JavaScript
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() {
}
}