#![allow(clippy::type_complexity)] use std::f32::consts::PI; use bevy::{prelude::*, window::close_on_esc}; use bevy_egui::{egui, EguiContexts, EguiPlugin}; use bevy_toon_shader::{ToonShaderMainCamera, ToonShaderMaterial, ToonShaderPlugin, ToonShaderSun}; fn main() { App::new() .add_plugins( DefaultPlugins .set(ImagePlugin::default_nearest()) .set(WindowPlugin { primary_window: Some(Window { title: "Bevy Toon Shader".to_owned(), ..default() }), ..default() }), ) .add_plugins(ToonShaderPlugin) .add_plugins(EguiPlugin) .add_systems(Startup, setup) .add_systems(Update, (ui_example_system, rotate_shapes, close_on_esc)) .run(); } fn setup( mut commands: Commands, mut meshes: ResMut>, mut images: ResMut>, mut toon_materials: ResMut>, mut materials: ResMut>, ) { commands.spawn(( Camera3dBundle { camera: Camera { hdr: true, ..default() }, transform: Transform::from_xyz(0.0, 8., 12.0) .looking_at(Vec3::new(0., 1., 0.), Vec3::Y), ..default() }, ToonShaderMainCamera, )); commands.spawn(( DirectionalLightBundle { directional_light: DirectionalLight { shadows_enabled: true, illuminance: 10_000., ..default() }, transform: Transform { translation: Vec3::new(2.0, 2.0, 2.0), rotation: Quat::from_euler(EulerRot::XYZ, -PI / 4., PI / 6., 0.), ..default() }, ..default() }, ToonShaderSun, )); commands.insert_resource(AmbientLight { color: Color::GRAY * 0.2, brightness: 0.10, }); let toon_material_textured = toon_materials.add(ToonShaderMaterial { base_color_texture: Some(images.add(uv_debug_texture())), ..default() }); let toon_material = toon_materials.add(ToonShaderMaterial::default()); let shapes = [ meshes.add(shape::Cube::default().into()), meshes.add(shape::Box::default().into()), meshes.add(shape::Capsule::default().into()), meshes.add(shape::Torus::default().into()), meshes.add(shape::Cylinder::default().into()), meshes.add(shape::Icosphere::default().try_into().unwrap()), meshes.add(shape::UVSphere::default().into()), ]; let num_shapes = shapes.len(); const X_EXTENT: f32 = 14.5; for (i, mesh) in shapes.into_iter().enumerate() { // Texture commands.spawn(( MaterialMeshBundle { mesh: mesh.clone(), material: toon_material_textured.clone(), transform: Transform::from_xyz( -X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * X_EXTENT, 2.0, 3.0, ) .with_rotation(Quat::from_rotation_x(-PI / 4.)), ..default() }, Shape, )); // Without Texture commands.spawn(( MaterialMeshBundle { mesh, material: toon_material.clone(), transform: Transform::from_xyz( -X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * X_EXTENT, 2.0, -3.0, ) .with_rotation(Quat::from_rotation_x(-PI / 4.)), ..default() }, Shape, )); } commands.spawn(PbrBundle { mesh: meshes.add(shape::Plane::from_size(50.0).into()), material: materials.add(Color::SILVER.into()), ..default() }); } fn ui_example_system( mut contexts: EguiContexts, mut ambient_light: Option>, mut controls: ParamSet<( // Camera position Query<&Transform, With>, // Sun position Query<(&mut Transform, &DirectionalLight), With>, )>, ) { egui::Window::new("Controls").show(contexts.ctx_mut(), |ui| { if let Some(ambient_light) = ambient_light.as_mut() { ui.heading("Ambient Light"); let mut orig = ambient_light.color.as_rgba_f32(); if ui.color_edit_button_rgba_unmultiplied(&mut orig).changed() { ambient_light.color = Color::from(orig); } } if let Ok((mut t, _)) = controls.p1().get_single_mut() { ui.heading("Sun"); ui.horizontal(|ui| { ui.label("Angle"); let (mut x, mut y, z) = t.rotation.to_euler(EulerRot::XYZ); x = x.to_degrees(); y = y.to_degrees(); ui.add(egui::widgets::DragValue::new(&mut x).speed(1.)); ui.add(egui::widgets::DragValue::new(&mut y).speed(1.)); t.rotation = Quat::from_euler(EulerRot::XYZ, x.to_radians(), y.to_radians(), z); }); } }); } #[derive(Component)] struct Shape; fn rotate_shapes(mut query: Query<&mut Transform, With>, time: Res