added object visitor

This commit is contained in:
Dmitry
2019-08-20 14:20:04 +03:00
parent 2b917917ad
commit 96def65b25
20 changed files with 31138 additions and 93 deletions
+15 -2
View File
@@ -6,8 +6,21 @@
<component name="ChangeListManager">
<list default="true" id="1932b2c4-d6ff-413c-9558-19959667326b" name="Default Changelist" comment="scene graph">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.lock" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.toml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/save.json" beforeDir="false" afterPath="$PROJECT_DIR$/save.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/engine/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/engine/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/game/level.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/game/level.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/game/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/game/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/main.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/math/triangulator.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/math/triangulator.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/renderer/render.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/renderer/render.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/renderer/surface.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/renderer/surface.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/resource/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/resource/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/resource/model.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/resource/model.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/scene/node.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/scene/node.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/utils/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/utils/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/utils/pool.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/utils/pool.rs" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
@@ -61,7 +74,7 @@
</list>
</option>
</component>
<component name="RunManager" selected="Cargo Command.Release">
<component name="RunManager" selected="Cargo Command.Test">
<configuration name="Clippy" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="channel" value="DEFAULT" />
<option name="command" value="clippy" />
Generated
+11
View File
@@ -66,6 +66,14 @@ dependencies = [
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "base64"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.0.4"
@@ -752,6 +760,8 @@ dependencies = [
name = "rg3d"
version = "0.1.1"
dependencies = [
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glutin 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lexical 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1090,6 +1100,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
"checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4"
"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
+3 -1
View File
@@ -15,4 +15,6 @@ glutin = "*"
image = "*"
lexical = "*"
serde = { version = "*", features = ["derive"] }
serde_json = "*"
serde_json = "*"
byteorder = "*"
base64="*"
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

+30104 -2
View File
File diff suppressed because it is too large Load Diff
+54 -25
View File
@@ -1,35 +1,39 @@
use crate::{
scene::*,
scene::{
*,
node::NodeKind
},
utils::{
pool::*,
rcpool::{
RcPool,
RcHandle
}
RcHandle,
},
},
renderer::{
render::*,
surface::SurfaceSharedData
surface::SurfaceSharedData,
surface::Surface
},
resource::{
*,
texture::*,
model::Model,
ttf::Font
ttf::Font,
},
gui::UserInterface,
math::vec2::Vec2
math::vec2::Vec2,
};
use std::{
path::*,
collections::VecDeque,
time::Duration
time::Duration,
any::TypeId
};
use serde::{
Serialize,
Deserialize
Deserialize,
};
use std::any::{Any, TypeId};
#[derive(Serialize, Deserialize)]
pub struct ResourceManager {
@@ -154,7 +158,7 @@ impl State {
"fbx" => {
match Model::load(path, self) {
Ok(model) => {
self.resource_manager.add_resource(Resource::new(path, ResourceKind::Model(model)))
self.resource_manager.add_resource(Resource::new(path, ResourceKind::Model(model)))
}
Err(_) => {
println!("Unable to load model from {}!", path.display());
@@ -202,27 +206,52 @@ impl State {
}
fn resolve(&mut self) {
// Reload all resources first.
let mut resources_to_reload = Vec::new();
for i in 0..self.resource_manager.resources.get_capacity() {
let path;
let id;
if let Some(resource) = self.resource_manager.resources.at(i) {
path = PathBuf::from(resource.get_path());
match resource.borrow_kind() {
ResourceKind::Model(model) => id = model.type_id(),
ResourceKind::Texture(texture) => id = texture.type_id()
}
} else {
continue
}
let handle = self.resource_manager.resources.handle_from_index(i);
if handle.is_some() {
resources_to_reload.push(handle);
}
}
// Reload all resources first.
for resource_handle in resources_to_reload.iter() {
let (path, id) =
if let Some(resource) = self.resource_manager.resources.borrow(resource_handle) {
(PathBuf::from(resource.get_path()), resource.get_kind_id())
} else {
continue;
};
if id == TypeId::of::<Model>() {
let model = Model::load(path.as_path(), self).unwrap();
let resource = Resource::new(path.as_path(), ResourceKind::Model(model));
self.resource_manager.resources.replace(&handle, resource);
} else if id == TypeId::of::<Texture>() {
self.resource_manager.resources.replace(&resource_handle, resource);
} else if id == TypeId::of::<Texture>() {}
}
for scene in self.scenes.iter_mut() {
for node in scene.nodes.iter_mut() {
let node_name = String::from(node.get_name());
if let Some(resource) = self.resource_manager.borrow_resource(node.get_resource()) {
if let NodeKind::Mesh(mesh) = node.borrow_kind_mut() {
if let ResourceKind::Model(model) = resource.borrow_kind() {
let resource_node_handle = model.find_node_by_name(node_name.as_str());
if let Some(resource_node) = model.get_scene().get_node(&resource_node_handle) {
if let NodeKind::Mesh(resource_mesh) = resource_node.borrow_kind() {
let surfaces = mesh.get_surfaces_mut();
surfaces.clear();
for resource_surface in resource_mesh.get_surfaces() {
surfaces.push(Surface {
data: self.surf_data_storage.share_handle(&resource_surface.get_data_handle()),
texture: self.resource_manager.share_resource_handle(&resource_surface.get_texture_resource_handle()),
});
}
}
}
}
}
}
}
}
}
+1 -1
View File
@@ -31,7 +31,7 @@ impl Level {
// Create collision geometry
let polygon_handle = scene.find_node_by_name(&map_root_handle, "Polygon");
if let Some(polygon) = scene.get_node(&polygon_handle) {
let global_transform = polygon.global_transform;
let global_transform = polygon.get_global_transform();
let mut static_geometry = StaticGeometry::new();
if let NodeKind::Mesh(mesh) = polygon.borrow_kind() {
for surface in mesh.get_surfaces() {
+5 -4
View File
@@ -16,18 +16,17 @@ use crate::{
scroll_viewer::ScrollViewerBuilder,
grid::{GridBuilder, Column, Row},
text::TextBuilder,
scroll_bar::ScrollBarBuilder
}
scroll_bar::ScrollBarBuilder,
},
};
use crate::gui::node::{UINode, UINodeKind};
use std::{
cell::RefCell,
fs::File,
path::Path,
fmt::Write,
time::Instant,
rc::Rc,
};
use crate::gui::node::{UINode, UINodeKind};
pub struct MenuState {
save_game: Option<()>,
@@ -52,6 +51,7 @@ pub struct GameTime {
delta: f64,
}
impl Game {
pub fn new() -> Game {
let engine = Engine::new();
@@ -270,6 +270,7 @@ impl Game {
}
debug_string.clear();
use std::fmt::Write;
write!(debug_string, "Frame time: {:.2} ms\nFPS: {}\nUp time: {:.2} s",
self.engine.get_rendering_statisting().frame_time * 1000.0,
self.engine.get_rendering_statisting().current_fps,
+3
View File
@@ -7,6 +7,9 @@ extern crate glutin;
// Fast string -> number conversion
extern crate lexical;
extern crate byteorder;
extern crate base64;
// Serialization
extern crate serde;
extern crate serde_json;
-3
View File
@@ -164,7 +164,6 @@ pub fn triangulate(vertices: &[Vec3], out_triangles: &mut Vec<(usize, usize, usi
}
}
#[test]
fn quadrilaterals_triangulation_non_concave() {
let polygon = vec![
Vec3::make(0.0, 0.0, 1.0),
@@ -179,7 +178,6 @@ fn quadrilaterals_triangulation_non_concave() {
assert_ne!(ref_indices.len(), 0);
}
#[test]
fn quadrilaterals_triangulation_concave() {
let polygon = vec![
Vec3::make(0.0, 2.0, 1.0),
@@ -194,7 +192,6 @@ fn quadrilaterals_triangulation_concave() {
assert_ne!(ref_indices.len(), 0);
}
//#[test]
fn ear_clip_test() {
let polygon = vec![
Vec3::make(0.0, 0.0, 1.0),
+57 -44
View File
@@ -3,27 +3,42 @@ use crate::{
renderer::{
gl,
gl::types::*,
surface::*
surface::*,
},
math::{
vec2::*,
vec3::*,
mat4::Mat4
mat4::Mat4,
},
utils::pool::*,
scene::node::*,
engine::{ResourceManager, State, duration_to_seconds_f32},
engine::{
ResourceManager,
State,
duration_to_seconds_f32,
},
resource::{
ResourceKind,
ttf::Font
ttf::Font,
},
gui::draw::{
DrawingContext,
CommandKind,
Color,
},
gui::draw::{DrawingContext, CommandKind, Color},
};
use std::{
ffi::{CStr, CString, c_void},
ffi::{
CStr,
CString,
c_void,
},
mem::size_of,
time::{Instant, Duration},
thread
time::{
Instant,
Duration,
},
thread,
};
// Welcome to the kingdom of Unsafe Code
@@ -310,44 +325,43 @@ fn create_white_dummy() -> GLuint {
impl Renderer {
pub fn new() -> Self {
let events_loop = glutin::EventsLoop::new();
let primary_monitor = events_loop.get_primary_monitor();
let mut monitor_dimensions = primary_monitor.get_dimensions();
monitor_dimensions.height *= 0.6;
monitor_dimensions.width *= 0.6;
let window_size = monitor_dimensions.to_logical(primary_monitor.get_hidpi_factor());
let window_builder = glutin::WindowBuilder::new()
.with_title("RG3D")
.with_dimensions(window_size)
.with_resizable(false);
let context = glutin::ContextBuilder::new()
.with_vsync(true)
.build_windowed(window_builder, &events_loop)
.unwrap();
unsafe {
let events_loop = glutin::EventsLoop::new();
let primary_monitor = events_loop.get_primary_monitor();
let mut monitor_dimensions = primary_monitor.get_dimensions();
monitor_dimensions.height *= 0.6;
monitor_dimensions.width *= 0.6;
let window_size = monitor_dimensions.to_logical(primary_monitor.get_hidpi_factor());
let window_builder = glutin::WindowBuilder::new()
.with_title("RG3D")
.with_dimensions(window_size)
.with_resizable(false);
let context = glutin::ContextBuilder::new()
.with_vsync(true)
.build_windowed(window_builder, &events_loop)
.unwrap();
context.make_current().unwrap();
gl::load_with(|symbol| context.get_proc_address(symbol) as *const _);
gl::Enable(gl::DEPTH_TEST);
}
Self {
context,
events_loop,
flat_shader: create_flat_shader(),
ui_shader: create_ui_shader(),
traversal_stack: Vec::new(),
cameras: Vec::new(),
lights: Vec::new(),
meshes: Vec::new(),
frame_rate_limit: 60,
statistics: Statistics::default(),
white_dummy: create_white_dummy(),
ui_render_buffers: create_ui_render_buffers(),
}
Self {
context,
events_loop,
flat_shader: create_flat_shader(),
ui_shader: create_ui_shader(),
traversal_stack: Vec::new(),
cameras: Vec::new(),
lights: Vec::new(),
meshes: Vec::new(),
frame_rate_limit: 60,
statistics: Statistics::default(),
white_dummy: create_white_dummy(),
ui_render_buffers: create_ui_render_buffers(),
}
}
@@ -539,7 +553,7 @@ impl Renderer {
_ => ()
}
// Queue children for render
for child_handle in node.children.iter() {
for child_handle in node.get_children() {
self.traversal_stack.push(child_handle.clone());
}
}
@@ -569,7 +583,7 @@ impl Renderer {
continue;
}
let mvp = view_projection * node.global_transform;
let mvp = view_projection * *node.get_global_transform();
gl::UseProgram(self.flat_shader.program.id);
gl::UniformMatrix4fv(self.flat_shader.wvp_matrix, 1, gl::FALSE, &mvp.f as *const GLfloat);
@@ -676,7 +690,6 @@ impl Renderer {
let index_offset_bytes = cmd.get_index_offset() * std::mem::size_of::<GLuint>();
gl::DrawElements(gl::TRIANGLES, index_count as i32, gl::UNSIGNED_INT,
index_offset_bytes as *const c_void);
}
gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
gl::BindVertexArray(0);
+2 -2
View File
@@ -353,8 +353,8 @@ impl Drop for SurfaceSharedData {
#[derive(Serialize, Deserialize)]
pub struct Surface {
data: RcHandle<SurfaceSharedData>,
texture: RcHandle<Resource>,
pub data: RcHandle<SurfaceSharedData>,
pub texture: RcHandle<Resource>,
}
impl Surface {
+8
View File
@@ -11,6 +11,7 @@ use crate::{
model::{Model}
}
};
use std::any::{TypeId, Any};
#[derive(Serialize, Deserialize)]
pub enum ResourceKind {
@@ -46,6 +47,13 @@ impl Resource {
pub fn borrow_kind_mut(&mut self) -> &mut ResourceKind {
&mut self.kind
}
pub fn get_kind_id(&self) -> TypeId {
match &self.kind {
ResourceKind::Model(model) => model.type_id(),
ResourceKind::Texture(texture) => texture.type_id()
}
}
}
pub trait ResourceBehavior {
+9 -1
View File
@@ -41,7 +41,7 @@ impl Model {
while let Some(node_handle) = stack.pop() {
if let Some(node) = dest_scene.nodes.borrow_mut(&node_handle) {
node.set_resource(state.get_resource_manager().share_resource_handle(resource_handle));
for child_handle in node.children.iter() {
for child_handle in node.get_children() {
stack.push(child_handle.clone());
}
}
@@ -55,4 +55,12 @@ impl Model {
pub fn get_scene_mut(&mut self) -> &mut Scene {
&mut self.scene
}
pub fn get_scene(&self) -> &Scene {
&self.scene
}
pub fn find_node_by_name(&self, name: &str) -> Handle<Node> {
self.scene.find_node_by_name(&self.scene.get_root(), name)
}
}
+18 -6
View File
@@ -148,6 +148,8 @@ impl Mesh {
self.surfaces.push(surface);
}
#[inline]
pub fn make_copy(&self, state: &State) -> Mesh {
Mesh {
@@ -177,14 +179,14 @@ pub struct Node {
rotation_pivot: Vec3,
scaling_offset: Vec3,
scaling_pivot: Vec3,
pub(super) visibility: bool,
pub(super) global_visibility: bool,
pub(super) parent: Handle<Node>,
pub(crate) children: Vec<Handle<Node>>,
pub(in crate::scene) visibility: bool,
pub(in crate::scene) global_visibility: bool,
pub(in crate::scene) parent: Handle<Node>,
pub(in crate::scene) children: Vec<Handle<Node>>,
#[serde(skip)]
pub(super) local_transform: Mat4,
pub(in crate::scene) local_transform: Mat4,
#[serde(skip)]
pub(crate) global_transform: Mat4,
pub(in crate::scene) global_transform: Mat4,
body: Handle<Body>,
resource: RcHandle<Resource>
}
@@ -368,6 +370,16 @@ impl Node {
self.local_position += vec;
}
#[inline]
pub fn get_children(&self) -> &[Handle<Node>] {
&self.children
}
#[inline]
pub fn get_global_transform(&self) -> &Mat4 {
&self.global_transform
}
#[inline]
pub fn get_global_position(&self) -> Vec3 {
Vec3 {
+1
View File
@@ -2,6 +2,7 @@ use std::marker::PhantomData;
pub mod pool;
pub mod rcpool;
pub mod visitor;
pub struct UnsafeCollectionView<T> {
items: *const T,
-2
View File
@@ -356,7 +356,6 @@ impl<'a, T> Iterator for PoolIteratorMut<'a, T> {
}
}
#[test]
fn pool_sanity_tests() {
let mut pool: Pool<String> = Pool::new();
let foobar_handle = pool.spawn(String::from("Foobar"));
@@ -377,7 +376,6 @@ fn pool_sanity_tests() {
assert_eq!(pool.borrow(&at_foobar_index).unwrap(), "AtFoobarIndex");
}
#[test]
fn pool_iterator_mut_test() {
let mut pool: Pool<String> = Pool::new();
let foobar = pool.spawn(format!("Foobar"));
+837
View File
@@ -0,0 +1,837 @@
use std::{
rc::Rc,
collections::HashMap,
fs::File,
any::Any,
path::Path,
cell::RefCell,
io::{
Write,
Read,
},
string::FromUtf8Error,
};
use byteorder::{
ReadBytesExt,
WriteBytesExt,
LittleEndian,
};
use crate::{
math::{
vec3::Vec3,
quat::Quat,
mat4::Mat4,
},
utils::{
pool::{
Handle,
Pool,
}
},
};
pub enum FieldKind {
U8(u8),
I8(i8),
U16(u16),
I16(i16),
U32(u32),
I32(i32),
U64(u64),
I64(i64),
F32(f32),
F64(f64),
Vec3(Vec3),
Quat(Quat),
Mat4(Mat4),
Data(Vec<u8>),
}
impl FieldKind {
fn as_string(&self) -> String {
match self {
FieldKind::U8(data) => format!("<u8 = {}>, ", data),
FieldKind::I8(data) => format!("<i8 = {}>, ", data),
FieldKind::U16(data) => format!("<u16 = {}>, ", data),
FieldKind::I16(data) => format!("<i16 = {}>, ", data),
FieldKind::U32(data) => format!("<u32 = {}>, ", data),
FieldKind::I32(data) => format!("<i32 = {}>, ", data),
FieldKind::U64(data) => format!("<u64 = {}>, ", data),
FieldKind::I64(data) => format!("<i64 = {}>, ", data),
FieldKind::F32(data) => format!("<f32 = {}>, ", data),
FieldKind::F64(data) => format!("<f64 = {}>, ", data),
FieldKind::Vec3(data) => {
format!("<vec3 = {}; {}; {}>, ", data.x, data.y, data.z)
}
FieldKind::Quat(data) => {
format!("<quat = {}; {}; {}; {}>, ", data.x, data.y, data.z, data.w)
}
FieldKind::Mat4(data) => {
// Lazy
format!("<mat4>, ")
}
FieldKind::Data(data) => {
base64::encode(data)
}
}
}
}
trait FieldData {
fn read(&mut self, kind: &FieldKind) -> VisitResult;
fn write(&self) -> FieldKind;
}
macro_rules! impl_field_data (($type_name:ty, $($kind:tt)*) => {
impl FieldData for $type_name {
fn read(&mut self, kind: &FieldKind) -> VisitResult {
match kind {
$($kind)*(data) => {
*self = data.clone();
Ok(())
},
_ => Err(VisitError::FieldTypeDoesNotMatch)
}
}
fn write(&self) -> FieldKind {
$($kind)*(self.clone())
}
}
});
impl_field_data!(u64, FieldKind::U64);
impl_field_data!(i64, FieldKind::I64);
impl_field_data!(u32, FieldKind::U32);
impl_field_data!(i32, FieldKind::I32);
impl_field_data!(u16, FieldKind::U16);
impl_field_data!(i16, FieldKind::I16);
impl_field_data!(u8, FieldKind::U8);
impl_field_data!(i8, FieldKind::I8);
impl_field_data!(f32, FieldKind::F32);
impl_field_data!(f64, FieldKind::F64);
impl_field_data!(Vec3, FieldKind::Vec3);
impl_field_data!(Quat, FieldKind::Quat);
impl_field_data!(Mat4, FieldKind::Mat4);
impl_field_data!(Vec<u8>, FieldKind::Data);
pub struct Field {
name: String,
kind: FieldKind,
}
#[derive(Debug)]
pub enum VisitError {
Io(std::io::Error),
UnknownFieldType(u8),
FieldDoesNotExist(String),
FieldAlreadyExists(String),
RegionAlreadyExists(String),
InvalidCurrentNode,
FieldTypeDoesNotMatch,
RegionDoesNotExist(String),
NoActiveNode,
NotSupportedFormat,
InvalidName,
TypeMismatch,
User(String),
}
impl From<std::io::Error> for VisitError {
fn from(io_err: std::io::Error) -> Self {
VisitError::Io(io_err)
}
}
impl From<FromUtf8Error> for VisitError {
fn from(_: FromUtf8Error) -> Self {
VisitError::InvalidName
}
}
pub type VisitResult = Result<(), VisitError>;
impl Field {
pub fn new(name: &str, kind: FieldKind) -> Self {
Self {
name: name.to_owned(),
kind,
}
}
fn save(field: &Field, file: &mut File) -> VisitResult {
let name = field.name.as_bytes();
file.write_u32::<LittleEndian>(name.len() as u32)?;
file.write(name)?;
match &field.kind {
FieldKind::U8(data) => {
file.write_u8(1)?;
file.write_u8(*data)?;
}
FieldKind::I8(data) => {
file.write_i8(2)?;
file.write_i8(*data)?;
}
FieldKind::U16(data) => {
file.write_u8(3)?;
file.write_u16::<LittleEndian>(*data)?;
}
FieldKind::I16(data) => {
file.write_u8(4)?;
file.write_i16::<LittleEndian>(*data)?;
}
FieldKind::U32(data) => {
file.write_u8(5)?;
file.write_u32::<LittleEndian>(*data)?;
}
FieldKind::I32(data) => {
file.write_u8(6)?;
file.write_i32::<LittleEndian>(*data)?;
}
FieldKind::U64(data) => {
file.write_u8(7)?;
file.write_u64::<LittleEndian>(*data)?;
}
FieldKind::I64(data) => {
file.write_u8(8)?;
file.write_i64::<LittleEndian>(*data)?;
}
FieldKind::F32(data) => {
file.write_u8(9)?;
file.write_f32::<LittleEndian>(*data)?;
}
FieldKind::F64(data) => {
file.write_u8(10)?;
file.write_f64::<LittleEndian>(*data)?;
}
FieldKind::Vec3(data) => {
file.write_u8(11)?;
file.write_f32::<LittleEndian>(data.x)?;
file.write_f32::<LittleEndian>(data.y)?;
file.write_f32::<LittleEndian>(data.z)?;
}
FieldKind::Quat(data) => {
file.write_u8(12)?;
file.write_f32::<LittleEndian>(data.x)?;
file.write_f32::<LittleEndian>(data.y)?;
file.write_f32::<LittleEndian>(data.z)?;
file.write_f32::<LittleEndian>(data.w)?;
}
FieldKind::Mat4(data) => {
file.write_u8(13)?;
for f in &data.f {
file.write_f32::<LittleEndian>(*f)?;
}
}
FieldKind::Data(data) => {
file.write_u8(14)?;
file.write_u32::<LittleEndian>(data.len() as u32)?;
file.write(data.as_slice());
}
}
Ok(())
}
fn load(file: &mut File) -> Result<Field, VisitError> {
let name_len = file.read_u32::<LittleEndian>()? as usize;
let mut raw_name = Vec::new();
for _ in 0..name_len {
raw_name.push(file.read_u8()?);
}
let id = file.read_u8()?;
Ok(Field::new(String::from_utf8(raw_name)?.as_str(), match id {
1 => FieldKind::U8(file.read_u8()?),
2 => FieldKind::I8(file.read_i8()?),
3 => FieldKind::U16(file.read_u16::<LittleEndian>()?),
4 => FieldKind::I16(file.read_i16::<LittleEndian>()?),
5 => FieldKind::U32(file.read_u32::<LittleEndian>()?),
6 => FieldKind::I32(file.read_i32::<LittleEndian>()?),
7 => FieldKind::U64(file.read_u64::<LittleEndian>()?),
8 => FieldKind::I64(file.read_i64::<LittleEndian>()?),
9 => FieldKind::F32(file.read_f32::<LittleEndian>()?),
10 => FieldKind::F64(file.read_f64::<LittleEndian>()?),
11 => FieldKind::Vec3({
let x = file.read_f32::<LittleEndian>()?;
let y = file.read_f32::<LittleEndian>()?;
let z = file.read_f32::<LittleEndian>()?;
Vec3 { x, y, z }
}),
12 => FieldKind::Quat({
let x = file.read_f32::<LittleEndian>()?;
let y = file.read_f32::<LittleEndian>()?;
let z = file.read_f32::<LittleEndian>()?;
let w = file.read_f32::<LittleEndian>()?;
Quat { x, y, z, w }
}),
13 => FieldKind::Mat4({
let mut f = [0.0f32; 16];
for n in &mut f {
*n = file.read_f32::<LittleEndian>()?;
}
Mat4 { f }
}),
14 => FieldKind::Data({
let len = file.read_u32::<LittleEndian>()? as usize;
let mut vec = Vec::with_capacity(len);
for _ in 0..len {
vec.push(file.read_u8()?);
}
vec
}),
_ => return Err(VisitError::UnknownFieldType(id))
}))
}
fn as_string(&self) -> String {
format!("{}{}", self.name, self.kind.as_string())
}
}
pub struct Node {
name: String,
fields: Vec<Field>,
parent: Handle<Node>,
children: Vec<Handle<Node>>,
}
impl Node {
fn new(name: &str, parent: Handle<Node>) -> Self {
Self {
name: name.to_owned(),
fields: Vec::new(),
parent,
children: Vec::new(),
}
}
}
impl Default for Node {
fn default() -> Self {
Self {
name: String::new(),
fields: Vec::new(),
parent: Handle::none(),
children: Vec::new(),
}
}
}
pub struct Visitor {
nodes: Pool<Node>,
rc_map: HashMap<u64, Rc<dyn Any>>,
reading: bool,
current_node: Handle<Node>,
root: Handle<Node>,
}
pub trait Visit {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult;
}
macro_rules! impl_generic_visit (($name:ident, $type_name:ty) => {
pub fn $name(&mut self, name: &str, value: &mut $type_name) -> VisitResult {
self.visit_generic(name, value)
}
});
impl Visitor {
const MAGIC: &'static str = "RG3D";
fn new() -> Self {
let mut nodes = Pool::new();
let root = nodes.spawn(Node::new("__ROOT__", Handle::none()));
Self {
nodes,
rc_map: HashMap::new(),
reading: false,
current_node: root.clone(),
root,
}
}
fn find_field(&mut self, name: &str) -> Option<&mut Field> {
if let Some(node) = self.nodes.borrow_mut(&self.current_node) {
for field in node.fields.iter_mut() {
if field.name == name {
return Some(field);
}
}
}
None
}
fn current_node(&mut self) -> Option<&mut Node> {
self.nodes.borrow_mut(&self.current_node)
}
fn visit_generic<T>(&mut self, name: &str, value: &mut T) -> VisitResult
where T: FieldData {
if self.reading {
if let Some(field) = self.find_field(name) {
value.read(&field.kind)
} else {
Err(VisitError::FieldDoesNotExist(name.to_owned()))
}
} else {
if let Some(_) = self.find_field(name) {
Err(VisitError::FieldAlreadyExists(name.to_owned()))
} else {
if let Some(node) = self.current_node() {
node.fields.push(Field::new(name, value.write()));
Ok(())
} else {
Err(VisitError::NoActiveNode)
}
}
}
}
impl_generic_visit!(visit_u64, u64);
impl_generic_visit!(visit_i64, i64);
impl_generic_visit!(visit_u32, u32);
impl_generic_visit!(visit_i32, i32);
impl_generic_visit!(visit_u16, u16);
impl_generic_visit!(visit_i16, i16);
impl_generic_visit!(visit_u8, u8);
impl_generic_visit!(visit_i8, i8);
impl_generic_visit!(visit_f32, f32);
impl_generic_visit!(visit_f64, f64);
impl_generic_visit!(visit_vec3, Vec3);
impl_generic_visit!(visit_quat, Quat);
impl_generic_visit!(visit_mat4, Mat4);
impl_generic_visit!(visit_data, Vec<u8>);
pub fn enter_region(&mut self, name: &str) -> VisitResult {
if self.reading {
if let Some(node) = self.nodes.borrow(&self.current_node) {
let mut region = Handle::none();
for child_handle in node.children.iter() {
if let Some(child) = self.nodes.borrow(child_handle) {
if child.name == name {
region = child_handle.clone();
break;
}
}
}
if region.is_some() {
self.current_node = region;
Ok(())
} else {
Err(VisitError::RegionDoesNotExist(name.to_owned()))
}
} else {
Err(VisitError::NoActiveNode)
}
} else {
// Make sure that node does not exists already.
if let Some(node) = self.nodes.borrow(&self.current_node) {
for child_handle in node.children.iter() {
if let Some(child) = self.nodes.borrow(child_handle) {
if child.name == name {
return Err(VisitError::RegionAlreadyExists(name.to_owned()));
}
}
}
}
let node_handle = self.nodes.spawn(Node::new(name, self.current_node.clone()));
if let Some(node) = self.nodes.borrow_mut(&self.current_node.clone()) {
node.children.push(node_handle.clone());
}
self.current_node = node_handle;
Ok(())
}
}
pub fn leave_region(&mut self) -> VisitResult {
self.current_node = if let Some(node) = self.nodes.borrow(&self.current_node) {
node.parent.clone()
} else {
return Err(VisitError::NoActiveNode);
};
Ok(())
}
pub fn visit_rc<T>(&mut self, name: &str, rc: &mut Rc<T>) -> VisitResult
where T: Visit + 'static {
self.enter_region(name)?;
if self.reading {
let mut raw = 0;
self.visit_u64("Id", &mut raw)?;
if let Some(ptr) = self.rc_map.get(&raw) {
if let Ok(res) = Rc::downcast::<T>(ptr.clone()) {
*rc = res;
} else {
return Err(VisitError::TypeMismatch);
}
} else {
self.rc_map.insert(raw as u64, rc.clone());
// Deserialize inner data.
let raw = Rc::into_raw(rc.clone()) as *const T as *mut T;
unsafe { &mut *raw }.visit("Data", self)?;
unsafe { Rc::from_raw(raw) };
}
} else {
let raw = Rc::into_raw(rc.clone()) as *const T as *mut T;
unsafe { Rc::from_raw(raw); };
let mut index = raw as u64;
self.visit_u64("Id", &mut index)?;
if !self.rc_map.contains_key(&index) {
self.rc_map.insert(index, rc.clone());
// Serialize inner data.
unsafe { &mut *raw }.visit("Data", self)?;
}
}
self.leave_region()?;
Ok(())
}
pub fn visit_vec<T>(&mut self, name: &str, vec: &mut Vec<T>) -> VisitResult
where T: Default + Visit + 'static {
self.enter_region(name)?;
let mut len = vec.len() as u32;
self.visit_u32("Length", &mut len)?;
if self.reading {
for index in 0..len {
let region_name = format!("Item{}", index);
self.enter_region(region_name.as_str())?;
let mut object = T::default();
object.visit("Data", self)?;
vec.push(object);
self.leave_region()?;
}
} else {
for (index, item) in vec.iter_mut().enumerate() {
let region_name = format!("Item{}", index);
self.enter_region(region_name.as_str())?;
item.visit("Data", self)?;
self.leave_region()?;
}
}
self.leave_region()?;
Ok(())
}
//pub fn visit_string(&mut self, name: &str, string: &mut String) -> VisitResult {
//}
pub fn visit_option<T>(&mut self, name: &str, opt: &mut Option<T>) -> VisitResult
where T: Default + Visit + 'static {
self.enter_region(name)?;
let mut is_some = if opt.is_some() { 1 } else { 0 };
self.visit_u8("IsSome", &mut is_some)?;
if is_some != 0 {
if self.reading {
let mut value = T::default();
value.visit("Data", self)?;
*opt = Some(value);
} else {
opt.as_mut().unwrap().visit("Data", self)?;
}
}
self.leave_region()?;
Ok(())
}
fn print_node(&self, node_handle: &Handle<Node>, nesting: usize, out_string: &mut String) {
let offset = (0..nesting).map(|_| { "\t" }).collect::<String>();
if let Some(node) = self.nodes.borrow(&node_handle) {
*out_string += format!("{}{}[Fields={}, Children={}]: ", offset, node.name, node.fields.len(), node.children.len()).as_str();
for field in node.fields.iter() {
*out_string += field.name.as_str();
match &field.kind {
FieldKind::U8(data) => *out_string += format!("<u8 = {}>, ", data).as_str(),
FieldKind::I8(data) => *out_string += format!("<i8 = {}>, ", data).as_str(),
FieldKind::U16(data) => *out_string += format!("<u16 = {}>, ", data).as_str(),
FieldKind::I16(data) => *out_string += format!("<i16 = {}>, ", data).as_str(),
FieldKind::U32(data) => *out_string += format!("<u32 = {}>, ", data).as_str(),
FieldKind::I32(data) => *out_string += format!("<i32 = {}>, ", data).as_str(),
FieldKind::U64(data) => *out_string += format!("<u64 = {}>, ", data).as_str(),
FieldKind::I64(data) => *out_string += format!("<i64 = {}>, ", data).as_str(),
FieldKind::F32(data) => *out_string += format!("<f32 = {}>, ", data).as_str(),
FieldKind::F64(data) => *out_string += format!("<f64 = {}>, ", data).as_str(),
FieldKind::Vec3(data) => {
*out_string += format!("<vec3 = {}; {}; {}>, ", data.x, data.y, data.z).as_str()
}
FieldKind::Quat(data) => {
*out_string += format!("<quat = {}; {}; {}; {}>, ", data.x, data.y, data.z, data.w).as_str()
}
FieldKind::Mat4(data) => {
// Lazy
*out_string += format!("<mat4>, ").as_str()
}
FieldKind::Data(data) => {
*out_string += base64::encode(data).as_str();
}
}
}
*out_string += format!("\n").as_str();
for child_handle in node.children.iter() {
self.print_node(child_handle, nesting + 1, out_string);
}
}
}
pub fn save_text(&self) -> String {
let mut out_string = String::new();
self.print_node(&self.root.clone(), 0, &mut out_string);
out_string
}
pub fn save_binary(&self, path: &Path) -> VisitResult {
let mut file = File::create(path)?;
file.write(Self::MAGIC.as_bytes())?;
let mut stack = Vec::new();
stack.push(self.root.clone());
while let Some(node_handle) = stack.pop() {
if let Some(node) = self.nodes.borrow(&node_handle) {
let name = node.name.as_bytes();
file.write_u32::<LittleEndian>(name.len() as u32)?;
file.write(name)?;
file.write_u32::<LittleEndian>(node.fields.len() as u32)?;
for field in node.fields.iter() {
Field::save(field, &mut file)?
}
file.write_u32::<LittleEndian>(node.children.len() as u32)?;
for child_handle in node.children.iter() {
stack.push(child_handle.clone());
}
}
}
Ok(())
}
fn load_node_binary(&mut self, file: &mut File) -> Result<Handle<Node>, VisitError> {
let name_len = file.read_u32::<LittleEndian>()? as usize;
let mut raw_name = Vec::new();
for _ in 0..name_len {
raw_name.push(file.read_u8()?);
}
let mut node = Node::default();
node.name = String::from_utf8(raw_name)?;
println!("{}", node.name);
let field_count = file.read_u32::<LittleEndian>()? as usize;
for _ in 0..field_count {
let field = Field::load(file)?;
println!("Field: {}", field.as_string());
node.fields.push(field);
}
let mut children = Vec::new();
let child_count = file.read_u32::<LittleEndian>()? as usize;
for _ in 0..child_count {
children.push(self.load_node_binary(file)?);
}
node.children = children.clone();
let handle = self.nodes.spawn(node);
for child_handle in children.iter() {
if let Some(child) = self.nodes.borrow_mut(child_handle) {
child.parent = handle.clone();
}
}
Ok(handle)
}
pub fn load_binary(path: &Path) -> Result<Self, VisitError> {
let mut file = File::open(path)?;
let mut magic: [u8; 4] = Default::default();
file.read(&mut magic)?;
if !magic.eq(Self::MAGIC.as_bytes()) {
return Err(VisitError::NotSupportedFormat);
}
let mut visitor = Self {
nodes: Pool::new(),
rc_map: Default::default(),
reading: true,
current_node: Handle::none(),
root: Handle::none(),
};
visitor.root = visitor.load_node_binary(&mut file)?;
visitor.current_node = visitor.root.clone();
Ok(visitor)
}
}
impl<T> Visit for RefCell<T> where T: Visit + 'static {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
self.borrow_mut().visit(name, visitor)
}
}
impl<T> Visit for Rc<T> where T: Default + Visit + 'static {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.visit_rc(name, self)
}
}
#[cfg(test)]
mod test {
use std::{
rc::Rc,
path::Path,
};
use crate::utils::visitor::{Visitor, Visit, VisitResult, VisitError};
use std::fs::File;
use std::io::Write;
pub struct Model {
data: u64
}
pub struct Texture {
data: Vec<u8>
}
impl Visit for Texture {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
visitor.visit_data("Data", &mut self.data)?;
visitor.leave_region()
}
}
impl Visit for Model {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
visitor.visit_u64("Data", &mut self.data)?;
visitor.leave_region()
}
}
impl Visit for ResourceKind {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
match self {
ResourceKind::Unknown => Err(VisitError::User(format!("invalid resource type"))),
ResourceKind::Texture(tex) => tex.visit(name, visitor),
ResourceKind::Model(model) => model.visit(name, visitor)
}
}
}
pub enum ResourceKind {
Unknown,
Model(Model),
Texture(Texture),
}
struct Resource {
kind: ResourceKind,
data: u16,
}
impl Resource {
fn new(kind: ResourceKind) -> Self {
Self {
kind,
data: 0,
}
}
}
impl Default for Resource {
fn default() -> Self {
Self {
kind: ResourceKind::Unknown,
data: 0,
}
}
}
impl Visit for Resource {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
if visitor.reading {} else {
let mut kind_id = match &self.kind {
ResourceKind::Unknown => return Err(VisitError::User(format!("Invalid resource!"))),
ResourceKind::Model(model) => 0,
ResourceKind::Texture(texture) => 1
};
visitor.visit_u8("KindId", &mut kind_id)?;
self.kind.visit("KindData", visitor)?;
}
visitor.visit_u16("ResData", &mut self.data)
}
}
struct Foo {
bar: u64,
shared_resource: Option<Rc<Resource>>,
}
impl Default for Foo {
fn default() -> Self {
Self {
bar: 0,
shared_resource: None,
}
}
}
impl Foo {
fn new(resource: Rc<Resource>) -> Self {
Self {
bar: 123,
shared_resource: Some(resource),
}
}
}
impl Visit for Foo {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.visit_u64("Bar", &mut self.bar)?;
visitor.visit_option("SharedResource", &mut self.shared_resource)?;
Ok(())
}
}
fn visitor_save_test() {
let mut visitor = Visitor::new();
let mut resource = Rc::new(Resource::new(ResourceKind::Model(Model { data: 555 })));
visitor.visit_rc("SharedResource", &mut resource).unwrap();
let mut objects = vec![
Foo::new(resource.clone()),
Foo::new(resource)
];
visitor.visit_vec("Objects", &mut objects).unwrap();
visitor.save_binary(Path::new("test.bin")).unwrap();
if let Ok(mut file) = File::create(Path::new("test.txt")) {
file.write(visitor.save_text().as_bytes()).unwrap();
}
}
#[test]
fn visitor_load_test() {
let mut visitor = Visitor::load_binary(Path::new("test.bin")).unwrap();
let mut resource: Rc<Resource> = Rc::new(Default::default());
visitor.visit_rc("SharedResource", &mut resource).unwrap();
let mut objects: Vec<Foo> = Vec::new();
visitor.visit_vec("Objects", &mut objects).unwrap();
}
}
BIN
View File
Binary file not shown.
+10
View File
@@ -0,0 +1,10 @@
__ROOT__[Fields=0, Children=2]:
SharedResource[Fields=3, Children=1]: Id<u64 = 1800001388368>, KindId<u8 = 0>, ResData<u16 = 0>,
KindData[Fields=1, Children=0]: Data<u64 = 555>,
Objects[Fields=1, Children=2]: Length<u32 = 2>,
Item0[Fields=1, Children=1]: Bar<u64 = 123>,
SharedResource[Fields=1, Children=1]: IsSome<u8 = 1>,
Data[Fields=1, Children=0]: Id<u64 = 1800001388368>,
Item1[Fields=1, Children=1]: Bar<u64 = 123>,
SharedResource[Fields=1, Children=1]: IsSome<u8 = 1>,
Data[Fields=1, Children=0]: Id<u64 = 1800001388368>,