@deck.gl-community/layers
Version:
Add-on layers for deck.gl
106 lines (89 loc) • 3.34 kB
text/typescript
// deck.gl-community
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import type {TextureCubeManifest, TextureCubeLoaderOptions} from '@loaders.gl/textures';
import type {TextureCubeData, TextureCubeFace} from '@luma.gl/engine';
const CUBE_FACES: TextureCubeFace[] = ['+X', '-X', '+Y', '-Y', '+Z', '-Z'];
type LoadedTextureLevel = {
/** Browser-native bitmap representation for a mip level. */
imageBitmap?: ImageBitmap;
/** Explicit WebGPU texture format name. */
textureFormat?: string;
/** Legacy format field returned by some loaders.gl code paths. */
format?: string;
/** Raw pixel data for CPU-side texture uploads. */
data?: Uint8Array;
/** Width in pixels for raw pixel data uploads. */
width?: number;
/** Height in pixels for raw pixel data uploads. */
height?: number;
};
type LoadedCubemapTexture = {
type: 'cube';
data: unknown[];
};
/**
* Normalizes loaders.gl cubemap load options so in-memory manifests can still
* resolve relative face URLs through `core.baseUrl`.
*/
export function createCubemapLoadOptions(
cubemap: string | TextureCubeManifest,
loadOptions?: TextureCubeLoaderOptions | null
): TextureCubeLoaderOptions | undefined {
if (!loadOptions) {
return undefined;
}
if (typeof cubemap === 'string' || loadOptions.core?.baseUrl || !loadOptions.baseUrl) {
return loadOptions;
}
return {
...loadOptions,
core: {
...loadOptions.core,
baseUrl: loadOptions.baseUrl
}
};
}
/** Converts a loaders.gl cubemap result into luma.gl `TextureCubeData`. */
export function convertLoadedCubemapToTextureData(texture: LoadedCubemapTexture): TextureCubeData {
if (texture.type !== 'cube' || !Array.isArray(texture.data) || texture.data.length !== 6) {
throw new Error('SkyboxLayer expected a cubemap texture with six faces.');
}
return Object.fromEntries(
CUBE_FACES.map((face, index) => [face, normalizeTextureSlice(texture.data[index], face)])
) as TextureCubeData;
}
/** Normalizes a cubemap face that may contain one or more mip levels. */
function normalizeTextureSlice(faceData: unknown, face: TextureCubeFace): any {
if (Array.isArray(faceData)) {
if (faceData.length === 0) {
throw new Error(`SkyboxLayer received an empty mip chain for face ${face}.`);
}
return faceData.map((level, mipLevel) => normalizeTextureLevel(level, face, mipLevel));
}
return normalizeTextureLevel(faceData, face, 0);
}
/** Normalizes a single cubemap face mip level into luma.gl upload data. */
function normalizeTextureLevel(faceData: unknown, face: TextureCubeFace, mipLevel: number) {
if (typeof ImageBitmap !== 'undefined' && faceData instanceof ImageBitmap) {
return faceData;
}
const level = faceData as LoadedTextureLevel | null | undefined;
if (level?.imageBitmap) {
return level.imageBitmap;
}
if (level && ArrayBuffer.isView(level.data) && level.width && level.height) {
return {
data: level.data,
width: level.width,
height: level.height,
format:
typeof level.textureFormat === 'string'
? level.textureFormat
: typeof level.format === 'string'
? level.format
: 'rgba8unorm'
};
}
throw new Error(`SkyboxLayer could not normalize cubemap face ${face} mip ${mipLevel}.`);
}