Created
May 10, 2020 14:36
-
-
Save bens/767a26f0665d206b5f3ac9c3d516d0b9 to your computer and use it in GitHub Desktop.
Hello World for OpenGL 3.3 with SDL2 in ziglang
This file contains hidden or 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
const std = @import("std"); | |
const Allocator = std.mem.Allocator; | |
const c = @cImport({ | |
@cInclude("./gl_core_3_3.h"); | |
@cInclude("SDL2/SDL.h"); | |
@cInclude("SDL2/SDL_opengl.h"); | |
}); | |
const SDLError = error{ | |
FailedInit, | |
FailedCreatingWindow, | |
FailedCreateGLContext, | |
FailedGettingEvent, | |
FailedDraw, | |
FailedScreenUpdate, | |
FailedSettingAttribute, | |
FailedSettingSwapInterval, | |
}; | |
const GLError = error{ | |
FailedCreateProgram, | |
FailedCreateShader, | |
FailedLinkingProgram, | |
FailedCompilingShader, | |
FailedGettingAttributeLocation, | |
}; | |
const vertexShaderSource: [*:0]const u8 = | |
\\#version 140 | |
\\in vec2 LVertexPos2D; | |
\\void main() { | |
\\ gl_Position = vec4( LVertexPos2D.x, LVertexPos2D.y, 0, 1 ); | |
\\} | |
; | |
const fragmentShaderSource: [*:0]const u8 = | |
\\#version 140 | |
\\out vec4 LFragment; | |
\\void main() { | |
\\ LFragment = vec4( 1.0, 1.0, 1.0, 1.0 ); | |
\\} | |
; | |
const System = struct { | |
window: *c.SDL_Window, | |
glContext: c.SDL_GLContext, | |
glVBO: c.GLuint, | |
glIBO: c.GLuint, | |
glProgram: c_uint, | |
glVertexShader: c_uint, | |
glFragmentShader: c_uint, | |
vertexPos2DLocation: c_uint, | |
fn render(self: System) SDLError!void { | |
c.glClear(c.GL_COLOR_BUFFER_BIT); | |
c.glUseProgram(self.glProgram); | |
c.glEnableVertexAttribArray(self.vertexPos2DLocation); | |
//Set vertex data | |
c.glBindBuffer(c.GL_ARRAY_BUFFER, self.glVBO); | |
c.glVertexAttribPointer( | |
self.vertexPos2DLocation, | |
2, | |
c.GL_FLOAT, | |
c.GL_FALSE, | |
2 * @sizeOf(c.GLfloat), | |
null, | |
); | |
c.glBindBuffer(c.GL_ELEMENT_ARRAY_BUFFER, self.glIBO); | |
c.glDrawElements(c.GL_TRIANGLE_FAN, 4, c.GL_UNSIGNED_INT, null); | |
c.glDisableVertexAttribArray(self.vertexPos2DLocation); | |
c.glUseProgram(0); | |
c.SDL_GL_SwapWindow(self.window); | |
} | |
}; | |
var logBuffer: [4096]u8 = undefined; | |
pub fn main() !void { | |
var logAlloc = std.heap.FixedBufferAllocator.init(&logBuffer); | |
const system = try initialise(&logAlloc.allocator); | |
defer deinitialise(system); | |
const surface = c.SDL_GetWindowSurface(system.window); | |
// Loop, blocking for events. | |
var event: c.SDL_Event = undefined; | |
while (true) { | |
if (c.SDL_WaitEvent(&event) == 0) { | |
std.debug.warn( | |
"Getting next event failed: {s}\n", | |
.{c.SDL_GetError()}, | |
); | |
return SDLError.FailedGettingEvent; | |
} | |
if (is_quit(&event)) break; | |
try system.render(); | |
} | |
} | |
fn initialise(logAlloc: *Allocator) !System { | |
try SDL.init(c.SDL_INIT_VIDEO | c.SDL_INIT_EVENTS); | |
errdefer SDL.quit(); | |
try SDL.setGLAttribute(c.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3); | |
try SDL.setGLAttribute(c.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 1); | |
try SDL.setGLAttribute( | |
c.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, | |
c.SDL_GL_CONTEXT_PROFILE_CORE, | |
); | |
const window = try SDL.createWindow( | |
"SDL2 & OGL3.1", | |
640, | |
480, | |
c.SDL_WINDOW_OPENGL | c.SDL_WINDOW_SHOWN, | |
); | |
const glctx = try SDL.createGLContext(window); | |
errdefer SDL.deleteGLContext(glctx); | |
const program = try GL.createProgram(); | |
errdefer GL.deleteProgram(program); | |
const vShader = try GL.createVertexShader(); | |
errdefer GL.deleteShader(vShader); | |
c.glShaderSource(vShader, 1, &vertexShaderSource, null); | |
try GL.compileShader(logAlloc, vShader); | |
c.glAttachShader(program, vShader); | |
const fShader = try GL.createFragmentShader(); | |
errdefer GL.deleteShader(fShader); | |
c.glShaderSource(fShader, 1, &fragmentShaderSource, null); | |
try GL.compileShader(logAlloc, fShader); | |
c.glAttachShader(program, fShader); | |
try GL.linkProgram(logAlloc, program); | |
errdefer GL.deleteShader(vShader); | |
const gVertexPos2DLocation = c.glGetAttribLocation(program, "LVertexPos2D"); | |
if (gVertexPos2DLocation == -1) { | |
std.debug.warn( | |
"LVertexPos2D is not a valid GLSL program variable!\n", | |
.{}, | |
); | |
return GLError.FailedGettingAttributeLocation; | |
} | |
std.debug.warn("Pos2d location: {}\n", .{gVertexPos2DLocation}); | |
//Create VBO | |
const vertexData = [_]c.GLfloat{ | |
-0.5, -0.5, | |
0.5, -0.5, | |
0.5, 0.5, | |
-0.5, 0.5, | |
}; | |
var vbo: c.GLuint = undefined; | |
c.glGenBuffers(1, &vbo); | |
c.glBindBuffer(c.GL_ARRAY_BUFFER, vbo); | |
c.glBufferData( | |
c.GL_ARRAY_BUFFER, | |
2 * 4 * @sizeOf(c.GLfloat), | |
&vertexData, | |
c.GL_STATIC_DRAW, | |
); | |
//Create IBO | |
const indexData = [_]c.GLuint{ 0, 1, 2, 3 }; | |
var ibo: c.GLuint = undefined; | |
c.glGenBuffers(1, &ibo); | |
c.glBindBuffer(c.GL_ELEMENT_ARRAY_BUFFER, ibo); | |
c.glBufferData( | |
c.GL_ELEMENT_ARRAY_BUFFER, | |
4 * @sizeOf(c.GLuint), | |
&indexData, | |
c.GL_STATIC_DRAW, | |
); | |
try SDL.setSwapInterval(1); | |
c.SDL_SetEventFilter(event_filter, null); | |
return System{ | |
.window = window, | |
.glContext = glctx, | |
.glVBO = vbo, | |
.glIBO = ibo, | |
.glProgram = program, | |
.glVertexShader = vShader, | |
.glFragmentShader = fShader, | |
.vertexPos2DLocation = @intCast(c_uint, gVertexPos2DLocation), | |
}; | |
} | |
fn deinitialise(system: System) void { | |
defer SDL.quit(); | |
defer SDL.deleteGLContext(system.glContext); | |
defer GL.deleteProgram(system.glProgram); | |
defer GL.deleteShader(system.glVertexShader); | |
defer GL.deleteShader(system.glFragmentShader); | |
} | |
// ************* | |
// ** HELPERS ** | |
// ************* | |
/// Pressing 'Q' stops the program. | |
fn is_quit(event: *c.SDL_Event) bool { | |
return switch (event.type) { | |
c.SDL_QUIT => true, | |
c.SDL_KEYDOWN => event.key.keysym.sym == c.SDLK_q, | |
else => false, | |
}; | |
} | |
/// A filter function to only enqueue certain events. | |
export fn event_filter(userdata: ?*c_void, event: [*c]c.SDL_Event) c_int { | |
return switch (event.*.type) { | |
c.SDL_QUIT, c.SDL_KEYDOWN, c.SDL_WINDOWEVENT => 1, | |
else => 0, | |
}; | |
} | |
// ************** | |
// ** WRAPPERS ** | |
// ************** | |
/// SDL Wrapper | |
const SDL = struct { | |
fn init(flags: u32) SDLError!void { | |
if (c.SDL_Init(flags) != 0) { | |
std.debug.warn( | |
"Initialising SDL failed: {s}\n", | |
.{c.SDL_GetError()}, | |
); | |
return SDLError.FailedInit; | |
} | |
} | |
fn quit() void { | |
c.SDL_Quit(); | |
} | |
fn createWindow( | |
title: [*c]const u8, | |
w: c_int, | |
h: c_int, | |
flags: u32, | |
) SDLError!*c.SDL_Window { | |
const try_window = c.SDL_CreateWindow( | |
title, | |
c.SDL_WINDOWPOS_UNDEFINED, | |
c.SDL_WINDOWPOS_UNDEFINED, | |
w, | |
h, | |
flags, | |
); | |
if (try_window) |window| { | |
return window; | |
} else { | |
std.debug.warn( | |
"Creating Window failed: {s}\n", | |
.{c.SDL_GetError()}, | |
); | |
return SDLError.FailedCreatingWindow; | |
} | |
} | |
fn createGLContext(window: *c.SDL_Window) SDLError!c.SDL_GLContext { | |
if (c.SDL_GL_CreateContext(window)) |glctx| { | |
return glctx; | |
} else { | |
std.debug.warn( | |
"Creating OpenGL context failed: {s}\n", | |
.{c.SDL_GetError()}, | |
); | |
return SDLError.FailedCreateGLContext; | |
} | |
} | |
fn deleteGLContext(glctx: c.SDL_GLContext) void { | |
c.SDL_GL_DeleteContext(glctx); | |
} | |
fn setGLAttribute(attr: c.SDL_GLattr, value: c_int) SDLError!void { | |
const r = c.SDL_GL_SetAttribute(attr, value); | |
if (r != 0) { | |
std.debug.warn( | |
"Setting attribute {} failed: {s}\n", | |
.{ attr, c.SDL_GetError() }, | |
); | |
return SDLError.FailedSettingAttribute; | |
} else { | |
return; | |
} | |
} | |
fn setSwapInterval(interval: c_int) SDLError!void { | |
if (c.SDL_GL_SetSwapInterval(interval) < 0) { | |
std.debug.warn( | |
"Set swap interval failed: {s}\n", | |
.{c.SDL_GetError()}, | |
); | |
return SDLError.FailedSettingSwapInterval; | |
} | |
} | |
}; | |
/// OpenGL Wrapper | |
const GL = struct { | |
fn createProgram() GLError!c_uint { | |
const r = c.glCreateProgram(); | |
if (r == 0) { | |
return GLError.FailedCreateProgram; | |
} else { | |
return r; | |
} | |
} | |
fn deleteProgram(program: c_uint) void { | |
c.glDeleteProgram(program); | |
} | |
fn createVertexShader() GLError!c_uint { | |
const r = c.glCreateShader(c.GL_VERTEX_SHADER); | |
if (r == 0) { | |
return GLError.FailedCreateShader; | |
} else { | |
return r; | |
} | |
} | |
fn createFragmentShader() GLError!c_uint { | |
const r = c.glCreateShader(c.GL_FRAGMENT_SHADER); | |
if (r == 0) { | |
return GLError.FailedCreateShader; | |
} else { | |
return r; | |
} | |
} | |
fn deleteShader(shader: c_uint) void { | |
c.glDeleteShader(shader); | |
} | |
fn compileShader(logAlloc: *Allocator, shader: c_uint) !void { | |
c.glCompileShader(shader); | |
var ok: c_int = undefined; | |
c.glGetShaderiv(shader, c.GL_COMPILE_STATUS, &ok); | |
if (ok == c.GL_FALSE) { | |
std.debug.warn("Unable to compile shader: {}\n", .{shader}); | |
try GL.printShaderLog(logAlloc, shader); | |
return GLError.FailedCompilingShader; | |
} else { | |
return; | |
} | |
} | |
fn linkProgram(logAlloc: *Allocator, program: c_uint) !void { | |
c.glLinkProgram(program); | |
var ok: c_int = undefined; | |
c.glGetProgramiv(program, c.GL_LINK_STATUS, &ok); | |
if (ok != c.GL_TRUE) { | |
std.debug.warn("Unable to link program: {}\n", .{program}); | |
try GL.printShaderLog(logAlloc, program); | |
return GLError.FailedLinkingProgram; | |
} else { | |
return; | |
} | |
} | |
fn printProgramLog(logAlloc: *Allocator, program: c.GLuint) !void { | |
if (c.glIsProgram(program) != c.GL_TRUE) { | |
std.debug.warn("Name {} is not a program\n", .{program}); | |
return; | |
} | |
var maxLength: c_int = undefined; | |
c.glGetShaderiv(program, c.GL_INFO_LOG_LENGTH, &maxLength); | |
if (maxLength == 0) { | |
std.debug.warn("No shader info log\n", .{}); | |
return; | |
} | |
var infoLog: [:0]u8 = try logAlloc.allocWithOptions( | |
u8, | |
@intCast(usize, maxLength), | |
null, | |
0, | |
); | |
defer logAlloc.free(infoLog); | |
var infoLogLength: c_int = 0; | |
c.glGetProgramInfoLog(program, maxLength, &infoLogLength, infoLog); | |
if (infoLogLength > 0) { | |
std.debug.warn("{s}\n", .{infoLog}); | |
} | |
} | |
fn printShaderLog(logAlloc: *Allocator, shader: c.GLuint) !void { | |
if (c.glIsShader(shader) != c.GL_TRUE) { | |
std.debug.warn("Name {} is not a shader\n", .{shader}); | |
return; | |
} | |
var maxLength: c_int = undefined; | |
c.glGetShaderiv(shader, c.GL_INFO_LOG_LENGTH, &maxLength); | |
if (maxLength == 0) { | |
std.debug.warn("No shader info log\n", .{}); | |
return; | |
} | |
var infoLog: [:0]u8 = try logAlloc.allocWithOptions( | |
u8, | |
@intCast(usize, maxLength), | |
null, | |
0, | |
); | |
defer logAlloc.free(infoLog); | |
var infoLogLength: c_int = 0; | |
c.glGetShaderInfoLog(shader, maxLength, &infoLogLength, infoLog); | |
if (infoLogLength > 0) { | |
std.debug.warn("{s}\n", .{infoLog}); | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment