File

src/app/models/babylon-integration.ts

Constructor

constructor(globalService: GlobalService, dataService: CabriDataService, ngZone: NgZone, cd: ChangeDetectorRef)

Methods

startRender
startRender()
Returns: void
stopRender
stopRender()
Returns: void
Private bindEvents
bindEvents()

Binds the required events for a full experience.

Returns: void
Public createBabylonEngine
createBabylonEngine(canvas: HTMLCanvasElement)
Returns: void
changeHSL
changeHSL(value: boolean)
Returns: void
scaleBabylonCanvasAndEngine
scaleBabylonCanvasAndEngine()
Returns: void
showFPS
showFPS(value: boolean)

show babylon engine FPS

Returns: void
cleanWebGl
cleanWebGl(gl: any)
Returns: void
Public changeBabylonLoader
changeBabylonLoader()
Returns: void
disablePhysics
disablePhysics()
Returns: void
createSceneOptimizer
createSceneOptimizer()
Returns: void
customOptimizerSet
customOptimizerSet()
Returns: SceneOptimizerOptions
Public launchConfetti
launchConfetti(duration: number, position: Vector3)

Confetti animation

Parameters :
  • duration

    animation duration

Returns: void
initScene
initScene()
Returns: void
createCamera
createCamera()
Returns: void
cameraControl
cameraControl()
Returns: void
createLight
createLight()
Returns: void

Properties

canvas
canvas: HTMLCanvasElement
cd
cd: ChangeDetectorRef
centerlight
centerlight: Light
changeHSLStatus
changeHSLStatus: boolean
cleanup
cleanup: { remove: string; handles: any[]; }[]
dataService
dataService: CabriDataService
engine
engine: Engine
fps
fps: number
fpsInterval
fpsInterval: Timeout
fpsStatus
fpsStatus: boolean
globalService
globalService: GlobalService
light
light: PointLight
loaderCamera
loaderCamera: any
ngZone
ngZone: NgZone
oldInputElement
oldInputElement: HTMLCanvasElement
optimizer
optimizer: SceneOptimizer
optimizerOption
optimizerOption: SceneOptimizerOptions
scene
scene: Scene
sceneDisposeSubject
sceneDisposeSubject: any
import { ChangeDetectorRef, NgZone } from "@angular/core";
import {
	ArcRotateCamera,
	ArcRotateCameraPointersInput,
	Camera,
	Color3,
	DefaultLoadingScreen,
	Engine,
	HardwareScalingOptimization,
	HemisphericLight,
	LensFlaresOptimization,
	Light,
	Matrix,
	Mesh,
	ParticlesOptimization,
	PointLight,
	PostProcessesOptimization,
	Scene,
	SceneOptimizer,
	SceneOptimizerOptions,
	ShadowsOptimization,
	TextureOptimization,
	Vector3
} from "@babylonjs/core";
import { ignoreElements, Subject } from "rxjs";
import { AppUtils } from "../app-utils";
import { CabriDataService } from "../services/cabri-data.service";
import { GlobalService } from "../services/global.service";
import { CustomLoadingScreen } from "./babylon-custom-loading";
import { ParticleAnimName } from "./babylonjs-confetti";
import { BabylonJs5Confetti } from "./babylonjs5-confetti";
import { EngineIntegration } from "./engine-integration";

export class BabylonIntegration extends EngineIntegration {
	engine: Engine;
	scene: Scene;
	light: PointLight;
	centerlight: Light;

	oldInputElement: HTMLCanvasElement;
	changeHSLStatus: boolean;
	canvas: HTMLCanvasElement;
	cleanup: { remove: string; handles: any[] }[];
	sceneDisposeSubject: any;
	optimizerOption: SceneOptimizerOptions;
	optimizer: SceneOptimizer;
	fpsStatus: boolean;
	fps: number;
	fpsInterval: NodeJS.Timeout;
	loaderCamera: any;
	

	constructor(
		public globalService: GlobalService,
		public dataService: CabriDataService,
		public ngZone: NgZone,
		public cd?: ChangeDetectorRef
	) {
		super(globalService);
	}

	startRender() {
		if (this.engine && this.scene && (!this.engine["_activeRenderLoops"] || this.engine["_activeRenderLoops"].length === 0)) {
			this.ngZone.runOutsideAngular(() => {
				this.engine.runRenderLoop(() => this.scene.render());
			});
		}
	}

	stopRender() {
		if (this.engine) {
			this.ngZone.runOutsideAngular(() => {
				this.engine.stopRenderLoop();
			});
		}
	}

	/**
	 * Binds the required events for a full experience.
	 */
	private bindEvents(): void {
		//window.addEventListener("resize", this.engine.resize);
	}

