use core::f32;
use bevy::{
camera::Hdr,
camera_controller::free_camera::{FreeCamera, FreeCameraPlugin},
light::ParallaxCorrection,
prelude::*,
};
use crate::widgets::{WidgetClickEvent, WidgetClickSender};
#[path = "../helpers/widgets.rs"]
mod widgets;
#[derive(Clone, Component)]
struct InnerCube;
const ENVIRONMENT_MAP_INTENSITY: f32 = 100.0;
const OUTER_CUBE_URL: &str =
"https://github.com/bevyengine/bevy_asset_files/raw/main/pccm_example/outer_cube.glb#Scene0";
const ENV_DIFFUSE_URL: &str =
"https://github.com/bevyengine/bevy_asset_files/raw/main/pccm_example/env_diffuse.ktx2";
const ENV_SPECULAR_URL: &str =
"https://github.com/bevyengine/bevy_asset_files/raw/main/pccm_example/env_specular.ktx2";
#[derive(Resource, Default)]
struct AppStatus {
pccm_enabled: PccmEnableStatus,
}
#[derive(Clone, Copy, PartialEq, Default)]
enum PccmEnableStatus {
#[default]
Enabled,
Disabled,
}
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Bevy Parallax-Corrected Cubemaps Example".into(),
..default()
}),
..default()
}),
FreeCameraPlugin,
))
.init_resource::<AppStatus>()
.add_message::<WidgetClickEvent<PccmEnableStatus>>()
.add_systems(Startup, setup)
.add_systems(Update, widgets::handle_ui_interactions::<PccmEnableStatus>)
.add_systems(
Update,
(handle_pccm_enable_change, update_radio_buttons)
.after(widgets::handle_ui_interactions::<PccmEnableStatus>),
)
.run();
}
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.spawn(SceneRoot(asset_server.load(OUTER_CUBE_URL)));
spawn_camera(&mut commands);
spawn_inner_cube(&mut commands, &mut meshes, &mut materials);
spawn_reflection_probe(&mut commands, &asset_server);
spawn_buttons(&mut commands);
}
fn spawn_camera(commands: &mut Commands) {
commands.spawn((
Camera3d::default(),
FreeCamera::default(),
Transform::from_xyz(0.0, 0.0, 4.0).looking_at(Vec3::new(0.0, -2.5, 0.0), Dir3::Y),
Hdr,
));
}
fn spawn_inner_cube(
commands: &mut Commands,
meshes: &mut Assets<Mesh>,
materials: &mut Assets<StandardMaterial>,
) {
let cube_mesh = meshes.add(
Cuboid {
half_size: Vec3::new(5.0, 1.0, 2.0),
}
.mesh()
.build()
.with_duplicated_vertices()
.with_computed_flat_normals(),
);
let cube_material = materials.add(StandardMaterial {
base_color: Color::WHITE,
metallic: 1.0,
reflectance: 1.0,
perceptual_roughness: 0.0,
..default()
});
commands.spawn((
Mesh3d(cube_mesh),
MeshMaterial3d(cube_material),
Transform::from_xyz(0.0, -4.0, -2.5),
InnerCube,
));
}
fn spawn_reflection_probe(commands: &mut Commands, asset_server: &AssetServer) {
let diffuse_map = asset_server.load(ENV_DIFFUSE_URL);
let specular_map = asset_server.load(ENV_SPECULAR_URL);
commands.spawn((
LightProbe::default(),
EnvironmentMapLight {
diffuse_map,
specular_map,
intensity: ENVIRONMENT_MAP_INTENSITY,
..default()
},
Transform::from_scale(Vec3::splat(10.01)),
));
}
fn spawn_buttons(commands: &mut Commands) {
commands.spawn((
widgets::main_ui_node(),
children![widgets::option_buttons(
"Parallax Correction",
&[
(PccmEnableStatus::Enabled, "On"),
(PccmEnableStatus::Disabled, "Off"),
],
)],
));
}
fn handle_pccm_enable_change(
mut commands: Commands,
light_probe_query: Query<Entity, With<LightProbe>>,
mut app_status: ResMut<AppStatus>,
mut messages: MessageReader<WidgetClickEvent<PccmEnableStatus>>,
) {
let Some(light_probe_entity) = light_probe_query.iter().next() else {
return;
};
for message in messages.read() {
app_status.pccm_enabled = **message;
match **message {
PccmEnableStatus::Enabled => {
commands
.entity(light_probe_entity)
.insert(ParallaxCorrection::Auto);
}
PccmEnableStatus::Disabled => {
commands
.entity(light_probe_entity)
.insert(ParallaxCorrection::None);
}
}
}
}
fn update_radio_buttons(
mut widgets_query: Query<(
Entity,
Option<&mut BackgroundColor>,
Has<Text>,
&WidgetClickSender<PccmEnableStatus>,
)>,
app_status: Res<AppStatus>,
mut text_ui_writer: TextUiWriter,
) {
for (entity, maybe_bg_color, has_text, sender) in &mut widgets_query {
let selected = app_status.pccm_enabled == **sender;
if let Some(mut bg_color) = maybe_bg_color {
widgets::update_ui_radio_button(&mut bg_color, selected);
}
if has_text {
widgets::update_ui_radio_button_text(entity, &mut text_ui_writer, selected);
}
}
}