Merge pull request #2 from eliasstepanik/es-branch-1
Working Rendering with expanding octree
This commit is contained in:
commit
d5a013db3e
@ -14,3 +14,4 @@ bevy_window = "0.15.0"
|
|||||||
egui_dock = "0.14.0"
|
egui_dock = "0.14.0"
|
||||||
bytemuck = "1.13"
|
bytemuck = "1.13"
|
||||||
bevy_mod_debugdump = "0.12.1"
|
bevy_mod_debugdump = "0.12.1"
|
||||||
|
log = "0.4.25"
|
||||||
@ -11,7 +11,7 @@ impl Plugin for EnvironmentPlugin {
|
|||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
|
|
||||||
app.add_systems(Startup, (setup).chain());
|
app.add_systems(Startup, (setup).chain());
|
||||||
app.add_systems(Update, (crate::systems::voxels::rendering::render,crate::systems::voxels::debug::visualize_octree.run_if(should_visualize_octree), crate::systems::voxels::debug::draw_grid.run_if(should_draw_grid)).chain());
|
app.add_systems(Update, (crate::systems::voxels::rendering::render,crate::systems::voxels::debug::visualize_octree_system.run_if(should_visualize_octree), crate::systems::voxels::debug::draw_grid.run_if(should_draw_grid)).chain());
|
||||||
|
|
||||||
app.register_type::<SparseVoxelOctree>();
|
app.register_type::<SparseVoxelOctree>();
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,14 @@ pub struct CameraController {
|
|||||||
pub sensitivity: f32,
|
pub sensitivity: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Component, Default)]
|
||||||
|
pub struct Selector {
|
||||||
|
pub selected_voxel: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl Default for CameraController {
|
impl Default for CameraController {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -40,8 +48,8 @@ pub fn setup(mut commands: Commands,){
|
|||||||
..default()
|
..default()
|
||||||
}),
|
}),
|
||||||
MainCamera,
|
MainCamera,
|
||||||
CameraController::default()
|
CameraController::default(),
|
||||||
|
Selector::default(),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
@ -59,6 +67,7 @@ pub fn camera_controller_system(
|
|||||||
// We'll update DoubleTransform for the "true" position
|
// We'll update DoubleTransform for the "true" position
|
||||||
// and keep Transform in sync for rendering.a
|
// and keep Transform in sync for rendering.a
|
||||||
mut query: Query<(&mut Transform, &mut CameraController)>,
|
mut query: Query<(&mut Transform, &mut CameraController)>,
|
||||||
|
mut selector: Query<(&mut Selector), With<CameraController>>,
|
||||||
mut octree_query: Query<&mut SparseVoxelOctree>,
|
mut octree_query: Query<&mut SparseVoxelOctree>,
|
||||||
mut app_exit_events: EventWriter<AppExit>,
|
mut app_exit_events: EventWriter<AppExit>,
|
||||||
) {
|
) {
|
||||||
@ -178,7 +187,7 @@ pub fn camera_controller_system(
|
|||||||
}
|
}
|
||||||
if keyboard_input.just_pressed(KeyCode::KeyQ) && window.cursor_options.visible == false{
|
if keyboard_input.just_pressed(KeyCode::KeyQ) && window.cursor_options.visible == false{
|
||||||
for mut octree in octree_query.iter_mut() {
|
for mut octree in octree_query.iter_mut() {
|
||||||
octree.insert(transform.translation.x, transform.translation.y, transform.translation.z, Voxel::new(Color::srgb(1.0, 0.0, 0.0)));
|
octree.insert(transform.translation, Voxel::new(Color::srgb(1.0, 0.0, 0.0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,49 +214,40 @@ pub fn camera_controller_system(
|
|||||||
if let Some((hit_x, hit_y, hit_z, depth,normal)) = octree.raycast(&ray) {
|
if let Some((hit_x, hit_y, hit_z, depth,normal)) = octree.raycast(&ray) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*//TODO: Currently broken needs fixing to work with double precision
|
|
||||||
println!("raycast: {:?}", ray);
|
|
||||||
// Visualize the ray
|
|
||||||
lines.lines.push(EphemeralLine {
|
|
||||||
start: ray_origin.as_vec3(),
|
|
||||||
end: DVec3::new(hit_x, hit_y, hit_z).as_vec3(),
|
|
||||||
color: Color::from(GREEN),
|
|
||||||
time_left: 5.0, // draw for 2 seconds
|
|
||||||
});*/
|
|
||||||
|
|
||||||
/*gizmos.ray(
|
|
||||||
ray.origin,
|
|
||||||
ray.direction,
|
|
||||||
BLUE,
|
|
||||||
);*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if mouse_button_input.just_pressed(MouseButton::Right) {
|
if mouse_button_input.just_pressed(MouseButton::Right) {
|
||||||
|
|
||||||
let voxel_size = octree.get_spacing_at_depth(depth);
|
if keyboard_input.pressed(KeyCode::ControlLeft) {
|
||||||
let hit_position = Vec3::new(hit_x as f32, hit_y as f32, hit_z as f32);
|
let voxel_size = octree.get_spacing_at_depth(depth);
|
||||||
let epsilon = voxel_size * 0.1; // Adjust this value as needed (e.g., 0.1 times the voxel size)
|
let hit_position = Vec3::new(hit_x as f32, hit_y as f32, hit_z as f32);
|
||||||
|
let epsilon = voxel_size * 0.1; // Adjust this value as needed (e.g., 0.1 times the voxel size)
|
||||||
|
|
||||||
// Offset position by epsilon in the direction of the normal
|
// Offset position by epsilon in the direction of the normal
|
||||||
let offset_position = hit_position - (normal * Vec3::new(epsilon as f32, epsilon as f32, epsilon as f32));
|
let offset_position = hit_position - (normal * Vec3::new(epsilon as f32, epsilon as f32, epsilon as f32));
|
||||||
|
|
||||||
|
// Align the offset position to the center of the nearest voxel
|
||||||
|
let new_voxel = octree.normalize_to_voxel_at_depth(
|
||||||
|
offset_position,
|
||||||
|
depth,
|
||||||
|
);
|
||||||
|
|
||||||
|
selector.single_mut().selected_voxel = new_voxel;
|
||||||
|
info!("Selected Voxel: {:?}", selector.single().selected_voxel);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
let voxel_size = octree.get_spacing_at_depth(depth);
|
||||||
|
let hit_position = Vec3::new(hit_x as f32, hit_y as f32, hit_z as f32);
|
||||||
|
let epsilon = voxel_size * 0.1; // Adjust this value as needed (e.g., 0.1 times the voxel size)
|
||||||
|
|
||||||
|
// Offset position by epsilon in the direction of the normal
|
||||||
|
let offset_position = hit_position - (normal * Vec3::new(epsilon as f32, epsilon as f32, epsilon as f32));
|
||||||
|
|
||||||
|
// Remove the voxel
|
||||||
|
octree.remove(offset_position);
|
||||||
|
}
|
||||||
|
|
||||||
// Align the offset position to the center of the nearest voxel
|
|
||||||
let (new_voxel_x, new_voxel_y, new_voxel_z) = octree.normalize_to_voxel_at_depth(
|
|
||||||
offset_position.x,
|
|
||||||
offset_position.y,
|
|
||||||
offset_position.z,
|
|
||||||
depth,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remove the voxel
|
|
||||||
octree.remove(new_voxel_x, new_voxel_y, new_voxel_z);
|
|
||||||
}
|
}
|
||||||
else if mouse_button_input.just_pressed(MouseButton::Left) {
|
else if mouse_button_input.just_pressed(MouseButton::Left) {
|
||||||
|
|
||||||
@ -258,19 +258,9 @@ pub fn camera_controller_system(
|
|||||||
// Offset position by epsilon in the direction of the normal
|
// Offset position by epsilon in the direction of the normal
|
||||||
let offset_position = hit_position + (normal * Vec3::new(epsilon as f32, epsilon as f32, epsilon as f32));
|
let offset_position = hit_position + (normal * Vec3::new(epsilon as f32, epsilon as f32, epsilon as f32));
|
||||||
|
|
||||||
// Align the offset position to the center of the nearest voxel
|
|
||||||
let (new_voxel_x, new_voxel_y, new_voxel_z) = octree.normalize_to_voxel_at_depth(
|
|
||||||
offset_position.x,
|
|
||||||
offset_position.y,
|
|
||||||
offset_position.z,
|
|
||||||
depth,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Insert the new voxel
|
// Insert the new voxel
|
||||||
octree.insert(
|
octree.insert(
|
||||||
new_voxel_x,
|
offset_position,
|
||||||
new_voxel_y,
|
|
||||||
new_voxel_z,
|
|
||||||
Voxel::new(Color::srgb(1.0, 0.0, 0.0)),
|
Voxel::new(Color::srgb(1.0, 0.0, 0.0)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,15 +62,8 @@ use crate::systems::voxels::structure::{SparseVoxelOctree, Voxel};
|
|||||||
pub fn setup(mut commands: Commands,) {
|
pub fn setup(mut commands: Commands,) {
|
||||||
|
|
||||||
|
|
||||||
let voxels_per_unit = 16;
|
let unit_size = 1.0;
|
||||||
let unit_size = 1.0; // 1 unit in your coordinate space
|
|
||||||
let voxel_size = unit_size / voxels_per_unit as f32;
|
|
||||||
|
|
||||||
/*//Octree
|
|
||||||
let octree_base_size = 64.0;
|
|
||||||
let octree_depth = 10;*/
|
|
||||||
|
|
||||||
// Octree parameters
|
|
||||||
let octree_base_size = 64.0 * unit_size; // Octree's total size in your world space
|
let octree_base_size = 64.0 * unit_size; // Octree's total size in your world space
|
||||||
let octree_depth = 10;
|
let octree_depth = 10;
|
||||||
|
|
||||||
@ -80,11 +73,15 @@ pub fn setup(mut commands: Commands,) {
|
|||||||
|
|
||||||
let color = Color::rgb(0.2, 0.8, 0.2);
|
let color = Color::rgb(0.2, 0.8, 0.2);
|
||||||
/*generate_voxel_rect(&mut octree,color);*/
|
/*generate_voxel_rect(&mut octree,color);*/
|
||||||
/*generate_voxel_sphere(&mut octree, 10.0, color);*/
|
generate_voxel_sphere(&mut octree, 10, color);
|
||||||
|
|
||||||
generate_large_plane(&mut octree, 200, 200,color );
|
/*generate_large_plane(&mut octree, 200, 200,color );*/
|
||||||
|
|
||||||
/*octree.insert(0.0,0.0,0.0, Voxel::new(Color::from(RED)));*/
|
|
||||||
|
/*let postion = octree.normalize_to_voxel_at_depth(Vec3::ZERO, 10);
|
||||||
|
|
||||||
|
octree.insert(postion, Voxel::new(Color::from(RED)));
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
commands.spawn(
|
commands.spawn(
|
||||||
@ -138,13 +135,13 @@ fn generate_voxel_sphere(
|
|||||||
let wx = x as f32 * step;
|
let wx = x as f32 * step;
|
||||||
let wy = y as f32 * step;
|
let wy = y as f32 * step;
|
||||||
let wz = z as f32 * step;
|
let wz = z as f32 * step;
|
||||||
|
let position = Vec3::new(wx, wy, wz);
|
||||||
|
|
||||||
// Insert the voxel
|
// Insert the voxel
|
||||||
let voxel = Voxel {
|
let voxel = Voxel {
|
||||||
color: voxel_color,
|
color: voxel_color,
|
||||||
position: Default::default(), // Will get set internally by `insert()`
|
|
||||||
};
|
};
|
||||||
octree.insert(wx, wy, wz, voxel);
|
octree.insert(position, voxel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,14 +177,13 @@ fn generate_voxel_rect(
|
|||||||
let wy = y * step;
|
let wy = y * step;
|
||||||
let wz = z * step;
|
let wz = z * step;
|
||||||
|
|
||||||
// Create the voxel
|
let position = Vec3::new(wx, wy, wz);
|
||||||
|
|
||||||
|
// Insert the voxel
|
||||||
let voxel = Voxel {
|
let voxel = Voxel {
|
||||||
color: voxel_color,
|
color: voxel_color,
|
||||||
position: Default::default(), // Will be set by octree internally
|
|
||||||
};
|
};
|
||||||
|
octree.insert(position, voxel);
|
||||||
// Insert the voxel into the octree
|
|
||||||
octree.insert(wx, wy, wz, voxel);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,14 +212,13 @@ fn generate_large_plane(
|
|||||||
let wy = y * step;
|
let wy = y * step;
|
||||||
let wz = z * step;
|
let wz = z * step;
|
||||||
|
|
||||||
// Create the voxel
|
let position = Vec3::new(wx, wy, wz);
|
||||||
|
|
||||||
|
// Insert the voxel
|
||||||
let voxel = Voxel {
|
let voxel = Voxel {
|
||||||
color,
|
color,
|
||||||
position: Vec3::ZERO, // Will be set internally by octree.insert()
|
|
||||||
};
|
};
|
||||||
|
octree.insert(position, voxel);
|
||||||
// Insert the voxel into the octree
|
|
||||||
octree.insert(wx, wy, wz, voxel);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,82 +6,109 @@ use bevy::prelude::*;
|
|||||||
use bevy::render::mesh::{Indices, PrimitiveTopology};
|
use bevy::render::mesh::{Indices, PrimitiveTopology};
|
||||||
use bevy::render::render_asset::RenderAssetUsages;
|
use bevy::render::render_asset::RenderAssetUsages;
|
||||||
use bevy_egui::egui::emath::Numeric;
|
use bevy_egui::egui::emath::Numeric;
|
||||||
use crate::systems::voxels::structure::{ OctreeNode, SparseVoxelOctree};
|
use crate::systems::camera_system::Selector;
|
||||||
|
use crate::systems::voxels::structure::{OctreeNode, SparseVoxelOctree};
|
||||||
|
|
||||||
pub fn visualize_octree(
|
/// Visualize each node of the octree as a scaled cuboid, **center-based**.
|
||||||
|
/// `octree_tf.translation` is the world-space center of the root bounding box.
|
||||||
|
pub fn visualize_octree_system(
|
||||||
mut gizmos: Gizmos,
|
mut gizmos: Gizmos,
|
||||||
camera_query: Query<&Transform, With<Camera>>,
|
|
||||||
octree_query: Query<(&SparseVoxelOctree, &Transform)>,
|
octree_query: Query<(&SparseVoxelOctree, &Transform)>,
|
||||||
) {
|
) {
|
||||||
let camera_tf = camera_query.single(); // your "real" camera position in double precision
|
|
||||||
let camera_pos = camera_tf.translation; // DVec3
|
|
||||||
|
|
||||||
for (octree, octree_tf) in octree_query.iter() {
|
for (octree, octree_tf) in octree_query.iter() {
|
||||||
visualize_recursive(
|
// The root node covers [-size/2..+size/2], so half_size is:
|
||||||
|
let half_size = octree.size * 0.5;
|
||||||
|
|
||||||
|
// Draw a translucent cuboid for the root
|
||||||
|
gizmos.cuboid(
|
||||||
|
Transform::from_translation(octree_tf.translation)
|
||||||
|
.with_scale(Vec3::splat(octree.size)),
|
||||||
|
Color::rgba(1.0, 1.0, 0.0, 0.15),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Recursively draw children:
|
||||||
|
// Start from depth=0. The node at depth=0 has bounding side = octree.size.
|
||||||
|
visualize_recursive_center(
|
||||||
&mut gizmos,
|
&mut gizmos,
|
||||||
&octree.root,
|
&octree.root,
|
||||||
octree_tf.translation, // octree’s root center
|
octree_tf.translation, // center of root in world
|
||||||
octree.size,
|
octree.size,
|
||||||
|
0,
|
||||||
octree.max_depth,
|
octree.max_depth,
|
||||||
camera_pos,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visualize_recursive(
|
/// Recursively draws cuboids for each node.
|
||||||
|
/// We follow the same indexing as insert_recursive, i.e. bit patterns:
|
||||||
|
/// i=0 => child in (-x,-y,-z) quadrant,
|
||||||
|
/// i=1 => (+x,-y,-z), i=2 => (-x,+y,-z), etc.
|
||||||
|
fn visualize_recursive_center(
|
||||||
gizmos: &mut Gizmos,
|
gizmos: &mut Gizmos,
|
||||||
node: &OctreeNode,
|
node: &OctreeNode,
|
||||||
node_center: Vec3,
|
parent_center: Vec3,
|
||||||
node_size: f32,
|
parent_size: f32,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
camera_pos: Vec3,
|
max_depth: u32,
|
||||||
) {
|
) {
|
||||||
if depth == 0 {
|
if depth >= max_depth {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you want to draw the bounding box of this node:
|
|
||||||
/*let half = node_size as f32 * 0.5;*/
|
|
||||||
// Convert double center -> local f32 position
|
|
||||||
let center_f32 = (node_center - camera_pos);
|
|
||||||
|
|
||||||
// A quick approach: draw a wireframe cube by drawing lines for each edge
|
|
||||||
// Or use "cuboid gizmo" methods in future bevy versions that might exist.
|
|
||||||
/*draw_wire_cube(gizmos, center_f32, half, Color::YELLOW);*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
gizmos.cuboid(
|
|
||||||
Transform::from_translation(center_f32).with_scale(Vec3::splat(node_size)),
|
|
||||||
BLACK,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Recurse children
|
|
||||||
if let Some(children) = &node.children {
|
if let Some(children) = &node.children {
|
||||||
let child_size = node_size / 2.0;
|
// Each child is half the parent’s size
|
||||||
|
let child_size = parent_size * 0.5;
|
||||||
|
let half = child_size * 0.5;
|
||||||
|
|
||||||
for (i, child) in children.iter().enumerate() {
|
for (i, child) in children.iter().enumerate() {
|
||||||
let offset_x = if (i & 1) == 1 { child_size / 2.0 } else { -child_size / 2.0 };
|
// For i in [0..8], bits: x=1, y=2, z=4
|
||||||
let offset_y = if (i & 2) == 2 { child_size / 2.0 } else { -child_size / 2.0 };
|
let offset_x = if (i & 1) != 0 { half } else { -half };
|
||||||
let offset_z = if (i & 4) == 4 { child_size / 2.0 } else { -child_size / 2.0 };
|
let offset_y = if (i & 2) != 0 { half } else { -half };
|
||||||
|
let offset_z = if (i & 4) != 0 { half } else { -half };
|
||||||
|
|
||||||
let child_center = Vec3::new(
|
let child_center = parent_center + Vec3::new(offset_x, offset_y, offset_z);
|
||||||
node_center.x + offset_x,
|
|
||||||
node_center.y + offset_y,
|
// Draw the child bounding box
|
||||||
node_center.z + offset_z,
|
gizmos.cuboid(
|
||||||
|
Transform::from_translation(child_center).with_scale(Vec3::splat(child_size)),
|
||||||
|
Color::rgba(0.5, 1.0, 0.5, 0.15), // greenish
|
||||||
);
|
);
|
||||||
|
|
||||||
visualize_recursive(
|
// Recurse
|
||||||
|
visualize_recursive_center(
|
||||||
gizmos,
|
gizmos,
|
||||||
child,
|
child,
|
||||||
child_center,
|
child_center,
|
||||||
child_size,
|
child_size,
|
||||||
depth - 1,
|
depth + 1,
|
||||||
camera_pos,
|
max_depth,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// If node.is_leaf && node.voxel.is_some(), draw a smaller marker
|
||||||
|
if node.is_leaf {
|
||||||
|
if let Some(voxel) = node.voxel {
|
||||||
|
// We'll choose a size that's a fraction of the parent's size.
|
||||||
|
// For example, 25% of the parent bounding box dimension.
|
||||||
|
let leaf_size = parent_size * 0.25;
|
||||||
|
|
||||||
|
// Draw a small cuboid at the same center as the parent node.
|
||||||
|
gizmos.cuboid(
|
||||||
|
Transform::from_translation(parent_center)
|
||||||
|
.with_scale(Vec3::splat(leaf_size)),
|
||||||
|
voxel.color,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
fn selction_visualizer(mut gizmos: Gizmos,
|
||||||
|
camera_query: Query<&Selector, With<Camera>>,
|
||||||
|
octree_query: Query<(&SparseVoxelOctree, &Transform)>,){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -91,66 +118,46 @@ pub fn draw_grid(
|
|||||||
camera_query: Query<&Transform, With<Camera>>,
|
camera_query: Query<&Transform, With<Camera>>,
|
||||||
octree_query: Query<(&SparseVoxelOctree, &Transform)>,
|
octree_query: Query<(&SparseVoxelOctree, &Transform)>,
|
||||||
) {
|
) {
|
||||||
// 1) Get the camera’s double transform for offset
|
|
||||||
let camera_tf = camera_query.single();
|
let camera_tf = camera_query.single();
|
||||||
let camera_pos = camera_tf.translation; // DVec3
|
let camera_pos = camera_tf.translation;
|
||||||
|
|
||||||
for (octree, octree_dtf) in octree_query.iter() {
|
for (octree, octree_tf) in octree_query.iter() {
|
||||||
|
|
||||||
|
|
||||||
// 2) Octree’s double position
|
|
||||||
let octree_pos = octree_dtf.translation; // e.g. [100_000, 0, 0] in double space
|
|
||||||
|
|
||||||
// 3) Compute spacing in f32
|
|
||||||
let grid_spacing = octree.get_spacing_at_depth(octree.max_depth) as f32;
|
|
||||||
let grid_size = (octree.size / grid_spacing) as i32;
|
|
||||||
|
|
||||||
// 4) Start position in local "octree space"
|
|
||||||
// We'll define the bounding region from [-size/2, +size/2]
|
|
||||||
let half_size = octree.size * 0.5;
|
let half_size = octree.size * 0.5;
|
||||||
let start_position = -half_size; // f32
|
let root_center = octree_tf.translation;
|
||||||
|
|
||||||
// 5) Loop over lines
|
// Voxel spacing at max depth
|
||||||
for i in 0..=grid_size {
|
let spacing = octree.get_spacing_at_depth(octree.max_depth);
|
||||||
// i-th line offset
|
let grid_count = (octree.size / spacing) as i32;
|
||||||
let offset = i as f32 * grid_spacing;
|
|
||||||
|
|
||||||
// a) Lines along Z
|
// We'll define the bounding region as [center-half_size .. center+half_size].
|
||||||
// from (start_position + offset, 0, start_position)
|
// So the min corner is (root_center - half_size).
|
||||||
// to (start_position + offset, 0, start_position + grid_size * spacing)
|
let min_corner = root_center - Vec3::splat(half_size);
|
||||||
{
|
|
||||||
let x = start_position + offset;
|
|
||||||
let z1 = start_position;
|
|
||||||
let z2 = start_position + (grid_size as f32 * grid_spacing);
|
|
||||||
|
|
||||||
// Convert these points to "world double" by adding octree_pos
|
// Draw lines in X & Z directions (like a ground plane).
|
||||||
let p1_d = Vec3::new(x, 0.0, z1) + octree_pos;
|
for i in 0..=grid_count {
|
||||||
let p2_d = Vec3::new(x, 0.0, z2) + octree_pos;
|
let offset = i as f32 * spacing;
|
||||||
|
|
||||||
// Then offset by camera_pos, convert to f32
|
// 1) line along Z
|
||||||
let p1_f32 = (p1_d - camera_pos);
|
let x = min_corner.x + offset;
|
||||||
let p2_f32 = (p2_d - camera_pos);
|
let z1 = min_corner.z;
|
||||||
|
let z2 = min_corner.z + (grid_count as f32 * spacing);
|
||||||
|
|
||||||
// Draw the line
|
let p1 = Vec3::new(x, min_corner.y, z1);
|
||||||
gizmos.line(p1_f32, p2_f32, Color::WHITE);
|
let p2 = Vec3::new(x, min_corner.y, z2);
|
||||||
}
|
|
||||||
|
|
||||||
// b) Lines along X
|
// offset by -camera_pos for stable Gizmos in large coords
|
||||||
// from (start_position, 0, start_position + offset)
|
let p1_f32 = p1 - camera_pos;
|
||||||
// to (start_position + grid_size * spacing, 0, start_position + offset)
|
let p2_f32 = p2 - camera_pos;
|
||||||
{
|
gizmos.line(p1_f32, p2_f32, Color::WHITE);
|
||||||
let z = start_position + offset;
|
|
||||||
let x1 = start_position;
|
|
||||||
let x2 = start_position + (grid_size as f32 * grid_spacing);
|
|
||||||
|
|
||||||
let p1_d = Vec3::new(x1, 0.0, z) + octree_pos;
|
// 2) line along X
|
||||||
let p2_d = Vec3::new(x2, 0.0, z) + octree_pos;
|
let z = min_corner.z + offset;
|
||||||
|
let x1 = min_corner.x;
|
||||||
|
let x2 = min_corner.x + (grid_count as f32 * spacing);
|
||||||
|
|
||||||
let p1_f32 = (p1_d - camera_pos);
|
let p3 = Vec3::new(x1, min_corner.y, z) - camera_pos;
|
||||||
let p2_f32 = (p2_d - camera_pos);
|
let p4 = Vec3::new(x2, min_corner.y, z) - camera_pos;
|
||||||
|
gizmos.line(p3, p4, Color::WHITE);
|
||||||
gizmos.line(p1_f32, p2_f32, Color::WHITE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,32 +21,32 @@ impl SparseVoxelOctree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Returns the size of one voxel at the given depth.
|
||||||
pub fn get_spacing_at_depth(&self, depth: u32) -> f32 {
|
pub fn get_spacing_at_depth(&self, depth: u32) -> f32 {
|
||||||
// Ensure the depth does not exceed the maximum depth
|
let effective = depth.min(self.max_depth);
|
||||||
let effective_depth = depth.min(self.max_depth);
|
self.size / (2_u32.pow(effective)) as f32
|
||||||
|
|
||||||
// Calculate the voxel size at the specified depth
|
|
||||||
self.size / (2_u32.pow(effective_depth)) as f32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Normalize the world position to the nearest voxel grid position at the specified depth.
|
|
||||||
pub fn normalize_to_voxel_at_depth(
|
|
||||||
&self,
|
|
||||||
world_x: f32,
|
|
||||||
world_y: f32,
|
|
||||||
world_z: f32,
|
|
||||||
depth: u32,
|
|
||||||
) -> (f32, f32, f32) {
|
|
||||||
// Calculate the voxel size at the specified depth
|
|
||||||
let voxel_size = self.get_spacing_at_depth(depth) as f32;
|
|
||||||
|
|
||||||
// Align the world position to the center of the voxel
|
/// Center-based: [-size/2..+size/2]. Shift +half_size => [0..size], floor, shift back.
|
||||||
let aligned_x = (world_x / voxel_size).floor() * voxel_size + voxel_size / 2.0;
|
pub fn normalize_to_voxel_at_depth(&self, position: Vec3, depth: u32) -> Vec3 {
|
||||||
let aligned_y = (world_y / voxel_size).floor() * voxel_size + voxel_size / 2.0;
|
// Convert world coordinate to normalized [0,1] space.
|
||||||
let aligned_z = (world_z / voxel_size).floor() * voxel_size + voxel_size / 2.0;
|
let half_size = self.size * 0.5;
|
||||||
|
// Shift to [0, self.size]
|
||||||
(aligned_x, aligned_y, aligned_z)
|
let shifted = (position + Vec3::splat(half_size)) / self.size;
|
||||||
|
// Determine the number of voxels along an edge at the given depth.
|
||||||
|
let voxel_count = 2_u32.pow(depth) as f32;
|
||||||
|
// Get the voxel index (as a float) and then compute the center in normalized space.
|
||||||
|
let voxel_index = (shifted * voxel_count).floor();
|
||||||
|
let voxel_center = (voxel_index + Vec3::splat(0.5)) / voxel_count;
|
||||||
|
voxel_center
|
||||||
}
|
}
|
||||||
|
pub fn denormalize_voxel_center(&self, voxel_center: Vec3) -> Vec3 {
|
||||||
|
let half_size = self.size * 0.5;
|
||||||
|
// Convert the normalized voxel center back to world space.
|
||||||
|
voxel_center * self.size - Vec3::splat(half_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn compute_child_bounds(&self, bounds: &AABB, index: usize) -> AABB {
|
pub fn compute_child_bounds(&self, bounds: &AABB, index: usize) -> AABB {
|
||||||
let min = bounds.min;
|
let min = bounds.min;
|
||||||
@ -75,7 +75,13 @@ impl SparseVoxelOctree {
|
|||||||
ray: &Ray,
|
ray: &Ray,
|
||||||
aabb: &AABB,
|
aabb: &AABB,
|
||||||
) -> Option<(f32, f32, Vec3)> {
|
) -> Option<(f32, f32, Vec3)> {
|
||||||
let inv_dir = 1.0 / ray.direction;
|
// Define a safe inverse function to avoid division by zero.
|
||||||
|
let safe_inv = |d: f32| if d.abs() < 1e-6 { 1e6 } else { 1.0 / d };
|
||||||
|
let inv_dir = Vec3::new(
|
||||||
|
safe_inv(ray.direction.x),
|
||||||
|
safe_inv(ray.direction.y),
|
||||||
|
safe_inv(ray.direction.z),
|
||||||
|
);
|
||||||
|
|
||||||
let t1 = (aabb.min - ray.origin) * inv_dir;
|
let t1 = (aabb.min - ray.origin) * inv_dir;
|
||||||
let t2 = (aabb.max - ray.origin) * inv_dir;
|
let t2 = (aabb.max - ray.origin) * inv_dir;
|
||||||
@ -87,10 +93,9 @@ impl SparseVoxelOctree {
|
|||||||
let t_exit = tmax.min_element();
|
let t_exit = tmax.min_element();
|
||||||
|
|
||||||
if t_enter <= t_exit && t_exit >= 0.0 {
|
if t_enter <= t_exit && t_exit >= 0.0 {
|
||||||
// Calculate normal based on which component contributed to t_enter
|
|
||||||
let epsilon = 1e-6;
|
let epsilon = 1e-6;
|
||||||
let mut normal = Vec3::ZERO;
|
let mut normal = Vec3::ZERO;
|
||||||
|
// Determine which face was hit by comparing t_enter to the computed values.
|
||||||
if (t_enter - t1.x).abs() < epsilon || (t_enter - t2.x).abs() < epsilon {
|
if (t_enter - t1.x).abs() < epsilon || (t_enter - t2.x).abs() < epsilon {
|
||||||
normal = Vec3::new(if ray.direction.x < 0.0 { 1.0 } else { -1.0 }, 0.0, 0.0);
|
normal = Vec3::new(if ray.direction.x < 0.0 { 1.0 } else { -1.0 }, 0.0, 0.0);
|
||||||
} else if (t_enter - t1.y).abs() < epsilon || (t_enter - t2.y).abs() < epsilon {
|
} else if (t_enter - t1.y).abs() < epsilon || (t_enter - t2.y).abs() < epsilon {
|
||||||
@ -98,53 +103,39 @@ impl SparseVoxelOctree {
|
|||||||
} else if (t_enter - t1.z).abs() < epsilon || (t_enter - t2.z).abs() < epsilon {
|
} else if (t_enter - t1.z).abs() < epsilon || (t_enter - t2.z).abs() < epsilon {
|
||||||
normal = Vec3::new(0.0, 0.0, if ray.direction.z < 0.0 { 1.0 } else { -1.0 });
|
normal = Vec3::new(0.0, 0.0, if ray.direction.z < 0.0 { 1.0 } else { -1.0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((t_enter, t_exit, normal))
|
Some((t_enter, t_exit, normal))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a position is within the current octree bounds.
|
/// Checks if (x,y,z) is within [-size/2..+size/2].
|
||||||
pub fn contains(&self, x: f32, y: f32, z: f32) -> bool {
|
pub fn contains(&self, x: f32, y: f32, z: f32) -> bool {
|
||||||
let half_size = self.size / 2.0;
|
let half_size = self.size / 2.0;
|
||||||
let epsilon = 1e-6; // Epsilon for floating-point precision
|
let eps = 1e-6;
|
||||||
|
(x >= -half_size - eps && x < half_size + eps)
|
||||||
(x >= -half_size - epsilon && x < half_size + epsilon) &&
|
&& (y >= -half_size - eps && y < half_size + eps)
|
||||||
(y >= -half_size - epsilon && y < half_size + epsilon) &&
|
&& (z >= -half_size - eps && z < half_size + eps)
|
||||||
(z >= -half_size - epsilon && z < half_size + epsilon)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_voxel_at_world_coords(&self, world_x: f32, world_y: f32, world_z: f32) -> Option<&Voxel> {
|
/// Retrieve a voxel at world coordinates by normalizing and looking up.
|
||||||
// Correct normalization: calculate the position relative to the octree's center
|
pub fn get_voxel_at_world_coords(&self, position: Vec3) -> Option<&Voxel> {
|
||||||
let normalized_x = (world_x + (self.size / 2.0)) / self.size;
|
let aligned = self.normalize_to_voxel_at_depth(position, self.max_depth);
|
||||||
let normalized_y = (world_y + (self.size / 2.0)) / self.size;
|
self.get_voxel_at(aligned.x, aligned.y, aligned.z)
|
||||||
let normalized_z = (world_z + (self.size / 2.0)) / self.size;
|
|
||||||
|
|
||||||
self.get_voxel_at(normalized_x, normalized_y, normalized_z)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn local_to_world(&self, local_pos: Vec3) -> Vec3 {
|
||||||
pub fn has_volume(&self, node: &OctreeNode) -> bool {
|
// Half the total octree size, used to shift the center to the origin.
|
||||||
// Check if this node is a leaf with a voxel
|
let half_size = self.size * 0.5;
|
||||||
if node.is_leaf && node.voxel.is_some() {
|
// Convert local coordinate to world space:
|
||||||
return true;
|
// 1. Subtract 0.5 to center the coordinate at zero (range becomes [-0.5, 0.5])
|
||||||
}
|
// 2. Multiply by the total size to scale into world units.
|
||||||
|
// 3. Add half_size to shift from a center–based system to one starting at zero.
|
||||||
// If the node has children, recursively check them
|
(local_pos - Vec3::splat(0.5)) * self.size + Vec3::splat(half_size)
|
||||||
if let Some(children) = &node.children {
|
|
||||||
for child in children.iter() {
|
|
||||||
if self.has_volume(child) {
|
|
||||||
return true; // If any child has a voxel, the chunk has volume
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no voxel found in this node or its children
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Helper function to recursively traverse the octree to a specific depth.
|
/// Helper function to recursively traverse the octree to a specific depth.
|
||||||
fn get_node_at_depth(
|
fn get_node_at_depth(
|
||||||
node: &OctreeNode,
|
node: &OctreeNode,
|
||||||
@ -185,6 +176,26 @@ impl SparseVoxelOctree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_volume(&self, node: &OctreeNode) -> bool {
|
||||||
|
// Check if this node is a leaf with a voxel
|
||||||
|
if node.is_leaf && node.voxel.is_some() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the node has children, recursively check them
|
||||||
|
if let Some(children) = &node.children {
|
||||||
|
for child in children.iter() {
|
||||||
|
if self.has_volume(child) {
|
||||||
|
return true; // If any child has a voxel, the chunk has volume
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no voxel found in this node or its children
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use bevy::math::{DQuat, DVec3};
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::render::mesh::{Indices, PrimitiveTopology, VertexAttributeValues};
|
use bevy::render::mesh::{Indices, PrimitiveTopology, VertexAttributeValues};
|
||||||
use bevy::render::render_asset::RenderAssetUsages;
|
use bevy::render::render_asset::RenderAssetUsages;
|
||||||
use crate::systems::voxels::structure::{OctreeNode, Ray, SparseVoxelOctree, Voxel, AABB, NEIGHBOR_OFFSETS};
|
use crate::systems::voxels::structure::{DirtyVoxel, OctreeNode, Ray, SparseVoxelOctree, Voxel, AABB, NEIGHBOR_OFFSETS};
|
||||||
|
|
||||||
impl SparseVoxelOctree {
|
impl SparseVoxelOctree {
|
||||||
/// Creates a new octree with the specified max depth, size, and wireframe visibility.
|
/// Creates a new octree with the specified max depth, size, and wireframe visibility.
|
||||||
@ -17,52 +17,50 @@ impl SparseVoxelOctree {
|
|||||||
show_wireframe,
|
show_wireframe,
|
||||||
show_world_grid,
|
show_world_grid,
|
||||||
show_chunks,
|
show_chunks,
|
||||||
dirty: true,
|
dirty: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn insert(&mut self, position: Vec3, voxel: Voxel) {
|
||||||
|
// Align to the center of the voxel at max_depth
|
||||||
|
let mut aligned = self.normalize_to_voxel_at_depth(position, self.max_depth);
|
||||||
|
let mut world_center = self.denormalize_voxel_center(aligned);
|
||||||
|
|
||||||
pub fn insert(&mut self, world_x: f32, world_y: f32, world_z: f32, voxel: Voxel) {
|
// Expand as needed using the denormalized position.
|
||||||
// Normalize the world coordinates to the nearest voxel grid position
|
while !self.contains(world_center.x, world_center.y, world_center.z) {
|
||||||
let (aligned_x, aligned_y, aligned_z) = self.normalize_to_voxel_at_depth(world_x, world_y, world_z, self.max_depth);
|
self.expand_root(world_center.x, world_center.y, world_center.z);
|
||||||
|
// Recompute aligned and world_center after expansion.
|
||||||
// Iteratively expand the root to include the voxel position
|
aligned = self.normalize_to_voxel_at_depth(position, self.max_depth);
|
||||||
while !self.contains(aligned_x, aligned_y, aligned_z) {
|
world_center = self.denormalize_voxel_center(aligned);
|
||||||
self.expand_root(aligned_x, aligned_y, aligned_z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correct normalization: calculate the position relative to the octree's center
|
let dirty_voxel = DirtyVoxel{
|
||||||
let normalized_x = (aligned_x + (self.size / 2.0)) / self.size;
|
position: aligned,
|
||||||
let normalized_y = (aligned_y + (self.size / 2.0)) / self.size;
|
};
|
||||||
let normalized_z = (aligned_z + (self.size / 2.0)) / self.size;
|
self.dirty.push(dirty_voxel);
|
||||||
|
|
||||||
// Insert the voxel with its world position
|
|
||||||
let mut voxel_with_position = voxel;
|
|
||||||
voxel_with_position.position = Vec3::new(world_x as f32, world_y as f32, world_z as f32);
|
|
||||||
|
|
||||||
|
|
||||||
self.dirty = true;
|
Self::insert_recursive(&mut self.root, aligned, voxel, self.max_depth);
|
||||||
|
|
||||||
|
|
||||||
SparseVoxelOctree::insert_recursive(&mut self.root, normalized_x, normalized_y, normalized_z, voxel_with_position, self.max_depth);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_recursive(node: &mut OctreeNode, x: f32, y: f32, z: f32, voxel: Voxel, depth: u32) {
|
fn insert_recursive(node: &mut OctreeNode, position: Vec3, voxel: Voxel, depth: u32) {
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
node.voxel = Some(voxel);
|
node.voxel = Some(voxel);
|
||||||
node.is_leaf = true;
|
node.is_leaf = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let epsilon = 1e-6;
|
||||||
|
// Determine octant index by comparing with 0.5
|
||||||
|
let index = ((position.x >= 0.5 - epsilon) as usize)
|
||||||
|
+ ((position.y >= 0.5 - epsilon) as usize * 2)
|
||||||
|
+ ((position.z >= 0.5 - epsilon) as usize * 4);
|
||||||
|
|
||||||
let epsilon = 1e-6; // Epsilon for floating-point precision
|
// If there are no children, create them.
|
||||||
|
|
||||||
let index = ((x >= 0.5 - epsilon) as usize) + ((y >= 0.5 - epsilon) as usize * 2) + ((z >= 0.5 - epsilon) as usize * 4);
|
|
||||||
|
|
||||||
if node.children.is_none() {
|
if node.children.is_none() {
|
||||||
node.children = Some(Box::new(core::array::from_fn(|_| OctreeNode::new())));
|
node.children = Some(Box::new(core::array::from_fn(|_| OctreeNode::new())));
|
||||||
node.is_leaf = false;
|
node.is_leaf = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref mut children) = node.children {
|
if let Some(ref mut children) = node.children {
|
||||||
|
// Adjust coordinate into the child’s [0, 1] range.
|
||||||
let adjust_coord = |coord: f32| {
|
let adjust_coord = |coord: f32| {
|
||||||
if coord >= 0.5 - epsilon {
|
if coord >= 0.5 - epsilon {
|
||||||
(coord - 0.5) * 2.0
|
(coord - 0.5) * 2.0
|
||||||
@ -70,47 +68,47 @@ impl SparseVoxelOctree {
|
|||||||
coord * 2.0
|
coord * 2.0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
SparseVoxelOctree::insert_recursive(&mut children[index], adjust_coord(x), adjust_coord(y), adjust_coord(z), voxel, depth - 1);
|
let child_pos = Vec3::new(
|
||||||
|
adjust_coord(position.x),
|
||||||
|
adjust_coord(position.y),
|
||||||
|
adjust_coord(position.z),
|
||||||
|
);
|
||||||
|
Self::insert_recursive(&mut children[index], child_pos, voxel, depth - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&mut self, world_x: f32, world_y: f32, world_z: f32) {
|
pub fn remove(&mut self, position: Vec3) {
|
||||||
// Normalize the world coordinates to the nearest voxel grid position
|
let aligned = self.normalize_to_voxel_at_depth(position, self.max_depth);
|
||||||
let (aligned_x, aligned_y, aligned_z) =
|
|
||||||
self.normalize_to_voxel_at_depth(world_x, world_y, world_z, self.max_depth);
|
|
||||||
|
|
||||||
// Correct normalization: calculate the position relative to the octree's center
|
let dirty_voxel = DirtyVoxel{
|
||||||
let normalized_x = (aligned_x + (self.size / 2.0)) / self.size;
|
position: aligned,
|
||||||
let normalized_y = (aligned_y + (self.size / 2.0)) / self.size;
|
};
|
||||||
let normalized_z = (aligned_z + (self.size / 2.0)) / self.size;
|
self.dirty.push(dirty_voxel);
|
||||||
|
|
||||||
self.dirty = true;
|
Self::remove_recursive(&mut self.root, aligned.x, aligned.y, aligned.z, self.max_depth);
|
||||||
|
|
||||||
|
|
||||||
// Call the recursive remove function
|
|
||||||
Self::remove_recursive(&mut self.root, normalized_x, normalized_y, normalized_z, self.max_depth);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_recursive(node: &mut OctreeNode, x: f32, y: f32, z: f32, depth: u32) -> bool {
|
fn remove_recursive(
|
||||||
|
node: &mut OctreeNode,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
z: f32,
|
||||||
|
depth: u32,
|
||||||
|
) -> bool {
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
// This is the leaf node where the voxel should be
|
|
||||||
if node.voxel.is_some() {
|
if node.voxel.is_some() {
|
||||||
node.voxel = None;
|
node.voxel = None;
|
||||||
node.is_leaf = false;
|
node.is_leaf = false;
|
||||||
// Since we've removed the voxel and there are no children, this node can be pruned
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// There was no voxel here
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.children.is_none() {
|
if node.children.is_none() {
|
||||||
// No children to traverse, voxel not found
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
let epsilon = 1e-6;
|
||||||
let epsilon = 1e-6; // Epsilon for floating-point precision
|
|
||||||
let index = ((x >= 0.5 - epsilon) as usize)
|
let index = ((x >= 0.5 - epsilon) as usize)
|
||||||
+ ((y >= 0.5 - epsilon) as usize * 2)
|
+ ((y >= 0.5 - epsilon) as usize * 2)
|
||||||
+ ((z >= 0.5 - epsilon) as usize * 4);
|
+ ((z >= 0.5 - epsilon) as usize * 4);
|
||||||
@ -124,7 +122,6 @@ impl SparseVoxelOctree {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let child = &mut node.children.as_mut().unwrap()[index];
|
let child = &mut node.children.as_mut().unwrap()[index];
|
||||||
|
|
||||||
let should_prune_child = Self::remove_recursive(
|
let should_prune_child = Self::remove_recursive(
|
||||||
child,
|
child,
|
||||||
adjust_coord(x),
|
adjust_coord(x),
|
||||||
@ -134,128 +131,142 @@ impl SparseVoxelOctree {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if should_prune_child {
|
if should_prune_child {
|
||||||
// Remove the child node
|
// remove the child node
|
||||||
node.children.as_mut().unwrap()[index] = OctreeNode::new();
|
node.children.as_mut().unwrap()[index] = OctreeNode::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
// After removing the child, check if all children are empty
|
// Check if all children are empty
|
||||||
let all_children_empty = node.children.as_ref().unwrap().iter().all(|child| child.is_empty());
|
let all_children_empty = node
|
||||||
|
.children
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.all(|child| child.is_empty());
|
||||||
|
|
||||||
if all_children_empty {
|
if all_children_empty {
|
||||||
// Remove the children array
|
|
||||||
node.children = None;
|
node.children = None;
|
||||||
node.is_leaf = true; // Now this node becomes a leaf
|
node.is_leaf = true;
|
||||||
// If this node has no voxel and no children, it can be pruned
|
|
||||||
return node.voxel.is_none();
|
return node.voxel.is_none();
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn expand_root(&mut self, x: f32, y: f32, z: f32) {
|
fn expand_root(&mut self, _x: f32, _y: f32, _z: f32) {
|
||||||
let new_size = self.size * 2.0;
|
info!("Root expanding ...");
|
||||||
let new_depth = self.max_depth + 1;
|
// Save the old root and its size.
|
||||||
|
let old_root = std::mem::replace(&mut self.root, OctreeNode::new());
|
||||||
|
let old_size = self.size;
|
||||||
|
|
||||||
// Create a new root node with 8 children
|
// Update the octree's size and depth.
|
||||||
let mut new_root = OctreeNode::new();
|
self.size *= 2.0;
|
||||||
new_root.children = Some(Box::new(core::array::from_fn(|_| OctreeNode::new())));
|
self.max_depth += 1;
|
||||||
|
|
||||||
// The old root had 8 children; move each child to the correct new position
|
// Reinsert each voxel from the old tree.
|
||||||
if let Some(old_children) = self.root.children.take() {
|
let voxels = Self::collect_voxels_from_node(&old_root, old_size);
|
||||||
for (i, old_child) in old_children.iter().enumerate() {
|
for (world_pos, voxel, _depth) in voxels {
|
||||||
// Determine which child of the new root the old child belongs in
|
self.insert(world_pos, voxel);
|
||||||
let offset_x = if (i & 1) == 1 { 1 } else { 0 };
|
|
||||||
let offset_y = if (i & 2) == 2 { 1 } else { 0 };
|
|
||||||
let offset_z = if (i & 4) == 4 { 1 } else { 0 };
|
|
||||||
|
|
||||||
let new_index = offset_x + (offset_y * 2) + (offset_z * 4);
|
|
||||||
|
|
||||||
// Now, move the old child into the correct new child of the new root
|
|
||||||
let new_child = &mut new_root.children.as_mut().unwrap()[new_index];
|
|
||||||
|
|
||||||
// Create new children for the new child if necessary
|
|
||||||
if new_child.children.is_none() {
|
|
||||||
new_child.children = Some(Box::new(core::array::from_fn(|_| OctreeNode::new())));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Place the old child in the correct "facing" position in the new child
|
|
||||||
let facing_x = if offset_x == 1 { 0 } else { 1 };
|
|
||||||
let facing_y = if offset_y == 1 { 0 } else { 1 };
|
|
||||||
let facing_z = if offset_z == 1 { 0 } else { 1 };
|
|
||||||
|
|
||||||
let facing_index = facing_x + (facing_y * 2) + (facing_z * 4);
|
|
||||||
new_child.children.as_mut().unwrap()[facing_index] = old_child.clone();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.root = new_root;
|
|
||||||
self.size = new_size;
|
|
||||||
self.max_depth = new_depth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper: Collect all voxels from a given octree node recursively.
|
||||||
/// Traverse the octree and collect voxel data.
|
/// The coordinate system here assumes the node covers [–old_size/2, +old_size/2] in each axis.
|
||||||
pub fn traverse(&self) -> Vec<(f32, f32, f32, Color, u32)> {
|
fn collect_voxels_from_node(node: &OctreeNode, old_size: f32) -> Vec<(Vec3, Voxel, u32)> {
|
||||||
let mut voxels = Vec::new();
|
let mut voxels = Vec::new();
|
||||||
Self::traverse_recursive(&self.root, 0.0, 0.0, 0.0, 1.0, 0, &mut voxels);
|
Self::collect_voxels_recursive(node, -old_size / 2.0, -old_size / 2.0, -old_size / 2.0, old_size, 0, &mut voxels);
|
||||||
voxels
|
voxels
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traverse_recursive(
|
fn collect_voxels_recursive(
|
||||||
node: &OctreeNode,
|
node: &OctreeNode,
|
||||||
x: f32,
|
x: f32,
|
||||||
y: f32,
|
y: f32,
|
||||||
z: f32,
|
z: f32,
|
||||||
size: f32,
|
size: f32,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
voxels: &mut Vec<(f32, f32, f32, Color, u32)>,
|
out: &mut Vec<(Vec3, Voxel, u32)>,
|
||||||
) {
|
) {
|
||||||
if node.is_leaf/* && !node.is_constant*/ {
|
if node.is_leaf {
|
||||||
if let Some(voxel) = node.voxel {
|
if let Some(voxel) = node.voxel {
|
||||||
voxels.push((x, y, z, voxel.color, depth));
|
// Compute the center of this node's region.
|
||||||
|
let center = Vec3::new(x + size / 2.0, y + size / 2.0, z + size / 2.0);
|
||||||
|
out.push((center, voxel, depth));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(children) = &node.children {
|
||||||
if let Some(ref children) = node.children {
|
let half = size / 2.0;
|
||||||
let half_size = size / 2.0;
|
|
||||||
for (i, child) in children.iter().enumerate() {
|
for (i, child) in children.iter().enumerate() {
|
||||||
let offset = |bit: usize| if (i & bit) == bit { half_size } else { 0.0 };
|
let offset_x = if (i & 1) != 0 { half } else { 0.0 };
|
||||||
Self::traverse_recursive(
|
let offset_y = if (i & 2) != 0 { half } else { 0.0 };
|
||||||
child,
|
let offset_z = if (i & 4) != 0 { half } else { 0.0 };
|
||||||
x + offset(1),
|
Self::collect_voxels_recursive(child, x + offset_x, y + offset_y, z + offset_z, half, depth + 1, out);
|
||||||
y + offset(2),
|
|
||||||
z + offset(4),
|
|
||||||
half_size,
|
|
||||||
depth + 1,
|
|
||||||
voxels,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Retrieves a reference to the voxel at the given normalized coordinates and depth, if it exists.
|
|
||||||
|
pub fn traverse(&self) -> Vec<(Vec3, Color, u32)> {
|
||||||
|
let mut voxels = Vec::new();
|
||||||
|
// Start at the normalized center (0.5, 0.5, 0.5) rather than (0,0,0)
|
||||||
|
Self::traverse_recursive(
|
||||||
|
&self.root,
|
||||||
|
Vec3::splat(0.5), // normalized center of the root cell
|
||||||
|
1.0, // full normalized cell size
|
||||||
|
0,
|
||||||
|
&mut voxels,
|
||||||
|
self,
|
||||||
|
);
|
||||||
|
voxels
|
||||||
|
}
|
||||||
|
|
||||||
|
fn traverse_recursive(
|
||||||
|
node: &OctreeNode,
|
||||||
|
local_center: Vec3,
|
||||||
|
size: f32,
|
||||||
|
depth: u32,
|
||||||
|
out: &mut Vec<(Vec3, Color, u32)>,
|
||||||
|
octree: &SparseVoxelOctree,
|
||||||
|
) {
|
||||||
|
// If a leaf contains a voxel, record its world-space center
|
||||||
|
if node.is_leaf {
|
||||||
|
if let Some(voxel) = node.voxel {
|
||||||
|
out.push((octree.denormalize_voxel_center(local_center), voxel.color, depth));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the node has children, subdivide the cell into 8 subcells.
|
||||||
|
if let Some(ref children) = node.children {
|
||||||
|
let offset = size / 4.0; // child center offset from parent center
|
||||||
|
let new_size = size / 2.0; // each child cell's size in normalized space
|
||||||
|
for (i, child) in children.iter().enumerate() {
|
||||||
|
// Compute each axis' offset: use +offset if the bit is set, else -offset.
|
||||||
|
let dx = if (i & 1) != 0 { offset } else { -offset };
|
||||||
|
let dy = if (i & 2) != 0 { offset } else { -offset };
|
||||||
|
let dz = if (i & 4) != 0 { offset } else { -offset };
|
||||||
|
let child_center = local_center + Vec3::new(dx, dy, dz);
|
||||||
|
|
||||||
|
Self::traverse_recursive(child, child_center, new_size, depth + 1, out, octree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Retrieve a voxel from the octree if it exists (x,y,z in [-0.5..+0.5] range).
|
||||||
pub fn get_voxel_at(&self, x: f32, y: f32, z: f32) -> Option<&Voxel> {
|
pub fn get_voxel_at(&self, x: f32, y: f32, z: f32) -> Option<&Voxel> {
|
||||||
Self::get_voxel_recursive(&self.root, x, y, z)
|
Self::get_voxel_recursive(&self.root, x, y, z)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_voxel_recursive(
|
fn get_voxel_recursive(node: &OctreeNode, x: f32, y: f32, z: f32) -> Option<&Voxel> {
|
||||||
node: &OctreeNode,
|
|
||||||
x: f32,
|
|
||||||
y: f32,
|
|
||||||
z: f32,
|
|
||||||
) -> Option<&Voxel> {
|
|
||||||
if node.is_leaf {
|
if node.is_leaf {
|
||||||
return node.voxel.as_ref();
|
return node.voxel.as_ref();
|
||||||
}
|
}
|
||||||
|
if let Some(children) = &node.children {
|
||||||
if let Some(ref children) = node.children {
|
let epsilon = 1e-6;
|
||||||
let epsilon = 1e-6; // Epsilon for floating-point precision
|
|
||||||
let index = ((x >= 0.5 - epsilon) as usize)
|
let index = ((x >= 0.5 - epsilon) as usize)
|
||||||
+ ((y >= 0.5 - epsilon) as usize * 2)
|
+ ((y >= 0.5 - epsilon) as usize * 2)
|
||||||
+ ((z >= 0.5 - epsilon) as usize * 4);
|
+ ((z >= 0.5 - epsilon) as usize * 4);
|
||||||
|
|
||||||
let adjust_coord = |coord: f32| {
|
let adjust_coord = |coord: f32| {
|
||||||
if coord >= 0.5 - epsilon {
|
if coord >= 0.5 - epsilon {
|
||||||
(coord - 0.5) * 2.0
|
(coord - 0.5) * 2.0
|
||||||
@ -263,7 +274,6 @@ impl SparseVoxelOctree {
|
|||||||
coord * 2.0
|
coord * 2.0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::get_voxel_recursive(
|
Self::get_voxel_recursive(
|
||||||
&children[index],
|
&children[index],
|
||||||
adjust_coord(x),
|
adjust_coord(x),
|
||||||
@ -279,34 +289,32 @@ impl SparseVoxelOctree {
|
|||||||
/// The offsets are directions (-1, 0, 1) for x, y, z.
|
/// The offsets are directions (-1, 0, 1) for x, y, z.
|
||||||
pub fn has_neighbor(
|
pub fn has_neighbor(
|
||||||
&self,
|
&self,
|
||||||
world_x: f32,
|
position: Vec3,
|
||||||
world_y: f32,
|
|
||||||
world_z: f32,
|
|
||||||
offset_x: i32,
|
offset_x: i32,
|
||||||
offset_y: i32,
|
offset_y: i32,
|
||||||
offset_z: i32,
|
offset_z: i32,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Normalize the world coordinates to the nearest voxel grid position at the specified depth
|
let aligned = self.normalize_to_voxel_at_depth(position, depth);
|
||||||
let (aligned_x, aligned_y, aligned_z) =
|
let voxel_count = 2_u32.pow(depth) as f32;
|
||||||
self.normalize_to_voxel_at_depth(world_x, world_y, world_z, depth);
|
// Normalized voxel size is 1/voxel_count
|
||||||
|
let norm_voxel_size = 1.0 / voxel_count;
|
||||||
|
|
||||||
// Calculate the voxel size at the specified depth
|
let neighbor = Vec3::new(
|
||||||
let voxel_size = self.get_spacing_at_depth(depth);
|
aligned.x + (offset_x as f32) * norm_voxel_size,
|
||||||
|
aligned.y + (offset_y as f32) * norm_voxel_size,
|
||||||
|
aligned.z + (offset_z as f32) * norm_voxel_size,
|
||||||
|
);
|
||||||
|
|
||||||
// Calculate the neighbor's world position
|
// Convert the normalized neighbor coordinate back to world space
|
||||||
let neighbor_x = aligned_x + (offset_x as f32) * voxel_size;
|
let half_size = self.size * 0.5;
|
||||||
let neighbor_y = aligned_y + (offset_y as f32) * voxel_size;
|
let neighbor_world = neighbor * self.size - Vec3::splat(half_size);
|
||||||
let neighbor_z = aligned_z + (offset_z as f32) * voxel_size;
|
|
||||||
|
|
||||||
// Check if the neighbor position is within bounds
|
if !self.contains(neighbor_world.x, neighbor_world.y, neighbor_world.z) {
|
||||||
if !self.contains(neighbor_x, neighbor_y, neighbor_z) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the voxel in the neighboring position
|
self.get_voxel_at_world_coords(neighbor_world).is_some()
|
||||||
self.get_voxel_at_world_coords(neighbor_x, neighbor_y, neighbor_z)
|
|
||||||
.is_some()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,66 +1,57 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use bevy::color::palettes::basic::BLUE;
|
use bevy::color::palettes::basic::BLUE;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use bevy::utils::info;
|
||||||
use bevy_asset::RenderAssetUsages;
|
use bevy_asset::RenderAssetUsages;
|
||||||
use bevy_render::mesh::{Indices, PrimitiveTopology, VertexAttributeValues};
|
use bevy_render::mesh::{Indices, PrimitiveTopology, VertexAttributeValues};
|
||||||
|
use bevy_render::render_resource::Face;
|
||||||
|
use log::info;
|
||||||
use crate::systems::ui_system::SpeedDisplay;
|
use crate::systems::ui_system::SpeedDisplay;
|
||||||
|
use crate::systems::voxels::octree;
|
||||||
use crate::systems::voxels::structure::{SparseVoxelOctree, NEIGHBOR_OFFSETS};
|
use crate::systems::voxels::structure::{SparseVoxelOctree, NEIGHBOR_OFFSETS};
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct VoxelTerrainMarker {}
|
||||||
|
|
||||||
|
|
||||||
pub fn render(
|
pub fn render(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut query: Query<&mut SparseVoxelOctree>,
|
mut query: Query<&mut SparseVoxelOctree>,
|
||||||
octree_transform_query: Query<&Transform, With<SparseVoxelOctree>>,
|
|
||||||
render_object_query: Query<Entity, With<VoxelTerrainMarker>>,
|
render_object_query: Query<Entity, With<VoxelTerrainMarker>>,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
camera_query: Query<&Transform, With<Camera>>,
|
|
||||||
) {
|
) {
|
||||||
// Get the camera's current position (if needed for LOD calculations)
|
|
||||||
let camera_transform = camera_query.single();
|
|
||||||
let _camera_position = camera_transform.translation;
|
|
||||||
|
|
||||||
|
|
||||||
for mut octree in query.iter_mut() {
|
for mut octree in query.iter_mut() {
|
||||||
|
// Only update when marked dirty
|
||||||
|
if !octree.dirty.is_empty() {
|
||||||
|
// Remove old render objects
|
||||||
// Handle updates to the octree only if it is marked as dirty
|
|
||||||
if octree.dirty {
|
|
||||||
// Clear existing render objects
|
|
||||||
for entity in render_object_query.iter() {
|
for entity in render_object_query.iter() {
|
||||||
commands.entity(entity).despawn();
|
commands.entity(entity).despawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect the voxels to render
|
// Get the voxel centers (world positions), color, and depth.
|
||||||
let voxels = octree.traverse();
|
let voxels = octree.traverse();
|
||||||
|
|
||||||
|
// Debug: Log the number of voxels traversed.
|
||||||
|
info!("Voxel count: {}", voxels.len());
|
||||||
|
|
||||||
let mut voxel_meshes = Vec::new();
|
let mut voxel_meshes = Vec::new();
|
||||||
|
|
||||||
for (x, y, z, _color, depth) in voxels {
|
for (world_position, _color, depth) in voxels {
|
||||||
|
// Get the size of the voxel at the current depth.
|
||||||
let voxel_size = octree.get_spacing_at_depth(depth);
|
let voxel_size = octree.get_spacing_at_depth(depth);
|
||||||
|
|
||||||
// Calculate the world position of the voxel
|
// The traverse method already returns the voxel center in world space.
|
||||||
let world_position = Vec3::new(
|
|
||||||
(x * octree.size) + (voxel_size / 2.0) - (octree.size / 2.0),
|
|
||||||
(y * octree.size) + (voxel_size / 2.0) - (octree.size / 2.0),
|
|
||||||
(z * octree.size) + (voxel_size / 2.0) - (octree.size / 2.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Convert world_position components to f32 for neighbor checking
|
// For each neighbor direction, check if this voxel face is exposed.
|
||||||
let world_x = world_position.x;
|
|
||||||
let world_y = world_position.y;
|
|
||||||
let world_z = world_position.z;
|
|
||||||
|
|
||||||
// Iterate over all possible neighbor offsets
|
|
||||||
for &(dx, dy, dz) in NEIGHBOR_OFFSETS.iter() {
|
for &(dx, dy, dz) in NEIGHBOR_OFFSETS.iter() {
|
||||||
|
// Pass the world-space voxel center directly.
|
||||||
|
if !octree.has_neighbor(world_position, dx as i32, dy as i32, dz as i32, depth) {
|
||||||
|
|
||||||
// Check if there's no neighbor in this direction
|
// Determine face normal and the local offset for the face.
|
||||||
if !octree.has_neighbor(world_x, world_y, world_z, dx as i32, dy as i32, dz as i32, depth) {
|
let (normal, offset) = match (dx, dy, dz) {
|
||||||
|
|
||||||
// Determine the face normal and local position based on the direction
|
|
||||||
let (normal, local_position) = match (dx, dy, dz) {
|
|
||||||
(-1.0, 0.0, 0.0) => (
|
(-1.0, 0.0, 0.0) => (
|
||||||
Vec3::new(-1.0, 0.0, 0.0),
|
Vec3::new(-1.0, 0.0, 0.0),
|
||||||
Vec3::new(-voxel_size / 2.0, 0.0, 0.0),
|
Vec3::new(-voxel_size / 2.0, 0.0, 0.0),
|
||||||
@ -88,77 +79,78 @@ pub fn render(
|
|||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate the face for rendering
|
|
||||||
voxel_meshes.push(generate_face(
|
voxel_meshes.push(generate_face(
|
||||||
normal,
|
world_position + offset, // offset the face
|
||||||
local_position,
|
|
||||||
world_position,
|
|
||||||
voxel_size / 2.0,
|
voxel_size / 2.0,
|
||||||
|
normal
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge the voxel meshes into a single mesh
|
// Merge all the face meshes into a single mesh.
|
||||||
let mesh = merge_meshes(voxel_meshes);
|
let mesh = merge_meshes(voxel_meshes);
|
||||||
let cube_handle = meshes.add(mesh);
|
let cube_handle = meshes.add(mesh);
|
||||||
|
|
||||||
|
// Create a material with cull_mode disabled to see both sides (for debugging)
|
||||||
|
let material = materials.add(StandardMaterial {
|
||||||
|
base_color: Color::srgba(0.8, 0.7, 0.6, 1.0),
|
||||||
|
cull_mode: Some(Face::Back), // disable culling for debugging
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Spawn the mesh into the scene
|
// Spawn the mesh into the scene
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
PbrBundle {
|
PbrBundle {
|
||||||
mesh: Mesh3d::from(cube_handle),
|
mesh: Mesh3d::from(cube_handle),
|
||||||
material: MeshMaterial3d::from(materials.add(StandardMaterial {
|
material: MeshMaterial3d::from(material),
|
||||||
base_color: Color::srgb(0.8, 0.7, 0.6),
|
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
transform: *octree_transform_query.single(),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
VoxelTerrainMarker {},
|
VoxelTerrainMarker {},
|
||||||
));
|
));
|
||||||
|
|
||||||
// Reset the dirty flag once the update is complete
|
// Reset the dirty flag after updating.
|
||||||
octree.dirty = false;
|
octree.dirty.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_face(position: Vec3, face_size: f32, normal: Vec3) -> Mesh {
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct VoxelTerrainMarker;
|
|
||||||
|
|
||||||
|
|
||||||
fn generate_face(orientation: Vec3, local_position: Vec3, position: Vec3, face_size: f32) -> Mesh {
|
|
||||||
// Initialize an empty mesh with triangle topology
|
// Initialize an empty mesh with triangle topology
|
||||||
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default());
|
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default());
|
||||||
|
|
||||||
|
// Define a quad centered at the origin
|
||||||
let mut positions = vec![
|
let mut positions = vec![
|
||||||
[-face_size, -face_size, 0.0],
|
[-face_size, -face_size, 0.0],
|
||||||
[face_size, -face_size, 0.0],
|
[ face_size, -face_size, 0.0],
|
||||||
[face_size, face_size, 0.0],
|
[ face_size, face_size, 0.0],
|
||||||
[-face_size, face_size, 0.0],
|
[-face_size, face_size, 0.0],
|
||||||
];
|
];
|
||||||
|
|
||||||
let rotation = Quat::from_rotation_arc(Vec3::Z, orientation);
|
// Normalize the provided normal to ensure correct rotation
|
||||||
|
let normal = normal.normalize();
|
||||||
|
// Compute a rotation that aligns the default +Z with the provided normal
|
||||||
|
let rotation = Quat::from_rotation_arc(Vec3::Z, normal);
|
||||||
|
|
||||||
// Rotate and translate the vertices based on orientation and position
|
// Rotate and translate the vertices based on the computed rotation and provided position
|
||||||
for p in positions.iter_mut() {
|
for p in positions.iter_mut() {
|
||||||
let vertex = rotation * Vec3::from(*p);
|
let vertex = rotation * Vec3::from(*p) + position;
|
||||||
let vertex = vertex + local_position + position; // Apply local and global translation
|
|
||||||
*p = [vertex.x, vertex.y, vertex.z];
|
*p = [vertex.x, vertex.y, vertex.z];
|
||||||
}
|
}
|
||||||
|
|
||||||
let uvs = vec![[0.0, 1.0], [1.0, 1.0], [1.0, 0.0], [0.0, 0.0]];
|
let uvs = vec![
|
||||||
|
[0.0, 1.0],
|
||||||
|
[1.0, 1.0],
|
||||||
|
[1.0, 0.0],
|
||||||
|
[0.0, 0.0],
|
||||||
|
];
|
||||||
|
|
||||||
let indices = Indices::U32(vec![0, 1, 2, 2, 3, 0]);
|
let indices = Indices::U32(vec![0, 1, 2, 2, 3, 0]);
|
||||||
|
|
||||||
let normal = rotation * Vec3::Z; // Since face is aligned to Vec3::Z initially
|
// Use the provided normal for all vertices
|
||||||
let normals = vec![
|
let normals = vec![[normal.x, normal.y, normal.z]; 4];
|
||||||
[normal.x, normal.y, normal.z]; // Use the same normal for all vertices
|
|
||||||
4 // Four vertices in a quad
|
|
||||||
];
|
|
||||||
|
|
||||||
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
|
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
|
||||||
mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
|
mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
|
||||||
@ -167,6 +159,7 @@ fn generate_face(orientation: Vec3, local_position: Vec3, position: Vec3, face_s
|
|||||||
|
|
||||||
mesh
|
mesh
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_meshes(meshes: Vec<Mesh>) -> Mesh {
|
fn merge_meshes(meshes: Vec<Mesh>) -> Mesh {
|
||||||
let mut merged_positions = Vec::new();
|
let mut merged_positions = Vec::new();
|
||||||
let mut merged_uvs = Vec::new();
|
let mut merged_uvs = Vec::new();
|
||||||
@ -209,4 +202,3 @@ fn merge_meshes(meshes: Vec<Mesh>) -> Mesh {
|
|||||||
|
|
||||||
merged_mesh
|
merged_mesh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,10 @@ use bevy_reflect::Reflect;
|
|||||||
#[derive(Debug, Clone, Copy, Component, PartialEq, Default)]
|
#[derive(Debug, Clone, Copy, Component, PartialEq, Default)]
|
||||||
pub struct Voxel {
|
pub struct Voxel {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy,Reflect)]
|
||||||
|
pub struct DirtyVoxel {
|
||||||
pub position: Vec3,
|
pub position: Vec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +36,8 @@ pub struct SparseVoxelOctree {
|
|||||||
pub show_wireframe: bool,
|
pub show_wireframe: bool,
|
||||||
pub show_world_grid: bool,
|
pub show_world_grid: bool,
|
||||||
pub show_chunks: bool,
|
pub show_chunks: bool,
|
||||||
pub dirty: bool,
|
|
||||||
|
pub dirty: Vec<DirtyVoxel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OctreeNode {
|
impl OctreeNode {
|
||||||
@ -55,7 +60,6 @@ impl Voxel {
|
|||||||
pub fn new(color: Color) -> Self {
|
pub fn new(color: Color) -> Self {
|
||||||
Self {
|
Self {
|
||||||
color,
|
color,
|
||||||
position: Vec3::ZERO
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user