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