7b19b3651f1d71b87301d82930695d8320d7b009
[oweals/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                 if(part_of_name.substr(0,6) == "[crack")
631                 {
632                         if(baseimg == NULL)
633                         {
634                                 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
635                                                 <<"for part_of_name="<<part_of_name
636                                                 <<", cancelling."<<std::endl;
637                                 return false;
638                         }
639
640                         u16 progression = stoi(part_of_name.substr(6));
641                         // Size of the base image
642                         core::dimension2d<u32> dim_base = baseimg->getDimension();
643                         // Crack will be drawn at this size
644                         u32 cracksize = 16;
645                         // Size of the crack image
646                         core::dimension2d<u32> dim_crack(cracksize,cracksize);
647                         // Position to copy the crack from in the crack image
648                         core::position2d<s32> pos_other(0, 16 * progression);
649
650                         video::IImage *crackimage = driver->createImageFromFile(
651                                         porting::getDataPath("crack.png").c_str());
652                 
653                         if(crackimage)
654                         {
655                                 /*crackimage->copyToWithAlpha(baseimg, v2s32(0,0),
656                                                 core::rect<s32>(pos_other, dim_base),
657                                                 video::SColor(255,255,255,255),
658                                                 NULL);*/
659
660                                 for(u32 y0=0; y0<dim_base.Height/dim_crack.Height; y0++)
661                                 for(u32 x0=0; x0<dim_base.Width/dim_crack.Width; x0++)
662                                 {
663                                         // Position to copy the crack to in the base image
664                                         core::position2d<s32> pos_base(x0*cracksize, y0*cracksize);
665                                         crackimage->copyToWithAlpha(baseimg, pos_base,
666                                                         core::rect<s32>(pos_other, dim_crack),
667                                                         video::SColor(255,255,255,255),
668                                                         NULL);
669                                 }
670
671                                 crackimage->drop();
672                         }
673                 }
674                 else if(part_of_name.substr(0,8) == "[combine")
675                 {
676                         // "[combine:16x128:0,0=stone.png:0,16=grass.png"
677                         Strfnd sf(part_of_name);
678                         sf.next(":");
679                         u32 w0 = stoi(sf.next("x"));
680                         u32 h0 = stoi(sf.next(":"));
681                         dstream<<"INFO: combined w="<<w0<<" h="<<h0<<std::endl;
682                         core::dimension2d<u32> dim(w0,h0);
683                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
684                         while(sf.atend() == false)
685                         {
686                                 u32 x = stoi(sf.next(","));
687                                 u32 y = stoi(sf.next("="));
688                                 std::string filename = sf.next(":");
689                                 dstream<<"INFO: Adding \""<<filename
690                                                 <<"\" to combined ("<<x<<","<<y<<")"
691                                                 <<std::endl;
692                                 video::IImage *img = driver->createImageFromFile(
693                                                 porting::getDataPath(filename.c_str()).c_str());
694                                 if(img)
695                                 {
696                                         core::dimension2d<u32> dim = img->getDimension();
697                                         dstream<<"INFO: Size "<<dim.Width
698                                                         <<"x"<<dim.Height<<std::endl;
699                                         core::position2d<s32> pos_base(x, y);
700                                         video::IImage *img2 =
701                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
702                                         img->copyTo(img2);
703                                         img->drop();
704                                         img2->copyToWithAlpha(baseimg, pos_base,
705                                                         core::rect<s32>(v2s32(0,0), dim),
706                                                         video::SColor(255,255,255,255),
707                                                         NULL);
708                                         img2->drop();
709                                 }
710                                 else
711                                 {
712                                         dstream<<"WARNING: img==NULL"<<std::endl;
713                                 }
714                         }
715                 }
716                 else if(part_of_name.substr(0,12) == "[progressbar")
717                 {
718                         if(baseimg == NULL)
719                         {
720                                 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
721                                                 <<"for part_of_name="<<part_of_name
722                                                 <<", cancelling."<<std::endl;
723                                 return false;
724                         }
725
726                         float value = stof(part_of_name.substr(12));
727                         make_progressbar(value, baseimg);
728                 }
729                 // "[noalpha:filename.png"
730                 // Use an image without it's alpha channel
731                 else if(part_of_name.substr(0,8) == "[noalpha")
732                 {
733                         if(baseimg != NULL)
734                         {
735                                 dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
736                                                 <<"for part_of_name="<<part_of_name
737                                                 <<", cancelling."<<std::endl;
738                                 return false;
739                         }
740
741                         std::string filename = part_of_name.substr(9);
742
743                         std::string path = porting::getDataPath(filename.c_str());
744
745                         dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
746                                         <<"\""<<std::endl;
747                         
748                         video::IImage *image = driver->createImageFromFile(path.c_str());
749                         
750                         if(image == NULL)
751                         {
752                                 dstream<<"WARNING: getTextureIdDirect(): Loading path \""
753                                                 <<path<<"\" failed"<<std::endl;
754                         }
755                         else
756                         {
757                                 core::dimension2d<u32> dim = image->getDimension();
758                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
759                                 
760                                 // Set alpha to full
761                                 for(u32 y=0; y<dim.Height; y++)
762                                 for(u32 x=0; x<dim.Width; x++)
763                                 {
764                                         video::SColor c = image->getPixel(x,y);
765                                         c.setAlpha(255);
766                                         image->setPixel(x,y,c);
767                                 }
768                                 // Blit
769                                 image->copyTo(baseimg);
770
771                                 image->drop();
772                         }
773                 }
774                 else
775                 {
776                         dstream<<"WARNING: getTextureIdDirect(): Invalid "
777                                         " modification: \""<<part_of_name<<"\""<<std::endl;
778                 }
779         }
780
781         return true;
782 }
783
784 void make_progressbar(float value, video::IImage *image)
785 {
786         if(image == NULL)
787                 return;
788         
789         core::dimension2d<u32> size = image->getDimension();
790
791         u32 barheight = 1;
792         u32 barpad_x = 1;
793         u32 barpad_y = 1;
794         u32 barwidth = size.Width - barpad_x*2;
795         v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
796
797         u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
798
799         video::SColor active(255,255,0,0);
800         video::SColor inactive(255,0,0,0);
801         for(u32 x0=0; x0<barwidth; x0++)
802         {
803                 video::SColor *c;
804                 if(x0 < barvalue_i)
805                         c = &active;
806                 else
807                         c = &inactive;
808                 u32 x = x0 + barpos.X;
809                 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
810                 {
811                         image->setPixel(x,y, *c);
812                 }
813         }
814 }
815