half of the architecture for putting oct-tree to mesh on screen

This commit is contained in:
Lillian Vixe 2024-04-02 09:25:00 -07:00
parent d1118af004
commit 7f2e781616
6 changed files with 391 additions and 1 deletions

View file

@ -1,5 +1,8 @@
{
"rust-analyzer.linkedProjects": [
"./Cargo.toml",
"./Cargo.toml",
"./Cargo.toml",
"./Cargo.toml",
"./Cargo.toml"
],

View file

@ -0,0 +1,4 @@
#![allow(dead_code)]
pub enum InputEvent {
DragMouse(Vec2),
}

2
src/vvedit/mod.rs Normal file
View file

@ -0,0 +1,2 @@
#![allow(dead_code)]
pub mod editor_events;

66
src/vvlib/inputs.rs Normal file
View file

@ -0,0 +1,66 @@
#![allow(dead_code)]
use bevy::input::{gamepad::*, keyboard::KeyCode, mouse::MouseButton};
use bevy::prelude::*;
#[derive(Default)]
pub enum BinaryInputID {
#[default]
Unbound,
KeyCode(KeyCode),
MouseButton(MouseButton),
GamepadButton(GamepadButton, Gamepad),
}
#[derive(Default)]
pub enum BinaryInputState {
#[default]
Inactive,
JustActivated,
Activated,
JustDeactivated,
}
#[derive(Default)]
pub struct BinaryInputBinding {
inputs: [BinaryInputID; 5],
}
impl BinaryInputBinding {
pub fn is_activated(
&self,
keycode_buttons: Res<ButtonInput<KeyCode>>,
mouse_buttons: Res<ButtonInput<MouseButton>>,
gamepad_buttons: Res<ButtonInput<GamepadButton>>,
) -> bool {
for input in self.inputs.as_slice() {
match input {
BinaryInputID::KeyCode(code) => {
if !keycode_buttons.pressed(*code) {
return false;
}
}
BinaryInputID::MouseButton(button) => {
if !mouse_buttons.pressed(*button) {
return false;
}
}
BinaryInputID::GamepadButton(button, pad) => {
if !gamepad_buttons.pressed(GamepadButton::new(*pad, button.button_type)) {
return false;
}
}
BinaryInputID::Unbound => {} // ignoring unbound bindings, just means we're not using 5 total keys to do one input. sometimes you Just X to jump.
}
}
true // all are pressed or unbound and thus not mattering. activated
}
}
#[derive(Default)]
pub enum AxisInputID {
#[default]
Unbound,
MouseMotionX,
MouseMotionY,
GamepadAxis(GamepadAxis, Gamepad),
}

View file

@ -1,11 +1,26 @@
#![allow(dead_code)]
use bevy::app::App;
use bevy::app::Last;
use bevy::app::Plugin;
use bevy::ecs::component::Component;
use bevy::ecs::system::Query;
use bevy::math::Vec3;
use bevy::render::color::Color;
use bevy::render::mesh::Indices;
use bevy::render::mesh::Mesh;
use bevy::render::mesh::PrimitiveTopology;
use bevy::render::render_asset::RenderAssetUsages;
use bevy::utils::hashbrown::HashMap;
pub mod inputs;
pub mod intersections;
pub mod obb;
pub mod octtree;
pub mod constants {
use bevy::math::Vec3;
pub const CORNER: f32 = 0.866025;
pub const CORNER_3D: f32 = 0.866025;
pub fn to_region_id(center: Vec3, subject: Vec3) -> i32 {
if subject.x == center.x && subject.y == center.y && subject.z == center.z {
return -1;
@ -98,3 +113,301 @@ pub mod constants {
pub const ORDERED: [Vec3; 6] = [PX, NX, PY, NY, PZ, NZ];
}
}
pub struct OctTreePlugin {}
impl Plugin for OctTreePlugin {
fn build(&self, app: &mut App) {
app.add_systems(Last, oct_tree_edit_updater);
}
}
#[derive(Component)]
pub struct OctTreeModelComponent {
model: octtree::OctTree<Color>,
//TODO: add Option<> handle for remeshing-event for async-computing it - so if it gets edited again we can cancel the old one and let it run a new mesher async compute
}
/// schedules re-meshing updates of the models used to represent oct-tree data. collisions are instantly updated (as any update to the oct-tree itself updates the data used for collisioning, so we can be lazy about when the visuals kick in (to an extent, of course). as such, we want to kick them into an async updater for the meshes used)
pub fn oct_tree_edit_updater(mut query: Query<&mut OctTreeModelComponent>) {
for mut model in &mut query {
if model.model.needs_update {
// spawn remeshing async task, put in the model
model.model.needs_update = false;
}
}
}
//TODO: event that takes finished remeshing events and emits an event to be caught with the relevant mesh to update the meshdata
impl OctTreeModelComponent {
pub fn render_as_mesh(&self) -> Mesh {
let mut vertices = Vec::new();
let mut normals = Vec::new();
let mut indices = Vec::new();
let mut colors = Vec::new();
let elements = self.model.collect_voxels();
let mut lookup = HashMap::new();
for (_, loc, col) in elements {
lookup.insert(as_ia(loc), col);
}
for (loc, col) in &lookup {
emit_cube_at(
as_v(*loc),
&mut vertices,
&mut indices,
&mut normals,
&mut colors,
*col,
lookup.get(&as_ia(as_v(*loc) + Vec3::X)).is_none(),
lookup.get(&as_ia(as_v(*loc) + Vec3::NEG_X)).is_none(),
lookup.get(&as_ia(as_v(*loc) + Vec3::Y)).is_none(),
lookup.get(&as_ia(as_v(*loc) + Vec3::NEG_Y)).is_none(),
lookup.get(&as_ia(as_v(*loc) + Vec3::Z)).is_none(),
lookup.get(&as_ia(as_v(*loc) + Vec3::NEG_Z)).is_none(),
None, // will let me do custom per-vox normals later. that requires extra calculation im not doing yet
);
}
return as_mesh(vertices, indices, normals, colors);
}
}
fn as_ia(input: Vec3) -> [i64; 3] {
[
input.x.round() as i64,
input.y.round() as i64,
input.z.round() as i64,
]
}
fn as_v(input: [i64; 3]) -> Vec3 {
Vec3::new(input[0] as f32, input[1] as f32, input[2] as f32)
}
fn as_mesh(
vertices: Vec<[f32; 3]>,
indices: Vec<u32>,
normals: Vec<[f32; 3]>,
colors: Vec<[f32; 4]>,
) -> Mesh {
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::RENDER_WORLD,
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
.with_inserted_attribute(Mesh::ATTRIBUTE_COLOR, colors)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_indices(Indices::U32(indices))
}
fn emit_cube_at(
pos: Vec3,
vertices: &mut Vec<[f32; 3]>,
indices: &mut Vec<u32>,
normals: &mut Vec<[f32; 3]>,
colors: &mut Vec<[f32; 4]>,
color: Color,
render_px: bool,
render_nx: bool,
render_py: bool,
render_ny: bool,
render_pz: bool,
render_nz: bool,
normal: Option<Vec3>,
) {
if render_px {
let len = vertices.len() as u32;
vertices.extend([
[pos.x + 0.5, pos.y - 0.5, pos.z - 0.5],
[pos.x + 0.5, pos.y - 0.5, pos.z + 0.5],
[pos.x + 0.5, pos.y + 0.5, pos.z + 0.5],
[pos.x + 0.5, pos.y + 0.5, pos.z - 0.5],
]);
if normal.is_some() {
let normal = normal.clone().unwrap();
normals.extend([
normal.to_array(),
normal.to_array(),
normal.to_array(),
normal.to_array(),
]);
} else {
normals.extend([
[1.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
]);
}
colors.extend([
color.as_rgba_f32(),
color.as_rgba_f32(),
color.as_rgba_f32(),
color.as_rgba_f32(),
]);
indices.extend([len, len + 3, len + 1, len + 1, len + 3, len + 2]);
}
if render_nx {
let len = vertices.len() as u32;
vertices.extend([
[pos.x - 0.5, pos.y - 0.5, pos.z - 0.5],
[pos.x - 0.5, pos.y - 0.5, pos.z + 0.5],
[pos.x - 0.5, pos.y + 0.5, pos.z + 0.5],
[pos.x - 0.5, pos.y + 0.5, pos.z - 0.5],
]);
if normal.is_some() {
let normal = normal.clone().unwrap();
normals.extend([
normal.to_array(),
normal.to_array(),
normal.to_array(),
normal.to_array(),
]);
} else {
normals.extend([
[-1.0, 0.0, 0.0],
[-1.0, 0.0, 0.0],
[-1.0, 0.0, 0.0],
[-1.0, 0.0, 0.0],
]);
}
colors.extend([
color.as_rgba_f32(),
color.as_rgba_f32(),
color.as_rgba_f32(),
color.as_rgba_f32(),
]);
indices.extend([len + 2, len + 3, len + 1, len + 1, len + 3, len]);
}
if render_py {
let len = vertices.len() as u32;
vertices.extend([
[pos.x - 0.5, pos.y + 0.5, pos.z - 0.5],
[pos.x + 0.5, pos.y + 0.5, pos.z - 0.5],
[pos.x + 0.5, pos.y + 0.5, pos.z + 0.5],
[pos.x - 0.5, pos.y + 0.5, pos.z + 0.5],
]);
if normal.is_some() {
let normal = normal.clone().unwrap();
normals.extend([
normal.to_array(),
normal.to_array(),
normal.to_array(),
normal.to_array(),
]);
} else {
normals.extend([
[0.0, 1.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 1.0, 0.0],
]);
}
colors.extend([
color.as_rgba_f32(),
color.as_rgba_f32(),
color.as_rgba_f32(),
color.as_rgba_f32(),
]);
indices.extend([len, len + 3, len + 1, len + 1, len + 3, len + 2]);
}
if render_ny {
let len = vertices.len() as u32;
vertices.extend([
[pos.x - 0.5, pos.y - 0.5, pos.z - 0.5],
[pos.x + 0.5, pos.y - 0.5, pos.z - 0.5],
[pos.x + 0.5, pos.y - 0.5, pos.z + 0.5],
[pos.x - 0.5, pos.y - 0.5, pos.z + 0.5],
]);
if normal.is_some() {
let normal = normal.clone().unwrap();
normals.extend([
normal.to_array(),
normal.to_array(),
normal.to_array(),
normal.to_array(),
]);
} else {
normals.extend([
[0.0, -1.0, 0.0],
[0.0, -1.0, 0.0],
[0.0, -1.0, 0.0],
[0.0, -1.0, 0.0],
]);
}
colors.extend([
color.as_rgba_f32(),
color.as_rgba_f32(),
color.as_rgba_f32(),
color.as_rgba_f32(),
]);
indices.extend([len + 2, len + 3, len + 1, len + 1, len + 3, len]);
}
if render_pz {
let len = vertices.len() as u32;
vertices.extend([
[pos.x - 0.5, pos.y - 0.5, pos.z + 0.5],
[pos.x - 0.5, pos.y + 0.5, pos.z + 0.5],
[pos.x + 0.5, pos.y + 0.5, pos.z + 0.5],
[pos.x + 0.5, pos.y - 0.5, pos.z + 0.5],
]);
if normal.is_some() {
let normal = normal.clone().unwrap();
normals.extend([
normal.to_array(),
normal.to_array(),
normal.to_array(),
normal.to_array(),
]);
} else {
normals.extend([
[0.0, 0.0, 1.0],
[0.0, 0.0, 1.0],
[0.0, 0.0, 1.0],
[0.0, 0.0, 1.0],
]);
}
colors.extend([
color.as_rgba_f32(),
color.as_rgba_f32(),
color.as_rgba_f32(),
color.as_rgba_f32(),
]);
indices.extend([len, len + 3, len + 1, len + 1, len + 3, len + 2]);
}
if render_nz {
let len = vertices.len() as u32;
vertices.extend([
[pos.x - 0.5, pos.y - 0.5, pos.z - 0.5],
[pos.x - 0.5, pos.y + 0.5, pos.z - 0.5],
[pos.x + 0.5, pos.y + 0.5, pos.z - 0.5],
[pos.x + 0.5, pos.y - 0.5, pos.z - 0.5],
]);
if normal.is_some() {
let normal = normal.clone().unwrap();
normals.extend([
normal.to_array(),
normal.to_array(),
normal.to_array(),
normal.to_array(),
]);
} else {
normals.extend([
[0.0, 0.0, -1.0],
[0.0, 0.0, -1.0],
[0.0, 0.0, -1.0],
[0.0, 0.0, -1.0],
]);
}
colors.extend([
color.as_rgba_f32(),
color.as_rgba_f32(),
color.as_rgba_f32(),
color.as_rgba_f32(),
]);
indices.extend([len + 2, len + 3, len + 1, len + 1, len + 3, len]);
}
}

View file

@ -23,6 +23,7 @@ pub struct OctTree<T> {
pub(crate) size: usize,
pub(crate) center: Vec3,
pub(crate) root: TreeNode<T>,
pub needs_update: bool,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
@ -119,6 +120,7 @@ impl<T: Clone> OctTree<T> {
size: 1,
center: npos,
root: TreeNode::Leaf(value),
needs_update: false,
}
}