Example #1
0
std::unique_ptr<Map> MapLoader::Load(const std::string& path)
{
	Tmx::Map* map = new Tmx::Map();
	map->ParseFile(RESOURCE_FOLDER + path);

	auto ThrowError = [&]() {
		Logger::Log("Unable to load map: \"" + path + "\"", licesium::Logger::Error);
		throw map->GetErrorCode();
	};

	if (map->HasError() || map->GetOrientation() != Tmx::TMX_MO_ISOMETRIC)
	{
		ThrowError();
	}

	auto resultMap = std::unique_ptr<Map>(new Map(map->GetFilename()));

	size_t tileWidth = static_cast<size_t>(map->GetTileWidth());
	size_t tileHeight = static_cast<size_t>(map->GetTileHeight());
	float tileRatio = static_cast<float>(tileWidth) / static_cast<float>(tileHeight);

	resultMap->m_mapSize = sf::Vector2u(map->GetWidth(), map->GetHeight());
	resultMap->m_tileSize = sf::Vector2u(tileWidth, tileHeight);
	resultMap->m_tileRatio = tileRatio;
	resultMap->m_renderOrder = map->GetRenderOrder();
	
	for (int i = 0; i < map->GetNumTilesets(); ++i)
	{
		const Tmx::Tileset* tileset = map->GetTileset(i);

		std::unique_ptr<sf::Texture> tilesetTexture(new sf::Texture);
		if (!tilesetTexture->loadFromFile(RESOURCE_FOLDER + tileset->GetImage()->GetSource()))
		{
			ThrowError();
		}

		int spacing = tileset->GetSpacing();
		int margin = tileset->GetMargin();

		int columns = (tilesetTexture->getSize().x - 2u * margin + spacing) / (tileWidth + spacing);
		int rows = (tilesetTexture->getSize().y - 2u * margin + spacing) / (tileHeight + spacing);

		for (int y = 0; y < rows; y++)
		{
			for (int x = 0; x < columns; x++)
			{
				sf::IntRect rect;
				rect.top = y * (tileHeight + spacing);
				rect.top += margin;
				rect.height = tileHeight;
				rect.left = x * (tileWidth + spacing);
				rect.left += margin;
				rect.width = tileWidth;

				int id = resultMap->m_tileInfo.size();

				resultMap->m_tileInfo.push_back(licesium::Map::TileInfo(rect,
					sf::Vector2f(static_cast<float>(rect.width), static_cast<float>(rect.height)),
					static_cast<sf::Uint16>(resultMap->m_tilesetTextures.size() - 1u)));

				const Tmx::Tile* tile = tileset->GetTile(id);
				if (tile->IsAnimated())
				{
					auto& resultTile = resultMap->m_tileInfo.back();
					resultTile.animated = true;
					resultTile.animationDuration = static_cast<float>(tile->GetTotalDuration());
					
					const auto& frames = tile->GetFrames();
					for (const auto& frame : frames)
					{
						resultTile.frames.push_back(std::make_pair(frame.GetTileID(), static_cast<float>(frame.GetDuration())));
					}
				}
			}
		}

		resultMap->m_tilesetTextures.push_back(std::move(tilesetTexture));
	}

	for (int i = 0; i < map->GetNumTileLayers(); ++i)
	{
		const Tmx::TileLayer* tileLayer = map->GetTileLayer(i);

		MapLayer mapLayer;

		mapLayer.m_name = tileLayer->GetName();
		mapLayer.m_opacity = tileLayer->GetOpacity();
		mapLayer.m_visible = tileLayer->IsVisible();

		for (int y = 0; y < tileLayer->GetHeight(); ++y)
		{
			for (int x = 0; x < tileLayer->GetWidth(); ++x)
			{
				if (tileLayer->GetTileTilesetIndex(x, y) != -1)
				{
					sf::Uint8 opacity = static_cast<sf::Uint8>(255.f * tileLayer->GetOpacity());
					sf::Color color = sf::Color(255u, 255u, 255u, opacity);

					sf::Vertex v0, v1, v2, v3;

					unsigned int gid = tileLayer->GetTileGid(x, y) - 1;
					Map::TileInfo tileInfo = resultMap->m_tileInfo[gid];

					v0.texCoords = tileInfo.coords[0] + sf::Vector2f( 0.5f, 0.5f);
					v1.texCoords = tileInfo.coords[1] + sf::Vector2f(-0.5f, 0.5f);
					v2.texCoords = tileInfo.coords[2] + sf::Vector2f(-0.5f,-0.5f);
					v3.texCoords = tileInfo.coords[3] + sf::Vector2f( 0.5f,-0.5f);

					v0.position = sf::Vector2f(static_cast<float>(tileWidth * x), static_cast<float>(tileHeight * y));
					v1.position = sf::Vector2f(static_cast<float>(tileWidth * x) + tileInfo.size.x, static_cast<float>(tileHeight * y));
					v2.position = sf::Vector2f(static_cast<float>(tileWidth * x) + tileInfo.size.x, static_cast<float>(tileHeight * y) + tileInfo.size.y);
					v3.position = sf::Vector2f(static_cast<float>(tileWidth * x), static_cast<float>(tileHeight * y) + tileInfo.size.y);

					sf::Uint16 thisTileHeight = static_cast<sf::Uint16>(tileInfo.size.y);
					if (thisTileHeight != tileHeight)
					{
						float diff = static_cast<float>(tileHeight - thisTileHeight);
						v0.position.y += diff;
						v1.position.y += diff;
						v2.position.y += diff;
						v3.position.y += diff;
					}

					sf::Vector2f isoOffset(-static_cast<float>(x * (tileWidth / 2u)), static_cast<float>(x * (tileHeight / 2u)));
					isoOffset.x -= static_cast<float>(y * (tileWidth / 2u));
					isoOffset.y -= static_cast<float>(y * (tileHeight / 2u));
					isoOffset.x -= static_cast<float>(tileWidth / 2u);
					isoOffset.y += static_cast<float>(tileHeight / 2u);

					v0.position += isoOffset;
					v1.position += isoOffset;
					v2.position += isoOffset;
					v3.position += isoOffset;

					v0.color = color;
					v1.color = color;
					v2.color = color;
					v3.color = color;

					size_t id = tileInfo.tilesetID;
					if (mapLayer.m_layerSets.find(id) == mapLayer.m_layerSets.end())
					{
						mapLayer.m_layerSets.insert(std::make_pair(id, 
							std::make_shared<LayerSet>(*resultMap->m_tilesetTextures[id], 
														sf::Vector2u(map->GetWidth(), map->GetHeight()), 
														sf::Vector2u(tileWidth, tileHeight))));
					}

					mapLayer.m_layerSets[id]->AddTile(v0, v1, v2, v3, x, y, tileInfo.animated, gid);
				}
			}
		}

		resultMap->m_layers.push_back(mapLayer);
	}

	for (int i = 0; i < map->GetNumObjectGroups(); ++i)
	{
		const Tmx::ObjectGroup* objectgroup = map->GetObjectGroup(i);

		MapLayer objectLayer;
		objectLayer.m_name = objectgroup->GetName();
		objectLayer.m_opacity = objectgroup->GetOpacity();
		objectLayer.m_visible = objectgroup->IsVisible();

		for (int j = 0; j < objectgroup->GetNumObjects(); ++j)
		{
			const Tmx::Object* object = objectgroup->GetObject(j);

			MapObject resultObject;

			sf::Vector2f offset(0.0f, tileHeight / 2.0f);

			resultObject.m_position = resultMap->IsoToOrtho(static_cast<float>(object->GetX()), static_cast<float>(object->GetY())) + offset;

			if (object->GetWidth() && object->GetHeight())
			{
				float width = static_cast<float>(object->GetWidth());
				float height = static_cast<float>(object->GetHeight());
				sf::Vector2f size = sf::Vector2f(width, height);
				resultObject.m_size = size;

				const Tmx::Ellipse* ellipse = object->GetEllipse();
				if (ellipse)
				{
					const float x = size.x / 2.0f;
					const float y = size.y / 2.0f;
					const float tau = 6.283185f;
					const float step = tau / 16.0f; //number of points to make up ellipse
					for (float angle = 0.0f; angle < tau; angle += step)
					{
						sf::Vector2f point(x + x * cos(angle), y + y * sin(angle));
						resultObject.m_polypoints.push_back(resultMap->IsoToOrtho(point));
					}

					resultObject.m_shapeType = MapObject::Ellipse;
				}
				else if (object->GetGid()) {
					sf::Color color = sf::Color(255u, 255u, 255u, 255u);

					sf::Vertex v0, v1, v2, v3;

					unsigned int gid = object->GetGid() - 1;
					Map::TileInfo tileInfo = resultMap->m_tileInfo[gid];

					v0.texCoords = tileInfo.coords[0] + sf::Vector2f(0.5f, 0.5f);
					v1.texCoords = tileInfo.coords[1] + sf::Vector2f(-0.5f, 0.5f);
					v2.texCoords = tileInfo.coords[2] + sf::Vector2f(-0.5f, -0.5f);
					v3.texCoords = tileInfo.coords[3] + sf::Vector2f(0.5f, -0.5f);

					v0.position = sf::Vector2f();
					v1.position = sf::Vector2f(size.x, 0.0f);
					v2.position = sf::Vector2f(size.x, size.y);
					v3.position = sf::Vector2f(0.0f, size.y);

					resultObject.m_position += sf::Vector2f((tileWidth - size.x) / 2, (tileHeight - size.y)) - resultMap->IsoToOrtho(tileWidth / 2.0f, 0.0f) - offset;
					sf::Vector2f position = resultObject.m_position;

					v0.position += position;
					v1.position += position;
					v2.position += position;
					v3.position += position;

					v0.color = color;
					v1.color = color;
					v2.color = color;
					v3.color = color;

					size_t id = tileInfo.tilesetID;
					if (objectLayer.m_layerSets.find(id) == objectLayer.m_layerSets.end())
					{
						objectLayer.m_layerSets.insert(std::make_pair(id,
							std::make_shared<LayerSet>(*resultMap->m_tilesetTextures[id],
								sf::Vector2u(map->GetWidth(), map->GetHeight()),
								sf::Vector2u(tileWidth, tileHeight))));
					}

					const int x = static_cast<int>(object->GetX() / tileWidth);
					const int y = static_cast<int>(object->GetY() / tileHeight);
					objectLayer.m_layerSets[id]->AddTile(v0, v1, v2, v3, x, y, tileInfo.animated, gid);

					resultObject.m_polypoints.push_back((sf::Vector2f(0.0f, size.y / 2.0f)));
					resultObject.m_polypoints.push_back((sf::Vector2f(size.x / 2.0f, 0.0f)));
					resultObject.m_polypoints.push_back((sf::Vector2f(size.x, size.y / 2.0f)));
					resultObject.m_polypoints.push_back((sf::Vector2f(size.x / 2.0f, size.y)));

					resultObject.m_shapeType = MapObject::Tile;
				}
				else {
					resultObject.m_polypoints.push_back(resultMap->IsoToOrtho(sf::Vector2f()));
					resultObject.m_polypoints.push_back(resultMap->IsoToOrtho(sf::Vector2f(size.x, 0.0f)));
					resultObject.m_polypoints.push_back(resultMap->IsoToOrtho(sf::Vector2f(size.x, size.y)));
					resultObject.m_polypoints.push_back(resultMap->IsoToOrtho(sf::Vector2f(0.0f, size.y)));

					resultObject.m_shapeType = MapObject::Rectangle;
				}
			}
			else if (object->GetPolygon()) {
				const Tmx::Polygon* polygon = object->GetPolygon();

				for (int k = 0; k < polygon->GetNumPoints(); k++)
				{
					const Tmx::Point &point = polygon->GetPoint(k);

					resultObject.m_polypoints.push_back(resultMap->IsoToOrtho(static_cast<float>(point.x), static_cast<float>(point.y)));
				}

				resultObject.m_shapeType = MapObject::Polygon;
			}
			else if (object->GetPolyline()) {
				//TODO: load polyline

				resultObject.m_shapeType = MapObject::Polyline;
			}

			resultObject.m_name = object->GetName();
			resultObject.m_type = object->GetType();
			resultObject.m_visible = object->IsVisible();
			resultObject.CreateDebugShape(sf::Color::Red);
			resultObject.m_tileRatio = tileRatio;

			resultObject.m_properties = object->GetProperties().GetList();

			objectLayer.m_objects.push_back(std::make_shared<MapObject>(resultObject));
		}

		resultMap->m_layers.push_back(objectLayer);
	}

	delete map;
	return resultMap;
}