summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2023-11-29 21:07:06 -0500
committerJonathan Bradley <jcb@pikum.xyz>2023-11-29 21:07:06 -0500
commit9830bc2425385de6e666251fa9df6318605c639c (patch)
treee52494a973ad8957c30cf7dacf92f1fa518105dc /editor
parente111f78d519363a3d76237ff0a62dcbd5e53e3d6 (diff)
major refactor to decouple the engine and the editor
Diffstat (limited to 'editor')
-rw-r--r--editor/CMakeLists.txt2
-rw-r--r--editor/editor.cpp753
-rw-r--r--editor/editor.hpp10
-rw-r--r--editor/main.cpp14
4 files changed, 777 insertions, 2 deletions
diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt
index 0b1c228..bd176c4 100644
--- a/editor/CMakeLists.txt
+++ b/editor/CMakeLists.txt
@@ -2,6 +2,8 @@ project(pke_editor VERSION 0.0)
set(PKE_EDITOR_SOURCE_FILES
main.cpp
+ editor.hpp
+ editor.cpp
)
add_executable(pke_editor
${PKE_EDITOR_SOURCE_FILES}
diff --git a/editor/editor.cpp b/editor/editor.cpp
new file mode 100644
index 0000000..6f887ed
--- /dev/null
+++ b/editor/editor.cpp
@@ -0,0 +1,753 @@
+
+#include "editor.hpp"
+
+#include "thread_pool.hpp"
+#include "game.hpp"
+#include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.h"
+#include "math-helpers.hpp"
+#include "camera.hpp"
+#include "entities.hpp"
+#include "game-settings.hpp"
+#include "player-input.hpp"
+#include "window.hpp"
+#include "vendor/glm_include.hpp"
+#include "vendor/tinyfiledialogs//tinyfiledialogs.h"
+#include "imgui.h"
+#include <future>
+
+const char *dbgCtrl_CameraLeft = "debug-camera-left";
+const char *dbgCtrl_CameraRight = "debug-camera-right";
+const char *dbgCtrl_CameraForward = "debug-camera-forward";
+const char *dbgCtrl_CameraBack = "debug-camera-back";
+const char *dbgCtrl_CameraUp = "debug-camera-up";
+const char *dbgCtrl_CameraDown = "debug-camera-down";
+const char *dbgCtrl_CameraRotCC = "debug-camera-rot-counter-clockwise";
+const char *dbgCtrl_CameraRotC = "debug-camera-rot-clockwise";
+const char *dbgCtrl_CameraRot = "debug-camera-rot";
+const char *dbgCtrl_UnlockCamera = "debug-camera-unlock";
+const char *dbgCtrl_SelectHovered = "debug-select-hovered";
+const char *dbgCtrl_ClearSelection = "debug-clear-selection";
+
+ThreadPoolHandle threadPoolHandle = ThreadPoolHandle_MAX;
+
+PkeCamera cameraDefault {
+ .pos = glm::vec3(-40.f, -40.f, -40.f),
+ .rot = glm::quat(1.f, 0.f, 0.f, 0.f),
+ .target = glm::vec3(0.f),
+ .type = PKE_CAMERA_TYPE_PERSPECTIVE,
+ .orientation = PKE_CAMERA_ORIENTATION_TARGET,
+ .stale = PKE_CAMERA_STALE_ALL,
+};
+PkeCamera cameraDbg {
+ .pos = glm::vec3(4.f, 4.f, 4.f),
+ .rot = glm::quat(1.f, 0.f, 0.f, 0.f),
+ .target = glm::vec3(0.f),
+ .type = PKE_CAMERA_TYPE_PERSPECTIVE,
+ .orientation = PKE_CAMERA_ORIENTATION_FREE,
+ .stale = PKE_CAMERA_STALE_ALL,
+};
+
+InputActionSetHandle debugControlsHandle = InputActionSetHandle_MAX;
+bool shouldSetupEditor = true;
+bool shouldDisableEditor = false;
+bool shouldLockCamera = false;
+bool shouldUnlockCamera = false;
+
+struct EntityTypeInstanceCreateInfo {
+ EntityHandle entityTypeEntityHandle;
+};
+DynArray<EntityTypeInstanceCreateInfo> entityInstancesToCreate{16};
+EntityHandle selectedEntity = EntityHandle_MAX;
+EntityHandle hoveredEntity = EntityHandle_MAX;
+bool shouldCreateEntityType = false;
+EntityType entityTypeToCreate{};
+
+char *sceneName = nullptr;
+bool shouldOpenLoadSceneDialog = false;
+bool shouldOpenSaveSceneDialog = false;
+bool shouldLoadScene = false;
+bool shouldSaveScene = false;
+
+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) {
+ if (shouldSetupEditor) {
+ PkeEditor_Setup();
+ }
+ if (shouldDisableEditor) {
+ PkeEditor_Teardown();
+ }
+ if (shouldOpenLoadSceneDialog) {
+ shouldOpenLoadSceneDialog = false;
+ PkeThreads_Enqueue(threadPoolHandle, std::move(std::packaged_task<void()>( [] {
+ const char * patterns[1] = {"*.pstf"};
+ char *selectedScene = tinyfd_openFileDialog(nullptr, nullptr, 1, patterns, "Pke Scene Text File", 0);
+ if (selectedScene != nullptr) {
+ sceneName = selectedScene;
+ shouldLoadScene = true;
+ }
+ })));
+ }
+ if (shouldLoadScene && sceneName) {
+ shouldLoadScene = false;
+ 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 (EntitiesToBeRemoved.Has(selectedEntity)) {
+ selectedEntity = EntityHandle_MAX;
+ }
+ if (EntitiesToBeRemoved.Has(hoveredEntity)) {
+ hoveredEntity = EntityHandle_MAX;
+ }
+
+ bool imGuiHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow);
+
+ if (selectedEntity != EntityHandle_MAX && !imGuiHovered) {
+ auto holder = PkeInput_Query(dbgCtrl_ClearSelection);
+ if (holder.type != InputEventHash{0}) {
+ const PkeMouseButtonEvent *mbEvent = static_cast<PkeMouseButtonEvent *>(holder.ptr);
+ if (mbEvent->isPressed) {
+ selectedEntity = EntityHandle_MAX;
+ }
+ }
+ }
+ // raycast for hovering
+ hoveredEntity = EntityHandle_MAX;
+ if (!imGuiHovered) {
+ double xMousePos, yMousePos;
+ glfwGetCursorPos(window, &xMousePos, &yMousePos);
+
+ glm::vec3 fromCoords{unproject(glm::vec3(xMousePos, yMousePos, -1.f))};
+ glm::vec3 toCoords{unproject(glm::vec3(xMousePos, yMousePos, 1.f))};
+
+ btVector3 rayOrigin{};
+ btVector3 rayDestination{};
+ GlmToBullet(fromCoords, rayOrigin);
+ GlmToBullet(toCoords, rayDestination);
+
+ btCollisionWorld::ClosestRayResultCallback rayResult{rayOrigin, rayDestination};
+ rayResult.m_flags |= btTriangleRaycastCallback::kF_FilterBackfaces;
+
+ BtDynamicsWorld->rayTest(rayOrigin, rayDestination, rayResult);
+ if (rayResult.hasHit()) {
+ EntityHandle_T hoveredEntity_T{reinterpret_cast<EntityHandle_T>(rayResult.m_collisionObject->getUserPointer())};
+ hoveredEntity = EntityHandle{hoveredEntity_T};
+ }
+ }
+
+ if (hoveredEntity != EntityHandle_MAX) {
+ auto holder = PkeInput_Query(dbgCtrl_SelectHovered);
+ if (holder.type != InputEventHash{0}) {
+ const PkeMouseButtonEvent *mbEvent = static_cast<PkeMouseButtonEvent *>(holder.ptr);
+ if (mbEvent->isPressed) {
+ selectedEntity = hoveredEntity;
+ }
+ }
+ }
+
+ EntityHandle focusedHandle = selectedEntity != EntityHandle_MAX ? selectedEntity : hoveredEntity;
+ if (focusedHandle != EntityHandle_MAX) {
+ const auto *inst = ECS_GetInstance(focusedHandle);
+ const auto *grBinds = ECS_GetGrBinds(inst->grBindsHandle);
+ pkeDebugHitbox.instanceBuffer = grBinds->instanceBuffer;
+ pkeDebugHitbox.instanceStartingIndex = inst->index;
+ } else {
+ pkeDebugHitbox.instanceBuffer = VK_NULL_HANDLE;
+ pkeDebugHitbox.instanceStartingIndex = 0;
+ }
+
+ if (shouldCreateEntityType) {
+ assert(entityTypeToCreate.entityHandle == EntityHandle_MAX);
+ // TODO this needs to be more elegant
+ int64_t existingEntityTypeIndex = EntityType_FindByTypeCode(entityTypeToCreate.entityTypeCode);
+ if (existingEntityTypeIndex == -1) {
+ entityTypeToCreate.entityHandle = ECS_CreateEntity();
+ EntityType_Load(entityTypeToCreate);
+ GlobalEntityTypes.Push(entityTypeToCreate);
+ }
+ entityTypeToCreate = EntityType{};
+ shouldCreateEntityType = false;
+ }
+ while (entityInstancesToCreate.Count() > 0) {
+ auto createInfo = entityInstancesToCreate.Pop();
+ // TODO needs to be more elegant
+ int64_t etIndex = EntityType_FindByEntityHandle(createInfo.entityTypeEntityHandle);
+ auto &et = GlobalEntityTypes[etIndex];
+ EntityHandle newEntity = ECS_CreateEntity();
+ auto &compInst = ECS_CreateInstance(newEntity, createInfo.entityTypeEntityHandle);
+
+ compInst.physicsLayer = et.bt.startingCollisionLayer;
+ compInst.physicsMask = et.bt.startingCollisionMask;
+ btVector3 localInertia(0, 0, 0);
+ et.bt.shape->calculateLocalInertia(et.bt.startingMass, localInertia);
+ btTransform posRot{};
+ posRot.setIdentity();
+ compInst.bt.motionState = Pke_New<btDefaultMotionState>(MemBkt_Bullet);
+ new (compInst.bt.motionState) btDefaultMotionState(posRot);
+ compInst.bt.rigidBody = Pke_New<btRigidBody>(MemBkt_Bullet);
+ new (compInst.bt.rigidBody) btRigidBody(et.bt.startingMass, compInst.bt.motionState, et.bt.shape, localInertia);
+ compInst.bt.rigidBody->setLinearVelocity(btVector3(0,0,0));
+ compInst.bt.rigidBody->setAngularVelocity(btVector3(0,0,0));
+ compInst.bt.rigidBody->getCollisionShape()->setLocalScaling(btVector3(1, 1, 1));
+ BtDynamicsWorld->addRigidBody(compInst.bt.rigidBody);
+ compInst.bt.rigidBody->getBroadphaseProxy()->m_collisionFilterGroup = static_cast<PhysicsCollision_T>(et.bt.startingCollisionLayer);
+ compInst.bt.rigidBody->getBroadphaseProxy()->m_collisionFilterMask = static_cast<PhysicsCollision_T>(et.bt.startingCollisionMask);
+ compInst.bt.rigidBody->setUserPointer(reinterpret_cast<void *>(compInst.entHandle));
+ }
+
+ PkeInputEventHolder holder = PkeInput_Query(dbgCtrl_UnlockCamera);
+ if (holder.type != InputEventHash{0}) {
+ PkeKeyEvent *keyEsc;
+ keyEsc = static_cast<PkeKeyEvent *>(holder.ptr);
+ if (keyEsc->isPressed || keyEsc->pressCount > 0) {
+ pkeSettings.editorSettings.isUsingDebugCamera = false;
+ shouldLockCamera = true;
+ }
+ }
+
+ if (shouldLockCamera) {
+ shouldLockCamera = false;
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+ cameraDefault.stale = PKE_CAMERA_STALE_ALL;
+ ActiveCamera = &cameraDefault;
+ }
+
+ if (pkeSettings.editorSettings.isUsingDebugCamera) {
+ holder = PkeInput_Query(dbgCtrl_CameraRot);
+ if (holder.type != InputEventHash{0}) {
+ const PkeCursorPosEvent *posEvent = static_cast<PkeCursorPosEvent *>(holder.ptr);
+ if (posEvent->xMotion || posEvent->yMotion) {
+ glm::vec3 axis1Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(1.f, 0.f, 0.f);
+ glm::vec3 axis2Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(0.f, 1.f, 0.f);
+ // glm::vec3 axis1Heading = cameraDbg.rot * glm::vec3(1.f, 0.f, 0.f);
+ // glm::vec3 axis2Heading = cameraDbg.rot * glm::vec3(0.f, 1.f, 0.f);
+ glm::quat pitch = glm::angleAxis(float(posEvent->yMotion) * 0.01f, glm::normalize(axis1Heading));
+ glm::quat yaw = glm::angleAxis(float(posEvent->xMotion) * 0.01f, glm::normalize(axis2Heading));
+ glm::quat rot = cameraDbg.rot * pitch * yaw;
+ // rot = glm::normalize(rot);
+ auto eul = glm::eulerAngles(rot);
+ cameraDbg.rot = glm::quat(eul);
+ cameraDbg.stale = cameraDbg.stale | PKE_CAMERA_STALE_ROT;
+ }
+ }
+
+ double leftCount = 0;
+ double rightCount = 0;
+ double forwardCount = 0;
+ double backCount = 0;
+ double upCount = 0;
+ double downCount = 0;
+ double rotCCCount = 0;
+ double rotCCount = 0;
+
+ holder = PkeInput_Query(dbgCtrl_CameraLeft);
+ if (holder.type != InputEventHash{0}) {
+ PkeKeyEvent *keyLeft;
+ keyLeft = static_cast<PkeKeyEvent *>(holder.ptr);
+ leftCount = keyLeft->isPressed ? 1 : 0;
+ }
+
+ holder = PkeInput_Query(dbgCtrl_CameraRight);
+ if (holder.type != InputEventHash{0}) {
+ PkeKeyEvent *keyRight;
+ keyRight = static_cast<PkeKeyEvent *>(holder.ptr);
+ rightCount = keyRight->isPressed ? 1 : 0;
+ }
+
+ holder = PkeInput_Query(dbgCtrl_CameraForward);
+ if (holder.type != InputEventHash{0}) {
+ PkeKeyEvent *keyForward;
+ keyForward = static_cast<PkeKeyEvent *>(holder.ptr);
+ forwardCount = keyForward->isPressed ? 1 : 0;
+ }
+
+ holder = PkeInput_Query(dbgCtrl_CameraBack);
+ if (holder.type != InputEventHash{0}) {
+ PkeKeyEvent *keyBack;
+ keyBack = static_cast<PkeKeyEvent *>(holder.ptr);
+ backCount = keyBack->isPressed ? 1 : 0;
+ }
+
+ holder = PkeInput_Query(dbgCtrl_CameraUp);
+ if (holder.type != InputEventHash{0}) {
+ PkeKeyEvent *keyUp;
+ keyUp = static_cast<PkeKeyEvent *>(holder.ptr);
+ upCount = keyUp->isPressed ? 1 : 0;
+ }
+
+ holder = PkeInput_Query(dbgCtrl_CameraDown);
+ if (holder.type != InputEventHash{0}) {
+ PkeKeyEvent *keyDown;
+ keyDown = static_cast<PkeKeyEvent *>(holder.ptr);
+ downCount = keyDown->isPressed ? 1 : 0;
+ }
+
+ holder = PkeInput_Query(dbgCtrl_CameraRotCC);
+ if (holder.type != InputEventHash{0}) {
+ PkeKeyEvent *keyRotCC;
+ keyRotCC = static_cast<PkeKeyEvent *>(holder.ptr);
+ rotCCCount = keyRotCC->isPressed ? 1 : 0;
+ }
+
+ holder = PkeInput_Query(dbgCtrl_CameraRotC);
+ if (holder.type != InputEventHash{0}) {
+ PkeKeyEvent *keyRotC;
+ keyRotC = static_cast<PkeKeyEvent *>(holder.ptr);
+ rotCCount = keyRotC->isPressed ? 1 : 0;
+ }
+
+ double accelerated = delta * 10.f;
+ double axis1 = -(leftCount * accelerated) + (rightCount * accelerated);
+ double axis2 = (forwardCount * accelerated) + -(backCount * accelerated);
+ double axis3 = -(upCount * accelerated) + (downCount * accelerated);
+ if (axis1 != 0 || axis2 != 0 || axis3 != 0) {
+ glm::vec3 axis1Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(-axis1, 0.f, 0.f);
+ glm::vec3 axis2Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(0.f, 0.f, axis2);
+ glm::vec3 axis3Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(0.f, axis3, 0.f);
+ cameraDbg.pos += glm::vec3(axis1Heading + axis2Heading + axis3Heading);
+ cameraDbg.stale = cameraDbg.stale | PKE_CAMERA_STALE_POS;
+ }
+
+ double axis4 = -(rotCCCount * delta) + (rotCCount * delta);
+ if (axis4 != 0.0) {
+ cameraDbg.rot = glm::quat(glm::vec3(0.f, 0.f, axis4)) * cameraDbg.rot;
+ cameraDbg.stale = cameraDbg.stale | PKE_CAMERA_STALE_ROT;
+ }
+ }
+
+}
+
+void RecordImGui_GLM(const char *label, glm::mat4 &mat) {
+ float min = -1;
+ float max = 1;
+ for (long i = 0; i < 4; ++i) {
+ ImGui::PushID(i);
+ ImGui::SliderFloat4(label, &mat[i][0], min, max, "%.3f", ImGuiSliderFlags_NoRoundToFormat);
+ ImGui::PopID();
+ }
+}
+
+void RecordImGuiEditorWrapper() {
+ ImGui::DockSpaceOverViewport(nullptr, ImGuiDockNodeFlags_PassthruCentralNode);
+ ImGui::BeginMainMenuBar();
+ if (ImGui::BeginMenu("File")) {
+ if (ImGui::MenuItem("Exit")) {
+ glfwSetWindowShouldClose(window, true);
+ }
+ ImGui::EndMenu();
+ }
+ if (ImGui::BeginMenu("Debug")) {
+ ImGui::Checkbox("Show Debug Hitboxes", &pkeSettings.isRenderingDebug);
+ ImGui::Checkbox("Pause Physics Simulation", &pkeSettings.isSimulationPaused);
+ if (ImGui::Checkbox("Uncap Framerate", &pkeSettings.graphicsSettings.isFramerateUnlocked)) {
+ shouldRecreateSwapchain = true;
+ }
+ if (ImGui::Checkbox("wait for v-sync", &pkeSettings.graphicsSettings.isWaitingForVsync)) {
+ shouldRecreateSwapchain = true;
+ }
+ // ImGui::Checkbox("Uncap Tickrate", &pkeSettings.isTickrateUnlocked);
+ if (ImGui::Checkbox("Use Debug Camera", &pkeSettings.editorSettings.isUsingDebugCamera)) {
+ if (pkeSettings.editorSettings.isUsingDebugCamera) {
+ shouldUnlockCamera = true;
+ } else {
+ shouldLockCamera = true;
+ }
+ }
+
+ ImGuiIO &io = ImGui::GetIO();
+ ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
+
+ ImGui::EndMenu();
+ }
+ ImGui::EndMainMenuBar();
+}
+
+void RecordImGuiEntityList() {
+ if (!ImGui::Begin("EntityList", &pkeSettings.editorSettings.isShowingEntityList)) {
+ ImGui::End();
+ return;
+ }
+ static ImGuiTableFlags tableFlags{
+ ImGuiTableFlags_Borders |
+ ImGuiTableFlags_RowBg
+ };
+ if (ImGui::BeginTable("Entities", 7, tableFlags)) {
+ ImGui::TableSetupColumn("Select");
+ ImGui::TableSetupColumn("EntityHandle");
+ ImGui::TableSetupColumn("ParentEntityHandle");
+ ImGui::TableSetupColumn("GrBindsHandle");
+ ImGui::TableSetupColumn("InstanceHandle");
+ ImGui::TableSetupColumn("IsMarkedForRemoval");
+ ImGui::TableSetupColumn("Delete");
+ ImGui::TableHeadersRow();
+
+ uint64_t bucketCount = ECS_GetEntities_BucketCount();
+ for (long bucket = 0; bucket < bucketCount; ++bucket) {
+ uint64_t itemCount = 0;
+ auto *entities = ECS_GetEntities(bucket, itemCount);
+ ImGui::PushID(bucket);
+ for (long row = 0; row < itemCount; row++) {
+ auto *entity = &entities[row];
+ ImGui::PushID(row);
+ ImGui::TableNextRow();
+ ImGui::TableSetColumnIndex(0);
+ ImGui::BeginDisabled(selectedEntity == entity->handle);
+ if (ImGui::Button("Select"))
+ selectedEntity = entity->handle;
+ ImGui::EndDisabled();
+ ImGui::TableSetColumnIndex(1);
+ ImGui::Text("0x%016lX", static_cast<EntityHandle_T>(entity->handle));
+ ImGui::TableSetColumnIndex(2);
+ ImGui::Text("0x%016lX", static_cast<EntityHandle_T>(entity->parentHandle));
+ ImGui::TableSetColumnIndex(3);
+ ImGui::Text("0x%016lX", static_cast<GrBindsHandle_T>(entity->grBindsHandle));
+ ImGui::TableSetColumnIndex(4);
+ ImGui::Text("0x%016lX", static_cast<InstanceHandle_T>(entity->instanceHandle));
+ ImGui::TableSetColumnIndex(5);
+ ImGui::Text("%u", entity->isMarkedForRemoval);
+ ImGui::TableSetColumnIndex(6);
+ ImGui::BeginDisabled(selectedEntity != entity->handle);
+ if (ImGui::Button("Delete"))
+ ECS_MarkForRemoval(entity->handle);
+ ImGui::EndDisabled();
+ ImGui::PopID();
+ }
+ ImGui::PopID();
+ }
+ ImGui::EndTable();
+ }
+ ImGui::End();
+}
+
+void RecordImGuiUBO() {
+ if (!ImGui::Begin("UBO", &pkeSettings.editorSettings.isShowingUBO)) {
+ ImGui::End();
+ return;
+ }
+ ImGui::Text("Model");
+ RecordImGui_GLM("UBO-model", UBO.model);
+ ImGui::Text("View");
+ RecordImGui_GLM("UBO-view", UBO.view);
+ ImGui::Text("Proj");
+ RecordImGui_GLM("UBO-proj", UBO.proj);
+ ImGui::End();
+}
+
+void RecordImGuiModalCreateEntityType() {
+ if (ImGui::BeginPopupModal("CreateEntityType", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
+ static char modelsDir[64];
+ static char modelFile[64];
+ static char entityTypeCode[32];
+
+ ImGui::InputText("Models Dir", modelsDir, 63);
+ ImGui::InputText("Model File", modelFile, 63);
+ ImGui::InputText("Entity Type Code", entityTypeCode, 31);
+ ImGui::InputScalar("Starting Instance Count", ImGuiDataType_U32, &entityTypeToCreate.startingInstanceCount);
+ ImGui::InputScalar("GLTF Import Index - Vertex", ImGuiDataType_S16, &entityTypeToCreate.Importer_GLTF.AccessorIndexVertex);
+ ImGui::InputScalar("GLTF Import Index - Normal", ImGuiDataType_S16, &entityTypeToCreate.Importer_GLTF.AccessorIndexNormal);
+ ImGui::InputScalar("GLTF Import Index - UV", ImGuiDataType_S16, &entityTypeToCreate.Importer_GLTF.AccessorIndexUV);
+ ImGui::InputScalar("GLTF Import Index - Index", ImGuiDataType_S16, &entityTypeToCreate.Importer_GLTF.AccessorIndexIndex);
+ ImGui::InputFloat("Physics - Mass", &entityTypeToCreate.bt.startingMass);
+ ImGui::InputScalar("Physics - Collision Layer", ImGuiDataType_U16, &entityTypeToCreate.bt.startingCollisionLayer);
+ ImGui::InputScalar("Physics - Collision Mask", ImGuiDataType_U16, &entityTypeToCreate.bt.startingCollisionMask);
+
+ ImGui::Separator();
+
+ if (ImGui::Button("Create")) {
+ // TODO some type of validation
+ memset(modelsDir, '\0', 64);
+ memset(modelFile, '\0', 64);
+ memset(entityTypeCode, '\0', 32);
+ // assert(entityTypeToCreate == nullptr || entityTypeToCreate == CAFE_BABE(EntityType));
+ // entityTypeToCreate = Pke_New<EntityType>();
+
+ char *sModelsDir = Pke_New<char>(strlen(modelsDir) + 1);
+ strncpy(sModelsDir, modelsDir, 63);
+ entityTypeToCreate.modelsDir = sModelsDir;
+
+ char *sModelFile = Pke_New<char>(strlen(modelFile) + 1);
+ strncpy(sModelFile, modelFile, 63);
+ entityTypeToCreate.modelFile = sModelFile;
+
+ char *sEntityTypeCode = Pke_New<char>(strlen(entityTypeCode) + 1);
+ strncpy(sEntityTypeCode, entityTypeCode, 31);
+ entityTypeToCreate.entityTypeCode = sEntityTypeCode;
+
+ shouldCreateEntityType = true;
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Cancel")) {
+ ImGui::CloseCurrentPopup();
+ }
+
+ ImGui::EndPopup();
+ }
+}
+
+void RecordImGui_CompGrBinds(bool readonly, CompGrBinds *component) {
+ if (component == nullptr) return;
+ int inputTextFlags = 0;
+ if (readonly)
+ inputTextFlags |= ImGuiInputTextFlags_ReadOnly;
+
+ ImGui::Text("CompGRBinds");
+ ImGui::Separator();
+
+ if (ImGui::Button("Create Instance")) {
+ entityInstancesToCreate.Push({
+ .entityTypeEntityHandle = component->entHandle,
+ });
+ }
+
+ if (component->vkPipelineLayout)
+ ImGui::InputScalar("VkPipelineLayout", ImGuiDataType_U64, &component->vkPipelineLayout, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly);
+ if (component->vkDescriptorSets){
+ ImGui::InputScalarN("VkPipelineDescriptorSets", ImGuiDataType_U64, &component->vkDescriptorSets, MAX_FRAMES_IN_FLIGHT, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly);
+ }
+
+ if (component->vertexBuffer)
+ ImGui::InputScalar("VkVertexBuffer", ImGuiDataType_U64, &component->vertexBuffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly);
+ ImGui::InputScalar("VertexFirstBinding", ImGuiDataType_U32, &component->vertexFirstBinding, nullptr, nullptr, nullptr, inputTextFlags);
+ ImGui::InputScalar("VertexBindingCount", ImGuiDataType_U32, &component->vertexBindingCount, nullptr, nullptr, nullptr, inputTextFlags);
+ ImGui::InputScalar("VertexOffsets", ImGuiDataType_U64, &component->vertexOffsets, nullptr, nullptr, nullptr, inputTextFlags);
+
+ if (component->normalsBuffer)
+ ImGui::InputScalar("VkNormalBuffer", ImGuiDataType_U64, &component->normalsBuffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly);
+ ImGui::InputScalar("NormalFirstBinding", ImGuiDataType_U32, &component->normalsFirstBinding, nullptr, nullptr, nullptr, inputTextFlags);
+ ImGui::InputScalar("NormalBindingCount", ImGuiDataType_U32, &component->normalsBindingCount, nullptr, nullptr, nullptr, inputTextFlags);
+ ImGui::InputScalar("NormalOffsets", ImGuiDataType_U64, &component->normalsOffsets, nullptr, nullptr, nullptr, inputTextFlags);
+
+ if (component->uvBuffer)
+ ImGui::InputScalar("VkUVBuffer", ImGuiDataType_U64, &component->uvBuffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly);
+ ImGui::InputScalar("UVFirstBinding", ImGuiDataType_U32, &component->uvFirstBinding, nullptr, nullptr, nullptr, inputTextFlags);
+ ImGui::InputScalar("UVBindingCount", ImGuiDataType_U32, &component->uvBindingCount, nullptr, nullptr, nullptr, inputTextFlags);
+ ImGui::InputScalar("UVOffsets", ImGuiDataType_U64, &component->uvOffsets, nullptr, nullptr, nullptr, inputTextFlags);
+
+ if (component->indexBuffer)
+ ImGui::InputScalar("VkIndexBuffer", ImGuiDataType_U64, &component->indexBuffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly);
+ ImGui::InputScalar("IndexBindingCount", ImGuiDataType_U32, &component->indexBindingCount, nullptr, nullptr, nullptr, inputTextFlags);
+ ImGui::InputScalar("IndexOffsets", ImGuiDataType_U64, &component->indexOffsets, nullptr, nullptr, nullptr, inputTextFlags);
+ ImGui::InputScalar("IndexCount", ImGuiDataType_U32, &component->indexCount, nullptr, nullptr, nullptr, inputTextFlags);
+
+ if (component->instanceBuffer)
+ ImGui::InputScalar("VkInstanceBuffer", ImGuiDataType_U64, &component->instanceBuffer, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly);
+ ImGui::InputScalar("InstanceFirstBinding", ImGuiDataType_U32, &component->instanceFirstBinding, nullptr, nullptr, nullptr, inputTextFlags);
+ ImGui::InputScalar("InstanceBindingCount", ImGuiDataType_U32, &component->instanceBindingCount, nullptr, nullptr, nullptr, inputTextFlags);
+ ImGui::InputScalar("InstanceOffsets", ImGuiDataType_U64, &component->instanceOffsets, nullptr, nullptr, nullptr, inputTextFlags);
+
+ ImGui::InputScalar("InstanceBufferMaxCount", ImGuiDataType_U32, &component->instanceBufferMaxCount, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly);
+ ImGui::InputScalar("Instance Count", ImGuiDataType_U32, &component->instanceCounter, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly);
+
+ ImGui::Spacing();
+}
+
+void RecordImGui_CompInstPos(bool readonly, CompInstance *component) {
+ if (component == nullptr) return;
+ int inputTextFlags = 0;
+ if (readonly)
+ inputTextFlags |= ImGuiInputTextFlags_ReadOnly;
+
+ ImGui::Text("CompInststance + InstPos");
+ ImGui::Separator();
+
+ bool changed = false;
+ InstPos instPos;
+ component->bt.motionState->getWorldTransform(instPos.posRot);
+ instPos.scale = component->bt.rigidBody->getCollisionShape()->getLocalScaling();
+ instPos.mass = component->bt.rigidBody->getMass();
+
+ btVector3 pos = instPos.posRot.getOrigin();
+ btQuaternion rot = instPos.posRot.getRotation();
+ glm::vec3 eul;
+ rot.getEulerZYX(eul.z, eul.y, eul.x);
+ eul = glm::degrees(eul);
+
+ changed = ImGui::InputScalar("Instance Index", ImGuiDataType_U64, &component->index, nullptr, nullptr, nullptr, ImGuiInputTextFlags_ReadOnly) || changed;
+ changed = ImGui::InputScalarN("pos", ImGuiDataType_Float, &pos, 3, nullptr, nullptr, nullptr, inputTextFlags) || changed;
+ changed = ImGui::InputScalarN("rot (eul)", ImGuiDataType_Float, &eul, 3, nullptr, nullptr, nullptr, inputTextFlags) || changed;
+ changed = ImGui::InputScalarN("scale", ImGuiDataType_Float, &instPos.scale, 3, nullptr, nullptr, nullptr, inputTextFlags) || changed;
+ changed = ImGui::InputFloat("mass", &instPos.mass, 0.0, 0.0, "%.3f", inputTextFlags) || changed;
+
+ changed = ImGui::InputScalar("Phys - Collision Layer", ImGuiDataType_U16, &component->physicsLayer, nullptr, nullptr, nullptr, inputTextFlags) || changed;
+ changed = ImGui::InputScalar("Phys - Collision Mask", ImGuiDataType_U16, &component->physicsMask, nullptr, nullptr, nullptr, inputTextFlags) || changed;
+
+ ImGui::InputScalar("Phys - Rigid Body", ImGuiDataType_U64, &component->bt.rigidBody, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly);
+ ImGui::InputScalar("Phys - Motion State", ImGuiDataType_U64, &component->bt.motionState, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly);
+
+ if (changed) {
+ instPos.posRot.setOrigin(pos);
+ eul = glm::radians(eul);
+ rot.setEulerZYX(eul.z, eul.y, eul.x);
+ instPos.posRot.setRotation(rot);
+ auto *broadphase = component->bt.rigidBody->getBroadphaseProxy();
+ broadphase->m_collisionFilterGroup = static_cast<PhysicsCollision_T>(component->physicsLayer);
+ broadphase->m_collisionFilterMask = static_cast<PhysicsCollision_T>(component->physicsMask);
+ ECS_UpdateInstance(component->entHandle, instPos, true);
+ }
+
+ ImGui::Spacing();
+}
+
+void RecordImGuiSceneEditor() {
+ {
+ if (!ImGui::Begin("SceneEditorEntityTypes", &pkeSettings.editorSettings.isShowingSceneEditor)) {
+ ImGui::End();
+ return;
+ }
+ if (ImGui::Button("Create Entity Type")) {
+ ImGui::OpenPopup("CreateEntityType");
+ }
+ if (ImGui::Button("Save")) {
+ // Game_SaveSceneFile(sceneName);
+ shouldOpenSaveSceneDialog = true;
+ }
+ if (ImGui::Button("Load")) {
+ // Game_LoadSceneFile(sceneName);
+ shouldOpenLoadSceneDialog = true;
+ }
+ if (ImGui::Button("Clear Selection")) {
+ selectedEntity = EntityHandle_MAX;
+ }
+
+ ImGui::Spacing();
+
+ if (selectedEntity != EntityHandle_MAX) {
+ RecordImGui_CompGrBinds(true, ECS_GetGrBinds(selectedEntity));
+ RecordImGui_CompInstPos(false, ECS_GetInstance(selectedEntity));
+ }
+
+ RecordImGuiModalCreateEntityType();
+
+ ImGui::End();
+ }
+}
+
+void RecordImGuiEditor() {
+ if (pkeSettings.isShowingEditor) {
+ RecordImGuiEditorWrapper();
+ RecordImGuiEntityList();
+ RecordImGuiSceneEditor();
+ RecordImGuiUBO();
+ }
+}
+
+void PkeEditor_Setup() {
+ shouldSetupEditor = false;
+ PkeInput_ActivateSet(debugControlsHandle);
+ Event_RegisterCallback("RenderImGui", RecordImGuiEditor);
+}
+
+void PkeEditor_Disable() {
+ shouldDisableEditor = false;
+ PkeInput_DeactivateSet(debugControlsHandle);
+ Event_UnregisterCallback("RenderImGui", RecordImGuiEditor);
+}
+
+void PkeEditor_Teardown() {
+ PkeEditor_Disable();
+ PkeThreads_Teardown(threadPoolHandle);
+ entityInstancesToCreate.~DynArray();
+}
+
+void PkeEditor_Init() {
+ PkeInputSet debugControlsSet;
+ debugControlsSet.title = "debug-editor-controls";
+ debugControlsSet.actionCount = 12;
+ debugControlsSet.actions = Pke_New<PkeInputAction>(debugControlsSet.actionCount);
+
+ debugControlsSet.actions[0].name = dbgCtrl_CameraLeft;
+ debugControlsSet.actions[0].primaryHash = PkeInputEventMask {
+ .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS,
+ .button = GLFW_KEY_A,
+ };
+ debugControlsSet.actions[1].name = dbgCtrl_CameraRight;
+ debugControlsSet.actions[1].primaryHash = PkeInputEventMask {
+ .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS,
+ .button = GLFW_KEY_D,
+ };
+ debugControlsSet.actions[2].name = dbgCtrl_CameraForward;
+ debugControlsSet.actions[2].primaryHash = PkeInputEventMask {
+ .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS,
+ .button = GLFW_KEY_W,
+ };
+ debugControlsSet.actions[3].name = dbgCtrl_CameraBack;
+ debugControlsSet.actions[3].primaryHash = PkeInputEventMask {
+ .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS,
+ .button = GLFW_KEY_S,
+ };
+ debugControlsSet.actions[4].name = dbgCtrl_CameraUp;
+ debugControlsSet.actions[4].primaryHash = PkeInputEventMask {
+ .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS,
+ .button = GLFW_KEY_R,
+ };
+ debugControlsSet.actions[5].name = dbgCtrl_CameraDown;
+ debugControlsSet.actions[5].primaryHash = PkeInputEventMask {
+ .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS,
+ .button = GLFW_KEY_F,
+ };
+ debugControlsSet.actions[6].name = dbgCtrl_CameraRotCC;
+ debugControlsSet.actions[6].primaryHash = PkeInputEventMask {
+ .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS,
+ .button = GLFW_KEY_Q,
+ };
+ debugControlsSet.actions[7].name = dbgCtrl_CameraRotC;
+ debugControlsSet.actions[7].primaryHash = PkeInputEventMask {
+ .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS,
+ .button = GLFW_KEY_E,
+ };
+ debugControlsSet.actions[8].name = dbgCtrl_CameraRot;
+ debugControlsSet.actions[8].primaryHash = PkeInputEventMask {
+ .computedHash = PKE_INPUT_HASH_ALL_CURSOR_POS_EVENTS,
+ };
+ debugControlsSet.actions[9].name = dbgCtrl_UnlockCamera;
+ debugControlsSet.actions[9].primaryHash = PkeInputEventMask {
+ .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS,
+ .button = GLFW_KEY_ESCAPE,
+ };
+ debugControlsSet.actions[10].name = dbgCtrl_SelectHovered;
+ debugControlsSet.actions[10].primaryHash = PkeInputEventMask {
+ .computedHash = PKE_INPUT_HASH_ALL_MOUSE_BUTTON_EVENTS,
+ .button = GLFW_MOUSE_BUTTON_LEFT,
+ };
+ debugControlsSet.actions[11].name = dbgCtrl_ClearSelection;
+ debugControlsSet.actions[11].primaryHash = PkeInputEventMask {
+ .computedHash = PKE_INPUT_HASH_ALL_MOUSE_BUTTON_EVENTS,
+ .button = GLFW_MOUSE_BUTTON_RIGHT,
+ };
+
+ debugControlsHandle = PkeInput_RegisterSet(debugControlsSet);
+
+ ActiveCamera = &cameraDefault;
+
+ threadPoolHandle = PkeThreads_Init(1, 1);
+
+ Event_RegisterCallback<TickEvent>("GAME_TICK", PkeEditor_Tick);
+}
diff --git a/editor/editor.hpp b/editor/editor.hpp
new file mode 100644
index 0000000..3cfb2df
--- /dev/null
+++ b/editor/editor.hpp
@@ -0,0 +1,10 @@
+#ifndef PKE_EDITOR_HPP
+#define PKE_EDITOR_HPP
+
+void PkeEditor_Tick(double delta);
+void PkeEditor_Init();
+void PkeEditor_Setup();
+void PkeEditor_Disable();
+void PkeEditor_Teardown();
+
+#endif /* PKE_EDITOR_HPP */
diff --git a/editor/main.cpp b/editor/main.cpp
index 0d4ef9f..7400583 100644
--- a/editor/main.cpp
+++ b/editor/main.cpp
@@ -1,5 +1,7 @@
#include <csignal>
+#include "editor.hpp"
+#include "event.hpp"
#include "game.hpp"
#include "game-settings.hpp"
#include "window-types.hpp"
@@ -13,8 +15,16 @@ PKEWindowProperties windowProps{};
int main() {
signal(SIGTERM, signal_handler);
- fprintf(stdout, "PKE ENTERING\n");
+ fprintf(stdout, "PKE_EDITOR ENTERING\n");
+ // setup
+ {
+ pkeSettings.isShowingEditor = true;
+ Event_RegisterCallback<EventHandler>("GAME_INIT", PkeEditor_Init);
+
+ Event_RegisterCallback<EventHandler>("GAME_TEARDOWN", PkeEditor_Teardown);
+ }
+ // run
Game_Main(&windowProps);
- fprintf(stdout, "PKE EXITING\n");
+ fprintf(stdout, "PKE_EDITOR EXITING\n");
return 0;
}