diff --git a/rust/spark-lib/Cargo.toml b/rust/spark-lib/Cargo.toml index be19442b..75dee09b 100644 --- a/rust/spark-lib/Cargo.toml +++ b/rust/spark-lib/Cargo.toml @@ -7,19 +7,33 @@ license.workspace = true authors.workspace = true repository.workspace = true +[features] +default = ["antisplat", "ksplat", "ply", "rad", "sogs", "spz", "csplat", "gsplat", "tiny_lod", "bhatt_lod"] +antisplat = [] +ksplat = [] +ply = [] +rad = ["dep:miniz_oxide"] +sogs = ["dep:zip", "dep:image"] +spz = ["dep:miniz_oxide"] +csplat = [] +gsplat = [] +quick_lod = ["gsplat"] +tiny_lod = [] +bhatt_lod = [] + [dependencies] ahash.workspace = true anyhow.workspace = true glam.workspace = true half.workspace = true ordered-float.workspace = true -miniz_oxide.workspace = true +miniz_oxide = { workspace = true, optional = true } serde.workspace = true smallvec.workspace = true itertools.workspace = true serde_json.workspace = true -zip.workspace = true -image.workspace = true +zip = { workspace = true, optional = true } +image = { workspace = true, optional = true } hnsw.workspace = true rand_pcg.workspace = true space.workspace = true diff --git a/rust/spark-lib/src/decoder.rs b/rust/spark-lib/src/decoder.rs index 74c4af30..2aa59a7a 100644 --- a/rust/spark-lib/src/decoder.rs +++ b/rust/spark-lib/src/decoder.rs @@ -1,16 +1,21 @@ use std::any::Any; +#[cfg(feature = "spz")] use miniz_oxide::inflate::{core::{decompress, inflate_flags::{TINFL_FLAG_HAS_MORE_INPUT, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF}, DecompressorOxide}, TINFLStatus}; use serde::{Deserialize, Serialize}; -use crate::{ - antisplat::AntiSplatDecoder, - ksplat::KsplatDecoder, - ply::{PLY_MAGIC, PlyDecoder}, - rad::{RAD_CHUNK_MAGIC, RAD_MAGIC, RadDecoder}, - sogs::SogsDecoder, - spz::{SPZ_MAGIC, SpzDecoder} -}; +#[cfg(feature = "antisplat")] +use crate::antisplat::AntiSplatDecoder; +#[cfg(feature = "ksplat")] +use crate::ksplat::KsplatDecoder; +#[cfg(feature = "ply")] +use crate::ply::{PLY_MAGIC, PlyDecoder}; +#[cfg(feature = "rad")] +use crate::rad::{RAD_CHUNK_MAGIC, RAD_MAGIC, RadDecoder}; +#[cfg(feature = "sogs")] +use crate::sogs::{PK_MAGIC, SogsDecoder}; +#[cfg(feature = "spz")] +use crate::spz::{SPZ_MAGIC, SpzDecoder}; pub trait ChunkReceiver: Any { fn push(&mut self, bytes: &[u8]) -> anyhow::Result<()>; @@ -329,33 +334,51 @@ pub trait SplatGetter: 'static { #[derive(Debug, Clone, Copy)] pub enum SplatFileType { + #[cfg(feature = "ply")] PLY, + #[cfg(feature = "spz")] SPZ, + #[cfg(feature = "antisplat")] ANTISPLAT, + #[cfg(feature = "ksplat")] KSPLAT, + #[cfg(feature = "sogs")] SOGS, + #[cfg(feature = "rad")] RAD, } impl SplatFileType { pub fn to_enum_str(self) -> &'static str { match self { + #[cfg(feature = "ply")] Self::PLY => "ply", + #[cfg(feature = "spz")] Self::SPZ => "spz", + #[cfg(feature = "antisplat")] Self::ANTISPLAT => "splat", + #[cfg(feature = "ksplat")] Self::KSPLAT => "ksplat", + #[cfg(feature = "sogs")] Self::SOGS => "pcsogszip", + #[cfg(feature = "rad")] Self::RAD => "rad", } } pub fn from_enum_str(enum_str: &str) -> anyhow::Result { match enum_str { + #[cfg(feature = "ply")] "ply" => Ok(Self::PLY), + #[cfg(feature = "spz")] "spz" => Ok(Self::SPZ), + #[cfg(feature = "antisplat")] "splat" => Ok(Self::ANTISPLAT), + #[cfg(feature = "ksplat")] "ksplat" => Ok(Self::KSPLAT), + #[cfg(feature = "sogs")] "pcsogszip" => Ok(Self::SOGS), + #[cfg(feature = "rad")] "rad" => Ok(Self::RAD), _ => Err(anyhow::anyhow!("Invalid file type: {}", enum_str)), } @@ -363,13 +386,21 @@ impl SplatFileType { pub fn from_extension(extension: &str) -> Option { match extension.to_lowercase().as_str() { + #[cfg(feature = "ply")] "ply" => Some(Self::PLY), + #[cfg(feature = "spz")] "spz" => Some(Self::SPZ), + #[cfg(feature = "antisplat")] "splat" => Some(Self::ANTISPLAT), + #[cfg(feature = "ksplat")] "ksplat" => Some(Self::KSPLAT), + #[cfg(feature = "sogs")] "sog" => Some(Self::SOGS), + #[cfg(feature = "sogs")] "sogs" => Some(Self::SOGS), + #[cfg(feature = "sogs")] "zip" => Some(Self::SOGS), + #[cfg(feature = "rad")] "rad" => Some(Self::RAD), _ => None, } @@ -418,26 +449,32 @@ impl MultiDecoder { pub fn into_splats(self) -> T { let inner_any = self.inner.unwrap().into_any(); + #[cfg(feature = "ply")] let inner_any = match inner_any.downcast::>() { Ok(ply) => { return ply.into_splats(); }, Err(inner_any) => inner_any, }; + #[cfg(feature = "spz")] let inner_any = match inner_any.downcast::>() { Ok(spz) => { return spz.into_splats(); }, Err(inner_any) => inner_any, }; + #[cfg(feature = "antisplat")] let inner_any = match inner_any.downcast::>() { Ok(antisplat) => { return antisplat.into_splats(); }, Err(inner_any) => inner_any, }; + #[cfg(feature = "ksplat")] let inner_any = match inner_any.downcast::>() { Ok(ksplat) => { return ksplat.into_splats(); }, Err(inner_any) => inner_any, }; + #[cfg(feature = "sogs")] let inner_any = match inner_any.downcast::>() { Ok(sogs) => { return sogs.into_splats(); }, Err(inner_any) => inner_any, }; + #[cfg(feature = "rad")] let inner_any = match inner_any.downcast::>() { Ok(rad) => { return rad.into_splats(); }, Err(inner_any) => inner_any, @@ -471,34 +508,43 @@ impl ChunkReceiver for MultiDecoder { let mut detection_complete = false; let magic = u32::from_le_bytes([self.buffer[0], self.buffer[1], self.buffer[2], self.buffer[3]]); - if (magic & 0x00ffffff) == PLY_MAGIC { - return self.init_file_type(SplatFileType::PLY); - } - if (magic & 0x00ffffff) == GZIP_MAGIC { - // Gzipped file, unpack beginning to check magic number - if self.buffer_gz.is_none() { - self.buffer_gz = try_gunzip(&self.buffer, 4)?; + match (magic, magic & 0x00ffffff) { + #[cfg(feature = "ply")] + (_, PLY_MAGIC) => { + return self.init_file_type(SplatFileType::PLY); } - if let Some(buffer_gz) = self.buffer_gz.as_ref() { - detection_complete = true; - if buffer_gz.len() >= 4 { - let magic = u32::from_le_bytes([buffer_gz[0], buffer_gz[1], buffer_gz[2], buffer_gz[3]]); - if magic == SPZ_MAGIC { - return self.init_file_type(SplatFileType::SPZ); + #[cfg(feature = "spz")] + (_, GZIP_MAGIC) => { + // Gzipped file, unpack beginning to check magic number + if self.buffer_gz.is_none() { + self.buffer_gz = try_gunzip(&self.buffer, 4)?; + } + if let Some(buffer_gz) = self.buffer_gz.as_ref() { + detection_complete = true; + if buffer_gz.len() >= 4 { + let magic = u32::from_le_bytes([buffer_gz[0], buffer_gz[1], buffer_gz[2], buffer_gz[3]]); + if magic == SPZ_MAGIC { + return self.init_file_type(SplatFileType::SPZ); + } } } } - } else if magic == 0x04034b50 { - detection_complete = true; - if let Some(pathname) = &self.pathname { - if let Some(SplatFileType::SOGS) = SplatFileType::from_pathname(pathname) { - return self.init_file_type(SplatFileType::SOGS); + #[cfg(feature = "sogs")] + (PK_MAGIC, _) => { + detection_complete = true; + if let Some(pathname) = &self.pathname { + if let Some(SplatFileType::SOGS) = SplatFileType::from_pathname(pathname) { + return self.init_file_type(SplatFileType::SOGS); + } } } - } else if magic == RAD_MAGIC || magic == RAD_CHUNK_MAGIC { - return self.init_file_type(SplatFileType::RAD); - } else { - detection_complete = true; + #[cfg(feature = "rad")] + (RAD_MAGIC, _) | (RAD_CHUNK_MAGIC, _) => { + return self.init_file_type(SplatFileType::RAD); + } + _ => { + detection_complete = true; + } } if detection_complete { @@ -528,15 +574,22 @@ impl ChunkReceiver for MultiDecoder { fn new_decoder(file_type: SplatFileType, splats: T) -> Box { match file_type { + #[cfg(feature = "ply")] SplatFileType::PLY => Box::new(PlyDecoder::new(splats)), + #[cfg(feature = "spz")] SplatFileType::SPZ => Box::new(SpzDecoder::new(splats)), + #[cfg(feature = "antisplat")] SplatFileType::ANTISPLAT => Box::new(AntiSplatDecoder::new(splats)), + #[cfg(feature = "ksplat")] SplatFileType::KSPLAT => Box::new(KsplatDecoder::new(splats)), + #[cfg(feature = "sogs")] SplatFileType::SOGS => Box::new(SogsDecoder::new(splats, None)), + #[cfg(feature = "rad")] SplatFileType::RAD => Box::new(RadDecoder::new(splats)), } } +#[cfg(feature = "spz")] fn try_gunzip(buffer: &[u8], max_bytes: usize) -> anyhow::Result>> { if buffer.len() < 10 { return Ok(None); diff --git a/rust/spark-lib/src/lib.rs b/rust/spark-lib/src/lib.rs index 5b492abe..2feacbf6 100644 --- a/rust/spark-lib/src/lib.rs +++ b/rust/spark-lib/src/lib.rs @@ -1,16 +1,27 @@ pub mod tsplat; +#[cfg(feature = "gsplat")] pub mod gsplat; +#[cfg(feature = "csplat")] pub mod csplat; pub mod symmat3; +#[cfg(feature = "quick_lod")] pub mod quick_lod; +#[cfg(feature = "tiny_lod")] pub mod tiny_lod; +#[cfg(feature = "bhatt_lod")] pub mod bhatt_lod; +#[cfg(feature = "ply")] pub mod ply; +#[cfg(feature = "spz")] pub mod spz; +#[cfg(feature = "antisplat")] pub mod antisplat; +#[cfg(feature = "ksplat")] pub mod ksplat; +#[cfg(feature = "sogs")] pub mod sogs; +#[cfg(feature = "rad")] pub mod rad; pub mod decoder; pub mod splat_encode; @@ -18,6 +29,9 @@ pub mod ordering; pub mod chunk_tree; pub mod sh_clustering; +#[cfg(not(any(feature = "gsplat", feature = "csplat")))] +compile_error!("at least one of \"gsplat\" and \"csplat\" must be enabled"); + #[cfg(test)] mod tests { use super::{ diff --git a/rust/spark-lib/src/sogs.rs b/rust/spark-lib/src/sogs.rs index 59a39785..6495d708 100644 --- a/rust/spark-lib/src/sogs.rs +++ b/rust/spark-lib/src/sogs.rs @@ -8,7 +8,7 @@ use zip::ZipArchive; use crate::decoder::{ChunkReceiver, SplatInit, SplatProps, SplatReceiver}; -const PK_MAGIC: u32 = 0x04034b50; +pub const PK_MAGIC: u32 = 0x04034b50; const SH_C0: f32 = 0.28209479177387814; const MAX_SPLAT_CHUNK: usize = 65536; diff --git a/rust/spark-rs/Cargo.toml b/rust/spark-rs/Cargo.toml index bab51740..32870ac8 100644 --- a/rust/spark-rs/Cargo.toml +++ b/rust/spark-rs/Cargo.toml @@ -13,6 +13,19 @@ crate-type = ["cdylib"] [package.metadata.wasm-pack.profile.release.wasm-bindgen] omit-default-module-path = true +[features] +default = ["antisplat", "ksplat", "ply", "rad", "sogs", "spz", "csplat", "gsplat", "tiny_lod", "bhatt_lod"] +antisplat = ["spark-lib/antisplat"] +ksplat = ["spark-lib/ksplat"] +ply = ["spark-lib/ply"] +rad = ["spark-lib/rad"] +sogs = ["spark-lib/sogs"] +spz = ["spark-lib/spz"] +csplat = ["spark-lib/csplat"] +gsplat = ["spark-lib/gsplat"] +tiny_lod = ["spark-lib/tiny_lod"] +bhatt_lod = ["spark-lib/bhatt_lod"] + [dependencies] ahash.workspace = true anyhow.workspace = true @@ -23,7 +36,7 @@ ordered-float.workspace = true smallvec.workspace = true wasm-bindgen.workspace = true web-sys = { workspace = true, features = ["Window", "Performance"] } -spark-lib = { path = "../spark-lib" } +spark-lib = { path = "../spark-lib", default-features = false, features = [] } serde-wasm-bindgen.workspace = true serde_json.workspace = true serde.workspace = true diff --git a/rust/spark-rs/src/ext_splats.rs b/rust/spark-rs/src/ext_splats.rs index eed4da9f..c4605ff5 100644 --- a/rust/spark-rs/src/ext_splats.rs +++ b/rust/spark-rs/src/ext_splats.rs @@ -1,10 +1,11 @@ use std::array; use js_sys::{Array, Object, Reflect, Uint32Array}; +#[cfg(feature = "csplat")] +use spark_lib::csplat::CsplatArray; use spark_lib::{ decoder::{SetSplatEncoding, SplatEncoding, SplatGetter, SplatInit, SplatProps, SplatPropsMut, SplatReceiver, copy_getter_to_receiver}, gsplat::GsplatArray, - csplat::CsplatArray, tsplat::{TsplatArray, Tsplat}, splat_encode::{ decode_ext_rgb, decode_ext_splat_center, decode_ext_splat_opacity, decode_ext_splat_quat, decode_ext_splat_rgb, decode_ext_splat_scale, encode_ext_rgb, encode_ext_splat, encode_ext_splat_center, encode_ext_splat_opacity, encode_ext_splat_quat, encode_ext_splat_rgb, encode_ext_splat_rgba, encode_ext_splat_scale, encode_lod_tree, get_splat_tex_size @@ -324,6 +325,7 @@ impl ExtSplatsData { } #[allow(dead_code)] + #[cfg(feature = "csplat")] pub fn to_csplat_array(&mut self) -> anyhow::Result { let mut out = CsplatArray::new(); copy_getter_to_receiver(self, &mut out)?; diff --git a/rust/spark-rs/src/lib.rs b/rust/spark-rs/src/lib.rs index 68d11d53..9f54267a 100644 --- a/rust/spark-rs/src/lib.rs +++ b/rust/spark-rs/src/lib.rs @@ -2,15 +2,22 @@ use std::cell::RefCell; use js_sys::{Array, Float32Array, Object, Reflect, Uint8Array, Uint16Array, Uint32Array}; use spark_lib::decoder::{ChunkReceiver, MultiDecoder, SplatEncoding, SplatFileType, SplatGetter}; +#[cfg(all(feature = "spz", feature = "gsplat"))] use spark_lib::spz::SpzEncoder; +#[cfg(feature = "gsplat")] use spark_lib::gsplat::{GsplatSH1,GsplatSH2,GsplatSH3}; +#[cfg(feature = "gsplat")] use spark_lib::gsplat::GsplatArray as GsplatArrayInner; +#[cfg(feature = "csplat")] use spark_lib::csplat::CsplatArray as CsplatArrayInner; use spark_lib::tsplat::TsplatArray; use wasm_bindgen::prelude::*; +use crate::decoder::ChunkDecoder; +#[cfg(feature = "gsplat")] use crate::ext_splats::ExtSplatsData; -use crate::{decoder::ChunkDecoder, packed_splats::PackedSplatsData}; +#[cfg(feature = "csplat")] +use crate::packed_splats::PackedSplatsData; mod raycast; use raycast::{raycast_packed_ellipsoids, raycast_ext_ellipsoids}; @@ -18,11 +25,15 @@ use raycast::{raycast_packed_ellipsoids, raycast_ext_ellipsoids}; mod sort; use sort::{sort_internal, SortBuffers, sort32_internal, Sort32Buffers}; +#[cfg(feature = "gsplat")] mod transform; +#[cfg(feature = "gsplat")] use transform::{transform_gsplatarray, TransformOptions}; mod decoder; +#[cfg(feature = "csplat")] mod packed_splats; +#[cfg(feature = "gsplat")] mod ext_splats; mod lod_tree; @@ -42,6 +53,16 @@ thread_local! { static SORT32_BUFFERS: RefCell = RefCell::new(Sort32Buffers::default()); } +macro_rules! stub_fn { + ($pred:meta, $name:ident) => { + #[cfg(not($pred))] + #[wasm_bindgen(variadic)] + pub fn $name(_args: &JsValue) -> Result { + Err(JsValue::from(&format!("'{}' is disabled in this build, it requires: {}", stringify!($name), stringify!($pred)))) + } + }; +} + #[wasm_bindgen] pub fn sort_splats( num_splats: u32, readback: Uint16Array, ordering: Uint32Array, @@ -101,6 +122,7 @@ pub fn sort32_splats( } #[wasm_bindgen] +#[cfg(feature = "csplat")] pub fn decode_to_packedsplats( file_type: Option, path_name: Option, encoding: JsValue, sh1_codes: Option, sh2_codes: Option, sh3_codes: Option, @@ -135,8 +157,10 @@ pub fn decode_to_packedsplats( let decoder = ChunkDecoder::new(Box::new(decoder), Box::new(on_finish)); Ok(decoder) } +stub_fn!(feature = "csplat", decode_to_packedsplats); #[wasm_bindgen] +#[cfg(feature = "gsplat")] pub fn decode_to_extsplats( file_type: Option, path_name: Option, sh1_codes: Option, sh2_codes: Option, sh3_codes: Option, @@ -165,15 +189,18 @@ pub fn decode_to_extsplats( let decoder = ChunkDecoder::new(Box::new(decoder), Box::new(on_finish)); Ok(decoder) } +stub_fn!(feature = "gsplat", decode_to_extsplats); #[wasm_bindgen] #[allow(non_snake_case)] +#[cfg(feature = "gsplat")] pub struct GsplatArray { pub numSplats: usize, pub maxShDegree: usize, inner: GsplatArrayInner, } +#[cfg(feature = "gsplat")] impl GsplatArray { pub fn new(inner: GsplatArrayInner) -> Self { Self { @@ -186,6 +213,7 @@ impl GsplatArray { #[wasm_bindgen] +#[cfg(feature = "gsplat")] impl GsplatArray { pub fn len(&self) -> usize { self.inner.len() @@ -200,6 +228,7 @@ impl GsplatArray { // // spark_lib::quick_lod::compute_lod_tree(&mut self.inner, lod_base, merge_filter, |_s| {}); // } + #[cfg(feature = "tiny_lod")] pub fn tiny_lod(&mut self, lod_base: f32, merge_filter: bool) { // let log = |s: &str| web_sys::console::log_1(&JsValue::from(s)); let log = |_s: &str| {}; @@ -209,6 +238,7 @@ impl GsplatArray { spark_lib::chunk_tree::chunk_tree(&mut self.inner, 0, log); } + #[cfg(feature = "bhatt_lod")] pub fn bhatt_lod(&mut self, lod_base: f32) { // let log = |s: &str| web_sys::console::log_1(&JsValue::from(s)); let log = |_s: &str| {}; @@ -218,6 +248,7 @@ impl GsplatArray { spark_lib::chunk_tree::chunk_tree(&mut self.inner, 0, log); } + #[cfg(feature = "csplat")] pub fn to_packedsplats(&self, encoding: JsValue) -> Result { let encoding = if encoding.is_falsy() { None @@ -231,6 +262,7 @@ impl GsplatArray { Ok(splats.into_splat_object()) } + #[cfg(feature = "csplat")] pub fn to_packedsplats_lod(&self, encoding: JsValue) -> Result { let encoding = if encoding.is_falsy() { None @@ -244,6 +276,7 @@ impl GsplatArray { Ok(splats.into_splat_object()) } + #[cfg(feature = "gsplat")] pub fn to_extsplats(&self) -> Result { let splats = match ExtSplatsData::new_from_tsplat_array(&self.inner) { Err(err) => { return Err(JsValue::from(err.to_string())); }, @@ -252,6 +285,7 @@ impl GsplatArray { Ok(splats.into_splat_object()) } + #[cfg(feature = "gsplat")] pub fn to_extsplats_lod(&self) -> Result { let splats = match ExtSplatsData::new_from_tsplat_array_lod(&self.inner) { Err(err) => { return Err(JsValue::from(err.to_string())); }, @@ -280,6 +314,7 @@ impl GsplatArray { Ok(()) } + #[cfg(feature = "spz")] pub fn encode_to_spz(mut self, max_sh: u32, fractional_bits: u8) -> Result { self.inner.clamp_sh_degree(max_sh as usize); self.maxShDegree = self.inner.max_sh_degree; @@ -292,6 +327,7 @@ impl GsplatArray { } #[wasm_bindgen] +#[cfg(feature = "gsplat")] pub fn decode_to_gsplatarray(file_type: Option, path_name: Option) -> Result { let file_type = if let Some(file_type) = file_type { match SplatFileType::from_enum_str(&file_type) { @@ -313,8 +349,10 @@ pub fn decode_to_gsplatarray(file_type: Option, path_name: Option, encoding: JsValue) -> Result { let encoding = serde_wasm_bindgen::from_value(encoding)?; let mut receiver = match PackedSplatsData::from_js_arrays(packed, num_splats as usize, extra.as_ref(), encoding) { @@ -327,15 +365,18 @@ pub fn packedsplats_to_gsplatarray(num_splats: u32, packed: Uint32Array, extra: }; Ok(GsplatArray::new(splats)) } +stub_fn!(all(feature = "csplat", feature = "gsplat"), packedsplats_to_gsplatarray); #[wasm_bindgen] #[allow(non_snake_case)] +#[cfg(feature = "csplat")] pub struct CsplatArray { pub numSplats: usize, pub maxShDegree: usize, inner: CsplatArrayInner, } +#[cfg(feature = "csplat")] impl CsplatArray { pub fn new(inner: CsplatArrayInner) -> Self { Self { @@ -347,6 +388,7 @@ impl CsplatArray { } #[wasm_bindgen] +#[cfg(feature = "csplat")] impl CsplatArray { pub fn len(&self) -> usize { self.inner.len() @@ -356,6 +398,7 @@ impl CsplatArray { self.inner.has_children() } + #[cfg(feature = "tiny_lod")] pub fn tiny_lod(&mut self, lod_base: f32, merge_filter: bool) { // let log = |s: &str| web_sys::console::log_1(&JsValue::from(s)); let log = |_s: &str| {}; @@ -365,6 +408,7 @@ impl CsplatArray { spark_lib::chunk_tree::chunk_tree(&mut self.inner, 0, log); } + #[cfg(feature = "bhatt_lod")] pub fn bhatt_lod(&mut self, lod_base: f32) { // let log = |s: &str| web_sys::console::log_1(&JsValue::from(s)); let log = |_s: &str| {}; @@ -392,6 +436,7 @@ impl CsplatArray { Ok(splats.into_splat_object()) } + #[cfg(feature = "gsplat")] pub fn to_extsplats(&self) -> Result { let splats = match ExtSplatsData::new_from_tsplat_array(&self.inner) { Err(err) => { return Err(JsValue::from(err.to_string())); }, @@ -400,6 +445,7 @@ impl CsplatArray { Ok(splats.into_splat_object()) } + #[cfg(feature = "gsplat")] pub fn to_extsplats_lod(&self) -> Result { let splats = match ExtSplatsData::new_from_tsplat_array_lod(&self.inner) { Err(err) => { return Err(JsValue::from(err.to_string())); }, @@ -414,6 +460,7 @@ impl CsplatArray { } #[wasm_bindgen] +#[cfg(feature = "csplat")] pub fn decode_to_csplatarray(file_type: Option, path_name: Option, encoding: JsValue) -> Result { let file_type = if let Some(file_type) = file_type { match SplatFileType::from_enum_str(&file_type) { @@ -440,8 +487,10 @@ pub fn decode_to_csplatarray(file_type: Option, path_name: Option, encoding: JsValue) -> Result { let encoding = serde_wasm_bindgen::from_value(encoding)?; let mut receiver = match PackedSplatsData::from_js_arrays(packed, num_splats as usize, extra.as_ref(), encoding) { @@ -456,6 +505,7 @@ pub fn packedsplats_to_csplatarray(num_splats: u32, packed: Uint32Array, extra: } #[wasm_bindgen] +#[cfg(feature = "gsplat")] pub fn extsplats_to_gsplatarray(num_splats: u32, ext1: Uint32Array, ext2: Uint32Array, extra: Option) -> Result { let mut receiver = match ExtSplatsData::from_js_arrays([ext1, ext2], num_splats as usize, extra.as_ref()) { Ok(receiver) => receiver, @@ -469,6 +519,7 @@ pub fn extsplats_to_gsplatarray(num_splats: u32, ext1: Uint32Array, ext2: Uint32 } #[wasm_bindgen] +#[cfg(all(feature = "csplat", feature = "tiny_lod"))] pub fn tiny_lod_packedsplats(num_splats: u32, packed: Uint32Array, extra: Option, lod_base: f32, merge_filter: bool, rgba: Option, encoding: JsValue) -> Result { let mut gs = packedsplats_to_csplatarray(num_splats, packed, extra, encoding)?; if let Some(rgba) = rgba { @@ -477,8 +528,10 @@ pub fn tiny_lod_packedsplats(num_splats: u32, packed: Uint32Array, extra: Option gs.tiny_lod(lod_base, merge_filter); gs.to_packedsplats_lod() } +stub_fn!(all(feature = "csplat", feature = "tiny_lod"), tiny_lod_packedsplats); #[wasm_bindgen] +#[cfg(all(feature = "csplat", feature = "bhatt_lod"))] pub fn bhatt_lod_packedsplats(num_splats: u32, packed: Uint32Array, extra: Option, lod_base: f32, rgba: Option, encoding: JsValue) -> Result { let mut gs = packedsplats_to_csplatarray(num_splats, packed, extra, encoding)?; if let Some(rgba) = rgba { @@ -487,8 +540,10 @@ pub fn bhatt_lod_packedsplats(num_splats: u32, packed: Uint32Array, extra: Optio gs.bhatt_lod(lod_base); gs.to_packedsplats_lod() } +stub_fn!(all(feature = "csplat", feature = "bhatt_lod"), bhatt_lod_packedsplats); #[wasm_bindgen] +#[cfg(all(feature = "gsplat", feature = "tiny_lod"))] pub fn tiny_lod_extsplats(num_splats: u32, ext1: Uint32Array, ext2: Uint32Array, extra: Option, lod_base: f32, merge_filter: bool, rgba: Option) -> Result { let mut gs = extsplats_to_gsplatarray(num_splats, ext1, ext2, extra)?; if let Some(rgba) = rgba { @@ -497,8 +552,10 @@ pub fn tiny_lod_extsplats(num_splats: u32, ext1: Uint32Array, ext2: Uint32Array, gs.tiny_lod(lod_base, merge_filter); gs.to_extsplats_lod() } +stub_fn!(all(feature = "gsplat", feature = "tiny_lod"), tiny_lod_extsplats); #[wasm_bindgen] +#[cfg(all(feature = "gsplat", feature = "bhatt_lod"))] pub fn bhatt_lod_extsplats(num_splats: u32, ext1: Uint32Array, ext2: Uint32Array, extra: Option, lod_base: f32, rgba: Option) -> Result { let mut gs = extsplats_to_gsplatarray(num_splats, ext1, ext2, extra)?; if let Some(rgba) = rgba { @@ -507,6 +564,7 @@ pub fn bhatt_lod_extsplats(num_splats: u32, ext1: Uint32Array, ext2: Uint32Array gs.bhatt_lod(lod_base); gs.to_extsplats_lod() } +stub_fn!(all(feature = "gsplat", feature = "bhatt_lod"), bhatt_lod_extsplats); const RAYCAST_BUFFER_COUNT: usize = 65536; @@ -617,6 +675,7 @@ pub fn raycast_packed_splats( } #[wasm_bindgen] +#[cfg(feature = "rad")] pub fn decode_rad_header(bytes: Uint8Array) -> Result { let bytes = bytes.to_vec(); let meta_chunks_start = match spark_lib::rad::decode_rad_header(&bytes) { @@ -632,3 +691,4 @@ pub fn decode_rad_header(bytes: Uint8Array) -> Result { Ok(JsValue::null()) } } +stub_fn!(feature = "rad", decode_rad_header); diff --git a/rust/spark-rs/src/packed_splats.rs b/rust/spark-rs/src/packed_splats.rs index 68c368a1..79e6de36 100644 --- a/rust/spark-rs/src/packed_splats.rs +++ b/rust/spark-rs/src/packed_splats.rs @@ -1,8 +1,10 @@ use std::array; use js_sys::{Object, Reflect, Uint32Array}; +#[cfg(feature = "gsplat")] +use spark_lib::gsplat::GsplatArray; use spark_lib::{ - csplat::CsplatArray, decoder::{SetSplatEncoding, SplatEncoding, SplatGetter, SplatInit, SplatProps, SplatPropsMut, SplatReceiver, copy_getter_to_receiver}, gsplat::GsplatArray, splat_encode::{ + csplat::CsplatArray, decoder::{SetSplatEncoding, SplatEncoding, SplatGetter, SplatInit, SplatProps, SplatPropsMut, SplatReceiver, copy_getter_to_receiver}, splat_encode::{ decode_packed_splat_center, decode_packed_splat_opacity, decode_packed_splat_quat, decode_packed_splat_rgb, decode_packed_splat_scale, decode_sh1_internal_words, decode_sh2_internal_words, decode_sh3_internal_words, encode_lod_tree, encode_packed_splat, encode_packed_splat_center, encode_packed_splat_opacity, encode_packed_splat_quat, encode_packed_splat_rgb, encode_packed_splat_rgba, encode_packed_splat_scale, encode_sh1_array, encode_sh2_array, encode_sh3_array, get_decode_sh1_scale, get_decode_sh2_scale, get_decode_sh3_scale, get_splat_tex_size }, tsplat::{Tsplat, TsplatArray} }; @@ -159,6 +161,7 @@ impl PackedSplatsData { } } + #[cfg(feature = "gsplat")] pub fn to_gsplat_array(&mut self) -> anyhow::Result { let mut out = GsplatArray::new(); copy_getter_to_receiver(self, &mut out)?;