@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
212 lines (161 loc) • 5.63 kB
JavaScript
import View from "../../View.js";
import Vector3 from "../../../core/geom/Vector3.js";
import ObservedValue from "../../../core/model/ObservedValue.js";
import { SurfacePoint3 } from "../../../core/geom/3d/SurfacePoint3.js";
import SVG from "../../SVG.js";
import { plane3_compute_ray_intersection } from "../../../core/geom/3d/plane/plane3_compute_ray_intersection.js";
const rayContact = new SurfacePoint3();
export class MinimapCameraView extends View {
/**
*
* @param {Camera} camera
* @param {Transform} transform
* @param entity
* @param {Rectangle} world
* @param {Vector2} worldScale
* @constructor
*/
constructor({ camera, transform, entity, world, worldScale }) {
super();
this.el = SVG.createElement('svg');
this.addClass('ui-camera-view');
/**
*
* @type {Element}
* @private
*/
this.__el_polygon = SVG.createElement('polygon');
this.el.appendChild(this.__el_polygon);
/**
*
* @type {Camera}
*/
this.camera = camera;
/**
*
* @type {Transform}
*/
this.transform = transform;
this.entity = entity;
/**
*
* @type {Rectangle}
* @private
*/
this.__world = world;
/**
*
* @type {Vector2}
* @private
*/
this.__world_scale = worldScale;
/**
*
* @type {ObservedValue<Terrain>}
*/
this.terrain = new ObservedValue(null);
this
.bindSignal(this.camera.projectionType.onChanged, this.__update, this)
.bindSignal(this.transform.position.onChanged, this.__update, this)
.bindSignal(worldScale.onChanged, this.__update, this)
.bindSignal(world.position.onChanged, this.__update, this)
.bindSignal(this.transform.rotation.onChanged, this.__update, this);
}
/**
*
* @param {Vector3} result
* @param {number} originX
* @param {number} originY
* @param {number} originZ
* @param {number} directionX
* @param {number} directionY
* @param {number} directionZ
*/
castTerrainRay(result, originX, originY, originZ, directionX, directionY, directionZ) {
const terrain = this.terrain.getValue();
const JITTER = 0.00001;
if (terrain === null) {
plane3_compute_ray_intersection(result, originX, originY, originZ, directionX, directionY, directionZ, 0, 1, 0, 0);
} else {
let oX = originX;
let oY = originY;
let oZ = originZ;
let foundHit = false;
for (let i = 0; i < 10 && !foundHit; i++) {
foundHit = terrain.raycastFirstSync(rayContact, originX, originY, originZ, directionX, directionY, directionZ);
if (!foundHit) {
//no hit found, try to jitter origin in case of mathematical error at polygon edges
oX += (Math.random() - 0.5) * JITTER;
oY += (Math.random() - 0.5) * JITTER;
oZ += (Math.random() - 0.5) * JITTER;
} else {
result.copy(rayContact.position);
}
}
if (!foundHit) {
//no hit found, fall back to planar test
plane3_compute_ray_intersection(result, originX, originY, originZ, directionX, directionY, directionZ, 0, 1, 0, 0);
}
}
}
/**
*
* @param {number} x
* @param {number} y
* @returns {Vector3}
*/
getPoint(x, y) {
const camera = this.camera;
/**
*
* @type {Rectangle}
*/
const world = this.__world;
/**
*
* @type {Vector2}
*/
const worldScale = this.__world_scale;
const vOrigin = new Vector3(-1, 1, 0.5);
const vDirection = new Vector3(1, -1, 0.5);
camera.projectRay(x, y, vOrigin, vDirection);
const vResult = new Vector3(0, 0, 0);
this.castTerrainRay(vResult, vOrigin.x, vOrigin.y, vOrigin.z, vDirection.x, vDirection.y, vDirection.z);
vResult.x -= world.position.x;
vResult.x *= worldScale.x;
vResult.z -= world.position.y;
vResult.z *= worldScale.y;
return vResult;
}
__update() {
const camera = this.camera;
/**
*
* @type {Transform}
*/
const transform = this.transform;
const c = camera.object;
if (c === null) {
console.warn(`Camera component doesn't have three.js object built.`);
return;
}
c.position.copy(transform.position);
camera.updateMatrices();
const v_tl = this.getPoint(-1, 1);
const v_tr = this.getPoint(1, 1);
const v_br = this.getPoint(1, -1);
const v_bl = this.getPoint(-1, -1);
this.__el_polygon.setAttribute('points', `${v_tl.x},${v_tl.z} ${v_tr.x},${v_tr.z} ${v_br.x},${v_br.z} ${v_bl.x},${v_bl.z}`);
};
link() {
super.link();
try {
this.__update();
} catch (e) {
console.error('Failed to execute update', e);
}
}
unlink() {
super.unlink();
}
}