Guest User

Untitled

a guest
Aug 17th, 2012
66
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2. Minetest-c55
  3. Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
  4.  
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU Lesser General Public License as published by
  7. the Free Software Foundation; either version 2.1 of the License, or
  8. (at your option) any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU Lesser General Public License for more details.
  14.  
  15. You should have received a copy of the GNU Lesser General Public License along
  16. with this program; if not, write to the Free Software Foundation, Inc.,
  17. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18. */
  19.  
  20. #include "tile.h"
  21. #include "debug.h"
  22. #include "main.h" // for g_settings
  23. #include "filesys.h"
  24. #include "settings.h"
  25. #include "mesh.h"
  26. #include <ICameraSceneNode.h>
  27. #include "log.h"
  28. #include "mapnode.h" // For texture atlas making
  29. #include "nodedef.h" // For texture atlas making
  30. #include "gamedef.h"
  31. #include "util/string.h"
  32. #include "util/container.h"
  33. #include "util/thread.h"
  34. #include "util/numeric.h"
  35.  
  36. // BEGIN WIKI IMAGE EXTRACT
  37. #include <string>
  38. #include <iostream>
  39. #include <algorithm>
  40. using std::string;
  41. using std::cout;
  42. using std::endl;
  43. using std::replace;
  44. // END WIKI IMAGE EXTRACT
  45.  
  46. /*
  47.     A cache from texture name to texture path
  48. */
  49. MutexedMap<std::string, std::string> g_texturename_to_path_cache;
  50.  
  51. /*
  52.     Replaces the filename extension.
  53.     eg:
  54.         std::string image = "a/image.png"
  55.         replace_ext(image, "jpg")
  56.         -> image = "a/image.jpg"
  57.     Returns true on success.
  58. */
  59. static bool replace_ext(std::string &path, const char *ext)
  60. {
  61.     if(ext == NULL)
  62.         return false;
  63.     // Find place of last dot, fail if \ or / found.
  64.     s32 last_dot_i = -1;
  65.     for(s32 i=path.size()-1; i>=0; i--)
  66.     {
  67.         if(path[i] == '.')
  68.         {
  69.             last_dot_i = i;
  70.             break;
  71.         }
  72.        
  73.         if(path[i] == '\\' || path[i] == '/')
  74.             break;
  75.     }
  76.     // If not found, return an empty string
  77.     if(last_dot_i == -1)
  78.         return false;
  79.     // Else make the new path
  80.     path = path.substr(0, last_dot_i+1) + ext;
  81.     return true;
  82. }
  83.  
  84. /*
  85.     Find out the full path of an image by trying different filename
  86.     extensions.
  87.  
  88.     If failed, return "".
  89. */
  90. static std::string getImagePath(std::string path)
  91. {
  92.     // A NULL-ended list of possible image extensions
  93.     const char *extensions[] = {
  94.         "png", "jpg", "bmp", "tga",
  95.         "pcx", "ppm", "psd", "wal", "rgb",
  96.         NULL
  97.     };
  98.     // If there is no extension, add one
  99.     if(removeStringEnd(path, extensions) == "")
  100.         path = path + ".png";
  101.     // Check paths until something is found to exist
  102.     const char **ext = extensions;
  103.     do{
  104.         bool r = replace_ext(path, *ext);
  105.         if(r == false)
  106.             return "";
  107.         if(fs::PathExists(path))
  108.             return path;
  109.     }
  110.     while((++ext) != NULL);
  111.    
  112.     return "";
  113. }
  114.  
  115. /*
  116.     Gets the path to a texture by first checking if the texture exists
  117.     in texture_path and if not, using the data path.
  118.  
  119.     Checks all supported extensions by replacing the original extension.
  120.  
  121.     If not found, returns "".
  122.  
  123.     Utilizes a thread-safe cache.
  124. */
  125. std::string getTexturePath(const std::string &filename)
  126. {
  127.     std::string fullpath = "";
  128.     /*
  129.         Check from cache
  130.     */
  131.     bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
  132.     if(incache)
  133.         return fullpath;
  134.    
  135.     /*
  136.         Check from texture_path
  137.     */
  138.     std::string texture_path = g_settings->get("texture_path");
  139.     if(texture_path != "")
  140.     {
  141.         std::string testpath = texture_path + DIR_DELIM + filename;
  142.         // Check all filename extensions. Returns "" if not found.
  143.         fullpath = getImagePath(testpath);
  144.     }
  145.    
  146.     /*
  147.         Check from $user/textures/all
  148.     */
  149.     if(fullpath == "")
  150.     {
  151.         std::string texture_path = porting::path_user + DIR_DELIM
  152.                 + "textures" + DIR_DELIM + "all";
  153.         std::string testpath = texture_path + DIR_DELIM + filename;
  154.         // Check all filename extensions. Returns "" if not found.
  155.         fullpath = getImagePath(testpath);
  156.     }
  157.  
  158.     /*
  159.         Check from default data directory
  160.     */
  161.     if(fullpath == "")
  162.     {
  163.         std::string base_path = porting::path_share + DIR_DELIM + "textures"
  164.                 + DIR_DELIM + "base" + DIR_DELIM + "pack";
  165.         std::string testpath = base_path + DIR_DELIM + filename;
  166.         // Check all filename extensions. Returns "" if not found.
  167.         fullpath = getImagePath(testpath);
  168.     }
  169.    
  170.     // Add to cache (also an empty result is cached)
  171.     g_texturename_to_path_cache.set(filename, fullpath);
  172.    
  173.     // Finally return it
  174.     return fullpath;
  175. }
  176.  
  177. /*
  178.     An internal variant of AtlasPointer with more data.
  179.     (well, more like a wrapper)
  180. */
  181.  
  182. struct SourceAtlasPointer
  183. {
  184.     std::string name;
  185.     AtlasPointer a;
  186.     video::IImage *atlas_img; // The source image of the atlas
  187.     // Integer variants of position and size
  188.     v2s32 intpos;
  189.     v2u32 intsize;
  190.  
  191.     SourceAtlasPointer(
  192.             const std::string &name_,
  193.             AtlasPointer a_=AtlasPointer(0, NULL),
  194.             video::IImage *atlas_img_=NULL,
  195.             v2s32 intpos_=v2s32(0,0),
  196.             v2u32 intsize_=v2u32(0,0)
  197.         ):
  198.         name(name_),
  199.         a(a_),
  200.         atlas_img(atlas_img_),
  201.         intpos(intpos_),
  202.         intsize(intsize_)
  203.     {
  204.     }
  205. };
  206.  
  207. /*
  208.     SourceImageCache: A cache used for storing source images.
  209. */
  210.  
  211. class SourceImageCache
  212. {
  213. public:
  214.     void insert(const std::string &name, video::IImage *img,
  215.             bool prefer_local, video::IVideoDriver *driver)
  216.     {
  217.         assert(img);
  218.         // Remove old image
  219.         core::map<std::string, video::IImage*>::Node *n;
  220.         n = m_images.find(name);
  221.         if(n){
  222.             video::IImage *oldimg = n->getValue();
  223.             if(oldimg)
  224.                 oldimg->drop();
  225.         }
  226.         // Try to use local texture instead if asked to
  227.         if(prefer_local){
  228.             std::string path = getTexturePath(name.c_str());
  229.             if(path != ""){
  230.                 video::IImage *img2 = driver->createImageFromFile(path.c_str());
  231.                 if(img2){
  232.                     m_images[name] = img2;
  233.                     return;
  234.                 }
  235.             }
  236.         }
  237.         img->grab();
  238.         m_images[name] = img;
  239.     }
  240.     video::IImage* get(const std::string &name)
  241.     {
  242.         core::map<std::string, video::IImage*>::Node *n;
  243.         n = m_images.find(name);
  244.         if(n)
  245.             return n->getValue();
  246.         return NULL;
  247.     }
  248.     // Primarily fetches from cache, secondarily tries to read from filesystem
  249.     video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *device)
  250.     {
  251.         core::map<std::string, video::IImage*>::Node *n;
  252.         n = m_images.find(name);
  253.         if(n){
  254.             n->getValue()->grab(); // Grab for caller
  255.             return n->getValue();
  256.         }
  257.         video::IVideoDriver* driver = device->getVideoDriver();
  258.         std::string path = getTexturePath(name.c_str());
  259.         if(path == ""){
  260.             infostream<<"SourceImageCache::getOrLoad(): No path found for \""
  261.                     <<name<<"\""<<std::endl;
  262.             return NULL;
  263.         }
  264.         infostream<<"SourceImageCache::getOrLoad(): Loading path \""<<path
  265.                 <<"\""<<std::endl;
  266.         video::IImage *img = driver->createImageFromFile(path.c_str());
  267.         // Even if could not be loaded, put as NULL
  268.         //m_images[name] = img;
  269.         if(img){
  270.             m_images[name] = img;
  271.             img->grab(); // Grab for caller
  272.         }
  273.         return img;
  274.     }
  275. private:
  276.     core::map<std::string, video::IImage*> m_images;
  277. };
  278.  
  279. /*
  280.     TextureSource
  281. */
  282.  
  283. class TextureSource : public IWritableTextureSource
  284. {
  285. public:
  286.     TextureSource(IrrlichtDevice *device);
  287.     ~TextureSource();
  288.  
  289.     /*
  290.         Example case:
  291.         Now, assume a texture with the id 1 exists, and has the name
  292.         "stone.png^mineral1".
  293.         Then a random thread calls getTextureId for a texture called
  294.         "stone.png^mineral1^crack0".
  295.         ...Now, WTF should happen? Well:
  296.         - getTextureId strips off stuff recursively from the end until
  297.           the remaining part is found, or nothing is left when
  298.           something is stripped out
  299.  
  300.         But it is slow to search for textures by names and modify them
  301.         like that?
  302.         - ContentFeatures is made to contain ids for the basic plain
  303.           textures
  304.         - Crack textures can be slow by themselves, but the framework
  305.           must be fast.
  306.  
  307.         Example case #2:
  308.         - Assume a texture with the id 1 exists, and has the name
  309.           "stone.png^mineral1" and is specified as a part of some atlas.
  310.         - Now getNodeTile() stumbles upon a node which uses
  311.           texture id 1, and determines that MATERIAL_FLAG_CRACK
  312.           must be applied to the tile
  313.         - MapBlockMesh::animate() finds the MATERIAL_FLAG_CRACK and
  314.           has received the current crack level 0 from the client. It
  315.           finds out the name of the texture with getTextureName(1),
  316.           appends "^crack0" to it and gets a new texture id with
  317.           getTextureId("stone.png^mineral1^crack0").
  318.  
  319.     */
  320.    
  321.     /*
  322.         Gets a texture id from cache or
  323.         - if main thread, from getTextureIdDirect
  324.         - if other thread, adds to request queue and waits for main thread
  325.     */
  326.     u32 getTextureId(const std::string &name);
  327.    
  328.     /*
  329.         Example names:
  330.         "stone.png"
  331.         "stone.png^crack2"
  332.         "stone.png^mineral_coal.png"
  333.         "stone.png^mineral_coal.png^crack1"
  334.  
  335.         - If texture specified by name is found from cache, return the
  336.           cached id.
  337.         - Otherwise generate the texture, add to cache and return id.
  338.           Recursion is used to find out the largest found part of the
  339.           texture and continue based on it.
  340.  
  341.         The id 0 points to a NULL texture. It is returned in case of error.
  342.     */
  343.     u32 getTextureIdDirect(const std::string &name);
  344.  
  345.     // Finds out the name of a cached texture.
  346.     std::string getTextureName(u32 id);
  347.  
  348.     /*
  349.         If texture specified by the name pointed by the id doesn't
  350.         exist, create it, then return the cached texture.
  351.  
  352.         Can be called from any thread. If called from some other thread
  353.         and not found in cache, the call is queued to the main thread
  354.         for processing.
  355.     */
  356.     AtlasPointer getTexture(u32 id);
  357.    
  358.     AtlasPointer getTexture(const std::string &name)
  359.     {
  360.         return getTexture(getTextureId(name));
  361.     }
  362.    
  363.     // Gets a separate texture
  364.     video::ITexture* getTextureRaw(const std::string &name)
  365.     {
  366.         AtlasPointer ap = getTexture(name + "^[forcesingle");
  367.         return ap.atlas;
  368.     }
  369.  
  370.     // Gets a separate texture atlas pointer
  371.     AtlasPointer getTextureRawAP(const AtlasPointer &ap)
  372.     {
  373.         return getTexture(getTextureName(ap.id) + "^[forcesingle");
  374.     }
  375.  
  376.     // Returns a pointer to the irrlicht device
  377.     virtual IrrlichtDevice* getDevice()
  378.     {
  379.         return m_device;
  380.     }
  381.  
  382.     // Update new texture pointer and texture coordinates to an
  383.     // AtlasPointer based on it's texture id
  384.     void updateAP(AtlasPointer &ap);
  385.  
  386.     // Processes queued texture requests from other threads.
  387.     // Shall be called from the main thread.
  388.     void processQueue();
  389.    
  390.     // Insert an image into the cache without touching the filesystem.
  391.     // Shall be called from the main thread.
  392.     void insertSourceImage(const std::string &name, video::IImage *img);
  393.    
  394.     // Rebuild images and textures from the current set of source images
  395.     // Shall be called from the main thread.
  396.     void rebuildImagesAndTextures();
  397.  
  398.     // Build the main texture atlas which contains most of the
  399.     // textures.
  400.     void buildMainAtlas(class IGameDef *gamedef);
  401.    
  402. private:
  403.    
  404.     // The id of the thread that is allowed to use irrlicht directly
  405.     threadid_t m_main_thread;
  406.     // The irrlicht device
  407.     IrrlichtDevice *m_device;
  408.    
  409.     // Cache of source images
  410.     // This should be only accessed from the main thread
  411.     SourceImageCache m_sourcecache;
  412.  
  413.     // A texture id is index in this array.
  414.     // The first position contains a NULL texture.
  415.     core::array<SourceAtlasPointer> m_atlaspointer_cache;
  416.     // Maps a texture name to an index in the former.
  417.     core::map<std::string, u32> m_name_to_id;
  418.     // The two former containers are behind this mutex
  419.     JMutex m_atlaspointer_cache_mutex;
  420.    
  421.     // Main texture atlas. This is filled at startup and is then not touched.
  422.     video::IImage *m_main_atlas_image;
  423.     video::ITexture *m_main_atlas_texture;
  424.  
  425.     // Queued texture fetches (to be processed by the main thread)
  426.     RequestQueue<std::string, u32, u8, u8> m_get_texture_queue;
  427. };
  428.  
  429. IWritableTextureSource* createTextureSource(IrrlichtDevice *device)
  430. {
  431.     return new TextureSource(device);
  432. }
  433.  
  434. TextureSource::TextureSource(IrrlichtDevice *device):
  435.         m_device(device),
  436.         m_main_atlas_image(NULL),
  437.         m_main_atlas_texture(NULL)
  438. {
  439.     assert(m_device);
  440.    
  441.     m_atlaspointer_cache_mutex.Init();
  442.    
  443.     m_main_thread = get_current_thread_id();
  444.    
  445.     // Add a NULL AtlasPointer as the first index, named ""
  446.     m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
  447.     m_name_to_id[""] = 0;
  448. }
  449.  
  450. TextureSource::~TextureSource()
  451. {
  452. }
  453.  
  454. u32 TextureSource::getTextureId(const std::string &name)
  455. {
  456.     //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
  457.  
  458.     {
  459.         /*
  460.             See if texture already exists
  461.         */
  462.         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
  463.         core::map<std::string, u32>::Node *n;
  464.         n = m_name_to_id.find(name);
  465.         if(n != NULL)
  466.         {
  467.             return n->getValue();
  468.         }
  469.     }
  470.    
  471.     /*
  472.         Get texture
  473.     */
  474.     if(get_current_thread_id() == m_main_thread)
  475.     {
  476.         return getTextureIdDirect(name);
  477.     }
  478.     else
  479.     {
  480.         infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
  481.  
  482.         // We're gonna ask the result to be put into here
  483.         ResultQueue<std::string, u32, u8, u8> result_queue;
  484.        
  485.         // Throw a request in
  486.         m_get_texture_queue.add(name, 0, 0, &result_queue);
  487.        
  488.         infostream<<"Waiting for texture from main thread, name=\""
  489.                 <<name<<"\""<<std::endl;
  490.        
  491.         try
  492.         {
  493.             // Wait result for a second
  494.             GetResult<std::string, u32, u8, u8>
  495.                     result = result_queue.pop_front(1000);
  496.        
  497.             // Check that at least something worked OK
  498.             assert(result.key == name);
  499.  
  500.             return result.item;
  501.         }
  502.         catch(ItemNotFoundException &e)
  503.         {
  504.             infostream<<"Waiting for texture timed out."<<std::endl;
  505.             return 0;
  506.         }
  507.     }
  508.    
  509.     infostream<<"getTextureId(): Failed"<<std::endl;
  510.  
  511.     return 0;
  512. }
  513.  
  514. // Overlay image on top of another image (used for cracks)
  515. void overlay(video::IImage *image, video::IImage *overlay);
  516.  
  517. // Draw an image on top of an another one, using the alpha channel of the
  518. // source image
  519. static void blit_with_alpha(video::IImage *src, video::IImage *dst,
  520.         v2s32 src_pos, v2s32 dst_pos, v2u32 size);
  521.  
  522. // Brighten image
  523. void brighten(video::IImage *image);
  524. // Parse a transform name
  525. u32 parseImageTransform(const std::string& s);
  526. // Apply transform to image dimension
  527. core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim);
  528. // Apply transform to image data
  529. void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
  530.  
  531. /*
  532.     Generate image based on a string like "stone.png" or "[crack0".
  533.     if baseimg is NULL, it is created. Otherwise stuff is made on it.
  534. */
  535. bool generate_image(std::string part_of_name, video::IImage *& baseimg,
  536.         IrrlichtDevice *device, SourceImageCache *sourcecache);
  537.  
  538. /*
  539.     Generates an image from a full string like
  540.     "stone.png^mineral_coal.png^[crack0".
  541.  
  542.     This is used by buildMainAtlas().
  543. */
  544. video::IImage* generate_image_from_scratch(std::string name,
  545.         IrrlichtDevice *device, SourceImageCache *sourcecache);
  546.  
  547. /*
  548.     This method generates all the textures
  549. */
  550. u32 TextureSource::getTextureIdDirect(const std::string &name)
  551. {
  552.     //infostream<<"getTextureIdDirect(): name=\""<<name<<"\""<<std::endl;
  553.  
  554.     // Empty name means texture 0
  555.     if(name == "")
  556.     {
  557.         infostream<<"getTextureIdDirect(): name is empty"<<std::endl;
  558.         return 0;
  559.     }
  560.    
  561.     /*
  562.         Calling only allowed from main thread
  563.     */
  564.     if(get_current_thread_id() != m_main_thread)
  565.     {
  566.         errorstream<<"TextureSource::getTextureIdDirect() "
  567.                 "called not from main thread"<<std::endl;
  568.         return 0;
  569.     }
  570.  
  571.     /*
  572.         See if texture already exists
  573.     */
  574.     {
  575.         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
  576.  
  577.         core::map<std::string, u32>::Node *n;
  578.         n = m_name_to_id.find(name);
  579.         if(n != NULL)
  580.         {
  581.             /*infostream<<"getTextureIdDirect(): \""<<name
  582.                     <<"\" found in cache"<<std::endl;*/
  583.             return n->getValue();
  584.         }
  585.     }
  586.  
  587.     /*infostream<<"getTextureIdDirect(): \""<<name
  588.             <<"\" NOT found in cache. Creating it."<<std::endl;*/
  589.    
  590.     /*
  591.         Get the base image
  592.     */
  593.  
  594.     char separator = '^';
  595.  
  596.     /*
  597.         This is set to the id of the base image.
  598.         If left 0, there is no base image and a completely new image
  599.         is made.
  600.     */
  601.     u32 base_image_id = 0;
  602.    
  603.     // Find last meta separator in name
  604.     s32 last_separator_position = -1;
  605.     for(s32 i=name.size()-1; i>=0; i--)
  606.     {
  607.         if(name[i] == separator)
  608.         {
  609.             last_separator_position = i;
  610.             break;
  611.         }
  612.     }
  613.     /*
  614.         If separator was found, construct the base name and make the
  615.         base image using a recursive call
  616.     */
  617.     std::string base_image_name;
  618.     if(last_separator_position != -1)
  619.     {
  620.         // Construct base name
  621.         base_image_name = name.substr(0, last_separator_position);
  622.         /*infostream<<"getTextureIdDirect(): Calling itself recursively"
  623.                 " to get base image of \""<<name<<"\" = \""
  624.                 <<base_image_name<<"\""<<std::endl;*/
  625.         base_image_id = getTextureIdDirect(base_image_name);
  626.     }
  627.    
  628.     //infostream<<"base_image_id="<<base_image_id<<std::endl;
  629.    
  630.     video::IVideoDriver* driver = m_device->getVideoDriver();
  631.     assert(driver);
  632.  
  633.     video::ITexture *t = NULL;
  634.  
  635.     /*
  636.         An image will be built from files and then converted into a texture.
  637.     */
  638.     video::IImage *baseimg = NULL;
  639.    
  640.     // If a base image was found, copy it to baseimg
  641.     if(base_image_id != 0)
  642.     {
  643.         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
  644.  
  645.         SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
  646.  
  647.         video::IImage *image = ap.atlas_img;
  648.        
  649.         if(image == NULL)
  650.         {
  651.             infostream<<"getTextureIdDirect(): WARNING: NULL image in "
  652.                     <<"cache: \""<<base_image_name<<"\""
  653.                     <<std::endl;
  654.         }
  655.         else
  656.         {
  657.             core::dimension2d<u32> dim = ap.intsize;
  658.  
  659.             baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
  660.  
  661.             core::position2d<s32> pos_to(0,0);
  662.             core::position2d<s32> pos_from = ap.intpos;
  663.            
  664.             image->copyTo(
  665.                     baseimg, // target
  666.                     v2s32(0,0), // position in target
  667.                     core::rect<s32>(pos_from, dim) // from
  668.             );
  669.  
  670.             /*infostream<<"getTextureIdDirect(): Loaded \""
  671.                     <<base_image_name<<"\" from image cache"
  672.                     <<std::endl;*/
  673.         }
  674.     }
  675.    
  676.     /*
  677.         Parse out the last part of the name of the image and act
  678.         according to it
  679.     */
  680.  
  681.     std::string last_part_of_name = name.substr(last_separator_position+1);
  682.     //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
  683.  
  684.     // Generate image according to part of name
  685.     if(!generate_image(last_part_of_name, baseimg, m_device, &m_sourcecache))
  686.     {
  687.         errorstream<<"getTextureIdDirect(): "
  688.                 "failed to generate \""<<last_part_of_name<<"\""
  689.                 <<std::endl;
  690.     }
  691.  
  692.     // If no resulting image, print a warning
  693.     if(baseimg == NULL)
  694.     {
  695.         errorstream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
  696.                 " create texture \""<<name<<"\""<<std::endl;
  697.     }
  698.    
  699.     if(baseimg != NULL)
  700.     {
  701.         // Create texture from resulting image
  702.         t = driver->addTexture(name.c_str(), baseimg);
  703.     }
  704.    
  705.     /*
  706.         Add texture to caches (add NULL textures too)
  707.     */
  708.  
  709.     JMutexAutoLock lock(m_atlaspointer_cache_mutex);
  710.    
  711.     u32 id = m_atlaspointer_cache.size();
  712.     AtlasPointer ap(id);
  713.     ap.atlas = t;
  714.     ap.pos = v2f(0,0);
  715.     ap.size = v2f(1,1);
  716.     ap.tiled = 0;
  717.     core::dimension2d<u32> baseimg_dim(0,0);
  718.     if(baseimg)
  719.         baseimg_dim = baseimg->getDimension();
  720.     SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
  721.     m_atlaspointer_cache.push_back(nap);
  722.     m_name_to_id.insert(name, id);
  723.  
  724.     /*infostream<<"getTextureIdDirect(): "
  725.             <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
  726.    
  727.     return id;
  728. }
  729.  
  730. std::string TextureSource::getTextureName(u32 id)
  731. {
  732.     JMutexAutoLock lock(m_atlaspointer_cache_mutex);
  733.  
  734.     if(id >= m_atlaspointer_cache.size())
  735.     {
  736.         errorstream<<"TextureSource::getTextureName(): id="<<id
  737.                 <<" >= m_atlaspointer_cache.size()="
  738.                 <<m_atlaspointer_cache.size()<<std::endl;
  739.         return "";
  740.     }
  741.    
  742.     return m_atlaspointer_cache[id].name;
  743. }
  744.  
  745.  
  746. AtlasPointer TextureSource::getTexture(u32 id)
  747. {
  748.     JMutexAutoLock lock(m_atlaspointer_cache_mutex);
  749.  
  750.     if(id >= m_atlaspointer_cache.size())
  751.         return AtlasPointer(0, NULL);
  752.    
  753.     return m_atlaspointer_cache[id].a;
  754. }
  755.  
  756. void TextureSource::updateAP(AtlasPointer &ap)
  757. {
  758.     AtlasPointer ap2 = getTexture(ap.id);
  759.     ap = ap2;
  760. }
  761.  
  762. void TextureSource::processQueue()
  763. {
  764.     /*
  765.         Fetch textures
  766.     */
  767.     if(m_get_texture_queue.size() > 0)
  768.     {
  769.         GetRequest<std::string, u32, u8, u8>
  770.                 request = m_get_texture_queue.pop();
  771.  
  772.         /*infostream<<"TextureSource::processQueue(): "
  773.                 <<"got texture request with "
  774.                 <<"name=\""<<request.key<<"\""
  775.                 <<std::endl;*/
  776.  
  777.         GetResult<std::string, u32, u8, u8>
  778.                 result;
  779.         result.key = request.key;
  780.         result.callers = request.callers;
  781.         result.item = getTextureIdDirect(request.key);
  782.  
  783.         request.dest->push_back(result);
  784.     }
  785. }
  786.  
  787. void TextureSource::insertSourceImage(const std::string &name, video::IImage *img)
  788. {
  789.     //infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
  790.    
  791.     assert(get_current_thread_id() == m_main_thread);
  792.    
  793.     m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
  794. }
  795.    
  796. void TextureSource::rebuildImagesAndTextures()
  797. {
  798.     JMutexAutoLock lock(m_atlaspointer_cache_mutex);
  799.  
  800.     /*// Oh well... just clear everything, they'll load sometime.
  801.     m_atlaspointer_cache.clear();
  802.     m_name_to_id.clear();*/
  803.  
  804.     video::IVideoDriver* driver = m_device->getVideoDriver();
  805.    
  806.     // Remove source images from textures to disable inheriting textures
  807.     // from existing textures
  808.     /*for(u32 i=0; i<m_atlaspointer_cache.size(); i++){
  809.         SourceAtlasPointer *sap = &m_atlaspointer_cache[i];
  810.         sap->atlas_img->drop();
  811.         sap->atlas_img = NULL;
  812.     }*/
  813.    
  814.     // Recreate textures
  815.     for(u32 i=0; i<m_atlaspointer_cache.size(); i++){
  816.         SourceAtlasPointer *sap = &m_atlaspointer_cache[i];
  817.         video::IImage *img =
  818.             generate_image_from_scratch(sap->name, m_device, &m_sourcecache);
  819.         // Create texture from resulting image
  820.         video::ITexture *t = NULL;
  821.         if(img)
  822.             t = driver->addTexture(sap->name.c_str(), img);
  823.        
  824.         // Replace texture
  825.         sap->a.atlas = t;
  826.         sap->a.pos = v2f(0,0);
  827.         sap->a.size = v2f(1,1);
  828.         sap->a.tiled = 0;
  829.         sap->atlas_img = img;
  830.         sap->intpos = v2s32(0,0);
  831.         sap->intsize = img->getDimension();
  832.     }
  833. }
  834.  
  835. void TextureSource::buildMainAtlas(class IGameDef *gamedef)
  836. {
  837.     assert(gamedef->tsrc() == this);
  838.     INodeDefManager *ndef = gamedef->ndef();
  839.  
  840.     infostream<<"TextureSource::buildMainAtlas()"<<std::endl;
  841.  
  842.     //return; // Disable (for testing)
  843.    
  844.     video::IVideoDriver* driver = m_device->getVideoDriver();
  845.     assert(driver);
  846.  
  847.     JMutexAutoLock lock(m_atlaspointer_cache_mutex);
  848.  
  849.     // Create an image of the right size
  850.     core::dimension2d<u32> max_dim = driver->getMaxTextureSize();
  851.     core::dimension2d<u32> atlas_dim(2048,2048);
  852.     atlas_dim.Width  = MYMIN(atlas_dim.Width,  max_dim.Width);
  853.     atlas_dim.Height = MYMIN(atlas_dim.Height, max_dim.Height);
  854.     video::IImage *atlas_img =
  855.             driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
  856.     //assert(atlas_img);
  857.     if(atlas_img == NULL)
  858.     {
  859.         errorstream<<"TextureSource::buildMainAtlas(): Failed to create atlas "
  860.                 "image; not building texture atlas."<<std::endl;
  861.         return;
  862.     }
  863.  
  864.     /*
  865.         Grab list of stuff to include in the texture atlas from the
  866.         main content features
  867.     */
  868.  
  869.     core::map<std::string, bool> sourcelist;
  870.  
  871.     for(u16 j=0; j<MAX_CONTENT+1; j++)
  872.     {
  873.         if(j == CONTENT_IGNORE || j == CONTENT_AIR)
  874.             continue;
  875.         const ContentFeatures &f = ndef->get(j);
  876.         for(u32 i=0; i<6; i++)
  877.         {
  878.             std::string name = f.tiledef[i].name;
  879.             sourcelist[name] = true;
  880.         }
  881.     }
  882.    
  883.     infostream<<"Creating texture atlas out of textures: ";
  884.     for(core::map<std::string, bool>::Iterator
  885.             i = sourcelist.getIterator();
  886.             i.atEnd() == false; i++)
  887.     {
  888.         std::string name = i.getNode()->getKey();
  889.         infostream<<"\""<<name<<"\" ";
  890.     }
  891.     infostream<<std::endl;
  892.  
  893.     // Padding to disallow texture bleeding
  894.     // (16 needed if mipmapping is used; otherwise less will work too)
  895.     s32 padding = 16;
  896.     s32 column_padding = 16;
  897.     s32 column_width = 256; // Space for 16 pieces of 16x16 textures
  898.  
  899.     /*
  900.         First pass: generate almost everything
  901.     */
  902.     core::position2d<s32> pos_in_atlas(0,0);
  903.    
  904.     pos_in_atlas.X = column_padding;
  905.     pos_in_atlas.Y = padding;
  906.  
  907.     for(core::map<std::string, bool>::Iterator
  908.             i = sourcelist.getIterator();
  909.             i.atEnd() == false; i++)
  910.     {
  911.         std::string name = i.getNode()->getKey();
  912.  
  913.         // Generate image by name
  914.         video::IImage *img2 = generate_image_from_scratch(name, m_device,
  915.                 &m_sourcecache);
  916.         if(img2 == NULL)
  917.         {
  918.             errorstream<<"TextureSource::buildMainAtlas(): "
  919.                     <<"Couldn't generate image \""<<name<<"\""<<std::endl;
  920.             continue;
  921.         }
  922.  
  923.         core::dimension2d<u32> dim = img2->getDimension();
  924.  
  925.         // Don't add to atlas if image is too large
  926.         core::dimension2d<u32> max_size_in_atlas(64,64);
  927.         if(dim.Width > max_size_in_atlas.Width
  928.         || dim.Height > max_size_in_atlas.Height)
  929.         {
  930.             infostream<<"TextureSource::buildMainAtlas(): Not adding "
  931.                     <<"\""<<name<<"\" because image is large"<<std::endl;
  932.             continue;
  933.         }
  934.  
  935.         // Wrap columns and stop making atlas if atlas is full
  936.         if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
  937.         {
  938.             if(pos_in_atlas.X > (s32)atlas_dim.Width - column_width - column_padding){
  939.                 errorstream<<"TextureSource::buildMainAtlas(): "
  940.                         <<"Atlas is full, not adding more textures."
  941.                         <<std::endl;
  942.                 break;
  943.             }
  944.             pos_in_atlas.Y = padding;
  945.             pos_in_atlas.X += column_width + column_padding*2;
  946.         }
  947.        
  948.         /*infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
  949.                 <<"\" to texture atlas"<<std::endl;*/
  950.  
  951.         // Tile it a few times in the X direction
  952.         u16 xwise_tiling = column_width / dim.Width;
  953.         if(xwise_tiling > 16) // Limit to 16 (more gives no benefit)
  954.             xwise_tiling = 16;
  955.         for(u32 j=0; j<xwise_tiling; j++)
  956.         {
  957.             // Copy the copy to the atlas
  958.             /*img2->copyToWithAlpha(atlas_img,
  959.                     pos_in_atlas + v2s32(j*dim.Width,0),
  960.                     core::rect<s32>(v2s32(0,0), dim),
  961.                     video::SColor(255,255,255,255),
  962.                     NULL);*/
  963.             img2->copyTo(atlas_img,
  964.                     pos_in_atlas + v2s32(j*dim.Width,0),
  965.                     core::rect<s32>(v2s32(0,0), dim),
  966.                     NULL);
  967.         }
  968.  
  969.         // Copy the borders a few times to disallow texture bleeding
  970.         for(u32 side=0; side<2; side++) // top and bottom
  971.         for(s32 y0=0; y0<padding; y0++)
  972.         for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
  973.         {
  974.             s32 dst_y;
  975.             s32 src_y;
  976.             if(side==0)
  977.             {
  978.                 dst_y = y0 + pos_in_atlas.Y + dim.Height;
  979.                 src_y = pos_in_atlas.Y + dim.Height - 1;
  980.             }
  981.             else
  982.             {
  983.                 dst_y = -y0 + pos_in_atlas.Y-1;
  984.                 src_y = pos_in_atlas.Y;
  985.             }
  986.             s32 x = x0 + pos_in_atlas.X;
  987.             video::SColor c = atlas_img->getPixel(x, src_y);
  988.             atlas_img->setPixel(x,dst_y,c);
  989.         }
  990.  
  991.         for(u32 side=0; side<2; side++) // left and right
  992.         for(s32 x0=0; x0<column_padding; x0++)
  993.         for(s32 y0=-padding; y0<(s32)dim.Height+padding; y0++)
  994.         {
  995.             s32 dst_x;
  996.             s32 src_x;
  997.             if(side==0)
  998.             {
  999.                 dst_x = x0 + pos_in_atlas.X + dim.Width*xwise_tiling;
  1000.                 src_x = pos_in_atlas.X + dim.Width*xwise_tiling - 1;
  1001.             }
  1002.             else
  1003.             {
  1004.                 dst_x = -x0 + pos_in_atlas.X-1;
  1005.                 src_x = pos_in_atlas.X;
  1006.             }
  1007.             s32 y = y0 + pos_in_atlas.Y;
  1008.             s32 src_y = MYMAX((int)pos_in_atlas.Y, MYMIN((int)pos_in_atlas.Y + (int)dim.Height - 1, y));
  1009.             s32 dst_y = y;
  1010.             video::SColor c = atlas_img->getPixel(src_x, src_y);
  1011.             atlas_img->setPixel(dst_x,dst_y,c);
  1012.         }
  1013.  
  1014.         img2->drop();
  1015.  
  1016.         /*
  1017.             Add texture to caches
  1018.         */
  1019.        
  1020.         bool reuse_old_id = false;
  1021.         u32 id = m_atlaspointer_cache.size();
  1022.         // Check old id without fetching a texture
  1023.         core::map<std::string, u32>::Node *n;
  1024.         n = m_name_to_id.find(name);
  1025.         // If it exists, we will replace the old definition
  1026.         if(n){
  1027.             id = n->getValue();
  1028.             reuse_old_id = true;
  1029.             /*infostream<<"TextureSource::buildMainAtlas(): "
  1030.                     <<"Replacing old AtlasPointer"<<std::endl;*/
  1031.         }
  1032.  
  1033.         // Create AtlasPointer
  1034.         AtlasPointer ap(id);
  1035.         ap.atlas = NULL; // Set on the second pass
  1036.         ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
  1037.                 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
  1038.         ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
  1039.                 (float)dim.Width/(float)atlas_dim.Height);
  1040.         ap.tiled = xwise_tiling;
  1041.  
  1042.         // Create SourceAtlasPointer and add to containers
  1043.         SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
  1044.         if(reuse_old_id)
  1045.             m_atlaspointer_cache[id] = nap;
  1046.         else
  1047.             m_atlaspointer_cache.push_back(nap);
  1048.         m_name_to_id[name] = id;
  1049.            
  1050.         // Increment position
  1051.         pos_in_atlas.Y += dim.Height + padding * 2;
  1052.     }
  1053.  
  1054.     /*
  1055.         Make texture
  1056.     */
  1057.     video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
  1058.     assert(t);
  1059.  
  1060.     /*
  1061.         Second pass: set texture pointer in generated AtlasPointers
  1062.     */
  1063.     for(core::map<std::string, bool>::Iterator
  1064.             i = sourcelist.getIterator();
  1065.             i.atEnd() == false; i++)
  1066.     {
  1067.         std::string name = i.getNode()->getKey();
  1068.         if(m_name_to_id.find(name) == NULL)
  1069.             continue;
  1070.         u32 id = m_name_to_id[name];
  1071.         //infostream<<"id of name "<<name<<" is "<<id<<std::endl;
  1072.         m_atlaspointer_cache[id].a.atlas = t;
  1073.     }
  1074.  
  1075.     /*
  1076.         Write image to file so that it can be inspected
  1077.     */
  1078.     /*std::string atlaspath = porting::path_user
  1079.             + DIR_DELIM + "generated_texture_atlas.png";
  1080.     infostream<<"Removing and writing texture atlas for inspection to "
  1081.             <<atlaspath<<std::endl;
  1082.     fs::RecursiveDelete(atlaspath);
  1083.     driver->writeImageToFile(atlas_img, atlaspath.c_str());*/
  1084. }
  1085.  
  1086. video::IImage* generate_image_from_scratch(std::string name,
  1087.         IrrlichtDevice *device, SourceImageCache *sourcecache)
  1088. {
  1089.     /*infostream<<"generate_image_from_scratch(): "
  1090.             "\""<<name<<"\""<<std::endl;*/
  1091.    
  1092.     video::IVideoDriver* driver = device->getVideoDriver();
  1093.     assert(driver);
  1094.  
  1095.     /*
  1096.         Get the base image
  1097.     */
  1098.  
  1099.     video::IImage *baseimg = NULL;
  1100.  
  1101.     char separator = '^';
  1102.  
  1103.     // Find last meta separator in name
  1104.     s32 last_separator_position = name.find_last_of(separator);
  1105.     //if(last_separator_position == std::npos)
  1106.     //  last_separator_position = -1;
  1107.  
  1108.     /*infostream<<"generate_image_from_scratch(): "
  1109.             <<"last_separator_position="<<last_separator_position
  1110.             <<std::endl;*/
  1111.  
  1112.     /*
  1113.         If separator was found, construct the base name and make the
  1114.         base image using a recursive call
  1115.     */
  1116.     std::string base_image_name;
  1117.     if(last_separator_position != -1)
  1118.     {
  1119.         // Construct base name
  1120.         base_image_name = name.substr(0, last_separator_position);
  1121.         /*infostream<<"generate_image_from_scratch(): Calling itself recursively"
  1122.                 " to get base image of \""<<name<<"\" = \""
  1123.                 <<base_image_name<<"\""<<std::endl;*/
  1124.         baseimg = generate_image_from_scratch(base_image_name, device,
  1125.                 sourcecache);
  1126.     }
  1127.    
  1128.     /*
  1129.         Parse out the last part of the name of the image and act
  1130.         according to it
  1131.     */
  1132.  
  1133.     std::string last_part_of_name = name.substr(last_separator_position+1);
  1134.     //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
  1135.    
  1136.     // Generate image according to part of name
  1137.     if(!generate_image(last_part_of_name, baseimg, device, sourcecache))
  1138.     {
  1139.         errorstream<<"generate_image_from_scratch(): "
  1140.                 "failed to generate \""<<last_part_of_name<<"\""
  1141.                 <<std::endl;
  1142.         return NULL;
  1143.     }
  1144.    
  1145.     return baseimg;
  1146. }
  1147.  
  1148. bool generate_image(std::string part_of_name, video::IImage *& baseimg,
  1149.         IrrlichtDevice *device, SourceImageCache *sourcecache)
  1150. {
  1151.     video::IVideoDriver* driver = device->getVideoDriver();
  1152.     assert(driver);
  1153.  
  1154.     // Stuff starting with [ are special commands
  1155.     if(part_of_name.size() == 0 || part_of_name[0] != '[')
  1156.     {
  1157.         video::IImage *image = sourcecache->getOrLoad(part_of_name, device);
  1158.  
  1159.         if(image == NULL)
  1160.         {
  1161.             if(part_of_name != ""){
  1162.                 errorstream<<"generate_image(): Could not load image \""
  1163.                         <<part_of_name<<"\""<<" while building texture"<<std::endl;
  1164.                 errorstream<<"generate_image(): Creating a dummy"
  1165.                         <<" image for \""<<part_of_name<<"\""<<std::endl;
  1166.             }
  1167.  
  1168.             // Just create a dummy image
  1169.             //core::dimension2d<u32> dim(2,2);
  1170.             core::dimension2d<u32> dim(1,1);
  1171.             image = driver->createImage(video::ECF_A8R8G8B8, dim);
  1172.             assert(image);
  1173.             /*image->setPixel(0,0, video::SColor(255,255,0,0));
  1174.             image->setPixel(1,0, video::SColor(255,0,255,0));
  1175.             image->setPixel(0,1, video::SColor(255,0,0,255));
  1176.             image->setPixel(1,1, video::SColor(255,255,0,255));*/
  1177.             image->setPixel(0,0, video::SColor(255,myrand()%256,
  1178.                     myrand()%256,myrand()%256));
  1179.             /*image->setPixel(1,0, video::SColor(255,myrand()%256,
  1180.                     myrand()%256,myrand()%256));
  1181.             image->setPixel(0,1, video::SColor(255,myrand()%256,
  1182.                     myrand()%256,myrand()%256));
  1183.             image->setPixel(1,1, video::SColor(255,myrand()%256,
  1184.                     myrand()%256,myrand()%256));*/
  1185.         }
  1186.  
  1187.         // If base image is NULL, load as base.
  1188.         if(baseimg == NULL)
  1189.         {
  1190.             //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
  1191.             /*
  1192.                 Copy it this way to get an alpha channel.
  1193.                 Otherwise images with alpha cannot be blitted on
  1194.                 images that don't have alpha in the original file.
  1195.             */
  1196.             core::dimension2d<u32> dim = image->getDimension();
  1197.             baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
  1198.             image->copyTo(baseimg);
  1199.             image->drop();
  1200.         }
  1201.         // Else blit on base.
  1202.         else
  1203.         {
  1204.             //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
  1205.             // Size of the copied area
  1206.             core::dimension2d<u32> dim = image->getDimension();
  1207.             //core::dimension2d<u32> dim(16,16);
  1208.             // Position to copy the blitted to in the base image
  1209.             core::position2d<s32> pos_to(0,0);
  1210.             // Position to copy the blitted from in the blitted image
  1211.             core::position2d<s32> pos_from(0,0);
  1212.             // Blit
  1213.             image->copyToWithAlpha(baseimg, pos_to,
  1214.                     core::rect<s32>(pos_from, dim),
  1215.                     video::SColor(255,255,255,255),
  1216.                     NULL);
  1217.             // Drop image
  1218.             image->drop();
  1219.         }
  1220.     }
  1221.     else
  1222.     {
  1223.         // A special texture modification
  1224.  
  1225.         /*infostream<<"generate_image(): generating special "
  1226.                 <<"modification \""<<part_of_name<<"\""
  1227.                 <<std::endl;*/
  1228.        
  1229.         /*
  1230.             This is the simplest of all; it just adds stuff to the
  1231.             name so that a separate texture is created.
  1232.  
  1233.             It is used to make textures for stuff that doesn't want
  1234.             to implement getting the texture from a bigger texture
  1235.             atlas.
  1236.         */
  1237.         if(part_of_name == "[forcesingle")
  1238.         {
  1239.             // If base image is NULL, create a random color
  1240.             if(baseimg == NULL)
  1241.             {
  1242.                 core::dimension2d<u32> dim(1,1);
  1243.                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
  1244.                 assert(baseimg);
  1245.                 baseimg->setPixel(0,0, video::SColor(255,myrand()%256,
  1246.                         myrand()%256,myrand()%256));
  1247.             }
  1248.         }
  1249.         /*
  1250.             [crackN
  1251.             Adds a cracking texture
  1252.         */
  1253.         else if(part_of_name.substr(0,6) == "[crack")
  1254.         {
  1255.             if(baseimg == NULL)
  1256.             {
  1257.                 errorstream<<"generate_image(): baseimg==NULL "
  1258.                         <<"for part_of_name=\""<<part_of_name
  1259.                         <<"\", cancelling."<<std::endl;
  1260.                 return false;
  1261.             }
  1262.            
  1263.             // Crack image number and overlay option
  1264.             s32 progression = 0;
  1265.             bool use_overlay = false;
  1266.             if(part_of_name.substr(6,1) == "o")
  1267.             {
  1268.                 progression = stoi(part_of_name.substr(7));
  1269.                 use_overlay = true;
  1270.             }
  1271.             else
  1272.             {
  1273.                 progression = stoi(part_of_name.substr(6));
  1274.                 use_overlay = false;
  1275.             }
  1276.  
  1277.             // Size of the base image
  1278.             core::dimension2d<u32> dim_base = baseimg->getDimension();
  1279.            
  1280.             /*
  1281.                 Load crack image.
  1282.  
  1283.                 It is an image with a number of cracking stages
  1284.                 horizontally tiled.
  1285.             */
  1286.             video::IImage *img_crack = sourcecache->getOrLoad(
  1287.                     "crack_anylength.png", device);
  1288.        
  1289.             if(img_crack && progression >= 0)
  1290.             {
  1291.                 // Dimension of original image
  1292.                 core::dimension2d<u32> dim_crack
  1293.                         = img_crack->getDimension();
  1294.                 // Count of crack stages
  1295.                 s32 crack_count = dim_crack.Height / dim_crack.Width;
  1296.                 // Limit progression
  1297.                 if(progression > crack_count-1)
  1298.                     progression = crack_count-1;
  1299.                 // Dimension of a single crack stage
  1300.                 core::dimension2d<u32> dim_crack_cropped(
  1301.                     dim_crack.Width,
  1302.                     dim_crack.Width
  1303.                 );
  1304.                 // Create cropped and scaled crack images
  1305.                 video::IImage *img_crack_cropped = driver->createImage(
  1306.                         video::ECF_A8R8G8B8, dim_crack_cropped);
  1307.                 video::IImage *img_crack_scaled = driver->createImage(
  1308.                         video::ECF_A8R8G8B8, dim_base);
  1309.  
  1310.                 if(img_crack_cropped && img_crack_scaled)
  1311.                 {
  1312.                     // Crop crack image
  1313.                     v2s32 pos_crack(0, progression*dim_crack.Width);
  1314.                     img_crack->copyTo(img_crack_cropped,
  1315.                             v2s32(0,0),
  1316.                             core::rect<s32>(pos_crack, dim_crack_cropped));
  1317.                     // Scale crack image by copying
  1318.                     img_crack_cropped->copyToScaling(img_crack_scaled);
  1319.                     // Copy or overlay crack image
  1320.                     if(use_overlay)
  1321.                     {
  1322.                         overlay(baseimg, img_crack_scaled);
  1323.                     }
  1324.                     else
  1325.                     {
  1326.                         /*img_crack_scaled->copyToWithAlpha(
  1327.                                 baseimg,
  1328.                                 v2s32(0,0),
  1329.                                 core::rect<s32>(v2s32(0,0), dim_base),
  1330.                                 video::SColor(255,255,255,255));*/
  1331.                         blit_with_alpha(img_crack_scaled, baseimg,
  1332.                                 v2s32(0,0), v2s32(0,0), dim_base);
  1333.                     }
  1334.                 }
  1335.  
  1336.                 if(img_crack_scaled)
  1337.                     img_crack_scaled->drop();
  1338.  
  1339.                 if(img_crack_cropped)
  1340.                     img_crack_cropped->drop();
  1341.                
  1342.                 img_crack->drop();
  1343.             }
  1344.         }
  1345.         /*
  1346.             [combine:WxH:X,Y=filename:X,Y=filename2
  1347.             Creates a bigger texture from an amount of smaller ones
  1348.         */
  1349.         else if(part_of_name.substr(0,8) == "[combine")
  1350.         {
  1351.             Strfnd sf(part_of_name);
  1352.             sf.next(":");
  1353.             u32 w0 = stoi(sf.next("x"));
  1354.             u32 h0 = stoi(sf.next(":"));
  1355.             infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
  1356.             core::dimension2d<u32> dim(w0,h0);
  1357.             baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
  1358.             while(sf.atend() == false)
  1359.             {
  1360.                 u32 x = stoi(sf.next(","));
  1361.                 u32 y = stoi(sf.next("="));
  1362.                 std::string filename = sf.next(":");
  1363.                 infostream<<"Adding \""<<filename
  1364.                         <<"\" to combined ("<<x<<","<<y<<")"
  1365.                         <<std::endl;
  1366.                 video::IImage *img = sourcecache->getOrLoad(filename, device);
  1367.                 if(img)
  1368.                 {
  1369.                     core::dimension2d<u32> dim = img->getDimension();
  1370.                     infostream<<"Size "<<dim.Width
  1371.                             <<"x"<<dim.Height<<std::endl;
  1372.                     core::position2d<s32> pos_base(x, y);
  1373.                     video::IImage *img2 =
  1374.                             driver->createImage(video::ECF_A8R8G8B8, dim);
  1375.                     img->copyTo(img2);
  1376.                     img->drop();
  1377.                     img2->copyToWithAlpha(baseimg, pos_base,
  1378.                             core::rect<s32>(v2s32(0,0), dim),
  1379.                             video::SColor(255,255,255,255),
  1380.                             NULL);
  1381.                     img2->drop();
  1382.                 }
  1383.                 else
  1384.                 {
  1385.                     infostream<<"img==NULL"<<std::endl;
  1386.                 }
  1387.             }
  1388.         }
  1389.         /*
  1390.             "[brighten"
  1391.         */
  1392.         else if(part_of_name.substr(0,9) == "[brighten")
  1393.         {
  1394.             if(baseimg == NULL)
  1395.             {
  1396.                 errorstream<<"generate_image(): baseimg==NULL "
  1397.                         <<"for part_of_name=\""<<part_of_name
  1398.                         <<"\", cancelling."<<std::endl;
  1399.                 return false;
  1400.             }
  1401.  
  1402.             brighten(baseimg);
  1403.         }
  1404.         /*
  1405.             "[noalpha"
  1406.             Make image completely opaque.
  1407.             Used for the leaves texture when in old leaves mode, so
  1408.             that the transparent parts don't look completely black
  1409.             when simple alpha channel is used for rendering.
  1410.         */
  1411.         else if(part_of_name.substr(0,8) == "[noalpha")
  1412.         {
  1413.             if(baseimg == NULL)
  1414.             {
  1415.                 errorstream<<"generate_image(): baseimg==NULL "
  1416.                         <<"for part_of_name=\""<<part_of_name
  1417.                         <<"\", cancelling."<<std::endl;
  1418.                 return false;
  1419.             }
  1420.  
  1421.             core::dimension2d<u32> dim = baseimg->getDimension();
  1422.            
  1423.             // Set alpha to full
  1424.             for(u32 y=0; y<dim.Height; y++)
  1425.             for(u32 x=0; x<dim.Width; x++)
  1426.             {
  1427.                 video::SColor c = baseimg->getPixel(x,y);
  1428.                 c.setAlpha(255);
  1429.                 baseimg->setPixel(x,y,c);
  1430.             }
  1431.         }
  1432.         /*
  1433.             "[makealpha:R,G,B"
  1434.             Convert one color to transparent.
  1435.         */
  1436.         else if(part_of_name.substr(0,11) == "[makealpha:")
  1437.         {
  1438.             if(baseimg == NULL)
  1439.             {
  1440.                 errorstream<<"generate_image(): baseimg==NULL "
  1441.                         <<"for part_of_name=\""<<part_of_name
  1442.                         <<"\", cancelling."<<std::endl;
  1443.                 return false;
  1444.             }
  1445.  
  1446.             Strfnd sf(part_of_name.substr(11));
  1447.             u32 r1 = stoi(sf.next(","));
  1448.             u32 g1 = stoi(sf.next(","));
  1449.             u32 b1 = stoi(sf.next(""));
  1450.             std::string filename = sf.next("");
  1451.  
  1452.             core::dimension2d<u32> dim = baseimg->getDimension();
  1453.            
  1454.             /*video::IImage *oldbaseimg = baseimg;
  1455.             baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
  1456.             oldbaseimg->copyTo(baseimg);
  1457.             oldbaseimg->drop();*/
  1458.  
  1459.             // Set alpha to full
  1460.             for(u32 y=0; y<dim.Height; y++)
  1461.             for(u32 x=0; x<dim.Width; x++)
  1462.             {
  1463.                 video::SColor c = baseimg->getPixel(x,y);
  1464.                 u32 r = c.getRed();
  1465.                 u32 g = c.getGreen();
  1466.                 u32 b = c.getBlue();
  1467.                 if(!(r == r1 && g == g1 && b == b1))
  1468.                     continue;
  1469.                 c.setAlpha(0);
  1470.                 baseimg->setPixel(x,y,c);
  1471.             }
  1472.         }
  1473.         /*
  1474.             "[transformN"
  1475.             Rotates and/or flips the image.
  1476.  
  1477.             N can be a number (between 0 and 7) or a transform name.
  1478.             Rotations are counter-clockwise.
  1479.             0  I      identity
  1480.             1  R90    rotate by 90 degrees
  1481.             2  R180   rotate by 180 degrees
  1482.             3  R270   rotate by 270 degrees
  1483.             4  FX     flip X
  1484.             5  FXR90  flip X then rotate by 90 degrees
  1485.             6  FY     flip Y
  1486.             7  FYR90  flip Y then rotate by 90 degrees
  1487.  
  1488.             Note: Transform names can be concatenated to produce
  1489.             their product (applies the first then the second).
  1490.             The resulting transform will be equivalent to one of the
  1491.             eight existing ones, though (see: dihedral group).
  1492.         */
  1493.         else if(part_of_name.substr(0,10) == "[transform")
  1494.         {
  1495.             if(baseimg == NULL)
  1496.             {
  1497.                 errorstream<<"generate_image(): baseimg==NULL "
  1498.                         <<"for part_of_name=\""<<part_of_name
  1499.                         <<"\", cancelling."<<std::endl;
  1500.                 return false;
  1501.             }
  1502.  
  1503.             u32 transform = parseImageTransform(part_of_name.substr(10));
  1504.             core::dimension2d<u32> dim = imageTransformDimension(
  1505.                     transform, baseimg->getDimension());
  1506.             video::IImage *image = driver->createImage(
  1507.                     baseimg->getColorFormat(), dim);
  1508.             assert(image);
  1509.             imageTransform(transform, baseimg, image);
  1510.             baseimg->drop();
  1511.             baseimg = image;
  1512.         }
  1513.         /*
  1514.             [inventorycube{topimage{leftimage{rightimage
  1515.             In every subimage, replace ^ with &.
  1516.             Create an "inventory cube".
  1517.             NOTE: This should be used only on its own.
  1518.             Example (a grass block (not actually used in game):
  1519.             "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
  1520.         */
  1521.         else if(part_of_name.substr(0,14) == "[inventorycube")
  1522.         {
  1523.             if(baseimg != NULL)
  1524.             {
  1525.                 errorstream<<"generate_image(): baseimg!=NULL "
  1526.                         <<"for part_of_name=\""<<part_of_name
  1527.                         <<"\", cancelling."<<std::endl;
  1528.                 return false;
  1529.             }
  1530.  
  1531.             str_replace_char(part_of_name, '&', '^');
  1532.             Strfnd sf(part_of_name);
  1533.             sf.next("{");
  1534.             std::string imagename_top = sf.next("{");
  1535.             std::string imagename_left = sf.next("{");
  1536.             std::string imagename_right = sf.next("{");
  1537.  
  1538.             // Generate images for the faces of the cube
  1539.             video::IImage *img_top = generate_image_from_scratch(
  1540.                     imagename_top, device, sourcecache);
  1541.             video::IImage *img_left = generate_image_from_scratch(
  1542.                     imagename_left, device, sourcecache);
  1543.             video::IImage *img_right = generate_image_from_scratch(
  1544.                     imagename_right, device, sourcecache);
  1545.             assert(img_top && img_left && img_right);
  1546.  
  1547.             // Create textures from images
  1548.             video::ITexture *texture_top = driver->addTexture(
  1549.                     (imagename_top + "__temp__").c_str(), img_top);
  1550.             video::ITexture *texture_left = driver->addTexture(
  1551.                     (imagename_left + "__temp__").c_str(), img_left);
  1552.             video::ITexture *texture_right = driver->addTexture(
  1553.                     (imagename_right + "__temp__").c_str(), img_right);
  1554.             assert(texture_top && texture_left && texture_right);
  1555.  
  1556.             // Drop images
  1557.             img_top->drop();
  1558.             img_left->drop();
  1559.             img_right->drop();
  1560.            
  1561.             /*
  1562.                 Draw a cube mesh into a render target texture
  1563.             */
  1564.             scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
  1565.             setMeshColor(cube, video::SColor(255, 255, 255, 255));
  1566.             cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
  1567.             cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
  1568.             cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
  1569.             cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
  1570.             cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
  1571.             cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
  1572.  
  1573.             core::dimension2d<u32> dim(64,64);
  1574.             std::string rtt_texture_name = part_of_name + "_RTT";
  1575.  
  1576.             v3f camera_position(0, 1.0, -1.5);
  1577.             camera_position.rotateXZBy(45);
  1578.             v3f camera_lookat(0, 0, 0);
  1579.             core::CMatrix4<f32> camera_projection_matrix;
  1580.             // Set orthogonal projection
  1581.             camera_projection_matrix.buildProjectionMatrixOrthoLH(
  1582.                     1.65, 1.65, 0, 100);
  1583.  
  1584.             video::SColorf ambient_light(0.2,0.2,0.2);
  1585.             v3f light_position(10, 100, -50);
  1586.             video::SColorf light_color(0.5,0.5,0.5);
  1587.             f32 light_radius = 1000;
  1588.  
  1589.             video::ITexture *rtt = generateTextureFromMesh(
  1590.                     cube, device, dim, rtt_texture_name,
  1591.                     camera_position,
  1592.                     camera_lookat,
  1593.                     camera_projection_matrix,
  1594.                     ambient_light,
  1595.                     light_position,
  1596.                     light_color,
  1597.                     light_radius);
  1598.            
  1599.             // Drop mesh
  1600.             cube->drop();
  1601.  
  1602.             // Free textures of images
  1603.             driver->removeTexture(texture_top);
  1604.             driver->removeTexture(texture_left);
  1605.             driver->removeTexture(texture_right);
  1606.            
  1607.             if(rtt == NULL)
  1608.             {
  1609.                 baseimg = generate_image_from_scratch(
  1610.                         imagename_top, device, sourcecache);
  1611.                 return true;
  1612.             }
  1613.  
  1614.             // Create image of render target
  1615.             video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
  1616.             assert(image);
  1617.  
  1618.             baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
  1619.  
  1620.             if(image)
  1621.             {
  1622.                 image->copyTo(baseimg);
  1623.                 image->drop();
  1624.  
  1625.                 // BEGIN WIKI IMAGE EXTRACT
  1626.                 infostream<<"part_of_name = "<<part_of_name<<std::endl;
  1627.                 string se(part_of_name);
  1628.                 replace(se.begin(), se.end(), ':', '-');
  1629.                 irr::c8 filename[200];
  1630.                 snprintf(filename, 200, "itemcubes/%s.png", se.c_str());
  1631.                 driver->writeImageToFile(baseimg, filename);
  1632.                 // END WIKI IMAGE EXTRACT
  1633.                
  1634.             }
  1635.         }
  1636.         /*
  1637.             [lowpart:percent:filename
  1638.             Adds the lower part of a texture
  1639.         */
  1640.         else if(part_of_name.substr(0,9) == "[lowpart:")
  1641.         {
  1642.             Strfnd sf(part_of_name);
  1643.             sf.next(":");
  1644.             u32 percent = stoi(sf.next(":"));
  1645.             std::string filename = sf.next(":");
  1646.             //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
  1647.  
  1648.             if(baseimg == NULL)
  1649.                 baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
  1650.             video::IImage *img = sourcecache->getOrLoad(filename, device);
  1651.             if(img)
  1652.             {
  1653.                 core::dimension2d<u32> dim = img->getDimension();
  1654.                 core::position2d<s32> pos_base(0, 0);
  1655.                 video::IImage *img2 =
  1656.                         driver->createImage(video::ECF_A8R8G8B8, dim);
  1657.                 img->copyTo(img2);
  1658.                 img->drop();
  1659.                 core::position2d<s32> clippos(0, 0);
  1660.                 clippos.Y = dim.Height * (100-percent) / 100;
  1661.                 core::dimension2d<u32> clipdim = dim;
  1662.                 clipdim.Height = clipdim.Height * percent / 100 + 1;
  1663.                 core::rect<s32> cliprect(clippos, clipdim);
  1664.                 img2->copyToWithAlpha(baseimg, pos_base,
  1665.                         core::rect<s32>(v2s32(0,0), dim),
  1666.                         video::SColor(255,255,255,255),
  1667.                         &cliprect);
  1668.                 img2->drop();
  1669.             }
  1670.         }
  1671.         /*
  1672.             [verticalframe:N:I
  1673.             Crops a frame of a vertical animation.
  1674.             N = frame count, I = frame index
  1675.         */
  1676.         else if(part_of_name.substr(0,15) == "[verticalframe:")
  1677.         {
  1678.             Strfnd sf(part_of_name);
  1679.             sf.next(":");
  1680.             u32 frame_count = stoi(sf.next(":"));
  1681.             u32 frame_index = stoi(sf.next(":"));
  1682.  
  1683.             if(baseimg == NULL){
  1684.                 errorstream<<"generate_image(): baseimg!=NULL "
  1685.                         <<"for part_of_name=\""<<part_of_name
  1686.                         <<"\", cancelling."<<std::endl;
  1687.                 return false;
  1688.             }
  1689.            
  1690.             v2u32 frame_size = baseimg->getDimension();
  1691.             frame_size.Y /= frame_count;
  1692.  
  1693.             video::IImage *img = driver->createImage(video::ECF_A8R8G8B8,
  1694.                     frame_size);
  1695.             if(!img){
  1696.                 errorstream<<"generate_image(): Could not create image "
  1697.                         <<"for part_of_name=\""<<part_of_name
  1698.                         <<"\", cancelling."<<std::endl;
  1699.                 return false;
  1700.             }
  1701.  
  1702.             // Fill target image with transparency
  1703.             img->fill(video::SColor(0,0,0,0));
  1704.  
  1705.             core::dimension2d<u32> dim = frame_size;
  1706.             core::position2d<s32> pos_dst(0, 0);
  1707.             core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
  1708.             baseimg->copyToWithAlpha(img, pos_dst,
  1709.                     core::rect<s32>(pos_src, dim),
  1710.                     video::SColor(255,255,255,255),
  1711.                     NULL);
  1712.             // Replace baseimg
  1713.             baseimg->drop();
  1714.             baseimg = img;
  1715.         }
  1716.         else
  1717.         {
  1718.             errorstream<<"generate_image(): Invalid "
  1719.                     " modification: \""<<part_of_name<<"\""<<std::endl;
  1720.         }
  1721.     }
  1722.  
  1723.     return true;
  1724. }
  1725.  
  1726. void overlay(video::IImage *image, video::IImage *overlay)
  1727. {
  1728.     /*
  1729.         Copy overlay to image, taking alpha into account.
  1730.         Where image is transparent, don't copy from overlay.
  1731.         Images sizes must be identical.
  1732.     */
  1733.     if(image == NULL || overlay == NULL)
  1734.         return;
  1735.    
  1736.     core::dimension2d<u32> dim = image->getDimension();
  1737.     core::dimension2d<u32> dim_overlay = overlay->getDimension();
  1738.     assert(dim == dim_overlay);
  1739.  
  1740.     for(u32 y=0; y<dim.Height; y++)
  1741.     for(u32 x=0; x<dim.Width; x++)
  1742.     {
  1743.         video::SColor c1 = image->getPixel(x,y);
  1744.         video::SColor c2 = overlay->getPixel(x,y);
  1745.         u32 a1 = c1.getAlpha();
  1746.         u32 a2 = c2.getAlpha();
  1747.         if(a1 == 255 && a2 != 0)
  1748.         {
  1749.             c1.setRed((c1.getRed()*(255-a2) + c2.getRed()*a2)/255);
  1750.             c1.setGreen((c1.getGreen()*(255-a2) + c2.getGreen()*a2)/255);
  1751.             c1.setBlue((c1.getBlue()*(255-a2) + c2.getBlue()*a2)/255);
  1752.         }
  1753.         image->setPixel(x,y,c1);
  1754.     }
  1755. }
  1756.  
  1757. /*
  1758.     Draw an image on top of an another one, using the alpha channel of the
  1759.     source image
  1760.  
  1761.     This exists because IImage::copyToWithAlpha() doesn't seem to always
  1762.     work.
  1763. */
  1764. static void blit_with_alpha(video::IImage *src, video::IImage *dst,
  1765.         v2s32 src_pos, v2s32 dst_pos, v2u32 size)
  1766. {
  1767.     for(u32 y0=0; y0<size.Y; y0++)
  1768.     for(u32 x0=0; x0<size.X; x0++)
  1769.     {
  1770.         s32 src_x = src_pos.X + x0;
  1771.         s32 src_y = src_pos.Y + y0;
  1772.         s32 dst_x = dst_pos.X + x0;
  1773.         s32 dst_y = dst_pos.Y + y0;
  1774.         video::SColor src_c = src->getPixel(src_x, src_y);
  1775.         video::SColor dst_c = dst->getPixel(dst_x, dst_y);
  1776.         dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
  1777.         dst->setPixel(dst_x, dst_y, dst_c);
  1778.     }
  1779. }
  1780.  
  1781. void brighten(video::IImage *image)
  1782. {
  1783.     if(image == NULL)
  1784.         return;
  1785.    
  1786.     core::dimension2d<u32> dim = image->getDimension();
  1787.  
  1788.     for(u32 y=0; y<dim.Height; y++)
  1789.     for(u32 x=0; x<dim.Width; x++)
  1790.     {
  1791.         video::SColor c = image->getPixel(x,y);
  1792.         c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
  1793.         c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
  1794.         c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
  1795.         image->setPixel(x,y,c);
  1796.     }
  1797. }
  1798.  
  1799. u32 parseImageTransform(const std::string& s)
  1800. {
  1801.     int total_transform = 0;
  1802.  
  1803.     std::string transform_names[8];
  1804.     transform_names[0] = "i";
  1805.     transform_names[1] = "r90";
  1806.     transform_names[2] = "r180";
  1807.     transform_names[3] = "r270";
  1808.     transform_names[4] = "fx";
  1809.     transform_names[6] = "fy";
  1810.  
  1811.     std::size_t pos = 0;
  1812.     while(pos < s.size())
  1813.     {
  1814.         int transform = -1;
  1815.         for(int i = 0; i <= 7; ++i)
  1816.         {
  1817.             const std::string &name_i = transform_names[i];
  1818.  
  1819.             if(s[pos] == ('0' + i))
  1820.             {
  1821.                 transform = i;
  1822.                 pos++;
  1823.                 break;
  1824.             }
  1825.             else if(!(name_i.empty()) &&
  1826.                 lowercase(s.substr(pos, name_i.size())) == name_i)
  1827.             {
  1828.                 transform = i;
  1829.                 pos += name_i.size();
  1830.                 break;
  1831.             }
  1832.         }
  1833.         if(transform < 0)
  1834.             break;
  1835.  
  1836.         // Multiply total_transform and transform in the group D4
  1837.         int new_total = 0;
  1838.         if(transform < 4)
  1839.             new_total = (transform + total_transform) % 4;
  1840.         else
  1841.             new_total = (transform - total_transform + 8) % 4;
  1842.         if((transform >= 4) ^ (total_transform >= 4))
  1843.             new_total += 4;
  1844.  
  1845.         total_transform = new_total;
  1846.     }
  1847.     return total_transform;
  1848. }
  1849.  
  1850. core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
  1851. {
  1852.     if(transform % 2 == 0)
  1853.         return dim;
  1854.     else
  1855.         return core::dimension2d<u32>(dim.Height, dim.Width);
  1856. }
  1857.  
  1858. void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
  1859. {
  1860.     if(src == NULL || dst == NULL)
  1861.         return;
  1862.    
  1863.     core::dimension2d<u32> srcdim = src->getDimension();
  1864.     core::dimension2d<u32> dstdim = dst->getDimension();
  1865.  
  1866.     assert(dstdim == imageTransformDimension(transform, srcdim));
  1867.     assert(transform >= 0 && transform <= 7);
  1868.  
  1869.     /*
  1870.         Compute the transformation from source coordinates (sx,sy)
  1871.         to destination coordinates (dx,dy).
  1872.     */
  1873.     int sxn = 0;
  1874.     int syn = 2;
  1875.     if(transform == 0)         // identity
  1876.         sxn = 0, syn = 2;  //   sx = dx, sy = dy
  1877.     else if(transform == 1)    // rotate by 90 degrees ccw
  1878.         sxn = 3, syn = 0;  //   sx = (H-1) - dy, sy = dx
  1879.     else if(transform == 2)    // rotate by 180 degrees
  1880.         sxn = 1, syn = 3;  //   sx = (W-1) - dx, sy = (H-1) - dy
  1881.     else if(transform == 3)    // rotate by 270 degrees ccw
  1882.         sxn = 2, syn = 1;  //   sx = dy, sy = (W-1) - dx
  1883.     else if(transform == 4)    // flip x
  1884.         sxn = 1, syn = 2;  //   sx = (W-1) - dx, sy = dy
  1885.     else if(transform == 5)    // flip x then rotate by 90 degrees ccw
  1886.         sxn = 2, syn = 0;  //   sx = dy, sy = dx
  1887.     else if(transform == 6)    // flip y
  1888.         sxn = 0, syn = 3;  //   sx = dx, sy = (H-1) - dy
  1889.     else if(transform == 7)    // flip y then rotate by 90 degrees ccw
  1890.         sxn = 3, syn = 1;  //   sx = (H-1) - dy, sy = (W-1) - dx
  1891.  
  1892.     for(u32 dy=0; dy<dstdim.Height; dy++)
  1893.     for(u32 dx=0; dx<dstdim.Width; dx++)
  1894.     {
  1895.         u32 entries[4] = {dx, dstdim.Width-1-dx, dy, dstdim.Height-1-dy};
  1896.         u32 sx = entries[sxn];
  1897.         u32 sy = entries[syn];
  1898.         video::SColor c = src->getPixel(sx,sy);
  1899.         dst->setPixel(dx,dy,c);
  1900.     }
  1901. }
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×