diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..af78532 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,40 @@ +# Build Workflow +name: build + +# Run on Push +on: [push] + + +jobs: + build: + # Run this on a default linux machine + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + + - uses: mymindstorm/setup-emsdk@v11 + + - name: Checkout ocen repo + uses: actions/checkout@v3 + with: + repository: ocen-lang/ocen + path: ocen + + - name: Build ocen + run: | + cd ocen + ./meta/bootstrap.sh + echo "OCEN_ROOT=${{ github.workspace }}/ocen" >> $GITHUB_ENV + echo "${{ github.workspace }}/ocen/bootstrap" >> $GITHUB_PATH + + - name: Build Wasm + run: | + cd ${{ github.workspace }} + ./meta/build_wasm.sh + ./meta/multi_build_wasm.sh + + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@4.1.0 + with: + branch: gh-pages # The branch the action should deploy to. + folder: build diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..543141f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.dSYM +*out +*.c +build diff --git a/README.md b/README.md new file mode 100644 index 0000000..4248dc8 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Ocen WASM Template + +This is a template using the `std::og` library in [ocen](https://github.com/ocen-lang/ocen) +to draw some graphics to the screen. `std::og` is a set of wrappers around `SDL` to make it +a friendlier API for fast-protoyping (loosely inspired by `p5.js` / `raylib`). + +This repository demonstrates how to compile such an application to WebAssembly using `emscripten`, +and has a sample github workflow to automatically build and deploy the application to github pages. + +## Customization + +- The `assets` folder is automatically bundled into the generated `wasm` (can be changed in `build_wasm.sh`). + Any files you want to be available to the application should be placed in this folder. + +- `assets/index.html` is the default HTML template for the application. It only works with the default emscriptem + build, and assumes the files generated are `index.js` and `index.wasm`. + +- `assets/multi-canvas.html` is a demonstration of how to load in different compiled WASM modules into the same + page. This requires building the WASM modules with different names (`-s MODULARIZE=1 -s EXPORT_NAME="FOO"` flags). + This is demonstrated in `./meta/multi_build_wasm.sh`. + +## Building + +### Native SDL build + +Need to have `SDL2` installed on your system, and should be able to compile C code with `-lSDL2 -lz` + +```shell +$ ocen main.oc -o main +$ ./main +``` + +### Emscripten build, Local toolchain + +Need to have `emscripten` installed locally, and activated so that `emcc` is in your path. + +```shell +$ ./meta/build_wasm.sh +$ python3 -m http.server build/ +``` + +### Emscripten build, Docker + +Must have docker installed on your system, and running. + +```shell +$ ./meta/build_wasm_docker.sh +$ python3 -m http.server build/ +``` \ No newline at end of file diff --git a/assets/index.html b/assets/index.html new file mode 100644 index 0000000..febbdbd --- /dev/null +++ b/assets/index.html @@ -0,0 +1,69 @@ + + + + + + RayCast + + + +

Ocen WASM Demo

+ View Source On GitHub +
+

This demo was written in the Ocen, and compiled to the web using Emscripten.

+ +

Use Up and Down keys to zoom in/out

+ + +
Check out the multi-canvas template + + + + + diff --git a/assets/multi-canvas.html b/assets/multi-canvas.html new file mode 100644 index 0000000..80db86e --- /dev/null +++ b/assets/multi-canvas.html @@ -0,0 +1,86 @@ + + + + + + RayCast + + + +

Ocen WASM Demo - Multi Canvas

+ View Source On GitHub +
+

This demo was written in the Ocen, and compiled to the web using Emscripten.

+

Use Up and Down keys to zoom in/out