	public createBabylonEngine(canvas: HTMLCanvasElement) {
		this.canvas = canvas;
		this.ngZone.runOutsideAngular(() => {
			if (!this.dataService.engineBabylon) {
				this.dataService.engineBabylon = this.engine = new Engine(canvas, false, {
					disableWebGL2Support: false,
					doNotHandleContextLost: true,
					audioEngine: true
				});
			} else {
				this.engine = this.dataService.engineBabylon;
				if (this.engine.activeView && this.engine.inputElement) {
					this.oldInputElement = this.engine.activeView.target;
					this.engine.unRegisterView(this.oldInputElement);
				}
				this.engine.inputElement = canvas as unknown as HTMLElement;
				this.engine.registerView(canvas);
			}
		});

		this.changeHSL(true);
		// this.resizeCanvas();
		const gl = this.engine._gl;
		this.cleanup = ["Buffer", "Framebuffer", "Renderbuffer", "Program", "Shader", "Texture"].map(suffix => {
			const remove = "delete" + suffix;
			const create = "create" + suffix;
			const original = gl[create];
			const handles = [];

			gl[create] = function () {
				const handle = original.apply(this, arguments);
				handles.push(handle);
				return handle;
			};

			return {
				remove,
				handles
			};
		});

		this.globalService.webGl1 = this.engine.webGLVersion === 1;
		this.globalService._lowPerformanceMode = this.engine.webGLVersion === 1 || this.globalService.lowPerformanceMode;
		this.globalService.lowPerformanceModeSetStorageValue();
		if (Engine && Engine.audioEngine) {
			// set babylon volume to default (music & sounds handled separately):
			Engine.audioEngine.setGlobalVolume(1);
		}
		// fix black shaders bug:
		if (!this.globalService.isIos) {
			this.engine.getCaps().highPrecisionShaderSupported = false;
		}
		this.scene = new Scene(this.engine);
		this.sceneDisposeSubject = new Subject<void>();
		this.scene.onDispose = () => {
			this.sceneDisposeSubject.next();
			this.sceneDisposeSubject.complete();
		};
		this.bindEvents();
		this.changeBabylonLoader();
		this.scene.onAfterRenderObservable.addOnce(() => {
			// disable physics if any engine (Cannon in narrations)
			this.disablePhysics();
			// this.removeFxaaSSao();
			if (!this.globalService.modeProd) {
				this.showFPS(true);
			}
			if (this.globalService.lowPerformanceMode) {
				if (!this.optimizer) {
					this.createSceneOptimizer();
				}
				this.optimizer.reset();
				this.optimizer.start();
			}
		});
		
	}

	changeHSL(value? : boolean) {
		if (value === true) {
			this.changeHSLStatus = true;
		} else if (value === false) {
			this.changeHSLStatus = false;
		} else {
			this.changeHSLStatus = !this.changeHSLStatus;
		}
		if (this.changeHSLStatus === true) {
			if (!this.globalService.isDesktop && !this.globalService.lowPerformanceMode) {
				this.scaleBabylonCanvasAndEngine();
			} else {
				if (this.globalService.isDesktop) {
					if (window.innerHeight > 2000 || window.innerWidth > 3000) {
						this.engine.setHardwareScalingLevel(2);
						this.globalService.hSL = 2;
					} else {
						this.engine.setHardwareScalingLevel(1);
						this.globalService.hSL = 1;
					}
				} else {
					this.changeHSLStatus = false;
				}
			}
		} else {
			this.engine.setHardwareScalingLevel(1);
			this.globalService.hSL = 1;
		}
		this.cd?.detectChanges();
	}

	scaleBabylonCanvasAndEngine() {
		// SCALING CANVAS
		this.canvas.style.aspectRatio = `${this.canvas.offsetWidth} / ${this.canvas.offsetHeight}`;
		const scaleCanvas = window.devicePixelRatio === 2 ? 2 : 1;
		const scaledCanvasWidth = this.canvas.offsetWidth * scaleCanvas;
		const scaledCanvasHeight = this.canvas.offsetHeight * scaleCanvas;
		const roundedscaledCanvasWidth = Math.round(scaledCanvasWidth);
		const roundedscaledCanvasHeight = Math.round(scaledCanvasHeight);
		this.canvas.width = roundedscaledCanvasWidth;
		this.canvas.height = roundedscaledCanvasHeight;

		// SCALING ENGINE
		let scale; // Change to 1 on retina screens to see blurry canvas.
		if (window.devicePixelRatio >= 1) {
			scale = 1.6; // HDPI displays
		} else {
			scale = 1.2; // no-HDPI displays
		}
		this.globalService.hSL = Math.round((scale / window.devicePixelRatio) * 10000) / 10000;
		// console.error("hSL = ", this.globalService.hSL);
		this.engine?.setHardwareScalingLevel(this.globalService.hSL);
		this.engine?.resize();
	}

