Skip to content

Commit 473e877

Browse files
committed
local <-> world light transform preserve in between light attachment
1 parent a5f4e1a commit 473e877

3 files changed

Lines changed: 54 additions & 48 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ js-sys = "0.3"
3131
[features]
3232
webgpu = ["bevy/webgpu"]
3333
webgl2 = ["bevy/webgl2"]
34+
dev-track-location = ["bevy/track_location"]
3435

3536
[lib]
3637
# need both, cdylib for wasm, rlib for main.rs

src/lib.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@ pub(crate) mod parse;
1010
pub(crate) mod structure;
1111

1212
use crate::client::{poll_websocket_stream, setup_websocket_stream};
13-
use crate::io::{
14-
handle_file_drag_drop, load_default_crystal, load_dropped_file, update_crystal_from_file,
15-
FileDragDrop,
16-
};
13+
use crate::io::{handle_file_drag_drop, load_dropped_file, update_crystal_from_file, FileDragDrop};
1714
use crate::structure::{update_crystal_system, UpdateStructure};
1815
use crate::ui::{
1916
camera_controls, handle_load_default_button, refresh_atoms_system,
20-
reset_camera_button_interaction, setup_cameras, setup_file_ui, setup_scene,
17+
reset_camera_button_interaction, setup_cameras, setup_file_ui, setup_light,
2118
toggle_light_attachment, update_file_ui, update_scene,
2219
};
2320
use crate::ui::{setup_buttons, spawn_axis};
@@ -50,10 +47,10 @@ pub fn run_app() {
5047
spawn_axis,
5148
setup_buttons,
5249
setup_file_ui,
53-
setup_scene,
5450
setup_websocket_stream,
55-
)
51+
),
5652
)
53+
.add_systems(Startup, (setup_light).after(setup_cameras))
5754
.add_systems(
5855
Update,
5956
(

src/ui.rs

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ pub(crate) fn update_file_ui(
9191
if let Some(path) = file_drag_drop.dragged_file() {
9292
if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) {
9393
// Update the text content
94-
**text = format!("Loaded: {}", file_name);
94+
**text = format!("Loaded: {file_name}");
9595
}
9696
} else {
9797
**text = "Drag and drop an XYZ file here to visualize".to_string();
@@ -127,10 +127,6 @@ pub(crate) struct CameraRig {
127127
initial_scale: Vec3,
128128
}
129129

130-
/// Button that resets the camera to its original position/orientation.
131-
#[derive(Component)]
132-
pub(crate) struct CameraButton;
133-
134130
// System to clear existing atoms when new crystal is loaded
135131
#[allow(dead_code)]
136132
pub fn clear_old_atoms(mut commands: Commands, atom_query: Query<Entity, With<AtomEntity>>) {
@@ -140,6 +136,7 @@ pub fn clear_old_atoms(mut commands: Commands, atom_query: Query<Entity, With<At
140136
}
141137

142138
// System to handle button click to load default structure
139+
#[allow(clippy::type_complexity)]
143140
pub(crate) fn handle_load_default_button(
144141
mut interaction_query: Query<
145142
(&Interaction, &mut BackgroundColor),
@@ -164,16 +161,6 @@ pub(crate) fn handle_load_default_button(
164161
}
165162
}
166163

167-
// System to set up the 3D scene
168-
pub(crate) fn setup_scene(mut commands: Commands) {
169-
// Add ambient light
170-
commands.insert_resource(AmbientLight {
171-
color: Color::WHITE,
172-
brightness: 0.3,
173-
..default()
174-
});
175-
}
176-
177164
// System to respawn atoms when crystal changes
178165
pub(crate) fn update_scene(
179166
mut commands: Commands,
@@ -281,19 +268,7 @@ pub fn setup_cameras(mut commands: Commands, windows: Query<&Window>) {
281268
})
282269
.id();
283270

284-
let light_entity = commands
285-
.spawn((
286-
DirectionalLight {
287-
shadows_enabled: true,
288-
..default()
289-
},
290-
Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_4)),
291-
ChildOf(camera_entity),
292-
))
293-
.id();
294-
295271
commands.insert_resource(MainCameraEntity(camera_entity));
296-
commands.insert_resource(MainLightEntity(light_entity));
297272
commands.insert_resource(CameraRig {
298273
target: initial_target,
299274
distance: initial_translation.distance(initial_target),
@@ -304,6 +279,18 @@ pub fn setup_cameras(mut commands: Commands, windows: Query<&Window>) {
304279
});
305280
}
306281

282+
pub(crate) fn setup_light(mut commands: Commands, camera: Res<MainCameraEntity>) {
283+
let light_entity = commands
284+
.spawn((
285+
DirectionalLight { ..default() },
286+
Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_4)),
287+
ChildOf(camera.0),
288+
))
289+
.id();
290+
291+
commands.insert_resource(MainLightEntity(light_entity));
292+
}
293+
307294
// Setup minimal UI with toggle buttons
308295
pub fn setup_buttons(mut commands: Commands) {
309296
// buttons at top-left
@@ -334,7 +321,7 @@ pub fn setup_buttons(mut commands: Commands) {
334321
))
335322
.with_children(|button| {
336323
button.spawn((
337-
Text::new("Light: Detached"),
324+
Text::new("light not follow cam"),
338325
TextFont {
339326
font: default(),
340327
font_size: 12.0,
@@ -571,33 +558,54 @@ pub fn toggle_light_attachment(
571558
),
572559
(Changed<Interaction>, With<LightAttachmentButton>),
573560
>,
561+
q_trans: Query<&GlobalTransform>,
574562
mut texts: Query<&mut Text>,
575563
) {
576564
for (interaction, mut background, mut button_state, children) in &mut interactions {
577565
match interaction {
578566
Interaction::Pressed => {
579567
*background = BackgroundColor(Color::srgb(0.25, 0.25, 0.25));
580568

569+
let old_state = button_state.attached;
570+
let new_state = !button_state.attached;
571+
572+
button_state.attached = new_state;
573+
574+
if old_state {
575+
let light_trans = q_trans
576+
.get(light.0)
577+
.expect("light must have global transform when disattach");
578+
let camera_trans = q_trans
579+
.get(camera.0)
580+
.expect("camera must have global transform");
581+
let local = light_trans.reparented_to(camera_trans);
582+
commands
583+
.entity(light.0)
584+
.insert(local)
585+
.insert(ChildOf(camera.0));
586+
info!("Light attached to camera");
587+
} else {
588+
let glb_trans = q_trans
589+
.get(light.0)
590+
.expect("light must have global transform when disattach");
591+
commands
592+
.entity(light.0)
593+
// preserve the world transform when detach
594+
.insert(Transform::from(*glb_trans))
595+
.remove::<ChildOf>();
596+
info!("Light detached from camera");
597+
}
598+
581599
// Update the text inside the button
582600
for child in children.iter() {
583601
if let Ok(mut text) = texts.get_mut(child) {
584-
text.0 = if button_state.attached {
585-
"Light: Attached".into()
602+
text.0 = if new_state {
603+
"light follow cam".into()
586604
} else {
587-
"Light: Detached".into()
605+
"light not follow cam".into()
588606
};
589607
}
590608
}
591-
592-
button_state.attached = !button_state.attached;
593-
594-
if button_state.attached {
595-
commands.entity(light.0).insert(ChildOf(camera.0));
596-
info!("Light attached to camera");
597-
} else {
598-
commands.entity(light.0).remove::<ChildOf>();
599-
info!("Light detached from camera");
600-
}
601609
}
602610
Interaction::Hovered => {
603611
*background = BackgroundColor(Color::srgb(0.2, 0.2, 0.2));

0 commit comments

Comments
 (0)