1d5f4d8338c97ec1d4237c5a3c5e09de7dfbddb7
[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 "mineral.h" // For texture atlas making
31 #include "nodedef.h" // For texture atlas making
32 #include "gamedef.h"
33
34 /*
35         A cache from texture name to texture path
36 */
37 MutexedMap<std::string, std::string> g_texturename_to_path_cache;
38
39 /*
40         Replaces the filename extension.
41         eg:
42                 std::string image = "a/image.png"
43                 replace_ext(image, "jpg")
44                 -> image = "a/image.jpg"
45         Returns true on success.
46 */
47 static bool replace_ext(std::string &path, const char *ext)
48 {
49         if(ext == NULL)
50                 return false;
51         // Find place of last dot, fail if \ or / found.
52         s32 last_dot_i = -1;
53         for(s32 i=path.size()-1; i>=0; i--)
54         {
55                 if(path[i] == '.')
56                 {
57                         last_dot_i = i;
58                         break;
59                 }
60                 
61                 if(path[i] == '\\' || path[i] == '/')
62                         break;
63         }
64         // If not found, return an empty string
65         if(last_dot_i == -1)
66                 return false;
67         // Else make the new path
68         path = path.substr(0, last_dot_i+1) + ext;
69         return true;
70 }
71
72 /*
73         Find out the full path of an image by trying different filename
74         extensions.
75
76         If failed, return "".
77 */
78 static std::string getImagePath(std::string path)
79 {
80         // A NULL-ended list of possible image extensions
81         const char *extensions[] = {
82                 "png", "jpg", "bmp", "tga",
83                 "pcx", "ppm", "psd", "wal", "rgb",
84                 NULL
85         };
86
87         const char **ext = extensions;
88         do{
89                 bool r = replace_ext(path, *ext);
90                 if(r == false)
91                         return "";
92                 if(fs::PathExists(path))
93                         return path;
94         }
95         while((++ext) != NULL);
96         
97         return "";
98 }
99
100 /*
101         Gets the path to a texture by first checking if the texture exists
102         in texture_path and if not, using the data path.
103
104         Checks all supported extensions by replacing the original extension.
105
106         If not found, returns "".
107
108         Utilizes a thread-safe cache.
109 */
110 std::string getTexturePath(const std::string &filename)
111 {
112         std::string fullpath = "";
113         /*
114                 Check from cache
115         */
116         bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
117         if(incache)
118                 return fullpath;
119         
120         /*
121                 Check from texture_path
122         */
123         std::string texture_path = g_settings->get("texture_path");
124         if(texture_path != "")
125         {
126                 std::string testpath = texture_path + DIR_DELIM + filename;
127                 // Check all filename extensions. Returns "" if not found.
128                 fullpath = getImagePath(testpath);
129         }
130         
131         /*
132                 Check from default data directory
133         */
134         if(fullpath == "")
135         {
136                 std::string rel_path = std::string("clienttextures")+DIR_DELIM+filename;
137                 std::string testpath = porting::path_data + 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^blit:mineral_coal.png"
303                 "stone.png^blit: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                         if(f.param_type == CPT_MINERAL){
829                                 for(int k=1; k<MINERAL_COUNT; k++){
830                                         std::string mineraltexture = mineral_block_texture(k);
831                                         std::string fulltexture = name + "^" + mineraltexture;
832                                         sourcelist[fulltexture] = true;
833                                 }
834                         }
835                 }
836         }
837         
838         infostream<<"Creating texture atlas out of textures: ";
839         for(core::map<std::string, bool>::Iterator
840                         i = sourcelist.getIterator();
841                         i.atEnd() == false; i++)
842         {
843                 std::string name = i.getNode()->getKey();
844                 infostream<<"\""<<name<<"\" ";
845         }
846         infostream<<std::endl;
847
848         // Padding to disallow texture bleeding
849         s32 padding = 16;
850
851         s32 column_width = 256;
852         s32 column_padding = 16;
853
854         /*
855                 First pass: generate almost everything
856         */
857         core::position2d<s32> pos_in_atlas(0,0);
858         
859         pos_in_atlas.Y = padding;
860
861         for(core::map<std::string, bool>::Iterator
862                         i = sourcelist.getIterator();
863                         i.atEnd() == false; i++)
864         {
865                 std::string name = i.getNode()->getKey();
866
867                 // Generate image by name
868                 video::IImage *img2 = generate_image_from_scratch(name, m_device,
869                                 &m_sourcecache);
870                 if(img2 == NULL)
871                 {
872                         errorstream<<"TextureSource::buildMainAtlas(): "
873                                         <<"Couldn't generate image \""<<name<<"\""<<std::endl;
874                         continue;
875                 }
876
877                 core::dimension2d<u32> dim = img2->getDimension();
878
879                 // Don't add to atlas if image is large
880                 core::dimension2d<u32> max_size_in_atlas(32,32);
881                 if(dim.Width > max_size_in_atlas.Width
882                 || dim.Height > max_size_in_atlas.Height)
883                 {
884                         infostream<<"TextureSource::buildMainAtlas(): Not adding "
885                                         <<"\""<<name<<"\" because image is large"<<std::endl;
886                         continue;
887                 }
888
889                 // Wrap columns and stop making atlas if atlas is full
890                 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
891                 {
892                         if(pos_in_atlas.X > (s32)atlas_dim.Width - 256 - padding){
893                                 errorstream<<"TextureSource::buildMainAtlas(): "
894                                                 <<"Atlas is full, not adding more textures."
895                                                 <<std::endl;
896                                 break;
897                         }
898                         pos_in_atlas.Y = padding;
899                         pos_in_atlas.X += column_width + column_padding;
900                 }
901                 
902                 /*infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
903                                 <<"\" to texture atlas"<<std::endl;*/
904
905                 // Tile it a few times in the X direction
906                 u16 xwise_tiling = column_width / dim.Width;
907                 if(xwise_tiling > 16) // Limit to 16 (more gives no benefit)
908                         xwise_tiling = 16;
909                 for(u32 j=0; j<xwise_tiling; j++)
910                 {
911                         // Copy the copy to the atlas
912                         /*img2->copyToWithAlpha(atlas_img,
913                                         pos_in_atlas + v2s32(j*dim.Width,0),
914                                         core::rect<s32>(v2s32(0,0), dim),
915                                         video::SColor(255,255,255,255),
916                                         NULL);*/
917                         img2->copyTo(atlas_img,
918                                         pos_in_atlas + v2s32(j*dim.Width,0),
919                                         core::rect<s32>(v2s32(0,0), dim),
920                                         NULL);
921                 }
922
923                 // Copy the borders a few times to disallow texture bleeding
924                 for(u32 side=0; side<2; side++) // top and bottom
925                 for(s32 y0=0; y0<padding; y0++)
926                 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
927                 {
928                         s32 dst_y;
929                         s32 src_y;
930                         if(side==0)
931                         {
932                                 dst_y = y0 + pos_in_atlas.Y + dim.Height;
933                                 src_y = pos_in_atlas.Y + dim.Height - 1;
934                         }
935                         else
936                         {
937                                 dst_y = -y0 + pos_in_atlas.Y-1;
938                                 src_y = pos_in_atlas.Y;
939                         }
940                         s32 x = x0 + pos_in_atlas.X;
941                         video::SColor c = atlas_img->getPixel(x, src_y);
942                         atlas_img->setPixel(x,dst_y,c);
943                 }
944
945                 img2->drop();
946
947                 /*
948                         Add texture to caches
949                 */
950                 
951                 bool reuse_old_id = false;
952                 u32 id = m_atlaspointer_cache.size();
953                 // Check old id without fetching a texture
954                 core::map<std::string, u32>::Node *n;
955                 n = m_name_to_id.find(name);
956                 // If it exists, we will replace the old definition
957                 if(n){
958                         id = n->getValue();
959                         reuse_old_id = true;
960                         /*infostream<<"TextureSource::buildMainAtlas(): "
961                                         <<"Replacing old AtlasPointer"<<std::endl;*/
962                 }
963
964                 // Create AtlasPointer
965                 AtlasPointer ap(id);
966                 ap.atlas = NULL; // Set on the second pass
967                 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
968                                 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
969                 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
970                                 (float)dim.Width/(float)atlas_dim.Height);
971                 ap.tiled = xwise_tiling;
972
973                 // Create SourceAtlasPointer and add to containers
974                 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
975                 if(reuse_old_id)
976                         m_atlaspointer_cache[id] = nap;
977                 else
978                         m_atlaspointer_cache.push_back(nap);
979                 m_name_to_id[name] = id;
980                         
981                 // Increment position
982                 pos_in_atlas.Y += dim.Height + padding * 2;
983         }
984
985         /*
986                 Make texture
987         */
988         video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
989         assert(t);
990
991         /*
992                 Second pass: set texture pointer in generated AtlasPointers
993         */
994         for(core::map<std::string, bool>::Iterator
995                         i = sourcelist.getIterator();
996                         i.atEnd() == false; i++)
997         {
998                 std::string name = i.getNode()->getKey();
999                 if(m_name_to_id.find(name) == NULL)
1000                         continue;
1001                 u32 id = m_name_to_id[name];
1002                 //infostream<<"id of name "<<name<<" is "<<id<<std::endl;
1003                 m_atlaspointer_cache[id].a.atlas = t;
1004         }
1005
1006         /*
1007                 Write image to file so that it can be inspected
1008         */
1009         /*std::string atlaspath = porting::path_userdata
1010                         + DIR_DELIM + "generated_texture_atlas.png";
1011         infostream<<"Removing and writing texture atlas for inspection to "
1012                         <<atlaspath<<std::endl;
1013         fs::RecursiveDelete(atlaspath);
1014         driver->writeImageToFile(atlas_img, atlaspath.c_str());*/
1015 }
1016
1017 video::IImage* generate_image_from_scratch(std::string name,
1018                 IrrlichtDevice *device, SourceImageCache *sourcecache)
1019 {
1020         /*infostream<<"generate_image_from_scratch(): "
1021                         "\""<<name<<"\""<<std::endl;*/
1022         
1023         video::IVideoDriver* driver = device->getVideoDriver();
1024         assert(driver);
1025
1026         /*
1027                 Get the base image
1028         */
1029
1030         video::IImage *baseimg = NULL;
1031
1032         char separator = '^';
1033
1034         // Find last meta separator in name
1035         s32 last_separator_position = -1;
1036         for(s32 i=name.size()-1; i>=0; i--)
1037         {
1038                 if(name[i] == separator)
1039                 {
1040                         last_separator_position = i;
1041                         break;
1042                 }
1043         }
1044
1045         /*infostream<<"generate_image_from_scratch(): "
1046                         <<"last_separator_position="<<last_separator_position
1047                         <<std::endl;*/
1048
1049         /*
1050                 If separator was found, construct the base name and make the
1051                 base image using a recursive call
1052         */
1053         std::string base_image_name;
1054         if(last_separator_position != -1)
1055         {
1056                 // Construct base name
1057                 base_image_name = name.substr(0, last_separator_position);
1058                 /*infostream<<"generate_image_from_scratch(): Calling itself recursively"
1059                                 " to get base image of \""<<name<<"\" = \""
1060                 <<base_image_name<<"\""<<std::endl;*/
1061                 baseimg = generate_image_from_scratch(base_image_name, device,
1062                                 sourcecache);
1063         }
1064         
1065         /*
1066                 Parse out the last part of the name of the image and act
1067                 according to it
1068         */
1069
1070         std::string last_part_of_name = name.substr(last_separator_position+1);
1071         //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
1072         
1073         // Generate image according to part of name
1074         if(!generate_image(last_part_of_name, baseimg, device, sourcecache))
1075         {
1076                 errorstream<<"generate_image_from_scratch(): "
1077                                 "failed to generate \""<<last_part_of_name<<"\""
1078                                 <<std::endl;
1079                 return NULL;
1080         }
1081         
1082         return baseimg;
1083 }
1084
1085 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
1086                 IrrlichtDevice *device, SourceImageCache *sourcecache)
1087 {
1088         video::IVideoDriver* driver = device->getVideoDriver();
1089         assert(driver);
1090
1091         // Stuff starting with [ are special commands
1092         if(part_of_name.size() == 0 || part_of_name[0] != '[')
1093         {
1094                 video::IImage *image = sourcecache->getOrLoad(part_of_name, device);
1095
1096                 if(image == NULL)
1097                 {
1098                         if(part_of_name != ""){
1099                                 errorstream<<"generate_image(): Could not load image \""
1100                                                 <<part_of_name<<"\""<<" while building texture"<<std::endl;
1101                                 errorstream<<"generate_image(): Creating a dummy"
1102                                                 <<" image for \""<<part_of_name<<"\""<<std::endl;
1103                         }
1104
1105                         // Just create a dummy image
1106                         //core::dimension2d<u32> dim(2,2);
1107                         core::dimension2d<u32> dim(1,1);
1108                         image = driver->createImage(video::ECF_A8R8G8B8, dim);
1109                         assert(image);
1110                         /*image->setPixel(0,0, video::SColor(255,255,0,0));
1111                         image->setPixel(1,0, video::SColor(255,0,255,0));
1112                         image->setPixel(0,1, video::SColor(255,0,0,255));
1113                         image->setPixel(1,1, video::SColor(255,255,0,255));*/
1114                         image->setPixel(0,0, video::SColor(255,myrand()%256,
1115                                         myrand()%256,myrand()%256));
1116                         /*image->setPixel(1,0, video::SColor(255,myrand()%256,
1117                                         myrand()%256,myrand()%256));
1118                         image->setPixel(0,1, video::SColor(255,myrand()%256,
1119                                         myrand()%256,myrand()%256));
1120                         image->setPixel(1,1, video::SColor(255,myrand()%256,
1121                                         myrand()%256,myrand()%256));*/
1122                 }
1123
1124                 // If base image is NULL, load as base.
1125                 if(baseimg == NULL)
1126                 {
1127                         //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
1128                         /*
1129                                 Copy it this way to get an alpha channel.
1130                                 Otherwise images with alpha cannot be blitted on 
1131                                 images that don't have alpha in the original file.
1132                         */
1133                         core::dimension2d<u32> dim = image->getDimension();
1134                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1135                         image->copyTo(baseimg);
1136                         image->drop();
1137                 }
1138                 // Else blit on base.
1139                 else
1140                 {
1141                         //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
1142                         // Size of the copied area
1143                         core::dimension2d<u32> dim = image->getDimension();
1144                         //core::dimension2d<u32> dim(16,16);
1145                         // Position to copy the blitted to in the base image
1146                         core::position2d<s32> pos_to(0,0);
1147                         // Position to copy the blitted from in the blitted image
1148                         core::position2d<s32> pos_from(0,0);
1149                         // Blit
1150                         image->copyToWithAlpha(baseimg, pos_to,
1151                                         core::rect<s32>(pos_from, dim),
1152                                         video::SColor(255,255,255,255),
1153                                         NULL);
1154                         // Drop image
1155                         image->drop();
1156                 }
1157         }
1158         else
1159         {
1160                 // A special texture modification
1161
1162                 /*infostream<<"generate_image(): generating special "
1163                                 <<"modification \""<<part_of_name<<"\""
1164                                 <<std::endl;*/
1165                 
1166                 /*
1167                         This is the simplest of all; it just adds stuff to the
1168                         name so that a separate texture is created.
1169
1170                         It is used to make textures for stuff that doesn't want
1171                         to implement getting the texture from a bigger texture
1172                         atlas.
1173                 */
1174                 if(part_of_name == "[forcesingle")
1175                 {
1176                         // If base image is NULL, create a random color
1177                         if(baseimg == NULL)
1178                         {
1179                                 core::dimension2d<u32> dim(1,1);
1180                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1181                                 assert(baseimg);
1182                                 baseimg->setPixel(0,0, video::SColor(255,myrand()%256,
1183                                                 myrand()%256,myrand()%256));
1184                         }
1185                 }
1186                 /*
1187                         [crackN
1188                         Adds a cracking texture
1189                 */
1190                 else if(part_of_name.substr(0,6) == "[crack")
1191                 {
1192                         if(baseimg == NULL)
1193                         {
1194                                 errorstream<<"generate_image(): baseimg==NULL "
1195                                                 <<"for part_of_name=\""<<part_of_name
1196                                                 <<"\", cancelling."<<std::endl;
1197                                 return false;
1198                         }
1199                         
1200                         // Crack image number
1201                         u16 progression = stoi(part_of_name.substr(6));
1202
1203                         // Size of the base image
1204                         core::dimension2d<u32> dim_base = baseimg->getDimension();
1205                         
1206                         /*
1207                                 Load crack image.
1208
1209                                 It is an image with a number of cracking stages
1210                                 horizontally tiled.
1211                         */
1212                         video::IImage *img_crack = sourcecache->getOrLoad("crack.png", device);
1213                 
1214                         if(img_crack)
1215                         {
1216                                 // Dimension of original image
1217                                 core::dimension2d<u32> dim_crack
1218                                                 = img_crack->getDimension();
1219                                 // Count of crack stages
1220                                 u32 crack_count = dim_crack.Height / dim_crack.Width;
1221                                 // Limit progression
1222                                 if(progression > crack_count-1)
1223                                         progression = crack_count-1;
1224                                 // Dimension of a single scaled crack stage
1225                                 core::dimension2d<u32> dim_crack_scaled_single(
1226                                         dim_base.Width,
1227                                         dim_base.Height
1228                                 );
1229                                 // Dimension of scaled size
1230                                 core::dimension2d<u32> dim_crack_scaled(
1231                                         dim_crack_scaled_single.Width,
1232                                         dim_crack_scaled_single.Height * crack_count
1233                                 );
1234                                 // Create scaled crack image
1235                                 video::IImage *img_crack_scaled = driver->createImage(
1236                                                 video::ECF_A8R8G8B8, dim_crack_scaled);
1237                                 if(img_crack_scaled)
1238                                 {
1239                                         // Scale crack image by copying
1240                                         img_crack->copyToScaling(img_crack_scaled);
1241                                         
1242                                         // Position to copy the crack from
1243                                         core::position2d<s32> pos_crack_scaled(
1244                                                 0,
1245                                                 dim_crack_scaled_single.Height * progression
1246                                         );
1247                                         
1248                                         // This tiling does nothing currently but is useful
1249                                         for(u32 y0=0; y0<dim_base.Height
1250                                                         / dim_crack_scaled_single.Height; y0++)
1251                                         for(u32 x0=0; x0<dim_base.Width
1252                                                         / dim_crack_scaled_single.Width; x0++)
1253                                         {
1254                                                 // Position to copy the crack to in the base image
1255                                                 core::position2d<s32> pos_base(
1256                                                         x0*dim_crack_scaled_single.Width,
1257                                                         y0*dim_crack_scaled_single.Height
1258                                                 );
1259                                                 // Rectangle to copy the crack from on the scaled image
1260                                                 core::rect<s32> rect_crack_scaled(
1261                                                         pos_crack_scaled,
1262                                                         dim_crack_scaled_single
1263                                                 );
1264                                                 // Copy it
1265                                                 img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
1266                                                                 rect_crack_scaled,
1267                                                                 video::SColor(255,255,255,255),
1268                                                                 NULL);
1269                                         }
1270
1271                                         img_crack_scaled->drop();
1272                                 }
1273                                 
1274                                 img_crack->drop();
1275                         }
1276                 }
1277                 /*
1278                         [combine:WxH:X,Y=filename:X,Y=filename2
1279                         Creates a bigger texture from an amount of smaller ones
1280                 */
1281                 else if(part_of_name.substr(0,8) == "[combine")
1282                 {
1283                         Strfnd sf(part_of_name);
1284                         sf.next(":");
1285                         u32 w0 = stoi(sf.next("x"));
1286                         u32 h0 = stoi(sf.next(":"));
1287                         infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
1288                         core::dimension2d<u32> dim(w0,h0);
1289                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1290                         while(sf.atend() == false)
1291                         {
1292                                 u32 x = stoi(sf.next(","));
1293                                 u32 y = stoi(sf.next("="));
1294                                 std::string filename = sf.next(":");
1295                                 infostream<<"Adding \""<<filename
1296                                                 <<"\" to combined ("<<x<<","<<y<<")"
1297                                                 <<std::endl;
1298                                 video::IImage *img = sourcecache->getOrLoad(filename, device);
1299                                 if(img)
1300                                 {
1301                                         core::dimension2d<u32> dim = img->getDimension();
1302                                         infostream<<"Size "<<dim.Width
1303                                                         <<"x"<<dim.Height<<std::endl;
1304                                         core::position2d<s32> pos_base(x, y);
1305                                         video::IImage *img2 =
1306                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
1307                                         img->copyTo(img2);
1308                                         img->drop();
1309                                         img2->copyToWithAlpha(baseimg, pos_base,
1310                                                         core::rect<s32>(v2s32(0,0), dim),
1311                                                         video::SColor(255,255,255,255),
1312                                                         NULL);
1313                                         img2->drop();
1314                                 }
1315                                 else
1316                                 {
1317                                         infostream<<"img==NULL"<<std::endl;
1318                                 }
1319                         }
1320                 }
1321                 /*
1322                         "[brighten"
1323                 */
1324                 else if(part_of_name.substr(0,9) == "[brighten")
1325                 {
1326                         if(baseimg == NULL)
1327                         {
1328                                 errorstream<<"generate_image(): baseimg==NULL "
1329                                                 <<"for part_of_name=\""<<part_of_name
1330                                                 <<"\", cancelling."<<std::endl;
1331                                 return false;
1332                         }
1333
1334                         brighten(baseimg);
1335                 }
1336                 /*
1337                         "[noalpha"
1338                         Make image completely opaque.
1339                         Used for the leaves texture when in old leaves mode, so
1340                         that the transparent parts don't look completely black 
1341                         when simple alpha channel is used for rendering.
1342                 */
1343                 else if(part_of_name.substr(0,8) == "[noalpha")
1344                 {
1345                         if(baseimg == NULL)
1346                         {
1347                                 errorstream<<"generate_image(): baseimg==NULL "
1348                                                 <<"for part_of_name=\""<<part_of_name
1349                                                 <<"\", cancelling."<<std::endl;
1350                                 return false;
1351                         }
1352
1353                         core::dimension2d<u32> dim = baseimg->getDimension();
1354                         
1355                         // Set alpha to full
1356                         for(u32 y=0; y<dim.Height; y++)
1357                         for(u32 x=0; x<dim.Width; x++)
1358                         {
1359                                 video::SColor c = baseimg->getPixel(x,y);
1360                                 c.setAlpha(255);
1361                                 baseimg->setPixel(x,y,c);
1362                         }
1363                 }
1364                 /*
1365                         "[makealpha:R,G,B"
1366                         Convert one color to transparent.
1367                 */
1368                 else if(part_of_name.substr(0,11) == "[makealpha:")
1369                 {
1370                         if(baseimg == NULL)
1371                         {
1372                                 errorstream<<"generate_image(): baseimg==NULL "
1373                                                 <<"for part_of_name=\""<<part_of_name
1374                                                 <<"\", cancelling."<<std::endl;
1375                                 return false;
1376                         }
1377
1378                         Strfnd sf(part_of_name.substr(11));
1379                         u32 r1 = stoi(sf.next(","));
1380                         u32 g1 = stoi(sf.next(","));
1381                         u32 b1 = stoi(sf.next(""));
1382                         std::string filename = sf.next("");
1383
1384                         core::dimension2d<u32> dim = baseimg->getDimension();
1385                         
1386                         /*video::IImage *oldbaseimg = baseimg;
1387                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1388                         oldbaseimg->copyTo(baseimg);
1389                         oldbaseimg->drop();*/
1390
1391                         // Set alpha to full
1392                         for(u32 y=0; y<dim.Height; y++)
1393                         for(u32 x=0; x<dim.Width; x++)
1394                         {
1395                                 video::SColor c = baseimg->getPixel(x,y);
1396                                 u32 r = c.getRed();
1397                                 u32 g = c.getGreen();
1398                                 u32 b = c.getBlue();
1399                                 if(!(r == r1 && g == g1 && b == b1))
1400                                         continue;
1401                                 c.setAlpha(0);
1402                                 baseimg->setPixel(x,y,c);
1403                         }
1404                 }
1405                 /*
1406                         [inventorycube{topimage{leftimage{rightimage
1407                         In every subimage, replace ^ with &.
1408                         Create an "inventory cube".
1409                         NOTE: This should be used only on its own.
1410                         Example (a grass block (not actually used in game):
1411                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1412                 */
1413                 else if(part_of_name.substr(0,14) == "[inventorycube")
1414                 {
1415                         if(baseimg != NULL)
1416                         {
1417                                 errorstream<<"generate_image(): baseimg!=NULL "
1418                                                 <<"for part_of_name=\""<<part_of_name
1419                                                 <<"\", cancelling."<<std::endl;
1420                                 return false;
1421                         }
1422
1423                         str_replace_char(part_of_name, '&', '^');
1424                         Strfnd sf(part_of_name);
1425                         sf.next("{");
1426                         std::string imagename_top = sf.next("{");
1427                         std::string imagename_left = sf.next("{");
1428                         std::string imagename_right = sf.next("{");
1429
1430                         // Generate images for the faces of the cube
1431                         video::IImage *img_top = generate_image_from_scratch(
1432                                         imagename_top, device, sourcecache);
1433                         video::IImage *img_left = generate_image_from_scratch(
1434                                         imagename_left, device, sourcecache);
1435                         video::IImage *img_right = generate_image_from_scratch(
1436                                         imagename_right, device, sourcecache);
1437                         assert(img_top && img_left && img_right);
1438
1439                         // Create textures from images
1440                         video::ITexture *texture_top = driver->addTexture(
1441                                         (imagename_top + "__temp__").c_str(), img_top);
1442                         video::ITexture *texture_left = driver->addTexture(
1443                                         (imagename_left + "__temp__").c_str(), img_left);
1444                         video::ITexture *texture_right = driver->addTexture(
1445                                         (imagename_right + "__temp__").c_str(), img_right);
1446                         assert(texture_top && texture_left && texture_right);
1447
1448                         // Drop images
1449                         img_top->drop();
1450                         img_left->drop();
1451                         img_right->drop();
1452                         
1453                         /*
1454                                 Draw a cube mesh into a render target texture
1455                         */
1456                         scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
1457                         setMeshColor(cube, video::SColor(255, 255, 255, 255));
1458                         cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
1459                         cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
1460                         cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
1461                         cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
1462                         cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
1463                         cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
1464
1465                         core::dimension2d<u32> dim(64,64);
1466                         std::string rtt_texture_name = part_of_name + "_RTT";
1467
1468                         v3f camera_position(0, 1.0, -1.5);
1469                         camera_position.rotateXZBy(45);
1470                         v3f camera_lookat(0, 0, 0);
1471                         core::CMatrix4<f32> camera_projection_matrix;
1472                         // Set orthogonal projection
1473                         camera_projection_matrix.buildProjectionMatrixOrthoLH(
1474                                         1.65, 1.65, 0, 100);
1475
1476                         video::SColorf ambient_light(0.2,0.2,0.2);
1477                         v3f light_position(10, 100, -50);
1478                         video::SColorf light_color(0.5,0.5,0.5);
1479                         f32 light_radius = 1000;
1480
1481                         video::ITexture *rtt = generateTextureFromMesh(
1482                                         cube, device, dim, rtt_texture_name,
1483                                         camera_position,
1484                                         camera_lookat,
1485                                         camera_projection_matrix,
1486                                         ambient_light,
1487                                         light_position,
1488                                         light_color,
1489                                         light_radius);
1490                         
1491                         // Drop mesh
1492                         cube->drop();
1493
1494                         // Free textures of images
1495                         driver->removeTexture(texture_top);
1496                         driver->removeTexture(texture_left);
1497                         driver->removeTexture(texture_right);
1498                         
1499                         if(rtt == NULL)
1500                         {
1501                                 errorstream<<"generate_image(): render to texture failed."
1502                                                 " Creating fallback image"<<std::endl;
1503                                 baseimg = generate_image_from_scratch(
1504                                                 imagename_top, device, sourcecache);
1505                                 return true;
1506                         }
1507
1508                         // Create image of render target
1509                         video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1510                         assert(image);
1511
1512                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1513
1514                         if(image)
1515                         {
1516                                 image->copyTo(baseimg);
1517                                 image->drop();
1518                         }
1519                 }
1520                 else
1521                 {
1522                         errorstream<<"generate_image(): Invalid "
1523                                         " modification: \""<<part_of_name<<"\""<<std::endl;
1524                 }
1525         }
1526
1527         return true;
1528 }
1529
1530 void brighten(video::IImage *image)
1531 {
1532         if(image == NULL)
1533                 return;
1534         
1535         core::dimension2d<u32> dim = image->getDimension();
1536
1537         for(u32 y=0; y<dim.Height; y++)
1538         for(u32 x=0; x<dim.Width; x++)
1539         {
1540                 video::SColor c = image->getPixel(x,y);
1541                 c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
1542                 c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
1543                 c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
1544                 image->setPixel(x,y,c);
1545         }
1546 }
1547