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