# aframe-rs
This is an [Aframe](https://aframe.io/) library for rust. It's still fairly experimental and a lot might change. I started writing this for a bit of fun to see if I could play with aframe from inside a [yew](https://yew.rs/) app. It started getting pretty large so I decided to abstract away all the yew-specific stuff and start making a library on its own. There's still a bunch missing and a bunch to do here, but what IS there is functional.
# Setup
### Initialization
This crate contains an `init` feature which may be enabled to allow initialization from an async function:
```rust,ignore
async fn app_main() -> Result<(), aframe::InitError>
{
aframe::init_aframe().await?;
// ... Now you can safely continue
}
```
You can also initialize simply by adding the Aframe script to your HTML header:
```html
```
### Use
You can either use this crate's `Htmlify` trait to output raw html, or use the `yew-support` feature to create a yew componment (described lower in this readme) to output your actual Aframe scene.
# API
## Scene
Instantiating a scene:
[scene!](https://docs.rs/aframe/*/aframe/macro.scene.html)
## Components
Defining a new component:
[component_def!](https://docs.rs/aframe/*/aframe/macro.component_def.html)
Declaring the structure of a defined component:
[component_struct!](https://docs.rs/aframe/*/aframe/macro.component_struct.html)
[simple_enum!](https://docs.rs/aframe/*/aframe/macro.simple_enum.html)
[complex_enum!](https://docs.rs/aframe/*/aframe/macro.complex_enum.html)
Instantiating a component struct:
[component!](https://docs.rs/aframe/*/aframe/macro.component.html)
See the [component](https://docs.rs/aframe/*/aframe/component/) module for more information and for
pre-defined component structs.
## Custom Geometry
Defining a new custom geometry:
[geometry_def!](https://docs.rs/aframe/*/aframe/macro.geometry_def.html)
Not yet implemented:
* `geometry_struct!` macro to declare structure of custom geometry data
* `geometry!` macro to serve as a helper when instantiating custom geometry
## Entities & Primitives
Instantiating an entity or defined primitive:
[entity!](https://docs.rs/aframe/*/aframe/macro.entity.html)
Defining a new primitive:
[primitive!](https://docs.rs/aframe/*/aframe/macro.primitive.html)
## Assets
The `assets!` and `mixin!` macros are provided to define an `Assets` struct.
[assets!](https://docs.rs/aframe/*/aframe/macro.assets.html)
[mixin!](https://docs.rs/aframe/*/aframe/macro.mixin.html)
## Systems
[system_def!](https://docs.rs/aframe/*/aframe/macro.system_def.html)
## Shaders
[Shader](https://docs.rs/aframe/*/aframe/shader/struct.Shader.html)
## Htmlify
The `Htmlify` trait is is to generate HTML from the structures provided in this crate. This was abstracted into a separate crate:
[Htmlify](https://docs.rs/htmlify/*/htmlify/)
You can use this to plop a Scene directly into your DOM with the web_sys crate,
making this crate usable even without any supporting framework:
```rust,ignore
// Say we have some `Scene` structure already constructed:
let body = web_sys::window()?.document()?.body()?;
body.append_with_node_1(scene.as_element()?.as_ref())?;
// Or even simpler:
htmlify::append_to_document_body(&scene);
```
Here's a basic example of a fully-functional page being created using `wasm-bindgen-test`:
[test example](https://github.com/Eolu/aframe-rs/blob/80558fa699178724cbd93657207db994630b55f1/src/tests.rs#L27)
## Sys API
The lowest-level calls to Aframe are defined in the `sys` module:
[registerPrimitive](https://docs.rs/aframe/*/aframe/sys/fn.registerPrimitive.html)
[registerComponent](https://docs.rs/aframe/*/aframe/sys/fn.registerComponent.html)
[registerSystem](https://docs.rs/aframe/*/aframe/sys/fn.registerSystem.html)
[registerShader](https://docs.rs/aframe/*/aframe/sys/fn.registerShader.html)
[registerGeometry](https://docs.rs/aframe/*/aframe/sys/fn.registerGeometry.html)
[registerElement](https://docs.rs/aframe/*/aframe/sys/fn.registerElement.html)
## yew_support feature
The `yew_support` feature adds yew support to this crate. At its core, all this does is implement `From<&Scene> for Html` along with a few other conversions to yew's Html type.
See the [yew-ext module page](https://docs.rs/aframe/*/aframe/yew_ext/index.html) for an example.
# WIP/Missing Features
* Event handling
* State handling
* High-level support for custom geometry
* Access to Aframe utility functions
* Some component implementations are still accepting strings where they could accept enums or more specific structures
# Example
Below is a full example of how a scene is constructed in yew (this also serves of a valid example of how to use the `scene!` macro even outside of a yew context):
```rust,ignore
html!
{
, Cow<'static, str>); 1] =
[(Cow::Borrowed("color"), Cow::Borrowed("lightblue"))];
scene!
{
// TODO: Some of these attributes are actually components, they need to be implemented in the library!
attributes: ("inspector", "true"), ("embedded", "true"), ("cursor", "rayOrigin: mouse"),
("mixin", "intersect_ray"), ("style", "min-height: 50px;"),
assets: assets!
{
// Assume we have a few assets available to use
Image::new("ramen", "/pics/ramen.png"),
Image::new("noise", "/pics/noise.bmp"),
// Create a mixin for shadows to know what to interact with
mixin!
{
"intersect_ray",
("raycaster", component!
{
RayCaster,
objects: List(Cow::Borrowed(&[Cow::Borrowed("#ramen-cube, #water")]))
})
}
},
children:
// The camera rig
entity!
{
attributes: ("id", "rig"),
components:
("position", component::Position { x: 0.0, y: 0.0, z: 0.0 }),
("geometry", component!
{
component::Geometry,
primitive: component::GeometryPrimitive::Ring
{
radius_inner: 0.06,
radius_outer: 0.2,
segments_theta: 32,
segments_phi: 8,
theta_start: 0.0,
theta_length: 360.0
}
}),
("material", component!
{
component::Material,
props: component::MaterialProps(Cow::Borrowed(&CURSOR_COLOR)),
opacity: 0.8
}),
children:
// The camera
entity!
{
attributes: ("id", "camera"),
components:
("position", component::Position { x: 0.0, y: 1.8, z: 0.0 }),
("camera", component!(component::Camera)),
("look-controls", component!(component::LookControls))
},
},
entity!
{
attributes: ("id", "cube-rig"),
components:
("position", component::Position{x: 0.0, y: 2.5, z: -2.0}),
("sound", component!
{
component::Sound,
src: Cow::Borrowed("#ambient_music"),
volume: 0.5
}),
("light", component!
{
component::Light,
light_type: component::LightType::Point
{
decay: 1.0,
distance: 50.0,
shadow: component::OptionalLocalShadow::NoCast{},
},
intensity: 0.0
}),
("animation__mouseenter", component!
{
component::Animation,
property: Cow::Borrowed("light.intensity"),
to: Cow::Borrowed("1.0"),
start_events: component::List(Cow::Borrowed(&[Cow::Borrowed("mouseenter")])),
dur: 250
}),
("animation__mouseleave", component!
{
component::Animation,
property: Cow::Borrowed("light.intensity"),
to: Cow::Borrowed("0.0"),
start_events: component::List(Cow::Borrowed(&[Cow::Borrowed("mouseleave")])),
dur: 250
}),
// This assumes the existence of a primitive registered as "ramen-cube"
children: entity!
{
primitive: "ramen-cube",
attributes: ("id", "ramen-cube"),
components: // None
}
},
// Ambient light
entity!
{
attributes: ("id", "ambient-light"),
components: ("light", component!
{
component::Light,
light_type: component::LightType::Ambient{},
color: color::GREY73,
intensity: 0.2
})
},
// Directional light
entity!
{
attributes: ("id", "directional-light"),
components:
("position", component::Position{ x: 0.5, y: 1.0, z: 1.0 }),
("light", component!
{
component::Light,
light_type: component::LightType::Directional
{
shadow: component::OptionalDirectionalShadow::Cast
{
shadow: component!
{
component::DirectionalShadow
}
}
},
color: color::WHITE,
intensity: 0.1
})
},
// The sky
entity!
{
primitive: "a-sky",
attributes: ("id", "sky"),
components: ("material", component!
{
component::Material,
// This assumes the existence of a shader registered as "strobe"
shader: Cow::Borrowed("strobe"),
props: component::MaterialProps(Cow::Owned(vec!
(
(Cow::Borrowed("color"), Cow::Borrowed("black")),
(Cow::Borrowed("color2"), Cow::Borrowed("#222222"))
)))
})
},
// The ocean
entity!
{
primitive: "a-ocean",
attributes: ("id", "water"), ("depth", "100"), ("width", "100"), ("amplitude", "0.5"),
components: ("material", component!
{
component::Material,
// This assumes the existence of a shader registered as "water"
shader: Cow::Borrowed("water"),
props: component::MaterialProps(Cow::Owned(vec!((Cow::Borrowed("transparent"), Cow::Borrowed("true")))))
})
}
}
} />
}
```