summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgrm <grm@eyesin.space>2026-03-13 22:05:58 +0200
committergrm <grm@eyesin.space>2026-03-13 22:05:58 +0200
commit21edd2596ff657fc2de61e8848e74cf3c5c9ef01 (patch)
treeb62cac3c8433cca716bb5c699552d9c3ff386af3
parent67eec36945ed1706f23d0fe6d3017a09516dc42b (diff)
downloadcgame-21edd2596ff657fc2de61e8848e74cf3c5c9ef01.tar.gz
cgame-21edd2596ff657fc2de61e8848e74cf3c5c9ef01.tar.bz2
cgame-21edd2596ff657fc2de61e8848e74cf3c5c9ef01.zip
not sure
-rw-r--r--b.c40
-rw-r--r--lib/fast_obj.h1564
l---------[-rw-r--r--]src/b.h879
-rw-r--r--src/render.c121
-rw-r--r--src/shader.comp34
-rw-r--r--src/shader.frag16
-rw-r--r--src/state.h3
7 files changed, 1735 insertions, 922 deletions
diff --git a/b.c b/b.c
index 89ea2ce..b22b330 100644
--- a/b.c
+++ b/b.c
@@ -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
diff --git a/src/b.h b/src/b.h
index 82bf223..f09dfb8 100644..120000
--- a/src/b.h
+++ b/src/b.h
@@ -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;