Skip to content

Commit 6f9505f

Browse files
committed
Add camera controllers.
1 parent 76bcbad commit 6f9505f

File tree

10 files changed

+663
-8
lines changed

10 files changed

+663
-8
lines changed

Cargo.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type_complexity = "allow"
2222
too_many_arguments = "allow"
2323

2424
[workspace.dependencies]
25-
bevy = { git = "https://github.com/bevyengine/bevy", branch = "main", features = ["file_watcher", "shader_format_wesl"] }
25+
bevy = { git = "https://github.com/bevyengine/bevy", branch = "main", features = ["file_watcher", "shader_format_wesl", "free_camera", "pan_camera"] }
2626
bevy_naga_reflect = { git = "https://github.com/tychedelia/bevy_naga_reflect" }
2727
naga = { version = "28", features = ["wgsl-in"] }
2828
wesl = { version = "0.3", default-features = false }
@@ -148,6 +148,10 @@ path = "examples/shapes.rs"
148148
name = "blend_modes"
149149
path = "examples/blend_modes.rs"
150150

151+
[[example]]
152+
name = "camera_controllers"
153+
path = "examples/camera_controllers.rs"
154+
151155
[profile.wasm-release]
152156
inherits = "release"
153157
opt-level = "z"

crates/processing_glfw/src/lib.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use bevy::prelude::Entity;
44
use glfw::{Action, Glfw, GlfwReceiver, PWindow, WindowEvent, WindowMode};
55
use processing_core::error::Result;
66
use processing_input::{
7-
input_flush, input_set_char, input_set_cursor_enter, input_set_cursor_leave, input_set_focus,
8-
input_set_key, input_set_mouse_button, input_set_mouse_move, input_set_scroll,
7+
input_cursor_grab_mode, input_cursor_visible, input_flush, input_set_char,
8+
input_set_cursor_enter, input_set_cursor_leave, input_set_focus, input_set_key,
9+
input_set_mouse_button, input_set_mouse_move, input_set_scroll,
910
};
1011

1112
pub struct GlfwContext {
@@ -173,10 +174,30 @@ impl GlfwContext {
173174
return false;
174175
}
175176

176-
input_flush().unwrap();
177+
let Ok(_) = input_flush() else {
178+
return false;
179+
};
180+
self.sync_cursor(surface);
177181

178182
true
179183
}
184+
185+
fn sync_cursor(&mut self, surface: Entity) {
186+
use bevy::window::CursorGrabMode;
187+
188+
let grab = input_cursor_grab_mode(surface).unwrap_or(CursorGrabMode::None);
189+
let visible = input_cursor_visible(surface).unwrap_or(true);
190+
191+
let mode = match grab {
192+
CursorGrabMode::Locked | CursorGrabMode::Confined => glfw::CursorMode::Disabled,
193+
CursorGrabMode::None if !visible => glfw::CursorMode::Hidden,
194+
CursorGrabMode::None => glfw::CursorMode::Normal,
195+
};
196+
197+
if self.window.get_cursor_mode() != mode {
198+
self.window.set_cursor_mode(mode);
199+
}
200+
}
180201
}
181202

182203
fn glfw_button_to_bevy(button: glfw::MouseButton) -> Option<MouseButton> {

crates/processing_input/src/lib.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,37 @@ pub fn input_set_focus(surface: Entity, focused: bool) -> error::Result<()> {
152152
})
153153
}
154154

155+
pub fn input_cursor_grab_mode(surface: Entity) -> error::Result<bevy::window::CursorGrabMode> {
156+
app_mut(|app| {
157+
let cursor = app
158+
.world()
159+
.get::<bevy::window::CursorOptions>(surface)
160+
.map(|c| c.grab_mode)
161+
.unwrap_or(bevy::window::CursorGrabMode::None);
162+
Ok(cursor)
163+
})
164+
}
165+
166+
pub fn input_cursor_visible(surface: Entity) -> error::Result<bool> {
167+
app_mut(|app| {
168+
let visible = app
169+
.world()
170+
.get::<bevy::window::CursorOptions>(surface)
171+
.map(|c| c.visible)
172+
.unwrap_or(true);
173+
Ok(visible)
174+
})
175+
}
176+
177+
/// Flushes the input state by running the relevant schedules. This is required to ensure that
178+
/// Bevy's bookkeeping of input state is up to date after manually sending input events.
179+
/// It should be called at the end of each frame
155180
pub fn input_flush() -> error::Result<()> {
156181
app_mut(|app| {
157-
app.world_mut().run_schedule(PreUpdate);
182+
let world = app.world_mut();
183+
world.run_schedule(First);
184+
world.run_schedule(PreUpdate);
185+
world.run_schedule(RunFixedMainLoop);
158186
Ok(())
159187
})
160188
}

crates/processing_pyo3/src/graphics.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,52 @@ impl Graphics {
10381038
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
10391039
}
10401040

