UNPKG

mylingo3d

Version:

Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor

258 lines 9.49 kB
import { rad2Deg, deg2Rad, distance3d, vertexAngle, Point, rotatePoint, quadrant } from "@lincode/math"; import { Vector3 } from "three"; import { vector3 } from "../../utils/reusables"; import { scaleDown, scaleUp } from "../../../engine/constants"; import { point2Vec } from "../../utils/vec2Point"; import getCenter from "../../utils/getCenter"; import PositionedItem from "../../../api/core/PositionedItem"; import { idMap } from "../StaticObjectManager"; import { applyMixins } from "@lincode/utils"; import { Reactive } from "@lincode/reactivity"; import { onBeforeRender } from "../../../events/onBeforeRender"; import getWorldPosition from "../../utils/getWorldPosition"; import getWorldQuaternion from "../../utils/getWorldQuaternion"; import { getCentripetal } from "../../../states/useCentripetal"; import AnimatedObjectManager from "../AnimatedObjectManager"; import getActualScale from "../../utils/getActualScale"; import { fpsRatio } from "../../../engine/eventLoop"; import fpsAlpha from "../../utils/fpsAlpha"; const ptDistCache = new WeakMap(); const distance3dCached = (pt, vecSelf) => { const cached = ptDistCache.get(pt); if (cached) return cached; const result = distance3d(pt.x, pt.y, pt.z, vecSelf.x * scaleUp, vecSelf.y * scaleUp, vecSelf.z * scaleUp); ptDistCache.set(pt, result); return result; }; class SimpleObjectManager extends AnimatedObjectManager { getRayIntersectionsAt(id, maxDistance) { const result = []; for (const target of idMap.get(id) ?? []) { if (target === this) continue; const pt = this.rayIntersectsAt(target, maxDistance); pt && result.push([target, pt]); } const vec = getWorldPosition(this.nativeObject3d); return result.sort((a, b) => { return distance3dCached(a[1], vec) - distance3dCached(b[1], vec); }); } getRayIntersections(id, maxDistance) { return this.getRayIntersectionsAt(id, maxDistance).map((result) => result[0]); } listenToRayIntersection(id, cb, maxDistance) { return this.beforeRender(() => { for (const [target, pt] of this.getRayIntersectionsAt(id, maxDistance)) cb(target, pt); }); } getIntersections(id) { const result = []; for (const target of idMap.get(id) ?? []) { if (target === this) continue; this.intersects(target) && result.push(target); } return result; } listenToIntersection(id, cb, cbOut) { let intersectionsOld = []; return this.beforeRender(() => { const intersections = this.getIntersections(id); if (cb) for (const target of intersections) if (!intersectionsOld.includes(target)) cb(target); if (cbOut) for (const target of intersectionsOld) if (!intersections.includes(target)) cbOut(target); intersectionsOld = intersections; }); } onIntersectState; onIntersectOutState; intersectIdsState; initIntersect() { if (this.onIntersectState) return; this.onIntersectState = new Reactive(undefined); this.onIntersectOutState = new Reactive(undefined); this.intersectIdsState = new Reactive(undefined); this.createEffect(() => { const { onIntersect, onIntersectOut, intersectIds } = this; if (!intersectIds || (!onIntersect && !onIntersectOut)) return; const handles = []; for (const id of intersectIds) handles.push(this.listenToIntersection(id, onIntersect, onIntersectOut)); return () => { for (const handle of handles) handle.cancel(); }; }, [ this.onIntersectState.get, this.onIntersectOutState.get, this.intersectIdsState.get ]); } get onIntersect() { return this.onIntersectState?.get(); } set onIntersect(val) { this.initIntersect(); this.onIntersectState?.set(val); } get onIntersectOut() { return this.onIntersectOutState?.get(); } set onIntersectOut(val) { this.initIntersect(); this.onIntersectOutState?.set(val); } get intersectIds() { return this.intersectIdsState?.get(); } set intersectIds(val) { this.initIntersect(); this.intersectIdsState?.set(val); } get scaleX() { return this.outerObject3d.scale.x; } set scaleX(val) { this.outerObject3d.scale.x = val; } get scaleY() { return this.outerObject3d.scale.y; } set scaleY(val) { this.outerObject3d.scale.y = val; } get scaleZ() { return this.outerObject3d.scale.z; } set scaleZ(val) { this.outerObject3d.scale.z = val; } get scale() { return this.scaleX; } set scale(val) { this.scaleX = val; this.scaleY = val; this.scaleZ = val; } get rotationX() { return this.outerObject3d.rotation.x * rad2Deg; } set rotationX(val) { this.outerObject3d.rotation.x = val * deg2Rad; } get rotationY() { return this.outerObject3d.rotation.y * rad2Deg; } set rotationY(val) { this.outerObject3d.rotation.y = val * deg2Rad; } get rotationZ() { return this.outerObject3d.rotation.z * rad2Deg; } set rotationZ(val) { this.outerObject3d.rotation.z = val * deg2Rad; } get rotation() { return this.rotationZ; } set rotation(val) { this.rotationZ = val; } translateX(val) { this.outerObject3d.translateX(val * scaleDown * fpsRatio[0]); } translateY(val) { this.outerObject3d.translateY(val * scaleDown * fpsRatio[0]); } translateZ(val) { this.outerObject3d.translateZ(val * scaleDown * fpsRatio[0]); } placeAt(object) { if (typeof object === "string") { const [found] = idMap.get(object) ?? [undefined]; if (!found) return; object = found; } if ("outerObject3d" in object) { if ("isSpawnPoint" in object) object.object3d.position.y = getActualScale(this).y * 0.5; this.outerObject3d.position.copy(getCenter(object.nativeObject3d)); this.outerObject3d.quaternion.copy(getWorldQuaternion(object.outerObject3d)); return; } this.outerObject3d.position.copy(point2Vec(object)); } moveForward(distance) { if (getCentripetal()) this.translateZ(-distance); else { vector3.setFromMatrixColumn(this.outerObject3d.matrix, 0); vector3.crossVectors(this.outerObject3d.up, vector3); this.outerObject3d.position.addScaledVector(vector3, distance * scaleDown * fpsRatio[0]); } } moveRight(distance) { if (getCentripetal()) this.translateX(distance); else { vector3.setFromMatrixColumn(this.outerObject3d.matrix, 0); this.outerObject3d.position.addScaledVector(vector3, distance * scaleDown * fpsRatio[0]); } } onMoveToEnd; lerpTo(x, y, z, alpha, onFrame) { const from = new Vector3(this.x, this.y, this.z); const to = new Vector3(x, y, z); this.cancelHandle("lerpTo", () => onBeforeRender(() => { const { x, y, z } = from.lerp(to, fpsAlpha(alpha)); if (Math.abs(this.x - x) < 0.1 && Math.abs(this.y - y) < 0.1 && Math.abs(this.z - z) < 0.1) { this.cancelHandle("lerpTo", undefined); this.onMoveToEnd?.(); } this.x = x; this.y = y; this.z = z; onFrame?.(); })); } moveTo(x, y, z, speed, onFrame) { if (x === this.x) x += 0.01; if (z === this.z) z += 0.01; const { x: rx, y: ry, z: rz } = new Vector3(x - this.x, y === undefined ? 0 : y - this.y, z - this.z).normalize(); const sx = speed * rx; const sy = speed * ry; const sz = speed * rz; const quad = quadrant(x, z, this.x, this.z); this.cancelHandle("lerpTo", () => onBeforeRender(() => { this.x += sx * fpsRatio[0]; y !== undefined && (this.y += sy * fpsRatio[0]); this.z += sz * fpsRatio[0]; const angle = vertexAngle(new Point(this.x, this.z), new Point(x, z), new Point(this.x, z)); const rotated = rotatePoint(new Point(x, z), new Point(this.x, this.z), quad === 1 || quad === 4 ? angle : -angle); if (z > rotated.y) { this.cancelHandle("lerpTo", undefined); this.onMoveToEnd?.(); } onFrame?.(y); })); } } applyMixins(SimpleObjectManager, [PositionedItem]); export default SimpleObjectManager; //# sourceMappingURL=index.js.map