6/6 load/save functions (untested)
TODO: UI rework to allow loading arbitrary files and saving them. described in what its like to load a file.md
This commit is contained in:
parent
1bb0987120
commit
51805dcbcb
|
@ -102,8 +102,37 @@ pub mod serialization {
|
||||||
|
|
||||||
use super::super::{StructureAsset, StructureLoadError};
|
use super::super::{StructureAsset, StructureLoadError};
|
||||||
|
|
||||||
pub fn load_detect_version(_bytes: Vec<u8>) -> Result<StructureAsset, StructureLoadError> {
|
pub fn load_detect_version(bytes: Vec<u8>) -> Result<StructureAsset, StructureLoadError> {
|
||||||
return Err(StructureLoadError::FileDataInvalid("".to_string()));
|
const NEWLINE: &str = "\n";
|
||||||
|
let data_str = std::str::from_utf8(bytes.as_slice());
|
||||||
|
if data_str.is_err() {
|
||||||
|
return Err(StructureLoadError::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 = determine_version(first);
|
||||||
|
let mut the_rest = "".to_string();
|
||||||
|
let mut first = true;
|
||||||
|
for each in iter {
|
||||||
|
if first {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
the_rest += NEWLINE;
|
||||||
|
}
|
||||||
|
the_rest += each;
|
||||||
|
}
|
||||||
|
match version {
|
||||||
|
StructureVersions::Version1 => {
|
||||||
|
return versions::version_1::load(the_rest);
|
||||||
|
}
|
||||||
|
StructureVersions::Error => {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"invalid version data".into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_latest_version(path: &String, asset: &StructureAsset) -> bool {
|
pub fn write_latest_version(path: &String, asset: &StructureAsset) -> bool {
|
||||||
|
@ -155,14 +184,276 @@ pub mod serialization {
|
||||||
|
|
||||||
pub mod version_1 {
|
pub mod version_1 {
|
||||||
|
|
||||||
use bevy::transform::components::Transform;
|
use bevy::{
|
||||||
|
math::{Quat, Vec3},
|
||||||
|
transform::components::Transform,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::vvedit::s_string_tree::StringTree;
|
use crate::{
|
||||||
|
vvedit::s_string_tree::StringTree, vvlib::s_structure_asset::ElementData,
|
||||||
|
};
|
||||||
|
|
||||||
use super::super::super::super::{StructureAsset, StructureLoadError};
|
use super::super::super::super::{StructureAsset, StructureLoadError};
|
||||||
pub fn load(
|
pub fn load(data: String) -> Result<StructureAsset, StructureLoadError> {
|
||||||
_data: std::str::Lines<'_>,
|
let mut result = StructureAsset {
|
||||||
) -> Result<StructureAsset, StructureLoadError> {
|
layout: StringTree::new(),
|
||||||
|
default_pose_positions: Default::default(),
|
||||||
|
element_data: Default::default(),
|
||||||
|
};
|
||||||
|
let mut sides = data.split("||");
|
||||||
|
let string_tree_data = sides.next();
|
||||||
|
let node_transforms = sides.next();
|
||||||
|
let node_attachments = sides.next();
|
||||||
|
// v STRING TREE (tree that defines shape/structure of it)
|
||||||
|
{
|
||||||
|
if string_tree_data.is_none() {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: lack of string tree data".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let lines = string_tree_data.unwrap().lines();
|
||||||
|
for each in lines {
|
||||||
|
let mut tokens = each.split("|");
|
||||||
|
let parent = tokens.next();
|
||||||
|
if parent.is_none() {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error in string tree data: parent not present".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let parent = parent.unwrap().trim().to_string();
|
||||||
|
if !result.layout.is_present(&parent) {
|
||||||
|
result.layout.add_root_level(&parent);
|
||||||
|
}
|
||||||
|
while let Some(each) = tokens.next() {
|
||||||
|
let each = each.trim().to_string();
|
||||||
|
result.layout.add_child(&each, &parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ^ STRING TREE (tree that defines shape/structure of it)
|
||||||
|
// v NODE ELEMENTS BY STRING NAME, FOLLOWED BY TRANSFORM DATA (optional)
|
||||||
|
'node_transforms: {
|
||||||
|
if node_transforms.is_none() {
|
||||||
|
break 'node_transforms;
|
||||||
|
}
|
||||||
|
let mut node_transforms =
|
||||||
|
node_transforms.unwrap().trim().lines().peekable();
|
||||||
|
let mut name = Option::<String>::None;
|
||||||
|
let mut position = Option::<Vec3>::None;
|
||||||
|
let mut scale = Option::<Vec3>::None;
|
||||||
|
let mut rotation = Option::<Quat>::None;
|
||||||
|
fn commit_node_transform(
|
||||||
|
asset: &mut StructureAsset,
|
||||||
|
name: &Option<String>,
|
||||||
|
position: &Option<Vec3>,
|
||||||
|
scale: &Option<Vec3>,
|
||||||
|
rotation: &Option<Quat>,
|
||||||
|
) {
|
||||||
|
let pos = if position.is_some() {
|
||||||
|
position.clone().unwrap()
|
||||||
|
} else {
|
||||||
|
Vec3::ZERO
|
||||||
|
};
|
||||||
|
let sca = if scale.is_some() {
|
||||||
|
scale.clone().unwrap()
|
||||||
|
} else {
|
||||||
|
Vec3::ONE
|
||||||
|
};
|
||||||
|
let rot = if rotation.is_some() {
|
||||||
|
rotation.clone().unwrap()
|
||||||
|
} else {
|
||||||
|
Quat::IDENTITY
|
||||||
|
};
|
||||||
|
let str = name.clone().unwrap();
|
||||||
|
asset.default_pose_positions.insert(
|
||||||
|
str,
|
||||||
|
Transform::with_translation(Transform::IDENTITY, pos)
|
||||||
|
.with_rotation(rot)
|
||||||
|
.with_scale(sca),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
'readloop: while node_transforms.peek().is_some() {
|
||||||
|
//load in node transforms
|
||||||
|
let next_line = node_transforms.next();
|
||||||
|
if next_line.is_none() {
|
||||||
|
break 'readloop;
|
||||||
|
}
|
||||||
|
let mut tokens = next_line.unwrap().split("|");
|
||||||
|
let identifier = tokens.next().unwrap().trim().to_lowercase();
|
||||||
|
let identifier = identifier.as_str();
|
||||||
|
match identifier {
|
||||||
|
//v indicates we are getting the name to be created. should be first, before data relevant to said element
|
||||||
|
"element" => {
|
||||||
|
let next_name = tokens.next();
|
||||||
|
if next_name.is_none() {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: lack of name after name token".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let next_name = next_name.unwrap().trim().to_lowercase();
|
||||||
|
if name.is_some()
|
||||||
|
& (position.is_some()
|
||||||
|
| scale.is_some()
|
||||||
|
| rotation.is_some())
|
||||||
|
{
|
||||||
|
commit_node_transform(
|
||||||
|
&mut result,
|
||||||
|
&name,
|
||||||
|
&position,
|
||||||
|
&scale,
|
||||||
|
&rotation,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
name = Some(next_name);
|
||||||
|
if position.is_some() {
|
||||||
|
position = None;
|
||||||
|
}
|
||||||
|
if scale.is_some() {
|
||||||
|
scale = None;
|
||||||
|
}
|
||||||
|
if rotation.is_some() {
|
||||||
|
rotation = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"position" => {
|
||||||
|
if name.is_none() {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: name was not supplied before position"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let x = if let Ok(res) = tokens.next().unwrap().parse::<f32>() {
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: structure of positions in node transforms incorrect at X"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let y = if let Ok(res) = tokens.next().unwrap().parse::<f32>() {
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: structure of positions in node transforms incorrect at Y"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let z = if let Ok(res) = tokens.next().unwrap().parse::<f32>() {
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: structure of positions in node transforms incorrect at Z"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
position = Some(Vec3::new(x, y, z));
|
||||||
|
}
|
||||||
|
"scale" => {
|
||||||
|
if name.is_none() {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: name was not supplied before scale".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let x = if let Ok(res) = tokens.next().unwrap().parse::<f32>() {
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: structure of positions in node transforms incorrect at X"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let y = if let Ok(res) = tokens.next().unwrap().parse::<f32>() {
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: structure of positions in node transforms incorrect at Y"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let z = if let Ok(res) = tokens.next().unwrap().parse::<f32>() {
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: structure of positions in node transforms incorrect at Z"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
scale = Some(Vec3::new(x, y, z));
|
||||||
|
}
|
||||||
|
"rotation" => {
|
||||||
|
if name.is_none() {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: name was not supplied before rotation"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let x = if let Ok(res) = tokens.next().unwrap().parse::<f32>() {
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: structure of positions in node transforms incorrect at X"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let y = if let Ok(res) = tokens.next().unwrap().parse::<f32>() {
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: structure of positions in node transforms incorrect at Y"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let z = if let Ok(res) = tokens.next().unwrap().parse::<f32>() {
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: structure of positions in node transforms incorrect at Z"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let w = if let Ok(res) = tokens.next().unwrap().parse::<f32>() {
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
return Err(StructureLoadError::FileDataInvalid(
|
||||||
|
"error: structure of positions in node transforms incorrect at Z"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
rotation = Some(Quat::from_array([x, y, z, w]));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if name.is_some()
|
||||||
|
& (position.is_some() | scale.is_some() | rotation.is_some())
|
||||||
|
{
|
||||||
|
commit_node_transform(&mut result, &name, &position, &scale, &rotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ^ NODE ELEMENTS BY STRING NAME, FOLLOWED BY TRANSFORM DATA (optional)
|
||||||
|
// v NODE DATA SUCH AS RELIANT GRIDS (optional)
|
||||||
|
'node_attachments: {
|
||||||
|
if node_attachments.is_none() {
|
||||||
|
break 'node_attachments;
|
||||||
|
}
|
||||||
|
let lines = node_attachments.unwrap().lines();
|
||||||
|
for each in lines {
|
||||||
|
let mut tokens = each.trim().split("|").peekable();
|
||||||
|
let name = tokens.next();
|
||||||
|
if name.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut the_rest = String::new();
|
||||||
|
while tokens.peek().is_some() {
|
||||||
|
the_rest += ("|".to_owned() + tokens.next().unwrap()).as_str();
|
||||||
|
}
|
||||||
|
result.element_data.insert(
|
||||||
|
name.unwrap().to_owned(),
|
||||||
|
ElementData::Unprocessed(the_rest),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ^ NODE DATA SUCH AS RELIANT GRIDS (optional)
|
||||||
Err(StructureLoadError::FileDataInvalid("".to_string()))
|
Err(StructureLoadError::FileDataInvalid("".to_string()))
|
||||||
}
|
}
|
||||||
pub fn save(asset: &StructureAsset) -> Option<Vec<u8>> {
|
pub fn save(asset: &StructureAsset) -> Option<Vec<u8>> {
|
||||||
|
@ -234,16 +525,19 @@ pub mod serialization {
|
||||||
// error if bool is true
|
// error if bool is true
|
||||||
fn transform_to_file(pos: &Transform, data: &mut String) -> bool {
|
fn transform_to_file(pos: &Transform, data: &mut String) -> bool {
|
||||||
let [x, y, z] = pos.translation.to_array();
|
let [x, y, z] = pos.translation.to_array();
|
||||||
*data += format!("{x}|{y}|{z}").as_str();
|
*data += NEWLINE;
|
||||||
|
*data += format!("position|{x}|{y}|{z}").as_str();
|
||||||
|
*data += NEWLINE;
|
||||||
let [x, y, z] = pos.scale.to_array();
|
let [x, y, z] = pos.scale.to_array();
|
||||||
*data += format!("|{x}|{y}|{z}").as_str();
|
*data += format!("scale|{x}|{y}|{z}").as_str();
|
||||||
|
*data += NEWLINE;
|
||||||
let [x, y, z, w] = pos.rotation.to_array();
|
let [x, y, z, w] = pos.rotation.to_array();
|
||||||
*data += format!("|{x}|{y}|{z}|{w}").as_str();
|
*data += format!("rotation|{x}|{y}|{z}|{w}").as_str();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
for (name, pos) in &asset.default_pose_positions {
|
for (name, pos) in &asset.default_pose_positions {
|
||||||
data += NEWLINE;
|
data += NEWLINE;
|
||||||
data += format!("{name}|").as_str();
|
data += format!("element|{name}").as_str();
|
||||||
if !transform_to_file(pos, &mut data) {
|
if !transform_to_file(pos, &mut data) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue