diff options
| -rw-r--r-- | b.c | 40 | ||||
| -rw-r--r-- | lib/fast_obj.h | 1564 | ||||
| l---------[-rw-r--r--] | src/b.h | 879 | ||||
| -rw-r--r-- | src/render.c | 121 | ||||
| -rw-r--r-- | src/shader.comp | 34 | ||||
| -rw-r--r-- | src/shader.frag | 16 | ||||
| -rw-r--r-- | src/state.h | 3 |
7 files changed, 1735 insertions, 922 deletions
@@ -7,7 +7,7 @@ int debug_level = 1; void -debug_or_release(B_Cmd* cmd) +debug_or_release(b_cmd* cmd) { if (debug_level == 0) b_cmd_append(cmd, "-O3", "-s", "-DNDEBUG"); @@ -22,7 +22,7 @@ debug_or_release(B_Cmd* cmd) } void -inlcude_dirs(B_Cmd* cmd) +inlcude_dirs(b_cmd* cmd) { b_cmd_append(cmd, "-I./src/"); b_cmd_append(cmd, "-I./lib"); @@ -30,27 +30,27 @@ inlcude_dirs(B_Cmd* cmd) } void -cflags(B_Cmd* cmd) +cflags(b_cmd* cmd) { b_cmd_append(cmd, "-Wall", "-Wextra"); debug_or_release(cmd); - b_cmd_append(cmd, "-march=native"); + //b_cmd_append(cmd, "-march=native"); b_cmd_append(cmd, "-fno-math-errno", "-funroll-loops"); b_cmd_append(cmd, "-flto", "-pthread"); inlcude_dirs(cmd); } -void cxxflags(B_Cmd *cmd) +void cxxflags(b_cmd *cmd) { b_cmd_append(cmd, "-Wall", "-Wextra"); b_cmd_append(cmd, "-Wno-string-plus-int", "-Wno-nullability-completeness", "-Wno-unused-function", "-Wno-missing-field-initializers", "-Wno-unused-parameter", "-Wno-unused-variable"); debug_or_release(cmd); - b_cmd_append(cmd, "-march=native"); + //b_cmd_append(cmd, "-march=native"); b_cmd_append(cmd, "-fno-math-errno", "-funroll-loops"); b_cmd_append(cmd, "-flto", "-pthread"); @@ -58,26 +58,26 @@ void cxxflags(B_Cmd *cmd) //b_cmd_append(cmd, "-O3"); } -void cxx(B_Cmd *cmd) +void cxx(b_cmd *cmd) { b_cmd_append(cmd, "clang"); cxxflags(cmd); } -void cc(B_Cmd *cmd) +void cc(b_cmd *cmd) { b_cmd_append(cmd, "clang"); cflags(cmd); } -void libs(B_Cmd *cmd) +void libs(b_cmd *cmd) { b_cmd_append(cmd, "-lSDL2", "-lm", "-lvulkan", "-lshaderc_shared", "-lstdc++"); } bool build_c(bool force, - B_Cmd* cmd, + b_cmd* cmd, const char** input_paths, size_t input_paths_len, const char** dep_paths, @@ -111,9 +111,9 @@ build_c(bool force, cmd->count = 0; cc(cmd); b_cmd_append(cmd, "-o", output_path); - b_da_append_many(cmd, new_array, size); + array_append_many(cmd, new_array, size); libs(cmd); - return b_cmd_run_sync(*cmd); + return b_cmd_run(cmd); } b_log(B_INFO, "%s is up-to-date", output_path); @@ -142,7 +142,7 @@ build_objects(const char* lang, path[ strlen(path) - 2 ] = '\0'; strcat(path, "/"); - b_mkdir_if_not_exists(path); + b_mkdir(path); int rebuild = 0; @@ -177,7 +177,7 @@ build_objects(const char* lang, rebuild += c; if (c != 0) { - B_Cmd cmd = { 0 }; + b_cmd cmd = { 0 }; if (!strcmp(lang, "C++")) cxx(&cmd); @@ -187,7 +187,7 @@ build_objects(const char* lang, b_cmd_append(&cmd, i == deps_size ? object : deps[i]); b_cmd_append(&cmd, "-o"); b_cmd_append(&cmd, out[*it]); - bool rc = b_cmd_run_sync(cmd); + bool rc = b_cmd_run(&cmd); if (!rc) return false; } *it = *it + 1; @@ -284,16 +284,16 @@ main(int argc, char *argv[]) "src/render.c", }; - B_Cmd cmd = {0}; + b_cmd cmd = {0}; size_t it = 0; - const char* objects[B_ARRAY_LEN(cplusplus_deps) + 1 + 1]; + const char* objects[ARRAY_LENGTH(cplusplus_deps) + 1 + 1]; - b_mkdir_if_not_exists(BUILD_DIR); + b_mkdir(BUILD_DIR); // TODO: make build_object func to build one by one, and add headers if (!build_objects("C", "src/test.c", NULL, 0, objects, &it)) return 1; - if (!build_objects("C++", "src/cplusplus.cpp", cplusplus_deps, B_ARRAY_LEN(cplusplus_deps), objects, &it)) return 1; - if (!build_c(force, &cmd, render_paths, B_ARRAY_LEN(render_paths), render_deps, B_ARRAY_LEN(render_deps), objects, B_ARRAY_LEN(objects), BUILD_DIR"render")) return 1; + if (!build_objects("C++", "src/cplusplus.cpp", cplusplus_deps, ARRAY_LENGTH(cplusplus_deps), objects, &it)) return 1; + if (!build_c(force, &cmd, render_paths, ARRAY_LENGTH(render_paths), render_deps, ARRAY_LENGTH(render_deps), objects, ARRAY_LENGTH(objects), BUILD_DIR"render")) return 1; b_log(B_INFO, "Build time: %.3f seconds", current_time() - before_build); diff --git a/lib/fast_obj.h b/lib/fast_obj.h new file mode 100644 index 0000000..b077a5c --- /dev/null +++ b/lib/fast_obj.h @@ -0,0 +1,1564 @@ +/* + * fast_obj + * + * Version 1.2 + * + * MIT License + * + * Copyright (c) 2018-2021 Richard Knight + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef FAST_OBJ_HDR +#define FAST_OBJ_HDR + +#define FAST_OBJ_VERSION_MAJOR 1 +#define FAST_OBJ_VERSION_MINOR 2 +#define FAST_OBJ_VERSION ((FAST_OBJ_VERSION_MAJOR << 8) | FAST_OBJ_VERSION_MINOR) + +#include <stdlib.h> + + +typedef struct +{ + /* Texture name from .mtl file */ + char* name; + + /* Resolved path to texture */ + char* path; + +} fastObjTexture; + + +typedef struct +{ + /* Material name */ + char* name; + + /* Parameters */ + float Ka[3]; /* Ambient */ + float Kd[3]; /* Diffuse */ + float Ks[3]; /* Specular */ + float Ke[3]; /* Emission */ + float Kt[3]; /* Transmittance */ + float Ns; /* Shininess */ + float Ni; /* Index of refraction */ + float Tf[3]; /* Transmission filter */ + float d; /* Disolve (alpha) */ + int illum; /* Illumination model */ + + /* Set for materials that don't come from the associated mtllib */ + int fallback; + + /* Texture maps */ + fastObjTexture map_Ka; + fastObjTexture map_Kd; + fastObjTexture map_Ks; + fastObjTexture map_Ke; + fastObjTexture map_Kt; + fastObjTexture map_Ns; + fastObjTexture map_Ni; + fastObjTexture map_d; + fastObjTexture map_bump; + +} fastObjMaterial; + +/* Allows user override to bigger indexable array */ +#ifndef FAST_OBJ_UINT_TYPE +#define FAST_OBJ_UINT_TYPE unsigned int +#endif + +typedef FAST_OBJ_UINT_TYPE fastObjUInt; + +typedef struct +{ + fastObjUInt p; + fastObjUInt t; + fastObjUInt n; + +} fastObjIndex; + + +typedef struct +{ + /* Group name */ + char* name; + + /* Number of faces */ + unsigned int face_count; + + /* First face in fastObjMesh face_* arrays */ + unsigned int face_offset; + + /* First index in fastObjMesh indices array */ + unsigned int index_offset; + +} fastObjGroup; + + +typedef struct +{ + /* Vertex data */ + unsigned int position_count; + float* positions; + + unsigned int texcoord_count; + float* texcoords; + + unsigned int normal_count; + float* normals; + + unsigned int color_count; + float* colors; + + /* Face data: one element for each face */ + unsigned int face_count; + unsigned int* face_vertices; + unsigned int* face_materials; + + /* Index data: one element for each face vertex */ + unsigned int index_count; + fastObjIndex* indices; + + /* Materials */ + unsigned int material_count; + fastObjMaterial* materials; + + /* Mesh objects ('o' tag in .obj file) */ + unsigned int object_count; + fastObjGroup* objects; + + /* Mesh groups ('g' tag in .obj file) */ + unsigned int group_count; + fastObjGroup* groups; + +} fastObjMesh; + +typedef struct +{ + void* (*file_open)(const char* path, void* user_data); + void (*file_close)(void* file, void* user_data); + size_t (*file_read)(void* file, void* dst, size_t bytes, void* user_data); + unsigned long (*file_size)(void* file, void* user_data); +} fastObjCallbacks; + +#ifdef __cplusplus +extern "C" { +#endif + +fastObjMesh* fast_obj_read(const char* path); +fastObjMesh* fast_obj_read_with_callbacks(const char* path, const fastObjCallbacks* callbacks, void* user_data); +void fast_obj_destroy(fastObjMesh* mesh); + +#ifdef __cplusplus +} +#endif + +#endif + + +#ifdef FAST_OBJ_IMPLEMENTATION + +#include <stdio.h> +#include <string.h> + +#ifndef FAST_OBJ_REALLOC +#define FAST_OBJ_REALLOC realloc +#endif + +#ifndef FAST_OBJ_FREE +#define FAST_OBJ_FREE free +#endif + +#ifdef _WIN32 +#define FAST_OBJ_SEPARATOR '\\' +#define FAST_OBJ_OTHER_SEP '/' +#else +#define FAST_OBJ_SEPARATOR '/' +#define FAST_OBJ_OTHER_SEP '\\' +#endif + + +/* Size of buffer to read into */ +#define BUFFER_SIZE 65536 + +/* Max supported power when parsing float */ +#define MAX_POWER 20 + +typedef struct +{ + /* Final mesh */ + fastObjMesh* mesh; + + /* Current object/group */ + fastObjGroup object; + fastObjGroup group; + + /* Current material index */ + unsigned int material; + + /* Current line in file */ + unsigned int line; + + /* Base path for materials/textures */ + char* base; + +} fastObjData; + + +static const +double POWER_10_POS[MAX_POWER] = +{ + 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, + 1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19, +}; + +static const +double POWER_10_NEG[MAX_POWER] = +{ + 1.0e0, 1.0e-1, 1.0e-2, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6, 1.0e-7, 1.0e-8, 1.0e-9, + 1.0e-10, 1.0e-11, 1.0e-12, 1.0e-13, 1.0e-14, 1.0e-15, 1.0e-16, 1.0e-17, 1.0e-18, 1.0e-19, +}; + + +static void* memory_realloc(void* ptr, size_t bytes) +{ + return FAST_OBJ_REALLOC(ptr, bytes); +} + + +static +void memory_dealloc(void* ptr) +{ + FAST_OBJ_FREE(ptr); +} + + +#define array_clean(_arr) ((_arr) ? memory_dealloc(_array_header(_arr)), 0 : 0) +#define array_push(_arr, _val) (_array_mgrow(_arr, 1) ? ((_arr)[_array_size(_arr)++] = (_val), _array_size(_arr) - 1) : 0) +#define array_size(_arr) ((_arr) ? _array_size(_arr) : 0) +#define array_capacity(_arr) ((_arr) ? _array_capacity(_arr) : 0) +#define array_empty(_arr) (array_size(_arr) == 0) + +#define _array_header(_arr) ((fastObjUInt*)(_arr)-2) +#define _array_size(_arr) (_array_header(_arr)[0]) +#define _array_capacity(_arr) (_array_header(_arr)[1]) +#define _array_ngrow(_arr, _n) ((_arr) == 0 || (_array_size(_arr) + (_n) >= _array_capacity(_arr))) +#define _array_mgrow(_arr, _n) (_array_ngrow(_arr, _n) ? (_array_grow(_arr, _n) != 0) : 1) +#define _array_grow(_arr, _n) (*((void**)&(_arr)) = array_realloc(_arr, _n, sizeof(*(_arr)))) + + +static void* array_realloc(void* ptr, fastObjUInt n, fastObjUInt b) +{ + fastObjUInt sz = array_size(ptr); + fastObjUInt nsz = sz + n; + fastObjUInt cap = array_capacity(ptr); + fastObjUInt ncap = cap + cap / 2; + fastObjUInt* r; + + if (ncap < nsz) + ncap = nsz; + ncap = (ncap + 15) & ~15u; + + r = (fastObjUInt*)(memory_realloc(ptr ? _array_header(ptr) : 0, (size_t)b * ncap + 2 * sizeof(fastObjUInt))); + if (!r) + return 0; + + r[0] = sz; + r[1] = ncap; + + return (r + 2); +} + + +static +void* file_open(const char* path, void* user_data) +{ + (void)(user_data); + return fopen(path, "rb"); +} + + +static +void file_close(void* file, void* user_data) +{ + FILE* f; + (void)(user_data); + + f = (FILE*)(file); + fclose(f); +} + + +static +size_t file_read(void* file, void* dst, size_t bytes, void* user_data) +{ + FILE* f; + (void)(user_data); + + f = (FILE*)(file); + return fread(dst, 1, bytes, f); +} + + +static +unsigned long file_size(void* file, void* user_data) +{ + FILE* f; + long p; + long n; + (void)(user_data); + + f = (FILE*)(file); + + p = ftell(f); + fseek(f, 0, SEEK_END); + n = ftell(f); + fseek(f, p, SEEK_SET); + + if (n > 0) + return (unsigned long)(n); + else + return 0; +} + + +static +char* string_copy(const char* s, const char* e) +{ + size_t n; + char* p; + + n = (size_t)(e - s); + p = (char*)(memory_realloc(0, n + 1)); + if (p) + { + memcpy(p, s, n); + p[n] = '\0'; + } + + return p; +} + + +static +char* string_substr(const char* s, size_t a, size_t b) +{ + return string_copy(s + a, s + b); +} + + +static +char* string_concat(const char* a, const char* s, const char* e) +{ + size_t an; + size_t sn; + char* p; + + an = a ? strlen(a) : 0; + sn = (size_t)(e - s); + p = (char*)(memory_realloc(0, an + sn + 1)); + if (p) + { + if (a) + memcpy(p, a, an); + memcpy(p + an, s, sn); + p[an + sn] = '\0'; + } + + return p; +} + + +static +int string_equal(const char* a, const char* s, const char* e) +{ + size_t an = strlen(a); + size_t sn = (size_t)(e - s); + + return an == sn && memcmp(a, s, an) == 0; +} + + +static +void string_fix_separators(char* s) +{ + while (*s) + { + if (*s == FAST_OBJ_OTHER_SEP) + *s = FAST_OBJ_SEPARATOR; + s++; + } +} + + +static +int is_whitespace(char c) +{ + return (c == ' ' || c == '\t' || c == '\r'); +} + +static +int is_newline(char c) +{ + return (c == '\n'); +} + + +static +int is_digit(char c) +{ + return (c >= '0' && c <= '9'); +} + + +static +int is_exponent(char c) +{ + return (c == 'e' || c == 'E'); +} + + +static +const char* skip_name(const char* ptr) +{ + const char* s = ptr; + + while (!is_newline(*ptr)) + ptr++; + + while (ptr > s && is_whitespace(*(ptr - 1))) + ptr--; + + return ptr; +} + + +static +const char* skip_whitespace(const char* ptr) +{ + while (is_whitespace(*ptr)) + ptr++; + + return ptr; +} + + +static +const char* skip_line(const char* ptr) +{ + while (!is_newline(*ptr++)) + ; + + return ptr; +} + + +static +fastObjGroup object_default(void) +{ + fastObjGroup object; + + object.name = 0; + object.face_count = 0; + object.face_offset = 0; + object.index_offset = 0; + + return object; +} + + +static +void object_clean(fastObjGroup* object) +{ + memory_dealloc(object->name); +} + + +static +void flush_object(fastObjData* data) +{ + /* Add object if not empty */ + if (data->object.face_count > 0) + array_push(data->mesh->objects, data->object); + else + object_clean(&data->object); + + /* Reset for more data */ + data->object = object_default(); + data->object.face_offset = array_size(data->mesh->face_vertices); + data->object.index_offset = array_size(data->mesh->indices); +} + + + +static +fastObjGroup group_default(void) +{ + fastObjGroup group; + + group.name = 0; + group.face_count = 0; + group.face_offset = 0; + group.index_offset = 0; + + return group; +} + + +static +void group_clean(fastObjGroup* group) +{ + memory_dealloc(group->name); +} + + +static +void flush_group(fastObjData* data) +{ + /* Add group if not empty */ + if (data->group.face_count > 0) + array_push(data->mesh->groups, data->group); + else + group_clean(&data->group); + + /* Reset for more data */ + data->group = group_default(); + data->group.face_offset = array_size(data->mesh->face_vertices); + data->group.index_offset = array_size(data->mesh->indices); +} + + +static +const char* parse_int(const char* ptr, int* val) +{ + int sign; + int num; + + + if (*ptr == '-') + { + sign = -1; + ptr++; + } + else + { + sign = +1; + } + + num = 0; + while (is_digit(*ptr)) + num = 10 * num + (*ptr++ - '0'); + + *val = sign * num; + + return ptr; +} + + +static +const char* parse_float(const char* ptr, float* val) +{ + double sign; + double num; + double fra; + double div; + unsigned int eval; + const double* powers; + + + ptr = skip_whitespace(ptr); + + switch (*ptr) + { + case '+': + sign = 1.0; + ptr++; + break; + + case '-': + sign = -1.0; + ptr++; + break; + + default: + sign = 1.0; + break; + } + + + num = 0.0; + while (is_digit(*ptr)) + num = 10.0 * num + (double)(*ptr++ - '0'); + + if (*ptr == '.') + ptr++; + + fra = 0.0; + div = 1.0; + + while (is_digit(*ptr)) + { + fra = 10.0 * fra + (double)(*ptr++ - '0'); + div *= 10.0; + } + + num += fra / div; + + if (is_exponent(*ptr)) + { + ptr++; + + switch (*ptr) + { + case '+': + powers = POWER_10_POS; + ptr++; + break; + + case '-': + powers = POWER_10_NEG; + ptr++; + break; + + default: + powers = POWER_10_POS; + break; + } + + eval = 0; + while (is_digit(*ptr)) + eval = 10 * eval + (*ptr++ - '0'); + + num *= (eval >= MAX_POWER) ? 0.0 : powers[eval]; + } + + *val = (float)(sign * num); + + return ptr; +} + + +static +const char* parse_vertex(fastObjData* data, const char* ptr) +{ + unsigned int ii; + float v; + + + for (ii = 0; ii < 3; ii++) + { + ptr = parse_float(ptr, &v); + array_push(data->mesh->positions, v); + } + + + ptr = skip_whitespace(ptr); + if (!is_newline(*ptr)) + { + /* Fill the colors array until it matches the size of the positions array */ + for (ii = array_size(data->mesh->colors); ii < array_size(data->mesh->positions) - 3; ++ii) + { + array_push(data->mesh->colors, 1.0f); + } + + for (ii = 0; ii < 3; ++ii) + { + ptr = parse_float(ptr, &v); + array_push(data->mesh->colors, v); + } + } + + return ptr; +} + + +static +const char* parse_texcoord(fastObjData* data, const char* ptr) +{ + unsigned int ii; + float v; + + + for (ii = 0; ii < 2; ii++) + { + ptr = parse_float(ptr, &v); + array_push(data->mesh->texcoords, v); + } + + return ptr; +} + + +static +const char* parse_normal(fastObjData* data, const char* ptr) +{ + unsigned int ii; + float v; + + + for (ii = 0; ii < 3; ii++) + { + ptr = parse_float(ptr, &v); + array_push(data->mesh->normals, v); + } + + return ptr; +} + + +static +const char* parse_face(fastObjData* data, const char* ptr) +{ + unsigned int count; + fastObjIndex vn; + int v; + int t; + int n; + + + ptr = skip_whitespace(ptr); + + count = 0; + while (!is_newline(*ptr)) + { + v = 0; + t = 0; + n = 0; + + ptr = parse_int(ptr, &v); + if (*ptr == '/') + { + ptr++; + if (*ptr != '/') + ptr = parse_int(ptr, &t); + + if (*ptr == '/') + { + ptr++; + ptr = parse_int(ptr, &n); + } + } + + if (v < 0) + vn.p = (array_size(data->mesh->positions) / 3) - (fastObjUInt)(-v); + else if (v > 0) + vn.p = (fastObjUInt)(v); + else + return ptr; /* Skip lines with no valid vertex index */ + + if (t < 0) + vn.t = (array_size(data->mesh->texcoords) / 2) - (fastObjUInt)(-t); + else if (t > 0) + vn.t = (fastObjUInt)(t); + else + vn.t = 0; + + if (n < 0) + vn.n = (array_size(data->mesh->normals) / 3) - (fastObjUInt)(-n); + else if (n > 0) + vn.n = (fastObjUInt)(n); + else + vn.n = 0; + + array_push(data->mesh->indices, vn); + count++; + + ptr = skip_whitespace(ptr); + } + + array_push(data->mesh->face_vertices, count); + array_push(data->mesh->face_materials, data->material); + + data->group.face_count++; + data->object.face_count++; + + return ptr; +} + + +static +const char* parse_object(fastObjData* data, const char* ptr) +{ + const char* s; + const char* e; + + + ptr = skip_whitespace(ptr); + + s = ptr; + ptr = skip_name(ptr); + e = ptr; + + flush_object(data); + data->object.name = string_copy(s, e); + + return ptr; +} + + +static +const char* parse_group(fastObjData* data, const char* ptr) +{ + const char* s; + const char* e; + + + ptr = skip_whitespace(ptr); + + s = ptr; + ptr = skip_name(ptr); + e = ptr; + + flush_group(data); + data->group.name = string_copy(s, e); + + return ptr; +} + + +static +fastObjTexture map_default(void) +{ + fastObjTexture map; + + map.name = 0; + map.path = 0; + + return map; +} + + +static +fastObjMaterial mtl_default(void) +{ + fastObjMaterial mtl; + + mtl.name = 0; + + mtl.Ka[0] = 0.0; + mtl.Ka[1] = 0.0; + mtl.Ka[2] = 0.0; + mtl.Kd[0] = 1.0; + mtl.Kd[1] = 1.0; + mtl.Kd[2] = 1.0; + mtl.Ks[0] = 0.0; + mtl.Ks[1] = 0.0; + mtl.Ks[2] = 0.0; + mtl.Ke[0] = 0.0; + mtl.Ke[1] = 0.0; + mtl.Ke[2] = 0.0; + mtl.Kt[0] = 0.0; + mtl.Kt[1] = 0.0; + mtl.Kt[2] = 0.0; + mtl.Ns = 1.0; + mtl.Ni = 1.0; + mtl.Tf[0] = 1.0; + mtl.Tf[1] = 1.0; + mtl.Tf[2] = 1.0; + mtl.d = 1.0; + mtl.illum = 1; + + mtl.fallback = 0; + + mtl.map_Ka = map_default(); + mtl.map_Kd = map_default(); + mtl.map_Ks = map_default(); + mtl.map_Ke = map_default(); + mtl.map_Kt = map_default(); + mtl.map_Ns = map_default(); + mtl.map_Ni = map_default(); + mtl.map_d = map_default(); + mtl.map_bump = map_default(); + + return mtl; +} + + +static +const char* parse_usemtl(fastObjData* data, const char* ptr) +{ + const char* s; + const char* e; + unsigned int idx; + fastObjMaterial* mtl; + + + ptr = skip_whitespace(ptr); + + /* Parse the material name */ + s = ptr; + ptr = skip_name(ptr); + e = ptr; + + /* Find an existing material with the same name */ + idx = 0; + while (idx < array_size(data->mesh->materials)) + { + mtl = &data->mesh->materials[idx]; + if (mtl->name && string_equal(mtl->name, s, e)) + break; + + idx++; + } + + /* If doesn't exists, create a default one with this name + Note: this case happens when OBJ doesn't have its MTL */ + if (idx == array_size(data->mesh->materials)) + { + fastObjMaterial new_mtl = mtl_default(); + new_mtl.name = string_copy(s, e); + new_mtl.fallback = 1; + array_push(data->mesh->materials, new_mtl); + } + + data->material = idx; + + return ptr; +} + + +static +void map_clean(fastObjTexture* map) +{ + memory_dealloc(map->name); + memory_dealloc(map->path); +} + + +static +void mtl_clean(fastObjMaterial* mtl) +{ + map_clean(&mtl->map_Ka); + map_clean(&mtl->map_Kd); + map_clean(&mtl->map_Ks); + map_clean(&mtl->map_Ke); + map_clean(&mtl->map_Kt); + map_clean(&mtl->map_Ns); + map_clean(&mtl->map_Ni); + map_clean(&mtl->map_d); + map_clean(&mtl->map_bump); + + memory_dealloc(mtl->name); +} + + +static +const char* read_mtl_int(const char* p, int* v) +{ + return parse_int(p, v); +} + + +static +const char* read_mtl_single(const char* p, float* v) +{ + return parse_float(p, v); +} + + +static +const char* read_mtl_triple(const char* p, float v[3]) +{ + p = read_mtl_single(p, &v[0]); + p = read_mtl_single(p, &v[1]); + p = read_mtl_single(p, &v[2]); + + return p; +} + + +static +const char* read_map(fastObjData* data, const char* ptr, fastObjTexture* map) +{ + const char* s; + const char* e; + char* name; + char* path; + + ptr = skip_whitespace(ptr); + + /* Don't support options at present */ + if (*ptr == '-') + return ptr; + + + /* Read name */ + s = ptr; + ptr = skip_name(ptr); + e = ptr; + + name = string_copy(s, e); + + path = string_concat(data->base, s, e); + string_fix_separators(path); + + map->name = name; + map->path = path; + + return e; +} + + +static +int read_mtllib(fastObjData* data, void* file, const fastObjCallbacks* callbacks, void* user_data) +{ + unsigned long n; + const char* s; + char* contents; + size_t l; + const char* p; + const char* e; + int found_d; + fastObjMaterial mtl; + + + /* Read entire file */ + n = callbacks->file_size(file, user_data); + + contents = (char*)(memory_realloc(0, n + 1)); + if (!contents) + return 0; + + l = callbacks->file_read(file, contents, n, user_data); + contents[l] = '\n'; + + mtl = mtl_default(); + + found_d = 0; + + p = contents; + e = contents + l; + while (p < e) + { + p = skip_whitespace(p); + + switch (*p) + { + case 'n': + p++; + if (p[0] == 'e' && + p[1] == 'w' && + p[2] == 'm' && + p[3] == 't' && + p[4] == 'l' && + is_whitespace(p[5])) + { + /* Push previous material (if there is one) */ + if (mtl.name) + { + array_push(data->mesh->materials, mtl); + mtl = mtl_default(); + } + + + /* Read name */ + p += 5; + + while (is_whitespace(*p)) + p++; + + s = p; + p = skip_name(p); + + mtl.name = string_copy(s, p); + } + break; + + case 'K': + if (p[1] == 'a') + p = read_mtl_triple(p + 2, mtl.Ka); + else if (p[1] == 'd') + p = read_mtl_triple(p + 2, mtl.Kd); + else if (p[1] == 's') + p = read_mtl_triple(p + 2, mtl.Ks); + else if (p[1] == 'e') + p = read_mtl_triple(p + 2, mtl.Ke); + else if (p[1] == 't') + p = read_mtl_triple(p + 2, mtl.Kt); + break; + + case 'N': + if (p[1] == 's') + p = read_mtl_single(p + 2, &mtl.Ns); + else if (p[1] == 'i') + p = read_mtl_single(p + 2, &mtl.Ni); + break; + + case 'T': + if (p[1] == 'r') + { + float Tr; + p = read_mtl_single(p + 2, &Tr); + if (!found_d) + { + /* Ignore Tr if we've already read d */ + mtl.d = 1.0f - Tr; + } + } + else if (p[1] == 'f') + p = read_mtl_triple(p + 2, mtl.Tf); + break; + + case 'd': + if (is_whitespace(p[1])) + { + p = read_mtl_single(p + 1, &mtl.d); + found_d = 1; + } + break; + + case 'i': + p++; + if (p[0] == 'l' && + p[1] == 'l' && + p[2] == 'u' && + p[3] == 'm' && + is_whitespace(p[4])) + { + p = read_mtl_int(p + 4, &mtl.illum); + } + break; + + case 'm': + p++; + if (p[0] == 'a' && + p[1] == 'p' && + p[2] == '_') + { + p += 3; + if (*p == 'K') + { + p++; + if (is_whitespace(p[1])) + { + if (*p == 'a') + p = read_map(data, p + 1, &mtl.map_Ka); + else if (*p == 'd') + p = read_map(data, p + 1, &mtl.map_Kd); + else if (*p == 's') + p = read_map(data, p + 1, &mtl.map_Ks); + else if (*p == 'e') + p = read_map(data, p + 1, &mtl.map_Ke); + else if (*p == 't') + p = read_map(data, p + 1, &mtl.map_Kt); + } + } + else if (*p == 'N') + { + p++; + if (is_whitespace(p[1])) + { + if (*p == 's') + p = read_map(data, p + 1, &mtl.map_Ns); + else if (*p == 'i') + p = read_map(data, p + 1, &mtl.map_Ni); + } + } + else if (*p == 'd') + { + p++; + if (is_whitespace(*p)) + p = read_map(data, p, &mtl.map_d); + } + else if ((p[0] == 'b' || p[0] == 'B') && + p[1] == 'u' && + p[2] == 'm' && + p[3] == 'p' && + is_whitespace(p[4])) + { + p = read_map(data, p + 4, &mtl.map_bump); + } + } + break; + + case '#': + break; + } + + p = skip_line(p); + } + + /* Push final material */ + if (mtl.name) + array_push(data->mesh->materials, mtl); + + memory_dealloc(contents); + + return 1; +} + + +static +const char* parse_mtllib(fastObjData* data, const char* ptr, const fastObjCallbacks* callbacks, void* user_data) +{ + const char* s; + const char* e; + char* lib; + void* file; + + + ptr = skip_whitespace(ptr); + + s = ptr; + ptr = skip_name(ptr); + e = ptr; + + lib = string_concat(data->base, s, e); + if (lib) + { + string_fix_separators(lib); + + file = callbacks->file_open(lib, user_data); + if (file) + { + read_mtllib(data, file, callbacks, user_data); + callbacks->file_close(file, user_data); + } + + memory_dealloc(lib); + } + + return ptr; +} + + +static +void parse_buffer(fastObjData* data, const char* ptr, const char* end, const fastObjCallbacks* callbacks, void* user_data) +{ + const char* p; + + + p = ptr; + while (p != end) + { + p = skip_whitespace(p); + + switch (*p) + { + case 'v': + p++; + + switch (*p++) + { + case ' ': + case '\t': + p = parse_vertex(data, p); + break; + + case 't': + p = parse_texcoord(data, p); + break; + + case 'n': + p = parse_normal(data, p); + break; + + default: + p--; /* roll p++ back in case *p was a newline */ + } + break; + + case 'f': + p++; + + switch (*p++) + { + case ' ': + case '\t': + p = parse_face(data, p); + break; + + default: + p--; /* roll p++ back in case *p was a newline */ + } + break; + + case 'o': + p++; + + switch (*p++) + { + case ' ': + case '\t': + p = parse_object(data, p); + break; + + default: + p--; /* roll p++ back in case *p was a newline */ + } + break; + + case 'g': + p++; + + switch (*p++) + { + case ' ': + case '\t': + p = parse_group(data, p); + break; + + default: + p--; /* roll p++ back in case *p was a newline */ + } + break; + + case 'm': + p++; + if (p[0] == 't' && + p[1] == 'l' && + p[2] == 'l' && + p[3] == 'i' && + p[4] == 'b' && + is_whitespace(p[5])) + p = parse_mtllib(data, p + 5, callbacks, user_data); + break; + + case 'u': + p++; + if (p[0] == 's' && + p[1] == 'e' && + p[2] == 'm' && + p[3] == 't' && + p[4] == 'l' && + is_whitespace(p[5])) + p = parse_usemtl(data, p + 5); + break; + + case '#': + break; + } + + p = skip_line(p); + + data->line++; + } + if (array_size(data->mesh->colors) > 0) + { + /* Fill the remaining slots in the colors array */ + unsigned int ii; + for (ii = array_size(data->mesh->colors); ii < array_size(data->mesh->positions); ++ii) + { + array_push(data->mesh->colors, 1.0f); + } + } +} + + +void fast_obj_destroy(fastObjMesh* m) +{ + unsigned int ii; + + + for (ii = 0; ii < array_size(m->objects); ii++) + object_clean(&m->objects[ii]); + + for (ii = 0; ii < array_size(m->groups); ii++) + group_clean(&m->groups[ii]); + + for (ii = 0; ii < array_size(m->materials); ii++) + mtl_clean(&m->materials[ii]); + + array_clean(m->positions); + array_clean(m->texcoords); + array_clean(m->normals); + array_clean(m->colors); + array_clean(m->face_vertices); + array_clean(m->face_materials); + array_clean(m->indices); + array_clean(m->objects); + array_clean(m->groups); + array_clean(m->materials); + + memory_dealloc(m); +} + + +fastObjMesh* fast_obj_read(const char* path) +{ + fastObjCallbacks callbacks; + callbacks.file_open = file_open; + callbacks.file_close = file_close; + callbacks.file_read = file_read; + callbacks.file_size = file_size; + + return fast_obj_read_with_callbacks(path, &callbacks, 0); +} + + +fastObjMesh* fast_obj_read_with_callbacks(const char* path, const fastObjCallbacks* callbacks, void* user_data) +{ + fastObjData data; + fastObjMesh* m; + void* file; + char* buffer; + char* start; + char* end; + char* last; + fastObjUInt read; + fastObjUInt bytes; + + /* Check if callbacks are valid */ + if(!callbacks) + return 0; + + + /* Open file */ + file = callbacks->file_open(path, user_data); + if (!file) + return 0; + + + /* Empty mesh */ + m = (fastObjMesh*)(memory_realloc(0, sizeof(fastObjMesh))); + if (!m) + return 0; + + m->positions = 0; + m->texcoords = 0; + m->normals = 0; + m->colors = 0; + m->face_vertices = 0; + m->face_materials = 0; + m->indices = 0; + m->materials = 0; + m->objects = 0; + m->groups = 0; + + + /* Add dummy position/texcoord/normal */ + array_push(m->positions, 0.0f); + array_push(m->positions, 0.0f); + array_push(m->positions, 0.0f); + + array_push(m->texcoords, 0.0f); + array_push(m->texcoords, 0.0f); + + array_push(m->normals, 0.0f); + array_push(m->normals, 0.0f); + array_push(m->normals, 1.0f); + + + /* Data needed during parsing */ + data.mesh = m; + data.object = object_default(); + data.group = group_default(); + data.material = 0; + data.line = 1; + data.base = 0; + + + /* Find base path for materials/textures */ + { + const char* sep1 = strrchr(path, FAST_OBJ_SEPARATOR); + const char* sep2 = strrchr(path, FAST_OBJ_OTHER_SEP); + + /* Use the last separator in the path */ + const char* sep = sep2 && (!sep1 || sep1 < sep2) ? sep2 : sep1; + + if (sep) + data.base = string_substr(path, 0, sep - path + 1); + } + + + /* Create buffer for reading file */ + buffer = (char*)(memory_realloc(0, 2 * BUFFER_SIZE * sizeof(char))); + if (!buffer) + return 0; + + start = buffer; + for (;;) + { + /* Read another buffer's worth from file */ + read = (fastObjUInt)(callbacks->file_read(file, start, BUFFER_SIZE, user_data)); + if (read == 0 && start == buffer) + break; + + + /* Ensure buffer ends in a newline */ + if (read < BUFFER_SIZE) + { + if (read == 0 || start[read - 1] != '\n') + start[read++] = '\n'; + } + + end = start + read; + if (end == buffer) + break; + + + /* Find last new line */ + last = end; + while (last > buffer) + { + last--; + if (*last == '\n') + break; + } + + + /* Check there actually is a new line */ + if (*last != '\n') + break; + + last++; + + + /* Process buffer */ + parse_buffer(&data, buffer, last, callbacks, user_data); + + + /* Copy overflow for next buffer */ + bytes = (fastObjUInt)(end - last); + memmove(buffer, last, bytes); + start = buffer + bytes; + } + + + /* Flush final object/group */ + flush_object(&data); + object_clean(&data.object); + + flush_group(&data); + group_clean(&data.group); + + m->position_count = array_size(m->positions) / 3; + m->texcoord_count = array_size(m->texcoords) / 2; + m->normal_count = array_size(m->normals) / 3; + m->color_count = array_size(m->colors) / 3; + m->face_count = array_size(m->face_vertices); + m->index_count = array_size(m->indices); + m->material_count = array_size(m->materials); + m->object_count = array_size(m->objects); + m->group_count = array_size(m->groups); + + + /* Clean up */ + memory_dealloc(buffer); + memory_dealloc(data.base); + + callbacks->file_close(file, user_data); + + return m; +} + +#endif @@ -1,878 +1 @@ -// This is an attempt on build library for building C with C akin to nob.h from -// tsoding which is itself based on https://github.com/tsoding/nobuild -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -#ifndef B_H_ -#define B_H_ - -#define B_ASSERT assert -#define B_REALLOC realloc -#define B_FREE free - -#include <assert.h> -#include <stdbool.h> -#include <stdlib.h> -#include <stdio.h> -#include <stdarg.h> -#include <string.h> -#include <errno.h> -#include <ctype.h> - -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/stat.h> -#include <unistd.h> -#include <fcntl.h> -#include <dirent.h> - -#define B_ARRAY_LEN(array) (sizeof(array)/sizeof(array[0])) -#define B_ARRAY_GET(array, index) \ - (B_ASSERT(index >= 0), B_ASSERT(index < B_ARRAY_LEN(array)), array[index]) - -typedef enum { - B_INFO, - B_CMD, - B_BUILDING, - B_CHANGE, - B_WARNING, - B_ERROR, -} B_Log_Level; - -void b_log(B_Log_Level level, const char *fmt, ...); - -// It is an equivalent of shift command from bash. It basically pops a command line -// argument from the beginning. -char *b_shift_args(int *argc, char ***argv); - -typedef struct { - const char **items; - size_t count; - size_t capacity; -} B_File_Paths; - -typedef enum { - B_FILE_REGULAR = 0, - B_FILE_DIRECTORY, - B_FILE_SYMLINK, - B_FILE_OTHER, -} B_File_Type; - -bool b_mkdir_if_not_exists(const char *path); -bool b_copy_file(const char *src_path, const char *dst_path); -bool b_copy_directory_recursively(const char *src_path, const char *dst_path); -bool b_read_entire_dir(const char *parent, B_File_Paths *children); -bool b_write_entire_file(const char *path, const void *data, size_t size); -B_File_Type b_get_file_type(const char *path); - -#define b_return_defer(value) do { result = (value); goto defer; } while(0) - -// Initial capacity of a dynamic array -#define B_DA_INIT_CAP 256 - -// Append an item to a dynamic array -#define b_da_append(da, item) \ - do { \ - if ((da)->count >= (da)->capacity) { \ - (da)->capacity = (da)->capacity == 0 ? B_DA_INIT_CAP : (da)->capacity*2; \ - (da)->items = B_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ - B_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ - } \ - \ - (da)->items[(da)->count++] = (item); \ - } while (0) - -#define b_da_free(da) B_FREE((da).items) - -// Append several items to a dynamic array -#define b_da_append_many(da, new_items, new_items_count) \ - do { \ - if ((da)->count + (new_items_count) > (da)->capacity) { \ - if ((da)->capacity == 0) { \ - (da)->capacity = B_DA_INIT_CAP; \ - } \ - while ((da)->count + (new_items_count) > (da)->capacity) { \ - (da)->capacity *= 2; \ - } \ - (da)->items = B_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ - B_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ - } \ - memcpy((da)->items + (da)->count, (new_items), (new_items_count)*sizeof(*(da)->items)); \ - (da)->count += (new_items_count); \ - } while (0) - -typedef struct { - char *items; - size_t count; - size_t capacity; -} B_String_Builder; - -bool b_read_entire_file(const char *path, B_String_Builder *sb); - -// Append a sized buffer to a string builder -#define b_sb_append_buf(sb, buf, size) b_da_append_many(sb, buf, size) - -// Append a NULL-terminated string to a string builder -#define b_sb_append_cstr(sb, cstr) \ - do { \ - const char *s = (cstr); \ - size_t n = strlen(s); \ - b_da_append_many(sb, s, n); \ - } while (0) - -// Append a single NULL character at the end of a string builder. So then you can -// use it a NULL-terminated C string -#define b_sb_append_null(sb) b_da_append_many(sb, "", 1) - -// Free the memory allocated by a string builder -#define b_sb_free(sb) B_FREE((sb).items) - -// Process handle -typedef int B_Proc; -#define B_INVALID_PROC (-1) - -typedef struct { - B_Proc *items; - size_t count; - size_t capacity; -} B_Procs; - -bool b_procs_wait(B_Procs procs); - -// Wait until the process has finished -bool b_proc_wait(B_Proc proc); - -// A command - the main workhorse of B. B is all about building commands an running them -typedef struct { - const char **items; - size_t count; - size_t capacity; -} B_Cmd; - -// Render a string representation of a command into a string builder. Keep in mind the the -// string builder is not NULL-terminated by default. Use b_sb_append_null if you plan to -// use it as a C string. -void b_cmd_render(B_Cmd cmd, B_String_Builder *render); - -#define b_cmd_append(cmd, ...) \ - b_da_append_many(cmd, ((const char*[]){__VA_ARGS__}), (sizeof((const char*[]){__VA_ARGS__})/sizeof(const char*))) - -// Free all the memory allocated by command arguments -#define b_cmd_free(cmd) B_FREE(cmd.items) - -// Run command asynchronously -B_Proc b_cmd_run_async(B_Cmd cmd); - -// Run command synchronously -bool b_cmd_run_sync(B_Cmd cmd); - -#ifndef B_TEMP_CAPACITY -#define B_TEMP_CAPACITY (8*1024*1024) -#endif // B_TEMP_CAPACITY -char *b_temp_strdup(const char *cstr); -void *b_temp_alloc(size_t size); -char *b_temp_sprintf(const char *format, ...); -void b_temp_reset(void); -size_t b_temp_save(void); -void b_temp_rewind(size_t checkpoint); - -int is_path1_modified_after_path2(const char *path1, const char *path2); -bool b_rename(const char *old_path, const char *new_path); -int b_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count); -int b_needs_rebuild1(const char *output_path, const char *input_path); -int b_file_exists(const char *file_path); - -// TODO: add MinGW support for Go Rebuild Urself™ Technology -#ifndef B_REBUILD_URSELF -# if _WIN32 -# if defined(__GNUC__) -# define B_REBUILD_URSELF(binary_path, source_path) "gcc", "-o", binary_path, source_path -# elif defined(__clang__) -# define B_REBUILD_URSELF(binary_path, source_path) "clang", "-o", binary_path, source_path -# elif defined(_MSC_VER) -# define B_REBUILD_URSELF(binary_path, source_path) "cl.exe", b_temp_sprintf("/Fe:%s", (binary_path)), source_path -# endif -# else -# if defined(__clang__) -# define B_REBUILD_URSELF(binary_path, source_path) "clang", "-o", binary_path, source_path -# else -# define B_REBUILD_URSELF(binary_path, source_path) "cc", "-o", binary_path, source_path -# endif -# endif -#endif - -// Go Rebuild Urself™ Technology -// -// How to use it: -// int main(int argc, char** argv) { -// GO_REBUILD_URSELF(argc, argv); -// // actual work -// return 0; -// } -// -// After your added this macro every time you run ./build it will detect -// that you modified its original source code and will try to rebuild itself -// before doing any actual work. So you only need to bootstrap your build system -// once. -// -// The modification is detected by comparing the last modified times of the executable -// and its source code. The same way the make utility usually does it. -// -// The rebuilding is done by using the REBUILD_URSELF macro which you can redefine -// if you need a special way of bootstraping your build system. (which I personally -// do not recommend since the whole idea of build is to keep the process of bootstrapping -// as simple as possible and doing all of the actual work inside of the build) -// -#define B_GO_REBUILD_URSELF(argc, argv) \ - do { \ - const char *source_path = __FILE__; \ - assert(argc >= 1); \ - const char *binary_path = argv[0]; \ - \ - int rebuild_is_needed = b_needs_rebuild(binary_path, &source_path, 1); \ - if (rebuild_is_needed < 0) exit(1); \ - if (rebuild_is_needed) { \ - B_String_Builder sb = {0}; \ - b_sb_append_cstr(&sb, binary_path); \ - b_sb_append_cstr(&sb, ".old"); \ - b_sb_append_null(&sb); \ - \ - if (!b_rename(binary_path, sb.items)) exit(1); \ - B_Cmd rebuild = {0}; \ - b_cmd_append(&rebuild, B_REBUILD_URSELF(binary_path, source_path)); \ - bool rebuild_succeeded = b_cmd_run_sync(rebuild); \ - b_cmd_free(rebuild); \ - if (!rebuild_succeeded) { \ - b_rename(sb.items, binary_path); \ - exit(1); \ - } \ - \ - B_Cmd cmd = {0}; \ - b_da_append_many(&cmd, argv, argc); \ - if (!b_cmd_run_sync(cmd)) exit(1); \ - exit(0); \ - } \ - } while(0) -// The implementation idea is stolen from https://github.com/zhiayang/nabs - -typedef struct { - size_t count; - const char *data; -} B_String_View; - -const char *b_temp_sv_to_cstr(B_String_View sv); - -B_String_View b_sv_chop_by_delim(B_String_View *sv, char delim); -B_String_View b_sv_trim(B_String_View sv); -bool b_sv_eq(B_String_View a, B_String_View b); -B_String_View b_sv_from_cstr(const char *cstr); -B_String_View b_sv_from_parts(const char *data, size_t count); - -// printf macros for String_View -#ifndef SV_Fmt -#define SV_Fmt "%.*s" -#endif // SV_Fmt -#ifndef SV_Arg -#define SV_Arg(sv) (int) (sv).count, (sv).data -#endif // SV_Arg -// USAGE: -// String_View name = ...; -// printf("Name: "SV_Fmt"\n", SV_Arg(name)); - -#endif // B_H_ - -#ifdef B_IMPLEMENTATION - -static size_t b_temp_size = 0; -static char b_temp[B_TEMP_CAPACITY] = {0}; - -bool b_mkdir_if_not_exists(const char *path) -{ - int result = mkdir(path, 0755); - if (result < 0) { - if (errno == EEXIST) { - //b_log(B_INFO, "directory `%s` already exists", path); - return true; - } - b_log(B_ERROR, "could not create directory `%s`: %s", path, strerror(errno)); - return false; - } - - b_log(B_INFO, "created directory `%s`", path); - return true; -} - -bool b_copy_file(const char *src_path, const char *dst_path) -{ - b_log(B_INFO, "copying %s -> %s", src_path, dst_path); - - int src_fd = -1; - int dst_fd = -1; - size_t buf_size = 32*1024; - char *buf = B_REALLOC(NULL, buf_size); - B_ASSERT(buf != NULL && "Buy more RAM lol!!"); - bool result = true; - - src_fd = open(src_path, O_RDONLY); - if (src_fd < 0) { - b_log(B_ERROR, "Could not open file %s: %s", src_path, strerror(errno)); - b_return_defer(false); - } - - struct stat src_stat; - if (fstat(src_fd, &src_stat) < 0) { - b_log(B_ERROR, "Could not get mode of file %s: %s", src_path, strerror(errno)); - b_return_defer(false); - } - - dst_fd = open(dst_path, O_CREAT | O_TRUNC | O_WRONLY, src_stat.st_mode); - if (dst_fd < 0) { - b_log(B_ERROR, "Could not create file %s: %s", dst_path, strerror(errno)); - b_return_defer(false); - } - - for (;;) { - ssize_t n = read(src_fd, buf, buf_size); - if (n == 0) break; - if (n < 0) { - b_log(B_ERROR, "Could not read from file %s: %s", src_path, strerror(errno)); - b_return_defer(false); - } - char *buf2 = buf; - while (n > 0) { - ssize_t m = write(dst_fd, buf2, n); - if (m < 0) { - b_log(B_ERROR, "Could not write to file %s: %s", dst_path, strerror(errno)); - b_return_defer(false); - } - n -= m; - buf2 += m; - } - } - -defer: - free(buf); - close(src_fd); - close(dst_fd); - return result; -} - -void b_cmd_render(B_Cmd cmd, B_String_Builder *render) -{ - for (size_t i = 0; i < cmd.count; ++i) { - const char *arg = cmd.items[i]; - if (arg == NULL) break; - if (i > 0) b_sb_append_cstr(render, " "); - if (!strchr(arg, ' ')) { - b_sb_append_cstr(render, arg); - } else { - b_da_append(render, '\''); - b_sb_append_cstr(render, arg); - b_da_append(render, '\''); - } - } -} - -B_Proc b_cmd_run_async(B_Cmd cmd) -{ - if (cmd.count < 1) { - b_log(B_ERROR, "Could not run empty command"); - return B_INVALID_PROC; - } - - B_String_Builder sb = {0}; - b_cmd_render(cmd, &sb); - b_sb_append_null(&sb); - b_log(B_CMD, "%s", sb.items); - b_sb_free(sb); - memset(&sb, 0, sizeof(sb)); - - pid_t cpid = fork(); - if (cpid < 0) { - b_log(B_ERROR, "Could not fork child process: %s", strerror(errno)); - return B_INVALID_PROC; - } - - if (cpid == 0) { - // NOTE: This leaks a bit of memory in the child process. - // But do we actually care? It's a one off leak anyway... - B_Cmd cmd_null = {0}; - b_da_append_many(&cmd_null, cmd.items, cmd.count); - b_cmd_append(&cmd_null, NULL); - - if (execvp(cmd.items[0], (char * const*) cmd_null.items) < 0) { - b_log(B_ERROR, "Could not exec child process: %s", strerror(errno)); - exit(1); - } - B_ASSERT(0 && "unreachable"); - } - - return cpid; -} - -bool b_procs_wait(B_Procs procs) -{ - bool success = true; - for (size_t i = 0; i < procs.count; ++i) { - success = b_proc_wait(procs.items[i]) && success; - } - return success; -} - -bool b_proc_wait(B_Proc proc) -{ - if (proc == B_INVALID_PROC) return false; - - for (;;) { - int wstatus = 0; - if (waitpid(proc, &wstatus, 0) < 0) { - b_log(B_ERROR, "could not wait on command (pid %d): %s", proc, strerror(errno)); - return false; - } - - if (WIFEXITED(wstatus)) { - int exit_status = WEXITSTATUS(wstatus); - if (exit_status != 0) { - b_log(B_ERROR, "command exited with exit code %d", exit_status); - return false; - } - - break; - } - - if (WIFSIGNALED(wstatus)) { - b_log(B_ERROR, "command process was terminated by %s", strsignal(WTERMSIG(wstatus))); - return false; - } - } - - return true; -} - -bool b_cmd_run_sync(B_Cmd cmd) -{ - B_Proc p = b_cmd_run_async(cmd); - if (p == B_INVALID_PROC) return false; - return b_proc_wait(p); -} - -char *b_shift_args(int *argc, char ***argv) -{ - B_ASSERT(*argc > 0); - char *result = **argv; - (*argv) += 1; - (*argc) -= 1; - return result; -} - -void b_log(B_Log_Level level, const char *fmt, ...) -{ - switch (level) { - case B_INFO: - fprintf(stderr, "[INFO] "); - break; - case B_CMD: - fprintf(stderr, "[CMD] "); - break; - case B_BUILDING: - fprintf(stderr, "[BUILDING] "); - break; - case B_CHANGE: - fprintf(stderr, "[CHANGE] "); - break; - case B_WARNING: - fprintf(stderr, "[WARNING] "); - break; - case B_ERROR: - fprintf(stderr, "[ERROR] "); - break; - default: - B_ASSERT(0 && "unreachable"); - } - - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - fprintf(stderr, "\n"); -} - -bool b_read_entire_dir(const char *parent, B_File_Paths *children) -{ - bool result = true; - DIR *dir = NULL; - - dir = opendir(parent); - if (dir == NULL) { - b_log(B_ERROR, "Could not open directory %s: %s", parent, strerror(errno)); - b_return_defer(false); - } - - errno = 0; - struct dirent *ent = readdir(dir); - while (ent != NULL) { - b_da_append(children, b_temp_strdup(ent->d_name)); - ent = readdir(dir); - } - - if (errno != 0) { - b_log(B_ERROR, "Could not read directory %s: %s", parent, strerror(errno)); - b_return_defer(false); - } - -defer: - if (dir) closedir(dir); - return result; -} - -bool b_write_entire_file(const char *path, const void *data, size_t size) -{ - bool result = true; - - FILE *f = fopen(path, "wb"); - if (f == NULL) { - b_log(B_ERROR, "Could not open file %s for writing: %s\n", path, strerror(errno)); - b_return_defer(false); - } - - // len - // v - // aaaaaaaaaa - // ^ - // data - - const char *buf = data; - while (size > 0) { - size_t n = fwrite(buf, 1, size, f); - if (ferror(f)) { - b_log(B_ERROR, "Could not write into file %s: %s\n", path, strerror(errno)); - b_return_defer(false); - } - size -= n; - buf += n; - } - -defer: - if (f) fclose(f); - return result; -} - -B_File_Type b_get_file_type(const char *path) -{ - struct stat statbuf; - if (stat(path, &statbuf) < 0) { - b_log(B_ERROR, "Could not get stat of %s: %s", path, strerror(errno)); - return -1; - } - - switch (statbuf.st_mode & S_IFMT) { - case S_IFDIR: return B_FILE_DIRECTORY; - case S_IFREG: return B_FILE_REGULAR; - case S_IFLNK: return B_FILE_SYMLINK; - default: return B_FILE_OTHER; - } -} - -bool b_copy_directory_recursively(const char *src_path, const char *dst_path) -{ - bool result = true; - B_File_Paths children = {0}; - B_String_Builder src_sb = {0}; - B_String_Builder dst_sb = {0}; - size_t temp_checkpoint = b_temp_save(); - - B_File_Type type = b_get_file_type(src_path); - if (type < 0) return false; - - switch (type) { - case B_FILE_DIRECTORY: { - if (!b_mkdir_if_not_exists(dst_path)) b_return_defer(false); - if (!b_read_entire_dir(src_path, &children)) b_return_defer(false); - - for (size_t i = 0; i < children.count; ++i) { - if (strcmp(children.items[i], ".") == 0) continue; - if (strcmp(children.items[i], "..") == 0) continue; - - src_sb.count = 0; - b_sb_append_cstr(&src_sb, src_path); - b_sb_append_cstr(&src_sb, "/"); - b_sb_append_cstr(&src_sb, children.items[i]); - b_sb_append_null(&src_sb); - - dst_sb.count = 0; - b_sb_append_cstr(&dst_sb, dst_path); - b_sb_append_cstr(&dst_sb, "/"); - b_sb_append_cstr(&dst_sb, children.items[i]); - b_sb_append_null(&dst_sb); - - if (!b_copy_directory_recursively(src_sb.items, dst_sb.items)) { - b_return_defer(false); - } - } - } break; - - case B_FILE_REGULAR: { - if (!b_copy_file(src_path, dst_path)) { - b_return_defer(false); - } - } break; - - case B_FILE_SYMLINK: { - b_log(B_WARNING, "TODO: Copying symlinks is not supported yet"); - } break; - - case B_FILE_OTHER: { - b_log(B_ERROR, "Unsupported type of file %s", src_path); - b_return_defer(false); - } break; - - default: B_ASSERT(0 && "unreachable"); - } - -defer: - b_temp_rewind(temp_checkpoint); - b_da_free(src_sb); - b_da_free(dst_sb); - b_da_free(children); - return result; -} - -char *b_temp_strdup(const char *cstr) -{ - size_t n = strlen(cstr); - char *result = b_temp_alloc(n + 1); - B_ASSERT(result != NULL && "Increase B_TEMP_CAPACITY"); - memcpy(result, cstr, n); - result[n] = '\0'; - return result; -} - -void *b_temp_alloc(size_t size) -{ - if (b_temp_size + size > B_TEMP_CAPACITY) return NULL; - void *result = &b_temp[b_temp_size]; - b_temp_size += size; - return result; -} - -char *b_temp_sprintf(const char *format, ...) -{ - va_list args; - va_start(args, format); - int n = vsnprintf(NULL, 0, format, args); - va_end(args); - - B_ASSERT(n >= 0); - char *result = b_temp_alloc(n + 1); - B_ASSERT(result != NULL && "Extend the size of the temporary allocator"); - // TODO: use proper arenas for the temporary allocator; - va_start(args, format); - vsnprintf(result, n + 1, format, args); - va_end(args); - - return result; -} - -void b_temp_reset(void) -{ - b_temp_size = 0; -} - -size_t b_temp_save(void) -{ - return b_temp_size; -} - -void b_temp_rewind(size_t checkpoint) -{ - b_temp_size = checkpoint; -} - -const char *b_temp_sv_to_cstr(B_String_View sv) -{ - char *result = b_temp_alloc(sv.count + 1); - B_ASSERT(result != NULL && "Extend the size of the temporary allocator"); - memcpy(result, sv.data, sv.count); - result[sv.count] = '\0'; - return result; -} - -int b_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count) -{ - struct stat statbuf = {0}; - - if (stat(output_path, &statbuf) < 0) { - // NOTE: if output does not exist it 100% must be rebuilt - if (errno == ENOENT) { - b_log(B_BUILDING, "%s", output_path); - return 1; - } - b_log(B_ERROR, "could not stat %s: %s", output_path, strerror(errno)); - return -1; - } - int output_path_time = statbuf.st_mtime; - - for (size_t i = 0; i < input_paths_count; ++i) { - const char *input_path = input_paths[i]; - if (stat(input_path, &statbuf) < 0) { - // NOTE: non-existing input is an error cause it is needed for building in the first place - b_log(B_ERROR, "could not stat %s: %s", input_path, strerror(errno)); - return -1; - } - int input_path_time = statbuf.st_mtime; - // NOTE: if even a single input_path is fresher than output_path that's 100% rebuild - if (input_path_time > output_path_time) { - b_log(B_CHANGE, "%s", input_path); - return 1; - } - } - - return 0; -} - -int b_needs_rebuild1(const char *output_path, const char *input_path) -{ - return b_needs_rebuild(output_path, &input_path, 1); -} - -bool b_rename(const char *old_path, const char *new_path) -{ - b_log(B_INFO, "renaming %s -> %s", old_path, new_path); - if (rename(old_path, new_path) < 0) { - b_log(B_ERROR, "could not rename %s to %s: %s", old_path, new_path, strerror(errno)); - return false; - } - return true; -} - -bool b_read_entire_file(const char *path, B_String_Builder *sb) -{ - bool result = true; - - FILE *f = fopen(path, "rb"); - if (f == NULL) b_return_defer(false); - if (fseek(f, 0, SEEK_END) < 0) b_return_defer(false); - long m = ftell(f); - if (m < 0) b_return_defer(false); - if (fseek(f, 0, SEEK_SET) < 0) b_return_defer(false); - - size_t new_count = sb->count + m; - if (new_count > sb->capacity) { - sb->items = realloc(sb->items, new_count); - B_ASSERT(sb->items != NULL && "Buy more RAM lool!!"); - sb->capacity = new_count; - } - - fread(sb->items + sb->count, m, 1, f); - if (ferror(f)) { - // TODO: Afaik, ferror does not set errno. So the error reporting in defer is not correct in this case. - b_return_defer(false); - } - sb->count = new_count; - -defer: - if (!result) b_log(B_ERROR, "Could not read file %s: %s", path, strerror(errno)); - if (f) fclose(f); - return result; -} - -B_String_View b_sv_chop_by_delim(B_String_View *sv, char delim) -{ - size_t i = 0; - while (i < sv->count && sv->data[i] != delim) { - i += 1; - } - - B_String_View result = b_sv_from_parts(sv->data, i); - - if (i < sv->count) { - sv->count -= i + 1; - sv->data += i + 1; - } else { - sv->count -= i; - sv->data += i; - } - - return result; -} - -B_String_View b_sv_from_parts(const char *data, size_t count) -{ - B_String_View sv; - sv.count = count; - sv.data = data; - return sv; -} - -B_String_View b_sv_trim_left(B_String_View sv) -{ - size_t i = 0; - while (i < sv.count && isspace(sv.data[i])) { - i += 1; - } - - return b_sv_from_parts(sv.data + i, sv.count - i); -} - -B_String_View b_sv_trim_right(B_String_View sv) -{ - size_t i = 0; - while (i < sv.count && isspace(sv.data[sv.count - 1 - i])) { - i += 1; - } - - return b_sv_from_parts(sv.data, sv.count - i); -} - -B_String_View b_sv_trim(B_String_View sv) -{ - return b_sv_trim_right(b_sv_trim_left(sv)); -} - -B_String_View b_sv_from_cstr(const char *cstr) -{ - return b_sv_from_parts(cstr, strlen(cstr)); -} - -bool b_sv_eq(B_String_View a, B_String_View b) -{ - if (a.count != b.count) { - return false; - } else { - return memcmp(a.data, b.data, a.count) == 0; - } -} - -// RETURNS: -// 0 - file does not exists -// 1 - file exists -// -1 - error while checking if file exists. The error is logged -int b_file_exists(const char *file_path) -{ - struct stat statbuf; - if (stat(file_path, &statbuf) < 0) { - if (errno == ENOENT) return 0; - b_log(B_ERROR, "Could not check if file %s exists: %s", file_path, strerror(errno)); - return -1; - } - return 1; -} - -#endif +../../b/b.h
\ No newline at end of file diff --git a/src/render.c b/src/render.c index 5e9202b..0fd2561 100644 --- a/src/render.c +++ b/src/render.c @@ -621,7 +621,12 @@ recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) /* vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, s.vk_compute_pipeline); */ - /* vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, s.vk_compute_pipeline_layout, 0, 1, &s.frames[imageIndex].vk_compute_descriptor_set, 0, 0); */ + /* vkCmdBindDescriptorSets(commandBuffer, */ + /* VK_PIPELINE_BIND_POINT_COMPUTE, */ + /* s.vk_compute_pipeline_layout, */ + /* 0, 1, */ + /* &s.frames[imageIndex].vk_compute_descriptor_set, */ + /* 0, NULL); */ /* vkCmdDispatch(commandBuffer, PARTICLE_COUNT / 256, 1, 1); */ @@ -633,11 +638,11 @@ recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) vks_transition_image_layout_info transition_info = { 0 }; - transition_info.image = s.vk.swapchain.images[imageIndex]; - transition_info.format = VK_FORMAT_R8G8B8A8_SRGB; - transition_info.srcAccessMask = 0; - transition_info.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - transition_info.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + transition_info.image = s.vk.swapchain.images[imageIndex]; + transition_info.format = VK_FORMAT_R8G8B8A8_SRGB; + transition_info.srcAccessMask = 0; + transition_info.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + transition_info.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; transition_info.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; transition_info.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; transition_info.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; @@ -854,19 +859,19 @@ vulkan_create_uniform_buffers() void vulkan_create_descriptor_pool() { - VkDescriptorPoolSize poolSizes[3] = {0}; + VkDescriptorPoolSize poolSizes[2] = {0}; poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; poolSizes[0].descriptorCount = MAX_FRAMES_IN_FLIGHT; poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; poolSizes[1].descriptorCount = MAX_FRAMES_IN_FLIGHT; - poolSizes[2].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - poolSizes[2].descriptorCount = (uint32_t)(MAX_FRAMES_IN_FLIGHT) * 2; + /* poolSizes[2].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; */ + /* poolSizes[2].descriptorCount = (uint32_t)(MAX_FRAMES_IN_FLIGHT) * 2; */ VkDescriptorPoolCreateInfo poolInfo = {0}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; poolInfo.poolSizeCount = VK_ARRAY_LEN(poolSizes); poolInfo.pPoolSizes = poolSizes; - poolInfo.maxSets = MAX_FRAMES_IN_FLIGHT; + poolInfo.maxSets = MAX_FRAMES_IN_FLIGHT; //* 3; // why 3? VK_CHECK(vkCreateDescriptorPool(s.vk.device, &poolInfo, NULL, &s.vk_descriptor_pool)); } @@ -882,6 +887,9 @@ vulkan_create_descriptor_sets() /* compute_layouts[i] = s.vk_compute_descriptor_set_layout; */ } + /* layouts[0] = s.vk_descriptor_set_layout; */ + /* layouts[1] = s.vk_compute_descriptor_set_layout; */ + // TODO Find a way to group allocation for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { VkDescriptorSetAllocateInfo allocInfo = {0}; @@ -935,7 +943,7 @@ vulkan_create_descriptor_sets() /* storageBufferInfoLastFrame.range = sizeof(Particle) * PARTICLE_COUNT; */ /* descriptorWrites[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; */ - /* descriptorWrites[2].dstSet = s.frames[i].vk_compute_descriptor_set; */ + /* descriptorWrites[2].dstSet = s.frames[i].vk_descriptor_sets[1]; */ /* descriptorWrites[2].dstBinding = 1; */ /* descriptorWrites[2].dstArrayElement = 0; */ /* descriptorWrites[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; */ @@ -948,7 +956,7 @@ vulkan_create_descriptor_sets() /* storageBufferInfoCurrentFrame.range = sizeof(Particle) * PARTICLE_COUNT; */ /* descriptorWrites[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; */ - /* descriptorWrites[3].dstSet = s.frames[i].vk_compute_descriptor_set; */ + /* descriptorWrites[3].dstSet = s.frames[i].vk_descriptor_sets[1]; */ /* descriptorWrites[3].dstBinding = 2; */ /* descriptorWrites[3].dstArrayElement = 0; */ /* descriptorWrites[3].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; */ @@ -1366,7 +1374,8 @@ float rand_float() { return rand() / (float)RAND_MAX; } -void vulkan_create_compute_stuff() +void +vulkan_create_compute_stuff() { shaderc_compilation_result_t comp_result = load_compile_shader_data("src/shader.comp", shaderc_compute_shader); if (!comp_result) { @@ -1388,7 +1397,6 @@ void vulkan_create_compute_stuff() computeShaderStageInfo.module = compShaderModule; computeShaderStageInfo.pName = "main"; - srand((unsigned int)time(NULL)); Particle particles[PARTICLE_COUNT]; @@ -1478,6 +1486,89 @@ void vulkan_create_compute_stuff() } void +vulkan_create_compute_descriptor_stuff() +{ + + // descriptor pool + VkDescriptorPoolSize poolSizes[2] = {0}; + poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSizes[0].descriptorCount = MAX_FRAMES_IN_FLIGHT; + poolSizes[1].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + poolSizes[1].descriptorCount = (uint32_t)(MAX_FRAMES_IN_FLIGHT) * 2; + + VkDescriptorPoolCreateInfo poolInfo = {0}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = VK_ARRAY_LEN(poolSizes); + poolInfo.pPoolSizes = poolSizes; + poolInfo.maxSets = MAX_FRAMES_IN_FLIGHT; //* 3; // why 3? + + VK_CHECK(vkCreateDescriptorPool(s.vk.device, &poolInfo, NULL, &s.vk_compute_descriptor_pool)); + + // descriptor sets + VkDescriptorSetLayout compute_layouts[MAX_FRAMES_IN_FLIGHT] = {0}; + + for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + compute_layouts[i] = s.vk_compute_descriptor_set_layout; + } + + // TODO Find a way to group allocation + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + VkDescriptorSetAllocateInfo allocInfo = {0}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = s.vk_compute_descriptor_pool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = compute_layouts; + + VK_CHECK(vkAllocateDescriptorSets(s.vk.device, &allocInfo, &s.frames[i].vk_compute_descriptor_set)); + } + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + VkWriteDescriptorSet descriptorWrites[3] = {0}; + + VkDescriptorBufferInfo bufferInfo = {0}; + bufferInfo.buffer = s.frames[i].uniform_buffer.handle; + bufferInfo.offset = 0; + bufferInfo.range = sizeof(UniformBufferObject); + + descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[0].dstSet = s.frames[i].vk_compute_descriptor_set; + descriptorWrites[0].dstBinding = 0; + descriptorWrites[0].dstArrayElement = 0; + descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[0].descriptorCount = 1; + descriptorWrites[0].pBufferInfo = &bufferInfo; + + VkDescriptorBufferInfo storageBufferInfoLastFrame = {0}; + storageBufferInfoLastFrame.buffer = s.frames[(i - 1) % MAX_FRAMES_IN_FLIGHT].shader_storage_buffer.handle; + storageBufferInfoLastFrame.offset = 0; + storageBufferInfoLastFrame.range = sizeof(Particle) * PARTICLE_COUNT; + + descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[1].dstSet = s.frames[i].vk_compute_descriptor_set; + descriptorWrites[1].dstBinding = 1; + descriptorWrites[1].dstArrayElement = 0; + descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[1].descriptorCount = 1; + descriptorWrites[1].pBufferInfo = &storageBufferInfoLastFrame; + + VkDescriptorBufferInfo storageBufferInfoCurrentFrame = {0}; + storageBufferInfoCurrentFrame.buffer = s.frames[i].shader_storage_buffer.handle; + storageBufferInfoCurrentFrame.offset = 0; + storageBufferInfoCurrentFrame.range = sizeof(Particle) * PARTICLE_COUNT; + + descriptorWrites[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[2].dstSet = s.frames[i].vk_compute_descriptor_set; + descriptorWrites[2].dstBinding = 2; + descriptorWrites[2].dstArrayElement = 0; + descriptorWrites[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[2].descriptorCount = 1; + descriptorWrites[2].pBufferInfo = &storageBufferInfoCurrentFrame; + + vkUpdateDescriptorSets(s.vk.device, VK_ARRAY_LEN(descriptorWrites), descriptorWrites, 0, NULL); + } +} + +void init_vulkan() { vk_log(VK_WARN, "====================================\n"); @@ -1505,6 +1596,8 @@ init_vulkan() vulkan_create_descriptor_pool(); vulkan_create_descriptor_sets(); + vulkan_create_compute_descriptor_stuff(); + vulkan_create_command_buffer(); vulkan_create_sync_objects(); } diff --git a/src/shader.comp b/src/shader.comp new file mode 100644 index 0000000..0e89702 --- /dev/null +++ b/src/shader.comp @@ -0,0 +1,34 @@ +// -*- mode: glsl;-*- +#version 450 + +layout (binding = 0) uniform ParameterUBO { + float deltaTime; +} ubo; + +struct Particle { + vec2 position; + vec2 velocity; + vec4 color; +}; + +layout(std140, binding = 1) readonly buffer ParticleSSBOIn { + Particle particlesIn[ ]; +}; + +layout(std140, binding = 2) buffer ParticleSSBOOut { + Particle particlesOut[ ]; +}; + +layout (local_size_x = 256, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + uint index = gl_GlobalInvocationID.x; + + Particle particleIn = particlesIn[index]; + + particlesOut[index].position = particleIn.position + particleIn.velocity.xy * ubo.deltaTime; + particlesOut[index].velocity = particleIn.velocity; + particlesOut[index].color = particleIn.color; +} + diff --git a/src/shader.frag b/src/shader.frag index 6f5abb6..0b45743 100644 --- a/src/shader.frag +++ b/src/shader.frag @@ -21,14 +21,14 @@ void main() { outColor = vec4(fragColor * texture(texSampler, fragTexCoord).rgb , 1.0); //outColor = texture(texSampler, fragTexCoord); //outColor = vec4(fragColor, 1); -// float repeat = 10; -// float f = zmod(ubo.time, repeat); -// if (f < repeat / 2) { -// outColor = vec4(fragColor * radians(f) ,1); -// } else { -// outColor = vec4(fragColor * radians(1 - f) ,1); -// } -// //outColor = (vec4(1) * ubo.model + vec4(1)) * vec4(texture(texSampler, fragTexCoord).rgb * fragColor, 1.0); + //float repeat = 10; + //float f = zmod(ubo.time, repeat); + //if (f < repeat / 2) { + // outColor = vec4(fragColor * radians(f) ,1); + //} else { + // outColor = vec4(fragColor * radians(1 - f) ,1); + //} + //outColor = (vec4(1) * ubo.model + vec4(1)) * vec4(texture(texSampler, fragTexCoord).rgb * fragColor, 1.0); } float noise(vec2 p) { diff --git a/src/state.h b/src/state.h index 8177c77..de6108a 100644 --- a/src/state.h +++ b/src/state.h @@ -109,8 +109,7 @@ typedef struct state { char model_path[1000]; - - + VkDescriptorPool vk_compute_descriptor_pool; VkDescriptorSetLayout vk_compute_descriptor_set_layout; VkPipelineLayout vk_compute_pipeline_layout; VkPipeline vk_compute_pipeline; |
