Support custom textures installed as /textures/all/*.png
[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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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> atlas_dim(1024,1024);
834         video::IImage *atlas_img =
835                         driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
836         //assert(atlas_img);
837         if(atlas_img == NULL)
838         {
839                 errorstream<<"TextureSource::buildMainAtlas(): Failed to create atlas "
840                                 "image; not building texture atlas."<<std::endl;
841                 return;
842         }
843
844         /*
845                 Grab list of stuff to include in the texture atlas from the
846                 main content features
847         */
848
849         core::map<std::string, bool> sourcelist;
850
851         for(u16 j=0; j<MAX_CONTENT+1; j++)
852         {
853                 if(j == CONTENT_IGNORE || j == CONTENT_AIR)
854                         continue;
855                 const ContentFeatures &f = ndef->get(j);
856                 for(u32 i=0; i<6; i++)
857                 {
858                         std::string name = f.tname_tiles[i];
859                         sourcelist[name] = true;
860                 }
861         }
862         
863         infostream<<"Creating texture atlas out of textures: ";
864         for(core::map<std::string, bool>::Iterator
865                         i = sourcelist.getIterator();
866                         i.atEnd() == false; i++)
867         {
868                 std::string name = i.getNode()->getKey();
869                 infostream<<"\""<<name<<"\" ";
870         }
871         infostream<<std::endl;
872
873         // Padding to disallow texture bleeding
874         s32 padding = 16;
875
876         s32 column_width = 256;
877         s32 column_padding = 16;
878
879         /*
880                 First pass: generate almost everything
881         */
882         core::position2d<s32> pos_in_atlas(0,0);
883         
884         pos_in_atlas.Y = padding;
885
886         for(core::map<std::string, bool>::Iterator
887                         i = sourcelist.getIterator();
888                         i.atEnd() == false; i++)
889         {
890                 std::string name = i.getNode()->getKey();
891
892                 // Generate image by name
893                 video::IImage *img2 = generate_image_from_scratch(name, m_device,
894                                 &m_sourcecache);
895                 if(img2 == NULL)
896                 {
897                         errorstream<<"TextureSource::buildMainAtlas(): "
898                                         <<"Couldn't generate image \""<<name<<"\""<<std::endl;
899                         continue;
900                 }
901
902                 core::dimension2d<u32> dim = img2->getDimension();
903
904                 // Don't add to atlas if image is large
905                 core::dimension2d<u32> max_size_in_atlas(32,32);
906                 if(dim.Width > max_size_in_atlas.Width
907                 || dim.Height > max_size_in_atlas.Height)
908                 {
909                         infostream<<"TextureSource::buildMainAtlas(): Not adding "
910                                         <<"\""<<name<<"\" because image is large"<<std::endl;
911                         continue;
912                 }
913
914                 // Wrap columns and stop making atlas if atlas is full
915                 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
916                 {
917                         if(pos_in_atlas.X > (s32)atlas_dim.Width - 256 - padding){
918                                 errorstream<<"TextureSource::buildMainAtlas(): "
919                                                 <<"Atlas is full, not adding more textures."
920                                                 <<std::endl;
921                                 break;
922                         }
923                         pos_in_atlas.Y = padding;
924                         pos_in_atlas.X += column_width + column_padding;
925                 }
926                 
927                 /*infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
928                                 <<"\" to texture atlas"<<std::endl;*/
929
930                 // Tile it a few times in the X direction
931                 u16 xwise_tiling = column_width / dim.Width;
932                 if(xwise_tiling > 16) // Limit to 16 (more gives no benefit)
933                         xwise_tiling = 16;
934                 for(u32 j=0; j<xwise_tiling; j++)
935                 {
936                         // Copy the copy to the atlas
937                         /*img2->copyToWithAlpha(atlas_img,
938                                         pos_in_atlas + v2s32(j*dim.Width,0),
939                                         core::rect<s32>(v2s32(0,0), dim),
940                                         video::SColor(255,255,255,255),
941                                         NULL);*/
942                         img2->copyTo(atlas_img,
943                                         pos_in_atlas + v2s32(j*dim.Width,0),
944                                         core::rect<s32>(v2s32(0,0), dim),
945                                         NULL);
946                 }
947
948                 // Copy the borders a few times to disallow texture bleeding
949                 for(u32 side=0; side<2; side++) // top and bottom
950                 for(s32 y0=0; y0<padding; y0++)
951                 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
952                 {
953                         s32 dst_y;
954                         s32 src_y;
955                         if(side==0)
956                         {
957                                 dst_y = y0 + pos_in_atlas.Y + dim.Height;
958                                 src_y = pos_in_atlas.Y + dim.Height - 1;
959                         }
960                         else
961                         {
962                                 dst_y = -y0 + pos_in_atlas.Y-1;
963                                 src_y = pos_in_atlas.Y;
964                         }
965                         s32 x = x0 + pos_in_atlas.X;
966                         video::SColor c = atlas_img->getPixel(x, src_y);
967                         atlas_img->setPixel(x,dst_y,c);
968                 }
969
970                 img2->drop();
971
972                 /*
973                         Add texture to caches
974                 */
975                 
976                 bool reuse_old_id = false;
977                 u32 id = m_atlaspointer_cache.size();
978                 // Check old id without fetching a texture
979                 core::map<std::string, u32>::Node *n;
980                 n = m_name_to_id.find(name);
981                 // If it exists, we will replace the old definition
982                 if(n){
983                         id = n->getValue();
984                         reuse_old_id = true;
985                         /*infostream<<"TextureSource::buildMainAtlas(): "
986                                         <<"Replacing old AtlasPointer"<<std::endl;*/
987                 }
988
989                 // Create AtlasPointer
990                 AtlasPointer ap(id);
991                 ap.atlas = NULL; // Set on the second pass
992                 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
993                                 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
994                 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
995                                 (float)dim.Width/(float)atlas_dim.Height);
996                 ap.tiled = xwise_tiling;
997
998                 // Create SourceAtlasPointer and add to containers
999                 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
1000                 if(reuse_old_id)
1001                         m_atlaspointer_cache[id] = nap;
1002                 else
1003                         m_atlaspointer_cache.push_back(nap);
1004                 m_name_to_id[name] = id;
1005                         
1006                 // Increment position
1007                 pos_in_atlas.Y += dim.Height + padding * 2;
1008         }
1009
1010         /*
1011                 Make texture
1012         */
1013         video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
1014         assert(t);
1015
1016         /*
1017                 Second pass: set texture pointer in generated AtlasPointers
1018         */
1019         for(core::map<std::string, bool>::Iterator
1020                         i = sourcelist.getIterator();
1021                         i.atEnd() == false; i++)
1022         {
1023                 std::string name = i.getNode()->getKey();
1024                 if(m_name_to_id.find(name) == NULL)
1025                         continue;
1026                 u32 id = m_name_to_id[name];
1027                 //infostream<<"id of name "<<name<<" is "<<id<<std::endl;
1028                 m_atlaspointer_cache[id].a.atlas = t;
1029         }
1030
1031         /*
1032                 Write image to file so that it can be inspected
1033         */
1034         /*std::string atlaspath = porting::path_user
1035                         + DIR_DELIM + "generated_texture_atlas.png";
1036         infostream<<"Removing and writing texture atlas for inspection to "
1037                         <<atlaspath<<std::endl;
1038         fs::RecursiveDelete(atlaspath);
1039         driver->writeImageToFile(atlas_img, atlaspath.c_str());*/
1040 }
1041
1042 video::IImage* generate_image_from_scratch(std::string name,
1043                 IrrlichtDevice *device, SourceImageCache *sourcecache)
1044 {
1045         /*infostream<<"generate_image_from_scratch(): "
1046                         "\""<<name<<"\""<<std::endl;*/
1047         
1048         video::IVideoDriver* driver = device->getVideoDriver();
1049         assert(driver);
1050
1051         /*
1052                 Get the base image
1053         */
1054
1055         video::IImage *baseimg = NULL;
1056
1057         char separator = '^';
1058
1059         // Find last meta separator in name
1060         s32 last_separator_position = name.find_last_of(separator);
1061         //if(last_separator_position == std::npos)
1062         //      last_separator_position = -1;
1063
1064         /*infostream<<"generate_image_from_scratch(): "
1065                         <<"last_separator_position="<<last_separator_position
1066                         <<std::endl;*/
1067
1068         /*
1069                 If separator was found, construct the base name and make the
1070                 base image using a recursive call
1071         */
1072         std::string base_image_name;
1073         if(last_separator_position != -1)
1074         {
1075                 // Construct base name
1076                 base_image_name = name.substr(0, last_separator_position);
1077                 /*infostream<<"generate_image_from_scratch(): Calling itself recursively"
1078                                 " to get base image of \""<<name<<"\" = \""
1079                 <<base_image_name<<"\""<<std::endl;*/
1080                 baseimg = generate_image_from_scratch(base_image_name, device,
1081                                 sourcecache);
1082         }
1083         
1084         /*
1085                 Parse out the last part of the name of the image and act
1086                 according to it
1087         */
1088
1089         std::string last_part_of_name = name.substr(last_separator_position+1);
1090         //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
1091         
1092         // Generate image according to part of name
1093         if(!generate_image(last_part_of_name, baseimg, device, sourcecache))
1094         {
1095                 errorstream<<"generate_image_from_scratch(): "
1096                                 "failed to generate \""<<last_part_of_name<<"\""
1097                                 <<std::endl;
1098                 return NULL;
1099         }
1100         
1101         return baseimg;
1102 }
1103
1104 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
1105                 IrrlichtDevice *device, SourceImageCache *sourcecache)
1106 {
1107         video::IVideoDriver* driver = device->getVideoDriver();
1108         assert(driver);
1109
1110         // Stuff starting with [ are special commands
1111         if(part_of_name.size() == 0 || part_of_name[0] != '[')
1112         {
1113                 video::IImage *image = sourcecache->getOrLoad(part_of_name, device);
1114
1115                 if(image == NULL)
1116                 {
1117                         if(part_of_name != ""){
1118                                 errorstream<<"generate_image(): Could not load image \""
1119                                                 <<part_of_name<<"\""<<" while building texture"<<std::endl;
1120                                 errorstream<<"generate_image(): Creating a dummy"
1121                                                 <<" image for \""<<part_of_name<<"\""<<std::endl;
1122                         }
1123
1124                         // Just create a dummy image
1125                         //core::dimension2d<u32> dim(2,2);
1126                         core::dimension2d<u32> dim(1,1);
1127                         image = driver->createImage(video::ECF_A8R8G8B8, dim);
1128                         assert(image);
1129                         /*image->setPixel(0,0, video::SColor(255,255,0,0));
1130                         image->setPixel(1,0, video::SColor(255,0,255,0));
1131                         image->setPixel(0,1, video::SColor(255,0,0,255));
1132                         image->setPixel(1,1, video::SColor(255,255,0,255));*/
1133                         image->setPixel(0,0, video::SColor(255,myrand()%256,
1134                                         myrand()%256,myrand()%256));
1135                         /*image->setPixel(1,0, video::SColor(255,myrand()%256,
1136                                         myrand()%256,myrand()%256));
1137                         image->setPixel(0,1, video::SColor(255,myrand()%256,
1138                                         myrand()%256,myrand()%256));
1139                         image->setPixel(1,1, video::SColor(255,myrand()%256,
1140                                         myrand()%256,myrand()%256));*/
1141                 }
1142
1143                 // If base image is NULL, load as base.
1144                 if(baseimg == NULL)
1145                 {
1146                         //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
1147                         /*
1148                                 Copy it this way to get an alpha channel.
1149                                 Otherwise images with alpha cannot be blitted on 
1150                                 images that don't have alpha in the original file.
1151                         */
1152                         core::dimension2d<u32> dim = image->getDimension();
1153                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1154                         image->copyTo(baseimg);
1155                         image->drop();
1156                 }
1157                 // Else blit on base.
1158                 else
1159                 {
1160                         //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
1161                         // Size of the copied area
1162                         core::dimension2d<u32> dim = image->getDimension();
1163                         //core::dimension2d<u32> dim(16,16);
1164                         // Position to copy the blitted to in the base image
1165                         core::position2d<s32> pos_to(0,0);
1166                         // Position to copy the blitted from in the blitted image
1167                         core::position2d<s32> pos_from(0,0);
1168                         // Blit
1169                         image->copyToWithAlpha(baseimg, pos_to,
1170                                         core::rect<s32>(pos_from, dim),
1171                                         video::SColor(255,255,255,255),
1172                                         NULL);
1173                         // Drop image
1174                         image->drop();
1175                 }
1176         }
1177         else
1178         {
1179                 // A special texture modification
1180
1181                 /*infostream<<"generate_image(): generating special "
1182                                 <<"modification \""<<part_of_name<<"\""
1183                                 <<std::endl;*/
1184                 
1185                 /*
1186                         This is the simplest of all; it just adds stuff to the
1187                         name so that a separate texture is created.
1188
1189                         It is used to make textures for stuff that doesn't want
1190                         to implement getting the texture from a bigger texture
1191                         atlas.
1192                 */
1193                 if(part_of_name == "[forcesingle")
1194                 {
1195                         // If base image is NULL, create a random color
1196                         if(baseimg == NULL)
1197                         {
1198                                 core::dimension2d<u32> dim(1,1);
1199                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1200                                 assert(baseimg);
1201                                 baseimg->setPixel(0,0, video::SColor(255,myrand()%256,
1202                                                 myrand()%256,myrand()%256));
1203                         }
1204                 }
1205                 /*
1206                         [crackN
1207                         Adds a cracking texture
1208                 */
1209                 else if(part_of_name.substr(0,6) == "[crack")
1210                 {
1211                         if(baseimg == NULL)
1212                         {
1213                                 errorstream<<"generate_image(): baseimg==NULL "
1214                                                 <<"for part_of_name=\""<<part_of_name
1215                                                 <<"\", cancelling."<<std::endl;
1216                                 return false;
1217                         }
1218                         
1219                         // Crack image number and overlay option
1220                         s32 progression = 0;
1221                         bool use_overlay = false;
1222                         if(part_of_name.substr(6,1) == "o")
1223                         {
1224                                 progression = stoi(part_of_name.substr(7));
1225                                 use_overlay = true;
1226                         }
1227                         else
1228                         {
1229                                 progression = stoi(part_of_name.substr(6));
1230                                 use_overlay = false;
1231                         }
1232
1233                         // Size of the base image
1234                         core::dimension2d<u32> dim_base = baseimg->getDimension();
1235                         
1236                         /*
1237                                 Load crack image.
1238
1239                                 It is an image with a number of cracking stages
1240                                 horizontally tiled.
1241                         */
1242                         video::IImage *img_crack = sourcecache->getOrLoad("crack.png", device);
1243                 
1244                         if(img_crack && progression >= 0)
1245                         {
1246                                 // Dimension of original image
1247                                 core::dimension2d<u32> dim_crack
1248                                                 = img_crack->getDimension();
1249                                 // Count of crack stages
1250                                 s32 crack_count = dim_crack.Height / dim_crack.Width;
1251                                 // Limit progression
1252                                 if(progression > crack_count-1)
1253                                         progression = crack_count-1;
1254                                 // Dimension of a single crack stage
1255                                 core::dimension2d<u32> dim_crack_cropped(
1256                                         dim_crack.Width,
1257                                         dim_crack.Width
1258                                 );
1259                                 // Create cropped and scaled crack images
1260                                 video::IImage *img_crack_cropped = driver->createImage(
1261                                                 video::ECF_A8R8G8B8, dim_crack_cropped);
1262                                 video::IImage *img_crack_scaled = driver->createImage(
1263                                                 video::ECF_A8R8G8B8, dim_base);
1264
1265                                 if(img_crack_cropped && img_crack_scaled)
1266                                 {
1267                                         // Crop crack image
1268                                         v2s32 pos_crack(0, progression*dim_crack.Width);
1269                                         img_crack->copyTo(img_crack_cropped,
1270                                                         v2s32(0,0),
1271                                                         core::rect<s32>(pos_crack, dim_crack_cropped));
1272                                         // Scale crack image by copying
1273                                         img_crack_cropped->copyToScaling(img_crack_scaled);
1274                                         // Copy or overlay crack image
1275                                         if(use_overlay)
1276                                         {
1277                                                 overlay(baseimg, img_crack_scaled);
1278                                         }
1279                                         else
1280                                         {
1281                                                 img_crack_scaled->copyToWithAlpha(
1282                                                                 baseimg,
1283                                                                 v2s32(0,0),
1284                                                                 core::rect<s32>(v2s32(0,0), dim_base),
1285                                                                 video::SColor(255,255,255,255));
1286                                         }
1287                                 }
1288
1289                                 if(img_crack_scaled)
1290                                         img_crack_scaled->drop();
1291
1292                                 if(img_crack_cropped)
1293                                         img_crack_cropped->drop();
1294                                 
1295                                 img_crack->drop();
1296                         }
1297                 }
1298                 /*
1299                         [combine:WxH:X,Y=filename:X,Y=filename2
1300                         Creates a bigger texture from an amount of smaller ones
1301                 */
1302                 else if(part_of_name.substr(0,8) == "[combine")
1303                 {
1304                         Strfnd sf(part_of_name);
1305                         sf.next(":");
1306                         u32 w0 = stoi(sf.next("x"));
1307                         u32 h0 = stoi(sf.next(":"));
1308                         infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
1309                         core::dimension2d<u32> dim(w0,h0);
1310                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1311                         while(sf.atend() == false)
1312                         {
1313                                 u32 x = stoi(sf.next(","));
1314                                 u32 y = stoi(sf.next("="));
1315                                 std::string filename = sf.next(":");
1316                                 infostream<<"Adding \""<<filename
1317                                                 <<"\" to combined ("<<x<<","<<y<<")"
1318                                                 <<std::endl;
1319                                 video::IImage *img = sourcecache->getOrLoad(filename, device);
1320                                 if(img)
1321                                 {
1322                                         core::dimension2d<u32> dim = img->getDimension();
1323                                         infostream<<"Size "<<dim.Width
1324                                                         <<"x"<<dim.Height<<std::endl;
1325                                         core::position2d<s32> pos_base(x, y);
1326                                         video::IImage *img2 =
1327                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
1328                                         img->copyTo(img2);
1329                                         img->drop();
1330                                         img2->copyToWithAlpha(baseimg, pos_base,
1331                                                         core::rect<s32>(v2s32(0,0), dim),
1332                                                         video::SColor(255,255,255,255),
1333                                                         NULL);
1334                                         img2->drop();
1335                                 }
1336                                 else
1337                                 {
1338                                         infostream<<"img==NULL"<<std::endl;
1339                                 }
1340                         }
1341                 }
1342                 /*
1343                         "[brighten"
1344                 */
1345                 else if(part_of_name.substr(0,9) == "[brighten")
1346                 {
1347                         if(baseimg == NULL)
1348                         {
1349                                 errorstream<<"generate_image(): baseimg==NULL "
1350                                                 <<"for part_of_name=\""<<part_of_name
1351                                                 <<"\", cancelling."<<std::endl;
1352                                 return false;
1353                         }
1354
1355                         brighten(baseimg);
1356                 }
1357                 /*
1358                         "[noalpha"
1359                         Make image completely opaque.
1360                         Used for the leaves texture when in old leaves mode, so
1361                         that the transparent parts don't look completely black 
1362                         when simple alpha channel is used for rendering.
1363                 */
1364                 else if(part_of_name.substr(0,8) == "[noalpha")
1365                 {
1366                         if(baseimg == NULL)
1367                         {
1368                                 errorstream<<"generate_image(): baseimg==NULL "
1369                                                 <<"for part_of_name=\""<<part_of_name
1370                                                 <<"\", cancelling."<<std::endl;
1371                                 return false;
1372                         }
1373
1374                         core::dimension2d<u32> dim = baseimg->getDimension();
1375                         
1376                         // Set alpha to full
1377                         for(u32 y=0; y<dim.Height; y++)
1378                         for(u32 x=0; x<dim.Width; x++)
1379                         {
1380                                 video::SColor c = baseimg->getPixel(x,y);
1381                                 c.setAlpha(255);
1382                                 baseimg->setPixel(x,y,c);
1383                         }
1384                 }
1385                 /*
1386                         "[makealpha:R,G,B"
1387                         Convert one color to transparent.
1388                 */
1389                 else if(part_of_name.substr(0,11) == "[makealpha:")
1390                 {
1391                         if(baseimg == NULL)
1392                         {
1393                                 errorstream<<"generate_image(): baseimg==NULL "
1394                                                 <<"for part_of_name=\""<<part_of_name
1395                                                 <<"\", cancelling."<<std::endl;
1396                                 return false;
1397                         }
1398
1399                         Strfnd sf(part_of_name.substr(11));
1400                         u32 r1 = stoi(sf.next(","));
1401                         u32 g1 = stoi(sf.next(","));
1402                         u32 b1 = stoi(sf.next(""));
1403                         std::string filename = sf.next("");
1404
1405                         core::dimension2d<u32> dim = baseimg->getDimension();
1406                         
1407                         /*video::IImage *oldbaseimg = baseimg;
1408                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1409                         oldbaseimg->copyTo(baseimg);
1410                         oldbaseimg->drop();*/
1411
1412                         // Set alpha to full
1413                         for(u32 y=0; y<dim.Height; y++)
1414                         for(u32 x=0; x<dim.Width; x++)
1415                         {
1416                                 video::SColor c = baseimg->getPixel(x,y);
1417                                 u32 r = c.getRed();
1418                                 u32 g = c.getGreen();
1419                                 u32 b = c.getBlue();
1420                                 if(!(r == r1 && g == g1 && b == b1))
1421                                         continue;
1422                                 c.setAlpha(0);
1423                                 baseimg->setPixel(x,y,c);
1424                         }
1425                 }
1426                 /*
1427                         "[transformN"
1428                         Rotates and/or flips the image.
1429
1430                         N can be a number (between 0 and 7) or a transform name.
1431                         Rotations are counter-clockwise.
1432                         0  I      identity
1433                         1  R90    rotate by 90 degrees
1434                         2  R180   rotate by 180 degrees
1435                         3  R270   rotate by 270 degrees
1436                         4  FX     flip X
1437                         5  FXR90  flip X then rotate by 90 degrees
1438                         6  FY     flip Y
1439                         7  FYR90  flip Y then rotate by 90 degrees
1440
1441                         Note: Transform names can be concatenated to produce
1442                         their product (applies the first then the second).
1443                         The resulting transform will be equivalent to one of the
1444                         eight existing ones, though (see: dihedral group).
1445                 */
1446                 else if(part_of_name.substr(0,10) == "[transform")
1447                 {
1448                         if(baseimg == NULL)
1449                         {
1450                                 errorstream<<"generate_image(): baseimg==NULL "
1451                                                 <<"for part_of_name=\""<<part_of_name
1452                                                 <<"\", cancelling."<<std::endl;
1453                                 return false;
1454                         }
1455
1456                         u32 transform = parseImageTransform(part_of_name.substr(10));
1457                         core::dimension2d<u32> dim = imageTransformDimension(
1458                                         transform, baseimg->getDimension());
1459                         video::IImage *image = driver->createImage(
1460                                         baseimg->getColorFormat(), dim);
1461                         assert(image);
1462                         imageTransform(transform, baseimg, image);
1463                         baseimg->drop();
1464                         baseimg = image;
1465                 }
1466                 /*
1467                         [inventorycube{topimage{leftimage{rightimage
1468                         In every subimage, replace ^ with &.
1469                         Create an "inventory cube".
1470                         NOTE: This should be used only on its own.
1471                         Example (a grass block (not actually used in game):
1472                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1473                 */
1474                 else if(part_of_name.substr(0,14) == "[inventorycube")
1475                 {
1476                         if(baseimg != NULL)
1477                         {
1478                                 errorstream<<"generate_image(): baseimg!=NULL "
1479                                                 <<"for part_of_name=\""<<part_of_name
1480                                                 <<"\", cancelling."<<std::endl;
1481                                 return false;
1482                         }
1483
1484                         str_replace_char(part_of_name, '&', '^');
1485                         Strfnd sf(part_of_name);
1486                         sf.next("{");
1487                         std::string imagename_top = sf.next("{");
1488                         std::string imagename_left = sf.next("{");
1489                         std::string imagename_right = sf.next("{");
1490
1491                         // Generate images for the faces of the cube
1492                         video::IImage *img_top = generate_image_from_scratch(
1493                                         imagename_top, device, sourcecache);
1494                         video::IImage *img_left = generate_image_from_scratch(
1495                                         imagename_left, device, sourcecache);
1496                         video::IImage *img_right = generate_image_from_scratch(
1497                                         imagename_right, device, sourcecache);
1498                         assert(img_top && img_left && img_right);
1499
1500                         // Create textures from images
1501                         video::ITexture *texture_top = driver->addTexture(
1502                                         (imagename_top + "__temp__").c_str(), img_top);
1503                         video::ITexture *texture_left = driver->addTexture(
1504                                         (imagename_left + "__temp__").c_str(), img_left);
1505                         video::ITexture *texture_right = driver->addTexture(
1506                                         (imagename_right + "__temp__").c_str(), img_right);
1507                         assert(texture_top && texture_left && texture_right);
1508
1509                         // Drop images
1510                         img_top->drop();
1511                         img_left->drop();
1512                         img_right->drop();
1513                         
1514                         /*
1515                                 Draw a cube mesh into a render target texture
1516                         */
1517                         scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
1518                         setMeshColor(cube, video::SColor(255, 255, 255, 255));
1519                         cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
1520                         cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
1521                         cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
1522                         cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
1523                         cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
1524                         cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
1525
1526                         core::dimension2d<u32> dim(64,64);
1527                         std::string rtt_texture_name = part_of_name + "_RTT";
1528
1529                         v3f camera_position(0, 1.0, -1.5);
1530                         camera_position.rotateXZBy(45);
1531                         v3f camera_lookat(0, 0, 0);
1532                         core::CMatrix4<f32> camera_projection_matrix;
1533                         // Set orthogonal projection
1534                         camera_projection_matrix.buildProjectionMatrixOrthoLH(
1535                                         1.65, 1.65, 0, 100);
1536
1537                         video::SColorf ambient_light(0.2,0.2,0.2);
1538                         v3f light_position(10, 100, -50);
1539                         video::SColorf light_color(0.5,0.5,0.5);
1540                         f32 light_radius = 1000;
1541
1542                         video::ITexture *rtt = generateTextureFromMesh(
1543                                         cube, device, dim, rtt_texture_name,
1544                                         camera_position,
1545                                         camera_lookat,
1546                                         camera_projection_matrix,
1547                                         ambient_light,
1548                                         light_position,
1549                                         light_color,
1550                                         light_radius);
1551                         
1552                         // Drop mesh
1553                         cube->drop();
1554
1555                         // Free textures of images
1556                         driver->removeTexture(texture_top);
1557                         driver->removeTexture(texture_left);
1558                         driver->removeTexture(texture_right);
1559                         
1560                         if(rtt == NULL)
1561                         {
1562                                 baseimg = generate_image_from_scratch(
1563                                                 imagename_top, device, sourcecache);
1564                                 return true;
1565                         }
1566
1567                         // Create image of render target
1568                         video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1569                         assert(image);
1570
1571                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1572
1573                         if(image)
1574                         {
1575                                 image->copyTo(baseimg);
1576                                 image->drop();
1577                         }
1578                 }
1579                 else
1580                 {
1581                         errorstream<<"generate_image(): Invalid "
1582                                         " modification: \""<<part_of_name<<"\""<<std::endl;
1583                 }
1584         }
1585
1586         return true;
1587 }
1588
1589 void overlay(video::IImage *image, video::IImage *overlay)
1590 {
1591         /*
1592                 Copy overlay to image, taking alpha into account.
1593                 Where image is transparent, don't copy from overlay.
1594                 Images sizes must be identical.
1595         */
1596         if(image == NULL || overlay == NULL)
1597                 return;
1598         
1599         core::dimension2d<u32> dim = image->getDimension();
1600         core::dimension2d<u32> dim_overlay = overlay->getDimension();
1601         assert(dim == dim_overlay);
1602
1603         for(u32 y=0; y<dim.Height; y++)
1604         for(u32 x=0; x<dim.Width; x++)
1605         {
1606                 video::SColor c1 = image->getPixel(x,y);
1607                 video::SColor c2 = overlay->getPixel(x,y);
1608                 u32 a1 = c1.getAlpha();
1609                 u32 a2 = c2.getAlpha();
1610                 if(a1 == 255 && a2 != 0)
1611                 {
1612                         c1.setRed((c1.getRed()*(255-a2) + c2.getRed()*a2)/255);
1613                         c1.setGreen((c1.getGreen()*(255-a2) + c2.getGreen()*a2)/255);
1614                         c1.setBlue((c1.getBlue()*(255-a2) + c2.getBlue()*a2)/255);
1615                 }
1616                 image->setPixel(x,y,c1);
1617         }
1618 }
1619
1620 void brighten(video::IImage *image)
1621 {
1622         if(image == NULL)
1623                 return;
1624         
1625         core::dimension2d<u32> dim = image->getDimension();
1626
1627         for(u32 y=0; y<dim.Height; y++)
1628         for(u32 x=0; x<dim.Width; x++)
1629         {
1630                 video::SColor c = image->getPixel(x,y);
1631                 c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
1632                 c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
1633                 c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
1634                 image->setPixel(x,y,c);
1635         }
1636 }
1637
1638 u32 parseImageTransform(const std::string& s)
1639 {
1640         int total_transform = 0;
1641
1642         std::string transform_names[8];
1643         transform_names[0] = "i";
1644         transform_names[1] = "r90";
1645         transform_names[2] = "r180";
1646         transform_names[3] = "r270";
1647         transform_names[4] = "fx";
1648         transform_names[6] = "fy";
1649
1650         std::size_t pos = 0;
1651         while(pos < s.size())
1652         {
1653                 int transform = -1;
1654                 for(int i = 0; i <= 7; ++i)
1655                 {
1656                         const std::string &name_i = transform_names[i];
1657
1658                         if(s[pos] == ('0' + i))
1659                         {
1660                                 transform = i;
1661                                 pos++;
1662                                 break;
1663                         }
1664                         else if(!(name_i.empty()) &&
1665                                 lowercase(s.substr(pos, name_i.size())) == name_i)
1666                         {
1667                                 transform = i;
1668                                 pos += name_i.size();
1669                                 break;
1670                         }
1671                 }
1672                 if(transform < 0)
1673                         break;
1674
1675                 // Multiply total_transform and transform in the group D4
1676                 int new_total = 0;
1677                 if(transform < 4)
1678                         new_total = (transform + total_transform) % 4;
1679                 else
1680                         new_total = (transform - total_transform + 8) % 4;
1681                 if((transform >= 4) ^ (total_transform >= 4))
1682                         new_total += 4;
1683
1684                 total_transform = new_total;
1685         }
1686         return total_transform;
1687 }
1688
1689 core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
1690 {
1691         if(transform % 2 == 0)
1692                 return dim;
1693         else
1694                 return core::dimension2d<u32>(dim.Height, dim.Width);
1695 }
1696
1697 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
1698 {
1699         if(src == NULL || dst == NULL)
1700                 return;
1701         
1702         core::dimension2d<u32> srcdim = src->getDimension();
1703         core::dimension2d<u32> dstdim = dst->getDimension();
1704
1705         assert(dstdim == imageTransformDimension(transform, srcdim));
1706         assert(transform >= 0 && transform <= 7);
1707
1708         /*
1709                 Compute the transformation from source coordinates (sx,sy)
1710                 to destination coordinates (dx,dy).
1711         */
1712         int sxn = 0;
1713         int syn = 2;
1714         if(transform == 0)         // identity
1715                 sxn = 0, syn = 2;  //   sx = dx, sy = dy
1716         else if(transform == 1)    // rotate by 90 degrees ccw
1717                 sxn = 3, syn = 0;  //   sx = (H-1) - dy, sy = dx
1718         else if(transform == 2)    // rotate by 180 degrees
1719                 sxn = 1, syn = 3;  //   sx = (W-1) - dx, sy = (H-1) - dy
1720         else if(transform == 3)    // rotate by 270 degrees ccw
1721                 sxn = 2, syn = 1;  //   sx = dy, sy = (W-1) - dx
1722         else if(transform == 4)    // flip x
1723                 sxn = 1, syn = 2;  //   sx = (W-1) - dx, sy = dy
1724         else if(transform == 5)    // flip x then rotate by 90 degrees ccw
1725                 sxn = 2, syn = 0;  //   sx = dy, sy = dx
1726         else if(transform == 6)    // flip y
1727                 sxn = 0, syn = 3;  //   sx = dx, sy = (H-1) - dy
1728         else if(transform == 7)    // flip y then rotate by 90 degrees ccw
1729                 sxn = 3, syn = 1;  //   sx = (H-1) - dy, sy = (W-1) - dx
1730
1731         for(u32 dy=0; dy<dstdim.Height; dy++)
1732         for(u32 dx=0; dx<dstdim.Width; dx++)
1733         {
1734                 u32 entries[4] = {dx, dstdim.Width-1-dx, dy, dstdim.Height-1-dy};
1735                 u32 sx = entries[sxn];
1736                 u32 sy = entries[syn];
1737                 video::SColor c = src->getPixel(sx,sy);
1738                 dst->setPixel(dx,dy,c);
1739         }
1740 }