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