mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-17 00:13:17 +00:00
Cowboy prototyping
This commit is contained in:
@@ -16,6 +16,7 @@ path = "src/main.rs"
|
||||
wasm-bindgen = { version = "0.2.84" }
|
||||
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||
parking_lot = "0.12.1"
|
||||
bitflags = "2.4.0"
|
||||
|
||||
[features]
|
||||
wasm = ["dep:console_error_panic_hook"]
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
use bevy::{prelude::*, window::PrimaryWindow};
|
||||
|
||||
use super::{InteractionMode, InteractionTransform, Selected, TransformSpace};
|
||||
|
||||
pub(super) fn draw(
|
||||
q_selection: Query<&GlobalTransform, With<Selected>>,
|
||||
q_camera_xform: Query<&Transform, With<Camera>>,
|
||||
q_window: Query<&Window, With<PrimaryWindow>>,
|
||||
interaction_mode: Res<InteractionMode>,
|
||||
mut gizmos: Gizmos,
|
||||
mut config: ResMut<GizmoConfig>,
|
||||
) {
|
||||
if !q_selection.is_empty() {
|
||||
// TODO: Use an app state or something so we only have to set this up once
|
||||
let scale_factor = q_window.single().scale_factor() as f32;
|
||||
config.line_width = 4. * scale_factor;
|
||||
config.line_perspective = false;
|
||||
config.depth_bias = -1.;
|
||||
|
||||
let InteractionMode { transform, space } = *interaction_mode;
|
||||
let camera_xform = q_camera_xform.single();
|
||||
|
||||
// TODO: We only want to render one gizmo -- this is just for testing
|
||||
for world_xform in q_selection.iter() {
|
||||
let xform = world_xform.compute_transform();
|
||||
// Note: Scaling the gizmo size by this isn't perfect due to
|
||||
// distortion from the perspective projection. It bugged me at
|
||||
// first, but even Blender exhibits this type of gizmo distortion if
|
||||
// you dial the camera's focal length way down, so I'm gonna call it
|
||||
// good enough.
|
||||
let dist = Vec3::distance(camera_xform.translation, xform.translation);
|
||||
|
||||
if transform.contains(InteractionTransform::TRANSLATE) {
|
||||
draw_translate_gizmo(&xform, &mut gizmos, space, dist / 14.);
|
||||
}
|
||||
if transform.contains(InteractionTransform::ROTATE) {
|
||||
draw_rotate_gizmo(&xform, &mut gizmos, space, dist / 20.);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_translate_gizmo(xform: &Transform, gizmos: &mut Gizmos, space: TransformSpace, size: f32) {
|
||||
let origin = xform.translation;
|
||||
let basis = match space {
|
||||
TransformSpace::Local => xform,
|
||||
TransformSpace::World => &Transform::IDENTITY,
|
||||
};
|
||||
|
||||
let mut draw_axis = |direction: Vec3, color: Color| {
|
||||
let end = origin + direction * size;
|
||||
gizmos.line(origin, end, color);
|
||||
};
|
||||
|
||||
draw_axis(basis.local_x(), Color::RED);
|
||||
draw_axis(basis.local_y(), Color::GREEN);
|
||||
draw_axis(basis.local_z(), Color::BLUE);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_rotate_gizmo(xform: &Transform, gizmos: &mut Gizmos, space: TransformSpace, radius: f32) {
|
||||
let origin = xform.translation;
|
||||
let basis = match space {
|
||||
TransformSpace::Local => xform,
|
||||
TransformSpace::World => &Transform::IDENTITY,
|
||||
};
|
||||
|
||||
gizmos.circle(origin, basis.local_x(), radius, Color::RED);
|
||||
gizmos.circle(origin, basis.local_y(), radius, Color::GREEN);
|
||||
gizmos.circle(origin, basis.local_z(), radius, Color::BLUE);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
use bevy::prelude::*;
|
||||
use bitflags::bitflags;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
mod gizmos;
|
||||
|
||||
pub struct InteractionPlugin;
|
||||
|
||||
impl Plugin for InteractionPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.insert_resource(SelectionMode::default())
|
||||
.insert_resource(InteractionMode::default())
|
||||
.add_systems(Update, gizmos::draw);
|
||||
|
||||
app.add_systems(
|
||||
PreUpdate,
|
||||
(wasm::update_selection_mode, wasm::update_interaction_mode),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Clone, Copy)]
|
||||
pub struct Selectable(SelectionChannel);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[wasm_bindgen]
|
||||
pub enum SelectionChannel {
|
||||
Objects,
|
||||
Bones,
|
||||
Widgets,
|
||||
}
|
||||
|
||||
impl Default for SelectionChannel {
|
||||
fn default() -> Self {
|
||||
Self::Objects
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Clone, Copy)]
|
||||
pub struct Selected;
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub struct SelectionMode(SelectionChannel);
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct InteractionMode {
|
||||
pub transform: InteractionTransform,
|
||||
pub space: TransformSpace,
|
||||
}
|
||||
|
||||
impl Default for InteractionMode {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
transform: InteractionTransform::TRANSLATE | InteractionTransform::ROTATE,
|
||||
space: default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[wasm_bindgen]
|
||||
pub struct InteractionTransform(u8);
|
||||
|
||||
bitflags! {
|
||||
impl InteractionTransform: u8 {
|
||||
const TRANSLATE = 0b0001;
|
||||
const ROTATE = 0b0010;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[wasm_bindgen]
|
||||
pub enum TransformSpace {
|
||||
Local,
|
||||
World,
|
||||
}
|
||||
|
||||
impl Default for TransformSpace {
|
||||
fn default() -> Self {
|
||||
Self::Local
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(target_arch = "wasm32")]
|
||||
pub mod wasm {
|
||||
use bevy::prelude::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::wasm::{self, WasmMessageBuffer};
|
||||
|
||||
use super::{
|
||||
InteractionMode, InteractionTransform, SelectionChannel, SelectionMode, TransformSpace,
|
||||
};
|
||||
|
||||
static SELECT_MODE_BUF: WasmMessageBuffer<SelectionChannel> = WasmMessageBuffer::new();
|
||||
static INTERACTION_TRANSFORM_BUF: WasmMessageBuffer<InteractionTransform> =
|
||||
WasmMessageBuffer::new();
|
||||
static INTERACTION_SPACE_BUF: WasmMessageBuffer<TransformSpace> = WasmMessageBuffer::new();
|
||||
|
||||
#[wasm_bindgen(js_name = setSelectionMode)]
|
||||
pub fn set_selection_mode(mode: SelectionChannel) {
|
||||
wasm::write_message!(SELECT_MODE_BUF, mode);
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = setInteractionTransform)]
|
||||
pub fn set_interaction_transform(flags: u8) {
|
||||
wasm::write_message!(INTERACTION_TRANSFORM_BUF, InteractionTransform(flags));
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = setTransformSpace)]
|
||||
pub fn set_transform_space(space: TransformSpace) {
|
||||
wasm::write_message!(INTERACTION_SPACE_BUF, space);
|
||||
}
|
||||
|
||||
pub(super) fn update_selection_mode(mut selection_mode: ResMut<SelectionMode>) {
|
||||
wasm::read_buffer!(SELECT_MODE_BUF, mode => {
|
||||
selection_mode.0 = mode;
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn update_interaction_mode(mut interaction_mode: ResMut<InteractionMode>) {
|
||||
wasm::read_buffer!(INTERACTION_TRANSFORM_BUF, transform => {
|
||||
interaction_mode.transform = transform;
|
||||
});
|
||||
wasm::read_buffer!(INTERACTION_SPACE_BUF, space => {
|
||||
interaction_mode.space = space;
|
||||
});
|
||||
}
|
||||
}
|
||||
+22
-12
@@ -2,7 +2,13 @@
|
||||
|
||||
use bevy::prelude::*;
|
||||
use input::StudioInputPlugin;
|
||||
use orbit_cam::{CameraMotionSystem, OrbitCam, OrbitCamPlugin};
|
||||
use interaction::InteractionPlugin;
|
||||
#[rustfmt::skip]
|
||||
use orbit_cam::{
|
||||
// CameraMotionSystem,
|
||||
// OrbitCam,
|
||||
OrbitCamPlugin,
|
||||
};
|
||||
use skybox::SkyboxPlugin;
|
||||
use test_scene::TestScenePlugin;
|
||||
use ui::UiLayerPlugin;
|
||||
@@ -10,11 +16,14 @@ use viewport::ViewportSize;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
mod input;
|
||||
mod interaction;
|
||||
mod orbit_cam;
|
||||
mod skybox;
|
||||
mod test_scene;
|
||||
mod ui;
|
||||
mod viewport;
|
||||
// #[cfg(target_arch = "wasm32")]
|
||||
mod wasm;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn init(viewport_size: ViewportSize) {
|
||||
@@ -36,24 +45,25 @@ pub fn init(viewport_size: ViewportSize) {
|
||||
..default()
|
||||
}),
|
||||
StudioInputPlugin,
|
||||
InteractionPlugin,
|
||||
OrbitCamPlugin,
|
||||
TestScenePlugin,
|
||||
SkyboxPlugin,
|
||||
UiLayerPlugin,
|
||||
))
|
||||
.add_systems(Update, draw_world_axes.after(CameraMotionSystem))
|
||||
// .add_systems(Update, draw_world_axes.after(CameraMotionSystem))
|
||||
.run();
|
||||
}
|
||||
|
||||
fn draw_world_axes(mut gizmos: Gizmos, q: Query<&OrbitCam>) {
|
||||
let &OrbitCam { focus, radius, .. } = q.single();
|
||||
// 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);
|
||||
};
|
||||
// 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);
|
||||
}
|
||||
// draw_axis(Vec3::X, Color::RED);
|
||||
// draw_axis(Vec3::Y, Color::GREEN);
|
||||
// draw_axis(Vec3::Z, Color::BLUE);
|
||||
// }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use bevy::math::vec3;
|
||||
#[rustfmt::skip]
|
||||
use bevy::{
|
||||
pbr::NotShadowCaster,
|
||||
@@ -6,7 +7,10 @@ use bevy::{
|
||||
render::render_resource::{AsBindGroup, ShaderRef},
|
||||
};
|
||||
|
||||
use crate::orbit_cam::{CameraMotionSystem, OrbitCam};
|
||||
use crate::{
|
||||
interaction::Selected,
|
||||
orbit_cam::{CameraMotionSystem, OrbitCam},
|
||||
};
|
||||
|
||||
pub struct TestScenePlugin;
|
||||
|
||||
@@ -15,6 +19,7 @@ impl Plugin for TestScenePlugin {
|
||||
app.insert_resource(ClearColor(Color::hsl(0.0, 0.0, 0.25)))
|
||||
.add_plugins(MaterialPlugin::<ProceduralGridMaterial>::default())
|
||||
.add_systems(Startup, spawn_scene)
|
||||
.add_systems(Update, right_round_round_round)
|
||||
.add_systems(PostUpdate, move_plane_with_camera.after(CameraMotionSystem));
|
||||
}
|
||||
}
|
||||
@@ -22,6 +27,9 @@ impl Plugin for TestScenePlugin {
|
||||
#[derive(Component)]
|
||||
struct WorldGridPlane;
|
||||
|
||||
#[derive(Component)]
|
||||
struct Spin(Vec3); // TODO: Debug
|
||||
|
||||
fn spawn_scene(
|
||||
mut cmd: Commands,
|
||||
assets: Res<AssetServer>,
|
||||
@@ -29,10 +37,18 @@ fn spawn_scene(
|
||||
mut mat_proc_grid: ResMut<Assets<ProceduralGridMaterial>>,
|
||||
) {
|
||||
// Skeletal mesh
|
||||
cmd.spawn(SceneBundle {
|
||||
scene: assets.load("gltf/base_human_male.gltf#Scene0"),
|
||||
..default()
|
||||
});
|
||||
cmd.spawn((SpatialBundle::default(), Spin(Vec3::Y * 0.5)))
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
SceneBundle {
|
||||
scene: assets.load("gltf/base_human_male.gltf#Scene0"),
|
||||
transform: Transform::from_translation(vec3(0., 0., 2.5)),
|
||||
..default()
|
||||
},
|
||||
Selected,
|
||||
Spin(vec3(0., -1.5, 1.)),
|
||||
));
|
||||
});
|
||||
|
||||
// Plane
|
||||
let plane = meshes.add(Mesh::from(shape::Plane {
|
||||
@@ -79,6 +95,17 @@ fn move_plane_with_camera(
|
||||
}
|
||||
}
|
||||
|
||||
fn right_round_round_round(mut q_spin: Query<(&mut Transform, &Spin)>, time: Res<Time>) {
|
||||
let dt = time.delta_seconds();
|
||||
|
||||
for (mut xform, spin) in q_spin.iter_mut() {
|
||||
let speed = spin.0.length();
|
||||
let axis = spin.0 / speed;
|
||||
|
||||
xform.rotate_local_axis(axis, std::f32::consts::PI * speed * dt);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(AsBindGroup, Clone, TypePath, TypeUuid)]
|
||||
#[uuid = "CE331A13-F247-4F5B-A75E-670B2BD5776A"]
|
||||
struct ProceduralGridMaterial {}
|
||||
|
||||
+8
-15
@@ -45,24 +45,20 @@ impl fmt::Debug for ViewportSize {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
// #[cfg(target_arch = "wasm32")]
|
||||
pub mod wasm {
|
||||
use std::{cell::RefCell, sync::OnceLock};
|
||||
|
||||
use bevy::prelude::*;
|
||||
use parking_lot::ReentrantMutex;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::wasm::{self, WasmMessageBuffer};
|
||||
|
||||
use super::ViewportSize;
|
||||
|
||||
static RESIZE_BUF: OnceLock<ReentrantMutex<RefCell<Option<ViewportSize>>>> = OnceLock::new();
|
||||
static RESIZE_BUF: WasmMessageBuffer<ViewportSize> = WasmMessageBuffer::new();
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn resize(viewport_size: ViewportSize) {
|
||||
let buffer = RESIZE_BUF.get_or_init(|| ReentrantMutex::new(RefCell::new(None)));
|
||||
if let Some(lock) = buffer.try_lock() {
|
||||
*lock.borrow_mut() = Some(viewport_size);
|
||||
}
|
||||
wasm::write_message!(RESIZE_BUF, viewport_size);
|
||||
}
|
||||
|
||||
pub struct ViewportPlugin;
|
||||
@@ -89,11 +85,8 @@ pub mod wasm {
|
||||
}
|
||||
|
||||
fn dispatch_resize_events(mut events: EventWriter<ViewportResizeEvent>) {
|
||||
let buffer = RESIZE_BUF.get_or_init(|| ReentrantMutex::new(RefCell::new(None)));
|
||||
if let Some(lock) = buffer.try_lock() {
|
||||
if let Some(resize) = lock.borrow_mut().take() {
|
||||
events.send(ViewportResizeEvent(resize));
|
||||
}
|
||||
}
|
||||
wasm::read_buffer!(RESIZE_BUF, resize => {
|
||||
events.send(ViewportResizeEvent(resize));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
use std::{cell::RefCell, sync::OnceLock};
|
||||
|
||||
use parking_lot::ReentrantMutex;
|
||||
|
||||
pub(crate) type WasmMessageBuffer<T> = OnceLock<ReentrantMutex<RefCell<Option<T>>>>;
|
||||
|
||||
macro_rules! get_buffer {
|
||||
($buffer:ident) => {
|
||||
$buffer.get_or_init(|| {
|
||||
::parking_lot::ReentrantMutex::new(::std::cell::RefCell::new(
|
||||
::core::option::Option::None,
|
||||
))
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! write_message {
|
||||
($buffer:ident, $expr:expr) => {{
|
||||
let buffer = crate::wasm::get_buffer!($buffer);
|
||||
if let Some(lock) = buffer.try_lock() {
|
||||
*lock.borrow_mut() = Some($expr);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! read_buffer {
|
||||
($buffer:ident, $msg:ident => $stmt:stmt) => {{
|
||||
let buffer = crate::wasm::get_buffer!($buffer);
|
||||
if let Some(lock) = buffer.try_lock() {
|
||||
if let Some($msg) = lock.borrow_mut().take() {
|
||||
$stmt
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) use get_buffer;
|
||||
pub(crate) use read_buffer;
|
||||
pub(crate) use write_message;
|
||||
Reference in New Issue
Block a user