working add-new-assets-at-runtime

NOT WORKING: selection swapper / seeing loaded asset in editor after creating or after loading.
This commit is contained in:
Lillian Vixe 2024-06-16 13:21:38 -07:00
parent 20f554b8d5
commit 41b673753e
5 changed files with 174 additions and 64 deletions

View file

@ -1,8 +1,5 @@
version 1 version 1
test
torso|head|waist|frontleftpaw|frontrightpaw
head|leftear|rightear
waist|tail|backleftpaw|backrightpaw
|| ||
element|torso element|torso
position|0|0|0 position|0|0|0
@ -12,5 +9,13 @@ element|head
position|0|0|0 position|0|0|0
scale|1|1|1 scale|1|1|1
rotation|0|0|0|1 rotation|0|0|0|1
element|test
position|0|0|0
scale|1|1|1
rotation|0|0|0|1
element|
position|30|0|0
scale|1|1|1
rotation|0|0|0|1
|| ||
head|grid|test.vvg head|grid|test.vvg

View file

@ -246,16 +246,19 @@ pub fn startup_system_edit_ui(
mut event_writer: EventWriter<StructureUpdateMessage>, mut event_writer: EventWriter<StructureUpdateMessage>,
) { ) {
spawn_camera(&mut commands); spawn_camera(&mut commands);
/*
let str = "test.vvs"; let str = "test.vvs";
let handle = asset_server.load::<StructureAsset>(str); let handle = asset_server.load::<StructureAsset>(str);
*/
//let id = OctTreeAsset::load(&mut pairs, asset_server.into(), mesh_assets, "test.vvg".into());
let display_parent = commands let display_parent = commands
.spawn(TransformBundle { .spawn(SpatialBundle {
local: Transform::from_scale(Vec3::splat(1f32)), visibility: Visibility::Visible,
transform: Transform::from_scale(Vec3::splat(0.25f32)),
..Default::default() ..Default::default()
}) })
.id(); .id();
shared_ui_state.display_root = display_parent;
/*
let id = commands.spawn((SpatialBundle { let id = commands.spawn((SpatialBundle {
visibility: Visibility::Visible, visibility: Visibility::Visible,
..Default::default() ..Default::default()
@ -263,7 +266,6 @@ pub fn startup_system_edit_ui(
asset: handle.clone(), asset: handle.clone(),
nodes: Default::default(), nodes: Default::default(),
})).id(); })).id();
shared_ui_state.display_root = display_parent;
let data = StructureEditData { let data = StructureEditData {
path: str.into(), path: str.into(),
id: handle.clone(), id: handle.clone(),
@ -276,6 +278,7 @@ pub fn startup_system_edit_ui(
update_type: StructureUpdateType::Rebuild, update_type: StructureUpdateType::Rebuild,
entity: id, entity: id,
}); });
*/
let light_transform = Transform::from_xyz(4f32, 4f32, 4f32).looking_at(Vec3::ZERO, Vec3::Y); let light_transform = Transform::from_xyz(4f32, 4f32, 4f32).looking_at(Vec3::ZERO, Vec3::Y);
commands.spawn(PointLightBundle { commands.spawn(PointLightBundle {
@ -448,7 +451,7 @@ pub fn edit_window_ui(
window: Query<&Window>, window: Query<&Window>,
mut asset_server: ResMut<AssetServer>, mut asset_server: ResMut<AssetServer>,
mut event_writer: EventWriter<StructureUpdateMessage>, mut event_writer: EventWriter<StructureUpdateMessage>,
//commands: Commands, mut commands: Commands,
) { ) {
shared_ui_state.egui.windows.clear(); shared_ui_state.egui.windows.clear();
let title = String::from("VVEdit"); let title = String::from("VVEdit");
@ -601,17 +604,19 @@ pub fn edit_window_ui(
shared_ui_state.selection = Selection::None; shared_ui_state.selection = Selection::None;
} }
} }
let mut selection: Selection = shared_ui_state.selection.clone();
ui.collapsing("grid assets", |ui| { ui.collapsing("grid assets", |ui| {
if !shared_ui_state.selection.is_none() { if !shared_ui_state.selection.is_none() {
if ui.button("Deselect Asset").clicked() { if ui.button("Deselect Asset").clicked() {
shared_ui_state.selection = Selection::None; shared_ui_state.selection = Selection::None;
} }
} }
for (id, _asset) in (&grid_assets).iter() { for grid in &shared_ui_state.grids {
let path = asset_server.get_path(id.untyped()); ui.radio_value(
if let Some(path) = path { &mut selection,
ui.label(path.to_string().as_str()); Selection::Grid(grid.path.to_string()),
} grid.path.to_string().as_str(),
);
} }
}); });
ui.collapsing("structure assets", |ui| { ui.collapsing("structure assets", |ui| {
@ -620,19 +625,17 @@ pub fn edit_window_ui(
shared_ui_state.selection = Selection::None; shared_ui_state.selection = Selection::None;
} }
} }
for structure in &shared_ui_state.structures {
for (id, _asset) in (&structure_assets).iter() {
let path = asset_server.get_path(id.untyped());
if let Some(path) = path {
//ui.label(path.to_string().as_str());
ui.radio_value( ui.radio_value(
&mut shared_ui_state.selection, &mut selection,
Selection::Structure(path.to_string()), Selection::Grid(structure.path.to_string()),
path.to_string().as_str(), structure.path.to_string().as_str(),
); );
} }
}
}); });
if selection != shared_ui_state.selection {
shared_ui_state.selection = selection;
}
let mut selected_structure = false; let mut selected_structure = false;
let mut selection = Option::<StringTree>::None; let mut selection = Option::<StringTree>::None;
if let Selection::Structure(select) = &shared_ui_state.selection { if let Selection::Structure(select) = &shared_ui_state.selection {
@ -812,6 +815,6 @@ pub fn edit_window_ui(
&mut asset_server, &mut asset_server,
&mut structure_assets, &mut structure_assets,
&mut event_writer, &mut event_writer,
//&mut commands &mut commands
); );
} }

