diff --git a/nuxt.config.ts b/nuxt.config.ts index 7c78968..f28cc17 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -4,11 +4,15 @@ import glslify from "vite-plugin-glslify"; export default defineNuxtConfig({ devtools: { enabled: true }, alias: { - "@exp-factories/*": "../src/experiences/factories/*", - "@exp-errors/*": "../src/experiences/errors/*", - "@exp-pages/*": "../src/experiences/pages/*", - "@interfaces/*": "../src/interfaces/*", - "@constants/*": "../src/constants/*", + "~blueprints/*": "../src/blueprints/*", + "~config": "../src/config", + "~config/*": "../src/config/*", + "~experiences/*": "../src/experiences/*", + "~errors": "../src/errors", + "~errors/*": "../src/errors/*", + "~common/*": "../src/common/*", + "~static": "../src/static", + "~static/*": "../src/static/*", }, app: { head: { diff --git a/src/assets/models/scene_1/lights_baked_texture.jpg b/src/assets/models/scene_1/lights_baked_texture.jpg new file mode 100644 index 0000000..85c091b Binary files /dev/null and b/src/assets/models/scene_1/lights_baked_texture.jpg differ diff --git a/src/assets/models/scene_1/model.glb b/src/assets/models/scene_1/model.glb new file mode 100644 index 0000000..bc17f97 Binary files /dev/null and b/src/assets/models/scene_1/model.glb differ diff --git a/src/assets/models/scene_1/no_lights_baked_texture.jpg b/src/assets/models/scene_1/no_lights_baked_texture.jpg new file mode 100644 index 0000000..c22b0ce Binary files /dev/null and b/src/assets/models/scene_1/no_lights_baked_texture.jpg differ diff --git a/src/assets/models/scene_1/tree_baked_texture.jpg b/src/assets/models/scene_1/tree_baked_texture.jpg new file mode 100644 index 0000000..df16a6e Binary files /dev/null and b/src/assets/models/scene_1/tree_baked_texture.jpg differ diff --git a/src/assets/models/scene_1/woods_lights_baked_texture.jpg b/src/assets/models/scene_1/woods_lights_baked_texture.jpg new file mode 100644 index 0000000..3e05715 Binary files /dev/null and b/src/assets/models/scene_1/woods_lights_baked_texture.jpg differ diff --git a/src/assets/models/scene_1/woods_no_lights_baked_texture.jpg b/src/assets/models/scene_1/woods_no_lights_baked_texture.jpg new file mode 100644 index 0000000..7acad17 Binary files /dev/null and b/src/assets/models/scene_1/woods_no_lights_baked_texture.jpg differ diff --git a/src/assets/models/scene_2/baked_texture.jpg b/src/assets/models/scene_2/baked_texture.jpg new file mode 100644 index 0000000..05e7f91 Binary files /dev/null and b/src/assets/models/scene_2/baked_texture.jpg differ diff --git a/src/public/models/scene_2/model.glb b/src/assets/models/scene_2/model.glb similarity index 100% rename from src/public/models/scene_2/model.glb rename to src/assets/models/scene_2/model.glb diff --git a/src/assets/models/scene_3/baked_texture.jpg b/src/assets/models/scene_3/baked_texture.jpg new file mode 100644 index 0000000..70b0b63 Binary files /dev/null and b/src/assets/models/scene_3/baked_texture.jpg differ diff --git a/src/assets/models/scene_3/model.glb b/src/assets/models/scene_3/model.glb new file mode 100644 index 0000000..daa9d6e Binary files /dev/null and b/src/assets/models/scene_3/model.glb differ diff --git a/src/assets/models/scene_container/baked_texture.jpg b/src/assets/models/scene_container/baked_texture.jpg new file mode 100644 index 0000000..3e8e6c1 Binary files /dev/null and b/src/assets/models/scene_container/baked_texture.jpg differ diff --git a/src/public/models/scene_container/model.glb b/src/assets/models/scene_container/model.glb similarity index 100% rename from src/public/models/scene_container/model.glb rename to src/assets/models/scene_container/model.glb diff --git a/src/assets/textures/cloudAlphaMap.jpg b/src/assets/textures/cloudAlphaMap.jpg new file mode 100644 index 0000000..8ad3dd4 Binary files /dev/null and b/src/assets/textures/cloudAlphaMap.jpg differ diff --git a/src/assets/textures/rocksAlphaMap.jpg b/src/assets/textures/rocksAlphaMap.jpg new file mode 100644 index 0000000..042d360 Binary files /dev/null and b/src/assets/textures/rocksAlphaMap.jpg differ diff --git a/src/public/videos/monitor_a_screen_recording.webm b/src/assets/videos/monitor_a_screen_recording.webm similarity index 100% rename from src/public/videos/monitor_a_screen_recording.webm rename to src/assets/videos/monitor_a_screen_recording.webm diff --git a/src/public/videos/monitor_b_screen_recording.webm b/src/assets/videos/monitor_b_screen_recording.webm similarity index 100% rename from src/public/videos/monitor_b_screen_recording.webm rename to src/assets/videos/monitor_b_screen_recording.webm diff --git a/src/public/videos/phone_screen_recording.webm b/src/assets/videos/phone_screen_recording.webm similarity index 100% rename from src/public/videos/phone_screen_recording.webm rename to src/assets/videos/phone_screen_recording.webm diff --git a/src/experiences/blueprints/experience-based.blueprint.ts b/src/blueprints/experiences/experience-based.blueprint.ts similarity index 77% rename from src/experiences/blueprints/experience-based.blueprint.ts rename to src/blueprints/experiences/experience-based.blueprint.ts index e73ce99..760a67d 100644 --- a/src/experiences/blueprints/experience-based.blueprint.ts +++ b/src/blueprints/experiences/experience-based.blueprint.ts @@ -3,13 +3,13 @@ import { EventEmitter } from "events"; // BLUEPRINTS import type { ExperienceBlueprint } from "./experience.blueprint"; -// INTERFACES -import type { ExperienceBase } from "~/interfaces/experienceBase"; +// MODELS +import type { Experience } from "~/common/experiences/experience.model"; /** Represent a class that depend on {@link ExperienceBlueprint}. */ export abstract class ExperienceBasedBlueprint extends EventEmitter - implements ExperienceBase + implements Experience { protected abstract readonly _experience: ExperienceBlueprint; diff --git a/src/experiences/blueprints/Experience.blueprint.ts b/src/blueprints/experiences/experience.blueprint.ts similarity index 63% rename from src/experiences/blueprints/Experience.blueprint.ts rename to src/blueprints/experiences/experience.blueprint.ts index 83690a9..f3d7932 100644 --- a/src/experiences/blueprints/Experience.blueprint.ts +++ b/src/blueprints/experiences/experience.blueprint.ts @@ -1,20 +1,12 @@ import QuickThree from "quick-threejs"; -// INTERFACES -import type { ExperienceBase } from "@/interfaces/experienceBase"; +// MODELS +import type { + Experience, + ExperienceConstructorProps, +} from "~/common/experiences/experience.model"; -export interface ExperienceProps { - /** String dom element reference of the canvas. */ - domElementRef?: string; - /* Start the project in debug mode */ - debug?: boolean; - /** Event triggered when the scene is constructed. */ - onConstruct?: () => unknown; - /** Event triggered when the scene is destructed. */ - onDestruct?: () => unknown; -} - -export abstract class ExperienceBlueprint implements ExperienceBase { +export abstract class ExperienceBlueprint implements Experience { /** * Self class reference. Used for *singleton* pattern. * @@ -26,7 +18,7 @@ export abstract class ExperienceBlueprint implements ExperienceBase { /** [`quick-threejs`](https://www.npmjs.com/package/quick-threejs) instance. */ readonly app!: QuickThree; /** To make the singleton logic, refer to the {@link ExperienceBlueprint._self `#_self`} declaration */ - constructor(_?: ExperienceProps | ExperienceBlueprint) { + constructor(_?: ExperienceConstructorProps | ExperienceBlueprint) { if (!(_ instanceof ExperienceBlueprint) && _) { this.app = new QuickThree( { diff --git a/src/experiences/blueprints/scene-component.blueprint.ts b/src/blueprints/experiences/scene-component.blueprint.ts similarity index 83% rename from src/experiences/blueprints/scene-component.blueprint.ts rename to src/blueprints/experiences/scene-component.blueprint.ts index 61713a9..38158be 100644 --- a/src/experiences/blueprints/scene-component.blueprint.ts +++ b/src/blueprints/experiences/scene-component.blueprint.ts @@ -5,20 +5,19 @@ import type { GLTF } from "three/examples/jsm/loaders/GLTFLoader"; import { ExperienceBasedBlueprint } from "./experience-based.blueprint"; // EXPERIENCES -import { HomeExperience } from "~/experiences/core/home"; +import { HomeExperience } from "~/experiences/home"; -// MODELS -import { CONSTRUCTED, DESTRUCTED, LOADED } from "~/common/event.model"; -import { WRONG_PARAM } from "~/common/error.model"; +// STATIC +import { errors, events } from "~/static"; // ERRORS -import { ErrorFactory } from "../errors/error.factory"; +import { ErrorFactory } from "~/errors"; -// INTERFACES +// MODELS import type { Materials, ModelChildrenMaterials, -} from "~/interfaces/experienceWorld"; +} from "~/common/experiences/experience-world.model"; // TODO: Link with the names of assets in the `app.loader` assets names export interface SceneBlueprintProps { @@ -50,7 +49,7 @@ export abstract class SceneComponentBlueprint extends ExperienceBasedBlueprint { this._childrenMaterials = _.childrenMaterials; - this._experience.loader?.on(LOADED, () => { + this._experience.loader?.on(events.LOADED, () => { const _MODEL = this._experience.app.resources.items[_.modelName] as | GLTF | undefined; @@ -108,7 +107,7 @@ export abstract class SceneComponentBlueprint extends ExperienceBasedBlueprint { if (!this.modelScene) throw new ErrorFactory( - new Error("No model scene founded", { cause: WRONG_PARAM }) + new Error("No model scene founded", { cause: errors.WRONG_PARAM }) ); if (typeof callback === "function") callback(); @@ -118,13 +117,13 @@ export abstract class SceneComponentBlueprint extends ExperienceBasedBlueprint { ...this._getAvailableMaterials(), }; this._initModelMaterials(); - this.emit(CONSTRUCTED); + this.emit(events.CONSTRUCTED); } public destruct() { this.modelScene?.clear(); this.modelScene?.removeFromParent(); - this.emit(DESTRUCTED); + this.emit(events.DESTRUCTED); } public intro(): void {} diff --git a/src/interfaces/experienceWorld.ts b/src/common/experiences/experience-world.model.ts similarity index 50% rename from src/interfaces/experienceWorld.ts rename to src/common/experiences/experience-world.model.ts index 30c90c6..97bf90b 100644 --- a/src/interfaces/experienceWorld.ts +++ b/src/common/experiences/experience-world.model.ts @@ -1,4 +1,4 @@ -import { CatmullRomCurve3, Material, Vector3 } from "three"; +import { Material } from "three"; export type MaterialName = string; @@ -9,9 +9,3 @@ export interface Materials { export interface ModelChildrenMaterials { [childName: string]: MaterialName; } - -export interface SceneConfig { - position: Vector3; - center: Vector3; - cameraPath: CatmullRomCurve3; -} diff --git a/src/common/experiences/experience.model.ts b/src/common/experiences/experience.model.ts new file mode 100644 index 0000000..dea3497 --- /dev/null +++ b/src/common/experiences/experience.model.ts @@ -0,0 +1,17 @@ +/** Represent the base structure of all experience classes in the application. */ +export interface Experience { + construct: (callback?: () => unknown) => unknown; + destruct: (callback?: () => unknown) => unknown; + update?: (callback?: () => unknown) => unknown; +} + +export interface ExperienceConstructorProps { + /** String dom element reference of the canvas. */ + domElementRef?: string; + /* Start the project in debug mode */ + debug?: boolean; + /** Event triggered when the scene is constructed. */ + onConstruct?: () => unknown; + /** Event triggered when the scene is destructed. */ + onDestruct?: () => unknown; +} diff --git a/src/experiences/config/Common.config.ts b/src/config/common.config.ts similarity index 100% rename from src/experiences/config/Common.config.ts rename to src/config/common.config.ts diff --git a/src/experiences/config/index.ts b/src/config/index.ts similarity index 91% rename from src/experiences/config/index.ts rename to src/config/index.ts index 2a16449..0aac271 100644 --- a/src/experiences/config/index.ts +++ b/src/config/index.ts @@ -1,7 +1,7 @@ import { CommonConfig } from "./common.config"; export class Config extends CommonConfig { - protected static _supportsPassive = false; + private static _supportsPassive = false; static set supportsPassive(val: boolean) { Config._supportsPassive = !!val; diff --git a/src/constants/UI.ts b/src/constants/UI.ts deleted file mode 100644 index b19b4a0..0000000 --- a/src/constants/UI.ts +++ /dev/null @@ -1 +0,0 @@ -export const HOME_DOM_REF = "home-three-app"; diff --git a/src/experiences/errors/Error.factory.ts b/src/errors/index.ts similarity index 73% rename from src/experiences/errors/Error.factory.ts rename to src/errors/index.ts index 4bc2586..fcc058e 100644 --- a/src/experiences/errors/Error.factory.ts +++ b/src/errors/index.ts @@ -1,4 +1,4 @@ -import { UnknownError } from "./unknown-error"; +import { UnknownError } from "./unknown.error"; export class ErrorFactory { constructor(_: any) { diff --git a/src/experiences/errors/unknown-error.ts b/src/errors/unknown.error.ts similarity index 100% rename from src/experiences/errors/unknown-error.ts rename to src/errors/unknown.error.ts diff --git a/src/experiences/core/home/ui.ts b/src/experiences/core/home/ui.ts deleted file mode 100644 index cb0f29a..0000000 --- a/src/experiences/core/home/ui.ts +++ /dev/null @@ -1,109 +0,0 @@ -import GSAP from "gsap"; - -// EXPERIENCE -import { HomeExperience } from "."; - -// CONFIG -import { Config } from "~/experiences/config"; - -// BLUEPRINTS -import { ExperienceBasedBlueprint } from "~/experiences/blueprints/experience-based.blueprint"; - -/** - * Class in charge of all DOM HTML interactions (HTML user interface) - */ -export class UI extends ExperienceBasedBlueprint { - protected readonly _experience = new HomeExperience(); - - loadedResourcesProgressLineElements?: HTMLElement | null; - loadedResourcesProgressElements?: HTMLElement | null; - lastLoadedResourceElement?: HTMLElement | null; - modelBubblesContainerElement = document.querySelector( - "#mode-bubbles-container" - ); - - constructor() { - super(); - - const _LOADED_RESOURCES_PROGRESS_LINE_ELEMENT = document.getElementById( - "loaded-resources-progress-line" - ); - const _LOADED_RESOURCES_PROGRESS_ELEMENT = document.getElementById( - "loaded-resources-progress" - ); - const _LAST_LOADED_RESOURCE_ELEMENT = document.getElementById( - "last-loaded-resource" - ); - - if (_LOADED_RESOURCES_PROGRESS_LINE_ELEMENT) - this.loadedResourcesProgressLineElements = - _LOADED_RESOURCES_PROGRESS_LINE_ELEMENT; - if (_LOADED_RESOURCES_PROGRESS_ELEMENT) - this.loadedResourcesProgressElements = _LOADED_RESOURCES_PROGRESS_ELEMENT; - if (_LAST_LOADED_RESOURCE_ELEMENT) - this.lastLoadedResourceElement = _LAST_LOADED_RESOURCE_ELEMENT; - } - - construct() { - // EVENTS - this._experience.loader?.on("start", () => { - this.lastLoadedResourceElement?.classList.remove("animate-pulse"); - if (this.loadedResourcesProgressLineElements) - this.loadedResourcesProgressLineElements.style.width = "0%"; - if (this.loadedResourcesProgressElements) - this.loadedResourcesProgressElements.innerHTML = "0%"; - }); - - this._experience.loader?.on("progress", (progress: number, url: string) => { - if (this.loadedResourcesProgressLineElements) - this.loadedResourcesProgressLineElements.style.width = progress + "%"; - if (this.loadedResourcesProgressElements) - this.loadedResourcesProgressElements.innerHTML = - progress.toFixed(0) + "%"; - if (this.lastLoadedResourceElement) - this.lastLoadedResourceElement.innerHTML = url.replace(/^.*\//, ""); - }); - - this._experience.loader?.on("load", () => { - if (this.loadedResourcesProgressElements) - this.loadedResourcesProgressElements.innerHTML = "100%"; - if (this.loadedResourcesProgressLineElements) - this.loadedResourcesProgressLineElements.style.width = "100%"; - - setTimeout(() => { - if (this.lastLoadedResourceElement) - this.lastLoadedResourceElement.innerHTML = - "Resources Loaded Successfully"; - - this.intro(); - this.emit("ready"); - }, 1000); - }); - - this._experience.app.resources.startLoading(); - } - - destruct() { - this.loadedResourcesProgressLineElements = undefined; - this.loadedResourcesProgressElements = undefined; - this.lastLoadedResourceElement = undefined; - } - - intro() { - const _TIMELINE = GSAP.timeline(); - _TIMELINE.to("#landing-view-wrapper", { - duration: Config.GSAP_ANIMATION_DURATION, - ease: Config.GSAP_ANIMATION_EASE, - opacity: 0, - delay: 2, - onComplete: () => { - const _LANDING_VIEW_WRAPPER = document.getElementById( - "landing-view-wrapper" - ); - - if (_LANDING_VIEW_WRAPPER?.style) - _LANDING_VIEW_WRAPPER.style.display = "none"; - }, - }); - } -} diff --git a/src/experiences/core/home/camera.ts b/src/experiences/home/camera.ts similarity index 70% rename from src/experiences/core/home/camera.ts rename to src/experiences/home/camera.ts index 612cfac..3ba0315 100644 --- a/src/experiences/core/home/camera.ts +++ b/src/experiences/home/camera.ts @@ -6,19 +6,13 @@ import { HomeExperience } from "."; import { Navigation } from "./navigation"; // BLUEPRINTS -import { ExperienceBasedBlueprint } from "~/experiences/blueprints/experience-based.blueprint"; +import { ExperienceBasedBlueprint } from "~/blueprints/experiences/experience-based.blueprint"; -// MODELS -import { - ANIMATION_ENDED, - ANIMATION_STARTED, - CONSTRUCTED, - DESTRUCTED, -} from "~/common/event.model"; -import { CAMERA_UNAVAILABLE, WRONG_PARAM } from "~/common/error.model"; +// STATIC +import { events, errors } from "~/static"; // CONFIG -import { Config } from "~/experiences/config"; +import { Config } from "~/config"; export class Camera extends ExperienceBasedBlueprint { protected readonly _experience = new HomeExperience(); @@ -28,10 +22,10 @@ export class Camera extends ExperienceBasedBlueprint { private readonly _appDebug = this._experience.app.debug; private readonly _timeline = gsap.timeline({ onStart: () => { - this.emit(ANIMATION_STARTED); + this.emit(events.ANIMATION_STARTED); }, onComplete: () => { - this.emit(ANIMATION_ENDED); + this.emit(events.ANIMATION_ENDED); }, }); @@ -98,11 +92,11 @@ export class Camera extends ExperienceBasedBlueprint { far: this._appCameraInstance.far, }; - this.emit(CONSTRUCTED); + this.emit(events.CONSTRUCTED); } public destruct() { - this.emit(DESTRUCTED); + this.emit(events.DESTRUCTED); } public cameraZoomIn() { @@ -135,9 +129,11 @@ export class Camera extends ExperienceBasedBlueprint { cameraIndex <= this.cameras.length - 1 ) ) - throw new Error("Camera index not available", { cause: WRONG_PARAM }); + throw new Error("Camera index not available", { + cause: errors.WRONG_PARAM, + }); if (!(this._appCameraInstance instanceof PerspectiveCamera)) - throw new Error(undefined, { cause: CAMERA_UNAVAILABLE }); + throw new Error(undefined, { cause: errors.CAMERA_UNAVAILABLE }); const currentCamera = this.cameras[this.currentCameraIndex]; const nextCamera = this.cameras[cameraIndex]; @@ -177,6 +173,7 @@ export class Camera extends ExperienceBasedBlueprint { /** * Set the camera look at position. + * * @param v3 The {@link Vector3} position where the the camera will look at. */ public setCameraLookAt(v3 = new Vector3()) { @@ -197,51 +194,5 @@ export class Camera extends ExperienceBasedBlueprint { return this._appCamera; } - /** - * Move the camera position with transition from - * the origin position to the passed position and, - * update the lookAt position using the passed lookAt position. - * - * @param toPosition The new camera position. - * @param lookAt Where the camera will look at. - * - * @deprecated use {@link Navigation}'s `updateCameraPosition` - */ - public updateCameraPosition( - toPosition = new Vector3(), - lookAt = new Vector3(), - onStart: gsap.Callback = () => {}, - onUpdate: gsap.Callback = () => {}, - onComplete: gsap.Callback = () => {} - ) { - if (!this._experience.app?.camera.instance) return this._timeline; - - const lookAtA = this._lookAtPosition.clone(); - const lookAtB = lookAt.clone(); - - return this._timeline.to(this._experience.app.camera.instance.position, { - x: toPosition.x, - y: toPosition.y, - z: toPosition.z, - duration: Config.GSAP_ANIMATION_DURATION, - ease: Config.GSAP_ANIMATION_EASE, - onStart: () => { - gsap.to(lookAtA, { - x: lookAtB.x, - y: lookAtB.y, - z: lookAtB.z, - duration: Config.GSAP_ANIMATION_DURATION * 0.55, - ease: Config.GSAP_ANIMATION_EASE, - onUpdate: () => { - this?.setCameraLookAt(lookAtA); - }, - }); - onStart(); - }, - onUpdate, - onComplete, - }); - } - public update() {} } diff --git a/src/experiences/core/home/composer.ts b/src/experiences/home/composer.ts similarity index 72% rename from src/experiences/core/home/composer.ts rename to src/experiences/home/composer.ts index ebd5384..bec605a 100644 --- a/src/experiences/core/home/composer.ts +++ b/src/experiences/home/composer.ts @@ -9,19 +9,19 @@ import { OutputPass } from "three/examples/jsm/postprocessing/OutputPass.js"; import { HomeExperience } from "."; // BLUEPRINTS -import { ExperienceBasedBlueprint } from "~/experiences/blueprints/experience-based.blueprint"; +import { ExperienceBasedBlueprint } from "~/blueprints/experiences/experience-based.blueprint"; export class Composer extends ExperienceBasedBlueprint { protected readonly _experience = new HomeExperience(); - protected readonly _appRender = this._experience.app.renderer; - protected readonly _appCamera = this._experience.app.camera; - protected readonly _renderer = this._experience.renderer; - protected readonly _appSizes = this._experience.app.sizes; - protected readonly _passes: { [name: string]: Pass } = {}; - protected _effect?: EffectComposer; - protected _renderPass?: RenderPass; - protected _outputPass?: OutputPass; - protected onResize?: () => unknown; + private readonly _appRender = this._experience.app.renderer; + private readonly _appCamera = this._experience.app.camera; + private readonly _appSizes = this._experience.app.sizes; + private readonly _passes: { [name: string]: Pass } = {}; + + private _effect?: EffectComposer; + private _renderPass?: RenderPass; + private _outputPass?: OutputPass; + private _onResize?: () => unknown; public get effect() { return this._effect; @@ -32,15 +32,15 @@ export class Composer extends ExperienceBasedBlueprint { this._effect.setPixelRatio(this._appSizes.pixelRatio); this._effect.setSize(this._appSizes.width, this._appSizes.height); - this.onResize = () => { + this._onResize = () => { this._effect?.setPixelRatio(this._appSizes.pixelRatio); this._effect?.setSize(this._appSizes.width, this._appSizes.height); }; - this._appSizes.on("resize", this.onResize); + this._appSizes.on("resize", this._onResize); } public destruct(): void { - this.onResize && this._appSizes.off("resize", this.onResize); + this._onResize && this._appSizes.off("resize", this._onResize); this._effect?.dispose(); this._effect = undefined; } diff --git a/src/experiences/core/home/debug.ts b/src/experiences/home/debug.ts similarity index 89% rename from src/experiences/core/home/debug.ts rename to src/experiences/home/debug.ts index a057fcc..2b3ab66 100644 --- a/src/experiences/core/home/debug.ts +++ b/src/experiences/home/debug.ts @@ -13,13 +13,15 @@ import GUI from "three/examples/jsm/libs/lil-gui.module.min.js"; // EXPERIENCE import { HomeExperience } from "."; -import { Config } from "~/experiences/config"; + +// CONFIG +import { Config } from "~/config"; // BLUEPRINTS -import { ExperienceBasedBlueprint } from "~/experiences/blueprints/experience-based.blueprint"; +import { ExperienceBasedBlueprint } from "~/blueprints/experiences/experience-based.blueprint"; -// MODELS -import { DESTRUCTED } from "~/common/event.model"; +// STATIC +import { DESTRUCTED } from "~/static/event.static"; export class Debug extends ExperienceBasedBlueprint { protected readonly _experience = new HomeExperience(); @@ -28,11 +30,9 @@ export class Debug extends ExperienceBasedBlueprint { protected readonly _camera = this._experience.camera; protected readonly _worldCameraHelpers?: CameraHelper[] = []; - /** Graphic user interface of the experience instance */ protected _gui?: GUI; /** Indicate where the camera is looking at. */ protected cameraLookAtPointIndicator?: Mesh; - protected _cameraCurvePathLine?: Line; construct() { diff --git a/src/experiences/core/home/index.ts b/src/experiences/home/index.ts similarity index 81% rename from src/experiences/core/home/index.ts rename to src/experiences/home/index.ts index 45394ae..5281b34 100644 --- a/src/experiences/core/home/index.ts +++ b/src/experiences/home/index.ts @@ -9,18 +9,20 @@ import { World } from "./world"; import { Navigation } from "./navigation"; import { Debug } from "./debug"; -// BLUEPRINT -import { - ExperienceBlueprint, - type ExperienceProps, -} from "~/experiences/blueprints/experience.blueprint"; +// BLUEPRINTS +import { ExperienceBlueprint } from "~/blueprints/experiences/experience.blueprint"; -// MODELS -import { LOADED } from "~/common/event.model"; +// CONFIG +import { Config } from "~/config"; + +// STATIC +import { events } from "~/static"; -// ERRORS -import { ErrorFactory } from "~/experiences/errors/error.factory"; -import { Config } from "~/experiences/config"; +// ERROR +import { ErrorFactory } from "~/errors"; + +// MODELS +import type { ExperienceConstructorProps } from "~/common/experiences/experience.model"; export class HomeExperience extends ExperienceBlueprint { ui?: UI; @@ -33,7 +35,7 @@ export class HomeExperience extends ExperienceBlueprint { navigation?: Navigation; debug?: Debug; - constructor(_?: Omit) { + constructor(_?: Omit) { try { super( HomeExperience._self ?? { @@ -87,7 +89,7 @@ export class HomeExperience extends ExperienceBlueprint { this.router?.construct(); this.renderer?.construct(); this.composer?.construct(); - this.loader?.on(LOADED, () => { + this.loader?.on(events.LOADED, () => { try { this.camera?.construct(); this.world?.construct(); diff --git a/src/experiences/core/home/loader.ts b/src/experiences/home/loader.ts similarity index 61% rename from src/experiences/core/home/loader.ts rename to src/experiences/home/loader.ts index d729e2f..46a5564 100644 --- a/src/experiences/core/home/loader.ts +++ b/src/experiences/home/loader.ts @@ -3,17 +3,35 @@ import { Texture, LinearSRGBColorSpace, CubeTexture } from "three"; // EXPERIENCE import { HomeExperience } from "."; -// MODELS +// STATIC import { CONSTRUCTED, DESTRUCTED, LOADED, PROGRESSED, STARTED, -} from "~/common/event.model"; +} from "~/static/event.static"; // BLUEPRINTS -import { ExperienceBasedBlueprint } from "~/experiences/blueprints/experience-based.blueprint"; +import { ExperienceBasedBlueprint } from "~/blueprints/experiences/experience-based.blueprint"; + +// ASSETS +import scene_1_model from "~/assets/models/scene_1/model.glb?url"; +import scene_1_lights_baked_texture from "~/assets/models/scene_1/lights_baked_texture.jpg?url"; +import scene_1no_lights_baked_texture from "~/assets/models/scene_1/no_lights_baked_texture.jpg?url"; +import scene_1_woods_lights_baked_texture from "~/assets/models/scene_1/woods_lights_baked_texture.jpg?url"; + +import scene_1_woods_no_lights_baked_texture from "~/assets/models/scene_1/woods_no_lights_baked_texture.jpg?url"; + +import scene_1_tree_baked_texture from "~/assets/models/scene_1/tree_baked_texture.jpg?url"; +import scene_2_model from "~/assets/models/scene_2/model.glb?url"; +import scene_2_baked_texture from "~/assets/models/scene_2/baked_texture.jpg?url"; +import scene_3_model from "~/assets/models/scene_3/model.glb?url"; +import scene_3_baked_texture from "~/assets/models/scene_3/baked_texture.jpg?url"; +import scene_container_model from "~/assets/models/scene_container/model.glb?url"; +import scene_container_baked_texture from "~/assets/models/scene_container/baked_texture.jpg?url"; +import cloudAlphaMapTexture from "~/assets/textures/cloudAlphaMap.jpg?url"; +import rocksAlphaMapTexture from "~/assets/textures/rocksAlphaMap.jpg?url"; export class Loader extends ExperienceBasedBlueprint { protected readonly _experience = new HomeExperience(); @@ -26,86 +44,86 @@ export class Loader extends ExperienceBasedBlueprint { super(); // RESOURCES - this._appResources.setDracoLoader("/decoders/draco/"); + this._appResources.setDracoLoader("/decoders/draco-gltf/"); this._appResources.setSources([ // SCENE 1 { name: "scene_1", type: "gltfModel", - path: "/models/scene_1/model.glb", + path: scene_1_model, }, { name: "scene_1_lights_baked_texture", type: "texture", - path: "/models/scene_1/lights_baked_texture.jpg", + path: scene_1_lights_baked_texture, }, { name: "scene_1_no_lights_baked_texture", type: "texture", - path: "/models/scene_1/no_lights_baked_texture.jpg", + path: scene_1no_lights_baked_texture, }, { name: "scene_1_woods_lights_baked_texture", type: "texture", - path: "/models/scene_1/woods_lights_baked_texture.jpg", + path: scene_1_woods_lights_baked_texture, }, { name: "scene_1_woods_no_lights_baked_texture", type: "texture", - path: "/models/scene_1/woods_no_lights_baked_texture.jpg", + path: scene_1_woods_no_lights_baked_texture, }, { name: "scene_1_tree_baked_texture", type: "texture", - path: "/models/scene_1/tree_baked_texture.jpg", + path: scene_1_tree_baked_texture, }, // SCENE 2 { name: "scene_2", type: "gltfModel", - path: "/models/scene_2/model.glb", + path: scene_2_model, }, { name: "scene_2_baked_texture", type: "texture", - path: "/models/scene_2/baked_texture.jpg", + path: scene_2_baked_texture, }, // SCENE 3 { name: "scene_3", type: "gltfModel", - path: "/models/scene_3/model.glb", + path: scene_3_model, }, { name: "scene_3_baked_texture", type: "texture", - path: "/models/scene_3/baked_texture.jpg", + path: scene_3_baked_texture, }, // SCENE CONTAINER { name: "scene_container", type: "gltfModel", - path: "/models/scene_container/model.glb", + path: scene_container_model, }, { name: "scene_container_baked_texture", type: "texture", - path: "/models/scene_container/baked_texture.jpg", + path: scene_container_baked_texture, }, // OTHER TEXTURES { name: "cloudAlphaMap", type: "texture", - path: "/textures/cloudAlphaMap.jpg", + path: cloudAlphaMapTexture, }, { name: "rocksAlphaMap", type: "texture", - path: "/textures/rocksAlphaMap.png", + path: rocksAlphaMapTexture, }, ]); } diff --git a/src/experiences/core/home/navigation.ts b/src/experiences/home/navigation.ts similarity index 95% rename from src/experiences/core/home/navigation.ts rename to src/experiences/home/navigation.ts index 2e3208e..b504980 100644 --- a/src/experiences/core/home/navigation.ts +++ b/src/experiences/home/navigation.ts @@ -6,11 +6,13 @@ import gsap from "gsap"; import { HomeExperience } from "./"; // BLUEPRINTS -import { ExperienceBasedBlueprint } from "~/experiences/blueprints/experience-based.blueprint"; -import { ANIMATION_ENDED, ANIMATION_STARTED } from "~/common/event.model"; +import { ExperienceBasedBlueprint } from "~/blueprints/experiences/experience-based.blueprint"; + +// STATIC +import { ANIMATION_ENDED, ANIMATION_STARTED } from "~/static/event.static"; // CONFIG -import { Config } from "~/experiences/config"; +import { Config } from "~/config"; /** * @original-author {@link @brunosimon} / https://github.com/brunonsimon @@ -19,6 +21,7 @@ import { Config } from "~/experiences/config"; */ export class Navigation extends ExperienceBasedBlueprint { protected _experience = new HomeExperience(); + private readonly _targetElement = this._experience.app.renderer.instance.domElement; private readonly _appCamera = this._experience.app.camera; @@ -42,6 +45,7 @@ export class Navigation extends ExperienceBasedBlueprint { }, 100); }, }); + private _view: { enabled?: boolean; center?: Vector3; diff --git a/src/experiences/core/home/renderer.ts b/src/experiences/home/renderer.ts similarity index 85% rename from src/experiences/core/home/renderer.ts rename to src/experiences/home/renderer.ts index d66869a..5c3b2bd 100644 --- a/src/experiences/core/home/renderer.ts +++ b/src/experiences/home/renderer.ts @@ -1,6 +1,5 @@ import { Box3, - Color, LinearSRGBColorSpace, Matrix4, Mesh, @@ -15,7 +14,7 @@ import { import { HomeExperience } from "."; // INTERFACES -import { ExperienceBasedBlueprint } from "~/experiences/blueprints/experience-based.blueprint"; +import { ExperienceBasedBlueprint } from "~/blueprints/experiences/experience-based.blueprint"; export interface PortalAssets { mesh: THREE.Mesh; @@ -33,26 +32,64 @@ export interface PortalMeshCorners { /** Renderer */ export class Renderer extends ExperienceBasedBlueprint { protected readonly _experience = new HomeExperience(); - protected readonly _appRenderer = this._experience.app.renderer; - protected readonly _appRendererInstance = + + private readonly _appRenderer = this._experience.app.renderer; + private readonly _appRendererInstance = this._experience.app.renderer.instance; - protected readonly _renderPortalAssets: { + private readonly _renderPortalAssets: { [callbackName: string]: { assets: PortalAssets; corners: PortalMeshCorners; }; } = {}; - protected readonly beforeRenderUpdateCallbacks: { + private readonly beforeRenderUpdateCallbacks: { [key: string]: () => unknown; } = {}; - protected _currentRenderTarget = this._appRendererInstance.getRenderTarget(); - protected _currentXrEnabled = this._appRendererInstance.xr.enabled; - protected _currentShadowAutoUpdate = + + private _currentRenderTarget = this._appRendererInstance.getRenderTarget(); + private _currentXrEnabled = this._appRendererInstance.xr.enabled; + private _currentShadowAutoUpdate = this._appRendererInstance.shadowMap.autoUpdate; - protected _portalBottomLeftCorner = new Vector3(); - protected _portalBottomRightCorner = new Vector3(); - protected _portalTopLeftCorner = new Vector3(); - protected _portalReflectedPosition = new Vector3(); + private _portalBottomLeftCorner = new Vector3(); + private _portalBottomRightCorner = new Vector3(); + private _portalTopLeftCorner = new Vector3(); + + private _renderPortal( + mesh: Mesh, + meshWebGLTexture: WebGLRenderTarget, + portalCamera: PerspectiveCamera, + corners: PortalMeshCorners + ) { + mesh.localToWorld( + this._portalBottomLeftCorner.set( + corners.bottomLeft.x, + corners.bottomLeft.y, + corners.bottomLeft.z + ) + ); + mesh.localToWorld( + this._portalBottomRightCorner.set( + corners.bottomRight.x, + corners.bottomRight.y, + corners.bottomRight.z + ) + ); + mesh.localToWorld( + this._portalTopLeftCorner.set( + corners.topLeft.x, + corners.topLeft.y, + corners.topLeft.z + ) + ); + + this._appRendererInstance.setRenderTarget(meshWebGLTexture); + this._appRendererInstance.state.buffers.depth.setMask(true); + if (this._appRendererInstance.autoClear === false) + this._appRendererInstance.clear(); + mesh.visible = false; + this._appRendererInstance.render(this._experience.app.scene, portalCamera); + mesh.visible = true; + } public construct() { // Configure renderer behaviors @@ -86,7 +123,7 @@ export class Renderer extends ExperienceBasedBlueprint { this._appRendererInstance.shadowMap.autoUpdate; this._appRendererInstance.xr.enabled = false; this._appRendererInstance.shadowMap.autoUpdate = false; - this.renderPortal( + this._renderPortal( this._renderPortalAssets[key].assets.mesh, this._renderPortalAssets[key].assets.meshWebGLTexture, this._renderPortalAssets[key].assets.meshCamera, @@ -120,43 +157,6 @@ export class Renderer extends ExperienceBasedBlueprint { this._appRenderer.afterRenderUpdate = undefined; } - public renderPortal( - mesh: Mesh, - meshWebGLTexture: WebGLRenderTarget, - portalCamera: PerspectiveCamera, - corners: PortalMeshCorners - ) { - mesh.localToWorld( - this._portalBottomLeftCorner.set( - corners.bottomLeft.x, - corners.bottomLeft.y, - corners.bottomLeft.z - ) - ); - mesh.localToWorld( - this._portalBottomRightCorner.set( - corners.bottomRight.x, - corners.bottomRight.y, - corners.bottomRight.z - ) - ); - mesh.localToWorld( - this._portalTopLeftCorner.set( - corners.topLeft.x, - corners.topLeft.y, - corners.topLeft.z - ) - ); - - this._appRendererInstance.setRenderTarget(meshWebGLTexture); - this._appRendererInstance.state.buffers.depth.setMask(true); - if (this._appRendererInstance.autoClear === false) - this._appRendererInstance.clear(); - mesh.visible = false; - this._appRendererInstance.render(this._experience.app.scene, portalCamera); - mesh.visible = true; - } - public addPortalAssets(portalName: string, assets: PortalAssets): void { // Calculate width, height const boundingBox = new Box3().setFromObject(assets.mesh); diff --git a/src/experiences/core/home/router.ts b/src/experiences/home/router.ts similarity index 74% rename from src/experiences/core/home/router.ts rename to src/experiences/home/router.ts index d51b239..54a2ad5 100644 --- a/src/experiences/core/home/router.ts +++ b/src/experiences/home/router.ts @@ -1,76 +1,83 @@ -import type { RouteRecordNormalized, RouteRecordRaw } from "#vue-router"; -// EXPERIENCES -import { HomeExperience } from "."; - -// MODELS -import { CHANGED } from "~/common/event.model"; -import { WRONG_PARAM } from "~/common/error.model"; - -// BLUEPRINTS -import { ExperienceBasedBlueprint } from "~/experiences/blueprints/experience-based.blueprint"; - -export class Router extends ExperienceBasedBlueprint { - protected _experience = new HomeExperience(); - protected _router = useRouter(); - protected _route = useRoute(); - protected _availableRoutes: { [routeName: string]: RouteRecordRaw } = {}; - protected _currentRouteName?: string; - - constructor() { - super(); - - try { - const ROUTES = this._router.getRoutes(); - ROUTES.forEach((route) => { - if (route.name === undefined) throw new Error("", { cause: route }); - }); - } catch (_: any) { - const CAUSE = _.cause as RouteRecordNormalized | undefined; - if (!CAUSE?.children.length) return; - - CAUSE.children.forEach((route) => { - this._availableRoutes[route.name?.toString() ?? ""] = route; - }); - } - - this._setCurrentRouteIndexFromName(this._route.name?.toString()); - - this.on(CHANGED, (route: RouteRecordNormalized) => { - const ROUTE_NAME = route.name?.toString(); - this._setCurrentRouteIndexFromName(ROUTE_NAME); - }); - } - - private _setCurrentRouteIndexFromName(routeName?: string) { - if (!routeName || !this._availableRoutes[routeName]) - throw new Error("Route name not available", { cause: WRONG_PARAM }); - - this._currentRouteName = routeName; - } - - public get availableRoutes() { - return this._availableRoutes; - } - - public get currentRoute() { - if (!this._currentRouteName) return undefined; - - return this.availableRoutes[this._currentRouteName]; - } - - public get currentRouteName() { - return this._currentRouteName; - } - - public get currentRouteKey() { - if (!this._currentRouteName) return undefined; - - return this.availableRoutes[this._currentRouteName].meta?.key; - } - - public construct(): void {} - - public destruct(): void {} - - public update(): void {} -} +import type { RouteRecordNormalized, RouteRecordRaw } from "#vue-router"; +// EXPERIENCES +import { HomeExperience } from "."; + +// STATIC +import { events, errors } from "~/static"; + +// BLUEPRINTS +import { ExperienceBasedBlueprint } from "~/blueprints/experiences/experience-based.blueprint"; + +export class Router extends ExperienceBasedBlueprint { + protected _experience = new HomeExperience(); + protected _router = useRouter(); + protected _route = useRoute(); + protected _availableRoutes: { [routeName: string]: RouteRecordRaw } = {}; + protected _currentRouteName?: string; + + constructor() { + super(); + + try { + const ROUTES = this._router.getRoutes(); + ROUTES.forEach((route) => { + if (route.name === undefined) throw new Error("", { cause: route }); + }); + } catch (_: any) { + const CAUSE = _.cause as RouteRecordNormalized | undefined; + if (!CAUSE?.children.length) return; + + CAUSE.children.forEach((route) => { + this._availableRoutes[route.name?.toString() ?? ""] = route; + }); + } + + this._setCurrentRouteIndexFromName(this._route.name?.toString()); + + this.on(events.CHANGED, (route: RouteRecordNormalized) => { + const ROUTE_NAME = route.name?.toString(); + this._setCurrentRouteIndexFromName(ROUTE_NAME); + }); + } + + private _setCurrentRouteIndexFromName(routeName?: string) { + if (!routeName || !this._availableRoutes[routeName]) + throw new Error("Route name not available", { + cause: errors.WRONG_PARAM, + }); + + this._currentRouteName = routeName; + } + + public get availableRoutes() { + return this._availableRoutes; + } + + public get currentRoute() { + if (!this._currentRouteName) return undefined; + + return this.availableRoutes[this._currentRouteName]; + } + + public get currentRouteName() { + return this._currentRouteName; + } + + public get currentRouteKey() { + if (!this._currentRouteName) return undefined; + + return this.availableRoutes[this._currentRouteName].meta?.key; + } + + public construct(): void { + this.emit(events.CONSTRUCTED); + } + + public destruct(): void { + this.emit(events.DESTRUCTED); + } + + public update(): void { + this.emit(events.UPDATED); + } +} diff --git a/src/experiences/home/ui.ts b/src/experiences/home/ui.ts new file mode 100644 index 0000000..734d121 --- /dev/null +++ b/src/experiences/home/ui.ts @@ -0,0 +1,107 @@ +import GSAP from "gsap"; + +// EXPERIENCE +import { HomeExperience } from "."; + +// CONFIG +import { Config } from "~/config"; + +// BLUEPRINTS +import { ExperienceBasedBlueprint } from "~/blueprints/experiences/experience-based.blueprint"; + +/** + * Class in charge of all the direct interactions with the DOM HTML elements. + */ +export class UI extends ExperienceBasedBlueprint { + protected readonly _experience = new HomeExperience(); + + private _loadedResourcesProgressLineElements?: HTMLElement | null; + private _loadedResourcesProgressElements?: HTMLElement | null; + private _lastLoadedResourceElement?: HTMLElement | null; + + constructor() { + super(); + + const _LOADED_RESOURCES_PROGRESS_LINE_ELEMENT = document.getElementById( + "loaded-resources-progress-line" + ); + const _LOADED_RESOURCES_PROGRESS_ELEMENT = document.getElementById( + "loaded-resources-progress" + ); + const _LAST_LOADED_RESOURCE_ELEMENT = document.getElementById( + "last-loaded-resource" + ); + + if (_LOADED_RESOURCES_PROGRESS_LINE_ELEMENT) + this._loadedResourcesProgressLineElements = + _LOADED_RESOURCES_PROGRESS_LINE_ELEMENT; + if (_LOADED_RESOURCES_PROGRESS_ELEMENT) + this._loadedResourcesProgressElements = + _LOADED_RESOURCES_PROGRESS_ELEMENT; + if (_LAST_LOADED_RESOURCE_ELEMENT) + this._lastLoadedResourceElement = _LAST_LOADED_RESOURCE_ELEMENT; + } + + construct() { + // EVENTS + this._experience.loader?.on("start", () => { + this._lastLoadedResourceElement?.classList.remove("animate-pulse"); + if (this._loadedResourcesProgressLineElements) + this._loadedResourcesProgressLineElements.style.width = "0%"; + if (this._loadedResourcesProgressElements) + this._loadedResourcesProgressElements.innerHTML = "0%"; + }); + + this._experience.loader?.on("progress", (progress: number, url: string) => { + if (this._loadedResourcesProgressLineElements) + this._loadedResourcesProgressLineElements.style.width = progress + "%"; + if (this._loadedResourcesProgressElements) + this._loadedResourcesProgressElements.innerHTML = + progress.toFixed(0) + "%"; + if (this._lastLoadedResourceElement) + this._lastLoadedResourceElement.innerHTML = url.replace(/^.*\//, ""); + }); + + this._experience.loader?.on("load", () => { + if (this._loadedResourcesProgressElements) + this._loadedResourcesProgressElements.innerHTML = "100%"; + if (this._loadedResourcesProgressLineElements) + this._loadedResourcesProgressLineElements.style.width = "100%"; + + setTimeout(() => { + if (this._lastLoadedResourceElement) + this._lastLoadedResourceElement.innerHTML = + "Resources Loaded Successfully"; + + this.intro(); + this.emit("ready"); + }, 1000); + }); + + this._experience.app.resources.startLoading(); + } + + destruct() { + this._loadedResourcesProgressLineElements = undefined; + this._loadedResourcesProgressElements = undefined; + this._lastLoadedResourceElement = undefined; + } + + intro() { + const _TIMELINE = GSAP.timeline(); + _TIMELINE.to("#landing-view-wrapper", { + duration: Config.GSAP_ANIMATION_DURATION, + ease: Config.GSAP_ANIMATION_EASE, + opacity: 0, + delay: 2, + onComplete: () => { + const _LANDING_VIEW_WRAPPER = document.getElementById( + "landing-view-wrapper" + ); + + if (_LANDING_VIEW_WRAPPER?.style) + _LANDING_VIEW_WRAPPER.style.display = "none"; + }, + }); + } +} diff --git a/src/experiences/core/home/world/index.ts b/src/experiences/home/world/index.ts similarity index 84% rename from src/experiences/core/home/world/index.ts rename to src/experiences/home/world/index.ts index 59bb40f..3241d30 100644 --- a/src/experiences/core/home/world/index.ts +++ b/src/experiences/home/world/index.ts @@ -19,16 +19,17 @@ import { Scene2Component } from "./scene-2.component"; import { Scene3Component } from "./scene-3.component"; // BLUEPRINTS -import { ExperienceBasedBlueprint } from "~/experiences/blueprints/experience-based.blueprint"; +import { ExperienceBasedBlueprint } from "~/blueprints/experiences/experience-based.blueprint"; // MODELS -import { CONSTRUCTED, DESTRUCTED } from "~/common/event.model"; -import { CAMERA_UNAVAILABLE } from "~/common/error.model"; -import { CONTACT_PAGE, HOME_PAGE, SKILL_PAGE } from "~/common/page.model"; +import { events, errors, pages } from "~/static"; -// INTERFACES -import type { Materials, SceneConfig } from "~/interfaces/experienceWorld"; -import type { SceneComponentBlueprint } from "~/experiences/blueprints/scene-component.blueprint"; +// MODELS +import type { Materials } from "~/common/experiences/experience-world.model"; +import type { SceneConfig } from "./model"; + +// BLUEPRINTS +import type { SceneComponentBlueprint } from "~/blueprints/experiences/scene-component.blueprint"; export class World extends ExperienceBasedBlueprint { protected readonly _experience = new HomeExperience(); @@ -53,7 +54,7 @@ export class World extends ExperienceBasedBlueprint { }; private _projectedSceneContainer?: Group; - public readonly mainSceneKey = HOME_PAGE; + public readonly mainSceneKey = pages.HOME_PAGE; public sceneContainer?: SceneContainerComponent; public scene1?: Scene1Component; @@ -139,13 +140,13 @@ export class World extends ExperienceBasedBlueprint { this.group?.clear(); this.group = undefined; - this.emit(DESTRUCTED, this); + this.emit(events.DESTRUCTED, this); } } public async construct() { if (!(this._appCamera.instance instanceof PerspectiveCamera)) - throw new Error(undefined, { cause: CAMERA_UNAVAILABLE }); + throw new Error(undefined, { cause: errors.CAMERA_UNAVAILABLE }); this.group = new Group(); this._manager = new WorldManager(); @@ -204,26 +205,26 @@ export class World extends ExperienceBasedBlueprint { } if (this.scene1?.modelScene) { - this._availablePageScenes[HOME_PAGE] = this.scene1; + this._availablePageScenes[pages.HOME_PAGE] = this.scene1; this.group?.add(this.scene1.modelScene); } if (this.scene2?.modelScene) { this.scene2.modelScene.position.copy(this.projectedSceneConfig.position); - this._availablePageScenes[SKILL_PAGE] = this.scene2; + this._availablePageScenes[pages.SKILL_PAGE] = this.scene2; this.group?.add(this.scene2.modelScene); } if (this.scene3?.modelScene) { this.scene3.modelScene.position.copy(this.projectedSceneConfig.position); - this._availablePageScenes[CONTACT_PAGE] = this.scene3; + this._availablePageScenes[pages.CONTACT_PAGE] = this.scene3; this.group?.add(this.scene3.modelScene); } this._experience.app.scene.add(this.group); this._manager?.construct(); - this.emit(CONSTRUCTED, this); + this.emit(events.CONSTRUCTED, this); } public update() { @@ -232,5 +233,6 @@ export class World extends ExperienceBasedBlueprint { this.scene3?.update(); this.sceneContainer?.update(); this._manager?.update(); + this.emit(events.UPDATED, this); } } diff --git a/src/experiences/core/home/world/manager.ts b/src/experiences/home/world/manager.ts similarity index 92% rename from src/experiences/core/home/world/manager.ts rename to src/experiences/home/world/manager.ts index 860e311..e2a2f98 100644 --- a/src/experiences/core/home/world/manager.ts +++ b/src/experiences/home/world/manager.ts @@ -13,19 +13,16 @@ import gsap, { Power0 } from "gsap"; import { HomeExperience } from ".."; // BLUEPRINTS -import { ExperienceBasedBlueprint } from "@/experiences/blueprints/experience-based.blueprint"; +import { ExperienceBasedBlueprint } from "~/blueprints/experiences/experience-based.blueprint"; // CONFIG -import { Config } from "~/experiences/config"; +import { Config } from "~/config"; -// COMMONS -import { WRONG_PARAM } from "~/common/error.model"; +// STATIC +import { errors, events, pages } from "~/static"; // ERROR -import { ErrorFactory } from "~/experiences/errors/error.factory"; - -// EVENTS -import { CHANGED, CONSTRUCTED } from "~/common/event.model"; +import { ErrorFactory } from "~/errors"; // UTILS import { lerpPosition } from "~/utils/three-utils"; @@ -57,11 +54,11 @@ export class WorldManager extends ExperienceBasedBlueprint { vertexShader: camTransitionVert, fragmentShader: camTransitionFrag, }); + private _glassEffectOptions: gsap.TweenVars = { duration: 0.3, ease: Power0.easeIn, }; - private _world: typeof this._experience.world; private _prevSceneKey?: string; @@ -87,7 +84,6 @@ export class WorldManager extends ExperienceBasedBlueprint { target: 0, ease: 0.1, }; - /** * Enable auto curve path animation */ @@ -129,7 +125,7 @@ export class WorldManager extends ExperienceBasedBlueprint { if (this._supportedPageKeys.length < 2) throw new ErrorFactory( new Error("Unable to display the projected scene ", { - cause: WRONG_PARAM, + cause: errors.WRONG_PARAM, }) ); @@ -140,14 +136,14 @@ export class WorldManager extends ExperienceBasedBlueprint { ) throw new ErrorFactory( new Error("No enough camera found", { - cause: WRONG_PARAM, + cause: errors.WRONG_PARAM, }) ); if (!this._world) throw new ErrorFactory( new Error("World not initialized", { - cause: WRONG_PARAM, + cause: errors.WRONG_PARAM, }) ); @@ -300,13 +296,13 @@ export class WorldManager extends ExperienceBasedBlueprint { ) === -1 ) throw new ErrorFactory( - new Error("Page not supported", { cause: WRONG_PARAM }) + new Error("Page not supported", { cause: errors.WRONG_PARAM }) ); if (!this._world) throw new ErrorFactory( new Error("World not initialized", { - cause: WRONG_PARAM, + cause: errors.WRONG_PARAM, }) ); @@ -317,7 +313,7 @@ export class WorldManager extends ExperienceBasedBlueprint { ) throw new ErrorFactory( new Error("No enough camera found", { - cause: WRONG_PARAM, + cause: errors.WRONG_PARAM, }) ); @@ -395,8 +391,8 @@ export class WorldManager extends ExperienceBasedBlueprint { public construct() { this._world = this._experience.world; this._initScenes(); - this._experience.router?.on(CHANGED, () => this._setScene()); - this.emit(CONSTRUCTED, this); + this._experience.router?.on(events.CHANGED, () => this._setScene()); + this.emit(events.CONSTRUCTED, this); } public destruct() {} diff --git a/src/experiences/home/world/model.ts b/src/experiences/home/world/model.ts new file mode 100644 index 0000000..4f3bf6c --- /dev/null +++ b/src/experiences/home/world/model.ts @@ -0,0 +1,7 @@ +import { CatmullRomCurve3, Vector3 } from "three"; + +export interface SceneConfig { + position: Vector3; + center: Vector3; + cameraPath: CatmullRomCurve3; +} diff --git a/src/experiences/core/home/world/scene-1.component.ts b/src/experiences/home/world/scene-1.component.ts similarity index 90% rename from src/experiences/core/home/world/scene-1.component.ts rename to src/experiences/home/world/scene-1.component.ts index ee1b5d5..90f856b 100644 --- a/src/experiences/core/home/world/scene-1.component.ts +++ b/src/experiences/home/world/scene-1.component.ts @@ -16,7 +16,7 @@ import { import gsap from "gsap"; // BLUEPRINTS -import { SceneComponentBlueprint } from "~/experiences/blueprints/scene-component.blueprint"; +import { SceneComponentBlueprint } from "~/blueprints/experiences/scene-component.blueprint"; // SHADERS import bakedTextureFragment from "./shaders/scene-1/lights/fragment.glsl"; @@ -26,19 +26,24 @@ import coffeeSteamFragment from "./shaders/scene-1/coffeeSteam/fragment.glsl"; import coffeeSteamVertex from "./shaders/scene-1/coffeeSteam/vertex.glsl"; // CONFIGS -import { Config } from "~/experiences/config"; +import { Config } from "~/config"; -// MODELS -import { DESTRUCTED } from "~/common/event.model"; +// STATICS +import { DESTRUCTED } from "~/static/event.static"; // ERROR -import { ErrorFactory } from "~/experiences/errors/error.factory"; +import { ErrorFactory } from "~/errors"; -// INTERFACES +// MODELS import type { Materials, ModelChildrenMaterials, -} from "~/interfaces/experienceWorld"; +} from "~/common/experiences/experience-world.model"; + +// ASSETS +import monitor_a_screen_recording from "~/assets/videos/monitor_a_screen_recording.webm?url"; +import monitor_b_screen_recording from "~/assets/videos/monitor_b_screen_recording.webm?url"; +import phone_screen_recording from "~/assets/videos/phone_screen_recording.webm"; export class Scene1Component extends SceneComponentBlueprint { private _renderer = this._experience.renderer; @@ -170,15 +175,13 @@ export class Scene1Component extends SceneComponentBlueprint { } private _initScreesVideos() { - this.phoneScreenVideo = this._createVideoElement( - "/videos/phone_screen_recording.webm" - ); + this.phoneScreenVideo = this._createVideoElement(phone_screen_recording); this.monitorAScreenVideo = this._createVideoElement( - "/videos/monitor_a_screen_recording.webm" + monitor_a_screen_recording ); this.monitorBScreenVideo = this._createVideoElement( - "/videos/monitor_b_screen_recording.webm" + monitor_b_screen_recording ); } diff --git a/src/experiences/core/home/world/scene-2.component.ts b/src/experiences/home/world/scene-2.component.ts similarity index 84% rename from src/experiences/core/home/world/scene-2.component.ts rename to src/experiences/home/world/scene-2.component.ts index 542bad9..e16d645 100644 --- a/src/experiences/core/home/world/scene-2.component.ts +++ b/src/experiences/home/world/scene-2.component.ts @@ -1,10 +1,10 @@ import { MeshBasicMaterial } from "three"; // BLUEPRINTS -import { SceneComponentBlueprint } from "~/experiences/blueprints/scene-component.blueprint"; +import { SceneComponentBlueprint } from "~/blueprints/experiences/scene-component.blueprint"; -// INTERFACES -import type { Materials } from "~/interfaces/experienceWorld"; +// MODELS +import type { Materials } from "~/common/experiences/experience-world.model"; export class Scene2Component extends SceneComponentBlueprint { constructor() { diff --git a/src/experiences/core/home/world/scene-3.component.ts b/src/experiences/home/world/scene-3.component.ts similarity index 84% rename from src/experiences/core/home/world/scene-3.component.ts rename to src/experiences/home/world/scene-3.component.ts index f717f0a..8cf7b4d 100644 --- a/src/experiences/core/home/world/scene-3.component.ts +++ b/src/experiences/home/world/scene-3.component.ts @@ -1,19 +1,14 @@ -import { - CatmullRomCurve3, - Object3D, - Vector3, - type Object3DEventMap, - MeshBasicMaterial, -} from "three"; +import { Object3D, type Object3DEventMap, MeshBasicMaterial } from "three"; // BLUEPRINTS -import { SceneComponentBlueprint } from "~/experiences/blueprints/scene-component.blueprint"; +import { SceneComponentBlueprint } from "~/blueprints/experiences/scene-component.blueprint"; -// INTERFACES -import type { Materials } from "~/interfaces/experienceWorld"; +// MODELS +import type { Materials } from "~/common/experiences/experience-world.model"; export class Scene3Component extends SceneComponentBlueprint { private _initialPcTopBone?: Object3D; + public pcTopBone?: Object3D; constructor() { diff --git a/src/experiences/core/home/world/scene-container.component.ts b/src/experiences/home/world/scene-container.component.ts similarity index 80% rename from src/experiences/core/home/world/scene-container.component.ts rename to src/experiences/home/world/scene-container.component.ts index 7d227aa..fc8ac08 100644 --- a/src/experiences/core/home/world/scene-container.component.ts +++ b/src/experiences/home/world/scene-container.component.ts @@ -1,5 +1,5 @@ // EXPERIENCES -import { SceneComponentBlueprint } from "~/experiences/blueprints/scene-component.blueprint"; +import { SceneComponentBlueprint } from "~/blueprints/experiences/scene-component.blueprint"; export class SceneContainerComponent extends SceneComponentBlueprint { constructor() { diff --git a/src/experiences/core/home/world/shaders/glass-effect/fragment.glsl b/src/experiences/home/world/shaders/glass-effect/fragment.glsl similarity index 100% rename from src/experiences/core/home/world/shaders/glass-effect/fragment.glsl rename to src/experiences/home/world/shaders/glass-effect/fragment.glsl diff --git a/src/experiences/core/home/world/shaders/glass-effect/vertex.glsl b/src/experiences/home/world/shaders/glass-effect/vertex.glsl similarity index 100% rename from src/experiences/core/home/world/shaders/glass-effect/vertex.glsl rename to src/experiences/home/world/shaders/glass-effect/vertex.glsl diff --git a/src/experiences/core/home/world/shaders/partials/perlin2d.glsl b/src/experiences/home/world/shaders/partials/perlin2d.glsl similarity index 100% rename from src/experiences/core/home/world/shaders/partials/perlin2d.glsl rename to src/experiences/home/world/shaders/partials/perlin2d.glsl diff --git a/src/experiences/core/home/world/shaders/scene-1/coffeeSteam/fragment.glsl b/src/experiences/home/world/shaders/scene-1/coffeeSteam/fragment.glsl similarity index 100% rename from src/experiences/core/home/world/shaders/scene-1/coffeeSteam/fragment.glsl rename to src/experiences/home/world/shaders/scene-1/coffeeSteam/fragment.glsl diff --git a/src/experiences/core/home/world/shaders/scene-1/coffeeSteam/vertex.glsl b/src/experiences/home/world/shaders/scene-1/coffeeSteam/vertex.glsl similarity index 100% rename from src/experiences/core/home/world/shaders/scene-1/coffeeSteam/vertex.glsl rename to src/experiences/home/world/shaders/scene-1/coffeeSteam/vertex.glsl diff --git a/src/experiences/core/home/world/shaders/scene-1/lights/fragment.glsl b/src/experiences/home/world/shaders/scene-1/lights/fragment.glsl similarity index 100% rename from src/experiences/core/home/world/shaders/scene-1/lights/fragment.glsl rename to src/experiences/home/world/shaders/scene-1/lights/fragment.glsl diff --git a/src/experiences/core/home/world/shaders/scene-1/lights/vertex.glsl b/src/experiences/home/world/shaders/scene-1/lights/vertex.glsl similarity index 100% rename from src/experiences/core/home/world/shaders/scene-1/lights/vertex.glsl rename to src/experiences/home/world/shaders/scene-1/lights/vertex.glsl diff --git a/src/interfaces/experienceBase.ts b/src/interfaces/experienceBase.ts deleted file mode 100644 index 9c80b4d..0000000 --- a/src/interfaces/experienceBase.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Represent the base struct of all threejs experience classes in the application - */ -export interface ExperienceBase { - construct: (callback?: () => unknown) => unknown; - destruct: (callback?: () => unknown) => unknown; - update?: (callback?: () => unknown) => unknown; -} diff --git a/src/pages/index.vue b/src/pages/index.vue index 34b8ad1..bbb0274 100644 --- a/src/pages/index.vue +++ b/src/pages/index.vue @@ -2,7 +2,7 @@
- +
@@ -20,13 +20,13 @@ diff --git a/src/pages/index/contact.vue b/src/pages/index/contact.vue index 3d3ee7a..1309b4a 100644 --- a/src/pages/index/contact.vue +++ b/src/pages/index/contact.vue @@ -5,7 +5,7 @@