Renderer/main.cpp

768 lines
17 KiB
C++

#include <cstdio>
#include <cstdint>
#include <cstring>
#include <cmath>
#include <array>
#ifndef _WIN32
# define APIENTRY
#endif
#define CGLTF_IMPLEMENTATION
#include "cgltf.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define CGLM_FORCE_DEPTH_ZERO_TO_ONE
#include "cglm/cglm.h"
#include "glad/gl.h"
#include "gl.c"
#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; };
};
};
struct Vec3
{
union
{
vec3 v;
struct { f32 x, y, z; };
struct { f32 r, g, b; };
};
};
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;
};
Vec4 operator*(const 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];
}
};
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 }; }
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__)
Vec3
Transform(Vec3 &vec, Mat4 &mat, f32 last = 1.0)
{
Vec3 dst = {};
glm_mat4_mulv3(mat.matrix, vec.v, last, dst.v);
return dst;
}
typedef Vector2<u32> UVec2;
typedef Vector2<i32> IVec2;
typedef Vector3<u32> UVec3;
typedef Vector3<i32> IVec3;
typedef Vector4<u32> UVec4;
typedef Vector4<i32> IVec4;
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);
ModelBuffers CreateModelBuffers();
struct Arena;
struct ShaderGlobals
{
Mat4 projection;
Mat4 view;
Mat4 model_matrix;
};
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;
};
struct ShaderInstanceState
{
Mat4 model_matrix;
};
struct Renderer
{
SDL_Window* window;
SDL_GLContext gl_context;
Arena *arena;
IVec2 resolution;
PipelineID pipeline_id;
u32 texture_locations[MMI_Max];
TextureID default_texture;
BufferID default_material;
BufferID globals_buffer_id;
ShaderGlobals globals;
};
Renderer g_renderer;
#include "alloc.cpp"
#include "util.cpp"
#include "assets.cpp"
#define InitCheckError(cond, msg) \
if(cond) \
{ \
error = msg; \
goto InitFailure; \
}
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)
{
BufferID buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_UNIFORM_BUFFER, buffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(T), data, GL_STATIC_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");
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"); 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"); 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);
}
#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");
}
{
renderer->default_texture = CreateTexture(DEFAULT_TEXTURE);
renderer->default_material = CreateBuffer(&DEFAULT_MATERIAL);
}
{
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(0.0f, 5.0f, 0.0f), Vec3(0.0f, 30.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f));
renderer->globals.model_matrix = Translate(IdentityMatrix(), Vec3(0.0f, 0.0f, 20.0f));
renderer->globals_buffer_id = CreateBuffer(&renderer->globals);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, g_renderer.globals_buffer_id);
}
return true;
InitFailure:
printf("Failed to initialize: [%s]", error);
return false;
}
int
main(int argc, char** argv)
{
bool running = Init(&g_renderer);
Model tree_model = {};
if(running)
{
running = LoadGLTF(g_renderer.arena, &tree_model, String8Lit("./assets/StylizedNature/glTF/CherryBlossom_1.gltf"));
if(!running)
{
Logf("Failed to open GLTF file");
}
}
Mat4 model_matrix = IdentityMatrix();
BufferID model_matrix_buffer_id = CreateBuffer(&model_matrix);
while(running)
{
BeginScratch();
SDL_Event event;
while(SDL_PollEvent(&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;
}
}
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), 10000.0f, 0.1f);
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;
}
*/
glClearColor(0.2, 0.3, 0.7, 1.0);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glUseProgram(g_renderer.pipeline_id);
glBindVertexArray(tree_model.buffers.vertex_array);
for(u64 i = 0; i < tree_model.meshes.length; i += 1)
{
Material *material = tree_model.materials.ptr + tree_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, 2, model_matrix_buffer_id);
glBindBufferBase(GL_UNIFORM_BUFFER, 3, material->buffer_id);
glDrawElements(GL_TRIANGLES, tree_model.meshes[i].index_length, GL_UNSIGNED_INT, (void *)(u64)(tree_model.meshes[i].index_start*sizeof(u32)));
glActiveTexture(GL_TEXTURE0);
}
glBindVertexArray(0);
SDL_GL_SwapWindow(g_renderer.window);
}
}