File

src/app/models/babylon-integration-photo-dome.ts

Constructor

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

Methods

initScene
initScene()

Populate scene a

Returns: void
restartScene
restartScene()
Returns: void
debug
debug()
Returns: void
showDebug
showDebug()
Returns: void
createGyzmoManager
createGyzmoManager()
Returns: void
createHighLayer
createHighLayer()
Returns: void
createGlowLayer
createGlowLayer()
Returns: void
addRenderingPipeline
addRenderingPipeline()
Returns: void
highlightMesh
highlightMesh(mesh: AbstractMesh, light: boolean)
Returns: void
detectMeshInsideCenterCamera
detectMeshInsideCenterCamera()
Returns: void
pickedMesh
pickedMesh(mesh: AbstractMesh)
Returns: void
addPickEvent
addPickEvent()
Returns: void
getTopMesh
getTopMesh(mesh: Node)
Returns: void
createPoiGui
createPoiGui(photodomeItem: PhotoDomeItem)
Returns: void
createCamera
createCamera()
Returns: void
fovAdjust
fovAdjust(delta: number)
Returns: void
addMouseWheelPinchEvent
addMouseWheelPinchEvent()
Returns: void
removeMouseWheelEvent
removeMouseWheelEvent()
Returns: void
placeClone
placeClone(selectedMesh: TransformNode | Mesh)
Returns: InstancedMesh
cloneMeshWAnim
cloneMeshWAnim(mesh: Mesh)
Returns: void
cloneNodeTransformWAnim
cloneNodeTransformWAnim(node: TransformNode)
Returns: void
totalBoundingInfo
totalBoundingInfo(meshes: any)
Returns: void
createLight
createLight()
Returns: void
createPhotoDome
createPhotoDome()
Returns: any
loadPhotodome
loadPhotodome(photoDome: PhotoDomeModel)
Returns: any
setCameraOrientation
setCameraOrientation(photoDome: PhotoDomeModel)
Returns: void
countClickedItemPhotodome
countClickedItemPhotodome()
Returns: void
loadItemsScene
loadItemsScene(photoDomeItem: PhotoDomeItem[])
Returns: void
removeInstancedItemsFromScene
removeInstancedItemsFromScene()
Returns: void
activateAr
activateAr()
Returns: void
importAsset
importAsset()
Returns: any
importMesh
importMesh()
Returns: void
importContainer
importContainer()
Returns: void
importImage
importImage()
Returns: void
importTexture
importTexture()
Returns: void
importFinish
importFinish(tasks: AbstractAssetTask[])
Returns: void
addCustomBehavior
addCustomBehavior(mesh: AbstractMesh, type: string)
Returns: void

Properties

assetsContainer
assetsContainer: AssetContainer
assetsManager
assetsManager: AssetsManager
banList
banList: string[]
camera
camera: UniversalCamera | ArcRotateCamera
camera2
camera2: ArcRotateCamera
cd
cd: ChangeDetectorRef
compostList
compostList: string[]
compostMeshs
compostMeshs: AbstractMesh[]
dataService
dataService: CabriDataService
dome
dome: PhotoDome | VideoDome
domOverlayFeature
domOverlayFeature: WebXRDomOverlay
gizmoManager
gizmoManager: GizmoManager
gl
gl: GlowLayer
glassList
glassList: string[]
glassMeshs
glassMeshs: AbstractMesh[]
globalService
globalService: GlobalService
hitTest
hitTest: WebXRHitTest
hl
hl: HighlightLayer
hoverAnimObserver
hoverAnimObserver: Observer<Scene>
images
images: any
lastCalled
lastCalled: number
meshs
meshs: any
ngZone
ngZone: NgZone
page
page: PhotoDomePage
photoDome
photoDome: PhotoDomeModel
pipeline
pipeline: DefaultRenderingPipeline
placedMesh
placedMesh: AbstractMesh[]
placedMeshs
placedMeshs: InstancedMesh[]
poi
poi: number
poiFound
poiFound: number
poiMeshs
poiMeshs: (AbstractMesh | TransformNode)[]
poiPanel
poiPanel: AdvancedDynamicTexture
recycleList
recycleList: string[]
recycleMeshs
recycleMeshs: AbstractMesh[]
scenarioOse
scenarioOse: ScenarioOse
scrapList
scrapList: string[]
scrapMeshs
scrapMeshs: AbstractMesh[]
textures
textures: any
xr
xr: WebXRDefaultExperience
xrAnchor
xrAnchor: WebXRAnchorSystem
xrBackgroundRemover
xrBackgroundRemover: WebXRBackgroundRemover
xrPlaneDetection
xrPlaneDetection: WebXRPlaneDetector
import { NgZone, ChangeDetectorRef } from "@angular/core";
import {
	AbstractAssetTask,
	AbstractMesh,
	ArcRotateCamera,
	AssetContainer,
	AssetsManager,
	ImageAssetTask,
	MeshAssetTask,
	MultiMaterial,
	PhotoDome,
	PointLight,
	Texture,
	TextureAssetTask,
	Tools,
	UniversalCamera,
	Vector3,
	WebXRAnchorSystem,
	WebXRBackgroundRemover,
	WebXRDefaultExperience,
	WebXRDomOverlay,
	WebXRFeatureName,
	WebXRHitTest,
	WebXRPlaneDetector,
	HemisphericLight,
	WebXRSessionManager,
	ArcRotateCameraPointersInput,
	Mesh,
	InstancedMesh,
	GizmoManager,
	VideoDome,
	HighlightLayer,
	Color3,
	MeshBuilder,
	ContainerAssetTask,
	Node,
	TransformNode,
	AnimationGroup,
	BoundingInfo,
	GlowLayer,
	Matrix,
	Frustum,
	Observer,
	Scene,
	Color4,
	DefaultRenderingPipeline,
	PointerEventTypes,
	RayHelper,
	PointerInfo,
	PointerTouch,
	ArcRotateCameraMouseWheelInput
} from "@babylonjs/core";
import { CabriDataService } from "../services/cabri-data.service";
import { GlobalService } from "../services/global.service";
import { BabylonIntegration } from "./babylon-integration";
//not remove load for inspector
import("@babylonjs/core/Debug/debugLayer");
import("@babylonjs/inspector");
import "@babylonjs/loaders/glTF";
import { AppUtils } from "../app-utils";
import { IonSegment } from "@ionic/angular";
import { PhotoDomePage } from "../page/photo-dome/photo-dome.page";
import { PhotoDomeItem, PhotoDomeModel, PmMeshType, TriggerAnimation, TypePhotoDome } from "./photodome";
import {
	AdvancedDynamicTexture,
	Button,
	Control,
	Grid,
	Image,
	Rectangle,
	ScrollViewer,
	StackPanel,
	TextBlock,
	TextWrapping
} from "@babylonjs/gui";
import { ContentTagNames, TTSContent } from "./fiche";
import { ScenarioOse } from "./scenario-ose";
import { ScrollViewerCustom } from "../babylon/components/ScrollViewerCustom";
import { environment } from "src/environments/environment";

