bc4c49cb1717a548babd6a5204f67c58cdd8e43d
[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 "mesh.h"
27 #include <ICameraSceneNode.h>
28 #include "log.h"
29 #include "mapnode.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^mineral_coal.png"
302                 "stone.png^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 + "^[forcesingle");
336                 return ap.atlas;
337         }
338
339         // Returns a pointer to the irrlicht device
340         virtual IrrlichtDevice* getDevice()
341         {
342                 return m_device;
343         }
344
345         // Update new texture pointer and texture coordinates to an
346         // AtlasPointer based on it's texture id
347         void updateAP(AtlasPointer &ap);
348
349         // Processes queued texture requests from other threads.
350         // Shall be called from the main thread.
351         void processQueue();
352         
353         // Insert an image into the cache without touching the filesystem.
354         // Shall be called from the main thread.
355         void insertSourceImage(const std::string &name, video::IImage *img);
356         
357         // Rebuild images and textures from the current set of source images
358         // Shall be called from the main thread.
359         void rebuildImagesAndTextures();
360
361         // Build the main texture atlas which contains most of the
362         // textures.
363         void buildMainAtlas(class IGameDef *gamedef);
364         
365 private:
366         
367         // The id of the thread that is allowed to use irrlicht directly
368         threadid_t m_main_thread;
369         // The irrlicht device
370         IrrlichtDevice *m_device;
371         
372         // Cache of source images
373         // This should be only accessed from the main thread
374         SourceImageCache m_sourcecache;
375
376         // A texture id is index in this array.
377         // The first position contains a NULL texture.
378         core::array<SourceAtlasPointer> m_atlaspointer_cache;
379         // Maps a texture name to an index in the former.
380         core::map<std::string, u32> m_name_to_id;
381         // The two former containers are behind this mutex
382         JMutex m_atlaspointer_cache_mutex;
383         
384         // Main texture atlas. This is filled at startup and is then not touched.
385         video::IImage *m_main_atlas_image;
386         video::ITexture *m_main_atlas_texture;
387
388         // Queued texture fetches (to be processed by the main thread)
389         RequestQueue<std::string, u32, u8, u8> m_get_texture_queue;
390 };
391
392 IWritableTextureSource* createTextureSource(IrrlichtDevice *device)
393 {
394         return new TextureSource(device);
395 }
396
397 TextureSource::TextureSource(IrrlichtDevice *device):
398                 m_device(device),
399                 m_main_atlas_image(NULL),
400                 m_main_atlas_texture(NULL)
401 {
402         assert(m_device);
403         
404         m_atlaspointer_cache_mutex.Init();
405         
406         m_main_thread = get_current_thread_id();
407         
408         // Add a NULL AtlasPointer as the first index, named ""
409         m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
410         m_name_to_id[""] = 0;
411 }
412
413 TextureSource::~TextureSource()
414 {
415 }
416
417 u32 TextureSource::getTextureId(const std::string &name)
418 {
419         //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
420
421         {
422                 /*
423                         See if texture already exists
424                 */
425                 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
426                 core::map<std::string, u32>::Node *n;
427                 n = m_name_to_id.find(name);
428                 if(n != NULL)
429                 {
430                         return n->getValue();
431                 }
432         }
433         
434         /*
435                 Get texture
436         */
437         if(get_current_thread_id() == m_main_thread)
438         {
439                 return getTextureIdDirect(name);
440         }
441         else
442         {
443                 infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
444
445                 // We're gonna ask the result to be put into here
446                 ResultQueue<std::string, u32, u8, u8> result_queue;
447                 
448                 // Throw a request in
449                 m_get_texture_queue.add(name, 0, 0, &result_queue);
450                 
451                 infostream<<"Waiting for texture from main thread, name=\""
452                                 <<name<<"\""<<std::endl;
453                 
454                 try
455                 {
456                         // Wait result for a second
457                         GetResult<std::string, u32, u8, u8>
458                                         result = result_queue.pop_front(1000);
459                 
460                         // Check that at least something worked OK
461                         assert(result.key == name);
462
463                         return result.item;
464                 }
465                 catch(ItemNotFoundException &e)
466                 {
467                         infostream<<"Waiting for texture timed out."<<std::endl;
468                         return 0;
469                 }
470         }
471         
472         infostream<<"getTextureId(): Failed"<<std::endl;
473
474         return 0;
475 }
476
477 // Brighten image
478 void brighten(video::IImage *image);
479
480 /*
481         Generate image based on a string like "stone.png" or "[crack0".
482         if baseimg is NULL, it is created. Otherwise stuff is made on it.
483 */
484 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
485                 IrrlichtDevice *device, SourceImageCache *sourcecache);
486
487 /*
488         Generates an image from a full string like
489         "stone.png^mineral_coal.png^[crack0".
490
491         This is used by buildMainAtlas().
492 */
493 video::IImage* generate_image_from_scratch(std::string name,
494                 IrrlichtDevice *device, SourceImageCache *sourcecache);
495
496 /*
497         This method generates all the textures
498 */
499 u32 TextureSource::getTextureIdDirect(const std::string &name)
500 {
501         //infostream<<"getTextureIdDirect(): name=\""<<name<<"\""<<std::endl;
502
503         // Empty name means texture 0
504         if(name == "")
505         {
506                 infostream<<"getTextureIdDirect(): name is empty"<<std::endl;
507                 return 0;
508         }
509         
510         /*
511                 Calling only allowed from main thread
512         */
513         if(get_current_thread_id() != m_main_thread)
514         {
515                 errorstream<<"TextureSource::getTextureIdDirect() "
516                                 "called not from main thread"<<std::endl;
517                 return 0;
518         }
519
520         /*
521                 See if texture already exists
522         */
523         {
524                 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
525
526                 core::map<std::string, u32>::Node *n;
527                 n = m_name_to_id.find(name);
528                 if(n != NULL)
529                 {
530                         /*infostream<<"getTextureIdDirect(): \""<<name
531                                         <<"\" found in cache"<<std::endl;*/
532                         return n->getValue();
533                 }
534         }
535
536         /*infostream<<"getTextureIdDirect(): \""<<name
537                         <<"\" NOT found in cache. Creating it."<<std::endl;*/
538         
539         /*
540                 Get the base image
541         */
542
543         char separator = '^';
544
545         /*
546                 This is set to the id of the base image.
547                 If left 0, there is no base image and a completely new image
548                 is made.
549         */
550         u32 base_image_id = 0;
551         
552         // Find last meta separator in name
553         s32 last_separator_position = -1;
554         for(s32 i=name.size()-1; i>=0; i--)
555         {
556                 if(name[i] == separator)
557                 {
558                         last_separator_position = i;
559                         break;
560                 }
561         }
562         /*
563                 If separator was found, construct the base name and make the
564                 base image using a recursive call
565         */
566         std::string base_image_name;
567         if(last_separator_position != -1)
568         {
569                 // Construct base name
570                 base_image_name = name.substr(0, last_separator_position);
571                 /*infostream<<"getTextureIdDirect(): Calling itself recursively"
572                                 " to get base image of \""<<name<<"\" = \""
573                 <<base_image_name<<"\""<<std::endl;*/
574                 base_image_id = getTextureIdDirect(base_image_name);
575         }
576         
577         //infostream<<"base_image_id="<<base_image_id<<std::endl;
578         
579         video::IVideoDriver* driver = m_device->getVideoDriver();
580         assert(driver);
581
582         video::ITexture *t = NULL;
583
584         /*
585                 An image will be built from files and then converted into a texture.
586         */
587         video::IImage *baseimg = NULL;
588         
589         // If a base image was found, copy it to baseimg
590         if(base_image_id != 0)
591         {
592                 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
593
594                 SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
595
596                 video::IImage *image = ap.atlas_img;
597                 
598                 if(image == NULL)
599                 {
600                         infostream<<"getTextureIdDirect(): WARNING: NULL image in "
601                                         <<"cache: \""<<base_image_name<<"\""
602                                         <<std::endl;
603                 }
604                 else
605                 {
606                         core::dimension2d<u32> dim = ap.intsize;
607
608                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
609
610                         core::position2d<s32> pos_to(0,0);
611                         core::position2d<s32> pos_from = ap.intpos;
612                         
613                         image->copyTo(
614                                         baseimg, // target
615                                         v2s32(0,0), // position in target
616                                         core::rect<s32>(pos_from, dim) // from
617                         );
618
619                         /*infostream<<"getTextureIdDirect(): Loaded \""
620                                         <<base_image_name<<"\" from image cache"
621                                         <<std::endl;*/
622                 }
623         }
624         
625         /*
626                 Parse out the last part of the name of the image and act
627                 according to it
628         */
629
630         std::string last_part_of_name = name.substr(last_separator_position+1);
631         //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
632
633         // Generate image according to part of name
634         if(!generate_image(last_part_of_name, baseimg, m_device, &m_sourcecache))
635         {
636                 errorstream<<"getTextureIdDirect(): "
637                                 "failed to generate \""<<last_part_of_name<<"\""
638                                 <<std::endl;
639         }
640
641         // If no resulting image, print a warning
642         if(baseimg == NULL)
643         {
644                 errorstream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
645                                 " create texture \""<<name<<"\""<<std::endl;
646         }
647         
648         if(baseimg != NULL)
649         {
650                 // Create texture from resulting image
651                 t = driver->addTexture(name.c_str(), baseimg);
652         }
653         
654         /*
655                 Add texture to caches (add NULL textures too)
656         */
657
658         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
659         
660         u32 id = m_atlaspointer_cache.size();
661         AtlasPointer ap(id);
662         ap.atlas = t;
663         ap.pos = v2f(0,0);
664         ap.size = v2f(1,1);
665         ap.tiled = 0;
666         core::dimension2d<u32> baseimg_dim(0,0);
667         if(baseimg)
668                 baseimg_dim = baseimg->getDimension();
669         SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
670         m_atlaspointer_cache.push_back(nap);
671         m_name_to_id.insert(name, id);
672
673         /*infostream<<"getTextureIdDirect(): "
674                         <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
675         
676         return id;
677 }
678
679 std::string TextureSource::getTextureName(u32 id)
680 {
681         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
682
683         if(id >= m_atlaspointer_cache.size())
684         {
685                 errorstream<<"TextureSource::getTextureName(): id="<<id
686                                 <<" >= m_atlaspointer_cache.size()="
687                                 <<m_atlaspointer_cache.size()<<std::endl;
688                 return "";
689         }
690         
691         return m_atlaspointer_cache[id].name;
692 }
693
694
695 AtlasPointer TextureSource::getTexture(u32 id)
696 {
697         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
698
699         if(id >= m_atlaspointer_cache.size())
700                 return AtlasPointer(0, NULL);
701         
702         return m_atlaspointer_cache[id].a;
703 }
704
705 void TextureSource::updateAP(AtlasPointer &ap)
706 {
707         AtlasPointer ap2 = getTexture(ap.id);
708         ap = ap2;
709 }
710
711 void TextureSource::processQueue()
712 {
713         /*
714                 Fetch textures
715         */
716         if(m_get_texture_queue.size() > 0)
717         {
718                 GetRequest<std::string, u32, u8, u8>
719                                 request = m_get_texture_queue.pop();
720
721                 /*infostream<<"TextureSource::processQueue(): "
722                                 <<"got texture request with "
723                                 <<"name=\""<<request.key<<"\""
724                                 <<std::endl;*/
725
726                 GetResult<std::string, u32, u8, u8>
727                                 result;
728                 result.key = request.key;
729                 result.callers = request.callers;
730                 result.item = getTextureIdDirect(request.key);
731
732                 request.dest->push_back(result);
733         }
734 }
735
736 void TextureSource::insertSourceImage(const std::string &name, video::IImage *img)
737 {
738         //infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
739         
740         assert(get_current_thread_id() == m_main_thread);
741         
742         m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
743 }
744         
745 void TextureSource::rebuildImagesAndTextures()
746 {
747         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
748
749         /*// Oh well... just clear everything, they'll load sometime.
750         m_atlaspointer_cache.clear();
751         m_name_to_id.clear();*/
752
753         video::IVideoDriver* driver = m_device->getVideoDriver();
754         
755         // Remove source images from textures to disable inheriting textures
756         // from existing textures
757         /*for(u32 i=0; i<m_atlaspointer_cache.size(); i++){
758                 SourceAtlasPointer *sap = &m_atlaspointer_cache[i];
759                 sap->atlas_img->drop();
760                 sap->atlas_img = NULL;
761         }*/
762         
763         // Recreate textures
764         for(u32 i=0; i<m_atlaspointer_cache.size(); i++){
765                 SourceAtlasPointer *sap = &m_atlaspointer_cache[i];
766                 video::IImage *img =
767                         generate_image_from_scratch(sap->name, m_device, &m_sourcecache);
768                 // Create texture from resulting image
769                 video::ITexture *t = NULL;
770                 if(img)
771                         t = driver->addTexture(sap->name.c_str(), img);
772                 
773                 // Replace texture
774                 sap->a.atlas = t;
775                 sap->a.pos = v2f(0,0);
776                 sap->a.size = v2f(1,1);
777                 sap->a.tiled = 0;
778                 sap->atlas_img = img;
779                 sap->intpos = v2s32(0,0);
780                 sap->intsize = img->getDimension();
781         }
782 }
783
784 void TextureSource::buildMainAtlas(class IGameDef *gamedef) 
785 {
786         assert(gamedef->tsrc() == this);
787         INodeDefManager *ndef = gamedef->ndef();
788
789         infostream<<"TextureSource::buildMainAtlas()"<<std::endl;
790
791         //return; // Disable (for testing)
792         
793         video::IVideoDriver* driver = m_device->getVideoDriver();
794         assert(driver);
795
796         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
797
798         // Create an image of the right size
799         core::dimension2d<u32> atlas_dim(1024,1024);
800         video::IImage *atlas_img =
801                         driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
802         //assert(atlas_img);
803         if(atlas_img == NULL)
804         {
805                 errorstream<<"TextureSource::buildMainAtlas(): Failed to create atlas "
806                                 "image; not building texture atlas."<<std::endl;
807                 return;
808         }
809
810         /*
811                 Grab list of stuff to include in the texture atlas from the
812                 main content features
813         */
814
815         core::map<std::string, bool> sourcelist;
816
817         for(u16 j=0; j<MAX_CONTENT+1; j++)
818         {
819                 if(j == CONTENT_IGNORE || j == CONTENT_AIR)
820                         continue;
821                 const ContentFeatures &f = ndef->get(j);
822                 for(u32 i=0; i<6; i++)
823                 {
824                         std::string name = f.tname_tiles[i];
825                         sourcelist[name] = true;
826                 }
827         }
828         
829         infostream<<"Creating texture atlas out of textures: ";
830         for(core::map<std::string, bool>::Iterator
831                         i = sourcelist.getIterator();
832                         i.atEnd() == false; i++)
833         {
834                 std::string name = i.getNode()->getKey();
835                 infostream<<"\""<<name<<"\" ";
836         }
837         infostream<<std::endl;
838
839         // Padding to disallow texture bleeding
840         s32 padding = 16;
841
842         s32 column_width = 256;
843         s32 column_padding = 16;
844
845         /*
846                 First pass: generate almost everything
847         */
848         core::position2d<s32> pos_in_atlas(0,0);
849         
850         pos_in_atlas.Y = padding;
851
852         for(core::map<std::string, bool>::Iterator
853                         i = sourcelist.getIterator();
854                         i.atEnd() == false; i++)
855         {
856                 std::string name = i.getNode()->getKey();
857
858                 // Generate image by name
859                 video::IImage *img2 = generate_image_from_scratch(name, m_device,
860                                 &m_sourcecache);
861                 if(img2 == NULL)
862                 {
863                         errorstream<<"TextureSource::buildMainAtlas(): "
864                                         <<"Couldn't generate image \""<<name<<"\""<<std::endl;
865                         continue;
866                 }
867
868                 core::dimension2d<u32> dim = img2->getDimension();
869
870                 // Don't add to atlas if image is large
871                 core::dimension2d<u32> max_size_in_atlas(32,32);
872                 if(dim.Width > max_size_in_atlas.Width
873                 || dim.Height > max_size_in_atlas.Height)
874                 {
875                         infostream<<"TextureSource::buildMainAtlas(): Not adding "
876                                         <<"\""<<name<<"\" because image is large"<<std::endl;
877                         continue;
878                 }
879
880                 // Wrap columns and stop making atlas if atlas is full
881                 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
882                 {
883                         if(pos_in_atlas.X > (s32)atlas_dim.Width - 256 - padding){
884                                 errorstream<<"TextureSource::buildMainAtlas(): "
885                                                 <<"Atlas is full, not adding more textures."
886                                                 <<std::endl;
887                                 break;
888                         }
889                         pos_in_atlas.Y = padding;
890                         pos_in_atlas.X += column_width + column_padding;
891                 }
892                 
893                 /*infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
894                                 <<"\" to texture atlas"<<std::endl;*/
895
896                 // Tile it a few times in the X direction
897                 u16 xwise_tiling = column_width / dim.Width;
898                 if(xwise_tiling > 16) // Limit to 16 (more gives no benefit)
899                         xwise_tiling = 16;
900                 for(u32 j=0; j<xwise_tiling; j++)
901                 {
902                         // Copy the copy to the atlas
903                         /*img2->copyToWithAlpha(atlas_img,
904                                         pos_in_atlas + v2s32(j*dim.Width,0),
905                                         core::rect<s32>(v2s32(0,0), dim),
906                                         video::SColor(255,255,255,255),
907                                         NULL);*/
908                         img2->copyTo(atlas_img,
909                                         pos_in_atlas + v2s32(j*dim.Width,0),
910                                         core::rect<s32>(v2s32(0,0), dim),
911                                         NULL);
912                 }
913
914                 // Copy the borders a few times to disallow texture bleeding
915                 for(u32 side=0; side<2; side++) // top and bottom
916                 for(s32 y0=0; y0<padding; y0++)
917                 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
918                 {
919                         s32 dst_y;
920                         s32 src_y;
921                         if(side==0)
922                         {
923                                 dst_y = y0 + pos_in_atlas.Y + dim.Height;
924                                 src_y = pos_in_atlas.Y + dim.Height - 1;
925                         }
926                         else
927                         {
928                                 dst_y = -y0 + pos_in_atlas.Y-1;
929                                 src_y = pos_in_atlas.Y;
930                         }
931                         s32 x = x0 + pos_in_atlas.X;
932                         video::SColor c = atlas_img->getPixel(x, src_y);
933                         atlas_img->setPixel(x,dst_y,c);
934                 }
935
936                 img2->drop();
937
938                 /*
939                         Add texture to caches
940                 */
941                 
942                 bool reuse_old_id = false;
943                 u32 id = m_atlaspointer_cache.size();
944                 // Check old id without fetching a texture
945                 core::map<std::string, u32>::Node *n;
946                 n = m_name_to_id.find(name);
947                 // If it exists, we will replace the old definition
948                 if(n){
949                         id = n->getValue();
950                         reuse_old_id = true;
951                         /*infostream<<"TextureSource::buildMainAtlas(): "
952                                         <<"Replacing old AtlasPointer"<<std::endl;*/
953                 }
954
955                 // Create AtlasPointer
956                 AtlasPointer ap(id);
957                 ap.atlas = NULL; // Set on the second pass
958                 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
959                                 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
960                 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
961                                 (float)dim.Width/(float)atlas_dim.Height);
962                 ap.tiled = xwise_tiling;
963
964                 // Create SourceAtlasPointer and add to containers
965                 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
966                 if(reuse_old_id)
967                         m_atlaspointer_cache[id] = nap;
968                 else
969                         m_atlaspointer_cache.push_back(nap);
970                 m_name_to_id[name] = id;
971                         
972                 // Increment position
973                 pos_in_atlas.Y += dim.Height + padding * 2;
974         }
975
976         /*
977                 Make texture
978         */
979         video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
980         assert(t);
981
982         /*
983                 Second pass: set texture pointer in generated AtlasPointers
984         */
985         for(core::map<std::string, bool>::Iterator
986                         i = sourcelist.getIterator();
987                         i.atEnd() == false; i++)
988         {
989                 std::string name = i.getNode()->getKey();
990                 if(m_name_to_id.find(name) == NULL)
991                         continue;
992                 u32 id = m_name_to_id[name];
993                 //infostream<<"id of name "<<name<<" is "<<id<<std::endl;
994                 m_atlaspointer_cache[id].a.atlas = t;
995         }
996
997         /*
998                 Write image to file so that it can be inspected
999         */
1000         /*std::string atlaspath = porting::path_userdata
1001                         + DIR_DELIM + "generated_texture_atlas.png";
1002         infostream<<"Removing and writing texture atlas for inspection to "
1003                         <<atlaspath<<std::endl;
1004         fs::RecursiveDelete(atlaspath);
1005         driver->writeImageToFile(atlas_img, atlaspath.c_str());*/
1006 }
1007
1008 video::IImage* generate_image_from_scratch(std::string name,
1009                 IrrlichtDevice *device, SourceImageCache *sourcecache)
1010 {
1011         /*infostream<<"generate_image_from_scratch(): "
1012                         "\""<<name<<"\""<<std::endl;*/
1013         
1014         video::IVideoDriver* driver = device->getVideoDriver();
1015         assert(driver);
1016
1017         /*
1018                 Get the base image
1019         */
1020
1021         video::IImage *baseimg = NULL;
1022
1023         char separator = '^';
1024
1025         // Find last meta separator in name
1026         s32 last_separator_position = -1;
1027         for(s32 i=name.size()-1; i>=0; i--)
1028         {
1029                 if(name[i] == separator)
1030                 {
1031                         last_separator_position = i;
1032                         break;
1033                 }
1034         }
1035
1036         /*infostream<<"generate_image_from_scratch(): "
1037                         <<"last_separator_position="<<last_separator_position
1038                         <<std::endl;*/
1039
1040         /*
1041                 If separator was found, construct the base name and make the
1042                 base image using a recursive call
1043         */
1044         std::string base_image_name;
1045         if(last_separator_position != -1)
1046         {
1047                 // Construct base name
1048                 base_image_name = name.substr(0, last_separator_position);
1049                 /*infostream<<"generate_image_from_scratch(): Calling itself recursively"
1050                                 " to get base image of \""<<name<<"\" = \""
1051                 <<base_image_name<<"\""<<std::endl;*/
1052                 baseimg = generate_image_from_scratch(base_image_name, device,
1053                                 sourcecache);
1054         }
1055         
1056         /*
1057                 Parse out the last part of the name of the image and act
1058                 according to it
1059         */
1060
1061         std::string last_part_of_name = name.substr(last_separator_position+1);
1062         //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
1063         
1064         // Generate image according to part of name
1065         if(!generate_image(last_part_of_name, baseimg, device, sourcecache))
1066         {
1067                 errorstream<<"generate_image_from_scratch(): "
1068                                 "failed to generate \""<<last_part_of_name<<"\""
1069                                 <<std::endl;
1070                 return NULL;
1071         }
1072         
1073         return baseimg;
1074 }
1075
1076 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
1077                 IrrlichtDevice *device, SourceImageCache *sourcecache)
1078 {
1079         video::IVideoDriver* driver = device->getVideoDriver();
1080         assert(driver);
1081
1082         // Stuff starting with [ are special commands
1083         if(part_of_name.size() == 0 || part_of_name[0] != '[')
1084         {
1085                 video::IImage *image = sourcecache->getOrLoad(part_of_name, device);
1086
1087                 if(image == NULL)
1088                 {
1089                         if(part_of_name != ""){
1090                                 errorstream<<"generate_image(): Could not load image \""
1091                                                 <<part_of_name<<"\""<<" while building texture"<<std::endl;
1092                                 errorstream<<"generate_image(): Creating a dummy"
1093                                                 <<" image for \""<<part_of_name<<"\""<<std::endl;
1094                         }
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                         // If base image is NULL, create a random color
1168                         if(baseimg == NULL)
1169                         {
1170                                 core::dimension2d<u32> dim(1,1);
1171                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1172                                 assert(baseimg);
1173                                 baseimg->setPixel(0,0, video::SColor(255,myrand()%256,
1174                                                 myrand()%256,myrand()%256));
1175                         }
1176                 }
1177                 /*
1178                         [crackN
1179                         Adds a cracking texture
1180                 */
1181                 else if(part_of_name.substr(0,6) == "[crack")
1182                 {
1183                         if(baseimg == NULL)
1184                         {
1185                                 errorstream<<"generate_image(): baseimg==NULL "
1186                                                 <<"for part_of_name=\""<<part_of_name
1187                                                 <<"\", cancelling."<<std::endl;
1188                                 return false;
1189                         }
1190                         
1191                         // Crack image number
1192                         u16 progression = stoi(part_of_name.substr(6));
1193
1194                         // Size of the base image
1195                         core::dimension2d<u32> dim_base = baseimg->getDimension();
1196                         
1197                         /*
1198                                 Load crack image.
1199
1200                                 It is an image with a number of cracking stages
1201                                 horizontally tiled.
1202                         */
1203                         video::IImage *img_crack = sourcecache->getOrLoad("crack.png", device);
1204                 
1205                         if(img_crack)
1206                         {
1207                                 // Dimension of original image
1208                                 core::dimension2d<u32> dim_crack
1209                                                 = img_crack->getDimension();
1210                                 // Count of crack stages
1211                                 u32 crack_count = dim_crack.Height / dim_crack.Width;
1212                                 // Limit progression
1213                                 if(progression > crack_count-1)
1214                                         progression = crack_count-1;
1215                                 // Dimension of a single scaled crack stage
1216                                 core::dimension2d<u32> dim_crack_scaled_single(
1217                                         dim_base.Width,
1218                                         dim_base.Height
1219                                 );
1220                                 // Dimension of scaled size
1221                                 core::dimension2d<u32> dim_crack_scaled(
1222                                         dim_crack_scaled_single.Width,
1223                                         dim_crack_scaled_single.Height * crack_count
1224                                 );
1225                                 // Create scaled crack image
1226                                 video::IImage *img_crack_scaled = driver->createImage(
1227                                                 video::ECF_A8R8G8B8, dim_crack_scaled);
1228                                 if(img_crack_scaled)
1229                                 {
1230                                         // Scale crack image by copying
1231                                         img_crack->copyToScaling(img_crack_scaled);
1232                                         
1233                                         // Position to copy the crack from
1234                                         core::position2d<s32> pos_crack_scaled(
1235                                                 0,
1236                                                 dim_crack_scaled_single.Height * progression
1237                                         );
1238                                         
1239                                         // This tiling does nothing currently but is useful
1240                                         for(u32 y0=0; y0<dim_base.Height
1241                                                         / dim_crack_scaled_single.Height; y0++)
1242                                         for(u32 x0=0; x0<dim_base.Width
1243                                                         / dim_crack_scaled_single.Width; x0++)
1244                                         {
1245                                                 // Position to copy the crack to in the base image
1246                                                 core::position2d<s32> pos_base(
1247                                                         x0*dim_crack_scaled_single.Width,
1248                                                         y0*dim_crack_scaled_single.Height
1249                                                 );
1250                                                 // Rectangle to copy the crack from on the scaled image
1251                                                 core::rect<s32> rect_crack_scaled(
1252                                                         pos_crack_scaled,
1253                                                         dim_crack_scaled_single
1254                                                 );
1255                                                 // Copy it
1256                                                 img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
1257                                                                 rect_crack_scaled,
1258                                                                 video::SColor(255,255,255,255),
1259                                                                 NULL);
1260                                         }
1261
1262                                         img_crack_scaled->drop();
1263                                 }
1264                                 
1265                                 img_crack->drop();
1266                         }
1267                 }
1268                 /*
1269                         [combine:WxH:X,Y=filename:X,Y=filename2
1270                         Creates a bigger texture from an amount of smaller ones
1271                 */
1272                 else if(part_of_name.substr(0,8) == "[combine")
1273                 {
1274                         Strfnd sf(part_of_name);
1275                         sf.next(":");
1276                         u32 w0 = stoi(sf.next("x"));
1277                         u32 h0 = stoi(sf.next(":"));
1278                         infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
1279                         core::dimension2d<u32> dim(w0,h0);
1280                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1281                         while(sf.atend() == false)
1282                         {
1283                                 u32 x = stoi(sf.next(","));
1284                                 u32 y = stoi(sf.next("="));
1285                                 std::string filename = sf.next(":");
1286                                 infostream<<"Adding \""<<filename
1287                                                 <<"\" to combined ("<<x<<","<<y<<")"
1288                                                 <<std::endl;
1289                                 video::IImage *img = sourcecache->getOrLoad(filename, device);
1290                                 if(img)
1291                                 {
1292                                         core::dimension2d<u32> dim = img->getDimension();
1293                                         infostream<<"Size "<<dim.Width
1294                                                         <<"x"<<dim.Height<<std::endl;
1295                                         core::position2d<s32> pos_base(x, y);
1296                                         video::IImage *img2 =
1297                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
1298                                         img->copyTo(img2);
1299                                         img->drop();
1300                                         img2->copyToWithAlpha(baseimg, pos_base,
1301                                                         core::rect<s32>(v2s32(0,0), dim),
1302                                                         video::SColor(255,255,255,255),
1303                                                         NULL);
1304                                         img2->drop();
1305                                 }
1306                                 else
1307                                 {
1308                                         infostream<<"img==NULL"<<std::endl;
1309                                 }
1310                         }
1311                 }
1312                 /*
1313                         "[brighten"
1314                 */
1315                 else if(part_of_name.substr(0,9) == "[brighten")
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                         brighten(baseimg);
1326                 }
1327                 /*
1328                         "[noalpha"
1329                         Make image completely opaque.
1330                         Used for the leaves texture when in old leaves mode, so
1331                         that the transparent parts don't look completely black 
1332                         when simple alpha channel is used for rendering.
1333                 */
1334                 else if(part_of_name.substr(0,8) == "[noalpha")
1335                 {
1336                         if(baseimg == NULL)
1337                         {
1338                                 errorstream<<"generate_image(): baseimg==NULL "
1339                                                 <<"for part_of_name=\""<<part_of_name
1340                                                 <<"\", cancelling."<<std::endl;
1341                                 return false;
1342                         }
1343
1344                         core::dimension2d<u32> dim = baseimg->getDimension();
1345                         
1346                         // Set alpha to full
1347                         for(u32 y=0; y<dim.Height; y++)
1348                         for(u32 x=0; x<dim.Width; x++)
1349                         {
1350                                 video::SColor c = baseimg->getPixel(x,y);
1351                                 c.setAlpha(255);
1352                                 baseimg->setPixel(x,y,c);
1353                         }
1354                 }
1355                 /*
1356                         "[makealpha:R,G,B"
1357                         Convert one color to transparent.
1358                 */
1359                 else if(part_of_name.substr(0,11) == "[makealpha:")
1360                 {
1361                         if(baseimg == NULL)
1362                         {
1363                                 errorstream<<"generate_image(): baseimg==NULL "
1364                                                 <<"for part_of_name=\""<<part_of_name
1365                                                 <<"\", cancelling."<<std::endl;
1366                                 return false;
1367                         }
1368
1369                         Strfnd sf(part_of_name.substr(11));
1370                         u32 r1 = stoi(sf.next(","));
1371                         u32 g1 = stoi(sf.next(","));
1372                         u32 b1 = stoi(sf.next(""));
1373                         std::string filename = sf.next("");
1374
1375                         core::dimension2d<u32> dim = baseimg->getDimension();
1376                         
1377                         /*video::IImage *oldbaseimg = baseimg;
1378                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1379                         oldbaseimg->copyTo(baseimg);
1380                         oldbaseimg->drop();*/
1381
1382                         // Set alpha to full
1383                         for(u32 y=0; y<dim.Height; y++)
1384                         for(u32 x=0; x<dim.Width; x++)
1385                         {
1386                                 video::SColor c = baseimg->getPixel(x,y);
1387                                 u32 r = c.getRed();
1388                                 u32 g = c.getGreen();
1389                                 u32 b = c.getBlue();
1390                                 if(!(r == r1 && g == g1 && b == b1))
1391                                         continue;
1392                                 c.setAlpha(0);
1393                                 baseimg->setPixel(x,y,c);
1394                         }
1395                 }
1396                 /*
1397                         [inventorycube{topimage{leftimage{rightimage
1398                         In every subimage, replace ^ with &.
1399                         Create an "inventory cube".
1400                         NOTE: This should be used only on its own.
1401                         Example (a grass block (not actually used in game):
1402                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1403                 */
1404                 else if(part_of_name.substr(0,14) == "[inventorycube")
1405                 {
1406                         if(baseimg != NULL)
1407                         {
1408                                 errorstream<<"generate_image(): baseimg!=NULL "
1409                                                 <<"for part_of_name=\""<<part_of_name
1410                                                 <<"\", cancelling."<<std::endl;
1411                                 return false;
1412                         }
1413
1414                         str_replace_char(part_of_name, '&', '^');
1415                         Strfnd sf(part_of_name);
1416                         sf.next("{");
1417                         std::string imagename_top = sf.next("{");
1418                         std::string imagename_left = sf.next("{");
1419                         std::string imagename_right = sf.next("{");
1420
1421                         // Generate images for the faces of the cube
1422                         video::IImage *img_top = generate_image_from_scratch(
1423                                         imagename_top, device, sourcecache);
1424                         video::IImage *img_left = generate_image_from_scratch(
1425                                         imagename_left, device, sourcecache);
1426                         video::IImage *img_right = generate_image_from_scratch(
1427                                         imagename_right, device, sourcecache);
1428                         assert(img_top && img_left && img_right);
1429
1430                         // Create textures from images
1431                         video::ITexture *texture_top = driver->addTexture(
1432                                         (imagename_top + "__temp__").c_str(), img_top);
1433                         video::ITexture *texture_left = driver->addTexture(
1434                                         (imagename_left + "__temp__").c_str(), img_left);
1435                         video::ITexture *texture_right = driver->addTexture(
1436                                         (imagename_right + "__temp__").c_str(), img_right);
1437                         assert(texture_top && texture_left && texture_right);
1438
1439                         // Drop images
1440                         img_top->drop();
1441                         img_left->drop();
1442                         img_right->drop();
1443                         
1444                         /*
1445                                 Draw a cube mesh into a render target texture
1446                         */
1447                         scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
1448                         setMeshColor(cube, video::SColor(255, 255, 255, 255));
1449                         cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
1450                         cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
1451                         cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
1452                         cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
1453                         cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
1454                         cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
1455
1456                         core::dimension2d<u32> dim(64,64);
1457                         std::string rtt_texture_name = part_of_name + "_RTT";
1458
1459                         v3f camera_position(0, 1.0, -1.5);
1460                         camera_position.rotateXZBy(45);
1461                         v3f camera_lookat(0, 0, 0);
1462                         core::CMatrix4<f32> camera_projection_matrix;
1463                         // Set orthogonal projection
1464                         camera_projection_matrix.buildProjectionMatrixOrthoLH(
1465                                         1.65, 1.65, 0, 100);
1466
1467                         video::SColorf ambient_light(0.2,0.2,0.2);
1468                         v3f light_position(10, 100, -50);
1469                         video::SColorf light_color(0.5,0.5,0.5);
1470                         f32 light_radius = 1000;
1471
1472                         video::ITexture *rtt = generateTextureFromMesh(
1473                                         cube, device, dim, rtt_texture_name,
1474                                         camera_position,
1475                                         camera_lookat,
1476                                         camera_projection_matrix,
1477                                         ambient_light,
1478                                         light_position,
1479                                         light_color,
1480                                         light_radius);
1481                         
1482                         // Drop mesh
1483                         cube->drop();
1484
1485                         // Free textures of images
1486                         driver->removeTexture(texture_top);
1487                         driver->removeTexture(texture_left);
1488                         driver->removeTexture(texture_right);
1489                         
1490                         if(rtt == NULL)
1491                         {
1492                                 errorstream<<"generate_image(): render to texture failed."
1493                                                 " Creating fallback image"<<std::endl;
1494                                 baseimg = generate_image_from_scratch(
1495                                                 imagename_top, device, sourcecache);
1496                                 return true;
1497                         }
1498
1499                         // Create image of render target
1500                         video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1501                         assert(image);
1502
1503                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1504
1505                         if(image)
1506                         {
1507                                 image->copyTo(baseimg);
1508                                 image->drop();
1509                         }
1510                 }
1511                 else
1512                 {
1513                         errorstream<<"generate_image(): Invalid "
1514                                         " modification: \""<<part_of_name<<"\""<<std::endl;
1515                 }
1516         }
1517
1518         return true;
1519 }
1520
1521 void brighten(video::IImage *image)
1522 {
1523         if(image == NULL)
1524                 return;
1525         
1526         core::dimension2d<u32> dim = image->getDimension();
1527
1528         for(u32 y=0; y<dim.Height; y++)
1529         for(u32 x=0; x<dim.Width; x++)
1530         {
1531                 video::SColor c = image->getPixel(x,y);
1532                 c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
1533                 c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
1534                 c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
1535                 image->setPixel(x,y,c);
1536         }
1537 }
1538