Skip to content

Commit

Permalink
feat: bevy 0.15 (#130)
Browse files Browse the repository at this point in the history
* feat: building with bevy 0.15

* refactor: gaussian cloud transform component

* fix: lint

* docs: compatible bevy version
  • Loading branch information
mosure authored Dec 18, 2024
1 parent f42bfa1 commit f00338b
Show file tree
Hide file tree
Showing 25 changed files with 2,497 additions and 713 deletions.
2,447 changes: 2,027 additions & 420 deletions Cargo.lock

Large diffs are not rendered by default.

18 changes: 10 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "bevy_gaussian_splatting"
description = "bevy gaussian splatting render pipeline plugin"
version = "2.7.6"
version = "3.0.0"
edition = "2021"
authors = ["mosure <[email protected]>"]
license = "MIT OR Apache-2.0"
Expand Down Expand Up @@ -106,6 +106,7 @@ tooling = ["byte-unit"]
perftest = []

headless = [
"bevy/png",
"io_flexbuffers",
"io_ply",
"planar",
Expand All @@ -116,6 +117,7 @@ headless = [
]

viewer = [
"bevy/png",
"bevy-inspector-egui",
"bevy_panorbit_camera",
# "bevy_transform_gizmo",
Expand All @@ -141,20 +143,20 @@ webgpu = ["bevy/webgpu"]


[dependencies]
bevy_args = "1.6"
bevy-inspector-egui = { version = "0.27", optional = true }
bevy_args = "1.7"
bevy-inspector-egui = { version = "0.28", optional = true }
bevy_mod_picking = { version = "0.20", optional = true }
# bevy_panorbit_camera = { git = "https://github.com/mosure/bevy_panorbit_camera.git", optional = true, features = ["bevy_egui"] }
bevy_panorbit_camera = { version = "0.19.5", optional = true, features = ["bevy_egui"] }
bevy_panorbit_camera = { version = "0.21", optional = true, features = ["bevy_egui"] }
bevy_transform_gizmo = { version = "0.12", optional = true }
bincode2 = { version = "2.0", optional = true }
byte-unit = { version = "5.1", optional = true }
bytemuck = "1.19"
bytemuck = "1.20"
clap = { version = "4.5", features = ["derive"] }
flate2 = { version = "1.0", optional = true }
flexbuffers = { version = "2.0", optional = true }
half = { version = "2.3", optional = true, features = ["serde"] }
image = { version = "0.25.0", default-features = false, features = ["png"] }
image = { version = "0.25.2", default-features = false, features = ["png"] }
kd-tree = { version = "0.6", optional = true }
noise = { version = "0.9.0", optional = true }
ply-rs = { version = "0.1", optional = true }
Expand All @@ -163,7 +165,7 @@ rayon = { version = "1.8", optional = true }
serde = "1.0"
static_assertions = "1.1"
typenum = "1.17"
wgpu = "0.20"
wgpu = "23.0.1"


[target.'cfg(target_arch = "wasm32")'.dependencies]
Expand All @@ -172,7 +174,7 @@ wasm-bindgen = "0.2"


[dependencies.bevy]
version = "0.14"
version = "0.15"
default-features = false
features = [
"bevy_asset",
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ fn setup_gaussian_cloud(
mut commands: Commands,
asset_server: Res<AssetServer>,
) {
commands.spawn(GaussianSplattingBundle {
cloud: asset_server.load("scenes/icecream.gcloud"),
..Default::default()
});
// GaussianCloudSettings and Visibility are automatically added
commands.spawn(
GaussianCloudHandle(asset_server.load("scenes/icecream.gcloud")),
);

commands.spawn(Camera3dBundle::default());
}
Expand All @@ -79,6 +79,7 @@ fn setup_gaussian_cloud(

| `bevy_gaussian_splatting` | `bevy` |
| :-- | :-- |
| `3.0` | `0.15` |
| `2.3` | `0.14` |
| `2.1` | `0.13` |
| `0.4 - 2.0` | `0.12` |
Expand Down
16 changes: 7 additions & 9 deletions examples/headless.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use bevy_args::BevyArgsPlugin;
use bevy_gaussian_splatting::{
GaussianCamera,
GaussianCloud,
GaussianSplattingBundle,
GaussianCloudHandle,
GaussianSplattingPlugin,
random_gaussians,
utils::GaussianSplattingViewer,
Expand Down Expand Up @@ -394,18 +394,16 @@ fn setup_gaussian_cloud(


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

commands.spawn((
Camera3dBundle {
transform: Transform::from_translation(Vec3::new(0.0, 1.5, 5.0)),
tonemapping: Tonemapping::None,
camera: Camera {
target: render_target,
..default()
},
Camera3d::default(),
Transform::from_translation(Vec3::new(0.0, 1.5, 5.0)),
Tonemapping::None,
Camera {
target: render_target,
..default()
},
GaussianCamera::default(),
Expand Down
51 changes: 23 additions & 28 deletions examples/multi_camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ use bevy_gaussian_splatting::{
Gaussian,
GaussianCamera,
GaussianCloud,
GaussianMode,
GaussianCloudHandle,
GaussianCloudSettings,
GaussianSplattingBundle,
GaussianMode,
GaussianSplattingPlugin,
gaussian::f32::Rotation,
utils::{
Expand Down Expand Up @@ -161,14 +161,13 @@ pub fn setup_surfel_compare(
}

commands.spawn((
GaussianSplattingBundle {
cloud: gaussian_assets.add(GaussianCloud::from_gaussians(red_gaussians)),
settings: GaussianCloudSettings {
aabb: true,
transform: Transform::from_translation(Vec3::new(spacing, spacing, 0.0)),
gaussian_mode: GaussianMode::GaussianSurfel,
..default()
},
Transform::from_translation(Vec3::new(spacing, spacing, 0.0)),
GaussianCloudHandle(
gaussian_assets.add(GaussianCloud::from_gaussians(red_gaussians))
),
GaussianCloudSettings {
aabb: true,
gaussian_mode: GaussianMode::GaussianSurfel,
..default()
},
Name::new("gaussian_cloud_2dgs"),
Expand All @@ -178,15 +177,13 @@ pub fn setup_surfel_compare(
GaussianCamera {
warmup: true,
},
Camera3dBundle {
camera: Camera{
order: 0,
..default()
},
transform: Transform::from_translation(Vec3::new(0.0, 1.5, 20.0)),
tonemapping: Tonemapping::None,
Camera3d::default(),
Camera{
order: 0,
..default()
},
Transform::from_translation(Vec3::new(0.0, 1.5, 20.0)),
Tonemapping::None,
CameraPosition {
pos: UVec2::new(0, 0),
},
Expand Down Expand Up @@ -232,20 +229,18 @@ fn press_s_to_spawn_camera(
GaussianCamera {
warmup: true,
},
Camera3dBundle {
camera: Camera{
order: 1,
viewport: Viewport {
physical_position: pos * size,
physical_size: size,
..default()
}.into(),
Camera3d::default(),
Camera{
order: 1,
viewport: Viewport {
physical_position: pos * size,
physical_size: size,
..default()
},
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 40.0)),
tonemapping: Tonemapping::None,
}.into(),
..default()
},
Transform::from_translation(Vec3::new(0.0, 0.0, 40.0)),
Tonemapping::None,
CameraPosition {
pos,
},
Expand Down
111 changes: 110 additions & 1 deletion src/gaussian/cloud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@ use rand::{
Rng,
};

use bevy::prelude::*;
use bevy::{
prelude::*,
render::{
primitives::Aabb,
sync_world::SyncToRenderWorld,
view::visibility::{
check_visibility,
NoFrustumCulling,
VisibilitySystems,
},
},
};
use serde::{
Deserialize,
Serialize,
Expand All @@ -23,6 +34,7 @@ use crate::{
ScaleOpacity,
},
packed::Gaussian,
settings::GaussianCloudSettings,
},
material::spherical_harmonics::{
HALF_SH_COEFF_COUNT,
Expand All @@ -40,6 +52,84 @@ use crate::gaussian::f16::{
};


#[derive(Default)]
pub struct GaussianCloudPlugin;

impl Plugin for GaussianCloudPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
PostUpdate,
(
calculate_bounds.in_set(VisibilitySystems::CalculateBounds),
check_visibility::<With<GaussianCloudHandle>>.in_set(VisibilitySystems::CheckVisibility),
)
);
}
}


// TODO: handle aabb updates (e.g. gaussian particle movements)
#[allow(clippy::type_complexity)]
pub fn calculate_bounds(
mut commands: Commands,
gaussian_clouds: Res<Assets<GaussianCloud>>,
without_aabb: Query<
(
Entity,
&GaussianCloudHandle,
),
(
Without<Aabb>,
Without<NoFrustumCulling>,
),
>,
) {
for (entity, cloud_handle) in &without_aabb {
if let Some(cloud) = gaussian_clouds.get(cloud_handle) {
if let Some(aabb) = cloud.compute_aabb() {
commands.entity(entity).try_insert(aabb);
}
}
}
}


#[derive(
Component,
Clone,
Debug,
Default,
PartialEq,
Reflect,
)]
#[reflect(Component, Default)]
#[require(
GaussianCloudSettings,
SyncToRenderWorld,
Transform,
Visibility,
)]
pub struct GaussianCloudHandle(pub Handle<GaussianCloud>);

impl From<Handle<GaussianCloud>> for GaussianCloudHandle {
fn from(handle: Handle<GaussianCloud>) -> Self {
Self(handle)
}
}

impl From<GaussianCloudHandle> for AssetId<GaussianCloud> {
fn from(handle: GaussianCloudHandle) -> Self {
handle.0.id()
}
}

impl From<&GaussianCloudHandle> for AssetId<GaussianCloud> {
fn from(handle: &GaussianCloudHandle) -> Self {
handle.0.id()
}
}


#[cfg(feature = "f16")]
#[derive(
Asset,
Expand Down Expand Up @@ -133,6 +223,25 @@ impl GaussianCloud {
&mut self.position_visibility[index].visibility
}

pub fn compute_aabb(&self) -> Option<Aabb> {
if self.is_empty() {
return None;
}

let mut min = Vec3::splat(f32::INFINITY);
let mut max = Vec3::splat(f32::NEG_INFINITY);

// TODO: find a more correct aabb bound derived from scalar max gaussian scale
let max_scale = 0.1;

for position in self.position_iter() {
min = min.min(Vec3::from(*position) - Vec3::splat(max_scale));
max = max.max(Vec3::from(*position) + Vec3::splat(max_scale));
}

Aabb::from_min_max(min, max).into()
}


// pub fn rotation(&self, index: usize) -> &[f32; 4] {
// #[cfg(feature = "f16")]
Expand Down
3 changes: 1 addition & 2 deletions src/gaussian/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ pub enum GaussianCloudRasterize {
}


// TODO: breakdown into components
#[derive(Component, Reflect, Clone)]
#[reflect(Component)]
pub struct GaussianCloudSettings {
pub aabb: bool,
pub global_opacity: f32,
pub global_scale: f32,
pub transform: Transform,
pub opacity_adaptive_radius: bool,
pub visualize_bounding_box: bool,
pub sort_mode: SortMode,
Expand All @@ -85,7 +85,6 @@ impl Default for GaussianCloudSettings {
aabb: false,
global_opacity: 1.0,
global_scale: 1.0,
transform: Transform::IDENTITY,
opacity_adaptive_radius: true,
visualize_bounding_box: false,
sort_mode: SortMode::default(),
Expand Down
11 changes: 5 additions & 6 deletions src/io/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use std::io::{

use bevy::asset::{
AssetLoader,
AsyncReadExt,
LoadContext,
io::Reader,
};
Expand All @@ -26,11 +25,11 @@ impl AssetLoader for GaussianCloudLoader {
type Settings = ();
type Error = std::io::Error;

async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
_settings: &'a Self::Settings,
load_context: &'a mut LoadContext<'_>,
async fn load(
&self,
reader: &mut dyn Reader,
_: &Self::Settings,
load_context: &mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
Expand Down
Loading

0 comments on commit f00338b

Please sign in to comment.