export class BabylonIntegrationPhotoDome extends BabylonIntegration {
	camera: UniversalCamera | ArcRotateCamera;
	dome: PhotoDome | VideoDome;
	assetsManager: AssetsManager;
	assetsContainer: AssetContainer;
	textures: Map<string, Texture>;
	images: Map<string, HTMLImageElement>;
	meshs: Map<string, AbstractMesh>;
	
	xr: WebXRDefaultExperience;
	compostList = ["banana", "egg_", "apple", "corn"];
	recycleList = ["paper", "box", "can_", "plastic", "holder"];
	glassList = ["bottle_of"];
	scrapList = ["metal","Pneu","Europalette"];
	banList = ["cracked", "plastic_coctail_cup", "paper_list"];
	compostMeshs: AbstractMesh[];
	recycleMeshs: AbstractMesh[];
	glassMeshs: AbstractMesh[];
	scrapMeshs: AbstractMesh[];
	poiMeshs: (AbstractMesh | TransformNode)[];
	xrAnchor: WebXRAnchorSystem;
	xrPlaneDetection: WebXRPlaneDetector;
	xrBackgroundRemover: WebXRBackgroundRemover;
	hitTest: WebXRHitTest;
	domOverlayFeature: WebXRDomOverlay;
	placedMesh: AbstractMesh[];
	camera2: ArcRotateCamera;
	photoDome: PhotoDomeModel;
	placedMeshs: InstancedMesh[];
	gizmoManager: GizmoManager;
	poiPanel: AdvancedDynamicTexture;
	lastCalled: number;
	hl: HighlightLayer;
	gl: GlowLayer;
	hoverAnimObserver: Observer<Scene>;
	pipeline: DefaultRenderingPipeline;
	poi: number;
	poiFound: number;
	scenarioOse: ScenarioOse;

	constructor(
		public globalService: GlobalService,
		public dataService: CabriDataService,
		public ngZone: NgZone,
		public page: PhotoDomePage,
		public cd?: ChangeDetectorRef
	) {
		super(globalService, dataService, ngZone, cd);
		this.placedMeshs = [];
	}
	/**
	 * Populate scene a
	 */
	async initScene() {
		this.createCamera();
		this.createLight();
		this.createGyzmoManager();
		this.createHighLayer();
		//this.createGlowLayer();
		this.addRenderingPipeline();
		await this.importAsset();
		this.assetsContainer.addAllToScene();
		await this.activateAr();
		if (this.page.currentPhotodome) {
			await this.loadPhotodome(this.page.currentPhotodome);
		} else {
			await this.createPhotoDome();
		}
		this.debug();
	}

	async restartScene() {
		this.placedMeshs.forEach(mesh => {
			mesh.isVisible = false;
		});
	}

	debug() {
		this.meshs.get("floor").setEnabled(false);
	}

	showDebug() {
		if (this.scene.debugLayer.isVisible()) {
			this.scene.debugLayer.hide();
		} else {
			this.scene.debugLayer.show();
		}
	}

	createGyzmoManager() {
		this.gizmoManager = new GizmoManager(this.scene);
	}

	createHighLayer() {
		this.hl = new HighlightLayer("hl1", this.scene, { camera: this.camera });
	}

	createGlowLayer() {
		this.gl = new GlowLayer("glow", this.scene);
		this.gl.intensity = 0.5;
	}

	addRenderingPipeline() {
		this.pipeline = new DefaultRenderingPipeline("defaultPipeline", true, this.scene, [this.scene.activeCamera]);
		if (!this.globalService.lowPerformanceMode) {
			this.pipeline.fxaaEnabled = true;
			this.pipeline.samples = 4;
		}
		this.pipeline.imageProcessingEnabled = false;
		this.pipeline.glowLayerEnabled = true;
		this.pipeline.glowLayer.intensity = 0.5;
	}

	highlightMesh(mesh: AbstractMesh, light: boolean) {
		if (light && !this.hl.hasMesh(mesh)) {
			this.hl.addMesh(mesh as Mesh, Color3.Green());
		} else if (!light && this.hl.hasMesh(mesh)) {
			this.hl.removeMesh(mesh as Mesh);
		}
	}

