summaryrefslogtreecommitdiff
path: root/application
diff options
context:
space:
mode:
Diffstat (limited to 'application')
-rw-r--r--application/main.cpp97
-rw-r--r--application/resviewer.cpp227
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;
+}