vendored bevy_egui and removed legacy code :/
Signed-off-by: Sienna Meridian Satterwhite <sienna@r3t.io>
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
//! This module handles the 3D camera setup for the cube demo.
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy::camera::RenderTarget;
|
||||
use bevy::window::WindowRef;
|
||||
|
||||
pub struct CameraPlugin;
|
||||
|
||||
@@ -17,11 +19,19 @@ impl Plugin for CameraPlugin {
|
||||
/// Camera is positioned at (4, 3, 6) looking at the cube's initial position (0,
|
||||
/// 0.5, 0). This provides a good viewing angle to see the cube, ground plane,
|
||||
/// and any movements.
|
||||
///
|
||||
/// libmarathon's debug_ui will automatically attach the primary egui context
|
||||
/// to this camera via the setup_primary_egui_context_system.
|
||||
fn setup_camera(mut commands: Commands) {
|
||||
info!("Setting up camera");
|
||||
|
||||
commands.spawn((
|
||||
Camera3d::default(),
|
||||
Camera {
|
||||
target: RenderTarget::Window(WindowRef::Primary),
|
||||
..default()
|
||||
},
|
||||
Transform::from_xyz(4.0, 3.0, 6.0).looking_at(Vec3::new(0.0, 0.5, 0.0), Vec3::Y),
|
||||
// PrimaryEguiContext will be auto-added by libmarathon
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
//! Debug UI overlay using egui
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_egui::{
|
||||
egui,
|
||||
EguiContexts,
|
||||
EguiPrimaryContextPass,
|
||||
};
|
||||
use bevy::ecs::message::MessageWriter;
|
||||
use libmarathon::debug_ui::{EguiContexts, EguiPrimaryContextPass};
|
||||
use libmarathon::networking::{
|
||||
EntityLockRegistry,
|
||||
GossipBridge,
|
||||
@@ -25,17 +22,15 @@ impl Plugin for DebugUiPlugin {
|
||||
|
||||
/// Render the debug UI panel
|
||||
fn render_debug_ui(
|
||||
mut contexts: EguiContexts,
|
||||
mut egui_ctx: EguiContexts,
|
||||
node_clock: Option<Res<NodeVectorClock>>,
|
||||
gossip_bridge: Option<Res<GossipBridge>>,
|
||||
lock_registry: Option<Res<EntityLockRegistry>>,
|
||||
cube_query: Query<(&Transform, &NetworkedEntity), With<CubeMarker>>,
|
||||
mut spawn_events: MessageWriter<SpawnCubeEvent>,
|
||||
mut delete_events: MessageWriter<DeleteCubeEvent>,
|
||||
) {
|
||||
let Ok(ctx) = contexts.ctx_mut() else {
|
||||
return;
|
||||
};
|
||||
) -> Result {
|
||||
let ctx: &egui::Context = egui_ctx.ctx_mut()?;
|
||||
|
||||
egui::Window::new("Debug Info")
|
||||
.default_pos([10.0, 10.0])
|
||||
@@ -186,4 +181,6 @@ fn render_debug_ui(
|
||||
ui.label("Scroll: Move cube (Z)");
|
||||
ui.label("ESC: Deselect");
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ use libmarathon::engine::InputEvent;
|
||||
use libmarathon::platform::desktop;
|
||||
use std::sync::Arc;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent as WinitWindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
use winit::event::{Event as WinitEvent, WindowEvent as WinitWindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop, EventLoopProxy};
|
||||
use winit::window::{Window as WinitWindow, WindowId, WindowAttributes};
|
||||
|
||||
// Re-export InputEventBuffer from the input module
|
||||
@@ -124,6 +124,9 @@ impl AppHandler {
|
||||
let physical_size = winit_window.inner_size();
|
||||
let scale_factor = winit_window.scale_factor();
|
||||
|
||||
// Set the scale factor in the input bridge so mouse coords are converted correctly
|
||||
desktop::set_scale_factor(scale_factor);
|
||||
|
||||
// Create window entity with all required components (use logical size)
|
||||
let mut window = bevy::window::Window {
|
||||
title: "Marathon".to_string(),
|
||||
@@ -134,10 +137,13 @@ impl AppHandler {
|
||||
mode: WindowMode::Windowed,
|
||||
position: WindowPosition::Automatic,
|
||||
focused: true,
|
||||
// Let Window use default theme - will auto-detect system theme, egui will follow
|
||||
..Default::default()
|
||||
};
|
||||
// Set scale factor explicitly
|
||||
window.resolution.set_scale_factor(scale_factor as f32);
|
||||
// Set scale factor using the proper API that applies to physical size
|
||||
window
|
||||
.resolution
|
||||
.set_scale_factor_and_apply_to_physical_size(scale_factor as f32);
|
||||
|
||||
// Create WindowWrapper and RawHandleWrapper for renderer
|
||||
let window_wrapper = WindowWrapper::new(winit_window.clone());
|
||||
@@ -151,10 +157,9 @@ impl AppHandler {
|
||||
)).id();
|
||||
info!("Created window entity {}", window_entity);
|
||||
|
||||
// Send initialization events
|
||||
// Send initialization event (only WindowCreated, like Bevy does)
|
||||
// WindowResized and WindowScaleFactorChanged should only fire in response to actual winit events
|
||||
send_window_created(&mut bevy_app, window_entity);
|
||||
send_window_resized(&mut bevy_app, window_entity, physical_size, scale_factor);
|
||||
send_scale_factor_changed(&mut bevy_app, window_entity, scale_factor);
|
||||
|
||||
// Now finish the app - the renderer will initialize with the window
|
||||
bevy_app.finish();
|
||||
@@ -187,9 +192,9 @@ impl AppHandler {
|
||||
// Run one final update to process close event
|
||||
bevy_app.update();
|
||||
|
||||
// Cleanup
|
||||
bevy_app.finish();
|
||||
bevy_app.cleanup();
|
||||
// Don't call finish/cleanup - let Bevy's AppExit handle it
|
||||
// bevy_app.finish();
|
||||
// bevy_app.cleanup();
|
||||
}
|
||||
|
||||
event_loop.exit();
|
||||
@@ -240,7 +245,14 @@ impl ApplicationHandler for AppHandler {
|
||||
}
|
||||
|
||||
WinitWindowEvent::Resized(physical_size) => {
|
||||
// Notify Bevy of window resize
|
||||
// Update the Bevy Window component's physical resolution
|
||||
if let Some(mut window_component) = bevy_app.world_mut().get_mut::<Window>(*bevy_window_entity) {
|
||||
window_component
|
||||
.resolution
|
||||
.set_physical_resolution(physical_size.width, physical_size.height);
|
||||
}
|
||||
|
||||
// Notify Bevy systems of window resize
|
||||
let scale_factor = window.scale_factor();
|
||||
send_window_resized(bevy_app, *bevy_window_entity, physical_size, scale_factor);
|
||||
}
|
||||
@@ -269,6 +281,26 @@ impl ApplicationHandler for AppHandler {
|
||||
window.request_redraw();
|
||||
}
|
||||
|
||||
WinitWindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||
// Update the Bevy Window component's scale factor
|
||||
if let Some(mut window_component) = bevy_app.world_mut().get_mut::<Window>(*bevy_window_entity) {
|
||||
let prior_factor = window_component.resolution.scale_factor();
|
||||
|
||||
// Use the proper API that applies to physical size
|
||||
window_component
|
||||
.resolution
|
||||
.set_scale_factor_and_apply_to_physical_size(scale_factor as f32);
|
||||
|
||||
// Send scale factor changed event so camera system can update
|
||||
send_scale_factor_changed(bevy_app, *bevy_window_entity, scale_factor);
|
||||
|
||||
info!(
|
||||
"Scale factor changed from {} to {} for window {:?}",
|
||||
prior_factor, scale_factor, bevy_window_entity
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -322,7 +354,8 @@ impl ApplicationHandler for AppHandler {
|
||||
///
|
||||
/// executor::run(app).expect("Failed to run executor");
|
||||
/// ```
|
||||
pub fn run(app: App) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn run(mut app: App) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create event loop (using default type for now, WakeUp will be added when implementing battery mode)
|
||||
let event_loop = EventLoop::new()?;
|
||||
|
||||
// TODO(@siennathesane): Add battery power detection and adaptive frame/tick rate limiting
|
||||
|
||||
@@ -7,7 +7,7 @@ use bevy::prelude::*;
|
||||
use bevy::input::keyboard::KeyboardInput;
|
||||
use bevy::input::mouse::{MouseButtonInput, MouseWheel};
|
||||
use bevy::window::CursorMoved;
|
||||
use libmarathon::engine::{InputEvent, KeyCode as EngineKeyCode, MouseButton as EngineMouseButton, TouchPhase, Modifiers};
|
||||
use libmarathon::engine::{InputEvent, InputEventBuffer, KeyCode as EngineKeyCode, MouseButton as EngineMouseButton, TouchPhase, Modifiers};
|
||||
|
||||
/// Convert Bevy's Vec2 to glam::Vec2
|
||||
///
|
||||
@@ -100,19 +100,6 @@ impl Plugin for DesktopInputBridgePlugin {
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffer for InputEvents collected this frame
|
||||
#[derive(Resource, Default)]
|
||||
pub struct InputEventBuffer {
|
||||
pub events: Vec<InputEvent>,
|
||||
}
|
||||
|
||||
impl InputEventBuffer {
|
||||
/// Get all events from this frame
|
||||
pub fn events(&self) -> &[InputEvent] {
|
||||
&self.events
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the buffer at the start of each frame
|
||||
fn clear_buffer(mut buffer: ResMut<InputEventBuffer>) {
|
||||
buffer.events.clear();
|
||||
@@ -152,17 +139,21 @@ fn collect_mouse_buttons(
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect mouse motion events (for drag tracking)
|
||||
/// Collect mouse motion events (for hover and drag tracking)
|
||||
fn collect_mouse_motion(
|
||||
mut buffer: ResMut<InputEventBuffer>,
|
||||
mut cursor_moved: MessageReader<CursorMoved>,
|
||||
mouse_buttons: Res<ButtonInput<MouseButton>>,
|
||||
) {
|
||||
// Only process if cursor actually moved
|
||||
for event in cursor_moved.read() {
|
||||
let cursor_pos = event.position;
|
||||
|
||||
// Generate drag events for currently pressed buttons
|
||||
// ALWAYS send MouseMove for cursor tracking (hover, tooltips, etc.)
|
||||
buffer.events.push(InputEvent::MouseMove {
|
||||
pos: to_glam_vec2(cursor_pos),
|
||||
});
|
||||
|
||||
// ALSO generate drag events for currently pressed buttons
|
||||
if mouse_buttons.pressed(MouseButton::Left) {
|
||||
buffer.events.push(InputEvent::Mouse {
|
||||
pos: to_glam_vec2(cursor_pos),
|
||||
|
||||
@@ -1,22 +1,5 @@
|
||||
//! Input event buffer shared between executor and ECS
|
||||
//! Input event buffer re-export
|
||||
//!
|
||||
//! InputEventBuffer is now defined in libmarathon::engine
|
||||
|
||||
use bevy::prelude::*;
|
||||
use libmarathon::engine::InputEvent;
|
||||
|
||||
/// Input event buffer resource for Bevy ECS
|
||||
#[derive(Resource, Default)]
|
||||
pub struct InputEventBuffer {
|
||||
pub events: Vec<InputEvent>,
|
||||
}
|
||||
|
||||
impl InputEventBuffer {
|
||||
/// Get all events from this frame
|
||||
pub fn events(&self) -> &[InputEvent] {
|
||||
&self.events
|
||||
}
|
||||
|
||||
/// Clear the buffer
|
||||
pub fn clear(&mut self) {
|
||||
self.events.clear();
|
||||
}
|
||||
}
|
||||
pub use libmarathon::engine::InputEventBuffer;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
use bevy::prelude::*;
|
||||
use libmarathon::{
|
||||
engine::{GameAction, InputController},
|
||||
networking::{EntityLockRegistry, NetworkedEntity, NodeVectorClock},
|
||||
networking::{EntityLockRegistry, NetworkedEntity, NetworkedSelection, NodeVectorClock},
|
||||
};
|
||||
|
||||
use super::event_buffer::InputEventBuffer;
|
||||
@@ -45,15 +45,17 @@ fn to_bevy_vec2(v: glam::Vec2) -> bevy::math::Vec2 {
|
||||
fn handle_game_actions(
|
||||
input_buffer: Res<InputEventBuffer>,
|
||||
mut controller_res: ResMut<InputControllerResource>,
|
||||
lock_registry: Res<EntityLockRegistry>,
|
||||
mut lock_registry: ResMut<EntityLockRegistry>,
|
||||
node_clock: Res<NodeVectorClock>,
|
||||
mut cube_query: Query<(&NetworkedEntity, &mut Transform), With<crate::cube::CubeMarker>>,
|
||||
mut cube_query: Query<(&NetworkedEntity, &mut Transform, &mut NetworkedSelection), With<crate::cube::CubeMarker>>,
|
||||
camera_query: Query<(&Camera, &GlobalTransform)>,
|
||||
window_query: Query<&Window>,
|
||||
) {
|
||||
let node_id = node_clock.node_id;
|
||||
|
||||
// Process all input events through the controller to get game actions
|
||||
let mut all_actions = Vec::new();
|
||||
for event in input_buffer.events() {
|
||||
for event in input_buffer.events.iter() {
|
||||
let actions = controller_res.controller.process_event(event);
|
||||
all_actions.extend(actions);
|
||||
}
|
||||
@@ -61,6 +63,17 @@ fn handle_game_actions(
|
||||
// Apply game actions to entities
|
||||
for action in all_actions {
|
||||
match action {
|
||||
GameAction::SelectEntity { position } => {
|
||||
apply_select_entity(
|
||||
position,
|
||||
&mut lock_registry,
|
||||
node_id,
|
||||
&mut cube_query,
|
||||
&camera_query,
|
||||
&window_query,
|
||||
);
|
||||
}
|
||||
|
||||
GameAction::MoveEntity { delta } => {
|
||||
apply_move_entity(delta, &lock_registry, node_id, &mut cube_query);
|
||||
}
|
||||
@@ -84,17 +97,83 @@ fn handle_game_actions(
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply SelectEntity action - raycast to find clicked cube and select it
|
||||
fn apply_select_entity(
|
||||
position: glam::Vec2,
|
||||
lock_registry: &mut EntityLockRegistry,
|
||||
node_id: uuid::Uuid,
|
||||
cube_query: &mut Query<(&NetworkedEntity, &mut Transform, &mut NetworkedSelection), With<crate::cube::CubeMarker>>,
|
||||
camera_query: &Query<(&Camera, &GlobalTransform)>,
|
||||
window_query: &Query<&Window>,
|
||||
) {
|
||||
// Get the camera and window
|
||||
let Ok((camera, camera_transform)) = camera_query.single() else {
|
||||
return;
|
||||
};
|
||||
let Ok(window) = window_query.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Convert screen position to world ray
|
||||
let Some(ray) = screen_to_world_ray(position, camera, camera_transform, window) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Find the closest cube hit by the ray
|
||||
let mut closest_hit: Option<(uuid::Uuid, f32)> = None;
|
||||
|
||||
for (networked, transform, _) in cube_query.iter() {
|
||||
// Test ray against cube AABB (1x1x1 cube)
|
||||
if let Some(distance) = ray_aabb_intersection(
|
||||
ray.origin,
|
||||
ray.direction,
|
||||
transform.translation,
|
||||
Vec3::splat(0.5), // Half extents for 1x1x1 cube
|
||||
) {
|
||||
if closest_hit.map_or(true, |(_, d)| distance < d) {
|
||||
closest_hit = Some((networked.network_id, distance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we hit a cube, clear all selections and select this one
|
||||
if let Some((hit_entity_id, _)) = closest_hit {
|
||||
// Clear all previous selections and locks
|
||||
for (networked, _, mut selection) in cube_query.iter_mut() {
|
||||
selection.clear();
|
||||
lock_registry.release(networked.network_id, node_id);
|
||||
}
|
||||
|
||||
// Select and lock the clicked cube
|
||||
for (networked, _, mut selection) in cube_query.iter_mut() {
|
||||
if networked.network_id == hit_entity_id {
|
||||
selection.add(hit_entity_id);
|
||||
let _ = lock_registry.try_acquire(hit_entity_id, node_id);
|
||||
info!("Selected cube {}", hit_entity_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Clicked on empty space - deselect all
|
||||
for (networked, _, mut selection) in cube_query.iter_mut() {
|
||||
selection.clear();
|
||||
lock_registry.release(networked.network_id, node_id);
|
||||
}
|
||||
info!("Deselected all cubes");
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply MoveEntity action to locked cubes
|
||||
fn apply_move_entity(
|
||||
delta: glam::Vec2,
|
||||
lock_registry: &EntityLockRegistry,
|
||||
node_id: uuid::Uuid,
|
||||
cube_query: &mut Query<(&NetworkedEntity, &mut Transform), With<crate::cube::CubeMarker>>,
|
||||
cube_query: &mut Query<(&NetworkedEntity, &mut Transform, &mut NetworkedSelection), With<crate::cube::CubeMarker>>,
|
||||
) {
|
||||
let bevy_delta = to_bevy_vec2(delta);
|
||||
let sensitivity = 0.01; // Scale factor
|
||||
|
||||
for (networked, mut transform) in cube_query.iter_mut() {
|
||||
for (networked, mut transform, _) in cube_query.iter_mut() {
|
||||
if lock_registry.is_locked_by(networked.network_id, node_id, node_id) {
|
||||
transform.translation.x += bevy_delta.x * sensitivity;
|
||||
transform.translation.y -= bevy_delta.y * sensitivity; // Invert Y for screen coords
|
||||
@@ -107,12 +186,12 @@ fn apply_rotate_entity(
|
||||
delta: glam::Vec2,
|
||||
lock_registry: &EntityLockRegistry,
|
||||
node_id: uuid::Uuid,
|
||||
cube_query: &mut Query<(&NetworkedEntity, &mut Transform), With<crate::cube::CubeMarker>>,
|
||||
cube_query: &mut Query<(&NetworkedEntity, &mut Transform, &mut NetworkedSelection), With<crate::cube::CubeMarker>>,
|
||||
) {
|
||||
let bevy_delta = to_bevy_vec2(delta);
|
||||
let sensitivity = 0.01;
|
||||
|
||||
for (networked, mut transform) in cube_query.iter_mut() {
|
||||
for (networked, mut transform, _) in cube_query.iter_mut() {
|
||||
if lock_registry.is_locked_by(networked.network_id, node_id, node_id) {
|
||||
let rotation_x = Quat::from_rotation_y(bevy_delta.x * sensitivity);
|
||||
let rotation_y = Quat::from_rotation_x(-bevy_delta.y * sensitivity);
|
||||
@@ -126,11 +205,11 @@ fn apply_move_depth(
|
||||
delta: f32,
|
||||
lock_registry: &EntityLockRegistry,
|
||||
node_id: uuid::Uuid,
|
||||
cube_query: &mut Query<(&NetworkedEntity, &mut Transform), With<crate::cube::CubeMarker>>,
|
||||
cube_query: &mut Query<(&NetworkedEntity, &mut Transform, &mut NetworkedSelection), With<crate::cube::CubeMarker>>,
|
||||
) {
|
||||
let sensitivity = 0.1;
|
||||
|
||||
for (networked, mut transform) in cube_query.iter_mut() {
|
||||
for (networked, mut transform, _) in cube_query.iter_mut() {
|
||||
if lock_registry.is_locked_by(networked.network_id, node_id, node_id) {
|
||||
transform.translation.z += delta * sensitivity;
|
||||
}
|
||||
@@ -141,12 +220,99 @@ fn apply_move_depth(
|
||||
fn apply_reset_entity(
|
||||
lock_registry: &EntityLockRegistry,
|
||||
node_id: uuid::Uuid,
|
||||
cube_query: &mut Query<(&NetworkedEntity, &mut Transform), With<crate::cube::CubeMarker>>,
|
||||
cube_query: &mut Query<(&NetworkedEntity, &mut Transform, &mut NetworkedSelection), With<crate::cube::CubeMarker>>,
|
||||
) {
|
||||
for (networked, mut transform) in cube_query.iter_mut() {
|
||||
for (networked, mut transform, _) in cube_query.iter_mut() {
|
||||
if lock_registry.is_locked_by(networked.network_id, node_id, node_id) {
|
||||
transform.translation = Vec3::ZERO;
|
||||
transform.rotation = Quat::IDENTITY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A 3D ray for raycasting
|
||||
struct Ray {
|
||||
origin: Vec3,
|
||||
direction: Vec3,
|
||||
}
|
||||
|
||||
/// Convert screen coordinates to a world-space ray from the camera
|
||||
fn screen_to_world_ray(
|
||||
screen_pos: glam::Vec2,
|
||||
camera: &Camera,
|
||||
camera_transform: &GlobalTransform,
|
||||
window: &Window,
|
||||
) -> Option<Ray> {
|
||||
// Convert screen position to viewport position (0..1 range)
|
||||
let viewport_pos = Vec2::new(screen_pos.x, screen_pos.y);
|
||||
|
||||
// Use Bevy's viewport_to_world method
|
||||
let ray_bevy = camera.viewport_to_world(camera_transform, viewport_pos).ok()?;
|
||||
|
||||
Some(Ray {
|
||||
origin: ray_bevy.origin,
|
||||
direction: *ray_bevy.direction,
|
||||
})
|
||||
}
|
||||
|
||||
/// Test ray-AABB (axis-aligned bounding box) intersection
|
||||
///
|
||||
/// Returns the distance along the ray if there's an intersection, None otherwise.
|
||||
fn ray_aabb_intersection(
|
||||
ray_origin: Vec3,
|
||||
ray_direction: Vec3,
|
||||
aabb_center: Vec3,
|
||||
aabb_half_extents: Vec3,
|
||||
) -> Option<f32> {
|
||||
// Calculate AABB min and max
|
||||
let aabb_min = aabb_center - aabb_half_extents;
|
||||
let aabb_max = aabb_center + aabb_half_extents;
|
||||
|
||||
// Slab method for ray-AABB intersection
|
||||
let mut tmin = f32::NEG_INFINITY;
|
||||
let mut tmax = f32::INFINITY;
|
||||
|
||||
for i in 0..3 {
|
||||
let origin_component = ray_origin[i];
|
||||
let dir_component = ray_direction[i];
|
||||
let min_component = aabb_min[i];
|
||||
let max_component = aabb_max[i];
|
||||
|
||||
if dir_component.abs() < f32::EPSILON {
|
||||
// Ray is parallel to slab, check if origin is within slab
|
||||
if origin_component < min_component || origin_component > max_component {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
// Compute intersection t values for near and far plane
|
||||
let inv_dir = 1.0 / dir_component;
|
||||
let mut t1 = (min_component - origin_component) * inv_dir;
|
||||
let mut t2 = (max_component - origin_component) * inv_dir;
|
||||
|
||||
// Ensure t1 is the near intersection
|
||||
if t1 > t2 {
|
||||
std::mem::swap(&mut t1, &mut t2);
|
||||
}
|
||||
|
||||
// Update tmin and tmax
|
||||
tmin = tmin.max(t1);
|
||||
tmax = tmax.min(t2);
|
||||
|
||||
// Check for intersection failure
|
||||
if tmin > tmax {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If tmin is negative, the ray origin is inside the AABB
|
||||
// Return tmax in that case, otherwise return tmin
|
||||
if tmin < 0.0 {
|
||||
if tmax < 0.0 {
|
||||
return None; // AABB is behind the ray
|
||||
}
|
||||
Some(tmax)
|
||||
} else {
|
||||
Some(tmin)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
//! This demonstrates real-time CRDT synchronization with Apple Pencil input.
|
||||
|
||||
use bevy::prelude::*;
|
||||
// use bevy_egui::EguiPlugin; // Disabled - needs WinitPlugin which we own directly
|
||||
use libmarathon::{
|
||||
engine::{EngineBridge, EngineCore},
|
||||
persistence::{PersistenceConfig, PersistencePlugin},
|
||||
persistence::PersistenceConfig,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -21,14 +20,13 @@ mod session;
|
||||
mod session_ui;
|
||||
mod setup;
|
||||
|
||||
use debug_ui::DebugUiPlugin;
|
||||
use engine_bridge::EngineBridgePlugin;
|
||||
|
||||
mod input;
|
||||
|
||||
use camera::*;
|
||||
use cube::*;
|
||||
use debug_ui::*;
|
||||
use input::*;
|
||||
use rendering::*;
|
||||
use selection::*;
|
||||
use session::*;
|
||||
@@ -80,9 +78,8 @@ fn main() {
|
||||
.disable::<bevy::gilrs::GilrsPlugin>() // We handle gamepad input ourselves
|
||||
);
|
||||
|
||||
// app.add_plugins(EguiPlugin::default()); // Disabled - needs WinitPlugin
|
||||
app.add_plugins(EngineBridgePlugin);
|
||||
app.add_plugins(PersistencePlugin::with_config(
|
||||
// Marathon core plugins (networking, debug UI, persistence)
|
||||
app.add_plugins(libmarathon::MarathonPlugin::new(
|
||||
db_path,
|
||||
PersistenceConfig {
|
||||
flush_interval_secs: 2,
|
||||
@@ -91,13 +88,16 @@ fn main() {
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
|
||||
// App-specific bridge for polling engine events
|
||||
app.add_plugins(EngineBridgePlugin);
|
||||
app.add_plugins(CameraPlugin);
|
||||
app.add_plugins(RenderingPlugin);
|
||||
app.add_plugins(input::InputHandlerPlugin);
|
||||
app.add_plugins(CubePlugin);
|
||||
app.add_plugins(SelectionPlugin);
|
||||
// app.add_plugins(DebugUiPlugin); // Disabled - uses egui
|
||||
// app.add_plugins(SessionUiPlugin); // Disabled - uses egui
|
||||
app.add_plugins(DebugUiPlugin);
|
||||
app.add_plugins(SessionUiPlugin);
|
||||
app.add_systems(Startup, initialize_offline_resources);
|
||||
|
||||
// Run with our executor (unbounded event loop)
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
//! and shows connected peer information.
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_egui::{egui, EguiContexts, EguiPrimaryContextPass};
|
||||
use libmarathon::{
|
||||
debug_ui::{egui, EguiContexts, EguiPrimaryContextPass},
|
||||
engine::{EngineBridge, EngineCommand},
|
||||
networking::{CurrentSession, NodeVectorClock, SessionId},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user