Fix random glitches in transparent animations
[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                         // Drop image
1208                         image->drop();
1209                 }
1210         }
1211         else
1212         {
1213                 // A special texture modification
1214
1215                 /*infostream<<"generate_image(): generating special "
1216                                 <<"modification \""<<part_of_name<<"\""
1217                                 <<std::endl;*/
1218                 
1219                 /*
1220                         This is the simplest of all; it just adds stuff to the
1221                         name so that a separate texture is created.
1222
1223                         It is used to make textures for stuff that doesn't want
1224                         to implement getting the texture from a bigger texture
1225                         atlas.
1226                 */
1227                 if(part_of_name == "[forcesingle")
1228                 {
1229                         // If base image is NULL, create a random color
1230                         if(baseimg == NULL)
1231                         {
1232                                 core::dimension2d<u32> dim(1,1);
1233                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1234                                 assert(baseimg);
1235                                 baseimg->setPixel(0,0, video::SColor(255,myrand()%256,
1236                                                 myrand()%256,myrand()%256));
1237                         }
1238                 }
1239                 /*
1240                         [crackN
1241                         Adds a cracking texture
1242                 */
1243                 else if(part_of_name.substr(0,6) == "[crack")
1244                 {
1245                         if(baseimg == NULL)
1246                         {
1247                                 errorstream<<"generate_image(): baseimg==NULL "
1248                                                 <<"for part_of_name=\""<<part_of_name
1249                                                 <<"\", cancelling."<<std::endl;
1250                                 return false;
1251                         }
1252                         
1253                         // Crack image number and overlay option
1254                         s32 progression = 0;
1255                         bool use_overlay = false;
1256                         if(part_of_name.substr(6,1) == "o")
1257                         {
1258                                 progression = stoi(part_of_name.substr(7));
1259                                 use_overlay = true;
1260                         }
1261                         else
1262                         {
1263                                 progression = stoi(part_of_name.substr(6));
1264                                 use_overlay = false;
1265                         }
1266
1267                         // Size of the base image
1268                         core::dimension2d<u32> dim_base = baseimg->getDimension();
1269                         
1270                         /*
1271                                 Load crack image.
1272
1273                                 It is an image with a number of cracking stages
1274                                 horizontally tiled.
1275                         */
1276                         video::IImage *img_crack = sourcecache->getOrLoad(
1277                                         "crack_anylength.png", device);
1278                 
1279                         if(img_crack && progression >= 0)
1280                         {
1281                                 // Dimension of original image
1282                                 core::dimension2d<u32> dim_crack
1283                                                 = img_crack->getDimension();
1284                                 // Count of crack stages
1285                                 s32 crack_count = dim_crack.Height / dim_crack.Width;
1286                                 // Limit progression
1287                                 if(progression > crack_count-1)
1288                                         progression = crack_count-1;
1289                                 // Dimension of a single crack stage
1290                                 core::dimension2d<u32> dim_crack_cropped(
1291                                         dim_crack.Width,
1292                                         dim_crack.Width
1293                                 );
1294                                 // Create cropped and scaled crack images
1295                                 video::IImage *img_crack_cropped = driver->createImage(
1296                                                 video::ECF_A8R8G8B8, dim_crack_cropped);
1297                                 video::IImage *img_crack_scaled = driver->createImage(
1298                                                 video::ECF_A8R8G8B8, dim_base);
1299
1300                                 if(img_crack_cropped && img_crack_scaled)
1301                                 {
1302                                         // Crop crack image
1303                                         v2s32 pos_crack(0, progression*dim_crack.Width);
1304                                         img_crack->copyTo(img_crack_cropped,
1305                                                         v2s32(0,0),
1306                                                         core::rect<s32>(pos_crack, dim_crack_cropped));
1307                                         // Scale crack image by copying
1308                                         img_crack_cropped->copyToScaling(img_crack_scaled);
1309                                         // Copy or overlay crack image
1310                                         if(use_overlay)
1311                                         {
1312                                                 overlay(baseimg, img_crack_scaled);
1313                                         }
1314                                         else
1315                                         {
1316                                                 /*img_crack_scaled->copyToWithAlpha(
1317                                                                 baseimg,
1318                                                                 v2s32(0,0),
1319                                                                 core::rect<s32>(v2s32(0,0), dim_base),
1320                                                                 video::SColor(255,255,255,255));*/
1321                                                 blit_with_alpha(img_crack_scaled, baseimg,
1322                                                                 v2s32(0,0), v2s32(0,0), dim_base);
1323                                         }
1324                                 }
1325
1326                                 if(img_crack_scaled)
1327                                         img_crack_scaled->drop();
1328
1329                                 if(img_crack_cropped)
1330                                         img_crack_cropped->drop();
1331                                 
1332                                 img_crack->drop();
1333                         }
1334                 }
1335                 /*
1336                         [combine:WxH:X,Y=filename:X,Y=filename2
1337                         Creates a bigger texture from an amount of smaller ones
1338                 */
1339                 else if(part_of_name.substr(0,8) == "[combine")
1340                 {
1341                         Strfnd sf(part_of_name);
1342                         sf.next(":");
1343                         u32 w0 = stoi(sf.next("x"));
1344                         u32 h0 = stoi(sf.next(":"));
1345                         infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
1346                         core::dimension2d<u32> dim(w0,h0);
1347                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1348                         while(sf.atend() == false)
1349                         {
1350                                 u32 x = stoi(sf.next(","));
1351                                 u32 y = stoi(sf.next("="));
1352                                 std::string filename = sf.next(":");
1353                                 infostream<<"Adding \""<<filename
1354                                                 <<"\" to combined ("<<x<<","<<y<<")"
1355                                                 <<std::endl;
1356                                 video::IImage *img = sourcecache->getOrLoad(filename, device);
1357                                 if(img)
1358                                 {
1359                                         core::dimension2d<u32> dim = img->getDimension();
1360                                         infostream<<"Size "<<dim.Width
1361                                                         <<"x"<<dim.Height<<std::endl;
1362                                         core::position2d<s32> pos_base(x, y);
1363                                         video::IImage *img2 =
1364                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
1365                                         img->copyTo(img2);
1366                                         img->drop();
1367                                         img2->copyToWithAlpha(baseimg, pos_base,
1368                                                         core::rect<s32>(v2s32(0,0), dim),
1369                                                         video::SColor(255,255,255,255),
1370                                                         NULL);
1371                                         img2->drop();
1372                                 }
1373                                 else
1374                                 {
1375                                         infostream<<"img==NULL"<<std::endl;
1376                                 }
1377                         }
1378                 }
1379                 /*
1380                         "[brighten"
1381                 */
1382                 else if(part_of_name.substr(0,9) == "[brighten")
1383                 {
1384                         if(baseimg == NULL)
1385                         {
1386                                 errorstream<<"generate_image(): baseimg==NULL "
1387                                                 <<"for part_of_name=\""<<part_of_name
1388                                                 <<"\", cancelling."<<std::endl;
1389                                 return false;
1390                         }
1391
1392                         brighten(baseimg);
1393                 }
1394                 /*
1395                         "[noalpha"
1396                         Make image completely opaque.
1397                         Used for the leaves texture when in old leaves mode, so
1398                         that the transparent parts don't look completely black 
1399                         when simple alpha channel is used for rendering.
1400                 */
1401                 else if(part_of_name.substr(0,8) == "[noalpha")
1402                 {
1403                         if(baseimg == NULL)
1404                         {
1405                                 errorstream<<"generate_image(): baseimg==NULL "
1406                                                 <<"for part_of_name=\""<<part_of_name
1407                                                 <<"\", cancelling."<<std::endl;
1408                                 return false;
1409                         }
1410
1411                         core::dimension2d<u32> dim = baseimg->getDimension();
1412                         
1413                         // Set alpha to full
1414                         for(u32 y=0; y<dim.Height; y++)
1415                         for(u32 x=0; x<dim.Width; x++)
1416                         {
1417                                 video::SColor c = baseimg->getPixel(x,y);
1418                                 c.setAlpha(255);
1419                                 baseimg->setPixel(x,y,c);
1420                         }
1421                 }
1422                 /*
1423                         "[makealpha:R,G,B"
1424                         Convert one color to transparent.
1425                 */
1426                 else if(part_of_name.substr(0,11) == "[makealpha:")
1427                 {
1428                         if(baseimg == NULL)
1429                         {
1430                                 errorstream<<"generate_image(): baseimg==NULL "
1431                                                 <<"for part_of_name=\""<<part_of_name
1432                                                 <<"\", cancelling."<<std::endl;
1433                                 return false;
1434                         }
1435
1436                         Strfnd sf(part_of_name.substr(11));
1437                         u32 r1 = stoi(sf.next(","));
1438                         u32 g1 = stoi(sf.next(","));
1439                         u32 b1 = stoi(sf.next(""));
1440                         std::string filename = sf.next("");
1441
1442                         core::dimension2d<u32> dim = baseimg->getDimension();
1443                         
1444                         /*video::IImage *oldbaseimg = baseimg;
1445                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1446                         oldbaseimg->copyTo(baseimg);
1447                         oldbaseimg->drop();*/
1448
1449                         // Set alpha to full
1450                         for(u32 y=0; y<dim.Height; y++)
1451                         for(u32 x=0; x<dim.Width; x++)
1452                         {
1453                                 video::SColor c = baseimg->getPixel(x,y);
1454                                 u32 r = c.getRed();
1455                                 u32 g = c.getGreen();
1456                                 u32 b = c.getBlue();
1457                                 if(!(r == r1 && g == g1 && b == b1))
1458                                         continue;
1459                                 c.setAlpha(0);
1460                                 baseimg->setPixel(x,y,c);
1461                         }
1462                 }
1463                 /*
1464                         "[transformN"
1465                         Rotates and/or flips the image.
1466
1467                         N can be a number (between 0 and 7) or a transform name.
1468                         Rotations are counter-clockwise.
1469                         0  I      identity
1470                         1  R90    rotate by 90 degrees
1471                         2  R180   rotate by 180 degrees
1472                         3  R270   rotate by 270 degrees
1473                         4  FX     flip X
1474                         5  FXR90  flip X then rotate by 90 degrees
1475                         6  FY     flip Y
1476                         7  FYR90  flip Y then rotate by 90 degrees
1477
1478                         Note: Transform names can be concatenated to produce
1479                         their product (applies the first then the second).
1480                         The resulting transform will be equivalent to one of the
1481                         eight existing ones, though (see: dihedral group).
1482                 */
1483                 else if(part_of_name.substr(0,10) == "[transform")
1484                 {
1485                         if(baseimg == NULL)
1486                         {
1487                                 errorstream<<"generate_image(): baseimg==NULL "
1488                                                 <<"for part_of_name=\""<<part_of_name
1489                                                 <<"\", cancelling."<<std::endl;
1490                                 return false;
1491                         }
1492
1493                         u32 transform = parseImageTransform(part_of_name.substr(10));
1494                         core::dimension2d<u32> dim = imageTransformDimension(
1495                                         transform, baseimg->getDimension());
1496                         video::IImage *image = driver->createImage(
1497                                         baseimg->getColorFormat(), dim);
1498                         assert(image);
1499                         imageTransform(transform, baseimg, image);
1500                         baseimg->drop();
1501                         baseimg = image;
1502                 }
1503                 /*
1504                         [inventorycube{topimage{leftimage{rightimage
1505                         In every subimage, replace ^ with &.
1506                         Create an "inventory cube".
1507                         NOTE: This should be used only on its own.
1508                         Example (a grass block (not actually used in game):
1509                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1510                 */
1511                 else if(part_of_name.substr(0,14) == "[inventorycube")
1512                 {
1513                         if(baseimg != NULL)
1514                         {
1515                                 errorstream<<"generate_image(): baseimg!=NULL "
1516                                                 <<"for part_of_name=\""<<part_of_name
1517                                                 <<"\", cancelling."<<std::endl;
1518                                 return false;
1519                         }
1520
1521                         str_replace_char(part_of_name, '&', '^');
1522                         Strfnd sf(part_of_name);
1523                         sf.next("{");
1524                         std::string imagename_top = sf.next("{");
1525                         std::string imagename_left = sf.next("{");
1526                         std::string imagename_right = sf.next("{");
1527
1528                         // Generate images for the faces of the cube
1529                         video::IImage *img_top = generate_image_from_scratch(
1530                                         imagename_top, device, sourcecache);
1531                         video::IImage *img_left = generate_image_from_scratch(
1532                                         imagename_left, device, sourcecache);
1533                         video::IImage *img_right = generate_image_from_scratch(
1534                                         imagename_right, device, sourcecache);
1535                         assert(img_top && img_left && img_right);
1536
1537                         // Create textures from images
1538                         video::ITexture *texture_top = driver->addTexture(
1539                                         (imagename_top + "__temp__").c_str(), img_top);
1540                         video::ITexture *texture_left = driver->addTexture(
1541                                         (imagename_left + "__temp__").c_str(), img_left);
1542                         video::ITexture *texture_right = driver->addTexture(
1543                                         (imagename_right + "__temp__").c_str(), img_right);
1544                         assert(texture_top && texture_left && texture_right);
1545
1546                         // Drop images
1547                         img_top->drop();
1548                         img_left->drop();
1549                         img_right->drop();
1550                         
1551                         /*
1552                                 Draw a cube mesh into a render target texture
1553                         */
1554                         scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
1555                         setMeshColor(cube, video::SColor(255, 255, 255, 255));
1556                         cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
1557                         cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
1558                         cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
1559                         cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
1560                         cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
1561                         cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
1562
1563                         core::dimension2d<u32> dim(64,64);
1564                         std::string rtt_texture_name = part_of_name + "_RTT";
1565
1566                         v3f camera_position(0, 1.0, -1.5);
1567                         camera_position.rotateXZBy(45);
1568                         v3f camera_lookat(0, 0, 0);
1569                         core::CMatrix4<f32> camera_projection_matrix;
1570                         // Set orthogonal projection
1571                         camera_projection_matrix.buildProjectionMatrixOrthoLH(
1572                                         1.65, 1.65, 0, 100);
1573
1574                         video::SColorf ambient_light(0.2,0.2,0.2);
1575                         v3f light_position(10, 100, -50);
1576                         video::SColorf light_color(0.5,0.5,0.5);
1577                         f32 light_radius = 1000;
1578
1579                         video::ITexture *rtt = generateTextureFromMesh(
1580                                         cube, device, dim, rtt_texture_name,
1581                                         camera_position,
1582                                         camera_lookat,
1583                                         camera_projection_matrix,
1584                                         ambient_light,
1585                                         light_position,
1586                                         light_color,
1587                                         light_radius);
1588                         
1589                         // Drop mesh
1590                         cube->drop();
1591
1592                         // Free textures of images
1593                         driver->removeTexture(texture_top);
1594                         driver->removeTexture(texture_left);
1595                         driver->removeTexture(texture_right);
1596                         
1597                         if(rtt == NULL)
1598                         {
1599                                 baseimg = generate_image_from_scratch(
1600                                                 imagename_top, device, sourcecache);
1601                                 return true;
1602                         }
1603
1604                         // Create image of render target
1605                         video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1606                         assert(image);
1607
1608                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1609
1610                         if(image)
1611                         {
1612                                 image->copyTo(baseimg);
1613                                 image->drop();
1614                         }
1615                 }
1616                 /*
1617                         [lowpart:percent:filename
1618                         Adds the lower part of a texture
1619                 */
1620                 else if(part_of_name.substr(0,9) == "[lowpart:")
1621                 {
1622                         Strfnd sf(part_of_name);
1623                         sf.next(":");
1624                         u32 percent = stoi(sf.next(":"));
1625                         std::string filename = sf.next(":");
1626                         //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
1627
1628                         if(baseimg == NULL)
1629                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
1630                         video::IImage *img = sourcecache->getOrLoad(filename, device);
1631                         if(img)
1632                         {
1633                                 core::dimension2d<u32> dim = img->getDimension();
1634                                 core::position2d<s32> pos_base(0, 0);
1635                                 video::IImage *img2 =
1636                                                 driver->createImage(video::ECF_A8R8G8B8, dim);
1637                                 img->copyTo(img2);
1638                                 img->drop();
1639                                 core::position2d<s32> clippos(0, 0);
1640                                 clippos.Y = dim.Height * (100-percent) / 100;
1641                                 core::dimension2d<u32> clipdim = dim;
1642                                 clipdim.Height = clipdim.Height * percent / 100 + 1;
1643                                 core::rect<s32> cliprect(clippos, clipdim);
1644                                 img2->copyToWithAlpha(baseimg, pos_base,
1645                                                 core::rect<s32>(v2s32(0,0), dim),
1646                                                 video::SColor(255,255,255,255),
1647                                                 &cliprect);
1648                                 img2->drop();
1649                         }
1650                 }
1651                 /*
1652                         [verticalframe:N:I
1653                         Crops a frame of a vertical animation.
1654                         N = frame count, I = frame index
1655                 */
1656                 else if(part_of_name.substr(0,15) == "[verticalframe:")
1657                 {
1658                         Strfnd sf(part_of_name);
1659                         sf.next(":");
1660                         u32 frame_count = stoi(sf.next(":"));
1661                         u32 frame_index = stoi(sf.next(":"));
1662
1663                         if(baseimg == NULL){
1664                                 errorstream<<"generate_image(): baseimg!=NULL "
1665                                                 <<"for part_of_name=\""<<part_of_name
1666                                                 <<"\", cancelling."<<std::endl;
1667                                 return false;
1668                         }
1669                         
1670                         v2u32 frame_size = baseimg->getDimension();
1671                         frame_size.Y /= frame_count;
1672
1673                         video::IImage *img = driver->createImage(video::ECF_A8R8G8B8,
1674                                         frame_size);
1675                         if(!img){
1676                                 errorstream<<"generate_image(): Could not create image "
1677                                                 <<"for part_of_name=\""<<part_of_name
1678                                                 <<"\", cancelling."<<std::endl;
1679                                 return false;
1680                         }
1681
1682                         // Fill target image with transparency
1683                         img->fill(video::SColor(0,0,0,0));
1684
1685                         core::dimension2d<u32> dim = frame_size;
1686                         core::position2d<s32> pos_dst(0, 0);
1687                         core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
1688                         baseimg->copyToWithAlpha(img, pos_dst,
1689                                         core::rect<s32>(pos_src, dim),
1690                                         video::SColor(255,255,255,255),
1691                                         NULL);
1692                         // Replace baseimg
1693                         baseimg->drop();
1694                         baseimg = img;
1695                 }
1696                 else
1697                 {
1698                         errorstream<<"generate_image(): Invalid "
1699                                         " modification: \""<<part_of_name<<"\""<<std::endl;
1700                 }
1701         }
1702
1703         return true;
1704 }
1705
1706 void overlay(video::IImage *image, video::IImage *overlay)
1707 {
1708         /*
1709                 Copy overlay to image, taking alpha into account.
1710                 Where image is transparent, don't copy from overlay.
1711                 Images sizes must be identical.
1712         */
1713         if(image == NULL || overlay == NULL)
1714                 return;
1715         
1716         core::dimension2d<u32> dim = image->getDimension();
1717         core::dimension2d<u32> dim_overlay = overlay->getDimension();
1718         assert(dim == dim_overlay);
1719
1720         for(u32 y=0; y<dim.Height; y++)
1721         for(u32 x=0; x<dim.Width; x++)
1722         {
1723                 video::SColor c1 = image->getPixel(x,y);
1724                 video::SColor c2 = overlay->getPixel(x,y);
1725                 u32 a1 = c1.getAlpha();
1726                 u32 a2 = c2.getAlpha();
1727                 if(a1 == 255 && a2 != 0)
1728                 {
1729                         c1.setRed((c1.getRed()*(255-a2) + c2.getRed()*a2)/255);
1730                         c1.setGreen((c1.getGreen()*(255-a2) + c2.getGreen()*a2)/255);
1731                         c1.setBlue((c1.getBlue()*(255-a2) + c2.getBlue()*a2)/255);
1732                 }
1733                 image->setPixel(x,y,c1);
1734         }
1735 }
1736
1737 /*
1738         Draw an image on top of an another one, using the alpha channel of the
1739         source image
1740
1741         This exists because IImage::copyToWithAlpha() doesn't seem to always
1742         work.
1743 */
1744 static void blit_with_alpha(video::IImage *src, video::IImage *dst,
1745                 v2s32 src_pos, v2s32 dst_pos, v2u32 size)
1746 {
1747         for(u32 y0=0; y0<size.Y; y0++)
1748         for(u32 x0=0; x0<size.X; x0++)
1749         {
1750                 s32 src_x = src_pos.X + x0;
1751                 s32 src_y = src_pos.Y + y0;
1752                 s32 dst_x = dst_pos.X + x0;
1753                 s32 dst_y = dst_pos.Y + y0;
1754                 video::SColor src_c = src->getPixel(src_x, src_y);
1755                 video::SColor dst_c = dst->getPixel(dst_x, dst_y);
1756                 dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
1757                 dst->setPixel(dst_x, dst_y, dst_c);
1758         }
1759 }
1760
1761 void brighten(video::IImage *image)
1762 {
1763         if(image == NULL)
1764                 return;
1765         
1766         core::dimension2d<u32> dim = image->getDimension();
1767
1768         for(u32 y=0; y<dim.Height; y++)
1769         for(u32 x=0; x<dim.Width; x++)
1770         {
1771                 video::SColor c = image->getPixel(x,y);
1772                 c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
1773                 c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
1774                 c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
1775                 image->setPixel(x,y,c);
1776         }
1777 }
1778
1779 u32 parseImageTransform(const std::string& s)
1780 {
1781         int total_transform = 0;
1782
1783         std::string transform_names[8];
1784         transform_names[0] = "i";
1785         transform_names[1] = "r90";
1786         transform_names[2] = "r180";
1787         transform_names[3] = "r270";
1788         transform_names[4] = "fx";
1789         transform_names[6] = "fy";
1790
1791         std::size_t pos = 0;
1792         while(pos < s.size())
1793         {
1794                 int transform = -1;
1795                 for(int i = 0; i <= 7; ++i)
1796                 {
1797                         const std::string &name_i = transform_names[i];
1798
1799                         if(s[pos] == ('0' + i))
1800                         {
1801                                 transform = i;
1802                                 pos++;
1803                                 break;
1804                         }
1805                         else if(!(name_i.empty()) &&
1806                                 lowercase(s.substr(pos, name_i.size())) == name_i)
1807                         {
1808                                 transform = i;
1809                                 pos += name_i.size();
1810                                 break;
1811                         }
1812                 }
1813                 if(transform < 0)
1814                         break;
1815
1816                 // Multiply total_transform and transform in the group D4
1817                 int new_total = 0;
1818                 if(transform < 4)
1819                         new_total = (transform + total_transform) % 4;
1820                 else
1821                         new_total = (transform - total_transform + 8) % 4;
1822                 if((transform >= 4) ^ (total_transform >= 4))
1823                         new_total += 4;
1824
1825                 total_transform = new_total;
1826         }
1827         return total_transform;
1828 }
1829
1830 core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
1831 {
1832         if(transform % 2 == 0)
1833                 return dim;
1834         else
1835                 return core::dimension2d<u32>(dim.Height, dim.Width);
1836 }
1837
1838 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
1839 {
1840         if(src == NULL || dst == NULL)
1841                 return;
1842         
1843         core::dimension2d<u32> srcdim = src->getDimension();
1844         core::dimension2d<u32> dstdim = dst->getDimension();
1845
1846         assert(dstdim == imageTransformDimension(transform, srcdim));
1847         assert(transform >= 0 && transform <= 7);
1848
1849         /*
1850                 Compute the transformation from source coordinates (sx,sy)
1851                 to destination coordinates (dx,dy).
1852         */
1853         int sxn = 0;
1854         int syn = 2;
1855         if(transform == 0)         // identity
1856                 sxn = 0, syn = 2;  //   sx = dx, sy = dy
1857         else if(transform == 1)    // rotate by 90 degrees ccw
1858                 sxn = 3, syn = 0;  //   sx = (H-1) - dy, sy = dx
1859         else if(transform == 2)    // rotate by 180 degrees
1860                 sxn = 1, syn = 3;  //   sx = (W-1) - dx, sy = (H-1) - dy
1861         else if(transform == 3)    // rotate by 270 degrees ccw
1862                 sxn = 2, syn = 1;  //   sx = dy, sy = (W-1) - dx
1863         else if(transform == 4)    // flip x
1864                 sxn = 1, syn = 2;  //   sx = (W-1) - dx, sy = dy
1865         else if(transform == 5)    // flip x then rotate by 90 degrees ccw
1866                 sxn = 2, syn = 0;  //   sx = dy, sy = dx
1867         else if(transform == 6)    // flip y
1868                 sxn = 0, syn = 3;  //   sx = dx, sy = (H-1) - dy
1869         else if(transform == 7)    // flip y then rotate by 90 degrees ccw
1870                 sxn = 3, syn = 1;  //   sx = (H-1) - dy, sy = (W-1) - dx
1871
1872         for(u32 dy=0; dy<dstdim.Height; dy++)
1873         for(u32 dx=0; dx<dstdim.Width; dx++)
1874         {
1875                 u32 entries[4] = {dx, dstdim.Width-1-dx, dy, dstdim.Height-1-dy};
1876                 u32 sx = entries[sxn];
1877                 u32 sy = entries[syn];
1878                 video::SColor c = src->getPixel(sx,sy);
1879                 dst->setPixel(dx,dy,c);
1880         }
1881 }