-
-
Notifications
You must be signed in to change notification settings - Fork 116
/
Environment.svelte
126 lines (104 loc) · 3.78 KB
/
Environment.svelte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<script lang="ts">
import { useCache, useParent, useThrelte } from '@threlte/core'
import { onDestroy } from 'svelte'
import type { Scene } from 'three'
import {
CubeReflectionMapping,
CubeTextureLoader,
EquirectangularReflectionMapping,
FloatType,
Texture,
TextureLoader,
SRGBColorSpace,
LinearSRGBColorSpace
} from 'three'
import { HDRCubeTextureLoader } from 'three/examples/jsm/loaders/HDRCubeTextureLoader.js'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
import type { EnvironmentProps } from './Environment.svelte'
import GroundProjectedSkybox from './GroundProjectedSkybox.svelte'
import { useSuspense } from '../../suspense/useSuspense'
type Props = EnvironmentProps
export let path: NonNullable<Props['path']> = ''
export let files: NonNullable<Props['files']>
export let isBackground: Props['isBackground'] = undefined
export let groundProjection: Props['groundProjection'] = undefined
export let format: Props['format'] = undefined
export let colorSpace: Props['colorSpace'] = undefined
const isScene = (obj: any): obj is Scene => !!obj.isScene
const { scene: globalScene, invalidate } = useThrelte()
const parent = useParent()
let scene = globalScene
if (isScene($parent)) scene = $parent
let previousSceneEnvironment = scene.environment
let previousSceneBackground = scene.background
$: isCubeMap = Array.isArray(files)
$: envPath = `${path}${files}`
let previousEnvPath: string = envPath
let previousEnvMap: Texture
let previousFormat: string | undefined
const pickLoader = (): new () => any => {
const inferredFormat =
format || (Array.isArray(files) ? files[0] : files).split('.').pop() == 'hdr' ? 'hdr' : 'ldr'
if (isCubeMap && inferredFormat == 'ldr') return CubeTextureLoader
if (!isCubeMap && inferredFormat == 'ldr') return TextureLoader
if (isCubeMap && inferredFormat == 'hdr') return HDRCubeTextureLoader
if (!isCubeMap && inferredFormat == 'hdr') return RGBELoader
return TextureLoader
}
const { remember } = useCache()
const suspend = useSuspense()
const loadEnvironment = async () => {
const LoaderType = pickLoader()
const loader: any = new LoaderType()
loader.setDataType?.(FloatType)
const filesKey = Array.isArray(files) ? files.join(',') : files
const cacheKey = [LoaderType, path, filesKey]
const texture = (await remember(async () => {
return suspend(
new Promise((resolve, reject) => {
loader.setPath(path).load(files, (texture: any) => {
resolve(texture)
})
})
)
}, cacheKey)) as Texture
texture.mapping = isCubeMap ? CubeReflectionMapping : EquirectangularReflectionMapping
texture.colorSpace = colorSpace ?? isCubeMap ? LinearSRGBColorSpace : SRGBColorSpace
previousEnvMap = texture
scene.environment = previousEnvMap
if (isBackground) scene.background = previousEnvMap
invalidate()
previousFormat = format || undefined
previousEnvPath = envPath
}
$: {
if (envPath != previousEnvPath || format != previousFormat) {
if (previousEnvMap) {
previousEnvMap.dispose()
}
loadEnvironment()
groundProjection = groundProjection
}
if (!isBackground && scene.background) {
scene.background = null
invalidate()
}
if (isBackground && !scene.background && previousEnvMap) {
scene.background = previousEnvMap
invalidate()
}
}
onDestroy(() => {
scene.environment = previousSceneEnvironment
scene.background = previousSceneBackground
if (previousEnvMap) previousEnvMap.dispose()
groundProjection = undefined
invalidate()
})
</script>
{#if groundProjection}
<GroundProjectedSkybox
{...groundProjection}
envMap={previousEnvMap}
/>
{/if}