mylingo3d
Version:
Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor
313 lines (273 loc) • 8.71 kB
text/typescript
import { Group, Material, Mesh, Object3D } from "three"
import { boxGeometry } from "../primitives/Cube"
import { wireframeMaterial } from "../utils/reusables"
import ObjectManager from "./ObjectManager"
import ILoaded from "../../interface/ILoaded"
import {
addOutline,
deleteOutline
} from "../../engine/renderLoop/effectComposer/outlinePass"
import {
addBloom,
deleteBloom
} from "../../engine/renderLoop/effectComposer/selectiveBloomPass/renderSelectiveBloom"
import Reresolvable from "./utils/Reresolvable"
import { Cancellable } from "@lincode/promiselikes"
import toResolvable from "../utils/toResolvable"
import MeshItem from "./MeshItem"
import { Point3d } from "@lincode/math"
export default abstract class Loaded<T = Object3D>
extends ObjectManager<Mesh>
implements ILoaded
{
public loadedGroup = new Group()
public constructor(unmounted?: boolean) {
super(new Mesh(boxGeometry, wireframeMaterial), unmounted)
this.outerObject3d.add(this.loadedGroup)
}
public loaded = new Reresolvable<Object3D>()
protected abstract load(src: string): Promise<T>
protected abstract resolveLoaded(data: T, src: string): Group
protected _src?: string
public get src() {
return this._src
}
public set src(val) {
this._src = val
this.loaded.done && this.loadedGroup.clear()
this.cancelHandle(
"src",
val &&
(() =>
toResolvable(this.load(val)).then((loaded) => {
const loadedObject3d = this.resolveLoaded(loaded, val)
this.loadedGroup.add(loadedObject3d)
this.loaded.resolve(loadedObject3d)
this.object3d.visible = !!this._boxVisible
}))
)
}
private _onLoad?: () => void
public get onLoad() {
return this._onLoad
}
public set onLoad(cb) {
this._onLoad = cb
this.cancelHandle(
"onLoad",
cb && (() => this.loaded.then(() => void cb()))
)
}
protected widthSet?: boolean
public override get width() {
return super.width
}
public override set width(val) {
super.width = val
this.widthSet = true
}
protected heightSet?: boolean
public override get height() {
return super.height
}
public override set height(val) {
super.height = val
this.heightSet = true
}
protected depthSet?: boolean
public override get depth() {
return super.depth
}
public override set depth(val) {
super.depth = val
this.depthSet = true
}
public override get innerRotationX() {
return super.innerRotationX
}
public override set innerRotationX(val) {
super.innerRotationX = val
this.loadedGroup.rotation.x = this.object3d.rotation.x
}
public override get innerRotationY() {
return super.innerRotationY
}
public override set innerRotationY(val) {
super.innerRotationY = val
this.loadedGroup.rotation.y = this.object3d.rotation.y
}
public override get innerRotationZ() {
return super.innerRotationZ
}
public override set innerRotationZ(val) {
super.innerRotationZ = val
this.loadedGroup.rotation.z = this.object3d.rotation.z
}
public override get innerX() {
return super.innerX
}
public override set innerX(val) {
super.innerX = val
this.loadedGroup.position.x = this.object3d.position.x
}
public override get innerY() {
return super.innerY
}
public override set innerY(val) {
super.innerY = val
this.loadedGroup.position.y = this.object3d.position.y
}
public override get innerZ() {
return super.innerZ
}
public override set innerZ(val) {
super.innerZ = val
this.loadedGroup.position.z = this.object3d.position.z
}
public override get innerVisible() {
return this.loadedGroup.visible
}
public override set innerVisible(val) {
this.loadedGroup.visible = val
}
public override get frustumCulled() {
return super.frustumCulled
}
public override set frustumCulled(val) {
this.outerObject3d.frustumCulled = val
this.cancelHandle("frustumCulled", () =>
this.loaded.then(() => {
super.frustumCulled = val
})
)
}
public override get castShadow() {
return super.castShadow
}
public override set castShadow(val) {
this._castShadow = val
this.cancelHandle("castShadow", () =>
this.loaded.then(() => {
super.castShadow = val
})
)
}
public override get receiveShadow() {
return super.receiveShadow
}
public override set receiveShadow(val) {
this._receiveShadow = val
this.cancelHandle("receiveShadow", () =>
this.loaded.then(() => {
super.receiveShadow = val
})
)
}
public override get physics() {
return super.physics
}
public override set physics(val) {
this._physics = val
const handle = this.cancelHandle("physics", () =>
this.loaded.then(() => {
this.initPhysics(val, handle!)
})
)
}
private _boxVisible?: boolean
public get boxVisible() {
return this._boxVisible ?? this.object3d.visible
}
public set boxVisible(val) {
this._boxVisible = val
this.object3d.visible = val
}
private _outline?: boolean
public override get outline() {
return !!this._outline
}
public override set outline(val) {
this._outline = val
this.cancelHandle("outline", () =>
this.loaded.then((loaded) => {
if (!val) return
addOutline(loaded)
return () => {
deleteOutline(loaded)
}
})
)
}
private _bloom?: boolean
public override get bloom() {
return !!this._bloom
}
public override set bloom(val) {
this._bloom = val
this.cancelHandle("bloom", () =>
this.loaded.then((loaded) => {
if (!val) return
addBloom(loaded)
return () => {
deleteBloom(loaded)
}
})
)
}
private managerSet?: boolean
protected override addToRaycastSet(set: Set<Object3D>) {
const handle = new Cancellable()
queueMicrotask(() => {
if (handle.done) return
if (this._physics === "map" || this._physics === "map-debug")
handle.watch(
this.loaded.then((loaded) => {
if (!this.managerSet) {
this.managerSet = true
loaded.traverse(
(child) => (child.userData.manager ??= this)
)
}
set.add(loaded)
return () => {
set.delete(loaded)
}
})
)
else handle.watch(super.addToRaycastSet(set))
})
return handle
}
protected override refreshFactors() {
this.cancelHandle("refreshFactorsLoaded", () =>
this.loaded.then(() => void super.refreshFactors())
)
}
protected materialCloned?: boolean
protected tryCloneMaterial() {
if (this.materialCloned) return
this.materialCloned = true
this.watch(
this.loaded.then((loaded) => {
const cloned: Array<Material> = []
loaded.traverse((child: any) => {
if (!child.material) return
cloned.push((child.material = child.material.clone()))
})
return () => {
for (const material of cloned) material.dispose()
}
})
)
}
public override placeAt(object: MeshItem | Point3d | string) {
this.cancelHandle("placeAt", () =>
this.loaded.then(() => void super.placeAt(object))
)
}
}
export const getLoadedObject = (item: Loaded | MeshItem) => {
if ("loadedGroup" in item) return item.loadedGroup
if ("object3d" in item) return item.object3d
return item.outerObject3d
}