	// dev menu:
	/**
	 * show babylon engine FPS
	 */
	showFPS(value?: boolean) {
		if (this.engine) {
			if (value === true) {
				this.fpsStatus = true;
			} else if (value === false) {
				this.fpsStatus = false;
			} else {
				this.fpsStatus = !this.fpsStatus;
			}
			if (this.fpsInterval) {
				clearInterval(this.fpsInterval);
				this.fpsInterval = null;
				this.fps = null;
			}
			if (this.fpsStatus === true) {
				this.fpsInterval = setInterval(() => {
					this.fps = Math.floor(this.engine.getFps());
					this.cd?.detectChanges();
				}, 1000);
			} else {
				if (this.fpsInterval) {
					clearInterval(this.fpsInterval);
					this.fpsInterval = null;
					this.fps = null;
					this.cd?.detectChanges();
				}
			}
		}
	}

	cleanWebGl(gl = null) {
		if (!gl && this.engine) {
			gl = this.engine._gl;
		}

		if (gl) {
			this.cleanup.forEach(kind => {
				// console.log("call", kind);
				for (let i = 0; i < kind.handles.length; i++) {
					// console.log("call", kind.remove, kind.handles[i]);
					gl[kind.remove].call(gl, kind.handles[i]);
				}
			});
		}
	}

	public changeBabylonLoader() {
		const loadingScreen = new CustomDefaultLoadingScreen(this.canvas,null,"#88B627");
		// replace the default loading screen
		this.engine.loadingScreen = loadingScreen;
	}

	disablePhysics() {
		if (this.scene) {
			this.scene.disablePhysicsEngine();
			this.scene.physicsEnabled = false;
		}
	}

	createSceneOptimizer() {
		// this.optimizerOption = SceneOptimizerOptions.LowDegradationAllowed(60);
		this.optimizerOption = this.customOptimizerSet();

		this.optimizer = new SceneOptimizer(this.scene, this.optimizerOption, false, false);
		this.optimizer.onNewOptimizationAppliedObservable.add(optim => {
			// console.log("Optimizer :" + optim.getDescription());
		});
	}

	customOptimizerSet(): SceneOptimizerOptions {
		const result = new SceneOptimizerOptions(25, 2000);

		let priority = 0;
		result.optimizations.push(new PostProcessesOptimization(priority));
		result.optimizations.push(new ParticlesOptimization(priority));

		// Next priority
		priority++;
		result.optimizations.push(new ShadowsOptimization(priority));
		result.optimizations.push(new LensFlaresOptimization(priority));

		// Next priority
		priority++;
		result.optimizations.push(new TextureOptimization(priority, 256));

		// Next priority
		priority++;
		// result.optimizations.push(new RenderTargetsOptimization(priority));

		// Next priority
		priority++;
		result.optimizations.push(new HardwareScalingOptimization(priority, 4));

		return result;
	}

	/**
	 * Confetti animation
	 * @param duration  animation duration
	 */
	public async launchConfetti(duration = 1000,position?: Vector3) {
		return new Promise<void>(resolve => {
			const particlesRightAnswer = new BabylonJs5Confetti(this, 1000, duration, ParticleAnimName.explosion,position);
			particlesRightAnswer.runParticles().then(() => {
				resolve();
			});
		});
	}

	initScene(){
		this.createCamera();
		this.createLight();
	}
	createCamera() {
		//outside angular zone camera declaration attach control by defaut an so declare event listener
		this.ngZone.runOutsideAngular(() => {
			this.camera = new ArcRotateCamera("Camera", -Math.PI / 2, Math.PI / 2, 10, Vector3.Zero(), this.scene);
			(this.camera as ArcRotateCamera).setTarget(Vector3.Zero());
			this.camera.attachControl(this.canvas, true);
		});
		this.cameraControl();
	}
	cameraControl() {
		(this.camera as ArcRotateCamera).inputs.attached.mousewheel.detachControl();
		//remove pinch zone interaction
		(this.camera.inputs.attached.pointers as ArcRotateCameraPointersInput).pinchZoom = false;
		(this.camera.inputs.attached.pointers as ArcRotateCameraPointersInput).multiTouchPanAndZoom = false;
		(this.camera.inputs.attached.pointers as ArcRotateCameraPointersInput).multiTouchPanning = false;
		//specify use button to only left button to remove right mouse action
		(this.camera.inputs.attached.pointers as ArcRotateCameraPointersInput).buttons = [0];
	}
	createLight() {
		this.light = new PointLight("bulb", new Vector3(0, 40, 0), this.scene);
		this.light.intensity = 9000;
		this.centerlight = new HemisphericLight("centerbulb", new Vector3(0, 0, 0), this.scene);
		this.centerlight.intensity = 1;
		(this.centerlight as HemisphericLight).groundColor = new Color3(1,1,1);
	}
}

class CustomDefaultLoadingScreen extends DefaultLoadingScreen{
	constructor(renderingCanvas: HTMLCanvasElement, loadingText?: string, loadingDivBackgroundColor?: string){
		DefaultLoadingScreen.DefaultLogoUrl = "./assets/kidaia/logo/logoKidaiaWhiteV2.png";
		super(renderingCanvas,loadingText,loadingDivBackgroundColor);
	}

}

results matching ""

    No results matching ""