Jelajahi Sumber

Merge pull request #3688 from vcmi/fix_penrose_tiling

Fix penrose tiling
DjWarmonger 1 tahun lalu
induk
melakukan
9214044f80
6 mengubah file dengan 78 tambahan dan 34 penghapusan
  1. 5 0
      lib/mapping/CMap.cpp
  2. 41 28
      lib/rmg/CZonePlacer.cpp
  3. 16 6
      lib/rmg/PenroseTiling.cpp
  4. 2 0
      lib/rmg/PenroseTiling.h
  5. 13 0
      lib/rmg/RmgMap.cpp
  6. 1 0
      lib/rmg/RmgMap.h

+ 5 - 0
lib/mapping/CMap.cpp

@@ -171,6 +171,7 @@ CMap::CMap(IGameCallback * cb)
 	, checksum(0)
 	, grailPos(-1, -1, -1)
 	, grailRadius(0)
+	, waterMap(false)
 	, uidCounter(0)
 {
 	allHeroes.resize(VLC->heroh->size());
@@ -605,6 +606,10 @@ bool CMap::calculateWaterContent()
 	{
 		waterMap = true;
 	}
+	else
+	{
+		waterMap = false;
+	}
 
 	return waterMap;
 }

+ 41 - 28
lib/rmg/CZonePlacer.cpp

@@ -873,6 +873,11 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
 	int levels = map.levels();
 
 	// Find current center of mass for each zone. Move zone to that center to balance zones sizes
+	std::vector<RmgMap::Zones> zonesOnLevel;
+	for(int level = 0; level < levels; level++)
+	{
+		zonesOnLevel.push_back(map.getZonesOnLevel(level));
+	}
 
 	int3 pos;
 
@@ -883,12 +888,9 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
 			for(pos.y = 0; pos.y < height; pos.y++)
 			{
 				distances.clear();
-				for(const auto & zone : zones)
+				for(const auto & zone : zonesOnLevel[pos.z])
 				{
-					if (zone.second->getPos().z == pos.z)
-						distances.emplace_back(zone.second, static_cast<float>(pos.dist2dSQ(zone.second->getPos())));
-					else
-						distances.emplace_back(zone.second, std::numeric_limits<float>::max());
+					distances.emplace_back(zone.second, static_cast<float>(pos.dist2dSQ(zone.second->getPos())));
 				}
 				boost::min_element(distances, compareByDistance)->first->area().add(pos); //closest tile belongs to zone
 			}
@@ -906,27 +908,30 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
 	for(const auto & zone : zones)
 		zone.second->clearTiles(); //now populate them again
 
-	// Assign zones to closest Penrose vertex
 	PenroseTiling penrose;
-	auto vertices = penrose.generatePenroseTiling(zones.size() / map.levels(), rand);
+	for (int level = 0; level < levels; level++)
+	{
+		//Create different tiling for each level
 
-	std::map<std::shared_ptr<Zone>, std::set<int3>> vertexMapping;
+		auto vertices = penrose.generatePenroseTiling(zonesOnLevel[level].size(), rand);
 
-	for (const auto & vertex : vertices)
-	{
-		distances.clear();
-		for(const auto & zone : zones)
+		// Assign zones to closest Penrose vertex
+		std::map<std::shared_ptr<Zone>, std::set<int3>> vertexMapping;
+
+		for (const auto & vertex : vertices)
 		{
-			distances.emplace_back(zone.second, zone.second->getCenter().dist2dSQ(float3(vertex.x(), vertex.y(), 0)));
-		}
-		auto closestZone = boost::min_element(distances, compareByDistance)->first;
+			distances.clear();
+			for(const auto & zone : zonesOnLevel[level])
+			{
+				distances.emplace_back(zone.second, zone.second->getCenter().dist2dSQ(float3(vertex.x(), vertex.y(), level)));
+			}
+			auto closestZone = boost::min_element(distances, compareByDistance)->first;
 
-		vertexMapping[closestZone].insert(int3(vertex.x() * width, vertex.y() * height, closestZone->getPos().z)); //Closest vertex belongs to zone
-	}
+			vertexMapping[closestZone].insert(int3(vertex.x() * width, vertex.y() * height, level)); //Closest vertex belongs to zone
+		}
 
