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