diff --git a/Cargo.lock b/Cargo.lock index f2392d5..6cfc93c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -434,6 +434,51 @@ dependencies = [ "bevy_internal", ] +[[package]] +name = "bevy-inspector-egui" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4971e763f289921fd4616418628458bec26a6fc13fe4299c0e4066f39d7ceaa2" +dependencies = [ + "bevy-inspector-egui-derive", + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_core_pipeline", + "bevy_ecs", + "bevy_egui", + "bevy_image", + "bevy_log", + "bevy_math", + "bevy_pbr", + "bevy_platform", + "bevy_reflect", + "bevy_render", + "bevy_state", + "bevy_time", + "bevy_utils", + "bevy_window", + "bytemuck", + "disqualified", + "egui", + "fuzzy-matcher", + "image", + "smallvec", + "uuid", + "winit", +] + +[[package]] +name = "bevy-inspector-egui-derive" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2656316165dbe2af6b3acaa763332f5dbdd12f809d59f5bf4304e0642a8005c9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "bevy_a11y" version = "0.16.0" @@ -1014,6 +1059,16 @@ dependencies = [ "glam", ] +[[package]] +name = "bevy_panorbit_camera" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6e15e297754d0bcb7665620c390c4f05665d4ac4ac91b4b5d3c66b9fe1f0e6" +dependencies = [ + "bevy", + "bevy_egui", +] + [[package]] name = "bevy_pbr" version = "0.16.0" @@ -1471,7 +1526,9 @@ name = "bevyart" version = "0.1.0" dependencies = [ "bevy", + "bevy-inspector-egui", "bevy_egui", + "bevy_panorbit_camera", ] [[package]] @@ -2507,6 +2564,15 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fuzzy-matcher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" +dependencies = [ + "thread_local", +] + [[package]] name = "generic-array" version = "0.14.7" diff --git a/Cargo.toml b/Cargo.toml index ed730ea..5cfb846 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,9 @@ edition = "2024" [dependencies] bevy = { version = "0.16.0", features = ["shader_format_wesl"] } +bevy-inspector-egui = "0.31.0" bevy_egui = "0.34.1" +bevy_panorbit_camera = { version = "0.26.0", features = ["bevy_egui"] } # Enable a small amount of optimization in the dev profile. [profile.dev] diff --git a/assets/shaders/material.wgsl b/assets/shaders/mandelbulb.wgsl similarity index 85% rename from assets/shaders/material.wgsl rename to assets/shaders/mandelbulb.wgsl index 834b8ae..2914d4f 100644 --- a/assets/shaders/material.wgsl +++ b/assets/shaders/mandelbulb.wgsl @@ -7,18 +7,24 @@ struct VoluMaterial { mesh_translation: vec3, - sphere_radius: f32, - color: vec4, model_inverse: mat4x4, power: f32, iterations: i32, + max_distance: f32, + max_steps: i32, + ray_epsilon: f32, + normal_epsilon: f32, + scale: f32, } @group(2) @binding(100) var volu_material: VoluMaterial; fn mandelbulb_sdf(p: vec3) -> f32 { - var z = p; + let scale = volu_material.scale; + let p_scaled = p / scale; + + var z = p_scaled; var dr = 1.0; var r = 0.0; let power = volu_material.power; @@ -42,14 +48,14 @@ fn mandelbulb_sdf(p: vec3) -> f32 { sin(new_theta) * cos(new_phi), sin(new_theta) * sin(new_phi), cos(new_theta) - ) + p; + ) + p_scaled; } - return 0.5 * log(max(r, 0.001)) * r / dr; + return 0.5 * log(max(r, 0.001)) * r / dr * scale; } fn mandelbulb_normal(p: vec3) -> vec3 { - let e = 0.001; + let e = volu_material.normal_epsilon; let dx = mandelbulb_sdf(p + vec3(e, 0.0, 0.0)) - mandelbulb_sdf(p - vec3(e, 0.0, 0.0)); let dy = mandelbulb_sdf(p + vec3(0.0, e, 0.0)) - mandelbulb_sdf(p - vec3(0.0, e, 0.0)); let dz = mandelbulb_sdf(p + vec3(0.0, 0.0, e)) - mandelbulb_sdf(p - vec3(0.0, 0.0, e)); @@ -58,9 +64,9 @@ fn mandelbulb_normal(p: vec3) -> vec3 { fn raymarch(ray_origin: vec3, ray_direction: vec3) -> f32 { var t = 0.0; - let max_distance = 100.0; - let max_steps = 128; - let epsilon = 0.001; + let max_distance = volu_material.max_distance; + let max_steps = volu_material.max_steps; + let epsilon = volu_material.ray_epsilon; for (var i = 0; i < max_steps; i++) { let current_pos = ray_origin + t * ray_direction; @@ -87,7 +93,7 @@ fn fragment( ) -> FragmentOutput { var in = vertex_output; - let ray_origin = view.world_position; + let ray_origin = in.world_position.xyz; let ray_direction = normalize(in.world_position.xyz - view.world_position); let local_origin = (volu_material.model_inverse * vec4(ray_origin, 1.0)).xyz; @@ -98,7 +104,7 @@ fn fragment( var out: FragmentOutput; if t > 0.0 { - let local_hit_point = ray_origin + t * ray_direction; + let local_hit_point = local_origin + t * local_direction; let local_normal = mandelbulb_normal(local_hit_point); let world_hit_point = (transpose(volu_material.model_inverse) * vec4(local_hit_point, 1.0)).xyz; diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..76b9a27 --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,14 @@ +use bevy::prelude::*; +use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin}; + +pub fn plugin(app: &mut App) { + app.add_plugins(PanOrbitCameraPlugin) + .add_systems(Startup, setup); +} + +fn setup(mut commands: Commands) { + commands.spawn(( + Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), + PanOrbitCamera::default(), + )); +} diff --git a/src/main.rs b/src/main.rs index 4f08275..2041992 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,50 +1,44 @@ -use bevy::{ - color::palettes::css::RED, - pbr::{ExtendedMaterial, MaterialExtension}, - prelude::*, - render::{render_resource::*, storage::ShaderStorageBuffer}, -}; +use bevy::{color::palettes::css::RED, prelude::*, render::storage::ShaderStorageBuffer}; +use bevy_egui::EguiPlugin; +use mandelbulb::{MandelbulbExtension, MandelbulbMaterial, MandelbulbStorage}; -const SHADER_ASSET_PATH: &str = "shaders/material.wgsl"; - -type VoluMaterial = ExtendedMaterial; +mod camera; +mod mandelbulb; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugins(MaterialPlugin::::default()) + .add_plugins(( + DefaultPlugins, + EguiPlugin { + enable_multipass_for_primary_context: false, + }, + )) + .add_plugins((camera::plugin, mandelbulb::plugin)) .add_systems(Startup, setup) - .add_systems(Update, (rotate_things, update_volu_material)) + .add_systems(Update, (rotate_things,)) .run(); } fn setup( mut commands: Commands, mut meshes: ResMut>, - mut materials: ResMut>, + mut materials: ResMut>, mut buffers: ResMut>, ) { let transform = Transform::from_xyz(0.0, 0.0, 0.0); - let storage = buffers.add(ShaderStorageBuffer::from(VoluStorage { - mesh_translation: transform.translation, - sphere_radius: 1.0, - color: Vec4::default(), - model_inverse: transform.compute_matrix().inverse(), - power: 8.0, - iterations: 32, - })); + let storage = buffers.add(ShaderStorageBuffer::from(MandelbulbStorage::default())); commands.spawn(( Mesh3d(meshes.add(Cuboid::new(2.0, 2.0, 2.0))), - MeshMaterial3d(materials.add(ExtendedMaterial { - base: StandardMaterial { + MeshMaterial3d(materials.add(MandelbulbExtension::create_material( + StandardMaterial { base_color: RED.into(), alpha_mode: AlphaMode::Blend, ..default() }, - extension: VoluExtension { storage }, - })), + storage, + ))), transform, Rotate, )); @@ -54,11 +48,6 @@ fn setup( Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y), Rotate, )); - - commands.spawn(( - Camera3d::default(), - Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), - )); } #[derive(Component)] @@ -69,53 +58,3 @@ fn rotate_things(mut q: Query<&mut Transform, With>, time: Res