	detectMeshInsideCenterCamera() {
		let widthPercentage = 0.1;
		let heightPercentage = 0.1;
		const m = new Matrix();
		m.copyFrom(this.camera.getProjectionMatrix());
		let vectorX = m.getRow(0);
		vectorX.x /= widthPercentage;
		m.setRow(0, vectorX);
		let vectorY = m.getRow(1);
		vectorY.y /= heightPercentage;
		m.setRow(0, vectorY);
		const frustumPlanes = Frustum.GetPlanes(this.camera.getViewMatrix(true).multiply(m));

		this.page.currentPhotodome.photoDomeItems.forEach(photoDomeItem => {
			photoDomeItem.animations?.forEach(animation => {
				if (animation.trigger === TriggerAnimation.hover) {
					const node = this.scene.getTransformNodeByUniqueId(photoDomeItem.meshId);
					let condition = false;
					if (node instanceof Mesh) {
						condition = node.isInFrustum(frustumPlanes);
					} else if (node instanceof TransformNode) {
						condition = node.getChildMeshes().some(currentValue => {
							return currentValue.isInFrustum(frustumPlanes);
						});
					}
					if (condition) {
						const animGroup = node.metadata.animations.find(item => item.name === animation.name) as AnimationGroup;
						if (!animGroup.isPlaying) {
							animGroup.play();
						}
					}
				}
			});
		});
	}

	pickedMesh(mesh: AbstractMesh) {
		this.page.pickedMesh = mesh;
		this.gizmoManager.attachToMesh(mesh);
		this.gizmoManager.positionGizmoEnabled;
	}

	addPickEvent() {
		//
		// Gestion des clicks
		//
		this.scene.pointerDownFastCheck = true;
		this.scene.pointerDownPredicate = mesh => {
			return mesh instanceof InstancedMesh;
		};
		this.lastCalled = new Date().getTime();
		this.scene.onPointerDown = (evt, pickResult, type) => {
			const now = new Date().getTime();
			if (now - this.lastCalled > 500 /* half a second */) {
				this.lastCalled = now;
				if (this.page.builder) {
					if (this.page.canAddMesh && evt.button === 2) {
						let clone = this.placeClone(this.page.selectedMesh);
						this.page.currentPhotodome.photoDomeItems.push(this.page.createPhotoDomeItem(clone));
						this.page.canAddMesh = false;
						this.pickedMesh(clone);
						//tag photodome modified
						this.page.modified(this.page.currentPhotodome);
					} else if (pickResult.pickedMesh instanceof InstancedMesh) {
						if (this.placedMeshs.includes(pickResult.pickedMesh)) {
							this.pickedMesh(pickResult.pickedMesh);
						} else {
							this.pickedMesh(this.getTopMesh(pickResult.pickedMesh) as any);
						}
					}
				} else {
					//pointerX pointerY on Scene already handle offsetting					
					pickResult = this.scene.pickWithBoundingInfo(this.scene.pointerX,this.scene.pointerY, mesh => {
						return mesh instanceof InstancedMesh;
					});
					if (pickResult.pickedMesh) {
						this.page?.audioService?.playSelectSound();
						//var rayHelper = new RayHelper(pickResult.ray);
						//rayHelper.show(this.scene);
						let photodomeItem = this.page.currentPhotodome.photoDomeItems.find(
							item =>
								item.meshId === pickResult.pickedMesh.uniqueId ||
								this.getTopMesh(pickResult.pickedMesh).uniqueId === item.meshId
						);
						if (photodomeItem) {
							if (photodomeItem.type === PmMeshType.portal) {
								const nextPhotoDome = this.page.photodomes.find(item => item.id == photodomeItem.nextPhotoDome);
								if (nextPhotoDome) {
									this.page.currentPhotodome = nextPhotoDome;
									if (this.camera instanceof ArcRotateCamera) {
										this.camera.alpha = this.page.cameraAlphaDefault;
										this.camera.beta = this.page.cameraBetaDefault;
									}
									if (nextPhotoDome.type === TypePhotoDome.visit) {
										this.loadPhotodome(nextPhotoDome);
									} else {
										this.page.router.navigate(["/waste-sorting"], { queryParams: { wasteSortingId: nextPhotoDome.id } });
									}
									
								}
							}
							if (photodomeItem.type === PmMeshType.poi) {
								photodomeItem.clicked = true;
								this.countClickedItemPhotodome();
								if (photodomeItem.ficheId) {
									this.pickedMesh(this.getTopMesh(pickResult.pickedMesh) as Mesh);
								} else {
									this.createPoiGui(photodomeItem);
								}
							}
						}
					}
				}
			}
		};

		/*this.scene.onPointerMove = (evt, pickResult) => {
			if (!this.page.builder) {
				if (pickResult.pickedMesh && pickResult.pickedMesh instanceof InstancedMesh) {
					this.highlightMesh(pickResult.pickedMesh, true);
				} else {
					this.hl.removeAllMeshes();
				}
			}
		};*/
	}

	getTopMesh(mesh: Node) {
		while (mesh.parent !== null) {
			mesh = mesh.parent;
		}
		return mesh;
	}

