Automate texture listing for texture atlas making
parent
eae2d35ca5
commit
05ab58cd14
|
@ -117,6 +117,7 @@ void content_mapnode_init()
|
||||||
f->setInventoryTextureCube("stone.png", "stone.png", "stone.png");
|
f->setInventoryTextureCube("stone.png", "stone.png", "stone.png");
|
||||||
f->param_type = CPT_MINERAL;
|
f->param_type = CPT_MINERAL;
|
||||||
f->is_ground_content = true;
|
f->is_ground_content = true;
|
||||||
|
f->often_contains_mineral = true;
|
||||||
f->dug_item = std::string("MaterialItem2 ")+itos(CONTENT_COBBLE)+" 1";
|
f->dug_item = std::string("MaterialItem2 ")+itos(CONTENT_COBBLE)+" 1";
|
||||||
setStoneLikeDiggingProperties(f->digging_properties, 1.0);
|
setStoneLikeDiggingProperties(f->digging_properties, 1.0);
|
||||||
if(invisible_stone)
|
if(invisible_stone)
|
||||||
|
|
|
@ -1278,6 +1278,9 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
// Initial call with g_texturesource not set.
|
// Initial call with g_texturesource not set.
|
||||||
init_mapnode();
|
init_mapnode();
|
||||||
|
// Must be called before g_texturesource is created
|
||||||
|
// (for texture atlas making)
|
||||||
|
init_mineral();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Run unit tests
|
Run unit tests
|
||||||
|
@ -1475,7 +1478,6 @@ int main(int argc, char *argv[])
|
||||||
*/
|
*/
|
||||||
|
|
||||||
init_mapnode(); // Second call with g_texturesource set
|
init_mapnode(); // Second call with g_texturesource set
|
||||||
init_mineral();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
GUI stuff
|
GUI stuff
|
||||||
|
|
|
@ -42,6 +42,8 @@ ContentFeatures::~ContentFeatures()
|
||||||
#ifndef SERVER
|
#ifndef SERVER
|
||||||
void ContentFeatures::setTexture(u16 i, std::string name, u8 alpha)
|
void ContentFeatures::setTexture(u16 i, std::string name, u8 alpha)
|
||||||
{
|
{
|
||||||
|
used_texturenames[name] = true;
|
||||||
|
|
||||||
if(g_texturesource)
|
if(g_texturesource)
|
||||||
{
|
{
|
||||||
tiles[i].texture = g_texturesource->getTexture(name);
|
tiles[i].texture = g_texturesource->getTexture(name);
|
||||||
|
|
|
@ -103,6 +103,9 @@ class NodeMetadata;
|
||||||
struct ContentFeatures
|
struct ContentFeatures
|
||||||
{
|
{
|
||||||
#ifndef SERVER
|
#ifndef SERVER
|
||||||
|
// List of all block textures that have been used (value is dummy)
|
||||||
|
core::map<std::string, bool> used_texturenames;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
0: up
|
0: up
|
||||||
1: down
|
1: down
|
||||||
|
@ -151,6 +154,10 @@ struct ContentFeatures
|
||||||
// If true, node is equivalent to air. Torches are, air is. Water is not.
|
// 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.
|
// Is used for example to check whether a mud block can have grass on.
|
||||||
bool air_equivalent;
|
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.
|
// Inventory item string as which the node appears in inventory when dug.
|
||||||
// Mineral overrides this.
|
// Mineral overrides this.
|
||||||
|
@ -207,6 +214,7 @@ struct ContentFeatures
|
||||||
liquid_type = LIQUID_NONE;
|
liquid_type = LIQUID_NONE;
|
||||||
wall_mounted = false;
|
wall_mounted = false;
|
||||||
air_equivalent = false;
|
air_equivalent = false;
|
||||||
|
often_contains_mineral = false;
|
||||||
dug_item = "";
|
dug_item = "";
|
||||||
initial_metadata = NULL;
|
initial_metadata = NULL;
|
||||||
liquid_alternative_flowing = CONTENT_IGNORE;
|
liquid_alternative_flowing = CONTENT_IGNORE;
|
||||||
|
|
102
src/tile.cpp
102
src/tile.cpp
|
@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include <ICameraSceneNode.h>
|
#include <ICameraSceneNode.h>
|
||||||
#include "log.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
|
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.
|
Grab list of stuff to include in the texture atlas from the
|
||||||
|
main content features
|
||||||
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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
core::array<std::string> sourcelist;
|
core::map<std::string, bool> sourcelist;
|
||||||
|
|
||||||
sourcelist.push_back("stone.png");
|
for(u16 j=0; j<MAX_CONTENT+1; j++)
|
||||||
sourcelist.push_back("mud.png");
|
{
|
||||||
sourcelist.push_back("sand.png");
|
if(j == CONTENT_IGNORE || j == CONTENT_AIR)
|
||||||
sourcelist.push_back("grass.png");
|
continue;
|
||||||
sourcelist.push_back("grass_footsteps.png");
|
ContentFeatures *f = &content_features(j);
|
||||||
sourcelist.push_back("tree.png");
|
for(core::map<std::string, bool>::Iterator
|
||||||
sourcelist.push_back("tree_top.png");
|
i = f->used_texturenames.getIterator();
|
||||||
sourcelist.push_back("water.png");
|
i.atEnd() == false; i++)
|
||||||
sourcelist.push_back("leaves.png");
|
{
|
||||||
sourcelist.push_back("glass.png");
|
std::string name = i.getNode()->getKey();
|
||||||
sourcelist.push_back("mud.png^grass_side.png");
|
sourcelist[name] = true;
|
||||||
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");
|
if(f->often_contains_mineral){
|
||||||
sourcelist.push_back("stone.png^mineral_iron.png");
|
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
|
// Padding to disallow texture bleeding
|
||||||
s32 padding = 16;
|
s32 padding = 16;
|
||||||
|
|
||||||
|
s32 column_width = 256;
|
||||||
|
s32 column_padding = 16;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
First pass: generate almost everything
|
First pass: generate almost everything
|
||||||
*/
|
*/
|
||||||
core::position2d<s32> pos_in_atlas(0,0);
|
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(
|
/*video::IImage *img = driver->createImageFromFile(
|
||||||
getTexturePath(name.c_str()).c_str());
|
getTexturePath(name.c_str()).c_str());
|
||||||
|
@ -586,20 +598,26 @@ void TextureSource::buildMainAtlas()
|
||||||
continue;
|
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)
|
if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
|
||||||
{
|
{
|
||||||
infostream<<"TextureSource::buildMainAtlas(): "
|
if(pos_in_atlas.X > (s32)atlas_dim.Width - 256 - padding){
|
||||||
|
errorstream<<"TextureSource::buildMainAtlas(): "
|
||||||
<<"Atlas is full, not adding more textures."
|
<<"Atlas is full, not adding more textures."
|
||||||
<<std::endl;
|
<<std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
pos_in_atlas.Y = padding;
|
||||||
|
pos_in_atlas.X += column_width + column_padding;
|
||||||
|
}
|
||||||
|
|
||||||
infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
|
infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
|
||||||
<<"\" to texture atlas"<<std::endl;
|
<<"\" to texture atlas"<<std::endl;
|
||||||
|
|
||||||
// Tile it a few times in the X direction
|
// 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++)
|
for(u32 j=0; j<xwise_tiling; j++)
|
||||||
{
|
{
|
||||||
// Copy the copy to the atlas
|
// Copy the copy to the atlas
|
||||||
|
@ -627,7 +645,7 @@ void TextureSource::buildMainAtlas()
|
||||||
dst_y = -y0 + pos_in_atlas.Y-1;
|
dst_y = -y0 + pos_in_atlas.Y-1;
|
||||||
src_y = pos_in_atlas.Y;
|
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);
|
video::SColor c = atlas_img->getPixel(x, src_y);
|
||||||
atlas_img->setPixel(x,dst_y,c);
|
atlas_img->setPixel(x,dst_y,c);
|
||||||
}
|
}
|
||||||
|
@ -668,9 +686,11 @@ void TextureSource::buildMainAtlas()
|
||||||
/*
|
/*
|
||||||
Second pass: set texture pointer in generated AtlasPointers
|
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)
|
if(m_name_to_id.find(name) == NULL)
|
||||||
continue;
|
continue;
|
||||||
u32 id = m_name_to_id[name];
|
u32 id = m_name_to_id[name];
|
||||||
|
@ -681,8 +701,12 @@ void TextureSource::buildMainAtlas()
|
||||||
/*
|
/*
|
||||||
Write image to file so that it can be inspected
|
Write image to file so that it can be inspected
|
||||||
*/
|
*/
|
||||||
/*driver->writeImageToFile(atlas_img,
|
/*std::string atlaspath = porting::path_userdata
|
||||||
getTexturePath("main_atlas.png").c_str());*/
|
+ 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,
|
video::IImage* generate_image_from_scratch(std::string name,
|
||||||
|
|
Loading…
Reference in New Issue