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