1041+
pub fn orbit_camera(&self) -> PyResult<()> {
1042+
graphics_orbit_camera(self.entity).map_err(|e| PyRuntimeError::new_err(format!("{e}")))
1043+
}
1044+
1045+
pub fn free_camera(&self) -> PyResult<()> {
1046+
graphics_free_camera(self.entity).map_err(|e| PyRuntimeError::new_err(format!("{e}")))
1047+
}
1048+
1049+
pub fn pan_camera(&self) -> PyResult<()> {
1050+
graphics_pan_camera(self.entity).map_err(|e| PyRuntimeError::new_err(format!("{e}")))
1051+
}
1052+
1053+
pub fn disable_camera(&self) -> PyResult<()> {
1054+
graphics_disable_camera_controller(self.entity)
1055+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
1056+
}
1057+
1058+
pub fn camera_distance(&self, distance: f32) -> PyResult<()> {
1059+
camera_set_distance(self.entity, distance)
1060+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
1061+
}
1062+
1063+
#[pyo3(signature = (*args))]
1064+
pub fn camera_center(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> {
1065+
let v = extract_vec3(args)?;
1066+
camera_set_center(self.entity, v).map_err(|e| PyRuntimeError::new_err(format!("{e}")))
1067+
}
1068+
1069+
pub fn camera_min_distance(&self, min: f32) -> PyResult<()> {
1070+
camera_set_min_distance(self.entity, min)
1071+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
1072+
}
1073+
1074+
pub fn camera_max_distance(&self, max: f32) -> PyResult<()> {
1075+
camera_set_max_distance(self.entity, max)
1076+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
1077+
}
1078+
1079+
pub fn camera_speed(&self, speed: f32) -> PyResult<()> {
1080+
camera_set_speed(self.entity, speed).map_err(|e| PyRuntimeError::new_err(format!("{e}")))
1081+
}
1082+
1083+
pub fn camera_reset(&self) -> PyResult<()> {
1084+
camera_reset(self.entity).map_err(|e| PyRuntimeError::new_err(format!("{e}")))
1085+
}
1086+
10411087
pub fn light_directional(
10421088
&self,
10431089
color: crate::color::ColorLike,

crates/processing_pyo3/src/lib.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,66 @@ mod mewnala {
751751
graphics!(module).camera_look_at(args)
752752
}
753753

754+
#[pyfunction]
755+
#[pyo3(pass_module)]
756+
fn orbit_camera(module: &Bound<'_, PyModule>) -> PyResult<()> {
757+
graphics!(module).orbit_camera()
758+
}
759+
760+
#[pyfunction]
761+
#[pyo3(pass_module)]
762+
fn free_camera(module: &Bound<'_, PyModule>) -> PyResult<()> {
763+
graphics!(module).free_camera()
764+
}
765+
766+
#[pyfunction]
767+
#[pyo3(pass_module)]
768+
fn pan_camera(module: &Bound<'_, PyModule>) -> PyResult<()> {
769+
graphics!(module).pan_camera()
770+
}
771+
772+
#[pyfunction]
773+
#[pyo3(pass_module)]
774+
fn disable_camera(module: &Bound<'_, PyModule>) -> PyResult<()> {
775+
graphics!(module).disable_camera()
776+
}
777+
778+
#[pyfunction]
779+
#[pyo3(pass_module)]
780+
fn camera_distance(module: &Bound<'_, PyModule>, distance: f32) -> PyResult<()> {
781+
graphics!(module).camera_distance(distance)
782+
}
783+
784+
#[pyfunction]
785+
#[pyo3(pass_module, signature = (*args))]
786+
fn camera_center(module: &Bound<'_, PyModule>, args: &Bound<'_, PyTuple>) -> PyResult<()> {
787+
graphics!(module).camera_center(args)
788+
}
789+
790+
#[pyfunction]
791+
#[pyo3(pass_module)]
792+
fn camera_min_distance(module: &Bound<'_, PyModule>, min: f32) -> PyResult<()> {
793+
graphics!(module).camera_min_distance(min)
794+
}
795+
796+
#[pyfunction]
797+
#[pyo3(pass_module)]
798+
fn camera_max_distance(module: &Bound<'_, PyModule>, max: f32) -> PyResult<()> {
799+
graphics!(module).camera_max_distance(max)
800+
}
801+
802+
#[pyfunction]
803+
#[pyo3(pass_module)]
804+
fn camera_speed(module: &Bound<'_, PyModule>, speed: f32) -> PyResult<()> {
805+
graphics!(module).camera_speed(speed)
806+
}
807+
808+
#[pyfunction]
809+
#[pyo3(pass_module)]
810+
fn camera_reset(module: &Bound<'_, PyModule>) -> PyResult<()> {
811+
graphics!(module).camera_reset()
812+
}
813+
754814
#[pyfunction]
755815
#[pyo3(pass_module)]
756816
fn push_matrix(module: &Bound<'_, PyModule>) -> PyResult<()> {

0 commit comments

Comments
 (0)