#include "game.hpp" #include GameSettings pkeSettings{}; const uint64_t consoleBufferCount = 30; const uint64_t consoleLineLength = 128; const long readLineLength = 128; char readLine[readLineLength]; const char *levelName = "demo-level"; const char *PKE_FILE_BEGIN = ":PKFB:"; const char *PKE_FILE_END = ":PKFE:"; const char *PKE_FILE_VERSION = ":0:"; const char *PKE_FILE_OBJ_END = ""; const char *PKE_FILE_OBJ_ENTITY_TYPE = "EntityType:"; const char *PKE_FILE_ENTITY_TYPE_MODELS_DIR = "ModelsDir: "; const char *PKE_FILE_ENTITY_TYPE_MODEL_FILE = "ModelFile: "; const char *PKE_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE = "EntityTypeCode: "; const char *PKE_FILE_ENTITY_TYPE_ENTITY_HANDLE = "EntityHandle: "; const char *PKE_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT = "StartingInstanceCount: "; const char *PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_VERTEX = "Importer_GLTF::AccessorIndexVertex: "; const char *PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_NORMAL = "Importer_GLTF::AccessorIndexNormal: "; const char *PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_UV = "Importer_GLTF::AccessorIndexUV: "; const char *PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_INDEX = "Importer_GLTF::AccessorIndexIndex: "; char consoleBuffer[consoleBufferCount][consoleLineLength]; long consoleBufferIndex = 0; EntityHandle selectedEntity = EntityHandle_MAX; bool shouldCreateEntityType = false; EntityType *entityTypeToCreate = nullptr; void SerializeEntityType(std::ofstream &stream, const EntityType &et) { char handleStr[19] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; snprintf(handleStr, 19, "0x%016lX",static_cast(et.entityHandle)); EntityType e{}; if (et.modelsDir != e.modelsDir) stream << PKE_FILE_ENTITY_TYPE_MODELS_DIR << et.modelsDir << "\n"; if (et.modelFile != e.modelFile) stream << PKE_FILE_ENTITY_TYPE_MODEL_FILE << et.modelFile << "\n"; if (et.entityTypeCode != e.entityTypeCode) stream << PKE_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE << et.entityTypeCode << "\n"; if (et.entityHandle != e.entityHandle) stream << PKE_FILE_ENTITY_TYPE_ENTITY_HANDLE << handleStr << "\n"; if (et.startingInstanceCount != e.startingInstanceCount) stream << PKE_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT << et.startingInstanceCount << "\n"; if (et.Importer_GLTF.AccessorIndexVertex != e.Importer_GLTF.AccessorIndexVertex) stream << PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_VERTEX << et.Importer_GLTF.AccessorIndexVertex << "\n"; if (et.Importer_GLTF.AccessorIndexNormal != e.Importer_GLTF.AccessorIndexNormal) stream << PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_NORMAL << et.Importer_GLTF.AccessorIndexNormal << "\n"; if (et.Importer_GLTF.AccessorIndexUV != e.Importer_GLTF.AccessorIndexUV) stream << PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_UV << et.Importer_GLTF.AccessorIndexUV << "\n"; if (et.Importer_GLTF.AccessorIndexIndex != e.Importer_GLTF.AccessorIndexIndex) stream << PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_INDEX << et.Importer_GLTF.AccessorIndexIndex << "\n"; } void ParseEntityType(std::ifstream &stream) { EntityType et{}; while (stream.getline(readLine, readLineLength)) { if (strcmp(PKE_FILE_OBJ_END, readLine) == 0) { int64_t existingEntityTypeIndex = EntityType_FindByTypeCode(et.entityTypeCode); if (existingEntityTypeIndex != -1) { continue; } et.entityHandle = ECS_CreateEntity(); EntityType_Load(et); GlobalEntityTypes.Push(et); return; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_MODELS_DIR)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_MODELS_DIR); uint64_t len = strlen(readLine + prefixLen) + 1; char *val = Pke_New(len); memset(reinterpret_cast(val), '\0', len); memcpy(val, readLine + prefixLen, len); et.modelsDir = val; continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_MODEL_FILE)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_MODEL_FILE); uint64_t len = strlen(readLine + prefixLen) + 1; char *val = Pke_New(len); memset(reinterpret_cast(val), '\0', len); memcpy(val, readLine + prefixLen, len); et.modelFile = val; continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_ENTITY_TYPE_CODE); uint64_t len = strlen(readLine + prefixLen) + 1; char *val = Pke_New(len); memset(reinterpret_cast(val), '\0', len); memcpy(val, readLine + prefixLen, len); et.entityTypeCode = val; continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_ENTITY_HANDLE)) { // TODO parse from hex ? uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_ENTITY_HANDLE); EntityHandle_T handle_t; STR2NUM_ERROR result = str2num(handle_t, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); et.entityHandle = EntityHandle{handle_t}; continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_STARTING_INSTANCE_COUNT); STR2NUM_ERROR result = str2num(et.startingInstanceCount, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_VERTEX)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_VERTEX); STR2NUM_ERROR result = str2num(et.Importer_GLTF.AccessorIndexVertex, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_NORMAL)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_NORMAL); STR2NUM_ERROR result = str2num(et.Importer_GLTF.AccessorIndexNormal, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_UV)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_UV); STR2NUM_ERROR result = str2num(et.Importer_GLTF.AccessorIndexUV, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } if (strstr(readLine, PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_INDEX)) { uint64_t prefixLen = strlen(PKE_FILE_ENTITY_TYPE_IMPORTER_GLTF_ACCESSOR_INDEX_INDEX); STR2NUM_ERROR result = str2num(et.Importer_GLTF.AccessorIndexIndex, readLine + prefixLen); assert(result == STR2NUM_ERROR::SUCCESS); continue; } } } void SaveSceneFile(const char *sceneFilePath) { std::ofstream f(sceneFilePath); assert(f.is_open()); f << PKE_FILE_BEGIN << "\n"; f << PKE_FILE_VERSION << "\n"; f << "" << "\n"; for (long i = 0; i < GlobalEntityTypes.Count(); ++i) { f << PKE_FILE_OBJ_ENTITY_TYPE << "\n"; const auto &et = GlobalEntityTypes[i]; const CompGrBinds *grBinds = ECS_GetGrBinds(et.entityHandle); // TODO ignore if no instances if (grBinds == nullptr) { continue; } SerializeEntityType(f, et); f << PKE_FILE_OBJ_END << "\n"; } f << PKE_FILE_END << "\n"; f.flush(); f.close(); } void LoadSceneFile(const char *sceneFilePath) { std::ifstream f(sceneFilePath); assert(f.is_open()); memset(readLine, '\0', readLineLength); while (f.getline(readLine, readLineLength)) { // EntityTypes if (strcmp(PKE_FILE_OBJ_ENTITY_TYPE, readLine) == 0) { ParseEntityType(f); continue; } } f.close(); } void Game_Tick(double delta) { /* * ECS_Tick() gets called first because it updates the public * `EntitiesToBeRemoved` for all other ticks to use. */ ECS_Tick(delta); if (shouldCreateEntityType) { assert(entityTypeToCreate != nullptr); assert(entityTypeToCreate != CAFE_BABE(EntityType)); assert(entityTypeToCreate->entityHandle == EntityHandle_MAX); // TODO this needs to be more elegant int64_t existingEntityTypeIndex = EntityType_FindByTypeCode(entityTypeToCreate->entityTypeCode); if (existingEntityTypeIndex == -1) { entityTypeToCreate->entityHandle = ECS_CreateEntity(); EntityType_Load(*entityTypeToCreate); GlobalEntityTypes.Push(*entityTypeToCreate); } Pke_Delete(entityTypeToCreate); shouldCreateEntityType = false; } } void RecordImGuiEditorWrapper() { ImGui::DockSpaceOverViewport(nullptr, ImGuiDockNodeFlags_PassthruCentralNode); ImGui::BeginMainMenuBar(); if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Exit")) { glfwSetWindowShouldClose(window, true); } ImGui::EndMenu(); } ImGui::EndMainMenuBar(); } void RecordImGuiConsole() { static bool scrollToBottom = true; if (!ImGui::Begin("Console", &pkeSettings.editorSettings.isShowingConsole)) { ImGui::End(); return; } ImVec2 region = ImGui::GetContentRegionAvail(); region.y -= 27; if (ImGui::BeginListBox("##ConsoleHistory", region)) { for (long i = consoleBufferIndex + 1; i < consoleBufferCount; ++i) { ImGui::Text("%s", consoleBuffer[i]); } for (long i = 0; i < consoleBufferIndex; ++i) { ImGui::Text("%s", consoleBuffer[i]); } if (scrollToBottom) ImGui::SetScrollHereY(1); scrollToBottom = false; ImGui::EndListBox(); } ImGui::Separator(); if (ImGui::InputText("##ConsoleInput", consoleBuffer[consoleBufferIndex], consoleLineLength, ImGuiInputTextFlags_EnterReturnsTrue)) { // TODO parse and execute. scrollToBottom = true; consoleBufferIndex = (consoleBufferIndex + 1) % consoleBufferCount; memset(consoleBuffer[consoleBufferIndex], '\0', consoleLineLength); } auto focusedFlags = (ImGuiFocusedFlags_ChildWindows); if (ImGui::IsWindowFocused(focusedFlags) && !ImGui::IsAnyItemFocused() && !ImGui::IsAnyItemHovered() && !ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !ImGui::IsMouseClicked(ImGuiMouseButton_Left, true)) { ImGui::SetKeyboardFocusHere(-1); } ImGui::End(); } void RecordImGuiEntityList() { if (!ImGui::Begin("EntityList", &pkeSettings.editorSettings.isShowingEntityList)) { ImGui::End(); return; } static ImGuiTableFlags tableFlags{ ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg }; if (ImGui::BeginTable("Entities", 6, tableFlags)) { ImGui::TableSetupColumn("Select"); ImGui::TableSetupColumn("EntityHandle"); ImGui::TableSetupColumn("ParentEntityHandle"); ImGui::TableSetupColumn("GrBindsHandle"); ImGui::TableSetupColumn("InstanceHandle"); ImGui::TableSetupColumn("IsMarkedForRemoval"); 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); for (long row = 0; row < itemCount; row++) { auto *entity = &entities[row]; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); if (ImGui::Button("Select")) selectedEntity = entity->handle; ImGui::TableSetColumnIndex(1); ImGui::Text("0x%016lX", static_cast(entity->handle)); ImGui::TableSetColumnIndex(2); ImGui::Text("0x%016lX", static_cast(entity->parentHandle)); ImGui::TableSetColumnIndex(3); ImGui::Text("0x%016lX", static_cast(entity->grBindsHandle)); ImGui::TableSetColumnIndex(4); ImGui::Text("0x%016lX", static_cast(entity->instanceHandle)); ImGui::TableSetColumnIndex(5); ImGui::Text("%u", entity->isMarkedForRemoval); } } ImGui::EndTable(); } ImGui::End(); } void RecordImGuiModalCreateEntityType() { if (ImGui::BeginPopupModal("CreateEntityType", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { static bool needsReset = true; static char modelsDir[64]; static char modelFile[64]; static char entityTypeCode[32]; if (needsReset) { memset(modelsDir, '\0', 64); memset(modelFile, '\0', 64); memset(entityTypeCode, '\0', 32); entityTypeToCreate = Pke_New(); needsReset = false; } 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::Separator(); if (ImGui::Button("Create")) { // TODO some type of validation char *sModelsDir = Pke_New(strlen(modelsDir) + 1); strncpy(sModelsDir, modelsDir, 63); entityTypeToCreate->modelsDir = sModelsDir; char *sModelFile = Pke_New(strlen(modelFile) + 1); strncpy(sModelFile, modelFile, 63); entityTypeToCreate->modelFile = sModelFile; char *sEntityTypeCode = Pke_New(strlen(entityTypeCode) + 1); strncpy(sEntityTypeCode, entityTypeCode, 31); entityTypeToCreate->entityTypeCode = sEntityTypeCode; shouldCreateEntityType = true; needsReset = 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 (component->vkPipelineLayout) ImGui::InputScalar("VkPipelineLayout", ImGuiDataType_U64, &component->vkPipelineLayout, nullptr, nullptr, "0x%016lX", ImGuiInputTextFlags_ReadOnly); if (component->vkDescriptorSet) ImGui::InputScalar("VkPipelineDescriptorSet", ImGuiDataType_U64, &component->vkDescriptorSet, 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("VertexCount", ImGuiDataType_U32, &component->vertexCount, 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("NormalCount", ImGuiDataType_U32, &component->normalsCount, 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("UVCount", ImGuiDataType_U32, &component->uvCount, 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("IndexFirstBinding", ImGuiDataType_U32, &component->indexFirstBinding, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("IndexCount", ImGuiDataType_U32, &component->indexCount, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("IndexOffsets", ImGuiDataType_U64, &component->indexOffsets, 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("InstanceCount", ImGuiDataType_U32, &component->instanceCount, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalar("InstanceOffsets", ImGuiDataType_U64, &component->instanceOffsets, nullptr, nullptr, nullptr, inputTextFlags); int64_t count = component->instances.Count(); ImGui::InputScalar("Instance Count", ImGuiDataType_S64, &count, 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(); ImGui::InputScalar("Instance Index", ImGuiDataType_U64, &component->index, nullptr, nullptr, nullptr, inputTextFlags); if (component->ptr) { ImGui::InputScalarN("pos", ImGuiDataType_Float, &component->ptr->pos, 3, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalarN("rot", ImGuiDataType_Float, &component->ptr->rot, 3, nullptr, nullptr, nullptr, inputTextFlags); ImGui::InputScalarN("scale", ImGuiDataType_Float, &component->ptr->scale, 3, nullptr, nullptr, nullptr, inputTextFlags); } ImGui::Spacing(); } void RecordImGuiSceneEditor() { { if (!ImGui::Begin("SceneEditorEntityTypes", &pkeSettings.editorSettings.isShowingSceneEditor)) { ImGui::End(); return; } if (ImGui::Button("Create Entity Type")) { ImGui::OpenPopup("CreateEntityType"); } if (ImGui::Button("Save")) { SaveSceneFile("test.pstf"); } if (ImGui::Button("Load")) { LoadSceneFile("test.pstf"); } if (ImGui::Button("Clear Selection")) { selectedEntity = EntityHandle_MAX; } if (selectedEntity != EntityHandle_MAX) { RecordImGui_CompGrBinds(true, ECS_GetGrBinds(selectedEntity)); RecordImGui_CompInstPos(false, ECS_GetInstance(selectedEntity)); } RecordImGuiModalCreateEntityType(); ImGui::End(); } } void RecordImGuiEditor() { if (pkeSettings.isShowingEditor) { RecordImGuiEditorWrapper(); RecordImGuiConsole(); RecordImGuiEntityList(); RecordImGuiSceneEditor(); } } void Game_Init() { for (long i = 0; i < consoleBufferCount; ++i) { memset(consoleBuffer[i], '\0', consoleLineLength); } Event_RegisterCallback("RenderImGui", RecordImGuiEditor); }