babylon.rs icon indicating copy to clipboard operation
babylon.rs copied to clipboard

A WebAssembly wrapper for babylon.js in Rust

babylon.rs

docs.rs docs

A WebAssembly wrapper for babylon.js in Rust.

This project is pre-alpha and the api is in active exploration. Current priorities:

  • get a basic GLTF up
  • get a camera
  • get some sort of interaction

This project uses js_ffi for javascript binding and lazy_static for global static singletons fairly extensively.

Idioms

  • Scenes hold 3D objects
  • Materials determine how a 3D object looks
  • When an 3D object drops it's removed from the scene

HelloWorld

use babylon::prelude::*;
#[macro_use]
extern crate lazy_static;
use std::sync::Mutex;

lazy_static! {
    static ref GAME: Mutex<Game> = Mutex::new(Game::new());
}

struct Game {
    scene: Scene,
    shape: Vec<Sphere>,
}

impl Game {
    fn new() -> Self {
        Game {
            scene: Scene::create_from_basic_engine("#renderCanvas"),
            shape: vec![],
        }
    }
}

#[no_mangle]
pub fn main() {
    babylon::js::log("Starting demo...");
    let mut game = GAME.lock().unwrap();
    for _ in 0..10 {
        let mut sphere = Sphere::new(&game.scene, babylon::js::random());
        sphere.set_position(Vector::new(
            babylon::js::random() - 0.5,
            babylon::js::random() - 0.5,
            babylon::js::random() - 0.5,
        ));
        game.shape.push(sphere);
    }
}

See this demo here ( be sure to play with mouse and arrow keys :arrow_left: :arrow_up: :arrow_down: :arrow_right:!)

Materials

let mut game = GAME.lock().unwrap();
for _ in 0..10 {
    let mut cube = Cube::new(
        &game.scene,
        babylon::js::random(),
        babylon::js::random(),
        babylon::js::random(),
    );
    let mut mat = StandardMaterial::new(&game.scene);
    mat.set_diffuse_color(Color::new(babylon::js::random(),babylon::js::random(),babylon::js::random()));
    mat.set_alpha(babylon::js::random());
    cube.set_material(&mat);
    cube.set_position(Vector::new(
        babylon::js::random() - 0.5,
        babylon::js::random() - 0.5,
        babylon::js::random() - 0.5,
    ));
    game.shape.push(cube);
}

See this demo here ( be sure to play with mouse and arrow keys :arrow_left: :arrow_up: :arrow_down: :arrow_right:!)

GLTF

let mut game = GAME.lock().unwrap();
let mut gltf = GLTF::new(&game.scene, "BoomBox.gltf");
gltf.set_scaling(Vector::new(50.0, 50.0, 50.0));

See this demo here ( be sure to play with mouse and arrow keys :arrow_left: :arrow_up: :arrow_down: :arrow_right:!)

Pong in 100 Lines

use babylon::prelude::*;

struct Game {
    scene: Scene,
    _camera: Camera,
    _light_1: HemisphericLight,
    _light_2: PointLight,
    ball: Sphere,
    paddle_1: Cube,
    paddle_2: Cube,
    paddle_dir: f64,
    ball_dir: Vector,
}

impl Default for Game {
    fn default() -> Self {
        let mut scene = Scene::new("#renderCanvas");
        scene.set_clear_color(Color::new(0.0, 0.0, 0.0));
        let _camera = Camera::new(&scene);
        let _light_1 = HemisphericLight::new(&scene);
        let _light_2 = PointLight::new(&scene);
        let ball = Sphere::new(&scene, 0.05);
        let mut paddle_mat = StandardMaterial::new(&scene);
        paddle_mat.set_diffuse_color(Color::new(0.5, 0.5, 0.5));
        paddle_mat.set_emmisive_color(Color::new(0.5, 0.5, 0.5));
        let mut paddle_1 = Cube::new(&scene, 0.5, 0.05, 0.05);
        paddle_1.set_position(Vector::new(0.0, 0.5, 0.0));
        paddle_1.set_material(&paddle_mat);
        let mut paddle_2 = Cube::new(&scene, 0.5, 0.05, 0.05);
        paddle_2.set_position(Vector::new(0.0, -0.5, 0.0));
        paddle_2.set_material(&paddle_mat);
        Game {
            scene,
            _camera,
            _light_1,
            _light_2,
            ball,
            paddle_1,
            paddle_2,
            paddle_dir: 0.0,
            ball_dir: Vector::new(babylon::js::random() - 0.5, -1.0, 0.0),
        }
    }
}

impl BasicGame for Game {
    fn get_scene(&self) -> &Scene {
        &self.scene
    }

    fn run(&mut self, delta_time: f64) {
        // get positions
        let p2 = self.paddle_2.get_position();
        let bp = self.ball.get_position();

        // move ball
        let b_x = self.ball_dir.x * delta_time + bp.x;
        let b_y = self.ball_dir.y * delta_time + bp.y;
        if b_x > 0.5 || b_x < -0.5 {
            self.ball_dir.x = -self.ball_dir.x;
        }

        if b_y > 0.75 || b_y < -0.75 {
            self.ball.set_position(Vector::new(0.0, 0.0, 0.0));
            self.ball_dir.x = babylon::js::random() - 0.5;
            self.ball_dir.y = -self.ball_dir.y;
        } else if b_y > 0.45 || (b_y < -0.45 && b_x <= p2.x + 0.25 && b_x >= p2.x - 0.25) {
            self.ball_dir.y = -self.ball_dir.y;
            self.ball.set_position(Vector::new(b_x, b_y, 0.0));
        } else {
            self.ball.set_position(Vector::new(b_x, b_y, 0.0));
        }

        // move opponent paddle to match ball
        self.paddle_1.set_position_x(b_x);

        // move paddle if it has velocity
        if self.paddle_dir != 0.0 {
            let p2_x = p2.x + delta_time * self.paddle_dir;
            self.paddle_2.set_position_x(p2_x)
        }
    }

    fn key_up(&mut self, _key_code: f64) {
        self.paddle_dir = 0.0;
    }

    fn key_down(&mut self, key_code: f64) {
        if key_code == 37.0 {
            self.paddle_dir = 1.0;
        } else if key_code == 39.0 {
            self.paddle_dir = -1.0;
        }
    }
}

#[no_mangle]
pub fn main() {
    run_basic_game::<Game>();
}

See this demo here

Other Demos

License

This project is licensed under either of

  • Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
  • MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in babylon.rs by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.