diff options
| author | Dan Goodliffe <dan@randomdan.homeip.net> | 2025-03-22 11:50:31 +0000 | 
|---|---|---|
| committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2025-03-22 11:50:31 +0000 | 
| commit | 9fd25e8b10b1291525a18c8b3e34256ca6151dd6 (patch) | |
| tree | c483b55010f310ed60cb7381130feedd2b3980a9 | |
| parent | Support resizing the main window (diff) | |
| download | ilt-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.cpp | 7 | ||||
| -rw-r--r-- | assetFactory/asset.h | 3 | ||||
| -rw-r--r-- | assetFactory/assetFactory.cpp | 3 | ||||
| -rw-r--r-- | assetFactory/assetFactory.h | 2 | ||||
| -rw-r--r-- | game/network/rail.cpp | 2 | ||||
| -rw-r--r-- | gfx/gl/shadowMapper.cpp | 2 | ||||
| -rw-r--r-- | lib/manyPtr.h | 86 | ||||
| -rw-r--r-- | lib/persistence.h | 3 | ||||
| -rw-r--r-- | test/test-assetFactory.cpp | 12 | ||||
| -rw-r--r-- | test/test-render.cpp | 13 | ||||
| -rw-r--r-- | ui/gameMainWindow.cpp | 12 | 
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); | 
