@@ -13,8 +13,8 @@ import InputSystem from "../input/InputSystem"
13
13
import Jolt from "@barclah/jolt-physics"
14
14
15
15
import { PixelSpaceCoord , SceneOverlayEvent , SceneOverlayEventKey } from "@/ui/components/SceneOverlayEvents"
16
- import { } from "@/ui/components/SceneOverlayEvents"
17
16
import PreferencesSystem from "../preferences/PreferencesSystem"
17
+ import { CSM } from "three/examples/jsm/csm/CSM.js"
18
18
19
19
const CLEAR_COLOR = 0x121212
20
20
const GROUND_COLOR = 0x4066c7
@@ -35,6 +35,8 @@ class SceneRenderer extends WorldSystem {
35
35
private _orbitControls : OrbitControls
36
36
private _transformControls : Map < TransformControls , number > // maps all rendered transform controls to their size
37
37
38
+ private _light : THREE . DirectionalLight | CSM | undefined
39
+
38
40
public get sceneObjects ( ) {
39
41
return this . _sceneObjects
40
42
}
@@ -75,25 +77,10 @@ class SceneRenderer extends WorldSystem {
75
77
this . _renderer . shadowMap . type = THREE . PCFSoftShadowMap
76
78
this . _renderer . setSize ( window . innerWidth , window . innerHeight )
77
79
78
- const directionalLight = new THREE . DirectionalLight ( 0xffffff , 3.0 )
79
- directionalLight . position . set ( - 1.0 , 3.0 , 2.0 )
80
- directionalLight . castShadow = true
81
- this . _scene . add ( directionalLight )
80
+ // Adding the lighting uisng quality preferences
81
+ this . ChangeLighting ( PreferencesSystem . getGlobalPreference < string > ( "QualitySettings" ) )
82
82
83
- const shadowMapSize = Math . min ( 4096 , this . _renderer . capabilities . maxTextureSize )
84
- const shadowCamSize = 15
85
- console . debug ( `Shadow Map Size: ${ shadowMapSize } ` )
86
-
87
- directionalLight . shadow . camera . top = shadowCamSize
88
- directionalLight . shadow . camera . bottom = - shadowCamSize
89
- directionalLight . shadow . camera . left = - shadowCamSize
90
- directionalLight . shadow . camera . right = shadowCamSize
91
- directionalLight . shadow . mapSize = new THREE . Vector2 ( shadowMapSize , shadowMapSize )
92
- directionalLight . shadow . blurSamples = 16
93
- directionalLight . shadow . normalBias = 0.01
94
- directionalLight . shadow . bias = 0.0
95
-
96
- const ambientLight = new THREE . AmbientLight ( 0xffffff , 0.1 )
83
+ const ambientLight = new THREE . AmbientLight ( 0xffffff , 0.3 )
97
84
this . _scene . add ( ambientLight )
98
85
99
86
const ground = new THREE . Mesh ( new THREE . BoxGeometry ( 10 , 1 , 10 ) , this . CreateToonMaterial ( GROUND_COLOR ) )
@@ -145,6 +132,11 @@ class SceneRenderer extends WorldSystem {
145
132
obj . Update ( )
146
133
} )
147
134
135
+ this . _mainCamera . updateMatrixWorld ( )
136
+
137
+ // updating the CSM light if it is enabled
138
+ if ( this . _light instanceof CSM ) this . _light . update ( )
139
+
148
140
this . _skybox . position . copy ( this . _mainCamera . position )
149
141
150
142
const mainCameraFovRadians = ( Math . PI * ( this . _mainCamera . fov * 0.5 ) ) / 180
@@ -167,6 +159,75 @@ class SceneRenderer extends WorldSystem {
167
159
this . RemoveAllSceneObjects ( )
168
160
}
169
161
162
+ /**
163
+ * Changes the quality of lighting between cascading shadows and directional lights
164
+ *
165
+ * @param quality: string representing the quality of lighting - "Low", "Medium", "High"
166
+ */
167
+ public ChangeLighting ( quality : string ) : void {
168
+ // removing the previous lighting method
169
+ if ( this . _light instanceof THREE . DirectionalLight ) {
170
+ this . _scene . remove ( this . _light )
171
+ } else if ( this . _light instanceof CSM ) {
172
+ this . _light . dispose ( )
173
+ this . _light . remove ( )
174
+ }
175
+
176
+ // setting the shadow map size
177
+ const shadowMapSize = Math . min ( 4096 , this . _renderer . capabilities . maxTextureSize )
178
+
179
+ // setting the light to a basic directional light
180
+ if ( quality === "Low" || quality === "Medium" ) {
181
+ const shadowCamSize = 15
182
+
183
+ this . _light = new THREE . DirectionalLight ( 0xffffff , 5.0 )
184
+ this . _light . position . set ( - 1.0 , 3.0 , 2.0 )
185
+ this . _light . castShadow = true
186
+ this . _light . shadow . camera . top = shadowCamSize
187
+ this . _light . shadow . camera . bottom = - shadowCamSize
188
+ this . _light . shadow . camera . left = - shadowCamSize
189
+ this . _light . shadow . camera . right = shadowCamSize
190
+ this . _light . shadow . mapSize = new THREE . Vector2 ( shadowMapSize , shadowMapSize )
191
+ this . _light . shadow . blurSamples = 16
192
+ this . _light . shadow . bias = 0.0
193
+ this . _light . shadow . normalBias = 0.01
194
+ this . _scene . add ( this . _light )
195
+ } else if ( quality === "High" ) {
196
+ // setting light to cascading shadows
197
+ this . _light = new CSM ( {
198
+ parent : this . _scene ,
199
+ camera : this . _mainCamera ,
200
+ cascades : 4 ,
201
+ lightDirection : new THREE . Vector3 ( 1.0 , - 3.0 , - 2.0 ) . normalize ( ) ,
202
+ lightIntensity : 5 ,
203
+ shadowMapSize : shadowMapSize ,
204
+ mode : "custom" ,
205
+ maxFar : 30 ,
206
+ shadowBias : - 0.00001 ,
207
+ customSplitsCallback : ( cascades : number , near : number , far : number , breaks : number [ ] ) => {
208
+ const blend = 0.7
209
+ for ( let i = 1 ; i < cascades ; i ++ ) {
210
+ const uniformFactor = ( near + ( ( far - near ) * i ) / cascades ) / far
211
+ const logarithmicFactor = ( near * ( far / near ) ** ( i / cascades ) ) / far
212
+ const combinedFactor = uniformFactor * ( 1 - blend ) + logarithmicFactor * blend
213
+
214
+ breaks . push ( combinedFactor )
215
+ }
216
+
217
+ breaks . push ( 1 )
218
+ } ,
219
+ } )
220
+
221
+ // setting up the materials for all objects in the scene
222
+ this . _light . fade = true
223
+ this . _scene . children . forEach ( child => {
224
+ if ( child instanceof THREE . Mesh ) {
225
+ if ( this . _light instanceof CSM ) this . _light . setupMaterial ( child . material )
226
+ }
227
+ } )
228
+ }
229
+ }
230
+
170
231
public RegisterSceneObject < T extends SceneObject > ( obj : T ) : number {
171
232
const id = nextSceneObjectId ++
172
233
obj . id = id
@@ -190,6 +251,7 @@ class SceneRenderer extends WorldSystem {
190
251
public CreateSphere ( radius : number , material ?: THREE . Material | undefined ) : THREE . Mesh {
191
252
const geo = new THREE . SphereGeometry ( radius )
192
253
if ( material ) {
254
+ if ( this . _light instanceof CSM ) this . _light . setupMaterial ( material )
193
255
return new THREE . Mesh ( geo , material )
194
256
} else {
195
257
return new THREE . Mesh ( geo , this . CreateToonMaterial ( ) )
@@ -213,10 +275,13 @@ class SceneRenderer extends WorldSystem {
213
275
}
214
276
const gradientMap = new THREE . DataTexture ( colors , colors . length , 1 , format )
215
277
gradientMap . needsUpdate = true
216
- return new THREE . MeshToonMaterial ( {
278
+ const material = new THREE . MeshToonMaterial ( {
217
279
color : color ,
280
+ shadowSide : THREE . DoubleSide ,
218
281
gradientMap : gradientMap ,
219
282
} )
283
+ if ( this . _light instanceof CSM ) this . _light . setupMaterial ( material )
284
+ return material
220
285
}
221
286
222
287
/**
@@ -249,7 +314,7 @@ class SceneRenderer extends WorldSystem {
249
314
return [ ( window . innerWidth * ( screenSpace . x + 1.0 ) ) / 2.0 , ( window . innerHeight * ( 1.0 - screenSpace . y ) ) / 2.0 ]
250
315
}
251
316
252
- /**
317
+ /**
253
318
* Updates the skybox colors based on the current theme
254
319
255
320
* @param currentTheme: current theme from ThemeContext.useTheme()
@@ -359,6 +424,15 @@ class SceneRenderer extends WorldSystem {
359
424
public RemoveObject ( obj : THREE . Object3D ) {
360
425
this . _scene . remove ( obj )
361
426
}
427
+
428
+ /**
429
+ * Sets up the threejs material for cascading shadows if the CSM is enabled
430
+ *
431
+ * @param material
432
+ */
433
+ public SetupMaterial ( material : THREE . Material ) {
434
+ if ( this . _light instanceof CSM ) this . _light . setupMaterial ( material )
435
+ }
362
436
}
363
437
364
438
export default SceneRenderer
0 commit comments