Skip to content

Commit

Permalink
simplified types
Browse files Browse the repository at this point in the history
  • Loading branch information
abernier committed Nov 13, 2024
1 parent 7f5bd92 commit eb44759
Showing 1 changed file with 22 additions and 25 deletions.
47 changes: 22 additions & 25 deletions src/core/Texture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,53 @@ import { Texture as _Texture, TextureLoader } from 'three'
import { useLoader, useThree } from '@react-three/fiber'
import { useEffect, useLayoutEffect, useMemo } from 'react'

type Composite<T> = T | T[] | Record<string, T>

type Input = Composite<string>
type Output = Composite<_Texture>

// @deprecated: no more used
export const IsObject = (url: unknown): url is Record<string, string> =>
url === Object(url) && !Array.isArray(url) && typeof url !== 'function'

type TextureArray<T> = T extends string[] ? _Texture[] : never
type TextureRecord<T> = T extends Record<string, string> ? { [key in keyof T]: _Texture } : never
type SingleTexture<T> = T extends string ? _Texture : never
// @deprecated: use ReturnType<typeof useTexture> instead
export type MappedTextureType<T extends Input> = Output
export function useTexture(input: Input, onLoad?: (texture: Output) => void) {
const gl = useThree((state) => state.gl)

export type MappedTextureType<T extends string[] | string | Record<string, string>> =
| TextureArray<T>
| TextureRecord<T>
| SingleTexture<T>
const strOrArr = Array.isArray(input) || typeof input === 'string'

export function useTexture<Url extends string[] | string | Record<string, string>>(
input: Url,
onLoad?: (texture: MappedTextureType<Url>) => void
): MappedTextureType<Url> {
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
// NOTE: only available for WebGLRenderer
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 mappedTextures = useMemo(() => {
if (IsObject(input)) {
const keyed = {} as MappedTextureType<Url>
const compositeTextures = useMemo(() => {
if (strOrArr) {
return textures
} else {
const keyed = {} as Record<string, _Texture> // 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<Url>
}
}, [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)
Expand Down

0 comments on commit eb44759

Please sign in to comment.