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