Rename projects

This commit is contained in:
Danny McGee
2023-09-29 18:33:00 -04:00
parent 32057039b0
commit bed97cff47
30 changed files with 40 additions and 40 deletions
+41
View File
@@ -0,0 +1,41 @@
#![allow(clippy::type_complexity)]
use bevy::prelude::*;
use wasm_bindgen::prelude::*;
mod orbit_cam;
mod test_scene;
mod ui;
use orbit_cam::{OrbitCam, OrbitCamPlugin};
use test_scene::TestScenePlugin;
use ui::UiLayerPlugin;
#[wasm_bindgen]
pub fn init() {
#[cfg(target_arch = "wasm32")]
console_error_panic_hook::set_once();
App::new()
.add_plugins((
DefaultPlugins,
OrbitCamPlugin,
TestScenePlugin,
UiLayerPlugin,
))
.add_systems(Update, draw_world_axes.after(orbit_cam::pan_orbit_camera))
.run();
}
fn draw_world_axes(mut gizmos: Gizmos, q: Query<&OrbitCam>) {
let &OrbitCam { focus, radius, .. } = q.single();
let mut draw_axis = |direction: Vec3, color: Color| {
let end = focus + direction * radius / 15.;
gizmos.line(focus, end, color);
};
draw_axis(Vec3::X, Color::RED);
draw_axis(Vec3::Y, Color::GREEN);
draw_axis(Vec3::Z, Color::BLUE);
}
+3
View File
@@ -0,0 +1,3 @@
fn main() {
studio::init();
}
+169
View File
@@ -0,0 +1,169 @@
use std::f32::consts;
use bevy::{
core_pipeline::prepass::{DepthPrepass, NormalPrepass},
input::mouse::{MouseButton, MouseMotion},
prelude::*,
window::PrimaryWindow,
};
pub struct OrbitCamPlugin;
impl Plugin for OrbitCamPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, spawn_camera)
.add_systems(Update, pan_orbit_camera);
}
}
#[derive(Component)]
pub struct OrbitCam {
pub focus: Vec3,
pub radius: f32,
pub upside_down: bool,
}
impl OrbitCam {
// TODO: Make these configurable
const ORBIT_BTN: MouseButton = MouseButton::Left;
const PAN_BTN: MouseButton = MouseButton::Middle;
const ZOOM_BTN: MouseButton = MouseButton::Right;
}
impl Default for OrbitCam {
fn default() -> Self {
Self {
focus: Vec3::ZERO,
radius: 10.0,
upside_down: false,
}
}
}
enum ControlMode {
Orbit,
Pan,
Zoom,
}
fn spawn_camera(mut cmd: Commands) {
let camera_xform = Transform::from_xyz(-2.5, 3.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y);
cmd.spawn((
Camera3dBundle {
transform: camera_xform,
..default()
},
DepthPrepass,
NormalPrepass,
OrbitCam {
radius: camera_xform.translation.length(),
..default()
},
));
}
pub fn pan_orbit_camera(
window: Query<&Window, With<PrimaryWindow>>,
mut mouse_motion: EventReader<MouseMotion>,
mouse_buttons: Res<Input<MouseButton>>,
keyboard: Res<Input<KeyCode>>,
mut camera: Query<(&mut OrbitCam, &mut Transform, &Projection)>,
) {
if !(keyboard.pressed(KeyCode::AltLeft) || keyboard.pressed(KeyCode::AltRight)) {
return;
}
let (mut orbit, mut xform, proj) = camera.single_mut();
if mouse_buttons.just_pressed(OrbitCam::ORBIT_BTN)
|| mouse_buttons.just_released(OrbitCam::ORBIT_BTN)
{
orbit.upside_down = xform.up().y <= 0.0;
}
let control_mode = if mouse_buttons.pressed(OrbitCam::ORBIT_BTN) {
ControlMode::Orbit
} else if mouse_buttons.pressed(OrbitCam::ZOOM_BTN) {
ControlMode::Zoom
} else if mouse_buttons.pressed(OrbitCam::PAN_BTN) {
ControlMode::Pan
} else {
return;
};
let window = window.single();
let mouse_delta = mouse_motion
.into_iter()
.fold(Vec2::ZERO, |accum, current| accum + current.delta);
match control_mode {
ControlMode::Orbit => {
do_orbit(window, mouse_delta, &mut xform, &orbit);
}
ControlMode::Pan => {
do_pan(proj, window, mouse_delta, &mut xform, &mut orbit);
}
ControlMode::Zoom => {
let delta = (mouse_delta.y * 1.5) / window.height();
orbit.radius -= orbit.radius * delta;
xform.translation = orbit.focus + xform.back() * orbit.radius;
}
}
}
#[inline]
fn do_orbit(win: &Window, mouse_delta: Vec2, xform: &mut Transform, orbit: &OrbitCam) {
// Translate to origin
xform.translation += xform.forward() * orbit.radius;
// Calculate rotation
let delta_x = {
let delta = mouse_delta.x / win.width() * consts::PI * 2.0;
match orbit.upside_down {
true => -delta,
false => delta,
}
};
let delta_y = mouse_delta.y / win.height() * consts::PI;
let yaw = Quat::from_rotation_y(-delta_x);
let pitch = Quat::from_rotation_x(-delta_y);
// Rotate around global Y and local X
xform.rotation = yaw * xform.rotation;
xform.rotation *= pitch;
// Translate back to the original position
xform.translation += xform.back() * orbit.radius;
}
#[inline]
fn do_pan(
proj: &Projection,
win: &Window,
mouse_delta: Vec2,
xform: &mut Transform,
orbit: &mut OrbitCam,
) {
// Normalize the pan distance with respect to the window resolution
let pan = match proj {
Projection::Perspective(p) => {
mouse_delta
* Vec2 {
x: (p.fov * p.aspect_ratio) / win.width(),
y: p.fov / win.height(),
}
}
_ => mouse_delta,
};
// Pan along local XY plane, with the distance scaled by the radius
let up = xform.up();
let left = xform.left();
let delta = (pan.y * up + pan.x * left) * orbit.radius * 0.8;
// Update camera translation and focus point
xform.translation += delta;
orbit.focus += delta;
}
+119
View File
@@ -0,0 +1,119 @@
#[rustfmt::skip]
use bevy::{
pbr::NotShadowCaster,
prelude::*,
reflect::{TypePath, TypeUuid},
render::render_resource::{AsBindGroup, ShaderRef},
};
use crate::orbit_cam::{self, OrbitCam};
pub struct TestScenePlugin;
impl Plugin for TestScenePlugin {
fn build(&self, app: &mut App) {
app.insert_resource(ClearColor(Color::hsl(0.0, 0.0, 0.25)))
.add_plugins(MaterialPlugin::<ProceduralGridMaterial> {
prepass_enabled: true,
..default()
})
.add_systems(Startup, spawn_scene)
.add_systems(
PostUpdate,
move_plane_with_camera.after(orbit_cam::pan_orbit_camera),
);
}
}
#[derive(Component)]
struct WorldGridPlane;
fn spawn_scene(
mut cmd: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut std_mats: ResMut<Assets<StandardMaterial>>,
mut mat_proc_grid: ResMut<Assets<ProceduralGridMaterial>>,
) {
let mut light_xform = Transform::IDENTITY;
light_xform.rotation = Quat::from_rotation_y(f32::to_radians(-70.0)) * light_xform.rotation;
light_xform.rotation *= Quat::from_rotation_x(f32::to_radians(-60.0));
cmd.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
illuminance: 25_000.0,
shadows_enabled: true,
..default()
},
transform: light_xform,
..default()
});
// Cube
cmd.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
material: std_mats.add(StandardMaterial {
base_color: Color::SILVER,
..default()
}),
transform: Transform::IDENTITY,
..default()
});
// Plane
let plane = meshes.add(Mesh::from(shape::Plane {
size: 200.0,
subdivisions: 0,
}));
let proc_grid = mat_proc_grid.add(ProceduralGridMaterial {});
cmd.spawn((
SpatialBundle::from_transform(Transform::IDENTITY),
WorldGridPlane,
))
.with_children(|parent| {
parent.spawn((
MaterialMeshBundle {
mesh: plane.clone(),
material: proc_grid.clone(),
transform: Transform::IDENTITY,
..default()
},
NotShadowCaster,
));
parent.spawn((
MaterialMeshBundle {
mesh: plane,
material: proc_grid,
transform: Transform::IDENTITY.looking_to(Vec3::Z, Vec3::NEG_Y),
..default()
},
NotShadowCaster,
));
});
}
fn move_plane_with_camera(
mut plane: Query<(&WorldGridPlane, &mut Transform)>,
camera: Query<&OrbitCam, Changed<OrbitCam>>,
) {
if let Ok(&OrbitCam { focus, .. }) = camera.get_single() {
let (_, mut xform) = plane.single_mut();
xform.translation.x = focus.x;
xform.translation.z = focus.z;
}
}
#[derive(AsBindGroup, Clone, TypePath, TypeUuid)]
#[uuid = "CE331A13-F247-4F5B-A75E-670B2BD5776A"]
struct ProceduralGridMaterial {}
impl Material for ProceduralGridMaterial {
fn fragment_shader() -> ShaderRef {
"shaders/procedural_grid.wgsl".into()
}
fn alpha_mode(&self) -> AlphaMode {
AlphaMode::Blend
}
}
+65
View File
@@ -0,0 +1,65 @@
use bevy::{
diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin},
prelude::*,
};
pub struct UiLayerPlugin;
impl Plugin for UiLayerPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(FrameTimeDiagnosticsPlugin)
.add_systems(Startup, layout)
.add_systems(Update, update);
}
}
#[derive(Component)]
pub struct FramerateCounter;
fn layout(mut cmd: Commands) {
cmd.spawn(NodeBundle {
style: Style {
width: Val::Percent(100.),
height: Val::Percent(100.),
padding: UiRect::all(Val::Px(16.)),
..default()
},
..default()
})
.with_children(|parent| {
parent.spawn((
FramerateCounter,
TextBundle::from_section(
"0 fps",
TextStyle {
color: Color::GREEN,
font_size: 14.,
..default()
},
)
.with_text_alignment(TextAlignment::Right)
.with_style(Style {
// TODO: This doesn't seem to have any effect
width: Val::Percent(100.),
..default()
}),
));
});
}
fn update(
time: Res<Time>,
diag: Res<DiagnosticsStore>,
mut counter: Query<&mut Text, With<FramerateCounter>>,
) {
if let Some(fps_diag) = diag.get(FrameTimeDiagnosticsPlugin::FPS) {
let fps = if let Some(fps_smoothed) = fps_diag.smoothed() {
fps_smoothed
} else {
let ms = time.delta().as_micros() as f64 / 1000.;
1000. / ms
};
counter.single_mut().sections[0].value = format!("{fps:.2} fps");
}
}