summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-04-02 20:46:41 -0400
committerJonathan Bradley <jcb@pikum.xyz>2025-04-02 20:46:41 -0400
commita02c7589c6c9e902c59a632aa650635336fe648c (patch)
tree8ec4800ff9f9effbd9141e02131123448679bc93 /src
parentfad302f7db146a78900f9b21dbbcd97761093c1b (diff)
pke: checkpoint: major serialization refactor
Diffstat (limited to 'src')
-rw-r--r--src/game.cpp537
-rw-r--r--src/game.hpp2
-rw-r--r--src/project-settings.hpp2
-rw-r--r--src/project.cpp3
-rw-r--r--src/project.hpp2
-rw-r--r--src/serialization-camera.cpp153
-rw-r--r--src/serialization-camera.hpp11
-rw-r--r--src/serialization-component.cpp256
-rw-r--r--src/serialization-component.hpp15
-rw-r--r--src/serialization.cpp102
-rw-r--r--src/serialization.hpp71
11 files changed, 615 insertions, 539 deletions
diff --git a/src/game.cpp b/src/game.cpp
index f7b539b..5e5e1b0 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -2,6 +2,7 @@
#include "game.hpp"
#include "camera.hpp"
+#include "serialization-component.hpp"
#include "components.hpp"
#include "ecs.hpp"
#include "entities.hpp"
@@ -13,11 +14,13 @@
#include "imgui.h"
#include "level-types.hpp"
#include "level.hpp"
+#include "math-helpers.hpp"
#include "physics.hpp"
#include "player-input.hpp"
#include "plugins.hpp"
#include "project.hpp"
#include "scene.hpp"
+#include "serialization.hpp"
#include "static-ui.hpp"
#include "thread-pool.hpp"
#include "window.hpp"
@@ -27,544 +30,10 @@
#include <BulletCollision/NarrowPhaseCollision/btRaycastCallback.h>
#include <GLFW/glfw3.h>
#include <cstring>
-#include <iomanip>
-#include <ostream>
-#include <fstream>
#include <thread>
-const long readLineLength = 128;
-char readLine[readLineLength];
-struct InstMapping {
- InstanceHandle origHandle = InstanceHandle_MAX;
- EntityHandle newEntHandle;
- InstanceHandle newInstHandle;
- InstPos newInstance{};
-};
-pk_arr_t<InstMapping> loadFileInstanceMappings{};
-
const char *levelName = "demo-level";
-const char *PKE_FILE_BEGIN = ":PKFB:";
-const char *PKE_FILE_END = ":PKFE:";
-const char *PKE_FILE_VERSION = ":0:";
-const char *PKE_FILE_OBJ_END = "";
-const char *PKE_FILE_OBJ_INSTANCE = "Instance:";
-const char *PKE_FILE_OBJ_CAMERA = "Camera:";
-
-const char *PKE_FILE_INSTANCE_HANDLE = "Inst::InstHandle: ";
-const char *PKE_FILE_INSTANCE_ENTITY_HANDLE = "EntityHandle: ";
-const char *PKE_FILE_INSTANCE_ENTITY_TYPE_CODE = "EntityTypeCode: ";
-const char *PKE_FILE_INSTANCE_UUID = "UUID: ";
-const char *PKE_FILE_INSTANCE_POS_POS = "InstPos::Pos: ";
-const char *PKE_FILE_INSTANCE_POS_ROT = "InstPos::Rot: ";
-const char *PKE_FILE_INSTANCE_POS_SCALE = "InstPos::Scale: ";
-const char *PKE_FILE_INSTANCE_PHYSICS_MASS = "InstPos::Mass: ";
-const char *PKE_FILE_INSTANCE_PHYSICS_COLLISION_LAYER = "InstPos::CollisionLayer: ";
-const char *PKE_FILE_INSTANCE_PHYSICS_COLLISION_MASK = "InstPos::CollisionMask: ";
-const char *PKE_FILE_INSTANCE_COLLISION_CALLBACK_SIGNATURE = "Inst::CollisionCallbackSignature: ";
-
-const char PKE_FILE_INSTANCE_SPECIAL_ENTITY_TYPE_CODE_CAMERA = 'C';
-
-const char *PKE_FILE_CAMERA_POS = "Cam::Pos: ";
-const char *PKE_FILE_CAMERA_ROT = "Cam::Rot: ";
-const char *PKE_FILE_CAMERA_TARGET = "Cam::Target: ";
-const char *PKE_FILE_CAMERA_TYPE = "Cam::Type: ";
-const char *PKE_FILE_CAMERA_UUID = "Cam::UUID: ";
-const char *PKE_FILE_CAMERA_ORIENTATION = "Cam::Orientation: ";
-const char *PKE_FILE_CAMERA_INSTANCE_HANDLE = "Cam::InstanceHandle: ";
-const char *PKE_FILE_CAMERA_TARGET_INSTANCE_HANDLE = "Cam::TargetInstanceHandle: ";
-const char *PKE_FILE_CAMERA_IS_PRIMARY = "Cam::IsPrimary: ";
-
-void SerializeCamera(std::ostream &stream, const PkeCamera &cam) {
- NULL_CHAR_ARR(handleStr, 23);
- PkeCamera c{};
- if (cam.uuid != pk_uuid_zed && cam.uuid != pk_uuid_max) {
- stream << PKE_FILE_CAMERA_UUID << cam.uuid << std::endl;
- }
- if (cam.type != c.type) {
- stream << PKE_FILE_CAMERA_TYPE << int(static_cast<PkeCameraType_T>(cam.type)) << std::endl;
- }
- if (cam.view != c.view) {
- stream << PKE_FILE_CAMERA_ORIENTATION << int(static_cast<PkeCameraView_T>(cam.view)) << std::endl;
- }
- if (cam.phys.instHandle != InstanceHandle_MAX) {
- snprintf(handleStr, 22, "0x%08X 0x%08X", cam.phys.instHandle.bucketIndex, cam.phys.instHandle.itemIndex);
- stream << PKE_FILE_CAMERA_INSTANCE_HANDLE << handleStr << std::endl;
- }
- if (cam.phys.targetInstHandle != InstanceHandle_MAX) {
- snprintf(handleStr, 22, "0x%08X 0x%08X", cam.phys.targetInstHandle.bucketIndex, cam.phys.targetInstHandle.itemIndex);
- stream << PKE_FILE_CAMERA_TARGET_INSTANCE_HANDLE << handleStr << std::endl;
- }
- if (cam.isPrimary != c.isPrimary) {
- stream << PKE_FILE_CAMERA_IS_PRIMARY << cam.isPrimary << std::endl;
- }
-
- CompInstance &comp = *ECS_GetInstance(cam.phys.instHandle);
- InstPos baseInst{};
- baseInst.posRot = btTransform{};
- baseInst.posRot.setIdentity();
- baseInst.scale = btVector3(1, 1, 1);
- btTransform trans;
- comp.bt.motionState->getWorldTransform(trans);
- btVector3 scale = comp.bt.rigidBody->getCollisionShape()->getLocalScaling();
- if (trans != baseInst.posRot) {
- btVector3 pos = trans.getOrigin();
- btQuaternion rot = trans.getRotation();
- stream << PKE_FILE_INSTANCE_POS_POS << "["
- << std::setw(10) << pos[0] << ","
- << std::setw(10) << pos[1] << ","
- << std::setw(10) << pos[2] << "]" << std::endl
- << PKE_FILE_INSTANCE_POS_ROT << "["
- << std::setw(10) << rot[0] << ","
- << std::setw(10) << rot[1] << ","
- << std::setw(10) << rot[2] << ","
- << std::setw(10) << rot[3] << "]" << std::endl;
- }
- if (scale != baseInst.scale)
- stream << PKE_FILE_INSTANCE_POS_SCALE << "["
- << std::setw(10) << scale[0] << ","
- << std::setw(10) << scale[1] << ","
- << std::setw(10) << scale[2] << "]" << std::endl;
-}
-
-void SerializeInstance(std::ostream &stream, const CompInstance &comp) {
- NULL_CHAR_ARR(handleStr, 23);
- EntityType *et = nullptr;
- if (comp.grBindsHandle != GrBindsHandle_MAX) {
- et = EntityType_FindByEntityHandle(ECS_GetGrBinds(comp.grBindsHandle)->entHandle);
- }
- CompInstance c{};
- InstPos baseInst{};
- baseInst.posRot = btTransform{};
- baseInst.posRot.setIdentity();
- baseInst.scale = btVector3(1, 1, 1);
- baseInst.mass = 1;
- if (comp.entHandle != InstanceHandle_MAX) {
- snprintf(handleStr, 22, "0x%08X 0x%08X", comp.entHandle.bucketIndex, comp.entHandle.itemIndex);
- stream << PKE_FILE_INSTANCE_ENTITY_HANDLE << handleStr << std::endl;
- }
- if (comp.instanceHandle != InstanceHandle_MAX) {
- snprintf(handleStr, 22, "0x%08X 0x%08X", comp.instanceHandle.bucketIndex, comp.instanceHandle.itemIndex);
- stream << PKE_FILE_INSTANCE_HANDLE << handleStr << std::endl;
- }
- if (comp.uuid != pk_uuid_zed && comp.uuid != pk_uuid_max) {
- stream << PKE_FILE_INSTANCE_UUID << comp.uuid << std::endl;
- }
- if (et != nullptr) {
- stream << PKE_FILE_INSTANCE_ENTITY_TYPE_CODE << et->entityTypeCode.val << std::endl;
- } else if (PkeCamera_Get(comp.entHandle)) {
- stream << PKE_FILE_INSTANCE_ENTITY_TYPE_CODE << PKE_FILE_INSTANCE_SPECIAL_ENTITY_TYPE_CODE_CAMERA << std::endl;
- }
-
- btTransform trans;
- comp.bt.motionState->getWorldTransform(trans);
- btVector3 scale = comp.bt.rigidBody->getCollisionShape()->getLocalScaling();
- btScalar mass = comp.bt.rigidBody->getMass();
- PhysicsCollision collisionLayer{static_cast<PhysicsCollision_T>(comp.bt.rigidBody->getBroadphaseProxy()->m_collisionFilterGroup)};
- PhysicsCollision collisionMask{static_cast<PhysicsCollision_T>(comp.bt.rigidBody->getBroadphaseProxy()->m_collisionFilterMask)};
- if (trans != baseInst.posRot) {
- btVector3 pos = trans.getOrigin();
- btQuaternion rot = trans.getRotation();
- stream << PKE_FILE_INSTANCE_POS_POS << "["
- << std::setw(10) << pos[0] << ","
- << std::setw(10) << pos[1] << ","
- << std::setw(10) << pos[2] << "]" << std::endl
- << PKE_FILE_INSTANCE_POS_ROT << "["
- << std::setw(10) << rot[0] << ","
- << std::setw(10) << rot[1] << ","
- << std::setw(10) << rot[2] << ","
- << std::setw(10) << rot[3] << "]" << std::endl;
- }
- if (scale != baseInst.scale)
- stream << PKE_FILE_INSTANCE_POS_SCALE << "["
- << std::setw(10) << scale[0] << ","
- << std::setw(10) << scale[1] << ","
- << std::setw(10) << scale[2] << "]" << std::endl;
- if (mass != baseInst.mass) {
- stream << PKE_FILE_INSTANCE_PHYSICS_MASS << mass << std::endl;
- }
- if (collisionLayer != c.physicsLayer) {
- stream << PKE_FILE_INSTANCE_PHYSICS_COLLISION_LAYER << static_cast<PhysicsCollision_T>(collisionLayer) << std::endl;
- }
- if (collisionMask != c.physicsMask) {
- stream << PKE_FILE_INSTANCE_PHYSICS_COLLISION_MASK << static_cast<PhysicsCollision_T>(collisionMask) << std::endl;
- }
- if (comp.collisionCallback.name[0] != '\0') {
- stream << PKE_FILE_INSTANCE_COLLISION_CALLBACK_SIGNATURE << comp.collisionCallback.name << std::endl;
- }
-}
-
-bool FindFirstInstanceHandle(void *handle, void *mapping) {
- InstMapping *inst_mapping = reinterpret_cast<InstMapping *>(mapping);
- return inst_mapping->origHandle == *reinterpret_cast<InstanceHandle *>(handle);
-}
-void DeserializeCamera(pke_scene *scene, std::istream &stream) {
- PkeCamera cam{};
- InstanceHandle instanceHandle = InstanceHandle_MAX;
- InstanceHandle targetInstanceHandle = InstanceHandle_MAX;
- while (stream.getline(readLine, readLineLength)) {
- if (strcmp(readLine, PKE_FILE_OBJ_END) == 0) {
-
- int64_t instanceIndex = -1, targetInstanceIndex = -1;
-
- instanceIndex = pk_arr_find_first_index(&loadFileInstanceMappings, &instanceHandle, FindFirstInstanceHandle);
-
- if (targetInstanceHandle != InstanceHandle_MAX) {
- targetInstanceIndex = pk_arr_find_first_index(&loadFileInstanceMappings, &targetInstanceHandle, FindFirstInstanceHandle);
- }
-
- InstPos instPos;
- if (instanceIndex == -1) {
- instPos.mass = 1.f;
- instPos.posRot.setIdentity();
- instPos.scale = btVector3(1.f, 1.f, 1.f);
- fprintf(stdout, "[DeserializeCamera] Failed to find instance mapping. Is this an outdated parse?\n");
- } else {
- instPos = loadFileInstanceMappings[instanceIndex].newInstance;
- }
- auto &rCam = PkeCamera_Register(cam.uuid, instPos);
- rCam.type = cam.type;
- rCam.view = cam.view;
- rCam.isPrimary = cam.isPrimary;
- rCam.phys.targetInstHandle = targetInstanceHandle;
- pke_scene_register_camera(scene->scene_handle, rCam.camHandle);
- if (targetInstanceIndex > -1) {
- PkeCamera_TargetInstance(rCam.camHandle, ECS_GetInstance(loadFileInstanceMappings[targetInstanceIndex].newInstHandle));
- }
- if (rCam.isPrimary == true) {
- ActiveCamera = &rCam;
- }
- return;
- }
- if (strncmp(readLine, PKE_FILE_CAMERA_UUID, strlen(PKE_FILE_CAMERA_TYPE)) == 0) {
- uint64_t prefixLen = strlen(PKE_FILE_CAMERA_UUID);
- (readLine + prefixLen) >> cam.uuid;
- continue;
- }
- if (strncmp(readLine, PKE_FILE_CAMERA_TYPE, strlen(PKE_FILE_CAMERA_TYPE)) == 0) {
- uint64_t prefixLen = strlen(PKE_FILE_CAMERA_TYPE);
- PkeCameraType_T handle_t;
- STR2NUM_ERROR result = str2num(handle_t, readLine + prefixLen);
- assert(result == STR2NUM_ERROR::SUCCESS);
- cam.type = PkeCameraType{handle_t};
- continue;
- }
- if (strncmp(readLine, PKE_FILE_CAMERA_ORIENTATION, strlen(PKE_FILE_CAMERA_ORIENTATION)) == 0) {
- uint64_t prefixLen = strlen(PKE_FILE_CAMERA_ORIENTATION);
- PkeCameraView_T handle_t;
- STR2NUM_ERROR result = str2num(handle_t, readLine + prefixLen);
- assert(result == STR2NUM_ERROR::SUCCESS);
- cam.view = PkeCameraView{handle_t};
- continue;
- }
- if (strstr(readLine, PKE_FILE_CAMERA_INSTANCE_HANDLE)) {
- uint64_t prefixLen = strlen(PKE_FILE_CAMERA_INSTANCE_HANDLE);
- readLine[prefixLen + 10] = '\0';
- STR2NUM_ERROR result1 = str2num(instanceHandle.bucketIndex, readLine + prefixLen);
- STR2NUM_ERROR result2 = str2num(instanceHandle.itemIndex, readLine + prefixLen + 11);
- assert(result1 == STR2NUM_ERROR::SUCCESS);
- assert(result2 == STR2NUM_ERROR::SUCCESS);
- // TODO add to global list
- continue;
- }
- if (strstr(readLine, PKE_FILE_CAMERA_TARGET_INSTANCE_HANDLE)) {
- uint64_t prefixLen = strlen(PKE_FILE_CAMERA_TARGET_INSTANCE_HANDLE);
- readLine[prefixLen + 10] = '\0';
- STR2NUM_ERROR result1 = str2num(targetInstanceHandle.bucketIndex, readLine + prefixLen);
- STR2NUM_ERROR result2 = str2num(targetInstanceHandle.itemIndex, readLine + prefixLen + 11);
- assert(result1 == STR2NUM_ERROR::SUCCESS);
- assert(result2 == STR2NUM_ERROR::SUCCESS);
- // TODO find and set
- continue;
- }
- if (strncmp(readLine, PKE_FILE_CAMERA_IS_PRIMARY, strlen(PKE_FILE_CAMERA_IS_PRIMARY)) == 0) {
- uint64_t prefixLen = strlen(PKE_FILE_CAMERA_IS_PRIMARY);
- uint8_t isPrimary;
- STR2NUM_ERROR result = str2num(isPrimary, readLine + prefixLen);
- assert(result == STR2NUM_ERROR::SUCCESS);
- cam.isPrimary = bool(isPrimary);
- continue;
- }
- }
-}
-
-void DeserializeInstance(Entity_Base *parentEntity, std::istream &stream) {
- CompInstance comp{};
- InstMapping mapping {
- .origHandle = InstanceHandle_MAX,
- .newEntHandle = EntityHandle_MAX,
- .newInstHandle = InstanceHandle_MAX,
- .newInstance = {
- .posRot = {},
- .scale = btVector3(1.f, 1.f, 1.f),
- .mass = 1.f,
- },
- };
- mapping.newInstance.posRot.setIdentity();
- comp.collisionCallback.name[0] = '\0';
- NULL_CHAR_ARR(entTypeCode, 21);
- while (stream.getline(readLine, readLineLength)) {
- if (strstr(PKE_FILE_OBJ_END, readLine)) {
- EntityType *etPtr = nullptr;
- Entity_Base *entity = nullptr;
- bool skipEntCreate = false;
- if (strlen(entTypeCode) > 1) {
- etPtr = EntityType_FindByTypeCode(entTypeCode);
- if (etPtr == nullptr) {
- fprintf(stdout, "[Game::DeserializeInstance] Unknown EntityTypeCode: \"%s\"\n", entTypeCode);
- break;
- }
- } else if (strlen(entTypeCode) == 1) {
- // handle internally
- if (entTypeCode[0] == PKE_FILE_INSTANCE_SPECIAL_ENTITY_TYPE_CODE_CAMERA) {
- skipEntCreate = true;
- }
- } else {
- fprintf(stdout, "[Game::DeserializeInstance] Failed to create instance from save file. No EntTypeCode present.\n");
- break;
- }
-
- if (skipEntCreate == false) {
- if (etPtr != nullptr && etPtr->createInstanceCallback.func != nullptr) {
- /* TODO 2025-03-27 JCB
- * We have not yet defined what the appropriate callback signature
- * for creating an entity instance is.
- * What should be passed as arguments? What would need to be passed
- * that couldn't be accessed globally?
- * Consider changing this callback to trigger after creating a
- * generic instance, rather than *creating* it.
- */
- // typedef Entity_Base *CreateInst();
- // entity = reinterpret_cast<CreateInst*>(etPtr->createInstanceCallback.func)();
- fprintf(stderr, "[%s] Attempted to call EntityType::createInstanceCallback and we have not yet defined a valid function signature", __FILE__);
- } else {
- entity = EntityType_CreateGenericInstance(etPtr, parentEntity, &comp, &mapping.newInstance);
- fprintf(stdout ,"[Game::DeserializeInstance] Debug: entTypeCode '%s' does not have a registered callback func to handle instance creation.\n", entTypeCode);
- }
- mapping.newEntHandle = entity->handle;
- }
- if (mapping.newEntHandle != EntityHandle_MAX) {
- // TODO this is messy
- pk_arr_t<CompInstance *> instances{};
- ECS_GetInstances(entity, instances);
- assert(instances.next > 0);
- mapping.newInstHandle = instances[0]->instanceHandle;
- }
- pk_arr_append(&loadFileInstanceMappings, &mapping);
- break;
- }
- if (strstr(readLine, PKE_FILE_INSTANCE_ENTITY_HANDLE)) {
- uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_ENTITY_HANDLE);
- readLine[prefixLen + 10] = '\0';
- STR2NUM_ERROR result1 = str2num(comp.entHandle.bucketIndex, readLine + prefixLen);
- STR2NUM_ERROR result2 = str2num(comp.entHandle.itemIndex, readLine + prefixLen + 11);
- assert(result1 == STR2NUM_ERROR::SUCCESS);
- assert(result2 == STR2NUM_ERROR::SUCCESS);
- continue;
- }
- if (strstr(readLine, PKE_FILE_INSTANCE_HANDLE)) {
- uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_HANDLE);
- readLine[prefixLen + 10] = '\0';
- STR2NUM_ERROR result1 = str2num(mapping.origHandle.bucketIndex, readLine + prefixLen);
- STR2NUM_ERROR result2 = str2num(mapping.origHandle.itemIndex, readLine + prefixLen + 11);
- assert(result1 == STR2NUM_ERROR::SUCCESS);
- assert(result2 == STR2NUM_ERROR::SUCCESS);
- continue;
- }
- if (strstr(readLine, PKE_FILE_INSTANCE_UUID)) {
- uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_UUID);
- (readLine + prefixLen) >> comp.uuid ;
- continue;
- }
- if (strstr(readLine, PKE_FILE_INSTANCE_ENTITY_TYPE_CODE)) {
- uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_ENTITY_TYPE_CODE);
- strncpy(entTypeCode, readLine + prefixLen, 21);
- continue;
- }
- if (strstr(readLine, PKE_FILE_INSTANCE_POS_POS)) {
- uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_POS_POS);
- char *startingChar = strchr(readLine + prefixLen, '[') + 1;
- assert(startingChar != nullptr);
- char *pEnd = nullptr;
- long index = 0;
- btVector3 pos;
- do {
- assert(index < 3);
- STR2NUM_ERROR result = str2num(pos[index], startingChar, pEnd);
- assert(result == STR2NUM_ERROR::SUCCESS);
- startingChar = pEnd + 1;
- ++index;
- } while (*pEnd != ']');
- mapping.newInstance.posRot.setOrigin(pos);
- continue;
- }
- if (strstr(readLine, PKE_FILE_INSTANCE_POS_ROT)) {
- uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_POS_ROT);
- char *startingChar = strchr(readLine + prefixLen, '[') + 1;
- assert(startingChar != nullptr);
- char *pEnd = nullptr;
- long index = 0;
- btQuaternion rot;
- do {
- assert(index < 4);
- STR2NUM_ERROR result = str2num(rot[index], startingChar, pEnd);
- assert(result == STR2NUM_ERROR::SUCCESS);
- startingChar = pEnd + 1;
- ++index;
- } while (*pEnd != ']');
- mapping.newInstance.posRot.setRotation(rot);
- continue;
- }
- if (strstr(readLine, PKE_FILE_INSTANCE_POS_SCALE)) {
- uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_POS_SCALE);
- char *startingChar = strchr(readLine + prefixLen, '[') + 1;
- assert(startingChar != nullptr);
- char *pEnd = nullptr;
- long index = 0;
- do {
- assert(index < 3);
- STR2NUM_ERROR result = str2num(mapping.newInstance.scale[index], startingChar, pEnd);
- assert(result == STR2NUM_ERROR::SUCCESS);
- startingChar = pEnd + 1;
- ++index;
- } while (*pEnd != ']');
- continue;
- }
- if (strstr(readLine, PKE_FILE_INSTANCE_PHYSICS_MASS)) {
- uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_PHYSICS_MASS);
- STR2NUM_ERROR result = str2num(mapping.newInstance.mass, readLine + prefixLen);
- assert(result == STR2NUM_ERROR::SUCCESS);
- continue;
- }
- if (strstr(readLine, PKE_FILE_INSTANCE_PHYSICS_COLLISION_LAYER)) {
- uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_PHYSICS_COLLISION_LAYER);
- PhysicsCollision_T val = static_cast<PhysicsCollision_T>(comp.physicsLayer);
- STR2NUM_ERROR result = str2num(val, readLine + prefixLen);
- comp.physicsLayer = PhysicsCollision{val};
- assert(result == STR2NUM_ERROR::SUCCESS);
- continue;
- }
- if (strstr(readLine, PKE_FILE_INSTANCE_PHYSICS_COLLISION_MASK)) {
- uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_PHYSICS_COLLISION_MASK);
- PhysicsCollision_T val = static_cast<PhysicsCollision_T>(comp.physicsMask);
- STR2NUM_ERROR result = str2num(val, readLine + prefixLen);
- comp.physicsMask = PhysicsCollision{val};
- assert(result == STR2NUM_ERROR::SUCCESS);
- continue;
- }
- if (strstr(readLine, PKE_FILE_INSTANCE_COLLISION_CALLBACK_SIGNATURE)) {
- uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_COLLISION_CALLBACK_SIGNATURE);
- strncpy(comp.collisionCallback.name, readLine + prefixLen, 16);
- continue;
- }
- }
-}
-
-void Game_SaveSceneFile(const char *sceneFilePath) {
- std::ostringstream stream{};
- bool failed = false;
-
- try {
- stream << PKE_FILE_BEGIN << std::endl;
- stream << PKE_FILE_VERSION << std::endl;
- stream << "" << std::endl;
-
- pk_handle_bucket_index_T instanceBucketCount = ECS_GetInstances_BucketCount();
- for (pk_handle_bucket_index_T b = 0; b < instanceBucketCount; ++b) {
- pk_handle_item_index_T count;
- auto *instances = ECS_GetInstances(b, count);
- for (pk_handle_item_index_T i = 0; i < count; ++i) {
- const auto &instance = instances[i];
- if (instance.entHandle == EntityHandle_MAX)
- continue;
- if (instance.flags & COMPONENT_INSTANCE_FLAG_DO_NOT_SERIALIZE) {
- continue;
- }
- stream << PKE_FILE_OBJ_INSTANCE << std::endl;
- SerializeInstance(stream, instance);
- stream << PKE_FILE_OBJ_END << std::endl;
- }
- }
-
- pk_handle_bucket_index_T cameraBucketCount = PkeCamera_GetBucketCount();
- for (pk_handle_bucket_index_T b = 0; b < cameraBucketCount; ++b) {
- pk_handle_item_index_T count;
- auto *cameras = PkeCamera_GetCameras(b, count);
- for (pk_handle_item_index_T i = 0; i < count; ++i) {
- const auto &cam = cameras[i];
- if (cam.handle == CameraHandle_MAX)
- continue;
- stream << PKE_FILE_OBJ_CAMERA << std::endl;
- SerializeCamera(stream, cam);
- stream << PKE_FILE_OBJ_END << std::endl;
- }
- }
-
- stream << PKE_FILE_END << std::endl;
- } catch (std::exception &e) {
- fprintf(stderr, "[%s][Game_SaveSceneFile] Failed to serialize scene file: %s\n", __FILE__, e.what());
- failed = false;
- } catch (...) {
- fprintf(stderr, "[%s][Game_SaveSceneFile] Failed to serialize scene file, uncaught exception.\n", __FILE__);
- failed = false;
- }
-
- if (failed == false) {
- std::ofstream f(sceneFilePath);
- if (!f.is_open()) {
- failed = true;
- } else {
- f << stream.str();
- }
- f.flush();
- f.close();
- }
-
- if (failed) {
- NULL_CHAR_ARR(errFileName, 256);
- strncpy(errFileName, sceneFilePath, 256);
- strncpy(errFileName + strlen(sceneFilePath), ".err", 256 - strlen(sceneFilePath));
- std::ofstream errF(sceneFilePath);
- if (errF.is_open()) {
- errF << stream.str();
- errF.flush();
- errF.close();
- fprintf(stderr, "Failed to save scene file '%s', partial output saved to '%s'\n", sceneFilePath, errFileName);
- } else {
- fprintf(stderr, "Failed to save scene file '%s' and also failed to write failed output\n", 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);
- 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(scn, f);
- continue;
- }
- if (strcmp(PKE_FILE_OBJ_CAMERA, readLine) == 0) {
- DeserializeCamera(scn, f);
- continue;
- }
- }
-
- f.close();
- pk_arr_clear(&loadFileInstanceMappings);
-}
-
const int64_t consoleBufferCount = 30;
const int64_t consoleLineLength = 128;
char consoleBuffer[consoleBufferCount][consoleLineLength];
diff --git a/src/game.hpp b/src/game.hpp
index 8476ff0..b879364 100644
--- a/src/game.hpp
+++ b/src/game.hpp
@@ -8,7 +8,5 @@ void Game_Init();
void Game_Tick(double delta);
void Game_Teardown();
void Game_RecordImGui();
-void Game_SaveSceneFile(const char *);
-void Game_LoadSceneFile(const char *);
#endif /* PKE_GAME_HPP */
diff --git a/src/project-settings.hpp b/src/project-settings.hpp
index c1873c4..9154ed1 100644
--- a/src/project-settings.hpp
+++ b/src/project-settings.hpp
@@ -4,6 +4,8 @@
#include "dynamic-array.hpp"
#include "pk.h"
+const char* const PKE_PROJ_DEFAULT_FILENAME = "project.pptf";
+
struct PkeProjectSettings {
const char *defaultSceneName = "default";
DynArray<pk_str> scenes{16};
diff --git a/src/project.cpp b/src/project.cpp
index c94451f..a27daba 100644
--- a/src/project.cpp
+++ b/src/project.cpp
@@ -5,6 +5,7 @@
#include "entities.hpp"
#include "helpers.hpp"
#include "font.hpp"
+#include "project-settings.hpp"
#include <fstream>
#include <ostream>
@@ -413,7 +414,7 @@ void PkeProject_Save(const char *filePath) {
}
stream << PKE_PROJ_FILE_END << std::endl;
- } catch (std::exception e) {
+ } catch (std::exception &e) {
fprintf(stderr, "[%s][PkeProject_Save] Failed to serialize project file: %s\n", __FILE__, e.what());
failed = false;
} catch (...) {
diff --git a/src/project.hpp b/src/project.hpp
index ba678db..c1a321e 100644
--- a/src/project.hpp
+++ b/src/project.hpp
@@ -1,8 +1,6 @@
#ifndef PKE_PROJECT_HPP
#define PKE_PROJECT_HPP
-const char* const PKE_PROJ_DEFAULT_FILENAME = "project.pptf";
-
void PkeProject_Load(const char *filePath = nullptr);
void PkeProject_Save(const char *filePath = nullptr);
diff --git a/src/serialization-camera.cpp b/src/serialization-camera.cpp
new file mode 100644
index 0000000..abf404b
--- /dev/null
+++ b/src/serialization-camera.cpp
@@ -0,0 +1,153 @@
+
+#include "serialization-camera.hpp"
+
+#include "serialization-component.hpp"
+#include "ecs.hpp"
+#include "math-helpers.hpp"
+#include "scene.hpp"
+
+#include <BulletCollision/CollisionShapes/btCollisionShape.h>
+
+#include "vendor-glm-include.hpp"
+
+void pke_serialize_camera(srlztn_serialize_helper *h, const PkeCamera *cam) {
+ NULL_CHAR_ARR(handleStr, 23);
+ PkeCamera c{};
+ if (cam->uuid != pk_uuid_zed && cam->uuid != pk_uuid_max) {
+ h->o << SRLZTN_CAMERA_UUID << cam->uuid << std::endl;
+ }
+ if (cam->type != c.type) {
+ h->o << SRLZTN_CAMERA_TYPE << int(static_cast<PkeCameraType_T>(cam->type)) << std::endl;
+ }
+ if (cam->view != c.view) {
+ h->o << SRLZTN_CAMERA_ORIENTATION << int(static_cast<PkeCameraView_T>(cam->view)) << std::endl;
+ }
+ if (cam->phys.instHandle != InstanceHandle_MAX) {
+ snprintf(handleStr, 22, "0x%08X 0x%08X", cam->phys.instHandle.bucketIndex, cam->phys.instHandle.itemIndex);
+ h->o << SRLZTN_CAMERA_INSTANCE_HANDLE << handleStr << std::endl;
+ }
+ if (cam->phys.targetInstHandle != InstanceHandle_MAX) {
+ snprintf(handleStr, 22, "0x%08X 0x%08X", cam->phys.targetInstHandle.bucketIndex, cam->phys.targetInstHandle.itemIndex);
+ h->o << SRLZTN_CAMERA_TARGET_INSTANCE_UUID << handleStr << std::endl;
+ }
+ if (cam->isPrimary != c.isPrimary) {
+ h->o << SRLZTN_CAMERA_IS_PRIMARY << cam->isPrimary << std::endl;
+ }
+
+ CompInstance &comp = *ECS_GetInstance(cam->phys.instHandle);
+ InstPos baseInst{};
+ baseInst.posRot = btTransform{};
+ baseInst.posRot.setIdentity();
+ baseInst.scale = btVector3(1, 1, 1);
+ btTransform trans;
+ comp.bt.motionState->getWorldTransform(trans);
+ btVector3 scale = comp.bt.rigidBody->getCollisionShape()->getLocalScaling();
+ btVector3 pos = trans.getOrigin();
+ btQuaternion rot = trans.getRotation();
+
+ glm::vec3 glm_pos;
+ glm::quat quat_rot;
+ glm::vec3 glm_scale;
+ BulletToGlm(pos, glm_pos);
+ BulletToGlm(rot, quat_rot);
+ BulletToGlm(scale, glm_scale);
+ pke_serialize_inst_pos(h, glm_pos, quat_rot, glm_scale);
+}
+
+bool FindFirstInstanceHandle(void *handle, void *mapping) {
+ srlztn_instance_mapping *inst_mapping = reinterpret_cast<srlztn_instance_mapping *>(mapping);
+ return inst_mapping->serialized_uuid == *reinterpret_cast<pk_uuid *>(handle);
+}
+void pke_deserialize_camera(srlztn_deserialize_helper *h) {
+ PK_STN_RES stn_res;
+ PkeCamera cam{};
+ InstPos instPos;
+ InstanceHandle instanceHandle = InstanceHandle_MAX;
+ pk_uuid target_uuid = pk_uuid_zed;
+ glm::vec3 pos;
+ glm::quat quat_rot;
+ glm::vec3 scale;
+ instPos.mass = 1;
+ instPos.posRot.setIdentity();
+ instPos.scale = btVector3(1, 1, 1);
+ while (h->i.getline(h->read_line, h->read_line_len)) {
+ if (strcmp(h->read_line, SRLZTN_OBJ_END) == 0) {
+
+ int64_t targetInstanceIndex = -1;
+
+ if (target_uuid != pk_uuid_zed) {
+ targetInstanceIndex = pk_arr_find_first_index(&h->mapping, &target_uuid, FindFirstInstanceHandle);
+ }
+
+ btVector3 bt_pos;
+ btQuaternion bt_rot;
+ GlmToBullet(pos, bt_pos);
+ GlmToBullet(scale, instPos.scale);
+ GlmToBullet(quat_rot, bt_rot);
+ instPos.posRot.setOrigin(bt_pos);
+ instPos.posRot.setRotation(bt_rot);
+ auto &rCam = PkeCamera_Register(cam.uuid, instPos);
+ rCam.type = cam.type;
+ rCam.view = cam.view;
+ rCam.isPrimary = cam.isPrimary;
+ pke_scene_register_camera(h->scene->scene_handle, rCam.camHandle);
+ if (targetInstanceIndex > -1) {
+ PkeCamera_TargetInstance(rCam.camHandle, h->mapping[targetInstanceIndex].created_instance);
+ }
+ if (rCam.isPrimary == true) {
+ ActiveCamera = &rCam;
+ }
+ return;
+ }
+ if (pke_deserialize_inst_pos(h, pos, quat_rot, scale)) {
+ continue;
+ }
+ if (strncmp(h->read_line, SRLZTN_CAMERA_UUID, strlen(SRLZTN_CAMERA_TYPE)) == 0) {
+ uint64_t prefixLen = strlen(SRLZTN_CAMERA_UUID);
+ (h->read_line + prefixLen) >> cam.uuid;
+ continue;
+ }
+ if (strncmp(h->read_line, SRLZTN_CAMERA_TYPE, strlen(SRLZTN_CAMERA_TYPE)) == 0) {
+ uint64_t prefixLen = strlen(SRLZTN_CAMERA_TYPE);
+ stn_res = pk_stn(&cam.type, h->read_line + prefixLen);
+ if (stn_res != PK_STN_RES_SUCCESS) {
+ fprintf(stderr, "[%s] Err '%u' parsing camera type from: '%s'", __FILE__, stn_res, h->read_line);
+ }
+ continue;
+ }
+ if (strncmp(h->read_line, SRLZTN_CAMERA_ORIENTATION, strlen(SRLZTN_CAMERA_ORIENTATION)) == 0) {
+ uint64_t prefixLen = strlen(SRLZTN_CAMERA_ORIENTATION);
+ stn_res = pk_stn(&cam.view, h->read_line + prefixLen);
+ if (stn_res != PK_STN_RES_SUCCESS) {
+ fprintf(stderr, "[%s] Err '%u' parsing camera view from: '%s'", __FILE__, stn_res, h->read_line);
+ }
+ continue;
+ }
+ if (strstr(h->read_line, SRLZTN_CAMERA_INSTANCE_HANDLE)) {
+ uint64_t prefixLen = strlen(SRLZTN_CAMERA_INSTANCE_HANDLE);
+ h->read_line[prefixLen + 10] = '\0';
+ stn_res = pk_stn(&instanceHandle.bucketIndex, h->read_line + prefixLen);
+ if (stn_res != PK_STN_RES_SUCCESS) {
+ fprintf(stderr, "[%s] Err '%u' parsing camera instance handle from: '%s'", __FILE__, stn_res, h->read_line);
+ }
+ stn_res = pk_stn(&instanceHandle.itemIndex, h->read_line + prefixLen + 11);
+ if (stn_res != PK_STN_RES_SUCCESS) {
+ fprintf(stderr, "[%s] Err '%u' parsing camera instance handle from: '%s'", __FILE__, stn_res, h->read_line);
+ }
+ continue;
+ }
+ if (strstr(h->read_line, SRLZTN_CAMERA_TARGET_INSTANCE_UUID)) {
+ uint64_t prefixLen = strlen(SRLZTN_CAMERA_TARGET_INSTANCE_UUID);
+ (h->read_line + prefixLen) >> cam.uuid;
+ continue;
+ }
+ if (strncmp(h->read_line, SRLZTN_CAMERA_IS_PRIMARY, strlen(SRLZTN_CAMERA_IS_PRIMARY)) == 0) {
+ uint64_t prefixLen = strlen(SRLZTN_CAMERA_IS_PRIMARY);
+ stn_res = pk_stn(&cam.isPrimary, h->read_line + prefixLen);
+ if (stn_res != PK_STN_RES_SUCCESS) {
+ fprintf(stderr, "[%s] Err '%u' parsing camera primary from: '%s'", __FILE__, stn_res, h->read_line);
+ }
+ continue;
+ }
+ }
+}
diff --git a/src/serialization-camera.hpp b/src/serialization-camera.hpp
new file mode 100644
index 0000000..778febd
--- /dev/null
+++ b/src/serialization-camera.hpp
@@ -0,0 +1,11 @@
+#ifndef PKE_SERIALIZATION_CAMERA_HPP
+#define PKE_SERIALIZATION_CAMERA_HPP
+
+#include "serialization.hpp"
+#include "camera.hpp"
+
+void pke_serialize_camera(srlztn_serialize_helper *helper, const PkeCamera *cam);
+
+void pke_deserialize_camera(srlztn_deserialize_helper *helper);
+
+#endif /* PKE_SERIALIZATION_CAMERA_HPP */
diff --git a/src/serialization-component.cpp b/src/serialization-component.cpp
new file mode 100644
index 0000000..34eb758
--- /dev/null
+++ b/src/serialization-component.cpp
@@ -0,0 +1,256 @@
+
+#include "serialization-component.hpp"
+
+#include "ecs.hpp"
+#include "entities.hpp"
+#include "serialization.hpp"
+#include "math-helpers.hpp"
+
+#include "BulletCollision/CollisionShapes/btCollisionShape.h"
+
+#include "pk.h"
+
+#define SRLZTN_FLOAT_WIDTH 10
+
+bool pke_serialize_inst_pos(srlztn_serialize_helper *h, const glm::vec3 pos, const glm::quat quat_rot, const glm::vec3 scale) {
+ if (pos != glm::vec3(0)) {
+ h->o << SRLZTN_POSROT_POS << "["
+ << std::setw(SRLZTN_FLOAT_WIDTH) << pos[0] << ","
+ << std::setw(SRLZTN_FLOAT_WIDTH) << pos[1] << ","
+ << std::setw(SRLZTN_FLOAT_WIDTH) << pos[2] << "]" << std::endl;
+ }
+ if (quat_rot != glm::quat{}) {
+ h->o << SRLZTN_POSROT_ROT << "["
+ << std::setw(SRLZTN_FLOAT_WIDTH) << quat_rot[0] << ","
+ << std::setw(SRLZTN_FLOAT_WIDTH) << quat_rot[1] << ","
+ << std::setw(SRLZTN_FLOAT_WIDTH) << quat_rot[2] << ","
+ << std::setw(SRLZTN_FLOAT_WIDTH) << quat_rot[3] << "]" << std::endl;
+ }
+ if (scale != glm::vec3(1)) {
+ h->o << SRLZTN_POSROT_SCALE << "["
+ << std::setw(SRLZTN_FLOAT_WIDTH) << scale[0] << ","
+ << std::setw(SRLZTN_FLOAT_WIDTH) << scale[1] << ","
+ << std::setw(SRLZTN_FLOAT_WIDTH) << scale[2] << "]" << std::endl;
+ }
+ return true;
+}
+
+bool pke_deserialize_inst_pos(srlztn_deserialize_helper *h, glm::vec3 &pos, glm::quat &quat_rot, glm::vec3 &scale) {
+ const char *starting_char;
+ uint64_t prefix_len;
+ PK_STN_RES stn_res;
+ if (strstr(h->read_line, SRLZTN_POSROT_POS)) {
+ prefix_len = strlen(SRLZTN_POSROT_POS);
+ starting_char = strchr(h->read_line + prefix_len, '[') + 1;
+ assert(starting_char != nullptr);
+ char *pEnd = nullptr;
+ long index = 0;
+ do {
+ assert(index < 3);
+ stn_res = pk_stn_float_e(&pos[index], starting_char, &pEnd);
+ if (stn_res != PK_STN_RES_SUCCESS) return false;
+ starting_char = pEnd + 1;
+ ++index;
+ } while (*pEnd != ']');
+ return true;
+ }
+ if (strstr(h->read_line, SRLZTN_POSROT_ROT)) {
+ prefix_len = strlen(SRLZTN_POSROT_ROT);
+ starting_char = strchr(h->read_line + prefix_len, '[') + 1;
+ assert(starting_char != nullptr);
+ char *pEnd = nullptr;
+ long index = 0;
+ do {
+ assert(index < 4);
+ stn_res = pk_stn_float_e(&quat_rot[index], starting_char, &pEnd);
+ if (stn_res != PK_STN_RES_SUCCESS) return false;
+ starting_char = pEnd + 1;
+ ++index;
+ } while (*pEnd != ']');
+ return true;
+ }
+ if (strstr(h->read_line, SRLZTN_POSROT_SCALE)) {
+ prefix_len = strlen(SRLZTN_POSROT_SCALE);
+ starting_char = strchr(h->read_line + prefix_len, '[') + 1;
+ assert(starting_char != nullptr);
+ char *pEnd = nullptr;
+ long index = 0;
+ do {
+ assert(index < 3);
+ stn_res = pk_stn_float_e(&scale[index], starting_char, &pEnd);
+ if (stn_res != PK_STN_RES_SUCCESS) return false;
+ starting_char = pEnd + 1;
+ ++index;
+ } while (*pEnd != ']');
+ return true;
+ }
+ return false;
+}
+
+bool pke_serialize_instance(srlztn_serialize_helper *h, const CompInstance *comp) {
+ NULL_CHAR_ARR(handle_str, 23);
+
+ EntityType *et = nullptr;
+ if (comp->grBindsHandle != GrBindsHandle_MAX) {
+ et = EntityType_FindByEntityHandle(ECS_GetGrBinds(comp->grBindsHandle)->entHandle);
+ }
+
+ CompInstance c{};
+ glm::vec3 pos;
+ glm::quat quat_rot;
+ glm::vec3 scale;
+ float mass;
+ PhysicsCollision collisionLayer;
+ PhysicsCollision collisionMask;
+
+ {
+ mass = comp->bt.rigidBody->getMass();
+ collisionLayer = PhysicsCollision{static_cast<PhysicsCollision_T>(comp->bt.rigidBody->getBroadphaseProxy()->m_collisionFilterGroup)};
+ collisionMask = PhysicsCollision{static_cast<PhysicsCollision_T>(comp->bt.rigidBody->getBroadphaseProxy()->m_collisionFilterMask)};
+
+ btTransform trans;
+ comp->bt.motionState->getWorldTransform(trans);
+ BulletToGlm(trans.getOrigin(), pos);
+ BulletToGlm(trans.getRotation(), quat_rot);
+ BulletToGlm(comp->bt.rigidBody->getCollisionShape()->getLocalScaling(), scale);
+ }
+
+ if (comp->instanceHandle != InstanceHandle_MAX) {
+ snprintf(handle_str, 22, "0x%08X 0x%08X", comp->instanceHandle.bucketIndex, comp->instanceHandle.itemIndex);
+ h->o << SRLZTN_INSTANCE_COMPONENT_ENTITY_HANDLE << handle_str << std::endl;
+ }
+ if (comp->entHandle != InstanceHandle_MAX) {
+ snprintf(handle_str, 22, "0x%08X 0x%08X", comp->entHandle.bucketIndex, comp->entHandle.itemIndex);
+ h->o << SRLZTN_INSTANCE_COMPONENT_HANDLE << handle_str << std::endl;
+ }
+ if (comp->uuid != pk_uuid_zed && comp->uuid != pk_uuid_max) {
+ h->o << SRLZTN_INSTANCE_COMPONENT_UUID << comp->uuid << std::endl;
+ }
+ if (et != nullptr) {
+ h->o << SRLZTN_INSTANCE_COMPONENT_ENTITY_TYPE_CODE << et->entityTypeCode.val << std::endl;
+ }
+
+ pke_serialize_inst_pos(h, pos, quat_rot, scale);
+
+ if (mass != 1) {
+ h->o << SRLZTN_INSTANCE_COMPONENT_MASS << mass << std::endl;
+ }
+ if (collisionLayer != c.physicsLayer) {
+ h->o << SRLZTN_INSTANCE_COMPONENT_COLLISION_LAYER << static_cast<PhysicsCollision_T>(collisionLayer) << std::endl;
+ }
+ if (collisionMask != c.physicsMask) {
+ h->o << SRLZTN_INSTANCE_COMPONENT_COLLISION_MASK << static_cast<PhysicsCollision_T>(collisionMask) << std::endl;
+ }
+ if (comp->collisionCallback.name[0] != '\0') {
+ h->o << SRLZTN_INSTANCE_COMPONENT_COLLISION_CB_SIGNATURE << comp->collisionCallback.name << std::endl;
+ }
+
+ return true;
+}
+
+bool pke_deserialize_instance(srlztn_deserialize_helper *h) {
+ uint64_t prefix_len;
+ PK_STN_RES stn_res;
+ NULL_CHAR_ARR(handle_str, 21);
+ EntityType *et_ptr = nullptr;
+ float mass;
+ InstPos inst_pos;
+ CompInstance comp{};
+ glm::vec3 pos;
+ glm::quat quat_rot;
+ glm::vec3 scale;
+ comp.collisionCallback.name[0] = '\0';
+ while (h->i.getline(h->read_line, h->read_line_len)) {
+ if (strstr(SRLZTN_OBJ_END, h->read_line)) {
+ if (et_ptr == nullptr) {
+ fprintf(stdout, "[Game::DeserializeInstance] Unknown EntityTypeCode, skipping instance.\n");
+ break;
+ }
+ btVector3 bt_pos;
+ btQuaternion bt_quat;
+ GlmToBullet(pos, bt_pos);
+ GlmToBullet(quat_rot, bt_quat);
+ GlmToBullet(scale, inst_pos.scale);
+ inst_pos.mass = mass;
+ inst_pos.posRot.setIdentity();
+ inst_pos.posRot.setOrigin(bt_pos);
+ inst_pos.posRot.setRotation(bt_quat);
+ if (et_ptr->createInstanceCallback.func != nullptr) {
+ /* TODO 2025-03-27 JCB
+ * We have not yet defined what the appropriate callback signature
+ * for creating an entity instance is.
+ * What should be passed as arguments? What would need to be passed
+ * that couldn't be accessed globally?
+ * Consider changing this callback to trigger after creating a
+ * generic instance, rather than *creating* it.
+ * Also consider just requiring a generic instance for any given
+ * EntityType.
+ */
+ // typedef Entity_Base *CreateInst();
+ // 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", __FILE__);
+ } else {
+ EntityType_CreateGenericInstance(et_ptr, h->scene, &comp, &inst_pos);
+ // fprintf(stdout ,"[Game::DeserializeInstance] Debug: entTypeCode '%s' does not have a registered callback func to handle instance creation.\n", entTypeCode);
+ }
+ return true;
+ }
+ if (strstr(h->read_line, SRLZTN_INSTANCE_COMPONENT_ENTITY_HANDLE)) {
+ prefix_len = strlen(SRLZTN_INSTANCE_COMPONENT_ENTITY_HANDLE);
+ sprintf(handle_str, "%.10s%c%s", h->read_line + prefix_len, '\0', h->read_line + prefix_len + 10);
+ stn_res = pk_stn(&comp.entHandle.bucketIndex, handle_str, 16);
+ if (stn_res != PK_STN_RES_SUCCESS) return false;
+ stn_res = pk_stn(&comp.entHandle.itemIndex, handle_str + 11, 16);
+ if (stn_res != PK_STN_RES_SUCCESS) return false;
+ continue;
+ }
+ if (strstr(h->read_line, SRLZTN_INSTANCE_COMPONENT_HANDLE)) {
+ prefix_len = strlen(SRLZTN_INSTANCE_COMPONENT_HANDLE);
+ sprintf(handle_str, "%.10s%c%s", h->read_line + prefix_len, '\0', h->read_line + prefix_len + 10);
+ stn_res = pk_stn(&comp.instanceHandle.bucketIndex, handle_str, 16);
+ if (stn_res != PK_STN_RES_SUCCESS) return false;
+ stn_res = pk_stn(&comp.instanceHandle.itemIndex, handle_str + 11, 16);
+ if (stn_res != PK_STN_RES_SUCCESS) return false;
+ continue;
+ }
+ if (strstr(h->read_line, SRLZTN_INSTANCE_COMPONENT_UUID)) {
+ prefix_len = strlen(SRLZTN_INSTANCE_COMPONENT_UUID);
+ (h->read_line + prefix_len) >> comp.uuid ;
+ continue;
+ }
+ if (strstr(h->read_line, SRLZTN_INSTANCE_COMPONENT_ENTITY_TYPE_CODE)) {
+ prefix_len = strlen(SRLZTN_INSTANCE_COMPONENT_ENTITY_TYPE_CODE);
+ if (strlen(h->read_line + prefix_len) > 1) {
+ et_ptr = EntityType_FindByTypeCode(h->read_line + prefix_len);
+ }
+ continue;
+ }
+ if (strstr(h->read_line, SRLZTN_INSTANCE_COMPONENT_MASS)) {
+ prefix_len = strlen(SRLZTN_INSTANCE_COMPONENT_MASS);
+ stn_res = pk_stn(&mass, h->read_line + prefix_len);
+ if (stn_res != PK_STN_RES_SUCCESS) return false;
+ continue;
+ }
+ if (strstr(h->read_line, SRLZTN_INSTANCE_COMPONENT_COLLISION_LAYER)) {
+ prefix_len = strlen(SRLZTN_INSTANCE_COMPONENT_COLLISION_LAYER);
+ stn_res = pk_stn(&comp.physicsLayer, h->read_line + prefix_len, 10);
+ if (stn_res != PK_STN_RES_SUCCESS) return false;
+ continue;
+ }
+ if (strstr(h->read_line, SRLZTN_INSTANCE_COMPONENT_COLLISION_MASK)) {
+ prefix_len = strlen(SRLZTN_INSTANCE_COMPONENT_COLLISION_MASK);
+ stn_res = pk_stn(&comp.physicsMask, h->read_line + prefix_len, 10);
+ if (stn_res != PK_STN_RES_SUCCESS) return false;
+ continue;
+ }
+ if (strstr(h->read_line, SRLZTN_INSTANCE_COMPONENT_COLLISION_CB_SIGNATURE)) {
+ prefix_len = strlen(SRLZTN_INSTANCE_COMPONENT_COLLISION_CB_SIGNATURE);
+ strncpy(comp.collisionCallback.name, h->read_line + prefix_len, 16);
+ continue;
+ }
+ if (pke_deserialize_inst_pos(h, pos, quat_rot, scale)) {
+ continue;
+ }
+ }
+ return false;
+}
diff --git a/src/serialization-component.hpp b/src/serialization-component.hpp
new file mode 100644
index 0000000..a01d235
--- /dev/null
+++ b/src/serialization-component.hpp
@@ -0,0 +1,15 @@
+#ifndef PKE_SERIALIZATION_COMPONENT_HPP
+#define PKE_SERIALIZATION_COMPONENT_HPP
+
+#include "components.hpp"
+#include "serialization.hpp"
+
+#include "vendor-glm-include.hpp"
+
+bool pke_serialize_inst_pos(srlztn_serialize_helper *h, const glm::vec3 pos, const glm::quat quat_rot, const glm::vec3 scale);
+bool pke_deserialize_inst_pos(srlztn_deserialize_helper *h, glm::vec3 &pos, glm::quat &quat_rot, glm::vec3 &scale);
+
+bool pke_serialize_instance(srlztn_serialize_helper *h, const CompInstance *comp);
+bool pke_deserialize_instance(srlztn_deserialize_helper *h);
+
+#endif /* PKE_SERIALIZATION_COMPONENT_HPP */
diff --git a/src/serialization.cpp b/src/serialization.cpp
new file mode 100644
index 0000000..d7dd312
--- /dev/null
+++ b/src/serialization.cpp
@@ -0,0 +1,102 @@
+
+#include "serialization.hpp"
+#include "serialization-component.hpp"
+#include "serialization-camera.hpp"
+#include "camera.hpp"
+#include "ecs.hpp"
+
+srlztn_serialize_helper *pke_serialize_init(pk_membucket *bkt) {
+ srlztn_serialize_helper *helper = pk_new<srlztn_serialize_helper>(bkt);
+ // TODO custom allocator
+ helper->o = {};
+ helper->bkt = bkt;
+ return helper;
+}
+
+srlztn_deserialize_helper *pke_deserialize_init(pk_membucket *bkt) {
+ srlztn_deserialize_helper *helper = pk_new<srlztn_deserialize_helper>(bkt);
+ helper->bkt = bkt;
+ helper->read_line = nullptr;
+ // TODO custom allocator
+ helper->i = {};
+ helper->mapping = {bkt};
+ return helper;
+}
+
+void pke_serialize_teardown(srlztn_serialize_helper *helper) {
+ (void)helper;
+}
+
+void pke_deserialize_teardown(srlztn_deserialize_helper *helper) {
+ (void)helper;
+}
+
+void pke_serialize_file_project(srlztn_serialize_helper *h) {
+ (void)h;
+}
+
+void pke_deserialize_file_project(srlztn_deserialize_helper *h) {
+ (void)h;
+}
+
+void pke_serialize_file_scene(srlztn_serialize_helper *h) {
+ h->o << SRLZTN_FILE_BEGIN << std::endl;
+ h->o << SRLZTN_FILE_VERSION << std::endl;
+ h->o << "" << std::endl;
+
+ pk_handle_bucket_index_T instanceBucketCount = ECS_GetInstances_BucketCount();
+ for (pk_handle_bucket_index_T b = 0; b < instanceBucketCount; ++b) {
+ pk_handle_item_index_T count;
+ auto *instances = ECS_GetInstances(b, count);
+ for (pk_handle_item_index_T i = 0; i < count; ++i) {
+ const auto &instance = instances[i];
+ if (instance.entHandle == EntityHandle_MAX)
+ continue;
+ if (instance.flags & COMPONENT_INSTANCE_FLAG_DO_NOT_SERIALIZE) {
+ continue;
+ }
+ h->o << SRLZTN_OBJ_INSTANCE << std::endl;
+ pke_serialize_instance(h, &instance);
+ h->o << SRLZTN_OBJ_END << std::endl;
+ }
+ }
+
+ pk_handle_bucket_index_T cameraBucketCount = PkeCamera_GetBucketCount();
+ for (pk_handle_bucket_index_T b = 0; b < cameraBucketCount; ++b) {
+ pk_handle_item_index_T count;
+ auto *cameras = PkeCamera_GetCameras(b, count);
+ for (pk_handle_item_index_T i = 0; i < count; ++i) {
+ const auto &cam = cameras[i];
+ if (cam.handle == CameraHandle_MAX)
+ continue;
+ h->o << SRLZTN_OBJ_CAMERA << std::endl;
+ pke_serialize_camera(h, &cam);
+ h->o << SRLZTN_OBJ_END << std::endl;
+ }
+ }
+
+ h->o << SRLZTN_FILE_END << std::endl;
+}
+void pke_deserialize_file_scene(srlztn_deserialize_helper *h) {
+ h->read_line = pk_new<char>(h->read_line_len, h->bkt);
+ memset(h->read_line, '\0', h->read_line_len);
+
+ while (h->i.getline(h->read_line, h->read_line_len)) {
+ if (strcmp(SRLZTN_OBJ_INSTANCE, h->read_line) == 0) {
+ pke_deserialize_instance(h);
+ continue;
+ }
+ if (strcmp(SRLZTN_OBJ_CAMERA, h->read_line) == 0) {
+ pke_deserialize_camera(h);
+ continue;
+ }
+ }
+}
+
+void pke_serialize_file_level(srlztn_serialize_helper *h) {
+ (void)h;
+}
+void pke_deserialize_file_level(srlztn_deserialize_helper *h) {
+ (void)h;
+}
+
diff --git a/src/serialization.hpp b/src/serialization.hpp
new file mode 100644
index 0000000..f4180e4
--- /dev/null
+++ b/src/serialization.hpp
@@ -0,0 +1,71 @@
+#ifndef PKE_SERIALIZATION_HPP
+#define PKE_SERIALIZATION_HPP
+
+#include "pk.h"
+#include "components.hpp"
+#include "scene-types.hpp"
+
+inline const char* const SRLZTN_FILE_BEGIN = ":PKFB:";
+inline const char* const SRLZTN_FILE_END = ":PKFE:";
+inline const char* const SRLZTN_FILE_VERSION = ":0:";
+
+inline const char* const SRLZTN_OBJ_END = "";
+inline const char* const SRLZTN_OBJ_INSTANCE = "Instance:";
+inline const char* const SRLZTN_OBJ_CAMERA = "Camera:";
+
+inline const char* const SRLZTN_POSROT_POS = "Position: ";
+inline const char* const SRLZTN_POSROT_ROT = "Rotation: ";
+inline const char* const SRLZTN_POSROT_SCALE = "Scale: ";
+
+inline const char* const SRLZTN_INSTANCE_COMPONENT_HANDLE = "InstanceHandle: ";
+inline const char* const SRLZTN_INSTANCE_COMPONENT_ENTITY_HANDLE = "EntityHandle: ";
+inline const char* const SRLZTN_INSTANCE_COMPONENT_ENTITY_TYPE_CODE = "EntityTypeCode: ";
+inline const char* const SRLZTN_INSTANCE_COMPONENT_UUID = "UUID: ";
+inline const char* const SRLZTN_INSTANCE_COMPONENT_MASS = "Mass: ";
+inline const char* const SRLZTN_INSTANCE_COMPONENT_COLLISION_LAYER = "CollisionLayer: ";
+inline const char* const SRLZTN_INSTANCE_COMPONENT_COLLISION_MASK = "CollisionMask: ";
+inline const char* const SRLZTN_INSTANCE_COMPONENT_COLLISION_CB_SIGNATURE = "CollisionCBSignature: ";
+
+inline const char* const SRLZTN_CAMERA_TARGET = "Target: ";
+inline const char* const SRLZTN_CAMERA_TYPE = "Type: ";
+inline const char* const SRLZTN_CAMERA_UUID = "UUID: ";
+inline const char* const SRLZTN_CAMERA_ORIENTATION = "Orientation: ";
+inline const char* const SRLZTN_CAMERA_INSTANCE_HANDLE = "InstanceHandle: ";
+inline const char* const SRLZTN_CAMERA_TARGET_INSTANCE_UUID = "TargetUUID: ";
+inline const char* const SRLZTN_CAMERA_IS_PRIMARY = "IsPrimary: ";
+
+struct srlztn_instance_mapping {
+ pk_uuid serialized_uuid = pk_uuid_zed;
+ Entity_Base *created_entity = nullptr;
+ CompInstance *created_instance = nullptr;
+};
+
+struct srlztn_serialize_helper {
+ pk_membucket *bkt;
+ std::ostringstream o;
+};
+
+struct srlztn_deserialize_helper {
+ size_t read_line_len = 256;
+ pke_scene *scene;
+ pk_membucket *bkt;
+ char *read_line;
+ std::istringstream i;
+ pk_arr_t<srlztn_instance_mapping> mapping;
+};
+
+srlztn_serialize_helper *pke_serialize_init(pk_membucket *bkt);
+srlztn_deserialize_helper *pke_deserialize_init(pk_membucket *bkt);
+void pke_serialize_teardown(srlztn_serialize_helper *helper);
+void pke_deserialize_teardown(srlztn_deserialize_helper *helper);
+
+void pke_serialize_file_project(srlztn_serialize_helper *helper);
+void pke_deserialize_file_project(srlztn_deserialize_helper *helper);
+
+void pke_serialize_file_scene(srlztn_serialize_helper *helper);
+void pke_deserialize_file_scene(srlztn_deserialize_helper *helper);
+
+void pke_serialize_file_level(srlztn_serialize_helper *helper);
+void pke_deserialize_file_level(srlztn_deserialize_helper *helper);
+
+#endif /* PKE_SERIALIZATION_HPP */