← Back to team overview

widelands-dev team mailing list archive

Re: [Merge] lp:~widelands-dev/widelands/full_texture_atlas into lp:widelands

 

Adressed all comments and merged trunk. If everything passes on travis I'll merge this. 

Diff comments:

> 
> === modified file 'src/graphic/make_texture_atlas_main.cc'
> --- src/graphic/make_texture_atlas_main.cc	2016-01-04 20:54:08 +0000
> +++ src/graphic/make_texture_atlas_main.cc	2016-01-07 22:08:20 +0000
> @@ -62,11 +88,121 @@
>  	g_gr = new Graphic(1, 1, false);
>  }
>  
> +// Returns true if 'filename' is ends with a image extension.

done.

> +bool is_image(const std::string& filename) {
> +	return boost::ends_with(filename, ".png") || boost::ends_with(filename, ".jpg");
> +}
> +
> +// Recursively adds all images in 'directory' to 'ordered_images' and
> +// 'handled_images' for which 'predicate' returns true. We keep track of the
> +// images twice because we want to make sure that some end up in the same
> +// (first) texture atlas, so we add them first and we use the set to know that
> +// we already added an image.
> +void find_images(const std::string& directory,
> +                 std::unordered_set<std::string>* images,
> +                 std::vector<std::string>* ordered_images) {
> +	for (const std::string& filename : g_fs->list_directory(directory)) {
> +		if (g_fs->is_directory(filename)) {
> +			find_images(filename, images, ordered_images);
> +			continue;
> +		}
> +		if (is_image(filename) && !images->count(filename)) {
> +			images->insert(filename);
> +			ordered_images->push_back(filename);
> +		}
> +	}
> +}
> +
> +void dump_result(const std::map<std::string, PackInfo>& pack_info,
> +                 std::vector<std::unique_ptr<Texture>>* texture_atlases,
> +                 FileSystem* fs) {
> +
> +	for (size_t i = 0; i < texture_atlases->size(); ++i) {
> +		std::unique_ptr<StreamWrite> sw(
> +		   fs->open_stream_write((boost::format("output_%02i.png") % i).str()));
> +		save_to_png(texture_atlases->at(i).get(), sw.get(), ColorType::RGBA);
> +	}
> +
> +	{
> +		std::unique_ptr<StreamWrite> sw(fs->open_stream_write("output.lua"));
> +		sw->text("return {\n");
> +		for (const auto& pair : pack_info) {
> +			sw->text("   [\"");
> +			sw->text(pair.first);
> +			sw->text("\"] = {\n");
> +
> +			switch (pair.second.type) {
> +			case PackInfo::Type::kPacked:
> +				sw->text("       type = \"packed\",\n");
> +				sw->text(
> +				   (boost::format("       texture_atlas = %d,\n") % pair.second.texture_atlas).str());
> +				sw->text((boost::format("       rect = { %d, %d, %d, %d },\n") % pair.second.rect.x %
> +				          pair.second.rect.y % pair.second.rect.w % pair.second.rect.h).str());
> +				break;
> +
> +			case PackInfo::Type::kUnpacked:
> +				sw->text("       type = \"unpacked\",\n");
> +				break;
> +			}
> +			sw->text("   },\n");
> +		}
> +		sw->text("}\n");
> +	}
> +}
> +
> +// Pack the images in 'filenames' into texture atlases.
> +std::vector<std::unique_ptr<Texture>> pack_images(const std::vector<std::string>& filenames,
> +                                                  const int max_size,
> +                                                  std::map<std::string, PackInfo>* pack_info,
> +                                                  Texture* first_texture,
> +																  TextureAtlas::PackedTexture* first_atlas_packed_texture) {
> +	std::vector<std::pair<std::string, std::unique_ptr<Texture>>> to_be_packed;
> +	for (const auto& filename : filenames) {
> +		std::unique_ptr<Texture> image = load_image(filename, g_fs);
> +		const auto area = image->width() * image->height();
> +		if (area < kMaxAreaForTextureAtlas) {
> +			to_be_packed.push_back(std::make_pair(filename, std::move(image)));
> +		} else {
> +			pack_info->insert(std::make_pair(filename, PackInfo{
> +			                                              PackInfo::Type::kUnpacked, 0, Rect(),
> +			                                           }));
> +		}
> +	}
> +
> +	TextureAtlas atlas;
> +	int packed_texture_index = 0;
> +	if (first_texture != nullptr) {
> +		atlas.add(*first_texture);
> +		packed_texture_index = 1;
> +	}
> +	for (auto& pair : to_be_packed) {
> +		atlas.add(*pair.second);
> +	}
> +
> +	std::vector<std::unique_ptr<Texture>> texture_atlases;
> +	std::vector<TextureAtlas::PackedTexture> packed_textures;
> +	atlas.pack(max_size, &texture_atlases, &packed_textures);
> +
> +	if (first_texture != nullptr) {
> +		assert(first_atlas_packed_texture != nullptr);
> +		*first_atlas_packed_texture = std::move(packed_textures[0]);
> +	}
> +
> +	for (size_t i = 0; i < to_be_packed.size(); ++i) {
> +		const auto& packed_texture = packed_textures.at(packed_texture_index++);
> +		pack_info->insert(
> +		   std::make_pair(to_be_packed[i].first, PackInfo{PackInfo::Type::kPacked,
> +		                                                  packed_texture.texture_atlas,
> +		                                                  packed_texture.texture->blit_data().rect}));
> +	}
> +	return texture_atlases;
> +}
> +
>  }  // namespace
>  
>  int main(int argc, char** argv) {
> -	std::string input_directory;
> -	if (parse_arguments(argc, argv, &input_directory))
> +	int max_size;
> +	if (parse_arguments(argc, argv, &max_size))
>  		return 1;
>  
>  	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
> @@ -75,26 +211,64 @@
>  	}
>  	initialize();
>  
> -	std::vector<std::unique_ptr<Texture>> images;
> -	std::unique_ptr<FileSystem> input_fs(&FileSystem::create(input_directory));
> -	std::vector<std::string> png_filenames;
> -	for (const std::string& filename : input_fs->list_directory("")) {
> -		if (boost::ends_with(filename, ".png")) {
> -			png_filenames.push_back(filename);
> -			images.emplace_back(load_image(filename, input_fs.get()));
> -		}
> -	}
> -
> -	TextureAtlas atlas;
> -	for (auto& image : images) {
> -		atlas.add(*image);
> -	}
> -	std::vector<std::unique_ptr<Texture>> new_textures;
> -	auto packed_texture = atlas.pack(&new_textures);
> +
> +	// For performance reasons, we need to have some images in the first texture
> +	// atlas, so that OpenGL texture switches do not happen during (for example)
> +	// terrain or road rendering. To ensure this, we separate all images into
> +	// two disjunct sets. We than pack all images that should go into the first
> +	// texture atlas into a texture atlas. Than, we pack all remaining textures

