From 665eace8709bd7506295c087910e69d10820b6d1 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 28 Aug 2024 15:53:51 +0200 Subject: [PATCH 1/3] feat(useEnvironment): add clear function --- src/core/useEnvironment.tsx | 111 ++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 31 deletions(-) diff --git a/src/core/useEnvironment.tsx b/src/core/useEnvironment.tsx index 54bc34eb5..c433d98c1 100644 --- a/src/core/useEnvironment.tsx +++ b/src/core/useEnvironment.tsx @@ -24,8 +24,10 @@ export type EnvironmentLoaderProps = { encoding?: TextureEncoding } +const defaultFiles = ['/px.png', '/nx.png', '/py.png', '/ny.png', '/pz.png', '/nz.png'] + export function useEnvironment({ - files = ['/px.png', '/nx.png', '/py.png', '/ny.png', '/pz.png', '/nz.png'], + files = defaultFiles, path = '', preset = undefined, encoding = undefined, @@ -35,41 +37,17 @@ export function useEnvironment({ let multiFile: boolean = false if (preset) { - if (!(preset in presetsObj)) throw new Error('Preset must be one of: ' + Object.keys(presetsObj).join(', ')) + validatePreset(preset) files = presetsObj[preset] path = CUBEMAP_ROOT } - const isCubemap = isArray(files) && files.length === 6 - const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith('json')) - const firstEntry = isArray(files) ? files[0] : files - // Everything else multiFile = isArray(files) - const extension: string | false | undefined = isCubemap - ? 'cube' - : isGainmap - ? 'webp' - : firstEntry.startsWith('data:application/exr') - ? 'exr' - : firstEntry.startsWith('data:application/hdr') - ? 'hdr' - : firstEntry.startsWith('data:image/jpeg') - ? 'jpg' - : firstEntry.split('.').pop()?.split('?')?.shift()?.toLowerCase() - loader = - extension === 'cube' - ? CubeTextureLoader - : extension === 'hdr' - ? RGBELoader - : extension === 'exr' - ? EXRLoader - : extension === 'jpg' || extension === 'jpeg' - ? (HDRJPGLoader as unknown as typeof Loader) - : extension === 'webp' - ? (GainMapLoader as unknown as typeof Loader) - : null + const { extension, isCubemap } = getExtension(files) + + loader = getLoader(extension) if (!loader) throw new Error('useEnvironment: Unrecognized file extension: ' + files) const gl = useThree((state) => state.gl) @@ -79,8 +57,11 @@ export function useEnvironment({ if (extension !== 'webp' && extension !== 'jpg' && extension !== 'jpeg') return function clearGainmapTexture() { - // @ts-expect-error - useLoader.clear(loader, multiFile ? [files] : files) + useLoader.clear( + // @ts-expect-error + loader, + multiFile ? [files] : files + ) } gl.domElement.addEventListener('webglcontextlost', clearGainmapTexture, { once: true }) @@ -115,3 +96,71 @@ export function useEnvironment({ return texture } + +type EnvironmentLoaderClearOptions = Pick +const clearDefaultOptins = { + files: defaultFiles, + preset: undefined, +} + +useEnvironment.clear = (clearOptions?: EnvironmentLoaderClearOptions) => { + const options = { ...clearDefaultOptins, ...clearOptions } + let { files } = options + const { preset } = options + + if (preset) { + validatePreset(preset) + files = presetsObj[preset] + } + + const { extension } = getExtension(files) + const loader = getLoader(extension) + if (!loader) throw new Error('useEnvironment: Unrecognized file extension: ' + files) + useLoader.clear( + // @ts-expect-error + loader, + isArray(files) ? [files] : files + ) +} + +function validatePreset(preset: string) { + if (!(preset in presetsObj)) throw new Error('Preset must be one of: ' + Object.keys(presetsObj).join(', ')) +} + +function getExtension(files: string | string[]) { + const isCubemap = isArray(files) && files.length === 6 + const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith('json')) + const firstEntry = isArray(files) ? files[0] : files + + // Everything else + const extension: string | false | undefined = isCubemap + ? 'cube' + : isGainmap + ? 'webp' + : firstEntry.startsWith('data:application/exr') + ? 'exr' + : firstEntry.startsWith('data:application/hdr') + ? 'hdr' + : firstEntry.startsWith('data:image/jpeg') + ? 'jpg' + : firstEntry.split('.').pop()?.split('?')?.shift()?.toLowerCase() + + return { extension, isCubemap, isGainmap } +} + +function getLoader(extension: string | undefined) { + const loader: typeof Loader | null = + extension === 'cube' + ? CubeTextureLoader + : extension === 'hdr' + ? RGBELoader + : extension === 'exr' + ? EXRLoader + : extension === 'jpg' || extension === 'jpeg' + ? (HDRJPGLoader as unknown as typeof Loader) + : extension === 'webp' + ? (GainMapLoader as unknown as typeof Loader) + : null + + return loader +} From 5fe8366b9d74912a20b6a98c8c2fb5b7dde9cc50 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 28 Aug 2024 15:54:29 +0200 Subject: [PATCH 2/3] feat(useEnvironment): add preload function --- src/core/useEnvironment.tsx | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/core/useEnvironment.tsx b/src/core/useEnvironment.tsx index c433d98c1..474a3a520 100644 --- a/src/core/useEnvironment.tsx +++ b/src/core/useEnvironment.tsx @@ -97,6 +97,45 @@ export function useEnvironment({ return texture } +type EnvironmentLoaderPreloadOptions = Omit +const preloadDefaultOptions = { + files: defaultFiles, + path: '', + preset: undefined, + extensions: undefined, +} + +useEnvironment.preload = (preloadOptions?: EnvironmentLoaderPreloadOptions) => { + const options = { ...preloadDefaultOptions, ...preloadOptions } + let { files, path = '' } = options + const { preset, extensions } = options + + if (preset) { + validatePreset(preset) + files = presetsObj[preset] + path = CUBEMAP_ROOT + } + + const { extension } = getExtension(files) + + if (extension === 'webp' || extension === 'jpg' || extension === 'jpeg') { + throw new Error('useEnvironment: Preloading gainmaps is not supported') + } + + const loader = getLoader(extension) + if (!loader) throw new Error('useEnvironment: Unrecognized file extension: ' + files) + + useLoader.preload( + // @ts-expect-error + loader, + isArray(files) ? [files] : files, + (loader) => { + loader.setPath?.(path) + if (extensions) extensions(loader) + } + ) +} + type EnvironmentLoaderClearOptions = Pick const clearDefaultOptins = { files: defaultFiles, From f75159e7ec95c761b54cce87988c9ff9dfa09b59 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 28 Aug 2024 17:20:56 +0200 Subject: [PATCH 3/3] docs(useEnvironment): add docs for clear and preload functions --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index cb29be2f3..10985220a 100644 --- a/README.md +++ b/README.md @@ -5063,6 +5063,24 @@ const rgbeTexture = useEnvironment({ files: 'model.hdr' }) const cubeTexture = useEnvironment({ files: ['px', 'nx', 'py', 'ny', 'pz', 'nz'].map((n) => `${n}.png`) }) ``` +In order to preload you do this: + +```jsx +useEnvironment.preload({ preset: 'city' }) +useEnvironment.preload({ files: 'model.hdr' }) +useEnvironment.preload({ files: ['px', 'nx', 'py', 'ny', 'pz', 'nz'].map((n) => `${n}.png`) }) +``` + +Keep in mind that preloading [gainmaps](https://github.com/MONOGRID/gainmap-js) is not possible, because their loader requires access to the renderer. + +You can also clear your environment map from the cache: + +```jsx +useEnvironment.clear({ preset: 'city' }) +useEnvironment.clear({ files: 'model.hdr' }) +useEnvironment.clear({ files: ['px', 'nx', 'py', 'ny', 'pz', 'nz'].map((n) => `${n}.png`) }) +``` + #### MatcapTexture / useMatcapTexture [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/staging-matcaptexture) ![](https://img.shields.io/badge/-suspense-brightgreen)