Skip to content

Commit

Permalink
fix: 581 environment lightformers are not working (#584)
Browse files Browse the repository at this point in the history
* fix(Environment): add EnvironmentDemo and Lightformers components, update routing and environment handling

- Enhanced environment handling in `useEnvironment` with improved texture management and disposal methods.
- Refactored `EnvSence` class to use `dispose` method for better resource management.
- Updated lightformer properties for consistency and improved functionality.

* chore: fix lint

* chore(EnvironmentDemo): set lightformers controls to false as default

* fix(useEnvironment): replace EnvSence with EnvironmentScene class for improved resource management and texture handling

* fix(useEnvironment): improve environment mesh handling by reusing existing meshes and updating background properties to avoid memory leaks
  • Loading branch information
alvarosabu authored Jan 17, 2025
1 parent 09584be commit 653a5df
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<!-- eslint-disable no-console -->
<script setup lang="ts">
import { Environment, Lightformer, OrbitControls, TorusKnot, useProgress } from '@tresjs/cientos'
import { Environment, Lightformer, OrbitControls, Sphere, useProgress } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'

import Lightformers from './Lightformers.vue'
import { TresLeches, useControls } from '@tresjs/leches'
import { BasicShadowMap, NoToneMapping, SRGBColorSpace } from 'three'
import '@tresjs/leches/styles'
Expand All @@ -18,7 +18,7 @@ const gl = {
toneMapping: NoToneMapping,
}

const { background, blur, preset, lightformers } = useControls({
const { background, blur, preset } = useControls({
background: true,
blur: {
value: 0,
Expand All @@ -43,7 +43,6 @@ const { background, blur, preset, lightformers } = useControls({
],
value: 'sunset',
},
lightformers: false,
})

const environmentRef = ref(null)
Expand Down Expand Up @@ -91,40 +90,24 @@ const { progress, hasFinishLoading } = await useProgress()
:background="background.value"
:blur="blur.value"
:preset="preset.value"
:frames="Infinity"
>
<TresGroup v-if="lightformers.value">
<Lightformer
:intensity="0.75"
:rotation-x="Math.PI / 2"
:position="[0, 5, -9]"
:scale="[10, 10, 1]"
/>
<Lightformer
:intensity="4"
:rotation-y="Math.PI / 2"
:position="[-5, 1, -1]"
:scale="[20, 0.1, 1]"
/>
<Lightformer
:rotation-y="Math.PI / 2"
:position="[-5, -1, -1]"
:scale="[20, 0.5, 1]"
/>
<Lightformer
:rotation-y="-Math.PI / 2"
:position="[10, 1, 0]"
:scale="[20, 11, 1]"
/>
</TresGroup>
<Lightformer
:intensity="0.75"
:rotation-x="Math.PI / 2"
:position="[0, 5, -9]"
:scale="[10, 10, 1]"
/>
<Lightformers />
</Environment>
</Suspense>
<TorusKnot>
<Sphere>
<TresMeshStandardMaterial
color="yellow"
:roughness="0"
:metalness="0.5"
/>
</TorusKnot>
</Sphere>
<TresGridHelper />
<TresAmbientLight :intensity="1" />
</TresCanvas>
Expand Down
50 changes: 50 additions & 0 deletions playground/vue/src/pages/staging/environment/Lightformers.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script setup lang="ts">
import { useControls } from '@tresjs/leches'
import { useLoop } from '@tresjs/core'
import { ref } from 'vue'
import { Lightformer } from '@tresjs/cientos'
const { lightformers } = useControls({
lightformers: false,
})
const lightformerRef = ref(null)
const { onBeforeRender } = useLoop()
onBeforeRender(({ elapsed }) => {
if (lightformerRef.value) {
lightformerRef.value.mesh.position.y = Math.sin(elapsed) * 2
}
})
</script>

<template>
<TresGroup v-if="lightformers">
<Lightformer
ref="lightformerRef"
:intensity="1"
color="red"
form="ring"
:rotation-x="Math.PI / 2"
:position="[0, 5, -9]"
:scale="[10, 10, 1]"
/>
<Lightformer
:intensity="4"
:rotation-y="Math.PI / 2"
:position="[-5, 1, -1]"
:scale="[20, 0.1, 1]"
/>
<Lightformer
:rotation-y="Math.PI / 2"
:position="[-5, -1, -1]"
:scale="[20, 0.5, 1]"
/>
<Lightformer
:rotation-y="-Math.PI / 2"
:position="[10, 1, 0]"
:scale="[20, 11, 1]"
/>
</TresGroup>
</template>
2 changes: 1 addition & 1 deletion playground/vue/src/router/routes/staging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const stagingRoutes = [
{
path: '/staging/environment',
name: 'Environment',
component: () => import('../../pages/staging/EnvironmentDemo.vue'),
component: () => import('../../pages/staging/environment/EnvironmentDemo.vue'),
},
{
path: '/staging/backdrop',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Mesh, Object3D, Scene } from 'three'

class EnvSence extends Object3D {
class EnvironmentScene extends Object3D {
virtualScene = null as unknown as Scene
constructor() {
super()
Expand All @@ -12,7 +12,7 @@ class EnvSence extends Object3D {
return this
}

destructor() {
dispose() {
this.virtualScene.traverse((object) => {
if (object instanceof Mesh) {
object.geometry.dispose()
Expand All @@ -25,4 +25,4 @@ class EnvSence extends Object3D {
}
}

export default EnvSence
export default EnvironmentScene
78 changes: 57 additions & 21 deletions src/core/staging/useEnvironment/component.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { useLoop, useTresContext } from '@tresjs/core'
import { CubeCamera, HalfFloatType, WebGLCubeRenderTarget } from 'three'
import { BackSide, BoxGeometry, CubeCamera, HalfFloatType, Mesh, MeshBasicMaterial, WebGLCubeRenderTarget } from 'three'
import { onUnmounted, ref, toRaw, useSlots, watch } from 'vue'
import type { CubeTexture, Texture } from 'three'
import type { Ref } from 'vue'
import { useEnvironment } from '.'
import EnvSence from './envSence'
import EnvironmentScene from './EnvironmentScene'
import type { EnvironmentOptions } from './const'
const props = withDefaults(defineProps<EnvironmentOptions>(), {
Expand All @@ -24,37 +24,75 @@ const texture: Ref<Texture | CubeTexture | null> = ref(null)
defineExpose({ texture })
const { extend, renderer, scene } = useTresContext()
extend({ EnvironmentScene })
let slots = null as any
const fbo = ref<WebGLCubeRenderTarget | null>(null)
let cubeCamera: CubeCamera | null = null
const environmentScene = ref<EnvironmentScene | null>(null)
const envSence = ref<EnvSence | null>(null)
const useEnvironmentTexture = await useEnvironment(props, fbo)
const { onBeforeRender } = useLoop()
let count = 1
onBeforeRender(() => {
if (cubeCamera && envSence.value && fbo.value) {
if (cubeCamera && environmentScene.value && fbo.value) {
if (props.frames === Number.POSITIVE_INFINITY || count < props.frames) {
cubeCamera.update(renderer.value, toRaw(envSence.value.virtualScene))
// Update cube camera
const autoClear = renderer.value.autoClear
renderer.value.autoClear = true
// Use raw scene to avoid proxy issues
const rawScene = toRaw(environmentScene.value).virtualScene
cubeCamera.update(renderer.value, rawScene)
renderer.value.autoClear = autoClear
count++
}
}
}, -1)
const useEnvironmentTexture = await useEnvironment(props, fbo)
// Add environment map to virtual scene when available
watch([useEnvironmentTexture, environmentScene], ([texture, scene]) => {
if (texture && scene?.virtualScene) {
const rawScene = toRaw(scene).virtualScene
// Find existing environment mesh or create a new one
let envMesh = rawScene.children.find(
child => child instanceof Mesh && child.userData.isEnvironment,
) as Mesh | undefined
if (!envMesh) {
// Create new environment mesh if none exists
envMesh = new Mesh(
new BoxGeometry(1, 1, 1),
new MeshBasicMaterial({ side: BackSide }),
)
envMesh.userData.isEnvironment = true
rawScene.add(envMesh)
}
// Update the environment map
rawScene.background = texture
rawScene.backgroundBlurriness = props.blur
}
}, { immediate: true })
const setTextureEnvAndBG = (fbo?: WebGLCubeRenderTarget) => {
if (fbo) {
if (fbo && slots?.length) {
// If we have lightformers, use FBO texture for both environment and background
scene.value.environment = fbo.texture
if (props.background) {
scene.value.background = fbo.texture
}
}
else {
else if (useEnvironmentTexture.value) {
// Otherwise use the original environment texture
scene.value.environment = useEnvironmentTexture.value
if (props.background) {
scene.value.background = useEnvironmentTexture.value
}
}
}
watch(useEnvironmentTexture, () => {
if (fbo.value) {
setTextureEnvAndBG(fbo.value)
Expand All @@ -63,16 +101,14 @@ watch(useEnvironmentTexture, () => {
watch(() => useSlots().default, (value) => {
if (value) {
slots = value
slots = value()
if (Array.isArray(slots) && slots.length > 0) {
if (typeof slots[0]?.type !== 'symbol') {
extend({ EnvSence })
fbo.value = new WebGLCubeRenderTarget(props.resolution)
fbo.value.texture.type = HalfFloatType
cubeCamera = new CubeCamera(props.near, props.far, fbo.value)
setTextureEnvAndBG(fbo.value)
return
}
extend({ EnvironmentScene })
fbo.value = new WebGLCubeRenderTarget(props.resolution)
fbo.value.texture.type = HalfFloatType
cubeCamera = new CubeCamera(props.near, props.far, fbo.value)
setTextureEnvAndBG(fbo.value)
return
}
}
fbo.value?.dispose()
Expand All @@ -83,16 +119,16 @@ watch(() => useSlots().default, (value) => {
texture.value = useEnvironmentTexture.value
onUnmounted(() => {
envSence.value?.destructor()
environmentScene.value?.dispose()
fbo.value?.dispose()
})
</script>

<template>
<TresEnvSence
<TresEnvironmentScene
v-if="fbo"
ref="envSence"
ref="environmentScene"
>
<slot></slot>
</TresEnvSence>
</TresEnvironmentScene>
</template>
23 changes: 15 additions & 8 deletions src/core/staging/useEnvironment/lightformer/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,50 @@ import type { MeshBasicMaterial, Texture } from 'three'
const props = withDefaults(defineProps<{
args?: any[]
from?: 'circle' | 'ring' | 'rect' | any
form?: 'circle' | 'ring' | 'rect' | any
toneMapped?: boolean
map?: Texture
intensity?: number
color?: any
}>(), {
args: null as any,
from: 'rect',
form: 'rect',
toneMapped: false,
map: null as any,
intensity: 1,
color: new Color(0xFFFFFF),
})
const material = ref<MeshBasicMaterial>()
const mesh = ref()
watchEffect(() => {
if (material.value) {
// Reset color before applying intensity to avoid accumulation
material.value.color.copy(new Color(props.color))
material.value.color.multiplyScalar(props.intensity)
material.value.needsUpdate = true
}
})
// Expose mesh for parent components
defineExpose({ mesh })
</script>

<template>
<TresMesh>
<TresMesh ref="mesh">
<TresRingGeometry
v-if="from === 'circle'"
v-if="form === 'circle'"
:args="[0, 1, 64]"
/>
<TresRingGeometry
v-else-if="from === 'ring'"
v-else-if="form === 'ring'"
:args="[0.5, 1, 64]"
/>
<TresPlaneGeometry v-else-if="from === 'rect'" />
<props.from
<TresPlaneGeometry v-else-if="form === 'rect'" />
<props.form
v-else
:args="props"
:args="args"
/>

<TresMeshBasicMaterial
Expand Down

0 comments on commit 653a5df

Please sign in to comment.