Renderer/main.cpp

989 lines
23 KiB
C++

#include <cstdio>
#include <cstdint>
#include <cstring>
#include <cmath>
#include <array>
#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<typename T>
struct Array
{
T *ptr;
u64 length;
T& operator[](u64 index) const
{
assert(index < length);
return ptr[index];
}
Array<T> operator[](u64 start, u64 end) const
{
assert(start < length && end <= length);
Array<T> 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<T> 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<typename T>
union Vector2
{
T v[2];
struct { T x, y; };
struct { T r, g; };
struct { T w, h; };
};
template<typename T>
union Vector3
{
T v[3];
struct { T x, y, z; };
struct { T r, g, b; };
};
template <typename T>
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<u32> UVec2;
typedef Vector2<i32> IVec2;
typedef Vector3<u32> UVec3;
typedef Vector3<i32> IVec3;
typedef Vector4<u32> UVec4;
typedef Vector4<i32> 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<u8> 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<typename T> 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<const char *> 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<u8> 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<const char *> vertex_src_array = {
.ptr = vertex_src,
.length = Length(vertex_src),
};
Array<const char *> 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<typename T>
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<typename T>
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<u8> 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);
}
}