Skip to content

Commit dee49c1

Browse files
authored
Merge pull request #4 from hacknus/fix_blending
fix_blending
2 parents db38857 + daeb2a9 commit dee49c1

File tree

6 files changed

+116
-27
lines changed

6 files changed

+116
-27
lines changed

CHANGELOG.MD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ All notable changes to the `bevy voxel plot` crate will be documented in this fi
88

99
* ...
1010

11+
# 2.3.0
12+
13+
* Fixed the sorting for transparent voxels.
14+
1115
# 2.2.0
1216

1317
* Assume voxel vertices are real world coordinates in shader.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "bevy_voxel_plot"
3-
version = "2.2.0"
3+
version = "2.3.0"
44
edition = "2021"
55
authors = ["Linus Leo Stöckli"]
66
repository = "https://github.com/hacknus/bevy_voxel_plot"

assets/shaders/instancing.wgsl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#import bevy_pbr::mesh_functions::{mesh_position_local_to_world}
21
#import bevy_pbr::view_transformations::position_world_to_clip
32

43
struct Vertex {

examples/bevy_egui.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,11 @@ fn voxel_plot_setup(
8686
let mut instances: Vec<InstanceData> = instances.into_iter().collect();
8787

8888
// Sort by opacity (color alpha channel) descending
89-
instances.sort_by(|a, b| {
90-
b.color[3]
91-
.partial_cmp(&a.color[3])
92-
.unwrap_or(std::cmp::Ordering::Equal)
93-
});
89+
// instances.sort_by(|a, b| {
90+
// b.color[3]
91+
// .partial_cmp(&a.color[3])
92+
// .unwrap_or(std::cmp::Ordering::Equal)
93+
// });
9494

9595
// Truncate to top 2 million most opaque points
9696
const MAX_INSTANCES: usize = 1_000_000;
@@ -102,6 +102,7 @@ fn voxel_plot_setup(
102102
let threshold = instances.last().unwrap().color[3];
103103
println!("Auto threshold for opacity was: {}", threshold);
104104
}
105+
let first_pass_layer = RenderLayers::layer(0);
105106

106107
commands.spawn((
107108
Mesh3d(meshes.add(Cuboid::new(cube_width, cube_height, cube_depth))),
@@ -153,7 +154,6 @@ fn voxel_plot_setup(
153154
commands.insert_resource(RenderImage(image_handle.clone()));
154155

155156
// This specifies the layer used for the first pass, which will be attached to the first pass camera and cube.
156-
let first_pass_layer = RenderLayers::layer(0);
157157

158158
let pan_orbit_id = commands
159159
.spawn((

examples/simple_cubes.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use bevy::prelude::*;
2+
use bevy::pbr::AmbientLight;
3+
use bevy_voxel_plot::{InstanceData, InstanceMaterialData, VoxelMaterialPlugin};
4+
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};
5+
6+
fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
7+
// Two cubes: one red (alpha 1.0), one blue (alpha 0.5)
8+
let instances = vec![
9+
InstanceData {
10+
pos_scale: [0.0, 0.0, 0.0, 1.0],
11+
color: LinearRgba::from(Color::srgba(1.0, 0.0, 0.0, 0.5))
12+
.to_f32_array()
13+
},
14+
InstanceData {
15+
pos_scale: [0.3, 0.0, 0.0, 1.0],
16+
color: LinearRgba::from(Color::srgba(0.0, 1.0, 0.0, 0.5))
17+
.to_f32_array()
18+
},
19+
InstanceData {
20+
pos_scale: [0.6, 0.0, 0.0, 1.0],
21+
color: LinearRgba::from(Color::srgba(0.0, 0.0, 1.0, 0.5))
22+
.to_f32_array()
23+
},
24+
];
25+
26+
let cube_mesh = meshes.add(Cuboid::new(0.2, 0.2, 0.2));
27+
28+
commands.spawn((
29+
Mesh3d(cube_mesh),
30+
InstanceMaterialData { instances },
31+
));
32+
33+
commands.insert_resource(AmbientLight {
34+
color: Color::WHITE,
35+
brightness: 1.5,
36+
affects_lightmapped_meshes: false,
37+
});
38+
39+
// Camera
40+
commands.spawn((
41+
Camera3d::default(),
42+
Transform::from_xyz(1.5, 1.0, 1.5).looking_at(Vec3::ZERO, Vec3::Y),
43+
PanOrbitCamera::default(),
44+
));
45+
}
46+
47+
fn main() {
48+
App::new()
49+
.add_plugins((DefaultPlugins, VoxelMaterialPlugin, PanOrbitCameraPlugin))
50+
.add_systems(Startup, setup)
51+
.run();
52+
}

src/bevy_voxel_plot.rs

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ pub const SHADER_HANDLE: Handle<Shader> = weak_handle!("123e4567-e89b-12d3-a456-
6363
impl Plugin for VoxelMaterialPlugin {
6464
fn build(&self, app: &mut App) {
6565
app.add_plugins(ExtractComponentPlugin::<InstanceMaterialData>::default());
66+
app.add_plugins(ExtractComponentPlugin::<CameraPosition>::default()); // Add this line
67+
6668
app.sub_app_mut(RenderApp)
6769
.add_render_command::<Transparent3d, DrawCustom>()
6870
.init_resource::<SpecializedMeshPipelines<CustomPipeline>>()
@@ -86,6 +88,7 @@ impl Plugin for VoxelMaterialPlugin {
8688
}
8789
}
8890

91+
8992
/// Single instance data containing position, scale and color.
9093
#[derive(Clone, Copy, Pod, Zeroable)]
9194
#[repr(C)]
@@ -155,40 +158,59 @@ struct InstanceBuffer {
155158
length: usize,
156159
}
157160

161+
#[derive(Component, Clone)]
162+
struct CameraPosition(Vec3);
163+
164+
impl ExtractComponent for CameraPosition {
165+
type QueryData = &'static GlobalTransform;
166+
type QueryFilter = With<Camera3d>;
167+
type Out = Self;
168+
169+
fn extract_component(transform: QueryItem<'_, Self::QueryData>) -> Option<Self> {
170+
Some(CameraPosition(transform.translation()))
171+
}
172+
}
173+
174+
158175
/// Prepares instance buffers each frame, sorting instances by distance to camera.
159176
fn prepare_instance_buffers(
160177
mut commands: Commands,
161178
query: Query<(Entity, &InstanceMaterialData)>,
162-
cameras: Query<&ExtractedView>,
163179
render_device: Res<RenderDevice>,
180+
camera_query: Query<&CameraPosition>,
164181
) {
165-
let Some(camera) = cameras.iter().next() else {
166-
return;
167-
};
168-
let cam_pos = camera.world_from_view.transform_point(Vec3::ZERO);
182+
let camera_pos = camera_query
183+
.iter()
184+
.next()
185+
.map(|pos| pos.0)
186+
.unwrap_or(Vec3::ZERO);
169187

170-
for (entity, instance_data) in &query {
171-
let mut sorted_instances = instance_data.instances.clone();
188+
// // Debug: Print camera position
189+
// println!("Camera position: {:?}", camera_pos);
172190

173-
if sorted_instances.is_empty() {
191+
for (entity, instance_data) in &query {
192+
if instance_data.instances.is_empty() {
174193
commands.entity(entity).remove::<InstanceBuffer>();
175194
continue;
176195
}
177196

178-
// Sort back-to-front for proper alpha blending
197+
let mut sorted_instances = instance_data.instances.clone();
179198
sorted_instances.sort_by(|a, b| {
180-
let a_pos = Vec3::new(a.pos_scale[0], a.pos_scale[1], a.pos_scale[2]);
181-
let b_pos = Vec3::new(b.pos_scale[0], b.pos_scale[1], b.pos_scale[2]);
182-
let a_dist = cam_pos.distance_squared(a_pos);
183-
let b_dist = cam_pos.distance_squared(b_pos);
184-
185-
b_dist
186-
.partial_cmp(&a_dist)
187-
.unwrap_or(std::cmp::Ordering::Equal)
199+
let dist_a = camera_pos.distance_squared(Vec3::from_slice(&a.pos_scale[0..3]));
200+
let dist_b = camera_pos.distance_squared(Vec3::from_slice(&b.pos_scale[0..3]));
201+
dist_b.partial_cmp(&dist_a).unwrap_or(std::cmp::Ordering::Equal)
188202
});
189203

204+
// Debug: Print instance order and distances
205+
// println!("Sorted instances (far to near):");
206+
// for inst in &sorted_instances {
207+
// let pos = Vec3::from_slice(&inst.pos_scale[0..3]);
208+
// let dist = camera_pos.distance(pos);
209+
// println!(" pos: {:?}, distance: {:.3}, color: {:?}", pos, dist, inst.color);
210+
// }
211+
190212
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
191-
label: Some("sorted instance data buffer"),
213+
label: Some("instance data buffer"),
192214
contents: bytemuck::cast_slice(sorted_instances.as_slice()),
193215
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
194216
});
@@ -200,6 +222,7 @@ fn prepare_instance_buffers(
200222
}
201223
}
202224

225+
203226
/// Custom pipeline for instanced mesh rendering.
204227
#[derive(Resource)]
205228
struct CustomPipeline {
@@ -242,7 +265,18 @@ impl SpecializedMeshPipeline for CustomPipeline {
242265

243266
descriptor.fragment.as_mut().unwrap().targets[0] = Some(ColorTargetState {
244267
format: color_format,
245-
blend: Some(BlendState::ALPHA_BLENDING),
268+
blend: Some(BlendState {
269+
color: BlendComponent {
270+
src_factor: BlendFactor::SrcAlpha,
271+
dst_factor: BlendFactor::OneMinusSrcAlpha,
272+
operation: BlendOperation::Add,
273+
},
274+
alpha: BlendComponent {
275+
src_factor: BlendFactor::SrcAlpha,
276+
dst_factor: BlendFactor::OneMinusSrcAlpha,
277+
operation: BlendOperation::Add,
278+
},
279+
}),
246280
write_mask: ColorWrites::ALL,
247281
});
248282

0 commit comments

Comments
 (0)