Automate texture listing for texture atlas making
authorPerttu Ahola <celeron55@gmail.com>
Tue, 18 Oct 2011 08:31:23 +0000 (11:31 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Tue, 18 Oct 2011 08:31:23 +0000 (11:31 +0300)
src/content_mapnode.cpp
src/main.cpp
src/mapnode.cpp
src/mapnode.h
src/tile.cpp

index af5e98951eb7fc5ca52d56cddd1883ff9f6c9f0b..139c46fb27a61cc1984dce02cd9fa77c12370cff 100644 (file)
@@ -117,6 +117,7 @@ void content_mapnode_init()
        f->setInventoryTextureCube("stone.png", "stone.png", "stone.png");
        f->param_type = CPT_MINERAL;
        f->is_ground_content = true;
+       f->often_contains_mineral = true;
        f->dug_item = std::string("MaterialItem2 ")+itos(CONTENT_COBBLE)+" 1";
        setStoneLikeDiggingProperties(f->digging_properties, 1.0);
        if(invisible_stone)
index be983676e31f11f6615b028b475dbf48be5aa2b3..df1347f121d3624aa8a494f707e3335e9322fd2a 100644 (file)
@@ -1278,6 +1278,9 @@ int main(int argc, char *argv[])
        
        // Initial call with g_texturesource not set.
        init_mapnode();
+       // Must be called before g_texturesource is created
+       // (for texture atlas making)
+       init_mineral();
 
        /*
                Run unit tests
@@ -1475,7 +1478,6 @@ int main(int argc, char *argv[])
        */
 
        init_mapnode(); // Second call with g_texturesource set
-       init_mineral();
 
        /*
                GUI stuff
index 170500f456740b17b1b1fb2b9e55d20f2c5eaf06..484fcbe0af3a8fddd1aac23d2cbcc18a0fef8a0e 100644 (file)
@@ -42,6 +42,8 @@ ContentFeatures::~ContentFeatures()
 #ifndef SERVER
 void ContentFeatures::setTexture(u16 i, std::string name, u8 alpha)
 {
+       used_texturenames[name] = true;
+       
        if(g_texturesource)
        {
                tiles[i].texture = g_texturesource->getTexture(name);
index 5db3379490088893d57b19e04c2c2b05d5fb2011..61f172eda69794d9e374cfeed546ae4e3170e75f 100644 (file)
@@ -103,6 +103,9 @@ class NodeMetadata;
 struct ContentFeatures
 {
 #ifndef SERVER
+       // List of all block textures that have been used (value is dummy)
+       core::map<std::string, bool> used_texturenames;
+       
        /*
                0: up
                1: down
@@ -151,6 +154,10 @@ struct ContentFeatures
        // If true, node is equivalent to air. Torches are, air is. Water is not.
        // Is used for example to check whether a mud block can have grass on.
        bool air_equivalent;
+       // Whether this content type often contains mineral.
+       // Used for texture atlas creation.
+       // Currently only enabled for CONTENT_STONE.
+       bool often_contains_mineral;
        
        // Inventory item string as which the node appears in inventory when dug.
        // Mineral overrides this.
@@ -207,6 +214,7 @@ struct ContentFeatures
                liquid_type = LIQUID_NONE;
                wall_mounted = false;
                air_equivalent = false;
+               often_contains_mineral = false;
                dug_item = "";
                initial_metadata = NULL;
                liquid_alternative_flowing = CONTENT_IGNORE;
index e274d2d3e2822bd5c7f9cc7e56fd1eff270000bf..366f2dfb798d6b66b54115d74deb9c3dbcb9279a 100644 (file)
@@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include <ICameraSceneNode.h>
 #include "log.h"
+#include "mapnode.h" // For texture atlas making
+#include "mineral.h" // For texture atlas making
 
 /*
        A cache from texture name to texture path
@@ -507,52 +509,62 @@ void TextureSource::buildMainAtlas()
        }
 
        /*
-               A list of stuff to include in the texture atlas.
-
-               It is a single-dimensional texture atlas due to the need to tile
-               textures.
-               
-               It should contain as much of the stuff shown in game as possible,
-               to minimize texture changes.
-
-               It fills up quickly, so do not add anything that isn't contained
-               in most MapBlocks. E.g. mese isn't suitable but stone is.
+               Grab list of stuff to include in the texture atlas from the
+               main content features
        */
 
-       core::array<std::string> sourcelist;
-
-       sourcelist.push_back("stone.png");
-       sourcelist.push_back("mud.png");
-       sourcelist.push_back("sand.png");
-       sourcelist.push_back("grass.png");
-       sourcelist.push_back("grass_footsteps.png");
-       sourcelist.push_back("tree.png");
-       sourcelist.push_back("tree_top.png");
-       sourcelist.push_back("water.png");
-       sourcelist.push_back("leaves.png");
-       sourcelist.push_back("glass.png");
-       sourcelist.push_back("mud.png^grass_side.png");
-       sourcelist.push_back("cobble.png");
-       sourcelist.push_back("mossycobble.png");
-       sourcelist.push_back("gravel.png");
-       sourcelist.push_back("jungletree.png");
-       
-       sourcelist.push_back("stone.png^mineral_coal.png");
-       sourcelist.push_back("stone.png^mineral_iron.png");
+       core::map<std::string, bool> sourcelist;
+
+       for(u16 j=0; j<MAX_CONTENT+1; j++)
+       {
+               if(j == CONTENT_IGNORE || j == CONTENT_AIR)
+                       continue;
+               ContentFeatures *f = &content_features(j);
+               for(core::map<std::string, bool>::Iterator
+                               i = f->used_texturenames.getIterator();
+                               i.atEnd() == false; i++)
+               {
+                       std::string name = i.getNode()->getKey();
+                       sourcelist[name] = true;
+
+                       if(f->often_contains_mineral){
+                               for(int k=1; k<MINERAL_COUNT; k++){
+                                       std::string mineraltexture = mineral_block_texture(k);
+                                       std::string fulltexture = name + "^" + mineraltexture;
+                                       sourcelist[fulltexture] = true;
+                               }
+                       }
+               }
+       }
        
+       infostream<<"Creating texture atlas out of textures: ";
+       for(core::map<std::string, bool>::Iterator
+                       i = sourcelist.getIterator();
+                       i.atEnd() == false; i++)
+       {
+               std::string name = i.getNode()->getKey();
+               infostream<<"\""<<name<<"\" ";
+       }
+       infostream<<std::endl;
+
        // Padding to disallow texture bleeding
        s32 padding = 16;
 
+       s32 column_width = 256;
+       s32 column_padding = 16;
+
        /*
                First pass: generate almost everything
        */
        core::position2d<s32> pos_in_atlas(0,0);
        
-       pos_in_atlas.Y += padding;
+       pos_in_atlas.Y = padding;
 
-       for(u32 i=0; i<sourcelist.size(); i++)
+       for(core::map<std::string, bool>::Iterator
+                       i = sourcelist.getIterator();
+                       i.atEnd() == false; i++)
        {
-               std::string name = sourcelist[i];
+               std::string name = i.getNode()->getKey();
 
                /*video::IImage *img = driver->createImageFromFile(
                                getTexturePath(name.c_str()).c_str());
@@ -586,20 +598,26 @@ void TextureSource::buildMainAtlas()
                        continue;
                }
 
-               // Stop making atlas if atlas is full
+               // Wrap columns and stop making atlas if atlas is full
                if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
                {
-                       infostream<<"TextureSource::buildMainAtlas(): "
-                                       <<"Atlas is full, not adding more textures."
-                                       <<std::endl;
-                       break;
+                       if(pos_in_atlas.X > (s32)atlas_dim.Width - 256 - padding){
+                               errorstream<<"TextureSource::buildMainAtlas(): "
+                                               <<"Atlas is full, not adding more textures."
+                                               <<std::endl;
+                               break;
+                       }
+                       pos_in_atlas.Y = padding;
+                       pos_in_atlas.X += column_width + column_padding;
                }
                
         infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
                 <<"\" to texture atlas"<<std::endl;
 
                // Tile it a few times in the X direction
-               u16 xwise_tiling = 16;
+               u16 xwise_tiling = column_width / dim.Width;
+               if(xwise_tiling > 16) // Limit to 16 (more gives no benefit)
+                       xwise_tiling = 16;
                for(u32 j=0; j<xwise_tiling; j++)
                {
                        // Copy the copy to the atlas
@@ -627,7 +645,7 @@ void TextureSource::buildMainAtlas()
                                dst_y = -y0 + pos_in_atlas.Y-1;
                                src_y = pos_in_atlas.Y;
                        }
-                       s32 x = x0 + pos_in_atlas.X * dim.Width;
+                       s32 x = x0 + pos_in_atlas.X;
                        video::SColor c = atlas_img->getPixel(x, src_y);
                        atlas_img->setPixel(x,dst_y,c);
                }
@@ -668,9 +686,11 @@ void TextureSource::buildMainAtlas()
        /*
                Second pass: set texture pointer in generated AtlasPointers
        */
-       for(u32 i=0; i<sourcelist.size(); i++)
+       for(core::map<std::string, bool>::Iterator
+                       i = sourcelist.getIterator();
+                       i.atEnd() == false; i++)
        {
-               std::string name = sourcelist[i];
+               std::string name = i.getNode()->getKey();
                if(m_name_to_id.find(name) == NULL)
                        continue;
                u32 id = m_name_to_id[name];
@@ -681,8 +701,12 @@ void TextureSource::buildMainAtlas()
        /*
                Write image to file so that it can be inspected
        */
-       /*driver->writeImageToFile(atlas_img, 
-                       getTexturePath("main_atlas.png").c_str());*/
+       /*std::string atlaspath = porting::path_userdata
+                       + DIR_DELIM + "generated_texture_atlas.png";
+       infostream<<"Removing and writing texture atlas for inspection to "
+                       <<atlaspath<<std::endl;
+       fs::RecursiveDelete(atlaspath);
+       driver->writeImageToFile(atlas_img, atlaspath.c_str());*/
 }
 
 video::IImage* generate_image_from_scratch(std::string name,