#include "serialization-component.hpp" #include "compile-time-assert.hpp" #include "ecs.hpp" #include "entities.hpp" #include "serialization.hpp" #include "math-helpers.hpp" #include "BulletCollision/CollisionShapes/btCollisionShape.h" #include "pk.h" pk_handle pke_serialize_inst_pos(srlztn_serialize_helper *h, const glm::vec3 pos, const glm::quat quat_rot, const glm::vec3 scale) { char *s; int len; pke_kve kve{}; pke_kve_container kvec{}; kvec.srlztn_handle = h->handle_head; kvec.type_code = cstring_to_pk_cstr(SRLZTN_OBJ_INSTANCE_POSITION); kvec.bkt = h->bkt; kvec.arr.bkt = h->bkt; kvec.children.bkt = h->bkt; kvec.child_handles.bkt = h->bkt; h->handle_head.itemIndex++; if (pos != glm::vec3(0)) { kve.key = SRLZTN_POSROT_POS; len = snprintf(NULL, 0, "%f%s%f%s%f", pos[0], SRLZTN_NUM_SEPARATOR, pos[1], SRLZTN_NUM_SEPARATOR, pos[2]); s = pk_new_arr(len+1, h->bkt); sprintf(s, "%f%s%f%s%f", pos[0], SRLZTN_NUM_SEPARATOR, pos[1], SRLZTN_NUM_SEPARATOR, pos[2]); kve.val = s; kve.end = SRLZTN_KVE_END; pk_arr_append_t(&kvec.arr, kve); } if (quat_rot != glm::quat{}) { kve.key = SRLZTN_POSROT_ROT; len = snprintf(NULL, 0, "%f%s%f%s%f%s%f", quat_rot[0], SRLZTN_NUM_SEPARATOR, quat_rot[1], SRLZTN_NUM_SEPARATOR, quat_rot[2], SRLZTN_NUM_SEPARATOR, quat_rot[3]); s = pk_new_arr(len+1, h->bkt); sprintf(s, "%f%s%f%s%f%s%f", quat_rot[0], SRLZTN_NUM_SEPARATOR, quat_rot[1], SRLZTN_NUM_SEPARATOR, quat_rot[2], SRLZTN_NUM_SEPARATOR, quat_rot[3]); kve.val = s; kve.end = SRLZTN_KVE_END; pk_arr_append_t(&kvec.arr, kve); } if (scale != glm::vec3(1)) { kve.key = SRLZTN_POSROT_SCALE; len = snprintf(NULL, 0, "%f%s%f%s%f", scale[0], SRLZTN_NUM_SEPARATOR, scale[1], SRLZTN_NUM_SEPARATOR, scale[2]); s = pk_new_arr(len+1, h->bkt); sprintf(s, "%f%s%f%s%f", scale[0], SRLZTN_NUM_SEPARATOR, scale[1], SRLZTN_NUM_SEPARATOR, scale[2]); kve.val = s; kve.end = SRLZTN_KVE_END; pk_arr_append_t(&kvec.arr, kve); } pk_arr_append_t(&h->kvp_containers, kvec); // 2025-05-08 JCB // this is dead code, but it's here as a reminder to not call pk_arr_reset // so that the underlying data is not freed. kvec.child_handles.data = nullptr; kvec.children.data = nullptr; kvec.arr.data = nullptr; return kvec.srlztn_handle; } void pke_deserialize_inst_pos(srlztn_deserialize_helper *h, pke_kve_container *kvec, glm::vec3 &pos, glm::quat &quat_rot, glm::vec3 &scale) { (void)h; char *pEnd = nullptr; uint32_t i, index; const char *starting_char; pke_kve *kve; PK_STN_RES stn_res; for (i = 0; i < kvec->arr.next; ++i) { kve = &kvec->arr[i]; if (strstr(kve->key, SRLZTN_POSROT_POS)) { starting_char = kve->val; index = 0; do { assert(index < 3); stn_res = pk_stn(&pos[index], starting_char, &pEnd); if (stn_res != PK_STN_RES_SUCCESS) break; starting_char = pEnd + 1; ++index; } while (*pEnd != '\0'); continue; } if (strstr(kve->key, SRLZTN_POSROT_ROT)) { starting_char = kve->val; index = 0; do { assert(index < 4); stn_res = pk_stn(&quat_rot[index], starting_char, &pEnd); if (stn_res != PK_STN_RES_SUCCESS) break; starting_char = pEnd + 1; ++index; } while (*pEnd != '\0'); continue; } if (strstr(kve->key, SRLZTN_POSROT_SCALE)) { starting_char = kve->val; index = 0; do { assert(index < 3); stn_res = pk_stn(&scale[index], starting_char, &pEnd); if (stn_res != PK_STN_RES_SUCCESS) break; starting_char = pEnd + 1; ++index; } while (*pEnd != '\0'); continue; } } } pk_handle pke_serialize_instance(srlztn_serialize_helper *h, const CompInstance *comp) { EntityType *et = nullptr; char *s; int len; pk_handle inst_pos_handle; pke_kve kve{}; pke_kve_container kvec{}; glm::vec3 pos = glm::vec3(0,0,0); glm::quat quat_rot = glm::quat(1,0,0,0); glm::vec3 scale = glm::vec3(1,1,1); float mass; PhysicsCollision collisionLayer; PhysicsCollision collisionMask; { mass = comp->bt.rigidBody->getMass(); collisionLayer = PhysicsCollision{static_cast(comp->bt.rigidBody->getBroadphaseProxy()->m_collisionFilterGroup)}; collisionMask = PhysicsCollision{static_cast(comp->bt.rigidBody->getBroadphaseProxy()->m_collisionFilterMask)}; btTransform trans; comp->bt.motionState->getWorldTransform(trans); BulletToGlm(trans.getOrigin(), pos); BulletToGlm(trans.getRotation(), quat_rot); if (comp->bt.rigidBody != nullptr) { auto a1 = comp->bt.rigidBody; auto a2 = a1 != nullptr ? a1->getCollisionShape() : nullptr; auto a3 = a2 != nullptr ? a2->getLocalScaling() : btVector3(1,1,1); BulletToGlm(a3, scale); } } inst_pos_handle = pke_serialize_inst_pos(h, pos, quat_rot, scale); kvec.srlztn_handle = h->handle_head; kvec.type_code = cstring_to_pk_cstr(SRLZTN_OBJ_INSTANCE); kvec.bkt = h->bkt; kvec.arr.bkt = h->bkt; kvec.children.bkt = h->bkt; kvec.child_handles.bkt = h->bkt; h->handle_head.itemIndex++; pk_arr_append_t(&kvec.child_handles, inst_pos_handle); if (comp->grBindsHandle != GrBindsHandle_MAX) { et = static_cast(ECS_GetEntity(ECS_GetGrBinds(comp->grBindsHandle)->entHandle)); } /* JCB - 2025-05-22 * uncomment for debugging if (et == nullptr) { EntityHandle ent_type_ent_handle = ECS_GetGrBinds(comp->grBindsHandle)->entHandle; Entity_Base *ent_type_ent = ECS_GetEntity(ent_type_ent_handle); fprintf(stderr, "[" __FILE__ "] WARNING: Failed to find EntityType. " "GrBinds Handle: '%.8X:%.8X' " "GrBinds EntHandle: '%.8X:%.8X'" "GrBinds entity_ptr: '%p'\n" ,comp->grBindsHandle.bucketIndex, comp->grBindsHandle.itemIndex ,ent_type_ent_handle.bucketIndex, ent_type_ent_handle.itemIndex ,(void *)ent_type_ent ); } */ compt_a<128==sizeof(CompInstance)>(); if (comp->uuid != pk_uuid_zed && comp->uuid != pk_uuid_max) { kve.key = SRLZTN_INSTANCE_COMPONENT_UUID; s = pk_new_arr(37, h->bkt); sprintf(s, pk_uuid_printf_format, pk_uuid_printf_var(comp->uuid)); kve.val = s; kve.end = SRLZTN_KVE_END; pk_arr_append_t(&kvec.arr, kve); } if (et != nullptr) { kve.key = SRLZTN_INSTANCE_COMPONENT_ENTITY_TYPE_CODE; len = strlen(et->entityTypeCode.val); s = pk_new_arr(len+1, h->bkt); sprintf(s, "%s", et->entityTypeCode.val); kve.val = s; kve.end = SRLZTN_KVE_END; pk_arr_append_t(&kvec.arr, kve); } { kve.key = SRLZTN_INSTANCE_COMPONENT_MASS; len = snprintf(NULL, 0, "%f", mass); s = pk_new_arr(len+1, h->bkt); sprintf(s, "%f", mass); kve.val = s; kve.end = SRLZTN_KVE_END; pk_arr_append_t(&kvec.arr, kve); } { kve.key = SRLZTN_INSTANCE_COMPONENT_COLLISION_LAYER; len = snprintf(NULL, 0, "0x%.016lX", static_cast(collisionLayer)); s = pk_new_arr(len+1, h->bkt); sprintf(s, "0x%.016lX", static_cast(collisionLayer)); kve.val = s; kve.end = SRLZTN_KVE_END; pk_arr_append_t(&kvec.arr, kve); } { kve.key = SRLZTN_INSTANCE_COMPONENT_COLLISION_MASK; len = snprintf(NULL, 0, "0x%.016lX", static_cast(collisionMask)); s = pk_new_arr(len+1, h->bkt); sprintf(s, "0x%.016lX", static_cast(collisionMask)); kve.val = s; kve.end = SRLZTN_KVE_END; pk_arr_append_t(&kvec.arr, kve); } if (comp->collisionCallback.name[0] != '\0') { kve.key = SRLZTN_INSTANCE_COMPONENT_COLLISION_CB_SIGNATURE; s = pk_new_arr(CallbackSignatureLength + 1, h->bkt); sprintf(s, "%s", comp->collisionCallback.name); kve.val = s; kve.end = SRLZTN_KVE_END; pk_arr_append_t(&kvec.arr, kve); } pk_arr_append_t(&h->kvp_containers, kvec); return kvec.srlztn_handle; } void pke_deserialize_instance(srlztn_deserialize_helper *h, pke_kve_container *kvec) { uint32_t i; PK_STN_RES stn_res; EntityType *et_ptr = nullptr; float mass = 1; InstPos inst_pos; CompInstance comp{}; glm::vec3 pos = glm::vec3(0); glm::quat quat_rot = glm::quat(0, 0, 0, 1); glm::vec3 scale = glm::vec3(1); comp.collisionCallback.name[0] = '\0'; compt_a<128==sizeof(CompInstance)>(); for (i = 0; i < kvec->children.next; ++i) { pke_kve_container *child_kvec = kvec->children[i]; if (strncmp(child_kvec->type_code.val, SRLZTN_OBJ_INSTANCE_POSITION, strlen(SRLZTN_OBJ_INSTANCE_POSITION)) == 0) { pke_deserialize_inst_pos(h, child_kvec, pos, quat_rot, scale); } } for (i = 0; i < kvec->arr.next; ++i) { if (strstr(kvec->arr[i].key, SRLZTN_INSTANCE_COMPONENT_UUID)) { kvec->arr[i].val >> comp.uuid ; continue; } if (strstr(kvec->arr[i].key, SRLZTN_INSTANCE_COMPONENT_ENTITY_TYPE_CODE)) { et_ptr = EntityType_FindByTypeCode(kvec->arr[i].val); continue; } if (strstr(kvec->arr[i].key, SRLZTN_INSTANCE_COMPONENT_MASS)) { stn_res = pk_stn(&mass, kvec->arr[i].val, nullptr); if (stn_res != PK_STN_RES_SUCCESS) { fprintf(stderr, "[pke_deserialize_instance] Failed to parse %s, %i\n", SRLZTN_INSTANCE_COMPONENT_MASS, stn_res); } continue; } if (strstr(kvec->arr[i].key, SRLZTN_INSTANCE_COMPONENT_COLLISION_LAYER)) { PhysicsCollision_T layer; stn_res = pk_stn(&layer, kvec->arr[i].val, nullptr, 10); if (stn_res != PK_STN_RES_SUCCESS) { fprintf(stderr, "[pke_deserialize_instance] Failed to parse %s, %i\n", SRLZTN_INSTANCE_COMPONENT_COLLISION_LAYER, stn_res); } else { comp.physicsLayer = PhysicsCollision{layer}; } continue; } if (strstr(kvec->arr[i].key, SRLZTN_INSTANCE_COMPONENT_COLLISION_MASK)) { PhysicsCollision_T mask; stn_res = pk_stn(&mask, kvec->arr[i].val, nullptr, 10); if (stn_res != PK_STN_RES_SUCCESS) { fprintf(stderr, "[pke_deserialize_instance] Failed to parse %s, %i\n", SRLZTN_INSTANCE_COMPONENT_COLLISION_MASK, stn_res); } else { comp.physicsMask = PhysicsCollision{mask}; } continue; } if (strstr(kvec->arr[i].key, SRLZTN_INSTANCE_COMPONENT_COLLISION_CB_SIGNATURE)) { strncpy(comp.collisionCallback.name, kvec->arr[i].val, 16); continue; } } if (et_ptr == nullptr) { fprintf(stdout, "[pke_deserialize_instance] Unknown EntityTypeCode, skipping instance.\n"); } 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); pk_arr_t instances; srlztn_ecs_mapping map{}; map.serialized_uuid = comp.uuid; if (et_ptr != nullptr) { 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(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->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); for (uint32_t i = 0; i < instances.next; ++i) { if (comp.uuid == instances[i]->uuid) { map.created_instance = instances[i]; } } pk_arr_reset(&instances); if (map.created_instance == nullptr) { fprintf(stderr, "[pke_deserialize_instance] Failed to find created instance for creating mapping\n"); } else { pk_arr_append(&h->mapping, &map); } return; }