-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathnoodle.go
230 lines (188 loc) · 6.2 KB
/
noodle.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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
//Package noodle is a WebGL game engine, designed for low level access for fast and efficent 3D applications
package noodle
/* Here is a list of interesting Readings
https://blog.scottlogic.com/2019/11/18/drawing-lines-with-webgl.html - Line Rendering
https://github.com/davidwparker/programmingtil-webgl/tree/master/0016-single-line - More Lines
https://mattdesl.svbtle.com/drawing-lines-is-hard - Even more lines
https://github.com/mattdesl/webgl-lines/blob/master/expanded/frag.glsl - A line shader
https://stdiopt.github.io/gowasm-experiments/rainbow-mouse/ - Example Go WASM
https://www.gamedev.net/forums/topic/696879-glsl-9-slicing/ - 9 Slice
https://github.com/Lachee/engi/blob/master/SpriteRenderer.go - Engi SpriteRenderer Renderer
*/
import (
"log"
"syscall/js"
)
var (
//GL gives direct access to the WebGL component of the canvas.
GL *WebGL
document js.Value
canvas js.Value
frameRenderFunc js.Func
inputHandler *InputHandler
app Application
width int
height int
texture *Texture
frameTime float64
deltaTime float64
frameCount int64
awaiter chan int
//DebugDraw causes renderers to display debug information
DebugDraw = false
//DebugDrawLoops is a less efficent way of drawing, but preserves the box representation when drawing
DebugDrawLoops = true
//AlwaysDraw continously draws
AlwaysDraw = true
)
//GetFrameTime returns the time the last frame was rendered
func GetFrameTime() float64 { return frameTime }
//GetDeltaTime returns a high accuracy difference in time between the last frame and the current one.
func GetDeltaTime() float64 { return deltaTime }
//GetFrameCount returns the current frame
func GetFrameCount() int64 { return frameCount }
//DT returns a less accurate version of GetDeltaTime, for all your 32bit mathmatic needs.
func DT() float32 { return float32(deltaTime) }
//Input returns the current input handler
func Input() *InputHandler {
return inputHandler
}
//Width gets the width of the screen
func Width() int {
return width
}
//Height gets the width of the screen
func Height() int {
return height
}
//Run setups the WebGL context and runs the application. It is blocking and returns an exit code if Exit() is ever called.
func Run(application Application) int {
app = application
inputHandler = newInput()
document = js.Global().Get("document")
canvas = document.Call("getElementById", "gocanvas")
//Set the width and height of the canvas to conver the entire screen
width = document.Get("body").Get("clientWidth").Int()
height = document.Get("body").Get("clientHeight").Int()
SetCanvasSize(width, height)
awaiter = make(chan int, 0)
//Get the GL context
contextOptions := js.Global().Get("JSON").Call("parse", "{ \"desynchronized\": true }")
context := canvas.Call("getContext", "webgl", contextOptions)
if context.IsUndefined() {
context = canvas.Call("getContext", "experimental-webgl", contextOptions)
}
if context.IsUndefined() {
js.Global().Call("alert", "browser might not support webgl")
return 0
}
//Create a new GL instance
GL = newWebGL(context)
//Setup the animation frame
if !app.Start() {
log.Println("Failed to start the application")
return 0
}
//Cursor Moved
onMouseChangeEvent := AddEventListener("mousemove", func(this js.Value, args []js.Value) interface{} {
evt := args[0]
x := evt.Get("offsetX").Int()
y := evt.Get("offsetY").Int()
inputHandler.setMousePosition(x, y)
return nil
})
defer onMouseChangeEvent.Release()
//Mouse Up
onMouseUpEvent := AddEventListener("mouseup", func(this js.Value, args []js.Value) interface{} {
evt := args[0]
button := evt.Get("button").Int()
inputHandler.setMouseUp(button)
return nil
})
defer onMouseUpEvent.Release()
//Mouse Down
onMouseDownEvent := AddEventListener("mousedown", func(this js.Value, args []js.Value) interface{} {
evt := args[0]
button := evt.Get("button").Int()
inputHandler.setMouseDown(button)
return nil
})
defer onMouseDownEvent.Release()
//Mouse Scroll
onMouseScrollEvent := AddEventListener("wheel", func(this js.Value, args []js.Value) interface{} {
evt := args[0]
scroll := evt.Get("deltaY").Float()
inputHandler.setMouseScroll(float32(scroll))
return nil
})
defer onMouseScrollEvent.Release()
//Key Down
onKeyDownEvent := AddEventListener("keydown", func(this js.Value, args []js.Value) interface{} {
//Get the event and ditch repeated keys
evt := args[0]
if evt.Get("repeat").Bool() {
return nil
}
//Set the key code
key := evt.Get("keyCode").Int()
inputHandler.setKeyDown(key)
return nil
})
defer onKeyDownEvent.Release()
//Key Up
onKeyUpEvent := AddEventListener("keyup", func(this js.Value, args []js.Value) interface{} {
evt := args[0]
key := evt.Get("keyCode").Int()
inputHandler.setKeyUp(key)
return nil
})
defer onKeyUpEvent.Release()
//Begin rendering
GL.Viewport(0, 0, width, height)
frameRenderFunc = js.FuncOf(onRequestAnimationFrame)
defer frameRenderFunc.Release()
js.Global().Call("requestAnimationFrame", frameRenderFunc)
return <-awaiter
}
//AddEventListener adds a new event listener to the canvas. It will return a JS function that needs to be Released() when its no longer required.
func AddEventListener(event string, fn func(this js.Value, args []js.Value) interface{}) js.Func {
jsfunc := js.FuncOf(fn)
document.Call("addEventListener", event, jsfunc)
return jsfunc
}
//SetCanvasSize the size of the canvas
func SetCanvasSize(w, h int) {
canvas.Set("width", w)
canvas.Set("height", h)
width = canvas.Get("width").Int()
height = canvas.Get("height").Int()
log.Println("resized canvas")
}
//RequestRedraw requests for a new animation frame
func RequestRedraw() {
js.Global().Call("requestAnimationFrame", frameRenderFunc)
}
//Exit the application
func Exit() {
awaiter <- 1
}
//onRequestAnimationFrame callback for animations
func onRequestAnimationFrame(this js.Value, args []js.Value) interface{} {
//Setupt he time
time := args[0].Float() / 1000
deltaTime = time - frameTime
frameTime = time
frameCount++
//Update the input
inputHandler.update()
//Call update on the Application
app.Update(float32(deltaTime))
//Render everything
app.Render()
//If we need to draw again, then do so
if AlwaysDraw {
RequestRedraw()
}
//Return nil to JS
return nil
}