]> git.lizzy.rs Git - dragonfireclient.git/blob - src/tile.cpp
f31f830762fb8096f3c0fcc3b061345343a02fd7
[dragonfireclient.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("mud.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         /*
391                 First pass: generate almost everything
392         */
393         core::position2d<s32> pos_in_atlas(0,0);
394         for(u32 i=0; i<sourcelist.size(); i++)
395         {
396                 std::string name = sourcelist[i];
397
398                 /*video::IImage *img = driver->createImageFromFile(
399                                 porting::getDataPath(name.c_str()).c_str());
400                 if(img == NULL)
401                         continue;
402                 
403                 core::dimension2d<u32> dim = img->getDimension();
404                 // Make a copy with the right color format
405                 video::IImage *img2 =
406                                 driver->createImage(video::ECF_A8R8G8B8, dim);
407                 img->copyTo(img2);
408                 img->drop();*/
409                 
410                 // Generate image by name
411                 video::IImage *img2 = generate_image_from_scratch(name, driver);
412                 core::dimension2d<u32> dim = img2->getDimension();
413                 
414                 // Tile it a few times in the X direction
415                 u16 xwise_tiling = 16;
416                 for(u32 j=0; j<xwise_tiling; j++)
417                 {
418                         // Copy the copy to the atlas
419                         img2->copyToWithAlpha(atlas_img,
420                                         pos_in_atlas + v2s32(j*dim.Width,0),
421                                         core::rect<s32>(v2s32(0,0), dim),
422                                         video::SColor(255,255,255,255),
423                                         NULL);
424                 }
425
426                 img2->drop();
427
428                 /*
429                         Add texture to caches
430                 */
431                 
432                 // Get next id
433                 u32 id = m_atlaspointer_cache.size();
434
435                 // Create AtlasPointer
436                 AtlasPointer ap(id);
437                 ap.atlas = NULL; // Set on the second pass
438                 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
439                                 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
440                 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
441                                 (float)dim.Width/(float)atlas_dim.Height);
442                 ap.tiled = xwise_tiling;
443
444                 // Create SourceAtlasPointer and add to containers
445                 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
446                 m_atlaspointer_cache.push_back(nap);
447                 m_name_to_id.insert(name, id);
448                         
449                 // Increment position
450                 pos_in_atlas.Y += dim.Height;
451         }
452
453         /*
454                 Make texture
455         */
456         video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
457         assert(t);
458
459         /*
460                 Second pass: set texture pointer in generated AtlasPointers
461         */
462         for(u32 i=0; i<sourcelist.size(); i++)
463         {
464                 std::string name = sourcelist[i];
465                 u32 id = m_name_to_id[name];
466                 m_atlaspointer_cache[id].a.atlas = t;
467         }
468
469         /*
470                 Write image to file so that it can be inspected
471         */
472         driver->writeImageToFile(atlas_img, 
473                         porting::getDataPath("main_atlas.png").c_str());
474 }
475
476 video::IImage* generate_image_from_scratch(std::string name,
477                 video::IVideoDriver* driver)
478 {
479         dstream<<"INFO: generate_image_from_scratch(): "
480                         "name="<<name<<std::endl;
481         
482         /*
483                 Get the base image
484         */
485
486         video::IImage *baseimg = NULL;
487
488         char separator = '^';
489
490         // Find last meta separator in name
491         s32 last_separator_position = -1;
492         for(s32 i=name.size()-1; i>=0; i--)
493         {
494                 if(name[i] == separator)
495                 {
496                         last_separator_position = i;
497                         break;
498                 }
499         }
500
501         /*dstream<<"INFO: generate_image_from_scratch(): "
502                         <<"last_separator_position="<<last_separator_position
503                         <<std::endl;*/
504
505         /*
506                 If separator was found, construct the base name and make the
507                 base image using a recursive call
508         */
509         std::string base_image_name;
510         if(last_separator_position != -1)
511         {
512                 // Construct base name
513                 base_image_name = name.substr(0, last_separator_position);
514                 dstream<<"INFO: generate_image_from_scratch(): Calling itself recursively"
515                                 " to get base image, name="<<base_image_name<<std::endl;
516                 baseimg = generate_image_from_scratch(base_image_name, driver);
517         }
518         
519         /*
520                 Parse out the last part of the name of the image and act
521                 according to it
522         */
523
524         std::string last_part_of_name = name.substr(last_separator_position+1);
525         dstream<<"last_part_of_name="<<last_part_of_name<<std::endl;
526         
527         // Generate image according to part of name
528         if(generate_image(last_part_of_name, baseimg, driver) == false)
529         {
530                 dstream<<"INFO: generate_image_from_scratch(): "
531                                 "failed to generate \""<<last_part_of_name<<"\""
532                                 <<std::endl;
533                 return NULL;
534         }
535         
536         return baseimg;
537 }
538
539 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
540                 video::IVideoDriver* driver)
541 {
542         // Stuff starting with [ are special commands
543         if(part_of_name[0] != '[')
544         {
545                 // A normal texture; load it from a file
546                 std::string path = porting::getDataPath(part_of_name.c_str());
547                 dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
548                                 <<"\""<<std::endl;
549                 
550                 video::IImage *image = driver->createImageFromFile(path.c_str());
551
552                 if(image == NULL)
553                 {
554                         dstream<<"WARNING: Could not load image \""<<part_of_name
555                                         <<"\" from path \""<<path<<"\""
556                                         <<" while building texture"<<std::endl;
557                         return false;
558                 }
559
560                 // If base image is NULL, load as base.
561                 if(baseimg == NULL)
562                 {
563                         dstream<<"INFO: Setting "<<part_of_name<<" as base"<<std::endl;
564                         /*
565                                 Copy it this way to get an alpha channel.
566                                 Otherwise images with alpha cannot be blitted on 
567                                 images that don't have alpha in the original file.
568                         */
569                         core::dimension2d<u32> dim = image->getDimension();
570                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
571                         image->copyTo(baseimg);
572                         image->drop();
573                 }
574                 // Else blit on base.
575                 else
576                 {
577                         dstream<<"INFO: Blitting "<<part_of_name<<" on base"<<std::endl;
578                         // Size of the copied area
579                         core::dimension2d<u32> dim = image->getDimension();
580                         //core::dimension2d<u32> dim(16,16);
581                         // Position to copy the blitted to in the base image
582                         core::position2d<s32> pos_to(0,0);
583                         // Position to copy the blitted from in the blitted image
584                         core::position2d<s32> pos_from(0,0);
585                         // Blit
586                         image->copyToWithAlpha(baseimg, pos_to,
587                                         core::rect<s32>(pos_from, dim),
588                                         video::SColor(255,255,255,255),
589                                         NULL);
590                         // Drop image
591                         image->drop();
592                 }
593         }
594         else
595         {
596                 // A special texture modification
597
598                 dstream<<"INFO: getTextureIdDirect(): generating special "
599                                 <<"modification \""<<part_of_name<<"\""
600                                 <<std::endl;
601
602                 if(part_of_name.substr(0,6) == "[crack")
603                 {
604                         if(baseimg == NULL)
605                         {
606                                 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
607                                                 <<"for part_of_name="<<part_of_name
608                                                 <<", cancelling."<<std::endl;
609                                 return false;
610                         }
611
612                         u16 progression = stoi(part_of_name.substr(6));
613                         // Size of the base image
614                         core::dimension2d<u32> dim_base = baseimg->getDimension();
615                         // Crack will be drawn at this size
616                         u32 cracksize = 16;
617                         // Size of the crack image
618                         core::dimension2d<u32> dim_crack(cracksize,cracksize);
619                         // Position to copy the crack from in the crack image
620                         core::position2d<s32> pos_other(0, 16 * progression);
621
622                         video::IImage *crackimage = driver->createImageFromFile(
623                                         porting::getDataPath("crack.png").c_str());
624                 
625                         if(crackimage)
626                         {
627                                 /*crackimage->copyToWithAlpha(baseimg, v2s32(0,0),
628                                                 core::rect<s32>(pos_other, dim_base),
629                                                 video::SColor(255,255,255,255),
630                                                 NULL);*/
631
632                                 for(u32 y0=0; y0<dim_base.Height/dim_crack.Height; y0++)
633                                 for(u32 x0=0; x0<dim_base.Width/dim_crack.Width; x0++)
634                                 {
635                                         // Position to copy the crack to in the base image
636                                         core::position2d<s32> pos_base(x0*cracksize, y0*cracksize);
637                                         crackimage->copyToWithAlpha(baseimg, pos_base,
638                                                         core::rect<s32>(pos_other, dim_crack),
639                                                         video::SColor(255,255,255,255),
640                                                         NULL);
641                                 }
642
643                                 crackimage->drop();
644                         }
645                 }
646                 else if(part_of_name.substr(0,8) == "[combine")
647                 {
648                         // "[combine:16x128:0,0=stone.png:0,16=grass.png"
649                         Strfnd sf(part_of_name);
650                         sf.next(":");
651                         u32 w0 = stoi(sf.next("x"));
652                         u32 h0 = stoi(sf.next(":"));
653                         dstream<<"INFO: combined w="<<w0<<" h="<<h0<<std::endl;
654                         core::dimension2d<u32> dim(w0,h0);
655                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
656                         while(sf.atend() == false)
657                         {
658                                 u32 x = stoi(sf.next(","));
659                                 u32 y = stoi(sf.next("="));
660                                 std::string filename = sf.next(":");
661                                 dstream<<"INFO: Adding \""<<filename
662                                                 <<"\" to combined ("<<x<<","<<y<<")"
663                                                 <<std::endl;
664                                 video::IImage *img = driver->createImageFromFile(
665                                                 porting::getDataPath(filename.c_str()).c_str());
666                                 if(img)
667                                 {
668                                         core::dimension2d<u32> dim = img->getDimension();
669                                         dstream<<"INFO: Size "<<dim.Width
670                                                         <<"x"<<dim.Height<<std::endl;
671                                         core::position2d<s32> pos_base(x, y);
672                                         video::IImage *img2 =
673                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
674                                         img->copyTo(img2);
675                                         img->drop();
676                                         img2->copyToWithAlpha(baseimg, pos_base,
677                                                         core::rect<s32>(v2s32(0,0), dim),
678                                                         video::SColor(255,255,255,255),
679                                                         NULL);
680                                         img2->drop();
681                                 }
682                                 else
683                                 {
684                                         dstream<<"WARNING: img==NULL"<<std::endl;
685                                 }
686                         }
687                 }
688                 else if(part_of_name.substr(0,12) == "[progressbar")
689                 {
690                         if(baseimg == NULL)
691                         {
692                                 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
693                                                 <<"for part_of_name="<<part_of_name
694                                                 <<", cancelling."<<std::endl;
695                                 return false;
696                         }
697
698                         float value = stof(part_of_name.substr(12));
699                         make_progressbar(value, baseimg);
700                 }
701                 // "[noalpha:filename.png"
702                 // Use an image without it's alpha channel
703                 else if(part_of_name.substr(0,8) == "[noalpha")
704                 {
705                         if(baseimg != NULL)
706                         {
707                                 dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
708                                                 <<"for part_of_name="<<part_of_name
709                                                 <<", cancelling."<<std::endl;
710                                 return false;
711                         }
712
713                         std::string filename = part_of_name.substr(9);
714
715                         std::string path = porting::getDataPath(filename.c_str());
716
717                         dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
718                                         <<"\""<<std::endl;
719                         
720                         video::IImage *image = driver->createImageFromFile(path.c_str());
721                         
722                         if(image == NULL)
723                         {
724                                 dstream<<"WARNING: getTextureIdDirect(): Loading path \""
725                                                 <<path<<"\" failed"<<std::endl;
726                         }
727                         else
728                         {
729                                 core::dimension2d<u32> dim = image->getDimension();
730                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
731                                 
732                                 // Set alpha to full
733                                 for(u32 y=0; y<dim.Height; y++)
734                                 for(u32 x=0; x<dim.Width; x++)
735                                 {
736                                         video::SColor c = image->getPixel(x,y);
737                                         c.setAlpha(255);
738                                         image->setPixel(x,y,c);
739                                 }
740                                 // Blit
741                                 image->copyTo(baseimg);
742
743                                 image->drop();
744                         }
745                 }
746                 else
747                 {
748                         dstream<<"WARNING: getTextureIdDirect(): Invalid "
749                                         " modification: \""<<part_of_name<<"\""<<std::endl;
750                 }
751         }
752
753         return true;
754 }
755
756 void make_progressbar(float value, video::IImage *image)
757 {
758         if(image == NULL)
759                 return;
760         
761         core::dimension2d<u32> size = image->getDimension();
762
763         u32 barheight = 1;
764         u32 barpad_x = 1;
765         u32 barpad_y = 1;
766         u32 barwidth = size.Width - barpad_x*2;
767         v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
768
769         u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
770
771         video::SColor active(255,255,0,0);
772         video::SColor inactive(255,0,0,0);
773         for(u32 x0=0; x0<barwidth; x0++)
774         {
775                 video::SColor *c;
776                 if(x0 < barvalue_i)
777                         c = &active;
778                 else
779                         c = &inactive;
780                 u32 x = x0 + barpos.X;
781                 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
782                 {
783                         image->setPixel(x,y, *c);
784                 }
785         }
786 }
787