Skip to content

Commit 14fe902

Browse files
start implementing
1 parent 61e3b6d commit 14fe902

File tree

8 files changed

+432
-310
lines changed

8 files changed

+432
-310
lines changed

src/app.rs

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use std::time::Instant;
1414

1515
pub struct ParticleApp {
1616
simulation: Box<dyn ParticleSimulation>,
17+
surface_format: wgpu::TextureFormat,
1718
renderer: ParticleRenderer,
1819
camera: Camera,
1920

@@ -94,20 +95,33 @@ impl ParticleApp {
9495
SimulationMethod::TransformFeedback
9596
};
9697

97-
// Create simulation based on method
98+
let surface_format = wgpu_render_state.target_format;
99+
98100
let initial_particles;
99101
let simulation: Box<dyn ParticleSimulation> = match default_method {
100102
SimulationMethod::Cpu => {
101103
initial_particles = 10_000;
102-
Box::new(CpuParticleSimulation::new(device, initial_particles))
104+
Box::new(CpuParticleSimulation::new(
105+
device,
106+
initial_particles,
107+
surface_format,
108+
))
103109
}
104110
SimulationMethod::ComputeShader => {
105111
initial_particles = 1_000_000;
106-
Box::new(ComputeParticleSimulation::new(device, initial_particles))
112+
Box::new(ComputeParticleSimulation::new(
113+
device,
114+
initial_particles,
115+
surface_format,
116+
))
107117
}
108118
SimulationMethod::TransformFeedback => {
109119
initial_particles = 100_000;
110-
Box::new(TransformFeedbackSimulation::new(device, initial_particles))
120+
Box::new(TransformFeedbackSimulation::new(
121+
device,
122+
initial_particles,
123+
surface_format,
124+
))
111125
}
112126
};
113127

@@ -122,6 +136,7 @@ impl ParticleApp {
122136

123137
Self {
124138
simulation,
139+
surface_format,
125140
renderer,
126141
camera,
127142

@@ -161,13 +176,21 @@ impl ParticleApp {
161176

162177
// Create new simulation with the same particle count
163178
self.simulation = match new_method {
164-
SimulationMethod::Cpu => Box::new(CpuParticleSimulation::new(device, current_count)),
165-
SimulationMethod::ComputeShader => {
166-
Box::new(ComputeParticleSimulation::new(device, current_count))
167-
}
168-
SimulationMethod::TransformFeedback => {
169-
Box::new(TransformFeedbackSimulation::new(device, current_count))
170-
}
179+
SimulationMethod::Cpu => Box::new(CpuParticleSimulation::new(
180+
device,
181+
current_count,
182+
self.surface_format,
183+
)),
184+
SimulationMethod::ComputeShader => Box::new(ComputeParticleSimulation::new(
185+
device,
186+
current_count,
187+
self.surface_format,
188+
)),
189+
SimulationMethod::TransformFeedback => Box::new(TransformFeedbackSimulation::new(
190+
device,
191+
current_count,
192+
self.surface_format,
193+
)),
171194
};
172195

173196
self.simulation.set_paused(was_paused);

src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ fn main() -> eframe::Result {
4848
limits.max_storage_buffer_binding_size =
4949
limits.max_storage_buffer_binding_size.max(128 << 20); // 128 MB
5050

51+
let mut features = wgpu::Features::empty();
52+
5153
wgpu::DeviceDescriptor {
5254
label: Some("Particle Simulation Device"),
5355
required_features: wgpu::Features::empty(),

src/renderer.rs

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -72,30 +72,19 @@ impl ParticleRenderer {
7272
entry_point: Some("fs_main"),
7373
targets: &[Some(wgpu::ColorTargetState {
7474
format: *surface_format,
75-
blend: Some(wgpu::BlendState {
76-
color: wgpu::BlendComponent {
77-
src_factor: wgpu::BlendFactor::SrcAlpha,
78-
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
79-
operation: wgpu::BlendOperation::Add,
80-
},
81-
alpha: wgpu::BlendComponent {
82-
src_factor: wgpu::BlendFactor::One,
83-
dst_factor: wgpu::BlendFactor::One,
84-
operation: wgpu::BlendOperation::Add,
85-
},
86-
}),
75+
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
8776
write_mask: wgpu::ColorWrites::ALL,
8877
})],
8978
compilation_options: Default::default(),
9079
}),
9180
primitive: wgpu::PrimitiveState {
9281
topology: wgpu::PrimitiveTopology::PointList,
93-
strip_index_format: None,
94-
front_face: wgpu::FrontFace::Ccw,
95-
cull_mode: Some(wgpu::Face::Back),
96-
polygon_mode: wgpu::PolygonMode::Fill,
97-
unclipped_depth: false,
98-
conservative: false,
82+
..Default::default() // strip_index_format: None,
83+
// front_face: wgpu::FrontFace::Ccw,
84+
// cull_mode: Some(wgpu::Face::Back),
85+
// polygon_mode: wgpu::PolygonMode::Fill,
86+
// unclipped_depth: false,
87+
// conservative: false,
9988
},
10089
depth_stencil: None,
10190
multisample: wgpu::MultisampleState {
Lines changed: 105 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,125 @@
1-
struct Particle {
2-
position: vec3<f32>,
3-
padding1: f32,
4-
velocity: vec3<f32>,
5-
padding2: f32,
6-
color: vec4<f32>,
1+
struct ParticleInput {
2+
@location(0) position: vec3<f32>,
3+
@location(1) padding1: f32,
4+
@location(2) velocity: vec3<f32>,
5+
@location(3) padding2: f32,
6+
@location(4) color: vec4<f32>,
77
};
88

9+
// Output structure matching the Particle struct layout for TF capture
10+
struct ParticleOutput {
11+
@location(0) out_position: vec3<f32>,
12+
@location(1) out_padding1: f32,
13+
@location(2) out_velocity: vec3<f32>,
14+
@location(3) out_padding2: f32,
15+
@location(4) out_color: vec4<f32>,
16+
// We still need a dummy @builtin(position) for the rasterizer, even if TF is active
17+
@builtin(position) clip_position: vec4<f32>,
18+
};
19+
20+
// Uniforms (same as compute shader)
921
struct SimParams {
22+
// First 16 bytes
1023
delta_time: f32,
1124
gravity: f32,
1225
color_mode: u32,
1326
mouse_force: f32,
27+
28+
// Second 16 bytes
1429
mouse_radius: f32,
15-
mouse_position: vec3<f32>,
1630
is_mouse_dragging: u32,
1731
damping: f32,
18-
};
19-
20-
@group(0) @binding(0)
21-
var<storage, read> particles_in: array<Particle>;
22-
23-
@group(0) @binding(1)
24-
var<uniform> params: SimParams;
25-
26-
@group(0) @binding(2)
27-
var<storage, read_write> particles_out: array<Particle>;
32+
_padding1: u32, // Ensure correct padding for std140/std430 alignment
2833

29-
struct VertexOutput {
30-
@builtin(position) position: vec4<f32>,
34+
// Last 16 bytes
35+
mouse_position: vec3<f32>,
36+
_padding2: u32, // Ensure correct padding
3137
};
3238

39+
@group(0) @binding(0) var<uniform> params: SimParams;
40+
3341
@vertex
34-
fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {
35-
var output: VertexOutput;
42+
fn vs_main(particle_in: ParticleInput) -> ParticleOutput {
43+
44+
// --- Simulation Logic (using particle_in) ---
45+
var new_velocity = particle_in.velocity;
46+
var new_position = particle_in.position;
47+
var new_color = particle_in.color;
48+
49+
// Apply gravity
50+
new_velocity.y -= params.gravity * params.delta_time;
51+
52+
// Apply mouse force
53+
if (params.is_mouse_dragging > 0u) {
54+
let dir = params.mouse_position - new_position;
55+
let dist = length(dir);
56+
57+
if (dist < params.mouse_radius * 2.0) {
58+
// Make the force stronger overall and more dramatic for closer particles
59+
let force_factor = pow(1.0 - dist / (params.mouse_radius * 2.0), 2.0) * 2.0;
60+
let force = normalize(dir) * params.mouse_force * force_factor;
61+
new_velocity += force * params.delta_time;
62+
}
63+
}
3664

37-
// Get current particle
38-
var particle = particles_in[vertex_index];
65+
// Update position
66+
new_position += new_velocity * params.delta_time;
67+
68+
// Handle boundaries
69+
let bounds = 500.0;
70+
if (new_position.x < -bounds) {
71+
new_position.x = -bounds;
72+
new_velocity.x = abs(new_velocity.x) * 0.5;
73+
} else if (new_position.x > bounds) {
74+
new_position.x = bounds;
75+
new_velocity.x = -abs(new_velocity.x) * 0.5;
76+
}
77+
// ... (repeat for y and z boundaries) ...
78+
if (new_position.y < -bounds) {
79+
new_position.y = -bounds;
80+
new_velocity.y = abs(new_velocity.y) * 0.5;
81+
} else if (new_position.y > bounds) {
82+
new_position.y = bounds;
83+
new_velocity.y = -abs(new_velocity.y) * 0.5;
84+
}
85+
if (new_position.z < -bounds) {
86+
new_position.z = -bounds;
87+
new_velocity.z = abs(new_velocity.z) * 0.5;
88+
} else if (new_position.z > bounds) {
89+
new_position.z = bounds;
90+
new_velocity.z = -abs(new_velocity.z) * 0.5;
91+
}
3992

40-
// Apply gravity
41-
particle.velocity.y -= params.gravity * params.delta_time;
4293

43-
// Apply mouse force (same as compute shader)
44-
if (params.is_mouse_dragging > 0u) {
45-
let dir = params.mouse_position - particle.position;
46-
let dist = length(dir);
94+
// Apply damping
95+
new_velocity *= params.damping;
4796

48-
if (dist < params.mouse_radius * 2.0) {
49-
let force_factor = pow(1.0 - dist / (params.mouse_radius * 2.0), 2.0) * 2.0;
50-
let force = normalize(dir) * params.mouse_force * force_factor;
51-
particle.velocity += force * params.delta_time;
97+
// Update color based on mode
98+
if (params.color_mode == 1u) {
99+
// Velocity-based coloring
100+
let speed = length(new_velocity);
101+
let norm_speed = min(speed / 5.0, 1.0);
102+
new_color = vec4<f32>(norm_speed, 0.5 - norm_speed * 0.5, 1.0 - norm_speed, 1.0);
103+
} else if (params.color_mode == 2u) {
104+
// Position-based coloring
105+
let norm_pos = (new_position / bounds + vec3<f32>(1.0)) * 0.5;
106+
new_color = vec4<f32>(norm_pos.x, norm_pos.y, norm_pos.z, 1.0);
52107
}
53-
}
54-
55-
// Update position
56-
particle.position += particle.velocity * params.delta_time;
57-
58-
// Handle boundaries
59-
let bounds = 500.0;
60-
61-
if (particle.position.x < -bounds) {
62-
particle.position.x = -bounds;
63-
particle.velocity.x = abs(particle.velocity.x) * 0.5;
64-
} else if (particle.position.x > bounds) {
65-
particle.position.x = bounds;
66-
particle.velocity.x = -abs(particle.velocity.x) * 0.5;
67-
}
68-
69-
if (particle.position.y < -bounds) {
70-
particle.position.y = -bounds;
71-
particle.velocity.y = abs(particle.velocity.y) * 0.5;
72-
} else if (particle.position.y > bounds) {
73-
particle.position.y = bounds;
74-
particle.velocity.y = -abs(particle.velocity.y) * 0.5;
75-
}
76-
77-
if (particle.position.z < -bounds) {
78-
particle.position.z = -bounds;
79-
particle.velocity.z = abs(particle.velocity.z) * 0.5;
80-
} else if (particle.position.z > bounds) {
81-
particle.position.z = bounds;
82-
particle.velocity.z = -abs(particle.velocity.z) * 0.5;
83-
}
84-
85-
// Apply damping
86-
particle.velocity *= params.damping;
87-
88-
// Update color based on mode (same logic as compute shader)
89-
if (params.color_mode == 1u) {
90-
// Velocity-based coloring
91-
let speed = length(particle.velocity);
92-
let norm_speed = min(speed / 5.0, 1.0);
93-
particle.color = vec4<f32>(norm_speed, 0.5 - norm_speed * 0.5, 1.0 - norm_speed, 1.0);
94-
} else if (params.color_mode == 2u) {
95-
// Position-based coloring
96-
let norm_pos = (particle.position / bounds + 1.0) * 0.5;
97-
particle.color = vec4<f32>(norm_pos.x, norm_pos.y, norm_pos.z, 1.0);
98-
}
99-
100-
// Write updated particle to output buffer
101-
particles_out[vertex_index] = particle;
102-
103-
// We don't actually render anything in this pass
104-
output.position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
105-
return output;
108+
// else: keep original color (already in new_color)
109+
110+
// --- Output the NEW state ---
111+
var output: ParticleOutput;
112+
output.out_position = new_position;
113+
output.out_padding1 = 0.0; // Ensure padding is written
114+
output.out_velocity = new_velocity;
115+
output.out_padding2 = 0.0; // Ensure padding is written
116+
output.out_color = new_color;
117+
output.clip_position = vec4(0.0, 0.0, 0.0, 1.0); // Dummy position, not used for rendering here
118+
119+
return output;
106120
}
121+
122+
@fragment
123+
fn fs_dummy_main() {
124+
125+
}

src/simulation/compute.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ pub struct ComputeParticleSimulation {
1414
}
1515

1616
impl ParticleSimulation for ComputeParticleSimulation {
17-
fn new(device: &wgpu::Device, initial_particle_count: u32) -> Self {
17+
fn new(
18+
device: &wgpu::Device,
19+
initial_particle_count: u32,
20+
_surface_format: wgpu::TextureFormat,
21+
) -> Self {
1822
// Create initial particles
1923
let particles = generate_initial_particles(initial_particle_count);
2024

src/simulation/cpu.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ pub struct CpuParticleSimulation {
1111
}
1212

1313
impl ParticleSimulation for CpuParticleSimulation {
14-
fn new(device: &wgpu::Device, initial_particle_count: u32) -> Self {
14+
fn new(
15+
device: &wgpu::Device,
16+
initial_particle_count: u32,
17+
_surface_format: wgpu::TextureFormat,
18+
) -> Self {
1519
let particles = generate_initial_particles(initial_particle_count);
1620

1721
let particle_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {

src/simulation/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ pub enum SimulationMethod {
1414
}
1515

1616
pub trait ParticleSimulation {
17-
fn new(device: &Device, initial_particle_count: u32) -> Self
17+
fn new(
18+
device: &Device,
19+
initial_particle_count: u32,
20+
surface_format: wgpu::TextureFormat,
21+
) -> Self
1822
where
1923
Self: Sized;
2024
fn update(
@@ -100,7 +104,7 @@ pub fn generate_initial_particles(count: u32) -> Vec<Particle> {
100104
let g = rng.random::<f32>();
101105
let b = rng.random::<f32>();
102106

103-
let phi = rng.random::<f32>() * std::f32::consts::PI * 2.0;
107+
let phi = rng.random::<f32>() * std::f32::consts::TAU;
104108
let theta = (rng.random::<f32>() - 0.5) * std::f32::consts::PI;
105109
let radius = rng.random::<f32>() * 50.0;
106110

0 commit comments

Comments
 (0)