+ infostream<<"getTextureIdDirect(): WARNING: NULL image in "
+ <<"cache: \""<<base_image_name<<"\""
+ <<std::endl;
+ }
+ else
+ {
+ core::dimension2d<u32> dim = ap.intsize;
+
+ baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+
+ core::position2d<s32> pos_to(0,0);
+ core::position2d<s32> pos_from = ap.intpos;
+
+ image->copyTo(
+ baseimg, // target
+ v2s32(0,0), // position in target
+ core::rect<s32>(pos_from, dim) // from
+ );
+
+ /*infostream<<"getTextureIdDirect(): Loaded \""
+ <<base_image_name<<"\" from image cache"
+ <<std::endl;*/
+ }
+ }
+
+ /*
+ Parse out the last part of the name of the image and act
+ according to it
+ */
+
+ std::string last_part_of_name = name.substr(last_separator_position+1);
+ //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
+
+ // Generate image according to part of name
+ if(!generate_image(last_part_of_name, baseimg, m_device, &m_sourcecache))
+ {
+ errorstream<<"getTextureIdDirect(): "
+ "failed to generate \""<<last_part_of_name<<"\""
+ <<std::endl;
+ }
+
+ // If no resulting image, print a warning
+ if(baseimg == NULL)
+ {
+ errorstream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
+ " create texture \""<<name<<"\""<<std::endl;
+ }
+
+ if(baseimg != NULL)
+ {
+ // Create texture from resulting image
+ t = driver->addTexture(name.c_str(), baseimg);
+ }
+
+ /*
+ Add texture to caches (add NULL textures too)
+ */
+
+ 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;
+ core::dimension2d<u32> baseimg_dim(0,0);
+ if(baseimg)
+ baseimg_dim = baseimg->getDimension();
+ SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
+ m_atlaspointer_cache.push_back(nap);
+ m_name_to_id.insert(name, id);
+
+ /*infostream<<"getTextureIdDirect(): "
+ <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
+
+ return id;
+}
+
+std::string TextureSource::getTextureName(u32 id)
+{
+ JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+
+ if(id >= m_atlaspointer_cache.size())
+ {
+ errorstream<<"TextureSource::getTextureName(): id="<<id
+ <<" >= m_atlaspointer_cache.size()="
+ <<m_atlaspointer_cache.size()<<std::endl;
+ return "";
+ }
+
+ return m_atlaspointer_cache[id].name;
+}
+
+
+AtlasPointer TextureSource::getTexture(u32 id)
+{
+ JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+
+ if(id >= m_atlaspointer_cache.size())
+ return AtlasPointer(0, NULL);
+
+ return m_atlaspointer_cache[id].a;
+}
+
+void TextureSource::updateAP(AtlasPointer &ap)
+{
+ AtlasPointer ap2 = getTexture(ap.id);
+ ap = ap2;
+}
+
+void TextureSource::processQueue()
+{
+ /*
+ Fetch textures
+ */
+ if(m_get_texture_queue.size() > 0)
+ {
+ GetRequest<std::string, u32, u8, u8>
+ request = m_get_texture_queue.pop();
+
+ /*infostream<<"TextureSource::processQueue(): "
+ <<"got texture request with "
+ <<"name=\""<<request.key<<"\""
+ <<std::endl;*/
+
+ GetResult<std::string, u32, u8, u8>
+ result;
+ result.key = request.key;
+ result.callers = request.callers;
+ result.item = getTextureIdDirect(request.key);
+
+ request.dest->push_back(result);
+ }
+}
+
+void TextureSource::insertSourceImage(const std::string &name, video::IImage *img)
+{
+ //infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
+
+ assert(get_current_thread_id() == m_main_thread);
+
+ m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
+}
+
+void TextureSource::rebuildImagesAndTextures()
+{
+ JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+
+ /*// Oh well... just clear everything, they'll load sometime.
+ m_atlaspointer_cache.clear();
+ m_name_to_id.clear();*/
+
+ video::IVideoDriver* driver = m_device->getVideoDriver();
+
+ // Remove source images from textures to disable inheriting textures
+ // from existing textures
+ /*for(u32 i=0; i<m_atlaspointer_cache.size(); i++){
+ SourceAtlasPointer *sap = &m_atlaspointer_cache[i];
+ sap->atlas_img->drop();
+ sap->atlas_img = NULL;
+ }*/
+
+ // Recreate textures
+ for(u32 i=0; i<m_atlaspointer_cache.size(); i++){
+ SourceAtlasPointer *sap = &m_atlaspointer_cache[i];
+ video::IImage *img =
+ generate_image_from_scratch(sap->name, m_device, &m_sourcecache);
+ // Create texture from resulting image
+ video::ITexture *t = NULL;
+ if(img)
+ t = driver->addTexture(sap->name.c_str(), img);
+
+ // Replace texture
+ sap->a.atlas = t;
+ sap->a.pos = v2f(0,0);
+ sap->a.size = v2f(1,1);
+ sap->a.tiled = 0;
+ sap->atlas_img = img;
+ sap->intpos = v2s32(0,0);
+ sap->intsize = img->getDimension();
+ }
+}
+
+void TextureSource::buildMainAtlas(class IGameDef *gamedef)
+{
+ assert(gamedef->tsrc() == this);
+ INodeDefManager *ndef = gamedef->ndef();
+
+ infostream<<"TextureSource::buildMainAtlas()"<<std::endl;
+
+ //return; // Disable (for testing)
+
+ video::IVideoDriver* driver = m_device->getVideoDriver();
+ assert(driver);
+
+ JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+
+ // Create an image of the right size
+ core::dimension2d<u32> atlas_dim(1024,1024);
+ video::IImage *atlas_img =
+ driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
+ //assert(atlas_img);
+ if(atlas_img == NULL)
+ {
+ errorstream<<"TextureSource::buildMainAtlas(): Failed to create atlas "
+ "image; not building texture atlas."<<std::endl;
+ return;
+ }
+
+ /*
+ Grab list of stuff to include in the texture atlas from the
+ main content features
+ */
+
+ core::map<std::string, bool> sourcelist;
+
+ for(u16 j=0; j<MAX_CONTENT+1; j++)
+ {
+ if(j == CONTENT_IGNORE || j == CONTENT_AIR)
+ continue;
+ const ContentFeatures &f = ndef->get(j);
+ for(std::set<std::string>::const_iterator
+ i = f.used_texturenames.begin();
+ i != f.used_texturenames.end(); i++)
+ {
+ std::string name = *i;
+ 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;
+
+ for(core::map<std::string, bool>::Iterator
+ i = sourcelist.getIterator();
+ i.atEnd() == false; i++)
+ {
+ std::string name = i.getNode()->getKey();
+
+ // Generate image by name
+ video::IImage *img2 = generate_image_from_scratch(name, m_device,
+ &m_sourcecache);
+ if(img2 == NULL)
+ {
+ errorstream<<"TextureSource::buildMainAtlas(): "
+ <<"Couldn't generate image \""<<name<<"\""<<std::endl;
+ continue;
+ }
+
+ core::dimension2d<u32> dim = img2->getDimension();
+
+ // Don't add to atlas if image is large
+ core::dimension2d<u32> max_size_in_atlas(32,32);
+ if(dim.Width > max_size_in_atlas.Width
+ || dim.Height > max_size_in_atlas.Height)
+ {
+ infostream<<"TextureSource::buildMainAtlas(): Not adding "
+ <<"\""<<name<<"\" because image is large"<<std::endl;
+ continue;
+ }
+
+ // Wrap columns and stop making atlas if atlas is full
+ if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
+ {
+ 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 = 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
+ /*img2->copyToWithAlpha(atlas_img,
+ pos_in_atlas + v2s32(j*dim.Width,0),
+ core::rect<s32>(v2s32(0,0), dim),
+ video::SColor(255,255,255,255),
+ NULL);*/
+ img2->copyTo(atlas_img,
+ pos_in_atlas + v2s32(j*dim.Width,0),
+ core::rect<s32>(v2s32(0,0), dim),
+ 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; y0<padding; y0++)
+ for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
+ {
+ s32 dst_y;
+ s32 src_y;
+ if(side==0)