From 7f32cbcdcce34e09e34fc68cd1d4e9b81f7e9671 Mon Sep 17 00:00:00 2001 From: Kayh Date: Thu, 22 Feb 2024 19:45:17 -0500 Subject: [PATCH] move bevy examples to web crate --- .github/workflows/deploy.yml | 35 +++ .gitignore | 1 + Cargo.lock | 14 +- Cargo.toml | 2 +- bevy_gltf_kun/Cargo.toml | 7 +- bevy_gltf_kun/examples/bevy_physics.rs | 230 ------------------ flake.nix | 31 ++- index.html | 23 ++ web/Cargo.toml | 15 ++ .../examples/bevy_gltf.rs => web/src/main.rs | 71 ++++-- 10 files changed, 164 insertions(+), 265 deletions(-) create mode 100644 .github/workflows/deploy.yml delete mode 100644 bevy_gltf_kun/examples/bevy_physics.rs create mode 100644 index.html create mode 100644 web/Cargo.toml rename bevy_gltf_kun/examples/bevy_gltf.rs => web/src/main.rs (80%) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..0ef4f67 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,35 @@ +name: Deploy + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - uses: Swatinem/rust-cache@v2 + - run: nix build .#web + - uses: actions/configure-pages@v4 + - uses: actions/upload-pages-artifact@v3 + with: + path: "result/web" + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 93490c0..2afa2db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /assets/temp +/dist /result /target diff --git a/Cargo.lock b/Cargo.lock index 36bcfe5..5cb0753 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -661,8 +661,6 @@ name = "bevy_gltf_kun" version = "0.0.6" dependencies = [ "bevy", - "bevy_egui", - "bevy_panorbit_camera", "bevy_xpbd_3d", "glam", "gltf_kun", @@ -4149,6 +4147,18 @@ version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +[[package]] +name = "web" +version = "0.0.6" +dependencies = [ + "bevy", + "bevy_egui", + "bevy_gltf_kun", + "bevy_panorbit_camera", + "bevy_xpbd_3d", + "gltf_kun", +] + [[package]] name = "web-sys" version = "0.3.67" diff --git a/Cargo.toml b/Cargo.toml index 54b889e..ec660c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["bevy_gltf_kun", "gltf_kun"] +members = ["bevy_gltf_kun", "gltf_kun", "web"] [workspace.package] version = "0.0.6" diff --git a/bevy_gltf_kun/Cargo.toml b/bevy_gltf_kun/Cargo.toml index c8a1449..406abdb 100644 --- a/bevy_gltf_kun/Cargo.toml +++ b/bevy_gltf_kun/Cargo.toml @@ -23,9 +23,4 @@ gltf_kun = { version = "0.0.6", path = "../gltf_kun" } serde_json.workspace = true thiserror.workspace = true -bevy_xpbd_3d = { version = "0.4.2", default-features = false, features = ["3d", "f32", "parry-f32", "debug-plugin", "parallel"], optional = true } - -[dev-dependencies] -bevy = "0.13.0" -bevy_egui = "0.25.0" -bevy_panorbit_camera = "0.14.0" +bevy_xpbd_3d = { version = "0.4.2", optional = true } diff --git a/bevy_gltf_kun/examples/bevy_physics.rs b/bevy_gltf_kun/examples/bevy_physics.rs deleted file mode 100644 index 591686d..0000000 --- a/bevy_gltf_kun/examples/bevy_physics.rs +++ /dev/null @@ -1,230 +0,0 @@ -use std::path::Path; - -use bevy::{core::FrameCount, prelude::*}; -use bevy_egui::{EguiContexts, EguiPlugin}; -use bevy_gltf_kun::{ - export::gltf::{GltfExport, GltfExportResult}, - GltfKunPlugin, -}; -use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin}; -use bevy_xpbd_3d::prelude::*; -use gltf_kun::{extensions::DefaultExtensions, io::format::glb::GlbIO}; - -const ASSETS_DIR: &str = "../assets"; -const CARGO_MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR"); - -const MODELS: &[&str] = &["DynamicBox.gltf"]; - -fn main() { - App::new() - .insert_resource(ClearColor(Color::rgb(0.1, 0.1, 0.2))) - .add_plugins(( - DefaultPlugins.set(AssetPlugin { - file_path: ASSETS_DIR.to_string(), - ..default() - }), - EguiPlugin, - GltfKunPlugin::::default(), - PanOrbitCameraPlugin, - PhysicsDebugPlugin::default(), - PhysicsPlugins::default(), - )) - .init_resource::() - .init_resource::() - .add_event::() - .add_systems(Startup, setup) - .add_systems(Update, (ui, spawn_model, export, reload, get_result)) - .run(); -} - -#[derive(Component)] -struct SceneMarker; - -#[derive(Event)] -struct LoadScene(String); - -#[derive(Default, Resource)] -struct SelectedModel(String); - -#[derive(Default, Resource)] -struct ExportedPath(String); - -fn setup(mut commands: Commands, mut writer: EventWriter) { - commands.spawn(( - Camera3dBundle { - transform: Transform::from_xyz(2.0, 5.0, 20.0), - ..default() - }, - PanOrbitCamera::default(), - )); - - commands.spawn(DirectionalLightBundle { - transform: Transform::from_xyz(4.0, 7.0, 3.0), - ..default() - }); - - commands.spawn(( - RigidBody::Static, - Collider::cuboid(10.0, 0.01, 10.0), - TransformBundle::from_transform(Transform::from_xyz(0.0, -5.0, 0.0)), - )); - - writer.send(LoadScene(MODELS[0].to_string())); -} - -fn ui( - mut contexts: EguiContexts, - mut writer: EventWriter, - mut selected: ResMut, - mut exported: ResMut, -) { - if selected.0.is_empty() { - selected.0 = MODELS[0].to_string(); - } - - bevy_egui::egui::Window::new("Controls").show(contexts.ctx_mut(), |ui| { - ui.label("Click and drag to orbit camera"); - ui.label("Scroll to zoom camera"); - ui.label("Press 'e' to test export"); - ui.label("Press 'r' to re-load the scene"); - - ui.separator(); - - bevy_egui::egui::ComboBox::from_label("Model") - .selected_text(selected.0.as_str()) - .show_ui(ui, |ui| { - for model in MODELS { - if ui - .selectable_label(selected.0.as_str() == *model, *model) - .clicked() - { - selected.0 = model.to_string(); - exported.0.clear(); - writer.send(LoadScene(model.to_string())); - } - } - }); - }); -} - -fn spawn_model( - asset_server: Res, - mut commands: Commands, - mut events: EventReader, - scenes: Query>, -) { - for event in events.read() { - for entity in scenes.iter() { - commands.entity(entity).despawn_recursive(); - } - - info!("Spawning model {}", event.0); - - commands.spawn(( - SceneBundle { - scene: asset_server.load(format!("{}#Scene0", event.0)), - ..default() - }, - SceneMarker, - )); - } -} - -fn export( - mut export: EventWriter>, - mut key_events: EventReader, - scene: Query<&Handle, With>, -) { - for event in key_events.read() { - if !event.char.eq_ignore_ascii_case("e") { - continue; - } - - info!("Exporting scene"); - - let handle = match scene.get_single() { - Ok(handle) => handle, - Err(e) => { - error!("Failed to get scene: {}", e); - return; - } - }; - - export.send(GltfExport::new(handle.clone())); - } -} - -fn reload( - mut writer: EventWriter, - mut key_events: EventReader, - exported: Res, - selected: Res, -) { - for event in key_events.read() { - if !event.char.eq_ignore_ascii_case("r") { - continue; - } - - let mut used_path = exported.0.clone(); - - if used_path.is_empty() { - used_path = selected.0.clone(); - } - - info!("Reloading scene"); - - writer.send(LoadScene(used_path)); - } -} - -fn get_result( - mut exports: ResMut>, - mut writer: EventWriter, - frame: Res, - mut exported_path: ResMut, -) { - for mut event in exports.drain() { - let doc = match event.result { - Ok(doc) => doc, - Err(e) => panic!("Failed to export from Bevy: {}", e), - }; - - let glb = match GlbIO::::export(&mut event.graph, &doc) { - Ok(glb) => glb, - Err(e) => panic!("Failed to export to glb: {}", e), - }; - - let temp_dir = Path::new(CARGO_MANIFEST_DIR) - .join(ASSETS_DIR) - .join(TEMP_FOLDER); - - // Delete and re-create temp dir - if temp_dir.exists() { - std::fs::remove_dir_all(temp_dir.clone()).expect("Failed to delete temp directory"); - } - - std::fs::create_dir_all(temp_dir).expect("Failed to create temp directory"); - - // Write glb to temp dir - let file_path = Path::new(TEMP_FOLDER).join(temp_file(frame.0)); - let file_path_str = file_path.to_str().unwrap().to_string(); - exported_path.0 = file_path_str.clone(); - - info!("Writing glb to {}", file_path.display()); - - let full_path = Path::new(CARGO_MANIFEST_DIR) - .join(ASSETS_DIR) - .join(file_path); - - std::fs::write(full_path, glb.0).expect("Failed to write glb"); - - // Load glb - writer.send(LoadScene(file_path_str)); - } -} - -const TEMP_FOLDER: &str = "temp/bevy_physics"; - -fn temp_file(frame: u32) -> String { - format!("model_{}.glb", frame) -} diff --git a/flake.nix b/flake.nix index c1c864f..7de1438 100644 --- a/flake.nix +++ b/flake.nix @@ -55,12 +55,8 @@ [ binaryen cargo-auditable - cargo-component - clang - cmake nodePackages.prettier pkg-config - protobuf trunk wasm-bindgen-cli wasm-tools @@ -78,6 +74,11 @@ cargoArtifacts = craneLib.buildDepsOnly (commonArgs // { pname = "deps"; }); + cargoArtifactsWasm = craneLib.buildDepsOnly (commonArgs // { + pname = "deps-wasm"; + doCheck = false; + }); + cargoClippy = craneLib.cargoClippy (commonArgs // { inherit cargoArtifacts; pname = "clippy"; @@ -91,22 +92,40 @@ bevy_gltf_kun = craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; pname = "bevy_gltf_kun"; + cargoExtraArgs = "-p bevy_gltf_kun"; }); gltf_kun = craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; pname = "gltf_kun"; + cargoExtraArgs = "-p gltf_kun"; + }); + + web = craneLib.buildTrunkPackage (commonArgs // { + inherit cargoArtifactsWasm; + pname = "web"; + cargoExtraArgs = "-p web --target wasm32-unknown-unknown"; + + src = lib.cleanSourceWith { + src = ./.; + filter = path: type: + (lib.hasSuffix ".html" path) || (lib.hasInfix "/assets/" path) + || (craneLib.filterCargoSources path type); + }; + + wasm-bindgen-cli = pkgs.wasm-bindgen-cli; }); in { - checks = { inherit gltf_kun bevy_gltf_kun cargoClippy cargoDoc; }; + checks = { inherit gltf_kun bevy_gltf_kun web cargoClippy cargoDoc; }; packages = { bevy_gltf_kun = bevy_gltf_kun; gltf_kun = gltf_kun; + web = web; default = pkgs.symlinkJoin { name = "all"; - paths = [ bevy_gltf_kun gltf_kun ]; + paths = [ bevy_gltf_kun gltf_kun web ]; }; }; diff --git a/index.html b/index.html new file mode 100644 index 0000000..88446c5 --- /dev/null +++ b/index.html @@ -0,0 +1,23 @@ + + + + + + + + gltf-kun >.< + + + + + diff --git a/web/Cargo.toml b/web/Cargo.toml new file mode 100644 index 0000000..e6dbe09 --- /dev/null +++ b/web/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "web" +publish = false +version.workspace = true +license.workspace = true +repository.workspace = true +edition.workspace = true + +[dependencies] +bevy = "0.13.0" +bevy_egui = "0.25.0" +bevy_gltf_kun = { path = "../bevy_gltf_kun" } +bevy_panorbit_camera = "0.14.0" +bevy_xpbd_3d = "0.4.2" +gltf_kun = { path = "../gltf_kun" } diff --git a/bevy_gltf_kun/examples/bevy_gltf.rs b/web/src/main.rs similarity index 80% rename from bevy_gltf_kun/examples/bevy_gltf.rs rename to web/src/main.rs index 1da5e59..e8fe0d8 100644 --- a/bevy_gltf_kun/examples/bevy_gltf.rs +++ b/web/src/main.rs @@ -1,6 +1,6 @@ use std::{fmt::Display, path::Path}; -use bevy::{core::FrameCount, gltf::Gltf, prelude::*}; +use bevy::{asset::AssetMetaCheck, core::FrameCount, gltf::Gltf, prelude::*}; use bevy_egui::{egui::ComboBox, EguiContexts, EguiPlugin}; use bevy_gltf_kun::{ export::gltf::{GltfExport, GltfExportResult}, @@ -8,15 +8,21 @@ use bevy_gltf_kun::{ GltfKunPlugin, }; use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin}; +use bevy_xpbd_3d::prelude::*; use gltf_kun::{extensions::DefaultExtensions, io::format::glb::GlbIO}; const ASSETS_DIR: &str = "../assets"; const CARGO_MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR"); -const MODELS: &[&str] = &["BoxTextured.glb", "BoxTextured/BoxTextured.gltf"]; +const MODELS: &[&str] = &[ + "BoxTextured.glb", + "BoxTextured/BoxTextured.gltf", + "DynamicBox.gltf", +]; fn main() { App::new() + .insert_resource(AssetMetaCheck::Never) .insert_resource(ClearColor(Color::rgb(0.1, 0.1, 0.2))) .add_plugins(( DefaultPlugins.set(AssetPlugin { @@ -26,6 +32,8 @@ fn main() { EguiPlugin, GltfKunPlugin::::default(), PanOrbitCameraPlugin, + PhysicsDebugPlugin::default(), + PhysicsPlugins::default(), )) .add_event::() .add_event::() @@ -302,32 +310,55 @@ fn get_result( Err(e) => panic!("Failed to export to glb: {}", e), }; - let temp_dir = Path::new(CARGO_MANIFEST_DIR) - .join(ASSETS_DIR) - .join(TEMP_FOLDER); + #[cfg(target_family = "wasm")] + { + // Write glb to temp dir + let file_path = temp_file(frame.0); + let file_path_str = file_path.clone(); + exported_path.0 = file_path_str.clone(); - // Delete and re-create temp dir - if temp_dir.exists() { - std::fs::remove_dir_all(temp_dir.clone()).expect("Failed to delete temp directory"); + info!("Writing glb to {}", file_path); + + let full_path = Path::new(CARGO_MANIFEST_DIR) + .join(ASSETS_DIR) + .join(file_path); + + let mut file = std::io::BufWriter::new(std::fs::File::create(full_path).unwrap()); + file.write_all(&glb.0).unwrap(); + + // Load glb + writer.send(LoadModel(file_path_str)); } - std::fs::create_dir_all(temp_dir).expect("Failed to create temp directory"); + #[cfg(not(target_family = "wasm"))] + { + let temp_dir = Path::new(CARGO_MANIFEST_DIR) + .join(ASSETS_DIR) + .join(TEMP_FOLDER); - // Write glb to temp dir - let file_path = Path::new(TEMP_FOLDER).join(temp_file(frame.0)); - let file_path_str = file_path.to_str().unwrap().to_string(); - exported_path.0 = file_path_str.clone(); + // Delete and re-create temp dir + if temp_dir.exists() { + std::fs::remove_dir_all(temp_dir.clone()).expect("Failed to delete temp directory"); + } - info!("Writing glb to {}", file_path.display()); + std::fs::create_dir_all(temp_dir).expect("Failed to create temp directory"); - let full_path = Path::new(CARGO_MANIFEST_DIR) - .join(ASSETS_DIR) - .join(file_path); + // Write glb to temp dir + let file_path = Path::new(TEMP_FOLDER).join(temp_file(frame.0)); + let file_path_str = file_path.to_str().unwrap().to_string(); + exported_path.0 = file_path_str.clone(); - std::fs::write(full_path, glb.0).expect("Failed to write glb"); + info!("Writing glb to {}", file_path.display()); - // Load glb - writer.send(LoadModel(file_path_str)); + let full_path = Path::new(CARGO_MANIFEST_DIR) + .join(ASSETS_DIR) + .join(file_path); + + std::fs::write(full_path, glb.0).expect("Failed to write glb"); + + // Load glb + writer.send(LoadModel(file_path_str)); + } } }