Create a common-api crate to make it easier to write a wasm guest
This commit is contained in:
parent
27c8f93611
commit
a58dcfb53d
|
@ -0,0 +1,147 @@
|
||||||
|
use crate::{lyra::api::ecs::ComponentInfo, Component, ComponentSerializationError};
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ComponentBundle {
|
||||||
|
pub(crate) bundle: Vec<u8>,
|
||||||
|
pub(crate) infos: Vec<ComponentInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentBundle {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push<C: Component>(&mut self, c: C) {
|
||||||
|
let info = C::component_info();
|
||||||
|
self.infos.push(info);
|
||||||
|
|
||||||
|
c.to_bytes_into(&mut self.bundle).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Bundle: Sized {
|
||||||
|
fn component_info() -> Vec<ComponentInfo>;
|
||||||
|
fn to_bytes(self) -> Result<Vec<u8>, ComponentSerializationError>;
|
||||||
|
fn from_bytes(bytes: Vec<u8>) -> Result<Self, ComponentSerializationError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_into_bundle_tuple {
|
||||||
|
($last: tt, $( $name: tt ),+) => {
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
impl<$($name: Component,)* $last: Component> Bundle for ($($name,)* $last,) {
|
||||||
|
fn component_info() -> Vec<ComponentInfo> {
|
||||||
|
vec![$($name::component_info(),)+ $last::component_info()]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_bytes(self) -> Result<Vec<u8>, ComponentSerializationError> {
|
||||||
|
let ($($name,)+ $last,) = self;
|
||||||
|
|
||||||
|
let mut bytes = vec![];
|
||||||
|
$(bytes.extend($name.to_bytes()?.into_iter());)+
|
||||||
|
bytes.extend($last.to_bytes()?.into_iter());
|
||||||
|
|
||||||
|
Ok(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_bytes(bytes: Vec<u8>) -> Result<Self, ComponentSerializationError> {
|
||||||
|
let len = {
|
||||||
|
let mut acc = 0;
|
||||||
|
$(acc += $name::component_info().size;)+
|
||||||
|
acc
|
||||||
|
};
|
||||||
|
if bytes.len() < len as _ {
|
||||||
|
return Err(ComponentSerializationError::ExpectedEndOfBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut bytes = VecDeque::from(bytes);
|
||||||
|
|
||||||
|
$(
|
||||||
|
let info = $name::component_info();
|
||||||
|
let mut part_bytes = Vec::new();
|
||||||
|
part_bytes.resize(info.size as _, 0u8);
|
||||||
|
bytes.read_exact(&mut part_bytes)
|
||||||
|
.map_err(|_| ComponentSerializationError::ExpectedEndOfBuffer)?;
|
||||||
|
|
||||||
|
let $name = $name::from_bytes(&part_bytes)?;
|
||||||
|
)+
|
||||||
|
|
||||||
|
let info = $last::component_info();
|
||||||
|
let mut part_bytes = Vec::new();
|
||||||
|
part_bytes.resize(info.size as _, 0u8);
|
||||||
|
bytes.read_exact(&mut part_bytes)
|
||||||
|
.map_err(|_| ComponentSerializationError::ExpectedEndOfBuffer)?;
|
||||||
|
|
||||||
|
let $last = $last::from_bytes(&part_bytes)?;
|
||||||
|
|
||||||
|
Ok(($($name,)+ $last,))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_into_bundle_tuple!($( $name ),+);
|
||||||
|
};
|
||||||
|
|
||||||
|
($only: tt) => {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
impl<$only: Component> Bundle for ($only,) {
|
||||||
|
fn component_info() -> Vec<ComponentInfo> {
|
||||||
|
vec![$only::component_info()]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_bytes(self) -> Result<Vec<u8>, ComponentSerializationError> {
|
||||||
|
let mut bytes = vec![];
|
||||||
|
bytes.extend(self.0.to_bytes()?.into_iter());
|
||||||
|
|
||||||
|
Ok(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_bytes(bytes: Vec<u8>) -> Result<Self, ComponentSerializationError> {
|
||||||
|
let mut bytes = VecDeque::from(bytes);
|
||||||
|
|
||||||
|
let info = $only::component_info();
|
||||||
|
let mut part_bytes = Vec::new();
|
||||||
|
part_bytes.resize(info.size as _, 0u8);
|
||||||
|
bytes.read_exact(&mut part_bytes)
|
||||||
|
.map_err(|_| ComponentSerializationError::ExpectedEndOfBuffer)?;
|
||||||
|
|
||||||
|
let $only = $only::from_bytes(&part_bytes)?;
|
||||||
|
|
||||||
|
Ok(($only,))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
impl<$only: Component> Bundle for $only {
|
||||||
|
fn component_info() -> Vec<ComponentInfo> {
|
||||||
|
vec![$only::component_info()]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_bytes(self) -> Result<Vec<u8>, ComponentSerializationError> {
|
||||||
|
let mut bytes = vec![];
|
||||||
|
bytes.extend(<Self as Component>::to_bytes(&self)?.into_iter());
|
||||||
|
|
||||||
|
Ok(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_bytes(bytes: Vec<u8>) -> Result<Self, ComponentSerializationError> {
|
||||||
|
let mut bytes = VecDeque::from(bytes);
|
||||||
|
|
||||||
|
let info = $only::component_info();
|
||||||
|
let mut part_bytes = Vec::new();
|
||||||
|
part_bytes.resize(info.size as _, 0u8);
|
||||||
|
bytes.read_exact(&mut part_bytes)
|
||||||
|
.map_err(|_| ComponentSerializationError::ExpectedEndOfBuffer)?;
|
||||||
|
|
||||||
|
let $only = $only::from_bytes(&part_bytes)?;
|
||||||
|
|
||||||
|
Ok($only)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//impl_into_bundle_tuple!(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16 );
|
||||||
|
impl_into_bundle_tuple!(C1, C2);
|
|
@ -0,0 +1,70 @@
|
||||||
|
use std::alloc::Layout;
|
||||||
|
|
||||||
|
use crate::lyra::api::ecs::{ComponentInfo, WasmTypeId};
|
||||||
|
|
||||||
|
impl ComponentInfo {
|
||||||
|
pub fn of<C: Component>() -> Self {
|
||||||
|
let layout = Layout::new::<C>();
|
||||||
|
Self {
|
||||||
|
size: layout.size() as _,
|
||||||
|
alignment: layout.align() as _,
|
||||||
|
type_id: WasmTypeId::of::<C>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ComponentSerializationMethod {
|
||||||
|
/// Serializes the component into bytes using [`bytemuck::bytes_of`].
|
||||||
|
Bytemuck,
|
||||||
|
/// Serializes the component into bytes using
|
||||||
|
/// [bincode](https://github.com/bincode-org/bincode).
|
||||||
|
Bincode
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum ComponentSerializationError {
|
||||||
|
#[error("{0}")]
|
||||||
|
Bytemuck(bytemuck::PodCastError),
|
||||||
|
#[error("unexpected end of buffer")]
|
||||||
|
ExpectedEndOfBuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Component: Sized + 'static {
|
||||||
|
const SERIALIZATION: ComponentSerializationMethod;
|
||||||
|
|
||||||
|
fn component_info() -> ComponentInfo;
|
||||||
|
fn to_bytes(&self) -> Result<Vec<u8>, ComponentSerializationError>;
|
||||||
|
fn to_bytes_into(&self, target: &mut Vec<u8>) -> Result<(), ComponentSerializationError>;
|
||||||
|
|
||||||
|
fn from_bytes(bytes: &[u8]) -> Result<Self, ComponentSerializationError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bytemuck_component_impl {
|
||||||
|
($type:ident) => {
|
||||||
|
impl common_api::Component for $type {
|
||||||
|
const SERIALIZATION: common_api::ComponentSerializationMethod = common_api::ComponentSerializationMethod::Bytemuck;
|
||||||
|
|
||||||
|
fn component_info() -> common_api::bindings::lyra::api::ecs::ComponentInfo {
|
||||||
|
common_api::bindings::lyra::api::ecs::ComponentInfo::of::<$type>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_bytes(&self) -> Result<Vec<u8>, common_api::ComponentSerializationError> {
|
||||||
|
Ok(common_api::bytemuck::bytes_of(self).to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_bytes_into(&self, target: &mut Vec<u8>) -> Result<(), common_api::ComponentSerializationError> {
|
||||||
|
let bytes = self.to_bytes()?;
|
||||||
|
target.extend(bytes.into_iter());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_bytes(bytes: &[u8]) -> Result<Self, common_api::ComponentSerializationError> {
|
||||||
|
common_api::bytemuck::try_from_bytes::<Self>(&bytes)
|
||||||
|
.map_err(|e| common_api::ComponentSerializationError::Bytemuck(e))
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,24 +1,23 @@
|
||||||
/* pub mod bindings {
|
use std::{any::TypeId, marker::PhantomData, mem};
|
||||||
wasmtime::component::bindgen!({
|
|
||||||
world: "example",
|
|
||||||
path: "../witguest/wit/world.wit",
|
|
||||||
//tracing: true,
|
|
||||||
async: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
use bindings::{component::witguest::ecs::EcsWorld, *}; */
|
|
||||||
|
|
||||||
use std::{alloc::Layout, any::TypeId, marker::PhantomData, mem};
|
|
||||||
|
|
||||||
pub use bytemuck;
|
pub use bytemuck;
|
||||||
|
|
||||||
wit_bindgen::generate!({
|
mod component;
|
||||||
world: "api",
|
pub use component::*;
|
||||||
path: "wit",
|
|
||||||
});
|
|
||||||
|
|
||||||
use lyra::api::ecs::{ComponentInfo, EcsDynamicView, EcsWorld, Entity, WasmTypeId};
|
mod bundle;
|
||||||
|
pub use bundle::*;
|
||||||
|
|
||||||
|
pub mod bindings {
|
||||||
|
wit_bindgen::generate!({
|
||||||
|
world: "api",
|
||||||
|
path: "wit",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
use bindings::*;
|
||||||
|
|
||||||
|
use lyra::api::ecs::{EcsDynamicView, EcsWorld, Entity, WasmTypeId};
|
||||||
|
|
||||||
impl WasmTypeId {
|
impl WasmTypeId {
|
||||||
pub fn of<T: 'static>() -> Self {
|
pub fn of<T: 'static>() -> Self {
|
||||||
|
@ -28,95 +27,6 @@ impl WasmTypeId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComponentInfo {
|
|
||||||
pub fn of<C: Component>() -> Self {
|
|
||||||
let layout = Layout::new::<C>();
|
|
||||||
Self {
|
|
||||||
size: layout.size() as _,
|
|
||||||
alignment: layout.align() as _,
|
|
||||||
type_id: WasmTypeId::of::<C>(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ComponentSerializationMethod {
|
|
||||||
/// Serializes the component into bytes using [`bytemuck::bytes_of`].
|
|
||||||
Bytemuck,
|
|
||||||
/// Serializes the component into bytes using
|
|
||||||
/// [bincode](https://github.com/bincode-org/bincode).
|
|
||||||
Bincode
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum ComponentSerializationError {
|
|
||||||
#[error("{0}")]
|
|
||||||
Bytemuck(bytemuck::PodCastError),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Component: Sized + 'static {
|
|
||||||
const SERIALIZATION: ComponentSerializationMethod;
|
|
||||||
|
|
||||||
fn component_info() -> ComponentInfo;
|
|
||||||
fn to_bytes(&self) -> Result<Vec<u8>, ComponentSerializationError>;
|
|
||||||
fn to_bytes_into(&self, target: &mut Vec<u8>) -> Result<(), ComponentSerializationError>;
|
|
||||||
|
|
||||||
fn from_bytes(bytes: &[u8]) -> Result<Self, ComponentSerializationError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! bytemuck_component_impl {
|
|
||||||
($type:ident) => {
|
|
||||||
impl common_api::Component for $type {
|
|
||||||
const SERIALIZATION: common_api::ComponentSerializationMethod = common_api::ComponentSerializationMethod::Bytemuck;
|
|
||||||
|
|
||||||
fn component_info() -> common_api::lyra::api::ecs::ComponentInfo {
|
|
||||||
common_api::lyra::api::ecs::ComponentInfo::of::<$type>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_bytes(&self) -> Result<Vec<u8>, common_api::ComponentSerializationError> {
|
|
||||||
Ok(common_api::bytemuck::bytes_of(self).to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_bytes_into(&self, target: &mut Vec<u8>) -> Result<(), common_api::ComponentSerializationError> {
|
|
||||||
let bytes = self.to_bytes()?;
|
|
||||||
target.extend(bytes.into_iter());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_bytes(bytes: &[u8]) -> Result<Self, common_api::ComponentSerializationError> {
|
|
||||||
common_api::bytemuck::try_from_bytes::<Self>(&bytes)
|
|
||||||
.map_err(|e| common_api::ComponentSerializationError::Bytemuck(e))
|
|
||||||
.cloned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ComponentBundle {
|
|
||||||
bundle: Vec<u8>,
|
|
||||||
infos: Vec<ComponentInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComponentBundle {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push<C: Component>(&mut self, c: C) {
|
|
||||||
let info = C::component_info();
|
|
||||||
self.infos.push(info);
|
|
||||||
|
|
||||||
c.to_bytes_into(&mut self.bundle);
|
|
||||||
/* self.bundle.reserve(info.size as _);
|
|
||||||
self.bundle.extend(comp_bytes.into_iter()); */
|
|
||||||
|
|
||||||
// dont call the destructor on C to avo
|
|
||||||
//mem::forget(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct World {
|
pub struct World {
|
||||||
inner: EcsWorld,
|
inner: EcsWorld,
|
||||||
}
|
}
|
||||||
|
@ -128,33 +38,48 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn(&self, components: ComponentBundle) -> Entity {
|
pub fn spawn<B: Bundle>(&self, bundle: B) -> Result<Entity, ComponentSerializationError> {
|
||||||
self.inner.spawn(&components.bundle, &components.infos)
|
let infos = B::component_info();
|
||||||
|
let bundle = bundle.to_bytes()?;
|
||||||
|
Ok(self.inner.spawn(&bundle, &infos))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view(&self, components: &[ComponentInfo]) -> View {
|
pub fn view<B: Bundle>(&self) -> View<B> {
|
||||||
View::from(self.inner.view(components))
|
let infos = B::component_info();
|
||||||
|
View::from(self.inner.view(&infos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct View {
|
pub struct View<B: Bundle> {
|
||||||
inner: EcsDynamicView,
|
inner: EcsDynamicView,
|
||||||
|
_marker: PhantomData<B>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<EcsDynamicView> for View {
|
impl<B: Bundle> From<EcsDynamicView> for View<B> {
|
||||||
fn from(value: EcsDynamicView) -> Self {
|
fn from(value: EcsDynamicView) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: value
|
inner: value,
|
||||||
|
_marker: PhantomData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View {
|
impl<B: Bundle> View<B> {
|
||||||
pub fn next<C: Component>(&mut self) -> Option<C> {
|
pub fn into_dynamic(self) -> EcsDynamicView {
|
||||||
let row = self.inner.next();
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(row) = row {
|
pub fn as_dynamic(&self) -> &EcsDynamicView {
|
||||||
Some(C::from_bytes(&row).unwrap())
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: Bundle> Iterator for View<B> {
|
||||||
|
type Item = Result<B, ComponentSerializationError>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if let Some(row) = self.inner.next() {
|
||||||
|
Some(B::from_bytes(row))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,26 +11,49 @@ interface ecs {
|
||||||
}
|
}
|
||||||
|
|
||||||
record wasm-type-id {
|
record wasm-type-id {
|
||||||
|
// represents a u128, can be converted into that with mem::transmute
|
||||||
inner: tuple<u64, u64>,
|
inner: tuple<u64, u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
record component-info {
|
record component-info {
|
||||||
|
/// The size of the component in memory.
|
||||||
size: u64,
|
size: u64,
|
||||||
|
/// The alignment of the component in memory.
|
||||||
alignment: u64,
|
alignment: u64,
|
||||||
|
/// The type id of the component.
|
||||||
|
///
|
||||||
|
/// This must be unique between component types since its used to identify the components
|
||||||
|
/// in spawning and querying.
|
||||||
type-id: wasm-type-id, // a u128
|
type-id: wasm-type-id, // a u128
|
||||||
}
|
}
|
||||||
|
|
||||||
resource ecs-dynamic-view {
|
resource ecs-dynamic-view {
|
||||||
constructor(wrld: borrow<ecs-world>, component-infos: list<component-info>);
|
constructor(wrld: borrow<ecs-world>, component-infos: list<component-info>);
|
||||||
|
|
||||||
|
/// Get the bytes of the next row in the view.
|
||||||
|
///
|
||||||
|
/// A row contains multiple component serialized as bytes. The buffer is tighly packed.
|
||||||
next: func() -> option<list<u8>>;
|
next: func() -> option<list<u8>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
resource ecs-world {
|
resource ecs-world {
|
||||||
constructor();
|
constructor();
|
||||||
|
|
||||||
// expects components to be tightly packed in the same order of component-infos
|
/// Spawn an entity.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// * `components`: A tightly packed byte buffer containing the components to spawn
|
||||||
|
/// with the entity. This expects the components in the same order of `component-infos`.
|
||||||
|
/// * `component-infos`: A list of `component-infos` uses to identify the components
|
||||||
|
/// and specify the layouts of them.
|
||||||
spawn: func(components: list<u8>, component-infos: list<component-info>) -> entity;
|
spawn: func(components: list<u8>, component-infos: list<component-info>) -> entity;
|
||||||
|
|
||||||
|
/// Query for a list of entities and their components.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// * `component-infos`: The `component-info`'s of the components that you are querying.
|
||||||
|
///
|
||||||
|
/// Returns: an iterator that returns the byte buffers of each row.
|
||||||
view: func(component-infos: list<component-info>) -> ecs-dynamic-view;
|
view: func(component-infos: list<component-info>) -> ecs-dynamic-view;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -45,7 +45,7 @@ impl From<ecs::Entity> for wasm_ecs::Entity {
|
||||||
|
|
||||||
impl Into<ecs::Entity> for wasm_ecs::Entity {
|
impl Into<ecs::Entity> for wasm_ecs::Entity {
|
||||||
fn into(self) -> ecs::Entity {
|
fn into(self) -> ecs::Entity {
|
||||||
let mut id = ecs::EntityId(self.id.id);
|
let id = ecs::EntityId(self.id.id);
|
||||||
ecs::Entity::new(id, self.generation)
|
ecs::Entity::new(id, self.generation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,7 @@
|
||||||
mod bindings;
|
mod bindings;
|
||||||
|
|
||||||
use bindings::Guest;
|
use bindings::Guest;
|
||||||
//use bindings::{component::witguest::ecs::{ComponentInfo, EcsWorld, Entity, WasmTypeId}, Guest};
|
use common_api::{bytemuck_component_impl, World};
|
||||||
use common_api::{bytemuck_component_impl, Component as EcsComponent, ComponentBundle, World};
|
|
||||||
|
|
||||||
use std::{alloc::Layout, any::TypeId, mem};
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
@ -25,22 +22,39 @@ impl Vec3 {
|
||||||
|
|
||||||
bytemuck_component_impl!(Vec3);
|
bytemuck_component_impl!(Vec3);
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
struct Vec4 {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
z: f32,
|
||||||
|
w: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vec4 {
|
||||||
|
pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
x, y, z, w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytemuck_component_impl!(Vec4);
|
||||||
|
|
||||||
struct Component;
|
struct Component;
|
||||||
|
|
||||||
impl Guest for Component {
|
impl Guest for Component {
|
||||||
fn on_init() -> Result<(), ()> {
|
fn on_init() -> Result<(), ()> {
|
||||||
let world = World::new();
|
let world = World::new();
|
||||||
|
let en = world.spawn((Vec3::new(7.0, 30.0, 18.0), Vec4::new(10.0, 332.35, 329.0, 95.0)));
|
||||||
let mut bundle = ComponentBundle::new();
|
|
||||||
bundle.push(Vec3::new(7.0, 30.0, 18.0));
|
|
||||||
let en = world.spawn(bundle);
|
|
||||||
|
|
||||||
println!("guest spawned {:?}", en);
|
println!("guest spawned {:?}", en);
|
||||||
|
|
||||||
|
let view = world.view::<(Vec3, Vec4)>();
|
||||||
let mut view = world.view(&[Vec3::component_info()]);
|
for pos in view {
|
||||||
while let Some(c) = view.next::<Vec3>() {
|
let (p3, p4) = pos.unwrap();
|
||||||
println!("Retrieved vec3: {:?}", c);
|
println!("Retrieved vec3: {:?}", p3);
|
||||||
|
println!("Retrieved vec4: {:?}", p4);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in New Issue