#include "editor.hpp" #include "editor-io.hpp" #include "editor-types.hpp" #include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.h" #include "asset-manager.hpp" #include "camera.hpp" #include "ecs.hpp" #include "entities.hpp" #include "font.hpp" #include "game-settings.hpp" #include "game.hpp" #include "imgui.h" #include "level-types.hpp" #include "level.hpp" #include "math-helpers.hpp" #include "msdf-atlas-gen/msdf-atlas-gen.h" #include "msdfgen.h" #include "player-input.hpp" #include "plugins.hpp" #include "project-settings.hpp" #include "project.hpp" #include "scene.hpp" #include "static-ui.hpp" #include "thread-pool.hpp" #include "vendor-glm-include.hpp" #include "vendor-imgui-ext.hpp" #include "vendor-stb-image-include.h" #include "vendor-tinyfiledialogs.h" #include "window.hpp" #include "pk.h" #include #include #include #include #include #include #include #ifdef WIN32 // TODO #else #include #include #endif const char* const dbgCtrl_CameraLeft = "debug-camera-left"; const char* const dbgCtrl_CameraRight = "debug-camera-right"; const char* const dbgCtrl_CameraForward = "debug-camera-forward"; const char* const dbgCtrl_CameraBack = "debug-camera-back"; const char* const dbgCtrl_CameraUp = "debug-camera-up"; const char* const dbgCtrl_CameraDown = "debug-camera-down"; const char* const dbgCtrl_CameraRotCC = "debug-camera-rot-counter-clockwise"; const char* const dbgCtrl_CameraRotC = "debug-camera-rot-clockwise"; const char* const dbgCtrl_CameraRot = "debug-camera-rot"; const char* const dbgCtrl_CameraButtonMask = "debug-camera-button-mask"; const char* const dbgCtrl_SelectHovered = "debug-select-hovered"; const char* const dbgCtrl_ClearSelection = "debug-clear-selection"; const char* const dbgCtrl_DeleteSelectedItem = "debug-delete-selected-item"; const char* const dbgCtrl_ImGui_Toggle = "debug-imgui-toggle"; ThreadPoolHandle threadPoolHandle = ThreadPoolHandle_MAX; pke_input_action_set_handle debugControlsHandle = pke_input_action_set_handle_MAX; bool shouldSetupEditor = true; bool shouldDisableEditor = false; bool shouldRebuildProjectDir = true; bool shouldRebuildAssetList = true; // TODO Can't make certain changes *this* tick through DearImGui. // ex: Changing text in a FontRender, adding an instance, etc. // Specifically when it causes a vulkan buffer resize. // Each "should" should be refactored *out* of the editor. // ex: ecs should be responsible for creating instances in a deferred manner. // - COULD lead to ugly callbacks or listeners? // Suggestion: Consider a minor engine change that adds a post-tick phase, so that if the engine sleeps we don't slow down the next tick when it can be avoided. // Note: Could have ecs implications (marked for removal, etc) pk_arr_t entityInstancesToCreate{}; CompInstance *selectedEntity = nullptr; CompInstance *hoveredEntity = nullptr; bool shouldCreateEntityType = false; EntityType entityTypeToCreate{}; CameraHandle selectedCamera = CameraHandle_MAX; static pke_ui_box *selected_ui_box = NULL; const char* const newSceneName = "newScene.pstf"; const char* const editorLevelName = "editor_level"; bool shouldOpenSaveSceneDialog = false; bool shouldOpenNewScene = false; bool shouldSaveProjectFile = false; bool shouldRunCurrentScene = false; __pid_t subProgramPid = -1; 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 PkeEditor_Tick(double delta) { const char *scene_to_load_or_create_path = nullptr; if (shouldRunCurrentScene) { shouldRunCurrentScene = false; auto *task = pk_new>(); new (task) std::packaged_task( [] { subProgramPid = fork(); if (subProgramPid == 0) { int status = -1; const char *argv[] = { "pke-runtime", "--plugin", pkeSettings.args.pluginPath == nullptr ? "libpke-example.o" : pkeSettings.args.pluginPath, "--project", pkeSettings.args.projectPath == nullptr ? PKE_PROJ_DEFAULT_FILENAME : pkeSettings.args.projectPath, "--scene", (editor_mstr.active_scene != nullptr && editor_mstr.active_scene->file_path.length > 0) ? editor_mstr.active_scene->file_path.val : PKE_PROJ_DEFAULT_FILENAME , NULL, }; status = execvp("pke-runtime", const_cast(argv)); fprintf(stderr, "pke-runtime (child) exited with a status of %i\n", status); subProgramPid = -1; } else if (subProgramPid == -1) { fprintf(stdout, "pke-runtime failed to fork\n"); subProgramPid = -1; } else { int status = 0xCAFEBABE; waitpid(subProgramPid, &status, 0); fprintf(stdout, "pke-runtime (parent) exited with a status of %i (0x%08X)\n", status, status); subProgramPid = -1; } }); PkeThreads_Enqueue(threadPoolHandle, task); } if (shouldSetupEditor) { PkeEditor_Setup(); } if (shouldDisableEditor) { PkeEditor_Teardown(); return; } if (shouldOpenNewScene) { shouldOpenNewScene = false; editor_mstr.shouldLoadScene = false; editor_mstr.shouldSaveScene = false; scene_to_load_or_create_path = newSceneName; } if (shouldSaveProjectFile) { shouldSaveProjectFile = false; PkeProject_Save(); } if (shouldOpenSaveSceneDialog) { assert(editor_mstr.active_scene != nullptr); shouldOpenSaveSceneDialog = false; auto *task = pk_new>(); new (task) std::packaged_task( [] { const char * patterns[1] = {"*.pstf"}; char *selectedScene = tinyfd_saveFileDialog(nullptr, editor_mstr.active_scene->file_path.val, 1, patterns, "Pke Scene Text File"); if (selectedScene != nullptr) { if (editor_mstr.active_scene->file_path.reserved > 0) { pk_delete_arr(editor_mstr.active_scene->file_path.val, editor_mstr.active_scene->file_path.reserved); } editor_mstr.active_scene->file_path.length = strlen(selectedScene); editor_mstr.active_scene->file_path.reserved = editor_mstr.active_scene->file_path.length + 1; editor_mstr.active_scene->file_path.val = pk_new_arr(editor_mstr.active_scene->file_path.reserved); strcpy(editor_mstr.active_scene->file_path.val, selectedScene); editor_mstr.shouldSaveScene = true; } }); PkeThreads_Enqueue(threadPoolHandle, task); } if (editor_mstr.shouldSaveScene && editor_mstr.active_scene) { NULL_CHAR_ARR(file_path, 256); editor_mstr.shouldSaveScene = false; if (editor_mstr.active_scene->file_path.val) { sprintf(file_path, "%.255s", editor_mstr.active_scene->file_path.val); } else { sprintf(file_path, "%.16s", editor_mstr.active_scene->name); } int len = strlen(file_path); if (strstr(file_path, ".pstf") == nullptr && len < 251) { file_path[len+0] = '.'; file_path[len+1] = 'p'; file_path[len+2] = 's'; file_path[len+3] = 't'; file_path[len+4] = 'f'; file_path[len+5] = '\0'; } pke_editor_scene_save(file_path); shouldRebuildProjectDir = true; } if (editor_mstr.target_scene_path.val != nullptr) { scene_to_load_or_create_path = editor_mstr.target_scene_path.val; } if (scene_to_load_or_create_path != nullptr) { ActiveCamera = &NullCamera; selectedEntity = nullptr; selected_ui_box = nullptr; pkeSettings.rt.nextLevel = pke_level_create(editorLevelName, pk_uuid_zed, pk_uuid_zed); editor_mstr.active_scene = pke_scene_create(scene_to_load_or_create_path); scene_instance si{}; si.scene_handle = editor_mstr.active_scene->scene_handle; pk_arr_append_t(&pkeSettings.rt.nextLevel->scene_instances, si); if (scene_to_load_or_create_path == newSceneName) { return; } std::filesystem::path p(scene_to_load_or_create_path); std::ifstream f(p); if (f.good()) { pke_editor_scene_load(pkeSettings.rt.nextLevel, p.c_str()); } if (editor_mstr.target_scene_path.reserved > 0) { editor_mstr.active_scene->file_path = editor_mstr.target_scene_path; editor_mstr.target_scene_path = {}; } return; } if (selectedEntity && ECS_GetEntity(selectedEntity->entHandle)->isMarkedForRemoval == true) { selectedEntity = nullptr; } bool imGuiHovered = ImGui::GetIO().WantCaptureMouse; const pke_input_event *holder = nullptr; if (selectedEntity != nullptr && !imGuiHovered) { holder = pke_input_query_by_action_name(dbgCtrl_ClearSelection); if (holder != nullptr) { assert(holder->type == PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON); if (holder->data.mouse_button.isPressed) { selectedEntity = nullptr; selected_ui_box = nullptr; } } } // raycast for hovering hoveredEntity = nullptr; if (!imGuiHovered) { double xMousePos, yMousePos; if (pkeSettings.editorSettings.isUsingDebugCamera) { xMousePos = Extent.width / 2.0; yMousePos = Extent.height / 2.0; } else { glfwGetCursorPos(window, &xMousePos, &yMousePos); } glm::vec3 fromCoords{unproject(glm::vec3(xMousePos, yMousePos, -1.f))}; glm::vec3 toCoords{unproject(glm::vec3(xMousePos, yMousePos, 1.f))}; if (fromCoords.x == fromCoords.x && toCoords.x == toCoords.x) { 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()) { hoveredEntity = reinterpret_cast(rayResult.m_collisionObject->getUserPointer()); } } } if (hoveredEntity != nullptr) { auto holder = pke_input_query_by_action_name(dbgCtrl_SelectHovered); if (holder != nullptr && holder->type != pke_input_event_hash{0}) { if (holder->data.mouse_button.isPressed) { selectedEntity = hoveredEntity; } } } CompInstance *focusedInst = selectedEntity != nullptr ? selectedEntity : hoveredEntity; bool found = false; if (focusedInst != nullptr) { const auto *grBinds = ECS_GetGrBinds(focusedInst->grBindsHandle); if (grBinds != nullptr) { pkeDebugHitbox.instanceBuffer = grBinds->instanceBD.buffer; pkeDebugHitbox.instanceStartingIndex = focusedInst->index; found = true; } } if (!found) { pkeDebugHitbox.instanceBuffer = VK_NULL_HANDLE; pkeDebugHitbox.instanceStartingIndex = 0; } if (shouldCreateEntityType) { EntityType *existingEntity = EntityType_FindByTypeCode(entityTypeToCreate.entityTypeCode.val); if (existingEntity == nullptr) { EntityType *newEntType = EntityType_Create(pk_uuid_zed); strncpy(newEntType->modelAssetKey, entityTypeToCreate.modelAssetKey, AssetKeyLength); newEntType->entityTypeCode = entityTypeToCreate.entityTypeCode; if (entityTypeToCreate.createInstanceCallback.name[0] != '\0') { strncpy(newEntType->createInstanceCallback.name, entityTypeToCreate.createInstanceCallback.name, CallbackSignatureLength); PkePlugin_SetSignatureFunc(&newEntType->createInstanceCallback); } newEntType->detailsCount = entityTypeToCreate.detailsCount; for (int64_t i = 0; i < entityTypeToCreate.detailsCount; ++i) { newEntType->details[i] = entityTypeToCreate.details[i]; } EntityType_Load(*newEntType); /* for (int64_t i = 0; i < entityTypeToCreate.detailsCount; ++i) { if (entityTypeToCreate.details[i].grBinds == nullptr) continue; // strncpy(newEntType->details[i].grBinds->collisionCallback.name, entityTypeToCreate.createInstanceCallback.name, CallbackSignatureLength); // newEntType->details[i].grBinds->collisionCallback.func = entityTypeToCreate.createInstanceCallback.func; PkePlugin_SetSignatureFunc(&newEntType->details[i].grBinds->collisionCallback); } */ } else { // TODO leaks entityTypeToCreate.entityTypeCode } entityTypeToCreate = EntityType{}; shouldCreateEntityType = false; } while (entityInstancesToCreate.next > 0) { EntityType *et = entityInstancesToCreate[entityInstancesToCreate.next-1]; if (et->createInstanceCallback.func) { // TODO function signature // reinterpret_cast(et->createInstanceCallback.func)(); fprintf(stderr, "[%s] Attempted to call EntityType::createInstanceCallback without a function signature", __FILE__); } else { EntityType_CreateGenericInstance(et, editor_mstr.active_scene, nullptr); } pk_arr_remove_at(&entityInstancesToCreate, entityInstancesToCreate.next-1); } holder = pke_input_query_by_action_name(dbgCtrl_CameraButtonMask); if (holder != nullptr && holder->type != pke_input_event_hash{0}) { if (holder->data.mouse_button.thisTick) { if (holder->data.mouse_button.isPressed && pkeSettings.editorSettings.isUsingDebugCamera == false) { pkeSettings.editorSettings.isUsingDebugCamera = true; glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); selectedCamera = CameraHandle_MAX; if (ActiveCamera != &NullCamera) { btTransform btfm; CompInstance *inst = ECS_GetInstance(ActiveCamera->phys.instHandle); inst->bt.motionState->getWorldTransform(btfm); NullCameraInstance.bt.motionState->setWorldTransform(btfm); NullCameraInstance.bt.rigidBody->setWorldTransform(btfm); NullCameraInstance.bt.rigidBody->activate(); NullCamera.phys.target_inst_uuid = ActiveCamera->phys.target_inst_uuid; NullCamera.phys.target_inst_handle = ActiveCamera->phys.target_inst_handle; NullCamera.type = ActiveCamera->type; NullCamera.view = ActiveCamera->view; NullCamera.stale = PKE_CAMERA_STALE_ALL; ActiveCamera = &NullCamera; } } else if (holder->data.mouse_button.isPressed && pkeSettings.editorSettings.isUsingDebugCamera == true) { pkeSettings.editorSettings.isUsingDebugCamera = false; glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } } } if (pkeSettings.editorSettings.isUsingDebugCamera && ActiveCamera == &NullCamera) { holder = pke_input_query_by_action_name(dbgCtrl_CameraRot); btTransform trfm; NullCameraInstance.bt.motionState->getWorldTransform(trfm); if (holder != nullptr && holder->type != pke_input_event_hash{0}) { if (holder->data.cursor_pos.xMotion || holder->data.cursor_pos.yMotion) { glm::quat gRot; BulletToGlm(trfm.getRotation(), gRot); glm::vec3 axis1Heading = glm::conjugate(gRot) * glm::vec3(1.f, 0.f, 0.f); glm::vec3 axis2Heading = glm::conjugate(gRot) * glm::vec3(0.f, 1.f, 0.f); glm::quat pitch = glm::angleAxis(float(holder->data.cursor_pos.yMotion) * 0.01f, glm::normalize(axis1Heading)); glm::quat yaw = glm::angleAxis(float(holder->data.cursor_pos.xMotion) * 0.01f, glm::normalize(axis2Heading)); glm::quat rot = gRot * pitch * yaw; auto eul = glm::eulerAngles(rot); gRot = glm::quat(eul); btQuaternion bRot; GlmToBullet(gRot, bRot); trfm.setRotation(bRot); NullCamera.stale = NullCamera.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 = pke_input_query_by_action_name(dbgCtrl_CameraLeft); if (holder != nullptr && holder->type != pke_input_event_hash{0}) { leftCount = holder->data.key.isPressed ? 1 : 0; } holder = pke_input_query_by_action_name(dbgCtrl_CameraRight); if (holder != nullptr && holder->type != pke_input_event_hash{0}) { rightCount = holder->data.key.isPressed ? 1 : 0; } holder = pke_input_query_by_action_name(dbgCtrl_CameraForward); if (holder != nullptr && holder->type != pke_input_event_hash{0}) { forwardCount = holder->data.key.isPressed ? 1 : 0; } holder = pke_input_query_by_action_name(dbgCtrl_CameraBack); if (holder != nullptr && holder->type != pke_input_event_hash{0}) { backCount = holder->data.key.isPressed ? 1 : 0; } holder = pke_input_query_by_action_name(dbgCtrl_CameraUp); if (holder != nullptr && holder->type != pke_input_event_hash{0}) { upCount = holder->data.key.isPressed ? 1 : 0; } holder = pke_input_query_by_action_name(dbgCtrl_CameraDown); if (holder != nullptr && holder->type != pke_input_event_hash{0}) { downCount = holder->data.key.isPressed ? 1 : 0; } holder = pke_input_query_by_action_name(dbgCtrl_CameraRotCC); if (holder != nullptr && holder->type != pke_input_event_hash{0}) { rotCCCount = holder->data.key.isPressed ? 1 : 0; } holder = pke_input_query_by_action_name(dbgCtrl_CameraRotC); if (holder != nullptr && holder->type != pke_input_event_hash{0}) { rotCCount = holder->data.key.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::quat gRot; BulletToGlm(trfm.getRotation(), gRot); glm::vec3 axis1Heading = glm::conjugate(gRot) * glm::vec3(-axis1, 0.f, 0.f); glm::vec3 axis2Heading = glm::conjugate(gRot) * glm::vec3(0.f, 0.f, axis2); glm::vec3 axis3Heading = glm::conjugate(gRot) * glm::vec3(0.f, axis3, 0.f); glm::vec3 gPos; BulletToGlm(trfm.getOrigin(), gPos); gPos += glm::vec3(axis1Heading + axis2Heading + axis3Heading); btVector3 bPos; GlmToBullet(gPos, bPos); trfm.setOrigin(bPos); NullCamera.stale = NullCamera.stale | PKE_CAMERA_STALE_POS; } double axis4 = -(rotCCCount * delta) + (rotCCount * delta); if (axis4 != 0.0) { glm::quat gRot; btQuaternion bRot; BulletToGlm(trfm.getRotation(), gRot); gRot = glm::quat(glm::vec3(0.f, 0.f, axis4)) * gRot; GlmToBullet(gRot, bRot); trfm.setRotation(bRot); NullCamera.stale = NullCamera.stale | PKE_CAMERA_STALE_ROT; } NullCameraInstance.bt.motionState->setWorldTransform(trfm); NullCameraInstance.bt.rigidBody->setWorldTransform(trfm); NullCameraInstance.bt.rigidBody->activate(); } holder = pke_input_query_by_action_name(dbgCtrl_DeleteSelectedItem); if (holder != nullptr && holder->type != pke_input_event_hash{0} && selectedEntity != nullptr) { if (holder->data.key.isPressed == true) { ECS_MarkForRemoval(ECS_GetEntity(selectedEntity->entHandle)); selectedEntity = nullptr; } } holder = pke_input_query_by_action_name(dbgCtrl_ImGui_Toggle); if (holder != nullptr && holder->type != pke_input_event_hash{0}) { if (holder->data.key.isPressed && holder->data.key.thisTick == true) { pkeSettings.isShowingEditor = !pkeSettings.isShowingEditor; } } } bool RecordImGui_FontRenderSettings(FontRenderSettings &frs, bool allow_surface_area_edit) { bool changed = false; unsigned int flags = FONT_RENDER_SURFACE_AREA_TYPE_FLAG_T(frs.surface_area_type_flags); changed = ImGui::ColorEdit4("color_foreground(txt)", &frs.color_foreground[0]) || changed; changed = ImGui::ColorEdit4("color_background(txt)", &frs.color_background[0]) || changed; changed = ImGui::DragFloat("char_scale", &frs.char_scale, 0.25, 0.0, 128.0) || changed; changed = ImGui::DragFloat("line_height_scale", &frs.line_height_scale, 0.25, 0.0, 128.0) || changed; changed = ImGui::DragFloat("char_spacing_scale", &frs.char_spacing_scale, 0.25, 0.0, 128.0) || changed; if (allow_surface_area_edit) { changed = ImGui::DragInt2("surface_area_size", &frs.surface_area_size[0], 1, 0, 4096) || changed; changed = ImGui::DragInt2("surface_area_pos", &frs.surface_area_size[0], 1, 0, 4096) || changed; } changed = ImGui::CheckboxFlags("Text Center Vertical", &flags, (unsigned int)FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_CENTER_VERTICAL) || changed; changed = ImGui::CheckboxFlags("Text Center Horizontal", &flags, (unsigned int)FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_CENTER_HORIZONTAL) || changed; if (changed) { frs.surface_area_type_flags = FONT_RENDER_SURFACE_AREA_TYPE_FLAG(flags); } return changed; } 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(); } } struct assetLabel { AssetKey key = {'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'}; AssetHandle handle{}; AssetType type{}; }; pk_arr_t assetEntries{}; int assetLabelCmp(const void *a, const void *b) { const auto &tA = *static_cast(a); const auto &tB = *static_cast(b); return strcmp(tA.key, tB.key); } void buildAssetLableList() { } struct AssetPickerSearchStruct { int64_t selectedItem = -1; char source[8]; char safeKey[AssetKeyLength + 1]; AssetType type{PKE_ASSET_TYPE_ALL}; }; void RecordImGui_AssetPicker(AssetPickerSearchStruct &apss) { bool b; pk_bkt_arr *bkt_arr_assets; pk_iter_t iter_asset{}; if (shouldRebuildAssetList == true) { if (assetEntries.next == 0) { bkt_arr_assets = AM_GetAssets(); b = pk_bkt_arr_iter_begin(bkt_arr_assets, &iter_asset); while (b == true) { assetLabel tmp{}; const Asset &a = *iter_asset; tmp.key[AssetKeyLength-1] = '\0'; tmp.handle = a.handle; tmp.type = a.type; strncpy(tmp.key, a.key, AssetKeyLength); pk_arr_append_t(&assetEntries, tmp); b = pk_bkt_arr_iter_increment(bkt_arr_assets, &iter_asset); } } std::qsort(assetEntries.data, assetEntries.next, sizeof(assetLabel), assetLabelCmp); } if (ImGui::BeginPopup(apss.source)) { ImGui::SeparatorText("Asset"); const uint32_t iCount = assetEntries.next; for (uint32_t i = 0; i < iCount; ++i) { const assetLabel &al = assetEntries[i]; if ((al.type & apss.type) == AssetType{0}) { continue; } char buf[AssetKeyLength + 1]; snprintf(buf, AssetKeyLength + 1, "%s", al.key); if (ImGui::Selectable(buf, apss.selectedItem == i)) { snprintf(apss.safeKey, AssetKeyLength + 1, "%s", buf); apss.selectedItem = i; ImGui::CloseCurrentPopup(); } } ImGui::EndPopup(); } } void RecordImGuiModalCreateAsset() { static char assetPath[256]; static char assetKeyBuffer[AssetKeyLength + 1]; static AssetType type; static union pke_asset_details type_data; assetPath[255] = '\0'; assetKeyBuffer[AssetKeyLength] = '\0'; if (ImGui::BeginPopupModal("CreateAsset", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::InputText("Asset Key", assetKeyBuffer, AssetKeyLength+1); static uint32_t assetTypeMask = 0U; if (ImGui::CheckboxFlags("Shader", &assetTypeMask, 1U << static_cast(PKE_ASSET_TYPE_SHADER))) { assetTypeMask = assetTypeMask & (1U << static_cast(PKE_ASSET_TYPE_SHADER)); type = PKE_ASSET_TYPE_SHADER; } if (ImGui::CheckboxFlags("Model", &assetTypeMask, 1U << static_cast(PKE_ASSET_TYPE_MODEL))) { assetTypeMask = assetTypeMask & (1U << static_cast(PKE_ASSET_TYPE_MODEL)); type = PKE_ASSET_TYPE_MODEL; } if (ImGui::CheckboxFlags("Texture", &assetTypeMask, 1U << static_cast(PKE_ASSET_TYPE_TEXTURE))) { assetTypeMask = assetTypeMask & (1U << static_cast(PKE_ASSET_TYPE_TEXTURE)); type = PKE_ASSET_TYPE_TEXTURE; } if (ImGui::CheckboxFlags("Audio", &assetTypeMask, 1U << static_cast(PKE_ASSET_TYPE_AUDIO))) { assetTypeMask = assetTypeMask & (1U << static_cast(PKE_ASSET_TYPE_AUDIO)); type = PKE_ASSET_TYPE_AUDIO; } if (ImGui::CheckboxFlags("Font", &assetTypeMask, 1U << static_cast(PKE_ASSET_TYPE_FONT))) { assetTypeMask = assetTypeMask & (1U << static_cast(PKE_ASSET_TYPE_FONT)); type = PKE_ASSET_TYPE_FONT; } if (ImGui::Button("Select File")) { const char * patterns[1] = {"*.*"}; char *selectedPath = tinyfd_openFileDialog(nullptr, "cafebabe.pstf", 1, patterns, "Pke Scene Text File", 0); if (selectedPath != nullptr) { strncpy(assetPath, selectedPath, 255); } } ImGui::SameLine(); ImGui::Text("%s", assetPath); ImGui::Separator(); bool shouldClose = false; ImGui::BeginDisabled(assetPath[0] == '\0' || type == PKE_ASSET_TYPE_UNSET); if (ImGui::Button("Create")) { AssetKey assetKey = {'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'}; memcpy(assetKey, assetKeyBuffer, PK_MIN(AssetKeyLength, strlen(assetKeyBuffer))); // TODO details for other types if (type == PKE_ASSET_TYPE_TEXTURE) { int x,y,c; stbi_info(assetPath, &x, &y, &c); type_data.texture.width = x; type_data.texture.height = y; } AM_Register(assetKey, type, assetPath, &type_data); shouldRebuildAssetList = true; pk_arr_clear(&assetEntries); shouldClose = true; } ImGui::EndDisabled(); ImGui::SameLine(); if (ImGui::Button("Cancel")) { shouldClose = true; } if (shouldClose) { type = PKE_ASSET_TYPE_UNSET; assetTypeMask = 0U; assetKeyBuffer[0] = '\0'; assetPath[0] = '\0'; memset(&type_data, 0, sizeof(union pke_asset_details)); ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } } void RecordImGuiEditorWrapper() { ImGui::DockSpaceOverViewport(0, nullptr, ImGuiDockNodeFlags_PassthruCentralNode); ImGui::BeginMainMenuBar(); if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("New Scene")) { shouldOpenNewScene = true; } /* * comparing pointer locations of c-strings is intentional * - the goal is not to prevent a specific scene name, * I just want to know if they clicked the "New Scene" button */ ImGui::BeginDisabled(editor_mstr.active_scene && (editor_mstr.active_scene->name == newSceneName || editor_mstr.active_scene->file_path.val == nullptr)); if (ImGui::MenuItem("Save")) { shouldSaveProjectFile = true; editor_mstr.shouldSaveScene = true; } if (editor_mstr.active_scene != nullptr) { ImGui::SameLine(); int offset = 0; const auto *slash = strrchr(editor_mstr.active_scene->name, '\\'); slash = slash != nullptr ? slash : strrchr(editor_mstr.active_scene->name, '/'); if (slash != nullptr) { offset = slash - editor_mstr.active_scene->name; } ImGui::Text("%s", editor_mstr.active_scene->name + offset); } ImGui::EndDisabled(); if (ImGui::MenuItem("Save As...")) { shouldSaveProjectFile = true; shouldOpenSaveSceneDialog = true; } if (ImGui::MenuItem("Exit")) { glfwSetWindowShouldClose(window, true); } ImGui::EndMenu(); } if (ImGui::BeginMenu("Debug")) { ImGui::Checkbox("Show Debug Hitboxes", &pkeSettings.isRenderingDebug); ImGui::BeginDisabled(); ImGui::Checkbox("Pause Physics Simulation", &pkeSettings.isSimulationPaused); ImGui::EndDisabled(); 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; ImGui::CloseCurrentPopup(); } else { // shouldLockCamera = true; } } ImGuiIO &io = ImGui::GetIO(); ImGui::Text("Application average %.03f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::Text("Application average %.03f ms/tick (%.1f TPS)", 1000.0f / pkeSettings.stats.tick_rate, pkeSettings.stats.tick_rate); ImGui::EndMenu(); } ImGui::Spacing(); ImGui::BeginDisabled(subProgramPid != -1); if (ImGui::Button("▶️")) { shouldRunCurrentScene = true; } ImGui::EndDisabled(); ImGui::EndMainMenuBar(); } void RecordImGuiEntityTypes() { bool b; pk_bkt_arr *bkt_arr_ent_types; pk_iter_t iter_ent_type{}; if (!ImGui::Begin("EntityTypes")) { ImGui::End(); return; } static ImGuiTableFlags tableFlags{ ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg }; if (ImGui::BeginTable("EntityTypes", 5, tableFlags)) { ImGui::TableSetupColumn("EntityHandle"); ImGui::TableSetupColumn("ModelAssetKey"); ImGui::TableSetupColumn("EntityTypeCode"); ImGui::TableSetupColumn("Details"); ImGui::TableSetupColumn("Instances"); ImGui::TableHeadersRow(); size_t counter = 0; bkt_arr_ent_types = EntityType_GetEntityTypes(); b = pk_bkt_arr_iter_begin(bkt_arr_ent_types, &iter_ent_type); while (b == true) { const EntityType &et = *iter_ent_type; ImGui::PushID(counter++); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text("0x%08X 0x%08X", et.handle.b, et.handle.i); ImGui::TableSetColumnIndex(1); ImGui::Text("%*.*s", 0, (int)AssetKeyLength, et.modelAssetKey); ImGui::TableSetColumnIndex(2); ImGui::Text("%s", et.entityTypeCode.val); ImGui::TableSetColumnIndex(3); ImGui::Text("count: %li", et.detailsCount); ImGui::TableSetColumnIndex(4); if (ImGui::Button("Add")) { pk_arr_append_t(&entityInstancesToCreate, const_cast(&et)); } ImGui::SameLine(); ImGui::Text("count: %u", et.details[0].grBinds->instanceCounter); ImGui::PopID(); b = pk_bkt_arr_iter_increment(bkt_arr_ent_types, &iter_ent_type); } ImGui::EndTable(); } ImGui::End(); } int SortFontGlyphChar(const void *a, const void *b) { return ((FontGlyphChar*)a)->unicode > ((FontGlyphChar*)b)->unicode; } void GenerateMTSDF(FontTypeMSDFSettings *msdf_settings, const Asset *a) { static pk_arr *required_font_glyph_chars_arr; required_font_glyph_chars_arr = pk_new(pkeSettings.mem_bkt.game_transient); required_font_glyph_chars_arr->next = 0; required_font_glyph_chars_arr->reserved = 0; required_font_glyph_chars_arr->stride = sizeof(FontGlyphChar); required_font_glyph_chars_arr->alignment = alignof(FontGlyphChar); required_font_glyph_chars_arr->bkt = pkeSettings.mem_bkt.game_transient; required_font_glyph_chars_arr->data = nullptr; pk_arr_reserve(required_font_glyph_chars_arr, 3); FontGlyphChar fgc{}; fgc.advance = 0; fgc.sprite_region_min = glm::vec2(0); fgc.sprite_region_max = glm::vec2(0); fgc.plane_bounds = glm::vec4(0); fgc.unicode = 0; fgc.flags = FONT_GLYPH_CHAR_FLAGS_NONE; // 9 horizontal tab fgc.unicode = 9; fgc.advance = 2; fgc.flags = FONT_GLYPH_CHAR_FLAGS_WHITESPACE | FONT_GLYPH_CHAR_FLAGS_ALIGN_ADVANCE; pk_arr_append(required_font_glyph_chars_arr, &fgc); // 10 line feed fgc.unicode = 10; fgc.advance = 0; fgc.flags = FONT_GLYPH_CHAR_FLAGS_NEW_LINE; pk_arr_append(required_font_glyph_chars_arr, &fgc); // 13 carriage return fgc.unicode = 13; fgc.advance = 0; fgc.flags = FONT_GLYPH_CHAR_FLAGS_NEW_LINE; pk_arr_append(required_font_glyph_chars_arr, &fgc); uint64_t found_unicode_map = 0; assert(PK_HAS_FLAG(a->type, PKE_ASSET_TYPE_FONT)); FontTypeSpacing ft_spacing{}; glm::dvec4 dbounds; NULL_CHAR_ARR(path_txtr, 128); NULL_CHAR_ARR(path_glyphs, 128); std::error_code e; std::filesystem::create_directory("./font", e); AssetHandle ah_image = AssetHandle_MAX; AssetHandle ah_glyphs = AssetHandle_MAX; snprintf(path_txtr, 128, "%s/%s.%s", "./font", a->key, "png"); snprintf(path_glyphs, 128, "%s/%s.%s", "./font", a->key, "glyph"); if (msdfgen::FreetypeHandle *ft = msdfgen::initializeFreetype()) { // Load font file if (msdfgen::FontHandle *font = msdfgen::loadFont(ft, a->basePath)) { // Storage for glyph geometry and their coordinates in the atlas std::vector glyphs; // FontGeometry is a helper class that loads a set of glyphs from a single font. // It can also be used to get additional font metrics, kerning information, etc. msdf_atlas::FontGeometry fontGeometry(&glyphs); // Load a set of character glyphs: // The second argument can be ignored unless you mix different font sizes in one atlas. // In the last argument, you can specify a charset other than ASCII. // To load specific glyph indices, use loadGlyphs instead. fontGeometry.loadCharset(font, 1.0, msdf_atlas::Charset::ASCII); // Apply MSDF edge coloring. See edge-coloring.h for other coloring strategies. const double maxCornerAngle = 3.0; for (msdf_atlas::GlyphGeometry &glyph : glyphs) glyph.edgeColoring(&msdfgen::edgeColoringByDistance, maxCornerAngle, 0); auto &metrics = fontGeometry.getMetrics(); ft_spacing.geometry_scale = fontGeometry.getGeometryScale(); ft_spacing.em_size = metrics.emSize; ft_spacing.ascender_y = metrics.ascenderY; ft_spacing.descender_y = metrics.descenderY; ft_spacing.line_height = metrics.lineHeight; ft_spacing.underline_y = metrics.underlineY; ft_spacing.underline_thickness = metrics.underlineThickness; // TightAtlasPacker class computes the layout of the atlas. msdf_atlas::TightAtlasPacker packer; // Set atlas parameters: // setDimensions or setDimensionsConstraint to find the best value packer.setDimensionsConstraint(msdf_atlas::DimensionsConstraint::SQUARE); // setScale for a fixed size or setMinimumScale to use the largest that fits packer.setMinimumScale(msdf_settings->minimum_scale); // setPixelRange or setUnitRange packer.setPixelRange(msdf_settings->px_range); packer.setMiterLimit(1.0); // Compute atlas layout - pack glyphs packer.pack(glyphs.data(), glyphs.size()); // Get final atlas dimensions int width = 0, height = 0; packer.getDimensions(width, height); // The ImmediateAtlasGenerator class facilitates the generation of the atlas bitmap. msdf_atlas::ImmediateAtlasGenerator< float, // pixel type of buffer for individual glyphs depends on generator function 4, // number of atlas color channels &msdf_atlas::mtsdfGenerator, // function to generate bitmaps for individual glyphs msdf_atlas::BitmapAtlasStorage // class that stores the atlas bitmap // For example, a custom atlas storage class that stores it in VRAM can be used. > generator(width, height); // GeneratorAttributes can be modified to change the generator's default settings. // msdf_atlas::GeneratorAttributes attributes{}; // generator.setAttributes(attributes); generator.setThreadCount(31); // TODO engine should know this // Generate atlas bitmap generator.generate(glyphs.data(), glyphs.size()); // The atlas bitmap can now be retrieved via atlasStorage as a BitmapConstRef. // The glyphs array (or fontGeometry) contains positioning data for typesetting text. msdf_atlas::BitmapAtlasStorage storage = generator.atlasStorage(); msdf_atlas::saveImage<4>(storage, msdf_atlas::ImageFormat::PNG, path_txtr, msdf_atlas::YDirection::TOP_DOWN); ah_image = AM_Register(path_txtr, PKE_ASSET_TYPE_UNSET); pk_arr arr_glyphs{}; arr_glyphs.alignment = alignof(FontGlyphChar); arr_glyphs.stride = sizeof(FontGlyphChar); pk_arr_resize(&arr_glyphs, glyphs.size()); FontGlyphChar *arr = reinterpret_cast(arr_glyphs.data); FontGlyphChar *req_arr = reinterpret_cast(required_font_glyph_chars_arr->data); for (uint64_t i = 0; i < glyphs.size(); ++i) { arr[i].advance = glyphs[i].getAdvance(); arr[i].unicode = glyphs[i].getCodepoint(); arr[i].flags = FONT_GLYPH_CHAR_FLAGS_NONE; if (glyphs[i].isWhitespace()) { arr[i].flags = FONT_GLYPH_CHAR_FLAGS_WHITESPACE; } for (uint32_t k = 0; k < required_font_glyph_chars_arr->next; ++k) { if (arr[i].unicode == req_arr[k].unicode) { found_unicode_map |= (1ull << k); break; } } glyphs[i].getQuadPlaneBounds(dbounds.x, dbounds.y, dbounds.z, dbounds.a); arr[i].plane_bounds = dbounds; // library counts up from the bottom of the image to the bottom of the glyph glyphs[i].getQuadAtlasBounds(dbounds.x, dbounds.y, dbounds.z, dbounds.a); arr[i].sprite_region_min = glm::vec2(dbounds.x, height - dbounds.a); arr[i].sprite_region_max = glm::vec2(dbounds.z, height - dbounds.y); } for (uint32_t k = 0; k < required_font_glyph_chars_arr->next; ++k) { if ((found_unicode_map & (1ull << k)) != 0) { continue; } pk_arr_append(&arr_glyphs, &req_arr[k]); } qsort(arr_glyphs.data, arr_glyphs.next, arr_glyphs.stride, SortFontGlyphChar); auto f = fopen(path_glyphs, "w+"); fwrite(arr_glyphs.data, arr_glyphs.stride, arr_glyphs.next, f); fclose(f); ah_glyphs = AM_Register(path_glyphs, PKE_ASSET_TYPE_UNSET); // Cleanup msdfgen::destroyFont(font); } else { fprintf(stderr, "failed to load font"); return; } msdfgen::deinitializeFreetype(ft); } pk_cstr font_title; font_title.length = strlen(a->key); font_title.reserved = font_title.length + 1; // TODO specific bucket? font_title.val = pk_new_arr(font_title.reserved); snprintf((char *)font_title.val, AssetKeyLength, "%s", a->key); assert(ah_image != ah_glyphs && ah_image != AssetHandle_MAX); FontType_RegisterFont(font_title, ah_image, ah_glyphs, msdf_settings, &ft_spacing); } void GenerateMTSDFGlyph(MSDFGlyphSettings *settings, const char *name) { assert(settings != nullptr); NULL_CHAR_ARR(src_path, 128); NULL_CHAR_ARR(dst_path, 128); std::error_code e; std::filesystem::create_directory("./glyphs", e); sprintf(src_path, "./assets/glyphs/%s.svg", name); sprintf(dst_path, "./glyphs/%s.png", name); msdfgen::Shape shape; if (!msdfgen::loadSvgShape(shape, src_path)) { return; } shape.normalize(); msdfgen::edgeColoringSimple(shape, 3.0); msdfgen::Bitmap mtsdf(settings->width, settings->height); msdfgen::Vector2 scale(settings->scale); msdfgen::Vector2 em_translation(settings->translate_em, settings->translate_em); msdfgen::SDFTransformation t(msdfgen::Projection(scale, em_translation), msdfgen::Range(settings->range_em)); msdfgen::generateMTSDF(mtsdf, shape, t); msdfgen::savePng(mtsdf, dst_path); } bool RecordImGui_GenerateMTSDFModal() { bool b; bool ret_value = false; pk_bkt_arr *bkt_arr_assets; pk_iter_t iter_asset{}; static struct FontTypeMSDFSettings msdf_settings{}; if (ImGui::BeginPopupModal("MTSDFModal", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::InputFloat("Minimum Scale", &msdf_settings.minimum_scale); ImGui::InputFloat("Pixel Range", &msdf_settings.px_range); ImGui::Text("Select font:"); ImGui::BeginDisabled(msdf_settings.minimum_scale <= 0.0 || msdf_settings.px_range <= 0.0); bkt_arr_assets = AM_GetAssets(); b = pk_bkt_arr_iter_begin(bkt_arr_assets, &iter_asset); while(b == true) { const Asset &a = *iter_asset; if (!PK_HAS_FLAG(a.type, PKE_ASSET_TYPE_FONT)) { b = pk_bkt_arr_iter_increment(bkt_arr_assets, &iter_asset); continue; } if (ImGui::Selectable(a.basePath)) { GenerateMTSDF(&msdf_settings, &a); ImGui::CloseCurrentPopup(); } b = pk_bkt_arr_iter_increment(bkt_arr_assets, &iter_asset); } ImGui::EndDisabled(); ImGui::Separator(); if (ImGui::Button("Cancel")) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } return ret_value; } bool RecordImGui_GenerateMTSDFGlyphModal() { bool ret_value = false; static std::regex reg_svg_file(".+\\.svg$", std::regex_constants::icase); static MSDFGlyphSettings msdf_glyph_settings = { .width = 80, .height = 80, .scale = 4, .translate_em = 2, .range_em = 4, }; if (ImGui::BeginPopupModal("MTSDFGlyphModal", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::InputFloat("width", &msdf_glyph_settings.width); ImGui::InputFloat("height", &msdf_glyph_settings.height); ImGui::InputFloat("scale", &msdf_glyph_settings.scale); ImGui::InputFloat("translate_em", &msdf_glyph_settings.translate_em); ImGui::InputFloat("range_em", &msdf_glyph_settings.range_em); ImGui::Separator(); ImGui::Text("%s", "Glyphs in asset dir:"); std::filesystem::directory_iterator di{"assets/glyphs"}; for (const std::filesystem::directory_entry &sde : di) { if (sde.is_regular_file() && std::regex_search(sde.path().c_str(), reg_svg_file)) { ImGui::Text("%s", sde.path().filename().c_str()); } } ImGui::Separator(); if (ImGui::Button("Generate")) { std::filesystem::directory_iterator d2{"assets/glyphs"}; for (const std::filesystem::directory_entry &sde : d2) { if (sde.is_regular_file() && std::regex_search(sde.path().c_str(), reg_svg_file)) { GenerateMTSDFGlyph(&msdf_glyph_settings, sde.path().stem().c_str()); } } ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Cancel")) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } return ret_value; } void RecordImGuiAssets() { bool b; pk_bkt_arr *bkt_arr_asset; pk_iter_t iter_asset; if (!ImGui::Begin("AssetList")) { ImGui::End(); return; } static ImGuiTableFlags tableFlags{ ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg }; if (ImGui::BeginTable("Assets", 7, tableFlags)) { ImGui::TableSetupColumn("Controls"); ImGui::TableSetupColumn("Type"); ImGui::TableSetupColumn("Handle"); ImGui::TableSetupColumn("Key"); ImGui::TableSetupColumn("Data"); ImGui::TableSetupColumn("Size"); ImGui::TableSetupColumn("Ref Count"); ImGui::TableHeadersRow(); size_t counter = 0; bkt_arr_asset = AM_GetAssets(); b = pk_bkt_arr_iter_begin(bkt_arr_asset, &iter_asset); while(b == true) { const Asset &asset = *iter_asset; ImGui::PushID(counter++); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); // buttons go here ImGui::TableSetColumnIndex(1); ImGui::Text("%i", (uint8_t)asset.type); ImGui::TableSetColumnIndex(2); ImGui::Text("0x%.08X 0x%.08X", asset.handle.b, asset.handle.i); ImGui::TableSetColumnIndex(3); ImGui::Text("%.16s", asset.key); ImGui::TableSetColumnIndex(4); ImGui::Text("%p", asset.ptr); ImGui::TableSetColumnIndex(5); ImGui::Text("%li", asset.size); ImGui::TableSetColumnIndex(6); ImGui::Text("%hhi", asset.referenceCount); ImGui::PopID(); b = pk_bkt_arr_iter_increment(bkt_arr_asset, &iter_asset); } ImGui::EndTable(); } ImGui::End(); } void RecordImGuiPlayerInput() { uint32_t u; int64_t i; pke_input_set *set; pke_input_action *action; if (!ImGui::Begin("PlayerInput")) { ImGui::End(); return; } static ImGuiTableFlags tableFlags{ ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg }; pk_arr_t &input_sets = pke_input_get_input_sets(); pk_arr_t &active_handles = pke_input_get_active_input_action_set_handles(); if (ImGui::BeginTable("Active Sets", 1, tableFlags)) { for (u = 0; u < active_handles.next; ++u) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text("Active: %hhu", static_cast(active_handles[u])); } ImGui::EndTable(); } if (ImGui::BeginTable("Player Inputs", 9, tableFlags)) { ImGui::TableSetupColumn("Set Index/Title"); ImGui::TableSetupColumn("Set Flags"); ImGui::TableSetupColumn("Action Name"); ImGui::TableSetupColumn("0 Hash"); ImGui::TableSetupColumn("0 Button"); ImGui::TableSetupColumn("0 Mods"); ImGui::TableSetupColumn("1 Hash"); ImGui::TableSetupColumn("1 Button"); ImGui::TableSetupColumn("1 Mods"); ImGui::TableHeadersRow(); for (u = 0; u < input_sets.next; ++u) { set = &input_sets[u]; for (i = 0; i < set->actionCount; ++i) { action = &set->actions[i]; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text("%hu: \"%s\"", u, set->title); ImGui::TableSetColumnIndex(1); ImGui::Text("%hhu", static_cast(set->flags)); ImGui::TableSetColumnIndex(2); ImGui::Text("\"%s\"", action->name); ImGui::TableSetColumnIndex(3); ImGui::Text("%hu", static_cast(action->masks[0].computedHash)); ImGui::TableSetColumnIndex(4); ImGui::Text("%hu", action->masks[0].button); ImGui::TableSetColumnIndex(5); ImGui::Text("%hu", action->masks[0].mods); ImGui::TableSetColumnIndex(6); ImGui::Text("%hu", static_cast(action->masks[1].computedHash)); ImGui::TableSetColumnIndex(7); ImGui::Text("%hu", action->masks[1].button); ImGui::TableSetColumnIndex(8); ImGui::Text("%hu", action->masks[1].mods); } } ImGui::EndTable(); } ImGui::End(); } void RecordImGuiFont() { pk_iter_t iter_ft{}; bool b; uint32_t c; if (!ImGui::Begin("Fonts")) { ImGui::End(); return; } static ImGuiTableFlags tableFlags{ ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg }; ImGui::Text("Selected Font: %s", editor_mstr.rt.selected_font_type != nullptr ? editor_mstr.rt.selected_font_type->title.val : nullptr); if (ImGui::Button("Clear")) { editor_mstr.rt.selected_font_type = nullptr; editor_mstr.rt.show_font_glyphs = false; } pk_bkt_arr_t &fonts_base = *static_cast*>(FontType_GetFonts()); if (ImGui::BeginTable("Font list", 3, tableFlags)) { c = 0; ImGui::TableSetupColumn("Title"); ImGui::TableSetupColumn("MSDF settings"); ImGui::TableSetupColumn("Spacing"); ImGui::TableHeadersRow(); b = pk_bkt_arr_iter_begin(&fonts_base, &iter_ft); while(b) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::PushID(c); if (ImGui::Button("select")) { editor_mstr.rt.selected_font_type = (FontType*)iter_ft.data; editor_mstr.rt.show_font_glyphs = true; } ImGui::PopID(); ImGui::SameLine(); ImGui::Text("Title: %s", iter_ft->title.val); ImGui::TableSetColumnIndex(1); ImGui::Text("minimum_scale: %f", iter_ft->msdf_settings.minimum_scale); ImGui::Text("px_range: %f", iter_ft->msdf_settings.px_range); ImGui::TableSetColumnIndex(2); ImGui::Text("geometry_scale: %f", iter_ft->spacing.geometry_scale); ImGui::Text("em_size: %f", iter_ft->spacing.em_size); ImGui::Text("ascender_y: %f", iter_ft->spacing.ascender_y); ImGui::Text("descender_y: %f", iter_ft->spacing.descender_y); ImGui::Text("line_height: %f", iter_ft->spacing.line_height); ImGui::Text("underline_y: %f", iter_ft->spacing.underline_y); ImGui::Text("underline_thickness: %f", iter_ft->spacing.underline_thickness); b = pk_bkt_arr_iter_increment(&fonts_base, &iter_ft); ++c; } ImGui::EndTable(); } ImGui::End(); } void RecordImGuiFontGlyphs() { uint32_t u; FontGlyphChar *fgc = nullptr; if (!ImGui::Begin("FontGlyphs", &editor_mstr.rt.show_font_glyphs)) { ImGui::End(); return; } if (editor_mstr.rt.show_font_glyphs == false || editor_mstr.rt.selected_font_type == nullptr) { editor_mstr.rt.selected_font_type = nullptr; editor_mstr.rt.show_font_glyphs = false; ImGui::End(); return; } static ImGuiTableFlags tableFlags{ ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg }; if (ImGui::BeginTable("Font list", 5, tableFlags)) { ImGui::TableSetupColumn("unicode"); ImGui::TableSetupColumn("advance"); ImGui::TableSetupColumn("plane bounds"); ImGui::TableSetupColumn("sprite region"); ImGui::TableSetupColumn("flags"); ImGui::TableHeadersRow(); for (u = 0; u < editor_mstr.rt.selected_font_type->n_glyphs; ++u) { fgc = &editor_mstr.rt.selected_font_type->glyphs[u]; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text("%u", fgc->unicode); ImGui::TableSetColumnIndex(1); ImGui::Text("%f", fgc->advance); ImGui::TableSetColumnIndex(2); ImGui::Text("%f", fgc->plane_bounds.x); ImGui::Text("%f", fgc->plane_bounds.y); ImGui::Text("%f", fgc->plane_bounds.z); ImGui::Text("%f", fgc->plane_bounds.w); ImGui::TableSetColumnIndex(3); ImGui::Text("min.x: %f", fgc->sprite_region_min.x); ImGui::Text("min.y: %f", fgc->sprite_region_min.y); ImGui::Text("max.x: %f", fgc->sprite_region_max.x); ImGui::Text("max.y: %f", fgc->sprite_region_max.y); ImGui::TableSetColumnIndex(4); ImGui::Text("CONTROL: %i", PK_HAS_FLAG(fgc->flags, FONT_GLYPH_CHAR_FLAGS_CONTROL)); ImGui::Text("WHITESPACE: %i", PK_HAS_FLAG(fgc->flags, FONT_GLYPH_CHAR_FLAGS_WHITESPACE)); ImGui::Text("ALIGN_ADVANCE: %i", PK_HAS_FLAG(fgc->flags, FONT_GLYPH_CHAR_FLAGS_ALIGN_ADVANCE)); ImGui::Text("NEW_LINE: %i", PK_HAS_FLAG(fgc->flags, FONT_GLYPH_CHAR_FLAGS_NEW_LINE)); } ImGui::EndTable(); } ImGui::End(); } void RecordImGuiCameras() { bool b; pk_bkt_arr *bkt_arr_cams; pk_iter_t iter_cam{}; CompInstance *active_inst = nullptr; if (!ImGui::Begin("Cameras")) { ImGui::End(); return; } active_inst = ECS_GetInstance(ActiveCamera->phys.instHandle); if (ImGui::Button("Create")) { InstPos instPos{}; instPos.mass = 1.f; if (active_inst == nullptr) { instPos.posRot.setIdentity(); instPos.scale = btVector3(1, 1, 1); } else { active_inst->bt.motionState->getWorldTransform(instPos.posRot); instPos.scale = active_inst->bt.rigidBody->getCollisionShape()->getLocalScaling(); } auto &cam = PkeCamera_Register(pk_uuid_zed, instPos); cam.phys.target_inst_uuid = ActiveCamera->phys.target_inst_uuid; cam.phys.target_inst_handle = ActiveCamera->phys.target_inst_handle; cam.type = ActiveCamera->type; cam.view = ActiveCamera->view; cam.isPrimary = false; pke_level_register_camera(pkeSettings.rt.activeLevel, &cam); } static ImGuiTableFlags tableFlags{ ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg }; if (ImGui::BeginTable("Entities", 8, tableFlags)) { ImGui::TableSetupColumn("Interact"); ImGui::TableSetupColumn("CameraHandle"); ImGui::TableSetupColumn("Target"); ImGui::TableSetupColumn("Type"); ImGui::TableSetupColumn("View"); ImGui::TableSetupColumn("Stale"); ImGui::TableSetupColumn("IsPrimary"); ImGui::TableSetupColumn("Controls"); ImGui::TableHeadersRow(); size_t cam_counter = 0; bkt_arr_cams = &PkeCamera_GetPkBktArr(); b = pk_bkt_arr_iter_begin(bkt_arr_cams, &iter_cam); while (b == true) { const auto &cam = *iter_cam; ImGui::PushID(cam_counter); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::BeginDisabled(selectedCamera.b == cam.camHandle.b && selectedCamera.i == cam.camHandle.i); if (ImGui::Button("Show")) { selectedCamera = cam.camHandle; ActiveCamera = const_cast(&cam); ActiveCamera->stale = PKE_CAMERA_STALE_ALL; } ImGui::SameLine(); if (ImGui::Button("Select")) { selectedEntity = ECS_GetInstance(cam.phys.instHandle); } ImGui::EndDisabled(); ImGui::TableSetColumnIndex(1); ImGui::Text("0x%08X 0x%08X", cam.handle.b, cam.handle.i); ImGui::TableSetColumnIndex(2); ImGui::Text(pk_uuid_printf_format, pk_uuid_printf_var(cam.phys.target_inst_uuid)); ImGui::TableSetColumnIndex(3); ImGui::Text("%hhu", (unsigned char)cam.type); ImGui::TableSetColumnIndex(4); ImGui::Text("%hhu", (unsigned char)cam.view); ImGui::TableSetColumnIndex(5); ImGui::Text("%hhu", (unsigned char)cam.stale); ImGui::TableSetColumnIndex(6); ImGui::Text("%i", cam.isPrimary); ImGui::TableSetColumnIndex(7); if (ImGui::Button("Update Position")) { InstPos instPos{}; instPos.mass = 1.f; if (active_inst == nullptr) { NullCameraInstance.bt.motionState->getWorldTransform(instPos.posRot); instPos.scale = NullCameraInstance.bt.rigidBody->getCollisionShape()->getLocalScaling(); } else { active_inst->bt.motionState->getWorldTransform(instPos.posRot); instPos.scale = active_inst->bt.rigidBody->getCollisionShape()->getLocalScaling(); } CompInstance *camInst = ECS_GetInstance(cam.phys.instHandle); ECS_UpdateInstance(camInst, instPos, true); } ImGui::SameLine(); if (ImGui::Button("Make Primary")) { PkeCamera_SetPrimary(cam.camHandle); } ImGui::PopID(); cam_counter += 1; b = pk_bkt_arr_iter_increment(bkt_arr_cams, &iter_cam); } ImGui::EndTable(); } if (selectedCamera != CameraHandle_MAX) { // int inputTextFlags = ImGuiInputTextFlags_ReadOnly; auto *cam = PkeCamera_Get(selectedCamera); if (cam) { bool isOrientTarget{bool(static_cast(cam->view & PKE_CAMERA_VIEW_TARGET))}; bool isOrientFree{bool(static_cast(cam->view & PKE_CAMERA_VIEW_FREE))}; 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->view = PKE_CAMERA_VIEW_TARGET; } ImGui::EndDisabled(); ImGui::SameLine(); ImGui::BeginDisabled(isOrientFree); if (ImGui::Button("Free")) { cam->view = PKE_CAMERA_VIEW_FREE; } ImGui::EndDisabled(); } } ImGui::End(); } void RecordImGuiUITree_inner(pke_ui_box *box) { if (ImGui::TreeNode(box, pk_uuid_printf_format, pk_uuid_printf_var(box->uuid))) { ImGui::SameLine(); if (ImGui::Selectable("SELECT", false, 0)) { selected_ui_box = box; } for (int i = 0; i < box->internal.h_children; ++i) { RecordImGuiUITree_inner(box->internal.children[i]); } ImGui::TreePop(); } } void RecordImGuiUITree() { pke_ui_box *box; bool clicked = false; static bool is_creating_new_box = false; static bool is_child = false; PKE_UI_BOX_TYPE type = PKE_UI_BOX_TYPE(-1); if (!ImGui::Begin("pke_ui_tree")) { ImGui::End(); return; } ImGui::BeginDisabled(selected_ui_box != nullptr); if (ImGui::Button("Create Root Box")) { is_creating_new_box = true; is_child = false; } ImGui::EndDisabled(); ImGui::SameLine(); ImGui::BeginDisabled(selected_ui_box == nullptr); if (ImGui::Button("Create Child Box")) { is_creating_new_box = true; is_child = true; } ImGui::SameLine(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1, .2, .2, 1)); clicked = ImGui::Button("Delete"); ImGui::PopStyleColor(); ImGui::EndDisabled(); if (clicked) { ECS_MarkForRemoval(selected_ui_box); if (selected_ui_box->internal.parent == nullptr) { pke_level_unregister_root_ui_box(pkeSettings.rt.activeLevel, selected_ui_box); } selected_ui_box = selected_ui_box->internal.parent; ImGui::End(); return; } if (is_creating_new_box) { ImGui::Text("Select Type:"); ImGui::Separator(); if (ImGui::Button("Standard")) { type = PKE_UI_BOX_TYPE_STANDARD; } if (ImGui::Button("Text")) { type = PKE_UI_BOX_TYPE_TEXT; } if (ImGui::Button("Button: Text")) { type = PKE_UI_BOX_TYPE_BUTTON_TEXT; } if (ImGui::Button("Button: Image")) { type = PKE_UI_BOX_TYPE_BUTTON_IMAGE; } if (type != PKE_UI_BOX_TYPE(-1)) { if (is_child && selected_ui_box != nullptr) { box = pke_ui_box_new_child(selected_ui_box, type); } else { box = pke_ui_box_new_root(type); pke_level_register_root_ui_box(pkeSettings.rt.activeLevel, box); } box->flags = PKE_UI_BOX_FLAG_POSITION_TYPE_DYNAMIC; box->flags |= PKE_UI_BOX_FLAG_CENTER_BOTH; box->max_size = glm::vec2(.6, .6); selected_ui_box = box; FontRenderSettings frs{}; if (type == PKE_UI_BOX_TYPE_TEXT) { box->type_data->text.font_type_render = FontType_AddStringRender(FontTypeHandle{0,0}, std::move(cstring_to_pk_cstr("")), &frs, box); } if (type == PKE_UI_BOX_TYPE_BUTTON_TEXT) { box->type_data->button_text.font_type_render = FontType_AddStringRender(FontTypeHandle{0,0}, std::move(cstring_to_pk_cstr("")), &frs, box); } is_creating_new_box = false; is_child = false; type = PKE_UI_BOX_TYPE(-1); } } pke_ui_box_count_T count; pke_ui_box ** root_boxes = pke_ui_get_root_boxes(&count); for (int i = 0; i < count; ++i) { box = root_boxes[i]; RecordImGuiUITree_inner(box); } // if (ImGui::Selectable( ImGui::End(); } void RecordImGuiUIEdit() { static AssetPickerSearchStruct apss_img_default { .source = {"cet_txr"}, .safeKey = {""}, .type = PKE_ASSET_TYPE_TEXTURE, }; static AssetPickerSearchStruct apss_img_hovered { .source = {"cet_txr"}, .safeKey = {""}, .type = PKE_ASSET_TYPE_TEXTURE, }; static AssetPickerSearchStruct apss_img_pressed { .source = {"cet_txr"}, .safeKey = {""}, .type = PKE_ASSET_TYPE_TEXTURE, }; static uint8_t helper_selected_index = 0; const ImGuiInputTextFlags text_flags = ImGuiInputTextFlags_AllowTabInput; bool changed, changed_sub; if (!ImGui::Begin("pke_ui_edit")) { ImGui::End(); return; } if (selected_ui_box == NULL) { ImGui::End(); return; } changed = false; changed_sub = false; ImGui::Text("Position Type:"); ImGui::BeginDisabled(PK_HAS_FLAG(selected_ui_box->flags, PKE_UI_BOX_FLAG_POSITION_TYPE_FLEX)); ImGui::BeginDisabled(selected_ui_box->internal.parent == nullptr); if (ImGui::Button("Flex")) { selected_ui_box->flags &= ~(const_cast(PKE_UI_BOX_FLAG_POSITION_TYPE_ALL)); selected_ui_box->flags |= PKE_UI_BOX_FLAG_POSITION_TYPE_FLEX; changed = true; } ImGui::EndDisabled(); ImGui::EndDisabled(); ImGui::SameLine(); ImGui::BeginDisabled(PK_HAS_FLAG(selected_ui_box->flags, PKE_UI_BOX_FLAG_POSITION_TYPE_STATIC)); if (ImGui::Button("Static")) { selected_ui_box->flags &= ~(const_cast(PKE_UI_BOX_FLAG_POSITION_TYPE_ALL)); selected_ui_box->flags |= PKE_UI_BOX_FLAG_POSITION_TYPE_STATIC; changed = true; } ImGui::EndDisabled(); ImGui::SameLine(); ImGui::BeginDisabled(PK_HAS_FLAG(selected_ui_box->flags, PKE_UI_BOX_FLAG_POSITION_TYPE_DYNAMIC)); if (ImGui::Button("Dynamic")) { selected_ui_box->flags &= ~(const_cast(PKE_UI_BOX_FLAG_POSITION_TYPE_ALL)); selected_ui_box->flags |= PKE_UI_BOX_FLAG_POSITION_TYPE_DYNAMIC; changed = true; } ImGui::EndDisabled(); ImGui::Text("state: %lu", static_cast(selected_ui_box->state_flags)); ImGui::Text("state_mem: %lu", static_cast(selected_ui_box->state_flags_mem)); ImGui::Separator(); if (ImGui::CheckboxFlags("flag: Center Horizontal", reinterpret_cast(&selected_ui_box->flags), static_cast(PKE_UI_BOX_FLAG_CENTER_HORIZONTAL))) { changed = true; } if (ImGui::CheckboxFlags("flag: Center Vertical", reinterpret_cast(&selected_ui_box->flags), static_cast(PKE_UI_BOX_FLAG_CENTER_VERTICAL))) { changed = true; } ImGui::Separator(); if (ImGui::CheckboxFlags("flag: Visibility Invisible", reinterpret_cast(&selected_ui_box->flags), static_cast(PKE_UI_BOX_FLAG_VISIBILITY_INVISIBLE))) { changed = true; } if (ImGui::CheckboxFlags("flag: Visibility Disabled", reinterpret_cast(&selected_ui_box->flags), static_cast(PKE_UI_BOX_FLAG_VISIBILITY_DISABLED))) { changed = true; } if (ImGui::CheckboxFlags("flag: Visibility Pixel Perfect", reinterpret_cast(&selected_ui_box->flags), static_cast(PKE_UI_BOX_FLAG_VISIBILITY_PIXEL_PERFECT))) { changed = true; } ImGui::Separator(); changed = ImGui::InputScalarN("flags", ImGuiDataType_U64, &selected_ui_box->flags, 1, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly) || changed; changed = ImGui::InputScalarN("pos_top_left", ImGuiDataType_Float, &selected_ui_box->pos_top_left, 2, nullptr, nullptr, nullptr, 0) || changed; changed = ImGui::InputScalarN("min_size", ImGuiDataType_Float, &selected_ui_box->min_size, 2, nullptr, nullptr, nullptr, 0) || changed; changed = ImGui::InputScalarN("max_size", ImGuiDataType_Float, &selected_ui_box->max_size, 2, nullptr, nullptr, nullptr, 0) || changed; if (PK_HAS_FLAG(selected_ui_box->flags, PKE_UI_BOX_FLAG_POSITION_TYPE_FLEX)) { changed = ImGui::InputScalarN("flex_weight", ImGuiDataType_Float, &selected_ui_box->flex_weight, 1, nullptr, nullptr, nullptr, 0) || changed; } changed = ImGui::InputScalarN("flex_padding", ImGuiDataType_Float, &selected_ui_box->flex_padding, 1, nullptr, nullptr, nullptr, 0) || changed; changed = ImGui::InputScalarN("flex_direction", ImGuiDataType_U8, &selected_ui_box->flex_direction, 1, nullptr, nullptr, nullptr, 0) || changed; changed = ImGui::InputScalarN("layer", ImGuiDataType_U8, &selected_ui_box->layer, 1, nullptr, nullptr, nullptr, 0) || changed; if (PK_HAS_FLAG(selected_ui_box->flags, PKE_UI_BOX_FLAG_VISIBILITY_INVISIBLE) == false) { changed = ImGui::ColorEdit4("color_border", &selected_ui_box->color_border[0]) || changed; changed = ImGui::ColorEdit4("color_background", &selected_ui_box->color_background[0]) || changed; } ImGui::Text("Internal:"); ImGui::Text("px_corner: %0.3f : %0.3f", selected_ui_box->internal.px_corner.x, selected_ui_box->internal.px_corner.y); ImGui::Text("px_size: %0.3f : %0.3f", selected_ui_box->internal.px_size.x, selected_ui_box->internal.px_size.y); if (selected_ui_box->type == PKE_UI_BOX_TYPE_TEXT) { assert(selected_ui_box->type_data != NULL); ImGui::Text("Type: Text"); ImGui::Separator(); FontRender fr = *FontType_GetFontRender(selected_ui_box->type_data->text.font_type_render); ImGui::Text("FontRender: %.4hu:%.4hu", fr.font_render_handle.b, fr.font_render_handle.i); const int buffer_len = 1024; char *text_buffer = pk_new_arr(buffer_len, pkeSettings.mem_bkt.game_transient); size_t len = fr.text.length; sprintf(text_buffer, "%s", fr.text.val); if (ImGui::InputText("Text", text_buffer, buffer_len-1, text_flags)) { // TODO specific bucket len = strlen(text_buffer); char *s = pk_new_arr(len + 1); sprintf(s, "%s", text_buffer); pk_cstr cstr{}; cstr.reserved = len+1; cstr.length = len; cstr.val = s; FontType_UpdateStringRenderText({fr.font_type_handle, fr.font_render_handle}, std::move(cstr)); changed = true; } ImGui::SameLine(); ImGui::Text("(%.4zu/%4i)", len, buffer_len); changed_sub = RecordImGui_FontRenderSettings(fr.settings, false); if (changed_sub) { FontType_UpdateStringRender({fr.font_type_handle, fr.font_render_handle}, &fr.settings); } changed |= changed_sub; } if (selected_ui_box->type == PKE_UI_BOX_TYPE_BUTTON_IMAGE) { assert(selected_ui_box->type_data != nullptr); ImGui::Text("Type: Button Image"); ImGui::Separator(); ImGui::Text("Image Default: %.16s", selected_ui_box->type_data->button_image.img_key_default); ImGui::SameLine(); if (ImGui::Button("Change Default")) { helper_selected_index = 1u << 0; ImGui::OpenPopup(apss_img_default.source); } ImGui::Text("Image Hovered: %.16s", selected_ui_box->type_data->button_image.img_key_hovered); ImGui::SameLine(); if (ImGui::Button("Change Hovered")) { helper_selected_index = 1u << 1; ImGui::OpenPopup(apss_img_hovered.source); } ImGui::Text("Image Pressed: %.16s", selected_ui_box->type_data->button_image.img_key_pressed); ImGui::SameLine(); if (ImGui::Button("Change Pressed")) { helper_selected_index = 1u << 2; ImGui::OpenPopup(apss_img_pressed.source); } if (helper_selected_index == 1u << 0) { RecordImGui_AssetPicker(apss_img_default); } if (helper_selected_index == 1u << 1) { RecordImGui_AssetPicker(apss_img_hovered); } if (helper_selected_index == 1u << 2) { RecordImGui_AssetPicker(apss_img_pressed); } if (apss_img_default.selectedItem > 0) { changed_sub = true; memcpy(selected_ui_box->type_data->button_image.img_key_default, apss_img_default.safeKey, AssetKeyLength); apss_img_default.selectedItem = -1; } if (apss_img_hovered.selectedItem > 0) { changed_sub = true; memcpy(selected_ui_box->type_data->button_image.img_key_hovered, apss_img_hovered.safeKey, AssetKeyLength); apss_img_hovered.selectedItem = -1; } if (apss_img_pressed.selectedItem > 0) { changed_sub = true; memcpy(selected_ui_box->type_data->button_image.img_key_pressed, apss_img_pressed.safeKey, AssetKeyLength); apss_img_pressed.selectedItem = -1; } if (changed_sub) { helper_selected_index = 0; pke_ui_box_update_textures(selected_ui_box); } changed |= changed_sub; } if (selected_ui_box->type == PKE_UI_BOX_TYPE_BUTTON_TEXT) { assert(selected_ui_box->type_data != NULL); ImGui::Text("Type: Button Text"); ImGui::Separator(); FontRender fr = *FontType_GetFontRender(selected_ui_box->type_data->button_text.font_type_render); ImGui::Text("FontRender: %.4hu:%.4hu", fr.font_render_handle.b, fr.font_render_handle.i); const int buffer_len = 1024; char *text_buffer = pk_new_arr(buffer_len, pkeSettings.mem_bkt.game_transient); size_t len = fr.text.length; sprintf(text_buffer, "%s", fr.text.val); if (ImGui::InputText("Text", text_buffer, buffer_len-1, text_flags)) { // TODO specific bucket len = strlen(text_buffer); char *s = pk_new_arr(len + 1); sprintf(s, "%s", text_buffer); pk_cstr cstr{}; cstr.reserved = len+1; cstr.length = len; cstr.val = s; FontType_UpdateStringRenderText({fr.font_type_handle, fr.font_render_handle}, std::move(cstr)); changed = true; } ImGui::SameLine(); ImGui::Text("(%.4zu/%4i)", len, buffer_len); changed_sub = RecordImGui_FontRenderSettings(fr.settings, false); if (changed_sub) { FontType_UpdateStringRender({fr.font_type_handle, fr.font_render_handle}, &fr.settings); } changed |= changed_sub; } if (changed) { pke_ui_force_recalc(); } 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(); } bool RecordImGui_CallbackSelectModal(long &selectedIndex) { bool returnValue = false; if (ImGui::BeginPopupModal("CallbackSelect", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { long count = 0; auto *signatures = PkePlugin_GetSortedSignatures(count); for (long i = 0; i < count; ++i) { const CallbackSignature &sig = signatures[i]; if (ImGui::Selectable(sig, selectedIndex == i)) { returnValue = true; selectedIndex = i; } } ImGui::EndPopup(); if (ImGui::IsPopupOpen("CallbackSelect") == true && count == 0) { fprintf(stderr, "[RecordImGui_CallbackSelectModal] No available callback signatures.\n"); ImGui::CloseCurrentPopup(); } } return returnValue; } void RecordImGuiModalCreateEntityType() { if (ImGui::BeginPopupModal("CreateEntityType", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { static char entityTypeCode[32] = {'\0'}; static char createInstanceSig[CallbackSignatureLength + 1] = {'\0'}; static AssetPickerSearchStruct apssModel{ .source = {"cet_mdl"}, .safeKey = {""}, .type = PKE_ASSET_TYPE_MODEL, }; static AssetPickerSearchStruct apssTexture{ .source = {"cet_txr"}, .safeKey = {""}, .type = PKE_ASSET_TYPE_TEXTURE, }; if (ImGui::Button("Model Key")) { ImGui::OpenPopup(apssModel.source); } ImGui::SameLine(); ImGui::Text("%s", apssModel.safeKey); ImGui::InputText("Entity Type Code", entityTypeCode, 31); static long index = -1; if (ImGui::Button("Clear")) { index = -1; createInstanceSig[0] = '\0'; } ImGui::SameLine(); if (ImGui::Button("Change")) { index = -1; ImGui::OpenPopup("CallbackSelect"); } ImGui::SameLine(); ImGui::Text("Create Instance Callback: '%s'", createInstanceSig); if (RecordImGui_CallbackSelectModal(index)) { long x = 0; memcpy(createInstanceSig, PkePlugin_GetSortedSignatures(x)[index], CallbackSignatureLength); } ImGui::InputScalar("Sub-Types", ImGuiDataType_S64, &entityTypeToCreate.detailsCount); ImGui::SameLine(); if (ImGui::Button("-")) { entityTypeToCreate.detailsCount -= 1; } ImGui::SameLine(); if (ImGui::Button("+")) { entityTypeToCreate.detailsCount += 1; } entityTypeToCreate.detailsCount = entityTypeToCreate.detailsCount < 1 ? 1 : entityTypeToCreate.detailsCount; entityTypeToCreate.detailsCount = entityTypeToCreate.detailsCount > EntityTypeDetails_MAX ? EntityTypeDetails_MAX : entityTypeToCreate.detailsCount; for (int64_t i = 0; i < entityTypeToCreate.detailsCount; ++i) { auto &etd = entityTypeToCreate.details[i]; ImGui::Separator(); RecordImGui_AssetPicker(apssModel); if (ImGui::Button("Texture Key")) { ImGui::OpenPopup(apssTexture.source); } ImGui::SameLine(); ImGui::Text("%s", apssTexture.safeKey); RecordImGui_AssetPicker(apssTexture); ImGui::InputScalar("Starting Instance Count", ImGuiDataType_U32, &etd.startingInstanceCount); ImGui::InputFloat("Physics - Mass", &etd.bt.startingMass); ImGui::InputScalar("Physics - Collision Layer", ImGuiDataType_U16, &etd.bt.startingCollisionLayer); ImGui::InputScalar("Physics - Collision Mask", ImGuiDataType_U16, &etd.bt.startingCollisionMask); } ImGui::Separator(); bool shouldReset = false; if (ImGui::Button("Create")) { // TODO some type of validation strncpy(entityTypeToCreate.createInstanceCallback.name, createInstanceSig, CallbackSignatureLength); strncpy(entityTypeToCreate.modelAssetKey, apssModel.safeKey, AssetKeyLength); // TODO this needs to be an array strncpy(entityTypeToCreate.details[0].textureAssetKey, apssTexture.safeKey, AssetKeyLength); char *sEntityTypeCode = pk_new_arr(strlen(entityTypeCode) + 1); strncpy(sEntityTypeCode, entityTypeCode, 31); entityTypeToCreate.entityTypeCode.val = sEntityTypeCode; shouldCreateEntityType = true; shouldReset = true; } ImGui::SameLine(); if (ImGui::Button("Cancel")) { shouldReset = true; } if (shouldReset) { createInstanceSig[0] = '\0'; apssModel.safeKey[0] = '\0'; apssTexture.safeKey[0] = '\0'; entityTypeCode[0] = '\0'; createInstanceSig[CallbackSignatureLength] = '\0'; apssModel.safeKey[AssetKeyLength] = '\0'; apssTexture.safeKey[AssetKeyLength] = '\0'; entityTypeCode[31] = '\0'; ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } } void RecordImGuiLevels() { bool b; pk_bkt_arr *bkt_arr_levels; pk_iter_t iter_level{}; if (!ImGui::Begin("Levels")) { ImGui::End(); return; } static ImGuiTableFlags tableFlags{ ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg }; if (ImGui::BeginTable("pke_levels", 3, tableFlags)) { ImGui::TableSetupColumn("Name"); ImGui::TableSetupColumn("Handle"); ImGui::TableHeadersRow(); bkt_arr_levels = pke_level_get_levels(); b = pk_bkt_arr_iter_begin(bkt_arr_levels, &iter_level); while (b == true) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text("%s", iter_level->name); ImGui::TableSetColumnIndex(1); ImGui::Text("0x%08X 0x%08X", iter_level->levelHandle.b, iter_level->levelHandle.i); b = pk_bkt_arr_iter_increment(bkt_arr_levels, &iter_level); } ImGui::EndTable(); } ImGui::End(); } 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 (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, swapchainLength, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); } if (component->vertexBD.buffer) ImGui::InputScalar("VkVertexBuffer", ImGuiDataType_U64, &component->vertexBD.buffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("VertexFirstBinding", ImGuiDataType_U32, &component->vertexBD.firstBinding, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("VertexBindingCount", ImGuiDataType_U32, &component->vertexBD.bindingCount, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("VertexOffsets", ImGuiDataType_U64, &component->vertexBD.offsets[0], nullptr, nullptr, nullptr, inputTextFlags); if (component->normalsBD.buffer) ImGui::InputScalar("VkNormalBuffer", ImGuiDataType_U64, &component->normalsBD.buffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("NormalFirstBinding", ImGuiDataType_U32, &component->normalsBD.firstBinding, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("NormalBindingCount", ImGuiDataType_U32, &component->normalsBD.bindingCount, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("NormalOffsets", ImGuiDataType_U64, &component->normalsBD.offsets[0], nullptr, nullptr, nullptr, inputTextFlags); if (component->uvBD.buffer) ImGui::InputScalar("VkUVBuffer", ImGuiDataType_U64, &component->uvBD.buffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("UVFirstBinding", ImGuiDataType_U32, &component->uvBD.firstBinding, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("UVBindingCount", ImGuiDataType_U32, &component->uvBD.bindingCount, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("UVOffsets", ImGuiDataType_U64, &component->uvBD.offsets[0], nullptr, nullptr, nullptr, inputTextFlags); if (component->indexBD.buffer) ImGui::InputScalar("VkIndexBuffer", ImGuiDataType_U64, &component->indexBD.buffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("IndexBindingCount", ImGuiDataType_U32, &component->indexBD.firstBinding, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("IndexOffsets", ImGuiDataType_U64, &component->indexBD.offsets[0], nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("IndexCount", ImGuiDataType_U32, &component->indexCount, nullptr, nullptr, nullptr, inputTextFlags); if (component->instanceBD.buffer) ImGui::InputScalar("VkInstanceBuffer", ImGuiDataType_U64, &component->instanceBD.buffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); ImGui::InputScalar("InstanceFirstBinding", ImGuiDataType_U32, &component->instanceBD.firstBinding, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("InstanceBindingCount", ImGuiDataType_U32, &component->instanceBD.bindingCount, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("InstanceOffsets", ImGuiDataType_U64, &component->instanceBD.offsets[0], 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); static long index = -1; if (ImGui::Button("Clear")) { index = -1; component->collisionCallback.name[0] = '\0'; } ImGui::SameLine(); if (ImGui::Button("Change")) { index = -1; ImGui::OpenPopup("CallbackSelect"); } ImGui::SameLine(); ImGui::Text("Collision Callback: '%s'", component->collisionCallback.name); if (RecordImGui_CallbackSelectModal(index)) { long x = 0; memcpy(component->collisionCallback.name, PkePlugin_GetSortedSignatures(x)[index], CallbackSignatureLength); PkePlugin_SetSignatureFunc(&component->collisionCallback); } 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); ImGui::Text("InstanceHandle: 0x%08X 0x%08X", component->instanceHandle.b, component->instanceHandle.i); 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; static long index = -1; if (ImGui::Button("Clear")) { index = -1; component->collisionCallback.name[0] = '\0'; } ImGui::SameLine(); if (ImGui::Button("Change")) { index = -1; ImGui::OpenPopup("CallbackSelect"); } ImGui::SameLine(); ImGui::Text("Collision Callback: '%s'", component->collisionCallback.name); if (RecordImGui_CallbackSelectModal(index)) { long x = 0; memcpy(component->collisionCallback.name, PkePlugin_GetSortedSignatures(x)[index], 16); PkePlugin_SetSignatureFunc(&component->collisionCallback); } 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); // exclude EntityHandle_MAX so you can't target via the NullCamera ImGui::BeginDisabled(ActiveCamera->handle == component->entHandle || ActiveCamera->handle == EntityHandle_MAX || ActiveCamera->phys.instHandle == InstanceHandle_MAX || PkeCamera_Get(selectedEntity->entHandle) != nullptr); if (ImGui::Button("Set Active Camera Target")) { PkeCamera_TargetInstance(ActiveCamera->camHandle, component); } ImGui::SameLine(); ImGui::Text("Active Camera: 0x%08X 0x%08X", ActiveCamera->handle.b, ActiveCamera->handle.i); ImGui::EndDisabled(); 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, instPos, true); } ImGui::Spacing(); } void RecordImGuiProjectSettingsEditor() { if (!ImGui::Begin("ProjectSettings")) { ImGui::End(); return; } if (ImGui::Button("Save")) { shouldSaveProjectFile = true; } ImGui::End(); } struct fsEntry { int type = 0; char *name = nullptr; char *path = nullptr; pk_arr_t children{}; }; pk_arr_t fsEntries{}; std::regex reg_sceneFile(".+\\.ps[tb]f$", std::regex_constants::icase); int fsEntryComp(const void *a, const void *b) { const auto &fsA = *static_cast(a); const auto &fsB = *static_cast(b); if (fsA.type == fsB.type) { return strcmp(fsA.name, fsB.name); } auto cmp = fsA.type <=> fsB.type; if (cmp == std::strong_ordering::less) { return -1; } if (cmp == std::strong_ordering::greater) { return 1; } return 0; } void SortRecursive(pk_arr_t &arr) { std::qsort(arr.data, arr.next, sizeof(fsEntry), fsEntryComp); for (uint32_t i = 0; i < arr.next; ++i) { SortRecursive(arr[i].children); } } void BuildDirRecursive(const std::filesystem::directory_entry &de, fsEntry *dirFsEntry) { fsEntry *entry_ptr; if (dirFsEntry == nullptr) { pk_arr_append_t(&fsEntries, {}); entry_ptr = &fsEntries[fsEntries.next-1]; } else { pk_arr_append_t(&dirFsEntry->children, {}); entry_ptr = &dirFsEntry->children[dirFsEntry->children.next-1]; } fsEntry &entry = *entry_ptr; auto fullPath = std::filesystem::absolute(de.path()); auto len = strlen(fullPath.c_str()); // TODO leaky entry.path = pk_new_arr(len + 1); memset(entry.path, '\0', len + 1); memcpy(entry.path, fullPath.c_str(), len); len = strlen(fullPath.filename().c_str()); // TODO leaky entry.name = pk_new_arr(len + 1); memset(entry.name, '\0', len + 1); memcpy(entry.name, fullPath.filename().c_str(), len); if (de.is_regular_file()) { if (std::regex_search(fullPath.c_str(), reg_sceneFile)) { entry.type = 1; } } else if (de.is_directory()) { entry.type = 0; std::filesystem::directory_iterator di{fullPath}; for (const std::filesystem::directory_entry &sde : di) { if (sde.is_directory() || (sde.is_regular_file() && std::regex_search(sde.path().c_str(), reg_sceneFile)) ) { BuildDirRecursive(sde, &entry); } } } } void BuildProjectMenuRecursive(fsEntry &entry) { if (entry.type == 1) { if (ImGui::Selectable(entry.name, false, ImGuiSelectableFlags_AllowDoubleClick) && ImGui::IsMouseDoubleClicked(0)) { assert(editor_mstr.target_scene_path.val == nullptr); editor_mstr.target_scene_path.length = strlen(entry.name); editor_mstr.target_scene_path.reserved = editor_mstr.target_scene_path.length + 1; editor_mstr.target_scene_path.val = pk_new_arr(editor_mstr.target_scene_path.reserved); strncpy(editor_mstr.target_scene_path.val, entry.name, editor_mstr.target_scene_path.reserved); ActiveCamera = &NullCamera; } } else if (entry.type == 0) { if (ImGui::TreeNode(entry.name)) { for (uint32_t i = 0; i < entry.children.next; ++i) { BuildProjectMenuRecursive(entry.children[i]); } ImGui::TreePop(); } } } void RecordImGuiProjectBrowser() { if (!ImGui::Begin("ProjectBrowser")) { ImGui::End(); return; } if (shouldRebuildProjectDir == true) { shouldRebuildProjectDir = false; pk_arr_clear(&fsEntries); std::filesystem::directory_iterator di{std::filesystem::current_path()}; for (const std::filesystem::directory_entry &sde : di) { if (sde.is_directory() || (sde.is_regular_file() && std::regex_search(sde.path().c_str(), reg_sceneFile)) ) { BuildDirRecursive(sde, nullptr); } } SortRecursive(fsEntries); } for (uint32_t i = 0; i < fsEntries.next; ++i) { BuildProjectMenuRecursive(fsEntries[i]); } ImGui::End(); } void RecordImGuiSceneBrowser() { bool b; EntityType *entType = NULL; pk_bkt_arr *bkt_arr_instances; pk_iter_t iter_inst{}; NULL_CHAR_ARR(text, 128); if (!ImGui::Begin("SceneBrowser", &pkeSettings.editorSettings.isShowingSceneEditor)) { ImGui::End(); return; } bkt_arr_instances = ECS_GetInstances(); b = pk_bkt_arr_iter_begin(bkt_arr_instances, &iter_inst); while(b == true) { entType = NULL; CompGrBinds *grBinds = ECS_GetGrBinds(iter_inst->grBindsHandle); if (grBinds != NULL) { entType = static_cast(ECS_GetEntity(grBinds->entHandle)); } sprintf(text, "%08x %08x", iter_inst->instanceHandle.b, iter_inst->instanceHandle.i); if (ImGui::Button(text)) { selectedEntity = iter_inst; } ImGui::SameLine(); sprintf(text, "EntityType: %s", entType != nullptr ? entType->entityTypeCode.val : "(no type)"); ImGui::Text("%s", text); b = pk_bkt_arr_iter_increment(bkt_arr_instances, &iter_inst); } ImGui::End(); } void RecordImGuiSceneEditor() { if (!ImGui::Begin("SceneEditor", &pkeSettings.editorSettings.isShowingSceneEditor)) { ImGui::End(); return; } if (ImGui::Button("Create Asset")) { ImGui::OpenPopup("CreateAsset"); } if (ImGui::Button("Create Entity Type")) { ImGui::OpenPopup("CreateEntityType"); } if (ImGui::Button("Generate MTSDF")) { ImGui::OpenPopup("MTSDFModal"); } if (ImGui::Button("Generate UI MTSDFs")) { ImGui::OpenPopup("MTSDFGlyphModal"); } if (ImGui::Button("Clear Selection")) { selectedEntity = nullptr; selected_ui_box = nullptr; } ImGui::Spacing(); if (selectedEntity != nullptr) { // TODO leaky static pk_arr_t entGrBinds; static pk_arr_t entInstances; static EntityType *entType; bool reset = false; if (entGrBinds.next > 0) { if (entGrBinds[0]->entHandle != selectedEntity->entHandle) { reset = true; } } else if (entInstances.next > 0) { if (entInstances[0]->entHandle != selectedEntity->entHandle) { reset = true; } } reset = reset || (entGrBinds.next == 0 && entInstances.next == 0); if (reset) { pk_arr_clear(&entGrBinds); pk_arr_clear(&entInstances); entType = nullptr; } if (entGrBinds.next == 0) { CompGrBinds *grBinds = ECS_GetGrBinds(selectedEntity->grBindsHandle); if (grBinds != NULL) { ECS_GetGrBinds(ECS_GetEntity(grBinds->entHandle), entGrBinds); } } if (entInstances.next == 0) { ECS_GetInstances(ECS_GetEntity(selectedEntity->entHandle), entInstances); } if (entGrBinds.next > 0) { if (ImGui::Button("Create Instance")) { Entity_Base *entity = ECS_GetEntity(selectedEntity->entHandle); auto *entityType = static_cast(entity); pk_arr_append_t(&entityInstancesToCreate, entityType); } } if (selectedEntity->grBindsHandle != GrBindsHandle_MAX) { entType = static_cast(ECS_GetEntity(ECS_GetGrBinds(selectedEntity->grBindsHandle)->entHandle)); } if (entType != nullptr) { ImGui::Text("%s: %s", "EntType: ", entType->entityTypeCode.val); } else { ImGui::Text("%s: %s", "EntType: ", "Unknown"); } ImGui::Text("%s: %08x %08x", "Entity Handle: ", selectedEntity->entHandle.b, selectedEntity->entHandle.i); for (int64_t i = 0; i < entGrBinds.next; ++i) { RecordImGui_CompGrBinds(true, entGrBinds[i]); } for (int64_t i = 0; i < entInstances.next; ++i) { RecordImGui_CompInstPos(false, entInstances[i]); } } RecordImGuiModalCreateAsset(); RecordImGuiModalCreateEntityType(); RecordImGui_GenerateMTSDFModal(); RecordImGui_GenerateMTSDFGlyphModal(); ImGui::End(); } void PkeEditor_RecordImGui() { if (pkeSettings.isShowingEditor) { RecordImGuiEditorWrapper(); RecordImGuiProjectSettingsEditor(); RecordImGuiProjectBrowser(); RecordImGuiSceneBrowser(); RecordImGuiSceneEditor(); RecordImGuiUBO(); RecordImGuiCameras(); RecordImGuiUITree(); RecordImGuiUIEdit(); RecordImGuiLevels(); RecordImGuiEntityTypes(); RecordImGuiAssets(); RecordImGuiPlayerInput(); RecordImGuiFont(); if (editor_mstr.rt.show_font_glyphs) { RecordImGuiFontGlyphs(); } } } void PkeEditor_Setup() { shouldSetupEditor = false; pke_input_activate_set(debugControlsHandle); } void PkeEditor_Disable() { shouldDisableEditor = false; if (debugControlsHandle != pke_input_action_set_handle_MAX) pke_input_deactivate_set(debugControlsHandle); } void PkeEditor_Teardown() { PkeEditor_Disable(); if (subProgramPid != -1) { kill(subProgramPid, SIGINT); } if (threadPoolHandle != ThreadPoolHandle_MAX) PkeThreads_Teardown(threadPoolHandle); pk_arr_reset(&entityInstancesToCreate); pke_input_unregister_set(debugControlsHandle); } void PkeEditor_Init() { pkeSettings.isSimulationPaused = true; pkeSettings.isShowingEditor = true; pke_input_set debugControlsSet{}; debugControlsSet.title = "debug-editor-controls"; debugControlsSet.actionCount = 14; debugControlsSet.bkt = pkeSettings.mem_bkt.game; debugControlsSet.actions = pk_new_arr(debugControlsSet.actionCount, debugControlsSet.bkt); debugControlsSet.flags = PKE_INPUT_ACTION_SET_FLAG_DO_NOT_SERIALIZE; debugControlsSet.actions[0].name = dbgCtrl_CameraLeft; debugControlsSet.actions[0].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_A, }; debugControlsSet.actions[1].name = dbgCtrl_CameraRight; debugControlsSet.actions[1].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_D, }; debugControlsSet.actions[2].name = dbgCtrl_CameraForward; debugControlsSet.actions[2].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_W, }; debugControlsSet.actions[3].name = dbgCtrl_CameraBack; debugControlsSet.actions[3].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_S, }; debugControlsSet.actions[4].name = dbgCtrl_CameraUp; debugControlsSet.actions[4].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_R, }; debugControlsSet.actions[5].name = dbgCtrl_CameraDown; debugControlsSet.actions[5].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_F, }; debugControlsSet.actions[6].name = dbgCtrl_CameraRotCC; debugControlsSet.actions[6].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_Q, }; debugControlsSet.actions[7].name = dbgCtrl_CameraRotC; debugControlsSet.actions[7].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_E, }; debugControlsSet.actions[8].name = dbgCtrl_CameraRot; debugControlsSet.actions[8].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_CURSOR_POS_EVENTS, }; debugControlsSet.actions[9].name = dbgCtrl_CameraButtonMask; debugControlsSet.actions[9].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_MOUSE_BUTTON_EVENTS, .button = GLFW_MOUSE_BUTTON_MIDDLE, }; debugControlsSet.actions[10].name = dbgCtrl_SelectHovered; debugControlsSet.actions[10].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_MOUSE_BUTTON_EVENTS, .button = GLFW_MOUSE_BUTTON_LEFT, }; debugControlsSet.actions[11].name = dbgCtrl_ClearSelection; debugControlsSet.actions[11].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_MOUSE_BUTTON_EVENTS, .button = GLFW_MOUSE_BUTTON_RIGHT, }; debugControlsSet.actions[12].name = dbgCtrl_DeleteSelectedItem; debugControlsSet.actions[12].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_DELETE, }; debugControlsSet.actions[13].name = dbgCtrl_ImGui_Toggle; debugControlsSet.actions[13].masks[0] = pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = GLFW_KEY_F1, }; debugControlsHandle = pke_input_register_set(std::move(debugControlsSet)); btTransform trfm{}; trfm.setIdentity(); trfm.setOrigin(btVector3(-40.f, -40.f, -40.f)); trfm.setRotation(btQuaternion(0.f, 0.f, 0.f, 1.f)); NullCameraInstance.bt.motionState->setWorldTransform(trfm); NullCameraInstance.bt.rigidBody->setWorldTransform(trfm); NullCameraInstance.bt.rigidBody->activate(); NullCamera.type = PKE_CAMERA_TYPE_PERSPECTIVE, NullCamera.view = PKE_CAMERA_VIEW_FREE, NullCamera.stale = PKE_CAMERA_STALE_ALL, threadPoolHandle = PkeThreads_Init(1, 1); }