aframe-rs
This is an Aframe 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 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:
async fn app_main() -> Result<(), aframe::InitError>
{
aframe::init_aframe().await?;
}
You can also initialize simply by adding the Aframe script to your HTML header:
<script src="https://aframe.io/releases/1.6.0/aframe.min.js"></script>
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!
Components
Defining a new component:
component_def!
Declaring the structure of a defined component:
component_struct!
simple_enum!
complex_enum!
Instantiating a component struct:
component!
See the component module for more information and for
pre-defined component structs.
Custom Geometry
Defining a new custom geometry:
geometry_def!
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!
Defining a new primitive:
primitive!
Assets
The assets! and mixin! macros are provided to define an Assets struct.
assets!
mixin!
Systems
system_def!
Shaders
Shader
Htmlify
The Htmlify trait is is to generate HTML from the structures provided in this crate. This was abstracted into a separate crate:
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:
let body = web_sys::window()?.document()?.body()?;
body.append_with_node_1(scene.as_element()?.as_ref())?;
htmlify::append_to_document_body(&scene);
Here's a basic example of a fully-functional page being created using wasm-bindgen-test:
test example
Sys API
The lowest-level calls to Aframe are defined in the sys module:
registerPrimitive
registerComponent
registerSystem
registerShader
registerGeometry
registerElement
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 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):
html!
{
<Aframe scene =
{
const CURSOR_COLOR: [(Cow<'static, str>, Cow<'static, str>); 1] =
[(Cow::Borrowed("color"), Cow::Borrowed("lightblue"))];
scene!
{
attributes: ("inspector", "true"), ("embedded", "true"), ("cursor", "rayOrigin: mouse"),
("mixin", "intersect_ray"), ("style", "min-height: 50px;"),
assets: assets!
{
Image::new("ramen", "/pics/ramen.png"),
Image::new("noise", "/pics/noise.bmp"),
mixin!
{
"intersect_ray",
("raycaster", component!
{
RayCaster,
objects: List(Cow::Borrowed(&[Cow::Borrowed("#ramen-cube, #water")]))
})
}
},
children:
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:
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
}),
children: entity!
{
primitive: "ramen-cube",
attributes: ("id", "ramen-cube"),
components: }
},
entity!
{
attributes: ("id", "ambient-light"),
components: ("light", component!
{
component::Light,
light_type: component::LightType::Ambient{},
color: color::GREY73,
intensity: 0.2
})
},
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
})
},
entity!
{
primitive: "a-sky",
attributes: ("id", "sky"),
components: ("material", component!
{
component::Material,
shader: Cow::Borrowed("strobe"),
props: component::MaterialProps(Cow::Owned(vec!
(
(Cow::Borrowed("color"), Cow::Borrowed("black")),
(Cow::Borrowed("color2"), Cow::Borrowed("#222222"))
)))
})
},
entity!
{
primitive: "a-ocean",
attributes: ("id", "water"), ("depth", "100"), ("width", "100"), ("amplitude", "0.5"),
components: ("material", component!
{
component::Material,
shader: Cow::Borrowed("water"),
props: component::MaterialProps(Cow::Owned(vec!((Cow::Borrowed("transparent"), Cow::Borrowed("true")))))
})
}
}
} />
}