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