UNPKG

mylingo3d

Version:

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

187 lines (162 loc) 5.75 kB
import { Cancellable } from "@lincode/promiselikes" import { Reactive } from "@lincode/reactivity" import { Color, DirectionalLightHelper, Group, Light, PointLightHelper, SpotLightHelper } from "three" import { RectAreaLightHelper } from "three/examples/jsm/helpers/RectAreaLightHelper" import mainCamera from "../../engine/mainCamera" import scene from "../../engine/scene" import { onBeforeRender } from "../../events/onBeforeRender" import { emitSelectionTarget, onSelectionTarget } from "../../events/onSelectionTarget" import { SHADOW_BIAS } from "../../globals" import ILightBase from "../../interface/ILightBase" import { getCameraRendered } from "../../states/useCameraRendered" import { getShadowBias } from "../../states/useShadowBias" import { getShadowResolution } from "../../states/useShadowResolution" import ObjectManager from "./ObjectManager" import makeLightSprite from "./utils/makeLightSprite" export default abstract class LightBase<T extends typeof Light> extends ObjectManager<Group> implements ILightBase { protected lightState = new Reactive<InstanceType<T> | undefined>(undefined) protected defaultShadowResolution = 256 protected defaultShadowBias = SHADOW_BIAS protected shadowResolutionComputedState = new Reactive<number | undefined>( undefined ) protected shadowBiasComputedState = new Reactive<number | undefined>( undefined ) public constructor( Light: T, Helper?: | typeof DirectionalLightHelper | typeof SpotLightHelper | typeof PointLightHelper | typeof RectAreaLightHelper ) { const group = new Group() super(group) this.createEffect(() => { const light = new Light() this.lightState.set(light as InstanceType<T>) group.add(light) if (light.shadow && this.castShadowState.get()) { const shadowResolution = this.shadowResolutionState.get() ?? getShadowResolution() ?? this.defaultShadowResolution const shadowBias = this.shadowBiasState.get() ?? getShadowBias() ?? this.defaultShadowBias this.shadowBiasComputedState.set(shadowBias) this.shadowResolutionComputedState.set(shadowResolution) light.castShadow = true light.shadow.bias = shadowBias light.shadow.mapSize.width = shadowResolution light.shadow.mapSize.height = shadowResolution } return () => { group.remove(light) light.dispose() } }, [ this.castShadowState.get, this.shadowResolutionState.get, this.shadowBiasState.get, getShadowResolution, getShadowBias ]) this.createEffect(() => { const light = this.lightState.get() if ( getCameraRendered() !== mainCamera || !this.helperState.get() || !light ) return const handle = new Cancellable() const sprite = makeLightSprite() handle.watch( onSelectionTarget(({ target }) => { target === sprite && emitSelectionTarget(this) }) ) if (Helper) { const helper = new Helper(light as any) scene.add(helper) helper.add(sprite.outerObject3d) if ("update" in helper) handle.watch(onBeforeRender(() => helper.update())) handle.then(() => { helper.dispose() scene.remove(helper) }) } else this.outerObject3d.add(sprite.outerObject3d) return () => { sprite.dispose() handle.cancel() } }, [getCameraRendered, this.helperState.get, this.lightState.get]) } private helperState = new Reactive(true) public get helper() { return this.helperState.get() } public set helper(val) { this.helperState.set(val) } private castShadowState = new Reactive(true) public override get castShadow() { return this.castShadowState.get() } public override set castShadow(val) { this.castShadowState.set(val) } private shadowResolutionState = new Reactive<number | undefined>(undefined) public get shadowResolution() { return this.shadowResolutionState.get() } public set shadowResolution(val) { this.shadowResolutionState.set(val) } private shadowBiasState = new Reactive<number | undefined>(undefined) public get shadowBias() { return this.shadowBiasState.get() } public set shadowBias(val) { this.shadowBiasState.set(val) } public get color() { const light = this.lightState.get() if (!light) return "#ffffff" return "#" + light.color.getHexString() } public set color(val) { this.cancelHandle("color", () => this.lightState.get( (light) => light && (light.color = new Color(val)) ) ) } public get intensity() { const light = this.lightState.get() if (!light) return 1 return light.intensity } public set intensity(val) { this.cancelHandle("intensity", () => this.lightState.get((light) => light && (light.intensity = val)) ) } }