summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt23
-rw-r--r--demo/main.lua16
-rw-r--r--src/camera/camera.c137
-rw-r--r--src/camera/camera.h132
-rw-r--r--src/common.h77
-rw-r--r--src/error/error.c126
-rw-r--r--src/error/error.h7
-rw-r--r--src/glad/glad.c (renamed from src/glad.c)0
-rw-r--r--src/glad/glad.h (renamed from src/glad.h)0
-rw-r--r--src/glad/khrplatform.h (renamed from src/khrplatform.h)0
-rw-r--r--src/honey.c69
-rw-r--r--src/honey.h52
-rw-r--r--src/input.h376
l---------src/input/.#input.h1
-rw-r--r--src/input/input.c437
-rw-r--r--src/input/input.h194
-rw-r--r--src/light/light.c20
-rw-r--r--src/light/light.h33
-rw-r--r--src/main.c19
-rw-r--r--src/mesh/mesh.c86
-rw-r--r--src/mesh/mesh.h57
-rw-r--r--src/model/model.c121
-rw-r--r--src/model/model.h29
-rw-r--r--src/primitives/primitives.c180
-rw-r--r--src/primitives/primitives.h59
-rw-r--r--src/shader/shader.c224
-rw-r--r--src/shader/shader.h117
-rw-r--r--src/stb_image/stb_image.c (renamed from src/stb_image.c)0
-rw-r--r--src/stb_image/stb_image.h (renamed from src/stb_image.h)0
-rw-r--r--src/texture/texture.c45
-rw-r--r--src/texture/texture.h42
31 files changed, 2270 insertions, 409 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0726040..21f5467 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.2)
project(honey_engine_demo)
-#set(CMAKE_C_FLAGS "-Wall -Wextra -Werror -Wfatal-errors -Wpedantic")
+set(CMAKE_C_FLAGS "-Wall -Wextra -Werror -Wfatal-errors -Wpedantic")
find_package(OpenGL REQUIRED)
find_package(Lua51 REQUIRED)
@@ -10,10 +10,23 @@ include_directories(${LUA_INCLUDE_DIR})
set(CMAKE_C_FLAGS "-g")
-add_library(glad src/glad.c)
-add_library(stb_image src/stb_image.c)
-
-add_executable(honey src/main.c)
+add_library(glad src/glad/glad.c)
+add_library(stb_image src/stb_image/stb_image.c)
+
+set(SOURCE_FILES
+ src/main.c
+ src/camera/camera.c
+ src/error/error.c
+ src/honey.c
+ src/input/input.c
+ src/light/light.c
+ src/mesh/mesh.c
+ src/model/model.c
+ src/primitives/primitives.c
+ src/shader/shader.c
+ src/texture/texture.c)
+
+add_executable(honey ${SOURCE_FILES})
set(LIBRARIES ${LUA_LIBRARIES} assimp glfw GL glad stb_image dl m)
target_link_libraries(honey ${LIBRARIES})
diff --git a/demo/main.lua b/demo/main.lua
index afc94d2..8c0072f 100644
--- a/demo/main.lua
+++ b/demo/main.lua
@@ -4,11 +4,11 @@ end
local total_time = 0
-function honey.update(dt)
- total_time = total_time + dt
- print(dt)
-end
-
-function honey.draw()
- print('draw!')
-end
+--function honey.update(dt)
+-- total_time = total_time + dt
+-- print(dt)
+--end
+--
+--function honey.draw()
+-- print('draw!')
+--end
diff --git a/src/camera/camera.c b/src/camera/camera.c
new file mode 100644
index 0000000..44f480d
--- /dev/null
+++ b/src/camera/camera.c
@@ -0,0 +1,137 @@
+#include "camera.h"
+
+void honey_camera_new(honey_camera* camera,
+ vec3 position,
+ vec3 angle,
+ enum honey_camera_projection projection_type,
+ float aspect_ratio,
+ float near, float far,
+ float fov,
+ float left, float right, float top, float bottom) {
+ glm_vec3_copy(position, camera->position);
+ glm_vec3_copy(angle, camera->angle);
+
+ camera->projection_type = projection_type;
+
+ camera->aspect_ratio = aspect_ratio;
+ camera->near = near;
+ camera->far = far;
+
+ if (projection_type == HONEY_PERSPECTIVE) {
+ camera->fov = fov;
+ }
+ else if (projection_type == HONEY_ORTHOGRAPHIC) {
+ camera->ortho_left = left;
+ camera->ortho_right = right;
+ camera->ortho_top = top;
+ camera->ortho_bottom = bottom;
+ }
+
+ honey_camera_calculate_view(camera);
+ honey_camera_calculate_projection(camera);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_camera_new_perspective(honey_camera* camera,
+ vec3 position,
+ vec3 angle,
+ float aspect_ratio,
+ float near, float far,
+ float fov) {
+ honey_camera_new(camera,
+ position, angle,
+ HONEY_PERSPECTIVE,
+ aspect_ratio, near, far, fov,
+ 0, 0, 0, 0);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_camera_new_orthographic(honey_camera* camera,
+ vec3 position,
+ vec3 angle,
+ float near, float far,
+ float left, float right, float top, float bottom) {
+ honey_camera_new(camera,
+ position, angle,
+ HONEY_ORTHOGRAPHIC,
+ 0,
+ near, far,
+ 0,
+ left, right, top, bottom);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_camera_calculate_look_direction(honey_camera* camera) {
+ float pitch = camera->angle[0];
+ float yaw = camera->angle[1];
+
+ float x = cos(pitch) * cos(yaw);
+ float y = sin(pitch);
+ float z = cos(pitch) * sin(yaw);
+
+ camera->look_direction[0] = x;
+ camera->look_direction[1] = y;
+ camera->look_direction[2] = z;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_camera_calculate_up(honey_camera* camera) {
+ float pitch = camera->angle[0];
+ float yaw = camera->angle[1];
+ float roll = camera->angle[2];
+
+ camera->up[0] = 0;
+ camera->up[1] = 1;
+ camera->up[2] = 0;
+
+ mat3 rot3;
+ mat4 rot4;
+ glm_mat4_identity(rot4);
+ glm_rotate(rot4, roll, camera->look_direction);
+ glm_mat4_pick3(rot4, rot3);
+
+ glm_mat3_mulv(rot3, camera->up, camera->up);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_camera_calculate_right(honey_camera* camera) {
+ glm_vec3_cross(camera->up, camera->look_direction, camera->right);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_camera_calculate_view(honey_camera* camera) {
+ honey_camera_calculate_look_direction(camera);
+ honey_camera_calculate_up(camera);
+ honey_camera_calculate_right(camera);
+
+ glm_look(camera->position, camera->look_direction, camera->up, camera->view);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_camera_calculate_projection(honey_camera* camera) {
+ if (camera->projection_type == HONEY_PERSPECTIVE) {
+ glm_mat4_identity(camera->projection);
+ glm_perspective(camera->fov,
+ camera->aspect_ratio,
+ camera->near,
+ camera->far,
+ camera->projection);
+ }
+ else if (camera->projection_type == HONEY_ORTHOGRAPHIC) {
+ glm_mat4_identity(camera->projection);
+ glm_ortho(camera->ortho_left,
+ camera->ortho_right,
+ camera->ortho_bottom,
+ camera->ortho_top,
+ camera->near,
+ camera->far,
+ camera->projection);
+ }
+}
diff --git a/src/camera/camera.h b/src/camera/camera.h
new file mode 100644
index 0000000..b8d3b61
--- /dev/null
+++ b/src/camera/camera.h
@@ -0,0 +1,132 @@
+#ifndef HONEY_CAMERA_H
+#define HONEY_CAMERA_H
+
+/** @file camera.h
+ *
+ * @brief Define the basic honey_camera struct, associated functions,
+ * and common camera variants.
+ */
+
+#include "../common.h"
+
+enum honey_camera_projection {
+ HONEY_PERSPECTIVE,
+ HONEY_ORTHOGRAPHIC,
+};
+
+typedef struct {
+ vec3 position;
+ vec3 angle; /* pitch, yaw, roll */
+ vec3 look_direction;
+ vec3 up;
+ vec3 right;
+ mat4 view;
+ mat4 projection;
+ enum honey_camera_projection projection_type;
+ float aspect_ratio;
+ float near, far;
+ float fov;
+ float ortho_left;
+ float ortho_right;
+ float ortho_top;
+ float ortho_bottom;
+} honey_camera;
+
+/** @brief Create a new camera.
+ *
+ * The full camera creation function. Most of the time, you will probably use either
+ * honey_camera_new_perspective() or honey_camera_new_projection() instead.
+ *
+ * @param[out] camera Pointer to the destination honey_camera.
+ * @param[in] position The position of the camera.
+ * @param[in] angle The Euler angles (pitch, yaw, roll) of the camera.
+ * @param[in] projection_type The type of projection to use.
+ * @param[in] aspect_ratio The aspect ratio of the camera. Set to zero if using orthographic projection.
+ * @param[in] near The distance of the near plane.
+ * @param[in] far The distance of the far plane.
+ * @param[in] fov The field of view. Set to zero if using orthographic projection.
+ * @param[in] left The leftmost distance. Set to zero if using perspective projection.
+ * @param[in] right The rightmost distance. Set to zero if using perspective projection.
+ * @param[in] top The uppermost distance. Set to zero if using perspective projection.
+ * @param[in] bottom The lowest distance. Set to zero if using perspective projection.
+ */
+void honey_camera_new(honey_camera* camera,
+ vec3 position,
+ vec3 angle,
+ enum honey_camera_projection projection_type,
+ float aspect_ratio,
+ float near, float far,
+ float fov,
+ float left, float right, float top, float bottom);
+
+/** @brief Create a camera with a perspective projection matrix.
+ *
+ * @param[out] camera Pointer to the destination honey_camera.
+ * @param[in] position The position of the camera.
+ * @param[in] angle The Euler angles (pitch, yaw, roll) of the camera.
+ * @param[in] aspect_ratio The aspect ratio of the camera.
+ * @param[in] near The distance of the near plane.
+ * @param[in] far The distance of the far plane.
+ * @param[in] fov The field of view.
+ */
+void honey_camera_new_perspective(honey_camera* camera,
+ vec3 position,
+ vec3 angle,
+ float aspect_ratio,
+ float near, float far,
+ float fov);
+
+/** @brief Create a camera with an orthographic projection matrix.
+ *
+ * @param[out] camera Pointer to the destination honey_camera.
+ * @param[in] position The position of the camera.
+ * @param[in] angle The Euler angles (pitch, yaw, roll) of the camera.
+ * @param[in] near The distance of the near plane.
+ * @param[in] far The distance of the far plane.
+ * @param[in] left The leftmost distance.
+ * @param[in] right The rightmost distance.
+ * @param[in] top The uppermost distance.
+ * @param[in] bottom The lowest distance.
+ */
+void honey_camera_new_orthographic(honey_camera* camera,
+ vec3 position,
+ vec3 angle,
+ float near, float far,
+ float left, float right, float top, float bottom);
+
+/** @brief (Re-)Calculate a camera's look_direction.
+ *
+ * @param[in] Pointer to the camera to re-calculate.
+ */
+void honey_camera_calculate_look_direction(honey_camera* camera);
+
+/** @brief (Re-)Calculate a camera's up vector.
+ *
+ * @param[in] Pointer to the camera to re-calculate.
+ */
+void honey_camera_calculate_up(honey_camera* camera);
+
+/** @brief (Re-)Calculate a camera's right vector.
+ *
+ * @param[in] Pointer to the camera to re-calculate.
+ */
+void honey_camera_calculate_right(honey_camera* camera);
+
+/** @brief (Re-)Calculate a camera's view matrix.
+ *
+ * This function need only be called when the camera has been moved in some way.
+ *
+ * @param[in] camera Pointer to the camera to re-calculate.
+ */
+void honey_camera_calculate_view(honey_camera* camera);
+
+/** @brief (Re-)Calculate a camera's projection matrix.
+ *
+ * This function need only be called when the projection has changes in some way.
+ * Most commonly, this would be changing the FOV.
+ *
+ * @param[in] camera Pointer to the camera to re-calculate.
+ */
+void honey_camera_calculate_projection(honey_camera* camera);
+
+#endif
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..ede67cd
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,77 @@
+#ifndef HONEY_COMMON_H
+#define HONEY_COMMON_H
+
+// standard c libraries
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+// POSIX options
+#include <unistd.h>
+
+// lua interpreter
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+// glad
+#include "glad/glad.h"
+#include <GLFW/glfw3.h>
+
+// c opengl mathematics function
+#include <cglm/cglm.h>
+#include <cglm/call.h>
+
+// assimp
+#include <assimp/cimport.h>
+#include <assimp/scene.h>
+#include <assimp/postprocess.h>
+
+// stb image
+#include "stb_image/stb_image.h"
+
+typedef GLFWwindow* honey_window;
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+typedef enum {
+ /* generic results */
+ HONEY_OK,
+ HONEY_MEMORY_ALLOCATION_ERROR,
+ HONEY_FILE_READ_ERROR,
+
+ /* shader errors */
+ HONEY_VERTEX_SHADER_COMPILATION_ERROR,
+ HONEY_FRAGMENT_SHADER_COMPILATION_ERROR,
+ HONEY_SHADER_LINK_ERROR,
+
+ /* mesh errors */
+ HONEY_MESH_BAD_VERTEX_DATA,
+ HONEY_MESH_BAD_INDEX_DATA,
+
+ /* model errors */
+ HONEY_MODEL_LOAD_ERROR,
+
+ HONEY_N_ERRORS
+} honey_result;
+
+#define HONEY_ERROR_DATA_STRING_LENGTH 4096
+
+static struct {
+ char string1[HONEY_ERROR_DATA_STRING_LENGTH];
+ char string2[HONEY_ERROR_DATA_STRING_LENGTH];
+} honey_error_data;
+
+void honey_error_clear_strings();
+void honey_error_set_string1(char* string);
+void honey_error_set_string2(char* string);
+
+/** @brief Generate a human-readable error message.
+ *
+ * @param[out] error_string A string with at least 3*HONEY_ERROR_DATA_STRING_LENGTH characters to store the result
+ * @param[in] error The error to generate a message for
+ */
+void honey_human_readable_error(char* error_string, honey_result error);
+
+#endif
diff --git a/src/error/error.c b/src/error/error.c
new file mode 100644
index 0000000..671d34e
--- /dev/null
+++ b/src/error/error.c
@@ -0,0 +1,126 @@
+#include "../common.h"
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_error_clear_strings() {
+ memset(honey_error_data.string1, 0, HONEY_ERROR_DATA_STRING_LENGTH);
+ memset(honey_error_data.string2, 0, HONEY_ERROR_DATA_STRING_LENGTH);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_error_set_string1(char* string) {
+ size_t n_bytes = strlen(string) + 1;
+ if (n_bytes > HONEY_ERROR_DATA_STRING_LENGTH)
+ n_bytes = HONEY_ERROR_DATA_STRING_LENGTH;
+ memcpy(honey_error_data.string1, string, n_bytes);
+ honey_error_data.string1[HONEY_ERROR_DATA_STRING_LENGTH-1] = 0;
+}
+
+void honey_error_set_string2(char* string) {
+ size_t n_bytes = strlen(string) + 1;
+ if (n_bytes > HONEY_ERROR_DATA_STRING_LENGTH)
+ n_bytes = HONEY_ERROR_DATA_STRING_LENGTH;
+ memcpy(honey_error_data.string2, string, n_bytes);
+ honey_error_data.string2[HONEY_ERROR_DATA_STRING_LENGTH-1] = 0;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_human_readable_error(char* error_string, honey_result error) {
+ size_t string_size = sizeof(char)*3*HONEY_ERROR_DATA_STRING_LENGTH;
+
+ switch(error) {
+ case HONEY_OK:
+ snprintf(error_string, string_size, "[honey] OK");
+ break;
+
+ case HONEY_MEMORY_ALLOCATION_ERROR:
+ snprintf(error_string, string_size, "[honey] ERROR: failed to allocate memory");
+ break;
+
+ case HONEY_FILE_READ_ERROR:
+ if (honey_error_data.string1 != NULL) {
+ snprintf(error_string,
+ string_size,
+ "[honey] ERROR: failed to read file '%s'",
+ honey_error_data.string1);
+ } else {
+ snprintf(error_string, string_size, "[honey] ERROR: failed to read file");
+ }
+ break;
+
+ case HONEY_VERTEX_SHADER_COMPILATION_ERROR:
+ if (honey_error_data.string1 != NULL) {
+ if (honey_error_data.string2 != NULL) {
+ snprintf(error_string,
+ string_size,
+ "[honey] ERROR: failed to compile vertex shader '%s'\n"
+ "[honey] GLSL compiler output:\n%s\n",
+ honey_error_data.string2,
+ honey_error_data.string1);
+ } else {
+ snprintf(error_string,
+ string_size,
+ "[honey] ERROR: failed to compile vertex shader\n"
+ "[honey] GLSL compiler output:\n%s\n",
+ honey_error_data.string1);
+ }
+ } else {
+ snprintf(error_string,
+ string_size,
+ "[honey] ERROR: failed to compile vertex shader.");
+ }
+ break;
+
+
+ case HONEY_FRAGMENT_SHADER_COMPILATION_ERROR:
+ if (honey_error_data.string1 != NULL) {
+ if (honey_error_data.string2 != NULL) {
+ snprintf(error_string,
+ string_size,
+ "[honey] ERROR: failed to compile fragment shader '%s'\n"
+ "[honey] GLSL compiler output:\n%s\n",
+ honey_error_data.string2,
+ honey_error_data.string1);
+ } else {
+ snprintf(error_string,
+ string_size,
+ "[honey] ERROR: failed to compile fragment shader\n"
+ "[honey] GLSL compiler output:\n%s\n",
+ honey_error_data.string1);
+ }
+ } else {
+ snprintf(error_string,
+ string_size,
+ "[honey] ERROR: failed to compile fragment shader.");
+ }
+ break;
+
+ case HONEY_SHADER_LINK_ERROR:
+ break;
+
+ case HONEY_MESH_BAD_VERTEX_DATA:
+ break;
+
+ case HONEY_MESH_BAD_INDEX_DATA:
+ break;
+
+ case HONEY_MODEL_LOAD_ERROR:
+ snprintf(error_string,
+ string_size,
+ "[honey] ERROR: model '%s' contains errors",
+ honey_error_data.string1);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+
+
+
+
+
diff --git a/src/error/error.h b/src/error/error.h
new file mode 100644
index 0000000..e456c88
--- /dev/null
+++ b/src/error/error.h
@@ -0,0 +1,7 @@
+#ifndef HONEY_ERROR_H
+#define HONEY_ERROR_H
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+
+#endif
diff --git a/src/glad.c b/src/glad/glad.c
index 260f1c0..260f1c0 100644
--- a/src/glad.c
+++ b/src/glad/glad.c
diff --git a/src/glad.h b/src/glad/glad.h
index cde8703..cde8703 100644
--- a/src/glad.h
+++ b/src/glad/glad.h
diff --git a/src/khrplatform.h b/src/glad/khrplatform.h
index 5b55ea2..5b55ea2 100644
--- a/src/khrplatform.h
+++ b/src/glad/khrplatform.h
diff --git a/src/honey.c b/src/honey.c
new file mode 100644
index 0000000..4d31090
--- /dev/null
+++ b/src/honey.c
@@ -0,0 +1,69 @@
+#include "honey.h"
+
+static void default_honey_update_callback(float dt) {}
+static void default_honey_draw_callback() {}
+
+honey_window honey_setup(int screen_width, int screen_height, char* window_title) {
+ honey_update_callback = &default_honey_update_callback;
+ honey_draw_callback = &default_honey_draw_callback;
+
+ glfwInit();
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+ honey_window window = glfwCreateWindow(screen_width, screen_height, window_title, NULL, NULL);
+ if (window == NULL) {
+ fprintf(stderr, "ERROR: failed to create window!\n");
+ glfwTerminate();
+ return NULL;
+ }
+
+ glfwMakeContextCurrent(window);
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+
+ if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
+ fprintf(stderr, "ERROR: failed to initialize GLAD!\n");
+ glfwTerminate();
+ return NULL;
+ }
+
+ //honey_setup_keyboard();
+ //glfwSetKeyCallback(window, default_honey_keyboard_callback);
+
+ // Enable blending
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ return window;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_set_update_callback(void (*update_callback)(float)) {
+ honey_update_callback = update_callback;
+}
+
+void honey_set_draw_callback(void (*draw_callback)()) {
+ honey_draw_callback = draw_callback;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_run(honey_window window) {
+ float prevTime = 0;
+ float currentTime = 0;
+ float dt;
+ float draw_dt = 0;
+
+ while(!glfwWindowShouldClose(window)) {
+ currentTime = (float) glfwGetTime();
+ dt = currentTime - prevTime;
+ prevTime = currentTime;
+
+ honey_update_callback(dt);
+ honey_draw_callback();
+ }
+}
+
+
diff --git a/src/honey.h b/src/honey.h
index c124903..c134894 100644
--- a/src/honey.h
+++ b/src/honey.h
@@ -1,6 +1,54 @@
-#ifndef HONEY_H
-#define HONEY_H
+#ifndef HONEY_ENGINE_H
+#define HONEY_ENGINE_H
+/** @file honey.h
+ *
+ * @brief Defines the basic loading and callback functions.
+*/
+#include "common.h"
+
+#include "camera/camera.h"
+#include "input/input.h"
+#include "light/light.h"
+#include "mesh/mesh.h"
+#include "model/model.h"
+#include "primitives/primitives.h"
+#include "shader/shader.h"
+#include "texture/texture.h"
+
+/** @brief Initialize Honey.
+ *
+ * @param[in] screen_width The desired width of the screen in pixels
+ * @param[in] screen_height The desired height of the screen in pixels
+ * @param[in] window_title Title to use for the window.
+ */
+honey_window honey_setup(int screen_width, int screen_height, char* window_title);
+
+static void (*honey_update_callback)(float dt);
+static void (*honey_draw_callback)();
+
+/** @brief Set the main update function.
+ *
+ * @param[in] update_callback The function to call every loop
+ */
+void honey_set_update_callback(void (*update_callback)(float));
+
+/** @brief Set the main draw function
+ *
+ * @param[in] draw_callback The function to call every draw cycle
+ */
+void honey_set_draw_callback(void (*draw_callback)());
+
+/** @brief The main game loop.
+ *
+ * @param[in] window The window the game is running in, created with honey_setup()
+ */
+void honey_run(honey_window window);
+
+#define honey_set_resize_callback glfwSetFramebufferSizeCallback
+#define honey_set_mouse_move_callback glfwSetCursorPosCallback
+
+#define honey_quit glfwTerminate
#endif
diff --git a/src/input.h b/src/input.h
deleted file mode 100644
index 77ff148..0000000
--- a/src/input.h
+++ /dev/null
@@ -1,376 +0,0 @@
-#ifndef HONEY_INPUT_H
-#define HONEY_INPUT_H
-
-#include <lua.h>
-
-/* @brief Place a lua table containing the enum values for GLFW keys on the stack. */
-void honey_setup_input(lua_State* L)
-{
-
- lua_createtable(L, 0, 121);
-
- lua_pushinteger(L, 0);
- lua_setfield(L, -2, "unknown");
-
- lua_pushinteger(L, 1);
- lua_setfield(L, -2, "space");
-
- lua_pushinteger(L, 2);
- lua_setfield(L, -2, "apostrophe");
-
- lua_pushinteger(L, 3);
- lua_setfield(L, -2, "comma");
-
- lua_pushinteger(L, 4);
- lua_setfield(L, -2, "minus");
-
- lua_pushinteger(L, 5);
- lua_setfield(L, -2, "period");
-
- lua_pushinteger(L, 6);
- lua_setfield(L, -2, "slash");
-
- lua_pushinteger(L, 7);
- lua_setfield(L, -2, "0");
-
- lua_pushinteger(L, 8);
- lua_setfield(L, -2, "1");
-
- lua_pushinteger(L, 9);
- lua_setfield(L, -2, "2");
-
- lua_pushinteger(L, 10);
- lua_setfield(L, -2, "3");
-
- lua_pushinteger(L, 11);
- lua_setfield(L, -2, "4");
-
- lua_pushinteger(L, 12);
- lua_setfield(L, -2, "5");
-
- lua_pushinteger(L, 13);
- lua_setfield(L, -2, "6");
-
- lua_pushinteger(L, 14);
- lua_setfield(L, -2, "7");
-
- lua_pushinteger(L, 15);
- lua_setfield(L, -2, "8");
-
- lua_pushinteger(L, 16);
- lua_setfield(L, -2, "9");
-
- lua_pushinteger(L, 17);
- lua_setfield(L, -2, "semicolon");
-
- lua_pushinteger(L, 18);
- lua_setfield(L, -2, "equal");
-
- lua_pushinteger(L, 19);
- lua_setfield(L, -2, "a");
-
- lua_pushinteger(L, 20);
- lua_setfield(L, -2, "b");
-
- lua_pushinteger(L, 21);
- lua_setfield(L, -2, "c");
-
- lua_pushinteger(L, 22);
- lua_setfield(L, -2, "d");
-
- lua_pushinteger(L, 23);
- lua_setfield(L, -2, "e");
-
- lua_pushinteger(L, 24);
- lua_setfield(L, -2, "f");
-
- lua_pushinteger(L, 25);
- lua_setfield(L, -2, "g");
-
- lua_pushinteger(L, 26);
- lua_setfield(L, -2, "h");
-
- lua_pushinteger(L, 27);
- lua_setfield(L, -2, "i");
-
- lua_pushinteger(L, 28);
- lua_setfield(L, -2, "j");
-
- lua_pushinteger(L, 29);
- lua_setfield(L, -2, "k");
-
- lua_pushinteger(L, 30);
- lua_setfield(L, -2, "l");
-
- lua_pushinteger(L, 31);
- lua_setfield(L, -2, "m");
-
- lua_pushinteger(L, 32);
- lua_setfield(L, -2, "n");
-
- lua_pushinteger(L, 33);
- lua_setfield(L, -2, "o");
-
- lua_pushinteger(L, 34);
- lua_setfield(L, -2, "p");
-
- lua_pushinteger(L, 35);
- lua_setfield(L, -2, "q");
-
- lua_pushinteger(L, 36);
- lua_setfield(L, -2, "r");
-
- lua_pushinteger(L, 37);
- lua_setfield(L, -2, "s");
-
- lua_pushinteger(L, 38);
- lua_setfield(L, -2, "t");
-
- lua_pushinteger(L, 39);
- lua_setfield(L, -2, "u");
-
- lua_pushinteger(L, 40);
- lua_setfield(L, -2, "v");
-
- lua_pushinteger(L, 41);
- lua_setfield(L, -2, "w");
-
- lua_pushinteger(L, 42);
- lua_setfield(L, -2, "x");
-
- lua_pushinteger(L, 43);
- lua_setfield(L, -2, "y");
-
- lua_pushinteger(L, 44);
- lua_setfield(L, -2, "z");
-
- lua_pushinteger(L, 45);
- lua_setfield(L, -2, "left_bracket");
-
- lua_pushinteger(L, 46);
- lua_setfield(L, -2, "backslash");
-
- lua_pushinteger(L, 47);
- lua_setfield(L, -2, "right_bracket");
-
- lua_pushinteger(L, 48);
- lua_setfield(L, -2, "grave_accent");
-
- lua_pushinteger(L, 49);
- lua_setfield(L, -2, "world_1");
-
- lua_pushinteger(L, 50);
- lua_setfield(L, -2, "world_2");
-
- lua_pushinteger(L, 51);
- lua_setfield(L, -2, "escape");
-
- lua_pushinteger(L, 52);
- lua_setfield(L, -2, "enter");
-
- lua_pushinteger(L, 53);
- lua_setfield(L, -2, "tab");
-
- lua_pushinteger(L, 54);
- lua_setfield(L, -2, "backspace");
-
- lua_pushinteger(L, 55);
- lua_setfield(L, -2, "insert");
-
- lua_pushinteger(L, 56);
- lua_setfield(L, -2, "delete");
-
- lua_pushinteger(L, 57);
- lua_setfield(L, -2, "right");
-
- lua_pushinteger(L, 58);
- lua_setfield(L, -2, "left");
-
- lua_pushinteger(L, 59);
- lua_setfield(L, -2, "down");
-
- lua_pushinteger(L, 60);
- lua_setfield(L, -2, "up");
-
- lua_pushinteger(L, 61);
- lua_setfield(L, -2, "page_up");
-
- lua_pushinteger(L, 62);
- lua_setfield(L, -2, "page_down");
-
- lua_pushinteger(L, 63);
- lua_setfield(L, -2, "home");
-
- lua_pushinteger(L, 64);
- lua_setfield(L, -2, "end");
-
- lua_pushinteger(L, 65);
- lua_setfield(L, -2, "caps_lock");
-
- lua_pushinteger(L, 66);
- lua_setfield(L, -2, "scroll_lock");
-
- lua_pushinteger(L, 67);
- lua_setfield(L, -2, "num_lock");
-
- lua_pushinteger(L, 68);
- lua_setfield(L, -2, "print_screen");
-
- lua_pushinteger(L, 69);
- lua_setfield(L, -2, "pause");
-
- lua_pushinteger(L, 70);
- lua_setfield(L, -2, "f1");
-
- lua_pushinteger(L, 71);
- lua_setfield(L, -2, "f2");
-
- lua_pushinteger(L, 72);
- lua_setfield(L, -2, "f3");
-
- lua_pushinteger(L, 73);
- lua_setfield(L, -2, "f4");
-
- lua_pushinteger(L, 74);
- lua_setfield(L, -2, "f5");
-
- lua_pushinteger(L, 75);
- lua_setfield(L, -2, "f6");
-
- lua_pushinteger(L, 76);
- lua_setfield(L, -2, "f7");
-
- lua_pushinteger(L, 77);
- lua_setfield(L, -2, "f8");
-
- lua_pushinteger(L, 78);
- lua_setfield(L, -2, "f9");
-
- lua_pushinteger(L, 79);
- lua_setfield(L, -2, "f10");
-
- lua_pushinteger(L, 80);
- lua_setfield(L, -2, "f11");
-
- lua_pushinteger(L, 81);
- lua_setfield(L, -2, "f12");
-
- lua_pushinteger(L, 82);
- lua_setfield(L, -2, "f13");
-
- lua_pushinteger(L, 83);
- lua_setfield(L, -2, "f14");
-
- lua_pushinteger(L, 84);
- lua_setfield(L, -2, "f15");
-
- lua_pushinteger(L, 85);
- lua_setfield(L, -2, "f16");
-
- lua_pushinteger(L, 86);
- lua_setfield(L, -2, "f17");
-
- lua_pushinteger(L, 87);
- lua_setfield(L, -2, "f18");
-
- lua_pushinteger(L, 88);
- lua_setfield(L, -2, "f19");
-
- lua_pushinteger(L, 89);
- lua_setfield(L, -2, "f20");
-
- lua_pushinteger(L, 90);
- lua_setfield(L, -2, "f21");
-
- lua_pushinteger(L, 91);
- lua_setfield(L, -2, "f22");
-
- lua_pushinteger(L, 92);
- lua_setfield(L, -2, "f23");
-
- lua_pushinteger(L, 93);
- lua_setfield(L, -2, "f24");
-
- lua_pushinteger(L, 94);
- lua_setfield(L, -2, "f25");
-
- lua_pushinteger(L, 95);
- lua_setfield(L, -2, "kp_0");
-
- lua_pushinteger(L, 96);
- lua_setfield(L, -2, "kp_1");
-
- lua_pushinteger(L, 97);
- lua_setfield(L, -2, "kp_2");
-
- lua_pushinteger(L, 98);
- lua_setfield(L, -2, "kp_3");
-
- lua_pushinteger(L, 99);
- lua_setfield(L, -2, "kp_4");
-
- lua_pushinteger(L, 100);
- lua_setfield(L, -2, "kp_5");
-
- lua_pushinteger(L, 101);
- lua_setfield(L, -2, "kp_6");
-
- lua_pushinteger(L, 102);
- lua_setfield(L, -2, "kp_7");
-
- lua_pushinteger(L, 103);
- lua_setfield(L, -2, "kp_8");
-
- lua_pushinteger(L, 104);
- lua_setfield(L, -2, "kp_9");
-
- lua_pushinteger(L, 105);
- lua_setfield(L, -2, "kp_decimal");
-
- lua_pushinteger(L, 106);
- lua_setfield(L, -2, "kp_divide");
-
- lua_pushinteger(L, 107);
- lua_setfield(L, -2, "kp_multiply");
-
- lua_pushinteger(L, 108);
- lua_setfield(L, -2, "kp_subtract");
-
- lua_pushinteger(L, 109);
- lua_setfield(L, -2, "kp_add");
-
- lua_pushinteger(L, 110);
- lua_setfield(L, -2, "kp_enter");
-
- lua_pushinteger(L, 111);
- lua_setfield(L, -2, "kp_equal");
-
- lua_pushinteger(L, 112);
- lua_setfield(L, -2, "left_shift");
-
- lua_pushinteger(L, 113);
- lua_setfield(L, -2, "left_control");
-
- lua_pushinteger(L, 114);
- lua_setfield(L, -2, "left_alt");
-
- lua_pushinteger(L, 115);
- lua_setfield(L, -2, "left_super");
-
- lua_pushinteger(L, 116);
- lua_setfield(L, -2, "right_shift");
-
- lua_pushinteger(L, 117);
- lua_setfield(L, -2, "right_control");
-
- lua_pushinteger(L, 118);
- lua_setfield(L, -2, "right_alt");
-
- lua_pushinteger(L, 119);
- lua_setfield(L, -2, "right_super");
-
- lua_pushinteger(L, 120);
- lua_setfield(L, -2, "menu");
-}
-
-#endif
diff --git a/src/input/.#input.h b/src/input/.#input.h
new file mode 120000
index 0000000..4ad0568
--- /dev/null
+++ b/src/input/.#input.h
@@ -0,0 +1 @@
+kate@ophelia.6336:1602737412 \ No newline at end of file
diff --git a/src/input/input.c b/src/input/input.c
new file mode 100644
index 0000000..31375cc
--- /dev/null
+++ b/src/input/input.c
@@ -0,0 +1,437 @@
+#include "input.h"
+
+void honey_setup_keyboard()
+{
+ memset(honey_key_states, 0, sizeof(honey_key_states));
+ memset(honey_key_callbacks, 0, sizeof(honey_key_callbacks));
+ memset(honey_key_callbacks_data, 0, sizeof(honey_key_callbacks_data));
+
+ honey_keyboard_callback = &default_honey_keyboard_callback;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_setup_input(lua_State* L)
+{
+ lua_createtable(L, 0, 121);
+
+ lua_pushinteger(L, HONEY_KEY_UNKNOWN);
+ lua_setfield(L, -2, "honey_key_unknown");
+
+ lua_pushinteger(L, HONEY_KEY_SPACE);
+ lua_setfield(L, -2, "honey_key_space");
+
+ lua_pushinteger(L, HONEY_KEY_APOSTROPHE);
+ lua_setfield(L, -2, "honey_key_apostrophe");
+
+ lua_pushinteger(L, HONEY_KEY_COMMA);
+ lua_setfield(L, -2, "honey_key_comma");
+
+ lua_pushinteger(L, HONEY_KEY_MINUS);
+ lua_setfield(L, -2, "honey_key_minus");
+
+ lua_pushinteger(L, HONEY_KEY_PERIOD);
+ lua_setfield(L, -2, "honey_key_period");
+
+ lua_pushinteger(L, HONEY_KEY_SLASH);
+ lua_setfield(L, -2, "honey_key_slash");
+
+ lua_pushinteger(L, HONEY_KEY_0);
+ lua_setfield(L, -2, "honey_key_0");
+
+ lua_pushinteger(L, HONEY_KEY_1);
+ lua_setfield(L, -2, "honey_key_1");
+
+ lua_pushinteger(L, HONEY_KEY_2);
+ lua_setfield(L, -2, "honey_key_2");
+
+ lua_pushinteger(L, HONEY_KEY_3);
+ lua_setfield(L, -2, "honey_key_3");
+
+ lua_pushinteger(L, HONEY_KEY_4);
+ lua_setfield(L, -2, "honey_key_4");
+
+ lua_pushinteger(L, HONEY_KEY_5);
+ lua_setfield(L, -2, "honey_key_5");
+
+ lua_pushinteger(L, HONEY_KEY_6);
+ lua_setfield(L, -2, "honey_key_6");
+
+ lua_pushinteger(L, HONEY_KEY_7);
+ lua_setfield(L, -2, "honey_key_7");
+
+ lua_pushinteger(L, HONEY_KEY_8);
+ lua_setfield(L, -2, "honey_key_8");
+
+ lua_pushinteger(L, HONEY_KEY_9);
+ lua_setfield(L, -2, "honey_key_9");
+
+ lua_pushinteger(L, HONEY_KEY_SEMICOLON);
+ lua_setfield(L, -2, "honey_key_semicolon");
+
+ lua_pushinteger(L, HONEY_KEY_EQUAL);
+ lua_setfield(L, -2, "honey_key_equal");
+
+ lua_pushinteger(L, HONEY_KEY_A);
+ lua_setfield(L, -2, "honey_key_a");
+
+ lua_pushinteger(L, HONEY_KEY_B);
+ lua_setfield(L, -2, "honey_key_b");
+
+ lua_pushinteger(L, HONEY_KEY_C);
+ lua_setfield(L, -2, "honey_key_c");
+
+ lua_pushinteger(L, HONEY_KEY_D);
+ lua_setfield(L, -2, "honey_key_d");
+
+ lua_pushinteger(L, HONEY_KEY_E);
+ lua_setfield(L, -2, "honey_key_e");
+
+ lua_pushinteger(L, HONEY_KEY_F);
+ lua_setfield(L, -2, "honey_key_f");
+
+ lua_pushinteger(L, HONEY_KEY_G);
+ lua_setfield(L, -2, "honey_key_g");
+
+ lua_pushinteger(L, HONEY_KEY_H);
+ lua_setfield(L, -2, "honey_key_h");
+
+ lua_pushinteger(L, HONEY_KEY_I);
+ lua_setfield(L, -2, "honey_key_i");
+
+ lua_pushinteger(L, HONEY_KEY_J);
+ lua_setfield(L, -2, "honey_key_j");
+
+ lua_pushinteger(L, HONEY_KEY_K);
+ lua_setfield(L, -2, "honey_key_k");
+
+ lua_pushinteger(L, HONEY_KEY_L);
+ lua_setfield(L, -2, "honey_key_l");
+
+ lua_pushinteger(L, HONEY_KEY_M);
+ lua_setfield(L, -2, "honey_key_m");
+
+ lua_pushinteger(L, HONEY_KEY_N);
+ lua_setfield(L, -2, "honey_key_n");
+
+ lua_pushinteger(L, HONEY_KEY_O);
+ lua_setfield(L, -2, "honey_key_o");
+
+ lua_pushinteger(L, HONEY_KEY_P);
+ lua_setfield(L, -2, "honey_key_p");
+
+ lua_pushinteger(L, HONEY_KEY_Q);
+ lua_setfield(L, -2, "honey_key_q");
+
+ lua_pushinteger(L, HONEY_KEY_R);
+ lua_setfield(L, -2, "honey_key_r");
+
+ lua_pushinteger(L, HONEY_KEY_S);
+ lua_setfield(L, -2, "honey_key_s");
+
+ lua_pushinteger(L, HONEY_KEY_T);
+ lua_setfield(L, -2, "honey_key_t");
+
+ lua_pushinteger(L, HONEY_KEY_U);
+ lua_setfield(L, -2, "honey_key_u");
+
+ lua_pushinteger(L, HONEY_KEY_V);
+ lua_setfield(L, -2, "honey_key_v");
+
+ lua_pushinteger(L, HONEY_KEY_W);
+ lua_setfield(L, -2, "honey_key_w");
+
+ lua_pushinteger(L, HONEY_KEY_X);
+ lua_setfield(L, -2, "honey_key_x");
+
+ lua_pushinteger(L, HONEY_KEY_Y);
+ lua_setfield(L, -2, "honey_key_y");
+
+ lua_pushinteger(L, HONEY_KEY_Z);
+ lua_setfield(L, -2, "honey_key_z");
+
+ lua_pushinteger(L, HONEY_KEY_LEFT_BRACKET);
+ lua_setfield(L, -2, "honey_key_left_bracket");
+
+ lua_pushinteger(L, HONEY_KEY_BACKSLASH);
+ lua_setfield(L, -2, "honey_key_backslash");
+
+ lua_pushinteger(L, HONEY_KEY_RIGHT_BRACKET);
+ lua_setfield(L, -2, "honey_key_right_bracket");
+
+ lua_pushinteger(L, HONEY_KEY_GRAVE_ACCENT);
+ lua_setfield(L, -2, "honey_key_grave_accent");
+
+ lua_pushinteger(L, HONEY_KEY_WORLD_1);
+ lua_setfield(L, -2, "honey_key_world_1");
+
+ lua_pushinteger(L, HONEY_KEY_WORLD_2);
+ lua_setfield(L, -2, "honey_key_world_2");
+
+ lua_pushinteger(L, HONEY_KEY_ESCAPE);
+ lua_setfield(L, -2, "honey_key_escape");
+
+ lua_pushinteger(L, HONEY_KEY_ENTER);
+ lua_setfield(L, -2, "honey_key_enter");
+
+ lua_pushinteger(L, HONEY_KEY_TAB);
+ lua_setfield(L, -2, "honey_key_tab");
+
+ lua_pushinteger(L, HONEY_KEY_BACKSPACE);
+ lua_setfield(L, -2, "honey_key_backspace");
+
+ lua_pushinteger(L, HONEY_KEY_INSERT);
+ lua_setfield(L, -2, "honey_key_insert");
+
+ lua_pushinteger(L, HONEY_KEY_DELETE);
+ lua_setfield(L, -2, "honey_key_delete");
+
+ lua_pushinteger(L, HONEY_KEY_RIGHT);
+ lua_setfield(L, -2, "honey_key_right");
+
+ lua_pushinteger(L, HONEY_KEY_LEFT);
+ lua_setfield(L, -2, "honey_key_left");
+
+ lua_pushinteger(L, HONEY_KEY_DOWN);
+ lua_setfield(L, -2, "honey_key_down");
+
+ lua_pushinteger(L, HONEY_KEY_UP);
+ lua_setfield(L, -2, "honey_key_up");
+
+ lua_pushinteger(L, HONEY_KEY_PAGE_UP);
+ lua_setfield(L, -2, "honey_key_page_up");
+
+ lua_pushinteger(L, HONEY_KEY_PAGE_DOWN);
+ lua_setfield(L, -2, "honey_key_page_down");
+
+ lua_pushinteger(L, HONEY_KEY_HOME);
+ lua_setfield(L, -2, "honey_key_home");
+
+ lua_pushinteger(L, HONEY_KEY_END);
+ lua_setfield(L, -2, "honey_key_end");
+
+ lua_pushinteger(L, HONEY_KEY_CAPS_LOCK);
+ lua_setfield(L, -2, "honey_key_caps_lock");
+
+ lua_pushinteger(L, HONEY_KEY_SCROLL_LOCK);
+ lua_setfield(L, -2, "honey_key_scroll_lock");
+
+ lua_pushinteger(L, HONEY_KEY_NUM_LOCK);
+ lua_setfield(L, -2, "honey_key_num_lock");
+
+ lua_pushinteger(L, HONEY_KEY_PRINT_SCREEN);
+ lua_setfield(L, -2, "honey_key_print_screen");
+
+ lua_pushinteger(L, HONEY_KEY_PAUSE);
+ lua_setfield(L, -2, "honey_key_pause");
+
+ lua_pushinteger(L, HONEY_KEY_F1);
+ lua_setfield(L, -2, "honey_key_f1");
+
+ lua_pushinteger(L, HONEY_KEY_F2);
+ lua_setfield(L, -2, "honey_key_f2");
+
+ lua_pushinteger(L, HONEY_KEY_F3);
+ lua_setfield(L, -2, "honey_key_f3");
+
+ lua_pushinteger(L, HONEY_KEY_F4);
+ lua_setfield(L, -2, "honey_key_f4");
+
+ lua_pushinteger(L, HONEY_KEY_F5);
+ lua_setfield(L, -2, "honey_key_f5");
+
+ lua_pushinteger(L, HONEY_KEY_F6);
+ lua_setfield(L, -2, "honey_key_f6");
+
+ lua_pushinteger(L, HONEY_KEY_F7);
+ lua_setfield(L, -2, "honey_key_f7");
+
+ lua_pushinteger(L, HONEY_KEY_F8);
+ lua_setfield(L, -2, "honey_key_f8");
+
+ lua_pushinteger(L, HONEY_KEY_F9);
+ lua_setfield(L, -2, "honey_key_f9");
+
+ lua_pushinteger(L, HONEY_KEY_F10);
+ lua_setfield(L, -2, "honey_key_f10");
+
+ lua_pushinteger(L, HONEY_KEY_F11);
+ lua_setfield(L, -2, "honey_key_f11");
+
+ lua_pushinteger(L, HONEY_KEY_F12);
+ lua_setfield(L, -2, "honey_key_f12");
+
+ lua_pushinteger(L, HONEY_KEY_F13);
+ lua_setfield(L, -2, "honey_key_f13");
+
+ lua_pushinteger(L, HONEY_KEY_F14);
+ lua_setfield(L, -2, "honey_key_f14");
+
+ lua_pushinteger(L, HONEY_KEY_F15);
+ lua_setfield(L, -2, "honey_key_f15");
+
+ lua_pushinteger(L, HONEY_KEY_F16);
+ lua_setfield(L, -2, "honey_key_f16");
+
+ lua_pushinteger(L, HONEY_KEY_F17);
+ lua_setfield(L, -2, "honey_key_f17");
+
+ lua_pushinteger(L, HONEY_KEY_F18);
+ lua_setfield(L, -2, "honey_key_f18");
+
+ lua_pushinteger(L, HONEY_KEY_F19);
+ lua_setfield(L, -2, "honey_key_f19");
+
+ lua_pushinteger(L, HONEY_KEY_F20);
+ lua_setfield(L, -2, "honey_key_f20");
+
+ lua_pushinteger(L, HONEY_KEY_F21);
+ lua_setfield(L, -2, "honey_key_f21");
+
+ lua_pushinteger(L, HONEY_KEY_F22);
+ lua_setfield(L, -2, "honey_key_f22");
+
+ lua_pushinteger(L, HONEY_KEY_F23);
+ lua_setfield(L, -2, "honey_key_f23");
+
+ lua_pushinteger(L, HONEY_KEY_F24);
+ lua_setfield(L, -2, "honey_key_f24");
+
+ lua_pushinteger(L, HONEY_KEY_F25);
+ lua_setfield(L, -2, "honey_key_f25");
+
+ lua_pushinteger(L, HONEY_KEY_KP_0);
+ lua_setfield(L, -2, "honey_key_kp_0");
+
+ lua_pushinteger(L, HONEY_KEY_KP_1);
+ lua_setfield(L, -2, "honey_key_kp_1");
+
+ lua_pushinteger(L, HONEY_KEY_KP_2);
+ lua_setfield(L, -2, "honey_key_kp_2");
+
+ lua_pushinteger(L, HONEY_KEY_KP_3);
+ lua_setfield(L, -2, "honey_key_kp_3");
+
+ lua_pushinteger(L, HONEY_KEY_KP_4);
+ lua_setfield(L, -2, "honey_key_kp_4");
+
+ lua_pushinteger(L, HONEY_KEY_KP_5);
+ lua_setfield(L, -2, "honey_key_kp_5");
+
+ lua_pushinteger(L, HONEY_KEY_KP_6);
+ lua_setfield(L, -2, "honey_key_kp_6");
+
+ lua_pushinteger(L, HONEY_KEY_KP_7);
+ lua_setfield(L, -2, "honey_key_kp_7");
+
+ lua_pushinteger(L, HONEY_KEY_KP_8);
+ lua_setfield(L, -2, "honey_key_kp_8");
+
+ lua_pushinteger(L, HONEY_KEY_KP_9);
+ lua_setfield(L, -2, "honey_key_kp_9");
+
+ lua_pushinteger(L, HONEY_KEY_KP_DECIMAL);
+ lua_setfield(L, -2, "honey_key_kp_decimal");
+
+ lua_pushinteger(L, HONEY_KEY_KP_DIVIDE);
+ lua_setfield(L, -2, "honey_key_kp_divide");
+
+ lua_pushinteger(L, HONEY_KEY_KP_MULTIPLY);
+ lua_setfield(L, -2, "honey_key_kp_multiply");
+
+ lua_pushinteger(L, HONEY_KEY_KP_SUBTRACT);
+ lua_setfield(L, -2, "honey_key_kp_subtract");
+
+ lua_pushinteger(L, HONEY_KEY_KP_ADD);
+ lua_setfield(L, -2, "honey_key_kp_add");
+
+ lua_pushinteger(L, HONEY_KEY_KP_ENTER);
+ lua_setfield(L, -2, "honey_key_kp_enter");
+
+ lua_pushinteger(L, HONEY_KEY_KP_EQUAL);
+ lua_setfield(L, -2, "honey_key_kp_equal");
+
+ lua_pushinteger(L, HONEY_KEY_LEFT_SHIFT);
+ lua_setfield(L, -2, "honey_key_left_shift");
+
+ lua_pushinteger(L, HONEY_KEY_LEFT_CONTROL);
+ lua_setfield(L, -2, "honey_key_left_control");
+
+ lua_pushinteger(L, HONEY_KEY_LEFT_ALT);
+ lua_setfield(L, -2, "honey_key_left_alt");
+
+ lua_pushinteger(L, HONEY_KEY_LEFT_SUPER);
+ lua_setfield(L, -2, "honey_key_left_super");
+
+ lua_pushinteger(L, HONEY_KEY_RIGHT_SHIFT);
+ lua_setfield(L, -2, "honey_key_right_shift");
+
+ lua_pushinteger(L, HONEY_KEY_RIGHT_CONTROL);
+ lua_setfield(L, -2, "honey_key_right_control");
+
+ lua_pushinteger(L, HONEY_KEY_RIGHT_ALT);
+ lua_setfield(L, -2, "honey_key_right_alt");
+
+ lua_pushinteger(L, HONEY_KEY_RIGHT_SUPER);
+ lua_setfield(L, -2, "honey_key_right_super");
+
+ lua_pushinteger(L, HONEY_KEY_MENU);
+ lua_setfield(L, -2, "honey_key_menu");
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+bool honey_key_down(int key)
+{
+ if (key < 0 || key >= HONEY_N_KEYS) {
+ return false;
+ }
+ return (bool) honey_key_states[key];
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_key_bind(int key,
+ void (*callback)(void*, int),
+ void* data)
+{
+ if (key >= 0 && key < HONEY_N_KEYS) {
+ honey_key_callbacks[key] = callback;
+ honey_key_callbacks_data[key] = data;
+ }
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_key_unbind(int key)
+{
+ if (key >=0 && key < HONEY_N_KEYS) {
+ honey_key_callbacks[key] = NULL;
+ honey_key_callbacks_data[key] = NULL;
+ }
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_key_unbind_all()
+{
+ for (int i=0; i<HONEY_N_KEYS; i++) {
+ honey_key_callbacks[i] = NULL;
+ honey_key_callbacks_data[i] = NULL;
+ }
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void default_honey_keyboard_callback(honey_window window, int key, int scancode, int action, int mods)
+{
+ if (key >= 0 && key < HONEY_N_KEYS) {
+ if (action == HONEY_KEY_PRESS) { honey_key_states[key] = 1; }
+ if (action == HONEY_KEY_RELEASE) { honey_key_states[key] = 0; }
+ if (honey_key_callbacks[key] != NULL) {
+ honey_key_callbacks[key](honey_key_callbacks_data[key], action);
+ }
+ }
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
diff --git a/src/input/input.h b/src/input/input.h
new file mode 100644
index 0000000..a8ae206
--- /dev/null
+++ b/src/input/input.h
@@ -0,0 +1,194 @@
+#ifndef HONEY_INPUT_H
+#define HONEY_INPUT_H
+
+/** @file input.h
+ *
+ * @brief Wrap GLFW input functions for honey.
+ */
+
+#include "../common.h"
+
+#define HONEY_KEY_UNKNOWN 0
+#define HONEY_KEY_SPACE 1
+#define HONEY_KEY_APOSTROPHE 2
+#define HONEY_KEY_COMMA 3
+#define HONEY_KEY_MINUS 4
+#define HONEY_KEY_PERIOD 5
+#define HONEY_KEY_SLASH 6
+#define HONEY_KEY_0 7
+#define HONEY_KEY_1 8
+#define HONEY_KEY_2 9
+#define HONEY_KEY_3 10
+#define HONEY_KEY_4 11
+#define HONEY_KEY_5 12
+#define HONEY_KEY_6 13
+#define HONEY_KEY_7 14
+#define HONEY_KEY_8 15
+#define HONEY_KEY_9 16
+#define HONEY_KEY_SEMICOLON 17
+#define HONEY_KEY_EQUAL 18
+#define HONEY_KEY_A 19
+#define HONEY_KEY_B 20
+#define HONEY_KEY_C 21
+#define HONEY_KEY_D 22
+#define HONEY_KEY_E 23
+#define HONEY_KEY_F 24
+#define HONEY_KEY_G 25
+#define HONEY_KEY_H 26
+#define HONEY_KEY_I 27
+#define HONEY_KEY_J 28
+#define HONEY_KEY_K 29
+#define HONEY_KEY_L 30
+#define HONEY_KEY_M 31
+#define HONEY_KEY_N 32
+#define HONEY_KEY_O 33
+#define HONEY_KEY_P 34
+#define HONEY_KEY_Q 35
+#define HONEY_KEY_R 36
+#define HONEY_KEY_S 37
+#define HONEY_KEY_T 38
+#define HONEY_KEY_U 39
+#define HONEY_KEY_V 40
+#define HONEY_KEY_W 41
+#define HONEY_KEY_X 42
+#define HONEY_KEY_Y 43
+#define HONEY_KEY_Z 44
+#define HONEY_KEY_LEFT_BRACKET 45
+#define HONEY_KEY_BACKSLASH 46
+#define HONEY_KEY_RIGHT_BRACKET 47
+#define HONEY_KEY_GRAVE_ACCENT 48
+#define HONEY_KEY_WORLD_1 49
+#define HONEY_KEY_WORLD_2 50
+#define HONEY_KEY_ESCAPE 51
+#define HONEY_KEY_ENTER 52
+#define HONEY_KEY_TAB 53
+#define HONEY_KEY_BACKSPACE 54
+#define HONEY_KEY_INSERT 55
+#define HONEY_KEY_DELETE 56
+#define HONEY_KEY_RIGHT 57
+#define HONEY_KEY_LEFT 58
+#define HONEY_KEY_DOWN 59
+#define HONEY_KEY_UP 60
+#define HONEY_KEY_PAGE_UP 61
+#define HONEY_KEY_PAGE_DOWN 62
+#define HONEY_KEY_HOME 63
+#define HONEY_KEY_END 64
+#define HONEY_KEY_CAPS_LOCK 65
+#define HONEY_KEY_SCROLL_LOCK 66
+#define HONEY_KEY_NUM_LOCK 67
+#define HONEY_KEY_PRINT_SCREEN 68
+#define HONEY_KEY_PAUSE 69
+#define HONEY_KEY_F1 70
+#define HONEY_KEY_F2 71
+#define HONEY_KEY_F3 72
+#define HONEY_KEY_F4 73
+#define HONEY_KEY_F5 74
+#define HONEY_KEY_F6 75
+#define HONEY_KEY_F7 76
+#define HONEY_KEY_F8 77
+#define HONEY_KEY_F9 78
+#define HONEY_KEY_F10 79
+#define HONEY_KEY_F11 80
+#define HONEY_KEY_F12 81
+#define HONEY_KEY_F13 82
+#define HONEY_KEY_F14 83
+#define HONEY_KEY_F15 84
+#define HONEY_KEY_F16 85
+#define HONEY_KEY_F17 86
+#define HONEY_KEY_F18 87
+#define HONEY_KEY_F19 88
+#define HONEY_KEY_F20 89
+#define HONEY_KEY_F21 90
+#define HONEY_KEY_F22 91
+#define HONEY_KEY_F23 92
+#define HONEY_KEY_F24 93
+#define HONEY_KEY_F25 94
+#define HONEY_KEY_KP_0 95
+#define HONEY_KEY_KP_1 96
+#define HONEY_KEY_KP_2 97
+#define HONEY_KEY_KP_3 98
+#define HONEY_KEY_KP_4 99
+#define HONEY_KEY_KP_5 100
+#define HONEY_KEY_KP_6 101
+#define HONEY_KEY_KP_7 102
+#define HONEY_KEY_KP_8 103
+#define HONEY_KEY_KP_9 104
+#define HONEY_KEY_KP_DECIMAL 105
+#define HONEY_KEY_KP_DIVIDE 106
+#define HONEY_KEY_KP_MULTIPLY 107
+#define HONEY_KEY_KP_SUBTRACT 108
+#define HONEY_KEY_KP_ADD 109
+#define HONEY_KEY_KP_ENTER 110
+#define HONEY_KEY_KP_EQUAL 111
+#define HONEY_KEY_LEFT_SHIFT 112
+#define HONEY_KEY_LEFT_CONTROL 113
+#define HONEY_KEY_LEFT_ALT 114
+#define HONEY_KEY_LEFT_SUPER 115
+#define HONEY_KEY_RIGHT_SHIFT 116
+#define HONEY_KEY_RIGHT_CONTROL 117
+#define HONEY_KEY_RIGHT_ALT 118
+#define HONEY_KEY_RIGHT_SUPER 119
+#define HONEY_KEY_MENU 120
+
+#define HONEY_N_KEYS 121
+
+#define HONEY_KEY_PRESS GLFW_PRESS
+#define HONEY_KEY_RELEASE GLFW_RELEASE
+
+unsigned int honey_key_states[HONEY_N_KEYS];
+static void (*honey_key_callbacks[HONEY_N_KEYS])(void*, int);
+static void* honey_key_callbacks_data[HONEY_N_KEYS];
+static void (*honey_keyboard_callback)(honey_window window, int key, int scancode, int action, int mods);
+
+/** @brief Initializes Honey's internal keyboard states.
+ *
+ * This function is called by honey_setup, so you shouldn't need
+ * to manually call it in most cases.
+ */
+void honey_setup_keyboard();
+
+/* @brief Place a lua table containing the enum values for GLFW keys on the stack.
+ *
+ * @param L The lua state to place the table on the top of the stack.
+ *
+ * @returns Nothing.
+ */
+void honey_setup_input(lua_State* L);
+
+/** @brief Check if a key is down.
+ *
+ * @param[in] key The key to query.
+ *
+ * @return TRUE if the key is presently held down; FALSE if it is not, or if the keycode was out of bounds.
+ */
+bool honey_key_down(int key);
+
+/** @brief Bind a callback to a key.
+ *
+ * The callback must have signature (void*, int), where the first argument is supplied by the
+ * void* supplied with this function, and the int is one of HONEY_KEY_PRESS or HONEY_KEY_RELEASE,
+ * depending on the action that triggered the callback.
+ *
+ * @param[in] key The key to bind the callback to.
+ * @param[in] callback The callback function to bind.
+ * @param[in] data Data to pass to the callback function upon triggering.
+ */
+void honey_key_bind(int key, void (*callback)(void*, int), void* data);
+
+/** @brief Unbind a key callback.
+ *
+ * THIS FUNCTION DOES NOT FREE MEMORY! If you allocated any, be sure to free it yourself!
+ *
+ * @param[in] key The key to unbind a callback from.
+ */
+void honey_key_unbind(int key);
+
+/** @brief Unbind all key callbacks.
+ *
+ * THIS FUNCTION DOES NOT FREE MEMORY! If you allocated any, be sure to free it yourself!
+ */
+void honey_key_unbind_all();
+
+void default_honey_keyboard_callback(honey_window window, int key, int scancode, int action, int mods);
+
+#endif
diff --git a/src/light/light.c b/src/light/light.c
new file mode 100644
index 0000000..839d747
--- /dev/null
+++ b/src/light/light.c
@@ -0,0 +1,20 @@
+#include "light.h"
+
+void honey_point_light_new(honey_point_light* light,
+ float x, float y, float z,
+ float r, float g, float b,
+ float constant,
+ float linear,
+ float quadratic) {
+ light->position[0] = x;
+ light->position[1] = y;
+ light->position[2] = z;
+
+ light->color[0] = r;
+ light->color[1] = g;
+ light->color[2] = b;
+
+ light->constant = constant;
+ light->linear = linear;
+ light->quadratic = quadratic;
+}
diff --git a/src/light/light.h b/src/light/light.h
new file mode 100644
index 0000000..dfb6979
--- /dev/null
+++ b/src/light/light.h
@@ -0,0 +1,33 @@
+#ifndef HONEY_LIGHT_H
+#define HONEY_LIGHT_H
+
+#include "../common.h"
+
+#define HONEY_MAX_LIGHT_NAME_LENGTH 64
+
+typedef struct {
+ vec3 position;
+ vec3 color;
+
+ float constant;
+ float linear;
+ float quadratic;
+} honey_point_light;
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+typedef struct {
+ vec3 direction;
+ vec3 color;
+} honey_directional_light;
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_point_light_new(honey_point_light* light,
+ float x, float y, float z,
+ float r, float g, float b,
+ float constant,
+ float linear,
+ float quadratic);
+
+#endif
diff --git a/src/main.c b/src/main.c
index 8cfcfaa..e3e98b0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,21 +1,4 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-
-#include <unistd.h>
-
-#include <lua.h>
-#include <lualib.h>
-#include <lauxlib.h>
-
-#include "glad.h"
-#include <GLFW/glfw3.h>
-
-#include <cglm/cglm.h>
-#include <cglm/call.h>
-
-#include "input.h"
+#include "honey.h"
struct options {
bool verbose;
diff --git a/src/mesh/mesh.c b/src/mesh/mesh.c
new file mode 100644
index 0000000..231fcc9
--- /dev/null
+++ b/src/mesh/mesh.c
@@ -0,0 +1,86 @@
+#include "mesh.h"
+
+honey_result honey_mesh_new(honey_mesh* mesh,
+ float* vertices,
+ unsigned int n_vertices,
+ unsigned int n_attributes,
+ unsigned int* attribute_sizes,
+ unsigned int* indices,
+ unsigned int n_indices) {
+ if (vertices == NULL || n_vertices == 0) {
+ return HONEY_MESH_BAD_VERTEX_DATA;
+ }
+ if (indices == NULL || n_indices == 0) {
+ return HONEY_MESH_BAD_INDEX_DATA;
+ }
+
+ unsigned int vertex_size = 0;
+ for (int i=0; i<n_attributes; i++) {
+ vertex_size += attribute_sizes[i];
+ }
+
+ (*mesh).vertices = malloc(vertex_size*n_vertices * sizeof(float));
+ if ((*mesh).vertices == NULL) {
+ return HONEY_MEMORY_ALLOCATION_ERROR;
+ }
+ memcpy((*mesh).vertices, vertices, vertex_size*n_vertices*sizeof(float));
+
+ (*mesh).indices = malloc(n_indices * sizeof(unsigned int));
+ if ((*mesh).indices == NULL) {
+ return HONEY_MEMORY_ALLOCATION_ERROR;
+ }
+ memcpy((*mesh).indices, indices, n_indices * sizeof(unsigned int));
+
+ (*mesh).n_vertices = n_vertices;
+ (*mesh).n_indices = n_indices;
+
+ glGenVertexArrays(1, &((*mesh).vertex_array));
+ glGenBuffers(1, &((*mesh).vertex_buffer));
+ glGenBuffers(1, &((*mesh).element_buffer));
+
+ glBindVertexArray((*mesh).vertex_array);
+
+ glBindBuffer(GL_ARRAY_BUFFER, (*mesh).vertex_buffer);
+ glBufferData(GL_ARRAY_BUFFER, vertex_size*n_vertices*sizeof(float), (*mesh).vertices, GL_STATIC_DRAW);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (*mesh).element_buffer);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, n_indices * sizeof(unsigned int), (*mesh).indices, GL_STATIC_DRAW);
+
+ /* set up vertex attributes */
+ unsigned int offset = 0;
+ for (int i=0; i<n_attributes; i++) {
+ glEnableVertexAttribArray(i);
+ glVertexAttribPointer(i,
+ attribute_sizes[i],
+ GL_FLOAT,
+ GL_FALSE,
+ vertex_size*sizeof(float),
+ (void*) (offset*sizeof(float)));
+ offset += attribute_sizes[i];
+ }
+
+ glBindVertexArray(0);
+
+ return HONEY_OK;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_mesh_draw(honey_mesh mesh, honey_shader shader) {
+ honey_shader_use(shader);
+
+ glBindVertexArray(mesh.vertex_array);
+ glDrawElements(GL_TRIANGLES, mesh.n_indices, GL_UNSIGNED_INT, 0);
+ glBindVertexArray(0);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_mesh_delete(honey_mesh mesh) {
+ free(mesh.vertices);
+ free(mesh.indices);
+
+ glDeleteVertexArrays(1, &(mesh.vertex_array));
+ glDeleteBuffers(1, &(mesh.vertex_buffer));
+ glDeleteBuffers(1, &(mesh.element_buffer));
+}
diff --git a/src/mesh/mesh.h b/src/mesh/mesh.h
new file mode 100644
index 0000000..a3e1e2a
--- /dev/null
+++ b/src/mesh/mesh.h
@@ -0,0 +1,57 @@
+#ifndef HONEY_MESH_H
+#define HONEY_MESH_H
+
+/** @file mesh.h
+ *
+ * @brief Defines the honey_mesh struct and related basic mesh functions.
+*/
+
+
+
+#include "../common.h"
+#include "../shader/shader.h"
+
+typedef struct {
+ float* vertices;
+ unsigned int n_vertices;
+ unsigned int* indices;
+ unsigned int n_indices;
+ unsigned int vertex_array, vertex_buffer, element_buffer;
+} honey_mesh;
+
+/** @brief Create a new mesh from vertex and index arrays.
+ *
+ * Note that this function creates copies of the vertex and index arrays,
+ * so you can deallocate those immediately.
+ *
+ * @param[out] mesh Pointer to the destination honey_mesh struct
+ * @param[in] vertices Array of floats representing the vertices
+ * @param[in] n_attributes The number of attributes per vertex
+ * @param[in] attribute_sizes An array containing for each attribute how many floats it contains
+ * @param[in] n_vertices The number of vertices (NOT the number of floats in the vertex array)
+ * @param[in] indices Array of vertex indices
+ * @param[in] n_indices The number of elements in the index array
+ */
+honey_result honey_mesh_new(honey_mesh* mesh,
+ float* vertices,
+ unsigned int n_vertices,
+ unsigned int n_attributes,
+ unsigned int* attribute_sizes,
+ unsigned int* indices,
+ unsigned int n_indices);
+
+/** @brief Draw a mesh on screen.
+ *
+ * @param[in] mesh The mesh to draw
+ * @param[in] shader The shader to use when drawing the mesh
+ */
+void honey_mesh_draw(honey_mesh mesh,
+ honey_shader shader);
+
+/** @brief Delete a mesh.
+ *
+ * @param[in] mesh The mesh to delete
+ */
+void honey_mesh_delete(honey_mesh mesh);
+
+#endif
diff --git a/src/model/model.c b/src/model/model.c
new file mode 100644
index 0000000..b317f15
--- /dev/null
+++ b/src/model/model.c
@@ -0,0 +1,121 @@
+#include "model.h"
+
+static honey_mesh assimp_to_honey_mesh(struct aiMesh* mesh, struct aiScene* scene) {
+ unsigned int vertex_step = 6;
+ bool mesh_has_uvs = false;
+ unsigned int n_vertices = mesh->mNumVertices;
+
+ if (mesh->mTextureCoords[0]) {
+ mesh_has_uvs = true;
+ vertex_step = 8;
+ }
+
+ float* vertices = malloc(sizeof(float) * vertex_step * n_vertices);
+ for (int i=0; i<n_vertices; i++) {
+ int j = i*vertex_step;
+ /* positions */
+ vertices[j+0] = mesh->mVertices[i].x;
+ vertices[j+1] = mesh->mVertices[i].y;
+ vertices[j+2] = mesh->mVertices[i].z;
+
+ /* normals */
+ vertices[j+3] = mesh->mNormals[i].x;
+ vertices[j+4] = mesh->mNormals[i].y;
+ vertices[j+5] = mesh->mNormals[i].z;
+
+ /* uvs? */
+ if (mesh_has_uvs) {
+ vertices[j+6] = mesh->mTextureCoords[0][i].x;
+ vertices[j+7] = mesh->mTextureCoords[0][i].y;
+ }
+ }
+
+ unsigned int n_indices = mesh->mNumFaces*3;
+ unsigned int* indices = malloc(sizeof(unsigned int) * n_indices);
+ for (int i=0; i<mesh->mNumFaces; i++) {
+ int j = 3*i;
+ struct aiFace face = mesh->mFaces[i];
+ indices[j+0] = face.mIndices[0];
+ indices[j+1] = face.mIndices[1];
+ indices[j+2] = face.mIndices[2];
+ }
+
+ honey_mesh result;
+
+ if (mesh_has_uvs) {
+ unsigned int n_attributes = 3;
+ unsigned int attribute_sizes[] = { 3, 3, 2 };
+ honey_mesh_new(&result,
+ vertices, n_vertices,
+ n_attributes, attribute_sizes,
+ indices, n_indices);
+ }
+ else {
+ unsigned int n_attributes = 2;
+ unsigned int attribute_sizes[] = { 3, 3 };
+ honey_mesh_new(&result,
+ vertices, n_vertices,
+ n_attributes, attribute_sizes,
+ indices, n_indices);
+ }
+
+ free(vertices);
+ free(indices);
+
+ return result;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+static void process_assimp_node(honey_model* model, struct aiNode* node, struct aiScene* scene) {
+ for (int i=0; i<node->mNumMeshes; i++) {
+ struct aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
+ model->meshes[model->n_meshes] = assimp_to_honey_mesh(mesh, scene);
+ model->n_meshes++;
+ }
+
+ for (int i=0; i<node->mNumChildren; i++) {
+ process_assimp_node(model, node->mChildren[i], scene);
+ }
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+honey_result honey_model_load(honey_model* model,
+ char* path) {
+ model->n_meshes = 0;
+
+ struct aiScene* scene = aiImportFile(path,
+ aiProcess_Triangulate |
+ aiProcess_FlipUVs);
+ if (scene == NULL) {
+ honey_error_set_string1(path);
+ return HONEY_FILE_READ_ERROR;
+ }
+
+ if (scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE ||
+ scene->mRootNode == NULL) {
+ honey_error_set_string1(path);
+ return HONEY_MODEL_LOAD_ERROR;
+ }
+
+ process_assimp_node(model, scene->mRootNode, scene);
+
+ return HONEY_OK;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_model_draw(honey_model* model, honey_shader shader) {
+ for (int i=0; i<model->n_meshes; i++) {
+ honey_mesh_draw(model->meshes[i], shader);
+ }
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_model_delete(honey_model* model) {
+ for (int i=0; i<model->n_meshes; i++) {
+ honey_mesh_delete(model->meshes[i]);
+ }
+}
diff --git a/src/model/model.h b/src/model/model.h
new file mode 100644
index 0000000..3672a44
--- /dev/null
+++ b/src/model/model.h
@@ -0,0 +1,29 @@
+#ifndef HONEY_MODEL_H
+#define HONEY_MODEL_H
+
+#include "../common.h"
+#include "../mesh/mesh.h"
+#include "../shader/shader.h"
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+#define HONEY_MODEL_MAX_MESHES 32
+
+typedef struct {
+ mat4 model_matrix;
+ honey_mesh meshes[HONEY_MODEL_MAX_MESHES];
+ unsigned int n_meshes;
+} honey_model;
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+/** @brief Load a model.
+ *
+ * @param[out] model Pointer to the destination honey_model struct.
+ * @param[in] path Path of the model to be loaded.
+ */
+honey_result honey_model_load(honey_model* model, char* path);
+void honey_model_draw(honey_model* model, honey_shader shader);
+void honey_model_delete(honey_model* model);
+
+#endif
diff --git a/src/primitives/primitives.c b/src/primitives/primitives.c
new file mode 100644
index 0000000..efc4a0f
--- /dev/null
+++ b/src/primitives/primitives.c
@@ -0,0 +1,180 @@
+#include "primitives.h"
+
+honey_result honey_mesh_new_textured_plane(honey_mesh* mesh,
+ float width,
+ float height) {
+ float x0 = 0;
+ float y0 = 0;
+ float x1 = width;
+ float y1 = height;
+
+ float vertices[] = {
+ /* position normal uv */
+ x0, y0, 0, 0, 0, 1, 0, 0,
+ x1, y0, 0, 0, 0, 1, 1, 0,
+ x0, y1, 0, 0, 0, 1, 0, 1,
+ x1, y1, 0, 0, 0, 1, 1, 1 };
+
+ unsigned int indices[] = {
+ 0, 1, 2,
+ 1, 2, 3 };
+
+ unsigned int attrib_sizes[] = { 3, 3, 2 };
+
+ honey_result result = honey_mesh_new(mesh,
+ vertices, 4,
+ 3, attrib_sizes,
+ indices, 6);
+ return result;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+honey_result honey_mesh_new_cube(honey_mesh* mesh,
+ float width,
+ float height,
+ float depth) {
+ float x0 = 0;
+ float y0 = 0;
+ float z0 = 0;
+
+ float x1 = width;
+ float y1 = height;
+ float z1 = depth;
+
+ float vertices[] = {
+ /* position normal tex coord */
+ /* back face */
+ x0, y0, z0, 0, 0, -1,
+ x1, y0, z0, 0, 0, -1,
+ x0, y1, z0, 0, 0, -1,
+ x1, y1, z0, 0, 0, -1,
+
+ /* front face */
+ x0, y0, z1, 0, 0, 1,
+ x1, y0, z1, 0, 0, 1,
+ x0, y1, z1, 0, 0, 1,
+ x1, y1, z1, 0, 0, 1,
+
+ /* left face */
+ x0, y0, z0, -1, 0, 0,
+ x0, y1, z0, -1, 0, 0,
+ x0, y0, z1, -1, 0, 0,
+ x0, y1, z1, -1, 0, 0,
+
+ /* right face */
+ x1, y0, z0, 1, 0, 0,
+ x1, y1, z0, 1, 0, 0,
+ x1, y0, z1, 1, 0, 0,
+ x1, y1, z1, 1, 0, 0,
+
+ /* bottom face */
+ x0, y0, z0, 0, -1, 0,
+ x1, y0, z0, 0, -1, 0,
+ x0, y0, z1, 0, -1, 0,
+ x1, y0, z1, 0, -1, 0,
+
+ /* top face */
+ x0, y1, z0, 0, 1, 0,
+ x1, y1, z0, 0, 1, 0,
+ x0, y1, z1, 0, 1, 0,
+ x1, y1, z1, 0, 1, 0 };
+
+ unsigned int indices[] = {
+ 0, 1, 2,
+ 1, 2, 3,
+ 4, 5, 6,
+ 5, 6, 7,
+ 8, 9, 10,
+ 9, 10, 11,
+ 12, 13, 14,
+ 13, 14, 15,
+ 16, 17, 18,
+ 17, 18, 19,
+ 20, 21, 22,
+ 21, 22, 23 };
+
+ unsigned int attrib_sizes[] = { 3, 3 };
+
+ honey_result result = honey_mesh_new(mesh,
+ vertices, 24,
+ 2, attrib_sizes,
+ indices, 36);
+
+ return result;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+honey_result honey_mesh_new_textured_cube(honey_mesh* mesh,
+ float width,
+ float height,
+ float depth) {
+ float x0 = 0;
+ float y0 = 0;
+ float z0 = 0;
+
+ float x1 = width;
+ float y1 = height;
+ float z1 = depth;
+
+ float vertices[] = {
+ /* position normal tex coord */
+ /* back face */
+ x0, y0, z0, 0, 0, -1, 0, 0,
+ x1, y0, z0, 0, 0, -1, 1, 0,
+ x0, y1, z0, 0, 0, -1, 0, 1,
+ x1, y1, z0, 0, 0, -1, 1, 1,
+
+ /* front face */
+ x0, y0, z1, 0, 0, 1, 0, 0,
+ x1, y0, z1, 0, 0, 1, 1, 0,
+ x0, y1, z1, 0, 0, 1, 0, 1,
+ x1, y1, z1, 0, 0, 1, 1, 1,
+
+ /* left face */
+ x0, y0, z0, -1, 0, 0, 0, 0,
+ x0, y1, z0, -1, 0, 0, 1, 0,
+ x0, y0, z1, -1, 0, 0, 0, 1,
+ x0, y1, z1, -1, 0, 0, 1, 1,
+
+ /* right face */
+ x1, y0, z0, 1, 0, 0, 0, 0,
+ x1, y1, z0, 1, 0, 0, 1, 0,
+ x1, y0, z1, 1, 0, 0, 0, 1,
+ x1, y1, z1, 1, 0, 0, 1, 1,
+
+ /* bottom face */
+ x0, y0, z0, 0, -1, 0, 0, 0,
+ x1, y0, z0, 0, -1, 0, 1, 0,
+ x0, y0, z1, 0, -1, 0, 0, 1,
+ x1, y0, z1, 0, -1, 0, 1, 1,
+
+ /* top face */
+ x0, y1, z0, 0, 1, 0, 0, 0,
+ x1, y1, z0, 0, 1, 0, 1, 0,
+ x0, y1, z1, 0, 1, 0, 0, 1,
+ x1, y1, z1, 0, 1, 0, 1, 1 };
+
+ unsigned int indices[] = {
+ 0, 1, 2,
+ 1, 2, 3,
+ 4, 5, 6,
+ 5, 6, 7,
+ 8, 9, 10,
+ 9, 10, 11,
+ 12, 13, 14,
+ 13, 14, 15,
+ 16, 17, 18,
+ 17, 18, 19,
+ 20, 21, 22,
+ 21, 22, 23 };
+
+ unsigned int attrib_sizes[] = { 3, 3, 2 };
+
+ honey_result result;
+ result = honey_mesh_new(mesh, vertices, 24,
+ 3, attrib_sizes,
+ indices, 36);
+ return result;
+}
diff --git a/src/primitives/primitives.h b/src/primitives/primitives.h
new file mode 100644
index 0000000..fbf7753
--- /dev/null
+++ b/src/primitives/primitives.h
@@ -0,0 +1,59 @@
+#ifndef HONEY_PRIMITIVES_H
+#define HONEY_PRIMITIVES H
+
+/** @file primitives.h
+ *
+ * @brief Define various common primitive objects.
+ */
+
+#include "../common.h"
+#include "../mesh/mesh.h"
+
+/** @brief Create a textured plane.
+ *
+ * This function creates a plane with vertex positions in attribute 0,
+ * vertex normals in attribute 1, and UV coordinates in attribute 2.
+ *
+ * @param[out] mesh Pointer to the destination mesh
+ * @param[in] width Desired width of the plane (x-axis)
+ * @param[in] height Desired height of the plane (y-axis)
+ *
+ * @return 0 (HONEY_OK) on success, and an error code otherwise.
+ */
+honey_result honey_mesh_new_textured_plane(honey_mesh* mesh,
+ float width,
+ float height);
+
+/** @brief Create a cube.
+ *
+ * This function creates a cube with vertex positions in attribute 0.
+ *
+ * @param[out] mesh Pointer to the destination mesh
+ * @param[in] width Desired width of the cube (x-axis)
+ * @param[in] height Desired height of the cube (y-axis)
+ * @param[in] depth Desired depth of the cube (z-axis)
+ *
+ * @return Success or failure code
+ */
+honey_result honey_mesh_new_cube(honey_mesh* mesh,
+ float width,
+ float height,
+ float depth);
+/** @brief Create a textured cube.
+ *
+ * This function creates a cube with vertex positions in attribute 0,
+ * and texture coordinates in attribute 1.
+ *
+ * @param[out] mesh Pointer to the destination mesh
+ * @param[in] width Desired width of the cube (x-axis)
+ * @param[in] height Desired height of the cube (y-axis)
+ * @param[in] depth Desired depth of the cube (z-axis)
+ *
+ * @return Success or failure code
+ */
+honey_result honey_mesh_new_textured_cube(honey_mesh* mesh,
+ float width,
+ float height,
+ float depth);
+
+#endif
diff --git a/src/shader/shader.c b/src/shader/shader.c
new file mode 100644
index 0000000..817d451
--- /dev/null
+++ b/src/shader/shader.c
@@ -0,0 +1,224 @@
+#include "shader.h"
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+static honey_result read_file(char** destination, char* file_path) {
+ FILE* f = fopen(file_path, "r");
+ if (f == NULL) {
+ honey_error_set_string1(file_path);
+ return HONEY_FILE_READ_ERROR;
+ }
+
+ fseek(f, 0, SEEK_END);
+ long fsize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ *destination = malloc(fsize + 1);
+ if (*destination == NULL) {
+ return HONEY_MEMORY_ALLOCATION_ERROR;
+ }
+ fread(*destination, 1, fsize, f);
+ fclose(f);
+
+ (*destination)[fsize] = 0;
+
+ return HONEY_OK;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+honey_result honey_shader_load(honey_shader* shader,
+ char* vertex_shader_path,
+ char* fragment_shader_path) {
+ /* load vertex shader code */
+ char* vertex_shader_code;
+ honey_result result = read_file(&vertex_shader_code,
+ vertex_shader_path);
+ if (result != HONEY_OK)
+ return result;
+
+ /* load fragment shader code */
+ char* fragment_shader_code;
+ result = read_file(&fragment_shader_code,
+ fragment_shader_path);
+ if (result != HONEY_OK)
+ return result;
+
+ result = honey_shader_new(shader,
+ vertex_shader_code,
+ fragment_shader_code);
+
+ if (result == HONEY_VERTEX_SHADER_COMPILATION_ERROR)
+ honey_error_set_string2(vertex_shader_path);
+
+ if (result == HONEY_FRAGMENT_SHADER_COMPILATION_ERROR)
+ honey_error_set_string2(fragment_shader_path);
+
+ free(vertex_shader_code);
+ free(fragment_shader_code);
+
+ return result;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+honey_result honey_shader_new(honey_shader* shader,
+ char* vertex_shader_code,
+ char* fragment_shader_code) {
+ /* compile shaders */
+ int success;
+ char error[512];
+
+ int vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vertex_shader, 1, ((const char**)&vertex_shader_code), NULL);
+ glCompileShader(vertex_shader);
+ glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ honey_error_clear_strings();
+ char compiler_error[HONEY_ERROR_DATA_STRING_LENGTH];
+ glGetShaderInfoLog(vertex_shader, HONEY_ERROR_DATA_STRING_LENGTH, NULL, compiler_error);
+ honey_error_set_string1(compiler_error);
+ return HONEY_VERTEX_SHADER_COMPILATION_ERROR;
+ }
+
+ int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(fragment_shader, 1, ((const char**)&fragment_shader_code), NULL);
+ glCompileShader(fragment_shader);
+ glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ honey_error_clear_strings();
+ char compiler_error[HONEY_ERROR_DATA_STRING_LENGTH];
+ glGetShaderInfoLog(fragment_shader, HONEY_ERROR_DATA_STRING_LENGTH, NULL, compiler_error);
+ honey_error_set_string1(compiler_error);
+ return HONEY_FRAGMENT_SHADER_COMPILATION_ERROR;
+ }
+
+ /* link shaders */
+ *shader = glCreateProgram();
+ glAttachShader(*shader, vertex_shader);
+ glAttachShader(*shader, fragment_shader);
+ glLinkProgram(*shader);
+ glGetShaderiv(*shader, GL_LINK_STATUS, &success);
+ if (!success) {
+ honey_error_clear_strings();
+ char compiler_error[HONEY_ERROR_DATA_STRING_LENGTH];
+ glGetShaderInfoLog(vertex_shader, HONEY_ERROR_DATA_STRING_LENGTH, NULL, compiler_error);
+ honey_error_set_string1(compiler_error);
+ return HONEY_SHADER_LINK_ERROR;
+ }
+
+ glDeleteShader(vertex_shader);
+ glDeleteShader(fragment_shader);
+
+ return HONEY_OK;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_shader_set_int(honey_shader shader,
+ char* int_name,
+ int value) {
+ honey_shader_use(shader);
+ unsigned int int_location = glGetUniformLocation(shader, int_name);
+ glUniform1i(int_location, value);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_shader_set_float(honey_shader shader,
+ char* float_name,
+ float value) {
+ honey_shader_use(shader);
+ unsigned int float_location = glGetUniformLocation(shader, float_name);
+ glUniform1f(float_location, value);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_shader_set_vec3(honey_shader shader,
+ char* vector_name,
+ vec3 value) {
+ honey_shader_use(shader);
+ unsigned int vector_location = glGetUniformLocation(shader, vector_name);
+ glUniform3fv(vector_location, 1, (float*) value);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_shader_set_mat3(honey_shader shader,
+ char* matrix_name,
+ mat3 value) {
+ glUseProgram(shader);
+ unsigned int matrix_location = glGetUniformLocation(shader, matrix_name);
+ glUniformMatrix3fv(matrix_location, 1, GL_FALSE, (float*) value);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_shader_set_mat4(honey_shader shader,
+ char* matrix_name,
+ mat4 value) {
+ glUseProgram(shader);
+ unsigned int matrix_location = glGetUniformLocation(shader, matrix_name);
+ glUniformMatrix4fv(matrix_location, 1, GL_FALSE, (float*) value);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_shader_set_point_light(honey_shader shader,
+ int point_light_index,
+ honey_point_light light) {
+ char name[HONEY_MAX_LIGHT_NAME_LENGTH];
+
+ snprintf(name,
+ HONEY_MAX_LIGHT_NAME_LENGTH,
+ "point_lights[%d].position",
+ point_light_index);
+ honey_shader_set_vec3(shader, name, light.position);
+
+ snprintf(name,
+ HONEY_MAX_LIGHT_NAME_LENGTH,
+ "point_lights[%d].color",
+ point_light_index);
+ honey_shader_set_vec3(shader, name, light.color);
+
+ snprintf(name,
+ HONEY_MAX_LIGHT_NAME_LENGTH,
+ "point_lights[%d].constant",
+ point_light_index);
+ honey_shader_set_float(shader, name, light.constant);
+
+ snprintf(name,
+ HONEY_MAX_LIGHT_NAME_LENGTH,
+ "point_lights[%d].linear",
+ point_light_index);
+ honey_shader_set_float(shader, name, light.linear);
+
+ snprintf(name,
+ HONEY_MAX_LIGHT_NAME_LENGTH,
+ "point_lights[%d].quadratic",
+ point_light_index);
+ honey_shader_set_float(shader, name, light.quadratic);
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_shader_set_directional_light(honey_shader shader,
+ int directional_light_index,
+ honey_directional_light light) {
+ char name[HONEY_MAX_LIGHT_NAME_LENGTH];
+
+ snprintf(name,
+ HONEY_MAX_LIGHT_NAME_LENGTH,
+ "directional_lights[%d].direction",
+ directional_light_index);
+ honey_shader_set_vec3(shader, name, light.direction);
+
+ snprintf(name,
+ HONEY_MAX_LIGHT_NAME_LENGTH,
+ "directional_lights[%d].color",
+ directional_light_index);
+ honey_shader_set_vec3(shader, name, light.color);
+}
+
+
diff --git a/src/shader/shader.h b/src/shader/shader.h
new file mode 100644
index 0000000..07cc22d
--- /dev/null
+++ b/src/shader/shader.h
@@ -0,0 +1,117 @@
+/** @file shader.h
+ *
+ * @brief Functions to create, manipulate, and destroy GLSL shaders.
+ */
+
+#ifndef HONEY_SHADER_H
+#define HONEY_SHADER_H
+
+#include "../common.h"
+#include "../light/light.h"
+
+typedef int honey_shader;
+
+/** @brief Load a shader.
+ *
+ * @param[out] shader Pointer to the shader destination
+ *
+ * @param[in] vertex_shader_path The path to the vertex shader source code
+ * @param[in] fragment_shader_path The path to the fragment shader source code
+ *
+ * @return The result of the shader load.
+ */
+honey_result honey_shader_load(honey_shader* shader,
+ char* vertex_shader_path,
+ char* fragment_shader_path);
+
+/** @brief Create a shader from code strings.
+ *
+ * @param[out] shader Pointer to the shader destination.
+ * @param[in] vertex_shader_code Zero-terminated string containing the vertex shader code to compile
+ * @param[in] fragment_shader_code Zero-terminated string containing the fragment shader code to compile
+ *
+ * @return The result of the shader creation.
+ */
+honey_result honey_shader_new(honey_shader* shader,
+ char* vertex_shader_code,
+ char* fragment_shader_code);
+
+/** @brief Set an integer uniform.
+ *
+ * @param[in] shader The shader to which the uniform belongs
+ * @param[in] int_name The name of the integer uniform
+ * @param[in] value The value of the integer uniform
+ */
+void honey_shader_set_int(honey_shader shader,
+ char* int_name,
+ int value);
+
+/** @brief Set a float uniform.
+ *
+ * @param[in] shader The shader to which the uniform belongs
+ * @param[in] float_name The name of the float uniform
+ * @param[in] value The value of the float uniform
+ */
+void honey_shader_set_float(honey_shader shader,
+ char* float_name,
+ float value);
+
+/** @brief Set a vec3 uniform.
+ * @param[in] shader The shader to which the uniform belongs
+ * @param[in] vector_name The name of the vec3 uniform
+ * @param[in] value The value of the vector uniform
+ */
+void honey_shader_set_vec3(honey_shader shader,
+ char* vector_name,
+ vec3 value);
+
+
+/** @brief Set a mat3 uniform.
+ *
+ * @param[in] shader The shader to which the uniform belongs
+ * @param[in] matrix_name The name of the matrix uniform
+ * @param[in] value The value of the matrix uniform
+ */
+void honey_shader_set_mat3(honey_shader shader,
+ char* matrix_name,
+ mat3 value);
+
+/** @brief Set a mat4 uniform.
+ *
+ * @param[in] shader The shader to which the uniform belongs
+ * @param[in] matrix_name The name of the matrix uniform
+ * @param[in] value The value of the matrix uniform
+ */
+void honey_shader_set_mat4(honey_shader shader,
+ char* matrix_name,
+ mat4 value);
+
+/** @brief Set a point_light uniform.
+ *
+ * @param[in] shader The shader to which the uniform belongs
+ * @param[in] point_light_index The index of the light to set
+ * @param[in] light The honey_point_light to set
+ */
+void honey_shader_set_point_light(honey_shader shader,
+ int point_light_index,
+ honey_point_light light);
+
+/** @brief Set a directional_light uniform.
+ *
+ * @param[in] shader The shader to which the uniform belongs
+ * @param[in] directional_light_index The index of the light to set
+ * @param[in] light The honey_directional_light to set
+ */
+void honey_shader_set_directional_light(honey_shader shader,
+ int directional_light_index,
+ honey_directional_light light);
+
+/** @brief Use a shader.
+ */
+#define honey_shader_use glUseProgram
+
+/** @brief delete a shader.
+ */
+#define honey_shader_delete glDeleteProgram
+
+#endif
diff --git a/src/stb_image.c b/src/stb_image/stb_image.c
index 6e5aa1c..6e5aa1c 100644
--- a/src/stb_image.c
+++ b/src/stb_image/stb_image.c
diff --git a/src/stb_image.h b/src/stb_image/stb_image.h
index 2857f05..2857f05 100644
--- a/src/stb_image.h
+++ b/src/stb_image/stb_image.h
diff --git a/src/texture/texture.c b/src/texture/texture.c
new file mode 100644
index 0000000..591ee11
--- /dev/null
+++ b/src/texture/texture.c
@@ -0,0 +1,45 @@
+#include "texture.h"
+
+enum honey_texture_result honey_texture_new(honey_texture* texture,
+ char* texture_path,
+ bool alpha_channel) {
+ unsigned int texture_id;
+ glGenTextures(1, &texture_id);
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ int width, height, channels;
+ unsigned char* image_data = stbi_load(texture_path, &width, &height, &channels, 0);
+ if (image_data == NULL) {
+ fprintf(stderr, "ERROR: failed to load '%s'\n", texture_path);
+ return TEXTURE_FAILED;
+ }
+
+ if (alpha_channel) {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
+ }
+ else {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image_data);
+ }
+
+ glGenerateMipmap(GL_TEXTURE_2D);
+ stbi_image_free(image_data);
+
+ (*texture).texture_id = texture_id;
+ (*texture).width = width;
+ (*texture).height = height;
+ (*texture).channels = channels;
+
+ return TEXTURE_OK;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+void honey_texture_use(honey_texture texture, int texture_unit) {
+ glActiveTexture(GL_TEXTURE0 + texture_unit);
+ glBindTexture(GL_TEXTURE_2D, texture.texture_id);
+}
diff --git a/src/texture/texture.h b/src/texture/texture.h
new file mode 100644
index 0000000..037d3d0
--- /dev/null
+++ b/src/texture/texture.h
@@ -0,0 +1,42 @@
+#ifndef HONEY_TEXTURE_H
+#define HONEY_TEXTURE_H
+
+/** @file texture.h
+ *
+ *@brief Defines the honey_texture struct and associated functions.
+*/
+
+#include "../common.h"
+
+enum honey_texture_result {
+ TEXTURE_OK,
+ TEXTURE_FAILED,
+ N_TEXTURE_RESULTS };
+
+typedef struct {
+ unsigned int texture_id;
+ int width;
+ int height;
+ int channels;
+} honey_texture;
+
+/** @brief Load a texture from disk.
+ *
+ * @param[out] texture Pointer to the destination texture
+ * @param[in] texture_path Path to the location of the texture
+ * @param[in] alpha_channel Set to true if the target image contains an alpha channel
+ *
+ * @return Success or failure type
+ */
+enum honey_texture_result honey_texture_new(honey_texture* texture,
+ char* texture_path,
+ bool alpha_channel);
+
+/** @brief Load a texture into a texture unit.
+ *
+ * @param[in] texture The texture to use
+ * @param[in] texture_unit The texture unit to put the texture in
+ */
+void honey_texture_use(honey_texture texture, int texture_unit);
+
+#endif