Split AmbientLight into two (#21595)

# Objective

For resources-as-components (#19731), structs mustn't doubly derive both
`Component` and `Resource`.

## Solution

Split `AmbientLight` in two: `AmbientLight` (the resource) and
`AmbientLightOverride` (the component).

## Testing

I initially made two structs `AmbientLightComponent` and
`AmbientLightResource`, and replaced every mention with the relevant one
to ensure that I didn't confuse the two.

## Notes

- I don't know if the names are correct. I kept the easiest name for the
resource, as that's the one most often used.
- I haven't provided any conversion methods as there are already plans
to replace the component variant with something else.

---------

Co-authored-by: atlv <email@atlasdostal.com>
This commit is contained in:
Trashtalk217
2025-10-22 01:04:39 +02:00
committed by GitHub
parent 78d940cbfe
commit d431f7e139
32 changed files with 127 additions and 59 deletions
+54 -21
View File
@@ -5,25 +5,9 @@ use bevy_reflect::prelude::*;
/// An ambient light, which lights the entire scene equally.
///
/// This resource is inserted by the [`LightPlugin`] and by default it is set to a low ambient light.
///
/// It can also be added to a camera to override the resource (or default) ambient for that camera only.
///
/// # Examples
///
/// Make ambient light slightly brighter:
///
/// ```
/// # use bevy_ecs::system::ResMut;
/// # use bevy_light::AmbientLight;
/// fn setup_ambient_light(mut ambient_light: ResMut<AmbientLight>) {
/// ambient_light.brightness = 100.0;
/// }
/// ```
///
/// [`LightPlugin`]: crate::LightPlugin
#[derive(Resource, Component, Clone, Debug, Reflect)]
#[reflect(Resource, Component, Debug, Default, Clone)]
/// It can be added to a camera to override [`GlobalAmbientLight`], which is the default that is otherwise used.
#[derive(Component, Clone, Debug, Reflect)]
#[reflect(Component, Debug, Default, Clone)]
#[require(Camera)]
pub struct AmbientLight {
pub color: Color,
@@ -54,8 +38,57 @@ impl Default for AmbientLight {
}
}
impl AmbientLight {
pub const NONE: AmbientLight = AmbientLight {
/// The global ambient light, which lights the entire scene equally.
///
/// This resource is inserted by the [`LightPlugin`] and by default it is set to a low ambient light.
/// Inserting an [`AmbientLight`] on a camera will override this default.
///
/// # Examples
///
/// Make ambient light slightly brighter:
///
/// ```
/// # use bevy_ecs::system::ResMut;
/// # use bevy_light::GlobalAmbientLight;
/// fn setup_ambient_light(mut ambient_light: ResMut<GlobalAmbientLight>) {
/// ambient_light.brightness = 100.0;
/// }
/// ```
///
/// [`LightPlugin`]: crate::LightPlugin
#[derive(Resource, Clone, Debug, Reflect)]
#[reflect(Resource, Debug, Default, Clone)]
pub struct GlobalAmbientLight {
pub color: Color,
/// A direct scale factor multiplied with `color` before being passed to the shader.
///
/// After applying this multiplier, the resulting value should be in units of [cd/m^2].
///
/// [cd/m^2]: https://en.wikipedia.org/wiki/Candela_per_square_metre
pub brightness: f32,
/// Whether this ambient light has an effect on meshes with lightmaps.
///
/// Set this to false if your lightmap baking tool bakes the ambient light
/// into the lightmaps, to avoid rendering that light twice.
///
/// By default, this is set to true.
pub affects_lightmapped_meshes: bool,
}
impl Default for GlobalAmbientLight {
fn default() -> Self {
Self {
color: Color::WHITE,
brightness: 80.0,
affects_lightmapped_meshes: true,
}
}
}
impl GlobalAmbientLight {
pub const NONE: GlobalAmbientLight = GlobalAmbientLight {
color: Color::WHITE,
brightness: 0.0,
affects_lightmapped_meshes: true,
+3 -3
View File
@@ -25,7 +25,7 @@ use cluster::{
VisibleClusterableObjects,
};
mod ambient_light;
pub use ambient_light::AmbientLight;
pub use ambient_light::{AmbientLight, GlobalAmbientLight};
mod probe;
pub use probe::{
AtmosphereEnvironmentMapLight, EnvironmentMapLight, GeneratedEnvironmentMapLight,
@@ -58,7 +58,7 @@ pub mod prelude {
#[doc(hidden)]
pub use crate::{
light_consts, AmbientLight, DirectionalLight, EnvironmentMapLight,
GeneratedEnvironmentMapLight, LightProbe, PointLight, SpotLight,
GeneratedEnvironmentMapLight, GlobalAmbientLight, LightProbe, PointLight, SpotLight,
};
}
@@ -133,7 +133,7 @@ pub struct LightPlugin;
impl Plugin for LightPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<GlobalVisibleClusterableObjects>()
.init_resource::<AmbientLight>()
.init_resource::<GlobalAmbientLight>()
.init_resource::<DirectionalLightShadowMap>()
.init_resource::<PointLightShadowMap>()
.configure_sets(
+10 -5
View File
@@ -25,8 +25,8 @@ use bevy_light::cluster::GlobalVisibleClusterableObjects;
use bevy_light::SunDisk;
use bevy_light::{
spot_light_clip_from_view, spot_light_world_from_view, AmbientLight, CascadeShadowConfig,
Cascades, DirectionalLight, DirectionalLightShadowMap, NotShadowCaster, PointLight,
PointLightShadowMap, ShadowFilteringMethod, SpotLight, VolumetricLight,
Cascades, DirectionalLight, DirectionalLightShadowMap, GlobalAmbientLight, NotShadowCaster,
PointLight, PointLightShadowMap, ShadowFilteringMethod, SpotLight, VolumetricLight,
};
use bevy_math::{ops, Mat4, UVec4, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles};
use bevy_platform::collections::{HashMap, HashSet};
@@ -248,8 +248,8 @@ pub fn extract_shadow_filtering_method(
// foreign trait ExtractResource on foreign type AmbientLight
pub fn extract_ambient_light_resource(
mut commands: Commands,
main_resource: Extract<Option<Res<AmbientLight>>>,
target_resource: Option<ResMut<AmbientLight>>,
main_resource: Extract<Option<Res<GlobalAmbientLight>>>,
target_resource: Option<ResMut<GlobalAmbientLight>>,
) {
if let Some(main_resource) = main_resource.as_ref() {
if let Some(mut target_resource) = target_resource {
@@ -720,7 +720,7 @@ pub fn prepare_lights(
),
With<Camera3d>,
>,
ambient_light: Res<AmbientLight>,
ambient_light: Res<GlobalAmbientLight>,
point_light_shadow_map: Res<PointLightShadowMap>,
directional_light_shadow_map: Res<DirectionalLightShadowMap>,
mut shadow_render_phases: ResMut<ViewBinnedRenderPhases<Shadow>>,
@@ -1150,6 +1150,11 @@ pub fn prepare_lights(
);
let n_clusters = clusters.dimensions.x * clusters.dimensions.y * clusters.dimensions.z;
let ambient_light = AmbientLight {
color: ambient_light.color,
brightness: ambient_light.brightness,
affects_lightmapped_meshes: ambient_light.affects_lightmapped_meshes,
};
let ambient_light = maybe_ambient_override.unwrap_or(&ambient_light);
let mut gpu_directional_lights = [GpuDirectionalLight::default(); MAX_DIRECTIONAL_LIGHTS];
+1 -1
View File
@@ -97,7 +97,7 @@ fn setup(
}
}
commands.insert_resource(AmbientLight {
commands.insert_resource(GlobalAmbientLight {
color: Color::WHITE,
brightness: 0.0,
..default()
+1 -1
View File
@@ -29,7 +29,7 @@ use bevy::{
fn main() {
App::new()
.insert_resource(AmbientLight::NONE)
.insert_resource(GlobalAmbientLight::NONE)
.add_plugins(DefaultPlugins)
.add_systems(
Startup,
+1 -1
View File
@@ -22,7 +22,7 @@ fn main() {
}),
..default()
}))
.insert_resource(AmbientLight::NONE)
.insert_resource(GlobalAmbientLight::NONE)
.add_systems(Startup, setup)
.add_systems(Update, rotate_camera)
.run();
+2 -2
View File
@@ -157,7 +157,7 @@ fn main() {
.add_plugins(MaterialPlugin::<VoxelVisualizationMaterial>::default())
.init_resource::<AppStatus>()
.init_resource::<ExampleAssets>()
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
color: Color::WHITE,
brightness: 0.0,
..default()
@@ -414,7 +414,7 @@ fn toggle_irradiance_volumes(
light_probe_query: Query<Entity, With<LightProbe>>,
mut app_status: ResMut<AppStatus>,
assets: Res<ExampleAssets>,
mut ambient_light: ResMut<AmbientLight>,
mut ambient_light: ResMut<GlobalAmbientLight>,
) {
if !keyboard.just_pressed(KeyCode::Space) {
return;
+2 -2
View File
@@ -120,7 +120,7 @@ fn setup(
// ambient light
// ambient lights' brightnesses are measured in candela per meter square, calculable as (color * brightness)
commands.insert_resource(AmbientLight {
commands.insert_resource(GlobalAmbientLight {
color: ORANGE_RED.into(),
brightness: 200.0,
..default()
@@ -290,7 +290,7 @@ fn update_exposure(
fn toggle_ambient_light(
key_input: Res<ButtonInput<KeyCode>>,
mut ambient_light: ResMut<AmbientLight>,
mut ambient_light: ResMut<GlobalAmbientLight>,
text: Single<Entity, With<Text>>,
mut writer: TextUiWriter,
) {
+1 -1
View File
@@ -27,7 +27,7 @@ fn main() {
let mut app = App::new();
app.add_plugins(DefaultPlugins)
.insert_resource(AmbientLight::NONE);
.insert_resource(GlobalAmbientLight::NONE);
if args.deferred {
app.insert_resource(DefaultOpaqueRendererMethod::deferred());
+1 -1
View File
@@ -124,7 +124,7 @@ fn main() {
..default()
}))
.add_plugins(MeshPickingPlugin)
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
color: ClearColor::default().0,
brightness: 10000.0,
affects_lightmapped_meshes: true,
+1 -1
View File
@@ -57,7 +57,7 @@ fn setup_scene(
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.insert_resource(AmbientLight {
commands.insert_resource(GlobalAmbientLight {
color: Color::WHITE,
brightness: 300.0,
..default()
+1 -1
View File
@@ -85,7 +85,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// ambient light
// NOTE: The ambient light is used to scale how bright the environment map is so with a bright
// environment map, use an appropriate color and brightness to match
commands.insert_resource(AmbientLight {
commands.insert_resource(GlobalAmbientLight {
color: Color::srgb_u8(210, 220, 240),
brightness: 1.0,
..default()
+1 -1
View File
@@ -59,7 +59,7 @@ fn main() {
}))
.init_resource::<AppAssets>()
.init_resource::<AppStatus>()
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
color: Color::BLACK,
brightness: 0.0,
..default()
+1 -1
View File
@@ -4,7 +4,7 @@ use bevy::prelude::*;
fn main() {
App::new()
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
brightness: 60.0,
..default()
})
+1 -1
View File
@@ -21,7 +21,7 @@ Rotate Camera: Left and Right Arrows";
fn main() {
App::new()
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
brightness: 20.0,
..default()
})
+1 -1
View File
@@ -11,7 +11,7 @@ use std::f32::consts::PI;
fn main() {
App::new()
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
brightness: 1000.,
..default()
})
+1 -1
View File
@@ -47,7 +47,7 @@ fn main() {
.add_plugins(DefaultPlugins)
.insert_resource(ClearColor(Color::BLACK))
.insert_resource(PointLightShadowMap { size: 2048 })
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
brightness: 0.0,
..default()
})
+1 -1
View File
@@ -47,7 +47,7 @@ fn main() {
blue: 0.02,
alpha: 1.0,
})))
.insert_resource(AmbientLight::NONE)
.insert_resource(GlobalAmbientLight::NONE)
.init_resource::<AppSettings>()
.add_systems(Startup, setup)
.add_systems(Update, tweak_scene)
+1 -1
View File
@@ -9,7 +9,7 @@ const GLTF_PATH: &str = "models/animated/Fox.glb";
fn main() {
App::new()
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
color: Color::WHITE,
brightness: 2000.,
..default()
+1 -1
View File
@@ -8,7 +8,7 @@ const FOX_PATH: &str = "models/animated/Fox.glb";
fn main() {
App::new()
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
color: Color::WHITE,
brightness: 2000.,
..default()
+1 -1
View File
@@ -15,7 +15,7 @@ const FOX_PATH: &str = "models/animated/Fox.glb";
fn main() {
App::new()
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
color: Color::WHITE,
brightness: 2000.,
..default()
+1 -1
View File
@@ -10,7 +10,7 @@ use bevy::{
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
color: Color::WHITE,
brightness: 150.0,
..default()
+1 -1
View File
@@ -88,7 +88,7 @@ fn main() {
(handle_weight_drag, update_ui, sync_weights).chain(),
)
.insert_resource(args)
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
color: WHITE.into(),
brightness: 100.0,
..default()
+1 -1
View File
@@ -105,7 +105,7 @@ fn main() {
.add_systems(Update, setup_animation_graph_once_loaded)
.add_systems(Update, handle_button_toggles)
.add_systems(Update, update_ui)
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
color: WHITE.into(),
brightness: 100.0,
..default()
+1 -1
View File
@@ -18,7 +18,7 @@ use rand_chacha::ChaCha8Rng;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
brightness: 3000.0,
..default()
})
+1 -1
View File
@@ -10,7 +10,7 @@ const GLTF_PATH: &str = "models/animated/MorphStressTest.gltf";
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
brightness: 150.0,
..default()
})
+1 -1
View File
@@ -17,7 +17,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_state::<LoadingState>()
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
color: Color::WHITE,
brightness: 2000.,
..default()
@@ -23,7 +23,7 @@ const ATTRIBUTE_BARYCENTRIC: MeshVertexAttribute =
fn main() {
App::new()
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
color: Color::WHITE,
brightness: 1.0 / 5.0f32,
..default()
+1 -1
View File
@@ -8,7 +8,7 @@ use bevy::{math::ops, mesh::skinning::SkinnedMesh, prelude::*};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
brightness: 750.0,
..default()
})
+1 -1
View File
@@ -306,7 +306,7 @@ fn setup_cameras(mut commands: Commands) {
));
}
fn setup_ambient_light(mut ambient_light: ResMut<AmbientLight>) {
fn setup_ambient_light(mut ambient_light: ResMut<GlobalAmbientLight>) {
ambient_light.brightness = 50.0;
}
@@ -0,0 +1,30 @@
---
title: "`AmbientLight` split into a component and a resource"
pull_requests: [21585]
---
The `AmbientLight` used to be both a component *and* a resource.
In 0.18, we've split this in two separate structs: `AmbientLight` and `GlobalAmbientLight`.
The resource `GlobalAmbientLight` is the default ambient light for the entire world and automatically added by `LightPlugin`.
Meanwhile, `AmbientLight` is a component that can be added to a `Camera` in order to override the default `GlobalAmbientLight`.
When appropriate, rename `AmbientLight` to `GlobalAmbientLight`.
Before:
```rust
app.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 2000.,
..default()
});
```
After:
```rust
app.insert_resource(GlobalAmbientLight {
color: Color::WHITE,
brightness: 2000.,
..default()
});
```
+1 -1
View File
@@ -16,7 +16,7 @@ use core::f32::consts::TAU;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
brightness: 20_000.0,
..default()
})