Skip to content

Commit 5a1a604

Browse files
committed
fixed sorting for transparent voxels
1 parent d129e1c commit 5a1a604

5 files changed

Lines changed: 107 additions & 13 deletions

File tree

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"

examples/bevy_egui.rs

Lines changed: 7 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(1);
105106

106107
commands.spawn((
107108
Mesh3d(meshes.add(Cuboid::new(cube_width, cube_height, cube_depth))),
@@ -114,6 +115,7 @@ fn voxel_plot_setup(
114115
// We must disable the built-in frustum culling by adding the `NoFrustumCulling` marker
115116
// component to avoid incorrect culling.
116117
NoFrustumCulling,
118+
first_pass_layer.clone(),
117119
));
118120

119121
commands.insert_resource(AmbientLight {
@@ -153,7 +155,6 @@ fn voxel_plot_setup(
153155
commands.insert_resource(RenderImage(image_handle.clone()));
154156

155157
// 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);
157158

158159
let pan_orbit_id = commands
159160
.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: 43 additions & 6 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,34 +158,57 @@ 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)>,
162179
render_device: Res<RenderDevice>,
163-
views: Query<&GlobalTransform, With<Camera>>,
180+
camera_query: Query<&CameraPosition>,
164181
) {
165-
// Get the first camera's position, or use a default if none exist
166-
let camera_pos = views
182+
let camera_pos = camera_query
167183
.iter()
168184
.next()
169-
.map(|transform| transform.translation())
185+
.map(|pos| pos.0)
170186
.unwrap_or(Vec3::ZERO);
171187

188+
// // Debug: Print camera position
189+
// println!("Camera position: {:?}", camera_pos);
190+
172191
for (entity, instance_data) in &query {
173192
if instance_data.instances.is_empty() {
174193
commands.entity(entity).remove::<InstanceBuffer>();
175194
continue;
176195
}
177196

178-
// Sort back-to-front by distance from camera
179197
let mut sorted_instances = instance_data.instances.clone();
180198
sorted_instances.sort_by(|a, b| {
181199
let dist_a = camera_pos.distance_squared(Vec3::from_slice(&a.pos_scale[0..3]));
182200
let dist_b = camera_pos.distance_squared(Vec3::from_slice(&b.pos_scale[0..3]));
183201
dist_b.partial_cmp(&dist_a).unwrap_or(std::cmp::Ordering::Equal)
184202
});
185203

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+
186212
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
187213
label: Some("instance data buffer"),
188214
contents: bytemuck::cast_slice(sorted_instances.as_slice()),
@@ -239,7 +265,18 @@ impl SpecializedMeshPipeline for CustomPipeline {
239265

240266
descriptor.fragment.as_mut().unwrap().targets[0] = Some(ColorTargetState {
241267
format: color_format,
242-
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+
}),
243280
write_mask: ColorWrites::ALL,
244281
});
245282

0 commit comments

Comments
 (0)