// Copyright 2024 The Google Research Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Utilities for fetching assets. */ const baseGoogleApiUrl = 'https://firebasestorage.googleapis.com/v0/b/test3-2d896.appspot.com/o'; // Gestion centralisée du token let gcsToken = null; function fetchGcsToken() { if (gcsToken) { return gcsToken; } const BASE_URL = window.location.origin; gcsToken = fetch('/get-gcs-token') .then(response => { if (!response.ok) { throw new Error(`Erreur lors de la récupération du jeton GCS: ${response.status}`); } return response.json(); }) .then(data => { gcsToken = data.token; // Réinitialiser la promesse après la résolution pour récupérer un nouveau token plus tard si nécessaire setTimeout(() => { gcsToken = null; }, 14 * 60 * 1000); // Expiration du token après 14 minutes return gcsToken; }) .catch(error => { console.error('Erreur lors de la récupération du jeton GCS:', error); gcsToken = null; // En cas d'erreur, réinitialiser pour permettre une nouvelle tentative throw error; }); return gcsTokenPromise; } /** * Fetch a set of occupancy grids. * @param {*} spec * @param {*} router * @returns */ function fetchScene(spec, router) { return { ...spec, occupancyGridsAsset: fetchAsset(spec.occupancyGridsSpec, router), distanceGridsAsset: fetchAsset(spec.distanceGridsSpec, router), triplaneAsset: fetchAsset(spec.triplaneSpec, router), sparseGridAsset: fetchAsset(spec.sparseGridSpec, router), }; } /** * Fetch a set of occupancy grids. * @param {*} spec * @param {*} router * @returns */ function fetchOccupancyGrids(spec, router) { let gridAssets = spec.gridSpecs.map((gridSpec) => fetchAsset(gridSpec, router)); return { ...spec, gridAssets: gridAssets }; } /** * Fetch assets for a sliced grid. * @param {*} spec * @param {*} router * @returns */ function fetchSlices(spec, router) { let sliceAssets = spec.sliceSpecs.map((sliceSpec) => fetchAsset(sliceSpec, router)); return { ...spec, sliceAssets: sliceAssets }; } /** * Fetch triplane representation. * @param {*} spec * @param {*} router * @returns */ function fetchTriplane(spec, router) { let result = { ...spec, featuresAsset: fetchAsset(spec.featuresSpec, router) }; if (spec.separateRgbAndDensity) { result.rgbAsset = fetchAsset(spec.rgbSpec, router); result.densityAsset = fetchAsset(spec.densitySpec, router); } else { result.rgbAndDensityAsset = fetchAsset(spec.rgbAndDensitySpec, router); } return result; } /** * Fetch a flat, monolithic array from GCS. * @param {*} spec - The specification for the asset. * @param {Router} router - Router object that contains the directory URL information. * @returns {object} The spec object with the fetched asset. */ function fetchArray(spec, router) { // Extract dirUrl from the Router object const dirUrl = router.dirUrl; // Check that dirUrl is a valid string if (typeof dirUrl !== 'string') { console.error('dirUrl is not a valid string:', dirUrl); throw new Error('dirUrl must be a valid string'); } // Perform validation on the spec if (!spec.filename) { console.error('Spec missing filename:', spec); throw new Error('Spec must contain a valid filename'); } // Clean the path to remove relative segments (`../`) const cleanedDirUrl = cleanPath(dirUrl); // Ensure the filename is properly formatted const cleanedFilename = spec.filename.endsWith('.gz') ? spec.filename.slice(0, -3) : spec.filename; // Construct the full path by encoding both parts const encodedDirUrl = encodeURIComponent(cleanedDirUrl); const encodedFilename = cleanedFilename; // Build the full URL for accessing Google Cloud Storage const fullUrl = `${baseGoogleApiUrl}/users%2Fvisite_3D%2F${encodedDirUrl}%2F${encodedFilename}?alt=media&token=${gcsToken}`; console.log('Fetching from URL:', fullUrl); // Make the request to load the asset const asset = loadAsset(fullUrl).then(validateSize(spec)).then(onImageLoaded); return { ...spec, asset: asset }; } /** * Validate the size of the fetched asset. * @param {*} spec * @returns {function} */ function validateSize(spec) { return (assetBuffer) => { const expectedSize = product(spec.shape) * spec.numChannels; console.assert(assetBuffer.length === expectedSize, `Size mismatch for ${spec.filename}`, spec, assetBuffer); return assetBuffer; }; } /** * Cleans the given path by removing any `../` or `./` segments. * @param {string} path - The path to be cleaned. * @return {string} The cleaned path. */ function cleanPath(path) { const parts = path.split('/'); const stack = []; for (let i = 0; i < parts.length; i++) { if (parts[i] === '..') { if (stack.length) { stack.pop(); } } else if (parts[i] !== '.' && parts[i] !== '') { stack.push(parts[i]); } } return stack.join('/'); } /** * Fetches sparse grid assets from GCS. * @param {*} spec * @param {*} router * @returns */ function fetchSparseGrid(spec, router) { let result = { ...spec, blockIndicesAsset: fetchAsset(spec.blockIndicesSpec, router), featuresAsset: fetchAsset(spec.featuresSpec, router), }; if (spec.separateRgbAndDensity) { result.rgbAsset = fetchAsset(spec.rgbSpec, router); result.densityAsset = fetchAsset(spec.densitySpec, router); } else { result.rgbAndDensityAsset = fetchAsset(spec.rgbAndDensitySpec, router); } return result; } /** * Report that no fetch function is available. */ function notImplementedError(spec, router) { console.error(`${spec.assetType} is not yet implemented`, spec); } const gFetchRegistry = { 'scene': fetchScene, // triplane 'triplane': fetchTriplane, 'triplane_rgb_and_density_slices': fetchSlices, 'triplane_rgb_and_density_slice': fetchArray, 'triplane_rgb_slices': fetchSlices, 'triplane_rgb_slice': fetchArray, 'triplane_density_slices': fetchSlices, 'triplane_density_slice': fetchArray, 'triplane_features_slices': fetchSlices, 'triplane_features_slice': fetchArray, // distance grids 'distance_grids': fetchOccupancyGrids, 'distance_grid_slices': fetchSlices, 'distance_grid_slice': fetchArray, // occupancy grids 'occupancy_grids': fetchOccupancyGrids, 'occupancy_grid_slices': fetchSlices, 'occupancy_grid_slice': fetchArray, // sparse grid 'sparse_grid': fetchSparseGrid, 'sparse_grid_block_indices': fetchArray, 'sparse_grid_rgb_and_density_slices': fetchSlices, 'sparse_grid_rgb_and_density_slice': fetchArray, 'sparse_grid_rgb_slices': fetchSlices, 'sparse_grid_rgb_slice': fetchArray, 'sparse_grid_density_slices': fetchSlices, 'sparse_grid_density_slice': fetchArray, 'sparse_grid_features_slices': fetchSlices, 'sparse_grid_features_slice': fetchArray, }; function fetchAsset(spec, router) { let fetchFn = gFetchRegistry[spec.assetType]; if (fetchFn == undefined) { console.error(`Failed to find fetchFn for assetType ${spec.assetType}`, spec); } return fetchFn(spec, router); }