bevy/examples/3d/skybox.rs
Alice Cecile 599e5e4e76
Migrate from LegacyColor to bevy_color::Color (#12163)
# Objective

- As part of the migration process we need to a) see the end effect of
the migration on user ergonomics b) check for serious perf regressions
c) actually migrate the code
- To accomplish this, I'm going to attempt to migrate all of the
remaining user-facing usages of `LegacyColor` in one PR, being careful
to keep a clean commit history.
- Fixes #12056.

## Solution

I've chosen to use the polymorphic `Color` type as our standard
user-facing API.

- [x] Migrate `bevy_gizmos`.
- [x] Take `impl Into<Color>` in all `bevy_gizmos` APIs
- [x] Migrate sprites
- [x] Migrate UI
- [x] Migrate `ColorMaterial`
- [x] Migrate `MaterialMesh2D`
- [x] Migrate fog
- [x] Migrate lights
- [x] Migrate StandardMaterial
- [x] Migrate wireframes
- [x] Migrate clear color
- [x] Migrate text
- [x] Migrate gltf loader
- [x] Register color types for reflection
- [x] Remove `LegacyColor`
- [x] Make sure CI passes

Incidental improvements to ease migration:

- added `Color::srgba_u8`, `Color::srgba_from_array` and friends
- added `set_alpha`, `is_fully_transparent` and `is_fully_opaque` to the
`Alpha` trait
- add and immediately deprecate (lol) `Color::rgb` and friends in favor
of more explicit and consistent `Color::srgb`
- standardized on white and black for most example text colors
- added vector field traits to `LinearRgba`: ~~`Add`, `Sub`,
`AddAssign`, `SubAssign`,~~ `Mul<f32>` and `Div<f32>`. Multiplications
and divisions do not scale alpha. `Add` and `Sub` have been cut from
this PR.
- added `LinearRgba` and `Srgba` `RED/GREEN/BLUE`
- added `LinearRgba_to_f32_array` and `LinearRgba::to_u32`

## Migration Guide

Bevy's color types have changed! Wherever you used a
`bevy::render::Color`, a `bevy::color::Color` is used instead.

These are quite similar! Both are enums storing a color in a specific
color space (or to be more precise, using a specific color model).
However, each of the different color models now has its own type.

TODO...

- `Color::rgba`, `Color::rgb`, `Color::rbga_u8`, `Color::rgb_u8`,
`Color::rgb_from_array` are now `Color::srgba`, `Color::srgb`,
`Color::srgba_u8`, `Color::srgb_u8` and `Color::srgb_from_array`.
- `Color::set_a` and `Color::a` is now `Color::set_alpha` and
`Color::alpha`. These are part of the `Alpha` trait in `bevy_color`.
- `Color::is_fully_transparent` is now part of the `Alpha` trait in
`bevy_color`
- `Color::r`, `Color::set_r`, `Color::with_r` and the equivalents for
`g`, `b` `h`, `s` and `l` have been removed due to causing silent
relatively expensive conversions. Convert your `Color` into the desired
color space, perform your operations there, and then convert it back
into a polymorphic `Color` enum.
- `Color::hex` is now `Srgba::hex`. Call `.into` or construct a
`Color::Srgba` variant manually to convert it.
- `WireframeMaterial`, `ExtractedUiNode`, `ExtractedDirectionalLight`,
`ExtractedPointLight`, `ExtractedSpotLight` and `ExtractedSprite` now
store a `LinearRgba`, rather than a polymorphic `Color`
- `Color::rgb_linear` and `Color::rgba_linear` are now
`Color::linear_rgb` and `Color::linear_rgba`
- The various CSS color constants are no longer stored directly on
`Color`. Instead, they're defined in the `Srgba` color space, and
accessed via `bevy::color::palettes::css`. Call `.into()` on them to
convert them into a `Color` for quick debugging use, and consider using
the much prettier `tailwind` palette for prototyping.
- The `LIME_GREEN` color has been renamed to `LIMEGREEN` to comply with
the standard naming.
- Vector field arithmetic operations on `Color` (add, subtract, multiply
and divide by a f32) have been removed. Instead, convert your colors
into `LinearRgba` space, and perform your operations explicitly there.
This is particularly relevant when working with emissive or HDR colors,
whose color channel values are routinely outside of the ordinary 0 to 1
range.
- `Color::as_linear_rgba_f32` has been removed. Call
`LinearRgba::to_f32_array` instead, converting if needed.
- `Color::as_linear_rgba_u32` has been removed. Call
`LinearRgba::to_u32` instead, converting if needed.
- Several other color conversion methods to transform LCH or HSL colors
into float arrays or `Vec` types have been removed. Please reimplement
these externally or open a PR to re-add them if you found them
particularly useful.
- Various methods on `Color` such as `rgb` or `hsl` to convert the color
into a specific color space have been removed. Convert into
`LinearRgba`, then to the color space of your choice.
- Various implicitly-converting color value methods on `Color` such as
`r`, `g`, `b` or `h` have been removed. Please convert it into the color
space of your choice, then check these properties.
- `Color` no longer implements `AsBindGroup`. Store a `LinearRgba`
internally instead to avoid conversion costs.

