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