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