Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Neosoulink committed Dec 10, 2023
2 parents 671a9ba + 95de5d2 commit c385c87
Show file tree
Hide file tree
Showing 12 changed files with 287 additions and 217 deletions.
9 changes: 7 additions & 2 deletions src/experiences/factories/ExperienceBased.factory.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
//
import { EventEmitter } from "events";

// BLUEPRINTS
import type { ExperienceFactory } from "./Experience.factory";

// INTERFACES
import type { ExperienceBase } from "@/interfaces/experienceBase";

/** Represent a class that depend on {@link ExperienceFactory}. */
export abstract class ExperienceBasedFactory implements ExperienceBase {
export abstract class ExperienceBasedFactory
extends EventEmitter
implements ExperienceBase
{
protected abstract readonly _experience: ExperienceFactory;

public abstract construct(): unknown;
Expand Down
2 changes: 1 addition & 1 deletion src/experiences/pages/Home/Camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class Camera implements ExperienceBase {
if (!(this._appCamera?.instance instanceof PerspectiveCamera)) return;

this._appCamera.instance.fov = this.initialCameraFov;
this._appCamera.instance.far = 50;
this._appCamera.instance.far = 500;
this._appCamera.miniCamera?.position.set(10, 8, 30);

if (this._appDebug?.cameraControls) {
Expand Down
28 changes: 11 additions & 17 deletions src/experiences/pages/Home/Debug.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
BufferGeometry,
CameraHelper,
Line,
LineBasicMaterial,
Mesh,
Expand All @@ -8,7 +9,7 @@ import {
SphereGeometry,
Vector3,
} from "three";
import GUI from "lil-gui";
import GUI from "three/examples/jsm/libs/lil-gui.module.min.js";

// EXPERIENCE
import Experience from ".";
Expand All @@ -20,6 +21,7 @@ export default class Debug implements ExperienceBase {
protected readonly _experience = new Experience();
protected readonly _appDebug = this._experience.app.debug;
protected readonly _appCamera = this._experience.app.camera;
protected _worldSecondaryCameraHelper?: CameraHelper;

/** Graphic user interface of the experience instance */
protected _gui?: GUI;
Expand Down Expand Up @@ -50,15 +52,18 @@ export default class Debug implements ExperienceBase {

if (!this._gui || !this._experience.world) return;

// this._gui.add(
// { destruct_experience: () => this._experience?.destruct() },
// "destruct_experience"
// );

this.cameraLookAtPointIndicator = new Mesh(
new SphereGeometry(0.1, 12, 12),
new MeshBasicMaterial({ color: "#ff0040" })
);
this.cameraLookAtPointIndicator.visible = false;

if (!this._experience.world?.secondaryCamera) return;

this._worldSecondaryCameraHelper = new CameraHelper(
this._experience.world.secondaryCamera
);
this._experience.app.scene.add(this._worldSecondaryCameraHelper);

this._gui
.add(
Expand Down Expand Up @@ -120,17 +125,6 @@ export default class Debug implements ExperienceBase {
)
.name("Toggle auto camera animation");

this._gui
.add(
{
fn: () => {
this._experience.world?.nextScene();
},
},
"fn"
)
.name("Next Scene");

this._experience.app.scene.add(
this._cameraCurvePathLine,
this.cameraLookAtPointIndicator
Expand Down
2 changes: 1 addition & 1 deletion src/experiences/pages/Home/Loader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Mesh, MeshBasicMaterial, SRGBColorSpace, Texture } from "three";
import { MeshBasicMaterial, Texture, SRGBColorSpace } from "three";
import EventEmitter from "events";

// ---
Expand Down
186 changes: 116 additions & 70 deletions src/experiences/pages/Home/Renderer.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import {
ACESFilmicToneMapping,
Box3,
Matrix4,
Mesh,
PCFShadowMap,
PerspectiveCamera,
SRGBColorSpace,
Vector3,
WebGLRenderTarget,
} from "three";
import * as CameraUtils from "three/examples/jsm/utils/CameraUtils";

// EXPERIENCE
import HomeExperience from ".";
Expand All @@ -14,21 +18,33 @@ import { type ExperienceBase } from "@/interfaces/experienceBase";

export interface PortalAssets {
mesh: THREE.Mesh;
texture: THREE.WebGLRenderTarget;
camera: THREE.PerspectiveCamera;
meshWebGLTexture: THREE.WebGLRenderTarget;
meshCamera: THREE.PerspectiveCamera;
}

export interface PortalMeshCorners {
bottomLeft: Vector3;
bottomRight: Vector3;
topLeft: Vector3;
topRight: Vector3;
}

/** Renderer */
export default class Renderer implements ExperienceBase {
protected readonly _experience = new HomeExperience();
protected readonly _appRenderer = this._experience.app.renderer;
protected readonly _appRendererInstance =
this._experience.app.renderer.instance;
protected readonly _renderPortalAssets: {
[callbackName: string]: PortalAssets;
[callbackName: string]: {
assets: PortalAssets;
corners: PortalMeshCorners;
};
} = {};
protected _currentRenderTarget = this._appRenderer.instance.getRenderTarget();
protected _currentXrEnabled = this._appRenderer.instance.xr.enabled;
protected _currentRenderTarget = this._appRendererInstance.getRenderTarget();
protected _currentXrEnabled = this._appRendererInstance.xr.enabled;
protected _currentShadowAutoUpdate =
this._appRenderer.instance.shadowMap.autoUpdate;
this._appRendererInstance.shadowMap.autoUpdate;
protected _portalBottomLeftCorner = new Vector3();
protected _portalBottomRightCorner = new Vector3();
protected _portalTopLeftCorner = new Vector3();
Expand All @@ -37,21 +53,23 @@ export default class Renderer implements ExperienceBase {
constructor() {}

public construct() {
this._appRenderer.instance.useLegacyLights = true;
this._appRenderer.instance.outputColorSpace = SRGBColorSpace;
this._appRenderer.instance.toneMapping = ACESFilmicToneMapping;
this._appRenderer.instance.toneMappingExposure = 1;
this._appRenderer.instance.shadowMap.enabled = false;
this._appRenderer.instance.shadowMap.type = PCFShadowMap;
this._appRenderer.instance.setClearColor("#211d20");
this._appRenderer.instance.setSize(
this._experience.app.sizes.width,
this._experience.app.sizes.height
);
this._appRenderer.instance.setPixelRatio(
this._experience.app.sizes.pixelRatio
);
this._appRenderer.instance.localClippingEnabled = true;
// Configure renderer behaviors
~(() => {
this._appRendererInstance.outputColorSpace = SRGBColorSpace;
this._appRendererInstance.toneMapping = ACESFilmicToneMapping;
this._appRendererInstance.toneMappingExposure = 1;
this._appRendererInstance.shadowMap.enabled = false;
this._appRendererInstance.shadowMap.type = PCFShadowMap;
this._appRendererInstance.setClearColor("#211d20");
this._appRendererInstance.setSize(
this._experience.app.sizes.width,
this._experience.app.sizes.height
);
this._appRendererInstance.setPixelRatio(
this._experience.app.sizes.pixelRatio
);
this._appRendererInstance.localClippingEnabled = true;
})();

~(() => {
this._appRenderer.beforeRenderUpdate = () => {
Expand All @@ -60,24 +78,23 @@ export default class Renderer implements ExperienceBase {
Object.keys(this._renderPortalAssets).forEach((key: string) => {
if (this._renderPortalAssets[key]) {
this._currentRenderTarget =
this._appRenderer.instance.getRenderTarget();
this._currentXrEnabled = this._appRenderer.instance.xr.enabled;
this._appRendererInstance.getRenderTarget();
this._currentXrEnabled = this._appRendererInstance.xr.enabled;
this._currentShadowAutoUpdate =
this._appRenderer.instance.shadowMap.autoUpdate;
// Avoid camera modification
this._appRenderer.instance.xr.enabled = false;
// Avoid re-computing shadows
this._appRenderer.instance.shadowMap.autoUpdate = false;
this._appRendererInstance.shadowMap.autoUpdate;
this._appRendererInstance.xr.enabled = false;
this._appRendererInstance.shadowMap.autoUpdate = false;
this.renderPortal(
this._renderPortalAssets[key].mesh,
this._renderPortalAssets[key].texture,
this._renderPortalAssets[key].camera
this._renderPortalAssets[key].assets.mesh,
this._renderPortalAssets[key].assets.meshWebGLTexture,
this._renderPortalAssets[key].assets.meshCamera,
this._renderPortalAssets[key].corners
);
// restore the original rendering properties
this._appRenderer.instance.xr.enabled = this._currentXrEnabled;
this._appRenderer.instance.shadowMap.autoUpdate =
this._appRendererInstance.xr.enabled = this._currentXrEnabled;
this._appRendererInstance.shadowMap.autoUpdate =
this._currentShadowAutoUpdate;
this._appRenderer.instance.setRenderTarget(
this._appRendererInstance.setRenderTarget(
this._currentRenderTarget
);
}
Expand All @@ -92,49 +109,78 @@ export default class Renderer implements ExperienceBase {
}

public renderPortal(
mesh: THREE.Mesh,
texture: THREE.WebGLRenderTarget,
portalCamera: THREE.PerspectiveCamera
mesh: Mesh,
meshWebGLTexture: WebGLRenderTarget,
portalCamera: PerspectiveCamera,
corners: PortalMeshCorners
) {
// set the portal camera position to be reflected about the portal plane
mesh.worldToLocal(
this._portalReflectedPosition.copy(
this._experience.app.camera.instance?.position ?? new Vector3()
mesh.localToWorld(
this._portalBottomLeftCorner.set(
corners.bottomLeft.x,
corners.bottomLeft.y,
corners.bottomLeft.z
)
);
this._portalReflectedPosition.x *= -1.0;
this._portalReflectedPosition.z *= -1.0;

mesh.localToWorld(this._portalReflectedPosition);
portalCamera.position.copy(this._portalReflectedPosition);

mesh.localToWorld(this._portalBottomLeftCorner.set(50.05, -50.05, 0.0));
mesh.localToWorld(this._portalBottomRightCorner.set(-50.05, -50.05, 0.0));
mesh.localToWorld(this._portalTopLeftCorner.set(50.05, 50.05, 0.0));

// set the projection matrix to encompass the portal's frame
CameraUtils.frameCorners(
portalCamera,
this._portalBottomLeftCorner,
this._portalBottomRightCorner,
this._portalTopLeftCorner,
false
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
)
);

// render the portal
texture.texture.colorSpace = this._appRenderer.instance.outputColorSpace;

this._appRenderer.instance.setRenderTarget(texture);
this._appRenderer.instance.state.buffers.depth.setMask(true); // making sure the depth buffer is writable so it can be properly cleared, see #18897
if (this._appRenderer.instance.autoClear === false)
this._appRenderer.instance.clear();
mesh.visible = false; // hide this portal from its own rendering
this._appRenderer.instance.render(this._experience.app.scene, portalCamera);
mesh.visible = true; // re-enable this portal's visibility for general rendering
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 addPortalMeshAssets(portalName: string, assets: PortalAssets): void {
this._renderPortalAssets[portalName] = assets;
// Calculate width, height
const boundingBox = new Box3().setFromObject(assets.mesh);
const width = boundingBox.max.x - boundingBox.min.x;
const height = boundingBox.max.y - boundingBox.min.y;
const halfWidth = width / 2;
const halfHeight = height / 2;

// Define the corners of the plane in local coordinates
const corners = [
new Vector3(-halfWidth, -halfHeight, 0),
new Vector3(halfWidth, -halfHeight, 0),
new Vector3(-halfWidth, halfHeight, 0),
new Vector3(halfWidth, halfHeight, 0),
];

// Apply the mesh's position and rotation to the corners
const matrix = new Matrix4();
matrix.makeRotationFromQuaternion(assets.mesh.quaternion);
matrix.setPosition(assets.mesh.position);
matrix.compose(
assets.mesh.position,
assets.mesh.quaternion,
assets.mesh.scale
);
corners.map((corner) => corner.applyMatrix4(matrix));

this._renderPortalAssets[portalName] = {
assets,
corners: {
bottomLeft: corners[0],
bottomRight: corners[1],
topLeft: corners[2],
topRight: corners[3],
},
};
}

public removeBeforeUpdateCallback(portalName: string): void {
Expand Down
Loading

0 comments on commit c385c87

Please sign in to comment.