added a glfwcanvas subpackage
This commit is contained in:
parent
32bbbdd7c8
commit
93d30a1ff7
4 changed files with 387 additions and 4 deletions
|
@ -10,15 +10,15 @@ Whereas the Javascript API uses a context that all draw calls go to, here all dr
|
|||
|
||||
The library is intended to provide decent performance. Obviously it will not be able to rival hand coded OpenGL for a given purpose, but for many purposes it will be enough. It can also be combined with hand coded OpenGL.
|
||||
|
||||
# SDL convenience package
|
||||
# SDL/GLFW convenience packages
|
||||
|
||||
The sdlcanvas subpackage provides a very simple way to get started with just a few lines of code. As the name implies it is based on the SDL library. It creates a window for you and gives you a canvas to draw with.
|
||||
The sdlcanvas and glfwcanvas subpackages provide a very simple way to get started with just a few lines of code. As the names imply they are based on the SDL library and the GLFW library respectively. They create a window for you and give you a canvas to draw with.
|
||||
|
||||
# OS support
|
||||
|
||||
- Linux
|
||||
- Windows
|
||||
- OSX
|
||||
- macOS
|
||||
- Android
|
||||
- iOS
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
func main() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
// init SDL
|
||||
// init GLFW
|
||||
err := glfw.Init()
|
||||
if err != nil {
|
||||
log.Fatalf("Error initializing GLFW: %v", err)
|
||||
|
|
167
glfwcanvas/glfwcanvas.go
Normal file
167
glfwcanvas/glfwcanvas.go
Normal file
|
@ -0,0 +1,167 @@
|
|||
package glfwcanvas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "image/gif" // Imported here so that applications based on this package support these formats by default
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
"log"
|
||||
"math"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/go-gl/gl/v3.2-core/gl"
|
||||
"github.com/go-gl/glfw/v3.2/glfw"
|
||||
"github.com/tfriedel6/canvas"
|
||||
"github.com/tfriedel6/canvas/glimpl/gogl"
|
||||
)
|
||||
|
||||
// Window represents the opened window with GL context. The Mouse* and Key*
|
||||
// functions can be set for callbacks
|
||||
type Window struct {
|
||||
Window *glfw.Window
|
||||
canvas *canvas.Canvas
|
||||
frameTimes [10]time.Time
|
||||
frameIndex int
|
||||
frameCount int
|
||||
fps float32
|
||||
close bool
|
||||
MouseDown func(button, x, y int)
|
||||
MouseMove func(x, y int)
|
||||
MouseUp func(button, x, y int)
|
||||
MouseWheel func(x, y int)
|
||||
KeyDown func(scancode int, rn rune, name string)
|
||||
KeyUp func(scancode int, rn rune, name string)
|
||||
KeyChar func(rn rune)
|
||||
SizeChange func(w, h int)
|
||||
}
|
||||
|
||||
// CreateWindow creates a window using SDL and initializes the OpenGL context
|
||||
func CreateWindow(w, h int, title string) (*Window, *canvas.Canvas, error) {
|
||||
runtime.LockOSThread()
|
||||
|
||||
// init GLFW
|
||||
err := glfw.Init()
|
||||
if err != nil {
|
||||
log.Fatalf("Error initializing GLFW: %v", err)
|
||||
}
|
||||
|
||||
// the stencil size setting is required for the canvas to work
|
||||
glfw.WindowHint(glfw.StencilBits, 8)
|
||||
glfw.WindowHint(glfw.DepthBits, 0)
|
||||
|
||||
// create window
|
||||
window, err := glfw.CreateWindow(w, h, title, nil, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Error creating window: %v", err)
|
||||
}
|
||||
window.MakeContextCurrent()
|
||||
|
||||
// init GL
|
||||
err = gl.Init()
|
||||
if err != nil {
|
||||
log.Fatalf("Error initializing GL: %v", err)
|
||||
}
|
||||
|
||||
// set vsync on, enable multisample (if available)
|
||||
glfw.SwapInterval(1)
|
||||
gl.Enable(gl.MULTISAMPLE)
|
||||
|
||||
// load canvas GL assets
|
||||
err = canvas.LoadGL(glimplgogl.GLImpl{})
|
||||
if err != nil {
|
||||
log.Fatalf("Error loading canvas GL assets: %v", err)
|
||||
}
|
||||
|
||||
err = canvas.LoadGL(glimplgogl.GLImpl{})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error loading canvas GL assets: %v", err)
|
||||
}
|
||||
|
||||
cv := canvas.New(0, 0, w, h)
|
||||
wnd := &Window{
|
||||
Window: window,
|
||||
canvas: cv,
|
||||
}
|
||||
|
||||
var mx, my int
|
||||
|
||||
window.SetMouseButtonCallback(func(w *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
|
||||
if action == glfw.Press && wnd.MouseDown != nil {
|
||||
wnd.MouseDown(int(button), mx, my)
|
||||
} else if action == glfw.Release && wnd.MouseUp != nil {
|
||||
wnd.MouseUp(int(button), mx, my)
|
||||
}
|
||||
})
|
||||
window.SetCursorPosCallback(func(w *glfw.Window, xpos float64, ypos float64) {
|
||||
mx, my = int(math.Round(xpos)), int(math.Round(ypos))
|
||||
if wnd.MouseMove != nil {
|
||||
wnd.MouseMove(mx, my)
|
||||
}
|
||||
})
|
||||
window.SetScrollCallback(func(w *glfw.Window, xoff float64, yoff float64) {
|
||||
if wnd.MouseWheel != nil {
|
||||
wnd.MouseWheel(int(math.Round(xoff)), int(math.Round(yoff)))
|
||||
}
|
||||
})
|
||||
window.SetKeyCallback(func(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
|
||||
if action == glfw.Press && wnd.KeyDown != nil {
|
||||
wnd.KeyDown(scancode, keyRune(key), keyName(key))
|
||||
} else if action == glfw.Release && wnd.KeyUp != nil {
|
||||
wnd.KeyUp(scancode, keyRune(key), keyName(key))
|
||||
}
|
||||
})
|
||||
window.SetCharCallback(func(w *glfw.Window, char rune) {
|
||||
if wnd.KeyChar != nil {
|
||||
wnd.KeyChar(char)
|
||||
}
|
||||
})
|
||||
window.SetSizeCallback(func(w *glfw.Window, width int, height int) {
|
||||
if wnd.SizeChange != nil {
|
||||
wnd.SizeChange(width, height)
|
||||
}
|
||||
})
|
||||
|
||||
return wnd, cv, nil
|
||||
}
|
||||
|
||||
// FPS returns the frames per second (averaged over 10 frames)
|
||||
func (wnd *Window) FPS() float32 {
|
||||
return wnd.fps
|
||||
}
|
||||
|
||||
// Close can be used to end a call to MainLoop
|
||||
func (wnd *Window) Close() {
|
||||
wnd.close = true
|
||||
}
|
||||
|
||||
// StartFrame handles events and gets the window ready for rendering
|
||||
func (wnd *Window) StartFrame() {
|
||||
wnd.Window.MakeContextCurrent()
|
||||
glfw.PollEvents()
|
||||
}
|
||||
|
||||
// FinishFrame updates the FPS count and displays the frame
|
||||
func (wnd *Window) FinishFrame() {
|
||||
now := time.Now()
|
||||
wnd.frameTimes[wnd.frameIndex] = now
|
||||
wnd.frameIndex++
|
||||
wnd.frameIndex %= len(wnd.frameTimes)
|
||||
if wnd.frameCount < len(wnd.frameTimes) {
|
||||
wnd.frameCount++
|
||||
} else {
|
||||
diff := now.Sub(wnd.frameTimes[wnd.frameIndex]).Seconds()
|
||||
wnd.fps = float32(wnd.frameCount-1) / float32(diff)
|
||||
}
|
||||
|
||||
wnd.Window.SwapBuffers()
|
||||
}
|
||||
|
||||
// MainLoop runs a main loop and calls run on every frame
|
||||
func (wnd *Window) MainLoop(run func()) {
|
||||
for !wnd.close {
|
||||
wnd.StartFrame()
|
||||
run()
|
||||
wnd.FinishFrame()
|
||||
}
|
||||
}
|
216
glfwcanvas/keynames.go
Normal file
216
glfwcanvas/keynames.go
Normal file
|
@ -0,0 +1,216 @@
|
|||
package glfwcanvas
|
||||
|
||||
import "github.com/go-gl/glfw/v3.2/glfw"
|
||||
|
||||
var keyNameMap [347]string
|
||||
var keyRuneMap [347]rune
|
||||
|
||||
func init() {
|
||||
keyNameMap[glfw.KeyEscape] = "Escape"
|
||||
keyNameMap[glfw.Key0] = "Digit0"
|
||||
keyNameMap[glfw.Key1] = "Digit1"
|
||||
keyNameMap[glfw.Key2] = "Digit2"
|
||||
keyNameMap[glfw.Key3] = "Digit3"
|
||||
keyNameMap[glfw.Key4] = "Digit4"
|
||||
keyNameMap[glfw.Key5] = "Digit5"
|
||||
keyNameMap[glfw.Key6] = "Digit6"
|
||||
keyNameMap[glfw.Key7] = "Digit7"
|
||||
keyNameMap[glfw.Key8] = "Digit8"
|
||||
keyNameMap[glfw.Key9] = "Digit9"
|
||||
keyNameMap[glfw.KeyMinus] = "Minus"
|
||||
keyNameMap[glfw.KeyEqual] = "Equal"
|
||||
keyNameMap[glfw.KeyBackspace] = "Backspace"
|
||||
keyNameMap[glfw.KeyTab] = "Tab"
|
||||
keyNameMap[glfw.KeyQ] = "KeyQ"
|
||||
keyNameMap[glfw.KeyW] = "KeyW"
|
||||
keyNameMap[glfw.KeyE] = "KeyE"
|
||||
keyNameMap[glfw.KeyR] = "KeyR"
|
||||
keyNameMap[glfw.KeyT] = "KeyT"
|
||||
keyNameMap[glfw.KeyY] = "KeyY"
|
||||
keyNameMap[glfw.KeyU] = "KeyU"
|
||||
keyNameMap[glfw.KeyI] = "KeyI"
|
||||
keyNameMap[glfw.KeyO] = "KeyO"
|
||||
keyNameMap[glfw.KeyP] = "KeyP"
|
||||
keyNameMap[glfw.KeyLeftBracket] = "BracketLeft"
|
||||
keyNameMap[glfw.KeyRightBracket] = "BracketRight"
|
||||
keyNameMap[glfw.KeyEnter] = "Enter"
|
||||
keyNameMap[glfw.KeyLeftControl] = "ControlLeft"
|
||||
keyNameMap[glfw.KeyA] = "KeyA"
|
||||
keyNameMap[glfw.KeyS] = "KeyS"
|
||||
keyNameMap[glfw.KeyD] = "KeyD"
|
||||
keyNameMap[glfw.KeyF] = "KeyF"
|
||||
keyNameMap[glfw.KeyG] = "KeyG"
|
||||
keyNameMap[glfw.KeyH] = "KeyH"
|
||||
keyNameMap[glfw.KeyJ] = "KeyJ"
|
||||
keyNameMap[glfw.KeyK] = "KeyK"
|
||||
keyNameMap[glfw.KeyL] = "KeyL"
|
||||
keyNameMap[glfw.KeySemicolon] = "Semicolon"
|
||||
keyNameMap[glfw.KeyApostrophe] = "Quote"
|
||||
keyNameMap[glfw.KeyGraveAccent] = "Backquote"
|
||||
keyNameMap[glfw.KeyLeftShift] = "ShiftLeft"
|
||||
keyNameMap[glfw.KeyBackslash] = "Backslash"
|
||||
keyNameMap[glfw.KeyZ] = "KeyZ"
|
||||
keyNameMap[glfw.KeyX] = "KeyX"
|
||||
keyNameMap[glfw.KeyC] = "KeyC"
|
||||
keyNameMap[glfw.KeyV] = "KeyV"
|
||||
keyNameMap[glfw.KeyB] = "KeyB"
|
||||
keyNameMap[glfw.KeyN] = "KeyN"
|
||||
keyNameMap[glfw.KeyM] = "KeyM"
|
||||
keyNameMap[glfw.KeyComma] = "Comma"
|
||||
keyNameMap[glfw.KeyPeriod] = "Period"
|
||||
keyNameMap[glfw.KeySlash] = "Slash"
|
||||
keyNameMap[glfw.KeyRightShift] = "RightShift"
|
||||
keyNameMap[glfw.KeyKPMultiply] = "NumpadMultiply"
|
||||
keyNameMap[glfw.KeyLeftAlt] = "AltLeft"
|
||||
keyNameMap[glfw.KeySpace] = "Space"
|
||||
keyNameMap[glfw.KeyCapsLock] = "CapsLock"
|
||||
keyNameMap[glfw.KeyF1] = "F1"
|
||||
keyNameMap[glfw.KeyF2] = "F2"
|
||||
keyNameMap[glfw.KeyF3] = "F3"
|
||||
keyNameMap[glfw.KeyF4] = "F4"
|
||||
keyNameMap[glfw.KeyF5] = "F5"
|
||||
keyNameMap[glfw.KeyF6] = "F6"
|
||||
keyNameMap[glfw.KeyF7] = "F7"
|
||||
keyNameMap[glfw.KeyF8] = "F8"
|
||||
keyNameMap[glfw.KeyF9] = "F9"
|
||||
keyNameMap[glfw.KeyF10] = "F10"
|
||||
keyNameMap[glfw.KeyPause] = "Pause"
|
||||
keyNameMap[glfw.KeyScrollLock] = "ScrollLock"
|
||||
keyNameMap[glfw.KeyKP7] = "Numpad7"
|
||||
keyNameMap[glfw.KeyKP8] = "Numpad8"
|
||||
keyNameMap[glfw.KeyKP9] = "Numpad9"
|
||||
keyNameMap[glfw.KeyKPSubtract] = "NumpadSubtract"
|
||||
keyNameMap[glfw.KeyKP4] = "Numpad4"
|
||||
keyNameMap[glfw.KeyKP5] = "Numpad5"
|
||||
keyNameMap[glfw.KeyKP6] = "Numpad6"
|
||||
keyNameMap[glfw.KeyKPAdd] = "NumpadAdd"
|
||||
keyNameMap[glfw.KeyKP1] = "Numpad1"
|
||||
keyNameMap[glfw.KeyKP2] = "Numpad2"
|
||||
keyNameMap[glfw.KeyKP3] = "Numpad3"
|
||||
keyNameMap[glfw.KeyKP0] = "Numpad0"
|
||||
keyNameMap[glfw.KeyKPDecimal] = "NumpadDecimal"
|
||||
keyNameMap[glfw.KeyPrintScreen] = "PrintScreen"
|
||||
// keyNameMap[glfw.KeyNonUSBackslash] = "IntlBackslash"
|
||||
keyNameMap[glfw.KeyF11] = "F11"
|
||||
keyNameMap[glfw.KeyF12] = "F12"
|
||||
keyNameMap[glfw.KeyKPEqual] = "NumpadEqual"
|
||||
keyNameMap[glfw.KeyF13] = "F13"
|
||||
keyNameMap[glfw.KeyF14] = "F14"
|
||||
keyNameMap[glfw.KeyF15] = "F15"
|
||||
keyNameMap[glfw.KeyF16] = "F16"
|
||||
keyNameMap[glfw.KeyF17] = "F17"
|
||||
keyNameMap[glfw.KeyF18] = "F18"
|
||||
keyNameMap[glfw.KeyF19] = "F19"
|
||||
// keyNameMap[glfw.KeyUndo] = "Undo"
|
||||
// keyNameMap[glfw.KeyPaste] = "Paste"
|
||||
// keyNameMap[glfw.KeyAudioNext] = "MediaTrackPrevious"
|
||||
// keyNameMap[glfw.KeyCut] = "Cut"
|
||||
// keyNameMap[glfw.KeyCopy] = "Copy"
|
||||
// keyNameMap[glfw.KeyAudioNext] = "MediaTrackNext"
|
||||
keyNameMap[glfw.KeyKPEnter] = "NumpadEnter"
|
||||
keyNameMap[glfw.KeyRightControl] = "ControlRight"
|
||||
// keyNameMap[glfw.KeyMute] = "AudioVolumeMute"
|
||||
// keyNameMap[glfw.KeyAudioPlay] = "MediaPlayPause"
|
||||
// keyNameMap[glfw.KeyAudioStop] = "MediaStop"
|
||||
// keyNameMap[glfw.KeyVolumeDown] = "AudioVolumeDown"
|
||||
// keyNameMap[glfw.KeyVolumeUp] = "AudioVolumeUp"
|
||||
keyNameMap[glfw.KeyKPDivide] = "NumpadDivide"
|
||||
keyNameMap[glfw.KeyRightAlt] = "AltRight"
|
||||
// keyNameMap[glfw.KeyHelp] = "Help"
|
||||
keyNameMap[glfw.KeyHome] = "Home"
|
||||
keyNameMap[glfw.KeyUp] = "ArrowUp"
|
||||
keyNameMap[glfw.KeyPageUp] = "PageUp"
|
||||
keyNameMap[glfw.KeyLeft] = "ArrowLeft"
|
||||
keyNameMap[glfw.KeyRight] = "ArrowRight"
|
||||
keyNameMap[glfw.KeyEnd] = "End"
|
||||
keyNameMap[glfw.KeyDown] = "ArrowDown"
|
||||
keyNameMap[glfw.KeyInsert] = "Insert"
|
||||
keyNameMap[glfw.KeyDelete] = "Delete"
|
||||
// keyNameMap[glfw.KeyApplication] = "ContextMenu"
|
||||
|
||||
keyRuneMap[glfw.Key0] = '0'
|
||||
keyRuneMap[glfw.Key1] = '1'
|
||||
keyRuneMap[glfw.Key2] = '2'
|
||||
keyRuneMap[glfw.Key3] = '3'
|
||||
keyRuneMap[glfw.Key4] = '4'
|
||||
keyRuneMap[glfw.Key5] = '5'
|
||||
keyRuneMap[glfw.Key6] = '6'
|
||||
keyRuneMap[glfw.Key7] = '7'
|
||||
keyRuneMap[glfw.Key8] = '8'
|
||||
keyRuneMap[glfw.Key9] = '9'
|
||||
keyRuneMap[glfw.KeyMinus] = '-'
|
||||
keyRuneMap[glfw.KeyEqual] = '='
|
||||
keyRuneMap[glfw.KeyTab] = '\t'
|
||||
keyRuneMap[glfw.KeyQ] = 'Q'
|
||||
keyRuneMap[glfw.KeyW] = 'W'
|
||||
keyRuneMap[glfw.KeyE] = 'E'
|
||||
keyRuneMap[glfw.KeyR] = 'R'
|
||||
keyRuneMap[glfw.KeyT] = 'T'
|
||||
keyRuneMap[glfw.KeyY] = 'Y'
|
||||
keyRuneMap[glfw.KeyU] = 'U'
|
||||
keyRuneMap[glfw.KeyI] = 'I'
|
||||
keyRuneMap[glfw.KeyO] = 'O'
|
||||
keyRuneMap[glfw.KeyP] = 'P'
|
||||
keyRuneMap[glfw.KeyLeftBracket] = '['
|
||||
keyRuneMap[glfw.KeyRightBracket] = ']'
|
||||
keyRuneMap[glfw.KeyEnter] = '\n'
|
||||
keyRuneMap[glfw.KeyA] = 'A'
|
||||
keyRuneMap[glfw.KeyS] = 'S'
|
||||
keyRuneMap[glfw.KeyD] = 'D'
|
||||
keyRuneMap[glfw.KeyF] = 'F'
|
||||
keyRuneMap[glfw.KeyG] = 'G'
|
||||
keyRuneMap[glfw.KeyH] = 'H'
|
||||
keyRuneMap[glfw.KeyJ] = 'J'
|
||||
keyRuneMap[glfw.KeyK] = 'K'
|
||||
keyRuneMap[glfw.KeyL] = 'L'
|
||||
keyRuneMap[glfw.KeySemicolon] = ';'
|
||||
keyRuneMap[glfw.KeyApostrophe] = '\''
|
||||
keyRuneMap[glfw.KeyGraveAccent] = '`'
|
||||
keyRuneMap[glfw.KeyBackslash] = '\\'
|
||||
keyRuneMap[glfw.KeyZ] = 'Z'
|
||||
keyRuneMap[glfw.KeyX] = 'X'
|
||||
keyRuneMap[glfw.KeyC] = 'C'
|
||||
keyRuneMap[glfw.KeyV] = 'V'
|
||||
keyRuneMap[glfw.KeyB] = 'B'
|
||||
keyRuneMap[glfw.KeyN] = 'N'
|
||||
keyRuneMap[glfw.KeyM] = 'M'
|
||||
keyRuneMap[glfw.KeyComma] = ','
|
||||
keyRuneMap[glfw.KeyPeriod] = '.'
|
||||
keyRuneMap[glfw.KeySlash] = '/'
|
||||
keyRuneMap[glfw.KeyKPMultiply] = '*'
|
||||
keyRuneMap[glfw.KeySpace] = ' '
|
||||
keyRuneMap[glfw.KeyKP7] = '7'
|
||||
keyRuneMap[glfw.KeyKP8] = '8'
|
||||
keyRuneMap[glfw.KeyKP9] = '9'
|
||||
keyRuneMap[glfw.KeyKPSubtract] = '-'
|
||||
keyRuneMap[glfw.KeyKP4] = '4'
|
||||
keyRuneMap[glfw.KeyKP5] = '5'
|
||||
keyRuneMap[glfw.KeyKP6] = '6'
|
||||
keyRuneMap[glfw.KeyKPAdd] = '+'
|
||||
keyRuneMap[glfw.KeyKP1] = '1'
|
||||
keyRuneMap[glfw.KeyKP2] = '2'
|
||||
keyRuneMap[glfw.KeyKP3] = '3'
|
||||
keyRuneMap[glfw.KeyKP0] = '0'
|
||||
keyRuneMap[glfw.KeyKPDecimal] = '.'
|
||||
keyRuneMap[glfw.KeyKPEqual] = '='
|
||||
keyRuneMap[glfw.KeyKPEnter] = '\n'
|
||||
keyRuneMap[glfw.KeyKPDivide] = '/'
|
||||
}
|
||||
|
||||
func keyName(key glfw.Key) string {
|
||||
if int(key) >= len(keyNameMap) {
|
||||
return "Unidentified"
|
||||
}
|
||||
name := keyNameMap[key]
|
||||
if name == "" {
|
||||
return "Unidentified"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func keyRune(key glfw.Key) rune {
|
||||
if int(key) >= len(keyNameMap) {
|
||||
return 0
|
||||
}
|
||||
return keyRuneMap[key]
|
||||
}
|
Loading…
Reference in a new issue