done.

> +	// into a texture atlas, but including the first texture atlas as a singular
> +	// image (which will probaby be the biggest we allow).

done.

> +	//
> +	// We have to adjust the sub rectangle rendering for the images in the first
> +	// texture atlas in 'pack_info' later, before dumping the results.
> +	std::vector<std::string> other_images, images_that_must_be_in_first_atlas;
> +	std::unordered_set<std::string> all_images;
> +
> +	// For terrain textures.
> +	find_images("world/terrains", &all_images, &images_that_must_be_in_first_atlas);
> +	// For flags and roads.
> +	find_images("tribes/images", &all_images, &images_that_must_be_in_first_atlas);
> +	// For UI elements mostly, but we get more than we need really.
> +	find_images("pics", &all_images, &images_that_must_be_in_first_atlas);
> +
> +	// Add all other images, we do not really cares about the order for these.
> +	find_images("pics", &all_images, &other_images);

Right, good catch.

> +	find_images("world", &all_images, &other_images);
> +	find_images("tribes", &all_images, &other_images);
> +	assert(images_that_must_be_in_first_atlas.size() + other_images.size() == all_images.size());
> +
> +	std::map<std::string, PackInfo> first_texture_atlas_pack_info;
> +	auto first_texture_atlas = pack_images(images_that_must_be_in_first_atlas, max_size,
> +	                                       &first_texture_atlas_pack_info, nullptr, nullptr);
> +	if (first_texture_atlas.size() != 1) {
> +		std::cout << "Not all images that should fit in the first texture atlas did actually fit."
> +		          << std::endl;
> +		return 1;
> +	}
> +
> +	std::map<std::string, PackInfo> pack_info;
> +	TextureAtlas::PackedTexture first_atlas_packed_texture;
> +	auto texture_atlases = pack_images(other_images, max_size, &pack_info,
> +	                                   first_texture_atlas[0].get(), &first_atlas_packed_texture);
> +
> +	const auto& blit_data = first_atlas_packed_texture.texture->blit_data();
> +	for (const auto& pair : first_texture_atlas_pack_info) {
> +		assert(pack_info.count(pair.first) == 0);
> +		pack_info.insert(std::make_pair(pair.first, PackInfo{
> +		                                               pair.second.type,
> +		                                               first_atlas_packed_texture.texture_atlas,
> +		                                               Rect(blit_data.rect.x + pair.second.rect.x,
> +		                                                    blit_data.rect.y + pair.second.rect.y,
> +		                                                    pair.second.rect.w, pair.second.rect.h),
> +		                                            }));
> +	}
> +
> +	// Make sure we have all images.
> +	assert(all_images.size() == pack_info.size());
>  
>  	std::unique_ptr<FileSystem> output_fs(&FileSystem::create("."));
> -	std::unique_ptr<StreamWrite> sw(output_fs->open_stream_write("output.png"));
> -	save_to_png(packed_texture.get(), sw.get(), ColorType::RGBA);
> +	dump_result(pack_info, &texture_atlases, output_fs.get());
>  
>  	SDL_Quit();
>  	return 0;


-- 
https://code.launchpad.net/~widelands-dev/widelands/full_texture_atlas/+merge/281909
Your team Widelands Developers is subscribed to branch lp:~widelands-dev/widelands/full_texture_atlas.


References