	createPoiGui(photodomeItem: PhotoDomeItem) {
		if (!this.poiPanel) {
			this.camera.detachControl();
			this.removeMouseWheelEvent();
			this.poiPanel = AdvancedDynamicTexture.CreateFullscreenUI("Poi");

			this.poiPanel.layer.layerMask = 2;
			this.poiPanel.layer.applyPostProcess = false;
			const modalPanel = new Rectangle("modalPanel");
			modalPanel.width = "80%";
			modalPanel.height = "80%";
			modalPanel.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
			modalPanel.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
			modalPanel.background = "#FFFFFFFF";
			modalPanel.color = "#82AB00FF";
			modalPanel.thickness = 10;
			modalPanel.cornerRadius = 50;
			this.poiPanel.addControl(modalPanel);

			const scrollPanel = new ScrollViewerCustom("content");
			scrollPanel.thickness = 0;
			scrollPanel.width = "100%";
			scrollPanel.height = "90%";
			scrollPanel.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
			scrollPanel.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;

			scrollPanel.barColor = "#FCB731FF";
			scrollPanel.barSize = 10;
			scrollPanel.barBackground = "#F5F5F5FF";
			/*scrollPanel.onPointerEnterObservable.add(() => {
				this.camera.detachControl();
			});

			scrollPanel.onPointerOutObservable.add(() => {
				this.camera.attachControl(this.canvas, true);
				this.cameraControl();
			});*/
			modalPanel.addControl(scrollPanel);
			//this.poiPanel.focusedControl(buttonExit.);
			const buttonExit = Button.CreateImageOnlyButton("Exit", "assets/icon/close_ose.svg");
			let size = 80
			if(!this.globalService.isDesktop){
				size = this.globalService.isTablet ? Math.round(window.innerHeight / 10) : Math.round(window.innerHeight / 8)
			}
			buttonExit.heightInPixels = size;
			buttonExit.widthInPixels = size;
			
			buttonExit.verticalAlignment = Button.VERTICAL_ALIGNMENT_TOP;
			buttonExit.horizontalAlignment = Button.HORIZONTAL_ALIGNMENT_RIGHT;
			buttonExit.top = "6.5%";
			buttonExit.left = "-8%";
			buttonExit.thickness = 0;
			this.poiPanel.addControl(buttonExit);
			//modalPanel.addControl(buttonExit);
			const titleTextBlock = new TextBlock("title", photodomeItem.title);
			titleTextBlock.height = "10%";
			titleTextBlock.width = "100%";
			titleTextBlock.verticalAlignment = Button.VERTICAL_ALIGNMENT_TOP;
			titleTextBlock.horizontalAlignment = Button.HORIZONTAL_ALIGNMENT_LEFT;
			titleTextBlock.fontFamily = "Quicksand-Bold";
			titleTextBlock.fontSize = "30px";
			titleTextBlock.fontWeight = "bold";
			titleTextBlock.top = '0px'
			titleTextBlock.color = "rgb(54, 73, 17)";
			scrollPanel.addControl(titleTextBlock);
			if(innerHeight <= 800){
				titleTextBlock.fontSize = "20px";
			}else if(innerHeight <= 600){
				titleTextBlock.fontSize = "16px";
			}

			const imgPoi = new Image("imagePoi", photodomeItem.image);
			imgPoi.top = "15%";
			imgPoi.height = "60%";
			imgPoi.stretch = Image.STRETCH_UNIFORM;
			imgPoi.verticalAlignment = Image.VERTICAL_ALIGNMENT_TOP;
			imgPoi.horizontalAlignment = Image.HORIZONTAL_ALIGNMENT_LEFT;
			scrollPanel.addControl(imgPoi);
			const contentTextBlock = new TextBlock("content", photodomeItem.text);
			contentTextBlock.top = "75%";
			contentTextBlock.width = "100%";
			contentTextBlock.paddingTop = "5%";
			contentTextBlock.paddingLeft = "5%";
			contentTextBlock.paddingRight = "5%";
			contentTextBlock.paddingBottom = "5%";
			contentTextBlock.resizeToFit = true;
			contentTextBlock.textWrapping = TextWrapping.WordWrap;
			contentTextBlock.verticalAlignment = TextBlock.VERTICAL_ALIGNMENT_TOP;
			contentTextBlock.horizontalAlignment = TextBlock.HORIZONTAL_ALIGNMENT_LEFT;
			contentTextBlock.fontFamily = "Quicksand-Bold";
			contentTextBlock.fontSize = "24px";
			if(innerHeight <= 800){
				contentTextBlock.fontSize = "18px";
			}else if(innerHeight <= 600){
				contentTextBlock.fontSize = "16px";
			}

			contentTextBlock.fontWeight = "bold";
			scrollPanel.addControl(contentTextBlock);
			if (this.page) {
				const ttsContent = new Array<TTSContent>();
				ttsContent.push({ tag: ContentTagNames.H1, text: photodomeItem.title });
				ttsContent.push({ tag: ContentTagNames.P, text: photodomeItem.text });
				this.scenarioOse = new ScenarioOse(
					this.page.accountService,
					this.globalService,
					null,
					this.page,
					this.cd,
					this.page.ttsService
				);
				this.scenarioOse.readSpeechSequenceFromFicheContent(ttsContent);
			}
			this.lastCalled = new Date().getTime();
			buttonExit.onPointerClickObservable.add((x, state) => {
				const now = new Date().getTime();
				if (now - this.lastCalled < 500 /* half a second */) {
					// skip next calls
					state.skipNextObservers = true;
				} else {
					this.lastCalled = now;
					this.camera.attachControl(this.canvas, true);
					this.cameraControl();
					this.addMouseWheelPinchEvent();
					if (this.scenarioOse) {
						this.page.ttsService.killSpeech();
						this.scenarioOse.skipMathiaSpeechSequence(true);
					}
					this.poiPanel.dispose();
					this.poiPanel = null;
				}
			});
		}
	}

	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, 5, Vector3.Zero(), this.scene);
			this.camera.setTarget(Vector3.Zero());
			this.camera.attachControl(this.canvas, true);

			this.camera2 = new ArcRotateCamera("camera-screenshot", -Math.PI / 2, Math.PI / 2, 1.55, Vector3.Zero(), this.scene);
			this.camera2.layerMask = this.camera.layerMask;
			this.camera2.minZ = 0;
			this.camera2.maxZ = 50;
		});
		this.cameraControl();
	}

	

	fovAdjust(delta: number) {
		if (delta > 0) {
			if (this.camera.fov - 0.05 > 0.14) {
				this.camera.fov -= 0.05;
			}
		} else {
			if (this.camera.fov + 0.05 < 1) {
				this.camera.fov += 0.05;
			}
		}
	}

	addMouseWheelPinchEvent() {
		this.scene.onPointerObservable.add(pointerInfo => {
			// prevent all default (native chrome pinch zoom)
			pointerInfo.event.preventDefault();
			switch (pointerInfo.type) {
				case PointerEventTypes.POINTERWHEEL:
					this.fovAdjust((pointerInfo.event as any).wheelDelta);
					break;
			}
		});
		if (this.camera instanceof ArcRotateCamera) {
			// hack reduce delay for pinch zoom or pan choice useful when only one activated to remove the delay
			this.camera.pinchToPanMaxDistance = 0;
			const pointers = this.camera.inputs.attached.pointers as ArcRotateCameraPointersInput;
			pointers.onMultiTouch = (
				pointA: PointerTouch,
				pointB: PointerTouch,
				previousPinchSquaredDistance: number,
				pinchSquaredDistance: number,
				previousMultiTouchPanPosition: PointerTouch,
				multiTouchPanPosition: PointerTouch
			) => {
				this.fovAdjust(pinchSquaredDistance - previousPinchSquaredDistance);
			};
		}
	}

	removeMouseWheelEvent() {
		this.scene.onPointerObservable.clear();
		if (this.camera instanceof ArcRotateCamera) {
			const pointers = this.camera.inputs.attached.pointers as ArcRotateCameraPointersInput;
			pointers.onMultiTouch = null;
		}
	}

	placeClone(selectedMesh: Mesh | TransformNode): InstancedMesh {
		var pick = this.scene.pick(this.scene.pointerX, this.scene.pointerY, (mesh) => { return mesh.isVisible; }, false, null, (p0, p1, p2, ray) => {
			var p0p1 = p0.subtract(p1);
			var p2p1 = p2.subtract(p1);
			var normal = Vector3.Cross(p0p1, p2p1);
			return Vector3.Dot(ray.direction, normal) < 0;
		});
		let clone = null;
		if (selectedMesh instanceof Mesh) {
			clone as InstancedMesh;
			clone = this.cloneMeshWAnim(selectedMesh);
			const range = clone.getBoundingInfo().boundingSphere.radius * clone.scaling.x;
			const directionOffset = pick.ray.direction.multiplyByFloats(range, range, range);
			clone.position = pick.pickedPoint.subtract(directionOffset);
			clone.isVisible = true;
		} else {
			clone = this.cloneNodeTransformWAnim(selectedMesh);
			const range = this.totalBoundingInfo(clone.getChildMeshes()).boundingSphere.radius * clone.scaling.x;
			const directionOffset = pick.ray.direction.multiplyByFloats(range, range, range);
			clone.position = pick.pickedPoint.subtract(directionOffset);
			clone.setEnabled(true);
		}
		this.placedMeshs.push(clone);
		this.assetsContainer.meshes.push(clone);
		return clone;
	}

	cloneMeshWAnim(mesh: Mesh) {
		if (mesh instanceof Mesh && !mesh.instancedBuffers) {
			mesh.registerInstancedBuffer("color", 3);
			mesh.instancedBuffers.color = new Color3(1, 1, 1);
		}
		const clone = mesh.createInstance(mesh.name + 1);
		const animations: AnimationGroup[] = [];
		if (mesh.metadata?.animations?.length > 0) {
			const sourceAnims: AnimationGroup[] = mesh.metadata.animations;
			sourceAnims.forEach(animSource => {
				// when an animation is clone a callback can be use to convert the old target "Mesh" whith a new
				//AnimationGroup -> animationTarget[] -> target "node" animation "curve coordinate"
				animations.push(
					animSource.clone(animSource.name, oldTarget => {
						return clone;
					})
				);
			});
			clone.metadata = { animations };
		}
		return clone;
	}

	cloneNodeTransformWAnim(node: TransformNode) {
		node.getChildMeshes().forEach(mesh => {
			if (mesh instanceof Mesh && !mesh.instancedBuffers) {
				mesh.registerInstancedBuffer("color", 3);
				mesh.instancedBuffers.color = new Color3(1, 1, 1);
			}
		});
		const animations: AnimationGroup[] = [];
		// associative array to keep relation between source and instantiate item inside transformNode children
		const sourceInstanceItems: { source; instance }[] = [];
		const clonedNode = node.instantiateHierarchy(null, null, (source, instance) => {
			sourceInstanceItems.push({ source, instance });
		});
		/*if (node.name.includes('POI')){
			clonedNode.getChildMeshes().forEach(mesh => {
					var minimum = mesh.getBoundingInfo().boundingBox.minimum.clone();
					var maximum = mesh.getBoundingInfo().boundingBox.maximum.clone();
					var scaling = Matrix.Scaling(2, 2, 2);							
					minimum = Vector3.TransformCoordinates(minimum, scaling);
					maximum = Vector3.TransformCoordinates(maximum, scaling);
					mesh.setBoundingInfo(new BoundingInfo(minimum, maximum));
			});
		}*/
		if (node.metadata?.animations?.length > 0) {
			const sourceAnims: AnimationGroup[] = node.metadata.animations;
			sourceAnims.forEach(animSource => {
				// when an animation is clone a callback can be use to convert the old target "Mesh" whith a new
				//AnimationGroup -> animationTarget[] -> target "node" animation "curve coordinate"
				animations.push(
					animSource.clone(animSource.name, oldTarget => {
						return sourceInstanceItems.find(item => {
							return item.source === oldTarget;
						}).instance;
					})
				);
			});
			clonedNode.metadata = { animations };
		}
		return clonedNode;
	}

	totalBoundingInfo(meshes) {
		var boundingInfo = meshes[0].getBoundingInfo();
		var min = boundingInfo.minimum.add(meshes[0].position);
		var max = boundingInfo.maximum.add(meshes[0].position);
		for (var i = 1; i < meshes.length; i++) {
			boundingInfo = meshes[i].getBoundingInfo();
			min = Vector3.Minimize(min, boundingInfo.minimum.add(meshes[i].position));
			max = Vector3.Maximize(max, boundingInfo.maximum.add(meshes[i].position));
		}
		return new BoundingInfo(min, max);
	}

	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 = 0.3;
	}

	createPhotoDome(): Promise<void> {
		return new Promise((resolve, reject) => {
			this.dome = new PhotoDome(
				"testdome",
				/*"assets/babylon/wasteSorting/greenhouse.jpg",*/
				"assets/babylon/wasteSorting/nature.jpg",
				{
					resolution: 32,
					size: 40
				},
				this.scene
			);
			this.dome.imageMode = PhotoDome.MODE_MONOSCOPIC;
			this.dome.onLoadObservable.addOnce(() => {
				resolve();
			});
		});
	}

	loadPhotodome(photoDome: PhotoDomeModel): Promise<void> {
		return new Promise((resolve, reject) => {
			if (!this.globalService.globalLoading) {
				this.engine.displayLoadingUI();
			}

			if (this.dome) {
				this.dome.dispose();
			}
			this.removeInstancedItemsFromScene();
			this.photoDome = photoDome;
			const videos = ["mp4", "3gp", "ogg", "webm"];

			const url = new URL(photoDome.srcImg + "?env=" + (environment.production?"prod":"dev"));
			const extension = url.pathname.split(".")[1];

			if (videos.includes(extension)) {
				this.dome = new VideoDome(
					photoDome.name,
					photoDome.srcImg,
					{
						resolution: 32,
						size: 40,
						faceForward: false,
					},
					this.scene
				);
				this.dome.videoMode = PhotoDome.MODE_MONOSCOPIC;
			} else {
				this.dome = new PhotoDome(
					photoDome.name,
					photoDome.srcImg,
					{
						resolution: 32,
						size: 40,
						faceForward: false,
					},
					this.scene
				);
				this.dome.imageMode = PhotoDome.MODE_MONOSCOPIC;
			}
			this.dome.getChildMeshes()[0].isPickable = false;
			this.loadItemsScene(photoDome.photoDomeItems);
			this.dome.onLoadObservable.addOnce(() => {
				this.setCameraOrientation(photoDome);
				this.engine.hideLoadingUI();
				resolve();
			});
		});
	}

	setCameraOrientation(photoDome: PhotoDomeModel) {
		if (photoDome.cameraAlpha && photoDome.cameraBeta) {
			if (this.camera instanceof ArcRotateCamera) {
				this.camera.alpha = photoDome.cameraAlpha;
				this.camera.beta = photoDome.cameraBeta;
			}
		}
	}

	countClickedItemPhotodome() {
		this.poiFound = this.photoDome.photoDomeItems.reduce((accumulator, item) => {
			if (item.type === PmMeshType.poi && item.clicked) {
				accumulator++;
			}
			return accumulator;
		}, 0);
	}

	loadItemsScene(photoDomeItem: PhotoDomeItem[]) {
		let haveHoverAnim = false;
		this.poi = photoDomeItem.filter(item => item.type === PmMeshType.poi).length;
		this.poiFound = 0;
		photoDomeItem.forEach(item => {
			if (item.clicked) {
				this.poiFound++;
			}
			const node = this.scene.getNodeByName(item.originalMeshName);
			if (item.type === PmMeshType.poi && item.ficheId){
				this.dataService.getFiche(item.ficheId);
			}
			if (node) {
				let clone = null;
				if (node instanceof Mesh) {
					clone = this.cloneMeshWAnim(node);
					if (item.color) {
						clone.instancedBuffers.color = Color3.FromHexString(item.color);
					}
					clone.isVisible = true;
					//Todo clone animation for mesh
				} else if (node instanceof TransformNode) {
					clone = this.cloneNodeTransformWAnim(node);
					if (item.color) {
						clone.getChildMeshes().forEach(mesh => {
							mesh.instancedBuffers.color = Color3.FromHexString(item.color);
						});
					}
				}
				if (item.animations?.length > 0) {
					item.animations.forEach(element => {
						if (element.trigger === TriggerAnimation.loop) {
							const animGroup = clone.metadata.animations.find(item => item.name === element.name) as AnimationGroup;
							animGroup.play(true);
						} else {
							haveHoverAnim = true;
						}
					});
				}
				clone.position.x = item.position.x;
				clone.position.y = item.position.y;
				clone.position.z = item.position.z;
				clone.rotation.x = item.rotation.x;
				clone.rotation.y = item.rotation.y;
				clone.rotation.z = item.rotation.z;
				clone.scaling.x = item.scale.x;
				clone.scaling.y = item.scale.y;
				clone.scaling.z = item.scale.z;
				clone.isVisible = true;
				item.meshId = clone.uniqueId;
				this.placedMeshs.push(clone);
				this.assetsContainer.meshes.push(clone);
			}
		});
		if (haveHoverAnim && !this.hoverAnimObserver) {
			this.hoverAnimObserver = this.scene.onBeforeRenderObservable.add(() => {
				this.detectMeshInsideCenterCamera();
			});
		} else if (!haveHoverAnim && this.hoverAnimObserver) {
			this.scene.onBeforeRenderObservable.remove(this.hoverAnimObserver);
		}
	}

	removeInstancedItemsFromScene() {
		this.placedMeshs.forEach(mesh => {
			mesh.dispose();
		});
		this.placedMeshs = [];
	}

	async activateAr() {
		await this.ngZone.runOutsideAngular(async () => {
			try {
				const supportedAr = await WebXRSessionManager.IsSessionSupportedAsync("immersive-ar");
				const supportedVr = await WebXRSessionManager.IsSessionSupportedAsync("immersive-vr");
				if (supportedAr) {
					this.xr = await this.scene.createDefaultXRExperienceAsync({
						uiOptions: {
							sessionMode: "immersive-ar"
						},
						optionalFeatures: true
					});
				} else if (supportedVr) {
					this.xr = await this.scene.createDefaultXRExperienceAsync({
						uiOptions: {
							sessionMode: "immersive-vr"
						},
						optionalFeatures: true
					});
				}
				//this.xr?.pointerSelection.attach();
			} catch (error) {
				console.error(error);
			}
			/*try {
				//this.xrAnchor = this.xr?.baseExperience.featuresManager.enableFeature(WebXRAnchorSystem, 'latest') as WebXRAnchorSystem;
			} catch (error) {
				console.error(error);
			}*/
			/*try {
				this.xrPlaneDetection = this.xr?.baseExperience.featuresManager.enableFeature(WebXRPlaneDetector, 1) as WebXRPlaneDetector;
			} catch (error) {
				console.error(error);
			}
			try {
				this.xrBackgroundRemover = this.xr?.baseExperience.featuresManager.enableFeature(WebXRBackgroundRemover, "latest", {
					environmentHelperRemovalFlags: {
						skyBox: true,
						ground: true
					}
				}) as WebXRBackgroundRemover;
			} catch (error) {
				// alert(error);
				console.error(error);
			}*/
			try {
				this.domOverlayFeature = this.xr?.baseExperience.featuresManager.enableFeature(WebXRFeatureName.DOM_OVERLAY, 1, {
					element: ".overlayAr" // domOverlayRef.current
				}) as WebXRDomOverlay;
			} catch (error) {}
		});
	}

	/********************************************* IMPORT ************************************************/
	importAsset(): Promise<void> {
		this.assetsManager = new AssetsManager(this.scene);
		this.assetsContainer = new AssetContainer(this.scene);
		this.importContainer();
		this.importTexture();
		this.importImage();
		this.importMesh();
		this.assetsManager.onFinish = this.importFinish.bind(this);
		if (this.page.currentPhotodome) {
			this.assetsManager.autoHideLoadingUI = false;
		}
		//this.assetsManager.useDefaultLoadingScreen = false;
		return this.assetsManager.loadAsync();
	}

	importMesh() {
		const mMeshs = [
			{ name: "waste", url: "/assets/babylon/wasteSorting/scenes/", filename: "Demo0.gltf" },
			{ name: "waste", url: "/assets/babylon/wasteSorting/scenes/", filename: "Pneu.gltf" },
			{ name: "pallet", url: "/assets/babylon/wasteSorting/scenes/", filename: "Europalette.babylon" },
			
			//{ name: "poi", url: "/assets/babylon/visit/scenes/", filename: "PoiPortal.babylon" }
		];

		mMeshs.forEach(mesh => {
			this.assetsManager.addMeshTask(mesh.name, null, mesh.url, mesh.filename);
		});
	}

	importContainer() {
		const mMeshs = [
			{ name: "poi", url: "/assets/babylon/poi/", filename: "poi.babylon" }
			//{ name: "poi", url: "/assets/babylon/animals/", filename: "animals.babylon" }
		];

		mMeshs.forEach(mesh => {
			this.assetsManager.addContainerTask(mesh.name, null, mesh.url, mesh.filename);
		});
	}

	importImage() {
		const images = [
			/*{
				name: "dome",
				url: "assets/babylon/wasteSorting/PANO_20230111_095151_1.jpg"
			}*/
		];

		images.forEach(image => {
			this.assetsManager.addImageTask(image.name, image.url);
		});
	}

	importTexture() {
		const textureMap = [
			/*
			{ name: "parking", url: "assets/babylon/justePoint/textures/parking.png" },
        */
		];

		textureMap.forEach(texture => {
			this.assetsManager.addTextureTask(texture.name, texture.url);
		});
	}

	importFinish(tasks: AbstractAssetTask[]) {
		this.textures = new Map<string, Texture>();
		this.images = new Map<string, HTMLImageElement>();
		this.meshs = new Map<string, AbstractMesh>();
		this.recycleMeshs = [];
		this.compostMeshs = [];
		this.scrapMeshs = [];
		this.glassMeshs = [];
		this.poiMeshs = [];
		tasks.forEach(task => {
			if (task.isCompleted) {
				//containerAssetTask import transformNode mesh animation and material/texture
				if (task instanceof ContainerAssetTask) {
					let nodeMeshs: AbstractMesh[] = [];
					console.log(task.loadedMeshes);
					this.assetsContainer.animationGroups = this.assetsContainer.animationGroups.concat(task.loadedAnimationGroups);
					this.assetsContainer.transformNodes = this.assetsContainer.transformNodes.concat(task.loadedTransformNodes);

					//search anim where target use a child mesh for each node
					task.loadedTransformNodes.forEach(node => {
						node.setEnabled(false);
						node.rotation = new Vector3(0, 0, 0);
						let animBind: AnimationGroup[] = [];
						task.loadedAnimationGroups.forEach(animGroup => {
							for (let index = 0; index < animGroup.targetedAnimations.length; index++) {
								const target = animGroup.targetedAnimations[index].target;
								if (
									node.getChildren(nodeChild => {
										return nodeChild.id === target.id;
									}).length > 0
								) {
									animBind.push(animGroup);
								}
							}
						});
						if (animBind.length > 0) {
							node.metadata = { animations: animBind };
						}
						//list meshs from transformNode to detect orphan mesh when we handle meshs
						nodeMeshs = nodeMeshs.concat(node.getChildMeshes());
						if (task.name === "poi") {
							this.poiMeshs.push(node);
						}
					});
					this.assetsContainer.meshes = this.assetsContainer.meshes.concat(task.loadedMeshes);

					task.loadedMeshes.forEach(mesh => {
						//orphan mesh
						if (!nodeMeshs.includes(mesh)) {
							mesh.rotation = new Vector3(0, 0, 0);
							let animBind: AnimationGroup[] = [];
							//search anim for this mesh
							task.loadedAnimationGroups.forEach(animGroup => {
								for (let index = 0; index < animGroup.targetedAnimations.length; index++) {
									const target = animGroup.targetedAnimations[index].target;
									if (target === mesh) {
										animBind.push(animGroup);
									}
								}
								if (animBind.length > 0) {
									mesh.metadata = { animations: animBind };
								}
							});
							mesh.isVisible = false;
							if (task.name === "poi") {
								this.poiMeshs.push(mesh);
							}
						}
						if (mesh.material) {
							if (mesh.material instanceof MultiMaterial) {
								this.assetsContainer.multiMaterials.push(mesh.material as MultiMaterial);
							} else {
								this.assetsContainer.materials.push(mesh.material);
								this.assetsContainer.textures = this.assetsContainer.textures.concat(mesh.material.getActiveTextures());
							}
						}
					});
				}
				if (task instanceof MeshAssetTask) {
					const lTask = task as MeshAssetTask;
					this.assetsContainer.meshes = this.assetsContainer.meshes.concat(lTask.loadedMeshes);
					lTask.loadedMeshes.forEach(mesh => {
						const lTask = task as MeshAssetTask;
						if (lTask.name === "poi") {
							console.log(lTask.loadedMeshes);
							this.poiMeshs.push(mesh);
						}
						else if (lTask.name === "pallet") {
							mesh.scaling = new Vector3(0.1, 0.1, 0.1);
						} 
						else {
							mesh.scaling = new Vector3(10, 10, 10);
						}

						mesh.rotation = new Vector3(0, 0, 0);

						this.meshs.set(mesh.name, mesh);
						mesh.isVisible = false;
						if (
							!this.banList.find(elem => {
								return mesh.name.includes(elem);
							})
						) {
							if (
								this.recycleList.find(elem => {
									return mesh.name.includes(elem);
								})
							) {
								this.addCustomBehavior(mesh, "recycle");
								this.recycleMeshs.push(mesh);
							}
							if (
								this.glassList.find(elem => {
									return mesh.name.includes(elem);
								})
							) {
								this.addCustomBehavior(mesh, "glass");
								this.glassMeshs.push(mesh);
							}
							if (
								this.compostList.find(elem => {
									return mesh.name.includes(elem);
								})
							) {
								this.addCustomBehavior(mesh, "compost");
								this.compostMeshs.push(mesh);
							}
							if (
								this.scrapList.find(elem => {
									return mesh.name.includes(elem);
								})
							) {
								this.addCustomBehavior(mesh, "scrap");
								this.scrapMeshs.push(mesh);
							}
						}
						if (mesh.material) {
							if (mesh.material instanceof MultiMaterial) {
								this.assetsContainer.multiMaterials.push(mesh.material as MultiMaterial);
							} else {
								this.assetsContainer.materials.push(mesh.material);
								this.assetsContainer.textures = this.assetsContainer.textures.concat(mesh.material.getActiveTextures());
							}
						}
					});
				} else if (task instanceof ImageAssetTask) {
					const lTask = task as ImageAssetTask;
					this.images.set(task.name, lTask.image);
				} else if (task instanceof TextureAssetTask) {
					const lTask = task as TextureAssetTask;
					this.assetsContainer.textures.push(lTask.texture);
					this.textures.set(lTask.name, lTask.texture);
				} else {
					console.error("Task Not Handle", task);
				}
			} else {
				console.error(task.errorObject);
			}
		});
	}

	addCustomBehavior(mesh: AbstractMesh, type: string) {
		const customBehavior = { name: "custom", type, init: () => {}, attach: () => {}, detach: () => {} };
		mesh.addBehavior(customBehavior);
		// console.error(mesh.name+" : "+(mesh.getBehaviorByName("custom") as any).displayName);
	}

	/******************************************END IMPORT ************************************************/
}

results matching ""

    No results matching ""