#include #include #include #include #include #ifndef _WIN32 # define APIENTRY #endif #include "glad/gl.h" #include "gl.c" #include "imgui.h" #include "imgui_impl_sdl3.h" #include "imgui_impl_opengl3.h" #define CGLTF_IMPLEMENTATION #include "cgltf.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define M3D_IMPLEMENTATION #include "m3d.h" #include "cglm/cglm.h" #include "SDL3/SDL.h" #include "SDL3/SDL_main.h" #include "SDL3/SDL_opengl.h" typedef int8_t i8 ; typedef int16_t i16; typedef int32_t i32; typedef int64_t i64; typedef uint8_t u8 ; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; typedef float f32; typedef double f64; typedef uintptr_t uintptr; typedef intptr_t intptr; typedef uint32_t b32; #ifdef __linux__ typedef ssize_t isize; typedef size_t usize; #endif #if _WIN64 typedef i64 isize; typedef u64 usize; #elif _WIN32 typedef i32 isize; typedef u32 usize; #endif typedef struct String { u8 *data; u64 length; } String; template struct Array { T *ptr; u64 length; T& operator[](u64 index) const { assert(index < length); return ptr[index]; } Array operator[](u64 start, u64 end) const { assert(start < length && end <= length); Array array; array.ptr = ptr+start; array.length = end-start; return array; } operator bool() const { assert(ptr == NULL || (ptr != NULL && length)); return ptr != NULL && length; } }; #define PrintVarArgs(...) __VA_ARGS__ #define _JoinArgs(x, y) x##y #define JoinArgs(x, y) _JoinArgs(x, y) #define Length(ptr) (sizeof(ptr)/sizeof(ptr[0])) #define ArrayLit(name, T, ...) \ std::array JoinArgs(array_lit_, __LINE__){PrintVarArgs(__VA_ARGS__)}; \ Array name = { .ptr = JoinArgs(array_lit_, __LINE__).data(), .length = JoinArgs(array_lit_, __LINE__).size() } struct String8 { union { u8 *ptr; char *ch_ptr; }; u64 length; bool operator==(const String8 &rhs) const { bool result = false; if(length == rhs.length) { result = strncmp((const char *)ptr, (const char *)(rhs.ptr), length) == 0; } return result; } u8& operator[](u64 index) const { assert(index < length); return ptr[index]; } String8 operator[](u64 start, u64 end) const { assert(start < length && end <= length); return (String8){ .ptr = ptr+start, .length = end-start }; } operator bool() const { assert(ptr == NULL || (ptr != NULL && length)); return ptr != NULL && length; } }; #define String8Lit(str) (String8){ .ptr = (u8 *)str, .length = sizeof(str) } #define String8(str) (String8){ .ptr = (u8 *)str, .length = strlen((const char *)str) } template union Vector2 { T v[2]; struct { T x, y; }; struct { T r, g; }; struct { T w, h; }; }; template union Vector3 { T v[3]; struct { T x, y, z; }; struct { T r, g, b; }; }; template union Vector4 { T v[4]; struct { T x, y, z, w; }; struct { T r, g, b, a; }; }; struct Vec2 { union { vec2 v; struct { f32 x, y; }; struct { f32 r, g; }; struct { f32 w, h; }; }; Vec2 operator-(const Vec2 &rhs) { return (Vec2){ .x = x-rhs.x, .y = y-rhs.y }; } }; struct Vec3 { union { vec3 v; struct { f32 x, y, z; }; struct { f32 r, g, b; }; }; void operator+=(const Vec3 &rhs) { x += rhs.x; y += rhs.y; z += rhs.z; } Vec3 operator+(const Vec3 &rhs) { return (Vec3){ .x = x+rhs.x, .y = y+rhs.y, .z = z+rhs.z }; } Vec3 operator/(const f32 rhs) { return (Vec3){ .x = x/rhs, .y = y/rhs, .z = z/rhs }; } Vec3 operator-(const Vec3 &rhs) { return (Vec3){ .x = x-rhs.x, .y = y-rhs.y, .z = z-rhs.z }; } Vec3 operator*(const Vec3 &rhs) { return (Vec3){ .x = x*rhs.x, .y = r*rhs.y, .z = z*rhs.z }; } Vec3 operator*(const f32 rhs) { return (Vec3){ .x = x*rhs, .y = y*rhs, .z = z*rhs }; } }; struct Vec4 { union { vec4 v; struct { f32 x, y, z, w; }; struct { f32 r, g, b, a; }; }; }; struct Mat4 { union { f32 v[4*4]; mat4 matrix; }; Mat4 operator*(Mat4 mat) { Mat4 result; glm_mat4_mul(matrix, mat.matrix, result.matrix); return result; } Vec4 operator*(Vec4 vec) { Vec4 result; glm_mat4_mulv(matrix, (f32 *)vec.v, result.v); return result; } f32 &operator[](u64 i, u64 j) { assert(i < 4 && j < 4); return v[(i*4) + j]; } }; struct Quat { union { f32 v[4]; versor quat; }; }; typedef Vector2 UVec2; typedef Vector2 IVec2; typedef Vector3 UVec3; typedef Vector3 IVec3; typedef Vector4 UVec4; typedef Vector4 IVec4; Vec4 MakeVec4(f32 x, f32 y, f32 z, f32 w) { return (Vec4){ .x = x, .y = y, .z = z, .w = w }; } Vec4 MakeVec4(f32 v) { return (Vec4){ .x = v, .y = v, .z = v, .w = v }; } Vec4 MakeVec4(Vec3 v, f32 w) { return (Vec4){ .x = v.x, .y = v.y, .z = v.z, .w = w }; } Vec3 MakeVec3(f32 x, f32 y, f32 z) { return (Vec3){ .x = x, .y = y, .z = z }; } Vec3 MakeVec3(f32 v) { return (Vec3){ .x = v, .y = v, .z = v }; } Vec3 MakeVec3(Vec2 v, f32 z) { return (Vec3){ .x = v.x, .y = v.y, .z = z }; } Vec3 MakeVec3(Vec4 v) { return (Vec3){ .x = v.x, .y = v.y, .z = v.z }; } Vec2 MakeVec2(f32 x, f32 y) { return (Vec2){ .x = x, .y = y }; } Vec2 MakeVec2(f32 v) { return (Vec2){ .x = v, .y = v }; } Mat4 MakeMat4(f32 matrix[16]) { Mat4 mat; memcpy(mat.v, matrix, sizeof(f32)*16); return mat; } #define Vec2(...) MakeVec2(__VA_ARGS__) #define Vec3(...) MakeVec3(__VA_ARGS__) #define Vec4(...) MakeVec4(__VA_ARGS__) #define Mat4(...) MakeMat4(__VA_ARGS__) typedef GLuint TextureID; typedef GLuint PipelineID; typedef GLuint ShaderID; typedef GLuint BufferID; enum PipelineFeature { PF_Albedo = 1<<0, PF_Normal = 1<<1, PF_Metallic = 1<<2, PF_Roughness = 1<<3, PF_Occlusion = 1<<4, PF_Emission = 1<<5, PF_Height = 1<<6, PF_Cubemap = 1<<7, PF_Irradiance = 1<<8, PF_Prefilter = 1<<9, PF_BRDF = 1<<10, }; const PipelineFeature PIPELINE_FEATURES[] = { PF_Albedo, PF_Normal, PF_Metallic, PF_Roughness, PF_Occlusion, PF_Emission, PF_Height, PF_Cubemap, PF_Irradiance, PF_Prefilter, PF_BRDF, }; struct ImageBuffer { Array data; u32 w, h, ch; }; struct ModelBuffers { BufferID vertex_array; BufferID vertex_buffer; BufferID index_buffer; }; enum MaterialMapIndex { MMI_Albedo, MMI_Normal, MMI_Metallic, MMI_Roughness, MMI_Occlusion, MMI_Emission, MMI_Height, MMI_Cubemap, MMI_Irradiance, MMI_Prefilter, MMI_BRDF, MMI_Max, }; #define AlignPow2(x, align) (((usize)(x) + align - 1) & ~(align - 1)) #define PtrSub(x, y) ((usize)(x) - (usize)(y)) #define PtrAdd(x, y) ((usize)(x) + (usize)(y)) #define MovePtrForward(ptr, x) (void *)AlignPow2(PtrAdd(ptr, x), DEFAULT_ALIGNMENT) #define Max(x, y) (x > y ? x : y) #define Min(x, y) (x < y ? x : y) TextureID CreateTexture(ImageBuffer image_buffer); template BufferID CreateBuffer(T *data, bool static_draw); ModelBuffers CreateModelBuffers(); struct Arena; struct Camera { Vec3 velocity; Vec3 position; f32 pitch; f32 yaw; }; struct ShaderGlobals { Mat4 projection; Mat4 view; Vec3 camera_position; Vec3 ambient; }; struct ShaderModelState { b32 albedo_texture; b32 normal_texture; b32 metallic_roughness_texture; b32 occlusion_texture; b32 emission_texture; b32 height_texture; b32 cubemap_texture; b32 irradiance_texture; b32 prefilter_texture; b32 brdf_texture; b32 no_material; }; struct ShaderInstanceState { Mat4 model_matrix; }; struct Renderer { SDL_Window* window; SDL_GLContext gl_context; Arena *arena; Camera camera; IVec2 resolution; PipelineID pipeline_id; u32 texture_locations[MMI_Max]; TextureID default_texture; BufferID default_material; BufferID globals_buffer_id; ShaderGlobals globals; BufferID default_material_buffer_id; BufferID default_shader_state_buffer_id; b32 exit; }; Renderer g_renderer = {}; #include "math.cpp" #include "alloc.cpp" #include "util.cpp" #include "assets.cpp" #define InitCheckError(cond, msg) \ if(cond) \ { \ error = msg; \ goto InitFailure; \ } Mat4 RotationMatrix(Camera &camera) { Quat pitch_rotation = AngleAxis(camera.pitch, Vec3(1.0f, 0.0f, 0.0f)); Quat yaw_rotation = AngleAxis(camera.yaw, Vec3(0.0f, -1.0f, 0.0f)); return ToMat4(yaw_rotation) * ToMat4(pitch_rotation); } Mat4 ViewMatrix(Camera &camera) { Mat4 camera_translation = Translate(IdentityMatrix(), camera.position); Mat4 camera_rotation = RotationMatrix(camera); return Inverse(camera_translation * camera_rotation); } void ProcessEvent(Camera &camera, SDL_Event &event) { switch(event.type) { case SDL_EVENT_KEY_DOWN: switch(event.key.scancode) { case SDL_SCANCODE_W: camera.velocity.z = -1; break; case SDL_SCANCODE_S: camera.velocity.z = +1; break; case SDL_SCANCODE_A: camera.velocity.x = -1; break; case SDL_SCANCODE_D: camera.velocity.x = +1; break; default: break; } break; case SDL_EVENT_KEY_UP: switch(event.key.scancode) { case SDL_SCANCODE_W: case SDL_SCANCODE_S: camera.velocity.z = 0; break; case SDL_SCANCODE_A: case SDL_SCANCODE_D: camera.velocity.x = 0; break; default: break; } break; case SDL_EVENT_MOUSE_MOTION: camera.yaw += (f32)(event.motion.xrel)/500.0f; camera.pitch -= (f32)(event.motion.yrel)/500.0f; break; } } void Update(Camera &camera) { Mat4 camera_rotation = RotationMatrix(camera); camera.position += Vec3(camera_rotation * Vec4(camera.velocity*0.5f, 0.0f)); } ModelBuffers CreateModelBuffers() { ModelBuffers model_buffers; glGenVertexArrays(1, &model_buffers.vertex_array); glGenBuffers(1, &model_buffers.vertex_buffer); glGenBuffers(1, &model_buffers.index_buffer); return model_buffers; } ShaderID CreateShader(GLenum type, Array src) { ShaderID shader_id = glCreateShader(type); if(shader_id) { GLint success; glShaderSource(shader_id, src.length, src.ptr, NULL); glCompileShader(shader_id); glGetShaderiv(shader_id, GL_COMPILE_STATUS, &success); if(!success) { GLint message_length; glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &message_length); if(message_length) { char message_buffer[512]; glGetShaderInfoLog(shader_id, message_length, NULL, message_buffer); printf("Error compiling %s shader: %s\n", type == GL_VERTEX_SHADER ? "vertex" : "fragment", message_buffer); } shader_id = 0; } } return shader_id; } PipelineID CreatePipeline(Array shader_src) { PipelineID pipeline_id; GLint success; const char *vertex_src[] = { "#version 460\n", "#define VERTEX_SHADER 1\n", (const char *)shader_src.ptr }; const char *fragment_src[] = { "#version 460\n", (const char *)shader_src.ptr }; Array vertex_src_array = { .ptr = vertex_src, .length = Length(vertex_src), }; Array fragment_src_array = { .ptr = fragment_src, .length = Length(fragment_src), }; ShaderID vertex_shader_id = CreateShader(GL_VERTEX_SHADER, vertex_src_array); ShaderID fragment_shader_id = CreateShader(GL_FRAGMENT_SHADER, fragment_src_array); if(vertex_shader_id && fragment_shader_id) { pipeline_id = glCreateProgram(); } if(pipeline_id) { glAttachShader(pipeline_id, vertex_shader_id); glAttachShader(pipeline_id, fragment_shader_id); glLinkProgram(pipeline_id); glGetProgramiv(pipeline_id, GL_LINK_STATUS, &success); if(!success) { GLint message_length; glGetProgramiv(pipeline_id, GL_INFO_LOG_LENGTH, &message_length); if(message_length) { char message_buffer[512]; glGetProgramInfoLog(pipeline_id, message_length, NULL, message_buffer); printf("Error linking program: %s\n", message_buffer); } pipeline_id = 0; } } glDeleteShader(vertex_shader_id); glDeleteShader(fragment_shader_id); return pipeline_id; } TextureID CreateTexture(ImageBuffer image_buffer) { TextureID texture_id; glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glGenTextures(1, &texture_id); glBindTexture(GL_TEXTURE_2D, texture_id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_buffer.w, image_buffer.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_buffer.data.ptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); return texture_id; } template void UpdateBuffer(BufferID target, T *data) { glBindBuffer(GL_UNIFORM_BUFFER, target); glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(T), data); glBindBuffer(GL_UNIFORM_BUFFER, 0); } template BufferID CreateBuffer(T *data, bool static_draw) { BufferID buffer; glGenBuffers(1, &buffer); glBindBuffer(GL_UNIFORM_BUFFER, buffer); glBufferData(GL_UNIFORM_BUFFER, sizeof(T), data, static_draw ? GL_STATIC_DRAW : GL_DYNAMIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); return buffer; } #ifdef BUILD_DEBUG void APIENTRY glDebugOutput(GLenum source, GLenum type, unsigned int id, GLenum severity, GLsizei length, const char *message, const void *userParam) { // ignore non-significant error/warning codes if(id == 131169 || id == 131185 || id == 131218 || id == 131204) return; printf("---------------\n"); b32 shader_failure = false; b32 error_level = false; switch (source) { case GL_DEBUG_SOURCE_API: printf("Source: API\n"); break; case GL_DEBUG_SOURCE_WINDOW_SYSTEM: printf("Source: Window System\n"); break; case GL_DEBUG_SOURCE_SHADER_COMPILER: printf("Source: Shader Compiler\n"); shader_failure = true; break; case GL_DEBUG_SOURCE_THIRD_PARTY: printf("Source: Third Party\n"); break; case GL_DEBUG_SOURCE_APPLICATION: printf("Source: Application\n"); break; case GL_DEBUG_SOURCE_OTHER: printf("Source: Other\n"); break; } switch (type) { case GL_DEBUG_TYPE_ERROR: printf("Type: Error\n"); error_level = true; break; case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: printf("Type: Deprecated Behaviour\n"); break; case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: printf("Type: Undefined Behaviour\n"); break; case GL_DEBUG_TYPE_PORTABILITY: printf("Type: Portability\n"); break; case GL_DEBUG_TYPE_PERFORMANCE: printf("Type: Performance\n"); break; case GL_DEBUG_TYPE_MARKER: printf("Type: Marker\n"); break; case GL_DEBUG_TYPE_PUSH_GROUP: printf("Type: Push Group\n"); break; case GL_DEBUG_TYPE_POP_GROUP: printf("Type: Pop Group\n"); break; case GL_DEBUG_TYPE_OTHER: printf("Type: Other\n"); break; } switch (severity) { case GL_DEBUG_SEVERITY_HIGH: printf("Severity: high\n\n"); break; case GL_DEBUG_SEVERITY_MEDIUM: printf("Severity: medium\n\n"); break; case GL_DEBUG_SEVERITY_LOW: printf("Severity: low\n\n"); break; case GL_DEBUG_SEVERITY_NOTIFICATION: printf("Severity: notification\n\n"); break; } printf("Debug message (%llu): %s\n", id, message); if(shader_failure && error_level) g_renderer.exit = true; } #endif bool Init(Renderer *renderer) { const char* error = NULL; BeginScratch(MB(1)); renderer->arena = CreateArena(MB(4)); SDL_SetAppMetadata("Renderer", "0.0", "com.sleepyday.renderer"); InitCheckError(!SDL_Init(SDL_INIT_VIDEO), SDL_GetError()); #ifdef BUILD_DEBUG SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); #else SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); #endif SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); { f32 main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; renderer->window = SDL_CreateWindow("Graphics Programming", (int)(1920*main_scale), (int)(1080*main_scale), window_flags); InitCheckError(!renderer->window, SDL_GetError()); } { renderer->gl_context = SDL_GL_CreateContext(renderer->window); InitCheckError(!renderer->gl_context, SDL_GetError()); } { int version = gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress); InitCheckError(!version, "Unable to load OpenGL Functions"); } #ifdef BUILD_DEBUG { glEnable(GL_DEBUG_OUTPUT); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback(glDebugOutput, NULL); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); } #endif { SDL_GL_MakeCurrent(renderer->window, renderer->gl_context); SDL_GL_SetSwapInterval(1); // Vsync SDL_SetWindowPosition(renderer->window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); SDL_ShowWindow(renderer->window); Array shader_src = OpenFile(String8Lit("./shaders/pbr.glsl")); if(shader_src) { renderer->pipeline_id = CreatePipeline(shader_src); Free(&shader_src); } InitCheckError(!renderer->pipeline_id, "Unable to create pipeline"); } { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO &io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; ImGui::StyleColorsDark(); f32 main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); ImGuiStyle &style = ImGui::GetStyle(); style.ScaleAllSizes(main_scale); style.FontScaleDpi = main_scale; ImGui_ImplSDL3_InitForOpenGL(renderer->window, renderer->gl_context); ImGui_ImplOpenGL3_Init("#version 130"); } { renderer->default_texture = CreateTexture(DEFAULT_TEXTURE); renderer->default_material = CreateBuffer(&DEFAULT_MATERIAL, true); } { for(u64 i = 0; i < MMI_Max; i += 1) { u8 buffer[64]; String8 texture_uniform = SPrintf(buffer, "u_texture%llu", i); renderer->texture_locations[i] = glGetUniformLocation(renderer->pipeline_id, texture_uniform.ch_ptr); } glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); renderer->globals.projection = IdentityMatrix(); renderer->globals.view = LookAt(Vec3(15.0f, 0.0f, 0.0f), Vec3(0.0f, 5.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f)); renderer->globals.ambient = Vec3(0.5); renderer->globals_buffer_id = CreateBuffer(&renderer->globals, false); ShaderModelState shader_model_state = { .no_material = true }; renderer->default_material_buffer_id = CreateBuffer(&DEFAULT_MATERIAL, true); renderer->default_shader_state_buffer_id = CreateBuffer(&shader_model_state, true); glBindBufferBase(GL_UNIFORM_BUFFER, 0, g_renderer.globals_buffer_id); } return true; InitFailure: printf("Failed to initialize: [%s]", error); return false; } void DrawModel(Model *model, BufferID model_matrix_id) { glBindVertexArray(model->buffers.vertex_array); for(u64 i = 0; i < model->meshes.length; i += 1) { if(model->materials.length) { Material *material = model->materials.ptr + model->meshes[i].material_index; for(u64 j = 0; j < MMI_Max; j += 1) { glActiveTexture(GL_TEXTURE0+j); glUniform1i(g_renderer.texture_locations[j], j); glBindTexture(GL_TEXTURE_2D, material->textures[j]); } glBindBufferBase(GL_UNIFORM_BUFFER, 1, material->shader_state_buffer_id); glBindBufferBase(GL_UNIFORM_BUFFER, 3, material->buffer_id); } else { glBindBufferBase(GL_UNIFORM_BUFFER, 1, g_renderer.default_shader_state_buffer_id); glBindBufferBase(GL_UNIFORM_BUFFER, 3, g_renderer.default_material_buffer_id); } glBindBufferBase(GL_UNIFORM_BUFFER, 2, model_matrix_id); glDrawElements(GL_TRIANGLES, model->meshes[i].index_length, GL_UNSIGNED_INT, (void *)(u64)(model->meshes[i].index_start*sizeof(u32))); glActiveTexture(GL_TEXTURE0); } glBindVertexArray(0); } int main(int argc, char** argv) { bool running = Init(&g_renderer); Model tree_model = {}; Model yoder = {}; if(running) { running &= LoadGLTF(g_renderer.arena, &tree_model, String8Lit("./assets/StylizedNature/glTF/CherryBlossom_1.gltf")); running &= LoadM3D(g_renderer.arena, &yoder, String8Lit("./assets/yoda.m3d")); if(!running) { Logf("Failed to open GLTF file"); } } Mat4 model_matrix = Translate(IdentityMatrix(), Vec3(0.0f, 0.0f, 0.0f)); BufferID model_matrix_buffer_id = CreateBuffer(&model_matrix, false); Mat4 yoder_matrix = Translate(IdentityMatrix(), Vec3(2.0f, 0.0f, 0.0f)); BufferID yoder_matrix_id = CreateBuffer(&yoder_matrix, false); while(running) { BeginScratch(); if(g_renderer.exit) { running = false; break; } SDL_Event event; while(SDL_PollEvent(&event)) { ImGui_ImplSDL3_ProcessEvent(&event); ProcessEvent(g_renderer.camera, event); if(event.type == SDL_EVENT_QUIT) { running = false; } if(event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(g_renderer.window)) { running = false; } } Update(g_renderer.camera); i32 w, h; SDL_GetWindowSize(g_renderer.window, &w, &h); if(g_renderer.resolution.w != w || g_renderer.resolution.h != h) { glViewport(0, 0, (int)w, (int)h); g_renderer.resolution.w = w; g_renderer.resolution.h = h; g_renderer.globals.projection = Perspective(Radians(90.0f), (f32)(w)/(f32)(h), 0.1f, 10000.0f); UpdateBuffer(g_renderer.globals_buffer_id, &g_renderer.globals); glBindBufferBase(GL_UNIFORM_BUFFER, 0, g_renderer.globals_buffer_id); } /* if(SDL_GetWindowFlags(renderer.window) && SDL_WINDOW_MINIMIZED) { SDL_Delay(10); continue; } */ ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL3_NewFrame(); ImGui::NewFrame(); ImGui::Begin("Hello, world!"); if(ImGui::Button("Yeah")) { Logf("yeah!"); } ImGui::End(); glClearColor(0.2, 0.3, 0.7, 1.0); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); static f32 time_passed = 0.0f; static f32 delta = 0.0f; const f32 radius = 15.0f; f32 new_time = (f32)SDL_GetTicks(); f32 d = (new_time - time_passed) / 100.0f; time_passed = new_time; delta += d; f32 cam_x = sin(delta * 0.1f) * radius; f32 cam_z = cos(delta * 0.1f) * radius; g_renderer.globals.camera_position = g_renderer.camera.position; g_renderer.globals.view = ViewMatrix(g_renderer.camera); UpdateBuffer(g_renderer.globals_buffer_id, &g_renderer.globals); glUseProgram(g_renderer.pipeline_id); DrawModel(&tree_model, model_matrix_buffer_id); DrawModel(&yoder, yoder_matrix_id); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); SDL_GL_SwapWindow(g_renderer.window); } }