Skip to content

Commit

Permalink
Add some shaders and frame loop going
Browse files Browse the repository at this point in the history
  • Loading branch information
thetarnav committed Jan 14, 2024
1 parent cdb944c commit 5c17f15
Show file tree
Hide file tree
Showing 21 changed files with 319 additions and 97 deletions.
7 changes: 6 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
{
"recommendations": ["danielgavin.ols", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
"recommendations": [
"danielgavin.ols",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"raczzalan.webgl-glsl-editor"
]
}
54 changes: 31 additions & 23 deletions example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import * as wasm from "../wasm/runtime.js"

import {IS_DEV, WEB_SOCKET_PORT, MESSAGE_RELOAD, WASM_FILENAME} from "./_config.js"

// eslint-disable-next-line @typescript-eslint/no-unused-vars
import * as t from "./types.js"

if (IS_DEV) {
wasm.enableConsole()

const socket = new WebSocket("ws://localhost:" + WEB_SOCKET_PORT)

socket.addEventListener("message", event => {
if (event.data === MESSAGE_RELOAD) {
location.reload()
}
/* Hot Reload */
new WebSocket("ws://localhost:" + WEB_SOCKET_PORT).addEventListener("message", event => {
event.data === MESSAGE_RELOAD && location.reload()
})
}

Expand All @@ -20,25 +20,33 @@ document.body.addEventListener("lol", () => {
console.log("lol event has been received")
})

/* To test scroll events */
document.body.style.minHeight = "200vh"

const wasm_instance = wasm.zeroWasmInstance()
const webgl_state = wasm.webgl.makeWebGLInterface()
const wasm_state = wasm.makeWasmState()
const webgl_state = wasm.webgl.makeWebGLState()

const response = await fetch(WASM_FILENAME)
const file = await response.arrayBuffer()
const source_instance = await WebAssembly.instantiate(file, {
const wasm_file = await fetch(WASM_FILENAME).then(r => r.arrayBuffer())
const src_instance = await WebAssembly.instantiate(wasm_file, {
env: {}, // TODO
odin_env: wasm.env.makeOdinEnv(wasm_instance),
odin_ls: wasm.ls.makeOdinLS(wasm_instance),
odin_dom: wasm.dom.makeOdinDOM(wasm_instance),
webgl: wasm.webgl.makeOdinWebGL(webgl_state, wasm_instance),
odin_env: wasm.env.makeOdinEnv(wasm_state),
odin_ls: wasm.ls.makeOdinLS(wasm_state),
odin_dom: wasm.dom.makeOdinDOM(wasm_state),
webgl: wasm.webgl.makeOdinWebGL(webgl_state, wasm_state),
})
wasm.initWasmInstance(wasm_instance, source_instance.instance.exports)

console.log("Exports", wasm_instance.exports)
console.log("Memory", wasm_instance.memory)
wasm.initWasmState(wasm_state, src_instance)
const exports = /** @type {t.WasmExports} */ (wasm_state.exports)

wasm_instance.exports._start()
wasm_instance.exports._end()
exports._start()
const odin_ctx = exports.default_context_ptr()
exports._end()

void requestAnimationFrame(prev_time => {
/** @type {FrameRequestCallback} */
const frame = time => {
const delta = (time - prev_time) * 0.001
prev_time = time
exports.frame(delta, odin_ctx)
void requestAnimationFrame(frame)
}

void requestAnimationFrame(frame)
})
119 changes: 112 additions & 7 deletions example/main.odin
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package main

import "../wasm"
import "../wasm/dom"
import "../wasm/webgl"
import "core:fmt"
import "core:mem"
import "core:runtime"
import "core:strings"

import "../wasm"
import "../wasm/dom"
import "../wasm/webgl"

shader_fragment := #load("shader_fragment.glsl", string)
shader_vertex := #load("shader_vertex.glsl", string)


main :: proc() {
test_buf, err := wasm.page_alloc(2)
context.allocator = mem.arena_allocator(&{data = test_buf})
Expand All @@ -16,10 +22,6 @@ main :: proc() {
fmt.print("Hello, WebAssembly!\n")
fmt.eprint("Hello, Error!\n\ttest\nbyebye!\n")

// Make sure that this matches the id of your canvas.
webgl.SetCurrentContextById("canvas")
webgl.ClearColor(1, 0, 0, 1)
webgl.Clear(webgl.COLOR_BUFFER_BIT)

dom.add_window_event_listener(.Scroll, {}, proc(e: dom.Event) {
fmt.println("Scroll event!", e.data.scroll.delta)
Expand All @@ -30,4 +32,107 @@ main :: proc() {
dom.add_window_event_listener(.Visibility_Change, {}, proc(e: dom.Event) {
fmt.println("Visibility_Change event!", e.data.visibility_change.is_visible)
})


// Make sure that this matches the id of your canvas.
if ok := webgl.SetCurrentContextById("canvas"); !ok {
fmt.println("Failed to set current context!")
return
}

program, program_ok := webgl.CreateProgramFromStrings({shader_vertex}, {shader_fragment})
if !program_ok {
fmt.println("Failed to create program!")
return
}
webgl.UseProgram(program)


a_position = webgl.GetAttribLocation(program, "a_position")
a_color = webgl.GetAttribLocation(program, "a_color")
u_resolution = webgl.GetUniformLocation(program, "u_resolution")

webgl.EnableVertexAttribArray(a_position)
webgl.EnableVertexAttribArray(a_color)


positions_buffer = webgl.CreateBuffer()
colors_buffer = webgl.CreateBuffer()
}

a_position: i32
a_color: i32
u_resolution: i32

positions_buffer: webgl.Buffer
colors_buffer: webgl.Buffer

iteration: i32

// odinfmt: disable
colors := [?]u8 {
255, 0, 0, 255,
0, 255, 0, 255,
0, 0, 255, 255,

255, 0, 0, 255,
0, 255, 0, 255,
0, 0, 255, 255,
}
// odinfmt: enable

@(export)
frame :: proc "c" (delta: f32, ctx: ^runtime.Context) {
context = ctx^

err := webgl.GetError()
if err != webgl.NO_ERROR {
fmt.println("WebGL error:", err)
return
}

iteration += 2
if iteration > 200 {
iteration = 0
}

H: f32 : 100
W: f32 : 200
x := f32(iteration)
// odinfmt: disable
positions := [?]f32 {
10+x, 20+x,
W+x, 20+x,
10+x, H+x,

10+x, H+x,
W+x, 20+x,
W+x, H+x,
}
// odinfmt: enable


webgl.BindBuffer(webgl.ARRAY_BUFFER, positions_buffer)
webgl.BufferDataSlice(webgl.ARRAY_BUFFER, positions[:], webgl.STATIC_DRAW)

// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
webgl.VertexAttribPointer(a_position, 2, webgl.FLOAT, false, 0, 0)

// bind, and fill color buffer
webgl.BindBuffer(webgl.ARRAY_BUFFER, colors_buffer)
webgl.BufferDataSlice(webgl.ARRAY_BUFFER, colors[:], webgl.STATIC_DRAW)
webgl.VertexAttribPointer(a_color, 4, webgl.UNSIGNED_BYTE, true, 0, 0)

// set the resolution
webgl.Uniform2f(u_resolution, 640, 480)

// Tell WebGL how to convert from clip space to pixels
webgl.Viewport(0, 0, 640, 480)

// Clear the canvas
webgl.ClearColor(0, 0, 0, 0)
webgl.Clear(webgl.COLOR_BUFFER_BIT)

// draw
webgl.DrawArrays(webgl.TRIANGLES, 0, 6) // 2 triangles, 6 vertices
}
11 changes: 11 additions & 0 deletions example/shader_fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// fragment shaders don't have a default precision so we need
// to pick one. mediump is a good default
precision mediump float;

// color varying received from vertex shader
varying vec4 v_color;

void main() {
// gl_FragColor is a special variable a fragment shader is responsible for setting
gl_FragColor = v_color;
}
20 changes: 20 additions & 0 deletions example/shader_vertex.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// an attribute will receive data from a buffer
attribute vec2 a_position;
attribute vec4 a_color;
uniform vec2 u_resolution;
// color to pass to the fragment shader
// value in fragment shader will be interpolated
varying vec4 v_color;

void main() {
// from pixels to 0->1 then to 0->2 then to -1->+1 (clipspace)
vec2 clip_space = (a_position / u_resolution) * 2.0 - 1.0;

gl_Position = vec4(clip_space * vec2(1, -1), 0, 1);

// Convert from clip space to color space.
// Clip space goes -1.0 to +1.0
// Color space goes from 0.0 to 1.0
// v_color = 1.0 - (gl_Position * 0.5 + 0.5);
v_color = a_color;
}
5 changes: 5 additions & 0 deletions example/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as wasm from "../wasm/runtime.js"

export interface WasmExports extends wasm.OdinExports {
frame: (time: number, ctx_ptr: number) => void
}
1 change: 1 addition & 0 deletions example/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {}
18 changes: 7 additions & 11 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,7 @@ function falseFn() {
*/
function unsafePromiseToError(promise) {
return promise.then(
// eslint-disable-next-line @nothing-but/no-return-to-void
result => result,
// eslint-disable-next-line @nothing-but/no-return-to-void
error => error,
)
}
Expand All @@ -406,13 +404,13 @@ function childProcessToPromise(/** @type {child_process.ChildProcess} */ child)
})
}

/**
* @param {number} ms
* @returns {Promise<void>}
*/
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
// /**
// * @param {number} ms
// * @returns {Promise<void>}
// */
// function sleep(ms) {
// return new Promise(resolve => setTimeout(resolve, ms))
// }

/** @returns {string} */
function toWebFilepath(/** @type {string} */ path) {
Expand All @@ -429,7 +427,6 @@ function fileExists(/** @type {string} */ filepath) {
* @returns {Promise<void>}
*/
function ensureEmptyDir(dirpath) {
// eslint-disable-next-line @nothing-but/no-return-to-void
return fsp.rm(dirpath, {recursive: true, force: true}).then(() => fsp.mkdir(dirpath))
}

Expand Down Expand Up @@ -459,7 +456,6 @@ async function copyDirContents(src, dest) {
* @returns {Promise<void>}
*/
function copyDir(src, dest) {
// eslint-disable-next-line @nothing-but/no-return-to-void
return fsp.mkdir(dest).then(() => copyDirContents(src, dest))
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"build-test": "pnpm run build && pnpm run typecheck:server && pnpm run typecheck:example && pnpm run lint && pnpm run test"
},
"devDependencies": {
"@nothing-but/eslint-plugin": "^0.1.4",
"@nothing-but/eslint-plugin": "^0.1.5",
"@total-typescript/ts-reset": "^0.5.1",
"@types/node": "^20.10.7",
"@types/ws": "^8.5.10",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

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

2 changes: 1 addition & 1 deletion wasm/dom/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function targetToKind(target) {
const KEYBOARD_MAX_KEY_SIZE = 16
const KEYBOARD_MAX_CODE_SIZE = 16

/** @param {import("../types.js").WasmInstance} _wasm */
/** @param {import("../types.js").WasmState} _wasm */
export function makeOdinDOM(_wasm) {
const wasm = /** @type {import("./types.js").OdinDOMInstance} */ (_wasm)

Expand Down
2 changes: 1 addition & 1 deletion wasm/dom/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ export interface OdinDOMExports extends wasm.OdinExports {
odin_dom_do_event_callback: (data: number, callback: number, ctx_ptr: number) => void
}

export interface OdinDOMInstance extends wasm.WasmInstance {
export interface OdinDOMInstance extends wasm.WasmState {
exports: OdinDOMExports
}
2 changes: 1 addition & 1 deletion wasm/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function writeToConsole(fd, str) {
}
}

/** @param {import("./types.js").WasmInstance} wasm */
/** @param {import("./types.js").WasmState} wasm */
export function makeOdinEnv(wasm) {
return {
/**
Expand Down
2 changes: 1 addition & 1 deletion wasm/ls/local_storage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as mem from "../memory.js"

/** @param {import("../types.js").WasmInstance} wasm */
/** @param {import("../types.js").WasmState} wasm */
export function makeOdinLS(wasm) {
return {
/**
Expand Down
Loading

0 comments on commit 5c17f15

Please sign in to comment.