summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am8
-rw-r--r--src/b.h860
-rw-r--r--src/render.c518
-rw-r--r--src/shader.frag73
-rw-r--r--src/shader.vert4
-rw-r--r--src/state.h39
-rw-r--r--src/vksetup.h172
7 files changed, 1540 insertions, 134 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 30ca37f..d2f2f8f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,6 +2,10 @@ bin_PROGRAMS = game
common_sources = state.h
+debug_flags = -O2 -DVKDEBUG
+release_flags = -O3
+common_flags = $(release_flags) -Wall -Wextra -march=native -fno-math-errno -funroll-loops -flto -pthread -ggdb -Wno-string-plus-int
+
AM_LDFLAGS = -fuse-ld=lld -flto
# -fwhole-program allows cross-file inlining, but only works when you put all
@@ -14,10 +18,10 @@ AM_LDFLAGS = -fuse-ld=lld -flto
# -fno-math-errno and stuff like that, without enabling
# -funsafe-math-optimizations. Some FP code can get big speedups from
# fast-math, like auto-vectorization.
-AM_CFLAGS = -march=native -fno-math-errno -funroll-loops -flto -pthread -ggdb -DVKDEBUG
+AM_CFLAGS = $(common_flags)
# AM_CFLAGS = -O0 -march=native -fno-math-errno -funroll-loops -flto -pthread -ggdb -fsanitize=address
-AM_CXXFLAGS = -Wall -Wextra -O2 -g -std=c++17
+AM_CXXFLAGS = $(common_flags) -std=c++17
game_SOURCES = render.c cplusplus.cpp $(common_sources)
game_LDADD = -lSDL2 -lm -lvulkan -lshaderc_shared -lstdc++
diff --git a/src/b.h b/src/b.h
new file mode 100644
index 0000000..cdf6772
--- /dev/null
+++ b/src/b.h
@@ -0,0 +1,860 @@
+// 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_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_INFO, "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_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) 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) 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
diff --git a/src/render.c b/src/render.c
index b6383c4..db3c168 100644
--- a/src/render.c
+++ b/src/render.c
@@ -1,4 +1,7 @@
+
+#include <emmintrin.h>
#include <stddef.h>
+#include <stdint.h>
#include <stdlib.h>
#include <time.h>
@@ -22,48 +25,25 @@
#define VKSETUP_IMPLEMENTATION
#include "vksetup.h"
+#define FAST_OBJ_IMPLEMENTATION
+#include "../lib/fast_obj.h"
+
+#define CGLTF_IMPLEMENTATION
+#include "../lib/cgltf.h"
+
//#include "cplusplus.h"
-#include "vkutil.h"
+//#include "vkutil.h"
#include "state.h"
+#define MODEL_PATH "assets/human.obj"
+#define TEXTURE_PATH "assets/viking_room.png"
+
+void load_model_obj();
+
// embedded clgm library
uint32_t currentFrame = 0;
state_t s;
-const char *const validation_layers[] = {
- "VK_LAYER_KHRONOS_validation"
-};
-const uint32_t validation_layer_count = VK_ARRAY_LEN(validation_layers);
-
-const char *const device_extensions[] = {
- VK_KHR_SWAPCHAIN_EXTENSION_NAME,
-};
-const uint32_t deviceExtensionCount = VK_ARRAY_LEN(device_extensions);
-
-
-#ifdef VKDEBUG
- const bool enableValidationLayers = true;
-#else
- const bool enableValidationLayers = false;
-#endif
-
-typedef struct {
- float x;
- float y;
-} V2;
-
-typedef struct {
- float x;
- float y;
- float z;
-} V3;
-
-typedef struct {
- V3 pos;
- V3 color;
- V2 texCoord;
-} Vertex;
-
/* Vertex vertices[] = { */
/* (Vertex) { (V2) {-0.2f, -0.5f}, (V3) {0.0f, 1.0f, 0.0f}}, */
/* (Vertex) { (V2) {0.5f, 0.3f}, (V3) {0.0f, 0.0f, 1.0f}}, */
@@ -97,7 +77,7 @@ Vertex vertices[] = {
};
const int VERTICES_SIZE = VK_ARRAY_LEN(vertices);
-const uint16_t indices[] = {
+const uint32_t indices[] = {
0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 8, 5, 4
};
const int INDICES_SIZE = VK_ARRAY_LEN(indices);
@@ -117,7 +97,8 @@ const int INDICES_SIZE = VK_ARRAY_LEN(indices);
/* }; */
/* const int INDICES_SIZE = VK_ARRAY_LEN(indices); */
-static int resizing_event_watcher(void* data, SDL_Event* event) {
+static int resizing_event_watcher(void *data, SDL_Event *event) {
+ (void) data;
if (event->type == SDL_WINDOWEVENT &&
event->window.event == SDL_WINDOWEVENT_RESIZED) {
s.sdl_window_resized = 1;
@@ -180,6 +161,9 @@ debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
{
+ (void) messageSeverity;
+ (void) messageType;
+ (void) pUserData;
vk_log(VK_ERROR, "validation layer: %s\n", pCallbackData->pMessage);
return VK_FALSE;
}
@@ -527,7 +511,6 @@ void
mouseCallback(SDL_Event *event)
{
if (event->type != SDL_MOUSEMOTION) return;
- if (!s.mouse_pressed) return;
float xoffset = event->motion.xrel;
float yoffset = -event->motion.yrel; // Reversed since y-coordinates range from bottom to top
@@ -535,7 +518,20 @@ mouseCallback(SDL_Event *event)
xoffset *= sensitivity;
yoffset *= sensitivity;
- update_camera(xoffset, yoffset);
+ if (s.mouse_pressed) {
+ update_camera(xoffset, yoffset);
+ } else if (s.middle_click) {
+ vec4 _up = {0, 1, 0, 0};
+ vec4 _right = {1, 0, 0, 0};
+
+ vec4 up, right;
+ glm_mat4_mulv(s.ubo.model, _up, up);
+ glm_mat4_mulv(s.ubo.model, _right, right);
+
+ glm_rotate(s.ubo.model, glm_rad(xoffset * glm_rad(90.0f)), GLM_ZUP);
+ glm_rotate(s.ubo.model, glm_rad(yoffset * glm_rad(90.0f)), GLM_XUP);
+ }
+ return;
}
void
@@ -612,7 +608,7 @@ vulkan_create_swap_chain()
VkImageView
-create_image_view(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags)
+create_image_view(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels)
{
VkImageViewCreateInfo viewInfo = {0};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@@ -621,7 +617,7 @@ create_image_view(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags
viewInfo.format = format;
viewInfo.subresourceRange.aspectMask = aspectFlags;
viewInfo.subresourceRange.baseMipLevel = 0;
- viewInfo.subresourceRange.levelCount = 1;
+ viewInfo.subresourceRange.levelCount = mipLevels;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
@@ -635,7 +631,7 @@ void
vulkan_create_image_views()
{
for (size_t i = 0; i < s.vk_swap_chain_image_count; i++) {
- s.vk_swap_chain_image_views[i] = create_image_view(s.vk_swap_chain_images[i], s.vk_swap_chain_image_format, VK_IMAGE_ASPECT_COLOR_BIT);
+ s.vk_swap_chain_image_views[i] = create_image_view(s.vk_swap_chain_images[i], s.vk_swap_chain_image_format, VK_IMAGE_ASPECT_COLOR_BIT, 1);
}
vk_log(VK_INFO, "Vulkan image views created!\n");
}
@@ -725,7 +721,7 @@ findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
}
void
-createImage(uint32_t width, uint32_t height, VkFormat format,
+createImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkFormat format,
VkImageTiling tiling, VkImageUsageFlags usage,
VkMemoryPropertyFlags properties, VkImage *image,
VkDeviceMemory *imageMemory)
@@ -736,7 +732,7 @@ createImage(uint32_t width, uint32_t height, VkFormat format,
imageInfo.extent.width = width;
imageInfo.extent.height = height;
imageInfo.extent.depth = 1;
- imageInfo.mipLevels = 1;
+ imageInfo.mipLevels = mipLevels;
imageInfo.arrayLayers = 1;
imageInfo.format = format;
imageInfo.tiling = tiling;
@@ -807,7 +803,7 @@ void
transitionImageLayout(VkImage image, VkFormat format,
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
- VkImageLayout oldLayout, VkImageLayout newLayout)
+ VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels)
{
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
@@ -833,7 +829,7 @@ transitionImageLayout(VkImage image, VkFormat format,
}
barrier.subresourceRange.baseMipLevel = 0;
- barrier.subresourceRange.levelCount = 1;
+ barrier.subresourceRange.levelCount = mipLevels;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
@@ -880,18 +876,18 @@ void
vulkan_create_depth_resources()
{
VkFormat depthFormat = findDepthFormat();
- createImage(s.vk_swap_chain_extent.width, s.vk_swap_chain_extent.height,
+ createImage(s.vk_swap_chain_extent.width, s.vk_swap_chain_extent.height, 1,
depthFormat, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&s.vk_depth_image, &s.vk_depth_image_memory);
s.vk_depth_image_view = create_image_view(s.vk_depth_image, depthFormat,
- VK_IMAGE_ASPECT_DEPTH_BIT);
+ VK_IMAGE_ASPECT_DEPTH_BIT, 1);
transitionImageLayout(s.vk_depth_image, depthFormat,
VK_ACCESS_NONE, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
- VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+ VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 1);
}
void
@@ -1013,7 +1009,7 @@ vulkan_create_graphics_pipeline(VkPolygonMode polygon_mode)
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_NONE;
//rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
- //rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
rasterizer.depthBiasConstantFactor = 0.0f; // Optional
rasterizer.depthBiasClamp = 0.0f; // Optional
@@ -1159,7 +1155,7 @@ recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
s.vk_swap_chain_images[imageIndex], VK_FORMAT_R8G8B8A8_SRGB, 0,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
- VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 1);
VkRenderingAttachmentInfo colorAttachment = {0};
colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
@@ -1168,7 +1164,7 @@ recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.clearValue.color =
- (VkClearColorValue){0.0f, 0.0f, 0.0f, 1.0f};
+ (VkClearColorValue){{0.0f, 0.0f, 0.0f, 1.0f}};
VkRenderingAttachmentInfo depthAttachment = {0};
depthAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
@@ -1210,13 +1206,13 @@ recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(commandBuffer, s.vk_index_buffer, 0,
- VK_INDEX_TYPE_UINT16);
+ VK_INDEX_TYPE_UINT32);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
s.vk_pipeline_layout, 0, 1,
&s.frames[currentFrame].vk_descriptor_set, 0, NULL);
- vkCmdDrawIndexed(commandBuffer, INDICES_SIZE, 1, 0, 0, 0);
+ vkCmdDrawIndexed(commandBuffer, s.indices_count, 1, 0, 0, 0);
vkCmdEndRendering(commandBuffer);
transitionImageLayout(s.vk_swap_chain_images[imageIndex],
@@ -1225,7 +1221,7 @@ recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
- VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
+ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 1);
VK_CHECK(vkEndCommandBuffer(commandBuffer));
}
@@ -1288,7 +1284,8 @@ void copy_buffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
void
vulkan_create_vertex_buffer()
{
- VkDeviceSize bufferSize = sizeof(vertices[0]) * VERTICES_SIZE;
+ VkDeviceSize bufferSize = sizeof(s.vertices[0]) * s.vertices_count;
+ /* VkDeviceSize bufferSize = sizeof(vertices[0]) * VERTICES_SIZE; */
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
@@ -1300,7 +1297,8 @@ vulkan_create_vertex_buffer()
void* data;
VK_CHECK(vkMapMemory(s.vk_device, stagingBufferMemory, 0, bufferSize, 0, &data));
- memcpy(data, vertices, (size_t) bufferSize);
+ // memcpy(data, vertices, (size_t) bufferSize);
+ memcpy(data, s.vertices, (size_t) bufferSize);
vkUnmapMemory(s.vk_device, stagingBufferMemory);
createBuffer(bufferSize,
@@ -1317,7 +1315,8 @@ vulkan_create_vertex_buffer()
void
vulkan_create_index_buffer()
{
- VkDeviceSize bufferSize = sizeof(indices[0]) * INDICES_SIZE;
+ VkDeviceSize bufferSize = sizeof(s.indices[0]) * s.indices_count;
+ /* VkDeviceSize bufferSize = sizeof(indices[0]) * INDICES_SIZE; */
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
@@ -1329,7 +1328,8 @@ vulkan_create_index_buffer()
void* data;
VK_CHECK(vkMapMemory(s.vk_device, stagingBufferMemory, 0, bufferSize, 0, &data));
- memcpy(data, indices, (size_t) bufferSize);
+ memcpy(data, s.indices, (size_t) bufferSize);
+ /* memcpy(data, indices, (size_t) bufferSize); */
vkUnmapMemory(s.vk_device, stagingBufferMemory);
createBuffer(bufferSize,
@@ -1544,6 +1544,25 @@ handle_input(bool * quit)
case SDLK_l:
s.rotate = s.rotate ? 0 : 1;
break;
+ case SDLK_m:
+ vkDeviceWaitIdle(s.vk_device);
+ free(s.vertices);
+ free(s.indices);
+ if (!strcmp(s.model_path, "assets/human.obj"))
+ strcpy(s.model_path, "assets/viking_room.obj");
+ else if (!strcmp(s.model_path, "assets/viking_room.obj"))
+ strcpy(s.model_path, "assets/test.obj");
+ else
+ strcpy(s.model_path, "assets/human.obj");
+ load_model_obj();
+ vkDestroyBuffer(s.vk_device, s.vk_vertex_buffer, NULL);
+ vkFreeMemory(s.vk_device, s.vk_vertex_buffer_memory, NULL);
+
+ vkDestroyBuffer(s.vk_device, s.vk_index_buffer, NULL);
+ vkFreeMemory(s.vk_device, s.vk_index_buffer_memory, NULL);
+ vulkan_create_vertex_buffer();
+ vulkan_create_index_buffer();
+ break;
case SDLK_r:
s.camera.pos[0] = 1.0f;
s.camera.pos[1] = 1.0f;
@@ -1562,6 +1581,10 @@ handle_input(bool * quit)
s.camera.lastX = 400.0f;
s.camera.lastY = 300.0f;
s.camera.fov = 45.0f;
+
+ glm_mat4_identity(s.ubo.model);
+
+ s.zoom = 10;
update_camera(0,0);
break;
}
@@ -1569,19 +1592,28 @@ handle_input(bool * quit)
else if (e.type == SDL_MOUSEWHEEL) {
if(e.wheel.y > 0) // scroll up
{
- s.zoom += 1;
+ s.zoom -= 1;
}
else if(e.wheel.y < 0) // scroll down
{
- s.zoom -= 1;
+ s.zoom += 1;
if (s.zoom == -100) s.zoom = 1;
}
}
else if (e.type == SDL_MOUSEBUTTONDOWN) {
- s.mouse_pressed = 1;
+ if (e.button.button != 2) {
+ s.mouse_pressed = 1;
+ } else {
+ s.middle_click = 1;
+ }
+
}
else if (e.type == SDL_MOUSEBUTTONUP) {
- s.mouse_pressed = 0;
+ if (e.button.button != 2) {
+ s.mouse_pressed = 0;
+ } else {
+ s.middle_click = 0;
+ }
}
mouseCallback(&e);
}
@@ -1617,13 +1649,88 @@ copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width,
endSingleTimeCommands(commandBuffer);
}
+void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels) {
+ VkCommandBuffer commandBuffer = beginSingleTimeCommands();
+
+ VkImageMemoryBarrier barrier = {0};
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.image = image;
+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = 1;
+ barrier.subresourceRange.levelCount = 1;
+
+ int32_t mipWidth = texWidth;
+ int32_t mipHeight = texHeight;
+
+ for (uint32_t i = 1; i < mipLevels; i++) {
+ barrier.subresourceRange.baseMipLevel = i - 1;
+ barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+
+ vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
+ &barrier);
+
+ VkImageBlit blit = {0};
+ blit.srcOffsets[0] = (VkOffset3D){ 0, 0, 0 };
+ blit.srcOffsets[1] = (VkOffset3D){ mipWidth, mipHeight, 1 };
+ blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ blit.srcSubresource.mipLevel = i - 1;
+ blit.srcSubresource.baseArrayLayer = 0;
+ blit.srcSubresource.layerCount = 1;
+ blit.dstOffsets[0] = (VkOffset3D){ 0, 0, 0 };
+ blit.dstOffsets[1] = (VkOffset3D){ mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 };
+ blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ blit.dstSubresource.mipLevel = i;
+ blit.dstSubresource.baseArrayLayer = 0;
+ blit.dstSubresource.layerCount = 1;
+
+ vkCmdBlitImage(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit,
+ VK_FILTER_LINEAR);
+
+ barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+
+ vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0,
+ NULL, 1, &barrier);
+
+ if (mipWidth > 1) mipWidth /= 2;
+ if (mipHeight > 1) mipHeight /= 2;
+ }
+
+ /* barrier.subresourceRange.baseMipLevel = mipLevels - 1; */
+ /* barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; */
+ /* barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; */
+ /* barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; */
+ /* barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; */
+
+ /* vkCmdPipelineBarrier(commandBuffer, */
+ /* VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, */
+ /* 0, NULL, */
+ /* 0, NULL, */
+ /* 1, &barrier); */
+
+ endSingleTimeCommands(commandBuffer);
+}
+
void
vulkan_create_texture_image()
{
int texWidth, texHeight, texChannels;
- stbi_uc* pixels = stbi_load("assets/texture.jpg", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
+ stbi_uc* pixels = stbi_load(TEXTURE_PATH, &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
VkDeviceSize imageSize = texWidth * texHeight * 4;
+ s.vk_mip_levels = (uint32_t)floor(log2(fmax(texWidth, texHeight))) + 1;
+
if (!pixels) {
vk_log(VK_ERROR, "failed to load texture image!\n");
}
@@ -1639,9 +1746,9 @@ vulkan_create_texture_image()
stbi_image_free(pixels);
- createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_SRGB,
+ createImage(texWidth, texHeight, s.vk_mip_levels, VK_FORMAT_R8G8B8A8_SRGB,
VK_IMAGE_TILING_OPTIMAL,
- VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &s.vk_texture_image,
&s.vk_texture_image_memory);
@@ -1649,15 +1756,16 @@ vulkan_create_texture_image()
s.vk_texture_image, VK_FORMAT_R8G8B8A8_SRGB, 0,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, s.vk_mip_levels);
copyBufferToImage(stagingBuffer, s.vk_texture_image, (uint32_t)texWidth,
(uint32_t)texHeight);
+ generateMipmaps(s.vk_texture_image, VK_FORMAT_R8G8B8A8_SRGB, texWidth, texHeight, s.vk_mip_levels);
transitionImageLayout(
s.vk_texture_image, VK_FORMAT_R8G8B8A8_SRGB,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
- VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, s.vk_mip_levels);
vkDestroyBuffer(s.vk_device, stagingBuffer, NULL);
vkFreeMemory(s.vk_device, stagingBufferMemory, NULL);
@@ -1666,7 +1774,7 @@ vulkan_create_texture_image()
void
vulkan_create_texture_image_view()
{
- s.vk_texture_image_view = create_image_view(s.vk_texture_image, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT);
+ s.vk_texture_image_view = create_image_view(s.vk_texture_image, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, s.vk_mip_levels);
}
void vulkan_create_texture_sampler() {
@@ -1690,12 +1798,235 @@ void vulkan_create_texture_sampler() {
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
- samplerInfo.maxLod = 0.0f;
+ samplerInfo.maxLod = VK_LOD_CLAMP_NONE;
VK_CHECK(vkCreateSampler(s.vk_device, &samplerInfo, NULL, &s.vk_texture_sampler));
}
void
+load_model_obj()
+{
+ fastObjMesh *m = fast_obj_read(s.model_path);
+
+ if (!m) {
+ vk_log(VK_ERROR, "Can't load object\n");
+ }
+
+ s.indices_count = m->index_count;
+ s.indices = (uint32_t *)malloc(s.indices_count * sizeof(uint32_t));
+ s.vertices_count = 0;
+
+ if (m->group_count > 1) {
+ vk_log(VK_WARN, "fast_obj Mesh with groups > 1 not supported.");
+ }
+
+ /*
+ Since blender exports objs indexed we use those same indices for our
+ indexed draw call.
+ */
+
+ /* Count indexs and vertices */
+ size_t c = 0;
+ for (size_t ii = 0; ii < m->group_count; ii++) {
+ const fastObjGroup *grp = &m->groups[ii];
+ for (unsigned int jj = 0; jj < grp->face_count; jj++) {
+ unsigned int fv = m->face_vertices[grp->face_offset + jj];
+ for (unsigned int kk = 0; kk < fv; kk++) {
+ /* position index */
+ uint32_t mip = m->indices[grp->index_offset + c].p - 1; /* make index start from zero */
+
+ /* flag if we've seen the index*/
+ int index_seen = 0;
+ for (size_t i = 0; i < c; i++) {
+ if (mip == s.indices[i]) {
+ index_seen = 1;
+ break;
+ }
+ }
+ s.indices[c] = mip;
+ if (!index_seen) {
+ /* If not seen, incremet vertices */
+ s.vertices_count++;
+ }
+ c++;
+ }
+ }
+ }
+
+ printf("vertex count %ld!!!!!!!!!!!!!!!!!!\n", s.vertices_count);
+ s.vertices = (Vertex *)malloc(s.vertices_count * sizeof(Vertex));
+
+ /* for (size_t i = 0; i < s.indices_count; i++) { */
+ /* uint32_t mip = s.indices[i]; */
+ /* int index_seen = 0; */
+ /* for (size_t j = 0; j < i; j++) { */
+ /* if (mip == s.indices[j]) { */
+ /* index_seen = 1; */
+ /* break; */
+ /* } */
+ /* } */
+ /* if (!index_seen) { */
+ /* s.vertices[mip].pos.x = m->positions[3 * (mip + 1) + 0]; */
+ /* s.vertices[mip].pos.y = m->positions[3 * (mip + 1) + 1]; */
+ /* s.vertices[mip].pos.z = m->positions[3 * (mip + 1) + 2]; */
+ /* } */
+ /* } */
+
+ for (size_t ii = 0; ii < m->group_count; ii++) {
+ const fastObjGroup *grp = &m->groups[ii];
+ size_t idx = 0;
+ for (unsigned int jj = 0; jj < grp->face_count; jj++) {
+ unsigned int fv = m->face_vertices[grp->face_offset + jj];
+ for (unsigned int kk = 0; kk < fv; kk++) {
+ fastObjIndex mi = m->indices[grp->index_offset + idx];
+
+ int flag = 0;
+ for (size_t i = 0; i < idx; i++) {
+ /* if it exists */
+ if (mi.p - 1 == s.indices[i]) {
+ flag = 1;
+ break;
+ }
+ }
+ if (!flag) {
+ int index = mi.p - 1; /* zero indexed */
+ if (mi.p) {
+ s.vertices[index].pos.x = m->positions[3 * mi.p + 0];
+ s.vertices[index].pos.y = m->positions[3 * mi.p + 1];
+ s.vertices[index].pos.z = m->positions[3 * mi.p + 2];
+ }
+ if (mi.t) {
+ s.vertices[index].texCoord.x = m->texcoords[2 * mi.t + 0];
+ s.vertices[index].texCoord.y = 1.0f - m->texcoords[2 * mi.t + 1];
+ }
+ if (mi.n) {
+ s.vertices[index].color.x = m->normals[3 * mi.n + 0];
+ s.vertices[index].color.y = m->normals[3 * mi.n + 1];
+ s.vertices[index].color.z = m->normals[3 * mi.n + 2];
+ }
+ }
+ idx++;
+ }
+ }
+ }
+
+ fast_obj_destroy(m);
+}
+
+void load_model_gltf() {
+ // TODO maybe copy the raylib implemenetation
+ /*
+ RESTRICTIONS:
+ - Only triangle meshes supported
+ - Vertex attribute types and formats supported:
+ > Vertices (position): vec3: float
+ > Normals: vec3: float
+ > Texcoords: vec2: float
+ > Colors: vec4: u8, u16, f32 (normalized)
+ > Indices: u16, u32 (truncated to u16)
+ - Node hierarchies or transforms not supported
+ */
+
+ /* cgltf_options options; */
+ /* memset(&options, 0, sizeof(cgltf_options)); */
+ /* cgltf_data* data = NULL; */
+ /* cgltf_result result = cgltf_parse_file(&options, MODEL_PATH, &data); */
+
+ /* if (result == cgltf_result_success) */
+ /* result = cgltf_load_buffers(&options, data, MODEL_PATH); */
+
+ /* if (result == cgltf_result_success) */
+ /* result = cgltf_validate(data); */
+
+ /* if (data->meshes_count < 1) { */
+ /* cgltf_free(data); */
+ /* return; */
+ /* } */
+
+ /* int idx = 0; */
+ /* int meshIndex = 0; */
+ /* for (unsigned int i = 0, meshIndex = 0; i < data->meshes_count; i++) { */
+ /* for (unsigned int p = 0; p < data->meshes[i].primitives_count; p++) { */
+ /* // NOTE: We only support primitives defined by triangles */
+ /* // Other alternatives: points, lines, line_strip, triangle_strip */
+ /* if (data->meshes[i].primitives[p].type != cgltf_primitive_type_triangles) continue; */
+
+ /* // NOTE: Attributes data could be provided in several data formats (8, 8u, 16u, 32...), */
+ /* // Only some formats for each attribute type are supported, read info at the top of this function! */
+
+ /* for (unsigned int j = 0; j < data->meshes[i].primitives[p].attributes_count; j++) { */
+ /* // Check the different attributes for every primitive */
+ /* if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_position) { // POSITION, vec3, float */
+ /* cgltf_accessor *attribute = data->meshes[i].primitives[p].attributes[j].data; */
+
+ /* // WARNING: SPECS: POSITION accessor MUST have its min and max properties defined */
+
+ /* if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_32f)) { */
+ /* // Init raylib mesh vertices to copy glTF attribute data */
+ /* s.vertices_count = attribute->count / 3; */
+ /* s.vertices = calloc(attribute->count, attribute->count*sizeof(Vertex)); */
+
+ /* size_t float_count = cgltf_accessor_unpack_floats(attribute, NULL, 0); */
+ /* printf("This many floats: %ld\n", float_count); */
+ /* float floats[float_count] = {}; */
+ /* cgltf_accessor_unpack_floats(attribute, floats, float_count); */
+
+ /* for (unsigned int k = 0; k < attribute->count + 3; k+=3) { */
+ /* s.vertices[idx].pos.x = floats[k + 0]; */
+ /* s.vertices[idx].pos.y = floats[k + 1]; */
+ /* s.vertices[idx].pos.z = floats[k + 2]; */
+ /* //s.indices[idx] = idx; */
+ /* idx++; */
+ /* } */
+ /* // Load 3 components of float data type into mesh.vertices */
+ /* } */
+ /* } */
+ /* } */
+ /* if (data->meshes[i].primitives[p].indices != NULL) { */
+ /* printf("THERE ARE INDIXCES!!!\n"); */
+ /* cgltf_accessor *attribute = data->meshes[i].primitives[p].indices; */
+
+ /* s.indices_count = attribute->count / 3; */
+ /* s.indices = calloc(attribute->count, attribute->count*sizeof(uint32_t)); */
+
+ /* if (attribute->component_type == cgltf_component_type_r_16u) { */
+ /* size_t float_count = cgltf_accessor_unpack_indices(attribute, NULL, 0, 0); */
+ /* printf("This many floats: %ld\n", float_count); */
+ /* uint16_t floats[float_count] = {}; */
+ /* cgltf_accessor_unpack_indices(attribute, floats, float_count, */
+ /* float_count); */
+
+ /* for (int x = 0; x < float_count; x++) { */
+ /* printf("%d \n", floats[x]); */
+ /* } */
+
+ /* } */
+ /* else if (attribute->component_type == cgltf_component_type_r_32u) { */
+ /* printf("ASDASDASDAS2222222!\n"); */
+ /* // Init raylib mesh indices to copy glTF attribute data */
+ /* /\* model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short)); *\/ */
+
+ /* /\* // Load data into a temp buffer to be converted to raylib data type *\/ */
+ /* /\* unsigned int *temp = malloc(attribute->count*sizeof(unsigned int)); *\/ */
+ /* /\* LOAD_ATTRIBUTE(attribute, 1, unsigned int, temp); *\/ */
+
+ /* /\* // Convert data to raylib indices data type (unsigned short) *\/ */
+ /* /\* for (unsigned int d = 0; d < attribute->count; d++) model.meshes[meshIndex].indices[d] = (unsigned short)temp[d]; *\/ */
+
+ /* //free(temp); */
+ /* } */
+ /* } */
+ /* } */
+ /* } */
+
+ /* for (int i = 0; i < s.vertices_count; i++) { */
+ /* printf("%d: %f %f %f\n",i, s.vertices[i].pos.x, s.vertices[i].pos.y, s.vertices[i].pos.z); */
+ /* } */
+
+ /* cgltf_free(data); */
+}
+
+void
init_vulkan()
{
vk_log(VK_WARN, "====================================\n");
@@ -1704,8 +2035,8 @@ init_vulkan()
//vulkan_create_instance();
s.vk_instance =
- vksetup_create_instance(enableValidationLayers, validation_layers,
- validation_layer_count, s.sdl_window);
+ vks_create_instance(enableValidationLayers, validation_layers,
+ validation_layer_count, s.sdl_window);
// vulkan_setup_debug_messenger();
vulkan_create_surface();
vulkan_pick_physical_device();
@@ -1719,6 +2050,8 @@ init_vulkan()
vulkan_create_texture_image();
vulkan_create_texture_image_view();
vulkan_create_texture_sampler();
+ load_model_obj();
+ //load_model_gltf();
vulkan_create_vertex_buffer();
vulkan_create_index_buffer();
vulkan_create_uniform_buffers();
@@ -1777,6 +2110,9 @@ close_vulkan()
if (s.prev_frag_result) {
shaderc_result_release(s.prev_frag_result);
}
+
+ free(s.vertices);
+ free(s.indices);
}
float
@@ -1798,24 +2134,28 @@ current_time()
}
void
-updateUniformBuffer(uint32_t currentImage)
+updateUniformBuffer(uint32_t currentImage, float dt)
{
- float time = current_time();
-
UniformBufferObject ubo = {0};
+ ubo.time = current_time();
+ ubo.dt = dt;
+
ubo.resolution[0] = s.vk_swap_chain_extent.width;
ubo.resolution[1] = s.vk_swap_chain_extent.height;
- glm_mat4_identity(ubo.model);
+ //glm_mat4_identity(ubo.model);
+ glm_mat4_dup(s.ubo.model, ubo.model);
+
+ if (s.rotate)
+ glm_rotate(ubo.model, glm_rad(30 * dt * glm_rad(90.0f)), GLM_ZUP);
- if (!s.rotate)
- glm_rotate(ubo.model, glm_rad(50 * time * glm_rad(90.0f)), GLM_ZUP);
+ glm_mat4_dup(ubo.model, s.ubo.model);
- vec3 eye = GLM_VEC3_ONE_INIT;
- vec3 center = GLM_VEC3_ZERO_INIT;
+ /* vec3 eye = GLM_VEC3_ONE_INIT; */
+ /* vec3 center = GLM_VEC3_ZERO_INIT; */
-// vk_log(VK_INFO, "{%.2f, %.2f, %.2f}\n", s.camera.front[0], s.camera.front[1], s.camera.front[2]);
+ // vk_log(VK_INFO, "{%.2f, %.2f, %.2f}\n", s.camera.front[0], s.camera.front[1], s.camera.front[2]);
/* glm_vec3_add(s.camera.pos, s.camera.front, center); */
glm_lookat(s.camera.pos, s.camera.front, s.camera.up, ubo.view);
@@ -1830,8 +2170,15 @@ updateUniformBuffer(uint32_t currentImage)
memcpy(s.frames[currentImage].vk_uniform_buffer_mapped, &ubo, sizeof(ubo));
}
+float prev_time = 0;
+
void
draw_frame() {
+ float time = current_time();
+
+ float dt = time - prev_time;
+
+
vkWaitForFences(s.vk_device, 1, &s.frames[currentFrame].in_flight_fence, VK_TRUE, UINT64_MAX);
uint32_t imageIndex;
@@ -1844,7 +2191,7 @@ draw_frame() {
vk_log(VK_ERROR, "failed to acquire swap chain image!\n");
}
- updateUniformBuffer(currentFrame);
+ updateUniformBuffer(currentFrame, dt);
vkResetFences(s.vk_device, 1, &s.frames[currentFrame].in_flight_fence);
@@ -1892,11 +2239,16 @@ draw_frame() {
}
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
+
+ prev_time = time;
}
int
-main(int argc, char* args[])
+main(int argc, char* argv[])
{
+ (void) argc;
+ (void)argv;
+
init_state(&s);
if (!init()) {
vk_log(VK_INFO, "Failed to initialize!\n");
diff --git a/src/shader.frag b/src/shader.frag
index 3f76290..075c2ae 100644
--- a/src/shader.frag
+++ b/src/shader.frag
@@ -12,10 +12,77 @@ layout(binding = 0) uniform UniformBufferObject {
mat4 view;
mat4 proj;
vec2 resolution;
+ float time;
+ float dt;
} ubo;
void main() {
- //outColor = vec4(fragColor * texture(texSampler, fragTexCoord).rgb, 1.0);
- outColor = texture(texSampler, fragTexCoord);
- //outColor = vec4(fragColor ,1);
+ float pulse = sin(ubo.time * .2) * .5 + .5;
+ outColor = vec4(fragColor * texture(texSampler, fragTexCoord).rgb * pulse, 1.0);
+ //outColor = texture(texSampler, fragTexCoord);
+// float repeat = 10;
+// float f = mod(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) {
+// return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
+// }
+
+// float smoothNoise(vec2 p) {
+// vec2 i = floor(p);
+// vec2 f = fract(p);
+// vec2 u = f * f * (3.0 - 2.0 * f);
+// return mix(mix(noise(i + vec2(0.0, 0.0)), noise(i + vec2(1.0, 0.0)), u.x),
+// mix(noise(i + vec2(0.0, 1.0)), noise(i + vec2(1.0, 1.0)), u.x), u.y);
+// }
+
+// float fbm(vec2 p) {
+// float value = 0.0;
+// float amplitude = 0.5;
+// for (int i = 0; i < 6; i++) {
+// value += amplitude * smoothNoise(p);
+// p *= 2.0;
+// amplitude *= 0.5;
+// }
+// return value;
+// }
+
+// void main() {
+// vec2 uv = gl_FragCoord.xy / ubo.resolution.xy;
+// vec2 p = uv * 2.0 - vec2(1.0);
+// float n = fbm(p * 5.0 - vec2(0.0, ubo.time * 2.0));
+// outColor = vec4(n, n * 0.5, 0.0, 1.0) * vec4(texture(texSampler, fragTexCoord).rgb, 1.0);
+// }
+
+// void main() {
+
+// // Calculate a pulsating factor based on sine wave
+// float pulse = sin(ubo.time * 2.0) * 0.5 + 0.5;
+
+// // Set the color using the pulse factor
+// outColor = vec4(pulse, 0.0, 1.0 - pulse, .5);
+// }
+
+// void main() {
+// vec2 uv = gl_FragCoord.xy / ubo.resolution;
+// vec2 center = uv - 0.5;
+
+// float angle = ubo.time;
+// float cosA = cos(angle);
+// float sinA = sin(angle);
+
+// vec2 rotated = vec2(
+// center.x * cosA - center.y * sinA,
+// center.x * sinA + center.y * cosA
+// );
+
+// float pattern = step(0.5, mod(rotated.x * 10.0, 1.0)) * step(0.5, mod(rotated.y * 10.0, 1.0));
+
+// outColor = vec4(vec3(pattern), 1.0);
+// }
diff --git a/src/shader.vert b/src/shader.vert
index dd50683..182bd51 100644
--- a/src/shader.vert
+++ b/src/shader.vert
@@ -5,6 +5,8 @@ layout(binding = 0) uniform UniformBufferObject {
mat4 view;
mat4 proj;
vec2 resolution;
+ float time;
+ float dt;
} ubo;
layout(location = 0) in vec3 inPosition;
@@ -17,7 +19,7 @@ layout(location = 1) out vec2 fragTexCoord;
void main() {
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
//gl_Position = vec4(inPosition * 2, 1.0);
- gl_PointSize = 10.0f;
+ gl_PointSize = 1.5f;
fragColor = inColor;
fragTexCoord = inTexCoord;
diff --git a/src/state.h b/src/state.h
index b56544a..c7b567f 100644
--- a/src/state.h
+++ b/src/state.h
@@ -27,6 +27,8 @@ typedef struct {
mat4 view;
mat4 proj;
vec2 resolution;
+ float time;
+ float dt;
} UniformBufferObject;
typedef struct {
@@ -43,6 +45,23 @@ typedef struct {
VkDescriptorSet vk_descriptor_set;
} frame_data;
+typedef struct {
+ float x;
+ float y;
+} V2;
+
+typedef struct {
+ float x;
+ float y;
+ float z;
+} V3;
+
+typedef struct {
+ V3 pos;
+ V3 color;
+ V2 texCoord;
+} Vertex;
+
typedef struct state {
camera3d camera;
@@ -54,6 +73,7 @@ typedef struct state {
int rotate;
int mouse_pressed;
+ int middle_click;
UniformBufferObject ubo;
@@ -102,6 +122,7 @@ typedef struct state {
shaderc_compilation_result_t prev_vert_result;
shaderc_compilation_result_t prev_frag_result;
+ uint32_t vk_mip_levels;
VkImage vk_texture_image;
VkDeviceMemory vk_texture_image_memory;
VkImageView vk_texture_image_view;
@@ -112,11 +133,22 @@ typedef struct state {
VkImageView vk_depth_image_view;
VkPolygonMode polygon_mode;
-} state_t ;
+
+ Vertex *vertices;
+ size_t vertices_count;
+ uint32_t * indices;
+ size_t indices_count;
+ VkBuffer vertexBuffer;
+ VkDeviceMemory vertexBufferMemory;
+
+ char model_path[1000];
+} state_t;
static void
-init_state(state_t * s)
+init_state(state_t *s)
{
+
+ strcpy(s->model_path, "assets/viking_room.obj");
s->camera.pos[0] = 2.0f;
s->camera.pos[1] = 2.0f;
s->camera.pos[2] = 2.0f;
@@ -137,6 +169,7 @@ init_state(state_t * s)
s->rotate = 0;
s->mouse_pressed = 0;
+ s->middle_click = 0;
s->sdl_window_resized = 0;
@@ -154,6 +187,8 @@ init_state(state_t * s)
s->zoom = 10;
s->x = 0.0f;
+ glm_mat4_identity(s->ubo.model);
+
s->sdl_window = NULL;
s->vk_swap_chain_image_count = 0;
diff --git a/src/vksetup.h b/src/vksetup.h
index 2feb533..88f579a 100644
--- a/src/vksetup.h
+++ b/src/vksetup.h
@@ -15,6 +15,10 @@
- Using shaderc for compiling glsl
- Using vulkan(!)
+ This is not meant to be a generic werapper around vulkan. It is actively
+ paired and chaned by the currently in development application, whatever that
+ might be. Think of it as the vulkan setup configuratior and helper.
+
USAGE:
~~~~~~
Do this:
@@ -32,60 +36,141 @@
#define SDL_MAIN_HANDLED
#define VK_USE_PLATFORM_XCB_KHR
+#include <stdarg.h>
+
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
#include <vulkan/vulkan.h>
+#include <vulkan/vk_enum_string_helper.h> // for string_VkResult
#include <shaderc/shaderc.h>
#include "../lib/cglm/include/cglm/cglm.h"
+#define VK_ARRAY_LEN(arr) sizeof((arr))/sizeof((arr)[0])
+
+#ifdef __clang__
+#define __FILENAME__ __FILE_NAME__
+#else
+#define __FILENAME__ __FILE__
+#endif
+
+#define VK_CHECK(x) \
+do { \
+ VkResult err = x; \
+ if (err < 0) { \
+ fprintf(stderr, "src/%s:%d:0: vulkan error: %s \n", \
+ __FILENAME__, __LINE__, string_VkResult(err)); \
+ abort(); \
+ } \
+} while (0)
+
+typedef enum {
+ VK_INFO = 0,
+ VK_WARN,
+ VK_ERROR,
+} log_type;
+
+static inline void
+_vk_log(log_type t, const char * f, ...)
+{
+#ifdef VKDEBUG
+ va_list args;
+ va_start(args, f);
+ switch (t) {
+ case VK_INFO:
+ printf("INFO: ");
+ vprintf(f, args);
+ break;
+ case VK_WARN:
+ fprintf(stderr, "WARN: ");
+ vfprintf(stderr, f, args);
+ break;
+ case VK_ERROR:
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, f, args);
+ break;
+ }
+ va_end(args);
+#else
+ return;
+#endif
+}
+
+#ifdef VKDEBUG
+#define vk_log(t, ...) do { \
+ fprintf(t == VK_INFO ? stdout : stderr, "src/%s:%d:0: ", \
+ __FILENAME__, __LINE__); \
+ _vk_log(t, __VA_ARGS__); \
+} while(0)
+#else
+#define vk_log(t, ...)
+#endif
+
+/**********/
+/* config */
+/**********/
+
+const char *const validation_layers[] = {
+ "VK_LAYER_KHRONOS_validation"
+};
+const uint32_t validation_layer_count = VK_ARRAY_LEN(validation_layers);
+
+const char *const device_extensions[] = {
+ VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+};
+const uint32_t deviceExtensionCount = VK_ARRAY_LEN(device_extensions);
+
+#ifdef VKDEBUG
+ const bool enableValidationLayers = true;
+#else
+ const bool enableValidationLayers = false;
+#endif
+
+
#ifdef __cplusplus
extern "C" {
#endif
-#ifndef VKSETUPDEF
-#ifdef VKSETUP_STATIC
-#define VKSETUPDEF static
+#ifndef VKSDEF
+#ifdef VKS_STATIC
+#define VKSDEF static
#else
-#define VKSETUPDEF extern
+#define VKSDEF extern
#endif
#endif
// TODO Create structs for vulkan data
-typedef struct {void * a;} vksetup_vulkan;
-typedef struct {void * a;} vksetup_image;
-typedef struct {void * a;} vksetup_;
-
-// need abstraction with the
-
+typedef struct {void * a;} vks_vulkan;
+typedef struct {void * a;} vks_image;
+typedef struct {void * a;} vks_;
/**
Create a VkInstance
*/
-VKSETUPDEF VkInstance vksetup_create_instance(bool validation_layers_toggle, const char * const validation_layers[], uint32_t validation_layer_count, SDL_Window *window);
-/* VKSETUPDEF void vulkan_create_surface(); */
-/* VKSETUPDEF void vulkan_pick_physical_device(); */
-/* VKSETUPDEF void vulkan_create_logical_device(); */
-/* VKSETUPDEF void vulkan_create_swap_chain(); */
-/* VKSETUPDEF void vulkan_create_image_views(); */
-/* VKSETUPDEF void vulkan_create_descriptor_set_layout(); */
-/* VKSETUPDEF void vulkan_create_graphics_pipeline(); */
-/* VKSETUPDEF void vulkan_create_command_pool(); */
-/* VKSETUPDEF void vulkan_create_depth_resources(); */
-/* VKSETUPDEF void vulkan_create_texture_image(); */
-/* VKSETUPDEF void vulkan_create_texture_image_view(); */
-/* VKSETUPDEF void vulkan_create_texture_sampler(); */
-/* VKSETUPDEF void vulkan_create_vertex_buffer(); */
-/* VKSETUPDEF void vulkan_create_index_buffer(); */
-/* VKSETUPDEF void vulkan_create_uniform_buffers(); */
-/* VKSETUPDEF void vulkan_create_descriptor_pool(); */
-/* VKSETUPDEF void vulkan_create_descriptor_sets(); */
-/* VKSETUPDEF void vulkan_create_command_buffer(); */
-/* VKSETUPDEF void vulkan_create_sync_objects(); */
+VKSDEF VkInstance vks_create_instance(bool validation_layers_toggle, const char * const validation_layers[], uint32_t validation_layer_count, SDL_Window *window);
+/* VKSDEF void vulkan_create_surface(); */
+/* VKSDEF void vulkan_pick_physical_device(); */
+/* VKSDEF void vulkan_create_logical_device(); */
+/* VKSDEF void vulkan_create_swap_chain(); */
+/* VKSDEF void vulkan_create_image_views(); */
+/* VKSDEF void vulkan_create_descriptor_set_layout(); */
+/* VKSDEF void vulkan_create_graphics_pipeline(); */
+/* VKSDEF void vulkan_create_command_pool(); */
+/* VKSDEF void vulkan_create_depth_resources(); */
+/* VKSDEF void vulkan_create_texture_image(); */
+/* VKSDEF void vulkan_create_texture_image_view(); */
+/* VKSDEF void vulkan_create_texture_sampler(); */
+/* VKSDEF void vulkan_create_vertex_buffer(); */
+/* VKSDEF void vulkan_create_index_buffer(); */
+/* VKSDEF void vulkan_create_uniform_buffers(); */
+/* VKSDEF void vulkan_create_descriptor_pool(); */
+/* VKSDEF void vulkan_create_descriptor_sets(); */
+/* VKSDEF void vulkan_create_command_buffer(); */
+/* VKSDEF void vulkan_create_sync_objects(); */
#ifdef __cplusplus
}
@@ -99,10 +184,11 @@ VKSETUPDEF VkInstance vksetup_create_instance(bool validation_layers_toggle, con
#include <stddef.h>
#include <stdlib.h>
#include <time.h>
-#include "vkutil.h"
+
+/* Vks helpers */
bool
-_checkValidationLayerSupport(const char * const validation_layers[], uint32_t validation_layer_count)
+vks_check_validation_layer_support(const char * const validation_layers[], uint32_t validation_layer_count)
{
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, NULL);
@@ -110,29 +196,28 @@ _checkValidationLayerSupport(const char * const validation_layers[], uint32_t va
VkLayerProperties availableLayers[layerCount];
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers);
+ bool layerFound = false;
for (uint32_t i = 0; i < validation_layer_count; i++) {
- bool layerFound = false;
-
for (uint32_t j = 0; j < layerCount; j++) {
if (strcmp(validation_layers[i], availableLayers[j].layerName) == 0) {
layerFound = true;
break;
}
}
-
- if (!layerFound) {
- return false;
- }
}
- return true;
+ return layerFound;
}
-VKSETUPDEF VkInstance
-vksetup_create_instance(bool validation_layers_toggle, const char * const validation_layers[], uint32_t validation_layer_count, SDL_Window *window)
+/* Vks API implementation */
+
+VKSDEF VkInstance
+vks_create_instance(bool validation_layers_toggle, const char * const validation_layers[],
+ uint32_t validation_layer_count, SDL_Window *window)
{
- if (validation_layers_toggle && !_checkValidationLayerSupport(validation_layers, validation_layer_count)) {
+ if (validation_layers_toggle && !vks_check_validation_layer_support(validation_layers, validation_layer_count)) {
vk_log(VK_ERROR, "validation layers requested, but not available!\n");
+ abort();
}
uint32_t instanceVersion;
@@ -166,6 +251,7 @@ vksetup_create_instance(bool validation_layers_toggle, const char * const valida
if (SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, NULL) == SDL_FALSE) {
vk_log(VK_ERROR, "SDL_Vulkan_GetInstanceExtensions failed: %s\n", SDL_GetError());
+ abort();
}
// make space for debug extenetion
@@ -177,6 +263,7 @@ vksetup_create_instance(bool validation_layers_toggle, const char * const valida
if (SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, sdlExtensions) == SDL_FALSE) {
vk_log(VK_ERROR, "SDL_Vulkan_GetInstanceExtensions failed: %s\n", SDL_GetError());
+ abort();
}
// add debug extenetion
@@ -213,4 +300,3 @@ vksetup_create_instance(bool validation_layers_toggle, const char * const valida
}
#endif /* VKSETUP_IMPLEMENTATION */
-