// Copyright (C) 2017 Jérôme Leclercq // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequesites.hpp #include #include #include #ifndef NDK_SERVER #include #include #include #include #include #include #endif namespace Ndk { /*! * \ingroup NDK * \class Ndk::Application * \brief NDK class that represents the application, it offers a set of tools to ease the development */ /*! * \brief Constructs an Application object with command-line arguments * * Pass the argc and argv arguments from the main function. * * Command-line arguments can be retrieved by application methods * * This calls Sdk::Initialize() * * \remark Only one Application instance can exist at a time */ Application::Application(int argc, char* argv[]) : Application() { std::regex optionRegex(R"(-(\w+))"); std::regex valueRegex(R"(-(\w+)\s*=\s*(.+))"); std::smatch results; for (int i = 1; i < argc; ++i) { std::string argument(argv[i]); if (std::regex_match(argument, results, valueRegex)) { Nz::String key(results[1].str()); Nz::String value(results[2].str()); m_parameters[key.ToLower()] = value; NazaraDebug("Registred parameter from command-line: " + key.ToLower() + "=" + value); } else if (std::regex_match(argument, results, optionRegex)) { Nz::String option(results[1].str()); m_options.insert(option); NazaraDebug("Registred option from command-line: " + option); } else NazaraWarning("Ignored command-line argument #" + Nz::String::Number(i) + " \"" + argument + '"'); } #ifndef NDK_SERVER if (HasOption("console")) EnableConsole(true); if (HasOption("fpscounter")) EnableFPSCounter(true); #endif } /*! * \brief Runs the application by updating worlds, taking care about windows, ... */ bool Application::Run() { #ifndef NDK_SERVER bool hasAtLeastOneActiveWindow = false; auto it = m_windows.begin(); while (it != m_windows.end()) { Nz::Window& window = *it->window; window.ProcessEvents(); if (!window.IsOpen(true)) { it = m_windows.erase(it); continue; } hasAtLeastOneActiveWindow = true; ++it; } if (m_exitOnClosedWindows && !hasAtLeastOneActiveWindow) return false; #endif if (m_shouldQuit) return false; m_updateTime = m_updateClock.GetSeconds(); m_updateClock.Restart(); for (World& world : m_worlds) world.Update(m_updateTime); #ifndef NDK_SERVER for (WindowInfo& info : m_windows) { if (!info.overlayWorld) continue; if (info.fpsCounter) { FPSCounterOverlay& fpsCounter = *info.fpsCounter; fpsCounter.frameCount++; fpsCounter.elapsedTime += m_updateTime; if (fpsCounter.elapsedTime >= 1.f) { fpsCounter.sprite->Update(Nz::SimpleTextDrawer::Draw("FPS: " + Nz::String::Number(fpsCounter.frameCount), 36)); fpsCounter.frameCount = 0; fpsCounter.elapsedTime = 0.f; } } info.overlayWorld->Update(m_updateTime); } #endif return true; } #ifndef NDK_SERVER void Application::SetupConsole(WindowInfo& info) { std::unique_ptr overlay = std::make_unique(); Nz::Vector2ui windowDimensions; if (info.window->IsValid()) windowDimensions.Set(info.window->GetWidth(), info.window->GetHeight()); else windowDimensions.MakeZero(); overlay->console = info.canvas->Add(overlay->lua); Console& consoleRef = *overlay->console; consoleRef.SetSize({float(windowDimensions.x), windowDimensions.y / 4.f}); consoleRef.Show(false); // Redirect logs toward the console overlay->logSlot.Connect(Nz::Log::OnLogWrite, [&consoleRef] (const Nz::String& str) { consoleRef.AddLine(str); }); LuaAPI::RegisterClasses(overlay->lua); // Override "print" function to add a line in the console overlay->lua.PushFunction([&consoleRef] (Nz::LuaState& state) { Nz::StringStream stream; unsigned int argCount = state.GetStackTop(); state.GetGlobal("tostring"); for (unsigned int i = 1; i <= argCount; ++i) { state.PushValue(-1); // tostring function state.PushValue(i); // argument state.Call(1, 1); std::size_t length; const char* str = state.CheckString(-1, &length); if (i > 1) stream << '\t'; stream << Nz::String(str, length); state.Pop(1); } consoleRef.AddLine(stream); return 0; }); overlay->lua.SetGlobal("print"); // Define a few base variables to allow our interface to interact with the application overlay->lua.PushGlobal("Application", Ndk::Application::Instance()); overlay->lua.PushGlobal("Console", consoleRef.CreateHandle()); // Setup a few event callback to handle the console Nz::EventHandler& eventHandler = info.window->GetEventHandler(); /*overlay->eventSlot.Connect(eventHandler.OnEvent, [&consoleRef] (const Nz::EventHandler*, const Nz::WindowEvent& event) { if (consoleRef.IsVisible()) consoleRef.SendEvent(event); });*/ overlay->keyPressedSlot.Connect(eventHandler.OnKeyPressed, [&consoleRef] (const Nz::EventHandler*, const Nz::WindowEvent::KeyEvent& event) { if (event.code == Nz::Keyboard::F9) consoleRef.Show(!consoleRef.IsVisible()); }); overlay->resizedSlot.Connect(info.renderTarget->OnRenderTargetSizeChange, [&consoleRef] (const Nz::RenderTarget* renderTarget) { consoleRef.SetSize({float(renderTarget->GetWidth()), renderTarget->GetHeight() / 4.f}); }); info.console = std::move(overlay); } void Application::SetupFPSCounter(WindowInfo& info) { std::unique_ptr fpsCounter = std::make_unique(); fpsCounter->sprite = Nz::TextSprite::New(); fpsCounter->entity = info.overlayWorld->CreateEntity(); fpsCounter->entity->AddComponent(); fpsCounter->entity->AddComponent().Attach(fpsCounter->sprite); info.fpsCounter = std::move(fpsCounter); } void Application::SetupOverlay(WindowInfo& info) { info.overlayWorld = std::make_unique(false); //< No default system if (info.window->IsValid()) info.canvas = std::make_unique(info.overlayWorld->CreateHandle(), info.window->GetEventHandler(), info.window->GetCursorController().CreateHandle()); RenderSystem& renderSystem = info.overlayWorld->AddSystem(); renderSystem.ChangeRenderTechnique(); renderSystem.SetDefaultBackground(nullptr); renderSystem.SetGlobalUp(Nz::Vector3f::Down()); EntityHandle viewer = info.overlayWorld->CreateEntity(); CameraComponent& camComponent = viewer->AddComponent(); viewer->AddComponent(); camComponent.SetProjectionType(Nz::ProjectionType_Orthogonal); camComponent.SetTarget(info.renderTarget); } #endif Application* Application::s_application = nullptr; }