#include "main.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ext.h" #include #include #include #include #include namespace { struct { uf::String input; std::ofstream output; struct { std::string output = "log/output.txt"; } filenames; } io; struct { spec::Time::time_t epoch; uf::Timer<> sys = uf::Timer<>(false); uf::Timer<> delta = uf::Timer<>(false); double prevTime = 0; double curTime = 0; double deltaTime = 0; } times; uf::Serializer config; } bool ext::ready = false; void EXT_API ext::initialize() { /**/ { srand(time(NULL)); } /* Open output file */ { io.output.open(io.filenames.output); } /* Initialize timers */ { times.epoch = spec::time.getTime(); times.sys.start(); times.delta.start(); times.prevTime = times.sys.elapsed().asDouble(); times.curTime = times.sys.elapsed().asDouble(); } /* Read persistent data */ { // #include "./inits/persistence.inl" } ::config = ext::getConfig(); /* Parse config */ { // Set memory pool sizes { auto deduceSize = []( auto& value )->size_t{ if ( value.isNumeric() ) return value.asUInt64(); if ( value.isString() ) { std::string str = value.asString(); std::regex regex("^(\\d+) ?((?:K|M|G)?(?:i?B)?)$"); std::smatch match; if ( std::regex_search( str, match, regex ) ) { size_t requested = std::stoi( match[1].str() ); std::string prefix = match[2].str(); switch ( prefix.at(0) ) { case 'K': return requested * 1024; case 'M': return requested * 1024 * 1024; case 'G': return requested * 1024 * 1024 * 1024; } return requested; } } return 0; }; { size_t size = deduceSize( ::config["engine"]["memory pool"]["size"] ); uf::MemoryPool::globalOverride = ::config["engine"]["memory pool"]["globalOverride"].asBool(); std::cout << "Requesting " << (int) size << " bytes for global memory pool: " << &uf::MemoryPool::global << std::endl; uf::MemoryPool::global.initialize( size ); uf::MemoryPool::subPool = ::config["engine"]["memory pool"]["subPools"].asBool(); if ( size <= 0 || uf::MemoryPool::subPool ) { { size_t size = deduceSize( ::config["engine"]["memory pools"]["component"] ); std::cout << "Requesting " << (int) size << " bytes for component memory pool: " << &uf::component::memoryPool << std::endl; uf::component::memoryPool.initialize( size ); } { size_t size = deduceSize( ::config["engine"]["memory pools"]["userdata"] ); std::cout << "Requesting " << (int) size << " bytes for userdata memory pool: " << &uf::userdata::memoryPool << std::endl; uf::userdata::memoryPool.initialize( size ); } { size_t size = deduceSize( ::config["engine"]["memory pools"]["entity"] ); std::cout << "Requesting " << (int) size << " bytes for entity memory pool: " << &uf::Entity::memoryPool << std::endl; uf::Entity::memoryPool.initialize( size ); } } } } /* Frame limiter */ { double limit = ::config["engine"]["frame limit"].asDouble(); if ( limit != 0 ) uf::thread::limiter = 1.0 / ::config["engine"]["frame limit"].asDouble(); else uf::thread::limiter = 0; } /* Max delta time */{ double limit = ::config["engine"]["delta limit"].asDouble(); if ( limit != 0 ) uf::physics::time::clamp = 1.0 / ::config["engine"]["delta limit"].asDouble(); else uf::physics::time::clamp = 0; } // Set worker threads uf::thread::workers = ::config["engine"]["worker threads"].asUInt64(); // Enable valiation layer uf::renderer::validation = ::config["engine"]["ext"]["vulkan"]["validation"]["enabled"].asBool(); for ( int i = 0; i < ::config["engine"]["ext"]["vulkan"]["validation"]["filters"].size(); ++i ) { uf::renderer::validationFilters.push_back( ::config["engine"]["ext"]["vulkan"]["validation"]["filters"][i].asString() ); } for ( int i = 0; i < ::config["engine"]["ext"]["vulkan"]["extensions"]["device"].size(); ++i ) { uf::renderer::requestedDeviceExtensions.push_back( ::config["engine"]["ext"]["vulkan"]["extensions"]["device"][i].asString() ); } for ( int i = 0; i < ::config["engine"]["ext"]["vulkan"]["extensions"]["instance"].size(); ++i ) { uf::renderer::requestedInstanceExtensions.push_back( ::config["engine"]["ext"]["vulkan"]["extensions"]["instance"][i].asString() ); } for ( int i = 0; i < ::config["engine"]["ext"]["vulkan"]["features"].size(); ++i ) { uf::renderer::requestedDeviceFeatures.push_back( ::config["engine"]["ext"]["vulkan"]["features"][i].asString() ); } ext::openvr::enabled = ::config["engine"]["ext"]["vr"]["enable"].asBool(); ext::openvr::swapEyes = ::config["engine"]["ext"]["vr"]["swap eyes"].asBool(); if ( ::config["engine"]["ext"]["vr"]["dominatEye"].isNumeric() ) ext::openvr::dominantEye = ::config["engine"]["ext"]["vr"]["dominatEye"].asUInt64(); else if ( ::config["engine"]["ext"]["vr"]["dominatEye"].asString() == "left" ) ext::openvr::dominantEye = 0; else if ( ::config["engine"]["ext"]["vr"]["dominatEye"].asString() == "right" ) ext::openvr::dominantEye = 1; ext::openvr::driver.manifest = ::config["engine"]["ext"]["vr"]["manifest"].asString(); if ( ext::openvr::enabled ) { ::config["engine"]["render modes"]["stereo deferred"] = true; } } /* Create initial scene (kludge) */ { uf::Scene& scene = uf::instantiator::instantiate(); //new uf::Scene; uf::scene::scenes.push_back(&scene); auto& metadata = scene.getComponent(); metadata["system"]["config"] = ::config; } /* Initialize Vulkan */ { // uf::renderer::width = ::config["window"]["size"]["x"].asInt(); // uf::renderer::height = ::config["window"]["size"]["y"].asInt(); // setup render mode if ( ::config["engine"]["render modes"]["gui"].asBool() ) { auto* renderMode = new uf::renderer::RenderTargetRenderMode; uf::renderer::addRenderMode( renderMode, "Gui" ); renderMode->blitter.descriptor.subpass = 1; } if ( ::config["engine"]["render modes"]["stereo deferred"].asBool() ) uf::renderer::addRenderMode( new uf::renderer::StereoscopicDeferredRenderMode, "" ); else if ( ::config["engine"]["render modes"]["deferred"].asBool() ) uf::renderer::addRenderMode( new uf::renderer::DeferredRenderMode, "" ); // if ( ::config["engine"]["render modes"]["compute"].asBool() ) // uf::renderer::addRenderMode( new uf::renderer::ComputeRenderMode, "C:RT:0" ); if ( ext::openvr::enabled ) { ext::openvr::initialize(); uint32_t width, height; ext::openvr::recommendedResolution( width, height ); auto& renderMode = uf::renderer::getRenderMode("Stereoscopic Deferred", true); renderMode.width = width; renderMode.height = height; std::cout << "Recommended VR Resolution: " << width << ", " << height << std::endl; } uf::renderer::initialize(); } /* */ { pod::Thread& threadMain = uf::thread::has("Main") ? uf::thread::get("Main") : uf::thread::create( "Main", false, true ); pod::Thread& threadAux = uf::thread::has("Aux") ? uf::thread::get("Aux") : uf::thread::create( "Aux", true, false ); pod::Thread& threadPhysics = uf::thread::has("Physics") ? uf::thread::get("Physics") : uf::thread::create( "Physics", true, false ); } /* Discord */ if ( ::config["engine"]["ex"]["discord"]["enabled"].asBool() ) { ext::discord::initialize(); } /* Initialize root scene*/ { uf::scene::unloadScene(); auto& scene = uf::scene::loadScene( ::config["engine"]["scenes"]["start"].asString() ); auto& metadata = scene.getComponent(); metadata["system"]["config"] = ::config; } /* Add hooks */ { uf::hooks.addHook( "game:LoadScene", [&](const std::string& event)->std::string{ uf::Serializer json = event; uf::scene::unloadScene(); auto& scene = uf::scene::loadScene( json["scene"].asString() ); auto& metadata = scene.getComponent(); metadata["system"]["config"] = ::config; return "true"; }); uf::hooks.addHook( "system:Quit", [&](const std::string& event)->std::string{ std::cout << "system:Quit: " << event << std::endl; ext::ready = false; return "true"; }); } ext::ready = true; uf::iostream << "EXT took " << times.sys.elapsed().asDouble() << " seconds to initialize!" << "\n"; { uf::thread::add( uf::thread::fetchWorker(), [&]() -> int { uf::Asset& assetLoader = uf::scene::getCurrentScene().getComponent(); assetLoader.processQueue(); return 0;}, false ); } } void EXT_API ext::tick() { /* Timer */ { times.prevTime = times.curTime; times.curTime = times.sys.elapsed().asDouble(); times.deltaTime = times.curTime - times.prevTime; } static uf::Timer timer(false); if ( !timer.running() ) timer.start(); /* Print World Tree */ { static uf::Timer timer(false); if ( !timer.running() ) timer.start(); if ( uf::Window::isKeyPressed("U") && timer.elapsed().asDouble() >= 1 ) { timer.reset(); std::function filter = []( uf::Entity* entity, int indent ) { for ( int i = 0; i < indent; ++i ) uf::iostream << "\t"; uf::iostream << entity->getName() << ": " << entity->getUid(); if ( entity->hasComponent>() ) { pod::Transform<> t = uf::transform::flatten(entity->getComponent>()); uf::iostream << " (" << t.position.x << ", " << t.position.y << ", " << t.position.z << ") (" << t.orientation.x << ", " << t.orientation.y << ", " << t.orientation.z << ", " << t.orientation.w << ")"; } uf::iostream << "\n"; }; for ( uf::Scene* scene : uf::renderer::scenes ) { if ( !scene ) continue; std::cout << "Scene: " << scene->getName() << ": " << scene << std::endl; scene->process(filter, 1); } } } /* Print Entity Information */ { static uf::Timer timer(false); if ( !timer.running() ) timer.start(); if ( uf::Window::isKeyPressed("P") && timer.elapsed().asDouble() >= 1 ) { timer.reset(); // uf::iostream << uf::renderer::allocatorStats() << "\n"; if ( uf::MemoryPool::global.size() > 0 ) uf::iostream << "Global Memory Pool:\n" << uf::MemoryPool::global.stats() << "\n"; if ( uf::Entity::memoryPool.size() > 0 ) uf::iostream << "Entity Memory Pool:\n" << uf::Entity::memoryPool.stats() << "\n"; if ( uf::component::memoryPool.size() > 0 ) uf::iostream << "Components Memory Pool:\n" << uf::component::memoryPool.stats() << "\n"; if ( uf::userdata::memoryPool.size() > 0 ) uf::iostream << "Userdata Memory Pool:\n" << uf::userdata::memoryPool.stats() << "\n"; /* size_t size = uf::component::memoryPool.size(); size_t allocated = uf::component::memoryPool.allocated(); uf::iostream << "Memory Pools:\n" << "\tComponents:\n" << "\t\tSize : " << size << "\n" << "\t\tAllocated: " << allocated << "\n" << "\t\tFree : " << (size-allocated) << "\n"; */ } } /* Attempt to reset VR position */ { static uf::Timer timer(false); if ( !timer.running() ) timer.start(); if ( uf::Window::isKeyPressed("Z") && timer.elapsed().asDouble() >= 1 ) { timer.reset(); uf::hooks.call("VR:Seat.Reset"); } } /* Print controller position */ if ( false ) { static uf::Timer timer(false); if ( !timer.running() ) timer.start(); if ( uf::Window::isKeyPressed("Z") && timer.elapsed().asDouble() >= 1 ) { timer.reset(); auto& scene = uf::scene::getCurrentScene(); auto* controller = scene.getController(); auto& camera = controller->getComponent(); auto& t = camera.getTransform(); //controller->getComponent>(); uf::iostream << "Viewport position: (" << t.position.x << ", " << t.position.y << ", " << t.position.z << ") (" << t.orientation.x << ", " << t.orientation.y << ", " << t.orientation.z << ", " << t.orientation.w << ")"; uf::iostream << "\n"; if ( false ) { uf::Entity* light = scene.findByUid(scene.loadChild("/light.json", true)); if ( light ) { auto& lTransform = light->getComponent>(); auto& lMetadata = light->getComponent(); lTransform.position = t.position; lTransform.orientation = t.orientation; if ( !lMetadata["light"].isArray() ) { lMetadata["light"]["color"][0] = (rand() % 100) / 100.0; lMetadata["light"]["color"][1] = (rand() % 100) / 100.0; lMetadata["light"]["color"][2] = (rand() % 100) / 100.0; } } auto& sMetadata = scene.getComponent(); sMetadata["light"]["should"] = true; } } } /* Update physics timer */ { uf::physics::tick(); } /* Update entities */ { uf::scene::tick(); } /* Tick Main Thread Queue */ { pod::Thread& thread = uf::thread::has("Main") ? uf::thread::get("Main") : uf::thread::create( "Main", false, true ); uf::thread::process( thread ); } /* Update vulkan */ { uf::renderer::tick(); } /* Discord */ if ( ::config["engine"]["ext"]["discord"]["enable"].asBool() ) { ext::discord::tick(); } /* OpenVR */ if ( ext::openvr::context ) { ext::openvr::tick(); } /* Frame limiter of sorts I guess */ { long long sleep = (uf::thread::limiter * 1000) - timer.elapsed().asMilliseconds(); if ( sleep > 0 ) { // std::cout << "Frame limiting: " << sleep << ", " << timer.elapsed().asMilliseconds() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); } timer.reset(); } } void EXT_API ext::render() { // uf::scene::render(); uf::renderer::render(); /* OpenVR */ if ( ext::openvr::context ) { ext::openvr::submit(); } } void EXT_API ext::terminate() { /* Kill threads */ { uf::thread::terminate(); } /* OpenVR */ if ( ext::openvr::context ) { ext::openvr::terminate(); } /* Close vulkan */ { uf::renderer::destroy(); } /* Flush input buffer */ { io.output << io.input << std::endl; for ( const auto& str : uf::iostream.getHistory() ) io.output << str << std::endl; io.output << "\nTerminated after " << times.sys.elapsed().asDouble() << " seconds" << std::endl; io.output.close(); } uf::scene::destroy(); /* Write persistent data */ if ( false ) { struct { bool exists = false; std::string filename = "./data/persistent.json"; } file; struct { uf::Serializer file; } config; /* Read from file */ { file.exists = config.file.readFromFile(file.filename); } /* Write persistent data */ { config.file.writeToFile(file.filename); } } } std::string EXT_API ext::getConfig() { struct { bool initialized = false; uf::Serializer file; uf::Serializer fallback; uf::Serializer merged; } static config; if ( config.initialized ) return config.merged; struct { bool exists = false; std::string filename = "./data/config.json"; } file; /* Read from file */ { file.exists = config.file.readFromFile(file.filename); } /* Initialize default configuration */ { config.fallback["window"]["terminal"]["ncurses"] = true; config.fallback["window"]["terminal"]["visible"] = true; config.fallback["window"]["title"] = "Grimgram"; config.fallback["window"]["icon"] = ""; config.fallback["window"]["size"]["x"] = 0; config.fallback["window"]["size"]["y"] = 0; config.fallback["window"]["visible"] = true; // config.fallback["window"]["fullscreen"] = false; config.fallback["window"]["mode"] = "windowed"; config.fallback["window"]["cursor"]["visible"] = true; config.fallback["window"]["cursor"]["center"] = false; config.fallback["window"]["keyboard"]["repeat"] = true; config.fallback["engine"]["scenes"]["start"] = "StartMenu"; config.fallback["engine"]["scenes"]["max lights"] = 32; config.fallback["engine"]["hook"]["mode"] = "Readable"; config.fallback["engine"]["frame limit"] = 60; config.fallback["engine"]["delta limit"] = 120; config.fallback["engine"]["worker threads"] = 1; config.fallback["engine"]["memory pool"]["size"] = "512 MiB"; config.fallback["engine"]["memory pool"]["globalOverride"] = false; config.fallback["engine"]["memory pool"]["subPools"] = true; } /* Merge */ if ( file.exists ){ config.merged = config.file; config.merged.merge( config.fallback, true ); } else { config.merged = config.fallback; } /* Write default to file */ if ( false ) { config.merged.writeToFile(file.filename); } config.initialized = true; return config.merged; }