#pragma once #pragma clang diagnostic ignored "-Wincompatible-pointer-types" #include #include #include #include #include "iron_system.h" #include "iron_thread.h" #include "iron_gpu.h" #include "iron_file.h" #include "iron_string.h" #include "iron_array.h" #include "iron_map.h" #include "iron_armpack.h" #include "iron_json.h" #include "iron_gc.h" #include "iron_obj.h" #include "iron_vec2.h" #include "iron_vec3.h" #include "iron_vec4.h" #include "iron_quat.h" #include "iron_mat3.h" #include "iron_mat4.h" #include "iron_ui.h" #include "iron_ui_nodes.h" #include "iron_draw.h" #ifdef IRON_WINDOWS #include #endif #ifdef WITH_AUDIO #include "iron_audio.h" #endif #ifdef WITH_EMBED #include EMBED_H_PATH #endif int _argc; char **_argv; #ifdef WITH_EVAL #include "quickjs.h" #include "quickjs-libc.h" JSRuntime *js_runtime = NULL; JSContext *js_ctx; #ifdef WITH_PLUGINS void plugin_api_init(); #endif void js_init() { js_runtime = JS_NewRuntime(); js_ctx = JS_NewContext(js_runtime); js_std_add_helpers(js_ctx, _argc, _argv); js_init_module_std(js_ctx, "std"); js_init_module_os(js_ctx, "os"); #ifdef WITH_PLUGINS plugin_api_init(); #endif } // float alang_eval(char *data); float js_eval(const char *js) { // return alang_eval(js); if (js_runtime == NULL) { js_init(); } JSValue ret = JS_Eval(js_ctx, js, strlen(js), "iron", JS_EVAL_TYPE_GLOBAL); if (JS_IsException(ret)) { js_std_dump_error(js_ctx); JS_ResetUncatchableError(js_ctx); } double d; JS_ToFloat64(js_ctx, &d, ret); JS_RunGC(js_runtime); if (d != d) { // nan d = 0.0; } return d; } JSValue js_call_arg(void *p, int argc, JSValue *argv) { if (js_runtime == NULL) { js_init(); } JSValue fn = *(JSValue *)p; JSValue global_obj = JS_GetGlobalObject(js_ctx); JSValue js_call_result = JS_Call(js_ctx, fn, global_obj, argc, argv); if (JS_IsException(js_call_result)) { js_std_dump_error(js_ctx); JS_ResetUncatchableError(js_ctx); } JS_FreeValue(js_ctx, global_obj); return js_call_result; } char *js_call_ptr(void *p, void *arg) { JSValue argv[] = { JS_NewBigUint64(js_ctx, (uint64_t)arg) }; return (char *)JS_ToCString(js_ctx, js_call_arg(p, 1, argv)); } char *js_call_ptr_str(void *p, void *arg0, char *arg1) { JSValue argv[] = { JS_NewBigUint64(js_ctx, (uint64_t)arg0), JS_NewString(js_ctx, arg1) }; return (char *)JS_ToCString(js_ctx, js_call_arg(p, 2, argv)); } void *js_pcall_str(void *p, char *arg0) { JSValue argv[] = { JS_NewString(js_ctx, arg0) }; uint64_t result; JS_ToBigUint64(js_ctx, &result, js_call_arg(p, 1, argv)); return (void *)result; } char *js_call(void *p) { return (char *)JS_ToCString(js_ctx, js_call_arg(p, 0, NULL)); } #else void js_init() {} float js_eval(const char *js) { return 0.0; } void js_call_arg(void *p, int argc, void *argv) { } char *js_call_ptr(void *p, void *arg) { return NULL; } char *js_call_ptr_str(void *p, void *arg0, char *arg1) { return NULL; } void *js_pcall_str(void *p, char *arg0) { return NULL; } char *js_call(void *p) { return NULL; } #endif #ifdef WITH_EMBED buffer_t *embed_get(char *key) { #ifdef IRON_WINDOWS key = string_replace_all(key, "\\", "/"); #endif for (int i = 0; i < embed_count; ++i) { if (strcmp(embed_keys[i], key) == 0) { buffer_t *buffer = buffer_create(0); buffer->buffer = embed_values[i]; buffer->length = embed_sizes[i]; return buffer; } } return NULL; } #endif #define f64 double #define i64 int64_t #define u64 uint64_t #define f32 float #define i32 int32_t #define u32 uint32_t #define i16 int16_t #define u16 uint16_t #define i8 int8_t #define u8 uint8_t #define string_t char #define any void * #define any_ptr void ** #define f64_ptr f64 * #define i64_ptr i64 * #define u64_ptr u64 * #define f32_ptr f32 * #define i32_ptr i32 * #define u32_ptr u32 * #define i16_ptr i16 * #define u16_ptr u16 * #define i8_ptr i8 * #define u8_ptr u8 * #define null NULL #define DEREFERENCE * #define ADDRESS & #define ARRAY_ACCESS(a, i) a[i] f32 f32_nan() { return NAN; } bool f32_isnan(f32 f) { return isnan(f); } void _kickstart(); bool enable_window = true; bool in_background = false; int paused_frames = 0; bool save_and_quit_callback_set = false; #ifdef IDLE_SLEEP bool input_down = false; int last_window_width = 0; int last_window_height = 0; #endif char temp_string[1024 * 128]; char temp_string_vs[1024 * 128]; char temp_string_fs[1024 * 128]; #ifdef IRON_WINDOWS wchar_t temp_wstring[1024 * 32]; struct HWND__ *iron_windows_window_handle(); #endif void (*iron_update)(void); void (*iron_drop_files)(char *); void (*iron_foreground)(void); void (*iron_resume)(void); void (*iron_pause)(void); void (*iron_background)(void); void (*iron_shutdown)(void); void (*iron_pause)(void); void (*iron_key_down)(int); void (*iron_key_up)(int); void (*iron_key_press)(int); void (*iron_mouse_down)(int, int, int); void (*iron_mouse_up)(int, int, int); void (*iron_mouse_move)(int, int, int, int); void (*iron_mouse_wheel)(int); void (*iron_touch_down)(int, int, int); void (*iron_touch_up)(int, int, int); void (*iron_touch_move)(int, int, int); void (*iron_pen_down)(int, int, float); void (*iron_pen_up)(int, int, float); void (*iron_pen_move)(int, int, float); #ifdef WITH_GAMEPAD void (*iron_gamepad_axis)(int, int, float); void (*iron_gamepad_button)(int, int, float); #endif void (*iron_save_and_quit)(bool); char *_substring(char *s, int32_t start, int32_t end) { char *buffer = calloc(1, end - start + 1); for (int i = 0; i < end - start; ++i) { buffer[i] = s[start + i]; } return buffer; } int kickstart(int argc, char **argv) { _argc = argc; _argv = argv; #ifdef IRON_ANDROID char *bindir = "/"; #elif defined(IRON_IOS) char *bindir = ""; #else char *bindir = argv[0]; #endif #ifdef IRON_WINDOWS // Handle non-ascii path HMODULE hmodule = GetModuleHandleW(NULL); GetModuleFileNameW(hmodule, temp_wstring, 1024); WideCharToMultiByte(CP_UTF8, 0, temp_wstring, -1, temp_string, 4096, NULL, NULL); bindir = _substring(temp_string, 0, string_last_index_of(temp_string, "\\")); #else bindir = _substring(bindir, 0, string_last_index_of(bindir, "/")); #endif char *assetsdir = argc > 1 ? argv[1] : bindir; // Opening a file int l = strlen(assetsdir); if ((l > 6 && assetsdir[l - 6] == '.') || (l > 5 && assetsdir[l - 5] == '.') || (l > 4 && assetsdir[l - 4] == '.')) { assetsdir = bindir; } for (int i = 2; i < argc; ++i) { if (strcmp(argv[i], "--nowindow") == 0) { enable_window = false; } } #if !defined(IRON_MACOS) && !defined(IRON_IOS) iron_internal_set_files_location(assetsdir); #endif iron_threads_init(); iron_display_init(); gc_start(&argc); _kickstart(); #ifdef WITH_AUDIO iron_a2_shutdown(); #endif gc_stop(); return 0; } i32 iron_get_arg_count() { return _argc; } string_t *iron_get_arg(i32 index) { return _argv[index]; } // ██╗██████╗ ██████╗ ███╗ ██╗ █████╗ ██████╗ ██╗ // ██║██╔══██╗██╔═══██╗████╗ ██║ ██╔══██╗██╔══██╗██║ // ██║██████╔╝██║ ██║██╔██╗ ██║ ███████║██████╔╝██║ // ██║██╔══██╗██║ ██║██║╚██╗██║ ██╔══██║██╔═══╝ ██║ // ██║██║ ██║╚██████╔╝██║ ╚████║ ██║ ██║██║ ██║ // ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝ #ifndef NO_IRON_API #include #include "iron_math.h" #include "iron_net.h" #include #include "kong/dir.h" #ifdef WITH_AUDIO #include "iron_audio.h" #endif #define STB_IMAGE_IMPLEMENTATION #include #ifdef IRON_DIRECT3D12 #include #endif #ifdef WITH_D3DCOMPILER #include #include #endif #ifdef WITH_NFD #include #elif defined(IRON_ANDROID) #include "backends/android_file_dialog.h" #include "backends/android_http_request.h" #elif defined(IRON_IOS) #include #include "backends/ios_file_dialog.h" #endif #ifdef WITH_IMAGE_WRITE #ifdef WITH_COMPRESS unsigned char *iron_deflate_raw(unsigned char *data, int data_len, int *out_len, int quality); #define STBIW_ZLIB_COMPRESS iron_deflate_raw #endif #define STBI_WINDOWS_UTF8 #define STB_IMAGE_WRITE_IMPLEMENTATION #include #endif #ifdef WITH_VIDEO_WRITE #include #include #endif #ifdef WITH_COMPRESS #define SDEFL_IMPLEMENTATION #include "sdefl.h" #define SINFL_IMPLEMENTATION #include "sinfl.h" #endif #if defined(IDLE_SLEEP) && !defined(IRON_WINDOWS) // #include int usleep(unsigned int usec); #endif #ifdef IRON_MACOS const char *iron_get_resource_path(); #endif #ifdef IRON_IOS const char *iron_get_resource_path(); #endif #if defined(IRON_IOS) || defined(IRON_ANDROID) char mobile_title[1024]; #endif static gpu_buffer_t rt_constant_buffer; static gpu_raytrace_pipeline_t rt_pipeline; static gpu_raytrace_acceleration_structure_t rt_accel; static bool rt_created = false; static bool rt_accel_created = false; const int rt_constant_buffer_size = 24; void _update(void *data) { #ifdef IRON_WINDOWS if (in_background && ++paused_frames > 3) { Sleep(1); return; } #endif #ifdef IDLE_SLEEP if (last_window_width != iron_window_width() || last_window_height != iron_window_height()) { last_window_width = iron_window_width(); last_window_height = iron_window_height(); paused_frames = 0; } #if defined(IRON_IOS) || defined(IRON_ANDROID) const int start_sleep = 1200; #else const int start_sleep = 120; #endif if (++paused_frames > start_sleep && !input_down) { #ifdef IRON_WINDOWS Sleep(1); #else usleep(1000); #endif return; } if (paused_frames == 30) { gc_run(); } #endif #ifdef WITH_AUDIO iron_a2_update(); #endif iron_update(); if (ui_get_current()) ui_end_frame(); gpu_present(); } char *_copy(void *data) { strcpy(temp_string, ui_copy()); return temp_string; } char *_cut(void *data) { strcpy(temp_string, ui_cut()); return temp_string; } void _paste(char *text, void *data) { ui_paste(text); } void _foreground(void *data) { iron_foreground(); in_background = false; } void _resume(void *data) { iron_resume(); } void _pause(void *data) { iron_pause(); } void _background(void *data) { iron_background(); in_background = true; paused_frames = 0; } void _shutdown(void *data) { iron_shutdown(); } void _key_down(int code, void *data) { iron_key_down(code); if (ui_get_current()) ui_key_down(ui_get_current(), code); #ifdef IDLE_SLEEP input_down = true; paused_frames = 0; #endif } void _key_up(int code, void *data) { iron_key_up(code); if (ui_get_current()) ui_key_up(ui_get_current(), code); #ifdef IDLE_SLEEP input_down = false; paused_frames = 0; #endif } void _key_press(unsigned int character, void *data) { iron_key_press(character); if (ui_get_current()) ui_key_press(ui_get_current(), character); #ifdef IDLE_SLEEP paused_frames = 0; #endif } void _mouse_down(int button, int x, int y, void *data) { iron_mouse_down(button, x, y); if (ui_get_current()) ui_mouse_down(ui_get_current(), button, x, y); #ifdef IDLE_SLEEP input_down = true; paused_frames = 0; #endif } void _mouse_up(int button, int x, int y, void *data) { iron_mouse_up(button, x, y); if (ui_get_current()) ui_mouse_up(ui_get_current(), button, x, y); #ifdef IDLE_SLEEP input_down = false; paused_frames = 0; #endif } void _mouse_move(int x, int y, int mx, int my, void *data) { iron_mouse_move(x, y, mx, my); if (ui_get_current()) ui_mouse_move(ui_get_current(), x, y, mx, my); #ifdef IDLE_SLEEP paused_frames = 0; #endif } void _mouse_wheel(int delta, void *data) { iron_mouse_wheel(delta); if (ui_get_current()) ui_mouse_wheel(ui_get_current(), delta); #ifdef IDLE_SLEEP paused_frames = 0; #endif } void _touch_move(int index, int x, int y) { iron_touch_move(index, x, y); #if defined(IRON_ANDROID) || defined(IRON_IOS) if (ui_get_current()) ui_touch_move(ui_get_current(), index, x, y); #endif #ifdef IDLE_SLEEP paused_frames = 0; #endif } void _touch_down(int index, int x, int y) { iron_touch_down(index, x, y); #if defined(IRON_ANDROID) || defined(IRON_IOS) if (ui_get_current()) ui_touch_down(ui_get_current(), index, x, y); #endif #ifdef IDLE_SLEEP input_down = true; paused_frames = 0; #endif } void _touch_up(int index, int x, int y) { iron_touch_up(index, x, y); #if defined(IRON_ANDROID) || defined(IRON_IOS) if (ui_get_current()) ui_touch_up(ui_get_current(), index, x, y); #endif #ifdef IDLE_SLEEP input_down = false; paused_frames = 0; #endif } void _pen_down(int x, int y, float pressure) { iron_pen_down(x, y, pressure); if (ui_get_current()) ui_pen_down(ui_get_current(), x, y, pressure); #ifdef IDLE_SLEEP input_down = true; paused_frames = 0; #endif } void _pen_up(int x, int y, float pressure) { iron_pen_up(x, y, pressure); if (ui_get_current()) ui_pen_up(ui_get_current(), x, y, pressure); #ifdef IDLE_SLEEP input_down = false; paused_frames = 0; #endif } void _pen_move(int x, int y, float pressure) { iron_pen_move(x, y, pressure); if (ui_get_current()) ui_pen_move(ui_get_current(), x, y, pressure); #ifdef IDLE_SLEEP paused_frames = 0; #endif } #ifdef WITH_GAMEPAD void _gamepad_axis(int gamepad, int axis, float value, void *data) { iron_gamepad_axis(gamepad, axis, value); #ifdef IDLE_SLEEP paused_frames = 0; #endif } void _gamepad_button(int gamepad, int button, float value, void *data) { iron_gamepad_button(gamepad, button, value); #ifdef IDLE_SLEEP paused_frames = 0; #endif } #endif void _drop_files(wchar_t *file_path, void *data) { // Update mouse position #ifdef IRON_WINDOWS POINT p; GetCursorPos(&p); ScreenToClient(iron_windows_window_handle(), &p); _mouse_move(p.x, p.y, 0, 0, NULL); #endif char buffer[1024]; #ifdef IRON_WINDOWS WideCharToMultiByte(CP_UTF8, 0, file_path, wcslen(file_path) + 1, buffer, sizeof(buffer), NULL, NULL); #else wcstombs(buffer, file_path, sizeof(buffer)); #endif iron_drop_files(buffer); in_background = false; #ifdef IDLE_SLEEP paused_frames = 0; #endif } char *uri_decode(const char *src) { char *res = gc_alloc(1024); char *dst = res; char a, b; while (*src) { if ((*src == '%') && ((a = src[1]) && (b = src[2])) && (isxdigit(a) && isxdigit(b))) { if (a >= 'a') { a -= 'a' - 'A'; } if (a >= 'A') { a -= ('A' - 10); } else { a -= '0'; } if (b >= 'a') { b -= 'a' - 'A'; } if (b >= 'A') { b -= ('A' - 10); } else { b -= '0'; } *dst++ = 16 * a + b; src += 3; } else if (*src == '+') { *dst++ = ' '; src++; } else { *dst++ = *src++; } } *dst++ = '\0'; return res; } f32 math_floor(f32 x) { return floorf(x); } f32 math_cos(f32 x) { return cosf(x); } f32 math_sin(f32 x) { return sinf(x); } f32 math_tan(f32 x) { return tanf(x); } f32 math_sqrt(f32 x) { return sqrtf(x); } f32 math_abs(f32 x) { return fabsf(x); } f32 math_random() { return rand() / (float)RAND_MAX; } f32 math_atan2(f32 y, f32 x) { return atan2f(y, x); } f32 math_asin(f32 x) { return asinf(x); } f32 math_pi() { return 3.14159265358979323846; } f32 math_pow(f32 x, f32 y) { return powf(x, y); } f32 math_round(f32 x) { return roundf(x); } f32 math_ceil(f32 x) { return ceilf(x); } f32 math_min(f32 x, f32 y) { return x < y ? x : y; } f32 math_max(f32 x, f32 y) { return x > y ? x : y; } f32 math_log(f32 x) { return logf(x); } f32 math_log2(f32 x) { return log2f(x); } f32 math_atan(f32 x) { return atanf(x); } f32 math_acos(f32 x) { return acosf(x); } f32 math_exp(f32 x) { return expf(x); } f32 math_fmod(f32 x, f32 y) { return fmod(x, y); } #ifdef _WIN32 i32 parse_int(const char *s) { return _strtoi64(s, NULL, 10); } i32 parse_int_hex(const char *s) { return _strtoi64(s, NULL, 16); } #else i32 parse_int(const char *s) { return strtol(s, NULL, 10); } i32 parse_int_hex(const char *s) { return strtol(s, NULL, 16); } #endif f32 parse_float(const char *s) { return strtof(s, NULL); } i32 color_from_floats(f32 r, f32 g, f32 b, f32 a) { return ((int)(a * 255) << 24) | ((int)(r * 255) << 16) | ((int)(g * 255) << 8) | (int)(b * 255); } u8 color_get_rb(i32 c) { return (c & 0x00ff0000) >> 16; } u8 color_get_gb(i32 c) { return (c & 0x0000ff00) >> 8; } u8 color_get_bb(i32 c) { return c & 0x000000ff; } u8 color_get_ab(i32 c) { return c & 0x000000ff; } i32 color_set_rb(i32 c, u8 i) { return (color_get_ab(c) << 24) | (i << 16) | (color_get_gb(c) << 8) | color_get_bb(c); } i32 color_set_gb(i32 c, u8 i) { return (color_get_ab(c) << 24) | (color_get_rb(c) << 16) | (i << 8) | color_get_bb(c); } i32 color_set_bb(i32 c, u8 i) { return (color_get_ab(c) << 24) | (color_get_rb(c) << 16) | (color_get_gb(c) << 8) | i; } i32 color_set_ab(i32 c, u8 i) { return (i << 24) | (color_get_rb(c) << 16) | (color_get_gb(c) << 8) | color_get_bb(c); } void _iron_init(iron_window_options_t *ops) { ops->display_index = -1; ops->visible = enable_window; ops->color_bits = 32; iron_init(ops); iron_random_init((int)(iron_time() * 1000)); iron_set_cut_callback(_cut, NULL); iron_set_copy_callback(_copy, NULL); iron_set_paste_callback(_paste, NULL); #ifdef IRON_WINDOWS // Maximized window has x < -1, prevent window centering if (ops->x < -1 && ops->y < -1) { iron_window_move(ops->x, ops->y); } #endif #ifdef WITH_AUDIO iron_a1_init(); iron_a2_init(); #endif #ifdef IRON_ANDROID android_check_permissions(); #endif } void _iron_set_update_callback(void (*callback)(void)) { iron_update = callback; iron_set_update_callback(_update, NULL); } void _iron_set_drop_files_callback(void (*callback)(char *)) { iron_drop_files = callback; iron_set_drop_files_callback(_drop_files, NULL); } void iron_set_application_state_callback(void (*on_foreground)(void), void (*on_resume)(void), void (*on_pause)(void), void (*on_background)(void), void (*on_shutdown)(void)) { iron_set_foreground_callback(on_foreground != NULL ? _foreground : NULL, NULL); iron_set_resume_callback(on_resume != NULL ? _resume : NULL, NULL); iron_set_pause_callback(on_pause != NULL ? _pause : NULL, NULL); iron_set_background_callback(on_background != NULL ? _background : NULL, NULL); iron_set_shutdown_callback(on_shutdown != NULL ? _shutdown : NULL, NULL); iron_foreground = on_foreground; iron_resume = on_resume; iron_pause = on_pause; iron_background = on_background; iron_shutdown = on_shutdown; } void iron_set_keyboard_down_callback(void (*callback)(int)) { iron_key_down = callback; iron_keyboard_set_key_down_callback(_key_down, NULL); } void iron_set_keyboard_up_callback(void (*callback)(int)) { iron_key_up = callback; iron_keyboard_set_key_up_callback(_key_up, NULL); } void iron_set_keyboard_press_callback(void (*callback)(int)) { iron_key_press = callback; iron_keyboard_set_key_press_callback(_key_press, NULL); } void iron_set_mouse_down_callback(void (*callback)(int, int, int)) { iron_mouse_down = callback; iron_mouse_set_press_callback(_mouse_down, NULL); } void iron_set_mouse_up_callback(void (*callback)(int, int, int)) { iron_mouse_up = callback; iron_mouse_set_release_callback(_mouse_up, NULL); } void iron_set_mouse_move_callback(void (*callback)(int, int, int, int)) { iron_mouse_move = callback; iron_mouse_set_move_callback(_mouse_move, NULL); } void iron_set_mouse_wheel_callback(void (*callback)(int)) { iron_mouse_wheel = callback; iron_mouse_set_scroll_callback(_mouse_wheel, NULL); } void iron_set_touch_down_callback(void (*callback)(int, int, int)) { iron_touch_down = callback; iron_surface_set_touch_start_callback(_touch_down); } void iron_set_touch_up_callback(void (*callback)(int, int, int)) { iron_touch_up = callback; iron_surface_set_touch_end_callback(_touch_up); } void iron_set_touch_move_callback(void (*callback)(int, int, int)) { iron_touch_move = callback; iron_surface_set_move_callback(_touch_move); } void iron_set_pen_down_callback(void (*callback)(int, int, float)) { iron_pen_down = callback; iron_pen_set_press_callback(_pen_down); } void iron_set_pen_up_callback(void (*callback)(int, int, float)) { iron_pen_up = callback; iron_pen_set_release_callback(_pen_up); } void iron_set_pen_move_callback(void (*callback)(int, int, float)) { iron_pen_move = callback; iron_pen_set_move_callback(_pen_move); } #ifdef WITH_GAMEPAD void iron_set_gamepad_axis_callback(void (*callback)(int, int, float)) { iron_gamepad_axis = callback; iron_gamepad_set_axis_callback(_gamepad_axis, NULL); } void iron_set_gamepad_button_callback(void (*callback)(int, int, float)) { iron_gamepad_button = callback; iron_gamepad_set_button_callback(_gamepad_button, NULL); } #endif void gpu_delete_buffer(gpu_buffer_t *buffer) { gpu_buffer_destroy(buffer); free(buffer); } any gpu_create_index_buffer(i32 count) { gpu_buffer_t *buffer = (gpu_buffer_t *)malloc(sizeof(gpu_buffer_t)); gpu_index_buffer_init(buffer, count); return buffer; } u32_array_t *gpu_lock_index_buffer(gpu_buffer_t *buffer) { u32_array_t *ar = (u32_array_t *)malloc(sizeof(u32_array_t)); ar->buffer = gpu_index_buffer_lock(buffer); ar->length = buffer->count; return ar; } any gpu_create_vertex_buffer(i32 count, gpu_vertex_structure_t *structure) { gpu_buffer_t *buffer = (gpu_buffer_t *)malloc(sizeof(gpu_buffer_t)); gpu_vertex_buffer_init(buffer, count, structure); return buffer; } buffer_t *gpu_lock_vertex_buffer(gpu_buffer_t *buffer) { buffer_t *b = (buffer_t *)malloc(sizeof(buffer_t)); b->buffer = gpu_vertex_buffer_lock(buffer); b->length = buffer->count * buffer->stride; return b; } gpu_shader_t *gpu_create_shader(buffer_t *data, i32 shader_type) { gpu_shader_t *shader = (gpu_shader_t *)malloc(sizeof(gpu_shader_t)); gpu_shader_init(shader, data->buffer, data->length, (gpu_shader_type_t)shader_type); return shader; } #ifdef WITH_KONG #include "../../sources/libs/kong/analyzer.h" #include "../../sources/libs/kong/compiler.h" #include "../../sources/libs/kong/disasm.h" #include "../../sources/libs/kong/errors.h" #include "../../sources/libs/kong/functions.h" #include "../../sources/libs/kong/globals.h" #include "../../sources/libs/kong/log.h" #include "../../sources/libs/kong/names.h" #include "../../sources/libs/kong/parser.h" #include "../../sources/libs/kong/tokenizer.h" #include "../../sources/libs/kong/typer.h" #include "../../sources/libs/kong/types.h" #include "../../sources/libs/kong/transformer.h" #include "../../sources/libs/kong/backends/hlsl.h" #include "../../sources/libs/kong/backends/metal.h" #include "../../sources/libs/kong/backends/spirv.h" #include "../../sources/libs/kong/backends/wgsl.h" #include "../../sources/libs/kong/libs/stb_ds.h" extern uint64_t next_variable_id; extern size_t allocated_globals_size; extern function_id next_function_index; extern global_id globals_size; extern name_id names_index; extern size_t sets_count; extern type_id next_type_index; extern size_t vertex_inputs_size; extern size_t fragment_inputs_size; extern size_t vertex_functions_size; extern size_t fragment_functions_size; extern struct { char *key; name_id value; } *hash; extern int expression_index; extern int statement_index; extern bool kong_error; uint64_t _next_variable_id; size_t _allocated_globals_size; function_id _next_function_index; global_id _globals_size; name_id _names_index; size_t _sets_count; type_id _next_type_index; size_t _vertex_inputs_size; size_t _fragment_inputs_size; size_t _vertex_functions_size; size_t _fragment_functions_size; struct { char *key; name_id value; } *_hash; int _expression_index; int _statement_index; void hlsl_export2(char **vs, char **fs, api_kind d3d, bool debug); void spirv_export2(char **vs, char **fs, int *vs_size, int *fs_size, bool debug); void console_info(char *s); static struct { char *key; name_id value; } *_clone_hash(struct { char *key; name_id value; } *hash) { struct { char *key; name_id value; } *clone = NULL; sh_new_arena(clone); ptrdiff_t len = shlen(hash); for (ptrdiff_t i = 0; i < len; i++) { shput(clone, hash[i].key, hash[i].value); } return clone; } void gpu_create_shaders_from_kong(char *kong, char **vs, char **fs, int *vs_size, int *fs_size) { static bool first = true; if (first) { first = false; names_init(); types_init(); functions_init(); globals_init(); _next_variable_id = next_variable_id; _allocated_globals_size = allocated_globals_size; _next_function_index = next_function_index; _globals_size = globals_size; _names_index = names_index; _sets_count = sets_count; _next_type_index = next_type_index; _vertex_inputs_size = vertex_inputs_size; _fragment_inputs_size = fragment_inputs_size; _vertex_functions_size = vertex_functions_size; _fragment_functions_size = fragment_functions_size; _hash = _clone_hash(hash); _expression_index = expression_index; _statement_index = statement_index; } else { next_variable_id = _next_variable_id; allocated_globals_size = _allocated_globals_size; next_function_index = _next_function_index; globals_size = _globals_size; names_index = _names_index; sets_count = _sets_count; next_type_index = _next_type_index; vertex_inputs_size = _vertex_inputs_size; fragment_inputs_size = _fragment_inputs_size; vertex_functions_size = _vertex_functions_size; fragment_functions_size = _fragment_functions_size; shfree(hash); hash = _clone_hash(_hash); expression_index = _expression_index; statement_index = _statement_index; } kong_error = false; char *from = ""; tokens tokens = tokenize(from, kong); parse(from, &tokens); resolve_types(); if (kong_error) { console_info("Warning: Shader compilation failed"); free(tokens.t); #if defined(__APPLE__) *vs = ""; *fs = ""; #endif return; } allocate_globals(); for (function_id i = 0; get_function(i) != NULL; ++i) { compile_function_block(&get_function(i)->code, get_function(i)->block); } analyze(); #ifdef _WIN32 hlsl_export2(vs, fs, API_DIRECT3D11, false); #elif defined(__APPLE__) static char vs_temp[1024 * 128]; strcpy(vs_temp, "//>kong_vert\n"); char *metal = metal_export(""); strcat(vs_temp, metal); *vs = &vs_temp[0]; *fs = "//>kong_frag\n"; free(metal); #else transform(TRANSFORM_FLAG_ONE_COMPONENT_SWIZZLE | TRANSFORM_FLAG_BINARY_UNIFY_LENGTH); spirv_export2(vs, fs, vs_size, fs_size, false); #endif free(tokens.t); } #endif gpu_shader_t *gpu_create_shader_from_source(string_t *source, int source_size, gpu_shader_type_t shader_type) { gpu_shader_t *shader = (gpu_shader_t *)malloc(sizeof(gpu_shader_t)); char *temp_string_s = shader_type == GPU_SHADER_TYPE_VERTEX ? temp_string_vs : temp_string_fs; #ifdef WITH_D3DCOMPILER strcpy(temp_string_s, source); ID3DBlob *error_message; ID3DBlob *shader_buffer; UINT flags = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_SKIP_VALIDATION; HRESULT hr = D3DCompile(temp_string_s, strlen(source) + 1, NULL, NULL, NULL, "main", shader_type == GPU_SHADER_TYPE_VERTEX ? "vs_5_0" : "ps_5_0", flags, 0, &shader_buffer, &error_message); if (hr != S_OK) { iron_log("%s", (char *)error_message->lpVtbl->GetBufferPointer(error_message)); return NULL; } int size = shader_buffer->lpVtbl->GetBufferSize(shader_buffer); gpu_shader_init(shader, (char *)shader_buffer->lpVtbl->GetBufferPointer(shader_buffer), size, shader_type); shader_buffer->lpVtbl->Release(shader_buffer); #elif defined(IRON_METAL) strcpy(temp_string_s, source); gpu_shader_init(shader, temp_string_s, strlen(temp_string_s), shader_type); #elif defined(IRON_VULKAN) gpu_shader_init(shader, source, source_size, shader_type); #endif return shader; } gpu_pipeline_t *gpu_create_pipeline() { gpu_pipeline_t *pipeline = (gpu_pipeline_t *)malloc(sizeof(gpu_pipeline_t)); gpu_pipeline_init(pipeline); return pipeline; } void gpu_delete_pipeline(gpu_pipeline_t *pipeline) { gpu_pipeline_destroy(pipeline); free(pipeline); } gpu_texture_t *gpu_create_render_target(i32 width, i32 height, i32 format) { gpu_texture_t *render_target = (gpu_texture_t *)malloc(sizeof(gpu_texture_t)); gpu_render_target_init(render_target, width, height, (gpu_texture_format_t)format); render_target->buffer = NULL; return render_target; } gpu_texture_t *gpu_create_texture_from_bytes(buffer_t *data, i32 width, i32 height, i32 format) { gpu_texture_t *texture = (gpu_texture_t *)malloc(sizeof(gpu_texture_t)); texture->buffer = NULL; gpu_texture_init_from_bytes(texture, data->buffer, width, height, (gpu_texture_format_t)format); return texture; } gpu_texture_t *gpu_create_texture_from_encoded_bytes(buffer_t *data, string_t *format) { gpu_texture_t *texture = (gpu_texture_t *)malloc(sizeof(gpu_texture_t)); texture->buffer = NULL; unsigned char *texture_data; gpu_texture_format_t texture_format; int width; int height; if (ends_with(format, "k")) { width = iron_read_s32le(data->buffer); height = iron_read_s32le(data->buffer + 4); char fourcc[5]; fourcc[0] = data->buffer[8]; fourcc[1] = data->buffer[9]; fourcc[2] = data->buffer[10]; fourcc[3] = data->buffer[11]; fourcc[4] = 0; int compressed_size = data->length - 12; if (strcmp(fourcc, "LZ4 ") == 0) { int output_size = width * height * 4; texture_data = (unsigned char *)malloc(output_size); LZ4_decompress_safe((char *)data->buffer + 12, (char *)texture_data, compressed_size, output_size); texture_format = GPU_TEXTURE_FORMAT_RGBA32; } else if (strcmp(fourcc, "LZ4F") == 0) { int output_size = width * height * 16; texture_data = (unsigned char *)malloc(output_size); LZ4_decompress_safe((char *)data->buffer + 12, (char *)texture_data, compressed_size, output_size); texture_format = GPU_TEXTURE_FORMAT_RGBA128; } } else if (ends_with(format, "hdr")) { int comp; texture_data = (unsigned char *)stbi_loadf_from_memory(data->buffer, data->length, &width, &height, &comp, 4); texture_format = GPU_TEXTURE_FORMAT_RGBA128; } else { // jpg, png, .. int comp; texture_data = stbi_load_from_memory(data->buffer, data->length, &width, &height, &comp, 4); texture_format = GPU_TEXTURE_FORMAT_RGBA32; } gpu_texture_init_from_bytes(texture, texture_data, width, height, texture_format); free(texture_data); return texture; } gpu_texture_t *iron_load_texture(string_t *file) { #ifdef WITH_EMBED buffer_t *b = embed_get(file); if (b != NULL) { return gpu_create_texture_from_encoded_bytes(b, ".k"); } #endif iron_file_reader_t reader; if (!iron_file_reader_open(&reader, file, IRON_FILE_TYPE_ASSET)) { return NULL; } int size = (int)iron_file_reader_size(&reader); unsigned char *data = (unsigned char *)malloc(size); iron_file_reader_read(&reader, data, size); iron_file_reader_close(&reader); buffer_t buf; buf.buffer = data; buf.length = size; return gpu_create_texture_from_encoded_bytes(&buf, file); } void iron_delete_texture(gpu_texture_t *texture) { gpu_texture_destroy(texture); free(texture); } #ifdef WITH_AUDIO any iron_load_sound(string_t *file) { iron_a1_sound_t *sound = iron_a1_sound_create(file); return sound; } #endif buffer_t *iron_load_blob(string_t *file) { #ifdef WITH_EMBED buffer_t *b = embed_get(file); if (b != NULL) { return b; } #endif iron_file_reader_t reader; if (!iron_file_reader_open(&reader, file, IRON_FILE_TYPE_ASSET)) { return NULL; } uint32_t reader_size = (uint32_t)iron_file_reader_size(&reader); buffer_t *buffer = buffer_create(reader_size); iron_file_reader_read(&reader, buffer->buffer, reader_size); iron_file_reader_close(&reader); return buffer; } void iron_set_window_title(string_t *title) { iron_window_set_title(title); #if defined(IRON_IOS) || defined(IRON_ANDROID) strcpy(mobile_title, title); #endif } void iron_set_window_mode(i32 mode) { iron_window_change_mode((iron_window_mode_t)mode); } i32 iron_screen_dpi() { return iron_display_current_mode(iron_primary_display()).pixels_per_inch; } i32 iron_display_width(i32 index) { return iron_display_current_mode(index).width; } i32 iron_display_height(i32 index) { return iron_display_current_mode(index).height; } i32 iron_display_x(i32 index) { return iron_display_current_mode(index).x; } i32 iron_display_y(i32 index) { return iron_display_current_mode(index).y; } i32 iron_display_frequency(i32 index) { return iron_display_current_mode(index).frequency; } bool iron_display_is_primary(i32 index) { return index == iron_primary_display(); } buffer_t *gpu_get_texture_pixels(gpu_texture_t *image) { if (image->buffer == NULL) { image->buffer = malloc(sizeof(buffer_t)); image->buffer->buffer = NULL; } image->buffer->length = gpu_texture_format_size(image->format) * image->width * image->height; if (image->buffer->buffer == NULL) { image->buffer->buffer = malloc(image->buffer->length); } gpu_get_render_target_pixels(image, image->buffer->buffer); return image->buffer; } void _gpu_begin(gpu_texture_t *render_target, any_array_t *additional, gpu_texture_t *depth_buffer, unsigned flags, unsigned color, float depth) { if (render_target == NULL) { gpu_begin(NULL, 0, NULL, flags, color, depth); } else { int32_t length = 1; gpu_texture_t *render_targets[8] = { render_target, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; if (additional != NULL) { length = additional->length + 1; for (int32_t i = 1; i < length; ++i) { render_targets[i] = additional->buffer[i - 1]; } } gpu_begin(render_targets, length, depth_buffer, flags, color, depth); } } void iron_file_save_bytes(string_t *path, buffer_t *bytes, u64 length) { u64 byte_length = length > 0 ? length : (u64)bytes->length; if (byte_length > (u64)bytes->length) { byte_length = (u64)bytes->length; } #ifdef IRON_WINDOWS MultiByteToWideChar(CP_UTF8, 0, path, -1, temp_wstring, 1024); FILE *file = _wfopen(temp_wstring, L"wb"); #else FILE *file = fopen(path, "wb"); #endif if (file == NULL) { return; } fwrite(bytes->buffer, 1, byte_length, file); fclose(file); } i32 iron_sys_command(string_t *cmd) { #ifdef IRON_WINDOWS int wlen = MultiByteToWideChar(CP_UTF8, 0, cmd, -1, NULL, 0); wchar_t *wstr = malloc(sizeof(wchar_t) * wlen); MultiByteToWideChar(CP_UTF8, 0, cmd, -1, wstr, wlen); wchar_t comspec[MAX_PATH]; GetEnvironmentVariableW(L"ComSpec", comspec, MAX_PATH); wchar_t cmdline[2048]; swprintf(cmdline, 2048, L"\"%s\" /c %s", comspec, wstr); STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; PROCESS_INFORMATION pi; memset(&pi, 0, sizeof(pi)); CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi); free(wstr); WaitForSingleObject(pi.hProcess, INFINITE); DWORD exit_code = 0; GetExitCodeProcess(pi.hProcess, &exit_code); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); int result = (int)exit_code; #elif defined(IRON_IOS) int result = 0; #else int result = system(cmd); #endif return result; } string_t *iron_get_files_location() { #ifdef IRON_MACOS char path[1024]; strcpy(path, iron_get_resource_path()); strcat(path, "/"); strcat(path, IRON_OUTDIR); strcat(path, "/"); return path; #elif defined(IRON_IOS) char path[1024]; strcpy(path, iron_get_resource_path()); strcat(path, "/"); strcat(path, IRON_OUTDIR); strcat(path, "/"); return path; #else return iron_internal_get_files_location(); #endif } typedef struct _callback_data { int32_t size; char url[512]; void (*func)(char *, buffer_t *); } _callback_data_t; void _http_callback(int error, int response, const char *body, void *callback_data) { _callback_data_t *cbd = (_callback_data_t *)callback_data; buffer_t *buffer = NULL; if (body != NULL) { buffer = malloc(sizeof(buffer_t)); buffer->length = cbd->size > 0 ? cbd->size : strlen(body); buffer->buffer = body; } cbd->func(cbd->url, buffer); free(cbd); } void _iron_http_request(string_t *url, i32 size, void (*callback)(char *, buffer_t *)) { _callback_data_t *cbd = malloc(sizeof(_callback_data_t)); cbd->size = size; strcpy(cbd->url, url); cbd->func = callback; char url_base[512]; char url_path[512]; const char *curl = url; int i = 0; for (; i < strlen(curl) - 8; ++i) { if (curl[i + 8] == '/') { break; } url_base[i] = curl[i + 8]; // Strip https:// } url_base[i] = 0; int j = 0; if (strlen(url_base) < strlen(curl) - 8) { ++i; // Skip / } for (; j < strlen(curl) - 8 - i; ++j) { if (curl[i + 8 + j] == 0) { break; } url_path[j] = curl[i + 8 + j]; } url_path[j] = 0; #ifdef IRON_ANDROID iron_http_request(curl, url_path, NULL, 443, true, 0, NULL, &_http_callback, cbd); #elif defined(IRON_LINUX) // TODO #else iron_http_request(url_base, url_path, NULL, 443, true, 0, NULL, &_http_callback, cbd); #endif } bool _window_close_callback(void *data) { #ifdef IRON_WINDOWS bool save = false; wchar_t title[1024]; GetWindowTextW(iron_windows_window_handle(), title, sizeof(title)); bool dirty = wcsstr(title, L"* - ArmorPaint") != NULL; if (dirty) { int res = MessageBox(iron_windows_window_handle(), L"Project has been modified, save changes?", L"Save Changes?", MB_YESNOCANCEL | MB_ICONEXCLAMATION); if (res == IDYES) { save = true; } else if (res == IDNO) { save = false; } else { // Cancel return false; } } if (save_and_quit_callback_set) { iron_save_and_quit(save); return false; } #endif return true; } void iron_set_save_and_quit_callback(void (*callback)(bool)) { iron_save_and_quit = callback; save_and_quit_callback_set = true; iron_window_set_close_callback(_window_close_callback, NULL); } void iron_delay_idle_sleep() { paused_frames = 0; } #ifdef WITH_NFD char_ptr_array_t *iron_open_dialog(char *filter_list, char *default_path, bool open_multiple) { nfdpathset_t out_paths; nfdchar_t* out_path; nfdresult_t result = open_multiple ? NFD_OpenDialogMultiple(filter_list, default_path, &out_paths) : NFD_OpenDialog(filter_list, default_path, &out_path); if (result == NFD_OKAY) { int path_count = open_multiple ? (int)NFD_PathSet_GetCount(&out_paths) : 1; char_ptr_array_t *result = any_array_create(path_count); if (open_multiple) { for (int i = 0; i < path_count; ++i) { nfdchar_t* out_path = NFD_PathSet_GetPath(&out_paths, i); result->buffer[i] = out_path; } // NFD_PathSet_Free(&out_paths); } else { result->buffer[0] = out_path; // free(out_path); } return result; } return NULL; } static char iron_save_dialog_path[512]; char *iron_save_dialog(char *filter_list, char *default_path) { nfdchar_t *out_path = NULL; nfdresult_t result = NFD_SaveDialog(filter_list, default_path, &out_path); if (result == NFD_OKAY) { strcpy(iron_save_dialog_path, out_path); free(out_path); return iron_save_dialog_path; } return NULL; } #elif defined(IRON_ANDROID) char_ptr_array_t *iron_open_dialog(char *filter_list, char *default_path, bool open_multiple) { AndroidFileDialogOpen(); return NULL; } char *iron_save_dialog(char *filter_list, char *default_path) { wchar_t *out_path = AndroidFileDialogSave(); wcstombs(temp_string, out_path, sizeof(temp_string)); return temp_string; } #elif defined(IRON_IOS) char_ptr_array_t *iron_open_dialog(char *filter_list, char *default_path, bool open_multiple) { // Once finished drop_files callback is called IOSFileDialogOpen(); return NULL; } char *iron_save_dialog(char *filter_list, char *default_path) { // Path to app document directory wchar_t *out_path = IOSFileDialogSave(); wcstombs(temp_string, out_path, sizeof(temp_string)); return temp_string; } #endif char *iron_read_directory(char *path) { char *files = temp_string; files[0] = 0; directory dir = open_dir(path); if (dir.handle == NULL) { return files; } while (true) { file f = read_next_file(&dir); if (!f.valid) { break; } #ifdef IRON_WINDOWS char file_path[512]; strcpy(file_path, path); strcat(file_path, "\\"); strcat(file_path, f.name); if (FILE_ATTRIBUTE_HIDDEN & GetFileAttributesA(file_path)) { continue; // Skip hidden files } #endif if (files[0] != '\0') { strcat(files, "\n"); } strcat(files, f.name); } close_dir(&dir); return files; } void iron_create_directory(char *path) { #ifdef IRON_IOS IOSCreateDirectory(path); #elif defined(IRON_WINDOWS) char cmd[1024]; strcpy(cmd, "mkdir \""); strcat(cmd, path); strcat(cmd, "\""); iron_sys_command(cmd); #else char cmd[1024]; strcpy(cmd, "mkdir -p \""); strcat(cmd, path); strcat(cmd, "\""); iron_sys_command(cmd); #endif } bool iron_file_exists(char *path) { iron_file_reader_t reader; if (iron_file_reader_open(&reader, path, IRON_FILE_TYPE_ASSET)) { iron_file_reader_close(&reader); return true; } return false; } void iron_delete_file(char *path) { #ifdef IRON_IOS IOSDeleteFile(path); #elif defined(IRON_WINDOWS) char cmd[1024]; strcpy(cmd, "del /f \""); strcat(cmd, path); strcat(cmd, "\""); iron_sys_command(cmd); #else char cmd[1024]; strcpy(cmd, "rm \""); strcat(cmd, path); strcat(cmd, "\""); iron_sys_command(cmd); #endif } #ifdef WITH_COMPRESS buffer_t *iron_inflate(buffer_t *bytes, bool raw) { unsigned char *inflated; int inflated_len = bytes->length * 2; int out_len = -1; while (out_len == -1) { inflated_len *= 2; inflated = (unsigned char *)realloc(inflated, inflated_len); out_len = sinflate(inflated, inflated_len, bytes->buffer, bytes->length); } buffer_t *output = buffer_create(0); output->buffer = inflated; output->length = out_len; return output; } buffer_t *iron_deflate(buffer_t *bytes, bool raw) { struct sdefl sdefl; memset(&sdefl, 0, sizeof(sdefl)); void *deflated = malloc(sdefl_bound(bytes->length)); // raw == sdeflate int out_len = zsdeflate(&sdefl, deflated, bytes->buffer, bytes->length, SDEFL_LVL_MIN); buffer_t *output = buffer_create(0); output->buffer = deflated; output->length = out_len; return output; } unsigned char *iron_deflate_raw(unsigned char *data, int data_len, int *out_len, int quality) { struct sdefl sdefl; memset(&sdefl, 0, sizeof(sdefl)); void *deflated = malloc(sdefl_bound(data_len)); *out_len = zsdeflate(&sdefl, deflated, data, data_len, SDEFL_LVL_MIN); return (unsigned char *)deflated; } #endif #ifdef WITH_IMAGE_WRITE void _write_image(char *path, buffer_t *bytes, i32 w, i32 h, i32 format, int image_format, int quality) { int comp = 0; unsigned char *pixels = NULL; unsigned char *rgba = (unsigned char *)bytes->buffer; if (format == 0) { // RGBA comp = 4; pixels = rgba; } else if (format == 1) { // R comp = 1; pixels = rgba; } else if (format == 2) { // RGB1 comp = 3; pixels = (unsigned char *)malloc(w * h * comp); for (int i = 0; i < w * h; ++i) { #ifdef IRON_BGRA pixels[i * 3 ] = rgba[i * 4 + 2]; pixels[i * 3 + 1] = rgba[i * 4 + 1]; pixels[i * 3 + 2] = rgba[i * 4 ]; #else pixels[i * 3 ] = rgba[i * 4 ]; pixels[i * 3 + 1] = rgba[i * 4 + 1]; pixels[i * 3 + 2] = rgba[i * 4 + 2]; #endif } } else if (format > 2) { // RRR1, GGG1, BBB1, AAA1 comp = 1; pixels = (unsigned char *)malloc(w * h * comp); int off = format - 3; #ifdef IRON_BGRA off = 2 - off; #endif for (int i = 0; i < w * h; ++i) { pixels[i] = rgba[i * 4 + off]; } } image_format == 0 ? stbi_write_jpg(path, w, h, comp, pixels, quality) : stbi_write_png(path, w, h, comp, pixels, w * comp); if (pixels != rgba) { free(pixels); } } void iron_write_jpg(char *path, buffer_t *bytes, i32 w, i32 h, i32 format, i32 quality) { // RGBA, R, RGB1, RRR1, GGG1, BBB1, AAA1 _write_image(path, bytes, w, h, format, 0, quality); } void iron_write_png(char *path, buffer_t *bytes, i32 w, i32 h, i32 format) { _write_image(path, bytes, w, h, format, 1, 100); } unsigned char *_encode_data; int _encode_size; void _encode_image_func(void *context, void *data, int size) { memcpy(_encode_data + _encode_size, data, size); _encode_size += size; } buffer_t *_encode_image(buffer_t *bytes, i32 w, i32 h, i32 format, i32 quality) { _encode_data = (unsigned char *)malloc(w * h * 4); _encode_size = 0; format == 0 ? stbi_write_jpg_to_func(&_encode_image_func, NULL, w, h, 4, bytes->buffer, quality) : stbi_write_png_to_func(&_encode_image_func, NULL, w, h, 4, bytes->buffer, w * 4); buffer_t *buffer = malloc(sizeof(buffer_t)); buffer->buffer = _encode_data; buffer->length = _encode_size; return buffer; } buffer_t *iron_encode_jpg(buffer_t *bytes, i32 w, i32 h, i32 format, i32 quality) { return _encode_image(bytes, w, h, 0, quality); } buffer_t *iron_encode_png(buffer_t *bytes, i32 w, i32 h, i32 format) { return _encode_image(bytes, w, h, 1, 100); } #endif #ifdef WITH_VIDEO_WRITE static FILE *iron_mp4_fp; static int iron_mp4_w; static int iron_mp4_h; static int iron_mp4_stride; static H264E_persist_t *iron_mp4_enc = NULL; static H264E_scratch_t *iron_mp4_scratch = NULL; static char iron_mp4_path[512]; static char iron_mp4_path_264[512]; static char *iron_mp4_yuv_buf; static size_t iron_mp4_get_nal_size(uint8_t *buf, size_t size) { size_t pos = 3; while ((size - pos) > 3) { if (buf[pos] == 0 && buf[pos + 1] == 0 && buf[pos + 2] == 1) { return pos; } if (buf[pos] == 0 && buf[pos + 1] == 0 && buf[pos + 2] == 0 && buf[pos + 3] == 1) { return pos; } pos++; } return size; } static int iron_mp4_write_callback(int64_t offset, const void *buffer, size_t size, void *token) { FILE *f = (FILE *)token; fseek(f, offset, SEEK_SET); return fwrite(buffer, 1, size, f) != size; } void iron_mp4_begin(char *path, i32 w, i32 h) { strcpy(iron_mp4_path, path); strcpy(iron_mp4_path_264, path); int len = strlen(iron_mp4_path_264); iron_mp4_path_264[len - 1] = '4'; iron_mp4_path_264[len - 2] = '6'; iron_mp4_path_264[len - 3] = '2'; iron_mp4_stride = w; iron_mp4_w = w - w % 16; iron_mp4_h = h - h % 16; H264E_create_param_t create_param = {0}; create_param.width = iron_mp4_w; create_param.height = iron_mp4_h; int sizeof_persist = 0; int sizeof_scratch = 0; H264E_sizeof(&create_param, &sizeof_persist, &sizeof_scratch); iron_mp4_enc = (H264E_persist_t *)malloc(sizeof_persist); iron_mp4_scratch = (H264E_scratch_t *)malloc(sizeof_scratch); H264E_init(iron_mp4_enc, &create_param); iron_mp4_fp = fopen(iron_mp4_path_264, "wb"); int frame_size = (int)(iron_mp4_w * iron_mp4_h * 1.5); iron_mp4_yuv_buf = malloc(frame_size); } void iron_mp4_end() { if (iron_mp4_fp == NULL) { return; } buffer_t *blob = iron_load_blob(iron_mp4_path_264); uint8_t *buf = blob->buffer; size_t buf_size = blob->length; FILE *fout = fopen(iron_mp4_path, "wb"); MP4E_mux_t *mux = MP4E_open(0, 0, fout, iron_mp4_write_callback); mp4_h26x_writer_t mp4wr; mp4_h26x_write_init(&mp4wr, mux, iron_mp4_w, iron_mp4_h, false); while (buf_size > 0) { size_t nal_size = iron_mp4_get_nal_size(buf, buf_size); if (nal_size < 4) { buf += 1; buf_size -= 1; continue; } int fps = 24; mp4_h26x_write_nal(&mp4wr, buf, nal_size, 90000 / fps); buf += nal_size; buf_size -= nal_size; } MP4E_close(mux); mp4_h26x_write_close(&mp4wr); free(iron_mp4_enc); free(iron_mp4_scratch); free(iron_mp4_yuv_buf); fclose(fout); fclose(iron_mp4_fp); iron_mp4_fp = NULL; } void iron_mp4_encode(buffer_t *pixels) { // rgba to yuv420p for (int i = 0; i < iron_mp4_w; ++i) { for (int j = 0; j < iron_mp4_h; ++j) { int k = i + j * iron_mp4_stride; uint8_t r = pixels->buffer[k * 4]; uint8_t g = pixels->buffer[k * 4 + 1]; uint8_t b = pixels->buffer[k * 4 + 2]; uint8_t y = (( 66 * r + 129 * g + 25 * b + 128) / 256) + 16; uint8_t u = ((-38 * r - 74 * g + 112 * b + 128) / 256) + 128; uint8_t v = ((112 * r - 94 * g - 18 * b + 128) / 256) + 128; int l = i + j * iron_mp4_w; int m = i / 2 + j / 2 * (iron_mp4_w / 2); iron_mp4_yuv_buf[l] = y; iron_mp4_yuv_buf[iron_mp4_w * iron_mp4_h + m] = u; iron_mp4_yuv_buf[iron_mp4_w * iron_mp4_h + (iron_mp4_w * iron_mp4_h) / 4 + m] = v; } } H264E_run_param_t run_param = {0}; run_param.frame_type = 0; run_param.encode_speed = H264E_SPEED_SLOWEST; // H264E_SPEED_FASTEST; run_param.desired_frame_bytes = (2048 * 4) * 1000 / 8 / 30; // 2048 * 4 kbps run_param.qp_min = 10; run_param.qp_max = 50; H264E_io_yuv_t yuv; yuv.yuv[0] = iron_mp4_yuv_buf; yuv.stride[0] = iron_mp4_w; yuv.yuv[1] = iron_mp4_yuv_buf + iron_mp4_w * iron_mp4_h; yuv.stride[1] = iron_mp4_w / 2; yuv.yuv[2] = iron_mp4_yuv_buf + (int)(iron_mp4_w * iron_mp4_h * 1.25); yuv.stride[2] = iron_mp4_w / 2; uint8_t *coded_data; int sizeof_coded_data; H264E_encode(iron_mp4_enc, iron_mp4_scratch, &run_param, &yuv, &coded_data, &sizeof_coded_data); fwrite(coded_data, sizeof_coded_data, 1, iron_mp4_fp); } #endif void iron_raytrace_init(buffer_t *shader) { if (rt_created) { gpu_buffer_destroy(&rt_constant_buffer); gpu_raytrace_pipeline_destroy(&rt_pipeline); } rt_created = true; gpu_constant_buffer_init(&rt_constant_buffer, rt_constant_buffer_size * 4); gpu_raytrace_pipeline_init(&rt_pipeline, shader->buffer, (int)shader->length, &rt_constant_buffer); } void iron_raytrace_as_init() { if (rt_accel_created) { gpu_raytrace_acceleration_structure_destroy(&rt_accel); } rt_accel_created = true; gpu_raytrace_acceleration_structure_init(&rt_accel); } void iron_raytrace_as_add(struct gpu_buffer *vb, gpu_buffer_t *ib, iron_matrix4x4_t transform) { gpu_raytrace_acceleration_structure_add(&rt_accel, vb, ib, transform); } void iron_raytrace_as_build(struct gpu_buffer *vb_full, gpu_buffer_t *ib_full) { gpu_raytrace_acceleration_structure_build(&rt_accel, vb_full, ib_full); } void iron_raytrace_set_textures(gpu_texture_t *tex0, gpu_texture_t *tex1, gpu_texture_t *tex2, gpu_texture_t *texenv, gpu_texture_t *texsobol, gpu_texture_t *texscramble, gpu_texture_t *texrank) { gpu_raytrace_set_textures(tex0, tex1, tex2, texenv, texsobol, texscramble, texrank); } void iron_raytrace_dispatch_rays(gpu_texture_t *render_target, buffer_t *buffer) { float *cb = (float *)buffer->buffer; gpu_constant_buffer_lock(&rt_constant_buffer, 0, rt_constant_buffer.count); for (int i = 0; i < rt_constant_buffer_size; ++i) { float *floats = (float *)(&rt_constant_buffer.data[i * 4]); floats[0] = cb[i]; } gpu_constant_buffer_unlock(&rt_constant_buffer); gpu_raytrace_set_acceleration_structure(&rt_accel); gpu_raytrace_set_pipeline(&rt_pipeline); gpu_raytrace_set_target(render_target); gpu_raytrace_dispatch_rays(); } #endif