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