Last active
September 23, 2024 06:31
-
-
Save SorenSaket/155afe1ec11a79def63341c588ade329 to your computer and use it in GitHub Desktop.
GLFW, OpenGL Window Tutorial in Odin language
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// GLFW and OpenGL example with very verbose comments and links to documentation for learning | |
// By Soren Saket | |
// semi-colons ; are not requied in odin | |
// | |
// Every Odin script belongs to a package | |
// Define the package with the package [packageName] statement | |
// The main package name is reserved for the program entry point package | |
// You cannot have two different packages in the same directory | |
// If you want to create another package create a new directory and name the package the same as the directory | |
// You can then import the package with the import keyword | |
// https://odin-lang.org/docs/overview/#packages | |
package main | |
// Import statement | |
// https://odin-lang.org/docs/overview/#packages | |
// Odin by default has two library collections. Core and Vendor | |
// Core contains the default library all implemented in the Odin language | |
// Vendor contains bindings for common useful packages aimed at game and software development | |
// https://odin-lang.org/docs/overview/#import-statement | |
// fmt contains formatted I/O procedures. | |
// https://pkg.odin-lang.org/core/fmt/ | |
import "core:fmt" | |
// C interoperation compatibility | |
import "core:c" | |
// Here we import OpenGL and rename it to gl for short | |
import gl "vendor:OpenGL" | |
// We use GLFW for cross platform window creation and input handling | |
import "vendor:glfw" | |
// Odin has type type inference | |
// variableName := value | |
// variableName : type = value | |
// You can set constants with :: | |
PROGRAMNAME :: "Program" | |
// GL_VERSION define the version of OpenGL to use. Here we use 4.6 which is the newest version | |
// You might need to lower this to 3.3 depending on how old your graphics card is. | |
// Constant with explicit type for example | |
GL_MAJOR_VERSION : c.int : 4 | |
// Constant with type inference | |
GL_MINOR_VERSION :: 6 | |
// Our own boolean storing if the application is running | |
// We use b32 for allignment and easy compatibility with the glfw.WindowShouldClose procedure | |
// See https://odin-lang.org/docs/overview/#basic-types for more information on the types in Odin | |
running : b32 = true | |
// The main function is the entry point for the application | |
// In Odin functions/methods are more precisely named procedures | |
// procedureName :: proc() -> returnType | |
// https://odin-lang.org/docs/overview/#procedures | |
main :: proc() { | |
// Set Window Hints | |
// https://www.glfw.org/docs/3.3/window_guide.html#window_hints | |
// https://www.glfw.org/docs/3.3/group__window.html#ga7d9c8c62384b1e2821c4dc48952d2033 | |
glfw.WindowHint(glfw.RESIZABLE, 1) | |
glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR,GL_MAJOR_VERSION) | |
glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR,GL_MINOR_VERSION) | |
glfw.WindowHint(glfw.OPENGL_PROFILE,glfw.OPENGL_CORE_PROFILE) | |
// Initialize glfw | |
// GLFW_TRUE if successful, or GLFW_FALSE if an error occurred. | |
// GLFW_TRUE = 1 | |
// GLFW_FALSE = 0 | |
// https://www.glfw.org/docs/latest/group__init.html#ga317aac130a235ab08c6db0834907d85e | |
if(glfw.Init() != 1){ | |
// Print Line | |
fmt.println("Failed to initialize GLFW") | |
// Return early | |
return | |
} | |
// the defer keyword makes the procedure run when the calling procedure exits scope | |
// Deferes are executed in reverse order. So the window will get destoryed first | |
// They can also just be called manually later instead without defer. This way of doing it ensures are terminated. | |
// https://odin-lang.org/docs/overview/#defer-statement | |
// https://www.glfw.org/docs/3.1/group__init.html#gaaae48c0a18607ea4a4ba951d939f0901 | |
defer glfw.Terminate() | |
// Create the window | |
// Return WindowHandle rawPtr | |
// https://www.glfw.org/docs/3.3/group__window.html#ga3555a418df92ad53f917597fe2f64aeb | |
window := glfw.CreateWindow(512, 512, PROGRAMNAME, nil, nil) | |
// https://www.glfw.org/docs/latest/group__window.html#gacdf43e51376051d2c091662e9fe3d7b2 | |
defer glfw.DestroyWindow(window) | |
// If the window pointer is invalid | |
if window == nil { | |
fmt.println("Unable to create window") | |
return | |
} | |
// | |
// https://www.glfw.org/docs/3.3/group__context.html#ga1c04dc242268f827290fe40aa1c91157 | |
glfw.MakeContextCurrent(window) | |
// Enable vsync | |
// https://www.glfw.org/docs/3.3/group__context.html#ga6d4e0cdf151b5e579bd67f13202994ed | |
glfw.SwapInterval(1) | |
// This function sets the key callback of the specified window, which is called when a key is pressed, repeated or released. | |
// https://www.glfw.org/docs/3.3/group__input.html#ga1caf18159767e761185e49a3be019f8d | |
glfw.SetKeyCallback(window, key_callback) | |
// This function sets the framebuffer resize callback of the specified window, which is called when the framebuffer of the specified window is resized. | |
// https://www.glfw.org/docs/3.3/group__window.html#gab3fb7c3366577daef18c0023e2a8591f | |
glfw.SetFramebufferSizeCallback(window, size_callback) | |
// Set OpenGL Context bindings using the helper function | |
// See Odin Vendor source for specifc implementation details | |
// https://github.com/odin-lang/Odin/tree/master/vendor/OpenGL | |
// https://www.glfw.org/docs/3.3/group__context.html#ga35f1837e6f666781842483937612f163 | |
// casting the c.int to int | |
// This is needed because the GL_MAJOR_VERSION has an explicit type of c.int | |
gl.load_up_to(int(GL_MAJOR_VERSION), GL_MINOR_VERSION, glfw.gl_set_proc_address) | |
init() | |
// There is only one kind of loop in Odin called for | |
// https://odin-lang.org/docs/overview/#for-statement | |
for (!glfw.WindowShouldClose(window) && running) { | |
// Process waiting events in queue | |
// https://www.glfw.org/docs/3.3/group__window.html#ga37bd57223967b4211d60ca1a0bf3c832 | |
glfw.PollEvents() | |
update() | |
draw() | |
// This function swaps the front and back buffers of the specified window. | |
// See https://en.wikipedia.org/wiki/Multiple_buffering to learn more about Multiple buffering | |
// https://www.glfw.org/docs/3.0/group__context.html#ga15a5a1ee5b3c2ca6b15ca209a12efd14 | |
glfw.SwapBuffers((window)) | |
} | |
exit() | |
} | |
init :: proc(){ | |
// Own initialization code there | |
} | |
update :: proc(){ | |
// Own update code here | |
} | |
draw :: proc(){ | |
// Set the opengl clear color | |
// 0-1 rgba values | |
gl.ClearColor(0.2, 0.3, 0.3, 1.0) | |
// Clear the screen with the set clearcolor | |
gl.Clear(gl.COLOR_BUFFER_BIT) | |
// Own drawing code here | |
} | |
exit :: proc(){ | |
// Own termination code here | |
} | |
// Called when glfw keystate changes | |
key_callback :: proc "c" (window: glfw.WindowHandle, key, scancode, action, mods: i32) { | |
// Exit program on escape pressed | |
if key == glfw.KEY_ESCAPE { | |
running = false | |
} | |
} | |
// Called when glfw window changes size | |
size_callback :: proc "c" (window: glfw.WindowHandle, width, height: i32) { | |
// Set the OpenGL viewport size | |
gl.Viewport(0, 0, width, height) | |
} |
FYI correct order for the hints is, as @aveplen has suggested, and also:
glfw.WindowHint(glfw.RESIZABLE, glfw.TRUE)
glfw.WindowHint(glfw.OPENGL_FORWARD_COMPAT, glfw.TRUE)
glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, GL_MAJOR_VERSION)
glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, GL_MINOR_VERSION)
One needs to specify GLFW_OPENGL_FORWARD_COMPAT
and GLFW_OPENGL_PROFILE
BEFORE the GLFW_CONTEXT_VERSION_MAJOR
and GLFW_CONTEXT_VERSION_MINOR
hints when on macOS.
macOS: The OS only supports forward-compatible core profile contexts for OpenGL versions 3.2 and later. Before creating an OpenGL context of version 3.2 or later you must set the GLFW_OPENGL_FORWARD_COMPAT and GLFW_OPENGL_PROFILE hints accordingly. OpenGL 3.0 and 3.1 contexts are not supported at all on macOS.
https://www.glfw.org/docs/3.3/window_guide.html#GLFW_CONTEXT_VERSION_MAJOR_hint
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Be careful with this example because it sets glfw window hints before initialization. It may not work on Mac M1 chips because opengl doesn't run natively on those - it uses some weird translation layer into Metal, that fallbacks to opengl version 2.1 (released in 2006), that doesn't support vertex arrays.
I would suggest moving glfw.Init() ... (line 73) to the top of main (line 60) and setting gl major and minor to those that would work - those can be found experimentally. For me it was 4.1 (macOS Monterey - version 12.1)
odin-lang/Odin#2556