From 6ca0358108c52de50db7c69f814cdcd8fe56003f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Roche?= Date: Tue, 29 Oct 2024 20:13:32 +0100 Subject: [PATCH 1/5] fix useTexture callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Johannes Wüller --- src/core/Texture.tsx | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/core/Texture.tsx b/src/core/Texture.tsx index 139d8a712..bdeeccda0 100644 --- a/src/core/Texture.tsx +++ b/src/core/Texture.tsx @@ -1,7 +1,6 @@ -import * as React from 'react' import { Texture as _Texture, TextureLoader } from 'three' import { useLoader, useThree } from '@react-three/fiber' -import { useLayoutEffect, useEffect, useMemo } from 'react' +import { useEffect, useLayoutEffect, useMemo } from 'react' export const IsObject = (url: unknown): url is Record => url === Object(url) && !Array.isArray(url) && typeof url !== 'function' @@ -20,31 +19,20 @@ export function useTexture) => void ): MappedTextureType { const gl = useThree((state) => state.gl) - const textures = useLoader(TextureLoader, IsObject(input) ? Object.values(input) : input) as MappedTextureType - - useLayoutEffect(() => { - onLoad?.(textures) - }, [onLoad]) + const textures = useLoader(TextureLoader, IsObject(input) ? Object.values(input) : input) // https://github.com/mrdoob/three.js/issues/22696 // Upload the texture to the GPU immediately instead of waiting for the first render // NOTE: only available for WebGLRenderer useEffect(() => { if ('initTexture' in gl) { - let textureArray: _Texture[] = [] if (Array.isArray(textures)) { - textureArray = textures - } else if (textures instanceof _Texture) { - textureArray = [textures] - } else if (IsObject(textures)) { - textureArray = Object.values(textures) - } - - textureArray.forEach((texture) => { - if (texture instanceof _Texture) { + for (const texture of textures) { gl.initTexture(texture) } - }) + } else { + gl.initTexture(textures) + } } }, [gl, textures]) @@ -55,10 +43,14 @@ export function useTexture } }, [input, textures]) + useLayoutEffect(() => { + onLoad?.(mappedTextures) + }, [mappedTextures]) + return mappedTextures } From b80a4750c4471212603e8a501a5d08fc1156cd02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Roche?= Date: Tue, 29 Oct 2024 21:59:54 +0100 Subject: [PATCH 2/5] import react MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Johannes Wüller (@jwueller) --- src/core/Texture.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/Texture.tsx b/src/core/Texture.tsx index bdeeccda0..95826d7d5 100644 --- a/src/core/Texture.tsx +++ b/src/core/Texture.tsx @@ -1,3 +1,4 @@ +import * as React from 'react' import { Texture as _Texture, TextureLoader } from 'three' import { useLoader, useThree } from '@react-three/fiber' import { useEffect, useLayoutEffect, useMemo } from 'react' From 7f5bd92d95755455faacdcb8fcc952d98d4abce7 Mon Sep 17 00:00:00 2001 From: Antoine BERNIER Date: Wed, 13 Nov 2024 09:00:36 +0100 Subject: [PATCH 3/5] exhaustive deps --- src/core/Texture.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/Texture.tsx b/src/core/Texture.tsx index 95826d7d5..e36c2b747 100644 --- a/src/core/Texture.tsx +++ b/src/core/Texture.tsx @@ -1,3 +1,4 @@ +/* eslint react-hooks/exhaustive-deps: 1 */ import * as React from 'react' import { Texture as _Texture, TextureLoader } from 'three' import { useLoader, useThree } from '@react-three/fiber' @@ -50,7 +51,7 @@ export function useTexture { onLoad?.(mappedTextures) - }, [mappedTextures]) + }, [mappedTextures, onLoad]) return mappedTextures } From eb4475934c2df4dc7972057ca44ac885f681b321 Mon Sep 17 00:00:00 2001 From: Antoine BERNIER Date: Wed, 13 Nov 2024 10:08:14 +0100 Subject: [PATCH 4/5] simplified types --- src/core/Texture.tsx | 47 +++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/core/Texture.tsx b/src/core/Texture.tsx index e36c2b747..4affbe7ad 100644 --- a/src/core/Texture.tsx +++ b/src/core/Texture.tsx @@ -4,24 +4,23 @@ import { Texture as _Texture, TextureLoader } from 'three' import { useLoader, useThree } from '@react-three/fiber' import { useEffect, useLayoutEffect, useMemo } from 'react' +type Composite = T | T[] | Record + +type Input = Composite +type Output = Composite<_Texture> + +// @deprecated: no more used export const IsObject = (url: unknown): url is Record => url === Object(url) && !Array.isArray(url) && typeof url !== 'function' -type TextureArray = T extends string[] ? _Texture[] : never -type TextureRecord = T extends Record ? { [key in keyof T]: _Texture } : never -type SingleTexture = T extends string ? _Texture : never +// @deprecated: use ReturnType instead +export type MappedTextureType = Output +export function useTexture(input: Input, onLoad?: (texture: Output) => void) { + const gl = useThree((state) => state.gl) -export type MappedTextureType> = - | TextureArray - | TextureRecord - | SingleTexture + const strOrArr = Array.isArray(input) || typeof input === 'string' -export function useTexture>( - input: Url, - onLoad?: (texture: MappedTextureType) => void -): MappedTextureType { - const gl = useThree((state) => state.gl) - const textures = useLoader(TextureLoader, IsObject(input) ? Object.values(input) : input) + const textures = useLoader(TextureLoader, strOrArr ? input : Object.values(input)) // https://github.com/mrdoob/three.js/issues/22696 // Upload the texture to the GPU immediately instead of waiting for the first render @@ -29,31 +28,29 @@ export function useTexture { if ('initTexture' in gl) { if (Array.isArray(textures)) { - for (const texture of textures) { - gl.initTexture(texture) - } + for (const texture of textures) gl.initTexture(texture) } else { gl.initTexture(textures) } } }, [gl, textures]) - const mappedTextures = useMemo(() => { - if (IsObject(input)) { - const keyed = {} as MappedTextureType + const compositeTextures = useMemo(() => { + if (strOrArr) { + return textures + } else { + const keyed = {} as Record // remap the `textures` to their `input` keys let i = 0 for (const key in input) keyed[key] = textures[i++] return keyed - } else { - return textures as MappedTextureType } - }, [input, textures]) + }, [input, strOrArr, textures]) useLayoutEffect(() => { - onLoad?.(mappedTextures) - }, [mappedTextures, onLoad]) + onLoad?.(compositeTextures) + }, [compositeTextures, onLoad]) - return mappedTextures + return compositeTextures } useTexture.preload = (url: string | string[]) => useLoader.preload(TextureLoader, url) From 290331e9e7d3bfd7bf9e33d11cd9d65309204420 Mon Sep 17 00:00:00 2001 From: Antoine BERNIER Date: Wed, 13 Nov 2024 10:19:21 +0100 Subject: [PATCH 5/5] Revert "simplified types" This reverts commit eb4475934c2df4dc7972057ca44ac885f681b321. --- src/core/Texture.tsx | 47 +++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/core/Texture.tsx b/src/core/Texture.tsx index 4affbe7ad..e36c2b747 100644 --- a/src/core/Texture.tsx +++ b/src/core/Texture.tsx @@ -4,23 +4,24 @@ import { Texture as _Texture, TextureLoader } from 'three' import { useLoader, useThree } from '@react-three/fiber' import { useEffect, useLayoutEffect, useMemo } from 'react' -type Composite = T | T[] | Record - -type Input = Composite -type Output = Composite<_Texture> - -// @deprecated: no more used export const IsObject = (url: unknown): url is Record => url === Object(url) && !Array.isArray(url) && typeof url !== 'function' -// @deprecated: use ReturnType instead -export type MappedTextureType = Output -export function useTexture(input: Input, onLoad?: (texture: Output) => void) { - const gl = useThree((state) => state.gl) +type TextureArray = T extends string[] ? _Texture[] : never +type TextureRecord = T extends Record ? { [key in keyof T]: _Texture } : never +type SingleTexture = T extends string ? _Texture : never - const strOrArr = Array.isArray(input) || typeof input === 'string' +export type MappedTextureType> = + | TextureArray + | TextureRecord + | SingleTexture - const textures = useLoader(TextureLoader, strOrArr ? input : Object.values(input)) +export function useTexture>( + input: Url, + onLoad?: (texture: MappedTextureType) => void +): MappedTextureType { + const gl = useThree((state) => state.gl) + const textures = useLoader(TextureLoader, IsObject(input) ? Object.values(input) : input) // https://github.com/mrdoob/three.js/issues/22696 // Upload the texture to the GPU immediately instead of waiting for the first render @@ -28,29 +29,31 @@ export function useTexture(input: Input, onLoad?: (texture: Output) => void) { useEffect(() => { if ('initTexture' in gl) { if (Array.isArray(textures)) { - for (const texture of textures) gl.initTexture(texture) + for (const texture of textures) { + gl.initTexture(texture) + } } else { gl.initTexture(textures) } } }, [gl, textures]) - const compositeTextures = useMemo(() => { - if (strOrArr) { - return textures - } else { - const keyed = {} as Record // remap the `textures` to their `input` keys + const mappedTextures = useMemo(() => { + if (IsObject(input)) { + const keyed = {} as MappedTextureType let i = 0 for (const key in input) keyed[key] = textures[i++] return keyed + } else { + return textures as MappedTextureType } - }, [input, strOrArr, textures]) + }, [input, textures]) useLayoutEffect(() => { - onLoad?.(compositeTextures) - }, [compositeTextures, onLoad]) + onLoad?.(mappedTextures) + }, [mappedTextures, onLoad]) - return compositeTextures + return mappedTextures } useTexture.preload = (url: string | string[]) => useLoader.preload(TextureLoader, url)