]> git.lizzy.rs Git - minetest.git/blob - src/tile.cpp
new texture stuff quite working
[minetest.git] / src / tile.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "tile.h"
21 #include "debug.h"
22
23 TextureSource::TextureSource(IrrlichtDevice *device):
24                 m_device(device),
25                 m_main_atlas_image(NULL),
26                 m_main_atlas_texture(NULL)
27 {
28         assert(m_device);
29         
30         m_atlaspointer_cache_mutex.Init();
31         
32         m_main_thread = get_current_thread_id();
33         
34         // Add a NULL AtlasPointer as the first index, named ""
35         m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
36         m_name_to_id[""] = 0;
37
38         // Build main texture atlas
39         buildMainAtlas();
40 }
41
42 TextureSource::~TextureSource()
43 {
44 }
45
46 void TextureSource::processQueue()
47 {
48         /*
49                 Fetch textures
50         */
51         if(m_get_texture_queue.size() > 0)
52         {
53                 GetRequest<std::string, u32, u8, u8>
54                                 request = m_get_texture_queue.pop();
55
56                 dstream<<"INFO: TextureSource::processQueue(): "
57                                 <<"got texture request with "
58                                 <<"name="<<request.key
59                                 <<std::endl;
60
61                 GetResult<std::string, u32, u8, u8>
62                                 result;
63                 result.key = request.key;
64                 result.callers = request.callers;
65                 result.item = getTextureIdDirect(request.key);
66
67                 request.dest->push_back(result);
68         }
69 }
70
71 u32 TextureSource::getTextureId(const std::string &name)
72 {
73         //dstream<<"INFO: getTextureId(): name="<<name<<std::endl;
74
75         {
76                 /*
77                         See if texture already exists
78                 */
79                 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
80                 core::map<std::string, u32>::Node *n;
81                 n = m_name_to_id.find(name);
82                 if(n != NULL)
83                 {
84                         return n->getValue();
85                 }
86         }
87         
88         /*
89                 Get texture
90         */
91         if(get_current_thread_id() == m_main_thread)
92         {
93                 return getTextureIdDirect(name);
94         }
95         else
96         {
97                 dstream<<"INFO: getTextureId(): Queued: name="<<name<<std::endl;
98
99                 // We're gonna ask the result to be put into here
100                 ResultQueue<std::string, u32, u8, u8> result_queue;
101                 
102                 // Throw a request in
103                 m_get_texture_queue.add(name, 0, 0, &result_queue);
104                 
105                 dstream<<"INFO: Waiting for texture from main thread, name="
106                                 <<name<<std::endl;
107                 
108                 try
109                 {
110                         // Wait result for a second
111                         GetResult<std::string, u32, u8, u8>
112                                         result = result_queue.pop_front(1000);
113                 
114                         // Check that at least something worked OK
115                         assert(result.key == name);
116
117                         return result.item;
118                 }
119                 catch(ItemNotFoundException &e)
120                 {
121                         dstream<<"WARNING: Waiting for texture timed out."<<std::endl;
122                         return 0;
123                 }
124         }
125         
126         dstream<<"WARNING: getTextureId(): Failed"<<std::endl;
127
128         return 0;
129 }
130
131 // Draw a progress bar on the image
132 void make_progressbar(float value, video::IImage *image);
133
134 /*
135         Generate image based on a string like "stone.png" or "[crack0".
136         if baseimg is NULL, it is created. Otherwise stuff is made on it.
137 */
138 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
139                 video::IVideoDriver* driver);
140
141 /*
142         Generates an image from a full string like
143         "stone.png^mineral_coal.png^[crack0".
144
145         This is used by buildMainAtlas().
146 */
147 video::IImage* generate_image_from_scratch(std::string name,
148                 video::IVideoDriver* driver);
149
150 /*
151         This method generates all the textures
152 */
153 u32 TextureSource::getTextureIdDirect(const std::string &name)
154 {
155         dstream<<"INFO: getTextureIdDirect(): name="<<name<<std::endl;
156
157         // Empty name means texture 0
158         if(name == "")
159         {
160                 dstream<<"INFO: getTextureIdDirect(): name is empty"<<std::endl;
161                 return 0;
162         }
163         
164         /*
165                 Calling only allowed from main thread
166         */
167         if(get_current_thread_id() != m_main_thread)
168         {
169                 dstream<<"ERROR: TextureSource::getTextureIdDirect() "
170                                 "called not from main thread"<<std::endl;
171                 return 0;
172         }
173
174         /*
175                 See if texture already exists
176         */
177         {
178                 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
179
180                 core::map<std::string, u32>::Node *n;
181                 n = m_name_to_id.find(name);
182                 if(n != NULL)
183                 {
184                         dstream<<"INFO: getTextureIdDirect(): name="<<name
185                                         <<" found in cache"<<std::endl;
186                         return n->getValue();
187                 }
188         }
189
190         dstream<<"INFO: getTextureIdDirect(): name="<<name
191                         <<" NOT found in cache. Creating it."<<std::endl;
192         
193         /*
194                 Get the base image
195         */
196
197         char separator = '^';
198
199         /*
200                 This is set to the id of the base image.
201                 If left 0, there is no base image and a completely new image
202                 is made.
203         */
204         u32 base_image_id = 0;
205         
206         // Find last meta separator in name
207         s32 last_separator_position = -1;
208         for(s32 i=name.size()-1; i>=0; i--)
209         {
210                 if(name[i] == separator)
211                 {
212                         last_separator_position = i;
213                         break;
214                 }
215         }
216         /*
217                 If separator was found, construct the base name and make the
218                 base image using a recursive call
219         */
220         std::string base_image_name;
221         if(last_separator_position != -1)
222         {
223                 // Construct base name
224                 base_image_name = name.substr(0, last_separator_position);
225                 dstream<<"INFO: getTextureIdDirect(): Calling itself recursively"
226                                 " to get base image, name="<<base_image_name<<std::endl;
227                 base_image_id = getTextureIdDirect(base_image_name);
228         }
229         
230         dstream<<"base_image_id="<<base_image_id<<std::endl;
231         
232         video::IVideoDriver* driver = m_device->getVideoDriver();
233         assert(driver);
234
235         video::ITexture *t = NULL;
236
237         /*
238                 An image will be built from files and then converted into a texture.
239         */
240         video::IImage *baseimg = NULL;
241         
242         // If a base image was found, copy it to baseimg
243         if(base_image_id != 0)
244         {
245                 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
246
247                 SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
248
249                 video::IImage *image = ap.atlas_img;
250
251                 core::dimension2d<u32> dim = ap.intsize;
252
253                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
254
255                 core::position2d<s32> pos_to(0,0);
256                 core::position2d<s32> pos_from = ap.intpos;
257                 
258                 image->copyTo(
259                                 baseimg, // target
260                                 v2s32(0,0), // position in target
261                                 core::rect<s32>(pos_from, dim) // from
262                 );
263
264                 dstream<<"INFO: getTextureIdDirect(): Loaded \""
265                                 <<base_image_name<<"\" from image cache"
266                                 <<std::endl;
267         }
268         
269         /*
270                 Parse out the last part of the name of the image and act
271                 according to it
272         */
273
274         std::string last_part_of_name = name.substr(last_separator_position+1);
275         dstream<<"last_part_of_name="<<last_part_of_name<<std::endl;
276         
277         // Generate image according to part of name
278         if(generate_image(last_part_of_name, baseimg, driver) == false)
279         {
280                 dstream<<"INFO: getTextureIdDirect(): "
281                                 "failed to generate \""<<last_part_of_name<<"\""
282                                 <<std::endl;
283                 return 0;
284         }
285
286         // If no resulting image, return NULL
287         if(baseimg == NULL)
288         {
289                 dstream<<"WARNING: getTextureIdDirect(): baseimg is NULL (attempted to"
290                                 " create texture \""<<name<<"\""<<std::endl;
291                 return 0;
292         }
293
294         // Create texture from resulting image
295         t = driver->addTexture(name.c_str(), baseimg);
296         
297         // If no texture
298         if(t == NULL)
299                 return 0;
300
301         /*
302                 Add texture to caches
303         */
304
305         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
306         
307         u32 id = m_atlaspointer_cache.size();
308         AtlasPointer ap(id);
309         ap.atlas = t;
310         ap.pos = v2f(0,0);
311         ap.size = v2f(1,1);
312         ap.tiled = 0;
313         SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg->getDimension());
314         m_atlaspointer_cache.push_back(nap);
315         m_name_to_id.insert(name, id);
316
317         dstream<<"INFO: getTextureIdDirect(): name="<<name
318                         <<": succesfully returning id="<<id<<std::endl;
319         
320         return id;
321 }
322
323 std::string TextureSource::getTextureName(u32 id)
324 {
325         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
326
327         if(id >= m_atlaspointer_cache.size())
328         {
329                 dstream<<"WARNING: TextureSource::getTextureName(): id="<<id
330                                 <<" >= m_atlaspointer_cache.size()="
331                                 <<m_atlaspointer_cache.size()<<std::endl;
332                 return "";
333         }
334         
335         return m_atlaspointer_cache[id].name;
336 }
337
338
339 AtlasPointer TextureSource::getTexture(u32 id)
340 {
341         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
342
343         if(id >= m_atlaspointer_cache.size())
344                 return AtlasPointer(0, NULL);
345         
346         return m_atlaspointer_cache[id].a;
347 }
348
349 void TextureSource::buildMainAtlas() 
350 {
351         dstream<<"TextureSource::buildMainAtlas()"<<std::endl;
352
353         //return; // Disable (for testing)
354         
355         video::IVideoDriver* driver = m_device->getVideoDriver();
356         assert(driver);
357
358         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
359
360         // Create an image of the right size
361         core::dimension2d<u32> atlas_dim(1024,1024);
362         video::IImage *atlas_img =
363                         driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
364
365         /*
366                 A list of stuff to add. This should contain as much of the
367                 stuff shown in game as possible, to minimize texture changes.
368         */
369
370         core::array<std::string> sourcelist;
371
372         sourcelist.push_back("stone.png");
373         sourcelist.push_back("mud.png");
374         sourcelist.push_back("sand.png");
375         sourcelist.push_back("grass.png");
376         sourcelist.push_back("grass_footsteps.png");
377         sourcelist.push_back("tree.png");
378         sourcelist.push_back("tree_top.png");
379         sourcelist.push_back("water.png");
380         sourcelist.push_back("leaves.png");
381         sourcelist.push_back("mud.png^grass_side.png");
382         
383         sourcelist.push_back("stone.png^mineral_coal.png");
384         sourcelist.push_back("stone.png^mineral_iron.png");
385         sourcelist.push_back("mud.png^mineral_coal.png");
386         sourcelist.push_back("mud.png^mineral_iron.png");
387         sourcelist.push_back("sand.png^mineral_coal.png");
388         sourcelist.push_back("sand.png^mineral_iron.png");
389         
390         // Padding to disallow texture bleeding
391         s32 padding = 8;
392
393         /*
394                 First pass: generate almost everything
395         */
396         core::position2d<s32> pos_in_atlas(0,0);
397         
398         pos_in_atlas.Y += padding;
399
400         for(u32 i=0; i<sourcelist.size(); i++)
401         {
402                 std::string name = sourcelist[i];
403
404                 /*video::IImage *img = driver->createImageFromFile(
405                                 porting::getDataPath(name.c_str()).c_str());
406                 if(img == NULL)
407                         continue;
408                 
409                 core::dimension2d<u32> dim = img->getDimension();
410                 // Make a copy with the right color format
411                 video::IImage *img2 =
412                                 driver->createImage(video::ECF_A8R8G8B8, dim);
413                 img->copyTo(img2);
414                 img->drop();*/
415                 
416                 // Generate image by name
417                 video::IImage *img2 = generate_image_from_scratch(name, driver);
418                 core::dimension2d<u32> dim = img2->getDimension();
419                 
420                 // Tile it a few times in the X direction
421                 u16 xwise_tiling = 16;
422                 for(u32 j=0; j<xwise_tiling; j++)
423                 {
424                         // Copy the copy to the atlas
425                         img2->copyToWithAlpha(atlas_img,
426                                         pos_in_atlas + v2s32(j*dim.Width,0),
427                                         core::rect<s32>(v2s32(0,0), dim),
428                                         video::SColor(255,255,255,255),
429                                         NULL);
430                 }
431
432                 // Copy the borders a few times to disallow texture bleeding
433                 for(u32 side=0; side<2; side++) // top and bottom
434                 for(s32 y0=0; y0<padding; y0++)
435                 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
436                 {
437                         s32 dst_y;
438                         s32 src_y;
439                         if(side==0)
440                         {
441                                 dst_y = y0 + pos_in_atlas.Y + dim.Height;
442                                 src_y = pos_in_atlas.Y + dim.Height - 1;
443                         }
444                         else
445                         {
446                                 dst_y = -y0 + pos_in_atlas.Y-1;
447                                 src_y = pos_in_atlas.Y;
448                         }
449                         s32 x = x0 + pos_in_atlas.X * dim.Width;
450                         video::SColor c = atlas_img->getPixel(x, src_y);
451                         atlas_img->setPixel(x,dst_y,c);
452                 }
453
454                 img2->drop();
455
456                 /*
457                         Add texture to caches
458                 */
459                 
460                 // Get next id
461                 u32 id = m_atlaspointer_cache.size();
462
463                 // Create AtlasPointer
464                 AtlasPointer ap(id);
465                 ap.atlas = NULL; // Set on the second pass
466                 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
467                                 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
468                 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
469                                 (float)dim.Width/(float)atlas_dim.Height);
470                 ap.tiled = xwise_tiling;
471
472                 // Create SourceAtlasPointer and add to containers
473                 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
474                 m_atlaspointer_cache.push_back(nap);
475                 m_name_to_id.insert(name, id);
476                         
477                 // Increment position
478                 pos_in_atlas.Y += dim.Height + padding * 2;
479         }
480
481         /*
482                 Make texture
483         */
484         video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
485         assert(t);
486
487         /*
488                 Second pass: set texture pointer in generated AtlasPointers
489         */
490         for(u32 i=0; i<sourcelist.size(); i++)
491         {
492                 std::string name = sourcelist[i];
493                 u32 id = m_name_to_id[name];
494                 m_atlaspointer_cache[id].a.atlas = t;
495         }
496
497         /*
498                 Write image to file so that it can be inspected
499         */
500         driver->writeImageToFile(atlas_img, 
501                         porting::getDataPath("main_atlas.png").c_str());
502 }
503
504 video::IImage* generate_image_from_scratch(std::string name,
505                 video::IVideoDriver* driver)
506 {
507         dstream<<"INFO: generate_image_from_scratch(): "
508                         "name="<<name<<std::endl;
509         
510         /*
511                 Get the base image
512         */
513
514         video::IImage *baseimg = NULL;
515
516         char separator = '^';
517
518         // Find last meta separator in name
519         s32 last_separator_position = -1;
520         for(s32 i=name.size()-1; i>=0; i--)
521         {
522                 if(name[i] == separator)
523                 {
524                         last_separator_position = i;
525                         break;
526                 }
527         }
528
529         /*dstream<<"INFO: generate_image_from_scratch(): "
530                         <<"last_separator_position="<<last_separator_position
531                         <<std::endl;*/
532
533         /*
534                 If separator was found, construct the base name and make the
535                 base image using a recursive call
536         */
537         std::string base_image_name;
538         if(last_separator_position != -1)
539         {
540                 // Construct base name
541                 base_image_name = name.substr(0, last_separator_position);
542                 dstream<<"INFO: generate_image_from_scratch(): Calling itself recursively"
543                                 " to get base image, name="<<base_image_name<<std::endl;
544                 baseimg = generate_image_from_scratch(base_image_name, driver);
545         }
546         
547         /*
548                 Parse out the last part of the name of the image and act
549                 according to it
550         */
551
552         std::string last_part_of_name = name.substr(last_separator_position+1);
553         dstream<<"last_part_of_name="<<last_part_of_name<<std::endl;
554         
555         // Generate image according to part of name
556         if(generate_image(last_part_of_name, baseimg, driver) == false)
557         {
558                 dstream<<"INFO: generate_image_from_scratch(): "
559                                 "failed to generate \""<<last_part_of_name<<"\""
560                                 <<std::endl;
561                 return NULL;
562         }
563         
564         return baseimg;
565 }
566
567 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
568                 video::IVideoDriver* driver)
569 {
570         // Stuff starting with [ are special commands
571         if(part_of_name[0] != '[')
572         {
573                 // A normal texture; load it from a file
574                 std::string path = porting::getDataPath(part_of_name.c_str());
575                 dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
576                                 <<"\""<<std::endl;
577                 
578                 video::IImage *image = driver->createImageFromFile(path.c_str());
579
580                 if(image == NULL)
581                 {
582                         dstream<<"WARNING: Could not load image \""<<part_of_name
583                                         <<"\" from path \""<<path<<"\""
584                                         <<" while building texture"<<std::endl;
585                         return false;
586                 }
587
588                 // If base image is NULL, load as base.
589                 if(baseimg == NULL)
590                 {
591                         dstream<<"INFO: Setting "<<part_of_name<<" as base"<<std::endl;
592                         /*
593                                 Copy it this way to get an alpha channel.
594                                 Otherwise images with alpha cannot be blitted on 
595                                 images that don't have alpha in the original file.
596                         */
597                         core::dimension2d<u32> dim = image->getDimension();
598                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
599                         image->copyTo(baseimg);
600                         image->drop();
601                 }
602                 // Else blit on base.
603                 else
604                 {
605                         dstream<<"INFO: Blitting "<<part_of_name<<" on base"<<std::endl;
606                         // Size of the copied area
607                         core::dimension2d<u32> dim = image->getDimension();
608                         //core::dimension2d<u32> dim(16,16);
609                         // Position to copy the blitted to in the base image
610                         core::position2d<s32> pos_to(0,0);
611                         // Position to copy the blitted from in the blitted image
612                         core::position2d<s32> pos_from(0,0);
613                         // Blit
614                         image->copyToWithAlpha(baseimg, pos_to,
615                                         core::rect<s32>(pos_from, dim),
616                                         video::SColor(255,255,255,255),
617                                         NULL);
618                         // Drop image
619                         image->drop();
620                 }
621         }
622         else
623         {
624                 // A special texture modification
625
626                 dstream<<"INFO: getTextureIdDirect(): generating special "
627                                 <<"modification \""<<part_of_name<<"\""
628                                 <<std::endl;
629                 
630                 /*
631                         This is the simplest of all; it just adds stuff to the
632                         name so that a separate texture is created.
633
634                         It is used to make textures for stuff that doesn't want
635                         to implement getting the texture from a bigger texture
636                         atlas.
637                 */
638                 if(part_of_name == "[forcesingle")
639                 {
640                 }
641                 /*
642                         [crackN
643                         Adds a cracking texture
644                 */
645                 else if(part_of_name.substr(0,6) == "[crack")
646                 {
647                         if(baseimg == NULL)
648                         {
649                                 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
650                                                 <<"for part_of_name="<<part_of_name
651                                                 <<", cancelling."<<std::endl;
652                                 return false;
653                         }
654
655                         u16 progression = stoi(part_of_name.substr(6));
656                         // Size of the base image
657                         core::dimension2d<u32> dim_base = baseimg->getDimension();
658                         // Crack will be drawn at this size
659                         u32 cracksize = 16;
660                         // Size of the crack image
661                         core::dimension2d<u32> dim_crack(cracksize,cracksize);
662                         // Position to copy the crack from in the crack image
663                         core::position2d<s32> pos_other(0, 16 * progression);
664
665                         video::IImage *crackimage = driver->createImageFromFile(
666                                         porting::getDataPath("crack.png").c_str());
667                 
668                         if(crackimage)
669                         {
670                                 /*crackimage->copyToWithAlpha(baseimg, v2s32(0,0),
671                                                 core::rect<s32>(pos_other, dim_base),
672                                                 video::SColor(255,255,255,255),
673                                                 NULL);*/
674
675                                 for(u32 y0=0; y0<dim_base.Height/dim_crack.Height; y0++)
676                                 for(u32 x0=0; x0<dim_base.Width/dim_crack.Width; x0++)
677                                 {
678                                         // Position to copy the crack to in the base image
679                                         core::position2d<s32> pos_base(x0*cracksize, y0*cracksize);
680                                         crackimage->copyToWithAlpha(baseimg, pos_base,
681                                                         core::rect<s32>(pos_other, dim_crack),
682                                                         video::SColor(255,255,255,255),
683                                                         NULL);
684                                 }
685
686                                 crackimage->drop();
687                         }
688                 }
689                 /*
690                         [combine:WxH:X,Y=filename:X,Y=filename2
691                         Creates a bigger texture from an amount of smaller ones
692                 */
693                 else if(part_of_name.substr(0,8) == "[combine")
694                 {
695                         Strfnd sf(part_of_name);
696                         sf.next(":");
697                         u32 w0 = stoi(sf.next("x"));
698                         u32 h0 = stoi(sf.next(":"));
699                         dstream<<"INFO: combined w="<<w0<<" h="<<h0<<std::endl;
700                         core::dimension2d<u32> dim(w0,h0);
701                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
702                         while(sf.atend() == false)
703                         {
704                                 u32 x = stoi(sf.next(","));
705                                 u32 y = stoi(sf.next("="));
706                                 std::string filename = sf.next(":");
707                                 dstream<<"INFO: Adding \""<<filename
708                                                 <<"\" to combined ("<<x<<","<<y<<")"
709                                                 <<std::endl;
710                                 video::IImage *img = driver->createImageFromFile(
711                                                 porting::getDataPath(filename.c_str()).c_str());
712                                 if(img)
713                                 {
714                                         core::dimension2d<u32> dim = img->getDimension();
715                                         dstream<<"INFO: Size "<<dim.Width
716                                                         <<"x"<<dim.Height<<std::endl;
717                                         core::position2d<s32> pos_base(x, y);
718                                         video::IImage *img2 =
719                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
720                                         img->copyTo(img2);
721                                         img->drop();
722                                         img2->copyToWithAlpha(baseimg, pos_base,
723                                                         core::rect<s32>(v2s32(0,0), dim),
724                                                         video::SColor(255,255,255,255),
725                                                         NULL);
726                                         img2->drop();
727                                 }
728                                 else
729                                 {
730                                         dstream<<"WARNING: img==NULL"<<std::endl;
731                                 }
732                         }
733                 }
734                 /*
735                         [progressbarN
736                         Adds a progress bar, 0.0 <= N <= 1.0
737                 */
738                 else if(part_of_name.substr(0,12) == "[progressbar")
739                 {
740                         if(baseimg == NULL)
741                         {
742                                 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
743                                                 <<"for part_of_name="<<part_of_name
744                                                 <<", cancelling."<<std::endl;
745                                 return false;
746                         }
747
748                         float value = stof(part_of_name.substr(12));
749                         make_progressbar(value, baseimg);
750                 }
751                 /*
752                         "[noalpha:filename.png"
753                         Use an image without it's alpha channel.
754                         Used for the leaves texture when in old leaves mode, so
755                         that the transparent parts don't look completely black 
756                         when simple alpha channel is used for rendering.
757                 */
758                 else if(part_of_name.substr(0,8) == "[noalpha")
759                 {
760                         if(baseimg != NULL)
761                         {
762                                 dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
763                                                 <<"for part_of_name="<<part_of_name
764                                                 <<", cancelling."<<std::endl;
765                                 return false;
766                         }
767
768                         std::string filename = part_of_name.substr(9);
769
770                         std::string path = porting::getDataPath(filename.c_str());
771
772                         dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
773                                         <<"\""<<std::endl;
774                         
775                         video::IImage *image = driver->createImageFromFile(path.c_str());
776                         
777                         if(image == NULL)
778                         {
779                                 dstream<<"WARNING: getTextureIdDirect(): Loading path \""
780                                                 <<path<<"\" failed"<<std::endl;
781                         }
782                         else
783                         {
784                                 core::dimension2d<u32> dim = image->getDimension();
785                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
786                                 
787                                 // Set alpha to full
788                                 for(u32 y=0; y<dim.Height; y++)
789                                 for(u32 x=0; x<dim.Width; x++)
790                                 {
791                                         video::SColor c = image->getPixel(x,y);
792                                         c.setAlpha(255);
793                                         image->setPixel(x,y,c);
794                                 }
795                                 // Blit
796                                 image->copyTo(baseimg);
797
798                                 image->drop();
799                         }
800                 }
801                 /*
802                         [inventorycube{topimage{leftimage{rightimage
803                         In every subimage, replace ^ with &.
804                         Create an "inventory cube".
805                         NOTE: This should be used only on its own.
806                         Example (a grass block (not actually used in game):
807                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
808                 */
809                 else if(part_of_name.substr(0,14) == "[inventorycube")
810                 {
811                         if(baseimg != NULL)
812                         {
813                                 dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
814                                                 <<"for part_of_name="<<part_of_name
815                                                 <<", cancelling."<<std::endl;
816                                 return false;
817                         }
818
819                         // This is just a placeholder
820
821                         str_replace_char(part_of_name, '&', '^');
822                         Strfnd sf(part_of_name);
823                         sf.next("{");
824                         std::string imagename_top = sf.next("{");
825                         std::string imagename_left = sf.next("{");
826                         std::string imagename_right = sf.next("{");
827
828                         baseimg = generate_image_from_scratch(
829                                         imagename_top, driver);
830
831                         //TODO
832 #if 0
833                         if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
834                         {
835                                 dstream<<"WARNING: getTextureIdDirect(): EVDF_RENDER_TO_TARGET"
836                                                 " not supported"<<std::endl;
837                                 return false;
838                         }
839                         
840                         u32 w0 = 16;
841                         u32 h0 = 16;
842                         dstream<<"INFO: inventorycube w="<<w0<<" h="<<h0<<std::endl;
843                         core::dimension2d<u32> dim(w0,h0);
844
845                         //baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
846
847                         video::IImage *img_top = generate_image_from_scratch(
848                                         imagename_top, driver);
849                         video::IImage *img_left = generate_image_from_scratch(
850                                         imagename_left, driver);
851                         video::IImage *img_right = generate_image_from_scratch(
852                                         imagename_right, driver);
853                         
854                         // Render target texture
855                         video::ITexture *rtt = NULL;
856                         std::string rtt_name = part_of_name + "_RTT";
857                         
858                         rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str());
859                         assert(rtt);
860                         
861                         
862                         
863                         img_top->drop();
864                         img_left->drop();
865                         img_right->drop();
866                         
867                         //TODO
868                         assert(0);
869 #endif
870                 }
871                 else
872                 {
873                         dstream<<"WARNING: getTextureIdDirect(): Invalid "
874                                         " modification: \""<<part_of_name<<"\""<<std::endl;
875                 }
876         }
877
878         return true;
879 }
880
881 void make_progressbar(float value, video::IImage *image)
882 {
883         if(image == NULL)
884                 return;
885         
886         core::dimension2d<u32> size = image->getDimension();
887
888         u32 barheight = 1;
889         u32 barpad_x = 1;
890         u32 barpad_y = 1;
891         u32 barwidth = size.Width - barpad_x*2;
892         v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
893
894         u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
895
896         video::SColor active(255,255,0,0);
897         video::SColor inactive(255,0,0,0);
898         for(u32 x0=0; x0<barwidth; x0++)
899         {
900                 video::SColor *c;
901                 if(x0 < barvalue_i)
902                         c = &active;
903                 else
904                         c = &inactive;
905                 u32 x = x0 + barpos.X;
906                 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
907                 {
908                         image->setPixel(x,y, *c);
909                 }
910         }
911 }
912