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