diff --git a/packages/engine/src/scene/systems/ShadowSystem.tsx b/packages/engine/src/scene/systems/ShadowSystem.tsx
index 80915d3d79..0570281960 100644
--- a/packages/engine/src/scene/systems/ShadowSystem.tsx
+++ b/packages/engine/src/scene/systems/ShadowSystem.tsx
@@ -55,15 +55,7 @@ import { createEntity, removeEntity, useEntityContext } from '@etherealengine/ec
import { defineQuery, QueryReactor } from '@etherealengine/ecs/src/QueryFunctions'
import { defineSystem, useExecute } from '@etherealengine/ecs/src/SystemFunctions'
import { AnimationSystemGroup } from '@etherealengine/ecs/src/SystemGroups'
-import {
- defineState,
- getMutableState,
- getState,
- hookstate,
- NO_PROXY,
- useHookstate,
- useMutableState
-} from '@etherealengine/hyperflux'
+import { defineState, getMutableState, getState, hookstate, NO_PROXY, useHookstate } from '@etherealengine/hyperflux'
import { Vector3_Back } from '@etherealengine/spatial/src/common/constants/MathConstants'
import {
createPriorityQueue,
@@ -74,11 +66,10 @@ import { DirectionalLightComponent } from '@etherealengine/spatial/src/renderer/
import { addObjectToGroup, GroupComponent } from '@etherealengine/spatial/src/renderer/components/GroupComponent'
import { MeshComponent } from '@etherealengine/spatial/src/renderer/components/MeshComponent'
import { ObjectLayerComponents } from '@etherealengine/spatial/src/renderer/components/ObjectLayerComponent'
-import { useScene } from '@etherealengine/spatial/src/renderer/components/SceneComponents'
import { VisibleComponent } from '@etherealengine/spatial/src/renderer/components/VisibleComponent'
import { ObjectLayers } from '@etherealengine/spatial/src/renderer/constants/ObjectLayers'
import { CSM } from '@etherealengine/spatial/src/renderer/csm/CSM'
-import { CSMHelper } from '@etherealengine/spatial/src/renderer/csm/CSMHelper'
+//import { CSMHelper } from '@etherealengine/spatial/src/renderer/csm/CSMHelper'
import {
getShadowsEnabled,
useShadowsEnabled
@@ -89,7 +80,7 @@ import { compareDistanceToCamera } from '@etherealengine/spatial/src/transform/c
import {
EntityTreeComponent,
iterateEntityNode,
- useTreeQuery
+ useChildWithComponent
} from '@etherealengine/spatial/src/transform/components/EntityTree'
import { TransformComponent } from '@etherealengine/spatial/src/transform/components/TransformComponent'
import { XRLightProbeState } from '@etherealengine/spatial/src/xr/XRLightProbeSystem'
@@ -123,10 +114,10 @@ const raycaster = new Raycaster()
raycaster.firstHitOnly = true
const raycasterPosition = new Vector3()
-const EntityCSMReactor = (props: { entity: Entity; rendererEntity: Entity }) => {
- const { entity, rendererEntity } = props
- const renderSettings = useComponent(entity, RenderSettingsComponent)
+const EntityCSMReactor = (props: { entity: Entity; rendererEntity: Entity; renderSettingsEntity: Entity }) => {
+ const { entity, rendererEntity, renderSettingsEntity } = props
const rendererComponent = useComponent(rendererEntity, RendererComponent)
+ const renderSettingsComponent = useComponent(renderSettingsEntity, RenderSettingsComponent)
const directionalLightComponent = useComponent(entity, DirectionalLightComponent)
const shadowMapResolution = useHookstate(getMutableState(RendererState).shadowMapResolution)
@@ -137,12 +128,15 @@ const EntityCSMReactor = (props: { entity: Entity; rendererEntity: Entity }) =>
useEffect(() => {
if (!directionalLightComponent.value) return
+ if (!directionalLightComponent.castShadow.value) return
const csm = new CSM({
light: directionalLight as DirectionalLight,
+ shadowMapSize: shadowMapResolution.value,
shadowBias: directionalLightComponent.shadowBias.value,
maxFar: directionalLightComponent.cameraFar.value,
lightIntensity: directionalLightComponent.intensity.value,
- cascades: renderSettings.cascades.value
+ lightColor: directionalLightComponent.color.value,
+ cascades: renderSettingsComponent?.cascades.value
})
rendererComponent.csm.set(csm)
return () => {
@@ -154,6 +148,7 @@ const EntityCSMReactor = (props: { entity: Entity; rendererEntity: Entity }) =>
/** Must run after scene object system to ensure source light is not lit */
useExecute(
() => {
+ if (!directionalLightComponent.castShadow.value) return
directionalLight.visible = false
},
{ after: SceneObjectSystem }
@@ -161,34 +156,43 @@ const EntityCSMReactor = (props: { entity: Entity; rendererEntity: Entity }) =>
useEffect(() => {
if (!csm) return
+ if (!directionalLightComponent.castShadow.value) return
csm.shadowBias = directionalLight.shadow.bias
+ csm.maxFar = directionalLightComponent.cameraFar.value
+ csm.shadowMapSize = shadowMapResolution.value
for (const light of csm.lights) {
light.color.copy(directionalLightComponent.color.value)
light.intensity = directionalLightComponent.intensity.value
- light.shadow.bias = directionalLightComponent.shadowBias.value
light.shadow.mapSize.setScalar(shadowMapResolution.value)
- csm.needsUpdate = true
+ light.shadow.radius = directionalLightComponent.shadowRadius.value
}
+ csm.needsUpdate = true
}, [
- csm,
+ rendererComponent.csm,
shadowMapResolution,
- directionalLightComponent?.shadowBias,
- directionalLightComponent?.intensity,
- directionalLightComponent?.color,
- directionalLightComponent?.castShadow,
- directionalLightComponent?.shadowRadius,
- directionalLightComponent?.cameraFar
+ directionalLightComponent.shadowBias,
+ directionalLightComponent.intensity,
+ directionalLightComponent.color,
+ directionalLightComponent.castShadow,
+ directionalLightComponent.shadowRadius,
+ directionalLightComponent.cameraFar
])
useEffect(() => {
if (!csm) return
- csm.cascades = renderSettings.cascades.value
+ csm.cascades = renderSettingsComponent.cascades.value
csm.needsUpdate = true
- }, [csm, renderSettings.cascades])
+ }, [csm, renderSettingsComponent.cascades])
- return
+ return (
+
+ )
}
const PlainCSMReactor = (props: { rendererEntity: Entity }) => {
@@ -222,22 +226,18 @@ const PlainCSMReactor = (props: { rendererEntity: Entity }) => {
}
}, [rendererComponent.csm, shadowMapResolution])
- return
-}
-
-const ChildCSMReactor = (props: { rendererEntity: Entity }) => {
- const entities = useTreeQuery(props.rendererEntity)
return (
- <>
- {entities.map((entity) => (
-
- ))}
- >
+
)
}
-const EntityChildCSMReactor = (props: { entity: Entity; rendererEntity: Entity }) => {
- const { entity, rendererEntity } = props
+const EntityChildCSMReactor = (props: { rendererEntity: Entity }) => {
+ const entity = useEntityContext()
+ const { rendererEntity } = props
const shadowComponent = useComponent(entity, ShadowComponent)
const groupComponent = useComponent(entity, GroupComponent)
@@ -246,6 +246,8 @@ const EntityChildCSMReactor = (props: { entity: Entity; rendererEntity: Entity }
useEffect(() => {
if (!csm || !shadowComponent.receive.value) return
+ if (!groupComponent) return
+
const objs = [...groupComponent.value] as Mesh[]
for (const obj of objs) {
if (obj.material) {
@@ -266,17 +268,18 @@ const EntityChildCSMReactor = (props: { entity: Entity; rendererEntity: Entity }
}
function _CSMReactor() {
- const renderSettingsEntity = useEntityContext()
- const rendererEntity = useScene(renderSettingsEntity)
+ const rendererEntity = useEntityContext()
+ const renderSettingsEntity = useChildWithComponent(rendererEntity, RenderSettingsComponent)
if (!rendererEntity) return null
+ if (!renderSettingsEntity) return null
- return
+ return
}
-function CSMReactor(props: { renderSettingsEntity: Entity; rendererEntity: Entity }) {
- const { renderSettingsEntity, rendererEntity } = props
- const rendererComponent = useComponent(rendererEntity, RendererComponent)
+function CSMReactor(props: { rendererEntity: Entity; renderSettingsEntity: Entity }) {
+ const { rendererEntity, renderSettingsEntity } = props
+ //const rendererComponent = useComponent(rendererEntity, RendererComponent)
const renderSettingsComponent = useComponent(renderSettingsEntity, RenderSettingsComponent)
@@ -284,19 +287,19 @@ function CSMReactor(props: { renderSettingsEntity: Entity; rendererEntity: Entit
const activeLightEntity = useHookstate(UndefinedEntity)
- const rendererState = useMutableState(RendererState)
+ //const rendererState = useMutableState(RendererState)
- useEffect(() => {
- if (!rendererComponent) return
- if (!rendererComponent.csm.value || !rendererState.nodeHelperVisibility.value) return
+ // useEffect(() => {
+ // if (!rendererComponent) return
+ // if (!rendererComponent.csm.value || !rendererState.nodeHelperVisibility.value) return
- const helper = new CSMHelper()
- rendererComponent.csmHelper.set(helper)
- return () => {
- helper.remove()
- rendererComponent.csmHelper.set(null)
- }
- }, [rendererComponent, renderSettingsComponent?.csm, rendererState.nodeHelperVisibility])
+ // const helper = new CSMHelper()
+ // rendererComponent.csmHelper.set(helper)
+ // return () => {
+ // helper.remove()
+ // rendererComponent.csmHelper.set(null)
+ // }
+ // }, [rendererComponent, renderSettingsComponent.csm, rendererState.nodeHelperVisibility])
useEffect(() => {
if (rendererEntity === Engine.instance.viewerEntity && xrLightProbeEntity.value) {
@@ -304,20 +307,24 @@ function CSMReactor(props: { renderSettingsEntity: Entity; rendererEntity: Entit
return
}
- if (renderSettingsComponent?.primaryLight.value) {
+ if (renderSettingsComponent.primaryLight.value) {
activeLightEntity.set(UUIDComponent.getEntityByUUID(renderSettingsComponent.primaryLight.value))
return
}
activeLightEntity.set(UndefinedEntity)
- }, [xrLightProbeEntity.value, renderSettingsComponent?.primaryLight?.value])
+ }, [xrLightProbeEntity.value, renderSettingsComponent.primaryLight.value])
- if (!renderSettingsComponent?.csm.value) return null
+ if (!renderSettingsComponent.csm.value) return null
if (!activeLightEntity.value) return
return (
-
+
)
}
@@ -432,7 +439,6 @@ const updateDropShadowTransforms = () => {
}
}
-const groupQuery = defineQuery([GroupComponent, VisibleComponent, ShadowComponent])
const rendererQuery = defineQuery([RendererComponent])
const execute = () => {
@@ -448,14 +454,7 @@ const execute = () => {
const { csm, csmHelper } = getComponent(entity, RendererComponent)
if (csm) {
csm.update()
- if (csmHelper) csmHelper.update(csm)
-
- /** hack fix to ensure CSM material is applied to all materials (which are not set reactively) */
- for (const entity of groupQuery()) {
- for (const obj of getComponent(entity, GroupComponent) as any as Mesh[]) {
- if (obj.material && obj.receiveShadow) csm.setupMaterial(obj)
- }
- }
+ //if (csmHelper) csmHelper.update(csm)
}
}
}
@@ -489,7 +488,7 @@ const reactor = () => {
return (
<>
{useShadows ? (
-
+
) : (
)}
diff --git a/packages/spatial/src/renderer/csm/CSM.ts b/packages/spatial/src/renderer/csm/CSM.ts
index d1ea35f04b..32e7a338db 100644
--- a/packages/spatial/src/renderer/csm/CSM.ts
+++ b/packages/spatial/src/renderer/csm/CSM.ts
@@ -25,6 +25,7 @@ Ethereal Engine. All Rights Reserved.
import {
Box3,
+ ColorRepresentation,
DirectionalLight,
Material,
MathUtils,
@@ -41,17 +42,15 @@ import { getComponent, setComponent } from '@etherealengine/ecs/src/ComponentFun
import { Engine } from '@etherealengine/ecs/src/Engine'
import { Entity } from '@etherealengine/ecs/src/Entity'
import { createEntity, removeEntity } from '@etherealengine/ecs/src/EntityFunctions'
-import { getState } from '@etherealengine/hyperflux'
import { CameraComponent } from '../../camera/components/CameraComponent'
+import { NameComponent } from '../../common/NameComponent'
import { Vector3_Zero } from '../../common/constants/MathConstants'
import { addOBCPlugin, removeOBCPlugin } from '../../common/functions/OnBeforeCompilePlugin'
-import { NameComponent } from '../../common/NameComponent'
import { addObjectToGroup } from '../../renderer/components/GroupComponent'
import { VisibleComponent } from '../../renderer/components/VisibleComponent'
import { EntityTreeComponent } from '../../transform/components/EntityTree'
import { TransformComponent } from '../../transform/components/TransformComponent'
-import { RendererState } from '../RendererState'
import Frustum from './Frustum'
import Shader from './Shader'
@@ -85,6 +84,7 @@ type CSMParams = {
lightDirection?: Vector3
lightDirectionUp?: Vector3
lightIntensity?: number
+ lightColor?: ColorRepresentation
lightNear?: number
lightFar?: number
lightMargin?: number
@@ -98,8 +98,10 @@ export class CSM {
mode: (typeof CSMModes)[keyof typeof CSMModes]
shadowBias: number
shadowNormalBias: number
+ shadowMapSize: number
lightDirection: Vector3
lightDirectionUp: Vector3
+ lightColor: ColorRepresentation
lightIntensity: number
lightMargin: number
customSplitsCallback?: (amount: number, near: number, far: number, target: number[]) => void
@@ -119,10 +121,12 @@ export class CSM {
this.cascades = data.cascades ?? 5
this.maxFar = data.maxFar ?? 100
this.mode = data.mode ?? CSMModes.PRACTICAL
+ this.shadowMapSize = data.shadowMapSize ?? 1024
this.shadowBias = data.shadowBias ?? 0
this.shadowNormalBias = 0
this.lightDirection = data.lightDirection ?? new Vector3(1, -1, 1).normalize()
this.lightDirectionUp = data.lightDirectionUp ?? Object3D.DEFAULT_UP
+ this.lightColor = data.lightColor ?? 0xffffff
this.lightIntensity = data.lightIntensity ?? 1
this.lightMargin = data.lightMargin ?? 200
this.customSplitsCallback = data.customSplitsCallback
@@ -164,6 +168,30 @@ export class CSM {
})
}
+ createLight(light: DirectionalLight, i: number): void {
+ light.castShadow = true
+ light.frustumCulled = false
+
+ light.shadow.mapSize.width = this.shadowMapSize
+ light.shadow.mapSize.height = this.shadowMapSize
+
+ light.shadow.camera.near = 0
+ light.shadow.camera.far = 1
+
+ light.intensity = this.lightIntensity
+
+ const entity = createEntity()
+ addObjectToGroup(entity, light)
+ setComponent(entity, NameComponent, 'CSM light ' + i)
+ setComponent(entity, VisibleComponent)
+ setComponent(entity, EntityTreeComponent, { parentEntity: Engine.instance.originEntity })
+
+ this.lightEntities.push(entity)
+ this.lights.push(light)
+ light.name = 'CSM_' + light.name
+ light.target.name = 'CSM_' + light.target.name
+ }
+
createLights(sourceLight?: DirectionalLight): void {
if (sourceLight) {
this.sourceLight = sourceLight
@@ -172,47 +200,15 @@ export class CSM {
for (let i = 0; i < this.cascades; i++) {
const light = sourceLight.clone()
- light.castShadow = true
- light.frustumCulled = false
-
- light.intensity = this.lightIntensity
-
- const entity = createEntity()
- addObjectToGroup(entity, light)
- setComponent(entity, NameComponent, 'CSM light ' + i)
- setComponent(entity, VisibleComponent)
- setComponent(entity, EntityTreeComponent, { parentEntity: Engine.instance.originEntity })
-
- this.lightEntities.push(entity)
- this.lights.push(light)
- light.name = 'CSM_' + sourceLight.name
- light.target.name = 'CSM_' + sourceLight.target.name
+ this.createLight(light, i)
}
return
}
// if no lights are provided, create default ones
-
for (let i = 0; i < this.cascades; i++) {
- const light = new DirectionalLight(0xffffff, this.lightIntensity)
-
- light.castShadow = true
-
- light.shadow.camera.near = 0
- light.shadow.camera.far = 1
-
- light.frustumCulled = false
-
- const entity = createEntity()
- addObjectToGroup(entity, light)
- setComponent(entity, NameComponent, 'CSM light ' + i)
- setComponent(entity, VisibleComponent)
- setComponent(entity, EntityTreeComponent, { parentEntity: Engine.instance.originEntity })
-
- this.lightEntities.push(entity)
- this.lights.push(light)
- light.name = 'CSM_' + light.name
- light.target.name = 'CSM_' + light.target.name
+ const light = new DirectionalLight(this.lightColor, this.lightIntensity)
+ this.createLight(light, i)
}
}
@@ -322,6 +318,7 @@ export class CSM {
update(): void {
if (this.sourceLight) this.lightDirection.subVectors(this.sourceLight.target.position, this.sourceLight.position)
if (this.needsUpdate) {
+ this.injectInclude()
this.updateFrustums()
for (const light of this.lights) {
light.shadow.map?.dispose()
@@ -338,10 +335,8 @@ export class CSM {
const frustum = frustums[i]
const shadowCam = light.shadow.camera
- const shadowMapSize = getState(RendererState).shadowMapResolution
-
- const texelWidth = (shadowCam.right - shadowCam.left) / shadowMapSize
- const texelHeight = (shadowCam.top - shadowCam.bottom) / shadowMapSize
+ const texelWidth = (shadowCam.right - shadowCam.left) / this.shadowMapSize
+ const texelHeight = (shadowCam.top - shadowCam.bottom) / this.shadowMapSize
// This matrix only represents sun orientation, origin is zero
_lightOrientationMatrix.lookAt(Vector3_Zero, this.lightDirection, this.lightDirectionUp)
@@ -490,6 +485,10 @@ export class CSM {
this.lightEntities.forEach((entity) => {
removeEntity(entity)
})
+ this.lights.forEach((light) => {
+ light.dispose()
+ })
+ this.lightEntities = []
this.lights = []
}
diff --git a/packages/spatial/src/transform/components/EntityTree.tsx b/packages/spatial/src/transform/components/EntityTree.tsx
index bd1fd8e720..e6d6222826 100644
--- a/packages/spatial/src/transform/components/EntityTree.tsx
+++ b/packages/spatial/src/transform/components/EntityTree.tsx
@@ -432,7 +432,11 @@ export function useAncestorWithComponent(entity: Entity, component: ComponentTyp
return result.value
}
-export function useChildWithComponent(entity: Entity, component: ComponentType) {
+/**
+ * @todo - return an array of entities that have the component
+ *
+ */
+export function useChildWithComponent(rootEntity: Entity, component: ComponentType) {
const result = useHookstate(UndefinedEntity)
useLayoutEffect(() => {
@@ -463,13 +467,23 @@ export function useChildWithComponent(entity: Entity, component: ComponentType
+ const isScene = useOptionalComponent(rootEntity, SceneComponent)
+ if (isScene) {
+ return (
+ <>
+ {isScene.children.value.map((entity) => (
+
+ ))}
+ >
+ )
+ }
+ return
})
return () => {
unmounted = true
root.stop()
}
- }, [entity, component])
+ }, [rootEntity, component])
return result.value
}