diff options
Diffstat (limited to 'application')
-rw-r--r-- | application/main.cpp | 97 | ||||
-rw-r--r-- | application/resviewer.cpp | 227 |
2 files changed, 287 insertions, 37 deletions
diff --git a/application/main.cpp b/application/main.cpp index d58cf6d..a0c87c0 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -11,6 +11,7 @@ #include <game/network/link.h> #include <game/network/rail.h> #include <game/objective.h> +#include <game/objectives/freeroam.h> #include <game/objectives/goto.h> #include <game/orders.h> #include <game/scenary/foliage.h> @@ -24,72 +25,94 @@ #include <glm/glm.hpp> #include <glm/gtx/transform.hpp> // IWYU pragma: keep #include <memory> +#include <random> +#include <ranges> #include <special_members.h> +#include <stream_support.h> #include <ui/applicationBase.h> +#include <ui/builders/freeExtend.h> +#include <ui/builders/join.h> +#include <ui/builders/straight.h> #include <ui/gameMainWindow.h> #include <ui/window.h> -static const int DISPLAY_WIDTH = 1280; -static const int DISPLAY_HEIGHT = 1024; +constexpr ScreenAbsCoord DEFAULT_WINDOW_SIZE {1280, 1024}; class DummyMainApplication : public GameState, public MainApplication { public: int run() { - geoData = std::make_shared<GeoData>(GeoData::loadFromAsciiGrid("test/fixtures/height/SD19.asc")); + windows.create<MainWindow>(DEFAULT_WINDOW_SIZE, "I Like Trains")->setContent<GameMainWindow>(); - windows.create<MainWindow>(DISPLAY_WIDTH, DISPLAY_HEIGHT)->setContent<GameMainWindow>(); - - world.create<Terrain>(geoData); - world.create<Water>(geoData); + terrain = world.create<Terrain>(GeoData::loadFromAsciiGrid("test/fixtures/height/SD19.asc")); + world.create<Water>(terrain); assets = AssetFactory::loadAll("res"); { auto rl = world.create<RailLinks>(); - const GlobalPosition3D j {-1120000, -1100000, 3000}, k {-1100000, -1000000, 15000}, - l {-1000000, -800000, 20000}, m {-900000, -600000, 30000}, n {-600000, -500000, 32000}, - o {-500000, -800000, 30000}, p {-600000, -900000, 25000}, q {-1025000, -1175000, 10000}, - r {-925000, -1075000, 10000}, s {-1100000, -500000, 15000}, t {-1100000, -450000, 15000}, - u {-1000000, -400000, 15000}; - auto l3 = rl->addLinksBetween(j, k); - rl->addLinksBetween(k, l); - rl->addLinksBetween(l, m); - rl->addLinksBetween(m, n); - rl->addLinksBetween(n, o); - rl->addLinksBetween(o, p); - // branch 1 - rl->addLinksBetween(p, q); - rl->addLinksBetween(q, j); - // branch 2 - rl->addLinksBetween(p, r); - rl->addLinksBetween(r, j); - // early loop - rl->addLinksBetween(s, t); - rl->addLinksBetween(l, s); - rl->addLinksBetween(t, u); - rl->addLinksBetween(u, m); - const std::shared_ptr<Train> train = world.create<Train>(l3); - auto b47 = std::dynamic_pointer_cast<RailVehicleClass>(assets.at("brush-47")); + const auto nodes = materializeRange(std::vector<GlobalPosition2D> { + {315103000, 491067000}, + {315977000, 490777000}, + {316312000, 490557000}, + {316885000, 491330000}, + {316510934, 491255979}, + {316129566, 490893054}, + {315825622, 490833929}, + {315106182, 491073714}, + } + | std::views::transform([this](const auto n) { + return terrain->positionAt(n); + })); + auto l3 = BuilderStraight {}.create(rl.get(), terrain.get(), *nodes.begin(), *++nodes.begin()).front(); + for (const auto & [from, to] : nodes | std::views::drop(1) | std::views::pairwise) { + const auto links = BuilderFreeExtend {}.createExtend(rl.get(), terrain.get(), from, to); + } + for (const auto & [from, to] : std::initializer_list<std::pair<GlobalPosition2D, GlobalPosition2D>> { + {{315103000, 491067000}, {315003434, 491076253}}, + {{315103000, 491067000}, {315016495, 491019224}}, + {{315016495, 491019224}, {314955393, 490999023}}, + }) { + const auto links = BuilderFreeExtend {}.createExtend( + rl.get(), terrain.get(), terrain->positionAt(from), terrain->positionAt(to)); + } + for (const auto & [from, to] : std::initializer_list<std::pair<GlobalPosition2D, GlobalPosition2D>> { + {{315106182, 491073714}, {314955393, 490999023}}, + }) { + auto p1 = rl->intersectRayNodes({from || 0, up})->pos; + auto p2 = rl->intersectRayNodes({to || 0, up})->pos; + const auto links = BuilderFreeExtend {}.createJoin(rl.get(), terrain.get(), p1, p2); + } + + const std::shared_ptr<Train> train = world.create<Train>(l3, 800000); + auto b47 = assets.at("brush-47").dynamicCast<RailVehicleClass>(); for (int N = 0; N < 6; N++) { train->create<RailVehicle>(b47); } - train->orders.removeAll(); - train->orders.create<GoTo>( - &train->orders, l3->ends[1], l3->length, rl->findNodeAt({-1100000, -450000, 15000})); + train->orders.clear(); + train->orders.create<FreeRoam>(&train->orders); train->currentActivity = train->orders.current()->createActivity(); - auto foliage = std::dynamic_pointer_cast<Foliage>(assets.at("Tree-01-1")); + std::random_device randomdev {}; + std::uniform_real_distribution<Angle> rotationDistribution {0, two_pi}; + std::uniform_int_distribution<GlobalDistance> positionOffsetDistribution {-1500, +1500}; + std::uniform_int_distribution<int> treeDistribution {1, 3}; + std::uniform_int_distribution<int> treeVariantDistribution {1, 4}; for (auto x = 311000000; x < 311830000; x += 5000) { for (auto y = 491100000; y < 491130000; y += 5000) { - world.create<Plant>(foliage, Location {geoData->positionAt({{x, y}})}); + world.create<Plant>(assets.at(std::format("Tree-{:#02}-{}", treeDistribution(randomdev), + treeVariantDistribution(randomdev))) + .dynamicCast<Foliage>(), + Location {terrain->positionAt({{x + positionOffsetDistribution(randomdev), + y + positionOffsetDistribution(randomdev)}}), + {0, rotationDistribution(randomdev), 0}}); } } } mainLoop(); - world.objects.clear(); + world.clear(); return 0; } }; diff --git a/application/resviewer.cpp b/application/resviewer.cpp new file mode 100644 index 0000000..c82017b --- /dev/null +++ b/application/resviewer.cpp @@ -0,0 +1,227 @@ +#include <backends/imgui_impl_opengl3.h> +#include <backends/imgui_impl_sdl2.h> +#include <boost/program_options.hpp> +#include <game/environment.h> +#include <game/gamestate.h> +#include <game/terrain.h> +#include <gfx/gl/sceneProvider.h> +#include <gfx/gl/sceneRenderer.h> +#include <gfx/renderable.h> +#include <location.h> +#include <ui/applicationBase.h> +#include <ui/mainApplication.h> +#include <ui/mainWindow.h> + +constexpr ScreenAbsCoord DEFAULT_WINDOW_SIZE {800, 600}; +constexpr GlobalDistance TERRAIN_LIMIT = 1'000'000; +constexpr GlobalDistance TERRAIN_HEIGHT = 10'000; +constexpr RelativeDistance DEFAULT_CAMERA_DIST = 7'000; +constexpr GlobalDistance DEFAULT_CAMERA_HEIGHT = 5'000; +constexpr GlobalDistance DEFAULT_CAMERA_FOCUS = 3'000; +constexpr GlobalDistance MAX_CAMERA_HEIGHT = 10'000; +constexpr GlobalDistance MIN_CAMERA_DIST = 1'000; +constexpr GlobalDistance MAX_CAMERA_DIST = 30'000; + +class ViewerContent : public WindowContent, SceneRenderer, SceneProvider { +public: + ViewerContent(ScreenAbsCoord size, std::span<const std::filesystem::path> files) : + SceneRenderer {size, 0}, fileList(files) + { + camera.setPosition(calcCameraPosition()); + camera.lookAt({0, 0, TERRAIN_HEIGHT + cameraFocus}); + gameState->terrain = std::make_shared<Terrain>( + GeoData::createFlat({-TERRAIN_LIMIT, -TERRAIN_LIMIT}, {TERRAIN_LIMIT, TERRAIN_LIMIT}, TERRAIN_HEIGHT)); + } + +private: + [[nodiscard]] + GlobalPosition3D + calcCameraPosition() const + { + return {sincos(cameraAngle) * cameraDistance, TERRAIN_HEIGHT + cameraHeight}; + } + + void + render() override + { + SceneRenderer::render(*this); + controls(); + } + + void + tick(TickDuration tick) override + { + if (autoRotate != 0) { + cameraAngle = normalize(cameraAngle + (autoRotate * tick.count())); + camera.setPosition(calcCameraPosition()); + camera.lookAt({0, 0, TERRAIN_HEIGHT + cameraFocus}); + } + if (selectedFile) { + try { + if (const auto curmTime = std::filesystem::last_write_time(*selectedFile); curmTime != fileTime) { + location.reset(); + selectedAsset = nullptr; + gameState->assets = AssetFactory::loadXML(*selectedFile)->assets; + fileTime = curmTime; + if (!selectedAssetId.empty() && gameState->assets.contains(selectedAssetId)) { + auto asset = gameState->assets.at(selectedAssetId); + auto renderable = asset.getAs<const Renderable>(); + if (renderable) { + location = asset->createAt(position); + selectedAsset = renderable; + } + } + } + } + catch (...) { + } + } + } + + bool + handleInput(const SDL_Event & event) override + { + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) { + SceneRenderer::resize({event.window.data1, event.window.data2}); + } + + return WindowContent::handleInput(event); + } + + void + controls() + { + if (ImGui::Begin("Resource view")) { + ImGui::SetWindowSize({}); + fileSelection(); + assetSelection(); + } + ImGui::End(); + + if (ImGui::Begin("Camera")) { + ImGui::SetWindowSize({}); + if (std::max({ImGui::SliderFloat("Camera position", &cameraAngle, -pi, pi), + ImGui::SliderInt("Camera focus", &cameraFocus, 1, MAX_CAMERA_HEIGHT), + ImGui::SliderInt("Camera height", &cameraHeight, 1, MAX_CAMERA_HEIGHT), + ImGui::SliderFloat("Camera distance", &cameraDistance, MIN_CAMERA_DIST, MAX_CAMERA_DIST)})) { + camera.setPosition(calcCameraPosition()); + camera.lookAt({0, 0, TERRAIN_HEIGHT + cameraFocus}); + } + ImGui::SliderFloat("Auto rotate speed", &autoRotate, -1, 1); + } + ImGui::End(); + } + + void + fileSelection() + { + ImGui::BeginListBox("File"); + for (const auto & file : fileList) { + if (ImGui::Selectable(file.c_str(), &file == selectedFile)) { + location.reset(); + selectedAssetId.clear(); + selectedAsset = nullptr; + gameState->assets = AssetFactory::loadXML(file)->assets; + fileTime = std::filesystem::last_write_time(file); + selectedFile = &file; + } + } + ImGui::EndListBox(); + } + + void + assetSelection() + { + if (!gameState->assets.empty()) { + ImGui::BeginListBox("Asset"); + for (const auto & asset : gameState->assets) { + auto renderable = asset.second.getAs<const Renderable>(); + if (renderable) { + if (ImGui::Selectable(asset.first.c_str(), renderable == selectedAsset)) { + selectedAssetId = asset.first; + selectedAsset = renderable; + location = asset.second->createAt(position); + } + } + } + ImGui::EndListBox(); + } + } + + void + content(const SceneShader & sceneShader, const Frustum & frustum) const override + { + gameState->terrain->render(sceneShader, frustum); + if (selectedAsset) { + selectedAsset->render(sceneShader, frustum); + } + } + + void + lights(const SceneShader & sceneShader) const override + { + if (selectedAsset) { + selectedAsset->lights(sceneShader); + } + } + + void + shadows(const ShadowMapper & mapper, const Frustum & frustum) const override + { + gameState->terrain->shadows(mapper, frustum); + if (selectedAsset) { + selectedAsset->shadows(mapper, frustum); + } + } + + std::span<const std::filesystem::path> fileList; + std::filesystem::file_time_type fileTime; + const std::filesystem::path * selectedFile {}; + std::string selectedAssetId; + const Renderable * selectedAsset {}; + Location position {.pos = {0, 0, TERRAIN_HEIGHT}, .rot = {}}; + std::any location; + Angle cameraAngle {0.F}; + RelativeDistance cameraDistance {DEFAULT_CAMERA_DIST}; + GlobalDistance cameraHeight {DEFAULT_CAMERA_HEIGHT}; + GlobalDistance cameraFocus {DEFAULT_CAMERA_FOCUS}; + float autoRotate {0.F}; +}; + +int +main(int argc, char ** argv) +{ + class ResViewer : GameState, MainApplication { + public: + void + run(std::span<const std::filesystem::path> fileList) + { + windows.create<MainWindow>(DEFAULT_WINDOW_SIZE, "ILT - Resource Viewer") + ->setContent<ViewerContent>(fileList); + mainLoop(); + } + }; + + namespace po = boost::program_options; + po::options_description opts("ILT - Resource Viewer"); + std::vector<std::filesystem::path> resources; + // clang-format off + opts.add_options() + ("resource,r", po::value(&resources)->composing(), "Resource file") + ("help,h", po::value<bool>()->default_value(false)->zero_tokens(), "Help") + ; + // clang-format on + po::positional_options_description pod; + pod.add("resource", -1); + po::variables_map varmap; + po::store(po::command_line_parser(argc, argv).options(opts).positional(pod).run(), varmap); + po::notify(varmap); + + if (varmap.at("help").as<bool>()) { + std::cout << opts << '\n'; + return EXIT_SUCCESS; + } + + ResViewer {}.run(resources); + return EXIT_SUCCESS; +} |