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