]> git.lizzy.rs Git - dragonfireclient.git/blob - src/tile.cpp
Add texture pack selection to main menu
[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^[crack0".
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 "[crack0".
387         // if baseimg is NULL, it is created. Otherwise stuff is made on it.
388         // Shall be called from the main thread.
389         bool generateImage(std::string part_of_name, video::IImage *& baseimg);
390
391 private:
392         
393         // The id of the thread that is allowed to use irrlicht directly
394         threadid_t m_main_thread;
395         // The irrlicht device
396         IrrlichtDevice *m_device;
397         
398         // Cache of source images
399         // This should be only accessed from the main thread
400         SourceImageCache m_sourcecache;
401
402         // Thread-safe cache of what source images are known (true = known)
403         MutexedMap<std::string, bool> m_source_image_existence;
404
405         // A texture id is index in this array.
406         // The first position contains a NULL texture.
407         std::vector<TextureInfo> m_textureinfo_cache;
408         // Maps a texture name to an index in the former.
409         std::map<std::string, u32> m_name_to_id;
410         // The two former containers are behind this mutex
411         JMutex m_textureinfo_cache_mutex;
412         
413         // Queued texture fetches (to be processed by the main thread)
414         RequestQueue<std::string, u32, u8, u8> m_get_texture_queue;
415
416         // Textures that have been overwritten with other ones
417         // but can't be deleted because the ITexture* might still be used
418         std::list<video::ITexture*> m_texture_trash;
419
420         // Cached settings needed for making textures from meshes
421         bool m_setting_trilinear_filter;
422         bool m_setting_bilinear_filter;
423         bool m_setting_anisotropic_filter;
424 };
425
426 IWritableTextureSource* createTextureSource(IrrlichtDevice *device)
427 {
428         return new TextureSource(device);
429 }
430
431 TextureSource::TextureSource(IrrlichtDevice *device):
432                 m_device(device)
433 {
434         assert(m_device);
435         
436         m_textureinfo_cache_mutex.Init();
437         
438         m_main_thread = get_current_thread_id();
439         
440         // Add a NULL TextureInfo as the first index, named ""
441         m_textureinfo_cache.push_back(TextureInfo(""));
442         m_name_to_id[""] = 0;
443         
444         // Cache some settings
445         // Note: Since this is only done once, the game must be restarted
446         // for these settings to take effect
447         m_setting_trilinear_filter = g_settings->getBool("trilinear_filter");
448         m_setting_bilinear_filter = g_settings->getBool("bilinear_filter");
449         m_setting_anisotropic_filter = g_settings->getBool("anisotropic_filter");
450 }
451
452 TextureSource::~TextureSource()
453 {
454         video::IVideoDriver* driver = m_device->getVideoDriver();
455
456         unsigned int textures_before = driver->getTextureCount();
457
458         for (std::vector<TextureInfo>::iterator iter =
459                         m_textureinfo_cache.begin();
460                         iter != m_textureinfo_cache.end(); iter++)
461         {
462                 //cleanup texture
463                 if (iter->texture)
464                         driver->removeTexture(iter->texture);
465
466                 //cleanup source image
467                 if (iter->img)
468                         iter->img->drop();
469         }
470         m_textureinfo_cache.clear();
471
472         for (std::list<video::ITexture*>::iterator iter =
473                         m_texture_trash.begin(); iter != m_texture_trash.end();
474                         iter++)
475         {
476                 video::ITexture *t = *iter;
477
478                 //cleanup trashed texture
479                 driver->removeTexture(t);
480         }
481
482         infostream << "~TextureSource() "<< textures_before << "/"
483                         << driver->getTextureCount() << std::endl;
484 }
485
486 u32 TextureSource::getTextureId(const std::string &name)
487 {
488         //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
489
490         {
491                 /*
492                         See if texture already exists
493                 */
494                 JMutexAutoLock lock(m_textureinfo_cache_mutex);
495                 std::map<std::string, u32>::iterator n;
496                 n = m_name_to_id.find(name);
497                 if(n != m_name_to_id.end())
498                 {
499                         return n->second;
500                 }
501         }
502         
503         /*
504                 Get texture
505         */
506         if(get_current_thread_id() == m_main_thread)
507         {
508                 return getTextureIdDirect(name);
509         }
510         else
511         {
512                 infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
513
514                 // We're gonna ask the result to be put into here
515                 ResultQueue<std::string, u32, u8, u8> result_queue;
516                 
517                 // Throw a request in
518                 m_get_texture_queue.add(name, 0, 0, &result_queue);
519                 
520                 infostream<<"Waiting for texture from main thread, name=\""
521                                 <<name<<"\""<<std::endl;
522                 
523                 try
524                 {
525                         // Wait result for a second
526                         GetResult<std::string, u32, u8, u8>
527                                         result = result_queue.pop_front(1000);
528                 
529                         // Check that at least something worked OK
530                         assert(result.key == name);
531
532                         return result.item;
533                 }
534                 catch(ItemNotFoundException &e)
535                 {
536                         infostream<<"Waiting for texture timed out."<<std::endl;
537                         return 0;
538                 }
539         }
540         
541         infostream<<"getTextureId(): Failed"<<std::endl;
542
543         return 0;
544 }
545
546 // Overlay image on top of another image (used for cracks)
547 void overlay(video::IImage *image, video::IImage *overlay);
548
549 // Draw an image on top of an another one, using the alpha channel of the
550 // source image
551 static void blit_with_alpha(video::IImage *src, video::IImage *dst,
552                 v2s32 src_pos, v2s32 dst_pos, v2u32 size);
553
554 // Brighten image
555 void brighten(video::IImage *image);
556 // Parse a transform name
557 u32 parseImageTransform(const std::string& s);
558 // Apply transform to image dimension
559 core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim);
560 // Apply transform to image data
561 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
562
563 /*
564         This method generates all the textures
565 */
566 u32 TextureSource::getTextureIdDirect(const std::string &name)
567 {
568         //infostream<<"getTextureIdDirect(): name=\""<<name<<"\""<<std::endl;
569
570         // Empty name means texture 0
571         if(name == "")
572         {
573                 infostream<<"getTextureIdDirect(): name is empty"<<std::endl;
574                 return 0;
575         }
576         
577         /*
578                 Calling only allowed from main thread
579         */
580         if(get_current_thread_id() != m_main_thread)
581         {
582                 errorstream<<"TextureSource::getTextureIdDirect() "
583                                 "called not from main thread"<<std::endl;
584                 return 0;
585         }
586
587         /*
588                 See if texture already exists
589         */
590         {
591                 JMutexAutoLock lock(m_textureinfo_cache_mutex);
592
593                 std::map<std::string, u32>::iterator n;
594                 n = m_name_to_id.find(name);
595                 if(n != m_name_to_id.end())
596                 {
597                         /*infostream<<"getTextureIdDirect(): \""<<name
598                                         <<"\" found in cache"<<std::endl;*/
599                         return n->second;
600                 }
601         }
602
603         /*infostream<<"getTextureIdDirect(): \""<<name
604                         <<"\" NOT found in cache. Creating it."<<std::endl;*/
605         
606         /*
607                 Get the base image
608         */
609
610         char separator = '^';
611
612         /*
613                 This is set to the id of the base image.
614                 If left 0, there is no base image and a completely new image
615                 is made.
616         */
617         u32 base_image_id = 0;
618         
619         // Find last meta separator in name
620         s32 last_separator_position = -1;
621         for(s32 i=name.size()-1; i>=0; i--)
622         {
623                 if(name[i] == separator)
624                 {
625                         last_separator_position = i;
626                         break;
627                 }
628         }
629         /*
630                 If separator was found, construct the base name and make the
631                 base image using a recursive call
632         */
633         std::string base_image_name;
634         if(last_separator_position != -1)
635         {
636                 // Construct base name
637                 base_image_name = name.substr(0, last_separator_position);
638                 /*infostream<<"getTextureIdDirect(): Calling itself recursively"
639                                 " to get base image of \""<<name<<"\" = \""
640                 <<base_image_name<<"\""<<std::endl;*/
641                 base_image_id = getTextureIdDirect(base_image_name);
642         }
643         
644         //infostream<<"base_image_id="<<base_image_id<<std::endl;
645         
646         video::IVideoDriver* driver = m_device->getVideoDriver();
647         assert(driver);
648
649         video::ITexture *t = NULL;
650
651         /*
652                 An image will be built from files and then converted into a texture.
653         */
654         video::IImage *baseimg = NULL;
655         
656         // If a base image was found, copy it to baseimg
657         if(base_image_id != 0)
658         {
659                 JMutexAutoLock lock(m_textureinfo_cache_mutex);
660
661                 TextureInfo *ti = &m_textureinfo_cache[base_image_id];
662                 
663                 if(ti->img == NULL)
664                 {
665                         infostream<<"getTextureIdDirect(): WARNING: NULL image in "
666                                         <<"cache: \""<<base_image_name<<"\""
667                                         <<std::endl;
668                 }
669                 else
670                 {
671                         core::dimension2d<u32> dim = ti->img->getDimension();
672
673                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
674
675                         ti->img->copyTo(
676                                         baseimg, // target
677                                         v2s32(0,0), // position in target
678                                         core::rect<s32>(v2s32(0,0), dim) // from
679                         );
680
681                         /*infostream<<"getTextureIdDirect(): Loaded \""
682                                         <<base_image_name<<"\" from image cache"
683                                         <<std::endl;*/
684                 }
685         }
686         
687         /*
688                 Parse out the last part of the name of the image and act
689                 according to it
690         */
691
692         std::string last_part_of_name = name.substr(last_separator_position+1);
693         //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
694
695         // Generate image according to part of name
696         if(!generateImage(last_part_of_name, baseimg))
697         {
698                 errorstream<<"getTextureIdDirect(): "
699                                 "failed to generate \""<<last_part_of_name<<"\""
700                                 <<std::endl;
701         }
702
703         // If no resulting image, print a warning
704         if(baseimg == NULL)
705         {
706                 errorstream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
707                                 " create texture \""<<name<<"\""<<std::endl;
708         }
709         
710         if(baseimg != NULL)
711         {
712                 // Create texture from resulting image
713                 t = driver->addTexture(name.c_str(), baseimg);
714         }
715         
716         /*
717                 Add texture to caches (add NULL textures too)
718         */
719
720         JMutexAutoLock lock(m_textureinfo_cache_mutex);
721         
722         u32 id = m_textureinfo_cache.size();
723         TextureInfo ti(name, t, baseimg);
724         m_textureinfo_cache.push_back(ti);
725         m_name_to_id[name] = id;
726
727         /*infostream<<"getTextureIdDirect(): "
728                         <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
729         
730         return id;
731 }
732
733 std::string TextureSource::getTextureName(u32 id)
734 {
735         JMutexAutoLock lock(m_textureinfo_cache_mutex);
736
737         if(id >= m_textureinfo_cache.size())
738         {
739                 errorstream<<"TextureSource::getTextureName(): id="<<id
740                                 <<" >= m_textureinfo_cache.size()="
741                                 <<m_textureinfo_cache.size()<<std::endl;
742                 return "";
743         }
744         
745         return m_textureinfo_cache[id].name;
746 }
747
748 video::ITexture* TextureSource::getTexture(u32 id)
749 {
750         JMutexAutoLock lock(m_textureinfo_cache_mutex);
751
752         if(id >= m_textureinfo_cache.size())
753                 return NULL;
754
755         return m_textureinfo_cache[id].texture;
756 }
757
758 video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id)
759 {
760         u32 actual_id = getTextureId(name);
761         if(id){
762                 *id = actual_id;
763         }
764         return getTexture(actual_id);
765 }
766
767 void TextureSource::processQueue()
768 {
769         /*
770                 Fetch textures
771         */
772         if(!m_get_texture_queue.empty())
773         {
774                 GetRequest<std::string, u32, u8, u8>
775                                 request = m_get_texture_queue.pop();
776
777                 /*infostream<<"TextureSource::processQueue(): "
778                                 <<"got texture request with "
779                                 <<"name=\""<<request.key<<"\""
780                                 <<std::endl;*/
781
782                 GetResult<std::string, u32, u8, u8>
783                                 result;
784                 result.key = request.key;
785                 result.callers = request.callers;
786                 result.item = getTextureIdDirect(request.key);
787
788                 request.dest->push_back(result);
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                         [crackN
1036                         Adds a cracking texture
1037                 */
1038                 if(part_of_name.substr(0,6) == "[crack")
1039                 {
1040                         if(baseimg == NULL)
1041                         {
1042                                 errorstream<<"generateImage(): baseimg==NULL "
1043                                                 <<"for part_of_name=\""<<part_of_name
1044                                                 <<"\", cancelling."<<std::endl;
1045                                 return false;
1046                         }
1047                         
1048                         // Crack image number and overlay option
1049                         s32 progression = 0;
1050                         bool use_overlay = false;
1051                         if(part_of_name.substr(6,1) == "o")
1052                         {
1053                                 progression = stoi(part_of_name.substr(7));
1054                                 use_overlay = true;
1055                         }
1056                         else
1057                         {
1058                                 progression = stoi(part_of_name.substr(6));
1059                                 use_overlay = false;
1060                         }
1061
1062                         // Size of the base image
1063                         core::dimension2d<u32> dim_base = baseimg->getDimension();
1064                         
1065                         /*
1066                                 Load crack image.
1067
1068                                 It is an image with a number of cracking stages
1069                                 horizontally tiled.
1070                         */
1071                         video::IImage *img_crack = m_sourcecache.getOrLoad(
1072                                         "crack_anylength.png", m_device);
1073                 
1074                         if(img_crack && progression >= 0)
1075                         {
1076                                 // Dimension of original image
1077                                 core::dimension2d<u32> dim_crack
1078                                                 = img_crack->getDimension();
1079                                 // Count of crack stages
1080                                 s32 crack_count = dim_crack.Height / dim_crack.Width;
1081                                 // Limit progression
1082                                 if(progression > crack_count-1)
1083                                         progression = crack_count-1;
1084                                 // Dimension of a single crack stage
1085                                 core::dimension2d<u32> dim_crack_cropped(
1086                                         dim_crack.Width,
1087                                         dim_crack.Width
1088                                 );
1089                                 // Create cropped and scaled crack images
1090                                 video::IImage *img_crack_cropped = driver->createImage(
1091                                                 video::ECF_A8R8G8B8, dim_crack_cropped);
1092                                 video::IImage *img_crack_scaled = driver->createImage(
1093                                                 video::ECF_A8R8G8B8, dim_base);
1094
1095                                 if(img_crack_cropped && img_crack_scaled)
1096                                 {
1097                                         // Crop crack image
1098                                         v2s32 pos_crack(0, progression*dim_crack.Width);
1099                                         img_crack->copyTo(img_crack_cropped,
1100                                                         v2s32(0,0),
1101                                                         core::rect<s32>(pos_crack, dim_crack_cropped));
1102                                         // Scale crack image by copying
1103                                         img_crack_cropped->copyToScaling(img_crack_scaled);
1104                                         // Copy or overlay crack image
1105                                         if(use_overlay)
1106                                         {
1107                                                 overlay(baseimg, img_crack_scaled);
1108                                         }
1109                                         else
1110                                         {
1111                                                 /*img_crack_scaled->copyToWithAlpha(
1112                                                                 baseimg,
1113                                                                 v2s32(0,0),
1114                                                                 core::rect<s32>(v2s32(0,0), dim_base),
1115                                                                 video::SColor(255,255,255,255));*/
1116                                                 blit_with_alpha(img_crack_scaled, baseimg,
1117                                                                 v2s32(0,0), v2s32(0,0), dim_base);
1118                                         }
1119                                 }
1120
1121                                 if(img_crack_scaled)
1122                                         img_crack_scaled->drop();
1123
1124                                 if(img_crack_cropped)
1125                                         img_crack_cropped->drop();
1126                                 
1127                                 img_crack->drop();
1128                         }
1129                 }
1130                 /*
1131                         [combine:WxH:X,Y=filename:X,Y=filename2
1132                         Creates a bigger texture from an amount of smaller ones
1133                 */
1134                 else if(part_of_name.substr(0,8) == "[combine")
1135                 {
1136                         Strfnd sf(part_of_name);
1137                         sf.next(":");
1138                         u32 w0 = stoi(sf.next("x"));
1139                         u32 h0 = stoi(sf.next(":"));
1140                         infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
1141                         core::dimension2d<u32> dim(w0,h0);
1142                         if(baseimg == NULL)
1143                         {
1144                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1145                                 baseimg->fill(video::SColor(0,0,0,0));
1146                         }
1147                         while(sf.atend() == false)
1148                         {
1149                                 u32 x = stoi(sf.next(","));
1150                                 u32 y = stoi(sf.next("="));
1151                                 std::string filename = sf.next(":");
1152                                 infostream<<"Adding \""<<filename
1153                                                 <<"\" to combined ("<<x<<","<<y<<")"
1154                                                 <<std::endl;
1155                                 video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
1156                                 if(img)
1157                                 {
1158                                         core::dimension2d<u32> dim = img->getDimension();
1159                                         infostream<<"Size "<<dim.Width
1160                                                         <<"x"<<dim.Height<<std::endl;
1161                                         core::position2d<s32> pos_base(x, y);
1162                                         video::IImage *img2 =
1163                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
1164                                         img->copyTo(img2);
1165                                         img->drop();
1166                                         /*img2->copyToWithAlpha(baseimg, pos_base,
1167                                                         core::rect<s32>(v2s32(0,0), dim),
1168                                                         video::SColor(255,255,255,255),
1169                                                         NULL);*/
1170                                         blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
1171                                         img2->drop();
1172                                 }
1173                                 else
1174                                 {
1175                                         infostream<<"img==NULL"<<std::endl;
1176                                 }
1177                         }
1178                 }
1179                 /*
1180                         "[brighten"
1181                 */
1182                 else if(part_of_name.substr(0,9) == "[brighten")
1183                 {
1184                         if(baseimg == NULL)
1185                         {
1186                                 errorstream<<"generateImage(): baseimg==NULL "
1187                                                 <<"for part_of_name=\""<<part_of_name
1188                                                 <<"\", cancelling."<<std::endl;
1189                                 return false;
1190                         }
1191
1192                         brighten(baseimg);
1193                 }
1194                 /*
1195                         "[noalpha"
1196                         Make image completely opaque.
1197                         Used for the leaves texture when in old leaves mode, so
1198                         that the transparent parts don't look completely black 
1199                         when simple alpha channel is used for rendering.
1200                 */
1201                 else if(part_of_name.substr(0,8) == "[noalpha")
1202                 {
1203                         if(baseimg == NULL)
1204                         {
1205                                 errorstream<<"generateImage(): baseimg==NULL "
1206                                                 <<"for part_of_name=\""<<part_of_name
1207                                                 <<"\", cancelling."<<std::endl;
1208                                 return false;
1209                         }
1210
1211                         core::dimension2d<u32> dim = baseimg->getDimension();
1212                         
1213                         // Set alpha to full
1214                         for(u32 y=0; y<dim.Height; y++)
1215                         for(u32 x=0; x<dim.Width; x++)
1216                         {
1217                                 video::SColor c = baseimg->getPixel(x,y);
1218                                 c.setAlpha(255);
1219                                 baseimg->setPixel(x,y,c);
1220                         }
1221                 }
1222                 /*
1223                         "[makealpha:R,G,B"
1224                         Convert one color to transparent.
1225                 */
1226                 else if(part_of_name.substr(0,11) == "[makealpha:")
1227                 {
1228                         if(baseimg == NULL)
1229                         {
1230                                 errorstream<<"generateImage(): baseimg==NULL "
1231                                                 <<"for part_of_name=\""<<part_of_name
1232                                                 <<"\", cancelling."<<std::endl;
1233                                 return false;
1234                         }
1235
1236                         Strfnd sf(part_of_name.substr(11));
1237                         u32 r1 = stoi(sf.next(","));
1238                         u32 g1 = stoi(sf.next(","));
1239                         u32 b1 = stoi(sf.next(""));
1240                         std::string filename = sf.next("");
1241
1242                         core::dimension2d<u32> dim = baseimg->getDimension();
1243                         
1244                         /*video::IImage *oldbaseimg = baseimg;
1245                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1246                         oldbaseimg->copyTo(baseimg);
1247                         oldbaseimg->drop();*/
1248
1249                         // Set alpha to full
1250                         for(u32 y=0; y<dim.Height; y++)
1251                         for(u32 x=0; x<dim.Width; x++)
1252                         {
1253                                 video::SColor c = baseimg->getPixel(x,y);
1254                                 u32 r = c.getRed();
1255                                 u32 g = c.getGreen();
1256                                 u32 b = c.getBlue();
1257                                 if(!(r == r1 && g == g1 && b == b1))
1258                                         continue;
1259                                 c.setAlpha(0);
1260                                 baseimg->setPixel(x,y,c);
1261                         }
1262                 }
1263                 /*
1264                         "[transformN"
1265                         Rotates and/or flips the image.
1266
1267                         N can be a number (between 0 and 7) or a transform name.
1268                         Rotations are counter-clockwise.
1269                         0  I      identity
1270                         1  R90    rotate by 90 degrees
1271                         2  R180   rotate by 180 degrees
1272                         3  R270   rotate by 270 degrees
1273                         4  FX     flip X
1274                         5  FXR90  flip X then rotate by 90 degrees
1275                         6  FY     flip Y
1276                         7  FYR90  flip Y then rotate by 90 degrees
1277
1278                         Note: Transform names can be concatenated to produce
1279                         their product (applies the first then the second).
1280                         The resulting transform will be equivalent to one of the
1281                         eight existing ones, though (see: dihedral group).
1282                 */
1283                 else if(part_of_name.substr(0,10) == "[transform")
1284                 {
1285                         if(baseimg == NULL)
1286                         {
1287                                 errorstream<<"generateImage(): baseimg==NULL "
1288                                                 <<"for part_of_name=\""<<part_of_name
1289                                                 <<"\", cancelling."<<std::endl;
1290                                 return false;
1291                         }
1292
1293                         u32 transform = parseImageTransform(part_of_name.substr(10));
1294                         core::dimension2d<u32> dim = imageTransformDimension(
1295                                         transform, baseimg->getDimension());
1296                         video::IImage *image = driver->createImage(
1297                                         baseimg->getColorFormat(), dim);
1298                         assert(image);
1299                         imageTransform(transform, baseimg, image);
1300                         baseimg->drop();
1301                         baseimg = image;
1302                 }
1303                 /*
1304                         [inventorycube{topimage{leftimage{rightimage
1305                         In every subimage, replace ^ with &.
1306                         Create an "inventory cube".
1307                         NOTE: This should be used only on its own.
1308                         Example (a grass block (not actually used in game):
1309                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1310                 */
1311                 else if(part_of_name.substr(0,14) == "[inventorycube")
1312                 {
1313                         if(baseimg != NULL)
1314                         {
1315                                 errorstream<<"generateImage(): baseimg!=NULL "
1316                                                 <<"for part_of_name=\""<<part_of_name
1317                                                 <<"\", cancelling."<<std::endl;
1318                                 return false;
1319                         }
1320
1321                         str_replace_char(part_of_name, '&', '^');
1322                         Strfnd sf(part_of_name);
1323                         sf.next("{");
1324                         std::string imagename_top = sf.next("{");
1325                         std::string imagename_left = sf.next("{");
1326                         std::string imagename_right = sf.next("{");
1327
1328                         // Generate images for the faces of the cube
1329                         video::IImage *img_top =
1330                                 generateImageFromScratch(imagename_top);
1331                         video::IImage *img_left =
1332                                 generateImageFromScratch(imagename_left);
1333                         video::IImage *img_right =
1334                                 generateImageFromScratch(imagename_right);
1335                         assert(img_top && img_left && img_right);
1336
1337                         // Create textures from images
1338                         video::ITexture *texture_top = driver->addTexture(
1339                                         (imagename_top + "__temp__").c_str(), img_top);
1340                         video::ITexture *texture_left = driver->addTexture(
1341                                         (imagename_left + "__temp__").c_str(), img_left);
1342                         video::ITexture *texture_right = driver->addTexture(
1343                                         (imagename_right + "__temp__").c_str(), img_right);
1344                         assert(texture_top && texture_left && texture_right);
1345
1346                         // Drop images
1347                         img_top->drop();
1348                         img_left->drop();
1349                         img_right->drop();
1350                         
1351                         /*
1352                                 Draw a cube mesh into a render target texture
1353                         */
1354                         scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
1355                         setMeshColor(cube, video::SColor(255, 255, 255, 255));
1356                         cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
1357                         cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
1358                         cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
1359                         cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
1360                         cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
1361                         cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
1362
1363                         TextureFromMeshParams params;
1364                         params.mesh = cube;
1365                         params.dim.set(64, 64);
1366                         params.rtt_texture_name = part_of_name + "_RTT";
1367                         // We will delete the rtt texture ourselves
1368                         params.delete_texture_on_shutdown = false;
1369                         params.camera_position.set(0, 1.0, -1.5);
1370                         params.camera_position.rotateXZBy(45);
1371                         params.camera_lookat.set(0, 0, 0);
1372                         // Set orthogonal projection
1373                         params.camera_projection_matrix.buildProjectionMatrixOrthoLH(
1374                                         1.65, 1.65, 0, 100);
1375
1376                         params.ambient_light.set(1.0, 0.2, 0.2, 0.2);
1377                         params.light_position.set(10, 100, -50);
1378                         params.light_color.set(1.0, 0.5, 0.5, 0.5);
1379                         params.light_radius = 1000;
1380                         
1381                         video::ITexture *rtt = generateTextureFromMesh(params);
1382                         
1383                         // Drop mesh
1384                         cube->drop();
1385
1386                         // Free textures of images
1387                         driver->removeTexture(texture_top);
1388                         driver->removeTexture(texture_left);
1389                         driver->removeTexture(texture_right);
1390                         
1391                         if(rtt == NULL)
1392                         {
1393                                 baseimg = generateImageFromScratch(imagename_top);
1394                                 return true;
1395                         }
1396
1397                         // Create image of render target
1398                         video::IImage *image = driver->createImage(rtt, v2s32(0,0), params.dim);
1399                         assert(image);
1400
1401                         // Cleanup texture
1402                         driver->removeTexture(rtt);
1403
1404                         baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim);
1405
1406                         if(image)
1407                         {
1408                                 image->copyTo(baseimg);
1409                                 image->drop();
1410                         }
1411                 }
1412                 /*
1413                         [lowpart:percent:filename
1414                         Adds the lower part of a texture
1415                 */
1416                 else if(part_of_name.substr(0,9) == "[lowpart:")
1417                 {
1418                         Strfnd sf(part_of_name);
1419                         sf.next(":");
1420                         u32 percent = stoi(sf.next(":"));
1421                         std::string filename = sf.next(":");
1422                         //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
1423
1424                         if(baseimg == NULL)
1425                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
1426                         video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
1427                         if(img)
1428                         {
1429                                 core::dimension2d<u32> dim = img->getDimension();
1430                                 core::position2d<s32> pos_base(0, 0);
1431                                 video::IImage *img2 =
1432                                                 driver->createImage(video::ECF_A8R8G8B8, dim);
1433                                 img->copyTo(img2);
1434                                 img->drop();
1435                                 core::position2d<s32> clippos(0, 0);
1436                                 clippos.Y = dim.Height * (100-percent) / 100;
1437                                 core::dimension2d<u32> clipdim = dim;
1438                                 clipdim.Height = clipdim.Height * percent / 100 + 1;
1439                                 core::rect<s32> cliprect(clippos, clipdim);
1440                                 img2->copyToWithAlpha(baseimg, pos_base,
1441                                                 core::rect<s32>(v2s32(0,0), dim),
1442                                                 video::SColor(255,255,255,255),
1443                                                 &cliprect);
1444                                 img2->drop();
1445                         }
1446                 }
1447                 /*
1448                         [verticalframe:N:I
1449                         Crops a frame of a vertical animation.
1450                         N = frame count, I = frame index
1451                 */
1452                 else if(part_of_name.substr(0,15) == "[verticalframe:")
1453                 {
1454                         Strfnd sf(part_of_name);
1455                         sf.next(":");
1456                         u32 frame_count = stoi(sf.next(":"));
1457                         u32 frame_index = stoi(sf.next(":"));
1458
1459                         if(baseimg == NULL){
1460                                 errorstream<<"generateImage(): baseimg!=NULL "
1461                                                 <<"for part_of_name=\""<<part_of_name
1462                                                 <<"\", cancelling."<<std::endl;
1463                                 return false;
1464                         }
1465                         
1466                         v2u32 frame_size = baseimg->getDimension();
1467                         frame_size.Y /= frame_count;
1468
1469                         video::IImage *img = driver->createImage(video::ECF_A8R8G8B8,
1470                                         frame_size);
1471                         if(!img){
1472                                 errorstream<<"generateImage(): Could not create image "
1473                                                 <<"for part_of_name=\""<<part_of_name
1474                                                 <<"\", cancelling."<<std::endl;
1475                                 return false;
1476                         }
1477
1478                         // Fill target image with transparency
1479                         img->fill(video::SColor(0,0,0,0));
1480
1481                         core::dimension2d<u32> dim = frame_size;
1482                         core::position2d<s32> pos_dst(0, 0);
1483                         core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
1484                         baseimg->copyToWithAlpha(img, pos_dst,
1485                                         core::rect<s32>(pos_src, dim),
1486                                         video::SColor(255,255,255,255),
1487                                         NULL);
1488                         // Replace baseimg
1489                         baseimg->drop();
1490                         baseimg = img;
1491                 }
1492                 else
1493                 {
1494                         errorstream<<"generateImage(): Invalid "
1495                                         " modification: \""<<part_of_name<<"\""<<std::endl;
1496                 }
1497         }
1498
1499         return true;
1500 }
1501
1502 void overlay(video::IImage *image, video::IImage *overlay)
1503 {
1504         /*
1505                 Copy overlay to image, taking alpha into account.
1506                 Where image is transparent, don't copy from overlay.
1507                 Images sizes must be identical.
1508         */
1509         if(image == NULL || overlay == NULL)
1510                 return;
1511         
1512         core::dimension2d<u32> dim = image->getDimension();
1513         core::dimension2d<u32> dim_overlay = overlay->getDimension();
1514         assert(dim == dim_overlay);
1515
1516         for(u32 y=0; y<dim.Height; y++)
1517         for(u32 x=0; x<dim.Width; x++)
1518         {
1519                 video::SColor c1 = image->getPixel(x,y);
1520                 video::SColor c2 = overlay->getPixel(x,y);
1521                 u32 a1 = c1.getAlpha();
1522                 u32 a2 = c2.getAlpha();
1523                 if(a1 == 255 && a2 != 0)
1524                 {
1525                         c1.setRed((c1.getRed()*(255-a2) + c2.getRed()*a2)/255);
1526                         c1.setGreen((c1.getGreen()*(255-a2) + c2.getGreen()*a2)/255);
1527                         c1.setBlue((c1.getBlue()*(255-a2) + c2.getBlue()*a2)/255);
1528                 }
1529                 image->setPixel(x,y,c1);
1530         }
1531 }
1532
1533 /*
1534         Draw an image on top of an another one, using the alpha channel of the
1535         source image
1536
1537         This exists because IImage::copyToWithAlpha() doesn't seem to always
1538         work.
1539 */
1540 static void blit_with_alpha(video::IImage *src, video::IImage *dst,
1541                 v2s32 src_pos, v2s32 dst_pos, v2u32 size)
1542 {
1543         for(u32 y0=0; y0<size.Y; y0++)
1544         for(u32 x0=0; x0<size.X; x0++)
1545         {
1546                 s32 src_x = src_pos.X + x0;
1547                 s32 src_y = src_pos.Y + y0;
1548                 s32 dst_x = dst_pos.X + x0;
1549                 s32 dst_y = dst_pos.Y + y0;
1550                 video::SColor src_c = src->getPixel(src_x, src_y);
1551                 video::SColor dst_c = dst->getPixel(dst_x, dst_y);
1552                 dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
1553                 dst->setPixel(dst_x, dst_y, dst_c);
1554         }
1555 }
1556
1557 void brighten(video::IImage *image)
1558 {
1559         if(image == NULL)
1560                 return;
1561         
1562         core::dimension2d<u32> dim = image->getDimension();
1563
1564         for(u32 y=0; y<dim.Height; y++)
1565         for(u32 x=0; x<dim.Width; x++)
1566         {
1567                 video::SColor c = image->getPixel(x,y);
1568                 c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
1569                 c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
1570                 c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
1571                 image->setPixel(x,y,c);
1572         }
1573 }
1574
1575 u32 parseImageTransform(const std::string& s)
1576 {
1577         int total_transform = 0;
1578
1579         std::string transform_names[8];
1580         transform_names[0] = "i";
1581         transform_names[1] = "r90";
1582         transform_names[2] = "r180";
1583         transform_names[3] = "r270";
1584         transform_names[4] = "fx";
1585         transform_names[6] = "fy";
1586
1587         std::size_t pos = 0;
1588         while(pos < s.size())
1589         {
1590                 int transform = -1;
1591                 for(int i = 0; i <= 7; ++i)
1592                 {
1593                         const std::string &name_i = transform_names[i];
1594
1595                         if(s[pos] == ('0' + i))
1596                         {
1597                                 transform = i;
1598                                 pos++;
1599                                 break;
1600                         }
1601                         else if(!(name_i.empty()) &&
1602                                 lowercase(s.substr(pos, name_i.size())) == name_i)
1603                         {
1604                                 transform = i;
1605                                 pos += name_i.size();
1606                                 break;
1607                         }
1608                 }
1609                 if(transform < 0)
1610                         break;
1611
1612                 // Multiply total_transform and transform in the group D4
1613                 int new_total = 0;
1614                 if(transform < 4)
1615                         new_total = (transform + total_transform) % 4;
1616                 else
1617                         new_total = (transform - total_transform + 8) % 4;
1618                 if((transform >= 4) ^ (total_transform >= 4))
1619                         new_total += 4;
1620
1621                 total_transform = new_total;
1622         }
1623         return total_transform;
1624 }
1625
1626 core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
1627 {
1628         if(transform % 2 == 0)
1629                 return dim;
1630         else
1631                 return core::dimension2d<u32>(dim.Height, dim.Width);
1632 }
1633
1634 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
1635 {
1636         if(src == NULL || dst == NULL)
1637                 return;
1638         
1639         core::dimension2d<u32> srcdim = src->getDimension();
1640         core::dimension2d<u32> dstdim = dst->getDimension();
1641
1642         assert(dstdim == imageTransformDimension(transform, srcdim));
1643         assert(transform >= 0 && transform <= 7);
1644
1645         /*
1646                 Compute the transformation from source coordinates (sx,sy)
1647                 to destination coordinates (dx,dy).
1648         */
1649         int sxn = 0;
1650         int syn = 2;
1651         if(transform == 0)         // identity
1652                 sxn = 0, syn = 2;  //   sx = dx, sy = dy
1653         else if(transform == 1)    // rotate by 90 degrees ccw
1654                 sxn = 3, syn = 0;  //   sx = (H-1) - dy, sy = dx
1655         else if(transform == 2)    // rotate by 180 degrees
1656                 sxn = 1, syn = 3;  //   sx = (W-1) - dx, sy = (H-1) - dy
1657         else if(transform == 3)    // rotate by 270 degrees ccw
1658                 sxn = 2, syn = 1;  //   sx = dy, sy = (W-1) - dx
1659         else if(transform == 4)    // flip x
1660                 sxn = 1, syn = 2;  //   sx = (W-1) - dx, sy = dy
1661         else if(transform == 5)    // flip x then rotate by 90 degrees ccw
1662                 sxn = 2, syn = 0;  //   sx = dy, sy = dx
1663         else if(transform == 6)    // flip y
1664                 sxn = 0, syn = 3;  //   sx = dx, sy = (H-1) - dy
1665         else if(transform == 7)    // flip y then rotate by 90 degrees ccw
1666                 sxn = 3, syn = 1;  //   sx = (H-1) - dy, sy = (W-1) - dx
1667
1668         for(u32 dy=0; dy<dstdim.Height; dy++)
1669         for(u32 dx=0; dx<dstdim.Width; dx++)
1670         {
1671                 u32 entries[4] = {dx, dstdim.Width-1-dx, dy, dstdim.Height-1-dy};
1672                 u32 sx = entries[sxn];
1673                 u32 sy = entries[syn];
1674                 video::SColor c = src->getPixel(sx,sy);
1675                 dst->setPixel(dx,dy,c);
1676         }
1677 }