1use clap::Parser;
10use glam::*;
11
12use wgpu_3dgs_editor::{self as gs};
13
14#[derive(Parser, Debug)]
16#[command(
17 version,
18 about,
19 long_about = "\
20 A 3D Gaussian splatting editor to apply basic modifier to all Gaussians in a model.
21 "
22)]
23struct Args {
24 #[arg(short, long, default_value = "examples/model.ply")]
26 model: String,
27
28 #[arg(short, long, default_value = "target/output.ply")]
30 output: String,
31
32 #[arg(long)]
34 override_rgb: bool,
35
36 #[arg(
42 long,
43 allow_hyphen_values = true,
44 num_args = 3,
45 value_delimiter = ',',
46 default_value = "0.0,1.0,1.0"
47 )]
48 rgb_or_hsv: Vec<f32>,
49
50 #[arg(long, allow_hyphen_values = true, default_value = "1.0")]
52 alpha: f32,
53
54 #[arg(long, allow_hyphen_values = true, default_value = "0.0")]
58 contrast: f32,
59
60 #[arg(long, allow_hyphen_values = true, default_value = "0.0")]
64 exposure: f32,
65
66 #[arg(long, allow_hyphen_values = true, default_value = "1.0")]
70 gamma: f32,
71}
72
73type GaussianPod = gs::core::GaussianPodWithShSingleCov3dRotScaleConfigs;
74
75#[pollster::main]
76async fn main() {
77 env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
78
79 let args = Args::parse();
80 let model_path = &args.model;
81
82 log::debug!("Creating wgpu instance");
83 let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
84
85 log::debug!("Requesting adapter");
86 let adapter = instance
87 .request_adapter(&wgpu::RequestAdapterOptions::default())
88 .await
89 .expect("adapter");
90
91 log::debug!("Requesting device");
92 let (device, queue) = adapter
93 .request_device(&wgpu::DeviceDescriptor {
94 label: Some("Device"),
95 required_limits: adapter.limits(),
96 ..Default::default()
97 })
98 .await
99 .expect("device");
100
101 log::debug!("Creating gaussians");
102 let gaussians = [
103 gs::core::GaussiansSource::Ply,
104 gs::core::GaussiansSource::Spz,
105 ]
106 .into_iter()
107 .find_map(|source| gs::core::Gaussians::read_from_file(model_path, source).ok())
108 .expect("gaussians");
109
110 log::debug!("Creating editor");
111 let editor = gs::Editor::<GaussianPod>::new(&device, &gaussians);
112
113 log::debug!("Creating basic modifier");
114 let basic_modifier = gs::BasicModifier::<GaussianPod>::new(
115 &device,
116 &editor.gaussians_buffer,
117 &editor.model_transform_buffer,
118 &editor.gaussian_transform_buffer,
119 );
120
121 log::debug!("Configuring modifiers");
122 basic_modifier.basic_color_modifiers_buffer.update(
123 &queue,
124 match args.override_rgb {
125 true => gs::BasicColorRgbOverrideOrHsvModifiersPod::new_rgb_override,
126 false => gs::BasicColorRgbOverrideOrHsvModifiersPod::new_hsv_modifiers,
127 }(Vec3::from_slice(&args.rgb_or_hsv)),
128 args.alpha,
129 args.contrast,
130 args.exposure,
131 args.gamma,
132 );
133
134 log::info!("Starting editing process");
135 let time = std::time::Instant::now();
136
137 log::debug!("Editing Gaussians");
138 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
139 label: Some("Edit Encoder"),
140 });
141
142 editor.apply(
143 &device,
144 &mut encoder,
145 [&basic_modifier as &dyn gs::Modifier<GaussianPod>],
146 );
147
148 queue.submit(Some(encoder.finish()));
149
150 device
151 .poll(wgpu::PollType::wait_indefinitely())
152 .expect("poll");
153
154 log::info!("Editing process completed in {:?}", time.elapsed());
155
156 log::debug!("Downloading Gaussians");
157 let modified_gaussians = editor
158 .gaussians_buffer
159 .download_gaussians(&device, &queue)
160 .await
161 .map(|gs| {
162 match &args.output[args.output.len().saturating_sub(4)..] {
163 ".ply" => {
164 gs::core::Gaussians::Ply(gs::core::PlyGaussians::from_iter(gs.into_iter()))
165 }
166 ".spz" => {
167 gs::core::Gaussians::Spz(
168 gs::core::SpzGaussians::from_gaussians_with_options(
169 gs,
170 &gs::core::SpzGaussiansFromGaussianSliceOptions {
171 version: 2, ..Default::default()
173 },
174 )
175 .expect("SpzGaussians from gaussians"),
176 )
177 }
178 _ => panic!("Unsupported output file extension, expected .ply or .spz"),
179 }
180 })
181 .expect("gaussians download");
182
183 log::debug!("Writing modified Gaussians to output file");
184 modified_gaussians
185 .write_to_file(&args.output)
186 .expect("write modified Gaussians to output file");
187
188 log::info!("Modified Gaussians written to {}", args.output);
189}