Fix Result of processed Request was written to invalid (non existent) ResultQueue...
[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                 static 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                         while(true) {
526                                 // Wait result for a second
527                                 GetResult<std::string, u32, u8, u8>
528                                         result = result_queue.pop_front(1000);
529
530                                 if (result.key == name) {
531                                         return result.item;
532                                 }
533                         }
534                 }
535                 catch(ItemNotFoundException &e)
536                 {
537                         errorstream<<"Waiting for texture " << name << " timed out."<<std::endl;
538                         return 0;
539                 }
540         }
541
542         infostream<<"getTextureId(): Failed"<<std::endl;
543
544         return 0;
545 }
546
547 // Draw an image on top of an another one, using the alpha channel of the
548 // source image
549 static void blit_with_alpha(video::IImage *src, video::IImage *dst,
550                 v2s32 src_pos, v2s32 dst_pos, v2u32 size);
551
552 // Like blit_with_alpha, but only modifies destination pixels that
553 // are fully opaque
554 static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
555                 v2s32 src_pos, v2s32 dst_pos, v2u32 size);
556
557 // Draw or overlay a crack
558 static void draw_crack(video::IImage *crack, video::IImage *dst,
559                 bool use_overlay, s32 frame_count, s32 progression,
560                 video::IVideoDriver *driver);
561
562 // Brighten image
563 void brighten(video::IImage *image);
564 // Parse a transform name
565 u32 parseImageTransform(const std::string& s);
566 // Apply transform to image dimension
567 core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim);
568 // Apply transform to image data
569 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
570
571 /*
572         This method generates all the textures
573 */
574 u32 TextureSource::getTextureIdDirect(const std::string &name)
575 {
576         //infostream<<"getTextureIdDirect(): name=\""<<name<<"\""<<std::endl;
577
578         // Empty name means texture 0
579         if(name == "")
580         {
581                 infostream<<"getTextureIdDirect(): name is empty"<<std::endl;
582                 return 0;
583         }
584
585         /*
586                 Calling only allowed from main thread
587         */
588         if(get_current_thread_id() != m_main_thread)
589         {
590                 errorstream<<"TextureSource::getTextureIdDirect() "
591                                 "called not from main thread"<<std::endl;
592                 return 0;
593         }
594
595         /*
596                 See if texture already exists
597         */
598         {
599                 JMutexAutoLock lock(m_textureinfo_cache_mutex);
600
601                 std::map<std::string, u32>::iterator n;
602                 n = m_name_to_id.find(name);
603                 if(n != m_name_to_id.end())
604                 {
605                         /*infostream<<"getTextureIdDirect(): \""<<name
606                                         <<"\" found in cache"<<std::endl;*/
607                         return n->second;
608                 }
609         }
610
611         /*infostream<<"getTextureIdDirect(): \""<<name
612                         <<"\" NOT found in cache. Creating it."<<std::endl;*/
613
614         /*
615                 Get the base image
616         */
617
618         char separator = '^';
619
620         /*
621                 This is set to the id of the base image.
622                 If left 0, there is no base image and a completely new image
623                 is made.
624         */
625         u32 base_image_id = 0;
626
627         // Find last meta separator in name
628         s32 last_separator_position = -1;
629         for(s32 i=name.size()-1; i>=0; i--)
630         {
631                 if(name[i] == separator)
632                 {
633                         last_separator_position = i;
634                         break;
635                 }
636         }
637         /*
638                 If separator was found, construct the base name and make the
639                 base image using a recursive call
640         */
641         std::string base_image_name;
642         if(last_separator_position != -1)
643         {
644                 // Construct base name
645                 base_image_name = name.substr(0, last_separator_position);
646                 /*infostream<<"getTextureIdDirect(): Calling itself recursively"
647                                 " to get base image of \""<<name<<"\" = \""
648                 <<base_image_name<<"\""<<std::endl;*/
649                 base_image_id = getTextureIdDirect(base_image_name);
650         }
651
652         //infostream<<"base_image_id="<<base_image_id<<std::endl;
653
654         video::IVideoDriver* driver = m_device->getVideoDriver();
655         assert(driver);
656
657         video::ITexture *t = NULL;
658
659         /*
660                 An image will be built from files and then converted into a texture.
661         */
662         video::IImage *baseimg = NULL;
663
664         // If a base image was found, copy it to baseimg
665         if(base_image_id != 0)
666         {
667                 JMutexAutoLock lock(m_textureinfo_cache_mutex);
668
669                 TextureInfo *ti = &m_textureinfo_cache[base_image_id];
670
671                 if(ti->img == NULL)
672                 {
673                         infostream<<"getTextureIdDirect(): WARNING: NULL image in "
674                                         <<"cache: \""<<base_image_name<<"\""
675                                         <<std::endl;
676                 }
677                 else
678                 {
679                         core::dimension2d<u32> dim = ti->img->getDimension();
680
681                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
682
683                         ti->img->copyTo(
684                                         baseimg, // target
685                                         v2s32(0,0), // position in target
686                                         core::rect<s32>(v2s32(0,0), dim) // from
687                         );
688
689                         /*infostream<<"getTextureIdDirect(): Loaded \""
690                                         <<base_image_name<<"\" from image cache"
691                                         <<std::endl;*/
692                 }
693         }
694
695         /*
696                 Parse out the last part of the name of the image and act
697                 according to it
698         */
699
700         std::string last_part_of_name = name.substr(last_separator_position+1);
701         //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
702
703         // Generate image according to part of name
704         if(!generateImage(last_part_of_name, baseimg))
705         {
706                 errorstream<<"getTextureIdDirect(): "
707                                 "failed to generate \""<<last_part_of_name<<"\""
708                                 <<std::endl;
709         }
710
711         // If no resulting image, print a warning
712         if(baseimg == NULL)
713         {
714                 errorstream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
715                                 " create texture \""<<name<<"\""<<std::endl;
716         }
717
718         if(baseimg != NULL)
719         {
720                 // Create texture from resulting image
721                 t = driver->addTexture(name.c_str(), baseimg);
722         }
723
724         /*
725                 Add texture to caches (add NULL textures too)
726         */
727
728         JMutexAutoLock lock(m_textureinfo_cache_mutex);
729
730         u32 id = m_textureinfo_cache.size();
731         TextureInfo ti(name, t, baseimg);
732         m_textureinfo_cache.push_back(ti);
733         m_name_to_id[name] = id;
734
735         /*infostream<<"getTextureIdDirect(): "
736                         <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
737
738         return id;
739 }
740
741 std::string TextureSource::getTextureName(u32 id)
742 {
743         JMutexAutoLock lock(m_textureinfo_cache_mutex);
744
745         if(id >= m_textureinfo_cache.size())
746         {
747                 errorstream<<"TextureSource::getTextureName(): id="<<id
748                                 <<" >= m_textureinfo_cache.size()="
749                                 <<m_textureinfo_cache.size()<<std::endl;
750                 return "";
751         }
752
753         return m_textureinfo_cache[id].name;
754 }
755
756 video::ITexture* TextureSource::getTexture(u32 id)
757 {
758         JMutexAutoLock lock(m_textureinfo_cache_mutex);
759
760         if(id >= m_textureinfo_cache.size())
761                 return NULL;
762
763         return m_textureinfo_cache[id].texture;
764 }
765
766 video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id)
767 {
768         u32 actual_id = getTextureId(name);
769         if(id){
770                 *id = actual_id;
771         }
772         return getTexture(actual_id);
773 }
774
775 void TextureSource::processQueue()
776 {
777         /*
778                 Fetch textures
779         */
780         if(!m_get_texture_queue.empty())
781         {
782                 GetRequest<std::string, u32, u8, u8>
783                                 request = m_get_texture_queue.pop();
784
785                 /*infostream<<"TextureSource::processQueue(): "
786                                 <<"got texture request with "
787                                 <<"name=\""<<request.key<<"\""
788                                 <<std::endl;*/
789
790                 m_get_texture_queue.pushResult(request,getTextureIdDirect(request.key));
791         }
792 }
793
794 void TextureSource::insertSourceImage(const std::string &name, video::IImage *img)
795 {
796         //infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
797
798         assert(get_current_thread_id() == m_main_thread);
799
800         m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
801         m_source_image_existence.set(name, true);
802 }
803
804 void TextureSource::rebuildImagesAndTextures()
805 {
806         JMutexAutoLock lock(m_textureinfo_cache_mutex);
807
808         video::IVideoDriver* driver = m_device->getVideoDriver();
809
810         // Recreate textures
811         for(u32 i=0; i<m_textureinfo_cache.size(); i++){
812                 TextureInfo *ti = &m_textureinfo_cache[i];
813                 video::IImage *img = generateImageFromScratch(ti->name);
814                 // Create texture from resulting image
815                 video::ITexture *t = NULL;
816                 if(img)
817                         t = driver->addTexture(ti->name.c_str(), img);
818                 video::ITexture *t_old = ti->texture;
819                 // Replace texture
820                 ti->texture = t;
821                 ti->img = img;
822
823                 if (t_old != 0)
824                         m_texture_trash.push_back(t_old);
825         }
826 }
827
828 video::ITexture* TextureSource::generateTextureFromMesh(
829                 const TextureFromMeshParams &params)
830 {
831         video::IVideoDriver *driver = m_device->getVideoDriver();
832         assert(driver);
833
834         if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
835         {
836                 static bool warned = false;
837                 if(!warned)
838                 {
839                         errorstream<<"TextureSource::generateTextureFromMesh(): "
840                                 <<"EVDF_RENDER_TO_TARGET not supported."<<std::endl;
841                         warned = true;
842                 }
843                 return NULL;
844         }
845
846         // Create render target texture
847         video::ITexture *rtt = driver->addRenderTargetTexture(
848                         params.dim, params.rtt_texture_name.c_str(),
849                         video::ECF_A8R8G8B8);
850         if(rtt == NULL)
851         {
852                 errorstream<<"TextureSource::generateTextureFromMesh(): "
853                         <<"addRenderTargetTexture returned NULL."<<std::endl;
854                 return NULL;
855         }
856
857         // Set render target
858         driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0));
859
860         // Get a scene manager
861         scene::ISceneManager *smgr_main = m_device->getSceneManager();
862         assert(smgr_main);
863         scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
864         assert(smgr);
865
866         scene::IMeshSceneNode* meshnode = smgr->addMeshSceneNode(params.mesh, NULL, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
867         meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
868         meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
869         meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter);
870         meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter);
871         meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter);
872
873         scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
874                         params.camera_position, params.camera_lookat);
875         // second parameter of setProjectionMatrix (isOrthogonal) is ignored
876         camera->setProjectionMatrix(params.camera_projection_matrix, false);
877
878         smgr->setAmbientLight(params.ambient_light);
879         smgr->addLightSceneNode(0,
880                         params.light_position,
881                         params.light_color,
882                         params.light_radius);
883
884         // Render scene
885         driver->beginScene(true, true, video::SColor(0,0,0,0));
886         smgr->drawAll();
887         driver->endScene();
888
889         // NOTE: The scene nodes should not be dropped, otherwise
890         //       smgr->drop() segfaults
891         /*cube->drop();
892         camera->drop();
893         light->drop();*/
894         // Drop scene manager
895         smgr->drop();
896
897         // Unset render target
898         driver->setRenderTarget(0, false, true, 0);
899
900         if(params.delete_texture_on_shutdown)
901                 m_texture_trash.push_back(rtt);
902
903         return rtt;
904 }
905
906 video::IImage* TextureSource::generateImageFromScratch(std::string name)
907 {
908         /*infostream<<"generateImageFromScratch(): "
909                         "\""<<name<<"\""<<std::endl;*/
910
911         video::IVideoDriver *driver = m_device->getVideoDriver();
912         assert(driver);
913
914         /*
915                 Get the base image
916         */
917
918         video::IImage *baseimg = NULL;
919
920         char separator = '^';
921
922         // Find last meta separator in name
923         s32 last_separator_position = name.find_last_of(separator);
924
925         /*
926                 If separator was found, construct the base name and make the
927                 base image using a recursive call
928         */
929         std::string base_image_name;
930         if(last_separator_position != -1)
931         {
932                 // Construct base name
933                 base_image_name = name.substr(0, last_separator_position);
934                 baseimg = generateImageFromScratch(base_image_name);
935         }
936
937         /*
938                 Parse out the last part of the name of the image and act
939                 according to it
940         */
941
942         std::string last_part_of_name = name.substr(last_separator_position+1);
943
944         // Generate image according to part of name
945         if(!generateImage(last_part_of_name, baseimg))
946         {
947                 errorstream<<"generateImageFromScratch(): "
948                                 "failed to generate \""<<last_part_of_name<<"\""
949                                 <<std::endl;
950                 return NULL;
951         }
952
953         return baseimg;
954 }
955
956 bool TextureSource::generateImage(std::string part_of_name, video::IImage *& baseimg)
957 {
958         video::IVideoDriver* driver = m_device->getVideoDriver();
959         assert(driver);
960
961         // Stuff starting with [ are special commands
962         if(part_of_name.size() == 0 || part_of_name[0] != '[')
963         {
964                 video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device);
965
966                 if(image == NULL)
967                 {
968                         if(part_of_name != ""){
969                                 errorstream<<"generateImage(): Could not load image \""
970                                                 <<part_of_name<<"\""<<" while building texture"<<std::endl;
971                                 errorstream<<"generateImage(): Creating a dummy"
972                                                 <<" image for \""<<part_of_name<<"\""<<std::endl;
973                         }
974
975                         // Just create a dummy image
976                         //core::dimension2d<u32> dim(2,2);
977                         core::dimension2d<u32> dim(1,1);
978                         image = driver->createImage(video::ECF_A8R8G8B8, dim);
979                         assert(image);
980                         /*image->setPixel(0,0, video::SColor(255,255,0,0));
981                         image->setPixel(1,0, video::SColor(255,0,255,0));
982                         image->setPixel(0,1, video::SColor(255,0,0,255));
983                         image->setPixel(1,1, video::SColor(255,255,0,255));*/
984                         image->setPixel(0,0, video::SColor(255,myrand()%256,
985                                         myrand()%256,myrand()%256));
986                         /*image->setPixel(1,0, video::SColor(255,myrand()%256,
987                                         myrand()%256,myrand()%256));
988                         image->setPixel(0,1, video::SColor(255,myrand()%256,
989                                         myrand()%256,myrand()%256));
990                         image->setPixel(1,1, video::SColor(255,myrand()%256,
991                                         myrand()%256,myrand()%256));*/
992                 }
993
994                 // If base image is NULL, load as base.
995                 if(baseimg == NULL)
996                 {
997                         //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
998                         /*
999                                 Copy it this way to get an alpha channel.
1000                                 Otherwise images with alpha cannot be blitted on
1001                                 images that don't have alpha in the original file.
1002                         */
1003                         core::dimension2d<u32> dim = image->getDimension();
1004                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1005                         image->copyTo(baseimg);
1006                 }
1007                 // Else blit on base.
1008                 else
1009                 {
1010                         //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
1011                         // Size of the copied area
1012                         core::dimension2d<u32> dim = image->getDimension();
1013                         //core::dimension2d<u32> dim(16,16);
1014                         // Position to copy the blitted to in the base image
1015                         core::position2d<s32> pos_to(0,0);
1016                         // Position to copy the blitted from in the blitted image
1017                         core::position2d<s32> pos_from(0,0);
1018                         // Blit
1019                         /*image->copyToWithAlpha(baseimg, pos_to,
1020                                         core::rect<s32>(pos_from, dim),
1021                                         video::SColor(255,255,255,255),
1022                                         NULL);*/
1023                         blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
1024                 }
1025                 //cleanup
1026                 image->drop();
1027         }
1028         else
1029         {
1030                 // A special texture modification
1031
1032                 /*infostream<<"generateImage(): generating special "
1033                                 <<"modification \""<<part_of_name<<"\""
1034                                 <<std::endl;*/
1035
1036                 /*
1037                         [crack:N:P
1038                         [cracko:N:P
1039                         Adds a cracking texture
1040                         N = animation frame count, P = crack progression
1041                 */
1042                 if(part_of_name.substr(0,6) == "[crack")
1043                 {
1044                         if(baseimg == NULL)
1045                         {
1046                                 errorstream<<"generateImage(): baseimg==NULL "
1047                                                 <<"for part_of_name=\""<<part_of_name
1048                                                 <<"\", cancelling."<<std::endl;
1049                                 return false;
1050                         }
1051
1052                         // Crack image number and overlay option
1053                         bool use_overlay = (part_of_name[6] == 'o');
1054                         Strfnd sf(part_of_name);
1055                         sf.next(":");
1056                         s32 frame_count = stoi(sf.next(":"));
1057                         s32 progression = stoi(sf.next(":"));
1058
1059                         /*
1060                                 Load crack image.
1061
1062                                 It is an image with a number of cracking stages
1063                                 horizontally tiled.
1064                         */
1065                         video::IImage *img_crack = m_sourcecache.getOrLoad(
1066                                         "crack_anylength.png", m_device);
1067
1068                         if(img_crack && progression >= 0)
1069                         {
1070                                 draw_crack(img_crack, baseimg,
1071                                                 use_overlay, frame_count,
1072                                                 progression, driver);
1073                                 img_crack->drop();
1074                         }
1075                 }
1076                 /*
1077                         [combine:WxH:X,Y=filename:X,Y=filename2
1078                         Creates a bigger texture from an amount of smaller ones
1079                 */
1080                 else if(part_of_name.substr(0,8) == "[combine")
1081                 {
1082                         Strfnd sf(part_of_name);
1083                         sf.next(":");
1084                         u32 w0 = stoi(sf.next("x"));
1085                         u32 h0 = stoi(sf.next(":"));
1086                         infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
1087                         core::dimension2d<u32> dim(w0,h0);
1088                         if(baseimg == NULL)
1089                         {
1090                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1091                                 baseimg->fill(video::SColor(0,0,0,0));
1092                         }
1093                         while(sf.atend() == false)
1094                         {
1095                                 u32 x = stoi(sf.next(","));
1096                                 u32 y = stoi(sf.next("="));
1097                                 std::string filename = sf.next(":");
1098                                 infostream<<"Adding \""<<filename
1099                                                 <<"\" to combined ("<<x<<","<<y<<")"
1100                                                 <<std::endl;
1101                                 video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
1102                                 if(img)
1103                                 {
1104                                         core::dimension2d<u32> dim = img->getDimension();
1105                                         infostream<<"Size "<<dim.Width
1106                                                         <<"x"<<dim.Height<<std::endl;
1107                                         core::position2d<s32> pos_base(x, y);
1108                                         video::IImage *img2 =
1109                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
1110                                         img->copyTo(img2);
1111                                         img->drop();
1112                                         /*img2->copyToWithAlpha(baseimg, pos_base,
1113                                                         core::rect<s32>(v2s32(0,0), dim),
1114                                                         video::SColor(255,255,255,255),
1115                                                         NULL);*/
1116                                         blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
1117                                         img2->drop();
1118                                 }
1119                                 else
1120                                 {
1121                                         infostream<<"img==NULL"<<std::endl;
1122                                 }
1123                         }
1124                 }
1125                 /*
1126                         "[brighten"
1127                 */
1128                 else if(part_of_name.substr(0,9) == "[brighten")
1129                 {
1130                         if(baseimg == NULL)
1131                         {
1132                                 errorstream<<"generateImage(): baseimg==NULL "
1133                                                 <<"for part_of_name=\""<<part_of_name
1134                                                 <<"\", cancelling."<<std::endl;
1135                                 return false;
1136                         }
1137
1138                         brighten(baseimg);
1139                 }
1140                 /*
1141                         "[noalpha"
1142                         Make image completely opaque.
1143                         Used for the leaves texture when in old leaves mode, so
1144                         that the transparent parts don't look completely black
1145                         when simple alpha channel is used for rendering.
1146                 */
1147                 else if(part_of_name.substr(0,8) == "[noalpha")
1148                 {
1149                         if(baseimg == NULL)
1150                         {
1151                                 errorstream<<"generateImage(): baseimg==NULL "
1152                                                 <<"for part_of_name=\""<<part_of_name
1153                                                 <<"\", cancelling."<<std::endl;
1154                                 return false;
1155                         }
1156
1157                         core::dimension2d<u32> dim = baseimg->getDimension();
1158
1159                         // Set alpha to full
1160                         for(u32 y=0; y<dim.Height; y++)
1161                         for(u32 x=0; x<dim.Width; x++)
1162                         {
1163                                 video::SColor c = baseimg->getPixel(x,y);
1164                                 c.setAlpha(255);
1165                                 baseimg->setPixel(x,y,c);
1166                         }
1167                 }
1168                 /*
1169                         "[makealpha:R,G,B"
1170                         Convert one color to transparent.
1171                 */
1172                 else if(part_of_name.substr(0,11) == "[makealpha:")
1173                 {
1174                         if(baseimg == NULL)
1175                         {
1176                                 errorstream<<"generateImage(): baseimg==NULL "
1177                                                 <<"for part_of_name=\""<<part_of_name
1178                                                 <<"\", cancelling."<<std::endl;
1179                                 return false;
1180                         }
1181
1182                         Strfnd sf(part_of_name.substr(11));
1183                         u32 r1 = stoi(sf.next(","));
1184                         u32 g1 = stoi(sf.next(","));
1185                         u32 b1 = stoi(sf.next(""));
1186                         std::string filename = sf.next("");
1187
1188                         core::dimension2d<u32> dim = baseimg->getDimension();
1189
1190                         /*video::IImage *oldbaseimg = baseimg;
1191                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1192                         oldbaseimg->copyTo(baseimg);
1193                         oldbaseimg->drop();*/
1194
1195                         // Set alpha to full
1196                         for(u32 y=0; y<dim.Height; y++)
1197                         for(u32 x=0; x<dim.Width; x++)
1198                         {
1199                                 video::SColor c = baseimg->getPixel(x,y);
1200                                 u32 r = c.getRed();
1201                                 u32 g = c.getGreen();
1202                                 u32 b = c.getBlue();
1203                                 if(!(r == r1 && g == g1 && b == b1))
1204                                         continue;
1205                                 c.setAlpha(0);
1206                                 baseimg->setPixel(x,y,c);
1207                         }
1208                 }
1209                 /*
1210                         "[transformN"
1211                         Rotates and/or flips the image.
1212
1213                         N can be a number (between 0 and 7) or a transform name.
1214                         Rotations are counter-clockwise.
1215                         0  I      identity
1216                         1  R90    rotate by 90 degrees
1217                         2  R180   rotate by 180 degrees
1218                         3  R270   rotate by 270 degrees
1219                         4  FX     flip X
1220                         5  FXR90  flip X then rotate by 90 degrees
1221                         6  FY     flip Y
1222                         7  FYR90  flip Y then rotate by 90 degrees
1223
1224                         Note: Transform names can be concatenated to produce
1225                         their product (applies the first then the second).
1226                         The resulting transform will be equivalent to one of the
1227                         eight existing ones, though (see: dihedral group).
1228                 */
1229                 else if(part_of_name.substr(0,10) == "[transform")
1230                 {
1231                         if(baseimg == NULL)
1232                         {
1233                                 errorstream<<"generateImage(): baseimg==NULL "
1234                                                 <<"for part_of_name=\""<<part_of_name
1235                                                 <<"\", cancelling."<<std::endl;
1236                                 return false;
1237                         }
1238
1239                         u32 transform = parseImageTransform(part_of_name.substr(10));
1240                         core::dimension2d<u32> dim = imageTransformDimension(
1241                                         transform, baseimg->getDimension());
1242                         video::IImage *image = driver->createImage(
1243                                         baseimg->getColorFormat(), dim);
1244                         assert(image);
1245                         imageTransform(transform, baseimg, image);
1246                         baseimg->drop();
1247                         baseimg = image;
1248                 }
1249                 /*
1250                         [inventorycube{topimage{leftimage{rightimage
1251                         In every subimage, replace ^ with &.
1252                         Create an "inventory cube".
1253                         NOTE: This should be used only on its own.
1254                         Example (a grass block (not actually used in game):
1255                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1256                 */
1257                 else if(part_of_name.substr(0,14) == "[inventorycube")
1258                 {
1259                         if(baseimg != NULL)
1260                         {
1261                                 errorstream<<"generateImage(): baseimg!=NULL "
1262                                                 <<"for part_of_name=\""<<part_of_name
1263                                                 <<"\", cancelling."<<std::endl;
1264                                 return false;
1265                         }
1266
1267                         str_replace_char(part_of_name, '&', '^');
1268                         Strfnd sf(part_of_name);
1269                         sf.next("{");
1270                         std::string imagename_top = sf.next("{");
1271                         std::string imagename_left = sf.next("{");
1272                         std::string imagename_right = sf.next("{");
1273
1274                         // Generate images for the faces of the cube
1275                         video::IImage *img_top =
1276                                 generateImageFromScratch(imagename_top);
1277                         video::IImage *img_left =
1278                                 generateImageFromScratch(imagename_left);
1279                         video::IImage *img_right =
1280                                 generateImageFromScratch(imagename_right);
1281                         assert(img_top && img_left && img_right);
1282
1283                         // Create textures from images
1284                         video::ITexture *texture_top = driver->addTexture(
1285                                         (imagename_top + "__temp__").c_str(), img_top);
1286                         video::ITexture *texture_left = driver->addTexture(
1287                                         (imagename_left + "__temp__").c_str(), img_left);
1288                         video::ITexture *texture_right = driver->addTexture(
1289                                         (imagename_right + "__temp__").c_str(), img_right);
1290                         assert(texture_top && texture_left && texture_right);
1291
1292                         // Drop images
1293                         img_top->drop();
1294                         img_left->drop();
1295                         img_right->drop();
1296
1297                         /*
1298                                 Draw a cube mesh into a render target texture
1299                         */
1300                         scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
1301                         setMeshColor(cube, video::SColor(255, 255, 255, 255));
1302                         cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
1303                         cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
1304                         cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
1305                         cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
1306                         cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
1307                         cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
1308
1309                         TextureFromMeshParams params;
1310                         params.mesh = cube;
1311                         params.dim.set(64, 64);
1312                         params.rtt_texture_name = part_of_name + "_RTT";
1313                         // We will delete the rtt texture ourselves
1314                         params.delete_texture_on_shutdown = false;
1315                         params.camera_position.set(0, 1.0, -1.5);
1316                         params.camera_position.rotateXZBy(45);
1317                         params.camera_lookat.set(0, 0, 0);
1318                         // Set orthogonal projection
1319                         params.camera_projection_matrix.buildProjectionMatrixOrthoLH(
1320                                         1.65, 1.65, 0, 100);
1321
1322                         params.ambient_light.set(1.0, 0.2, 0.2, 0.2);
1323                         params.light_position.set(10, 100, -50);
1324                         params.light_color.set(1.0, 0.5, 0.5, 0.5);
1325                         params.light_radius = 1000;
1326
1327                         video::ITexture *rtt = generateTextureFromMesh(params);
1328
1329                         // Drop mesh
1330                         cube->drop();
1331
1332                         // Free textures of images
1333                         driver->removeTexture(texture_top);
1334                         driver->removeTexture(texture_left);
1335                         driver->removeTexture(texture_right);
1336
1337                         if(rtt == NULL)
1338                         {
1339                                 baseimg = generateImageFromScratch(imagename_top);
1340                                 return true;
1341                         }
1342
1343                         // Create image of render target
1344                         video::IImage *image = driver->createImage(rtt, v2s32(0,0), params.dim);
1345                         assert(image);
1346
1347                         // Cleanup texture
1348                         driver->removeTexture(rtt);
1349
1350                         baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim);
1351
1352                         if(image)
1353                         {
1354                                 image->copyTo(baseimg);
1355                                 image->drop();
1356                         }
1357                 }
1358                 /*
1359                         [lowpart:percent:filename
1360                         Adds the lower part of a texture
1361                 */
1362                 else if(part_of_name.substr(0,9) == "[lowpart:")
1363                 {
1364                         Strfnd sf(part_of_name);
1365                         sf.next(":");
1366                         u32 percent = stoi(sf.next(":"));
1367                         std::string filename = sf.next(":");
1368                         //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
1369
1370                         if(baseimg == NULL)
1371                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
1372                         video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
1373                         if(img)
1374                         {
1375                                 core::dimension2d<u32> dim = img->getDimension();
1376                                 core::position2d<s32> pos_base(0, 0);
1377                                 video::IImage *img2 =
1378                                                 driver->createImage(video::ECF_A8R8G8B8, dim);
1379                                 img->copyTo(img2);
1380                                 img->drop();
1381                                 core::position2d<s32> clippos(0, 0);
1382                                 clippos.Y = dim.Height * (100-percent) / 100;
1383                                 core::dimension2d<u32> clipdim = dim;
1384                                 clipdim.Height = clipdim.Height * percent / 100 + 1;
1385                                 core::rect<s32> cliprect(clippos, clipdim);
1386                                 img2->copyToWithAlpha(baseimg, pos_base,
1387                                                 core::rect<s32>(v2s32(0,0), dim),
1388                                                 video::SColor(255,255,255,255),
1389                                                 &cliprect);
1390                                 img2->drop();
1391                         }
1392                 }
1393                 /*
1394                         [verticalframe:N:I
1395                         Crops a frame of a vertical animation.
1396                         N = frame count, I = frame index
1397                 */
1398                 else if(part_of_name.substr(0,15) == "[verticalframe:")
1399                 {
1400                         Strfnd sf(part_of_name);
1401                         sf.next(":");
1402                         u32 frame_count = stoi(sf.next(":"));
1403                         u32 frame_index = stoi(sf.next(":"));
1404
1405                         if(baseimg == NULL){
1406                                 errorstream<<"generateImage(): baseimg!=NULL "
1407                                                 <<"for part_of_name=\""<<part_of_name
1408                                                 <<"\", cancelling."<<std::endl;
1409                                 return false;
1410                         }
1411
1412                         v2u32 frame_size = baseimg->getDimension();
1413                         frame_size.Y /= frame_count;
1414
1415                         video::IImage *img = driver->createImage(video::ECF_A8R8G8B8,
1416                                         frame_size);
1417                         if(!img){
1418                                 errorstream<<"generateImage(): Could not create image "
1419                                                 <<"for part_of_name=\""<<part_of_name
1420                                                 <<"\", cancelling."<<std::endl;
1421                                 return false;
1422                         }
1423
1424                         // Fill target image with transparency
1425                         img->fill(video::SColor(0,0,0,0));
1426
1427                         core::dimension2d<u32> dim = frame_size;
1428                         core::position2d<s32> pos_dst(0, 0);
1429                         core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
1430                         baseimg->copyToWithAlpha(img, pos_dst,
1431                                         core::rect<s32>(pos_src, dim),
1432                                         video::SColor(255,255,255,255),
1433                                         NULL);
1434                         // Replace baseimg
1435                         baseimg->drop();
1436                         baseimg = img;
1437                 }
1438                 else
1439                 {
1440                         errorstream<<"generateImage(): Invalid "
1441                                         " modification: \""<<part_of_name<<"\""<<std::endl;
1442                 }
1443         }
1444
1445         return true;
1446 }
1447
1448 /*
1449         Draw an image on top of an another one, using the alpha channel of the
1450         source image
1451
1452         This exists because IImage::copyToWithAlpha() doesn't seem to always
1453         work.
1454 */
1455 static void blit_with_alpha(video::IImage *src, video::IImage *dst,
1456                 v2s32 src_pos, v2s32 dst_pos, v2u32 size)
1457 {
1458         for(u32 y0=0; y0<size.Y; y0++)
1459         for(u32 x0=0; x0<size.X; x0++)
1460         {
1461                 s32 src_x = src_pos.X + x0;
1462                 s32 src_y = src_pos.Y + y0;
1463                 s32 dst_x = dst_pos.X + x0;
1464                 s32 dst_y = dst_pos.Y + y0;
1465                 video::SColor src_c = src->getPixel(src_x, src_y);
1466                 video::SColor dst_c = dst->getPixel(dst_x, dst_y);
1467                 dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
1468                 dst->setPixel(dst_x, dst_y, dst_c);
1469         }
1470 }
1471
1472 /*
1473         Draw an image on top of an another one, using the alpha channel of the
1474         source image; only modify fully opaque pixels in destinaion
1475 */
1476 static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
1477                 v2s32 src_pos, v2s32 dst_pos, v2u32 size)
1478 {
1479         for(u32 y0=0; y0<size.Y; y0++)
1480         for(u32 x0=0; x0<size.X; x0++)
1481         {
1482                 s32 src_x = src_pos.X + x0;
1483                 s32 src_y = src_pos.Y + y0;
1484                 s32 dst_x = dst_pos.X + x0;
1485                 s32 dst_y = dst_pos.Y + y0;
1486                 video::SColor src_c = src->getPixel(src_x, src_y);
1487                 video::SColor dst_c = dst->getPixel(dst_x, dst_y);
1488                 if(dst_c.getAlpha() == 255 && src_c.getAlpha() != 0)
1489                 {
1490                         dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
1491                         dst->setPixel(dst_x, dst_y, dst_c);
1492                 }
1493         }
1494 }
1495
1496 static void draw_crack(video::IImage *crack, video::IImage *dst,
1497                 bool use_overlay, s32 frame_count, s32 progression,
1498                 video::IVideoDriver *driver)
1499 {
1500         // Dimension of destination image
1501         core::dimension2d<u32> dim_dst = dst->getDimension();
1502         // Dimension of original image
1503         core::dimension2d<u32> dim_crack = crack->getDimension();
1504         // Count of crack stages
1505         s32 crack_count = dim_crack.Height / dim_crack.Width;
1506         // Limit frame_count
1507         if(frame_count > (s32) dim_dst.Height)
1508                 frame_count = dim_dst.Height;
1509         if(frame_count < 1)
1510                 frame_count = 1;
1511         // Limit progression
1512         if(progression > crack_count-1)
1513                 progression = crack_count-1;
1514         // Dimension of a single crack stage
1515         core::dimension2d<u32> dim_crack_cropped(
1516                 dim_crack.Width,
1517                 dim_crack.Width
1518         );
1519         // Dimension of the scaled crack stage,
1520         // which is the same as the dimension of a single destination frame
1521         core::dimension2d<u32> dim_crack_scaled(
1522                 dim_dst.Width,
1523                 dim_dst.Height / frame_count
1524         );
1525         // Create cropped and scaled crack images
1526         video::IImage *crack_cropped = driver->createImage(
1527                         video::ECF_A8R8G8B8, dim_crack_cropped);
1528         video::IImage *crack_scaled = driver->createImage(
1529                         video::ECF_A8R8G8B8, dim_crack_scaled);
1530
1531         if(crack_cropped && crack_scaled)
1532         {
1533                 // Crop crack image
1534                 v2s32 pos_crack(0, progression*dim_crack.Width);
1535                 crack->copyTo(crack_cropped,
1536                                 v2s32(0,0),
1537                                 core::rect<s32>(pos_crack, dim_crack_cropped));
1538                 // Scale crack image by copying
1539                 crack_cropped->copyToScaling(crack_scaled);
1540                 // Copy or overlay crack image onto each frame
1541                 for(s32 i = 0; i < frame_count; ++i)
1542                 {
1543                         v2s32 dst_pos(0, dim_crack_scaled.Height * i);
1544                         if(use_overlay)
1545                         {
1546                                 blit_with_alpha_overlay(crack_scaled, dst,
1547                                                 v2s32(0,0), dst_pos,
1548                                                 dim_crack_scaled);
1549                         }
1550                         else
1551                         {
1552                                 blit_with_alpha(crack_scaled, dst,
1553                                                 v2s32(0,0), dst_pos,
1554                                                 dim_crack_scaled);
1555                         }
1556                 }
1557         }
1558
1559         if(crack_scaled)
1560                 crack_scaled->drop();
1561
1562         if(crack_cropped)
1563                 crack_cropped->drop();
1564 }
1565
1566 void brighten(video::IImage *image)
1567 {
1568         if(image == NULL)
1569                 return;
1570
1571         core::dimension2d<u32> dim = image->getDimension();
1572
1573         for(u32 y=0; y<dim.Height; y++)
1574         for(u32 x=0; x<dim.Width; x++)
1575         {
1576                 video::SColor c = image->getPixel(x,y);
1577                 c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
1578                 c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
1579                 c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
1580                 image->setPixel(x,y,c);
1581         }
1582 }
1583
1584 u32 parseImageTransform(const std::string& s)
1585 {
1586         int total_transform = 0;
1587
1588         std::string transform_names[8];
1589         transform_names[0] = "i";
1590         transform_names[1] = "r90";
1591         transform_names[2] = "r180";
1592         transform_names[3] = "r270";
1593         transform_names[4] = "fx";
1594         transform_names[6] = "fy";
1595
1596         std::size_t pos = 0;
1597         while(pos < s.size())
1598         {
1599                 int transform = -1;
1600                 for(int i = 0; i <= 7; ++i)
1601                 {
1602                         const std::string &name_i = transform_names[i];
1603
1604                         if(s[pos] == ('0' + i))
1605                         {
1606                                 transform = i;
1607                                 pos++;
1608                                 break;
1609                         }
1610                         else if(!(name_i.empty()) &&
1611                                 lowercase(s.substr(pos, name_i.size())) == name_i)
1612                         {
1613                                 transform = i;
1614                                 pos += name_i.size();
1615                                 break;
1616                         }
1617                 }
1618                 if(transform < 0)
1619                         break;
1620
1621                 // Multiply total_transform and transform in the group D4
1622                 int new_total = 0;
1623                 if(transform < 4)
1624                         new_total = (transform + total_transform) % 4;
1625                 else
1626                         new_total = (transform - total_transform + 8) % 4;
1627                 if((transform >= 4) ^ (total_transform >= 4))
1628                         new_total += 4;
1629
1630                 total_transform = new_total;
1631         }
1632         return total_transform;
1633 }
1634
1635 core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
1636 {
1637         if(transform % 2 == 0)
1638                 return dim;
1639         else
1640                 return core::dimension2d<u32>(dim.Height, dim.Width);
1641 }
1642
1643 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
1644 {
1645         if(src == NULL || dst == NULL)
1646                 return;
1647
1648         core::dimension2d<u32> srcdim = src->getDimension();
1649         core::dimension2d<u32> dstdim = dst->getDimension();
1650
1651         assert(dstdim == imageTransformDimension(transform, srcdim));
1652         assert(transform >= 0 && transform <= 7);
1653
1654         /*
1655                 Compute the transformation from source coordinates (sx,sy)
1656                 to destination coordinates (dx,dy).
1657         */
1658         int sxn = 0;
1659         int syn = 2;
1660         if(transform == 0)         // identity
1661                 sxn = 0, syn = 2;  //   sx = dx, sy = dy
1662         else if(transform == 1)    // rotate by 90 degrees ccw
1663                 sxn = 3, syn = 0;  //   sx = (H-1) - dy, sy = dx
1664         else if(transform == 2)    // rotate by 180 degrees
1665                 sxn = 1, syn = 3;  //   sx = (W-1) - dx, sy = (H-1) - dy
1666         else if(transform == 3)    // rotate by 270 degrees ccw
1667                 sxn = 2, syn = 1;  //   sx = dy, sy = (W-1) - dx
1668         else if(transform == 4)    // flip x
1669                 sxn = 1, syn = 2;  //   sx = (W-1) - dx, sy = dy
1670         else if(transform == 5)    // flip x then rotate by 90 degrees ccw
1671                 sxn = 2, syn = 0;  //   sx = dy, sy = dx
1672         else if(transform == 6)    // flip y
1673                 sxn = 0, syn = 3;  //   sx = dx, sy = (H-1) - dy
1674         else if(transform == 7)    // flip y then rotate by 90 degrees ccw
1675                 sxn = 3, syn = 1;  //   sx = (H-1) - dy, sy = (W-1) - dx
1676
1677         for(u32 dy=0; dy<dstdim.Height; dy++)
1678         for(u32 dx=0; dx<dstdim.Width; dx++)
1679         {
1680                 u32 entries[4] = {dx, dstdim.Width-1-dx, dy, dstdim.Height-1-dy};
1681                 u32 sx = entries[sxn];
1682                 u32 sy = entries[syn];
1683                 video::SColor c = src->getPixel(sx,sy);
1684                 dst->setPixel(dx,dy,c);
1685         }
1686 }