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