mylingo3d
Version:
Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor
401 lines • 15.3 kB
JavaScript
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