]> git.lizzy.rs Git - minetest.git/blob - src/tile.cpp
e65e92b7e43601409f5ae89e00ed2572605b859c
[minetest.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("mud.png^grass_side.png");
480         sourcelist.push_back("cobble.png");
481         
482         sourcelist.push_back("stone.png^mineral_coal.png");
483         sourcelist.push_back("stone.png^mineral_iron.png");
484         sourcelist.push_back("mud.png^mineral_coal.png");
485         sourcelist.push_back("mud.png^mineral_iron.png");
486         sourcelist.push_back("sand.png^mineral_coal.png");
487         sourcelist.push_back("sand.png^mineral_iron.png");
488         
489         // Padding to disallow texture bleeding
490         s32 padding = 16;
491
492         /*
493                 First pass: generate almost everything
494         */
495         core::position2d<s32> pos_in_atlas(0,0);
496         
497         pos_in_atlas.Y += padding;
498
499         for(u32 i=0; i<sourcelist.size(); i++)
500         {
501                 std::string name = sourcelist[i];
502
503                 /*video::IImage *img = driver->createImageFromFile(
504                                 getTexturePath(name.c_str()).c_str());
505                 if(img == NULL)
506                         continue;
507                 
508                 core::dimension2d<u32> dim = img->getDimension();
509                 // Make a copy with the right color format
510                 video::IImage *img2 =
511                                 driver->createImage(video::ECF_A8R8G8B8, dim);
512                 img->copyTo(img2);
513                 img->drop();*/
514                 
515                 // Generate image by name
516                 video::IImage *img2 = generate_image_from_scratch(name, m_device);
517                 if(img2 == NULL)
518                 {
519                         dstream<<"WARNING: TextureSource::buildMainAtlas(): Couldn't generate texture atlas: Couldn't generate image \""<<name<<"\""<<std::endl;
520                         continue;
521                 }
522
523                 core::dimension2d<u32> dim = img2->getDimension();
524
525                 // Don't add to atlas if image is large
526                 core::dimension2d<u32> max_size_in_atlas(32,32);
527                 if(dim.Width > max_size_in_atlas.Width
528                 || dim.Height > max_size_in_atlas.Height)
529                 {
530                         dstream<<"INFO: TextureSource::buildMainAtlas(): Not adding "
531                                         <<"\""<<name<<"\" because image is large"<<std::endl;
532                         continue;
533                 }
534
535                 // Stop making atlas if atlas is full
536                 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
537                 {
538                         dstream<<"WARNING: TextureSource::buildMainAtlas(): "
539                                         <<"Atlas is full, not adding more textures."
540                                         <<std::endl;
541                         break;
542                 }
543                 
544                 // Tile it a few times in the X direction
545                 u16 xwise_tiling = 16;
546                 for(u32 j=0; j<xwise_tiling; j++)
547                 {
548                         // Copy the copy to the atlas
549                         img2->copyToWithAlpha(atlas_img,
550                                         pos_in_atlas + v2s32(j*dim.Width,0),
551                                         core::rect<s32>(v2s32(0,0), dim),
552                                         video::SColor(255,255,255,255),
553                                         NULL);
554                 }
555
556                 // Copy the borders a few times to disallow texture bleeding
557                 for(u32 side=0; side<2; side++) // top and bottom
558                 for(s32 y0=0; y0<padding; y0++)
559                 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
560                 {
561                         s32 dst_y;
562                         s32 src_y;
563                         if(side==0)
564                         {
565                                 dst_y = y0 + pos_in_atlas.Y + dim.Height;
566                                 src_y = pos_in_atlas.Y + dim.Height - 1;
567                         }
568                         else
569                         {
570                                 dst_y = -y0 + pos_in_atlas.Y-1;
571                                 src_y = pos_in_atlas.Y;
572                         }
573                         s32 x = x0 + pos_in_atlas.X * dim.Width;
574                         video::SColor c = atlas_img->getPixel(x, src_y);
575                         atlas_img->setPixel(x,dst_y,c);
576                 }
577
578                 img2->drop();
579
580                 /*
581                         Add texture to caches
582                 */
583                 
584                 // Get next id
585                 u32 id = m_atlaspointer_cache.size();
586
587                 // Create AtlasPointer
588                 AtlasPointer ap(id);
589                 ap.atlas = NULL; // Set on the second pass
590                 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
591                                 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
592                 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
593                                 (float)dim.Width/(float)atlas_dim.Height);
594                 ap.tiled = xwise_tiling;
595
596                 // Create SourceAtlasPointer and add to containers
597                 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
598                 m_atlaspointer_cache.push_back(nap);
599                 m_name_to_id.insert(name, id);
600                         
601                 // Increment position
602                 pos_in_atlas.Y += dim.Height + padding * 2;
603         }
604
605         /*
606                 Make texture
607         */
608         video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
609         assert(t);
610
611         /*
612                 Second pass: set texture pointer in generated AtlasPointers
613         */
614         for(u32 i=0; i<sourcelist.size(); i++)
615         {
616                 std::string name = sourcelist[i];
617                 if(m_name_to_id.find(name) == NULL)
618                         continue;
619                 u32 id = m_name_to_id[name];
620                 //dstream<<"id of name "<<name<<" is "<<id<<std::endl;
621                 m_atlaspointer_cache[id].a.atlas = t;
622         }
623
624         /*
625                 Write image to file so that it can be inspected
626         */
627         /*driver->writeImageToFile(atlas_img, 
628                         getTexturePath("main_atlas.png").c_str());*/
629 }
630
631 video::IImage* generate_image_from_scratch(std::string name,
632                 IrrlichtDevice *device)
633 {
634         dstream<<"INFO: generate_image_from_scratch(): "
635                         "name="<<name<<std::endl;
636         
637         video::IVideoDriver* driver = device->getVideoDriver();
638         assert(driver);
639
640         /*
641                 Get the base image
642         */
643
644         video::IImage *baseimg = NULL;
645
646         char separator = '^';
647
648         // Find last meta separator in name
649         s32 last_separator_position = -1;
650         for(s32 i=name.size()-1; i>=0; i--)
651         {
652                 if(name[i] == separator)
653                 {
654                         last_separator_position = i;
655                         break;
656                 }
657         }
658
659         /*dstream<<"INFO: generate_image_from_scratch(): "
660                         <<"last_separator_position="<<last_separator_position
661                         <<std::endl;*/
662
663         /*
664                 If separator was found, construct the base name and make the
665                 base image using a recursive call
666         */
667         std::string base_image_name;
668         if(last_separator_position != -1)
669         {
670                 // Construct base name
671                 base_image_name = name.substr(0, last_separator_position);
672                 dstream<<"INFO: generate_image_from_scratch(): Calling itself recursively"
673                                 " to get base image, name="<<base_image_name<<std::endl;
674                 baseimg = generate_image_from_scratch(base_image_name, device);
675         }
676         
677         /*
678                 Parse out the last part of the name of the image and act
679                 according to it
680         */
681
682         std::string last_part_of_name = name.substr(last_separator_position+1);
683         dstream<<"last_part_of_name="<<last_part_of_name<<std::endl;
684         
685         // Generate image according to part of name
686         if(generate_image(last_part_of_name, baseimg, device) == false)
687         {
688                 dstream<<"INFO: generate_image_from_scratch(): "
689                                 "failed to generate \""<<last_part_of_name<<"\""
690                                 <<std::endl;
691                 return NULL;
692         }
693         
694         return baseimg;
695 }
696
697 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
698                 IrrlichtDevice *device)
699 {
700         video::IVideoDriver* driver = device->getVideoDriver();
701         assert(driver);
702
703         // Stuff starting with [ are special commands
704         if(part_of_name[0] != '[')
705         {
706                 // A normal texture; load it from a file
707                 std::string path = getTexturePath(part_of_name.c_str());
708                 dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
709                                 <<"\""<<std::endl;
710                 
711                 video::IImage *image = driver->createImageFromFile(path.c_str());
712
713                 if(image == NULL)
714                 {
715                         dstream<<"WARNING: Could not load image \""<<part_of_name
716                                         <<"\" from path \""<<path<<"\""
717                                         <<" while building texture"<<std::endl;
718
719                         //return false;
720
721                         dstream<<"WARNING: Creating a dummy"<<" image for \""
722                                         <<part_of_name<<"\""<<std::endl;
723
724                         // Just create a dummy image
725                         //core::dimension2d<u32> dim(2,2);
726                         core::dimension2d<u32> dim(1,1);
727                         image = driver->createImage(video::ECF_A8R8G8B8, dim);
728                         assert(image);
729                         /*image->setPixel(0,0, video::SColor(255,255,0,0));
730                         image->setPixel(1,0, video::SColor(255,0,255,0));
731                         image->setPixel(0,1, video::SColor(255,0,0,255));
732                         image->setPixel(1,1, video::SColor(255,255,0,255));*/
733                         image->setPixel(0,0, video::SColor(255,myrand()%256,
734                                         myrand()%256,myrand()%256));
735                         /*image->setPixel(1,0, video::SColor(255,myrand()%256,
736                                         myrand()%256,myrand()%256));
737                         image->setPixel(0,1, video::SColor(255,myrand()%256,
738                                         myrand()%256,myrand()%256));
739                         image->setPixel(1,1, video::SColor(255,myrand()%256,
740                                         myrand()%256,myrand()%256));*/
741                 }
742
743                 // If base image is NULL, load as base.
744                 if(baseimg == NULL)
745                 {
746                         dstream<<"INFO: Setting "<<part_of_name<<" as base"<<std::endl;
747                         /*
748                                 Copy it this way to get an alpha channel.
749                                 Otherwise images with alpha cannot be blitted on 
750                                 images that don't have alpha in the original file.
751                         */
752                         core::dimension2d<u32> dim = image->getDimension();
753                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
754                         image->copyTo(baseimg);
755                         image->drop();
756                 }
757                 // Else blit on base.
758                 else
759                 {
760                         dstream<<"INFO: Blitting "<<part_of_name<<" on base"<<std::endl;
761                         // Size of the copied area
762                         core::dimension2d<u32> dim = image->getDimension();
763                         //core::dimension2d<u32> dim(16,16);
764                         // Position to copy the blitted to in the base image
765                         core::position2d<s32> pos_to(0,0);
766                         // Position to copy the blitted from in the blitted image
767                         core::position2d<s32> pos_from(0,0);
768                         // Blit
769                         image->copyToWithAlpha(baseimg, pos_to,
770                                         core::rect<s32>(pos_from, dim),
771                                         video::SColor(255,255,255,255),
772                                         NULL);
773                         // Drop image
774                         image->drop();
775                 }
776         }
777         else
778         {
779                 // A special texture modification
780
781                 dstream<<"INFO: getTextureIdDirect(): generating special "
782                                 <<"modification \""<<part_of_name<<"\""
783                                 <<std::endl;
784                 
785                 /*
786                         This is the simplest of all; it just adds stuff to the
787                         name so that a separate texture is created.
788
789                         It is used to make textures for stuff that doesn't want
790                         to implement getting the texture from a bigger texture
791                         atlas.
792                 */
793                 if(part_of_name == "[forcesingle")
794                 {
795                 }
796                 /*
797                         [crackN
798                         Adds a cracking texture
799                 */
800                 else if(part_of_name.substr(0,6) == "[crack")
801                 {
802                         if(baseimg == NULL)
803                         {
804                                 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
805                                                 <<"for part_of_name="<<part_of_name
806                                                 <<", cancelling."<<std::endl;
807                                 return false;
808                         }
809                         
810                         // Crack image number
811                         u16 progression = stoi(part_of_name.substr(6));
812
813                         // Size of the base image
814                         core::dimension2d<u32> dim_base = baseimg->getDimension();
815                         
816                         /*
817                                 Load crack image.
818
819                                 It is an image with a number of cracking stages
820                                 horizontally tiled.
821                         */
822                         video::IImage *img_crack = driver->createImageFromFile(
823                                         getTexturePath("crack.png").c_str());
824                 
825                         if(img_crack)
826                         {
827                                 // Dimension of original image
828                                 core::dimension2d<u32> dim_crack
829                                                 = img_crack->getDimension();
830                                 // Count of crack stages
831                                 u32 crack_count = dim_crack.Height / dim_crack.Width;
832                                 // Limit progression
833                                 if(progression > crack_count-1)
834                                         progression = crack_count-1;
835                                 // Dimension of a single scaled crack stage
836                                 core::dimension2d<u32> dim_crack_scaled_single(
837                                         dim_base.Width,
838                                         dim_base.Height
839                                 );
840                                 // Dimension of scaled size
841                                 core::dimension2d<u32> dim_crack_scaled(
842                                         dim_crack_scaled_single.Width,
843                                         dim_crack_scaled_single.Height * crack_count
844                                 );
845                                 // Create scaled crack image
846                                 video::IImage *img_crack_scaled = driver->createImage(
847                                                 video::ECF_A8R8G8B8, dim_crack_scaled);
848                                 if(img_crack_scaled)
849                                 {
850                                         // Scale crack image by copying
851                                         img_crack->copyToScaling(img_crack_scaled);
852                                         
853                                         // Position to copy the crack from
854                                         core::position2d<s32> pos_crack_scaled(
855                                                 0,
856                                                 dim_crack_scaled_single.Height * progression
857                                         );
858                                         
859                                         // This tiling does nothing currently but is useful
860                                         for(u32 y0=0; y0<dim_base.Height
861                                                         / dim_crack_scaled_single.Height; y0++)
862                                         for(u32 x0=0; x0<dim_base.Width
863                                                         / dim_crack_scaled_single.Width; x0++)
864                                         {
865                                                 // Position to copy the crack to in the base image
866                                                 core::position2d<s32> pos_base(
867                                                         x0*dim_crack_scaled_single.Width,
868                                                         y0*dim_crack_scaled_single.Height
869                                                 );
870                                                 // Rectangle to copy the crack from on the scaled image
871                                                 core::rect<s32> rect_crack_scaled(
872                                                         pos_crack_scaled,
873                                                         dim_crack_scaled_single
874                                                 );
875                                                 // Copy it
876                                                 img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
877                                                                 rect_crack_scaled,
878                                                                 video::SColor(255,255,255,255),
879                                                                 NULL);
880                                         }
881
882                                         img_crack_scaled->drop();
883                                 }
884                                 
885                                 img_crack->drop();
886                         }
887                 }
888                 /*
889                         [combine:WxH:X,Y=filename:X,Y=filename2
890                         Creates a bigger texture from an amount of smaller ones
891                 */
892                 else if(part_of_name.substr(0,8) == "[combine")
893                 {
894                         Strfnd sf(part_of_name);
895                         sf.next(":");
896                         u32 w0 = stoi(sf.next("x"));
897                         u32 h0 = stoi(sf.next(":"));
898                         dstream<<"INFO: combined w="<<w0<<" h="<<h0<<std::endl;
899                         core::dimension2d<u32> dim(w0,h0);
900                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
901                         while(sf.atend() == false)
902                         {
903                                 u32 x = stoi(sf.next(","));
904                                 u32 y = stoi(sf.next("="));
905                                 std::string filename = sf.next(":");
906                                 dstream<<"INFO: Adding \""<<filename
907                                                 <<"\" to combined ("<<x<<","<<y<<")"
908                                                 <<std::endl;
909                                 video::IImage *img = driver->createImageFromFile(
910                                                 getTexturePath(filename.c_str()).c_str());
911                                 if(img)
912                                 {
913                                         core::dimension2d<u32> dim = img->getDimension();
914                                         dstream<<"INFO: Size "<<dim.Width
915                                                         <<"x"<<dim.Height<<std::endl;
916                                         core::position2d<s32> pos_base(x, y);
917                                         video::IImage *img2 =
918                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
919                                         img->copyTo(img2);
920                                         img->drop();
921                                         img2->copyToWithAlpha(baseimg, pos_base,
922                                                         core::rect<s32>(v2s32(0,0), dim),
923                                                         video::SColor(255,255,255,255),
924                                                         NULL);
925                                         img2->drop();
926                                 }
927                                 else
928                                 {
929                                         dstream<<"WARNING: img==NULL"<<std::endl;
930                                 }
931                         }
932                 }
933                 /*
934                         [progressbarN
935                         Adds a progress bar, 0.0 <= N <= 1.0
936                 */
937                 else if(part_of_name.substr(0,12) == "[progressbar")
938                 {
939                         if(baseimg == NULL)
940                         {
941                                 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
942                                                 <<"for part_of_name="<<part_of_name
943                                                 <<", cancelling."<<std::endl;
944                                 return false;
945                         }
946
947                         float value = stof(part_of_name.substr(12));
948                         make_progressbar(value, baseimg);
949                 }
950                 /*
951                         "[noalpha:filename.png"
952                         Use an image without it's alpha channel.
953                         Used for the leaves texture when in old leaves mode, so
954                         that the transparent parts don't look completely black 
955                         when simple alpha channel is used for rendering.
956                 */
957                 else if(part_of_name.substr(0,8) == "[noalpha")
958                 {
959                         if(baseimg != NULL)
960                         {
961                                 dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
962                                                 <<"for part_of_name="<<part_of_name
963                                                 <<", cancelling."<<std::endl;
964                                 return false;
965                         }
966
967                         std::string filename = part_of_name.substr(9);
968
969                         std::string path = getTexturePath(filename.c_str());
970
971                         dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
972                                         <<"\""<<std::endl;
973                         
974                         video::IImage *image = driver->createImageFromFile(path.c_str());
975                         
976                         if(image == NULL)
977                         {
978                                 dstream<<"WARNING: getTextureIdDirect(): Loading path \""
979                                                 <<path<<"\" failed"<<std::endl;
980                         }
981                         else
982                         {
983                                 core::dimension2d<u32> dim = image->getDimension();
984                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
985                                 
986                                 // Set alpha to full
987                                 for(u32 y=0; y<dim.Height; y++)
988                                 for(u32 x=0; x<dim.Width; x++)
989                                 {
990                                         video::SColor c = image->getPixel(x,y);
991                                         c.setAlpha(255);
992                                         image->setPixel(x,y,c);
993                                 }
994                                 // Blit
995                                 image->copyTo(baseimg);
996
997                                 image->drop();
998                         }
999                 }
1000                 /*
1001                         [inventorycube{topimage{leftimage{rightimage
1002                         In every subimage, replace ^ with &.
1003                         Create an "inventory cube".
1004                         NOTE: This should be used only on its own.
1005                         Example (a grass block (not actually used in game):
1006                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1007                 */
1008                 else if(part_of_name.substr(0,14) == "[inventorycube")
1009                 {
1010                         if(baseimg != NULL)
1011                         {
1012                                 dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
1013                                                 <<"for part_of_name="<<part_of_name
1014                                                 <<", cancelling."<<std::endl;
1015                                 return false;
1016                         }
1017
1018                         str_replace_char(part_of_name, '&', '^');
1019                         Strfnd sf(part_of_name);
1020                         sf.next("{");
1021                         std::string imagename_top = sf.next("{");
1022                         std::string imagename_left = sf.next("{");
1023                         std::string imagename_right = sf.next("{");
1024
1025 #if 1
1026                         //TODO
1027
1028                         if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
1029                         {
1030                                 dstream<<"WARNING: getTextureIdDirect(): EVDF_RENDER_TO_TARGET"
1031                                                 " not supported. Creating fallback image"<<std::endl;
1032                                 baseimg = generate_image_from_scratch(
1033                                                 imagename_top, device);
1034                                 return true;
1035                         }
1036                         
1037                         u32 w0 = 64;
1038                         u32 h0 = 64;
1039                         dstream<<"INFO: inventorycube w="<<w0<<" h="<<h0<<std::endl;
1040                         core::dimension2d<u32> dim(w0,h0);
1041                         
1042                         // Generate images for the faces of the cube
1043                         video::IImage *img_top = generate_image_from_scratch(
1044                                         imagename_top, device);
1045                         video::IImage *img_left = generate_image_from_scratch(
1046                                         imagename_left, device);
1047                         video::IImage *img_right = generate_image_from_scratch(
1048                                         imagename_right, device);
1049                         assert(img_top && img_left && img_right);
1050
1051                         // TODO: Create textures from images
1052                         video::ITexture *texture_top = driver->addTexture(
1053                                         (imagename_top + "__temp__").c_str(), img_top);
1054                         assert(texture_top);
1055                         
1056                         // Drop images
1057                         img_top->drop();
1058                         img_left->drop();
1059                         img_right->drop();
1060                         
1061                         // Create render target texture
1062                         video::ITexture *rtt = NULL;
1063                         std::string rtt_name = part_of_name + "_RTT";
1064                         rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str(),
1065                                         video::ECF_A8R8G8B8);
1066                         assert(rtt);
1067                         
1068                         // Set render target
1069                         driver->setRenderTarget(rtt, true, true,
1070                                         video::SColor(0,0,0,0));
1071                         
1072                         // Get a scene manager
1073                         scene::ISceneManager *smgr_main = device->getSceneManager();
1074                         assert(smgr_main);
1075                         scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
1076                         assert(smgr);
1077                         
1078                         /*
1079                                 Create scene:
1080                                 - An unit cube is centered at 0,0,0
1081                                 - Camera looks at cube from Y+, Z- towards Y-, Z+
1082                                 NOTE: Cube has to be changed to something else because
1083                                 the textures cannot be set individually (or can they?)
1084                         */
1085
1086                         scene::ISceneNode* cube = smgr->addCubeSceneNode(1.0, NULL, -1,
1087                                         v3f(0,0,0), v3f(0, 45, 0));
1088                         // Set texture of cube
1089                         cube->setMaterialTexture(0, texture_top);
1090                         //cube->setMaterialFlag(video::EMF_LIGHTING, false);
1091                         cube->setMaterialFlag(video::EMF_ANTI_ALIASING, false);
1092                         cube->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1093
1094                         scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
1095                                         v3f(0, 1.0, -1.5), v3f(0, 0, 0));
1096                         // Set orthogonal projection
1097                         core::CMatrix4<f32> pm;
1098                         pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100);
1099                         camera->setProjectionMatrix(pm, true);
1100
1101                         /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0,
1102                                         v3f(-50, 100, 0), video::SColorf(0.5,0.5,0.5), 1000);
1103
1104                         smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2));
1105
1106                         // Render scene
1107                         driver->beginScene(true, true, video::SColor(0,0,0,0));
1108                         smgr->drawAll();
1109                         driver->endScene();
1110                         
1111                         // NOTE: The scene nodes should not be dropped, otherwise
1112                         //       smgr->drop() segfaults
1113                         /*cube->drop();
1114                         camera->drop();
1115                         light->drop();*/
1116                         // Drop scene manager
1117                         smgr->drop();
1118                         
1119                         // Unset render target
1120                         driver->setRenderTarget(0, true, true, 0);
1121
1122                         //TODO: Free textures of images
1123                         driver->removeTexture(texture_top);
1124                         
1125                         // Create image of render target
1126                         video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1127
1128                         assert(image);
1129                         
1130                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1131
1132                         if(image)
1133                         {
1134                                 image->copyTo(baseimg);
1135                                 image->drop();
1136                         }
1137 #endif
1138                 }
1139                 else
1140                 {
1141                         dstream<<"WARNING: getTextureIdDirect(): Invalid "
1142                                         " modification: \""<<part_of_name<<"\""<<std::endl;
1143                 }
1144         }
1145
1146         return true;
1147 }
1148
1149 void make_progressbar(float value, video::IImage *image)
1150 {
1151         if(image == NULL)
1152                 return;
1153         
1154         core::dimension2d<u32> size = image->getDimension();
1155
1156         u32 barheight = size.Height/16;
1157         u32 barpad_x = size.Width/16;
1158         u32 barpad_y = size.Height/16;
1159         u32 barwidth = size.Width - barpad_x*2;
1160         v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
1161
1162         u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
1163
1164         video::SColor active(255,255,0,0);
1165         video::SColor inactive(255,0,0,0);
1166         for(u32 x0=0; x0<barwidth; x0++)
1167         {
1168                 video::SColor *c;
1169                 if(x0 < barvalue_i)
1170                         c = &active;
1171                 else
1172                         c = &inactive;
1173                 u32 x = x0 + barpos.X;
1174                 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
1175                 {
1176                         image->setPixel(x,y, *c);
1177                 }
1178         }
1179 }
1180