From 1704badc306fc8c7c6609aff9f809aee3ac00d3a Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Thu, 10 Feb 2011 02:13:03 +0200 Subject: work-in-progress texture atlas optimization --- src/tile.cpp | 770 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 765 insertions(+), 5 deletions(-) (limited to 'src/tile.cpp') diff --git a/src/tile.cpp b/src/tile.cpp index 60e9873c0..f31f83076 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -18,10 +18,770 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "tile.h" -//#include "porting.h" -// For IrrlichtWrapper -//#include "main.h" -//#include +#include "debug.h" -// Nothing here +TextureSource::TextureSource(IrrlichtDevice *device): + m_device(device), + m_main_atlas_image(NULL), + m_main_atlas_texture(NULL) +{ + assert(m_device); + + m_atlaspointer_cache_mutex.Init(); + + m_main_thread = get_current_thread_id(); + + // Add a NULL AtlasPointer as the first index, named "" + m_atlaspointer_cache.push_back(SourceAtlasPointer("")); + m_name_to_id[""] = 0; + + // Build main texture atlas + buildMainAtlas(); +} + +TextureSource::~TextureSource() +{ +} + +void TextureSource::processQueue() +{ + /* + Fetch textures + */ + if(m_get_texture_queue.size() > 0) + { + GetRequest + request = m_get_texture_queue.pop(); + + dstream<<"INFO: TextureSource::processQueue(): " + <<"got texture request with " + <<"name="< + result; + result.key = request.key; + result.callers = request.callers; + result.item = getTextureIdDirect(request.key); + + request.dest->push_back(result); + } +} + +u32 TextureSource::getTextureId(const std::string &name) +{ + //dstream<<"INFO: getTextureId(): name="<::Node *n; + n = m_name_to_id.find(name); + if(n != NULL) + { + return n->getValue(); + } + } + + /* + Get texture + */ + if(get_current_thread_id() == m_main_thread) + { + return getTextureIdDirect(name); + } + else + { + dstream<<"INFO: getTextureId(): Queued: name="< result_queue; + + // Throw a request in + m_get_texture_queue.add(name, 0, 0, &result_queue); + + dstream<<"INFO: Waiting for texture from main thread, name=" + < + result = result_queue.pop_front(1000); + + // Check that at least something worked OK + assert(result.key == name); + + return result.item; + } + catch(ItemNotFoundException &e) + { + dstream<<"WARNING: Waiting for texture timed out."<::Node *n; + n = m_name_to_id.find(name); + if(n != NULL) + { + dstream<<"INFO: getTextureIdDirect(): name="<getValue(); + } + } + + dstream<<"INFO: getTextureIdDirect(): name="<=0; i--) + { + if(name[i] == separator) + { + last_separator_position = i; + break; + } + } + /* + If separator was found, construct the base name and make the + base image using a recursive call + */ + std::string base_image_name; + if(last_separator_position != -1) + { + // Construct base name + base_image_name = name.substr(0, last_separator_position); + dstream<<"INFO: getTextureIdDirect(): Calling itself recursively" + " to get base image, name="<addTexture(name.c_str(), baseimg); + + // If no texture + if(t == NULL) + return 0; + + /* + Add texture to caches + */ + + JMutexAutoLock lock(m_atlaspointer_cache_mutex); + + u32 id = m_atlaspointer_cache.size(); + AtlasPointer ap(id); + ap.atlas = t; + ap.pos = v2f(0,0); + ap.size = v2f(1,1); + ap.tiled = 0; + SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg->getDimension()); + m_atlaspointer_cache.push_back(nap); + m_name_to_id.insert(name, id); + + dstream<<"INFO: getTextureIdDirect(): name="<= m_atlaspointer_cache.size()=" + <= m_atlaspointer_cache.size()) + return AtlasPointer(0, NULL); + + return m_atlaspointer_cache[id].a; +} + +void TextureSource::buildMainAtlas() +{ + dstream<<"TextureSource::buildMainAtlas()"<getVideoDriver(); + assert(driver); + + JMutexAutoLock lock(m_atlaspointer_cache_mutex); + + // Create an image of the right size + core::dimension2d atlas_dim(1024,1024); + video::IImage *atlas_img = + driver->createImage(video::ECF_A8R8G8B8, atlas_dim); + + /* + A list of stuff to add. This should contain as much of the + stuff shown in game as possible, to minimize texture changes. + */ + + core::array sourcelist; + + sourcelist.push_back("stone.png"); + sourcelist.push_back("mud.png"); + sourcelist.push_back("sand.png"); + sourcelist.push_back("grass.png"); + sourcelist.push_back("mud.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("mud.png^grass_side.png"); + + sourcelist.push_back("stone.png^mineral_coal.png"); + sourcelist.push_back("stone.png^mineral_iron.png"); + sourcelist.push_back("mud.png^mineral_coal.png"); + sourcelist.push_back("mud.png^mineral_iron.png"); + sourcelist.push_back("sand.png^mineral_coal.png"); + sourcelist.push_back("sand.png^mineral_iron.png"); + + /* + First pass: generate almost everything + */ + core::position2d pos_in_atlas(0,0); + for(u32 i=0; icreateImageFromFile( + porting::getDataPath(name.c_str()).c_str()); + if(img == NULL) + continue; + + core::dimension2d dim = img->getDimension(); + // Make a copy with the right color format + video::IImage *img2 = + driver->createImage(video::ECF_A8R8G8B8, dim); + img->copyTo(img2); + img->drop();*/ + + // Generate image by name + video::IImage *img2 = generate_image_from_scratch(name, driver); + core::dimension2d dim = img2->getDimension(); + + // Tile it a few times in the X direction + u16 xwise_tiling = 16; + for(u32 j=0; jcopyToWithAlpha(atlas_img, + pos_in_atlas + v2s32(j*dim.Width,0), + core::rect(v2s32(0,0), dim), + video::SColor(255,255,255,255), + NULL); + } + + img2->drop(); + + /* + Add texture to caches + */ + + // Get next id + u32 id = m_atlaspointer_cache.size(); + + // Create AtlasPointer + AtlasPointer ap(id); + ap.atlas = NULL; // Set on the second pass + ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width, + (float)pos_in_atlas.Y/(float)atlas_dim.Height); + ap.size = v2f((float)dim.Width/(float)atlas_dim.Width, + (float)dim.Width/(float)atlas_dim.Height); + ap.tiled = xwise_tiling; + + // Create SourceAtlasPointer and add to containers + SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim); + m_atlaspointer_cache.push_back(nap); + m_name_to_id.insert(name, id); + + // Increment position + pos_in_atlas.Y += dim.Height; + } + + /* + Make texture + */ + video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img); + assert(t); + + /* + Second pass: set texture pointer in generated AtlasPointers + */ + for(u32 i=0; iwriteImageToFile(atlas_img, + porting::getDataPath("main_atlas.png").c_str()); +} + +video::IImage* generate_image_from_scratch(std::string name, + video::IVideoDriver* driver) +{ + dstream<<"INFO: generate_image_from_scratch(): " + "name="<=0; i--) + { + if(name[i] == separator) + { + last_separator_position = i; + break; + } + } + + /*dstream<<"INFO: generate_image_from_scratch(): " + <<"last_separator_position="<createImageFromFile(path.c_str()); + + if(image == NULL) + { + dstream<<"WARNING: Could not load image \""< dim = image->getDimension(); + baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + image->copyTo(baseimg); + image->drop(); + } + // Else blit on base. + else + { + dstream<<"INFO: Blitting "< dim = image->getDimension(); + //core::dimension2d dim(16,16); + // Position to copy the blitted to in the base image + core::position2d pos_to(0,0); + // Position to copy the blitted from in the blitted image + core::position2d pos_from(0,0); + // Blit + image->copyToWithAlpha(baseimg, pos_to, + core::rect(pos_from, dim), + video::SColor(255,255,255,255), + NULL); + // Drop image + image->drop(); + } + } + else + { + // A special texture modification + + dstream<<"INFO: getTextureIdDirect(): generating special " + <<"modification \""< dim_base = baseimg->getDimension(); + // Crack will be drawn at this size + u32 cracksize = 16; + // Size of the crack image + core::dimension2d dim_crack(cracksize,cracksize); + // Position to copy the crack from in the crack image + core::position2d pos_other(0, 16 * progression); + + video::IImage *crackimage = driver->createImageFromFile( + porting::getDataPath("crack.png").c_str()); + + if(crackimage) + { + /*crackimage->copyToWithAlpha(baseimg, v2s32(0,0), + core::rect(pos_other, dim_base), + video::SColor(255,255,255,255), + NULL);*/ + + for(u32 y0=0; y0 pos_base(x0*cracksize, y0*cracksize); + crackimage->copyToWithAlpha(baseimg, pos_base, + core::rect(pos_other, dim_crack), + video::SColor(255,255,255,255), + NULL); + } + + crackimage->drop(); + } + } + else if(part_of_name.substr(0,8) == "[combine") + { + // "[combine:16x128:0,0=stone.png:0,16=grass.png" + Strfnd sf(part_of_name); + sf.next(":"); + u32 w0 = stoi(sf.next("x")); + u32 h0 = stoi(sf.next(":")); + dstream<<"INFO: combined w="<createImageFromFile( + porting::getDataPath(filename.c_str()).c_str()); + if(img) + { + core::dimension2d dim = img->getDimension(); + dstream<<"INFO: Size "< pos_base(x, y); + video::IImage *img2 = + driver->createImage(video::ECF_A8R8G8B8, dim); + img->copyTo(img2); + img->drop(); + img2->copyToWithAlpha(baseimg, pos_base, + core::rect(v2s32(0,0), dim), + video::SColor(255,255,255,255), + NULL); + img2->drop(); + } + else + { + dstream<<"WARNING: img==NULL"<createImageFromFile(path.c_str()); + + if(image == NULL) + { + dstream<<"WARNING: getTextureIdDirect(): Loading path \"" + < dim = image->getDimension(); + baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + + // Set alpha to full + for(u32 y=0; ygetPixel(x,y); + c.setAlpha(255); + image->setPixel(x,y,c); + } + // Blit + image->copyTo(baseimg); + + image->drop(); + } + } + else + { + dstream<<"WARNING: getTextureIdDirect(): Invalid " + " modification: \""< size = image->getDimension(); + + u32 barheight = 1; + u32 barpad_x = 1; + u32 barpad_y = 1; + u32 barwidth = size.Width - barpad_x*2; + v2u32 barpos(barpad_x, size.Height - barheight - barpad_y); + + u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5); + + video::SColor active(255,255,0,0); + video::SColor inactive(255,0,0,0); + for(u32 x0=0; x0setPixel(x,y, *c); + } + } +} -- cgit v1.2.3