From dae5a07e0a078117d5a604c6dbad8c0efd476fa2 Mon Sep 17 00:00:00 2001 From: MNAth_ Date: Mon, 11 Dec 2023 16:15:30 +0300 Subject: [PATCH] refactor: restructure app config logic --- src/constants/ANIMATION.ts | 4 - src/experiences/blueprints/Scene.blueprint.ts | 3 +- src/experiences/common/Event.model.ts | 6 ++ src/experiences/config/Common.config.ts | 7 ++ src/experiences/config/Config.ts | 3 + src/experiences/errors/Error.factory.ts | 9 ++ src/experiences/errors/Unknown.error.ts | 7 ++ src/experiences/pages/Home/Camera.ts | 37 ++++--- src/experiences/pages/Home/Debug.ts | 10 +- src/experiences/pages/Home/Loader.ts | 31 ++++-- src/experiences/pages/Home/Renderer.ts | 5 +- src/experiences/pages/Home/UI.ts | 23 ++-- .../pages/Home/World/SceneBackground.ts | 9 +- src/experiences/pages/Home/World/Scene_1.ts | 17 +-- src/experiences/pages/Home/World/Scene_2.ts | 11 +- src/experiences/pages/Home/index.ts | 100 +++++++++++------- 16 files changed, 180 insertions(+), 102 deletions(-) delete mode 100644 src/constants/ANIMATION.ts create mode 100644 src/experiences/common/Event.model.ts create mode 100644 src/experiences/config/Common.config.ts create mode 100644 src/experiences/config/Config.ts create mode 100644 src/experiences/errors/Error.factory.ts create mode 100644 src/experiences/errors/Unknown.error.ts diff --git a/src/constants/ANIMATION.ts b/src/constants/ANIMATION.ts deleted file mode 100644 index 03cd0ef..0000000 --- a/src/constants/ANIMATION.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const GSAP_DEFAULT_INTRO_PROPS = { - duration: 3, - ease: "M0,0 C0.001,0.001 0.002,0.003 0.003,0.004 0.142,0.482 0.284,0.75 0.338,0.836 0.388,0.924 0.504,1 1,1 ", -}; diff --git a/src/experiences/blueprints/Scene.blueprint.ts b/src/experiences/blueprints/Scene.blueprint.ts index 73626f1..44d3b48 100644 --- a/src/experiences/blueprints/Scene.blueprint.ts +++ b/src/experiences/blueprints/Scene.blueprint.ts @@ -7,6 +7,7 @@ import HomeExperience from "@/experiences/pages/Home"; // INTERFACES import { type ExperienceBase } from "@interfaces/experienceBase"; +import { LOADED } from "../common/Event.model"; // TODO: Link with the names of assets in the `app.loader` assets names export interface ModelChildrenTextures { @@ -42,7 +43,7 @@ export abstract class SceneBlueprint this.cameraPath = _.cameraPath; this._modelChildrenTextures = _.modelChildrenTextures; - this._experience.loader?.on("load", () => { + this._experience.loader?.on(LOADED, () => { const _MODEL = this._experience.app.resources.items[_.modelName] as | GLTF | undefined; diff --git a/src/experiences/common/Event.model.ts b/src/experiences/common/Event.model.ts new file mode 100644 index 0000000..9bd9df7 --- /dev/null +++ b/src/experiences/common/Event.model.ts @@ -0,0 +1,6 @@ +export const CONSTRUCTED = Symbol("0"); +export const DESTRUCTED = Symbol("1"); +export const UPDATED = Symbol("2"); +export const STARTED = Symbol("3"); +export const PROGRESSED = Symbol("4"); +export const LOADED = Symbol("5"); diff --git a/src/experiences/config/Common.config.ts b/src/experiences/config/Common.config.ts new file mode 100644 index 0000000..2dd42ec --- /dev/null +++ b/src/experiences/config/Common.config.ts @@ -0,0 +1,7 @@ +export abstract class CommonConfig { + static readonly ASSET_DIR = "/"; + static readonly GSAP_ANIMATION_DURATION = 3; + static readonly GSAP_ANIMATION_EASE = + "M0,0 C0.001,0.001 0.002,0.003 0.003,0.004 0.142,0.482 0.284,0.75 0.338,0.836 0.388,0.924 0.504,1 1,1 "; + static readonly HOME_DOM_REF = "home-three-app"; +} diff --git a/src/experiences/config/Config.ts b/src/experiences/config/Config.ts new file mode 100644 index 0000000..526f3c9 --- /dev/null +++ b/src/experiences/config/Config.ts @@ -0,0 +1,3 @@ +import { CommonConfig } from "./Common.config"; + +export class Config extends CommonConfig {} diff --git a/src/experiences/errors/Error.factory.ts b/src/experiences/errors/Error.factory.ts new file mode 100644 index 0000000..1bc253e --- /dev/null +++ b/src/experiences/errors/Error.factory.ts @@ -0,0 +1,9 @@ +import { UnknownError } from "./Unknown.error"; + +export class ErrorFactory { + constructor(_: any) { + if (_ instanceof Error) throw new UnknownError(_); + + throw new Error("🚧 Something went wrong", _); + } +} diff --git a/src/experiences/errors/Unknown.error.ts b/src/experiences/errors/Unknown.error.ts new file mode 100644 index 0000000..7b31d5c --- /dev/null +++ b/src/experiences/errors/Unknown.error.ts @@ -0,0 +1,7 @@ +export class UnknownError extends Error { + constructor(_: Error) { + super(); + + console.log("🚧 Unknown error caught ==>", _.name, _.cause, _.message); + } +} diff --git a/src/experiences/pages/Home/Camera.ts b/src/experiences/pages/Home/Camera.ts index 541a973..3967e38 100644 --- a/src/experiences/pages/Home/Camera.ts +++ b/src/experiences/pages/Home/Camera.ts @@ -5,18 +5,25 @@ import GSAP from "gsap"; import HomeExperience from "."; import Debug from "./Debug"; -// INTERFACES -import { type ExperienceBase } from "@/interfaces/experienceBase"; - -export class Camera implements ExperienceBase { - private readonly _experience = new HomeExperience(); - private readonly _appCamera = this._experience.app.camera; - private readonly _appDebug = this._experience.app.debug; +// BLUEPRINTS +import { ExperienceBasedBlueprint } from "@/experiences/blueprints/ExperienceBased.blueprint"; +import { + CONSTRUCTED, + DESTRUCTED, + UPDATED, +} from "@/experiences/common/Event.model"; + +export class Camera extends ExperienceBasedBlueprint { + protected readonly _experience = new HomeExperience(); + protected readonly _appCamera = this._experience.app.camera; + protected readonly _appDebug = this._experience.app.debug; + + public readonly initialCameraFov = 35; public lookAtPosition = new Vector3(); - initialCameraFov = 35; - - constructor() {} + constructor() { + super(); + } construct() { if (!Debug.enable && this._appDebug?.cameraHelper) { @@ -38,11 +45,17 @@ export class Camera implements ExperienceBase { new Vector3(); } }); + + this.emit(CONSTRUCTED); } - destruct() {} + destruct() { + this.emit(DESTRUCTED); + } - update() {} + update() { + this.emit(UPDATED); + } cameraZoomIn() { if (this._experience.app?.camera.instance instanceof PerspectiveCamera) diff --git a/src/experiences/pages/Home/Debug.ts b/src/experiences/pages/Home/Debug.ts index 28cb1e2..c6e0dbe 100644 --- a/src/experiences/pages/Home/Debug.ts +++ b/src/experiences/pages/Home/Debug.ts @@ -14,10 +14,11 @@ import GUI from "three/examples/jsm/libs/lil-gui.module.min.js"; // EXPERIENCE import Experience from "."; -// INTERFACES -import { type ExperienceBase } from "@/interfaces/experienceBase"; +// BLUEPRINTS +import { ExperienceBasedBlueprint } from "@/experiences/blueprints/ExperienceBased.blueprint"; +import { DESTRUCTED } from "@/experiences/common/Event.model"; -export default class Debug implements ExperienceBase { +export default class Debug extends ExperienceBasedBlueprint { protected readonly _experience = new Experience(); protected readonly _appDebug = this._experience.app.debug; protected readonly _appCamera = this._experience.app.camera; @@ -38,8 +39,6 @@ export default class Debug implements ExperienceBase { protected _cameraCurvePathLine?: Line; - constructor() {} - construct() { if (!Debug.enable) { this._appDebug?.ui?.destroy(); @@ -158,6 +157,7 @@ export default class Debug implements ExperienceBase { // "construct_experience" // ); // } + this.emit(DESTRUCTED); } update() { diff --git a/src/experiences/pages/Home/Loader.ts b/src/experiences/pages/Home/Loader.ts index d06f950..ff27e79 100644 --- a/src/experiences/pages/Home/Loader.ts +++ b/src/experiences/pages/Home/Loader.ts @@ -1,14 +1,22 @@ import { MeshBasicMaterial, Texture, SRGBColorSpace } from "three"; -import EventEmitter from "events"; -// --- -import Experience from "."; +// EXPERIENCE +import HomeExperience from "."; -// INTERFACES -import { type ExperienceBase } from "@/interfaces/experienceBase"; +// MODELS +import { + CONSTRUCTED, + DESTRUCTED, + LOADED, + PROGRESSED, + STARTED, +} from "@/experiences/common/Event.model"; -export default class Loader extends EventEmitter implements ExperienceBase { - protected readonly _experience = new Experience(); +// BLUEPRINTS +import { ExperienceBasedBlueprint } from "@/experiences/blueprints/ExperienceBased.blueprint"; + +export default class Loader extends ExperienceBasedBlueprint { + protected readonly _experience = new HomeExperience(); protected readonly _appResources = this._experience.app.resources; texturesMeshBasicMaterials: { @@ -52,7 +60,7 @@ export default class Loader extends EventEmitter implements ExperienceBase { construct() { ~(this._appResources.loadingManager.onStart = () => { - this.emit("start", (this.progress = 0)); + this.emit(STARTED, (this.progress = 0)); }); ~(this._appResources.loadingManager.onProgress = ( @@ -61,7 +69,7 @@ export default class Loader extends EventEmitter implements ExperienceBase { itemsToLoad ) => { this.emit( - "progress", + PROGRESSED, (this.progress = (itemsLoaded / itemsToLoad) * 100), itemUrl ); @@ -71,9 +79,10 @@ export default class Loader extends EventEmitter implements ExperienceBase { this.correctTextures(); this._initMeshTextures(); - this.emit("load", this.progress); + this.emit(LOADED, this.progress); }); + this.emit(CONSTRUCTED, this.progress); this._appResources.startLoading(); } @@ -87,6 +96,8 @@ export default class Loader extends EventEmitter implements ExperienceBase { /onStart|onError|onProgress|onLoad/ ); this._appResources.setSources([]); + + this.emit(DESTRUCTED); } /** Correct resources texture color and flip faces. */ diff --git a/src/experiences/pages/Home/Renderer.ts b/src/experiences/pages/Home/Renderer.ts index ef69cff..9f9fbc9 100644 --- a/src/experiences/pages/Home/Renderer.ts +++ b/src/experiences/pages/Home/Renderer.ts @@ -15,6 +15,7 @@ import HomeExperience from "."; // INTERFACES import { type ExperienceBase } from "@/interfaces/experienceBase"; +import { ExperienceBasedBlueprint } from "@/experiences/blueprints/ExperienceBased.blueprint"; export interface PortalAssets { mesh: THREE.Mesh; @@ -30,7 +31,7 @@ export interface PortalMeshCorners { } /** Renderer */ -export default class Renderer implements ExperienceBase { +export default class Renderer extends ExperienceBasedBlueprint { protected readonly _experience = new HomeExperience(); protected readonly _appRenderer = this._experience.app.renderer; protected readonly _appRendererInstance = @@ -50,8 +51,6 @@ export default class Renderer implements ExperienceBase { protected _portalTopLeftCorner = new Vector3(); protected _portalReflectedPosition = new Vector3(); - constructor() {} - public construct() { // Configure renderer behaviors ~(() => { diff --git a/src/experiences/pages/Home/UI.ts b/src/experiences/pages/Home/UI.ts index f7380b6..01b4b48 100644 --- a/src/experiences/pages/Home/UI.ts +++ b/src/experiences/pages/Home/UI.ts @@ -4,17 +4,17 @@ import { EventEmitter } from "events"; // EXPERIENCE import HomeExperience from "."; -// INTERFACES -import { type ExperienceBase } from "@/interfaces/experienceBase"; +// CONFIG +import { Config } from "@/experiences/config/Config"; -// CONSTANTS -import { GSAP_DEFAULT_INTRO_PROPS } from "@/constants/ANIMATION"; +// MODELS +import { ExperienceBasedBlueprint } from "@/experiences/blueprints/ExperienceBased.blueprint"; /** * Class in charge of all DOM HTML interactions (HTML user interface) */ -export default class UI extends EventEmitter implements ExperienceBase { - private readonly experience = new HomeExperience(); +export default class UI extends ExperienceBasedBlueprint { + protected readonly _experience = new HomeExperience(); loadedResourcesProgressLineElements?: HTMLElement | null; loadedResourcesProgressElements?: HTMLElement | null; @@ -47,7 +47,7 @@ export default class UI extends EventEmitter implements ExperienceBase { construct() { // EVENTS - this.experience.loader?.on("start", () => { + this._experience.loader?.on("start", () => { this.lastLoadedResourceElement?.classList.remove("animate-pulse"); if (this.loadedResourcesProgressLineElements) this.loadedResourcesProgressLineElements.style.width = "0%"; @@ -55,7 +55,7 @@ export default class UI extends EventEmitter implements ExperienceBase { this.loadedResourcesProgressElements.innerHTML = "0%"; }); - this.experience.loader?.on("progress", (progress: number, url: string) => { + this._experience.loader?.on("progress", (progress: number, url: string) => { if (this.loadedResourcesProgressLineElements) this.loadedResourcesProgressLineElements.style.width = progress + "%"; if (this.loadedResourcesProgressElements) @@ -65,7 +65,7 @@ export default class UI extends EventEmitter implements ExperienceBase { this.lastLoadedResourceElement.innerHTML = url.replace(/^.*\//, ""); }); - this.experience.loader?.on("load", () => { + this._experience.loader?.on("load", () => { if (this.loadedResourcesProgressElements) this.loadedResourcesProgressElements.innerHTML = "100%"; if (this.loadedResourcesProgressLineElements) @@ -81,7 +81,7 @@ export default class UI extends EventEmitter implements ExperienceBase { }, 1000); }); - this.experience.app.resources.startLoading(); + this._experience.app.resources.startLoading(); } destruct() { @@ -93,7 +93,8 @@ export default class UI extends EventEmitter implements ExperienceBase { intro() { const _TIMELINE = GSAP.timeline(); _TIMELINE.to("#landing-view-wrapper", { - ...GSAP_DEFAULT_INTRO_PROPS, + duration: Config.GSAP_ANIMATION_DURATION, + ease: Config.GSAP_ANIMATION_EASE, opacity: 0, delay: 2, onComplete: () => { diff --git a/src/experiences/pages/Home/World/SceneBackground.ts b/src/experiences/pages/Home/World/SceneBackground.ts index c58792a..e3c9c7b 100644 --- a/src/experiences/pages/Home/World/SceneBackground.ts +++ b/src/experiences/pages/Home/World/SceneBackground.ts @@ -4,8 +4,8 @@ import GSAP from "gsap"; // EXPERIENCES import { SceneBlueprint } from "@/experiences/blueprints/Scene.blueprint"; -// CONSTANTS -import { GSAP_DEFAULT_INTRO_PROPS } from "@/constants/ANIMATION"; +// CONFIGS +import { Config } from "@/experiences/config/Config"; export default class SceneBackground extends SceneBlueprint { constructor() { @@ -55,11 +55,12 @@ export default class SceneBackground extends SceneBlueprint { GSAP.to(this._appCamera.instance.position, { ...this._experience.world?.controls?.getGsapDefaultProps(), - ...GSAP_DEFAULT_INTRO_PROPS, + duration: Config.GSAP_ANIMATION_DURATION, + ease: Config.GSAP_ANIMATION_EASE, x, y, z, - delay: GSAP_DEFAULT_INTRO_PROPS.duration * 0.8, + delay: Config.GSAP_ANIMATION_DURATION * 0.8, onUpdate: () => { WORLD_CONTROLS?.setCameraLookAt(WORLD_CONTROLS.initialLookAtPosition); }, diff --git a/src/experiences/pages/Home/World/Scene_1.ts b/src/experiences/pages/Home/World/Scene_1.ts index f4921f8..293b181 100644 --- a/src/experiences/pages/Home/World/Scene_1.ts +++ b/src/experiences/pages/Home/World/Scene_1.ts @@ -1,7 +1,6 @@ import { CatmullRomCurve3, Color, - Group, Material, Mesh, MeshBasicMaterial, @@ -14,16 +13,18 @@ import { } from "three"; import GSAP from "gsap"; -// EXPERIENCES +// BLUEPRINTS import { SceneBlueprint } from "@/experiences/blueprints/Scene.blueprint"; -// CONSTANTS -import { GSAP_DEFAULT_INTRO_PROPS } from "@/constants/ANIMATION"; +// CONFIG // SHADERS import fragment from "./shaders/scene1/fragment.frag"; import vertex from "./shaders/scene1/vertex.vert"; +// CONFIGS +import { Config } from "@/experiences/config/Config"; + export default class Scene_1 extends SceneBlueprint { protected _renderer = this._experience.renderer; @@ -99,7 +100,8 @@ export default class Scene_1 extends SceneBlueprint { if (!this.modelScene) return; GSAP.to((this.modelScene.children[0] as Mesh).material as Material, { - ...GSAP_DEFAULT_INTRO_PROPS, + duration: Config.GSAP_ANIMATION_DURATION, + ease: Config.GSAP_ANIMATION_EASE, opacity: 0, onUpdate: () => {}, onComplete: () => { @@ -123,11 +125,12 @@ export default class Scene_1 extends SceneBlueprint { GSAP.to(this._appCamera.instance.position, { ...this._experience.world?.controls?.getGsapDefaultProps(), - ...GSAP_DEFAULT_INTRO_PROPS, + duration: Config.GSAP_ANIMATION_DURATION, + ease: Config.GSAP_ANIMATION_EASE, x, y, z, - delay: GSAP_DEFAULT_INTRO_PROPS.duration * 0.8, + delay: Config.GSAP_ANIMATION_DURATION * 0.8, onUpdate: () => { WORLD_CONTROLS?.setCameraLookAt(WORLD_CONTROLS.initialLookAtPosition); }, diff --git a/src/experiences/pages/Home/World/Scene_2.ts b/src/experiences/pages/Home/World/Scene_2.ts index df9a0ff..19234c3 100644 --- a/src/experiences/pages/Home/World/Scene_2.ts +++ b/src/experiences/pages/Home/World/Scene_2.ts @@ -1,11 +1,11 @@ import { CatmullRomCurve3, PerspectiveCamera, Vector3 } from "three"; import GSAP from "gsap"; -// EXPERIENCES +// BLUEPRINTS import { SceneBlueprint } from "@/experiences/blueprints/Scene.blueprint"; -// CONSTANTS -import { GSAP_DEFAULT_INTRO_PROPS } from "@/constants/ANIMATION"; +// CONFIGS +import { Config } from "@/experiences/config/Config"; export default class Scene_2 extends SceneBlueprint { constructor() { @@ -59,11 +59,12 @@ export default class Scene_2 extends SceneBlueprint { GSAP.to(this._appCamera.instance.position, { ...this._experience.world?.controls?.getGsapDefaultProps(), - ...GSAP_DEFAULT_INTRO_PROPS, + duration: Config.GSAP_ANIMATION_DURATION, + ease: Config.GSAP_ANIMATION_EASE, x, y, z, - delay: GSAP_DEFAULT_INTRO_PROPS.duration * 0.8, + delay: Config.GSAP_ANIMATION_DURATION * 0.8, onUpdate: () => { WORLD_CONTROLS?.setCameraLookAt(WORLD_CONTROLS.initialLookAtPosition); }, diff --git a/src/experiences/pages/Home/index.ts b/src/experiences/pages/Home/index.ts index 3a61738..aee2df9 100644 --- a/src/experiences/pages/Home/index.ts +++ b/src/experiences/pages/Home/index.ts @@ -6,12 +6,16 @@ import World from "./World"; import UI from "./UI"; import Debug from "./Debug"; -// FACTORIES +// BLUEPRINT import { ExperienceBlueprint, type ExperienceProps, } from "@/experiences/blueprints/Experience.blueprint"; +// MODELS +import { LOADED } from "@/experiences/common/Event.model"; +import { ErrorFactory } from "@/experiences/errors/Error.factory"; + export class HomeExperience extends ExperienceBlueprint { renderer?: Renderer; ui?: UI; @@ -21,58 +25,74 @@ export class HomeExperience extends ExperienceBlueprint { debug?: Debug; constructor(_?: Omit) { - super( - HomeExperience._self ?? { - ..._, - debug: Debug.enable, - } - ); - if (HomeExperience._self) return HomeExperience._self; - HomeExperience._self = this; + try { + super( + HomeExperience._self ?? { + ..._, + debug: Debug.enable, + } + ); + if (HomeExperience._self) return HomeExperience._self; + HomeExperience._self = this; - this.renderer = new Renderer(); - this.ui = new UI(); - this.loader = new Loader(); - this.camera = new Camera(); - this.world = new World(); - this.debug = new Debug(); + this.renderer = new Renderer(); + this.ui = new UI(); + this.loader = new Loader(); + this.camera = new Camera(); + this.world = new World(); + this.debug = new Debug(); + } catch (_) { + throw new ErrorFactory(_); + } } public destruct() { - this.app.updateCallbacks[HomeExperience.name] && - delete this.app.updateCallbacks[HomeExperience.name]; + try { + this.app.updateCallbacks[HomeExperience.name] && + delete this.app.updateCallbacks[HomeExperience.name]; - this.renderer?.destruct(); - this.ui?.destruct(); - this.loader?.destruct(); - this.camera?.destruct(); - this.world?.destruct(); - this.debug?.destruct(); - this.app.destroy(); + this.renderer?.destruct(); + this.ui?.destruct(); + this.loader?.destruct(); + this.camera?.destruct(); + this.world?.destruct(); + this.debug?.destruct(); + this.app.destroy(); - HomeExperience._self = undefined; - this._onDestruct && this._onDestruct(); + HomeExperience._self = undefined; + this._onDestruct && this._onDestruct(); + } catch (_) { + throw new ErrorFactory(_); + } } public construct() { - if (this.world?.currentSceneIndex !== undefined) this.destruct(); + try { + if (this.world?.currentSceneIndex !== undefined) this.destruct(); - this.renderer?.construct(); - this.ui?.construct(); - this.camera?.construct(); - this.loader?.on("load", () => { - this.world?.construct(); - this.debug?.construct(); - this.app?.setUpdateCallback(HomeExperience.name, () => this.update()); - this._onConstruct && this._onConstruct(); - }); - this.loader?.construct(); + this.renderer?.construct(); + this.ui?.construct(); + this.camera?.construct(); + this.loader?.on(LOADED, () => { + this.world?.construct(); + this.debug?.construct(); + this.app?.setUpdateCallback(HomeExperience.name, () => this.update()); + this._onConstruct && this._onConstruct(); + }); + this.loader?.construct(); + } catch (_) { + throw new ErrorFactory(_); + } } public update() { - this.world?.update(); - this.camera?.update(); - this.debug?.update(); + try { + this.world?.update(); + this.camera?.update(); + this.debug?.update(); + } catch (_) { + throw new ErrorFactory(_); + } } }