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