summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-03-21 20:54:14 -0400
committerJonathan Bradley <jcb@pikum.xyz>2025-03-21 21:40:21 -0400
commit677a3cbec2f7908ee0897b97d508a6bd66a0a344 (patch)
treefc88b21dd61dbb10dd8b5c8aef73702d15514f00
parentcae76dd98e301a4560bb46ecb59b5952dff04149 (diff)
pke: first-pass level is a collection of scenes
-rw-r--r--Makefile2
-rw-r--r--editor/editor.cpp110
-rw-r--r--src/arg-handler.cpp6
-rw-r--r--src/bucketed-array.hpp10
-rw-r--r--src/components.hpp4
-rw-r--r--src/ecs.cpp4
-rw-r--r--src/entities.cpp3
-rw-r--r--src/game-settings.hpp4
-rw-r--r--src/game.cpp57
-rw-r--r--src/game.hpp3
-rw-r--r--src/level-types.hpp19
-rw-r--r--src/level.cpp109
-rw-r--r--src/level.hpp11
-rw-r--r--src/scene-types.hpp18
-rw-r--r--src/scene.cpp96
-rw-r--r--src/scene.hpp20
16 files changed, 354 insertions, 122 deletions
diff --git a/Makefile b/Makefile
index 1e00955..6334b18 100644
--- a/Makefile
+++ b/Makefile
@@ -182,6 +182,7 @@ $(DIR_BIN)/libpke.a: $(DIR_OBJ)/player-input.o
$(DIR_BIN)/libpke.a: $(DIR_OBJ)/plugins.o
$(DIR_BIN)/libpke.a: $(DIR_OBJ)/project-settings.o
$(DIR_BIN)/libpke.a: $(DIR_OBJ)/project.o
+$(DIR_BIN)/libpke.a: $(DIR_OBJ)/scene.o
$(DIR_BIN)/libpke.a: $(DIR_OBJ)/static-cube.o
$(DIR_BIN)/libpke.a: $(DIR_OBJ)/static-plane.o
$(DIR_BIN)/libpke.a: $(DIR_OBJ)/static-ui.o
@@ -215,6 +216,7 @@ $(DIR_DBG)/libpke.a: $(DIR_OBJ)/player-input.so
$(DIR_DBG)/libpke.a: $(DIR_OBJ)/plugins.so
$(DIR_DBG)/libpke.a: $(DIR_OBJ)/project-settings.so
$(DIR_DBG)/libpke.a: $(DIR_OBJ)/project.so
+$(DIR_DBG)/libpke.a: $(DIR_OBJ)/scene.so
$(DIR_DBG)/libpke.a: $(DIR_OBJ)/static-cube.so
$(DIR_DBG)/libpke.a: $(DIR_OBJ)/static-plane.so
$(DIR_DBG)/libpke.a: $(DIR_OBJ)/static-ui.so
diff --git a/editor/editor.cpp b/editor/editor.cpp
index 685d402..2de4772 100644
--- a/editor/editor.cpp
+++ b/editor/editor.cpp
@@ -17,6 +17,7 @@
#include "player-input.hpp"
#include "plugins.hpp"
#include "project.hpp"
+#include "scene.hpp"
#include "static-ui.hpp"
#include "thread-pool.hpp"
#include "vendor-glm-include.hpp"
@@ -53,6 +54,14 @@ const char* const dbgCtrl_ClearSelection = "debug-clear-selection";
const char* const dbgCtrl_DeleteSelectedItem = "debug-delete-selected-item";
const char* const dbgCtrl_ImGui_Toggle = "debug-imgui-toggle";
+// TODO editor state (scene vs level)
+struct editor_master {
+ pke_scene *active_scene;
+ pk_str target_scene_name;
+ bool shouldLoadScene = false;
+ bool shouldSaveScene = false;
+} editor_mstr;
+
ThreadPoolHandle threadPoolHandle = ThreadPoolHandle_MAX;
InputActionSetHandle debugControlsHandle = InputActionSetHandle_MAX;
@@ -104,7 +113,7 @@ void PkeEditor_Tick(double delta) {
"pke-runtime",
"--plugin", pkeSettings.args.pluginPath == nullptr ? "libpke-example.o" : pkeSettings.args.pluginPath,
"--project", pkeSettings.args.projectPath == nullptr ? PKE_PROJ_DEFAULT_FILENAME : pkeSettings.args.projectPath,
- "--scene", pkeSettings.rt.sceneName == nullptr ? PKE_PROJ_DEFAULT_FILENAME : pkeSettings.rt.sceneName,
+ "--scene", (editor_mstr.active_scene != nullptr && editor_mstr.active_scene->name[0] != '\0') ? editor_mstr.active_scene->name : PKE_PROJ_DEFAULT_FILENAME ,
NULL,
};
status = execvp("pke-runtime", const_cast<char **>(argv));
@@ -130,12 +139,12 @@ void PkeEditor_Tick(double delta) {
}
if (shouldOpenNewScene) {
shouldOpenNewScene = false;
- // queues unloading
- pkeSettings.rt.previousLevel = pkeSettings.rt.activeLevel;
- // bypasses loading a level by setting a new active one
- pkeSettings.rt.activeLevel = PkeLevel_Create("editorLevel");
- pkeSettings.rt.sceneName = newSceneName;
+ if (editor_mstr.active_scene != nullptr) {
+ pke_scene_remove(editor_mstr.active_scene->scene_handle);
+ }
ActiveCamera = &NullCamera;
+ editor_mstr.active_scene = pke_scene_create(newSceneName);
+ return;
}
if (shouldSaveProjectFile) {
shouldSaveProjectFile = false;
@@ -147,33 +156,52 @@ void PkeEditor_Tick(double delta) {
new (task) std::packaged_task<void()>( [] {
const char * patterns[1] = {"*.pstf"};
char *selectedScene = tinyfd_openFileDialog(nullptr, "cafebabe.pstf", 1, patterns, "Pke Scene Text File", 0);
- if (selectedScene != nullptr) {
- pkeSettings.rt.sceneName = selectedScene;
- pkeSettings.rt.shouldLoadScene = true;
- ActiveCamera = &NullCamera;
+ 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;
auto *task = pk_new<std::packaged_task<void()>>();
new (task) std::packaged_task<void()>( [] {
const char * patterns[1] = {"*.pstf"};
- char *selectedScene = tinyfd_saveFileDialog(nullptr, pkeSettings.rt.sceneName, 1, patterns, "Pke Scene Text File");
+ char *selectedScene = tinyfd_saveFileDialog(nullptr, editor_mstr.active_scene->file_path.val, 1, patterns, "Pke Scene Text File");
if (selectedScene != nullptr) {
- pkeSettings.rt.sceneName = selectedScene;
- pkeSettings.rt.shouldSaveScene = true;
+ if (editor_mstr.active_scene->file_path.reserved > 0) {
+ pk_delete<char>(editor_mstr.active_scene->file_path.val, editor_mstr.active_scene->file_path.reserved);
+ }
+ editor_mstr.active_scene->file_path.length = strlen(selectedScene);
+ editor_mstr.active_scene->file_path.reserved = editor_mstr.active_scene->file_path.length + 1;
+ editor_mstr.active_scene->file_path.val = pk_new<char>(editor_mstr.active_scene->file_path.reserved);
+ strcpy(editor_mstr.active_scene->file_path.val, selectedScene);
+ editor_mstr.shouldSaveScene = true;
}
});
PkeThreads_Enqueue(threadPoolHandle, task);
}
- if (pkeSettings.rt.shouldSaveScene && pkeSettings.rt.sceneName) {
- pkeSettings.rt.shouldSaveScene = false;
- Game_SaveSceneFile(pkeSettings.rt.sceneName);
+ if (editor_mstr.shouldSaveScene && editor_mstr.active_scene->file_path.val) {
+ editor_mstr.shouldSaveScene = false;
+ Game_SaveSceneFile(editor_mstr.active_scene->file_path.val);
shouldRebuildProjectDir = true;
}
+ if (editor_mstr.target_scene_name.val != nullptr) {
+ if (editor_mstr.active_scene != nullptr) {
+ pke_scene_remove(editor_mstr.active_scene->scene_handle);
+ }
+ ActiveCamera = &NullCamera;
+ Game_LoadSceneFile(editor_mstr.target_scene_name.val);
+ editor_mstr.active_scene = pke_scene_get_by_name(editor_mstr.target_scene_name.val);
+ pk_delete<char>(editor_mstr.target_scene_name.val, editor_mstr.target_scene_name.reserved);
+ editor_mstr.target_scene_name = {};
+ return;
+ }
if (selectedEntity && EntitiesToBeRemoved.Has(ECS_GetEntity(selectedEntity->entHandle))) {
selectedEntity = nullptr;
@@ -608,26 +636,28 @@ 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(pkeSettings.rt.sceneName == newSceneName);
+ ImGui::BeginDisabled(editor_mstr.active_scene && editor_mstr.active_scene->name == newSceneName);
if (ImGui::MenuItem("Save")) {
shouldSaveProjectFile = true;
- pkeSettings.rt.shouldSaveScene = true;
+ editor_mstr.shouldSaveScene = true;
}
- if (pkeSettings.rt.sceneName) {
+ if (editor_mstr.active_scene != nullptr) {
ImGui::SameLine();
int offset = 0;
- const auto *slash = strrchr(pkeSettings.rt.sceneName, '\\');
- slash = slash != nullptr ? slash : strrchr(pkeSettings.rt.sceneName, '/');
+ const auto *slash = strrchr(editor_mstr.active_scene->name, '\\');
+ slash = slash != nullptr ? slash : strrchr(editor_mstr.active_scene->name, '/');
if (slash != nullptr) {
- offset = slash - pkeSettings.rt.sceneName;
+ offset = slash - editor_mstr.active_scene->name;
}
- ImGui::Text("%s", pkeSettings.rt.sceneName + offset);
+ ImGui::Text("%s", editor_mstr.active_scene->name + offset);
}
ImGui::EndDisabled();
+
if (ImGui::MenuItem("Save As...")) {
shouldSaveProjectFile = true;
shouldOpenSaveSceneDialog = true;
}
+
if (ImGui::MenuItem("Exit")) {
glfwSetWindowShouldClose(window, true);
}
@@ -1075,7 +1105,7 @@ void RecordImGuiCameras() {
cam.type = ActiveCamera->type;
cam.view = ActiveCamera->view;
cam.isPrimary = false;
- PkeLevel_RegisterCamera(pkeSettings.rt.activeLevel, cam.camHandle);
+ pke_scene_register_camera(editor_mstr.active_scene->scene_handle, cam.camHandle);
}
static ImGuiTableFlags tableFlags{
@@ -1337,18 +1367,20 @@ void RecordImGuiLevels() {
if (ImGui::BeginTable("PkeLevels", 3, tableFlags)) {
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Handle");
- ImGui::TableSetupColumn("CameraCount");
ImGui::TableHeadersRow();
- for (long i = 0; i < MAX_LEVEL_COUNT; ++i) {
- if (LEVELS[i].levelHandle == LevelHandle_MAX)
- continue;
- ImGui::TableNextRow();
- ImGui::TableSetColumnIndex(0);
- ImGui::Text("%s", LEVELS[i].name);
- ImGui::TableSetColumnIndex(1);
- ImGui::Text("0x%04hx", static_cast<unsigned short>(LEVELS[i].levelHandle));
- ImGui::TableSetColumnIndex(2);
- ImGui::Text("%u", LEVELS[i].cameras.next - 1);
+ pk_handle_bucket_index_T b, bb;
+ pk_handle_item_index_T i, ii;
+ PkeLevel *lvls;
+ bb = pke_level_get_bucket_count();
+ for (b = 0; b < bb; ++b) {
+ lvls = pke_level_get_levels(b, &ii);
+ for (i = 0; i < ii; ++i) {
+ ImGui::TableNextRow();
+ ImGui::TableSetColumnIndex(0);
+ ImGui::Text("%s", lvls[i].name);
+ ImGui::TableSetColumnIndex(1);
+ ImGui::Text("0x%08X 0x%08X", b, i);
+ }
}
ImGui::EndTable();
}
@@ -1574,8 +1606,11 @@ void BuildDirRecursive(const std::filesystem::directory_entry &de, fsEntry *dirF
void BuildProjectMenuRecursive(fsEntry &entry) {
if (entry.type == 1) {
if (ImGui::Selectable(entry.name, false, ImGuiSelectableFlags_AllowDoubleClick) && ImGui::IsMouseDoubleClicked(0)) {
- pkeSettings.rt.sceneName = entry.name;
- pkeSettings.rt.shouldLoadScene = true;
+ assert(editor_mstr.target_scene_name.val == nullptr);
+ editor_mstr.target_scene_name.length = strlen(entry.name);
+ editor_mstr.target_scene_name.reserved = editor_mstr.target_scene_name.length + 1;
+ editor_mstr.target_scene_name.val = pk_new<char>(editor_mstr.target_scene_name.reserved);
+ strcpy(editor_mstr.target_scene_name.val, entry.name);
ActiveCamera = &NullCamera;
}
} else if (entry.type == 0) {
@@ -1626,6 +1661,7 @@ void RecordImGuiSceneBrowser() {
for (i = 0; i < bucketCount; ++i) {
instances = ECS_GetInstances(0, itemCount);
for (k = 0; k < itemCount; ++k) {
+ if (instances[k].instanceHandle == InstanceHandle_MAX) continue;
entType = EntityType_FindByEntityHandle(instances[k].entHandle);
sprintf(text, "%s: %08x %08x", entType != nullptr ? entType->entityTypeCode.val : "(no type)", instances[k].instanceHandle.bucketIndex, instances[k].instanceHandle.itemIndex);
if (ImGui::Button(text)) {
diff --git a/src/arg-handler.cpp b/src/arg-handler.cpp
index 24695fe..f192584 100644
--- a/src/arg-handler.cpp
+++ b/src/arg-handler.cpp
@@ -9,6 +9,7 @@ void PkeArgs_Parse(int argc, char *argv[]) {
while (1) {
static struct option long_options[] = {
+ {"level", required_argument, 0, 'l'},
{"plugin", required_argument, 0, 'p'},
{"project", required_argument, 0, 'r'},
{"scene", required_argument, 0, 's'},
@@ -24,6 +25,9 @@ void PkeArgs_Parse(int argc, char *argv[]) {
switch (c) {
case 0:
break;
+ case 'l':
+ pkeSettings.args.levelName = optarg;
+ break;
case 'p':
pkeSettings.args.pluginPath = optarg;
break;
@@ -32,8 +36,6 @@ void PkeArgs_Parse(int argc, char *argv[]) {
break;
case 's':
pkeSettings.args.sceneName = optarg;
- pkeSettings.rt.shouldLoadScene = true;
- pkeSettings.rt.sceneName = pkeSettings.args.sceneName;
break;
default:
fprintf(stderr, "Unused parameter: %c\n", c);
diff --git a/src/bucketed-array.hpp b/src/bucketed-array.hpp
index fb24712..150fa66 100644
--- a/src/bucketed-array.hpp
+++ b/src/bucketed-array.hpp
@@ -14,9 +14,19 @@ struct BucketContainer {
CT limits;
CT pkeHandle;
T* buckets[BKT_CNT];
+ T& operator[](CT) const;
};
template<typename T, typename CT, pk_handle_item_index_T BKT_CNT>
+T&
+BucketContainer<T, CT, BKT_CNT>::operator[](CT handle) const {
+ assert(handle.bucketIndex < this->limits.bucketIndex);
+ assert(handle.itemIndex < this->limits.itemIndex);
+ assert(handle.bucketIndex != this->pkeHandle.bucketIndex || handle.itemIndex < this->pkeHandle.itemIndex);
+ return this->buckets[handle.bucketIndex][handle.itemIndex];
+}
+
+template<typename T, typename CT, pk_handle_item_index_T BKT_CNT>
void Buckets_Init(BucketContainer<T, CT, BKT_CNT> &bktContainer, size_t maxItemCount = BucketContainerDefaultItemCount) {
bktContainer.limits.bucketIndex = BKT_CNT;
bktContainer.limits.itemIndex = maxItemCount;
diff --git a/src/components.hpp b/src/components.hpp
index b587fcc..326b0e6 100644
--- a/src/components.hpp
+++ b/src/components.hpp
@@ -16,10 +16,14 @@ const uint32_t ECS_UNSET_VAL_32 = 0xFFFFFFFF;
struct EntityHandle : public pk_handle { };
struct GrBindsHandle : public pk_handle { };
struct InstanceHandle : public pk_handle { };
+struct SceneHandle : public pk_handle { };
+struct LevelHandle : public pk_handle { };
constexpr EntityHandle EntityHandle_MAX = EntityHandle{ pk_handle_MAX_constexpr };
constexpr GrBindsHandle GrBindsHandle_MAX = GrBindsHandle{ pk_handle_MAX_constexpr };
constexpr InstanceHandle InstanceHandle_MAX = InstanceHandle{ pk_handle_MAX_constexpr };
+constexpr SceneHandle SceneHandle_MAX = SceneHandle{ pk_handle_MAX_constexpr };
+constexpr LevelHandle LevelHandle_MAX = LevelHandle{ pk_handle_MAX_constexpr };
struct Entity_Base {
EntityHandle handle = EntityHandle_MAX;
diff --git a/src/ecs.cpp b/src/ecs.cpp
index 3ab8a80..4174bd0 100644
--- a/src/ecs.cpp
+++ b/src/ecs.cpp
@@ -83,8 +83,8 @@ EntityHandle ECS_CreateEntity(Entity_Base *entity, Entity_Base *parentEntity) {
}
Entity_Base *ECS_GetEntity(EntityHandle handle) {
- pk_handle_validate(handle, ecs.bc.entityPtrs.pkeHandle, ecs.bc.entityPtrs.limits.itemIndex);
- return ecs.bc.entityPtrs.buckets[handle.bucketIndex][handle.itemIndex];
+ if (pk_handle_validate(handle, ecs.bc.entityPtrs.pkeHandle, ecs.bc.entityPtrs.limits.itemIndex) != PK_HANDLE_VALIDATION_VALID) return nullptr;
+ return ecs.bc.entityPtrs[handle];
}
void ECS_MarkForRemoval(Entity_Base *entity) {
diff --git a/src/entities.cpp b/src/entities.cpp
index 1abe632..2ec6c60 100644
--- a/src/entities.cpp
+++ b/src/entities.cpp
@@ -108,6 +108,9 @@ EntityType *EntityType_FindByTypeCode(const char *typeCode) {
EntityType *EntityType_FindByEntityHandle_Inner(EntityHandle handle) {
if (handle == EntityHandle_MAX) return nullptr;
+ if (handle.bucketIndex > EntityType_BC.limits.bucketIndex) return nullptr;
+ if (handle.itemIndex > EntityType_BC.limits.itemIndex) return nullptr;
+ if (handle.bucketIndex == EntityType_BC.pkeHandle.bucketIndex && handle.itemIndex >= EntityType_BC.pkeHandle.bucketIndex) return nullptr;
for (pk_handle_bucket_index_T b = 0; b <= EntityType_BC.pkeHandle.bucketIndex; ++b) {
auto &bkt = EntityType_BC.buckets[b];
long itemCount = EntityType_BC.pkeHandle.bucketIndex == b ? EntityType_BC.pkeHandle.itemIndex : EntityType_BC.limits.itemIndex;
diff --git a/src/game-settings.hpp b/src/game-settings.hpp
index 65f4242..ba078b9 100644
--- a/src/game-settings.hpp
+++ b/src/game-settings.hpp
@@ -34,6 +34,7 @@ struct GameSettings {
struct pk_membucket *bkt = nullptr;
} mem;
struct engineArgs {
+ const char *levelName = nullptr;
const char *pluginPath = nullptr;
const char *projectPath = nullptr;
const char *sceneName = nullptr;
@@ -45,9 +46,6 @@ struct GameSettings {
LevelHandle nextLevel = LevelHandle_MAX;
// level to unload
LevelHandle previousLevel = LevelHandle_MAX;
- const char *sceneName = nullptr;
- bool shouldLoadScene = false;
- bool shouldSaveScene = false;
bool was_framebuffer_resized = false;
} rt;
};
diff --git a/src/game.cpp b/src/game.cpp
index 3c39018..2edea8c 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -17,6 +17,7 @@
#include "player-input.hpp"
#include "plugins.hpp"
#include "project.hpp"
+#include "scene.hpp"
#include "static-ui.hpp"
#include "thread-pool.hpp"
#include "window.hpp"
@@ -162,7 +163,7 @@ bool FindFirstInstanceHandle(void *handle, void *mapping) {
InstMapping *inst_mapping = reinterpret_cast<InstMapping *>(mapping);
return inst_mapping->origHandle == *reinterpret_cast<InstanceHandle *>(handle);
}
-void DeserializeCamera(PkeLevel *level, std::istream &stream) {
+void DeserializeCamera(pke_scene *scene, std::istream &stream) {
PkeCamera cam{};
InstanceHandle instanceHandle = InstanceHandle_MAX;
InstanceHandle targetInstanceHandle = InstanceHandle_MAX;
@@ -191,7 +192,7 @@ void DeserializeCamera(PkeLevel *level, std::istream &stream) {
rCam.view = cam.view;
rCam.isPrimary = cam.isPrimary;
rCam.phys.targetInstHandle = targetInstanceHandle;
- PkeLevel_RegisterCamera(level->levelHandle, rCam.camHandle);
+ pke_scene_register_camera(scene->scene_handle, rCam.camHandle);
if (targetInstanceIndex > -1) {
PkeCamera_TargetInstance(rCam.camHandle, ECS_GetInstance(loadFileInstanceMappings[targetInstanceIndex].newInstHandle));
}
@@ -476,21 +477,27 @@ void Game_SaveSceneFile(const char *sceneFilePath) {
}
}
-void Game_LoadSceneFile(PkeLevel *level, const char *sceneFilePath) {
+void Game_LoadSceneFile(const char *sceneFilePath) {
std::ifstream f(sceneFilePath);
if (!f.is_open()) {
- fprintf(stderr, "Failed to load requested scene file: %s\n", sceneFilePath);
+ fprintf(stderr, "Failed to load requested scene file: '%s'\n", sceneFilePath);
return;
}
+ // TODO scene name is in the file?
+ pke_scene *scn = pke_scene_get_by_name(sceneFilePath);
+ if (scn == nullptr) {
+ scn = pke_scene_create(sceneFilePath);
+ }
+
memset(readLine, '\0', readLineLength);
while (f.getline(readLine, readLineLength)) {
if (strcmp(PKE_FILE_OBJ_INSTANCE, readLine) == 0) {
- DeserializeInstance(level, f);
+ DeserializeInstance(scn, f);
continue;
}
if (strcmp(PKE_FILE_OBJ_CAMERA, readLine) == 0) {
- DeserializeCamera(level, f);
+ DeserializeCamera(scn, f);
continue;
}
}
@@ -539,17 +546,18 @@ void Game_RecordImGui() {
void Game_Tick(double delta) {
pk_bucket_reset(pkeSettings.mem.bkt);
- // TODO this should be removed in favor of storing the scene details inside a level definition
- if (pkeSettings.rt.shouldLoadScene && pkeSettings.rt.sceneName) {
- pkeSettings.rt.shouldLoadScene = false;
- if (pkeSettings.rt.activeLevel != LevelHandle_MAX) {
- pkeSettings.rt.previousLevel = pkeSettings.rt.activeLevel;
- }
- pkeSettings.rt.nextLevel = PkeLevel_Create(pkeSettings.rt.sceneName);
- }
if (pkeSettings.rt.nextLevel != LevelHandle_MAX) {
// TODO async this
- Game_LoadSceneFile(PkeLevel_Get(pkeSettings.rt.nextLevel), pkeSettings.rt.sceneName);
+ PkeLevel *lvl = PkeLevel_Get(pkeSettings.rt.activeLevel);
+ for (uint32_t i = 0; i < lvl->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);
+ if (scene != nullptr) {
+ Game_LoadSceneFile(scene->name);
+ }
+ */
+ }
pkeSettings.rt.activeLevel = pkeSettings.rt.nextLevel;
pkeSettings.rt.nextLevel = LevelHandle_MAX;
}
@@ -593,6 +601,7 @@ void Game_Main(PKEWindowProperties windowProps, const char *executablePath) {
Physics_Init();
PkeCamera_Init();
PkeLevel_Init();
+ pke_scene_master_init();
Game_Init();
CreateWindow(windowProps);
EntityType_Init();
@@ -610,6 +619,20 @@ void Game_Main(PKEWindowProperties windowProps, const char *executablePath) {
}
}
+ //
+ // at this point, everything is loaded or initialized.
+ //
+
+ // if we were passed only a scene name, create a faux level.
+ if (!pkeSettings.args.levelName && pkeSettings.args.sceneName) {
+ pkeSettings.rt.nextLevel = PkeLevel_Create("faux-level")->levelHandle;
+ PkeLevel *lvl = PkeLevel_Get(pkeSettings.rt.nextLevel);
+ scene_instance si{};
+ si.scene_handle = pke_scene_get_by_name(pkeSettings.args.sceneName)->scene_handle;
+ pk_arr_append_t(&lvl->scene_instances, si);
+ }
+
+ // TODO remove me: temp stuff for testing
pk_cstr test_text = cstring_to_pk_cstr("012\n3456789\tThe quick\r\nbrown fox jumped over the lazy dog.");
// pk_cstr test_text = cstring_to_pk_cstr("%+-*0123456789$");
// pk_cstr test_text = cstring_to_pk_cstr("$#");
@@ -634,6 +657,7 @@ void Game_Main(PKEWindowProperties windowProps, const char *executablePath) {
FontType_AddStringRender(FontTypeIndex{1}, pk_cstr_to_pk_str(&test_text2), &fr_set);
}
+ // TODO remove me: temp stuff for testing
pke_ui_box *ui_box = pke_ui_box_new_root();
ui_box->flags = PKE_UI_BOX_FLAG_POSITION_TYPE_DYNAMIC;
ui_box->pos_top_left_x = 0.1;
@@ -641,6 +665,7 @@ void Game_Main(PKEWindowProperties windowProps, const char *executablePath) {
ui_box->max_width = 0.8;
ui_box->max_height = 0.8;
+ // TODO remove me: temp stuff for testing
pke_ui_box *c_ui_box = pke_ui_box_new_child(ui_box);
c_ui_box->flags = PKE_UI_BOX_FLAG_POSITION_TYPE_STATIC;
c_ui_box->pos_top_left_x = 20;
@@ -747,6 +772,8 @@ void Game_Main(PKEWindowProperties windowProps, const char *executablePath) {
FontType_Teardown();
pke_ui_teardown();
PkeInput_Teardown();
+ pke_scene_master_teardown();
+ PkeLevel_Teardown();
PkeCamera_Teardown();
Physics_Teardown();
ECS_Teardown();
diff --git a/src/game.hpp b/src/game.hpp
index 95d010c..8476ff0 100644
--- a/src/game.hpp
+++ b/src/game.hpp
@@ -1,7 +1,6 @@
#ifndef PKE_GAME_HPP
#define PKE_GAME_HPP
-#include "level-types.hpp"
#include "window-types.hpp"
void Game_Main(PKEWindowProperties windowProps, const char *executablePath);
@@ -10,6 +9,6 @@ void Game_Tick(double delta);
void Game_Teardown();
void Game_RecordImGui();
void Game_SaveSceneFile(const char *);
-void Game_LoadSceneFile(PkeLevel *level, const char *);
+void Game_LoadSceneFile(const char *);
#endif /* PKE_GAME_HPP */
diff --git a/src/level-types.hpp b/src/level-types.hpp
index 4de0f76..75bc6eb 100644
--- a/src/level-types.hpp
+++ b/src/level-types.hpp
@@ -1,19 +1,26 @@
#ifndef PKE_LEVEL_TYPES_HPP
#define PKE_LEVEL_TYPES_HPP
-#include "pk.h"
-#include "camera.hpp"
#include "components.hpp"
+#include "vendor-glm-include.hpp"
+#include "pk.h"
-TypeSafeInt_constexpr(LevelHandle, uint16_t, 0xFFFF);
+struct scene_instance {
+ glm::vec3 pos; // TODO
+ glm::vec3 rot; // TODO
+ glm::vec3 scale; // TODO
+ SceneHandle scene_handle;
+};
-struct LvlCamArr : public pk_arr_t<CameraHandle>{};
+const uint8_t LEVEL_NAME_MAX_LEN = 16;
+#define pke_level_name_printf_format "%16s"
struct PkeLevel : public Entity_Base {
+ char *file_path = nullptr;
struct pk_membucket *bkt = nullptr;
- char name[16] = {'\0'};
+ char name[LEVEL_NAME_MAX_LEN] = {'\0'};
LevelHandle levelHandle = LevelHandle_MAX;
- LvlCamArr cameras;
+ pk_arr_t<scene_instance> scene_instances;
};
#endif /* PKE_LEVEL_TYPES_HPP */
diff --git a/src/level.cpp b/src/level.cpp
index 047363b..57faf80 100644
--- a/src/level.cpp
+++ b/src/level.cpp
@@ -1,87 +1,98 @@
#include "level.hpp"
-#include "camera.hpp"
+#include "bucketed-array.hpp"
#include "ecs.hpp"
#include "pk.h"
-LevelHandle nextHandle = LevelHandle{0};
-long levelCount = 0;
-constexpr long LEVEL_NAME_LENGTH = 16;
-PkeLevel LEVELS[MAX_LEVEL_COUNT];
+struct level_mstr {
+ BucketContainer<PkeLevel, LevelHandle> bc;
+} level_mstr;
+void PkeLevel_Init() {
+ Buckets_Init(level_mstr.bc);
+}
+
+void PkeLevel_Teardown() {
+ Buckets_Destroy(level_mstr.bc);
+}
+
+/*
PkeLevel *PkeLevel_Get_Inner(LevelHandle handle) {
- for (long i = 0; i < MAX_LEVEL_COUNT; ++i) {
- if (LEVELS[i].levelHandle == handle) {
- return &LEVELS[i];
- }
+ if ( || handle.itemIndex >= level_mstr.bc.limits.itemIndex || (handle.bucketIndex == handle.bucketIndex) {
}
- return nullptr;
+ return &level_mstr.bc.buckets[handle.bucketIndex][handle.itemIndex];
}
+*/
-void PkeLevel_Init() {
- for (long i = 0; i < MAX_LEVEL_COUNT; ++i) {
- new (&LEVELS[i]) PkeLevel{};
- pk_arr_reset(&LEVELS[i].cameras);
+PkeLevel *PkeLevel_Create(const char *levelName) {
+ NULL_CHAR_ARR(safe_name, LEVEL_NAME_MAX_LEN + 1);
+
+ 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);
+
+ PkeLevel *lvl = PkeLevel_GetByName(levelName);
+ if (lvl != nullptr) {
+ fprintf(stderr, "[PkeLevel_Create] Failed to create new level: name already exists.");
+ return nullptr;
}
-}
-LevelHandle PkeLevel_Create(const char *levelName) {
- assert(levelCount < MAX_LEVEL_COUNT && "only MAX_LEVEL_COUNT levels can be loaded at once");
- levelCount += 1;
+ LevelHandle level_handle = Buckets_NewHandle(level_mstr.bc);
+ if (level_handle == LevelHandle_MAX) {
+ fprintf(stderr, "[PkeLevel_Create] Failed to create new level handle from BucketContainer");
+ return nullptr;
+ }
- PkeLevel *lvl = PkeLevel_Get_Inner(LevelHandle_MAX);
- assert(lvl != nullptr && "max level count not reached, but failed to find a valid level slot");
+ lvl = &level_mstr.bc[level_handle];
ECS_CreateEntity(lvl);
if (lvl->bkt == nullptr) {
lvl->bkt = pk_bucket_create(levelName, PK_DEFAULT_BUCKET_SIZE, false);
}
- lvl->levelHandle = nextHandle;
- ++nextHandle;
- auto len = strlen(levelName);
- auto start = len <= (LEVEL_NAME_LENGTH - 1) ? 0 : len - (LEVEL_NAME_LENGTH - 1);
- strncpy(lvl->name, levelName + start, LEVEL_NAME_LENGTH);
- return lvl->levelHandle;
+ return lvl;
}
PkeLevel *PkeLevel_Get(LevelHandle handle) {
- return PkeLevel_Get_Inner(handle);
+ return &level_mstr.bc[handle];
}
-LevelHandle PkeLevel_GetHandle(const char *levelName) {
- char safeName[LEVEL_NAME_LENGTH];
+PkeLevel *PkeLevel_GetByName(const char *levelName) {
+ NULL_CHAR_ARR(safe_name, LEVEL_NAME_MAX_LEN + 1);
auto len = strlen(levelName);
- auto start = len <= (LEVEL_NAME_LENGTH - 1) ? 0 : len - (LEVEL_NAME_LENGTH - 1);
- strncpy(safeName, levelName + start, LEVEL_NAME_LENGTH);
- for (long i = 0; i < MAX_LEVEL_COUNT; ++i) {
- if (LEVELS[i].levelHandle != LevelHandle_MAX) {
- if (strcmp(safeName, LEVELS[i].name)) {
- return LEVELS[i].levelHandle;
+ auto start = len <= (LEVEL_NAME_MAX_LEN - 1) ? 0 : len - (LEVEL_NAME_MAX_LEN - 1);
+ sprintf(safe_name, pke_level_name_printf_format, levelName + start);
+
+ pk_handle_bucket_index_T b;
+ pk_handle_item_index_T i, ii;
+ for (b = 0; b < level_mstr.bc.pkeHandle.bucketIndex; ++b) {
+ ii = level_mstr.bc.pkeHandle.bucketIndex == b ? level_mstr.bc.pkeHandle.itemIndex : level_mstr.bc.limits.itemIndex;
+ for (i = 0; i < ii; ++i) {
+ if (memcmp(safe_name, level_mstr.bc.buckets[b][i].name, LEVEL_NAME_MAX_LEN) == 0) {
+ return &level_mstr.bc.buckets[b][i];
}
}
}
- return LevelHandle_MAX;
+ return nullptr;
+}
+
+pk_handle_bucket_index_T pke_level_get_bucket_count() {
+ return level_mstr.bc.pkeHandle.bucketIndex + 1;
}
-void PkeLevel_RegisterCamera(LevelHandle levelHandle, CameraHandle cameraHandle) {
- assert(levelHandle != LevelHandle_MAX);
- assert(cameraHandle != CameraHandle_MAX);
- PkeLevel *lvl = PkeLevel_Get_Inner(levelHandle);
- assert(lvl != nullptr && "Failed to find level by requested LevelHandle");
- pk_arr_append(&lvl->cameras, &cameraHandle);
+struct PkeLevel *pke_level_get_levels(pk_handle_bucket_index_T bucket_index, pk_handle_item_index_T *item_count) {
+ assert(bucket_index <= level_mstr.bc.pkeHandle.bucketIndex);
+ assert(item_count != nullptr);
+ *item_count = bucket_index == level_mstr.bc.pkeHandle.bucketIndex ? level_mstr.bc.pkeHandle.itemIndex : level_mstr.bc.limits.itemIndex;
+ return level_mstr.bc.buckets[bucket_index];
}
void PkeLevel_Remove(LevelHandle handle) {
- PkeLevel *lvl = PkeLevel_Get_Inner(handle);
- assert(lvl != nullptr && "Failed to find level to remove by requested LevelHandle");
- levelCount -= 1;
+ PkeLevel *lvl = &level_mstr.bc[handle];
+ assert(lvl == nullptr && "Failed to find level to remove by requested LevelHandle");
+ // TODO mark bucket slot as open
ECS_MarkForRemoval(lvl);
- for (long i = 0; i < lvl->cameras.next; ++i) {
- PkeCamera_Destroy(lvl->cameras[i]);
- }
- pk_arr_reset(&lvl->cameras);
pk_bucket_reset(lvl->bkt);
lvl->levelHandle = LevelHandle_MAX;
}
diff --git a/src/level.hpp b/src/level.hpp
index 58cdcd3..1a07914 100644
--- a/src/level.hpp
+++ b/src/level.hpp
@@ -3,14 +3,13 @@
#include "level-types.hpp"
-constexpr long MAX_LEVEL_COUNT = 16;
-extern PkeLevel LEVELS[];
-
void PkeLevel_Init();
-LevelHandle PkeLevel_Create(const char *levelName);
+void PkeLevel_Teardown();
+PkeLevel *PkeLevel_Create(const char *levelName);
PkeLevel *PkeLevel_Get(LevelHandle handle);
-LevelHandle PkeLevel_GetHandle(const char *levelName);
-void PkeLevel_RegisterCamera(LevelHandle levelHandle, CameraHandle cameraHandle);
+PkeLevel *PkeLevel_GetByName(const char *levelName);
+pk_handle_bucket_index_T pke_level_get_bucket_count();
+struct PkeLevel *pke_level_get_levels(pk_handle_bucket_index_T bucket_index, pk_handle_item_index_T *item_count);
void PkeLevel_Remove(LevelHandle handle);
#endif /* PKE_LEVEL_HPP */
diff --git a/src/scene-types.hpp b/src/scene-types.hpp
new file mode 100644
index 0000000..655c621
--- /dev/null
+++ b/src/scene-types.hpp
@@ -0,0 +1,18 @@
+#ifndef PKE_SCENE_TYPES_HPP
+#define PKE_SCENE_TYPES_HPP
+
+#include "pk.h"
+#include "components.hpp"
+#include "camera.hpp"
+
+const uint8_t SCENE_NAME_MAX_LEN = 16;
+#define pke_scene_name_printf_format "%16s"
+
+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;
+};
+
+#endif /* PKE_SCENE_TYPES_HPP */
diff --git a/src/scene.cpp b/src/scene.cpp
new file mode 100644
index 0000000..0e5a22c
--- /dev/null
+++ b/src/scene.cpp
@@ -0,0 +1,96 @@
+
+#include "scene.hpp"
+#include "bucketed-array.hpp"
+#include "ecs.hpp"
+#include <cstring>
+
+struct pke_scene_master {
+ BucketContainer<pke_scene, SceneHandle> bc;
+} scene_mstr;
+
+void pke_scene_master_init() {
+ Buckets_Init(scene_mstr.bc);
+}
+
+void pke_scene_master_teardown() {
+ Buckets_Destroy(scene_mstr.bc);
+}
+
+pke_scene *pke_scene_create(const char *scene_name) {
+ struct pke_scene *scene = pke_scene_get_by_name(scene_name);
+ if (scene != nullptr) {
+ fprintf(stderr, "[pke_scene_create] failed to create scene: pke_scene::name already in use.");
+ return nullptr;
+ }
+ SceneHandle scene_handle = Buckets_NewHandle(scene_mstr.bc);
+ if (scene_handle == SceneHandle_MAX) {
+ fprintf(stderr, "[pke_scene_create] failed to get new scene handle from BucketContainer.");
+ return nullptr;
+ }
+ scene = &scene_mstr.bc.buckets[scene_handle.bucketIndex][scene_handle.itemIndex];
+ ECS_CreateEntity(scene);
+
+ scene->scene_handle = scene_handle;
+ size_t offset = (strlen(scene_name) > SCENE_NAME_MAX_LEN ? strlen(scene_name) - SCENE_NAME_MAX_LEN : 0);
+ for (int i = 0; i < SCENE_NAME_MAX_LEN; ++i) {
+ scene->name[i] = scene_name[i + offset];
+ }
+ return scene;
+}
+
+struct pke_scene *pke_scene_get_by_handle(SceneHandle scene_handle) {
+ return &scene_mstr.bc.buckets[scene_handle.bucketIndex][scene_handle.itemIndex];
+}
+
+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);
+ strncpy(safe_name, scene_name, SCENE_NAME_MAX_LEN + 1);
+ pk_handle_bucket_index_T b;
+ pk_handle_item_index_T i, ii;
+ for (b = 0; b < scene_mstr.bc.pkeHandle.bucketIndex + 1; ++b) {
+ ii = scene_mstr.bc.pkeHandle.bucketIndex == b ? scene_mstr.bc.pkeHandle.itemIndex : scene_mstr.bc.limits.itemIndex;
+ for (i = 0; i < ii; ++i) {
+ if (memcmp(safe_name, scene_mstr.bc.buckets[b][i].name, PK_MIN(strlen(safe_name), SCENE_NAME_MAX_LEN)) == 0) {
+ return &scene_mstr.bc.buckets[b][i];
+ }
+ }
+ }
+ return nullptr;
+}
+
+pk_handle_bucket_index_T pke_scene_get_bucket_count() {
+ return scene_mstr.bc.pkeHandle.bucketIndex + 1;
+}
+
+struct pke_scene *pke_scene_get_scenes(pk_handle_bucket_index_T bucket_index, pk_handle_item_index_T *item_count) {
+ assert(bucket_index <= scene_mstr.bc.pkeHandle.bucketIndex);
+ assert(item_count != nullptr);
+ *item_count = bucket_index == scene_mstr.bc.pkeHandle.bucketIndex ? scene_mstr.bc.pkeHandle.itemIndex : scene_mstr.bc.limits.itemIndex;
+ return scene_mstr.bc.buckets[bucket_index];
+}
+
+void pke_scene_remove(SceneHandle handle) {
+ pke_scene *scn = pke_scene_get_by_handle(handle);
+ assert(scn != nullptr && "[pke_scene_remove] Failed to find scene by requested SceneHandle");
+ // TODO mark bucket slot as open
+ ECS_MarkForRemoval(scn);
+ for (long i = 0; i < scn->cameras.next; ++i) {
+ PkeCamera_Destroy(scn->cameras[i]);
+ }
+ if (scn->file_path.reserved > 0) {
+ pk_delete<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);
+}
+
+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);
+}
diff --git a/src/scene.hpp b/src/scene.hpp
new file mode 100644
index 0000000..46d406c
--- /dev/null
+++ b/src/scene.hpp
@@ -0,0 +1,20 @@
+#ifndef PKE_SCENE_HPP
+#define PKE_SCENE_HPP
+
+#include "pk.h"
+#include "scene-types.hpp"
+#include "camera.hpp"
+
+void pke_scene_master_init();
+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);
+pk_handle_bucket_index_T pke_scene_get_bucket_count();
+struct pke_scene *pke_scene_get_scenes(pk_handle_bucket_index_T bucket_index, pk_handle_item_index_T *item_count);
+void pke_scene_remove(SceneHandle handle);
+
+void pke_scene_register_camera(SceneHandle scene_handle, CameraHandle cameraHandle);
+
+#endif /* PKE_SCENE_HPP */