From e2e1d4646fa3a196b6247ba2dc04603d393df62f Mon Sep 17 00:00:00 2001 From: Jonathan Bradley Date: Wed, 6 Dec 2023 22:38:14 -0500 Subject: large camera refactor for saving and ui --- editor/editor.cpp | 190 ++++++++++++++++++++++++++++++++++++++++++++---------- src/game.cpp | 135 +++++++++++++++++++++++++++++++++++++- 2 files changed, 288 insertions(+), 37 deletions(-) diff --git a/editor/editor.cpp b/editor/editor.cpp index 4fab198..1786a18 100644 --- a/editor/editor.cpp +++ b/editor/editor.cpp @@ -24,7 +24,7 @@ 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_CameraButtonMask = "debug-camera-button-mask"; const char *dbgCtrl_SelectHovered = "debug-select-hovered"; const char *dbgCtrl_ClearSelection = "debug-clear-selection"; @@ -42,8 +42,6 @@ PkeCamera cameraDbg { InputActionSetHandle debugControlsHandle = InputActionSetHandle_MAX; bool shouldSetupEditor = true; bool shouldDisableEditor = false; -bool shouldLockCamera = false; -bool shouldUnlockCamera = false; struct EntityTypeInstanceCreateInfo { EntityHandle entityTypeEntityHandle; @@ -53,6 +51,7 @@ EntityHandle selectedEntity = EntityHandle_MAX; EntityHandle hoveredEntity = EntityHandle_MAX; bool shouldCreateEntityType = false; EntityType entityTypeToCreate{}; +CameraHandle selectedCamera = CameraHandle_MAX; char *sceneName = nullptr; bool shouldOpenLoadSceneDialog = false; @@ -99,17 +98,9 @@ void PkeEditor_Tick(double delta) { Game_LoadSceneFile(sceneName); } - 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; + if (shouldSaveScene && sceneName) { + shouldSaveScene = false; + Game_SaveSceneFile(sceneName); } if (EntitiesToBeRemoved.Has(selectedEntity)) { @@ -219,24 +210,33 @@ void PkeEditor_Tick(double delta) { compInst.bt.rigidBody->setUserPointer(reinterpret_cast(compInst.entHandle)); } - PkeInputEventHolder holder = PkeInput_Query(dbgCtrl_UnlockCamera); + PkeInputEventHolder holder = PkeInput_Query(dbgCtrl_CameraButtonMask); if (holder.type != InputEventHash{0}) { - PkeKeyEvent *keyEsc; - keyEsc = static_cast(holder.ptr); - if (keyEsc->isPressed || keyEsc->pressCount > 0) { - pkeSettings.editorSettings.isUsingDebugCamera = false; - shouldLockCamera = true; + PkeMouseButtonEvent *toggleCameraMovement; + toggleCameraMovement = static_cast(holder.ptr); + if (toggleCameraMovement->thisTick) { + if (toggleCameraMovement->isPressed && pkeSettings.editorSettings.isUsingDebugCamera == false) { + pkeSettings.editorSettings.isUsingDebugCamera = true; + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + selectedCamera = CameraHandle_MAX; + if (ActiveCamera != &cameraDbg) { + 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; + } + } else if (toggleCameraMovement->isPressed && pkeSettings.editorSettings.isUsingDebugCamera == true) { + pkeSettings.editorSettings.isUsingDebugCamera = false; + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } } } - if (shouldLockCamera) { - shouldLockCamera = false; - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - NullCamera.stale = PKE_CAMERA_STALE_ALL; - ActiveCamera = &NullCamera; - } - - if (pkeSettings.editorSettings.isUsingDebugCamera) { + if (pkeSettings.editorSettings.isUsingDebugCamera && ActiveCamera == &cameraDbg) { holder = PkeInput_Query(dbgCtrl_CameraRot); if (holder.type != InputEventHash{0}) { const PkeCursorPosEvent *posEvent = static_cast(holder.ptr); @@ -371,10 +371,10 @@ void RecordImGuiEditorWrapper() { // ImGui::Checkbox("Uncap Tickrate", &pkeSettings.isTickrateUnlocked); if (ImGui::Checkbox("Use Debug Camera", &pkeSettings.editorSettings.isUsingDebugCamera)) { if (pkeSettings.editorSettings.isUsingDebugCamera) { - shouldUnlockCamera = true; + // shouldUnlockCamera = true; ImGui::CloseCurrentPopup(); } else { - shouldLockCamera = true; + // shouldLockCamera = true; } } @@ -443,6 +443,120 @@ void RecordImGuiEntityList() { ImGui::End(); } +void RecordImGuiCameras() { + if (!ImGui::Begin("Cameras")) { + ImGui::End(); + return; + } + if (ImGui::Button("Create")) { + auto &cam = PkeCamera_Register(); + cam.pos = ActiveCamera->pos; + cam.rot = ActiveCamera->rot; + cam.target = ActiveCamera->target; + cam.type = ActiveCamera->type; + cam.orientation = ActiveCamera->orientation; + } + + static ImGuiTableFlags tableFlags{ + ImGuiTableFlags_Borders | + ImGuiTableFlags_RowBg + }; + if (ImGui::BeginTable("Entities", 8, tableFlags)) { + ImGui::TableSetupColumn("Interact"); + ImGui::TableSetupColumn("CameraHandle"); + ImGui::TableSetupColumn("Pos"); + ImGui::TableSetupColumn("Rot"); + ImGui::TableSetupColumn("Target"); + ImGui::TableSetupColumn("Type"); + ImGui::TableSetupColumn("Orientation"); + ImGui::TableSetupColumn("Stale"); + ImGui::TableHeadersRow(); + + int64_t cameraBucketCount = PkeCamera_GetBucketCount(); + for (long b = 0; b < cameraBucketCount; ++b) { + int64_t count; + auto *cameras = PkeCamera_GetCameras(b, count); + ImGui::PushID(b); + for (long i = 0; i < count; ++i) { + const auto &cam = cameras[i]; + if (cam.handle == CameraHandle_MAX) + continue; + ImGui::PushID(i); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::BeginDisabled(selectedCamera == cam.handle); + if (ImGui::Button("Select")) { + selectedCamera = cam.handle; + ActiveCamera = const_cast(&cam); + ActiveCamera->stale = PKE_CAMERA_STALE_ALL; + } + ImGui::EndDisabled(); + ImGui::TableSetColumnIndex(1); + ImGui::Text("0x%016lX", static_cast(cam.handle)); + ImGui::TableSetColumnIndex(2); + ImGui::Text("%4.2f,%4.2f,%4.2f", cam.pos[0], cam.pos[1], cam.pos[2]); + ImGui::TableSetColumnIndex(3); + ImGui::Text("%4.2f,%4.2f,%4.2f,%4.2f", cam.rot[0], cam.rot[1], cam.rot[2], cam.rot[3]); + ImGui::TableSetColumnIndex(4); + ImGui::Text("%4.2f,%4.2f,%4.2f", cam.target[0], cam.target[1], cam.target[2]); + ImGui::TableSetColumnIndex(5); + ImGui::Text("%hhu", cam.type); + ImGui::TableSetColumnIndex(6); + ImGui::Text("%hhu", cam.orientation); + ImGui::TableSetColumnIndex(7); + ImGui::Text("%hhu", cam.stale); + ImGui::PopID(); + } + ImGui::PopID(); + } + ImGui::EndTable(); + } + + if (selectedCamera != CameraHandle_MAX) { + int inputTextFlags = ImGuiInputTextFlags_ReadOnly; + auto *cam = PkeCamera_Get(selectedCamera); + if (cam) { + bool isOrientTarget{bool(static_cast(cam->orientation & PKE_CAMERA_ORIENTATION_TARGET))}; + bool isOrientFree{bool(static_cast(cam->orientation & PKE_CAMERA_ORIENTATION_FREE))}; + if (ImGui::InputScalarN("Pos", ImGuiDataType_Float, &cam->pos, 3, nullptr, nullptr, nullptr)) + cam->stale = cam->stale | PKE_CAMERA_STALE_POS; + if (ImGui::InputScalarN("Rot", ImGuiDataType_Float, &cam->rot, 4, nullptr, nullptr, nullptr, isOrientFree ? 0 : inputTextFlags)) + cam->stale = cam->stale | PKE_CAMERA_STALE_ROT; + if (ImGui::InputScalarN("Target", ImGuiDataType_Float, &cam->target, 3, nullptr, nullptr, nullptr, isOrientTarget ? 0 : inputTextFlags)) + cam->stale = cam->stale | PKE_CAMERA_STALE_ROT; + + bool isPerspective{bool(static_cast(cam->type & PKE_CAMERA_TYPE_PERSPECTIVE))}; + bool isOrthogonal{bool(static_cast(cam->type & PKE_CAMERA_TYPE_ORTHOGONAL))}; + ImGui::BeginDisabled(isPerspective); + if (ImGui::Button("Perspective")) { + cam->type = PKE_CAMERA_TYPE_PERSPECTIVE; + } + ImGui::EndDisabled(); + ImGui::SameLine(); + ImGui::BeginDisabled(isOrthogonal); + if (ImGui::Button("Orthogonal")) { + cam->type = PKE_CAMERA_TYPE_ORTHOGONAL; + } + ImGui::EndDisabled(); + + ImGui::BeginDisabled(isOrientTarget); + if (ImGui::Button("Target")) { + cam->orientation = PKE_CAMERA_ORIENTATION_TARGET; + } + ImGui::EndDisabled(); + ImGui::SameLine(); + ImGui::BeginDisabled(isOrientFree); + if (ImGui::Button("Free")) { + cam->orientation = PKE_CAMERA_ORIENTATION_FREE; + } + ImGui::EndDisabled(); + + } + } + + ImGui::End(); +} + void RecordImGuiUBO() { if (!ImGui::Begin("UBO", &pkeSettings.editorSettings.isShowingUBO)) { ImGui::End(); @@ -623,11 +737,16 @@ void RecordImGuiSceneEditor() { ImGui::OpenPopup("CreateEntityType"); } if (ImGui::Button("Save")) { - // Game_SaveSceneFile(sceneName); + shouldSaveScene = true; + } + if (sceneName) { + ImGui::SameLine(); + ImGui::Text("%s", sceneName); + } + if (ImGui::Button("Save As...")) { shouldOpenSaveSceneDialog = true; } if (ImGui::Button("Load")) { - // Game_LoadSceneFile(sceneName); shouldOpenLoadSceneDialog = true; } if (ImGui::Button("Clear Selection")) { @@ -653,6 +772,7 @@ void PkeEditor_RecordImGui() { RecordImGuiEntityList(); RecordImGuiSceneEditor(); RecordImGuiUBO(); + RecordImGuiCameras(); Game_RecordImGui(); } } @@ -723,10 +843,10 @@ void PkeEditor_Init() { debugControlsSet.actions[8].primaryHash = PkeInputEventMask { .computedHash = PKE_INPUT_HASH_ALL_CURSOR_POS_EVENTS, }; - debugControlsSet.actions[9].name = dbgCtrl_UnlockCamera; + debugControlsSet.actions[9].name = dbgCtrl_CameraButtonMask; debugControlsSet.actions[9].primaryHash = PkeInputEventMask { - .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, - .button = GLFW_KEY_ESCAPE, + .computedHash = PKE_INPUT_HASH_ALL_MOUSE_BUTTON_EVENTS, + .button = GLFW_MOUSE_BUTTON_MIDDLE, }; debugControlsSet.actions[10].name = dbgCtrl_SelectHovered; debugControlsSet.actions[10].primaryHash = PkeInputEventMask { diff --git a/src/game.cpp b/src/game.cpp index 6fab5f4..6c47381 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -34,6 +34,7 @@ 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_OBJ_CAMERA = "Camera:"; const char *PKE_FILE_ENTITY_TYPE_MODELS_DIR = "ModelsDir: "; const char *PKE_FILE_ENTITY_TYPE_MODEL_FILE = "ModelFile: "; @@ -57,6 +58,41 @@ 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_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_ORIENTATION = "Cam::Orientation: "; + +void SerializeCamera(std::ofstream &stream, const PkeCamera &cam) { + PkeCamera c{}; + if (cam.pos != c.pos) { + stream << PKE_FILE_CAMERA_POS << "[" + << std::setw(10) << cam.pos[0] << "," + << std::setw(10) << cam.pos[1] << "," + << std::setw(10) << cam.pos[2] << "]" << std::endl; + } + if (cam.rot != c.rot) { + stream << PKE_FILE_CAMERA_ROT << "[" + << std::setw(10) << cam.rot[0] << "," + << std::setw(10) << cam.rot[1] << "," + << std::setw(10) << cam.rot[2] << "," + << std::setw(10) << cam.rot[3] << "]" << std::endl; + } + if (cam.target != c.target) { + stream << PKE_FILE_CAMERA_TARGET << "[" + << std::setw(10) << cam.target[0] << "," + << std::setw(10) << cam.target[1] << "," + << std::setw(10) << cam.target[2] << "]" << std::endl; + } + if (cam.type != c.type) { + stream << PKE_FILE_CAMERA_TYPE << int(static_cast(cam.type)) << std::endl; + } + if (cam.orientation != c.orientation) { + stream << PKE_FILE_CAMERA_ORIENTATION << int(static_cast(cam.orientation)) << std::endl; + } +} + 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)); @@ -143,6 +179,79 @@ void SerializeInstance(std::ofstream &stream, const CompInstance &comp) { } } +void ParseCamera(std::ifstream &stream) { + PkeCamera cam{}; + while (stream.getline(readLine, readLineLength)) { + if (strcmp(readLine, PKE_FILE_OBJ_END) == 0) { + auto &rCam = PkeCamera_Register(); + rCam.pos = cam.pos; + rCam.rot = cam.rot; + rCam.target = cam.target; + rCam.type = cam.type; + rCam.orientation = cam.orientation; + return; + } + if (strncmp(readLine, PKE_FILE_CAMERA_POS, strlen(PKE_FILE_CAMERA_POS)) == 0) { + uint64_t prefixLen = strlen(PKE_FILE_CAMERA_POS); + char *startingChar = strchr(readLine + prefixLen, '[') + 1; + assert(startingChar != nullptr); + char *pEnd = nullptr; + long index = 0; + do { + assert(index < 3); + STR2NUM_ERROR result = str2num(cam.pos[index], startingChar, pEnd); + assert(result == STR2NUM_ERROR::SUCCESS); + startingChar = pEnd + 1; + ++index; + } while (*pEnd != ']'); + } + if (strncmp(readLine, PKE_FILE_CAMERA_ROT, strlen(PKE_FILE_CAMERA_ROT)) == 0) { + uint64_t prefixLen = strlen(PKE_FILE_CAMERA_ROT); + char *startingChar = strchr(readLine + prefixLen, '[') + 1; + assert(startingChar != nullptr); + char *pEnd = nullptr; + long index = 0; + do { + assert(index < 4); + STR2NUM_ERROR result = str2num(cam.rot[index], startingChar, pEnd); + assert(result == STR2NUM_ERROR::SUCCESS); + startingChar = pEnd + 1; + ++index; + } while (*pEnd != ']'); + } + if (strncmp(readLine, PKE_FILE_CAMERA_TARGET, strlen(PKE_FILE_CAMERA_TARGET)) == 0) { + uint64_t prefixLen = strlen(PKE_FILE_CAMERA_TARGET); + char *startingChar = strchr(readLine + prefixLen, '[') + 1; + assert(startingChar != nullptr); + char *pEnd = nullptr; + long index = 0; + do { + assert(index < 3); + STR2NUM_ERROR result = str2num(cam.target[index], startingChar, pEnd); + assert(result == STR2NUM_ERROR::SUCCESS); + startingChar = pEnd + 1; + ++index; + } while (*pEnd != ']'); + } + 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); + PkeCameraOrientation_T handle_t; + STR2NUM_ERROR result = str2num(handle_t, readLine + prefixLen); + assert(result == STR2NUM_ERROR::SUCCESS); + cam.orientation = PkeCameraOrientation{handle_t}; + continue; + } + } +} + void ParseEntityType(std::ifstream &stream) { EntityType et{}; while (stream.getline(readLine, readLineLength)) { @@ -384,6 +493,20 @@ void Game_SaveSceneFile(const char *sceneFilePath) { f << PKE_FILE_VERSION << std::endl; f << "" << std::endl; + int64_t cameraBucketCount = PkeCamera_GetBucketCount(); + for (long b = 0; b < cameraBucketCount; ++b) { + int64_t count; + auto *cameras = PkeCamera_GetCameras(b, count); + for (long i = 0; i < count; ++i) { + const auto &cam = cameras[i]; + if (cam.handle == CameraHandle_MAX) + continue; + f << PKE_FILE_OBJ_CAMERA << std::endl; + SerializeCamera(f, cam); + f << PKE_FILE_OBJ_END << std::endl; + } + } + for (long i = 0; i < GlobalEntityTypes.Count(); ++i) { f << PKE_FILE_OBJ_ENTITY_TYPE << std::endl; const auto &et = GlobalEntityTypes[i]; @@ -418,11 +541,17 @@ void Game_SaveSceneFile(const char *sceneFilePath) { void Game_LoadSceneFile(const char *sceneFilePath) { std::ifstream f(sceneFilePath); - assert(f.is_open()); + if (!f.is_open()) { + fprintf(stderr, "Failed to load requested scene file: %s", sceneFilePath); + return; + } memset(readLine, '\0', readLineLength); while (f.getline(readLine, readLineLength)) { - // EntityTypes + if (strcmp(PKE_FILE_OBJ_CAMERA, readLine) == 0) { + ParseCamera(f); + continue; + } if (strcmp(PKE_FILE_OBJ_ENTITY_TYPE, readLine) == 0) { ParseEntityType(f); continue; @@ -498,6 +627,7 @@ void Game_Main(PKEWindowProperties windowProps, const char *executablePath) { fprintf(stdout, "Game_Main Entering\n"); try { AM_Init(); + PkeCamera_Init(); Physics_Init(); Game_Init(); ECS_Init(); @@ -604,6 +734,7 @@ void Game_Main(PKEWindowProperties windowProps, const char *executablePath) { PkeInput_Teardown(); Physics_Teardown(); ECS_Teardown(); + PkeCamera_Teardown(); AM_DebugPrint(); AM_Teardown(); DestroyWindow(); -- cgit v1.2.3