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