@@ -4,7 +4,7 @@ use bevy_color::ColorToComponents;
4
4
use bevy_core_pipeline:: core_3d:: { Camera3d , CORE_3D_DEPTH_FORMAT } ;
5
5
use bevy_derive:: { Deref , DerefMut } ;
6
6
use bevy_ecs:: {
7
- entity:: { EntityHashMap , EntityHashSet } ,
7
+ entity:: { EntityHash , EntityHashMap , EntityHashSet } ,
8
8
prelude:: * ,
9
9
system:: lifetimeless:: Read ,
10
10
} ;
@@ -459,7 +459,9 @@ fn create_render_visible_mesh_entities(
459
459
}
460
460
461
461
#[ derive( Component , Default , Deref , DerefMut ) ]
462
- pub struct LightViewEntities ( Vec < Entity > ) ;
462
+ /// Component automatically attached to a light entity to track light-view entities
463
+ /// for each view.
464
+ pub struct LightViewEntities ( EntityHashMap < Vec < Entity > > ) ;
463
465
464
466
// TODO: using required component
465
467
pub ( crate ) fn add_light_view_entities (
@@ -477,9 +479,11 @@ pub(crate) fn remove_light_view_entities(
477
479
mut commands : Commands ,
478
480
) {
479
481
if let Ok ( entities) = query. get ( trigger. entity ( ) ) {
480
- for e in entities. 0 . iter ( ) . copied ( ) {
481
- if let Some ( mut v) = commands. get_entity ( e) {
482
- v. despawn ( ) ;
482
+ for v in entities. 0 . values ( ) {
483
+ for e in v. iter ( ) . copied ( ) {
484
+ if let Some ( mut v) = commands. get_entity ( e) {
485
+ v. despawn ( ) ;
486
+ }
483
487
}
484
488
}
485
489
}
@@ -731,7 +735,8 @@ pub fn prepare_lights(
731
735
let point_light_count = point_lights
732
736
. iter ( )
733
737
. filter ( |light| light. 1 . spot_light_angles . is_none ( ) )
734
- . count ( ) ;
738
+ . count ( )
739
+ . min ( max_texture_cubes) ;
735
740
736
741
let point_light_volumetric_enabled_count = point_lights
737
742
. iter ( )
@@ -759,6 +764,12 @@ pub fn prepare_lights(
759
764
. count ( )
760
765
. min ( max_texture_array_layers / MAX_CASCADES_PER_LIGHT ) ;
761
766
767
+ let spot_light_count = point_lights
768
+ . iter ( )
769
+ . filter ( |( _, light, _) | light. spot_light_angles . is_some ( ) )
770
+ . count ( )
771
+ . min ( max_texture_array_layers - directional_shadow_enabled_count * MAX_CASCADES_PER_LIGHT ) ;
772
+
762
773
let spot_light_volumetric_enabled_count = point_lights
763
774
. iter ( )
764
775
. filter ( |( _, light, _) | light. volumetric && light. spot_light_angles . is_some ( ) )
@@ -956,9 +967,12 @@ pub fn prepare_lights(
956
967
957
968
live_shadow_mapping_lights. clear ( ) ;
958
969
959
- let mut dir_light_view_offset = 0 ;
970
+ let mut live_views = EntityHashSet :: with_capacity_and_hasher ( views_count, EntityHash ) ;
971
+
960
972
// set up light data for each view
961
- for ( offset, ( entity, extracted_view, clusters, maybe_layers) ) in views. iter ( ) . enumerate ( ) {
973
+ for ( entity, extracted_view, clusters, maybe_layers) in views. iter ( ) {
974
+ live_views. insert ( entity) ;
975
+
962
976
let point_light_depth_texture = texture_cache. get (
963
977
& render_device,
964
978
TextureDescriptor {
@@ -1032,9 +1046,19 @@ pub fn prepare_lights(
1032
1046
for & ( light_entity, light, ( point_light_frusta, _) ) in point_lights
1033
1047
. iter ( )
1034
1048
// Lights are sorted, shadow enabled lights are first
1035
- . take ( point_light_shadow_maps_count)
1036
- . filter ( |( _, light, _) | light. shadows_enabled )
1049
+ . take ( point_light_count)
1037
1050
{
1051
+ let Ok ( mut light_view_entities) = light_view_entities. get_mut ( light_entity) else {
1052
+ continue ;
1053
+ } ;
1054
+
1055
+ if !light. shadows_enabled {
1056
+ if let Some ( entities) = light_view_entities. remove ( & entity) {
1057
+ despawn_entities ( & mut commands, entities) ;
1058
+ }
1059
+ continue ;
1060
+ }
1061
+
1038
1062
let light_index = * global_light_meta
1039
1063
. entity_to_index
1040
1064
. get ( & light_entity)
@@ -1044,14 +1068,10 @@ pub fn prepare_lights(
1044
1068
// and ignore rotation because we want the shadow map projections to align with the axes
1045
1069
let view_translation = GlobalTransform :: from_translation ( light. transform . translation ( ) ) ;
1046
1070
1047
- let Ok ( mut light_entities) = light_view_entities. get_mut ( light_entity) else {
1048
- continue ;
1049
- } ;
1050
-
1051
1071
// for each face of a cube and each view we spawn a light entity
1052
- while light_entities . len ( ) < 6 * ( offset + 1 ) {
1053
- light_entities . push ( commands . spawn_empty ( ) . id ( ) ) ;
1054
- }
1072
+ let light_view_entities = light_view_entities
1073
+ . entry ( entity )
1074
+ . or_insert_with ( || ( 0 .. 6 ) . map ( |_| commands . spawn_empty ( ) . id ( ) ) . collect ( ) ) ;
1055
1075
1056
1076
let cube_face_projection = Mat4 :: perspective_infinite_reverse_rh (
1057
1077
core:: f32:: consts:: FRAC_PI_2 ,
@@ -1062,7 +1082,7 @@ pub fn prepare_lights(
1062
1082
for ( face_index, ( ( view_rotation, frustum) , view_light_entity) ) in cube_face_rotations
1063
1083
. iter ( )
1064
1084
. zip ( & point_light_frusta. unwrap ( ) . frusta )
1065
- . zip ( light_entities . iter ( ) . skip ( 6 * offset ) . copied ( ) )
1085
+ . zip ( light_view_entities . iter ( ) . copied ( ) )
1066
1086
. enumerate ( )
1067
1087
{
1068
1088
let depth_texture_view =
@@ -1119,16 +1139,23 @@ pub fn prepare_lights(
1119
1139
for ( light_index, & ( light_entity, light, ( _, spot_light_frustum) ) ) in point_lights
1120
1140
. iter ( )
1121
1141
. skip ( point_light_count)
1122
- . take ( spot_light_shadow_maps_count )
1142
+ . take ( spot_light_count )
1123
1143
. enumerate ( )
1124
1144
{
1125
- let spot_world_from_view = spot_light_world_from_view ( & light. transform ) ;
1126
- let spot_world_from_view = spot_world_from_view. into ( ) ;
1127
-
1128
1145
let Ok ( mut light_view_entities) = light_view_entities. get_mut ( light_entity) else {
1129
1146
continue ;
1130
1147
} ;
1131
1148
1149
+ if !light. shadows_enabled {
1150
+ if let Some ( entities) = light_view_entities. remove ( & entity) {
1151
+ despawn_entities ( & mut commands, entities) ;
1152
+ }
1153
+ continue ;
1154
+ }
1155
+
1156
+ let spot_world_from_view = spot_light_world_from_view ( & light. transform ) ;
1157
+ let spot_world_from_view = spot_world_from_view. into ( ) ;
1158
+
1132
1159
let angle = light. spot_light_angles . expect ( "lights should be sorted so that \
1133
1160
[point_light_count..point_light_count + spot_light_shadow_maps_count] are spot lights") . 1 ;
1134
1161
let spot_projection = spot_light_clip_from_view ( angle, light. shadow_map_near_z ) ;
@@ -1147,11 +1174,11 @@ pub fn prepare_lights(
1147
1174
array_layer_count : Some ( 1u32 ) ,
1148
1175
} ) ;
1149
1176
1150
- while light_view_entities. len ( ) < offset + 1 {
1151
- light_view_entities . push ( commands . spawn_empty ( ) . id ( ) ) ;
1152
- }
1177
+ let light_view_entities = light_view_entities
1178
+ . entry ( entity )
1179
+ . or_insert_with ( || vec ! [ commands . spawn_empty ( ) . id ( ) ] ) ;
1153
1180
1154
- let view_light_entity = light_view_entities[ offset ] ;
1181
+ let view_light_entity = light_view_entities[ 0 ] ;
1155
1182
1156
1183
commands. entity ( view_light_entity) . insert ( (
1157
1184
ShadowView {
@@ -1194,14 +1221,21 @@ pub fn prepare_lights(
1194
1221
let Ok ( mut light_view_entities) = light_view_entities. get_mut ( light_entity) else {
1195
1222
continue ;
1196
1223
} ;
1224
+
1197
1225
// Check if the light intersects with the view.
1198
1226
if !view_layers. intersects ( & light. render_layers ) {
1199
1227
gpu_light. skip = 1u32 ;
1228
+ if let Some ( entities) = light_view_entities. remove ( & entity) {
1229
+ despawn_entities ( & mut commands, entities) ;
1230
+ }
1200
1231
continue ;
1201
1232
}
1202
1233
1203
1234
// Only deal with cascades when shadows are enabled.
1204
1235
if ( gpu_light. flags & DirectionalLightFlags :: SHADOWS_ENABLED . bits ( ) ) == 0u32 {
1236
+ if let Some ( entities) = light_view_entities. remove ( & entity) {
1237
+ despawn_entities ( & mut commands, entities) ;
1238
+ }
1205
1239
continue ;
1206
1240
}
1207
1241
@@ -1222,18 +1256,19 @@ pub fn prepare_lights(
1222
1256
. zip ( frusta)
1223
1257
. zip ( & light. cascade_shadow_config . bounds ) ;
1224
1258
1225
- while light_view_entities. len ( ) < dir_light_view_offset + iter. len ( ) {
1226
- light_view_entities. push ( commands. spawn_empty ( ) . id ( ) ) ;
1259
+ let light_view_entities = light_view_entities. entry ( entity) . or_insert_with ( || {
1260
+ ( 0 ..iter. len ( ) )
1261
+ . map ( |_| commands. spawn_empty ( ) . id ( ) )
1262
+ . collect ( )
1263
+ } ) ;
1264
+ if light_view_entities. len ( ) != iter. len ( ) {
1265
+ let entities = core:: mem:: take ( light_view_entities) ;
1266
+ despawn_entities ( & mut commands, entities) ;
1267
+ light_view_entities. extend ( ( 0 ..iter. len ( ) ) . map ( |_| commands. spawn_empty ( ) . id ( ) ) ) ;
1227
1268
}
1228
1269
1229
- for ( cascade_index, ( ( ( cascade, frustum) , bound) , view_light_entity) ) in iter
1230
- . zip (
1231
- light_view_entities
1232
- . iter ( )
1233
- . skip ( dir_light_view_offset)
1234
- . copied ( ) ,
1235
- )
1236
- . enumerate ( )
1270
+ for ( cascade_index, ( ( ( cascade, frustum) , bound) , view_light_entity) ) in
1271
+ iter. zip ( light_view_entities. iter ( ) . copied ( ) ) . enumerate ( )
1237
1272
{
1238
1273
gpu_lights. directional_lights [ light_index] . cascades [ cascade_index] =
1239
1274
GpuDirectionalCascade {
@@ -1292,7 +1327,6 @@ pub fn prepare_lights(
1292
1327
1293
1328
shadow_render_phases. insert_or_clear ( view_light_entity) ;
1294
1329
live_shadow_mapping_lights. insert ( view_light_entity) ;
1295
- dir_light_view_offset += 1 ;
1296
1330
}
1297
1331
}
1298
1332
@@ -1360,9 +1394,29 @@ pub fn prepare_lights(
1360
1394
) ) ;
1361
1395
}
1362
1396
1397
+ // Despawn light-view entities for views that no longer exist
1398
+ for mut entities in & mut light_view_entities {
1399
+ for ( _, light_view_entities) in
1400
+ entities. extract_if ( |entity, _| !live_views. contains ( entity) )
1401
+ {
1402
+ despawn_entities ( & mut commands, light_view_entities) ;
1403
+ }
1404
+ }
1405
+
1363
1406
shadow_render_phases. retain ( |entity, _| live_shadow_mapping_lights. contains ( entity) ) ;
1364
1407
}
1365
1408
1409
+ fn despawn_entities ( commands : & mut Commands , entities : Vec < Entity > ) {
1410
+ if entities. is_empty ( ) {
1411
+ return ;
1412
+ }
1413
+ commands. queue ( move |world : & mut World | {
1414
+ for entity in entities {
1415
+ world. despawn ( entity) ;
1416
+ }
1417
+ } ) ;
1418
+ }
1419
+
1366
1420
/// For each shadow cascade, iterates over all the meshes "visible" from it and
1367
1421
/// adds them to [`BinnedRenderPhase`]s or [`SortedRenderPhase`]s as
1368
1422
/// appropriate.
0 commit comments