From 580f0dee81742b6270bfc22a158cd45fd5b4e0ec Mon Sep 17 00:00:00 2001 From: Danny McGee Date: Mon, 13 Nov 2023 19:29:06 -0500 Subject: [PATCH] Feature flag, conditional compilation attributes --- studio/Cargo.toml | 2 + studio/project.json | 10 ++-- studio/src/lib.rs | 41 ++++++++++---- studio/src/math/interp/mod.rs | 2 +- studio/src/scene.rs | 104 ++++++++++++++++++++-------------- 5 files changed, 102 insertions(+), 57 deletions(-) diff --git a/studio/Cargo.toml b/studio/Cargo.toml index 2324107..68d81e3 100644 --- a/studio/Cargo.toml +++ b/studio/Cargo.toml @@ -34,6 +34,8 @@ web-sys = { version = "0.3.65", optional = true, features = [ ] } [features] +default = [] +headless = [] wasm = [ "dep:console_error_panic_hook", "dep:js-sys", diff --git a/studio/project.json b/studio/project.json index 7ec0755..618b979 100644 --- a/studio/project.json +++ b/studio/project.json @@ -9,13 +9,15 @@ "targets": { "build": { "executor": "@nxrs/cargo:build", - "inputs": ["assets"], - "options": { - "release": false - }, + "options": {}, "configurations": { "production": { "release": true + }, + "headless": { + "bin": "studio-standalone", + "release": true, + "features": "headless" } } }, diff --git a/studio/src/lib.rs b/studio/src/lib.rs index d7a06eb..684824b 100644 --- a/studio/src/lib.rs +++ b/studio/src/lib.rs @@ -1,29 +1,34 @@ #![allow(clippy::type_complexity, clippy::too_many_arguments)] use bevy::prelude::*; -use bevy_mod_picking::DefaultPickingPlugins; -use camera_controller::CameraControllerPlugin; -use debug::DebugPlugin; -use input::StudioInputPlugin; -use interaction::InteractionPlugin; -use math::InterpolationPlugin; -use scene::ScenePlugin; -use skybox::SkyboxPlugin; -use ui::UiLayerPlugin; -use viewport::ViewportSize; use wasm_bindgen::prelude::*; +#[cfg(not(feature = "headless"))] mod camera_controller; +#[cfg(not(feature = "headless"))] mod debug; +#[cfg(not(feature = "headless"))] mod design_system; +#[cfg(not(feature = "headless"))] mod input; +#[cfg(not(feature = "headless"))] mod interaction; mod math; mod scene; mod skybox; +#[cfg(not(feature = "headless"))] mod ui; mod viewport; +#[cfg(not(feature = "headless"))] +use crate::{ + camera_controller::CameraControllerPlugin, debug::DebugPlugin, input::StudioInputPlugin, + interaction::InteractionPlugin, ui::UiLayerPlugin, +}; +use crate::{ + math::InterpolationPlugin, scene::ScenePlugin, skybox::SkyboxPlugin, viewport::ViewportSize, +}; + #[cfg(feature = "wasm")] mod wasm; @@ -40,6 +45,7 @@ pub fn init(viewport_size: ViewportSize) { #[cfg(feature = "wasm")] app.add_plugins(viewport::wasm::ViewportPlugin); + #[cfg(not(feature = "headless"))] app.add_plugins(( DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { @@ -61,4 +67,19 @@ pub fn init(viewport_size: ViewportSize) { UiLayerPlugin, )) .run(); + + #[cfg(feature = "headless")] + app.add_plugins(( + bevy::log::LogPlugin::default(), + bevy::core::TaskPoolPlugin::default(), + bevy::core::TypeRegistrationPlugin, + bevy::time::TimePlugin, + bevy::transform::TransformPlugin, + bevy::hierarchy::HierarchyPlugin, + bevy::diagnostic::DiagnosticsPlugin, + InterpolationPlugin, + ScenePlugin, + SkyboxPlugin, + )) + .run(); } diff --git a/studio/src/math/interp/mod.rs b/studio/src/math/interp/mod.rs index 5944eaf..425e4f1 100644 --- a/studio/src/math/interp/mod.rs +++ b/studio/src/math/interp/mod.rs @@ -6,12 +6,12 @@ pub use traits::*; mod ecs; mod traits; +#[allow(dead_code)] #[derive(Clone, Copy, Debug)] pub enum Easing { Linear, Sine, SineOut, - #[allow(dead_code)] SineIn, } diff --git a/studio/src/scene.rs b/studio/src/scene.rs index a31c510..3c2e824 100644 --- a/studio/src/scene.rs +++ b/studio/src/scene.rs @@ -1,35 +1,43 @@ +#[cfg(not(feature = "headless"))] use bevy::{ asset::LoadState, + gltf, + pbr::NotShadowCaster, + reflect::{TypePath, TypeUuid}, + render::render_resource::{AsBindGroup, ShaderRef}, + utils::HashSet, +}; +use bevy::{ core_pipeline::{ bloom::{BloomCompositeMode, BloomPrefilterSettings, BloomSettings}, fxaa::Fxaa, tonemapping::Tonemapping, }, - gltf::{self, Gltf}, + gltf::Gltf, math::vec3, - pbr::NotShadowCaster, prelude::*, - reflect::{TypePath, TypeUuid}, - render::render_resource::{AsBindGroup, ShaderRef}, - utils::HashSet, }; + +#[cfg(not(feature = "headless"))] use bevy_mod_picking::PickableBundle; +#[cfg(not(feature = "headless"))] use crate::{ camera_controller::{CameraController, CameraControllerBundle, CameraMotionSystem}, interaction::Selectable, - skybox::SkyboxId, - MainCamera, }; +use crate::{skybox::SkyboxId, MainCamera}; pub struct ScenePlugin; impl Plugin for ScenePlugin { fn build(&self, app: &mut App) { app.insert_resource(ClearColor(Color::hsl(0.0, 0.0, 0.25))) - .insert_resource(Msaa::Off) + .add_systems(Startup, initialize); + + #[cfg(not(feature = "headless"))] + app.insert_resource(Msaa::Off) .add_plugins(MaterialPlugin::::default()) - .add_systems(Startup, initialize) .add_systems( Update, mark_scene_objects.run_if( @@ -45,6 +53,7 @@ impl Plugin for ScenePlugin { } } +#[cfg(not(feature = "headless"))] #[derive(Component)] struct WorldGridPlane; @@ -57,8 +66,8 @@ struct Inspectable; fn initialize( mut cmd: Commands, assets: Res, - mut ra_meshes: ResMut>, - mut ra_mat_proc_grid: ResMut>, + #[cfg(not(feature = "headless"))] mut ra_meshes: ResMut>, + #[cfg(not(feature = "headless"))] mut ra_mat_proc_grid: ResMut>, ) { // Skybox cmd.insert_resource(SkyboxId("test_scene".into())); @@ -89,6 +98,7 @@ fn initialize( }, ..default() }, + #[cfg(not(feature = "headless"))] CameraControllerBundle::new(focus, camera_xform.translation), BloomSettings { composite_mode: BloomCompositeMode::EnergyConserving, @@ -106,6 +116,7 @@ fn initialize( // TODO: get Blender to export enough metadata to automate light placements cmd.spawn(( Name::new("Ceiling Light"), + #[cfg(not(feature = "headless"))] Selectable, Inspectable, PointLightBundle { @@ -124,6 +135,7 @@ fn initialize( // Monitor glow cmd.spawn(( Name::new("Monitor Glow"), + #[cfg(not(feature = "headless"))] Selectable, Inspectable, PointLightBundle { @@ -140,43 +152,48 @@ fn initialize( )); // Plane - let plane = ra_meshes.add(Mesh::from(shape::Plane { - size: 200.0, - subdivisions: 0, - })); - let proc_grid = ra_mat_proc_grid.add(ProceduralGridMaterial {}); + #[cfg(not(feature = "headless"))] + { + let plane = ra_meshes.add(Mesh::from(shape::Plane { + size: 200.0, + subdivisions: 0, + })); + let proc_grid = ra_mat_proc_grid.add(ProceduralGridMaterial {}); - cmd.spawn(( - Name::new("World Grid"), - SpatialBundle::from_transform(Transform::IDENTITY), - WorldGridPlane, - )) - .with_children(|parent| { - parent.spawn(( - MaterialMeshBundle { - mesh: plane.clone(), - material: proc_grid.clone(), - transform: Transform::IDENTITY, - ..default() - }, - NotShadowCaster, - )); - parent.spawn(( - MaterialMeshBundle { - mesh: plane, - material: proc_grid, - transform: Transform::IDENTITY.looking_to(Vec3::Z, Vec3::NEG_Y), - ..default() - }, - NotShadowCaster, - )); - }); + cmd.spawn(( + Name::new("World Grid"), + SpatialBundle::from_transform(Transform::IDENTITY), + WorldGridPlane, + )) + .with_children(|parent| { + parent.spawn(( + MaterialMeshBundle { + mesh: plane.clone(), + material: proc_grid.clone(), + transform: Transform::IDENTITY, + ..default() + }, + NotShadowCaster, + )); + parent.spawn(( + MaterialMeshBundle { + mesh: plane, + material: proc_grid, + transform: Transform::IDENTITY.looking_to(Vec3::Z, Vec3::NEG_Y), + ..default() + }, + NotShadowCaster, + )); + }); + } } +#[cfg(not(feature = "headless"))] fn gltf_loaded(assets: Res, gltf: Res) -> bool { assets.get_load_state(&**gltf) == LoadState::Loaded } +#[cfg(not(feature = "headless"))] fn gltf_scene_spawned( r_assets: Res, r_gltf: Res, @@ -200,6 +217,7 @@ fn gltf_scene_spawned( }) } +#[cfg(not(feature = "headless"))] fn mark_scene_objects( mut cmd: Commands, r_gltf: Res, @@ -255,7 +273,6 @@ fn mark_scene_objects( }) .collect::>(); - // If the sets match, this is the entity that represents the glTF mesh if prims_from_gltf == prims_from_ecs { cmd.entity(ent_parent).insert(Selectable); @@ -271,6 +288,7 @@ fn mark_scene_objects( cmd.remove_resource::(); } +#[cfg(not(feature = "headless"))] fn move_plane_with_camera( mut plane: Query<(&WorldGridPlane, &mut Transform)>, camera: Query<&CameraController, Changed>, @@ -283,10 +301,12 @@ fn move_plane_with_camera( } } +#[cfg(not(feature = "headless"))] #[derive(AsBindGroup, Clone, TypePath, TypeUuid)] #[uuid = "CE331A13-F247-4F5B-A75E-670B2BD5776A"] struct ProceduralGridMaterial {} +#[cfg(not(feature = "headless"))] impl Material for ProceduralGridMaterial { fn fragment_shader() -> ShaderRef { "shaders/procedural_grid.wgsl".into()