From e1cd4afd50537c59c28071fd29f423898a6321d0 Mon Sep 17 00:00:00 2001 From: davids91 Date: Fri, 2 Aug 2024 10:57:24 +0200 Subject: [PATCH] Continued dot_vox import implementation --- Cargo.toml | 2 +- examples/cpu_render.rs | 304 +++++++++++++++--------------- examples/minecraft.rs | 72 ++++--- src/octree/convert/magicavoxel.rs | 46 ++--- src/octree/convert/mod.rs | 2 +- src/spatial/math/mod.rs | 2 + src/spatial/math/tests.rs | 48 +++++ 7 files changed, 263 insertions(+), 213 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 01b5536..1fff708 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" default = ["bevy_wgpu", "magica_voxel"] raytracing = ["dep:image", "dep:show-image"] serialization = ["dep:serde"] -magica_voxel = ["dep:dot_vox"] +dot_vox_support = ["dep:dot_vox"] bevy_wgpu = ["raytracing", "dep:bevy"] [dependencies] diff --git a/examples/cpu_render.rs b/examples/cpu_render.rs index 4ab4a24..ea2565b 100644 --- a/examples/cpu_render.rs +++ b/examples/cpu_render.rs @@ -1,155 +1,155 @@ -// #[cfg(feature = "raytracing")] -// use rand::Rng; - -// #[cfg(feature = "raytracing")] -// use shocovox_rs::octree::{raytracing::Ray, V3c}; - -// #[cfg(feature = "raytracing")] -// #[show_image::main] -// fn main() { -// let voxel_color: Albedo = 0x645097FF.into(); - -// // fill octree with data -// const BRICK_DIMENSION: usize = 8; -// const TREE_SIZE: u32 = 64; -// let viewport_size_width = 150;ű -// let viewport_size_height = 150; -// let mut tree = shocovox_rs::octree::Octree::::new(TREE_SIZE) -// .ok() -// .unwrap(); - -// tree.insert(&V3c::new(1, 3, 3), voxel_color) -// .expect("insert of voxel to work"); -// for x in 0..TREE_SIZE { -// for y in 0..TREE_SIZE { -// for z in 0..TREE_SIZE { -// if ((x < (TREE_SIZE / 4) || y < (TREE_SIZE / 4) || z < (TREE_SIZE / 4)) -// && (0 == x % 2 && 0 == y % 4 && 0 == z % 2)) -// || ((TREE_SIZE / 2) <= x && (TREE_SIZE / 2) <= y && (TREE_SIZE / 2) <= z) -// { -// tree.insert( -// &V3c::new(x, y, z), -// Albedo::default() -// .with_red((255 as f32 * x as f32 / TREE_SIZE as f32) as u8) -// .with_green((255 as f32 * y as f32 / TREE_SIZE as f32) as u8) -// .with_blue((255 as f32 * z as f32 / TREE_SIZE as f32) as u8) -// .with_alpha(255), -// ) -// .ok() -// .unwrap(); -// } -// } -// } -// } - -// use shocovox_rs::octree::types::Albedo; -// use show_image::create_window; -// let window = create_window("image", Default::default()).ok().unwrap(); - -// let radius = 2. * TREE_SIZE as f32; -// let mut rng = rand::thread_rng(); -// let mut angle = 40.; -// let mut velos = V3c::new(-0.05, 0., 0.); - -// // Close app on window exit -// window -// .add_event_handler(|_, event, _| { -// match event { -// show_image::event::WindowEvent::Destroyed(_) => { -// std::process::exit(0); -// } -// _ => {} -// }; -// }) -// .ok() -// .unwrap(); - -// loop { -// //generate a random number to add to velos -// velos = velos -// + V3c::new( -// (-5 + rng.gen_range(0..10)) as f32 / 2000., -// (-5 + rng.gen_range(0..10)) as f32 / 2000., -// (-5 + rng.gen_range(0..10)) as f32 / 2000., -// ); -// angle = angle + velos.x / 10.; - -// // Set the viewport -// let origin = V3c::new(angle.sin() * radius, radius, angle.cos() * radius); -// let viewport_ray = Ray { -// direction: (V3c::unit(0.) - origin).normalized(), -// origin, -// }; -// let viewport_up_direction = V3c::new(0., 1., 0.); -// let viewport_right_direction = viewport_up_direction -// .cross(viewport_ray.direction) -// .normalized(); -// let viewport_width = 4.; -// let viewport_height = 4.; -// let viewport_fov = 3.; -// let pixel_width = viewport_width as f32 / viewport_size_width as f32; -// let pixel_height = viewport_height as f32 / viewport_size_height as f32; -// let viewport_bottom_left = viewport_ray.origin + (viewport_ray.direction * viewport_fov) -// - (viewport_up_direction * (viewport_height / 2.)) -// - (viewport_right_direction * (viewport_width / 2.)); - -// // define light -// let diffuse_light_normal = V3c::new(0., -1., 1.).normalized(); - -// use image::ImageBuffer; -// use image::Rgb; -// let mut img = ImageBuffer::new(viewport_size_width, viewport_size_height); - -// // cast each ray for a hit -// for y in 0..viewport_size_width { -// for x in 0..viewport_size_height { -// let actual_y_in_image = viewport_size_height - y - 1; -// //from the origin of the camera to the current point of the viewport -// let glass_point = viewport_bottom_left -// + viewport_right_direction * x as f32 * pixel_width -// + viewport_up_direction * y as f32 * pixel_height; -// let ray = Ray { -// origin: viewport_ray.origin, -// direction: (glass_point - viewport_ray.origin).normalized(), -// }; - -// use std::io::Write; -// std::io::stdout().flush().ok().unwrap(); - -// if let Some(hit) = tree.get_by_ray(&ray) { -// let (data, _, normal) = hit; -// //Because both vector should be normalized, the dot product should be 1*1*cos(angle) -// //That means it is in range -1, +1, which should be accounted for -// let diffuse_light_strength = -// 1. - (normal.dot(&diffuse_light_normal) / 2. + 0.5); -// img.put_pixel( -// x, -// actual_y_in_image, -// Rgb([ -// (data.r as f32 * diffuse_light_strength) as u8, -// (data.g as f32 * diffuse_light_strength) as u8, -// (data.b as f32 * diffuse_light_strength) as u8, -// ]), -// ); -// } else { -// img.put_pixel(x, actual_y_in_image, Rgb([128, 128, 128])); -// } -// } -// } - -// use show_image::{ImageInfo, ImageView}; -// let binding = img.into_raw(); -// let image = ImageView::new( -// ImageInfo::rgb8(viewport_size_width, viewport_size_height), -// &binding, -// ); - -// // Create a window with default options and display the image. -// window.set_image("image-001", image).ok().unwrap(); -// } -// } - -// #[cfg(not(feature = "raytracing"))] +#[cfg(feature = "raytracing")] +use rand::Rng; + +#[cfg(feature = "raytracing")] +use shocovox_rs::octree::{raytracing::Ray, Octree, V3c}; + +#[cfg(feature = "raytracing")] +#[show_image::main] +fn main() { + let voxel_color: Albedo = 0x645097FF.into(); + + // fill octree with data + const BRICK_DIMENSION: usize = 8; + const TREE_SIZE: u32 = 64; + let viewport_size_width = 150; + let viewport_size_height = 150; + // let mut tree = shocovox_rs::octree::Octree::::new(TREE_SIZE) + // .ok() + // .unwrap(); + + let tree; + if std::path::Path::new("example_junk_minecraft_tree").exists() { + tree = Octree::::load("example_junk_minecraft_tree") + .ok() + .unwrap(); + } else { + tree = match shocovox_rs::octree::Octree::::load_magica_voxel_file( + "assets/models/minecraft.vox", + ) { + Ok(tree_) => tree_, + Err(message) => panic!("Parsing model file failed with message: {message}"), + }; + tree.save("example_junk_minecraft_tree").ok().unwrap(); + } + + use shocovox_rs::octree::types::Albedo; + use show_image::create_window; + let window = create_window("image", Default::default()).ok().unwrap(); + + let radius = 2. * TREE_SIZE as f32; + let mut rng = rand::thread_rng(); + let mut angle = 40.; + let mut velos = V3c::new(-0.05, 0., 0.); + + // Close app on window exit + window + .add_event_handler(|_, event, _| { + match event { + show_image::event::WindowEvent::Destroyed(_) => { + std::process::exit(0); + } + _ => {} + }; + }) + .ok() + .unwrap(); + + loop { + //generate a random number to add to velos + velos = velos + + V3c::new( + (-5 + rng.gen_range(0..10)) as f32 / 2000., + (-5 + rng.gen_range(0..10)) as f32 / 2000., + (-5 + rng.gen_range(0..10)) as f32 / 2000., + ); + angle = angle + velos.x / 10.; + + // Set the viewport + let viewport_ray = Ray { + origin: V3c { + x: 6.15997, + y: 5.9686174, + z: 5.3837276, + }, + direction: V3c { + x: -0.6286289, + y: -0.5966367, + z: -0.49884903, + }, + }; + let viewport_up_direction = V3c::new(0., 1., 0.); + let viewport_right_direction = viewport_up_direction + .cross(viewport_ray.direction) + .normalized(); + let viewport_width = 4.; + let viewport_height = 4.; + let viewport_fov = 3.; + let pixel_width = viewport_width as f32 / viewport_size_width as f32; + let pixel_height = viewport_height as f32 / viewport_size_height as f32; + let viewport_bottom_left = viewport_ray.origin + (viewport_ray.direction * viewport_fov) + - (viewport_up_direction * (viewport_height / 2.)) + - (viewport_right_direction * (viewport_width / 2.)); + + // define light + let diffuse_light_normal = V3c::new(0., -1., 1.).normalized(); + + use image::ImageBuffer; + use image::Rgb; + let mut img = ImageBuffer::new(viewport_size_width, viewport_size_height); + + // cast each ray for a hit + for y in 0..viewport_size_width { + for x in 0..viewport_size_height { + let actual_y_in_image = viewport_size_height - y - 1; + //from the origin of the camera to the current point of the viewport + let glass_point = viewport_bottom_left + + viewport_right_direction * x as f32 * pixel_width + + viewport_up_direction * y as f32 * pixel_height; + let ray = Ray { + origin: viewport_ray.origin, + direction: (glass_point - viewport_ray.origin).normalized(), + }; + + use std::io::Write; + std::io::stdout().flush().ok().unwrap(); + + if let Some(hit) = tree.get_by_ray(&ray) { + let (data, _, normal) = hit; + //Because both vector should be normalized, the dot product should be 1*1*cos(angle) + //That means it is in range -1, +1, which should be accounted for + let diffuse_light_strength = + 1. - (normal.dot(&diffuse_light_normal) / 2. + 0.5); + img.put_pixel( + x, + actual_y_in_image, + Rgb([ + (data.r as f32 * diffuse_light_strength) as u8, + (data.g as f32 * diffuse_light_strength) as u8, + (data.b as f32 * diffuse_light_strength) as u8, + ]), + ); + } else { + img.put_pixel(x, actual_y_in_image, Rgb([128, 128, 128])); + } + } + } + + img.save("example_junk_cpu_render.png").ok().unwrap(); + std::process::exit(0); + // use show_image::{ImageInfo, ImageView}; + // let binding = img.into_raw(); + // let image = ImageView::new( + // ImageInfo::rgb8(viewport_size_width, viewport_size_height), + // &binding, + // ); + + // // Create a window with default options and display the image. + // window.set_image("image-001", image).ok().unwrap(); + } +} + +#[cfg(not(feature = "raytracing"))] fn main() { println!("You probably forgot to enable the raytracing feature!"); //nothing to do when the feature is not enabled diff --git a/examples/minecraft.rs b/examples/minecraft.rs index 48da139..b67e429 100644 --- a/examples/minecraft.rs +++ b/examples/minecraft.rs @@ -15,9 +15,6 @@ use shocovox_rs::octree::{ #[cfg(feature = "bevy_wgpu")] const DISPLAY_RESOLUTION: [u32; 2] = [1024, 768]; -#[cfg(feature = "bevy_wgpu")] -const TREE_SIZE: u32 = 64; - #[cfg(feature = "bevy_wgpu")] fn main() { App::new() @@ -36,17 +33,11 @@ fn main() { #[cfg(feature = "bevy_wgpu")] fn setup(mut commands: Commands, images: ResMut>) { - let origin = V3c::new( - TREE_SIZE as f32 * 2., - TREE_SIZE as f32 / 2., - TREE_SIZE as f32 * -2., - ); - commands.spawn(DomePosition { yaw: 0. }); - // fill octree with data let tree; - if std::path::Path::new("example_junk_minecraft_tree").exists() { - tree = Octree::::load("test_junk_octree").ok().unwrap(); + let tree_path = "example_junk_minecraft_tree"; // instead of test_junk_octree + if std::path::Path::new(tree_path).exists() { + tree = Octree::::load(&tree_path).ok().unwrap(); } else { tree = match shocovox_rs::octree::Octree::::load_magica_voxel_file( "assets/models/minecraft.vox", @@ -54,14 +45,32 @@ fn setup(mut commands: Commands, images: ResMut>) { Ok(tree_) => tree_, Err(message) => panic!("Parsing model file failed with message: {message}"), }; - tree.save("example_junk_minecraft_tree"); + tree.save(&tree_path).ok().unwrap(); } + let origin = V3c::new( + tree.get_size() as f32 * 2., + tree.get_size() as f32 / 2., + tree.get_size() as f32 * -2., + ); + commands.spawn(DomePosition { + yaw: 0., + radius: tree.get_size() as f32 * 2.2, + }); + let render_data = tree.create_bevy_view(); let viewing_glass = create_viewing_glass( &Viewport { - origin, - direction: (V3c::new(0., 0., 0.) - origin).normalized(), + origin: V3c { + x: 6.15997, + y: 5.9686174, + z: 5.3837276, + }, + direction: V3c { + x: -0.6286289, + y: -0.5966367, + z: -0.49884903, + }, w_h_fov: V3c::new(10., 10., 3.), }, DISPLAY_RESOLUTION, @@ -83,6 +92,7 @@ fn setup(mut commands: Commands, images: ResMut>) { #[cfg(feature = "bevy_wgpu")] #[derive(Component)] struct DomePosition { + radius: f32, yaw: f32, } @@ -91,30 +101,16 @@ fn rotate_camera( angles_query: Query<&mut DomePosition>, mut viewing_glass: ResMut, ) { - // let angle = { - // let addition = ARRAY_DIMENSION as f32 / 10.; - // let angle = angles_query.single().yaw + addition; - // if angle < 360. { - // angle - // } else { - // 0. - // } - // }; - // angles_query.single_mut().yaw = angle; - - let radius = TREE_SIZE as f32 * 2.5; let angle = angles_query.single().yaw; + + let radius = angles_query.single().radius; viewing_glass.viewport.origin = V3c::new( - TREE_SIZE as f32 / 2. + angle.sin() * radius, - TREE_SIZE as f32 + angle.cos() * angle.sin() * radius / 2., - TREE_SIZE as f32 / 2. + angle.cos() * radius, + radius / 2. + angle.sin() * radius, + radius + angle.cos() * angle.sin() * radius / 2., + radius / 2. + angle.cos() * radius, ); - viewing_glass.viewport.direction = (V3c::new( - TREE_SIZE as f32 / 2., - TREE_SIZE as f32 / 2., - TREE_SIZE as f32 / 2., - ) - viewing_glass.viewport.origin) - .normalized(); + viewing_glass.viewport.direction = + (V3c::unit(radius / 2.) - viewing_glass.viewport.origin).normalized(); } #[cfg(feature = "bevy_wgpu")] @@ -131,7 +127,7 @@ fn handle_zoom( viewing_glass.viewport.w_h_fov.x *= 0.9; viewing_glass.viewport.w_h_fov.y *= 0.9; } - let addition = TREE_SIZE as f32 / 10.; + let addition = 0.05; if keys.pressed(KeyCode::ArrowLeft) { let angle = { let angle = angles_query.single().yaw - addition; @@ -142,6 +138,7 @@ fn handle_zoom( } }; angles_query.single_mut().yaw = angle; + println!("viewport: {:?}", viewing_glass.viewport); } if keys.pressed(KeyCode::ArrowRight) { let angle = { @@ -153,6 +150,7 @@ fn handle_zoom( } }; angles_query.single_mut().yaw = angle; + println!("viewport: {:?}", viewing_glass.viewport); } } diff --git a/src/octree/convert/magicavoxel.rs b/src/octree/convert/magicavoxel.rs index 3536194..b209465 100644 --- a/src/octree/convert/magicavoxel.rs +++ b/src/octree/convert/magicavoxel.rs @@ -2,7 +2,7 @@ use crate::{ octree::{Albedo, Octree, V3c, VoxelData}, spatial::math::{convert_coordinate, CoordinateSystemType}, }; -use dot_vox::{Color, DotVoxData, Model, SceneNode}; +use dot_vox::{Color, DotVoxData, Model, SceneNode, Voxel}; impl From for Color { fn from(color: Albedo) -> Self { @@ -26,6 +26,16 @@ impl From for Albedo { } } +impl From for V3c { + fn from(other: Voxel) -> Self { + Self { + x: other.x as i32, + y: other.y as i32, + z: other.z as i32, + } + } +} + impl VoxelData for Color { fn new(albedo: Albedo, _: u32) -> Self { albedo.into() @@ -54,14 +64,6 @@ fn iterate_vox_tree) -> ()>(vox_tree: &DotVoxData, mu child, layer_id: _, } => { - // let transform_position: Vec = frames[0] - // .attributes - // .get("_t") - // .unwrap( - // .split(" ") - // .map(|x| x.parse().expect("Not an integer!")) - // .collect(); - // node_stack.push((*child, transform_position.into(), 0)); node_stack.push((*child, V3c::unit(0), 0)); } _ => { @@ -125,12 +127,13 @@ fn iterate_vox_tree) -> ()>(vox_tree: &DotVoxData, mu attributes: _, models, } => { + //TODO: convert to correct coordinate system, offset by half of the size // println!("Processing shape"); - // let transform = convert_coordinate( - // transform, - // CoordinateSystemType::LeftHandedZup, - // CoordinateSystemType::LeftHandedYup, - // ); + let transform = convert_coordinate( + transform, + CoordinateSystemType::LZUP, + CoordinateSystemType::LYUP, + ); for model in models { fun(&vox_tree.models[model.model_id as usize], &transform); } @@ -163,7 +166,6 @@ where // } // panic!("AH"); - std::env::set_var("RUST_BACKTRACE", "1"); let mut min_position = V3c::new(0, 0, 0); let mut max_position = V3c::new(0, 0, 0); iterate_vox_tree(&vox_tree, |model, position| { @@ -189,24 +191,24 @@ where println!("octree size: {max_dimension}"); let mut shocovox_octree = Octree::::new(max_dimension).ok().unwrap(); iterate_vox_tree(&vox_tree, |model, position| { - let current_position = *position - V3c::::from(min_position); - for v in &model.voxels { + let current_position = *position - min_position; + for voxel in &model.voxels { let voxel_position = convert_coordinate( - V3c::new(v.x as i32, v.y as i32, v.z as i32), - CoordinateSystemType::LZUP, + V3c::from(*voxel), + CoordinateSystemType::RZUP, CoordinateSystemType::LYUP, ); - + // println!("voxel position: {:?} ==> {:?}", v_pos, voxel_position); // println!("{:?} + {:?} = ? ", current_position, voxel_position); shocovox_octree .insert( &V3c::::from(current_position + voxel_position.into()), - T::new(vox_tree.palette[v.i as usize].into(), 0), + T::new(vox_tree.palette[voxel.i as usize].into(), 0), ) .ok(); } }); - println!("Tree built form model!"); + println!("Tree built from model!"); Ok(shocovox_octree) } } diff --git a/src/octree/convert/mod.rs b/src/octree/convert/mod.rs index e51032a..3b68ae2 100644 --- a/src/octree/convert/mod.rs +++ b/src/octree/convert/mod.rs @@ -1,4 +1,4 @@ mod bytecode; -#[cfg(feature = "magica_voxel")] +#[cfg(feature = "dot_vox_support")] mod magicavoxel; diff --git a/src/spatial/math/mod.rs b/src/spatial/math/mod.rs index f05898e..5a8e3af 100644 --- a/src/spatial/math/mod.rs +++ b/src/spatial/math/mod.rs @@ -102,6 +102,7 @@ pub(crate) fn octant_bitmask(octant: u8) -> u8 { 0x01 << octant } +#[cfg(feature = "dot_vox_support")] pub(crate) enum CoordinateSystemType { LZUP, // Left handed Z Up LYUP, // Left handed Y Up @@ -109,6 +110,7 @@ pub(crate) enum CoordinateSystemType { RYUP, // Right handed Y Up } +#[cfg(feature = "dot_vox_support")] pub(crate) fn convert_coordinate>( c: V3c, src_type: CoordinateSystemType, diff --git a/src/spatial/math/tests.rs b/src/spatial/math/tests.rs index 620be90..be734f0 100644 --- a/src/spatial/math/tests.rs +++ b/src/spatial/math/tests.rs @@ -84,3 +84,51 @@ mod wgpu_tests { assert_eq!(value, original_value); } } + +#[cfg(test)] +#[cfg(feature = "dot_vox_support")] +mod dot_vox_tests { + + use crate::octree::V3c; + use crate::spatial::math::convert_coordinate; + use crate::spatial::math::CoordinateSystemType; + + #[test] + fn test_coordinate_conversion() { + assert_eq!( + V3c::new(1., 2., 3.), + convert_coordinate( + V3c::new(1., 2., 3.), + CoordinateSystemType::RZUP, + CoordinateSystemType::RZUP, + ), + ); + + assert_eq!( + V3c::new(1., 3., 2.), + convert_coordinate( + V3c::new(1., 2., 3.), + CoordinateSystemType::LZUP, + CoordinateSystemType::RYUP, + ), + ); + + assert_eq!( + V3c::new(1., 3., -2.), + convert_coordinate( + V3c::new(1., 2., 3.), + CoordinateSystemType::RZUP, + CoordinateSystemType::RYUP, + ), + ); + + assert_eq!( + V3c::new(1., 2., -3.), + convert_coordinate( + V3c::new(1., 2., 3.), + CoordinateSystemType::LYUP, + CoordinateSystemType::RYUP, + ), + ); + } +}