---------

Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
Co-authored-by: Afonso Lage <lage.afonso@gmail.com>
Co-authored-by: Rob Parrett <robparrett@gmail.com>
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2024-02-29 19:35:12 +00:00

177 lines
5.1 KiB
Rust

//! Load a cubemap texture onto a cube like a skybox and cycle through different compressed texture formats
#[path = "../helpers/camera_controller.rs"]
mod camera_controller;
use bevy::{
asset::LoadState,
core_pipeline::Skybox,
prelude::*,
render::{
render_resource::{TextureViewDescriptor, TextureViewDimension},
renderer::RenderDevice,
texture::CompressedImageFormats,
},
};
use camera_controller::{CameraController, CameraControllerPlugin};
use std::f32::consts::PI;
const CUBEMAPS: &[(&str, CompressedImageFormats)] = &[
(
"textures/Ryfjallet_cubemap.png",
CompressedImageFormats::NONE,
),
(
"textures/Ryfjallet_cubemap_astc4x4.ktx2",
CompressedImageFormats::ASTC_LDR,
),
(
"textures/Ryfjallet_cubemap_bc7.ktx2",
CompressedImageFormats::BC,
),
(
"textures/Ryfjallet_cubemap_etc2.ktx2",
CompressedImageFormats::ETC2,
),
];
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(CameraControllerPlugin)
.add_systems(Startup, setup)
.add_systems(
Update,
(
cycle_cubemap_asset,
asset_loaded.after(cycle_cubemap_asset),
animate_light_direction,
),
)
.run();
}
#[derive(Resource)]
struct Cubemap {
is_loaded: bool,
index: usize,
image_handle: Handle<Image>,
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// directional 'sun' light
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
illuminance: 32000.0,
..default()
},
transform: Transform::from_xyz(0.0, 2.0, 0.0)
.with_rotation(Quat::from_rotation_x(-PI / 4.)),
..default()
});
let skybox_handle = asset_server.load(CUBEMAPS[0].0);
// camera
commands.spawn((
Camera3dBundle {
transform: Transform::from_xyz(0.0, 0.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
},
CameraController::default(),
Skybox {
image: skybox_handle.clone(),
brightness: 1000.0,
},
));
// 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 {
color: Color::srgb_u8(210, 220, 240),
brightness: 1.0,
});
commands.insert_resource(Cubemap {
is_loaded: false,
index: 0,
image_handle: skybox_handle,
});
}
const CUBEMAP_SWAP_DELAY: f32 = 3.0;
fn cycle_cubemap_asset(
time: Res<Time>,
mut next_swap: Local<f32>,
mut cubemap: ResMut<Cubemap>,
asset_server: Res<AssetServer>,
render_device: Res<RenderDevice>,
) {
let now = time.elapsed_seconds();
if *next_swap == 0.0 {
*next_swap = now + CUBEMAP_SWAP_DELAY;
return;
} else if now < *next_swap {
return;
}
*next_swap += CUBEMAP_SWAP_DELAY;
let supported_compressed_formats =
CompressedImageFormats::from_features(render_device.features());
let mut new_index = cubemap.index;
for _ in 0..CUBEMAPS.len() {
new_index = (new_index + 1) % CUBEMAPS.len();
if supported_compressed_formats.contains(CUBEMAPS[new_index].1) {
break;
}
info!("Skipping unsupported format: {:?}", CUBEMAPS[new_index]);
}
// Skip swapping to the same texture. Useful for when ktx2, zstd, or compressed texture support
// is missing
if new_index == cubemap.index {
return;
}
cubemap.index = new_index;
cubemap.image_handle = asset_server.load(CUBEMAPS[cubemap.index].0);
cubemap.is_loaded = false;
}
fn asset_loaded(
asset_server: Res<AssetServer>,
mut images: ResMut<Assets<Image>>,
mut cubemap: ResMut<Cubemap>,
mut skyboxes: Query<&mut Skybox>,
) {
if !cubemap.is_loaded && asset_server.load_state(&cubemap.image_handle) == LoadState::Loaded {
info!("Swapping to {}...", CUBEMAPS[cubemap.index].0);
let image = images.get_mut(&cubemap.image_handle).unwrap();
// NOTE: PNGs do not have any metadata that could indicate they contain a cubemap texture,
// so they appear as one texture. The following code reconfigures the texture as necessary.
if image.texture_descriptor.array_layer_count() == 1 {
image.reinterpret_stacked_2d_as_array(image.height() / image.width());
image.texture_view_descriptor = Some(TextureViewDescriptor {
dimension: Some(TextureViewDimension::Cube),
..default()
});
}
for mut skybox in &mut skyboxes {
skybox.image = cubemap.image_handle.clone();
}
cubemap.is_loaded = true;
}
}
fn animate_light_direction(
time: Res<Time>,
mut query: Query<&mut Transform, With<DirectionalLight>>,
) {
for mut transform in &mut query {
transform.rotate_y(time.delta_seconds() * 0.5);
}
}