View file

@ -1,10 +1,16 @@
use std::{fs, io, path::Path}; use std::{
fs, io,
path::{Path, PathBuf},
sync::RwLock,
};
use bevy::{ use bevy::{
asset::{AssetServer, Assets}, asset::{AssetServer, Assets},
ecs::{entity::Entity, event::EventWriter, system::ResMut}, ecs::{entity::Entity, event::EventWriter, system::ResMut},
hierarchy::BuildChildren,
math::{Quat, Vec3}, math::{Quat, Vec3},
render::color::Color, prelude::{Commands, SpatialBundle},
render::{color::Color, view::Visibility},
utils::hashbrown::hash_map::Entry, utils::hashbrown::hash_map::Entry,
}; };
use bevy_egui::{ use bevy_egui::{
@ -22,7 +28,10 @@ use crate::{
vvlib::{ vvlib::{
s_identifier_validation::{self, is_valid}, s_identifier_validation::{self, is_valid},
s_oct_asset::OctTreeAsset, s_oct_asset::OctTreeAsset,
s_structure_asset::{StructureAsset, StructureUpdateMessage, StructureUpdateType}, s_octtree::OctTree,
s_structure_asset::{
StructureAsset, StructureComponent, StructureUpdateMessage, StructureUpdateType,
},
}, },
}; };
@ -81,7 +90,7 @@ pub fn show_popup(
asset_server: &mut ResMut<AssetServer>, asset_server: &mut ResMut<AssetServer>,
structure_assets: &mut ResMut<Assets<StructureAsset>>, structure_assets: &mut ResMut<Assets<StructureAsset>>,
event_writer: &mut EventWriter<StructureUpdateMessage>, event_writer: &mut EventWriter<StructureUpdateMessage>,
//commands: &mut Commands, commands: &mut Commands,
) { ) {
enum PopupStateChange { enum PopupStateChange {
None, None,
@ -89,7 +98,7 @@ pub fn show_popup(
RenameComplete, RenameComplete,
Reparent(String, String, String), //structure subject, name of element to reparent, new parent Reparent(String, String, String), //structure subject, name of element to reparent, new parent
Addition(String, String, String), //structure subject, new element name, new parent Addition(String, String, String), //structure subject, new element name, new parent
Cancel, Close,
} }
let mut state_change = PopupStateChange::None; let mut state_change = PopupStateChange::None;
//let mut added_valid_name = false; //let mut added_valid_name = false;
@ -184,7 +193,7 @@ pub fn show_popup(
}); });
// todo: rename action. edit subject action (mesh, light), remove action (with, without reparenting) // todo: rename action. edit subject action (mesh, light), remove action (with, without reparenting)
if ui.button("Cancel").clicked() { if ui.button("Cancel").clicked() {
state_change = PopupStateChange::Cancel; state_change = PopupStateChange::Close;
} }
}); });
} }
@ -302,21 +311,40 @@ pub fn show_popup(
Extension::Invalid => {} Extension::Invalid => {}
Extension::Structure => { Extension::Structure => {
let handle = asset_server.load::<StructureAsset>(path_pre.clone()); let handle = asset_server.load::<StructureAsset>(path_pre.clone());
let data = StructureEditData { let handleclone = handle.clone();
path: path_pre,
id: handle,
has_changed_since_last_save: false,
edit_entity: Entity::PLACEHOLDER, //BAD BAD BAD
};
let mut loaded_already = false; let mut loaded_already = false;
for each in &state.structures { for each in &state.structures {
if each.id == data.id { if each.id == handleclone {
loaded_already = true; loaded_already = true;
} }
} }
if !loaded_already { if !loaded_already {
let id = commands
.spawn((
SpatialBundle {
visibility: Visibility::Visible,
..Default::default()
},
StructureComponent {
asset: handleclone,
nodes: Default::default(),
},
))
.id();
let data = StructureEditData {
path: path_pre,
id: handle,
has_changed_since_last_save: false,
edit_entity: id.clone(),
};
state.structures.push(data); state.structures.push(data);
commands.entity(state.display_root).add_child(id);
event_writer.send(StructureUpdateMessage {
update_type: StructureUpdateType::Rebuild,
entity: id,
});
} }
state_change = PopupStateChange::Close;
} }
Extension::Grid => { Extension::Grid => {
let handle = asset_server.load::<OctTreeAsset>(path_pre.clone()); let handle = asset_server.load::<OctTreeAsset>(path_pre.clone());
@ -334,15 +362,16 @@ pub fn show_popup(
if !loaded_already { if !loaded_already {
state.grids.push(data); state.grids.push(data);
} }
state_change = PopupStateChange::Close;
} }
} }
} }
ui.label(""); ui.label("");
} else { } else {
ui.label(""); ui.label("X");
} }
if ui.button("close").clicked() { if ui.button("close").clicked() {
state_change = PopupStateChange::Cancel; state_change = PopupStateChange::Close;
} }
ui.separator(); ui.separator();
for each in files { for each in files {
@ -358,33 +387,115 @@ pub fn show_popup(
// selector for type of asset // selector for type of asset
// name (selector ensures its unique before allowing creation) // name (selector ensures its unique before allowing creation)
// upon adding, in V state_change, submit to the selective visibility tech the entities spawned // upon adding, in V state_change, submit to the selective visibility tech the entities spawned
let mut can_commit = false; let mut valid_identifier = false;
let ui = let ui =
egui::Window::new("new asset".to_owned()).id(state.egui.popup_identifier.unwrap()); egui::Window::new("new asset".to_owned()).id(state.egui.popup_identifier.unwrap());
ui.show(contexts.ctx_mut(), |ui| { ui.show(contexts.ctx_mut(), |ui| {
ui.vertical(|ui| { ui.vertical(|ui| {
ui.text_edit_singleline(current_name);
// ENSURING UNIQUENESS: Must not already exist in assets folder or loaded assets. // ENSURING UNIQUENESS: Must not already exist in assets folder or loaded assets.
fn as_final_path(input: &String, is_structure_or_grid: &bool) -> String {
let extension = if *is_structure_or_grid {
".vvs"
} else {
".vvg"
};
"assets/".to_string() + input.as_str() + extension
}
if is_valid(current_name.clone()) {
let as_asset_path = as_final_path(&current_name, is_structure_or_grid);
valid_identifier = !Path::new(&as_asset_path).exists();
}
// MUST BE A VALID IDENTIFIER // MUST BE A VALID IDENTIFIER
ui.label( ui.label(
"identifiers cannot contain the pipe character \"|\", \ "identifiers cannot contain the pipe character \"|\", \
whitespace (spaces, newlines, tabs), or the comma character \",\". \ whitespace (spaces, newlines, tabs), or the comma character \",\". \
slashes \"/\" will create folders automatically if needed.", use of slashes \"/\" will create folders automatically if needed.",
); );
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.radio_value(is_structure_or_grid, true, "Structure"); ui.radio_value(is_structure_or_grid, true, "Structure");
ui.radio_value(is_structure_or_grid, false, "Grid"); ui.radio_value(is_structure_or_grid, false, "Grid");
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
if can_commit { if valid_identifier {
ui.label(""); ui.label("");
} else { } else {
ui.label(""); ui.label("X");
} }
if ui.button("Cancel").clicked() { if ui.button("Cancel").clicked() {
state_change = PopupStateChange::Cancel; state_change = PopupStateChange::Close;
} }
if ui.add_enabled(can_commit, Button::new("create")).clicked() { if ui
.add_enabled(valid_identifier, Button::new("create"))
.clicked()
{
//commit changes //commit changes
//first check if we need to make the directory
// TODO: move to when you click Save All Changes so directory doesnt exist if they scrap the idea/close the app immediately after creating
let mut final_path = as_final_path(&current_name, is_structure_or_grid);
let full_path = PathBuf::from(final_path.clone());
let dir_path = full_path.parent();
if let Some(dir_path) = dir_path {
if !dir_path.is_dir() {
let result = fs::create_dir_all(dir_path);
if result.is_err() {
println!("ah shit");
} else {
println!("success!");
}
}
}
final_path = final_path.replace("assets/", "");
if *is_structure_or_grid {
// add structure
let handle = asset_server.add::<StructureAsset>(Default::default());
let handleclone = handle.clone();
let data = StructureEditData {
path: final_path,
id: handle,
has_changed_since_last_save: false,
edit_entity: Entity::PLACEHOLDER, //BAD BAD BAD
};
state.structures.push(data);
state_change = PopupStateChange::Close;
let id = commands
.spawn((
SpatialBundle {
visibility: Visibility::Visible,
..Default::default()
},
StructureComponent {
asset: handleclone,
nodes: Default::default(),
},
))
.id();
commands.entity(state.display_root).add_child(id);
event_writer.send(StructureUpdateMessage {
update_type: StructureUpdateType::Rebuild,
entity: id,
});
} else {
// add grid
let handle = asset_server.add::<OctTreeAsset>(OctTreeAsset {
model: RwLock::new(OctTree::<Color>::new(
Vec3::ZERO,
Color::RED,
)),
handle: None,
task: None,
});
let data = GridEditData {
path: final_path,
id: handle,
has_changed_since_last_save: true,
};
state.grids.push(data);
state_change = PopupStateChange::Close;
// SPAWN ENTITY?
}
} }
}); });
}); });
@ -423,7 +534,7 @@ pub fn show_popup(
state.popup = PopupWindowMode::None; state.popup = PopupWindowMode::None;
} }
} }
PopupStateChange::Cancel => { PopupStateChange::Close => {
state.popup = PopupWindowMode::None; state.popup = PopupWindowMode::None;
} }
PopupStateChange::RenameComplete => { PopupStateChange::RenameComplete => {

View file

@ -4,5 +4,6 @@ no meshes loaded
load a structure: display structure is updated. load all meshes in the structure. load a structure: display structure is updated. load all meshes in the structure.
load a mesh: add a mesh node in the structure at root with name of mesh load a mesh: add a mesh node in the structure at root with name of mesh
~~BONUS: want a discovery-list-display of all known assets - scan assets folder~~ DONE:
~~BONUS: Keep track of if structure or meshes have changed, when they changed offer to save if loading a new structure or closing program (meshes will stay loaded unchanged until close, loading a new structure erases current structure (temp?))~~ want a discovery-list-display of all known assets - scan assets folder
Keep track of if structure or meshes have changed, when they changed offer to save if loading a new structure or closing program (meshes will stay loaded unchanged until close, loading a new structure erases current structure (temp?))

View file

@ -25,7 +25,7 @@ use thiserror::Error;
use super::s_oct_asset::{MeshingOctTreePairs, OctAssetMarker, OctTreeAsset}; use super::s_oct_asset::{MeshingOctTreePairs, OctAssetMarker, OctTreeAsset};
#[derive(Asset, TypePath, Debug)] #[derive(Asset, TypePath, Debug, Default)]
pub struct StructureAsset { pub struct StructureAsset {
pub layout: StringTree, pub layout: StringTree,
pub default_pose_positions: HashMap<String, Transform>, pub default_pose_positions: HashMap<String, Transform>,
@ -675,26 +675,16 @@ pub mod serialization {
// v STRING TREE (tree that defines shape/structure of it) // v STRING TREE (tree that defines shape/structure of it)
{ {
//let mut is_first = true; //let mut is_first = true;
data += NEWLINE;
let mut to_process = Vec::<String>::new(); let mut to_process = Vec::<String>::new();
for each in &asset.layout.root { for each in &asset.layout.root {
to_process.push(each.name_of().unwrap().clone()); to_process.push(each.name_of().unwrap().clone());
/*if let Some(name) = each.name_of() {
//
if !is_first {
data += "|";
} else {
is_first = false;
}
data += name.clone().as_str();
} else {
return None;
}*/
} }
// v HELPER FUNCTION // v HELPER FUNCTION
fn save_child(element: &String, tree: &StringTree, data: &mut String) { fn save_child(element: &String, tree: &StringTree, data: &mut String) {
if let Some(children) = tree.children_of(element) { if let Some(children) = tree.children_of(element) {
if children.is_empty() { if children.is_empty() {
*data += NEWLINE;
*data += element.as_str();
return; return;
} }
*data += NEWLINE; *data += NEWLINE;