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