X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Ftile.cpp;h=3c4989ea85a58bf43853de59056f0132644848ff;hb=1e4e64f83195ed1be0aa0c4a237a50de6dd132a9;hp=e676c56c438c8eb54859e2d4ae1db0d7c22f1aeb;hpb=41c00e87d42580881d8c7001f39014814e3b55f2;p=dragonfireclient.git diff --git a/src/tile.cpp b/src/tile.cpp index e676c56c4..3c4989ea8 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -1,6 +1,6 @@ /* -Minetest-c55 -Copyright (C) 2010-2011 celeron55, Perttu Ahola +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "tile.h" +#include "irrlichttypes_extrabloated.h" #include "debug.h" #include "main.h" // for g_settings #include "filesys.h" @@ -25,8 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mesh.h" #include #include "log.h" -#include "mapnode.h" // For texture atlas making -#include "nodedef.h" // For texture atlas making #include "gamedef.h" #include "util/string.h" #include "util/container.h" @@ -77,7 +76,7 @@ static bool replace_ext(std::string &path, const char *ext) If failed, return "". */ -static std::string getImagePath(std::string path) +std::string getImagePath(std::string path) { // A NULL-ended list of possible image extensions const char *extensions[] = { @@ -132,18 +131,6 @@ std::string getTexturePath(const std::string &filename) // Check all filename extensions. Returns "" if not found. fullpath = getImagePath(testpath); } - - /* - Check from $user/textures/all - */ - if(fullpath == "") - { - std::string texture_path = porting::path_user + DIR_DELIM - + "textures" + DIR_DELIM + "all"; - std::string testpath = texture_path + DIR_DELIM + filename; - // Check all filename extensions. Returns "" if not found. - fullpath = getImagePath(testpath); - } /* Check from default data directory @@ -164,32 +151,29 @@ std::string getTexturePath(const std::string &filename) return fullpath; } +void clearTextureNameCache() +{ + g_texturename_to_path_cache.clear(); +} + /* - An internal variant of AtlasPointer with more data. - (well, more like a wrapper) + Stores internal information about a texture. */ -struct SourceAtlasPointer +struct TextureInfo { std::string name; - AtlasPointer a; - video::IImage *atlas_img; // The source image of the atlas - // Integer variants of position and size - v2s32 intpos; - v2u32 intsize; + video::ITexture *texture; + video::IImage *img; // The source image - SourceAtlasPointer( + TextureInfo( const std::string &name_, - AtlasPointer a_=AtlasPointer(0, NULL), - video::IImage *atlas_img_=NULL, - v2s32 intpos_=v2s32(0,0), - v2u32 intsize_=v2u32(0,0) + video::ITexture *texture_=NULL, + video::IImage *img_=NULL ): name(name_), - a(a_), - atlas_img(atlas_img_), - intpos(intpos_), - intsize(intsize_) + texture(texture_), + img(img_) { } }; @@ -201,48 +185,60 @@ struct SourceAtlasPointer class SourceImageCache { public: + ~SourceImageCache() { + for(std::map::iterator iter = m_images.begin(); + iter != m_images.end(); iter++) { + iter->second->drop(); + } + m_images.clear(); + } void insert(const std::string &name, video::IImage *img, bool prefer_local, video::IVideoDriver *driver) { assert(img); // Remove old image - core::map::Node *n; + std::map::iterator n; n = m_images.find(name); - if(n){ - video::IImage *oldimg = n->getValue(); - if(oldimg) - oldimg->drop(); + if(n != m_images.end()){ + if(n->second) + n->second->drop(); } + + video::IImage* toadd = img; + bool need_to_grab = true; + // Try to use local texture instead if asked to if(prefer_local){ std::string path = getTexturePath(name.c_str()); if(path != ""){ video::IImage *img2 = driver->createImageFromFile(path.c_str()); if(img2){ - m_images[name] = img2; - return; + toadd = img2; + need_to_grab = false; } } } - img->grab(); - m_images[name] = img; + + if (need_to_grab) + toadd->grab(); + m_images[name] = toadd; } video::IImage* get(const std::string &name) { - core::map::Node *n; + std::map::iterator n; n = m_images.find(name); - if(n) - return n->getValue(); + if(n != m_images.end()) + return n->second; return NULL; } // Primarily fetches from cache, secondarily tries to read from filesystem video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *device) { - core::map::Node *n; + std::map::iterator n; n = m_images.find(name); - if(n){ - n->getValue()->grab(); // Grab for caller - return n->getValue(); + if(n != m_images.end()){ + n->second->grab(); // Grab for caller + return n->second; } video::IVideoDriver* driver = device->getVideoDriver(); std::string path = getTexturePath(name.c_str()); @@ -254,8 +250,7 @@ class SourceImageCache infostream<<"SourceImageCache::getOrLoad(): Loading path \""<createImageFromFile(path.c_str()); - // Even if could not be loaded, put as NULL - //m_images[name] = img; + if(img){ m_images[name] = img; img->grab(); // Grab for caller @@ -263,7 +258,7 @@ class SourceImageCache return img; } private: - core::map m_images; + std::map m_images; }; /* @@ -274,7 +269,7 @@ class TextureSource : public IWritableTextureSource { public: TextureSource(IrrlichtDevice *device); - ~TextureSource(); + virtual ~TextureSource(); /* Example case: @@ -296,7 +291,7 @@ class TextureSource : public IWritableTextureSource Example case #2: - Assume a texture with the id 1 exists, and has the name - "stone.png^mineral1" and is specified as a part of some atlas. + "stone.png^mineral_coal.png". - Now getNodeTile() stumbles upon a node which uses texture id 1, and determines that MATERIAL_FLAG_CRACK must be applied to the tile @@ -304,7 +299,7 @@ class TextureSource : public IWritableTextureSource has received the current crack level 0 from the client. It finds out the name of the texture with getTextureName(1), appends "^crack0" to it and gets a new texture id with - getTextureId("stone.png^mineral1^crack0"). + getTextureId("stone.png^mineral_coal.png^crack0"). */ @@ -343,25 +338,9 @@ class TextureSource : public IWritableTextureSource and not found in cache, the call is queued to the main thread for processing. */ - AtlasPointer getTexture(u32 id); - - AtlasPointer getTexture(const std::string &name) - { - return getTexture(getTextureId(name)); - } - - // Gets a separate texture - video::ITexture* getTextureRaw(const std::string &name) - { - AtlasPointer ap = getTexture(name + "^[forcesingle"); - return ap.atlas; - } + video::ITexture* getTexture(u32 id); - // Gets a separate texture atlas pointer - AtlasPointer getTextureRawAP(const AtlasPointer &ap) - { - return getTexture(getTextureName(ap.id) + "^[forcesingle"); - } + video::ITexture* getTexture(const std::string &name, u32 *id); // Returns a pointer to the irrlicht device virtual IrrlichtDevice* getDevice() @@ -369,9 +348,17 @@ class TextureSource : public IWritableTextureSource return m_device; } - // Update new texture pointer and texture coordinates to an - // AtlasPointer based on it's texture id - void updateAP(AtlasPointer &ap); + bool isKnownSourceImage(const std::string &name) + { + bool is_known = false; + bool cache_found = m_source_image_existence.get(name, &is_known); + if(cache_found) + return is_known; + // Not found in cache; find out if a local file exists + is_known = (getTexturePath(name) != ""); + m_source_image_existence.set(name, is_known); + return is_known; + } // Processes queued texture requests from other threads. // Shall be called from the main thread. @@ -384,11 +371,23 @@ class TextureSource : public IWritableTextureSource // Rebuild images and textures from the current set of source images // Shall be called from the main thread. void rebuildImagesAndTextures(); - - // Build the main texture atlas which contains most of the - // textures. - void buildMainAtlas(class IGameDef *gamedef); + // Render a mesh to a texture. + // Returns NULL if render-to-texture failed. + // Shall be called from the main thread. + video::ITexture* generateTextureFromMesh( + const TextureFromMeshParams ¶ms); + + // Generates an image from a full string like + // "stone.png^mineral_coal.png^[crack:1:0". + // Shall be called from the main thread. + video::IImage* generateImageFromScratch(std::string name); + + // Generate image based on a string like "stone.png" or "[crack:1:0". + // if baseimg is NULL, it is created. Otherwise stuff is made on it. + // Shall be called from the main thread. + bool generateImage(std::string part_of_name, video::IImage *& baseimg); + private: // The id of the thread that is allowed to use irrlicht directly @@ -400,20 +399,28 @@ class TextureSource : public IWritableTextureSource // This should be only accessed from the main thread SourceImageCache m_sourcecache; + // Thread-safe cache of what source images are known (true = known) + MutexedMap m_source_image_existence; + // A texture id is index in this array. // The first position contains a NULL texture. - core::array m_atlaspointer_cache; + std::vector m_textureinfo_cache; // Maps a texture name to an index in the former. - core::map m_name_to_id; + std::map m_name_to_id; // The two former containers are behind this mutex - JMutex m_atlaspointer_cache_mutex; + JMutex m_textureinfo_cache_mutex; - // Main texture atlas. This is filled at startup and is then not touched. - video::IImage *m_main_atlas_image; - video::ITexture *m_main_atlas_texture; - // Queued texture fetches (to be processed by the main thread) RequestQueue m_get_texture_queue; + + // Textures that have been overwritten with other ones + // but can't be deleted because the ITexture* might still be used + std::list m_texture_trash; + + // Cached settings needed for making textures from meshes + bool m_setting_trilinear_filter; + bool m_setting_bilinear_filter; + bool m_setting_anisotropic_filter; }; IWritableTextureSource* createTextureSource(IrrlichtDevice *device) @@ -422,23 +429,58 @@ IWritableTextureSource* createTextureSource(IrrlichtDevice *device) } TextureSource::TextureSource(IrrlichtDevice *device): - m_device(device), - m_main_atlas_image(NULL), - m_main_atlas_texture(NULL) + m_device(device) { assert(m_device); - m_atlaspointer_cache_mutex.Init(); + m_textureinfo_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("")); + // Add a NULL TextureInfo as the first index, named "" + m_textureinfo_cache.push_back(TextureInfo("")); m_name_to_id[""] = 0; + + // Cache some settings + // Note: Since this is only done once, the game must be restarted + // for these settings to take effect + m_setting_trilinear_filter = g_settings->getBool("trilinear_filter"); + m_setting_bilinear_filter = g_settings->getBool("bilinear_filter"); + m_setting_anisotropic_filter = g_settings->getBool("anisotropic_filter"); } TextureSource::~TextureSource() { + video::IVideoDriver* driver = m_device->getVideoDriver(); + + unsigned int textures_before = driver->getTextureCount(); + + for (std::vector::iterator iter = + m_textureinfo_cache.begin(); + iter != m_textureinfo_cache.end(); iter++) + { + //cleanup texture + if (iter->texture) + driver->removeTexture(iter->texture); + + //cleanup source image + if (iter->img) + iter->img->drop(); + } + m_textureinfo_cache.clear(); + + for (std::list::iterator iter = + m_texture_trash.begin(); iter != m_texture_trash.end(); + iter++) + { + video::ITexture *t = *iter; + + //cleanup trashed texture + driver->removeTexture(t); + } + + infostream << "~TextureSource() "<< textures_before << "/" + << driver->getTextureCount() << std::endl; } u32 TextureSource::getTextureId(const std::string &name) @@ -449,12 +491,12 @@ u32 TextureSource::getTextureId(const std::string &name) /* See if texture already exists */ - JMutexAutoLock lock(m_atlaspointer_cache_mutex); - core::map::Node *n; + JMutexAutoLock lock(m_textureinfo_cache_mutex); + std::map::iterator n; n = m_name_to_id.find(name); - if(n != NULL) + if(n != m_name_to_id.end()) { - return n->getValue(); + return n->second; } } @@ -501,14 +543,21 @@ u32 TextureSource::getTextureId(const std::string &name) return 0; } -// Overlay image on top of another image (used for cracks) -void overlay(video::IImage *image, video::IImage *overlay); - // Draw an image on top of an another one, using the alpha channel of the // source image static void blit_with_alpha(video::IImage *src, video::IImage *dst, v2s32 src_pos, v2s32 dst_pos, v2u32 size); +// Like blit_with_alpha, but only modifies destination pixels that +// are fully opaque +static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst, + v2s32 src_pos, v2s32 dst_pos, v2u32 size); + +// Draw or overlay a crack +static void draw_crack(video::IImage *crack, video::IImage *dst, + bool use_overlay, s32 frame_count, s32 progression, + video::IVideoDriver *driver); + // Brighten image void brighten(video::IImage *image); // Parse a transform name @@ -518,31 +567,6 @@ core::dimension2d imageTransformDimension(u32 transform, core::dimension2d< // Apply transform to image data void imageTransform(u32 transform, video::IImage *src, video::IImage *dst); -/* - Adds a new texture to the video driver and returns a pointer to it. - This pointer should not be dropped. Any texture that was registered - with that name before is removed (this may invalidate some ITexture - pointers). -*/ -video::ITexture* register_texture(video::IVideoDriver *driver, - std::string name, video::IImage *img); - -/* - Generate image based on a string like "stone.png" or "[crack0". - if baseimg is NULL, it is created. Otherwise stuff is made on it. -*/ -bool generate_image(std::string part_of_name, video::IImage *& baseimg, - IrrlichtDevice *device, SourceImageCache *sourcecache); - -/* - Generates an image from a full string like - "stone.png^mineral_coal.png^[crack0". - - This is used by buildMainAtlas(). -*/ -video::IImage* generate_image_from_scratch(std::string name, - IrrlichtDevice *device, SourceImageCache *sourcecache); - /* This method generates all the textures */ @@ -571,15 +595,15 @@ u32 TextureSource::getTextureIdDirect(const std::string &name) See if texture already exists */ { - JMutexAutoLock lock(m_atlaspointer_cache_mutex); + JMutexAutoLock lock(m_textureinfo_cache_mutex); - core::map::Node *n; + std::map::iterator n; n = m_name_to_id.find(name); - if(n != NULL) + if(n != m_name_to_id.end()) { /*infostream<<"getTextureIdDirect(): \""<getValue(); + return n->second; } } @@ -639,13 +663,11 @@ u32 TextureSource::getTextureIdDirect(const std::string &name) // If a base image was found, copy it to baseimg if(base_image_id != 0) { - JMutexAutoLock lock(m_atlaspointer_cache_mutex); + JMutexAutoLock lock(m_textureinfo_cache_mutex); - SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id]; - - video::IImage *image = ap.atlas_img; + TextureInfo *ti = &m_textureinfo_cache[base_image_id]; - if(image == NULL) + if(ti->img == NULL) { infostream<<"getTextureIdDirect(): WARNING: NULL image in " <<"cache: \""< dim = ap.intsize; + core::dimension2d dim = ti->img->getDimension(); baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); - core::position2d pos_to(0,0); - core::position2d pos_from = ap.intpos; - - image->copyTo( + ti->img->copyTo( baseimg, // target v2s32(0,0), // position in target - core::rect(pos_from, dim) // from + core::rect(v2s32(0,0), dim) // from ); /*infostream<<"getTextureIdDirect(): Loaded \"" @@ -681,7 +700,7 @@ u32 TextureSource::getTextureIdDirect(const std::string &name) //infostream<<"last_part_of_name=\""<addTexture(name.c_str(), baseimg); + } /* Add texture to caches (add NULL textures too) */ - JMutexAutoLock lock(m_atlaspointer_cache_mutex); + JMutexAutoLock lock(m_textureinfo_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 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); + u32 id = m_textureinfo_cache.size(); + TextureInfo ti(name, t, baseimg); + m_textureinfo_cache.push_back(ti); + m_name_to_id[name] = id; /*infostream<<"getTextureIdDirect(): " <<"Returning id="<= m_atlaspointer_cache.size()) + if(id >= m_textureinfo_cache.size()) { errorstream<<"TextureSource::getTextureName(): id="<= m_atlaspointer_cache.size()=" - <= m_textureinfo_cache.size()=" + <= m_atlaspointer_cache.size()) - return AtlasPointer(0, NULL); - - return m_atlaspointer_cache[id].a; + if(id >= m_textureinfo_cache.size()) + return NULL; + + return m_textureinfo_cache[id].texture; } -void TextureSource::updateAP(AtlasPointer &ap) +video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id) { - AtlasPointer ap2 = getTexture(ap.id); - ap = ap2; + u32 actual_id = getTextureId(name); + if(id){ + *id = actual_id; + } + return getTexture(actual_id); } void TextureSource::processQueue() @@ -761,7 +776,7 @@ void TextureSource::processQueue() /* Fetch textures */ - if(m_get_texture_queue.size() > 0) + if(!m_get_texture_queue.empty()) { GetRequest request = m_get_texture_queue.pop(); @@ -788,305 +803,117 @@ void TextureSource::insertSourceImage(const std::string &name, video::IImage *im assert(get_current_thread_id() == m_main_thread); m_sourcecache.insert(name, img, true, m_device->getVideoDriver()); + m_source_image_existence.set(name, true); } - + 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();*/ + JMutexAutoLock lock(m_textureinfo_cache_mutex); video::IVideoDriver* driver = m_device->getVideoDriver(); - - // Remove source images from textures to disable inheriting textures - // from existing textures - /*for(u32 i=0; iatlas_img->drop(); - sap->atlas_img = NULL; - }*/ - + // Recreate textures - for(u32 i=0; iname, m_device, &m_sourcecache); + for(u32 i=0; iname); // Create texture from resulting image video::ITexture *t = NULL; if(img) - t = register_texture(driver, sap->name, img); - + t = driver->addTexture(ti->name.c_str(), img); + video::ITexture *t_old = ti->texture; // 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(); + ti->texture = t; + ti->img = img; + + if (t_old != 0) + m_texture_trash.push_back(t_old); } } -void TextureSource::buildMainAtlas(class IGameDef *gamedef) +video::ITexture* TextureSource::generateTextureFromMesh( + const TextureFromMeshParams ¶ms) { - assert(gamedef->tsrc() == this); - INodeDefManager *ndef = gamedef->ndef(); - - infostream<<"TextureSource::buildMainAtlas()"<getVideoDriver(); + video::IVideoDriver *driver = m_device->getVideoDriver(); assert(driver); - JMutexAutoLock lock(m_atlaspointer_cache_mutex); - - // Create an image of the right size - core::dimension2d max_dim = driver->getMaxTextureSize(); - core::dimension2d atlas_dim(2048,2048); - atlas_dim.Width = MYMIN(atlas_dim.Width, max_dim.Width); - atlas_dim.Height = MYMIN(atlas_dim.Height, max_dim.Height); - 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."< sourcelist; - - for(u16 j=0; jqueryFeature(video::EVDF_RENDER_TO_TARGET) == false) { - if(j == CONTENT_IGNORE || j == CONTENT_AIR) - continue; - const ContentFeatures &f = ndef->get(j); - for(u32 i=0; i<6; i++) + static bool warned = false; + if(!warned) { - std::string name = f.tiledef[i].name; - sourcelist[name] = true; + errorstream<<"TextureSource::generateTextureFromMesh(): " + <<"EVDF_RENDER_TO_TARGET not supported."<::Iterator - i = sourcelist.getIterator(); - i.atEnd() == false; i++) - { - std::string name = i.getNode()->getKey(); - infostream<<"\""< pos_in_atlas(0,0); - - pos_in_atlas.X = column_padding; - pos_in_atlas.Y = padding; - - for(core::map::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 \""< dim = img2->getDimension(); - - // Don't add to atlas if image is too large - core::dimension2d max_size_in_atlas(64,64); - if(dim.Width > max_size_in_atlas.Width - || dim.Height > max_size_in_atlas.Height) - { - infostream<<"TextureSource::buildMainAtlas(): Not adding " - <<"\""< atlas_dim.Height) - { - if(pos_in_atlas.X > (s32)atlas_dim.Width - column_width - column_padding){ - errorstream<<"TextureSource::buildMainAtlas(): " - <<"Atlas is full, not adding more textures." - < 16) // Limit to 16 (more gives no benefit) - 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->copyTo(atlas_img, - pos_in_atlas + v2s32(j*dim.Width,0), - core::rect(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; y0getPixel(x, src_y); - atlas_img->setPixel(x,dst_y,c); - } - - for(u32 side=0; side<2; side++) // left and right - for(s32 x0=0; x0getPixel(src_x, src_y); - atlas_img->setPixel(dst_x,dst_y,c); - } - - img2->drop(); - - /* - Add texture to caches - */ - - bool reuse_old_id = false; - u32 id = m_atlaspointer_cache.size(); - // Check old id without fetching a texture - core::map::Node *n; - n = m_name_to_id.find(name); - // If it exists, we will replace the old definition - if(n){ - id = n->getValue(); - reuse_old_id = true; - /*infostream<<"TextureSource::buildMainAtlas(): " - <<"Replacing old AtlasPointer"<::Iterator - i = sourcelist.getIterator(); - i.atEnd() == false; i++) + // Create render target texture + video::ITexture *rtt = driver->addRenderTargetTexture( + params.dim, params.rtt_texture_name.c_str(), + video::ECF_A8R8G8B8); + if(rtt == NULL) { - std::string name = i.getNode()->getKey(); - if(m_name_to_id.find(name) == NULL) - continue; - u32 id = m_name_to_id[name]; - //infostream<<"id of name "<writeImageToFile(atlas_img, atlaspath.c_str());*/ + // Set render target + driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0)); + + // Get a scene manager + scene::ISceneManager *smgr_main = m_device->getSceneManager(); + assert(smgr_main); + scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); + assert(smgr); + + scene::IMeshSceneNode* meshnode = smgr->addMeshSceneNode(params.mesh, NULL, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); + meshnode->setMaterialFlag(video::EMF_LIGHTING, true); + meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); + meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter); + meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter); + meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter); + + scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, + params.camera_position, params.camera_lookat); + // second parameter of setProjectionMatrix (isOrthogonal) is ignored + camera->setProjectionMatrix(params.camera_projection_matrix, false); + + smgr->setAmbientLight(params.ambient_light); + smgr->addLightSceneNode(0, + params.light_position, + params.light_color, + params.light_radius); + + // Render scene + driver->beginScene(true, true, video::SColor(0,0,0,0)); + smgr->drawAll(); + driver->endScene(); + + // NOTE: The scene nodes should not be dropped, otherwise + // smgr->drop() segfaults + /*cube->drop(); + camera->drop(); + light->drop();*/ + // Drop scene manager + smgr->drop(); + + // Unset render target + driver->setRenderTarget(0, false, true, 0); + + if(params.delete_texture_on_shutdown) + m_texture_trash.push_back(rtt); + + return rtt; } -video::IImage* generate_image_from_scratch(std::string name, - IrrlichtDevice *device, SourceImageCache *sourcecache) +video::IImage* TextureSource::generateImageFromScratch(std::string name) { - /*infostream<<"generate_image_from_scratch(): " + /*infostream<<"generateImageFromScratch(): " "\""<getVideoDriver(); + + video::IVideoDriver *driver = m_device->getVideoDriver(); assert(driver); /* @@ -1099,12 +926,6 @@ video::IImage* generate_image_from_scratch(std::string name, // Find last meta separator in name s32 last_separator_position = name.find_last_of(separator); - //if(last_separator_position == std::npos) - // last_separator_position = -1; - - /*infostream<<"generate_image_from_scratch(): " - <<"last_separator_position="<findTexture(name.c_str()); - if(old_texture) - driver->removeTexture(old_texture); - return driver->addTexture(name.c_str(), img); -} - -bool generate_image(std::string part_of_name, video::IImage *& baseimg, - IrrlichtDevice *device, SourceImageCache *sourcecache) -{ - video::IVideoDriver* driver = device->getVideoDriver(); + video::IVideoDriver* driver = m_device->getVideoDriver(); assert(driver); // Stuff starting with [ are special commands if(part_of_name.size() == 0 || part_of_name[0] != '[') { - video::IImage *image = sourcecache->getOrLoad(part_of_name, device); + video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device); if(image == NULL) { if(part_of_name != ""){ - errorstream<<"generate_image(): Could not load image \"" + errorstream<<"generateImage(): Could not load image \"" < dim = image->getDimension(); baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); image->copyTo(baseimg); - image->drop(); } // Else blit on base. else @@ -1221,131 +1026,55 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, video::SColor(255,255,255,255), NULL);*/ blit_with_alpha(image, baseimg, pos_from, pos_to, dim); - // Drop image - image->drop(); } + //cleanup + image->drop(); } else { // A special texture modification - /*infostream<<"generate_image(): generating special " + /*infostream<<"generateImage(): generating special " <<"modification \""< dim(1,1); - baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); - assert(baseimg); - baseimg->setPixel(0,0, video::SColor(255,myrand()%256, - myrand()%256,myrand()%256)); - } - } - /* - [crackN + [crack:N:P + [cracko:N:P Adds a cracking texture + N = animation frame count, P = crack progression */ - else if(part_of_name.substr(0,6) == "[crack") + if(part_of_name.substr(0,6) == "[crack") { if(baseimg == NULL) { - errorstream<<"generate_image(): baseimg==NULL " + errorstream<<"generateImage(): baseimg==NULL " <<"for part_of_name=\""< dim_base = baseimg->getDimension(); - /* Load crack image. It is an image with a number of cracking stages horizontally tiled. */ - video::IImage *img_crack = sourcecache->getOrLoad( - "crack_anylength.png", device); - + video::IImage *img_crack = m_sourcecache.getOrLoad( + "crack_anylength.png", m_device); + if(img_crack && progression >= 0) { - // Dimension of original image - core::dimension2d dim_crack - = img_crack->getDimension(); - // Count of crack stages - s32 crack_count = dim_crack.Height / dim_crack.Width; - // Limit progression - if(progression > crack_count-1) - progression = crack_count-1; - // Dimension of a single crack stage - core::dimension2d dim_crack_cropped( - dim_crack.Width, - dim_crack.Width - ); - // Create cropped and scaled crack images - video::IImage *img_crack_cropped = driver->createImage( - video::ECF_A8R8G8B8, dim_crack_cropped); - video::IImage *img_crack_scaled = driver->createImage( - video::ECF_A8R8G8B8, dim_base); - - if(img_crack_cropped && img_crack_scaled) - { - // Crop crack image - v2s32 pos_crack(0, progression*dim_crack.Width); - img_crack->copyTo(img_crack_cropped, - v2s32(0,0), - core::rect(pos_crack, dim_crack_cropped)); - // Scale crack image by copying - img_crack_cropped->copyToScaling(img_crack_scaled); - // Copy or overlay crack image - if(use_overlay) - { - overlay(baseimg, img_crack_scaled); - } - else - { - /*img_crack_scaled->copyToWithAlpha( - baseimg, - v2s32(0,0), - core::rect(v2s32(0,0), dim_base), - video::SColor(255,255,255,255));*/ - blit_with_alpha(img_crack_scaled, baseimg, - v2s32(0,0), v2s32(0,0), dim_base); - } - } - - if(img_crack_scaled) - img_crack_scaled->drop(); - - if(img_crack_cropped) - img_crack_cropped->drop(); - + draw_crack(img_crack, baseimg, + use_overlay, frame_count, + progression, driver); img_crack->drop(); } } @@ -1374,7 +1103,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, infostream<<"Adding \""<getOrLoad(filename, device); + video::IImage *img = m_sourcecache.getOrLoad(filename, m_device); if(img) { core::dimension2d dim = img->getDimension(); @@ -1405,7 +1134,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, { if(baseimg == NULL) { - errorstream<<"generate_image(): baseimg==NULL " + errorstream<<"generateImage(): baseimg==NULL " <<"for part_of_name=\""<addTexture( + (imagename_top + "__temp__").c_str(), img_top); + video::ITexture *texture_left = driver->addTexture( + (imagename_left + "__temp__").c_str(), img_left); + video::ITexture *texture_right = driver->addTexture( + (imagename_right + "__temp__").c_str(), img_right); assert(texture_top && texture_left && texture_right); // Drop images @@ -1582,31 +1311,25 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left); cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left); - core::dimension2d dim(64,64); - std::string rtt_texture_name = part_of_name + "_RTT"; - - v3f camera_position(0, 1.0, -1.5); - camera_position.rotateXZBy(45); - v3f camera_lookat(0, 0, 0); - core::CMatrix4 camera_projection_matrix; + TextureFromMeshParams params; + params.mesh = cube; + params.dim.set(64, 64); + params.rtt_texture_name = part_of_name + "_RTT"; + // We will delete the rtt texture ourselves + params.delete_texture_on_shutdown = false; + params.camera_position.set(0, 1.0, -1.5); + params.camera_position.rotateXZBy(45); + params.camera_lookat.set(0, 0, 0); // Set orthogonal projection - camera_projection_matrix.buildProjectionMatrixOrthoLH( + params.camera_projection_matrix.buildProjectionMatrixOrthoLH( 1.65, 1.65, 0, 100); - video::SColorf ambient_light(0.2,0.2,0.2); - v3f light_position(10, 100, -50); - video::SColorf light_color(0.5,0.5,0.5); - f32 light_radius = 1000; - - video::ITexture *rtt = generateTextureFromMesh( - cube, device, dim, rtt_texture_name, - camera_position, - camera_lookat, - camera_projection_matrix, - ambient_light, - light_position, - light_color, - light_radius); + params.ambient_light.set(1.0, 0.2, 0.2, 0.2); + params.light_position.set(10, 100, -50); + params.light_color.set(1.0, 0.5, 0.5, 0.5); + params.light_radius = 1000; + + video::ITexture *rtt = generateTextureFromMesh(params); // Drop mesh cube->drop(); @@ -1618,16 +1341,18 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, if(rtt == NULL) { - baseimg = generate_image_from_scratch( - imagename_top, device, sourcecache); + baseimg = generateImageFromScratch(imagename_top); return true; } // Create image of render target - video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim); + video::IImage *image = driver->createImage(rtt, v2s32(0,0), params.dim); assert(image); - baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + // Cleanup texture + driver->removeTexture(rtt); + + baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim); if(image) { @@ -1649,7 +1374,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, if(baseimg == NULL) baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16)); - video::IImage *img = sourcecache->getOrLoad(filename, device); + video::IImage *img = m_sourcecache.getOrLoad(filename, m_device); if(img) { core::dimension2d dim = img->getDimension(); @@ -1683,7 +1408,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, u32 frame_index = stoi(sf.next(":")); if(baseimg == NULL){ - errorstream<<"generate_image(): baseimg!=NULL " + errorstream<<"generateImage(): baseimg!=NULL " <<"for part_of_name=\""<createImage(video::ECF_A8R8G8B8, frame_size); if(!img){ - errorstream<<"generate_image(): Could not create image " + errorstream<<"generateImage(): Could not create image " <<"for part_of_name=\""< dim = image->getDimension(); - core::dimension2d dim_overlay = overlay->getDimension(); - assert(dim == dim_overlay); - - for(u32 y=0; ygetPixel(x,y); - video::SColor c2 = overlay->getPixel(x,y); - u32 a1 = c1.getAlpha(); - u32 a2 = c2.getAlpha(); - if(a1 == 255 && a2 != 0) - { - c1.setRed((c1.getRed()*(255-a2) + c2.getRed()*a2)/255); - c1.setGreen((c1.getGreen()*(255-a2) + c2.getGreen()*a2)/255); - c1.setBlue((c1.getBlue()*(255-a2) + c2.getBlue()*a2)/255); - } - image->setPixel(x,y,c1); - } -} - /* Draw an image on top of an another one, using the alpha channel of the source image @@ -1780,6 +1474,100 @@ static void blit_with_alpha(video::IImage *src, video::IImage *dst, } } +/* + Draw an image on top of an another one, using the alpha channel of the + source image; only modify fully opaque pixels in destinaion +*/ +static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst, + v2s32 src_pos, v2s32 dst_pos, v2u32 size) +{ + for(u32 y0=0; y0getPixel(src_x, src_y); + video::SColor dst_c = dst->getPixel(dst_x, dst_y); + if(dst_c.getAlpha() == 255 && src_c.getAlpha() != 0) + { + dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f); + dst->setPixel(dst_x, dst_y, dst_c); + } + } +} + +static void draw_crack(video::IImage *crack, video::IImage *dst, + bool use_overlay, s32 frame_count, s32 progression, + video::IVideoDriver *driver) +{ + // Dimension of destination image + core::dimension2d dim_dst = dst->getDimension(); + // Dimension of original image + core::dimension2d dim_crack = crack->getDimension(); + // Count of crack stages + s32 crack_count = dim_crack.Height / dim_crack.Width; + // Limit frame_count + if(frame_count > (s32) dim_dst.Height) + frame_count = dim_dst.Height; + if(frame_count < 1) + frame_count = 1; + // Limit progression + if(progression > crack_count-1) + progression = crack_count-1; + // Dimension of a single crack stage + core::dimension2d dim_crack_cropped( + dim_crack.Width, + dim_crack.Width + ); + // Dimension of the scaled crack stage, + // which is the same as the dimension of a single destination frame + core::dimension2d dim_crack_scaled( + dim_dst.Width, + dim_dst.Height / frame_count + ); + // Create cropped and scaled crack images + video::IImage *crack_cropped = driver->createImage( + video::ECF_A8R8G8B8, dim_crack_cropped); + video::IImage *crack_scaled = driver->createImage( + video::ECF_A8R8G8B8, dim_crack_scaled); + + if(crack_cropped && crack_scaled) + { + // Crop crack image + v2s32 pos_crack(0, progression*dim_crack.Width); + crack->copyTo(crack_cropped, + v2s32(0,0), + core::rect(pos_crack, dim_crack_cropped)); + // Scale crack image by copying + crack_cropped->copyToScaling(crack_scaled); + // Copy or overlay crack image onto each frame + for(s32 i = 0; i < frame_count; ++i) + { + v2s32 dst_pos(0, dim_crack_scaled.Height * i); + if(use_overlay) + { + blit_with_alpha_overlay(crack_scaled, dst, + v2s32(0,0), dst_pos, + dim_crack_scaled); + } + else + { + blit_with_alpha(crack_scaled, dst, + v2s32(0,0), dst_pos, + dim_crack_scaled); + } + } + } + + if(crack_scaled) + crack_scaled->drop(); + + if(crack_cropped) + crack_cropped->drop(); +} + void brighten(video::IImage *image) { if(image == NULL)