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