UNPKG

@mcmhomes/panorama-viewer

Version:
148 lines (119 loc) 3.96 kB
/*eslint-disable react-compiler/react-compiler*/ import React from 'react'; import {useThree} from '@react-three/fiber'; import {PanoramaRendererLayers} from './PanoramaRendererLayers.jsx'; import {loadMultiresTexture} from '../utils/PanoramaRendererUtils.jsx'; import {PanoramaRenderingLayerMinimumLoadTime} from './PanoramaRenderingLayer.jsx'; import {each, mapToArray, purgeErrorMessage, setAnimationFrameIntervalRemovable, STRING, uniqueId} from '../utils/PanoramaUtils.jsx'; import {memo, useEffect, useRef, useState} from '../utils/PanoramaUtilsReact.jsx'; export const PanoramaRendererTexturePreloader = memo(({src, homeId, host, homeUrl, basisTranscoderPath, setError, setLoading}) => { const {gl} = useThree(); const currentLayersRef = useRef([]); const [currentLayers, setCurrentLayers] = useState([]); const readyLayersRef = useRef([]); const [readyLayers, setReadyLayers] = useState([]); useEffect(() => { if(!gl) { return; } /** create a lookup table **/ let newSrc = {}; each(src, (item, index) => { newSrc[item.basePath] = item; }); /** dispose layers that are not in the new src **/ each(currentLayersRef.current, (layer, index) => { if(newSrc[layer.src.basePath]) { /** exists, don't dispose **/ delete newSrc[layer.src.basePath]; return; } /** removed, dispose **/ layer.removed = true; currentLayersRef.current = currentLayersRef.current.filter(item => (item.key !== layer.key)); /** if not in readyLayers, dispose textures now **/ if(!readyLayersRef.current.find(item => (item.key === layer.key))) { layer.src.dispose(); } }); /** add new layers **/ each(newSrc, (item, basePath) => { let layer = {key:uniqueId()}; const onLoadingError = ({level, error}) => { if(!layer.removed && (level <= 0)) { setError({canRetry:true, id:'could-not-load-home', message:'Couldn\'t load the home: ' + homeId, reason:STRING(purgeErrorMessage(error)), data:{homeId, host, homeUrl}}); } }; const loader = loadMultiresTexture({gl, basePath:item.basePath, maskBasePath:item.maskBasePath, basisTranscoderPath, minimumLoadTime:(readyLayersRef.current.length <= 0) ? 0 : PanoramaRenderingLayerMinimumLoadTime, onLoadingLevelFail:onLoadingError}); if(loader) { layer.src = {...item, ...loader}; currentLayersRef.current.push(layer); } }); setCurrentLayers([...currentLayersRef.current]); // changes made to the other props (that are used in this function) are not reflected here, this useEffect simply checks for added and removed layers // if the other props are changed (homeId, host, homeUrl, basisTranscoderPath), it won't affect the code here, so for performance reasons, it's safe to ignore //eslint-disable-next-line react-hooks/exhaustive-deps }, [gl, src /*, homeId, host, homeUrl, basisTranscoderPath, setError*/]); useEffect(() => { const timer = setAnimationFrameIntervalRemovable(() => { let ready = true; each(currentLayers, (layer, index) => { if(!layer.src.isReady(0)) { ready = false; return false; } }); if(ready) { readyLayersRef.current = [...currentLayers]; setReadyLayers(readyLayersRef.current); timer.remove(); } }); return timer.remove; }, [currentLayers]); const readyLayersIsEmpty = (readyLayers.length <= 0); useEffect(() => { setLoading(readyLayersIsEmpty); }, [readyLayersIsEmpty, setLoading]); useEffect(() => { return () => { /** cleanup **/ each(currentLayersRef.current, (layer, index) => { layer.removed = true; layer.src.dispose(); }); currentLayersRef.current = []; readyLayersRef.current = []; setCurrentLayers([]); setReadyLayers([]); }; }, []); if(readyLayers.length <= 0) { return; } return (<> <PanoramaRendererLayers src={mapToArray(readyLayers, layer => layer.src)}/> </>); });