352 lines
12 KiB
Odin
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)
|
|
}
|