├── Blinking-Pink
├── blinking-pink.exe
└── blinking.odin
├── Hidden-Animal
├── main.odin
├── nodes.txt
├── sc-fragment-shader.glsl
├── sc-vertex-shader.glsl
└── triangles.txt
├── Moving-Character
├── fragment.glsl
├── moving-character.exe
├── moving-character.odin
└── vertex.glsl
├── Psychedelic-Earth
├── blue-marble.qoi
├── fragment.glsl
├── psychedelic-earth.exe
├── psychedelic-earth.odin
└── vertex.glsl
├── README.md
├── Rainbow-Triangle
├── fragment.glsl
├── rainbow-triangle.exe
├── rainbow-triangle.odin
└── vertex.glsl
├── Readme-Imgs
├── blinking-pink.png
├── hidden-animal.png
├── moving-character.png
├── psychedelic-earth.png
├── rainbow-triangle.jpg
├── rotating-cube.png
└── wavy-words.png
├── Rotating-Cube
├── fragment.glsl
├── rotating-cube.exe
├── rotating-cube.odin
└── vertex.glsl
└── Wavy-Words
├── fragment.glsl
├── vertex.glsl
├── wavy-words.exe
└── wavy-words.odin
/Blinking-Pink/blinking-pink.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Blinking-Pink/blinking-pink.exe
--------------------------------------------------------------------------------
/Blinking-Pink/blinking.odin:
--------------------------------------------------------------------------------
1 | // An elementary program in Odin which creates a window that oscillates
2 | // between pink and blue.
3 | //
4 | // Created by Benjamin Thompson. Available at:
5 | // https://github.com/bg-thompson/OpenGL-Tutorials-In-Odin
6 | // Last updated: 2024.09.15
7 | //
8 | // To compile and run the program with optimizations, use the command
9 | //
10 | // odin run Blinking-Pink -o:speed
11 | //
12 | // Created for educational purposes. Used verbatim, it is probably
13 | // unsuitable for production code.
14 |
15 | package main
16 |
17 | import "vendor:glfw"
18 | import gl "vendor:OpenGL"
19 | import "core:time"
20 | import "core:math"
21 |
22 | main :: proc() {
23 | // Initialize glfw, specify OpenGL version.
24 | glfw.Init()
25 | defer glfw.Terminate()
26 | glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
27 | glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
28 | glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
29 |
30 | // Create render window.
31 | window := glfw.CreateWindow(800, 600, "Blinking", nil, nil)
32 | assert(window != nil)
33 | defer glfw.DestroyWindow(window)
34 | glfw.MakeContextCurrent(window)
35 |
36 | // Enable Vsync.
37 | glfw.SwapInterval(1)
38 |
39 | // Load OpenGL function pointers.
40 | gl.load_up_to(3,3, glfw.gl_set_proc_address)
41 |
42 | // Set normalized device coords to window coords transformation.
43 | w, h := glfw.GetFramebufferSize(window)
44 | gl.Viewport(0,0,w,h)
45 |
46 | // Start blinking timer.
47 | watch : time.Stopwatch
48 | time.stopwatch_start(&watch)
49 |
50 | // Render loop
51 | for !glfw.WindowShouldClose(window) {
52 | glfw.PollEvents()
53 | // Note: glfw.PollEvents will block on Windows during window resize, hence
54 | // strange rendering occurs during resize. To keep this example simple, we
55 | // will not fix this here. A partial solution is found in Rainbow-Triangle
56 | // and subsequent examples.
57 |
58 | // Create oscillating value (osl).
59 | raw_duration := time.stopwatch_duration(watch)
60 | secs := f32(time.duration_seconds(raw_duration))
61 | osl := (math.sin(3 * secs) + 1) * 0.5
62 |
63 | // Clear screen with color.
64 | gl.ClearColor(0.9 * osl, 0.2, 0.8, 1) // Pink: 0.9, 0.2, 0.8
65 | gl.Clear(gl.COLOR_BUFFER_BIT)
66 |
67 | // Render screen with background color.
68 | glfw.SwapBuffers(window)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Hidden-Animal/main.odin:
--------------------------------------------------------------------------------
1 | // A program which draws a hidden triangulated animal.
2 | // Moving the cursor over a triangle reveals it, in time
3 | // revealing the animal.
4 |
5 | // Inspired by an image in "Vector Elephant set." by Rashevskaya Art at
6 | // https://thehungryjpeg.com/product/3734798-vector-elephant-set-elephant-triangle-geometric-illustration
7 | // Note: the geometry underlying the image is simple geometry and therefore
8 | // public domain under US copywrite law.
9 |
10 | // Created by Benjamin Thompson. Available at:
11 | // https://github.com/bg-thompson/OpenGL-Tutorials-In-Odin
12 | // Last updated: 2024.09.15
13 | //
14 | // To compile and run the program with optimizations, use the command
15 | //
16 | // odin run Hidden-Animal -o:speed
17 | //
18 | // Created for educational purposes. Used verbatim, it is probably
19 | // unsuitable for production code.
20 |
21 | package animal
22 |
23 | import f "core:fmt"
24 | import s "core:strings"
25 | import v "core:strconv"
26 | import "core:time"
27 | import m "core:math"
28 | import mr "core:math/rand"
29 | import "core:os"
30 | import "vendor:glfw"
31 | import gl "vendor:OpenGL"
32 | import "base:runtime"
33 |
34 | cam_pos_wv : [2] f32
35 | mouse_wv : [2] f32
36 |
37 | Triangle :: struct {
38 | a,b,c : Node,
39 | diameter2 : f32,
40 | color : Solid_Color,
41 | hovering : bool,
42 | }
43 |
44 | triangles : [] Triangle
45 |
46 | Solid_Color :: distinct [3] f32
47 |
48 | // Global Colors
49 | color_black :: Solid_Color{0,0,0}
50 | color_white :: Solid_Color{1,1,1}
51 | color_cyan :: Solid_Color{0,1,1}
52 | color_grey :: Solid_Color{0.5,0.5,0.5}
53 |
54 | main :: proc() {
55 | // Initialize window and OpenGL.
56 | glfw.Init()
57 | defer glfw.Terminate()
58 | glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
59 | glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
60 | glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
61 |
62 | // Create window for the illustration.
63 | global_window = glfw.CreateWindow(INITIAL_WINDOW_SIZE_W,
64 | INITIAL_WINDOW_SIZE_H,
65 | "Hidden animal",
66 | nil, nil)
67 | assert(global_window != nil)
68 | defer glfw.DestroyWindow(global_window)
69 | glfw.MakeContextCurrent(global_window)
70 | glfw.SetWindowSizeLimits(global_window, MINIMUM_WINDOW_SIZE_W, MINIMUM_WINDOW_SIZE_H, glfw.DONT_CARE, glfw.DONT_CARE)
71 |
72 | // Initialize the window and GPU buffers, and
73 | // initialize textures used to render pieces.
74 | init_window(global_window)
75 |
76 | // Load data, create triangles.
77 | import_nodes()
78 | import_triangles()
79 |
80 | // Main loop.
81 | for !glfw.WindowShouldClose(global_window) {
82 |
83 | // Check for window resize and process keyboard presses.
84 | glfw.PollEvents()
85 |
86 | cam_zom = 1 // pixels-to-world-space-ratio
87 |
88 | process_keyboard_events(global_window)
89 | process_mouse_events(global_window)
90 |
91 | // Keyboard camera movement
92 | dx := cam_zom * CAMERA_SCROLL_SPEED
93 |
94 | if shift_camera.x { cam_pos_wv.x -= dx }
95 | if shift_camera.y { cam_pos_wv.y -= dx }
96 | if shift_camera.z { cam_pos_wv.x += dx }
97 | if shift_camera.w { cam_pos_wv.y += dx }
98 |
99 | // Reset keyboard camera movement
100 | shift_camera = false
101 |
102 | // Change color of triangle if cursor underneath it.
103 | for t,i in triangles {
104 | xdist := mouse_wv.x - t.a.coord.x
105 | ydist := mouse_wv.y - t.a.coord.y
106 | // Don't call inside_triangles for triangles that are far away.
107 | if xdist * xdist + ydist * ydist <= t.diameter2 {
108 | if inside_triangle(mouse_wv, t.a.coord, t.b.coord, t.c.coord) {
109 | if !t.hovering {
110 | triangles[i].hovering = true
111 | current_color := triangles[i].color
112 | triangles[i].color = update_color(current_color)
113 | }
114 | } else {
115 | triangles[i].hovering = false
116 | }
117 | }
118 | }
119 |
120 | // Render screen.
121 | render_screen(global_window)
122 | }
123 | }
124 |
125 | update_color :: proc(color : Solid_Color) -> Solid_Color {
126 | sufficently_different := false
127 | nr : f32
128 | for !sufficently_different {
129 | r := mr.int_max(128)
130 | nr = 0.5 * f32(r) / f32(128) + 0.3
131 | if m.abs(color.r - nr) > 0.2 {
132 | sufficently_different = true
133 | }
134 | }
135 | return Solid_Color{ nr, nr, nr}
136 | }
137 |
138 | process_keyboard_events :: proc(window : glfw.WindowHandle) {
139 | // GetKey returns either glfw.PRESS or glfw.RELEASE.
140 | // ESC
141 | if glfw.GetKey(window, glfw.KEY_ESCAPE) == glfw.PRESS {
142 | glfw.SetWindowShouldClose(window, true)
143 | }
144 | // Right
145 | if glfw.GetKey(window, glfw.KEY_RIGHT) == glfw.PRESS {
146 | shift_camera.x = true
147 | }
148 | // Up
149 | if glfw.GetKey(window, glfw.KEY_UP) == glfw.PRESS {
150 | shift_camera.y = true
151 | }
152 | // Left
153 | if glfw.GetKey(window, glfw.KEY_LEFT) == glfw.PRESS {
154 | shift_camera.z = true
155 | }
156 | // Down
157 | if glfw.GetKey(window, glfw.KEY_DOWN) == glfw.PRESS {
158 | shift_camera.w = true
159 | }
160 | }
161 |
162 | process_mouse_events :: proc( window : glfw.WindowHandle) {
163 | // Get mouse position, normalized to a point in [0,1] x [0,1].
164 | // Convert the ridiculous standard of setting the origin to be the top left
165 | // instead of the bottom left.
166 | mousexraw, mouseyraw := glfw.GetCursorPos(window)
167 | mouse_px : [2] f32 = {f32(mousexraw), window_h_px - f32(mouseyraw)}
168 | mouse_wv = cam_zom * mouse_px + cam_pos_wv
169 | }
170 |
171 | // If the user blocks .PollEvents() (e.g. due to resizing the window, or right
172 | // clicking on the menu bar, rendering will freeze, so redraw the window.
173 |
174 | window_refresh :: proc "c" ( window : glfw.WindowHandle ) {
175 | context = runtime.default_context()
176 | w, h : i32
177 | w, h = glfw.GetWindowSize(window)
178 | gl.Viewport(0,0,w,h)
179 | render_screen(global_window)
180 | }
181 |
182 | // Import drawing data
183 | NODEDATA :: string(#load(`nodes.txt`))
184 | TRIANGLES :: string(#load(`triangles.txt`))
185 |
186 | Node :: struct{
187 | coord : [2] f32,
188 | index : i32,
189 | }
190 |
191 | nodes : [] Node
192 |
193 | import_nodes :: proc() {
194 | lines := s.split_lines(NODEDATA)
195 | defer delete(lines)
196 | number_nodes := len(lines) / 2
197 | nodes = make([] Node, number_nodes)
198 | for i in 0..=number_nodes-1 {
199 | nodes[i].index = i32(i)
200 | float, ok := v.parse_f32(s.trim_right_space(lines[2*i]))
201 | assert(ok)
202 | nodes[i].coord.x = float
203 | float2, ok2 := v.parse_f32(s.trim_right_space(lines[2*i + 1]))
204 | assert(ok2)
205 | nodes[i].coord.y = -float2
206 | }
207 | }
208 |
209 | import_triangles :: proc() {
210 | lines := s.split_lines(TRIANGLES)
211 | defer delete(lines)
212 |
213 | triangles = make([] Triangle, len(lines))
214 | for l, i in lines {
215 | if l == "" { continue }
216 | ints := s.fields(l)
217 | int1, ok1 := v.parse_int(ints[0])
218 | int2, ok2 := v.parse_int(ints[1])
219 | int3, ok3 := v.parse_int(ints[2])
220 | assert(ok1 && ok2 && ok3)
221 | na := nodes[int1]
222 | nb := nodes[int2]
223 | nc := nodes[int3]
224 | triangles[i].a = na
225 | triangles[i].b = nb
226 | triangles[i].c = nc
227 | // Compute diameter of triangle, i.e. its longest side length.
228 | l1 := na.coord - nb.coord
229 | l2 := nb.coord - nc.coord
230 | l3 := nc.coord - na.coord
231 | ll :: proc ( vec : [2] f32 ) -> f32 { return vec.x * vec.x + vec.y * vec.y }
232 | diameter2 := max(ll(l1), ll(l2), ll(l3))
233 | triangles[i].diameter2 = diameter2
234 | triangles[i].color = color_white
235 | triangles[i].hovering = false
236 | }
237 | }
238 |
239 | // Rendering Types
240 | VAO :: distinct u32 // Vertex array object
241 | VBO :: distinct u32 // Vertex buffer object
242 | Shader :: distinct u32
243 |
244 | // Rendering Globals
245 | solid_color_vao : VAO
246 | solid_color_vbo : VBO
247 | global_window : glfw.WindowHandle
248 | window_w_px : f32
249 | window_h_px : f32
250 |
251 | // Actions globals
252 | shift_camera : [4] bool // 0-4: R, U, L, D
253 |
254 | CAMERA_SCROLL_SPEED :: 25 // Feels about right.
255 |
256 | // Rendering Constants
257 | INITIAL_WINDOW_SIZE_W :: 1920
258 | INITIAL_WINDOW_SIZE_H :: 1080
259 | MINIMUM_WINDOW_SIZE_W :: 200
260 | MINIMUM_WINDOW_SIZE_H :: 200
261 |
262 | // Picture constants and globals.
263 | // The camera position is the lower-left coord.
264 | // Setting the camera to the center of the screen leads to code
265 | // that is more complicated.
266 | //
267 | // The zoom is the ratio of 1 pixel to 1 unit it world-space.
268 |
269 | cam_zom : f32
270 | sc_shader : Shader
271 |
272 | // Maximum number of vertices in vertex buffers.
273 | // Note: a large number of grid points doesn't lead to
274 | // an assertion error.
275 | VB_STAGE_SIZE_IN_FLOATS :: (1 << 10) * 6 // About 6000 floats.
276 |
277 | Vertex_Buffer_Stage :: struct {
278 | vb : [VB_STAGE_SIZE_IN_FLOATS] f32,
279 | curr_index : int,
280 | fpv : int, // floats per vertex
281 | }
282 |
283 | // Number of floats in a color vertex set to the GPU.
284 | FLOATS_IN_COLOR_VERTEX :: 5
285 | // | x | y | r | g | b |
286 | // | xy coords (f32 x 2) | rgb color (f32 x 3) |
287 |
288 | init_window :: proc( window : glfw.WindowHandle) {
289 | // Enable V-Sync
290 | glfw.SwapInterval(1)
291 |
292 | // Load OpenGL 3.3 function pointers.
293 | gl.load_up_to(3, 3, glfw.gl_set_proc_address)
294 | w,h := glfw.GetFramebufferSize(window)
295 | gl.Viewport(0,0,w,h)
296 |
297 | // Window_refresh is defined below.
298 | glfw.SetWindowRefreshCallback(window, window_refresh)
299 |
300 | // Setup a Vertex Array Object for solid colors.
301 | gl.GenVertexArrays(1, cast(^u32) &solid_color_vao)
302 | gl.BindVertexArray( u32(solid_color_vao) )
303 |
304 | // Set up solid_color_vbo.
305 | gl.GenBuffers(1, cast(^u32) &solid_color_vbo)
306 | gl.BindBuffer(gl.ARRAY_BUFFER, u32(solid_color_vbo) )
307 |
308 | // Create GPU buffer space. Remember to enable!
309 | gl.BufferData(gl.ARRAY_BUFFER, // target
310 | VB_STAGE_SIZE_IN_FLOATS * size_of(f32), // size
311 | nil, // fill with SubData later
312 | gl.DYNAMIC_DRAW) // usage
313 |
314 | // Position attributes for solid_vbo_color.
315 | gl.VertexAttribPointer(0, // index
316 | 2, // size
317 | gl.FLOAT, // type
318 | gl.FALSE, // normalized
319 | FLOATS_IN_COLOR_VERTEX * size_of(f32), // stride
320 | 0 * size_of(f32), // offset
321 | )
322 |
323 |
324 |
325 | // Color attributes for solid_vbo_color.
326 | gl.VertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, FLOATS_IN_COLOR_VERTEX * size_of(f32), 2 * size_of(f32))
327 |
328 | // Enable the attributes for vbo_color.
329 | gl.EnableVertexAttribArray(0)
330 | gl.EnableVertexAttribArray(1)
331 |
332 | // Compile vertex and fragment shaders.
333 | shader_comp1_ok : bool
334 | // Load shaders into executable at compile time.
335 | vs1 :: string(#load("sc-vertex-shader.glsl"))
336 | fs1 :: string(#load("sc-fragment-shader.glsl"))
337 |
338 | // sc_shader is a global variable.
339 | sc_shader_u32 : u32
340 | sc_shader_u32, shader_comp1_ok = gl.load_shaders_source(vs1, fs1)
341 | sc_shader = Shader(sc_shader_u32)
342 |
343 | if !shader_comp1_ok {
344 | f.println("ERROR: Loading and/or compilation of shaders failed!")
345 | os.exit(1)
346 | }
347 |
348 | // Set initial camera position.
349 | cam_pos_wv.x = -900
350 | cam_pos_wv.y = -600
351 | return
352 | }
353 | draw_triangle_solid_color :: proc( a,b,c : [2] f32, color : Solid_Color, vas : ^Vertex_Buffer_Stage) {
354 | assert(vas.fpv == 5)
355 | cvn := vas.curr_index
356 | // Triangle Coordinates
357 | vas.vb[(cvn + 0) * 5 + 0] = a.x
358 | vas.vb[(cvn + 0) * 5 + 1] = a.y
359 | vas.vb[(cvn + 1) * 5 + 0] = b.x
360 | vas.vb[(cvn + 1) * 5 + 1] = b.y
361 | vas.vb[(cvn + 2) * 5 + 0] = c.x
362 | vas.vb[(cvn + 2) * 5 + 1] = c.y
363 | // Triangle Colors
364 | for i in 0..=2 {
365 | vas.vb[(cvn + i) * 5 + 2] = color.r
366 | vas.vb[(cvn + i) * 5 + 3] = color.g
367 | vas.vb[(cvn + i) * 5 + 4] = color.b
368 | }
369 | vas.curr_index += 3
370 | }
371 |
372 | render_screen :: proc( window : glfw.WindowHandle) {
373 | // Get window width and height, and tell the GPU about them.
374 | window_w_px_int, window_h_px_int := glfw.GetFramebufferSize(window)
375 | window_w_px = f32(window_w_px_int)
376 | window_h_px = f32(window_h_px_int)
377 |
378 | // Make a buffer to hold the grid squares.
379 | poly_verts1 : Vertex_Buffer_Stage
380 | poly_verts1.curr_index = 0
381 | poly_verts1.fpv = FLOATS_IN_COLOR_VERTEX
382 |
383 | // Draw grid.
384 | // Select grid color based on mouse pressed.
385 |
386 | for trip in triangles {
387 | c1, c2, c3 := trip.a.coord, trip.b.coord, trip.c.coord
388 | draw_triangle_solid_color(c1, c2, c3, trip.color, &poly_verts1)
389 | }
390 |
391 | // Begin rendering.
392 | // Background color.
393 | bgc := color_white
394 | gl.ClearColor(bgc.r, bgc.g, bgc.b, 1)
395 | gl.Clear(gl.COLOR_BUFFER_BIT)
396 |
397 | // Draw solid_color triangles.
398 | gl.UseProgram(u32(sc_shader))
399 | // Set uniforms in solid color shader.
400 | // Note: this can only be done when the shader is active!
401 |
402 | // Calculate width and hidth of window in world-view.
403 | // Send these and cam_posx, cam_posy to the shaders.
404 | window_w_wv := window_w_px * cam_zom
405 | window_h_wv := window_h_px * cam_zom
406 | cam_posx_wv := cam_pos_wv.x
407 | cam_posy_wv := cam_pos_wv.y
408 |
409 | set_uniform_f32 :: proc( float : f32, str : cstring, shader : Shader) {
410 | gl.Uniform1f(gl.GetUniformLocation(u32(shader), str), float)
411 | }
412 |
413 | set_uniform_f32(window_w_wv, "window_w_wv", sc_shader)
414 | set_uniform_f32(window_h_wv, "window_h_wv", sc_shader)
415 | set_uniform_f32(cam_posx_wv, "cam_posx_wv", sc_shader)
416 | set_uniform_f32(cam_posy_wv, "cam_posy_wv", sc_shader)
417 |
418 | gl.BindVertexArray( u32(solid_color_vao) )
419 | gl.BindBuffer(gl.ARRAY_BUFFER, u32(solid_color_vbo))
420 |
421 | gl.BufferSubData(gl.ARRAY_BUFFER, //target
422 | 0, //offset
423 | poly_verts1.curr_index * poly_verts1.fpv * size_of(f32), // size
424 | &poly_verts1.vb) // data pointer
425 |
426 | gl.DrawArrays(gl.TRIANGLES, 0, i32(poly_verts1.curr_index))
427 |
428 | // Display render.
429 | glfw.SwapBuffers(window)
430 |
431 | free_all(context.temp_allocator)
432 | }
433 |
434 | // Determine if a point is inside a triangle.
435 | // Uses the cross-product formula to calculate the sign of sin(theta).
436 | inside_triangle :: proc( p : [2] $T, a, b, c : [2] T) -> bool {
437 | d1 := a - p
438 | d2 := b - p
439 | d3 := c - p
440 | s1 := int( (d1.x * d2.y - d1.y * d2.x) >= 0)
441 | s2 := int( (d2.x * d3.y - d2.y * d3.x) >= 0)
442 | s3 := int( (d3.x * d1.y - d3.y * d1.x) >= 0)
443 | sign_sum := s1 + s2 + s3
444 | return sign_sum == 0 || sign_sum == 3
445 | }
446 |
--------------------------------------------------------------------------------
/Hidden-Animal/nodes.txt:
--------------------------------------------------------------------------------
1 | -322.22974
2 | 447.87057
3 | -335.09863
4 | 422.64084
5 | -305.46637
6 | 422.97946
7 | -307.66763
8 | 281.92987
9 | -363.88425
10 | 228.93044
11 | -329.98264
12 | 121.64817
13 | -309.50836
14 | 52.682278
15 | -339.56122
16 | 52.562546
17 | -351.41473
18 | 70.522415
19 | -399.42746
20 | 80.460205
21 | -385.53848
22 | 83.57325
23 | -399.18796
24 | 69.564552
25 | -464.80136
26 | 76.868233
27 | -392.96188
28 | 52.682278
29 | -371.64951
30 | -5.6274252
31 | -275.74384
32 | -50.527092
33 | -267.87573
34 | 42.501141
35 | -227.40649
36 | -10.498289
37 | -237.05815
38 | -59.603191
39 | -246.03249
40 | -113.27993
41 | -298.35461
42 | -167.8033
43 | -264.31985
44 | -197.60489
45 | -161.70752
46 | -177.45496
47 | -169.15793
48 | -156.45837
49 | -176.439
50 | -68.238884
51 | -183.38141
52 | 3.5558722
53 | -263.64252
54 | 106.16819
55 | -193.54105
56 | 100.91904
57 | -100.24173
58 | 84.324974
59 | -128.68871
60 | -120.89966
61 | -56.893955
62 | -169.49658
63 | -51.644814
64 | -206.91791
65 | 18.625998
66 | -197.94356
67 | 70.609467
68 | -140.88026
69 | 90.59008
70 | -44.533066
71 | 27.092358
72 | 52.15279
73 | -133.5992
74 | 222.32668
75 | -146.63739
76 | 421.45554
77 | -146.63739
78 | 467.8512
79 | -181.01083
80 | 503.07126
81 | -83.816994
82 | 504.59518
83 | -72.133408
84 | 497.48346
85 | -92.960663
86 | 474.28561
87 | -59.264538
88 | 309.86887
89 | 10.328962
90 | 172.71379
91 | -13.038198
92 | 216.73888
93 | 17.271379
94 | 310.88483
95 | 96.68586
96 | 372.35062
97 | 45.887688
98 | 417.561
99 | 83.986313
100 | 456.50626
101 | 120.22234
102 | 478.18015
103 | 123.60889
104 | 519.32666
105 | 161.70752
106 | 504.93387
107 | 198.62086
108 | 459.04617
109 | 120.22234
110 | 418.069
111 | 104.13626
112 | 240.27536
113 | 128.68871
114 | 241.29132
115 | 182.19612
116 | 273.4635
117 | 273.9715
118 | 163.57011
119 | 382.34094
120 | 273.12488
121 | 251.11232
122 | 273.29416
123 | 359.65106
124 | 273.9715
125 | 329.50369
126 | 392.96188
127 | 325.19333
128 | 460.49097
129 | 419.54248
130 | 450.194
131 | 422.17661
132 | 470.30905
133 | 398.70905
134 | 485.87427
135 | 359.91571
136 | 501.20001
137 | 306.51508
138 | 491.8609
139 | 434.49371
140 | 327.30957
141 | 446.51596
142 | 343.22632
143 | 514.92413
144 | 325.6163
145 | 511.19894
146 | 419.25427
147 | 446.85458
148 | 474.28561
149 | 408.24802
150 | 506.28848
151 | 464.12601
152 | 516.44812
153 | 521.18927
154 | 494.77423
155 | 434.49371
156 | 67.730896
157 | 532.70355
158 | 154.76511
159 | 541.84717
160 | 46.903648
161 | 543.03247
162 | 204.20866
163 | 560.64252
164 | 283.45383
165 | 542.01654
166 | 300.21719
167 | 555.05475
168 | 43.855759
169 | 552.17615
170 | 161.03023
171 | 504.25656
172 | -75.858604
173 | 408.41733
174 | -132.41391
175 | 257.88541
176 | -98.040474
177 | 263.30386
178 | -143.75882
179 | 149.00798
180 | 114.12656
181 |
--------------------------------------------------------------------------------
/Hidden-Animal/sc-fragment-shader.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | in vec3 ourColor;
4 | out vec4 FragColor;
5 |
6 | void main() {
7 | FragColor = vec4(ourColor, 1);
8 | }
9 |
--------------------------------------------------------------------------------
/Hidden-Animal/sc-vertex-shader.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | layout (location = 0) in vec2 aPos;
4 | layout (location = 1) in vec3 aColor;
5 |
6 | out vec3 ourColor;
7 |
8 | uniform float cam_posx_wv;
9 | uniform float cam_posy_wv;
10 | uniform float window_w_wv;
11 | uniform float window_h_wv;
12 |
13 | void main() {
14 | // Convert (x,y) coordinates in pixels to [0,1] x [0,1] coordinates.
15 | float sposx = (aPos.x - cam_posx_wv) / window_w_wv;
16 | float sposy = (aPos.y - cam_posy_wv) / window_h_wv;
17 | // Convert [0,1] x [0,1] into [-1,1] x [-1,1]
18 | gl_Position = vec4(2 * sposx - 1, 2 * sposy - 1, 0, 1);
19 |
20 | // Pass the color on, unmodified.
21 | ourColor = aColor;
22 | }
23 |
--------------------------------------------------------------------------------
/Hidden-Animal/triangles.txt:
--------------------------------------------------------------------------------
1 | 0 1 2
2 | 1 2 3
3 | 1 3 4
4 | 3 4 5
5 | 4 5 10
6 | 10 11 9
7 | 9 12 11
8 | 5 10 8
9 | 10 8 11
10 | 11 8 13
11 | 8 13 14
12 | 5 8 7
13 | 8 7 14
14 | 5 7 6
15 | 7 14 15
16 | 14 15 19
17 | 14 19 20
18 | 19 20 23
19 | 20 21 23
20 | 21 23 22
21 | 19 23 24
22 | 15 18 19
23 | 18 19 24
24 | 15 18 17
25 | 18 24 17
26 | 7 6 15
27 | 6 15 17
28 | 6 17 16
29 | 16 26 25
30 | 26 25 27
31 | 16 17 25
32 | 17 25 24
33 | 27 25 28
34 | 28 25 29
35 | 25 24 29
36 | 24 29 23
37 | 23 29 30
38 | 22 23 30
39 | 30 22 31
40 | 31 30 32
41 | 30 32 33
42 | 29 30 33
43 | 29 33 34
44 | 29 34 35
45 | 29 35 28
46 | 35 28 36
47 | 35 36 44
48 | 44 36 45
49 | 45 36 43
50 | 36 43 37
51 | 43 37 42
52 | 37 42 38
53 | 42 41 40
54 | 38 42 40
55 | 38 40 39
56 | 44 45 55
57 | 45 55 46
58 | 55 46 47
59 | 46 47 48
60 | 47 48 49
61 | 47 54 49
62 | 54 53 52
63 | 54 49 50
64 | 54 50 52
65 | 50 52 51
66 | 33 88 87
67 | 33 87 34
68 | 34 87 89
69 | 34 35 89
70 | 35 44 89
71 | 44 89 58
72 | 44 55 58
73 | 55 56 58
74 | 56 57 58
75 | 57 60 58
76 | 60 61 58
77 | 60 61 62
78 | 61 62 64
79 | 62 64 63
80 | 64 63 68
81 | 68 66 64
82 | 64 65 66
83 | 66 67 68
84 | 61 59 58
85 | 89 58 77
86 | 89 77 87
87 | 88 87 86
88 | 86 87 77
89 | 86 85 77
90 | 85 79 77
91 | 85 83 79
92 | 79 83 84
93 | 79 78 84
94 | 79 77 78
95 | 59 58 77
96 | 80 82 81
97 | 81 80 84
98 | 80 84 78
99 | 59 69 77
100 | 69 71 77
101 | 71 78 77
102 | 69 70 71
103 | 71 70 72
104 | 70 72 73
105 | 72 76 75
106 | 72 73 75
107 | 75 73 74
108 |
--------------------------------------------------------------------------------
/Moving-Character/fragment.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | in vec2 TexCoords;
4 | out vec4 FragColor;
5 |
6 | uniform sampler2D glyph_texture;
7 |
8 | void main() {
9 | // Cyan color is: 0.5, 1, 1
10 | FragColor = vec4(0.5, 1, 1, texture(glyph_texture, TexCoords).r);
11 | }
12 |
--------------------------------------------------------------------------------
/Moving-Character/moving-character.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Moving-Character/moving-character.exe
--------------------------------------------------------------------------------
/Moving-Character/moving-character.odin:
--------------------------------------------------------------------------------
1 | // An elementary program in Odin which renders a character from a
2 | // font and moves it in a circle.
3 | //
4 | // Created by Benjamin Thompson. Available at:
5 | // https://github.com/bg-thompson/OpenGL-Tutorials-In-Odin
6 | // Last updated: 2024.09.15
7 | //
8 | // To compile and run the program with optimizations, use the command
9 | //
10 | // odin run Moving-Character -o:speed
11 | //
12 | // Created for educational purposes. Used verbatim, it is probably
13 | // unsuitable for production code.
14 |
15 | package main
16 |
17 | import "vendor:glfw"
18 | import gl "vendor:OpenGL"
19 | import tt "vendor:stb/truetype"
20 | import "core:time"
21 | import m "core:math"
22 | import f "core:fmt"
23 | import "core:os"
24 | import "base:runtime"
25 |
26 | // Create alias types for vertex array / buffer objects
27 | VAO :: u32
28 | VBO :: u32
29 | ShaderProgram :: u32
30 | Texture :: u32
31 |
32 | // Global variables.
33 | global_vao : VAO
34 | global_shader : ShaderProgram
35 | watch : time.Stopwatch
36 |
37 | // Constants
38 | WINDOW_H :: 800
39 | WINDOW_W :: 800
40 | FONT :: `C:\Windows\Fonts\arialbd.ttf`
41 | CHARACTER:: '@'
42 | FONTSCALE:: 100
43 |
44 | // Functions to update constants in shader programs.
45 | update_uni_2fv :: proc( program : u32, var_name : cstring, new_value_ptr : [^] f32) {
46 | gl.UniformMatrix2fv(gl.GetUniformLocation(program, var_name), 1, gl.TRUE, new_value_ptr)
47 | }
48 |
49 | update_uni_4fv :: proc( program : u32, var_name : cstring, new_value_ptr : [^] f32) {
50 | gl.UniformMatrix4fv(gl.GetUniformLocation(program, var_name), 1, gl.TRUE, new_value_ptr)
51 | }
52 |
53 | main :: proc() {
54 | //-------------------------------------------------------------
55 | // Use glfw to setup a window to render with OpenGl.
56 | //-------------------------------------------------------------
57 | glfw.Init()
58 | defer glfw.Terminate()
59 |
60 | glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
61 | glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
62 | glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
63 |
64 | window := glfw.CreateWindow(WINDOW_W, WINDOW_H, "Moving Character", nil, nil)
65 | assert(window != nil)
66 | defer glfw.DestroyWindow(window)
67 |
68 | glfw.MakeContextCurrent(window)
69 | glfw.SwapInterval(1)
70 |
71 | // Load OpenGL 3.3 function pointers.
72 | gl.load_up_to(3,3, glfw.gl_set_proc_address)
73 |
74 | ww, hh := glfw.GetFramebufferSize(window)
75 | gl.Viewport(0,0,ww,hh)
76 |
77 | // Key press / Window-resize behaviour
78 | glfw.SetKeyCallback(window, callback_key)
79 | glfw.SetWindowRefreshCallback(window, window_refresh)
80 |
81 | //-------------------------------------------------------------
82 | //Set up a rectangle to have the character texture drawn on it.
83 | //-------------------------------------------------------------
84 |
85 | h, w : f32
86 | h = 300
87 | w = 300
88 |
89 | rect_verts : [6 * 4] f32
90 | rect_verts = { // rect coords : vec2, texture coords : vec2
91 | 0, h, 0, 0,
92 | 0, 0, 0, 1,
93 | w, 0, 1, 1,
94 | 0, h, 0, 0,
95 | w, 0, 1, 1,
96 | w, h, 1, 0,
97 | }
98 |
99 | //-------------------------------------------------------------
100 | //Use stb to load a .ttf font and create a bitmap from it.
101 | //-------------------------------------------------------------
102 |
103 | // Load .ttf file into buffer.
104 | ttf_buffer :: [1<<23] u8 // Assumes a .ttf file of under 8MB.
105 | fontdata, succ := os.read_entire_file(FONT)
106 | if !succ {
107 | f.println("ERROR: Couldn't load font at: ", FONT)
108 | os.exit(1)
109 | }
110 | font_ptr : [^] u8 = &fontdata[0]
111 |
112 | // Initialize font.
113 | font : tt.fontinfo
114 | tt.InitFont(info = &font, data = font_ptr, offset = 0)
115 |
116 | // Find glyph of character to render.
117 | char_index := tt.FindGlyphIndex(&font, CHARACTER)
118 |
119 | // Create Bitmap of glyph, and loading width and height.
120 | bitmap_w, bitmap_h, xo, yo : i32
121 | glyph_bitmap := tt.GetGlyphBitmap(
122 | info = &font,
123 | scale_x = 0,
124 | scale_y = tt.ScaleForPixelHeight(&font, FONTSCALE),
125 | glyph = char_index,
126 | width = &bitmap_w,
127 | height = &bitmap_h,
128 | xoff = &xo,
129 | yoff = &yo,
130 | )
131 | // Memory Leak: the above should be freed with tt.FreeBitmap
132 |
133 | //f.println("bitmap width, height of", CHARACTER, ":", bitmap_w, bitmap_h) // Debug
134 |
135 | //-------------------------------------------------------------
136 | //Tell the GPU about the data, especially the font texture.
137 | //-------------------------------------------------------------
138 |
139 | gl.GenVertexArrays(1, &global_vao)
140 | gl.BindVertexArray(global_vao)
141 |
142 | vbo : VBO
143 | gl.GenBuffers(1, &vbo)
144 | gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
145 |
146 | Glyph_texture : Texture
147 | gl.GenTextures(1, &Glyph_texture)
148 | gl.BindTexture(gl.TEXTURE_2D, Glyph_texture)
149 |
150 | // Describe GPU buffer.
151 | gl.BufferData(gl.ARRAY_BUFFER, size_of(rect_verts), &rect_verts, gl.STATIC_DRAW)
152 |
153 | // Position and color attributes. Don't forget to enable!
154 | gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 4 * size_of(f32), 0 * size_of(f32))
155 | gl.VertexAttribPointer(1, 2, gl.FLOAT, gl.FALSE, 4 * size_of(f32), 2 * size_of(f32))
156 |
157 | gl.EnableVertexAttribArray(0)
158 | gl.EnableVertexAttribArray(1)
159 |
160 | gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1)
161 |
162 | gl.TexImage2D(
163 | gl.TEXTURE_2D, // texture type
164 | 0, // level of detail number (default = 0)
165 | gl.RED, // texture format
166 | bitmap_w, // width
167 | bitmap_h, // height
168 | 0, // border, must be 0
169 | gl.RED, // pixel data format
170 | gl.UNSIGNED_BYTE, // data type of pixel data
171 | glyph_bitmap, // image data
172 | )
173 |
174 | // Texture wrapping options.
175 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
176 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
177 |
178 | // Texture filtering options.
179 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
180 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
181 |
182 | //-------------------------------------------------------------
183 | //Compile the vertex and fragment shader.
184 | //-------------------------------------------------------------
185 |
186 | program_ok : bool
187 | vertex_shader := string(#load("vertex.glsl" ))
188 | fragment_shader := string(#load("fragment.glsl"))
189 |
190 | global_shader, program_ok = gl.load_shaders_source(vertex_shader, fragment_shader);
191 |
192 | if !program_ok {
193 | f.println("ERROR: Failed to load and compile shaders."); os.exit(1)
194 | }
195 |
196 | gl.UseProgram(global_shader)
197 |
198 | //-------------------------------------------------------------
199 | //Render the character!
200 | //-------------------------------------------------------------
201 |
202 | // Start rotation timer.
203 | time.stopwatch_start(&watch)
204 |
205 | // Texture blending options.
206 | gl.Enable(gl.BLEND)
207 | gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
208 |
209 | // Main loop.
210 | for !glfw.WindowShouldClose(window) {
211 | glfw.PollEvents()
212 | // If a key press happens, .PollEvents calls callback_key, defined below.
213 | // Note: glfw.PollEvents blocks on window menu interaction selection or
214 | // window resize. During window_resize, glfw.SetWindowRefreshCallback
215 | // calls window_refresh to redraw the window.
216 |
217 | render_screen(window, global_vao)
218 | }
219 | }
220 |
221 | render_screen :: proc( window : glfw.WindowHandle, vao : VAO) {
222 | // Calculate projection matrix.
223 | render_rect_w , render_rect_h : f32
224 | render_rect_w = WINDOW_W
225 | render_rect_h = WINDOW_H
226 |
227 | proj_mat := [4] f32 {
228 | 1/render_rect_w, 0,
229 | 0, 1/render_rect_h,
230 | }
231 |
232 | proj_mat_ptr : [^] f32 = &proj_mat[0]
233 |
234 | // Calculate translation matrix.
235 | raw_duration := time.stopwatch_duration(watch)
236 | secs := f32(time.duration_seconds(raw_duration))
237 |
238 | theta := f32(m.PI * secs )
239 |
240 | radius := f32(0.5)
241 | translation_mat := [16] f32 {
242 | 1, 0, 0, radius * m.cos(theta),
243 | 0, 1, 0, radius * m.sin(theta),
244 | 0, 0, 1, 0,
245 | 0, 0, 0, 1,
246 | }
247 |
248 | trans_mat_ptr : [^] f32 = &translation_mat[0]
249 |
250 | gl.BindVertexArray(vao)
251 | defer gl.BindVertexArray(0)
252 |
253 | // Send matrices to the shader.
254 | update_uni_2fv(global_shader, "projection", proj_mat_ptr)
255 | update_uni_4fv(global_shader, "translation", trans_mat_ptr)
256 |
257 | // Draw commands.
258 | gl.ClearColor(0.1, 0.1, 0.1, 1)
259 | gl.Clear(gl.COLOR_BUFFER_BIT)
260 |
261 | gl.DrawArrays(gl.TRIANGLES, 0, 6)
262 | glfw.SwapBuffers(window)
263 | }
264 |
265 | // Quit the window if the ESC key is pressed. This procedure is called by
266 | // glfw.SetKeyCallback.
267 | callback_key :: proc "c" ( window : glfw.WindowHandle, key, scancode, action, mods : i32 ) {
268 | if action == glfw.PRESS && key == glfw.KEY_ESCAPE {
269 | glfw.SetWindowShouldClose(window, true)
270 | }
271 | }
272 |
273 | // If the window needs to be redrawn (e.g. the user resizes the window), redraw the window.
274 | // This procedure is called by glfw.SetWindowRefreshCallback.
275 | window_refresh :: proc "c" ( window : glfw.WindowHandle ) {
276 | context = runtime.default_context()
277 | w, h : i32
278 | w, h = glfw.GetWindowSize(window)
279 | gl.Viewport(0,0,w,h)
280 | render_screen(window, global_vao)
281 | }
282 |
--------------------------------------------------------------------------------
/Moving-Character/vertex.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | layout (location = 0) in vec2 xyPos;
4 | layout (location = 1) in vec2 aTexCoords;
5 | out vec2 TexCoords;
6 |
7 | uniform mat2 projection;
8 | uniform mat4 translation;
9 |
10 | void main() {
11 | gl_Position = translation * vec4(projection * xyPos, 0, 1);
12 | TexCoords = aTexCoords;
13 | }
14 |
--------------------------------------------------------------------------------
/Psychedelic-Earth/blue-marble.qoi:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Psychedelic-Earth/blue-marble.qoi
--------------------------------------------------------------------------------
/Psychedelic-Earth/fragment.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | in vec2 TexCoords;
4 | out vec4 FragColor;
5 |
6 | uniform sampler2D earth_texture;
7 |
8 | uniform mat3 psych_mat;
9 |
10 | vec4 NormalColor;
11 |
12 | void main() {
13 | NormalColor = texture(earth_texture, TexCoords);
14 | FragColor = vec4( psych_mat * NormalColor.rgb, NormalColor.a);
15 | }
16 |
--------------------------------------------------------------------------------
/Psychedelic-Earth/psychedelic-earth.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Psychedelic-Earth/psychedelic-earth.exe
--------------------------------------------------------------------------------
/Psychedelic-Earth/psychedelic-earth.odin:
--------------------------------------------------------------------------------
1 | // An elementary program in Odin which renders "The Blue Marble" taken
2 | // by the Apollo 17 Crew, 1972, animated with mild psychedelic colors.
3 | //
4 | // The photo is in the public domain, and available at:
5 | // https://commons.wikimedia.org/wiki/File:The_Blue_Marble.jpg
6 | //
7 | // The .qoi (Quite OK Image Format) version of the image in this directory
8 | // was converted from the .jpg version above with IrfanView.
9 | //
10 | // Created by Benjamin Thompson. Available at:
11 | // https://github.com/bg-thompson/OpenGL-Tutorials-In-Odin
12 | // Last updated: 2024.09.15
13 | //
14 | // To compile and run the program with optimizations, use the command
15 | //
16 | // odin run Psychedelic-Earth -o:speed
17 | //
18 | // Created for educational purposes. Used verbatim, it is probably
19 | // unsuitable for production code.
20 |
21 | package main
22 |
23 | import "vendor:glfw"
24 | import gl "vendor:OpenGL"
25 | import "core:image"
26 | import q "core:image/qoi"
27 | import "core:time"
28 | import m "core:math"
29 | import f "core:fmt"
30 | import "core:os"
31 | import "base:runtime"
32 |
33 | // Create alias types for vertex array / buffer objects
34 | VAO :: u32
35 | VBO :: u32
36 | ShaderProgram :: u32
37 | Texture :: u32
38 |
39 | // Global variables.
40 | global_vao : VAO
41 | global_shader : ShaderProgram
42 | watch : time.Stopwatch
43 |
44 | // Constants
45 | WINDOW_H :: 800
46 | WINDOW_W :: 800
47 | IMAGELOC :: `blue-marble.qoi`
48 |
49 | // Functions to update constants in shader programs.
50 | update_uni_2fv :: proc( program : u32, var_name : cstring, new_value_ptr : [^] f32) {
51 | gl.UniformMatrix2fv(gl.GetUniformLocation(program, var_name), 1, gl.TRUE, new_value_ptr)
52 | }
53 |
54 | update_uni_3fv :: proc( program : u32, var_name : cstring, new_value_ptr : [^] f32) {
55 | gl.UniformMatrix3fv(gl.GetUniformLocation(program, var_name), 1, gl.TRUE, new_value_ptr)
56 | }
57 |
58 | main :: proc() {
59 | //-------------------------------------------------------------
60 | // Use glfw to setup a window to render with OpenGl.
61 | //-------------------------------------------------------------
62 | glfw.Init()
63 | defer glfw.Terminate()
64 |
65 | glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
66 | glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
67 | glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
68 |
69 | window := glfw.CreateWindow(WINDOW_W, WINDOW_H, "Psychedelic Earth", nil, nil)
70 | assert(window != nil)
71 | defer glfw.DestroyWindow(window)
72 |
73 | glfw.MakeContextCurrent(window)
74 | glfw.SwapInterval(1)
75 |
76 | // Load OpenGL 3.3 function pointers.
77 | gl.load_up_to(3,3, glfw.gl_set_proc_address)
78 |
79 | ww, hh := glfw.GetFramebufferSize(window)
80 | gl.Viewport(0,0,ww,hh)
81 |
82 | // Key press / Window-resize behaviour
83 | glfw.SetKeyCallback(window, callback_key)
84 | glfw.SetWindowRefreshCallback(window, window_refresh)
85 |
86 | //-------------------------------------------------------------
87 | //Set up a rectangle to have the image texture drawn on it.
88 | //-------------------------------------------------------------
89 |
90 | w, h : f32
91 | w = WINDOW_W
92 | h = WINDOW_H
93 |
94 | rect_verts : [6 * 4] f32
95 | rect_verts = { // rect coords : vec2, texture coords : vec2
96 | -w, h, 0, 0,
97 | -w, -h, 0, 1,
98 | w, -h, 1, 1,
99 | -w, h, 0, 0,
100 | w, h, 1, 0,
101 | w, -h, 1, 1,
102 | }
103 |
104 | // Load image at compile time
105 | image_file_bytes := #load(IMAGELOC)
106 |
107 | // Load image Odin's core:image library.
108 | image_ptr : ^image.Image
109 | err : image.Error
110 | options := image.Options{.alpha_add_if_missing}
111 |
112 | // image_ptr, err = q.load_from_file(IMAGELOC, options)
113 | image_ptr, err = q.load_from_bytes(image_file_bytes, options)
114 | defer q.destroy(image_ptr)
115 | image_w := i32(image_ptr.width)
116 | image_h := i32(image_ptr.height)
117 |
118 | if err != nil {
119 | f.println("ERROR: Image:", IMAGELOC, "failed to load.")
120 | }
121 |
122 | // Copy bytes from icon buffer into slice.
123 | earth_pixels_u8 := make([]u8, len(image_ptr.pixels.buf))
124 | for b, i in image_ptr.pixels.buf {
125 | earth_pixels_u8[i] = b
126 | }
127 |
128 | //-------------------------------------------------------------
129 | //Tell the GPU about the image (texture).
130 | //-------------------------------------------------------------
131 |
132 | gl.GenVertexArrays(1, &global_vao)
133 | gl.BindVertexArray(global_vao)
134 |
135 | vbo : VBO
136 | gl.GenBuffers(1, &vbo)
137 | gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
138 |
139 | Earth_texture : Texture
140 | gl.GenTextures(1, &Earth_texture)
141 | gl.BindTexture(gl.TEXTURE_2D, Earth_texture)
142 |
143 | // Describe GPU buffer.
144 | gl.BufferData(gl.ARRAY_BUFFER, size_of(rect_verts), &rect_verts, gl.STATIC_DRAW)
145 |
146 | // Position and color attributes. Don't forget to enable!
147 | gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 4 * size_of(f32), 0 * size_of(f32))
148 | gl.VertexAttribPointer(1, 2, gl.FLOAT, gl.FALSE, 4 * size_of(f32), 2 * size_of(f32))
149 |
150 | gl.EnableVertexAttribArray(0)
151 | gl.EnableVertexAttribArray(1)
152 |
153 | // Describe texture.
154 | gl.TexImage2D(
155 | gl.TEXTURE_2D, // texture type
156 | 0, // level of detail number (default = 0)
157 | gl.RGBA, // texture format
158 | image_w, // width
159 | image_h, // height
160 | 0, // border, must be 0
161 | gl.RGBA, // pixel data format
162 | gl.UNSIGNED_BYTE, // data type of pixel data
163 | &earth_pixels_u8[0], // image data
164 | )
165 |
166 | // Texture wrapping options.
167 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
168 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
169 |
170 | // Texture filtering options.
171 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
172 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
173 |
174 | //-------------------------------------------------------------
175 | //Compile the vertex and fragment shader.
176 | //-------------------------------------------------------------
177 |
178 | program_ok : bool
179 | vertex_shader := string(#load("vertex.glsl" ))
180 | fragment_shader := string(#load("fragment.glsl"))
181 |
182 | global_shader, program_ok = gl.load_shaders_source(vertex_shader, fragment_shader);
183 |
184 | if !program_ok {
185 | f.println("ERROR: Failed to load and compile shaders."); os.exit(1)
186 | }
187 |
188 | gl.UseProgram(global_shader)
189 |
190 | //-------------------------------------------------------------
191 | //Render the image!
192 | //-------------------------------------------------------------
193 |
194 | // Start rotation timer.
195 | time.stopwatch_start(&watch)
196 |
197 | // Texture blending options.
198 | gl.Enable(gl.BLEND)
199 | gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
200 |
201 | // Main loop.
202 | for !glfw.WindowShouldClose(window) {
203 | glfw.PollEvents()
204 | // If a key press happens, .PollEvents calls callback_key, defined below.
205 | // Note: glfw.PollEvents blocks on window menu interaction selection or
206 | // window resize. During window_resize, glfw.SetWindowRefreshCallback
207 | // calls window_refresh to redraw the window.
208 |
209 | render_screen(window, global_vao)
210 | }
211 | }
212 |
213 | render_screen :: proc( window : glfw.WindowHandle, vao : VAO) {
214 | // Calculate projection matrix.
215 | render_rect_w , render_rect_h : f32
216 | render_rect_w = WINDOW_W
217 | render_rect_h = WINDOW_H
218 |
219 | proj_mat := [4] f32 {
220 | 1/render_rect_w, 0,
221 | 0, 1/render_rect_h,
222 | }
223 |
224 | // Calculate translation matrix.
225 | raw_duration := time.stopwatch_duration(watch)
226 | secs := f32(time.duration_seconds(raw_duration))
227 |
228 | // Small function which oscillates between 0 and 1 with wavelength
229 | // 2*pi/n secs.
230 | osc :: proc( t : f32, n : f32) -> f32 {
231 | return (1 + m.sin(n * t)) / 2
232 | }
233 |
234 | // Calculate fragment shader values to get psychedelic effect.
235 | t1 := osc(secs, 2)
236 | t2 := osc(secs, 2.5)
237 | t3 := osc(secs, 3)
238 |
239 | psych_mat := [9] f32 {
240 | 1 - t1, 0, t1,
241 | 0, 1 - t2, t2,
242 | 0, t3, 1 - t3,
243 | }
244 |
245 | proj_mat_ptr : [^] f32 = &proj_mat[0]
246 | psych_mat_ptr : [^] f32 = &psych_mat[0]
247 |
248 | gl.BindVertexArray(vao)
249 | defer gl.BindVertexArray(0)
250 |
251 | // Send matrices to the shader.
252 | update_uni_2fv(global_shader, "projection", proj_mat_ptr)
253 | update_uni_3fv(global_shader, "psych_mat", psych_mat_ptr)
254 |
255 | // Draw commands.
256 | gl.ClearColor(0.1, 0.1, 0.1, 1)
257 | gl.Clear(gl.COLOR_BUFFER_BIT)
258 |
259 | gl.DrawArrays(gl.TRIANGLES, 0, 6)
260 | glfw.SwapBuffers(window)
261 | }
262 |
263 | // Quit the window if the ESC key is pressed. This procedure is called by
264 | // glfw.SetKeyCallback.
265 | callback_key :: proc "c" ( window : glfw.WindowHandle, key, scancode, action, mods : i32 ) {
266 | if action == glfw.PRESS && key == glfw.KEY_ESCAPE {
267 | glfw.SetWindowShouldClose(window, true)
268 | }
269 | }
270 |
271 | // If the window needs to be redrawn (e.g. the user resizes the window), redraw the window.
272 | // This procedure is called by glfw.SetWindowRefreshCallback.
273 | window_refresh :: proc "c" ( window : glfw.WindowHandle ) {
274 | context = runtime.default_context()
275 | w, h : i32
276 | w, h = glfw.GetWindowSize(window)
277 | gl.Viewport(0,0,w,h)
278 | render_screen(window, global_vao)
279 | }
280 |
--------------------------------------------------------------------------------
/Psychedelic-Earth/vertex.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | layout (location = 0) in vec2 xyPos;
4 | layout (location = 1) in vec2 aTexCoords;
5 | out vec2 TexCoords;
6 |
7 | uniform mat2 projection;
8 |
9 | void main() {
10 | gl_Position = vec4(projection * xyPos, 0, 1);
11 | TexCoords = aTexCoords;
12 | }
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A collection of introductory OpenGL programs in Odin.
2 |
3 | The programs below illustrate OpenGL functions in Odin using the wrappers in `"vendor:OpenGL"`. The programs start very basic and get progressively more complicated.
4 |
5 | The glfw library wrappers for Odin in `"vendor:glfw"` are also illustrated.
6 |
7 | ## Building
8 |
9 | Building the examples is trivial; the standard Odin distribution is the ONLY dependency.
10 |
11 | Simply call `odin run DIRECTORY -o:speed` where DIRECTORY is the name of one of the project's directories to build and run an example. (E.g. `odin run Rainbow-Triangle -o:speed`.)
12 |
13 | ## [Blinking Pink](./Blinking-Pink)
14 |
15 | A window which oscillates between pink and blue.
16 |
17 |
18 |
19 | ## [Rainbow Triangle](./Rainbow-Triangle)
20 |
21 | An RGB triangle which rotates over time.
22 |
23 |
24 |
25 | ## [Rotating Cube](./Rotating-Cube)
26 |
27 | A cube with different faces which rotates over time.
28 |
29 |
30 |
31 | ## [Psychedelic Earth](./Psychedelic-Earth)
32 |
33 | An image of the Earth changes color over time.
34 |
35 |
36 |
37 | ## [Moving Character](./Moving-Character)
38 |
39 | A character moves in a counter-clockwise circle over time.
40 |
41 |
42 |
43 | ## [Wavy Words](./Wavy-Words)
44 |
45 | The characters of a sentence move in the form of a sine wave over time.
46 |
47 |
48 |
49 | ## [Hidden Animal](./Hidden-Animal)
50 |
51 | Triangles which together form an animal are revealed when the mouse cursor passes over them.
52 |
53 |
--------------------------------------------------------------------------------
/Rainbow-Triangle/fragment.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | in vec3 ourColor;
4 | out vec4 FragColor;
5 |
6 | void main() {
7 | FragColor = vec4(ourColor, 1);
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/Rainbow-Triangle/rainbow-triangle.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Rainbow-Triangle/rainbow-triangle.exe
--------------------------------------------------------------------------------
/Rainbow-Triangle/rainbow-triangle.odin:
--------------------------------------------------------------------------------
1 | // An elementary program in Odin which creates a rainbow RGB triangle
2 | // which rotates over time.
3 | //
4 | // Created by Benjamin Thompson. Available at:
5 | // https://github.com/bg-thompson/OpenGL-Tutorials-In-Odin
6 | // Last updated: 2024.09.15
7 | //
8 | // To compile and run the program with optimizations, use the command
9 | //
10 | // odin run Rainbow-Triangle -o:speed
11 | //
12 | // Created for educational purposes. Used verbatim, it is probably
13 | // unsuitable for production code.
14 |
15 | package main
16 |
17 | import "vendor:glfw"
18 | import gl "vendor:OpenGL"
19 | import "core:time"
20 | import "core:math"
21 | import "core:fmt"
22 | import "core:os"
23 | import "base:runtime"
24 |
25 | // Create alias types for vertex array / buffer objects
26 | VAO :: u32
27 | VBO :: u32
28 | ShaderProgram :: u32
29 |
30 | // Global variables.
31 | global_vao : VAO
32 | global_shader : ShaderProgram
33 | watch : time.Stopwatch
34 |
35 | main :: proc() {
36 | // Setup window, including priming for OpenGL 3.3.
37 | glfw.Init()
38 | defer glfw.Terminate()
39 |
40 | glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
41 | glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
42 | glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
43 |
44 | window := glfw.CreateWindow(800, 800, "Rainbow Triangle", nil, nil)
45 | assert(window != nil)
46 | defer glfw.DestroyWindow(window)
47 |
48 | glfw.MakeContextCurrent(window)
49 | glfw.SwapInterval(1)
50 |
51 | // Load OpenGL 3.3 function pointers.
52 | gl.load_up_to(3,3, glfw.gl_set_proc_address)
53 |
54 | w, h := glfw.GetFramebufferSize(window)
55 | gl.Viewport(0,0,w,h)
56 |
57 | // Key press / Window-resize behaviour
58 | glfw.SetKeyCallback(window, callback_key)
59 | glfw.SetWindowRefreshCallback(window, window_refresh)
60 |
61 | // Create equilateral triangle on unit circle.
62 |
63 | sq := math.sqrt(f32(3)) * 0.5
64 |
65 | vertices : [15] f32 = {
66 | // Coordinates ; Colors
67 | 1.0, 0, 1, 0, 0,
68 | -0.5, sq, 0, 1, 0,
69 | -0.5, -sq, 0, 0, 1,
70 | }
71 |
72 | // Set up vertex array / buffer objects.
73 | gl.GenVertexArrays(1, &global_vao)
74 | gl.BindVertexArray(global_vao)
75 |
76 | vbo : VBO
77 | gl.GenBuffers(1, &vbo)
78 | gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
79 |
80 | // Describe GPU buffer.
81 | gl.BufferData(gl.ARRAY_BUFFER, // target
82 | size_of(vertices), // size of the buffer object's data store
83 | &vertices, // data used for initialization
84 | gl.STATIC_DRAW) // usage
85 |
86 | // Position and color attributes. Don't forget to enable!
87 | gl.VertexAttribPointer(0, // index
88 | 2, // size
89 | gl.FLOAT, // type
90 | gl.FALSE, // normalized
91 | 5 * size_of(f32), // stride
92 | 0) // offset
93 |
94 | gl.VertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, 5 * size_of(f32), 2 * size_of(f32))
95 |
96 | // Enable the vertex position and color attributes defined above.
97 | gl.EnableVertexAttribArray(0)
98 | gl.EnableVertexAttribArray(1)
99 |
100 | // Compile vertex shader and fragment shader.
101 | // Note how much easier this is in Odin than in C++!
102 |
103 | program_ok : bool
104 | vertex_shader := string(#load("vertex.glsl" ))
105 | fragment_shader := string(#load("fragment.glsl"))
106 |
107 | global_shader, program_ok = gl.load_shaders_source(vertex_shader, fragment_shader);
108 |
109 | if !program_ok {
110 | fmt.println("ERROR: Failed to load and compile shaders."); os.exit(1)
111 | }
112 |
113 | gl.UseProgram(global_shader)
114 |
115 | // Start rotation timer.
116 | time.stopwatch_start(&watch)
117 |
118 | for !glfw.WindowShouldClose(window) {
119 | glfw.PollEvents()
120 | // If a key press happens, .PollEvents calls callback_key, defined below.
121 | // Note: glfw.PollEvents blocks on window menu interaction selection or
122 | // window resize. During window_resize, glfw.SetWindowRefreshCallback
123 | // calls window_refresh to redraw the window.
124 |
125 | render_screen(window, global_vao)
126 | }
127 | }
128 |
129 | render_screen :: proc( window : glfw.WindowHandle, vao : VAO) {
130 | // Send theta rotation value to GPU.
131 | raw_duration := time.stopwatch_duration(watch)
132 | secs := f32(time.duration_seconds(raw_duration))
133 | theta := -secs
134 | gl.Uniform1f(gl.GetUniformLocation(global_shader, "theta"), theta)
135 |
136 | gl.BindVertexArray(vao)
137 | defer gl.BindVertexArray(0)
138 |
139 | // Draw commands.
140 | gl.ClearColor(0.1, 0.1, 0.1, 1)
141 | gl.Clear(gl.COLOR_BUFFER_BIT)
142 |
143 | gl.DrawArrays(gl.TRIANGLES, // Draw triangles.
144 | 0, // Begin drawing at index 0.
145 | 3) // Use 3 indices.
146 | glfw.SwapBuffers(window)
147 | }
148 |
149 | // Quit the window if the ESC key is pressed. This procedure is called by
150 | // glfw.SetKeyCallback.
151 | callback_key :: proc "c" ( window : glfw.WindowHandle, key, scancode, action, mods : i32 ) {
152 | if action == glfw.PRESS && key == glfw.KEY_ESCAPE {
153 | glfw.SetWindowShouldClose(window, true)
154 | }
155 | }
156 |
157 | // If the window needs to be redrawn (e.g. the user resizes the window), redraw the window.
158 | // This procedure is called by glfw.SetWindowRefreshCallback.
159 | window_refresh :: proc "c" ( window : glfw.WindowHandle ) {
160 | context = runtime.default_context()
161 | w, h : i32
162 | w, h = glfw.GetWindowSize(window)
163 | gl.Viewport(0,0,w,h)
164 | render_screen(window, global_vao)
165 | }
166 |
--------------------------------------------------------------------------------
/Rainbow-Triangle/vertex.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | layout (location = 0) in vec2 aPos;
3 | layout (location = 1) in vec3 aColor;
4 |
5 | out vec3 ourColor;
6 |
7 | uniform float theta;
8 |
9 | void main() {
10 |
11 | mat2 rotation_mat = mat2(
12 | cos(theta), sin(-theta),
13 | sin(theta), cos( theta)
14 | );
15 |
16 | gl_Position = vec4(rotation_mat * aPos, 0, 1);
17 | ourColor = aColor;
18 | }
19 |
--------------------------------------------------------------------------------
/Readme-Imgs/blinking-pink.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Readme-Imgs/blinking-pink.png
--------------------------------------------------------------------------------
/Readme-Imgs/hidden-animal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Readme-Imgs/hidden-animal.png
--------------------------------------------------------------------------------
/Readme-Imgs/moving-character.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Readme-Imgs/moving-character.png
--------------------------------------------------------------------------------
/Readme-Imgs/psychedelic-earth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Readme-Imgs/psychedelic-earth.png
--------------------------------------------------------------------------------
/Readme-Imgs/rainbow-triangle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Readme-Imgs/rainbow-triangle.jpg
--------------------------------------------------------------------------------
/Readme-Imgs/rotating-cube.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Readme-Imgs/rotating-cube.png
--------------------------------------------------------------------------------
/Readme-Imgs/wavy-words.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Readme-Imgs/wavy-words.png
--------------------------------------------------------------------------------
/Rotating-Cube/fragment.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | in vec3 ourColor;
4 | out vec4 FragColor;
5 |
6 | void main() {
7 | FragColor = vec4(ourColor, 1);
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/Rotating-Cube/rotating-cube.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Rotating-Cube/rotating-cube.exe
--------------------------------------------------------------------------------
/Rotating-Cube/rotating-cube.odin:
--------------------------------------------------------------------------------
1 | // An elementary program in Odin which creates a colored cube
2 | // which rotates over time.
3 | //
4 | // Created by Benjamin Thompson. Available at:
5 | // https://github.com/bg-thompson/OpenGL-Tutorials-In-Odin
6 | // Last updated: 2024.09.15
7 | //
8 | // To compile and run the program with optimizations, use the command
9 | //
10 | // odin run Rotating-Cube -o:speed
11 | //
12 | // Created for educational purposes. Used verbatim, it is probably
13 | // unsuitable for production code.
14 |
15 | package main
16 |
17 | import "vendor:glfw"
18 | import gl "vendor:OpenGL"
19 | import "core:time"
20 | import m "core:math"
21 | import "core:fmt"
22 | import "core:os"
23 | import "base:runtime"
24 |
25 | // Create alias types for vertex array / buffer objects
26 | VAO :: u32
27 | VBO :: u32
28 | EBO :: u32
29 | ShaderProgram :: u32
30 |
31 | // Global variables.
32 | global_vao : VAO
33 | global_shader : ShaderProgram
34 | watch : time.Stopwatch
35 |
36 | main :: proc() {
37 | // Setup window, including priming for OpenGL 3.3.
38 | glfw.Init()
39 | defer glfw.Terminate()
40 |
41 | glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
42 | glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
43 | glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
44 |
45 | window := glfw.CreateWindow(800, 800, "Rotating Cube", nil, nil)
46 | assert(window != nil)
47 | defer glfw.DestroyWindow(window)
48 |
49 | glfw.MakeContextCurrent(window)
50 | glfw.SwapInterval(1)
51 |
52 | // Load OpenGL 3.3 function pointers.
53 | gl.load_up_to(3,3, glfw.gl_set_proc_address)
54 |
55 | w, h := glfw.GetFramebufferSize(window)
56 | gl.Viewport(0,0,w,h)
57 |
58 | // Key press / Window-resize behaviour
59 | glfw.SetKeyCallback(window, callback_key)
60 | glfw.SetWindowRefreshCallback(window, window_refresh)
61 |
62 | // Cube face colors.
63 | c1 := rgbHexToFractions(0xd3_47_3d) // red
64 | c2 := rgbHexToFractions(0xf5_ef_eb) // white
65 | c3 := rgbHexToFractions(0xf6_ad_0f) // orange
66 | c4 := rgbHexToFractions(0x31_6a_96) // blue
67 | c5 := rgbHexToFractions(0x2e_24_3f) // purple
68 | c6 := rgbHexToFractions(0x86_bc_d1) // light blue
69 |
70 |
71 | // Cube vertices and vertex colors.
72 | vertices : [6*4*6] f32
73 | vertices = {
74 | // coords ; colors
75 | -1, -1, -1, c1.r, c1.g, c1.b,
76 | +1, -1, -1, c1.r, c1.g, c1.b,
77 | -1, +1, -1, c1.r, c1.g, c1.b,
78 | +1, +1, -1, c1.r, c1.g, c1.b,
79 |
80 | -1, -1, +1, c2.r, c2.g, c2.b,
81 | +1, -1, +1, c2.r, c2.g, c2.b,
82 | -1, +1, +1, c2.r, c2.g, c2.b,
83 | +1, +1, +1, c2.r, c2.g, c2.b,
84 |
85 | -1, -1, -1, c3.r, c3.g, c3.b,
86 | -1, +1, -1, c3.r, c3.g, c3.b,
87 | -1, -1, +1, c3.r, c3.g, c3.b,
88 | -1, +1, +1, c3.r, c3.g, c3.b,
89 |
90 | +1, -1, -1, c4.r, c4.g, c4.b,
91 | +1, +1, -1, c4.r, c4.g, c4.b,
92 | +1, -1, +1, c4.r, c4.g, c4.b,
93 | +1, +1, +1, c4.r, c4.g, c4.b,
94 |
95 | -1, -1, -1, c5.r, c5.g, c5.b,
96 | +1, -1, -1, c5.r, c5.g, c5.b,
97 | -1, -1, +1, c5.r, c5.g, c5.b,
98 | +1, -1, +1, c5.r, c5.g, c5.b,
99 |
100 | -1, +1, -1, c6.r, c6.g, c6.b,
101 | +1, +1, -1, c6.r, c6.g, c6.b,
102 | -1, +1, +1, c6.r, c6.g, c6.b,
103 | +1, +1, +1, c6.r, c6.g, c6.b,
104 | }
105 |
106 | index_array : [3 * 2 * 6] u32
107 | index_array = {
108 | 0, 1, 2, 1, 2, 3, // Face 1
109 | 4, 5, 6, 5, 6, 7, // Face 2
110 | 8, 9, 10, 9, 10, 11, // Face 3
111 | 12, 13, 14, 13, 14, 15, // Face 4
112 | 16, 17, 18, 17, 18, 19, // Face 5
113 | 20, 21, 22, 21, 22, 23, // Face 6
114 | }
115 |
116 | // Set up vertex array / element array / buffer objects
117 | gl.GenVertexArrays(1, &global_vao)
118 | gl.BindVertexArray(global_vao)
119 |
120 | vbo : VBO
121 | gl.GenBuffers(1, &vbo)
122 | gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
123 |
124 | ebo : EBO
125 | gl.GenBuffers(1, &ebo)
126 | gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo)
127 |
128 | // Describe GPU buffer.
129 | gl.BufferData(gl.ARRAY_BUFFER, // target
130 | size_of(vertices), // size of the buffer object's data store
131 | &vertices, // data used for initialization
132 | gl.STATIC_DRAW) // usage
133 |
134 | gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size_of(index_array), &index_array, gl.STATIC_DRAW)
135 |
136 | // Position and color attributes. Don't forget to enable!
137 | gl.VertexAttribPointer(0, // index
138 | 3, // size
139 | gl.FLOAT, // type
140 | gl.FALSE, // normalized
141 | 6 * size_of(f32), // stride
142 | 0) // offset
143 |
144 | gl.VertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, 6 * size_of(f32), 3 * size_of(f32))
145 |
146 | // Enable the vertex position and color attributes defined above.
147 | gl.EnableVertexAttribArray(0)
148 | gl.EnableVertexAttribArray(1)
149 |
150 | // Compile vertex shader and fragment shader.
151 | // Note how much easier this is in Odin than in C++!
152 |
153 | program_ok : bool
154 | vertex_shader := string(#load("vertex.glsl" ))
155 | fragment_shader := string(#load("fragment.glsl"))
156 |
157 | global_shader, program_ok = gl.load_shaders_source(vertex_shader, fragment_shader);
158 |
159 | if !program_ok {
160 | fmt.println("ERROR: Failed to load and compile shaders."); os.exit(1)
161 | }
162 |
163 | // Enable shader, and depth-testing during rendering.
164 | gl.UseProgram(global_shader)
165 | gl.Enable(gl.DEPTH_TEST)
166 |
167 |
168 | // Start rotation timer.
169 | time.stopwatch_start(&watch)
170 |
171 | for !glfw.WindowShouldClose(window) {
172 | glfw.PollEvents()
173 | // If a key press happens, .PollEvents calls callback_key, defined below.
174 | // Note: glfw.PollEvents blocks on window menu interaction selection or
175 | // window resize. During window_resize, glfw.SetWindowRefreshCallback
176 | // calls window_refresh to redraw the window.
177 |
178 | render_screen(window, global_vao)
179 | }
180 | }
181 |
182 | render_screen :: proc( window : glfw.WindowHandle, vao : VAO) {
183 | // Send theta rotation value to GPU.
184 | raw_duration := time.stopwatch_duration(watch)
185 | secs := f32(time.duration_seconds(raw_duration))
186 | theta := f32(-secs)
187 |
188 | // Define matrices for model rotation and camera.
189 | rotation_mat := [9] f32 {
190 | m.cos(theta), m.sin(-theta), 0,
191 | m.sin(theta), m.cos( theta), 0,
192 | 0, 0, 1,
193 | }
194 |
195 | // Note: while the following matrices are constant, Odin
196 | // cannot take pointer address of constants, so put them
197 | // in the stack. It is clearer to have them here
198 | // than as a constant in the vertex shader.
199 | camera_to_x_mat := [9] f32 {
200 | 1, 0, 0,
201 | 0, 0, -1,
202 | 0, 1, 0,
203 | }
204 |
205 | ch :: f32(3) // Cube offset
206 | k :: f32(0.5) // Cube scale
207 |
208 | translation_mat := [16] f32 {
209 | k, 0, 0, 0,
210 | 0, k, 0, 0,
211 | 0, 0, k, ch,
212 | 0, 0, 0, 1,
213 | }
214 | // The math behind calculating a perspective matrix like that
215 | // below can be found in any decent graphics programming textbook.
216 | perspective_mat := [16] f32 {
217 | 2, 0, 0, 0,
218 | 0, 2, 0, 0,
219 | 0, 0, 3, -8,
220 | 0, 0, 1, 0,
221 | };
222 |
223 | rotation_ptr : [^] f32 = &rotation_mat[0]
224 | camera_to_x_ptr : [^] f32 = &camera_to_x_mat[0]
225 | translation_ptr : [^] f32 = &translation_mat[0]
226 | perspective_ptr : [^] f32 = &perspective_mat[0]
227 |
228 | // Send the matrices above to the vertex shader program.
229 | gl.UniformMatrix3fv(gl.GetUniformLocation(global_shader, "rotation_mat" ), 1, gl.TRUE, rotation_ptr)
230 | gl.UniformMatrix3fv(gl.GetUniformLocation(global_shader, "camera_to_x_mat"), 1, gl.TRUE, camera_to_x_ptr)
231 | gl.UniformMatrix4fv(gl.GetUniformLocation(global_shader, "translation_mat"), 1, gl.TRUE, translation_ptr)
232 | gl.UniformMatrix4fv(gl.GetUniformLocation(global_shader, "perspective_mat"), 1, gl.TRUE, perspective_ptr)
233 |
234 | gl.BindVertexArray(vao)
235 | defer gl.BindVertexArray(0)
236 |
237 | // Draw commands.
238 | gl.ClearColor(0.0, 0.0, 0.0, 1)
239 | gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
240 |
241 | gl.DrawElements(gl.TRIANGLES, // Draw triangles.
242 | 6 * 6, // Draw 36 vertices.
243 | gl.UNSIGNED_INT, // Data type of the indices.
244 | rawptr(uintptr(0))) // Pointer to indices. (Not needed.)
245 | glfw.SwapBuffers(window)
246 | }
247 |
248 | // A function which simply converts colors specified in hex
249 | // to a triple of floats ranging from 0 to 1.
250 | rgbHexToFractions :: proc( hex_color : int ) -> ( ret : [3] f32 ) {
251 | ret.r = f32( (hex_color & 0x00_FF_00_00) >> 16 )
252 | ret.g = f32( (hex_color & 0x00_00_FF_00) >> 8 )
253 | ret.b = f32( (hex_color & 0x00_00_00_FF) >> 0 )
254 | ret *= 1.0/255
255 | return
256 | }
257 |
258 | // Quit the window if the ESC key is pressed. This procedure is called by
259 | // glfw.SetKeyCallback.
260 | callback_key :: proc "c" ( window : glfw.WindowHandle, key, scancode, action, mods : i32 ) {
261 | if action == glfw.PRESS && key == glfw.KEY_ESCAPE {
262 | glfw.SetWindowShouldClose(window, true)
263 | }
264 | }
265 |
266 | // If the window needs to be redrawn (e.g. the user resizes the window), redraw the window.
267 | // This procedure is called by glfw.SetWindowRefreshCallback.
268 | window_refresh :: proc "c" ( window : glfw.WindowHandle ) {
269 | context = runtime.default_context()
270 | w, h : i32
271 | w, h = glfw.GetWindowSize(window)
272 | gl.Viewport(0,0,w,h)
273 | render_screen(window, global_vao)
274 | }
275 |
--------------------------------------------------------------------------------
/Rotating-Cube/vertex.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | layout (location = 0) in vec3 aPos;
3 | layout (location = 1) in vec3 aColor;
4 |
5 | out vec3 ourColor;
6 |
7 | uniform float k;
8 |
9 | float kk = 0.5;
10 |
11 | uniform mat3 rotation_mat;
12 | uniform mat3 camera_to_x_mat;
13 | uniform mat4 translation_mat;
14 | uniform mat4 perspective_mat;
15 |
16 | void main() {
17 |
18 | gl_Position = perspective_mat * translation_mat * vec4(camera_to_x_mat * rotation_mat * aPos, 1);
19 | ourColor = aColor;
20 | }
21 |
--------------------------------------------------------------------------------
/Wavy-Words/fragment.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | in vec2 TexCoords;
4 | out vec4 FragColor;
5 |
6 | uniform sampler2D glyph_texture;
7 |
8 | void main() {
9 | FragColor = vec4(0.5, 1, 1, texture(glyph_texture, TexCoords).r);
10 | //FragColor = vec4(TexCoords.x,TexCoords.y,1,1);
11 | }
12 |
--------------------------------------------------------------------------------
/Wavy-Words/vertex.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | layout (location = 0) in vec2 xyPos;
4 | layout (location = 1) in vec2 aTexCoords;
5 | out vec2 TexCoords;
6 |
7 | uniform mat2 projection;
8 |
9 | void main() {
10 | gl_Position = vec4(projection * xyPos, 0, 1);
11 | TexCoords = aTexCoords;
12 | }
13 |
--------------------------------------------------------------------------------
/Wavy-Words/wavy-words.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bg-thompson/OpenGL-Tutorials-In-Odin/c0f17d8a3ba82ce6e067506eff573f8454c6d3d4/Wavy-Words/wavy-words.exe
--------------------------------------------------------------------------------
/Wavy-Words/wavy-words.odin:
--------------------------------------------------------------------------------
1 | // A simple program in Odin which renders a sentence and animates
2 | // it like a wave.
3 | //
4 | // Created by Benjamin Thompson. Available at:
5 | // https://github.com/bg-thompson/OpenGL-Tutorials-In-Odin
6 | // Last updated: 2024.09.15
7 | //
8 | // To compile and run the program with optimizations, use the command
9 | //
10 | // odin run Wavy-Words -o:speed
11 | //
12 | // Created for educational purposes. Used verbatim, it is probably
13 | // unsuitable for production code.
14 |
15 | package main
16 |
17 | import "vendor:glfw"
18 | import gl "vendor:OpenGL"
19 | import tt "vendor:stb/truetype"
20 | import "core:time"
21 | import "core:math"
22 | import f "core:fmt"
23 | import "core:os"
24 | import "base:runtime"
25 |
26 | // Create alias types for vertex array / buffer objects
27 | VAO :: u32
28 | VBO :: u32
29 | ShaderProgram :: u32
30 | Texture :: u32
31 |
32 | // Global variables.
33 | global_vao : VAO
34 | global_shader : ShaderProgram
35 | watch : time.Stopwatch
36 |
37 | // Constants
38 | WINDOW_H :: 800
39 | WINDOW_W :: 800
40 | FONT :: `C:\Windows\Fonts\arialbd.ttf`
41 | FONTSCALE :: 125
42 | CHARACTER :: '#'
43 | SENTENCE :: "Duty calls, 3 o'clock tea!"
44 |
45 | // Information needed to render a single letter.
46 | CharacterTexture :: struct {
47 | texID : u32,
48 | width : i32,
49 | height : i32,
50 | bbox_x : f32,
51 | bbox_y : f32,
52 | advance : f32,
53 | bitmap : [^] byte,
54 | }
55 |
56 | rune_to_glyph_texture : map[rune]CharacterTexture
57 |
58 | update_uni_2fv :: proc( program : u32, var_name : cstring, new_value_ptr : [^] f32) {
59 | gl.UniformMatrix2fv(gl.GetUniformLocation(program, var_name), 1, gl.TRUE, new_value_ptr)
60 | }
61 |
62 | main :: proc() {
63 |
64 | //-------------------------------------------------------------
65 | // Use glfw to setup a window to render with OpenGl.
66 | //-------------------------------------------------------------
67 | glfw.Init()
68 | defer glfw.Terminate()
69 |
70 | glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
71 | glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
72 | glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
73 |
74 | window := glfw.CreateWindow(WINDOW_W, WINDOW_H, "Wavy Words", nil, nil)
75 | assert(window != nil)
76 | defer glfw.DestroyWindow(window)
77 |
78 | glfw.MakeContextCurrent(window)
79 | glfw.SwapInterval(1)
80 |
81 | // Load OpenGL 3.3 function pointers.
82 | gl.load_up_to(3,3, glfw.gl_set_proc_address)
83 |
84 | ww, hh := glfw.GetFramebufferSize(window)
85 | gl.Viewport(0,0,ww,hh)
86 |
87 | // Key press / Window-resize behaviour
88 | glfw.SetKeyCallback(window, callback_key)
89 | glfw.SetWindowRefreshCallback(window, window_refresh)
90 |
91 | //-------------------------------------------------------------
92 | // Create texture maps for printable ascii characters (32-126)
93 | //-------------------------------------------------------------
94 |
95 | // Load .ttf file
96 | ttf_buffer :: [1<<23] u8 // Assumes file < 8MB
97 | fontdata, succ := os.read_entire_file(FONT)
98 | if !succ {
99 | f.println("ERROR: Couldn't load font file: ", FONT)
100 | os.exit(1)
101 | }
102 | font_ptr : [^] u8 = &fontdata[0]
103 |
104 | // Initialize font for stb_truetype
105 | font : tt.fontinfo
106 | tt.InitFont(
107 | info = &font,
108 | data = font_ptr,
109 | offset = 0,
110 | )
111 |
112 | // Create rune to character_texture map for printable characters.
113 | rune_to_glyph_texture = make(map[rune] CharacterTexture)
114 | defer delete(rune_to_glyph_texture)
115 |
116 | character_scale : f32
117 | character_scale = tt.ScaleForPixelHeight(&font, FONTSCALE)
118 |
119 | gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1)
120 |
121 | for r in 32..=126 {// The printable ASCII range, per wikipedia.
122 | bm_w, bm_h, xo, yo : i32
123 | glyph_index := tt.FindGlyphIndex(&font, rune(r))
124 | glyph_bitmap := tt.GetGlyphBitmap(
125 | info = &font,
126 | scale_x = 0,
127 | scale_y = character_scale,
128 | glyph = glyph_index,
129 | width = &bm_w,
130 | height = &bm_h,
131 | xoff = &xo,
132 | yoff = &yo,
133 | )
134 | // Memory leak: the bitmaps should be freed with tt.FreeBitmap...
135 | // ...but it is unclear what the second arg of FreeBitmap does.
136 |
137 | // Get bbox values.
138 | box1, box2, box3, box4 : i32
139 | tt.GetGlyphBox(&font, glyph_index, &box1, &box2, &box3, &box4)
140 |
141 | // Get advance and l_bearing.
142 | raw_advance, raw_l_bearing : i32
143 | tt.GetGlyphHMetrics(&font, glyph_index, &raw_advance, &raw_l_bearing)
144 |
145 | // Scale to font size.
146 | bbox_x := character_scale * f32(box1)
147 | bbox_y := character_scale * f32(box2)
148 | advance := character_scale * f32(raw_advance)
149 | l_bearing := character_scale * f32(raw_l_bearing)
150 |
151 | // Register glyph texture with GPU.
152 | texture_id : Texture
153 | gl.GenTextures(1, &texture_id)
154 | // Note: textures are so small, we can load all of them instead
155 | // of the ones we need.
156 | gl.BindTexture(gl.TEXTURE_2D, texture_id)
157 |
158 | // Describe textures.
159 | gl.TexImage2D(
160 | gl.TEXTURE_2D,
161 | 0,
162 | gl.RED,
163 | bm_w,
164 | bm_h,
165 | 0,
166 | gl.RED,
167 | gl.UNSIGNED_BYTE,
168 | glyph_bitmap,
169 | )
170 |
171 | // Wrapping and filtering options.
172 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
173 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
174 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
175 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
176 |
177 | // Save vital texture information for use when rendering the sentence.
178 | ct : CharacterTexture
179 | ct.texID = texture_id
180 | ct.width = bm_w
181 | ct.height = bm_h
182 | ct.bbox_x = bbox_x
183 | ct.bbox_y = bbox_y
184 | ct.advance = advance
185 | ct.bitmap = glyph_bitmap
186 | rune_to_glyph_texture[rune(r)] = ct
187 | }
188 |
189 | //------------------------------------------------------------------
190 | //Tell the GPU about the data, and give it space to draw characters.
191 | //------------------------------------------------------------------
192 |
193 | gl.GenVertexArrays(1, &global_vao)
194 | gl.BindVertexArray(global_vao)
195 |
196 | vbo : VBO
197 | gl.GenBuffers(1, &vbo)
198 | gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
199 |
200 | // Allocate space to draw a rectangle with the character glyph on it.
201 |
202 | // The zero value of a multi pointer is nil (per odin-lang.org/docs/overview).
203 | gl.BufferData(gl.ARRAY_BUFFER,
204 | size_of(f32) * 4*6, // Two triangles per letter.
205 | nil, // No vertex data, yet.
206 | gl.DYNAMIC_DRAW) // Expect the data to be updated.
207 |
208 | // Position / texture position attributes. Don't forget to enable!
209 | gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 4 * size_of(f32), 0 * size_of(f32))
210 | gl.VertexAttribPointer(1, 2, gl.FLOAT, gl.FALSE, 4 * size_of(f32), 2 * size_of(f32))
211 | gl.EnableVertexAttribArray(0)
212 | gl.EnableVertexAttribArray(1)
213 |
214 | //-------------------------------------------------------------
215 | //Compile the vertex and fragment shader.
216 | //-------------------------------------------------------------
217 |
218 | program_ok : bool
219 | vertex_shader := string(#load("vertex.glsl" ))
220 | fragment_shader := string(#load("fragment.glsl"))
221 |
222 | global_shader, program_ok = gl.load_shaders_source(vertex_shader, fragment_shader);
223 |
224 | if !program_ok {
225 | f.println("ERROR: Failed to load and compile shaders."); os.exit(1)
226 | }
227 |
228 | gl.UseProgram(global_shader)
229 |
230 | //-------------------------------------------------------------
231 | //Render a character!
232 | //-------------------------------------------------------------
233 |
234 | // Start rotation timer.
235 | time.stopwatch_start(&watch)
236 |
237 | // Texture blending options.
238 | gl.Enable(gl.BLEND)
239 | gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
240 |
241 | // Main loop.
242 | for !glfw.WindowShouldClose(window) {
243 | glfw.PollEvents()
244 | // If a key press happens, .PollEvents calls callback_key, defined below.
245 | // Note: glfw.PollEvents blocks on window menu interaction selection or
246 | // window resize. During window_resize, glfw.SetWindowRefreshCallback
247 | // calls window_refresh to redraw the window.
248 |
249 | render_screen(window, global_vao)
250 | }
251 | }
252 |
253 | render_screen :: proc( window : glfw.WindowHandle, vao : VAO) {
254 | // Calculate projection matrix.
255 | render_rect_w , render_rect_h : f32
256 | render_rect_w = WINDOW_W
257 | render_rect_h = WINDOW_H
258 |
259 | proj_mat := [4] f32 {
260 | 1/render_rect_w, 0,
261 | 0, 1/render_rect_h,
262 | }
263 |
264 | proj_mat_ptr : [^] f32 = &proj_mat[0]
265 |
266 | // Draw commands.
267 | gl.BindVertexArray(vao)
268 | defer gl.BindVertexArray(0)
269 |
270 | update_uni_2fv(global_shader, "projection", proj_mat_ptr)
271 |
272 | gl.ClearColor(0.1, 0.1, 0.1, 1)
273 | gl.Clear(gl.COLOR_BUFFER_BIT)
274 |
275 | // Render SENTENCE.
276 | x, y, advance: f32
277 | x = -0.9 * WINDOW_H
278 | y = 0
279 |
280 | // Add wave effect to the text
281 | ha, wl : f32 // Half-amplitude, wavelength
282 | // Call
283 | //
284 | // odin run Wavy-Words -define:ha=200
285 | //
286 | // to change this value at compile time.
287 | ha = #config(ha, 100)
288 | wl = f32(WINDOW_W) * #config(wl, 0.2)
289 |
290 | raw_duration := time.stopwatch_duration(watch)
291 | secs := f32(time.duration_seconds(raw_duration))
292 | t := 3 * secs
293 |
294 | // Render letters in SENTENCE.
295 | // Note that render_character calls gl.DrawArrays, and in general
296 | // gl.DrawArrays calls should be minimized if possible. In our
297 | // case the sentence is small enough that it's fine to call it
298 | // several times.
299 | for r in SENTENCE {
300 | ywave := ha * math.sin(x/wl + t) + y
301 | advance = render_character(r, x, ywave, vao)
302 | x += advance
303 | }
304 |
305 | // Send buffer to screen.
306 | glfw.SwapBuffers(window)
307 | }
308 |
309 | render_character :: proc( r : rune, xpos , ypos : f32, vao : VAO) -> (advance : f32) {
310 | char_texture : CharacterTexture
311 | char_texture = rune_to_glyph_texture[r]
312 | w := f32(char_texture.width)
313 | h := f32(char_texture.height)
314 | x := xpos + char_texture.bbox_x
315 | y := ypos + char_texture.bbox_y
316 |
317 | character_vertices : [4 * 6] f32 = {
318 | // Position ; Texture Coords
319 | x , y + h, 0, 0,
320 | x , y , 0, 1,
321 | x + w, y , 1, 1,
322 | x , y + h, 0, 0,
323 | x + w, y , 1, 1,
324 | x + w, y + h, 1, 0,
325 | }
326 |
327 | gl.BindVertexArray(vao)
328 | gl.BindTexture(gl.TEXTURE_2D, char_texture.texID)
329 | gl.BufferSubData(gl.ARRAY_BUFFER, // update vertices
330 | 0, // offset
331 | size_of(character_vertices), // size
332 | &character_vertices) // data
333 | // Draw the character!
334 | gl.DrawArrays(gl.TRIANGLES, 0, 6)
335 | advance = char_texture.advance
336 | return
337 | }
338 |
339 | // Quit the window if the ESC key is pressed. This procedure is called by
340 | // glfw.SetKeyCallback.
341 | callback_key :: proc "c" ( window : glfw.WindowHandle, key, scancode, action, mods : i32 ) {
342 | if action == glfw.PRESS && key == glfw.KEY_ESCAPE {
343 | glfw.SetWindowShouldClose(window, true)
344 | }
345 | }
346 |
347 | // If the window needs to be redrawn (e.g. the user resizes the window), redraw the window.
348 | // This procedure is called by glfw.SetWindowRefreshCallback.
349 | window_refresh :: proc "c" ( window : glfw.WindowHandle ) {
350 | context = runtime.default_context()
351 | w, h : i32
352 | w, h = glfw.GetWindowSize(window)
353 | gl.Viewport(0,0,w,h)
354 | render_screen(window, global_vao)
355 | }
356 |
--------------------------------------------------------------------------------