Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ All notable changes to the `bevy voxel plot` crate will be documented in this fi

### Added:

* ...
* Update to bevy 0.19

# 4.0.0 - 6.4.2026

Expand Down
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bevy_voxel_plot"
version = "4.0.0"
version = "5.0.0"
edition = "2021"
authors = ["Linus Leo Stöckli"]
repository = "https://github.com/hacknus/bevy_voxel_plot"
Expand All @@ -13,9 +13,9 @@ homepage = "https://github.com/hacknus/bevy_voxel_plot"
license = "Apache-2.0"

[dependencies]
bevy = "0.18"
bevy = "0.19.0-rc.1"
bytemuck = "1.25"

[dev-dependencies]
bevy_panorbit_camera = { version = "0.34" }
bevy_egui = { version = "0.39" }
bevy_panorbit_camera = { git = "https://github.com/hacknus/bevy_panorbit_camera", branch = "bevy_0.19" }
bevy_egui = { git = "https://github.com/taboky-dev/bevy_egui.git", rev = "d34a47e" }
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ low alpha.

| bevy | bevy_voxel_plot |
|------|-----------------|
| 0.19 | 5.0 |
| 0.18 | 4.0 |
| 0.17 | 3.0 |
| 0.16 | 2.0 |
Expand Down
30 changes: 18 additions & 12 deletions examples/bevy_egui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,18 +217,24 @@ pub fn update_gui(
let height = 500.0;

if let Ok(ctx) = contexts.ctx_mut() {
egui::CentralPanel::default().show(ctx, |ui| {
show_plot(
&mut meshes,
&cube_preview_texture_id,
width,
height,
ui,
&mut query,
&mut opacity_threshold,
&mut cam_input,
)
});
let available = ctx.content_rect();
egui::Area::new(egui::Id::new("bevy_egui_central_panel"))
.fixed_pos(available.min)
.show(ctx, |ui| {
ui.set_min_size(available.size());
egui::CentralPanel::default().show_inside(ui, |ui| {
show_plot(
&mut meshes,
&cube_preview_texture_id,
width,
height,
ui,
&mut query,
&mut opacity_threshold,
&mut cam_input,
)
});
});
}
}
fn show_plot(
Expand Down
124 changes: 69 additions & 55 deletions src/bevy_voxel_plot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
//! It's generally recommended to try the built-in instancing before going with this approach.

use bevy::asset::{load_internal_asset, uuid_handle};
use bevy::core_pipeline::core_3d::TransparentSortingInfo3d;
use bevy::mesh::{MeshVertexBufferLayoutRef, VertexBufferLayout};
use bevy::pbr::SetMeshViewBindingArrayBindGroup;
use bevy::render::sync_component::SyncComponent;
use bevy::render::RenderSystems;
use bevy::{
core_pipeline::core_3d::Transparent3d,
Expand All @@ -18,10 +20,12 @@ use bevy::{
system::{lifetimeless::*, SystemParamItem},
},
pbr::{
MeshPipeline, MeshPipelineKey, RenderMeshInstances, SetMeshBindGroup, SetMeshViewBindGroup,
self, MeshInputUniform, MeshPipeline, MeshPipelineKey, MeshPipelineSystems, MeshUniform,
RenderMeshInstances, SetMeshBindGroup, SetMeshViewBindGroup, ViewKeyCache,
},
prelude::*,
render::{
batching::gpu_preprocessing::BatchedInstanceBuffers,
extract_component::{ExtractComponent, ExtractComponentPlugin},
mesh::{allocator::MeshAllocator, RenderMesh, RenderMeshBufferInfo},
render_asset::RenderAssets,
Expand All @@ -33,11 +37,18 @@ use bevy::{
renderer::RenderDevice,
sync_world::MainEntity,
view::ExtractedView,
Render, RenderApp,
Render, RenderApp, RenderStartup,
},
};
use bytemuck::{Pod, Zeroable};

impl SyncComponent<()> for CameraPosition {
type Target = ();
}
impl SyncComponent<()> for InstanceMaterialData {
type Target = ();
}

/// Component holding per-instance data for custom rendering.
#[derive(Component)]
pub struct InstanceMaterialData {
Expand Down Expand Up @@ -69,6 +80,10 @@ impl Plugin for VoxelMaterialPlugin {
app.sub_app_mut(RenderApp)
.add_render_command::<Transparent3d, DrawCustom>()
.init_resource::<SpecializedMeshPipelines<CustomPipeline>>()
.add_systems(
RenderStartup,
init_custom_pipeline.after(MeshPipelineSystems),
)
.add_systems(
Render,
(
Expand All @@ -84,9 +99,22 @@ impl Plugin for VoxelMaterialPlugin {
);
}

fn finish(&self, app: &mut App) {
app.sub_app_mut(RenderApp).init_resource::<CustomPipeline>();
fn finish(&self, _app: &mut App) {}
}

fn init_custom_pipeline(
mut commands: Commands,
custom_pipeline: Option<Res<CustomPipeline>>,
mesh_pipeline: Res<MeshPipeline>,
) {
if custom_pipeline.is_some() {
return;
}

commands.insert_resource(CustomPipeline {
shader: SHADER_HANDLE.clone(),
mesh_pipeline: mesh_pipeline.clone(),
});
}

/// Single instance data containing position, scale and color.
Expand All @@ -110,44 +138,68 @@ fn queue_custom(
pipeline_cache: Res<PipelineCache>,
meshes: Res<RenderAssets<RenderMesh>>,
render_mesh_instances: Res<RenderMeshInstances>,
maybe_batched_instance_buffers: Option<
Res<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
>,
material_meshes: Query<(Entity, &MainEntity), With<InstanceMaterialData>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
views: Query<(&ExtractedView, &Msaa)>,
views: Query<&ExtractedView>,
view_key_cache: Res<ViewKeyCache>,
) {
let draw_custom = transparent_3d_draw_functions.read().id::<DrawCustom>();

for (view, msaa) in &views {
for view in &views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity)
else {
continue;
};

let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples());
let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr);
let rangefinder = view.rangefinder3d();
let Some(&view_key) = view_key_cache.get(&view.retained_view_entity) else {
continue;
};

for (entity, main_entity) in &material_meshes {
let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*main_entity)
else {
continue;
};
let Some(mesh) = meshes.get(mesh_instance.mesh_asset_id) else {
let Some(mesh) = meshes.get(mesh_instance.mesh_asset_id()) else {
continue;
};

let key =
view_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology());
let key = view_key
| MeshPipelineKey::BLEND_ALPHA
| MeshPipelineKey::from_primitive_topology_and_strip_index(
mesh.primitive_topology(),
mesh.index_format(),
);

let pipeline = pipelines
.specialize(&pipeline_cache, &custom_pipeline, key, &mesh.layout)
.unwrap();
transparent_phase.add(Transparent3d {
sorting_info: TransparentSortingInfo3d::Sorted {
mesh_center: pbr::get_mesh_instance_world_from_local(
*main_entity,
mesh_instance.current_uniform_index,
&render_mesh_instances,
maybe_batched_instance_buffers.as_deref(),
)
.transform_point3(
meshes
.get(mesh_instance.mesh_asset_id())
.unwrap()
.aabb_center,
),
depth_bias: 0.0,
},
entity: (entity, *main_entity),
pipeline,
draw_function: draw_custom,
distance: rangefinder.distance(&mesh_instance.center),
distance: 0.0,
batch_range: 0..1,
extra_index: PhaseItemExtraIndex::None,
indexed: false,
indexed: true,
});
}
}
Expand Down Expand Up @@ -234,17 +286,6 @@ struct CustomPipeline {
mesh_pipeline: MeshPipeline,
}

impl FromWorld for CustomPipeline {
fn from_world(world: &mut World) -> Self {
let mesh_pipeline = world.resource::<MeshPipeline>().clone();

CustomPipeline {
shader: SHADER_HANDLE.clone(),
mesh_pipeline,
}
}
}

impl SpecializedMeshPipeline for CustomPipeline {
type Key = MeshPipelineKey;

Expand All @@ -255,33 +296,6 @@ impl SpecializedMeshPipeline for CustomPipeline {
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut descriptor = self.mesh_pipeline.specialize(key, layout)?;

let color_format = TextureFormat::Rgba8UnormSrgb;

descriptor.depth_stencil = Some(DepthStencilState {
format: TextureFormat::Depth32Float,
depth_compare: CompareFunction::Always,
stencil: StencilState::default(),
depth_write_enabled: false,
bias: DepthBiasState::default(),
});

descriptor.fragment.as_mut().unwrap().targets[0] = Some(ColorTargetState {
format: color_format,
blend: Some(BlendState {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
}),
write_mask: ColorWrites::ALL,
});

descriptor.vertex.shader = self.shader.clone();
descriptor.vertex.buffers.push(VertexBufferLayout {
array_stride: size_of::<InstanceData>() as u64,
Expand Down Expand Up @@ -340,14 +354,14 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMeshInstanced {
else {
return RenderCommandResult::Skip;
};
let Some(gpu_mesh) = meshes.into_inner().get(mesh_instance.mesh_asset_id) else {
let Some(gpu_mesh) = meshes.into_inner().get(mesh_instance.mesh_asset_id()) else {
return RenderCommandResult::Skip;
};
let Some(instance_buffer) = instance_buffer else {
return RenderCommandResult::Skip;
};
let Some(vertex_buffer_slice) =
mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id)
mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id())
else {
return RenderCommandResult::Skip;
};
Expand All @@ -361,7 +375,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMeshInstanced {
count,
} => {
let Some(index_buffer_slice) =
mesh_allocator.mesh_index_slice(&mesh_instance.mesh_asset_id)
mesh_allocator.mesh_index_slice(&mesh_instance.mesh_asset_id())
else {
return RenderCommandResult::Skip;
};
Expand Down
Loading