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