summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-09-09 16:05:14 -0400
committerJonathan Bradley <jcb@pikum.xyz>2025-09-09 16:05:14 -0400
commit5daa12fed0449a7811fc25ec62236bc060a56fa7 (patch)
tree5af5ff1c4de2ab49dd4d1db9934b3b4ee34a54c2
parentc40277cd665e04d300ea839a1dd2ff675655f5fb (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.cpp10
-rw-r--r--editor/editor-io.hpp4
-rw-r--r--editor/editor-types.cpp2
-rw-r--r--editor/editor-types.hpp4
-rw-r--r--editor/editor.cpp85
-rw-r--r--src/camera.cpp21
-rw-r--r--src/ecs.cpp214
-rw-r--r--src/ecs.hpp1
-rw-r--r--src/entities.cpp21
-rw-r--r--src/game-settings.hpp6
-rw-r--r--src/game.cpp50
-rw-r--r--src/level-types.hpp12
-rw-r--r--src/level.cpp75
-rw-r--r--src/level.hpp8
-rw-r--r--src/plugin-types.hpp2
-rw-r--r--src/project.cpp2
-rw-r--r--src/scene-types.hpp4
-rw-r--r--src/scene.cpp63
-rw-r--r--src/scene.hpp5
-rw-r--r--src/serialization-camera.cpp14
-rw-r--r--src/serialization-component.cpp3
-rw-r--r--src/serialization-font.cpp1
-rw-r--r--src/serialization-input.cpp8
-rw-r--r--src/serialization-static-ui.cpp3
-rw-r--r--src/serialization.cpp3
-rw-r--r--src/serialization.hpp6
-rw-r--r--src/static-ui.cpp18
-rw-r--r--tests/pke-test-dummy.cpp1
-rw-r--r--tests/pke-test-serialization.cpp27
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);