From ecfd781f73e0ef78b4d6f8bd82b17a0348ce735a Mon Sep 17 00:00:00 2001 From: sanine-a Date: Tue, 15 Dec 2020 20:23:47 -0600 Subject: refactor textures and allow parameter setting --- .gitignore | 1 + demo/main.lua | 3 +- src/common.h | 5 +- src/honey_lua.c | 6 + src/texture.c | 413 +++++++++++++++++++++++++++++++++++++++----------------- src/texture.h | 84 ++++++------ 6 files changed, 345 insertions(+), 167 deletions(-) diff --git a/.gitignore b/.gitignore index 6fe60c7..3389738 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build/* *~ *# *.#* +demo/plagiarism.dae diff --git a/demo/main.lua b/demo/main.lua index 6aa5775..b956d04 100644 --- a/demo/main.lua +++ b/demo/main.lua @@ -11,8 +11,7 @@ honey.input.key.bind(honey.input.key.escape, honey.exit) local buffer = false honey.input.key.bind(honey.input.key.f, function(action) if action == 1 then buffer = not buffer end end) -local tex = honey.texture.new() -tex:load('checkerboard.png', false) +local tex = honey.texture.load('checkerboard.png') local sceneRoot = Node.new() diff --git a/src/common.h b/src/common.h index 03d6944..13d3a14 100644 --- a/src/common.h +++ b/src/common.h @@ -161,9 +161,10 @@ int honey_lua_parse_arguments(lua_State* L, unsigned int n, ...); * the table will trigger an error. * * The variadic portion of this function expects arguments as - * param_name_1, function1, (void*) data1, param_name_2, function_2, (void*) data_2, ... + * type_1, param_name_1, function1, (void*) data1, type_2, param_name_2, function_2, (void*) data_2, ... * - * Each function should be of the form void (*)(lua_State*, void*) + * Each function should be of the form void (*)(lua_State*, void*), and should + * not return with a modified stack. * * @param[in] L The lua state to parse the table from. * @param[in] n The number of params to parse. diff --git a/src/honey_lua.c b/src/honey_lua.c index ff1d82d..307f4c6 100644 --- a/src/honey_lua.c +++ b/src/honey_lua.c @@ -257,6 +257,7 @@ void honey_lua_parse_params(lua_State* L, int n, int m, ...) va_start(args, m); for (int i=0; iparams)); + + if (use_params) { + lua_pushvalue(L, -2); + configure_params(L, &(texture->params)); + lua_pop(L, 1); + } + lua_rawgeti(L, LUA_REGISTRYINDEX, honey_texture_mt_ref); lua_setmetatable(L, -2); - return 1; } -static int honey_lua_texture_create(lua_State* L) +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static int honey_lua_texture_new(lua_State* L) { honey_texture* texture; - int width, height; - char* type; - honey_lua_parse_arguments(L, 1, 4, - HONEY_USERDATA, &texture, - HONEY_STRING, &type, - HONEY_INTEGER, &width, - HONEY_INTEGER, &height); + int choice = honey_lua_parse_arguments(L, 2, 0, 1, HONEY_TABLE, NULL); + setup_texture(L, &texture, choice == 1); - if (strcmp(type, "greyscale") == 0) - honey_texture_new_greyscale(texture, width, height, NULL); - else if (strcmp(type, "rgb") == 0) - honey_texture_new_rgb(texture, width, height, NULL); - else if (strcmp(type, "rgba") == 0) - honey_texture_new_rgba(texture, width, height, NULL); - else if (strcmp(type, "depth") == 0) - honey_texture_new_depth(texture, width, height, NULL); - else { - honey_lua_throw_error - (L, "unknown texture type '%s'", type); - } - return 0; + honey_texture_generate(texture, NULL); + return 1; } -static int honey_lua_texture_destroy(lua_State* L) -{ - honey_texture* texture; - honey_lua_parse_arguments(L, 1, 1, HONEY_USERDATA, &texture); - glDeleteTextures(1, &(texture->id)); - return 0; -} +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ static int honey_lua_texture_load(lua_State* L) { honey_texture* texture; char* texture_path; - honey_lua_parse_arguments(L, 1, 3, - HONEY_USERDATA, &texture, - HONEY_STRING, &texture_path); + int choice = honey_lua_parse_arguments + (L, 2, + 1, HONEY_STRING, &texture_path, + 2, HONEY_STRING, &texture_path, HONEY_TABLE, NULL); + setup_texture(L, &texture, choice == 1); + enum honey_texture_result result = honey_texture_load(texture, texture_path); - if (result != TEXTURE_OK) { - char* error; - honey_format_string(&error, - "failed to load '%s'", - texture_path); - lua_pushstring(L, error); - free(error); - lua_error(L); - } + if (result != TEXTURE_OK) + honey_lua_throw_error(L, "failed to load '%s'", + texture_path); + + return 1; +} +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static int honey_lua_texture_destroy(lua_State* L) +{ + honey_texture* texture; + honey_lua_parse_arguments(L, 1, 1, HONEY_USERDATA, &texture); + glDeleteTextures(1, &(texture->id)); return 0; } +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + static int honey_lua_texture_use(lua_State* L) { honey_texture* texture; @@ -107,109 +121,86 @@ static int honey_lua_framebuffer_new(lua_State* L) void honey_setup_texture(lua_State* L) { honey_lua_create_table - (L, 2, - HONEY_TABLE, "__index", 3, - HONEY_FUNCTION, "create", honey_lua_texture_create, - HONEY_FUNCTION, "load", honey_lua_texture_load, - HONEY_FUNCTION, "use", honey_lua_texture_use, + (L, 2, + HONEY_TABLE, "__index", 1, + HONEY_FUNCTION, "use", honey_lua_texture_use, - HONEY_FUNCTION, "__gc", honey_lua_texture_destroy); + HONEY_FUNCTION, "__gc", honey_lua_texture_destroy); honey_texture_mt_ref = luaL_ref(L, LUA_REGISTRYINDEX); honey_lua_create_table - (L, 2, - HONEY_FUNCTION, "new", honey_lua_texture_new, - HONEY_FUNCTION, "new_framebuffer", honey_lua_framebuffer_new); + (L, 2, + HONEY_FUNCTION, "new", honey_lua_texture_new, + HONEY_FUNCTION, "load", honey_lua_texture_load); lua_setfield(L, -2, "texture"); } -/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Non-lua texture functions + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ -static void generate_texture(honey_texture* texture, - int width, int height, - int format, int type, - void* data) +void honey_texture_generate(honey_texture* texture, + void* data) { - 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); + honey_texture_params params = texture->params; + glGenTextures(1, &(texture->id)); - glTexImage2D(GL_TEXTURE_2D, 0, - format, - width, height, 0, - format, - type, data); - - texture->id = texture_id; - texture->width = width; - texture->height = height; - - switch(format) { - case GL_RED: - texture->type = GREY; - texture->channels = 1; - break; - - case GL_RGB: - texture->type = RGB; - texture->channels = 3; - break; - - case GL_RGBA: - texture->type = RGBA; - texture->channels = 4; + honey_texture_configure(texture); + + int type; + switch(params.type) { + case HONEY_TEXTURE_TYPE_GREY: + case HONEY_TEXTURE_TYPE_RGB: + case HONEY_TEXTURE_TYPE_RGBA: + type = GL_UNSIGNED_BYTE; break; - case GL_DEPTH_COMPONENT: - texture->type = DEPTH; - texture->channels = 1; + case HONEY_TEXTURE_TYPE_DEPTH: + type = GL_FLOAT; break; default: + // should never happen break; } -} - -/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + glTexImage2D(GL_TEXTURE_2D, 0, + params.type, + params.width, params.height, 0, + params.type, + type, data); -void honey_texture_new_greyscale(honey_texture* texture, - int height, int width, - unsigned char* data) -{ - generate_texture(texture, width, height, GL_RED, GL_UNSIGNED_BYTE, data); + honey_texture_update_mipmaps(texture); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -void honey_texture_new_rgb(honey_texture* texture, - int height, int width, - unsigned char* data) +void honey_texture_configure(honey_texture* texture) { - generate_texture(texture, width, height, GL_RGB, GL_UNSIGNED_BYTE, data); -} + honey_texture_params params = texture->params; + glBindTexture(GL_TEXTURE_2D, texture->id); -/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, params.min_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, params.mag_filter); -void honey_texture_new_rgba(honey_texture* texture, - int height, int width, - unsigned char* data) -{ - generate_texture(texture, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, params.wrap_s); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, params.wrap_t); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, params.wrap_r); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -void honey_texture_new_depth(honey_texture* texture, - int height, int width, - float* data) +void honey_texture_update_mipmaps(honey_texture* texture) { - generate_texture(texture, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, data); + if (texture->params.mipmaps == false) + return; + + glBindTexture(GL_TEXTURE_2D, texture->id); + glGenerateMipmap(GL_TEXTURE_2D); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -217,30 +208,35 @@ void honey_texture_new_depth(honey_texture* texture, enum honey_texture_result honey_texture_load(honey_texture* texture, char* texture_path) { - int width, height, channels; - unsigned char* image_data = stbi_load(texture_path, &width, &height, &channels, 0); + honey_texture_params *params = &(texture->params); + int channels; + + unsigned char* image_data = stbi_load(texture_path, + &(params->width), + &(params->height), + &channels, 0); if (image_data == NULL) { return TEXTURE_FAILED; } switch(channels) { case 1: - honey_texture_new_greyscale(texture, width, height, image_data); + params->type = HONEY_TEXTURE_TYPE_GREY; break; case 3: - honey_texture_new_rgb(texture, width, height, image_data); + params->type = HONEY_TEXTURE_TYPE_RGB; break; case 4: - honey_texture_new_rgba(texture, width, height, image_data); + params->type = HONEY_TEXTURE_TYPE_RGBA; break; default: return TEXTURE_CHANNEL_ERROR; } - glGenerateMipmap(GL_TEXTURE_2D); + honey_texture_generate(texture, image_data); stbi_image_free(image_data); return TEXTURE_OK; @@ -280,3 +276,178 @@ void honey_texture_framebuffer_object_new(unsigned int* destination, glBindFramebuffer(GL_FRAMEBUFFER, 0); } + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Texture parameter setup function definitions + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +static void setup_default_texture_params(honey_texture_params* params) +{ + params->width = HONEY_TEXTURE_DEFAULT_WIDTH; + params->height = HONEY_TEXTURE_DEFAULT_HEIGHT; + params->channels = HONEY_TEXTURE_DEFAULT_CHANNELS; + params->type = HONEY_TEXTURE_DEFAULT_TYPE; + params->mipmaps = HONEY_TEXTURE_DEFAULT_MIPMAPS; + params->min_filter = HONEY_TEXTURE_DEFAULT_MIN_FILTER; + params->mag_filter = HONEY_TEXTURE_DEFAULT_MAG_FILTER; + params->wrap_s = HONEY_TEXTURE_DEFAULT_WRAP_S; + params->wrap_t = HONEY_TEXTURE_DEFAULT_WRAP_T; + params->wrap_r = HONEY_TEXTURE_DEFAULT_WRAP_R; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + +static void configure_width(lua_State* L, void* data) +{ + honey_texture_params* params = (honey_texture_params*) data; + params->width = lua_tointeger(L, -1); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void configure_height(lua_State* L, void* data) +{ + honey_texture_params* params = (honey_texture_params*) data; + params->height = lua_tointeger(L, -1); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void configure_type(lua_State* L, void* data) +{ + honey_texture_params* params = (honey_texture_params*) data; + const char* type_string = lua_tostring(L, -1); + if (strcmp(type_string, "grey") == 0) { + params->type = HONEY_TEXTURE_TYPE_GREY; + params->channels = 1; + } + else if (strcmp(type_string, "rgb") == 0) { + params->type = HONEY_TEXTURE_TYPE_RGB; + params->channels = 3; + } + else if (strcmp(type_string, "rgba") == 0) { + params->type = HONEY_TEXTURE_TYPE_RGBA; + params->channels = 4; + } + else if (strcmp(type_string, "depth") == 0) { + params->type = HONEY_TEXTURE_TYPE_DEPTH; + params->channels = 1; + } + else { + honey_lua_throw_error + (L, "unknown texture type: '%s'", type_string); + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void configure_mipmaps(lua_State* L, void* data) +{ + honey_texture_params* params = (honey_texture_params*) data; + params->mipmaps = lua_toboolean(L, -1); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void configure_min_filter(lua_State* L, void* data) +{ + honey_texture_params* params = (honey_texture_params*) data; + const char* str = lua_tostring(L, -1); + if (strcmp(str, "nearest")) + params->min_filter = GL_NEAREST; + else if (strcmp(str, "linear")) + params->min_filter = GL_LINEAR; + else if (strcmp(str, "nearest-nearest")) + params->min_filter = GL_NEAREST_MIPMAP_NEAREST; + else if (strcmp(str, "linear-nearest")) + params->min_filter = GL_LINEAR_MIPMAP_NEAREST; + else if (strcmp(str, "nearest-linear")) + params->min_filter = GL_NEAREST_MIPMAP_LINEAR; + else if (strcmp(str, "linear-linear")) + params->min_filter = GL_LINEAR_MIPMAP_LINEAR; + else + honey_lua_throw_error + (L, "unknown minFilter type: '%s'", str); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void configure_mag_filter(lua_State* L, void* data) +{ + honey_texture_params* params = (honey_texture_params*) data; + const char* str = lua_tostring(L, -1); + if (strcmp(str, "nearest")) + params->min_filter = GL_NEAREST; + else if (strcmp(str, "linear")) + params->min_filter = GL_LINEAR; + else + honey_lua_throw_error + (L, "unknown magFilter type: '%s'", str); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void configure_wrap(lua_State* L, const char* string, int* wrap) +{ + if (strcmp(string, "clamp")) + *wrap = GL_CLAMP_TO_EDGE; + else if (strcmp(string, "clamp-border")) + *wrap = GL_CLAMP_TO_BORDER; + else if (strcmp(string, "repeat")) + *wrap = GL_REPEAT; + else if (strcmp(string, "repeat-mirror")) + *wrap = GL_MIRRORED_REPEAT; + else + honey_lua_throw_error + (L, "unknown wrapping type: '%s'", string); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void configure_wrap_s(lua_State* L, void* data) +{ + honey_texture_params* params = (honey_texture_params*) data; + const char* str = lua_tostring(L, -1); + configure_wrap(L, str, &(params->wrap_s)); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void configure_wrap_t(lua_State* L, void* data) +{ + honey_texture_params* params = (honey_texture_params*) data; + const char* str = lua_tostring(L, -1); + configure_wrap(L, str, &(params->wrap_t)); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void configure_wrap_r(lua_State* L, void* data) +{ + honey_texture_params* params = (honey_texture_params*) data; + const char* str = lua_tostring(L, -1); + configure_wrap(L, str, &(params->wrap_r)); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void configure_params(lua_State* L, honey_texture_params* params) +{ + honey_lua_parse_params + (L, 8, 0, + HONEY_INTEGER, "width", configure_width, params, + HONEY_INTEGER, "height", configure_height, params, + HONEY_STRING, "type", configure_type, params, + HONEY_BOOLEAN, "mipmaps", configure_mipmaps, params, + HONEY_STRING, "minFilter", configure_min_filter, params, + HONEY_STRING, "magFilter", configure_min_filter, params, + HONEY_STRING, "sWrap", configure_wrap_s, params, + HONEY_STRING, "tWrap", configure_wrap_t, params, + HONEY_STRING, "rWrap", configure_wrap_r, params); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ diff --git a/src/texture.h b/src/texture.h index 93417f0..6bbcc4d 100644 --- a/src/texture.h +++ b/src/texture.h @@ -8,6 +8,23 @@ #include "common.h" +#define HONEY_TEXTURE_DEFAULT_WIDTH 1024 +#define HONEY_TEXTURE_DEFAULT_HEIGHT 1024 +#define HONEY_TEXTURE_DEFAULT_CHANNELS 4 +#define HONEY_TEXTURE_DEFAULT_TYPE HONEY_TEXTURE_TYPE_RGBA +#define HONEY_TEXTURE_DEFAULT_MIPMAPS true +#define HONEY_TEXTURE_DEFAULT_MIN_FILTER GL_LINEAR +#define HONEY_TEXTURE_DEFAULT_MAG_FILTER GL_LINEAR +#define HONEY_TEXTURE_DEFAULT_WRAP_S GL_REPEAT +#define HONEY_TEXTURE_DEFAULT_WRAP_T GL_REPEAT +#define HONEY_TEXTURE_DEFAULT_WRAP_R GL_REPEAT + +#define HONEY_TEXTURE_TYPE_GREY GL_RED +#define HONEY_TEXTURE_TYPE_RGB GL_RGB +#define HONEY_TEXTURE_TYPE_RGBA GL_RGBA +#define HONEY_TEXTURE_TYPE_DEPTH GL_DEPTH_COMPONENT + + extern int honey_texture_mt_ref; enum honey_texture_result { @@ -17,72 +34,55 @@ enum honey_texture_result { N_TEXTURE_RESULTS }; typedef struct { - unsigned int id; - enum { - GREY, - RGB, - RGBA, - DEPTH - } type; int width; int height; int channels; + int type; + bool mipmaps; + int min_filter; + int mag_filter; + int wrap_s; + int wrap_t; + int wrap_r; +} honey_texture_params; + +typedef struct { + unsigned int id; + honey_texture_params params; } honey_texture; /** @brief Place the honey.texture bindings as a table on the stack. */ void honey_setup_texture(lua_State* L); -/** @brief Create a greyscale texture. +/** @brief Generate a texture. * * @param[out] texture Pointer to the destination texture. - * @param[in] width The width in pixels of the texture to create. - * @param[in] height The height in pixels of the texture to create. * @param[in] data The data to populate the texture with, or NULL to leave it unpopulated. * * @returns Nothing. */ -void honey_texture_new_greyscale(honey_texture* texture, - int width, int height, - unsigned char* data); +void honey_texture_generate(honey_texture* texture, + void* data); -/** @brief Create an RGB texture. +/** @brief Set the parameters of a texture. * - * @param[out] texture Pointer to the destination texture. - * @param[in] width The width in pixels of the texture to create. - * @param[in] height The height in pixels of the texture to create. - * @param[in] data The data to populate the texture with, or NULL to leave it unpopulated. + * This function takes the parameters given in a texture's `params` field + * and applies them to it's OpenGL object. `honey_texture_generate` must be called + * before using this function. * - * @returns Nothing. + * @param[inout] texture The texture to configure. */ -void honey_texture_new_rgb(honey_texture* texture, - int width, int height, - unsigned char* data); +void honey_texture_configure(honey_texture* texture); -/** @brief Create an RGBA texture. +/** @brief Update the mipmaps of a texture. * - * @param[out] texture Pointer to the destination texture. - * @param[in] width The width in pixels of the texture to create. - * @param[in] height The height in pixels of the texture to create. - * @param[in] data The data to populate the texture with, or NULL to leave it unpopulated. + * If a texture has params.mipmaps set to false, this function does nothing. * - * @returns Nothing. - */ -void honey_texture_new_rgba(honey_texture* texture, - int width, int height, - unsigned char* data); - -/** @brief Create a depth texture. - * - * @param[out] texture Pointer to the destination texture. - * @param[in] width The width in pixels of the texture to create. - * @param[in] height The height in pixels of the texture to create. - * @param[in] data The data to populate the texture with, or NULL to leave it unpopulated. + * @param[inout] texture The texture to generate new mipmaps for. * * @returns Nothing. */ -void honey_texture_new_depth(honey_texture* texture, - int width, int height, - float* data); +void honey_texture_update_mipmaps(honey_texture* texture); /** @brief Load a texture from disk. * -- cgit v1.2.1