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

enable wasm builds and automatically deploy the builds to github pages #24

Merged
merged 2 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.wasm32-unknown-unknown]
runner = "wasm-server-runner"
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,25 @@ jobs:
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libopenxr-loader1 libopenxr-dev
- name: Run cargo test
run: cargo test
- name: Install wasm libcore/libstd
if: matrix.os == 'ubuntu-latest'
run: rustup target install wasm32-unknown-unknown
- name: Install wasm-bindgen
if: matrix.os == 'ubuntu-latest'
run: cargo install wasm-bindgen-cli
- name: Build wasm
if: matrix.os == 'ubuntu-latest'
run: cargo build --release --target wasm32-unknown-unknown --bin demo && wasm-bindgen --out-dir ./web/ --target web ./target/wasm32-unknown-unknown/release/demo.wasm
- name: Push to gh-pages
if: matrix.os == 'ubuntu-latest'
uses: s0/git-publish-subdir-action@develop
env:
SQUASH_HISTORY: true
REPO: self
BRANCH: gh-pages # The branch name where you want to push the assets
FOLDER: web # The directory where your assets are generated
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GitHub will automatically add this - you don't need to bother getting a token
MESSAGE: "Build: ({sha}) {msg}" # The commit message

# Run cargo clippy -- -D warnings
clippy:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
/assets
/target
/z_Usefull
/web/*
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,12 @@ run `x doctor` to see what else you need to install for android, then run
```
x build --release --platform android --format apk --arch arm64
```
## wasm

To run the project in the browser locally, you need to do the following setup:

```bash
rustup target install wasm32-unknown-unknown
cargo install wasm-server-runner
cargo run --target wasm32-unknown-unknown
```
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,19 @@ futures-core = "0.3.29"
futures-io = "0.3.29"
## bevy_config_cam = { git = "https://github.com/BlackPhlox/bevy_config_cam" } ## { version = "0.3.0"}
glam = "0"
surf = { version = "2.3.2", default-features = false, features = ["h1-client-rustls"] }

[target.'cfg(not(target_os="macos"))'.dependencies]
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
surf = { version = "2.3.2", default-features = false, features = [
"h1-client-rustls",
] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = {version = "0.3.22", default-features = false}
js-sys = {version = "0.3", default-features = false}
wasm-bindgen = {version = "0.2", default-features = false}
wasm-bindgen-futures = "0.4"

[target.'cfg(not(any(target_os="macos", target_arch = "wasm32")))'.dependencies]
bevy_oxr = { git = "https://github.com/awtterpip/bevy_openxr", optional = true }

[features]
Expand Down
108 changes: 71 additions & 37 deletions src/http_assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,13 @@ use bevy::{
utils::BoxedFuture,
};
use flate2::read::GzDecoder;
use std::{
io::Read,
path::{Path, PathBuf},
};

use std::pin::Pin;
use std::task::Poll;
use std::{io::Read, path::Path};

/// A custom asset reader implementation that wraps a given asset reader implementation
pub struct HttpAssetReader {
#[cfg(target_arch = "wasm32")]
base_url: String,
#[cfg(not(target_arch = "wasm32"))]
client: surf::Client,
/// Whether to load tiles from this path
tile: bool,
Expand All @@ -30,16 +27,73 @@ pub struct HttpAssetReader {
impl HttpAssetReader {
/// Creates a new `HttpAssetReader`. The path provided will be used to build URLs to query for assets.
pub fn new(base_url: &str, tile: bool) -> Self {
let base_url = surf::Url::parse(base_url).expect("invalid base url");
#[cfg(not(target_arch = "wasm32"))]
{
let base_url = surf::Url::parse(base_url).expect("invalid base url");

let client = surf::Config::new().set_timeout(Some(std::time::Duration::from_secs(5)));
let client = client.set_base_url(base_url);
let client = surf::Config::new().set_timeout(Some(std::time::Duration::from_secs(5)));
let client = client.set_base_url(base_url);

let client = client.try_into().expect("could not create http client");
let client = client.try_into().expect("could not create http client");
Self { client, tile }
}
#[cfg(target_arch = "wasm32")]
{
Self {
base_url: base_url.into(),
tile,
}
}
}

Self { client, tile }
#[cfg(target_arch = "wasm32")]
async fn fetch_bytes<'a>(&self, path: &str) -> Result<Box<Reader<'a>>, AssetReaderError> {
use js_sys::Uint8Array;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::Response;

fn js_value_to_err<'a>(
context: &'a str,
) -> impl FnOnce(wasm_bindgen::JsValue) -> std::io::Error + 'a {
move |value| {
let message = match js_sys::JSON::stringify(&value) {
Ok(js_str) => format!("Failed to {context}: {js_str}"),
Err(_) => {
format!(
"Failed to {context} and also failed to stringify the JSValue of the error"
)
}
};

std::io::Error::new(std::io::ErrorKind::Other, message)
}
}

let window = web_sys::window().unwrap();
let resp_value =
JsFuture::from(window.fetch_with_str(&format!("{}/{path}", self.base_url)))
.await
.map_err(js_value_to_err("fetch path"))?;
let resp = resp_value
.dyn_into::<Response>()
.map_err(js_value_to_err("convert fetch to Response"))?;
match resp.status() {
200 => {
let data = JsFuture::from(resp.array_buffer().unwrap()).await.unwrap();
let bytes = Uint8Array::new(&data).to_vec();
let reader: Box<Reader> = Box::new(VecReader::new(bytes));
Ok(reader)
}
404 => Err(AssetReaderError::NotFound(path.into())),
status => Err(AssetReaderError::Io(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Encountered unexpected HTTP status {status}"),
))),
}
}

#[cfg(not(target_arch = "wasm32"))]
async fn fetch_bytes<'a>(&self, path: &str) -> Result<Box<Reader<'a>>, AssetReaderError> {
let resp = self.client.get(path).await;

Expand Down Expand Up @@ -75,19 +129,6 @@ impl HttpAssetReader {
}
}

struct EmptyPathStream;

impl futures_core::Stream for EmptyPathStream {
type Item = PathBuf;

fn poll_next(
self: Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> Poll<Option<Self::Item>> {
Poll::Ready(None)
}
}

impl AssetReader for HttpAssetReader {
fn read<'a>(
&'a self,
Expand Down Expand Up @@ -121,28 +162,21 @@ impl AssetReader for HttpAssetReader {
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
Box::pin(async move {
let path = path.display().to_string();
let meta_path = path + ".meta";
self.fetch_bytes(&meta_path).await
})
Box::pin(async move { Err(AssetReaderError::NotFound(path.into())) })
}

fn read_directory<'a>(
&'a self,
_path: &'a Path,
path: &'a Path,
) -> BoxedFuture<'a, Result<Box<PathStream>, AssetReaderError>> {
let stream: Box<PathStream> = Box::new(EmptyPathStream);
error!("Reading directories is not supported with the HttpAssetReader");
Box::pin(async move { Ok(stream) })
Box::pin(async move { Err(AssetReaderError::NotFound(path.into())) })
}

fn is_directory<'a>(
&'a self,
_path: &'a Path,
path: &'a Path,
) -> BoxedFuture<'a, std::result::Result<bool, AssetReaderError>> {
error!("Reading directories is not supported with the HttpAssetReader");
Box::pin(async move { Ok(false) })
Box::pin(async move { Err(AssetReaderError::NotFound(path.into())) })
}
}

Expand Down
9 changes: 4 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Loads and renders a glTF file as a scene.

use bevy::prelude::*;
#[cfg(all(feature = "xr", not(target_os = "macos")))]
#[cfg(all(feature = "xr", not(any(target_os = "macos", target_arch = "wasm32"))))]
use bevy_oxr::xr_input::trackers::OpenXRTrackingRoot;
use http_assets::HttpAssetReaderPlugin;

Expand All @@ -11,18 +11,17 @@ mod flycam;
mod http_assets;
mod sun;
mod tilemap;
#[cfg(all(feature = "xr", not(target_os = "macos")))]
#[cfg(all(feature = "xr", not(any(target_os = "macos", target_arch = "wasm32"))))]
mod xr;

#[bevy_main]
pub fn main() {
std::env::set_var("RUST_BACKTRACE", "1");
let mut app = App::new();
app.add_plugins(HttpAssetReaderPlugin {
base_url: "https://gltiles.osm2world.org/glb/".into(),
});
if std::env::args().any(|arg| arg == "xr") {
#[cfg(all(feature = "xr", not(target_os = "macos")))]
#[cfg(all(feature = "xr", not(any(target_os = "macos", target_arch = "wasm32"))))]
app.add_plugins(xr::Plugin);
} else {
app.add_plugins(DefaultPlugins);
Expand Down Expand Up @@ -59,7 +58,7 @@ fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
/// Used to make sure all players have a map to walk on.
pub struct LocalPlayer;

#[cfg(not(all(feature = "xr", not(target_os = "macos"))))]
#[cfg(not(all(feature = "xr", not(any(target_os = "macos", target_arch = "wasm32")))))]
/// HACK: we can't attach `LocalPlayer` to the xr player yet, so we need
/// to access the OpenXRTrackingRoot, but that doesn't exist without the xr feature
type OpenXRTrackingRoot = LocalPlayer;
Expand Down
30 changes: 30 additions & 0 deletions web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">

<head>
<title>OSMeta</title>
<meta charset="UTF-8" />
<style>
body {
background: linear-gradient(135deg,
white 0%,
white 49%,
black 49%,
black 51%,
white 51%,
white 100%);
background-repeat: repeat;
background-size: 20px 20px;
}

canvas {
background-color: white;
}
</style>
</head>
<script type="module">
import init from './demo.js'
init()
</script>

</html>