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