Skip to content
Open
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
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,27 @@ for !shouldQuit {
It uses OpenGL 3.3 and uses
[`github.com/go-gl/gl/v3.3-core/gl`](https://github.com/go-gl/gl/tree/master/v3.3-core/gl).

### Does GLHF work in a browser (WebAssembly)?

Yes. Under `GOOS=js GOARCH=wasm` GLHF dispatches to a WebGL2 rendering context
via `syscall/js` instead of go-gl. The exported API is the same, with one extra
step: before calling `glhf.Init` (or any `New*` / `Make*` constructor), you
must hand GLHF the `WebGL2RenderingContext` you created from your HTML canvas:

```go
//go:build js && wasm

gl := jsCanvas.Call("getContext", "webgl2")
glhf.SetContext(gl)
glhf.Init()
```

GLSL 330 core shader sources are transparently adapted to GLSL ES 3.00 (the
only version WebGL2 accepts). A few desktop-OpenGL concepts don't exist in
WebGL2 and are stubbed or substituted — notably `CLAMP_TO_BORDER` falls back to
`CLAMP_TO_EDGE`, and `glGetTexImage` is emulated through an off-screen
framebuffer + `readPixels`.

### Why do I have to use `github.com/gopxl/mainthread` package with GLHF?

First of all, OpenGL has to be done from one thread and many operating systems require, that the one
Expand Down
7 changes: 6 additions & 1 deletion doc.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// Package glhf provides abstractions around the basic OpenGL primitives and operations.
//
// All calls should be done from the main thread using "github.com/gopxl/mainthread/v2" package.
// On desktop targets all calls should be done from the main thread using the
// "github.com/gopxl/mainthread/v2" package.
//
// A parallel WebGL2 backend is built under GOOS=js GOARCH=wasm. The exported
// API is the same, except that the caller must install a WebGL2 rendering
// context once via SetContext before invoking Init or any constructor.
//
// This package deliberately does not handle nor report trivial OpenGL errors, it's up to you to
// cause none. It does of course report errors like shader compilation error and such.
Expand Down
2 changes: 2 additions & 0 deletions examples/demo/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !js

package main

import (
Expand Down
2 changes: 2 additions & 0 deletions frame.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !js

package glhf

import (
Expand Down
65 changes: 65 additions & 0 deletions gl_wasm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//go:build js && wasm

package glhf

import (
"syscall/js"
"unsafe"
)

// Package-level WebGL2 state. Callers (pixel backend, standalone consumers) must
// invoke SetContext exactly once, after creating a WebGL2RenderingContext, before
// any glhf.Init / New* / Make* call runs.

var (
gl js.Value
glReady bool
uint8ArrayCtor js.Value
f32ArrayCtor js.Value
)

// SetContext installs the WebGL2 rendering context used by all glhf calls.
func SetContext(ctx js.Value) {
gl = ctx
uint8ArrayCtor = js.Global().Get("Uint8Array")
f32ArrayCtor = js.Global().Get("Float32Array")
glReady = true
}

// Context returns the installed WebGL2 context (empty js.Value if unset).
func Context() js.Value { return gl }

// glEnum looks up a WebGL constant by name (e.g. "TEXTURE_2D").
func glEnum(name string) int { return gl.Get(name).Int() }

// uint8ToJS copies a Go byte slice into a new JS Uint8Array.
func uint8ToJS(b []byte) js.Value {
u8 := uint8ArrayCtor.New(len(b))
if len(b) > 0 {
js.CopyBytesToJS(u8, b)
}
return u8
}

// float32ToJS copies a Go float32 slice into a new JS Float32Array.
func float32ToJS(f []float32) js.Value {
if len(f) == 0 {
return f32ArrayCtor.New(0)
}
b := unsafe.Slice((*byte)(unsafe.Pointer(&f[0])), len(f)*4)
u8 := uint8ToJS(b)
return f32ArrayCtor.New(u8.Get("buffer"), u8.Get("byteOffset"), len(f))
}

// float32FromJS copies a JS Float32Array back into a Go float32 slice.
func float32FromJS(arr js.Value, n int) []float32 {
out := make([]float32, n)
if n == 0 {
return out
}
// Read bytes via a Uint8Array view over the same buffer.
u8 := uint8ArrayCtor.New(arr.Get("buffer"), arr.Get("byteOffset"), n*4)
b := unsafe.Slice((*byte)(unsafe.Pointer(&out[0])), n*4)
js.CopyBytesToGo(b, u8)
return out
}
Loading