mylingo3d
Version:
Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor
158 lines (133 loc) • 5.09 kB
text/typescript
import { Group } from "three"
import fit from "./utils/fit"
import Loaded from "./core/Loaded"
import AnimationManager, {
PlayOptions
} from "./core/AnimatedObjectManager/AnimationManager"
import IModel, { modelDefaults, modelSchema } from "../interface/IModel"
import { Resolvable } from "@lincode/promiselikes"
import FoundManager from "./core/FoundManager"
import { Reactive } from "@lincode/reactivity"
import measure from "./utils/measure"
import { getExtensionIncludingObjectURL } from "./core/utils/objectURL"
import {
decreaseLoadingCount,
increaseLoadingCount
} from "../states/useLoadingCount"
export default class Model extends Loaded<Group> implements IModel {
public static componentName = "model"
public static defaults = modelDefaults
public static schema = modelSchema
public constructor(private unmounted?: boolean) {
super(unmounted)
}
private loadingState = new Reactive(0)
public override playAnimation(name?: string | number, o?: PlayOptions) {
setTimeout(() =>
this.cancelHandle("playAnimation", () =>
this.loadingState.get((count, handle) => {
if (count) return
handle.cancel()
super.playAnimation(name, o)
})
)
)
}
public override stopAnimation() {
setTimeout(() =>
this.cancelHandle("stopAnimation", () =>
this.loadingState.get((count, handle) => {
if (count) return
handle.cancel()
super.stopAnimation()
})
)
)
}
protected serializeAnimations?: Record<string, string>
public async loadAnimation(url: string, name = url) {
;(this.serializeAnimations ??= {})[name] = url
const clip = (await this.load(url)).animations[0]
if (!clip) return
this.animations[name] = this.watch(
new AnimationManager(clip, await this.loaded)
)
}
public override get animations(): Record<string, AnimationManager> {
return super.animations
}
public override set animations(
val: Record<string, string | AnimationManager>
) {
for (const [key, value] of Object.entries(val))
if (typeof value === "string") this.loadAnimation(value, key)
else this.animations[key] = value
}
protected async load(url: string) {
increaseLoadingCount()
const resolvable = new Resolvable()
this.loadingState.set(this.loadingState.get() + 1)
const extension = getExtensionIncludingObjectURL(url)
if (!extension || !["fbx", "glb", "gltf"].includes(extension)) {
resolvable.resolve()
setTimeout(() => this.loadingState.set(this.loadingState.get() - 1))
decreaseLoadingCount()
throw new Error("Unsupported file extension " + extension)
}
const module =
extension === "fbx"
? await import("./utils/loaders/loadFBX")
: await import("./utils/loaders/loadGLTF")
let result: Group
try {
result = await module.default(url, !this.unmounted)
} catch {
resolvable.resolve()
setTimeout(() => this.loadingState.set(this.loadingState.get() - 1))
decreaseLoadingCount()
throw new Error("Failed to load model, check if src is correct")
}
resolvable.resolve()
setTimeout(() => this.loadingState.set(this.loadingState.get() - 1))
decreaseLoadingCount()
return result
}
private _resize?: boolean
public get resize() {
return this._resize ?? true
}
public set resize(val) {
this._resize = val
this.loaded.done && (this.src = this._src)
}
protected resolveLoaded(loadedObject3d: Group, src: string) {
if (this.unmounted) return loadedObject3d
for (const clip of loadedObject3d.animations)
this.animations[clip.name] = this.watch(
new AnimationManager(clip, loadedObject3d)
)
const measuredSize =
this._resize === false
? measure(loadedObject3d, src)
: fit(loadedObject3d, src)
!this.widthSet && (this.object3d.scale.x = measuredSize.x)
!this.heightSet && (this.object3d.scale.y = measuredSize.y)
!this.depthSet && (this.object3d.scale.z = measuredSize.z)
return loadedObject3d
}
public override find(
name: string,
hiddenFromSceneGraph?: boolean
): FoundManager | undefined {
const child = super.find(name, hiddenFromSceneGraph)
child && (child.model = this)
return child
}
public override findAll(
name?: string | RegExp | ((name: string) => boolean)
): Array<FoundManager> {
const children = super.findAll(name)
for (const child of children) child.model = this
return children
}
}