-	//Assign actual tiles to each zone
-	for (pos.z = 0; pos.z < levels; pos.z++)
-	{
+		//Assign actual tiles to each zone
+		pos.z = level;
 		for (pos.x = 0; pos.x < width; pos.x++)
 		{
 			for (pos.y = 0; pos.y < height; pos.y++)
@@ -937,27 +942,35 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
 					auto zone = zoneVertex.first;
 					for (const auto & vertex : zoneVertex.second)
 					{
-						if (zone->getCenter().z == pos.z)
-							distances.emplace_back(zone, metric(pos, vertex));
-						else
-							distances.emplace_back(zone, std::numeric_limits<float>::max());
+						distances.emplace_back(zone, metric(pos, vertex));
 					}
 				}
 
-				//Tile closes to vertex belongs to zone
+				//Tile closest to vertex belongs to zone
 				auto closestZone = boost::min_element(distances, simpleCompareByDistance)->first;
 				closestZone->area().add(pos);
 				map.setZoneID(pos, closestZone->getId());
 			}
 		}
+
+		for(const auto & zone : zonesOnLevel[level])
+		{
+			if(zone.second->area().empty())
+			{
+				// FIXME: Some vertices are duplicated, but it's not a source of problem
+				logGlobal->error("Zone %d at %s is empty, dumping Penrose tiling", zone.second->getId(), zone.second->getCenter().toString());
+				for (const auto & vertex : vertices)
+				{
+					logGlobal->warn("Penrose Vertex: %s", vertex.toString());
+				}
+				throw rmgException("Empty zone after Penrose tiling");
+			}
+		}
 	}
 
 	//set position (town position) to center of mass of irregular zone
 	for(const auto & zone : zones)
 	{
-		if(zone.second->area().empty())
-			throw rmgException("Empty zone after Penrose tiling");
-
 		moveZoneToCenterOfMass(zone.second);
 
 		//TODO: similiar for islands

+ 16 - 6
lib/rmg/PenroseTiling.cpp

@@ -38,6 +38,16 @@ bool Point2D::operator < (const Point2D& other) const
 	}
 }
 
+std::string Point2D::toString() const
+{
+	//Performance is important here
+	std::string result = "(" +
+			std::to_string(this->x()) + " " +
+			std::to_string(this->y()) + ")";
+
+	return result;
+}
+
 Triangle::Triangle(bool t_123, const TIndices & inds):
 	tiling(t_123),
 	indices(inds)
@@ -57,14 +67,14 @@ Triangle::~Triangle()
 
 Point2D Point2D::rotated(float radians) const
 {
-    float cosAngle = cos(radians);
-    float sinAngle = sin(radians);
+	float cosAngle = cos(radians);
+	float sinAngle = sin(radians);
 
-    // Apply rotation matrix transformation
-    float newX = x() * cosAngle - y() * sinAngle;
-    float newY = x() * sinAngle + y() * cosAngle;
+	// Apply rotation matrix transformation
+	float newX = x() * cosAngle - y() * sinAngle;
+	float newY = x() * sinAngle + y() * cosAngle;
 
-    return Point2D(newX, newY);
+	return Point2D(newX, newY);
 }
 
 void PenroseTiling::split(Triangle& p, std::vector<Point2D>& points,

+ 2 - 0
lib/rmg/PenroseTiling.h

@@ -32,6 +32,8 @@ public:
 	Point2D rotated(float radians) const;
 
 	bool operator < (const Point2D& other) const;
+
+	std::string toString() const;
 };
 
 Point2D rotatePoint(const Point2D& point, double radians, const Point2D& origin);

+ 13 - 0
lib/rmg/RmgMap.cpp

@@ -239,6 +239,19 @@ RmgMap::Zones & RmgMap::getZones()
 	return zones;
 }
 
+RmgMap::Zones RmgMap::getZonesOnLevel(int level) const
+{
+	Zones zonesOnLevel;
+	for(const auto& zonePair : zones)
+	{
+		if(zonePair.second->isUnderground() == (bool)level)
+		{
+			zonesOnLevel.insert(zonePair);
+		}
+	}
+	return zonesOnLevel;
+}
+
 bool RmgMap::isBlocked(const int3 &tile) const
 {
 	assertOnMap(tile);

+ 1 - 0
lib/rmg/RmgMap.h

@@ -74,6 +74,7 @@ public:
 	using ZoneVector = std::vector<ZonePair>;
 	
 	Zones & getZones();
+	Zones getZonesOnLevel(int level) const;
 	
 	void registerZone(FactionID faction);
 	ui32 getZoneCount(FactionID faction);