+ + + + + +
Check out the single-canvas template + + + + + + + + + diff --git a/main.oc b/main.oc new file mode 100644 index 0000000..b665cc4 --- /dev/null +++ b/main.oc @@ -0,0 +1,47 @@ +import std::og + +def main() { + let rows = 20i32 + let cols = 20i32 + let cell_size = 20i32 + + og::init((cols * cell_size) as u32, (rows * cell_size) as u32, "Demo") + + let off = 0i32 + let frames = 0 + while og::is_running() { + if og::is_key_pressed(Escape) { + break + } + + if og::is_key_pressed(Up) { + cell_size += 1 + } + if og::is_key_pressed(Down) { + cell_size -= 1 + } + + for let i = -1; i < rows + 1; i++ { + for let j = -1; j < cols + 1; j++ { + og::draw_rect( + (j * cell_size + off) as i32, + (i * cell_size + off) as i32, + (cell_size) as i32, + (cell_size) as i32, + if (i + j) % 2 == 0 then og::colors::BLACK else og::colors::WHITE + ) + } + } + + frames++ + if frames % 20 == 0 { + if (off == cell_size) { + off = 0 + } + off++ + } + } + og::quit() +} + + diff --git a/meta/build_wasm.sh b/meta/build_wasm.sh new file mode 100755 index 0000000..b6e7081 --- /dev/null +++ b/meta/build_wasm.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +set -xe + +mkdir -p build + +if [[ -z "$1" ]]; then + ocen main.oc -n -c build/main.c + FILE=build/main.c +else + FILE=$1 +fi + + +cp assets/index.html build/ + +# GUIDE for flags: +#------------------ +# -O3: Optimize the code. +# -DOC_NO_BACKTRACE: Disable backtrace: Ocen specific, required, since WASM doesn't have execinfo.h +# -s USE_SDL=2: Use SDL2 for graphics. (`std::og` is based on SDL2) +# -s USE_ZLIB=1: Use zlib for compression. (`std::image::png` uses zlib) +# -s ALLOW_MEMORY_GROWTH=1: Allow memory to grow dynamically. +# --preload-file assets: Bundle the `assets` directory into generated WASM data +# --use-preload-plugins: Use the plugins in the preload file. +# -o build/index.js: Output file name (JS + corresponding WASM file) +# -s ASYNCIFY=1: Enable asyncify (allows `while` loops in the code for main event loop) +# +# Optional flags (not used here, but needed for multi-wasm pages) +# ---------------------------------------------------------------- +# -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0: Without this, SDL always assumes single canvas with ID `#canvas`. +# -s MODULARIZE=1: Generate a module that can be loaded into the same HTML page. +# -s EXPORT_NAME="'Module1'": Name of the module. This is used in the HTML file to load the module. +# -s 'EXTRA_EXPORTED_RUNTIME_METHODS=["ENV"]': Export the ENV object to the module. This is needed so we can configure +# the module to not capture keyboard events outside the canvas. Look at +# `assets/multi-canvas.html` or `assets/index.html` for more details. + + +emcc $FILE \ + -O3 \ + -DOC_NO_BACKTRACE \ + -s USE_SDL=2 \ + -s USE_ZLIB=1 \ + -s ALLOW_MEMORY_GROWTH=1 \ + --preload-file assets \ + --use-preload-plugins \ + -o build/index.js \ + -s ASYNCIFY=1 \ No newline at end of file diff --git a/meta/build_wasm_docker.sh b/meta/build_wasm_docker.sh new file mode 100755 index 0000000..236563f --- /dev/null +++ b/meta/build_wasm_docker.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -xe + +ocen main.oc -n -c build/main.c +docker run --rm -v $(PWD):/mnt/ -w /mnt/ emscripten/emsdk ./meta/build_wasm.sh build/main.c diff --git a/meta/multi_build_wasm.sh b/meta/multi_build_wasm.sh new file mode 100755 index 0000000..16834ea --- /dev/null +++ b/meta/multi_build_wasm.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# NOTE: This is a demo on how to build separate files into modules which can be loaded +# into the **same** HTML page. Note the usage of `-s MODULARIZE` and `-s EXPORT_NAME` +# flags. +# Look at `assets/multi-canvas.html` for how to load multiple modules into the same +# HTML page. + +set -xe + +mkdir -p build + +cp assets/multi-canvas.html build/ + + +ocen main.oc -n -c build/main.c + +# NOTE: This is just to make the second file different from the first one. +cat main.oc | sed 's/BLACK/RED/g' > build/main2.oc +ocen build/main2.oc -n -c build/main2.c +rm build/main2.oc + +# Optional flags (needed for multi-wasm pages) +# ---------------------------------------------------------------- +# -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0: Without this, SDL always assumes single canvas with ID `#canvas`. +# -s MODULARIZE=1: Generate a module that can be loaded into the same HTML page. +# -s EXPORT_NAME="'Module1'": Name of the module. This is used in the HTML file to load the module. +# -s 'EXTRA_EXPORTED_RUNTIME_METHODS=["ENV"]': Export the ENV object to the module. This is needed so we can configure +# the module to not capture keyboard events outside the canvas. Look at +# `assets/multi-canvas.html` or `assets/index.html` for more details. +# NOTE: ENV is exported by default when NOT using MODULARIZE. + +emcc build/main.c \ + -DOC_NO_BACKTRACE \ + -s USE_SDL=2 \ + -s USE_ZLIB=1 \ + -s ALLOW_MEMORY_GROWTH=1 \ + --preload-file assets \ + --use-preload-plugins \ + -s ASYNCIFY=1 \ + -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 \ + -s MODULARIZE=1 \ + -s EXPORT_NAME=Module1 \ + -o build/mod1.js \ + -s 'EXTRA_EXPORTED_RUNTIME_METHODS=["ENV"]' + +emcc build/main2.c \ + -DOC_NO_BACKTRACE \ + -s USE_SDL=2 \ + -s USE_ZLIB=1 \ + -s ALLOW_MEMORY_GROWTH=1 \ + --preload-file assets \ + --use-preload-plugins \ + -s ASYNCIFY=1 \ + -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 \ + -s MODULARIZE=1 \ + -s EXPORT_NAME=Module2 \ + -o build/mod2.js \ + -s 'EXTRA_EXPORTED_RUNTIME_METHODS=["ENV"]' \ No newline at end of file