diff --git a/Cargo.lock b/Cargo.lock index a9ed1b611..ee78f9ccd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4131,6 +4131,7 @@ dependencies = [ name = "example-wired-input" version = "0.0.3" dependencies = [ + "rand", "wit-bindgen-rt", ] diff --git a/crates/unavi-player/src/input.rs b/crates/unavi-player/src/input.rs index 9136c31a0..030405fe1 100644 --- a/crates/unavi-player/src/input.rs +++ b/crates/unavi-player/src/input.rs @@ -2,7 +2,9 @@ use bevy::input::keyboard::KeyCode; use bevy::prelude::*; use bevy_xpbd_3d::prelude::*; use unavi_constants::layers::{OTHER_PLAYER_LAYER, WORLD_LAYER}; -use unavi_scripting::actions::handler::NodeId; +use unavi_scripting::{ + actions::handler::InputHandler, api::wired_input::input_handler::ScriptInputEvent, +}; use crate::PlayerCamera; @@ -53,28 +55,19 @@ const RAYCAST_DISTANCE: f32 = 10.0; pub fn handle_raycast_input( camera: Query<&GlobalTransform, With>, mouse: Res>, - mut draw_ray: Local<(Vec3, Vec3)>, - mut gizmos: Gizmos, - nodes: Query<(Entity, &NodeId)>, + nodes: Query<(Entity, &InputHandler)>, query: SpatialQuery, ) { if camera.is_empty() { return; } - gizmos.arrow(draw_ray.0, draw_ray.1, Color::BLUE); - if mouse.just_pressed(MouseButton::Left) { let transform = camera.single(); let (_, rotation, translation) = transform.to_scale_rotation_translation(); let direction = rotation.normalize() * Direction3d::NEG_Z; - // TODO: Raycast from cursor location if not pointer-locked. - - let end = translation + direction * RAYCAST_DISTANCE; - *draw_ray = (translation, end); - if let Some(hit) = query.cast_ray( translation, direction, @@ -85,11 +78,16 @@ pub fn handle_raycast_input( ..default() }, ) { - for (ent, id) in nodes.iter() { - // TODO: Recursive check to see if children were hit. + for (ent, handler) in nodes.iter() { + // TODO: Recursive check if children were hit. if hit.entity == ent { - info!("Hit node: {:?}", id); + if let Err(e) = handler.send(ScriptInputEvent::Raycast { + origin: translation, + orientation: rotation, + }) { + error!("Failed to send script input event: {}", e); + }; break; } } diff --git a/crates/unavi-scripting/src/actions/handler.rs b/crates/unavi-scripting/src/actions/handler.rs index 1ab53682e..56f8c8926 100644 --- a/crates/unavi-scripting/src/actions/handler.rs +++ b/crates/unavi-scripting/src/actions/handler.rs @@ -7,12 +7,15 @@ use bevy::{ utils::HashMap, }; use bevy_xpbd_3d::prelude::*; +use crossbeam::channel::Sender; use unavi_constants::layers::WORLD_LAYER; +use crate::api::wired_input::input_handler::ScriptInputEvent; + use super::{ActionReceiver, ScriptAction}; -#[derive(Component)] -pub struct InputHandler; +#[derive(Component, Deref)] +pub struct InputHandler(pub Sender); #[derive(Component, Clone, Copy, Debug)] pub struct NodeId(pub u32); @@ -308,8 +311,8 @@ pub fn handle_actions( let s = span.entered(); if let Some((ent, ..)) = find_node(nodes, id, world) { - if handler.is_some() { - world.entity_mut(ent).insert(InputHandler); + if let Some(sender) = handler { + world.entity_mut(ent).insert(InputHandler(sender)); } else { world.entity_mut(ent).remove::(); } diff --git a/crates/unavi-scripting/src/actions/mod.rs b/crates/unavi-scripting/src/actions/mod.rs index 1c40dc895..0858a6410 100644 --- a/crates/unavi-scripting/src/actions/mod.rs +++ b/crates/unavi-scripting/src/actions/mod.rs @@ -1,6 +1,8 @@ use bevy::prelude::*; use bevy_xpbd_3d::prelude::*; -use crossbeam::channel::Receiver; +use crossbeam::channel::{Receiver, Sender}; + +use crate::api::wired_input::input_handler::ScriptInputEvent; pub mod handler; @@ -45,7 +47,7 @@ pub enum ScriptAction { }, SetNodeInputHandler { id: u32, - handler: Option<()>, + handler: Option>, }, SetNodeMesh { id: u32, diff --git a/crates/unavi-scripting/src/api/wired_input/input_handler.rs b/crates/unavi-scripting/src/api/wired_input/input_handler.rs index e78296d67..f9cb9d612 100644 --- a/crates/unavi-scripting/src/api/wired_input/input_handler.rs +++ b/crates/unavi-scripting/src/api/wired_input/input_handler.rs @@ -1,5 +1,6 @@ use std::cell::Cell; +use crossbeam::channel::{Receiver, Sender}; use wasm_bridge::component::Resource; use crate::{ @@ -7,9 +8,24 @@ use crate::{ state::StoreState, }; -use super::wired::input::{handler::HostInputHandler, types::InputEvent}; +use super::wired::{ + input::{ + handler::HostInputHandler, + types::{InputEvent, InputType, Ray}, + }, + math::types::{Quat, Vec3}, +}; + +pub enum ScriptInputEvent { + Raycast { + origin: bevy::math::Vec3, + orientation: bevy::math::Quat, + }, +} pub struct InputHandler { + pub sender: Sender, + receiver: Receiver, ref_count: Cell, } @@ -23,8 +39,12 @@ impl RefResource for InputHandler {} impl InputHandler { pub fn new() -> Self { + let (sender, receiver) = crossbeam::channel::bounded(10); + Self { + receiver, ref_count: Cell::new(1), + sender, } } } @@ -39,9 +59,45 @@ impl HostInputHandler for StoreState { fn handle_input( &mut self, - _self_: Resource, + self_: Resource, ) -> wasm_bridge::Result> { - Ok(None) + let data = self.table.get(&self_)?; + + if let Ok(event) = data.receiver.try_recv() { + let e = match event { + ScriptInputEvent::Raycast { + origin, + orientation, + } => { + let origin = Vec3 { + x: origin.x, + y: origin.y, + z: origin.z, + }; + + let orientation = Quat { + x: orientation.x, + y: orientation.y, + z: orientation.z, + w: orientation.w, + }; + + InputEvent { + id: 0, + input: InputType::Ray(Ray { + origin, + orientation, + }), + order: 0, + distance: 0.0, + } + } + }; + + Ok(Some(e)) + } else { + Ok(None) + } } fn drop(&mut self, rep: Resource) -> wasm_bridge::Result<()> { diff --git a/crates/unavi-scripting/src/api/wired_scene/node.rs b/crates/unavi-scripting/src/api/wired_scene/node.rs index a950b9067..dff7c8c6a 100644 --- a/crates/unavi-scripting/src/api/wired_scene/node.rs +++ b/crates/unavi-scripting/src/api/wired_scene/node.rs @@ -265,8 +265,21 @@ impl HostNode for StoreState { self_: Resource, value: Option>, ) -> wasm_bridge::Result<()> { + let handler = if let Some(value) = &value { + let data = self.table.get(value)?; + Some(data.sender.clone()) + } else { + None + }; + let node = self.table.get_mut(&self_)?; node.input_handler = value; + + self.sender.send(ScriptAction::SetNodeInputHandler { + id: self_.rep(), + handler, + })?; + Ok(()) } diff --git a/crates/unavi-scripting/src/lib.rs b/crates/unavi-scripting/src/lib.rs index dfbf1aeac..ff4d83472 100644 --- a/crates/unavi-scripting/src/lib.rs +++ b/crates/unavi-scripting/src/lib.rs @@ -3,7 +3,7 @@ use bevy::prelude::*; use self::{asset::Wasm, load::Scripts}; pub mod actions; -mod api; +pub mod api; pub mod asset; mod execution; mod load; diff --git a/wasm/example-wired-input/Cargo.toml b/wasm/example-wired-input/Cargo.toml index 657a9e822..150e518dd 100644 --- a/wasm/example-wired-input/Cargo.toml +++ b/wasm/example-wired-input/Cargo.toml @@ -9,6 +9,7 @@ license.workspace = true crate-type = ["cdylib"] [dependencies] +rand = "0.8.5" wit-bindgen-rt.workspace = true [package.metadata.component] diff --git a/wasm/example-wired-input/src/lib.rs b/wasm/example-wired-input/src/lib.rs index 6f405f2a0..3ece16c5c 100644 --- a/wasm/example-wired-input/src/lib.rs +++ b/wasm/example-wired-input/src/lib.rs @@ -7,11 +7,12 @@ use bindings::{ math::types::Vec3, physics::types::{Collider, Shape}, scene::{ - material::{create_material, Material}, + material::{create_material, Color, Material}, node::create_node, }, }, }; +use rand::Rng; #[allow(warnings)] mod bindings; @@ -49,8 +50,13 @@ impl GuestScript for Script { while let Some(event) = self.handler.handle_input() { log(LogLevel::Info, &format!("Got input: {:?}", event)); - let mut color = self.material.color(); - color.b += 0.1; + let mut rng = rand::thread_rng(); + let r = rng.gen_range(0.0..1.0); + let g = rng.gen_range(0.0..1.0); + let b = rng.gen_range(0.0..1.0); + let a = rng.gen_range(0.2..1.0); + + self.material.set_color(Color { r, g, b, a }); } } }