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