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