#+feature dynamic-literals package main import "base:runtime" import "core:fmt" import "core:log" import glm "core:math/linalg/glsl" import "core:os" import gl "vendor:OpenGL" import "vendor:glfw" import stbi "vendor:stb/image" import "helpers" import "image" import "model" import "texture" import "vertex" GL_MAJOR :: 4 GL_MINOR :: 6 default_context: runtime.Context draw :: proc(mdl: model.Model) { gl.BindVertexArray(mdl.vao) for material_name, material in mdl.materials { gl.BindTextureUnit(0, material.texture.id) for range in material.ranges { gl.DrawElements( gl.TRIANGLES, range.count, gl.UNSIGNED_INT, rawptr(uintptr(size_of(u32) * range.first)), ) } } } Object :: struct { model: model.Model, transform: glm.mat4, update: proc(self: ^Object), } Scene :: struct { objects: [dynamic]Object, } main :: proc() { context.logger = log.create_console_logger() default_context = context _, use_x11 := os.lookup_env("USE_X11") if use_x11 { glfw.InitHint(glfw.PLATFORM, glfw.PLATFORM_X11) } stbi.set_flip_vertically_on_load(1) if !glfw.Init() { log.error("Failed to initialize GLFW:", glfw.GetError()) return } defer glfw.Terminate() glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, GL_MAJOR) glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, GL_MINOR) glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) glfw.WindowHint(glfw.SAMPLES, 4) window := glfw.CreateWindow(800, 600, "Karts", nil, nil) if window == nil { log.error("Failed to create window:", glfw.GetError()) return } defer glfw.DestroyWindow(window) glfw.MakeContextCurrent(window) // glfw.SwapInterval(0) gl.load_up_to(GL_MAJOR, GL_MINOR, glfw.gl_set_proc_address) framebuffer_size(window, glfw.GetFramebufferSize(window)) // initial viewport setup glfw.SetFramebufferSizeCallback(window, framebuffer_size) gl.Enable(gl.CULL_FACE) gl.CullFace(gl.BACK) gl.FrontFace(gl.CCW) gl.Enable(gl.DEPTH_TEST) gl.Enable(gl.DEBUG_OUTPUT) gl.Enable(gl.DEBUG_OUTPUT_SYNCHRONOUS) gl.DebugMessageCallback(message_callback, nil) basic_program, program_ok := gl.load_shaders_source(#load("basic.vert.glsl"), #load("basic.frag.glsl")) if !program_ok { log.error("Failed to load shader:", gl.get_last_error_message()) return } defer gl.DeleteProgram(basic_program) basic_uniforms := gl.get_uniforms_from_program(basic_program) defer delete(basic_uniforms) lighting_program, lighting_ok := gl.load_shaders_source( #load("lighting.vert.glsl"), #load("lighting.frag.glsl"), ) if !lighting_ok { log.error("Failed to load shader:", gl.get_last_error_message()) return } defer gl.DeleteProgram(lighting_program) lighting_uniforms := gl.get_uniforms_from_program(lighting_program) defer delete(lighting_uniforms) vbo: u32 gl.CreateBuffers(1, &vbo) defer gl.DeleteBuffers(1, &vbo) ibo: u32 gl.CreateBuffers(1, &ibo) defer gl.DeleteBuffers(1, &ibo) mat_map := map[string]string { "textures/lion.tga" = #load("textures/lion.tga"), "textures/background.tga" = #load("textures/background.tga"), "textures/vase_plant.tga" = #load("textures/vase_plant.tga"), "textures/sponza_arch_diff.tga" = #load("textures/sponza_arch_diff.tga"), "textures/spnza_bricks_a_diff.tga" = #load("textures/spnza_bricks_a_diff.tga"), "textures/sponza_ceiling_a_diff.tga" = #load("textures/sponza_ceiling_a_diff.tga"), "textures/chain_texture.tga" = #load("textures/chain_texture.tga"), "textures/sponza_column_a_diff.tga" = #load("textures/sponza_column_a_diff.tga"), "textures/sponza_column_b_diff.tga" = #load("textures/sponza_column_b_diff.tga"), "textures/sponza_column_c_diff.tga" = #load("textures/sponza_column_c_diff.tga"), "textures/sponza_details_diff.tga" = #load("textures/sponza_details_diff.tga"), "textures/sponza_fabric_diff.tga" = #load("textures/sponza_fabric_diff.tga"), "textures/sponza_curtain_diff.tga" = #load("textures/sponza_curtain_diff.tga"), "textures/sponza_fabric_blue_diff.tga" = #load("textures/sponza_fabric_blue_diff.tga"), "textures/sponza_fabric_green_diff.tga" = #load("textures/sponza_fabric_green_diff.tga"), "textures/sponza_curtain_green_diff.tga" = #load("textures/sponza_curtain_green_diff.tga"), "textures/sponza_curtain_blue_diff.tga" = #load("textures/sponza_curtain_blue_diff.tga"), "textures/sponza_flagpole_diff.tga" = #load("textures/sponza_flagpole_diff.tga"), "textures/sponza_floor_a_diff.tga" = #load("textures/sponza_floor_a_diff.tga"), "textures/sponza_thorn_diff.tga" = #load("textures/sponza_thorn_diff.tga"), "textures/sponza_roof_diff.tga" = #load("textures/sponza_roof_diff.tga"), "textures/vase_dif.tga" = #load("textures/vase_dif.tga"), "textures/vase_hanging.tga" = #load("textures/vase_hanging.tga"), "textures/vase_round.tga" = #load("textures/vase_round.tga"), "images/bodyk_std_spb_alb.png" = #load("images/bodyk_std_spb_alb.png"), } maybe_sponza, sponza_err := model.load_obj_string(#load("sponza.obj"), #load("sponza.mtl"), mat_map) if sponza_err != nil { log.warn("Failed to load object file:", sponza_err) return } sponza := maybe_sponza.? maybe_kart, kart_err := model.load_obj_string(#load("kart.obj"), #load("kart.mtl"), mat_map) if kart_err != nil { log.warn("Failed to load object file:", kart_err) return } kart := maybe_kart.? scene: Scene append(&scene.objects, Object { model = sponza, update = proc(self: ^Object) { self.transform = glm.mat4Rotate({0, 1, 0}, f32(glfw.GetTime()) / 10) * glm.mat4Scale(glm.vec3(0.1)) }, }) append(&scene.objects, Object { model = kart, update = proc(self: ^Object) { self.transform = glm.mat4Rotate({0, 1, 0}, f32(glfw.GetTime()) / 10) * glm.mat4Translate({-1, 1, 0}) }, }) prev_time: f64 = 0 frame_times := make([dynamic]f64, 100) shadowmap: u32 gl.CreateFramebuffers(1, &shadowmap) defer gl.DeleteFramebuffers(1, &shadowmap) color_tex: u32 gl.CreateTextures(gl.TEXTURE_2D, 1, &color_tex) gl.TextureStorage2D(color_tex, 1, gl.RGBA8, 1024, 1024) gl.TextureParameteri(color_tex, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TextureParameteri(color_tex, gl.TEXTURE_MAG_FILTER, gl.LINEAR) depth_tex: u32 gl.CreateTextures(gl.TEXTURE_2D, 1, &depth_tex) gl.TextureStorage2D(depth_tex, 1, gl.DEPTH_COMPONENT32F, 1024, 1024) gl.TextureParameteri(depth_tex, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TextureParameteri(depth_tex, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TextureParameteri(depth_tex, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_BORDER) gl.TextureParameteri(depth_tex, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_BORDER) gl.NamedFramebufferTexture(shadowmap, gl.COLOR_ATTACHMENT0, color_tex, 0) gl.NamedFramebufferTexture(shadowmap, gl.DEPTH_ATTACHMENT, depth_tex, 0) for !glfw.WindowShouldClose(window) { glfw.PollEvents() time := glfw.GetTime() ordered_remove(&frame_times, 0) frame_time := time - prev_time append(&frame_times, frame_time) prev_time = time sum: f64 = 0 for ft in frame_times { sum += ft } frame_time = sum / f64(len(frame_times)) fmt.println(i64(1 / frame_time)) for &object in scene.objects { object.update(&object) } width, height := glfw.GetFramebufferSize(window) // view := glm.mat4LookAt({-75, 60, -110}, {5, 50, 0}, {0, 1, 0}) view := glm.mat4Translate({5, -5, -40}) proj := glm.mat4PerspectiveInfinite(70, f32(width) / f32(height), 0.1) viewproj := proj * view gl.BindFramebuffer(gl.FRAMEBUFFER, shadowmap) gl.Viewport(0, 0, 1024, 1024) gl.CullFace(gl.FRONT) shadowmap_view := glm.mat4LookAt({30, 100, -40}, {30, 0, 0}, {0, 1, 0}) shadowmap_proj := glm.mat4Ortho3d(0, 50, 0, 50, 0, 1000) shadowmap_viewproj := shadowmap_proj * shadowmap_view gl.ClearColor(0.3, 0.4, 0.8, 1.0) gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.UseProgram(basic_program) gl.UniformMatrix4fv(basic_uniforms["viewproj"].location, 1, false, &shadowmap_viewproj[0, 0]) for &object in scene.objects { gl.UniformMatrix4fv(basic_uniforms["model"].location, 1, false, &object.transform[0, 0]) draw(object.model) } gl.BindFramebuffer(gl.FRAMEBUFFER, 0) gl.Viewport(0, 0, width, height) gl.CullFace(gl.BACK) gl.ClearColor(0.3, 0.4, 0.8, 1.0) gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.UseProgram(lighting_program) gl.Uniform1i(lighting_uniforms["tex"].location, 0) gl.Uniform1i(lighting_uniforms["shadowmap"].location, 1) gl.UniformMatrix4fv(lighting_uniforms["viewproj"].location, 1, false, &viewproj[0, 0]) gl.UniformMatrix4fv( lighting_uniforms["shadowmap_viewproj"].location, 1, false, &shadowmap_viewproj[0, 0], ) gl.BindTextureUnit(1, depth_tex) for &object in scene.objects { gl.UniformMatrix4fv(basic_uniforms["model"].location, 1, false, &object.transform[0, 0]) draw(object.model) } glfw.SwapBuffers(window) } } framebuffer_size :: proc "c" (window: glfw.WindowHandle, width, height: i32) { gl.Viewport(0, 0, width, height) } message_callback :: proc "c" ( source, type: u32, id: u32, severity: u32, length: i32, message: cstring, user_param: rawptr, ) { context = default_context source_str := proc(source: u32) -> string { switch (source) { case gl.DEBUG_SOURCE_API: return "API" case gl.DEBUG_SOURCE_WINDOW_SYSTEM: return "WINDOW SYSTEM" case gl.DEBUG_SOURCE_SHADER_COMPILER: return "SHADER COMPILER" case gl.DEBUG_SOURCE_THIRD_PARTY: return "THIRD PARTY" case gl.DEBUG_SOURCE_APPLICATION: return "APPLICATION" case gl.DEBUG_SOURCE_OTHER: return "OTHER" case: return "UNKNOWN" } }(source) type_str := proc(type: u32) -> string { switch (type) { case gl.DEBUG_TYPE_ERROR: return "ERROR" case gl.DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "DEPRECATED_BEHAVIOR" case gl.DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "UNDEFINED_BEHAVIOR" case gl.DEBUG_TYPE_PORTABILITY: return "PORTABILITY" case gl.DEBUG_TYPE_PERFORMANCE: return "PERFORMANCE" case gl.DEBUG_TYPE_MARKER: return "MARKER" case gl.DEBUG_TYPE_OTHER: return "OTHER" case: return "UNKNOWN" } }(type) severity_str := proc(severity: u32) -> string { switch (severity) { case gl.DEBUG_SEVERITY_NOTIFICATION: return "NOTIFICATION" case gl.DEBUG_SEVERITY_LOW: return "LOW" case gl.DEBUG_SEVERITY_MEDIUM: return "MEDIUM" case gl.DEBUG_SEVERITY_HIGH: return "HIGH" case: return "UNKNOWN" } }(severity) fmt.printfln("[OpenGL:{}] severity {}:{} {}", source_str, severity_str, type_str, message) }