basic proof of concept for saving structure stringtrees to file. complete (janky) stringtree editor proof.

This commit is contained in:
Lillian Vixe 2024-05-21 14:59:13 -07:00
parent 8c7fba2a04
commit 4123df7072
5 changed files with 202 additions and 28 deletions

View file

@ -9,7 +9,8 @@ use bevy::{
math::Vec3,
prelude::default,
render::{camera::Camera, color::Color},
transform::components::GlobalTransform,
transform::components::{GlobalTransform, Transform},
utils::hashbrown::HashMap,
window::{PrimaryWindow, Window},
};
use bevy_egui::{
@ -20,8 +21,11 @@ use bevy_egui::{
use crate::{
vvedit::s_editor_bevy_input_shim::keybind_codes::REMOVE_VOXEL,
vvlib::{
s_inputs::InputRegister, s_intersections::octtree_ray_overlap, s_obb::OBB,
s_inputs::InputRegister,
s_intersections::octtree_ray_overlap,
s_obb::OBB,
s_oct_asset::OctAssetMarker,
s_structure_asset::{self, StructureAsset},
},
};
use crate::{
@ -69,7 +73,8 @@ impl Default for EditWindowUIState {
pub enum PopupWindowMode {
#[default]
None,
EditStructureElement(String, String), //rename from and to, otherwise second string is unused
EditStructureElement(String, String), // rename from and to, otherwise second string is unused
CreateStructureElement(String, String), // node name, parent node
}
impl PopupWindowMode {
pub fn is_none(&self) -> bool {
@ -205,6 +210,14 @@ pub fn startup_system_edit_ui(
shared_ui_state
.structure
.add(&String::from("c2"), Some(&String::from("c1")));
let asset_test = StructureAsset {
layout: shared_ui_state.structure.clone(),
default_pose_positions: HashMap::<String, Transform>::new(),
};
s_structure_asset::serialization::structures::write_latest_version(
&"test.vvs".to_string(),
&asset_test,
);
}
fn update_ui_scale_factor(
mut egui_settings: ResMut<EguiSettings>,
@ -498,8 +511,15 @@ pub fn edit_window_ui(
});
ui.collapsing("Model Structure", |ui| {
ui.vertical(|ui| {
if ui.button("add element").clicked() {
shared_ui_state.popup = PopupWindowMode::CreateStructureElement(
"unnamed".to_owned(),
"".to_owned(),
);
}
ui.separator();
if shared_ui_state.selected_structure_element != "" {
if ui.button("...").clicked() {
if ui.button("Edit Structure of Node").clicked() {
let selection = shared_ui_state.selected_structure_element.clone();
let mut should_close = false;
if let PopupWindowMode::EditStructureElement(val, _) =

View file

@ -20,6 +20,20 @@ impl StringTreeElement {
pub fn new(key: &String) -> StringTreeElement {
StringTreeElement::Element(key.clone(), Box::new(Vec::new()))
}
pub fn children(&self) -> Option<Vec<String>> {
match self {
StringTreeElement::Element(_, children) => {
let mut result = Vec::new();
for each in children.as_ref() {
if let Some(name) = each.name_of() {
result.push(name.clone());
}
}
return Some(result);
}
_ => None,
}
}
pub fn contains_down_branch(&self, search: &String) -> bool {
self.down_branch(search)
}
@ -38,6 +52,7 @@ impl StringTreeElement {
false
}
}
#[derive(Default, Debug, Clone)]
pub struct StringTree {
pub root: Vec<StringTreeElement>,
@ -54,6 +69,37 @@ impl StringTree {
}
false
}
pub fn children_of(&self, subject: &String) -> Option<Vec<String>> {
for each in &self.root {
let res = Self::check_children_of(each, subject);
if res.is_some() {
return res;
}
}
None
}
fn check_children_of(element: &StringTreeElement, subject: &String) -> Option<Vec<String>> {
if let StringTreeElement::Element(name, children) = element {
if name == subject {
// return children's names
let mut result = Vec::new();
for each in children.as_ref() {
if let StringTreeElement::Element(name, _) = each {
result.push(name.clone());
}
}
return Some(result);
} else {
for each in children.as_ref() {
let res = Self::check_children_of(each, subject);
if res.is_some() {
return res;
}
}
}
}
None
}
fn recursive_search_rename(
element: &mut StringTreeElement,
subject: &String,

View file

@ -1,3 +1,5 @@
use std::mem;
use bevy::render::color::Color;
use bevy_egui::{
egui::{
@ -8,7 +10,7 @@ use bevy_egui::{
EguiContexts,
};
use crate::vvlib::s_identifier_validation;
use crate::vvlib::s_identifier_validation::{self, is_valid};
use super::{
s_editor_ui::{EditWindowUIState, PopupWindowMode},
@ -60,9 +62,15 @@ pub fn round_trip_color() {
}
pub fn show_popup(state: &mut EditWindowUIState, contexts: &mut EguiContexts) {
enum PopupStateChange {
None,
RemoveStructureElement(bool), //keep children(reparenting upward) or drop
}
let mut state_change = PopupStateChange::None;
let mut change_state = None;
let mut reparent = None;
let mut rename_complete = false;
let mut added_valid_name = false;
match &mut state.popup {
PopupWindowMode::None => return,
PopupWindowMode::EditStructureElement(name, rename) => {
@ -86,6 +94,14 @@ pub fn show_popup(state: &mut EditWindowUIState, contexts: &mut EguiContexts) {
rename_complete = true;
}
});
ui.horizontal(|ui| {
if ui.button("remove and reparent to parent").clicked() {
state_change = PopupStateChange::RemoveStructureElement(true);
}
if ui.button("remove and drop children").clicked() {
state_change = PopupStateChange::RemoveStructureElement(false);
}
});
enum Tasks {
MoveElement,
RemoveElement,
@ -119,6 +135,33 @@ pub fn show_popup(state: &mut EditWindowUIState, contexts: &mut EguiContexts) {
}
});
}
PopupWindowMode::CreateStructureElement(name, parent) => {
//
let response = egui::Window::new("new structure element ".to_owned() + &name.clone())
.id(state.egui.popup_identifier.unwrap());
response.show(contexts.ctx_mut(), |ui| {
ui.label("please name the new structure node.");
let mut newname = name.clone();
ui.text_edit_singleline(&mut newname);
if is_valid(newname.clone()) && !state.structure.is_present(&newname) {
*name = newname;
}
if ui.button("Confirm and add.").clicked() {
added_valid_name = true;
}
ui.collapsing(
"parent status: ".to_owned() + parent.clone().as_str(),
|ui| {
ui.vertical(|ui| {
ui.radio_value(parent, "".into(), "Root level - no parent");
for each in state.structure.list_of_elements() {
ui.radio_value(parent, each.clone(), each.clone());
}
});
},
);
});
}
}
if change_state.is_some() {
state.popup = change_state.unwrap();
@ -140,6 +183,29 @@ pub fn show_popup(state: &mut EditWindowUIState, contexts: &mut EguiContexts) {
}
state.popup = PopupWindowMode::None;
}
if added_valid_name {
if let PopupWindowMode::CreateStructureElement(name, parent) = &state.popup {
let par = if parent == "" { None } else { Some(parent) };
state.structure.add(name, par);
} else {
return;
}
state.popup = PopupWindowMode::None;
}
match state_change {
PopupStateChange::None => {} // nothing to do if state does not change
PopupStateChange::RemoveStructureElement(keep_children) => {
let mut consumed = PopupWindowMode::None;
mem::swap(&mut state.popup, &mut consumed);
if let PopupWindowMode::EditStructureElement(subject, _) = consumed {
if keep_children {
state.structure.remove_element_preserve_branch(&subject);
} else {
state.structure.remove_branch(&subject);
}
}
}
}
}
pub fn show_stringtree_selector(ui: &mut egui::Ui, tree: &StringTree, selection: &mut String) {
@ -180,19 +246,6 @@ pub fn show_child(
ui.horizontal(|ui| {
ui.label(prec);
ui.radio_value(selection, name.clone(), name.clone());
/* if ui.button("...").clicked() {
let mut should_close = false;
if let PopupWindowMode::EditStructureElement(val, _) = state {
if val == name {
should_close = true;
}
}
if should_close {
*state = PopupWindowMode::None;
} else {
*state = PopupWindowMode::EditStructureElement(name.clone(), name.clone())
}
} */
});
for each in children.as_ref() {
show_child(ui, each, depth + 1, selection);

View file

@ -80,10 +80,10 @@ pub mod serialization {
}
pub fn write_latest_version(path: &String, asset: &OctTreeAsset) -> bool {
write_any_version(path, asset, Default::default())
write_by_version(path, asset, Default::default())
}
pub fn write_any_version(
pub fn write_by_version(
path: &String,
asset: &OctTreeAsset,
version: MeshFileVersions,

View file

@ -11,8 +11,8 @@ use thiserror::Error;
#[derive(Asset, TypePath)]
pub struct StructureAsset {
layout: StringTree,
positions: HashMap<String, Transform>,
pub layout: StringTree,
pub default_pose_positions: HashMap<String, Transform>,
}
#[derive(Default)]
@ -31,7 +31,7 @@ pub enum StructureLoadError {
pub mod serialization {
pub mod meshes {
pub mod structures {
use std::path::Path;
use bevy::{
@ -46,10 +46,10 @@ pub mod serialization {
}
pub fn write_latest_version(path: &String, asset: &StructureAsset) -> bool {
write_any_version(path, asset, Default::default())
write_by_version(path, asset, Default::default())
}
pub fn write_any_version(
pub fn write_by_version(
path: &String,
asset: &StructureAsset,
version: StructureVersions,
@ -94,14 +94,69 @@ pub mod serialization {
pub mod version_1 {
use crate::vvedit::s_string_tree::StringTree;
use super::super::super::super::{StructureAsset, StructureLoadError};
pub fn load(
_data: std::str::Lines<'_>,
) -> Result<StructureAsset, StructureLoadError> {
Err(StructureLoadError::FileDataInvalid("".to_string()))
}
pub fn save(_asset: &StructureAsset) -> Option<Vec<u8>> {
None
pub fn save(asset: &StructureAsset) -> Option<Vec<u8>> {
// FILE STRUCTURE:
// FIRST, STRING TREE
// STRING TREE IS STORED AS LISTS OF STRINGS - FIRST STRING IS PARENT
// EACH LINE DEFINES ONE NODE TO ITS CHILDREN
// FIRST LINE IS ROOT LEVEL NODES AFTER THAT EACH CHILD NODE OF ROOT NODES IN ORDER
// PAUSE BETWEEN STRING TREE AND POSITIONS
// LIST OF NODE IDS BY STRING NAME, FOLLOWED BY TRANSFORM DATA, FOLLOWED BY NONSTANDARD DATA (MESH BEING RENDERED AT THIS TRANSFORM)
let mut data: String = "".into();
let mut is_first = true;
data += "\n";
let mut to_process = Vec::<String>::new();
for each in &asset.layout.root {
if let Some(name) = each.name_of() {
//
if !is_first {
data += "|";
} else {
is_first = false;
}
data += name.clone().as_str();
} else {
return None;
}
if let Some(children) = each.children() {
for each in children {
to_process.push(each);
}
}
}
fn save_child(element: &String, tree: &StringTree, data: &mut String) {
if let Some(children) = tree.children_of(element) {
if children.is_empty() {
return;
}
*data += "\n";
*data += element.as_str();
for each in children {
*data += "|";
*data += each.as_str();
}
}
}
while !to_process.is_empty() {
if let Some(next) = to_process.pop() {
save_child(&next, &asset.layout, &mut data);
if let Some(children) = asset.layout.children_of(&next) {
for each in children {
to_process.push(each);
}
}
}
}
Some(data.as_bytes().to_vec())
}
}
}
@ -143,7 +198,7 @@ impl AssetLoader for StructureLoader {
"could not read_to_end the bytes".to_string(),
));
}
return serialization::meshes::load_detect_version(bytes);
return serialization::structures::load_detect_version(bytes);
})
}
}