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