这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ Cargo.lock


*.ply
*.gcloud

.DS_Store
17 changes: 17 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@ categories = ["computer-vision", "graphics", "rendering", "rendering::data-forma
homepage = "https://github.com/mosure/bevy_gaussian_splatting"
repository = "https://github.com/mosure/bevy_gaussian_splatting"
readme = "README.md"
include = ["tools"]
exclude = [".devcontainer", ".github", "docs", "dist", "build", "assets", "credits"]
default-run = "bevy_gaussian_splatting"

[dependencies]
bevy = "0.11.3"
bevy-inspector-egui = "0.20.0"
bevy_panorbit_camera = "0.8.0"
bincode2 = "2.0.1"
bytemuck = "1.14.0"
flate2 = "1.0.28"
ply-rs = "0.1.3"
serde = "1.0.189"


[target.'cfg(target_arch = "wasm32")'.dependencies]
Expand Down Expand Up @@ -51,3 +56,15 @@ inherits = "release"
opt-level = "z"
lto = "fat"
codegen-units = 1


[lib]
path = "src/lib.rs"

[[bin]]
name = "bevy_gaussian_splatting"
path = "src/main.rs"

[[bin]]
name = "ply_to_gcloud"
path = "tools/ply_to_gcloud.rs"
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@

bevy gaussian splatting render pipeline plugin

`cargo run -- {path to ply or gcloud.gz file}`

## capabilities

- [ ] bevy gaussian cloud render pipeline
- [X] ply to gcloud converter
- [X] gcloud and ply asset loaders
- [X] bevy gaussian cloud render pipeline
- [ ] 4D gaussian clouds via morph targets
- [ ] bevy 3D camera to gaussian cloud pipeline

Expand Down Expand Up @@ -61,6 +65,7 @@ fn setup_gaussian_cloud(
- [diff-gaussian-rasterization](https://github.com/graphdeco-inria/diff-gaussian-rasterization)
- [dreamgaussian](https://github.com/dreamgaussian/dreamgaussian)
- [dynamic-3d-gaussians](https://github.com/JonathonLuiten/Dynamic3DGaussians)
- [ewa splatting](https://www.cs.umd.edu/~zwicker/publications/EWASplatting-TVCG02.pdf)
- [gaussian-splatting](https://github.com/graphdeco-inria/gaussian-splatting)
- [gaussian-splatting-web](https://github.com/cvlab-epfl/gaussian-splatting-web)
- [making gaussian splats smaller](https://aras-p.info/blog/2023/09/13/Making-Gaussian-Splats-smaller/)
Expand Down
Binary file added assets/scenes/icecream.gcloud
Binary file not shown.
115 changes: 100 additions & 15 deletions src/gaussian.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,18 @@ use bevy::{
render::render_resource::ShaderType,
utils::BoxedFuture,
};
use bincode2::deserialize_from;
use bytemuck::{
Pod,
Zeroable,
};
use flate2::read::GzDecoder;
use serde::{
Deserialize,
Serialize,
Serializer,
ser::SerializeTuple,
};

use crate::ply::parse_ply;

Expand All @@ -34,9 +42,19 @@ const fn num_sh_coefficients(degree: usize) -> usize {
}
const SH_DEGREE: usize = 3;
pub const MAX_SH_COEFF_COUNT: usize = num_sh_coefficients(SH_DEGREE) * 3;
#[derive(Clone, Copy, Reflect, ShaderType, Pod, Zeroable)]
#[derive(
Clone,
Copy,
Reflect,
ShaderType,
Pod,
Zeroable,
Serialize,
Deserialize,
)]
#[repr(C)]
pub struct SphericalHarmonicCoefficients {
#[serde(serialize_with = "coefficients_serializer", deserialize_with = "coefficients_deserializer")]
pub coefficients: [f32; MAX_SH_COEFF_COUNT],
}
impl Default for SphericalHarmonicCoefficients {
Expand All @@ -46,14 +64,64 @@ impl Default for SphericalHarmonicCoefficients {
}
}
}
fn coefficients_serializer<S>(n: &[f32; MAX_SH_COEFF_COUNT], s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut tup = s.serialize_tuple(MAX_SH_COEFF_COUNT)?;
for &x in n.iter() {
tup.serialize_element(&x)?;
}

tup.end()
}

fn coefficients_deserializer<'de, D>(d: D) -> Result<[f32; MAX_SH_COEFF_COUNT], D::Error>
where
D: serde::Deserializer<'de>,
{
struct CoefficientsVisitor;

impl<'de> serde::de::Visitor<'de> for CoefficientsVisitor {
type Value = [f32; MAX_SH_COEFF_COUNT];

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("an array of floats")
}

fn visit_seq<A>(self, mut seq: A) -> Result<[f32; MAX_SH_COEFF_COUNT], A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut coefficients = [0.0; MAX_SH_COEFF_COUNT];
for i in 0..MAX_SH_COEFF_COUNT {
coefficients[i] = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(i, &self))?;
}
Ok(coefficients)
}
}

d.deserialize_tuple(MAX_SH_COEFF_COUNT, CoefficientsVisitor)
}


pub const MAX_SIZE_VARIANCE: f32 = 5.0;

#[derive(Clone, Default, Copy, Reflect, ShaderType, Pod, Zeroable)]
#[derive(
Clone,
Default,
Copy,
Reflect,
ShaderType,
Pod,
Zeroable,
Serialize,
Deserialize,
)]
#[repr(C)]
pub struct Gaussian {
//pub anisotropic_covariance: AnisotropicCovariance,
//pub normal: Vec3,
pub rotation: [f32; 4],
pub position: Vec3,
pub scale: Vec3,
Expand All @@ -62,7 +130,13 @@ pub struct Gaussian {
padding: f32,
}

#[derive(Clone, Reflect, TypeUuid)]
#[derive(
Clone,
Reflect,
TypeUuid,
Serialize,
Deserialize,
)]
#[uuid = "ac2f08eb-bc32-aabb-ff21-51571ea332d5"]
pub struct GaussianCloud(pub Vec<Gaussian>);

