#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "GameData.h" #include "Components.h" #include "LuaData.h" #include "AppModule.h" #include "TerrainModule.h" #include "StaticGeometryModule.h" #include "EditorGizmoModule.h" #include "PhysicsModule.h" #include "PlayerActionModule.h" #include "CharacterModule.h" #include "CharacterManagerModule.h" #include "items.h" #include "physics.h" #include "GUIModule.h" #include "GUIModuleCommon.h" namespace ECS { struct GUIListener; struct GUIData { Ogre::ImGuiOverlay *mGuiOverlay; std::vector glb_names; GUIListener *mGUIListener; }; struct GUIListener : public Ogre::RenderTargetListener { float panel_width; bool enableEditor; bool enableMapEditor; ImFont *smallFont, *midFont, *bigFont; Ogre::FontPtr _smallFont, _midFont, _bigFont; GUIListener() : Ogre::RenderTargetListener() { _midFont = createFont("midFont", "General", "Jupiteroid-Regular.ttf", 18.0f); _smallFont = createFont("smallFont", "General", "Jupiteroid-Regular.ttf", 13.0f); _bigFont = createFont("bigFont", "General", "Kenney Bold.ttf", 32.0f); smallFont = ECS::get().mGuiOverlay->addFont( "smallFont", "General"); OgreAssert(smallFont, "Could not load font"); midFont = ECS::get().mGuiOverlay->addFont("midFont", "General"); OgreAssert(midFont, "Could not load font"); bigFont = ECS::get().mGuiOverlay->addFont("bigFont", "General"); OgreAssert(bigFont, "Could not load font"); #if 0 Ogre::FontPtr _midFont = createFont("midFont", "General", "Kenney Bold.ttf", 28.0f); #endif #if 0 ImGui::GetIO().Fonts->Build(); #endif } Ogre::FontPtr createFont(const Ogre::String &name, const Ogre::String &group, const Ogre::String &ttfname, float fontSize) { Ogre::FontPtr ret = Ogre::FontManager::getSingleton().create(name, group); ret->setType(Ogre::FontType::FT_TRUETYPE); ret->setSource(ttfname); ret->setTrueTypeSize(fontSize); ret->setTrueTypeResolution(75); ret->addCodePointRange(Ogre::Font::CodePointRange(30, 128)); ret->load(); return ret; } void preViewportUpdate(const Ogre::RenderTargetViewportEvent &evt) override { preview(evt); } void buttons_panel() { ImVec2 size = ImGui::GetMainViewport()->Size; float window_width = size.x * 0.2f; if (window_width > panel_width) window_width = panel_width; float window_height = size.y * 0.5f - 20; ImGui::SetNextWindowPos(ImVec2(0, size.y * 0.5f + 20), ImGuiCond_Always); ImGui::SetNextWindowSize(ImVec2(window_width, window_height), ImGuiCond_Always); ImGui::Begin("Control"); // if (ECS::get().get().enabled) // ECS::get().get().app->setWindowGrab(true); if (ImGui::Button("Quit")) Ogre::Root::getSingleton().queueEndRendering(); if (ImGui::Button("Return")) ECS::get().get().finish(); if (ImGui::Button("Enable/Disable item editor")) { enableEditor ^= true; if (enableEditor) enableMapEditor = false; } if (ImGui::Button("Enable/Disable map editor")) { enableMapEditor ^= true; if (enableMapEditor) enableEditor = false; } ImGui::Text("Text message..."); ImGui::End(); } void create_entity_node(const Ogre::String &name, int key) { Ogre::Entity *ent = ECS::get().get().mScnMgr->createEntity( name); Ogre::SceneNode *pnode = ECS::get() .get() .mScnMgr->getRootSceneNode() ->createChildSceneNode( "ent:" + name + Ogre::StringConverter::toString( key), ECS::get() .get() .mCameraPivot->getPosition(), ECS::get() .get() .mCameraPivot->getOrientation()); pnode->attachObject(ent); Ogre::Quaternion q = pnode->getOrientation(); Ogre::Radian yaw = q.getYaw(); Ogre::Quaternion nq(yaw, Ogre::Vector3(0, 1, 0)); pnode->setOrientation(nq); ECS::get().get().finish(); } void buildings_editor() { int i; ImVec2 size = ImGui::GetMainViewport()->Size; float window_width = size.x * 0.2f; if (window_width > panel_width) window_width = panel_width; float window_height = size.y * 0.5 - 20; ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); ImGui::SetNextWindowSize(ImVec2(window_width, window_height), ImGuiCond_Always); ImGui::Begin("Droppings..."); for (i = 0; i < ECS::get().get().glb_names.size(); i++) { Ogre::String id_button = "Create entity: " + ECS::get().get().glb_names[i] + "##ent:" + ECS::get().get().glb_names[i]; if (ImGui::Button(id_button.c_str())) { create_entity_node( ECS::get().get().glb_names[i], i); } } ImGui::End(); } void position_editor(Ogre::SceneNode *node) { Ogre::Vector3 position = node->getPosition(); float v[3] = { position.x, position.y, position.z }; ImGui::InputFloat3("position", v); position.x = v[0]; position.y = v[1]; position.z = v[2]; node->setPosition(position); } void orientation_editor(Ogre::SceneNode *node) { Ogre::Quaternion q = node->getOrientation(); float yaw = Ogre::Radian(q.getYaw()).valueDegrees(); float pitch = Ogre::Radian(q.getPitch()).valueDegrees(); float roll = Ogre::Radian(q.getRoll()).valueDegrees(); bool m1 = ImGui::InputFloat("yaw", &yaw); bool m2 = ImGui::InputFloat("pitch", &pitch); bool m3 = ImGui::InputFloat("roll", &roll); if (m1 || m2 || m3) { Ogre::Quaternion q1(Ogre::Radian(Ogre::Degree(yaw)), Ogre::Vector3::UNIT_Y); Ogre::Quaternion q2(Ogre::Degree(pitch), Ogre::Vector3::UNIT_X); Ogre::Quaternion q3(Ogre::Degree(roll), Ogre::Vector3::UNIT_Z); node->setOrientation(q1 * q2 * q3); } } void attachments_editor(Ogre::SceneNode *node) { const Ogre::SceneNode::ObjectMap &pmap = node->getAttachedObjects(); int i; for (i = 0; i < pmap.size(); i++) { const Ogre::MovableObject *mobj = pmap[i]; const Ogre::String &pname = mobj->getName(); ImGui::Text("Name: %s", pname.c_str()); } } void map_editor() { } Ogre::Vector2 projectToScreen(const Ogre::Vector3 &worldPoint) { ImVec2 size = ImGui::GetMainViewport()->Size; float width = size.x; float height = size.y; Ogre::Camera *camera = ECS::get().mCamera; // 1. Convert to camera space Ogre::Vector3 eyeSpacePoint = camera->getViewMatrix() * worldPoint; // 2. Project to clip space Ogre::Vector3 clipSpacePoint = camera->getProjectionMatrix() * eyeSpacePoint; if (clipSpacePoint.z < 0.0f) return Ogre::Vector2(-1, -1); // 3. Convert from clip space (-1 to 1) to screen space (0 to 1) // Note: Y is usually flipped in API screen coordinates compared to projection float screenX = (clipSpacePoint.x / 2.0f) + 0.5f; float screenY = 1.0f - ((clipSpacePoint.y / 2.0f) + 0.5f); // 4. Map to actual pixel dimensions return Ogre::Vector2(screenX * width, screenY * height); } void preview(const Ogre::RenderTargetViewportEvent &evt) { int i; Ogre::ImGuiOverlay::NewFrame(); if (ECS::get().get().startupDelay > 0.0f) { ImVec2 size = ImGui::GetMainViewport()->Size; ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); ImGui::SetNextWindowSize(ImVec2(size.x, size.y), ImGuiCond_Always); ImGui::Begin( "StartupScreen", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoInputs); // if (ECS::get().get().enabled) // ECS::get().get().app->setWindowGrab(true); ImGui::PushFont(bigFont); ImGui::TextWrapped( "%s", "This game does not autosave. Please use save function to keep your state"); ImGui::PopFont(); ImGui::End(); } else if (ECS::get().narrationHandlers.size() > 0) { ECS::get_mut().grab = false; ECS::get_mut().grabChanged = true; ECS::get_mut().enabled = true; ECS::get_mut().narrationBox = true; ImVec2 size = ImGui::GetMainViewport()->Size; ImGui::SetNextWindowPos(ImVec2(0, size.y * 0.75f), ImGuiCond_Always); ImGui::SetNextWindowSize(ImVec2(size.x, size.y * 0.25f), ImGuiCond_Always); ImGui::Begin("Narration...", NULL, ImGuiWindowFlags_NoTitleBar); ImGui::PushFont(midFont); ImVec2 p = ImGui::GetCursorScreenPos(); GUI::NarrationHandler *narration = ECS::get().narrationHandlers.front(); if (!narration->is_active()) narration->_event("narration_activate"); ImGui::TextWrapped( "%s", narration->getNarrationText().c_str()); if (narration->getChoices().size() == 0) { ImGui::SetCursorScreenPos(p); if (ImGui::InvisibleButton( "Background", ImGui::GetWindowSize())) narration->progress(); } else { int i; for (i = 0; i < narration->getChoices().size(); i++) { if (ImGui::Button( narration->getChoices()[i] .c_str())) { narration->setNarrationAnswer( i + 1); std::cout << "answer: " << i + 1 << std::endl; } } } if (narration->is_complete()) { ECS::get_mut().enabled = false; ECS::get_mut().narrationBox = false; ECS::get_mut().grab = true; ECS::get_mut().grabChanged = true; ECS::get_mut().removeNarrationHandler( narration); delete narration; } ImGui::Spacing(); ImGui::PopFont(); ImGui::End(); ECS::modified(); } else if (ECS::get().get().narrationBox) { ImVec2 size = ImGui::GetMainViewport()->Size; ImGui::SetNextWindowPos(ImVec2(0, size.y * 0.75f), ImGuiCond_Always); ImGui::SetNextWindowSize(ImVec2(size.x, size.y * 0.25f), ImGuiCond_Always); ImGui::Begin("Narration...", NULL, ImGuiWindowFlags_NoTitleBar); ImGui::PushFont(midFont); ImVec2 p = ImGui::GetCursorScreenPos(); ImGui::TextWrapped( "%s", ECS::get().get().narrationText.c_str()); if (ECS::get().get().choices.size() == 0) { ImGui::SetCursorScreenPos(p); if (ImGui::InvisibleButton( "Background", ImGui::GetWindowSize())) ECS::get().mLua->call_handler( "narration_progress"); } else { int i; for (i = 0; i < ECS::get().get().choices.size(); i++) { if (ImGui::Button(ECS::get() .get() .choices[i] .c_str())) { ECS::get() .get_mut() .narration_answer = i + 1; std::cout << "answer: " << i + 1 << std::endl; ECS::modified(); ECS::get().mLua->call_handler( "narration_answered"); } } } ImGui::Spacing(); ImGui::PopFont(); ImGui::End(); } else if (ECS::get().get().enabled) { if (ECS::get().get().mainMenu) { ImVec2 size = ImGui::GetMainViewport()->Size; ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); ImGui::SetNextWindowSize(ImVec2(size.x, size.y), ImGuiCond_Always); ImGui::Begin( "MainMenu", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoFocusOnAppearing | 0); // if (ECS::get().get().enabled) // ECS::get().get().app->setWindowGrab(true); ImGui::PushFont(bigFont); ImGui::TextWrapped("%s", "Booo!!!!"); bool pressed = false; bool new_game = false, cont = false, load_game = false, opts = false, quit = false; ImGui::SetCursorPosY(size.y / 2.0f - 300.0f); ImGui::SetCursorPosX(size.x / 2.0f - 300.0f); new_game = ImGui::Button("New Game"); ImGui::SetCursorPosX(size.x / 2.0f - 300.0f); cont = ImGui::Button("Continue"); ImGui::SetCursorPosX(size.x / 2.0f - 300.0f); load_game = ImGui::Button("Load Game"); ImGui::SetCursorPosX(size.x / 2.0f - 300.0f); opts = ImGui::Button("Options"); ImGui::SetCursorPosX(size.x / 2.0f - 300.0f); quit = ImGui::Button("Quit###quit"); pressed = new_game || cont || load_game || opts || quit; ImGui::PopFont(); ImGui::Spacing(); ImGui::End(); if (quit) Ogre::Root::getSingleton() .queueEndRendering(); if (pressed) ECS::get().get().finish(); if (new_game) { ECS::get().mLua->call_handler( "new_game"); } } else { buttons_panel(); if (enableEditor) buildings_editor(); if (enableMapEditor) map_editor(); ImVec2 size = ImGui::GetMainViewport()->Size; float window_width = size.x * 0.2f; if (window_width > panel_width) window_width = panel_width; float window_height = size.y * 0.5f - 20; ImGui::SetNextWindowPos( ImVec2(size.x - window_width, size.y * 0.5f + 20), ImGuiCond_Always); ImGui::SetNextWindowSize(ImVec2(window_width, window_height), ImGuiCond_Always); // ImGui::Begin("Dumb and Stupid", &mKbd.gui_active); ImGui::Begin("Panel..."); std::deque tree_input_queue, tree_output_queue; std::vector tree_list; tree_input_queue.push_back( ECS::get() .get() .mScnMgr->getRootSceneNode()); tree_input_queue.push_back(nullptr); std::set visited; while (true) { int new_nodes_count = 0; while (!tree_input_queue.empty()) { int child; Ogre::SceneNode *item = tree_input_queue.front(); tree_input_queue.pop_front(); if (item && visited.find(item) == visited.end()) { // new node new_nodes_count++; tree_output_queue .push_back( item); visited.insert(item); const Ogre::Node::ChildNodeMap &children = item->getChildren(); for (child = 0; child < children.size(); child++) { tree_output_queue .push_back(static_cast< Ogre::SceneNode *>( children[child])); tree_output_queue .push_back( nullptr); } } else tree_output_queue .push_back( item); } if (new_nodes_count == 0) break; tree_input_queue = tree_output_queue; tree_output_queue.clear(); } tree_list.insert(tree_list.begin(), tree_output_queue.begin(), tree_output_queue.end()); int count = 0; int depth = 0; std::vector check_depth; int max_depth = 0; check_depth.push_back(0); for (count = 0; count < tree_list.size(); count++) { int t; Ogre::SceneNode *node = tree_list[count]; if (node && max_depth >= depth) { Ogre::String name = node->getName(); if (name.length() == 0) { name = "Node #" + Ogre::StringConverter:: toString( count); } if (ImGui::TreeNode( name.c_str())) { check_depth.push_back( max_depth); max_depth++; ImGui::Text( "%s", (name + "##caption") .c_str()); position_editor(node); ImGui::Separator(); orientation_editor( node); ImGui::Separator(); ImGui::Text( "Attachments"); attachments_editor( node); } } else if (!node && max_depth >= depth) { max_depth = check_depth.back(); check_depth.pop_back(); ImGui::TreePop(); } if (tree_list[count]) depth++; else depth--; } ImGui::Spacing(); ImGui::End(); } } else { ECS::ActionNodeList &list = ECS::get_mut(); if (list.dynamicNodes.size() > 0) { int j; Ogre::Vector3 queryPos; std::vector points = list.points; std::vector distances = list.distances; Ogre::SceneNode *cameraNode = ECS::get().mCameraNode; Ogre::Vector3 cameraPos = cameraNode->_getDerivedPosition(); float minDistance = 25.0f; int i; list.selected = -1; for (i = 0; i < points.size(); i++) { size_t p = points[i]; float distance = distances[i]; float radius = list.dynamicNodes[p] .props["radius"] .get(); float height = list.dynamicNodes[p] .props["height"] .get(); float actDistance = radius * radius + height * height; float textDistance = actDistance + 2.0f * 2.0f; Ogre::Vector2 screenPos = projectToScreen( list.dynamicNodes[p] .position); if (screenPos.x < 0) continue; // FIXME: move this to system to filter points there Ogre::Vector3 hitPosition; JPH::BodyID hitBody; bool hit = JoltPhysicsWrapper::getSingleton() .raycastQuery( list.dynamicNodes[p] .position, cameraPos, hitPosition, hitBody); if (hit) continue; if (list.selected == -1) { if (distance < actDistance) list.selected = p; } ImDrawList *drawList = ImGui::GetBackgroundDrawList(); ImVec2 center = ImVec2(screenPos.x, screenPos.y); float circleRadius = 8.0f; ImColor circleColor( ImVec4(0.3f, 0.3f, 0.3f, 1.0f)); if (p == list.selected) { circleRadius = 16.0f; circleColor = ImColor( ImVec4(0, 0, 1, 1)); } drawList->AddCircleFilled(center, circleRadius, circleColor); drawList->AddCircle( center, circleRadius, IM_COL32(64, 64, 255, 255)); ImVec2 textSize = ImGui::CalcTextSize( list.dynamicNodes[p] .action_text.c_str()); ImVec2 textPos = ImVec2( center.x - (textSize.x * 0.5f), center.y + circleRadius + 4.0f); if (distance < textDistance) { drawList->AddText( ImVec2(textPos.x + 1, textPos.y + 1), IM_COL32(0, 0, 0, 200), list.dynamicNodes[p] .action_text .c_str()); drawList->AddText( ImVec2(textPos.x, textPos.y), IM_COL32(255, 32, 64, 255), list.dynamicNodes[p] .action_text .c_str()); } #if 0 ImGui::SetNextWindowPos( ImVec2(screenPos.x, screenPos.y), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::Begin( ("SensorLabel##" + Ogre::StringConverter::toString( p)) .c_str(), nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoInputs); ImDrawList *drawList = ImGui::GetWindowDrawList(); ImVec2 cursor = ImGui::GetCursorScreenPos(); drawList->AddCircleFilled( ImVec2(cursor.x, cursor.y), 16.0f, IM_COL32(0, 0, 255, 255)); drawList->AddCircle( ImVec2(cursor.x, cursor.y), 16.0f, IM_COL32(32, 32, 255, 255)); ImGui::TextColored( ImVec4(1, 0, 0, 1), "%s", list.nodes[p] .action_text.c_str()); ImGui::End(); #endif } } } } }; GUIModule::GUIModule(flecs::world &ecs) { ecs.module(); ecs.import (); ecs.component() .on_add([](GUI &gui) { gui.enabled = false; gui.grab = false; gui.grabChanged = false; }) .add(flecs::Singleton); ecs.component() .on_add([](GUIData &priv) { priv.glb_names.clear(); priv.mGUIListener = nullptr; priv.mGuiOverlay = nullptr; }) .add(flecs::Singleton); ecs.set({ false, true, false, false, false, "", {}, -1 }); ecs.set({ nullptr, {}, nullptr }); ui_wait = ecs.system("SetupGUI") .kind(flecs::OnUpdate) .each([this](const RenderWindow &window, App &app, GUIData &gui) { if (!gui.mGuiOverlay) { float vpScale = window.dpi / 96 * window.window->getWidth() / 1600.0f; Ogre::OverlayManager::getSingleton() .setPixelRatio(vpScale); std::cout << "GUI configure\n"; OgreAssert(app.mGuiOverlay, "No ImGUI overlay"); gui.mGuiOverlay = app.mGuiOverlay; gui.mGUIListener = new GUIListener(); gui.mGuiOverlay->setZOrder(300); gui.mGuiOverlay->show(); gui.mGUIListener->panel_width = 300.0f; gui.mGUIListener->enableEditor = false; window.window->addListener( gui.mGUIListener); int i; const std::vector &groups = Ogre::ResourceGroupManager:: getSingleton() .getResourceGroups(); for (i = 0; i < groups.size(); i++) { std::vector names = *Ogre::ResourceGroupManager::getSingleton() .findResourceNames( groups[i], "*.glb"); gui.glb_names.insert( gui.glb_names.end(), names.begin(), names.end()); } ECS::modified(); std::cout << "GUI configure finished\n"; } }); } }