X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftile.cpp;h=4af7a327273d0059ad32aecdff3416836f6d2132;hb=194258b4794ec13b9ffa7b5f37a3060ecc0e59b3;hp=73f2a85ea5c61bb545bf9964a28bbf248155a4ff;hpb=71418639d3e81a0c4f03633042f9c3472572a87e;p=oweals%2Fminetest.git diff --git a/src/tile.cpp b/src/tile.cpp index 73f2a85ea..4af7a3272 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -22,6 +22,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "main.h" // for g_settings #include "filesys.h" #include "utility.h" +#include "settings.h" +#include "mesh.h" +#include +#include "log.h" +#include "mapnode.h" // For texture atlas making +#include "nodedef.h" // For texture atlas making +#include "gamedef.h" /* A cache from texture name to texture path @@ -112,10 +119,10 @@ std::string getTexturePath(const std::string &filename) /* Check from texture_path */ - std::string texture_path = g_settings.get("texture_path"); + std::string texture_path = g_settings->get("texture_path"); if(texture_path != "") { - std::string testpath = texture_path + '/' + filename; + std::string testpath = texture_path + DIR_DELIM + filename; // Check all filename extensions. Returns "" if not found. fullpath = getImagePath(testpath); } @@ -125,7 +132,9 @@ std::string getTexturePath(const std::string &filename) */ if(fullpath == "") { - std::string testpath = porting::getDataPath(filename.c_str()); + std::string base_path = porting::path_share + DIR_DELIM + "textures" + + DIR_DELIM + "base" + DIR_DELIM + "pack"; + std::string testpath = base_path + DIR_DELIM + filename; // Check all filename extensions. Returns "" if not found. fullpath = getImagePath(testpath); } @@ -137,10 +146,263 @@ std::string getTexturePath(const std::string &filename) return fullpath; } +/* + An internal variant of AtlasPointer with more data. + (well, more like a wrapper) +*/ + +struct SourceAtlasPointer +{ + 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; + + SourceAtlasPointer( + const std::string &name_, + AtlasPointer a_=AtlasPointer(0, NULL), + video::IImage *atlas_img_=NULL, + v2s32 intpos_=v2s32(0,0), + v2u32 intsize_=v2u32(0,0) + ): + name(name_), + a(a_), + atlas_img(atlas_img_), + intpos(intpos_), + intsize(intsize_) + { + } +}; + +/* + SourceImageCache: A cache used for storing source images. +*/ + +class SourceImageCache +{ +public: + void insert(const std::string &name, video::IImage *img, + bool prefer_local, video::IVideoDriver *driver) + { + assert(img); + // Remove old image + core::map::Node *n; + n = m_images.find(name); + if(n){ + video::IImage *oldimg = n->getValue(); + if(oldimg) + oldimg->drop(); + } + // 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; + } + } + } + img->grab(); + m_images[name] = img; + } + video::IImage* get(const std::string &name) + { + core::map::Node *n; + n = m_images.find(name); + if(n) + return n->getValue(); + 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; + n = m_images.find(name); + if(n){ + n->getValue()->grab(); // Grab for caller + return n->getValue(); + } + video::IVideoDriver* driver = device->getVideoDriver(); + std::string path = getTexturePath(name.c_str()); + if(path == ""){ + infostream<<"SourceImageCache::getOrLoad(): No path found for \"" + <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 + } + return img; + } +private: + core::map m_images; +}; + /* TextureSource */ +class TextureSource : public IWritableTextureSource +{ +public: + TextureSource(IrrlichtDevice *device); + ~TextureSource(); + + /* + Example case: + Now, assume a texture with the id 1 exists, and has the name + "stone.png^mineral1". + Then a random thread calls getTextureId for a texture called + "stone.png^mineral1^crack0". + ...Now, WTF should happen? Well: + - getTextureId strips off stuff recursively from the end until + the remaining part is found, or nothing is left when + something is stripped out + + But it is slow to search for textures by names and modify them + like that? + - ContentFeatures is made to contain ids for the basic plain + textures + - Crack textures can be slow by themselves, but the framework + must be fast. + + 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. + - Now getNodeTile() stumbles upon a node which uses + texture id 1, and determines that MATERIAL_FLAG_CRACK + must be applied to the tile + - MapBlockMesh::animate() finds the MATERIAL_FLAG_CRACK and + 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"). + + */ + + /* + Gets a texture id from cache or + - if main thread, from getTextureIdDirect + - if other thread, adds to request queue and waits for main thread + */ + u32 getTextureId(const std::string &name); + + /* + Example names: + "stone.png" + "stone.png^crack2" + "stone.png^mineral_coal.png" + "stone.png^mineral_coal.png^crack1" + + - If texture specified by name is found from cache, return the + cached id. + - Otherwise generate the texture, add to cache and return id. + Recursion is used to find out the largest found part of the + texture and continue based on it. + + The id 0 points to a NULL texture. It is returned in case of error. + */ + u32 getTextureIdDirect(const std::string &name); + + // Finds out the name of a cached texture. + std::string getTextureName(u32 id); + + /* + If texture specified by the name pointed by the id doesn't + exist, create it, then return the cached texture. + + Can be called from any thread. If called from some other thread + 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; + } + + // Gets a separate texture atlas pointer + AtlasPointer getTextureRawAP(const AtlasPointer &ap) + { + return getTexture(getTextureName(ap.id) + "^[forcesingle"); + } + + // Returns a pointer to the irrlicht device + virtual IrrlichtDevice* getDevice() + { + return m_device; + } + + // Update new texture pointer and texture coordinates to an + // AtlasPointer based on it's texture id + void updateAP(AtlasPointer &ap); + + // Processes queued texture requests from other threads. + // Shall be called from the main thread. + void processQueue(); + + // Insert an image into the cache without touching the filesystem. + // Shall be called from the main thread. + void insertSourceImage(const std::string &name, video::IImage *img); + + // 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); + +private: + + // The id of the thread that is allowed to use irrlicht directly + threadid_t m_main_thread; + // The irrlicht device + IrrlichtDevice *m_device; + + // Cache of source images + // This should be only accessed from the main thread + SourceImageCache m_sourcecache; + + // A texture id is index in this array. + // The first position contains a NULL texture. + core::array m_atlaspointer_cache; + // Maps a texture name to an index in the former. + core::map m_name_to_id; + // The two former containers are behind this mutex + JMutex m_atlaspointer_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; +}; + +IWritableTextureSource* createTextureSource(IrrlichtDevice *device) +{ + return new TextureSource(device); +} + TextureSource::TextureSource(IrrlichtDevice *device): m_device(device), m_main_atlas_image(NULL), @@ -155,46 +417,15 @@ TextureSource::TextureSource(IrrlichtDevice *device): // Add a NULL AtlasPointer as the first index, named "" m_atlaspointer_cache.push_back(SourceAtlasPointer("")); m_name_to_id[""] = 0; - - // Build main texture atlas - if(g_settings.getBool("enable_texture_atlas")) - buildMainAtlas(); - else - dstream<<"INFO: Not building texture atlas."< 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(): \""< result_queue; @@ -226,7 +457,7 @@ u32 TextureSource::getTextureId(const std::string &name) // Throw a request in m_get_texture_queue.add(name, 0, 0, &result_queue); - dstream<<"INFO: Waiting for texture from main thread, name=\"" + infostream<<"Waiting for texture from main thread, name=\"" <getValue(); } } - dstream<<"INFO: getTextureIdDirect(): \""<(pos_from, dim) // from ); - /*dstream<<"INFO: getTextureIdDirect(): Loaded \"" + /*infostream<<"getTextureIdDirect(): Loaded \"" <= m_atlaspointer_cache.size()) { - dstream<<"WARNING: TextureSource::getTextureName(): id="<= m_atlaspointer_cache.size()=" < 0) + { + GetRequest + request = m_get_texture_queue.pop(); + + /*infostream<<"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); + } +} + +void TextureSource::insertSourceImage(const std::string &name, video::IImage *img) +{ + //infostream<<"TextureSource::insertSourceImage(): name="<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; iatlas_img->drop(); + sap->atlas_img = NULL; + }*/ + + // Recreate textures + for(u32 i=0; iname, 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) { - dstream<<"TextureSource::buildMainAtlas()"<tsrc() == this); + INodeDefManager *ndef = gamedef->ndef(); + + infostream<<"TextureSource::buildMainAtlas()"< 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("glass.png"); - sourcelist.push_back("mud.png^grass_side.png"); - 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"); - sourcelist.push_back("stone.png^mineral_iron.png"); + core::map sourcelist; + + for(u16 j=0; jget(j); + for(u32 i=0; i<6; i++) + { + std::string name = f.tname_tiles[i]; + sourcelist[name] = true; + } + } + infostream<<"Creating texture atlas out of textures: "; + for(core::map::Iterator + i = sourcelist.getIterator(); + i.atEnd() == false; i++) + { + std::string name = i.getNode()->getKey(); + infostream<<"\""< pos_in_atlas(0,0); - pos_in_atlas.Y += padding; + pos_in_atlas.Y = padding; - for(u32 i=0; i::Iterator + i = sourcelist.getIterator(); + i.atEnd() == false; i++) { - std::string name = sourcelist[i]; + std::string name = i.getNode()->getKey(); - /*video::IImage *img = driver->createImageFromFile( - getTexturePath(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, m_device); + video::IImage *img2 = generate_image_from_scratch(name, m_device, + &m_sourcecache); if(img2 == NULL) { - dstream<<"WARNING: TextureSource::buildMainAtlas(): Couldn't generate texture atlas: Couldn't generate image \""< max_size_in_atlas.Width || dim.Height > max_size_in_atlas.Height) { - dstream<<"INFO: TextureSource::buildMainAtlas(): Not adding " + infostream<<"TextureSource::buildMainAtlas(): Not adding " <<"\""< atlas_dim.Height) { - dstream<<"WARNING: TextureSource::buildMainAtlas(): " - <<"Atlas is full, not adding more textures." - < (s32)atlas_dim.Width - 256 - 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, + /*img2->copyToWithAlpha(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); } @@ -624,7 +940,7 @@ void TextureSource::buildMainAtlas() dst_y = -y0 + pos_in_atlas.Y-1; 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); atlas_img->setPixel(x,dst_y,c); } @@ -635,8 +951,18 @@ void TextureSource::buildMainAtlas() Add texture to caches */ - // Get next id + 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++) { - std::string name = sourcelist[i]; + std::string name = i.getNode()->getKey(); if(m_name_to_id.find(name) == NULL) continue; u32 id = m_name_to_id[name]; - //dstream<<"id of name "<writeImageToFile(atlas_img, - getTexturePath("main_atlas.png").c_str());*/ + /*std::string atlaspath = porting::path_user + + DIR_DELIM + "generated_texture_atlas.png"; + infostream<<"Removing and writing texture atlas for inspection to " + <writeImageToFile(atlas_img, atlaspath.c_str());*/ } video::IImage* generate_image_from_scratch(std::string name, - IrrlichtDevice *device) + IrrlichtDevice *device, SourceImageCache *sourcecache) { - /*dstream<<"INFO: generate_image_from_scratch(): " + /*infostream<<"generate_image_from_scratch(): " "\""<getVideoDriver(); @@ -700,17 +1035,11 @@ video::IImage* generate_image_from_scratch(std::string name, char separator = '^'; // Find last meta separator in name - s32 last_separator_position = -1; - for(s32 i=name.size()-1; i>=0; i--) - { - if(name[i] == separator) - { - last_separator_position = i; - break; - } - } + s32 last_separator_position = name.find_last_of(separator); + //if(last_separator_position == std::npos) + // last_separator_position = -1; - /*dstream<<"INFO: generate_image_from_scratch(): " + /*infostream<<"generate_image_from_scratch(): " <<"last_separator_position="<getVideoDriver(); assert(driver); // Stuff starting with [ are special commands - if(part_of_name[0] != '[') + if(part_of_name.size() == 0 || part_of_name[0] != '[') { - // A normal texture; load it from a file - std::string path = getTexturePath(part_of_name.c_str()); - /*dstream<<"INFO: generate_image(): Loading path \""<createImageFromFile(path.c_str()); + video::IImage *image = sourcecache->getOrLoad(part_of_name, device); if(image == NULL) { - dstream<<"WARNING: generate_image(): Could not load image \"" - < dim(2,2); @@ -798,7 +1121,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, // If base image is NULL, load as base. if(baseimg == NULL) { - //dstream<<"INFO: Setting "< dim = image->getDimension(); //core::dimension2d dim(16,16); @@ -833,9 +1156,9 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, { // A special texture modification - dstream<<"INFO: generate_image(): generating special " + /*infostream<<"generate_image(): 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 @@ -856,14 +1188,25 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, { if(baseimg == NULL) { - dstream<<"WARNING: generate_image(): baseimg==NULL " + errorstream<<"generate_image(): baseimg==NULL " <<"for part_of_name=\""< dim_base = baseimg->getDimension(); @@ -874,68 +1217,58 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, It is an image with a number of cracking stages horizontally tiled. */ - video::IImage *img_crack = driver->createImageFromFile( - getTexturePath("crack.png").c_str()); + video::IImage *img_crack = sourcecache->getOrLoad("crack.png", device); - if(img_crack) + if(img_crack && progression >= 0) { // Dimension of original image core::dimension2d dim_crack = img_crack->getDimension(); // Count of crack stages - u32 crack_count = dim_crack.Height / dim_crack.Width; + s32 crack_count = dim_crack.Height / dim_crack.Width; // Limit progression if(progression > crack_count-1) progression = crack_count-1; - // Dimension of a single scaled crack stage - core::dimension2d dim_crack_scaled_single( - dim_base.Width, - dim_base.Height + // Dimension of a single crack stage + core::dimension2d dim_crack_cropped( + dim_crack.Width, + dim_crack.Width ); - // Dimension of scaled size - core::dimension2d dim_crack_scaled( - dim_crack_scaled_single.Width, - dim_crack_scaled_single.Height * crack_count - ); - // Create scaled crack image + // 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_crack_scaled); - if(img_crack_scaled) + 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->copyToScaling(img_crack_scaled); - - // Position to copy the crack from - core::position2d pos_crack_scaled( - 0, - dim_crack_scaled_single.Height * progression - ); - - // This tiling does nothing currently but is useful - for(u32 y0=0; y0copyToScaling(img_crack_scaled); + // Copy or overlay crack image + if(use_overlay) + { + overlay(baseimg, img_crack_scaled); + } + else { - // Position to copy the crack to in the base image - core::position2d pos_base( - x0*dim_crack_scaled_single.Width, - y0*dim_crack_scaled_single.Height - ); - // Rectangle to copy the crack from on the scaled image - core::rect rect_crack_scaled( - pos_crack_scaled, - dim_crack_scaled_single - ); - // Copy it - img_crack_scaled->copyToWithAlpha(baseimg, pos_base, - rect_crack_scaled, - video::SColor(255,255,255,255), - NULL); + img_crack_scaled->copyToWithAlpha( + baseimg, + v2s32(0,0), + core::rect(v2s32(0,0), dim_base), + video::SColor(255,255,255,255)); } + } + if(img_crack_scaled) img_crack_scaled->drop(); - } + + if(img_crack_cropped) + img_crack_cropped->drop(); img_crack->drop(); } @@ -950,7 +1283,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, sf.next(":"); u32 w0 = stoi(sf.next("x")); u32 h0 = stoi(sf.next(":")); - dstream<<"INFO: combined w="<createImageFromFile( - getTexturePath(filename.c_str()).c_str()); + video::IImage *img = sourcecache->getOrLoad(filename, device); if(img) { core::dimension2d dim = img->getDimension(); - dstream<<"INFO: Size "< pos_base(x, y); video::IImage *img2 = @@ -981,75 +1313,92 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, } else { - dstream<<"WARNING: img==NULL"< dim = baseimg->getDimension(); - video::IImage *image = driver->createImageFromFile(path.c_str()); - - if(image == NULL) + // Set alpha to full + for(u32 y=0; ygetPixel(x,y); + c.setAlpha(255); + baseimg->setPixel(x,y,c); } - else + } + /* + "[makealpha:R,G,B" + Convert one color to transparent. + */ + else if(part_of_name.substr(0,11) == "[makealpha:") + { + if(baseimg == NULL) { - core::dimension2d 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); + errorstream<<"generate_image(): baseimg==NULL " + <<"for part_of_name=\""<drop(); + Strfnd sf(part_of_name.substr(11)); + u32 r1 = stoi(sf.next(",")); + u32 g1 = stoi(sf.next(",")); + u32 b1 = stoi(sf.next("")); + std::string filename = sf.next(""); + + core::dimension2d dim = baseimg->getDimension(); + + /*video::IImage *oldbaseimg = baseimg; + baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + oldbaseimg->copyTo(baseimg); + oldbaseimg->drop();*/ + + // Set alpha to full + for(u32 y=0; ygetPixel(x,y); + u32 r = c.getRed(); + u32 g = c.getGreen(); + u32 b = c.getBlue(); + if(!(r == r1 && g == g1 && b == b1)) + continue; + c.setAlpha(0); + baseimg->setPixel(x,y,c); } } /* @@ -1064,7 +1413,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, { if(baseimg != NULL) { - dstream<<"WARNING: generate_image(): baseimg!=NULL " + errorstream<<"generate_image(): baseimg!=NULL " <<"for part_of_name=\""<queryFeature(video::EVDF_RENDER_TO_TARGET) == false) - { - dstream<<"WARNING: generate_image(): EVDF_RENDER_TO_TARGET" - " not supported. Creating fallback image"<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 img_top->drop(); img_left->drop(); img_right->drop(); - // Create render target texture - video::ITexture *rtt = NULL; - std::string rtt_name = part_of_name + "_RTT"; - rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str(), - video::ECF_A8R8G8B8); - assert(rtt); - - // Set render target - driver->setRenderTarget(rtt, true, true, - video::SColor(0,0,0,0)); - - // Get a scene manager - scene::ISceneManager *smgr_main = device->getSceneManager(); - assert(smgr_main); - scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); - assert(smgr); - /* - Create scene: - - An unit cube is centered at 0,0,0 - - Camera looks at cube from Y+, Z- towards Y-, Z+ - NOTE: Cube has to be changed to something else because - the textures cannot be set individually (or can they?) + Draw a cube mesh into a render target texture */ - - scene::ISceneNode* cube = smgr->addCubeSceneNode(1.0, NULL, -1, - v3f(0,0,0), v3f(0, 45, 0)); - // Set texture of cube - cube->setMaterialTexture(0, texture_top); - //cube->setMaterialFlag(video::EMF_LIGHTING, false); - cube->setMaterialFlag(video::EMF_ANTI_ALIASING, false); - cube->setMaterialFlag(video::EMF_BILINEAR_FILTER, false); - - scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, - v3f(0, 1.0, -1.5), v3f(0, 0, 0)); + scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1)); + setMeshColor(cube, video::SColor(255, 255, 255, 255)); + cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top); + cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top); + cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right); + cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right); + 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; // Set orthogonal projection - core::CMatrix4 pm; - pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100); - camera->setProjectionMatrix(pm, true); - - /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0, - v3f(-50, 100, 0), video::SColorf(0.5,0.5,0.5), 1000); - - smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2)); - - // Render scene - driver->beginScene(true, true, video::SColor(0,0,0,0)); - smgr->drawAll(); - driver->endScene(); + 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); - // 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, true, true, 0); + // Drop mesh + cube->drop(); - //TODO: Free textures of images + // Free textures of images driver->removeTexture(texture_top); + driver->removeTexture(texture_left); + driver->removeTexture(texture_right); + if(rtt == NULL) + { + baseimg = generate_image_from_scratch( + imagename_top, device, sourcecache); + return true; + } + // Create image of render target video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim); - assert(image); - + baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); if(image) @@ -1189,11 +1513,10 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, image->copyTo(baseimg); image->drop(); } -#endif } else { - dstream<<"WARNING: generate_image(): Invalid " + errorstream<<"generate_image(): Invalid " " modification: \""< size = image->getDimension(); + core::dimension2d dim = image->getDimension(); + core::dimension2d dim_overlay = overlay->getDimension(); + assert(dim == dim_overlay); - u32 barheight = size.Height/16; - u32 barpad_x = size.Width/16; - u32 barpad_y = size.Height/16; - 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; x0getPixel(x,y); + video::SColor c2 = overlay->getPixel(x,y); + u32 a1 = c1.getAlpha(); + u32 a2 = c2.getAlpha(); + if(a1 == 255 && a2 != 0) { - image->setPixel(x,y, *c); + 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); + } +} + +void brighten(video::IImage *image) +{ + if(image == NULL) + return; + + core::dimension2d dim = image->getDimension(); + + for(u32 y=0; ygetPixel(x,y); + c.setRed(0.5 * 255 + 0.5 * (float)c.getRed()); + c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen()); + c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue()); + image->setPixel(x,y,c); } }