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};
|
||||
|
||||
pub fn load_detect_version(_bytes: Vec<u8>) -> Result<StructureAsset, StructureLoadError> {
|
||||
return Err(StructureLoadError::FileDataInvalid("".to_string()));
|
||||
pub fn load_detect_version(bytes: Vec<u8>) -> Result<StructureAsset, StructureLoadError> {
|
||||
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 {
|
||||
|
@ -155,14 +184,276 @@ pub mod serialization {
|
|||
|
||||
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};
|
||||
pub fn load(
|
||||
_data: std::str::Lines<'_>,
|
||||
) -> Result<StructureAsset, StructureLoadError> {
|
||||
pub fn load(data: String) -> Result<StructureAsset, StructureLoadError> {
|
||||
let mut result = StructureAsset {
|
||||
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()))
|
||||
}
|
||||
pub fn save(asset: &StructureAsset) -> Option<Vec<u8>> {
|
||||
|
@ -234,16 +525,19 @@ pub mod serialization {
|
|||
// error if bool is true
|
||||
fn transform_to_file(pos: &Transform, data: &mut String) -> bool {
|
||||
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();
|
||||
*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();
|
||||
*data += format!("|{x}|{y}|{z}|{w}").as_str();
|
||||
*data += format!("rotation|{x}|{y}|{z}|{w}").as_str();
|
||||
true
|
||||
}
|
||||
for (name, pos) in &asset.default_pose_positions {
|
||||
data += NEWLINE;
|
||||
data += format!("{name}|").as_str();
|
||||
data += format!("element|{name}").as_str();
|
||||
if !transform_to_file(pos, &mut data) {
|
||||
return None;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue