/* Minetest-c55 Copyright (C) 2010 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "tile.h" #include "debug.h" 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("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("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"); // Padding to disallow texture bleeding s32 padding = 8; /* First pass: generate almost everything */ core::position2d pos_in_atlas(0,0); pos_in_atlas.Y += padding; 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); } // Copy the borders a few times to disallow texture bleeding for(u32 side=0; side<2; side++) // top and bottom for(s32 y0=0; y0getPixel(x, src_y); atlas_img->setPixel(x,dst_y,c); } 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 + padding * 2; } /* 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); } } }