-
Notifications
You must be signed in to change notification settings - Fork 40
/
glfwcanvas.go
184 lines (162 loc) · 4.93 KB
/
glfwcanvas.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package glfwcanvas
import (
"fmt"
_ "image/gif" // Imported here so that applications based on this package support these formats by default
_ "image/jpeg"
_ "image/png"
"math"
"runtime"
"time"
"github.com/go-gl/gl/v3.2-core/gl"
"github.com/go-gl/glfw/v3.3/glfw"
"github.com/tfriedel6/canvas"
"github.com/tfriedel6/canvas/backend/goglbackend"
)
// 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 {
return nil, nil, fmt.Errorf("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 {
return nil, nil, fmt.Errorf("Error creating window: %v", err)
}
window.MakeContextCurrent()
// init GL
err = gl.Init()
if err != nil {
return nil, nil, fmt.Errorf("Error initializing GL: %v", err)
}
// set vsync on, enable multisample (if available)
glfw.SwapInterval(1)
gl.Enable(gl.MULTISAMPLE)
// load canvas GL backend
fbw, fbh := window.GetFramebufferSize()
backend, err := goglbackend.New(0, 0, fbw, fbh, nil)
if err != nil {
return nil, nil, fmt.Errorf("Error loading GoGL backend: %v", err)
}
cv := canvas.New(backend)
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, 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, 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, height int) {
if wnd.SizeChange != nil {
wnd.SizeChange(width, height)
} else {
fbw, fbh := window.GetFramebufferSize()
backend.SetBounds(0, 0, fbw, fbh)
}
})
window.SetCloseCallback(func(w *glfw.Window) {
wnd.Close()
})
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()
}
}
// Size returns the current width and height of the window.
// Note that this size may not be the same as the size of the
// framebuffer, since some operating systems scale the window.
// Use the Width/Height/Size function on Canvas to determine
// the drawing size
func (wnd *Window) Size() (int, int) {
return wnd.Window.GetSize()
}
// FramebufferSize returns the current width and height of
// the framebuffer, which is also the internal size of the
// canvas
func (wnd *Window) FramebufferSize() (int, int) {
return wnd.Window.GetFramebufferSize()
}