Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Initial WASM Web Runtime #6

Merged
merged 14 commits into from
Jun 4, 2024
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: build
name: Build Native

on:
push:
Expand Down Expand Up @@ -49,5 +49,10 @@ jobs:
- name: Build F#
run: npm run build:examples:pong:rust

- name: Build Rust
- name: Build Runtime
run: cargo build
working-directory: runtime/functor-runtime-desktop

- name: Build Pong
run: cargo build
working-directory: examples/Pong/build-native
46 changes: 46 additions & 0 deletions .github/workflows/build-wasm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Build WebAssembly

on:
push:
branches: [main]
pull_request:

jobs:
build-wasm:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
- uses: actions/checkout@v2

- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: "8.0.x"

- name: Restore tools
run: dotnet tool restore

- name: Setup Node.js environment
uses: actions/[email protected]

- name: Setup Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable

- name: Install wasm-pack
run: npm install -g wasm-pack

- name: Build F#
run: npm run build:examples:pong:rust

- name: Build runtime wasm bundle
run: wasm-pack build --target web
working-directory: runtime/functor-runtime-web

- name: Build pong wasm bundle
run: wasm-pack build --target web
working-directory: examples/Pong/build-wasm
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ resolver = "1"
members = [
"runtime/functor-runtime-common",
"runtime/functor-runtime-desktop",
"runtime/functor-runtime-web",
"src",
"examples",
"examples/Pong/build-native",
"examples/Pong/build-wasm",
]
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
[![Build Native](https://github.com/tommy-xr/functor/actions/workflows/build-native.yml/badge.svg)](https://github.com/tommy-xr/functor/actions/workflows/build-native.yml) [![Build WebAssembly](https://github.com/tommy-xr/functor/actions/workflows/build-wasm.yml/badge.svg)](https://github.com/tommy-xr/functor/actions/workflows/build-wasm.yml)

# functor

Functor: A functional toolkit for building 3D games in F#

### Online demo:

https://ncave.github.io/fable-raytracer/

### Build and run:

- install [.NET](https://dotnet.microsoft.com/en-us/download) and [Node.js](https://nodejs.org/en/)
- install [Rust](https://www.rust-lang.org/tools/install) and [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/)
- run `dotnet tool restore` to get [Fable](https://github.com/fable-compiler/Fable) installed locally
-
-
14 changes: 0 additions & 14 deletions examples/Cargo.toml

This file was deleted.

2 changes: 1 addition & 1 deletion examples/Pong/Pong.fs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ let init (_args: array<string>) =
game
|> GameBuilder.draw3d (fun model ->
printfn "Tick: %d" model.counter;
Graphics.Scene3D.sphere()
Graphics.Scene3D.cube()
)
|> GameBuilder.tick tick
|> Runtime.runGame
Expand Down
14 changes: 14 additions & 0 deletions examples/Pong/build-native/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "pong_native"
version = "0.1.0"
edition = "2021"

[lib]
name = "pong_native"
path = "../Pong.rs"
crate-type = ["dylib"]

[dependencies]
functor-lib = { path = "./../../../src" }
functor_runtime_common = { path = "./../../../runtime/functor-runtime-common" }
fable_library_rust = { path = "./../../../fable_modules/fable-library-rust" }
15 changes: 15 additions & 0 deletions examples/Pong/build-wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "pong_wasm"
version = "0.1.0"
edition = "2021"

[lib]
name = "pong_wasm"
path = "../Pong.rs"
crate-type = ["cdylib"]

[dependencies]
functor-lib = { path = "./../../../src" }
functor_runtime_common = { path = "./../../../runtime/functor-runtime-common" }
fable_library_rust = { path = "./../../../fable_modules/fable-library-rust" }
wasm-bindgen = { version = "0.2" }
6 changes: 6 additions & 0 deletions runtime/functor-runtime-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ cgmath = "0.18.0"
glow = "0.13.1"
image = "0.25.1"
once_cell = "1.19.0"
serde = { version = "1.0", features = ["derive"] }

# Conditionally include dependencies for the WebAssembly target
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
serde-wasm-bindgen = "0.4"
6 changes: 3 additions & 3 deletions runtime/functor-runtime-common/src/geometry/mesh.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use glow::{HasContext, NativeBuffer, NativeVertexArray};
use glow::{Buffer, HasContext, VertexArray};
use once_cell::sync::OnceCell;

use super::Geometry;

struct HydratedContext {
vbo: NativeBuffer,
vao: NativeVertexArray,
vbo: Buffer,
vao: VertexArray,
triangle_count: i32,
}

Expand Down
148 changes: 74 additions & 74 deletions runtime/functor-runtime-common/src/geometry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,77 +15,77 @@ pub use empty_mesh::*;
pub use mesh::*;
pub use sphere::*;

pub mod plane {
use glow::{HasContext, NativeBuffer, NativeVertexArray};
use once_cell::sync::OnceCell;

use super::Geometry;

static CUBE_GEOMETRY: OnceCell<(NativeBuffer, NativeVertexArray)> = OnceCell::new();

pub struct Plane {}

pub fn create() -> Plane {
Plane {}
}

impl Geometry for Plane {
fn draw(&mut self, gl: &glow::Context) {
let (_vbo, vao) = *CUBE_GEOMETRY.get_or_init(|| {
let uv_scale = 100.0;
let vertices: [f32; 30] = [
-0.5, 0.0, -0.5, 0.0, 0.0, 0.5, 0.0, -0.5, uv_scale, 0.0, 0.5, 0.0, 0.5,
uv_scale, uv_scale, -0.5, 0.0, -0.5, 0.0, 0.0, -0.5, 0.0, 0.5, 0.0, uv_scale,
0.5, 0.0, 0.5, uv_scale, uv_scale,
];

let (vbo, vao) = unsafe {
let vertices_u8: &[u8] = core::slice::from_raw_parts(
vertices.as_ptr() as *const u8,
vertices.len() * core::mem::size_of::<f32>(),
);

let vbo = gl.create_buffer().unwrap();
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
gl.buffer_data_u8_slice(glow::ARRAY_BUFFER, vertices_u8, glow::STATIC_DRAW);

let vao = gl.create_vertex_array().unwrap();
gl.bind_vertex_array(Some(vao));

gl.enable_vertex_attrib_array(0);
gl.vertex_attrib_pointer_f32(
0,
3,
glow::FLOAT,
false,
(5 * core::mem::size_of::<f32>()) as i32,
0,
);

gl.enable_vertex_attrib_array(1);
gl.vertex_attrib_pointer_f32(
1,
2,
glow::FLOAT,
false,
(5 * core::mem::size_of::<f32>()) as i32,
3,
);

// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
gl.bind_buffer(glow::ARRAY_BUFFER, None);
gl.bind_vertex_array(None);
(vbo, vao)
};

(vbo, vao)
});

unsafe {
gl.bind_vertex_array(Some(vao));
gl.draw_arrays(glow::TRIANGLES, 0, 6);
}
}
}
}
// pub mod plane {
// use glow::{HasContext, Buffer, VertexArray};
// use once_cell::sync::OnceCell;

// use super::Geometry;

// static CUBE_GEOMETRY: OnceCell<(NativeBuffer, NativeVertexArray)> = OnceCell::new();

// pub struct Plane {}

// pub fn create() -> Plane {
// Plane {}
// }

// impl Geometry for Plane {
// fn draw(&mut self, gl: &glow::Context) {
// let (_vbo, vao) = *CUBE_GEOMETRY.get_or_init(|| {
// let uv_scale = 100.0;
// let vertices: [f32; 30] = [
// -0.5, 0.0, -0.5, 0.0, 0.0, 0.5, 0.0, -0.5, uv_scale, 0.0, 0.5, 0.0, 0.5,
// uv_scale, uv_scale, -0.5, 0.0, -0.5, 0.0, 0.0, -0.5, 0.0, 0.5, 0.0, uv_scale,
// 0.5, 0.0, 0.5, uv_scale, uv_scale,
// ];

// let (vbo, vao) = unsafe {
// let vertices_u8: &[u8] = core::slice::from_raw_parts(
// vertices.as_ptr() as *const u8,
// vertices.len() * core::mem::size_of::<f32>(),
// );

// let vbo = gl.create_buffer().unwrap();
// gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
// gl.buffer_data_u8_slice(glow::ARRAY_BUFFER, vertices_u8, glow::STATIC_DRAW);

// let vao = gl.create_vertex_array().unwrap();
// gl.bind_vertex_array(Some(vao));

// gl.enable_vertex_attrib_array(0);
// gl.vertex_attrib_pointer_f32(
// 0,
// 3,
// glow::FLOAT,
// false,
// (5 * core::mem::size_of::<f32>()) as i32,
// 0,
// );

// gl.enable_vertex_attrib_array(1);
// gl.vertex_attrib_pointer_f32(
// 1,
// 2,
// glow::FLOAT,
// false,
// (5 * core::mem::size_of::<f32>()) as i32,
// 3,
// );

// // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
// // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
// gl.bind_buffer(glow::ARRAY_BUFFER, None);
// gl.bind_vertex_array(None);
// (vbo, vao)
// };

// (vbo, vao)
// });

// unsafe {
// gl.bind_vertex_array(Some(vao));
// gl.draw_arrays(glow::TRIANGLES, 0, 6);
// }
// }
// }
// }
25 changes: 24 additions & 1 deletion runtime/functor-runtime-common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
use serde::*;
use std::any::Any;

#[derive(Debug, Clone)]
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;

#[cfg(target_arch = "wasm32")]
pub fn to_js_value<T>(value: &T) -> JsValue
where
T: Serialize,
{
serde_wasm_bindgen::to_value(value).unwrap()
}

#[cfg(target_arch = "wasm32")]
pub fn from_js_value<T>(value: JsValue) -> T
where
T: for<'de> Deserialize<'de>,
{
serde_wasm_bindgen::from_value(value).unwrap()
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Scene3D {
Cube,
Sphere,
Expand Down Expand Up @@ -48,10 +68,13 @@ impl OpaqueState {

pub mod geometry;
pub mod material;
mod render_context;
mod shader;
mod shader_program;
pub mod texture;

pub use render_context::*;

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading
Loading