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