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