sparsey ecs
This commit is contained in:
32
Cargo.lock
generated
32
Cargo.lock
generated
@@ -11,6 +11,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic_refcell"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cursebreaker-parser"
|
name = "cursebreaker-parser"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -21,6 +27,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
|
"sparsey",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -45,6 +52,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.16.1"
|
version = "0.16.1"
|
||||||
@@ -58,7 +71,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown 0.16.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
@@ -132,6 +145,12 @@ version = "0.8.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.22"
|
version = "1.0.22"
|
||||||
@@ -181,6 +200,17 @@ dependencies = [
|
|||||||
"unsafe-libyaml",
|
"unsafe-libyaml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sparsey"
|
||||||
|
version = "0.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb745f2600e2b85825349ae307cbddba8061afa1dc83f3901bf3cc546fc2c542"
|
||||||
|
dependencies = [
|
||||||
|
"atomic_refcell",
|
||||||
|
"hashbrown 0.15.5",
|
||||||
|
"rustc-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.111"
|
version = "2.0.111"
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ regex = "1.10"
|
|||||||
# Math types (Vector2, Vector3, Quaternion, etc.)
|
# Math types (Vector2, Vector3, Quaternion, etc.)
|
||||||
glam = { version = "0.29", features = ["serde"] }
|
glam = { version = "0.29", features = ["serde"] }
|
||||||
|
|
||||||
|
# ECS (Entity Component System)
|
||||||
|
sparsey = "0.13"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# Testing utilities
|
# Testing utilities
|
||||||
pretty_assertions = "1.4"
|
pretty_assertions = "1.4"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
//! GameObject wrapper for ergonomic access to GameObject properties
|
//! GameObject wrapper for ergonomic access to GameObject properties
|
||||||
|
|
||||||
use crate::model::UnityDocument;
|
use crate::model::UnityDocument;
|
||||||
use crate::types::{FileID, FileRef};
|
|
||||||
|
|
||||||
/// A wrapper around a UnityDocument that represents a GameObject
|
/// A wrapper around a UnityDocument that represents a GameObject
|
||||||
///
|
///
|
||||||
@@ -17,19 +16,16 @@ use crate::types::{FileID, FileRef};
|
|||||||
/// if let Some(go) = GameObject::parse(doc) {
|
/// if let Some(go) = GameObject::parse(doc) {
|
||||||
/// println!("GameObject: {}", go.name().unwrap_or("Unnamed"));
|
/// println!("GameObject: {}", go.name().unwrap_or("Unnamed"));
|
||||||
/// println!(" Active: {}", go.is_active());
|
/// println!(" Active: {}", go.is_active());
|
||||||
/// println!(" Components: {}", go.components().len());
|
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// # Ok::<(), cursebreaker_parser::Error>(())
|
/// # Ok::<(), cursebreaker_parser::Error>(())
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GameObject {
|
pub struct GameObject {
|
||||||
file_id: FileID,
|
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
is_active: bool,
|
is_active: bool,
|
||||||
layer: Option<i64>,
|
layer: Option<i64>,
|
||||||
tag: Option<i64>,
|
tag: Option<i64>,
|
||||||
components: Vec<FileRef>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameObject {
|
impl GameObject {
|
||||||
@@ -64,29 +60,11 @@ impl GameObject {
|
|||||||
.and_then(|p| p.get("m_TagString"))
|
.and_then(|p| p.get("m_TagString"))
|
||||||
.and_then(|v| v.as_i64());
|
.and_then(|v| v.as_i64());
|
||||||
|
|
||||||
let components = props
|
|
||||||
.and_then(|p| p.get("m_Component"))
|
|
||||||
.and_then(|v| v.as_array())
|
|
||||||
.map(|arr| {
|
|
||||||
arr.iter()
|
|
||||||
.filter_map(|item| {
|
|
||||||
item.as_object().and_then(|obj| {
|
|
||||||
obj.get("component")
|
|
||||||
.and_then(|v| v.as_file_ref())
|
|
||||||
.copied()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
file_id: document.file_id,
|
|
||||||
name,
|
name,
|
||||||
is_active,
|
is_active,
|
||||||
layer,
|
layer,
|
||||||
tag,
|
tag,
|
||||||
components,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,22 +87,14 @@ impl GameObject {
|
|||||||
pub fn tag(&self) -> Option<i64> {
|
pub fn tag(&self) -> Option<i64> {
|
||||||
self.tag
|
self.tag
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the list of component references attached to this GameObject
|
|
||||||
pub fn components(&self) -> &[FileRef] {
|
|
||||||
&self.components
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the file ID of this GameObject
|
|
||||||
pub fn file_id(&self) -> FileID {
|
|
||||||
self.file_id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::model::UnityDocument;
|
||||||
use crate::property::PropertyValue;
|
use crate::property::PropertyValue;
|
||||||
|
use crate::types::FileID;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
fn create_test_game_object() -> UnityDocument {
|
fn create_test_game_object() -> UnityDocument {
|
||||||
@@ -134,7 +104,6 @@ mod tests {
|
|||||||
go_props.insert("m_Name".to_string(), PropertyValue::String("TestObject".to_string()));
|
go_props.insert("m_Name".to_string(), PropertyValue::String("TestObject".to_string()));
|
||||||
go_props.insert("m_IsActive".to_string(), PropertyValue::Boolean(true));
|
go_props.insert("m_IsActive".to_string(), PropertyValue::Boolean(true));
|
||||||
go_props.insert("m_Layer".to_string(), PropertyValue::Integer(0));
|
go_props.insert("m_Layer".to_string(), PropertyValue::Integer(0));
|
||||||
go_props.insert("m_Component".to_string(), PropertyValue::Array(vec![]));
|
|
||||||
|
|
||||||
properties.insert("GameObject".to_string(), PropertyValue::Object(go_props));
|
properties.insert("GameObject".to_string(), PropertyValue::Object(go_props));
|
||||||
|
|
||||||
@@ -174,13 +143,6 @@ mod tests {
|
|||||||
assert_eq!(go.layer(), Some(0));
|
assert_eq!(go.layer(), Some(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_game_object_file_id() {
|
|
||||||
let doc = create_test_game_object();
|
|
||||||
let go = GameObject::parse(&doc).unwrap();
|
|
||||||
assert_eq!(go.file_id().as_i64(), 12345);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_non_game_object() {
|
fn test_non_game_object() {
|
||||||
let mut doc = create_test_game_object();
|
let mut doc = create_test_game_object();
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
//! Transform and RectTransform component wrappers
|
//! Transform and RectTransform component wrappers
|
||||||
|
|
||||||
use crate::model::UnityDocument;
|
use crate::model::UnityDocument;
|
||||||
use crate::types::{Component, FileID, FileRef, Quaternion, Vector2, Vector3};
|
use crate::types::{Quaternion, Vector2, Vector3};
|
||||||
|
use sparsey::Entity;
|
||||||
|
|
||||||
/// A wrapper around a UnityDocument that represents a Transform component
|
/// A wrapper around a UnityDocument that represents a Transform component
|
||||||
///
|
///
|
||||||
@@ -24,13 +25,11 @@ use crate::types::{Component, FileID, FileRef, Quaternion, Vector2, Vector3};
|
|||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Transform {
|
pub struct Transform {
|
||||||
file_id: FileID,
|
|
||||||
game_object: Option<FileRef>,
|
|
||||||
local_position: Option<Vector3>,
|
local_position: Option<Vector3>,
|
||||||
local_rotation: Option<Quaternion>,
|
local_rotation: Option<Quaternion>,
|
||||||
local_scale: Option<Vector3>,
|
local_scale: Option<Vector3>,
|
||||||
parent: Option<FileRef>,
|
parent: Option<Entity>,
|
||||||
children: Vec<FileRef>,
|
children: Vec<Entity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transform {
|
impl Transform {
|
||||||
@@ -47,11 +46,6 @@ impl Transform {
|
|||||||
.get(&document.class_name)
|
.get(&document.class_name)
|
||||||
.and_then(|v| v.as_object());
|
.and_then(|v| v.as_object());
|
||||||
|
|
||||||
let game_object = props
|
|
||||||
.and_then(|p| p.get("m_GameObject"))
|
|
||||||
.and_then(|v| v.as_file_ref())
|
|
||||||
.copied();
|
|
||||||
|
|
||||||
let local_position = props
|
let local_position = props
|
||||||
.and_then(|p| p.get("m_LocalPosition"))
|
.and_then(|p| p.get("m_LocalPosition"))
|
||||||
.and_then(|v| v.as_vector3())
|
.and_then(|v| v.as_vector3())
|
||||||
@@ -67,30 +61,15 @@ impl Transform {
|
|||||||
.and_then(|v| v.as_vector3())
|
.and_then(|v| v.as_vector3())
|
||||||
.copied();
|
.copied();
|
||||||
|
|
||||||
let parent = props
|
// Note: parent and children entities will be set later when building the ECS world
|
||||||
.and_then(|p| p.get("m_Father"))
|
// The FileRef data is still in the UnityDocument but needs to be converted to entities separately
|
||||||
.and_then(|v| v.as_file_ref())
|
|
||||||
.copied();
|
|
||||||
|
|
||||||
let children = props
|
|
||||||
.and_then(|p| p.get("m_Children"))
|
|
||||||
.and_then(|v| v.as_array())
|
|
||||||
.map(|arr| {
|
|
||||||
arr.iter()
|
|
||||||
.filter_map(|item| item.as_file_ref())
|
|
||||||
.copied()
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
file_id: document.file_id,
|
|
||||||
game_object,
|
|
||||||
local_position,
|
local_position,
|
||||||
local_rotation,
|
local_rotation,
|
||||||
local_scale,
|
local_scale,
|
||||||
parent,
|
parent: None,
|
||||||
children,
|
children: Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,27 +89,28 @@ impl Transform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the parent transform reference
|
/// Get the parent transform reference
|
||||||
pub fn parent(&self) -> Option<FileRef> {
|
pub fn parent(&self) -> Option<Entity> {
|
||||||
self.parent
|
self.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the parent transform entity
|
||||||
|
pub fn set_parent(&mut self, parent: Option<Entity>) {
|
||||||
|
self.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the list of child transform references
|
/// Get the list of child transform references
|
||||||
pub fn children(&self) -> &[FileRef] {
|
pub fn children(&self) -> &[Entity] {
|
||||||
&self.children
|
&self.children
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for Transform {
|
/// Set the child transform entities
|
||||||
fn game_object(&self) -> Option<FileRef> {
|
pub fn set_children(&mut self, children: Vec<Entity>) {
|
||||||
self.game_object
|
self.children = children;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_enabled(&self) -> bool {
|
/// Add a child transform entity
|
||||||
true // Transforms are always enabled
|
pub fn add_child(&mut self, child: Entity) {
|
||||||
}
|
self.children.push(child);
|
||||||
|
|
||||||
fn file_id(&self) -> FileID {
|
|
||||||
self.file_id
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,33 +236,35 @@ impl RectTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the parent transform reference (from Transform)
|
/// Get the parent transform reference (from Transform)
|
||||||
pub fn parent(&self) -> Option<FileRef> {
|
pub fn parent(&self) -> Option<Entity> {
|
||||||
self.transform.parent()
|
self.transform.parent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the parent transform entity (from Transform)
|
||||||
|
pub fn set_parent(&mut self, parent: Option<Entity>) {
|
||||||
|
self.transform.set_parent(parent);
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the list of child transform references (from Transform)
|
/// Get the list of child transform references (from Transform)
|
||||||
pub fn children(&self) -> &[FileRef] {
|
pub fn children(&self) -> &[Entity] {
|
||||||
self.transform.children()
|
self.transform.children()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for RectTransform {
|
/// Set the child transform entities (from Transform)
|
||||||
fn game_object(&self) -> Option<FileRef> {
|
pub fn set_children(&mut self, children: Vec<Entity>) {
|
||||||
self.transform.game_object()
|
self.transform.set_children(children);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_enabled(&self) -> bool {
|
/// Add a child transform entity (from Transform)
|
||||||
self.transform.is_enabled()
|
pub fn add_child(&mut self, child: Entity) {
|
||||||
}
|
self.transform.add_child(child);
|
||||||
|
|
||||||
fn file_id(&self) -> FileID {
|
|
||||||
self.transform.file_id()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::model::UnityDocument;
|
||||||
use crate::property::PropertyValue;
|
use crate::property::PropertyValue;
|
||||||
use crate::types::FileID;
|
use crate::types::FileID;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
@@ -303,11 +285,6 @@ mod tests {
|
|||||||
"m_LocalScale".to_string(),
|
"m_LocalScale".to_string(),
|
||||||
PropertyValue::Vector3(Vector3::ONE),
|
PropertyValue::Vector3(Vector3::ONE),
|
||||||
);
|
);
|
||||||
transform_props.insert(
|
|
||||||
"m_GameObject".to_string(),
|
|
||||||
PropertyValue::FileRef(crate::types::FileRef::new(FileID::from_i64(67890))),
|
|
||||||
);
|
|
||||||
transform_props.insert("m_Children".to_string(), PropertyValue::Array(vec![]));
|
|
||||||
|
|
||||||
properties.insert("Transform".to_string(), PropertyValue::Object(transform_props));
|
properties.insert("Transform".to_string(), PropertyValue::Object(transform_props));
|
||||||
|
|
||||||
@@ -399,13 +376,6 @@ mod tests {
|
|||||||
assert_eq!(scale, &Vector3::ONE);
|
assert_eq!(scale, &Vector3::ONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_transform_file_id() {
|
|
||||||
let doc = create_test_transform();
|
|
||||||
let transform = Transform::parse(&doc).unwrap();
|
|
||||||
assert_eq!(transform.file_id().as_i64(), 12345);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rect_transform_creation() {
|
fn test_rect_transform_creation() {
|
||||||
let doc = create_test_rect_transform();
|
let doc = create_test_rect_transform();
|
||||||
|
|||||||
Reference in New Issue
Block a user