summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2025-03-22 11:50:31 +0000
committerDan Goodliffe <dan@randomdan.homeip.net>2025-03-22 11:50:31 +0000
commit9fd25e8b10b1291525a18c8b3e34256ca6151dd6 (patch)
treec483b55010f310ed60cb7381130feedd2b3980a9
parentSupport resizing the main window (diff)
downloadilt-9fd25e8b10b1291525a18c8b3e34256ca6151dd6.tar.bz2
ilt-9fd25e8b10b1291525a18c8b3e34256ca6151dd6.tar.xz
ilt-9fd25e8b10b1291525a18c8b3e34256ca6151dd6.zip
Add ManyPtr which tracks specified subclasses
This removes the need to repeated dynamic_cast the pointer. Provides interface which enforces the fastest option for the required types.
-rw-r--r--application/main.cpp7
-rw-r--r--assetFactory/asset.h3
-rw-r--r--assetFactory/assetFactory.cpp3
-rw-r--r--assetFactory/assetFactory.h2
-rw-r--r--game/network/rail.cpp2
-rw-r--r--gfx/gl/shadowMapper.cpp2
-rw-r--r--lib/manyPtr.h86
-rw-r--r--lib/persistence.h3
-rw-r--r--test/test-assetFactory.cpp12
-rw-r--r--test/test-render.cpp13
-rw-r--r--ui/gameMainWindow.cpp12
11 files changed, 119 insertions, 26 deletions
diff --git a/application/main.cpp b/application/main.cpp
index 723f3d2..514fab6 100644
--- a/application/main.cpp
+++ b/application/main.cpp
@@ -86,7 +86,7 @@ public:
}
const std::shared_ptr<Train> train = world.create<Train>(l3, 800000);
- auto b47 = std::dynamic_pointer_cast<RailVehicleClass>(assets.at("brush-47"));
+ auto b47 = assets.at("brush-47").dynamicCast<RailVehicleClass>();
for (int N = 0; N < 6; N++) {
train->create<RailVehicle>(b47);
}
@@ -101,8 +101,9 @@ public:
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>(std::dynamic_pointer_cast<Foliage>(assets.at(std::format("Tree-{:#02}-{}",
- treeDistribution(randomdev), treeVariantDistribution(randomdev)))),
+ 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}});
diff --git a/assetFactory/asset.h b/assetFactory/asset.h
index 5bdd2f2..b5de056 100644
--- a/assetFactory/asset.h
+++ b/assetFactory/asset.h
@@ -2,12 +2,15 @@
#include "factoryMesh.h"
#include "persistence.h"
+#include <manyPtr.h>
#include <stdTypeDefs.h>
class TextureAtlas;
+class Renderable;
class Asset : public Persistence::Persistable, public StdTypeDefs<Asset> {
public:
+ using ManyPtr = ManySharedPtr<Asset, const Renderable>;
using TexturePtr = std::shared_ptr<TextureAtlas>;
std::string id;
diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp
index 176e1f5..426fecc 100644
--- a/assetFactory/assetFactory.cpp
+++ b/assetFactory/assetFactory.cpp
@@ -5,6 +5,7 @@
#include "filesystem.h"
#include "gfx/image.h"
#include "gfx/models/texture.h"
+#include "gfx/renderable.h"
#include "object.h"
#include "plane.h"
#include "saxParse-persistence.h"
@@ -146,7 +147,7 @@ bool
AssetFactory::persist(Persistence::PersistenceStore & store)
{
using MapObjects = Persistence::MapByMember<Shapes, std::shared_ptr<Object>>;
- using MapAssets = Persistence::MapByMember<Assets>;
+ using MapAssets = Persistence::MapByMember<Assets, Asset::Ptr>;
using MapTextureFragments = Persistence::MapByMember<TextureFragments>;
using MapAssImp = Persistence::MapByMember<AssImps, std::shared_ptr<AssImp>, &AssImp::path>;
return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects)
diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h
index 787f0a4..864e882 100644
--- a/assetFactory/assetFactory.h
+++ b/assetFactory/assetFactory.h
@@ -12,7 +12,7 @@ class Texture;
class AssetFactory : public Persistence::Persistable {
public:
using Shapes = std::map<std::string, Shape::Ptr, std::less<>>;
- using Assets = std::map<std::string, Asset::Ptr, std::less<>>;
+ using Assets = std::map<std::string, Asset::ManyPtr, std::less<>>;
using AssImps = std::map<std::string, AssImp::Ptr, std::less<>>;
using TextureFragments = std::map<std::string, TextureFragment::Ptr, std::less<>>;
using Colour = RGB;
diff --git a/game/network/rail.cpp b/game/network/rail.cpp
index 2a18b9a..5ce6036 100644
--- a/game/network/rail.cpp
+++ b/game/network/rail.cpp
@@ -182,7 +182,7 @@ RailLinks::render(const SceneShader & shader, const Frustum &) const
const Surface *
RailLinks::getBaseSurface() const
{
- return std::dynamic_pointer_cast<Surface>(gameState->assets.at("terrain.surface.gravel")).get();
+ return gameState->assets.at("terrain.surface.gravel").dynamicCast<const Surface>().get();
}
RelativeDistance
diff --git a/gfx/gl/shadowMapper.cpp b/gfx/gl/shadowMapper.cpp
index 6525f76..3cb73f7 100644
--- a/gfx/gl/shadowMapper.cpp
+++ b/gfx/gl/shadowMapper.cpp
@@ -88,7 +88,7 @@ ShadowMapper::update(const SceneProvider & scene, const LightDirection & dir, co
shadowStenciller.setLightDirection(dir);
for (const auto & [id, asset] : gameState->assets) {
- if (const auto r = std::dynamic_pointer_cast<const Renderable>(asset)) {
+ if (const auto r = asset.getAs<const Renderable>()) {
r->updateStencil(shadowStenciller);
}
}
diff --git a/lib/manyPtr.h b/lib/manyPtr.h
new file mode 100644
index 0000000..9e08452
--- /dev/null
+++ b/lib/manyPtr.h
@@ -0,0 +1,86 @@
+#pragma once
+
+#include <memory>
+#include <tuple>
+
+template<typename Primary, typename... Others> class ManyPtr : Primary {
+public:
+ using element_type = typename Primary::element_type;
+
+ template<typename... Params> ManyPtr(Params &&... params) : Primary {std::forward<Params>(params)...}
+ {
+ updatePtrs();
+ }
+
+ using Primary::operator->;
+ using Primary::operator*;
+ using Primary::operator bool;
+ using Primary::get;
+
+ template<typename... Params>
+ void
+ reset(Params &&... params)
+ {
+ Primary::reset(std::forward<Params>(params)...);
+ updatePtrs();
+ }
+
+ template<typename Other>
+ [[nodiscard]] consteval static bool
+ couldBe()
+ {
+ return (std::is_convertible_v<Others *, Other *> || ...);
+ }
+
+ template<typename Other>
+ requires(couldBe<Other>())
+ [[nodiscard]] auto
+ getAs() const
+ {
+ return std::get<idx<Other>()>(others);
+ }
+
+ template<typename Other>
+ requires(!couldBe<Other>() && requires { std::dynamic_pointer_cast<Other>(std::declval<Primary>()); })
+ [[nodiscard]] auto
+ dynamicCast() const
+ {
+ return std::dynamic_pointer_cast<Other>(*this);
+ }
+
+ template<typename Other>
+ requires(!couldBe<Other>() && !requires { std::dynamic_pointer_cast<Other>(std::declval<Primary>()); })
+ [[nodiscard]] auto
+ dynamicCast() const
+ {
+ return dynamic_cast<Other *>(get());
+ }
+
+private:
+ using OtherPtrs = std::tuple<Others *...>;
+
+ template<typename Other>
+ requires(couldBe<Other>())
+ [[nodiscard]] consteval static bool
+ idx()
+ {
+ size_t typeIdx = 0;
+ return ((typeIdx++ && std::is_convertible_v<Others *, Other *>) || ...);
+ }
+
+ void
+ updatePtrs()
+ {
+ if (*this) {
+ others = {dynamic_cast<Others *>(get())...};
+ }
+ else {
+ others = {};
+ }
+ }
+
+ OtherPtrs others;
+};
+
+template<typename Primary, typename... Others> using ManySharedPtr = ManyPtr<std::shared_ptr<Primary>, Others...>;
+template<typename Primary, typename... Others> using ManyUniquePtr = ManyPtr<std::unique_ptr<Primary>, Others...>;
diff --git a/lib/persistence.h b/lib/persistence.h
index e385b20..75bcea6 100644
--- a/lib/persistence.h
+++ b/lib/persistence.h
@@ -1,5 +1,6 @@
#pragma once
+#include "manyPtr.h"
#include <charconv>
#include <format>
#include <functional>
@@ -200,7 +201,7 @@ namespace Persistence {
}
[[nodiscard]] virtual NameActionSelection setName(const std::string_view key, SelectionFactory &&) = 0;
- virtual void selHandler() {};
+ virtual void selHandler() { };
virtual void setType(const std::string_view, const Persistable *) = 0;
SelectionPtr sel {};
diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp
index 9bade82..03319da 100644
--- a/test/test-assetFactory.cpp
+++ b/test/test-assetFactory.cpp
@@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(surfaces, *boost::unit_test::timeout(5))
BOOST_CHECK_EQUAL(4, mf->assets.size());
auto gravelAsset = mf->assets.at("terrain.surface.gravel");
BOOST_REQUIRE(gravelAsset);
- auto gravel = std::dynamic_pointer_cast<Surface>(gravelAsset);
+ auto gravel = gravelAsset.dynamicCast<Surface>();
BOOST_REQUIRE(gravel);
BOOST_REQUIRE_EQUAL(gravel->name, "Gravel");
BOOST_REQUIRE_EQUAL(gravel->colorBias, RGB {.9F});
@@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(brush47xml, *boost::unit_test::timeout(5))
BOOST_CHECK_EQUAL(1, mf->assets.size());
auto brush47 = mf->assets.at("brush-47");
BOOST_REQUIRE(brush47);
- auto brush47rvc = std::dynamic_pointer_cast<RailVehicleClass>(brush47);
+ auto brush47rvc = brush47.dynamicCast<RailVehicleClass>();
BOOST_REQUIRE(brush47rvc);
BOOST_REQUIRE(brush47rvc->bodyMesh);
BOOST_REQUIRE(brush47rvc->bogies.front());
@@ -130,7 +130,7 @@ BOOST_AUTO_TEST_CASE(foliage, *boost::unit_test::timeout(5))
gameState.assets = mf->assets;
auto tree_01_1 = mf->assets.at("Tree-01-1");
BOOST_REQUIRE(tree_01_1);
- auto tree_01_1_f = std::dynamic_pointer_cast<Foliage>(tree_01_1);
+ auto tree_01_1_f = tree_01_1.dynamicCast<Foliage>();
BOOST_REQUIRE(tree_01_1_f);
auto plant1 = std::make_shared<Plant>(tree_01_1_f, Location {{-2000, 2000, 0}, {0, 0, 0}});
@@ -151,9 +151,9 @@ BOOST_AUTO_TEST_CASE(lights, *boost::unit_test::timeout(5))
BOOST_REQUIRE(rlight);
auto oldlamp = mf->assets.at("old-lamp");
BOOST_REQUIRE(oldlamp);
- auto rlight_f = std::dynamic_pointer_cast<Illuminator>(rlight);
+ auto rlight_f = rlight.dynamicCast<Illuminator>();
BOOST_REQUIRE(rlight_f);
- auto oldlamp_f = std::dynamic_pointer_cast<Illuminator>(oldlamp);
+ auto oldlamp_f = oldlamp.dynamicCast<Illuminator>();
BOOST_REQUIRE(oldlamp_f);
auto light1 = std::make_shared<Light>(oldlamp_f, Location {{0, 0, 0}, {0, 0, 0}});
@@ -164,7 +164,7 @@ BOOST_AUTO_TEST_CASE(lights, *boost::unit_test::timeout(5))
objects.objects.push_back(oldlamp_f);
// yes I'm hacking some floor to light up as though its a bush
- auto floorf = std::dynamic_pointer_cast<Foliage>(mf->assets.at("floor"));
+ auto floorf = mf->assets.at("floor").dynamicCast<Foliage>();
auto floor = std::make_shared<Plant>(floorf, Location {});
objects.objects.push_back(floorf);
diff --git a/test/test-render.cpp b/test/test-render.cpp
index 8390d25..a6e28bc 100644
--- a/test/test-render.cpp
+++ b/test/test-render.cpp
@@ -41,7 +41,7 @@ public:
terrain->point(GeoData::VertexHandle {517}).z = 100'000;
terrain->generateMeshes();
gameState->assets = AssetFactory::loadAll(RESDIR);
- brush47rvc = std::dynamic_pointer_cast<RailVehicleClass>(gameState->assets.at("brush-47"));
+ brush47rvc = gameState->assets.at("brush-47").dynamicCast<RailVehicleClass>();
std::random_device randomdev {};
std::uniform_real_distribution<Angle> rotationDistribution {0, two_pi};
std::uniform_int_distribution<GlobalDistance> positionOffsetDistribution {-1500, +1500};
@@ -57,9 +57,10 @@ public:
train2->bogies.back().setPosition(train2->bogies.back().position() + train2->location.position());
for (auto x = 40000; x < 100000; x += 5000) {
for (auto y = 65000; y < 125000; y += 5000) {
- gameState->world.create<Plant>(
- std::dynamic_pointer_cast<Foliage>(gameState->assets.at(std::format(
- "Tree-{:#02}-{}", treeDistribution(randomdev), treeVariantDistribution(randomdev)))),
+ gameState->world.create<Plant>(gameState->assets
+ .at(std::format("Tree-{:#02}-{}", treeDistribution(randomdev),
+ treeVariantDistribution(randomdev)))
+ .dynamicCast<Foliage>(),
Location {{x + positionOffsetDistribution(randomdev), y + positionOffsetDistribution(randomdev),
1},
{0, rotationDistribution(randomdev), 0}});
@@ -76,7 +77,7 @@ public:
water.render(shader, frustum);
rail.render(shader, frustum);
std::ranges::for_each(gameState->assets, [&shader, &frustum](const auto & asset) {
- if (const auto renderable = std::dynamic_pointer_cast<const Renderable>(asset.second)) {
+ if (const auto renderable = asset.second.template getAs<const Renderable>()) {
renderable->render(shader, frustum);
}
});
@@ -98,7 +99,7 @@ public:
{
terrain->shadows(shadowMapper, frustum);
std::ranges::for_each(gameState->assets, [&shadowMapper, &frustum](const auto & asset) {
- if (const auto renderable = std::dynamic_pointer_cast<const Renderable>(asset.second)) {
+ if (const auto renderable = asset.second.template getAs<const Renderable>()) {
renderable->shadows(shadowMapper, frustum);
}
});
diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp
index d88bab5..b58f3dc 100644
--- a/ui/gameMainWindow.cpp
+++ b/ui/gameMainWindow.cpp
@@ -75,9 +75,9 @@ GameMainWindow::render() const
void
GameMainWindow::content(const SceneShader & shader, const Frustum & frustum) const
{
- for (const auto & [id, asset] : gameState->assets) {
- if (const auto r = std::dynamic_pointer_cast<const Renderable>(asset)) {
- r->render(shader, frustum);
+ for (const auto & [assetId, asset] : gameState->assets) {
+ if (const auto renderable = asset.getAs<const Renderable>()) {
+ renderable->render(shader, frustum);
}
}
gameState->world.apply<Renderable>(&Renderable::render, shader, frustum);
@@ -99,9 +99,9 @@ GameMainWindow::lights(const SceneShader & shader) const
void
GameMainWindow::shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const
{
- for (const auto & [id, asset] : gameState->assets) {
- if (const auto r = std::dynamic_pointer_cast<const Renderable>(asset)) {
- r->shadows(shadowMapper, frustum);
+ for (const auto & [assetId, asset] : gameState->assets) {
+ if (const auto renderable = asset.getAs<const Renderable>()) {
+ renderable->shadows(shadowMapper, frustum);
}
}
gameState->world.apply<Renderable>(&Renderable::shadows, shadowMapper, frustum);