@tolokoban/tgd
Version:
ToloGameDev library for WebGL2
189 lines • 15.6 kB
JavaScript
/* eslint-disable unicorn/prevent-abbreviations */
import { TgdCameraPerspective } from "./../../camera/index.js";
import { TgdContext } from "./../../context/index.js";
import { TgdControllerCameraOrbit } from "./../../controller/index.js";
import { TgdEvent } from "./../../event/index.js";
import { TgdQuat, TgdMat3, TgdVec3 } from "./../../math/index.js";
import { TgdPainterClear, TgdPainterDepth, TgdPainterLogic } from "./../../painter/index.js";
import { TipsPainter } from "./painter/tips.js";
/**
* The Gizmo displays the orientation of an attached camera.
* You can click one of the axis to force the camera to face it.
*/
export class TgdCanvasGizmo {
constructor(options = {}) {
this.options = options;
/**
* The user clicked a tip, so we dispatch the target orientation.
*/
this.eventTipClick = new TgdEvent();
this._canvas = null;
this.context = null;
this.contextExternal = null;
this.cameraInternal = new TgdCameraPerspective({
fovy: Math.PI / 3,
near: 0.01,
far: 10,
transfo: { distance: 2.7 },
});
this.orbiter = null;
this.tipsPainter = null;
this.handleExternalPaint = () => {
var _a;
(_a = this.context) === null || _a === void 0 ? void 0 : _a.paint();
};
this.handleInternalToExternal = (internalCamera) => {
const { contextExternal } = this;
if (contextExternal === null || contextExternal === void 0 ? void 0 : contextExternal.camera) {
contextExternal.camera.transfo.orientation =
internalCamera.transfo.orientation;
contextExternal.paint();
}
};
this.handleTap = (event) => {
var _a;
const camera = (_a = this.context) === null || _a === void 0 ? void 0 : _a.camera;
if (!camera)
return;
const { origin, direction } = camera.castRay(event.x, event.y);
const maxDistance = 1;
let bestDistance = maxDistance;
let bestTip = TIPS[0];
for (const tip of TIPS) {
const distance = tip.distanceToLineSquared(origin, direction);
if (distance < bestDistance) {
bestDistance = distance;
bestTip = tip;
}
}
if (bestDistance < maxDistance) {
const axisX = new TgdVec3();
const axisY = new TgdVec3();
const axisZ = bestTip;
axisX.from(this.findAxisX());
if (axisX.isClose(axisZ)) {
axisY.from(this.findAxisY());
axisX.from(axisY).cross(axisZ);
}
else {
axisY.from(axisZ).cross(axisX);
}
const quat = new TgdQuat().fromAxes(axisX, axisY, axisZ);
if (quat.isEqual(camera.transfo.orientation)) {
quat.rotateAroundY(Math.PI);
}
this.eventTipClick.dispatch({
from: camera.transfo.orientation,
to: quat,
});
}
};
if (options.canvas)
this.canvas = options.canvas;
}
/**
* Attach the camera we want to track and control.
*/
attachContext(context) {
this.detach();
this.contextExternal = context;
this.attach();
}
detach() {
if (!this.contextExternal)
return;
this.contextExternal.eventPaint.removeListener(this.handleExternalPaint);
this.contextExternal = null;
}
attach() {
var _a, _b;
(_a = this.contextExternal) === null || _a === void 0 ? void 0 : _a.eventPaint.addListener(this.handleExternalPaint);
(_b = this.context) === null || _b === void 0 ? void 0 : _b.paint();
}
get canvas() {
return this._canvas;
}
set canvas(canvas) {
var _a;
if (canvas === this._canvas)
return;
this._canvas = canvas;
if (this.context) {
this.context.inputs.pointer.eventTap.removeListener(this.handleTap);
this.context.destroy();
this.context = null;
const { orbiter } = this;
if (orbiter) {
orbiter.detach();
orbiter.eventChange.removeListener(this.handleInternalToExternal);
}
}
(_a = this.tipsPainter) === null || _a === void 0 ? void 0 : _a.delete();
if (!canvas)
return;
const context = new TgdContext(canvas, Object.assign({ alpha: true, depth: true, antialias: true, name: "GizmoCanvas" }, this.options));
context.inputs.pointer.eventTap.addListener(this.handleTap);
this.context = context;
context.camera = this.cameraInternal;
this.orbiter = new TgdControllerCameraOrbit(context, {
speedPanning: 0,
speedZoom: 0,
});
this.orbiter.eventChange.addListener(this.handleInternalToExternal);
const painter = new TipsPainter(context);
this.tipsPainter = painter;
context.add(new TgdPainterLogic(() => {
var _a, _b;
const srcTransfo = (_a = this.contextExternal) === null || _a === void 0 ? void 0 : _a.camera.transfo;
if (!srcTransfo)
return;
const dstTransfo = (_b = this.context) === null || _b === void 0 ? void 0 : _b.camera.transfo;
if (!dstTransfo)
return;
dstTransfo.orientation = srcTransfo.orientation;
}), new TgdPainterClear(context, {
color: [0, 0, 0, 0],
depth: 1,
}), new TgdPainterDepth(context, { enabled: true }), painter);
context.paint();
}
findAxisX() {
let bestScore = 0;
let bestTip = TIPS[0];
const vec = new TgdVec3();
const mat = new TgdMat3();
mat.fromQuat(this.cameraInternal.transfo.orientation).transpose();
for (const tip of TIPS) {
vec.from(tip).applyMatrix(mat);
if (vec.x > bestScore) {
bestScore = vec.x;
bestTip = tip;
}
}
return bestTip;
}
findAxisY() {
let bestScore = 0;
let bestTip = TIPS[0];
const vec = new TgdVec3();
const mat = new TgdMat3();
mat.fromQuat(this.cameraInternal.transfo.orientation).transpose();
for (const tip of TIPS) {
vec.from(tip).applyMatrix(mat);
if (vec.y > bestScore) {
bestScore = vec.y;
bestTip = tip;
}
}
return bestTip;
}
}
const TIPS = [
new TgdVec3(1, 0, 0),
new TgdVec3(0, 1, 0),
new TgdVec3(0, 0, 1),
new TgdVec3(-1, 0, 0),
new TgdVec3(0, -1, 0),
new TgdVec3(0, 0, -1),
];
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2l6bW8uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY2FudmFzL2dpem1vL2dpem1vLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGtEQUFrRDtBQUNsRCxPQUFPLEVBQWEsb0JBQW9CLEVBQUUsTUFBTSxhQUFhLENBQUE7QUFDN0QsT0FBTyxFQUFFLFVBQVUsRUFBcUIsTUFBTSxjQUFjLENBQUE7QUFDNUQsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0saUJBQWlCLENBQUE7QUFDMUQsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUNyQyxPQUFPLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFDckQsT0FBTyxFQUFFLGVBQWUsRUFBRSxlQUFlLEVBQUUsZUFBZSxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBRWhGLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQTtBQU01Qzs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sY0FBYztJQXFCdkIsWUFBNkIsVUFBaUMsRUFBRTtRQUFuQyxZQUFPLEdBQVAsT0FBTyxDQUE0QjtRQXBCaEU7O1dBRUc7UUFDSSxrQkFBYSxHQUFHLElBQUksUUFBUSxFQUcvQixDQUFBO1FBRUksWUFBTyxHQUE2QixJQUFJLENBQUE7UUFDeEMsWUFBTyxHQUFzQixJQUFJLENBQUE7UUFDakMsb0JBQWUsR0FBc0IsSUFBSSxDQUFBO1FBQ2hDLG1CQUFjLEdBQUcsSUFBSSxvQkFBb0IsQ0FBQztZQUN2RCxJQUFJLEVBQUUsSUFBSSxDQUFDLEVBQUUsR0FBRyxDQUFDO1lBQ2pCLElBQUksRUFBRSxJQUFJO1lBQ1YsR0FBRyxFQUFFLEVBQUU7WUFDUCxPQUFPLEVBQUUsRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFO1NBQzdCLENBQUMsQ0FBQTtRQUNNLFlBQU8sR0FBb0MsSUFBSSxDQUFBO1FBQy9DLGdCQUFXLEdBQXVCLElB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