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