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