mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Refactoring of MapView
This commit is contained in:
		| @@ -57,7 +57,7 @@ public: | ||||
| 	virtual uint32_t getAnimationTime() const = 0; | ||||
|  | ||||
| 	/// returns size of ouput tile, in pixels. 32x32 for "standard" map, may be smaller for world view mode | ||||
| 	virtual Point tileSize() const = 0; | ||||
| 	virtual Point getTileSize() const = 0; | ||||
|  | ||||
| 	/// if true, map grid should be visible on map | ||||
| 	virtual bool showGrid() const = 0; | ||||
|   | ||||
| @@ -43,55 +43,17 @@ | ||||
|  | ||||
| MapCache::~MapCache() = default; | ||||
|  | ||||
| MapCache::MapCache(const Point & tileSize, const Point & dimensions) | ||||
| 	: tileSize(tileSize) | ||||
| MapCache::MapCache(const std::shared_ptr<MapViewModel> & model) | ||||
| 	: model(model) | ||||
| 	, context(new MapRendererContext()) | ||||
| 	, mapRenderer(new MapRenderer(*context)) | ||||
| 	, targetDimensionsPixels(dimensions) | ||||
| 	, viewCenter(0, 0) | ||||
| 	, mapLevel(0) | ||||
| { | ||||
| 	// total number of potentially visible tiles is: | ||||
| 	// 1) number of completely visible tiles | ||||
| 	// 2) additional tile that might be partially visible from left/top size | ||||
| 	// 3) additional tile that might be partially visible from right/bottom size | ||||
| 	Point visibleTiles{ | ||||
| 		dimensions.x / tileSize.x + 2, | ||||
| 		dimensions.y / tileSize.y + 2, | ||||
| 	}; | ||||
|  | ||||
| 	viewDimensionsTiles = visibleTiles; | ||||
| 	viewDimensionsPixels = visibleTiles * tileSize; | ||||
|  | ||||
| 	terrain = std::make_unique<Canvas>(viewDimensionsPixels); | ||||
| } | ||||
|  | ||||
| void MapCache::setViewCenter(const Point & center, int newLevel) | ||||
| { | ||||
| 	viewCenter = center; | ||||
| 	mapLevel = newLevel; | ||||
|  | ||||
| 	int3 mapSize = LOCPLINT->cb->getMapSize(); | ||||
| 	Point viewMax = Point(mapSize) * tileSize; | ||||
|  | ||||
| 	vstd::abetween(viewCenter.x, 0, viewMax.x); | ||||
| 	vstd::abetween(viewCenter.y, 0, viewMax.y); | ||||
| 	terrain = std::make_unique<Canvas>(model->getCacheDimensionsPixels()); | ||||
| } | ||||
|  | ||||
| Canvas MapCache::getTile(const int3 & coordinates) | ||||
| { | ||||
| 	assert(mapLevel == coordinates.z); | ||||
| 	assert(viewDimensionsTiles.x + coordinates.x >= 0); | ||||
| 	assert(viewDimensionsTiles.y + coordinates.y >= 0); | ||||
|  | ||||
| 	Point tileIndex{ | ||||
| 		(viewDimensionsTiles.x + coordinates.x) % viewDimensionsTiles.x, | ||||
| 		(viewDimensionsTiles.y + coordinates.y) % viewDimensionsTiles.y | ||||
| 	}; | ||||
|  | ||||
| 	Rect terrainSection(tileIndex * tileSize, tileSize); | ||||
|  | ||||
| 	return Canvas(*terrain, terrainSection); | ||||
| 	return Canvas(*terrain, model->getCacheTileArea(coordinates)); | ||||
| } | ||||
|  | ||||
| void MapCache::updateTile(const int3 & coordinates) | ||||
| @@ -101,120 +63,56 @@ void MapCache::updateTile(const int3 & coordinates) | ||||
| 	mapRenderer->renderTile(*context, target, coordinates); | ||||
| } | ||||
|  | ||||
| Rect MapCache::getVisibleAreaTiles() const | ||||
| { | ||||
| 	Rect visibleAreaPixels = { | ||||
| 		viewCenter.x - targetDimensionsPixels.x / 2, | ||||
| 		viewCenter.y - targetDimensionsPixels.y / 2, | ||||
| 		targetDimensionsPixels.x, | ||||
| 		targetDimensionsPixels.y | ||||
| 	}; | ||||
|  | ||||
| 	// NOTE: use division via double in order to use floor (which rounds to negative infinity and not towards zero) | ||||
| 	Point topLeftTile{ | ||||
| 		static_cast<int>(std::floor(static_cast<double>(visibleAreaPixels.left()) / tileSize.x)), | ||||
| 		static_cast<int>(std::floor(static_cast<double>(visibleAreaPixels.top()) / tileSize.y)), | ||||
| 	}; | ||||
|  | ||||
| 	Point bottomRightTile{ | ||||
| 		visibleAreaPixels.right() / tileSize.x, | ||||
| 		visibleAreaPixels.bottom() / tileSize.y | ||||
| 	}; | ||||
|  | ||||
| 	return Rect(topLeftTile, bottomRightTile - topLeftTile + Point(1, 1)); | ||||
| } | ||||
|  | ||||
| int3 MapCache::getTileAtPoint(const Point & position) const | ||||
| { | ||||
| 	Point topLeftOffset = viewCenter - targetDimensionsPixels / 2; | ||||
|  | ||||
| 	Point absolutePosition = position + topLeftOffset; | ||||
|  | ||||
| 	return { | ||||
| 		static_cast<int>(std::floor(static_cast<double>(absolutePosition.x) / tileSize.x)), | ||||
| 		static_cast<int>(std::floor(static_cast<double>(absolutePosition.y) / tileSize.y)), | ||||
| 		mapLevel | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| int3 MapCache::getTileCenter() const | ||||
| { | ||||
| 	return getTileAtPoint(getViewCenter()); | ||||
| } | ||||
|  | ||||
| Point MapCache::getViewCenter() const | ||||
| { | ||||
| 	return viewCenter; | ||||
| } | ||||
|  | ||||
| void MapCache::update(uint32_t timeDelta) | ||||
| { | ||||
| 	context->advanceAnimations(timeDelta); | ||||
| 	context->setTileSize(model->getSingleTileSize()); | ||||
|  | ||||
| 	Rect dimensions = getVisibleAreaTiles(); | ||||
| 	Rect dimensions = model->getTilesTotalRect(); | ||||
|  | ||||
| 	for(int y = dimensions.top(); y < dimensions.bottom(); ++y) | ||||
| 		for(int x = dimensions.left(); x < dimensions.right(); ++x) | ||||
| 			updateTile({x, y, mapLevel}); | ||||
| 			updateTile({x, y, model->getLevel()}); | ||||
| } | ||||
|  | ||||
| void MapCache::render(Canvas & target) | ||||
| { | ||||
| 	update(GH.mainFPSmng->getElapsedMilliseconds()); | ||||
|  | ||||
| 	Rect dimensions = getVisibleAreaTiles(); | ||||
| 	Rect dimensions = model->getTilesTotalRect(); | ||||
|  | ||||
| 	for(int y = dimensions.top(); y < dimensions.bottom(); ++y) | ||||
| 	{ | ||||
| 		for(int x = dimensions.left(); x < dimensions.right(); ++x) | ||||
| 		{ | ||||
| 			Point topLeftOffset = viewCenter - targetDimensionsPixels / 2; | ||||
| 			Point tilePosAbsolute = Point(x, y) * tileSize; | ||||
| 			Point tilePosRelative = tilePosAbsolute - topLeftOffset; | ||||
|  | ||||
| 			Canvas source = getTile(int3(x, y, mapLevel)); | ||||
| 			target.draw(source, tilePosRelative); | ||||
| 			int3 tile( x,y,model->getLevel()); | ||||
| 			Canvas source = getTile(tile); | ||||
| 			Rect targetRect = model->getTargetTileArea(tile); | ||||
| 			target.draw(source, targetRect.topLeft()); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| std::shared_ptr<MapViewModel> MapView::createModel(const Point & dimensions) const | ||||
| { | ||||
| 	auto result = std::make_shared<MapViewModel>(); | ||||
|  | ||||
| 	result->setLevel(0); | ||||
| 	result->setTileSize(Point(32,32)); | ||||
| 	result->setViewCenter(Point(0,0)); | ||||
| 	result->setViewDimensions(dimensions); | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| MapView::MapView(const Point & offset, const Point & dimensions) | ||||
| 	: tilesCache(new MapCache(Point(32, 32), dimensions)) | ||||
| 	, tileSize(Point(32, 32)) | ||||
| 	: model(createModel(dimensions)) | ||||
| 	, tilesCache(new MapCache(model)) | ||||
| { | ||||
| 	pos += offset; | ||||
| 	pos.w = dimensions.x; | ||||
| 	pos.h = dimensions.y; | ||||
| } | ||||
|  | ||||
| Rect MapView::getVisibleAreaTiles() const | ||||
| { | ||||
| 	return tilesCache->getVisibleAreaTiles(); | ||||
| } | ||||
|  | ||||
| int3 MapView::getTileCenter() const | ||||
| { | ||||
| 	return tilesCache->getTileCenter(); | ||||
| } | ||||
|  | ||||
| int3 MapView::getTileAtPoint(const Point & position) const | ||||
| { | ||||
| 	return tilesCache->getTileAtPoint(position); | ||||
| } | ||||
|  | ||||
| Point MapView::getViewCenter() const | ||||
| { | ||||
| 	return tilesCache->getViewCenter(); | ||||
| } | ||||
|  | ||||
| void MapView::setViewCenter(const int3 & position) | ||||
| { | ||||
| 	setViewCenter(Point(position.x, position.y) * tileSize, position.z); | ||||
| } | ||||
|  | ||||
| void MapView::setViewCenter(const Point & position, int level) | ||||
| { | ||||
| 	tilesCache->setViewCenter(position, level); | ||||
| } | ||||
|  | ||||
| void MapView::show(SDL_Surface * to) | ||||
| @@ -267,6 +165,11 @@ bool MapRendererContext::isVisible(const int3 & coordinates) const | ||||
| 	return LOCPLINT->cb->isVisible(coordinates) || settings["session"]["spectate"].Bool(); | ||||
| } | ||||
|  | ||||
| void MapRendererContext::setTileSize(const Point & dimensions) | ||||
| { | ||||
| 	tileSize = dimensions; | ||||
| } | ||||
|  | ||||
| const CGPath * MapRendererContext::currentPath() const | ||||
| { | ||||
| 	const auto * hero = adventureInt->curHero(); | ||||
| @@ -283,7 +186,7 @@ const CGPath * MapRendererContext::currentPath() const | ||||
| uint32_t MapRendererContext::getAnimationPeriod() const | ||||
| { | ||||
| 	// H3 timing for adventure map objects animation is 180 ms | ||||
| 	// Terrain animations also use identical interval, however it is only present in HotA and/or HD Mod | ||||
| 	// Terrain animations also use identical interval, however those are only present in HotA and/or HD Mod | ||||
| 	// TODO: duration of fade-in/fade-out for teleport, entering/leaving boat, removal of objects | ||||
| 	// TOOD: duration of hero movement animation, frame timing of hero movement animation, effect of hero speed option | ||||
| 	// TOOD: duration of enemy hero movement animation, frame timing of enemy hero movement animation, effect of enemy hero speed option | ||||
| @@ -295,7 +198,7 @@ uint32_t MapRendererContext::getAnimationTime() const | ||||
| 	return animationTime; | ||||
| } | ||||
|  | ||||
| Point MapRendererContext::tileSize() const | ||||
| Point MapRendererContext::getTileSize() const | ||||
| { | ||||
| 	return Point(32, 32); | ||||
| } | ||||
| @@ -304,3 +207,136 @@ bool MapRendererContext::showGrid() const | ||||
| { | ||||
| 	return true; // settings["session"]["showGrid"].Bool(); | ||||
| } | ||||
|  | ||||
| void MapView::setViewCenter(const int3 & position) | ||||
| { | ||||
| 	model->setViewCenter(Point(position.x, position.y) * model->getSingleTileSize()); | ||||
| 	model->setLevel(position.z); | ||||
| } | ||||
|  | ||||
| void MapView::setViewCenter(const Point & position, int level) | ||||
| { | ||||
| 	model->setViewCenter(position); | ||||
| 	model->setLevel(level); | ||||
| } | ||||
|  | ||||
| void MapView::setTileSize(const Point & tileSize) | ||||
| { | ||||
| 	model->setTileSize(tileSize); | ||||
| } | ||||
|  | ||||
| std::shared_ptr<const MapViewModel> MapView::getModel() const | ||||
| { | ||||
| 	return model; | ||||
| } | ||||
|  | ||||
| void MapViewModel::setTileSize(const Point & newValue) | ||||
| { | ||||
| 	tileSize = newValue; | ||||
| } | ||||
|  | ||||
| void MapViewModel::setViewCenter(const Point & newValue) | ||||
| { | ||||
| 	viewCenter = newValue; | ||||
| } | ||||
|  | ||||
| void MapViewModel::setViewDimensions(const Point & newValue) | ||||
| { | ||||
| 	viewDimensions = newValue; | ||||
| } | ||||
|  | ||||
| void MapViewModel::setLevel(int newLevel) | ||||
| { | ||||
| 	mapLevel = newLevel; | ||||
| } | ||||
|  | ||||
| Point MapViewModel::getSingleTileSize() const | ||||
| { | ||||
| 	return tileSize; | ||||
| } | ||||
|  | ||||
| Point MapViewModel::getMapViewCenter() const | ||||
| { | ||||
| 	return viewCenter; | ||||
| } | ||||
|  | ||||
| Point MapViewModel::getPixelsVisibleDimensions() const | ||||
| { | ||||
| 	return viewDimensions; | ||||
| } | ||||
|  | ||||
| int MapViewModel::getLevel() const | ||||
| { | ||||
| 	return mapLevel; | ||||
| } | ||||
|  | ||||
| Point MapViewModel::getTilesVisibleDimensions() const | ||||
| { | ||||
| 	// total number of potentially visible tiles is: | ||||
| 	// 1) number of completely visible tiles | ||||
| 	// 2) additional tile that might be partially visible from left/top size | ||||
| 	// 3) additional tile that might be partially visible from right/bottom size | ||||
| 	return { | ||||
| 		getPixelsVisibleDimensions().x / getSingleTileSize().x + 2, | ||||
| 		getPixelsVisibleDimensions().y / getSingleTileSize().y + 2, | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| Rect MapViewModel::getTilesTotalRect() const | ||||
| { | ||||
| 	return Rect( | ||||
| 		Point(getTileAtPoint(Point(0,0))), | ||||
| 		getTilesVisibleDimensions() | ||||
| 	); | ||||
| } | ||||
|  | ||||
| int3 MapViewModel::getTileCenter() const | ||||
| { | ||||
| 	return getTileAtPoint(getMapViewCenter()); | ||||
| } | ||||
|  | ||||
| int3 MapViewModel::getTileAtPoint(const Point & position) const | ||||
| { | ||||
| 	Point topLeftOffset = getMapViewCenter() - getPixelsVisibleDimensions() / 2; | ||||
|  | ||||
| 	Point absolutePosition = position + topLeftOffset; | ||||
|  | ||||
| 	// NOTE: using division via double in order to use std::floor | ||||
| 	// which rounds to negative infinity and not towards zero (like integer division) | ||||
| 	return { | ||||
| 		static_cast<int>(std::floor(static_cast<double>(absolutePosition.x) / getSingleTileSize().x)), | ||||
| 		static_cast<int>(std::floor(static_cast<double>(absolutePosition.y) / getSingleTileSize().y)), | ||||
| 		getLevel() | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| Point MapViewModel::getCacheDimensionsPixels() const | ||||
| { | ||||
| 	return getTilesVisibleDimensions() * getSingleTileSize(); | ||||
| } | ||||
|  | ||||
| Rect MapViewModel::getCacheTileArea(const int3 & coordinates) const | ||||
| { | ||||
| 	assert(mapLevel == coordinates.z); | ||||
| 	assert(getTilesVisibleDimensions().x + coordinates.x >= 0); | ||||
| 	assert(getTilesVisibleDimensions().y + coordinates.y >= 0); | ||||
|  | ||||
| 	Point tileIndex{ | ||||
| 		(getTilesVisibleDimensions().x + coordinates.x) % getTilesVisibleDimensions().x, | ||||
| 		(getTilesVisibleDimensions().y + coordinates.y) % getTilesVisibleDimensions().y | ||||
| 	}; | ||||
|  | ||||
| 	return Rect(tileIndex * tileSize, tileSize); | ||||
| } | ||||
|  | ||||
| Rect MapViewModel::getTargetTileArea(const int3 & coordinates) const | ||||
| { | ||||
| 	Point topLeftOffset = getMapViewCenter() - getPixelsVisibleDimensions() / 2; | ||||
| 	Point tilePosAbsolute = Point(coordinates) * tileSize; | ||||
| 	Point tilePosRelative = tilePosAbsolute - topLeftOffset; | ||||
|  | ||||
| 	return { | ||||
| 		tilePosRelative, | ||||
| 		tileSize | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -18,10 +18,12 @@ class MapRenderer; | ||||
|  | ||||
| class MapRendererContext : public IMapRendererContext | ||||
| { | ||||
| 	Point tileSize = Point(32,32); | ||||
| 	uint32_t animationTime = 0; | ||||
|  | ||||
| public: | ||||
| 	void advanceAnimations(uint32_t ms); | ||||
| 	void setTileSize(const Point & dimensions); | ||||
|  | ||||
| 	int3 getMapSize() const override; | ||||
| 	bool isInMap(const int3 & coordinates) const override; | ||||
| @@ -36,61 +38,88 @@ public: | ||||
|  | ||||
| 	uint32_t getAnimationPeriod() const override; | ||||
| 	uint32_t getAnimationTime() const override; | ||||
| 	Point tileSize() const override; | ||||
| 	Point getTileSize() const override; | ||||
|  | ||||
| 	bool showGrid() const override; | ||||
| }; | ||||
|  | ||||
| class MapViewModel | ||||
| { | ||||
| 	Point tileSize; | ||||
| 	Point viewCenter; | ||||
| 	Point viewDimensions; | ||||
|  | ||||
| 	int mapLevel = 0; | ||||
| public: | ||||
| 	void setTileSize(Point const & newValue); | ||||
| 	void setViewCenter(Point const & newValue); | ||||
| 	void setViewDimensions(Point const & newValue); | ||||
| 	void setLevel(int newLevel); | ||||
|  | ||||
| 	/// returns current size of map tile in pixels | ||||
| 	Point getSingleTileSize() const; | ||||
|  | ||||
| 	/// returns center point of map view, in Map coordinates | ||||
| 	Point getMapViewCenter() const; | ||||
|  | ||||
| 	/// returns total number of visible tiles | ||||
| 	Point getTilesVisibleDimensions() const; | ||||
|  | ||||
| 	/// returns rect encompassing all visible tiles | ||||
| 	Rect getTilesTotalRect() const; | ||||
|  | ||||
| 	/// returns required area in pixels of cache canvas | ||||
| 	Point getCacheDimensionsPixels() const; | ||||
|  | ||||
| 	/// returns actual player-visible area | ||||
| 	Point getPixelsVisibleDimensions() const; | ||||
|  | ||||
| 	/// returns area covered by specified tile in map cache | ||||
| 	Rect getCacheTileArea(const int3 & coordinates) const; | ||||
|  | ||||
| 	/// returns area covered by specified tile in target view | ||||
| 	Rect getTargetTileArea(const int3 & coordinates) const; | ||||
|  | ||||
| 	int getLevel() const; | ||||
| 	int3 getTileCenter() const; | ||||
|  | ||||
| 	/// returns tile under specified position in target view | ||||
| 	int3 getTileAtPoint(const Point & position) const; | ||||
| }; | ||||
|  | ||||
| class MapCache | ||||
| { | ||||
| 	std::unique_ptr<Canvas> terrain; | ||||
|  | ||||
| 	Point tileSize; | ||||
| 	Point viewCenter; | ||||
| 	Point viewDimensionsTiles; | ||||
| 	Point viewDimensionsPixels; | ||||
| 	Point targetDimensionsPixels; | ||||
|  | ||||
| 	int mapLevel; | ||||
| 	std::shared_ptr<MapViewModel> model; | ||||
|  | ||||
| 	std::unique_ptr<MapRendererContext> context; | ||||
| 	std::unique_ptr<MapRenderer> mapRenderer; | ||||
|  | ||||
| 	Canvas getTile(const int3 & coordinates); | ||||
|  | ||||
| 	void updateTile(const int3 & coordinates); | ||||
|  | ||||
| public: | ||||
| 	explicit MapCache(const Point & tileSize, const Point & dimensions); | ||||
| 	explicit MapCache(const std::shared_ptr<MapViewModel> & model); | ||||
| 	~MapCache(); | ||||
|  | ||||
| 	void setViewCenter(const Point & center, int level); | ||||
|  | ||||
| 	Rect getVisibleAreaTiles() const; | ||||
| 	int3 getTileCenter() const; | ||||
| 	int3 getTileAtPoint(const Point & position) const; | ||||
| 	Point getViewCenter() const; | ||||
|  | ||||
| 	void update(uint32_t timeDelta); | ||||
| 	void render(Canvas & target); | ||||
| }; | ||||
|  | ||||
| class MapView : public CIntObject | ||||
| { | ||||
| 	std::shared_ptr<MapViewModel> model; | ||||
| 	std::unique_ptr<MapCache> tilesCache; | ||||
|  | ||||
| 	Point tileSize; | ||||
|  | ||||
| 	std::shared_ptr<MapViewModel> createModel(const Point & dimensions) const; | ||||
| public: | ||||
| 	MapView(const Point & offset, const Point & dimensions); | ||||
| 	std::shared_ptr<const MapViewModel> getModel() const; | ||||
|  | ||||
| 	Rect getVisibleAreaTiles() const; | ||||
| 	Point getViewCenter() const; | ||||
| 	int3 getTileCenter() const; | ||||
| 	int3 getTileAtPoint(const Point & position) const; | ||||
| 	MapView(const Point & offset, const Point & dimensions); | ||||
|  | ||||
| 	void setViewCenter(const int3 & position); | ||||
| 	void setViewCenter(const Point & position, int level); | ||||
| 	void setTileSize(const Point & tileSize); | ||||
|  | ||||
| 	void show(SDL_Surface * to) override; | ||||
| 	void showAll(SDL_Surface * to) override; | ||||
|   | ||||
| @@ -16,7 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| Point::Point(const int3 & a) | ||||
| 	: x(a.x) | ||||
| 	, y(a.x) | ||||
| 	, y(a.y) | ||||
| { | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user