From 75d019aa341dc78f9d0fb6129e2599dd96aaeb6f Mon Sep 17 00:00:00 2001 From: En Yi Date: Sat, 4 May 2024 17:57:07 +0800 Subject: [PATCH] Rework rendering to allow multi-layer render Internal Changelog: - A scene now has a maximum amount of render texture. It is to be rendered in order of the array by the engine. - A scene now needs an explicit render system to draw onto the render textures. The scene will also init the number of render layers needed. - The change is to allow more separate of concerns when it comes to rendering. A scene can also now compartmentalise the rendered items, which hopefully make understanding the code easier. - Update all exisiting code to use the new render system. --- engine/engine.c | 35 ++++++++++++++++++++++-- engine/engine.h | 15 +++++++++++ engine/engine_conf.h | 2 +- scenes/editor_scene.c | 60 +++++++++++++++++++----------------------- scenes/game_scene.c | 36 ++++++++++++------------- scenes/menu_scene.c | 7 ++--- scenes/scene_impl.h | 3 +-- scenes/scene_systems.c | 6 ----- water_test.c | 24 +++++++---------- 9 files changed, 109 insertions(+), 79 deletions(-) diff --git a/engine/engine.c b/engine/engine.c index caa0b26..4c8a45f 100644 --- a/engine/engine.c +++ b/engine/engine.c @@ -152,15 +152,32 @@ void init_scene(Scene_t* scene, render_func_t render_func, action_func_t action_ //scene->scene_type = scene_type; scene->render_function = render_func; + scene->layers.n_layers = 0; + scene->bg_colour = WHITE; + scene->action_function = action_func; scene->state = SCENE_ENDED; scene->time_scale = 1.0f; } +bool add_scene_layer(Scene_t* scene, int width, int height, Rectangle render_area) +{ + if (scene->layers.n_layers >= MAX_RENDER_LAYERS) return false; + + scene->layers.render_layers[scene->layers.n_layers].layer_tex = LoadRenderTexture(width, height); + scene->layers.render_layers[scene->layers.n_layers].render_area = render_area; + scene->layers.n_layers++; + return true; +} + void free_scene(Scene_t* scene) { sc_map_term_64(&scene->action_map); sc_array_term(&scene->systems); + for (uint8_t i = 0; i < scene->layers.n_layers; ++i) + { + UnloadRenderTexture(scene->layers.render_layers[i].layer_tex); + } free_entity_manager(&scene->ent_manager); deinit_particle_system(&scene->part_sys); } @@ -178,10 +195,24 @@ inline void update_scene(Scene_t* scene, float delta_time) inline void render_scene(Scene_t* scene) { - if (scene->render_function != NULL) + BeginDrawing(); + ClearBackground(scene->bg_colour); + for (uint8_t i = 0; i < scene->layers.n_layers; ++i) { - scene->render_function(scene); + RenderLayer_t* layer = scene->layers.render_layers + i; + Rectangle draw_rec = layer->render_area; + Vector2 draw_pos = {draw_rec.x, draw_rec.y}; + draw_rec.x = 0; + draw_rec.y = 0; + draw_rec.height *= -1; + DrawTextureRec( + layer->layer_tex.texture, + draw_rec, + draw_pos, + WHITE + ); } + EndDrawing(); } inline void do_action(Scene_t* scene, ActionType_t action, bool pressed) diff --git a/engine/engine.h b/engine/engine.h index 1147550..3e61a1f 100644 --- a/engine/engine.h +++ b/engine/engine.h @@ -42,9 +42,22 @@ typedef void(*system_func_t)(Scene_t*); typedef void(*action_func_t)(Scene_t*, ActionType_t, bool); sc_array_def(system_func_t, systems); +typedef struct RenderLayer { + RenderTexture2D layer_tex; + Rectangle render_area; +}RenderLayer_t; + +typedef struct SceneRenderLayers { + RenderLayer_t render_layers[MAX_RENDER_LAYERS]; + uint8_t n_layers; +} SceneRenderLayers_t; + struct Scene { struct sc_map_64 action_map; // key -> actions struct sc_array_systems systems; + SceneRenderLayers_t layers; + Color bg_colour; + // TODO: Render function is obsolete and should be treated like a system render_func_t render_function; action_func_t action_function; EntityManager_t ent_manager; @@ -57,6 +70,7 @@ struct Scene { GameEngine_t *engine; }; + void init_engine(GameEngine_t* engine); void deinit_engine(GameEngine_t* engine); void process_inputs(GameEngine_t* engine, Scene_t* scene); @@ -74,6 +88,7 @@ extern void do_action(Scene_t* scene, ActionType_t action, bool pressed); //void init_scene(Scene_t* scene, SceneType_t scene_type, system_func_t render_func, action_func_t action_func); void init_scene(Scene_t* scene, render_func_t render_func, action_func_t action_func); +bool add_scene_layer(Scene_t* scene, int width, int height, Rectangle render_area); void free_scene(Scene_t* scene); #endif // __ENGINE_H diff --git a/engine/engine_conf.h b/engine/engine_conf.h index 68c195d..3986128 100644 --- a/engine/engine_conf.h +++ b/engine/engine_conf.h @@ -1,6 +1,7 @@ #ifndef _ENGINE_CONF_H #define _ENGINE_CONF_H +#define MAX_RENDER_LAYERS 4 #define MAX_ENTITIES 2048 #define MAX_TEXTURES 16 #define MAX_SPRITES 64 @@ -16,7 +17,6 @@ #define MAX_PARTICLES 64 #define MAX_TILE_TYPES 16 - #define N_TAGS 10 #define N_COMPONENTS 20 #define MAX_COMP_POOL_SIZE MAX_ENTITIES diff --git a/scenes/editor_scene.c b/scenes/editor_scene.c index e1f0efc..ccc9796 100644 --- a/scenes/editor_scene.c +++ b/scenes/editor_scene.c @@ -40,6 +40,9 @@ static bool crate_activation = false; #define SELECTION_REGION_WIDTH (SELECTION_TILE_SIZE * MAX_SPAWN_TYPE) #define SELECTION_REGION_HEIGHT SELECTION_TILE_SIZE +#define GAME_LAYER 0 +#define SELECTION_LAYER 1 +#define CONTROL_LAYER 2 static char* get_spawn_selection_string(enum EntitySpawnSelection sel) { switch(sel) @@ -80,41 +83,19 @@ static inline unsigned int get_tile_idx(int x, int y, const TileGrid_t* tilemap) } // This means you might be able to have two editor scene without running into problems -static RenderTexture2D selection_section; #define SELECTION_RENDER_HEIGHT (SELECTION_REGION_HEIGHT * 3) static void level_scene_render_func(Scene_t* scene) { LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data); Entity_t* p_ent; - Rectangle draw_rec = data->game_rec; - draw_rec.x = 0; - draw_rec.y = 0; - draw_rec.height *= -1; static char buffer[512]; - BeginDrawing(); - ClearBackground(LIGHTGRAY); - DrawTextureRec( - data->game_viewport.texture, - draw_rec, - (Vector2){data->game_rec.x, data->game_rec.y}, - WHITE - ); - draw_rec.width = SELECTION_REGION_WIDTH; - draw_rec.height = -SELECTION_RENDER_HEIGHT; - - Vector2 draw_pos = {data->game_rec.x, data->game_rec.y + data->game_rec.height + SELECTION_GAP}; - DrawTextureRec( - selection_section.texture, - draw_rec, - draw_pos, - WHITE - ); - - draw_pos.x = data->game_rec.x + current_spawn_selection * SELECTION_TILE_SIZE; + Vector2 draw_pos = {data->game_rec.x, data->game_rec.y + data->game_rec.height + SELECTION_GAP}; + BeginTextureMode(scene->layers.render_layers[CONTROL_LAYER].layer_tex); + ClearBackground(BLANK); DrawRectangleLines( - draw_pos.x, draw_pos.y, + data->game_rec.x + current_spawn_selection * SELECTION_TILE_SIZE, draw_pos.y, SELECTION_TILE_SIZE, SELECTION_TILE_SIZE, GREEN ); @@ -185,10 +166,10 @@ static void level_scene_render_func(Scene_t* scene) print_mempool_stats(buffer); DrawText(buffer, gui_x, gui_y, 12, BLACK); - gui_y += 300; + gui_y += 330; sprintf(buffer, "Chests: %u / %u", data->coins.current, data->coins.total); DrawText(buffer, gui_x, gui_y, 24, BLACK); - EndDrawing(); + EndTextureMode(); } static void render_editor_game_scene(Scene_t* scene) @@ -212,7 +193,7 @@ static void render_editor_game_scene(Scene_t* scene) max.x = (int)fmin(tilemap.width, max.x + 1); max.y = (int)fmin(tilemap.height, max.y + 1); - BeginTextureMode(data->game_viewport); + BeginTextureMode(scene->layers.render_layers[GAME_LAYER].layer_tex); ClearBackground(WHITE); BeginMode2D(data->camera.cam); for (int tile_y = min.y; tile_y < max.y; tile_y++) @@ -837,7 +818,7 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed) if (!pressed) metal_toggle = !metal_toggle; const Color crate_colour = metal_toggle ? GRAY : BROWN; Vector2 draw_pos = {SPAWN_CRATE * SELECTION_TILE_SIZE , 0}; - BeginTextureMode(selection_section); + BeginTextureMode(scene->layers.render_layers[SELECTION_LAYER].layer_tex); for (uint8_t i = SPAWN_CRATE; i <= SPAWN_CRATE_BOMB; ++i) { DrawRectangle(draw_pos.x, draw_pos.y, SELECTION_TILE_SIZE, SELECTION_TILE_SIZE, crate_colour); @@ -1015,9 +996,22 @@ void init_sandbox_scene(LevelScene_t* scene) unsigned int tile_idx = (scene->data.tilemap.height - 1) * scene->data.tilemap.width + i; change_a_tile(&scene->data.tilemap, tile_idx, SOLID_TILE); } - selection_section = LoadRenderTexture(SELECTION_REGION_WIDTH, SELECTION_RENDER_HEIGHT); - BeginTextureMode(selection_section); + scene->scene.bg_colour = LIGHTGRAY; + add_scene_layer( + &scene->scene, scene->data.game_rec.width, scene->data.game_rec.height, + scene->data.game_rec + ); + add_scene_layer( + &scene->scene, SELECTION_REGION_WIDTH, SELECTION_REGION_HEIGHT, + (Rectangle){ + scene->data.game_rec.x, scene->data.game_rec.y + scene->data.game_rec.height + SELECTION_GAP, + SELECTION_REGION_WIDTH, SELECTION_REGION_HEIGHT + } + ); + add_scene_layer(&scene->scene, 1280, 640, (Rectangle){0, 0, 1280, 640}); + + BeginTextureMode(scene->scene.layers.render_layers[SELECTION_LAYER].layer_tex); ClearBackground(LIGHTGRAY); Vector2 draw_pos = {0, 0}; const Color crate_colour = metal_toggle ? GRAY : BROWN; @@ -1228,6 +1222,7 @@ void init_sandbox_scene(LevelScene_t* scene) sc_array_add(&scene->scene.systems, &player_respawn_system); sc_array_add(&scene->scene.systems, &level_end_detection_system); sc_array_add(&scene->scene.systems, &render_editor_game_scene); + sc_array_add(&scene->scene.systems, &level_scene_render_func); // This avoid graphical glitch, not essential //sc_array_add(&scene->scene.systems, &update_tilemap_system); @@ -1260,5 +1255,4 @@ void free_sandbox_scene(LevelScene_t* scene) { free_scene(&scene->scene); term_level_scene_data(&scene->data); - UnloadRenderTexture(selection_section); // Unload render texture } diff --git a/scenes/game_scene.c b/scenes/game_scene.c index 93cc9a9..ae533da 100644 --- a/scenes/game_scene.c +++ b/scenes/game_scene.c @@ -10,24 +10,15 @@ static Tile_t all_tiles[MAX_N_TILES] = {0}; +#define GAME_LAYER 0 +#define CONTROL_LAYER 1 static void level_scene_render_func(Scene_t* scene) { LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data); - Rectangle draw_rec = data->game_rec; - draw_rec.x = 0; - draw_rec.y = 0; - draw_rec.height *= -1; - static char buffer[512]; - BeginDrawing(); - ClearBackground(LIGHTGRAY); - DrawTextureRec( - data->game_viewport.texture, - draw_rec, - (Vector2){data->game_rec.x, data->game_rec.y}, - WHITE - ); + BeginTextureMode(scene->layers.render_layers[CONTROL_LAYER].layer_tex); + ClearBackground(BLANK); Entity_t* p_ent; sc_map_foreach_value(&scene->ent_manager.entities_map[PLAYER_ENT_TAG], p_ent) @@ -45,13 +36,13 @@ static void level_scene_render_func(Scene_t* scene) //sprintf(buffer, "Spawn Entity: %s", get_spawn_selection_string(current_spawn_selection)); //DrawText(buffer, gui_x, 240, 12, BLACK); sprintf(buffer, "Number of Entities: %u", sc_map_size_64v(&scene->ent_manager.entities)); - DrawText(buffer, gui_x, 270, 12, BLACK); + DrawText(buffer, gui_x, 70, 12, BLACK); sprintf(buffer, "FPS: %u", GetFPS()); - DrawText(buffer, gui_x, 320, 12, BLACK); + DrawText(buffer, gui_x, 120, 12, BLACK); print_mempool_stats(buffer); - DrawText(buffer, gui_x, 350, 12, BLACK); - EndDrawing(); + DrawText(buffer, gui_x, 150, 12, BLACK); + EndTextureMode(); } static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed) @@ -130,7 +121,7 @@ static void render_regular_game_scene(Scene_t* scene) max.x = (int)fmin(tilemap.width, max.x + 1); max.y = (int)fmin(tilemap.height, max.y + 1); - BeginTextureMode(data->game_viewport); + BeginTextureMode(scene->layers.render_layers[GAME_LAYER].layer_tex); ClearBackground(WHITE); BeginMode2D(data->camera.cam); @@ -369,6 +360,14 @@ void init_game_scene(LevelScene_t* scene) (Rectangle){25, 25, 32*TILE_SIZE, 18*TILE_SIZE} ); + // TODO: Remove the hardcoded window size + scene->scene.bg_colour = LIGHTGRAY; + add_scene_layer( + &scene->scene, scene->data.game_rec.width, scene->data.game_rec.height, + scene->data.game_rec + ); + add_scene_layer(&scene->scene, 1280, 640, (Rectangle){0, 0, 1280, 640}); + create_player(&scene->scene.ent_manager); update_entity_manager(&scene->scene.ent_manager); @@ -401,6 +400,7 @@ void init_game_scene(LevelScene_t* scene) sc_array_add(&scene->scene.systems, &player_respawn_system); sc_array_add(&scene->scene.systems, &update_water_runner_system); sc_array_add(&scene->scene.systems, &render_regular_game_scene); + sc_array_add(&scene->scene.systems, &level_scene_render_func); // This avoid graphical glitch, not essential //sc_array_add(&scene->scene.systems, &update_tilemap_system); diff --git a/scenes/menu_scene.c b/scenes/menu_scene.c index a2e2d73..f9bc55e 100644 --- a/scenes/menu_scene.c +++ b/scenes/menu_scene.c @@ -6,14 +6,14 @@ static void menu_scene_render_func(Scene_t* scene) { MenuSceneData_t* data = &(CONTAINER_OF(scene, MenuScene_t, scene)->data); - BeginDrawing(); + BeginTextureMode(scene->layers.render_layers[0].layer_tex); ClearBackground(RAYWHITE); DrawText("This is a game", 25, 220, 12, BLACK); UI_button(data->buttons, "Start"); UI_button(data->buttons + 1, "Sandbox"); UI_button(data->buttons + 2, "Continue"); UI_button(data->buttons + 3, "Exit"); - EndDrawing(); + EndTextureMode(); } static void exec_component_function(Scene_t* scene, int sel) @@ -132,10 +132,10 @@ static void gui_loop(Scene_t* scene) void init_menu_scene(MenuScene_t* scene) { - //init_scene(&scene->scene, MENU_SCENE, &menu_scene_render_func, &menu_do_action); init_scene(&scene->scene, &menu_scene_render_func, &menu_do_action); sc_array_add(&scene->scene.systems, &gui_loop); + sc_array_add(&scene->scene.systems, &menu_scene_render_func); scene->data.buttons[0] = (UIComp_t) { .bbox = {25,255,125,30}, @@ -160,6 +160,7 @@ void init_menu_scene(MenuScene_t* scene) scene->data.max_comp = 4; scene->data.selected_comp = 0; scene->data.mode = MOUSE_MODE; + add_scene_layer(&scene->scene, 1280, 640, (Rectangle){0, 0, 1280, 640}); sc_map_put_64(&scene->scene.action_map, KEY_UP, ACTION_UP); sc_map_put_64(&scene->scene.action_map, KEY_DOWN, ACTION_DOWN); diff --git a/scenes/scene_impl.h b/scenes/scene_impl.h index c38776c..2ca00be 100644 --- a/scenes/scene_impl.h +++ b/scenes/scene_impl.h @@ -40,7 +40,7 @@ typedef struct LevelCamera { typedef struct LevelSceneData { TileGrid_t tilemap; - RenderTexture2D game_viewport; + // TODO: game_rec is actually obsolete since this is in the scene game layer Rectangle game_rec; LevelCamera_t camera; Sprite_t* tile_sprites[MAX_TILE_SPRITES]; @@ -60,7 +60,6 @@ void free_game_scene(LevelScene_t* scene); void init_sandbox_scene(LevelScene_t* scene); void free_sandbox_scene(LevelScene_t* scene); void init_level_scene_data(LevelSceneData_t* data, uint32_t max_tiles, Tile_t* tiles, Rectangle view_zone); -//void init_level_scene_data(LevelSceneData_t* data, uint32_t max_tiles, Tile_t* tiles); void term_level_scene_data(LevelSceneData_t* data); void reload_level_tilemap(LevelScene_t* scene); void load_next_level_tilemap(LevelScene_t* scene); diff --git a/scenes/scene_systems.c b/scenes/scene_systems.c index fdd9e34..3ea4934 100644 --- a/scenes/scene_systems.c +++ b/scenes/scene_systems.c @@ -4,13 +4,8 @@ #include "constants.h" void init_level_scene_data(LevelSceneData_t* data, uint32_t max_tiles, Tile_t* tiles, Rectangle view_zone) -//void init_level_scene_data(LevelSceneData_t* data, uint32_t max_tiles, Tile_t* tiles) { - //data->game_viewport = LoadRenderTexture(VIEWABLE_MAP_WIDTH*TILE_SIZE, VIEWABLE_MAP_HEIGHT*TILE_SIZE); - //data->game_rec = (Rectangle){25, 25, VIEWABLE_MAP_WIDTH*TILE_SIZE, VIEWABLE_MAP_HEIGHT*TILE_SIZE}; - data->game_viewport = LoadRenderTexture(view_zone.width, view_zone.height); data->game_rec = view_zone; - //data->camera.cam = (Camera2D){0}; memset(&data->camera, 0, sizeof(LevelCamera_t)); data->camera.cam.rotation = 0.0f; data->camera.cam.zoom = 1.0f; @@ -53,7 +48,6 @@ void term_level_scene_data(LevelSceneData_t* data) { sc_map_term_64v(&data->tilemap.tiles[i].entities_set); } - UnloadRenderTexture(data->game_viewport); // Unload render texture } bool load_level_tilemap(LevelScene_t* scene, unsigned int level_num) diff --git a/water_test.c b/water_test.c index 82b0273..36f01d2 100644 --- a/water_test.c +++ b/water_test.c @@ -23,6 +23,7 @@ static GameEngine_t engine = }; static bool water_toggle = false; +#define GAME_LAYER 0 static void level_scene_render_func(Scene_t* scene) { @@ -31,7 +32,7 @@ static void level_scene_render_func(Scene_t* scene) Entity_t* p_ent; - BeginTextureMode(data->game_viewport); + BeginTextureMode(scene->layers.render_layers[GAME_LAYER].layer_tex); ClearBackground(WHITE); BeginMode2D(data->camera.cam); for (size_t i = 0; i < tilemap.n_tiles; ++i) @@ -177,19 +178,7 @@ static void level_scene_render_func(Scene_t* scene) EndMode2D(); EndTextureMode(); - Rectangle draw_rec = data->game_rec; - draw_rec.x = 0; - draw_rec.y = 0; - draw_rec.height *= -1; - BeginDrawing(); - ClearBackground( water_toggle? ColorAlpha(BLUE, 0.2) : LIGHTGRAY); - DrawTextureRec( - data->game_viewport.texture, - draw_rec, - (Vector2){data->game_rec.x, data->game_rec.y}, - WHITE - ); - EndDrawing(); + scene->bg_colour = water_toggle ? ColorAlpha(BLUE, 0.2) : LIGHTGRAY; } static inline unsigned int get_tile_idx(int x, int y, const TileGrid_t* tilemap) @@ -413,11 +402,17 @@ int main(void) LevelScene_t scene; scene.scene.engine = &engine; init_scene(&scene.scene, &level_scene_render_func, &level_do_action); + init_entity_tag_map(&scene.scene.ent_manager, PLAYER_ENT_TAG, 4); + init_entity_tag_map(&scene.scene.ent_manager, DYNMEM_ENT_TAG, 16); init_level_scene_data( &scene.data, MAX_N_TILES, all_tiles, (Rectangle){25, 25, VIEWABLE_MAP_WIDTH*TILE_SIZE, VIEWABLE_MAP_HEIGHT*TILE_SIZE} ); assert(scene.data.tilemap.n_tiles <= MAX_N_TILES); + add_scene_layer( + &scene.scene, scene.data.game_rec.width, scene.data.game_rec.height, + scene.data.game_rec + ); for (size_t i = 0; i < scene.data.tilemap.width; ++i) { @@ -441,6 +436,7 @@ int main(void) sc_array_add(&scene.scene.systems, &toggle_block_system); sc_array_add(&scene.scene.systems, &camera_update_system); sc_array_add(&scene.scene.systems, &player_dir_reset_system); + sc_array_add(&scene.scene.systems, &level_scene_render_func); sc_map_put_64(&scene.scene.action_map, KEY_R, ACTION_RESTART);