package main
import "base:runtime"
import "core:c"
import "core:log"
import "core:math/linalg/glsl"
import "core:mem/virtual"
import "core:slice"
import gl "vendor:OpenGL"
import "vendor:glfw"
@(export)
NvOptimusEnablement: u32 = 1
@(export)
AmdPowerXpressRequestHighPerformance: int = 1
TARGET_FPS :: 60
vertices := []struct {
x, y: f32,
u, v: f32,
}{{-1.0, -1.0, 0.0, 1.0}, {1.0, -1.0, 1.0, 1.0}, {-1.0, 1.0, 0.0, 0.0}, {1.0, 1.0, 1.0, 0.0}}
indices := []u32{0, 1, 2, 2, 3, 1}
Bitmap :: struct {
width: int,
height: int,
buffer: []u8,
_internal: struct {
texture: u32,
},
}
create_bitmap :: proc(width, height: int) -> Bitmap {
bitmap: Bitmap = {
width = width,
height = height,
buffer = make([]u8, width * height * 4),
}
gl.GenTextures(1, &bitmap._internal.texture)
gl.BindTexture(gl.TEXTURE_2D, bitmap._internal.texture)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_BORDER)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_BORDER)
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, i32(width), i32(height), 0, gl.BGRA, gl.UNSIGNED_BYTE, nil)
gl.TexSubImage2D(
gl.TEXTURE_2D,
0,
0,
0,
i32(width),
i32(height),
gl.BGRA,
gl.UNSIGNED_BYTE,
raw_data(bitmap.buffer),
)
gl.BindBuffer(gl.TEXTURE_BUFFER, 0)
return bitmap
}
destroy_bitmap :: proc(bitmap: ^Bitmap) {
gl.DeleteTextures(1, &bitmap._internal.texture)
delete(bitmap.buffer)
}
main :: proc() {
context.logger = log.create_console_logger(.Debug, log.Default_Console_Logger_Opts, "MAIN")
log.info("booting up...")
if ok := glfw.Init(); !ok {
log.panic("fail :: init glfw")
}
defer glfw.Terminate()
glfw.WindowHint(glfw.RESIZABLE, false)
glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 4)
glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 1)
glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
glfw.WindowHint(glfw.OPENGL_FORWARD_COMPAT, true)
glfw.WindowHint(glfw.OPENGL_DEBUG_CONTEXT, true)
glfw.SetErrorCallback(proc "c" (error: c.int, description: cstring) {
context = runtime.default_context()
context.logger = log.create_console_logger(.Debug, log.Default_Console_Logger_Opts, "GLFW")
log.errorf("%d:%s", error, description)
})
window := glfw.CreateWindow(1280, 720, "Cellular Automata", nil, nil)
if window == nil {
log.panic("fail :: create window")
}
defer glfw.DestroyWindow(window)
glfw.MakeContextCurrent(window)
glfw.SwapInterval(0)
glfw.SetKeyCallback(window, key_callback)
gl.load_up_to(4, 1, glfw.gl_set_proc_address)
log.debugf("GL Context: %s", gl.GetString(gl.VERSION))
/*if glfw.ExtensionSupported("GL_KHR_debug") != 0 {
// NOTE: Not supported on macos?
gl.Enable(gl.DEBUG_OUTPUT)
gl.Enable(gl.DEBUG_OUTPUT_SYNCHRONOUS)
gl.DebugMessageCallback(debug_message_proc, nil)
gl.DebugMessageControl(gl.DONT_CARE, gl.DONT_CARE, gl.DONT_CARE, 0, nil, true)
gl.DebugMessageInsert(
gl.DEBUG_SOURCE_APPLICATION,
gl.DEBUG_TYPE_ERROR,
0,
gl.DEBUG_SEVERITY_NOTIFICATION,
-1,
"Vary dangerous error",
)
}*/
width, height := glfw.GetFramebufferSize(window)
gl.Viewport(0, 0, width, height)
log.debugf("viewport %dx%d", width, height)
frame_vb, frame_ib: u32
gl.GenBuffers(1, &frame_vb)
gl.GenBuffers(1, &frame_ib)
gl.BindBuffer(gl.ARRAY_BUFFER, frame_vb)
gl.BufferData(gl.ARRAY_BUFFER, size_of(vertices[0]) * len(vertices), raw_data(vertices), gl.STATIC_DRAW)
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, frame_ib)
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size_of(indices[0]) * len(indices), raw_data(indices), gl.STATIC_DRAW)
bitmap := create_bitmap(int(width), int(height))
defer destroy_bitmap(&bitmap)
log.debugf("bitmap: %dx%d, %d bytes", bitmap.width, bitmap.height, len(bitmap.buffer))
program, ok := gl.load_shaders_file("src/simple.vs", "src/simple.fs")
//msg, _, _, _ := gl.get_last_error_messages()
//log.errorf("msg: %s", msg)
assert(ok, "shader failed")
log.infof("program %d %b", program, ok)
mvp_location := gl.GetUniformLocation(program, "MVP")
vpos_location := gl.GetAttribLocation(program, "vPos")
uv_location := gl.GetAttribLocation(program, "vTexCoord")
log.debugf("locs %d %d %d", mvp_location, vpos_location, uv_location)
frame_vao: u32
gl.GenVertexArrays(1, &frame_vao)
gl.BindVertexArray(frame_vao)
gl.BindBuffer(gl.ARRAY_BUFFER, frame_vb)
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, frame_ib)
gl.EnableVertexAttribArray(u32(vpos_location))
gl.EnableVertexAttribArray(u32(uv_location))
gl.VertexAttribPointer(u32(vpos_location), 2, gl.FLOAT, false, size_of(vertices[0]), 0)
gl.VertexAttribPointer(u32(uv_location), 2, gl.FLOAT, false, size_of(vertices[0]), size_of(f32) * 2)
gl.BindVertexArray(0)
gl.ClearColor(0.5, 0.5, 1.0, 1.0)
projection: glsl.mat4
projection = glsl.mat4Ortho3d(-1, 1, -1, 1, 1, -1)
app_memory: virtual.Arena
if err := virtual.arena_init_static(&app_memory); err != .None {
log.panicf("fail :: arena_init, %v", err)
}
defer virtual.arena_destroy(&app_memory)
app: ^App
{
context.allocator = virtual.arena_allocator(&app_memory)
app = app_init()
}
log.debugf("app_memory: %v", app_memory)
max_tick := 1.0 / TARGET_FPS
log.debugf("max_tick: %f (target %d fps)", max_tick, TARGET_FPS)
tick := glfw.GetTime()
draws_tick := glfw.GetTime()
draws: int
for !glfw.WindowShouldClose(window) {
gl.Clear(gl.COLOR_BUFFER_BIT)
gl.UseProgram(program)
defer gl.UseProgram(0)
gl.UniformMatrix4fv(mvp_location, 1, false, &projection[0, 0])
{
new_tick := glfw.GetTime()
gl.BindTexture(gl.TEXTURE_2D, bitmap._internal.texture)
if (new_tick - tick > max_tick) {
tick = new_tick
slice.fill(bitmap.buffer, 0)
{
context.allocator = virtual.arena_allocator(&app_memory)
app_update_and_draw(app, &bitmap)
}
gl.TexSubImage2D(
gl.TEXTURE_2D,
0,
0,
0,
i32(bitmap.width),
i32(bitmap.height),
gl.BGRA,
gl.UNSIGNED_INT_8_8_8_8_REV,
raw_data(bitmap.buffer),
)
}
defer gl.BindTexture(gl.TEXTURE_2D, 0)
gl.BindVertexArray(frame_vao)
defer gl.BindVertexArray(0)
gl.DrawElements(gl.TRIANGLES, i32(len(indices)), gl.UNSIGNED_INT, nil)
}
glfw.SwapBuffers(window)
glfw.PollEvents()
draws += 1
new_draws_tick := glfw.GetTime()
if (new_draws_tick - draws_tick > 1) {
draws_tick = new_draws_tick
log.debugf("draws: %d", draws)
draws = 0
}
}
}
debug_message_proc :: proc "c" (
source: u32,
type: u32,
id: u32,
severity: u32,
length: i32,
message: cstring,
user_param: rawptr,
) {
context = runtime.default_context()
context.logger = log.create_console_logger(.Debug, log.Default_Console_Logger_Opts, "GL")
log.debugf("%d %d %d %d %d %s %p", source, type, id, severity, length, message, user_param)
}
key_callback :: proc "c" (window: glfw.WindowHandle, key, scancode, action, mods: i32) {
// Exit program on escape pressed
if key == glfw.KEY_ESCAPE {
glfw.SetWindowShouldClose(window, true)
}
}