#include "game.hpp" #include "camera.hpp" #include "components.hpp" #include "dynamic-array.hpp" #include "entities.hpp" #include "game-settings.hpp" #include "helpers.hpp" #include "imgui.h" #include "math-helpers.hpp" #include "physics.hpp" #include "player-input.hpp" #include "vendor/glm_include.hpp" #include "window.hpp" #include #include #include #include #include #include const uint64_t consoleBufferCount = 30; const uint64_t consoleLineLength = 128; const long readLineLength = 128; char readLine[readLineLength]; const char *levelName = "demo-level"; PkeCamera cameraDefault { .pos = glm::vec3(-40.f, -40.f, -40.f), .rot = glm::quat(1.f, 0.f, 0.f, 0.f), .target = glm::vec3(0.f), .type = PKE_CAMERA_TYPE_PERSPECTIVE, .orientation = PKE_CAMERA_ORIENTATION_TARGET, .stale = PKE_CAMERA_STALE_ALL, }; PkeCamera cameraDbg { .pos = glm::vec3(4.f, 4.f, 4.f), .rot = glm::quat(1.f, 0.f, 0.f, 0.f), .target = glm::vec3(0.f), .type = PKE_CAMERA_TYPE_PERSPECTIVE, .orientation = PKE_CAMERA_ORIENTATION_FREE, .stale = PKE_CAMERA_STALE_ALL, }; const char *dbgCtrl_CameraLeft = "debug-camera-left"; const char *dbgCtrl_CameraRight = "debug-camera-right"; const char *dbgCtrl_CameraForward = "debug-camera-forward"; const char *dbgCtrl_CameraBack = "debug-camera-back"; const char *dbgCtrl_CameraUp = "debug-camera-up"; const char *dbgCtrl_CameraDown = "debug-camera-down"; const char *dbgCtrl_CameraRotCC = "debug-camera-rot-counter-clockwise"; const char *dbgCtrl_CameraRotC = "debug-camera-rot-clockwise"; const char *dbgCtrl_CameraRot = "debug-camera-rot"; const char *dbgCtrl_UnlockCamera = "debug-camera-unlock"; const char *dbgCtrl_SelectHovered = "debug-select-hovered"; const char *dbgCtrl_ClearSelection = "debug-clear-selection"; InputActionSetHandle debugControlsHandle = InputActionSetHandle_MAX; struct EntityTypeInstanceCreateInfo { EntityHandle entityTypeEntityHandle; }; DynArray entityInstancesToCreate{16}; 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_ENTITY_TYPE = "EntityType:"; const char *PKE_FILE_OBJ_INSTANCE = "Instance:"; const char *PKE_FILE_ENTITY_TYPE_MODELS_DIR = "ModelsDir: "; const char *PKE_FILE_ENTITY_TYPE_MODEL_FILE = "ModelFile: "; const char *PKE_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE = "EntityTypeCode: "; const char *PKE_FILE_ENTITY_TYPE_ENTITY_HANDLE = "EntityHandle: "; const char *PKE_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT = "StartingInstanceCount: "; const char *PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_VERTEX = "Importer_GLTF::AccessorIndexVertex: "; const char *PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_NORMAL = "Importer_GLTF::AccessorIndexNormal: "; const char *PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_UV = "Importer_GLTF::AccessorIndexUV: "; const char *PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_INDEX = "Importer_GLTF::AccessorIndexIndex: "; const char *PKE_FILE_ENTITY_TYPE_PHYSICS_STARTING_MASS = "BT::StartingMass: "; const char *PKE_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_LAYER = "BT::StartingCollisionLayer: "; const char *PKE_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_MASK = "BT::StartingCollisionMask: "; const char *PKE_FILE_INSTANCE_ENTITY_HANDLE = "EntityHandle: "; const char *PKE_FILE_INSTANCE_ENTITY_TYPE_CODE = "EntityTypeCode: "; 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: "; char consoleBuffer[consoleBufferCount][consoleLineLength]; long consoleBufferIndex = 0; EntityHandle selectedEntity = EntityHandle_MAX; EntityHandle hoveredEntity = EntityHandle_MAX; bool shouldCreateEntityType = false; EntityType *entityTypeToCreate = nullptr; bool shouldInitEditor = true; bool shouldLockCamera = false; bool shouldUnlockCamera = false; bool shouldTeardownEditor = false; void SerializeEntityType(std::ofstream &stream, const EntityType &et) { char handleStr[19] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; snprintf(handleStr, 19, "0x%016lX",static_cast(et.entityHandle)); EntityType e{}; if (et.modelsDir != e.modelsDir) stream << PKE_FILE_ENTITY_TYPE_MODELS_DIR << et.modelsDir << std::endl; if (et.modelFile != e.modelFile) stream << PKE_FILE_ENTITY_TYPE_MODEL_FILE << et.modelFile << std::endl; if (et.entityTypeCode != e.entityTypeCode) stream << PKE_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE << et.entityTypeCode << std::endl; if (et.entityHandle != e.entityHandle) stream << PKE_FILE_ENTITY_TYPE_ENTITY_HANDLE << handleStr << std::endl; if (et.startingInstanceCount != e.startingInstanceCount) stream << PKE_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT << et.startingInstanceCount << std::endl; if (et.Importer_GLTF.AccessorIndexVertex != e.Importer_GLTF.AccessorIndexVertex) stream << PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_VERTEX << et.Importer_GLTF.AccessorIndexVertex << std::endl; if (et.Importer_GLTF.AccessorIndexNormal != e.Importer_GLTF.AccessorIndexNormal) stream << PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_NORMAL << et.Importer_GLTF.AccessorIndexNormal << std::endl; if (et.Importer_GLTF.AccessorIndexUV != e.Importer_GLTF.AccessorIndexUV) stream << PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_UV << et.Importer_GLTF.AccessorIndexUV << std::endl; if (et.Importer_GLTF.AccessorIndexIndex != e.Importer_GLTF.AccessorIndexIndex) stream << PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_INDEX << et.Importer_GLTF.AccessorIndexIndex << std::endl; if (et.bt.startingMass != e.bt.startingMass) stream << PKE_FILE_ENTITY_TYPE_PHYSICS_STARTING_MASS << et.bt.startingMass << std::endl; if (et.bt.startingCollisionLayer != e.bt.startingCollisionLayer) stream << PKE_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_LAYER << static_cast(et.bt.startingCollisionLayer) << std::endl; if (et.bt.startingCollisionMask != e.bt.startingCollisionMask) stream << PKE_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_MASK << static_cast(et.bt.startingCollisionMask) << std::endl; } void SerializeInstance(std::ofstream &stream, const CompInstance &comp) { char handleStr[19] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; snprintf(handleStr, 19, "0x%016lX",static_cast(comp.entHandle)); EntityType *et = nullptr; for (long i = 0; i < GlobalEntityTypes.Count(); ++i) { if (GlobalEntityTypes[i].grBindsHandle == comp.grBindsHandle) { et = &GlobalEntityTypes[i]; break; } } assert(et != nullptr); CompInstance c{}; InstPos baseInst{}; baseInst.posRot = btTransform{}; baseInst.posRot.setIdentity(); baseInst.scale = btVector3(1, 1, 1); baseInst.mass = 1; if (comp.entHandle != c.entHandle) stream << PKE_FILE_INSTANCE_ENTITY_HANDLE << handleStr << std::endl; stream << PKE_FILE_INSTANCE_ENTITY_TYPE_CODE << et->entityTypeCode << 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(comp.bt.rigidBody->getBroadphaseProxy()->m_collisionFilterGroup)}; PhysicsCollision collisionMask{static_cast(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(collisionLayer) << std::endl; } if (collisionMask != c.physicsMask) { stream << PKE_FILE_INSTANCE_PHYSICS_COLLISION_MASK << static_cast(collisionMask) << std::endl; } } void ParseEntityType(std::ifstream &stream) { EntityType et{}; while (stream.getline(readLine, readLineLength)) { if (strcmp(PKE_FILE_OBJ_END, readLine) == 0) { int64_t existingEntityTypeIndex = EntityType_FindByTypeCode(et.entityTypeCode); if (existingEntityTypeIndex != -1) { continue; } et.entityHandle = ECS_CreateEntity(); EntityType_Load(et); GlobalEntityTypes.Push(et); return; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_MODELS_DIR)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_MODELS_DIR); uint64_t len = strlen(readLine + prefixLen) + 1; char *val = Pke_New(len); memset(reinterpret_cast(val), '\0', len); memcpy(val, readLine + prefixLen, len); et.modelsDir = val; continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_MODEL_FILE)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_MODEL_FILE); uint64_t len = strlen(readLine + prefixLen) + 1; char *val = Pke_New(len); memset(reinterpret_cast(val), '\0', len); memcpy(val, readLine + prefixLen, len); et.modelFile = val; continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE); uint64_t len = strlen(readLine + prefixLen) + 1; char *val = Pke_New(len); memset(reinterpret_cast(val), '\0', len); memcpy(val, readLine + prefixLen, len); et.entityTypeCode = val; continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_ENTITY_HANDLE)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_ENTITY_HANDLE); EntityHandle_T handle_t; STR2NUM_ERROR result = str2num(handle_t, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); et.entityHandle = EntityHandle{handle_t}; continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT); STR2NUM_ERROR result = str2num(et.startingInstanceCount, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_VERTEX)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_VERTEX); STR2NUM_ERROR result = str2num(et.Importer_GLTF.AccessorIndexVertex, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_NORMAL)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_NORMAL); STR2NUM_ERROR result = str2num(et.Importer_GLTF.AccessorIndexNormal, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_UV)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_UV); STR2NUM_ERROR result = str2num(et.Importer_GLTF.AccessorIndexUV, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_INDEX)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_INDEX); STR2NUM_ERROR result = str2num(et.Importer_GLTF.AccessorIndexIndex, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_PHYSICS_STARTING_MASS)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_PHYSICS_STARTING_MASS); STR2NUM_ERROR result = str2num(et.bt.startingMass, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_LAYER)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_LAYER); PhysicsCollision_T val = static_cast(et.bt.startingCollisionLayer); STR2NUM_ERROR result = str2num(val, readLine + prefixLen); et.bt.startingCollisionLayer = PhysicsCollision{val}; assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_MASK)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_PHYSICS_STARTING_COLLISION_MASK); PhysicsCollision_T val = static_cast(et.bt.startingCollisionMask); STR2NUM_ERROR result = str2num(val, readLine + prefixLen); et.bt.startingCollisionMask = PhysicsCollision{val}; assert(result == STR2NUM_ERROR::SUCCESS); continue; } } } void ParseInstance(std::ifstream &stream) { CompInstance comp{}; InstPos instPos{}; instPos.posRot = btTransform{}; instPos.posRot.setIdentity(); instPos.scale = btVector3(1, 1, 1); instPos.mass = 1.f; char entTypeCode[21]; memset(reinterpret_cast(entTypeCode), '\0', 21); while (stream.getline(readLine, readLineLength)) { if (strstr(PKE_FILE_OBJ_END, readLine)) { if (entTypeCode[0] == '\0') { printf("[Game::ParseInstance] Failed to create instance from save file. No EntTypeCode present.\n"); break; } int64_t existingEntityTypeIndex = EntityType_FindByTypeCode(entTypeCode); if (existingEntityTypeIndex == -1) { printf("[Game::ParseInstance] Failed to create instance from save file. Unknown EntityTypeCode: \"%s\"", entTypeCode); break; } const auto &et = GlobalEntityTypes[existingEntityTypeIndex]; auto entityHandle = ECS_CreateEntity(); auto &compInst = ECS_CreateInstance(entityHandle, et.entityHandle); compInst.physicsLayer = comp.physicsLayer; compInst.physicsMask = comp.physicsMask; btVector3 localInertia(0, 0, 0); et.bt.shape->calculateLocalInertia(instPos.mass, localInertia); compInst.bt.motionState = Pke_New(MemBkt_Bullet); new (compInst.bt.motionState) btDefaultMotionState(instPos.posRot); compInst.bt.rigidBody = Pke_New(MemBkt_Bullet); new (compInst.bt.rigidBody) btRigidBody(instPos.mass, compInst.bt.motionState, et.bt.shape, localInertia); compInst.bt.rigidBody->setLinearVelocity(btVector3(0,0,0)); compInst.bt.rigidBody->setAngularVelocity(btVector3(0,0,0)); compInst.bt.rigidBody->getCollisionShape()->setLocalScaling(instPos.scale); BtDynamicsWorld->addRigidBody(compInst.bt.rigidBody); auto *broadphaseProxy = compInst.bt.rigidBody->getBroadphaseProxy(); broadphaseProxy->m_collisionFilterGroup = static_cast(comp.physicsLayer); broadphaseProxy->m_collisionFilterMask = static_cast(comp.physicsMask); compInst.bt.rigidBody->setUserPointer(reinterpret_cast(compInst.entHandle)); break; } if (strstr(readLine, PKE_FILE_INSTANCE_ENTITY_HANDLE)) { uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_ENTITY_HANDLE); EntityHandle_T handle_t; STR2NUM_ERROR result = str2num(handle_t, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); comp.entHandle = EntityHandle{handle_t}; continue; } if (strstr(readLine, PKE_FILE_INSTANCE_ENTITY_TYPE_CODE)) { uint64_t prefixLen = strlen(PKE_FILE_INSTANCE_ENTITY_TYPE_CODE); uint64_t len = strlen(readLine + prefixLen); memcpy(entTypeCode, readLine + prefixLen, len); 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 != ']'); instPos.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 != ']'); instPos.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(instPos.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(instPos.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(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(comp.physicsMask); STR2NUM_ERROR result = str2num(val, readLine + prefixLen); comp.physicsMask = PhysicsCollision{val}; assert(result == STR2NUM_ERROR::SUCCESS); continue; } } } void SaveSceneFile(const char *sceneFilePath) { std::ofstream f(sceneFilePath); assert(f.is_open()); f << PKE_FILE_BEGIN << std::endl; f << PKE_FILE_VERSION << std::endl; f << "" << std::endl; for (long i = 0; i < GlobalEntityTypes.Count(); ++i) { f << PKE_FILE_OBJ_ENTITY_TYPE << std::endl; const auto &et = GlobalEntityTypes[i]; const CompGrBinds *grBinds = ECS_GetGrBinds(et.entityHandle); // TODO ignore if no instances if (grBinds == nullptr) { continue; } SerializeEntityType(f, et); f << PKE_FILE_OBJ_END << std::endl; } int64_t instanceBucketCount = ECS_GetInstances_BucketCount(); for (long b = 0; b < instanceBucketCount; ++b) { uint64_t count; auto *instances = ECS_GetInstances(b, count); for (long i = 0; i < count; ++i) { const auto &instance = instances[i]; if (instance.entHandle == EntityHandle_MAX) continue; f << PKE_FILE_OBJ_INSTANCE << std::endl; SerializeInstance(f, instance); f << PKE_FILE_OBJ_END << std::endl; } } f << PKE_FILE_END << std::endl; f.flush(); f.close(); } void LoadSceneFile(const char *sceneFilePath) { std::ifstream f(sceneFilePath); assert(f.is_open()); memset(readLine, '\0', readLineLength); while (f.getline(readLine, readLineLength)) { // EntityTypes if (strcmp(PKE_FILE_OBJ_ENTITY_TYPE, readLine) == 0) { ParseEntityType(f); continue; } if (strcmp(PKE_FILE_OBJ_INSTANCE, readLine) == 0) { ParseInstance(f); continue; } } f.close(); } void setupEditor() { shouldInitEditor = false; PkeInput_ActivateSet(debugControlsHandle); } void teardownEditor() { shouldTeardownEditor = false; PkeInput_DeactivateSet(debugControlsHandle); } glm::vec3 unproject(glm::vec3 windowCoords) { double xDevNorm = (2.0f * windowCoords.x) / Extent.width - 1.0f; double yDevNorm = 1.0f - (2.0f * windowCoords.y) / Extent.height; yDevNorm *= -1; glm::vec4 clipCoords(xDevNorm, yDevNorm, windowCoords.z, 1.f); glm::vec4 rayEye = glm::inverse(UBO.proj) * clipCoords; glm::vec4 worldCoords = glm::inverse(UBO.model) * glm::inverse(UBO.view) * rayEye; if (std::abs(0.0f - worldCoords.w) > 0.00001f) { worldCoords *= 1.0f / worldCoords.w; } return glm::vec3(worldCoords); } void Game_Tick(double delta) { Pke_ResetBucket(pkeSettings.mem.bkt); /* * ECS_Tick() gets called first because it updates the public * `EntitiesToBeRemoved` for all other ticks to use. */ if (shouldInitEditor) { setupEditor(); } if (shouldUnlockCamera) { shouldUnlockCamera = false; glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); cameraDbg.pos = ActiveCamera->pos; if (ActiveCamera->orientation == cameraDbg.orientation) { cameraDbg.rot = ActiveCamera->rot; } else { cameraDbg.rot = glm::quat(UBO.view); } cameraDbg.stale = PKE_CAMERA_STALE_ALL; ActiveCamera = &cameraDbg; } ECS_Tick_Early(delta); if (EntitiesToBeRemoved.Has(selectedEntity)) { selectedEntity = EntityHandle_MAX; } if (EntitiesToBeRemoved.Has(hoveredEntity)) { hoveredEntity = EntityHandle_MAX; } ECS_Tick(delta); PkeInput_Tick(delta); bool imGuiHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow); if (selectedEntity != EntityHandle_MAX && !imGuiHovered) { auto holder = PkeInput_Query(dbgCtrl_ClearSelection); if (holder.type != InputEventHash{0}) { const PkeMouseButtonEvent *mbEvent = static_cast(holder.ptr); if (mbEvent->isPressed) { selectedEntity = EntityHandle_MAX; } } } // raycast for hovering hoveredEntity = EntityHandle_MAX; if (!imGuiHovered) { double xMousePos, yMousePos; glfwGetCursorPos(window, &xMousePos, &yMousePos); glm::vec3 fromCoords{unproject(glm::vec3(xMousePos, yMousePos, -1.f))}; glm::vec3 toCoords{unproject(glm::vec3(xMousePos, yMousePos, 1.f))}; btVector3 rayOrigin{}; btVector3 rayDestination{}; GlmToBullet(fromCoords, rayOrigin); GlmToBullet(toCoords, rayDestination); btCollisionWorld::ClosestRayResultCallback rayResult{rayOrigin, rayDestination}; rayResult.m_flags |= btTriangleRaycastCallback::kF_FilterBackfaces; BtDynamicsWorld->rayTest(rayOrigin, rayDestination, rayResult); if (rayResult.hasHit()) { EntityHandle_T hoveredEntity_T{reinterpret_cast(rayResult.m_collisionObject->getUserPointer())}; hoveredEntity = EntityHandle{hoveredEntity_T}; } } if (hoveredEntity != EntityHandle_MAX) { auto holder = PkeInput_Query(dbgCtrl_SelectHovered); if (holder.type != InputEventHash{0}) { const PkeMouseButtonEvent *mbEvent = static_cast(holder.ptr); if (mbEvent->isPressed) { selectedEntity = hoveredEntity; } } } EntityHandle focusedHandle = selectedEntity != EntityHandle_MAX ? selectedEntity : hoveredEntity; if (focusedHandle != EntityHandle_MAX) { const auto *inst = ECS_GetInstance(focusedHandle); const auto *grBinds = ECS_GetGrBinds(inst->grBindsHandle); pkeDebugHitbox.instanceBuffer = grBinds->instanceBuffer; pkeDebugHitbox.instanceStartingIndex = inst->index; } else { pkeDebugHitbox.instanceBuffer = VK_NULL_HANDLE; pkeDebugHitbox.instanceStartingIndex = 0; } if (shouldCreateEntityType) { assert(entityTypeToCreate != nullptr); assert(entityTypeToCreate != CAFE_BABE(EntityType)); assert(entityTypeToCreate->entityHandle == EntityHandle_MAX); // TODO this needs to be more elegant int64_t existingEntityTypeIndex = EntityType_FindByTypeCode(entityTypeToCreate->entityTypeCode); if (existingEntityTypeIndex == -1) { entityTypeToCreate->entityHandle = ECS_CreateEntity(); EntityType_Load(*entityTypeToCreate); GlobalEntityTypes.Push(*entityTypeToCreate); } Pke_Delete(entityTypeToCreate); shouldCreateEntityType = false; } while (entityInstancesToCreate.Count() > 0) { auto createInfo = entityInstancesToCreate.Pop(); // TODO needs to be more elegant int64_t etIndex = EntityType_FindByEntityHandle(createInfo.entityTypeEntityHandle); auto &et = GlobalEntityTypes[etIndex]; EntityHandle newEntity = ECS_CreateEntity(); auto &compInst = ECS_CreateInstance(newEntity, createInfo.entityTypeEntityHandle); compInst.physicsLayer = et.bt.startingCollisionLayer; compInst.physicsMask = et.bt.startingCollisionMask; btVector3 localInertia(0, 0, 0); et.bt.shape->calculateLocalInertia(et.bt.startingMass, localInertia); btTransform posRot{}; posRot.setIdentity(); compInst.bt.motionState = Pke_New(MemBkt_Bullet); new (compInst.bt.motionState) btDefaultMotionState(posRot); compInst.bt.rigidBody = Pke_New(MemBkt_Bullet); new (compInst.bt.rigidBody) btRigidBody(et.bt.startingMass, compInst.bt.motionState, et.bt.shape, localInertia); compInst.bt.rigidBody->setLinearVelocity(btVector3(0,0,0)); compInst.bt.rigidBody->setAngularVelocity(btVector3(0,0,0)); compInst.bt.rigidBody->getCollisionShape()->setLocalScaling(btVector3(1, 1, 1)); BtDynamicsWorld->addRigidBody(compInst.bt.rigidBody); compInst.bt.rigidBody->getBroadphaseProxy()->m_collisionFilterGroup = static_cast(et.bt.startingCollisionLayer); compInst.bt.rigidBody->getBroadphaseProxy()->m_collisionFilterMask = static_cast(et.bt.startingCollisionMask); compInst.bt.rigidBody->setUserPointer(reinterpret_cast(compInst.entHandle)); } PkeInputEventHolder holder = PkeInput_Query(dbgCtrl_UnlockCamera); if (holder.type != InputEventHash{0}) { PkeKeyEvent *keyEsc; keyEsc = static_cast(holder.ptr); if (keyEsc->isPressed || keyEsc->pressCount > 0) { pkeSettings.editorSettings.isUsingDebugCamera = false; shouldLockCamera = true; } } if (shouldLockCamera) { shouldLockCamera = false; glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); cameraDefault.stale = PKE_CAMERA_STALE_ALL; ActiveCamera = &cameraDefault; } if (pkeSettings.editorSettings.isUsingDebugCamera) { holder = PkeInput_Query(dbgCtrl_CameraRot); if (holder.type != InputEventHash{0}) { const PkeCursorPosEvent *posEvent = static_cast(holder.ptr); if (posEvent->xMotion || posEvent->yMotion) { glm::vec3 axis1Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(1.f, 0.f, 0.f); glm::vec3 axis2Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(0.f, 1.f, 0.f); // glm::vec3 axis1Heading = cameraDbg.rot * glm::vec3(1.f, 0.f, 0.f); // glm::vec3 axis2Heading = cameraDbg.rot * glm::vec3(0.f, 1.f, 0.f); glm::quat pitch = glm::angleAxis(float(posEvent->yMotion) * 0.01f, glm::normalize(axis1Heading)); glm::quat yaw = glm::angleAxis(float(posEvent->xMotion) * 0.01f, glm::normalize(axis2Heading)); glm::quat rot = cameraDbg.rot * pitch * yaw; // rot = glm::normalize(rot); auto eul = glm::eulerAngles(rot); cameraDbg.rot = glm::quat(eul); cameraDbg.stale = cameraDbg.stale | PKE_CAMERA_STALE_ROT; } } double leftCount = 0; double rightCount = 0; double forwardCount = 0; double backCount = 0; double upCount = 0; double downCount = 0; double rotCCCount = 0; double rotCCount = 0; holder = PkeInput_Query(dbgCtrl_CameraLeft); if (holder.type != InputEventHash{0}) { PkeKeyEvent *keyLeft; keyLeft = static_cast(holder.ptr); leftCount = keyLeft->isPressed ? 1 : 0; } holder = PkeInput_Query(dbgCtrl_CameraRight); if (holder.type != InputEventHash{0}) { PkeKeyEvent *keyRight; keyRight = static_cast(holder.ptr); rightCount = keyRight->isPressed ? 1 : 0; } holder = PkeInput_Query(dbgCtrl_CameraForward); if (holder.type != InputEventHash{0}) { PkeKeyEvent *keyForward; keyForward = static_cast(holder.ptr); forwardCount = keyForward->isPressed ? 1 : 0; } holder = PkeInput_Query(dbgCtrl_CameraBack); if (holder.type != InputEventHash{0}) { PkeKeyEvent *keyBack; keyBack = static_cast(holder.ptr); backCount = keyBack->isPressed ? 1 : 0; } holder = PkeInput_Query(dbgCtrl_CameraUp); if (holder.type != InputEventHash{0}) { PkeKeyEvent *keyUp; keyUp = static_cast(holder.ptr); upCount = keyUp->isPressed ? 1 : 0; } holder = PkeInput_Query(dbgCtrl_CameraDown); if (holder.type != InputEventHash{0}) { PkeKeyEvent *keyDown; keyDown = static_cast(holder.ptr); downCount = keyDown->isPressed ? 1 : 0; } holder = PkeInput_Query(dbgCtrl_CameraRotCC); if (holder.type != InputEventHash{0}) { PkeKeyEvent *keyRotCC; keyRotCC = static_cast(holder.ptr); rotCCCount = keyRotCC->isPressed ? 1 : 0; } holder = PkeInput_Query(dbgCtrl_CameraRotC); if (holder.type != InputEventHash{0}) { PkeKeyEvent *keyRotC; keyRotC = static_cast(holder.ptr); rotCCount = keyRotC->isPressed ? 1 : 0; } double accelerated = delta * 10.f; double axis1 = -(leftCount * accelerated) + (rightCount * accelerated); double axis2 = (forwardCount * accelerated) + -(backCount * accelerated); double axis3 = -(upCount * accelerated) + (downCount * accelerated); if (axis1 != 0 || axis2 != 0 || axis3 != 0) { glm::vec3 axis1Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(-axis1, 0.f, 0.f); glm::vec3 axis2Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(0.f, 0.f, axis2); glm::vec3 axis3Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(0.f, axis3, 0.f); cameraDbg.pos += glm::vec3(axis1Heading + axis2Heading + axis3Heading); cameraDbg.stale = cameraDbg.stale | PKE_CAMERA_STALE_POS; } double axis4 = -(rotCCCount * delta) + (rotCCount * delta); if (axis4 != 0.0) { cameraDbg.rot = glm::quat(glm::vec3(0.f, 0.f, axis4)) * cameraDbg.rot; cameraDbg.stale = cameraDbg.stale | PKE_CAMERA_STALE_ROT; } } EntityType_Tick_Late(delta); ECS_Tick_Late(delta); if (shouldTeardownEditor) { teardownEditor(); } } void RecordImGui_GLM(const char *label, glm::mat4 &mat) { float min = -1; float max = 1; for (long i = 0; i < 4; ++i) { ImGui::PushID(i); ImGui::SliderFloat4(label, &mat[i][0], min, max, "%.3f", ImGuiSliderFlags_NoRoundToFormat); ImGui::PopID(); } } void RecordImGuiEditorWrapper() { ImGui::DockSpaceOverViewport(nullptr, ImGuiDockNodeFlags_PassthruCentralNode); ImGui::BeginMainMenuBar(); if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Exit")) { glfwSetWindowShouldClose(window, true); } ImGui::EndMenu(); } if (ImGui::BeginMenu("Debug")) { ImGui::Checkbox("Show Debug Hitboxes", &pkeSettings.isRenderingDebug); ImGui::Checkbox("Pause Physics Simulation", &pkeSettings.isSimulationPaused); if (ImGui::Checkbox("Uncap Framerate", &pkeSettings.graphicsSettings.isFramerateUnlocked)) { shouldRecreateSwapchain = true; } if (ImGui::Checkbox("wait for v-sync", &pkeSettings.graphicsSettings.isWaitingForVsync)) { shouldRecreateSwapchain = true; } // ImGui::Checkbox("Uncap Tickrate", &pkeSettings.isTickrateUnlocked); if (ImGui::Checkbox("Use Debug Camera", &pkeSettings.editorSettings.isUsingDebugCamera)) { if (pkeSettings.editorSettings.isUsingDebugCamera) { shouldUnlockCamera = true; } else { shouldLockCamera = true; } } ImGuiIO &io = ImGui::GetIO(); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::EndMenu(); } ImGui::EndMainMenuBar(); } void RecordImGuiConsole() { static bool scrollToBottom = true; if (!ImGui::Begin("Console", &pkeSettings.editorSettings.isShowingConsole)) { ImGui::End(); return; } ImVec2 region = ImGui::GetContentRegionAvail(); region.y -= 27; if (ImGui::BeginListBox("##ConsoleHistory", region)) { for (long i = consoleBufferIndex + 1; i < consoleBufferCount; ++i) { ImGui::Text("%s", consoleBuffer[i]); } for (long i = 0; i < consoleBufferIndex; ++i) { ImGui::Text("%s", consoleBuffer[i]); } if (scrollToBottom) ImGui::SetScrollHereY(1); scrollToBottom = false; ImGui::EndListBox(); } ImGui::Separator(); if (ImGui::InputText("##ConsoleInput", consoleBuffer[consoleBufferIndex], consoleLineLength, ImGuiInputTextFlags_EnterReturnsTrue)) { // TODO parse and execute. scrollToBottom = true; consoleBufferIndex = (consoleBufferIndex + 1) % consoleBufferCount; memset(consoleBuffer[consoleBufferIndex], '\0', consoleLineLength); } auto focusedFlags = (ImGuiFocusedFlags_ChildWindows); if (ImGui::IsWindowFocused(focusedFlags) && !ImGui::IsAnyItemFocused() && !ImGui::IsAnyItemHovered() && !ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !ImGui::IsMouseClicked(ImGuiMouseButton_Left, true)) { ImGui::SetKeyboardFocusHere(-1); } ImGui::End(); } void RecordImGuiEntityList() { if (!ImGui::Begin("EntityList", &pkeSettings.editorSettings.isShowingEntityList)) { ImGui::End(); return; } static ImGuiTableFlags tableFlags{ ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg }; if (ImGui::BeginTable("Entities", 7, tableFlags)) { ImGui::TableSetupColumn("Select"); ImGui::TableSetupColumn("EntityHandle"); ImGui::TableSetupColumn("ParentEntityHandle"); ImGui::TableSetupColumn("GrBindsHandle"); ImGui::TableSetupColumn("InstanceHandle"); ImGui::TableSetupColumn("IsMarkedForRemoval"); ImGui::TableSetupColumn("Delete"); ImGui::TableHeadersRow(); uint64_t bucketCount = ECS_GetEntities_BucketCount(); for (long bucket = 0; bucket < bucketCount; ++bucket) { uint64_t itemCount = 0; auto *entities = ECS_GetEntities(bucket, itemCount); ImGui::PushID(bucket); for (long row = 0; row < itemCount; row++) { auto *entity = &entities[row]; ImGui::PushID(row); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::BeginDisabled(selectedEntity == entity->handle); if (ImGui::Button("Select")) selectedEntity = entity->handle; ImGui::EndDisabled(); ImGui::TableSetColumnIndex(1); ImGui::Text("0x%016lX", static_cast(entity->handle)); ImGui::TableSetColumnIndex(2); ImGui::Text("0x%016lX", static_cast(entity->parentHandle)); ImGui::TableSetColumnIndex(3); ImGui::Text("0x%016lX", static_cast(entity->grBindsHandle)); ImGui::TableSetColumnIndex(4); ImGui::Text("0x%016lX", static_cast(entity->instanceHandle)); ImGui::TableSetColumnIndex(5); ImGui::Text("%u", entity->isMarkedForRemoval); ImGui::TableSetColumnIndex(6); ImGui::BeginDisabled(selectedEntity != entity->handle); if (ImGui::Button("Delete")) ECS_MarkForRemoval(entity->handle); ImGui::EndDisabled(); ImGui::PopID(); } ImGui::PopID(); } ImGui::EndTable(); } ImGui::End(); } void RecordImGuiUBO() { if (!ImGui::Begin("UBO", &pkeSettings.editorSettings.isShowingUBO)) { ImGui::End(); return; } ImGui::Text("Model"); RecordImGui_GLM("UBO-model", UBO.model); ImGui::Text("View"); RecordImGui_GLM("UBO-view", UBO.view); ImGui::Text("Proj"); RecordImGui_GLM("UBO-proj", UBO.proj); ImGui::End(); } void RecordImGuiModalCreateEntityType() { if (ImGui::BeginPopupModal("CreateEntityType", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { static char modelsDir[64]; static char modelFile[64]; static char entityTypeCode[32]; ImGui::InputText("Models Dir", modelsDir, 63); ImGui::InputText("Model File", modelFile, 63); ImGui::InputText("Entity Type Code", entityTypeCode, 31); ImGui::InputScalar("Starting Instance Count", ImGuiDataType_U32, &entityTypeToCreate->startingInstanceCount); ImGui::InputScalar("GLTF Import Index - Vertex", ImGuiDataType_S16, &entityTypeToCreate->Importer_GLTF.AccessorIndexVertex); ImGui::InputScalar("GLTF Import Index - Normal", ImGuiDataType_S16, &entityTypeToCreate->Importer_GLTF.AccessorIndexNormal); ImGui::InputScalar("GLTF Import Index - UV", ImGuiDataType_S16, &entityTypeToCreate->Importer_GLTF.AccessorIndexUV); ImGui::InputScalar("GLTF Import Index - Index", ImGuiDataType_S16, &entityTypeToCreate->Importer_GLTF.AccessorIndexIndex); ImGui::InputFloat("Physics - Mass", &entityTypeToCreate->bt.startingMass); ImGui::InputScalar("Physics - Collision Layer", ImGuiDataType_U16, &entityTypeToCreate->bt.startingCollisionLayer); ImGui::InputScalar("Physics - Collision Mask", ImGuiDataType_U16, &entityTypeToCreate->bt.startingCollisionMask); ImGui::Separator(); if (ImGui::Button("Create")) { // TODO some type of validation memset(modelsDir, '\0', 64); memset(modelFile, '\0', 64); memset(entityTypeCode, '\0', 32); assert(entityTypeToCreate == nullptr || entityTypeToCreate == CAFE_BABE(EntityType)); entityTypeToCreate = Pke_New(); char *sModelsDir = Pke_New(strlen(modelsDir) + 1); strncpy(sModelsDir, modelsDir, 63); entityTypeToCreate->modelsDir = sModelsDir; char *sModelFile = Pke_New(strlen(modelFile) + 1); strncpy(sModelFile, modelFile, 63); entityTypeToCreate->modelFile = sModelFile; char *sEntityTypeCode = Pke_New(strlen(entityTypeCode) + 1); strncpy(sEntityTypeCode, entityTypeCode, 31); entityTypeToCreate->entityTypeCode = sEntityTypeCode; shouldCreateEntityType = true; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Cancel")) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } } void RecordImGui_CompGrBinds(bool readonly, CompGrBinds *component) { if (component == nullptr) return; int inputTextFlags = 0; if (readonly) inputTextFlags |= ImGuiInputTextFlags_ReadOnly; ImGui::Text("CompGRBinds"); ImGui::Separator(); if (ImGui::Button("Create Instance")) { entityInstancesToCreate.Push({ .entityTypeEntityHandle = component->entHandle, }); } if (component->vkPipelineLayout) ImGui::InputScalar("VkPipelineLayout", ImGuiDataType_U64, &component->vkPipelineLayout, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); if (component->vkDescriptorSets){ ImGui::InputScalarN("VkPipelineDescriptorSets", ImGuiDataType_U64, &component->vkDescriptorSets, MAX_FRAMES_IN_FLIGHT, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); } if (component->vertexBuffer) ImGui::InputScalar("VkVertexBuffer", ImGuiDataType_U64, &component->vertexBuffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("VertexFirstBinding", ImGuiDataType_U32, &component->vertexFirstBinding, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("VertexBindingCount", ImGuiDataType_U32, &component->vertexBindingCount, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("VertexOffsets", ImGuiDataType_U64, &component->vertexOffsets, nullptr, nullptr, nullptr, inputTextFlags); if (component->normalsBuffer) ImGui::InputScalar("VkNormalBuffer", ImGuiDataType_U64, &component->normalsBuffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("NormalFirstBinding", ImGuiDataType_U32, &component->normalsFirstBinding, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("NormalBindingCount", ImGuiDataType_U32, &component->normalsBindingCount, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("NormalOffsets", ImGuiDataType_U64, &component->normalsOffsets, nullptr, nullptr, nullptr, inputTextFlags); if (component->uvBuffer) ImGui::InputScalar("VkUVBuffer", ImGuiDataType_U64, &component->uvBuffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("UVFirstBinding", ImGuiDataType_U32, &component->uvFirstBinding, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("UVBindingCount", ImGuiDataType_U32, &component->uvBindingCount, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("UVOffsets", ImGuiDataType_U64, &component->uvOffsets, nullptr, nullptr, nullptr, inputTextFlags); if (component->indexBuffer) ImGui::InputScalar("VkIndexBuffer", ImGuiDataType_U64, &component->indexBuffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("IndexBindingCount", ImGuiDataType_U32, &component->indexBindingCount, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("IndexOffsets", ImGuiDataType_U64, &component->indexOffsets, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("IndexCount", ImGuiDataType_U32, &component->indexCount, nullptr, nullptr, nullptr, inputTextFlags); if (component->instanceBuffer) ImGui::InputScalar("VkInstanceBuffer", ImGuiDataType_U64, &component->instanceBuffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("InstanceFirstBinding", ImGuiDataType_U32, &component->instanceFirstBinding, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("InstanceBindingCount", ImGuiDataType_U32, &component->instanceBindingCount, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("InstanceOffsets", ImGuiDataType_U64, &component->instanceOffsets, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("InstanceBufferMaxCount", ImGuiDataType_U32, &component->instanceBufferMaxCount, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("Instance Count", ImGuiDataType_U32, &component->instanceCounter, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly); ImGui::Spacing(); } void RecordImGui_CompInstPos(bool readonly, CompInstance *component) { if (component == nullptr) return; int inputTextFlags = 0; if (readonly) inputTextFlags |= ImGuiInputTextFlags_ReadOnly; ImGui::Text("CompInststance + InstPos"); ImGui::Separator(); bool changed = false; InstPos instPos; component->bt.motionState->getWorldTransform(instPos.posRot); instPos.scale = component->bt.rigidBody->getCollisionShape()->getLocalScaling(); instPos.mass = component->bt.rigidBody->getMass(); btVector3 pos = instPos.posRot.getOrigin(); btQuaternion rot = instPos.posRot.getRotation(); glm::vec3 eul; rot.getEulerZYX(eul.z, eul.y, eul.x); eul = glm::degrees(eul); changed = ImGui::InputScalar("Instance Index", ImGuiDataType_U64, &component->index, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly) || changed; changed = ImGui::InputScalarN("pos", ImGuiDataType_Float, &pos, 3, nullptr, nullptr, nullptr, inputTextFlags) || changed; changed = ImGui::InputScalarN("rot (eul)", ImGuiDataType_Float, &eul, 3, nullptr, nullptr, nullptr, inputTextFlags) || changed; changed = ImGui::InputScalarN("scale", ImGuiDataType_Float, &instPos.scale, 3, nullptr, nullptr, nullptr, inputTextFlags) || changed; changed = ImGui::InputFloat("mass", &instPos.mass, 0.0, 0.0, "%.3f", inputTextFlags) || changed; changed = ImGui::InputScalar("Phys - Collision Layer", ImGuiDataType_U16, &component->physicsLayer, nullptr, nullptr, nullptr, inputTextFlags) || changed; changed = ImGui::InputScalar("Phys - Collision Mask", ImGuiDataType_U16, &component->physicsMask, nullptr, nullptr, nullptr, inputTextFlags) || changed; ImGui::InputScalar("Phys - Rigid Body", ImGuiDataType_U64, &component->bt.rigidBody, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("Phys - Motion State", ImGuiDataType_U64, &component->bt.motionState, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); if (changed) { instPos.posRot.setOrigin(pos); eul = glm::radians(eul); rot.setEulerZYX(eul.z, eul.y, eul.x); instPos.posRot.setRotation(rot); auto *broadphase = component->bt.rigidBody->getBroadphaseProxy(); broadphase->m_collisionFilterGroup = static_cast(component->physicsLayer); broadphase->m_collisionFilterMask = static_cast(component->physicsMask); ECS_UpdateInstance(component->entHandle, instPos, true); } ImGui::Spacing(); } void RecordImGuiSceneEditor() { { if (!ImGui::Begin("SceneEditorEntityTypes", &pkeSettings.editorSettings.isShowingSceneEditor)) { ImGui::End(); return; } if (ImGui::Button("Create Entity Type")) { ImGui::OpenPopup("CreateEntityType"); } if (ImGui::Button("Save")) { SaveSceneFile("test.pstf"); } if (ImGui::Button("Load")) { LoadSceneFile("test.pstf"); } if (ImGui::Button("Clear Selection")) { selectedEntity = EntityHandle_MAX; } ImGui::Spacing(); if (selectedEntity != EntityHandle_MAX) { RecordImGui_CompGrBinds(true, ECS_GetGrBinds(selectedEntity)); RecordImGui_CompInstPos(false, ECS_GetInstance(selectedEntity)); } RecordImGuiModalCreateEntityType(); ImGui::End(); } } void RecordImGuiEditor() { if (pkeSettings.isShowingEditor) { RecordImGuiEditorWrapper(); RecordImGuiConsole(); RecordImGuiEntityList(); RecordImGuiSceneEditor(); RecordImGuiUBO(); } } void Game_Init() { pkeSettings.mem.bkt = Pke_BeginTransientBucket(1UL << 26); for (long i = 0; i < consoleBufferCount; ++i) { memset(consoleBuffer[i], '\0', consoleLineLength); } Event_RegisterCallback("RenderImGui", RecordImGuiEditor); PkeInputSet debugControlsSet; debugControlsSet.title = "debug-editor-controls"; debugControlsSet.actionCount = 12; debugControlsSet.actions = Pke_New(debugControlsSet.actionCount); debugControlsSet.actions[0].name = dbgCtrl_CameraLeft; debugControlsSet.actions[0].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_A, }; debugControlsSet.actions[1].name = dbgCtrl_CameraRight; debugControlsSet.actions[1].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_D, }; debugControlsSet.actions[2].name = dbgCtrl_CameraForward; debugControlsSet.actions[2].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_W, }; debugControlsSet.actions[3].name = dbgCtrl_CameraBack; debugControlsSet.actions[3].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_S, }; debugControlsSet.actions[4].name = dbgCtrl_CameraUp; debugControlsSet.actions[4].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_R, }; debugControlsSet.actions[5].name = dbgCtrl_CameraDown; debugControlsSet.actions[5].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_F, }; debugControlsSet.actions[6].name = dbgCtrl_CameraRotCC; debugControlsSet.actions[6].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_Q, }; debugControlsSet.actions[7].name = dbgCtrl_CameraRotC; debugControlsSet.actions[7].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_E, }; debugControlsSet.actions[8].name = dbgCtrl_CameraRot; debugControlsSet.actions[8].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_CURSOR_POS_EVENTS, }; debugControlsSet.actions[9].name = dbgCtrl_UnlockCamera; debugControlsSet.actions[9].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_ESCAPE, }; debugControlsSet.actions[10].name = dbgCtrl_SelectHovered; debugControlsSet.actions[10].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_MOUSE_BUTTON_EVENTS, .button = GLFW_MOUSE_BUTTON_LEFT, }; debugControlsSet.actions[11].name = dbgCtrl_ClearSelection; debugControlsSet.actions[11].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_MOUSE_BUTTON_EVENTS, .button = GLFW_MOUSE_BUTTON_RIGHT, }; debugControlsHandle = PkeInput_RegisterSet(debugControlsSet); ActiveCamera = &cameraDefault; } void Game_Teardown() { PkeInput_UnregisterSet(debugControlsHandle); entityInstancesToCreate.~DynArray(); }