Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add 'proper' argument parsing #71

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ bevy_transform_gizmo = { version = "0.9", optional = true }
bincode2 = { version = "2.0", optional = true }
byte-unit = { version = "5.0", optional = true }
bytemuck = "1.14"
clap = { version = "4.4.12", features = ["derive"] }
flate2 = { version = "1.0", optional = true }
flexbuffers = { version = "2.0", optional = true }
half = { version = "2.3.1", optional = true, features = ["serde"] }
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ fn setup_gaussian_cloud(
}
```

### examples

```cargo run --release -- -f <PATH_TO_PLY_OR_GCLOUD_FILE>```

i.e
```cargo run --release -- -f scenes/icecream.gcloud```

## tools

- [ply to gcloud converter](tools/README.md#ply-to-gcloud-converter)
Expand Down
48 changes: 41 additions & 7 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
// #[cfg(target_arch = "wasm32")]
// pub use wasm_bindgen_rayon::init_thread_pool;
use clap::Parser;

#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use std::collections::HashMap;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;

#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug, Parser)]
#[command(about = "bevy gaussian splatting render pipeline plugin", version, long_about = None)]
struct CliArgs {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think merging CliArgs and GaussianSplattingViewer structs would be great (can happen in a future PR though, let me know your bandwidth):

pub struct GaussianSplattingViewer {

e.g. this allows args like --height, --width, --hide_fps, --hide_editor, etc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as for bandwidth, I plan on working / contributing on this project long term. I'll play around with merging the structs and see how it goes.

/// number of random gaussians to generate
#[arg(short, long)]
num_of_gaussians: Option<usize>,

/// number of random particle behaviors to generate
#[arg(short = 'p', long)]
num_of_particle_behaviors: Option<usize>,

/// .gcloud or .ply file to load
#[arg(short = 'f', long)]
filename: Option<String>,
}

#[derive(Debug, Clone, Copy)]
pub enum MainArgs {
NumOfGaussians = 1,
NumOfParticleBehaviors,
Filename,
}

pub fn setup_hooks() {
#[cfg(debug_assertions)]
Expand All @@ -15,14 +39,18 @@ pub fn setup_hooks() {
}
}


#[cfg(not(target_arch = "wasm32"))]
pub fn get_arg(n: usize) -> Option<String> {
std::env::args().nth(n)
pub fn get_arg(arg: MainArgs) -> Option<String> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

converting this function to:
pub fn get_args() -> CliArgs (or pub fn get_args() -> GaussianSplattingViewer with the above suggestion) would simplify argument retrieval and avoid duplicate parse calls.

let args = CliArgs::parse();
match arg {
MainArgs::NumOfGaussians => args.num_of_gaussians.map(|n| n.to_string()),
MainArgs::NumOfParticleBehaviors => args.num_of_particle_behaviors.map(|n| n.to_string()),
MainArgs::Filename => args.filename,
}
}

#[cfg(target_arch = "wasm32")]
pub fn get_arg(n: usize) -> Option<String> {
pub fn get_arg(arg: MainArgs) -> Option<String> {
let window = web_sys::window()?;
let location = window.location();
let search = location.search().ok()?;
Expand All @@ -35,5 +63,11 @@ pub fn get_arg(n: usize) -> Option<String> {
.map(|v| (v[0].to_string(), v[1].to_string()))
.collect::<std::collections::HashMap<_, _>>();

args.get(&format!("arg{}", n)).cloned()
let arg_value = args.get(&format!("arg{}", arg as u8)).cloned();
match arg {
MainArgs::NumOfGaussians | MainArgs::NumOfParticleBehaviors => {
arg_value.and_then(|a| a.parse::<usize>().ok().map(|_| a))
}
_ => arg_value,
}
}
132 changes: 41 additions & 91 deletions viewer/viewer.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,31 @@
use bevy::{
prelude::*,
app::AppExit,
core::Name,
core_pipeline::tonemapping::Tonemapping,
diagnostic::{
DiagnosticsStore,
FrameTimeDiagnosticsPlugin,
},
diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin},
prelude::*,
};
use bevy_inspector_egui::quick::WorldInspectorPlugin;
use bevy_panorbit_camera::{
PanOrbitCamera,
PanOrbitCameraPlugin,
};
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};

use bevy_gaussian_splatting::{
GaussianCloud,
GaussianSplattingBundle,
GaussianSplattingPlugin,
random_gaussians,
utils::{
get_arg,
setup_hooks,
},
utils::{get_arg, setup_hooks, MainArgs},
GaussianCloud, GaussianSplattingBundle, GaussianSplattingPlugin,
};

#[cfg(feature = "material_noise")]
use bevy_gaussian_splatting::material::noise::NoiseMaterial;

#[cfg(feature = "morph_particles")]
use bevy_gaussian_splatting::morph::particle::{
ParticleBehaviors,
random_particle_behaviors,
};
use bevy_gaussian_splatting::morph::particle::{random_particle_behaviors, ParticleBehaviors};

#[cfg(feature = "query_select")]
use bevy_gaussian_splatting::query::select::{
InvertSelectionEvent,
SaveSelectionEvent,
};
use bevy_gaussian_splatting::query::select::{InvertSelectionEvent, SaveSelectionEvent};

#[cfg(feature = "query_sparse")]
use bevy_gaussian_splatting::query::sparse::SparseSelect;


pub struct GaussianSplattingViewer {
pub editor: bool,
pub esc_close: bool,
Expand All @@ -66,36 +48,26 @@ impl Default for GaussianSplattingViewer {
}
}


fn setup_gaussian_cloud(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut gaussian_assets: ResMut<Assets<GaussianCloud>>,
) {
let cloud: Handle<GaussianCloud>;

// TODO: add proper GaussianSplattingViewer argument parsing
let file_arg = get_arg(1);
if let Some(n) = file_arg.clone().and_then(|s| s.parse::<usize>().ok()) {
if let Some(n) = get_arg(MainArgs::NumOfGaussians) {
let n = n.parse::<usize>().unwrap();
println!("generating {} gaussians", n);
cloud = gaussian_assets.add(random_gaussians(n));
} else if let Some(filename) = file_arg {
if filename == "--help" {
println!("usage: cargo run -- [filename | n]");
return;
}

} else if let Some(filename) = get_arg(MainArgs::Filename) {
println!("loading {}", filename);
cloud = asset_server.load(filename.to_string());
} else {
cloud = gaussian_assets.add(GaussianCloud::test_model());
}

commands.spawn((
GaussianSplattingBundle {
cloud,
..default()
},
GaussianSplattingBundle { cloud, ..default() },
Name::new("gaussian_cloud"),
));

Expand All @@ -105,7 +77,7 @@ fn setup_gaussian_cloud(
tonemapping: Tonemapping::None,
..default()
},
PanOrbitCamera{
PanOrbitCamera {
allow_upside_down: true,
orbit_smoothness: 0.0,
pan_smoothness: 0.0,
Expand All @@ -115,7 +87,6 @@ fn setup_gaussian_cloud(
));
}


#[cfg(feature = "morph_particles")]
fn setup_particle_behavior(
mut commands: Commands,
Expand All @@ -130,19 +101,23 @@ fn setup_particle_behavior(
return;
}

let mut particle_behaviors = None;

let file_arg = get_arg(1);
if let Some(_n) = file_arg.clone().and_then(|s| s.parse::<usize>().ok()) {
let behavior_arg = get_arg(2);
if let Some(k) = behavior_arg.clone().and_then(|s| s.parse::<usize>().ok()) {
let particle_behaviors = match (
get_arg(MainArgs::NumOfGaussians),
get_arg(MainArgs::NumOfParticleBehaviors),
) {
(Some(_), Some(k)) => {
let k = k.parse::<usize>().unwrap();
println!("generating {} particle behaviors", k);
particle_behaviors = particle_behavior_assets.add(random_particle_behaviors(k)).into();
particle_behavior_assets
.add(random_particle_behaviors(k))
.into()
}
}
_ => None,
};

if let Some(particle_behaviors) = particle_behaviors {
commands.entity(gaussian_cloud.single().0)
commands
.entity(gaussian_cloud.single().0)
.insert(particle_behaviors);
}
}
Expand All @@ -151,31 +126,21 @@ fn setup_particle_behavior(
fn setup_noise_material(
mut commands: Commands,
asset_server: Res<AssetServer>,
gaussian_clouds: Query<(
Entity,
&Handle<GaussianCloud>,
Without<NoiseMaterial>,
)>,
gaussian_clouds: Query<(Entity, &Handle<GaussianCloud>, Without<NoiseMaterial>)>,
) {
if gaussian_clouds.is_empty() {
return;
}

for (
entity,
cloud_handle,
_
) in gaussian_clouds.iter() {
for (entity, cloud_handle, _) in gaussian_clouds.iter() {
if Some(bevy::asset::LoadState::Loading) == asset_server.get_load_state(cloud_handle) {
continue;
}

commands.entity(entity)
.insert(NoiseMaterial::default());
commands.entity(entity).insert(NoiseMaterial::default());
}
}


#[cfg(feature = "query_select")]
fn press_i_invert_selection(
keys: Res<Input<KeyCode>>,
Expand All @@ -198,28 +163,23 @@ fn press_o_save_selection(
}
}


#[cfg(feature = "query_sparse")]
fn setup_sparse_select(
mut commands: Commands,
gaussian_cloud: Query<(
Entity,
&Handle<GaussianCloud>,
Without<SparseSelect>,
)>,
gaussian_cloud: Query<(Entity, &Handle<GaussianCloud>, Without<SparseSelect>)>,
) {
if gaussian_cloud.is_empty() {
return;
}

commands.entity(gaussian_cloud.single().0)
commands
.entity(gaussian_cloud.single().0)
.insert(SparseSelect {
completed: true,
..default()
});
}


fn example_app() {
let config = GaussianSplattingViewer::default();
let mut app = App::new();
Expand Down Expand Up @@ -259,15 +219,13 @@ fn example_app() {
app.insert_resource(ClearColor(Color::rgb_u8(0, 0, 0)));
app.add_plugins(
DefaultPlugins
.set(ImagePlugin::default_nearest())
.set(WindowPlugin {
primary_window,
..default()
}),
.set(ImagePlugin::default_nearest())
.set(WindowPlugin {
primary_window,
..default()
}),
);
app.add_plugins((
PanOrbitCameraPlugin,
));
app.add_plugins((PanOrbitCameraPlugin,));

if config.editor {
app.add_plugins(WorldInspectorPlugin::new());
Expand All @@ -283,7 +241,6 @@ fn example_app() {
app.add_systems(Update, fps_update_system);
}


// setup for gaussian splatting
app.add_plugins(GaussianSplattingPlugin);
app.add_systems(Startup, setup_gaussian_cloud);
Expand All @@ -306,20 +263,13 @@ fn example_app() {
app.run();
}


pub fn esc_close(
keys: Res<Input<KeyCode>>,
mut exit: EventWriter<AppExit>
) {
pub fn esc_close(keys: Res<Input<KeyCode>>, mut exit: EventWriter<AppExit>) {
if keys.just_pressed(KeyCode::Escape) {
exit.send(AppExit);
}
}

fn fps_display_setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
) {
fn fps_display_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn((
TextBundle::from_sections([
TextSection::new(
Expand All @@ -335,7 +285,8 @@ fn fps_display_setup(
font_size: 60.0,
color: Color::GOLD,
}),
]).with_style(Style {
])
.with_style(Style {
position_type: PositionType::Absolute,
bottom: Val::Px(5.0),
left: Val::Px(15.0),
Expand All @@ -361,7 +312,6 @@ fn fps_update_system(
}
}


pub fn main() {
setup_hooks();
example_app();
Expand Down