file save and loading with expandable backwards compat layer
This commit is contained in:
parent
87dacb619e
commit
f764cd14c3
|
@ -1,6 +1,9 @@
|
||||||
0 0 0|1 0 0
|
version 1
|
||||||
0 -0 1|0.68343294 0.073312916 0.7192233
|
0 0 0|1 0 0
|
||||||
-0 1 0|0.60703295 0.3218385 0.47832417
|
0 0 1|1 0 0.77999973
|
||||||
0 1 1|0.68343294 0.073312916 0.7192233
|
0 0 2|0.0800004 0 1
|
||||||
1 -0 -0|0.19322336 0.48136216 0.49846292
|
0 0 3|0 0.7600002 1
|
||||||
1 -0 1|0.19322336 0.48136216 0.49846292
|
0 0 4|0 1 0.3399999
|
||||||
|
0 0 5|0.5 1 0
|
||||||
|
0 0 6|1 0.8399999 0
|
||||||
|
0 0 7|1 0.4200003 0
|
|
@ -1,16 +1,14 @@
|
||||||
use bevy::{
|
use bevy::{
|
||||||
app::{Startup, Update},
|
app::{Startup, Update},
|
||||||
asset::{AssetId, AssetServer, Assets},
|
asset::{AssetServer, Assets, Handle},
|
||||||
ecs::{
|
ecs::{
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
query::With,
|
query::With,
|
||||||
system::{Commands, Query, Res, ResMut, Resource},
|
system::{Commands, Query, Res, ResMut, Resource},
|
||||||
},
|
},
|
||||||
math::Vec3,
|
math::Vec3,
|
||||||
reflect::Reflect,
|
|
||||||
render::{camera::Camera, color::Color},
|
render::{camera::Camera, color::Color},
|
||||||
transform::components::GlobalTransform,
|
transform::components::GlobalTransform,
|
||||||
utils::HashMap,
|
|
||||||
window::Window,
|
window::Window,
|
||||||
};
|
};
|
||||||
use bevy_egui::{
|
use bevy_egui::{
|
||||||
|
@ -18,7 +16,7 @@ use bevy_egui::{
|
||||||
self, color_picker,
|
self, color_picker,
|
||||||
ecolor::{hsv_from_rgb, rgb_from_hsv},
|
ecolor::{hsv_from_rgb, rgb_from_hsv},
|
||||||
epaint::Hsva,
|
epaint::Hsva,
|
||||||
Id,
|
Id, Rect,
|
||||||
},
|
},
|
||||||
EguiContexts, EguiPlugin,
|
EguiContexts, EguiPlugin,
|
||||||
};
|
};
|
||||||
|
@ -40,34 +38,97 @@ use super::{
|
||||||
orbit_camera::orbit_camera::{pan_orbit_camera, spawn_camera},
|
orbit_camera::orbit_camera::{pan_orbit_camera, spawn_camera},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub struct AssetEditData {
|
||||||
|
path: String,
|
||||||
|
id: Handle<OctTreeAsset>,
|
||||||
|
has_changed_since_last_save: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Resource)]
|
#[derive(Default, Resource)]
|
||||||
pub struct EditWindowUIState {
|
pub struct EditWindowUIState {
|
||||||
brush_color: Hsva,
|
brush_color: Hsva,
|
||||||
window_identifier: Option<Id>,
|
window_identifier: Option<Id>,
|
||||||
meshes: HashMap<String, AssetId<OctTreeAsset>>,
|
meshes: Vec<AssetEditData>,
|
||||||
|
window: Option<Rect>,
|
||||||
}
|
}
|
||||||
|
// refactor into hashmaps of the AssetId
|
||||||
impl EditWindowUIState {
|
impl EditWindowUIState {
|
||||||
pub fn name_for(&self, id: AssetId<OctTreeAsset>) -> Option<String> {
|
pub fn name_from_handle(&self, id: Handle<OctTreeAsset>) -> Option<String> {
|
||||||
for (each_str, each_id) in &self.meshes {
|
for each in &self.meshes {
|
||||||
if each_id == &id {
|
if &each.id == &id {
|
||||||
return Some(each_str.clone());
|
return Some(each.path.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
pub fn id_for(&self, name: &String) -> Option<AssetId<OctTreeAsset>> {
|
pub fn has_changed_from_name(&self, name: &String) -> Option<bool> {
|
||||||
if self.meshes.contains_key(name) {
|
for each in &self.meshes {
|
||||||
return Some(self.meshes.get(name).unwrap().clone());
|
if &each.path == name {
|
||||||
|
return Some(each.has_changed_since_last_save);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
pub fn has_changed_from_handle(&self, id: Handle<OctTreeAsset>) -> Option<bool> {
|
||||||
|
for each in &self.meshes {
|
||||||
|
if &each.id == &id {
|
||||||
|
return Some(each.has_changed_since_last_save);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
pub fn handle_from_name(&self, name: &String) -> Option<Handle<OctTreeAsset>> {
|
||||||
|
for each in &self.meshes {
|
||||||
|
if &each.path == name {
|
||||||
|
return Some(each.id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
pub fn set_grid_data(
|
||||||
|
&mut self,
|
||||||
|
path: &String,
|
||||||
|
id: Handle<OctTreeAsset>,
|
||||||
|
has_changed: bool,
|
||||||
|
) -> bool {
|
||||||
|
let mut match_count: usize = 0;
|
||||||
|
for each in &self.meshes {
|
||||||
|
if &each.path == path {
|
||||||
|
match_count += 1;
|
||||||
|
} else if &each.id == &id {
|
||||||
|
match_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match_count == 1 {
|
||||||
|
for each in &mut self.meshes {
|
||||||
|
if &each.path == path {
|
||||||
|
each.id = id.clone();
|
||||||
|
each.has_changed_since_last_save = has_changed;
|
||||||
|
} else if &each.id == &id {
|
||||||
|
each.path = path.clone();
|
||||||
|
each.has_changed_since_last_save = has_changed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if match_count == 0 {
|
||||||
|
self.meshes.push(AssetEditData {
|
||||||
|
path: path.clone(),
|
||||||
|
id,
|
||||||
|
has_changed_since_last_save: has_changed,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_edit_ui(app: &mut bevy::prelude::App) -> &mut bevy::prelude::App {
|
pub fn register_edit_ui(app: &mut bevy::prelude::App) -> &mut bevy::prelude::App {
|
||||||
app.insert_resource(EditWindowUIState {
|
app.insert_resource(EditWindowUIState {
|
||||||
brush_color: egui_color_from(Color::RED),
|
brush_color: egui_color_from(Color::RED),
|
||||||
window_identifier: None,
|
window_identifier: None,
|
||||||
meshes: HashMap::new(),
|
meshes: Vec::new(),
|
||||||
|
window: None,
|
||||||
});
|
});
|
||||||
app.add_plugins(EguiPlugin);
|
app.add_plugins(EguiPlugin);
|
||||||
app.add_systems(Startup, startup_system_edit_ui);
|
app.add_systems(Startup, startup_system_edit_ui);
|
||||||
|
@ -88,7 +149,7 @@ pub fn edit_click_events(
|
||||||
oct_tree: Query<(&GlobalTransform, &OctAssetMarker)>,
|
oct_tree: Query<(&GlobalTransform, &OctAssetMarker)>,
|
||||||
windows: Query<&Window>,
|
windows: Query<&Window>,
|
||||||
mut oct_assets: ResMut<Assets<OctTreeAsset>>,
|
mut oct_assets: ResMut<Assets<OctTreeAsset>>,
|
||||||
shared_ui_state: ResMut<EditWindowUIState>,
|
mut shared_ui_state: ResMut<EditWindowUIState>,
|
||||||
) {
|
) {
|
||||||
let paint = input_data.get_binary_input(&PAINT_VOXEL.into());
|
let paint = input_data.get_binary_input(&PAINT_VOXEL.into());
|
||||||
let remove = input_data.get_binary_input(&REMOVE_VOXEL.into());
|
let remove = input_data.get_binary_input(&REMOVE_VOXEL.into());
|
||||||
|
@ -119,12 +180,20 @@ pub fn edit_click_events(
|
||||||
col.voxel_index_location,
|
col.voxel_index_location,
|
||||||
color_from(shared_ui_state.brush_color),
|
color_from(shared_ui_state.brush_color),
|
||||||
);
|
);
|
||||||
|
let path = shared_ui_state.name_from_handle(oct.oct_handle.clone());
|
||||||
|
if let Some(path) = path {
|
||||||
|
shared_ui_state.set_grid_data(&path, oct.oct_handle.clone(), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(_) = remove.filter(|input| input.pressed()) {
|
} else if let Some(_) = remove.filter(|input| input.pressed()) {
|
||||||
if collision.is_some() {
|
if collision.is_some() {
|
||||||
let col = collision.unwrap();
|
let col = collision.unwrap();
|
||||||
|
|
||||||
edit_octtree.remove_voxel(col.voxel_index_location);
|
edit_octtree.remove_voxel(col.voxel_index_location);
|
||||||
|
let path = shared_ui_state.name_from_handle(oct.oct_handle.clone());
|
||||||
|
if let Some(path) = path {
|
||||||
|
shared_ui_state.set_grid_data(&path, oct.oct_handle.clone(), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(_) = insert.filter(|input| input.pressed()) {
|
} else if let Some(_) = insert.filter(|input| input.pressed()) {
|
||||||
if collision.is_some() {
|
if collision.is_some() {
|
||||||
|
@ -136,13 +205,17 @@ pub fn edit_click_events(
|
||||||
hitvox.round(),
|
hitvox.round(),
|
||||||
color_from(shared_ui_state.brush_color),
|
color_from(shared_ui_state.brush_color),
|
||||||
);
|
);
|
||||||
|
let path = shared_ui_state.name_from_handle(oct.oct_handle.clone());
|
||||||
|
if let Some(path) = path {
|
||||||
|
shared_ui_state.set_grid_data(&path, oct.oct_handle.clone(), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// update to use events to respond to
|
||||||
pub fn update_asset_mesh_list(
|
pub fn update_asset_mesh_list(
|
||||||
oct_assets: Res<Assets<OctTreeAsset>>,
|
oct_assets: Res<Assets<OctTreeAsset>>,
|
||||||
mut shared_ui_state: ResMut<EditWindowUIState>,
|
mut shared_ui_state: ResMut<EditWindowUIState>,
|
||||||
|
@ -150,17 +223,22 @@ pub fn update_asset_mesh_list(
|
||||||
) {
|
) {
|
||||||
for each in oct_assets.ids() {
|
for each in oct_assets.ids() {
|
||||||
let asset = oct_assets.get(each);
|
let asset = oct_assets.get(each);
|
||||||
if asset.is_some() {
|
let handle = server.get_id_handle(each);
|
||||||
|
if asset.is_some() && handle.is_some() {
|
||||||
|
let handle = handle.unwrap();
|
||||||
let path = server.get_path(each);
|
let path = server.get_path(each);
|
||||||
if path.is_some() && shared_ui_state.name_for(each).is_none() {
|
if let Some(path) = path {
|
||||||
let path_str = path.unwrap().to_string();
|
if shared_ui_state.name_from_handle(handle.clone()).is_none() {
|
||||||
if path_str.to_lowercase().ends_with(".vvg") {
|
let path_str = path.to_string();
|
||||||
let reversed: String = path_str.chars().rev().collect();
|
if path_str.to_lowercase().ends_with(".vvg") {
|
||||||
let reversed_removed = reversed.replacen("gvv.", "", 1);
|
let reversed: String = path_str.chars().rev().collect();
|
||||||
let forward: String = reversed_removed.chars().rev().collect();
|
let reversed_removed = reversed.replacen("gvv.", "", 1);
|
||||||
shared_ui_state.meshes.insert(forward, each.clone());
|
let forward: String = reversed_removed.chars().rev().collect();
|
||||||
|
shared_ui_state.set_grid_data(&forward, handle.clone(), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if shared_ui_state.name_for(each).is_none() {
|
}
|
||||||
|
if shared_ui_state.name_from_handle(handle.clone()).is_none() {
|
||||||
let base_str: String = "unnamed_mesh_".into();
|
let base_str: String = "unnamed_mesh_".into();
|
||||||
let mut found_name = false;
|
let mut found_name = false;
|
||||||
let mut count: usize = 1;
|
let mut count: usize = 1;
|
||||||
|
@ -168,19 +246,20 @@ pub fn update_asset_mesh_list(
|
||||||
while !found_name {
|
while !found_name {
|
||||||
attempt = base_str.clone();
|
attempt = base_str.clone();
|
||||||
attempt.push_str(count.to_string().as_str());
|
attempt.push_str(count.to_string().as_str());
|
||||||
if shared_ui_state.id_for(&attempt).is_none() {
|
if shared_ui_state.handle_from_name(&attempt).is_none() {
|
||||||
found_name = true;
|
found_name = true;
|
||||||
} else {
|
} else {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shared_ui_state.meshes.insert(attempt, each.clone());
|
let mut has_changed = shared_ui_state.has_changed_from_handle(handle.clone());
|
||||||
|
if has_changed.is_none() {
|
||||||
|
has_changed = Some(false);
|
||||||
|
}
|
||||||
|
shared_ui_state.set_grid_data(&attempt, handle.clone(), has_changed.unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (each_name, each_id) in &shared_ui_state.meshes {
|
|
||||||
println!("{}", each_name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn edit_window_ui(
|
pub fn edit_window_ui(
|
||||||
|
@ -199,17 +278,14 @@ pub fn edit_window_ui(
|
||||||
shared_ui_state.window_identifier = Some(id);
|
shared_ui_state.window_identifier = Some(id);
|
||||||
} else {
|
} else {
|
||||||
response = response.id(shared_ui_state.window_identifier.unwrap());
|
response = response.id(shared_ui_state.window_identifier.unwrap());
|
||||||
/*for window_entity in &windows {
|
for window_entity in &windows {
|
||||||
let ctx = contexts.ctx_for_window_mut(window_entity);
|
let ctx = contexts.ctx_for_window_mut(window_entity);
|
||||||
let pos = ctx.memory(|memory| {
|
let pos = ctx.memory(|memory| {
|
||||||
// formatting comment
|
// formatting comment
|
||||||
memory.area_rect(shared_ui_state.window_identifier.unwrap())
|
memory.area_rect(shared_ui_state.window_identifier.unwrap())
|
||||||
});
|
});
|
||||||
if let Some(pos) = pos {
|
shared_ui_state.window = pos;
|
||||||
let (x, y, w, h) = (pos.min.x, pos.min.y, pos.max.x, pos.max.y);
|
}
|
||||||
println!("window data: {x}, {y} to {w}, {h}");
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
response.show(contexts.ctx_mut(), |ui| {
|
response.show(contexts.ctx_mut(), |ui| {
|
||||||
|
@ -231,13 +307,32 @@ pub fn edit_window_ui(
|
||||||
let id = pair.oct_handle.clone();
|
let id = pair.oct_handle.clone();
|
||||||
let tree = oct_assets.get_mut(id);
|
let tree = oct_assets.get_mut(id);
|
||||||
if tree.is_some() {
|
if tree.is_some() {
|
||||||
//
|
|
||||||
let tree = tree.unwrap();
|
let tree = tree.unwrap();
|
||||||
tree.model
|
tree.model
|
||||||
.set_voxel_at_location(Vec3::ZERO, color_from(shared_ui_state.brush_color));
|
.set_voxel_at_location(Vec3::ZERO, color_from(shared_ui_state.brush_color));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ui.button("Save Any Changes").clicked() {
|
||||||
|
//save all files.
|
||||||
|
for each in &shared_ui_state.meshes {
|
||||||
|
if each.has_changed_since_last_save {
|
||||||
|
let handle = each.id.clone();
|
||||||
|
let path = each.path.clone() + ".vvg";
|
||||||
|
let asset = oct_assets.get(handle);
|
||||||
|
if asset.is_some() {
|
||||||
|
let result =
|
||||||
|
crate::vvlib::oct_asset::serialization::meshes::write_latest_version(
|
||||||
|
&path,
|
||||||
|
asset.unwrap(),
|
||||||
|
);
|
||||||
|
if result {
|
||||||
|
println!("{} saved to disk.", &path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ use bevy::{
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use super::octtree::{self, OctTree, Path};
|
use super::octtree::{self, Path};
|
||||||
|
|
||||||
#[derive(Asset, TypePath)]
|
#[derive(Asset, TypePath)]
|
||||||
pub struct OctTreeAsset {
|
pub struct OctTreeAsset {
|
||||||
|
@ -47,6 +47,229 @@ pub enum OctLoadError {
|
||||||
FileNotFound(String),
|
FileNotFound(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod serialization {
|
||||||
|
|
||||||
|
pub mod meshes {
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use bevy::{
|
||||||
|
asset::io::{AssetSource, AssetWriter},
|
||||||
|
tasks::block_on,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::super::{OctLoadError, OctTreeAsset};
|
||||||
|
|
||||||
|
pub fn load_detect_version(bytes: Vec<u8>) -> Result<OctTreeAsset, OctLoadError> {
|
||||||
|
let data_str = std::str::from_utf8(bytes.as_slice());
|
||||||
|
if data_str.is_err() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid("".to_string()));
|
||||||
|
}
|
||||||
|
let data: String = data_str.unwrap().into();
|
||||||
|
let lines: std::str::Lines<'_> = data.lines();
|
||||||
|
let mut iter: std::str::Lines<'_> = lines.into_iter();
|
||||||
|
let first: Option<&str> = iter.next();
|
||||||
|
let version: MeshFileVersions = determine_version(first);
|
||||||
|
match version {
|
||||||
|
MeshFileVersions::Version1 => {
|
||||||
|
return versions::version_1::load(iter);
|
||||||
|
}
|
||||||
|
MeshFileVersions::Error => {
|
||||||
|
return Err(OctLoadError::FileDataInvalid("invalid version data".into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_latest_version(path: &String, asset: &OctTreeAsset) -> bool {
|
||||||
|
write_any_version(path, asset, Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_any_version(
|
||||||
|
path: &String,
|
||||||
|
asset: &OctTreeAsset,
|
||||||
|
version: MeshFileVersions,
|
||||||
|
) -> bool {
|
||||||
|
if let Some(writer) = get_writer() {
|
||||||
|
let mut bytes: Vec<u8> = Vec::new();
|
||||||
|
if let Some(mut header) = version_header(version) {
|
||||||
|
bytes.append(&mut header);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
match version {
|
||||||
|
MeshFileVersions::Error => return false,
|
||||||
|
MeshFileVersions::Version1 => {
|
||||||
|
if let Some(mut contents) = versions::version_1::save(&asset) {
|
||||||
|
bytes.append(&mut contents);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let res =
|
||||||
|
block_on(async { writer.write_bytes(Path::new(path), bytes.as_slice()).await });
|
||||||
|
return res.is_ok();
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_writer() -> Option<Box<dyn AssetWriter>> {
|
||||||
|
AssetSource::get_default_writer("assets".into())(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default)]
|
||||||
|
pub enum MeshFileVersions {
|
||||||
|
#[default]
|
||||||
|
Version1,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod versions {
|
||||||
|
|
||||||
|
pub mod version_1 {
|
||||||
|
use bevy::{math::Vec3, render::color::Color};
|
||||||
|
|
||||||
|
use crate::vvlib::octtree::OctTree;
|
||||||
|
|
||||||
|
use super::super::super::super::{OctLoadError, OctTreeAsset};
|
||||||
|
pub fn load(data: std::str::Lines<'_>) -> Result<OctTreeAsset, OctLoadError> {
|
||||||
|
let mut oct: Option<OctTree<Color>> = None;
|
||||||
|
for line in data {
|
||||||
|
//
|
||||||
|
let line: String = line.into();
|
||||||
|
let mut sides = line.split("|");
|
||||||
|
if sides.clone().count() != 2 {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let left = sides.next();
|
||||||
|
let right = sides.next();
|
||||||
|
if !(left.is_some() && right.is_some()) {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let left: String = left.unwrap().into();
|
||||||
|
let right: String = right.unwrap().into();
|
||||||
|
let mut left_ax = left.split_whitespace();
|
||||||
|
let mut right_ax = right.split_whitespace();
|
||||||
|
if left_ax.clone().count() != 3 || right_ax.clone().count() != 3 {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
let loc_x = left_ax.next();
|
||||||
|
if loc_x.is_none() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let loc_x = loc_x.unwrap().parse::<f32>();
|
||||||
|
if loc_x.is_err() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let loc_x = loc_x.unwrap();
|
||||||
|
|
||||||
|
let loc_y = left_ax.next();
|
||||||
|
if loc_y.is_none() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let loc_y = loc_y.unwrap().parse::<f32>();
|
||||||
|
if loc_y.is_err() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let loc_y = loc_y.unwrap();
|
||||||
|
|
||||||
|
let loc_z = left_ax.next();
|
||||||
|
if loc_z.is_none() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let loc_z = loc_z.unwrap().parse::<f32>();
|
||||||
|
if loc_z.is_err() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let loc_z = loc_z.unwrap();
|
||||||
|
let loc = Vec3::new(loc_x, loc_y, loc_z);
|
||||||
|
|
||||||
|
let col_r = right_ax.next();
|
||||||
|
if col_r.is_none() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let col_r = col_r.unwrap();
|
||||||
|
let col_r = col_r.parse::<f32>();
|
||||||
|
if col_r.is_err() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let col_r = col_r.unwrap();
|
||||||
|
|
||||||
|
let col_g = right_ax.next();
|
||||||
|
if col_g.is_none() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let col_g = col_g.unwrap();
|
||||||
|
let col_g = col_g.parse::<f32>();
|
||||||
|
if col_g.is_err() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let col_g = col_g.unwrap();
|
||||||
|
|
||||||
|
let col_b = right_ax.next();
|
||||||
|
if col_b.is_none() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let col_b = col_b.unwrap();
|
||||||
|
let col_b = col_b.parse::<f32>();
|
||||||
|
if col_b.is_err() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid(line));
|
||||||
|
}
|
||||||
|
let col_b = col_b.unwrap();
|
||||||
|
|
||||||
|
let col = Color::rgb(col_r, col_g, col_b);
|
||||||
|
if oct.is_none() {
|
||||||
|
oct = Some(OctTree::new(loc, col));
|
||||||
|
} else {
|
||||||
|
let mut other = oct.unwrap();
|
||||||
|
other.set_voxel_at_location(loc, col);
|
||||||
|
oct = Some(other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if oct.is_none() {
|
||||||
|
return Err(OctLoadError::FileDataInvalid("".to_string()));
|
||||||
|
}
|
||||||
|
Ok(OctTreeAsset {
|
||||||
|
model: oct.unwrap(),
|
||||||
|
handle: None,
|
||||||
|
task: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn save(asset: &OctTreeAsset) -> Option<Vec<u8>> {
|
||||||
|
let mut data: String = "".into();
|
||||||
|
let voxels = asset.model.collect_voxels();
|
||||||
|
if voxels.len() == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
for (_, each_pos, each_val) in voxels {
|
||||||
|
let (x, y, z) = (each_pos.x, each_pos.y, each_pos.z);
|
||||||
|
let (r, g, b) = (each_val.r(), each_val.g(), each_val.b());
|
||||||
|
data = format!("{data}\n{x} {y} {z}|{r} {g} {b}");
|
||||||
|
}
|
||||||
|
Some(data.as_bytes().to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const VERSION1: &str = "version 1";
|
||||||
|
pub fn determine_version(line: Option<&str>) -> MeshFileVersions {
|
||||||
|
if line.is_some() {
|
||||||
|
let line: String = line.unwrap().into();
|
||||||
|
if line.to_lowercase() == String::from(VERSION1) {
|
||||||
|
return MeshFileVersions::Version1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MeshFileVersions::Error
|
||||||
|
}
|
||||||
|
pub fn version_header(version: MeshFileVersions) -> Option<Vec<u8>> {
|
||||||
|
match version {
|
||||||
|
MeshFileVersions::Error => None,
|
||||||
|
MeshFileVersions::Version1 => Some((VERSION1.to_string()).as_bytes().to_vec()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AssetLoader for OctLoader {
|
impl AssetLoader for OctLoader {
|
||||||
type Asset = OctTreeAsset;
|
type Asset = OctTreeAsset;
|
||||||
type Settings = ();
|
type Settings = ();
|
||||||
|
@ -59,119 +282,14 @@ impl AssetLoader for OctLoader {
|
||||||
_load_context: &'a mut bevy::asset::LoadContext,
|
_load_context: &'a mut bevy::asset::LoadContext,
|
||||||
) -> bevy::utils::BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
|
) -> bevy::utils::BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut bytes = Vec::<u8>::new();
|
let mut bytes: Vec<u8> = Vec::<u8>::new();
|
||||||
let result = reader.read_to_end(&mut bytes).await;
|
let result = reader.read_to_end(&mut bytes).await;
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
return Err(OctLoadError::FileNotFound("".to_string()));
|
return Err(OctLoadError::FileNotFound(
|
||||||
|
"could not read_to_end the bytes".to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
let data_str = std::str::from_utf8(bytes.as_slice());
|
return serialization::meshes::load_detect_version(bytes);
|
||||||
if data_str.is_err() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid("".to_string()));
|
|
||||||
}
|
|
||||||
let data: String = data_str.unwrap().into();
|
|
||||||
let lines = data.lines();
|
|
||||||
let mut oct: Option<OctTree<Color>> = None;
|
|
||||||
for line in lines {
|
|
||||||
//
|
|
||||||
let line: String = line.into();
|
|
||||||
let mut sides = line.split("|");
|
|
||||||
if sides.clone().count() != 2 {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let left = sides.next();
|
|
||||||
let right = sides.next();
|
|
||||||
if !(left.is_some() && right.is_some()) {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let left: String = left.unwrap().into();
|
|
||||||
let right: String = right.unwrap().into();
|
|
||||||
let mut left_ax = left.split_whitespace();
|
|
||||||
let mut right_ax = right.split_whitespace();
|
|
||||||
if left_ax.clone().count() != 3 || right_ax.clone().count() != 3 {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
|
|
||||||
let loc_x = left_ax.next();
|
|
||||||
if loc_x.is_none() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let loc_x = loc_x.unwrap().parse::<f32>();
|
|
||||||
if loc_x.is_err() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let loc_x = loc_x.unwrap();
|
|
||||||
|
|
||||||
let loc_y = left_ax.next();
|
|
||||||
if loc_y.is_none() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let loc_y = loc_y.unwrap().parse::<f32>();
|
|
||||||
if loc_y.is_err() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let loc_y = loc_y.unwrap();
|
|
||||||
|
|
||||||
let loc_z = left_ax.next();
|
|
||||||
if loc_z.is_none() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let loc_z = loc_z.unwrap().parse::<f32>();
|
|
||||||
if loc_z.is_err() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let loc_z = loc_z.unwrap();
|
|
||||||
let loc = Vec3::new(loc_x, loc_y, loc_z);
|
|
||||||
|
|
||||||
let col_r = right_ax.next();
|
|
||||||
if col_r.is_none() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let col_r = col_r.unwrap();
|
|
||||||
let col_r = col_r.parse::<f32>();
|
|
||||||
if col_r.is_err() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let col_r = col_r.unwrap();
|
|
||||||
|
|
||||||
let col_g = right_ax.next();
|
|
||||||
if col_g.is_none() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let col_g = col_g.unwrap();
|
|
||||||
let col_g = col_g.parse::<f32>();
|
|
||||||
if col_g.is_err() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let col_g = col_g.unwrap();
|
|
||||||
|
|
||||||
let col_b = right_ax.next();
|
|
||||||
if col_b.is_none() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let col_b = col_b.unwrap();
|
|
||||||
let col_b = col_b.parse::<f32>();
|
|
||||||
if col_b.is_err() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid(line));
|
|
||||||
}
|
|
||||||
let col_b = col_b.unwrap();
|
|
||||||
|
|
||||||
let col = Color::rgb(col_r, col_g, col_b);
|
|
||||||
if oct.is_none() {
|
|
||||||
oct = Some(OctTree::new(loc, col));
|
|
||||||
} else {
|
|
||||||
let mut other = oct.unwrap();
|
|
||||||
other.set_voxel_at_location(loc, col);
|
|
||||||
oct = Some(other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if oct.is_none() {
|
|
||||||
return Err(OctLoadError::FileDataInvalid("".to_string()));
|
|
||||||
}
|
|
||||||
Ok(OctTreeAsset {
|
|
||||||
model: oct.unwrap(),
|
|
||||||
handle: None,
|
|
||||||
task: None,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue