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