diff --git a/android/app/src/main/assets/resource.pack b/android/app/src/main/assets/resource.pack index f593206..4900d04 100644 Binary files a/android/app/src/main/assets/resource.pack and b/android/app/src/main/assets/resource.pack differ diff --git a/android/app/src/main/java/com/julesgrc0/wispy/MainActivity.java b/android/app/src/main/java/com/julesgrc0/wispy/MainActivity.java index c7c65db..dff50b0 100644 --- a/android/app/src/main/java/com/julesgrc0/wispy/MainActivity.java +++ b/android/app/src/main/java/com/julesgrc0/wispy/MainActivity.java @@ -2,13 +2,13 @@ import android.app.NativeActivity; -import android.content.pm.ActivityInfo; import android.os.Build; import android.os.Bundle; import android.view.View; import android.view.WindowManager; + public class MainActivity extends NativeActivity { static { System.loadLibrary("main"); @@ -21,10 +21,20 @@ protected void onCreate(Bundle savedInstanceState) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { getWindow().getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; } - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); + getWindow().setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN + | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED + + ); getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + View.SYSTEM_UI_FLAG_IMMERSIVE + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION ); } } \ No newline at end of file diff --git a/src/core/config.c b/src/core/config.c index fcafc51..2340cde 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -5,51 +5,56 @@ w_config *load_config() { if (cfg == NULL) return NULL; - char *config_path = malloc(MAX_PATH * 2); + char *config_path = malloc(PATH_LENGTH); if (config_path == NULL) return NULL; - config_path[0] = 0; strcat(config_path, GetApplicationDirectory()); strcat(config_path, CONFIG_NAME); if (FileExists(config_path)) { - char *data = LoadFileText(config_path); - if (data == NULL) { + char *config_data = LoadFileText(config_path); + if (config_data == NULL) { free(config_path); free(cfg); return NULL; } - json_object *root = json_tokener_parse(data); + + json_object *root = json_tokener_parse(config_data); if (root == NULL) { - free(data); free(config_path); free(cfg); + UnloadFileText(config_data); return NULL; } json_object *js_fullscreen = json_object_object_get(root, "fullscreen"); - json_object *js_msaa4x = json_object_object_get(root, "msaa4x"); - json_object *js_vsync = json_object_object_get(root, "vsync"); - json_object *js_width = json_object_object_get(root, "width"); - json_object *js_height = json_object_object_get(root, "height"); - json_object *js_max_fps = json_object_object_get(root, "max_fps"); - cfg->fullscreen = (unsigned int)json_object_get_uint64(js_fullscreen); - cfg->msaa4x = (unsigned int)json_object_get_uint64(js_msaa4x); - cfg->vsync = (unsigned int)json_object_get_uint64(js_vsync); - cfg->width = (unsigned int)json_object_get_uint64(js_width); - cfg->height = (unsigned int)json_object_get_uint64(js_height); - cfg->max_fps = (unsigned int)json_object_get_uint64(js_max_fps); - json_object_put(js_fullscreen); + + + json_object *js_msaa4x = json_object_object_get(root, "msaa4x"); + cfg->msaa4x = (unsigned int)json_object_get_uint64(js_msaa4x); json_object_put(js_msaa4x); + + json_object *js_vsync = json_object_object_get(root, "vsync"); + cfg->vsync = (unsigned int)json_object_get_uint64(js_vsync); json_object_put(js_vsync); + + json_object *js_width = json_object_object_get(root, "width"); + cfg->width = (unsigned int)json_object_get_uint64(js_width); json_object_put(js_width); + + json_object *js_height = json_object_object_get(root, "height"); + cfg->height = (unsigned int)json_object_get_uint64(js_height); json_object_put(js_height); + + json_object *js_max_fps = json_object_object_get(root, "max_fps"); + cfg->max_fps = (unsigned int)json_object_get_uint64(js_max_fps); json_object_put(js_max_fps); + json_object_put(root); - free(data); + UnloadFileText(config_data); } else { memset(cfg, 0, sizeof(w_config)); cfg->fullscreen = 1; @@ -70,7 +75,7 @@ w_config *load_config() { } void save_config(w_config *config) { - char *config_path = malloc(MAX_PATH * 2); + char *config_path = malloc(PATH_LENGTH); if (config_path == NULL) return; @@ -85,23 +90,33 @@ void save_config(w_config *config) { } json_object *js_fullscreen = json_object_new_uint64(config->fullscreen); - json_object *js_msaa4x = json_object_new_uint64(config->msaa4x); - json_object *js_vsync = json_object_new_uint64(config->vsync); - json_object *js_width = json_object_new_uint64(config->width); - json_object *js_height = json_object_new_uint64(config->height); - json_object *js_max_fps = json_object_new_uint64(config->max_fps); - json_object_object_add(root, "fullscreen", js_fullscreen); + + json_object *js_msaa4x = json_object_new_uint64(config->msaa4x); json_object_object_add(root, "msaa4x", js_msaa4x); + + json_object *js_vsync = json_object_new_uint64(config->vsync); json_object_object_add(root, "vsync", js_vsync); + + json_object *js_width = json_object_new_uint64(config->width); json_object_object_add(root, "width", js_width); + + json_object *js_height = json_object_new_uint64(config->height); json_object_object_add(root, "height", js_height); + + json_object *js_max_fps = json_object_new_uint64(config->max_fps); json_object_object_add(root, "max_fps", js_max_fps); const char *data = - json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY); + json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY_TAB); SaveFileText(config_path, (char *)data); - json_object_put(root); + json_object_put(js_fullscreen); + json_object_put(js_msaa4x); + json_object_put(js_vsync); + json_object_put(js_width); + json_object_put(js_height); + json_object_put(js_max_fps); + json_object_put(root); free(config_path); } diff --git a/src/core/utils/camera.c b/src/core/utils/camera.c index e2298f9..6f30c0d 100644 --- a/src/core/utils/camera.c +++ b/src/core/utils/camera.c @@ -45,6 +45,9 @@ void destroy_camera(w_camera *camera) { LOG("camera (null) already destroyed"); return; } + if (camera->matrix != NULL) { + free(camera->matrix); + } free(camera); } @@ -119,7 +122,5 @@ void begin_camera(w_camera *camera) { rlLoadIdentity(); rlMultMatrixf(camera->matrix); } -void end_camera() { - EndMode2D(); -} +void end_camera() { EndMode2D(); } #endif \ No newline at end of file diff --git a/src/gui/action.h b/src/gui/action.h deleted file mode 100644 index c046759..0000000 --- a/src/gui/action.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "../core/controls.h" -#include "../stdafx.h" -#include "gui.h" - -typedef struct w_guiaction { - Vector2 position; - Texture icon; - float size; - w_guicontext *ctx; - -} w_guiaction; - -w_guiaction *create_action(w_guicontext *ctx, Vector2 position, float size, - Texture texture); -bool update_action(w_guiaction *act); -void destroy_action(w_guiaction *act); \ No newline at end of file diff --git a/src/gui/action.c b/src/gui/iconbutton.c similarity index 60% rename from src/gui/action.c rename to src/gui/iconbutton.c index 6d0f798..0a327e6 100644 --- a/src/gui/action.c +++ b/src/gui/iconbutton.c @@ -1,20 +1,20 @@ -#include "action.h" +#include "iconbutton.h" -w_guiaction *create_action(w_guicontext *ctx, Vector2 position, float size, +w_guiiconbutton *create_iconbutton(w_guicontext *ctx, Vector2 position, float size, Texture texture) { - w_guiaction *action = malloc(sizeof(w_guiaction)); - if (action == NULL) + w_guiiconbutton *iconbutton = malloc(sizeof(w_guiiconbutton)); + if (iconbutton == NULL) return NULL; - memset(action, 0, sizeof(w_guiaction)); - action->ctx = ctx; - action->position = Vector2SubtractValue(position, size); - action->size = size; - action->icon = texture; + memset(iconbutton, 0, sizeof(w_guiiconbutton)); + iconbutton->ctx = ctx; + iconbutton->position = Vector2SubtractValue(position, size); + iconbutton->size = size; + iconbutton->icon = texture; - return action; + return iconbutton; } -bool update_action(w_guiaction *act) { +bool update_iconbutton(w_guiiconbutton *act) { #if defined(WISPY_ANDROID) bool clicked = check_collision_touch(act->position, act->size); #else @@ -36,7 +36,7 @@ bool update_action(w_guiaction *act) { return clicked; } -void destroy_action(w_guiaction *act) { +void destroy_iconbutton(w_guiiconbutton *act) { if (act) { free(act); } diff --git a/src/gui/iconbutton.h b/src/gui/iconbutton.h new file mode 100644 index 0000000..3ac045b --- /dev/null +++ b/src/gui/iconbutton.h @@ -0,0 +1,17 @@ +#pragma once +#include "../core/controls.h" +#include "../stdafx.h" +#include "gui.h" + +typedef struct w_guiiconbutton { + Vector2 position; + Texture icon; + float size; + w_guicontext *ctx; + +} w_guiiconbutton; + +w_guiiconbutton *create_iconbutton(w_guicontext *ctx, Vector2 position, float size, + Texture texture); +bool update_iconbutton(w_guiiconbutton *act); +void destroy_iconbutton(w_guiiconbutton *act); \ No newline at end of file diff --git a/src/screens/game/bridge.c b/src/screens/game/bridge.c index f29ca4c..f2bb402 100644 --- a/src/screens/game/bridge.c +++ b/src/screens/game/bridge.c @@ -8,19 +8,13 @@ w_bridge *create_bridge() { } memset(td, 0, sizeof(w_bridge)); - td->chunk_group = create_chunkgroup(CHUNK_GROUP_MID_LEN); - if (td->chunk_group == NULL) { + td->terrain = create_terrain(CHUNK_GROUP_MID_LEN); + if (td->terrain == NULL) { destroy_bridge(td); return NULL; } - td->chunk_view = create_chunkview(td->chunk_group->chunks[0]); - if (td->chunk_view == NULL) { - destroy_bridge(td); - return NULL; - } - - td->player = create_player(td->chunk_group->position); + td->player = create_player(td->terrain->group->position); if (td->player == NULL) { destroy_bridge(td); return NULL; @@ -89,8 +83,7 @@ void destroy_bridge(w_bridge *td) { #endif destroy_controls(td->ctrl); - destroy_chunkgroup(td->chunk_group); - destroy_chunkview(td->chunk_view); + destroy_terrain(td->terrain); destroy_player(td->player); destroy_camera(td->camera); @@ -99,7 +92,7 @@ void destroy_bridge(w_bridge *td) { void physics_update_bridge(w_bridge *td) { - check_player_collision_vel(td->player, td->chunk_view); + check_player_collision_vel(td->player, td->terrain->view); Vector2 next_position = Vector2Scale(td->player->velocity, PHYSICS_TICK * PLAYER_SPEED); @@ -119,19 +112,25 @@ void physics_update_bridge(w_bridge *td) { void update_bridge(w_bridge *td) { if (td->ctrl->key != 0 || - #if defined(WISPY_ANDROID) +#if defined(WISPY_ANDROID) !Vector2Equals(td->camera->target_position, td->camera->position) || - #else +#else !Vector2Equals(td->camera->target_position, get_camera_vec(td->camera)) || - #endif +#endif td->force_update) { - if (!update_chunkview(td->chunk_view, td->chunk_group, td->camera)) { + if (!update_chunkview(td->terrain->view, td->terrain->group, td->camera, +#if defined(WISPY_ANDROID) + update_renderblock +#else + update_renderblock_threadsafe +#endif + )) { LOG("failed to update chunk view"); td->is_active = false; return; } - update_chunkview_lighting(td->chunk_view, get_player_center(td->player), + update_chunkview_lighting(td->terrain->view, get_player_center(td->player), RENDER_CUBE_COUNT * CUBE_W); td->force_update = false; } diff --git a/src/screens/game/bridge.h b/src/screens/game/bridge.h index cc537a7..e5f05b9 100644 --- a/src/screens/game/bridge.h +++ b/src/screens/game/bridge.h @@ -3,9 +3,7 @@ #include "../../stdafx.h" #include "../../entities/player.h" #include "../../terrain/block.h" -#include "../../terrain/chunk.h" -#include "../../terrain/chunk_group.h" -#include "../../terrain/chunk_view.h" +#include "../../terrain/terrain.h" #include "../../core/utils/camera.h" #include "../../core/controls.h" @@ -15,8 +13,7 @@ typedef struct w_bridge { bool is_active; bool force_update; - w_chunkgroup *chunk_group; - w_chunkview *chunk_view; + w_terrain* terrain; w_controls *ctrl; w_player *player; diff --git a/src/screens/game/game.c b/src/screens/game/game.c index 8e2e5bb..48d30ce 100644 --- a/src/screens/game/game.c +++ b/src/screens/game/game.c @@ -21,7 +21,7 @@ void game_screen(w_state *state) { if (td == NULL) return; - w_blockbreaker *bb = create_blockbreaker(state, td->chunk_view, td->camera); + w_blockbreaker *bb = create_blockbreaker(state, td->terrain->view, td->camera); if (bb == NULL) { destroy_bridge(td); return; @@ -90,9 +90,9 @@ void game_screen(w_state *state) { #endif #if defined(WISPY_WINDOWS) - if (TryEnterCriticalSection(&td->chunk_view->csec)) + if (TryEnterCriticalSection(&td->terrain->view->csec)) #elif defined(WISPY_LINUX) - if (pthread_mutex_trylock(&td->chunk_view->mutex) == 0) + if (pthread_mutex_trylock(&td->terrain->view->mutex) == 0) #endif { @@ -106,16 +106,16 @@ void game_screen(w_state *state) { begin_camera(td->camera); #endif - for (unsigned int i = 0; i < td->chunk_view->len; i++) { + for (unsigned int i = 0; i < td->terrain->view->len; i++) { DrawTexturePro( - block_textures[td->chunk_view->blocks[i].block.type - 1], - td->chunk_view->blocks[i].src, + block_textures[td->terrain->view->blocks[i].block.type - 1], + td->terrain->view->blocks[i].src, #if defined(WISPY_ANDROID) - get_rect_to_camera(td->chunk_view->blocks[i].dst, td->camera), + get_rect_to_camera(td->terrain->view->blocks[i].dst, td->camera), #else - td->chunk_view->blocks[i].dst, + td->terrain->view->blocks[i].dst, #endif - VEC_ZERO, 0, td->chunk_view->blocks[i].light); + VEC_ZERO, 0, td->terrain->view->blocks[i].light); } #if defined(WISPY_WINDOWS) || defined(WISPY_LINUX) bstate = update_blockbreaker(bb, td->ctrl, td->player, dt); @@ -142,9 +142,9 @@ void game_screen(w_state *state) { DrawText(TextFormat("FPS: %i", GetFPS()), 50, 50, 30, WHITE); EndTextureMode(); #if defined(WISPY_WINDOWS) - LeaveCriticalSection(&td->chunk_view->csec); + LeaveCriticalSection(&td->terrain->view->csec); #elif defined(WISPY_LINUX) - pthread_mutex_unlock(&td->chunk_view->mutex); + pthread_mutex_unlock(&td->terrain->view->mutex); #endif } diff --git a/src/screens/game/game.h b/src/screens/game/game.h index 704487c..8e08e66 100644 --- a/src/screens/game/game.h +++ b/src/screens/game/game.h @@ -1,13 +1,16 @@ #pragma once -#include "../../stdafx.h" #include "../../core/state.h" #include "../../entities/player.h" -#include "../../terrain/break.h" +#include "../../stdafx.h" +#include "../../terrain/actions/break.h" #include "../../terrain/chunk.h" -#include "../../gui/action.h" +#include "bridge.h" + #include "../../gui/gui.h" -#include "../../gui/joystick.h" -#include "bridge.h" +#if defined(WISPY_ANDROID) +#include "../../gui/iconbutton.h" +#include "../../gui/joystick.h" +#endif void game_screen(w_state *state); diff --git a/src/screens/menu/menu.c b/src/screens/menu/menu.c index 365b74c..ec92e21 100644 --- a/src/screens/menu/menu.c +++ b/src/screens/menu/menu.c @@ -63,7 +63,7 @@ void menu_screen(w_state *state) { cosf(angle) * 1000.f * speed)); #endif - update_chunkview(view, grp, camera); + update_chunkview(view, grp, camera, update_renderblock); update_chunkview_lighting(view, get_camera_center(camera), DEFAULT_LIGHT_RADIUS * 0.75); diff --git a/src/stdafx.h b/src/stdafx.h index 6617ed2..3b9fd48 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -4,6 +4,7 @@ #define _CRT_SECURE_NO_WARNINGS #define _CRTDBG_MAP_ALLOC +#define PATH_LENGTH 520 #include #include @@ -49,14 +50,12 @@ #elif defined(__linux__) && !defined(__ANDROID__) #define WISPY_LINUX -#define MAX_PATH 260 #include #include #elif defined(__ANDROID__) #define WISPY_ANDROID -#define MAX_PATH 260 #include #include diff --git a/src/terrain/break.c b/src/terrain/actions/break.c similarity index 100% rename from src/terrain/break.c rename to src/terrain/actions/break.c diff --git a/src/terrain/break.h b/src/terrain/actions/break.h similarity index 71% rename from src/terrain/break.h rename to src/terrain/actions/break.h index c13e124..3fdb7f1 100644 --- a/src/terrain/break.h +++ b/src/terrain/actions/break.h @@ -1,11 +1,11 @@ #pragma once -#include "../core/controls.h" -#include "../core/state.h" -#include "../core/utils/camera.h" -#include "../core/utils/smooth.h" -#include "../entities/player.h" -#include "../stdafx.h" -#include "chunk_view.h" +#include "../../core/controls.h" +#include "../../core/state.h" +#include "../../core/utils/camera.h" +#include "../../core/utils/smooth.h" +#include "../../entities/player.h" +#include "../../stdafx.h" +#include "../chunk_view.h" #define BREAKER_TIME .8f #define BREAKER_STAGES 6 @@ -36,6 +36,6 @@ w_blockbreaker *create_blockbreaker(w_state *state, w_chunkview *chunk_view, w_breakstate update_blockbreaker(w_blockbreaker *bb, w_controls *ctrl, w_player *player, float dt); -void draw_blockbreaker(w_blockbreaker *bb, w_camera* camera); +void draw_blockbreaker(w_blockbreaker *bb, w_camera *camera); void destroy_blockbreaker(w_blockbreaker *bb); diff --git a/src/terrain/block.h b/src/terrain/block.h index d6f4c93..71ec3b8 100644 --- a/src/terrain/block.h +++ b/src/terrain/block.h @@ -1,4 +1,12 @@ #pragma once +#include "../stdafx.h" + +#define BLOCK_BACKGROUND_MASK 0x80 + +#define BLOCK_IS_BACKGROUND(block_type) (block_type & BLOCK_BACKGROUND_MASK) +#define BLOCK_SET_BACKGROUND(block_type) (block_type | BLOCK_BACKGROUND_MASK) +#define BLOCK_UNSET_BACKGROUND(block_type) (block_type & ~BLOCK_BACKGROUND_MASK) +#define BLOCK_TYPE(block_type) (block_type & ~BLOCK_BACKGROUND_MASK) #define CUBE_SRC_RECT \ (Rectangle) { 0, 0, 8, 8 } diff --git a/src/terrain/chunk.c b/src/terrain/chunk.c index 72e8a6f..541fc8e 100644 --- a/src/terrain/chunk.c +++ b/src/terrain/chunk.c @@ -1,45 +1,84 @@ #include "chunk.h" -w_chunk *create_chunk(unsigned int position, bool thread) { +w_chunk *load_chunk(unsigned int position) { w_chunk *chunk = malloc(sizeof(w_chunk)); if (chunk == NULL) { LOG("failed to allocate chunk"); return NULL; } chunk->position = position; + if (!load_chunk_blocks(chunk)) { + LOG("failed to load chunk blocks"); + free(chunk); + return NULL; + } -#if defined(WISPY_WINDOWS) - chunk->handle = INVALID_HANDLE_VALUE; -#else - chunk->handle = 0; -#endif + LOG("loading chunk: %u", position); + return chunk; +} + +w_chunk *load_chunk_async(unsigned int position) { + w_chunk *chunk = malloc(sizeof(w_chunk)); + if (chunk == NULL) { + LOG("failed to allocate chunk"); + return NULL; + } + chunk->position = position; - if (thread) { + w_chunkthread ct = {.chunk = chunk, .state = CHUNKTHREAD_LOAD}; #if defined(WISPY_WINDOWS) - chunk->handle = CreateThread(NULL, 0, &create_chunk_thread, chunk, 0, 0); - if (chunk->handle == INVALID_HANDLE_VALUE) -#else - if (pthread_create(&chunk->handle, NULL, &create_chunk_thread, chunk) != 0) + chunk->handle = CreateThread(NULL, 0, chunk_thread, &ct, 0, NULL); + if (chunk->handle == NULL || chunk->handle == INVALID_HANDLE_VALUE) +#elif defined(WISPY_LINUX) || defined(WISPY_ANDROID) + if (pthread_create(&chunk->handle, NULL, chunk_thread, &ct) != 0) #endif - { - LOG("failed to create chunk thread"); - free(chunk); - return NULL; - } - - LOG("creating chunk thread: %u", position); - return chunk; + { + LOG("failed to create chunk thread (load)"); + free(chunk); + return NULL; } - create_chunk_thread(chunk); - LOG("creating chunk: %u", position); + LOG("loading chunk: %u", position); return chunk; } +void unload_chunk(w_chunk *chunk) { + if (chunk == NULL) { + LOG("chunk (null) already unloaded"); + return; + } + + if (!write_chunk_file(chunk->position, chunk->blocks)) { + LOG("failed save chunk blocks %u", chunk->position); + } + LOG("unloading chunk: %u", chunk->position); + free(chunk); +} + +void unload_chunk_async(w_chunk *chunk) { + if (chunk == NULL) { + LOG("chunk (null) already unloaded"); + return; + } + + w_chunkthread ct = {.chunk = chunk, .state = CHUNKTHREAD_UNLOAD}; #if defined(WISPY_WINDOWS) -DWORD WINAPI create_chunk_thread(PVOID arg) + chunk->handle = CreateThread(NULL, 0, chunk_thread, &ct, 0, NULL); + if (chunk->handle == NULL || chunk->handle == INVALID_HANDLE_VALUE) +#elif defined(WISPY_LINUX) || defined(WISPY_ANDROID) + if (pthread_create(&chunk->handle, NULL, chunk_thread, &ct) != 0) +#endif + { + LOG("failed to create chunk thread (unload)"); + free(chunk); + return; + } +} + +#if defined(WISPY_WINDOWS) +DWORD chunk_thread(PVOID arg) #else -void *create_chunk_thread(void *arg) +void *chunk_thread(void *arg) #endif { if (!arg) { @@ -50,9 +89,38 @@ void *create_chunk_thread(void *arg) #endif } - LOG("creating chunk"); - w_chunk *chunk = arg; + w_chunkthread *ct = arg; + switch (ct->state) { + case CHUNKTHREAD_LOAD: + if (!load_chunk_blocks(ct->chunk)) { + memset(ct->chunk->blocks, 0, CHUNK_W * CHUNK_H * sizeof(w_block)); + LOG("failed to load chunk blocks: %u", ct->chunk->position); + } + break; + case CHUNKTHREAD_UNLOAD: + unload_chunk(ct->chunk); + break; + } + +#if defined(WISPY_WINDOWS) + return EXIT_SUCCESS; +#else + return NULL; +#endif +} +bool load_chunk_blocks(w_chunk *chunk) { + char *chunk_path = get_terrain_path_chunk(chunk->position); + if (chunk_path == NULL) { + return false; + } + bool exists = FileExists(chunk_path); + free(chunk_path); + return exists ? read_chunk_file(chunk->position, chunk->blocks) + : create_chunk_blocks(chunk); +} + +bool create_chunk_blocks(w_chunk *chunk) { Image base = GenImagePerlinNoise(CHUNK_W, BLOCK_TOP_LAYER_H, chunk->position * CHUNK_W, 0, 6.f); Image mineral = @@ -113,17 +181,5 @@ void *create_chunk_thread(void *arg) UnloadImage(base); UnloadImage(mineral); -#if defined(WISPY_WINDOWS) - chunk->handle = INVALID_HANDLE_VALUE; -#else - chunk->handle = 0; -#endif - - LOG("chunk created: %u", chunk->position); - -#if defined(WISPY_WINDOWS) - return EXIT_SUCCESS; -#else - return NULL; -#endif -} + return write_chunk_file(chunk->position, chunk->blocks); +} \ No newline at end of file diff --git a/src/terrain/chunk.h b/src/terrain/chunk.h index d517306..11424ef 100644 --- a/src/terrain/chunk.h +++ b/src/terrain/chunk.h @@ -1,6 +1,7 @@ #pragma once #include "../stdafx.h" #include "block.h" +#include "terrain_data.h" typedef struct w_chunk { w_block blocks[CHUNK_H * CHUNK_W]; // 2^14 @@ -8,16 +9,33 @@ typedef struct w_chunk { #if defined(WISPY_WINDOWS) HANDLE handle; -#else +#elif defined(WISPY_LINUX) || defined(WISPY_ANDROID) pthread_t handle; #endif } w_chunk; -w_chunk *create_chunk(unsigned int position, bool thread); +typedef enum w_chunkthread_state { + CHUNKTHREAD_LOAD, + CHUNKTHREAD_UNLOAD, +} w_chunkthread_state; + +typedef struct w_chunkthread { + w_chunk *chunk; + w_chunkthread_state state; +} w_chunkthread; + +w_chunk *load_chunk(unsigned int position); +w_chunk *load_chunk_async(unsigned int position); + +void unload_chunk(w_chunk *chunk); +void unload_chunk_async(w_chunk *chunk); + +bool load_chunk_blocks(w_chunk *chunk); +bool create_chunk_blocks(w_chunk* chunk); #if defined(WISPY_WINDOWS) -DWORD WINAPI create_chunk_thread(PVOID arg); -#else -void *create_chunk_thread(void *arg); -#endif +DWORD WINAPI chunk_thread(PVOID arg); +#elif defined(WISPY_LINUX) || defined(WISPY_ANDROID) +void *chunk_thread(void *arg); +#endif \ No newline at end of file diff --git a/src/terrain/chunk_group.c b/src/terrain/chunk_group.c index 062a1f6..8ba8d8a 100644 --- a/src/terrain/chunk_group.c +++ b/src/terrain/chunk_group.c @@ -14,7 +14,7 @@ w_chunkgroup *create_chunkgroup(unsigned int position) { grp->position = position - CHUNK_GROUP_MID_LEN; for (unsigned int x = 0; x < CHUNK_GROUP_LEN; x++) { - grp->chunks[x] = create_chunk(grp->position + x, false); + grp->chunks[x] = load_chunk(grp->position + x); } LOG("creating chunk group: %u", grp->position); @@ -33,13 +33,13 @@ void destroy_chunkgroup(w_chunkgroup *grp) { if (grp->chunks[i]->handle != INVALID_HANDLE_VALUE) { WaitForSingleObject(grp->chunks[i]->handle, INFINITE); } -#elif defined(WISPY_LINUX) +#elif defined(WISPY_LINUX) || defined(WISPY_ANDROID) if (grp->chunks[i]->handle != 0) { pthread_join(grp->chunks[i]->handle, NULL); } #endif - free(grp->chunks[i]); + unload_chunk(grp->chunks[i]); } free(grp); } @@ -48,10 +48,10 @@ void next_chunkgroup(w_chunkgroup *grp) { grp->position += CHUNK_GROUP_MID_LEN; LOG("loading next chunk group: %d", grp->position); for (unsigned int x = 0; x < CHUNK_GROUP_MID_LEN; x++) { - free(grp->chunks[x]); + unload_chunk_async(grp->chunks[x]); grp->chunks[x] = grp->chunks[x + CHUNK_GROUP_MID_LEN]; grp->chunks[x + CHUNK_GROUP_MID_LEN] = - create_chunk(grp->position + CHUNK_GROUP_MID_LEN + x, true); + load_chunk_async(grp->position + CHUNK_GROUP_MID_LEN + x); } } @@ -59,9 +59,9 @@ void prev_chunkgroup(w_chunkgroup *grp) { grp->position -= CHUNK_GROUP_MID_LEN; LOG("loading prev chunk group: %d", grp->position); for (unsigned int x = 0; x < CHUNK_GROUP_MID_LEN; x++) { - free(grp->chunks[x + CHUNK_GROUP_MID_LEN]); + unload_chunk_async(grp->chunks[x + CHUNK_GROUP_MID_LEN]); grp->chunks[x + CHUNK_GROUP_MID_LEN] = grp->chunks[x]; - grp->chunks[x] = create_chunk(grp->position + x, true); + grp->chunks[x] = load_chunk_async(grp->position + x); } } @@ -85,3 +85,20 @@ w_chunk *get_chunkgroup_chunk(w_chunkgroup *grp, unsigned int position) { } return grp->chunks[position - grp->position]; } + +w_block *get_chunkgroup_block(w_chunkgroup *grp, Vector2 position) { + if (position.x < grp->position * CHUNK_W || + position.x >= (grp->position + CHUNK_GROUP_LEN) * CHUNK_W || + position.y < 0 || position.y >= CHUNK_H) { + return NULL; + } + + w_chunk *chunk = get_chunkgroup_chunk(grp, position.x / CHUNK_W); + if (chunk == NULL) { + return NULL; + } + + return &chunk->blocks[(int)position.y * CHUNK_W + + ((int)(position.x - chunk->position * CHUNK_W) / + CHUNK_W)]; +} \ No newline at end of file diff --git a/src/terrain/chunk_group.h b/src/terrain/chunk_group.h index 626fdec..e0cc15e 100644 --- a/src/terrain/chunk_group.h +++ b/src/terrain/chunk_group.h @@ -12,6 +12,7 @@ void destroy_chunkgroup(w_chunkgroup *grp); void next_chunkgroup(w_chunkgroup *grp); void prev_chunkgroup(w_chunkgroup *grp); - int need_chunkgroup_update(w_chunkgroup *grp, unsigned int position); + w_chunk *get_chunkgroup_chunk(w_chunkgroup *grp, unsigned int position); +w_block *get_chunkgroup_block(w_chunkgroup *grp, Vector2 position); \ No newline at end of file diff --git a/src/terrain/chunk_view.c b/src/terrain/chunk_view.c index 20cedb1..7bcc039 100644 --- a/src/terrain/chunk_view.c +++ b/src/terrain/chunk_view.c @@ -51,17 +51,15 @@ void destroy_chunkview(w_chunkview *chunk_view) { free(chunk_view); } -void update_renderblock_async(w_chunkview *chunk_view, w_renderblock *blocks, - size_t len) { +void update_renderblock_threadsafe(w_chunkview *chunk_view, + w_renderblock *blocks, size_t len) { #if defined(WISPY_WINDOWS) EnterCriticalSection(&chunk_view->csec); #elif defined(WISPY_LINUX) pthread_mutex_lock(&chunk_view->mutex); #endif - sfree(chunk_view->blocks); - chunk_view->len = len; - chunk_view->blocks = blocks; + update_renderblock(chunk_view, blocks, len); #if defined(WISPY_WINDOWS) LeaveCriticalSection(&chunk_view->csec); @@ -70,8 +68,18 @@ void update_renderblock_async(w_chunkview *chunk_view, w_renderblock *blocks, #endif } +void update_renderblock(w_chunkview *chunk_view, w_renderblock *blocks, + size_t len) { + sfree(chunk_view->blocks); + chunk_view->len = len; + chunk_view->blocks = blocks; +} + bool update_chunkview(w_chunkview *chunk_view, w_chunkgroup *grp, - w_camera *camera) { + w_camera *camera, + void (*update_renderblock_callback)(w_chunkview *, + w_renderblock *, + size_t)){ Rectangle view = get_camera_view_with_gap(camera); if (view.x < 0) { view.width += view.x; @@ -87,7 +95,7 @@ bool update_chunkview(w_chunkview *chunk_view, w_chunkgroup *grp, } if (view.width <= 0 || view.height <= 0) { - update_renderblock_async(chunk_view, NULL, 0); + update_renderblock_callback(chunk_view, NULL, 0); return true; } @@ -131,7 +139,7 @@ bool update_chunkview(w_chunkview *chunk_view, w_chunkgroup *grp, if (blocks_count == 0) { free(blocks); - update_renderblock_async(chunk_view, NULL, 0); + update_renderblock_callback(chunk_view, NULL, 0); return true; } else if (blocks_count != blocks_len) { w_renderblock *tmp = realloc(blocks, sizeof(w_renderblock) * blocks_count); @@ -142,7 +150,7 @@ bool update_chunkview(w_chunkview *chunk_view, w_chunkgroup *grp, blocks = tmp; } - update_renderblock_async(chunk_view, blocks, blocks_count); + update_renderblock_callback(chunk_view, blocks, blocks_count); return true; } diff --git a/src/terrain/chunk_view.h b/src/terrain/chunk_view.h index 40efdc2..d752012 100644 --- a/src/terrain/chunk_view.h +++ b/src/terrain/chunk_view.h @@ -24,10 +24,17 @@ typedef struct w_chunkview { w_chunkview *create_chunkview(w_chunk *current); void destroy_chunkview(w_chunkview *chunk_view); -void update_renderblock_async(w_chunkview *chunk_view, w_renderblock *blocks, - size_t len); +void update_renderblock_threadsafe(w_chunkview *chunk_view, + w_renderblock *blocks, size_t len); +void update_renderblock(w_chunkview *chunk_view, w_renderblock *blocks, + size_t len); + bool update_chunkview(w_chunkview *chunk_view, w_chunkgroup *grp, - w_camera *camera); + w_camera *camera, + void (*update_renderblock_callback)(w_chunkview *, + w_renderblock *, + size_t)); + void filter_chunkview_blocks(w_chunk *chunk, Rectangle view, w_renderblock *blocks, size_t *rendercount); diff --git a/src/terrain/terrain.c b/src/terrain/terrain.c new file mode 100644 index 0000000..790c580 --- /dev/null +++ b/src/terrain/terrain.c @@ -0,0 +1,38 @@ +#include "terrain.h" + +w_terrain *create_terrain(unsigned int start_position) { + + w_terrain *terrain = malloc(sizeof(w_terrain)); + if (!terrain) + return NULL; + + terrain->position = start_position; + + char *terrain_path = get_terrain_path_folder(); + if (!DirectoryExists(terrain_path)) { +#ifdef WISPY_WINDOWS + if (!CreateDirectoryA(terrain_path, NULL)) +#elif WISPY_LINUX || WISPY_ANDROID + if (mkdir(map_path, 0777) == -1) +#endif + { + free(terrain_path); + free(terrain); + return NULL; + } + } + free(terrain_path); + + terrain->group = create_chunkgroup(start_position); + terrain->view = create_chunkview(terrain->group->chunks[0]); + return terrain; +} + +void destroy_terrain(w_terrain *terrain) { + if (!terrain) + return; + + destroy_chunkgroup(terrain->group); + destroy_chunkview(terrain->view); + free(terrain); +} \ No newline at end of file diff --git a/src/terrain/terrain.h b/src/terrain/terrain.h new file mode 100644 index 0000000..360d31f --- /dev/null +++ b/src/terrain/terrain.h @@ -0,0 +1,16 @@ +#pragma once +#include "../stdafx.h" +#include "chunk.h" +#include "chunk_group.h" +#include "chunk_view.h" + + +typedef struct w_terrain { + unsigned int position; + w_chunkgroup* group; + w_chunkview* view; +} w_terrain; + +w_terrain* create_terrain(unsigned int start_position); +void destroy_terrain(w_terrain* terrain); + diff --git a/src/terrain/terrain_data.c b/src/terrain/terrain_data.c new file mode 100644 index 0000000..b9428a3 --- /dev/null +++ b/src/terrain/terrain_data.c @@ -0,0 +1,93 @@ +#include "terrain_data.h" + +char *get_terrain_path_folder() { + char *path = malloc(PATH_LENGTH); + if (path == NULL) { + return NULL; + } + path[0] = 0; + + strcat(path, GetApplicationDirectory()); + strcat(path, TERRAIN_FOLDER); + + return path; +} + +char *get_terrain_path_config() { + char *path = get_terrain_path_folder(); + if (path == NULL) { + return NULL; + } + strcat(path, TERRAIN_CONFIG); + return path; +} + +char *get_terrain_path_chunk(unsigned int position) { + char *path = get_terrain_path_folder(); + if (path == NULL) { + return NULL; + } + + char position_str[12]; // log10(2^32) + 1 + 1 + sprintf(position_str, "/%u", position); + strcat(path, position_str); + + return path; +} + +bool read_chunk_file(unsigned int position, w_block *blocks) { + char *chunk_path = get_terrain_path_chunk(position); + if (chunk_path == NULL) { + return false; + } + if (!FileExists(chunk_path)) { + free(chunk_path); + return false; + } + + int size = 0; + char *data = LoadFileData(chunk_path, &size); + + if (size != CHUNK_W * CHUNK_H) { + UnloadFileData(data); + return false; + } + + for (unsigned int i = 0; i < CHUNK_W * CHUNK_H; i++) { + blocks[i] = (w_block){ + .is_background = BLOCK_IS_BACKGROUND(data[i]), + .type = BLOCK_TYPE(data[i]), + }; + } + + UnloadFileData(data); + free(chunk_path); + return true; +} + +bool write_chunk_file(unsigned int position, w_block *blocks) { + char *chunk_path = get_terrain_path_chunk(position); + if (chunk_path == NULL) { + return false; + } + + char *data = malloc(CHUNK_W * CHUNK_H); + if (data == NULL) { + free(chunk_path); + return false; + } + + for (unsigned int i = 0; i < CHUNK_W * CHUNK_H; i++) { + + if (blocks[i].is_background) { + data[i] = BLOCK_SET_BACKGROUND(data[i]); + } else { + data[i] = blocks[i].type; + } + } + + SaveFileData(chunk_path, data, CHUNK_W * CHUNK_H); + free(chunk_path); + free(data); + return true; +} \ No newline at end of file diff --git a/src/terrain/terrain_data.h b/src/terrain/terrain_data.h new file mode 100644 index 0000000..7df0dfd --- /dev/null +++ b/src/terrain/terrain_data.h @@ -0,0 +1,14 @@ +#pragma once +#include "../stdafx.h" +#include "block.h" + +#define TERRAIN_FOLDER "terrain" +#define TERRAIN_CONFIG "/data.json" + +char *get_terrain_path_folder(); +char *get_terrain_path_config(); +char *get_terrain_path_chunk(unsigned int position); + +// TODO: compress chunk files +bool read_chunk_file(unsigned int position, w_block *blocks); +bool write_chunk_file(unsigned int position, w_block *blocks); \ No newline at end of file