From e7b8e44b758f9d4759786300f195c7fe97550ed8 Mon Sep 17 00:00:00 2001 From: Vatroslav Vrbanic Date: Sun, 27 Nov 2022 00:44:49 +0100 Subject: [PATCH] Remove `target` prop and related logic / classes (#135) --- src/lib/components/DirectionalLight.svelte | 80 +------------- src/lib/components/SpotLight.svelte | 80 +------------- src/lib/utils/LightTarget.ts | 117 --------------------- src/lib/utils/LightUtils.ts | 48 --------- src/lib/utils/PropUtils.ts | 26 +---- src/lib/utils/Propeller.ts | 7 +- src/lib/utils/index.ts | 2 - 7 files changed, 5 insertions(+), 355 deletions(-) delete mode 100644 src/lib/utils/LightTarget.ts delete mode 100644 src/lib/utils/LightUtils.ts diff --git a/src/lib/components/DirectionalLight.svelte b/src/lib/components/DirectionalLight.svelte index dfa9978..2958670 100644 --- a/src/lib/components/DirectionalLight.svelte +++ b/src/lib/components/DirectionalLight.svelte @@ -32,8 +32,7 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` import SvelthreeLightWithShadow from "../components-internal/SvelthreeLightWithShadow.svelte" - import { LightTarget } from "../utils" - import { Object3D } from "three" + import type { Object3D } from "three" import { DirectionalLight as THREE_DirectionalLight } from "three" import { DirectionalLightHelper } from "three" @@ -283,83 +282,6 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` } } - /** Defaults to `true` which means that components / objects with targets (_`DirectionalLight`, `SpotLight`, `OrthographicCamera` and `PerspectiveCamera`_) - * will add the built-in 'blank' target-Object3D to component's / object's parent on initialization. `target` can be either set to `false` ( TODO ) (_which will remove the target from parent only if it's - * not the built-in 'blank' `Object3D`_) or some other object in the scene (_any `Object3D` instance or a supported svelthree component reference._) */ - export let target: Targetable | boolean | undefined = undefined - $: if (target === undefined) target = true - - let light_target: LightTarget | undefined | null = undefined - $: if (light && scene && target) light_target = new LightTarget(false, target, scene, light) - - $: if (!matrix && light && target && light_target) light_target.change = true - - // if the Light is a Light with a 'target' property, start / stop monitoring 'target' position changes if helper is enabled / disabled - $: if ( - !matrix && - light && - target && - light_target && - light.target?.isObject3D && - $svelthreeStores[sti]?.rendererComponent - ) { - start_monitoring_target_position() - } else { - stop_monitoring_target_position() - } - - // call this to remove the renderer component listener - let remove_target_position_listener: (() => void) | undefined - - function start_monitoring_target_position(): void { - // IMPORTANT this does NOT cause a component update! (instead of using requestAnimationFrame) - // COOL! this way the helper is 100% synchronious (not 1 frame late) - if (!remove_target_position_listener) { - if (verbose && log_rs) console.debug(...c_rs(c_name, "start_monitoring_target_position!")) - remove_target_position_listener = store?.rendererComponent?.$on("before_render_int", update_light_target) - } - } - - function update_light_target(): void { - if (light_target) { - light_target.update(!!helper) - } else { - console.error(`SVELTHREE > ${c_name} > update_light_target : Couldn't update unavailable 'light_target'!`, { - light_target - }) - } - } - - function stop_monitoring_target_position(): void { - if (remove_target_position_listener) { - remove_target_position_listener() - remove_target_position_listener = undefined - kill_light_target() - } - } - - function kill_light_target(): void { - if (!target) { - light_target = null - - if (light) { - if (light.target) { - // remove target from parent only if it's the built-in target - if (light.target.userData.is_builtin_target && light.target.parent) { - light.target.parent.remove(light.target) - } - // recreate / reset target - light.target = new Object3D() - } - } else { - console.error( - `SVELTHREE > ${c_name} > kill_light_target : Couldn't try to remove built-in 'target' from unavailable 'light' instance!`, - { light } - ) - } - } - } - /** Override object's `.matrixAutoUpdate` set (*on initialzation*) by scene's `.matrixAutoUpdate` (*default is `true`*). Also: `mau` can be changed on-the-fly.*/ export let mau: boolean | undefined = undefined $: if (light) light.matrixAutoUpdate = scene.matrixAutoUpdate diff --git a/src/lib/components/SpotLight.svelte b/src/lib/components/SpotLight.svelte index 978959f..6c23024 100644 --- a/src/lib/components/SpotLight.svelte +++ b/src/lib/components/SpotLight.svelte @@ -32,8 +32,7 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` import SvelthreeLightWithShadow from "../components-internal/SvelthreeLightWithShadow.svelte" - import { LightTarget } from "../utils" - import { Object3D } from "three" + import type { Object3D } from "three" import { SpotLight as THREE_SpotLight } from "three" import { SpotLightHelper } from "three" @@ -279,83 +278,6 @@ svelthree uses svelte-accmod, where accessors are always `true`, regardless of ` } } - /** Defaults to `true` which means that components / objects with targets (_`DirectionalLight`, `SpotLight`, `OrthographicCamera` and `PerspectiveCamera`_) - * will add the built-in 'blank' target-Object3D to component's / object's parent on initialization. `target` can be either set to `false` ( TODO ) (_which will remove the target from parent only if it's - * not the built-in 'blank' `Object3D`_) or some other object in the scene (_any `Object3D` instance or a supported svelthree component reference._) */ - export let target: Targetable | boolean | undefined = undefined - $: if (target === undefined) target = true - - let light_target: LightTarget | undefined | null = undefined - $: if (light && scene && target) light_target = new LightTarget(false, target, scene, light) - - $: if (!matrix && light && target && light_target) light_target.change = true - - // if the Light is a Light with a 'target' property, start / stop monitoring 'target' position changes if helper is enabled / disabled - $: if ( - !matrix && - light && - target && - light_target && - light.target?.isObject3D && - $svelthreeStores[sti]?.rendererComponent - ) { - start_monitoring_target_position() - } else { - stop_monitoring_target_position() - } - - // call this to remove the renderer component listener - let remove_target_position_listener: (() => void) | undefined - - function start_monitoring_target_position(): void { - // IMPORTANT this does NOT cause a component update! (instead of using requestAnimationFrame) - // COOL! this way the helper is 100% synchronious (not 1 frame late) - if (!remove_target_position_listener) { - if (verbose && log_rs) console.debug(...c_rs(c_name, "start_monitoring_target_position!")) - remove_target_position_listener = store?.rendererComponent?.$on("before_render_int", update_light_target) - } - } - - function update_light_target(): void { - if (light_target) { - light_target.update(!!helper) - } else { - console.error(`SVELTHREE > ${c_name} > update_light_target : Couldn't update unavailable 'light_target'!`, { - light_target - }) - } - } - - function stop_monitoring_target_position(): void { - if (remove_target_position_listener) { - remove_target_position_listener() - remove_target_position_listener = undefined - kill_light_target() - } - } - - function kill_light_target(): void { - if (!target) { - light_target = null - - if (light) { - if (light.target) { - // remove target from parent only if it's the built-in target - if (light.target.userData.is_builtin_target && light.target.parent) { - light.target.parent.remove(light.target) - } - // recreate / reset target - light.target = new Object3D() - } - } else { - console.error( - `SVELTHREE > ${c_name} > kill_light_target : Couldn't try to remove built-in 'target' from unavailable 'light' instance!`, - { light } - ) - } - } - } - /** Override object's `.matrixAutoUpdate` set (*on initialzation*) by scene's `.matrixAutoUpdate` (*default is `true`*). Also: `mau` can be changed on-the-fly.*/ export let mau: boolean | undefined = undefined $: if (light) light.matrixAutoUpdate = scene.matrixAutoUpdate diff --git a/src/lib/utils/LightTarget.ts b/src/lib/utils/LightTarget.ts deleted file mode 100644 index 6fcda35..0000000 --- a/src/lib/utils/LightTarget.ts +++ /dev/null @@ -1,117 +0,0 @@ -import type { Targetable, LightWithTarget, TargetableSvelthreeComponent } from "../types/types-extra" -import type { Object3D, Scene } from "three" -import { LightUtils } from "../utils" - -export default class LightTarget { - private targetPos: [number, number, number] = [0, 0, 0] - private targetPosPrev: [number, number, number] | undefined - - constructor( - public change: boolean, - private target: Targetable | boolean, - private scene: Scene, - private light: LightWithTarget - ) {} - - public on_light_target_change() { - this.change = false - - if (Object.hasOwn(this.light, "target")) { - // remove current target from parent if it's a built-in target - if (this.light["target"].userData.is_builtin_target && this.light["target"].parent) { - this.light["target"].parent.remove(this.light["target"]) - } - - // Check if provided `this.target` is a (targetable) `svelthree`-component -> check if it has a targetable instance prop (see `targetable_instance_names`) e.g. `mesh` or `object3d` - // retrun undefined if it's a `boolean` value - const targetable_instance_name: string | undefined = this.has_targetable_instance(this.target) - - // if `this.target` is truthy -> `true` (boolean) or some instance reference - if (this.target) { - if (targetable_instance_name) { - // provided 'this.target' is a (targetable) `svelthree`-component -> we've found a valid (see `targetable_instance_names`) instance prop (e.g. `mesh` or `object3d`) inside `this.target` - // e.g. light_comp_ref.target = comp_ref - // -> use any (created) three.js object instance (e.g. "mesh" or "object3d") of a provided targetable `svelthree`-component (reference) as `light`-target (already added to the scene) - this.light["target"] = (this.target as TargetableSvelthreeComponent)[ - targetable_instance_name as keyof TargetableSvelthreeComponent - ] - } else if ((this.target as Object3D).isObject3D) { - // provided `this.target` is a THREE.Object3D instance - // e.g. light_comp_ref.target = obj3d_ref - // -> use any 'THREE.Object3D' as target (already added to the scene) - this.light["target"] = this.target as Object3D - } else if (typeof this.target === "boolean") { - // provided `this.target` is a boolean value (`true`) - // light_comp_ref.target = true -> use the built-in 'Object3D' as target (add it to the scene) - this.light["target"].userData.is_builtin_target = true - this.scene.add(this.light["target"]) - } - } else if (typeof this.target === "boolean") { - // provided `this.target` is a boolean value (`false`) - // TODO ... - } - } - } - - // targetable component instances - private targetable_instance_names = ["scene", "object3d", "group", "mesh", "light", "camera", "container", "points"] - - // TODO see https://github.com/vatro/svelthree/issues/135 - private has_targetable_instance(target_to_check: boolean | Targetable): string | undefined { - if (typeof target_to_check !== "boolean") { - for (let i = 0; i < this.targetable_instance_names.length; i++) { - if ( - (target_to_check as TargetableSvelthreeComponent)[ - this.targetable_instance_names[i] as keyof TargetableSvelthreeComponent - ] - ) - return this.targetable_instance_names[i] - } - } - return undefined - } - - public update(helper: boolean): void { - // don't check for changes if 'target' is not added to scene (doesn't have parent) - - if (this.change) this.on_light_target_change() - - if (this.light["target"]?.isObject3D && this.light["target"].parent !== null) { - this.targetPos[0] = (this.light["target"] as Object3D).position.x - this.targetPos[1] = (this.light["target"] as Object3D).position.y - this.targetPos[2] = (this.light["target"] as Object3D).position.z - - if (this.targetPosPrev === undefined) { - this.targetPosPrev = [...this.targetPos] - this.onTargetPosChanged() - } else { - if (this.targetPosChanged()) { - this.onTargetPosChanged() - } - } - } - - this.light.updateMatrix() - this.light.updateMatrixWorld() - if (helper) { - LightUtils.updateHelper(this.light) - } - } - - private targetPosChanged(): boolean { - for (let i = 0; i < 3; i++) { - if (!this.targetPosPrev || (this.targetPosPrev && this.targetPos[i] !== this.targetPosPrev[i])) { - this.targetPosPrev = [...this.targetPos] - return true - } - } - return false - } - - private onTargetPosChanged(): void { - if (this.light["target"].matrixAutoUpdate === false) { - this.light["target"].updateMatrix() - this.light["target"].updateMatrixWorld() - } - } -} diff --git a/src/lib/utils/LightUtils.ts b/src/lib/utils/LightUtils.ts deleted file mode 100644 index 6ebeb54..0000000 --- a/src/lib/utils/LightUtils.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { Light, Scene } from "three" -import type { Object3D } from "three" -import { verbose_mode } from "../utils/SvelthreeLogger" - -/** - * Containes public static methods for updating Light (and shadow) properties via component shorthand attributes. - */ -export default class LightUtils { - /** - * Creates a specific Light helper. - */ - public static addHelper(light: Light, scene: Scene, helper: Object3D): void { - light.userData.helper = helper - LightUtils.updateHelper(light) - - scene.add(helper) - helper.visible = true - - if (verbose_mode()) - console.debug("SVELTHREE > " + light.type + " HELPER added!", { - camHelper: light.userData.helper, - scene: scene, - total: scene.children.length - }) - } - - /** - * Syncs Light and Light helper. - */ - public static updateHelper(light: Light): void { - if (light.userData.helper) { - // updates appearance / elements - light.userData.helper.update() - - // updates matrix (position, rotation, scale) - // this approach is bulletproof for all scene / matrix update modes. - //light.userData.helper.matrix.copy(light.matrix) - //light.userData.helper.matrixWorld.copy(light.matrixWorld) - } - } - - public static removeHelper(light: Light, scene: Scene): void { - if (light.userData.helper) { - scene.remove(light.userData.helper) - light.userData.helper = null - } - } -} diff --git a/src/lib/utils/PropUtils.ts b/src/lib/utils/PropUtils.ts index e5b64a7..d4eefa8 100644 --- a/src/lib/utils/PropUtils.ts +++ b/src/lib/utils/PropUtils.ts @@ -13,7 +13,7 @@ import type { //background_value, any_proputils_value } from "../types/types-extra" -import type { Targetable, TargetableSvelthreeComponent } from "../types/types-extra" +import type { TargetableSvelthreeComponent } from "../types/types-extra" import { verbose_mode, log_prop_utils } from "../utils/SvelthreeLogger" /** @@ -666,28 +666,6 @@ export default class PropUtils { } } - /** - * TODO : test / polish this! - */ - public static setLightTarget(obj: LightWithTarget, val: Targetable) { - if (this.verbose && log_prop_utils(obj)) - console.debug("SVELTHREE > [ PropUtils ] -> setLightTarget : ", { obj, val }) - if (!val) { - console.warn(`[ PropUtils ] -> setLightTarget : invalid 'target' value!`, { val }) - } else { - // TODO : Check why this being called twice on init! Not severe problem, but still to be checked. - //console.warn("SVELTHREE > Propeller > setLightTarget : " + this.objTypeStr + " : target in 'props' now defined!!") - - if ((val as TargetableSvelthreeComponent).is_svelthree_component) { - ;(obj as LightWithTarget).target = (val as TargetableSvelthreeComponent).get_instance() as Object3D - } else if ((val as Object3D).isObject3D) { - obj["target"] = val as Object3D - } else { - console.error(`[ PropUtils ] -> setLightTarget : invalid 'target' value!`, { val }) - } - } - } - /** * Sets `matrix` of an object. * IMPORTANT Setting "manually" updating the `matrix` property will automatically set `matrixAutoUpdate` to `false`. @@ -761,7 +739,7 @@ export default class PropUtils { public static setQuaternionFromValue(obj: Object3D, val: quat_value, complex?: ComplexValueType) { if (this.verbose && log_prop_utils(obj)) { - console.debug("SVELTHREE > [ PropUtils ] -> setLightTarget : ", { obj, val, complex }) + console.debug("SVELTHREE > [ PropUtils ] -> setQuaternionFromValue : ", { obj, val, complex }) } if (val && (val as Quaternion).isQuaternion) { diff --git a/src/lib/utils/Propeller.ts b/src/lib/utils/Propeller.ts index 0db9935..364def1 100644 --- a/src/lib/utils/Propeller.ts +++ b/src/lib/utils/Propeller.ts @@ -12,7 +12,7 @@ import type { any_propeller_value } from "../types/types-extra" import PropUtils from "./PropUtils" -import type { ComplexValueType, SvelthreePropsOwner, Targetable, LightWithTarget } from "../types/types-extra" +import type { ComplexValueType, SvelthreePropsOwner, LightWithTarget } from "../types/types-extra" /** ⚙️ `Propeller`'s `update` method redirects some `props` object properties to specific `PropUtils` update * methods and also allows special handling if needed when updating a specific `props` object's property. @@ -61,11 +61,6 @@ export default class Propeller { //PropUtils.setBackgroundAsTextureFromValueKey(obj as Scene, value as Texture, key, complex) } break - case "target": - if (obj_type === "DirectionalLight" || obj_type === "SpotLight") { - PropUtils.setLightTarget(obj as LightWithTarget, value as Targetable) - } - break default: // wildcard updating of **own** properties diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index ffa381e..1bbccf4 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -1,9 +1,7 @@ export { default as CameraUtils } from "./CameraUtils" -export { default as LightUtils } from "./LightUtils" export { default as Propeller } from "./Propeller" export { default as PropUtils } from "./PropUtils" export { default as SvelthreeProps } from "./SvelthreeProps" -export { default as LightTarget } from "./LightTarget" export { default as CubeCameraHelper } from "./CubeCameraHelper" export { default as GLTF_afterLoaded } from "./GLTF_afterLoaded" export { default as GLTF_utils } from "./GLTF_utils"