UNPKG

mylingo3d

Version:

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

401 lines 15.3 kB
import { distance3d, Point3d } from "@lincode/math"; import { Color, CubeCamera, Matrix3, MeshToonMaterial, PropertyBinding, WebGLCubeRenderTarget } from "three"; import { frustum, matrix4, ray, vector3, vector3_1, vector3_half } from "../../utils/reusables"; import { forceGet, throttle } from "@lincode/utils"; import { OBB } from "three/examples/jsm/math/OBB"; import { scaleDown, scaleUp } from "../../../engine/constants"; import { addBloom, deleteBloom } from "../../../engine/renderLoop/effectComposer/selectiveBloomPass/renderSelectiveBloom"; import worldToClient from "../../utils/worldToClient"; import { Cancellable } from "@lincode/promiselikes"; import { point2Vec, vec2Point } from "../../utils/vec2Point"; import { addOutline, deleteOutline } from "../../../engine/renderLoop/effectComposer/outlinePass"; import getCenter from "../../utils/getCenter"; import EventLoopItem from "../../../api/core/EventLoopItem"; import copyToon from "./applyMaterialProperties/copyToon"; import { getCameraRendered } from "../../../states/useCameraRendered"; import { onBeforeRender } from "../../../events/onBeforeRender"; import diffQuaternions from "../../utils/diffQuaternions"; import getWorldPosition from "../../utils/getWorldPosition"; import getWorldDirection from "../../utils/getWorldDirection"; import { clickSet, mouseDownSet, mouseUpSet, mouseOverSet, mouseOutSet, mouseMoveSet } from "./raycast/sets"; import "./raycast"; import fpsAlpha from "../../utils/fpsAlpha"; import { createEffect } from "@lincode/reactivity"; import scene from "../../../engine/scene"; import { getRenderer } from "../../../states/useRenderer"; import { FAR, NEAR } from "../../../globals"; import { onRenderSlow } from "../../../events/onRenderSlow"; import { onRenderSlowest } from "../../../events/onRenderSlowest"; const thisOBB = new OBB(); const targetOBB = new OBB(); const updateFrustum = throttle(() => { const camera = getCameraRendered(); frustum.setFromProjectionMatrix(matrix4.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)); }, 200, "leading"); const setNumber = (material, property, factor) => { const defaultValue = (material.userData[property] ??= material[property]); material[property] = factor === undefined ? defaultValue : Math.max(defaultValue || 0, 0.25) * factor; }; const setProperty = (material, property, value) => { const defaultValue = (material.userData[property] ??= material[property]); material[property] = value === undefined ? defaultValue : value; }; export const idMap = new Map(); const makeSet = () => new Set(); export default class StaticObjectManager extends EventLoopItem { dispose() { if (this.done) return this; super.dispose(); this._id !== undefined && idMap.get(this._id).delete(this); return this; } _id; get id() { return this._id; } set id(val) { this._id !== undefined && idMap.get(this._id).delete(this); this._id = val; val !== undefined && forceGet(idMap, val, makeSet).add(this); } addToRaycastSet(set) { set.add(this.nativeObject3d); return new Cancellable(() => set.delete(this.nativeObject3d)); } _onClick; get onClick() { return this._onClick; } set onClick(cb) { this._onClick = cb; this.cancelHandle("onClick", cb && (() => this.addToRaycastSet(clickSet))); } _onMouseDown; get onMouseDown() { return this._onMouseDown; } set onMouseDown(cb) { this._onMouseDown = cb; this.cancelHandle("onMouseDown", cb && (() => this.addToRaycastSet(mouseDownSet))); } _onMouseUp; get onMouseUp() { return this._onMouseUp; } set onMouseUp(cb) { this._onMouseUp = cb; this.cancelHandle("onMouseUp", cb && (() => this.addToRaycastSet(mouseUpSet))); } _onMouseOver; get onMouseOver() { return this._onMouseOver; } set onMouseOver(cb) { this._onMouseOver = cb; this.cancelHandle("onMouseOver", cb && (() => this.addToRaycastSet(mouseOverSet))); } _onMouseOut; get onMouseOut() { return this._onMouseOut; } set onMouseOut(cb) { this._onMouseOut = cb; this.cancelHandle("onMouseOut", cb && (() => this.addToRaycastSet(mouseOutSet))); } _onMouseMove; get onMouseMove() { return this._onMouseMove; } set onMouseMove(cb) { this._onMouseMove = cb; this.cancelHandle("onMouseMove", cb && (() => this.addToRaycastSet(mouseMoveSet))); } get name() { return this.outerObject3d.name; } set name(val) { this.outerObject3d.name = PropertyBinding.sanitizeNodeName(val); } getRay() { return ray.set(getWorldPosition(this.nativeObject3d), getWorldDirection(this.nativeObject3d)); } pointAt(distance) { return vec2Point(this.getRay().at(distance * scaleDown, vector3)); } rayIntersectsAt(target, maxDistance) { if (this.done) return undefined; if (target.done) return undefined; if (this === target) return undefined; targetOBB.set(getWorldPosition(target.nativeObject3d), vector3_half, new Matrix3().setFromMatrix4(target.nativeObject3d.matrixWorld)); const vec = targetOBB.intersectRay(this.getRay(), vector3); if (!vec) return; if (maxDistance) { const { x, y, z } = getWorldPosition(this.nativeObject3d); if (distance3d(vec.x, vec.y, vec.z, x, y, z) * scaleUp > maxDistance) return; } return vec2Point(vec); } rayIntersects(target) { return !!this.rayIntersectsAt(target); } intersects(target) { if (this.done) return false; if (target.done) return false; if (this === target) return false; thisOBB.set(getWorldPosition(this.nativeObject3d), vector3_1.clone(), new Matrix3()); thisOBB.applyMatrix4(this.nativeObject3d.matrixWorld); targetOBB.set(getWorldPosition(target.nativeObject3d), vector3_1.clone(), new Matrix3()); targetOBB.applyMatrix4(target.nativeObject3d.matrixWorld); return thisOBB.intersectsOBB(targetOBB, 0); } get clientX() { return worldToClient(this.nativeObject3d).x; } get clientY() { return worldToClient(this.nativeObject3d).y; } get bloom() { return !!this.outerObject3d.userData.bloom; } set bloom(val) { val && addBloom(this.outerObject3d); this.cancelHandle("bloom", val && (() => new Cancellable(() => deleteBloom(this.outerObject3d)))); } get outline() { return !!this.nativeObject3d.userData.outline; } set outline(val) { val && addOutline(this.nativeObject3d); this.cancelHandle("outline", val && (() => new Cancellable(() => deleteOutline(this.nativeObject3d)))); } _visible; get visible() { return this._visible !== false; } set visible(val) { this._visible = val; this.outerObject3d.visible = val; } get frustumCulled() { return this.outerObject3d.frustumCulled; } set frustumCulled(val) { this.outerObject3d.frustumCulled = val; this.outerObject3d.traverse((child) => (child.frustumCulled = val)); } _castShadow; get castShadow() { return this._castShadow ?? true; } set castShadow(val) { this._castShadow = val; this.outerObject3d.traverse((child) => (child.castShadow = val)); } _receiveShadow; get receiveShadow() { return this._receiveShadow ?? true; } set receiveShadow(val) { this._receiveShadow = val; this.outerObject3d.traverse((child) => (child.receiveShadow = val)); } refreshFactors() { this.cancelHandle("refreshFactors", () => { const handle = new Cancellable(); //@ts-ignore "tryCloneMaterial" in this && this.tryCloneMaterial(); queueMicrotask(() => { if (handle.done) return; const { _toon, _metalnessFactor, _roughnessFactor, _opacityFactor, _envFactor, _adjustColor, _reflection, _illumination } = this; let reflectionTexture; if (!_toon && (_reflection || _illumination)) { const cubeRenderTarget = new WebGLCubeRenderTarget(_reflection ? 256 : 16); reflectionTexture = cubeRenderTarget.texture; handle.then(() => { cubeRenderTarget.dispose(); reflectionTexture = undefined; }); const cubeCamera = new CubeCamera(NEAR, _reflection ? FAR : 5, cubeRenderTarget); handle.watch(createEffect(() => { const renderer = getRenderer(); if (!renderer) return; const onRender = _reflection ? onRenderSlow : onRenderSlowest; const handle = onRender(() => { cubeCamera.position.copy(getWorldPosition(this.outerObject3d)); this.outerObject3d.visible = false; cubeCamera.update(renderer, scene); this.outerObject3d.visible = true; }); return () => { handle.cancel(); }; }, [getRenderer])); } this.outerObject3d.traverse((child) => { let material = child.material; if (!material) return; Array.isArray(material) && (material = material[0]); if (_toon) { child.material = new MeshToonMaterial(); copyToon(material, child.material); handle.then(() => { child.material.dispose(); child.material = material; }); return; } if (_metalnessFactor !== undefined) setNumber(material, "metalness", _metalnessFactor !== 0 ? _metalnessFactor : undefined); if (_roughnessFactor !== undefined) setNumber(material, "roughness", _roughnessFactor !== 1 ? _roughnessFactor : undefined); if (_opacityFactor !== undefined) { setNumber(material, "opacity", _opacityFactor); setProperty(material, "transparent", _opacityFactor <= 1 ? true : undefined); } if (_envFactor !== undefined) setNumber(material, "envMapIntensity", _envFactor !== 1 ? _envFactor : undefined); if (_adjustColor !== undefined) setProperty(material, "color", _adjustColor !== "#ffffff" ? new Color(_adjustColor) : undefined); if (_reflection !== undefined || _illumination !== undefined) setProperty(material, "envMap", reflectionTexture); }); }); return handle; }); } _metalnessFactor; get metalnessFactor() { return this._metalnessFactor; } set metalnessFactor(val) { this._metalnessFactor = val; this.refreshFactors(); } _roughnessFactor; get roughnessFactor() { return this._roughnessFactor; } set roughnessFactor(val) { this._roughnessFactor = val; this.refreshFactors(); } _opacityFactor; get opacityFactor() { return this._opacityFactor; } set opacityFactor(val) { this._opacityFactor = val; this.refreshFactors(); } _envFactor; get envFactor() { return this._envFactor; } set envFactor(val) { this._envFactor = val; this.refreshFactors(); } _adjustColor; get adjustColor() { return this._adjustColor; } set adjustColor(val) { this._adjustColor = val; this.refreshFactors(); } _reflection; get reflection() { return this._reflection ?? false; } set reflection(val) { this._reflection = val; this.refreshFactors(); } _illumination; get illumination() { return this._illumination ?? false; } set illumination(val) { this._illumination = val; this.refreshFactors(); } _toon; get toon() { return this._toon ?? false; } set toon(val) { this._toon = val; this.refreshFactors(); } get frustumVisible() { updateFrustum(); return frustum.containsPoint(getCenter(this.nativeObject3d)); } lookAt(a0, a1, a2) { if (typeof a0 === "number") { this.lookAt(new Point3d(a0, a1 === undefined ? this.outerObject3d.position.y * scaleUp : a1, a2)); return; } if ("outerObject3d" in a0) this.outerObject3d.lookAt(getWorldPosition(a0.nativeObject3d)); else this.outerObject3d.lookAt(point2Vec(a0)); } onLookToEnd; lookTo(a0, a1, a2, a3) { if (typeof a0 === "number") { this.lookTo(new Point3d(a0, a1 === undefined ? this.outerObject3d.position.y * scaleUp : a1, a2), a3); return; } const { quaternion } = this.outerObject3d; const quaternionOld = quaternion.clone(); this.lookAt(a0); const quaternionNew = quaternion.clone(); quaternion.copy(quaternionOld); this.cancelHandle("lookTo", () => onBeforeRender(() => { quaternion.slerp(quaternionNew, fpsAlpha(a1)); const { x, y, z } = diffQuaternions(quaternion, quaternionNew); if (Math.abs(x) + Math.abs(y) + Math.abs(z) < 0.001) { this.cancelHandle("lookTo", undefined); this.onLookToEnd?.(); quaternion.copy(quaternionNew); } })); } getWorldPosition() { return vec2Point(getWorldPosition(this.nativeObject3d)); } } //# sourceMappingURL=index.js.map