aframe 0.1.10

High-level Aframe VR bindings for Rust WASM
Documentation
# 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

Currently, this crate doesn't contain any features to initialize Aframe itself, so in your HTML header you ought to include:

```html
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
```

Beyond that, 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!](macro.scene.html)    

## Components
Defining a new component:
[component_def!](macro.component_def.html)   

Declaring the structure of a defined component:
[component_struct!](macro.component_struct.html)   
[simple_enum!](macro.simple_enum.html)   
[complex_enum!](macro.complex_enum.html)   

Instantiating a component struct:
[component!](macro.component.html)   
 
See the [component](component/index.html) module for more information and for 
pre-defined component constants.

## Entities & Primitives
Instantiating an entity or defined primitive
[entity!](macro.entity.html)   

Defining a new primitive:
[primitive!](macro.primitive.html)   

## Shaders

[Shader](shader/struct.Shader.html)   

## Htmlify

The `Htmlify` trait is is to generate raw HTML from the structures provided in this crate. This may eventually be abstracted into a separate crate. (TBD: Is there a better crate in existence already?)

[Htmlify](utils/htmlify/trait.Htmlify.html)    

## Assets

The `assets!` and `mixin!` macros are provided to define an `Assets` struct. Their signatures are as follows:

[assets!](macro.assets.html)    
[mixin!](macro.mixin.html)    

## Sys API

The lowest-level calls to Aframe are defined in the `sys` module:

[registerPrimitive](sys/fn.registerPrimitive.html)
[registerComponent](sys/fn.registerComponent.html)
[registerShader](sys/fn.registerShader.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`. This allows you to write a yew component as such:

```ignore
static INIT: AtomicBool = AtomicBool::new(false);

#[derive(Clone, PartialEq, Properties)]
pub struct AframeProps
{
    scene: aframe::Scene
}

pub struct Aframe
{
    props: AframeProps
}

impl crate::utils::Component for Aframe 
{
    type Message = Msg;
    type Properties = AframeProps;

    fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self 
    {
        // Register aframe stuff first time only
        if !INIT.load(Ordering::Relaxed)
        {
            unsafe 
            {
                // Code in this block registers shaders, components, and primitives with aframe
                shaders::register_shaders(); 
                component::register_components();
                primitive::register_primitives();
            }
            INIT.store(true, Ordering::Relaxed)
        }
        Self 
        { 
            props
        }
    }

    fn update(&mut self, _: Self::Message) -> ShouldRender 
    {
        true
    }

    fn change(&mut self, _props: Self::Properties) -> ShouldRender 
    {
        false
    }

    fn view(&self) -> Html 
    {
        (&self.props.scene).into()
    }
}
```

Below is a full definition of how the scene is defined in yew (this also serves of a valid example of how to use the `scene!` macro even outside of a yew context):

```ignore
html!
{
    <Aframe scene = 
    { 
        // Using this contant to clean up some fluff in the code below.
        const CURSOR_COLOR: [(Cow<'static, str>, 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")))))
                })
            }
        }
    } />
}
```