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