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