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