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