989 lines
23 KiB
C++
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);
|
|
}
|
|
}
|