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