@@ -723,8 +723,15 @@ type BevyMonUnifiedLogger = UnifiedLoggerWrite;
723723fn setup_copper ( mut commands : Commands ) {
724724 #[ allow( clippy:: identity_op) ]
725725 const LOG_SLAB_SIZE : Option < usize > = Some ( 128 * 1024 * 1024 ) ;
726- let logger_path = "logs/flight_controller_sim.copper" ;
727- if let Some ( parent) = Path :: new ( logger_path) . parent ( )
726+ commands. insert_resource ( build_sim_copper_state (
727+ Path :: new ( "logs/flight_controller_sim.copper" ) ,
728+ LOG_SLAB_SIZE ,
729+ ) ) ;
730+ }
731+
732+ #[ cfg( all( not( target_arch = "wasm32" ) , feature = "sim" ) ) ]
733+ fn build_sim_copper_state ( logger_path : & Path , log_slab_size : Option < usize > ) -> CopperState {
734+ if let Some ( parent) = logger_path. parent ( )
728735 && !parent. exists ( )
729736 {
730737 fs:: create_dir_all ( parent) . expect ( "failed to create logs directory" ) ;
@@ -733,7 +740,7 @@ fn setup_copper(mut commands: Commands) {
733740 let ( clock, clock_mock) = RobotClock :: mock ( ) ;
734741 let mut app = gnss:: FlightControllerSim :: builder ( )
735742 . with_clock ( clock. clone ( ) )
736- . with_log_path ( PathBuf :: from ( logger_path) , LOG_SLAB_SIZE )
743+ . with_log_path ( PathBuf :: from ( logger_path) , log_slab_size )
737744 . expect ( "failed to create logger" )
738745 . with_sim_callback ( & mut default_callback)
739746 . build ( )
@@ -742,11 +749,11 @@ fn setup_copper(mut commands: Commands) {
742749 app. start_all_tasks ( & mut default_callback)
743750 . expect ( "failed to start tasks" ) ;
744751
745- commands . insert_resource ( CopperState {
752+ CopperState {
746753 clock,
747754 clock_mock,
748755 app,
749- } ) ;
756+ }
750757}
751758
752759#[ cfg( feature = "bevymon" ) ]
@@ -1506,11 +1513,29 @@ fn run_copper(
15061513 mut osd_overlay : ResMut < SimOsdOverlay > ,
15071514 mut exit_writer : MessageWriter < AppExit > ,
15081515) {
1509- copper
1510- . clock_mock
1511- . set_value ( physics_time. elapsed ( ) . as_nanos ( ) as u64 ) ;
1512- let vehicle = sim_state. vehicle . clone ( ) ;
1513- let rc = rc_input. clone ( ) ;
1516+ let elapsed_ns = physics_time. elapsed ( ) . as_nanos ( ) as u64 ;
1517+ if let Err ( err) = run_copper_iteration (
1518+ & mut copper,
1519+ elapsed_ns,
1520+ sim_state. vehicle . clone ( ) ,
1521+ rc_input. clone ( ) ,
1522+ & mut motor_commands,
1523+ & mut osd_overlay,
1524+ ) {
1525+ error ! ( "sim loop stopped: {}" , err) ;
1526+ exit_writer. write ( AppExit :: Success ) ;
1527+ }
1528+ }
1529+
1530+ fn run_copper_iteration (
1531+ copper : & mut CopperState ,
1532+ elapsed_ns : u64 ,
1533+ vehicle : SimVehicleState ,
1534+ rc : SimRcInput ,
1535+ motor_commands : & mut SimMotorCommands ,
1536+ osd_overlay : & mut SimOsdOverlay ,
1537+ ) -> CuResult < ( ) > {
1538+ copper. clock_mock . set_value ( elapsed_ns) ;
15141539 sim_battery_set_armed ( rc. armed ) ;
15151540 sim_battery_set_throttle ( rc. throttle ) ;
15161541 sim_gnss_set_vehicle_state (
@@ -1596,10 +1621,7 @@ fn run_copper(
15961621 }
15971622 } ;
15981623
1599- if let Err ( err) = copper. app . run_one_iteration ( & mut sim_callback) {
1600- error ! ( "sim loop stopped: {}" , err) ;
1601- exit_writer. write ( AppExit :: Success ) ;
1602- }
1624+ copper. app . run_one_iteration ( & mut sim_callback)
16031625}
16041626
16051627fn apply_multicopter_dynamics (
@@ -2047,6 +2069,7 @@ fn register_scene_reflect_types(app: &mut App) {
20472069 app. register_type :: < bevy:: prelude:: Handle < bevy:: prelude:: WorldAsset > > ( ) ;
20482070 app. register_type :: < bevy:: gltf:: GltfExtras > ( ) ;
20492071 app. register_type :: < bevy:: gltf:: GltfSceneExtras > ( ) ;
2072+ app. register_type :: < bevy:: gltf:: GltfSceneName > ( ) ;
20502073 app. register_type :: < bevy:: gltf:: GltfMeshExtras > ( ) ;
20512074 app. register_type :: < bevy:: gltf:: GltfMeshName > ( ) ;
20522075 app. register_type :: < bevy:: gltf:: GltfMaterialExtras > ( ) ;
@@ -2351,12 +2374,157 @@ mod tests {
23512374 ) ;
23522375 }
23532376
2377+ #[ cfg( not( target_arch = "wasm32" ) ) ]
2378+ fn build_test_copper_state ( ) -> CopperState {
2379+ let ( clock, clock_mock) = RobotClock :: mock ( ) ;
2380+ let mut sim_callback = default_callback;
2381+ let mut app = gnss:: FlightControllerSim :: builder ( )
2382+ . with_clock ( clock. clone ( ) )
2383+ . with_sim_callback ( & mut sim_callback)
2384+ . build ( )
2385+ . expect ( "failed to create runtime" ) ;
2386+
2387+ app. start_all_tasks ( & mut sim_callback)
2388+ . expect ( "failed to start tasks" ) ;
2389+
2390+ CopperState {
2391+ clock,
2392+ clock_mock,
2393+ app,
2394+ }
2395+ }
2396+
2397+ #[ cfg( not( target_arch = "wasm32" ) ) ]
2398+ fn build_scene_asset_test_app ( register_reflect_types : bool ) -> App {
2399+ let mut app = App :: new ( ) ;
2400+ app. add_plugins ( (
2401+ MinimalPlugins ,
2402+ asset_plugin ( ) ,
2403+ bevy:: world_serialization:: WorldSerializationPlugin ,
2404+ bevy:: image:: ImagePlugin :: default ( ) ,
2405+ bevy:: mesh:: MeshPlugin ,
2406+ bevy:: pbr:: MaterialPlugin :: < bevy:: prelude:: StandardMaterial > :: default ( ) ,
2407+ bevy:: gltf:: GltfPlugin :: default ( ) ,
2408+ ) ) ;
2409+ app. insert_resource ( bevy:: image:: CompressedImageFormatSupport (
2410+ bevy:: image:: CompressedImageFormats :: NONE ,
2411+ ) ) ;
2412+ if register_reflect_types {
2413+ register_scene_reflect_types ( & mut app) ;
2414+ }
2415+ app. finish ( ) ;
2416+ app
2417+ }
2418+
2419+ #[ cfg( not( target_arch = "wasm32" ) ) ]
2420+ fn load_scene_asset_handles (
2421+ app : & mut App ,
2422+ scene_assets : & SceneAssetPaths ,
2423+ ) -> ( Handle < WorldAsset > , Handle < WorldAsset > ) {
2424+ let asset_server = app. world ( ) . resource :: < AssetServer > ( ) ;
2425+ (
2426+ asset_server. load ( GltfAssetLabel :: Scene ( 0 ) . from_asset ( scene_assets. quadcopter . clone ( ) ) ) ,
2427+ asset_server. load ( GltfAssetLabel :: Scene ( 0 ) . from_asset ( scene_assets. city . clone ( ) ) ) ,
2428+ )
2429+ }
2430+
2431+ #[ cfg( not( target_arch = "wasm32" ) ) ]
2432+ fn wait_for_scene_asset_loads (
2433+ app : & mut App ,
2434+ quadcopter_scene : & Handle < WorldAsset > ,
2435+ city_scene : & Handle < WorldAsset > ,
2436+ ) -> bool {
2437+ for _ in 0 ..300 {
2438+ app. update ( ) ;
2439+ let asset_server = app. world ( ) . resource :: < AssetServer > ( ) ;
2440+ if asset_server. is_loaded_with_dependencies ( quadcopter_scene. id ( ) )
2441+ && asset_server. is_loaded_with_dependencies ( city_scene. id ( ) )
2442+ {
2443+ return true ;
2444+ }
2445+ std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 10 ) ) ;
2446+ }
2447+ false
2448+ }
2449+
23542450 #[ test]
23552451 fn sim_world_starts ( ) {
23562452 let mut app = build_world ( true , false ) ;
23572453 app. update ( ) ;
23582454 }
23592455
2456+ #[ cfg( not( target_arch = "wasm32" ) ) ]
2457+ #[ test]
2458+ fn sim_copper_runs_one_iteration ( ) {
2459+ let mut copper = build_test_copper_state ( ) ;
2460+ let mut motors = SimMotorCommands :: default ( ) ;
2461+ let mut osd_overlay = SimOsdOverlay :: default ( ) ;
2462+
2463+ run_copper_iteration (
2464+ & mut copper,
2465+ 0 ,
2466+ SimVehicleState :: default ( ) ,
2467+ SimRcInput :: default ( ) ,
2468+ & mut motors,
2469+ & mut osd_overlay,
2470+ )
2471+ . expect ( "copper sim iteration should keep running" ) ;
2472+
2473+ copper
2474+ . app
2475+ . stop_all_tasks ( & mut default_callback)
2476+ . expect ( "failed to stop tasks" ) ;
2477+ copper
2478+ . app
2479+ . log_shutdown_completed ( )
2480+ . expect ( "failed to log shutdown" ) ;
2481+ }
2482+
2483+ #[ cfg( not( target_arch = "wasm32" ) ) ]
2484+ #[ test]
2485+ fn sim_gltf_world_assets_load ( ) {
2486+ let scene_assets = prepare_scene_assets ( ) ;
2487+ let mut app = build_scene_asset_test_app ( false ) ;
2488+ let ( quadcopter_scene, city_scene) = load_scene_asset_handles ( & mut app, & scene_assets) ;
2489+
2490+ if wait_for_scene_asset_loads ( & mut app, & quadcopter_scene, & city_scene) {
2491+ return ;
2492+ }
2493+
2494+ let asset_server = app. world ( ) . resource :: < AssetServer > ( ) ;
2495+ panic ! (
2496+ "scene assets did not load: quadcopter_loaded={} city_loaded={}" ,
2497+ asset_server. is_loaded_with_dependencies( quadcopter_scene. id( ) ) ,
2498+ asset_server. is_loaded_with_dependencies( city_scene. id( ) )
2499+ ) ;
2500+ }
2501+
2502+ #[ cfg( not( target_arch = "wasm32" ) ) ]
2503+ #[ test]
2504+ fn sim_gltf_world_assets_clone_with_registered_types ( ) {
2505+ let scene_assets = prepare_scene_assets ( ) ;
2506+ let mut app = build_scene_asset_test_app ( true ) ;
2507+ let ( quadcopter_scene, city_scene) = load_scene_asset_handles ( & mut app, & scene_assets) ;
2508+ assert ! (
2509+ wait_for_scene_asset_loads( & mut app, & quadcopter_scene, & city_scene) ,
2510+ "scene assets did not load"
2511+ ) ;
2512+
2513+ let registry = app
2514+ . world ( )
2515+ . resource :: < bevy:: ecs:: reflect:: AppTypeRegistry > ( )
2516+ . clone ( ) ;
2517+ let world_assets = app. world ( ) . resource :: < Assets < WorldAsset > > ( ) ;
2518+ for ( name, handle) in [ ( "quadcopter" , & quadcopter_scene) , ( "city" , & city_scene) ] {
2519+ let world_asset = world_assets
2520+ . get ( handle)
2521+ . unwrap_or_else ( || panic ! ( "{name} world asset did not load" ) ) ;
2522+ world_asset
2523+ . clone_with ( & registry)
2524+ . unwrap_or_else ( |err| panic ! ( "{name} world asset cannot spawn: {err}" ) ) ;
2525+ }
2526+ }
2527+
23602528 #[ test]
23612529 fn sim_world_magnetic_field_is_three_dimensional ( ) {
23622530 let world_mag = Vec3 :: from_array ( WORLD_MAG_FIELD_UT ) ;
0 commit comments