karts/main.odin
2025-04-25 11:20:58 +12:00

352 lines
12 KiB
Odin

#+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)
}