1pub mod camera;
7pub mod lighting;
8pub mod scene;
9
10use bevy::prelude::*;
11use camera::CameraPlugin;
12use lighting::PhotometricLightPlugin;
13use scene::{ScenePlugin, SceneType};
14
15#[derive(Resource, Default)]
17pub struct LdtTimestamp(pub String);
18
19#[derive(Resource)]
21pub struct SceneSettings {
22 pub scene_type: SceneType,
23 pub room_width: f32,
24 pub room_length: f32,
25 pub room_height: f32,
26 pub mounting_height: f32,
27 pub light_intensity: f32,
28 pub show_luminaire: bool,
29 pub show_photometric_solid: bool,
30 pub show_shadows: bool,
31 pub ldt_data: Option<eulumdat::Eulumdat>,
32}
33
34impl Default for SceneSettings {
35 fn default() -> Self {
36 Self {
37 scene_type: SceneType::Room,
38 room_width: 4.0,
39 room_length: 5.0,
40 room_height: 2.8,
41 mounting_height: 2.5,
42 light_intensity: 1000.0,
43 show_luminaire: true,
44 show_photometric_solid: false,
45 show_shadows: false,
46 ldt_data: None,
47 }
48 }
49}
50
51#[cfg(target_arch = "wasm32")]
52const LDT_STORAGE_KEY: &str = "eulumdat_current_ldt";
53#[cfg(target_arch = "wasm32")]
54const LDT_TIMESTAMP_KEY: &str = "eulumdat_ldt_timestamp";
55
56#[cfg(target_arch = "wasm32")]
58pub fn load_from_local_storage() -> Option<eulumdat::Eulumdat> {
59 use wasm_bindgen::JsCast;
60
61 let window = web_sys::window()?;
62 let storage = window.local_storage().ok()??;
63 let ldt_string = storage.get_item(LDT_STORAGE_KEY).ok()??;
64
65 eulumdat::Eulumdat::parse(&ldt_string).ok()
66}
67
68#[cfg(not(target_arch = "wasm32"))]
69pub fn load_from_local_storage() -> Option<eulumdat::Eulumdat> {
70 None
71}
72
73#[cfg(target_arch = "wasm32")]
75pub fn get_ldt_timestamp() -> Option<String> {
76 let window = web_sys::window()?;
77 let storage = window.local_storage().ok()??;
78 storage.get_item(LDT_TIMESTAMP_KEY).ok()?
79}
80
81#[cfg(not(target_arch = "wasm32"))]
82pub fn get_ldt_timestamp() -> Option<String> {
83 None
84}
85
86pub fn load_default_ldt() -> Option<eulumdat::Eulumdat> {
88 #[cfg(target_arch = "wasm32")]
90 {
91 if let Some(ldt) = load_from_local_storage() {
93 return Some(ldt);
94 }
95 let ldt_content = include_str!("../../eulumdat-wasm/templates/road_luminaire.ldt");
97 eulumdat::Eulumdat::parse(ldt_content).ok()
98 }
99
100 #[cfg(not(target_arch = "wasm32"))]
102 {
103 let sample_paths = [
104 "crates/eulumdat-wasm/templates/road_luminaire.ldt",
105 "../eulumdat-wasm/templates/road_luminaire.ldt",
106 "crates/eulumdat-wasm/templates/fluorescent_luminaire.ldt",
107 "../eulumdat-wasm/templates/fluorescent_luminaire.ldt",
108 ];
109
110 for path in sample_paths {
111 if let Ok(ldt) = eulumdat::Eulumdat::from_file(path) {
112 return Some(ldt);
113 }
114 }
115 None
116 }
117}
118
119#[allow(unused_mut, unused_variables)]
121pub fn poll_ldt_changes(
122 mut settings: ResMut<SceneSettings>,
123 mut last_timestamp: ResMut<LdtTimestamp>,
124) {
125 #[cfg(target_arch = "wasm32")]
126 {
127 if let Some(new_timestamp) = get_ldt_timestamp() {
128 if new_timestamp != last_timestamp.0 {
129 if let Some(ldt) = load_from_local_storage() {
131 settings.ldt_data = Some(ldt);
132 last_timestamp.0 = new_timestamp;
133 }
134 }
135 }
136 }
137}
138
139pub fn ui_controls_system(
141 mut settings: ResMut<SceneSettings>,
142 keyboard: Res<ButtonInput<KeyCode>>,
143) {
144 if keyboard.just_pressed(KeyCode::KeyP) {
146 settings.show_photometric_solid = !settings.show_photometric_solid;
147 }
148
149 if keyboard.just_pressed(KeyCode::KeyL) {
151 settings.show_luminaire = !settings.show_luminaire;
152 }
153
154 if keyboard.just_pressed(KeyCode::KeyH) {
156 settings.show_shadows = !settings.show_shadows;
157 }
158
159 if keyboard.just_pressed(KeyCode::Digit1) {
161 settings.scene_type = SceneType::Room;
162 }
163 if keyboard.just_pressed(KeyCode::Digit2) {
164 settings.scene_type = SceneType::Road;
165 }
166 if keyboard.just_pressed(KeyCode::Digit3) {
167 settings.scene_type = SceneType::Parking;
168 }
169 if keyboard.just_pressed(KeyCode::Digit4) {
170 settings.scene_type = SceneType::Outdoor;
171 }
172}
173
174fn setup_with_default_ldt(mut commands: Commands) {
176 let ldt = load_default_ldt();
177 commands.insert_resource(SceneSettings {
178 ldt_data: ldt,
179 ..default()
180 });
181}
182
183#[cfg(target_arch = "wasm32")]
188pub fn run_on_canvas(canvas_selector: &str) {
189 App::new()
190 .add_plugins(DefaultPlugins.set(WindowPlugin {
191 primary_window: Some(Window {
192 title: "Eulumdat 3D Viewer".to_string(),
193 canvas: Some(canvas_selector.to_string()),
194 fit_canvas_to_parent: true,
195 prevent_default_event_handling: false,
196 ..default()
197 }),
198 ..default()
199 }))
200 .add_plugins((CameraPlugin, ScenePlugin, PhotometricLightPlugin))
201 .insert_resource(SceneSettings::default())
202 .insert_resource(LdtTimestamp::default())
203 .add_systems(Startup, setup_with_default_ldt)
204 .add_systems(Update, ui_controls_system)
205 .add_systems(Update, poll_ldt_changes)
206 .run();
207}
208
209#[cfg(not(target_arch = "wasm32"))]
211pub fn run_on_canvas(_canvas_selector: &str) {
212 run_native();
213}
214
215#[cfg(not(target_arch = "wasm32"))]
217pub fn run_native() {
218 App::new()
219 .add_plugins(DefaultPlugins.set(WindowPlugin {
220 primary_window: Some(Window {
221 title: "Eulumdat 3D Viewer".to_string(),
222 resolution: (1280.0, 720.0).into(),
223 ..default()
224 }),
225 ..default()
226 }))
227 .add_plugins((CameraPlugin, ScenePlugin, PhotometricLightPlugin))
228 .insert_resource(SceneSettings::default())
229 .insert_resource(LdtTimestamp::default())
230 .add_systems(Startup, setup_with_default_ldt)
231 .add_systems(Update, ui_controls_system)
232 .add_systems(Update, poll_ldt_changes)
233 .run();
234}
235
236#[cfg(target_arch = "wasm32")]
237pub fn run_native() {
238 run_on_canvas("#bevy-canvas");
240}