diff options
| author | Jonathan Bradley <jcb@pikum.xyz> | 2025-09-09 16:05:14 -0400 |
|---|---|---|
| committer | Jonathan Bradley <jcb@pikum.xyz> | 2025-09-09 16:05:14 -0400 |
| commit | 5daa12fed0449a7811fc25ec62236bc060a56fa7 (patch) | |
| tree | 5af5ff1c4de2ab49dd4d1db9934b3b4ee34a54c2 | |
| parent | c40277cd665e04d300ea839a1dd2ff675655f5fb (diff) | |
pke: first-pass promote pke_level over pke_scene
Major runtime ownership overhaul (scene -> level).
Major ecs 'marked for removal' overhaul, ensuring
that entities and their components are actually
being removed when levels are unloaded.
| -rw-r--r-- | editor/editor-io.cpp | 10 | ||||
| -rw-r--r-- | editor/editor-io.hpp | 4 | ||||
| -rw-r--r-- | editor/editor-types.cpp | 2 | ||||
| -rw-r--r-- | editor/editor-types.hpp | 4 | ||||
| -rw-r--r-- | editor/editor.cpp | 85 | ||||
| -rw-r--r-- | src/camera.cpp | 21 | ||||
| -rw-r--r-- | src/ecs.cpp | 214 | ||||
| -rw-r--r-- | src/ecs.hpp | 1 | ||||
| -rw-r--r-- | src/entities.cpp | 21 | ||||
| -rw-r--r-- | src/game-settings.hpp | 6 | ||||
| -rw-r--r-- | src/game.cpp | 50 | ||||
| -rw-r--r-- | src/level-types.hpp | 12 | ||||
| -rw-r--r-- | src/level.cpp | 75 | ||||
| -rw-r--r-- | src/level.hpp | 8 | ||||
| -rw-r--r-- | src/plugin-types.hpp | 2 | ||||
| -rw-r--r-- | src/project.cpp | 2 | ||||
| -rw-r--r-- | src/scene-types.hpp | 4 | ||||
| -rw-r--r-- | src/scene.cpp | 63 | ||||
| -rw-r--r-- | src/scene.hpp | 5 | ||||
| -rw-r--r-- | src/serialization-camera.cpp | 14 | ||||
| -rw-r--r-- | src/serialization-component.cpp | 3 | ||||
| -rw-r--r-- | src/serialization-font.cpp | 1 | ||||
| -rw-r--r-- | src/serialization-input.cpp | 8 | ||||
| -rw-r--r-- | src/serialization-static-ui.cpp | 3 | ||||
| -rw-r--r-- | src/serialization.cpp | 3 | ||||
| -rw-r--r-- | src/serialization.hpp | 6 | ||||
| -rw-r--r-- | src/static-ui.cpp | 18 | ||||
| -rw-r--r-- | tests/pke-test-dummy.cpp | 1 | ||||
| -rw-r--r-- | tests/pke-test-serialization.cpp | 27 |
29 files changed, 359 insertions, 314 deletions
diff --git a/editor/editor-io.cpp b/editor/editor-io.cpp index ae928e1..27d84fb 100644 --- a/editor/editor-io.cpp +++ b/editor/editor-io.cpp @@ -2,7 +2,6 @@ #include "editor-io.hpp" #include "game-settings.hpp" -#include "scene.hpp" #include "serialization.hpp" #include <fstream> @@ -51,18 +50,13 @@ void pke_editor_scene_save(const char *file_path) { pke_serialize_teardown(helper); } -void pke_editor_scene_load(const char *file_path) { +void pke_editor_scene_load(pke_level *level, const char *file_path) { std::ifstream f(file_path); if (!f.is_open()) { fprintf(stderr, "Failed to load requested scene file: '%s'\n", file_path); return; } - srlztn_deserialize_helper *helper = pke_deserialize_init(pkeSettings.mem_bkt.game_transient); - // TODO scene name is in the file? - helper->scene = pke_scene_get_by_name(file_path); - if (helper->scene == nullptr) { - helper->scene = pke_scene_create(file_path); - } + srlztn_deserialize_helper *helper = pke_deserialize_init(level, pkeSettings.mem_bkt.game_transient); pke_deserialize_scene_from_stream(f, helper); pke_deserialize_scene(helper); diff --git a/editor/editor-io.hpp b/editor/editor-io.hpp index 07e9354..171b67c 100644 --- a/editor/editor-io.hpp +++ b/editor/editor-io.hpp @@ -1,8 +1,10 @@ #ifndef PKE_EDITOR_EDITOR_IO_HPP #define PKE_EDITOR_EDITOR_IO_HPP +#include "level-types.hpp" + void pke_editor_scene_save(const char *file_path); -void pke_editor_scene_load(const char *file_path); +void pke_editor_scene_load(pke_level *level, const char *file_path); void pke_editor_project_save(const char *file_path); void pke_editor_project_load(const char *file_path); diff --git a/editor/editor-types.cpp b/editor/editor-types.cpp index b9d278a..7e2c392 100644 --- a/editor/editor-types.cpp +++ b/editor/editor-types.cpp @@ -1,4 +1,4 @@ #include "editor-types.hpp" -struct editor_master editor_mstr; +struct editor_master editor_mstr{}; diff --git a/editor/editor-types.hpp b/editor/editor-types.hpp index ff8239b..f84b7d3 100644 --- a/editor/editor-types.hpp +++ b/editor/editor-types.hpp @@ -6,8 +6,8 @@ // TODO editor state (scene vs level) struct editor_master { - pke_scene *active_scene; - pk_str target_scene_path; + pke_scene *active_scene = nullptr; + pk_str target_scene_path = {}; bool shouldLoadScene = false; bool shouldSaveScene = false; }; diff --git a/editor/editor.cpp b/editor/editor.cpp index ab8598c..2a4860a 100644 --- a/editor/editor.cpp +++ b/editor/editor.cpp @@ -12,6 +12,7 @@ #include "game-settings.hpp" #include "game.hpp" #include "imgui.h" +#include "level-types.hpp" #include "level.hpp" #include "math-helpers.hpp" #include "msdf-atlas-gen/msdf-atlas-gen.h" @@ -35,6 +36,7 @@ #include <cstdio> #include <filesystem> #include <future> +#include <fstream> #include <regex> #ifdef WIN32 @@ -84,7 +86,7 @@ CameraHandle selectedCamera = CameraHandle_MAX; static pke_ui_box *selected_ui_box = NULL; const char* const newSceneName = "newScene.pstf"; -bool shouldOpenLoadSceneDialog = false; +const char* const editorLevelName = "editor_level"; bool shouldOpenSaveSceneDialog = false; bool shouldOpenNewScene = false; bool shouldSaveProjectFile = false; @@ -108,6 +110,7 @@ glm::vec3 unproject(glm::vec3 windowCoords) { } void PkeEditor_Tick(double delta) { + const char *scene_to_load_or_create_path = nullptr; if (shouldRunCurrentScene) { shouldRunCurrentScene = false; auto *task = pk_new<std::packaged_task<void()>>(); @@ -142,48 +145,18 @@ void PkeEditor_Tick(double delta) { } if (shouldDisableEditor) { PkeEditor_Teardown(); + return; } if (shouldOpenNewScene) { shouldOpenNewScene = false; - if (editor_mstr.active_scene != nullptr) { - // TODO move most of this to the level - pke_ui_box_count_T box_count; - pke_ui_box **root_boxes = pke_ui_get_root_boxes(&box_count); - for (pke_ui_box_count_T i = 0; i < box_count; ++i) { - ECS_MarkForRemoval(root_boxes[i]); - } - pke_scene_remove(editor_mstr.active_scene->scene_handle); - } - ActiveCamera = &NullCamera; - editor_mstr.active_scene = pke_scene_create(newSceneName); - return; + editor_mstr.shouldLoadScene = false; + editor_mstr.shouldSaveScene = false; + scene_to_load_or_create_path = newSceneName; } if (shouldSaveProjectFile) { shouldSaveProjectFile = false; PkeProject_Save(); } - if (shouldOpenLoadSceneDialog) { - shouldOpenLoadSceneDialog = false; - auto *task = pk_new<std::packaged_task<void()>>(); - new (task) std::packaged_task<void()>( [] { - // TODO move most of this to the level - pke_ui_box_count_T box_count; - pke_ui_box **root_boxes = pke_ui_get_root_boxes(&box_count); - for (pke_ui_box_count_T i = 0; i < box_count; ++i) { - ECS_MarkForRemoval(root_boxes[i]); - } - const char * patterns[1] = {"*.pstf"}; - char *selectedScene = tinyfd_openFileDialog(nullptr, "cafebabe.pstf", 1, patterns, "Pke Scene Text File", 0); - if (editor_mstr.active_scene != nullptr) { - pke_scene_remove(editor_mstr.active_scene->scene_handle); - } - ActiveCamera = &NullCamera; - editor_mstr.active_scene = pke_scene_create(selectedScene); - return; - }); - PkeThreads_Enqueue(threadPoolHandle, task); - } - if (shouldOpenSaveSceneDialog) { assert(editor_mstr.active_scene != nullptr); shouldOpenSaveSceneDialog = false; @@ -225,27 +198,36 @@ void PkeEditor_Tick(double delta) { shouldRebuildProjectDir = true; } if (editor_mstr.target_scene_path.val != nullptr) { - // TODO move most of this to the level - pke_ui_box_count_T box_count; - pke_ui_box **root_boxes = pke_ui_get_root_boxes(&box_count); - for (pke_ui_box_count_T i = 0; i < box_count; ++i) { - ECS_MarkForRemoval(root_boxes[i]); + scene_to_load_or_create_path = editor_mstr.target_scene_path.val; + } + if (scene_to_load_or_create_path != nullptr) { + ActiveCamera = &NullCamera; + + pkeSettings.rt.nextLevel = pke_level_create(editorLevelName, pk_uuid_zed, pk_uuid_zed); + editor_mstr.active_scene = pke_scene_create(scene_to_load_or_create_path); + scene_instance si{}; + si.scene_handle = editor_mstr.active_scene->scene_handle; + pk_arr_append_t(&pkeSettings.rt.nextLevel->scene_instances, si); + + if (scene_to_load_or_create_path == newSceneName) { + return; } - if (editor_mstr.active_scene != nullptr) { - pke_scene_remove(editor_mstr.active_scene->scene_handle); + + std::filesystem::path p(scene_to_load_or_create_path); + std::ifstream f(p); + if (f.good()) { + pke_editor_scene_load(pkeSettings.rt.nextLevel, p.c_str()); } - ActiveCamera = &NullCamera; - pke_editor_scene_load(editor_mstr.target_scene_path.val); - std::filesystem::path p(editor_mstr.target_scene_path.val); - editor_mstr.active_scene = pke_scene_get_by_name(p.stem().c_str()); - if (editor_mstr.active_scene) { + + if (editor_mstr.target_scene_path.reserved > 0) { editor_mstr.active_scene->file_path = editor_mstr.target_scene_path; + editor_mstr.target_scene_path = {}; } - editor_mstr.target_scene_path = {}; + return; } - if (selectedEntity && pk_arr_find_first_index(&EntitiesToBeRemoved, ECS_GetEntity(selectedEntity->entHandle), ecs_pk_arr_find_first_matching_pointer) != uint32_t(-1)) { + if (selectedEntity && ECS_GetEntity(selectedEntity->entHandle)->isMarkedForRemoval == true) { selectedEntity = nullptr; } @@ -681,7 +663,7 @@ void RecordImGuiEditorWrapper() { * - the goal is not to prevent a specific scene name, * I just want to know if they clicked the "New Scene" button */ - ImGui::BeginDisabled(editor_mstr.active_scene && editor_mstr.active_scene->name == newSceneName); + ImGui::BeginDisabled(editor_mstr.active_scene && (editor_mstr.active_scene->name == newSceneName || editor_mstr.active_scene->file_path.val == nullptr)); if (ImGui::MenuItem("Save")) { shouldSaveProjectFile = true; editor_mstr.shouldSaveScene = true; @@ -1153,7 +1135,7 @@ void RecordImGuiCameras() { cam.type = ActiveCamera->type; cam.view = ActiveCamera->view; cam.isPrimary = false; - pke_scene_register_camera(editor_mstr.active_scene->scene_handle, cam.camHandle); + pke_level_register_camera(pkeSettings.rt.activeLevel, &cam); } static ImGuiTableFlags tableFlags{ @@ -1317,6 +1299,7 @@ void RecordImGuiUITree() { box = pke_ui_box_new_child(selected_ui_box, type); } else { box = pke_ui_box_new_root(type); + pke_level_register_root_ui_box(pkeSettings.rt.activeLevel, box); } box->flags = PKE_UI_BOX_FLAG_POSITION_TYPE_DYNAMIC; box->flags |= PKE_UI_BOX_FLAG_CENTER_BOTH; diff --git a/src/camera.cpp b/src/camera.cpp index 8635211..ca2c11f 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -170,13 +170,6 @@ void PkeCamera_Destroy(CameraHandle cameraHandle) { } ECS_MarkForRemoval(camPtr); - cam.camHandle = CameraHandle_MAX; - cam.type = PkeCameraType_MAX; - cam.view = PkeCameraView_MAX; - cam.stale = PkeCameraStaleFlags_MAX; - cam.phys.instHandle = InstanceHandle_MAX; - cam.phys.target_inst_uuid = pk_uuid_zed; - cam.phys.constraint = CAFE_BABE(btTypedConstraint); } pk_bkt_arr &PkeCamera_GetPkBktArr() { @@ -219,11 +212,21 @@ void PkeCamera_Tick(double delta) { * See the camera serializer for more. */ (void)delta; - bool b; + CompInstance *inst; pk_iter_t<PkeCamera> iter_cam{}; + bool b; b = pk_bkt_arr_iter_begin(&cam_mstr.bktc_cameras, &iter_cam); while (b == true) { - CompInstance *inst = ECS_GetInstance(iter_cam->phys.instHandle); + if (iter_cam->isMarkedForRemoval == true) { + pk_bkt_arr_free_handle(&cam_mstr.bktc_cameras, iter_cam->camHandle); + if (ActiveCamera == iter_cam.data) { + ActiveCamera = &NullCamera; + } + new (iter_cam.data) PkeCamera{}; + b = pk_bkt_arr_iter_increment(&cam_mstr.bktc_cameras, &iter_cam); + continue; + } + inst = ECS_GetInstance(iter_cam->phys.instHandle); assert(inst != nullptr); if (inst->isNeedingUpdated == true) { iter_cam->stale = iter_cam->stale | PKE_CAMERA_STALE_POSROT; diff --git a/src/ecs.cpp b/src/ecs.cpp index cdf125b..dd521ce 100644 --- a/src/ecs.cpp +++ b/src/ecs.cpp @@ -29,19 +29,13 @@ struct ECS { * * Used to build the other "removal" lists. */ -pk_arr_t<Entity_Base *> entitiesMarkedForRemoval; -/* - * Public list of entities that will be removed next tick - * - * Entity or child of entity that had ECS_MarkForRemoval called - */ -pk_arr_t<Entity_Base *> EntitiesToBeRemoved; +pk_arr_t<Entity_Base *> entitiesMarkedForRemoval{}; /* * The entities being removed this tick * * Each of these entities has gone a full tick in the "to be removed" state */ -pk_arr_t<Entity_Base *> entitiesYetToBeRemoved; +pk_arr_t<EntityHandle> entitiesYetToBeRemoved{}; /* * Entities that have more instances registered than their current @@ -55,6 +49,10 @@ bool ecs_pk_arr_find_first_matching_pointer(void *search_ptr, void *list_ptr) { return search_ptr == list_ptr; } +bool ecs_pk_arr_find_first_matching_handle(void *search_ptr, void *list_ptr) { + return *reinterpret_cast<EntityHandle*>(search_ptr) == *reinterpret_cast<EntityHandle*>(list_ptr); +} + void ECS_GetEntity_Inner(EntityHandle entHandle, Entity_Base*& ent) { assert(pk_bkt_arr_handle_validate(&ecs.bc.entityPtrs, entHandle) == PK_BKT_ARR_HANDLE_VALIDATION_VALID); ent = ecs.bc.entityPtrs[entHandle]; @@ -68,7 +66,6 @@ void ECS_Init() { new (&ecs.bc.instances) pk_bkt_arr_t<CompInstance>{ pk_bkt_arr_handle_MAX_constexpr, ecs.bkt, ecs.bkt }; new (&ecs.bc.ev_mgrs) pk_bkt_arr_t<pke_component_event>{ pk_bkt_arr_handle_MAX_constexpr, ecs.bkt, ecs.bkt }; pk_arr_reserve(&entitiesMarkedForRemoval, 16); - pk_arr_reserve(&EntitiesToBeRemoved, 16); pk_arr_reserve(&entitiesYetToBeRemoved, 16); pk_arr_reserve(&EntitiesWithExcessInstances, 16); pk_ev_create_mgr(); @@ -129,18 +126,15 @@ pk_bkt_arr *ECS_GetEntities() { void ECS_Tick_Early(double delta) { // these reserves might happen 1 tick early, but that's fine (void)delta; - bool b; - pk_iter_t<Entity_Base> iter_ent{}; - pk_arr_clear(&EntitiesToBeRemoved); - bool shouldRun = entitiesMarkedForRemoval.next > 0 || EntitiesToBeRemoved.next > 0 || entitiesYetToBeRemoved.next > 0; - pk_arr_reserve(&entitiesYetToBeRemoved, entitiesMarkedForRemoval.reserved); - pk_arr_reserve(&EntitiesToBeRemoved, entitiesMarkedForRemoval.reserved); - memcpy(EntitiesToBeRemoved.data, entitiesMarkedForRemoval.data, sizeof(void *) * entitiesMarkedForRemoval.next); - - pk_arr_clear(&entitiesYetToBeRemoved); + bool b, any; + pk_iter_t<Entity_Base> iter_ent_obj{}; + pk_iter_t<Entity_Base*> iter_ent{}; + pk_iter_t<CompGrBinds> iter_grbinds{}; + pk_iter_t<pke_component_event> iter_comp_ev{}; + pk_iter_t<CompInstance> iter_inst{}; + Entity_Base *parentEnt; + bool shouldRun = entitiesMarkedForRemoval.next > 0 || entitiesYetToBeRemoved.next > 0; if (!shouldRun) return; - EntitiesToBeRemoved.next = entitiesMarkedForRemoval.next; - pk_arr_clear(&entitiesMarkedForRemoval); // this has the potential to be slow as balls // TODO 2025-05-29 JCB PERF @@ -153,24 +147,91 @@ void ECS_Tick_Early(double delta) { // That may or may not have implications about this logic. // Might need to do several passes? - b = pk_bkt_arr_iter_begin(&ecs.bc.entityPtrs, &iter_ent); + pk_arr_reserve(&entitiesYetToBeRemoved, entitiesMarkedForRemoval.reserved); + pk_arr_clear(&entitiesYetToBeRemoved); + + b = pk_arr_iter_begin(&entitiesMarkedForRemoval, &iter_ent); + while (b == true) { + (*iter_ent)->isMarkedForRemoval = true; + pk_arr_append_t(&entitiesYetToBeRemoved, (*iter_ent)->handle); + b = pk_arr_iter_increment(&entitiesMarkedForRemoval, &iter_ent); + } + pk_arr_clear(&entitiesMarkedForRemoval); + + do { + any = false; + b = pk_bkt_arr_iter_begin(&ecs.bc.entityPtrs, &iter_ent); + while (b == true) { + if ((*iter_ent)->isMarkedForRemoval == true) { + b = pk_bkt_arr_iter_increment(&ecs.bc.entityPtrs, &iter_ent); + continue; + } + + if ((*iter_ent)->parentHandle == EntityHandle_MAX) { + b = pk_bkt_arr_iter_increment(&ecs.bc.entityPtrs, &iter_ent); + continue; + } + + parentEnt = ecs.bc.entityPtrs[(*iter_ent)->parentHandle]; + if (pk_arr_find_first_index(&entitiesYetToBeRemoved, &parentEnt->handle, ecs_pk_arr_find_first_matching_handle) != uint32_t(-1)) { + any = true; + (*iter_ent)->isMarkedForRemoval = true; + pk_arr_append_t(&entitiesYetToBeRemoved, (*iter_ent)->handle); + } + + b = pk_bkt_arr_iter_increment(&ecs.bc.entityPtrs, &iter_ent); + } + } while (any == true); + + b = pk_bkt_arr_iter_begin(&ecs.bc.grBinds, &iter_grbinds); while (b == true) { - Entity_Base *parentEnt = nullptr; - if (iter_ent->parentHandle != EntityHandle_MAX) { - parentEnt = ecs.bc.entityPtrs[iter_ent->parentHandle]; + Entity_Base *ent = ecs.bc.entityPtrs[iter_grbinds->entHandle]; + if (ent->isMarkedForRemoval) { + /* + * 2023-09-05 JB note - the Vulkan assets (device memory, buffers, + * pipeline layout, and descriptor set) are unloaded elsewhere, just, + * as they were created elsewhere. + * 2025-09-08 JCB note - reminder that the bindings + */ + pk_bkt_arr_free_handle(&ecs.bc.grBinds, iter_grbinds->grBindsHandle); + new (&(*iter_grbinds)) CompGrBinds{}; } - if (iter_ent->isMarkedForRemoval) { - pk_arr_append_t<Entity_Base*>(&entitiesYetToBeRemoved, iter_ent); - iter_ent->handle = EntityHandle_MAX; - iter_ent->parentHandle = EntityHandle_MAX; - iter_ent->isMarkedForRemoval = false; - } else if (pk_arr_find_first_index(&EntitiesToBeRemoved, iter_ent, ecs_pk_arr_find_first_matching_pointer) != uint32_t(-1)) { - iter_ent->isMarkedForRemoval = true; - } else if (parentEnt != nullptr && pk_arr_find_first_index(&EntitiesToBeRemoved, parentEnt, ecs_pk_arr_find_first_matching_pointer) != uint32_t(-1)) { - iter_ent->isMarkedForRemoval = true; - pk_arr_append_t<Entity_Base*>(&EntitiesToBeRemoved, iter_ent); + b = pk_bkt_arr_iter_increment(&ecs.bc.grBinds, &iter_grbinds); + } + + b = pk_bkt_arr_iter_begin(&ecs.bc.ev_mgrs, &iter_comp_ev); + while (b == true) { + Entity_Base *ent = ecs.bc.entityPtrs[iter_comp_ev->entity_handle]; + if (ent->isMarkedForRemoval) { + pk_ev_destroy_mgr(iter_comp_ev->ev_mgr_id); + pk_bkt_arr_free_handle(&ecs.bc.ev_mgrs, iter_comp_ev->entity_handle); + new (&(*iter_comp_ev)) pke_component_event{}; } - b = pk_bkt_arr_iter_increment(&ecs.bc.entityPtrs, &iter_ent); + b = pk_bkt_arr_iter_increment(&ecs.bc.ev_mgrs, &iter_comp_ev); + } + + b = pk_bkt_arr_iter_begin(&ecs.bc.instances, &iter_inst); + while (b == true) { + Entity_Base *ent = ecs.bc.entityPtrs[iter_inst->entHandle]; + if (ent->isMarkedForRemoval) { + BtDynamicsWorld->removeRigidBody(iter_inst->bt.rigidBody); + pk_delete<btDefaultMotionState>(iter_inst->bt.motionState, MemBkt_Bullet); + pk_delete<btRigidBody>(iter_inst->bt.rigidBody, MemBkt_Bullet); + iter_inst->bt.rigidBody = CAFE_BABE(btRigidBody); + iter_inst->bt.motionState = CAFE_BABE(btDefaultMotionState); + + pk_bkt_arr_free_handle(&ecs.bc.instances, iter_inst->instanceHandle); + new (&(*iter_inst)) CompInstance{}; + } + b = pk_bkt_arr_iter_increment(&ecs.bc.instances, &iter_inst); + } + + b = pk_bkt_arr_iter_begin(&ecs.bc.generics, &iter_ent_obj); + while (b == true) { + if (iter_ent_obj->isMarkedForRemoval == true) { + pk_bkt_arr_free_handle(&ecs.bc.generics, pk_bkt_arr_handle { iter_ent_obj.id.bkt.b, iter_ent_obj.id.bkt.i }); + } + b = pk_bkt_arr_iter_increment(&ecs.bc.generics, &iter_ent_obj); } } @@ -189,24 +250,21 @@ void ECS_Tick(double delta) { bool b; pk_iter_t<CompInstance> iter_inst{}; pk_iter_t<CompGrBinds> iter_grbinds{}; - pk_iter_t<pke_component_event> iter_comp_ev{}; int32_t physicsTickCount = Physics_Tick(delta); uint32_t entityRemovalCount = entitiesYetToBeRemoved.next; if (physicsTickCount == 0 && entityRemovalCount == 0) return; - using InstIterFn = pk_tmpln_1<void, CompInstance *, void *>; - using GrBindsIterFn = pk_tmpln_1<void, CompGrBinds *, void *>; - using CompEvMgrIterFn = pk_tmpln_1<void, pke_component_event *, void *>; - InstIterFn inst_iter_cb{}; - GrBindsIterFn grbinds_iter_cb{}; - CompEvMgrIterFn comp_ev_mgr_iter_cb{}; - pk_arr_t<updateGrBindsAfter> updateGrBinds; updateGrBinds.bkt = pkeSettings.mem_bkt.game_transient; b = pk_bkt_arr_iter_begin(&ecs.bc.instances, &iter_inst); while (b == true) { + Entity_Base *ent = ecs.bc.entityPtrs[iter_inst->entHandle]; + if (ent->isMarkedForRemoval) { + b = pk_bkt_arr_iter_increment(&ecs.bc.instances, &iter_inst); + continue; + } CompInstance &inst = *iter_inst; auto activationState = inst.bt.rigidBody->getActivationState(); if (activationState == ISLAND_SLEEPING || activationState == DISABLE_SIMULATION || activationState == WANTS_DEACTIVATION) { @@ -214,36 +272,6 @@ void ECS_Tick(double delta) { } else { inst.isNeedingUpdated = true; } - Entity_Base *ent = ecs.bc.entityPtrs[inst.entHandle]; - if (entityRemovalCount > 0 && pk_arr_find_first_index(&entitiesYetToBeRemoved, ent, ecs_pk_arr_find_first_matching_pointer) != uint32_t(-1)) { - if (inst.grBindsHandle != GrBindsHandle_MAX) { - uint32_t afterIndex = pk_arr_find_first_index(&updateGrBinds, &inst.grBindsHandle, ecs_pk_arr_find_by_gr_binds_handle); - updateGrBindsAfter *after = nullptr; - if (afterIndex != uint32_t(-1)) { - after = &updateGrBinds[afterIndex]; - } else { - struct updateGrBindsAfter tmp{}; - tmp.grBindsHandle = inst.grBindsHandle; - tmp.count = 0; - pk_arr_append_t(&updateGrBinds, tmp); - after = &updateGrBinds[updateGrBinds.next-1]; - } - after->count += 1; - } - - inst.entHandle = EntityHandle_MAX; - inst.grBindsHandle = GrBindsHandle_MAX; - inst.index = ECS_UNSET_VAL_32; - inst.instanceHandle = InstanceHandle_MAX; - inst.isNeedingUpdated = false; - BtDynamicsWorld->removeRigidBody(inst.bt.rigidBody); - pk_delete<btDefaultMotionState>(inst.bt.motionState, MemBkt_Bullet); - pk_delete<btRigidBody>(inst.bt.rigidBody, MemBkt_Bullet); - inst.bt.rigidBody = CAFE_BABE(btRigidBody); - inst.bt.motionState = CAFE_BABE(btDefaultMotionState); - b = pk_bkt_arr_iter_increment(&ecs.bc.instances, &iter_inst); - continue; - } if (updateGrBinds.next > 0 && inst.instanceHandle != InstanceHandle_MAX) { uint32_t afterIndex = pk_arr_find_first_index(&updateGrBinds, &inst.grBindsHandle, ecs_pk_arr_find_by_gr_binds_handle); if (afterIndex != uint32_t(-1)) { @@ -258,36 +286,18 @@ void ECS_Tick(double delta) { b = pk_bkt_arr_iter_begin(&ecs.bc.grBinds, &iter_grbinds); while ((entityRemovalCount > 0 || updateGrBinds.next > 0) && b == true) { CompGrBinds &grBinds = *iter_grbinds; - uint32_t afterIndex = pk_arr_find_first_index(&updateGrBinds, &grBinds.grBindsHandle, ecs_pk_arr_find_by_gr_binds_handle); Entity_Base *ent = ecs.bc.entityPtrs[grBinds.entHandle]; - if (pk_arr_find_first_index(&entitiesYetToBeRemoved, ent, ecs_pk_arr_find_first_matching_pointer) != uint32_t(-1)) { - grBinds.entHandle = EntityHandle_MAX; - grBinds.grBindsHandle = GrBindsHandle_MAX; - grBinds.vkPipelineLayout = VK_NULL_HANDLE; - grBinds.graphicsPipeline = VK_NULL_HANDLE; - grBinds.collisionCallback = PkeCallback{}; - /* - * 2023-09-05 JB note - the Vulkan assets (device memory, buffers, - * pipeline layout, and descriptor set) are unloaded elsewhere, just, - * as they were created elsewhere. - */ + if (ent->isMarkedForRemoval) { + b = pk_bkt_arr_iter_increment(&ecs.bc.grBinds, &iter_grbinds); + continue; } + uint32_t afterIndex = pk_arr_find_first_index(&updateGrBinds, &grBinds.grBindsHandle, ecs_pk_arr_find_by_gr_binds_handle); if (afterIndex != uint32_t(-1)) { auto &after = updateGrBinds[afterIndex]; grBinds.instanceCounter -= after.count; } b = pk_bkt_arr_iter_increment(&ecs.bc.grBinds, &iter_grbinds); } - - b = pk_bkt_arr_iter_begin(&ecs.bc.ev_mgrs, &iter_comp_ev); - while (entityRemovalCount > 0 && b == true) { - Entity_Base *ent = ecs.bc.entityPtrs[iter_comp_ev->entity_handle]; - if (pk_arr_find_first_index(&entitiesYetToBeRemoved, ent, ecs_pk_arr_find_first_matching_pointer) != uint32_t(-1)) { - pk_ev_destroy_mgr(iter_comp_ev->ev_mgr_id); - pk_bkt_arr_free_handle(&ecs.bc.ev_mgrs, iter_comp_ev->pke_event_handle); - } - b = pk_bkt_arr_iter_increment(&ecs.bc.ev_mgrs, &iter_comp_ev); - } } struct InstanceBufferCopyChunk { @@ -302,17 +312,14 @@ struct InstanceBufferCopy { pk_arr_t<InstanceBufferCopyChunk> chunks; }; void ECS_Tick_Late(double delta) { + (void)delta; bool b; + pk_iter_t<EntityHandle> iter_ent_handle{}; pk_iter_t<CompInstance> iter_inst{}; - // using a pointer here avoids calling the destructor when the object goes out of scope - (void)delta; PKVK_TmpBufferDetails tmpBufferDetails{}; pk_arr_t<InstanceBufferCopy> bufferUpdates; bufferUpdates.bkt = pkeSettings.mem_bkt.game_transient; - using InstIterFn = pk_tmpln_1<void, CompInstance*, void*>; - InstIterFn inst_iter_cb{}; - b = pk_bkt_arr_iter_begin(&ecs.bc.instances, &iter_inst); while (b == true) { CompInstance &inst = *iter_inst; @@ -381,6 +388,12 @@ void ECS_Tick_Late(double delta) { b = pk_bkt_arr_iter_increment(&ecs.bc.instances, &iter_inst); } + b = pk_arr_iter_begin(&entitiesYetToBeRemoved, &iter_ent_handle); + while (b == true) { + pk_bkt_arr_free_handle(&ecs.bc.entityPtrs, *iter_ent_handle); + b = pk_arr_iter_increment(&entitiesYetToBeRemoved, &iter_ent_handle); + } + while (bufferUpdates.next > 0) { InstanceBufferCopy &ibc = bufferUpdates[bufferUpdates.next - 1]; @@ -646,7 +659,6 @@ pk_bkt_arr *ECS_GetEvs() { void ECS_Teardown() { pk_arr_reset(&EntitiesWithExcessInstances); pk_arr_reset(&entitiesYetToBeRemoved); - pk_arr_reset(&EntitiesToBeRemoved); pk_arr_reset(&entitiesMarkedForRemoval); ecs.bc.ev_mgrs.~pk_bkt_arr_t<pke_component_event>(); ecs.bc.instances.~pk_bkt_arr_t<CompInstance>(); diff --git a/src/ecs.hpp b/src/ecs.hpp index 20f7444..edc1c37 100644 --- a/src/ecs.hpp +++ b/src/ecs.hpp @@ -4,7 +4,6 @@ #include "pk.h" #include "components.hpp" -extern pk_arr_t<Entity_Base *> EntitiesToBeRemoved; extern pk_arr_t<Entity_Base *> EntitiesWithExcessInstances; void ECS_Init(); diff --git a/src/entities.cpp b/src/entities.cpp index 8b39cea..1f05b47 100644 --- a/src/entities.cpp +++ b/src/entities.cpp @@ -1267,19 +1267,20 @@ void EntityType_Unload(EntityType &et, CompGrBinds *grBindsArr[1]) { void EntityType_Tick(double delta) { (void)delta; - const uint32_t count = EntitiesToBeRemoved.next; - for (uint32_t i = 0; i < count; ++i) { - auto *entTypePtr = EntityType_FindByEntityHandle(EntitiesToBeRemoved[i]->handle); - if (entTypePtr != nullptr) { - auto &entType = *entTypePtr; - pk_arr_append_t(&EntityTypesToTeardown, {}); - auto &td = EntityTypesToTeardown[EntityTypesToTeardown.next-1]; - td.handle = EntitiesToBeRemoved[i]->handle; + pk_iter_t<EntityType> iter_ent_type; + bool b; + b = pk_bkt_arr_iter_begin(&et_mstr.bc, &iter_ent_type); + while (b == true) { + if (iter_ent_type->isMarkedForRemoval == true) { + EntToTeardown td{}; + td.handle = iter_ent_type->handle; td.ticksToWait = 1; - for (long k = 0; k < entType.detailsCount; ++k) { - td.grBinds[k] = entType.details[k].grBinds; + for (long k = 0; k < iter_ent_type->detailsCount; ++k) { + td.grBinds[k] = iter_ent_type->details[k].grBinds; } + pk_arr_append_t(&EntityTypesToTeardown, td); } + b = pk_bkt_arr_iter_increment(&et_mstr.bc, &iter_ent_type); } } diff --git a/src/game-settings.hpp b/src/game-settings.hpp index 651d827..46f5d38 100644 --- a/src/game-settings.hpp +++ b/src/game-settings.hpp @@ -42,11 +42,11 @@ struct GameSettings { } args; struct runtime { // current level - LevelHandle activeLevel = LevelHandle_MAX; + pke_level *activeLevel = nullptr; // level to start loading - LevelHandle nextLevel = LevelHandle_MAX; + pke_level *nextLevel = nullptr; // level to unload - LevelHandle previousLevel = LevelHandle_MAX; + pke_level *previousLevel = nullptr; bool was_framebuffer_resized = false; } rt; struct stats { diff --git a/src/game.cpp b/src/game.cpp index 0784ce9..ab7d211 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -18,6 +18,7 @@ #include "plugins.hpp" #include "project.hpp" #include "scene.hpp" +#include "serialization.hpp" #include "static-ui.hpp" #include "thread-pool.hpp" #include "window.hpp" @@ -27,6 +28,7 @@ #include <BulletCollision/NarrowPhaseCollision/btRaycastCallback.h> #include <GLFW/glfw3.h> #include <cstring> +#include <fstream> #include <thread> const char *levelName = "demo-level"; @@ -71,10 +73,9 @@ void Game_RecordImGui() { void Game_Tick(double delta) { pk_mem_bucket_reset(pkeSettings.mem_bkt.game_transient); - if (pkeSettings.rt.nextLevel != LevelHandle_MAX) { + if (pkeSettings.rt.nextLevel != nullptr) { // TODO async this - pke_level *lvl = pke_level_get(pkeSettings.rt.activeLevel); - for (uint32_t i = 0; i < lvl->scene_instances.next; ++i) { + for (uint32_t i = 0; i < pkeSettings.rt.nextLevel->scene_instances.next; ++i) { // TODO can't instantiate a scene that hasn't been loaded yet /* struct pke_scene *scene = pke_scene_get_by_handle(lvl->scene_instances[i].scene_handle); @@ -83,17 +84,20 @@ void Game_Tick(double delta) { } */ } + pkeSettings.rt.previousLevel = pkeSettings.rt.activeLevel; pkeSettings.rt.activeLevel = pkeSettings.rt.nextLevel; - pkeSettings.rt.nextLevel = LevelHandle_MAX; + pkeSettings.rt.nextLevel = nullptr; + if (pkeSettings.rt.activeLevel->pke_cb_spinup.func != nullptr) { + pkeSettings.rt.activeLevel->pke_cb_spinup.func(); + } } - if (pkeSettings.rt.previousLevel != LevelHandle_MAX) { - pke_level_remove(pkeSettings.rt.previousLevel); - pkeSettings.rt.previousLevel = LevelHandle_MAX; + if (pkeSettings.rt.previousLevel != nullptr) { + pke_level_teardown(pkeSettings.rt.previousLevel); + pkeSettings.rt.previousLevel = nullptr; } /* - * ECS_Tick_Early() gets called first because it updates the public - * `EntitiesToBeRemoved` for all other ticks to use. + * ECS_Tick_Early() gets called first for GC */ ECS_Tick_Early(delta); @@ -109,6 +113,10 @@ void Game_Tick(double delta) { } } + if (pkeSettings.rt.activeLevel->pke_cb_tick.func != nullptr) { + pkeSettings.rt.activeLevel->pke_cb_tick.func(); + } + PkeCamera_Tick(delta); pke_audio_tick(delta); @@ -153,18 +161,28 @@ void Game_Main(PKEWindowProperties windowProps, const char *executablePath) { // // if we were passed only a scene name, create a faux level. - if (!pkeSettings.args.levelName && pkeSettings.args.sceneName) { + if (!pkeSettings.args.levelName) { // TODO uuids pke_level *lvl = pke_level_create("faux-level", pk_uuid_zed, pk_uuid_zed); - pkeSettings.rt.nextLevel = lvl->levelHandle; + pkeSettings.rt.activeLevel = lvl; + } + + if (!pkeSettings.args.levelName && pkeSettings.args.sceneName) { scene_instance si{}; - pke_scene *scn = pke_scene_get_by_path(pkeSettings.args.sceneName); - if (scn == nullptr) { - fprintf(stdout, "[Game_Main] Did not find scene by name: '%s'\n", pkeSettings.args.sceneName); + pke_scene *scene; + std::ifstream f(pkeSettings.args.sceneName); + if (!f.is_open()) { + fprintf(stdout, "[Game_Main] Did not find scene by name specified in arg: '%s'\n", pkeSettings.args.sceneName); goto GAME_SHUTDOWN; } - si.scene_handle = scn->scene_handle; - pk_arr_append_t(&lvl->scene_instances, si); + srlztn_deserialize_helper *h = pke_deserialize_init(pkeSettings.rt.activeLevel, pkeSettings.mem_bkt.game_transient); + // 2025-09-09 JCB Scenes no longer contain anything so I'm not sure there's a reason to create one here. + // spit-balling here, maybe "scene" files should be assets and not much more. + scene = pke_scene_create(pkeSettings.args.sceneName); + pke_deserialize_scene_from_stream(f, h); + pke_deserialize_teardown(h); + si.scene_handle = scene->scene_handle; + pk_arr_append_t(&pkeSettings.rt.activeLevel->scene_instances, si); } GameTimePoint lastTimePoint = pkeSettings.steadyClock.now(); diff --git a/src/level-types.hpp b/src/level-types.hpp index ce0d060..46cbad2 100644 --- a/src/level-types.hpp +++ b/src/level-types.hpp @@ -1,8 +1,12 @@ #ifndef PKE_LEVEL_TYPES_HPP #define PKE_LEVEL_TYPES_HPP +#include "camera.hpp" #include "components.hpp" +#include "player-input.hpp" +#include "static-ui.hpp" #include "vendor-glm-include.hpp" + #include "pk.h" struct scene_instance { @@ -16,11 +20,17 @@ const uint8_t LEVEL_NAME_MAX_LEN = 16; #define pke_level_name_printf_format "%16s" struct pke_level : public Entity_Base { - char *file_path = nullptr; + pk_cstr file_path = {}; struct pk_membucket *bkt = nullptr; char name[LEVEL_NAME_MAX_LEN] = {'\0'}; LevelHandle levelHandle = LevelHandle_MAX; pk_arr_t<scene_instance> scene_instances; + pk_arr_t<PkeCamera *> cameras; + pk_arr_t<pke_ui_box *> root_ui_boxes; + pk_arr_t<pke_input_action_set_handle> input_handles; + PkeCallback pke_cb_tick = {}; + PkeCallback pke_cb_spinup = {}; + PkeCallback pke_cb_teardown = {}; }; #endif /* PKE_LEVEL_TYPES_HPP */ diff --git a/src/level.cpp b/src/level.cpp index 15517ac..120ef57 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -3,6 +3,7 @@ #include "ecs.hpp" #include "pk.h" +#include "scene.hpp" struct level_mstr { pk_membucket *bkt; @@ -29,15 +30,25 @@ pke_level *pke_level_Get_Inner(LevelHandle handle) { pke_level *pke_level_create(const char *levelName, pk_uuid level_uuid, pk_uuid ev_mgr_uuid) { NULL_CHAR_ARR(safe_name, LEVEL_NAME_MAX_LEN + 1); + pke_level *lvl; + bool valid; size_t len = strlen(levelName); size_t start = len <= (LEVEL_NAME_MAX_LEN - 1) ? 0 : len - (LEVEL_NAME_MAX_LEN - 1); sprintf(safe_name, pke_level_name_printf_format, levelName + start); - pke_level *lvl = pke_level_get_by_name(levelName); - if (lvl != nullptr) { - fprintf(stderr, "[pke_level_Create] Failed to create new level: name already exists."); - return nullptr; + /* 2025-09-05 JCB + * There used to be logic here enforcing names unique level names. + * I'm replacing it with a uuid check that ignores pk_uuid_zed + */ + pk_iter_t<pke_level> it{}; + valid = level_uuid != pk_uuid_zed && pk_bkt_arr_iter_begin(&level_mstr.bc, &it); + while (valid == true) { + if (it->uuid == level_uuid) { + fprintf(stderr, "[pke_level_Create] Failed to create new level: uuid already exists."); + return nullptr; + } + valid = pk_bkt_arr_iter_increment(&level_mstr.bc, &it); } LevelHandle level_handle { pk_bkt_arr_new_handle(&level_mstr.bc) }; @@ -50,6 +61,7 @@ pke_level *pke_level_create(const char *levelName, pk_uuid level_uuid, pk_uuid e new (lvl) pke_level{}; lvl->uuid = level_uuid; ECS_CreateEntity(lvl); + lvl->levelHandle = level_handle; (void)ev_mgr_uuid; // ECS_CreateEvManager(lvl, ev_mgr_uuid); // TODO @@ -86,10 +98,53 @@ struct pk_bkt_arr *pke_level_get_levels() { return &level_mstr.bc; } -void pke_level_remove(LevelHandle handle) { - pke_level *lvl = &level_mstr.bc[handle]; - assert(lvl == nullptr && "Failed to find level to remove by requested LevelHandle"); - ECS_MarkForRemoval(lvl); - pk_mem_bucket_destroy(lvl->bkt); - pk_bkt_arr_free_handle(&level_mstr.bc, handle); +void pke_level_teardown(pke_level *level) { + uint32_t u; + assert(level != nullptr); + if (level->pke_cb_teardown.func != nullptr) { + level->pke_cb_teardown.func(); + } + ECS_MarkForRemoval(level); + for (u = 0; u < level->scene_instances.next; ++u) { + pke_scene_remove(level->scene_instances[u].scene_handle); + } + for (u = 0; u < level->cameras.next; ++u) { + PkeCamera_Destroy(level->cameras[u]->camHandle); + } + for (u = 0; u < level->input_handles.next; ++u) { + pke_input_deactivate_set(level->input_handles[u]); + pke_input_unregister_set(level->input_handles[u]); + } + for (u = 0; u < level->root_ui_boxes.next; ++u) { + ECS_MarkForRemoval(level->root_ui_boxes[u]); + } + if (level->file_path.reserved > 0) { + pk_delete_arr<char>(level->file_path.val, level->file_path.reserved); + } + if (level->bkt != nullptr) { + pk_mem_bucket_destroy(level->bkt); + } + pk_arr_reset(&level->scene_instances); + pk_arr_reset(&level->cameras); + pk_arr_reset(&level->root_ui_boxes); + pk_arr_reset(&level->input_handles); + pk_bkt_arr_free_handle(&level_mstr.bc, level->levelHandle); +} + +void pke_level_register_camera(pke_level *level, PkeCamera *camera) { + assert(level != nullptr); + assert(camera != nullptr); + pk_arr_append_t(&level->cameras, camera); +} + +void pke_level_register_input_action_set(pke_level *level, pke_input_action_set_handle handle) { + assert(level != nullptr); + assert(handle != pke_input_action_set_handle_MAX); + pk_arr_append_t(&level->input_handles, handle); +} + +void pke_level_register_root_ui_box(pke_level *level, pke_ui_box *box) { + assert(level != nullptr); + assert(box != nullptr); + pk_arr_append_t(&level->root_ui_boxes, box); } diff --git a/src/level.hpp b/src/level.hpp index c515eea..e9cb0c5 100644 --- a/src/level.hpp +++ b/src/level.hpp @@ -2,6 +2,7 @@ #define PKE_LEVEL_HPP #include "level-types.hpp" +#include "static-ui.hpp" void pke_level_init(); void pke_level_teardown(); @@ -9,6 +10,11 @@ pke_level *pke_level_create(const char *levelName, pk_uuid level_uuid, pk_uuid e pke_level *pke_level_get(LevelHandle handle); pke_level *pke_level_get_by_name(const char *levelName); struct pk_bkt_arr *pke_level_get_levels(); -void pke_level_remove(LevelHandle handle); + +void pke_level_teardown(pke_level *level); + +void pke_level_register_camera(pke_level *level, PkeCamera *camera); +void pke_level_register_input_action_set(pke_level *level, pke_input_action_set_handle handle); +void pke_level_register_root_ui_box(pke_level *level, pke_ui_box *box); #endif /* PKE_LEVEL_HPP */ diff --git a/src/plugin-types.hpp b/src/plugin-types.hpp index f7a119b..63b097e 100644 --- a/src/plugin-types.hpp +++ b/src/plugin-types.hpp @@ -33,7 +33,7 @@ struct PkeCallback { // the 16 char signature(name) of a function CallbackSignature name = {'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'}; // the address of the function to call - populated on startup - void *func = nullptr; + void (*func)() = nullptr; }; #endif /* PKE_PLUGIN_TYPES_HPP */ diff --git a/src/project.cpp b/src/project.cpp index 463c2c9..62525ef 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -201,7 +201,7 @@ void Proj_DeserializeEntityType(std::istream &stream) { PkePlugin_SetSignatureFunc(&etPtr->createInstanceCallback); } else { etPtr->createInstanceCallback.name[0] = 'd'; - etPtr->createInstanceCallback.func = reinterpret_cast<void *>(EntityType_CreateGenericInstance); + etPtr->createInstanceCallback.func = reinterpret_cast<void(*)()>(EntityType_CreateGenericInstance); } for (int64_t i = 0; i < detailCount; ++i) { diff --git a/src/scene-types.hpp b/src/scene-types.hpp index 9caea6e..8230506 100644 --- a/src/scene-types.hpp +++ b/src/scene-types.hpp @@ -1,10 +1,8 @@ #ifndef PKE_SCENE_TYPES_HPP #define PKE_SCENE_TYPES_HPP -#include "camera.hpp" #include "components.hpp" #include "pk.h" -#include "player-input.hpp" const uint8_t SCENE_NAME_MAX_LEN = 16; #define pke_scene_name_printf_format "%16s" @@ -13,8 +11,6 @@ struct pke_scene : public Entity_Base { pk_str file_path = {}; char name[SCENE_NAME_MAX_LEN] = {'\0'}; SceneHandle scene_handle = SceneHandle_MAX; - pk_arr_t<CameraHandle> cameras; - pk_arr_t<pke_input_action_set_handle> input_handles; }; #endif /* PKE_SCENE_TYPES_HPP */ diff --git a/src/scene.cpp b/src/scene.cpp index bdb7a3a..ecc0fc8 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -2,10 +2,7 @@ #include "scene.hpp" #include "ecs.hpp" #include "pk.h" -#include "serialization.hpp" -#include "game-settings.hpp" -#include <fstream> #include <string.h> #include <filesystem> @@ -28,12 +25,21 @@ void pke_scene_master_teardown() { pke_scene *pke_scene_create(const char *scene_name) { NULL_CHAR_ARR(safe_scene_name, SCENE_NAME_MAX_LEN); size_t offset; + pk_iter_t<pke_scene> it{}; + bool valid; std::filesystem::path p(scene_name); + struct pke_scene *scene = nullptr; sprintf(safe_scene_name, "%.15s", p.stem().c_str()); - struct pke_scene *scene = pke_scene_get_by_name(safe_scene_name); - if (scene != nullptr) { - fprintf(stderr, "[pke_scene_create] failed to create scene: pke_scene::name already in use."); - return nullptr; + /* 2025-09-05 JCB + * There used to be logic here enforcing names unique scene names. + */ + valid = pk_bkt_arr_iter_begin(&scene_mstr.bc, &it); + while (valid == true) { + // TODO add uuid logic here. + if (false) { + fprintf(stderr, "[pke_scene_create] failed to create scene: pke_scene::name already in use."); + } + valid = pk_bkt_arr_iter_increment(&scene_mstr.bc, &it); } SceneHandle scene_handle{ pk_bkt_arr_new_handle(&scene_mstr.bc) }; if (scene_handle == SceneHandle_MAX) { @@ -44,6 +50,9 @@ pke_scene *pke_scene_create(const char *scene_name) { new (scene) pke_scene{}; ECS_CreateEntity(scene); + scene->file_path.val = 0; + scene->file_path.length = 0; + scene->file_path.reserved = 0; scene->scene_handle = scene_handle; offset = (strlen(safe_scene_name) > SCENE_NAME_MAX_LEN ? strlen(safe_scene_name) - SCENE_NAME_MAX_LEN : 0); for (int i = 0; i < SCENE_NAME_MAX_LEN; ++i) { @@ -56,20 +65,6 @@ struct pke_scene *pke_scene_get_by_handle(SceneHandle scene_handle) { return &scene_mstr.bc[scene_handle]; } - // TODO remove me? Need to decide if we always read from files or if we should have a serialized representation of a scene -struct pke_scene *pke_scene_get_by_path(const char *file_path) { - std::ifstream f(file_path); - if (!f.is_open()) { - fprintf(stderr, "[pke_scene_get_by_name] Scene not found in memory nor filesystem: '%s'\n", file_path); - return nullptr; - } - srlztn_deserialize_helper *h = pke_deserialize_init(pkeSettings.mem_bkt.game_transient); - h->scene = pke_scene_create(file_path); - pke_deserialize_scene_from_stream(f, h); - pke_deserialize_teardown(h); - return h->scene; -} - struct pke_scene *pke_scene_get_by_name(const char *scene_name) { assert(scene_name != nullptr); NULL_CHAR_ARR(safe_name, SCENE_NAME_MAX_LEN + 1); @@ -92,40 +87,14 @@ pk_bkt_arr *pke_scene_get_scenes() { } void pke_scene_remove(SceneHandle handle) { - uint32_t i; pke_scene *scn = &scene_mstr.bc[handle]; assert(scn != nullptr && "[pke_scene_remove] Failed to find scene by requested SceneHandle"); ECS_MarkForRemoval(scn); - for (i = 0; i < scn->cameras.next; ++i) { - PkeCamera_Destroy(scn->cameras[i]); - } - for (i = 0; i < scn->input_handles.next; ++i) { - pke_input_deactivate_set(scn->input_handles[i]); - pke_input_unregister_set(scn->input_handles[i]); - } if (scn->file_path.reserved > 0) { pk_delete_arr<char>(scn->file_path.val, scn->file_path.reserved); } scn->file_path = {}; scn->name[0] = '\0'; scn->scene_handle = SceneHandle_MAX; - pk_arr_reset(&scn->cameras); - pk_arr_reset(&scn->input_handles); pk_bkt_arr_free_handle(&scene_mstr.bc, handle); } - -void pke_scene_register_camera(SceneHandle scene_handle, CameraHandle cameraHandle) { - assert(scene_handle != SceneHandle_MAX); - assert(cameraHandle != CameraHandle_MAX); - pke_scene *scene = pke_scene_get_by_handle(scene_handle); - assert(scene != nullptr && "Failed to find scene by requested SceneHandle"); - pk_arr_append(&scene->cameras, &cameraHandle); -} - -void pke_scene_register_input_action_set(SceneHandle scene_handle, pke_input_action_set_handle handle) { - assert(scene_handle != SceneHandle_MAX); - assert(handle != pke_input_action_set_handle_MAX); - pke_scene *scene = pke_scene_get_by_handle(scene_handle); - assert(scene != nullptr && "Failed to find scene by requested SceneHandle"); - pk_arr_append_t(&scene->input_handles, handle); -} diff --git a/src/scene.hpp b/src/scene.hpp index 0a1e9ce..70b5f06 100644 --- a/src/scene.hpp +++ b/src/scene.hpp @@ -3,7 +3,6 @@ #include "pk.h" #include "scene-types.hpp" -#include "camera.hpp" void pke_scene_master_init(); void pke_scene_master_teardown(); @@ -11,11 +10,7 @@ void pke_scene_master_teardown(); struct pke_scene *pke_scene_create(const char *scene_name); struct pke_scene *pke_scene_get_by_handle(SceneHandle scene_handle); struct pke_scene *pke_scene_get_by_name(const char *scene_name); -struct pke_scene *pke_scene_get_by_path(const char *file_path); pk_bkt_arr *pke_scene_get_scenes(); void pke_scene_remove(SceneHandle handle); -void pke_scene_register_camera(SceneHandle scene_handle, CameraHandle cameraHandle); -void pke_scene_register_input_action_set(SceneHandle scene_handle, pke_input_action_set_handle handle); - #endif /* PKE_SCENE_HPP */ diff --git a/src/serialization-camera.cpp b/src/serialization-camera.cpp index c955a9f..bc3c716 100644 --- a/src/serialization-camera.cpp +++ b/src/serialization-camera.cpp @@ -3,17 +3,17 @@ #include "camera.hpp" #include "compile-time-assert.hpp" -#include "pk.h" -#include "serialization-component.hpp" #include "ecs.hpp" +#include "level.hpp" #include "math-helpers.hpp" -#include "scene.hpp" - -#include <BulletCollision/CollisionShapes/btCollisionShape.h> - +#include "serialization-component.hpp" #include "serialization.hpp" #include "vendor-glm-include.hpp" +#include "pk.h" + +#include <BulletCollision/CollisionShapes/btCollisionShape.h> + pk_handle pke_serialize_camera(srlztn_serialize_helper *h, const PkeCamera *cam) { assert(h != nullptr); assert(cam != nullptr); @@ -184,7 +184,7 @@ void pke_deserialize_camera(srlztn_deserialize_helper *h, pke_kve_container *kve rCam.type = cam.type; rCam.view = cam.view; rCam.isPrimary = cam.isPrimary; - pke_scene_register_camera(h->scene->scene_handle, rCam.camHandle); + pke_level_register_camera(h->level, &rCam); if (targetInstanceIndex != uint32_t(-1)) { rCam.phys.target_inst_handle = h->mapping[targetInstanceIndex].created_instance->instanceHandle; PkeCamera_TargetInstance(rCam.camHandle, h->mapping[targetInstanceIndex].created_instance); diff --git a/src/serialization-component.cpp b/src/serialization-component.cpp index 266eb2d..a401d50 100644 --- a/src/serialization-component.cpp +++ b/src/serialization-component.cpp @@ -320,10 +320,11 @@ void pke_deserialize_instance(srlztn_deserialize_helper *h, pke_kve_container *k // entity = reinterpret_cast<CreateInst*>(et_ptr->createInstanceCallback.func)(); fprintf(stderr, "[%s] Attempted to call EntityType::createInstanceCallback and we have not yet defined a valid function signature\n", __FILE__); } else { - map.created_entity = EntityType_CreateGenericInstance(et_ptr, h->scene, &comp, &inst_pos); + map.created_entity = EntityType_CreateGenericInstance(et_ptr, h->level, &comp, &inst_pos); } } else { map.created_entity = ECS_CreateGenericEntity(); + ECS_CreateEntity(map.created_entity); map.created_instance = ECS_CreateInstance(map.created_entity, comp.uuid, nullptr, &inst_pos); } ECS_GetInstances(map.created_entity, instances); diff --git a/src/serialization-font.cpp b/src/serialization-font.cpp index 0ca0a94..bd7341d 100644 --- a/src/serialization-font.cpp +++ b/src/serialization-font.cpp @@ -2,7 +2,6 @@ #include "serialization-font.hpp" #include "compile-time-assert.hpp" -#include "ecs.hpp" #include "font.hpp" #include "pk.h" #include "serialization.hpp" diff --git a/src/serialization-input.cpp b/src/serialization-input.cpp index c4c5cc3..d8a2f86 100644 --- a/src/serialization-input.cpp +++ b/src/serialization-input.cpp @@ -2,8 +2,10 @@ #include "serialization-input.hpp" #include "compile-time-assert.hpp" +#include "game-settings.hpp" +#include "level.hpp" + #include "pk.h" -#include "scene.hpp" #include <cstring> @@ -200,7 +202,9 @@ void pke_deserialize_input_set(srlztn_deserialize_helper *h, pke_kve_container * } pke_input_action_set_handle action_set_handle = pke_input_register_set(set); - pke_scene_register_input_action_set(h->scene->scene_handle, action_set_handle); + if (pkeSettings.rt.activeLevel != nullptr) { + pke_level_register_input_action_set(pkeSettings.rt.activeLevel, action_set_handle); + } if (PK_HAS_FLAG(set.flags, PKE_INPUT_ACTION_SET_FLAG_AUTO_ENABLE)) { pke_input_activate_set(action_set_handle); } diff --git a/src/serialization-static-ui.cpp b/src/serialization-static-ui.cpp index 1fa7cfd..67c0c1e 100644 --- a/src/serialization-static-ui.cpp +++ b/src/serialization-static-ui.cpp @@ -1,9 +1,9 @@ #include "serialization-static-ui.hpp" -#include "asset-manager.hpp" #include "compile-time-assert.hpp" #include "ecs.hpp" #include "font.hpp" +#include "level.hpp" #include "pk.h" #include "serialization.hpp" #include "static-ui.hpp" @@ -482,6 +482,7 @@ void pke_deserialize_ui_box(srlztn_deserialize_helper *h, pke_kve_container *kve pke_ui_box *box; if (parent_box == nullptr) { box = pke_ui_box_new_root(bx.type, bx.uuid); + pke_level_register_root_ui_box(h->level, box); } else { box = pke_ui_box_new_child(parent_box, bx.type, bx.uuid); } diff --git a/src/serialization.cpp b/src/serialization.cpp index 96dd5b5..63123fe 100644 --- a/src/serialization.cpp +++ b/src/serialization.cpp @@ -30,13 +30,14 @@ srlztn_serialize_helper *pke_serialize_init(pk_membucket *bkt) { return helper; } -srlztn_deserialize_helper *pke_deserialize_init(pk_membucket *bkt) { +srlztn_deserialize_helper *pke_deserialize_init(pke_level *level, pk_membucket *bkt) { srlztn_deserialize_helper *helper = pk_new<srlztn_deserialize_helper>(bkt); helper->bkt = bkt; helper->kvp_containers = {}; helper->kvp_containers.bkt = bkt; helper->mapping = {}; helper->mapping.bkt = bkt; + helper->level = level; pk_arr_reserve(&helper->kvp_containers, 1); pk_arr_reserve(&helper->mapping, 1); return helper; diff --git a/src/serialization.hpp b/src/serialization.hpp index 4d95d61..fd8ebb8 100644 --- a/src/serialization.hpp +++ b/src/serialization.hpp @@ -2,9 +2,9 @@ #define PKE_SERIALIZATION_HPP #include "kve.hpp" +#include "level-types.hpp" #include "pk.h" #include "components.hpp" -#include "scene-types.hpp" #define iccsc inline const char* const @@ -99,7 +99,7 @@ struct srlztn_serialize_helper { struct srlztn_deserialize_helper { pk_membucket *bkt; - pke_scene *scene; + pke_level *level; pk_arr_t<srlztn_ecs_mapping> mapping; pk_arr_t<pke_kve_container> kvp_containers; }; @@ -108,7 +108,7 @@ bool srlztn_mapping_find_first_handle_by_uuid(void *handle, void *mapping); bool srlztn_kvec_find_first_by_handle(void *handle, void *container); srlztn_serialize_helper *pke_serialize_init(pk_membucket *bkt); -srlztn_deserialize_helper *pke_deserialize_init(pk_membucket *bkt); +srlztn_deserialize_helper *pke_deserialize_init(pke_level *level, pk_membucket *bkt); void pke_serialize_teardown(srlztn_serialize_helper *helper); void pke_deserialize_teardown(srlztn_deserialize_helper *helper); diff --git a/src/static-ui.cpp b/src/static-ui.cpp index f80c000..b4bdc5c 100644 --- a/src/static-ui.cpp +++ b/src/static-ui.cpp @@ -575,17 +575,15 @@ void pke_ui_tick(double delta) { // This currently only calls tear-down for boxes on the root list. // Leaving this as-is for the time being because you can control the // visibility of child boxes with flags and don't need to completely remove them. - for (u = 0; u < EntitiesToBeRemoved.next; ++u) { - if (EntitiesToBeRemoved[u] == box) { - pke_ui_teardown_box_recursive(box); - for (ii = 0; i + ii + 1 < pke_ui_master.r_root_boxes; ++ii) { - pke_ui_master.root_boxes[i + ii] = pke_ui_master.root_boxes[i + ii + 1]; - } - pke_ui_master.h_root_boxes -= 1; - i -= 1; - pke_ui_master.should_update_buffer = true; - goto skip_calc; + if (box->isMarkedForRemoval == true) { + pke_ui_teardown_box_recursive(box); + for (ii = 0; i + ii + 1 < pke_ui_master.r_root_boxes; ++ii) { + pke_ui_master.root_boxes[i + ii] = pke_ui_master.root_boxes[i + ii + 1]; } + pke_ui_master.h_root_boxes -= 1; + i -= 1; + pke_ui_master.should_update_buffer = true; + goto skip_calc; } pke_ui_calc_px(arr, tmp_txtr_arr, nullptr, box); pke_ui_recalc_sizes_recursive(arr, tmp_txtr_arr, box); diff --git a/tests/pke-test-dummy.cpp b/tests/pke-test-dummy.cpp index efd1ff7..e8c34ba 100644 --- a/tests/pke-test-dummy.cpp +++ b/tests/pke-test-dummy.cpp @@ -6,6 +6,7 @@ #include "ecs.hpp" #include "physics.hpp" #include "pk.h" +#include "player-input.hpp" #include "scene.hpp" #include "static-ui.hpp" #include "thread-pool.hpp" diff --git a/tests/pke-test-serialization.cpp b/tests/pke-test-serialization.cpp index bf491f5..374d77e 100644 --- a/tests/pke-test-serialization.cpp +++ b/tests/pke-test-serialization.cpp @@ -6,6 +6,7 @@ #include "game-settings.hpp" #include "camera.hpp" #include "ecs.hpp" +#include "level.hpp" #include "physics.hpp" #include "pk.h" #include "scene.hpp" @@ -18,8 +19,8 @@ #include <sstream> pk_membucket *bkt = nullptr; -const char *test_scene_name = "srlztn_test_scene"; -pke_scene *test_scene = nullptr; +const char *test_level_name = "srlztn_test_level"; +pke_level *test_level = nullptr; FontType *ft; #define FAKE_UUID_GEN(oct) { .uuid = { oct,oct,oct,oct,oct,oct,oct,oct,oct,oct,oct,oct,oct,oct,oct,oct } } @@ -53,29 +54,27 @@ void pke_test_serialization_spinup() { AM_Init(); ECS_Init(); Physics_Init(); + pke_level_init(); PkeCamera_Init(); pke_scene_master_init(); pke_input_init(); pke_ui_init(); FontType_Init(); - test_scene = pke_scene_create(test_scene_name); - // FontTypeIndex fti; - // FontType *ft = FontType_GetFonts(fti); - // assert(fti > FontTypeIndex{0}); - // ft->uuid = uuid_17; + test_level = pke_level_create(test_level_name, pk_uuid_zed, pk_uuid_zed); // pk_funcinstr_teardown(); }; void pke_test_serialization_teardown() { // pk_funcinstr_init(); - // pke_scene_remove(test_scene->scene_handle); // TODO this doesn't work? - test_scene = nullptr; + pke_level_teardown(test_level); + test_level = nullptr; FontType_Teardown(); pke_ui_teardown(); pke_input_teardown(); pke_scene_master_teardown(); PkeCamera_Teardown(); Physics_Teardown(); + pke_level_teardown(); ECS_Teardown(); AM_Teardown(); PkeThreads_Teardown(); @@ -186,7 +185,7 @@ int pke_test_deserialization_101() { std::stringstream ss(test_001_str); try { bkt = pk_mem_bucket_create("pke_test_serialization", PK_MEM_DEFAULT_BUCKET_SIZE, PK_MEMBUCKET_FLAG_NONE); - h = pke_deserialize_init(bkt); + h = pke_deserialize_init(test_level, bkt); pke_deserialize_scene_from_stream(ss, h); @@ -329,8 +328,7 @@ int pke_test_deserialization_102() { std::stringstream ss(test_002_str); try { bkt = pk_mem_bucket_create("pke_test_serialization", PK_MEM_DEFAULT_BUCKET_SIZE, PK_MEMBUCKET_FLAG_NONE); - h = pke_deserialize_init(bkt); - h->scene = test_scene; + h = pke_deserialize_init(test_level, bkt); pke_deserialize_scene_from_stream(ss, h); @@ -508,8 +506,7 @@ int pke_test_deserialization_103() { std::stringstream ss(test_003_str); try { bkt = pk_mem_bucket_create("pke_test_serialization", PK_MEM_DEFAULT_BUCKET_SIZE, PK_MEMBUCKET_FLAG_NONE); - h = pke_deserialize_init(bkt); - h->scene = test_scene; + h = pke_deserialize_init(test_level, bkt); pke_deserialize_scene_from_stream(ss, h); @@ -688,7 +685,7 @@ int pke_test_deserialization_104() { std::stringstream ss(test_004_str); try { bkt = pk_mem_bucket_create("pke_test_serialization", PK_MEM_DEFAULT_BUCKET_SIZE, PK_MEMBUCKET_FLAG_NONE); - h = pke_deserialize_init(bkt); + h = pke_deserialize_init(test_level, bkt); pke_deserialize_scene_from_stream(ss, h); |