Expand Down Expand Up @@ -147,19 +221,30 @@ impl AssetLoader for GaussianCloudLoader {
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<(), bevy::asset::Error>> {
Box::pin(async move {
let cursor = Cursor::new(bytes);
let mut f = BufReader::new(cursor);

let ply_cloud = parse_ply(&mut f)?;
let cloud = GaussianCloud(ply_cloud);

load_context.set_default_asset(LoadedAsset::new(cloud));
Ok(())
match load_context.path().extension() {
Some(ext) if ext == "ply" => {
let cursor = Cursor::new(bytes);
let mut f = BufReader::new(cursor);

let ply_cloud = parse_ply(&mut f)?;
let cloud = GaussianCloud(ply_cloud);

load_context.set_default_asset(LoadedAsset::new(cloud));
return Ok(());
},
Some(ext) if ext == "gcloud" => {
let decompressed = GzDecoder::new(bytes);
let cloud: GaussianCloud = deserialize_from(decompressed).expect("failed to decode cloud");

load_context.set_default_asset(LoadedAsset::new(cloud));
return Ok(());
},
_ => Ok(()),
}
})
}

fn extensions(&self) -> &[&str] {
// TODO: add support for .gcloud files (and utility to convert from .ply)
&["ply"]
&["ply", "gcloud"]
}
}
26 changes: 17 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,27 @@ impl Default for GaussianSplattingViewer {

fn setup_gaussian_cloud(
mut commands: Commands,
_asset_server: Res<AssetServer>,
asset_server: Res<AssetServer>,
mut gaussian_assets: ResMut<Assets<GaussianCloud>>,
) {
// TODO: add file path argument
let cloud = gaussian_assets.add(GaussianCloud::test_model());
let cloud: Handle<GaussianCloud>;
let settings = GaussianCloudSettings {
aabb: true,
visualize_bounding_box: false,
..default()
};

let filename = std::env::args().nth(1);
if let Some(filename) = filename {
println!("loading {}", filename);
cloud = asset_server.load(filename.as_str());
} else {
cloud = gaussian_assets.add(GaussianCloud::test_model());
}

commands.spawn(GaussianSplattingBundle {
cloud,
// cloud: _asset_server.load("scenes/icecream.ply"),
settings: GaussianCloudSettings {
aabb: true, // TODO: default to OBB (when working)
visualize_bounding_box: false,
..default()
},
settings,
});

commands.spawn((
Expand Down
4 changes: 4 additions & 0 deletions src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,10 @@ pub fn queue_gaussian_bind_group(
continue;
}

if !gaussian_cloud_res.contains_key(cloud_handle) {
continue;
}

let cloud = gaussian_cloud_res.get(cloud_handle).unwrap();

commands.entity(entity).insert(GaussianCloudBindGroup {
Expand Down
37 changes: 37 additions & 0 deletions tools/ply_to_gcloud.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use bincode2::serialize_into;
use flate2::{
Compression,
write::GzEncoder,
};

use bevy_gaussian_splatting::{
GaussianCloud,
ply::parse_ply,
};


fn main() {
let filename = std::env::args().nth(1).expect("no filename given");

println!("converting {}", filename);

// filepath to BufRead
let file = std::fs::File::open(&filename).expect("failed to open file");
let mut reader = std::io::BufReader::new(file);

let cloud = GaussianCloud(parse_ply(&mut reader).expect("failed to parse ply file"));

// write cloud to .gcloud file (remove .ply)
let base_filename = filename.split('.').next().expect("no extension").to_string();
let gcloud_filename = base_filename + ".gcloud";
// let gcloud_file = std::fs::File::create(&gcloud_filename).expect("failed to create file");
// let mut writer = std::io::BufWriter::new(gcloud_file);

// serialize_into(&mut writer, &cloud).expect("failed to encode cloud");

// write gloud.gz
let gz_file = std::fs::File::create(&gcloud_filename).expect("failed to create file");
let mut gz_writer = std::io::BufWriter::new(gz_file);
let mut gz_encoder = GzEncoder::new(&mut gz_writer, Compression::default()); // TODO: consider switching to fast (or support multiple options), default is a bit slow
serialize_into(&mut gz_encoder, &cloud).expect("failed to encode cloud");
}