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