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