summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
author0scar <qgt268@alumni.ku.dk>2024-02-05 16:36:47 +0000
committer0scar <qgt268@alumni.ku.dk>2024-02-05 16:48:20 +0000
commite85f81ec109782a01f1f741d4c2ed5f21af0c124 (patch)
tree8708c742b296de20e2d54ef9fa8ea7c2036d3d82 /src/core
parent457a4a3b1f00bf4d6c31b693085aa7150d2dcded (diff)
Organize the sourcefiles
Diffstat (limited to 'src/core')
-rw-r--r--src/core/cleanup.c0
-rw-r--r--src/core/dltools.c57
-rw-r--r--src/core/harness.c0
-rw-r--r--src/core/init.c0
-rw-r--r--src/core/logging.c82
-rw-r--r--src/core/loop.c976
-rw-r--r--src/core/memory.c62
-rw-r--r--src/core/state.c178
8 files changed, 1355 insertions, 0 deletions
diff --git a/src/core/cleanup.c b/src/core/cleanup.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/core/cleanup.c
diff --git a/src/core/dltools.c b/src/core/dltools.c
new file mode 100644
index 0000000..1e43b79
--- /dev/null
+++ b/src/core/dltools.c
@@ -0,0 +1,57 @@
+#include <stdlib.h>
+
+#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+/* include winapi */
+#else
+#include <dlfcn.h>
+#endif
+
+#include <engine/dltools.h>
+#include <engine/logging.h>
+
+bool dynamic_library_close(void* shared_library) {
+#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+ return true;
+#else
+ return dlclose(shared_library) == 0;
+#endif
+}
+
+void* dynamic_library_open(const char* library_path) {
+#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+ return NULL;
+#else
+ return dlopen(library_path, RTLD_NOW);
+#endif
+}
+
+char* dynamic_library_get_error(void) {
+#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+ return "unsupported on windows";
+#else
+ return dlerror();
+#endif
+}
+
+void* dynamic_library_reload(void* shared_library, const char* library_path) {
+ void* library_address = NULL;
+ if (!dynamic_library_close(shared_library)) {
+ ERROR("Failed to close shared library: %s", dynamic_library_get_error());
+ ERROR("Reloading dynamic library failed.");
+ return library_address;
+ }
+ if ((library_address = dynamic_library_open(library_path)) == NULL) {
+ ERROR("Failed to open shared library: %s", dynamic_library_get_error());
+ ERROR("Reloading dynamic library %s failed.", library_path);
+ }
+ return library_address;
+}
+
+void* dynamic_library_get_symbol(void* restrict shared_library,
+ const char* symbol) {
+#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+ return NULL;
+#else
+ return dlsym(shared_library, symbol);
+#endif
+}
diff --git a/src/core/harness.c b/src/core/harness.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/core/harness.c
diff --git a/src/core/init.c b/src/core/init.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/core/init.c
diff --git a/src/core/logging.c b/src/core/logging.c
new file mode 100644
index 0000000..7870258
--- /dev/null
+++ b/src/core/logging.c
@@ -0,0 +1,82 @@
+#include <engine/logging.h>
+#include <engine/types.h>
+#include <stdlib.h>
+
+char* itoa(i32 x) {
+ const i32 size = (((i32)ceil(log10((f64)x))) + 1) * sizeof(char);
+ char* retval = (char*)malloc(size);
+ if (retval == NULL) {
+ perror("Failed to allocate memory for itoa");
+ exit(EXIT_FAILURE);
+ }
+ sprintf(retval, "%d", x);
+ return retval;
+}
+
+void _log(FILE* stream, const char* prefix, const char* fmt, va_list ap) {
+ if (stream == NULL) {
+ fprintf(stderr, "_log got NULL in stream argument\n");
+ exit(EXIT_FAILURE);
+ }
+ fputs(prefix, stream);
+ vfprintf(stream, fmt, ap);
+}
+
+void LOG(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ _log(stdout, "[" TERM_COLOR_BLUE "LOG" TERM_COLOR_RESET "] ", fmt, ap);
+ va_end(ap);
+ puts("");
+}
+
+void INFO_(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ _log(stdout, "[" TERM_COLOR_GREEN "INFO" TERM_COLOR_RESET "] ", fmt, ap);
+ va_end(ap);
+}
+
+void INFO(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ _log(stdout, "[" TERM_COLOR_GREEN "INFO" TERM_COLOR_RESET "] ", fmt, ap);
+ va_end(ap);
+ puts("");
+}
+
+void __DEBUG(const char* file, const i32 line, const char* func,
+ const char* fmt, ...) {
+ va_list ap;
+
+ const usize prefix_len = 1024;
+
+ char* prefix = malloc(sizeof(char) * 1024);
+
+ snprintf(prefix, prefix_len,
+ "[" TERM_COLOR_YELLOW "DEBUG" TERM_COLOR_RESET "] "
+ "%s:%d <%s> ",
+ file, line, func);
+
+ va_start(ap, fmt);
+ _log(stdout, prefix, fmt, ap);
+ va_end(ap);
+
+ free(prefix);
+}
+
+void WARN(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ _log(stderr, "[" TERM_COLOR_PURPLE "WARN" TERM_COLOR_RESET "] ", fmt, ap);
+ va_end(ap);
+ puts("");
+}
+
+void ERROR(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ _log(stderr, "[" TERM_COLOR_RED "ERROR" TERM_COLOR_RESET "] ", fmt, ap);
+ va_end(ap);
+ puts("");
+}
diff --git a/src/core/loop.c b/src/core/loop.c
new file mode 100644
index 0000000..79e0c0c
--- /dev/null
+++ b/src/core/loop.c
@@ -0,0 +1,976 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define GLAD_GL_IMPLEMENTATION
+#include <glad/gl.h>
+#undef GLAD_GL_IMPLEMENTATION
+
+#undef GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <cglm/cglm.h>
+
+#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+/* include winapi */
+#include <Windows.h>
+#elif defined(__APPLE__)
+/* mac includes */
+#elif defined(__linux) || defined(__linux__) || defined(linux)
+
+#include <unistd.h>
+#include <sys/sysinfo.h>
+
+#endif
+
+#define ENGINE_INTERNALS
+#include <engine/btree.h>
+#include <engine/engine.h>
+#include <engine/hashmap.h>
+#include <engine/list.h>
+
+#include <engine/state.h>
+// #include <states/titlescreen.h>
+// #include <states/gameover.h>
+
+#define DEFAULT_NUM_PROCS 8
+
+#ifdef BENCHMARK
+#define BENCHEXPR(timevar, expr) \
+ { \
+ f64 t = get_time(); \
+ expr timevar += get_time() - t; \
+ }
+
+extern i32 drawcall_len;
+
+#else
+#define BENCHEXPR(timevar, expr) expr
+#endif
+
+
+// http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/
+GLuint LoadShaders(const GladGLContext* gl, const char * vertex_file_path, const char * fragment_file_path){
+
+ // Create the shaders
+ GLuint VertexShaderID = gl->CreateShader(GL_VERTEX_SHADER);
+ GLuint FragmentShaderID = gl->CreateShader(GL_FRAGMENT_SHADER);
+
+ // Read the Vertex Shader code from the file
+ char* VertexShaderCode;
+ FILE* VertexShaderStream = fopen(vertex_file_path, "r");
+ if(VertexShaderStream != NULL){
+ fseek(VertexShaderStream, 0, SEEK_END);
+ const i64 size = ftell(VertexShaderStream);
+ rewind(VertexShaderStream);
+ VertexShaderCode = calloc(size + 1, sizeof(char));
+
+ // Assume the whole file is successfully read
+ fread(VertexShaderCode, sizeof(char), size, VertexShaderStream);
+ //LOG("vertex source is %d bytes\n%s\n", size, VertexShaderCode);
+
+ fclose(VertexShaderStream);
+ } else {
+ ERROR("Impossible to open %s. Are you in the right directory?\n", vertex_file_path);
+ getchar();
+ return 0;
+ }
+
+ // Read the Fragment Shader code from the file
+ char* FragmentShaderCode;
+ FILE* FragmentShaderStream = fopen(fragment_file_path, "r");
+ if(FragmentShaderStream != NULL){
+ fseek(FragmentShaderStream, 0, SEEK_END);
+ const i64 size = ftell(FragmentShaderStream);
+ rewind(FragmentShaderStream);
+ FragmentShaderCode = calloc(size + 1, sizeof(char));
+
+ // Assume the whole file is successfully read
+ fread(FragmentShaderCode, sizeof(char), size, FragmentShaderStream);
+ LOG("fragment source is %d bytes", size);
+
+ fclose(FragmentShaderStream);
+ } else {
+ ERROR("Impossible to open %s. Are you in the right directory?\n", fragment_file_path);
+ getchar();
+ return 0;
+ }
+
+ GLint Result = GL_FALSE;
+ int InfoLogLength;
+
+ // Compile Vertex Shader
+ INFO("Compiling shader: %s\n", vertex_file_path);
+ char const * VertexSourcePointer = VertexShaderCode;
+ gl->ShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
+ gl->CompileShader(VertexShaderID);
+
+ // Check Vertex Shader
+ gl->GetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
+ gl->GetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
+ if ( InfoLogLength > 0 ) {
+ char* msg = calloc(InfoLogLength + 1, sizeof(char));
+ gl->GetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, msg);
+ ERROR("Compiling shader: %s\n", msg);
+ free(msg);
+ }
+
+ // Compile Fragment Shader
+ INFO("Compiling shader: %s\n", fragment_file_path);
+ char const * FragmentSourcePointer = FragmentShaderCode;
+ gl->ShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
+ gl->CompileShader(FragmentShaderID);
+
+ // Check Fragment Shader
+ gl->GetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
+ gl->GetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
+ if ( InfoLogLength > 0 ){
+ char* msg = calloc(InfoLogLength + 1, sizeof(char));
+ gl->GetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, msg);
+ ERROR("Compiling shader: %s\n", msg);
+ free(msg);
+ }
+
+ // Link the program
+ INFO("Linking program\n");
+ GLuint ProgramID = gl->CreateProgram();
+ gl->AttachShader(ProgramID, VertexShaderID);
+ gl->AttachShader(ProgramID, FragmentShaderID);
+ gl->LinkProgram(ProgramID);
+
+ // Check the program
+ gl->GetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
+ gl->GetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
+ if ( InfoLogLength > 0 ){
+ char* msg = calloc(InfoLogLength + 1, sizeof(char));
+ gl->GetShaderInfoLog(ProgramID, InfoLogLength, NULL, msg);
+ ERROR("Compiling shader: %s\n", msg);
+ free(msg);
+ }
+
+ gl->DetachShader(ProgramID, VertexShaderID);
+ gl->DetachShader(ProgramID, FragmentShaderID);
+
+ gl->DeleteShader(VertexShaderID);
+ gl->DeleteShader(FragmentShaderID);
+
+
+ free(VertexShaderCode);
+ free(FragmentShaderCode);
+ return ProgramID;
+}
+
+
+
+
+
+
+
+
+
+
+
+static u64 FPS_CAP = 50;
+Platform* GLOBAL_PLATFORM = NULL;
+
+input_callback_t* callbacks[128];
+usize callbacks_len;
+
+i32 nproc(void) {
+ return get_nprocs();
+}
+
+void delay( uint32_t ms )
+{
+#ifdef _WIN32
+ Sleep( ms );
+#else
+ usleep( ms * 1000 );
+#endif
+}
+
+i32 cmp_int(const void* a, const void* b) {
+ const i32* x = a;
+ const i32* y = b;
+
+ return *x - *y;
+}
+
+v2_i32 get_canvas_size(void* window) {
+ v2_i32 realsize;
+ glfwGetWindowSize(window, &(realsize.x), &(realsize.y));
+
+ /* Set logical render size */
+ return realsize;
+}
+
+Texture* load_texture(void* render, const Asset_TextureSpec* ts) {
+ Texture* t = NULL;
+
+ if (ts == NULL) {
+ ERROR("Invalid Asset_TextureSpec\n");
+ return NULL;
+ }
+
+ //t = (Texture*)malloc(sizeof(Texture));
+ //t->texture = new_texture;
+ ///* Assigning const value */
+ //*(i32*)&t->tilesize = tw;
+ //*(i32*)&t->width = ts->width;
+ //*(i32*)&t->height = ts->height;
+
+ return t;
+}
+
+void engine_update_window(Window* w, void* e) {
+ switch ((i32)e) {
+ default:
+ WARN("Unhandled window event 0x%04x", (i32)e);
+ break;
+ }
+ return;
+}
+
+struct glfw_ctx {
+ GLFWwindow* w;
+ GladGLContext* c;
+} glfw_ctx;
+
+/* GLFW And vulkan spaghetti boiler */
+void glfw_err_callback(int code, const char* description) {
+ ERROR("glfw [%d]: %s\n", code, description);
+ // Terminate?
+ exit(EXIT_FAILURE);
+}
+
+struct QueueFamilyIndices {
+ int64_t graphicsFamily;
+ int64_t presentFamily;
+};
+
+GladGLContext* create_context(GLFWwindow *window) {
+ glfwMakeContextCurrent(window);
+
+ GladGLContext* context = (GladGLContext*) calloc(1, sizeof(GladGLContext));
+ if (!context) return NULL;
+
+ int version = gladLoadGLContext(context, glfwGetProcAddress);
+ INFO("Loaded OpenGL %d.%d", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));
+
+ return context;
+}
+
+struct glfw_ctx initialize_GLFW(
+ const char* windowtitle, v2_i32 windowsize,
+ const u32 flags
+ ) {
+ GLFWwindow* window = NULL;
+
+ glfwSetErrorCallback(&glfw_err_callback);
+
+ INFO_("initializing glfw...");
+ if (glfwInit() == GLFW_FALSE) {
+ const char *desc;
+ int code = glfwGetError(&desc);
+ ERROR("failed to initialize glfw [%d]: %s\n", code, *desc);
+ exit(EXIT_FAILURE);
+ } else
+ printf("ok\n");
+
+
+ INFO_("initializing window...");
+ //glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+ glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
+
+ glfwWindowHint(GLFW_SAMPLES, 0); // Disable anti aliasing
+
+ // Use a modern opengl version
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
+
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+#ifdef __APPLE__
+ // To make MacOS happy; should not be needed
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+#endif
+
+
+ /* "On Wayland specifically, you need to swap the buffers
+ * of a window for it to become visible." */
+ window = glfwCreateWindow(windowsize.x, windowsize.y, windowtitle, NULL, NULL);
+ if (window == NULL) {
+ ERROR("Failed to create GLFW window!\n");
+ const char *desc;
+ int code = glfwGetError(&desc);
+ ERROR("failed to initialize glfw window [%d]: %s\n", code, desc);
+ exit(EXIT_FAILURE);
+ } else
+ printf("ok\n");
+
+ //glfwMakeContextCurrent(window);
+
+ // Remember to load GL :) (hours wasted because i forgot: approx 4)
+ GladGLContext *ctx = create_context(window);
+ //printf("GL %d.%d\n", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));
+
+ //ctx->Viewport(0, 0, 200, 200);
+
+ if (ctx == NULL) {
+ ERROR("Failed to create glad context");
+ exit(EXIT_FAILURE);
+ }
+
+ glfwSetFramebufferSizeCallback(window, window_size_callback);
+ glfwSwapInterval(0);
+
+#ifdef _DEBUG
+ ctx->ClearColor((float)0x10 / 255.f, (float)0x0a / 255.f, (float)0x33 / 255.f, 0.f);
+#else
+ ctx->ClearColor(0x0, 0x0, 0x0, 0.f);
+#endif
+
+ ctx->Enable(GL_DEPTH_TEST);
+ ctx->DepthFunc(GL_LESS);
+
+ return (struct glfw_ctx){.w = window, .c = ctx};
+}
+
+
+/* Creates the window, initializes IO, Rendering, Fonts and engine-specific
+ * resources. */
+Platform* engine_init(const char* windowtitle, v2_i32 windowsize,
+ const f32 render_scale, const u32 flags,
+ const usize initial_memory, const Asset_FontSpec* fonts[],
+ const Asset_TextureSpec* textures[]) {
+
+#ifdef BENCHMARK
+ f64 init_start = get_time();
+#endif
+
+#if defined(__linux) || defined(__linux__) || defined(linux)
+ {
+ pid_t pid = getpid();
+ INFO("Starting with pid %lu", pid);
+ }
+#endif
+
+ Platform* p = (Platform*)malloc(sizeof(Platform));
+ Window* w = (Window*)malloc(sizeof(Window));
+
+ /* initialize resources */
+ struct Resources* resources =
+ (struct Resources*)malloc(sizeof(struct Resources));
+ resources->textures_len = 0;
+ resources->fonts_len = 0;
+ resources->texturepaths_len = 0;
+ resources->fontpaths_len = 0;
+ resources->texture_paths = NULL;
+ resources->font_paths = NULL;
+ resources->textures = NULL;
+ //resources->fonts = NULL;
+
+ {
+ struct glfw_ctx ctx = initialize_GLFW(windowtitle, windowsize, flags);
+ w->window = ctx.w;
+ w->context = ctx.c;
+ }
+
+ const GladGLContext *gl = w->context;
+
+ struct RenderObject *testobject = malloc(sizeof(struct RenderObject));
+
+ gl->GenVertexArrays(1, &(testobject->vao));
+ gl->BindVertexArray(testobject->vao);
+
+ p->testobject = testobject;
+
+ testobject->g_vertex_buffer_data[0] = -1.0f;
+ testobject->g_vertex_buffer_data[1] = -1.0f;
+ testobject->g_vertex_buffer_data[2] = 0.0f;
+
+ testobject->g_vertex_buffer_data[3] = 1.0f;
+ testobject->g_vertex_buffer_data[4] = -1.0f;
+ testobject->g_vertex_buffer_data[5] = 0.0f;
+
+ testobject->g_vertex_buffer_data[6] = 0.0f;
+ testobject->g_vertex_buffer_data[7] = 1.0f;
+ testobject->g_vertex_buffer_data[8] = 0.0f;
+
+ static const float bufdata[] = {
+ -1.0f,-1.0f,-1.0f, // triangle 1 : begin
+ -1.0f,-1.0f, 1.0f,
+ -1.0f, 1.0f, 1.0f, // triangle 1 : end
+ 1.0f, 1.0f,-1.0f, // triangle 2 : begin
+ -1.0f,-1.0f,-1.0f,
+ -1.0f, 1.0f,-1.0f, // triangle 2 : end
+ 1.0f,-1.0f, 1.0f,
+ -1.0f,-1.0f,-1.0f,
+ 1.0f,-1.0f,-1.0f,
+ 1.0f, 1.0f,-1.0f,
+ 1.0f,-1.0f,-1.0f,
+ -1.0f,-1.0f,-1.0f,
+ -1.0f,-1.0f,-1.0f,
+ -1.0f, 1.0f, 1.0f,
+ -1.0f, 1.0f,-1.0f,
+ 1.0f,-1.0f, 1.0f,
+ -1.0f,-1.0f, 1.0f,
+ -1.0f,-1.0f,-1.0f,
+ -1.0f, 1.0f, 1.0f,
+ -1.0f,-1.0f, 1.0f,
+ 1.0f,-1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+ 1.0f,-1.0f,-1.0f,
+ 1.0f, 1.0f,-1.0f,
+ 1.0f,-1.0f,-1.0f,
+ 1.0f, 1.0f, 1.0f,
+ 1.0f,-1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f,-1.0f,
+ -1.0f, 1.0f,-1.0f,
+ 1.0f, 1.0f, 1.0f,
+ -1.0f, 1.0f,-1.0f,
+ -1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+ -1.0f, 1.0f, 1.0f,
+ 1.0f,-1.0f, 1.0f
+ };
+
+ static const GLfloat g_color_buffer_data[] = {
+ 0.583f, 0.771f, 0.014f,
+ 0.609f, 0.115f, 0.436f,
+ 0.327f, 0.483f, 0.844f,
+ 0.822f, 0.569f, 0.201f,
+ 0.435f, 0.602f, 0.223f,
+ 0.310f, 0.747f, 0.185f,
+ 0.597f, 0.770f, 0.761f,
+ 0.559f, 0.436f, 0.730f,
+ 0.359f, 0.583f, 0.152f,
+ 0.483f, 0.596f, 0.789f,
+ 0.559f, 0.861f, 0.639f,
+ 0.195f, 0.548f, 0.859f,
+ 0.014f, 0.184f, 0.576f,
+ 0.771f, 0.328f, 0.970f,
+ 0.406f, 0.615f, 0.116f,
+ 0.676f, 0.977f, 0.133f,
+ 0.971f, 0.572f, 0.833f,
+ 0.140f, 0.616f, 0.489f,
+ 0.997f, 0.513f, 0.064f,
+ 0.945f, 0.719f, 0.592f,
+ 0.543f, 0.021f, 0.978f,
+ 0.279f, 0.317f, 0.505f,
+ 0.167f, 0.620f, 0.077f,
+ 0.347f, 0.857f, 0.137f,
+ 0.055f, 0.953f, 0.042f,
+ 0.714f, 0.505f, 0.345f,
+ 0.783f, 0.290f, 0.734f,
+ 0.722f, 0.645f, 0.174f,
+ 0.302f, 0.455f, 0.848f,
+ 0.225f, 0.587f, 0.040f,
+ 0.517f, 0.713f, 0.338f,
+ 0.053f, 0.959f, 0.120f,
+ 0.393f, 0.621f, 0.362f,
+ 0.673f, 0.211f, 0.457f,
+ 0.820f, 0.883f, 0.371f,
+ 0.982f, 0.099f, 0.879f
+};
+
+
+// LOG("sizeof(bufdata) = %lu", sizeof(bufdata));
+// LOG("sizeof(g_vertex_buffer_data) = %lu", sizeof(testobject->g_vertex_buffer_data));
+//
+
+ // Generate 1 buffer, put the resulting identifier in vertexbuffer
+ gl->GenBuffers(1, &(testobject->vbo));
+ // The following commands will talk about our 'vertexbuffer' buffer
+ gl->BindBuffer(GL_ARRAY_BUFFER, testobject->vbo);
+ // Give our vertices to OpenGL.
+ gl->BufferData(GL_ARRAY_BUFFER, sizeof(bufdata), bufdata, GL_STATIC_DRAW);
+
+ // Same for the color buffer
+ gl->GenBuffers(1, &(testobject->col));
+ gl->BindBuffer(GL_ARRAY_BUFFER, testobject->col);
+ gl->BufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);
+
+ testobject->shaderprogram = LoadShaders(gl, "shader.vertexshader", "shader.fragmentshader");
+ INFO("Shaderprogram %d", testobject->shaderprogram);
+
+ { /* Resource loading */
+
+ /* Count resources */
+ usize n_textures = 0;
+ usize n_fonts = 0;
+
+ if (textures != NULL)
+ while (textures[n_textures] != NULL) n_textures++;
+ if (fonts != NULL)
+ while (fonts[n_fonts] != NULL) n_fonts++;
+
+ INFO("Number of textures: " TERM_COLOR_YELLOW "%d" TERM_COLOR_RESET,
+ n_textures);
+ INFO("Number of fonts: " TERM_COLOR_YELLOW "%d" TERM_COLOR_RESET, n_fonts);
+
+ /* Save the textures and fonts, if we should need to reload them later */
+ resources->texture_paths = (Asset_TextureSpec**)textures;
+ resources->font_paths = (Asset_FontSpec**)fonts;
+
+ /* Allocate memory for textures and fonts */
+ resources->textures = (Texture**)malloc(sizeof(Texture*) * n_textures);
+ //resources->fonts = (TTF_Font**)malloc(sizeof(TTF_Font*) * n_fonts);
+ resources->textures_size = n_textures;
+
+ for (usize i = 0; i < n_textures; i++) resources->textures[i] = NULL;
+ //for (usize i = 0; i < n_fonts; i++) resources->fonts[i] = NULL;
+
+ /* Load textures */
+ for (usize i = 0; i < n_textures; i++) {
+ Texture* t = NULL;
+ INFO_("loading texture \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET
+ "\"...",
+ textures[i]->path);
+
+// t = load_texture(renderer, textures[i]);
+// if (t == NULL) {
+// puts("");
+// ERROR("failed to load texture\n");
+// exit(EXIT_FAILURE);
+// }
+//
+// if (t->tilesize < 8) {
+// puts("");
+// ERROR("texture too small!\n");
+// exit(EXIT_FAILURE);
+// }
+//
+ //if (t->texture == NULL) {
+ // puts("");
+ // ERROR("failed to load texture\n");
+ //} else {
+ // printf("ok\n");
+ // resources->textures[i] = t;
+ // resources->textures_len++;
+ //}
+ }
+
+ /* Load fonts */
+ for (usize i = 0; i < n_fonts; i++) {
+ INFO_("loading font \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET "\"...",
+ fonts[i]->font_path);
+
+ //TTF_Font* font = TTF_OpenFont(fonts[i]->font_path, fonts[i]->ptsize);
+ //if (!font) {
+ // ERROR("failed to load font: %s\n", TTF_GetError());
+ //} else {
+ // printf("ok\n");
+ // resources->fonts[i] = font;
+ // resources->fonts_len++;
+ //}
+ }
+
+ if (resources->textures_len != n_textures) {
+ WARN("Done. %d/%d textures loaded.", resources->textures_len, n_textures);
+ } else {
+ INFO("Done. All %d textures loaded.", n_textures);
+ }
+
+ if (resources->fonts_len != n_fonts) {
+ WARN("Done. %d/%d fonts loaded.", resources->fonts_len, n_fonts);
+ } else {
+ INFO("Done. All %d fonts loaded.", n_fonts);
+ }
+
+ resources->texturepaths_len = resources->textures_len;
+ resources->fontpaths_len = resources->fonts_len;
+ }
+
+ { /* Adjust window and such */
+ /* Set actual windowsize, which might be forced by OS */
+ INFO("Adjusting window size...");
+ //windowsize = get_canvas_size(renderer);
+
+ INFO("Windowsize: <%d,%d>", windowsize.x, windowsize.y);
+ }
+
+ //w->renderer = renderer;
+ //w->window = window;
+ w->render_scale = render_scale;
+ w->windowsize = windowsize;
+ w->game_w = NULL;
+ w->game_h = NULL;
+
+ p->data = (void*)resources;
+ p->data_len = sizeof(struct Resources);
+ p->window = w;
+ p->quit = false;
+
+ p->frame = 0;
+ p->fps_target = 60;
+
+ p->mem = memory_new(initial_memory);
+
+ /* Getting the mouse coords now resolves the issue where a click "isn't
+ * registered" when the mouse isn't moved before the user clicks */
+
+ p->mousedown = (v2_i32){-1, -1};
+ p->mouseup = (v2_i32){-1, -1};
+
+ p->mouse_lclick = false;
+ p->mouse_rclick = false;
+
+ p->camera_x = 0;
+ p->camera_y = 0;
+
+ p->edit_text = NULL;
+ p->edit_pos = 0;
+
+ p->bindings = NULL;
+ p->bindings_sz = 0;
+ p->bindings_len = 0;
+
+ // TODO: Add global bindings
+
+#ifdef BENCHMARK
+ f64 init_stop = get_time();
+ INFO("Initialization took %dms", init_stop - init_start);
+#endif
+
+ INFO("Available cores: %d", nproc());
+
+ GLOBAL_PLATFORM = p;
+
+#ifdef DAW_BUILD_HOTRELOAD
+
+#define State(name) \
+ if (!State_reload(STATE_##name, p->bindings, p->bindings_len)) { \
+ ERROR("Failed to reload shared object file for state %s", #name); \
+ };
+
+#include <states/list_of_states.h>
+#undef State
+
+#endif
+
+ return p;
+}
+
+i32 engine_run(Platform* p, StateType initial_state) {
+ if (p == NULL) {
+ ERROR("Platform is uninitialized.\n");
+ INFO("initialize with `engine_init`");
+ return -1;
+ }
+
+ memory* mem = p->mem;
+
+ StateType state = initial_state;
+
+ {
+ f64 state_init_time = get_time();
+ State_init(state, mem);
+ INFO("Initializing state \"%s\" took %ldms", StateTypeStr[state],
+ get_time() - state_init_time);
+ }
+
+ f64 time = get_time();
+
+ // Update ticks
+ u64 ticks = 0;
+
+ /* Profiling values */
+#ifdef BENCHMARK
+ u64 profile_tick_counter = 0;
+ // u64 profile_slack = 0;
+ u64 profile_rendering = 0;
+ u64 profile_gameloop = 0;
+ u64 profile_input = 0;
+ u64 profile_input_handling = 0;
+ u64 profile_num_drawcalls = 0;
+ u32 profile_interval_timer = time;
+ const u32 profile_interval_ms = 5000;
+ const f32 profile_interval_scale = (f32)(profile_interval_ms) / 100.0f;
+#endif
+
+ const f64 frame_interval = 1000.0 / FPS_CAP;
+
+ StateType (*update_func)(void*) = State_updateFunc(state);
+
+ f64 last_fps_measurement = get_time();
+
+ /* Main loop */
+ INFO("Program: %d", p->testobject->shaderprogram);
+ GladGLContext *gl = p->window->context;
+ do {
+ const f64 now = get_time();
+ const f64 dt = now - time;
+ time = now;
+ /* Wait frame_interval */
+ if (dt < frame_interval) {
+#ifndef BENCHMARK
+ //delay(frame_interval - dt);
+
+#else
+ /* We want to know how much time is spend sleeping */
+ // profile_slack += frame_interval - dt;
+#endif
+ }
+
+ if (now - last_fps_measurement > 1.000) {
+ last_fps_measurement = now;
+ printf("\n FPS: %.1f \t ticks: %lu", (double)ticks / now, ticks);
+ }
+
+#ifdef BENCHMARK
+ if (time - profile_interval_timer > profile_interval_ms) {
+ /* Ticks/frames since last measurement */
+ u32 fps = (ticks - profile_tick_counter) / profile_interval_scale;
+ u64 drawcalls = profile_num_drawcalls / profile_interval_scale / fps;
+
+ u32 sum = +profile_rendering
+ //+ profile_slack
+ + profile_input + profile_input_handling + profile_gameloop;
+
+ /* Log fps and slack percentage */
+ LOG("fps:%d\t"
+ "rendering:%.2f%%\t"
+ //"slack:%.2f%%\t"
+ "input:%.2f%% (%.2f%%)\t"
+ "gameloop:%.2f%%\t"
+ "unaccounted:%llu / %llu ms\t"
+ "avg drawcalls:%llu",
+ fps, 100.0f * (f32)profile_rendering / (f32)sum,
+ // 100.0f * (f32)profile_slack / (f32)sum,
+ 100.0f * (f32)profile_input / (f32)sum,
+ 100.0f * (f32)profile_input_handling / (f32)sum,
+ 100.0f * (f32)profile_gameloop / (f32)sum,
+ time - profile_interval_timer - sum, sum, drawcalls);
+ /* Reset values */
+ profile_tick_counter = ticks;
+ profile_interval_timer = time;
+ // profile_slack = 0;
+ profile_rendering = 0;
+ profile_gameloop = 0;
+ profile_input = 0;
+ profile_input_handling = 0;
+ profile_num_drawcalls = 0;
+ }
+#endif
+
+ glfwPollEvents();
+ /* Events */
+// if (p->mouse_lclick) {
+// p->mouseup.x = -1;
+// p->mouseup.y = -1;
+// p->mousedown.x = -1;
+// p->mousedown.y = -1;
+// p->mouse_lclick = false;
+// }
+// if (p->mouse_rclick) {
+// p->mouse_rclick = false;
+// }
+//
+// /* Window events */
+// i32 num_events;
+//
+// /* Mouse events */
+//
+// if (p->bindings != NULL) {
+// const i_ctx* bindings = *p->bindings;
+// const usize bindings_len = p->bindings_len;
+//
+// }
+//
+// i_flush_bindings(callbacks_len, mem->data, callbacks);
+// callbacks_len = 0;
+//
+// /* update */
+// StateType next_state;
+// next_state = update_func((void*)(mem->data));
+//
+// if (next_state != STATE_null) {
+// if (next_state == STATE_quit) break;
+//
+// drawcall_reset();
+//
+// engine_window_resize_pointers_reset();
+// State_free(state, mem);
+// memory_clear(mem);
+//
+// engine_input_ctx_reset();
+//
+// state = next_state;
+// update_func = State_updateFunc(state);
+//#ifdef BENCHMARK
+// {
+// f64 t = get_time();
+// State_init(state, mem);
+// LOG("Initializing %s took %dms", StateTypeStr[state],
+// (int)((get_time() - t) * 1000.0));
+// }
+//#else
+// State_init(state, mem);
+//#endif
+// } else {
+//#ifdef BENCHMARK
+// profile_num_drawcalls += drawcall_len;
+//#endif
+ render_begin(p->window);
+
+
+ gl->UseProgram(p->testobject->shaderprogram);
+
+ {
+ vec3 cam = {4., 3., 3.}; // perspective
+ mat4 per; // perspective
+ mat4 v; // view
+ mat4 model = GLM_MAT4_IDENTITY_INIT;
+ mat4 modelviewprojection;
+
+ f32 ratio = (float)p->window->windowsize.x / (float)p->window->windowsize.y;
+ //glm_perspective(45.f , 600.f / 400.f, 0.1, 100.0f, per);
+ glm_ortho(-10 * ratio, 10 * ratio, -10, 10, -10, 10, per);
+
+ glm_lookat(cam, GLM_VEC3_ZERO, GLM_YUP, v);
+
+ { mat4 t;
+ //modelviewprojection = p * v * model
+ glm_mat4_mul(v, model, t);
+ glm_rotate_at(t, (vec3){0,0,0}, get_time() / 2.f, (vec3){0,1,0}); //, (vec3)({0,1,0}));
+ glm_mat4_mul(per, t, modelviewprojection);
+ }
+
+ // TODO: Do this only once during initialization
+ u32 matrix = gl->GetUniformLocation(p->testobject->shaderprogram, "MVP");
+
+ gl->UniformMatrix4fv(matrix, 1, GL_FALSE, &modelviewprojection[0][0]);
+ }
+
+
+ gl->EnableVertexAttribArray(0);
+ gl->BindBuffer(GL_ARRAY_BUFFER, p->testobject->vbo);
+ gl->VertexAttribPointer(
+ 0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
+ 3, // size
+ GL_FLOAT, // type
+ GL_FALSE, // normalized?
+ 0, // stride
+ (void*)0 // array buffer offset
+ );
+
+ // Do the color buffer (?)
+ gl->EnableVertexAttribArray(1);
+ gl->BindBuffer(GL_ARRAY_BUFFER, p->testobject->col);
+ gl->VertexAttribPointer(
+ 1, // attribute. No particular reason for 1, but must match the layout in the shader.
+ 3, // size
+ GL_FLOAT, // type
+ GL_FALSE, // normalized?
+ 0, // stride
+ (void*)0 // array buffer offset
+ );
+
+
+
+
+ gl->UseProgram(p->testobject->shaderprogram);
+ // Draw the triangle !
+ gl->DrawArrays(GL_TRIANGLES, 0, 3*12); // Starting from vertex 0; 3 vertices total -> 1 triangle
+
+ gl->DisableVertexAttribArray(0);
+ gl->DisableVertexAttribArray(1);
+
+ render_present(p->window);
+// }
+
+ ticks++;
+ } while(
+ !glfwWindowShouldClose(p->window->window)
+ && state != STATE_quit);
+
+ return 0;
+}
+
+void stop(Platform* p) {
+ if (p == NULL) return;
+
+ { /* Deallocate resources */
+ struct Resources* r = (struct Resources*)p->data;
+ if (r != NULL) {
+ /* Destroy textures */
+ for (usize i = 0; i < r->textures_len; i++) {
+ if (r->textures[i] != NULL) {
+ r->textures[i] = NULL;
+ }
+ }
+ free(r->textures);
+
+ /* Destroy Fonts */
+ }
+ }
+
+ glfwDestroyWindow(p->window->window);
+ glfwTerminate();
+
+}
+
+/* Set the maximum framerate */
+void engine_fps_max(u64 cap) { FPS_CAP = cap; }
+
+/* Pushes an input context onto the input handling stack */
+void engine_input_ctx_push(i_ctx* ctx) {
+ if (GLOBAL_PLATFORM->bindings == NULL) {
+ GLOBAL_PLATFORM->bindings = calloc(8, sizeof(i_ctx*));
+ GLOBAL_PLATFORM->bindings_sz = 8;
+ }
+
+ if (GLOBAL_PLATFORM->bindings_len + 1 >= GLOBAL_PLATFORM->bindings_sz) {
+ void* m =
+ realloc(GLOBAL_PLATFORM->bindings, GLOBAL_PLATFORM->bindings_sz + 8);
+ if (m == NULL) {
+ ERROR("Failed to allocate 8 bytes (%d): %s", errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ GLOBAL_PLATFORM->bindings_sz += 8;
+ }
+
+ LOG("Bindings in ctx[%d]:", GLOBAL_PLATFORM->bindings_len);
+ for (isize i = 0; i < ctx->len; i++) {
+ switch (ctx->bindings[i].action.type) {
+ case InputType_error:
+ LOG("(error)");
+ break;
+
+ case InputType_action:
+ LOG("(action) %s", ctx->bindings[i].action.action.callback_str);
+ break;
+
+ case InputType_state:
+ LOG("(+state) %s", ctx->bindings[i].action.state.activate_str);
+ LOG("(-state) %s", ctx->bindings[i].action.state.deactivate_str);
+ break;
+ case InputType_range:
+ LOG("(range) --unhandled--");
+ break;
+ }
+ }
+
+ GLOBAL_PLATFORM->bindings[GLOBAL_PLATFORM->bindings_len++] = ctx;
+}
+
+/* Pops an input context from the input stack */
+void engine_input_ctx_pop(void) {
+ if (GLOBAL_PLATFORM->bindings == NULL || GLOBAL_PLATFORM->bindings_sz == 0)
+ return;
+ i_ctx_t_free(GLOBAL_PLATFORM->bindings[--GLOBAL_PLATFORM->bindings_len]);
+}
+
+/* Removes all input contexts from the input stack */
+void engine_input_ctx_reset(void) {
+ while (GLOBAL_PLATFORM->bindings_len > 0) {
+ i_ctx_t_free(GLOBAL_PLATFORM->bindings[--GLOBAL_PLATFORM->bindings_len]);
+ }
+}
+
+f64 get_time(void) { return glfwGetTime(); }
+v2_i32 get_windowsize(void) { return GLOBAL_PLATFORM->window->windowsize; }
+v2_i32* get_mousepos(void) { return &GLOBAL_PLATFORM->mouse_pos; }
diff --git a/src/core/memory.c b/src/core/memory.c
new file mode 100644
index 0000000..f19803e
--- /dev/null
+++ b/src/core/memory.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <engine/logging.h>
+
+#include <engine/memory.h>
+
+memory* memory_new(usize max_size) {
+ memory* m = malloc(sizeof(memory));
+ m->data = malloc(max_size);
+ m->size = max_size;
+ m->pos = 0;
+ m->free = max_size;
+
+ memset(m->data, 0, max_size);
+
+ return m;
+}
+
+/* Returns a pointer to the allocated data */
+void* memory_allocate(memory* mem, usize size) {
+ void* data = NULL;
+
+ if (mem->pos + size <= mem->size) {
+ data = (void*)((usize)mem->data + mem->pos);
+ mem->pos += size;
+ mem->free -= size;
+ } else {
+ ERROR("Trying to allocate %lu in a %lu sized memory block", size,
+ mem->size);
+ ERROR("No more room!");
+ exit(EXIT_FAILURE);
+ }
+
+ return data;
+}
+
+memory memory_init(void* data, usize size) {
+ memory m = {0};
+ m.data = data;
+ m.size = size;
+ m.free = 0;
+ return m;
+}
+
+void memory_free(memory* mem, usize size) {
+ if (size > mem->pos) {
+ perror("Freeing too much memory!");
+ exit(EXIT_FAILURE);
+ } else {
+ mem->pos -= size;
+ mem->free += size;
+ }
+}
+
+void memory_clear(memory* mem) {
+ mem->pos = 0;
+ mem->free = mem->size;
+ /* Reset the memory? */
+ memset(mem->data, 0, mem->size);
+}
diff --git a/src/core/state.c b/src/core/state.c
new file mode 100644
index 0000000..55f2a12
--- /dev/null
+++ b/src/core/state.c
@@ -0,0 +1,178 @@
+#include <string.h>
+
+#include <engine/dltools.h>
+#include <engine/input.h>
+#include <engine/logging.h>
+#include <engine/state.h>
+
+typedef StateType state_update_t(void*);
+
+const char* StateTypeStr[] = {
+ "null",
+#define State(name) #name,
+#include <states/list_of_states.h>
+#undef State
+ "quit",
+};
+
+// Setup API for states
+#define State(name) \
+ typedef struct name##_state name##_state; \
+ typedef void(state_##name##_init_t)(name##_state*); \
+ typedef void(state_##name##_free_t)(name##_state*); \
+ typedef StateType(state_##name##_update_t)(name##_state*);
+#include <states/list_of_states.h>
+#undef State
+
+#ifdef DAW_BUILD_HOTRELOAD
+
+// When hotreloading is enabled, we want to assign state function pointers
+// dynamically.
+#define State(name) \
+ state_##name##_init_t* name##_init = NULL; \
+ state_##name##_free_t* name##_free = NULL; \
+ state_##name##_update_t* name##_update = NULL; \
+ \
+ void* libstate_##name = NULL; \
+ const char* libstate_##name##_str = "lib" #name ".so";
+#else
+
+// Otherwise we just declare them.
+#define State(name) \
+ extern state_##name##_init_t name##_init; \
+ extern state_##name##_free_t name##_free; \
+ extern state_##name##_update_t name##_update;
+#endif
+
+#include <states/list_of_states.h>
+#undef State
+
+#include <states/all_states.h>
+
+void binding_t_free(binding_t* b);
+
+void State_init(StateType type, memory* mem) {
+ switch (type) {
+#define State(name) \
+ case (STATE_##name): { \
+ name##_init(memory_allocate(mem, sizeof(name##_state))); \
+ break; \
+ }
+#include <states/list_of_states.h>
+#undef State
+ case STATE_null:
+ case STATE_quit:
+ DEBUG("Got %s state.\n", StateTypeStr[type]);
+ break;
+ default:
+ exit(EXIT_FAILURE);
+ }
+}
+
+void State_free(StateType type, memory* mem) {
+ switch (type) {
+#define State(name) \
+ case (STATE_##name): { \
+ name##_free(mem->data); \
+ break; \
+ }
+#include <states/list_of_states.h>
+#undef State
+ case STATE_null:
+ case STATE_quit:
+ DEBUG("Got %s state.\n", StateTypeStr[type]);
+ break;
+ default:
+ exit(EXIT_FAILURE);
+ }
+ memory_clear(mem);
+}
+
+StateType (*State_updateFunc(StateType type))(void*) {
+ switch (type) {
+#ifdef DAW_BUILD_HOTRELOAD
+#define State(name) \
+ case (STATE_##name): { \
+ return (state_update_t*)name##_update; \
+ break; \
+ }
+#else
+#define State(name) \
+ case (STATE_##name): { \
+ return (state_update_t*)&name##_update; \
+ break; \
+ }
+#endif
+#include <states/list_of_states.h>
+#undef State
+ case STATE_null:
+ case STATE_quit:
+ return NULL; // DEBUG("Got %s state.\n", StateTypeStr[type]);
+ break;
+ default:
+ exit(EXIT_FAILURE);
+ }
+ return NULL;
+}
+
+StateType State_update(StateType type, memory* mem) {
+ StateType next_state = STATE_null;
+ switch (type) {
+#define State(name) \
+ case (STATE_##name): { \
+ next_state = name##_update(mem->data); \
+ break; \
+ }
+#include <states/list_of_states.h>
+#undef State
+ case STATE_null:
+ case STATE_quit:
+ DEBUG("Got %s state.\n", StateTypeStr[type]);
+ break;
+ default:
+ exit(EXIT_FAILURE);
+ }
+ return next_state;
+}
+
+#ifdef DAW_BUILD_HOTRELOAD
+bool State_reload(StateType type, i_ctx** ctx, usize ctx_len) {
+ void* libptr = NULL;
+
+ switch (type) {
+#define State(name) \
+ case (STATE_##name): { \
+ if (libstate_##name == NULL) { \
+ libstate_##name = dynamic_library_open(libstate_##name##_str); \
+ } else { \
+ libstate_##name = \
+ dynamic_library_reload(libstate_##name, libstate_##name##_str); \
+ } \
+ if (libstate_##name == NULL) { \
+ ERROR("Failed loading shared object: %s (%s)", libstate_##name##_str, \
+ dynamic_library_get_error()); \
+ return false; \
+ } \
+ \
+ name##_init = (state_##name##_init_t*)dynamic_library_get_symbol( \
+ libstate_##name, STR(name##_init)); \
+ name##_free = (state_##name##_free_t*)dynamic_library_get_symbol( \
+ libstate_##name, STR(name##_free)); \
+ name##_update = (state_##name##_update_t*)dynamic_library_get_symbol( \
+ libstate_##name, STR(name##_update)); \
+ libptr = libstate_##name; \
+ break; \
+ }
+#include <states/list_of_states.h>
+#undef State
+ case STATE_null:
+ case STATE_quit:
+ ERROR("Invalid state");
+ DEBUG("Got %s state.\n", StateTypeStr[type]);
+ return false;
+ default:
+ exit(EXIT_FAILURE);
+ }
+ return state_refresh_input_ctx(libptr, ctx, ctx_len);
+}
+#endif